From 106b4a2a78b2a2f14885604891957b84bba502db Mon Sep 17 00:00:00 2001 From: Adrian Betschart Date: Fri, 11 Sep 2026 17:13:14 +0200 Subject: drm/amd/display: Try RGB before YCbCr 4:4:4 in stream validation amdgpu_dm_create_validate_stream_for_sink() walks encoding_order[] and uses the first encoding that validates. YCbCr 4:4:4 is listed before RGB, so an HDMI sink that advertises 4:4:4 gets YCbCr 4:4:4 whenever the "color format" property is left at AUTO, even though RGB fits the same link. That contradicts the documented AUTO behaviour for HDMI in enum drm_connector_color_format (RGB, falling back to YCbCr 4:2:0 only when the bandwidth is not available or the mode is 4:2:0-only), which the amdgpu implementation of the property also describes. It also leaves the "Broadcast RGB" property without effect on such sinks, since the quantization range it selects only applies to RGB output. Try RGB first. The mask still holds every encoding the sink supports, so a mode that cannot carry RGB falls back exactly as before. For reference, v7.2 picked RGB here unless YCbCr 4:4:4 was forced through debugfs, while earlier kernels picked YCbCr 4:4:4 for any HDMI sink that advertised it. Fixes: 0b0ff65d3ca1 ("drm/amd/display: Refactor stream validation") Suggested-by: Adolfo Rodrigues Assisted-by: Claude Code:claude-fable-5-1 Signed-off-by: Adrian Betschart Reviewed-by: Fangzhi Zuo Tested-by: Adolfo Rodrigues Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 7de432bc753133764dc40d37f9388da56de251e7) --- drivers/usb/storage/Kconfig | 199 +++ drivers/usb/storage/Makefile | 47 + drivers/usb/storage/alauda.c | 1274 +++++++++++++++ drivers/usb/storage/cypress_atacb.c | 286 ++++ drivers/usb/storage/datafab.c | 757 +++++++++ drivers/usb/storage/debug.c | 174 +++ drivers/usb/storage/debug.h | 51 + drivers/usb/storage/ene_ub6250.c | 2443 +++++++++++++++++++++++++++++ drivers/usb/storage/freecom.c | 577 +++++++ drivers/usb/storage/initializers.c | 94 ++ drivers/usb/storage/initializers.h | 39 + drivers/usb/storage/isd200.c | 1586 +++++++++++++++++++ drivers/usb/storage/jumpshot.c | 683 ++++++++ drivers/usb/storage/karma.c | 234 +++ drivers/usb/storage/onetouch.c | 307 ++++ drivers/usb/storage/option_ms.c | 160 ++ drivers/usb/storage/option_ms.h | 5 + drivers/usb/storage/protocol.c | 180 +++ drivers/usb/storage/protocol.h | 42 + drivers/usb/storage/realtek_cr.c | 1070 +++++++++++++ drivers/usb/storage/scsiglue.c | 699 +++++++++ drivers/usb/storage/scsiglue.h | 34 + drivers/usb/storage/sddr09.c | 1790 +++++++++++++++++++++ drivers/usb/storage/sddr55.c | 1020 ++++++++++++ drivers/usb/storage/shuttle_usbat.c | 1872 ++++++++++++++++++++++ drivers/usb/storage/sierra_ms.c | 192 +++ drivers/usb/storage/sierra_ms.h | 5 + drivers/usb/storage/transport.c | 1462 +++++++++++++++++ drivers/usb/storage/transport.h | 88 ++ drivers/usb/storage/uas-detect.h | 162 ++ drivers/usb/storage/uas.c | 1304 ++++++++++++++++ drivers/usb/storage/unusual_alauda.h | 19 + drivers/usb/storage/unusual_cypress.h | 27 + drivers/usb/storage/unusual_datafab.h | 87 ++ drivers/usb/storage/unusual_devs.h | 2521 ++++++++++++++++++++++++++++++ drivers/usb/storage/unusual_ene_ub6250.h | 10 + drivers/usb/storage/unusual_freecom.h | 14 + drivers/usb/storage/unusual_isd200.h | 45 + drivers/usb/storage/unusual_jumpshot.h | 15 + drivers/usb/storage/unusual_karma.h | 14 + drivers/usb/storage/unusual_onetouch.h | 25 + drivers/usb/storage/unusual_realtek.h | 45 + drivers/usb/storage/unusual_sddr09.h | 44 + drivers/usb/storage/unusual_sddr55.h | 32 + drivers/usb/storage/unusual_uas.h | 208 +++ drivers/usb/storage/unusual_usbat.h | 31 + drivers/usb/storage/usb.c | 1258 +++++++++++++++ drivers/usb/storage/usb.h | 210 +++ drivers/usb/storage/usual-tables.c | 93 ++ 49 files changed, 23534 insertions(+) create mode 100644 drivers/usb/storage/Kconfig create mode 100644 drivers/usb/storage/Makefile create mode 100644 drivers/usb/storage/alauda.c create mode 100644 drivers/usb/storage/cypress_atacb.c create mode 100644 drivers/usb/storage/datafab.c create mode 100644 drivers/usb/storage/debug.c create mode 100644 drivers/usb/storage/debug.h create mode 100644 drivers/usb/storage/ene_ub6250.c create mode 100644 drivers/usb/storage/freecom.c create mode 100644 drivers/usb/storage/initializers.c create mode 100644 drivers/usb/storage/initializers.h create mode 100644 drivers/usb/storage/isd200.c create mode 100644 drivers/usb/storage/jumpshot.c create mode 100644 drivers/usb/storage/karma.c create mode 100644 drivers/usb/storage/onetouch.c create mode 100644 drivers/usb/storage/option_ms.c create mode 100644 drivers/usb/storage/option_ms.h create mode 100644 drivers/usb/storage/protocol.c create mode 100644 drivers/usb/storage/protocol.h create mode 100644 drivers/usb/storage/realtek_cr.c create mode 100644 drivers/usb/storage/scsiglue.c create mode 100644 drivers/usb/storage/scsiglue.h create mode 100644 drivers/usb/storage/sddr09.c create mode 100644 drivers/usb/storage/sddr55.c create mode 100644 drivers/usb/storage/shuttle_usbat.c create mode 100644 drivers/usb/storage/sierra_ms.c create mode 100644 drivers/usb/storage/sierra_ms.h create mode 100644 drivers/usb/storage/transport.c create mode 100644 drivers/usb/storage/transport.h create mode 100644 drivers/usb/storage/uas-detect.h create mode 100644 drivers/usb/storage/uas.c create mode 100644 drivers/usb/storage/unusual_alauda.h create mode 100644 drivers/usb/storage/unusual_cypress.h create mode 100644 drivers/usb/storage/unusual_datafab.h create mode 100644 drivers/usb/storage/unusual_devs.h create mode 100644 drivers/usb/storage/unusual_ene_ub6250.h create mode 100644 drivers/usb/storage/unusual_freecom.h create mode 100644 drivers/usb/storage/unusual_isd200.h create mode 100644 drivers/usb/storage/unusual_jumpshot.h create mode 100644 drivers/usb/storage/unusual_karma.h create mode 100644 drivers/usb/storage/unusual_onetouch.h create mode 100644 drivers/usb/storage/unusual_realtek.h create mode 100644 drivers/usb/storage/unusual_sddr09.h create mode 100644 drivers/usb/storage/unusual_sddr55.h create mode 100644 drivers/usb/storage/unusual_uas.h create mode 100644 drivers/usb/storage/unusual_usbat.h create mode 100644 drivers/usb/storage/usb.c create mode 100644 drivers/usb/storage/usb.h create mode 100644 drivers/usb/storage/usual-tables.c (limited to 'drivers/usb/storage') diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig new file mode 100644 index 000000000..4be1d617d --- /dev/null +++ b/drivers/usb/storage/Kconfig @@ -0,0 +1,199 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# USB Storage driver configuration +# + +comment "NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed; see USB_STORAGE Help for more info" + +config USB_STORAGE + tristate "USB Mass Storage support" + depends on SCSI + help + Say Y here if you want to connect USB mass storage devices to your + computer's USB port. This is the driver you need for USB + floppy drives, USB hard disks, USB tape drives, USB CD-ROMs, + USB flash devices, and memory sticks, along with + similar devices. This driver may also be used for some cameras + and card readers. + + This option depends on 'SCSI' support being enabled, but you + probably also need 'SCSI device support: SCSI disk support' + (BLK_DEV_SD) for most USB storage devices. + + To compile this driver as a module, choose M here: the + module will be called usb-storage. + +if USB_STORAGE + +config USB_STORAGE_DEBUG + bool "USB Mass Storage verbose debug" + help + Say Y here in order to have the USB Mass Storage code generate + verbose debugging messages. + +config USB_STORAGE_REALTEK + tristate "Realtek Card Reader support" + help + Say Y here to include additional code to support the power-saving function + for Realtek RTS51xx USB card readers. + + If this driver is compiled as a module, it will be named ums-realtek. + +config REALTEK_AUTOPM + bool "Realtek Card Reader autosuspend support" + depends on USB_STORAGE_REALTEK && PM + default y + +config USB_STORAGE_DATAFAB + tristate "Datafab Compact Flash Reader support" + help + Support for certain Datafab CompactFlash readers. + Datafab has a web page at . + + If this driver is compiled as a module, it will be named ums-datafab. + +config USB_STORAGE_FREECOM + tristate "Freecom USB/ATAPI Bridge support" + help + Support for the Freecom USB to IDE/ATAPI adaptor. + Freecom has a web page at . + + If this driver is compiled as a module, it will be named ums-freecom. + +config USB_STORAGE_ISD200 + tristate "ISD-200 USB/ATA Bridge support" + help + Say Y here if you want to use USB Mass Store devices based + on the In-Systems Design ISD-200 USB/ATA bridge. + + Some of the products that use this chip are: + + - Archos Jukebox 6000 + - ISD SmartCable for Storage + - Taiwan Skymaster CD530U/DEL-0241 IDE bridge + - Sony CRX10U CD-R/RW drive + - CyQ've CQ8060A CDRW drive + - Planex eXtreme Drive RX-25HU USB-IDE cable (not model RX-25U) + + If this driver is compiled as a module, it will be named ums-isd200. + +config USB_STORAGE_USBAT + tristate "USBAT/USBAT02-based storage support" + help + Say Y here to include additional code to support storage devices + based on the SCM/Shuttle USBAT/USBAT02 processors. + + Devices reported to work with this driver include: + - CompactFlash reader included with Kodak DC3800 camera + - Dane-Elec Zmate CompactFlash reader + - Delkin Efilm reader2 + - HP 8200e/8210e/8230e CD-Writer Plus drives + - I-JAM JS-50U + - Jessops CompactFlash JESDCFRU BLACK + - Kingston Technology PCREAD-USB/CF + - Maxell UA4 CompactFlash reader + - Memorex UCF-100 + - Microtech ZiO! ICS-45 CF2 + - RCA LYRA MP3 portable + - Sandisk ImageMate SDDR-05b + + If this driver is compiled as a module, it will be named ums-usbat. + +config USB_STORAGE_SDDR09 + tristate "SanDisk SDDR-09 (and other SmartMedia, including DPCM) support" + help + Say Y here to include additional code to support the Sandisk SDDR-09 + SmartMedia reader in the USB Mass Storage driver. + Also works for the Microtech Zio! CompactFlash/SmartMedia reader. + + If this driver is compiled as a module, it will be named ums-sddr09. + +config USB_STORAGE_SDDR55 + tristate "SanDisk SDDR-55 SmartMedia support" + help + Say Y here to include additional code to support the Sandisk SDDR-55 + SmartMedia reader in the USB Mass Storage driver. + + If this driver is compiled as a module, it will be named ums-sddr55. + +config USB_STORAGE_JUMPSHOT + tristate "Lexar Jumpshot Compact Flash Reader" + help + Say Y here to include additional code to support the Lexar Jumpshot + USB CompactFlash reader. + + If this driver is compiled as a module, it will be named ums-jumpshot. + +config USB_STORAGE_ALAUDA + tristate "Olympus MAUSB-10/Fuji DPC-R1 support" + help + Say Y here to include additional code to support the Olympus MAUSB-10 + and Fujifilm DPC-R1 USB Card reader/writer devices. + + These devices are based on the Alauda chip and support both + XD and SmartMedia cards. + + If this driver is compiled as a module, it will be named ums-alauda. + +config USB_STORAGE_ONETOUCH + tristate "Support OneTouch Button on Maxtor Hard Drives" + depends on INPUT=y || INPUT=USB_STORAGE + help + Say Y here to include additional code to support the Maxtor OneTouch + USB hard drive's onetouch button. + + This code registers the button on the front of Maxtor OneTouch USB + hard drive's as an input device. An action can be associated with + this input in any keybinding software. (e.g. gnome's keyboard short- + cuts) + + If this driver is compiled as a module, it will be named ums-onetouch. + +config USB_STORAGE_KARMA + tristate "Support for Rio Karma music player" + help + Say Y here to include additional code to support the Rio Karma + USB interface. + + This code places the Rio Karma into mass storage mode, enabling + it to be mounted as an ordinary filesystem. Performing an eject + on the resulting scsi device node returns the Karma to normal + operation. + + If this driver is compiled as a module, it will be named ums-karma. + +config USB_STORAGE_CYPRESS_ATACB + tristate "SAT emulation on Cypress USB/ATA Bridge with ATACB" + help + Say Y here if you want to use SAT (ata pass through) on devices based + on the Cypress USB/ATA bridge supporting ATACB. This will allow you + to use tools to tune and monitor your drive (like hdparm or smartctl). + + If you say no here your device will still work with the standard usb + mass storage class. + + If this driver is compiled as a module, it will be named ums-cypress. + +config USB_STORAGE_ENE_UB6250 + tristate "USB ENE card reader support" + help + Say Y here if you wish to control a ENE SD/MS Card reader. + Note that this driver does not support SM cards. + + To compile this driver as a module, choose M here: the + module will be called ums-eneub6250. + +endif # USB_STORAGE + +config USB_UAS + tristate "USB Attached SCSI" + depends on SCSI && USB_STORAGE + help + The USB Attached SCSI protocol is supported by some USB + storage devices. It permits higher performance by supporting + multiple outstanding commands. + + If you don't know whether you have a UAS device, it is safe to + say 'Y' or 'M' here and the kernel will use the right driver. + + If you compile this driver as a module, it will be named uas. diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile new file mode 100644 index 000000000..28db337f1 --- /dev/null +++ b/drivers/usb/storage/Makefile @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the USB Mass Storage device drivers. +# +# 15 Aug 2000, Christoph Hellwig +# Rewritten to use lists instead of if-statements. +# + +ccflags-y := -I $(srctree)/drivers/scsi + +ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE='"USB_STORAGE"' + +obj-$(CONFIG_USB_UAS) += uas.o +obj-$(CONFIG_USB_STORAGE) += usb-storage.o + +usb-storage-y := scsiglue.o protocol.o transport.o usb.o +usb-storage-y += initializers.o sierra_ms.o option_ms.o +usb-storage-y += usual-tables.o +usb-storage-$(CONFIG_USB_STORAGE_DEBUG) += debug.o + +obj-$(CONFIG_USB_STORAGE_ALAUDA) += ums-alauda.o +obj-$(CONFIG_USB_STORAGE_CYPRESS_ATACB) += ums-cypress.o +obj-$(CONFIG_USB_STORAGE_DATAFAB) += ums-datafab.o +obj-$(CONFIG_USB_STORAGE_ENE_UB6250) += ums-eneub6250.o +obj-$(CONFIG_USB_STORAGE_FREECOM) += ums-freecom.o +obj-$(CONFIG_USB_STORAGE_ISD200) += ums-isd200.o +obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += ums-jumpshot.o +obj-$(CONFIG_USB_STORAGE_KARMA) += ums-karma.o +obj-$(CONFIG_USB_STORAGE_ONETOUCH) += ums-onetouch.o +obj-$(CONFIG_USB_STORAGE_REALTEK) += ums-realtek.o +obj-$(CONFIG_USB_STORAGE_SDDR09) += ums-sddr09.o +obj-$(CONFIG_USB_STORAGE_SDDR55) += ums-sddr55.o +obj-$(CONFIG_USB_STORAGE_USBAT) += ums-usbat.o + +ums-alauda-y := alauda.o +ums-cypress-y := cypress_atacb.o +ums-datafab-y := datafab.o +ums-eneub6250-y := ene_ub6250.o +ums-freecom-y := freecom.o +ums-isd200-y := isd200.o +ums-jumpshot-y := jumpshot.o +ums-karma-y := karma.o +ums-onetouch-y := onetouch.o +ums-realtek-y := realtek_cr.o +ums-sddr09-y := sddr09.o +ums-sddr55-y := sddr55.o +ums-usbat-y := shuttle_usbat.o diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c new file mode 100644 index 000000000..691fe4700 --- /dev/null +++ b/drivers/usb/storage/alauda.c @@ -0,0 +1,1274 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Alauda-based card readers + * + * Current development and maintenance by: + * (c) 2005 Daniel Drake + * + * The 'Alauda' is a chip manufacturered by RATOC for OEM use. + * + * Alauda implements a vendor-specific command set to access two media reader + * ports (XD, SmartMedia). This driver converts SCSI commands to the commands + * which are accepted by these devices. + * + * The driver was developed through reverse-engineering, with the help of the + * sddr09 driver which has many similarities, and with some help from the + * (very old) vendor-supplied GPL sma03 driver. + * + * For protocol info, see http://alauda.sourceforge.net + */ + +#include +#include + +#include +#include +#include + +#include "usb.h" +#include "transport.h" +#include "protocol.h" +#include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-alauda" + +MODULE_DESCRIPTION("Driver for Alauda-based card readers"); +MODULE_AUTHOR("Daniel Drake "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("USB_STORAGE"); + +/* + * Status bytes + */ +#define ALAUDA_STATUS_ERROR 0x01 +#define ALAUDA_STATUS_READY 0x40 + +/* + * Control opcodes (for request field) + */ +#define ALAUDA_GET_XD_MEDIA_STATUS 0x08 +#define ALAUDA_GET_SM_MEDIA_STATUS 0x98 +#define ALAUDA_ACK_XD_MEDIA_CHANGE 0x0a +#define ALAUDA_ACK_SM_MEDIA_CHANGE 0x9a +#define ALAUDA_GET_XD_MEDIA_SIG 0x86 +#define ALAUDA_GET_SM_MEDIA_SIG 0x96 + +/* + * Bulk command identity (byte 0) + */ +#define ALAUDA_BULK_CMD 0x40 + +/* + * Bulk opcodes (byte 1) + */ +#define ALAUDA_BULK_GET_REDU_DATA 0x85 +#define ALAUDA_BULK_READ_BLOCK 0x94 +#define ALAUDA_BULK_ERASE_BLOCK 0xa3 +#define ALAUDA_BULK_WRITE_BLOCK 0xb4 +#define ALAUDA_BULK_GET_STATUS2 0xb7 +#define ALAUDA_BULK_RESET_MEDIA 0xe0 + +/* + * Port to operate on (byte 8) + */ +#define ALAUDA_PORT_XD 0x00 +#define ALAUDA_PORT_SM 0x01 + +/* + * LBA and PBA are unsigned ints. Special values. + */ +#define UNDEF 0xffff +#define SPARE 0xfffe +#define UNUSABLE 0xfffd + +struct alauda_media_info { + unsigned long capacity; /* total media size in bytes */ + unsigned int pagesize; /* page size in bytes */ + unsigned int blocksize; /* number of pages per block */ + unsigned int uzonesize; /* number of usable blocks per zone */ + unsigned int zonesize; /* number of blocks per zone */ + unsigned int blockmask; /* mask to get page from address */ + + unsigned char pageshift; + unsigned char blockshift; + unsigned char zoneshift; + + u16 **lba_to_pba; /* logical to physical block map */ + u16 **pba_to_lba; /* physical to logical block map */ +}; + +struct alauda_info { + struct alauda_media_info port[2]; + int wr_ep; /* endpoint to write data out of */ + + unsigned char sense_key; + unsigned long sense_asc; /* additional sense code */ + unsigned long sense_ascq; /* additional sense code qualifier */ + + bool media_initialized; +}; + +#define short_pack(lsb,msb) ( ((u16)(lsb)) | ( ((u16)(msb))<<8 ) ) +#define LSB_of(s) ((s)&0xFF) +#define MSB_of(s) ((s)>>8) + +#define MEDIA_PORT(us) us->srb->device->lun +#define MEDIA_INFO(us) ((struct alauda_info *)us->extra)->port[MEDIA_PORT(us)] + +#define PBA_LO(pba) ((pba & 0xF) << 5) +#define PBA_HI(pba) (pba >> 3) +#define PBA_ZONE(pba) (pba >> 11) + +static int init_alauda(struct us_data *us); + + +/* + * The table of devices + */ +#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ + vendorName, productName, useProtocol, useTransport, \ + initFunction, flags) \ +{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ + .driver_info = (flags) } + +static const struct usb_device_id alauda_usb_ids[] = { +# include "unusual_alauda.h" + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, alauda_usb_ids); + +#undef UNUSUAL_DEV + +/* + * The flags table + */ +#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ + vendor_name, product_name, use_protocol, use_transport, \ + init_function, Flags) \ +{ \ + .vendorName = vendor_name, \ + .productName = product_name, \ + .useProtocol = use_protocol, \ + .useTransport = use_transport, \ + .initFunction = init_function, \ +} + +static const struct us_unusual_dev alauda_unusual_dev_list[] = { +# include "unusual_alauda.h" + { } /* Terminating entry */ +}; + +#undef UNUSUAL_DEV + + +/* + * Media handling + */ + +struct alauda_card_info { + unsigned char id; /* id byte */ + unsigned char chipshift; /* 1< LBA mappings for a particular port + */ +static void alauda_free_maps (struct alauda_media_info *media_info) +{ + unsigned int shift = media_info->zoneshift + + media_info->blockshift + media_info->pageshift; + unsigned int num_zones = media_info->capacity >> shift; + unsigned int i; + + if (media_info->lba_to_pba != NULL) + for (i = 0; i < num_zones; i++) { + kfree(media_info->lba_to_pba[i]); + media_info->lba_to_pba[i] = NULL; + } + + if (media_info->pba_to_lba != NULL) + for (i = 0; i < num_zones; i++) { + kfree(media_info->pba_to_lba[i]); + media_info->pba_to_lba[i] = NULL; + } +} + +/* + * Returns 2 bytes of status data + * The first byte describes media status, and second byte describes door status + */ +static int alauda_get_media_status(struct us_data *us, unsigned char *data) +{ + int rc; + unsigned char command; + + if (MEDIA_PORT(us) == ALAUDA_PORT_XD) + command = ALAUDA_GET_XD_MEDIA_STATUS; + else + command = ALAUDA_GET_SM_MEDIA_STATUS; + + rc = usb_stor_ctrl_transfer(us, us->recv_ctrl_pipe, + command, 0xc0, 0, 1, data, 2); + + if (rc == USB_STOR_XFER_GOOD) + usb_stor_dbg(us, "Media status %02X %02X\n", data[0], data[1]); + + return rc; +} + +/* + * Clears the "media was changed" bit so that we know when it changes again + * in the future. + */ +static int alauda_ack_media(struct us_data *us) +{ + unsigned char command; + + if (MEDIA_PORT(us) == ALAUDA_PORT_XD) + command = ALAUDA_ACK_XD_MEDIA_CHANGE; + else + command = ALAUDA_ACK_SM_MEDIA_CHANGE; + + return usb_stor_ctrl_transfer(us, us->send_ctrl_pipe, + command, 0x40, 0, 1, NULL, 0); +} + +/* + * Retrieves a 4-byte media signature, which indicates manufacturer, capacity, + * and some other details. + */ +static int alauda_get_media_signature(struct us_data *us, unsigned char *data) +{ + unsigned char command; + + if (MEDIA_PORT(us) == ALAUDA_PORT_XD) + command = ALAUDA_GET_XD_MEDIA_SIG; + else + command = ALAUDA_GET_SM_MEDIA_SIG; + + return usb_stor_ctrl_transfer(us, us->recv_ctrl_pipe, + command, 0xc0, 0, 0, data, 4); +} + +/* + * Resets the media status (but not the whole device?) + */ +static int alauda_reset_media(struct us_data *us) +{ + unsigned char *command = us->iobuf; + + memset(command, 0, 9); + command[0] = ALAUDA_BULK_CMD; + command[1] = ALAUDA_BULK_RESET_MEDIA; + command[8] = MEDIA_PORT(us); + + return usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + command, 9, NULL); +} + +/* + * Examines the media and deduces capacity, etc. + */ +static int alauda_init_media(struct us_data *us) +{ + unsigned char *data = us->iobuf; + int ready = 0; + const struct alauda_card_info *media_info; + unsigned int num_zones; + + while (ready == 0) { + msleep(20); + + if (alauda_get_media_status(us, data) != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + if (data[0] & 0x10) + ready = 1; + } + + usb_stor_dbg(us, "We are ready for action!\n"); + + if (alauda_ack_media(us) != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + msleep(10); + + if (alauda_get_media_status(us, data) != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + if (data[0] != 0x14) { + usb_stor_dbg(us, "Media not ready after ack\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + if (alauda_get_media_signature(us, data) != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + usb_stor_dbg(us, "Media signature: %4ph\n", data); + media_info = alauda_card_find_id(data[1]); + if (media_info == NULL) { + pr_warn("alauda_init_media: Unrecognised media signature: %4ph\n", + data); + return USB_STOR_TRANSPORT_ERROR; + } + + MEDIA_INFO(us).capacity = 1 << media_info->chipshift; + usb_stor_dbg(us, "Found media with capacity: %ldMB\n", + MEDIA_INFO(us).capacity >> 20); + + MEDIA_INFO(us).pageshift = media_info->pageshift; + MEDIA_INFO(us).blockshift = media_info->blockshift; + MEDIA_INFO(us).zoneshift = media_info->zoneshift; + + MEDIA_INFO(us).pagesize = 1 << media_info->pageshift; + MEDIA_INFO(us).blocksize = 1 << media_info->blockshift; + MEDIA_INFO(us).zonesize = 1 << media_info->zoneshift; + + MEDIA_INFO(us).uzonesize = ((1 << media_info->zoneshift) / 128) * 125; + MEDIA_INFO(us).blockmask = MEDIA_INFO(us).blocksize - 1; + + num_zones = MEDIA_INFO(us).capacity >> (MEDIA_INFO(us).zoneshift + + MEDIA_INFO(us).blockshift + MEDIA_INFO(us).pageshift); + MEDIA_INFO(us).pba_to_lba = kcalloc(num_zones, sizeof(u16*), GFP_NOIO); + MEDIA_INFO(us).lba_to_pba = kcalloc(num_zones, sizeof(u16*), GFP_NOIO); + if (MEDIA_INFO(us).pba_to_lba == NULL || MEDIA_INFO(us).lba_to_pba == NULL) + return USB_STOR_TRANSPORT_ERROR; + + if (alauda_reset_media(us) != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Examines the media status and does the right thing when the media has gone, + * appeared, or changed. + */ +static int alauda_check_media(struct us_data *us) +{ + struct alauda_info *info = (struct alauda_info *) us->extra; + unsigned char *status = us->iobuf; + int rc; + + rc = alauda_get_media_status(us, status); + if (rc != USB_STOR_XFER_GOOD) { + status[0] = 0xF0; /* Pretend there's no media */ + status[1] = 0; + } + + /* Check for no media or door open */ + if ((status[0] & 0x80) || ((status[0] & 0x1F) == 0x10) + || ((status[1] & 0x01) == 0)) { + usb_stor_dbg(us, "No media, or door open\n"); + alauda_free_maps(&MEDIA_INFO(us)); + info->sense_key = 0x02; + info->sense_asc = 0x3A; + info->sense_ascq = 0x00; + return USB_STOR_TRANSPORT_FAILED; + } + + /* Check for media change */ + if (status[0] & 0x08 || !info->media_initialized) { + usb_stor_dbg(us, "Media change detected\n"); + alauda_free_maps(&MEDIA_INFO(us)); + rc = alauda_init_media(us); + if (rc == USB_STOR_TRANSPORT_GOOD) + info->media_initialized = true; + info->sense_key = UNIT_ATTENTION; + info->sense_asc = 0x28; + info->sense_ascq = 0x00; + return USB_STOR_TRANSPORT_FAILED; + } + + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Checks the status from the 2nd status register + * Returns 3 bytes of status data, only the first is known + */ +static int alauda_check_status2(struct us_data *us) +{ + int rc; + unsigned char command[] = { + ALAUDA_BULK_CMD, ALAUDA_BULK_GET_STATUS2, + 0, 0, 0, 0, 3, 0, MEDIA_PORT(us) + }; + unsigned char data[3]; + + rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + command, 9, NULL); + if (rc != USB_STOR_XFER_GOOD) + return rc; + + rc = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + data, 3, NULL); + if (rc != USB_STOR_XFER_GOOD) + return rc; + + usb_stor_dbg(us, "%3ph\n", data); + if (data[0] & ALAUDA_STATUS_ERROR) + return USB_STOR_XFER_ERROR; + + return USB_STOR_XFER_GOOD; +} + +/* + * Gets the redundancy data for the first page of a PBA + * Returns 16 bytes. + */ +static int alauda_get_redu_data(struct us_data *us, u16 pba, unsigned char *data) +{ + int rc; + unsigned char command[] = { + ALAUDA_BULK_CMD, ALAUDA_BULK_GET_REDU_DATA, + PBA_HI(pba), PBA_ZONE(pba), 0, PBA_LO(pba), 0, 0, MEDIA_PORT(us) + }; + + rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + command, 9, NULL); + if (rc != USB_STOR_XFER_GOOD) + return rc; + + return usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + data, 16, NULL); +} + +/* + * Finds the first unused PBA in a zone + * Returns the absolute PBA of an unused PBA, or 0 if none found. + */ +static u16 alauda_find_unused_pba(struct alauda_media_info *info, + unsigned int zone) +{ + u16 *pba_to_lba = info->pba_to_lba[zone]; + unsigned int i; + + for (i = 0; i < info->zonesize; i++) + if (pba_to_lba[i] == UNDEF) + return (zone << info->zoneshift) + i; + + return 0; +} + +/* + * Reads the redundancy data for all PBA's in a zone + * Produces lba <--> pba mappings + */ +static int alauda_read_map(struct us_data *us, unsigned int zone) +{ + unsigned char *data = us->iobuf; + int result; + int i, j; + unsigned int zonesize = MEDIA_INFO(us).zonesize; + unsigned int uzonesize = MEDIA_INFO(us).uzonesize; + unsigned int lba_offset, lba_real, blocknum; + unsigned int zone_base_lba = zone * uzonesize; + unsigned int zone_base_pba = zone * zonesize; + u16 *lba_to_pba = kcalloc(zonesize, sizeof(u16), GFP_NOIO); + u16 *pba_to_lba = kcalloc(zonesize, sizeof(u16), GFP_NOIO); + if (lba_to_pba == NULL || pba_to_lba == NULL) { + result = USB_STOR_TRANSPORT_ERROR; + goto error; + } + + usb_stor_dbg(us, "Mapping blocks for zone %d\n", zone); + + /* 1024 PBA's per zone */ + for (i = 0; i < zonesize; i++) + lba_to_pba[i] = pba_to_lba[i] = UNDEF; + + for (i = 0; i < zonesize; i++) { + blocknum = zone_base_pba + i; + + result = alauda_get_redu_data(us, blocknum, data); + if (result != USB_STOR_XFER_GOOD) { + result = USB_STOR_TRANSPORT_ERROR; + goto error; + } + + /* special PBAs have control field 0^16 */ + for (j = 0; j < 16; j++) + if (data[j] != 0) + goto nonz; + pba_to_lba[i] = UNUSABLE; + usb_stor_dbg(us, "PBA %d has no logical mapping\n", blocknum); + continue; + + nonz: + /* unwritten PBAs have control field FF^16 */ + for (j = 0; j < 16; j++) + if (data[j] != 0xff) + goto nonff; + continue; + + nonff: + /* normal PBAs start with six FFs */ + if (j < 6) { + usb_stor_dbg(us, "PBA %d has no logical mapping: reserved area = %02X%02X%02X%02X data status %02X block status %02X\n", + blocknum, + data[0], data[1], data[2], data[3], + data[4], data[5]); + pba_to_lba[i] = UNUSABLE; + continue; + } + + if ((data[6] >> 4) != 0x01) { + usb_stor_dbg(us, "PBA %d has invalid address field %02X%02X/%02X%02X\n", + blocknum, data[6], data[7], + data[11], data[12]); + pba_to_lba[i] = UNUSABLE; + continue; + } + + /* check even parity */ + if (parity[data[6] ^ data[7]]) { + printk(KERN_WARNING + "alauda_read_map: Bad parity in LBA for block %d" + " (%02X %02X)\n", i, data[6], data[7]); + pba_to_lba[i] = UNUSABLE; + continue; + } + + lba_offset = short_pack(data[7], data[6]); + lba_offset = (lba_offset & 0x07FF) >> 1; + lba_real = lba_offset + zone_base_lba; + + /* + * Every 1024 physical blocks ("zone"), the LBA numbers + * go back to zero, but are within a higher block of LBA's. + * Also, there is a maximum of 1000 LBA's per zone. + * In other words, in PBA 1024-2047 you will find LBA 0-999 + * which are really LBA 1000-1999. This allows for 24 bad + * or special physical blocks per zone. + */ + + if (lba_offset >= uzonesize) { + printk(KERN_WARNING + "alauda_read_map: Bad low LBA %d for block %d\n", + lba_real, blocknum); + continue; + } + + if (lba_to_pba[lba_offset] != UNDEF) { + printk(KERN_WARNING + "alauda_read_map: " + "LBA %d seen for PBA %d and %d\n", + lba_real, lba_to_pba[lba_offset], blocknum); + continue; + } + + pba_to_lba[i] = lba_real; + lba_to_pba[lba_offset] = blocknum; + continue; + } + + MEDIA_INFO(us).lba_to_pba[zone] = lba_to_pba; + MEDIA_INFO(us).pba_to_lba[zone] = pba_to_lba; + result = 0; + goto out; + +error: + kfree(lba_to_pba); + kfree(pba_to_lba); +out: + return result; +} + +/* + * Checks to see whether we have already mapped a certain zone + * If we haven't, the map is generated + */ +static void alauda_ensure_map_for_zone(struct us_data *us, unsigned int zone) +{ + if (MEDIA_INFO(us).lba_to_pba[zone] == NULL + || MEDIA_INFO(us).pba_to_lba[zone] == NULL) + alauda_read_map(us, zone); +} + +/* + * Erases an entire block + */ +static int alauda_erase_block(struct us_data *us, u16 pba) +{ + int rc; + unsigned char command[] = { + ALAUDA_BULK_CMD, ALAUDA_BULK_ERASE_BLOCK, PBA_HI(pba), + PBA_ZONE(pba), 0, PBA_LO(pba), 0x02, 0, MEDIA_PORT(us) + }; + unsigned char buf[2]; + + usb_stor_dbg(us, "Erasing PBA %d\n", pba); + + rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + command, 9, NULL); + if (rc != USB_STOR_XFER_GOOD) + return rc; + + rc = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + buf, 2, NULL); + if (rc != USB_STOR_XFER_GOOD) + return rc; + + usb_stor_dbg(us, "Erase result: %02X %02X\n", buf[0], buf[1]); + return rc; +} + +/* + * Reads data from a certain offset page inside a PBA, including interleaved + * redundancy data. Returns (pagesize+64)*pages bytes in data. + */ +static int alauda_read_block_raw(struct us_data *us, u16 pba, + unsigned int page, unsigned int pages, unsigned char *data) +{ + int rc; + unsigned char command[] = { + ALAUDA_BULK_CMD, ALAUDA_BULK_READ_BLOCK, PBA_HI(pba), + PBA_ZONE(pba), 0, PBA_LO(pba) + page, pages, 0, MEDIA_PORT(us) + }; + + usb_stor_dbg(us, "pba %d page %d count %d\n", pba, page, pages); + + rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + command, 9, NULL); + if (rc != USB_STOR_XFER_GOOD) + return rc; + + return usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + data, (MEDIA_INFO(us).pagesize + 64) * pages, NULL); +} + +/* + * Reads data from a certain offset page inside a PBA, excluding redundancy + * data. Returns pagesize*pages bytes in data. Note that data must be big enough + * to hold (pagesize+64)*pages bytes of data, but you can ignore those 'extra' + * trailing bytes outside this function. + */ +static int alauda_read_block(struct us_data *us, u16 pba, + unsigned int page, unsigned int pages, unsigned char *data) +{ + int i, rc; + unsigned int pagesize = MEDIA_INFO(us).pagesize; + + rc = alauda_read_block_raw(us, pba, page, pages, data); + if (rc != USB_STOR_XFER_GOOD) + return rc; + + /* Cut out the redundancy data */ + for (i = 0; i < pages; i++) { + int dest_offset = i * pagesize; + int src_offset = i * (pagesize + 64); + memmove(data + dest_offset, data + src_offset, pagesize); + } + + return rc; +} + +/* + * Writes an entire block of data and checks status after write. + * Redundancy data must be already included in data. Data should be + * (pagesize+64)*blocksize bytes in length. + */ +static int alauda_write_block(struct us_data *us, u16 pba, unsigned char *data) +{ + int rc; + struct alauda_info *info = (struct alauda_info *) us->extra; + unsigned char command[] = { + ALAUDA_BULK_CMD, ALAUDA_BULK_WRITE_BLOCK, PBA_HI(pba), + PBA_ZONE(pba), 0, PBA_LO(pba), 32, 0, MEDIA_PORT(us) + }; + + usb_stor_dbg(us, "pba %d\n", pba); + + rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + command, 9, NULL); + if (rc != USB_STOR_XFER_GOOD) + return rc; + + rc = usb_stor_bulk_transfer_buf(us, info->wr_ep, data, + (MEDIA_INFO(us).pagesize + 64) * MEDIA_INFO(us).blocksize, + NULL); + if (rc != USB_STOR_XFER_GOOD) + return rc; + + return alauda_check_status2(us); +} + +/* + * Write some data to a specific LBA. + */ +static int alauda_write_lba(struct us_data *us, u16 lba, + unsigned int page, unsigned int pages, + unsigned char *ptr, unsigned char *blockbuffer) +{ + u16 pba, lbap, new_pba; + unsigned char *bptr, *cptr, *xptr; + unsigned char ecc[3]; + int i, result; + unsigned int uzonesize = MEDIA_INFO(us).uzonesize; + unsigned int zonesize = MEDIA_INFO(us).zonesize; + unsigned int pagesize = MEDIA_INFO(us).pagesize; + unsigned int blocksize = MEDIA_INFO(us).blocksize; + unsigned int lba_offset = lba % uzonesize; + unsigned int new_pba_offset; + unsigned int zone = lba / uzonesize; + + alauda_ensure_map_for_zone(us, zone); + + pba = MEDIA_INFO(us).lba_to_pba[zone][lba_offset]; + if (pba == 1) { + /* + * Maybe it is impossible to write to PBA 1. + * Fake success, but don't do anything. + */ + printk(KERN_WARNING + "alauda_write_lba: avoid writing to pba 1\n"); + return USB_STOR_TRANSPORT_GOOD; + } + + new_pba = alauda_find_unused_pba(&MEDIA_INFO(us), zone); + if (!new_pba) { + printk(KERN_WARNING + "alauda_write_lba: Out of unused blocks\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + /* read old contents */ + if (pba != UNDEF) { + result = alauda_read_block_raw(us, pba, 0, + blocksize, blockbuffer); + if (result != USB_STOR_XFER_GOOD) + return result; + } else { + memset(blockbuffer, 0, blocksize * (pagesize + 64)); + } + + lbap = (lba_offset << 1) | 0x1000; + if (parity[MSB_of(lbap) ^ LSB_of(lbap)]) + lbap ^= 1; + + /* check old contents and fill lba */ + for (i = 0; i < blocksize; i++) { + bptr = blockbuffer + (i * (pagesize + 64)); + cptr = bptr + pagesize; + nand_compute_ecc(bptr, ecc); + if (!nand_compare_ecc(cptr+13, ecc)) { + usb_stor_dbg(us, "Warning: bad ecc in page %d- of pba %d\n", + i, pba); + nand_store_ecc(cptr+13, ecc); + } + nand_compute_ecc(bptr + (pagesize / 2), ecc); + if (!nand_compare_ecc(cptr+8, ecc)) { + usb_stor_dbg(us, "Warning: bad ecc in page %d+ of pba %d\n", + i, pba); + nand_store_ecc(cptr+8, ecc); + } + cptr[6] = cptr[11] = MSB_of(lbap); + cptr[7] = cptr[12] = LSB_of(lbap); + } + + /* copy in new stuff and compute ECC */ + xptr = ptr; + for (i = page; i < page+pages; i++) { + bptr = blockbuffer + (i * (pagesize + 64)); + cptr = bptr + pagesize; + memcpy(bptr, xptr, pagesize); + xptr += pagesize; + nand_compute_ecc(bptr, ecc); + nand_store_ecc(cptr+13, ecc); + nand_compute_ecc(bptr + (pagesize / 2), ecc); + nand_store_ecc(cptr+8, ecc); + } + + result = alauda_write_block(us, new_pba, blockbuffer); + if (result != USB_STOR_XFER_GOOD) + return result; + + new_pba_offset = new_pba - (zone * zonesize); + MEDIA_INFO(us).pba_to_lba[zone][new_pba_offset] = lba; + MEDIA_INFO(us).lba_to_pba[zone][lba_offset] = new_pba; + usb_stor_dbg(us, "Remapped LBA %d to PBA %d\n", lba, new_pba); + + if (pba != UNDEF) { + unsigned int pba_offset = pba - (zone * zonesize); + result = alauda_erase_block(us, pba); + if (result != USB_STOR_XFER_GOOD) + return result; + MEDIA_INFO(us).pba_to_lba[zone][pba_offset] = UNDEF; + } + + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Read data from a specific sector address + */ +static int alauda_read_data(struct us_data *us, unsigned long address, + unsigned int sectors) +{ + unsigned char *buffer; + u16 lba, max_lba; + unsigned int page, len, offset; + unsigned int blockshift = MEDIA_INFO(us).blockshift; + unsigned int pageshift = MEDIA_INFO(us).pageshift; + unsigned int blocksize = MEDIA_INFO(us).blocksize; + unsigned int pagesize = MEDIA_INFO(us).pagesize; + unsigned int uzonesize = MEDIA_INFO(us).uzonesize; + struct scatterlist *sg; + int result; + + /* + * Since we only read in one block at a time, we have to create + * a bounce buffer and move the data a piece at a time between the + * bounce buffer and the actual transfer buffer. + * We make this buffer big enough to hold temporary redundancy data, + * which we use when reading the data blocks. + */ + + len = min(sectors, blocksize) * (pagesize + 64); + buffer = kmalloc(len, GFP_NOIO); + if (!buffer) + return USB_STOR_TRANSPORT_ERROR; + + /* Figure out the initial LBA and page */ + lba = address >> blockshift; + page = (address & MEDIA_INFO(us).blockmask); + max_lba = MEDIA_INFO(us).capacity >> (blockshift + pageshift); + + result = USB_STOR_TRANSPORT_GOOD; + offset = 0; + sg = NULL; + + while (sectors > 0) { + unsigned int zone = lba / uzonesize; /* integer division */ + unsigned int lba_offset = lba - (zone * uzonesize); + unsigned int pages; + u16 pba; + alauda_ensure_map_for_zone(us, zone); + + /* Not overflowing capacity? */ + if (lba >= max_lba) { + usb_stor_dbg(us, "Error: Requested lba %u exceeds maximum %u\n", + lba, max_lba); + result = USB_STOR_TRANSPORT_ERROR; + break; + } + + /* Find number of pages we can read in this block */ + pages = min(sectors, blocksize - page); + len = pages << pageshift; + + /* Find where this lba lives on disk */ + pba = MEDIA_INFO(us).lba_to_pba[zone][lba_offset]; + + if (pba == UNDEF) { /* this lba was never written */ + usb_stor_dbg(us, "Read %d zero pages (LBA %d) page %d\n", + pages, lba, page); + + /* + * This is not really an error. It just means + * that the block has never been written. + * Instead of returning USB_STOR_TRANSPORT_ERROR + * it is better to return all zero data. + */ + + memset(buffer, 0, len); + } else { + usb_stor_dbg(us, "Read %d pages, from PBA %d (LBA %d) page %d\n", + pages, pba, lba, page); + + result = alauda_read_block(us, pba, page, pages, buffer); + if (result != USB_STOR_TRANSPORT_GOOD) + break; + } + + /* Store the data in the transfer buffer */ + usb_stor_access_xfer_buf(buffer, len, us->srb, + &sg, &offset, TO_XFER_BUF); + + page = 0; + lba++; + sectors -= pages; + } + + kfree(buffer); + return result; +} + +/* + * Write data to a specific sector address + */ +static int alauda_write_data(struct us_data *us, unsigned long address, + unsigned int sectors) +{ + unsigned char *buffer, *blockbuffer; + unsigned int page, len, offset; + unsigned int blockshift = MEDIA_INFO(us).blockshift; + unsigned int pageshift = MEDIA_INFO(us).pageshift; + unsigned int blocksize = MEDIA_INFO(us).blocksize; + unsigned int pagesize = MEDIA_INFO(us).pagesize; + struct scatterlist *sg; + u16 lba, max_lba; + int result; + + /* + * Since we don't write the user data directly to the device, + * we have to create a bounce buffer and move the data a piece + * at a time between the bounce buffer and the actual transfer buffer. + */ + + len = min(sectors, blocksize) * pagesize; + buffer = kmalloc(len, GFP_NOIO); + if (!buffer) + return USB_STOR_TRANSPORT_ERROR; + + /* + * We also need a temporary block buffer, where we read in the old data, + * overwrite parts with the new data, and manipulate the redundancy data + */ + blockbuffer = kmalloc_array(pagesize + 64, blocksize, GFP_NOIO); + if (!blockbuffer) { + kfree(buffer); + return USB_STOR_TRANSPORT_ERROR; + } + + /* Figure out the initial LBA and page */ + lba = address >> blockshift; + page = (address & MEDIA_INFO(us).blockmask); + max_lba = MEDIA_INFO(us).capacity >> (pageshift + blockshift); + + result = USB_STOR_TRANSPORT_GOOD; + offset = 0; + sg = NULL; + + while (sectors > 0) { + /* Write as many sectors as possible in this block */ + unsigned int pages = min(sectors, blocksize - page); + len = pages << pageshift; + + /* Not overflowing capacity? */ + if (lba >= max_lba) { + usb_stor_dbg(us, "Requested lba %u exceeds maximum %u\n", + lba, max_lba); + result = USB_STOR_TRANSPORT_ERROR; + break; + } + + /* Get the data from the transfer buffer */ + usb_stor_access_xfer_buf(buffer, len, us->srb, + &sg, &offset, FROM_XFER_BUF); + + result = alauda_write_lba(us, lba, page, pages, buffer, + blockbuffer); + if (result != USB_STOR_TRANSPORT_GOOD) + break; + + page = 0; + lba++; + sectors -= pages; + } + + kfree(buffer); + kfree(blockbuffer); + return result; +} + +/* + * Our interface with the rest of the world + */ + +static void alauda_info_destructor(void *extra) +{ + struct alauda_info *info = (struct alauda_info *) extra; + int port; + + if (!info) + return; + + for (port = 0; port < 2; port++) { + struct alauda_media_info *media_info = &info->port[port]; + + alauda_free_maps(media_info); + kfree(media_info->lba_to_pba); + kfree(media_info->pba_to_lba); + } +} + +/* + * Initialize alauda_info struct and find the data-write endpoint + */ +static int init_alauda(struct us_data *us) +{ + struct alauda_info *info; + struct usb_host_interface *altsetting = us->pusb_intf->cur_altsetting; + nand_init_ecc(); + + us->extra = kzalloc_obj(struct alauda_info, GFP_NOIO); + if (!us->extra) + return -ENOMEM; + + info = (struct alauda_info *) us->extra; + us->extra_destructor = alauda_info_destructor; + + info->wr_ep = usb_sndbulkpipe(us->pusb_dev, + altsetting->endpoint[0].desc.bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK); + + return 0; +} + +static int alauda_transport(struct scsi_cmnd *srb, struct us_data *us) +{ + int rc; + struct alauda_info *info = (struct alauda_info *) us->extra; + unsigned char *ptr = us->iobuf; + static const unsigned char inquiry_response[36] = { + 0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00 + }; + + if (srb->cmnd[0] == INQUIRY) { + usb_stor_dbg(us, "INQUIRY - Returning bogus response\n"); + memcpy(ptr, inquiry_response, sizeof(inquiry_response)); + fill_inquiry_response(us, ptr, 36); + return USB_STOR_TRANSPORT_GOOD; + } + + if (srb->cmnd[0] == TEST_UNIT_READY) { + usb_stor_dbg(us, "TEST_UNIT_READY\n"); + return alauda_check_media(us); + } + + if (srb->cmnd[0] == READ_CAPACITY) { + unsigned int num_zones; + unsigned long capacity; + + rc = alauda_check_media(us); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; + + num_zones = MEDIA_INFO(us).capacity >> (MEDIA_INFO(us).zoneshift + + MEDIA_INFO(us).blockshift + MEDIA_INFO(us).pageshift); + + capacity = num_zones * MEDIA_INFO(us).uzonesize + * MEDIA_INFO(us).blocksize; + + /* Report capacity and page size */ + ((__be32 *) ptr)[0] = cpu_to_be32(capacity - 1); + ((__be32 *) ptr)[1] = cpu_to_be32(512); + + usb_stor_set_xfer_buf(ptr, 8, srb); + return USB_STOR_TRANSPORT_GOOD; + } + + if (srb->cmnd[0] == READ_10) { + unsigned int page, pages; + + rc = alauda_check_media(us); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; + + page = short_pack(srb->cmnd[3], srb->cmnd[2]); + page <<= 16; + page |= short_pack(srb->cmnd[5], srb->cmnd[4]); + pages = short_pack(srb->cmnd[8], srb->cmnd[7]); + + usb_stor_dbg(us, "READ_10: page %d pagect %d\n", page, pages); + + return alauda_read_data(us, page, pages); + } + + if (srb->cmnd[0] == WRITE_10) { + unsigned int page, pages; + + rc = alauda_check_media(us); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; + + page = short_pack(srb->cmnd[3], srb->cmnd[2]); + page <<= 16; + page |= short_pack(srb->cmnd[5], srb->cmnd[4]); + pages = short_pack(srb->cmnd[8], srb->cmnd[7]); + + usb_stor_dbg(us, "WRITE_10: page %d pagect %d\n", page, pages); + + return alauda_write_data(us, page, pages); + } + + if (srb->cmnd[0] == REQUEST_SENSE) { + usb_stor_dbg(us, "REQUEST_SENSE\n"); + + memset(ptr, 0, 18); + ptr[0] = 0xF0; + ptr[2] = info->sense_key; + ptr[7] = 11; + ptr[12] = info->sense_asc; + ptr[13] = info->sense_ascq; + usb_stor_set_xfer_buf(ptr, 18, srb); + + return USB_STOR_TRANSPORT_GOOD; + } + + if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) { + /* + * sure. whatever. not like we can stop the user from popping + * the media out of the device (no locking doors, etc) + */ + return USB_STOR_TRANSPORT_GOOD; + } + + usb_stor_dbg(us, "Gah! Unknown command: %d (0x%x)\n", + srb->cmnd[0], srb->cmnd[0]); + info->sense_key = 0x05; + info->sense_asc = 0x20; + info->sense_ascq = 0x00; + return USB_STOR_TRANSPORT_FAILED; +} + +static struct scsi_host_template alauda_host_template; + +static int alauda_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct us_data *us; + int result; + + result = usb_stor_probe1(&us, intf, id, + (id - alauda_usb_ids) + alauda_unusual_dev_list, + &alauda_host_template); + if (result) + return result; + + us->transport_name = "Alauda Control/Bulk"; + us->transport = alauda_transport; + us->transport_reset = usb_stor_Bulk_reset; + us->max_lun = 1; + + result = usb_stor_probe2(us); + return result; +} + +static struct usb_driver alauda_driver = { + .name = DRV_NAME, + .probe = alauda_probe, + .disconnect = usb_stor_disconnect, + .suspend = usb_stor_suspend, + .resume = usb_stor_resume, + .reset_resume = usb_stor_reset_resume, + .pre_reset = usb_stor_pre_reset, + .post_reset = usb_stor_post_reset, + .id_table = alauda_usb_ids, + .soft_unbind = 1, + .no_dynamic_id = 1, +}; + +module_usb_stor_driver(alauda_driver, alauda_host_template, DRV_NAME); diff --git a/drivers/usb/storage/cypress_atacb.c b/drivers/usb/storage/cypress_atacb.c new file mode 100644 index 000000000..2fce5f95b --- /dev/null +++ b/drivers/usb/storage/cypress_atacb.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Support for emulating SAT (ata pass through) on devices based + * on the Cypress USB/ATA bridge supporting ATACB. + * + * Copyright (c) 2008 Matthieu Castet (castet.matthieu@free.fr) + */ + +#include +#include +#include +#include +#include + +#include "usb.h" +#include "protocol.h" +#include "scsiglue.h" +#include "debug.h" + +#define DRV_NAME "ums-cypress" + +MODULE_DESCRIPTION("SAT support for Cypress USB/ATA bridges with ATACB"); +MODULE_AUTHOR("Matthieu Castet "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("USB_STORAGE"); + +/* + * The table of devices + */ +#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ + vendorName, productName, useProtocol, useTransport, \ + initFunction, flags) \ +{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ + .driver_info = (flags) } + +static const struct usb_device_id cypress_usb_ids[] = { +# include "unusual_cypress.h" + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, cypress_usb_ids); + +#undef UNUSUAL_DEV + +/* + * The flags table + */ +#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ + vendor_name, product_name, use_protocol, use_transport, \ + init_function, Flags) \ +{ \ + .vendorName = vendor_name, \ + .productName = product_name, \ + .useProtocol = use_protocol, \ + .useTransport = use_transport, \ + .initFunction = init_function, \ +} + +static const struct us_unusual_dev cypress_unusual_dev_list[] = { +# include "unusual_cypress.h" + { } /* Terminating entry */ +}; + +#undef UNUSUAL_DEV + + +/* + * ATACB is a protocol used on cypress usb<->ata bridge to + * send raw ATA command over mass storage + * There is a ATACB2 protocol that support LBA48 on newer chip. + * More info that be found on cy7c68310_8.pdf and cy7c68300c_8.pdf + * datasheet from cypress.com. + */ +static void cypress_atacb_passthrough(struct scsi_cmnd *srb, struct us_data *us) +{ + unsigned char save_cmnd[MAX_COMMAND_SIZE]; + + if (likely(srb->cmnd[0] != ATA_16 && srb->cmnd[0] != ATA_12)) { + usb_stor_transparent_scsi_command(srb, us); + return; + } + + memcpy(save_cmnd, srb->cmnd, sizeof(save_cmnd)); + memset(srb->cmnd, 0, MAX_COMMAND_SIZE); + + /* check if we support the command */ + if (save_cmnd[1] >> 5) /* MULTIPLE_COUNT */ + goto invalid_fld; + /* check protocol */ + switch ((save_cmnd[1] >> 1) & 0xf) { + case 3: /*no DATA */ + case 4: /* PIO in */ + case 5: /* PIO out */ + break; + default: + goto invalid_fld; + } + + /* first build the ATACB command */ + srb->cmd_len = 16; + + srb->cmnd[0] = 0x24; /* + * bVSCBSignature : vendor-specific command + * this value can change, but most(all ?) manufacturers + * keep the cypress default : 0x24 + */ + srb->cmnd[1] = 0x24; /* bVSCBSubCommand : 0x24 for ATACB */ + + srb->cmnd[3] = 0xff - 1; /* + * features, sector count, lba low, lba med + * lba high, device, command are valid + */ + srb->cmnd[4] = 1; /* TransferBlockCount : 512 */ + + if (save_cmnd[0] == ATA_16) { + srb->cmnd[ 6] = save_cmnd[ 4]; /* features */ + srb->cmnd[ 7] = save_cmnd[ 6]; /* sector count */ + srb->cmnd[ 8] = save_cmnd[ 8]; /* lba low */ + srb->cmnd[ 9] = save_cmnd[10]; /* lba med */ + srb->cmnd[10] = save_cmnd[12]; /* lba high */ + srb->cmnd[11] = save_cmnd[13]; /* device */ + srb->cmnd[12] = save_cmnd[14]; /* command */ + + if (save_cmnd[1] & 0x01) {/* extended bit set for LBA48 */ + /* this could be supported by atacb2 */ + if (save_cmnd[3] || save_cmnd[5] || save_cmnd[7] || save_cmnd[9] + || save_cmnd[11]) + goto invalid_fld; + } + } else { /* ATA12 */ + srb->cmnd[ 6] = save_cmnd[3]; /* features */ + srb->cmnd[ 7] = save_cmnd[4]; /* sector count */ + srb->cmnd[ 8] = save_cmnd[5]; /* lba low */ + srb->cmnd[ 9] = save_cmnd[6]; /* lba med */ + srb->cmnd[10] = save_cmnd[7]; /* lba high */ + srb->cmnd[11] = save_cmnd[8]; /* device */ + srb->cmnd[12] = save_cmnd[9]; /* command */ + + } + /* Filter SET_FEATURES - XFER MODE command */ + if ((srb->cmnd[12] == ATA_CMD_SET_FEATURES) + && (srb->cmnd[6] == SETFEATURES_XFER)) + goto invalid_fld; + + if (srb->cmnd[12] == ATA_CMD_ID_ATA || srb->cmnd[12] == ATA_CMD_ID_ATAPI) + srb->cmnd[2] |= (1<<7); /* set IdentifyPacketDevice for these cmds */ + + + usb_stor_transparent_scsi_command(srb, us); + + /* if the device doesn't support ATACB */ + if (srb->result == SAM_STAT_CHECK_CONDITION && + memcmp(srb->sense_buffer, usb_stor_sense_invalidCDB, + sizeof(usb_stor_sense_invalidCDB)) == 0) { + usb_stor_dbg(us, "cypress atacb not supported ???\n"); + goto end; + } + + /* + * if ck_cond flags is set, and there wasn't critical error, + * build the special sense + */ + if ((srb->result != (DID_ERROR << 16) && + srb->result != (DID_ABORT << 16)) && + save_cmnd[2] & 0x20) { + struct scsi_eh_save ses; + unsigned char regs[8]; + unsigned char *sb = srb->sense_buffer; + unsigned char *desc = sb + 8; + int tmp_result; + + /* build the command for reading the ATA registers */ + scsi_eh_prep_cmnd(srb, &ses, NULL, 0, sizeof(regs)); + + /* + * we use the same command as before, but we set + * the read taskfile bit, for not executing atacb command, + * but reading register selected in srb->cmnd[4] + */ + srb->cmd_len = 16; + srb->cmnd[2] = 1; + + usb_stor_transparent_scsi_command(srb, us); + memcpy(regs, srb->sense_buffer, sizeof(regs)); + tmp_result = srb->result; + scsi_eh_restore_cmnd(srb, &ses); + /* we fail to get registers, report invalid command */ + if (tmp_result != SAM_STAT_GOOD) + goto invalid_fld; + + /* build the sense */ + memset(sb, 0, SCSI_SENSE_BUFFERSIZE); + + /* set sk, asc for a good command */ + sb[1] = RECOVERED_ERROR; + sb[2] = 0; /* ATA PASS THROUGH INFORMATION AVAILABLE */ + sb[3] = 0x1D; + + /* + * XXX we should generate sk, asc, ascq from status and error + * regs + * (see 11.1 Error translation ATA device error to SCSI error + * map, and ata_to_sense_error from libata.) + */ + + /* Sense data is current and format is descriptor. */ + sb[0] = 0x72; + desc[0] = 0x09; /* ATA_RETURN_DESCRIPTOR */ + + /* set length of additional sense data */ + sb[7] = 14; + desc[1] = 12; + + /* Copy registers into sense buffer. */ + desc[ 2] = 0x00; + desc[ 3] = regs[1]; /* features */ + desc[ 5] = regs[2]; /* sector count */ + desc[ 7] = regs[3]; /* lba low */ + desc[ 9] = regs[4]; /* lba med */ + desc[11] = regs[5]; /* lba high */ + desc[12] = regs[6]; /* device */ + desc[13] = regs[7]; /* command */ + + srb->result = SAM_STAT_CHECK_CONDITION; + } + goto end; +invalid_fld: + srb->result = SAM_STAT_CHECK_CONDITION; + + memcpy(srb->sense_buffer, + usb_stor_sense_invalidCDB, + sizeof(usb_stor_sense_invalidCDB)); +end: + memcpy(srb->cmnd, save_cmnd, sizeof(save_cmnd)); + if (srb->cmnd[0] == ATA_12) + srb->cmd_len = 12; +} + +static struct scsi_host_template cypress_host_template; + +static int cypress_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct us_data *us; + int result; + struct usb_device *device; + + result = usb_stor_probe1(&us, intf, id, + (id - cypress_usb_ids) + cypress_unusual_dev_list, + &cypress_host_template); + if (result) + return result; + + /* + * Among CY7C68300 chips, the A revision does not support Cypress ATACB + * Filter out this revision from EEPROM default descriptor values + */ + device = interface_to_usbdev(intf); + if (device->descriptor.iManufacturer != 0x38 || + device->descriptor.iProduct != 0x4e || + device->descriptor.iSerialNumber != 0x64) { + us->protocol_name = "Transparent SCSI with Cypress ATACB"; + us->proto_handler = cypress_atacb_passthrough; + } else { + us->protocol_name = "Transparent SCSI"; + us->proto_handler = usb_stor_transparent_scsi_command; + } + + result = usb_stor_probe2(us); + return result; +} + +static struct usb_driver cypress_driver = { + .name = DRV_NAME, + .probe = cypress_probe, + .disconnect = usb_stor_disconnect, + .suspend = usb_stor_suspend, + .resume = usb_stor_resume, + .reset_resume = usb_stor_reset_resume, + .pre_reset = usb_stor_pre_reset, + .post_reset = usb_stor_post_reset, + .id_table = cypress_usb_ids, + .soft_unbind = 1, + .no_dynamic_id = 1, +}; + +module_usb_stor_driver(cypress_driver, cypress_host_template, DRV_NAME); diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c new file mode 100644 index 000000000..0ded8ef72 --- /dev/null +++ b/drivers/usb/storage/datafab.c @@ -0,0 +1,757 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Datafab USB Compact Flash reader + * + * datafab driver v0.1: + * + * First release + * + * Current development and maintenance by: + * (c) 2000 Jimmie Mayfield (mayfield+datafab@sackheads.org) + * + * Many thanks to Robert Baruch for the SanDisk SmartMedia reader driver + * which I used as a template for this driver. + * + * Some bugfixes and scatter-gather code by Gregory P. Smith + * (greg-usb@electricrain.com) + * + * Fix for media change by Joerg Schneider (js@joergschneider.com) + * + * Other contributors: + * (c) 2002 Alan Stern + */ + +/* + * This driver attempts to support USB CompactFlash reader/writer devices + * based on Datafab USB-to-ATA chips. It was specifically developed for the + * Datafab MDCFE-B USB CompactFlash reader but has since been found to work + * with a variety of Datafab-based devices from a number of manufacturers. + * I've received a report of this driver working with a Datafab-based + * SmartMedia device though please be aware that I'm personally unable to + * test SmartMedia support. + * + * This driver supports reading and writing. If you're truly paranoid, + * however, you can force the driver into a write-protected state by setting + * the WP enable bits in datafab_handle_mode_sense(). See the comments + * in that routine. + */ + +#include +#include +#include + +#include +#include + +#include "usb.h" +#include "transport.h" +#include "protocol.h" +#include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-datafab" + +MODULE_DESCRIPTION("Driver for Datafab USB Compact Flash reader"); +MODULE_AUTHOR("Jimmie Mayfield "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("USB_STORAGE"); + +struct datafab_info { + unsigned long sectors; /* total sector count */ + unsigned long ssize; /* sector size in bytes */ + signed char lun; /* used for dual-slot readers */ + + /* the following aren't used yet */ + unsigned char sense_key; + unsigned long sense_asc; /* additional sense code */ + unsigned long sense_ascq; /* additional sense code qualifier */ +}; + +static int datafab_determine_lun(struct us_data *us, + struct datafab_info *info); + + +/* + * The table of devices + */ +#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ + vendorName, productName, useProtocol, useTransport, \ + initFunction, flags) \ +{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ + .driver_info = (flags) } + +static const struct usb_device_id datafab_usb_ids[] = { +# include "unusual_datafab.h" + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, datafab_usb_ids); + +#undef UNUSUAL_DEV + +/* + * The flags table + */ +#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ + vendor_name, product_name, use_protocol, use_transport, \ + init_function, Flags) \ +{ \ + .vendorName = vendor_name, \ + .productName = product_name, \ + .useProtocol = use_protocol, \ + .useTransport = use_transport, \ + .initFunction = init_function, \ +} + +static const struct us_unusual_dev datafab_unusual_dev_list[] = { +# include "unusual_datafab.h" + { } /* Terminating entry */ +}; + +#undef UNUSUAL_DEV + + +static inline int +datafab_bulk_read(struct us_data *us, unsigned char *data, unsigned int len) { + if (len == 0) + return USB_STOR_XFER_GOOD; + + usb_stor_dbg(us, "len = %d\n", len); + return usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + data, len, NULL); +} + + +static inline int +datafab_bulk_write(struct us_data *us, unsigned char *data, unsigned int len) { + if (len == 0) + return USB_STOR_XFER_GOOD; + + usb_stor_dbg(us, "len = %d\n", len); + return usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + data, len, NULL); +} + + +static int datafab_read_data(struct us_data *us, + struct datafab_info *info, + u32 sector, + u32 sectors) +{ + unsigned char *command = us->iobuf; + unsigned char *buffer; + unsigned char thistime; + unsigned int totallen, alloclen; + int len, result; + unsigned int sg_offset = 0; + struct scatterlist *sg = NULL; + + // we're working in LBA mode. according to the ATA spec, + // we can support up to 28-bit addressing. I don't know if Datafab + // supports beyond 24-bit addressing. It's kind of hard to test + // since it requires > 8GB CF card. + // + if (sectors > 0x0FFFFFFF) + return USB_STOR_TRANSPORT_ERROR; + + if (info->lun == -1) { + result = datafab_determine_lun(us, info); + if (result != USB_STOR_TRANSPORT_GOOD) + return result; + } + + totallen = sectors * info->ssize; + + // Since we don't read more than 64 KB at a time, we have to create + // a bounce buffer and move the data a piece at a time between the + // bounce buffer and the actual transfer buffer. + + alloclen = min(totallen, 65536u); + buffer = kmalloc(alloclen, GFP_NOIO); + if (buffer == NULL) + return USB_STOR_TRANSPORT_ERROR; + + do { + // loop, never allocate or transfer more than 64k at once + // (min(128k, 255*info->ssize) is the real limit) + + len = min(totallen, alloclen); + thistime = (len / info->ssize) & 0xff; + + command[0] = 0; + command[1] = thistime; + command[2] = sector & 0xFF; + command[3] = (sector >> 8) & 0xFF; + command[4] = (sector >> 16) & 0xFF; + + command[5] = 0xE0 + (info->lun << 4); + command[5] |= (sector >> 24) & 0x0F; + command[6] = 0x20; + command[7] = 0x01; + + // send the read command + result = datafab_bulk_write(us, command, 8); + if (result != USB_STOR_XFER_GOOD) + goto leave; + + // read the result + result = datafab_bulk_read(us, buffer, len); + if (result != USB_STOR_XFER_GOOD) + goto leave; + + // Store the data in the transfer buffer + usb_stor_access_xfer_buf(buffer, len, us->srb, + &sg, &sg_offset, TO_XFER_BUF); + + sector += thistime; + totallen -= len; + } while (totallen > 0); + + kfree(buffer); + return USB_STOR_TRANSPORT_GOOD; + + leave: + kfree(buffer); + return USB_STOR_TRANSPORT_ERROR; +} + + +static int datafab_write_data(struct us_data *us, + struct datafab_info *info, + u32 sector, + u32 sectors) +{ + unsigned char *command = us->iobuf; + unsigned char *reply = us->iobuf; + unsigned char *buffer; + unsigned char thistime; + unsigned int totallen, alloclen; + int len, result; + unsigned int sg_offset = 0; + struct scatterlist *sg = NULL; + + // we're working in LBA mode. according to the ATA spec, + // we can support up to 28-bit addressing. I don't know if Datafab + // supports beyond 24-bit addressing. It's kind of hard to test + // since it requires > 8GB CF card. + // + if (sectors > 0x0FFFFFFF) + return USB_STOR_TRANSPORT_ERROR; + + if (info->lun == -1) { + result = datafab_determine_lun(us, info); + if (result != USB_STOR_TRANSPORT_GOOD) + return result; + } + + totallen = sectors * info->ssize; + + // Since we don't write more than 64 KB at a time, we have to create + // a bounce buffer and move the data a piece at a time between the + // bounce buffer and the actual transfer buffer. + + alloclen = min(totallen, 65536u); + buffer = kmalloc(alloclen, GFP_NOIO); + if (buffer == NULL) + return USB_STOR_TRANSPORT_ERROR; + + do { + // loop, never allocate or transfer more than 64k at once + // (min(128k, 255*info->ssize) is the real limit) + + len = min(totallen, alloclen); + thistime = (len / info->ssize) & 0xff; + + // Get the data from the transfer buffer + usb_stor_access_xfer_buf(buffer, len, us->srb, + &sg, &sg_offset, FROM_XFER_BUF); + + command[0] = 0; + command[1] = thistime; + command[2] = sector & 0xFF; + command[3] = (sector >> 8) & 0xFF; + command[4] = (sector >> 16) & 0xFF; + + command[5] = 0xE0 + (info->lun << 4); + command[5] |= (sector >> 24) & 0x0F; + command[6] = 0x30; + command[7] = 0x02; + + // send the command + result = datafab_bulk_write(us, command, 8); + if (result != USB_STOR_XFER_GOOD) + goto leave; + + // send the data + result = datafab_bulk_write(us, buffer, len); + if (result != USB_STOR_XFER_GOOD) + goto leave; + + // read the result + result = datafab_bulk_read(us, reply, 2); + if (result != USB_STOR_XFER_GOOD) + goto leave; + + if (reply[0] != 0x50 && reply[1] != 0) { + usb_stor_dbg(us, "Gah! write return code: %02x %02x\n", + reply[0], reply[1]); + goto leave; + } + + sector += thistime; + totallen -= len; + } while (totallen > 0); + + kfree(buffer); + return USB_STOR_TRANSPORT_GOOD; + + leave: + kfree(buffer); + return USB_STOR_TRANSPORT_ERROR; +} + + +static int datafab_determine_lun(struct us_data *us, + struct datafab_info *info) +{ + // Dual-slot readers can be thought of as dual-LUN devices. + // We need to determine which card slot is being used. + // We'll send an IDENTIFY DEVICE command and see which LUN responds... + // + // There might be a better way of doing this? + + static const unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 }; + unsigned char *command = us->iobuf; + unsigned char *buf; + int count = 0, rc; + + if (!info) + return USB_STOR_TRANSPORT_ERROR; + + memcpy(command, scommand, 8); + buf = kmalloc(512, GFP_NOIO); + if (!buf) + return USB_STOR_TRANSPORT_ERROR; + + usb_stor_dbg(us, "locating...\n"); + + // we'll try 3 times before giving up... + // + while (count++ < 3) { + command[5] = 0xa0; + + rc = datafab_bulk_write(us, command, 8); + if (rc != USB_STOR_XFER_GOOD) { + rc = USB_STOR_TRANSPORT_ERROR; + goto leave; + } + + rc = datafab_bulk_read(us, buf, 512); + if (rc == USB_STOR_XFER_GOOD) { + info->lun = 0; + rc = USB_STOR_TRANSPORT_GOOD; + goto leave; + } + + command[5] = 0xb0; + + rc = datafab_bulk_write(us, command, 8); + if (rc != USB_STOR_XFER_GOOD) { + rc = USB_STOR_TRANSPORT_ERROR; + goto leave; + } + + rc = datafab_bulk_read(us, buf, 512); + if (rc == USB_STOR_XFER_GOOD) { + info->lun = 1; + rc = USB_STOR_TRANSPORT_GOOD; + goto leave; + } + + msleep(20); + } + + rc = USB_STOR_TRANSPORT_ERROR; + + leave: + kfree(buf); + return rc; +} + +static int datafab_id_device(struct us_data *us, + struct datafab_info *info) +{ + // this is a variation of the ATA "IDENTIFY DEVICE" command...according + // to the ATA spec, 'Sector Count' isn't used but the Windows driver + // sets this bit so we do too... + // + static const unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 }; + unsigned char *command = us->iobuf; + unsigned char *reply; + int rc; + + if (!info) + return USB_STOR_TRANSPORT_ERROR; + + if (info->lun == -1) { + rc = datafab_determine_lun(us, info); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; + } + + memcpy(command, scommand, 8); + reply = kmalloc(512, GFP_NOIO); + if (!reply) + return USB_STOR_TRANSPORT_ERROR; + + command[5] += (info->lun << 4); + + rc = datafab_bulk_write(us, command, 8); + if (rc != USB_STOR_XFER_GOOD) { + rc = USB_STOR_TRANSPORT_ERROR; + goto leave; + } + + // we'll go ahead and extract the media capacity while we're here... + // + rc = datafab_bulk_read(us, reply, 512); + if (rc == USB_STOR_XFER_GOOD) { + // capacity is at word offset 57-58 + // + info->sectors = ((u32)(reply[117]) << 24) | + ((u32)(reply[116]) << 16) | + ((u32)(reply[115]) << 8) | + ((u32)(reply[114]) ); + rc = USB_STOR_TRANSPORT_GOOD; + goto leave; + } + + rc = USB_STOR_TRANSPORT_ERROR; + + leave: + kfree(reply); + return rc; +} + + +static int datafab_handle_mode_sense(struct us_data *us, + struct scsi_cmnd * srb, + int sense_6) +{ + static const unsigned char rw_err_page[12] = { + 0x1, 0xA, 0x21, 1, 0, 0, 0, 0, 1, 0, 0, 0 + }; + static const unsigned char cache_page[12] = { + 0x8, 0xA, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + static const unsigned char rbac_page[12] = { + 0x1B, 0xA, 0, 0x81, 0, 0, 0, 0, 0, 0, 0, 0 + }; + static const unsigned char timer_page[8] = { + 0x1C, 0x6, 0, 0, 0, 0 + }; + unsigned char pc, page_code; + unsigned int i = 0; + struct datafab_info *info = (struct datafab_info *) (us->extra); + unsigned char *ptr = us->iobuf; + + // most of this stuff is just a hack to get things working. the + // datafab reader doesn't present a SCSI interface so we + // fudge the SCSI commands... + // + + pc = srb->cmnd[2] >> 6; + page_code = srb->cmnd[2] & 0x3F; + + switch (pc) { + case 0x0: + usb_stor_dbg(us, "Current values\n"); + break; + case 0x1: + usb_stor_dbg(us, "Changeable values\n"); + break; + case 0x2: + usb_stor_dbg(us, "Default values\n"); + break; + case 0x3: + usb_stor_dbg(us, "Saves values\n"); + break; + } + + memset(ptr, 0, 8); + if (sense_6) { + ptr[2] = 0x00; // WP enable: 0x80 + i = 4; + } else { + ptr[3] = 0x00; // WP enable: 0x80 + i = 8; + } + + switch (page_code) { + default: + // vendor-specific mode + info->sense_key = 0x05; + info->sense_asc = 0x24; + info->sense_ascq = 0x00; + return USB_STOR_TRANSPORT_FAILED; + + case 0x1: + memcpy(ptr + i, rw_err_page, sizeof(rw_err_page)); + i += sizeof(rw_err_page); + break; + + case 0x8: + memcpy(ptr + i, cache_page, sizeof(cache_page)); + i += sizeof(cache_page); + break; + + case 0x1B: + memcpy(ptr + i, rbac_page, sizeof(rbac_page)); + i += sizeof(rbac_page); + break; + + case 0x1C: + memcpy(ptr + i, timer_page, sizeof(timer_page)); + i += sizeof(timer_page); + break; + + case 0x3F: // retrieve all pages + memcpy(ptr + i, timer_page, sizeof(timer_page)); + i += sizeof(timer_page); + memcpy(ptr + i, rbac_page, sizeof(rbac_page)); + i += sizeof(rbac_page); + memcpy(ptr + i, cache_page, sizeof(cache_page)); + i += sizeof(cache_page); + memcpy(ptr + i, rw_err_page, sizeof(rw_err_page)); + i += sizeof(rw_err_page); + break; + } + + if (sense_6) + ptr[0] = i - 1; + else + ((__be16 *) ptr)[0] = cpu_to_be16(i - 2); + usb_stor_set_xfer_buf(ptr, i, srb); + + return USB_STOR_TRANSPORT_GOOD; +} + +static void datafab_info_destructor(void *extra) +{ + // this routine is a placeholder... + // currently, we don't allocate any extra memory so we're okay +} + + +// Transport for the Datafab MDCFE-B +// +static int datafab_transport(struct scsi_cmnd *srb, struct us_data *us) +{ + struct datafab_info *info; + int rc; + unsigned long block, blocks; + unsigned char *ptr = us->iobuf; + static const unsigned char inquiry_reply[8] = { + 0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00 + }; + + if (!us->extra) { + us->extra = kzalloc_obj(struct datafab_info, GFP_NOIO); + if (!us->extra) + return USB_STOR_TRANSPORT_ERROR; + + us->extra_destructor = datafab_info_destructor; + ((struct datafab_info *)us->extra)->lun = -1; + } + + info = (struct datafab_info *) (us->extra); + + if (srb->cmnd[0] == INQUIRY) { + usb_stor_dbg(us, "INQUIRY - Returning bogus response\n"); + memcpy(ptr, inquiry_reply, sizeof(inquiry_reply)); + fill_inquiry_response(us, ptr, 36); + return USB_STOR_TRANSPORT_GOOD; + } + + if (srb->cmnd[0] == READ_CAPACITY) { + info->ssize = 0x200; // hard coded 512 byte sectors as per ATA spec + rc = datafab_id_device(us, info); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; + + usb_stor_dbg(us, "READ_CAPACITY: %ld sectors, %ld bytes per sector\n", + info->sectors, info->ssize); + + // build the reply + // we need the last sector, not the number of sectors + ((__be32 *) ptr)[0] = cpu_to_be32(info->sectors - 1); + ((__be32 *) ptr)[1] = cpu_to_be32(info->ssize); + usb_stor_set_xfer_buf(ptr, 8, srb); + + return USB_STOR_TRANSPORT_GOOD; + } + + if (srb->cmnd[0] == MODE_SELECT_10) { + usb_stor_dbg(us, "Gah! MODE_SELECT_10\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + // don't bother implementing READ_6 or WRITE_6. + // + if (srb->cmnd[0] == READ_10) { + block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) | + ((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5])); + + blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8])); + + usb_stor_dbg(us, "READ_10: read block 0x%04lx count %ld\n", + block, blocks); + return datafab_read_data(us, info, block, blocks); + } + + if (srb->cmnd[0] == READ_12) { + // we'll probably never see a READ_12 but we'll do it anyway... + // + block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) | + ((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5])); + + blocks = ((u32)(srb->cmnd[6]) << 24) | ((u32)(srb->cmnd[7]) << 16) | + ((u32)(srb->cmnd[8]) << 8) | ((u32)(srb->cmnd[9])); + + usb_stor_dbg(us, "READ_12: read block 0x%04lx count %ld\n", + block, blocks); + return datafab_read_data(us, info, block, blocks); + } + + if (srb->cmnd[0] == WRITE_10) { + block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) | + ((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5])); + + blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8])); + + usb_stor_dbg(us, "WRITE_10: write block 0x%04lx count %ld\n", + block, blocks); + return datafab_write_data(us, info, block, blocks); + } + + if (srb->cmnd[0] == WRITE_12) { + // we'll probably never see a WRITE_12 but we'll do it anyway... + // + block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) | + ((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5])); + + blocks = ((u32)(srb->cmnd[6]) << 24) | ((u32)(srb->cmnd[7]) << 16) | + ((u32)(srb->cmnd[8]) << 8) | ((u32)(srb->cmnd[9])); + + usb_stor_dbg(us, "WRITE_12: write block 0x%04lx count %ld\n", + block, blocks); + return datafab_write_data(us, info, block, blocks); + } + + if (srb->cmnd[0] == TEST_UNIT_READY) { + usb_stor_dbg(us, "TEST_UNIT_READY\n"); + return datafab_id_device(us, info); + } + + if (srb->cmnd[0] == REQUEST_SENSE) { + usb_stor_dbg(us, "REQUEST_SENSE - Returning faked response\n"); + + // this response is pretty bogus right now. eventually if necessary + // we can set the correct sense data. so far though it hasn't been + // necessary + // + memset(ptr, 0, 18); + ptr[0] = 0xF0; + ptr[2] = info->sense_key; + ptr[7] = 11; + ptr[12] = info->sense_asc; + ptr[13] = info->sense_ascq; + usb_stor_set_xfer_buf(ptr, 18, srb); + + return USB_STOR_TRANSPORT_GOOD; + } + + if (srb->cmnd[0] == MODE_SENSE) { + usb_stor_dbg(us, "MODE_SENSE_6 detected\n"); + return datafab_handle_mode_sense(us, srb, 1); + } + + if (srb->cmnd[0] == MODE_SENSE_10) { + usb_stor_dbg(us, "MODE_SENSE_10 detected\n"); + return datafab_handle_mode_sense(us, srb, 0); + } + + if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) { + /* + * sure. whatever. not like we can stop the user from + * popping the media out of the device (no locking doors, etc) + */ + return USB_STOR_TRANSPORT_GOOD; + } + + if (srb->cmnd[0] == START_STOP) { + /* + * this is used by sd.c'check_scsidisk_media_change to detect + * media change + */ + usb_stor_dbg(us, "START_STOP\n"); + /* + * the first datafab_id_device after a media change returns + * an error (determined experimentally) + */ + rc = datafab_id_device(us, info); + if (rc == USB_STOR_TRANSPORT_GOOD) { + info->sense_key = NO_SENSE; + srb->result = SUCCESS; + } else { + info->sense_key = UNIT_ATTENTION; + srb->result = SAM_STAT_CHECK_CONDITION; + } + return rc; + } + + usb_stor_dbg(us, "Gah! Unknown command: %d (0x%x)\n", + srb->cmnd[0], srb->cmnd[0]); + info->sense_key = 0x05; + info->sense_asc = 0x20; + info->sense_ascq = 0x00; + return USB_STOR_TRANSPORT_FAILED; +} + +static struct scsi_host_template datafab_host_template; + +static int datafab_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct us_data *us; + int result; + + result = usb_stor_probe1(&us, intf, id, + (id - datafab_usb_ids) + datafab_unusual_dev_list, + &datafab_host_template); + if (result) + return result; + + us->transport_name = "Datafab Bulk-Only"; + us->transport = datafab_transport; + us->transport_reset = usb_stor_Bulk_reset; + us->max_lun = 1; + + result = usb_stor_probe2(us); + return result; +} + +static struct usb_driver datafab_driver = { + .name = DRV_NAME, + .probe = datafab_probe, + .disconnect = usb_stor_disconnect, + .suspend = usb_stor_suspend, + .resume = usb_stor_resume, + .reset_resume = usb_stor_reset_resume, + .pre_reset = usb_stor_pre_reset, + .post_reset = usb_stor_post_reset, + .id_table = datafab_usb_ids, + .soft_unbind = 1, + .no_dynamic_id = 1, +}; + +module_usb_stor_driver(datafab_driver, datafab_host_template, DRV_NAME); diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c new file mode 100644 index 000000000..dda610f68 --- /dev/null +++ b/drivers/usb/storage/debug.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for USB Mass Storage compliant devices + * Debugging Functions Source Code File + * + * Current development and maintenance by: + * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * Developed with the assistance of: + * (c) 2002 Alan Stern + * + * Initial work by: + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + */ + +#include +#include +#include +#include +#include +#include + +#include "usb.h" +#include "debug.h" + + +void usb_stor_show_command(const struct us_data *us, struct scsi_cmnd *srb) +{ + char *what = NULL; + + switch (srb->cmnd[0]) { + case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break; + case REZERO_UNIT: what = "REZERO_UNIT"; break; + case REQUEST_SENSE: what = "REQUEST_SENSE"; break; + case FORMAT_UNIT: what = "FORMAT_UNIT"; break; + case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break; + case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break; + case READ_6: what = "READ_6"; break; + case WRITE_6: what = "WRITE_6"; break; + case SEEK_6: what = "SEEK_6"; break; + case READ_REVERSE: what = "READ_REVERSE"; break; + case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break; + case SPACE: what = "SPACE"; break; + case INQUIRY: what = "INQUIRY"; break; + case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break; + case MODE_SELECT: what = "MODE_SELECT"; break; + case RESERVE_6: what = "RESERVE"; break; + case RELEASE_6: what = "RELEASE"; break; + case COPY: what = "COPY"; break; + case ERASE: what = "ERASE"; break; + case MODE_SENSE: what = "MODE_SENSE"; break; + case START_STOP: what = "START_STOP"; break; + case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break; + case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break; + case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break; + case SET_WINDOW: what = "SET_WINDOW"; break; + case READ_CAPACITY: what = "READ_CAPACITY"; break; + case READ_10: what = "READ_10"; break; + case WRITE_10: what = "WRITE_10"; break; + case SEEK_10: what = "SEEK_10"; break; + case WRITE_VERIFY: what = "WRITE_VERIFY"; break; + case VERIFY: what = "VERIFY"; break; + case SEARCH_HIGH: what = "SEARCH_HIGH"; break; + case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break; + case SEARCH_LOW: what = "SEARCH_LOW"; break; + case SET_LIMITS: what = "SET_LIMITS"; break; + case READ_POSITION: what = "READ_POSITION"; break; + case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break; + case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break; + case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break; + case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break; + case COMPARE: what = "COMPARE"; break; + case COPY_VERIFY: what = "COPY_VERIFY"; break; + case WRITE_BUFFER: what = "WRITE_BUFFER"; break; + case READ_BUFFER: what = "READ_BUFFER"; break; + case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break; + case READ_LONG: what = "READ_LONG"; break; + case WRITE_LONG: what = "WRITE_LONG"; break; + case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break; + case WRITE_SAME: what = "WRITE_SAME"; break; + case GPCMD_READ_SUBCHANNEL: what = "READ SUBCHANNEL"; break; + case READ_TOC: what = "READ_TOC"; break; + case GPCMD_READ_HEADER: what = "READ HEADER"; break; + case GPCMD_PLAY_AUDIO_10: what = "PLAY AUDIO (10)"; break; + case GPCMD_PLAY_AUDIO_MSF: what = "PLAY AUDIO MSF"; break; + case GPCMD_GET_EVENT_STATUS_NOTIFICATION: + what = "GET EVENT/STATUS NOTIFICATION"; break; + case GPCMD_PAUSE_RESUME: what = "PAUSE/RESUME"; break; + case LOG_SELECT: what = "LOG_SELECT"; break; + case LOG_SENSE: what = "LOG_SENSE"; break; + case GPCMD_STOP_PLAY_SCAN: what = "STOP PLAY/SCAN"; break; + case GPCMD_READ_DISC_INFO: what = "READ DISC INFORMATION"; break; + case GPCMD_READ_TRACK_RZONE_INFO: + what = "READ TRACK INFORMATION"; break; + case GPCMD_RESERVE_RZONE_TRACK: what = "RESERVE TRACK"; break; + case GPCMD_SEND_OPC: what = "SEND OPC"; break; + case MODE_SELECT_10: what = "MODE_SELECT_10"; break; + case GPCMD_REPAIR_RZONE_TRACK: what = "REPAIR TRACK"; break; + case 0x59: what = "READ MASTER CUE"; break; + case MODE_SENSE_10: what = "MODE_SENSE_10"; break; + case GPCMD_CLOSE_TRACK: what = "CLOSE TRACK/SESSION"; break; + case 0x5C: what = "READ BUFFER CAPACITY"; break; + case 0x5D: what = "SEND CUE SHEET"; break; + case GPCMD_BLANK: what = "BLANK"; break; + case REPORT_LUNS: what = "REPORT LUNS"; break; + case MOVE_MEDIUM: what = "MOVE_MEDIUM or PLAY AUDIO (12)"; break; + case READ_12: what = "READ_12"; break; + case WRITE_12: what = "WRITE_12"; break; + case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break; + case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break; + case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break; + case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break; + case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break; + case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break; + case GPCMD_READ_CD_MSF: what = "READ CD MSF"; break; + case GPCMD_SCAN: what = "SCAN"; break; + case GPCMD_SET_SPEED: what = "SET CD SPEED"; break; + case GPCMD_MECHANISM_STATUS: what = "MECHANISM STATUS"; break; + case GPCMD_READ_CD: what = "READ CD"; break; + case 0xE1: what = "WRITE CONTINUE"; break; + case WRITE_LONG_2: what = "WRITE_LONG_2"; break; + default: what = "(unknown command)"; break; + } + usb_stor_dbg(us, "Command %s (%d bytes)\n", what, srb->cmd_len); + usb_stor_dbg(us, "bytes: %*ph\n", min_t(int, srb->cmd_len, 16), + (const unsigned char *)srb->cmnd); +} + +void usb_stor_show_sense(const struct us_data *us, + unsigned char key, + unsigned char asc, + unsigned char ascq) +{ + const char *what, *keystr, *fmt; + + keystr = scsi_sense_key_string(key); + what = scsi_extd_sense_format(asc, ascq, &fmt); + + if (keystr == NULL) + keystr = "(Unknown Key)"; + if (what == NULL) + what = "(unknown ASC/ASCQ)"; + + if (fmt) + usb_stor_dbg(us, "%s: %s (%s%x)\n", keystr, what, fmt, ascq); + else + usb_stor_dbg(us, "%s: %s\n", keystr, what); +} + +void usb_stor_dbg(const struct us_data *us, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + dev_vprintk_emit(LOGLEVEL_DEBUG, &us->pusb_dev->dev, fmt, args); + + va_end(args); +} +EXPORT_SYMBOL_GPL(usb_stor_dbg); diff --git a/drivers/usb/storage/debug.h b/drivers/usb/storage/debug.h new file mode 100644 index 000000000..a6505ceb6 --- /dev/null +++ b/drivers/usb/storage/debug.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Driver for USB Mass Storage compliant devices + * Debugging Functions Header File + * + * Current development and maintenance by: + * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * Initial work by: + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + */ + +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +#include + +#ifdef CONFIG_USB_STORAGE_DEBUG +void usb_stor_show_command(const struct us_data *us, struct scsi_cmnd *srb); +void usb_stor_show_sense(const struct us_data *us, unsigned char key, + unsigned char asc, unsigned char ascq); +__printf(2, 3) void usb_stor_dbg(const struct us_data *us, + const char *fmt, ...); + +#define US_DEBUG(x) x +#else +__printf(2, 3) +static inline void _usb_stor_dbg(const struct us_data *us, + const char *fmt, ...) +{ +} +#define usb_stor_dbg(us, fmt, ...) \ + do { if (0) _usb_stor_dbg(us, fmt, ##__VA_ARGS__); } while (0) +#define US_DEBUG(x) +#endif + +#endif diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c new file mode 100644 index 000000000..895f90c7a --- /dev/null +++ b/drivers/usb/storage/ene_ub6250.c @@ -0,0 +1,2443 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include +#include +#include +#include + +#include +#include + +#include + +#include "usb.h" +#include "transport.h" +#include "protocol.h" +#include "debug.h" +#include "scsiglue.h" + +#define SD_INIT1_FIRMWARE "ene-ub6250/sd_init1.bin" +#define SD_INIT2_FIRMWARE "ene-ub6250/sd_init2.bin" +#define SD_RW_FIRMWARE "ene-ub6250/sd_rdwr.bin" +#define MS_INIT_FIRMWARE "ene-ub6250/ms_init.bin" +#define MSP_RW_FIRMWARE "ene-ub6250/msp_rdwr.bin" +#define MS_RW_FIRMWARE "ene-ub6250/ms_rdwr.bin" + +#define DRV_NAME "ums_eneub6250" + +MODULE_DESCRIPTION("Driver for ENE UB6250 reader"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("USB_STORAGE"); +MODULE_FIRMWARE(SD_INIT1_FIRMWARE); +MODULE_FIRMWARE(SD_INIT2_FIRMWARE); +MODULE_FIRMWARE(SD_RW_FIRMWARE); +MODULE_FIRMWARE(MS_INIT_FIRMWARE); +MODULE_FIRMWARE(MSP_RW_FIRMWARE); +MODULE_FIRMWARE(MS_RW_FIRMWARE); + +/* + * The table of devices + */ +#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ + vendorName, productName, useProtocol, useTransport, \ + initFunction, flags) \ +{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ + .driver_info = (flags)} + +static const struct usb_device_id ene_ub6250_usb_ids[] = { +# include "unusual_ene_ub6250.h" + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, ene_ub6250_usb_ids); + +#undef UNUSUAL_DEV + +/* + * The flags table + */ +#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ + vendor_name, product_name, use_protocol, use_transport, \ + init_function, Flags) \ +{ \ + .vendorName = vendor_name, \ + .productName = product_name, \ + .useProtocol = use_protocol, \ + .useTransport = use_transport, \ + .initFunction = init_function, \ +} + +static const struct us_unusual_dev ene_ub6250_unusual_dev_list[] = { +# include "unusual_ene_ub6250.h" + { } /* Terminating entry */ +}; + +#undef UNUSUAL_DEV + + + +/* ENE bin code len */ +#define ENE_BIN_CODE_LEN 0x800 +/* EnE HW Register */ +#define REG_CARD_STATUS 0xFF83 +#define REG_HW_TRAP1 0xFF89 + +/* SRB Status */ +#define SS_SUCCESS 0x000000 /* No Sense */ +#define SS_NOT_READY 0x023A00 /* Medium not present */ +#define SS_MEDIUM_ERR 0x031100 /* Unrecovered read error */ +#define SS_HW_ERR 0x040800 /* Communication failure */ +#define SS_ILLEGAL_REQUEST 0x052000 /* Invalid command */ +#define SS_UNIT_ATTENTION 0x062900 /* Reset occurred */ + +/* ENE Load FW Pattern */ +#define SD_INIT1_PATTERN 1 +#define SD_INIT2_PATTERN 2 +#define SD_RW_PATTERN 3 +#define MS_INIT_PATTERN 4 +#define MSP_RW_PATTERN 5 +#define MS_RW_PATTERN 6 +#define SM_INIT_PATTERN 7 +#define SM_RW_PATTERN 8 + +#define FDIR_WRITE 0 +#define FDIR_READ 1 + +/* For MS Card */ + +/* Status Register 1 */ +#define MS_REG_ST1_MB 0x80 /* media busy */ +#define MS_REG_ST1_FB1 0x40 /* flush busy 1 */ +#define MS_REG_ST1_DTER 0x20 /* error on data(corrected) */ +#define MS_REG_ST1_UCDT 0x10 /* unable to correct data */ +#define MS_REG_ST1_EXER 0x08 /* error on extra(corrected) */ +#define MS_REG_ST1_UCEX 0x04 /* unable to correct extra */ +#define MS_REG_ST1_FGER 0x02 /* error on overwrite flag(corrected) */ +#define MS_REG_ST1_UCFG 0x01 /* unable to correct overwrite flag */ +#define MS_REG_ST1_DEFAULT (MS_REG_ST1_MB | MS_REG_ST1_FB1 | MS_REG_ST1_DTER | MS_REG_ST1_UCDT | MS_REG_ST1_EXER | MS_REG_ST1_UCEX | MS_REG_ST1_FGER | MS_REG_ST1_UCFG) + +/* Overwrite Area */ +#define MS_REG_OVR_BKST 0x80 /* block status */ +#define MS_REG_OVR_BKST_OK MS_REG_OVR_BKST /* OK */ +#define MS_REG_OVR_BKST_NG 0x00 /* NG */ +#define MS_REG_OVR_PGST0 0x40 /* page status */ +#define MS_REG_OVR_PGST1 0x20 +#define MS_REG_OVR_PGST_MASK (MS_REG_OVR_PGST0 | MS_REG_OVR_PGST1) +#define MS_REG_OVR_PGST_OK (MS_REG_OVR_PGST0 | MS_REG_OVR_PGST1) /* OK */ +#define MS_REG_OVR_PGST_NG MS_REG_OVR_PGST1 /* NG */ +#define MS_REG_OVR_PGST_DATA_ERROR 0x00 /* data error */ +#define MS_REG_OVR_UDST 0x10 /* update status */ +#define MS_REG_OVR_UDST_UPDATING 0x00 /* updating */ +#define MS_REG_OVR_UDST_NO_UPDATE MS_REG_OVR_UDST +#define MS_REG_OVR_RESERVED 0x08 +#define MS_REG_OVR_DEFAULT (MS_REG_OVR_BKST_OK | MS_REG_OVR_PGST_OK | MS_REG_OVR_UDST_NO_UPDATE | MS_REG_OVR_RESERVED) + +/* Management Flag */ +#define MS_REG_MNG_SCMS0 0x20 /* serial copy management system */ +#define MS_REG_MNG_SCMS1 0x10 +#define MS_REG_MNG_SCMS_MASK (MS_REG_MNG_SCMS0 | MS_REG_MNG_SCMS1) +#define MS_REG_MNG_SCMS_COPY_OK (MS_REG_MNG_SCMS0 | MS_REG_MNG_SCMS1) +#define MS_REG_MNG_SCMS_ONE_COPY MS_REG_MNG_SCMS1 +#define MS_REG_MNG_SCMS_NO_COPY 0x00 +#define MS_REG_MNG_ATFLG 0x08 /* address transfer table flag */ +#define MS_REG_MNG_ATFLG_OTHER MS_REG_MNG_ATFLG /* other */ +#define MS_REG_MNG_ATFLG_ATTBL 0x00 /* address transfer table */ +#define MS_REG_MNG_SYSFLG 0x04 /* system flag */ +#define MS_REG_MNG_SYSFLG_USER MS_REG_MNG_SYSFLG /* user block */ +#define MS_REG_MNG_SYSFLG_BOOT 0x00 /* system block */ +#define MS_REG_MNG_RESERVED 0xc3 +#define MS_REG_MNG_DEFAULT (MS_REG_MNG_SCMS_COPY_OK | MS_REG_MNG_ATFLG_OTHER | MS_REG_MNG_SYSFLG_USER | MS_REG_MNG_RESERVED) + + +#define MS_MAX_PAGES_PER_BLOCK 32 +#define MS_MAX_INITIAL_ERROR_BLOCKS 10 +#define MS_LIB_BITS_PER_BYTE 8 + +#define MS_SYSINF_FORMAT_FAT 1 +#define MS_SYSINF_USAGE_GENERAL 0 + +#define MS_SYSINF_MSCLASS_TYPE_1 1 +#define MS_SYSINF_PAGE_SIZE MS_BYTES_PER_PAGE /* fixed */ + +#define MS_SYSINF_CARDTYPE_RDONLY 1 +#define MS_SYSINF_CARDTYPE_RDWR 2 +#define MS_SYSINF_CARDTYPE_HYBRID 3 +#define MS_SYSINF_SECURITY 0x01 +#define MS_SYSINF_SECURITY_NO_SUPPORT MS_SYSINF_SECURITY +#define MS_SYSINF_SECURITY_SUPPORT 0 + +#define MS_SYSINF_RESERVED1 1 +#define MS_SYSINF_RESERVED2 1 + +#define MS_SYSENT_TYPE_INVALID_BLOCK 0x01 +#define MS_SYSENT_TYPE_CIS_IDI 0x0a /* CIS/IDI */ + +#define SIZE_OF_KIRO 1024 +#define BYTE_MASK 0xff + +/* ms error code */ +#define MS_STATUS_WRITE_PROTECT 0x0106 +#define MS_STATUS_SUCCESS 0x0000 +#define MS_ERROR_FLASH_READ 0x8003 +#define MS_ERROR_FLASH_ERASE 0x8005 +#define MS_LB_ERROR 0xfff0 +#define MS_LB_BOOT_BLOCK 0xfff1 +#define MS_LB_INITIAL_ERROR 0xfff2 +#define MS_STATUS_SUCCESS_WITH_ECC 0xfff3 +#define MS_LB_ACQUIRED_ERROR 0xfff4 +#define MS_LB_NOT_USED_ERASED 0xfff5 +#define MS_NOCARD_ERROR 0xfff8 +#define MS_NO_MEMORY_ERROR 0xfff9 +#define MS_STATUS_INT_ERROR 0xfffa +#define MS_STATUS_ERROR 0xfffe +#define MS_LB_NOT_USED 0xffff + +#define MS_REG_MNG_SYSFLG 0x04 /* system flag */ +#define MS_REG_MNG_SYSFLG_USER MS_REG_MNG_SYSFLG /* user block */ + +#define MS_BOOT_BLOCK_ID 0x0001 +#define MS_BOOT_BLOCK_FORMAT_VERSION 0x0100 +#define MS_BOOT_BLOCK_DATA_ENTRIES 2 + +#define MS_NUMBER_OF_SYSTEM_ENTRY 4 +#define MS_NUMBER_OF_BOOT_BLOCK 2 +#define MS_BYTES_PER_PAGE 512 +#define MS_LOGICAL_BLOCKS_PER_SEGMENT 496 +#define MS_LOGICAL_BLOCKS_IN_1ST_SEGMENT 494 + +#define MS_PHYSICAL_BLOCKS_PER_SEGMENT 0x200 /* 512 */ +#define MS_PHYSICAL_BLOCKS_PER_SEGMENT_MASK 0x1ff + +/* overwrite area */ +#define MS_REG_OVR_BKST 0x80 /* block status */ +#define MS_REG_OVR_BKST_OK MS_REG_OVR_BKST /* OK */ +#define MS_REG_OVR_BKST_NG 0x00 /* NG */ + +/* Status Register 1 */ +#define MS_REG_ST1_DTER 0x20 /* error on data(corrected) */ +#define MS_REG_ST1_EXER 0x08 /* error on extra(corrected) */ +#define MS_REG_ST1_FGER 0x02 /* error on overwrite flag(corrected) */ + +/* MemoryStick Register */ +/* Status Register 0 */ +#define MS_REG_ST0_WP 0x01 /* write protected */ +#define MS_REG_ST0_WP_ON MS_REG_ST0_WP + +#define MS_LIB_CTRL_RDONLY 0 +#define MS_LIB_CTRL_WRPROTECT 1 + +/*dphy->log table */ +#define ms_libconv_to_logical(pdx, PhyBlock) (((PhyBlock) >= (pdx)->MS_Lib.NumberOfPhyBlock) ? MS_STATUS_ERROR : (pdx)->MS_Lib.Phy2LogMap[PhyBlock]) +#define ms_libconv_to_physical(pdx, LogBlock) (((LogBlock) >= (pdx)->MS_Lib.NumberOfLogBlock) ? MS_STATUS_ERROR : (pdx)->MS_Lib.Log2PhyMap[LogBlock]) + +#define ms_lib_ctrl_set(pdx, Flag) ((pdx)->MS_Lib.flags |= (1 << (Flag))) +#define ms_lib_ctrl_reset(pdx, Flag) ((pdx)->MS_Lib.flags &= ~(1 << (Flag))) +#define ms_lib_ctrl_check(pdx, Flag) ((pdx)->MS_Lib.flags & (1 << (Flag))) + +#define ms_lib_iswritable(pdx) ((ms_lib_ctrl_check((pdx), MS_LIB_CTRL_RDONLY) == 0) && (ms_lib_ctrl_check(pdx, MS_LIB_CTRL_WRPROTECT) == 0)) +#define ms_lib_clear_pagemap(pdx) memset((pdx)->MS_Lib.pagemap, 0, sizeof((pdx)->MS_Lib.pagemap)) +#define memstick_logaddr(logadr1, logadr0) ((((u16)(logadr1)) << 8) | (logadr0)) + + +/* SD_STATUS bits */ +#define SD_Insert BIT(0) +#define SD_Ready BIT(1) +#define SD_MediaChange BIT(2) +#define SD_IsMMC BIT(3) +#define SD_HiCapacity BIT(4) +#define SD_HiSpeed BIT(5) +#define SD_WtP BIT(6) + /* Bit 7 reserved */ + +/* MS_STATUS bits */ +#define MS_Insert BIT(0) +#define MS_Ready BIT(1) +#define MS_MediaChange BIT(2) +#define MS_IsMSPro BIT(3) +#define MS_IsMSPHG BIT(4) + /* Bit 5 reserved */ +#define MS_WtP BIT(6) + /* Bit 7 reserved */ + +/* SM_STATUS bits */ +#define SM_Insert BIT(0) +#define SM_Ready BIT(1) +#define SM_MediaChange BIT(2) + /* Bits 3-5 reserved */ +#define SM_WtP BIT(6) +#define SM_IsMS BIT(7) + +struct ms_bootblock_cis { + u8 bCistplDEVICE[6]; /* 0 */ + u8 bCistplDEVICE0C[6]; /* 6 */ + u8 bCistplJEDECC[4]; /* 12 */ + u8 bCistplMANFID[6]; /* 16 */ + u8 bCistplVER1[32]; /* 22 */ + u8 bCistplFUNCID[4]; /* 54 */ + u8 bCistplFUNCE0[4]; /* 58 */ + u8 bCistplFUNCE1[5]; /* 62 */ + u8 bCistplCONF[7]; /* 67 */ + u8 bCistplCFTBLENT0[10];/* 74 */ + u8 bCistplCFTBLENT1[8]; /* 84 */ + u8 bCistplCFTBLENT2[12];/* 92 */ + u8 bCistplCFTBLENT3[8]; /* 104 */ + u8 bCistplCFTBLENT4[17];/* 112 */ + u8 bCistplCFTBLENT5[8]; /* 129 */ + u8 bCistplCFTBLENT6[17];/* 137 */ + u8 bCistplCFTBLENT7[8]; /* 154 */ + u8 bCistplNOLINK[3]; /* 162 */ +} ; + +struct ms_bootblock_idi { +#define MS_IDI_GENERAL_CONF 0x848A + u16 wIDIgeneralConfiguration; /* 0 */ + u16 wIDInumberOfCylinder; /* 1 */ + u16 wIDIreserved0; /* 2 */ + u16 wIDInumberOfHead; /* 3 */ + u16 wIDIbytesPerTrack; /* 4 */ + u16 wIDIbytesPerSector; /* 5 */ + u16 wIDIsectorsPerTrack; /* 6 */ + u16 wIDItotalSectors[2]; /* 7-8 high,low */ + u16 wIDIreserved1[11]; /* 9-19 */ + u16 wIDIbufferType; /* 20 */ + u16 wIDIbufferSize; /* 21 */ + u16 wIDIlongCmdECC; /* 22 */ + u16 wIDIfirmVersion[4]; /* 23-26 */ + u16 wIDImodelName[20]; /* 27-46 */ + u16 wIDIreserved2; /* 47 */ + u16 wIDIlongWordSupported; /* 48 */ + u16 wIDIdmaSupported; /* 49 */ + u16 wIDIreserved3; /* 50 */ + u16 wIDIpioTiming; /* 51 */ + u16 wIDIdmaTiming; /* 52 */ + u16 wIDItransferParameter; /* 53 */ + u16 wIDIformattedCylinder; /* 54 */ + u16 wIDIformattedHead; /* 55 */ + u16 wIDIformattedSectorsPerTrack;/* 56 */ + u16 wIDIformattedTotalSectors[2];/* 57-58 */ + u16 wIDImultiSector; /* 59 */ + u16 wIDIlbaSectors[2]; /* 60-61 */ + u16 wIDIsingleWordDMA; /* 62 */ + u16 wIDImultiWordDMA; /* 63 */ + u16 wIDIreserved4[192]; /* 64-255 */ +}; + +struct ms_bootblock_sysent_rec { + u32 dwStart; + u32 dwSize; + u8 bType; + u8 bReserved[3]; +}; + +struct ms_bootblock_sysent { + struct ms_bootblock_sysent_rec entry[MS_NUMBER_OF_SYSTEM_ENTRY]; +}; + +struct ms_bootblock_sysinf { + u8 bMsClass; /* must be 1 */ + u8 bCardType; /* see below */ + u16 wBlockSize; /* n KB */ + u16 wBlockNumber; /* number of physical block */ + u16 wTotalBlockNumber; /* number of logical block */ + u16 wPageSize; /* must be 0x200 */ + u8 bExtraSize; /* 0x10 */ + u8 bSecuritySupport; + u8 bAssemblyDate[8]; + u8 bFactoryArea[4]; + u8 bAssemblyMakerCode; + u8 bAssemblyMachineCode[3]; + u16 wMemoryMakerCode; + u16 wMemoryDeviceCode; + u16 wMemorySize; + u8 bReserved1; + u8 bReserved2; + u8 bVCC; + u8 bVPP; + u16 wControllerChipNumber; + u16 wControllerFunction; /* New MS */ + u8 bReserved3[9]; /* New MS */ + u8 bParallelSupport; /* New MS */ + u16 wFormatValue; /* New MS */ + u8 bFormatType; + u8 bUsage; + u8 bDeviceType; + u8 bReserved4[22]; + u8 bFUValue3; + u8 bFUValue4; + u8 bReserved5[15]; +}; + +struct ms_bootblock_header { + u16 wBlockID; + u16 wFormatVersion; + u8 bReserved1[184]; + u8 bNumberOfDataEntry; + u8 bReserved2[179]; +}; + +struct ms_bootblock_page0 { + struct ms_bootblock_header header; + struct ms_bootblock_sysent sysent; + struct ms_bootblock_sysinf sysinf; +}; + +struct ms_bootblock_cis_idi { + union { + struct ms_bootblock_cis cis; + u8 dmy[256]; + } cis; + + union { + struct ms_bootblock_idi idi; + u8 dmy[256]; + } idi; + +}; + +/* ENE MS Lib struct */ +struct ms_lib_type_extdat { + u8 reserved; + u8 intr; + u8 status0; + u8 status1; + u8 ovrflg; + u8 mngflg; + u16 logadr; +}; + +struct ms_lib_ctrl { + u32 flags; + u32 BytesPerSector; + u32 NumberOfCylinder; + u32 SectorsPerCylinder; + u16 cardType; /* R/W, RO, Hybrid */ + u16 blockSize; + u16 PagesPerBlock; + u16 NumberOfPhyBlock; + u16 NumberOfLogBlock; + u16 NumberOfSegment; + u16 *Phy2LogMap; /* phy2log table */ + u16 *Log2PhyMap; /* log2phy table */ + u16 wrtblk; + unsigned char *pagemap[(MS_MAX_PAGES_PER_BLOCK + (MS_LIB_BITS_PER_BYTE-1)) / MS_LIB_BITS_PER_BYTE]; + unsigned char *blkpag; + struct ms_lib_type_extdat *blkext; + unsigned char copybuf[512]; +}; + + +/* SD Block Length */ +/* 2^9 = 512 Bytes, The HW maximum read/write data length */ +#define SD_BLOCK_LEN 9 + +struct ene_ub6250_info { + + /* I/O bounce buffer */ + u8 *bbuf; + + /* for 6250 code */ + u8 SD_Status; + u8 MS_Status; + u8 SM_Status; + + /* ----- SD Control Data ---------------- */ + /*SD_REGISTER SD_Regs; */ + u16 SD_Block_Mult; + u8 SD_READ_BL_LEN; + u16 SD_C_SIZE; + u8 SD_C_SIZE_MULT; + + /* SD/MMC New spec. */ + u8 SD_SPEC_VER; + u8 SD_CSD_VER; + u8 SD20_HIGH_CAPACITY; + u32 HC_C_SIZE; + u8 MMC_SPEC_VER; + u8 MMC_BusWidth; + u8 MMC_HIGH_CAPACITY; + + /*----- MS Control Data ---------------- */ + bool MS_SWWP; + u32 MSP_TotalBlock; + struct ms_lib_ctrl MS_Lib; + bool MS_IsRWPage; + u16 MS_Model; + + /*----- SM Control Data ---------------- */ + u8 SM_DeviceID; + u8 SM_CardID; + + unsigned char *testbuf; + u8 BIN_FLAG; + u32 bl_num; + int SrbStatus; + + /*------Power Managerment ---------------*/ + bool Power_IsResum; +}; + +static int ene_sd_init(struct us_data *us); +static int ene_ms_init(struct us_data *us); +static int ene_load_bincode(struct us_data *us, unsigned char flag); + +static void ene_ub6250_info_destructor(void *extra) +{ + struct ene_ub6250_info *info = (struct ene_ub6250_info *) extra; + + if (!extra) + return; + kfree(info->bbuf); +} + +static int ene_send_scsi_cmd(struct us_data *us, u8 fDir, void *buf, int use_sg) +{ + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap *) us->iobuf; + + int result; + unsigned int residue; + unsigned int cswlen = 0, partial = 0; + unsigned int transfer_length = bcb->DataTransferLength; + + /* usb_stor_dbg(us, "transport --- ene_send_scsi_cmd\n"); */ + /* send cmd to out endpoint */ + result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + bcb, US_BULK_CB_WRAP_LEN, NULL); + if (result != USB_STOR_XFER_GOOD) { + usb_stor_dbg(us, "send cmd to out endpoint fail ---\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + if (buf) { + unsigned int pipe = fDir; + + if (fDir == FDIR_READ) + pipe = us->recv_bulk_pipe; + else + pipe = us->send_bulk_pipe; + + /* Bulk */ + if (use_sg) { + result = usb_stor_bulk_srb(us, pipe, us->srb); + } else { + result = usb_stor_bulk_transfer_sg(us, pipe, buf, + transfer_length, 0, &partial); + } + if (result != USB_STOR_XFER_GOOD) { + usb_stor_dbg(us, "data transfer fail ---\n"); + return USB_STOR_TRANSPORT_ERROR; + } + } + + /* Get CSW for device status */ + result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, bcs, + US_BULK_CS_WRAP_LEN, &cswlen); + + if (result == USB_STOR_XFER_SHORT && cswlen == 0) { + usb_stor_dbg(us, "Received 0-length CSW; retrying...\n"); + result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + bcs, US_BULK_CS_WRAP_LEN, &cswlen); + } + + if (result == USB_STOR_XFER_STALLED) { + /* get the status again */ + usb_stor_dbg(us, "Attempting to get CSW (2nd try)...\n"); + result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + bcs, US_BULK_CS_WRAP_LEN, NULL); + } + + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + /* check bulk status */ + residue = le32_to_cpu(bcs->Residue); + + /* + * try to compute the actual residue, based on how much data + * was really transferred and what the device tells us + */ + if (residue && !(us->fflags & US_FL_IGNORE_RESIDUE)) { + residue = min(residue, transfer_length); + if (us->srb != NULL) + scsi_set_resid(us->srb, max(scsi_get_resid(us->srb), + residue)); + } + + if (bcs->Status != US_BULK_STAT_OK) + return USB_STOR_TRANSPORT_ERROR; + + return USB_STOR_TRANSPORT_GOOD; +} + +static int do_scsi_request_sense(struct us_data *us, struct scsi_cmnd *srb) +{ + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + unsigned char buf[18]; + + memset(buf, 0, 18); + buf[0] = 0x70; /* Current error */ + buf[2] = info->SrbStatus >> 16; /* Sense key */ + buf[7] = 10; /* Additional length */ + buf[12] = info->SrbStatus >> 8; /* ASC */ + buf[13] = info->SrbStatus; /* ASCQ */ + + usb_stor_set_xfer_buf(buf, sizeof(buf), srb); + return USB_STOR_TRANSPORT_GOOD; +} + +static int do_scsi_inquiry(struct us_data *us, struct scsi_cmnd *srb) +{ + unsigned char data_ptr[36] = { + 0x00, 0x00, 0x02, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x55, + 0x53, 0x42, 0x32, 0x2E, 0x30, 0x20, 0x20, 0x43, 0x61, + 0x72, 0x64, 0x52, 0x65, 0x61, 0x64, 0x65, 0x72, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x31, 0x30, 0x30 }; + + usb_stor_set_xfer_buf(data_ptr, 36, srb); + return USB_STOR_TRANSPORT_GOOD; +} + +static int sd_scsi_test_unit_ready(struct us_data *us, struct scsi_cmnd *srb) +{ + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + if ((info->SD_Status & SD_Insert) && (info->SD_Status & SD_Ready)) + return USB_STOR_TRANSPORT_GOOD; + else { + ene_sd_init(us); + return USB_STOR_TRANSPORT_GOOD; + } + + return USB_STOR_TRANSPORT_GOOD; +} + +static int sd_scsi_mode_sense(struct us_data *us, struct scsi_cmnd *srb) +{ + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + unsigned char mediaNoWP[12] = { + 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x71, 0xc0, 0x00, 0x00, 0x02, 0x00 }; + unsigned char mediaWP[12] = { + 0x0b, 0x00, 0x80, 0x08, 0x00, 0x00, + 0x71, 0xc0, 0x00, 0x00, 0x02, 0x00 }; + + if (info->SD_Status & SD_WtP) + usb_stor_set_xfer_buf(mediaWP, 12, srb); + else + usb_stor_set_xfer_buf(mediaNoWP, 12, srb); + + + return USB_STOR_TRANSPORT_GOOD; +} + +static int sd_scsi_read_capacity(struct us_data *us, struct scsi_cmnd *srb) +{ + u32 bl_num; + u32 bl_len; + unsigned int offset = 0; + unsigned char buf[8]; + struct scatterlist *sg = NULL; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + usb_stor_dbg(us, "sd_scsi_read_capacity\n"); + if (info->SD_Status & SD_HiCapacity) { + bl_len = 0x200; + if (info->SD_Status & SD_IsMMC) + bl_num = info->HC_C_SIZE-1; + else + bl_num = (info->HC_C_SIZE + 1) * 1024 - 1; + } else { + bl_len = 1 << (info->SD_READ_BL_LEN); + bl_num = info->SD_Block_Mult * (info->SD_C_SIZE + 1) + * (1 << (info->SD_C_SIZE_MULT + 2)) - 1; + } + info->bl_num = bl_num; + usb_stor_dbg(us, "bl_len = %x\n", bl_len); + usb_stor_dbg(us, "bl_num = %x\n", bl_num); + + /*srb->request_bufflen = 8; */ + buf[0] = (bl_num >> 24) & 0xff; + buf[1] = (bl_num >> 16) & 0xff; + buf[2] = (bl_num >> 8) & 0xff; + buf[3] = (bl_num >> 0) & 0xff; + buf[4] = (bl_len >> 24) & 0xff; + buf[5] = (bl_len >> 16) & 0xff; + buf[6] = (bl_len >> 8) & 0xff; + buf[7] = (bl_len >> 0) & 0xff; + + usb_stor_access_xfer_buf(buf, 8, srb, &sg, &offset, TO_XFER_BUF); + + return USB_STOR_TRANSPORT_GOOD; +} + +static int sd_scsi_read(struct us_data *us, struct scsi_cmnd *srb) +{ + int result; + unsigned char *cdb = srb->cmnd; + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + u32 bn = ((cdb[2] << 24) & 0xff000000) | ((cdb[3] << 16) & 0x00ff0000) | + ((cdb[4] << 8) & 0x0000ff00) | ((cdb[5] << 0) & 0x000000ff); + u16 blen = ((cdb[7] << 8) & 0xff00) | ((cdb[8] << 0) & 0x00ff); + u32 bnByte = bn * 0x200; + u32 blenByte = blen * 0x200; + + if (bn > info->bl_num) + return USB_STOR_TRANSPORT_ERROR; + + result = ene_load_bincode(us, SD_RW_PATTERN); + if (result != USB_STOR_XFER_GOOD) { + usb_stor_dbg(us, "Load SD RW pattern Fail !!\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + if (info->SD_Status & SD_HiCapacity) + bnByte = bn; + + /* set up the command wrapper */ + memset(bcb, 0, sizeof(struct bulk_cb_wrap)); + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->DataTransferLength = blenByte; + bcb->Flags = US_BULK_FLAG_IN; + bcb->CDB[0] = 0xF1; + bcb->CDB[5] = (unsigned char)(bnByte); + bcb->CDB[4] = (unsigned char)(bnByte>>8); + bcb->CDB[3] = (unsigned char)(bnByte>>16); + bcb->CDB[2] = (unsigned char)(bnByte>>24); + + result = ene_send_scsi_cmd(us, FDIR_READ, scsi_sglist(srb), 1); + return result; +} + +static int sd_scsi_write(struct us_data *us, struct scsi_cmnd *srb) +{ + int result; + unsigned char *cdb = srb->cmnd; + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + u32 bn = ((cdb[2] << 24) & 0xff000000) | ((cdb[3] << 16) & 0x00ff0000) | + ((cdb[4] << 8) & 0x0000ff00) | ((cdb[5] << 0) & 0x000000ff); + u16 blen = ((cdb[7] << 8) & 0xff00) | ((cdb[8] << 0) & 0x00ff); + u32 bnByte = bn * 0x200; + u32 blenByte = blen * 0x200; + + if (bn > info->bl_num) + return USB_STOR_TRANSPORT_ERROR; + + result = ene_load_bincode(us, SD_RW_PATTERN); + if (result != USB_STOR_XFER_GOOD) { + usb_stor_dbg(us, "Load SD RW pattern Fail !!\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + if (info->SD_Status & SD_HiCapacity) + bnByte = bn; + + /* set up the command wrapper */ + memset(bcb, 0, sizeof(struct bulk_cb_wrap)); + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->DataTransferLength = blenByte; + bcb->Flags = US_BULK_FLAG_OUT; + bcb->CDB[0] = 0xF0; + bcb->CDB[5] = (unsigned char)(bnByte); + bcb->CDB[4] = (unsigned char)(bnByte>>8); + bcb->CDB[3] = (unsigned char)(bnByte>>16); + bcb->CDB[2] = (unsigned char)(bnByte>>24); + + result = ene_send_scsi_cmd(us, FDIR_WRITE, scsi_sglist(srb), 1); + return result; +} + +/* + * ENE MS Card + */ + +static int ms_lib_set_logicalpair(struct us_data *us, u16 logblk, u16 phyblk) +{ + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + if ((logblk >= info->MS_Lib.NumberOfLogBlock) || (phyblk >= info->MS_Lib.NumberOfPhyBlock)) + return (u32)-1; + + info->MS_Lib.Phy2LogMap[phyblk] = logblk; + info->MS_Lib.Log2PhyMap[logblk] = phyblk; + + return 0; +} + +static int ms_lib_set_logicalblockmark(struct us_data *us, u16 phyblk, u16 mark) +{ + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + if (phyblk >= info->MS_Lib.NumberOfPhyBlock) + return (u32)-1; + + info->MS_Lib.Phy2LogMap[phyblk] = mark; + + return 0; +} + +static int ms_lib_set_initialerrorblock(struct us_data *us, u16 phyblk) +{ + return ms_lib_set_logicalblockmark(us, phyblk, MS_LB_INITIAL_ERROR); +} + +static int ms_lib_set_bootblockmark(struct us_data *us, u16 phyblk) +{ + return ms_lib_set_logicalblockmark(us, phyblk, MS_LB_BOOT_BLOCK); +} + +static int ms_lib_free_logicalmap(struct us_data *us) +{ + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + kfree(info->MS_Lib.Phy2LogMap); + info->MS_Lib.Phy2LogMap = NULL; + + kfree(info->MS_Lib.Log2PhyMap); + info->MS_Lib.Log2PhyMap = NULL; + + return 0; +} + +static int ms_lib_alloc_logicalmap(struct us_data *us) +{ + u32 i; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + info->MS_Lib.Phy2LogMap = kmalloc_array(info->MS_Lib.NumberOfPhyBlock, + sizeof(u16), + GFP_KERNEL); + info->MS_Lib.Log2PhyMap = kmalloc_array(info->MS_Lib.NumberOfLogBlock, + sizeof(u16), + GFP_KERNEL); + + if ((info->MS_Lib.Phy2LogMap == NULL) || (info->MS_Lib.Log2PhyMap == NULL)) { + ms_lib_free_logicalmap(us); + return (u32)-1; + } + + for (i = 0; i < info->MS_Lib.NumberOfPhyBlock; i++) + info->MS_Lib.Phy2LogMap[i] = MS_LB_NOT_USED; + + for (i = 0; i < info->MS_Lib.NumberOfLogBlock; i++) + info->MS_Lib.Log2PhyMap[i] = MS_LB_NOT_USED; + + return 0; +} + +static void ms_lib_clear_writebuf(struct us_data *us) +{ + int i; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + info->MS_Lib.wrtblk = (u16)-1; + ms_lib_clear_pagemap(info); + + if (info->MS_Lib.blkpag) + memset(info->MS_Lib.blkpag, 0xff, info->MS_Lib.PagesPerBlock * info->MS_Lib.BytesPerSector); + + if (info->MS_Lib.blkext) { + for (i = 0; i < info->MS_Lib.PagesPerBlock; i++) { + info->MS_Lib.blkext[i].status1 = MS_REG_ST1_DEFAULT; + info->MS_Lib.blkext[i].ovrflg = MS_REG_OVR_DEFAULT; + info->MS_Lib.blkext[i].mngflg = MS_REG_MNG_DEFAULT; + info->MS_Lib.blkext[i].logadr = MS_LB_NOT_USED; + } + } +} + +static int ms_count_freeblock(struct us_data *us, u16 PhyBlock) +{ + u32 Ende, Count; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + Ende = PhyBlock + MS_PHYSICAL_BLOCKS_PER_SEGMENT; + for (Count = 0; PhyBlock < Ende; PhyBlock++) { + switch (info->MS_Lib.Phy2LogMap[PhyBlock]) { + case MS_LB_NOT_USED: + case MS_LB_NOT_USED_ERASED: + Count++; + break; + default: + break; + } + } + + return Count; +} + +static int ms_read_readpage(struct us_data *us, u32 PhyBlockAddr, + u8 PageNum, u32 *PageBuf, struct ms_lib_type_extdat *ExtraDat) +{ + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + u8 *bbuf = info->bbuf; + int result; + u32 bn = PhyBlockAddr * 0x20 + PageNum; + + result = ene_load_bincode(us, MS_RW_PATTERN); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + /* Read Page Data */ + memset(bcb, 0, sizeof(struct bulk_cb_wrap)); + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->DataTransferLength = 0x200; + bcb->Flags = US_BULK_FLAG_IN; + bcb->CDB[0] = 0xF1; + + bcb->CDB[1] = 0x02; /* in init.c ENE_MSInit() is 0x01 */ + + bcb->CDB[5] = (unsigned char)(bn); + bcb->CDB[4] = (unsigned char)(bn>>8); + bcb->CDB[3] = (unsigned char)(bn>>16); + bcb->CDB[2] = (unsigned char)(bn>>24); + + result = ene_send_scsi_cmd(us, FDIR_READ, PageBuf, 0); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + + /* Read Extra Data */ + memset(bcb, 0, sizeof(struct bulk_cb_wrap)); + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->DataTransferLength = 0x4; + bcb->Flags = US_BULK_FLAG_IN; + bcb->CDB[0] = 0xF1; + bcb->CDB[1] = 0x03; + + bcb->CDB[5] = (unsigned char)(PageNum); + bcb->CDB[4] = (unsigned char)(PhyBlockAddr); + bcb->CDB[3] = (unsigned char)(PhyBlockAddr>>8); + bcb->CDB[2] = (unsigned char)(PhyBlockAddr>>16); + bcb->CDB[6] = 0x01; + + result = ene_send_scsi_cmd(us, FDIR_READ, bbuf, 0); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + ExtraDat->reserved = 0; + ExtraDat->intr = 0x80; /* Not yet,fireware support */ + ExtraDat->status0 = 0x10; /* Not yet,fireware support */ + + ExtraDat->status1 = 0x00; /* Not yet,fireware support */ + ExtraDat->ovrflg = bbuf[0]; + ExtraDat->mngflg = bbuf[1]; + ExtraDat->logadr = memstick_logaddr(bbuf[2], bbuf[3]); + + return USB_STOR_TRANSPORT_GOOD; +} + +static int ms_lib_process_bootblock(struct us_data *us, u16 PhyBlock, u8 *PageData) +{ + struct ms_bootblock_sysent *SysEntry; + struct ms_bootblock_sysinf *SysInfo; + u32 i, result; + u8 PageNumber; + u8 *PageBuffer; + struct ms_lib_type_extdat ExtraData; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + PageBuffer = kzalloc(MS_BYTES_PER_PAGE * 2, GFP_KERNEL); + if (PageBuffer == NULL) + return (u32)-1; + + result = (u32)-1; + + SysInfo = &(((struct ms_bootblock_page0 *)PageData)->sysinf); + + if ((SysInfo->bMsClass != MS_SYSINF_MSCLASS_TYPE_1) || + (be16_to_cpu(SysInfo->wPageSize) != MS_SYSINF_PAGE_SIZE) || + ((SysInfo->bSecuritySupport & MS_SYSINF_SECURITY) == MS_SYSINF_SECURITY_SUPPORT) || + (SysInfo->bReserved1 != MS_SYSINF_RESERVED1) || + (SysInfo->bReserved2 != MS_SYSINF_RESERVED2) || + (SysInfo->bFormatType != MS_SYSINF_FORMAT_FAT) || + (SysInfo->bUsage != MS_SYSINF_USAGE_GENERAL)) + goto exit; + /* */ + switch (info->MS_Lib.cardType = SysInfo->bCardType) { + case MS_SYSINF_CARDTYPE_RDONLY: + ms_lib_ctrl_set(info, MS_LIB_CTRL_RDONLY); + break; + case MS_SYSINF_CARDTYPE_RDWR: + ms_lib_ctrl_reset(info, MS_LIB_CTRL_RDONLY); + break; + case MS_SYSINF_CARDTYPE_HYBRID: + default: + goto exit; + } + + info->MS_Lib.blockSize = be16_to_cpu(SysInfo->wBlockSize); + info->MS_Lib.NumberOfPhyBlock = be16_to_cpu(SysInfo->wBlockNumber); + info->MS_Lib.NumberOfLogBlock = be16_to_cpu(SysInfo->wTotalBlockNumber)-2; + info->MS_Lib.PagesPerBlock = info->MS_Lib.blockSize * SIZE_OF_KIRO / MS_BYTES_PER_PAGE; + info->MS_Lib.NumberOfSegment = info->MS_Lib.NumberOfPhyBlock / MS_PHYSICAL_BLOCKS_PER_SEGMENT; + info->MS_Model = be16_to_cpu(SysInfo->wMemorySize); + + /*Allocate to all number of logicalblock and physicalblock */ + if (ms_lib_alloc_logicalmap(us)) + goto exit; + + /* Mark the book block */ + ms_lib_set_bootblockmark(us, PhyBlock); + + SysEntry = &(((struct ms_bootblock_page0 *)PageData)->sysent); + + for (i = 0; i < MS_NUMBER_OF_SYSTEM_ENTRY; i++) { + u32 EntryOffset, EntrySize; + + EntryOffset = be32_to_cpu(SysEntry->entry[i].dwStart); + + if (EntryOffset == 0xffffff) + continue; + EntrySize = be32_to_cpu(SysEntry->entry[i].dwSize); + + if (EntrySize == 0) + continue; + + if (EntryOffset + MS_BYTES_PER_PAGE + EntrySize > info->MS_Lib.blockSize * (u32)SIZE_OF_KIRO) + continue; + + if (i == 0) { + u8 PrevPageNumber = 0; + u16 phyblk; + + if (SysEntry->entry[i].bType != MS_SYSENT_TYPE_INVALID_BLOCK) + goto exit; + + while (EntrySize > 0) { + + PageNumber = (u8)(EntryOffset / MS_BYTES_PER_PAGE + 1); + if (PageNumber != PrevPageNumber) { + switch (ms_read_readpage(us, PhyBlock, PageNumber, (u32 *)PageBuffer, &ExtraData)) { + case MS_STATUS_SUCCESS: + break; + case MS_STATUS_WRITE_PROTECT: + case MS_ERROR_FLASH_READ: + case MS_STATUS_ERROR: + default: + goto exit; + } + + PrevPageNumber = PageNumber; + } + + phyblk = be16_to_cpu(*(u16 *)(PageBuffer + (EntryOffset % MS_BYTES_PER_PAGE))); + if (phyblk < 0x0fff) + ms_lib_set_initialerrorblock(us, phyblk); + + EntryOffset += 2; + EntrySize -= 2; + } + } else if (i == 1) { /* CIS/IDI */ + struct ms_bootblock_idi *idi; + + if (SysEntry->entry[i].bType != MS_SYSENT_TYPE_CIS_IDI) + goto exit; + + switch (ms_read_readpage(us, PhyBlock, (u8)(EntryOffset / MS_BYTES_PER_PAGE + 1), (u32 *)PageBuffer, &ExtraData)) { + case MS_STATUS_SUCCESS: + break; + case MS_STATUS_WRITE_PROTECT: + case MS_ERROR_FLASH_READ: + case MS_STATUS_ERROR: + default: + goto exit; + } + + idi = &((struct ms_bootblock_cis_idi *)(PageBuffer + (EntryOffset % MS_BYTES_PER_PAGE)))->idi.idi; + if (le16_to_cpu(idi->wIDIgeneralConfiguration) != MS_IDI_GENERAL_CONF) + goto exit; + + info->MS_Lib.BytesPerSector = le16_to_cpu(idi->wIDIbytesPerSector); + if (info->MS_Lib.BytesPerSector != MS_BYTES_PER_PAGE) + goto exit; + } + } /* End for .. */ + + result = 0; + +exit: + if (result) + ms_lib_free_logicalmap(us); + + kfree(PageBuffer); + + result = 0; + return result; +} + +static void ms_lib_free_writebuf(struct us_data *us) +{ + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + info->MS_Lib.wrtblk = (u16)-1; /* set to -1 */ + + /* memset((fdoExt)->MS_Lib.pagemap, 0, sizeof((fdoExt)->MS_Lib.pagemap)) */ + + ms_lib_clear_pagemap(info); /* (pdx)->MS_Lib.pagemap memset 0 in ms.h */ + + if (info->MS_Lib.blkpag) { + kfree(info->MS_Lib.blkpag); /* Arnold test ... */ + info->MS_Lib.blkpag = NULL; + } + + if (info->MS_Lib.blkext) { + kfree(info->MS_Lib.blkext); /* Arnold test ... */ + info->MS_Lib.blkext = NULL; + } +} + + +static void ms_lib_free_allocatedarea(struct us_data *us) +{ + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + ms_lib_free_writebuf(us); /* Free MS_Lib.pagemap */ + ms_lib_free_logicalmap(us); /* kfree MS_Lib.Phy2LogMap and MS_Lib.Log2PhyMap */ + + /* set struct us point flag to 0 */ + info->MS_Lib.flags = 0; + info->MS_Lib.BytesPerSector = 0; + info->MS_Lib.SectorsPerCylinder = 0; + + info->MS_Lib.cardType = 0; + info->MS_Lib.blockSize = 0; + info->MS_Lib.PagesPerBlock = 0; + + info->MS_Lib.NumberOfPhyBlock = 0; + info->MS_Lib.NumberOfLogBlock = 0; +} + + +static int ms_lib_alloc_writebuf(struct us_data *us) +{ + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + info->MS_Lib.wrtblk = (u16)-1; + + info->MS_Lib.blkpag = kmalloc_array(info->MS_Lib.PagesPerBlock, + info->MS_Lib.BytesPerSector, + GFP_KERNEL); + info->MS_Lib.blkext = kmalloc_objs(struct ms_lib_type_extdat, + info->MS_Lib.PagesPerBlock); + + if ((info->MS_Lib.blkpag == NULL) || (info->MS_Lib.blkext == NULL)) { + ms_lib_free_writebuf(us); + return (u32)-1; + } + + ms_lib_clear_writebuf(us); + + return 0; +} + +static int ms_lib_force_setlogical_pair(struct us_data *us, u16 logblk, u16 phyblk) +{ + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + if (logblk == MS_LB_NOT_USED) + return 0; + + if ((logblk >= info->MS_Lib.NumberOfLogBlock) || + (phyblk >= info->MS_Lib.NumberOfPhyBlock)) + return (u32)-1; + + info->MS_Lib.Phy2LogMap[phyblk] = logblk; + info->MS_Lib.Log2PhyMap[logblk] = phyblk; + + return 0; +} + +static int ms_read_copyblock(struct us_data *us, u16 oldphy, u16 newphy, + u16 PhyBlockAddr, u8 PageNum, unsigned char *buf, u16 len) +{ + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + int result; + + result = ene_load_bincode(us, MS_RW_PATTERN); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + memset(bcb, 0, sizeof(struct bulk_cb_wrap)); + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->DataTransferLength = 0x200*len; + bcb->Flags = US_BULK_FLAG_OUT; + bcb->CDB[0] = 0xF0; + bcb->CDB[1] = 0x08; + bcb->CDB[4] = (unsigned char)(oldphy); + bcb->CDB[3] = (unsigned char)(oldphy>>8); + bcb->CDB[2] = 0; /* (BYTE)(oldphy>>16) */ + bcb->CDB[7] = (unsigned char)(newphy); + bcb->CDB[6] = (unsigned char)(newphy>>8); + bcb->CDB[5] = 0; /* (BYTE)(newphy>>16) */ + bcb->CDB[9] = (unsigned char)(PhyBlockAddr); + bcb->CDB[8] = (unsigned char)(PhyBlockAddr>>8); + bcb->CDB[10] = PageNum; + + result = ene_send_scsi_cmd(us, FDIR_WRITE, buf, 0); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + return USB_STOR_TRANSPORT_GOOD; +} + +static int ms_read_eraseblock(struct us_data *us, u32 PhyBlockAddr) +{ + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + int result; + u32 bn = PhyBlockAddr; + + result = ene_load_bincode(us, MS_RW_PATTERN); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + memset(bcb, 0, sizeof(struct bulk_cb_wrap)); + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->DataTransferLength = 0x200; + bcb->Flags = US_BULK_FLAG_IN; + bcb->CDB[0] = 0xF2; + bcb->CDB[1] = 0x06; + bcb->CDB[4] = (unsigned char)(bn); + bcb->CDB[3] = (unsigned char)(bn>>8); + bcb->CDB[2] = (unsigned char)(bn>>16); + + result = ene_send_scsi_cmd(us, FDIR_READ, NULL, 0); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + return USB_STOR_TRANSPORT_GOOD; +} + +static int ms_lib_check_disableblock(struct us_data *us, u16 PhyBlock) +{ + unsigned char *PageBuf = NULL; + u16 result = MS_STATUS_SUCCESS; + u16 blk, index = 0; + struct ms_lib_type_extdat extdat; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + PageBuf = kmalloc(MS_BYTES_PER_PAGE, GFP_KERNEL); + if (PageBuf == NULL) { + result = MS_NO_MEMORY_ERROR; + goto exit; + } + + ms_read_readpage(us, PhyBlock, 1, (u32 *)PageBuf, &extdat); + do { + blk = be16_to_cpu(PageBuf[index]); + if (blk == MS_LB_NOT_USED) + break; + if (blk == info->MS_Lib.Log2PhyMap[0]) { + result = MS_ERROR_FLASH_READ; + break; + } + index++; + } while (1); + +exit: + kfree(PageBuf); + return result; +} + +static int ms_lib_setacquired_errorblock(struct us_data *us, u16 phyblk) +{ + u16 log; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + if (phyblk >= info->MS_Lib.NumberOfPhyBlock) + return (u32)-1; + + log = info->MS_Lib.Phy2LogMap[phyblk]; + + if (log < info->MS_Lib.NumberOfLogBlock) + info->MS_Lib.Log2PhyMap[log] = MS_LB_NOT_USED; + + if (info->MS_Lib.Phy2LogMap[phyblk] != MS_LB_INITIAL_ERROR) + info->MS_Lib.Phy2LogMap[phyblk] = MS_LB_ACQUIRED_ERROR; + + return 0; +} + +static int ms_lib_overwrite_extra(struct us_data *us, u32 PhyBlockAddr, + u8 PageNum, u8 OverwriteFlag) +{ + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + int result; + + result = ene_load_bincode(us, MS_RW_PATTERN); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + memset(bcb, 0, sizeof(struct bulk_cb_wrap)); + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->DataTransferLength = 0x4; + bcb->Flags = US_BULK_FLAG_IN; + bcb->CDB[0] = 0xF2; + bcb->CDB[1] = 0x05; + bcb->CDB[5] = (unsigned char)(PageNum); + bcb->CDB[4] = (unsigned char)(PhyBlockAddr); + bcb->CDB[3] = (unsigned char)(PhyBlockAddr>>8); + bcb->CDB[2] = (unsigned char)(PhyBlockAddr>>16); + bcb->CDB[6] = OverwriteFlag; + bcb->CDB[7] = 0xFF; + bcb->CDB[8] = 0xFF; + bcb->CDB[9] = 0xFF; + + result = ene_send_scsi_cmd(us, FDIR_READ, NULL, 0); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + return USB_STOR_TRANSPORT_GOOD; +} + +static int ms_lib_error_phyblock(struct us_data *us, u16 phyblk) +{ + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + if (phyblk >= info->MS_Lib.NumberOfPhyBlock) + return MS_STATUS_ERROR; + + ms_lib_setacquired_errorblock(us, phyblk); + + if (ms_lib_iswritable(info)) + return ms_lib_overwrite_extra(us, phyblk, 0, (u8)(~MS_REG_OVR_BKST & BYTE_MASK)); + + return MS_STATUS_SUCCESS; +} + +static int ms_lib_erase_phyblock(struct us_data *us, u16 phyblk) +{ + u16 log; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + if (phyblk >= info->MS_Lib.NumberOfPhyBlock) + return MS_STATUS_ERROR; + + log = info->MS_Lib.Phy2LogMap[phyblk]; + + if (log < info->MS_Lib.NumberOfLogBlock) + info->MS_Lib.Log2PhyMap[log] = MS_LB_NOT_USED; + + info->MS_Lib.Phy2LogMap[phyblk] = MS_LB_NOT_USED; + + if (ms_lib_iswritable(info)) { + switch (ms_read_eraseblock(us, phyblk)) { + case MS_STATUS_SUCCESS: + info->MS_Lib.Phy2LogMap[phyblk] = MS_LB_NOT_USED_ERASED; + return MS_STATUS_SUCCESS; + case MS_ERROR_FLASH_ERASE: + case MS_STATUS_INT_ERROR: + ms_lib_error_phyblock(us, phyblk); + return MS_ERROR_FLASH_ERASE; + case MS_STATUS_ERROR: + default: + ms_lib_ctrl_set(info, MS_LIB_CTRL_RDONLY); /* MS_LibCtrlSet will used by ENE_MSInit ,need check, and why us to info*/ + ms_lib_setacquired_errorblock(us, phyblk); + return MS_STATUS_ERROR; + } + } + + ms_lib_setacquired_errorblock(us, phyblk); + + return MS_STATUS_SUCCESS; +} + +static int ms_lib_read_extra(struct us_data *us, u32 PhyBlock, + u8 PageNum, struct ms_lib_type_extdat *ExtraDat) +{ + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + u8 *bbuf = info->bbuf; + int result; + + memset(bcb, 0, sizeof(struct bulk_cb_wrap)); + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->DataTransferLength = 0x4; + bcb->Flags = US_BULK_FLAG_IN; + bcb->CDB[0] = 0xF1; + bcb->CDB[1] = 0x03; + bcb->CDB[5] = (unsigned char)(PageNum); + bcb->CDB[4] = (unsigned char)(PhyBlock); + bcb->CDB[3] = (unsigned char)(PhyBlock>>8); + bcb->CDB[2] = (unsigned char)(PhyBlock>>16); + bcb->CDB[6] = 0x01; + + result = ene_send_scsi_cmd(us, FDIR_READ, bbuf, 0); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + ExtraDat->reserved = 0; + ExtraDat->intr = 0x80; /* Not yet, waiting for fireware support */ + ExtraDat->status0 = 0x10; /* Not yet, waiting for fireware support */ + ExtraDat->status1 = 0x00; /* Not yet, waiting for fireware support */ + ExtraDat->ovrflg = bbuf[0]; + ExtraDat->mngflg = bbuf[1]; + ExtraDat->logadr = memstick_logaddr(bbuf[2], bbuf[3]); + + return USB_STOR_TRANSPORT_GOOD; +} + +static int ms_libsearch_block_from_physical(struct us_data *us, u16 phyblk) +{ + u16 blk; + struct ms_lib_type_extdat extdat; /* need check */ + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + + if (phyblk >= info->MS_Lib.NumberOfPhyBlock) + return MS_LB_ERROR; + + for (blk = phyblk + 1; blk != phyblk; blk++) { + if ((blk & MS_PHYSICAL_BLOCKS_PER_SEGMENT_MASK) == 0) + blk -= MS_PHYSICAL_BLOCKS_PER_SEGMENT; + + if (info->MS_Lib.Phy2LogMap[blk] == MS_LB_NOT_USED_ERASED) { + return blk; + } else if (info->MS_Lib.Phy2LogMap[blk] == MS_LB_NOT_USED) { + switch (ms_lib_read_extra(us, blk, 0, &extdat)) { + case MS_STATUS_SUCCESS: + case MS_STATUS_SUCCESS_WITH_ECC: + break; + case MS_NOCARD_ERROR: + return MS_NOCARD_ERROR; + case MS_STATUS_INT_ERROR: + return MS_LB_ERROR; + case MS_ERROR_FLASH_READ: + default: + ms_lib_setacquired_errorblock(us, blk); + continue; + } /* End switch */ + + if ((extdat.ovrflg & MS_REG_OVR_BKST) != MS_REG_OVR_BKST_OK) { + ms_lib_setacquired_errorblock(us, blk); + continue; + } + + switch (ms_lib_erase_phyblock(us, blk)) { + case MS_STATUS_SUCCESS: + return blk; + case MS_STATUS_ERROR: + return MS_LB_ERROR; + case MS_ERROR_FLASH_ERASE: + default: + ms_lib_error_phyblock(us, blk); + break; + } + } + } /* End for */ + + return MS_LB_ERROR; +} +static int ms_libsearch_block_from_logical(struct us_data *us, u16 logblk) +{ + u16 phyblk; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + phyblk = ms_libconv_to_physical(info, logblk); + if (phyblk >= MS_LB_ERROR) { + if (logblk >= info->MS_Lib.NumberOfLogBlock) + return MS_LB_ERROR; + + phyblk = (logblk + MS_NUMBER_OF_BOOT_BLOCK) / MS_LOGICAL_BLOCKS_PER_SEGMENT; + phyblk *= MS_PHYSICAL_BLOCKS_PER_SEGMENT; + phyblk += MS_PHYSICAL_BLOCKS_PER_SEGMENT - 1; + } + + return ms_libsearch_block_from_physical(us, phyblk); +} + +static int ms_scsi_test_unit_ready(struct us_data *us, struct scsi_cmnd *srb) +{ + struct ene_ub6250_info *info = (struct ene_ub6250_info *)(us->extra); + + /* pr_info("MS_SCSI_Test_Unit_Ready\n"); */ + if ((info->MS_Status & MS_Insert) && (info->MS_Status & MS_Ready)) { + return USB_STOR_TRANSPORT_GOOD; + } else { + ene_ms_init(us); + return USB_STOR_TRANSPORT_GOOD; + } + + return USB_STOR_TRANSPORT_GOOD; +} + +static int ms_scsi_mode_sense(struct us_data *us, struct scsi_cmnd *srb) +{ + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + unsigned char mediaNoWP[12] = { + 0x0b, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x71, 0xc0, 0x00, 0x00, 0x02, 0x00 }; + unsigned char mediaWP[12] = { + 0x0b, 0x00, 0x80, 0x08, 0x00, 0x00, + 0x71, 0xc0, 0x00, 0x00, 0x02, 0x00 }; + + if (info->MS_Status & MS_WtP) + usb_stor_set_xfer_buf(mediaWP, 12, srb); + else + usb_stor_set_xfer_buf(mediaNoWP, 12, srb); + + return USB_STOR_TRANSPORT_GOOD; +} + +static int ms_scsi_read_capacity(struct us_data *us, struct scsi_cmnd *srb) +{ + u32 bl_num; + u32 bl_len; + unsigned int offset = 0; + unsigned char buf[8]; + struct scatterlist *sg = NULL; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + usb_stor_dbg(us, "ms_scsi_read_capacity\n"); + bl_len = 0x200; + if (info->MS_Status & MS_IsMSPro) + bl_num = info->MSP_TotalBlock - 1; + else + bl_num = info->MS_Lib.NumberOfLogBlock * info->MS_Lib.blockSize * 2 - 1; + + info->bl_num = bl_num; + usb_stor_dbg(us, "bl_len = %x\n", bl_len); + usb_stor_dbg(us, "bl_num = %x\n", bl_num); + + /*srb->request_bufflen = 8; */ + buf[0] = (bl_num >> 24) & 0xff; + buf[1] = (bl_num >> 16) & 0xff; + buf[2] = (bl_num >> 8) & 0xff; + buf[3] = (bl_num >> 0) & 0xff; + buf[4] = (bl_len >> 24) & 0xff; + buf[5] = (bl_len >> 16) & 0xff; + buf[6] = (bl_len >> 8) & 0xff; + buf[7] = (bl_len >> 0) & 0xff; + + usb_stor_access_xfer_buf(buf, 8, srb, &sg, &offset, TO_XFER_BUF); + + return USB_STOR_TRANSPORT_GOOD; +} + +static void ms_lib_phy_to_log_range(u16 PhyBlock, u16 *LogStart, u16 *LogEnde) +{ + PhyBlock /= MS_PHYSICAL_BLOCKS_PER_SEGMENT; + + if (PhyBlock) { + *LogStart = MS_LOGICAL_BLOCKS_IN_1ST_SEGMENT + (PhyBlock - 1) * MS_LOGICAL_BLOCKS_PER_SEGMENT;/*496*/ + *LogEnde = *LogStart + MS_LOGICAL_BLOCKS_PER_SEGMENT;/*496*/ + } else { + *LogStart = 0; + *LogEnde = MS_LOGICAL_BLOCKS_IN_1ST_SEGMENT;/*494*/ + } +} + +static int ms_lib_read_extrablock(struct us_data *us, u32 PhyBlock, + u8 PageNum, u8 blen, void *buf) +{ + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + int result; + + /* Read Extra Data */ + memset(bcb, 0, sizeof(struct bulk_cb_wrap)); + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->DataTransferLength = 0x4 * blen; + bcb->Flags = US_BULK_FLAG_IN; + bcb->CDB[0] = 0xF1; + bcb->CDB[1] = 0x03; + bcb->CDB[5] = (unsigned char)(PageNum); + bcb->CDB[4] = (unsigned char)(PhyBlock); + bcb->CDB[3] = (unsigned char)(PhyBlock>>8); + bcb->CDB[2] = (unsigned char)(PhyBlock>>16); + bcb->CDB[6] = blen; + + result = ene_send_scsi_cmd(us, FDIR_READ, buf, 0); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + return USB_STOR_TRANSPORT_GOOD; +} + +static int ms_lib_scan_logicalblocknumber(struct us_data *us, u16 btBlk1st) +{ + u16 PhyBlock, newblk, i; + u16 LogStart, LogEnde; + struct ms_lib_type_extdat extdat; + u32 count = 0, index = 0; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + u8 *bbuf = info->bbuf; + + for (PhyBlock = 0; PhyBlock < info->MS_Lib.NumberOfPhyBlock;) { + ms_lib_phy_to_log_range(PhyBlock, &LogStart, &LogEnde); + + for (i = 0; i < MS_PHYSICAL_BLOCKS_PER_SEGMENT; i++, PhyBlock++) { + switch (ms_libconv_to_logical(info, PhyBlock)) { + case MS_STATUS_ERROR: + continue; + default: + break; + } + + if (count == PhyBlock) { + ms_lib_read_extrablock(us, PhyBlock, 0, 0x80, + bbuf); + count += 0x80; + } + index = (PhyBlock % 0x80) * 4; + + extdat.ovrflg = bbuf[index]; + extdat.mngflg = bbuf[index+1]; + extdat.logadr = memstick_logaddr(bbuf[index+2], + bbuf[index+3]); + + if ((extdat.ovrflg & MS_REG_OVR_BKST) != MS_REG_OVR_BKST_OK) { + ms_lib_setacquired_errorblock(us, PhyBlock); + continue; + } + + if ((extdat.mngflg & MS_REG_MNG_ATFLG) == MS_REG_MNG_ATFLG_ATTBL) { + ms_lib_erase_phyblock(us, PhyBlock); + continue; + } + + if (extdat.logadr != MS_LB_NOT_USED) { + if ((extdat.logadr < LogStart) || (LogEnde <= extdat.logadr)) { + ms_lib_erase_phyblock(us, PhyBlock); + continue; + } + + newblk = ms_libconv_to_physical(info, extdat.logadr); + + if (newblk != MS_LB_NOT_USED) { + if (extdat.logadr == 0) { + ms_lib_set_logicalpair(us, extdat.logadr, PhyBlock); + if (ms_lib_check_disableblock(us, btBlk1st)) { + ms_lib_set_logicalpair(us, extdat.logadr, newblk); + continue; + } + } + + ms_lib_read_extra(us, newblk, 0, &extdat); + if ((extdat.ovrflg & MS_REG_OVR_UDST) == MS_REG_OVR_UDST_UPDATING) { + ms_lib_erase_phyblock(us, PhyBlock); + continue; + } else { + ms_lib_erase_phyblock(us, newblk); + } + } + + ms_lib_set_logicalpair(us, extdat.logadr, PhyBlock); + } + } + } /* End for ... */ + + return MS_STATUS_SUCCESS; +} + + +static int ms_scsi_read(struct us_data *us, struct scsi_cmnd *srb) +{ + int result; + unsigned char *cdb = srb->cmnd; + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + u32 bn = ((cdb[2] << 24) & 0xff000000) | ((cdb[3] << 16) & 0x00ff0000) | + ((cdb[4] << 8) & 0x0000ff00) | ((cdb[5] << 0) & 0x000000ff); + u16 blen = ((cdb[7] << 8) & 0xff00) | ((cdb[8] << 0) & 0x00ff); + u32 blenByte = blen * 0x200; + + if (bn > info->bl_num) + return USB_STOR_TRANSPORT_ERROR; + + if (info->MS_Status & MS_IsMSPro) { + result = ene_load_bincode(us, MSP_RW_PATTERN); + if (result != USB_STOR_XFER_GOOD) { + usb_stor_dbg(us, "Load MPS RW pattern Fail !!\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + /* set up the command wrapper */ + memset(bcb, 0, sizeof(struct bulk_cb_wrap)); + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->DataTransferLength = blenByte; + bcb->Flags = US_BULK_FLAG_IN; + bcb->CDB[0] = 0xF1; + bcb->CDB[1] = 0x02; + bcb->CDB[5] = (unsigned char)(bn); + bcb->CDB[4] = (unsigned char)(bn>>8); + bcb->CDB[3] = (unsigned char)(bn>>16); + bcb->CDB[2] = (unsigned char)(bn>>24); + + result = ene_send_scsi_cmd(us, FDIR_READ, scsi_sglist(srb), 1); + } else { + void *buf; + int offset = 0; + u16 phyblk, logblk; + u8 PageNum; + u16 len; + u32 blkno; + + buf = kmalloc(blenByte, GFP_KERNEL); + if (buf == NULL) + return USB_STOR_TRANSPORT_ERROR; + + result = ene_load_bincode(us, MS_RW_PATTERN); + if (result != USB_STOR_XFER_GOOD) { + pr_info("Load MS RW pattern Fail !!\n"); + result = USB_STOR_TRANSPORT_ERROR; + goto exit; + } + + logblk = (u16)(bn / info->MS_Lib.PagesPerBlock); + PageNum = (u8)(bn % info->MS_Lib.PagesPerBlock); + + while (1) { + if (blen > (info->MS_Lib.PagesPerBlock-PageNum)) + len = info->MS_Lib.PagesPerBlock-PageNum; + else + len = blen; + + phyblk = ms_libconv_to_physical(info, logblk); + blkno = phyblk * 0x20 + PageNum; + + /* set up the command wrapper */ + memset(bcb, 0, sizeof(struct bulk_cb_wrap)); + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->DataTransferLength = 0x200 * len; + bcb->Flags = US_BULK_FLAG_IN; + bcb->CDB[0] = 0xF1; + bcb->CDB[1] = 0x02; + bcb->CDB[5] = (unsigned char)(blkno); + bcb->CDB[4] = (unsigned char)(blkno>>8); + bcb->CDB[3] = (unsigned char)(blkno>>16); + bcb->CDB[2] = (unsigned char)(blkno>>24); + + result = ene_send_scsi_cmd(us, FDIR_READ, buf+offset, 0); + if (result != USB_STOR_XFER_GOOD) { + pr_info("MS_SCSI_Read --- result = %x\n", result); + result = USB_STOR_TRANSPORT_ERROR; + goto exit; + } + + blen -= len; + if (blen <= 0) + break; + logblk++; + PageNum = 0; + offset += MS_BYTES_PER_PAGE*len; + } + usb_stor_set_xfer_buf(buf, blenByte, srb); +exit: + kfree(buf); + } + return result; +} + +static int ms_scsi_write(struct us_data *us, struct scsi_cmnd *srb) +{ + int result; + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + unsigned char *cdb = srb->cmnd; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + u32 bn = ((cdb[2] << 24) & 0xff000000) | + ((cdb[3] << 16) & 0x00ff0000) | + ((cdb[4] << 8) & 0x0000ff00) | + ((cdb[5] << 0) & 0x000000ff); + u16 blen = ((cdb[7] << 8) & 0xff00) | ((cdb[8] << 0) & 0x00ff); + u32 blenByte = blen * 0x200; + + if (bn > info->bl_num) + return USB_STOR_TRANSPORT_ERROR; + + if (info->MS_Status & MS_IsMSPro) { + result = ene_load_bincode(us, MSP_RW_PATTERN); + if (result != USB_STOR_XFER_GOOD) { + pr_info("Load MSP RW pattern Fail !!\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + /* set up the command wrapper */ + memset(bcb, 0, sizeof(struct bulk_cb_wrap)); + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->DataTransferLength = blenByte; + bcb->Flags = US_BULK_FLAG_OUT; + bcb->CDB[0] = 0xF0; + bcb->CDB[1] = 0x04; + bcb->CDB[5] = (unsigned char)(bn); + bcb->CDB[4] = (unsigned char)(bn>>8); + bcb->CDB[3] = (unsigned char)(bn>>16); + bcb->CDB[2] = (unsigned char)(bn>>24); + + result = ene_send_scsi_cmd(us, FDIR_WRITE, scsi_sglist(srb), 1); + } else { + void *buf; + int offset = 0; + u16 PhyBlockAddr; + u8 PageNum; + u16 len, oldphy, newphy; + + buf = kmalloc(blenByte, GFP_KERNEL); + if (buf == NULL) + return USB_STOR_TRANSPORT_ERROR; + usb_stor_set_xfer_buf(buf, blenByte, srb); + + result = ene_load_bincode(us, MS_RW_PATTERN); + if (result != USB_STOR_XFER_GOOD) { + pr_info("Load MS RW pattern Fail !!\n"); + result = USB_STOR_TRANSPORT_ERROR; + goto exit; + } + + PhyBlockAddr = (u16)(bn / info->MS_Lib.PagesPerBlock); + PageNum = (u8)(bn % info->MS_Lib.PagesPerBlock); + + while (1) { + if (blen > (info->MS_Lib.PagesPerBlock-PageNum)) + len = info->MS_Lib.PagesPerBlock-PageNum; + else + len = blen; + + oldphy = ms_libconv_to_physical(info, PhyBlockAddr); /* need check us <-> info */ + newphy = ms_libsearch_block_from_logical(us, PhyBlockAddr); + + result = ms_read_copyblock(us, oldphy, newphy, PhyBlockAddr, PageNum, buf+offset, len); + + if (result != USB_STOR_XFER_GOOD) { + pr_info("MS_SCSI_Write --- result = %x\n", result); + result = USB_STOR_TRANSPORT_ERROR; + goto exit; + } + + info->MS_Lib.Phy2LogMap[oldphy] = MS_LB_NOT_USED_ERASED; + ms_lib_force_setlogical_pair(us, PhyBlockAddr, newphy); + + blen -= len; + if (blen <= 0) + break; + PhyBlockAddr++; + PageNum = 0; + offset += MS_BYTES_PER_PAGE*len; + } +exit: + kfree(buf); + } + return result; +} + +/* + * ENE MS Card + */ + +static int ene_get_card_type(struct us_data *us, u16 index, void *buf) +{ + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + int result; + + memset(bcb, 0, sizeof(struct bulk_cb_wrap)); + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->DataTransferLength = 0x01; + bcb->Flags = US_BULK_FLAG_IN; + bcb->CDB[0] = 0xED; + bcb->CDB[2] = (unsigned char)(index>>8); + bcb->CDB[3] = (unsigned char)index; + + result = ene_send_scsi_cmd(us, FDIR_READ, buf, 0); + return result; +} + +static int ene_get_card_status(struct us_data *us, u8 *buf) +{ + u16 tmpreg; + u32 reg4b; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + /*usb_stor_dbg(us, "transport --- ENE_ReadSDReg\n");*/ + reg4b = *(u32 *)&buf[0x18]; + info->SD_READ_BL_LEN = (u8)((reg4b >> 8) & 0x0f); + + tmpreg = (u16) reg4b; + reg4b = *(u32 *)(&buf[0x14]); + if ((info->SD_Status & SD_HiCapacity) && !(info->SD_Status & SD_IsMMC)) + info->HC_C_SIZE = (reg4b >> 8) & 0x3fffff; + + info->SD_C_SIZE = ((tmpreg & 0x03) << 10) | (u16)(reg4b >> 22); + info->SD_C_SIZE_MULT = (u8)(reg4b >> 7) & 0x07; + if ((info->SD_Status & SD_HiCapacity) && (info->SD_Status & SD_IsMMC)) + info->HC_C_SIZE = *(u32 *)(&buf[0x100]); + + if (info->SD_READ_BL_LEN > SD_BLOCK_LEN) { + info->SD_Block_Mult = 1 << (info->SD_READ_BL_LEN-SD_BLOCK_LEN); + info->SD_READ_BL_LEN = SD_BLOCK_LEN; + } else { + info->SD_Block_Mult = 1; + } + + return USB_STOR_TRANSPORT_GOOD; +} + +static int ene_load_bincode(struct us_data *us, unsigned char flag) +{ + int err; + char *fw_name = NULL; + unsigned char *buf = NULL; + const struct firmware *sd_fw = NULL; + int result = USB_STOR_TRANSPORT_ERROR; + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + if (info->BIN_FLAG == flag) + return USB_STOR_TRANSPORT_GOOD; + + switch (flag) { + /* For SD */ + case SD_INIT1_PATTERN: + usb_stor_dbg(us, "SD_INIT1_PATTERN\n"); + fw_name = SD_INIT1_FIRMWARE; + break; + case SD_INIT2_PATTERN: + usb_stor_dbg(us, "SD_INIT2_PATTERN\n"); + fw_name = SD_INIT2_FIRMWARE; + break; + case SD_RW_PATTERN: + usb_stor_dbg(us, "SD_RW_PATTERN\n"); + fw_name = SD_RW_FIRMWARE; + break; + /* For MS */ + case MS_INIT_PATTERN: + usb_stor_dbg(us, "MS_INIT_PATTERN\n"); + fw_name = MS_INIT_FIRMWARE; + break; + case MSP_RW_PATTERN: + usb_stor_dbg(us, "MSP_RW_PATTERN\n"); + fw_name = MSP_RW_FIRMWARE; + break; + case MS_RW_PATTERN: + usb_stor_dbg(us, "MS_RW_PATTERN\n"); + fw_name = MS_RW_FIRMWARE; + break; + default: + usb_stor_dbg(us, "----------- Unknown PATTERN ----------\n"); + goto nofw; + } + + err = request_firmware(&sd_fw, fw_name, &us->pusb_dev->dev); + if (err) { + usb_stor_dbg(us, "load firmware %s failed\n", fw_name); + goto nofw; + } + buf = kmemdup(sd_fw->data, sd_fw->size, GFP_KERNEL); + if (buf == NULL) + goto nofw; + + memset(bcb, 0, sizeof(struct bulk_cb_wrap)); + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->DataTransferLength = sd_fw->size; + bcb->Flags = US_BULK_FLAG_OUT; + bcb->CDB[0] = 0xEF; + + result = ene_send_scsi_cmd(us, FDIR_WRITE, buf, 0); + if (us->srb != NULL) + scsi_set_resid(us->srb, 0); + info->BIN_FLAG = flag; + kfree(buf); + +nofw: + release_firmware(sd_fw); + return result; +} + +static int ms_card_init(struct us_data *us) +{ + u32 result; + u16 TmpBlock; + unsigned char *PageBuffer0 = NULL, *PageBuffer1 = NULL; + struct ms_lib_type_extdat extdat; + u16 btBlk1st, btBlk2nd; + u32 btBlk1stErred; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + + printk(KERN_INFO "MS_CardInit start\n"); + + ms_lib_free_allocatedarea(us); /* Clean buffer and set struct us_data flag to 0 */ + + /* get two PageBuffer */ + PageBuffer0 = kmalloc(MS_BYTES_PER_PAGE, GFP_KERNEL); + PageBuffer1 = kmalloc(MS_BYTES_PER_PAGE, GFP_KERNEL); + if ((PageBuffer0 == NULL) || (PageBuffer1 == NULL)) { + result = MS_NO_MEMORY_ERROR; + goto exit; + } + + btBlk1st = btBlk2nd = MS_LB_NOT_USED; + btBlk1stErred = 0; + + for (TmpBlock = 0; TmpBlock < MS_MAX_INITIAL_ERROR_BLOCKS+2; TmpBlock++) { + + switch (ms_read_readpage(us, TmpBlock, 0, (u32 *)PageBuffer0, &extdat)) { + case MS_STATUS_SUCCESS: + break; + case MS_STATUS_INT_ERROR: + break; + case MS_STATUS_ERROR: + default: + continue; + } + + if ((extdat.ovrflg & MS_REG_OVR_BKST) == MS_REG_OVR_BKST_NG) + continue; + + if (((extdat.mngflg & MS_REG_MNG_SYSFLG) == MS_REG_MNG_SYSFLG_USER) || + (be16_to_cpu(((struct ms_bootblock_page0 *)PageBuffer0)->header.wBlockID) != MS_BOOT_BLOCK_ID) || + (be16_to_cpu(((struct ms_bootblock_page0 *)PageBuffer0)->header.wFormatVersion) != MS_BOOT_BLOCK_FORMAT_VERSION) || + (((struct ms_bootblock_page0 *)PageBuffer0)->header.bNumberOfDataEntry != MS_BOOT_BLOCK_DATA_ENTRIES)) + continue; + + if (btBlk1st != MS_LB_NOT_USED) { + btBlk2nd = TmpBlock; + break; + } + + btBlk1st = TmpBlock; + memcpy(PageBuffer1, PageBuffer0, MS_BYTES_PER_PAGE); + if (extdat.status1 & (MS_REG_ST1_DTER | MS_REG_ST1_EXER | MS_REG_ST1_FGER)) + btBlk1stErred = 1; + } + + if (btBlk1st == MS_LB_NOT_USED) { + result = MS_STATUS_ERROR; + goto exit; + } + + /* write protect */ + if ((extdat.status0 & MS_REG_ST0_WP) == MS_REG_ST0_WP_ON) + ms_lib_ctrl_set(info, MS_LIB_CTRL_WRPROTECT); + + result = MS_STATUS_ERROR; + /* 1st Boot Block */ + if (btBlk1stErred == 0) + result = ms_lib_process_bootblock(us, btBlk1st, PageBuffer1); + /* 1st */ + /* 2nd Boot Block */ + if (result && (btBlk2nd != MS_LB_NOT_USED)) + result = ms_lib_process_bootblock(us, btBlk2nd, PageBuffer0); + + if (result) { + result = MS_STATUS_ERROR; + goto exit; + } + + for (TmpBlock = 0; TmpBlock < btBlk1st; TmpBlock++) + info->MS_Lib.Phy2LogMap[TmpBlock] = MS_LB_INITIAL_ERROR; + + info->MS_Lib.Phy2LogMap[btBlk1st] = MS_LB_BOOT_BLOCK; + + if (btBlk2nd != MS_LB_NOT_USED) { + for (TmpBlock = btBlk1st + 1; TmpBlock < btBlk2nd; TmpBlock++) + info->MS_Lib.Phy2LogMap[TmpBlock] = MS_LB_INITIAL_ERROR; + + info->MS_Lib.Phy2LogMap[btBlk2nd] = MS_LB_BOOT_BLOCK; + } + + result = ms_lib_scan_logicalblocknumber(us, btBlk1st); + if (result) + goto exit; + + for (TmpBlock = MS_PHYSICAL_BLOCKS_PER_SEGMENT; + TmpBlock < info->MS_Lib.NumberOfPhyBlock; + TmpBlock += MS_PHYSICAL_BLOCKS_PER_SEGMENT) { + if (ms_count_freeblock(us, TmpBlock) == 0) { + ms_lib_ctrl_set(info, MS_LIB_CTRL_WRPROTECT); + break; + } + } + + /* write */ + if (ms_lib_alloc_writebuf(us)) { + result = MS_NO_MEMORY_ERROR; + goto exit; + } + + result = MS_STATUS_SUCCESS; + +exit: + kfree(PageBuffer1); + kfree(PageBuffer0); + + printk(KERN_INFO "MS_CardInit end\n"); + return result; +} + +static int ene_ms_init(struct us_data *us) +{ + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + int result; + u16 MSP_BlockSize, MSP_UserAreaBlocks; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + u8 *bbuf = info->bbuf; + unsigned int s; + + printk(KERN_INFO "transport --- ENE_MSInit\n"); + + /* the same part to test ENE */ + + result = ene_load_bincode(us, MS_INIT_PATTERN); + if (result != USB_STOR_XFER_GOOD) { + printk(KERN_ERR "Load MS Init Code Fail !!\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + memset(bcb, 0, sizeof(struct bulk_cb_wrap)); + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->DataTransferLength = 0x200; + bcb->Flags = US_BULK_FLAG_IN; + bcb->CDB[0] = 0xF1; + bcb->CDB[1] = 0x01; + + result = ene_send_scsi_cmd(us, FDIR_READ, bbuf, 0); + if (result != USB_STOR_XFER_GOOD) { + printk(KERN_ERR "Execution MS Init Code Fail !!\n"); + return USB_STOR_TRANSPORT_ERROR; + } + /* the same part to test ENE */ + info->MS_Status = bbuf[0]; + + s = info->MS_Status; + if ((s & MS_Insert) && (s & MS_Ready)) { + printk(KERN_INFO "Insert = %x\n", !!(s & MS_Insert)); + printk(KERN_INFO "Ready = %x\n", !!(s & MS_Ready)); + printk(KERN_INFO "IsMSPro = %x\n", !!(s & MS_IsMSPro)); + printk(KERN_INFO "IsMSPHG = %x\n", !!(s & MS_IsMSPHG)); + printk(KERN_INFO "WtP= %x\n", !!(s & MS_WtP)); + if (s & MS_IsMSPro) { + MSP_BlockSize = (bbuf[6] << 8) | bbuf[7]; + MSP_UserAreaBlocks = (bbuf[10] << 8) | bbuf[11]; + info->MSP_TotalBlock = MSP_BlockSize * MSP_UserAreaBlocks; + } else { + ms_card_init(us); /* Card is MS (to ms.c)*/ + } + usb_stor_dbg(us, "MS Init Code OK !!\n"); + } else { + usb_stor_dbg(us, "MS Card Not Ready --- %x\n", bbuf[0]); + return USB_STOR_TRANSPORT_ERROR; + } + + return USB_STOR_TRANSPORT_GOOD; +} + +static int ene_sd_init(struct us_data *us) +{ + int result; + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + u8 *bbuf = info->bbuf; + + usb_stor_dbg(us, "transport --- ENE_SDInit\n"); + /* SD Init Part-1 */ + result = ene_load_bincode(us, SD_INIT1_PATTERN); + if (result != USB_STOR_XFER_GOOD) { + usb_stor_dbg(us, "Load SD Init Code Part-1 Fail !!\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + memset(bcb, 0, sizeof(struct bulk_cb_wrap)); + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->Flags = US_BULK_FLAG_IN; + bcb->CDB[0] = 0xF2; + + result = ene_send_scsi_cmd(us, FDIR_READ, NULL, 0); + if (result != USB_STOR_XFER_GOOD) { + usb_stor_dbg(us, "Execution SD Init Code Fail !!\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + /* SD Init Part-2 */ + result = ene_load_bincode(us, SD_INIT2_PATTERN); + if (result != USB_STOR_XFER_GOOD) { + usb_stor_dbg(us, "Load SD Init Code Part-2 Fail !!\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + memset(bcb, 0, sizeof(struct bulk_cb_wrap)); + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->DataTransferLength = 0x200; + bcb->Flags = US_BULK_FLAG_IN; + bcb->CDB[0] = 0xF1; + + result = ene_send_scsi_cmd(us, FDIR_READ, bbuf, 0); + if (result != USB_STOR_XFER_GOOD) { + usb_stor_dbg(us, "Execution SD Init Code Fail !!\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + info->SD_Status = bbuf[0]; + if ((info->SD_Status & SD_Insert) && (info->SD_Status & SD_Ready)) { + unsigned int s = info->SD_Status; + + ene_get_card_status(us, bbuf); + usb_stor_dbg(us, "Insert = %x\n", !!(s & SD_Insert)); + usb_stor_dbg(us, "Ready = %x\n", !!(s & SD_Ready)); + usb_stor_dbg(us, "IsMMC = %x\n", !!(s & SD_IsMMC)); + usb_stor_dbg(us, "HiCapacity = %x\n", !!(s & SD_HiCapacity)); + usb_stor_dbg(us, "HiSpeed = %x\n", !!(s & SD_HiSpeed)); + usb_stor_dbg(us, "WtP = %x\n", !!(s & SD_WtP)); + } else { + usb_stor_dbg(us, "SD Card Not Ready --- %x\n", bbuf[0]); + return USB_STOR_TRANSPORT_ERROR; + } + return USB_STOR_TRANSPORT_GOOD; +} + + +static int ene_init(struct us_data *us) +{ + int result; + u8 misc_reg03; + struct ene_ub6250_info *info = (struct ene_ub6250_info *)(us->extra); + u8 *bbuf = info->bbuf; + + result = ene_get_card_type(us, REG_CARD_STATUS, bbuf); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + misc_reg03 = bbuf[0]; + if (misc_reg03 & 0x01) { + if (!(info->SD_Status & SD_Ready)) { + result = ene_sd_init(us); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + } + } + if (misc_reg03 & 0x02) { + if (!(info->MS_Status & MS_Ready)) { + result = ene_ms_init(us); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + } + } + return result; +} + +/*----- sd_scsi_irp() ---------*/ +static int sd_scsi_irp(struct us_data *us, struct scsi_cmnd *srb) +{ + int result; + struct ene_ub6250_info *info = (struct ene_ub6250_info *)us->extra; + + switch (srb->cmnd[0]) { + case TEST_UNIT_READY: + result = sd_scsi_test_unit_ready(us, srb); + break; /* 0x00 */ + case REQUEST_SENSE: + result = do_scsi_request_sense(us, srb); + break; /* 0x03 */ + case INQUIRY: + result = do_scsi_inquiry(us, srb); + break; /* 0x12 */ + case MODE_SENSE: + result = sd_scsi_mode_sense(us, srb); + break; /* 0x1A */ + /* + case START_STOP: + result = SD_SCSI_Start_Stop(us, srb); + break; //0x1B + */ + case READ_CAPACITY: + result = sd_scsi_read_capacity(us, srb); + break; /* 0x25 */ + case READ_10: + result = sd_scsi_read(us, srb); + break; /* 0x28 */ + case WRITE_10: + result = sd_scsi_write(us, srb); + break; /* 0x2A */ + default: + info->SrbStatus = SS_ILLEGAL_REQUEST; + result = USB_STOR_TRANSPORT_FAILED; + break; + } + if (result == USB_STOR_TRANSPORT_GOOD) + info->SrbStatus = SS_SUCCESS; + return result; +} + +/* + * ms_scsi_irp() + */ +static int ms_scsi_irp(struct us_data *us, struct scsi_cmnd *srb) +{ + int result; + struct ene_ub6250_info *info = (struct ene_ub6250_info *)us->extra; + + switch (srb->cmnd[0]) { + case TEST_UNIT_READY: + result = ms_scsi_test_unit_ready(us, srb); + break; /* 0x00 */ + case REQUEST_SENSE: + result = do_scsi_request_sense(us, srb); + break; /* 0x03 */ + case INQUIRY: + result = do_scsi_inquiry(us, srb); + break; /* 0x12 */ + case MODE_SENSE: + result = ms_scsi_mode_sense(us, srb); + break; /* 0x1A */ + case READ_CAPACITY: + result = ms_scsi_read_capacity(us, srb); + break; /* 0x25 */ + case READ_10: + result = ms_scsi_read(us, srb); + break; /* 0x28 */ + case WRITE_10: + result = ms_scsi_write(us, srb); + break; /* 0x2A */ + default: + info->SrbStatus = SS_ILLEGAL_REQUEST; + result = USB_STOR_TRANSPORT_FAILED; + break; + } + if (result == USB_STOR_TRANSPORT_GOOD) + info->SrbStatus = SS_SUCCESS; + return result; +} + +static int ene_transport(struct scsi_cmnd *srb, struct us_data *us) +{ + int result = USB_STOR_XFER_GOOD; + struct ene_ub6250_info *info = (struct ene_ub6250_info *)(us->extra); + + /*US_DEBUG(usb_stor_show_command(us, srb)); */ + scsi_set_resid(srb, 0); + if (unlikely(!(info->SD_Status & SD_Ready) && + !(info->MS_Status & MS_Ready))) + result = ene_init(us); + if (result == USB_STOR_XFER_GOOD) { + result = USB_STOR_TRANSPORT_ERROR; + if (info->SD_Status & SD_Ready) + result = sd_scsi_irp(us, srb); + + if (info->MS_Status & MS_Ready) + result = ms_scsi_irp(us, srb); + } + return result; +} + +static struct scsi_host_template ene_ub6250_host_template; + +static int ene_ub6250_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + int result; + u8 misc_reg03; + struct us_data *us; + struct ene_ub6250_info *info; + + result = usb_stor_probe1(&us, intf, id, + (id - ene_ub6250_usb_ids) + ene_ub6250_unusual_dev_list, + &ene_ub6250_host_template); + if (result) + return result; + + /* FIXME: where should the code alloc extra buf ? */ + us->extra = kzalloc_obj(struct ene_ub6250_info); + if (!us->extra) + return -ENOMEM; + us->extra_destructor = ene_ub6250_info_destructor; + + info = (struct ene_ub6250_info *)(us->extra); + info->bbuf = kmalloc(512, GFP_KERNEL); + if (!info->bbuf) { + kfree(us->extra); + return -ENOMEM; + } + + us->transport_name = "ene_ub6250"; + us->transport = ene_transport; + us->max_lun = 0; + + result = usb_stor_probe2(us); + if (result) + return result; + + /* probe card type */ + mutex_lock(&us->dev_mutex); + result = ene_get_card_type(us, REG_CARD_STATUS, info->bbuf); + mutex_unlock(&us->dev_mutex); + if (result != USB_STOR_XFER_GOOD) { + usb_stor_disconnect(intf); + return USB_STOR_TRANSPORT_ERROR; + } + + misc_reg03 = info->bbuf[0]; + if (!(misc_reg03 & 0x01)) { + pr_info("ums_eneub6250: This driver only supports SD/MS cards. " + "It does not support SM cards.\n"); + } + + return result; +} + + +#ifdef CONFIG_PM + +static int ene_ub6250_resume(struct usb_interface *iface) +{ + struct us_data *us = usb_get_intfdata(iface); + struct ene_ub6250_info *info = (struct ene_ub6250_info *)(us->extra); + + mutex_lock(&us->dev_mutex); + + if (us->suspend_resume_hook) + (us->suspend_resume_hook)(us, US_RESUME); + + mutex_unlock(&us->dev_mutex); + + info->Power_IsResum = true; + /* info->SD_Status &= ~SD_Ready; */ + info->SD_Status = 0; + info->MS_Status = 0; + info->SM_Status = 0; + + return 0; +} + +static int ene_ub6250_reset_resume(struct usb_interface *iface) +{ + struct us_data *us = usb_get_intfdata(iface); + struct ene_ub6250_info *info = (struct ene_ub6250_info *)(us->extra); + + /* Report the reset to the SCSI core */ + usb_stor_reset_resume(iface); + + /* + * FIXME: Notify the subdrivers that they need to reinitialize + * the device + */ + info->Power_IsResum = true; + /* info->SD_Status &= ~SD_Ready; */ + info->SD_Status = 0; + info->MS_Status = 0; + info->SM_Status = 0; + + return 0; +} + +#else + +#define ene_ub6250_resume NULL +#define ene_ub6250_reset_resume NULL + +#endif + +static struct usb_driver ene_ub6250_driver = { + .name = DRV_NAME, + .probe = ene_ub6250_probe, + .disconnect = usb_stor_disconnect, + .suspend = usb_stor_suspend, + .resume = ene_ub6250_resume, + .reset_resume = ene_ub6250_reset_resume, + .pre_reset = usb_stor_pre_reset, + .post_reset = usb_stor_post_reset, + .id_table = ene_ub6250_usb_ids, + .soft_unbind = 1, + .no_dynamic_id = 1, +}; + +module_usb_stor_driver(ene_ub6250_driver, ene_ub6250_host_template, DRV_NAME); diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c new file mode 100644 index 000000000..a07562090 --- /dev/null +++ b/drivers/usb/storage/freecom.c @@ -0,0 +1,577 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Freecom USB/IDE adaptor + * + * Freecom v0.1: + * + * First release + * + * Current development and maintenance by: + * (C) 2000 David Brown + * + * This driver was developed with information provided in FREECOM's USB + * Programmers Reference Guide. For further information contact Freecom + * (https://www.freecom.de/) + */ + +#include +#include +#include + +#include "usb.h" +#include "transport.h" +#include "protocol.h" +#include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-freecom" + +MODULE_DESCRIPTION("Driver for Freecom USB/IDE adaptor"); +MODULE_AUTHOR("David Brown "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("USB_STORAGE"); + +#ifdef CONFIG_USB_STORAGE_DEBUG +static void pdump(struct us_data *us, void *ibuffer, int length); +#endif + +/* Bits of HD_STATUS */ +#define ERR_STAT 0x01 +#define DRQ_STAT 0x08 + +/* All of the outgoing packets are 64 bytes long. */ +struct freecom_cb_wrap { + u8 Type; /* Command type. */ + u8 Timeout; /* Timeout in seconds. */ + u8 Atapi[12]; /* An ATAPI packet. */ + u8 Filler[50]; /* Padding Data. */ +}; + +struct freecom_xfer_wrap { + u8 Type; /* Command type. */ + u8 Timeout; /* Timeout in seconds. */ + __le32 Count; /* Number of bytes to transfer. */ + u8 Pad[58]; +} __attribute__ ((packed)); + +struct freecom_ide_out { + u8 Type; /* Type + IDE register. */ + u8 Pad; + __le16 Value; /* Value to write. */ + u8 Pad2[60]; +}; + +struct freecom_ide_in { + u8 Type; /* Type | IDE register. */ + u8 Pad[63]; +}; + +struct freecom_status { + u8 Status; + u8 Reason; + __le16 Count; + u8 Pad[60]; +}; + +/* + * Freecom stuffs the interrupt status in the INDEX_STAT bit of the ide + * register. + */ +#define FCM_INT_STATUS 0x02 /* INDEX_STAT */ +#define FCM_STATUS_BUSY 0x80 + +/* + * These are the packet types. The low bit indicates that this command + * should wait for an interrupt. + */ +#define FCM_PACKET_ATAPI 0x21 +#define FCM_PACKET_STATUS 0x20 + +/* + * Receive data from the IDE interface. The ATAPI packet has already + * waited, so the data should be immediately available. + */ +#define FCM_PACKET_INPUT 0x81 + +/* Send data to the IDE interface. */ +#define FCM_PACKET_OUTPUT 0x01 + +/* + * Write a value to an ide register. Or the ide register to write after + * munging the address a bit. + */ +#define FCM_PACKET_IDE_WRITE 0x40 +#define FCM_PACKET_IDE_READ 0xC0 + +/* All packets (except for status) are 64 bytes long. */ +#define FCM_PACKET_LENGTH 64 +#define FCM_STATUS_PACKET_LENGTH 4 + +static int init_freecom(struct us_data *us); + + +/* + * The table of devices + */ +#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ + vendorName, productName, useProtocol, useTransport, \ + initFunction, flags) \ +{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ + .driver_info = (flags) } + +static const struct usb_device_id freecom_usb_ids[] = { +# include "unusual_freecom.h" + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, freecom_usb_ids); + +#undef UNUSUAL_DEV + +/* + * The flags table + */ +#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ + vendor_name, product_name, use_protocol, use_transport, \ + init_function, Flags) \ +{ \ + .vendorName = vendor_name, \ + .productName = product_name, \ + .useProtocol = use_protocol, \ + .useTransport = use_transport, \ + .initFunction = init_function, \ +} + +static const struct us_unusual_dev freecom_unusual_dev_list[] = { +# include "unusual_freecom.h" + { } /* Terminating entry */ +}; + +#undef UNUSUAL_DEV + +static int +freecom_readdata (struct scsi_cmnd *srb, struct us_data *us, + unsigned int ipipe, unsigned int opipe, int count) +{ + struct freecom_xfer_wrap *fxfr = + (struct freecom_xfer_wrap *) us->iobuf; + int result; + + fxfr->Type = FCM_PACKET_INPUT | 0x00; + fxfr->Timeout = 0; /* Short timeout for debugging. */ + fxfr->Count = cpu_to_le32 (count); + memset (fxfr->Pad, 0, sizeof (fxfr->Pad)); + + usb_stor_dbg(us, "Read data Freecom! (c=%d)\n", count); + + /* Issue the transfer command. */ + result = usb_stor_bulk_transfer_buf (us, opipe, fxfr, + FCM_PACKET_LENGTH, NULL); + if (result != USB_STOR_XFER_GOOD) { + usb_stor_dbg(us, "Freecom readdata transport error\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + /* Now transfer all of our blocks. */ + usb_stor_dbg(us, "Start of read\n"); + result = usb_stor_bulk_srb(us, ipipe, srb); + usb_stor_dbg(us, "freecom_readdata done!\n"); + + if (result > USB_STOR_XFER_SHORT) + return USB_STOR_TRANSPORT_ERROR; + return USB_STOR_TRANSPORT_GOOD; +} + +static int +freecom_writedata (struct scsi_cmnd *srb, struct us_data *us, + int unsigned ipipe, unsigned int opipe, int count) +{ + struct freecom_xfer_wrap *fxfr = + (struct freecom_xfer_wrap *) us->iobuf; + int result; + + fxfr->Type = FCM_PACKET_OUTPUT | 0x00; + fxfr->Timeout = 0; /* Short timeout for debugging. */ + fxfr->Count = cpu_to_le32 (count); + memset (fxfr->Pad, 0, sizeof (fxfr->Pad)); + + usb_stor_dbg(us, "Write data Freecom! (c=%d)\n", count); + + /* Issue the transfer command. */ + result = usb_stor_bulk_transfer_buf (us, opipe, fxfr, + FCM_PACKET_LENGTH, NULL); + if (result != USB_STOR_XFER_GOOD) { + usb_stor_dbg(us, "Freecom writedata transport error\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + /* Now transfer all of our blocks. */ + usb_stor_dbg(us, "Start of write\n"); + result = usb_stor_bulk_srb(us, opipe, srb); + + usb_stor_dbg(us, "freecom_writedata done!\n"); + if (result > USB_STOR_XFER_SHORT) + return USB_STOR_TRANSPORT_ERROR; + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Transport for the Freecom USB/IDE adaptor. + * + */ +static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us) +{ + struct freecom_cb_wrap *fcb; + struct freecom_status *fst; + unsigned int ipipe, opipe; /* We need both pipes. */ + int result; + unsigned int partial; + int length; + + fcb = (struct freecom_cb_wrap *) us->iobuf; + fst = (struct freecom_status *) us->iobuf; + + usb_stor_dbg(us, "Freecom TRANSPORT STARTED\n"); + + /* Get handles for both transports. */ + opipe = us->send_bulk_pipe; + ipipe = us->recv_bulk_pipe; + + /* The ATAPI Command always goes out first. */ + fcb->Type = FCM_PACKET_ATAPI | 0x00; + fcb->Timeout = 0; + memcpy (fcb->Atapi, srb->cmnd, 12); + memset (fcb->Filler, 0, sizeof (fcb->Filler)); + + US_DEBUG(pdump(us, srb->cmnd, 12)); + + /* Send it out. */ + result = usb_stor_bulk_transfer_buf (us, opipe, fcb, + FCM_PACKET_LENGTH, NULL); + + /* + * The Freecom device will only fail if there is something wrong in + * USB land. It returns the status in its own registers, which + * come back in the bulk pipe. + */ + if (result != USB_STOR_XFER_GOOD) { + usb_stor_dbg(us, "freecom transport error\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + /* + * There are times we can optimize out this status read, but it + * doesn't hurt us to always do it now. + */ + result = usb_stor_bulk_transfer_buf (us, ipipe, fst, + FCM_STATUS_PACKET_LENGTH, &partial); + usb_stor_dbg(us, "foo Status result %d %u\n", result, partial); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + US_DEBUG(pdump(us, (void *)fst, partial)); + + /* + * The firmware will time-out commands after 20 seconds. Some commands + * can legitimately take longer than this, so we use a different + * command that only waits for the interrupt and then sends status, + * without having to send a new ATAPI command to the device. + * + * NOTE: There is some indication that a data transfer after a timeout + * may not work, but that is a condition that should never happen. + */ + while (fst->Status & FCM_STATUS_BUSY) { + usb_stor_dbg(us, "20 second USB/ATAPI bridge TIMEOUT occurred!\n"); + usb_stor_dbg(us, "fst->Status is %x\n", fst->Status); + + /* Get the status again */ + fcb->Type = FCM_PACKET_STATUS; + fcb->Timeout = 0; + memset (fcb->Atapi, 0, sizeof(fcb->Atapi)); + memset (fcb->Filler, 0, sizeof (fcb->Filler)); + + /* Send it out. */ + result = usb_stor_bulk_transfer_buf (us, opipe, fcb, + FCM_PACKET_LENGTH, NULL); + + /* + * The Freecom device will only fail if there is something + * wrong in USB land. It returns the status in its own + * registers, which come back in the bulk pipe. + */ + if (result != USB_STOR_XFER_GOOD) { + usb_stor_dbg(us, "freecom transport error\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + /* get the data */ + result = usb_stor_bulk_transfer_buf (us, ipipe, fst, + FCM_STATUS_PACKET_LENGTH, &partial); + + usb_stor_dbg(us, "bar Status result %d %u\n", result, partial); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + US_DEBUG(pdump(us, (void *)fst, partial)); + } + + if (partial != 4) + return USB_STOR_TRANSPORT_ERROR; + if ((fst->Status & 1) != 0) { + usb_stor_dbg(us, "operation failed\n"); + return USB_STOR_TRANSPORT_FAILED; + } + + /* + * The device might not have as much data available as we + * requested. If you ask for more than the device has, this reads + * and such will hang. + */ + usb_stor_dbg(us, "Device indicates that it has %d bytes available\n", + le16_to_cpu(fst->Count)); + usb_stor_dbg(us, "SCSI requested %d\n", scsi_bufflen(srb)); + + /* Find the length we desire to read. */ + switch (srb->cmnd[0]) { + case INQUIRY: + case REQUEST_SENSE: /* 16 or 18 bytes? spec says 18, lots of devices only have 16 */ + case MODE_SENSE: + case MODE_SENSE_10: + length = le16_to_cpu(fst->Count); + break; + default: + length = scsi_bufflen(srb); + } + + /* verify that this amount is legal */ + if (length > scsi_bufflen(srb)) { + length = scsi_bufflen(srb); + usb_stor_dbg(us, "Truncating request to match buffer length: %d\n", + length); + } + + /* + * What we do now depends on what direction the data is supposed to + * move in. + */ + + switch (us->srb->sc_data_direction) { + case DMA_FROM_DEVICE: + /* catch bogus "read 0 length" case */ + if (!length) + break; + /* + * Make sure that the status indicates that the device + * wants data as well. + */ + if ((fst->Status & DRQ_STAT) == 0 || (fst->Reason & 3) != 2) { + usb_stor_dbg(us, "SCSI wants data, drive doesn't have any\n"); + return USB_STOR_TRANSPORT_FAILED; + } + result = freecom_readdata (srb, us, ipipe, opipe, length); + if (result != USB_STOR_TRANSPORT_GOOD) + return result; + + usb_stor_dbg(us, "Waiting for status\n"); + result = usb_stor_bulk_transfer_buf (us, ipipe, fst, + FCM_PACKET_LENGTH, &partial); + US_DEBUG(pdump(us, (void *)fst, partial)); + + if (partial != 4 || result > USB_STOR_XFER_SHORT) + return USB_STOR_TRANSPORT_ERROR; + if ((fst->Status & ERR_STAT) != 0) { + usb_stor_dbg(us, "operation failed\n"); + return USB_STOR_TRANSPORT_FAILED; + } + if ((fst->Reason & 3) != 3) { + usb_stor_dbg(us, "Drive seems still hungry\n"); + return USB_STOR_TRANSPORT_FAILED; + } + usb_stor_dbg(us, "Transfer happy\n"); + break; + + case DMA_TO_DEVICE: + /* catch bogus "write 0 length" case */ + if (!length) + break; + /* + * Make sure the status indicates that the device wants to + * send us data. + */ + /* !!IMPLEMENT!! */ + result = freecom_writedata (srb, us, ipipe, opipe, length); + if (result != USB_STOR_TRANSPORT_GOOD) + return result; + + usb_stor_dbg(us, "Waiting for status\n"); + result = usb_stor_bulk_transfer_buf (us, ipipe, fst, + FCM_PACKET_LENGTH, &partial); + + if (partial != 4 || result > USB_STOR_XFER_SHORT) + return USB_STOR_TRANSPORT_ERROR; + if ((fst->Status & ERR_STAT) != 0) { + usb_stor_dbg(us, "operation failed\n"); + return USB_STOR_TRANSPORT_FAILED; + } + if ((fst->Reason & 3) != 3) { + usb_stor_dbg(us, "Drive seems still hungry\n"); + return USB_STOR_TRANSPORT_FAILED; + } + + usb_stor_dbg(us, "Transfer happy\n"); + break; + + + case DMA_NONE: + /* Easy, do nothing. */ + break; + + default: + /* should never hit here -- filtered in usb.c */ + usb_stor_dbg(us, "freecom unimplemented direction: %d\n", + us->srb->sc_data_direction); + /* Return fail, SCSI seems to handle this better. */ + return USB_STOR_TRANSPORT_FAILED; + } + + return USB_STOR_TRANSPORT_GOOD; +} + +static int init_freecom(struct us_data *us) +{ + int result; + char *buffer = us->iobuf; + + /* + * The DMA-mapped I/O buffer is 64 bytes long, just right for + * all our packets. No need to allocate any extra buffer space. + */ + + result = usb_stor_control_msg(us, us->recv_ctrl_pipe, + 0x4c, 0xc0, 0x4346, 0x0, buffer, 0x20, 3*HZ); + buffer[32] = '\0'; + usb_stor_dbg(us, "String returned from FC init is: %s\n", buffer); + + /* + * Special thanks to the people at Freecom for providing me with + * this "magic sequence", which they use in their Windows and MacOS + * drivers to make sure that all the attached perhiperals are + * properly reset. + */ + + /* send reset */ + result = usb_stor_control_msg(us, us->send_ctrl_pipe, + 0x4d, 0x40, 0x24d8, 0x0, NULL, 0x0, 3*HZ); + usb_stor_dbg(us, "result from activate reset is %d\n", result); + + /* wait 250ms */ + msleep(250); + + /* clear reset */ + result = usb_stor_control_msg(us, us->send_ctrl_pipe, + 0x4d, 0x40, 0x24f8, 0x0, NULL, 0x0, 3*HZ); + usb_stor_dbg(us, "result from clear reset is %d\n", result); + + /* wait 3 seconds */ + msleep(3 * 1000); + + return USB_STOR_TRANSPORT_GOOD; +} + +static int usb_stor_freecom_reset(struct us_data *us) +{ + printk (KERN_CRIT "freecom reset called\n"); + + /* We don't really have this feature. */ + return FAILED; +} + +#ifdef CONFIG_USB_STORAGE_DEBUG +static void pdump(struct us_data *us, void *ibuffer, int length) +{ + static char line[80]; + int offset = 0; + unsigned char *buffer = (unsigned char *) ibuffer; + int i, j; + int from, base; + + offset = 0; + for (i = 0; i < length; i++) { + if ((i & 15) == 0) { + if (i > 0) { + offset += sprintf (line+offset, " - "); + for (j = i - 16; j < i; j++) { + if (buffer[j] >= 32 && buffer[j] <= 126) + line[offset++] = buffer[j]; + else + line[offset++] = '.'; + } + line[offset] = 0; + usb_stor_dbg(us, "%s\n", line); + offset = 0; + } + offset += sprintf (line+offset, "%08x:", i); + } else if ((i & 7) == 0) { + offset += sprintf (line+offset, " -"); + } + offset += sprintf (line+offset, " %02x", buffer[i] & 0xff); + } + + /* Add the last "chunk" of data. */ + from = (length - 1) % 16; + base = ((length - 1) / 16) * 16; + + for (i = from + 1; i < 16; i++) + offset += sprintf (line+offset, " "); + if (from < 8) + offset += sprintf (line+offset, " "); + offset += sprintf (line+offset, " - "); + + for (i = 0; i <= from; i++) { + if (buffer[base+i] >= 32 && buffer[base+i] <= 126) + line[offset++] = buffer[base+i]; + else + line[offset++] = '.'; + } + line[offset] = 0; + usb_stor_dbg(us, "%s\n", line); +} +#endif + +static struct scsi_host_template freecom_host_template; + +static int freecom_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct us_data *us; + int result; + + result = usb_stor_probe1(&us, intf, id, + (id - freecom_usb_ids) + freecom_unusual_dev_list, + &freecom_host_template); + if (result) + return result; + + us->transport_name = "Freecom"; + us->transport = freecom_transport; + us->transport_reset = usb_stor_freecom_reset; + us->max_lun = 0; + + result = usb_stor_probe2(us); + return result; +} + +static struct usb_driver freecom_driver = { + .name = DRV_NAME, + .probe = freecom_probe, + .disconnect = usb_stor_disconnect, + .suspend = usb_stor_suspend, + .resume = usb_stor_resume, + .reset_resume = usb_stor_reset_resume, + .pre_reset = usb_stor_pre_reset, + .post_reset = usb_stor_post_reset, + .id_table = freecom_usb_ids, + .soft_unbind = 1, + .no_dynamic_id = 1, +}; + +module_usb_stor_driver(freecom_driver, freecom_host_template, DRV_NAME); diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c new file mode 100644 index 000000000..b243bd552 --- /dev/null +++ b/drivers/usb/storage/initializers.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Special Initializers for certain USB Mass Storage devices + * + * Current development and maintenance by: + * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + */ + +#include + +#include "usb.h" +#include "initializers.h" +#include "debug.h" +#include "transport.h" + +/* + * This places the Shuttle/SCM USB<->SCSI bridge devices in multi-target + * mode + */ +int usb_stor_euscsi_init(struct us_data *us) +{ + int result; + + usb_stor_dbg(us, "Attempting to init eUSCSI bridge...\n"); + result = usb_stor_control_msg(us, us->send_ctrl_pipe, + 0x0C, USB_RECIP_INTERFACE | USB_TYPE_VENDOR, + 0x01, 0x0, NULL, 0x0, 5 * HZ); + usb_stor_dbg(us, "-- result is %d\n", result); + + return 0; +} + +/* + * This function is required to activate all four slots on the UCR-61S2B + * flash reader + */ +int usb_stor_ucr61s2b_init(struct us_data *us) +{ + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap*) us->iobuf; + struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap*) us->iobuf; + int res; + unsigned int partial; + static const char init_string[] = "\xec\x0a\x06\x00$PCCHIPS"; + + usb_stor_dbg(us, "Sending UCR-61S2B initialization packet...\n"); + + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->Tag = 0; + bcb->DataTransferLength = cpu_to_le32(0); + bcb->Flags = bcb->Lun = 0; + bcb->Length = sizeof(init_string) - 1; + memset(bcb->CDB, 0, sizeof(bcb->CDB)); + memcpy(bcb->CDB, init_string, sizeof(init_string) - 1); + + res = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, bcb, + US_BULK_CB_WRAP_LEN, &partial); + if (res) + return -EIO; + + usb_stor_dbg(us, "Getting status packet...\n"); + res = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, bcs, + US_BULK_CS_WRAP_LEN, &partial); + if (res) + return -EIO; + + return 0; +} + +/* This places the HUAWEI E220 devices in multi-port mode */ +int usb_stor_huawei_e220_init(struct us_data *us) +{ + int result; + + result = usb_stor_control_msg(us, us->send_ctrl_pipe, + USB_REQ_SET_FEATURE, + USB_TYPE_STANDARD | USB_RECIP_DEVICE, + 0x01, 0x0, NULL, 0x0, 1 * HZ); + usb_stor_dbg(us, "Huawei mode set result is %d\n", result); + return 0; +} diff --git a/drivers/usb/storage/initializers.h b/drivers/usb/storage/initializers.h new file mode 100644 index 000000000..dcd7b7e5e --- /dev/null +++ b/drivers/usb/storage/initializers.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Header file for Special Initializers for certain USB Mass Storage devices + * + * Current development and maintenance by: + * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + */ + +#include "usb.h" +#include "transport.h" + +/* + * This places the Shuttle/SCM USB<->SCSI bridge devices in multi-target + * mode + */ +int usb_stor_euscsi_init(struct us_data *us); + +/* + * This function is required to activate all four slots on the UCR-61S2B + * flash reader + */ +int usb_stor_ucr61s2b_init(struct us_data *us); + +/* This places the HUAWEI E220 devices in multi-port mode */ +int usb_stor_huawei_e220_init(struct us_data *us); diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c new file mode 100644 index 000000000..a4fd5dda3 --- /dev/null +++ b/drivers/usb/storage/isd200.c @@ -0,0 +1,1586 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Transport & Protocol Driver for In-System Design, Inc. ISD200 ASIC + * + * Current development and maintenance: + * (C) 2001-2002 Björn Stenberg (bjorn@haxx.se) + * + * Developed with the assistance of: + * (C) 2002 Alan Stern + * + * Initial work: + * (C) 2000 In-System Design, Inc. (support@in-system.com) + * + * The ISD200 ASIC does not natively support ATA devices. The chip + * does implement an interface, the ATA Command Block (ATACB) which provides + * a means of passing ATA commands and ATA register accesses to a device. + * + * History: + * + * 2002-10-19: Removed the specialized transfer routines. + * (Alan Stern ) + * 2001-02-24: Removed lots of duplicate code and simplified the structure. + * (bjorn@haxx.se) + * 2002-01-16: Fixed endianness bug so it works on the ppc arch. + * (Luc Saillard ) + * 2002-01-17: All bitfields removed. + * (bjorn@haxx.se) + */ + + +/* Include files */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "usb.h" +#include "transport.h" +#include "protocol.h" +#include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-isd200" + +MODULE_DESCRIPTION("Driver for In-System Design, Inc. ISD200 ASIC"); +MODULE_AUTHOR("Björn Stenberg "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("USB_STORAGE"); + +static int isd200_Initialization(struct us_data *us); + + +/* + * The table of devices + */ +#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ + vendorName, productName, useProtocol, useTransport, \ + initFunction, flags) \ +{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ + .driver_info = (flags) } + +static const struct usb_device_id isd200_usb_ids[] = { +# include "unusual_isd200.h" + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, isd200_usb_ids); + +#undef UNUSUAL_DEV + +/* + * The flags table + */ +#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ + vendor_name, product_name, use_protocol, use_transport, \ + init_function, Flags) \ +{ \ + .vendorName = vendor_name, \ + .productName = product_name, \ + .useProtocol = use_protocol, \ + .useTransport = use_transport, \ + .initFunction = init_function, \ +} + +static const struct us_unusual_dev isd200_unusual_dev_list[] = { +# include "unusual_isd200.h" + { } /* Terminating entry */ +}; + +#undef UNUSUAL_DEV + +/* Timeout defines (in Seconds) */ + +#define ISD200_ENUM_BSY_TIMEOUT 35 +#define ISD200_ENUM_DETECT_TIMEOUT 30 +#define ISD200_DEFAULT_TIMEOUT 30 + +/* device flags */ +#define DF_ATA_DEVICE 0x0001 +#define DF_MEDIA_STATUS_ENABLED 0x0002 +#define DF_REMOVABLE_MEDIA 0x0004 + +/* capability bit definitions */ +#define CAPABILITY_DMA 0x01 +#define CAPABILITY_LBA 0x02 + +/* command_setX bit definitions */ +#define COMMANDSET_REMOVABLE 0x02 +#define COMMANDSET_MEDIA_STATUS 0x10 + +/* ATA Vendor Specific defines */ +#define ATA_ADDRESS_DEVHEAD_STD 0xa0 +#define ATA_ADDRESS_DEVHEAD_LBA_MODE 0x40 +#define ATA_ADDRESS_DEVHEAD_SLAVE 0x10 + +/* Action Select bits */ +#define ACTION_SELECT_0 0x01 +#define ACTION_SELECT_1 0x02 +#define ACTION_SELECT_2 0x04 +#define ACTION_SELECT_3 0x08 +#define ACTION_SELECT_4 0x10 +#define ACTION_SELECT_5 0x20 +#define ACTION_SELECT_6 0x40 +#define ACTION_SELECT_7 0x80 + +/* Register Select bits */ +#define REG_ALTERNATE_STATUS 0x01 +#define REG_DEVICE_CONTROL 0x01 +#define REG_ERROR 0x02 +#define REG_FEATURES 0x02 +#define REG_SECTOR_COUNT 0x04 +#define REG_SECTOR_NUMBER 0x08 +#define REG_CYLINDER_LOW 0x10 +#define REG_CYLINDER_HIGH 0x20 +#define REG_DEVICE_HEAD 0x40 +#define REG_STATUS 0x80 +#define REG_COMMAND 0x80 + +/* ATA registers offset definitions */ +#define ATA_REG_ERROR_OFFSET 1 +#define ATA_REG_LCYL_OFFSET 4 +#define ATA_REG_HCYL_OFFSET 5 +#define ATA_REG_STATUS_OFFSET 7 + +/* ATA error definitions not in */ +#define ATA_ERROR_MEDIA_CHANGE 0x20 + +/* ATA command definitions not in */ +#define ATA_COMMAND_GET_MEDIA_STATUS 0xDA +#define ATA_COMMAND_MEDIA_EJECT 0xED + +/* ATA drive control definitions */ +#define ATA_DC_DISABLE_INTERRUPTS 0x02 +#define ATA_DC_RESET_CONTROLLER 0x04 +#define ATA_DC_REENABLE_CONTROLLER 0x00 + +/* + * General purpose return codes + */ + +#define ISD200_ERROR -1 +#define ISD200_GOOD 0 + +/* + * Transport return codes + */ + +#define ISD200_TRANSPORT_GOOD 0 /* Transport good, command good */ +#define ISD200_TRANSPORT_FAILED 1 /* Transport good, command failed */ +#define ISD200_TRANSPORT_ERROR 2 /* Transport bad (i.e. device dead) */ + +/* driver action codes */ +#define ACTION_READ_STATUS 0 +#define ACTION_RESET 1 +#define ACTION_REENABLE 2 +#define ACTION_SOFT_RESET 3 +#define ACTION_ENUM 4 +#define ACTION_IDENTIFY 5 + + +/* + * ata_cdb struct + */ + + +union ata_cdb { + struct { + unsigned char SignatureByte0; + unsigned char SignatureByte1; + unsigned char ActionSelect; + unsigned char RegisterSelect; + unsigned char TransferBlockSize; + unsigned char WriteData3F6; + unsigned char WriteData1F1; + unsigned char WriteData1F2; + unsigned char WriteData1F3; + unsigned char WriteData1F4; + unsigned char WriteData1F5; + unsigned char WriteData1F6; + unsigned char WriteData1F7; + unsigned char Reserved[3]; + } generic; + + struct { + unsigned char SignatureByte0; + unsigned char SignatureByte1; + unsigned char ActionSelect; + unsigned char RegisterSelect; + unsigned char TransferBlockSize; + unsigned char AlternateStatusByte; + unsigned char ErrorByte; + unsigned char SectorCountByte; + unsigned char SectorNumberByte; + unsigned char CylinderLowByte; + unsigned char CylinderHighByte; + unsigned char DeviceHeadByte; + unsigned char StatusByte; + unsigned char Reserved[3]; + } read; + + struct { + unsigned char SignatureByte0; + unsigned char SignatureByte1; + unsigned char ActionSelect; + unsigned char RegisterSelect; + unsigned char TransferBlockSize; + unsigned char DeviceControlByte; + unsigned char FeaturesByte; + unsigned char SectorCountByte; + unsigned char SectorNumberByte; + unsigned char CylinderLowByte; + unsigned char CylinderHighByte; + unsigned char DeviceHeadByte; + unsigned char CommandByte; + unsigned char Reserved[3]; + } write; +}; + + +/* + * Inquiry data structure. This is the data returned from the target + * after it receives an inquiry. + * + * This structure may be extended by the number of bytes specified + * in the field AdditionalLength. The defined size constant only + * includes fields through ProductRevisionLevel. + */ + +/* + * DeviceType field + */ +#define DIRECT_ACCESS_DEVICE 0x00 /* disks */ +#define DEVICE_REMOVABLE 0x80 + +struct inquiry_data { + unsigned char DeviceType; + unsigned char DeviceTypeModifier; + unsigned char Versions; + unsigned char Format; + unsigned char AdditionalLength; + unsigned char Reserved[2]; + unsigned char Capability; + unsigned char VendorId[8]; + unsigned char ProductId[16]; + unsigned char ProductRevisionLevel[4]; + unsigned char VendorSpecific[20]; + unsigned char Reserved3[40]; +} __attribute__ ((packed)); + +/* + * INQUIRY data buffer size + */ + +#define INQUIRYDATABUFFERSIZE 36 + + +/* + * ISD200 CONFIG data struct + */ + +#define ATACFG_TIMING 0x0f +#define ATACFG_ATAPI_RESET 0x10 +#define ATACFG_MASTER 0x20 +#define ATACFG_BLOCKSIZE 0xa0 + +#define ATACFGE_LAST_LUN 0x07 +#define ATACFGE_DESC_OVERRIDE 0x08 +#define ATACFGE_STATE_SUSPEND 0x10 +#define ATACFGE_SKIP_BOOT 0x20 +#define ATACFGE_CONF_DESC2 0x40 +#define ATACFGE_INIT_STATUS 0x80 + +#define CFG_CAPABILITY_SRST 0x01 + +struct isd200_config { + unsigned char EventNotification; + unsigned char ExternalClock; + unsigned char ATAInitTimeout; + unsigned char ATAConfig; + unsigned char ATAMajorCommand; + unsigned char ATAMinorCommand; + unsigned char ATAExtraConfig; + unsigned char Capability; +}__attribute__ ((packed)); + + +/* + * ISD200 driver information struct + */ + +struct isd200_info { + struct inquiry_data InquiryData; + u16 *id; + struct isd200_config ConfigData; + unsigned char *RegsBuf; + unsigned char ATARegs[8]; + unsigned char DeviceHead; + unsigned char DeviceFlags; + + /* maximum number of LUNs supported */ + unsigned char MaxLUNs; + unsigned char cmnd[MAX_COMMAND_SIZE]; + struct scsi_cmnd srb; + struct scatterlist sg; +}; + + +/* + * Read Capacity Data - returned in Big Endian format + */ + +struct read_capacity_data { + __be32 LogicalBlockAddress; + __be32 BytesPerBlock; +}; + +/* + * Read Block Limits Data - returned in Big Endian format + * This structure returns the maximum and minimum block + * size for a TAPE device. + */ + +struct read_block_limits { + unsigned char Reserved; + unsigned char BlockMaximumSize[3]; + unsigned char BlockMinimumSize[2]; +}; + + +/* + * Sense Data Format + */ + +#define SENSE_ERRCODE 0x7f +#define SENSE_ERRCODE_VALID 0x80 +#define SENSE_FLAG_SENSE_KEY 0x0f +#define SENSE_FLAG_BAD_LENGTH 0x20 +#define SENSE_FLAG_END_OF_MEDIA 0x40 +#define SENSE_FLAG_FILE_MARK 0x80 +struct sense_data { + unsigned char ErrorCode; + unsigned char SegmentNumber; + unsigned char Flags; + unsigned char Information[4]; + unsigned char AdditionalSenseLength; + unsigned char CommandSpecificInformation[4]; + unsigned char AdditionalSenseCode; + unsigned char AdditionalSenseCodeQualifier; + unsigned char FieldReplaceableUnitCode; + unsigned char SenseKeySpecific[3]; +} __attribute__ ((packed)); + +/* + * Default request sense buffer size + */ + +#define SENSE_BUFFER_SIZE 18 + +/*********************************************************************** + * Helper routines + ***********************************************************************/ + +/************************************************************************** + * isd200_build_sense + * + * Builds an artificial sense buffer to report the results of a + * failed command. + * + * RETURNS: + * void + */ +static void isd200_build_sense(struct us_data *us, struct scsi_cmnd *srb) +{ + struct isd200_info *info = (struct isd200_info *)us->extra; + struct sense_data *buf = (struct sense_data *) &srb->sense_buffer[0]; + unsigned char error = info->ATARegs[ATA_REG_ERROR_OFFSET]; + + if(error & ATA_ERROR_MEDIA_CHANGE) { + buf->ErrorCode = 0x70 | SENSE_ERRCODE_VALID; + buf->AdditionalSenseLength = 0xb; + buf->Flags = UNIT_ATTENTION; + buf->AdditionalSenseCode = 0; + buf->AdditionalSenseCodeQualifier = 0; + } else if (error & ATA_MCR) { + buf->ErrorCode = 0x70 | SENSE_ERRCODE_VALID; + buf->AdditionalSenseLength = 0xb; + buf->Flags = UNIT_ATTENTION; + buf->AdditionalSenseCode = 0; + buf->AdditionalSenseCodeQualifier = 0; + } else if (error & ATA_TRK0NF) { + buf->ErrorCode = 0x70 | SENSE_ERRCODE_VALID; + buf->AdditionalSenseLength = 0xb; + buf->Flags = NOT_READY; + buf->AdditionalSenseCode = 0; + buf->AdditionalSenseCodeQualifier = 0; + } else if (error & ATA_UNC) { + buf->ErrorCode = 0x70 | SENSE_ERRCODE_VALID; + buf->AdditionalSenseLength = 0xb; + buf->Flags = DATA_PROTECT; + buf->AdditionalSenseCode = 0; + buf->AdditionalSenseCodeQualifier = 0; + } else { + buf->ErrorCode = 0; + buf->AdditionalSenseLength = 0; + buf->Flags = 0; + buf->AdditionalSenseCode = 0; + buf->AdditionalSenseCodeQualifier = 0; + } +} + + +/*********************************************************************** + * Transport routines + ***********************************************************************/ + +/************************************************************************** + * isd200_set_srb(), isd200_srb_set_bufflen() + * + * Two helpers to facilitate in initialization of scsi_cmnd structure + * Will need to change when struct scsi_cmnd changes + */ +static void isd200_set_srb(struct isd200_info *info, + enum dma_data_direction dir, void* buff, unsigned bufflen) +{ + struct scsi_cmnd *srb = &info->srb; + + if (buff) + sg_init_one(&info->sg, buff, bufflen); + + srb->sc_data_direction = dir; + srb->sdb.table.sgl = buff ? &info->sg : NULL; + srb->sdb.length = bufflen; + srb->sdb.table.nents = buff ? 1 : 0; +} + +static void isd200_srb_set_bufflen(struct scsi_cmnd *srb, unsigned bufflen) +{ + srb->sdb.length = bufflen; +} + + +/************************************************************************** + * isd200_action + * + * Routine for sending commands to the isd200 + * + * RETURNS: + * ISD status code + */ +static int isd200_action( struct us_data *us, int action, + void* pointer, int value ) +{ + union ata_cdb ata; + /* static to prevent this large struct being placed on the valuable stack */ + static struct scsi_device srb_dev; + struct isd200_info *info = (struct isd200_info *)us->extra; + struct scsi_cmnd *srb = &info->srb; + int status; + + memset(&ata, 0, sizeof(ata)); + memcpy(srb->cmnd, info->cmnd, MAX_COMMAND_SIZE); + srb->device = &srb_dev; + + ata.generic.SignatureByte0 = info->ConfigData.ATAMajorCommand; + ata.generic.SignatureByte1 = info->ConfigData.ATAMinorCommand; + ata.generic.TransferBlockSize = 1; + + switch ( action ) { + case ACTION_READ_STATUS: + usb_stor_dbg(us, " isd200_action(READ_STATUS)\n"); + ata.generic.ActionSelect = ACTION_SELECT_0|ACTION_SELECT_2; + ata.generic.RegisterSelect = + REG_CYLINDER_LOW | REG_CYLINDER_HIGH | + REG_STATUS | REG_ERROR; + isd200_set_srb(info, DMA_FROM_DEVICE, pointer, value); + break; + + case ACTION_ENUM: + usb_stor_dbg(us, " isd200_action(ENUM,0x%02x)\n", value); + ata.generic.ActionSelect = ACTION_SELECT_1|ACTION_SELECT_2| + ACTION_SELECT_3|ACTION_SELECT_4| + ACTION_SELECT_5; + ata.generic.RegisterSelect = REG_DEVICE_HEAD; + ata.write.DeviceHeadByte = value; + isd200_set_srb(info, DMA_NONE, NULL, 0); + break; + + case ACTION_RESET: + usb_stor_dbg(us, " isd200_action(RESET)\n"); + ata.generic.ActionSelect = ACTION_SELECT_1|ACTION_SELECT_2| + ACTION_SELECT_3|ACTION_SELECT_4; + ata.generic.RegisterSelect = REG_DEVICE_CONTROL; + ata.write.DeviceControlByte = ATA_DC_RESET_CONTROLLER; + isd200_set_srb(info, DMA_NONE, NULL, 0); + break; + + case ACTION_REENABLE: + usb_stor_dbg(us, " isd200_action(REENABLE)\n"); + ata.generic.ActionSelect = ACTION_SELECT_1|ACTION_SELECT_2| + ACTION_SELECT_3|ACTION_SELECT_4; + ata.generic.RegisterSelect = REG_DEVICE_CONTROL; + ata.write.DeviceControlByte = ATA_DC_REENABLE_CONTROLLER; + isd200_set_srb(info, DMA_NONE, NULL, 0); + break; + + case ACTION_SOFT_RESET: + usb_stor_dbg(us, " isd200_action(SOFT_RESET)\n"); + ata.generic.ActionSelect = ACTION_SELECT_1|ACTION_SELECT_5; + ata.generic.RegisterSelect = REG_DEVICE_HEAD | REG_COMMAND; + ata.write.DeviceHeadByte = info->DeviceHead; + ata.write.CommandByte = ATA_CMD_DEV_RESET; + isd200_set_srb(info, DMA_NONE, NULL, 0); + break; + + case ACTION_IDENTIFY: + usb_stor_dbg(us, " isd200_action(IDENTIFY)\n"); + ata.generic.RegisterSelect = REG_COMMAND; + ata.write.CommandByte = ATA_CMD_ID_ATA; + isd200_set_srb(info, DMA_FROM_DEVICE, info->id, + ATA_ID_WORDS * 2); + break; + + default: + usb_stor_dbg(us, "Error: Undefined action %d\n", action); + return ISD200_ERROR; + } + + memcpy(srb->cmnd, &ata, sizeof(ata.generic)); + srb->cmd_len = sizeof(ata.generic); + status = usb_stor_Bulk_transport(srb, us); + if (status == USB_STOR_TRANSPORT_GOOD) + status = ISD200_GOOD; + else { + usb_stor_dbg(us, " isd200_action(0x%02x) error: %d\n", + action, status); + status = ISD200_ERROR; + /* need to reset device here */ + } + + return status; +} + +/************************************************************************** + * isd200_read_regs + * + * Read ATA Registers + * + * RETURNS: + * ISD status code + */ +static int isd200_read_regs( struct us_data *us ) +{ + struct isd200_info *info = (struct isd200_info *)us->extra; + int retStatus = ISD200_GOOD; + int transferStatus; + + usb_stor_dbg(us, "Entering isd200_IssueATAReadRegs\n"); + + transferStatus = isd200_action( us, ACTION_READ_STATUS, + info->RegsBuf, sizeof(info->ATARegs) ); + if (transferStatus != ISD200_TRANSPORT_GOOD) { + usb_stor_dbg(us, " Error reading ATA registers\n"); + retStatus = ISD200_ERROR; + } else { + memcpy(info->ATARegs, info->RegsBuf, sizeof(info->ATARegs)); + usb_stor_dbg(us, " Got ATA Register[ATA_REG_ERROR_OFFSET] = 0x%x\n", + info->ATARegs[ATA_REG_ERROR_OFFSET]); + } + + return retStatus; +} + + +/************************************************************************** + * Invoke the transport and basic error-handling/recovery methods + * + * This is used by the protocol layers to actually send the message to + * the device and receive the response. + */ +static void isd200_invoke_transport( struct us_data *us, + struct scsi_cmnd *srb, + union ata_cdb *ataCdb ) +{ + int need_auto_sense = 0; + int transferStatus; + int result; + + /* send the command to the transport layer */ + memcpy(srb->cmnd, ataCdb, sizeof(ataCdb->generic)); + srb->cmd_len = sizeof(ataCdb->generic); + transferStatus = usb_stor_Bulk_transport(srb, us); + + /* + * if the command gets aborted by the higher layers, we need to + * short-circuit all other processing + */ + if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { + usb_stor_dbg(us, "-- command was aborted\n"); + goto Handle_Abort; + } + + switch (transferStatus) { + + case USB_STOR_TRANSPORT_GOOD: + /* Indicate a good result */ + srb->result = SAM_STAT_GOOD; + break; + + case USB_STOR_TRANSPORT_NO_SENSE: + usb_stor_dbg(us, "-- transport indicates protocol failure\n"); + srb->result = SAM_STAT_CHECK_CONDITION; + return; + + case USB_STOR_TRANSPORT_FAILED: + usb_stor_dbg(us, "-- transport indicates command failure\n"); + need_auto_sense = 1; + break; + + case USB_STOR_TRANSPORT_ERROR: + usb_stor_dbg(us, "-- transport indicates transport error\n"); + srb->result = DID_ERROR << 16; + /* Need reset here */ + return; + + default: + usb_stor_dbg(us, "-- transport indicates unknown error\n"); + srb->result = DID_ERROR << 16; + /* Need reset here */ + return; + } + + if ((scsi_get_resid(srb) > 0) && + !((srb->cmnd[0] == REQUEST_SENSE) || + (srb->cmnd[0] == INQUIRY) || + (srb->cmnd[0] == MODE_SENSE) || + (srb->cmnd[0] == LOG_SENSE) || + (srb->cmnd[0] == MODE_SENSE_10))) { + usb_stor_dbg(us, "-- unexpectedly short transfer\n"); + need_auto_sense = 1; + } + + if (need_auto_sense) { + result = isd200_read_regs(us); + if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { + usb_stor_dbg(us, "-- auto-sense aborted\n"); + goto Handle_Abort; + } + if (result == ISD200_GOOD) { + isd200_build_sense(us, srb); + srb->result = SAM_STAT_CHECK_CONDITION; + + /* If things are really okay, then let's show that */ + if ((srb->sense_buffer[2] & 0xf) == 0x0) + srb->result = SAM_STAT_GOOD; + } else { + srb->result = DID_ERROR << 16; + /* Need reset here */ + } + } + + /* + * Regardless of auto-sense, if we _know_ we have an error + * condition, show that in the result code + */ + if (transferStatus == USB_STOR_TRANSPORT_FAILED) + srb->result = SAM_STAT_CHECK_CONDITION; + return; + + /* + * abort processing: the bulk-only transport requires a reset + * following an abort + */ + Handle_Abort: + srb->result = DID_ABORT << 16; + + /* permit the reset transfer to take place */ + clear_bit(US_FLIDX_ABORTING, &us->dflags); + /* Need reset here */ +} + +#ifdef CONFIG_USB_STORAGE_DEBUG +static void isd200_log_config(struct us_data *us, struct isd200_info *info) +{ + usb_stor_dbg(us, " Event Notification: 0x%x\n", + info->ConfigData.EventNotification); + usb_stor_dbg(us, " External Clock: 0x%x\n", + info->ConfigData.ExternalClock); + usb_stor_dbg(us, " ATA Init Timeout: 0x%x\n", + info->ConfigData.ATAInitTimeout); + usb_stor_dbg(us, " ATAPI Command Block Size: 0x%x\n", + (info->ConfigData.ATAConfig & ATACFG_BLOCKSIZE) >> 6); + usb_stor_dbg(us, " Master/Slave Selection: 0x%x\n", + info->ConfigData.ATAConfig & ATACFG_MASTER); + usb_stor_dbg(us, " ATAPI Reset: 0x%x\n", + info->ConfigData.ATAConfig & ATACFG_ATAPI_RESET); + usb_stor_dbg(us, " ATA Timing: 0x%x\n", + info->ConfigData.ATAConfig & ATACFG_TIMING); + usb_stor_dbg(us, " ATA Major Command: 0x%x\n", + info->ConfigData.ATAMajorCommand); + usb_stor_dbg(us, " ATA Minor Command: 0x%x\n", + info->ConfigData.ATAMinorCommand); + usb_stor_dbg(us, " Init Status: 0x%x\n", + info->ConfigData.ATAExtraConfig & ATACFGE_INIT_STATUS); + usb_stor_dbg(us, " Config Descriptor 2: 0x%x\n", + info->ConfigData.ATAExtraConfig & ATACFGE_CONF_DESC2); + usb_stor_dbg(us, " Skip Device Boot: 0x%x\n", + info->ConfigData.ATAExtraConfig & ATACFGE_SKIP_BOOT); + usb_stor_dbg(us, " ATA 3 State Suspend: 0x%x\n", + info->ConfigData.ATAExtraConfig & ATACFGE_STATE_SUSPEND); + usb_stor_dbg(us, " Descriptor Override: 0x%x\n", + info->ConfigData.ATAExtraConfig & ATACFGE_DESC_OVERRIDE); + usb_stor_dbg(us, " Last LUN Identifier: 0x%x\n", + info->ConfigData.ATAExtraConfig & ATACFGE_LAST_LUN); + usb_stor_dbg(us, " SRST Enable: 0x%x\n", + info->ConfigData.ATAExtraConfig & CFG_CAPABILITY_SRST); +} +#endif + +/************************************************************************** + * isd200_write_config + * + * Write the ISD200 Configuration data + * + * RETURNS: + * ISD status code + */ +static int isd200_write_config( struct us_data *us ) +{ + struct isd200_info *info = (struct isd200_info *)us->extra; + int retStatus = ISD200_GOOD; + int result; + +#ifdef CONFIG_USB_STORAGE_DEBUG + usb_stor_dbg(us, "Entering isd200_write_config\n"); + usb_stor_dbg(us, " Writing the following ISD200 Config Data:\n"); + isd200_log_config(us, info); +#endif + + /* let's send the command via the control pipe */ + result = usb_stor_ctrl_transfer( + us, + us->send_ctrl_pipe, + 0x01, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + 0x0000, + 0x0002, + (void *) &info->ConfigData, + sizeof(info->ConfigData)); + + if (result >= 0) { + usb_stor_dbg(us, " ISD200 Config Data was written successfully\n"); + } else { + usb_stor_dbg(us, " Request to write ISD200 Config Data failed!\n"); + retStatus = ISD200_ERROR; + } + + usb_stor_dbg(us, "Leaving isd200_write_config %08X\n", retStatus); + return retStatus; +} + + +/************************************************************************** + * isd200_read_config + * + * Reads the ISD200 Configuration data + * + * RETURNS: + * ISD status code + */ +static int isd200_read_config( struct us_data *us ) +{ + struct isd200_info *info = (struct isd200_info *)us->extra; + int retStatus = ISD200_GOOD; + int result; + + usb_stor_dbg(us, "Entering isd200_read_config\n"); + + /* read the configuration information from ISD200. Use this to */ + /* determine what the special ATA CDB bytes are. */ + + result = usb_stor_ctrl_transfer( + us, + us->recv_ctrl_pipe, + 0x02, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x0000, + 0x0002, + (void *) &info->ConfigData, + sizeof(info->ConfigData)); + + + if (result >= 0) { + usb_stor_dbg(us, " Retrieved the following ISD200 Config Data:\n"); +#ifdef CONFIG_USB_STORAGE_DEBUG + isd200_log_config(us, info); +#endif + } else { + usb_stor_dbg(us, " Request to get ISD200 Config Data failed!\n"); + retStatus = ISD200_ERROR; + } + + usb_stor_dbg(us, "Leaving isd200_read_config %08X\n", retStatus); + return retStatus; +} + + +/************************************************************************** + * isd200_atapi_soft_reset + * + * Perform an Atapi Soft Reset on the device + * + * RETURNS: + * NT status code + */ +static int isd200_atapi_soft_reset( struct us_data *us ) +{ + int retStatus = ISD200_GOOD; + int transferStatus; + + usb_stor_dbg(us, "Entering isd200_atapi_soft_reset\n"); + + transferStatus = isd200_action( us, ACTION_SOFT_RESET, NULL, 0 ); + if (transferStatus != ISD200_TRANSPORT_GOOD) { + usb_stor_dbg(us, " Error issuing Atapi Soft Reset\n"); + retStatus = ISD200_ERROR; + } + + usb_stor_dbg(us, "Leaving isd200_atapi_soft_reset %08X\n", retStatus); + return retStatus; +} + + +/************************************************************************** + * isd200_srst + * + * Perform an SRST on the device + * + * RETURNS: + * ISD status code + */ +static int isd200_srst( struct us_data *us ) +{ + int retStatus = ISD200_GOOD; + int transferStatus; + + usb_stor_dbg(us, "Entering isd200_SRST\n"); + + transferStatus = isd200_action( us, ACTION_RESET, NULL, 0 ); + + /* check to see if this request failed */ + if (transferStatus != ISD200_TRANSPORT_GOOD) { + usb_stor_dbg(us, " Error issuing SRST\n"); + retStatus = ISD200_ERROR; + } else { + /* delay 10ms to give the drive a chance to see it */ + msleep(10); + + transferStatus = isd200_action( us, ACTION_REENABLE, NULL, 0 ); + if (transferStatus != ISD200_TRANSPORT_GOOD) { + usb_stor_dbg(us, " Error taking drive out of reset\n"); + retStatus = ISD200_ERROR; + } else { + /* delay 50ms to give the drive a chance to recover after SRST */ + msleep(50); + } + } + + usb_stor_dbg(us, "Leaving isd200_srst %08X\n", retStatus); + return retStatus; +} + + +/************************************************************************** + * isd200_try_enum + * + * Helper function for isd200_manual_enum(). Does ENUM and READ_STATUS + * and tries to analyze the status registers + * + * RETURNS: + * ISD status code + */ +static int isd200_try_enum(struct us_data *us, unsigned char master_slave, + int detect ) +{ + int status = ISD200_GOOD; + unsigned long endTime; + struct isd200_info *info = (struct isd200_info *)us->extra; + unsigned char *regs = info->RegsBuf; + int recheckAsMaster = 0; + + if ( detect ) + endTime = jiffies + ISD200_ENUM_DETECT_TIMEOUT * HZ; + else + endTime = jiffies + ISD200_ENUM_BSY_TIMEOUT * HZ; + + /* loop until we detect !BSY or timeout */ + while(1) { + + status = isd200_action( us, ACTION_ENUM, NULL, master_slave ); + if ( status != ISD200_GOOD ) + break; + + status = isd200_action( us, ACTION_READ_STATUS, + regs, 8 ); + if ( status != ISD200_GOOD ) + break; + + if (!detect) { + if (regs[ATA_REG_STATUS_OFFSET] & ATA_BUSY) { + usb_stor_dbg(us, " %s status is still BSY, try again...\n", + master_slave == ATA_ADDRESS_DEVHEAD_STD ? + "Master" : "Slave"); + } else { + usb_stor_dbg(us, " %s status !BSY, continue with next operation\n", + master_slave == ATA_ADDRESS_DEVHEAD_STD ? + "Master" : "Slave"); + break; + } + } + /* check for ATA_BUSY and */ + /* ATA_DF (workaround ATA Zip drive) and */ + /* ATA_ERR (workaround for Archos CD-ROM) */ + else if (regs[ATA_REG_STATUS_OFFSET] & + (ATA_BUSY | ATA_DF | ATA_ERR)) { + usb_stor_dbg(us, " Status indicates it is not ready, try again...\n"); + } + /* check for DRDY, ATA devices set DRDY after SRST */ + else if (regs[ATA_REG_STATUS_OFFSET] & ATA_DRDY) { + usb_stor_dbg(us, " Identified ATA device\n"); + info->DeviceFlags |= DF_ATA_DEVICE; + info->DeviceHead = master_slave; + break; + } + /* + * check Cylinder High/Low to + * determine if it is an ATAPI device + */ + else if (regs[ATA_REG_HCYL_OFFSET] == 0xEB && + regs[ATA_REG_LCYL_OFFSET] == 0x14) { + /* + * It seems that the RICOH + * MP6200A CD/RW drive will + * report itself okay as a + * slave when it is really a + * master. So this check again + * as a master device just to + * make sure it doesn't report + * itself okay as a master also + */ + if ((master_slave & ATA_ADDRESS_DEVHEAD_SLAVE) && + !recheckAsMaster) { + usb_stor_dbg(us, " Identified ATAPI device as slave. Rechecking again as master\n"); + recheckAsMaster = 1; + master_slave = ATA_ADDRESS_DEVHEAD_STD; + } else { + usb_stor_dbg(us, " Identified ATAPI device\n"); + info->DeviceHead = master_slave; + + status = isd200_atapi_soft_reset(us); + break; + } + } else { + usb_stor_dbg(us, " Not ATA, not ATAPI - Weird\n"); + break; + } + + /* check for timeout on this request */ + if (time_after_eq(jiffies, endTime)) { + if (!detect) + usb_stor_dbg(us, " BSY check timeout, just continue with next operation...\n"); + else + usb_stor_dbg(us, " Device detect timeout!\n"); + break; + } + } + + return status; +} + +/************************************************************************** + * isd200_manual_enum + * + * Determines if the drive attached is an ATA or ATAPI and if it is a + * master or slave. + * + * RETURNS: + * ISD status code + */ +static int isd200_manual_enum(struct us_data *us) +{ + struct isd200_info *info = (struct isd200_info *)us->extra; + int retStatus = ISD200_GOOD; + + usb_stor_dbg(us, "Entering isd200_manual_enum\n"); + + retStatus = isd200_read_config(us); + if (retStatus == ISD200_GOOD) { + int isslave; + /* master or slave? */ + retStatus = isd200_try_enum( us, ATA_ADDRESS_DEVHEAD_STD, 0); + if (retStatus == ISD200_GOOD) + retStatus = isd200_try_enum( us, ATA_ADDRESS_DEVHEAD_SLAVE, 0); + + if (retStatus == ISD200_GOOD) { + retStatus = isd200_srst(us); + if (retStatus == ISD200_GOOD) + /* ata or atapi? */ + retStatus = isd200_try_enum( us, ATA_ADDRESS_DEVHEAD_STD, 1); + } + + isslave = (info->DeviceHead & ATA_ADDRESS_DEVHEAD_SLAVE) ? 1 : 0; + if (!(info->ConfigData.ATAConfig & ATACFG_MASTER)) { + usb_stor_dbg(us, " Setting Master/Slave selection to %d\n", + isslave); + info->ConfigData.ATAConfig &= 0x3f; + info->ConfigData.ATAConfig |= (isslave<<6); + retStatus = isd200_write_config(us); + } + } + + usb_stor_dbg(us, "Leaving isd200_manual_enum %08X\n", retStatus); + return(retStatus); +} + +static void isd200_fix_driveid(u16 *id) +{ +#ifndef __LITTLE_ENDIAN +# ifdef __BIG_ENDIAN + int i; + + for (i = 0; i < ATA_ID_WORDS; i++) + id[i] = __le16_to_cpu(id[i]); +# else +# error "Please fix " +# endif +#endif +} + +static void isd200_dump_driveid(struct us_data *us, u16 *id) +{ + usb_stor_dbg(us, " Identify Data Structure:\n"); + usb_stor_dbg(us, " config = 0x%x\n", id[ATA_ID_CONFIG]); + usb_stor_dbg(us, " cyls = 0x%x\n", id[ATA_ID_CYLS]); + usb_stor_dbg(us, " heads = 0x%x\n", id[ATA_ID_HEADS]); + usb_stor_dbg(us, " track_bytes = 0x%x\n", id[4]); + usb_stor_dbg(us, " sector_bytes = 0x%x\n", id[5]); + usb_stor_dbg(us, " sectors = 0x%x\n", id[ATA_ID_SECTORS]); + usb_stor_dbg(us, " serial_no[0] = 0x%x\n", *(char *)&id[ATA_ID_SERNO]); + usb_stor_dbg(us, " buf_type = 0x%x\n", id[20]); + usb_stor_dbg(us, " buf_size = 0x%x\n", id[ATA_ID_BUF_SIZE]); + usb_stor_dbg(us, " ecc_bytes = 0x%x\n", id[22]); + usb_stor_dbg(us, " fw_rev[0] = 0x%x\n", *(char *)&id[ATA_ID_FW_REV]); + usb_stor_dbg(us, " model[0] = 0x%x\n", *(char *)&id[ATA_ID_PROD]); + usb_stor_dbg(us, " max_multsect = 0x%x\n", id[ATA_ID_MAX_MULTSECT] & 0xff); + usb_stor_dbg(us, " dword_io = 0x%x\n", id[ATA_ID_DWORD_IO]); + usb_stor_dbg(us, " capability = 0x%x\n", id[ATA_ID_CAPABILITY] >> 8); + usb_stor_dbg(us, " tPIO = 0x%x\n", id[ATA_ID_OLD_PIO_MODES] >> 8); + usb_stor_dbg(us, " tDMA = 0x%x\n", id[ATA_ID_OLD_DMA_MODES] >> 8); + usb_stor_dbg(us, " field_valid = 0x%x\n", id[ATA_ID_FIELD_VALID]); + usb_stor_dbg(us, " cur_cyls = 0x%x\n", id[ATA_ID_CUR_CYLS]); + usb_stor_dbg(us, " cur_heads = 0x%x\n", id[ATA_ID_CUR_HEADS]); + usb_stor_dbg(us, " cur_sectors = 0x%x\n", id[ATA_ID_CUR_SECTORS]); + usb_stor_dbg(us, " cur_capacity = 0x%x\n", ata_id_u32(id, 57)); + usb_stor_dbg(us, " multsect = 0x%x\n", id[ATA_ID_MULTSECT] & 0xff); + usb_stor_dbg(us, " lba_capacity = 0x%x\n", ata_id_u32(id, ATA_ID_LBA_CAPACITY)); + usb_stor_dbg(us, " command_set_1 = 0x%x\n", id[ATA_ID_COMMAND_SET_1]); + usb_stor_dbg(us, " command_set_2 = 0x%x\n", id[ATA_ID_COMMAND_SET_2]); +} + +/************************************************************************** + * isd200_get_inquiry_data + * + * Get inquiry data + * + * RETURNS: + * ISD status code + */ +static int isd200_get_inquiry_data( struct us_data *us ) +{ + struct isd200_info *info = (struct isd200_info *)us->extra; + int retStatus; + u16 *id = info->id; + + usb_stor_dbg(us, "Entering isd200_get_inquiry_data\n"); + + /* set default to Master */ + info->DeviceHead = ATA_ADDRESS_DEVHEAD_STD; + + /* attempt to manually enumerate this device */ + retStatus = isd200_manual_enum(us); + if (retStatus == ISD200_GOOD) { + int transferStatus; + + /* check for an ATA device */ + if (info->DeviceFlags & DF_ATA_DEVICE) { + /* this must be an ATA device */ + /* perform an ATA Command Identify */ + transferStatus = isd200_action( us, ACTION_IDENTIFY, + id, ATA_ID_WORDS * 2); + if (transferStatus != ISD200_TRANSPORT_GOOD) { + /* Error issuing ATA Command Identify */ + usb_stor_dbg(us, " Error issuing ATA Command Identify\n"); + retStatus = ISD200_ERROR; + } else { + /* ATA Command Identify successful */ + int i; + __be16 *src; + __u16 *dest; + + isd200_fix_driveid(id); + isd200_dump_driveid(us, id); + + /* Prevent division by 0 in isd200_scsi_to_ata() */ + if (id[ATA_ID_HEADS] == 0 || id[ATA_ID_SECTORS] == 0) { + usb_stor_dbg(us, " Invalid ATA Identify data\n"); + retStatus = ISD200_ERROR; + goto Done; + } + + memset(&info->InquiryData, 0, sizeof(info->InquiryData)); + + /* Standard IDE interface only supports disks */ + info->InquiryData.DeviceType = DIRECT_ACCESS_DEVICE; + + /* The length must be at least 36 (5 + 31) */ + info->InquiryData.AdditionalLength = 0x1F; + + if (id[ATA_ID_COMMAND_SET_1] & COMMANDSET_MEDIA_STATUS) { + /* set the removable bit */ + info->InquiryData.DeviceTypeModifier = DEVICE_REMOVABLE; + info->DeviceFlags |= DF_REMOVABLE_MEDIA; + } + + /* Fill in vendor identification fields */ + src = (__be16 *)&id[ATA_ID_PROD]; + dest = (__u16*)info->InquiryData.VendorId; + for (i = 0; i < 4; i++) + dest[i] = be16_to_cpu(src[i]); + + src = (__be16 *)&id[ATA_ID_PROD + 8/2]; + dest = (__u16*)info->InquiryData.ProductId; + for (i=0;i<8;i++) + dest[i] = be16_to_cpu(src[i]); + + src = (__be16 *)&id[ATA_ID_FW_REV]; + dest = (__u16*)info->InquiryData.ProductRevisionLevel; + for (i=0;i<2;i++) + dest[i] = be16_to_cpu(src[i]); + + /* determine if it supports Media Status Notification */ + if (id[ATA_ID_COMMAND_SET_2] & COMMANDSET_MEDIA_STATUS) { + usb_stor_dbg(us, " Device supports Media Status Notification\n"); + + /* + * Indicate that it is enabled, even + * though it is not. + * This allows the lock/unlock of the + * media to work correctly. + */ + info->DeviceFlags |= DF_MEDIA_STATUS_ENABLED; + } + else + info->DeviceFlags &= ~DF_MEDIA_STATUS_ENABLED; + + } + } else { + /* + * this must be an ATAPI device + * use an ATAPI protocol (Transparent SCSI) + */ + us->protocol_name = "Transparent SCSI"; + us->proto_handler = usb_stor_transparent_scsi_command; + + usb_stor_dbg(us, "Protocol changed to: %s\n", + us->protocol_name); + + /* Free driver structure */ + us->extra_destructor(info); + kfree(info); + us->extra = NULL; + us->extra_destructor = NULL; + } + } + + Done: + usb_stor_dbg(us, "Leaving isd200_get_inquiry_data %08X\n", retStatus); + + return(retStatus); +} + +/************************************************************************** + * isd200_scsi_to_ata + * + * Translate SCSI commands to ATA commands. + * + * RETURNS: + * 1 if the command needs to be sent to the transport layer + * 0 otherwise + */ +static int isd200_scsi_to_ata(struct scsi_cmnd *srb, struct us_data *us, + union ata_cdb * ataCdb) +{ + struct isd200_info *info = (struct isd200_info *)us->extra; + u16 *id = info->id; + int sendToTransport = 1; + unsigned char sectnum, head; + unsigned short cylinder; + unsigned long lba; + unsigned long blockCount; + unsigned char senseData[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + memset(ataCdb, 0, sizeof(union ata_cdb)); + + /* SCSI Command */ + switch (srb->cmnd[0]) { + case INQUIRY: + usb_stor_dbg(us, " ATA OUT - INQUIRY\n"); + + /* copy InquiryData */ + usb_stor_set_xfer_buf((unsigned char *) &info->InquiryData, + sizeof(info->InquiryData), srb); + srb->result = SAM_STAT_GOOD; + sendToTransport = 0; + break; + + case MODE_SENSE: + usb_stor_dbg(us, " ATA OUT - SCSIOP_MODE_SENSE\n"); + + /* Initialize the return buffer */ + usb_stor_set_xfer_buf(senseData, sizeof(senseData), srb); + + if (info->DeviceFlags & DF_MEDIA_STATUS_ENABLED) + { + ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand; + ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand; + ataCdb->generic.TransferBlockSize = 1; + ataCdb->generic.RegisterSelect = REG_COMMAND; + ataCdb->write.CommandByte = ATA_COMMAND_GET_MEDIA_STATUS; + isd200_srb_set_bufflen(srb, 0); + } else { + usb_stor_dbg(us, " Media Status not supported, just report okay\n"); + srb->result = SAM_STAT_GOOD; + sendToTransport = 0; + } + break; + + case TEST_UNIT_READY: + usb_stor_dbg(us, " ATA OUT - SCSIOP_TEST_UNIT_READY\n"); + + if (info->DeviceFlags & DF_MEDIA_STATUS_ENABLED) + { + ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand; + ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand; + ataCdb->generic.TransferBlockSize = 1; + ataCdb->generic.RegisterSelect = REG_COMMAND; + ataCdb->write.CommandByte = ATA_COMMAND_GET_MEDIA_STATUS; + isd200_srb_set_bufflen(srb, 0); + } else { + usb_stor_dbg(us, " Media Status not supported, just report okay\n"); + srb->result = SAM_STAT_GOOD; + sendToTransport = 0; + } + break; + + case READ_CAPACITY: + { + unsigned long capacity; + struct read_capacity_data readCapacityData; + + usb_stor_dbg(us, " ATA OUT - SCSIOP_READ_CAPACITY\n"); + + if (ata_id_has_lba(id)) + capacity = ata_id_u32(id, ATA_ID_LBA_CAPACITY) - 1; + else + capacity = (id[ATA_ID_HEADS] * id[ATA_ID_CYLS] * + id[ATA_ID_SECTORS]) - 1; + + readCapacityData.LogicalBlockAddress = cpu_to_be32(capacity); + readCapacityData.BytesPerBlock = cpu_to_be32(0x200); + + usb_stor_set_xfer_buf((unsigned char *) &readCapacityData, + sizeof(readCapacityData), srb); + srb->result = SAM_STAT_GOOD; + sendToTransport = 0; + } + break; + + case READ_10: + usb_stor_dbg(us, " ATA OUT - SCSIOP_READ\n"); + + lba = be32_to_cpu(*(__be32 *)&srb->cmnd[2]); + blockCount = (unsigned long)srb->cmnd[7]<<8 | (unsigned long)srb->cmnd[8]; + + if (ata_id_has_lba(id)) { + sectnum = (unsigned char)(lba); + cylinder = (unsigned short)(lba>>8); + head = ATA_ADDRESS_DEVHEAD_LBA_MODE | (unsigned char)(lba>>24 & 0x0F); + } else { + sectnum = (u8)((lba % id[ATA_ID_SECTORS]) + 1); + cylinder = (u16)(lba / (id[ATA_ID_SECTORS] * + id[ATA_ID_HEADS])); + head = (u8)((lba / id[ATA_ID_SECTORS]) % + id[ATA_ID_HEADS]); + } + ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand; + ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand; + ataCdb->generic.TransferBlockSize = 1; + ataCdb->generic.RegisterSelect = + REG_SECTOR_COUNT | REG_SECTOR_NUMBER | + REG_CYLINDER_LOW | REG_CYLINDER_HIGH | + REG_DEVICE_HEAD | REG_COMMAND; + ataCdb->write.SectorCountByte = (unsigned char)blockCount; + ataCdb->write.SectorNumberByte = sectnum; + ataCdb->write.CylinderHighByte = (unsigned char)(cylinder>>8); + ataCdb->write.CylinderLowByte = (unsigned char)cylinder; + ataCdb->write.DeviceHeadByte = (head | ATA_ADDRESS_DEVHEAD_STD); + ataCdb->write.CommandByte = ATA_CMD_PIO_READ; + break; + + case WRITE_10: + usb_stor_dbg(us, " ATA OUT - SCSIOP_WRITE\n"); + + lba = be32_to_cpu(*(__be32 *)&srb->cmnd[2]); + blockCount = (unsigned long)srb->cmnd[7]<<8 | (unsigned long)srb->cmnd[8]; + + if (ata_id_has_lba(id)) { + sectnum = (unsigned char)(lba); + cylinder = (unsigned short)(lba>>8); + head = ATA_ADDRESS_DEVHEAD_LBA_MODE | (unsigned char)(lba>>24 & 0x0F); + } else { + sectnum = (u8)((lba % id[ATA_ID_SECTORS]) + 1); + cylinder = (u16)(lba / (id[ATA_ID_SECTORS] * + id[ATA_ID_HEADS])); + head = (u8)((lba / id[ATA_ID_SECTORS]) % + id[ATA_ID_HEADS]); + } + ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand; + ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand; + ataCdb->generic.TransferBlockSize = 1; + ataCdb->generic.RegisterSelect = + REG_SECTOR_COUNT | REG_SECTOR_NUMBER | + REG_CYLINDER_LOW | REG_CYLINDER_HIGH | + REG_DEVICE_HEAD | REG_COMMAND; + ataCdb->write.SectorCountByte = (unsigned char)blockCount; + ataCdb->write.SectorNumberByte = sectnum; + ataCdb->write.CylinderHighByte = (unsigned char)(cylinder>>8); + ataCdb->write.CylinderLowByte = (unsigned char)cylinder; + ataCdb->write.DeviceHeadByte = (head | ATA_ADDRESS_DEVHEAD_STD); + ataCdb->write.CommandByte = ATA_CMD_PIO_WRITE; + break; + + case ALLOW_MEDIUM_REMOVAL: + usb_stor_dbg(us, " ATA OUT - SCSIOP_MEDIUM_REMOVAL\n"); + + if (info->DeviceFlags & DF_REMOVABLE_MEDIA) { + usb_stor_dbg(us, " srb->cmnd[4] = 0x%X\n", + srb->cmnd[4]); + + ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand; + ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand; + ataCdb->generic.TransferBlockSize = 1; + ataCdb->generic.RegisterSelect = REG_COMMAND; + ataCdb->write.CommandByte = (srb->cmnd[4] & 0x1) ? + ATA_CMD_MEDIA_LOCK : ATA_CMD_MEDIA_UNLOCK; + isd200_srb_set_bufflen(srb, 0); + } else { + usb_stor_dbg(us, " Not removable media, just report okay\n"); + srb->result = SAM_STAT_GOOD; + sendToTransport = 0; + } + break; + + case START_STOP: + usb_stor_dbg(us, " ATA OUT - SCSIOP_START_STOP_UNIT\n"); + usb_stor_dbg(us, " srb->cmnd[4] = 0x%X\n", srb->cmnd[4]); + + if ((srb->cmnd[4] & 0x3) == 0x2) { + usb_stor_dbg(us, " Media Eject\n"); + ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand; + ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand; + ataCdb->generic.TransferBlockSize = 0; + ataCdb->generic.RegisterSelect = REG_COMMAND; + ataCdb->write.CommandByte = ATA_COMMAND_MEDIA_EJECT; + } else if ((srb->cmnd[4] & 0x3) == 0x1) { + usb_stor_dbg(us, " Get Media Status\n"); + ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand; + ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand; + ataCdb->generic.TransferBlockSize = 1; + ataCdb->generic.RegisterSelect = REG_COMMAND; + ataCdb->write.CommandByte = ATA_COMMAND_GET_MEDIA_STATUS; + isd200_srb_set_bufflen(srb, 0); + } else { + usb_stor_dbg(us, " Nothing to do, just report okay\n"); + srb->result = SAM_STAT_GOOD; + sendToTransport = 0; + } + break; + + default: + usb_stor_dbg(us, "Unsupported SCSI command - 0x%X\n", + srb->cmnd[0]); + srb->result = DID_ERROR << 16; + sendToTransport = 0; + break; + } + + return(sendToTransport); +} + + +/************************************************************************** + * isd200_free_info + * + * Frees the driver structure. + */ +static void isd200_free_info_ptrs(void *info_) +{ + struct isd200_info *info = (struct isd200_info *) info_; + + if (info) { + kfree(info->id); + kfree(info->RegsBuf); + kfree(info->srb.sense_buffer); + } +} + +/************************************************************************** + * isd200_init_info + * + * Allocates (if necessary) and initializes the driver structure. + * + * RETURNS: + * error status code + */ +static int isd200_init_info(struct us_data *us) +{ + struct isd200_info *info; + + info = kzalloc_obj(struct isd200_info); + if (!info) + return -ENOMEM; + + info->id = kzalloc(ATA_ID_WORDS * 2, GFP_KERNEL); + info->RegsBuf = kmalloc(sizeof(info->ATARegs), GFP_KERNEL); + info->srb.sense_buffer = kmalloc(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL); + + if (!info->id || !info->RegsBuf || !info->srb.sense_buffer) { + isd200_free_info_ptrs(info); + kfree(info); + return -ENOMEM; + } + + us->extra = info; + us->extra_destructor = isd200_free_info_ptrs; + + return 0; +} + +/************************************************************************** + * Initialization for the ISD200 + */ + +static int isd200_Initialization(struct us_data *us) +{ + int rc = 0; + + usb_stor_dbg(us, "ISD200 Initialization...\n"); + + /* Initialize ISD200 info struct */ + + if (isd200_init_info(us) < 0) { + usb_stor_dbg(us, "ERROR Initializing ISD200 Info struct\n"); + rc = -ENOMEM; + } else { + /* Get device specific data */ + + if (isd200_get_inquiry_data(us) != ISD200_GOOD) { + usb_stor_dbg(us, "ISD200 Initialization Failure\n"); + rc = -EINVAL; + } else { + usb_stor_dbg(us, "ISD200 Initialization complete\n"); + } + } + + return rc; +} + + +/************************************************************************** + * Protocol and Transport for the ISD200 ASIC + * + * This protocol and transport are for ATA devices connected to an ISD200 + * ASIC. An ATAPI device that is connected as a slave device will be + * detected in the driver initialization function and the protocol will + * be changed to an ATAPI protocol (Transparent SCSI). + * + */ + +static void isd200_ata_command(struct scsi_cmnd *srb, struct us_data *us) +{ + int sendToTransport, orig_bufflen; + union ata_cdb ataCdb; + + /* Make sure driver was initialized */ + + if (us->extra == NULL) { + usb_stor_dbg(us, "ERROR Driver not initialized\n"); + srb->result = DID_ERROR << 16; + return; + } + + scsi_set_resid(srb, 0); + /* scsi_bufflen might change in protocol translation to ata */ + orig_bufflen = scsi_bufflen(srb); + sendToTransport = isd200_scsi_to_ata(srb, us, &ataCdb); + + /* send the command to the transport layer */ + if (sendToTransport) + isd200_invoke_transport(us, srb, &ataCdb); + + isd200_srb_set_bufflen(srb, orig_bufflen); +} + +static struct scsi_host_template isd200_host_template; + +static int isd200_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct us_data *us; + int result; + + result = usb_stor_probe1(&us, intf, id, + (id - isd200_usb_ids) + isd200_unusual_dev_list, + &isd200_host_template); + if (result) + return result; + + us->protocol_name = "ISD200 ATA/ATAPI"; + us->proto_handler = isd200_ata_command; + + result = usb_stor_probe2(us); + return result; +} + +static struct usb_driver isd200_driver = { + .name = DRV_NAME, + .probe = isd200_probe, + .disconnect = usb_stor_disconnect, + .suspend = usb_stor_suspend, + .resume = usb_stor_resume, + .reset_resume = usb_stor_reset_resume, + .pre_reset = usb_stor_pre_reset, + .post_reset = usb_stor_post_reset, + .id_table = isd200_usb_ids, + .soft_unbind = 1, + .no_dynamic_id = 1, +}; + +module_usb_stor_driver(isd200_driver, isd200_host_template, DRV_NAME); diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c new file mode 100644 index 000000000..a9c480914 --- /dev/null +++ b/drivers/usb/storage/jumpshot.c @@ -0,0 +1,683 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Lexar "Jumpshot" Compact Flash reader + * + * jumpshot driver v0.1: + * + * First release + * + * Current development and maintenance by: + * (c) 2000 Jimmie Mayfield (mayfield+usb@sackheads.org) + * + * Many thanks to Robert Baruch for the SanDisk SmartMedia reader driver + * which I used as a template for this driver. + * + * Some bugfixes and scatter-gather code by Gregory P. Smith + * (greg-usb@electricrain.com) + * + * Fix for media change by Joerg Schneider (js@joergschneider.com) + * + * Developed with the assistance of: + * + * (C) 2002 Alan Stern + */ + + /* + * This driver attempts to support the Lexar Jumpshot USB CompactFlash + * reader. Like many other USB CompactFlash readers, the Jumpshot contains + * a USB-to-ATA chip. + * + * This driver supports reading and writing. If you're truly paranoid, + * however, you can force the driver into a write-protected state by setting + * the WP enable bits in jumpshot_handle_mode_sense. See the comments + * in that routine. + */ + +#include +#include +#include + +#include +#include + +#include "usb.h" +#include "transport.h" +#include "protocol.h" +#include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-jumpshot" + +MODULE_DESCRIPTION("Driver for Lexar \"Jumpshot\" Compact Flash reader"); +MODULE_AUTHOR("Jimmie Mayfield "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("USB_STORAGE"); + +/* + * The table of devices + */ +#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ + vendorName, productName, useProtocol, useTransport, \ + initFunction, flags) \ +{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ + .driver_info = (flags) } + +static const struct usb_device_id jumpshot_usb_ids[] = { +# include "unusual_jumpshot.h" + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, jumpshot_usb_ids); + +#undef UNUSUAL_DEV + +/* + * The flags table + */ +#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ + vendor_name, product_name, use_protocol, use_transport, \ + init_function, Flags) \ +{ \ + .vendorName = vendor_name, \ + .productName = product_name, \ + .useProtocol = use_protocol, \ + .useTransport = use_transport, \ + .initFunction = init_function, \ +} + +static const struct us_unusual_dev jumpshot_unusual_dev_list[] = { +# include "unusual_jumpshot.h" + { } /* Terminating entry */ +}; + +#undef UNUSUAL_DEV + + +struct jumpshot_info { + unsigned long sectors; /* total sector count */ + unsigned long ssize; /* sector size in bytes */ + + /* the following aren't used yet */ + unsigned char sense_key; + unsigned long sense_asc; /* additional sense code */ + unsigned long sense_ascq; /* additional sense code qualifier */ +}; + +static inline int jumpshot_bulk_read(struct us_data *us, + unsigned char *data, + unsigned int len) +{ + if (len == 0) + return USB_STOR_XFER_GOOD; + + usb_stor_dbg(us, "len = %d\n", len); + return usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + data, len, NULL); +} + + +static inline int jumpshot_bulk_write(struct us_data *us, + unsigned char *data, + unsigned int len) +{ + if (len == 0) + return USB_STOR_XFER_GOOD; + + usb_stor_dbg(us, "len = %d\n", len); + return usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + data, len, NULL); +} + + +static int jumpshot_get_status(struct us_data *us) +{ + int rc; + + if (!us) + return USB_STOR_TRANSPORT_ERROR; + + // send the setup + rc = usb_stor_ctrl_transfer(us, us->recv_ctrl_pipe, + 0, 0xA0, 0, 7, us->iobuf, 1); + + if (rc != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + if (us->iobuf[0] != 0x50) { + usb_stor_dbg(us, "0x%2x\n", us->iobuf[0]); + return USB_STOR_TRANSPORT_ERROR; + } + + return USB_STOR_TRANSPORT_GOOD; +} + +static int jumpshot_read_data(struct us_data *us, + struct jumpshot_info *info, + u32 sector, + u32 sectors) +{ + unsigned char *command = us->iobuf; + unsigned char *buffer; + unsigned char thistime; + unsigned int totallen, alloclen; + int len, result; + unsigned int sg_offset = 0; + struct scatterlist *sg = NULL; + + // we're working in LBA mode. according to the ATA spec, + // we can support up to 28-bit addressing. I don't know if Jumpshot + // supports beyond 24-bit addressing. It's kind of hard to test + // since it requires > 8GB CF card. + + if (sector > 0x0FFFFFFF) + return USB_STOR_TRANSPORT_ERROR; + + totallen = sectors * info->ssize; + + // Since we don't read more than 64 KB at a time, we have to create + // a bounce buffer and move the data a piece at a time between the + // bounce buffer and the actual transfer buffer. + + alloclen = min(totallen, 65536u); + buffer = kmalloc(alloclen, GFP_NOIO); + if (buffer == NULL) + return USB_STOR_TRANSPORT_ERROR; + + do { + // loop, never allocate or transfer more than 64k at once + // (min(128k, 255*info->ssize) is the real limit) + len = min(totallen, alloclen); + thistime = (len / info->ssize) & 0xff; + + command[0] = 0; + command[1] = thistime; + command[2] = sector & 0xFF; + command[3] = (sector >> 8) & 0xFF; + command[4] = (sector >> 16) & 0xFF; + + command[5] = 0xE0 | ((sector >> 24) & 0x0F); + command[6] = 0x20; + + // send the setup + command + result = usb_stor_ctrl_transfer(us, us->send_ctrl_pipe, + 0, 0x20, 0, 1, command, 7); + if (result != USB_STOR_XFER_GOOD) + goto leave; + + // read the result + result = jumpshot_bulk_read(us, buffer, len); + if (result != USB_STOR_XFER_GOOD) + goto leave; + + usb_stor_dbg(us, "%d bytes\n", len); + + // Store the data in the transfer buffer + usb_stor_access_xfer_buf(buffer, len, us->srb, + &sg, &sg_offset, TO_XFER_BUF); + + sector += thistime; + totallen -= len; + } while (totallen > 0); + + kfree(buffer); + return USB_STOR_TRANSPORT_GOOD; + + leave: + kfree(buffer); + return USB_STOR_TRANSPORT_ERROR; +} + + +static int jumpshot_write_data(struct us_data *us, + struct jumpshot_info *info, + u32 sector, + u32 sectors) +{ + unsigned char *command = us->iobuf; + unsigned char *buffer; + unsigned char thistime; + unsigned int totallen, alloclen; + int len, result, waitcount; + unsigned int sg_offset = 0; + struct scatterlist *sg = NULL; + + // we're working in LBA mode. according to the ATA spec, + // we can support up to 28-bit addressing. I don't know if Jumpshot + // supports beyond 24-bit addressing. It's kind of hard to test + // since it requires > 8GB CF card. + // + if (sector > 0x0FFFFFFF) + return USB_STOR_TRANSPORT_ERROR; + + totallen = sectors * info->ssize; + + // Since we don't write more than 64 KB at a time, we have to create + // a bounce buffer and move the data a piece at a time between the + // bounce buffer and the actual transfer buffer. + + alloclen = min(totallen, 65536u); + buffer = kmalloc(alloclen, GFP_NOIO); + if (buffer == NULL) + return USB_STOR_TRANSPORT_ERROR; + + do { + // loop, never allocate or transfer more than 64k at once + // (min(128k, 255*info->ssize) is the real limit) + + len = min(totallen, alloclen); + thistime = (len / info->ssize) & 0xff; + + // Get the data from the transfer buffer + usb_stor_access_xfer_buf(buffer, len, us->srb, + &sg, &sg_offset, FROM_XFER_BUF); + + command[0] = 0; + command[1] = thistime; + command[2] = sector & 0xFF; + command[3] = (sector >> 8) & 0xFF; + command[4] = (sector >> 16) & 0xFF; + + command[5] = 0xE0 | ((sector >> 24) & 0x0F); + command[6] = 0x30; + + // send the setup + command + result = usb_stor_ctrl_transfer(us, us->send_ctrl_pipe, + 0, 0x20, 0, 1, command, 7); + if (result != USB_STOR_XFER_GOOD) + goto leave; + + // send the data + result = jumpshot_bulk_write(us, buffer, len); + if (result != USB_STOR_XFER_GOOD) + goto leave; + + // read the result. apparently the bulk write can complete + // before the jumpshot drive is finished writing. so we loop + // here until we get a good return code + waitcount = 0; + do { + result = jumpshot_get_status(us); + if (result != USB_STOR_TRANSPORT_GOOD) { + // I have not experimented to find the smallest value. + // + msleep(50); + } + } while ((result != USB_STOR_TRANSPORT_GOOD) && (waitcount < 10)); + + if (result != USB_STOR_TRANSPORT_GOOD) + usb_stor_dbg(us, "Gah! Waitcount = 10. Bad write!?\n"); + + sector += thistime; + totallen -= len; + } while (totallen > 0); + + kfree(buffer); + return result; + + leave: + kfree(buffer); + return USB_STOR_TRANSPORT_ERROR; +} + +static int jumpshot_id_device(struct us_data *us, + struct jumpshot_info *info) +{ + unsigned char *command = us->iobuf; + unsigned char *reply; + int rc; + + if (!info) + return USB_STOR_TRANSPORT_ERROR; + + command[0] = 0xE0; + command[1] = 0xEC; + reply = kmalloc(512, GFP_NOIO); + if (!reply) + return USB_STOR_TRANSPORT_ERROR; + + // send the setup + rc = usb_stor_ctrl_transfer(us, us->send_ctrl_pipe, + 0, 0x20, 0, 6, command, 2); + + if (rc != USB_STOR_XFER_GOOD) { + usb_stor_dbg(us, "Gah! send_control for read_capacity failed\n"); + rc = USB_STOR_TRANSPORT_ERROR; + goto leave; + } + + // read the reply + rc = jumpshot_bulk_read(us, reply, 512); + if (rc != USB_STOR_XFER_GOOD) { + rc = USB_STOR_TRANSPORT_ERROR; + goto leave; + } + + info->sectors = ((u32)(reply[117]) << 24) | + ((u32)(reply[116]) << 16) | + ((u32)(reply[115]) << 8) | + ((u32)(reply[114]) ); + + rc = USB_STOR_TRANSPORT_GOOD; + + leave: + kfree(reply); + return rc; +} + +static int jumpshot_handle_mode_sense(struct us_data *us, + struct scsi_cmnd * srb, + int sense_6) +{ + static const unsigned char rw_err_page[12] = { + 0x1, 0xA, 0x21, 1, 0, 0, 0, 0, 1, 0, 0, 0 + }; + static const unsigned char cache_page[12] = { + 0x8, 0xA, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + static const unsigned char rbac_page[12] = { + 0x1B, 0xA, 0, 0x81, 0, 0, 0, 0, 0, 0, 0, 0 + }; + static const unsigned char timer_page[8] = { + 0x1C, 0x6, 0, 0, 0, 0 + }; + unsigned char pc, page_code; + unsigned int i = 0; + struct jumpshot_info *info = (struct jumpshot_info *) (us->extra); + unsigned char *ptr = us->iobuf; + + pc = srb->cmnd[2] >> 6; + page_code = srb->cmnd[2] & 0x3F; + + switch (pc) { + case 0x0: + usb_stor_dbg(us, "Current values\n"); + break; + case 0x1: + usb_stor_dbg(us, "Changeable values\n"); + break; + case 0x2: + usb_stor_dbg(us, "Default values\n"); + break; + case 0x3: + usb_stor_dbg(us, "Saves values\n"); + break; + } + + memset(ptr, 0, 8); + if (sense_6) { + ptr[2] = 0x00; // WP enable: 0x80 + i = 4; + } else { + ptr[3] = 0x00; // WP enable: 0x80 + i = 8; + } + + switch (page_code) { + case 0x0: + // vendor-specific mode + info->sense_key = 0x05; + info->sense_asc = 0x24; + info->sense_ascq = 0x00; + return USB_STOR_TRANSPORT_FAILED; + + case 0x1: + memcpy(ptr + i, rw_err_page, sizeof(rw_err_page)); + i += sizeof(rw_err_page); + break; + + case 0x8: + memcpy(ptr + i, cache_page, sizeof(cache_page)); + i += sizeof(cache_page); + break; + + case 0x1B: + memcpy(ptr + i, rbac_page, sizeof(rbac_page)); + i += sizeof(rbac_page); + break; + + case 0x1C: + memcpy(ptr + i, timer_page, sizeof(timer_page)); + i += sizeof(timer_page); + break; + + case 0x3F: + memcpy(ptr + i, timer_page, sizeof(timer_page)); + i += sizeof(timer_page); + memcpy(ptr + i, rbac_page, sizeof(rbac_page)); + i += sizeof(rbac_page); + memcpy(ptr + i, cache_page, sizeof(cache_page)); + i += sizeof(cache_page); + memcpy(ptr + i, rw_err_page, sizeof(rw_err_page)); + i += sizeof(rw_err_page); + break; + } + + if (sense_6) + ptr[0] = i - 1; + else + ((__be16 *) ptr)[0] = cpu_to_be16(i - 2); + usb_stor_set_xfer_buf(ptr, i, srb); + + return USB_STOR_TRANSPORT_GOOD; +} + + +static void jumpshot_info_destructor(void *extra) +{ + // this routine is a placeholder... + // currently, we don't allocate any extra blocks so we're okay +} + + + +// Transport for the Lexar 'Jumpshot' +// +static int jumpshot_transport(struct scsi_cmnd *srb, struct us_data *us) +{ + struct jumpshot_info *info; + int rc; + unsigned long block, blocks; + unsigned char *ptr = us->iobuf; + static const unsigned char inquiry_response[8] = { + 0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00 + }; + + if (!us->extra) { + us->extra = kzalloc_obj(struct jumpshot_info, GFP_NOIO); + if (!us->extra) + return USB_STOR_TRANSPORT_ERROR; + + us->extra_destructor = jumpshot_info_destructor; + } + + info = (struct jumpshot_info *) (us->extra); + + if (srb->cmnd[0] == INQUIRY) { + usb_stor_dbg(us, "INQUIRY - Returning bogus response\n"); + memcpy(ptr, inquiry_response, sizeof(inquiry_response)); + fill_inquiry_response(us, ptr, 36); + return USB_STOR_TRANSPORT_GOOD; + } + + if (srb->cmnd[0] == READ_CAPACITY) { + info->ssize = 0x200; // hard coded 512 byte sectors as per ATA spec + + rc = jumpshot_get_status(us); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; + + rc = jumpshot_id_device(us, info); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; + + usb_stor_dbg(us, "READ_CAPACITY: %ld sectors, %ld bytes per sector\n", + info->sectors, info->ssize); + + // build the reply + // + ((__be32 *) ptr)[0] = cpu_to_be32(info->sectors - 1); + ((__be32 *) ptr)[1] = cpu_to_be32(info->ssize); + usb_stor_set_xfer_buf(ptr, 8, srb); + + return USB_STOR_TRANSPORT_GOOD; + } + + if (srb->cmnd[0] == MODE_SELECT_10) { + usb_stor_dbg(us, "Gah! MODE_SELECT_10\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + if (srb->cmnd[0] == READ_10) { + block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) | + ((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5])); + + blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8])); + + usb_stor_dbg(us, "READ_10: read block 0x%04lx count %ld\n", + block, blocks); + return jumpshot_read_data(us, info, block, blocks); + } + + if (srb->cmnd[0] == READ_12) { + // I don't think we'll ever see a READ_12 but support it anyway... + // + block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) | + ((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5])); + + blocks = ((u32)(srb->cmnd[6]) << 24) | ((u32)(srb->cmnd[7]) << 16) | + ((u32)(srb->cmnd[8]) << 8) | ((u32)(srb->cmnd[9])); + + usb_stor_dbg(us, "READ_12: read block 0x%04lx count %ld\n", + block, blocks); + return jumpshot_read_data(us, info, block, blocks); + } + + if (srb->cmnd[0] == WRITE_10) { + block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) | + ((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5])); + + blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8])); + + usb_stor_dbg(us, "WRITE_10: write block 0x%04lx count %ld\n", + block, blocks); + return jumpshot_write_data(us, info, block, blocks); + } + + if (srb->cmnd[0] == WRITE_12) { + // I don't think we'll ever see a WRITE_12 but support it anyway... + // + block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) | + ((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5])); + + blocks = ((u32)(srb->cmnd[6]) << 24) | ((u32)(srb->cmnd[7]) << 16) | + ((u32)(srb->cmnd[8]) << 8) | ((u32)(srb->cmnd[9])); + + usb_stor_dbg(us, "WRITE_12: write block 0x%04lx count %ld\n", + block, blocks); + return jumpshot_write_data(us, info, block, blocks); + } + + + if (srb->cmnd[0] == TEST_UNIT_READY) { + usb_stor_dbg(us, "TEST_UNIT_READY\n"); + return jumpshot_get_status(us); + } + + if (srb->cmnd[0] == REQUEST_SENSE) { + usb_stor_dbg(us, "REQUEST_SENSE\n"); + + memset(ptr, 0, 18); + ptr[0] = 0xF0; + ptr[2] = info->sense_key; + ptr[7] = 11; + ptr[12] = info->sense_asc; + ptr[13] = info->sense_ascq; + usb_stor_set_xfer_buf(ptr, 18, srb); + + return USB_STOR_TRANSPORT_GOOD; + } + + if (srb->cmnd[0] == MODE_SENSE) { + usb_stor_dbg(us, "MODE_SENSE_6 detected\n"); + return jumpshot_handle_mode_sense(us, srb, 1); + } + + if (srb->cmnd[0] == MODE_SENSE_10) { + usb_stor_dbg(us, "MODE_SENSE_10 detected\n"); + return jumpshot_handle_mode_sense(us, srb, 0); + } + + if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) { + /* + * sure. whatever. not like we can stop the user from popping + * the media out of the device (no locking doors, etc) + */ + return USB_STOR_TRANSPORT_GOOD; + } + + if (srb->cmnd[0] == START_STOP) { + /* + * this is used by sd.c'check_scsidisk_media_change to detect + * media change + */ + usb_stor_dbg(us, "START_STOP\n"); + /* + * the first jumpshot_id_device after a media change returns + * an error (determined experimentally) + */ + rc = jumpshot_id_device(us, info); + if (rc == USB_STOR_TRANSPORT_GOOD) { + info->sense_key = NO_SENSE; + srb->result = SUCCESS; + } else { + info->sense_key = UNIT_ATTENTION; + srb->result = SAM_STAT_CHECK_CONDITION; + } + return rc; + } + + usb_stor_dbg(us, "Gah! Unknown command: %d (0x%x)\n", + srb->cmnd[0], srb->cmnd[0]); + info->sense_key = 0x05; + info->sense_asc = 0x20; + info->sense_ascq = 0x00; + return USB_STOR_TRANSPORT_FAILED; +} + +static struct scsi_host_template jumpshot_host_template; + +static int jumpshot_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct us_data *us; + int result; + + result = usb_stor_probe1(&us, intf, id, + (id - jumpshot_usb_ids) + jumpshot_unusual_dev_list, + &jumpshot_host_template); + if (result) + return result; + + us->transport_name = "Lexar Jumpshot Control/Bulk"; + us->transport = jumpshot_transport; + us->transport_reset = usb_stor_Bulk_reset; + us->max_lun = 1; + + result = usb_stor_probe2(us); + return result; +} + +static struct usb_driver jumpshot_driver = { + .name = DRV_NAME, + .probe = jumpshot_probe, + .disconnect = usb_stor_disconnect, + .suspend = usb_stor_suspend, + .resume = usb_stor_resume, + .reset_resume = usb_stor_reset_resume, + .pre_reset = usb_stor_pre_reset, + .post_reset = usb_stor_post_reset, + .id_table = jumpshot_usb_ids, + .soft_unbind = 1, + .no_dynamic_id = 1, +}; + +module_usb_stor_driver(jumpshot_driver, jumpshot_host_template, DRV_NAME); diff --git a/drivers/usb/storage/karma.c b/drivers/usb/storage/karma.c new file mode 100644 index 000000000..5f199c21b --- /dev/null +++ b/drivers/usb/storage/karma.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Rio Karma + * + * (c) 2006 Bob Copeland + * (c) 2006 Keith Bennett + */ + +#include +#include + +#include +#include +#include + +#include "usb.h" +#include "transport.h" +#include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-karma" + +MODULE_DESCRIPTION("Driver for Rio Karma"); +MODULE_AUTHOR("Bob Copeland , Keith Bennett "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("USB_STORAGE"); + +#define RIO_PREFIX "RIOP\x00" +#define RIO_PREFIX_LEN 5 +#define RIO_SEND_LEN 40 +#define RIO_RECV_LEN 0x200 + +#define RIO_ENTER_STORAGE 0x1 +#define RIO_LEAVE_STORAGE 0x2 +#define RIO_RESET 0xC + +struct karma_data { + int in_storage; + char *recv; +}; + +static int rio_karma_init(struct us_data *us); + + +/* + * The table of devices + */ +#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ + vendorName, productName, useProtocol, useTransport, \ + initFunction, flags) \ +{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ + .driver_info = (flags) } + +static const struct usb_device_id karma_usb_ids[] = { +# include "unusual_karma.h" + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, karma_usb_ids); + +#undef UNUSUAL_DEV + +/* + * The flags table + */ +#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ + vendor_name, product_name, use_protocol, use_transport, \ + init_function, Flags) \ +{ \ + .vendorName = vendor_name, \ + .productName = product_name, \ + .useProtocol = use_protocol, \ + .useTransport = use_transport, \ + .initFunction = init_function, \ +} + +static const struct us_unusual_dev karma_unusual_dev_list[] = { +# include "unusual_karma.h" + { } /* Terminating entry */ +}; + +#undef UNUSUAL_DEV + + +/* + * Send commands to Rio Karma. + * + * For each command we send 40 bytes starting 'RIOP\0' followed by + * the command number and a sequence number, which the device will ack + * with a 512-byte packet with the high four bits set and everything + * else null. Then we send 'RIOP\x80' followed by a zero and the + * sequence number, until byte 5 in the response repeats the sequence + * number. + */ +static int rio_karma_send_command(char cmd, struct us_data *us) +{ + int result; + unsigned long timeout; + static unsigned char seq = 1; + struct karma_data *data = (struct karma_data *) us->extra; + + usb_stor_dbg(us, "sending command %04x\n", cmd); + memset(us->iobuf, 0, RIO_SEND_LEN); + memcpy(us->iobuf, RIO_PREFIX, RIO_PREFIX_LEN); + us->iobuf[5] = cmd; + us->iobuf[6] = seq; + + timeout = jiffies + msecs_to_jiffies(6000); + for (;;) { + result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + us->iobuf, RIO_SEND_LEN, NULL); + if (result != USB_STOR_XFER_GOOD) + goto err; + + result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + data->recv, RIO_RECV_LEN, NULL); + if (result != USB_STOR_XFER_GOOD) + goto err; + + if (data->recv[5] == seq) + break; + + if (time_after(jiffies, timeout)) + goto err; + + us->iobuf[4] = 0x80; + us->iobuf[5] = 0; + msleep(50); + } + + seq++; + if (seq == 0) + seq = 1; + + usb_stor_dbg(us, "sent command %04x\n", cmd); + return 0; +err: + usb_stor_dbg(us, "command %04x failed\n", cmd); + return USB_STOR_TRANSPORT_FAILED; +} + +/* + * Trap START_STOP and READ_10 to leave/re-enter storage mode. + * Everything else is propagated to the normal bulk layer. + */ +static int rio_karma_transport(struct scsi_cmnd *srb, struct us_data *us) +{ + int ret; + struct karma_data *data = (struct karma_data *) us->extra; + + if (srb->cmnd[0] == READ_10 && !data->in_storage) { + ret = rio_karma_send_command(RIO_ENTER_STORAGE, us); + if (ret) + return ret; + + data->in_storage = 1; + return usb_stor_Bulk_transport(srb, us); + } else if (srb->cmnd[0] == START_STOP) { + ret = rio_karma_send_command(RIO_LEAVE_STORAGE, us); + if (ret) + return ret; + + data->in_storage = 0; + return rio_karma_send_command(RIO_RESET, us); + } + return usb_stor_Bulk_transport(srb, us); +} + +static void rio_karma_destructor(void *extra) +{ + struct karma_data *data = (struct karma_data *) extra; + + kfree(data->recv); +} + +static int rio_karma_init(struct us_data *us) +{ + struct karma_data *data = kzalloc_obj(struct karma_data, GFP_NOIO); + + if (!data) + return -ENOMEM; + + data->recv = kmalloc(RIO_RECV_LEN, GFP_NOIO); + if (!data->recv) { + kfree(data); + return -ENOMEM; + } + + us->extra = data; + us->extra_destructor = rio_karma_destructor; + if (rio_karma_send_command(RIO_ENTER_STORAGE, us)) + return -EIO; + + data->in_storage = 1; + + return 0; +} + +static struct scsi_host_template karma_host_template; + +static int karma_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct us_data *us; + int result; + + result = usb_stor_probe1(&us, intf, id, + (id - karma_usb_ids) + karma_unusual_dev_list, + &karma_host_template); + if (result) + return result; + + us->transport_name = "Rio Karma/Bulk"; + us->transport = rio_karma_transport; + us->transport_reset = usb_stor_Bulk_reset; + + result = usb_stor_probe2(us); + return result; +} + +static struct usb_driver karma_driver = { + .name = DRV_NAME, + .probe = karma_probe, + .disconnect = usb_stor_disconnect, + .suspend = usb_stor_suspend, + .resume = usb_stor_resume, + .reset_resume = usb_stor_reset_resume, + .pre_reset = usb_stor_pre_reset, + .post_reset = usb_stor_post_reset, + .id_table = karma_usb_ids, + .soft_unbind = 1, + .no_dynamic_id = 1, +}; + +module_usb_stor_driver(karma_driver, karma_host_template, DRV_NAME); diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c new file mode 100644 index 000000000..49d281f7b --- /dev/null +++ b/drivers/usb/storage/onetouch.c @@ -0,0 +1,307 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Support for the Maxtor OneTouch USB hard drive's button + * + * Current development and maintenance by: + * Copyright (c) 2005 Nick Sillik + * + * Initial work by: + * Copyright (c) 2003 Erik Thyren + * + * Based on usbmouse.c (Vojtech Pavlik) and xpad.c (Marko Friedemann) + * + */ +#include +#include +#include +#include +#include +#include "usb.h" +#include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-onetouch" + +MODULE_DESCRIPTION("Maxtor USB OneTouch hard drive button driver"); +MODULE_AUTHOR("Nick Sillik "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("USB_STORAGE"); + +#define ONETOUCH_PKT_LEN 0x02 +#define ONETOUCH_BUTTON KEY_PROG1 + +static int onetouch_connect_input(struct us_data *ss); +static void onetouch_release_input(void *onetouch_); + +struct usb_onetouch { + char name[128]; + char phys[64]; + struct input_dev *dev; /* input device interface */ + struct usb_device *udev; /* usb device */ + + struct urb *irq; /* urb for interrupt in report */ + unsigned char *data; /* input data */ + dma_addr_t data_dma; + unsigned int is_open:1; +}; + + +/* + * The table of devices + */ +#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ + vendorName, productName, useProtocol, useTransport, \ + initFunction, flags) \ +{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ + .driver_info = (flags) } + +static const struct usb_device_id onetouch_usb_ids[] = { +# include "unusual_onetouch.h" + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, onetouch_usb_ids); + +#undef UNUSUAL_DEV + +/* + * The flags table + */ +#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ + vendor_name, product_name, use_protocol, use_transport, \ + init_function, Flags) \ +{ \ + .vendorName = vendor_name, \ + .productName = product_name, \ + .useProtocol = use_protocol, \ + .useTransport = use_transport, \ + .initFunction = init_function, \ +} + +static const struct us_unusual_dev onetouch_unusual_dev_list[] = { +# include "unusual_onetouch.h" + { } /* Terminating entry */ +}; + +#undef UNUSUAL_DEV + + +static void usb_onetouch_irq(struct urb *urb) +{ + struct usb_onetouch *onetouch = urb->context; + signed char *data = onetouch->data; + struct input_dev *dev = onetouch->dev; + int status = urb->status; + int retval; + + switch (status) { + case 0: /* success */ + break; + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -ESHUTDOWN: + return; + /* -EPIPE: should clear the halt */ + default: /* error */ + goto resubmit; + } + + input_report_key(dev, ONETOUCH_BUTTON, data[0] & 0x02); + input_sync(dev); + +resubmit: + retval = usb_submit_urb (urb, GFP_ATOMIC); + if (retval) + dev_err(&dev->dev, "can't resubmit intr, %s-%s/input0, " + "retval %d\n", onetouch->udev->bus->bus_name, + onetouch->udev->devpath, retval); +} + +static int usb_onetouch_open(struct input_dev *dev) +{ + struct usb_onetouch *onetouch = input_get_drvdata(dev); + + onetouch->is_open = 1; + onetouch->irq->dev = onetouch->udev; + if (usb_submit_urb(onetouch->irq, GFP_KERNEL)) { + dev_err(&dev->dev, "usb_submit_urb failed\n"); + return -EIO; + } + + return 0; +} + +static void usb_onetouch_close(struct input_dev *dev) +{ + struct usb_onetouch *onetouch = input_get_drvdata(dev); + + usb_kill_urb(onetouch->irq); + onetouch->is_open = 0; +} + +#ifdef CONFIG_PM +static void usb_onetouch_pm_hook(struct us_data *us, int action) +{ + struct usb_onetouch *onetouch = (struct usb_onetouch *) us->extra; + + if (onetouch->is_open) { + switch (action) { + case US_SUSPEND: + usb_kill_urb(onetouch->irq); + break; + case US_RESUME: + if (usb_submit_urb(onetouch->irq, GFP_NOIO) != 0) + dev_err(&onetouch->irq->dev->dev, + "usb_submit_urb failed\n"); + break; + default: + break; + } + } +} +#endif /* CONFIG_PM */ + +static int onetouch_connect_input(struct us_data *ss) +{ + struct usb_device *udev = ss->pusb_dev; + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_onetouch *onetouch; + struct input_dev *input_dev; + int pipe, maxp; + int error = -ENOMEM; + + interface = ss->pusb_intf->cur_altsetting; + + if (interface->desc.bNumEndpoints != 3) + return -ENODEV; + + endpoint = &interface->endpoint[2].desc; + if (!usb_endpoint_is_int_in(endpoint)) + return -ENODEV; + + pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(udev, pipe); + maxp = min(maxp, ONETOUCH_PKT_LEN); + + onetouch = kzalloc_obj(struct usb_onetouch); + input_dev = input_allocate_device(); + if (!onetouch || !input_dev) + goto fail1; + + onetouch->data = usb_alloc_coherent(udev, ONETOUCH_PKT_LEN, + GFP_KERNEL, &onetouch->data_dma); + if (!onetouch->data) + goto fail1; + + onetouch->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!onetouch->irq) + goto fail2; + + onetouch->udev = udev; + onetouch->dev = input_dev; + + if (udev->manufacturer) + strscpy(onetouch->name, udev->manufacturer, + sizeof(onetouch->name)); + if (udev->product) { + if (udev->manufacturer) + strlcat(onetouch->name, " ", sizeof(onetouch->name)); + strlcat(onetouch->name, udev->product, sizeof(onetouch->name)); + } + + if (!strlen(onetouch->name)) + snprintf(onetouch->name, sizeof(onetouch->name), + "Maxtor Onetouch %04x:%04x", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + usb_make_path(udev, onetouch->phys, sizeof(onetouch->phys)); + strlcat(onetouch->phys, "/input0", sizeof(onetouch->phys)); + + input_dev->name = onetouch->name; + input_dev->phys = onetouch->phys; + usb_to_input_id(udev, &input_dev->id); + input_dev->dev.parent = &udev->dev; + + set_bit(EV_KEY, input_dev->evbit); + set_bit(ONETOUCH_BUTTON, input_dev->keybit); + clear_bit(0, input_dev->keybit); + + input_set_drvdata(input_dev, onetouch); + + input_dev->open = usb_onetouch_open; + input_dev->close = usb_onetouch_close; + + usb_fill_int_urb(onetouch->irq, udev, pipe, onetouch->data, maxp, + usb_onetouch_irq, onetouch, endpoint->bInterval); + onetouch->irq->transfer_dma = onetouch->data_dma; + onetouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + ss->extra_destructor = onetouch_release_input; + ss->extra = onetouch; +#ifdef CONFIG_PM + ss->suspend_resume_hook = usb_onetouch_pm_hook; +#endif + + error = input_register_device(onetouch->dev); + if (error) + goto fail3; + + return 0; + + fail3: usb_free_urb(onetouch->irq); + fail2: usb_free_coherent(udev, ONETOUCH_PKT_LEN, + onetouch->data, onetouch->data_dma); + fail1: kfree(onetouch); + input_free_device(input_dev); + return error; +} + +static void onetouch_release_input(void *onetouch_) +{ + struct usb_onetouch *onetouch = (struct usb_onetouch *) onetouch_; + + if (onetouch) { + usb_kill_urb(onetouch->irq); + input_unregister_device(onetouch->dev); + usb_free_urb(onetouch->irq); + usb_free_coherent(onetouch->udev, ONETOUCH_PKT_LEN, + onetouch->data, onetouch->data_dma); + } +} + +static struct scsi_host_template onetouch_host_template; + +static int onetouch_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct us_data *us; + int result; + + result = usb_stor_probe1(&us, intf, id, + (id - onetouch_usb_ids) + onetouch_unusual_dev_list, + &onetouch_host_template); + if (result) + return result; + + /* Use default transport and protocol */ + + result = usb_stor_probe2(us); + return result; +} + +static struct usb_driver onetouch_driver = { + .name = DRV_NAME, + .probe = onetouch_probe, + .disconnect = usb_stor_disconnect, + .suspend = usb_stor_suspend, + .resume = usb_stor_resume, + .reset_resume = usb_stor_reset_resume, + .pre_reset = usb_stor_pre_reset, + .post_reset = usb_stor_post_reset, + .id_table = onetouch_usb_ids, + .soft_unbind = 1, + .no_dynamic_id = 1, +}; + +module_usb_stor_driver(onetouch_driver, onetouch_host_template, DRV_NAME); diff --git a/drivers/usb/storage/option_ms.c b/drivers/usb/storage/option_ms.c new file mode 100644 index 000000000..7c0b05a36 --- /dev/null +++ b/drivers/usb/storage/option_ms.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Option High Speed Mobile Devices. + * + * (c) 2008 Dan Williams + * + * Inspiration taken from sierra_ms.c by Kevin Lloyd + */ + +#include +#include +#include + +#include "usb.h" +#include "transport.h" +#include "option_ms.h" +#include "debug.h" + +#define ZCD_FORCE_MODEM 0x01 +#define ZCD_ALLOW_MS 0x02 + +static unsigned int option_zero_cd = ZCD_FORCE_MODEM; +module_param(option_zero_cd, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(option_zero_cd, "ZeroCD mode (1=Force Modem (default)," + " 2=Allow CD-Rom"); + +#define RESPONSE_LEN 1024 + +static int option_rezero(struct us_data *us) +{ + static const unsigned char rezero_msg[] = { + 0x55, 0x53, 0x42, 0x43, 0x78, 0x56, 0x34, 0x12, + 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + char *buffer; + int result; + + usb_stor_dbg(us, "Option MS: %s\n", "DEVICE MODE SWITCH"); + + buffer = kzalloc(RESPONSE_LEN, GFP_KERNEL); + if (buffer == NULL) + return USB_STOR_TRANSPORT_ERROR; + + memcpy(buffer, rezero_msg, sizeof(rezero_msg)); + result = usb_stor_bulk_transfer_buf(us, + us->send_bulk_pipe, + buffer, sizeof(rezero_msg), NULL); + if (result != USB_STOR_XFER_GOOD) { + result = USB_STOR_XFER_ERROR; + goto out; + } + + /* + * Some of the devices need to be asked for a response, but we don't + * care what that response is. + */ + usb_stor_bulk_transfer_buf(us, + us->recv_bulk_pipe, + buffer, RESPONSE_LEN, NULL); + + /* Read the CSW */ + usb_stor_bulk_transfer_buf(us, + us->recv_bulk_pipe, + buffer, 13, NULL); + + result = USB_STOR_XFER_GOOD; + +out: + kfree(buffer); + return result; +} + +static int option_inquiry(struct us_data *us) +{ + static const unsigned char inquiry_msg[] = { + 0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78, + 0x24, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 0x12, + 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + char *buffer; + int result; + + usb_stor_dbg(us, "Option MS: %s\n", "device inquiry for vendor name"); + + buffer = kzalloc(0x24, GFP_KERNEL); + if (buffer == NULL) + return USB_STOR_TRANSPORT_ERROR; + + memcpy(buffer, inquiry_msg, sizeof(inquiry_msg)); + result = usb_stor_bulk_transfer_buf(us, + us->send_bulk_pipe, + buffer, sizeof(inquiry_msg), NULL); + if (result != USB_STOR_XFER_GOOD) { + result = USB_STOR_XFER_ERROR; + goto out; + } + + result = usb_stor_bulk_transfer_buf(us, + us->recv_bulk_pipe, + buffer, 0x24, NULL); + if (result != USB_STOR_XFER_GOOD) { + result = USB_STOR_XFER_ERROR; + goto out; + } + + result = memcmp(buffer+8, "Option", 6); + + if (result != 0) + result = memcmp(buffer+8, "ZCOPTION", 8); + + /* Read the CSW */ + usb_stor_bulk_transfer_buf(us, + us->recv_bulk_pipe, + buffer, 13, NULL); + +out: + kfree(buffer); + return result; +} + + +int option_ms_init(struct us_data *us) +{ + int result; + + usb_stor_dbg(us, "Option MS: %s\n", "option_ms_init called"); + + /* + * Additional test for vendor information via INQUIRY, + * because some vendor/product IDs are ambiguous + */ + result = option_inquiry(us); + if (result != 0) { + usb_stor_dbg(us, "Option MS: %s\n", + "vendor is not Option or not determinable, no action taken"); + return 0; + } else + usb_stor_dbg(us, "Option MS: %s\n", + "this is a genuine Option device, proceeding"); + + /* Force Modem mode */ + if (option_zero_cd == ZCD_FORCE_MODEM) { + usb_stor_dbg(us, "Option MS: %s\n", "Forcing Modem Mode"); + result = option_rezero(us); + if (result != USB_STOR_XFER_GOOD) + usb_stor_dbg(us, "Option MS: %s\n", + "Failed to switch to modem mode"); + return -EIO; + } else if (option_zero_cd == ZCD_ALLOW_MS) { + /* Allow Mass Storage mode (keep CD-Rom) */ + usb_stor_dbg(us, "Option MS: %s\n", + "Allowing Mass Storage Mode if device requests it"); + } + + return 0; +} + diff --git a/drivers/usb/storage/option_ms.h b/drivers/usb/storage/option_ms.h new file mode 100644 index 000000000..643999218 --- /dev/null +++ b/drivers/usb/storage/option_ms.h @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _OPTION_MS_H_ +#define _OPTION_MS_H_ +extern int option_ms_init(struct us_data *us); +#endif diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c new file mode 100644 index 000000000..0cff54ad9 --- /dev/null +++ b/drivers/usb/storage/protocol.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for USB Mass Storage compliant devices + * + * Current development and maintenance by: + * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * Developed with the assistance of: + * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org) + * (c) 2002 Alan Stern (stern@rowland.org) + * + * Initial work by: + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + */ + +#include +#include +#include +#include + +#include "usb.h" +#include "protocol.h" +#include "debug.h" +#include "scsiglue.h" +#include "transport.h" + +/*********************************************************************** + * Protocol routines + ***********************************************************************/ + +void usb_stor_pad12_command(struct scsi_cmnd *srb, struct us_data *us) +{ + /* + * Pad the SCSI command with zeros out to 12 bytes. If the + * command already is 12 bytes or longer, leave it alone. + * + * NOTE: This only works because a scsi_cmnd struct field contains + * a unsigned char cmnd[16], so we know we have storage available + */ + for (; srb->cmd_len < 12; srb->cmd_len++) + srb->cmnd[srb->cmd_len] = 0; + + /* send the command to the transport layer */ + usb_stor_invoke_transport(srb, us); +} + +void usb_stor_ufi_command(struct scsi_cmnd *srb, struct us_data *us) +{ + /* + * fix some commands -- this is a form of mode translation + * UFI devices only accept 12 byte long commands + * + * NOTE: This only works because a scsi_cmnd struct field contains + * a unsigned char cmnd[16], so we know we have storage available + */ + + /* Pad the ATAPI command with zeros */ + for (; srb->cmd_len < 12; srb->cmd_len++) + srb->cmnd[srb->cmd_len] = 0; + + /* set command length to 12 bytes (this affects the transport layer) */ + srb->cmd_len = 12; + + /* XXX We should be constantly re-evaluating the need for these */ + + /* determine the correct data length for these commands */ + switch (srb->cmnd[0]) { + + /* for INQUIRY, UFI devices only ever return 36 bytes */ + case INQUIRY: + srb->cmnd[4] = 36; + break; + + /* again, for MODE_SENSE_10, we get the minimum (8) */ + case MODE_SENSE_10: + srb->cmnd[7] = 0; + srb->cmnd[8] = 8; + break; + + /* for REQUEST_SENSE, UFI devices only ever return 18 bytes */ + case REQUEST_SENSE: + srb->cmnd[4] = 18; + break; + } /* end switch on cmnd[0] */ + + /* send the command to the transport layer */ + usb_stor_invoke_transport(srb, us); +} + +void usb_stor_transparent_scsi_command(struct scsi_cmnd *srb, + struct us_data *us) +{ + /* send the command to the transport layer */ + usb_stor_invoke_transport(srb, us); +} +EXPORT_SYMBOL_GPL(usb_stor_transparent_scsi_command); + +/*********************************************************************** + * Scatter-gather transfer buffer access routines + ***********************************************************************/ + +/* + * Copy a buffer of length buflen to/from the srb's transfer buffer. + * Update the **sgptr and *offset variables so that the next copy will + * pick up from where this one left off. + */ +unsigned int usb_stor_access_xfer_buf(unsigned char *buffer, + unsigned int buflen, struct scsi_cmnd *srb, struct scatterlist **sgptr, + unsigned int *offset, enum xfer_buf_dir dir) +{ + unsigned int cnt = 0; + struct scatterlist *sg = *sgptr; + struct sg_mapping_iter miter; + unsigned int nents = scsi_sg_count(srb); + + if (sg) + nents = sg_nents(sg); + else + sg = scsi_sglist(srb); + + sg_miter_start(&miter, sg, nents, dir == FROM_XFER_BUF ? + SG_MITER_FROM_SG: SG_MITER_TO_SG); + + if (!sg_miter_skip(&miter, *offset)) + return cnt; + + while (sg_miter_next(&miter) && cnt < buflen) { + unsigned int len = min(miter.length, buflen - cnt); + + if (dir == FROM_XFER_BUF) + memcpy(buffer + cnt, miter.addr, len); + else + memcpy(miter.addr, buffer + cnt, len); + + if (*offset + len < miter.piter.sg->length) { + *offset += len; + *sgptr = miter.piter.sg; + } else { + *offset = 0; + *sgptr = sg_next(miter.piter.sg); + } + cnt += len; + } + sg_miter_stop(&miter); + + return cnt; +} +EXPORT_SYMBOL_GPL(usb_stor_access_xfer_buf); + +/* + * Store the contents of buffer into srb's transfer buffer and set the + * SCSI residue. + */ +void usb_stor_set_xfer_buf(unsigned char *buffer, + unsigned int buflen, struct scsi_cmnd *srb) +{ + unsigned int offset = 0; + struct scatterlist *sg = NULL; + + buflen = min(buflen, scsi_bufflen(srb)); + buflen = usb_stor_access_xfer_buf(buffer, buflen, srb, &sg, &offset, + TO_XFER_BUF); + if (buflen < scsi_bufflen(srb)) + scsi_set_resid(srb, scsi_bufflen(srb) - buflen); +} +EXPORT_SYMBOL_GPL(usb_stor_set_xfer_buf); diff --git a/drivers/usb/storage/protocol.h b/drivers/usb/storage/protocol.h new file mode 100644 index 000000000..1d102463a --- /dev/null +++ b/drivers/usb/storage/protocol.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Driver for USB Mass Storage compliant devices + * Protocol Functions Header File + * + * Current development and maintenance by: + * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + */ + +#ifndef _PROTOCOL_H_ +#define _PROTOCOL_H_ + +/* Protocol handling routines */ +extern void usb_stor_pad12_command(struct scsi_cmnd*, struct us_data*); +extern void usb_stor_ufi_command(struct scsi_cmnd*, struct us_data*); +extern void usb_stor_transparent_scsi_command(struct scsi_cmnd*, + struct us_data*); + +/* struct scsi_cmnd transfer buffer access utilities */ +enum xfer_buf_dir {TO_XFER_BUF, FROM_XFER_BUF}; + +extern unsigned int usb_stor_access_xfer_buf(unsigned char *buffer, + unsigned int buflen, struct scsi_cmnd *srb, struct scatterlist **, + unsigned int *offset, enum xfer_buf_dir dir); + +extern void usb_stor_set_xfer_buf(unsigned char *buffer, + unsigned int buflen, struct scsi_cmnd *srb); +#endif diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c new file mode 100644 index 000000000..c4b287446 --- /dev/null +++ b/drivers/usb/storage/realtek_cr.c @@ -0,0 +1,1070 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Realtek RTS51xx USB card reader + * + * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * + * Author: + * wwang (wei_wang@realsil.com.cn) + * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "usb.h" +#include "transport.h" +#include "protocol.h" +#include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-realtek" + +MODULE_DESCRIPTION("Driver for Realtek USB Card Reader"); +MODULE_AUTHOR("wwang "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("USB_STORAGE"); + +static int auto_delink_en = 1; +module_param(auto_delink_en, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(auto_delink_en, "auto delink mode (0=firmware, 1=software [default])"); + +#ifdef CONFIG_REALTEK_AUTOPM +static int ss_en = 1; +module_param(ss_en, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(ss_en, "enable selective suspend"); + +static int ss_delay = 50; +module_param(ss_delay, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(ss_delay, + "seconds to delay before entering selective suspend"); + +enum RTS51X_STAT { + RTS51X_STAT_INIT, + RTS51X_STAT_IDLE, + RTS51X_STAT_RUN, + RTS51X_STAT_SS +}; + +#define POLLING_INTERVAL 50 + +#define rts51x_set_stat(chip, stat) \ + ((chip)->state = (enum RTS51X_STAT)(stat)) +#define rts51x_get_stat(chip) ((chip)->state) + +#define SET_LUN_READY(chip, lun) ((chip)->lun_ready |= ((u8)1 << (lun))) +#define CLR_LUN_READY(chip, lun) ((chip)->lun_ready &= ~((u8)1 << (lun))) +#define TST_LUN_READY(chip, lun) ((chip)->lun_ready & ((u8)1 << (lun))) + +#endif + +struct rts51x_status { + u16 vid; + u16 pid; + u8 cur_lun; + u8 card_type; + u8 total_lun; + u16 fw_ver; + u8 phy_exist; + u8 multi_flag; + u8 multi_card; + u8 log_exist; + union { + u8 detailed_type1; + u8 detailed_type2; + } detailed_type; + u8 function[2]; +}; + +struct rts51x_chip { + u16 vendor_id; + u16 product_id; + char max_lun; + + struct rts51x_status *status; + int status_len; + + u32 flag; + struct us_data *us; + +#ifdef CONFIG_REALTEK_AUTOPM + struct timer_list rts51x_suspend_timer; + unsigned long timer_expires; + int pwr_state; + u8 lun_ready; + enum RTS51X_STAT state; + int support_auto_delink; +#endif + /* used to back up the protocol chosen in probe1 phase */ + proto_cmnd proto_handler_backup; +}; + +/* flag definition */ +#define FLIDX_AUTO_DELINK 0x01 + +#define SCSI_LUN(srb) ((srb)->device->lun) + +/* Bit Operation */ +#define SET_BIT(data, idx) ((data) |= 1 << (idx)) +#define CLR_BIT(data, idx) ((data) &= ~(1 << (idx))) +#define CHK_BIT(data, idx) ((data) & (1 << (idx))) + +#define SET_AUTO_DELINK(chip) ((chip)->flag |= FLIDX_AUTO_DELINK) +#define CLR_AUTO_DELINK(chip) ((chip)->flag &= ~FLIDX_AUTO_DELINK) +#define CHK_AUTO_DELINK(chip) ((chip)->flag & FLIDX_AUTO_DELINK) + +#define RTS51X_GET_VID(chip) ((chip)->vendor_id) +#define RTS51X_GET_PID(chip) ((chip)->product_id) + +#define VENDOR_ID(chip) ((chip)->status[0].vid) +#define PRODUCT_ID(chip) ((chip)->status[0].pid) +#define FW_VERSION(chip) ((chip)->status[0].fw_ver) +#define STATUS_LEN(chip) ((chip)->status_len) + +#define STATUS_SUCCESS 0 +#define STATUS_FAIL 1 + +/* Check card reader function */ +#define SUPPORT_DETAILED_TYPE1(chip) \ + CHK_BIT((chip)->status[0].function[0], 1) +#define SUPPORT_OT(chip) \ + CHK_BIT((chip)->status[0].function[0], 2) +#define SUPPORT_OC(chip) \ + CHK_BIT((chip)->status[0].function[0], 3) +#define SUPPORT_AUTO_DELINK(chip) \ + CHK_BIT((chip)->status[0].function[0], 4) +#define SUPPORT_SDIO(chip) \ + CHK_BIT((chip)->status[0].function[1], 0) +#define SUPPORT_DETAILED_TYPE2(chip) \ + CHK_BIT((chip)->status[0].function[1], 1) + +#define CHECK_PID(chip, pid) (RTS51X_GET_PID(chip) == (pid)) +#define CHECK_FW_VER(chip, fw_ver) (FW_VERSION(chip) == (fw_ver)) +#define CHECK_ID(chip, pid, fw_ver) \ + (CHECK_PID((chip), (pid)) && CHECK_FW_VER((chip), (fw_ver))) + +static int init_realtek_cr(struct us_data *us); + +/* + * The table of devices + */ +#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ + vendorName, productName, useProtocol, useTransport, \ + initFunction, flags) \ +{\ + USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ + .driver_info = (flags) \ +} + +static const struct usb_device_id realtek_cr_ids[] = { +# include "unusual_realtek.h" + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, realtek_cr_ids); + +#undef UNUSUAL_DEV + +/* + * The flags table + */ +#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ + vendor_name, product_name, use_protocol, use_transport, \ + init_function, Flags) \ +{ \ + .vendorName = vendor_name, \ + .productName = product_name, \ + .useProtocol = use_protocol, \ + .useTransport = use_transport, \ + .initFunction = init_function, \ +} + +static const struct us_unusual_dev realtek_cr_unusual_dev_list[] = { +# include "unusual_realtek.h" + {} /* Terminating entry */ +}; + +#undef UNUSUAL_DEV + +static int rts51x_bulk_transport(struct us_data *us, u8 lun, + u8 *cmd, int cmd_len, u8 *buf, int buf_len, + enum dma_data_direction dir, int *act_len) +{ + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *)us->iobuf; + struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap *)us->iobuf; + int result; + unsigned int residue; + unsigned int cswlen; + unsigned int cbwlen = US_BULK_CB_WRAP_LEN; + + /* set up the command wrapper */ + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->DataTransferLength = cpu_to_le32(buf_len); + bcb->Flags = (dir == DMA_FROM_DEVICE) ? US_BULK_FLAG_IN : US_BULK_FLAG_OUT; + bcb->Tag = ++us->tag; + bcb->Lun = lun; + bcb->Length = cmd_len; + + /* copy the command payload */ + memset(bcb->CDB, 0, sizeof(bcb->CDB)); + memcpy(bcb->CDB, cmd, bcb->Length); + + /* send it to out endpoint */ + result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + bcb, cbwlen, NULL); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + /* DATA STAGE */ + /* send/receive data payload, if there is any */ + + if (buf && buf_len) { + unsigned int pipe = (dir == DMA_FROM_DEVICE) ? + us->recv_bulk_pipe : us->send_bulk_pipe; + result = usb_stor_bulk_transfer_buf(us, pipe, + buf, buf_len, NULL); + if (result == USB_STOR_XFER_ERROR) + return USB_STOR_TRANSPORT_ERROR; + } + + /* get CSW for device status */ + result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + bcs, US_BULK_CS_WRAP_LEN, &cswlen); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + /* check bulk status */ + if (bcs->Signature != cpu_to_le32(US_BULK_CS_SIGN)) { + usb_stor_dbg(us, "Signature mismatch: got %08X, expecting %08X\n", + le32_to_cpu(bcs->Signature), US_BULK_CS_SIGN); + return USB_STOR_TRANSPORT_ERROR; + } + + residue = le32_to_cpu(bcs->Residue); + if (bcs->Tag != us->tag) + return USB_STOR_TRANSPORT_ERROR; + + /* + * try to compute the actual residue, based on how much data + * was really transferred and what the device tells us + */ + if (residue > buf_len) + residue = buf_len; + + if (act_len) + *act_len = buf_len - residue; + + /* based on the status code, we report good or bad */ + switch (bcs->Status) { + case US_BULK_STAT_OK: + /* command good -- note that data could be short */ + return USB_STOR_TRANSPORT_GOOD; + + case US_BULK_STAT_FAIL: + /* command failed */ + return USB_STOR_TRANSPORT_FAILED; + + case US_BULK_STAT_PHASE: + /* + * phase error -- note that a transport reset will be + * invoked by the invoke_transport() function + */ + return USB_STOR_TRANSPORT_ERROR; + } + + /* we should never get here, but if we do, we're in trouble */ + return USB_STOR_TRANSPORT_ERROR; +} + +static int rts51x_bulk_transport_special(struct us_data *us, u8 lun, + u8 *cmd, int cmd_len, u8 *buf, int buf_len, + enum dma_data_direction dir, int *act_len) +{ + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap *) us->iobuf; + int result; + unsigned int cswlen; + unsigned int cbwlen = US_BULK_CB_WRAP_LEN; + + /* set up the command wrapper */ + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->DataTransferLength = cpu_to_le32(buf_len); + bcb->Flags = (dir == DMA_FROM_DEVICE) ? US_BULK_FLAG_IN : US_BULK_FLAG_OUT; + bcb->Tag = ++us->tag; + bcb->Lun = lun; + bcb->Length = cmd_len; + + /* copy the command payload */ + memset(bcb->CDB, 0, sizeof(bcb->CDB)); + memcpy(bcb->CDB, cmd, bcb->Length); + + /* send it to out endpoint */ + result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + bcb, cbwlen, NULL); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + /* DATA STAGE */ + /* send/receive data payload, if there is any */ + + if (buf && buf_len) { + unsigned int pipe = (dir == DMA_FROM_DEVICE) ? + us->recv_bulk_pipe : us->send_bulk_pipe; + result = usb_stor_bulk_transfer_buf(us, pipe, + buf, buf_len, NULL); + if (result == USB_STOR_XFER_ERROR) + return USB_STOR_TRANSPORT_ERROR; + } + + /* get CSW for device status */ + result = usb_bulk_msg(us->pusb_dev, us->recv_bulk_pipe, bcs, + US_BULK_CS_WRAP_LEN, &cswlen, 250); + return result; +} + +/* Determine what the maximum LUN supported is */ +static int rts51x_get_max_lun(struct us_data *us) +{ + int result; + + /* issue the command */ + us->iobuf[0] = 0; + result = usb_stor_control_msg(us, us->recv_ctrl_pipe, + US_BULK_GET_MAX_LUN, + USB_DIR_IN | USB_TYPE_CLASS | + USB_RECIP_INTERFACE, + 0, us->ifnum, us->iobuf, 1, 10 * HZ); + + usb_stor_dbg(us, "GetMaxLUN command result is %d, data is %d\n", + result, us->iobuf[0]); + + /* if we have a successful request, return the result */ + if (result > 0) + return us->iobuf[0]; + + return 0; +} + +static int rts51x_read_mem(struct us_data *us, u16 addr, u8 *data, u16 len) +{ + int retval; + u8 cmnd[12] = { 0 }; + u8 *buf; + + buf = kmalloc(len, GFP_NOIO); + if (buf == NULL) + return -ENOMEM; + + usb_stor_dbg(us, "addr = 0x%x, len = %d\n", addr, len); + + cmnd[0] = 0xF0; + cmnd[1] = 0x0D; + cmnd[2] = (u8) (addr >> 8); + cmnd[3] = (u8) addr; + cmnd[4] = (u8) (len >> 8); + cmnd[5] = (u8) len; + + retval = rts51x_bulk_transport(us, 0, cmnd, 12, + buf, len, DMA_FROM_DEVICE, NULL); + if (retval != USB_STOR_TRANSPORT_GOOD) { + kfree(buf); + return -EIO; + } + + memcpy(data, buf, len); + kfree(buf); + return 0; +} + +static int rts51x_write_mem(struct us_data *us, u16 addr, u8 *data, u16 len) +{ + int retval; + u8 cmnd[12] = { 0 }; + u8 *buf; + + buf = kmemdup(data, len, GFP_NOIO); + if (buf == NULL) + return USB_STOR_TRANSPORT_ERROR; + + usb_stor_dbg(us, "addr = 0x%x, len = %d\n", addr, len); + + cmnd[0] = 0xF0; + cmnd[1] = 0x0E; + cmnd[2] = (u8) (addr >> 8); + cmnd[3] = (u8) addr; + cmnd[4] = (u8) (len >> 8); + cmnd[5] = (u8) len; + + retval = rts51x_bulk_transport(us, 0, cmnd, 12, + buf, len, DMA_TO_DEVICE, NULL); + kfree(buf); + if (retval != USB_STOR_TRANSPORT_GOOD) + return -EIO; + + return 0; +} + +static int rts51x_read_status(struct us_data *us, + u8 lun, u8 *status, int len, int *actlen) +{ + int retval; + u8 cmnd[12] = { 0 }; + u8 *buf; + + buf = kmalloc(len, GFP_NOIO); + if (buf == NULL) + return USB_STOR_TRANSPORT_ERROR; + + usb_stor_dbg(us, "lun = %d\n", lun); + + cmnd[0] = 0xF0; + cmnd[1] = 0x09; + + retval = rts51x_bulk_transport(us, lun, cmnd, 12, + buf, len, DMA_FROM_DEVICE, actlen); + if (retval != USB_STOR_TRANSPORT_GOOD) { + kfree(buf); + return -EIO; + } + + memcpy(status, buf, len); + kfree(buf); + return 0; +} + +static int rts51x_check_status(struct us_data *us, u8 lun) +{ + struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra); + int retval; + u8 buf[16]; + + retval = rts51x_read_status(us, lun, buf, 16, &(chip->status_len)); + if (retval != STATUS_SUCCESS) + return -EIO; + + usb_stor_dbg(us, "chip->status_len = %d\n", chip->status_len); + + chip->status[lun].vid = ((u16) buf[0] << 8) | buf[1]; + chip->status[lun].pid = ((u16) buf[2] << 8) | buf[3]; + chip->status[lun].cur_lun = buf[4]; + chip->status[lun].card_type = buf[5]; + chip->status[lun].total_lun = buf[6]; + chip->status[lun].fw_ver = ((u16) buf[7] << 8) | buf[8]; + chip->status[lun].phy_exist = buf[9]; + chip->status[lun].multi_flag = buf[10]; + chip->status[lun].multi_card = buf[11]; + chip->status[lun].log_exist = buf[12]; + if (chip->status_len == 16) { + chip->status[lun].detailed_type.detailed_type1 = buf[13]; + chip->status[lun].function[0] = buf[14]; + chip->status[lun].function[1] = buf[15]; + } + + return 0; +} + +static int enable_oscillator(struct us_data *us) +{ + int retval; + u8 value; + + retval = rts51x_read_mem(us, 0xFE77, &value, 1); + if (retval < 0) + return -EIO; + + value |= 0x04; + retval = rts51x_write_mem(us, 0xFE77, &value, 1); + if (retval < 0) + return -EIO; + + retval = rts51x_read_mem(us, 0xFE77, &value, 1); + if (retval < 0) + return -EIO; + + if (!(value & 0x04)) + return -EIO; + + return 0; +} + +static int __do_config_autodelink(struct us_data *us, u8 *data, u16 len) +{ + int retval; + u8 cmnd[12] = {0}; + u8 *buf; + + usb_stor_dbg(us, "addr = 0xfe47, len = %d\n", len); + + buf = kmemdup(data, len, GFP_NOIO); + if (!buf) + return USB_STOR_TRANSPORT_ERROR; + + cmnd[0] = 0xF0; + cmnd[1] = 0x0E; + cmnd[2] = 0xfe; + cmnd[3] = 0x47; + cmnd[4] = (u8)(len >> 8); + cmnd[5] = (u8)len; + + retval = rts51x_bulk_transport_special(us, 0, cmnd, 12, buf, len, DMA_TO_DEVICE, NULL); + kfree(buf); + if (retval != USB_STOR_TRANSPORT_GOOD) { + return -EIO; + } + + return 0; +} + +static int do_config_autodelink(struct us_data *us, int enable, int force) +{ + int retval; + u8 value; + + retval = rts51x_read_mem(us, 0xFE47, &value, 1); + if (retval < 0) + return -EIO; + + if (enable) { + if (force) + value |= 0x03; + else + value |= 0x01; + } else { + value &= ~0x03; + } + + usb_stor_dbg(us, "set 0xfe47 to 0x%x\n", value); + + /* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */ + retval = __do_config_autodelink(us, &value, 1); + if (retval < 0) + return -EIO; + + return 0; +} + +static int config_autodelink_after_power_on(struct us_data *us) +{ + struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra); + int retval; + u8 value; + + if (!CHK_AUTO_DELINK(chip)) + return 0; + + retval = rts51x_read_mem(us, 0xFE47, &value, 1); + if (retval < 0) + return -EIO; + + if (auto_delink_en) { + CLR_BIT(value, 0); + CLR_BIT(value, 1); + SET_BIT(value, 2); + + if (CHECK_ID(chip, 0x0138, 0x3882)) + CLR_BIT(value, 2); + + SET_BIT(value, 7); + + /* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */ + retval = __do_config_autodelink(us, &value, 1); + if (retval < 0) + return -EIO; + + retval = enable_oscillator(us); + if (retval == 0) + (void)do_config_autodelink(us, 1, 0); + } else { + /* Autodelink controlled by firmware */ + + SET_BIT(value, 2); + + if (CHECK_ID(chip, 0x0138, 0x3882)) + CLR_BIT(value, 2); + + if (CHECK_ID(chip, 0x0159, 0x5889) || + CHECK_ID(chip, 0x0138, 0x3880)) { + CLR_BIT(value, 0); + CLR_BIT(value, 7); + } + + /* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */ + retval = __do_config_autodelink(us, &value, 1); + if (retval < 0) + return -EIO; + + if (CHECK_ID(chip, 0x0159, 0x5888)) { + value = 0xFF; + retval = rts51x_write_mem(us, 0xFE79, &value, 1); + if (retval < 0) + return -EIO; + + value = 0x01; + retval = rts51x_write_mem(us, 0x48, &value, 1); + if (retval < 0) + return -EIO; + } + } + + return 0; +} + +#ifdef CONFIG_PM +static int config_autodelink_before_power_down(struct us_data *us) +{ + struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra); + int retval; + u8 value; + + if (!CHK_AUTO_DELINK(chip)) + return 0; + + if (auto_delink_en) { + retval = rts51x_read_mem(us, 0xFE77, &value, 1); + if (retval < 0) + return -EIO; + + SET_BIT(value, 2); + retval = rts51x_write_mem(us, 0xFE77, &value, 1); + if (retval < 0) + return -EIO; + + if (CHECK_ID(chip, 0x0159, 0x5888)) { + value = 0x01; + retval = rts51x_write_mem(us, 0x48, &value, 1); + if (retval < 0) + return -EIO; + } + + retval = rts51x_read_mem(us, 0xFE47, &value, 1); + if (retval < 0) + return -EIO; + + SET_BIT(value, 0); + if (CHECK_ID(chip, 0x0138, 0x3882)) + SET_BIT(value, 2); + retval = rts51x_write_mem(us, 0xFE77, &value, 1); + if (retval < 0) + return -EIO; + } else { + if (CHECK_ID(chip, 0x0159, 0x5889) || + CHECK_ID(chip, 0x0138, 0x3880) || + CHECK_ID(chip, 0x0138, 0x3882)) { + retval = rts51x_read_mem(us, 0xFE47, &value, 1); + if (retval < 0) + return -EIO; + + if (CHECK_ID(chip, 0x0159, 0x5889) || + CHECK_ID(chip, 0x0138, 0x3880)) { + SET_BIT(value, 0); + SET_BIT(value, 7); + } + + if (CHECK_ID(chip, 0x0138, 0x3882)) + SET_BIT(value, 2); + + /* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */ + retval = __do_config_autodelink(us, &value, 1); + if (retval < 0) + return -EIO; + } + + if (CHECK_ID(chip, 0x0159, 0x5888)) { + value = 0x01; + retval = rts51x_write_mem(us, 0x48, &value, 1); + if (retval < 0) + return -EIO; + } + } + + return 0; +} + +static void fw5895_init(struct us_data *us) +{ + struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra); + int retval; + u8 val; + + if ((PRODUCT_ID(chip) != 0x0158) || (FW_VERSION(chip) != 0x5895)) { + usb_stor_dbg(us, "Not the specified device, return immediately!\n"); + } else { + retval = rts51x_read_mem(us, 0xFD6F, &val, 1); + if (retval == STATUS_SUCCESS && (val & 0x1F) == 0) { + val = 0x1F; + retval = rts51x_write_mem(us, 0xFD70, &val, 1); + if (retval != STATUS_SUCCESS) + usb_stor_dbg(us, "Write memory fail\n"); + } else { + usb_stor_dbg(us, "Read memory fail, OR (val & 0x1F) != 0\n"); + } + } +} +#endif + +#ifdef CONFIG_REALTEK_AUTOPM +static void fw5895_set_mmc_wp(struct us_data *us) +{ + struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra); + int retval; + u8 buf[13]; + + if ((PRODUCT_ID(chip) != 0x0158) || (FW_VERSION(chip) != 0x5895)) { + usb_stor_dbg(us, "Not the specified device, return immediately!\n"); + } else { + retval = rts51x_read_mem(us, 0xFD6F, buf, 1); + if (retval == STATUS_SUCCESS && (buf[0] & 0x24) == 0x24) { + /* SD Exist and SD WP */ + retval = rts51x_read_mem(us, 0xD04E, buf, 1); + if (retval == STATUS_SUCCESS) { + buf[0] |= 0x04; + retval = rts51x_write_mem(us, 0xFD70, buf, 1); + if (retval != STATUS_SUCCESS) + usb_stor_dbg(us, "Write memory fail\n"); + } else { + usb_stor_dbg(us, "Read memory fail\n"); + } + } else { + usb_stor_dbg(us, "Read memory fail, OR (buf[0]&0x24)!=0x24\n"); + } + } +} + +static void rts51x_modi_suspend_timer(struct rts51x_chip *chip) +{ + struct us_data *us = chip->us; + + usb_stor_dbg(us, "state:%d\n", rts51x_get_stat(chip)); + + chip->timer_expires = jiffies + secs_to_jiffies(ss_delay); + mod_timer(&chip->rts51x_suspend_timer, chip->timer_expires); +} + +static void rts51x_suspend_timer_fn(struct timer_list *t) +{ + struct rts51x_chip *chip = timer_container_of(chip, t, + rts51x_suspend_timer); + struct us_data *us = chip->us; + + switch (rts51x_get_stat(chip)) { + case RTS51X_STAT_INIT: + case RTS51X_STAT_RUN: + rts51x_modi_suspend_timer(chip); + break; + case RTS51X_STAT_IDLE: + case RTS51X_STAT_SS: + usb_stor_dbg(us, "RTS51X_STAT_SS, power.usage:%d\n", + atomic_read(&us->pusb_intf->dev.power.usage_count)); + + if (atomic_read(&us->pusb_intf->dev.power.usage_count) > 0) { + usb_stor_dbg(us, "Ready to enter SS state\n"); + rts51x_set_stat(chip, RTS51X_STAT_SS); + /* ignore mass storage interface's children */ + pm_suspend_ignore_children(&us->pusb_intf->dev, true); + usb_autopm_put_interface_async(us->pusb_intf); + usb_stor_dbg(us, "RTS51X_STAT_SS 01, power.usage:%d\n", + atomic_read(&us->pusb_intf->dev.power.usage_count)); + } + break; + default: + usb_stor_dbg(us, "Unknown state !!!\n"); + break; + } +} + +static inline int working_scsi(struct scsi_cmnd *srb) +{ + if ((srb->cmnd[0] == TEST_UNIT_READY) || + (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL)) { + return 0; + } + + return 1; +} + +static void rts51x_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) +{ + struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra); + static int card_first_show = 1; + static const u8 media_not_present[] = { 0x70, 0, 0x02, 0, 0, 0, 0, + 10, 0, 0, 0, 0, 0x3A, 0, 0, 0, 0, 0 + }; + static const u8 invalid_cmd_field[] = { 0x70, 0, 0x05, 0, 0, 0, 0, + 10, 0, 0, 0, 0, 0x24, 0, 0, 0, 0, 0 + }; + int ret; + + if (working_scsi(srb)) { + usb_stor_dbg(us, "working scsi, power.usage:%d\n", + atomic_read(&us->pusb_intf->dev.power.usage_count)); + + if (atomic_read(&us->pusb_intf->dev.power.usage_count) <= 0) { + ret = usb_autopm_get_interface(us->pusb_intf); + usb_stor_dbg(us, "working scsi, ret=%d\n", ret); + } + if (rts51x_get_stat(chip) != RTS51X_STAT_RUN) + rts51x_set_stat(chip, RTS51X_STAT_RUN); + chip->proto_handler_backup(srb, us); + } else { + if (rts51x_get_stat(chip) == RTS51X_STAT_SS) { + usb_stor_dbg(us, "NOT working scsi\n"); + if ((srb->cmnd[0] == TEST_UNIT_READY) && + (chip->pwr_state == US_SUSPEND)) { + if (TST_LUN_READY(chip, srb->device->lun)) { + srb->result = SAM_STAT_GOOD; + } else { + srb->result = SAM_STAT_CHECK_CONDITION; + memcpy(srb->sense_buffer, + media_not_present, + US_SENSE_SIZE); + } + usb_stor_dbg(us, "TEST_UNIT_READY\n"); + goto out; + } + if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) { + int prevent = srb->cmnd[4] & 0x1; + if (prevent) { + srb->result = SAM_STAT_CHECK_CONDITION; + memcpy(srb->sense_buffer, + invalid_cmd_field, + US_SENSE_SIZE); + } else { + srb->result = SAM_STAT_GOOD; + } + usb_stor_dbg(us, "ALLOW_MEDIUM_REMOVAL\n"); + goto out; + } + } else { + usb_stor_dbg(us, "NOT working scsi, not SS\n"); + chip->proto_handler_backup(srb, us); + /* Check whether card is plugged in */ + if (srb->cmnd[0] == TEST_UNIT_READY) { + if (srb->result == SAM_STAT_GOOD) { + SET_LUN_READY(chip, srb->device->lun); + if (card_first_show) { + card_first_show = 0; + fw5895_set_mmc_wp(us); + } + } else { + CLR_LUN_READY(chip, srb->device->lun); + card_first_show = 1; + } + } + if (rts51x_get_stat(chip) != RTS51X_STAT_IDLE) + rts51x_set_stat(chip, RTS51X_STAT_IDLE); + } + } +out: + usb_stor_dbg(us, "state:%d\n", rts51x_get_stat(chip)); + if (rts51x_get_stat(chip) == RTS51X_STAT_RUN) + rts51x_modi_suspend_timer(chip); +} + +static int realtek_cr_autosuspend_setup(struct us_data *us) +{ + struct rts51x_chip *chip; + struct rts51x_status *status = NULL; + u8 buf[16]; + int retval; + + chip = (struct rts51x_chip *)us->extra; + chip->support_auto_delink = 0; + chip->pwr_state = US_RESUME; + chip->lun_ready = 0; + rts51x_set_stat(chip, RTS51X_STAT_INIT); + + retval = rts51x_read_status(us, 0, buf, 16, &(chip->status_len)); + if (retval != STATUS_SUCCESS) { + usb_stor_dbg(us, "Read status fail\n"); + return -EIO; + } + status = chip->status; + status->vid = ((u16) buf[0] << 8) | buf[1]; + status->pid = ((u16) buf[2] << 8) | buf[3]; + status->cur_lun = buf[4]; + status->card_type = buf[5]; + status->total_lun = buf[6]; + status->fw_ver = ((u16) buf[7] << 8) | buf[8]; + status->phy_exist = buf[9]; + status->multi_flag = buf[10]; + status->multi_card = buf[11]; + status->log_exist = buf[12]; + if (chip->status_len == 16) { + status->detailed_type.detailed_type1 = buf[13]; + status->function[0] = buf[14]; + status->function[1] = buf[15]; + } + + /* back up the proto_handler in us->extra */ + chip = (struct rts51x_chip *)(us->extra); + chip->proto_handler_backup = us->proto_handler; + /* Set the autosuspend_delay to 0 */ + pm_runtime_set_autosuspend_delay(&us->pusb_dev->dev, 0); + /* override us->proto_handler setted in get_protocol() */ + us->proto_handler = rts51x_invoke_transport; + + chip->timer_expires = 0; + fw5895_init(us); + + /* enable autosuspend function of the usb device */ + usb_enable_autosuspend(us->pusb_dev); + + return 0; +} +#endif + +static void realtek_cr_destructor(void *extra) +{ + struct rts51x_chip *chip = extra; + + if (!chip) + return; + +#ifdef CONFIG_REALTEK_AUTOPM + timer_shutdown_sync(&chip->rts51x_suspend_timer); +#endif + kfree(chip->status); +} + +#ifdef CONFIG_PM +static int realtek_cr_suspend(struct usb_interface *iface, pm_message_t message) +{ + struct us_data *us = usb_get_intfdata(iface); + + /* wait until no command is running */ + mutex_lock(&us->dev_mutex); + + config_autodelink_before_power_down(us); + + mutex_unlock(&us->dev_mutex); + + return 0; +} + +static int realtek_cr_resume(struct usb_interface *iface) +{ + struct us_data *us = usb_get_intfdata(iface); + + fw5895_init(us); + config_autodelink_after_power_on(us); + + return 0; +} +#else +#define realtek_cr_suspend NULL +#define realtek_cr_resume NULL +#endif + +static int init_realtek_cr(struct us_data *us) +{ + struct rts51x_chip *chip; + int size, i, retval; + + chip = kzalloc_obj(struct rts51x_chip); + if (!chip) + return -ENOMEM; + + us->extra = chip; + us->extra_destructor = realtek_cr_destructor; +#ifdef CONFIG_REALTEK_AUTOPM + timer_setup(&chip->rts51x_suspend_timer, rts51x_suspend_timer_fn, 0); +#endif + us->max_lun = chip->max_lun = rts51x_get_max_lun(us); + chip->us = us; + + usb_stor_dbg(us, "chip->max_lun = %d\n", chip->max_lun); + + size = (chip->max_lun + 1) * sizeof(struct rts51x_status); + chip->status = kzalloc(size, GFP_KERNEL); + if (!chip->status) + goto INIT_FAIL; + + for (i = 0; i <= (int)(chip->max_lun); i++) { + retval = rts51x_check_status(us, (u8) i); + if (retval < 0) + goto INIT_FAIL; + } + + if (CHECK_PID(chip, 0x0138) || CHECK_PID(chip, 0x0158) || + CHECK_PID(chip, 0x0159)) { + if (CHECK_FW_VER(chip, 0x5888) || CHECK_FW_VER(chip, 0x5889) || + CHECK_FW_VER(chip, 0x5901)) + SET_AUTO_DELINK(chip); + if (STATUS_LEN(chip) == 16) { + if (SUPPORT_AUTO_DELINK(chip)) + SET_AUTO_DELINK(chip); + } + } +#ifdef CONFIG_REALTEK_AUTOPM + if (ss_en) + realtek_cr_autosuspend_setup(us); +#endif + + usb_stor_dbg(us, "chip->flag = 0x%x\n", chip->flag); + + (void)config_autodelink_after_power_on(us); + + return 0; + +INIT_FAIL: + if (us->extra) { + kfree(chip->status); + kfree(us->extra); + us->extra = NULL; + } + + return -EIO; +} + +static struct scsi_host_template realtek_cr_host_template; + +static int realtek_cr_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct us_data *us; + int result; + + dev_dbg(&intf->dev, "Probe Realtek Card Reader!\n"); + + result = usb_stor_probe1(&us, intf, id, + (id - realtek_cr_ids) + + realtek_cr_unusual_dev_list, + &realtek_cr_host_template); + if (result) + return result; + + result = usb_stor_probe2(us); + + return result; +} + +static struct usb_driver realtek_cr_driver = { + .name = DRV_NAME, + .probe = realtek_cr_probe, + .disconnect = usb_stor_disconnect, + /* .suspend = usb_stor_suspend, */ + /* .resume = usb_stor_resume, */ + .reset_resume = usb_stor_reset_resume, + .suspend = realtek_cr_suspend, + .resume = realtek_cr_resume, + .pre_reset = usb_stor_pre_reset, + .post_reset = usb_stor_post_reset, + .id_table = realtek_cr_ids, + .soft_unbind = 1, + .supports_autosuspend = 1, + .no_dynamic_id = 1, +}; + +module_usb_stor_driver(realtek_cr_driver, realtek_cr_host_template, DRV_NAME); diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c new file mode 100644 index 000000000..97de28e85 --- /dev/null +++ b/drivers/usb/storage/scsiglue.c @@ -0,0 +1,699 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for USB Mass Storage compliant devices + * SCSI layer glue code + * + * Current development and maintenance by: + * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * Developed with the assistance of: + * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org) + * (c) 2000 Stephen J. Gowdy (SGowdy@lbl.gov) + * + * Initial work by: + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "usb.h" +#include "scsiglue.h" +#include "debug.h" +#include "transport.h" +#include "protocol.h" + +/* + * Vendor IDs for companies that seem to include the READ CAPACITY bug + * in all their devices + */ +#define VENDOR_ID_NOKIA 0x0421 +#define VENDOR_ID_NIKON 0x04b0 +#define VENDOR_ID_PENTAX 0x0a17 +#define VENDOR_ID_MOTOROLA 0x22b8 + +/*********************************************************************** + * Host functions + ***********************************************************************/ + +static const char* host_info(struct Scsi_Host *host) +{ + struct us_data *us = host_to_us(host); + return us->scsi_name; +} + +static int sdev_init (struct scsi_device *sdev) +{ + struct us_data *us = host_to_us(sdev->host); + + /* + * Set the INQUIRY transfer length to 36. We don't use any of + * the extra data and many devices choke if asked for more or + * less than 36 bytes. + */ + sdev->inquiry_len = 36; + + /* Tell the SCSI layer if we know there is more than one LUN */ + if (us->protocol == USB_PR_BULK && us->max_lun > 0) + sdev->sdev_bflags |= BLIST_FORCELUN; + + /* + * Some USB storage devices reset if the IO advice hints grouping mode + * page is queried. Hence skip that mode page. + */ + sdev->sdev_bflags |= BLIST_SKIP_IO_HINTS; + + return 0; +} + +static int sdev_configure(struct scsi_device *sdev, struct queue_limits *lim) +{ + struct us_data *us = host_to_us(sdev->host); + struct device *dev = us->pusb_dev->bus->sysdev; + + /* + * Many devices have trouble transferring more than 32KB at a time, + * while others have trouble with more than 64K. At this time we + * are limiting both to 32K (64 sectores). + */ + if (us->fflags & (US_FL_MAX_SECTORS_64 | US_FL_MAX_SECTORS_MIN)) { + unsigned int max_sectors = 64; + + if (us->fflags & US_FL_MAX_SECTORS_MIN) + max_sectors = PAGE_SIZE >> 9; + lim->max_hw_sectors = min(lim->max_hw_sectors, max_sectors); + } else if (sdev->type == TYPE_TAPE) { + /* + * Tapes need much higher max_sector limits, so just + * raise it to the maximum possible (4 GB / 512) and + * let the queue segment size sort out the real limit. + */ + lim->max_hw_sectors = 0x7FFFFF; + } else if (us->pusb_dev->speed >= USB_SPEED_SUPER) { + /* + * USB3 devices will be limited to 2048 sectors. This gives us + * better throughput on most devices. + */ + lim->max_hw_sectors = 2048; + } + + /* + * The max_hw_sectors should be up to maximum size of a mapping for + * the device. Otherwise, a DMA API might fail on swiotlb environment. + */ + lim->max_hw_sectors = min_t(size_t, + lim->max_hw_sectors, dma_max_mapping_size(dev) >> SECTOR_SHIFT); + + /* + * We can't put these settings in sdev_init() because that gets + * called before the device type is known. Consequently these + * settings can't be overridden via the scsi devinfo mechanism. + */ + if (sdev->type == TYPE_DISK) { + + /* + * Some vendors seem to put the READ CAPACITY bug into + * all their devices -- primarily makers of cell phones + * and digital cameras. Since these devices always use + * flash media and can be expected to have an even number + * of sectors, we will always enable the CAPACITY_HEURISTICS + * flag unless told otherwise. + */ + switch (le16_to_cpu(us->pusb_dev->descriptor.idVendor)) { + case VENDOR_ID_NOKIA: + case VENDOR_ID_NIKON: + case VENDOR_ID_PENTAX: + case VENDOR_ID_MOTOROLA: + if (!(us->fflags & (US_FL_FIX_CAPACITY | + US_FL_CAPACITY_OK))) + us->fflags |= US_FL_CAPACITY_HEURISTICS; + break; + } + + /* + * Disk-type devices use MODE SENSE(6) if the protocol + * (SubClass) is Transparent SCSI, otherwise they use + * MODE SENSE(10). + */ + if (us->subclass != USB_SC_SCSI && us->subclass != USB_SC_CYP_ATACB) + sdev->use_10_for_ms = 1; + + /* + *Many disks only accept MODE SENSE transfer lengths of + * 192 bytes (that's what Windows uses). + */ + sdev->use_192_bytes_for_3f = 1; + + /* + * Some devices report generic values until the media has been + * accessed. Force a READ(10) prior to querying device + * characteristics. + */ + sdev->read_before_ms = 1; + + /* + * Some devices don't like MODE SENSE with page=0x3f, + * which is the command used for checking if a device + * is write-protected. Now that we tell the sd driver + * to do a 192-byte transfer with this command the + * majority of devices work fine, but a few still can't + * handle it. The sd driver will simply assume those + * devices are write-enabled. + */ + if (us->fflags & US_FL_NO_WP_DETECT) + sdev->skip_ms_page_3f = 1; + + /* + * A number of devices have problems with MODE SENSE for + * page x08, so we will skip it. + */ + sdev->skip_ms_page_8 = 1; + + /* + * Some devices don't handle VPD pages correctly, so skip vpd + * pages if not forced by SCSI layer. + */ + sdev->skip_vpd_pages = !sdev->try_vpd_pages; + + /* Do not attempt to use REPORT SUPPORTED OPERATION CODES */ + sdev->no_report_opcodes = 1; + + /* Do not attempt to use WRITE SAME */ + sdev->no_write_same = 1; + + /* + * Some disks return the total number of blocks in response + * to READ CAPACITY rather than the highest block number. + * If this device makes that mistake, tell the sd driver. + */ + if (us->fflags & US_FL_FIX_CAPACITY) + sdev->fix_capacity = 1; + + /* + * A few disks have two indistinguishable version, one of + * which reports the correct capacity and the other does not. + * The sd driver has to guess which is the case. + */ + if (us->fflags & US_FL_CAPACITY_HEURISTICS) + sdev->guess_capacity = 1; + + /* Some devices cannot handle READ_CAPACITY_16 */ + if (us->fflags & US_FL_NO_READ_CAPACITY_16) + sdev->no_read_capacity_16 = 1; + + /* + * Many devices do not respond properly to READ_CAPACITY_16. + * Tell the SCSI layer to try READ_CAPACITY_10 first. + * However some USB 3.0 drive enclosures return capacity + * modulo 2TB. Those must use READ_CAPACITY_16 + */ + if (!(us->fflags & US_FL_NEEDS_CAP16)) + sdev->try_rc_10_first = 1; + + /* + * assume SPC3 or latter devices support sense size > 18 + * unless US_FL_BAD_SENSE quirk is specified. + */ + if (sdev->scsi_level > SCSI_SPC_2 && + !(us->fflags & US_FL_BAD_SENSE)) + us->fflags |= US_FL_SANE_SENSE; + + /* + * USB-IDE bridges tend to report SK = 0x04 (Non-recoverable + * Hardware Error) when any low-level error occurs, + * recoverable or not. Setting this flag tells the SCSI + * midlayer to retry such commands, which frequently will + * succeed and fix the error. The worst this can lead to + * is an occasional series of retries that will all fail. + */ + sdev->retry_hwerror = 1; + + /* + * USB disks should allow restart. Some drives spin down + * automatically, requiring a START-STOP UNIT command. + */ + sdev->allow_restart = 1; + + /* + * Some USB cardreaders have trouble reading an sdcard's last + * sector in a larger then 1 sector read, since the performance + * impact is negligible we set this flag for all USB disks + */ + sdev->last_sector_bug = 1; + + /* + * Enable last-sector hacks for single-target devices using + * the Bulk-only transport, unless we already know the + * capacity will be decremented or is correct. + */ + if (!(us->fflags & (US_FL_FIX_CAPACITY | US_FL_CAPACITY_OK | + US_FL_SCM_MULT_TARG)) && + us->protocol == USB_PR_BULK) + us->use_last_sector_hacks = 1; + + /* Check if write cache default on flag is set or not */ + if (us->fflags & US_FL_WRITE_CACHE) + sdev->wce_default_on = 1; + + /* A few buggy USB-ATA bridges don't understand FUA */ + if (us->fflags & US_FL_BROKEN_FUA) + sdev->broken_fua = 1; + + /* Some even totally fail to indicate a cache */ + if (us->fflags & US_FL_ALWAYS_SYNC) { + /* don't read caching information */ + sdev->skip_ms_page_8 = 1; + sdev->skip_ms_page_3f = 1; + /* assume sync is needed */ + sdev->wce_default_on = 1; + } + } else { + + /* + * Non-disk-type devices don't need to ignore any pages + * or to force 192-byte transfer lengths for MODE SENSE. + * But they do need to use MODE SENSE(10). + */ + sdev->use_10_for_ms = 1; + + /* Some (fake) usb cdrom devices don't like READ_DISC_INFO */ + if (us->fflags & US_FL_NO_READ_DISC_INFO) + sdev->no_read_disc_info = 1; + } + + /* + * The CB and CBI transports have no way to pass LUN values + * other than the bits in the second byte of a CDB. But those + * bits don't get set to the LUN value if the device reports + * scsi_level == 0 (UNKNOWN). Hence such devices must necessarily + * be single-LUN. + */ + if ((us->protocol == USB_PR_CB || us->protocol == USB_PR_CBI) && + sdev->scsi_level == SCSI_UNKNOWN) + us->max_lun = 0; + + /* + * Some devices choke when they receive a PREVENT-ALLOW MEDIUM + * REMOVAL command, so suppress those commands. + */ + if (us->fflags & US_FL_NOT_LOCKABLE) + sdev->lockable = 0; + + /* + * this is to satisfy the compiler, tho I don't think the + * return code is ever checked anywhere. + */ + return 0; +} + +static int target_alloc(struct scsi_target *starget) +{ + struct us_data *us = host_to_us(dev_to_shost(starget->dev.parent)); + + /* + * Some USB drives don't support REPORT LUNS, even though they + * report a SCSI revision level above 2. Tell the SCSI layer + * not to issue that command; it will perform a normal sequential + * scan instead. + */ + starget->no_report_luns = 1; + + /* + * The UFI spec treats the Peripheral Qualifier bits in an + * INQUIRY result as reserved and requires devices to set them + * to 0. However the SCSI spec requires these bits to be set + * to 3 to indicate when a LUN is not present. + * + * Let the scanning code know if this target merely sets + * Peripheral Device Type to 0x1f to indicate no LUN. + */ + if (us->subclass == USB_SC_UFI) + starget->pdt_1f_for_no_lun = 1; + + return 0; +} + +/* queue a command */ +/* This is always called with scsi_lock(host) held */ +static enum scsi_qc_status queuecommand_lck(struct scsi_cmnd *srb) +{ + void (*done)(struct scsi_cmnd *) = scsi_done; + struct us_data *us = host_to_us(srb->device->host); + + /* check for state-transition errors */ + if (us->srb != NULL) { + dev_err(&us->pusb_intf->dev, + "Error in %s: us->srb = %p\n", __func__, us->srb); + return SCSI_MLQUEUE_HOST_BUSY; + } + + /* fail the command if we are disconnecting */ + if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) { + usb_stor_dbg(us, "Fail command during disconnect\n"); + srb->result = DID_NO_CONNECT << 16; + done(srb); + return 0; + } + + if ((us->fflags & US_FL_NO_ATA_1X) && + (srb->cmnd[0] == ATA_12 || srb->cmnd[0] == ATA_16)) { + memcpy(srb->sense_buffer, usb_stor_sense_invalidCDB, + sizeof(usb_stor_sense_invalidCDB)); + srb->result = SAM_STAT_CHECK_CONDITION; + done(srb); + return 0; + } + + /* enqueue the command and wake up the control thread */ + us->srb = srb; + complete(&us->cmnd_ready); + + return 0; +} + +static DEF_SCSI_QCMD(queuecommand) + +/*********************************************************************** + * Error handling functions + ***********************************************************************/ + +/* Command timeout and abort */ +static int command_abort_matching(struct us_data *us, struct scsi_cmnd *srb_match) +{ + /* + * us->srb together with the TIMED_OUT, RESETTING, and ABORTING + * bits are protected by the host lock. + */ + scsi_lock(us_to_host(us)); + + /* is there any active pending command to abort ? */ + if (!us->srb) { + scsi_unlock(us_to_host(us)); + usb_stor_dbg(us, "-- nothing to abort\n"); + return SUCCESS; + } + + /* Does the command match the passed srb if any ? */ + if (srb_match && us->srb != srb_match) { + scsi_unlock(us_to_host(us)); + usb_stor_dbg(us, "-- pending command mismatch\n"); + return FAILED; + } + + /* + * Set the TIMED_OUT bit. Also set the ABORTING bit, but only if + * a device reset isn't already in progress (to avoid interfering + * with the reset). Note that we must retain the host lock while + * calling usb_stor_stop_transport(); otherwise it might interfere + * with an auto-reset that begins as soon as we release the lock. + */ + set_bit(US_FLIDX_TIMED_OUT, &us->dflags); + if (!test_bit(US_FLIDX_RESETTING, &us->dflags)) { + set_bit(US_FLIDX_ABORTING, &us->dflags); + usb_stor_stop_transport(us); + } + scsi_unlock(us_to_host(us)); + + /* Wait for the aborted command to finish */ + wait_for_completion(&us->notify); + return SUCCESS; +} + +static int command_abort(struct scsi_cmnd *srb) +{ + struct us_data *us = host_to_us(srb->device->host); + + usb_stor_dbg(us, "%s called\n", __func__); + return command_abort_matching(us, srb); +} + +/* + * This invokes the transport reset mechanism to reset the state of the + * device + */ +static int device_reset(struct scsi_cmnd *srb) +{ + struct us_data *us = host_to_us(srb->device->host); + int result; + + usb_stor_dbg(us, "%s called\n", __func__); + + /* abort any pending command before reset */ + command_abort_matching(us, NULL); + + /* lock the device pointers and do the reset */ + mutex_lock(&(us->dev_mutex)); + result = us->transport_reset(us); + mutex_unlock(&us->dev_mutex); + + return result < 0 ? FAILED : SUCCESS; +} + +/* Simulate a SCSI bus reset by resetting the device's USB port. */ +static int bus_reset(struct scsi_cmnd *srb) +{ + struct us_data *us = host_to_us(srb->device->host); + int result; + + usb_stor_dbg(us, "%s called\n", __func__); + + result = usb_stor_port_reset(us); + return result < 0 ? FAILED : SUCCESS; +} + +/* + * Report a driver-initiated device reset to the SCSI layer. + * Calling this for a SCSI-initiated reset is unnecessary but harmless. + * The caller must own the SCSI host lock. + */ +void usb_stor_report_device_reset(struct us_data *us) +{ + int i; + struct Scsi_Host *host = us_to_host(us); + + scsi_report_device_reset(host, 0, 0); + if (us->fflags & US_FL_SCM_MULT_TARG) { + for (i = 1; i < host->max_id; ++i) + scsi_report_device_reset(host, 0, i); + } +} + +/* + * Report a driver-initiated bus reset to the SCSI layer. + * Calling this for a SCSI-initiated reset is unnecessary but harmless. + * The caller must not own the SCSI host lock. + */ +void usb_stor_report_bus_reset(struct us_data *us) +{ + struct Scsi_Host *host = us_to_host(us); + + scsi_lock(host); + scsi_report_bus_reset(host, 0); + scsi_unlock(host); +} + +/*********************************************************************** + * /proc/scsi/ functions + ***********************************************************************/ + +static int write_info(struct Scsi_Host *host, char *buffer, int length) +{ + /* if someone is sending us data, just throw it away */ + return length; +} + +static int show_info (struct seq_file *m, struct Scsi_Host *host) +{ + struct us_data *us = host_to_us(host); + const char *string; + + /* print the controller name */ + seq_printf(m, " Host scsi%d: usb-storage\n", host->host_no); + + /* print product, vendor, and serial number strings */ + if (us->pusb_dev->manufacturer) + string = us->pusb_dev->manufacturer; + else if (us->unusual_dev->vendorName) + string = us->unusual_dev->vendorName; + else + string = "Unknown"; + seq_printf(m, " Vendor: %s\n", string); + if (us->pusb_dev->product) + string = us->pusb_dev->product; + else if (us->unusual_dev->productName) + string = us->unusual_dev->productName; + else + string = "Unknown"; + seq_printf(m, " Product: %s\n", string); + if (us->pusb_dev->serial) + string = us->pusb_dev->serial; + else + string = "None"; + seq_printf(m, "Serial Number: %s\n", string); + + /* show the protocol and transport */ + seq_printf(m, " Protocol: %s\n", us->protocol_name); + seq_printf(m, " Transport: %s\n", us->transport_name); + + /* show the device flags */ + seq_printf(m, " Quirks:"); + +#define US_FLAG(name, value) \ + if (us->fflags & value) seq_printf(m, " " #name); +US_DO_ALL_FLAGS +#undef US_FLAG + seq_putc(m, '\n'); + return 0; +} + +/*********************************************************************** + * Sysfs interface + ***********************************************************************/ + +/* Output routine for the sysfs max_sectors file */ +static ssize_t max_sectors_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct scsi_device *sdev = to_scsi_device(dev); + + return sprintf(buf, "%u\n", queue_max_hw_sectors(sdev->request_queue)); +} + +/* Input routine for the sysfs max_sectors file */ +static ssize_t max_sectors_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct queue_limits lim; + unsigned short ms; + int ret; + + if (sscanf(buf, "%hu", &ms) <= 0) + return -EINVAL; + + lim = queue_limits_start_update(sdev->request_queue); + lim.max_hw_sectors = ms; + ret = queue_limits_commit_update_frozen(sdev->request_queue, &lim); + if (ret) + return ret; + return count; +} +static DEVICE_ATTR_RW(max_sectors); + +static struct attribute *usb_sdev_attrs[] = { + &dev_attr_max_sectors.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(usb_sdev); + +/* + * this defines our host template, with which we'll allocate hosts + */ + +static const struct scsi_host_template usb_stor_host_template = { + /* basic userland interface stuff */ + .name = "usb-storage", + .proc_name = "usb-storage", + .show_info = show_info, + .write_info = write_info, + .info = host_info, + + /* command interface -- queued only */ + .queuecommand = queuecommand, + + /* error and abort handlers */ + .eh_abort_handler = command_abort, + .eh_device_reset_handler = device_reset, + .eh_bus_reset_handler = bus_reset, + + /* queue commands only, only one command per LUN */ + .can_queue = 1, + + /* unknown initiator id */ + .this_id = -1, + + .sdev_init = sdev_init, + .sdev_configure = sdev_configure, + .target_alloc = target_alloc, + + /* lots of sg segments can be handled */ + .sg_tablesize = SG_MAX_SEGMENTS, + + /* + * Some host controllers may have alignment requirements. + * We'll play it safe by requiring 512-byte alignment always. + */ + .dma_alignment = 511, + + /* + * Limit the total size of a transfer to 120 KB. + * + * Some devices are known to choke with anything larger. It seems like + * the problem stems from the fact that original IDE controllers had + * only an 8-bit register to hold the number of sectors in one transfer + * and even those couldn't handle a full 256 sectors. + * + * Because we want to make sure we interoperate with as many devices as + * possible, we will maintain a 240 sector transfer size limit for USB + * Mass Storage devices. + * + * Tests show that other operating have similar limits with Microsoft + * Windows 7 limiting transfers to 128 sectors for both USB2 and USB3 + * and Apple Mac OS X 10.11 limiting transfers to 256 sectors for USB2 + * and 2048 for USB3 devices. + */ + .max_sectors = 240, + + /* emulated HBA */ + .emulated = 1, + + /* we do our own delay after a device or bus reset */ + .skip_settle_delay = 1, + + /* sysfs device attributes */ + .sdev_groups = usb_sdev_groups, + + /* module management */ + .module = THIS_MODULE +}; + +void usb_stor_host_template_init(struct scsi_host_template *sht, + const char *name, struct module *owner) +{ + *sht = usb_stor_host_template; + sht->name = name; + sht->proc_name = name; + sht->module = owner; +} +EXPORT_SYMBOL_GPL(usb_stor_host_template_init); + +/* To Report "Illegal Request: Invalid Field in CDB */ +unsigned char usb_stor_sense_invalidCDB[18] = { + [0] = 0x70, /* current error */ + [2] = ILLEGAL_REQUEST, /* Illegal Request = 0x05 */ + [7] = 0x0a, /* additional length */ + [12] = 0x24 /* Invalid Field in CDB */ +}; +EXPORT_SYMBOL_GPL(usb_stor_sense_invalidCDB); diff --git a/drivers/usb/storage/scsiglue.h b/drivers/usb/storage/scsiglue.h new file mode 100644 index 000000000..2a79c3ed4 --- /dev/null +++ b/drivers/usb/storage/scsiglue.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Driver for USB Mass Storage compliant devices + * SCSI Connecting Glue Header File + * + * Current development and maintenance by: + * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + */ + +#ifndef _SCSIGLUE_H_ +#define _SCSIGLUE_H_ + +extern void usb_stor_report_device_reset(struct us_data *us); +extern void usb_stor_report_bus_reset(struct us_data *us); +extern void usb_stor_host_template_init(struct scsi_host_template *sht, + const char *name, struct module *owner); + +extern unsigned char usb_stor_sense_invalidCDB[18]; + +#endif diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c new file mode 100644 index 000000000..3d45e1b54 --- /dev/null +++ b/drivers/usb/storage/sddr09.c @@ -0,0 +1,1790 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for SanDisk SDDR-09 SmartMedia reader + * + * (c) 2000, 2001 Robert Baruch (autophile@starband.net) + * (c) 2002 Andries Brouwer (aeb@cwi.nl) + * Developed with the assistance of: + * (c) 2002 Alan Stern + * + * The SanDisk SDDR-09 SmartMedia reader uses the Shuttle EUSB-01 chip. + * This chip is a programmable USB controller. In the SDDR-09, it has + * been programmed to obey a certain limited set of SCSI commands. + * This driver translates the "real" SCSI commands to the SDDR-09 SCSI + * commands. + */ + +/* + * Known vendor commands: 12 bytes, first byte is opcode + * + * E7: read scatter gather + * E8: read + * E9: write + * EA: erase + * EB: reset + * EC: read status + * ED: read ID + * EE: write CIS (?) + * EF: compute checksum (?) + */ + +#include +#include +#include + +#include +#include +#include + +#include "usb.h" +#include "transport.h" +#include "protocol.h" +#include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-sddr09" + +MODULE_DESCRIPTION("Driver for SanDisk SDDR-09 SmartMedia reader"); +MODULE_AUTHOR("Andries Brouwer , Robert Baruch "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("USB_STORAGE"); + +static int usb_stor_sddr09_dpcm_init(struct us_data *us); +static int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us); +static int usb_stor_sddr09_init(struct us_data *us); + + +/* + * The table of devices + */ +#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ + vendorName, productName, useProtocol, useTransport, \ + initFunction, flags) \ +{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ + .driver_info = (flags) } + +static const struct usb_device_id sddr09_usb_ids[] = { +# include "unusual_sddr09.h" + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, sddr09_usb_ids); + +#undef UNUSUAL_DEV + +/* + * The flags table + */ +#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ + vendor_name, product_name, use_protocol, use_transport, \ + init_function, Flags) \ +{ \ + .vendorName = vendor_name, \ + .productName = product_name, \ + .useProtocol = use_protocol, \ + .useTransport = use_transport, \ + .initFunction = init_function, \ +} + +static const struct us_unusual_dev sddr09_unusual_dev_list[] = { +# include "unusual_sddr09.h" + { } /* Terminating entry */ +}; + +#undef UNUSUAL_DEV + + +#define short_pack(lsb,msb) ( ((u16)(lsb)) | ( ((u16)(msb))<<8 ) ) +#define LSB_of(s) ((s)&0xFF) +#define MSB_of(s) ((s)>>8) + +/* + * First some stuff that does not belong here: + * data on SmartMedia and other cards, completely + * unrelated to this driver. + * Similar stuff occurs in . + */ + +struct nand_flash_dev { + int model_id; + int chipshift; /* 1<recv_ctrl_pipe; + else + pipe = us->send_ctrl_pipe; + + rc = usb_stor_ctrl_transfer(us, pipe, request, requesttype, + 0, 0, xfer_data, xfer_len); + switch (rc) { + case USB_STOR_XFER_GOOD: return 0; + case USB_STOR_XFER_STALLED: return -EPIPE; + default: return -EIO; + } +} + +static int +sddr09_send_scsi_command(struct us_data *us, + unsigned char *command, + unsigned int command_len) { + return sddr09_send_command(us, 0, USB_DIR_OUT, command, command_len); +} + +#if 0 +/* + * Test Unit Ready Command: 12 bytes. + * byte 0: opcode: 00 + */ +static int +sddr09_test_unit_ready(struct us_data *us) { + unsigned char *command = us->iobuf; + int result; + + memset(command, 0, 6); + command[1] = LUNBITS; + + result = sddr09_send_scsi_command(us, command, 6); + + usb_stor_dbg(us, "sddr09_test_unit_ready returns %d\n", result); + + return result; +} +#endif + +/* + * Request Sense Command: 12 bytes. + * byte 0: opcode: 03 + * byte 4: data length + */ +static int +sddr09_request_sense(struct us_data *us, unsigned char *sensebuf, int buflen) { + unsigned char *command = us->iobuf; + int result; + + memset(command, 0, 12); + command[0] = 0x03; + command[1] = LUNBITS; + command[4] = buflen; + + result = sddr09_send_scsi_command(us, command, 12); + if (result) + return result; + + result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + sensebuf, buflen, NULL); + return (result == USB_STOR_XFER_GOOD ? 0 : -EIO); +} + +/* + * Read Command: 12 bytes. + * byte 0: opcode: E8 + * byte 1: last two bits: 00: read data, 01: read blockwise control, + * 10: read both, 11: read pagewise control. + * It turns out we need values 20, 21, 22, 23 here (LUN 1). + * bytes 2-5: address (interpretation depends on byte 1, see below) + * bytes 10-11: count (idem) + * + * A page has 512 data bytes and 64 control bytes (16 control and 48 junk). + * A read data command gets data in 512-byte pages. + * A read control command gets control in 64-byte chunks. + * A read both command gets data+control in 576-byte chunks. + * + * Blocks are groups of 32 pages, and read blockwise control jumps to the + * next block, while read pagewise control jumps to the next page after + * reading a group of 64 control bytes. + * [Here 512 = 1<iobuf; + int result; + + command[0] = 0xE8; + command[1] = LUNBITS | x; + command[2] = MSB_of(fromaddress>>16); + command[3] = LSB_of(fromaddress>>16); + command[4] = MSB_of(fromaddress & 0xFFFF); + command[5] = LSB_of(fromaddress & 0xFFFF); + command[6] = 0; + command[7] = 0; + command[8] = 0; + command[9] = 0; + command[10] = MSB_of(nr_of_pages); + command[11] = LSB_of(nr_of_pages); + + result = sddr09_send_scsi_command(us, command, 12); + + if (result) { + usb_stor_dbg(us, "Result for send_control in sddr09_read2%d %d\n", + x, result); + return result; + } + + result = usb_stor_bulk_transfer_sg(us, us->recv_bulk_pipe, + buf, bulklen, use_sg, NULL); + + if (result != USB_STOR_XFER_GOOD) { + usb_stor_dbg(us, "Result for bulk_transfer in sddr09_read2%d %d\n", + x, result); + return -EIO; + } + return 0; +} + +/* + * Read Data + * + * fromaddress counts data shorts: + * increasing it by 256 shifts the bytestream by 512 bytes; + * the last 8 bits are ignored. + * + * nr_of_pages counts pages of size (1 << pageshift). + */ +static int +sddr09_read20(struct us_data *us, unsigned long fromaddress, + int nr_of_pages, int pageshift, unsigned char *buf, int use_sg) { + int bulklen = nr_of_pages << pageshift; + + /* The last 8 bits of fromaddress are ignored. */ + return sddr09_readX(us, 0, fromaddress, nr_of_pages, bulklen, + buf, use_sg); +} + +/* + * Read Blockwise Control + * + * fromaddress gives the starting position (as in read data; + * the last 8 bits are ignored); increasing it by 32*256 shifts + * the output stream by 64 bytes. + * + * count counts control groups of size (1 << controlshift). + * For me, controlshift = 6. Is this constant? + * + * After getting one control group, jump to the next block + * (fromaddress += 8192). + */ +static int +sddr09_read21(struct us_data *us, unsigned long fromaddress, + int count, int controlshift, unsigned char *buf, int use_sg) { + + int bulklen = (count << controlshift); + return sddr09_readX(us, 1, fromaddress, count, bulklen, + buf, use_sg); +} + +/* + * Read both Data and Control + * + * fromaddress counts data shorts, ignoring control: + * increasing it by 256 shifts the bytestream by 576 = 512+64 bytes; + * the last 8 bits are ignored. + * + * nr_of_pages counts pages of size (1 << pageshift) + (1 << controlshift). + */ +static int +sddr09_read22(struct us_data *us, unsigned long fromaddress, + int nr_of_pages, int pageshift, unsigned char *buf, int use_sg) { + + int bulklen = (nr_of_pages << pageshift) + (nr_of_pages << CONTROL_SHIFT); + usb_stor_dbg(us, "reading %d pages, %d bytes\n", nr_of_pages, bulklen); + return sddr09_readX(us, 2, fromaddress, nr_of_pages, bulklen, + buf, use_sg); +} + +#if 0 +/* + * Read Pagewise Control + * + * fromaddress gives the starting position (as in read data; + * the last 8 bits are ignored); increasing it by 256 shifts + * the output stream by 64 bytes. + * + * count counts control groups of size (1 << controlshift). + * For me, controlshift = 6. Is this constant? + * + * After getting one control group, jump to the next page + * (fromaddress += 256). + */ +static int +sddr09_read23(struct us_data *us, unsigned long fromaddress, + int count, int controlshift, unsigned char *buf, int use_sg) { + + int bulklen = (count << controlshift); + return sddr09_readX(us, 3, fromaddress, count, bulklen, + buf, use_sg); +} +#endif + +/* + * Erase Command: 12 bytes. + * byte 0: opcode: EA + * bytes 6-9: erase address (big-endian, counting shorts, sector aligned). + * + * Always precisely one block is erased; bytes 2-5 and 10-11 are ignored. + * The byte address being erased is 2*Eaddress. + * The CIS cannot be erased. + */ +static int +sddr09_erase(struct us_data *us, unsigned long Eaddress) { + unsigned char *command = us->iobuf; + int result; + + usb_stor_dbg(us, "erase address %lu\n", Eaddress); + + memset(command, 0, 12); + command[0] = 0xEA; + command[1] = LUNBITS; + command[6] = MSB_of(Eaddress>>16); + command[7] = LSB_of(Eaddress>>16); + command[8] = MSB_of(Eaddress & 0xFFFF); + command[9] = LSB_of(Eaddress & 0xFFFF); + + result = sddr09_send_scsi_command(us, command, 12); + + if (result) + usb_stor_dbg(us, "Result for send_control in sddr09_erase %d\n", + result); + + return result; +} + +/* + * Write CIS Command: 12 bytes. + * byte 0: opcode: EE + * bytes 2-5: write address in shorts + * bytes 10-11: sector count + * + * This writes at the indicated address. Don't know how it differs + * from E9. Maybe it does not erase? However, it will also write to + * the CIS. + * + * When two such commands on the same page follow each other directly, + * the second one is not done. + */ + +/* + * Write Command: 12 bytes. + * byte 0: opcode: E9 + * bytes 2-5: write address (big-endian, counting shorts, sector aligned). + * bytes 6-9: erase address (big-endian, counting shorts, sector aligned). + * bytes 10-11: sector count (big-endian, in 512-byte sectors). + * + * If write address equals erase address, the erase is done first, + * otherwise the write is done first. When erase address equals zero + * no erase is done? + */ +static int +sddr09_writeX(struct us_data *us, + unsigned long Waddress, unsigned long Eaddress, + int nr_of_pages, int bulklen, unsigned char *buf, int use_sg) { + + unsigned char *command = us->iobuf; + int result; + + command[0] = 0xE9; + command[1] = LUNBITS; + + command[2] = MSB_of(Waddress>>16); + command[3] = LSB_of(Waddress>>16); + command[4] = MSB_of(Waddress & 0xFFFF); + command[5] = LSB_of(Waddress & 0xFFFF); + + command[6] = MSB_of(Eaddress>>16); + command[7] = LSB_of(Eaddress>>16); + command[8] = MSB_of(Eaddress & 0xFFFF); + command[9] = LSB_of(Eaddress & 0xFFFF); + + command[10] = MSB_of(nr_of_pages); + command[11] = LSB_of(nr_of_pages); + + result = sddr09_send_scsi_command(us, command, 12); + + if (result) { + usb_stor_dbg(us, "Result for send_control in sddr09_writeX %d\n", + result); + return result; + } + + result = usb_stor_bulk_transfer_sg(us, us->send_bulk_pipe, + buf, bulklen, use_sg, NULL); + + if (result != USB_STOR_XFER_GOOD) { + usb_stor_dbg(us, "Result for bulk_transfer in sddr09_writeX %d\n", + result); + return -EIO; + } + return 0; +} + +/* erase address, write same address */ +static int +sddr09_write_inplace(struct us_data *us, unsigned long address, + int nr_of_pages, int pageshift, unsigned char *buf, + int use_sg) { + int bulklen = (nr_of_pages << pageshift) + (nr_of_pages << CONTROL_SHIFT); + return sddr09_writeX(us, address, address, nr_of_pages, bulklen, + buf, use_sg); +} + +#if 0 +/* + * Read Scatter Gather Command: 3+4n bytes. + * byte 0: opcode E7 + * byte 2: n + * bytes 4i-1,4i,4i+1: page address + * byte 4i+2: page count + * (i=1..n) + * + * This reads several pages from the card to a single memory buffer. + * The last two bits of byte 1 have the same meaning as for E8. + */ +static int +sddr09_read_sg_test_only(struct us_data *us) { + unsigned char *command = us->iobuf; + int result, bulklen, nsg, ct; + unsigned char *buf; + unsigned long address; + + nsg = bulklen = 0; + command[0] = 0xE7; + command[1] = LUNBITS; + command[2] = 0; + address = 040000; ct = 1; + nsg++; + bulklen += (ct << 9); + command[4*nsg+2] = ct; + command[4*nsg+1] = ((address >> 9) & 0xFF); + command[4*nsg+0] = ((address >> 17) & 0xFF); + command[4*nsg-1] = ((address >> 25) & 0xFF); + + address = 0340000; ct = 1; + nsg++; + bulklen += (ct << 9); + command[4*nsg+2] = ct; + command[4*nsg+1] = ((address >> 9) & 0xFF); + command[4*nsg+0] = ((address >> 17) & 0xFF); + command[4*nsg-1] = ((address >> 25) & 0xFF); + + address = 01000000; ct = 2; + nsg++; + bulklen += (ct << 9); + command[4*nsg+2] = ct; + command[4*nsg+1] = ((address >> 9) & 0xFF); + command[4*nsg+0] = ((address >> 17) & 0xFF); + command[4*nsg-1] = ((address >> 25) & 0xFF); + + command[2] = nsg; + + result = sddr09_send_scsi_command(us, command, 4*nsg+3); + + if (result) { + usb_stor_dbg(us, "Result for send_control in sddr09_read_sg %d\n", + result); + return result; + } + + buf = kmalloc(bulklen, GFP_NOIO); + if (!buf) + return -ENOMEM; + + result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + buf, bulklen, NULL); + kfree(buf); + if (result != USB_STOR_XFER_GOOD) { + usb_stor_dbg(us, "Result for bulk_transfer in sddr09_read_sg %d\n", + result); + return -EIO; + } + + return 0; +} +#endif + +/* + * Read Status Command: 12 bytes. + * byte 0: opcode: EC + * + * Returns 64 bytes, all zero except for the first. + * bit 0: 1: Error + * bit 5: 1: Suspended + * bit 6: 1: Ready + * bit 7: 1: Not write-protected + */ + +static int +sddr09_read_status(struct us_data *us, unsigned char *status) { + + unsigned char *command = us->iobuf; + unsigned char *data = us->iobuf; + int result; + + usb_stor_dbg(us, "Reading status...\n"); + + memset(command, 0, 12); + command[0] = 0xEC; + command[1] = LUNBITS; + + result = sddr09_send_scsi_command(us, command, 12); + if (result) + return result; + + result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + data, 64, NULL); + *status = data[0]; + return (result == USB_STOR_XFER_GOOD ? 0 : -EIO); +} + +static int +sddr09_read_data(struct us_data *us, + unsigned long address, + unsigned int sectors) { + + struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra; + unsigned char *buffer; + unsigned int lba, maxlba, pba; + unsigned int page, pages; + unsigned int len, offset; + struct scatterlist *sg; + int result; + + // Figure out the initial LBA and page + lba = address >> info->blockshift; + page = (address & info->blockmask); + maxlba = info->capacity >> (info->pageshift + info->blockshift); + if (lba >= maxlba) + return -EIO; + + // Since we only read in one block at a time, we have to create + // a bounce buffer and move the data a piece at a time between the + // bounce buffer and the actual transfer buffer. + + len = min_t(unsigned int, sectors, info->blocksize) * info->pagesize; + buffer = kmalloc(len, GFP_NOIO); + if (!buffer) + return -ENOMEM; + + // This could be made much more efficient by checking for + // contiguous LBA's. Another exercise left to the student. + + result = 0; + offset = 0; + sg = NULL; + + while (sectors > 0) { + + /* Find number of pages we can read in this block */ + pages = min(sectors, info->blocksize - page); + len = pages << info->pageshift; + + /* Not overflowing capacity? */ + if (lba >= maxlba) { + usb_stor_dbg(us, "Error: Requested lba %u exceeds maximum %u\n", + lba, maxlba); + result = -EIO; + break; + } + + /* Find where this lba lives on disk */ + pba = info->lba_to_pba[lba]; + + if (pba == UNDEF) { /* this lba was never written */ + + usb_stor_dbg(us, "Read %d zero pages (LBA %d) page %d\n", + pages, lba, page); + + /* + * This is not really an error. It just means + * that the block has never been written. + * Instead of returning an error + * it is better to return all zero data. + */ + + memset(buffer, 0, len); + + } else { + usb_stor_dbg(us, "Read %d pages, from PBA %d (LBA %d) page %d\n", + pages, pba, lba, page); + + address = ((pba << info->blockshift) + page) << + info->pageshift; + + result = sddr09_read20(us, address>>1, + pages, info->pageshift, buffer, 0); + if (result) + break; + } + + // Store the data in the transfer buffer + usb_stor_access_xfer_buf(buffer, len, us->srb, + &sg, &offset, TO_XFER_BUF); + + page = 0; + lba++; + sectors -= pages; + } + + kfree(buffer); + return result; +} + +static unsigned int +sddr09_find_unused_pba(struct sddr09_card_info *info, unsigned int lba) { + static unsigned int lastpba = 1; + int zonestart, end, i; + + zonestart = (lba/1000) << 10; + end = info->capacity >> (info->blockshift + info->pageshift); + end -= zonestart; + if (end > 1024) + end = 1024; + + for (i = lastpba+1; i < end; i++) { + if (info->pba_to_lba[zonestart+i] == UNDEF) { + lastpba = i; + return zonestart+i; + } + } + for (i = 0; i <= lastpba; i++) { + if (info->pba_to_lba[zonestart+i] == UNDEF) { + lastpba = i; + return zonestart+i; + } + } + return 0; +} + +static int +sddr09_write_lba(struct us_data *us, unsigned int lba, + unsigned int page, unsigned int pages, + unsigned char *ptr, unsigned char *blockbuffer) { + + struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra; + unsigned long address; + unsigned int pba, lbap; + unsigned int pagelen; + unsigned char *bptr, *cptr, *xptr; + unsigned char ecc[3]; + int i, result; + + lbap = ((lba % 1000) << 1) | 0x1000; + if (parity[MSB_of(lbap) ^ LSB_of(lbap)]) + lbap ^= 1; + pba = info->lba_to_pba[lba]; + + if (pba == UNDEF) { + pba = sddr09_find_unused_pba(info, lba); + if (!pba) { + printk(KERN_WARNING + "sddr09_write_lba: Out of unused blocks\n"); + return -ENOSPC; + } + info->pba_to_lba[pba] = lba; + info->lba_to_pba[lba] = pba; + } + + if (pba == 1) { + /* + * Maybe it is impossible to write to PBA 1. + * Fake success, but don't do anything. + */ + printk(KERN_WARNING "sddr09: avoid writing to pba 1\n"); + return 0; + } + + pagelen = (1 << info->pageshift) + (1 << CONTROL_SHIFT); + + /* read old contents */ + address = (pba << (info->pageshift + info->blockshift)); + result = sddr09_read22(us, address>>1, info->blocksize, + info->pageshift, blockbuffer, 0); + if (result) + return result; + + /* check old contents and fill lba */ + for (i = 0; i < info->blocksize; i++) { + bptr = blockbuffer + i*pagelen; + cptr = bptr + info->pagesize; + nand_compute_ecc(bptr, ecc); + if (!nand_compare_ecc(cptr+13, ecc)) { + usb_stor_dbg(us, "Warning: bad ecc in page %d- of pba %d\n", + i, pba); + nand_store_ecc(cptr+13, ecc); + } + nand_compute_ecc(bptr+(info->pagesize / 2), ecc); + if (!nand_compare_ecc(cptr+8, ecc)) { + usb_stor_dbg(us, "Warning: bad ecc in page %d+ of pba %d\n", + i, pba); + nand_store_ecc(cptr+8, ecc); + } + cptr[6] = cptr[11] = MSB_of(lbap); + cptr[7] = cptr[12] = LSB_of(lbap); + } + + /* copy in new stuff and compute ECC */ + xptr = ptr; + for (i = page; i < page+pages; i++) { + bptr = blockbuffer + i*pagelen; + cptr = bptr + info->pagesize; + memcpy(bptr, xptr, info->pagesize); + xptr += info->pagesize; + nand_compute_ecc(bptr, ecc); + nand_store_ecc(cptr+13, ecc); + nand_compute_ecc(bptr+(info->pagesize / 2), ecc); + nand_store_ecc(cptr+8, ecc); + } + + usb_stor_dbg(us, "Rewrite PBA %d (LBA %d)\n", pba, lba); + + result = sddr09_write_inplace(us, address>>1, info->blocksize, + info->pageshift, blockbuffer, 0); + + usb_stor_dbg(us, "sddr09_write_inplace returns %d\n", result); + +#if 0 + { + unsigned char status = 0; + int result2 = sddr09_read_status(us, &status); + if (result2) + usb_stor_dbg(us, "cannot read status\n"); + else if (status != 0xc0) + usb_stor_dbg(us, "status after write: 0x%x\n", status); + } +#endif + +#if 0 + { + int result2 = sddr09_test_unit_ready(us); + } +#endif + + return result; +} + +static int +sddr09_write_data(struct us_data *us, + unsigned long address, + unsigned int sectors) { + + struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra; + unsigned int lba, maxlba, page, pages; + unsigned int pagelen, blocklen; + unsigned char *blockbuffer; + unsigned char *buffer; + unsigned int len, offset; + struct scatterlist *sg; + int result; + + /* Figure out the initial LBA and page */ + lba = address >> info->blockshift; + page = (address & info->blockmask); + maxlba = info->capacity >> (info->pageshift + info->blockshift); + if (lba >= maxlba) + return -EIO; + + /* + * blockbuffer is used for reading in the old data, overwriting + * with the new data, and performing ECC calculations + */ + + /* + * TODO: instead of doing kmalloc/kfree for each write, + * add a bufferpointer to the info structure + */ + + pagelen = (1 << info->pageshift) + (1 << CONTROL_SHIFT); + blocklen = (pagelen << info->blockshift); + blockbuffer = kmalloc(blocklen, GFP_NOIO); + if (!blockbuffer) + return -ENOMEM; + + /* + * Since we don't write the user data directly to the device, + * we have to create a bounce buffer and move the data a piece + * at a time between the bounce buffer and the actual transfer buffer. + */ + + len = min_t(unsigned int, sectors, info->blocksize) * info->pagesize; + buffer = kmalloc(len, GFP_NOIO); + if (!buffer) { + kfree(blockbuffer); + return -ENOMEM; + } + + result = 0; + offset = 0; + sg = NULL; + + while (sectors > 0) { + + /* Write as many sectors as possible in this block */ + + pages = min(sectors, info->blocksize - page); + len = (pages << info->pageshift); + + /* Not overflowing capacity? */ + if (lba >= maxlba) { + usb_stor_dbg(us, "Error: Requested lba %u exceeds maximum %u\n", + lba, maxlba); + result = -EIO; + break; + } + + /* Get the data from the transfer buffer */ + usb_stor_access_xfer_buf(buffer, len, us->srb, + &sg, &offset, FROM_XFER_BUF); + + result = sddr09_write_lba(us, lba, page, pages, + buffer, blockbuffer); + if (result) + break; + + page = 0; + lba++; + sectors -= pages; + } + + kfree(buffer); + kfree(blockbuffer); + + return result; +} + +static int +sddr09_read_control(struct us_data *us, + unsigned long address, + unsigned int blocks, + unsigned char *content, + int use_sg) { + + usb_stor_dbg(us, "Read control address %lu, blocks %d\n", + address, blocks); + + return sddr09_read21(us, address, blocks, + CONTROL_SHIFT, content, use_sg); +} + +/* + * Read Device ID Command: 12 bytes. + * byte 0: opcode: ED + * + * Returns 2 bytes: Manufacturer ID and Device ID. + * On more recent cards 3 bytes: the third byte is an option code A5 + * signifying that the secret command to read an 128-bit ID is available. + * On still more recent cards 4 bytes: the fourth byte C0 means that + * a second read ID cmd is available. + */ +static int +sddr09_read_deviceID(struct us_data *us, unsigned char *deviceID) { + unsigned char *command = us->iobuf; + unsigned char *content = us->iobuf; + int result, i; + + memset(command, 0, 12); + command[0] = 0xED; + command[1] = LUNBITS; + + result = sddr09_send_scsi_command(us, command, 12); + if (result) + return result; + + result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + content, 64, NULL); + + for (i = 0; i < 4; i++) + deviceID[i] = content[i]; + + return (result == USB_STOR_XFER_GOOD ? 0 : -EIO); +} + +static int +sddr09_get_wp(struct us_data *us, struct sddr09_card_info *info) { + int result; + unsigned char status; + const char *wp_fmt; + + result = sddr09_read_status(us, &status); + if (result) { + usb_stor_dbg(us, "read_status fails\n"); + return result; + } + if ((status & 0x80) == 0) { + info->flags |= SDDR09_WP; /* write protected */ + wp_fmt = " WP"; + } else { + wp_fmt = ""; + } + usb_stor_dbg(us, "status 0x%02X%s%s%s%s\n", status, wp_fmt, + status & 0x40 ? " Ready" : "", + status & LUNBITS ? " Suspended" : "", + status & 0x01 ? " Error" : ""); + + return 0; +} + +#if 0 +/* + * Reset Command: 12 bytes. + * byte 0: opcode: EB + */ +static int +sddr09_reset(struct us_data *us) { + + unsigned char *command = us->iobuf; + + memset(command, 0, 12); + command[0] = 0xEB; + command[1] = LUNBITS; + + return sddr09_send_scsi_command(us, command, 12); +} +#endif + +static const struct nand_flash_dev * +sddr09_get_cardinfo(struct us_data *us, unsigned char flags) { + const struct nand_flash_dev *cardinfo; + unsigned char deviceID[4]; + char blurbtxt[256]; + int result; + + usb_stor_dbg(us, "Reading capacity...\n"); + + result = sddr09_read_deviceID(us, deviceID); + + if (result) { + usb_stor_dbg(us, "Result of read_deviceID is %d\n", result); + printk(KERN_WARNING "sddr09: could not read card info\n"); + return NULL; + } + + sprintf(blurbtxt, "sddr09: Found Flash card, ID = %4ph", deviceID); + + /* Byte 0 is the manufacturer */ + sprintf(blurbtxt + strlen(blurbtxt), + ": Manuf. %s", + nand_flash_manufacturer(deviceID[0])); + + /* Byte 1 is the device type */ + cardinfo = nand_find_id(deviceID[1]); + if (cardinfo) { + /* + * MB or MiB? It is neither. A 16 MB card has + * 17301504 raw bytes, of which 16384000 are + * usable for user data. + */ + sprintf(blurbtxt + strlen(blurbtxt), + ", %d MB", 1<<(cardinfo->chipshift - 20)); + } else { + sprintf(blurbtxt + strlen(blurbtxt), + ", type unrecognized"); + } + + /* Byte 2 is code to signal availability of 128-bit ID */ + if (deviceID[2] == 0xa5) { + sprintf(blurbtxt + strlen(blurbtxt), + ", 128-bit ID"); + } + + /* Byte 3 announces the availability of another read ID command */ + if (deviceID[3] == 0xc0) { + sprintf(blurbtxt + strlen(blurbtxt), + ", extra cmd"); + } + + if (flags & SDDR09_WP) + sprintf(blurbtxt + strlen(blurbtxt), + ", WP"); + + printk(KERN_WARNING "%s\n", blurbtxt); + + return cardinfo; +} + +static int +sddr09_read_map(struct us_data *us) { + + struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra; + int numblocks, alloc_len, alloc_blocks; + int i, j, result; + unsigned char *buffer, *buffer_end, *ptr; + unsigned int lba, lbact; + + if (!info->capacity) + return -1; + + /* + * size of a block is 1 << (blockshift + pageshift) bytes + * divide into the total capacity to get the number of blocks + */ + + numblocks = info->capacity >> (info->blockshift + info->pageshift); + + /* + * read 64 bytes for every block (actually 1 << CONTROL_SHIFT) + * but only use a 64 KB buffer + * buffer size used must be a multiple of (1 << CONTROL_SHIFT) + */ +#define SDDR09_READ_MAP_BUFSZ 65536 + + alloc_blocks = min(numblocks, SDDR09_READ_MAP_BUFSZ >> CONTROL_SHIFT); + alloc_len = (alloc_blocks << CONTROL_SHIFT); + buffer = kmalloc(alloc_len, GFP_NOIO); + if (!buffer) { + result = -1; + goto done; + } + buffer_end = buffer + alloc_len; + +#undef SDDR09_READ_MAP_BUFSZ + + kfree(info->lba_to_pba); + kfree(info->pba_to_lba); + info->lba_to_pba = kmalloc_objs(int, numblocks, GFP_NOIO); + info->pba_to_lba = kmalloc_objs(int, numblocks, GFP_NOIO); + + if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) { + printk(KERN_WARNING "sddr09_read_map: out of memory\n"); + result = -1; + goto done; + } + + for (i = 0; i < numblocks; i++) + info->lba_to_pba[i] = info->pba_to_lba[i] = UNDEF; + + /* + * Define lba-pba translation table + */ + + ptr = buffer_end; + for (i = 0; i < numblocks; i++) { + ptr += (1 << CONTROL_SHIFT); + if (ptr >= buffer_end) { + unsigned long address; + + address = i << (info->pageshift + info->blockshift); + result = sddr09_read_control( + us, address>>1, + min(alloc_blocks, numblocks - i), + buffer, 0); + if (result) { + result = -1; + goto done; + } + ptr = buffer; + } + + if (i == 0 || i == 1) { + info->pba_to_lba[i] = UNUSABLE; + continue; + } + + /* special PBAs have control field 0^16 */ + for (j = 0; j < 16; j++) + if (ptr[j] != 0) + goto nonz; + info->pba_to_lba[i] = UNUSABLE; + printk(KERN_WARNING "sddr09: PBA %d has no logical mapping\n", + i); + continue; + + nonz: + /* unwritten PBAs have control field FF^16 */ + for (j = 0; j < 16; j++) + if (ptr[j] != 0xff) + goto nonff; + continue; + + nonff: + /* normal PBAs start with six FFs */ + if (j < 6) { + printk(KERN_WARNING + "sddr09: PBA %d has no logical mapping: " + "reserved area = %02X%02X%02X%02X " + "data status %02X block status %02X\n", + i, ptr[0], ptr[1], ptr[2], ptr[3], + ptr[4], ptr[5]); + info->pba_to_lba[i] = UNUSABLE; + continue; + } + + if ((ptr[6] >> 4) != 0x01) { + printk(KERN_WARNING + "sddr09: PBA %d has invalid address field " + "%02X%02X/%02X%02X\n", + i, ptr[6], ptr[7], ptr[11], ptr[12]); + info->pba_to_lba[i] = UNUSABLE; + continue; + } + + /* check even parity */ + if (parity[ptr[6] ^ ptr[7]]) { + printk(KERN_WARNING + "sddr09: Bad parity in LBA for block %d" + " (%02X %02X)\n", i, ptr[6], ptr[7]); + info->pba_to_lba[i] = UNUSABLE; + continue; + } + + lba = short_pack(ptr[7], ptr[6]); + lba = (lba & 0x07FF) >> 1; + + /* + * Every 1024 physical blocks ("zone"), the LBA numbers + * go back to zero, but are within a higher block of LBA's. + * Also, there is a maximum of 1000 LBA's per zone. + * In other words, in PBA 1024-2047 you will find LBA 0-999 + * which are really LBA 1000-1999. This allows for 24 bad + * or special physical blocks per zone. + */ + + if (lba >= 1000) { + printk(KERN_WARNING + "sddr09: Bad low LBA %d for block %d\n", + lba, i); + goto possibly_erase; + } + + lba += 1000*(i/0x400); + + if (info->lba_to_pba[lba] != UNDEF) { + printk(KERN_WARNING + "sddr09: LBA %d seen for PBA %d and %d\n", + lba, info->lba_to_pba[lba], i); + goto possibly_erase; + } + + info->pba_to_lba[i] = lba; + info->lba_to_pba[lba] = i; + continue; + + possibly_erase: + if (erase_bad_lba_entries) { + unsigned long address; + + address = (i << (info->pageshift + info->blockshift)); + sddr09_erase(us, address>>1); + info->pba_to_lba[i] = UNDEF; + } else + info->pba_to_lba[i] = UNUSABLE; + } + + /* + * Approximate capacity. This is not entirely correct yet, + * since a zone with less than 1000 usable pages leads to + * missing LBAs. Especially if it is the last zone, some + * LBAs can be past capacity. + */ + lbact = 0; + for (i = 0; i < numblocks; i += 1024) { + int ct = 0; + + for (j = 0; j < 1024 && i+j < numblocks; j++) { + if (info->pba_to_lba[i+j] != UNUSABLE) { + if (ct >= 1000) + info->pba_to_lba[i+j] = SPARE; + else + ct++; + } + } + lbact += ct; + } + info->lbact = lbact; + usb_stor_dbg(us, "Found %d LBA's\n", lbact); + result = 0; + + done: + if (result != 0) { + kfree(info->lba_to_pba); + kfree(info->pba_to_lba); + info->lba_to_pba = NULL; + info->pba_to_lba = NULL; + } + kfree(buffer); + return result; +} + +static void +sddr09_card_info_destructor(void *extra) { + struct sddr09_card_info *info = (struct sddr09_card_info *)extra; + + if (!info) + return; + + kfree(info->lba_to_pba); + kfree(info->pba_to_lba); +} + +static int +sddr09_common_init(struct us_data *us) { + int result; + + /* set the configuration -- STALL is an acceptable response here */ + if (us->pusb_dev->actconfig->desc.bConfigurationValue != 1) { + usb_stor_dbg(us, "active config #%d != 1 ??\n", + us->pusb_dev->actconfig->desc.bConfigurationValue); + return -EINVAL; + } + + result = usb_reset_configuration(us->pusb_dev); + usb_stor_dbg(us, "Result of usb_reset_configuration is %d\n", result); + if (result == -EPIPE) { + usb_stor_dbg(us, "-- stall on control interface\n"); + } else if (result != 0) { + /* it's not a stall, but another error -- time to bail */ + usb_stor_dbg(us, "-- Unknown error. Rejecting device\n"); + return -EINVAL; + } + + us->extra = kzalloc_obj(struct sddr09_card_info, GFP_NOIO); + if (!us->extra) + return -ENOMEM; + us->extra_destructor = sddr09_card_info_destructor; + + nand_init_ecc(); + return 0; +} + + +/* + * This is needed at a very early stage. If this is not listed in the + * unusual devices list but called from here then LUN 0 of the combo reader + * is not recognized. But I do not know what precisely these calls do. + */ +static int +usb_stor_sddr09_dpcm_init(struct us_data *us) { + int result; + unsigned char *data = us->iobuf; + + result = sddr09_common_init(us); + if (result) + return result; + + result = sddr09_send_command(us, 0x01, USB_DIR_IN, data, 2); + if (result) { + usb_stor_dbg(us, "send_command fails\n"); + return result; + } + + usb_stor_dbg(us, "%02X %02X\n", data[0], data[1]); + // get 07 02 + + result = sddr09_send_command(us, 0x08, USB_DIR_IN, data, 2); + if (result) { + usb_stor_dbg(us, "2nd send_command fails\n"); + return result; + } + + usb_stor_dbg(us, "%02X %02X\n", data[0], data[1]); + // get 07 00 + + result = sddr09_request_sense(us, data, 18); + if (result == 0 && data[2] != 0) { + int j; + for (j=0; j<18; j++) + printk(" %02X", data[j]); + printk("\n"); + // get 70 00 00 00 00 00 00 * 00 00 00 00 00 00 + // 70: current command + // sense key 0, sense code 0, extd sense code 0 + // additional transfer length * = sizeof(data) - 7 + // Or: 70 00 06 00 00 00 00 0b 00 00 00 00 28 00 00 00 00 00 + // sense key 06, sense code 28: unit attention, + // not ready to ready transition + } + + // test unit ready + + return 0; /* not result */ +} + +/* + * Transport for the Microtech DPCM-USB + */ +static int dpcm_transport(struct scsi_cmnd *srb, struct us_data *us) +{ + int ret; + + usb_stor_dbg(us, "LUN=%d\n", (u8)srb->device->lun); + + switch (srb->device->lun) { + case 0: + + /* + * LUN 0 corresponds to the CompactFlash card reader. + */ + ret = usb_stor_CB_transport(srb, us); + break; + + case 1: + + /* + * LUN 1 corresponds to the SmartMedia card reader. + */ + + /* + * Set the LUN to 0 (just in case). + */ + srb->device->lun = 0; + ret = sddr09_transport(srb, us); + srb->device->lun = 1; + break; + + default: + usb_stor_dbg(us, "Invalid LUN %d\n", (u8)srb->device->lun); + ret = USB_STOR_TRANSPORT_ERROR; + break; + } + return ret; +} + + +/* + * Transport for the Sandisk SDDR-09 + */ +static int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us) +{ + static unsigned char sensekey = 0, sensecode = 0; + static unsigned char havefakesense = 0; + int result, i; + unsigned char *ptr = us->iobuf; + unsigned long capacity; + unsigned int page, pages; + + struct sddr09_card_info *info; + + static const unsigned char inquiry_response[8] = { + 0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00 + }; + + /* note: no block descriptor support */ + static const unsigned char mode_page_01[19] = { + 0x00, 0x0F, 0x00, 0x0, 0x0, 0x0, 0x00, + 0x01, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + info = (struct sddr09_card_info *)us->extra; + + if (srb->cmnd[0] == REQUEST_SENSE && havefakesense) { + /* for a faked command, we have to follow with a faked sense */ + memset(ptr, 0, 18); + ptr[0] = 0x70; + ptr[2] = sensekey; + ptr[7] = 11; + ptr[12] = sensecode; + usb_stor_set_xfer_buf(ptr, 18, srb); + sensekey = sensecode = havefakesense = 0; + return USB_STOR_TRANSPORT_GOOD; + } + + havefakesense = 1; + + /* + * Dummy up a response for INQUIRY since SDDR09 doesn't + * respond to INQUIRY commands + */ + + if (srb->cmnd[0] == INQUIRY) { + memcpy(ptr, inquiry_response, 8); + fill_inquiry_response(us, ptr, 36); + return USB_STOR_TRANSPORT_GOOD; + } + + if (srb->cmnd[0] == READ_CAPACITY) { + const struct nand_flash_dev *cardinfo; + + sddr09_get_wp(us, info); /* read WP bit */ + + cardinfo = sddr09_get_cardinfo(us, info->flags); + if (!cardinfo) { + /* probably no media */ + init_error: + sensekey = 0x02; /* not ready */ + sensecode = 0x3a; /* medium not present */ + return USB_STOR_TRANSPORT_FAILED; + } + + info->capacity = (1 << cardinfo->chipshift); + info->pageshift = cardinfo->pageshift; + info->pagesize = (1 << info->pageshift); + info->blockshift = cardinfo->blockshift; + info->blocksize = (1 << info->blockshift); + info->blockmask = info->blocksize - 1; + + // map initialization, must follow get_cardinfo() + if (sddr09_read_map(us)) { + /* probably out of memory */ + goto init_error; + } + + // Report capacity + + capacity = (info->lbact << info->blockshift) - 1; + + ((__be32 *) ptr)[0] = cpu_to_be32(capacity); + + // Report page size + + ((__be32 *) ptr)[1] = cpu_to_be32(info->pagesize); + usb_stor_set_xfer_buf(ptr, 8, srb); + + return USB_STOR_TRANSPORT_GOOD; + } + + if (srb->cmnd[0] == MODE_SENSE_10) { + int modepage = (srb->cmnd[2] & 0x3F); + + /* + * They ask for the Read/Write error recovery page, + * or for all pages. + */ + /* %% We should check DBD %% */ + if (modepage == 0x01 || modepage == 0x3F) { + usb_stor_dbg(us, "Dummy up request for mode page 0x%x\n", + modepage); + + memcpy(ptr, mode_page_01, sizeof(mode_page_01)); + ((__be16*)ptr)[0] = cpu_to_be16(sizeof(mode_page_01) - 2); + ptr[3] = (info->flags & SDDR09_WP) ? 0x80 : 0; + usb_stor_set_xfer_buf(ptr, sizeof(mode_page_01), srb); + return USB_STOR_TRANSPORT_GOOD; + } + + sensekey = 0x05; /* illegal request */ + sensecode = 0x24; /* invalid field in CDB */ + return USB_STOR_TRANSPORT_FAILED; + } + + if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) + return USB_STOR_TRANSPORT_GOOD; + + havefakesense = 0; + + if (srb->cmnd[0] == READ_10) { + + page = short_pack(srb->cmnd[3], srb->cmnd[2]); + page <<= 16; + page |= short_pack(srb->cmnd[5], srb->cmnd[4]); + pages = short_pack(srb->cmnd[8], srb->cmnd[7]); + + usb_stor_dbg(us, "READ_10: read page %d pagect %d\n", + page, pages); + + result = sddr09_read_data(us, page, pages); + return (result == 0 ? USB_STOR_TRANSPORT_GOOD : + USB_STOR_TRANSPORT_ERROR); + } + + if (srb->cmnd[0] == WRITE_10) { + + page = short_pack(srb->cmnd[3], srb->cmnd[2]); + page <<= 16; + page |= short_pack(srb->cmnd[5], srb->cmnd[4]); + pages = short_pack(srb->cmnd[8], srb->cmnd[7]); + + usb_stor_dbg(us, "WRITE_10: write page %d pagect %d\n", + page, pages); + + result = sddr09_write_data(us, page, pages); + return (result == 0 ? USB_STOR_TRANSPORT_GOOD : + USB_STOR_TRANSPORT_ERROR); + } + + /* + * catch-all for all other commands, except + * pass TEST_UNIT_READY and REQUEST_SENSE through + */ + if (srb->cmnd[0] != TEST_UNIT_READY && + srb->cmnd[0] != REQUEST_SENSE) { + sensekey = 0x05; /* illegal request */ + sensecode = 0x20; /* invalid command */ + havefakesense = 1; + return USB_STOR_TRANSPORT_FAILED; + } + + for (; srb->cmd_len<12; srb->cmd_len++) + srb->cmnd[srb->cmd_len] = 0; + + srb->cmnd[1] = LUNBITS; + + ptr[0] = 0; + for (i=0; i<12; i++) + sprintf(ptr+strlen(ptr), "%02X ", srb->cmnd[i]); + + usb_stor_dbg(us, "Send control for command %s\n", ptr); + + result = sddr09_send_scsi_command(us, srb->cmnd, 12); + if (result) { + usb_stor_dbg(us, "sddr09_send_scsi_command returns %d\n", + result); + return USB_STOR_TRANSPORT_ERROR; + } + + if (scsi_bufflen(srb) == 0) + return USB_STOR_TRANSPORT_GOOD; + + if (srb->sc_data_direction == DMA_TO_DEVICE || + srb->sc_data_direction == DMA_FROM_DEVICE) { + unsigned int pipe = (srb->sc_data_direction == DMA_TO_DEVICE) + ? us->send_bulk_pipe : us->recv_bulk_pipe; + + usb_stor_dbg(us, "%s %d bytes\n", + (srb->sc_data_direction == DMA_TO_DEVICE) ? + "sending" : "receiving", + scsi_bufflen(srb)); + + result = usb_stor_bulk_srb(us, pipe, srb); + + return (result == USB_STOR_XFER_GOOD ? + USB_STOR_TRANSPORT_GOOD : USB_STOR_TRANSPORT_ERROR); + } + + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Initialization routine for the sddr09 subdriver + */ +static int +usb_stor_sddr09_init(struct us_data *us) { + return sddr09_common_init(us); +} + +static struct scsi_host_template sddr09_host_template; + +static int sddr09_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct us_data *us; + int result; + + result = usb_stor_probe1(&us, intf, id, + (id - sddr09_usb_ids) + sddr09_unusual_dev_list, + &sddr09_host_template); + if (result) + return result; + + if (us->protocol == USB_PR_DPCM_USB) { + us->transport_name = "Control/Bulk-EUSB/SDDR09"; + us->transport = dpcm_transport; + us->transport_reset = usb_stor_CB_reset; + us->max_lun = 1; + } else { + us->transport_name = "EUSB/SDDR09"; + us->transport = sddr09_transport; + us->transport_reset = usb_stor_CB_reset; + us->max_lun = 0; + } + + result = usb_stor_probe2(us); + return result; +} + +static struct usb_driver sddr09_driver = { + .name = DRV_NAME, + .probe = sddr09_probe, + .disconnect = usb_stor_disconnect, + .suspend = usb_stor_suspend, + .resume = usb_stor_resume, + .reset_resume = usb_stor_reset_resume, + .pre_reset = usb_stor_pre_reset, + .post_reset = usb_stor_post_reset, + .id_table = sddr09_usb_ids, + .soft_unbind = 1, + .no_dynamic_id = 1, +}; + +module_usb_stor_driver(sddr09_driver, sddr09_host_template, DRV_NAME); diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c new file mode 100644 index 000000000..e05e824cd --- /dev/null +++ b/drivers/usb/storage/sddr55.c @@ -0,0 +1,1020 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for SanDisk SDDR-55 SmartMedia reader + * + * SDDR55 driver v0.1: + * + * First release + * + * Current development and maintenance by: + * (c) 2002 Simon Munton + */ + +#include +#include +#include +#include + +#include +#include + +#include "usb.h" +#include "transport.h" +#include "protocol.h" +#include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-sddr55" + +MODULE_DESCRIPTION("Driver for SanDisk SDDR-55 SmartMedia reader"); +MODULE_AUTHOR("Simon Munton"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("USB_STORAGE"); + +/* + * The table of devices + */ +#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ + vendorName, productName, useProtocol, useTransport, \ + initFunction, flags) \ +{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ + .driver_info = (flags) } + +static const struct usb_device_id sddr55_usb_ids[] = { +# include "unusual_sddr55.h" + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, sddr55_usb_ids); + +#undef UNUSUAL_DEV + +/* + * The flags table + */ +#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ + vendor_name, product_name, use_protocol, use_transport, \ + init_function, Flags) \ +{ \ + .vendorName = vendor_name, \ + .productName = product_name, \ + .useProtocol = use_protocol, \ + .useTransport = use_transport, \ + .initFunction = init_function, \ +} + +static const struct us_unusual_dev sddr55_unusual_dev_list[] = { +# include "unusual_sddr55.h" + { } /* Terminating entry */ +}; + +#undef UNUSUAL_DEV + + +#define short_pack(lsb,msb) ( ((u16)(lsb)) | ( ((u16)(msb))<<8 ) ) +#define LSB_of(s) ((s)&0xFF) +#define MSB_of(s) ((s)>>8) +#define PAGESIZE 512 + +#define set_sense_info(sk, asc, ascq) \ + do { \ + info->sense_data[2] = sk; \ + info->sense_data[12] = asc; \ + info->sense_data[13] = ascq; \ + } while (0) + + +struct sddr55_card_info { + unsigned long capacity; /* Size of card in bytes */ + int max_log_blks; /* maximum number of logical blocks */ + int pageshift; /* log2 of pagesize */ + int smallpageshift; /* 1 if pagesize == 256 */ + int blocksize; /* Size of block in pages */ + int blockshift; /* log2 of blocksize */ + int blockmask; /* 2^blockshift - 1 */ + int read_only; /* non zero if card is write protected */ + int force_read_only; /* non zero if we find a map error*/ + int *lba_to_pba; /* logical to physical map */ + int *pba_to_lba; /* physical to logical map */ + int fatal_error; /* set if we detect something nasty */ + unsigned long last_access; /* number of jiffies since we last talked to device */ + unsigned char sense_data[18]; +}; + + +#define NOT_ALLOCATED 0xffffffff +#define BAD_BLOCK 0xffff +#define CIS_BLOCK 0x400 +#define UNUSED_BLOCK 0x3ff + +static int +sddr55_bulk_transport(struct us_data *us, int direction, + unsigned char *data, unsigned int len) { + struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra; + unsigned int pipe = (direction == DMA_FROM_DEVICE) ? + us->recv_bulk_pipe : us->send_bulk_pipe; + + if (!len) + return USB_STOR_XFER_GOOD; + info->last_access = jiffies; + return usb_stor_bulk_transfer_buf(us, pipe, data, len, NULL); +} + +/* + * check if card inserted, if there is, update read_only status + * return non zero if no card + */ + +static int sddr55_status(struct us_data *us) +{ + int result; + unsigned char *command = us->iobuf; + unsigned char *status = us->iobuf; + struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra; + + /* send command */ + memset(command, 0, 8); + command[5] = 0xB0; + command[7] = 0x80; + result = sddr55_bulk_transport(us, + DMA_TO_DEVICE, command, 8); + + usb_stor_dbg(us, "Result for send_command in status %d\n", result); + + if (result != USB_STOR_XFER_GOOD) { + set_sense_info (4, 0, 0); /* hardware error */ + return USB_STOR_TRANSPORT_ERROR; + } + + result = sddr55_bulk_transport(us, + DMA_FROM_DEVICE, status, 4); + + /* expect to get short transfer if no card fitted */ + if (result == USB_STOR_XFER_SHORT || result == USB_STOR_XFER_STALLED) { + /* had a short transfer, no card inserted, free map memory */ + kfree(info->lba_to_pba); + kfree(info->pba_to_lba); + info->lba_to_pba = NULL; + info->pba_to_lba = NULL; + + info->fatal_error = 0; + info->force_read_only = 0; + + set_sense_info (2, 0x3a, 0); /* not ready, medium not present */ + return USB_STOR_TRANSPORT_FAILED; + } + + if (result != USB_STOR_XFER_GOOD) { + set_sense_info (4, 0, 0); /* hardware error */ + return USB_STOR_TRANSPORT_FAILED; + } + + /* check write protect status */ + info->read_only = (status[0] & 0x20); + + /* now read status */ + result = sddr55_bulk_transport(us, + DMA_FROM_DEVICE, status, 2); + + if (result != USB_STOR_XFER_GOOD) { + set_sense_info (4, 0, 0); /* hardware error */ + } + + return (result == USB_STOR_XFER_GOOD ? + USB_STOR_TRANSPORT_GOOD : USB_STOR_TRANSPORT_FAILED); +} + + +static int sddr55_read_data(struct us_data *us, + unsigned int lba, + unsigned int page, + unsigned short sectors) { + + int result = USB_STOR_TRANSPORT_GOOD; + unsigned char *command = us->iobuf; + unsigned char *status = us->iobuf; + struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra; + unsigned char *buffer; + + unsigned int pba; + unsigned int address; + + unsigned short pages; + unsigned int len, offset; + struct scatterlist *sg; + + // Since we only read in one block at a time, we have to create + // a bounce buffer and move the data a piece at a time between the + // bounce buffer and the actual transfer buffer. + + len = min_t(unsigned int, sectors, info->blocksize >> + info->smallpageshift) * PAGESIZE; + buffer = kmalloc(len, GFP_NOIO); + if (buffer == NULL) + return USB_STOR_TRANSPORT_ERROR; /* out of memory */ + offset = 0; + sg = NULL; + + while (sectors>0) { + + /* have we got to end? */ + if (lba >= info->max_log_blks) + break; + + pba = info->lba_to_pba[lba]; + + // Read as many sectors as possible in this block + + pages = min_t(unsigned int, sectors << info->smallpageshift, + info->blocksize - page); + len = pages << info->pageshift; + + usb_stor_dbg(us, "Read %02X pages, from PBA %04X (LBA %04X) page %02X\n", + pages, pba, lba, page); + + if (pba == NOT_ALLOCATED) { + /* no pba for this lba, fill with zeroes */ + memset (buffer, 0, len); + } else { + + address = (pba << info->blockshift) + page; + + command[0] = 0; + command[1] = LSB_of(address>>16); + command[2] = LSB_of(address>>8); + command[3] = LSB_of(address); + + command[4] = 0; + command[5] = 0xB0; + command[6] = LSB_of(pages << (1 - info->smallpageshift)); + command[7] = 0x85; + + /* send command */ + result = sddr55_bulk_transport(us, + DMA_TO_DEVICE, command, 8); + + usb_stor_dbg(us, "Result for send_command in read_data %d\n", + result); + + if (result != USB_STOR_XFER_GOOD) { + result = USB_STOR_TRANSPORT_ERROR; + goto leave; + } + + /* read data */ + result = sddr55_bulk_transport(us, + DMA_FROM_DEVICE, buffer, len); + + if (result != USB_STOR_XFER_GOOD) { + result = USB_STOR_TRANSPORT_ERROR; + goto leave; + } + + /* now read status */ + result = sddr55_bulk_transport(us, + DMA_FROM_DEVICE, status, 2); + + if (result != USB_STOR_XFER_GOOD) { + result = USB_STOR_TRANSPORT_ERROR; + goto leave; + } + + /* check status for error */ + if (status[0] == 0xff && status[1] == 0x4) { + set_sense_info (3, 0x11, 0); + result = USB_STOR_TRANSPORT_FAILED; + goto leave; + } + } + + // Store the data in the transfer buffer + usb_stor_access_xfer_buf(buffer, len, us->srb, + &sg, &offset, TO_XFER_BUF); + + page = 0; + lba++; + sectors -= pages >> info->smallpageshift; + } + + result = USB_STOR_TRANSPORT_GOOD; + +leave: + kfree(buffer); + + return result; +} + +static int sddr55_write_data(struct us_data *us, + unsigned int lba, + unsigned int page, + unsigned short sectors) { + + int result = USB_STOR_TRANSPORT_GOOD; + unsigned char *command = us->iobuf; + unsigned char *status = us->iobuf; + struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra; + unsigned char *buffer; + + unsigned int pba; + unsigned int new_pba; + unsigned int address; + + unsigned short pages; + int i; + unsigned int len, offset; + struct scatterlist *sg; + + /* check if we are allowed to write */ + if (info->read_only || info->force_read_only) { + set_sense_info (7, 0x27, 0); /* read only */ + return USB_STOR_TRANSPORT_FAILED; + } + + // Since we only write one block at a time, we have to create + // a bounce buffer and move the data a piece at a time between the + // bounce buffer and the actual transfer buffer. + + len = min_t(unsigned int, sectors, info->blocksize >> + info->smallpageshift) * PAGESIZE; + buffer = kmalloc(len, GFP_NOIO); + if (buffer == NULL) + return USB_STOR_TRANSPORT_ERROR; + offset = 0; + sg = NULL; + + while (sectors > 0) { + + /* have we got to end? */ + if (lba >= info->max_log_blks) + break; + + pba = info->lba_to_pba[lba]; + + // Write as many sectors as possible in this block + + pages = min_t(unsigned int, sectors << info->smallpageshift, + info->blocksize - page); + len = pages << info->pageshift; + + // Get the data from the transfer buffer + usb_stor_access_xfer_buf(buffer, len, us->srb, + &sg, &offset, FROM_XFER_BUF); + + usb_stor_dbg(us, "Write %02X pages, to PBA %04X (LBA %04X) page %02X\n", + pages, pba, lba, page); + + command[4] = 0; + + if (pba == NOT_ALLOCATED) { + /* no pba allocated for this lba, find a free pba to use */ + + int max_pba = (info->max_log_blks / 250 ) * 256; + int found_count = 0; + int found_pba = -1; + + /* set pba to first block in zone lba is in */ + pba = (lba / 1000) * 1024; + + usb_stor_dbg(us, "No PBA for LBA %04X\n", lba); + + if (max_pba > 1024) + max_pba = 1024; + + /* + * Scan through the map looking for an unused block + * leave 16 unused blocks at start (or as many as + * possible) since the sddr55 seems to reuse a used + * block when it shouldn't if we don't leave space. + */ + for (i = 0; i < max_pba; i++, pba++) { + if (info->pba_to_lba[pba] == UNUSED_BLOCK) { + found_pba = pba; + if (found_count++ > 16) + break; + } + } + + pba = found_pba; + + if (pba == -1) { + /* oh dear */ + usb_stor_dbg(us, "Couldn't find unallocated block\n"); + + set_sense_info (3, 0x31, 0); /* medium error */ + result = USB_STOR_TRANSPORT_FAILED; + goto leave; + } + + usb_stor_dbg(us, "Allocating PBA %04X for LBA %04X\n", + pba, lba); + + /* set writing to unallocated block flag */ + command[4] = 0x40; + } + + address = (pba << info->blockshift) + page; + + command[1] = LSB_of(address>>16); + command[2] = LSB_of(address>>8); + command[3] = LSB_of(address); + + /* set the lba into the command, modulo 1000 */ + command[0] = LSB_of(lba % 1000); + command[6] = MSB_of(lba % 1000); + + command[4] |= LSB_of(pages >> info->smallpageshift); + command[5] = 0xB0; + command[7] = 0x86; + + /* send command */ + result = sddr55_bulk_transport(us, + DMA_TO_DEVICE, command, 8); + + if (result != USB_STOR_XFER_GOOD) { + usb_stor_dbg(us, "Result for send_command in write_data %d\n", + result); + + /* set_sense_info is superfluous here? */ + set_sense_info (3, 0x3, 0);/* peripheral write error */ + result = USB_STOR_TRANSPORT_FAILED; + goto leave; + } + + /* send the data */ + result = sddr55_bulk_transport(us, + DMA_TO_DEVICE, buffer, len); + + if (result != USB_STOR_XFER_GOOD) { + usb_stor_dbg(us, "Result for send_data in write_data %d\n", + result); + + /* set_sense_info is superfluous here? */ + set_sense_info (3, 0x3, 0);/* peripheral write error */ + result = USB_STOR_TRANSPORT_FAILED; + goto leave; + } + + /* now read status */ + result = sddr55_bulk_transport(us, DMA_FROM_DEVICE, status, 6); + + if (result != USB_STOR_XFER_GOOD) { + usb_stor_dbg(us, "Result for get_status in write_data %d\n", + result); + + /* set_sense_info is superfluous here? */ + set_sense_info (3, 0x3, 0);/* peripheral write error */ + result = USB_STOR_TRANSPORT_FAILED; + goto leave; + } + + new_pba = (status[3] + (status[4] << 8) + (status[5] << 16)) + >> info->blockshift; + + /* check if device-reported new_pba is out of range */ + if (new_pba >= (info->capacity >> (info->blockshift + info->pageshift))) { + result = USB_STOR_TRANSPORT_FAILED; + goto leave; + } + + /* check status for error */ + if (status[0] == 0xff && status[1] == 0x4) { + info->pba_to_lba[new_pba] = BAD_BLOCK; + + set_sense_info (3, 0x0c, 0); + result = USB_STOR_TRANSPORT_FAILED; + goto leave; + } + + usb_stor_dbg(us, "Updating maps for LBA %04X: old PBA %04X, new PBA %04X\n", + lba, pba, new_pba); + + /* update the lba<->pba maps, note new_pba might be the same as pba */ + info->lba_to_pba[lba] = new_pba; + info->pba_to_lba[pba] = UNUSED_BLOCK; + + /* check that new_pba wasn't already being used */ + if (info->pba_to_lba[new_pba] != UNUSED_BLOCK) { + printk(KERN_ERR "sddr55 error: new PBA %04X already in use for LBA %04X\n", + new_pba, info->pba_to_lba[new_pba]); + info->fatal_error = 1; + set_sense_info (3, 0x31, 0); + result = USB_STOR_TRANSPORT_FAILED; + goto leave; + } + + /* update the pba<->lba maps for new_pba */ + info->pba_to_lba[new_pba] = lba % 1000; + + page = 0; + lba++; + sectors -= pages >> info->smallpageshift; + } + result = USB_STOR_TRANSPORT_GOOD; + + leave: + kfree(buffer); + return result; +} + +static int sddr55_read_deviceID(struct us_data *us, + unsigned char *manufacturerID, + unsigned char *deviceID) { + + int result; + unsigned char *command = us->iobuf; + unsigned char *content = us->iobuf; + + memset(command, 0, 8); + command[5] = 0xB0; + command[7] = 0x84; + result = sddr55_bulk_transport(us, DMA_TO_DEVICE, command, 8); + + usb_stor_dbg(us, "Result of send_control for device ID is %d\n", + result); + + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + result = sddr55_bulk_transport(us, + DMA_FROM_DEVICE, content, 4); + + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + *manufacturerID = content[0]; + *deviceID = content[1]; + + if (content[0] != 0xff) { + result = sddr55_bulk_transport(us, + DMA_FROM_DEVICE, content, 2); + } + + return USB_STOR_TRANSPORT_GOOD; +} + + +static int sddr55_reset(struct us_data *us) +{ + return 0; +} + + +static unsigned long sddr55_get_capacity(struct us_data *us) { + + unsigned char manufacturerID; + unsigned char deviceID; + int result; + struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra; + + usb_stor_dbg(us, "Reading capacity...\n"); + + result = sddr55_read_deviceID(us, + &manufacturerID, + &deviceID); + + usb_stor_dbg(us, "Result of read_deviceID is %d\n", result); + + if (result != USB_STOR_XFER_GOOD) + return 0; + + usb_stor_dbg(us, "Device ID = %02X\n", deviceID); + usb_stor_dbg(us, "Manuf ID = %02X\n", manufacturerID); + + info->pageshift = 9; + info->smallpageshift = 0; + info->blocksize = 16; + info->blockshift = 4; + info->blockmask = 15; + + switch (deviceID) { + + case 0x6e: // 1MB + case 0xe8: + case 0xec: + info->pageshift = 8; + info->smallpageshift = 1; + return 0x00100000; + + case 0xea: // 2MB + case 0x64: + info->pageshift = 8; + info->smallpageshift = 1; + fallthrough; + case 0x5d: // 5d is a ROM card with pagesize 512. + return 0x00200000; + + case 0xe3: // 4MB + case 0xe5: + case 0x6b: + case 0xd5: + return 0x00400000; + + case 0xe6: // 8MB + case 0xd6: + return 0x00800000; + + case 0x73: // 16MB + info->blocksize = 32; + info->blockshift = 5; + info->blockmask = 31; + return 0x01000000; + + case 0x75: // 32MB + info->blocksize = 32; + info->blockshift = 5; + info->blockmask = 31; + return 0x02000000; + + case 0x76: // 64MB + info->blocksize = 32; + info->blockshift = 5; + info->blockmask = 31; + return 0x04000000; + + case 0x79: // 128MB + info->blocksize = 32; + info->blockshift = 5; + info->blockmask = 31; + return 0x08000000; + + default: // unknown + return 0; + + } +} + +static int sddr55_read_map(struct us_data *us) { + + struct sddr55_card_info *info = (struct sddr55_card_info *)(us->extra); + int numblocks; + unsigned char *buffer; + unsigned char *command = us->iobuf; + int i; + unsigned short lba; + unsigned short max_lba; + int result; + + if (!info->capacity) + return -1; + + numblocks = info->capacity >> (info->blockshift + info->pageshift); + + buffer = kmalloc_array(numblocks, 2, GFP_NOIO ); + + if (!buffer) + return -1; + + memset(command, 0, 8); + command[5] = 0xB0; + command[6] = numblocks * 2 / 256; + command[7] = 0x8A; + + result = sddr55_bulk_transport(us, DMA_TO_DEVICE, command, 8); + + if ( result != USB_STOR_XFER_GOOD) { + kfree (buffer); + return -1; + } + + result = sddr55_bulk_transport(us, DMA_FROM_DEVICE, buffer, numblocks * 2); + + if ( result != USB_STOR_XFER_GOOD) { + kfree (buffer); + return -1; + } + + result = sddr55_bulk_transport(us, DMA_FROM_DEVICE, command, 2); + + if ( result != USB_STOR_XFER_GOOD) { + kfree (buffer); + return -1; + } + + kfree(info->lba_to_pba); + kfree(info->pba_to_lba); + info->lba_to_pba = kmalloc_objs(int, numblocks, GFP_NOIO); + info->pba_to_lba = kmalloc_objs(int, numblocks, GFP_NOIO); + + if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) { + kfree(info->lba_to_pba); + kfree(info->pba_to_lba); + info->lba_to_pba = NULL; + info->pba_to_lba = NULL; + kfree(buffer); + return -1; + } + + memset(info->lba_to_pba, 0xff, numblocks*sizeof(int)); + memset(info->pba_to_lba, 0xff, numblocks*sizeof(int)); + + /* set maximum lba */ + max_lba = info->max_log_blks; + if (max_lba > 1000) + max_lba = 1000; + + /* + * Each block is 64 bytes of control data, so block i is located in + * scatterlist block i*64/128k = i*(2^6)*(2^-17) = i*(2^-11) + */ + + for (i=0; ipba_to_lba[i] = lba; + + if (lba >= max_lba) { + continue; + } + + if (info->lba_to_pba[lba + zone * 1000] != NOT_ALLOCATED && + !info->force_read_only) { + printk(KERN_WARNING + "sddr55: map inconsistency at LBA %04X\n", + lba + zone * 1000); + info->force_read_only = 1; + } + + if (lba<0x10 || (lba>=0x3E0 && lba<0x3EF)) + usb_stor_dbg(us, "LBA %04X <-> PBA %04X\n", lba, i); + + info->lba_to_pba[lba + zone * 1000] = i; + } + + kfree(buffer); + return 0; +} + + +static void sddr55_card_info_destructor(void *extra) { + struct sddr55_card_info *info = (struct sddr55_card_info *)extra; + + if (!extra) + return; + + kfree(info->lba_to_pba); + kfree(info->pba_to_lba); +} + + +/* + * Transport for the Sandisk SDDR-55 + */ +static int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us) +{ + int result; + static const unsigned char inquiry_response[8] = { + 0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00 + }; + // write-protected for now, no block descriptor support + static const unsigned char mode_page_01[20] = { + 0x0, 0x12, 0x00, 0x80, 0x0, 0x0, 0x0, 0x0, + 0x01, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + unsigned char *ptr = us->iobuf; + unsigned long capacity; + unsigned int lba; + unsigned int pba; + unsigned int page; + unsigned short pages; + struct sddr55_card_info *info; + + if (!us->extra) { + us->extra = kzalloc_obj(struct sddr55_card_info, GFP_NOIO); + if (!us->extra) + return USB_STOR_TRANSPORT_ERROR; + us->extra_destructor = sddr55_card_info_destructor; + } + + info = (struct sddr55_card_info *)(us->extra); + + if (srb->cmnd[0] == REQUEST_SENSE) { + usb_stor_dbg(us, "request sense %02x/%02x/%02x\n", + info->sense_data[2], + info->sense_data[12], + info->sense_data[13]); + + memcpy (ptr, info->sense_data, sizeof info->sense_data); + ptr[0] = 0x70; + ptr[7] = 11; + usb_stor_set_xfer_buf (ptr, sizeof info->sense_data, srb); + memset (info->sense_data, 0, sizeof info->sense_data); + + return USB_STOR_TRANSPORT_GOOD; + } + + memset (info->sense_data, 0, sizeof info->sense_data); + + /* + * Dummy up a response for INQUIRY since SDDR55 doesn't + * respond to INQUIRY commands + */ + + if (srb->cmnd[0] == INQUIRY) { + memcpy(ptr, inquiry_response, 8); + fill_inquiry_response(us, ptr, 36); + return USB_STOR_TRANSPORT_GOOD; + } + + /* + * only check card status if the map isn't allocated, ie no card seen yet + * or if it's been over half a second since we last accessed it + */ + if (info->lba_to_pba == NULL || time_after(jiffies, info->last_access + HZ/2)) { + + /* check to see if a card is fitted */ + result = sddr55_status (us); + if (result) { + result = sddr55_status (us); + if (!result) { + set_sense_info (6, 0x28, 0); /* new media, set unit attention, not ready to ready */ + } + return USB_STOR_TRANSPORT_FAILED; + } + } + + /* + * if we detected a problem with the map when writing, + * don't allow any more access + */ + if (info->fatal_error) { + + set_sense_info (3, 0x31, 0); + return USB_STOR_TRANSPORT_FAILED; + } + + if (srb->cmnd[0] == READ_CAPACITY) { + + capacity = sddr55_get_capacity(us); + + if (!capacity) { + set_sense_info (3, 0x30, 0); /* incompatible medium */ + return USB_STOR_TRANSPORT_FAILED; + } + + info->capacity = capacity; + + /* + * figure out the maximum logical block number, allowing for + * the fact that only 250 out of every 256 are used + */ + info->max_log_blks = ((info->capacity >> (info->pageshift + info->blockshift)) / 256) * 250; + + /* + * Last page in the card, adjust as we only use 250 out of + * every 256 pages + */ + capacity = (capacity / 256) * 250; + + capacity /= PAGESIZE; + capacity--; + + ((__be32 *) ptr)[0] = cpu_to_be32(capacity); + ((__be32 *) ptr)[1] = cpu_to_be32(PAGESIZE); + usb_stor_set_xfer_buf(ptr, 8, srb); + + sddr55_read_map(us); + + return USB_STOR_TRANSPORT_GOOD; + } + + if (srb->cmnd[0] == MODE_SENSE_10) { + + memcpy(ptr, mode_page_01, sizeof mode_page_01); + ptr[3] = (info->read_only || info->force_read_only) ? 0x80 : 0; + usb_stor_set_xfer_buf(ptr, sizeof(mode_page_01), srb); + + if ( (srb->cmnd[2] & 0x3F) == 0x01 ) { + usb_stor_dbg(us, "Dummy up request for mode page 1\n"); + return USB_STOR_TRANSPORT_GOOD; + + } else if ( (srb->cmnd[2] & 0x3F) == 0x3F ) { + usb_stor_dbg(us, "Dummy up request for all mode pages\n"); + return USB_STOR_TRANSPORT_GOOD; + } + + set_sense_info (5, 0x24, 0); /* invalid field in command */ + return USB_STOR_TRANSPORT_FAILED; + } + + if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) { + + usb_stor_dbg(us, "%s medium removal. Not that I can do anything about it...\n", + (srb->cmnd[4]&0x03) ? "Prevent" : "Allow"); + + return USB_STOR_TRANSPORT_GOOD; + + } + + if (srb->cmnd[0] == READ_10 || srb->cmnd[0] == WRITE_10) { + + page = short_pack(srb->cmnd[3], srb->cmnd[2]); + page <<= 16; + page |= short_pack(srb->cmnd[5], srb->cmnd[4]); + pages = short_pack(srb->cmnd[8], srb->cmnd[7]); + + page <<= info->smallpageshift; + + // convert page to block and page-within-block + + lba = page >> info->blockshift; + page = page & info->blockmask; + + // locate physical block corresponding to logical block + + if (lba >= info->max_log_blks) { + + usb_stor_dbg(us, "Error: Requested LBA %04X exceeds maximum block %04X\n", + lba, info->max_log_blks - 1); + + set_sense_info (5, 0x24, 0); /* invalid field in command */ + + return USB_STOR_TRANSPORT_FAILED; + } + + pba = info->lba_to_pba[lba]; + + if (srb->cmnd[0] == WRITE_10) { + usb_stor_dbg(us, "WRITE_10: write block %04X (LBA %04X) page %01X pages %d\n", + pba, lba, page, pages); + + return sddr55_write_data(us, lba, page, pages); + } else { + usb_stor_dbg(us, "READ_10: read block %04X (LBA %04X) page %01X pages %d\n", + pba, lba, page, pages); + + return sddr55_read_data(us, lba, page, pages); + } + } + + + if (srb->cmnd[0] == TEST_UNIT_READY) { + return USB_STOR_TRANSPORT_GOOD; + } + + if (srb->cmnd[0] == START_STOP) { + return USB_STOR_TRANSPORT_GOOD; + } + + set_sense_info (5, 0x20, 0); /* illegal command */ + + return USB_STOR_TRANSPORT_FAILED; // FIXME: sense buffer? +} + +static struct scsi_host_template sddr55_host_template; + +static int sddr55_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct us_data *us; + int result; + + result = usb_stor_probe1(&us, intf, id, + (id - sddr55_usb_ids) + sddr55_unusual_dev_list, + &sddr55_host_template); + if (result) + return result; + + us->transport_name = "SDDR55"; + us->transport = sddr55_transport; + us->transport_reset = sddr55_reset; + us->max_lun = 0; + + result = usb_stor_probe2(us); + return result; +} + +static struct usb_driver sddr55_driver = { + .name = DRV_NAME, + .probe = sddr55_probe, + .disconnect = usb_stor_disconnect, + .suspend = usb_stor_suspend, + .resume = usb_stor_resume, + .reset_resume = usb_stor_reset_resume, + .pre_reset = usb_stor_pre_reset, + .post_reset = usb_stor_post_reset, + .id_table = sddr55_usb_ids, + .soft_unbind = 1, + .no_dynamic_id = 1, +}; + +module_usb_stor_driver(sddr55_driver, sddr55_host_template, DRV_NAME); diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c new file mode 100644 index 000000000..7e5424268 --- /dev/null +++ b/drivers/usb/storage/shuttle_usbat.c @@ -0,0 +1,1872 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for SCM Microsystems (a.k.a. Shuttle) USB-ATAPI cable + * + * Current development and maintenance by: + * (c) 2000, 2001 Robert Baruch (autophile@starband.net) + * (c) 2004, 2005 Daniel Drake + * + * Developed with the assistance of: + * (c) 2002 Alan Stern + * + * Flash support based on earlier work by: + * (c) 2002 Thomas Kreiling + * + * Many originally ATAPI devices were slightly modified to meet the USB + * market by using some kind of translation from ATAPI to USB on the host, + * and the peripheral would translate from USB back to ATAPI. + * + * SCM Microsystems (www.scmmicro.com) makes a device, sold to OEM's only, + * which does the USB-to-ATAPI conversion. By obtaining the data sheet on + * their device under nondisclosure agreement, I have been able to write + * this driver for Linux. + * + * The chip used in the device can also be used for EPP and ISA translation + * as well. This driver is only guaranteed to work with the ATAPI + * translation. + * + * See the Kconfig help text for a list of devices known to be supported by + * this driver. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "usb.h" +#include "transport.h" +#include "protocol.h" +#include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-usbat" + +MODULE_DESCRIPTION("Driver for SCM Microsystems (a.k.a. Shuttle) USB-ATAPI cable"); +MODULE_AUTHOR("Daniel Drake , Robert Baruch "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("USB_STORAGE"); + +/* Supported device types */ +#define USBAT_DEV_HP8200 0x01 +#define USBAT_DEV_FLASH 0x02 + +#define USBAT_EPP_PORT 0x10 +#define USBAT_EPP_REGISTER 0x30 +#define USBAT_ATA 0x40 +#define USBAT_ISA 0x50 + +/* Commands (need to be logically OR'd with an access type */ +#define USBAT_CMD_READ_REG 0x00 +#define USBAT_CMD_WRITE_REG 0x01 +#define USBAT_CMD_READ_BLOCK 0x02 +#define USBAT_CMD_WRITE_BLOCK 0x03 +#define USBAT_CMD_COND_READ_BLOCK 0x04 +#define USBAT_CMD_COND_WRITE_BLOCK 0x05 +#define USBAT_CMD_WRITE_REGS 0x07 + +/* Commands (these don't need an access type) */ +#define USBAT_CMD_EXEC_CMD 0x80 +#define USBAT_CMD_SET_FEAT 0x81 +#define USBAT_CMD_UIO 0x82 + +/* Methods of accessing UIO register */ +#define USBAT_UIO_READ 1 +#define USBAT_UIO_WRITE 0 + +/* Qualifier bits */ +#define USBAT_QUAL_FCQ 0x20 /* full compare */ +#define USBAT_QUAL_ALQ 0x10 /* auto load subcount */ + +/* USBAT Flash Media status types */ +#define USBAT_FLASH_MEDIA_NONE 0 +#define USBAT_FLASH_MEDIA_CF 1 + +/* USBAT Flash Media change types */ +#define USBAT_FLASH_MEDIA_SAME 0 +#define USBAT_FLASH_MEDIA_CHANGED 1 + +/* USBAT ATA registers */ +#define USBAT_ATA_DATA 0x10 /* read/write data (R/W) */ +#define USBAT_ATA_FEATURES 0x11 /* set features (W) */ +#define USBAT_ATA_ERROR 0x11 /* error (R) */ +#define USBAT_ATA_SECCNT 0x12 /* sector count (R/W) */ +#define USBAT_ATA_SECNUM 0x13 /* sector number (R/W) */ +#define USBAT_ATA_LBA_ME 0x14 /* cylinder low (R/W) */ +#define USBAT_ATA_LBA_HI 0x15 /* cylinder high (R/W) */ +#define USBAT_ATA_DEVICE 0x16 /* head/device selection (R/W) */ +#define USBAT_ATA_STATUS 0x17 /* device status (R) */ +#define USBAT_ATA_CMD 0x17 /* device command (W) */ +#define USBAT_ATA_ALTSTATUS 0x0E /* status (no clear IRQ) (R) */ + +/* USBAT User I/O Data registers */ +#define USBAT_UIO_EPAD 0x80 /* Enable Peripheral Control Signals */ +#define USBAT_UIO_CDT 0x40 /* Card Detect (Read Only) */ + /* CDT = ACKD & !UI1 & !UI0 */ +#define USBAT_UIO_1 0x20 /* I/O 1 */ +#define USBAT_UIO_0 0x10 /* I/O 0 */ +#define USBAT_UIO_EPP_ATA 0x08 /* 1=EPP mode, 0=ATA mode */ +#define USBAT_UIO_UI1 0x04 /* Input 1 */ +#define USBAT_UIO_UI0 0x02 /* Input 0 */ +#define USBAT_UIO_INTR_ACK 0x01 /* Interrupt (ATA/ISA)/Acknowledge (EPP) */ + +/* USBAT User I/O Enable registers */ +#define USBAT_UIO_DRVRST 0x80 /* Reset Peripheral */ +#define USBAT_UIO_ACKD 0x40 /* Enable Card Detect */ +#define USBAT_UIO_OE1 0x20 /* I/O 1 set=output/clr=input */ + /* If ACKD=1, set OE1 to 1 also. */ +#define USBAT_UIO_OE0 0x10 /* I/O 0 set=output/clr=input */ +#define USBAT_UIO_ADPRST 0x01 /* Reset SCM chip */ + +/* USBAT Features */ +#define USBAT_FEAT_ETEN 0x80 /* External trigger enable */ +#define USBAT_FEAT_U1 0x08 +#define USBAT_FEAT_U0 0x04 +#define USBAT_FEAT_ET1 0x02 +#define USBAT_FEAT_ET2 0x01 + +struct usbat_info { + int devicetype; + + /* Used for Flash readers only */ + unsigned long sectors; /* total sector count */ + unsigned long ssize; /* sector size in bytes */ + + unsigned char sense_key; + unsigned long sense_asc; /* additional sense code */ + unsigned long sense_ascq; /* additional sense code qualifier */ +}; + +#define short_pack(LSB,MSB) ( ((u16)(LSB)) | ( ((u16)(MSB))<<8 ) ) +#define LSB_of(s) ((s)&0xFF) +#define MSB_of(s) ((s)>>8) + +static int transferred = 0; + +static int usbat_flash_transport(struct scsi_cmnd * srb, struct us_data *us); +static int usbat_hp8200e_transport(struct scsi_cmnd *srb, struct us_data *us); + +static int init_usbat_cd(struct us_data *us); +static int init_usbat_flash(struct us_data *us); + + +/* + * The table of devices + */ +#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ + vendorName, productName, useProtocol, useTransport, \ + initFunction, flags) \ +{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ + .driver_info = (flags) } + +static const struct usb_device_id usbat_usb_ids[] = { +# include "unusual_usbat.h" + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, usbat_usb_ids); + +#undef UNUSUAL_DEV + +/* + * The flags table + */ +#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ + vendor_name, product_name, use_protocol, use_transport, \ + init_function, Flags) \ +{ \ + .vendorName = vendor_name, \ + .productName = product_name, \ + .useProtocol = use_protocol, \ + .useTransport = use_transport, \ + .initFunction = init_function, \ +} + +static const struct us_unusual_dev usbat_unusual_dev_list[] = { +# include "unusual_usbat.h" + { } /* Terminating entry */ +}; + +#undef UNUSUAL_DEV + +/* + * Convenience function to produce an ATA read/write sectors command + * Use cmd=0x20 for read, cmd=0x30 for write + */ +static void usbat_pack_ata_sector_cmd(unsigned char *buf, + unsigned char thistime, + u32 sector, unsigned char cmd) +{ + buf[0] = 0; + buf[1] = thistime; + buf[2] = sector & 0xFF; + buf[3] = (sector >> 8) & 0xFF; + buf[4] = (sector >> 16) & 0xFF; + buf[5] = 0xE0 | ((sector >> 24) & 0x0F); + buf[6] = cmd; +} + +/* + * Convenience function to get the device type (flash or hp8200) + */ +static int usbat_get_device_type(struct us_data *us) +{ + return ((struct usbat_info*)us->extra)->devicetype; +} + +/* + * Read a register from the device + */ +static int usbat_read(struct us_data *us, + unsigned char access, + unsigned char reg, + unsigned char *content) +{ + return usb_stor_ctrl_transfer(us, + us->recv_ctrl_pipe, + access | USBAT_CMD_READ_REG, + 0xC0, + (u16)reg, + 0, + content, + 1); +} + +/* + * Write to a register on the device + */ +static int usbat_write(struct us_data *us, + unsigned char access, + unsigned char reg, + unsigned char content) +{ + return usb_stor_ctrl_transfer(us, + us->send_ctrl_pipe, + access | USBAT_CMD_WRITE_REG, + 0x40, + short_pack(reg, content), + 0, + NULL, + 0); +} + +/* + * Convenience function to perform a bulk read + */ +static int usbat_bulk_read(struct us_data *us, + void* buf, + unsigned int len, + int use_sg) +{ + if (len == 0) + return USB_STOR_XFER_GOOD; + + usb_stor_dbg(us, "len = %d\n", len); + return usb_stor_bulk_transfer_sg(us, us->recv_bulk_pipe, buf, len, use_sg, NULL); +} + +/* + * Convenience function to perform a bulk write + */ +static int usbat_bulk_write(struct us_data *us, + void* buf, + unsigned int len, + int use_sg) +{ + if (len == 0) + return USB_STOR_XFER_GOOD; + + usb_stor_dbg(us, "len = %d\n", len); + return usb_stor_bulk_transfer_sg(us, us->send_bulk_pipe, buf, len, use_sg, NULL); +} + +/* + * Some USBAT-specific commands can only be executed over a command transport + * This transport allows one (len=8) or two (len=16) vendor-specific commands + * to be executed. + */ +static int usbat_execute_command(struct us_data *us, + unsigned char *commands, + unsigned int len) +{ + return usb_stor_ctrl_transfer(us, us->send_ctrl_pipe, + USBAT_CMD_EXEC_CMD, 0x40, 0, 0, + commands, len); +} + +/* + * Read the status register + */ +static int usbat_get_status(struct us_data *us, unsigned char *status) +{ + int rc; + rc = usbat_read(us, USBAT_ATA, USBAT_ATA_STATUS, status); + + usb_stor_dbg(us, "0x%02X\n", *status); + return rc; +} + +/* + * Check the device status + */ +static int usbat_check_status(struct us_data *us) +{ + unsigned char *reply = us->iobuf; + int rc; + + rc = usbat_get_status(us, reply); + if (rc != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_FAILED; + + /* error/check condition (0x51 is ok) */ + if (*reply & 0x01 && *reply != 0x51) + return USB_STOR_TRANSPORT_FAILED; + + /* device fault */ + if (*reply & 0x20) + return USB_STOR_TRANSPORT_FAILED; + + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Stores critical information in internal registers in preparation for the execution + * of a conditional usbat_read_blocks or usbat_write_blocks call. + */ +static int usbat_set_shuttle_features(struct us_data *us, + unsigned char external_trigger, + unsigned char epp_control, + unsigned char mask_byte, + unsigned char test_pattern, + unsigned char subcountH, + unsigned char subcountL) +{ + unsigned char *command = us->iobuf; + + command[0] = 0x40; + command[1] = USBAT_CMD_SET_FEAT; + + /* + * The only bit relevant to ATA access is bit 6 + * which defines 8 bit data access (set) or 16 bit (unset) + */ + command[2] = epp_control; + + /* + * If FCQ is set in the qualifier (defined in R/W cmd), then bits U0, U1, + * ET1 and ET2 define an external event to be checked for on event of a + * _read_blocks or _write_blocks operation. The read/write will not take + * place unless the defined trigger signal is active. + */ + command[3] = external_trigger; + + /* + * The resultant byte of the mask operation (see mask_byte) is compared for + * equivalence with this test pattern. If equal, the read/write will take + * place. + */ + command[4] = test_pattern; + + /* + * This value is logically ANDed with the status register field specified + * in the read/write command. + */ + command[5] = mask_byte; + + /* + * If ALQ is set in the qualifier, this field contains the address of the + * registers where the byte count should be read for transferring the data. + * If ALQ is not set, then this field contains the number of bytes to be + * transferred. + */ + command[6] = subcountL; + command[7] = subcountH; + + return usbat_execute_command(us, command, 8); +} + +/* + * Block, waiting for an ATA device to become not busy or to report + * an error condition. + */ +static int usbat_wait_not_busy(struct us_data *us, int minutes) +{ + int i; + int result; + unsigned char *status = us->iobuf; + + /* + * Synchronizing cache on a CDR could take a heck of a long time, + * but probably not more than 10 minutes or so. On the other hand, + * doing a full blank on a CDRW at speed 1 will take about 75 + * minutes! + */ + + for (i=0; i<1200+minutes*60; i++) { + + result = usbat_get_status(us, status); + + if (result!=USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + if (*status & 0x01) { /* check condition */ + result = usbat_read(us, USBAT_ATA, 0x10, status); + return USB_STOR_TRANSPORT_FAILED; + } + if (*status & 0x20) /* device fault */ + return USB_STOR_TRANSPORT_FAILED; + + if ((*status & 0x80)==0x00) { /* not busy */ + usb_stor_dbg(us, "Waited not busy for %d steps\n", i); + return USB_STOR_TRANSPORT_GOOD; + } + + if (i<500) + msleep(10); /* 5 seconds */ + else if (i<700) + msleep(50); /* 10 seconds */ + else if (i<1200) + msleep(100); /* 50 seconds */ + else + msleep(1000); /* X minutes */ + } + + usb_stor_dbg(us, "Waited not busy for %d minutes, timing out\n", + minutes); + return USB_STOR_TRANSPORT_FAILED; +} + +/* + * Read block data from the data register + */ +static int usbat_read_block(struct us_data *us, + void* buf, + unsigned short len, + int use_sg) +{ + int result; + unsigned char *command = us->iobuf; + + if (!len) + return USB_STOR_TRANSPORT_GOOD; + + command[0] = 0xC0; + command[1] = USBAT_ATA | USBAT_CMD_READ_BLOCK; + command[2] = USBAT_ATA_DATA; + command[3] = 0; + command[4] = 0; + command[5] = 0; + command[6] = LSB_of(len); + command[7] = MSB_of(len); + + result = usbat_execute_command(us, command, 8); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + result = usbat_bulk_read(us, buf, len, use_sg); + return (result == USB_STOR_XFER_GOOD ? + USB_STOR_TRANSPORT_GOOD : USB_STOR_TRANSPORT_ERROR); +} + +/* + * Write block data via the data register + */ +static int usbat_write_block(struct us_data *us, + unsigned char access, + void* buf, + unsigned short len, + int minutes, + int use_sg) +{ + int result; + unsigned char *command = us->iobuf; + + if (!len) + return USB_STOR_TRANSPORT_GOOD; + + command[0] = 0x40; + command[1] = access | USBAT_CMD_WRITE_BLOCK; + command[2] = USBAT_ATA_DATA; + command[3] = 0; + command[4] = 0; + command[5] = 0; + command[6] = LSB_of(len); + command[7] = MSB_of(len); + + result = usbat_execute_command(us, command, 8); + + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + result = usbat_bulk_write(us, buf, len, use_sg); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + return usbat_wait_not_busy(us, minutes); +} + +/* + * Process read and write requests + */ +static int usbat_hp8200e_rw_block_test(struct us_data *us, + unsigned char access, + unsigned char *registers, + unsigned char *data_out, + unsigned short num_registers, + unsigned char data_reg, + unsigned char status_reg, + unsigned char timeout, + unsigned char qualifier, + int direction, + void *buf, + unsigned short len, + int use_sg, + int minutes) +{ + int result; + unsigned int pipe = (direction == DMA_FROM_DEVICE) ? + us->recv_bulk_pipe : us->send_bulk_pipe; + + unsigned char *command = us->iobuf; + int i, j; + int cmdlen; + unsigned char *data = us->iobuf; + unsigned char *status = us->iobuf; + + BUG_ON(num_registers > US_IOBUF_SIZE/2); + + for (i=0; i<20; i++) { + + /* + * The first time we send the full command, which consists + * of downloading the SCSI command followed by downloading + * the data via a write-and-test. Any other time we only + * send the command to download the data -- the SCSI command + * is still 'active' in some sense in the device. + * + * We're only going to try sending the data 10 times. After + * that, we just return a failure. + */ + + if (i==0) { + cmdlen = 16; + /* + * Write to multiple registers + * Not really sure the 0x07, 0x17, 0xfc, 0xe7 is + * necessary here, but that's what came out of the + * trace every single time. + */ + command[0] = 0x40; + command[1] = access | USBAT_CMD_WRITE_REGS; + command[2] = 0x07; + command[3] = 0x17; + command[4] = 0xFC; + command[5] = 0xE7; + command[6] = LSB_of(num_registers*2); + command[7] = MSB_of(num_registers*2); + } else + cmdlen = 8; + + /* Conditionally read or write blocks */ + command[cmdlen-8] = (direction==DMA_TO_DEVICE ? 0x40 : 0xC0); + command[cmdlen-7] = access | + (direction==DMA_TO_DEVICE ? + USBAT_CMD_COND_WRITE_BLOCK : USBAT_CMD_COND_READ_BLOCK); + command[cmdlen-6] = data_reg; + command[cmdlen-5] = status_reg; + command[cmdlen-4] = timeout; + command[cmdlen-3] = qualifier; + command[cmdlen-2] = LSB_of(len); + command[cmdlen-1] = MSB_of(len); + + result = usbat_execute_command(us, command, cmdlen); + + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + if (i==0) { + + for (j=0; jsend_bulk_pipe) < 0) + return USB_STOR_TRANSPORT_ERROR; + } + + /* + * Read status: is the device angry, or just busy? + */ + + result = usbat_read(us, USBAT_ATA, + direction==DMA_TO_DEVICE ? + USBAT_ATA_STATUS : USBAT_ATA_ALTSTATUS, + status); + + if (result!=USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + if (*status & 0x01) /* check condition */ + return USB_STOR_TRANSPORT_FAILED; + if (*status & 0x20) /* device fault */ + return USB_STOR_TRANSPORT_FAILED; + + usb_stor_dbg(us, "Redoing %s\n", + str_write_read(direction == DMA_TO_DEVICE)); + + } else if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + else + return usbat_wait_not_busy(us, minutes); + + } + + usb_stor_dbg(us, "Bummer! %s bulk data 20 times failed\n", + direction == DMA_TO_DEVICE ? "Writing" : "Reading"); + + return USB_STOR_TRANSPORT_FAILED; +} + +/* + * Write to multiple registers: + * Allows us to write specific data to any registers. The data to be written + * gets packed in this sequence: reg0, data0, reg1, data1, ..., regN, dataN + * which gets sent through bulk out. + * Not designed for large transfers of data! + */ +static int usbat_multiple_write(struct us_data *us, + unsigned char *registers, + unsigned char *data_out, + unsigned short num_registers) +{ + int i, result; + unsigned char *data = us->iobuf; + unsigned char *command = us->iobuf; + + BUG_ON(num_registers > US_IOBUF_SIZE/2); + + /* Write to multiple registers, ATA access */ + command[0] = 0x40; + command[1] = USBAT_ATA | USBAT_CMD_WRITE_REGS; + + /* No relevance */ + command[2] = 0; + command[3] = 0; + command[4] = 0; + command[5] = 0; + + /* Number of bytes to be transferred (incl. addresses and data) */ + command[6] = LSB_of(num_registers*2); + command[7] = MSB_of(num_registers*2); + + /* The setup command */ + result = usbat_execute_command(us, command, 8); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + /* Create the reg/data, reg/data sequence */ + for (i=0; iiobuf; + + command[0] = 0xC0; + command[1] = USBAT_ATA | USBAT_CMD_COND_READ_BLOCK; + command[2] = USBAT_ATA_DATA; + command[3] = USBAT_ATA_STATUS; + command[4] = 0xFD; /* Timeout (ms); */ + command[5] = USBAT_QUAL_FCQ; + command[6] = LSB_of(len); + command[7] = MSB_of(len); + + /* Multiple block read setup command */ + result = usbat_execute_command(us, command, 8); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_FAILED; + + /* Read the blocks we just asked for */ + result = usbat_bulk_read(us, buffer, len, use_sg); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_FAILED; + + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Conditionally write blocks to device: + * Allows us to write blocks to a specific data register, based upon the + * condition that a status register can be successfully masked with a status + * qualifier. If this condition is not initially met, the write will wait + * up until a maximum amount of time has elapsed, as specified by timeout. + * The read will start when the condition is met, otherwise the command aborts. + * + * The qualifier defined here is not the value that is masked, it defines + * conditions for the write to take place. The actual masked qualifier (and + * other related details) are defined beforehand with _set_shuttle_features(). + */ +static int usbat_write_blocks(struct us_data *us, + void* buffer, + int len, + int use_sg) +{ + int result; + unsigned char *command = us->iobuf; + + command[0] = 0x40; + command[1] = USBAT_ATA | USBAT_CMD_COND_WRITE_BLOCK; + command[2] = USBAT_ATA_DATA; + command[3] = USBAT_ATA_STATUS; + command[4] = 0xFD; /* Timeout (ms) */ + command[5] = USBAT_QUAL_FCQ; + command[6] = LSB_of(len); + command[7] = MSB_of(len); + + /* Multiple block write setup command */ + result = usbat_execute_command(us, command, 8); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_FAILED; + + /* Write the data */ + result = usbat_bulk_write(us, buffer, len, use_sg); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_FAILED; + + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Read the User IO register + */ +static int usbat_read_user_io(struct us_data *us, unsigned char *data_flags) +{ + int result; + + result = usb_stor_ctrl_transfer(us, + us->recv_ctrl_pipe, + USBAT_CMD_UIO, + 0xC0, + 0, + 0, + data_flags, + USBAT_UIO_READ); + + usb_stor_dbg(us, "UIO register reads %02X\n", *data_flags); + + return result; +} + +/* + * Write to the User IO register + */ +static int usbat_write_user_io(struct us_data *us, + unsigned char enable_flags, + unsigned char data_flags) +{ + return usb_stor_ctrl_transfer(us, + us->send_ctrl_pipe, + USBAT_CMD_UIO, + 0x40, + short_pack(enable_flags, data_flags), + 0, + NULL, + USBAT_UIO_WRITE); +} + +/* + * Reset the device + * Often needed on media change. + */ +static int usbat_device_reset(struct us_data *us) +{ + int rc; + + /* + * Reset peripheral, enable peripheral control signals + * (bring reset signal up) + */ + rc = usbat_write_user_io(us, + USBAT_UIO_DRVRST | USBAT_UIO_OE1 | USBAT_UIO_OE0, + USBAT_UIO_EPAD | USBAT_UIO_1); + if (rc != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + /* + * Enable peripheral control signals + * (bring reset signal down) + */ + rc = usbat_write_user_io(us, + USBAT_UIO_OE1 | USBAT_UIO_OE0, + USBAT_UIO_EPAD | USBAT_UIO_1); + if (rc != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Enable card detect + */ +static int usbat_device_enable_cdt(struct us_data *us) +{ + int rc; + + /* Enable peripheral control signals and card detect */ + rc = usbat_write_user_io(us, + USBAT_UIO_ACKD | USBAT_UIO_OE1 | USBAT_UIO_OE0, + USBAT_UIO_EPAD | USBAT_UIO_1); + if (rc != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Determine if media is present. + */ +static int usbat_flash_check_media_present(struct us_data *us, + unsigned char *uio) +{ + if (*uio & USBAT_UIO_UI0) { + usb_stor_dbg(us, "no media detected\n"); + return USBAT_FLASH_MEDIA_NONE; + } + + return USBAT_FLASH_MEDIA_CF; +} + +/* + * Determine if media has changed since last operation + */ +static int usbat_flash_check_media_changed(struct us_data *us, + unsigned char *uio) +{ + if (*uio & USBAT_UIO_0) { + usb_stor_dbg(us, "media change detected\n"); + return USBAT_FLASH_MEDIA_CHANGED; + } + + return USBAT_FLASH_MEDIA_SAME; +} + +/* + * Check for media change / no media and handle the situation appropriately + */ +static int usbat_flash_check_media(struct us_data *us, + struct usbat_info *info) +{ + int rc; + unsigned char *uio = us->iobuf; + + rc = usbat_read_user_io(us, uio); + if (rc != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + /* Check for media existence */ + rc = usbat_flash_check_media_present(us, uio); + if (rc == USBAT_FLASH_MEDIA_NONE) { + info->sense_key = 0x02; + info->sense_asc = 0x3A; + info->sense_ascq = 0x00; + return USB_STOR_TRANSPORT_FAILED; + } + + /* Check for media change */ + rc = usbat_flash_check_media_changed(us, uio); + if (rc == USBAT_FLASH_MEDIA_CHANGED) { + + /* Reset and re-enable card detect */ + rc = usbat_device_reset(us); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; + rc = usbat_device_enable_cdt(us); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; + + msleep(50); + + rc = usbat_read_user_io(us, uio); + if (rc != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + info->sense_key = UNIT_ATTENTION; + info->sense_asc = 0x28; + info->sense_ascq = 0x00; + return USB_STOR_TRANSPORT_FAILED; + } + + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Determine whether we are controlling a flash-based reader/writer, + * or a HP8200-based CD drive. + * Sets transport functions as appropriate. + */ +static int usbat_identify_device(struct us_data *us, + struct usbat_info *info) +{ + int rc; + unsigned char status; + + if (!us || !info) + return USB_STOR_TRANSPORT_ERROR; + + rc = usbat_device_reset(us); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; + msleep(500); + + /* + * In attempt to distinguish between HP CDRW's and Flash readers, we now + * execute the IDENTIFY PACKET DEVICE command. On ATA devices (i.e. flash + * readers), this command should fail with error. On ATAPI devices (i.e. + * CDROM drives), it should succeed. + */ + rc = usbat_write(us, USBAT_ATA, USBAT_ATA_CMD, 0xA1); + if (rc != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + rc = usbat_get_status(us, &status); + if (rc != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + /* Check for error bit, or if the command 'fell through' */ + if (status == 0xA1 || !(status & 0x01)) { + /* Device is HP 8200 */ + usb_stor_dbg(us, "Detected HP8200 CDRW\n"); + info->devicetype = USBAT_DEV_HP8200; + } else { + /* Device is a CompactFlash reader/writer */ + usb_stor_dbg(us, "Detected Flash reader/writer\n"); + info->devicetype = USBAT_DEV_FLASH; + } + + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Set the transport function based on the device type + */ +static int usbat_set_transport(struct us_data *us, + struct usbat_info *info, + int devicetype) +{ + + if (!info->devicetype) + info->devicetype = devicetype; + + if (!info->devicetype) + usbat_identify_device(us, info); + + switch (info->devicetype) { + default: + return USB_STOR_TRANSPORT_ERROR; + + case USBAT_DEV_HP8200: + us->transport = usbat_hp8200e_transport; + break; + + case USBAT_DEV_FLASH: + us->transport = usbat_flash_transport; + break; + } + + return 0; +} + +/* + * Read the media capacity + */ +static int usbat_flash_get_sector_count(struct us_data *us, + struct usbat_info *info) +{ + unsigned char registers[3] = { + USBAT_ATA_SECCNT, + USBAT_ATA_DEVICE, + USBAT_ATA_CMD, + }; + unsigned char command[3] = { 0x01, 0xA0, 0xEC }; + unsigned char *reply; + unsigned char status; + int rc; + + if (!us || !info) + return USB_STOR_TRANSPORT_ERROR; + + reply = kmalloc(512, GFP_NOIO); + if (!reply) + return USB_STOR_TRANSPORT_ERROR; + + /* ATA command : IDENTIFY DEVICE */ + rc = usbat_multiple_write(us, registers, command, 3); + if (rc != USB_STOR_XFER_GOOD) { + usb_stor_dbg(us, "Gah! identify_device failed\n"); + rc = USB_STOR_TRANSPORT_ERROR; + goto leave; + } + + /* Read device status */ + if (usbat_get_status(us, &status) != USB_STOR_XFER_GOOD) { + rc = USB_STOR_TRANSPORT_ERROR; + goto leave; + } + + msleep(100); + + /* Read the device identification data */ + rc = usbat_read_block(us, reply, 512, 0); + if (rc != USB_STOR_TRANSPORT_GOOD) + goto leave; + + info->sectors = ((u32)(reply[117]) << 24) | + ((u32)(reply[116]) << 16) | + ((u32)(reply[115]) << 8) | + ((u32)(reply[114]) ); + + rc = USB_STOR_TRANSPORT_GOOD; + + leave: + kfree(reply); + return rc; +} + +/* + * Read data from device + */ +static int usbat_flash_read_data(struct us_data *us, + struct usbat_info *info, + u32 sector, + u32 sectors) +{ + unsigned char registers[7] = { + USBAT_ATA_FEATURES, + USBAT_ATA_SECCNT, + USBAT_ATA_SECNUM, + USBAT_ATA_LBA_ME, + USBAT_ATA_LBA_HI, + USBAT_ATA_DEVICE, + USBAT_ATA_STATUS, + }; + unsigned char command[7]; + unsigned char *buffer; + unsigned char thistime; + unsigned int totallen, alloclen; + int len, result; + unsigned int sg_offset = 0; + struct scatterlist *sg = NULL; + + result = usbat_flash_check_media(us, info); + if (result != USB_STOR_TRANSPORT_GOOD) + return result; + + /* + * we're working in LBA mode. according to the ATA spec, + * we can support up to 28-bit addressing. I don't know if Jumpshot + * supports beyond 24-bit addressing. It's kind of hard to test + * since it requires > 8GB CF card. + */ + + if (sector > 0x0FFFFFFF) + return USB_STOR_TRANSPORT_ERROR; + + totallen = sectors * info->ssize; + + /* + * Since we don't read more than 64 KB at a time, we have to create + * a bounce buffer and move the data a piece at a time between the + * bounce buffer and the actual transfer buffer. + */ + + alloclen = min(totallen, 65536u); + buffer = kmalloc(alloclen, GFP_NOIO); + if (buffer == NULL) + return USB_STOR_TRANSPORT_ERROR; + + do { + /* + * loop, never allocate or transfer more than 64k at once + * (min(128k, 255*info->ssize) is the real limit) + */ + len = min(totallen, alloclen); + thistime = (len / info->ssize) & 0xff; + + /* ATA command 0x20 (READ SECTORS) */ + usbat_pack_ata_sector_cmd(command, thistime, sector, 0x20); + + /* Write/execute ATA read command */ + result = usbat_multiple_write(us, registers, command, 7); + if (result != USB_STOR_TRANSPORT_GOOD) + goto leave; + + /* Read the data we just requested */ + result = usbat_read_blocks(us, buffer, len, 0); + if (result != USB_STOR_TRANSPORT_GOOD) + goto leave; + + usb_stor_dbg(us, "%d bytes\n", len); + + /* Store the data in the transfer buffer */ + usb_stor_access_xfer_buf(buffer, len, us->srb, + &sg, &sg_offset, TO_XFER_BUF); + + sector += thistime; + totallen -= len; + } while (totallen > 0); + + kfree(buffer); + return USB_STOR_TRANSPORT_GOOD; + +leave: + kfree(buffer); + return USB_STOR_TRANSPORT_ERROR; +} + +/* + * Write data to device + */ +static int usbat_flash_write_data(struct us_data *us, + struct usbat_info *info, + u32 sector, + u32 sectors) +{ + unsigned char registers[7] = { + USBAT_ATA_FEATURES, + USBAT_ATA_SECCNT, + USBAT_ATA_SECNUM, + USBAT_ATA_LBA_ME, + USBAT_ATA_LBA_HI, + USBAT_ATA_DEVICE, + USBAT_ATA_STATUS, + }; + unsigned char command[7]; + unsigned char *buffer; + unsigned char thistime; + unsigned int totallen, alloclen; + int len, result; + unsigned int sg_offset = 0; + struct scatterlist *sg = NULL; + + result = usbat_flash_check_media(us, info); + if (result != USB_STOR_TRANSPORT_GOOD) + return result; + + /* + * we're working in LBA mode. according to the ATA spec, + * we can support up to 28-bit addressing. I don't know if the device + * supports beyond 24-bit addressing. It's kind of hard to test + * since it requires > 8GB media. + */ + + if (sector > 0x0FFFFFFF) + return USB_STOR_TRANSPORT_ERROR; + + totallen = sectors * info->ssize; + + /* + * Since we don't write more than 64 KB at a time, we have to create + * a bounce buffer and move the data a piece at a time between the + * bounce buffer and the actual transfer buffer. + */ + + alloclen = min(totallen, 65536u); + buffer = kmalloc(alloclen, GFP_NOIO); + if (buffer == NULL) + return USB_STOR_TRANSPORT_ERROR; + + do { + /* + * loop, never allocate or transfer more than 64k at once + * (min(128k, 255*info->ssize) is the real limit) + */ + len = min(totallen, alloclen); + thistime = (len / info->ssize) & 0xff; + + /* Get the data from the transfer buffer */ + usb_stor_access_xfer_buf(buffer, len, us->srb, + &sg, &sg_offset, FROM_XFER_BUF); + + /* ATA command 0x30 (WRITE SECTORS) */ + usbat_pack_ata_sector_cmd(command, thistime, sector, 0x30); + + /* Write/execute ATA write command */ + result = usbat_multiple_write(us, registers, command, 7); + if (result != USB_STOR_TRANSPORT_GOOD) + goto leave; + + /* Write the data */ + result = usbat_write_blocks(us, buffer, len, 0); + if (result != USB_STOR_TRANSPORT_GOOD) + goto leave; + + sector += thistime; + totallen -= len; + } while (totallen > 0); + + kfree(buffer); + return result; + +leave: + kfree(buffer); + return USB_STOR_TRANSPORT_ERROR; +} + +/* + * Squeeze a potentially huge (> 65535 byte) read10 command into + * a little ( <= 65535 byte) ATAPI pipe + */ +static int usbat_hp8200e_handle_read10(struct us_data *us, + unsigned char *registers, + unsigned char *data, + struct scsi_cmnd *srb) +{ + int result = USB_STOR_TRANSPORT_GOOD; + unsigned char *buffer; + unsigned int len; + unsigned int sector; + unsigned int sg_offset = 0; + struct scatterlist *sg = NULL; + + usb_stor_dbg(us, "transfersize %d\n", srb->transfersize); + + if (scsi_bufflen(srb) < 0x10000) { + + result = usbat_hp8200e_rw_block_test(us, USBAT_ATA, + registers, data, 19, + USBAT_ATA_DATA, USBAT_ATA_STATUS, 0xFD, + (USBAT_QUAL_FCQ | USBAT_QUAL_ALQ), + DMA_FROM_DEVICE, + scsi_sglist(srb), + scsi_bufflen(srb), scsi_sg_count(srb), 1); + + return result; + } + + /* + * Since we're requesting more data than we can handle in + * a single read command (max is 64k-1), we will perform + * multiple reads, but each read must be in multiples of + * a sector. Luckily the sector size is in srb->transfersize + * (see linux/drivers/scsi/sr.c). + */ + + if (data[7+0] == GPCMD_READ_CD) { + len = short_pack(data[7+9], data[7+8]); + len <<= 16; + len |= data[7+7]; + usb_stor_dbg(us, "GPCMD_READ_CD: len %d\n", len); + srb->transfersize = scsi_bufflen(srb)/len; + } + + if (!srb->transfersize) { + srb->transfersize = 2048; /* A guess */ + usb_stor_dbg(us, "transfersize 0, forcing %d\n", + srb->transfersize); + } + + /* + * Since we only read in one block at a time, we have to create + * a bounce buffer and move the data a piece at a time between the + * bounce buffer and the actual transfer buffer. + */ + + len = (65535/srb->transfersize) * srb->transfersize; + usb_stor_dbg(us, "Max read is %d bytes\n", len); + len = min(len, scsi_bufflen(srb)); + buffer = kmalloc(len, GFP_NOIO); + if (buffer == NULL) /* bloody hell! */ + return USB_STOR_TRANSPORT_FAILED; + sector = short_pack(data[7+3], data[7+2]); + sector <<= 16; + sector |= short_pack(data[7+5], data[7+4]); + transferred = 0; + + while (transferred != scsi_bufflen(srb)) { + + if (len > scsi_bufflen(srb) - transferred) + len = scsi_bufflen(srb) - transferred; + + data[3] = len&0xFF; /* (cylL) = expected length (L) */ + data[4] = (len>>8)&0xFF; /* (cylH) = expected length (H) */ + + /* Fix up the SCSI command sector and num sectors */ + + data[7+2] = MSB_of(sector>>16); /* SCSI command sector */ + data[7+3] = LSB_of(sector>>16); + data[7+4] = MSB_of(sector&0xFFFF); + data[7+5] = LSB_of(sector&0xFFFF); + if (data[7+0] == GPCMD_READ_CD) + data[7+6] = 0; + data[7+7] = MSB_of(len / srb->transfersize); /* SCSI command */ + data[7+8] = LSB_of(len / srb->transfersize); /* num sectors */ + + result = usbat_hp8200e_rw_block_test(us, USBAT_ATA, + registers, data, 19, + USBAT_ATA_DATA, USBAT_ATA_STATUS, 0xFD, + (USBAT_QUAL_FCQ | USBAT_QUAL_ALQ), + DMA_FROM_DEVICE, + buffer, + len, 0, 1); + + if (result != USB_STOR_TRANSPORT_GOOD) + break; + + /* Store the data in the transfer buffer */ + usb_stor_access_xfer_buf(buffer, len, srb, + &sg, &sg_offset, TO_XFER_BUF); + + /* Update the amount transferred and the sector number */ + + transferred += len; + sector += len / srb->transfersize; + + } /* while transferred != scsi_bufflen(srb) */ + + kfree(buffer); + return result; +} + +static int usbat_select_and_test_registers(struct us_data *us) +{ + int selector; + unsigned char *status = us->iobuf; + + /* try device = master, then device = slave. */ + for (selector = 0xA0; selector <= 0xB0; selector += 0x10) { + if (usbat_write(us, USBAT_ATA, USBAT_ATA_DEVICE, selector) != + USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + if (usbat_read(us, USBAT_ATA, USBAT_ATA_STATUS, status) != + USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + if (usbat_read(us, USBAT_ATA, USBAT_ATA_DEVICE, status) != + USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + if (usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_ME, status) != + USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + if (usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_HI, status) != + USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + if (usbat_write(us, USBAT_ATA, USBAT_ATA_LBA_ME, 0x55) != + USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + if (usbat_write(us, USBAT_ATA, USBAT_ATA_LBA_HI, 0xAA) != + USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + if (usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_ME, status) != + USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + if (usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_ME, status) != + USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + } + + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Initialize the USBAT processor and the storage device + */ +static int init_usbat(struct us_data *us, int devicetype) +{ + int rc; + struct usbat_info *info; + unsigned char subcountH = USBAT_ATA_LBA_HI; + unsigned char subcountL = USBAT_ATA_LBA_ME; + unsigned char *status = us->iobuf; + + us->extra = kzalloc_obj(struct usbat_info, GFP_NOIO); + if (!us->extra) + return -ENOMEM; + + info = (struct usbat_info *) (us->extra); + + /* Enable peripheral control signals */ + rc = usbat_write_user_io(us, + USBAT_UIO_OE1 | USBAT_UIO_OE0, + USBAT_UIO_EPAD | USBAT_UIO_1); + if (rc != USB_STOR_XFER_GOOD) + return -EIO; + + usb_stor_dbg(us, "INIT 1\n"); + + msleep(2000); + + rc = usbat_read_user_io(us, status); + if (rc != USB_STOR_TRANSPORT_GOOD) + return -EIO; + + usb_stor_dbg(us, "INIT 2\n"); + + rc = usbat_read_user_io(us, status); + if (rc != USB_STOR_XFER_GOOD) + return -EIO; + + rc = usbat_read_user_io(us, status); + if (rc != USB_STOR_XFER_GOOD) + return -EIO; + + usb_stor_dbg(us, "INIT 3\n"); + + rc = usbat_select_and_test_registers(us); + if (rc != USB_STOR_TRANSPORT_GOOD) + return -EIO; + + usb_stor_dbg(us, "INIT 4\n"); + + rc = usbat_read_user_io(us, status); + if (rc != USB_STOR_XFER_GOOD) + return -EIO; + + usb_stor_dbg(us, "INIT 5\n"); + + /* Enable peripheral control signals and card detect */ + rc = usbat_device_enable_cdt(us); + if (rc != USB_STOR_TRANSPORT_GOOD) + return -EIO; + + usb_stor_dbg(us, "INIT 6\n"); + + rc = usbat_read_user_io(us, status); + if (rc != USB_STOR_XFER_GOOD) + return -EIO; + + usb_stor_dbg(us, "INIT 7\n"); + + msleep(1400); + + rc = usbat_read_user_io(us, status); + if (rc != USB_STOR_XFER_GOOD) + return -EIO; + + usb_stor_dbg(us, "INIT 8\n"); + + rc = usbat_select_and_test_registers(us); + if (rc != USB_STOR_TRANSPORT_GOOD) + return -EIO; + + usb_stor_dbg(us, "INIT 9\n"); + + /* At this point, we need to detect which device we are using */ + if (usbat_set_transport(us, info, devicetype)) + return -EIO; + + usb_stor_dbg(us, "INIT 10\n"); + + if (usbat_get_device_type(us) == USBAT_DEV_FLASH) { + subcountH = 0x02; + subcountL = 0x00; + } + rc = usbat_set_shuttle_features(us, (USBAT_FEAT_ETEN | USBAT_FEAT_ET2 | USBAT_FEAT_ET1), + 0x00, 0x88, 0x08, subcountH, subcountL); + if (rc != USB_STOR_XFER_GOOD) + return -EIO; + + usb_stor_dbg(us, "INIT 11\n"); + + return 0; +} + +/* + * Transport for the HP 8200e + */ +static int usbat_hp8200e_transport(struct scsi_cmnd *srb, struct us_data *us) +{ + int result; + unsigned char *status = us->iobuf; + unsigned char registers[32]; + unsigned char data[32]; + unsigned int len; + int i; + + len = scsi_bufflen(srb); + + /* + * Send A0 (ATA PACKET COMMAND). + * Note: I guess we're never going to get any of the ATA + * commands... just ATA Packet Commands. + */ + + registers[0] = USBAT_ATA_FEATURES; + registers[1] = USBAT_ATA_SECCNT; + registers[2] = USBAT_ATA_SECNUM; + registers[3] = USBAT_ATA_LBA_ME; + registers[4] = USBAT_ATA_LBA_HI; + registers[5] = USBAT_ATA_DEVICE; + registers[6] = USBAT_ATA_CMD; + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x00; + data[3] = len&0xFF; /* (cylL) = expected length (L) */ + data[4] = (len>>8)&0xFF; /* (cylH) = expected length (H) */ + data[5] = 0xB0; /* (device sel) = slave */ + data[6] = 0xA0; /* (command) = ATA PACKET COMMAND */ + + for (i=7; i<19; i++) { + registers[i] = 0x10; + data[i] = (i-7 >= srb->cmd_len) ? 0 : srb->cmnd[i-7]; + } + + result = usbat_get_status(us, status); + usb_stor_dbg(us, "Status = %02X\n", *status); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + if (srb->cmnd[0] == TEST_UNIT_READY) + transferred = 0; + + if (srb->sc_data_direction == DMA_TO_DEVICE) { + + result = usbat_hp8200e_rw_block_test(us, USBAT_ATA, + registers, data, 19, + USBAT_ATA_DATA, USBAT_ATA_STATUS, 0xFD, + (USBAT_QUAL_FCQ | USBAT_QUAL_ALQ), + DMA_TO_DEVICE, + scsi_sglist(srb), + len, scsi_sg_count(srb), 10); + + if (result == USB_STOR_TRANSPORT_GOOD) { + transferred += len; + usb_stor_dbg(us, "Wrote %08X bytes\n", transferred); + } + + return result; + + } else if (srb->cmnd[0] == READ_10 || + srb->cmnd[0] == GPCMD_READ_CD) { + + return usbat_hp8200e_handle_read10(us, registers, data, srb); + + } + + if (len > 0xFFFF) { + usb_stor_dbg(us, "Error: len = %08X... what do I do now?\n", + len); + return USB_STOR_TRANSPORT_ERROR; + } + + result = usbat_multiple_write(us, registers, data, 7); + + if (result != USB_STOR_TRANSPORT_GOOD) + return result; + + /* + * Write the 12-byte command header. + * + * If the command is BLANK then set the timer for 75 minutes. + * Otherwise set it for 10 minutes. + * + * NOTE: THE 8200 DOCUMENTATION STATES THAT BLANKING A CDRW + * AT SPEED 4 IS UNRELIABLE!!! + */ + + result = usbat_write_block(us, USBAT_ATA, srb->cmnd, 12, + srb->cmnd[0] == GPCMD_BLANK ? 75 : 10, 0); + + if (result != USB_STOR_TRANSPORT_GOOD) + return result; + + /* If there is response data to be read in then do it here. */ + + if (len != 0 && (srb->sc_data_direction == DMA_FROM_DEVICE)) { + + /* How many bytes to read in? Check cylL register */ + + if (usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_ME, status) != + USB_STOR_XFER_GOOD) { + return USB_STOR_TRANSPORT_ERROR; + } + + if (len > 0xFF) { /* need to read cylH also */ + len = *status; + if (usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_HI, status) != + USB_STOR_XFER_GOOD) { + return USB_STOR_TRANSPORT_ERROR; + } + len += ((unsigned int) *status)<<8; + } + else + len = *status; + + + result = usbat_read_block(us, scsi_sglist(srb), len, + scsi_sg_count(srb)); + } + + return result; +} + +/* + * Transport for USBAT02-based CompactFlash and similar storage devices + */ +static int usbat_flash_transport(struct scsi_cmnd * srb, struct us_data *us) +{ + int rc; + struct usbat_info *info = (struct usbat_info *) (us->extra); + unsigned long block, blocks; + unsigned char *ptr = us->iobuf; + static const unsigned char inquiry_response[36] = { + 0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00 + }; + + if (srb->cmnd[0] == INQUIRY) { + usb_stor_dbg(us, "INQUIRY - Returning bogus response\n"); + memcpy(ptr, inquiry_response, sizeof(inquiry_response)); + fill_inquiry_response(us, ptr, 36); + return USB_STOR_TRANSPORT_GOOD; + } + + if (srb->cmnd[0] == READ_CAPACITY) { + rc = usbat_flash_check_media(us, info); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; + + rc = usbat_flash_get_sector_count(us, info); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; + + /* hard coded 512 byte sectors as per ATA spec */ + info->ssize = 0x200; + usb_stor_dbg(us, "READ_CAPACITY: %ld sectors, %ld bytes per sector\n", + info->sectors, info->ssize); + + /* + * build the reply + * note: must return the sector number of the last sector, + * *not* the total number of sectors + */ + ((__be32 *) ptr)[0] = cpu_to_be32(info->sectors - 1); + ((__be32 *) ptr)[1] = cpu_to_be32(info->ssize); + usb_stor_set_xfer_buf(ptr, 8, srb); + + return USB_STOR_TRANSPORT_GOOD; + } + + if (srb->cmnd[0] == MODE_SELECT_10) { + usb_stor_dbg(us, "Gah! MODE_SELECT_10\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + if (srb->cmnd[0] == READ_10) { + block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) | + ((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5])); + + blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8])); + + usb_stor_dbg(us, "READ_10: read block 0x%04lx count %ld\n", + block, blocks); + return usbat_flash_read_data(us, info, block, blocks); + } + + if (srb->cmnd[0] == READ_12) { + /* + * I don't think we'll ever see a READ_12 but support it anyway + */ + block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) | + ((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5])); + + blocks = ((u32)(srb->cmnd[6]) << 24) | ((u32)(srb->cmnd[7]) << 16) | + ((u32)(srb->cmnd[8]) << 8) | ((u32)(srb->cmnd[9])); + + usb_stor_dbg(us, "READ_12: read block 0x%04lx count %ld\n", + block, blocks); + return usbat_flash_read_data(us, info, block, blocks); + } + + if (srb->cmnd[0] == WRITE_10) { + block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) | + ((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5])); + + blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8])); + + usb_stor_dbg(us, "WRITE_10: write block 0x%04lx count %ld\n", + block, blocks); + return usbat_flash_write_data(us, info, block, blocks); + } + + if (srb->cmnd[0] == WRITE_12) { + /* + * I don't think we'll ever see a WRITE_12 but support it anyway + */ + block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) | + ((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5])); + + blocks = ((u32)(srb->cmnd[6]) << 24) | ((u32)(srb->cmnd[7]) << 16) | + ((u32)(srb->cmnd[8]) << 8) | ((u32)(srb->cmnd[9])); + + usb_stor_dbg(us, "WRITE_12: write block 0x%04lx count %ld\n", + block, blocks); + return usbat_flash_write_data(us, info, block, blocks); + } + + + if (srb->cmnd[0] == TEST_UNIT_READY) { + usb_stor_dbg(us, "TEST_UNIT_READY\n"); + + rc = usbat_flash_check_media(us, info); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; + + return usbat_check_status(us); + } + + if (srb->cmnd[0] == REQUEST_SENSE) { + usb_stor_dbg(us, "REQUEST_SENSE\n"); + + memset(ptr, 0, 18); + ptr[0] = 0xF0; + ptr[2] = info->sense_key; + ptr[7] = 11; + ptr[12] = info->sense_asc; + ptr[13] = info->sense_ascq; + usb_stor_set_xfer_buf(ptr, 18, srb); + + return USB_STOR_TRANSPORT_GOOD; + } + + if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) { + /* + * sure. whatever. not like we can stop the user from popping + * the media out of the device (no locking doors, etc) + */ + return USB_STOR_TRANSPORT_GOOD; + } + + usb_stor_dbg(us, "Gah! Unknown command: %d (0x%x)\n", + srb->cmnd[0], srb->cmnd[0]); + info->sense_key = 0x05; + info->sense_asc = 0x20; + info->sense_ascq = 0x00; + return USB_STOR_TRANSPORT_FAILED; +} + +static int init_usbat_cd(struct us_data *us) +{ + return init_usbat(us, USBAT_DEV_HP8200); +} + +static int init_usbat_flash(struct us_data *us) +{ + return init_usbat(us, USBAT_DEV_FLASH); +} + +static struct scsi_host_template usbat_host_template; + +static int usbat_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct us_data *us; + int result; + + result = usb_stor_probe1(&us, intf, id, + (id - usbat_usb_ids) + usbat_unusual_dev_list, + &usbat_host_template); + if (result) + return result; + + /* + * The actual transport will be determined later by the + * initialization routine; this is just a placeholder. + */ + us->transport_name = "Shuttle USBAT"; + us->transport = usbat_flash_transport; + us->transport_reset = usb_stor_CB_reset; + us->max_lun = 0; + + result = usb_stor_probe2(us); + return result; +} + +static struct usb_driver usbat_driver = { + .name = DRV_NAME, + .probe = usbat_probe, + .disconnect = usb_stor_disconnect, + .suspend = usb_stor_suspend, + .resume = usb_stor_resume, + .reset_resume = usb_stor_reset_resume, + .pre_reset = usb_stor_pre_reset, + .post_reset = usb_stor_post_reset, + .id_table = usbat_usb_ids, + .soft_unbind = 1, + .no_dynamic_id = 1, +}; + +module_usb_stor_driver(usbat_driver, usbat_host_template, DRV_NAME); diff --git a/drivers/usb/storage/sierra_ms.c b/drivers/usb/storage/sierra_ms.c new file mode 100644 index 000000000..c5e3eeeb2 --- /dev/null +++ b/drivers/usb/storage/sierra_ms.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include + +#include "usb.h" +#include "transport.h" +#include "protocol.h" +#include "scsiglue.h" +#include "sierra_ms.h" +#include "debug.h" + +#define SWIMS_USB_REQUEST_SetSwocMode 0x0B +#define SWIMS_USB_REQUEST_GetSwocInfo 0x0A +#define SWIMS_USB_INDEX_SetMode 0x0000 +#define SWIMS_SET_MODE_Modem 0x0001 + +#define TRU_NORMAL 0x01 +#define TRU_FORCE_MS 0x02 +#define TRU_FORCE_MODEM 0x03 + +static unsigned int swi_tru_install = 1; +module_param(swi_tru_install, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(swi_tru_install, "TRU-Install mode (1=Full Logic (def)," + " 2=Force CD-Rom, 3=Force Modem)"); + +struct swoc_info { + __u8 rev; + __u8 reserved[8]; + __u16 LinuxSKU; + __u16 LinuxVer; + __u8 reserved2[47]; +} __attribute__((__packed__)); + +static bool containsFullLinuxPackage(struct swoc_info *swocInfo) +{ + if ((swocInfo->LinuxSKU >= 0x2100 && swocInfo->LinuxSKU <= 0x2FFF) || + (swocInfo->LinuxSKU >= 0x7100 && swocInfo->LinuxSKU <= 0x7FFF)) + return true; + else + return false; +} + +static int sierra_set_ms_mode(struct usb_device *udev, __u16 eSWocMode) +{ + int result; + dev_dbg(&udev->dev, "SWIMS: %s", "DEVICE MODE SWITCH\n"); + result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + SWIMS_USB_REQUEST_SetSwocMode, /* __u8 request */ + USB_TYPE_VENDOR | USB_DIR_OUT, /* __u8 request type */ + eSWocMode, /* __u16 value */ + 0x0000, /* __u16 index */ + NULL, /* void *data */ + 0, /* __u16 size */ + USB_CTRL_SET_TIMEOUT); /* int timeout */ + return result; +} + + +static int sierra_get_swoc_info(struct usb_device *udev, + struct swoc_info *swocInfo) +{ + int result; + + dev_dbg(&udev->dev, "SWIMS: Attempting to get TRU-Install info\n"); + + result = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + SWIMS_USB_REQUEST_GetSwocInfo, /* __u8 request */ + USB_TYPE_VENDOR | USB_DIR_IN, /* __u8 request type */ + 0, /* __u16 value */ + 0, /* __u16 index */ + (void *) swocInfo, /* void *data */ + sizeof(struct swoc_info), /* __u16 size */ + USB_CTRL_SET_TIMEOUT); /* int timeout */ + + swocInfo->LinuxSKU = le16_to_cpu(swocInfo->LinuxSKU); + swocInfo->LinuxVer = le16_to_cpu(swocInfo->LinuxVer); + return result; +} + +static void debug_swoc(const struct device *dev, struct swoc_info *swocInfo) +{ + dev_dbg(dev, "SWIMS: SWoC Rev: %02d\n", swocInfo->rev); + dev_dbg(dev, "SWIMS: Linux SKU: %04X\n", swocInfo->LinuxSKU); + dev_dbg(dev, "SWIMS: Linux Version: %04X\n", swocInfo->LinuxVer); +} + + +static ssize_t truinst_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct swoc_info *swocInfo; + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *udev = interface_to_usbdev(intf); + int result; + if (swi_tru_install == TRU_FORCE_MS) { + result = sysfs_emit(buf, "Forced Mass Storage\n"); + } else { + swocInfo = kmalloc_obj(struct swoc_info); + if (!swocInfo) { + sysfs_emit(buf, "Error\n"); + return -ENOMEM; + } + result = sierra_get_swoc_info(udev, swocInfo); + if (result < 0) { + dev_dbg(dev, "SWIMS: failed SWoC query\n"); + kfree(swocInfo); + sysfs_emit(buf, "Error\n"); + return -EIO; + } + debug_swoc(dev, swocInfo); + result = sysfs_emit(buf, + "REV=%02d SKU=%04X VER=%04X\n", + swocInfo->rev, + swocInfo->LinuxSKU, + swocInfo->LinuxVer); + kfree(swocInfo); + } + return result; +} +static DEVICE_ATTR_RO(truinst); + +int sierra_ms_init(struct us_data *us) +{ + int result, retries; + struct swoc_info *swocInfo; + struct usb_device *udev; + + udev = us->pusb_dev; + + /* Force Modem mode */ + if (swi_tru_install == TRU_FORCE_MODEM) { + usb_stor_dbg(us, "SWIMS: Forcing Modem Mode\n"); + result = sierra_set_ms_mode(udev, SWIMS_SET_MODE_Modem); + if (result < 0) + usb_stor_dbg(us, "SWIMS: Failed to switch to modem mode\n"); + return -EIO; + } + /* Force Mass Storage mode (keep CD-Rom) */ + else if (swi_tru_install == TRU_FORCE_MS) { + usb_stor_dbg(us, "SWIMS: Forcing Mass Storage Mode\n"); + goto complete; + } + /* Normal TRU-Install Logic */ + else { + usb_stor_dbg(us, "SWIMS: Normal SWoC Logic\n"); + + swocInfo = kmalloc_obj(struct swoc_info); + if (!swocInfo) + return -ENOMEM; + + retries = 3; + do { + retries--; + result = sierra_get_swoc_info(udev, swocInfo); + if (result < 0) { + usb_stor_dbg(us, "SWIMS: Failed SWoC query\n"); + schedule_timeout_uninterruptible(2*HZ); + } + } while (retries && result < 0); + + if (result < 0) { + usb_stor_dbg(us, "SWIMS: Completely failed SWoC query\n"); + kfree(swocInfo); + return -EIO; + } + + debug_swoc(&us->pusb_dev->dev, swocInfo); + + /* + * If there is not Linux software on the TRU-Install device + * then switch to modem mode + */ + if (!containsFullLinuxPackage(swocInfo)) { + usb_stor_dbg(us, "SWIMS: Switching to Modem Mode\n"); + result = sierra_set_ms_mode(udev, + SWIMS_SET_MODE_Modem); + if (result < 0) + usb_stor_dbg(us, "SWIMS: Failed to switch modem\n"); + kfree(swocInfo); + return -EIO; + } + kfree(swocInfo); + } +complete: + return device_create_file(&us->pusb_intf->dev, &dev_attr_truinst); +} + diff --git a/drivers/usb/storage/sierra_ms.h b/drivers/usb/storage/sierra_ms.h new file mode 100644 index 000000000..3e9da537d --- /dev/null +++ b/drivers/usb/storage/sierra_ms.h @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _SIERRA_MS_H_ +#define _SIERRA_MS_H_ +extern int sierra_ms_init(struct us_data *us); +#endif diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c new file mode 100644 index 000000000..9a4bf86e7 --- /dev/null +++ b/drivers/usb/storage/transport.c @@ -0,0 +1,1462 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for USB Mass Storage compliant devices + * + * Current development and maintenance by: + * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * Developed with the assistance of: + * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org) + * (c) 2000 Stephen J. Gowdy (SGowdy@lbl.gov) + * (c) 2002 Alan Stern + * + * Initial work by: + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "usb.h" +#include "transport.h" +#include "protocol.h" +#include "scsiglue.h" +#include "debug.h" + +#include +#include "../../scsi/sd.h" + + +/*********************************************************************** + * Data transfer routines + ***********************************************************************/ + +/* + * This is subtle, so pay attention: + * --------------------------------- + * We're very concerned about races with a command abort. Hanging this code + * is a sure fire way to hang the kernel. (Note that this discussion applies + * only to transactions resulting from a scsi queued-command, since only + * these transactions are subject to a scsi abort. Other transactions, such + * as those occurring during device-specific initialization, must be handled + * by a separate code path.) + * + * The abort function (usb_storage_command_abort() in scsiglue.c) first + * sets the machine state and the ABORTING bit in us->dflags to prevent + * new URBs from being submitted. It then calls usb_stor_stop_transport() + * below, which atomically tests-and-clears the URB_ACTIVE bit in us->dflags + * to see if the current_urb needs to be stopped. Likewise, the SG_ACTIVE + * bit is tested to see if the current_sg scatter-gather request needs to be + * stopped. The timeout callback routine does much the same thing. + * + * When a disconnect occurs, the DISCONNECTING bit in us->dflags is set to + * prevent new URBs from being submitted, and usb_stor_stop_transport() is + * called to stop any ongoing requests. + * + * The submit function first verifies that the submitting is allowed + * (neither ABORTING nor DISCONNECTING bits are set) and that the submit + * completes without errors, and only then sets the URB_ACTIVE bit. This + * prevents the stop_transport() function from trying to cancel the URB + * while the submit call is underway. Next, the submit function must test + * the flags to see if an abort or disconnect occurred during the submission + * or before the URB_ACTIVE bit was set. If so, it's essential to cancel + * the URB if it hasn't been cancelled already (i.e., if the URB_ACTIVE bit + * is still set). Either way, the function must then wait for the URB to + * finish. Note that the URB can still be in progress even after a call to + * usb_unlink_urb() returns. + * + * The idea is that (1) once the ABORTING or DISCONNECTING bit is set, + * either the stop_transport() function or the submitting function + * is guaranteed to call usb_unlink_urb() for an active URB, + * and (2) test_and_clear_bit() prevents usb_unlink_urb() from being + * called more than once or from being called during usb_submit_urb(). + */ + +/* + * This is the completion handler which will wake us up when an URB + * completes. + */ +static void usb_stor_blocking_completion(struct urb *urb) +{ + struct completion *urb_done_ptr = urb->context; + + complete(urb_done_ptr); +} + +/* + * This is the common part of the URB message submission code + * + * All URBs from the usb-storage driver involved in handling a queued scsi + * command _must_ pass through this function (or something like it) for the + * abort mechanisms to work properly. + */ +static int usb_stor_msg_common(struct us_data *us, int timeout) +{ + struct completion urb_done; + long timeleft; + int status; + + /* don't submit URBs during abort processing */ + if (test_bit(US_FLIDX_ABORTING, &us->dflags)) + return -EIO; + + /* set up data structures for the wakeup system */ + init_completion(&urb_done); + + /* fill the common fields in the URB */ + us->current_urb->context = &urb_done; + us->current_urb->transfer_flags = 0; + + /* + * we assume that if transfer_buffer isn't us->iobuf then it + * hasn't been mapped for DMA. Yes, this is clunky, but it's + * easier than always having the caller tell us whether the + * transfer buffer has already been mapped. + */ + if (us->current_urb->transfer_buffer == us->iobuf) + us->current_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + us->current_urb->transfer_dma = us->iobuf_dma; + + /* submit the URB */ + status = usb_submit_urb(us->current_urb, GFP_NOIO); + if (status) { + /* something went wrong */ + return status; + } + + /* + * since the URB has been submitted successfully, it's now okay + * to cancel it + */ + set_bit(US_FLIDX_URB_ACTIVE, &us->dflags); + + /* did an abort occur during the submission? */ + if (test_bit(US_FLIDX_ABORTING, &us->dflags)) { + + /* cancel the URB, if it hasn't been cancelled already */ + if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags)) { + usb_stor_dbg(us, "-- cancelling URB\n"); + usb_unlink_urb(us->current_urb); + } + } + + /* wait for the completion of the URB */ + timeleft = wait_for_completion_interruptible_timeout( + &urb_done, timeout ? : MAX_SCHEDULE_TIMEOUT); + + clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags); + + if (timeleft <= 0) { + usb_stor_dbg(us, "%s -- cancelling URB\n", + timeleft == 0 ? "Timeout" : "Signal"); + usb_kill_urb(us->current_urb); + } + + /* return the URB status */ + return us->current_urb->status; +} + +/* + * Transfer one control message, with timeouts, and allowing early + * termination. Return codes are usual -Exxx, *not* USB_STOR_XFER_xxx. + */ +int usb_stor_control_msg(struct us_data *us, unsigned int pipe, + u8 request, u8 requesttype, u16 value, u16 index, + void *data, u16 size, int timeout) +{ + int status; + + usb_stor_dbg(us, "rq=%02x rqtype=%02x value=%04x index=%02x len=%u\n", + request, requesttype, value, index, size); + + /* fill in the devrequest structure */ + us->cr->bRequestType = requesttype; + us->cr->bRequest = request; + us->cr->wValue = cpu_to_le16(value); + us->cr->wIndex = cpu_to_le16(index); + us->cr->wLength = cpu_to_le16(size); + + /* fill and submit the URB */ + usb_fill_control_urb(us->current_urb, us->pusb_dev, pipe, + (unsigned char*) us->cr, data, size, + usb_stor_blocking_completion, NULL); + status = usb_stor_msg_common(us, timeout); + + /* return the actual length of the data transferred if no error */ + if (status == 0) + status = us->current_urb->actual_length; + return status; +} +EXPORT_SYMBOL_GPL(usb_stor_control_msg); + +/* + * This is a version of usb_clear_halt() that allows early termination and + * doesn't read the status from the device -- this is because some devices + * crash their internal firmware when the status is requested after a halt. + * + * A definitive list of these 'bad' devices is too difficult to maintain or + * make complete enough to be useful. This problem was first observed on the + * Hagiwara FlashGate DUAL unit. However, bus traces reveal that neither + * MacOS nor Windows checks the status after clearing a halt. + * + * Since many vendors in this space limit their testing to interoperability + * with these two OSes, specification violations like this one are common. + */ +int usb_stor_clear_halt(struct us_data *us, unsigned int pipe) +{ + int result; + int endp = usb_pipeendpoint(pipe); + + if (usb_pipein (pipe)) + endp |= USB_DIR_IN; + + result = usb_stor_control_msg(us, us->send_ctrl_pipe, + USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, + USB_ENDPOINT_HALT, endp, + NULL, 0, 3*HZ); + + if (result >= 0) + usb_reset_endpoint(us->pusb_dev, endp); + + usb_stor_dbg(us, "result = %d\n", result); + return result; +} +EXPORT_SYMBOL_GPL(usb_stor_clear_halt); + + +/* + * Interpret the results of a URB transfer + * + * This function prints appropriate debugging messages, clears halts on + * non-control endpoints, and translates the status to the corresponding + * USB_STOR_XFER_xxx return code. + */ +static int interpret_urb_result(struct us_data *us, unsigned int pipe, + unsigned int length, int result, unsigned int partial) +{ + usb_stor_dbg(us, "Status code %d; transferred %u/%u\n", + result, partial, length); + switch (result) { + + /* no error code; did we send all the data? */ + case 0: + if (partial != length) { + usb_stor_dbg(us, "-- short transfer\n"); + return USB_STOR_XFER_SHORT; + } + + usb_stor_dbg(us, "-- transfer complete\n"); + return USB_STOR_XFER_GOOD; + + /* stalled */ + case -EPIPE: + /* + * for control endpoints, (used by CB[I]) a stall indicates + * a failed command + */ + if (usb_pipecontrol(pipe)) { + usb_stor_dbg(us, "-- stall on control pipe\n"); + return USB_STOR_XFER_STALLED; + } + + /* for other sorts of endpoint, clear the stall */ + usb_stor_dbg(us, "clearing endpoint halt for pipe 0x%x\n", + pipe); + if (usb_stor_clear_halt(us, pipe) < 0) + return USB_STOR_XFER_ERROR; + return USB_STOR_XFER_STALLED; + + /* babble - the device tried to send more than we wanted to read */ + case -EOVERFLOW: + usb_stor_dbg(us, "-- babble\n"); + return USB_STOR_XFER_LONG; + + /* the transfer was cancelled by abort, disconnect, or timeout */ + case -ECONNRESET: + usb_stor_dbg(us, "-- transfer cancelled\n"); + return USB_STOR_XFER_ERROR; + + /* short scatter-gather read transfer */ + case -EREMOTEIO: + usb_stor_dbg(us, "-- short read transfer\n"); + return USB_STOR_XFER_SHORT; + + /* abort or disconnect in progress */ + case -EIO: + usb_stor_dbg(us, "-- abort or disconnect in progress\n"); + return USB_STOR_XFER_ERROR; + + /* the catch-all error case */ + default: + usb_stor_dbg(us, "-- unknown error\n"); + return USB_STOR_XFER_ERROR; + } +} + +/* + * Transfer one control message, without timeouts, but allowing early + * termination. Return codes are USB_STOR_XFER_xxx. + */ +int usb_stor_ctrl_transfer(struct us_data *us, unsigned int pipe, + u8 request, u8 requesttype, u16 value, u16 index, + void *data, u16 size) +{ + int result; + + usb_stor_dbg(us, "rq=%02x rqtype=%02x value=%04x index=%02x len=%u\n", + request, requesttype, value, index, size); + + /* fill in the devrequest structure */ + us->cr->bRequestType = requesttype; + us->cr->bRequest = request; + us->cr->wValue = cpu_to_le16(value); + us->cr->wIndex = cpu_to_le16(index); + us->cr->wLength = cpu_to_le16(size); + + /* fill and submit the URB */ + usb_fill_control_urb(us->current_urb, us->pusb_dev, pipe, + (unsigned char*) us->cr, data, size, + usb_stor_blocking_completion, NULL); + result = usb_stor_msg_common(us, 0); + + return interpret_urb_result(us, pipe, size, result, + us->current_urb->actual_length); +} +EXPORT_SYMBOL_GPL(usb_stor_ctrl_transfer); + +/* + * Receive one interrupt buffer, without timeouts, but allowing early + * termination. Return codes are USB_STOR_XFER_xxx. + * + * This routine always uses us->recv_intr_pipe as the pipe and + * us->ep_bInterval as the interrupt interval. + */ +static int usb_stor_intr_transfer(struct us_data *us, void *buf, + unsigned int length) +{ + int result; + unsigned int pipe = us->recv_intr_pipe; + unsigned int maxp; + + usb_stor_dbg(us, "xfer %u bytes\n", length); + + /* calculate the max packet size */ + maxp = usb_maxpacket(us->pusb_dev, pipe); + if (maxp > length) + maxp = length; + + /* fill and submit the URB */ + usb_fill_int_urb(us->current_urb, us->pusb_dev, pipe, buf, + maxp, usb_stor_blocking_completion, NULL, + us->ep_bInterval); + result = usb_stor_msg_common(us, 0); + + return interpret_urb_result(us, pipe, length, result, + us->current_urb->actual_length); +} + +/* + * Transfer one buffer via bulk pipe, without timeouts, but allowing early + * termination. Return codes are USB_STOR_XFER_xxx. If the bulk pipe + * stalls during the transfer, the halt is automatically cleared. + */ +int usb_stor_bulk_transfer_buf(struct us_data *us, unsigned int pipe, + void *buf, unsigned int length, unsigned int *act_len) +{ + int result; + + usb_stor_dbg(us, "xfer %u bytes\n", length); + + /* fill and submit the URB */ + usb_fill_bulk_urb(us->current_urb, us->pusb_dev, pipe, buf, length, + usb_stor_blocking_completion, NULL); + result = usb_stor_msg_common(us, 0); + + /* store the actual length of the data transferred */ + if (act_len) + *act_len = us->current_urb->actual_length; + return interpret_urb_result(us, pipe, length, result, + us->current_urb->actual_length); +} +EXPORT_SYMBOL_GPL(usb_stor_bulk_transfer_buf); + +/* + * Transfer a scatter-gather list via bulk transfer + * + * This function does basically the same thing as usb_stor_bulk_transfer_buf() + * above, but it uses the usbcore scatter-gather library. + */ +static int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe, + struct scatterlist *sg, int num_sg, unsigned int length, + unsigned int *act_len) +{ + int result; + + /* don't submit s-g requests during abort processing */ + if (test_bit(US_FLIDX_ABORTING, &us->dflags)) + goto usb_stor_xfer_error; + + /* initialize the scatter-gather request block */ + usb_stor_dbg(us, "xfer %u bytes, %d entries\n", length, num_sg); + result = usb_sg_init(&us->current_sg, us->pusb_dev, pipe, 0, + sg, num_sg, length, GFP_NOIO); + if (result) { + usb_stor_dbg(us, "usb_sg_init returned %d\n", result); + goto usb_stor_xfer_error; + } + + /* + * since the block has been initialized successfully, it's now + * okay to cancel it + */ + set_bit(US_FLIDX_SG_ACTIVE, &us->dflags); + + /* did an abort occur during the submission? */ + if (test_bit(US_FLIDX_ABORTING, &us->dflags)) { + + /* cancel the request, if it hasn't been cancelled already */ + if (test_and_clear_bit(US_FLIDX_SG_ACTIVE, &us->dflags)) { + usb_stor_dbg(us, "-- cancelling sg request\n"); + usb_sg_cancel(&us->current_sg); + } + } + + /* wait for the completion of the transfer */ + usb_sg_wait(&us->current_sg); + clear_bit(US_FLIDX_SG_ACTIVE, &us->dflags); + + result = us->current_sg.status; + if (act_len) + *act_len = us->current_sg.bytes; + return interpret_urb_result(us, pipe, length, result, + us->current_sg.bytes); + +usb_stor_xfer_error: + if (act_len) + *act_len = 0; + return USB_STOR_XFER_ERROR; +} + +/* + * Common used function. Transfer a complete command + * via usb_stor_bulk_transfer_sglist() above. Set cmnd resid + */ +int usb_stor_bulk_srb(struct us_data* us, unsigned int pipe, + struct scsi_cmnd* srb) +{ + unsigned int partial; + int result = usb_stor_bulk_transfer_sglist(us, pipe, scsi_sglist(srb), + scsi_sg_count(srb), scsi_bufflen(srb), + &partial); + + scsi_set_resid(srb, scsi_bufflen(srb) - partial); + return result; +} +EXPORT_SYMBOL_GPL(usb_stor_bulk_srb); + +/* + * Transfer an entire SCSI command's worth of data payload over the bulk + * pipe. + * + * Note that this uses usb_stor_bulk_transfer_buf() and + * usb_stor_bulk_transfer_sglist() to achieve its goals -- + * this function simply determines whether we're going to use + * scatter-gather or not, and acts appropriately. + */ +int usb_stor_bulk_transfer_sg(struct us_data* us, unsigned int pipe, + void *buf, unsigned int length_left, int use_sg, int *residual) +{ + int result; + unsigned int partial; + + /* are we scatter-gathering? */ + if (use_sg) { + /* use the usb core scatter-gather primitives */ + result = usb_stor_bulk_transfer_sglist(us, pipe, + (struct scatterlist *) buf, use_sg, + length_left, &partial); + length_left -= partial; + } else { + /* no scatter-gather, just make the request */ + result = usb_stor_bulk_transfer_buf(us, pipe, buf, + length_left, &partial); + length_left -= partial; + } + + /* store the residual and return the error code */ + if (residual) + *residual = length_left; + return result; +} +EXPORT_SYMBOL_GPL(usb_stor_bulk_transfer_sg); + +/*********************************************************************** + * Transport routines + ***********************************************************************/ + +/* + * There are so many devices that report the capacity incorrectly, + * this routine was written to counteract some of the resulting + * problems. + */ +static void last_sector_hacks(struct us_data *us, struct scsi_cmnd *srb) +{ + struct gendisk *disk; + struct scsi_disk *sdkp; + u32 sector; + + /* To Report "Medium Error: Record Not Found */ + static const unsigned char record_not_found[18] = { + [0] = 0x70, /* current error */ + [2] = MEDIUM_ERROR, /* = 0x03 */ + [7] = 0x0a, /* additional length */ + [12] = 0x14 /* Record Not Found */ + }; + + /* + * If last-sector problems can't occur, whether because the + * capacity was already decremented or because the device is + * known to report the correct capacity, then we don't need + * to do anything. + */ + if (!us->use_last_sector_hacks) + return; + + /* Was this command a READ(10) or a WRITE(10)? */ + if (srb->cmnd[0] != READ_10 && srb->cmnd[0] != WRITE_10) + goto done; + + /* Did this command access the last sector? */ + sector = (srb->cmnd[2] << 24) | (srb->cmnd[3] << 16) | + (srb->cmnd[4] << 8) | (srb->cmnd[5]); + disk = scsi_cmd_to_rq(srb)->q->disk; + if (!disk) + goto done; + sdkp = scsi_disk(disk); + if (!sdkp) + goto done; + if (sector + 1 != sdkp->capacity) + goto done; + + if (srb->result == SAM_STAT_GOOD && scsi_get_resid(srb) == 0) { + + /* + * The command succeeded. We know this device doesn't + * have the last-sector bug, so stop checking it. + */ + us->use_last_sector_hacks = 0; + + } else { + /* + * The command failed. Allow up to 3 retries in case this + * is some normal sort of failure. After that, assume the + * capacity is wrong and we're trying to access the sector + * beyond the end. Replace the result code and sense data + * with values that will cause the SCSI core to fail the + * command immediately, instead of going into an infinite + * (or even just a very long) retry loop. + */ + if (++us->last_sector_retries < 3) + return; + srb->result = SAM_STAT_CHECK_CONDITION; + memcpy(srb->sense_buffer, record_not_found, + sizeof(record_not_found)); + } + + done: + /* + * Don't reset the retry counter for TEST UNIT READY commands, + * because they get issued after device resets which might be + * caused by a failed last-sector access. + */ + if (srb->cmnd[0] != TEST_UNIT_READY) + us->last_sector_retries = 0; +} + +/* + * Invoke the transport and basic error-handling/recovery methods + * + * This is used by the protocol layers to actually send the message to + * the device and receive the response. + */ +void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) +{ + int need_auto_sense; + int result; + + /* send the command to the transport layer */ + scsi_set_resid(srb, 0); + result = us->transport(srb, us); + + /* + * if the command gets aborted by the higher layers, we need to + * short-circuit all other processing + */ + if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { + usb_stor_dbg(us, "-- command was aborted\n"); + srb->result = DID_ABORT << 16; + goto Handle_Errors; + } + + /* if there is a transport error, reset and don't auto-sense */ + if (result == USB_STOR_TRANSPORT_ERROR) { + usb_stor_dbg(us, "-- transport indicates error, resetting\n"); + srb->result = DID_ERROR << 16; + goto Handle_Errors; + } + + /* if the transport provided its own sense data, don't auto-sense */ + if (result == USB_STOR_TRANSPORT_NO_SENSE) { + srb->result = SAM_STAT_CHECK_CONDITION; + last_sector_hacks(us, srb); + return; + } + + srb->result = SAM_STAT_GOOD; + + /* + * Determine if we need to auto-sense + * + * I normally don't use a flag like this, but it's almost impossible + * to understand what's going on here if I don't. + */ + need_auto_sense = 0; + + /* + * If we're running the CB transport, which is incapable + * of determining status on its own, we will auto-sense + * unless the operation involved a data-in transfer. Devices + * can signal most data-in errors by stalling the bulk-in pipe. + */ + if ((us->protocol == USB_PR_CB || us->protocol == USB_PR_DPCM_USB) && + srb->sc_data_direction != DMA_FROM_DEVICE) { + usb_stor_dbg(us, "-- CB transport device requiring auto-sense\n"); + need_auto_sense = 1; + } + + /* Some devices (Kindle) require another command after SYNC CACHE */ + if ((us->fflags & US_FL_SENSE_AFTER_SYNC) && + srb->cmnd[0] == SYNCHRONIZE_CACHE) { + usb_stor_dbg(us, "-- sense after SYNC CACHE\n"); + need_auto_sense = 1; + } + + /* + * If we have a failure, we're going to do a REQUEST_SENSE + * automatically. Note that we differentiate between a command + * "failure" and an "error" in the transport mechanism. + */ + if (result == USB_STOR_TRANSPORT_FAILED) { + usb_stor_dbg(us, "-- transport indicates command failure\n"); + need_auto_sense = 1; + } + + /* + * Determine if this device is SAT by seeing if the + * command executed successfully. Otherwise we'll have + * to wait for at least one CHECK_CONDITION to determine + * SANE_SENSE support + */ + if (unlikely((srb->cmnd[0] == ATA_16 || srb->cmnd[0] == ATA_12) && + result == USB_STOR_TRANSPORT_GOOD && + !(us->fflags & US_FL_SANE_SENSE) && + !(us->fflags & US_FL_BAD_SENSE) && + !(srb->cmnd[2] & 0x20))) { + usb_stor_dbg(us, "-- SAT supported, increasing auto-sense\n"); + us->fflags |= US_FL_SANE_SENSE; + } + + /* + * A short transfer on a command where we don't expect it + * is unusual, but it doesn't mean we need to auto-sense. + */ + if ((scsi_get_resid(srb) > 0) && + !((srb->cmnd[0] == REQUEST_SENSE) || + (srb->cmnd[0] == INQUIRY) || + (srb->cmnd[0] == MODE_SENSE) || + (srb->cmnd[0] == LOG_SENSE) || + (srb->cmnd[0] == MODE_SENSE_10))) { + usb_stor_dbg(us, "-- unexpectedly short transfer\n"); + } + + /* Now, if we need to do the auto-sense, let's do it */ + if (need_auto_sense) { + int temp_result; + struct scsi_eh_save ses; + int sense_size = US_SENSE_SIZE; + struct scsi_sense_hdr sshdr; + const u8 *scdd; + u8 fm_ili; + + /* device supports and needs bigger sense buffer */ + if (us->fflags & US_FL_SANE_SENSE) + sense_size = ~0; +Retry_Sense: + usb_stor_dbg(us, "Issuing auto-REQUEST_SENSE\n"); + + scsi_eh_prep_cmnd(srb, &ses, NULL, 0, sense_size); + + /* FIXME: we must do the protocol translation here */ + if (us->subclass == USB_SC_RBC || us->subclass == USB_SC_SCSI || + us->subclass == USB_SC_CYP_ATACB) + srb->cmd_len = 6; + else + srb->cmd_len = 12; + + /* issue the auto-sense command */ + scsi_set_resid(srb, 0); + temp_result = us->transport(us->srb, us); + + /* let's clean up right away */ + scsi_eh_restore_cmnd(srb, &ses); + + if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { + usb_stor_dbg(us, "-- auto-sense aborted\n"); + srb->result = DID_ABORT << 16; + + /* If SANE_SENSE caused this problem, disable it */ + if (sense_size != US_SENSE_SIZE) { + us->fflags &= ~US_FL_SANE_SENSE; + us->fflags |= US_FL_BAD_SENSE; + } + goto Handle_Errors; + } + + /* + * Some devices claim to support larger sense but fail when + * trying to request it. When a transport failure happens + * using US_FS_SANE_SENSE, we always retry with a standard + * (small) sense request. This fixes some USB GSM modems + */ + if (temp_result == USB_STOR_TRANSPORT_FAILED && + sense_size != US_SENSE_SIZE) { + usb_stor_dbg(us, "-- auto-sense failure, retry small sense\n"); + sense_size = US_SENSE_SIZE; + us->fflags &= ~US_FL_SANE_SENSE; + us->fflags |= US_FL_BAD_SENSE; + goto Retry_Sense; + } + + /* Other failures */ + if (temp_result != USB_STOR_TRANSPORT_GOOD) { + usb_stor_dbg(us, "-- auto-sense failure\n"); + + /* + * we skip the reset if this happens to be a + * multi-target device, since failure of an + * auto-sense is perfectly valid + */ + srb->result = DID_ERROR << 16; + if (!(us->fflags & US_FL_SCM_MULT_TARG)) + goto Handle_Errors; + return; + } + + /* + * If the sense data returned is larger than 18-bytes then we + * assume this device supports requesting more in the future. + * The response code must be 70h through 73h inclusive. + */ + if (srb->sense_buffer[7] > (US_SENSE_SIZE - 8) && + !(us->fflags & US_FL_SANE_SENSE) && + !(us->fflags & US_FL_BAD_SENSE) && + (srb->sense_buffer[0] & 0x7C) == 0x70) { + usb_stor_dbg(us, "-- SANE_SENSE support enabled\n"); + us->fflags |= US_FL_SANE_SENSE; + + /* + * Indicate to the user that we truncated their sense + * because we didn't know it supported larger sense. + */ + usb_stor_dbg(us, "-- Sense data truncated to %i from %i\n", + US_SENSE_SIZE, + srb->sense_buffer[7] + 8); + srb->sense_buffer[7] = (US_SENSE_SIZE - 8); + } + + scsi_normalize_sense(srb->sense_buffer, SCSI_SENSE_BUFFERSIZE, + &sshdr); + + usb_stor_dbg(us, "-- Result from auto-sense is %d\n", + temp_result); + usb_stor_dbg(us, "-- code: 0x%x, key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n", + sshdr.response_code, sshdr.sense_key, + sshdr.asc, sshdr.ascq); +#ifdef CONFIG_USB_STORAGE_DEBUG + usb_stor_show_sense(us, sshdr.sense_key, sshdr.asc, sshdr.ascq); +#endif + + /* set the result so the higher layers expect this data */ + srb->result = SAM_STAT_CHECK_CONDITION; + + scdd = scsi_sense_desc_find(srb->sense_buffer, + SCSI_SENSE_BUFFERSIZE, 4); + fm_ili = (scdd ? scdd[3] : srb->sense_buffer[2]) & 0xA0; + + /* + * We often get empty sense data. This could indicate that + * everything worked or that there was an unspecified + * problem. We have to decide which. + */ + if (sshdr.sense_key == 0 && sshdr.asc == 0 && sshdr.ascq == 0 && + fm_ili == 0) { + /* + * If things are really okay, then let's show that. + * Zero out the sense buffer so the higher layers + * won't realize we did an unsolicited auto-sense. + */ + if (result == USB_STOR_TRANSPORT_GOOD) { + srb->result = SAM_STAT_GOOD; + srb->sense_buffer[0] = 0x0; + } + + /* + * ATA-passthru commands use sense data to report + * the command completion status, and often devices + * return Check Condition status when nothing is + * wrong. + */ + else if (srb->cmnd[0] == ATA_16 || + srb->cmnd[0] == ATA_12) { + /* leave the data alone */ + } + + /* + * If there was a problem, report an unspecified + * hardware error to prevent the higher layers from + * entering an infinite retry loop. + */ + else { + srb->result = DID_ERROR << 16; + if ((sshdr.response_code & 0x72) == 0x72) + srb->sense_buffer[1] = HARDWARE_ERROR; + else + srb->sense_buffer[2] = HARDWARE_ERROR; + } + } + } + + /* + * Some devices don't work or return incorrect data the first + * time they get a READ(10) command, or for the first READ(10) + * after a media change. If the INITIAL_READ10 flag is set, + * keep track of whether READ(10) commands succeed. If the + * previous one succeeded and this one failed, set the REDO_READ10 + * flag to force a retry. + */ + if (unlikely((us->fflags & US_FL_INITIAL_READ10) && + srb->cmnd[0] == READ_10)) { + if (srb->result == SAM_STAT_GOOD) { + set_bit(US_FLIDX_READ10_WORKED, &us->dflags); + } else if (test_bit(US_FLIDX_READ10_WORKED, &us->dflags)) { + clear_bit(US_FLIDX_READ10_WORKED, &us->dflags); + set_bit(US_FLIDX_REDO_READ10, &us->dflags); + } + + /* + * Next, if the REDO_READ10 flag is set, return a result + * code that will cause the SCSI core to retry the READ(10) + * command immediately. + */ + if (test_bit(US_FLIDX_REDO_READ10, &us->dflags)) { + clear_bit(US_FLIDX_REDO_READ10, &us->dflags); + srb->result = DID_IMM_RETRY << 16; + srb->sense_buffer[0] = 0; + } + } + + /* Did we transfer less than the minimum amount required? */ + if ((srb->result == SAM_STAT_GOOD || srb->sense_buffer[2] == 0) && + scsi_bufflen(srb) - scsi_get_resid(srb) < srb->underflow) + srb->result = DID_ERROR << 16; + + last_sector_hacks(us, srb); + return; + + /* + * Error and abort processing: try to resynchronize with the device + * by issuing a port reset. If that fails, try a class-specific + * device reset. + */ + Handle_Errors: + + /* + * Set the RESETTING bit, and clear the ABORTING bit so that + * the reset may proceed. + */ + scsi_lock(us_to_host(us)); + set_bit(US_FLIDX_RESETTING, &us->dflags); + clear_bit(US_FLIDX_ABORTING, &us->dflags); + scsi_unlock(us_to_host(us)); + + /* + * We must release the device lock because the pre_reset routine + * will want to acquire it. + */ + mutex_unlock(&us->dev_mutex); + result = usb_stor_port_reset(us); + mutex_lock(&us->dev_mutex); + + if (result < 0) { + scsi_lock(us_to_host(us)); + usb_stor_report_device_reset(us); + scsi_unlock(us_to_host(us)); + us->transport_reset(us); + } + clear_bit(US_FLIDX_RESETTING, &us->dflags); + last_sector_hacks(us, srb); +} + +/* Stop the current URB transfer */ +void usb_stor_stop_transport(struct us_data *us) +{ + /* + * If the state machine is blocked waiting for an URB, + * let's wake it up. The test_and_clear_bit() call + * guarantees that if a URB has just been submitted, + * it won't be cancelled more than once. + */ + if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags)) { + usb_stor_dbg(us, "-- cancelling URB\n"); + usb_unlink_urb(us->current_urb); + } + + /* If we are waiting for a scatter-gather operation, cancel it. */ + if (test_and_clear_bit(US_FLIDX_SG_ACTIVE, &us->dflags)) { + usb_stor_dbg(us, "-- cancelling sg request\n"); + usb_sg_cancel(&us->current_sg); + } +} + +/* + * Control/Bulk and Control/Bulk/Interrupt transport + */ + +int usb_stor_CB_transport(struct scsi_cmnd *srb, struct us_data *us) +{ + unsigned int transfer_length = scsi_bufflen(srb); + unsigned int pipe = 0; + int result; + + /* COMMAND STAGE */ + /* let's send the command via the control pipe */ + /* + * Command is sometime (f.e. after scsi_eh_prep_cmnd) on the stack. + * Stack may be vmallocated. So no DMA for us. Make a copy. + */ + memcpy(us->iobuf, srb->cmnd, srb->cmd_len); + result = usb_stor_ctrl_transfer(us, us->send_ctrl_pipe, + US_CBI_ADSC, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, + us->ifnum, us->iobuf, srb->cmd_len); + + /* check the return code for the command */ + usb_stor_dbg(us, "Call to usb_stor_ctrl_transfer() returned %d\n", + result); + + /* if we stalled the command, it means command failed */ + if (result == USB_STOR_XFER_STALLED) { + return USB_STOR_TRANSPORT_FAILED; + } + + /* Uh oh... serious problem here */ + if (result != USB_STOR_XFER_GOOD) { + return USB_STOR_TRANSPORT_ERROR; + } + + /* DATA STAGE */ + /* transfer the data payload for this command, if one exists*/ + if (transfer_length) { + pipe = srb->sc_data_direction == DMA_FROM_DEVICE ? + us->recv_bulk_pipe : us->send_bulk_pipe; + result = usb_stor_bulk_srb(us, pipe, srb); + usb_stor_dbg(us, "CBI data stage result is 0x%x\n", result); + + /* if we stalled the data transfer it means command failed */ + if (result == USB_STOR_XFER_STALLED) + return USB_STOR_TRANSPORT_FAILED; + if (result > USB_STOR_XFER_STALLED) + return USB_STOR_TRANSPORT_ERROR; + } + + /* STATUS STAGE */ + + /* + * NOTE: CB does not have a status stage. Silly, I know. So + * we have to catch this at a higher level. + */ + if (us->protocol != USB_PR_CBI) + return USB_STOR_TRANSPORT_GOOD; + + result = usb_stor_intr_transfer(us, us->iobuf, 2); + usb_stor_dbg(us, "Got interrupt data (0x%x, 0x%x)\n", + us->iobuf[0], us->iobuf[1]); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + /* + * UFI gives us ASC and ASCQ, like a request sense + * + * REQUEST_SENSE and INQUIRY don't affect the sense data on UFI + * devices, so we ignore the information for those commands. Note + * that this means we could be ignoring a real error on these + * commands, but that can't be helped. + */ + if (us->subclass == USB_SC_UFI) { + if (srb->cmnd[0] == REQUEST_SENSE || + srb->cmnd[0] == INQUIRY) + return USB_STOR_TRANSPORT_GOOD; + if (us->iobuf[0]) + goto Failed; + return USB_STOR_TRANSPORT_GOOD; + } + + /* + * If not UFI, we interpret the data as a result code + * The first byte should always be a 0x0. + * + * Some bogus devices don't follow that rule. They stuff the ASC + * into the first byte -- so if it's non-zero, call it a failure. + */ + if (us->iobuf[0]) { + usb_stor_dbg(us, "CBI IRQ data showed reserved bType 0x%x\n", + us->iobuf[0]); + goto Failed; + + } + + /* The second byte & 0x0F should be 0x0 for good, otherwise error */ + switch (us->iobuf[1] & 0x0F) { + case 0x00: + return USB_STOR_TRANSPORT_GOOD; + case 0x01: + goto Failed; + } + return USB_STOR_TRANSPORT_ERROR; + + /* + * the CBI spec requires that the bulk pipe must be cleared + * following any data-in/out command failure (section 2.4.3.1.3) + */ + Failed: + if (pipe) + usb_stor_clear_halt(us, pipe); + return USB_STOR_TRANSPORT_FAILED; +} +EXPORT_SYMBOL_GPL(usb_stor_CB_transport); + +/* + * Bulk only transport + */ + +/* Determine what the maximum LUN supported is */ +int usb_stor_Bulk_max_lun(struct us_data *us) +{ + int result; + + /* issue the command */ + us->iobuf[0] = 0; + result = usb_stor_control_msg(us, us->recv_ctrl_pipe, + US_BULK_GET_MAX_LUN, + USB_DIR_IN | USB_TYPE_CLASS | + USB_RECIP_INTERFACE, + 0, us->ifnum, us->iobuf, 1, 10*HZ); + + usb_stor_dbg(us, "GetMaxLUN command result is %d, data is %d\n", + result, us->iobuf[0]); + + /* If we have a successful request, return the result if valid. */ + if (result > 0) { + if (us->iobuf[0] <= US_BULK_MAX_LUN_LIMIT) { + return us->iobuf[0]; + } else { + dev_info(&us->pusb_intf->dev, + "Max LUN %d is not valid, using 0 instead", + us->iobuf[0]); + } + } + + /* + * Some devices don't like GetMaxLUN. They may STALL the control + * pipe, they may return a zero-length result, they may do nothing at + * all and timeout, or they may fail in even more bizarrely creative + * ways. In these cases the best approach is to use the default + * value: only one LUN. + */ + return 0; +} + +int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) +{ + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap *) us->iobuf; + unsigned int transfer_length = scsi_bufflen(srb); + unsigned int residue; + int result; + int fake_sense = 0; + unsigned int cswlen; + unsigned int cbwlen = US_BULK_CB_WRAP_LEN; + + /* Take care of BULK32 devices; set extra byte to 0 */ + if (unlikely(us->fflags & US_FL_BULK32)) { + cbwlen = 32; + us->iobuf[31] = 0; + } + + /* set up the command wrapper */ + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->DataTransferLength = cpu_to_le32(transfer_length); + bcb->Flags = srb->sc_data_direction == DMA_FROM_DEVICE ? + US_BULK_FLAG_IN : US_BULK_FLAG_OUT; + bcb->Tag = ++us->tag; + bcb->Lun = srb->device->lun; + if (us->fflags & US_FL_SCM_MULT_TARG) + bcb->Lun |= srb->device->id << 4; + bcb->Length = srb->cmd_len; + + /* copy the command payload */ + memset(bcb->CDB, 0, sizeof(bcb->CDB)); + memcpy(bcb->CDB, srb->cmnd, bcb->Length); + + /* send it to out endpoint */ + usb_stor_dbg(us, "Bulk Command S 0x%x T 0x%x L %d F %d Trg %d LUN %d CL %d\n", + le32_to_cpu(bcb->Signature), bcb->Tag, + le32_to_cpu(bcb->DataTransferLength), bcb->Flags, + (bcb->Lun >> 4), (bcb->Lun & 0x0F), + bcb->Length); + result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + bcb, cbwlen, NULL); + usb_stor_dbg(us, "Bulk command transfer result=%d\n", result); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + /* DATA STAGE */ + /* send/receive data payload, if there is any */ + + /* + * Some USB-IDE converter chips need a 100us delay between the + * command phase and the data phase. Some devices need a little + * more than that, probably because of clock rate inaccuracies. + */ + if (unlikely(us->fflags & US_FL_GO_SLOW)) + usleep_range(125, 150); + + if (transfer_length) { + unsigned int pipe = srb->sc_data_direction == DMA_FROM_DEVICE ? + us->recv_bulk_pipe : us->send_bulk_pipe; + result = usb_stor_bulk_srb(us, pipe, srb); + usb_stor_dbg(us, "Bulk data transfer result 0x%x\n", result); + if (result == USB_STOR_XFER_ERROR) + return USB_STOR_TRANSPORT_ERROR; + + /* + * If the device tried to send back more data than the + * amount requested, the spec requires us to transfer + * the CSW anyway. Since there's no point retrying + * the command, we'll return fake sense data indicating + * Illegal Request, Invalid Field in CDB. + */ + if (result == USB_STOR_XFER_LONG) + fake_sense = 1; + + /* + * Sometimes a device will mistakenly skip the data phase + * and go directly to the status phase without sending a + * zero-length packet. If we get a 13-byte response here, + * check whether it really is a CSW. + */ + if (result == USB_STOR_XFER_SHORT && + srb->sc_data_direction == DMA_FROM_DEVICE && + transfer_length - scsi_get_resid(srb) == + US_BULK_CS_WRAP_LEN) { + struct scatterlist *sg = NULL; + unsigned int offset = 0; + + if (usb_stor_access_xfer_buf((unsigned char *) bcs, + US_BULK_CS_WRAP_LEN, srb, &sg, + &offset, FROM_XFER_BUF) == + US_BULK_CS_WRAP_LEN && + bcs->Signature == + cpu_to_le32(US_BULK_CS_SIGN)) { + unsigned char buf[US_BULK_CS_WRAP_LEN]; + + usb_stor_dbg(us, "Device skipped data phase\n"); + + /* + * Devices skipping data phase might leave CSW data in srb's + * transfer buffer. Zero it to prevent USB protocol leakage. + */ + sg = NULL; + offset = 0; + memset(buf, 0, sizeof(buf)); + if (usb_stor_access_xfer_buf(buf, + US_BULK_CS_WRAP_LEN, srb, &sg, + &offset, TO_XFER_BUF) != + US_BULK_CS_WRAP_LEN) + usb_stor_dbg(us, "Failed to clear CSW data\n"); + + scsi_set_resid(srb, transfer_length); + goto skipped_data_phase; + } + } + } + + /* + * See flow chart on pg 15 of the Bulk Only Transport spec for + * an explanation of how this code works. + */ + + /* get CSW for device status */ + usb_stor_dbg(us, "Attempting to get CSW...\n"); + result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + bcs, US_BULK_CS_WRAP_LEN, &cswlen); + + /* + * Some broken devices add unnecessary zero-length packets to the + * end of their data transfers. Such packets show up as 0-length + * CSWs. If we encounter such a thing, try to read the CSW again. + */ + if (result == USB_STOR_XFER_SHORT && cswlen == 0) { + usb_stor_dbg(us, "Received 0-length CSW; retrying...\n"); + result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + bcs, US_BULK_CS_WRAP_LEN, &cswlen); + } + + /* did the attempt to read the CSW fail? */ + if (result == USB_STOR_XFER_STALLED) { + + /* get the status again */ + usb_stor_dbg(us, "Attempting to get CSW (2nd try)...\n"); + result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + bcs, US_BULK_CS_WRAP_LEN, NULL); + } + + /* if we still have a failure at this point, we're in trouble */ + usb_stor_dbg(us, "Bulk status result = %d\n", result); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + skipped_data_phase: + /* check bulk status */ + residue = le32_to_cpu(bcs->Residue); + usb_stor_dbg(us, "Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n", + le32_to_cpu(bcs->Signature), bcs->Tag, + residue, bcs->Status); + if (!(bcs->Tag == us->tag || (us->fflags & US_FL_BULK_IGNORE_TAG)) || + bcs->Status > US_BULK_STAT_PHASE) { + usb_stor_dbg(us, "Bulk logical error\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + /* + * Some broken devices report odd signatures, so we do not check them + * for validity against the spec. We store the first one we see, + * and check subsequent transfers for validity against this signature. + */ + if (!us->bcs_signature) { + us->bcs_signature = bcs->Signature; + if (us->bcs_signature != cpu_to_le32(US_BULK_CS_SIGN)) + usb_stor_dbg(us, "Learnt BCS signature 0x%08X\n", + le32_to_cpu(us->bcs_signature)); + } else if (bcs->Signature != us->bcs_signature) { + usb_stor_dbg(us, "Signature mismatch: got %08X, expecting %08X\n", + le32_to_cpu(bcs->Signature), + le32_to_cpu(us->bcs_signature)); + return USB_STOR_TRANSPORT_ERROR; + } + + /* + * try to compute the actual residue, based on how much data + * was really transferred and what the device tells us + */ + if (residue && !(us->fflags & US_FL_IGNORE_RESIDUE)) { + + /* + * Heuristically detect devices that generate bogus residues + * by seeing what happens with INQUIRY and READ CAPACITY + * commands. + */ + if (bcs->Status == US_BULK_STAT_OK && + scsi_get_resid(srb) == 0 && + ((srb->cmnd[0] == INQUIRY && + transfer_length == 36) || + (srb->cmnd[0] == READ_CAPACITY && + transfer_length == 8))) { + us->fflags |= US_FL_IGNORE_RESIDUE; + + } else { + residue = min(residue, transfer_length); + scsi_set_resid(srb, max(scsi_get_resid(srb), residue)); + } + } + + /* based on the status code, we report good or bad */ + switch (bcs->Status) { + case US_BULK_STAT_OK: + /* device babbled -- return fake sense data */ + if (fake_sense) { + memcpy(srb->sense_buffer, + usb_stor_sense_invalidCDB, + sizeof(usb_stor_sense_invalidCDB)); + return USB_STOR_TRANSPORT_NO_SENSE; + } + + /* command good -- note that data could be short */ + return USB_STOR_TRANSPORT_GOOD; + + case US_BULK_STAT_FAIL: + /* command failed */ + return USB_STOR_TRANSPORT_FAILED; + + case US_BULK_STAT_PHASE: + /* + * phase error -- note that a transport reset will be + * invoked by the invoke_transport() function + */ + return USB_STOR_TRANSPORT_ERROR; + } + + /* we should never get here, but if we do, we're in trouble */ + return USB_STOR_TRANSPORT_ERROR; +} +EXPORT_SYMBOL_GPL(usb_stor_Bulk_transport); + +/*********************************************************************** + * Reset routines + ***********************************************************************/ + +/* + * This is the common part of the device reset code. + * + * It's handy that every transport mechanism uses the control endpoint for + * resets. + * + * Basically, we send a reset with a 5-second timeout, so we don't get + * jammed attempting to do the reset. + */ +static int usb_stor_reset_common(struct us_data *us, + u8 request, u8 requesttype, + u16 value, u16 index, void *data, u16 size) +{ + int result; + int result2; + + if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) { + usb_stor_dbg(us, "No reset during disconnect\n"); + return -EIO; + } + + result = usb_stor_control_msg(us, us->send_ctrl_pipe, + request, requesttype, value, index, data, size, + 5*HZ); + if (result < 0) { + usb_stor_dbg(us, "Soft reset failed: %d\n", result); + return result; + } + + /* + * Give the device some time to recover from the reset, + * but don't delay disconnect processing. + */ + wait_event_interruptible_timeout(us->delay_wait, + test_bit(US_FLIDX_DISCONNECTING, &us->dflags), + HZ*6); + if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) { + usb_stor_dbg(us, "Reset interrupted by disconnect\n"); + return -EIO; + } + + usb_stor_dbg(us, "Soft reset: clearing bulk-in endpoint halt\n"); + result = usb_stor_clear_halt(us, us->recv_bulk_pipe); + + usb_stor_dbg(us, "Soft reset: clearing bulk-out endpoint halt\n"); + result2 = usb_stor_clear_halt(us, us->send_bulk_pipe); + + /* return a result code based on the result of the clear-halts */ + if (result >= 0) + result = result2; + if (result < 0) + usb_stor_dbg(us, "Soft reset failed\n"); + else + usb_stor_dbg(us, "Soft reset done\n"); + return result; +} + +/* This issues a CB[I] Reset to the device in question */ +#define CB_RESET_CMD_SIZE 12 + +int usb_stor_CB_reset(struct us_data *us) +{ + memset(us->iobuf, 0xFF, CB_RESET_CMD_SIZE); + us->iobuf[0] = SEND_DIAGNOSTIC; + us->iobuf[1] = 4; + return usb_stor_reset_common(us, US_CBI_ADSC, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, us->ifnum, us->iobuf, CB_RESET_CMD_SIZE); +} +EXPORT_SYMBOL_GPL(usb_stor_CB_reset); + +/* + * This issues a Bulk-only Reset to the device in question, including + * clearing the subsequent endpoint halts that may occur. + */ +int usb_stor_Bulk_reset(struct us_data *us) +{ + return usb_stor_reset_common(us, US_BULK_RESET_REQUEST, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, us->ifnum, NULL, 0); +} +EXPORT_SYMBOL_GPL(usb_stor_Bulk_reset); + +/* + * Issue a USB port reset to the device. The caller must not hold + * us->dev_mutex. + */ +int usb_stor_port_reset(struct us_data *us) +{ + int result; + + /*for these devices we must use the class specific method */ + if (us->pusb_dev->quirks & USB_QUIRK_RESET) + return -EPERM; + + result = usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf); + if (result < 0) + usb_stor_dbg(us, "unable to lock device for reset: %d\n", + result); + else { + /* Were we disconnected while waiting for the lock? */ + if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) { + result = -EIO; + usb_stor_dbg(us, "No reset during disconnect\n"); + } else { + result = usb_reset_device(us->pusb_dev); + usb_stor_dbg(us, "usb_reset_device returns %d\n", + result); + } + usb_unlock_device(us->pusb_dev); + } + return result; +} diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h new file mode 100644 index 000000000..74ffd0d7e --- /dev/null +++ b/drivers/usb/storage/transport.h @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Driver for USB Mass Storage compliant devices + * Transport Functions Header File + * + * Current development and maintenance by: + * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + */ + +#ifndef _TRANSPORT_H_ +#define _TRANSPORT_H_ + +#include + +/* + * usb_stor_bulk_transfer_xxx() return codes, in order of severity + */ + +#define USB_STOR_XFER_GOOD 0 /* good transfer */ +#define USB_STOR_XFER_SHORT 1 /* transferred less than expected */ +#define USB_STOR_XFER_STALLED 2 /* endpoint stalled */ +#define USB_STOR_XFER_LONG 3 /* device tried to send too much */ +#define USB_STOR_XFER_ERROR 4 /* transfer died in the middle */ + +/* + * Transport return codes + */ + +#define USB_STOR_TRANSPORT_GOOD 0 /* Transport good, command good */ +#define USB_STOR_TRANSPORT_FAILED 1 /* Transport good, command failed */ +#define USB_STOR_TRANSPORT_NO_SENSE 2 /* Command failed, no auto-sense */ +#define USB_STOR_TRANSPORT_ERROR 3 /* Transport bad (i.e. device dead) */ + +/* + * We used to have USB_STOR_XFER_ABORTED and USB_STOR_TRANSPORT_ABORTED + * return codes. But now the transport and low-level transfer routines + * treat an abort as just another error (-ENOENT for a cancelled URB). + * It is up to the invoke_transport() function to test for aborts and + * distinguish them from genuine communication errors. + */ + +/* + * CBI accept device specific command + */ + +#define US_CBI_ADSC 0 + +extern int usb_stor_CB_transport(struct scsi_cmnd *, struct us_data*); +extern int usb_stor_CB_reset(struct us_data*); + +extern int usb_stor_Bulk_transport(struct scsi_cmnd *, struct us_data*); +extern int usb_stor_Bulk_max_lun(struct us_data*); +extern int usb_stor_Bulk_reset(struct us_data*); + +extern void usb_stor_invoke_transport(struct scsi_cmnd *, struct us_data*); +extern void usb_stor_stop_transport(struct us_data*); + +extern int usb_stor_control_msg(struct us_data *us, unsigned int pipe, + u8 request, u8 requesttype, u16 value, u16 index, + void *data, u16 size, int timeout); +extern int usb_stor_clear_halt(struct us_data *us, unsigned int pipe); + +extern int usb_stor_ctrl_transfer(struct us_data *us, unsigned int pipe, + u8 request, u8 requesttype, u16 value, u16 index, + void *data, u16 size); +extern int usb_stor_bulk_transfer_buf(struct us_data *us, unsigned int pipe, + void *buf, unsigned int length, unsigned int *act_len); +extern int usb_stor_bulk_transfer_sg(struct us_data *us, unsigned int pipe, + void *buf, unsigned int length, int use_sg, int *residual); +extern int usb_stor_bulk_srb(struct us_data* us, unsigned int pipe, + struct scsi_cmnd* srb); + +extern int usb_stor_port_reset(struct us_data *us); +#endif diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h new file mode 100644 index 000000000..4d3b49e5b --- /dev/null +++ b/drivers/usb/storage/uas-detect.h @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include +#include "usb.h" + +static int uas_is_interface(struct usb_host_interface *intf) +{ + return (intf->desc.bInterfaceClass == USB_CLASS_MASS_STORAGE && + intf->desc.bInterfaceSubClass == USB_SC_SCSI && + intf->desc.bInterfaceProtocol == USB_PR_UAS); +} + +static struct usb_host_interface *uas_find_uas_alt_setting( + struct usb_interface *intf) +{ + int i; + + for (i = 0; i < intf->num_altsetting; i++) { + struct usb_host_interface *alt = &intf->altsetting[i]; + + if (uas_is_interface(alt)) + return alt; + } + + return NULL; +} + +static int uas_find_endpoints(struct usb_host_interface *alt, + struct usb_host_endpoint *eps[]) +{ + struct usb_host_endpoint *endpoint = alt->endpoint; + unsigned i, n_endpoints = alt->desc.bNumEndpoints; + + for (i = 0; i < n_endpoints; i++) { + unsigned char *extra = endpoint[i].extra; + int len = endpoint[i].extralen; + while (len >= 3) { + if (extra[1] == USB_DT_PIPE_USAGE) { + unsigned pipe_id = extra[2]; + if (pipe_id > 0 && pipe_id < 5) + eps[pipe_id - 1] = &endpoint[i]; + break; + } + len -= extra[0]; + extra += extra[0]; + } + } + + if (!eps[0] || !eps[1] || !eps[2] || !eps[3]) + return -ENODEV; + + return 0; +} + +static int uas_use_uas_driver(struct usb_interface *intf, + const struct usb_device_id *id, + u64 *flags_ret) +{ + struct usb_host_endpoint *eps[4] = { }; + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + u64 flags = id->driver_info; + struct usb_host_interface *alt; + int r; + + alt = uas_find_uas_alt_setting(intf); + if (!alt) + return 0; + + r = uas_find_endpoints(alt, eps); + if (r < 0) + return 0; + + /* + * ASMedia has a number of usb3 to sata bridge chips, at the time of + * this writing the following versions exist: + * ASM1051 - no uas support version + * ASM1051 - with broken (*) uas support + * ASM1053 - with working uas support, but problems with large xfers + * ASM1153 - with working uas support + * + * Devices with these chips re-use a number of device-ids over the + * entire line, so the device-id is useless to determine if we're + * dealing with an ASM1051 (which we want to avoid). + * + * The ASM1153 can be identified by config.MaxPower == 0, + * where as the ASM105x models have config.MaxPower == 36. + * + * Differentiating between the ASM1053 and ASM1051 is trickier, when + * connected over USB-3 we can look at the number of streams supported, + * ASM1051 supports 32 streams, where as early ASM1053 versions support + * 16 streams, newer ASM1053-s also support 32 streams, but have a + * different prod-id. + * + * (*) ASM1051 chips do work with UAS with some disks (with the + * US_FL_NO_REPORT_OPCODES quirk), but are broken with other disks + */ + if (le16_to_cpu(udev->descriptor.idVendor) == 0x174c && + (le16_to_cpu(udev->descriptor.idProduct) == 0x5106 || + le16_to_cpu(udev->descriptor.idProduct) == 0x55aa)) { + if (udev->actconfig->desc.bMaxPower == 0) { + /* ASM1153, do nothing */ + } else if (udev->speed < USB_SPEED_SUPER) { + /* No streams info, assume ASM1051 */ + flags |= US_FL_IGNORE_UAS; + } else if (usb_ss_max_streams(&eps[1]->ss_ep_comp) == 32) { + /* Possibly an ASM1051, disable uas */ + flags |= US_FL_IGNORE_UAS; + } else { + /* ASM1053, these have issues with large transfers */ + flags |= US_FL_MAX_SECTORS_240; + } + } + + /* All Seagate disk enclosures have broken ATA pass-through support */ + if (le16_to_cpu(udev->descriptor.idVendor) == 0x0bc2) + flags |= US_FL_NO_ATA_1X; + + /* + * RTL9210-based enclosure from HIKSEMI, MD202 reportedly have issues + * with UAS. This isn't distinguishable with just idVendor and + * idProduct, use manufacturer and product too. + * + * Reported-by: Hongling Zeng + */ + if (le16_to_cpu(udev->descriptor.idVendor) == 0x0bda && + le16_to_cpu(udev->descriptor.idProduct) == 0x9210 && + (udev->manufacturer && !strcmp(udev->manufacturer, "HIKSEMI")) && + (udev->product && !strcmp(udev->product, "MD202"))) + flags |= US_FL_IGNORE_UAS; + + usb_stor_adjust_quirks(udev, &flags); + + if (flags & US_FL_IGNORE_UAS) { + dev_warn(&udev->dev, + "UAS is ignored for this device, using usb-storage instead\n"); + return 0; + } + + if (udev->bus->sg_tablesize == 0) { + dev_warn(&udev->dev, + "The driver for the USB controller %s does not support scatter-gather which is\n", + hcd->driver->description); + dev_warn(&udev->dev, + "required by the UAS driver. Please try an other USB controller if you wish to use UAS.\n"); + return 0; + } + + if (udev->speed >= USB_SPEED_SUPER && !hcd->can_do_streams) { + dev_warn(&udev->dev, + "USB controller %s does not support streams, which are required by the UAS driver.\n", + hcd_to_bus(hcd)->bus_name); + dev_warn(&udev->dev, + "Please try an other USB controller if you wish to use UAS.\n"); + return 0; + } + + if (flags_ret) + *flags_ret = flags; + + return 1; +} diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c new file mode 100644 index 000000000..265162981 --- /dev/null +++ b/drivers/usb/storage/uas.c @@ -0,0 +1,1304 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * USB Attached SCSI + * Note that this is not the same as the USB Mass Storage driver + * + * Copyright Hans de Goede for Red Hat, Inc. 2013 - 2016 + * Copyright Matthew Wilcox for Intel Corp, 2010 + * Copyright Sarah Sharp for Intel Corp, 2010 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uas-detect.h" +#include "scsiglue.h" + +#define MAX_CMNDS 256 + +struct uas_dev_info { + struct usb_interface *intf; + struct usb_device *udev; + struct usb_anchor cmd_urbs; + struct usb_anchor sense_urbs; + struct usb_anchor data_urbs; + u64 flags; + int qdepth, resetting; + unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe; + unsigned use_streams:1; + unsigned shutdown:1; + struct scsi_cmnd *cmnd[MAX_CMNDS]; + spinlock_t lock; + struct work_struct work; + struct work_struct scan_work; /* for async scanning */ +}; + +enum { + SUBMIT_STATUS_URB = BIT(1), + ALLOC_DATA_IN_URB = BIT(2), + SUBMIT_DATA_IN_URB = BIT(3), + ALLOC_DATA_OUT_URB = BIT(4), + SUBMIT_DATA_OUT_URB = BIT(5), + ALLOC_CMD_URB = BIT(6), + SUBMIT_CMD_URB = BIT(7), + COMMAND_INFLIGHT = BIT(8), + DATA_IN_URB_INFLIGHT = BIT(9), + DATA_OUT_URB_INFLIGHT = BIT(10), + COMMAND_ABORTED = BIT(11), + IS_IN_WORK_LIST = BIT(12), +}; + +/* Overrides scsi_pointer */ +struct uas_cmd_info { + unsigned int state; + unsigned int uas_tag; + struct urb *cmd_urb; + struct urb *data_in_urb; + struct urb *data_out_urb; +}; + +/* I hate forward declarations, but I actually have a loop */ +static int uas_submit_urbs(struct scsi_cmnd *cmnd, + struct uas_dev_info *devinfo); +static void uas_do_work(struct work_struct *work); +static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller); +static void uas_free_streams(struct uas_dev_info *devinfo); +static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *prefix, + int status); + +/* + * This driver needs its own workqueue, as we need to control memory allocation. + * + * In the course of error handling and power management uas_wait_for_pending_cmnds() + * needs to flush pending work items. In these contexts we cannot allocate memory + * by doing block IO as we would deadlock. For the same reason we cannot wait + * for anything allocating memory not heeding these constraints. + * + * So we have to control all work items that can be on the workqueue we flush. + * Hence we cannot share a queue and need our own. + */ +static struct workqueue_struct *workqueue; + +static void uas_do_work(struct work_struct *work) +{ + struct uas_dev_info *devinfo = + container_of(work, struct uas_dev_info, work); + struct uas_cmd_info *cmdinfo; + struct scsi_cmnd *cmnd; + unsigned long flags; + int i, err; + + spin_lock_irqsave(&devinfo->lock, flags); + + if (devinfo->resetting) + goto out; + + for (i = 0; i < devinfo->qdepth; i++) { + if (!devinfo->cmnd[i]) + continue; + + cmnd = devinfo->cmnd[i]; + cmdinfo = scsi_cmd_priv(cmnd); + + if (!(cmdinfo->state & IS_IN_WORK_LIST)) + continue; + + err = uas_submit_urbs(cmnd, cmnd->device->hostdata); + if (!err) + cmdinfo->state &= ~IS_IN_WORK_LIST; + else + queue_work(workqueue, &devinfo->work); + } +out: + spin_unlock_irqrestore(&devinfo->lock, flags); +} + +static void uas_scan_work(struct work_struct *work) +{ + struct uas_dev_info *devinfo = + container_of(work, struct uas_dev_info, scan_work); + struct Scsi_Host *shost = usb_get_intfdata(devinfo->intf); + + dev_dbg(&devinfo->intf->dev, "starting scan\n"); + scsi_scan_host(shost); + dev_dbg(&devinfo->intf->dev, "scan complete\n"); +} + +static void uas_add_work(struct scsi_cmnd *cmnd) +{ + struct uas_cmd_info *cmdinfo = scsi_cmd_priv(cmnd); + struct uas_dev_info *devinfo = cmnd->device->hostdata; + + lockdep_assert_held(&devinfo->lock); + cmdinfo->state |= IS_IN_WORK_LIST; + queue_work(workqueue, &devinfo->work); +} + +static void uas_zap_pending(struct uas_dev_info *devinfo, int result) +{ + struct uas_cmd_info *cmdinfo; + struct scsi_cmnd *cmnd; + unsigned long flags; + int i, err; + + spin_lock_irqsave(&devinfo->lock, flags); + for (i = 0; i < devinfo->qdepth; i++) { + if (!devinfo->cmnd[i]) + continue; + + cmnd = devinfo->cmnd[i]; + cmdinfo = scsi_cmd_priv(cmnd); + uas_log_cmd_state(cmnd, __func__, 0); + /* Sense urbs were killed, clear COMMAND_INFLIGHT manually */ + cmdinfo->state &= ~COMMAND_INFLIGHT; + cmnd->result = result << 16; + err = uas_try_complete(cmnd, __func__); + WARN_ON(err != 0); + } + spin_unlock_irqrestore(&devinfo->lock, flags); +} + +static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd) +{ + struct sense_iu *sense_iu = urb->transfer_buffer; + struct scsi_device *sdev = cmnd->device; + + if (urb->actual_length > 16) { + unsigned len = be16_to_cpup(&sense_iu->len); + if (len + 16 != urb->actual_length) { + int newlen = min(len + 16, urb->actual_length) - 16; + if (newlen < 0) + newlen = 0; + sdev_printk(KERN_INFO, sdev, "%s: urb length %d " + "disagrees with IU sense data length %d, " + "using %d bytes of sense data\n", __func__, + urb->actual_length, len, newlen); + len = newlen; + } + memcpy(cmnd->sense_buffer, sense_iu->sense, len); + } + + cmnd->result = sense_iu->status; +} + +static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *prefix, + int status) +{ + struct uas_cmd_info *ci = scsi_cmd_priv(cmnd); + + if (status == -ENODEV) /* too late */ + return; + + scmd_printk(KERN_INFO, cmnd, + "%s %d uas-tag %d inflight:%s%s%s%s%s%s%s%s%s%s%s%s ", + prefix, status, ci->uas_tag, + (ci->state & SUBMIT_STATUS_URB) ? " s-st" : "", + (ci->state & ALLOC_DATA_IN_URB) ? " a-in" : "", + (ci->state & SUBMIT_DATA_IN_URB) ? " s-in" : "", + (ci->state & ALLOC_DATA_OUT_URB) ? " a-out" : "", + (ci->state & SUBMIT_DATA_OUT_URB) ? " s-out" : "", + (ci->state & ALLOC_CMD_URB) ? " a-cmd" : "", + (ci->state & SUBMIT_CMD_URB) ? " s-cmd" : "", + (ci->state & COMMAND_INFLIGHT) ? " CMD" : "", + (ci->state & DATA_IN_URB_INFLIGHT) ? " IN" : "", + (ci->state & DATA_OUT_URB_INFLIGHT) ? " OUT" : "", + (ci->state & COMMAND_ABORTED) ? " abort" : "", + (ci->state & IS_IN_WORK_LIST) ? " work" : ""); + scsi_print_command(cmnd); +} + +static void uas_free_unsubmitted_urbs(struct scsi_cmnd *cmnd) +{ + struct uas_cmd_info *cmdinfo; + + if (!cmnd) + return; + + cmdinfo = scsi_cmd_priv(cmnd); + + if (cmdinfo->state & SUBMIT_CMD_URB) + usb_free_urb(cmdinfo->cmd_urb); + + /* data urbs may have never gotten their submit flag set */ + if (!(cmdinfo->state & DATA_IN_URB_INFLIGHT)) + usb_free_urb(cmdinfo->data_in_urb); + if (!(cmdinfo->state & DATA_OUT_URB_INFLIGHT)) + usb_free_urb(cmdinfo->data_out_urb); +} + +static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller) +{ + struct uas_cmd_info *cmdinfo = scsi_cmd_priv(cmnd); + struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; + + lockdep_assert_held(&devinfo->lock); + if (cmdinfo->state & (COMMAND_INFLIGHT | + DATA_IN_URB_INFLIGHT | + DATA_OUT_URB_INFLIGHT | + COMMAND_ABORTED)) + return -EBUSY; + devinfo->cmnd[cmdinfo->uas_tag - 1] = NULL; + uas_free_unsubmitted_urbs(cmnd); + scsi_done(cmnd); + return 0; +} + +static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd, + unsigned direction) +{ + struct uas_cmd_info *cmdinfo = scsi_cmd_priv(cmnd); + int err; + + cmdinfo->state |= direction | SUBMIT_STATUS_URB; + err = uas_submit_urbs(cmnd, cmnd->device->hostdata); + if (err) { + uas_add_work(cmnd); + } +} + +static bool uas_evaluate_response_iu(struct response_iu *riu, struct scsi_cmnd *cmnd) +{ + u8 response_code = riu->response_code; + + switch (response_code) { + case RC_INCORRECT_LUN: + set_host_byte(cmnd, DID_BAD_TARGET); + break; + case RC_TMF_SUCCEEDED: + set_host_byte(cmnd, DID_OK); + break; + case RC_TMF_NOT_SUPPORTED: + set_host_byte(cmnd, DID_BAD_TARGET); + break; + default: + uas_log_cmd_state(cmnd, "response iu", response_code); + set_host_byte(cmnd, DID_ERROR); + break; + } + + return response_code == RC_TMF_SUCCEEDED; +} + +static void uas_stat_cmplt(struct urb *urb) +{ + struct iu *iu = urb->transfer_buffer; + struct Scsi_Host *shost = urb->context; + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; + struct urb *data_in_urb = NULL; + struct urb *data_out_urb = NULL; + struct scsi_cmnd *cmnd; + struct uas_cmd_info *cmdinfo; + unsigned long flags; + unsigned int idx; + int status = urb->status; + bool success; + + if (status) { + if (status != -ENOENT && status != -ECONNRESET && status != -ESHUTDOWN) + dev_err(&urb->dev->dev, "stat urb: status %d\n", status); + goto bail; + } + + idx = be16_to_cpup(&iu->tag) - 1; + + spin_lock_irqsave(&devinfo->lock, flags); + + if (devinfo->resetting) + goto out; + if (idx >= MAX_CMNDS || !devinfo->cmnd[idx]) { + dev_err(&urb->dev->dev, + "stat urb: no pending cmd for uas-tag %d\n", idx + 1); + goto out; + } + + cmnd = devinfo->cmnd[idx]; + cmdinfo = scsi_cmd_priv(cmnd); + + if (!(cmdinfo->state & COMMAND_INFLIGHT)) { + uas_log_cmd_state(cmnd, "unexpected status cmplt", 0); + goto out; + } + + switch (iu->iu_id) { + case IU_ID_STATUS: + uas_sense(urb, cmnd); + if (cmnd->result != 0) { + /* cancel data transfers on error */ + data_in_urb = usb_get_urb(cmdinfo->data_in_urb); + data_out_urb = usb_get_urb(cmdinfo->data_out_urb); + } + cmdinfo->state &= ~COMMAND_INFLIGHT; + uas_try_complete(cmnd, __func__); + break; + case IU_ID_READ_READY: + if (!cmdinfo->data_in_urb || + (cmdinfo->state & DATA_IN_URB_INFLIGHT)) { + uas_log_cmd_state(cmnd, "unexpected read rdy", 0); + break; + } + uas_xfer_data(urb, cmnd, SUBMIT_DATA_IN_URB); + break; + case IU_ID_WRITE_READY: + if (!cmdinfo->data_out_urb || + (cmdinfo->state & DATA_OUT_URB_INFLIGHT)) { + uas_log_cmd_state(cmnd, "unexpected write rdy", 0); + break; + } + uas_xfer_data(urb, cmnd, SUBMIT_DATA_OUT_URB); + break; + case IU_ID_RESPONSE: + cmdinfo->state &= ~COMMAND_INFLIGHT; + success = uas_evaluate_response_iu((struct response_iu *)iu, cmnd); + if (!success) { + /* Error, cancel data transfers */ + data_in_urb = usb_get_urb(cmdinfo->data_in_urb); + data_out_urb = usb_get_urb(cmdinfo->data_out_urb); + } + uas_try_complete(cmnd, __func__); + break; + default: + uas_log_cmd_state(cmnd, "bogus IU", iu->iu_id); + } + spin_unlock_irqrestore(&devinfo->lock, flags); + usb_free_urb(urb); + + /* Unlinking of data urbs must be done without holding the lock */ + if (data_in_urb) { + usb_unlink_urb(data_in_urb); + usb_put_urb(data_in_urb); + } + if (data_out_urb) { + usb_unlink_urb(data_out_urb); + usb_put_urb(data_out_urb); + } + return; + +out: + spin_unlock_irqrestore(&devinfo->lock, flags); +bail: + usb_free_urb(urb); +} + +static void uas_data_cmplt(struct urb *urb) +{ + struct scsi_cmnd *cmnd = urb->context; + struct uas_cmd_info *cmdinfo = scsi_cmd_priv(cmnd); + struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; + struct scsi_data_buffer *sdb = &cmnd->sdb; + unsigned long flags; + int status = urb->status; + + spin_lock_irqsave(&devinfo->lock, flags); + + if (cmdinfo->data_in_urb == urb) { + cmdinfo->state &= ~DATA_IN_URB_INFLIGHT; + cmdinfo->data_in_urb = NULL; + } else if (cmdinfo->data_out_urb == urb) { + cmdinfo->state &= ~DATA_OUT_URB_INFLIGHT; + cmdinfo->data_out_urb = NULL; + } + + if (devinfo->resetting) + goto out; + + /* Data urbs should not complete before the cmd urb is submitted */ + if (cmdinfo->state & SUBMIT_CMD_URB) { + uas_log_cmd_state(cmnd, "unexpected data cmplt", 0); + goto out; + } + + if (status) { + if (status != -ENOENT && status != -ECONNRESET && status != -ESHUTDOWN) + uas_log_cmd_state(cmnd, "data cmplt err", status); + /* error: no data transfered */ + scsi_set_resid(cmnd, sdb->length); + set_host_byte(cmnd, DID_ERROR); + } else { + scsi_set_resid(cmnd, sdb->length - urb->actual_length); + } + uas_try_complete(cmnd, __func__); +out: + spin_unlock_irqrestore(&devinfo->lock, flags); + usb_free_urb(urb); +} + +static void uas_cmd_cmplt(struct urb *urb) +{ + if (urb->status) + dev_err(&urb->dev->dev, "cmd cmplt err %d\n", urb->status); + + usb_free_urb(urb); +} + +static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp, + struct scsi_cmnd *cmnd, + enum dma_data_direction dir) +{ + struct usb_device *udev = devinfo->udev; + struct uas_cmd_info *cmdinfo = scsi_cmd_priv(cmnd); + struct urb *urb = usb_alloc_urb(0, gfp); + struct scsi_data_buffer *sdb = &cmnd->sdb; + unsigned int pipe = (dir == DMA_FROM_DEVICE) + ? devinfo->data_in_pipe : devinfo->data_out_pipe; + + if (!urb) + goto out; + usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length, + uas_data_cmplt, cmnd); + if (devinfo->use_streams) + urb->stream_id = cmdinfo->uas_tag; + urb->num_sgs = udev->bus->sg_tablesize ? sdb->table.nents : 0; + urb->sg = sdb->table.sgl; + out: + return urb; +} + +static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp, + struct scsi_cmnd *cmnd) +{ + struct usb_device *udev = devinfo->udev; + struct uas_cmd_info *cmdinfo = scsi_cmd_priv(cmnd); + struct urb *urb = usb_alloc_urb(0, gfp); + struct sense_iu *iu; + + if (!urb) + goto out; + + iu = kzalloc_obj(*iu, gfp); + if (!iu) + goto free; + + usb_fill_bulk_urb(urb, udev, devinfo->status_pipe, iu, sizeof(*iu), + uas_stat_cmplt, cmnd->device->host); + if (devinfo->use_streams) + urb->stream_id = cmdinfo->uas_tag; + urb->transfer_flags |= URB_FREE_BUFFER; + out: + return urb; + free: + usb_free_urb(urb); + return NULL; +} + +static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp, + struct scsi_cmnd *cmnd) +{ + struct usb_device *udev = devinfo->udev; + struct scsi_device *sdev = cmnd->device; + struct uas_cmd_info *cmdinfo = scsi_cmd_priv(cmnd); + struct urb *urb = usb_alloc_urb(0, gfp); + struct command_iu *iu; + int len; + + if (!urb) + goto out; + + len = cmnd->cmd_len - 16; + if (len < 0) + len = 0; + len = ALIGN(len, 4); + iu = kzalloc(sizeof(*iu) + len, gfp); + if (!iu) + goto free; + + iu->iu_id = IU_ID_COMMAND; + iu->tag = cpu_to_be16(cmdinfo->uas_tag); + iu->prio_attr = UAS_SIMPLE_TAG; + iu->len = len; + int_to_scsilun(sdev->lun, &iu->lun); + memcpy(iu->cdb, cmnd->cmnd, cmnd->cmd_len); + + usb_fill_bulk_urb(urb, udev, devinfo->cmd_pipe, iu, sizeof(*iu) + len, + uas_cmd_cmplt, NULL); + urb->transfer_flags |= URB_FREE_BUFFER; + out: + return urb; + free: + usb_free_urb(urb); + return NULL; +} + +/* + * Why should I request the Status IU before sending the Command IU? Spec + * says to, but also says the device may receive them in any order. Seems + * daft to me. + */ + +static int uas_submit_sense_urb(struct scsi_cmnd *cmnd, gfp_t gfp) +{ + struct uas_dev_info *devinfo = cmnd->device->hostdata; + struct urb *urb; + int err; + + urb = uas_alloc_sense_urb(devinfo, gfp, cmnd); + if (!urb) + return -ENOMEM; + usb_anchor_urb(urb, &devinfo->sense_urbs); + err = usb_submit_urb(urb, gfp); + if (err) { + usb_unanchor_urb(urb); + uas_log_cmd_state(cmnd, "sense submit err", err); + usb_free_urb(urb); + } + return err; +} + +static int uas_submit_urbs(struct scsi_cmnd *cmnd, + struct uas_dev_info *devinfo) +{ + struct uas_cmd_info *cmdinfo = scsi_cmd_priv(cmnd); + int err; + + lockdep_assert_held(&devinfo->lock); + if (cmdinfo->state & SUBMIT_STATUS_URB) { + err = uas_submit_sense_urb(cmnd, GFP_ATOMIC); + if (err) + return err; + cmdinfo->state &= ~SUBMIT_STATUS_URB; + } + + if (cmdinfo->state & ALLOC_DATA_IN_URB) { + cmdinfo->data_in_urb = uas_alloc_data_urb(devinfo, GFP_ATOMIC, + cmnd, DMA_FROM_DEVICE); + if (!cmdinfo->data_in_urb) + return -ENOMEM; + cmdinfo->state &= ~ALLOC_DATA_IN_URB; + } + + if (cmdinfo->state & SUBMIT_DATA_IN_URB) { + usb_anchor_urb(cmdinfo->data_in_urb, &devinfo->data_urbs); + err = usb_submit_urb(cmdinfo->data_in_urb, GFP_ATOMIC); + if (err) { + usb_unanchor_urb(cmdinfo->data_in_urb); + uas_log_cmd_state(cmnd, "data in submit err", err); + return err; + } + cmdinfo->state &= ~SUBMIT_DATA_IN_URB; + cmdinfo->state |= DATA_IN_URB_INFLIGHT; + } + + if (cmdinfo->state & ALLOC_DATA_OUT_URB) { + cmdinfo->data_out_urb = uas_alloc_data_urb(devinfo, GFP_ATOMIC, + cmnd, DMA_TO_DEVICE); + if (!cmdinfo->data_out_urb) + return -ENOMEM; + cmdinfo->state &= ~ALLOC_DATA_OUT_URB; + } + + if (cmdinfo->state & SUBMIT_DATA_OUT_URB) { + usb_anchor_urb(cmdinfo->data_out_urb, &devinfo->data_urbs); + err = usb_submit_urb(cmdinfo->data_out_urb, GFP_ATOMIC); + if (err) { + usb_unanchor_urb(cmdinfo->data_out_urb); + uas_log_cmd_state(cmnd, "data out submit err", err); + return err; + } + cmdinfo->state &= ~SUBMIT_DATA_OUT_URB; + cmdinfo->state |= DATA_OUT_URB_INFLIGHT; + } + + if (cmdinfo->state & ALLOC_CMD_URB) { + cmdinfo->cmd_urb = uas_alloc_cmd_urb(devinfo, GFP_ATOMIC, cmnd); + if (!cmdinfo->cmd_urb) + return -ENOMEM; + cmdinfo->state &= ~ALLOC_CMD_URB; + } + + if (cmdinfo->state & SUBMIT_CMD_URB) { + usb_anchor_urb(cmdinfo->cmd_urb, &devinfo->cmd_urbs); + err = usb_submit_urb(cmdinfo->cmd_urb, GFP_ATOMIC); + if (err) { + usb_unanchor_urb(cmdinfo->cmd_urb); + uas_log_cmd_state(cmnd, "cmd submit err", err); + return err; + } + cmdinfo->cmd_urb = NULL; + cmdinfo->state &= ~SUBMIT_CMD_URB; + cmdinfo->state |= COMMAND_INFLIGHT; + } + + return 0; +} + +static enum scsi_qc_status uas_queuecommand_lck(struct scsi_cmnd *cmnd) +{ + struct scsi_device *sdev = cmnd->device; + struct uas_dev_info *devinfo = sdev->hostdata; + struct uas_cmd_info *cmdinfo = scsi_cmd_priv(cmnd); + unsigned long flags; + int idx, err; + + /* Re-check scsi_block_requests now that we've the host-lock */ + if (cmnd->device->host->host_self_blocked) + return SCSI_MLQUEUE_DEVICE_BUSY; + + if ((devinfo->flags & US_FL_NO_ATA_1X) && + (cmnd->cmnd[0] == ATA_12 || cmnd->cmnd[0] == ATA_16)) { + memcpy(cmnd->sense_buffer, usb_stor_sense_invalidCDB, + sizeof(usb_stor_sense_invalidCDB)); + cmnd->result = SAM_STAT_CHECK_CONDITION; + scsi_done(cmnd); + return 0; + } + + spin_lock_irqsave(&devinfo->lock, flags); + + if (devinfo->resetting) { + set_host_byte(cmnd, DID_ERROR); + scsi_done(cmnd); + goto zombie; + } + + /* Find a free uas-tag */ + for (idx = 0; idx < devinfo->qdepth; idx++) { + if (!devinfo->cmnd[idx]) + break; + } + if (idx == devinfo->qdepth) { + spin_unlock_irqrestore(&devinfo->lock, flags); + return SCSI_MLQUEUE_DEVICE_BUSY; + } + + memset(cmdinfo, 0, sizeof(*cmdinfo)); + cmdinfo->uas_tag = idx + 1; /* uas-tag == usb-stream-id, so 1 based */ + cmdinfo->state = SUBMIT_STATUS_URB | ALLOC_CMD_URB | SUBMIT_CMD_URB; + + switch (cmnd->sc_data_direction) { + case DMA_FROM_DEVICE: + cmdinfo->state |= ALLOC_DATA_IN_URB | SUBMIT_DATA_IN_URB; + break; + case DMA_BIDIRECTIONAL: + cmdinfo->state |= ALLOC_DATA_IN_URB | SUBMIT_DATA_IN_URB; + fallthrough; + case DMA_TO_DEVICE: + cmdinfo->state |= ALLOC_DATA_OUT_URB | SUBMIT_DATA_OUT_URB; + break; + case DMA_NONE: + break; + } + + if (!devinfo->use_streams) + cmdinfo->state &= ~(SUBMIT_DATA_IN_URB | SUBMIT_DATA_OUT_URB); + + err = uas_submit_urbs(cmnd, devinfo); + /* + * in case of fatal errors the SCSI layer is peculiar + * a command that has finished is a success for the purpose + * of queueing, no matter how fatal the error + */ + if (err == -ENODEV) { + if (cmdinfo->state & (COMMAND_INFLIGHT | DATA_IN_URB_INFLIGHT | + DATA_OUT_URB_INFLIGHT)) + goto out; + + set_host_byte(cmnd, DID_NO_CONNECT); + scsi_done(cmnd); + goto zombie; + } + if (err) { + /* If we did nothing, give up now */ + if (cmdinfo->state & SUBMIT_STATUS_URB) { + spin_unlock_irqrestore(&devinfo->lock, flags); + return SCSI_MLQUEUE_DEVICE_BUSY; + } + uas_add_work(cmnd); + } + +out: + devinfo->cmnd[idx] = cmnd; +zombie: + spin_unlock_irqrestore(&devinfo->lock, flags); + return 0; +} + +static DEF_SCSI_QCMD(uas_queuecommand) + +/* + * For now we do not support actually sending an abort to the device, so + * this eh always fails. Still we must define it to make sure that we've + * dropped all references to the cmnd in question once this function exits. + */ +static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) +{ + struct uas_cmd_info *cmdinfo = scsi_cmd_priv(cmnd); + struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; + struct urb *data_in_urb = NULL; + struct urb *data_out_urb = NULL; + unsigned long flags; + + spin_lock_irqsave(&devinfo->lock, flags); + + uas_log_cmd_state(cmnd, __func__, 0); + + /* Ensure that try_complete does not call scsi_done */ + cmdinfo->state |= COMMAND_ABORTED; + + /* Drop all refs to this cmnd, kill data urbs to break their ref */ + devinfo->cmnd[cmdinfo->uas_tag - 1] = NULL; + if (cmdinfo->state & DATA_IN_URB_INFLIGHT) + data_in_urb = usb_get_urb(cmdinfo->data_in_urb); + if (cmdinfo->state & DATA_OUT_URB_INFLIGHT) + data_out_urb = usb_get_urb(cmdinfo->data_out_urb); + + uas_free_unsubmitted_urbs(cmnd); + + spin_unlock_irqrestore(&devinfo->lock, flags); + + if (data_in_urb) { + usb_kill_urb(data_in_urb); + usb_put_urb(data_in_urb); + } + if (data_out_urb) { + usb_kill_urb(data_out_urb); + usb_put_urb(data_out_urb); + } + + return FAILED; +} + +static int uas_eh_host_reset_handler(struct scsi_cmnd *cmnd) +{ + struct scsi_device *sdev = cmnd->device; + struct uas_dev_info *devinfo = sdev->hostdata; + struct usb_device *udev = devinfo->udev; + unsigned long flags; + int err; + + err = usb_lock_device_for_reset(udev, devinfo->intf); + if (err) { + shost_printk(KERN_ERR, sdev->host, + "%s FAILED to get lock err %d\n", __func__, err); + return FAILED; + } + + shost_printk(KERN_INFO, sdev->host, "%s start\n", __func__); + + spin_lock_irqsave(&devinfo->lock, flags); + devinfo->resetting = 1; + spin_unlock_irqrestore(&devinfo->lock, flags); + + usb_kill_anchored_urbs(&devinfo->cmd_urbs); + usb_kill_anchored_urbs(&devinfo->sense_urbs); + usb_kill_anchored_urbs(&devinfo->data_urbs); + uas_zap_pending(devinfo, DID_RESET); + + err = usb_reset_device(udev); + + spin_lock_irqsave(&devinfo->lock, flags); + devinfo->resetting = 0; + spin_unlock_irqrestore(&devinfo->lock, flags); + + usb_unlock_device(udev); + + if (err) { + shost_printk(KERN_INFO, sdev->host, "%s FAILED err %d\n", + __func__, err); + return FAILED; + } + + shost_printk(KERN_INFO, sdev->host, "%s success\n", __func__); + return SUCCESS; +} + +static int uas_target_alloc(struct scsi_target *starget) +{ + struct uas_dev_info *devinfo = (struct uas_dev_info *) + dev_to_shost(starget->dev.parent)->hostdata; + + if (devinfo->flags & US_FL_NO_REPORT_LUNS) + starget->no_report_luns = 1; + + return 0; +} + +static int uas_sdev_init(struct scsi_device *sdev) +{ + struct uas_dev_info *devinfo = + (struct uas_dev_info *)sdev->host->hostdata; + + /* + * Some USB storage devices reset if the IO advice hints grouping mode + * page is queried. Hence skip that mode page. + */ + sdev->sdev_bflags |= BLIST_SKIP_IO_HINTS; + + sdev->hostdata = devinfo; + return 0; +} + +static int uas_sdev_configure(struct scsi_device *sdev, + struct queue_limits *lim) +{ + struct uas_dev_info *devinfo = sdev->hostdata; + + if (devinfo->flags & US_FL_MAX_SECTORS_64) + lim->max_hw_sectors = 64; + else if (devinfo->flags & US_FL_MAX_SECTORS_240) + lim->max_hw_sectors = 240; + + if (devinfo->flags & US_FL_NO_REPORT_OPCODES) + sdev->no_report_opcodes = 1; + + /* A few buggy USB-ATA bridges don't understand FUA */ + if (devinfo->flags & US_FL_BROKEN_FUA) + sdev->broken_fua = 1; + + /* UAS also needs to support FL_ALWAYS_SYNC */ + if (devinfo->flags & US_FL_ALWAYS_SYNC) { + sdev->skip_ms_page_3f = 1; + sdev->skip_ms_page_8 = 1; + sdev->wce_default_on = 1; + } + + /* Some disks cannot handle READ_CAPACITY_16 */ + if (devinfo->flags & US_FL_NO_READ_CAPACITY_16) + sdev->no_read_capacity_16 = 1; + + /* Some disks cannot handle WRITE_SAME */ + if (devinfo->flags & US_FL_NO_SAME) + sdev->no_write_same = 1; + /* + * Some disks return the total number of blocks in response + * to READ CAPACITY rather than the highest block number. + * If this device makes that mistake, tell the sd driver. + */ + if (devinfo->flags & US_FL_FIX_CAPACITY) + sdev->fix_capacity = 1; + + /* + * in some cases we have to guess + */ + if (devinfo->flags & US_FL_CAPACITY_HEURISTICS) + sdev->guess_capacity = 1; + + /* + * Some devices report generic values until the media has been + * accessed. Force a READ(10) prior to querying device + * characteristics. + */ + sdev->read_before_ms = 1; + + /* + * Some devices don't like MODE SENSE with page=0x3f, + * which is the command used for checking if a device + * is write-protected. Now that we tell the sd driver + * to do a 192-byte transfer with this command the + * majority of devices work fine, but a few still can't + * handle it. The sd driver will simply assume those + * devices are write-enabled. + */ + if (devinfo->flags & US_FL_NO_WP_DETECT) + sdev->skip_ms_page_3f = 1; + + scsi_change_queue_depth(sdev, devinfo->qdepth - 2); + return 0; +} + +static const struct scsi_host_template uas_host_template = { + .module = THIS_MODULE, + .name = "uas", + .queuecommand = uas_queuecommand, + .target_alloc = uas_target_alloc, + .sdev_init = uas_sdev_init, + .sdev_configure = uas_sdev_configure, + .eh_abort_handler = uas_eh_abort_handler, + .eh_host_reset_handler = uas_eh_host_reset_handler, + .this_id = -1, + .skip_settle_delay = 1, + /* + * The protocol has no requirements on alignment in the strict sense. + * Controllers may or may not have alignment restrictions. + * As this is not exported, we use an extremely conservative guess. + */ + .dma_alignment = 511, + .dma_boundary = PAGE_SIZE - 1, + .cmd_size = sizeof(struct uas_cmd_info), +}; + +#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ + vendorName, productName, useProtocol, useTransport, \ + initFunction, flags) \ +{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ + .driver_info = (flags) } + +static const struct usb_device_id uas_usb_ids[] = { +# include "unusual_uas.h" + { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, USB_SC_SCSI, USB_PR_BULK) }, + { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, USB_SC_SCSI, USB_PR_UAS) }, + { } +}; +MODULE_DEVICE_TABLE(usb, uas_usb_ids); + +#undef UNUSUAL_DEV + +static int uas_switch_interface(struct usb_device *udev, + struct usb_interface *intf) +{ + struct usb_host_interface *alt; + + alt = uas_find_uas_alt_setting(intf); + if (!alt) + return -ENODEV; + + return usb_set_interface(udev, alt->desc.bInterfaceNumber, + alt->desc.bAlternateSetting); +} + +static int uas_configure_endpoints(struct uas_dev_info *devinfo) +{ + struct usb_host_endpoint *eps[4] = { }; + struct usb_device *udev = devinfo->udev; + int r; + + r = uas_find_endpoints(devinfo->intf->cur_altsetting, eps); + if (r) + return r; + + devinfo->cmd_pipe = usb_sndbulkpipe(udev, + usb_endpoint_num(&eps[0]->desc)); + devinfo->status_pipe = usb_rcvbulkpipe(udev, + usb_endpoint_num(&eps[1]->desc)); + devinfo->data_in_pipe = usb_rcvbulkpipe(udev, + usb_endpoint_num(&eps[2]->desc)); + devinfo->data_out_pipe = usb_sndbulkpipe(udev, + usb_endpoint_num(&eps[3]->desc)); + + if (udev->speed < USB_SPEED_SUPER) { + devinfo->qdepth = 32; + devinfo->use_streams = 0; + } else { + devinfo->qdepth = usb_alloc_streams(devinfo->intf, eps + 1, + 3, MAX_CMNDS, GFP_NOIO); + if (devinfo->qdepth < 0) + return devinfo->qdepth; + devinfo->use_streams = 1; + } + + return 0; +} + +static void uas_free_streams(struct uas_dev_info *devinfo) +{ + struct usb_device *udev = devinfo->udev; + struct usb_host_endpoint *eps[3]; + + eps[0] = usb_pipe_endpoint(udev, devinfo->status_pipe); + eps[1] = usb_pipe_endpoint(udev, devinfo->data_in_pipe); + eps[2] = usb_pipe_endpoint(udev, devinfo->data_out_pipe); + usb_free_streams(devinfo->intf, eps, 3, GFP_NOIO); +} + +static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + int result = -ENOMEM; + struct Scsi_Host *shost = NULL; + struct uas_dev_info *devinfo; + struct usb_device *udev = interface_to_usbdev(intf); + u64 dev_flags; + + if (!uas_use_uas_driver(intf, id, &dev_flags)) + return -ENODEV; + + if (uas_switch_interface(udev, intf)) + return -ENODEV; + + shost = scsi_host_alloc(&uas_host_template, + sizeof(struct uas_dev_info)); + if (!shost) + goto set_alt0; + + shost->max_cmd_len = 16 + 252; + shost->max_id = 1; + shost->max_lun = 256; + shost->max_channel = 0; + shost->sg_tablesize = udev->bus->sg_tablesize; + + devinfo = (struct uas_dev_info *)shost->hostdata; + devinfo->intf = intf; + devinfo->udev = udev; + devinfo->resetting = 0; + devinfo->shutdown = 0; + devinfo->flags = dev_flags; + init_usb_anchor(&devinfo->cmd_urbs); + init_usb_anchor(&devinfo->sense_urbs); + init_usb_anchor(&devinfo->data_urbs); + spin_lock_init(&devinfo->lock); + INIT_WORK(&devinfo->work, uas_do_work); + INIT_WORK(&devinfo->scan_work, uas_scan_work); + + result = uas_configure_endpoints(devinfo); + if (result) + goto set_alt0; + + /* + * 1 tag is reserved for untagged commands + + * 1 tag to avoid off by one errors in some bridge firmwares + */ + shost->can_queue = devinfo->qdepth - 2; + + usb_set_intfdata(intf, shost); + result = scsi_add_host(shost, &intf->dev); + if (result) + goto free_streams; + + /* Submit the delayed_work for SCSI-device scanning */ + schedule_work(&devinfo->scan_work); + + return result; + +free_streams: + uas_free_streams(devinfo); + usb_set_intfdata(intf, NULL); +set_alt0: + usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0); + if (shost) + scsi_host_put(shost); + return result; +} + +static int uas_cmnd_list_empty(struct uas_dev_info *devinfo) +{ + unsigned long flags; + int i, r = 1; + + spin_lock_irqsave(&devinfo->lock, flags); + + for (i = 0; i < devinfo->qdepth; i++) { + if (devinfo->cmnd[i]) { + r = 0; /* Not empty */ + break; + } + } + + spin_unlock_irqrestore(&devinfo->lock, flags); + + return r; +} + +/* + * Wait for any pending cmnds to complete, on usb-2 sense_urbs may temporarily + * get empty while there still is more work to do due to sense-urbs completing + * with a READ/WRITE_READY iu code, so keep waiting until the list gets empty. + */ +static int uas_wait_for_pending_cmnds(struct uas_dev_info *devinfo) +{ + unsigned long start_time; + int r; + + start_time = jiffies; + do { + flush_work(&devinfo->work); + + r = usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 5000); + if (r == 0) + return -ETIME; + + r = usb_wait_anchor_empty_timeout(&devinfo->data_urbs, 500); + if (r == 0) + return -ETIME; + + if (time_after(jiffies, start_time + 5 * HZ)) + return -ETIME; + } while (!uas_cmnd_list_empty(devinfo)); + + return 0; +} + +static int uas_pre_reset(struct usb_interface *intf) +{ + struct Scsi_Host *shost = usb_get_intfdata(intf); + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; + unsigned long flags; + + if (devinfo->shutdown) + return 0; + + /* Block new requests */ + spin_lock_irqsave(shost->host_lock, flags); + scsi_block_requests(shost); + spin_unlock_irqrestore(shost->host_lock, flags); + + if (uas_wait_for_pending_cmnds(devinfo) != 0) { + shost_printk(KERN_ERR, shost, "%s: timed out\n", __func__); + scsi_unblock_requests(shost); + return 1; + } + + uas_free_streams(devinfo); + + return 0; +} + +static int uas_post_reset(struct usb_interface *intf) +{ + struct Scsi_Host *shost = usb_get_intfdata(intf); + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; + unsigned long flags; + int err; + + if (devinfo->shutdown) + return 0; + + err = uas_configure_endpoints(devinfo); + if (err && err != -ENODEV) + shost_printk(KERN_ERR, shost, + "%s: alloc streams error %d after reset", + __func__, err); + + /* we must unblock the host in every case lest we deadlock */ + spin_lock_irqsave(shost->host_lock, flags); + scsi_report_bus_reset(shost, 0); + spin_unlock_irqrestore(shost->host_lock, flags); + + scsi_unblock_requests(shost); + + return err ? 1 : 0; +} + +static int uas_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct Scsi_Host *shost = usb_get_intfdata(intf); + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; + + if (uas_wait_for_pending_cmnds(devinfo) != 0) { + shost_printk(KERN_ERR, shost, "%s: timed out\n", __func__); + return -ETIME; + } + + return 0; +} + +static int uas_resume(struct usb_interface *intf) +{ + return 0; +} + +static int uas_reset_resume(struct usb_interface *intf) +{ + struct Scsi_Host *shost = usb_get_intfdata(intf); + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; + unsigned long flags; + int err; + + err = uas_configure_endpoints(devinfo); + if (err) { + shost_printk(KERN_ERR, shost, + "%s: alloc streams error %d after reset", + __func__, err); + return -EIO; + } + + spin_lock_irqsave(shost->host_lock, flags); + scsi_report_bus_reset(shost, 0); + spin_unlock_irqrestore(shost->host_lock, flags); + + return 0; +} + +static void uas_disconnect(struct usb_interface *intf) +{ + struct Scsi_Host *shost = usb_get_intfdata(intf); + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; + unsigned long flags; + + spin_lock_irqsave(&devinfo->lock, flags); + devinfo->resetting = 1; + spin_unlock_irqrestore(&devinfo->lock, flags); + + cancel_work_sync(&devinfo->work); + usb_kill_anchored_urbs(&devinfo->cmd_urbs); + usb_kill_anchored_urbs(&devinfo->sense_urbs); + usb_kill_anchored_urbs(&devinfo->data_urbs); + uas_zap_pending(devinfo, DID_NO_CONNECT); + + /* + * Prevent SCSI scanning (if it hasn't started yet) + * or wait for the SCSI-scanning routine to stop. + */ + cancel_work_sync(&devinfo->scan_work); + + scsi_remove_host(shost); + uas_free_streams(devinfo); + scsi_host_put(shost); +} + +/* + * Put the device back in usb-storage mode on shutdown, as some BIOS-es + * hang on reboot when the device is still in uas mode. Note the reset is + * necessary as some devices won't revert to usb-storage mode without it. + */ +static void uas_shutdown(struct usb_interface *intf) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct Scsi_Host *shost = usb_get_intfdata(intf); + struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; + + if (system_state != SYSTEM_RESTART) + return; + + devinfo->shutdown = 1; + uas_free_streams(devinfo); + usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0); + usb_reset_device(udev); +} + +static struct usb_driver uas_driver = { + .name = "uas", + .probe = uas_probe, + .disconnect = uas_disconnect, + .pre_reset = uas_pre_reset, + .post_reset = uas_post_reset, + .suspend = uas_suspend, + .resume = uas_resume, + .reset_resume = uas_reset_resume, + .shutdown = uas_shutdown, + .id_table = uas_usb_ids, +}; + +static int __init uas_init(void) +{ + int rv; + + workqueue = alloc_workqueue("uas", WQ_MEM_RECLAIM | WQ_PERCPU, 0); + if (!workqueue) + return -ENOMEM; + + rv = usb_register(&uas_driver); + if (rv) { + destroy_workqueue(workqueue); + return -ENOMEM; + } + + return 0; +} + +static void __exit uas_exit(void) +{ + usb_deregister(&uas_driver); + destroy_workqueue(workqueue); +} + +module_init(uas_init); +module_exit(uas_exit); + +MODULE_DESCRIPTION("USB Attached SCSI driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("USB_STORAGE"); +MODULE_AUTHOR( + "Hans de Goede , Matthew Wilcox and Sarah Sharp"); diff --git a/drivers/usb/storage/unusual_alauda.h b/drivers/usb/storage/unusual_alauda.h new file mode 100644 index 000000000..13f61ec88 --- /dev/null +++ b/drivers/usb/storage/unusual_alauda.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Unusual Devices File for the Alauda-based card readers + */ + +#if defined(CONFIG_USB_STORAGE_ALAUDA) || \ + defined(CONFIG_USB_STORAGE_ALAUDA_MODULE) + +UNUSUAL_DEV( 0x0584, 0x0008, 0x0102, 0x0102, + "Fujifilm", + "DPC-R1 (Alauda)", + USB_SC_SCSI, USB_PR_ALAUDA, init_alauda, 0), + +UNUSUAL_DEV( 0x07b4, 0x010a, 0x0102, 0x0102, + "Olympus", + "MAUSB-10 (Alauda)", + USB_SC_SCSI, USB_PR_ALAUDA, init_alauda, 0), + +#endif /* defined(CONFIG_USB_STORAGE_ALAUDA) || ... */ diff --git a/drivers/usb/storage/unusual_cypress.h b/drivers/usb/storage/unusual_cypress.h new file mode 100644 index 000000000..5df40759d --- /dev/null +++ b/drivers/usb/storage/unusual_cypress.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Unusual Devices File for devices based on the Cypress USB/ATA bridge + * with support for ATACB + */ + +#if defined(CONFIG_USB_STORAGE_CYPRESS_ATACB) || \ + defined(CONFIG_USB_STORAGE_CYPRESS_ATACB_MODULE) + +/* CY7C68300 : support atacb */ +UNUSUAL_DEV( 0x04b4, 0x6830, 0x0000, 0x9999, + "Cypress", + "Cypress AT2LP", + USB_SC_CYP_ATACB, USB_PR_DEVICE, NULL, 0), + +/* CY7C68310 : support atacb and atacb2 */ +UNUSUAL_DEV( 0x04b4, 0x6831, 0x0000, 0x9999, + "Cypress", + "Cypress ISD-300LP", + USB_SC_CYP_ATACB, USB_PR_DEVICE, NULL, 0), + +UNUSUAL_DEV( 0x14cd, 0x6116, 0x0150, 0x0160, + "Super Top", + "USB 2.0 SATA BRIDGE", + USB_SC_CYP_ATACB, USB_PR_DEVICE, NULL, 0), + +#endif /* defined(CONFIG_USB_STORAGE_CYPRESS_ATACB) || ... */ diff --git a/drivers/usb/storage/unusual_datafab.h b/drivers/usb/storage/unusual_datafab.h new file mode 100644 index 000000000..5335b5d2b --- /dev/null +++ b/drivers/usb/storage/unusual_datafab.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Unusual Devices File for the Datafab USB Compact Flash reader + */ + +#if defined(CONFIG_USB_STORAGE_DATAFAB) || \ + defined(CONFIG_USB_STORAGE_DATAFAB_MODULE) + +UNUSUAL_DEV( 0x07c4, 0xa000, 0x0000, 0x0015, + "Datafab", + "MDCFE-B USB CF Reader", + USB_SC_SCSI, USB_PR_DATAFAB, NULL, + 0), + +/* + * The following Datafab-based devices may or may not work + * using the current driver...the 0xffff is arbitrary since I + * don't know what device versions exist for these guys. + * + * The 0xa003 and 0xa004 devices in particular I'm curious about. + * I'm told they exist but so far nobody has come forward to say that + * they work with this driver. Given the success we've had getting + * other Datafab-based cards operational with this driver, I've decided + * to leave these two devices in the list. + */ +UNUSUAL_DEV( 0x07c4, 0xa001, 0x0000, 0xffff, + "SIIG/Datafab", + "SIIG/Datafab Memory Stick+CF Reader/Writer", + USB_SC_SCSI, USB_PR_DATAFAB, NULL, + 0), + +/* Reported by Josef Reisinger */ +UNUSUAL_DEV( 0x07c4, 0xa002, 0x0000, 0xffff, + "Datafab/Unknown", + "MD2/MD3 Disk enclosure", + USB_SC_SCSI, USB_PR_DATAFAB, NULL, + US_FL_SINGLE_LUN), + +UNUSUAL_DEV( 0x07c4, 0xa003, 0x0000, 0xffff, + "Datafab/Unknown", + "Datafab-based Reader", + USB_SC_SCSI, USB_PR_DATAFAB, NULL, + 0), + +UNUSUAL_DEV( 0x07c4, 0xa004, 0x0000, 0xffff, + "Datafab/Unknown", + "Datafab-based Reader", + USB_SC_SCSI, USB_PR_DATAFAB, NULL, + 0), + +UNUSUAL_DEV( 0x07c4, 0xa005, 0x0000, 0xffff, + "PNY/Datafab", + "PNY/Datafab CF+SM Reader", + USB_SC_SCSI, USB_PR_DATAFAB, NULL, + 0), + +UNUSUAL_DEV( 0x07c4, 0xa006, 0x0000, 0xffff, + "Simple Tech/Datafab", + "Simple Tech/Datafab CF+SM Reader", + USB_SC_SCSI, USB_PR_DATAFAB, NULL, + 0), + +/* Submitted by Olaf Hering */ +UNUSUAL_DEV( 0x07c4, 0xa109, 0x0000, 0xffff, + "Datafab Systems, Inc.", + "USB to CF + SM Combo (LC1)", + USB_SC_SCSI, USB_PR_DATAFAB, NULL, + 0), + +/* + * Reported by Felix Moeller + * in Germany this is sold by Hama with the productnumber 46952 + * as "DualSlot CompactFlash(TM) & MStick Drive USB" + */ +UNUSUAL_DEV( 0x07c4, 0xa10b, 0x0000, 0xffff, + "DataFab Systems Inc.", + "USB CF+MS", + USB_SC_SCSI, USB_PR_DATAFAB, NULL, + 0), + +UNUSUAL_DEV( 0x0c0b, 0xa109, 0x0000, 0xffff, + "Acomdata", + "CF", + USB_SC_SCSI, USB_PR_DATAFAB, NULL, + US_FL_SINGLE_LUN), + +#endif /* defined(CONFIG_USB_STORAGE_DATAFAB) || ... */ diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h new file mode 100644 index 000000000..ac22fa318 --- /dev/null +++ b/drivers/usb/storage/unusual_devs.h @@ -0,0 +1,2521 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Driver for USB Mass Storage compliant devices + * Unusual Devices File + * + * Current development and maintenance by: + * (c) 2000-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * Initial work by: + * (c) 2000 Adam J. Richter (adam@yggdrasil.com), Yggdrasil Computing, Inc. + */ + +/* + * IMPORTANT NOTE: This file must be included in another file which does + * the following thing for it to work: + * The UNUSUAL_DEV, COMPLIANT_DEV, and USUAL_DEV macros must be defined + * before this file is included. + */ + +/* + * If you edit this file, please try to keep it sorted first by VendorID, + * then by ProductID. + * + * If you want to add an entry for this file, be sure to include the + * following information: + * - a patch that adds the entry for your device, including your + * email address right above the entry (plus maybe a brief + * explanation of the reason for the entry), + * - a copy of /sys/kernel/debug/usb/devices with your device plugged in + * running with this patch. + * Send your submission to the USB development list + */ + +/* + * Note: If you add an entry only in order to set the CAPACITY_OK flag, + * use the COMPLIANT_DEV macro instead of UNUSUAL_DEV. This is + * because such entries mark devices which actually work correctly, + * as opposed to devices that do something strangely or wrongly. + */ + +/* + * In-kernel mode switching is deprecated. Do not add new devices to + * this list for the sole purpose of switching them to a different + * mode. Existing userspace solutions are superior. + * + * New mode switching devices should instead be added to the database + * maintained at https://www.draisberghof.de/usb_modeswitch/ + */ + +#if !defined(CONFIG_USB_STORAGE_SDDR09) && \ + !defined(CONFIG_USB_STORAGE_SDDR09_MODULE) +#define NO_SDDR09 +#endif + +/* patch submitted by Vivian Bregier */ +UNUSUAL_DEV( 0x03eb, 0x2002, 0x0100, 0x0100, + "ATMEL", + "SND1 Storage", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE), + +/* Reported by Rodolfo Quesada */ +UNUSUAL_DEV( 0x03ee, 0x6906, 0x0003, 0x0003, + "VIA Technologies Inc.", + "Mitsumi multi cardreader", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +UNUSUAL_DEV( 0x03f0, 0x0107, 0x0200, 0x0200, + "HP", + "CD-Writer+", + USB_SC_8070, USB_PR_CB, NULL, 0), + +/* Reported by Ben Efros */ +UNUSUAL_DEV( 0x03f0, 0x070c, 0x0000, 0x0000, + "HP", + "Personal Media Drive", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_SANE_SENSE ), + +/* + * Reported by Grant Grundler + * HP r707 camera in "Disk" mode with 2.00.23 or 2.00.24 firmware. + */ +UNUSUAL_DEV( 0x03f0, 0x4002, 0x0001, 0x0001, + "HP", + "PhotoSmart R707", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_FIX_CAPACITY), + +UNUSUAL_DEV( 0x03f3, 0x0001, 0x0000, 0x9999, + "Adaptec", + "USBConnect 2000", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + +/* + * Reported by Sebastian Kapfer + * and Olaf Hering (different bcd's, same vendor/product) + * for USB floppies that need the SINGLE_LUN enforcement. + */ +UNUSUAL_DEV( 0x0409, 0x0040, 0x0000, 0x9999, + "NEC", + "NEC USB UF000x", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_SINGLE_LUN ), + +/* Patch submitted by Mihnea-Costin Grigore */ +UNUSUAL_DEV( 0x040d, 0x6205, 0x0003, 0x0003, + "VIA Technologies Inc.", + "USB 2.0 Card Reader", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* + * Deduced by Jonathan Woithe + * Entry needed for flags: US_FL_FIX_INQUIRY because initial inquiry message + * always fails and confuses drive. + */ +UNUSUAL_DEV( 0x0411, 0x001c, 0x0113, 0x0113, + "Buffalo", + "DUB-P40G HDD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY ), + +/* Submitted by Ernestas Vaiciukevicius */ +UNUSUAL_DEV( 0x0419, 0x0100, 0x0100, 0x0100, + "Samsung Info. Systems America, Inc.", + "MP3 Player", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* Reported by Orgad Shaneh */ +UNUSUAL_DEV( 0x0419, 0xaace, 0x0100, 0x0100, + "Samsung", "MP3 Player", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* Reported by Christian Leber */ +UNUSUAL_DEV( 0x0419, 0xaaf5, 0x0100, 0x0100, + "TrekStor", + "i.Beat 115 2.0", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE | US_FL_NOT_LOCKABLE ), + +/* Reported by Stefan Werner */ +UNUSUAL_DEV( 0x0419, 0xaaf6, 0x0100, 0x0100, + "TrekStor", + "i.Beat Joy 2.0", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* Reported by Pete Zaitcev , bz#176584 */ +UNUSUAL_DEV( 0x0420, 0x0001, 0x0100, 0x0100, + "GENERIC", "MP3 PLAYER", /* MyMusix PD-205 on the outside. */ + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* + * Reported by Andrew Nayenko + * Updated for new firmware by Phillip Potter + */ +UNUSUAL_DEV( 0x0421, 0x0019, 0x0592, 0x0610, + "Nokia", + "Nokia 6288", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64 ), + +/* Reported by Mario Rettig */ +UNUSUAL_DEV( 0x0421, 0x042e, 0x0100, 0x0100, + "Nokia", + "Nokia 3250", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE | US_FL_FIX_CAPACITY ), + +/* Reported by */ +UNUSUAL_DEV( 0x0421, 0x0433, 0x0100, 0x0100, + "Nokia", + "E70", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE | US_FL_FIX_CAPACITY ), + +/* Reported by Jon Hart */ +UNUSUAL_DEV( 0x0421, 0x0434, 0x0100, 0x0100, + "Nokia", + "E60", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY | US_FL_IGNORE_RESIDUE ), + +/* + * Reported by Sumedha Swamy and + * Einar Th. Einarsson + */ +UNUSUAL_DEV( 0x0421, 0x0444, 0x0100, 0x0100, + "Nokia", + "N91", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE | US_FL_FIX_CAPACITY ), + +/* + * Reported by Jiri Slaby and + * Rene C. Castberg + */ +UNUSUAL_DEV( 0x0421, 0x0446, 0x0100, 0x0100, + "Nokia", + "N80", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE | US_FL_FIX_CAPACITY ), + +/* Reported by Matthew Bloch */ +UNUSUAL_DEV( 0x0421, 0x044e, 0x0100, 0x0100, + "Nokia", + "E61", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE | US_FL_FIX_CAPACITY ), + +/* Reported by Bardur Arantsson */ +UNUSUAL_DEV( 0x0421, 0x047c, 0x0370, 0x0610, + "Nokia", + "6131", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64 ), + +/* Reported by Manuel Osdoba */ +UNUSUAL_DEV( 0x0421, 0x0492, 0x0452, 0x9999, + "Nokia", + "Nokia 6233", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64 ), + +/* Reported by Alex Corcoles */ +UNUSUAL_DEV( 0x0421, 0x0495, 0x0370, 0x0370, + "Nokia", + "6234", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64 ), + +/* Reported by Daniele Forsi */ +UNUSUAL_DEV( 0x0421, 0x04b9, 0x0350, 0x0350, + "Nokia", + "5300", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64 ), + +/* Patch submitted by Victor A. Santos */ +UNUSUAL_DEV( 0x0421, 0x05af, 0x0742, 0x0742, + "Nokia", + "305", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64), + +/* Patch submitted by Mikhail Zolotaryov */ +UNUSUAL_DEV( 0x0421, 0x06aa, 0x1110, 0x1110, + "Nokia", + "502", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64 ), + +/* Added by Lubomir Rintel , a very fine chap */ +UNUSUAL_DEV( 0x0421, 0x06c2, 0x0000, 0x0406, + "Nokia", + "Nokia 208", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64 ), + +#ifdef NO_SDDR09 +UNUSUAL_DEV( 0x0436, 0x0005, 0x0100, 0x0100, + "Microtech", + "CameraMate", + USB_SC_SCSI, USB_PR_CB, NULL, + US_FL_SINGLE_LUN ), +#endif + +/* + * Patch submitted by Daniel Drake + * Device reports nonsense bInterfaceProtocol 6 when connected over USB2 + */ +UNUSUAL_DEV( 0x0451, 0x5416, 0x0100, 0x0100, + "Neuros Audio", + "USB 2.0 HD 2.5", + USB_SC_DEVICE, USB_PR_BULK, NULL, + US_FL_NEED_OVERRIDE ), + +/* + * Pete Zaitcev , from Patrick C. F. Ernzer, bz#162559. + * The key does not actually break, but it returns zero sense which + * makes our SCSI stack to print confusing messages. + */ +UNUSUAL_DEV( 0x0457, 0x0150, 0x0100, 0x0100, + "USBest Technology", /* sold by Transcend */ + "USB Mass Storage Device", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE ), + +/* + * Bohdan Linda + * 1GB USB sticks MyFlash High Speed. I have restricted + * the revision to my model only + */ +UNUSUAL_DEV( 0x0457, 0x0151, 0x0100, 0x0100, + "USB 2.0", + "Flash Disk", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NOT_LOCKABLE ), + +/* + * Reported by Tamas Kerecsen + * Obviously the PROM has not been customized by the VAR; + * the Vendor and Product string descriptors are: + * Generic Mass Storage (PROTOTYPE--Remember to change idVendor) + * Generic Manufacturer (PROTOTYPE--Remember to change idVendor) + */ +UNUSUAL_DEV( 0x045e, 0xffff, 0x0000, 0x0000, + "Mitac", + "GPS", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64 ), + +/* + * This virtual floppy is found in Sun equipment (x4600, x4200m2, etc.) + * Reported by Pete Zaitcev + * This device chokes on both version of MODE SENSE which we have, so + * use_10_for_ms is not effective, and we use US_FL_NO_WP_DETECT. + */ +UNUSUAL_DEV( 0x046b, 0xff40, 0x0100, 0x0100, + "AMI", + "Virtual Floppy", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_WP_DETECT), + +/* Reported by Egbert Eich */ +UNUSUAL_DEV( 0x0480, 0xd010, 0x0100, 0x9999, + "Toshiba", + "External USB 3.0", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_ALWAYS_SYNC), + +/* Patch submitted by Philipp Friedrich */ +UNUSUAL_DEV( 0x0482, 0x0100, 0x0100, 0x0100, + "Kyocera", + "Finecam S3x", + USB_SC_8070, USB_PR_CB, NULL, US_FL_FIX_INQUIRY), + +/* Patch submitted by Philipp Friedrich */ +UNUSUAL_DEV( 0x0482, 0x0101, 0x0100, 0x0100, + "Kyocera", + "Finecam S4", + USB_SC_8070, USB_PR_CB, NULL, US_FL_FIX_INQUIRY), + +/* Patch submitted by Stephane Galles */ +UNUSUAL_DEV( 0x0482, 0x0103, 0x0100, 0x0100, + "Kyocera", + "Finecam S5", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_FIX_INQUIRY), + +/* Patch submitted by Jens Taprogge */ +UNUSUAL_DEV( 0x0482, 0x0107, 0x0100, 0x0100, + "Kyocera", + "CONTAX SL300R T*", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY | US_FL_NOT_LOCKABLE), + +/* + * Reported by Paul Stewart + * This entry is needed because the device reports Sub=ff + */ +UNUSUAL_DEV( 0x04a4, 0x0004, 0x0001, 0x0001, + "Hitachi", + "DVD-CAM DZ-MV100A Camcorder", + USB_SC_SCSI, USB_PR_CB, NULL, US_FL_SINGLE_LUN), + +/* + * BENQ DC5330 + * Reported by Manuel Fombuena and + * Frank Copeland + */ +UNUSUAL_DEV( 0x04a5, 0x3010, 0x0100, 0x0100, + "Tekom Technologies, Inc", + "300_CAMERA", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* + * Patch for Nikon coolpix 2000 + * Submitted by Fabien Cosse + */ +UNUSUAL_DEV( 0x04b0, 0x0301, 0x0010, 0x0010, + "NIKON", + "NIKON DSC E2000", + USB_SC_DEVICE, USB_PR_DEVICE,NULL, + US_FL_NOT_LOCKABLE ), + +/* Reported by Doug Maxey (dwm@austin.ibm.com) */ +UNUSUAL_DEV( 0x04b3, 0x4001, 0x0110, 0x0110, + "IBM", + "IBM RSA2", + USB_SC_DEVICE, USB_PR_CB, NULL, + US_FL_MAX_SECTORS_MIN), + +/* Reported by Ai Chao */ +UNUSUAL_DEV( 0x04b4, 0xb708, 0x0000, 0xffff, + "Longmai Technologies", + "USB Key", + USB_SC_SCSI, USB_PR_BULK, NULL, + US_FL_NO_ATA_1X), + +/* + * Reported by Simon Levitt + * This entry needs Sub and Proto fields + */ +UNUSUAL_DEV( 0x04b8, 0x0601, 0x0100, 0x0100, + "Epson", + "875DC Storage", + USB_SC_SCSI, USB_PR_CB, NULL, US_FL_FIX_INQUIRY), + +/* + * Reported by Khalid Aziz + * This entry is needed because the device reports Sub=ff + */ +UNUSUAL_DEV( 0x04b8, 0x0602, 0x0110, 0x0110, + "Epson", + "785EPX Storage", + USB_SC_SCSI, USB_PR_BULK, NULL, US_FL_SINGLE_LUN), + +/* + * Reported by James Buren + * Virtual ISOs cannot be remounted if ejected while the device is locked + * Disable locking to mimic Windows behavior that bypasses the issue + */ +UNUSUAL_DEV( 0x04c5, 0x2028, 0x0001, 0x0001, + "iODD", + "2531/2541", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE), + +/* + * Not sure who reported this originally but + * Pavel Machek reported that the extra US_FL_SINGLE_LUN + * flag be added */ +UNUSUAL_DEV( 0x04cb, 0x0100, 0x0000, 0x2210, + "Fujifilm", + "FinePix 1400Zoom", + USB_SC_UFI, USB_PR_DEVICE, NULL, US_FL_FIX_INQUIRY | US_FL_SINGLE_LUN), + +/* + * Reported by Ondrej Zary + * The device reports one sector more and breaks when that sector is accessed + * Firmwares older than 2.6c (the latest one and the only that claims Linux + * support) have also broken tag handling + */ +UNUSUAL_DEV( 0x04ce, 0x0002, 0x0000, 0x026b, + "ScanLogic", + "SL11R-IDE", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY | US_FL_BULK_IGNORE_TAG), +UNUSUAL_DEV( 0x04ce, 0x0002, 0x026c, 0x026c, + "ScanLogic", + "SL11R-IDE", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY), + +/* + * Reported by Kriston Fincher + * Patch submitted by Sean Millichamp + * This is to support the Panasonic PalmCam PV-SD4090 + * This entry is needed because the device reports Sub=ff + */ +UNUSUAL_DEV( 0x04da, 0x0901, 0x0100, 0x0200, + "Panasonic", + "LS-120 Camera", + USB_SC_UFI, USB_PR_DEVICE, NULL, 0), + +/* + * From Yukihiro Nakai, via zaitcev@yahoo.com. + * This is needed for CB instead of CBI + */ +UNUSUAL_DEV( 0x04da, 0x0d05, 0x0000, 0x0000, + "Sharp CE-CW05", + "CD-R/RW Drive", + USB_SC_8070, USB_PR_CB, NULL, 0), + +/* Reported by Adriaan Penning */ +UNUSUAL_DEV( 0x04da, 0x2372, 0x0000, 0x9999, + "Panasonic", + "DMC-LCx Camera", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY | US_FL_NOT_LOCKABLE ), + +/* Reported by Simeon Simeonov */ +UNUSUAL_DEV( 0x04da, 0x2373, 0x0000, 0x9999, + "LEICA", + "D-LUX Camera", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY | US_FL_NOT_LOCKABLE ), + +/* + * Most of the following entries were developed with the help of + * Shuttle/SCM directly. + */ +UNUSUAL_DEV( 0x04e6, 0x0001, 0x0200, 0x0200, + "Matshita", + "LS-120", + USB_SC_8020, USB_PR_CB, NULL, 0), + +UNUSUAL_DEV( 0x04e6, 0x0002, 0x0100, 0x0100, + "Shuttle", + "eUSCSI Bridge", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + +#ifdef NO_SDDR09 +UNUSUAL_DEV( 0x04e6, 0x0005, 0x0100, 0x0208, + "SCM Microsystems", + "eUSB CompactFlash Adapter", + USB_SC_SCSI, USB_PR_CB, NULL, + US_FL_SINGLE_LUN), +#endif + +/* Reported by Markus Demleitner */ +UNUSUAL_DEV( 0x04e6, 0x0006, 0x0100, 0x0100, + "SCM Microsystems Inc.", + "eUSB MMC Adapter", + USB_SC_SCSI, USB_PR_CB, NULL, + US_FL_SINGLE_LUN), + +/* Reported by Daniel Nouri */ +UNUSUAL_DEV( 0x04e6, 0x0006, 0x0205, 0x0205, + "Shuttle", + "eUSB MMC Adapter", + USB_SC_SCSI, USB_PR_DEVICE, NULL, + US_FL_SINGLE_LUN), + +UNUSUAL_DEV( 0x04e6, 0x0007, 0x0100, 0x0200, + "Sony", + "Hifd", + USB_SC_SCSI, USB_PR_CB, NULL, + US_FL_SINGLE_LUN), + +UNUSUAL_DEV( 0x04e6, 0x0009, 0x0200, 0x0200, + "Shuttle", + "eUSB ATA/ATAPI Adapter", + USB_SC_8020, USB_PR_CB, NULL, 0), + +UNUSUAL_DEV( 0x04e6, 0x000a, 0x0200, 0x0200, + "Shuttle", + "eUSB CompactFlash Adapter", + USB_SC_8020, USB_PR_CB, NULL, 0), + +UNUSUAL_DEV( 0x04e6, 0x000b, 0x0100, 0x0100, + "Shuttle", + "eUSCSI Bridge", + USB_SC_SCSI, USB_PR_BULK, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + +UNUSUAL_DEV( 0x04e6, 0x000c, 0x0100, 0x0100, + "Shuttle", + "eUSCSI Bridge", + USB_SC_SCSI, USB_PR_BULK, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + +UNUSUAL_DEV( 0x04e6, 0x000f, 0x0000, 0x9999, + "SCM Microsystems", + "eUSB SCSI Adapter (Bus Powered)", + USB_SC_SCSI, USB_PR_BULK, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + +UNUSUAL_DEV( 0x04e6, 0x0101, 0x0200, 0x0200, + "Shuttle", + "CD-RW Device", + USB_SC_8020, USB_PR_CB, NULL, 0), + +/* Reported by Dmitry Khlystov */ +UNUSUAL_DEV( 0x04e8, 0x507c, 0x0220, 0x0220, + "Samsung", + "YP-U3", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64), + +/* Reported by Vitaly Kuznetsov */ +UNUSUAL_DEV( 0x04e8, 0x5122, 0x0000, 0x9999, + "Samsung", + "YP-CP3", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64 | US_FL_BULK_IGNORE_TAG), + +/* Added by Dmitry Artamonow */ +UNUSUAL_DEV( 0x04e8, 0x5136, 0x0000, 0x9999, + "Samsung", + "YP-Z3", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64), + +/* + * Entry and supporting patch by Theodore Kilgore . + * Device uses standards-violating 32-byte Bulk Command Block Wrappers and + * reports itself as "Proprietary SCSI Bulk." Cf. device entry 0x084d:0x0011. + */ +UNUSUAL_DEV( 0x04fc, 0x80c2, 0x0100, 0x0100, + "Kobian Mercury", + "Binocam DCB-132", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BULK32), + +/* Reported by Bob Sass -- only rev 1.33 tested */ +UNUSUAL_DEV( 0x050d, 0x0115, 0x0133, 0x0133, + "Belkin", + "USB SCSI Adaptor", + USB_SC_SCSI, USB_PR_BULK, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + +/* + * Iomega Clik! Drive + * Reported by David Chatenay + * The reason this is needed is not fully known. + */ +UNUSUAL_DEV( 0x0525, 0xa140, 0x0100, 0x0100, + "Iomega", + "USB Clik! 40", + USB_SC_8070, USB_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY ), + +/* Added by Alan Stern */ +COMPLIANT_DEV(0x0525, 0xa4a5, 0x0000, 0x9999, + "Linux", + "File-backed Storage Gadget", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_CAPACITY_OK ), + +/* + * Yakumo Mega Image 37 + * Submitted by Stephan Fuhrmann */ +UNUSUAL_DEV( 0x052b, 0x1801, 0x0100, 0x0100, + "Tekom Technologies, Inc", + "300_CAMERA", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* + * Another Yakumo camera. + * Reported by Michele Alzetta + */ +UNUSUAL_DEV( 0x052b, 0x1804, 0x0100, 0x0100, + "Tekom Technologies, Inc", + "300_CAMERA", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* Reported by Iacopo Spalletti */ +UNUSUAL_DEV( 0x052b, 0x1807, 0x0100, 0x0100, + "Tekom Technologies, Inc", + "300_CAMERA", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* + * Yakumo Mega Image 47 + * Reported by Bjoern Paetzel + */ +UNUSUAL_DEV( 0x052b, 0x1905, 0x0100, 0x0100, + "Tekom Technologies, Inc", + "400_CAMERA", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* + * Reported by Paul Ortyl + * Note that it's similar to the device above, only different prodID + */ +UNUSUAL_DEV( 0x052b, 0x1911, 0x0100, 0x0100, + "Tekom Technologies, Inc", + "400_CAMERA", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +UNUSUAL_DEV( 0x054c, 0x0010, 0x0106, 0x0450, + "Sony", + "DSC-S30/S70/S75/505V/F505/F707/F717/P8", + USB_SC_SCSI, USB_PR_DEVICE, NULL, + US_FL_SINGLE_LUN | US_FL_NOT_LOCKABLE | US_FL_NO_WP_DETECT ), + +/* + * Submitted by Lars Jacob + * This entry is needed because the device reports Sub=ff + */ +UNUSUAL_DEV( 0x054c, 0x0010, 0x0500, 0x0610, + "Sony", + "DSC-T1/T5/H5", + USB_SC_8070, USB_PR_DEVICE, NULL, + US_FL_SINGLE_LUN ), + + +/* Reported by wim@geeks.nl */ +UNUSUAL_DEV( 0x054c, 0x0025, 0x0100, 0x0100, + "Sony", + "Memorystick NW-MS7", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_SINGLE_LUN ), + +/* Submitted by Olaf Hering, SuSE Bugzilla #49049 */ +UNUSUAL_DEV( 0x054c, 0x002c, 0x0501, 0x2000, + "Sony", + "USB Floppy Drive", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_SINGLE_LUN ), + +UNUSUAL_DEV( 0x054c, 0x002d, 0x0100, 0x0100, + "Sony", + "Memorystick MSAC-US1", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_SINGLE_LUN ), + +/* Submitted by Klaus Mueller */ +UNUSUAL_DEV( 0x054c, 0x002e, 0x0106, 0x0310, + "Sony", + "Handycam", + USB_SC_SCSI, USB_PR_DEVICE, NULL, + US_FL_SINGLE_LUN ), + +/* Submitted by Rajesh Kumble Nayak */ +UNUSUAL_DEV( 0x054c, 0x002e, 0x0500, 0x0500, + "Sony", + "Handycam HC-85", + USB_SC_UFI, USB_PR_DEVICE, NULL, + US_FL_SINGLE_LUN ), + +UNUSUAL_DEV( 0x054c, 0x0032, 0x0000, 0x9999, + "Sony", + "Memorystick MSC-U01N", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_SINGLE_LUN ), + +/* Submitted by Michal Mlotek */ +UNUSUAL_DEV( 0x054c, 0x0058, 0x0000, 0x9999, + "Sony", + "PEG N760c Memorystick", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY ), + +UNUSUAL_DEV( 0x054c, 0x0069, 0x0000, 0x9999, + "Sony", + "Memorystick MSC-U03", + USB_SC_UFI, USB_PR_CB, NULL, + US_FL_SINGLE_LUN ), + +/* Submitted by Nathan Babb */ +UNUSUAL_DEV( 0x054c, 0x006d, 0x0000, 0x9999, + "Sony", + "PEG Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY ), + +/* Submitted by Frank Engel */ +UNUSUAL_DEV( 0x054c, 0x0099, 0x0000, 0x9999, + "Sony", + "PEG Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY ), + +/* Submitted by Mike Alborn */ +UNUSUAL_DEV( 0x054c, 0x016a, 0x0000, 0x9999, + "Sony", + "PEG Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY ), + +/* Submitted by Ren Bigcren */ +UNUSUAL_DEV( 0x054c, 0x02a5, 0x0100, 0x0100, + "Sony Corp.", + "MicroVault Flash Drive", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_READ_CAPACITY_16 ), + +/* floppy reports multiple luns */ +UNUSUAL_DEV( 0x055d, 0x2020, 0x0000, 0x0210, + "SAMSUNG", + "SFD-321U [FW 0C]", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_SINGLE_LUN ), + +/* We keep this entry to force the transport; firmware 3.00 and later is ok. */ +UNUSUAL_DEV( 0x057b, 0x0000, 0x0000, 0x0299, + "Y-E Data", + "Flashbuster-U", + USB_SC_DEVICE, USB_PR_CB, NULL, + US_FL_SINGLE_LUN), + +/* + * Reported by Johann Cardon + * This entry is needed only because the device reports + * bInterfaceClass = 0xff (vendor-specific) + */ +UNUSUAL_DEV( 0x057b, 0x0022, 0x0000, 0x9999, + "Y-E Data", + "Silicon Media R/W", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, 0), + +/* Reported by RTE */ +UNUSUAL_DEV( 0x058f, 0x6387, 0x0141, 0x0141, + "JetFlash", + "TS1GJF2A/120", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64 ), + +/* Fabrizio Fellini */ +UNUSUAL_DEV( 0x0595, 0x4343, 0x0000, 0x2210, + "Fujifilm", + "Digital Camera EX-20 DSC", + USB_SC_8070, USB_PR_DEVICE, NULL, 0 ), + +/* + * Reported by Andre Welter + * This antique device predates the release of the Bulk-only Transport + * spec, and if it gets a Get-Max-LUN then it requires the host to do a + * Clear-Halt on the bulk endpoints. The SINGLE_LUN flag will prevent + * us from sending the request. + */ +UNUSUAL_DEV( 0x059b, 0x0001, 0x0100, 0x0100, + "Iomega", + "ZIP 100", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_SINGLE_LUN ), + +UNUSUAL_DEV( 0x059b, 0x0040, 0x0100, 0x0100, + "Iomega", + "Jaz USB Adapter", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_SINGLE_LUN ), + +/* Reported by */ +UNUSUAL_DEV( 0x059f, 0x0643, 0x0000, 0x0000, + "LaCie", + "DVD+-RW", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_GO_SLOW ), + +/* Reported by Christian Schaller */ +UNUSUAL_DEV( 0x059f, 0x0651, 0x0000, 0x0000, + "LaCie", + "External HDD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_WP_DETECT ), + +/* + * Submitted by Joel Bourquard + * Some versions of this device need the SubClass and Protocol overrides + * while others don't. + */ +UNUSUAL_DEV( 0x05ab, 0x0060, 0x1104, 0x1110, + "In-System", + "PyroGate External CD-ROM Enclosure (FCD-523)", + USB_SC_SCSI, USB_PR_BULK, NULL, + US_FL_NEED_OVERRIDE ), + +/* + * Submitted by Sven Anderson + * There are at least four ProductIDs used for iPods, so I added 0x1202 and + * 0x1204. They just need the US_FL_FIX_CAPACITY. As the bcdDevice appears + * to change with firmware updates, I changed the range to maximum for all + * iPod entries. + */ +UNUSUAL_DEV( 0x05ac, 0x1202, 0x0000, 0x9999, + "Apple", + "iPod", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY ), + +/* Reported by Avi Kivity */ +UNUSUAL_DEV( 0x05ac, 0x1203, 0x0000, 0x9999, + "Apple", + "iPod", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY ), + +UNUSUAL_DEV( 0x05ac, 0x1204, 0x0000, 0x9999, + "Apple", + "iPod", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY | US_FL_NOT_LOCKABLE ), + +UNUSUAL_DEV( 0x05ac, 0x1205, 0x0000, 0x9999, + "Apple", + "iPod", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY ), + +/* + * Reported by Tyson Vinson + * This particular productId is the iPod Nano + */ +UNUSUAL_DEV( 0x05ac, 0x120a, 0x0000, 0x9999, + "Apple", + "iPod", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY ), + +/* + * Reported by Dan Williams + * Option N.V. mobile broadband modems + * Ignore driver CD mode and force into modem mode by default. + */ + +/* Globetrotter HSDPA; mass storage shows up as Qualcomm for vendor */ +UNUSUAL_DEV( 0x05c6, 0x1000, 0x0000, 0x9999, + "Option N.V.", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, option_ms_init, + 0), + +/* Reported by Blake Matheny */ +UNUSUAL_DEV( 0x05dc, 0xb002, 0x0000, 0x0113, + "Lexar", + "USB CF Reader", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY ), + +/* + * The following two entries are for a Genesys USB to IDE + * converter chip, but it changes its ProductId depending + * on whether or not a disk or an optical device is enclosed + * They were originally reported by Alexander Oltu + * and Peter Marks + * respectively. + * + * US_FL_GO_SLOW and US_FL_MAX_SECTORS_64 added by Phil Dibowitz + * as these flags were made and hard-coded + * special-cases were pulled from scsiglue.c. + */ +UNUSUAL_DEV( 0x05e3, 0x0701, 0x0000, 0xffff, + "Genesys Logic", + "USB to IDE Optical", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_GO_SLOW | US_FL_MAX_SECTORS_64 | US_FL_IGNORE_RESIDUE ), + +UNUSUAL_DEV( 0x05e3, 0x0702, 0x0000, 0xffff, + "Genesys Logic", + "USB to IDE Disk", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_GO_SLOW | US_FL_MAX_SECTORS_64 | US_FL_IGNORE_RESIDUE ), + +/* Reported by Ben Efros */ +UNUSUAL_DEV( 0x05e3, 0x0723, 0x9451, 0x9451, + "Genesys Logic", + "USB to SATA", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_SANE_SENSE ), + +/* Added by Maël GUERIN */ +UNUSUAL_DEV( 0x0603, 0x8611, 0x0000, 0xffff, + "Novatek", + "NTK96550-based camera", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BULK_IGNORE_TAG ), + +/* + * Reported by Hanno Boeck + * Taken from the Lycoris Kernel + */ +UNUSUAL_DEV( 0x0636, 0x0003, 0x0000, 0x9999, + "Vivitar", + "Vivicam 35Xx", + USB_SC_SCSI, USB_PR_BULK, NULL, + US_FL_FIX_INQUIRY ), + +UNUSUAL_DEV( 0x0644, 0x0000, 0x0100, 0x0100, + "TEAC", + "Floppy Drive", + USB_SC_UFI, USB_PR_CB, NULL, 0 ), + +/* Reported by Darsen Lu */ +UNUSUAL_DEV( 0x066f, 0x8000, 0x0001, 0x0001, + "SigmaTel", + "USBMSC Audio Player", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY ), + +/* Reported by Daniel Kukula */ +UNUSUAL_DEV( 0x067b, 0x1063, 0x0100, 0x0100, + "Prolific Technology, Inc.", + "Prolific Storage Gadget", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BAD_SENSE ), + +/* Reported by Rogerio Brito */ +UNUSUAL_DEV( 0x067b, 0x2317, 0x0001, 0x001, + "Prolific Technology, Inc.", + "Mass Storage Device", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NOT_LOCKABLE ), + +/* Reported by Richard -=[]=- */ +/* + * Change to bcdDeviceMin (0x0100 to 0x0001) reported by + * Thomas Bartosik + */ +UNUSUAL_DEV( 0x067b, 0x2507, 0x0001, 0x0100, + "Prolific Technology Inc.", + "Mass Storage Device", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY | US_FL_GO_SLOW ), + +/* Reported by Alex Butcher */ +UNUSUAL_DEV( 0x067b, 0x3507, 0x0001, 0x0101, + "Prolific Technology Inc.", + "ATAPI-6 Bridge Controller", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY | US_FL_GO_SLOW ), + +/* Submitted by Benny Sjostrand */ +UNUSUAL_DEV( 0x0686, 0x4011, 0x0001, 0x0001, + "Minolta", + "Dimage F300", + USB_SC_SCSI, USB_PR_BULK, NULL, 0 ), + +/* Reported by Miguel A. Fosas */ +UNUSUAL_DEV( 0x0686, 0x4017, 0x0001, 0x0001, + "Minolta", + "DIMAGE E223", + USB_SC_SCSI, USB_PR_DEVICE, NULL, 0 ), + +UNUSUAL_DEV( 0x0693, 0x0005, 0x0100, 0x0100, + "Hagiwara", + "Flashgate", + USB_SC_SCSI, USB_PR_BULK, NULL, 0 ), + +/* Reported by David Hamilton */ +UNUSUAL_DEV( 0x069b, 0x3004, 0x0001, 0x0001, + "Thomson Multimedia Inc.", + "RCA RD1080 MP3 Player", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY ), + +UNUSUAL_DEV( 0x06ca, 0x2003, 0x0100, 0x0100, + "Newer Technology", + "uSCSI", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + +/* Reported by Adrian Pilchowiec */ +UNUSUAL_DEV( 0x071b, 0x3203, 0x0000, 0x0000, + "RockChip", + "MP3", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_WP_DETECT | US_FL_MAX_SECTORS_64 | + US_FL_NO_READ_CAPACITY_16), + +/* + * Reported by Jean-Baptiste Onofre + * Support the following product : + * "Dane-Elec MediaTouch" + */ +UNUSUAL_DEV( 0x071b, 0x32bb, 0x0000, 0x0000, + "RockChip", + "MTP", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_WP_DETECT | US_FL_MAX_SECTORS_64), + +/* + * Reported by Massimiliano Ghilardi + * This USB MP3/AVI player device fails and disconnects if more than 128 + * sectors (64kB) are read/written in a single command, and may be present + * at least in the following products: + * "Magnex Digital Video Panel DVP 1800" + * "MP4 AIGO 4GB SLOT SD" + * "Teclast TL-C260 MP3" + * "i.Meizu PMP MP3/MP4" + * "Speed MV8 MP4 Audio Player" + */ +UNUSUAL_DEV( 0x071b, 0x3203, 0x0100, 0x0100, + "RockChip", + "ROCK MP3", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64), + +/* Reported by Olivier Blondeau */ +UNUSUAL_DEV( 0x0727, 0x0306, 0x0100, 0x0100, + "ATMEL", + "SND1 Storage", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE), + +/* Submitted by Roman Hodek */ +UNUSUAL_DEV( 0x0781, 0x0001, 0x0200, 0x0200, + "Sandisk", + "ImageMate SDDR-05a", + USB_SC_SCSI, USB_PR_CB, NULL, + US_FL_SINGLE_LUN ), + +UNUSUAL_DEV( 0x0781, 0x0002, 0x0009, 0x0009, + "SanDisk Corporation", + "ImageMate CompactFlash USB", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY ), + +UNUSUAL_DEV( 0x0781, 0x0100, 0x0100, 0x0100, + "Sandisk", + "ImageMate SDDR-12", + USB_SC_SCSI, USB_PR_CB, NULL, + US_FL_SINGLE_LUN ), + +/* Reported by Eero Volotinen */ +UNUSUAL_DEV( 0x07ab, 0xfccd, 0x0000, 0x9999, + "Freecom Technologies", + "FHD-Classic", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY), + +UNUSUAL_DEV( 0x07af, 0x0004, 0x0100, 0x0133, + "Microtech", + "USB-SCSI-DB25", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + +UNUSUAL_DEV( 0x07af, 0x0005, 0x0100, 0x0100, + "Microtech", + "USB-SCSI-HD50", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + +#ifdef NO_SDDR09 +UNUSUAL_DEV( 0x07af, 0x0006, 0x0100, 0x0100, + "Microtech", + "CameraMate", + USB_SC_SCSI, USB_PR_CB, NULL, + US_FL_SINGLE_LUN ), +#endif + +/* + * Datafab KECF-USB / Sagatek DCS-CF / Simpletech Flashlink UCF-100 + * Only revision 1.13 tested (same for all of the above devices, + * based on the Datafab DF-UG-07 chip). Needed for US_FL_FIX_INQUIRY. + * Submitted by Marek Michalkiewicz . + * See also http://martin.wilck.bei.t-online.de/#kecf . + */ +UNUSUAL_DEV( 0x07c4, 0xa400, 0x0000, 0xffff, + "Datafab", + "KECF-USB", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY | US_FL_FIX_CAPACITY ), + +/* + * Reported by Rauch Wolke + * and augmented by binbin (Bugzilla #12882) + */ +UNUSUAL_DEV( 0x07c4, 0xa4a5, 0x0000, 0xffff, + "Simple Tech/Datafab", + "CF+SM Reader", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE | US_FL_MAX_SECTORS_64 ), + +/* + * Casio QV 2x00/3x00/4000/8000 digital still cameras are not conformant + * to the USB storage specification in two ways: + * - They tell us they are using transport protocol CBI. In reality they + * are using transport protocol CB. + * - They don't like the INQUIRY command. So we must handle this command + * of the SCSI layer ourselves. + * - Some cameras with idProduct=0x1001 and bcdDevice=0x1000 have + * bInterfaceProtocol=0x00 (USB_PR_CBI) while others have 0x01 (USB_PR_CB). + * So don't remove the USB_PR_CB override! + * - Cameras with bcdDevice=0x9009 require the USB_SC_8070 override. + */ +UNUSUAL_DEV( 0x07cf, 0x1001, 0x1000, 0x9999, + "Casio", + "QV DigitalCamera", + USB_SC_8070, USB_PR_CB, NULL, + US_FL_NEED_OVERRIDE | US_FL_FIX_INQUIRY ), + +/* Submitted by Oleksandr Chumachenko */ +UNUSUAL_DEV( 0x07cf, 0x1167, 0x0100, 0x0100, + "Casio", + "EX-N1 DigitalCamera", + USB_SC_8070, USB_PR_DEVICE, NULL, 0), + +/* Submitted by Hartmut Wahl */ +UNUSUAL_DEV( 0x0839, 0x000a, 0x0001, 0x0001, + "Samsung", + "Digimax 410", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY), + +/* Reported by Luciano Rocha */ +UNUSUAL_DEV( 0x0840, 0x0082, 0x0001, 0x0001, + "Argosy", + "Storage", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY), + +/* Reported and patched by Nguyen Anh Quynh */ +UNUSUAL_DEV( 0x0840, 0x0084, 0x0001, 0x0001, + "Argosy", + "Storage", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY), + +/* Reported by Martijn Hijdra */ +UNUSUAL_DEV( 0x0840, 0x0085, 0x0001, 0x0001, + "Argosy", + "Storage", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY), + +/* Supplied with some Castlewood ORB removable drives */ +UNUSUAL_DEV( 0x084b, 0xa001, 0x0000, 0x9999, + "Castlewood Systems", + "USB to SCSI cable", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + +/* + * Entry and supporting patch by Theodore Kilgore . + * Flag will support Bulk devices which use a standards-violating 32-byte + * Command Block Wrapper. Here, the "DC2MEGA" cameras (several brands) with + * Grandtech GT892x chip, which request "Proprietary SCSI Bulk" support. + */ + +UNUSUAL_DEV( 0x084d, 0x0011, 0x0110, 0x0110, + "Grandtech", + "DC2MEGA", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BULK32), + +/* + * Reported by + * The device reports a vendor-specific device class, requiring an + * explicit vendor/product match. + */ +UNUSUAL_DEV( 0x0851, 0x1542, 0x0002, 0x0002, + "MagicPixel", + "FW_Omega2", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, 0), + +/* + * Andrew Lunn + * PanDigital Digital Picture Frame. Does not like ALLOW_MEDIUM_REMOVAL + * on LUN 4. + * Note: Vend:Prod clash with "Ltd Maxell WS30 Slim Digital Camera" + */ +UNUSUAL_DEV( 0x0851, 0x1543, 0x0200, 0x0200, + "PanDigital", + "Photo Frame", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NOT_LOCKABLE), + +UNUSUAL_DEV( 0x085a, 0x0026, 0x0100, 0x0133, + "Xircom", + "PortGear USB-SCSI (Mac USB Dock)", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + +UNUSUAL_DEV( 0x085a, 0x0028, 0x0100, 0x0133, + "Xircom", + "PortGear USB to SCSI Converter", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + +/* Submitted by Jan De Luyck */ +UNUSUAL_DEV( 0x08bd, 0x1100, 0x0000, 0x0000, + "CITIZEN", + "X1DE-USB", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_SINGLE_LUN), + +/* + * Submitted by Dylan Taft + * US_FL_IGNORE_RESIDUE Needed + */ +UNUSUAL_DEV( 0x08ca, 0x3103, 0x0100, 0x0100, + "AIPTEK", + "Aiptek USB Keychain MP3 Player", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE), + +/* + * Entry needed for flags. Moreover, all devices with this ID use + * bulk-only transport, but _some_ falsely report Control/Bulk instead. + * One example is "Trumpion Digital Research MYMP3". + * Submitted by Bjoern Brill + */ +UNUSUAL_DEV( 0x090a, 0x1001, 0x0100, 0x0100, + "Trumpion", + "t33520 USB Flash Card Controller", + USB_SC_DEVICE, USB_PR_BULK, NULL, + US_FL_NEED_OVERRIDE ), + +/* + * Reported by Filippo Bardelli + * The device reports a subclass of RBC, which is wrong. + */ +UNUSUAL_DEV( 0x090a, 0x1050, 0x0100, 0x0100, + "Trumpion Microelectronics, Inc.", + "33520 USB Digital Voice Recorder", + USB_SC_UFI, USB_PR_DEVICE, NULL, + 0), + +/* Trumpion Microelectronics MP3 player (felipe_alfaro@linuxmail.org) */ +UNUSUAL_DEV( 0x090a, 0x1200, 0x0000, 0x9999, + "Trumpion", + "MP3 player", + USB_SC_RBC, USB_PR_BULK, NULL, + 0 ), + +/* aeb */ +UNUSUAL_DEV( 0x090c, 0x1132, 0x0000, 0xffff, + "Feiya", + "5-in-1 Card Reader", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY ), + +/* + * Reported by Icenowy Zheng + * The SMI SM3350 USB-UFS bridge controller will enter a wrong state + * that do not process read/write command if a long sense is requested, + * so force to use 18-byte sense. + */ +UNUSUAL_DEV( 0x090c, 0x3350, 0x0000, 0xffff, + "SMI", + "SM3350 UFS-to-USB-Mass-Storage bridge", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BAD_SENSE ), + +/* + * Reported by Paul Hartman + * This card reader returns "Illegal Request, Logical Block Address + * Out of Range" for the first READ(10) after a new card is inserted. + */ +UNUSUAL_DEV( 0x090c, 0x6000, 0x0100, 0x0100, + "Feiya", + "SD/SDHC Card Reader", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_INITIAL_READ10 ), + +/* + * Patch by Tasos Sahanidis + * This flash drive always shows up with write protect enabled + * during the first mode sense. + */ +UNUSUAL_DEV(0x0951, 0x1697, 0x0100, 0x0100, + "Kingston", + "DT Ultimate G3", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_WP_DETECT), + +/* + * This Pentax still camera is not conformant + * to the USB storage specification: - + * - It does not like the INQUIRY command. So we must handle this command + * of the SCSI layer ourselves. + * Tested on Rev. 10.00 (0x1000) + * Submitted by James Courtier-Dutton + */ +UNUSUAL_DEV( 0x0a17, 0x0004, 0x1000, 0x1000, + "Pentax", + "Optio 2/3/400", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY ), + +/* + * These are virtual windows driver CDs, which the zd1211rw driver + * automatically converts into WLAN devices. + */ +UNUSUAL_DEV( 0x0ace, 0x2011, 0x0101, 0x0101, + "ZyXEL", + "G-220F USB-WLAN Install", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_DEVICE ), + +UNUSUAL_DEV( 0x0ace, 0x20ff, 0x0101, 0x0101, + "SiteCom", + "WL-117 USB-WLAN Install", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_DEVICE ), + +/* + * Reported by Dan Williams + * Option N.V. mobile broadband modems + * Ignore driver CD mode and force into modem mode by default. + */ + +/* iCON 225 */ +UNUSUAL_DEV( 0x0af0, 0x6971, 0x0000, 0x9999, + "Option N.V.", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, option_ms_init, + 0), + +/* + * Reported by F. Aben + * This device (wrongly) has a vendor-specific device descriptor. + * The entry is needed so usb-storage can bind to it's mass-storage + * interface as an interface driver + */ +UNUSUAL_DEV( 0x0af0, 0x7401, 0x0000, 0x0000, + "Option", + "GI 0401 SD-Card", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + 0 ), + +/* + * Reported by Jan Dumon + * These devices (wrongly) have a vendor-specific device descriptor. + * These entries are needed so usb-storage can bind to their mass-storage + * interface as an interface driver + */ +UNUSUAL_DEV( 0x0af0, 0x7501, 0x0000, 0x0000, + "Option", + "GI 0431 SD-Card", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + 0 ), + +UNUSUAL_DEV( 0x0af0, 0x7701, 0x0000, 0x0000, + "Option", + "GI 0451 SD-Card", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + 0 ), + +UNUSUAL_DEV( 0x0af0, 0x7706, 0x0000, 0x0000, + "Option", + "GI 0451 SD-Card", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + 0 ), + +UNUSUAL_DEV( 0x0af0, 0x7901, 0x0000, 0x0000, + "Option", + "GI 0452 SD-Card", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + 0 ), + +UNUSUAL_DEV( 0x0af0, 0x7A01, 0x0000, 0x0000, + "Option", + "GI 0461 SD-Card", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + 0 ), + +UNUSUAL_DEV( 0x0af0, 0x7A05, 0x0000, 0x0000, + "Option", + "GI 0461 SD-Card", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + 0 ), + +UNUSUAL_DEV( 0x0af0, 0x8300, 0x0000, 0x0000, + "Option", + "GI 033x SD-Card", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + 0 ), + +UNUSUAL_DEV( 0x0af0, 0x8302, 0x0000, 0x0000, + "Option", + "GI 033x SD-Card", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + 0 ), + +UNUSUAL_DEV( 0x0af0, 0x8304, 0x0000, 0x0000, + "Option", + "GI 033x SD-Card", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + 0 ), + +UNUSUAL_DEV( 0x0af0, 0xc100, 0x0000, 0x0000, + "Option", + "GI 070x SD-Card", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + 0 ), + +UNUSUAL_DEV( 0x0af0, 0xd057, 0x0000, 0x0000, + "Option", + "GI 1505 SD-Card", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + 0 ), + +UNUSUAL_DEV( 0x0af0, 0xd058, 0x0000, 0x0000, + "Option", + "GI 1509 SD-Card", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + 0 ), + +UNUSUAL_DEV( 0x0af0, 0xd157, 0x0000, 0x0000, + "Option", + "GI 1515 SD-Card", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + 0 ), + +UNUSUAL_DEV( 0x0af0, 0xd257, 0x0000, 0x0000, + "Option", + "GI 1215 SD-Card", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + 0 ), + +UNUSUAL_DEV( 0x0af0, 0xd357, 0x0000, 0x0000, + "Option", + "GI 1505 SD-Card", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + 0 ), + +/* Reported by Namjae Jeon */ +UNUSUAL_DEV(0x0bc2, 0x2300, 0x0000, 0x9999, + "Seagate", + "Portable HDD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_WRITE_CACHE), + +/* Reported by Ben Efros */ +UNUSUAL_DEV( 0x0bc2, 0x3010, 0x0000, 0x0000, + "Seagate", + "FreeAgent Pro", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_SANE_SENSE ), + +/* Reported by Kris Lindgren */ +UNUSUAL_DEV( 0x0bc2, 0x3332, 0x0000, 0x9999, + "Seagate", + "External", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_WP_DETECT ), + +/* + * Reported by Zenm Chen + * Ignore driver CD mode, otherwise usb_modeswitch may fail to switch + * the device into Wi-Fi mode. + */ +UNUSUAL_DEV( 0x0bda, 0x1a2b, 0x0000, 0xffff, + "Realtek", + "DISK", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_DEVICE ), + +/* + * Reported by Zenm Chen + * Ignore driver CD mode, otherwise usb_modeswitch may fail to switch + * the device into Wi-Fi mode. + */ +UNUSUAL_DEV( 0x0bda, 0xa192, 0x0000, 0xffff, + "Realtek", + "DISK", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_DEVICE ), + +UNUSUAL_DEV( 0x0d49, 0x7310, 0x0000, 0x9999, + "Maxtor", + "USB to SATA", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_SANE_SENSE), + +/* + * Pete Zaitcev , bz#164688. + * The device blatantly ignores LUN and returns 1 in GetMaxLUN. + */ +UNUSUAL_DEV( 0x0c45, 0x1060, 0x0100, 0x0100, + "Unknown", + "Unknown", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_SINGLE_LUN ), + +/* Submitted by Joris Struyve */ +UNUSUAL_DEV( 0x0d96, 0x410a, 0x0001, 0xffff, + "Medion", + "MD 7425", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY), + +/* + * Entry for Jenoptik JD 5200z3 + * + * email: car.busse@gmx.de + */ +UNUSUAL_DEV( 0x0d96, 0x5200, 0x0001, 0x0200, + "Jenoptik", + "JD 5200 z3", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_FIX_INQUIRY), + +/* Reported by Jason Johnston */ +UNUSUAL_DEV( 0x0dc4, 0x0073, 0x0000, 0x0000, + "Macpower Technology Co.LTD.", + "USB 2.0 3.5\" DEVICE", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY), + +/* + * Reported by Lubomir Blaha + * I _REALLY_ don't know what 3rd, 4th number and all defines mean, but this + * works for me. Can anybody correct these values? (I able to test corrected + * version.) + */ +UNUSUAL_DEV( 0x0dd8, 0x1060, 0x0000, 0xffff, + "Netac", + "USB-CF-Card", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY ), + +/* + * Reported by Edward Chapman (taken from linux-usb mailing list) + * Netac OnlyDisk Mini U2CV2 512MB USB 2.0 Flash Drive + */ +UNUSUAL_DEV( 0x0dd8, 0xd202, 0x0000, 0x9999, + "Netac", + "USB Flash Disk", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + + +/* + * Patch by Stephan Walter + * I don't know why, but it works... + */ +UNUSUAL_DEV( 0x0dda, 0x0001, 0x0012, 0x0012, + "WINWARD", + "Music Disk", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* Reported by Ian McConnell */ +UNUSUAL_DEV( 0x0dda, 0x0301, 0x0012, 0x0012, + "PNP_MP3", + "PNP_MP3 PLAYER", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* Reported by Jim McCloskey */ +UNUSUAL_DEV( 0x0e21, 0x0520, 0x0100, 0x0100, + "Cowon Systems", + "iAUDIO M5", + USB_SC_DEVICE, USB_PR_BULK, NULL, + US_FL_NEED_OVERRIDE ), + +/* Submitted by Antoine Mairesse */ +UNUSUAL_DEV( 0x0ed1, 0x6660, 0x0100, 0x0300, + "USB", + "Solid state disk", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY ), + +/* + * Submitted by Daniel Drake + * Reported by dayul on the Gentoo Forums + */ +UNUSUAL_DEV( 0x0ea0, 0x2168, 0x0110, 0x0110, + "Ours Technology", + "Flash Disk", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* Reported by Rastislav Stanik */ +UNUSUAL_DEV( 0x0ea0, 0x6828, 0x0110, 0x0110, + "USB", + "Flash Disk", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* + * Reported by Benjamin Schiller + * It is also sold by Easylite as DJ 20 + */ +UNUSUAL_DEV( 0x0ed1, 0x7636, 0x0103, 0x0103, + "Typhoon", + "My DJ 1820", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE | US_FL_GO_SLOW | US_FL_MAX_SECTORS_64), + +/* + * Patch by Leonid Petrov mail at lpetrov.net + * Reported by Robert Spitzenpfeil + * http://www.qbik.ch/usb/devices/showdev.php?id=1705 + * Updated to 103 device by MJ Ray mjr at phonecoop.coop + */ +UNUSUAL_DEV( 0x0f19, 0x0103, 0x0100, 0x0100, + "Oracom Co., Ltd", + "ORC-200M", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* + * David Kuehling : + * for MP3-Player AVOX WSX-300ER (bought in Japan). Reports lots of SCSI + * errors when trying to write. + */ +UNUSUAL_DEV( 0x0f19, 0x0105, 0x0100, 0x0100, + "C-MEX", + "A-VOX", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* Submitted by Nick Holloway */ +UNUSUAL_DEV( 0x0f88, 0x042e, 0x0100, 0x0100, + "VTech", + "Kidizoom", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY ), + +/* Reported by Moritz Moeller-Herrmann */ +UNUSUAL_DEV( 0x0fca, 0x8004, 0x0201, 0x0201, + "Research In Motion", + "BlackBerry Bold 9000", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64 ), + +/* Reported by Michael Stattmann */ +UNUSUAL_DEV( 0x0fce, 0xd008, 0x0000, 0x0000, + "Sony Ericsson", + "V800-Vodafone 802", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_WP_DETECT ), + +/* Reported by The Solutor */ +UNUSUAL_DEV( 0x0fce, 0xd0e1, 0x0000, 0x0000, + "Sony Ericsson", + "MD400", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_DEVICE), + +/* + * Reported by Jan Mate + * and by Soeren Sonnenburg + */ +UNUSUAL_DEV( 0x0fce, 0xe030, 0x0000, 0x0000, + "Sony Ericsson", + "P990i", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY | US_FL_IGNORE_RESIDUE ), + +/* Reported by Emmanuel Vasilakis */ +UNUSUAL_DEV( 0x0fce, 0xe031, 0x0000, 0x0000, + "Sony Ericsson", + "M600i", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE | US_FL_FIX_CAPACITY ), + +/* Reported by Ricardo Barberis */ +UNUSUAL_DEV( 0x0fce, 0xe092, 0x0000, 0x0000, + "Sony Ericsson", + "P1i", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* + * Reported by Kevin Cernekee + * Tested on hardware version 1.10. + * Entry is needed only for the initializer function override. + * Devices with bcd > 110 seem to not need it while those + * with bcd < 110 appear to need it. + */ +UNUSUAL_DEV( 0x1019, 0x0c55, 0x0000, 0x0110, + "Desknote", + "UCR-61S2B", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_ucr61s2b_init, + 0 ), + +UNUSUAL_DEV( 0x1058, 0x0704, 0x0000, 0x9999, + "Western Digital", + "External HDD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_SANE_SENSE), + +/* Reported by Namjae Jeon */ +UNUSUAL_DEV(0x1058, 0x070a, 0x0000, 0x9999, + "Western Digital", + "My Passport HDD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_WRITE_CACHE), + +/* + * Reported by Fabio Venturi + * The device reports a vendor-specific bDeviceClass. + */ +UNUSUAL_DEV( 0x10d6, 0x2200, 0x0100, 0x0100, + "Actions Semiconductor", + "Mtp device", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + 0), + +/* + * Reported by Pascal Terjan + * Ignore driver CD mode and force into modem mode by default. + */ +UNUSUAL_DEV( 0x1186, 0x3e04, 0x0000, 0x0000, + "D-Link", + "USB Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, option_ms_init, US_FL_IGNORE_DEVICE), + +/* + * Reported by Kevin Lloyd + * Entry is needed for the initializer function override, + * which instructs the device to load as a modem + * device. + */ +UNUSUAL_DEV( 0x1199, 0x0fff, 0x0000, 0x9999, + "Sierra Wireless", + "USB MMC Storage", + USB_SC_DEVICE, USB_PR_DEVICE, sierra_ms_init, + 0), + +/* + * Reported by Jaco Kroon + * The usb-storage module found on the Digitech GNX4 (and supposedly other + * devices) misbehaves and causes a bunch of invalid I/O errors. + */ +UNUSUAL_DEV( 0x1210, 0x0003, 0x0100, 0x0100, + "Digitech HMG", + "DigiTech Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* + * Reported by fangxiaozhi + * This brings the HUAWEI data card devices into multi-port mode + */ +UNUSUAL_DEV( 0x12d1, 0x1001, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1003, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1004, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1401, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1402, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1403, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1404, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1405, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1406, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1407, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1408, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1409, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x140A, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x140B, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x140C, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x140D, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x140E, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x140F, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1410, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1411, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1412, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1413, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1414, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1415, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1416, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1417, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1418, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1419, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x141A, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x141B, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x141C, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x141D, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x141E, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x141F, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1420, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1421, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1422, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1423, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1424, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1425, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1426, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1427, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1428, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1429, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x142A, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x142B, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x142C, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x142D, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x142E, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x142F, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1430, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1431, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1432, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1433, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1434, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1435, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1436, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1437, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1438, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1439, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x143A, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x143B, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x143C, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x143D, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x143E, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x143F, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + 0), + +/* Reported by Vilius Bilinkevicius */ +UNUSUAL_DEV( 0x1370, 0x6828, 0x0110, 0x0110, + "SWISSBIT", + "Black Silver", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* + * Reported by Tobias Jakobi + * The INIC-3619 bridge is used in the StarTech SLSODDU33B + * SATA-USB enclosure for slimline optical drives. + * + * The quirk enables MakeMKV to properly exchange keys with + * an installed BD drive. + */ +UNUSUAL_DEV( 0x13fd, 0x3609, 0x0209, 0x0209, + "Initio Corporation", + "INIC-3619", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* Reported by Qinglin Ye */ +UNUSUAL_DEV( 0x13fe, 0x3600, 0x0100, 0x0100, + "Kingston", + "DT 101 G2", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BULK_IGNORE_TAG ), + +/* Reported by Francesco Foresti */ +UNUSUAL_DEV( 0x14cd, 0x6600, 0x0201, 0x0201, + "Super Top", + "IDE DEVICE", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* Reported by Michael Büsch */ +UNUSUAL_DEV( 0x152d, 0x0567, 0x0114, 0x0117, + "JMicron", + "USB to ATA/ATAPI Bridge", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BROKEN_FUA ), + +/* Reported by David Kozub */ +UNUSUAL_DEV(0x152d, 0x0578, 0x0000, 0x9999, + "JMicron", + "JMS567", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BROKEN_FUA), + +/* + * Reported by Alexandre Oliva + * JMicron responds to USN and several other SCSI ioctls with a + * residue that causes subsequent I/O requests to fail. */ +UNUSUAL_DEV( 0x152d, 0x2329, 0x0100, 0x0100, + "JMicron", + "USB to ATA/ATAPI Bridge", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE | US_FL_SANE_SENSE ), + +/* Reported by Dmitry Nezhevenko */ +UNUSUAL_DEV( 0x152d, 0x2566, 0x0114, 0x0114, + "JMicron", + "USB to ATA/ATAPI Bridge", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BROKEN_FUA ), + +/* Reported by Teijo Kinnunen */ +UNUSUAL_DEV( 0x152d, 0x2567, 0x0117, 0x0117, + "JMicron", + "USB to ATA/ATAPI Bridge", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BROKEN_FUA ), + +/* Reported-by George Cherian */ +UNUSUAL_DEV(0x152d, 0x9561, 0x0000, 0x9999, + "JMicron", + "JMS56x", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_REPORT_OPCODES), + +/* + * Entrega Technologies U1-SC25 (later Xircom PortGear PGSCSI) + * and Mac USB Dock USB-SCSI */ +UNUSUAL_DEV( 0x1645, 0x0007, 0x0100, 0x0133, + "Entrega Technologies", + "USB to SCSI Converter", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + +/* + * Reported by Robert Schedel + * Note: this is a 'super top' device like the above 14cd/6600 device + */ +UNUSUAL_DEV( 0x1652, 0x6600, 0x0201, 0x0201, + "Teac", + "HD-35PUK-B", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* Reported by Oliver Neukum */ +UNUSUAL_DEV( 0x174c, 0x55aa, 0x0100, 0x0100, + "ASMedia", + "AS2105", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NEEDS_CAP16), + +/* Reported by Jesse Feddema */ +UNUSUAL_DEV( 0x177f, 0x0400, 0x0000, 0x0000, + "Yarvik", + "PMP400", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BULK_IGNORE_TAG | US_FL_MAX_SECTORS_64 ), + +UNUSUAL_DEV( 0x1822, 0x0001, 0x0000, 0x9999, + "Ariston Technologies", + "iConnect USB to SCSI adapter", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + +/* + * Reported by Hans de Goede + * These Appotech controllers are found in Picture Frames, they provide a + * (buggy) emulation of a cdrom drive which contains the windows software + * Uploading of pictures happens over the corresponding /dev/sg device. + */ +UNUSUAL_DEV( 0x1908, 0x1315, 0x0000, 0x0000, + "BUILDWIN", + "Photo Frame", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BAD_SENSE ), +UNUSUAL_DEV( 0x1908, 0x1320, 0x0000, 0x0000, + "BUILDWIN", + "Photo Frame", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BAD_SENSE ), +UNUSUAL_DEV( 0x1908, 0x3335, 0x0200, 0x0200, + "BUILDWIN", + "Photo Frame", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_READ_DISC_INFO ), + +/* + * Reported by Matthias Schwarzott + * The Amazon Kindle treats SYNCHRONIZE CACHE as an indication that + * the host may be finished with it, and automatically ejects its + * emulated media unless it receives another command within one second. + */ +UNUSUAL_DEV( 0x1949, 0x0004, 0x0000, 0x9999, + "Amazon", + "Kindle", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_SENSE_AFTER_SYNC ), + +/* + * Reported by Oliver Neukum + * This device morphes spontaneously into another device if the access + * pattern of Windows isn't followed. Thus writable media would be dirty + * if the initial instance is used. So the device is limited to its + * virtual CD. + * And yes, the concept that BCD goes up to 9 is not heeded + */ +UNUSUAL_DEV( 0x19d2, 0x1225, 0x0000, 0xffff, + "ZTE,Incorporated", + "ZTE WCDMA Technologies MSM", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_SINGLE_LUN ), + +/* + * Reported by Sven Geggus + * This encrypted pen drive returns bogus data for the initial READ(10). + */ +UNUSUAL_DEV( 0x1b1c, 0x1ab5, 0x0200, 0x0200, + "Corsair", + "Padlock v2", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_INITIAL_READ10 ), + +/* + * Reported by Hans de Goede + * These are mini projectors using USB for both power and video data transport + * The usb-storage interface is a virtual windows driver CD, which the gm12u320 + * driver automatically converts into framebuffer & kms dri device nodes. + */ +UNUSUAL_DEV( 0x1de1, 0xc102, 0x0000, 0xffff, + "Grain-media Technology Corp.", + "USB3.0 Device GM12U320", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_DEVICE ), + +/* + * Patch by Richard Schütz + * This external hard drive enclosure uses a JMicron chip which + * needs the US_FL_IGNORE_RESIDUE flag to work properly. + */ +UNUSUAL_DEV( 0x1e68, 0x001b, 0x0000, 0x0000, + "TrekStor GmbH & Co. KG", + "DataStation maxi g.u", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE | US_FL_SANE_SENSE ), + +/* Reported by Jasper Mackenzie */ +UNUSUAL_DEV( 0x1e74, 0x4621, 0x0000, 0x0000, + "Coby Electronics", + "MP3 Player", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BULK_IGNORE_TAG | US_FL_MAX_SECTORS_64 ), + +/* Reported by Witold Lipieta */ +UNUSUAL_DEV( 0x1fc9, 0x0117, 0x0100, 0x0100, + "NXP Semiconductors", + "PN7462AU", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* Supplied with some Castlewood ORB removable drives */ +UNUSUAL_DEV( 0x2027, 0xa001, 0x0000, 0x9999, + "Double-H Technology", + "USB to SCSI Intelligent Cable", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + +/* + * Reported by DocMAX , + * Thomas Weißschuh + * and Daniel Brát + */ +UNUSUAL_DEV( 0x2109, 0x0715, 0x0000, 0x9999, + "VIA Labs, Inc.", + "VL817 SATA Bridge", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_UAS), + +UNUSUAL_DEV( 0x2116, 0x0320, 0x0001, 0x0001, + "ST", + "2A", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY), + +/* + * patch submitted by Davide Perini + * and Renato Perini + */ +UNUSUAL_DEV( 0x22b8, 0x3010, 0x0001, 0x0001, + "Motorola", + "RAZR V3x", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY | US_FL_IGNORE_RESIDUE ), + +/* + * Patch by Constantin Baranov + * Report by Andreas Koenecke. + * Motorola ROKR Z6. + */ +UNUSUAL_DEV( 0x22b8, 0x6426, 0x0101, 0x0101, + "Motorola", + "MSnc.", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY | US_FL_FIX_CAPACITY | US_FL_BULK_IGNORE_TAG), + +/* Reported by Radovan Garabik */ +UNUSUAL_DEV( 0x2735, 0x100b, 0x0000, 0x9999, + "MPIO", + "HS200", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_GO_SLOW ), + +/* Reported-by: Tim Anderson */ +UNUSUAL_DEV( 0x2ca3, 0x0031, 0x0000, 0x9999, + "DJI", + "CineSSD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_ATA_1X), + +/* + * Reported by Frederic Marchal + * Mio Moov 330 + */ +UNUSUAL_DEV( 0x3340, 0xffff, 0x0000, 0x0000, + "Mitac", + "Mio DigiWalker USB Sync", + USB_SC_DEVICE,USB_PR_DEVICE,NULL, + US_FL_MAX_SECTORS_64 ), + +/* Reported by Cyril Roelandt */ +UNUSUAL_DEV( 0x357d, 0x7788, 0x0114, 0x0114, + "JMicron", + "USB to ATA/ATAPI Bridge", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BROKEN_FUA | US_FL_IGNORE_UAS ), + +/* Reported by Andrey Rahmatullin */ +UNUSUAL_DEV( 0x4102, 0x1020, 0x0100, 0x0100, + "iRiver", + "MP3 T10", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* Reported by Sergey Pinaev */ +UNUSUAL_DEV( 0x4102, 0x1059, 0x0000, 0x0000, + "iRiver", + "P7K", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64 ), + +/* + * David Härdeman + * The key makes the SCSI stack print confusing (but harmless) messages + */ +UNUSUAL_DEV( 0x4146, 0xba01, 0x0100, 0x0100, + "Iomega", + "Micro Mini 1GB", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE ), + +/* "G-DRIVE" external HDD hangs on write without these. + * Patch submitted by Alexander Kappner + */ +UNUSUAL_DEV(0x4971, 0x8024, 0x0000, 0x9999, + "SimpleTech", + "External HDD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_ALWAYS_SYNC), + +/* + * Nick Bowler + * SCSI stack spams (otherwise harmless) error messages. + */ +UNUSUAL_DEV( 0xc251, 0x4003, 0x0100, 0x0100, + "Keil Software, Inc.", + "V2M MotherBoard", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NOT_LOCKABLE), + +/* + * Reported by Icenowy Zheng + * This is an interface for vendor-specific cryptic commands instead + * of real USB storage device. + */ +UNUSUAL_DEV( 0xe5b7, 0x0811, 0x0100, 0x0100, + "ZhuHai JieLi Technology", + "JieLi BR21", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_DEVICE), + +/* Reported by Andrew Simmons */ +UNUSUAL_DEV( 0xed06, 0x4500, 0x0001, 0x0001, + "DataStor", + "USB4500 FW1.04", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_CAPACITY_HEURISTICS), + +/* Reported by Alessio Treglia */ +UNUSUAL_DEV( 0xed10, 0x7636, 0x0001, 0x0001, + "TGE", + "Digital MP3 Audio Player", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE ), + +/* Unusual uas devices */ +#if IS_ENABLED(CONFIG_USB_UAS) +#include "unusual_uas.h" +#endif + +/* Control/Bulk transport for all SubClass values */ +USUAL_DEV(USB_SC_RBC, USB_PR_CB), +USUAL_DEV(USB_SC_8020, USB_PR_CB), +USUAL_DEV(USB_SC_QIC, USB_PR_CB), +USUAL_DEV(USB_SC_UFI, USB_PR_CB), +USUAL_DEV(USB_SC_8070, USB_PR_CB), +USUAL_DEV(USB_SC_SCSI, USB_PR_CB), + +/* Control/Bulk/Interrupt transport for all SubClass values */ +USUAL_DEV(USB_SC_RBC, USB_PR_CBI), +USUAL_DEV(USB_SC_8020, USB_PR_CBI), +USUAL_DEV(USB_SC_QIC, USB_PR_CBI), +USUAL_DEV(USB_SC_UFI, USB_PR_CBI), +USUAL_DEV(USB_SC_8070, USB_PR_CBI), +USUAL_DEV(USB_SC_SCSI, USB_PR_CBI), + +/* Bulk-only transport for all SubClass values */ +USUAL_DEV(USB_SC_RBC, USB_PR_BULK), +USUAL_DEV(USB_SC_8020, USB_PR_BULK), +USUAL_DEV(USB_SC_QIC, USB_PR_BULK), +USUAL_DEV(USB_SC_UFI, USB_PR_BULK), +USUAL_DEV(USB_SC_8070, USB_PR_BULK), +USUAL_DEV(USB_SC_SCSI, USB_PR_BULK), diff --git a/drivers/usb/storage/unusual_ene_ub6250.h b/drivers/usb/storage/unusual_ene_ub6250.h new file mode 100644 index 000000000..a3b32abc2 --- /dev/null +++ b/drivers/usb/storage/unusual_ene_ub6250.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +#if defined(CONFIG_USB_STORAGE_ENE_UB6250) || \ + defined(CONFIG_USB_STORAGE_ENE_UB6250_MODULE) + +UNUSUAL_DEV(0x0cf2, 0x6250, 0x0000, 0x9999, + "ENE", + "ENE UB6250 reader", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, 0), + +#endif /* defined(CONFIG_USB_STORAGE_ENE_UB6250) || ... */ diff --git a/drivers/usb/storage/unusual_freecom.h b/drivers/usb/storage/unusual_freecom.h new file mode 100644 index 000000000..9ca686364 --- /dev/null +++ b/drivers/usb/storage/unusual_freecom.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Unusual Devices File for the Freecom USB/IDE adaptor + */ + +#if defined(CONFIG_USB_STORAGE_FREECOM) || \ + defined(CONFIG_USB_STORAGE_FREECOM_MODULE) + +UNUSUAL_DEV( 0x07ab, 0xfc01, 0x0000, 0x9999, + "Freecom", + "USB-IDE", + USB_SC_QIC, USB_PR_FREECOM, init_freecom, 0), + +#endif /* defined(CONFIG_USB_STORAGE_FREECOM) || ... */ diff --git a/drivers/usb/storage/unusual_isd200.h b/drivers/usb/storage/unusual_isd200.h new file mode 100644 index 000000000..f248190bd --- /dev/null +++ b/drivers/usb/storage/unusual_isd200.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Unusual Devices File for In-System Design, Inc. ISD200 ASIC + */ + +#if defined(CONFIG_USB_STORAGE_ISD200) || \ + defined(CONFIG_USB_STORAGE_ISD200_MODULE) + +UNUSUAL_DEV( 0x054c, 0x002b, 0x0100, 0x0110, + "Sony", + "Portable USB Harddrive V2", + USB_SC_ISD200, USB_PR_BULK, isd200_Initialization, + 0), + +UNUSUAL_DEV( 0x05ab, 0x0031, 0x0100, 0x0110, + "In-System", + "USB/IDE Bridge (ATA/ATAPI)", + USB_SC_ISD200, USB_PR_BULK, isd200_Initialization, + 0), + +UNUSUAL_DEV( 0x05ab, 0x0301, 0x0100, 0x0110, + "In-System", + "Portable USB Harddrive V2", + USB_SC_ISD200, USB_PR_BULK, isd200_Initialization, + 0), + +UNUSUAL_DEV( 0x05ab, 0x0351, 0x0100, 0x0110, + "In-System", + "Portable USB Harddrive V2", + USB_SC_ISD200, USB_PR_BULK, isd200_Initialization, + 0), + +UNUSUAL_DEV( 0x05ab, 0x5701, 0x0100, 0x0110, + "In-System", + "USB Storage Adapter V2", + USB_SC_ISD200, USB_PR_BULK, isd200_Initialization, + 0), + +UNUSUAL_DEV( 0x0bf6, 0xa001, 0x0100, 0x0110, + "ATI", + "USB Cable 205", + USB_SC_ISD200, USB_PR_BULK, isd200_Initialization, + 0), + +#endif /* defined(CONFIG_USB_STORAGE_ISD200) || ... */ diff --git a/drivers/usb/storage/unusual_jumpshot.h b/drivers/usb/storage/unusual_jumpshot.h new file mode 100644 index 000000000..44878f849 --- /dev/null +++ b/drivers/usb/storage/unusual_jumpshot.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Unusual Devices File for the Lexar "Jumpshot" Compact Flash reader + */ + +#if defined(CONFIG_USB_STORAGE_JUMPSHOT) || \ + defined(CONFIG_USB_STORAGE_JUMPSHOT_MODULE) + +UNUSUAL_DEV( 0x05dc, 0x0001, 0x0000, 0x0001, + "Lexar", + "Jumpshot USB CF Reader", + USB_SC_SCSI, USB_PR_JUMPSHOT, NULL, + US_FL_NEED_OVERRIDE), + +#endif /* defined(CONFIG_USB_STORAGE_JUMPSHOT) || ... */ diff --git a/drivers/usb/storage/unusual_karma.h b/drivers/usb/storage/unusual_karma.h new file mode 100644 index 000000000..9fbed4cbc --- /dev/null +++ b/drivers/usb/storage/unusual_karma.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Unusual Devices File for the Rio Karma + */ + +#if defined(CONFIG_USB_STORAGE_KARMA) || \ + defined(CONFIG_USB_STORAGE_KARMA_MODULE) + +UNUSUAL_DEV( 0x045a, 0x5210, 0x0101, 0x0101, + "Rio", + "Rio Karma", + USB_SC_SCSI, USB_PR_KARMA, rio_karma_init, 0), + +#endif /* defined(CONFIG_USB_STORAGE_KARMA) || ... */ diff --git a/drivers/usb/storage/unusual_onetouch.h b/drivers/usb/storage/unusual_onetouch.h new file mode 100644 index 000000000..cdfee8f6c --- /dev/null +++ b/drivers/usb/storage/unusual_onetouch.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Unusual Devices File for the Maxtor OneTouch USB hard drive's button + */ + +#if defined(CONFIG_USB_STORAGE_ONETOUCH) || \ + defined(CONFIG_USB_STORAGE_ONETOUCH_MODULE) + +/* + * Submitted by: Nick Sillik + * Needed for OneTouch extension to usb-storage + */ +UNUSUAL_DEV( 0x0d49, 0x7000, 0x0000, 0x9999, + "Maxtor", + "OneTouch External Harddrive", + USB_SC_DEVICE, USB_PR_DEVICE, onetouch_connect_input, + 0), + +UNUSUAL_DEV( 0x0d49, 0x7010, 0x0000, 0x9999, + "Maxtor", + "OneTouch External Harddrive", + USB_SC_DEVICE, USB_PR_DEVICE, onetouch_connect_input, + 0), + +#endif /* defined(CONFIG_USB_STORAGE_ONETOUCH) || ... */ diff --git a/drivers/usb/storage/unusual_realtek.h b/drivers/usb/storage/unusual_realtek.h new file mode 100644 index 000000000..945dcb19d --- /dev/null +++ b/drivers/usb/storage/unusual_realtek.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Driver for Realtek RTS51xx USB card reader + * + * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * + * Author: + * wwang (wei_wang@realsil.com.cn) + * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China + */ + +#if defined(CONFIG_USB_STORAGE_REALTEK) || \ + defined(CONFIG_USB_STORAGE_REALTEK_MODULE) + +UNUSUAL_DEV(0x0bda, 0x0138, 0x0000, 0x9999, + "Realtek", + "USB Card Reader", + USB_SC_DEVICE, USB_PR_DEVICE, init_realtek_cr, 0), + +UNUSUAL_DEV(0x0bda, 0x0153, 0x0000, 0x9999, + "Realtek", + "USB Card Reader", + USB_SC_DEVICE, USB_PR_DEVICE, init_realtek_cr, 0), + +UNUSUAL_DEV(0x0bda, 0x0158, 0x0000, 0x9999, + "Realtek", + "USB Card Reader", + USB_SC_DEVICE, USB_PR_DEVICE, init_realtek_cr, 0), + +UNUSUAL_DEV(0x0bda, 0x0159, 0x0000, 0x9999, + "Realtek", + "USB Card Reader", + USB_SC_DEVICE, USB_PR_DEVICE, init_realtek_cr, 0), + +UNUSUAL_DEV(0x0bda, 0x0177, 0x0000, 0x9999, + "Realtek", + "USB Card Reader", + USB_SC_DEVICE, USB_PR_DEVICE, init_realtek_cr, 0), + +UNUSUAL_DEV(0x0bda, 0x0184, 0x0000, 0x9999, + "Realtek", + "USB Card Reader", + USB_SC_DEVICE, USB_PR_DEVICE, init_realtek_cr, 0), + +#endif /* defined(CONFIG_USB_STORAGE_REALTEK) || ... */ diff --git a/drivers/usb/storage/unusual_sddr09.h b/drivers/usb/storage/unusual_sddr09.h new file mode 100644 index 000000000..bfb650974 --- /dev/null +++ b/drivers/usb/storage/unusual_sddr09.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Unusual Devices File for SanDisk SDDR-09 SmartMedia reader + */ + +#if defined(CONFIG_USB_STORAGE_SDDR09) || \ + defined(CONFIG_USB_STORAGE_SDDR09_MODULE) + +UNUSUAL_DEV( 0x0436, 0x0005, 0x0100, 0x0100, + "Microtech", + "CameraMate (DPCM_USB)", + USB_SC_SCSI, USB_PR_DPCM_USB, NULL, 0), + +UNUSUAL_DEV( 0x04e6, 0x0003, 0x0000, 0x9999, + "Sandisk", + "ImageMate SDDR09", + USB_SC_SCSI, USB_PR_EUSB_SDDR09, usb_stor_sddr09_init, + 0), + +/* This entry is from Andries.Brouwer@cwi.nl */ +UNUSUAL_DEV( 0x04e6, 0x0005, 0x0100, 0x0208, + "SCM Microsystems", + "eUSB SmartMedia / CompactFlash Adapter", + USB_SC_SCSI, USB_PR_DPCM_USB, usb_stor_sddr09_dpcm_init, + 0), + +UNUSUAL_DEV( 0x066b, 0x0105, 0x0100, 0x0100, + "Olympus", + "Camedia MAUSB-2", + USB_SC_SCSI, USB_PR_EUSB_SDDR09, usb_stor_sddr09_init, + 0), + +UNUSUAL_DEV( 0x0781, 0x0200, 0x0000, 0x9999, + "Sandisk", + "ImageMate SDDR-09", + USB_SC_SCSI, USB_PR_EUSB_SDDR09, usb_stor_sddr09_init, + 0), + +UNUSUAL_DEV( 0x07af, 0x0006, 0x0100, 0x0100, + "Microtech", + "CameraMate (DPCM_USB)", + USB_SC_SCSI, USB_PR_DPCM_USB, NULL, 0), + +#endif /* defined(CONFIG_USB_STORAGE_SDDR09) || ... */ diff --git a/drivers/usb/storage/unusual_sddr55.h b/drivers/usb/storage/unusual_sddr55.h new file mode 100644 index 000000000..6d6f76eb0 --- /dev/null +++ b/drivers/usb/storage/unusual_sddr55.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Unusual Devices File for SanDisk SDDR-55 SmartMedia reader + */ + +#if defined(CONFIG_USB_STORAGE_SDDR55) || \ + defined(CONFIG_USB_STORAGE_SDDR55_MODULE) + +/* Contributed by Peter Waechtler */ +UNUSUAL_DEV( 0x07c4, 0xa103, 0x0000, 0x9999, + "Datafab", + "MDSM-B reader", + USB_SC_SCSI, USB_PR_SDDR55, NULL, + US_FL_FIX_INQUIRY), + +/* SM part - aeb */ +UNUSUAL_DEV( 0x07c4, 0xa109, 0x0000, 0xffff, + "Datafab Systems, Inc.", + "USB to CF + SM Combo (LC1)", + USB_SC_SCSI, USB_PR_SDDR55, NULL, 0), + +UNUSUAL_DEV( 0x0c0b, 0xa109, 0x0000, 0xffff, + "Acomdata", + "SM", + USB_SC_SCSI, USB_PR_SDDR55, NULL, 0), + +UNUSUAL_DEV( 0x55aa, 0xa103, 0x0000, 0x9999, + "Sandisk", + "ImageMate SDDR55", + USB_SC_SCSI, USB_PR_SDDR55, NULL, 0), + +#endif /* defined(CONFIG_USB_STORAGE_SDDR55) || ... */ diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h new file mode 100644 index 000000000..d6f86d5db --- /dev/null +++ b/drivers/usb/storage/unusual_uas.h @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Driver for USB Attached SCSI devices - Unusual Devices File + * + * (c) 2013 Hans de Goede + * + * Based on the same file for the usb-storage driver, which is: + * (c) 2000-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * (c) 2000 Adam J. Richter (adam@yggdrasil.com), Yggdrasil Computing, Inc. + */ + +/* + * IMPORTANT NOTE: This file must be included in another file which defines + * a UNUSUAL_DEV macro before this file is included. + */ + +/* + * If you edit this file, please try to keep it sorted first by VendorID, + * then by ProductID. + * + * If you want to add an entry for this file, be sure to include the + * following information: + * - a patch that adds the entry for your device, including your + * email address right above the entry (plus maybe a brief + * explanation of the reason for the entry), + * - lsusb -v output for the device + * Send your submission to Hans de Goede + * and don't forget to CC: the USB development list + */ + +/* Reported-by: Till Dörges */ +UNUSUAL_DEV(0x054c, 0x087d, 0x0000, 0x9999, + "Sony", + "PSZ-HA*", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_REPORT_OPCODES), + +/* + * Initially Reported-by: Julian Groß + * Further reports David C. Partridge + */ +UNUSUAL_DEV(0x059f, 0x105f, 0x0000, 0x9999, + "LaCie", + "2Big Quadra USB3", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_REPORT_OPCODES | US_FL_NO_SAME), + +/* Reported-by: Julian Sikorski */ +UNUSUAL_DEV(0x059f, 0x1061, 0x0000, 0x9999, + "LaCie", + "Rugged USB3-FW", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_REPORT_OPCODES | US_FL_NO_SAME), + +/* Reported-by: Zhihong Zhou */ +UNUSUAL_DEV(0x0781, 0x55e8, 0x0000, 0x9999, + "SanDisk", + "", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_UAS), + +/* Reported-by: Hongling Zeng */ +UNUSUAL_DEV(0x090c, 0x2000, 0x0000, 0x9999, + "Hiksemi", + "External HDD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_UAS), + +/* + * Apricorn USB3 dongle sometimes returns "USBSUSBSUSBS" in response to SCSI + * commands in UAS mode. Observed with the 1.28 firmware; are there others? + */ +UNUSUAL_DEV(0x0984, 0x0301, 0x0128, 0x0128, + "Apricorn", + "", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_UAS), + +/* Reported-by: Tom Hu */ +UNUSUAL_DEV(0x0b05, 0x1932, 0x0000, 0x9999, + "ASUS", + "External HDD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_UAS), + +/* Reported-by: David Webb */ +UNUSUAL_DEV(0x0bc2, 0x331a, 0x0000, 0x9999, + "Seagate", + "Expansion Desk", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_REPORT_LUNS), + +/* Reported-by: Oliver Neukum */ +UNUSUAL_DEV(0x125f, 0xa94a, 0x0160, 0x0160, + "ADATA", + "Portable HDD CH94", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_ATA_1X), + +/* Reported-by: Benjamin Tissoires */ +UNUSUAL_DEV(0x13fd, 0x3940, 0x0000, 0x0309, + "Initio Corporation", + "INIC-3069", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_ATA_1X | US_FL_IGNORE_RESIDUE), + +/* Reported-by: Tom Arild Naess */ +UNUSUAL_DEV(0x152d, 0x0539, 0x0000, 0x9999, + "JMicron", + "JMS539", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_REPORT_OPCODES), + +/* Reported-by: Claudio Bizzarri */ +UNUSUAL_DEV(0x152d, 0x0567, 0x0000, 0x9999, + "JMicron", + "JMS567", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BROKEN_FUA | US_FL_NO_REPORT_OPCODES), + +/* Reported-by: David Kozub */ +UNUSUAL_DEV(0x152d, 0x0578, 0x0000, 0x9999, + "JMicron", + "JMS567", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BROKEN_FUA), + +/* Reported by: Yaroslav Furman */ +UNUSUAL_DEV(0x152d, 0x0583, 0x0000, 0x9999, + "JMicron", + "JMS583Gen 2", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_REPORT_OPCODES), + +/* Reported-by: Sam Burkels */ +UNUSUAL_DEV(0x154b, 0xf009, 0x0000, 0x9999, + "PNY", + "PNY ELITE PSSD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES), + +/* Reported-by: Thinh Nguyen */ +UNUSUAL_DEV(0x154b, 0xf00b, 0x0000, 0x9999, + "PNY", + "Pro Elite SSD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_ATA_1X), + +/* Reported-by: Thinh Nguyen */ +UNUSUAL_DEV(0x154b, 0xf00d, 0x0000, 0x9999, + "PNY", + "Pro Elite SSD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_ATA_1X), + +/* Reported-by: Hongling Zeng */ +UNUSUAL_DEV(0x17ef, 0x3899, 0x0000, 0x9999, + "Thinkplus", + "External HDD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_UAS), + +/* Reported-by: Hans de Goede */ +UNUSUAL_DEV(0x2109, 0x0711, 0x0000, 0x9999, + "VIA", + "VL711", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_ATA_1X), + +/* Reported-by: Icenowy Zheng */ +UNUSUAL_DEV(0x2537, 0x1068, 0x0000, 0x9999, + "Norelsys", + "NS1068X", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_UAS), + +/* + * Initially Reported-by: Takeo Nakayama + * UAS Ignore Reported by Steven Ellis + */ +UNUSUAL_DEV(0x357d, 0x7788, 0x0000, 0x9999, + "JMicron", + "JMS566", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_REPORT_OPCODES | US_FL_IGNORE_UAS), + +/* Reported-by: Hans de Goede */ +UNUSUAL_DEV(0x4971, 0x1012, 0x0000, 0x9999, + "Hitachi", + "External HDD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_UAS), + +/* Reported-by: Richard Henderson */ +UNUSUAL_DEV(0x4971, 0x8017, 0x0000, 0x9999, + "SimpleTech", + "External HDD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_REPORT_OPCODES), + +/* "G-DRIVE" external HDD hangs on write without these. + * Patch submitted by Alexander Kappner + */ +UNUSUAL_DEV(0x4971, 0x8024, 0x0000, 0x9999, + "SimpleTech", + "External HDD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_ALWAYS_SYNC), diff --git a/drivers/usb/storage/unusual_usbat.h b/drivers/usb/storage/unusual_usbat.h new file mode 100644 index 000000000..f9d3e5efc --- /dev/null +++ b/drivers/usb/storage/unusual_usbat.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Unusual Devices File for SCM Microsystems (a.k.a. Shuttle) USB-ATAPI cable + */ + +#if defined(CONFIG_USB_STORAGE_USBAT) || \ + defined(CONFIG_USB_STORAGE_USBAT_MODULE) + +UNUSUAL_DEV( 0x03f0, 0x0207, 0x0001, 0x0001, + "HP", + "CD-Writer+ 8200e", + USB_SC_8070, USB_PR_USBAT, init_usbat_cd, 0), + +UNUSUAL_DEV( 0x03f0, 0x0307, 0x0001, 0x0001, + "HP", + "CD-Writer+ CD-4e", + USB_SC_8070, USB_PR_USBAT, init_usbat_cd, 0), + +UNUSUAL_DEV( 0x04e6, 0x1010, 0x0000, 0x9999, + "Shuttle/SCM", + "USBAT-02", + USB_SC_SCSI, USB_PR_USBAT, init_usbat_flash, + US_FL_SINGLE_LUN), + +UNUSUAL_DEV( 0x0781, 0x0005, 0x0005, 0x0005, + "Sandisk", + "ImageMate SDDR-05b", + USB_SC_SCSI, USB_PR_USBAT, init_usbat_flash, + US_FL_SINGLE_LUN), + +#endif /* defined(CONFIG_USB_STORAGE_USBAT) || ... */ diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c new file mode 100644 index 000000000..064c7fc8e --- /dev/null +++ b/drivers/usb/storage/usb.c @@ -0,0 +1,1258 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for USB Mass Storage compliant devices + * + * Current development and maintenance by: + * (c) 1999-2003 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * Developed with the assistance of: + * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org) + * (c) 2003-2009 Alan Stern (stern@rowland.harvard.edu) + * + * Initial work by: + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * + * usb_device_id support by Adam J. Richter (adam@yggdrasil.com): + * (c) 2000 Yggdrasil Computing, Inc. + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + */ + +#ifdef CONFIG_USB_STORAGE_DEBUG +#define DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "usb.h" +#include +#include "scsiglue.h" +#include "transport.h" +#include "protocol.h" +#include "debug.h" +#include "initializers.h" + +#include "sierra_ms.h" +#include "option_ms.h" + +#if IS_ENABLED(CONFIG_USB_UAS) +#include "uas-detect.h" +#endif + +#define DRV_NAME "usb-storage" + +/* Some informational data */ +MODULE_AUTHOR("Matthew Dharm "); +MODULE_DESCRIPTION("USB Mass Storage driver for Linux"); +MODULE_LICENSE("GPL"); + +static unsigned int delay_use = 1 * MSEC_PER_SEC; + +/** + * parse_delay_str - parse an unsigned decimal integer delay + * @str: String to parse. + * @ndecimals: Number of decimal to scale up. + * @suffix: Suffix string to parse. + * @val: Where to store the parsed value. + * + * Parse an unsigned decimal value in @str, optionally end with @suffix. + * Stores the parsed value in @val just as it is if @str ends with @suffix. + * Otherwise store the value scale up by 10^(@ndecimal). + * + * Returns 0 on success, a negative error code otherwise. + */ +static int parse_delay_str(const char *str, int ndecimals, const char *suffix, + unsigned int *val) +{ + int n, n2, l; + char buf[16]; + + l = strlen(suffix); + n = strlen(str); + if (n > 0 && str[n - 1] == '\n') + --n; + if (n >= l && !strncmp(&str[n - l], suffix, l)) { + n -= l; + n2 = 0; + } else + n2 = ndecimals; + + if (n + n2 > sizeof(buf) - 1) + return -EINVAL; + + memcpy(buf, str, n); + while (n2-- > 0) + buf[n++] = '0'; + buf[n] = 0; + + return kstrtouint(buf, 10, val); +} + +/** + * format_delay_ms - format an integer value into a delay string + * @val: The integer value to format, scaled by 10^(@ndecimals). + * @ndecimals: Number of decimal to scale down. + * @suffix: Suffix string to format. + * @str: Where to store the formatted string. + * @size: The size of buffer for @str. + * + * Format an integer value in @val scale down by 10^(@ndecimals) without @suffix + * if @val is divisible by 10^(@ndecimals). + * Otherwise format a value in @val just as it is with @suffix + * + * Returns the number of characters written into @str. + */ +static int format_delay_ms(unsigned int val, int ndecimals, const char *suffix, + char *str, int size) +{ + u64 delay_ms = val; + unsigned int rem = do_div(delay_ms, int_pow(10, ndecimals)); + int ret; + + if (rem) + ret = scnprintf(str, size, "%u%s\n", val, suffix); + else + ret = scnprintf(str, size, "%u\n", (unsigned int)delay_ms); + return ret; +} + +static int delay_use_set(const char *s, const struct kernel_param *kp) +{ + unsigned int delay_ms; + int ret; + + ret = parse_delay_str(skip_spaces(s), 3, "ms", &delay_ms); + if (ret < 0) + return ret; + + *((unsigned int *)kp->arg) = delay_ms; + return 0; +} + +static int delay_use_get(char *s, const struct kernel_param *kp) +{ + unsigned int delay_ms = *((unsigned int *)kp->arg); + + return format_delay_ms(delay_ms, 3, "ms", s, PAGE_SIZE); +} + +static const struct kernel_param_ops delay_use_ops = { + .set = delay_use_set, + .get = delay_use_get, +}; +module_param_cb(delay_use, &delay_use_ops, &delay_use, 0644); +MODULE_PARM_DESC(delay_use, "time to delay before using a new device"); + +static char quirks[128]; +module_param_string(quirks, quirks, sizeof(quirks), S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(quirks, "supplemental list of device IDs and their quirks"); + + +/* + * The entries in this table correspond, line for line, + * with the entries in usb_storage_usb_ids[], defined in usual-tables.c. + */ + +/* + *The vendor name should be kept at eight characters or less, and + * the product name should be kept at 16 characters or less. If a device + * has the US_FL_FIX_INQUIRY flag, then the vendor and product names + * normally generated by a device through the INQUIRY response will be + * taken from this list, and this is the reason for the above size + * restriction. However, if the flag is not present, then you + * are free to use as many characters as you like. + */ + +#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ + vendor_name, product_name, use_protocol, use_transport, \ + init_function, Flags) \ +{ \ + .vendorName = vendor_name, \ + .productName = product_name, \ + .useProtocol = use_protocol, \ + .useTransport = use_transport, \ + .initFunction = init_function, \ +} + +#define COMPLIANT_DEV UNUSUAL_DEV + +#define USUAL_DEV(use_protocol, use_transport) \ +{ \ + .useProtocol = use_protocol, \ + .useTransport = use_transport, \ +} + +static const struct us_unusual_dev us_unusual_dev_list[] = { +# include "unusual_devs.h" + { } /* Terminating entry */ +}; + +static const struct us_unusual_dev for_dynamic_ids = + USUAL_DEV(USB_SC_SCSI, USB_PR_BULK); + +#undef UNUSUAL_DEV +#undef COMPLIANT_DEV +#undef USUAL_DEV + +#ifdef CONFIG_LOCKDEP + +static struct lock_class_key us_interface_key[USB_MAXINTERFACES]; + +static void us_set_lock_class(struct mutex *mutex, + struct usb_interface *intf) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_host_config *config = udev->actconfig; + int i; + + for (i = 0; i < config->desc.bNumInterfaces; i++) { + if (config->interface[i] == intf) + break; + } + + BUG_ON(i == config->desc.bNumInterfaces); + + lockdep_set_class(mutex, &us_interface_key[i]); +} + +#else + +static void us_set_lock_class(struct mutex *mutex, + struct usb_interface *intf) +{ +} + +#endif + +#ifdef CONFIG_PM /* Minimal support for suspend and resume */ + +int usb_stor_suspend(struct usb_interface *iface, pm_message_t message) +{ + struct us_data *us = usb_get_intfdata(iface); + + /* Wait until no command is running */ + mutex_lock(&us->dev_mutex); + + if (us->suspend_resume_hook) + (us->suspend_resume_hook)(us, US_SUSPEND); + + /* + * When runtime PM is working, we'll set a flag to indicate + * whether we should autoresume when a SCSI request arrives. + */ + + mutex_unlock(&us->dev_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(usb_stor_suspend); + +int usb_stor_resume(struct usb_interface *iface) +{ + struct us_data *us = usb_get_intfdata(iface); + + mutex_lock(&us->dev_mutex); + + if (us->suspend_resume_hook) + (us->suspend_resume_hook)(us, US_RESUME); + + mutex_unlock(&us->dev_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(usb_stor_resume); + +int usb_stor_reset_resume(struct usb_interface *iface) +{ + struct us_data *us = usb_get_intfdata(iface); + + /* Report the reset to the SCSI core */ + usb_stor_report_bus_reset(us); + + /* + * If any of the subdrivers implemented a reinitialization scheme, + * this is where the callback would be invoked. + */ + return 0; +} +EXPORT_SYMBOL_GPL(usb_stor_reset_resume); + +#endif /* CONFIG_PM */ + +/* + * The next two routines get called just before and just after + * a USB port reset, whether from this driver or a different one. + */ + +int usb_stor_pre_reset(struct usb_interface *iface) +{ + struct us_data *us = usb_get_intfdata(iface); + + /* Make sure no command runs during the reset */ + mutex_lock(&us->dev_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(usb_stor_pre_reset); + +int usb_stor_post_reset(struct usb_interface *iface) +{ + struct us_data *us = usb_get_intfdata(iface); + + /* Report the reset to the SCSI core */ + usb_stor_report_bus_reset(us); + + /* + * If any of the subdrivers implemented a reinitialization scheme, + * this is where the callback would be invoked. + */ + + mutex_unlock(&us->dev_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(usb_stor_post_reset); + +/* + * fill_inquiry_response takes an unsigned char array (which must + * be at least 36 characters) and populates the vendor name, + * product name, and revision fields. Then the array is copied + * into the SCSI command's response buffer (oddly enough + * called request_buffer). data_len contains the length of the + * data array, which again must be at least 36. + */ + +void fill_inquiry_response(struct us_data *us, unsigned char *data, + unsigned int data_len) +{ + if (data_len < 36) /* You lose. */ + return; + + memset(data+8, ' ', 28); + if (data[0]&0x20) { /* + * USB device currently not connected. Return + * peripheral qualifier 001b ("...however, the + * physical device is not currently connected + * to this logical unit") and leave vendor and + * product identification empty. ("If the target + * does store some of the INQUIRY data on the + * device, it may return zeros or ASCII spaces + * (20h) in those fields until the data is + * available from the device."). + */ + } else { + u16 bcdDevice = le16_to_cpu(us->pusb_dev->descriptor.bcdDevice); + int n; + + n = strlen(us->unusual_dev->vendorName); + memcpy(data+8, us->unusual_dev->vendorName, min(8, n)); + n = strlen(us->unusual_dev->productName); + memcpy(data+16, us->unusual_dev->productName, min(16, n)); + + data[32] = 0x30 + ((bcdDevice>>12) & 0x0F); + data[33] = 0x30 + ((bcdDevice>>8) & 0x0F); + data[34] = 0x30 + ((bcdDevice>>4) & 0x0F); + data[35] = 0x30 + ((bcdDevice) & 0x0F); + } + + usb_stor_set_xfer_buf(data, data_len, us->srb); +} +EXPORT_SYMBOL_GPL(fill_inquiry_response); + +static int usb_stor_control_thread(void * __us) +{ + struct us_data *us = (struct us_data *)__us; + struct Scsi_Host *host = us_to_host(us); + struct scsi_cmnd *srb; + + for (;;) { + usb_stor_dbg(us, "*** thread sleeping\n"); + if (wait_for_completion_interruptible(&us->cmnd_ready)) + break; + + usb_stor_dbg(us, "*** thread awakened\n"); + + /* lock the device pointers */ + mutex_lock(&(us->dev_mutex)); + + /* lock access to the state */ + scsi_lock(host); + + /* When we are called with no command pending, we're done */ + srb = us->srb; + if (srb == NULL) { + scsi_unlock(host); + mutex_unlock(&us->dev_mutex); + usb_stor_dbg(us, "-- exiting\n"); + break; + } + + /* has the command timed out *already* ? */ + if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { + srb->result = DID_ABORT << 16; + goto SkipForAbort; + } + + scsi_unlock(host); + + /* + * reject the command if the direction indicator + * is UNKNOWN + */ + if (srb->sc_data_direction == DMA_BIDIRECTIONAL) { + usb_stor_dbg(us, "UNKNOWN data direction\n"); + srb->result = DID_ERROR << 16; + } + + /* + * reject if target != 0 or if LUN is higher than + * the maximum known LUN + */ + else if (srb->device->id && + !(us->fflags & US_FL_SCM_MULT_TARG)) { + usb_stor_dbg(us, "Bad target number (%d:%llu)\n", + srb->device->id, + srb->device->lun); + srb->result = DID_BAD_TARGET << 16; + } + + else if (srb->device->lun > us->max_lun) { + usb_stor_dbg(us, "Bad LUN (%d:%llu)\n", + srb->device->id, + srb->device->lun); + srb->result = DID_BAD_TARGET << 16; + } + + /* + * Handle those devices which need us to fake + * their inquiry data + */ + else if ((srb->cmnd[0] == INQUIRY) && + (us->fflags & US_FL_FIX_INQUIRY)) { + unsigned char data_ptr[36] = { + 0x00, 0x80, 0x02, 0x02, + 0x1F, 0x00, 0x00, 0x00}; + + usb_stor_dbg(us, "Faking INQUIRY command\n"); + fill_inquiry_response(us, data_ptr, 36); + srb->result = SAM_STAT_GOOD; + } + + /* we've got a command, let's do it! */ + else { + US_DEBUG(usb_stor_show_command(us, srb)); + us->proto_handler(srb, us); + usb_mark_last_busy(us->pusb_dev); + } + + /* lock access to the state */ + scsi_lock(host); + + /* was the command aborted? */ + if (srb->result == DID_ABORT << 16) { +SkipForAbort: + usb_stor_dbg(us, "scsi command aborted\n"); + srb = NULL; /* Don't call scsi_done() */ + } + + /* + * If an abort request was received we need to signal that + * the abort has finished. The proper test for this is + * the TIMED_OUT flag, not srb->result == DID_ABORT, because + * the timeout might have occurred after the command had + * already completed with a different result code. + */ + if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { + complete(&(us->notify)); + + /* Allow USB transfers to resume */ + clear_bit(US_FLIDX_ABORTING, &us->dflags); + clear_bit(US_FLIDX_TIMED_OUT, &us->dflags); + } + + /* finished working on this command */ + us->srb = NULL; + scsi_unlock(host); + + /* unlock the device pointers */ + mutex_unlock(&us->dev_mutex); + + /* now that the locks are released, notify the SCSI core */ + if (srb) { + usb_stor_dbg(us, "scsi cmd done, result=0x%x\n", + srb->result); + scsi_done_direct(srb); + } + } /* for (;;) */ + + /* Wait until we are told to stop */ + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (kthread_should_stop()) + break; + schedule(); + } + __set_current_state(TASK_RUNNING); + return 0; +} + +/*********************************************************************** + * Device probing and disconnecting + ***********************************************************************/ + +/* Associate our private data with the USB device */ +static int associate_dev(struct us_data *us, struct usb_interface *intf) +{ + /* Fill in the device-related fields */ + us->pusb_dev = interface_to_usbdev(intf); + us->pusb_intf = intf; + us->ifnum = intf->cur_altsetting->desc.bInterfaceNumber; + usb_stor_dbg(us, "Vendor: 0x%04x, Product: 0x%04x, Revision: 0x%04x\n", + le16_to_cpu(us->pusb_dev->descriptor.idVendor), + le16_to_cpu(us->pusb_dev->descriptor.idProduct), + le16_to_cpu(us->pusb_dev->descriptor.bcdDevice)); + usb_stor_dbg(us, "Interface Subclass: 0x%02x, Protocol: 0x%02x\n", + intf->cur_altsetting->desc.bInterfaceSubClass, + intf->cur_altsetting->desc.bInterfaceProtocol); + + /* Store our private data in the interface */ + usb_set_intfdata(intf, us); + + /* Allocate the control/setup and DMA-mapped buffers */ + us->cr = kmalloc_obj(*us->cr); + if (!us->cr) + return -ENOMEM; + + us->iobuf = usb_alloc_coherent(us->pusb_dev, US_IOBUF_SIZE, + GFP_KERNEL, &us->iobuf_dma); + if (!us->iobuf) { + usb_stor_dbg(us, "I/O buffer allocation failed\n"); + return -ENOMEM; + } + return 0; +} + +/* Works only for digits and letters, but small and fast */ +#define TOLOWER(x) ((x) | 0x20) + +/* Adjust device flags based on the "quirks=" module parameter */ +void usb_stor_adjust_quirks(struct usb_device *udev, u64 *fflags) +{ + char *p; + u16 vid = le16_to_cpu(udev->descriptor.idVendor); + u16 pid = le16_to_cpu(udev->descriptor.idProduct); + u64 f = 0; + u64 mask = (US_FL_SANE_SENSE | US_FL_BAD_SENSE | + US_FL_FIX_CAPACITY | US_FL_IGNORE_UAS | + US_FL_CAPACITY_HEURISTICS | US_FL_IGNORE_DEVICE | + US_FL_NOT_LOCKABLE | US_FL_MAX_SECTORS_64 | + US_FL_CAPACITY_OK | US_FL_IGNORE_RESIDUE | + US_FL_SINGLE_LUN | US_FL_NO_WP_DETECT | + US_FL_NO_READ_DISC_INFO | US_FL_NO_READ_CAPACITY_16 | + US_FL_INITIAL_READ10 | US_FL_WRITE_CACHE | + US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES | + US_FL_MAX_SECTORS_240 | US_FL_NO_REPORT_LUNS | + US_FL_ALWAYS_SYNC | US_FL_NO_SAME); + + p = quirks; + while (*p) { + /* Each entry consists of VID:PID:flags */ + if (vid == simple_strtoul(p, &p, 16) && + *p == ':' && + pid == simple_strtoul(p+1, &p, 16) && + *p == ':') + break; + + /* Move forward to the next entry */ + while (*p) { + if (*p++ == ',') + break; + } + } + if (!*p) /* No match */ + return; + + /* Collect the flags */ + while (*++p && *p != ',') { + switch (TOLOWER(*p)) { + case 'a': + f |= US_FL_SANE_SENSE; + break; + case 'b': + f |= US_FL_BAD_SENSE; + break; + case 'c': + f |= US_FL_FIX_CAPACITY; + break; + case 'd': + f |= US_FL_NO_READ_DISC_INFO; + break; + case 'e': + f |= US_FL_NO_READ_CAPACITY_16; + break; + case 'f': + f |= US_FL_NO_REPORT_OPCODES; + break; + case 'g': + f |= US_FL_MAX_SECTORS_240; + break; + case 'h': + f |= US_FL_CAPACITY_HEURISTICS; + break; + case 'i': + f |= US_FL_IGNORE_DEVICE; + break; + case 'j': + f |= US_FL_NO_REPORT_LUNS; + break; + case 'k': + f |= US_FL_NO_SAME; + break; + case 'l': + f |= US_FL_NOT_LOCKABLE; + break; + case 'm': + f |= US_FL_MAX_SECTORS_64; + break; + case 'n': + f |= US_FL_INITIAL_READ10; + break; + case 'o': + f |= US_FL_CAPACITY_OK; + break; + case 'p': + f |= US_FL_WRITE_CACHE; + break; + case 'r': + f |= US_FL_IGNORE_RESIDUE; + break; + case 's': + f |= US_FL_SINGLE_LUN; + break; + case 't': + f |= US_FL_NO_ATA_1X; + break; + case 'u': + f |= US_FL_IGNORE_UAS; + break; + case 'w': + f |= US_FL_NO_WP_DETECT; + break; + case 'y': + f |= US_FL_ALWAYS_SYNC; + break; + /* Ignore unrecognized flag characters */ + } + } + *fflags = (*fflags & ~mask) | f; +} +EXPORT_SYMBOL_GPL(usb_stor_adjust_quirks); + +/* Get the unusual_devs entries and the string descriptors */ +static int get_device_info(struct us_data *us, const struct usb_device_id *id, + const struct us_unusual_dev *unusual_dev) +{ + struct usb_device *dev = us->pusb_dev; + struct usb_interface_descriptor *idesc = + &us->pusb_intf->cur_altsetting->desc; + struct device *pdev = &us->pusb_intf->dev; + + /* Store the entries */ + us->unusual_dev = unusual_dev; + us->subclass = (unusual_dev->useProtocol == USB_SC_DEVICE) ? + idesc->bInterfaceSubClass : + unusual_dev->useProtocol; + us->protocol = (unusual_dev->useTransport == USB_PR_DEVICE) ? + idesc->bInterfaceProtocol : + unusual_dev->useTransport; + us->fflags = id->driver_info; + usb_stor_adjust_quirks(us->pusb_dev, &us->fflags); + + if (us->fflags & US_FL_IGNORE_DEVICE) { + dev_info(pdev, "device ignored\n"); + return -ENODEV; + } + + /* + * This flag is only needed when we're in high-speed, so let's + * disable it if we're in full-speed + */ + if (dev->speed != USB_SPEED_HIGH) + us->fflags &= ~US_FL_GO_SLOW; + + if (us->fflags) + dev_info(pdev, "Quirks match for vid %04x pid %04x: %llx\n", + le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct), + us->fflags); + + /* + * Log a message if a non-generic unusual_dev entry contains an + * unnecessary subclass or protocol override. This may stimulate + * reports from users that will help us remove unneeded entries + * from the unusual_devs.h table. + */ + if (id->idVendor || id->idProduct) { + static const char *msgs[3] = { + "an unneeded SubClass entry", + "an unneeded Protocol entry", + "unneeded SubClass and Protocol entries"}; + struct usb_device_descriptor *ddesc = &dev->descriptor; + int msg = -1; + + if (unusual_dev->useProtocol != USB_SC_DEVICE && + us->subclass == idesc->bInterfaceSubClass) + msg += 1; + if (unusual_dev->useTransport != USB_PR_DEVICE && + us->protocol == idesc->bInterfaceProtocol) + msg += 2; + if (msg >= 0 && !(us->fflags & US_FL_NEED_OVERRIDE)) + dev_notice(pdev, "This device " + "(%04x,%04x,%04x S %02x P %02x)" + " has %s in unusual_devs.h (kernel" + " %s)\n" + " Please send a copy of this message to " + " and " + "\n", + le16_to_cpu(ddesc->idVendor), + le16_to_cpu(ddesc->idProduct), + le16_to_cpu(ddesc->bcdDevice), + idesc->bInterfaceSubClass, + idesc->bInterfaceProtocol, + msgs[msg], + utsname()->release); + } + + return 0; +} + +/* Get the transport settings */ +static void get_transport(struct us_data *us) +{ + switch (us->protocol) { + case USB_PR_CB: + us->transport_name = "Control/Bulk"; + us->transport = usb_stor_CB_transport; + us->transport_reset = usb_stor_CB_reset; + us->max_lun = 7; + break; + + case USB_PR_CBI: + us->transport_name = "Control/Bulk/Interrupt"; + us->transport = usb_stor_CB_transport; + us->transport_reset = usb_stor_CB_reset; + us->max_lun = 7; + break; + + case USB_PR_BULK: + us->transport_name = "Bulk"; + us->transport = usb_stor_Bulk_transport; + us->transport_reset = usb_stor_Bulk_reset; + break; + } +} + +/* Get the protocol settings */ +static void get_protocol(struct us_data *us) +{ + switch (us->subclass) { + case USB_SC_RBC: + us->protocol_name = "Reduced Block Commands (RBC)"; + us->proto_handler = usb_stor_transparent_scsi_command; + break; + + case USB_SC_8020: + us->protocol_name = "8020i"; + us->proto_handler = usb_stor_pad12_command; + us->max_lun = 0; + break; + + case USB_SC_QIC: + us->protocol_name = "QIC-157"; + us->proto_handler = usb_stor_pad12_command; + us->max_lun = 0; + break; + + case USB_SC_8070: + us->protocol_name = "8070i"; + us->proto_handler = usb_stor_pad12_command; + us->max_lun = 0; + break; + + case USB_SC_SCSI: + us->protocol_name = "Transparent SCSI"; + us->proto_handler = usb_stor_transparent_scsi_command; + break; + + case USB_SC_UFI: + us->protocol_name = "Uniform Floppy Interface (UFI)"; + us->proto_handler = usb_stor_ufi_command; + break; + } +} + +/* Get the pipe settings */ +static int get_pipes(struct us_data *us) +{ + struct usb_host_interface *alt = us->pusb_intf->cur_altsetting; + struct usb_endpoint_descriptor *ep_in; + struct usb_endpoint_descriptor *ep_out; + struct usb_endpoint_descriptor *ep_int; + int res; + + /* + * Find the first endpoint of each type we need. + * We are expecting a minimum of 2 endpoints - in and out (bulk). + * An optional interrupt-in is OK (necessary for CBI protocol). + * We will ignore any others. + */ + res = usb_find_common_endpoints(alt, &ep_in, &ep_out, NULL, NULL); + if (res) { + usb_stor_dbg(us, "bulk endpoints not found\n"); + return res; + } + + res = usb_find_int_in_endpoint(alt, &ep_int); + if (res && us->protocol == USB_PR_CBI) { + usb_stor_dbg(us, "interrupt endpoint not found\n"); + return res; + } + + /* Calculate and store the pipe values */ + us->send_ctrl_pipe = usb_sndctrlpipe(us->pusb_dev, 0); + us->recv_ctrl_pipe = usb_rcvctrlpipe(us->pusb_dev, 0); + us->send_bulk_pipe = usb_sndbulkpipe(us->pusb_dev, + usb_endpoint_num(ep_out)); + us->recv_bulk_pipe = usb_rcvbulkpipe(us->pusb_dev, + usb_endpoint_num(ep_in)); + if (ep_int) { + us->recv_intr_pipe = usb_rcvintpipe(us->pusb_dev, + usb_endpoint_num(ep_int)); + us->ep_bInterval = ep_int->bInterval; + } + return 0; +} + +/* Initialize all the dynamic resources we need */ +static int usb_stor_acquire_resources(struct us_data *us) +{ + int p; + struct task_struct *th; + + us->current_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!us->current_urb) + return -ENOMEM; + + /* + * Just before we start our control thread, initialize + * the device if it needs initialization + */ + if (us->unusual_dev->initFunction) { + p = us->unusual_dev->initFunction(us); + if (p) + return p; + } + + /* Start up our control thread */ + th = kthread_run(usb_stor_control_thread, us, "usb-storage"); + if (IS_ERR(th)) { + dev_warn(&us->pusb_intf->dev, + "Unable to start control thread\n"); + return PTR_ERR(th); + } + us->ctl_thread = th; + + return 0; +} + +/* Release all our dynamic resources */ +static void usb_stor_release_resources(struct us_data *us) +{ + /* + * Tell the control thread to exit. The SCSI host must + * already have been removed and the DISCONNECTING flag set + * so that we won't accept any more commands. + */ + usb_stor_dbg(us, "-- sending exit command to thread\n"); + complete(&us->cmnd_ready); + if (us->ctl_thread) + kthread_stop(us->ctl_thread); + + /* Call the destructor routine, if it exists */ + if (us->extra_destructor) { + usb_stor_dbg(us, "-- calling extra_destructor()\n"); + us->extra_destructor(us->extra); + } + + /* Free the extra data and the URB */ + kfree(us->extra); + usb_free_urb(us->current_urb); +} + +/* Dissociate from the USB device */ +static void dissociate_dev(struct us_data *us) +{ + /* Free the buffers */ + kfree(us->cr); + usb_free_coherent(us->pusb_dev, US_IOBUF_SIZE, us->iobuf, us->iobuf_dma); + + /* Remove our private data from the interface */ + usb_set_intfdata(us->pusb_intf, NULL); +} + +/* + * First stage of disconnect processing: stop SCSI scanning, + * remove the host, and stop accepting new commands + */ +static void quiesce_and_remove_host(struct us_data *us) +{ + struct Scsi_Host *host = us_to_host(us); + + /* If the device is really gone, cut short reset delays */ + if (us->pusb_dev->state == USB_STATE_NOTATTACHED) { + set_bit(US_FLIDX_DISCONNECTING, &us->dflags); + wake_up(&us->delay_wait); + } + + /* + * Prevent SCSI scanning (if it hasn't started yet) + * or wait for the SCSI-scanning routine to stop. + */ + cancel_delayed_work_sync(&us->scan_dwork); + + /* Balance autopm calls if scanning was cancelled */ + if (test_bit(US_FLIDX_SCAN_PENDING, &us->dflags)) + usb_autopm_put_interface_no_suspend(us->pusb_intf); + + /* + * Removing the host will perform an orderly shutdown: caches + * synchronized, disks spun down, etc. + */ + scsi_remove_host(host); + + /* + * Prevent any new commands from being accepted and cut short + * reset delays. + */ + scsi_lock(host); + set_bit(US_FLIDX_DISCONNECTING, &us->dflags); + scsi_unlock(host); + wake_up(&us->delay_wait); +} + +/* Second stage of disconnect processing: deallocate all resources */ +static void release_everything(struct us_data *us) +{ + usb_stor_release_resources(us); + dissociate_dev(us); + + /* + * Drop our reference to the host; the SCSI core will free it + * (and "us" along with it) when the refcount becomes 0. + */ + scsi_host_put(us_to_host(us)); +} + +/* Delayed-work routine to carry out SCSI-device scanning */ +static void usb_stor_scan_dwork(struct work_struct *work) +{ + struct us_data *us = container_of(work, struct us_data, + scan_dwork.work); + struct device *dev = &us->pusb_intf->dev; + + dev_dbg(dev, "starting scan\n"); + + /* For bulk-only devices, determine the max LUN value */ + if (us->protocol == USB_PR_BULK && + !(us->fflags & US_FL_SINGLE_LUN) && + !(us->fflags & US_FL_SCM_MULT_TARG)) { + mutex_lock(&us->dev_mutex); + us->max_lun = usb_stor_Bulk_max_lun(us); + /* + * Allow proper scanning of devices that present more than 8 LUNs + * While not affecting other devices that may need the previous + * behavior + */ + if (us->max_lun >= 8) + us_to_host(us)->max_lun = us->max_lun+1; + mutex_unlock(&us->dev_mutex); + } + scsi_scan_host(us_to_host(us)); + dev_dbg(dev, "scan complete\n"); + + /* Should we unbind if no devices were detected? */ + + usb_autopm_put_interface(us->pusb_intf); + clear_bit(US_FLIDX_SCAN_PENDING, &us->dflags); +} + +static unsigned int usb_stor_sg_tablesize(struct usb_interface *intf) +{ + struct usb_device *usb_dev = interface_to_usbdev(intf); + + if (usb_dev->bus->sg_tablesize) { + return usb_dev->bus->sg_tablesize; + } + return SG_ALL; +} + +/* First part of general USB mass-storage probing */ +int usb_stor_probe1(struct us_data **pus, + struct usb_interface *intf, + const struct usb_device_id *id, + const struct us_unusual_dev *unusual_dev, + const struct scsi_host_template *sht) +{ + struct Scsi_Host *host; + struct us_data *us; + int result; + + dev_info(&intf->dev, "USB Mass Storage device detected\n"); + + /* + * Ask the SCSI layer to allocate a host structure, with extra + * space at the end for our private us_data structure. + */ + host = scsi_host_alloc(sht, sizeof(*us)); + if (!host) { + dev_warn(&intf->dev, "Unable to allocate the scsi host\n"); + return -ENOMEM; + } + + /* + * Allow 16-byte CDBs and thus > 2TB + */ + host->max_cmd_len = 16; + host->sg_tablesize = usb_stor_sg_tablesize(intf); + *pus = us = host_to_us(host); + mutex_init(&(us->dev_mutex)); + us_set_lock_class(&us->dev_mutex, intf); + init_completion(&us->cmnd_ready); + init_completion(&(us->notify)); + init_waitqueue_head(&us->delay_wait); + INIT_DELAYED_WORK(&us->scan_dwork, usb_stor_scan_dwork); + + /* Associate the us_data structure with the USB device */ + result = associate_dev(us, intf); + if (result) + goto BadDevice; + + /* + * Some USB host controllers can't do DMA: They have to use PIO, or they + * have to use a small dedicated local memory area, or they have other + * restrictions on addressable memory. + * + * We can't support these controllers on highmem systems as we don't + * kmap or bounce buffer. + */ + if (IS_ENABLED(CONFIG_HIGHMEM) && + (!hcd_uses_dma(bus_to_hcd(us->pusb_dev->bus)) || + bus_to_hcd(us->pusb_dev->bus)->localmem_pool)) { + dev_warn(&intf->dev, "USB Mass Storage not supported on this host controller\n"); + result = -EINVAL; + goto release; + } + + /* Get the unusual_devs entries and the descriptors */ + result = get_device_info(us, id, unusual_dev); + if (result) + goto BadDevice; + + /* Get standard transport and protocol settings */ + get_transport(us); + get_protocol(us); + + /* + * Give the caller a chance to fill in specialized transport + * or protocol settings. + */ + return 0; + +BadDevice: + usb_stor_dbg(us, "storage_probe() failed\n"); +release: + release_everything(us); + return result; +} +EXPORT_SYMBOL_GPL(usb_stor_probe1); + +/* Second part of general USB mass-storage probing */ +int usb_stor_probe2(struct us_data *us) +{ + int result; + struct device *dev = &us->pusb_intf->dev; + + /* Make sure the transport and protocol have both been set */ + if (!us->transport || !us->proto_handler) { + result = -ENXIO; + goto BadDevice; + } + usb_stor_dbg(us, "Transport: %s\n", us->transport_name); + usb_stor_dbg(us, "Protocol: %s\n", us->protocol_name); + + if (us->fflags & US_FL_SCM_MULT_TARG) { + /* + * SCM eUSCSI bridge devices can have different numbers + * of LUNs on different targets; allow all to be probed. + */ + us->max_lun = 7; + /* The eUSCSI itself has ID 7, so avoid scanning that */ + us_to_host(us)->this_id = 7; + /* max_id is 8 initially, so no need to set it here */ + } else { + /* In the normal case there is only a single target */ + us_to_host(us)->max_id = 1; + /* + * Like Windows, we won't store the LUN bits in CDB[1] for + * SCSI-2 devices using the Bulk-Only transport (even though + * this violates the SCSI spec). + */ + if (us->transport == usb_stor_Bulk_transport) + us_to_host(us)->no_scsi2_lun_in_cdb = 1; + } + + /* fix for single-lun devices */ + if (us->fflags & US_FL_SINGLE_LUN) + us->max_lun = 0; + + /* Find the endpoints and calculate pipe values */ + result = get_pipes(us); + if (result) + goto BadDevice; + + /* + * If the device returns invalid data for the first READ(10) + * command, indicate the command should be retried. + */ + if (us->fflags & US_FL_INITIAL_READ10) + set_bit(US_FLIDX_REDO_READ10, &us->dflags); + + /* Acquire all the other resources and add the host */ + result = usb_stor_acquire_resources(us); + if (result) + goto BadDevice; + usb_autopm_get_interface_no_resume(us->pusb_intf); + snprintf(us->scsi_name, sizeof(us->scsi_name), "usb-storage %s", + dev_name(&us->pusb_intf->dev)); + result = scsi_add_host(us_to_host(us), dev); + if (result) { + dev_warn(dev, + "Unable to add the scsi host\n"); + goto HostAddErr; + } + + /* Submit the delayed_work for SCSI-device scanning */ + set_bit(US_FLIDX_SCAN_PENDING, &us->dflags); + + if (delay_use > 0) + dev_dbg(dev, "waiting for device to settle before scanning\n"); + queue_delayed_work(system_freezable_wq, &us->scan_dwork, + msecs_to_jiffies(delay_use)); + return 0; + + /* We come here if there are any problems */ +HostAddErr: + usb_autopm_put_interface_no_suspend(us->pusb_intf); +BadDevice: + usb_stor_dbg(us, "storage_probe() failed\n"); + release_everything(us); + return result; +} +EXPORT_SYMBOL_GPL(usb_stor_probe2); + +/* Handle a USB mass-storage disconnect */ +void usb_stor_disconnect(struct usb_interface *intf) +{ + struct us_data *us = usb_get_intfdata(intf); + + quiesce_and_remove_host(us); + release_everything(us); +} +EXPORT_SYMBOL_GPL(usb_stor_disconnect); + +static struct scsi_host_template usb_stor_host_template; + +/* The main probe routine for standard devices */ +static int storage_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + const struct us_unusual_dev *unusual_dev; + struct us_data *us; + int result; + int size; + + /* If uas is enabled and this device can do uas then ignore it. */ +#if IS_ENABLED(CONFIG_USB_UAS) + if (uas_use_uas_driver(intf, id, NULL)) + return -ENXIO; +#endif + + /* + * If the device isn't standard (is handled by a subdriver + * module) then don't accept it. + */ + if (usb_usual_ignore_device(intf)) + return -ENXIO; + + /* + * Call the general probe procedures. + * + * The unusual_dev_list array is parallel to the usb_storage_usb_ids + * table, so we use the index of the id entry to find the + * corresponding unusual_devs entry. + */ + + size = ARRAY_SIZE(us_unusual_dev_list); + if (id >= usb_storage_usb_ids && id < usb_storage_usb_ids + size) { + unusual_dev = (id - usb_storage_usb_ids) + us_unusual_dev_list; + } else { + unusual_dev = &for_dynamic_ids; + + dev_dbg(&intf->dev, "Use Bulk-Only transport with the Transparent SCSI protocol for dynamic id: 0x%04x 0x%04x\n", + id->idVendor, id->idProduct); + } + + result = usb_stor_probe1(&us, intf, id, unusual_dev, + &usb_stor_host_template); + if (result) + return result; + + /* No special transport or protocol settings in the main module */ + + result = usb_stor_probe2(us); + return result; +} + +static struct usb_driver usb_storage_driver = { + .name = DRV_NAME, + .probe = storage_probe, + .disconnect = usb_stor_disconnect, + .suspend = usb_stor_suspend, + .resume = usb_stor_resume, + .reset_resume = usb_stor_reset_resume, + .pre_reset = usb_stor_pre_reset, + .post_reset = usb_stor_post_reset, + .id_table = usb_storage_usb_ids, + .supports_autosuspend = 1, + .soft_unbind = 1, +}; + +module_usb_stor_driver(usb_storage_driver, usb_stor_host_template, DRV_NAME); diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h new file mode 100644 index 000000000..97c6196d6 --- /dev/null +++ b/drivers/usb/storage/usb.h @@ -0,0 +1,210 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Driver for USB Mass Storage compliant devices + * Main Header File + * + * Current development and maintenance by: + * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * Initial work by: + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + */ + +#ifndef _USB_H_ +#define _USB_H_ + +#include +#include +#include +#include +#include +#include +#include + +struct us_data; +struct scsi_cmnd; + +/* + * Unusual device list definitions + */ + +struct us_unusual_dev { + const char* vendorName; + const char* productName; + __u8 useProtocol; + __u8 useTransport; + int (*initFunction)(struct us_data *); +}; + + +/* Dynamic bitflag definitions (us->dflags): used in set_bit() etc. */ +#define US_FLIDX_URB_ACTIVE 0 /* current_urb is in use */ +#define US_FLIDX_SG_ACTIVE 1 /* current_sg is in use */ +#define US_FLIDX_ABORTING 2 /* abort is in progress */ +#define US_FLIDX_DISCONNECTING 3 /* disconnect in progress */ +#define US_FLIDX_RESETTING 4 /* device reset in progress */ +#define US_FLIDX_TIMED_OUT 5 /* SCSI midlayer timed out */ +#define US_FLIDX_SCAN_PENDING 6 /* scanning not yet done */ +#define US_FLIDX_REDO_READ10 7 /* redo READ(10) command */ +#define US_FLIDX_READ10_WORKED 8 /* previous READ(10) succeeded */ + +#define USB_STOR_STRING_LEN 32 + +/* + * We provide a DMA-mapped I/O buffer for use with small USB transfers. + * It turns out that CB[I] needs a 12-byte buffer and Bulk-only needs a + * 31-byte buffer. But Freecom needs a 64-byte buffer, so that's the + * size we'll allocate. + */ + +#define US_IOBUF_SIZE 64 /* Size of the DMA-mapped I/O buffer */ +#define US_SENSE_SIZE 18 /* Size of the autosense data buffer */ + +typedef int (*trans_cmnd)(struct scsi_cmnd *, struct us_data*); +typedef int (*trans_reset)(struct us_data*); +typedef void (*proto_cmnd)(struct scsi_cmnd*, struct us_data*); +typedef void (*extra_data_destructor)(void *); /* extra data destructor */ +typedef void (*pm_hook)(struct us_data *, int); /* power management hook */ + +#define US_SUSPEND 0 +#define US_RESUME 1 + +/* we allocate one of these for every device that we remember */ +struct us_data { + /* + * The device we're working with + * It's important to note: + * (o) you must hold dev_mutex to change pusb_dev + */ + struct mutex dev_mutex; /* protect pusb_dev */ + struct usb_device *pusb_dev; /* this usb_device */ + struct usb_interface *pusb_intf; /* this interface */ + const struct us_unusual_dev *unusual_dev; + /* device-filter entry */ + u64 fflags; /* fixed flags from filter */ + unsigned long dflags; /* dynamic atomic bitflags */ + unsigned int send_bulk_pipe; /* cached pipe values */ + unsigned int recv_bulk_pipe; + unsigned int send_ctrl_pipe; + unsigned int recv_ctrl_pipe; + unsigned int recv_intr_pipe; + + /* information about the device */ + char *transport_name; + char *protocol_name; + __le32 bcs_signature; + u8 subclass; + u8 protocol; + u8 max_lun; + + u8 ifnum; /* interface number */ + u8 ep_bInterval; /* interrupt interval */ + + /* function pointers for this device */ + trans_cmnd transport; /* transport function */ + trans_reset transport_reset; /* transport device reset */ + proto_cmnd proto_handler; /* protocol handler */ + + /* SCSI interfaces */ + struct scsi_cmnd *srb; /* current srb */ + unsigned int tag; /* current dCBWTag */ + char scsi_name[32]; /* scsi_host name */ + + /* control and bulk communications data */ + struct urb *current_urb; /* USB requests */ + struct usb_ctrlrequest *cr; /* control requests */ + struct usb_sg_request current_sg; /* scatter-gather req. */ + unsigned char *iobuf; /* I/O buffer */ + dma_addr_t iobuf_dma; /* buffer DMA addresses */ + struct task_struct *ctl_thread; /* the control thread */ + + /* mutual exclusion and synchronization structures */ + struct completion cmnd_ready; /* to sleep thread on */ + struct completion notify; /* thread begin/end */ + wait_queue_head_t delay_wait; /* wait during reset */ + struct delayed_work scan_dwork; /* for async scanning */ + + /* subdriver information */ + void *extra; /* Any extra data */ + extra_data_destructor extra_destructor;/* extra data destructor */ +#ifdef CONFIG_PM + pm_hook suspend_resume_hook; +#endif + + /* hacks for READ CAPACITY bug handling */ + int use_last_sector_hacks; + int last_sector_retries; +}; + +/* Convert between us_data and the corresponding Scsi_Host */ +static inline struct Scsi_Host *us_to_host(struct us_data *us) { + return container_of((void *) us, struct Scsi_Host, hostdata); +} +static inline struct us_data *host_to_us(struct Scsi_Host *host) { + return (struct us_data *) host->hostdata; +} + +/* Function to fill an inquiry response. See usb.c for details */ +extern void fill_inquiry_response(struct us_data *us, + unsigned char *data, unsigned int data_len); + +/* + * The scsi_lock() and scsi_unlock() macros protect the sm_state and the + * single queue element srb for write access + */ +#define scsi_unlock(host) spin_unlock_irq(host->host_lock) +#define scsi_lock(host) spin_lock_irq(host->host_lock) + +/* General routines provided by the usb-storage standard core */ +#ifdef CONFIG_PM +extern int usb_stor_suspend(struct usb_interface *iface, pm_message_t message); +extern int usb_stor_resume(struct usb_interface *iface); +extern int usb_stor_reset_resume(struct usb_interface *iface); +#else +#define usb_stor_suspend NULL +#define usb_stor_resume NULL +#define usb_stor_reset_resume NULL +#endif + +extern int usb_stor_pre_reset(struct usb_interface *iface); +extern int usb_stor_post_reset(struct usb_interface *iface); + +extern int usb_stor_probe1(struct us_data **pus, + struct usb_interface *intf, + const struct usb_device_id *id, + const struct us_unusual_dev *unusual_dev, + const struct scsi_host_template *sht); +extern int usb_stor_probe2(struct us_data *us); +extern void usb_stor_disconnect(struct usb_interface *intf); + +extern void usb_stor_adjust_quirks(struct usb_device *dev, + u64 *fflags); + +#define module_usb_stor_driver(__driver, __sht, __name) \ +static int __init __driver##_init(void) \ +{ \ + usb_stor_host_template_init(&(__sht), __name, THIS_MODULE); \ + return usb_register(&(__driver)); \ +} \ +module_init(__driver##_init); \ +static void __exit __driver##_exit(void) \ +{ \ + usb_deregister(&(__driver)); \ +} \ +module_exit(__driver##_exit) + +#endif diff --git a/drivers/usb/storage/usual-tables.c b/drivers/usb/storage/usual-tables.c new file mode 100644 index 000000000..a26029e43 --- /dev/null +++ b/drivers/usb/storage/usual-tables.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for USB Mass Storage devices + * Usual Tables File for usb-storage and libusual + * + * Copyright (C) 2009 Alan Stern (stern@rowland.harvard.edu) + */ + +#include +#include +#include +#include + + +/* + * The table of devices + */ +#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ + vendorName, productName, useProtocol, useTransport, \ + initFunction, flags) \ +{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ + .driver_info = (kernel_ulong_t)(flags) } + +#define COMPLIANT_DEV UNUSUAL_DEV + +#define USUAL_DEV(useProto, useTrans) \ +{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, useProto, useTrans) } + +const struct usb_device_id usb_storage_usb_ids[] = { +# include "unusual_devs.h" + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, usb_storage_usb_ids); + +#undef UNUSUAL_DEV +#undef COMPLIANT_DEV +#undef USUAL_DEV + +/* + * The table of devices to ignore + */ +struct ignore_entry { + u16 vid, pid, bcdmin, bcdmax; +}; + +#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ + vendorName, productName, useProtocol, useTransport, \ + initFunction, flags) \ +{ \ + .vid = id_vendor, \ + .pid = id_product, \ + .bcdmin = bcdDeviceMin, \ + .bcdmax = bcdDeviceMax, \ +} + +static const struct ignore_entry ignore_ids[] = { +# include "unusual_alauda.h" +# include "unusual_cypress.h" +# include "unusual_datafab.h" +# include "unusual_ene_ub6250.h" +# include "unusual_freecom.h" +# include "unusual_isd200.h" +# include "unusual_jumpshot.h" +# include "unusual_karma.h" +# include "unusual_onetouch.h" +# include "unusual_realtek.h" +# include "unusual_sddr09.h" +# include "unusual_sddr55.h" +# include "unusual_usbat.h" + { } /* Terminating entry */ +}; + +#undef UNUSUAL_DEV + +/* Return an error if a device is in the ignore_ids list */ +int usb_usual_ignore_device(struct usb_interface *intf) +{ + struct usb_device *udev; + unsigned vid, pid, bcd; + const struct ignore_entry *p; + + udev = interface_to_usbdev(intf); + vid = le16_to_cpu(udev->descriptor.idVendor); + pid = le16_to_cpu(udev->descriptor.idProduct); + bcd = le16_to_cpu(udev->descriptor.bcdDevice); + + for (p = ignore_ids; p->vid; ++p) { + if (p->vid == vid && p->pid == pid && + p->bcdmin <= bcd && p->bcdmax >= bcd) + return -ENXIO; + } + return 0; +} -- cgit v1.3.1