From 871480933a1c28f8a9fed4c4d34d06c439a7a422 Mon Sep 17 00:00:00 2001
From: Srikant Patnaik
Date: Sun, 11 Jan 2015 12:28:04 +0530
Subject: Moved, renamed, and deleted files
The original directory structure was scattered and unorganized.
Changes are basically to make it look like kernel structure.
---
drivers/usb/storage/Kconfig | 229 +++
drivers/usb/storage/Makefile | 51 +
drivers/usb/storage/alauda.c | 1282 ++++++++++++++++
drivers/usb/storage/cypress_atacb.c | 278 ++++
drivers/usb/storage/datafab.c | 757 ++++++++++
drivers/usb/storage/debug.c | 177 +++
drivers/usb/storage/debug.h | 62 +
drivers/usb/storage/ene_ub6250.c | 2413 ++++++++++++++++++++++++++++++
drivers/usb/storage/freecom.c | 559 +++++++
drivers/usb/storage/initializers.c | 106 ++
drivers/usb/storage/initializers.h | 50 +
drivers/usb/storage/isd200.c | 1572 +++++++++++++++++++
drivers/usb/storage/jumpshot.c | 683 +++++++++
drivers/usb/storage/karma.c | 236 +++
drivers/usb/storage/libusual.c | 243 +++
drivers/usb/storage/onetouch.c | 318 ++++
drivers/usb/storage/option_ms.c | 170 +++
drivers/usb/storage/option_ms.h | 4 +
drivers/usb/storage/protocol.c | 220 +++
drivers/usb/storage/protocol.h | 57 +
drivers/usb/storage/realtek_cr.c | 1112 ++++++++++++++
drivers/usb/storage/scsiglue.c | 595 ++++++++
drivers/usb/storage/scsiglue.h | 48 +
drivers/usb/storage/sddr09.c | 1793 ++++++++++++++++++++++
drivers/usb/storage/sddr55.c | 1012 +++++++++++++
drivers/usb/storage/shuttle_usbat.c | 1869 +++++++++++++++++++++++
drivers/usb/storage/sierra_ms.c | 207 +++
drivers/usb/storage/sierra_ms.h | 4 +
drivers/usb/storage/transport.c | 1353 +++++++++++++++++
drivers/usb/storage/transport.h | 103 ++
drivers/usb/storage/uas.c | 862 +++++++++++
drivers/usb/storage/unusual_alauda.h | 31 +
drivers/usb/storage/unusual_cypress.h | 39 +
drivers/usb/storage/unusual_datafab.h | 98 ++
drivers/usb/storage/unusual_devs.h | 2050 +++++++++++++++++++++++++
drivers/usb/storage/unusual_ene_ub6250.h | 26 +
drivers/usb/storage/unusual_freecom.h | 26 +
drivers/usb/storage/unusual_isd200.h | 57 +
drivers/usb/storage/unusual_jumpshot.h | 27 +
drivers/usb/storage/unusual_karma.h | 26 +
drivers/usb/storage/unusual_onetouch.h | 36 +
drivers/usb/storage/unusual_realtek.h | 41 +
drivers/usb/storage/unusual_sddr09.h | 56 +
drivers/usb/storage/unusual_sddr55.h | 44 +
drivers/usb/storage/unusual_usbat.h | 43 +
drivers/usb/storage/usb.c | 1127 ++++++++++++++
drivers/usb/storage/usb.h | 204 +++
drivers/usb/storage/usual-tables.c | 118 ++
48 files changed, 22474 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/libusual.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.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_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 00000000..7691c866
--- /dev/null
+++ b/drivers/usb/storage/Kconfig
@@ -0,0 +1,229 @@
+#
+# USB Storage driver configuration
+#
+
+comment "NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may"
+comment "also be needed; see USB_STORAGE Help for more info"
+ depends on USB
+
+config USB_STORAGE
+ tristate "USB Mass Storage support"
+ depends on USB && 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.
+
+config USB_STORAGE_DEBUG
+ bool "USB Mass Storage verbose debug"
+ depends on USB_STORAGE
+ 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"
+ depends on USB_STORAGE
+ 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_RUNTIME
+ default y
+
+config USB_STORAGE_DATAFAB
+ tristate "Datafab Compact Flash Reader support"
+ depends on USB_STORAGE
+ 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"
+ depends on USB_STORAGE
+ 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"
+ depends on USB_STORAGE
+ ---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"
+ depends on USB_STORAGE
+ 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"
+ depends on USB_STORAGE
+ 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"
+ depends on USB_STORAGE
+ 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"
+ depends on USB_STORAGE
+ 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"
+ depends on USB_STORAGE
+ 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 USB_STORAGE
+ 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"
+ depends on USB_STORAGE
+ 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"
+ depends on USB_STORAGE
+ ---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"
+ depends on USB && SCSI
+ depends on USB_STORAGE
+ ---help---
+ Say Y here if you wish to control a ENE SD/MS Card reader.
+ To use SM card, please build driver/staging/keucr/keucr.ko
+
+ 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 ums-eneub6250.
+
+config USB_UAS
+ tristate "USB Attached SCSI"
+ depends on USB && SCSI
+ 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.
+
+config USB_LIBUSUAL
+ bool "The shared table of common (or usual) storage devices"
+ depends on USB
+ help
+ This module contains a table of common (or usual) devices
+ for usb-storage and ub drivers, and allows to switch binding
+ of these devices without rebuilding modules.
+
+ Typical syntax of /etc/modprobe.d/*conf is:
+
+ options libusual bias="ub"
+
+ If unsure, say N.
diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile
new file mode 100644
index 00000000..82e6416a
--- /dev/null
+++ b/drivers/usb/storage/Makefile
@@ -0,0 +1,51 @@
+#
+# Makefile for the USB Mass Storage device drivers.
+#
+# 15 Aug 2000, Christoph Hellwig
+# Rewritten to use lists instead of if-statements.
+#
+
+ccflags-y := -Idrivers/scsi
+
+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-$(CONFIG_USB_STORAGE_DEBUG) += debug.o
+
+ifeq ($(CONFIG_USB_LIBUSUAL),)
+ usb-storage-y += usual-tables.o
+else
+ obj-$(CONFIG_USB) += usb-libusual.o
+ usb-libusual-y := libusual.o usual-tables.o
+endif
+
+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 00000000..bab8c8fe
--- /dev/null
+++ b/drivers/usb/storage/alauda.c
@@ -0,0 +1,1282 @@
+/*
+ * 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
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include "usb.h"
+#include "transport.h"
+#include "protocol.h"
+#include "debug.h"
+
+MODULE_DESCRIPTION("Driver for Alauda-based card readers");
+MODULE_AUTHOR("Daniel Drake ");
+MODULE_LICENSE("GPL");
+
+/*
+ * 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 */
+};
+
+#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)|(USB_US_TYPE_STOR<<24) }
+
+static 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 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);
+
+ US_DEBUGP("alauda_get_media_status: 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;
+ 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;
+ }
+
+ US_DEBUGP("alauda_init_media: 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) {
+ US_DEBUGP("alauda_init_media: 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;
+
+ US_DEBUGP("alauda_init_media: Media signature: %02X %02X %02X %02X\n",
+ data[0], data[1], data[2], data[3]);
+ media_info = alauda_card_find_id(data[1]);
+ if (media_info == NULL) {
+ printk(KERN_WARNING
+ "alauda_init_media: Unrecognised media signature: "
+ "%02X %02X %02X %02X\n",
+ data[0], data[1], data[2], data[3]);
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ MEDIA_INFO(us).capacity = 1 << media_info->chipshift;
+ US_DEBUGP("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 (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[2];
+ int rc;
+
+ rc = alauda_get_media_status(us, status);
+
+ /* Check for no media or door open */
+ if ((status[0] & 0x80) || ((status[0] & 0x1F) == 0x10)
+ || ((status[1] & 0x01) == 0)) {
+ US_DEBUGP("alauda_check_media: 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) {
+ US_DEBUGP("alauda_check_media: Media change detected\n");
+ alauda_free_maps(&MEDIA_INFO(us));
+ alauda_init_media(us);
+
+ 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;
+
+ US_DEBUGP("alauda_check_status2: %02X %02X %02X\n", data[0], data[1], data[2]);
+ 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;
+ }
+
+ US_DEBUGP("alauda_read_map: 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;
+ US_DEBUGP("alauda_read_map: 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) {
+ US_DEBUGP("alauda_read_map: 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) {
+ US_DEBUGP("alauda_read_map: 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];
+
+ US_DEBUGP("alauda_erase_block: 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;
+
+ US_DEBUGP("alauda_erase_block: 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)
+ };
+
+ US_DEBUGP("alauda_read_block: 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)
+ };
+
+ US_DEBUGP("alauda_write_block: 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)) {
+ US_DEBUGP("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)) {
+ US_DEBUGP("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;
+ US_DEBUGP("alauda_write_lba: 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 == NULL) {
+ printk(KERN_WARNING "alauda_read_data: Out of memory\n");
+ 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) {
+ US_DEBUGP("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 */
+ US_DEBUGP("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 {
+ US_DEBUGP("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 == NULL) {
+ printk(KERN_WARNING "alauda_write_data: Out of memory\n");
+ 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((pagesize + 64) * blocksize, GFP_NOIO);
+ if (blockbuffer == NULL) {
+ printk(KERN_WARNING "alauda_write_data: Out of memory\n");
+ 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) {
+ US_DEBUGP("alauda_write_data: 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(sizeof(struct alauda_info), GFP_NOIO);
+ if (!us->extra) {
+ US_DEBUGP("init_alauda: Gah! Can't allocate storage for"
+ "alauda info struct!\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+ 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 USB_STOR_TRANSPORT_GOOD;
+}
+
+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 unsigned char inquiry_response[36] = {
+ 0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
+ };
+
+ if (srb->cmnd[0] == INQUIRY) {
+ US_DEBUGP("alauda_transport: 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) {
+ US_DEBUGP("alauda_transport: 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]);
+
+ US_DEBUGP("alauda_transport: 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]);
+
+ US_DEBUGP("alauda_transport: WRITE_10: page %d pagect %d\n",
+ page, pages);
+
+ return alauda_write_data(us, page, pages);
+ }
+
+ if (srb->cmnd[0] == REQUEST_SENSE) {
+ US_DEBUGP("alauda_transport: 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;
+ }
+
+ US_DEBUGP("alauda_transport: 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 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);
+ 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 = "ums-alauda",
+ .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_driver(alauda_driver);
diff --git a/drivers/usb/storage/cypress_atacb.c b/drivers/usb/storage/cypress_atacb.c
new file mode 100644
index 00000000..5fe451d1
--- /dev/null
+++ b/drivers/usb/storage/cypress_atacb.c
@@ -0,0 +1,278 @@
+/*
+ * 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)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "usb.h"
+#include "protocol.h"
+#include "scsiglue.h"
+#include "debug.h"
+
+MODULE_DESCRIPTION("SAT support for Cypress USB/ATA bridges with ATACB");
+MODULE_AUTHOR("Matthieu Castet ");
+MODULE_LICENSE("GPL");
+
+/*
+ * 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)|(USB_US_TYPE_STOR<<24) }
+
+static 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 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) {
+ US_DEBUGP("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 = ses.cmnd;
+ 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 = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;
+ }
+ goto end;
+invalid_fld:
+ srb->result = (DRIVER_SENSE << 24) | 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 int cypress_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 - cypress_usb_ids) + cypress_unusual_dev_list);
+ if (result)
+ return result;
+
+ us->protocol_name = "Transparent SCSI with Cypress ATACB";
+ us->proto_handler = cypress_atacb_passthrough;
+
+ result = usb_stor_probe2(us);
+ return result;
+}
+
+static struct usb_driver cypress_driver = {
+ .name = "ums-cypress",
+ .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_driver(cypress_driver);
diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c
new file mode 100644
index 00000000..35e9c51e
--- /dev/null
+++ b/drivers/usb/storage/datafab.c
@@ -0,0 +1,757 @@
+/* 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 program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * 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"
+
+MODULE_DESCRIPTION("Driver for Datafab USB Compact Flash reader");
+MODULE_AUTHOR("Jimmie Mayfield ");
+MODULE_LICENSE("GPL");
+
+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)|(USB_US_TYPE_STOR<<24) }
+
+static 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 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;
+
+ US_DEBUGP("datafab_bulk_read: 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;
+
+ US_DEBUGP("datafab_bulk_write: 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) {
+ US_DEBUGP("datafab_write_data: Gah! "
+ "write return code: %02x %02x\n",
+ reply[0], reply[1]);
+ result = USB_STOR_TRANSPORT_ERROR;
+ 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 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;
+
+ US_DEBUGP("datafab_determine_lun: 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 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 unsigned char rw_err_page[12] = {
+ 0x1, 0xA, 0x21, 1, 0, 0, 0, 0, 1, 0, 0, 0
+ };
+ static unsigned char cache_page[12] = {
+ 0x8, 0xA, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ static unsigned char rbac_page[12] = {
+ 0x1B, 0xA, 0, 0x81, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ static 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:
+ US_DEBUGP("datafab_handle_mode_sense: Current values\n");
+ break;
+ case 0x1:
+ US_DEBUGP("datafab_handle_mode_sense: Changeable values\n");
+ break;
+ case 0x2:
+ US_DEBUGP("datafab_handle_mode_sense: Default values\n");
+ break;
+ case 0x3:
+ US_DEBUGP("datafab_handle_mode_sense: 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 unsigned char inquiry_reply[8] = {
+ 0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
+ };
+
+ if (!us->extra) {
+ us->extra = kzalloc(sizeof(struct datafab_info), GFP_NOIO);
+ if (!us->extra) {
+ US_DEBUGP("datafab_transport: Gah! "
+ "Can't allocate storage for Datafab info struct!\n");
+ 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) {
+ US_DEBUGP("datafab_transport: INQUIRY. Returning bogus response");
+ 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;
+
+ US_DEBUGP("datafab_transport: 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) {
+ US_DEBUGP("datafab_transport: 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]));
+
+ US_DEBUGP("datafab_transport: 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]));
+
+ US_DEBUGP("datafab_transport: 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]));
+
+ US_DEBUGP("datafab_transport: 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]));
+
+ US_DEBUGP("datafab_transport: 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) {
+ US_DEBUGP("datafab_transport: TEST_UNIT_READY.\n");
+ return datafab_id_device(us, info);
+ }
+
+ if (srb->cmnd[0] == REQUEST_SENSE) {
+ US_DEBUGP("datafab_transport: 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) {
+ US_DEBUGP("datafab_transport: MODE_SENSE_6 detected\n");
+ return datafab_handle_mode_sense(us, srb, 1);
+ }
+
+ if (srb->cmnd[0] == MODE_SENSE_10) {
+ US_DEBUGP("datafab_transport: 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 */
+ US_DEBUGP("datafab_transport: 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;
+ }
+
+ US_DEBUGP("datafab_transport: 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 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);
+ 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 = "ums-datafab",
+ .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_driver(datafab_driver);
diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c
new file mode 100644
index 00000000..a2b5526c
--- /dev/null
+++ b/drivers/usb/storage/debug.c
@@ -0,0 +1,177 @@
+/* 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.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include
+#include
+#include
+#include
+
+#include "debug.h"
+#include "scsi.h"
+
+
+void usb_stor_show_command(struct scsi_cmnd *srb)
+{
+ char *what = NULL;
+ int i;
+
+ 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: what = "RESERVE"; break;
+ case RELEASE: 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;
+ }
+ US_DEBUGP("Command %s (%d bytes)\n", what, srb->cmd_len);
+ US_DEBUGP("");
+ for (i = 0; i < srb->cmd_len && i < 16; i++)
+ US_DEBUGPX(" %02x", srb->cmnd[i]);
+ US_DEBUGPX("\n");
+}
+
+void usb_stor_show_sense(
+ unsigned char key,
+ unsigned char asc,
+ unsigned char ascq) {
+
+ const char *what, *keystr;
+
+ keystr = scsi_sense_key_string(key);
+ what = scsi_extd_sense_format(asc, ascq);
+
+ if (keystr == NULL)
+ keystr = "(Unknown Key)";
+ if (what == NULL)
+ what = "(unknown ASC/ASCQ)";
+
+ US_DEBUGP("%s: ", keystr);
+ US_DEBUGPX(what, ascq);
+ US_DEBUGPX("\n");
+}
diff --git a/drivers/usb/storage/debug.h b/drivers/usb/storage/debug.h
new file mode 100644
index 00000000..dbb985d5
--- /dev/null
+++ b/drivers/usb/storage/debug.h
@@ -0,0 +1,62 @@
+/* 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.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _DEBUG_H_
+#define _DEBUG_H_
+
+#include
+
+#define USB_STORAGE "usb-storage: "
+
+#ifdef CONFIG_USB_STORAGE_DEBUG
+void usb_stor_show_command(struct scsi_cmnd *srb);
+void usb_stor_show_sense( unsigned char key,
+ unsigned char asc, unsigned char ascq );
+#define US_DEBUGP(x...) printk( KERN_DEBUG USB_STORAGE x )
+#define US_DEBUGPX(x...) printk( x )
+#define US_DEBUG(x) x
+#else
+#define US_DEBUGP(x...)
+#define US_DEBUGPX(x...)
+#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 00000000..e7e67810
--- /dev/null
+++ b/drivers/usb/storage/ene_ub6250.c
@@ -0,0 +1,2413 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+
+#include "usb.h"
+#include "transport.h"
+#include "protocol.h"
+#include "debug.h"
+
+MODULE_DESCRIPTION("Driver for ENE UB6250 reader");
+MODULE_LICENSE("GPL");
+
+
+/*
+ * 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)|(USB_US_TYPE_STOR<<24) }
+
+static 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 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 0x00 /* No Sense */
+#define SS_NOT_READY 0x02
+#define SS_MEDIUM_ERR 0x03
+#define SS_HW_ERR 0x04
+#define SS_ILLEGAL_REQUEST 0x05
+#define SS_UNIT_ATTENTION 0x06
+
+/* 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))
+
+
+struct SD_STATUS {
+ u8 Insert:1;
+ u8 Ready:1;
+ u8 MediaChange:1;
+ u8 IsMMC:1;
+ u8 HiCapacity:1;
+ u8 HiSpeed:1;
+ u8 WtP:1;
+ u8 Reserved:1;
+};
+
+struct MS_STATUS {
+ u8 Insert:1;
+ u8 Ready:1;
+ u8 MediaChange:1;
+ u8 IsMSPro:1;
+ u8 IsMSPHG:1;
+ u8 Reserved1:1;
+ u8 WtP:1;
+ u8 Reserved2:1;
+};
+
+struct SM_STATUS {
+ u8 Insert:1;
+ u8 Ready:1;
+ u8 MediaChange:1;
+ u8 Reserved:3;
+ u8 WtP:1;
+ u8 IsMS:1;
+};
+
+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 {
+ /* for 6250 code */
+ struct SD_STATUS SD_Status;
+ struct MS_STATUS MS_Status;
+ struct SM_STATUS 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)
+{
+ if (!extra)
+ return;
+}
+
+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;
+
+ /* US_DEBUGP("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) {
+ US_DEBUGP("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) {
+ US_DEBUGP("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) {
+ US_DEBUGP("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 */
+ US_DEBUGP("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),
+ (int)residue));
+ }
+
+ if (bcs->Status != US_BULK_STAT_OK)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ 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.Insert && info->SD_Status.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_inquiry(struct us_data *us, struct scsi_cmnd *srb)
+{
+ unsigned char data_ptr[36] = {
+ 0x00, 0x80, 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_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.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;
+
+ US_DEBUGP("sd_scsi_read_capacity\n");
+ if (info->SD_Status.HiCapacity) {
+ bl_len = 0x200;
+ if (info->SD_Status.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;
+ US_DEBUGP("bl_len = %x\n", bl_len);
+ US_DEBUGP("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) {
+ US_DEBUGP("Load SD RW pattern Fail !!\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ if (info->SD_Status.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) {
+ US_DEBUGP("Load SD RW pattern Fail !!\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ if (info->SD_Status.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 = 0x00;
+ 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(info->MS_Lib.NumberOfPhyBlock * sizeof(u16), GFP_KERNEL);
+ info->MS_Lib.Log2PhyMap = kmalloc(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++;
+ 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;
+ int result;
+ u8 ExtBuf[4];
+ u32 bn = PhyBlockAddr * 0x20 + PageNum;
+
+ /* printk(KERN_INFO "MS --- MS_ReaderReadPage,
+ PhyBlockAddr = %x, PageNum = %x\n", PhyBlockAddr, 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, &ExtBuf, 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 = ExtBuf[0];
+ ExtraDat->mngflg = ExtBuf[1];
+ ExtraDat->logadr = memstick_logaddr(ExtBuf[2], ExtBuf[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 = kmalloc(MS_BYTES_PER_PAGE, 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((u8 *)(info->MS_Lib.blkpag)); /* Arnold test ... */
+ info->MS_Lib.blkpag = NULL;
+ }
+
+ if (info->MS_Lib.blkext) {
+ kfree((u8 *)(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(info->MS_Lib.PagesPerBlock * info->MS_Lib.BytesPerSector, GFP_KERNEL);
+ info->MS_Lib.blkext = kmalloc(info->MS_Lib.PagesPerBlock * sizeof(struct ms_lib_type_extdat), GFP_KERNEL);
+
+ 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;
+
+ /* printk(KERN_INFO "MS_ReaderCopyBlock --- PhyBlockAddr = %x,
+ PageNum = %x\n", PhyBlockAddr, PageNum); */
+ 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 = 0x00;
+ 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;
+
+ /* printk(KERN_INFO "MS --- ms_read_eraseblock,
+ PhyBlockAddr = %x\n", 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;
+
+ /* printk("MS --- MS_LibOverwriteExtra,
+ PhyBlockAddr = %x, PageNum = %x\n", PhyBlockAddr, PageNum); */
+ 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;
+ int result;
+ u8 ExtBuf[4];
+
+ /* printk("MS_LibReadExtra --- PhyBlock = %x, PageNum = %x\n", PhyBlock, PageNum); */
+ 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, &ExtBuf, 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 = ExtBuf[0];
+ ExtraDat->mngflg = ExtBuf[1];
+ ExtraDat->logadr = memstick_logaddr(ExtBuf[2], ExtBuf[3]);
+
+ return USB_STOR_TRANSPORT_GOOD;
+}
+
+static int ms_libsearch_block_from_physical(struct us_data *us, u16 phyblk)
+{
+ u16 Newblk;
+ 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;
+
+ Newblk = info->MS_Lib.Phy2LogMap[blk];
+ 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.Insert && info->MS_Status.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_inquiry(struct us_data *us, struct scsi_cmnd *srb)
+{
+ /* pr_info("MS_SCSI_Inquiry\n"); */
+ unsigned char data_ptr[36] = {
+ 0x00, 0x80, 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 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.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;
+ u16 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;
+
+ US_DEBUGP("ms_scsi_read_capacity\n");
+ bl_len = 0x200;
+ if (info->MS_Status.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;
+ US_DEBUGP("bl_len = %x\n", bl_len);
+ US_DEBUGP("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;
+
+ /* printk("MS_LibReadExtraBlock --- PhyBlock = %x,
+ PageNum = %x, blen = %x\n", PhyBlock, PageNum, blen); */
+
+ /* 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;
+ u8 buf[0x200];
+ u32 count = 0, index = 0;
+ struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra;
+
+ 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, &buf);
+ count += 0x80;
+ }
+ index = (PhyBlock % 0x80) * 4;
+
+ extdat.ovrflg = buf[index];
+ extdat.mngflg = buf[index+1];
+ extdat.logadr = memstick_logaddr(buf[index+2], buf[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.IsMSPro) {
+ result = ene_load_bincode(us, MSP_RW_PATTERN);
+ if (result != USB_STOR_XFER_GOOD) {
+ US_DEBUGP("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.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 = 0x00;
+ 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;
+
+ /*US_DEBUGP("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.HiCapacity && !info->SD_Status.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.HiCapacity && info->SD_Status.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:
+ US_DEBUGP("SD_INIT1_PATTERN\n");
+ fw_name = "ene-ub6250/sd_init1.bin";
+ break;
+ case SD_INIT2_PATTERN:
+ US_DEBUGP("SD_INIT2_PATTERN\n");
+ fw_name = "ene-ub6250/sd_init2.bin";
+ break;
+ case SD_RW_PATTERN:
+ US_DEBUGP("SD_RDWR_PATTERN\n");
+ fw_name = "ene-ub6250/sd_rdwr.bin";
+ break;
+ /* For MS */
+ case MS_INIT_PATTERN:
+ US_DEBUGP("MS_INIT_PATTERN\n");
+ fw_name = "ene-ub6250/ms_init.bin";
+ break;
+ case MSP_RW_PATTERN:
+ US_DEBUGP("MSP_RW_PATTERN\n");
+ fw_name = "ene-ub6250/msp_rdwr.bin";
+ break;
+ case MS_RW_PATTERN:
+ US_DEBUGP("MS_RW_PATTERN\n");
+ fw_name = "ene-ub6250/ms_rdwr.bin";
+ break;
+ default:
+ US_DEBUGP("----------- Unknown PATTERN ----------\n");
+ goto nofw;
+ }
+
+ err = request_firmware(&sd_fw, fw_name, &us->pusb_dev->dev);
+ if (err) {
+ US_DEBUGP("load firmware %s failed\n", fw_name);
+ goto nofw;
+ }
+ buf = kmalloc(sd_fw->size, GFP_KERNEL);
+ if (buf == NULL) {
+ US_DEBUGP("Malloc memory for fireware failed!\n");
+ goto nofw;
+ }
+ memcpy(buf, sd_fw->data, sd_fw->size);
+ memset(bcb, 0, sizeof(struct bulk_cb_wrap));
+ bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
+ bcb->DataTransferLength = sd_fw->size;
+ bcb->Flags = 0x00;
+ bcb->CDB[0] = 0xEF;
+
+ result = ene_send_scsi_cmd(us, FDIR_WRITE, buf, 0);
+ info->BIN_FLAG = flag;
+ kfree(buf);
+
+nofw:
+ if (sd_fw != NULL) {
+ release_firmware(sd_fw);
+ sd_fw = NULL;
+ }
+
+ 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;
+ u8 buf[0x200];
+ u16 MSP_BlockSize, MSP_UserAreaBlocks;
+ struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra;
+
+ 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, &buf, 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 = *(struct MS_STATUS *)&buf[0];
+
+ if (info->MS_Status.Insert && info->MS_Status.Ready) {
+ printk(KERN_INFO "Insert = %x\n", info->MS_Status.Insert);
+ printk(KERN_INFO "Ready = %x\n", info->MS_Status.Ready);
+ printk(KERN_INFO "IsMSPro = %x\n", info->MS_Status.IsMSPro);
+ printk(KERN_INFO "IsMSPHG = %x\n", info->MS_Status.IsMSPHG);
+ printk(KERN_INFO "WtP= %x\n", info->MS_Status.WtP);
+ if (info->MS_Status.IsMSPro) {
+ MSP_BlockSize = (buf[6] << 8) | buf[7];
+ MSP_UserAreaBlocks = (buf[10] << 8) | buf[11];
+ info->MSP_TotalBlock = MSP_BlockSize * MSP_UserAreaBlocks;
+ } else {
+ ms_card_init(us); /* Card is MS (to ms.c)*/
+ }
+ US_DEBUGP("MS Init Code OK !!\n");
+ } else {
+ US_DEBUGP("MS Card Not Ready --- %x\n", buf[0]);
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ return USB_STOR_TRANSPORT_GOOD;
+}
+
+static int ene_sd_init(struct us_data *us)
+{
+ int result;
+ u8 buf[0x200];
+ struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
+ struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra;
+
+ US_DEBUGP("transport --- ENE_SDInit\n");
+ /* SD Init Part-1 */
+ result = ene_load_bincode(us, SD_INIT1_PATTERN);
+ if (result != USB_STOR_XFER_GOOD) {
+ US_DEBUGP("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) {
+ US_DEBUGP("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) {
+ US_DEBUGP("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, &buf, 0);
+ if (result != USB_STOR_XFER_GOOD) {
+ US_DEBUGP("Execution SD Init Code Fail !!\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ info->SD_Status = *(struct SD_STATUS *)&buf[0];
+ if (info->SD_Status.Insert && info->SD_Status.Ready) {
+ ene_get_card_status(us, (unsigned char *)&buf);
+ US_DEBUGP("Insert = %x\n", info->SD_Status.Insert);
+ US_DEBUGP("Ready = %x\n", info->SD_Status.Ready);
+ US_DEBUGP("IsMMC = %x\n", info->SD_Status.IsMMC);
+ US_DEBUGP("HiCapacity = %x\n", info->SD_Status.HiCapacity);
+ US_DEBUGP("HiSpeed = %x\n", info->SD_Status.HiSpeed);
+ US_DEBUGP("WtP = %x\n", info->SD_Status.WtP);
+ } else {
+ US_DEBUGP("SD Card Not Ready --- %x\n", buf[0]);
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+ return USB_STOR_TRANSPORT_GOOD;
+}
+
+
+static int ene_init(struct us_data *us)
+{
+ int result;
+ u8 misc_reg03 = 0;
+ struct ene_ub6250_info *info = (struct ene_ub6250_info *)(us->extra);
+
+ result = ene_get_card_type(us, REG_CARD_STATUS, &misc_reg03);
+ if (result != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ if (misc_reg03 & 0x01) {
+ if (!info->SD_Status.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.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;
+
+ info->SrbStatus = SS_SUCCESS;
+ switch (srb->cmnd[0]) {
+ case TEST_UNIT_READY:
+ result = sd_scsi_test_unit_ready(us, srb);
+ break; /* 0x00 */
+ case INQUIRY:
+ result = sd_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;
+ }
+ 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;
+ info->SrbStatus = SS_SUCCESS;
+ switch (srb->cmnd[0]) {
+ case TEST_UNIT_READY:
+ result = ms_scsi_test_unit_ready(us, srb);
+ break; /* 0x00 */
+ case INQUIRY:
+ result = ms_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;
+ }
+ return result;
+}
+
+static int ene_transport(struct scsi_cmnd *srb, struct us_data *us)
+{
+ int result = 0;
+ struct ene_ub6250_info *info = (struct ene_ub6250_info *)(us->extra);
+
+ /*US_DEBUG(usb_stor_show_command(srb)); */
+ scsi_set_resid(srb, 0);
+ if (unlikely(!(info->SD_Status.Ready || info->MS_Status.Ready))) {
+ result = ene_init(us);
+ } else {
+ if (info->SD_Status.Ready)
+ result = sd_scsi_irp(us, srb);
+
+ if (info->MS_Status.Ready)
+ result = ms_scsi_irp(us, srb);
+ }
+ return 0;
+}
+
+
+static int ene_ub6250_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int result;
+ u8 misc_reg03 = 0;
+ struct us_data *us;
+
+ result = usb_stor_probe1(&us, intf, id,
+ (id - ene_ub6250_usb_ids) + ene_ub6250_unusual_dev_list);
+ if (result)
+ return result;
+
+ /* FIXME: where should the code alloc extra buf ? */
+ if (!us->extra) {
+ us->extra = kzalloc(sizeof(struct ene_ub6250_info), GFP_KERNEL);
+ if (!us->extra)
+ return -ENOMEM;
+ us->extra_destructor = ene_ub6250_info_destructor;
+ }
+
+ 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 */
+ result = ene_get_card_type(us, REG_CARD_STATUS, &misc_reg03);
+ if (result != USB_STOR_XFER_GOOD) {
+ usb_stor_disconnect(intf);
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ if (!(misc_reg03 & 0x01)) {
+ pr_info("ums_eneub6250: The driver only supports SD/MS card. "
+ "To use SM card, please build driver/staging/keucr\n");
+ }
+
+ return result;
+}
+
+
+#ifdef CONFIG_PM
+
+static int ene_ub6250_resume(struct usb_interface *iface)
+{
+ u8 tmp = 0;
+ struct us_data *us = usb_get_intfdata(iface);
+ struct ene_ub6250_info *info = (struct ene_ub6250_info *)(us->extra);
+
+ mutex_lock(&us->dev_mutex);
+
+ US_DEBUGP("%s\n", __func__);
+ if (us->suspend_resume_hook)
+ (us->suspend_resume_hook)(us, US_RESUME);
+
+ mutex_unlock(&us->dev_mutex);
+
+ info->Power_IsResum = true;
+ /*info->SD_Status.Ready = 0; */
+ info->SD_Status = *(struct SD_STATUS *)&tmp;
+ info->MS_Status = *(struct MS_STATUS *)&tmp;
+ info->SM_Status = *(struct SM_STATUS *)&tmp;
+
+ return 0;
+}
+
+static int ene_ub6250_reset_resume(struct usb_interface *iface)
+{
+ u8 tmp = 0;
+ struct us_data *us = usb_get_intfdata(iface);
+ struct ene_ub6250_info *info = (struct ene_ub6250_info *)(us->extra);
+ US_DEBUGP("%s\n", __func__);
+ /* 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.Ready = 0; */
+ info->SD_Status = *(struct SD_STATUS *)&tmp;
+ info->MS_Status = *(struct MS_STATUS *)&tmp;
+ info->SM_Status = *(struct SM_STATUS *)&tmp;
+
+ return 0;
+}
+
+#else
+
+#define ene_ub6250_resume NULL
+#define ene_ub6250_reset_resume NULL
+
+#endif
+
+static struct usb_driver ene_ub6250_driver = {
+ .name = "ums_eneub6250",
+ .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_driver(ene_ub6250_driver);
diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c
new file mode 100644
index 00000000..042cf9ef
--- /dev/null
+++ b/drivers/usb/storage/freecom.c
@@ -0,0 +1,559 @@
+/* Driver for Freecom USB/IDE adaptor
+ *
+ * Freecom v0.1:
+ *
+ * First release
+ *
+ * Current development and maintenance by:
+ * (C) 2000 David Brown
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This driver was developed with information provided in FREECOM's USB
+ * Programmers Reference Guide. For further information contact Freecom
+ * (http://www.freecom.de/)
+ */
+
+#include
+#include
+#include
+
+#include "usb.h"
+#include "transport.h"
+#include "protocol.h"
+#include "debug.h"
+
+MODULE_DESCRIPTION("Driver for Freecom USB/IDE adaptor");
+MODULE_AUTHOR("David Brown ");
+MODULE_LICENSE("GPL");
+
+#ifdef CONFIG_USB_STORAGE_DEBUG
+static void pdump (void *, int);
+#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)|(USB_US_TYPE_STOR<<24) }
+
+static 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 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));
+
+ US_DEBUGP("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) {
+ US_DEBUGP ("Freecom readdata transport error\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ /* Now transfer all of our blocks. */
+ US_DEBUGP("Start of read\n");
+ result = usb_stor_bulk_srb(us, ipipe, srb);
+ US_DEBUGP("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));
+
+ US_DEBUGP("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) {
+ US_DEBUGP ("Freecom writedata transport error\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ /* Now transfer all of our blocks. */
+ US_DEBUGP("Start of write\n");
+ result = usb_stor_bulk_srb(us, opipe, srb);
+
+ US_DEBUGP("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;
+
+ US_DEBUGP("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 (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) {
+ US_DEBUGP ("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);
+ US_DEBUGP("foo Status result %d %u\n", result, partial);
+ if (result != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ US_DEBUG(pdump ((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) {
+ US_DEBUGP("20 second USB/ATAPI bridge TIMEOUT occurred!\n");
+ US_DEBUGP("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) {
+ US_DEBUGP ("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);
+
+ US_DEBUGP("bar Status result %d %u\n", result, partial);
+ if (result != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ US_DEBUG(pdump ((void *) fst, partial));
+ }
+
+ if (partial != 4)
+ return USB_STOR_TRANSPORT_ERROR;
+ if ((fst->Status & 1) != 0) {
+ US_DEBUGP("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. */
+ US_DEBUGP("Device indicates that it has %d bytes available\n",
+ le16_to_cpu (fst->Count));
+ US_DEBUGP("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);
+ US_DEBUGP("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) {
+ US_DEBUGP("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;
+
+ US_DEBUGP("FCM: Waiting for status\n");
+ result = usb_stor_bulk_transfer_buf (us, ipipe, fst,
+ FCM_PACKET_LENGTH, &partial);
+ US_DEBUG(pdump ((void *) fst, partial));
+
+ if (partial != 4 || result > USB_STOR_XFER_SHORT)
+ return USB_STOR_TRANSPORT_ERROR;
+ if ((fst->Status & ERR_STAT) != 0) {
+ US_DEBUGP("operation failed\n");
+ return USB_STOR_TRANSPORT_FAILED;
+ }
+ if ((fst->Reason & 3) != 3) {
+ US_DEBUGP("Drive seems still hungry\n");
+ return USB_STOR_TRANSPORT_FAILED;
+ }
+ US_DEBUGP("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;
+
+ US_DEBUGP("FCM: 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) {
+ US_DEBUGP("operation failed\n");
+ return USB_STOR_TRANSPORT_FAILED;
+ }
+ if ((fst->Reason & 3) != 3) {
+ US_DEBUGP("Drive seems still hungry\n");
+ return USB_STOR_TRANSPORT_FAILED;
+ }
+
+ US_DEBUGP("Transfer happy\n");
+ break;
+
+
+ case DMA_NONE:
+ /* Easy, do nothing. */
+ break;
+
+ default:
+ /* should never hit here -- filtered in usb.c */
+ US_DEBUGP ("freecom unimplemented direction: %d\n",
+ us->srb->sc_data_direction);
+ /* Return fail, SCSI seems to handle this better. */
+ return USB_STOR_TRANSPORT_FAILED;
+ break;
+ }
+
+ 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';
+ US_DEBUGP("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);
+ US_DEBUGP("result from activate reset is %d\n", result);
+
+ /* wait 250ms */
+ mdelay(250);
+
+ /* clear reset */
+ result = usb_stor_control_msg(us, us->send_ctrl_pipe,
+ 0x4d, 0x40, 0x24f8, 0x0, NULL, 0x0, 3*HZ);
+ US_DEBUGP("result from clear reset is %d\n", result);
+
+ /* wait 3 seconds */
+ mdelay(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 (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;
+ US_DEBUGP("%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;
+ US_DEBUGP("%s\n", line);
+ offset = 0;
+}
+#endif
+
+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);
+ 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 = "ums-freecom",
+ .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_driver(freecom_driver);
diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c
new file mode 100644
index 00000000..105d9001
--- /dev/null
+++ b/drivers/usb/storage/initializers.c
@@ -0,0 +1,106 @@
+/* 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.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#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;
+
+ US_DEBUGP("Attempting to init eUSCSI bridge...\n");
+ us->iobuf[0] = 0x1;
+ result = usb_stor_control_msg(us, us->send_ctrl_pipe,
+ 0x0C, USB_RECIP_INTERFACE | USB_TYPE_VENDOR,
+ 0x01, 0x0, us->iobuf, 0x1, 5000);
+ US_DEBUGP("-- 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 char init_string[] = "\xec\x0a\x06\x00$PCCHIPS";
+
+ US_DEBUGP("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;
+
+ US_DEBUGP("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, 1000);
+ US_DEBUGP("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 00000000..529327fb
--- /dev/null
+++ b/drivers/usb/storage/initializers.h
@@ -0,0 +1,50 @@
+/* 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.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#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 00000000..31fa24e7
--- /dev/null
+++ b/drivers/usb/storage/isd200.c
@@ -0,0 +1,1572 @@
+/* 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.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 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"
+
+MODULE_DESCRIPTION("Driver for In-System Design, Inc. ISD200 ASIC");
+MODULE_AUTHOR("Björn Stenberg ");
+MODULE_LICENSE("GPL");
+
+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)|(USB_US_TYPE_STOR<<24) }
+
+static struct usb_device_id isd200_usb_ids[] = {
+# include "unusual_isd200.h"
+ { } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, isd200_usb_ids);
+
+#undef UNUSUAL_DEV
+#undef USUAL_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 struct us_unusual_dev isd200_unusual_dev_list[] = {
+# include "unusual_isd200.h"
+ { } /* Terminating entry */
+};
+
+#undef UNUSUAL_DEV
+#undef USUAL_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[BLK_MAX_CDB];
+ 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));
+ srb->cmnd = info->cmnd;
+ 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:
+ US_DEBUGP(" 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:
+ US_DEBUGP(" 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:
+ US_DEBUGP(" 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:
+ US_DEBUGP(" 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:
+ US_DEBUGP(" 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:
+ US_DEBUGP(" 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:
+ US_DEBUGP("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 {
+ US_DEBUGP(" 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;
+
+ US_DEBUGP("Entering isd200_IssueATAReadRegs\n");
+
+ transferStatus = isd200_action( us, ACTION_READ_STATUS,
+ info->RegsBuf, sizeof(info->ATARegs) );
+ if (transferStatus != ISD200_TRANSPORT_GOOD) {
+ US_DEBUGP(" Error reading ATA registers\n");
+ retStatus = ISD200_ERROR;
+ } else {
+ memcpy(info->ATARegs, info->RegsBuf, sizeof(info->ATARegs));
+ US_DEBUGP(" 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)) {
+ US_DEBUGP("-- 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:
+ US_DEBUGP("-- transport indicates protocol failure\n");
+ srb->result = SAM_STAT_CHECK_CONDITION;
+ return;
+
+ case USB_STOR_TRANSPORT_FAILED:
+ US_DEBUGP("-- transport indicates command failure\n");
+ need_auto_sense = 1;
+ break;
+
+ case USB_STOR_TRANSPORT_ERROR:
+ US_DEBUGP("-- transport indicates transport error\n");
+ srb->result = DID_ERROR << 16;
+ /* Need reset here */
+ return;
+
+ default:
+ US_DEBUGP("-- 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))) {
+ US_DEBUGP("-- 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)) {
+ US_DEBUGP("-- 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 isd200_info* info )
+{
+ US_DEBUGP(" Event Notification: 0x%x\n",
+ info->ConfigData.EventNotification);
+ US_DEBUGP(" External Clock: 0x%x\n",
+ info->ConfigData.ExternalClock);
+ US_DEBUGP(" ATA Init Timeout: 0x%x\n",
+ info->ConfigData.ATAInitTimeout);
+ US_DEBUGP(" ATAPI Command Block Size: 0x%x\n",
+ (info->ConfigData.ATAConfig & ATACFG_BLOCKSIZE) >> 6);
+ US_DEBUGP(" Master/Slave Selection: 0x%x\n",
+ info->ConfigData.ATAConfig & ATACFG_MASTER);
+ US_DEBUGP(" ATAPI Reset: 0x%x\n",
+ info->ConfigData.ATAConfig & ATACFG_ATAPI_RESET);
+ US_DEBUGP(" ATA Timing: 0x%x\n",
+ info->ConfigData.ATAConfig & ATACFG_TIMING);
+ US_DEBUGP(" ATA Major Command: 0x%x\n",
+ info->ConfigData.ATAMajorCommand);
+ US_DEBUGP(" ATA Minor Command: 0x%x\n",
+ info->ConfigData.ATAMinorCommand);
+ US_DEBUGP(" Init Status: 0x%x\n",
+ info->ConfigData.ATAExtraConfig & ATACFGE_INIT_STATUS);
+ US_DEBUGP(" Config Descriptor 2: 0x%x\n",
+ info->ConfigData.ATAExtraConfig & ATACFGE_CONF_DESC2);
+ US_DEBUGP(" Skip Device Boot: 0x%x\n",
+ info->ConfigData.ATAExtraConfig & ATACFGE_SKIP_BOOT);
+ US_DEBUGP(" ATA 3 State Supsend: 0x%x\n",
+ info->ConfigData.ATAExtraConfig & ATACFGE_STATE_SUSPEND);
+ US_DEBUGP(" Descriptor Override: 0x%x\n",
+ info->ConfigData.ATAExtraConfig & ATACFGE_DESC_OVERRIDE);
+ US_DEBUGP(" Last LUN Identifier: 0x%x\n",
+ info->ConfigData.ATAExtraConfig & ATACFGE_LAST_LUN);
+ US_DEBUGP(" 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
+ US_DEBUGP("Entering isd200_write_config\n");
+ US_DEBUGP(" Writing the following ISD200 Config Data:\n");
+ isd200_log_config(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) {
+ US_DEBUGP(" ISD200 Config Data was written successfully\n");
+ } else {
+ US_DEBUGP(" Request to write ISD200 Config Data failed!\n");
+ retStatus = ISD200_ERROR;
+ }
+
+ US_DEBUGP("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;
+
+ US_DEBUGP("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) {
+ US_DEBUGP(" Retrieved the following ISD200 Config Data:\n");
+#ifdef CONFIG_USB_STORAGE_DEBUG
+ isd200_log_config(info);
+#endif
+ } else {
+ US_DEBUGP(" Request to get ISD200 Config Data failed!\n");
+ retStatus = ISD200_ERROR;
+ }
+
+ US_DEBUGP("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;
+
+ US_DEBUGP("Entering isd200_atapi_soft_reset\n");
+
+ transferStatus = isd200_action( us, ACTION_SOFT_RESET, NULL, 0 );
+ if (transferStatus != ISD200_TRANSPORT_GOOD) {
+ US_DEBUGP(" Error issuing Atapi Soft Reset\n");
+ retStatus = ISD200_ERROR;
+ }
+
+ US_DEBUGP("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;
+
+ US_DEBUGP("Entering isd200_SRST\n");
+
+ transferStatus = isd200_action( us, ACTION_RESET, NULL, 0 );
+
+ /* check to see if this request failed */
+ if (transferStatus != ISD200_TRANSPORT_GOOD) {
+ US_DEBUGP(" 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) {
+ US_DEBUGP(" 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);
+ }
+ }
+
+ US_DEBUGP("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) {
+#ifdef CONFIG_USB_STORAGE_DEBUG
+ char* mstr = master_slave == ATA_ADDRESS_DEVHEAD_STD ?
+ "Master" : "Slave";
+#endif
+
+ 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) {
+ US_DEBUGP(" %s status is still BSY, try again...\n",mstr);
+ } else {
+ US_DEBUGP(" %s status !BSY, continue with next operation\n",mstr);
+ 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)) {
+ US_DEBUGP(" 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) {
+ US_DEBUGP(" 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) {
+ US_DEBUGP(" Identified ATAPI device as slave. Rechecking again as master\n");
+ recheckAsMaster = 1;
+ master_slave = ATA_ADDRESS_DEVHEAD_STD;
+ } else {
+ US_DEBUGP(" Identified ATAPI device\n");
+ info->DeviceHead = master_slave;
+
+ status = isd200_atapi_soft_reset(us);
+ break;
+ }
+ } else {
+ US_DEBUGP(" Not ATA, not ATAPI. Weird.\n");
+ break;
+ }
+
+ /* check for timeout on this request */
+ if (time_after_eq(jiffies, endTime)) {
+ if (!detect)
+ US_DEBUGP(" BSY check timeout, just continue with next operation...\n");
+ else
+ US_DEBUGP(" 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;
+
+ US_DEBUGP("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)) {
+ US_DEBUGP(" Setting Master/Slave selection to %d\n", isslave);
+ info->ConfigData.ATAConfig &= 0x3f;
+ info->ConfigData.ATAConfig |= (isslave<<6);
+ retStatus = isd200_write_config(us);
+ }
+ }
+
+ US_DEBUGP("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(u16 *id)
+{
+ US_DEBUGP(" Identify Data Structure:\n");
+ US_DEBUGP(" config = 0x%x\n", id[ATA_ID_CONFIG]);
+ US_DEBUGP(" cyls = 0x%x\n", id[ATA_ID_CYLS]);
+ US_DEBUGP(" heads = 0x%x\n", id[ATA_ID_HEADS]);
+ US_DEBUGP(" track_bytes = 0x%x\n", id[4]);
+ US_DEBUGP(" sector_bytes = 0x%x\n", id[5]);
+ US_DEBUGP(" sectors = 0x%x\n", id[ATA_ID_SECTORS]);
+ US_DEBUGP(" serial_no[0] = 0x%x\n", *(char *)&id[ATA_ID_SERNO]);
+ US_DEBUGP(" buf_type = 0x%x\n", id[20]);
+ US_DEBUGP(" buf_size = 0x%x\n", id[ATA_ID_BUF_SIZE]);
+ US_DEBUGP(" ecc_bytes = 0x%x\n", id[22]);
+ US_DEBUGP(" fw_rev[0] = 0x%x\n", *(char *)&id[ATA_ID_FW_REV]);
+ US_DEBUGP(" model[0] = 0x%x\n", *(char *)&id[ATA_ID_PROD]);
+ US_DEBUGP(" max_multsect = 0x%x\n", id[ATA_ID_MAX_MULTSECT] & 0xff);
+ US_DEBUGP(" dword_io = 0x%x\n", id[ATA_ID_DWORD_IO]);
+ US_DEBUGP(" capability = 0x%x\n", id[ATA_ID_CAPABILITY] >> 8);
+ US_DEBUGP(" tPIO = 0x%x\n", id[ATA_ID_OLD_PIO_MODES] >> 8);
+ US_DEBUGP(" tDMA = 0x%x\n", id[ATA_ID_OLD_DMA_MODES] >> 8);
+ US_DEBUGP(" field_valid = 0x%x\n", id[ATA_ID_FIELD_VALID]);
+ US_DEBUGP(" cur_cyls = 0x%x\n", id[ATA_ID_CUR_CYLS]);
+ US_DEBUGP(" cur_heads = 0x%x\n", id[ATA_ID_CUR_HEADS]);
+ US_DEBUGP(" cur_sectors = 0x%x\n", id[ATA_ID_CUR_SECTORS]);
+ US_DEBUGP(" cur_capacity = 0x%x\n", ata_id_u32(id, 57));
+ US_DEBUGP(" multsect = 0x%x\n", id[ATA_ID_MULTSECT] & 0xff);
+ US_DEBUGP(" lba_capacity = 0x%x\n", ata_id_u32(id, ATA_ID_LBA_CAPACITY));
+ US_DEBUGP(" command_set_1 = 0x%x\n", id[ATA_ID_COMMAND_SET_1]);
+ US_DEBUGP(" 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 = ISD200_GOOD;
+ u16 *id = info->id;
+
+ US_DEBUGP("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 */
+ US_DEBUGP(" 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(id);
+
+ 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) {
+ US_DEBUGP(" 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;
+
+ US_DEBUGP("Protocol changed to: %s\n", us->protocol_name);
+
+ /* Free driver structure */
+ us->extra_destructor(info);
+ kfree(info);
+ us->extra = NULL;
+ us->extra_destructor = NULL;
+ }
+ }
+
+ US_DEBUGP("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:
+ US_DEBUGP(" 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:
+ US_DEBUGP(" 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 {
+ US_DEBUGP(" Media Status not supported, just report okay\n");
+ srb->result = SAM_STAT_GOOD;
+ sendToTransport = 0;
+ }
+ break;
+
+ case TEST_UNIT_READY:
+ US_DEBUGP(" 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 {
+ US_DEBUGP(" 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;
+
+ US_DEBUGP(" 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:
+ US_DEBUGP(" 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:
+ US_DEBUGP(" 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:
+ US_DEBUGP(" ATA OUT - SCSIOP_MEDIUM_REMOVAL\n");
+
+ if (info->DeviceFlags & DF_REMOVABLE_MEDIA) {
+ US_DEBUGP(" 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 {
+ US_DEBUGP(" Not removeable media, just report okay\n");
+ srb->result = SAM_STAT_GOOD;
+ sendToTransport = 0;
+ }
+ break;
+
+ case START_STOP:
+ US_DEBUGP(" ATA OUT - SCSIOP_START_STOP_UNIT\n");
+ US_DEBUGP(" srb->cmnd[4] = 0x%X\n", srb->cmnd[4]);
+
+ if ((srb->cmnd[4] & 0x3) == 0x2) {
+ US_DEBUGP(" 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) {
+ US_DEBUGP(" 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 {
+ US_DEBUGP(" Nothing to do, just report okay\n");
+ srb->result = SAM_STAT_GOOD;
+ sendToTransport = 0;
+ }
+ break;
+
+ default:
+ US_DEBUGP("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:
+ * ISD status code
+ */
+static int isd200_init_info(struct us_data *us)
+{
+ int retStatus = ISD200_GOOD;
+ struct isd200_info *info;
+
+ info = kzalloc(sizeof(struct isd200_info), GFP_KERNEL);
+ if (!info)
+ retStatus = ISD200_ERROR;
+ else {
+ info->id = kzalloc(ATA_ID_WORDS * 2, GFP_KERNEL);
+ info->RegsBuf = (unsigned char *)
+ 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);
+ retStatus = ISD200_ERROR;
+ }
+ }
+
+ if (retStatus == ISD200_GOOD) {
+ us->extra = info;
+ us->extra_destructor = isd200_free_info_ptrs;
+ } else
+ US_DEBUGP("ERROR - kmalloc failure\n");
+
+ return retStatus;
+}
+
+/**************************************************************************
+ * Initialization for the ISD200
+ */
+
+static int isd200_Initialization(struct us_data *us)
+{
+ US_DEBUGP("ISD200 Initialization...\n");
+
+ /* Initialize ISD200 info struct */
+
+ if (isd200_init_info(us) == ISD200_ERROR) {
+ US_DEBUGP("ERROR Initializing ISD200 Info struct\n");
+ } else {
+ /* Get device specific data */
+
+ if (isd200_get_inquiry_data(us) != ISD200_GOOD)
+ US_DEBUGP("ISD200 Initialization Failure\n");
+ else
+ US_DEBUGP("ISD200 Initialization complete\n");
+ }
+
+ return 0;
+}
+
+
+/**************************************************************************
+ * 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 = 1, orig_bufflen;
+ union ata_cdb ataCdb;
+
+ /* Make sure driver was initialized */
+
+ if (us->extra == NULL)
+ US_DEBUGP("ERROR Driver not initialized\n");
+
+ 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 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);
+ 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 = "ums-isd200",
+ .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_driver(isd200_driver);
diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c
new file mode 100644
index 00000000..e3b97383
--- /dev/null
+++ b/drivers/usb/storage/jumpshot.c
@@ -0,0 +1,683 @@
+/* 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 program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+ /*
+ * 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"
+
+
+MODULE_DESCRIPTION("Driver for Lexar \"Jumpshot\" Compact Flash reader");
+MODULE_AUTHOR("Jimmie Mayfield ");
+MODULE_LICENSE("GPL");
+
+/*
+ * 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)|(USB_US_TYPE_STOR<<24) }
+
+static 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 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;
+
+ US_DEBUGP("jumpshot_bulk_read: 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;
+
+ US_DEBUGP("jumpshot_bulk_write: 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) {
+ US_DEBUGP("jumpshot_get_status: 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;
+
+ US_DEBUGP("jumpshot_read_data: %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)
+ US_DEBUGP("jumpshot_write_data: 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) {
+ US_DEBUGP("jumpshot_id_device: 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 unsigned char rw_err_page[12] = {
+ 0x1, 0xA, 0x21, 1, 0, 0, 0, 0, 1, 0, 0, 0
+ };
+ static unsigned char cache_page[12] = {
+ 0x8, 0xA, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ static unsigned char rbac_page[12] = {
+ 0x1B, 0xA, 0, 0x81, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ static 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:
+ US_DEBUGP("jumpshot_handle_mode_sense: Current values\n");
+ break;
+ case 0x1:
+ US_DEBUGP("jumpshot_handle_mode_sense: Changeable values\n");
+ break;
+ case 0x2:
+ US_DEBUGP("jumpshot_handle_mode_sense: Default values\n");
+ break;
+ case 0x3:
+ US_DEBUGP("jumpshot_handle_mode_sense: 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 unsigned char inquiry_response[8] = {
+ 0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
+ };
+
+ if (!us->extra) {
+ us->extra = kzalloc(sizeof(struct jumpshot_info), GFP_NOIO);
+ if (!us->extra) {
+ US_DEBUGP("jumpshot_transport: Gah! Can't allocate storage for jumpshot info struct!\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+ us->extra_destructor = jumpshot_info_destructor;
+ }
+
+ info = (struct jumpshot_info *) (us->extra);
+
+ if (srb->cmnd[0] == INQUIRY) {
+ US_DEBUGP("jumpshot_transport: 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;
+
+ US_DEBUGP("jumpshot_transport: 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) {
+ US_DEBUGP("jumpshot_transport: 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]));
+
+ US_DEBUGP("jumpshot_transport: 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]));
+
+ US_DEBUGP("jumpshot_transport: 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]));
+
+ US_DEBUGP("jumpshot_transport: 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]));
+
+ US_DEBUGP("jumpshot_transport: 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) {
+ US_DEBUGP("jumpshot_transport: TEST_UNIT_READY.\n");
+ return jumpshot_get_status(us);
+ }
+
+ if (srb->cmnd[0] == REQUEST_SENSE) {
+ US_DEBUGP("jumpshot_transport: 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) {
+ US_DEBUGP("jumpshot_transport: MODE_SENSE_6 detected\n");
+ return jumpshot_handle_mode_sense(us, srb, 1);
+ }
+
+ if (srb->cmnd[0] == MODE_SENSE_10) {
+ US_DEBUGP("jumpshot_transport: 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 */
+ US_DEBUGP("jumpshot_transport: 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;
+ }
+
+ US_DEBUGP("jumpshot_transport: 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 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);
+ 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 = "ums-jumpshot",
+ .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_driver(jumpshot_driver);
diff --git a/drivers/usb/storage/karma.c b/drivers/usb/storage/karma.c
new file mode 100644
index 00000000..a8708eae
--- /dev/null
+++ b/drivers/usb/storage/karma.c
@@ -0,0 +1,236 @@
+/* Driver for Rio Karma
+ *
+ * (c) 2006 Bob Copeland
+ * (c) 2006 Keith Bennett
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include "usb.h"
+#include "transport.h"
+#include "debug.h"
+
+MODULE_DESCRIPTION("Driver for Rio Karma");
+MODULE_AUTHOR("Bob Copeland , Keith Bennett ");
+MODULE_LICENSE("GPL");
+
+#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)|(USB_US_TYPE_STOR<<24) }
+
+static 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 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, partial;
+ unsigned long timeout;
+ static unsigned char seq = 1;
+ struct karma_data *data = (struct karma_data *) us->extra;
+
+ US_DEBUGP("karma: 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, &partial);
+ if (result != USB_STOR_XFER_GOOD)
+ goto err;
+
+ result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
+ data->recv, RIO_RECV_LEN, &partial);
+ 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;
+
+ US_DEBUGP("karma: sent command %04x\n", cmd);
+ return 0;
+err:
+ US_DEBUGP("karma: 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)
+{
+ int ret = 0;
+ struct karma_data *data = kzalloc(sizeof(struct karma_data), GFP_NOIO);
+ if (!data)
+ goto out;
+
+ data->recv = kmalloc(RIO_RECV_LEN, GFP_NOIO);
+ if (!data->recv) {
+ kfree(data);
+ goto out;
+ }
+
+ us->extra = data;
+ us->extra_destructor = rio_karma_destructor;
+ ret = rio_karma_send_command(RIO_ENTER_STORAGE, us);
+ data->in_storage = (ret == 0);
+out:
+ return ret;
+}
+
+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);
+ 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 = "ums-karma",
+ .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_driver(karma_driver);
diff --git a/drivers/usb/storage/libusual.c b/drivers/usb/storage/libusual.c
new file mode 100644
index 00000000..fe3ffe14
--- /dev/null
+++ b/drivers/usb/storage/libusual.c
@@ -0,0 +1,243 @@
+/*
+ * libusual
+ *
+ * The libusual contains the table of devices common for ub and usb-storage.
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/*
+ */
+#define USU_MOD_FL_THREAD 1 /* Thread is running */
+#define USU_MOD_FL_PRESENT 2 /* The module is loaded */
+
+struct mod_status {
+ unsigned long fls;
+};
+
+static struct mod_status stat[3];
+static DEFINE_SPINLOCK(usu_lock);
+
+/*
+ */
+#define USB_US_DEFAULT_BIAS USB_US_TYPE_STOR
+static atomic_t usu_bias = ATOMIC_INIT(USB_US_DEFAULT_BIAS);
+
+#define BIAS_NAME_SIZE (sizeof("usb-storage"))
+static const char *bias_names[3] = { "none", "usb-storage", "ub" };
+
+static DEFINE_MUTEX(usu_probe_mutex);
+static DECLARE_COMPLETION(usu_end_notify);
+static atomic_t total_threads = ATOMIC_INIT(0);
+
+static int usu_probe_thread(void *arg);
+
+/*
+ * @type: the module type as an integer
+ */
+void usb_usual_set_present(int type)
+{
+ struct mod_status *st;
+ unsigned long flags;
+
+ if (type <= 0 || type >= 3)
+ return;
+ st = &stat[type];
+ spin_lock_irqsave(&usu_lock, flags);
+ st->fls |= USU_MOD_FL_PRESENT;
+ spin_unlock_irqrestore(&usu_lock, flags);
+}
+EXPORT_SYMBOL_GPL(usb_usual_set_present);
+
+void usb_usual_clear_present(int type)
+{
+ struct mod_status *st;
+ unsigned long flags;
+
+ if (type <= 0 || type >= 3)
+ return;
+ st = &stat[type];
+ spin_lock_irqsave(&usu_lock, flags);
+ st->fls &= ~USU_MOD_FL_PRESENT;
+ spin_unlock_irqrestore(&usu_lock, flags);
+}
+EXPORT_SYMBOL_GPL(usb_usual_clear_present);
+
+/*
+ * Match the calling driver type against the table.
+ * Returns: 0 if the device matches.
+ */
+int usb_usual_check_type(const struct usb_device_id *id, int caller_type)
+{
+ int id_type = USB_US_TYPE(id->driver_info);
+
+ if (caller_type <= 0 || caller_type >= 3)
+ return -EINVAL;
+
+ /* Drivers grab fixed assignment devices */
+ if (id_type == caller_type)
+ return 0;
+ /* Drivers grab devices biased to them */
+ if (id_type == USB_US_TYPE_NONE && caller_type == atomic_read(&usu_bias))
+ return 0;
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(usb_usual_check_type);
+
+/*
+ */
+static int usu_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int rc;
+ unsigned long type;
+ struct task_struct* task;
+ unsigned long flags;
+
+ type = USB_US_TYPE(id->driver_info);
+ if (type == 0)
+ type = atomic_read(&usu_bias);
+
+ spin_lock_irqsave(&usu_lock, flags);
+ if ((stat[type].fls & (USU_MOD_FL_THREAD|USU_MOD_FL_PRESENT)) != 0) {
+ spin_unlock_irqrestore(&usu_lock, flags);
+ return -ENXIO;
+ }
+ stat[type].fls |= USU_MOD_FL_THREAD;
+ spin_unlock_irqrestore(&usu_lock, flags);
+
+ task = kthread_run(usu_probe_thread, (void*)type, "libusual_%ld", type);
+ if (IS_ERR(task)) {
+ rc = PTR_ERR(task);
+ printk(KERN_WARNING "libusual: "
+ "Unable to start the thread for %s: %d\n",
+ bias_names[type], rc);
+ spin_lock_irqsave(&usu_lock, flags);
+ stat[type].fls &= ~USU_MOD_FL_THREAD;
+ spin_unlock_irqrestore(&usu_lock, flags);
+ return rc; /* Not being -ENXIO causes a message printed */
+ }
+ atomic_inc(&total_threads);
+
+ return -ENXIO;
+}
+
+static void usu_disconnect(struct usb_interface *intf)
+{
+ ; /* We should not be here. */
+}
+
+static struct usb_driver usu_driver = {
+ .name = "libusual",
+ .probe = usu_probe,
+ .disconnect = usu_disconnect,
+ .id_table = usb_storage_usb_ids,
+};
+
+/*
+ * A whole new thread for a purpose of request_module seems quite stupid.
+ * The request_module forks once inside again. However, if we attempt
+ * to load a storage module from our own modprobe thread, that module
+ * references our symbols, which cannot be resolved until our module is
+ * initialized. I wish there was a way to wait for the end of initialization.
+ * The module notifier reports MODULE_STATE_COMING only.
+ * So, we wait until module->init ends as the next best thing.
+ */
+static int usu_probe_thread(void *arg)
+{
+ int type = (unsigned long) arg;
+ struct mod_status *st = &stat[type];
+ int rc;
+ unsigned long flags;
+
+ mutex_lock(&usu_probe_mutex);
+ rc = request_module(bias_names[type]);
+ spin_lock_irqsave(&usu_lock, flags);
+ if (rc == 0 && (st->fls & USU_MOD_FL_PRESENT) == 0) {
+ /*
+ * This should not happen, but let us keep tabs on it.
+ */
+ printk(KERN_NOTICE "libusual: "
+ "modprobe for %s succeeded, but module is not present\n",
+ bias_names[type]);
+ }
+ st->fls &= ~USU_MOD_FL_THREAD;
+ spin_unlock_irqrestore(&usu_lock, flags);
+ mutex_unlock(&usu_probe_mutex);
+
+ complete_and_exit(&usu_end_notify, 0);
+}
+
+/*
+ */
+static int __init usb_usual_init(void)
+{
+ int rc;
+
+ mutex_lock(&usu_probe_mutex);
+ rc = usb_register(&usu_driver);
+ mutex_unlock(&usu_probe_mutex);
+ return rc;
+}
+
+static void __exit usb_usual_exit(void)
+{
+ /*
+ * We do not check for any drivers present, because
+ * they keep us pinned with symbol references.
+ */
+
+ usb_deregister(&usu_driver);
+
+ while (atomic_read(&total_threads) > 0) {
+ wait_for_completion(&usu_end_notify);
+ atomic_dec(&total_threads);
+ }
+}
+
+/*
+ * Validate and accept the bias parameter.
+ */
+static int usu_set_bias(const char *bias_s, struct kernel_param *kp)
+{
+ int i;
+ int len;
+ int bias_n = 0;
+
+ len = strlen(bias_s);
+ if (len == 0)
+ return -EDOM;
+ if (bias_s[len-1] == '\n')
+ --len;
+
+ for (i = 1; i < 3; i++) {
+ if (strncmp(bias_s, bias_names[i], len) == 0) {
+ bias_n = i;
+ break;
+ }
+ }
+ if (bias_n == 0)
+ return -EINVAL;
+
+ atomic_set(&usu_bias, bias_n);
+ return 0;
+}
+
+static int usu_get_bias(char *buffer, struct kernel_param *kp)
+{
+ return strlen(strcpy(buffer, bias_names[atomic_read(&usu_bias)]));
+}
+
+module_init(usb_usual_init);
+module_exit(usb_usual_exit);
+
+module_param_call(bias, usu_set_bias, usu_get_bias, NULL, S_IRUGO|S_IWUSR);
+__MODULE_PARM_TYPE(bias, "string");
+MODULE_PARM_DESC(bias, "Bias to usb-storage or ub");
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c
new file mode 100644
index 00000000..886567a3
--- /dev/null
+++ b/drivers/usb/storage/onetouch.c
@@ -0,0 +1,318 @@
+/*
+ * 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)
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "usb.h"
+#include "debug.h"
+
+MODULE_DESCRIPTION("Maxtor USB OneTouch hard drive button driver");
+MODULE_AUTHOR("Nick Sillik ");
+MODULE_LICENSE("GPL");
+
+#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)|(USB_US_TYPE_STOR<<24) }
+
+static 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 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, usb_pipeout(pipe));
+
+ onetouch = kzalloc(sizeof(struct usb_onetouch), GFP_KERNEL);
+ 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)
+ strlcpy(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 > 8 ? 8 : 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 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);
+ if (result)
+ return result;
+
+ /* Use default transport and protocol */
+
+ result = usb_stor_probe2(us);
+ return result;
+}
+
+static struct usb_driver onetouch_driver = {
+ .name = "ums-onetouch",
+ .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_driver(onetouch_driver);
diff --git a/drivers/usb/storage/option_ms.c b/drivers/usb/storage/option_ms.c
new file mode 100644
index 00000000..e0f76bb0
--- /dev/null
+++ b/drivers/usb/storage/option_ms.c
@@ -0,0 +1,170 @@
+/*
+ * Driver for Option High Speed Mobile Devices.
+ *
+ * (c) 2008 Dan Williams
+ *
+ * Inspiration taken from sierra_ms.c by Kevin Lloyd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#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)
+{
+ 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;
+
+ US_DEBUGP("Option MS: %s", "DEVICE MODE SWITCH\n");
+
+ 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)
+{
+ 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;
+
+ US_DEBUGP("Option MS: %s", "device inquiry for vendor name\n");
+
+ 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;
+
+ US_DEBUGP("Option MS: option_ms_init called\n");
+
+ /* Additional test for vendor information via INQUIRY,
+ * because some vendor/product IDs are ambiguous
+ */
+ result = option_inquiry(us);
+ if (result != 0) {
+ US_DEBUGP("Option MS: vendor is not Option or not determinable,"
+ " no action taken\n");
+ return 0;
+ } else
+ US_DEBUGP("Option MS: this is a genuine Option device,"
+ " proceeding\n");
+
+ /* Force Modem mode */
+ if (option_zero_cd == ZCD_FORCE_MODEM) {
+ US_DEBUGP("Option MS: %s", "Forcing Modem Mode\n");
+ result = option_rezero(us);
+ if (result != USB_STOR_XFER_GOOD)
+ US_DEBUGP("Option MS: Failed to switch to modem mode.\n");
+ return -EIO;
+ } else if (option_zero_cd == ZCD_ALLOW_MS) {
+ /* Allow Mass Storage mode (keep CD-Rom) */
+ US_DEBUGP("Option MS: %s", "Allowing Mass Storage Mode if device"
+ " requests it\n");
+ }
+
+ return 0;
+}
+
diff --git a/drivers/usb/storage/option_ms.h b/drivers/usb/storage/option_ms.h
new file mode 100644
index 00000000..b6e448ca
--- /dev/null
+++ b/drivers/usb/storage/option_ms.h
@@ -0,0 +1,4 @@
+#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 00000000..82dd8347
--- /dev/null
+++ b/drivers/usb/storage/protocol.c
@@ -0,0 +1,220 @@
+/* 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.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#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;
+ struct scatterlist *sg = *sgptr;
+
+ /* We have to go through the list one entry
+ * at a time. Each s-g entry contains some number of pages, and
+ * each page has to be kmap()'ed separately. If the page is already
+ * in kernel-addressable memory then kmap() will return its address.
+ * If the page is not directly accessible -- such as a user buffer
+ * located in high memory -- then kmap() will map it to a temporary
+ * position in the kernel's virtual address space.
+ */
+
+ if (!sg)
+ sg = scsi_sglist(srb);
+
+ /* This loop handles a single s-g list entry, which may
+ * include multiple pages. Find the initial page structure
+ * and the starting offset within the page, and update
+ * the *offset and **sgptr values for the next loop.
+ */
+ cnt = 0;
+ while (cnt < buflen && sg) {
+ struct page *page = sg_page(sg) +
+ ((sg->offset + *offset) >> PAGE_SHIFT);
+ unsigned int poff = (sg->offset + *offset) & (PAGE_SIZE-1);
+ unsigned int sglen = sg->length - *offset;
+
+ if (sglen > buflen - cnt) {
+
+ /* Transfer ends within this s-g entry */
+ sglen = buflen - cnt;
+ *offset += sglen;
+ } else {
+
+ /* Transfer continues to next s-g entry */
+ *offset = 0;
+ sg = sg_next(sg);
+ }
+
+ /* Transfer the data for all the pages in this
+ * s-g entry. For each page: call kmap(), do the
+ * transfer, and call kunmap() immediately after. */
+ while (sglen > 0) {
+ unsigned int plen = min(sglen, (unsigned int)
+ PAGE_SIZE - poff);
+ unsigned char *ptr = kmap(page);
+
+ if (dir == TO_XFER_BUF)
+ memcpy(ptr + poff, buffer + cnt, plen);
+ else
+ memcpy(buffer + cnt, ptr + poff, plen);
+ kunmap(page);
+
+ /* Start at the beginning of the next page */
+ poff = 0;
+ ++page;
+ cnt += plen;
+ sglen -= plen;
+ }
+ }
+ *sgptr = sg;
+
+ /* Return the amount actually transferred */
+ 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 00000000..ffc3e2af
--- /dev/null
+++ b/drivers/usb/storage/protocol.h
@@ -0,0 +1,57 @@
+/* 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.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#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 00000000..63cf2822
--- /dev/null
+++ b/drivers/usb/storage/realtek_cr.c
@@ -0,0 +1,1112 @@
+/* Driver for Realtek RTS51xx USB card reader
+ *
+ * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see .
+ *
+ * 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"
+
+MODULE_DESCRIPTION("Driver for Realtek USB Card Reader");
+MODULE_AUTHOR("wwang ");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.03");
+
+static int auto_delink_en = 1;
+module_param(auto_delink_en, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(auto_delink_en, "enable auto delink");
+
+#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;
+#ifdef CONFIG_REALTEK_AUTOPM
+ struct us_data *us;
+ 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 protocal choosen 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)|(USB_US_TYPE_STOR<<24)\
+}
+
+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 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 : 0;
+ 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)) {
+ US_DEBUGP("Signature mismatch: got %08X, expecting %08X\n",
+ le32_to_cpu(bcs->Signature), US_BULK_CS_SIGN);
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ residue = 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)
+ residue = 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 : 0;
+ 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);
+
+ US_DEBUGP("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 USB_STOR_TRANSPORT_ERROR;
+
+ US_DEBUGP("%s, addr = 0x%x, len = %d\n", __func__, 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;
+
+ US_DEBUGP("%s, addr = 0x%x, len = %d\n", __func__, 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;
+
+ US_DEBUGP("%s, lun = %d\n", __func__, 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 < 0)
+ return -EIO;
+
+ US_DEBUGP("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;
+
+ US_DEBUGP("%s, addr = 0xfe47, len = %d\n", __FUNCTION__, 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;
+ }
+
+ US_DEBUGP("In %s,set 0xfe47 to 0x%x\n", __func__, 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;
+
+ US_DEBUGP("%s: <---\n", __func__);
+
+ 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;
+ }
+ }
+
+ US_DEBUGP("%s: --->\n", __func__);
+
+ return 0;
+}
+
+static int config_autodelink_before_power_down(struct us_data *us)
+{
+ struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra);
+ int retval;
+ u8 value;
+
+ US_DEBUGP("%s: <---\n", __func__);
+
+ 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;
+ }
+ }
+
+ US_DEBUGP("%s: --->\n", __func__);
+
+ return 0;
+}
+
+static void fw5895_init(struct us_data *us)
+{
+ struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra);
+ int retval;
+ u8 val;
+
+ US_DEBUGP("%s: <---\n", __func__);
+
+ if ((PRODUCT_ID(chip) != 0x0158) || (FW_VERSION(chip) != 0x5895)) {
+ US_DEBUGP("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)
+ US_DEBUGP("Write memory fail\n");
+ } else {
+ US_DEBUGP("Read memory fail, OR (val & 0x1F) != 0\n");
+ }
+ }
+
+ US_DEBUGP("%s: --->\n", __func__);
+}
+
+#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];
+
+ US_DEBUGP("%s: <---\n", __func__);
+
+ if ((PRODUCT_ID(chip) != 0x0158) || (FW_VERSION(chip) != 0x5895)) {
+ US_DEBUGP("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)
+ US_DEBUGP("Write memory fail\n");
+ } else {
+ US_DEBUGP("Read memory fail\n");
+ }
+ } else {
+ US_DEBUGP("Read memory fail, OR (buf[0]&0x24)!=0x24\n");
+ }
+ }
+
+ US_DEBUGP("%s: --->\n", __func__);
+}
+
+static void rts51x_modi_suspend_timer(struct rts51x_chip *chip)
+{
+ US_DEBUGP("%s: <---, state:%d\n", __func__, rts51x_get_stat(chip));
+
+ chip->timer_expires = jiffies + msecs_to_jiffies(1000*ss_delay);
+ mod_timer(&chip->rts51x_suspend_timer, chip->timer_expires);
+
+ US_DEBUGP("%s: --->\n", __func__);
+}
+
+static void rts51x_suspend_timer_fn(unsigned long data)
+{
+ struct rts51x_chip *chip = (struct rts51x_chip *)data;
+ struct us_data *us = chip->us;
+
+ US_DEBUGP("%s: <---\n", __func__);
+
+ 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:
+ US_DEBUGP("%s: RTS51X_STAT_SS, intf->pm_usage_cnt:%d,"
+ "power.usage:%d\n", __func__,
+ atomic_read(&us->pusb_intf->pm_usage_cnt),
+ atomic_read(&us->pusb_intf->dev.power.usage_count));
+
+ if (atomic_read(&us->pusb_intf->pm_usage_cnt) > 0) {
+ US_DEBUGP("%s: Ready to enter SS state.\n",
+ __func__);
+ 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);
+ US_DEBUGP("%s: RTS51X_STAT_SS 01,"
+ "intf->pm_usage_cnt:%d, power.usage:%d\n",
+ __func__,
+ atomic_read(&us->pusb_intf->pm_usage_cnt),
+ atomic_read(
+ &us->pusb_intf->dev.power.usage_count));
+ }
+ break;
+ default:
+ US_DEBUGP("%s: Unknonwn state !!!\n", __func__);
+ break;
+ }
+
+ US_DEBUGP("%s: --->\n", __func__);
+}
+
+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 u8 media_not_present[] = { 0x70, 0, 0x02, 0, 0, 0, 0,
+ 10, 0, 0, 0, 0, 0x3A, 0, 0, 0, 0, 0
+ };
+ static u8 invalid_cmd_field[] = { 0x70, 0, 0x05, 0, 0, 0, 0,
+ 10, 0, 0, 0, 0, 0x24, 0, 0, 0, 0, 0
+ };
+ int ret;
+
+ US_DEBUGP("%s: <---\n", __func__);
+
+ if (working_scsi(srb)) {
+ US_DEBUGP("%s: working scsi, intf->pm_usage_cnt:%d,"
+ "power.usage:%d\n", __func__,
+ atomic_read(&us->pusb_intf->pm_usage_cnt),
+ atomic_read(&us->pusb_intf->dev.power.usage_count));
+
+ if (atomic_read(&us->pusb_intf->pm_usage_cnt) <= 0) {
+ ret = usb_autopm_get_interface(us->pusb_intf);
+ US_DEBUGP("%s: working scsi, ret=%d\n", __func__, 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) {
+ US_DEBUGP("%s: NOT working scsi\n", __func__);
+ 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);
+ }
+ US_DEBUGP("%s: TEST_UNIT_READY--->\n",
+ __func__);
+ 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;
+ }
+ US_DEBUGP("%s: ALLOW_MEDIUM_REMOVAL--->\n",
+ __func__);
+ goto out;
+ }
+ } else {
+ US_DEBUGP("%s: NOT working scsi, not SS\n", __func__);
+ chip->proto_handler_backup(srb, us);
+ /* Check wether 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:
+ US_DEBUGP("%s: state:%d\n", __func__, rts51x_get_stat(chip));
+ if (rts51x_get_stat(chip) == RTS51X_STAT_RUN)
+ rts51x_modi_suspend_timer(chip);
+
+ US_DEBUGP("%s: --->\n", __func__);
+}
+
+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) {
+ US_DEBUGP("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;
+ setup_timer(&chip->rts51x_suspend_timer, rts51x_suspend_timer_fn,
+ (unsigned long)chip);
+ fw5895_init(us);
+
+ /* enable autosuspend funciton of the usb device */
+ usb_enable_autosuspend(us->pusb_dev);
+
+ return 0;
+}
+#endif
+
+static void realtek_cr_destructor(void *extra)
+{
+ struct rts51x_chip *chip = (struct rts51x_chip *)extra;
+
+ US_DEBUGP("%s: <---\n", __func__);
+
+ if (!chip)
+ return;
+#ifdef CONFIG_REALTEK_AUTOPM
+ if (ss_en) {
+ del_timer(&chip->rts51x_suspend_timer);
+ chip->timer_expires = 0;
+ }
+#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);
+
+ US_DEBUGP("%s: <---\n", __func__);
+
+ /* wait until no command is running */
+ mutex_lock(&us->dev_mutex);
+
+ config_autodelink_before_power_down(us);
+
+ mutex_unlock(&us->dev_mutex);
+
+ US_DEBUGP("%s: --->\n", __func__);
+
+ return 0;
+}
+
+static int realtek_cr_resume(struct usb_interface *iface)
+{
+ struct us_data *us = usb_get_intfdata(iface);
+
+ US_DEBUGP("%s: <---\n", __func__);
+
+ fw5895_init(us);
+ config_autodelink_after_power_on(us);
+
+ US_DEBUGP("%s: --->\n", __func__);
+
+ 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(sizeof(struct rts51x_chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ us->extra = chip;
+ us->extra_destructor = realtek_cr_destructor;
+ us->max_lun = chip->max_lun = rts51x_get_max_lun(us);
+
+ US_DEBUGP("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_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) {
+ chip->us = us;
+ realtek_cr_autosuspend_setup(us);
+ }
+#endif
+
+ US_DEBUGP("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 int realtek_cr_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct us_data *us;
+ int result;
+
+ US_DEBUGP("Probe Realtek Card Reader!\n");
+
+ result = usb_stor_probe1(&us, intf, id,
+ (id - realtek_cr_ids) +
+ realtek_cr_unusual_dev_list);
+ if (result)
+ return result;
+
+ result = usb_stor_probe2(us);
+
+ return result;
+}
+
+static struct usb_driver realtek_cr_driver = {
+ .name = "ums-realtek",
+ .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_driver(realtek_cr_driver);
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
new file mode 100644
index 00000000..11418da9
--- /dev/null
+++ b/drivers/usb/storage/scsiglue.c
@@ -0,0 +1,595 @@
+/* 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.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#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 slave_alloc (struct scsi_device *sdev)
+{
+ /*
+ * 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;
+
+ /* USB has unusual DMA-alignment requirements: Although the
+ * starting address of each scatter-gather element doesn't matter,
+ * the length of each element except the last must be divisible
+ * by the Bulk maxpacket value. There's currently no way to
+ * express this by block-layer constraints, so we'll cop out
+ * and simply require addresses to be aligned at 512-byte
+ * boundaries. This is okay since most block I/O involves
+ * hardware sectors that are multiples of 512 bytes in length,
+ * and since host controllers up through USB 2.0 have maxpacket
+ * values no larger than 512.
+ *
+ * But it doesn't suffice for Wireless USB, where Bulk maxpacket
+ * values can be as large as 2048. To make that work properly
+ * will require changes to the block layer.
+ */
+ blk_queue_update_dma_alignment(sdev->request_queue, (512 - 1));
+
+ return 0;
+}
+
+static int slave_configure(struct scsi_device *sdev)
+{
+ struct us_data *us = host_to_us(sdev->host);
+
+ /* 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_CACHE_SIZE >> 9;
+ if (queue_max_hw_sectors(sdev->request_queue) > max_sectors)
+ blk_queue_max_hw_sectors(sdev->request_queue,
+ 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.
+ */
+ blk_queue_max_hw_sectors(sdev->request_queue, 0x7FFFFF);
+ }
+
+ /* Some USB host controllers can't do DMA; they have to use PIO.
+ * They indicate this by setting their dma_mask to NULL. For
+ * such controllers we need to make sure the block layer sets
+ * up bounce buffers in addressable memory.
+ */
+ if (!us->pusb_dev->bus->controller->dma_mask)
+ blk_queue_bounce_limit(sdev->request_queue, BLK_BOUNCE_HIGH);
+
+ /* We can't put these settings in slave_alloc() 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 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 */
+ sdev->skip_vpd_pages = 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.
+ */
+ sdev->try_rc_10_first = 1;
+
+ /* assume SPC3 or latter devices support sense size > 18 */
+ if (sdev->scsi_level > SCSI_SPC_2)
+ 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 negible 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;
+ } else {
+
+ /* Non-disk-type devices don't need to blacklist 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 int queuecommand_lck(struct scsi_cmnd *srb,
+ void (*done)(struct scsi_cmnd *))
+{
+ struct us_data *us = host_to_us(srb->device->host);
+
+ US_DEBUGP("%s called\n", __func__);
+
+ /* check for state-transition errors */
+ if (us->srb != NULL) {
+ printk(KERN_ERR USB_STORAGE "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)) {
+ US_DEBUGP("Fail command during disconnect\n");
+ srb->result = DID_NO_CONNECT << 16;
+ done(srb);
+ return 0;
+ }
+
+ /* enqueue the command and wake up the control thread */
+ srb->scsi_done = done;
+ 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(struct scsi_cmnd *srb)
+{
+ struct us_data *us = host_to_us(srb->device->host);
+
+ US_DEBUGP("%s called\n", __func__);
+
+ /* us->srb together with the TIMED_OUT, RESETTING, and ABORTING
+ * bits are protected by the host lock. */
+ scsi_lock(us_to_host(us));
+
+ /* Is this command still active? */
+ if (us->srb != srb) {
+ scsi_unlock(us_to_host(us));
+ US_DEBUGP ("-- nothing to abort\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;
+}
+
+/* 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;
+
+ US_DEBUGP("%s called\n", __func__);
+
+ /* 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;
+
+ US_DEBUGP("%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
+ ***********************************************************************/
+
+/* we use this macro to help us write into the buffer */
+#undef SPRINTF
+#define SPRINTF(args...) \
+ do { if (pos < buffer+length) pos += sprintf(pos, ## args); } while (0)
+
+static int proc_info (struct Scsi_Host *host, char *buffer,
+ char **start, off_t offset, int length, int inout)
+{
+ struct us_data *us = host_to_us(host);
+ char *pos = buffer;
+ const char *string;
+
+ /* if someone is sending us data, just throw it away */
+ if (inout)
+ return length;
+
+ /* print the controller name */
+ SPRINTF(" 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";
+ SPRINTF(" 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";
+ SPRINTF(" Product: %s\n", string);
+ if (us->pusb_dev->serial)
+ string = us->pusb_dev->serial;
+ else
+ string = "None";
+ SPRINTF("Serial Number: %s\n", string);
+
+ /* show the protocol and transport */
+ SPRINTF(" Protocol: %s\n", us->protocol_name);
+ SPRINTF(" Transport: %s\n", us->transport_name);
+
+ /* show the device flags */
+ if (pos < buffer + length) {
+ pos += sprintf(pos, " Quirks:");
+
+#define US_FLAG(name, value) \
+ if (us->fflags & value) pos += sprintf(pos, " " #name);
+US_DO_ALL_FLAGS
+#undef US_FLAG
+
+ *(pos++) = '\n';
+ }
+
+ /*
+ * Calculate start of next buffer, and return value.
+ */
+ *start = buffer + offset;
+
+ if ((pos - buffer) < offset)
+ return (0);
+ else if ((pos - buffer - offset) < length)
+ return (pos - buffer - offset);
+ else
+ return (length);
+}
+
+/***********************************************************************
+ * Sysfs interface
+ ***********************************************************************/
+
+/* Output routine for the sysfs max_sectors file */
+static ssize_t show_max_sectors(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 store_max_sectors(struct device *dev, struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ unsigned short ms;
+
+ if (sscanf(buf, "%hu", &ms) > 0) {
+ blk_queue_max_hw_sectors(sdev->request_queue, ms);
+ return count;
+ }
+ return -EINVAL;
+}
+
+static DEVICE_ATTR(max_sectors, S_IRUGO | S_IWUSR, show_max_sectors,
+ store_max_sectors);
+
+static struct device_attribute *sysfs_device_attr_list[] = {
+ &dev_attr_max_sectors,
+ NULL,
+ };
+
+/*
+ * this defines our host template, with which we'll allocate hosts
+ */
+
+struct scsi_host_template usb_stor_host_template = {
+ /* basic userland interface stuff */
+ .name = "usb-storage",
+ .proc_name = "usb-storage",
+ .proc_info = proc_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,
+ .cmd_per_lun = 1,
+
+ /* unknown initiator id */
+ .this_id = -1,
+
+ .slave_alloc = slave_alloc,
+ .slave_configure = slave_configure,
+ .target_alloc = target_alloc,
+
+ /* lots of sg segments can be handled */
+ .sg_tablesize = SCSI_MAX_SG_CHAIN_SEGMENTS,
+
+ /* limit the total size of a transfer to 120 KB */
+ .max_sectors = 240,
+
+ /* merge commands... this seems to help performance, but
+ * periodically someone should test to see which setting is more
+ * optimal.
+ */
+ .use_clustering = 1,
+
+ /* emulated HBA */
+ .emulated = 1,
+
+ /* we do our own delay after a device or bus reset */
+ .skip_settle_delay = 1,
+
+ /* sysfs device attributes */
+ .sdev_attrs = sysfs_device_attr_list,
+
+ /* module management */
+ .module = THIS_MODULE
+};
+
+/* 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 00000000..ffa1cca9
--- /dev/null
+++ b/drivers/usb/storage/scsiglue.h
@@ -0,0 +1,48 @@
+/* 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.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#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 unsigned char usb_stor_sense_invalidCDB[18];
+extern struct scsi_host_template usb_stor_host_template;
+
+#endif
diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c
new file mode 100644
index 00000000..3252a62b
--- /dev/null
+++ b/drivers/usb/storage/sddr09.c
@@ -0,0 +1,1793 @@
+/* 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.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * 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"
+
+MODULE_DESCRIPTION("Driver for SanDisk SDDR-09 SmartMedia reader");
+MODULE_AUTHOR("Andries Brouwer , Robert Baruch ");
+MODULE_LICENSE("GPL");
+
+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)|(USB_US_TYPE_STOR<<24) }
+
+static 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 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)
+
+/* #define US_DEBUGP printk */
+
+/*
+ * 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);
+
+ US_DEBUGP("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) {
+ US_DEBUGP("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) {
+ US_DEBUGP("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);
+ US_DEBUGP("sddr09_read22: 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;
+
+ US_DEBUGP("sddr09_erase: 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)
+ US_DEBUGP("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) {
+ US_DEBUGP("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) {
+ US_DEBUGP("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) {
+ US_DEBUGP("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) {
+ US_DEBUGP("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;
+
+ US_DEBUGP("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(sectors, (unsigned int) info->blocksize) * info->pagesize;
+ buffer = kmalloc(len, GFP_NOIO);
+ if (buffer == NULL) {
+ printk(KERN_WARNING "sddr09_read_data: Out of memory\n");
+ 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) {
+ US_DEBUGP("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 */
+
+ US_DEBUGP("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 {
+ US_DEBUGP("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, isnew;
+
+ lbap = ((lba % 1000) << 1) | 0x1000;
+ if (parity[MSB_of(lbap) ^ LSB_of(lbap)])
+ lbap ^= 1;
+ pba = info->lba_to_pba[lba];
+ isnew = 0;
+
+ 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;
+ isnew = 1;
+ }
+
+ 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)) {
+ US_DEBUGP("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)) {
+ US_DEBUGP("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);
+ }
+
+ US_DEBUGP("Rewrite PBA %d (LBA %d)\n", pba, lba);
+
+ result = sddr09_write_inplace(us, address>>1, info->blocksize,
+ info->pageshift, blockbuffer, 0);
+
+ US_DEBUGP("sddr09_write_inplace returns %d\n", result);
+
+#if 0
+ {
+ unsigned char status = 0;
+ int result2 = sddr09_read_status(us, &status);
+ if (result2)
+ US_DEBUGP("sddr09_write_inplace: cannot read status\n");
+ else if (status != 0xc0)
+ US_DEBUGP("sddr09_write_inplace: 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) {
+ printk(KERN_WARNING "sddr09_write_data: Out of memory\n");
+ 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(sectors, (unsigned int) info->blocksize) * info->pagesize;
+ buffer = kmalloc(len, GFP_NOIO);
+ if (buffer == NULL) {
+ printk(KERN_WARNING "sddr09_write_data: Out of memory\n");
+ 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) {
+ US_DEBUGP("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) {
+
+ US_DEBUGP("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;
+
+ result = sddr09_read_status(us, &status);
+ if (result) {
+ US_DEBUGP("sddr09_get_wp: read_status fails\n");
+ return result;
+ }
+ US_DEBUGP("sddr09_get_wp: status 0x%02X", status);
+ if ((status & 0x80) == 0) {
+ info->flags |= SDDR09_WP; /* write protected */
+ US_DEBUGP(" WP");
+ }
+ if (status & 0x40)
+ US_DEBUGP(" Ready");
+ if (status & LUNBITS)
+ US_DEBUGP(" Suspended");
+ if (status & 0x1)
+ US_DEBUGP(" Error");
+ US_DEBUGP("\n");
+ 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 struct nand_flash_dev *
+sddr09_get_cardinfo(struct us_data *us, unsigned char flags) {
+ struct nand_flash_dev *cardinfo;
+ unsigned char deviceID[4];
+ char blurbtxt[256];
+ int result;
+
+ US_DEBUGP("Reading capacity...\n");
+
+ result = sddr09_read_deviceID(us, deviceID);
+
+ if (result) {
+ US_DEBUGP("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 = %02X %02X %02X %02X",
+ deviceID[0], deviceID[1], deviceID[2], deviceID[3]);
+
+ /* 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 == NULL) {
+ printk(KERN_WARNING "sddr09_read_map: out of memory\n");
+ 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(numblocks*sizeof(int), GFP_NOIO);
+ info->pba_to_lba = kmalloc(numblocks*sizeof(int), 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;
+ US_DEBUGP("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) {
+ US_DEBUGP("active config #%d != 1 ??\n", us->pusb_dev
+ ->actconfig->desc.bConfigurationValue);
+ return -EINVAL;
+ }
+
+ result = usb_reset_configuration(us->pusb_dev);
+ US_DEBUGP("Result of usb_reset_configuration is %d\n", result);
+ if (result == -EPIPE) {
+ US_DEBUGP("-- stall on control interface\n");
+ } else if (result != 0) {
+ /* it's not a stall, but another error -- time to bail */
+ US_DEBUGP("-- Unknown error. Rejecting device\n");
+ return -EINVAL;
+ }
+
+ us->extra = kzalloc(sizeof(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) {
+ US_DEBUGP("sddr09_init: send_command fails\n");
+ return result;
+ }
+
+ US_DEBUGP("SDDR09init: %02X %02X\n", data[0], data[1]);
+ // get 07 02
+
+ result = sddr09_send_command(us, 0x08, USB_DIR_IN, data, 2);
+ if (result) {
+ US_DEBUGP("sddr09_init: 2nd send_command fails\n");
+ return result;
+ }
+
+ US_DEBUGP("SDDR09init: %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;
+
+ US_DEBUGP("dpcm_transport: LUN=%d\n", 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:
+ US_DEBUGP("dpcm_transport: Invalid LUN %d\n",
+ 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 unsigned char inquiry_response[8] = {
+ 0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00
+ };
+
+ /* note: no block descriptor support */
+ static 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) {
+ 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) {
+ US_DEBUGP("SDDR09: 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]);
+
+ US_DEBUGP("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]);
+
+ US_DEBUGP("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]);
+
+ US_DEBUGP("SDDR09: Send control for command %s\n", ptr);
+
+ result = sddr09_send_scsi_command(us, srb->cmnd, 12);
+ if (result) {
+ US_DEBUGP("sddr09_transport: 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;
+
+ US_DEBUGP("SDDR09: %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 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);
+ 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 = "ums-sddr09",
+ .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_driver(sddr09_driver);
diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c
new file mode 100644
index 00000000..c1440780
--- /dev/null
+++ b/drivers/usb/storage/sddr55.c
@@ -0,0 +1,1012 @@
+/* Driver for SanDisk SDDR-55 SmartMedia reader
+ *
+ * SDDR55 driver v0.1:
+ *
+ * First release
+ *
+ * Current development and maintenance by:
+ * (c) 2002 Simon Munton
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "usb.h"
+#include "transport.h"
+#include "protocol.h"
+#include "debug.h"
+
+MODULE_DESCRIPTION("Driver for SanDisk SDDR-55 SmartMedia reader");
+MODULE_AUTHOR("Simon Munton");
+MODULE_LICENSE("GPL");
+
+/*
+ * 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)|(USB_US_TYPE_STOR<<24) }
+
+static 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 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);
+
+ US_DEBUGP("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 long 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((unsigned int) sectors, (unsigned int) 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((unsigned int) sectors << info->smallpageshift,
+ info->blocksize - page);
+ len = pages << info->pageshift;
+
+ US_DEBUGP("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);
+
+ US_DEBUGP("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 long 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((unsigned int) sectors, (unsigned int) 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((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);
+
+ US_DEBUGP("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;
+
+ US_DEBUGP("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 */
+ US_DEBUGP("Couldn't find unallocated block\n");
+
+ set_sense_info (3, 0x31, 0); /* medium error */
+ result = USB_STOR_TRANSPORT_FAILED;
+ goto leave;
+ }
+
+ US_DEBUGP("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) {
+ US_DEBUGP("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) {
+ US_DEBUGP("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) {
+ US_DEBUGP("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 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;
+ }
+
+ US_DEBUGP("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);
+
+ US_DEBUGP("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 uninitialized_var(manufacturerID);
+ unsigned char uninitialized_var(deviceID);
+ int result;
+ struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra;
+
+ US_DEBUGP("Reading capacity...\n");
+
+ result = sddr55_read_deviceID(us,
+ &manufacturerID,
+ &deviceID);
+
+ US_DEBUGP("Result of read_deviceID is %d\n",
+ result);
+
+ if (result != USB_STOR_XFER_GOOD)
+ return 0;
+
+ US_DEBUGP("Device ID = %02X\n", deviceID);
+ US_DEBUGP("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;
+ 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( 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(numblocks*sizeof(int), GFP_NOIO);
+ info->pba_to_lba = kmalloc(numblocks*sizeof(int), 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))
+ US_DEBUGP("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 unsigned char inquiry_response[8] = {
+ 0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00
+ };
+ // write-protected for now, no block descriptor support
+ static 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(
+ sizeof(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) {
+ US_DEBUGP("SDDR55: 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 ) {
+ US_DEBUGP(
+ "SDDR55: Dummy up request for mode page 1\n");
+ return USB_STOR_TRANSPORT_GOOD;
+
+ } else if ( (srb->cmnd[2] & 0x3F) == 0x3F ) {
+ US_DEBUGP(
+ "SDDR55: 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) {
+
+ US_DEBUGP(
+ "SDDR55: %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) {
+
+ US_DEBUGP("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) {
+ US_DEBUGP("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 {
+ US_DEBUGP("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 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);
+ 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 = "ums-sddr55",
+ .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_driver(sddr55_driver);
diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c
new file mode 100644
index 00000000..fa1ceebc
--- /dev/null
+++ b/drivers/usb/storage/shuttle_usbat.c
@@ -0,0 +1,1869 @@
+/* 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.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "usb.h"
+#include "transport.h"
+#include "protocol.h"
+#include "debug.h"
+
+MODULE_DESCRIPTION("Driver for SCM Microsystems (a.k.a. Shuttle) USB-ATAPI cable");
+MODULE_AUTHOR("Daniel Drake , Robert Baruch ");
+MODULE_LICENSE("GPL");
+
+/* 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)|(USB_US_TYPE_STOR<<24) }
+
+static 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 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;
+
+ US_DEBUGP("usbat_bulk_read: 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;
+
+ US_DEBUGP("usbat_bulk_write: 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);
+
+ US_DEBUGP("usbat_get_status: 0x%02X\n", (unsigned short) (*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 */
+ US_DEBUGP("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 */
+ }
+
+ US_DEBUGP("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;
+
+ US_DEBUGP("Redoing %s\n",
+ direction==DMA_TO_DEVICE ? "write" : "read");
+
+ } else if (result != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+ else
+ return usbat_wait_not_busy(us, minutes);
+
+ }
+
+ US_DEBUGP("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);
+
+ US_DEBUGP("usbat_read_user_io: UIO register reads %02X\n", (unsigned short) (*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(unsigned char *uio)
+{
+ if (*uio & USBAT_UIO_UI0) {
+ US_DEBUGP("usbat_flash_check_media_present: 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(unsigned char *uio)
+{
+ if (*uio & USBAT_UIO_0) {
+ US_DEBUGP("usbat_flash_check_media_changed: 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(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(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 */
+ US_DEBUGP("usbat_identify_device: Detected HP8200 CDRW\n");
+ info->devicetype = USBAT_DEV_HP8200;
+ } else {
+ /* Device is a CompactFlash reader/writer */
+ US_DEBUGP("usbat_identify_device: 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) {
+ US_DEBUGP("usbat_flash_get_sector_count: 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;
+
+ US_DEBUGP("usbat_flash_read_data: %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;
+
+ US_DEBUGP("handle_read10: 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];
+ US_DEBUGP("handle_read10: GPCMD_READ_CD: len %d\n", len);
+ srb->transfersize = scsi_bufflen(srb)/len;
+ }
+
+ if (!srb->transfersize) {
+ srb->transfersize = 2048; /* A guess */
+ US_DEBUGP("handle_read10: 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;
+ US_DEBUGP("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(sizeof(struct usbat_info), GFP_NOIO);
+ if (!us->extra) {
+ US_DEBUGP("init_usbat: Gah! Can't allocate storage for usbat info struct!\n");
+ return 1;
+ }
+ 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 USB_STOR_TRANSPORT_ERROR;
+
+ US_DEBUGP("INIT 1\n");
+
+ msleep(2000);
+
+ rc = usbat_read_user_io(us, status);
+ if (rc != USB_STOR_TRANSPORT_GOOD)
+ return rc;
+
+ US_DEBUGP("INIT 2\n");
+
+ rc = usbat_read_user_io(us, status);
+ if (rc != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ rc = usbat_read_user_io(us, status);
+ if (rc != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ US_DEBUGP("INIT 3\n");
+
+ rc = usbat_select_and_test_registers(us);
+ if (rc != USB_STOR_TRANSPORT_GOOD)
+ return rc;
+
+ US_DEBUGP("INIT 4\n");
+
+ rc = usbat_read_user_io(us, status);
+ if (rc != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ US_DEBUGP("INIT 5\n");
+
+ /* Enable peripheral control signals and card detect */
+ rc = usbat_device_enable_cdt(us);
+ if (rc != USB_STOR_TRANSPORT_GOOD)
+ return rc;
+
+ US_DEBUGP("INIT 6\n");
+
+ rc = usbat_read_user_io(us, status);
+ if (rc != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ US_DEBUGP("INIT 7\n");
+
+ msleep(1400);
+
+ rc = usbat_read_user_io(us, status);
+ if (rc != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ US_DEBUGP("INIT 8\n");
+
+ rc = usbat_select_and_test_registers(us);
+ if (rc != USB_STOR_TRANSPORT_GOOD)
+ return rc;
+
+ US_DEBUGP("INIT 9\n");
+
+ /* At this point, we need to detect which device we are using */
+ if (usbat_set_transport(us, info, devicetype))
+ return USB_STOR_TRANSPORT_ERROR;
+
+ US_DEBUGP("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 USB_STOR_TRANSPORT_ERROR;
+
+ US_DEBUGP("INIT 11\n");
+
+ return USB_STOR_TRANSPORT_GOOD;
+}
+
+/*
+ * 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);
+ US_DEBUGP("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;
+ US_DEBUGP("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) {
+ US_DEBUGP("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 unsigned char inquiry_response[36] = {
+ 0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
+ };
+
+ if (srb->cmnd[0] == INQUIRY) {
+ US_DEBUGP("usbat_flash_transport: 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;
+ US_DEBUGP("usbat_flash_transport: 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) {
+ US_DEBUGP("usbat_flash_transport: 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]));
+
+ US_DEBUGP("usbat_flash_transport: 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]));
+
+ US_DEBUGP("usbat_flash_transport: 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]));
+
+ US_DEBUGP("usbat_flash_transport: 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]));
+
+ US_DEBUGP("usbat_flash_transport: 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) {
+ US_DEBUGP("usbat_flash_transport: 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) {
+ US_DEBUGP("usbat_flash_transport: 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;
+ }
+
+ US_DEBUGP("usbat_flash_transport: 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 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);
+ 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 = 1;
+
+ result = usb_stor_probe2(us);
+ return result;
+}
+
+static struct usb_driver usbat_driver = {
+ .name = "ums-usbat",
+ .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_driver(usbat_driver);
diff --git a/drivers/usb/storage/sierra_ms.c b/drivers/usb/storage/sierra_ms.c
new file mode 100644
index 00000000..37539c89
--- /dev/null
+++ b/drivers/usb/storage/sierra_ms.c
@@ -0,0 +1,207 @@
+#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;
+ US_DEBUGP("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;
+
+ US_DEBUGP("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(struct swoc_info *swocInfo)
+{
+ US_DEBUGP("SWIMS: SWoC Rev: %02d \n", swocInfo->rev);
+ US_DEBUGP("SWIMS: Linux SKU: %04X \n", swocInfo->LinuxSKU);
+ US_DEBUGP("SWIMS: Linux Version: %04X \n", swocInfo->LinuxVer);
+}
+
+
+static ssize_t show_truinst(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 = snprintf(buf, PAGE_SIZE, "Forced Mass Storage\n");
+ } else {
+ swocInfo = kmalloc(sizeof(struct swoc_info), GFP_KERNEL);
+ if (!swocInfo) {
+ US_DEBUGP("SWIMS: Allocation failure\n");
+ snprintf(buf, PAGE_SIZE, "Error\n");
+ return -ENOMEM;
+ }
+ result = sierra_get_swoc_info(udev, swocInfo);
+ if (result < 0) {
+ US_DEBUGP("SWIMS: failed SWoC query\n");
+ kfree(swocInfo);
+ snprintf(buf, PAGE_SIZE, "Error\n");
+ return -EIO;
+ }
+ debug_swoc(swocInfo);
+ result = snprintf(buf, PAGE_SIZE,
+ "REV=%02d SKU=%04X VER=%04X\n",
+ swocInfo->rev,
+ swocInfo->LinuxSKU,
+ swocInfo->LinuxVer);
+ kfree(swocInfo);
+ }
+ return result;
+}
+static DEVICE_ATTR(truinst, S_IRUGO, show_truinst, NULL);
+
+int sierra_ms_init(struct us_data *us)
+{
+ int result, retries;
+ struct swoc_info *swocInfo;
+ struct usb_device *udev;
+ struct Scsi_Host *sh;
+ struct scsi_device *sd;
+
+ retries = 3;
+ result = 0;
+ udev = us->pusb_dev;
+
+ sh = us_to_host(us);
+ sd = scsi_get_host_dev(sh);
+
+ US_DEBUGP("SWIMS: sierra_ms_init called\n");
+
+ /* Force Modem mode */
+ if (swi_tru_install == TRU_FORCE_MODEM) {
+ US_DEBUGP("SWIMS: %s", "Forcing Modem Mode\n");
+ result = sierra_set_ms_mode(udev, SWIMS_SET_MODE_Modem);
+ if (result < 0)
+ US_DEBUGP("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) {
+ US_DEBUGP("SWIMS: %s", "Forcing Mass Storage Mode\n");
+ goto complete;
+ }
+ /* Normal TRU-Install Logic */
+ else {
+ US_DEBUGP("SWIMS: %s", "Normal SWoC Logic\n");
+
+ swocInfo = kmalloc(sizeof(struct swoc_info),
+ GFP_KERNEL);
+ if (!swocInfo) {
+ US_DEBUGP("SWIMS: %s", "Allocation failure\n");
+ return -ENOMEM;
+ }
+
+ retries = 3;
+ do {
+ retries--;
+ result = sierra_get_swoc_info(udev, swocInfo);
+ if (result < 0) {
+ US_DEBUGP("SWIMS: %s", "Failed SWoC query\n");
+ schedule_timeout_uninterruptible(2*HZ);
+ }
+ } while (retries && result < 0);
+
+ if (result < 0) {
+ US_DEBUGP("SWIMS: %s",
+ "Completely failed SWoC query\n");
+ kfree(swocInfo);
+ return -EIO;
+ }
+
+ debug_swoc(swocInfo);
+
+ /* If there is not Linux software on the TRU-Install device
+ * then switch to modem mode
+ */
+ if (!containsFullLinuxPackage(swocInfo)) {
+ US_DEBUGP("SWIMS: %s",
+ "Switching to Modem Mode\n");
+ result = sierra_set_ms_mode(udev,
+ SWIMS_SET_MODE_Modem);
+ if (result < 0)
+ US_DEBUGP("SWIMS: Failed to switch modem\n");
+ kfree(swocInfo);
+ return -EIO;
+ }
+ kfree(swocInfo);
+ }
+complete:
+ result = device_create_file(&us->pusb_intf->dev, &dev_attr_truinst);
+
+ return 0;
+}
+
diff --git a/drivers/usb/storage/sierra_ms.h b/drivers/usb/storage/sierra_ms.h
new file mode 100644
index 00000000..bb48634a
--- /dev/null
+++ b/drivers/usb/storage/sierra_ms.h
@@ -0,0 +1,4 @@
+#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 00000000..c70109e5
--- /dev/null
+++ b/drivers/usb/storage/transport.c
@@ -0,0 +1,1353 @@
+/* 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.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#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)) {
+ US_DEBUGP("-- 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) {
+ US_DEBUGP("%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;
+
+ US_DEBUGP("%s: rq=%02x rqtype=%02x value=%04x index=%02x len=%u\n",
+ __func__, 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);
+
+ US_DEBUGP("%s: result = %d\n", __func__, 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)
+{
+ US_DEBUGP("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) {
+ US_DEBUGP("-- short transfer\n");
+ return USB_STOR_XFER_SHORT;
+ }
+
+ US_DEBUGP("-- 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)) {
+ US_DEBUGP("-- stall on control pipe\n");
+ return USB_STOR_XFER_STALLED;
+ }
+
+ /* for other sorts of endpoint, clear the stall */
+ US_DEBUGP("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:
+ US_DEBUGP("-- babble\n");
+ return USB_STOR_XFER_LONG;
+
+ /* the transfer was cancelled by abort, disconnect, or timeout */
+ case -ECONNRESET:
+ US_DEBUGP("-- transfer cancelled\n");
+ return USB_STOR_XFER_ERROR;
+
+ /* short scatter-gather read transfer */
+ case -EREMOTEIO:
+ US_DEBUGP("-- short read transfer\n");
+ return USB_STOR_XFER_SHORT;
+
+ /* abort or disconnect in progress */
+ case -EIO:
+ US_DEBUGP("-- abort or disconnect in progress\n");
+ return USB_STOR_XFER_ERROR;
+
+ /* the catch-all error case */
+ default:
+ US_DEBUGP("-- 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;
+
+ US_DEBUGP("%s: rq=%02x rqtype=%02x value=%04x index=%02x len=%u\n",
+ __func__, 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;
+
+ US_DEBUGP("%s: xfer %u bytes\n", __func__, length);
+
+ /* calculate the max packet size */
+ maxp = usb_maxpacket(us->pusb_dev, pipe, usb_pipeout(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;
+
+ US_DEBUGP("%s: xfer %u bytes\n", __func__, 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))
+ return USB_STOR_XFER_ERROR;
+
+ /* initialize the scatter-gather request block */
+ US_DEBUGP("%s: xfer %u bytes, %d entries\n", __func__,
+ length, num_sg);
+ result = usb_sg_init(&us->current_sg, us->pusb_dev, pipe, 0,
+ sg, num_sg, length, GFP_NOIO);
+ if (result) {
+ US_DEBUGP("usb_sg_init returned %d\n", result);
+ return 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)) {
+ US_DEBUGP("-- 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);
+}
+
+/*
+ * 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 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 = srb->request->rq_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)) {
+ US_DEBUGP("-- 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) {
+ US_DEBUGP("-- 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) {
+ US_DEBUGP("-- CB transport device requiring auto-sense\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) {
+ US_DEBUGP("-- 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))) {
+ US_DEBUGP("-- 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))) {
+ US_DEBUGP("-- 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:
+ US_DEBUGP("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)) {
+ US_DEBUGP("-- 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) {
+ US_DEBUGP("-- 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) {
+ US_DEBUGP("-- 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) {
+ US_DEBUGP("-- 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.
+ */
+ US_DEBUGP("-- 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);
+
+ US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
+ US_DEBUGP("-- 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(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;
+
+ /* 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)
+{
+ US_DEBUGP("%s called\n", __func__);
+
+ /* 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)) {
+ US_DEBUGP("-- 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)) {
+ US_DEBUGP("-- 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 */
+ result = usb_stor_ctrl_transfer(us, us->send_ctrl_pipe,
+ US_CBI_ADSC,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
+ us->ifnum, srb->cmnd, srb->cmd_len);
+
+ /* check the return code for the command */
+ US_DEBUGP("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);
+ US_DEBUGP("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);
+ US_DEBUGP("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]) {
+ US_DEBUGP("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);
+
+ US_DEBUGP("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];
+
+ /*
+ * 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 : 0;
+ 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 */
+ US_DEBUGP("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);
+ US_DEBUGP("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))
+ udelay(125);
+
+ 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);
+ US_DEBUGP("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
+ * the command, we'll return fake sense data indicating
+ * Illegal Request, Invalid Field in CDB.
+ */
+ if (result == USB_STOR_XFER_LONG)
+ fake_sense = 1;
+ }
+
+ /* 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 */
+ US_DEBUGP("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) {
+ US_DEBUGP("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 */
+ US_DEBUGP("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 */
+ US_DEBUGP("Bulk status result = %d\n", result);
+ if (result != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ /* check bulk status */
+ residue = le32_to_cpu(bcs->Residue);
+ US_DEBUGP("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) {
+ US_DEBUGP("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))
+ US_DEBUGP("Learnt BCS signature 0x%08X\n",
+ le32_to_cpu(us->bcs_signature));
+ } else if (bcs->Signature != us->bcs_signature) {
+ US_DEBUGP("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),
+ (int) 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)) {
+ US_DEBUGP("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) {
+ US_DEBUGP("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)) {
+ US_DEBUGP("Reset interrupted by disconnect\n");
+ return -EIO;
+ }
+
+ US_DEBUGP("Soft reset: clearing bulk-in endpoint halt\n");
+ result = usb_stor_clear_halt(us, us->recv_bulk_pipe);
+
+ US_DEBUGP("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)
+ US_DEBUGP("Soft reset failed\n");
+ else
+ US_DEBUGP("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)
+{
+ US_DEBUGP("%s called\n", __func__);
+
+ 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)
+{
+ US_DEBUGP("%s called\n", __func__);
+
+ 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_MORPHS)
+ return -EPERM;
+
+ result = usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf);
+ if (result < 0)
+ US_DEBUGP("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;
+ US_DEBUGP("No reset during disconnect\n");
+ } else {
+ result = usb_reset_device(us->pusb_dev);
+ US_DEBUGP("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 00000000..9369d752
--- /dev/null
+++ b/drivers/usb/storage/transport.h
@@ -0,0 +1,103 @@
+/* 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.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#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.c b/drivers/usb/storage/uas.c
new file mode 100644
index 00000000..8ec8a6e6
--- /dev/null
+++ b/drivers/usb/storage/uas.c
@@ -0,0 +1,862 @@
+/*
+ * USB Attached SCSI
+ * Note that this is not the same as the USB Mass Storage driver
+ *
+ * Copyright Matthew Wilcox for Intel Corp, 2010
+ * Copyright Sarah Sharp for Intel Corp, 2010
+ *
+ * Distributed under the terms of the GNU GPL, version two.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+/*
+ * The r00-r01c specs define this version of the SENSE IU data structure.
+ * It's still in use by several different firmware releases.
+ */
+struct sense_iu_old {
+ __u8 iu_id;
+ __u8 rsvd1;
+ __be16 tag;
+ __be16 len;
+ __u8 status;
+ __u8 service_response;
+ __u8 sense[SCSI_SENSE_BUFFERSIZE];
+};
+
+struct uas_dev_info {
+ struct usb_interface *intf;
+ struct usb_device *udev;
+ int qdepth;
+ unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe;
+ unsigned use_streams:1;
+ unsigned uas_sense_old:1;
+ struct scsi_cmnd *cmnd;
+ struct urb *status_urb; /* used only if stream support is available */
+};
+
+enum {
+ ALLOC_STATUS_URB = (1 << 0),
+ SUBMIT_STATUS_URB = (1 << 1),
+ ALLOC_DATA_IN_URB = (1 << 2),
+ SUBMIT_DATA_IN_URB = (1 << 3),
+ ALLOC_DATA_OUT_URB = (1 << 4),
+ SUBMIT_DATA_OUT_URB = (1 << 5),
+ ALLOC_CMD_URB = (1 << 6),
+ SUBMIT_CMD_URB = (1 << 7),
+ COMPLETED_DATA_IN = (1 << 8),
+ COMPLETED_DATA_OUT = (1 << 9),
+ DATA_COMPLETES_CMD = (1 << 10),
+};
+
+/* Overrides scsi_pointer */
+struct uas_cmd_info {
+ unsigned int state;
+ unsigned int stream;
+ struct urb *cmd_urb;
+ /* status_urb is used only if stream support isn't available */
+ struct urb *status_urb;
+ struct urb *data_in_urb;
+ struct urb *data_out_urb;
+ struct list_head list;
+};
+
+/* I hate forward declarations, but I actually have a loop */
+static int uas_submit_urbs(struct scsi_cmnd *cmnd,
+ struct uas_dev_info *devinfo, gfp_t gfp);
+static void uas_do_work(struct work_struct *work);
+
+static DECLARE_WORK(uas_work, uas_do_work);
+static DEFINE_SPINLOCK(uas_work_lock);
+static LIST_HEAD(uas_work_list);
+
+static void uas_do_work(struct work_struct *work)
+{
+ struct uas_cmd_info *cmdinfo;
+ struct uas_cmd_info *temp;
+ struct list_head list;
+ int err;
+
+ spin_lock_irq(&uas_work_lock);
+ list_replace_init(&uas_work_list, &list);
+ spin_unlock_irq(&uas_work_lock);
+
+ list_for_each_entry_safe(cmdinfo, temp, &list, list) {
+ struct scsi_pointer *scp = (void *)cmdinfo;
+ struct scsi_cmnd *cmnd = container_of(scp,
+ struct scsi_cmnd, SCp);
+ err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO);
+ if (err) {
+ list_del(&cmdinfo->list);
+ spin_lock_irq(&uas_work_lock);
+ list_add_tail(&cmdinfo->list, &uas_work_list);
+ spin_unlock_irq(&uas_work_lock);
+ schedule_work(&uas_work);
+ }
+ }
+}
+
+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;
+ struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
+
+ 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;
+ if (!(cmdinfo->state & DATA_COMPLETES_CMD))
+ cmnd->scsi_done(cmnd);
+}
+
+static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd)
+{
+ struct sense_iu_old *sense_iu = urb->transfer_buffer;
+ struct scsi_device *sdev = cmnd->device;
+ struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
+
+ if (urb->actual_length > 8) {
+ unsigned len = be16_to_cpup(&sense_iu->len) - 2;
+ if (len + 8 != urb->actual_length) {
+ int newlen = min(len + 8, urb->actual_length) - 8;
+ 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;
+ if (!(cmdinfo->state & DATA_COMPLETES_CMD))
+ cmnd->scsi_done(cmnd);
+}
+
+static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd,
+ unsigned direction)
+{
+ struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
+ int err;
+
+ cmdinfo->state = direction;
+ err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC);
+ if (err) {
+ spin_lock(&uas_work_lock);
+ list_add_tail(&cmdinfo->list, &uas_work_list);
+ spin_unlock(&uas_work_lock);
+ schedule_work(&uas_work);
+ }
+}
+
+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 = (void *)shost->hostdata[0];
+ struct scsi_cmnd *cmnd;
+ struct uas_cmd_info *cmdinfo;
+ u16 tag;
+ int ret;
+
+ if (urb->status) {
+ dev_err(&urb->dev->dev, "URB BAD STATUS %d\n", urb->status);
+ if (devinfo->use_streams)
+ usb_free_urb(urb);
+ return;
+ }
+
+ tag = be16_to_cpup(&iu->tag) - 1;
+ if (tag == 0)
+ cmnd = devinfo->cmnd;
+ else
+ cmnd = scsi_host_find_tag(shost, tag - 1);
+ if (!cmnd) {
+ if (devinfo->use_streams) {
+ usb_free_urb(urb);
+ return;
+ }
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret)
+ dev_err(&urb->dev->dev, "failed submit status urb\n");
+ return;
+ }
+ cmdinfo = (void *)&cmnd->SCp;
+
+ switch (iu->iu_id) {
+ case IU_ID_STATUS:
+ if (devinfo->cmnd == cmnd)
+ devinfo->cmnd = NULL;
+
+ if (!(cmdinfo->state & COMPLETED_DATA_IN) &&
+ cmdinfo->data_in_urb) {
+ if (devinfo->use_streams) {
+ cmdinfo->state |= DATA_COMPLETES_CMD;
+ usb_unlink_urb(cmdinfo->data_in_urb);
+ } else {
+ usb_free_urb(cmdinfo->data_in_urb);
+ }
+ }
+ if (!(cmdinfo->state & COMPLETED_DATA_OUT) &&
+ cmdinfo->data_out_urb) {
+ if (devinfo->use_streams) {
+ cmdinfo->state |= DATA_COMPLETES_CMD;
+ usb_unlink_urb(cmdinfo->data_in_urb);
+ } else {
+ usb_free_urb(cmdinfo->data_out_urb);
+ }
+ }
+
+ if (urb->actual_length < 16)
+ devinfo->uas_sense_old = 1;
+ if (devinfo->uas_sense_old)
+ uas_sense_old(urb, cmnd);
+ else
+ uas_sense(urb, cmnd);
+ break;
+ case IU_ID_READ_READY:
+ uas_xfer_data(urb, cmnd, SUBMIT_DATA_IN_URB);
+ break;
+ case IU_ID_WRITE_READY:
+ uas_xfer_data(urb, cmnd, SUBMIT_DATA_OUT_URB);
+ break;
+ default:
+ scmd_printk(KERN_ERR, cmnd,
+ "Bogus IU (%d) received on status pipe\n", iu->iu_id);
+ }
+
+ if (devinfo->use_streams) {
+ usb_free_urb(urb);
+ return;
+ }
+
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret)
+ dev_err(&urb->dev->dev, "failed submit status urb\n");
+}
+
+static void uas_data_out_cmplt(struct urb *urb)
+{
+ struct scsi_cmnd *cmnd = urb->context;
+ struct scsi_data_buffer *sdb = scsi_out(cmnd);
+ struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
+
+ cmdinfo->state |= COMPLETED_DATA_OUT;
+
+ sdb->resid = sdb->length - urb->actual_length;
+ usb_free_urb(urb);
+
+ if (cmdinfo->state & DATA_COMPLETES_CMD)
+ cmnd->scsi_done(cmnd);
+}
+
+static void uas_data_in_cmplt(struct urb *urb)
+{
+ struct scsi_cmnd *cmnd = urb->context;
+ struct scsi_data_buffer *sdb = scsi_in(cmnd);
+ struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
+
+ cmdinfo->state |= COMPLETED_DATA_IN;
+
+ sdb->resid = sdb->length - urb->actual_length;
+ usb_free_urb(urb);
+
+ if (cmdinfo->state & DATA_COMPLETES_CMD)
+ cmnd->scsi_done(cmnd);
+}
+
+static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp,
+ unsigned int pipe, struct scsi_cmnd *cmnd,
+ enum dma_data_direction dir)
+{
+ struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
+ struct usb_device *udev = devinfo->udev;
+ struct urb *urb = usb_alloc_urb(0, gfp);
+ struct scsi_data_buffer *sdb;
+ usb_complete_t complete_fn;
+ u16 stream_id = cmdinfo->stream;
+
+ if (!urb)
+ goto out;
+ if (dir == DMA_FROM_DEVICE) {
+ sdb = scsi_in(cmnd);
+ complete_fn = uas_data_in_cmplt;
+ } else {
+ sdb = scsi_out(cmnd);
+ complete_fn = uas_data_out_cmplt;
+ }
+ usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length,
+ complete_fn, cmnd);
+ urb->stream_id = stream_id;
+ 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_Host *shost, u16 stream_id)
+{
+ struct usb_device *udev = devinfo->udev;
+ struct urb *urb = usb_alloc_urb(0, gfp);
+ struct sense_iu *iu;
+
+ if (!urb)
+ goto out;
+
+ iu = kzalloc(sizeof(*iu), gfp);
+ if (!iu)
+ goto free;
+
+ usb_fill_bulk_urb(urb, udev, devinfo->status_pipe, iu, sizeof(*iu),
+ uas_stat_cmplt, shost);
+ urb->stream_id = stream_id;
+ 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, u16 stream_id)
+{
+ struct usb_device *udev = devinfo->udev;
+ struct scsi_device *sdev = cmnd->device;
+ 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;
+ if (blk_rq_tagged(cmnd->request))
+ iu->tag = cpu_to_be16(cmnd->request->tag + 2);
+ else
+ iu->tag = cpu_to_be16(1);
+ 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,
+ usb_free_urb, 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_urbs(struct scsi_cmnd *cmnd,
+ struct uas_dev_info *devinfo, gfp_t gfp)
+{
+ struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
+
+ if (cmdinfo->state & ALLOC_STATUS_URB) {
+ cmdinfo->status_urb = uas_alloc_sense_urb(devinfo, gfp,
+ cmnd->device->host, cmdinfo->stream);
+ if (!cmdinfo->status_urb)
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+ cmdinfo->state &= ~ALLOC_STATUS_URB;
+ }
+
+ if (cmdinfo->state & SUBMIT_STATUS_URB) {
+ if (usb_submit_urb(cmdinfo->status_urb, gfp)) {
+ scmd_printk(KERN_INFO, cmnd,
+ "sense urb submission failure\n");
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+ }
+ cmdinfo->state &= ~SUBMIT_STATUS_URB;
+ }
+
+ if (cmdinfo->state & ALLOC_DATA_IN_URB) {
+ cmdinfo->data_in_urb = uas_alloc_data_urb(devinfo, gfp,
+ devinfo->data_in_pipe, cmnd,
+ DMA_FROM_DEVICE);
+ if (!cmdinfo->data_in_urb)
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+ cmdinfo->state &= ~ALLOC_DATA_IN_URB;
+ }
+
+ if (cmdinfo->state & SUBMIT_DATA_IN_URB) {
+ if (usb_submit_urb(cmdinfo->data_in_urb, gfp)) {
+ scmd_printk(KERN_INFO, cmnd,
+ "data in urb submission failure\n");
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+ }
+ cmdinfo->state &= ~SUBMIT_DATA_IN_URB;
+ }
+
+ if (cmdinfo->state & ALLOC_DATA_OUT_URB) {
+ cmdinfo->data_out_urb = uas_alloc_data_urb(devinfo, gfp,
+ devinfo->data_out_pipe, cmnd,
+ DMA_TO_DEVICE);
+ if (!cmdinfo->data_out_urb)
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+ cmdinfo->state &= ~ALLOC_DATA_OUT_URB;
+ }
+
+ if (cmdinfo->state & SUBMIT_DATA_OUT_URB) {
+ if (usb_submit_urb(cmdinfo->data_out_urb, gfp)) {
+ scmd_printk(KERN_INFO, cmnd,
+ "data out urb submission failure\n");
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+ }
+ cmdinfo->state &= ~SUBMIT_DATA_OUT_URB;
+ }
+
+ if (cmdinfo->state & ALLOC_CMD_URB) {
+ cmdinfo->cmd_urb = uas_alloc_cmd_urb(devinfo, gfp, cmnd,
+ cmdinfo->stream);
+ if (!cmdinfo->cmd_urb)
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+ cmdinfo->state &= ~ALLOC_CMD_URB;
+ }
+
+ if (cmdinfo->state & SUBMIT_CMD_URB) {
+ if (usb_submit_urb(cmdinfo->cmd_urb, gfp)) {
+ scmd_printk(KERN_INFO, cmnd,
+ "cmd urb submission failure\n");
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+ }
+ cmdinfo->state &= ~SUBMIT_CMD_URB;
+ }
+
+ return 0;
+}
+
+static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
+ void (*done)(struct scsi_cmnd *))
+{
+ struct scsi_device *sdev = cmnd->device;
+ struct uas_dev_info *devinfo = sdev->hostdata;
+ struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
+ int err;
+
+ BUILD_BUG_ON(sizeof(struct uas_cmd_info) > sizeof(struct scsi_pointer));
+
+ if (devinfo->cmnd)
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+
+ if (blk_rq_tagged(cmnd->request)) {
+ cmdinfo->stream = cmnd->request->tag + 2;
+ } else {
+ devinfo->cmnd = cmnd;
+ cmdinfo->stream = 1;
+ }
+
+ cmnd->scsi_done = done;
+
+ cmdinfo->state = ALLOC_STATUS_URB | 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;
+ case DMA_TO_DEVICE:
+ cmdinfo->state |= ALLOC_DATA_OUT_URB | SUBMIT_DATA_OUT_URB;
+ case DMA_NONE:
+ break;
+ }
+
+ if (!devinfo->use_streams) {
+ cmdinfo->state &= ~(SUBMIT_DATA_IN_URB | SUBMIT_DATA_OUT_URB |
+ ALLOC_STATUS_URB | SUBMIT_STATUS_URB);
+ cmdinfo->stream = 0;
+ }
+
+ err = uas_submit_urbs(cmnd, devinfo, GFP_ATOMIC);
+ if (err) {
+ /* If we did nothing, give up now */
+ if (cmdinfo->state & SUBMIT_STATUS_URB) {
+ usb_free_urb(cmdinfo->status_urb);
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+ }
+ spin_lock(&uas_work_lock);
+ list_add_tail(&cmdinfo->list, &uas_work_list);
+ spin_unlock(&uas_work_lock);
+ schedule_work(&uas_work);
+ }
+
+ return 0;
+}
+
+static DEF_SCSI_QCMD(uas_queuecommand)
+
+static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
+{
+ struct scsi_device *sdev = cmnd->device;
+ sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__,
+ cmnd->request->tag);
+
+/* XXX: Send ABORT TASK Task Management command */
+ return FAILED;
+}
+
+static int uas_eh_device_reset_handler(struct scsi_cmnd *cmnd)
+{
+ struct scsi_device *sdev = cmnd->device;
+ sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__,
+ cmnd->request->tag);
+
+/* XXX: Send LOGICAL UNIT RESET Task Management command */
+ return FAILED;
+}
+
+static int uas_eh_target_reset_handler(struct scsi_cmnd *cmnd)
+{
+ struct scsi_device *sdev = cmnd->device;
+ sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__,
+ cmnd->request->tag);
+
+/* XXX: Can we reset just the one USB interface?
+ * Would calling usb_set_interface() have the right effect?
+ */
+ return FAILED;
+}
+
+static int uas_eh_bus_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;
+
+ sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__,
+ cmnd->request->tag);
+
+ if (usb_reset_device(udev))
+ return SUCCESS;
+
+ return FAILED;
+}
+
+static int uas_slave_alloc(struct scsi_device *sdev)
+{
+ sdev->hostdata = (void *)sdev->host->hostdata[0];
+ return 0;
+}
+
+static int uas_slave_configure(struct scsi_device *sdev)
+{
+ struct uas_dev_info *devinfo = sdev->hostdata;
+ scsi_set_tag_type(sdev, MSG_ORDERED_TAG);
+ scsi_activate_tcq(sdev, devinfo->qdepth - 2);
+ return 0;
+}
+
+static struct scsi_host_template uas_host_template = {
+ .module = THIS_MODULE,
+ .name = "uas",
+ .queuecommand = uas_queuecommand,
+ .slave_alloc = uas_slave_alloc,
+ .slave_configure = uas_slave_configure,
+ .eh_abort_handler = uas_eh_abort_handler,
+ .eh_device_reset_handler = uas_eh_device_reset_handler,
+ .eh_target_reset_handler = uas_eh_target_reset_handler,
+ .eh_bus_reset_handler = uas_eh_bus_reset_handler,
+ .can_queue = 65536, /* Is there a limit on the _host_ ? */
+ .this_id = -1,
+ .sg_tablesize = SG_NONE,
+ .cmd_per_lun = 1, /* until we override it */
+ .skip_settle_delay = 1,
+ .ordered_tag = 1,
+};
+
+static struct usb_device_id uas_usb_ids[] = {
+ { 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) },
+ /* 0xaa is a prototype device I happen to have access to */
+ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, USB_SC_SCSI, 0xaa) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, uas_usb_ids);
+
+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 int uas_isnt_supported(struct usb_device *udev)
+{
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
+ 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"
+ "alternative USB controller if you wish to use UAS.\n");
+ return -ENODEV;
+}
+
+static int uas_switch_interface(struct usb_device *udev,
+ struct usb_interface *intf)
+{
+ int i;
+ int sg_supported = udev->bus->sg_tablesize != 0;
+
+ for (i = 0; i < intf->num_altsetting; i++) {
+ struct usb_host_interface *alt = &intf->altsetting[i];
+
+ if (uas_is_interface(alt)) {
+ if (!sg_supported)
+ return uas_isnt_supported(udev);
+ return usb_set_interface(udev,
+ alt->desc.bInterfaceNumber,
+ alt->desc.bAlternateSetting);
+ }
+ }
+
+ return -ENODEV;
+}
+
+static void uas_configure_endpoints(struct uas_dev_info *devinfo)
+{
+ struct usb_host_endpoint *eps[4] = { };
+ struct usb_interface *intf = devinfo->intf;
+ struct usb_device *udev = devinfo->udev;
+ struct usb_host_endpoint *endpoint = intf->cur_altsetting->endpoint;
+ unsigned i, n_endpoints = intf->cur_altsetting->desc.bNumEndpoints;
+
+ devinfo->uas_sense_old = 0;
+ devinfo->cmnd = NULL;
+
+ for (i = 0; i < n_endpoints; i++) {
+ unsigned char *extra = endpoint[i].extra;
+ int len = endpoint[i].extralen;
+ while (len > 1) {
+ 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];
+ }
+ }
+
+ /*
+ * Assume that if we didn't find a control pipe descriptor, we're
+ * using a device with old firmware that happens to be set up like
+ * this.
+ */
+ if (!eps[0]) {
+ devinfo->cmd_pipe = usb_sndbulkpipe(udev, 1);
+ devinfo->status_pipe = usb_rcvbulkpipe(udev, 1);
+ devinfo->data_in_pipe = usb_rcvbulkpipe(udev, 2);
+ devinfo->data_out_pipe = usb_sndbulkpipe(udev, 2);
+
+ eps[1] = usb_pipe_endpoint(udev, devinfo->status_pipe);
+ eps[2] = usb_pipe_endpoint(udev, devinfo->data_in_pipe);
+ eps[3] = usb_pipe_endpoint(udev, devinfo->data_out_pipe);
+ } else {
+ devinfo->cmd_pipe = usb_sndbulkpipe(udev,
+ eps[0]->desc.bEndpointAddress);
+ devinfo->status_pipe = usb_rcvbulkpipe(udev,
+ eps[1]->desc.bEndpointAddress);
+ devinfo->data_in_pipe = usb_rcvbulkpipe(udev,
+ eps[2]->desc.bEndpointAddress);
+ devinfo->data_out_pipe = usb_sndbulkpipe(udev,
+ eps[3]->desc.bEndpointAddress);
+ }
+
+ devinfo->qdepth = usb_alloc_streams(devinfo->intf, eps + 1, 3, 256,
+ GFP_KERNEL);
+ if (devinfo->qdepth < 0) {
+ devinfo->qdepth = 256;
+ devinfo->use_streams = 0;
+ } else {
+ devinfo->use_streams = 1;
+ }
+}
+
+static int uas_alloc_status_urb(struct uas_dev_info *devinfo,
+ struct Scsi_Host *shost)
+{
+ if (devinfo->use_streams) {
+ devinfo->status_urb = NULL;
+ return 0;
+ }
+
+ devinfo->status_urb = uas_alloc_sense_urb(devinfo, GFP_KERNEL,
+ shost, 0);
+ if (!devinfo->status_urb)
+ goto err_s_urb;
+
+ if (usb_submit_urb(devinfo->status_urb, GFP_KERNEL))
+ goto err_submit_urb;
+
+ return 0;
+err_submit_urb:
+ usb_free_urb(devinfo->status_urb);
+err_s_urb:
+ return -ENOMEM;
+}
+
+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_KERNEL);
+}
+
+/*
+ * XXX: What I'd like to do here is register a SCSI host for each USB host in
+ * the system. Follow usb-storage's design of registering a SCSI host for
+ * each USB device for the moment. Can implement this by walking up the
+ * USB hierarchy until we find a USB host.
+ */
+static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ int result;
+ struct Scsi_Host *shost;
+ struct uas_dev_info *devinfo;
+ struct usb_device *udev = interface_to_usbdev(intf);
+
+ if (uas_switch_interface(udev, intf))
+ return -ENODEV;
+
+ devinfo = kmalloc(sizeof(struct uas_dev_info), GFP_KERNEL);
+ if (!devinfo)
+ return -ENOMEM;
+
+ result = -ENOMEM;
+ shost = scsi_host_alloc(&uas_host_template, sizeof(void *));
+ if (!shost)
+ goto free;
+
+ shost->max_cmd_len = 16 + 252;
+ shost->max_id = 1;
+ shost->sg_tablesize = udev->bus->sg_tablesize;
+
+ devinfo->intf = intf;
+ devinfo->udev = udev;
+ uas_configure_endpoints(devinfo);
+
+ result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 2);
+ if (result)
+ goto free;
+
+ result = scsi_add_host(shost, &intf->dev);
+ if (result)
+ goto deconfig_eps;
+
+ shost->hostdata[0] = (unsigned long)devinfo;
+
+ result = uas_alloc_status_urb(devinfo, shost);
+ if (result)
+ goto err_alloc_status;
+
+ scsi_scan_host(shost);
+ usb_set_intfdata(intf, shost);
+ return result;
+
+err_alloc_status:
+ scsi_remove_host(shost);
+ shost = NULL;
+deconfig_eps:
+ uas_free_streams(devinfo);
+ free:
+ kfree(devinfo);
+ if (shost)
+ scsi_host_put(shost);
+ return result;
+}
+
+static int uas_pre_reset(struct usb_interface *intf)
+{
+/* XXX: Need to return 1 if it's not our device in error handling */
+ return 0;
+}
+
+static int uas_post_reset(struct usb_interface *intf)
+{
+/* XXX: Need to return 1 if it's not our device in error handling */
+ return 0;
+}
+
+static void uas_disconnect(struct usb_interface *intf)
+{
+ struct Scsi_Host *shost = usb_get_intfdata(intf);
+ struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
+
+ scsi_remove_host(shost);
+ usb_kill_urb(devinfo->status_urb);
+ usb_free_urb(devinfo->status_urb);
+ uas_free_streams(devinfo);
+ kfree(devinfo);
+}
+
+/*
+ * XXX: Should this plug into libusual so we can auto-upgrade devices from
+ * Bulk-Only to UAS?
+ */
+static struct usb_driver uas_driver = {
+ .name = "uas",
+ .probe = uas_probe,
+ .disconnect = uas_disconnect,
+ .pre_reset = uas_pre_reset,
+ .post_reset = uas_post_reset,
+ .id_table = uas_usb_ids,
+};
+
+module_usb_driver(uas_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("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 00000000..fa3e9eda
--- /dev/null
+++ b/drivers/usb/storage/unusual_alauda.h
@@ -0,0 +1,31 @@
+/* Unusual Devices File for the Alauda-based card readers
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#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 00000000..2c855302
--- /dev/null
+++ b/drivers/usb/storage/unusual_cypress.h
@@ -0,0 +1,39 @@
+/* Unusual Devices File for devices based on the Cypress USB/ATA bridge
+ * with support for ATACB
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#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, 0x0000, 0x9999,
+ "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 00000000..582a603c
--- /dev/null
+++ b/drivers/usb/storage/unusual_datafab.h
@@ -0,0 +1,98 @@
+/* Unusual Devices File for the Datafab USB Compact Flash reader
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#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 00000000..8f3cbb8d
--- /dev/null
+++ b/drivers/usb/storage/unusual_devs.h
@@ -0,0 +1,2050 @@
+/* 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.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* 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 /proc/bus/usb/devices with your device plugged in
+ * running with this patch.
+ * Send your submission to either Phil Dibowitz or
+ * Alan Stern , and don't forget to CC: the
+ * USB development list and the USB storage 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.
+ */
+
+#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),
+
+/* 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 ),
+
+#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),
+
+/* 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 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),
+
+/* 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
+ */
+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, 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),
+
+/* 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