summaryrefslogtreecommitdiff
path: root/drivers/usb/storage
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/usb/storage
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/usb/storage')
-rw-r--r--drivers/usb/storage/Kconfig113
-rw-r--r--drivers/usb/storage/Makefile23
-rw-r--r--drivers/usb/storage/datafab.c669
-rw-r--r--drivers/usb/storage/datafab.h40
-rw-r--r--drivers/usb/storage/debug.c177
-rw-r--r--drivers/usb/storage/debug.h65
-rw-r--r--drivers/usb/storage/dpcm.c89
-rw-r--r--drivers/usb/storage/dpcm.h34
-rw-r--r--drivers/usb/storage/freecom.c488
-rw-r--r--drivers/usb/storage/freecom.h36
-rw-r--r--drivers/usb/storage/initializers.c93
-rw-r--r--drivers/usb/storage/initializers.h54
-rw-r--r--drivers/usb/storage/isd200.c1442
-rw-r--r--drivers/usb/storage/isd200.h31
-rw-r--r--drivers/usb/storage/jumpshot.c596
-rw-r--r--drivers/usb/storage/jumpshot.h39
-rw-r--r--drivers/usb/storage/protocol.c254
-rw-r--r--drivers/usb/storage/protocol.h74
-rw-r--r--drivers/usb/storage/scsiglue.c500
-rw-r--r--drivers/usb/storage/scsiglue.h49
-rw-r--r--drivers/usb/storage/sddr09.c1608
-rw-r--r--drivers/usb/storage/sddr09.h48
-rw-r--r--drivers/usb/storage/sddr55.c938
-rw-r--r--drivers/usb/storage/sddr55.h34
-rw-r--r--drivers/usb/storage/shuttle_usbat.c1712
-rw-r--r--drivers/usb/storage/shuttle_usbat.h123
-rw-r--r--drivers/usb/storage/transport.c1215
-rw-r--r--drivers/usb/storage/transport.h174
-rw-r--r--drivers/usb/storage/unusual_devs.h986
-rw-r--r--drivers/usb/storage/usb.c1051
-rw-r--r--drivers/usb/storage/usb.h204
31 files changed, 12959 insertions, 0 deletions
diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig
new file mode 100644
index 000000000000..f1f1c0608c22
--- /dev/null
+++ b/drivers/usb/storage/Kconfig
@@ -0,0 +1,113 @@
+#
+# USB Storage driver configuration
+#
+
+comment "NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information"
+ depends on USB
+
+config USB_STORAGE
+ tristate "USB Mass Storage support"
+ depends on USB
+ select 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 'selects' (turns on, enables) 'SCSI', 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_DATAFAB
+ bool "Datafab Compact Flash Reader support (EXPERIMENTAL)"
+ depends on USB_STORAGE && EXPERIMENTAL
+ help
+ Support for certain Datafab CompactFlash readers.
+ Datafab has a web page at <http://www.datafabusa.com/>.
+
+config USB_STORAGE_FREECOM
+ bool "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 <http://www.freecom.de/>.
+
+config USB_STORAGE_ISD200
+ bool "ISD-200 USB/ATA Bridge support"
+ depends on USB_STORAGE && BLK_DEV_IDE
+ ---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)
+
+config USB_STORAGE_DPCM
+ bool "Microtech/ZiO! CompactFlash/SmartMedia support"
+ depends on USB_STORAGE
+ help
+ Say Y here to support the Microtech/ZiO! CompactFlash reader.
+ There is a web page at <http://www.ziocorp.com/products/>.
+
+config USB_STORAGE_USBAT
+ bool "USBAT/USBAT02-based storage support (EXPERIMENTAL)"
+ depends on USB_STORAGE && EXPERIMENTAL
+ 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
+
+config USB_STORAGE_SDDR09
+ bool "SanDisk SDDR-09 (and other SmartMedia) support (EXPERIMENTAL)"
+ depends on USB_STORAGE && EXPERIMENTAL
+ 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! SmartMedia reader.
+
+config USB_STORAGE_SDDR55
+ bool "SanDisk SDDR-55 SmartMedia support (EXPERIMENTAL)"
+ depends on USB_STORAGE && EXPERIMENTAL
+ help
+ Say Y here to include additional code to support the Sandisk SDDR-55
+ SmartMedia reader in the USB Mass Storage driver.
+
+config USB_STORAGE_JUMPSHOT
+ bool "Lexar Jumpshot Compact Flash Reader (EXPERIMENTAL)"
+ depends on USB_STORAGE && EXPERIMENTAL
+ help
+ Say Y here to include additional code to support the Lexar Jumpshot
+ USB CompactFlash reader.
+
diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile
new file mode 100644
index 000000000000..56652ccc2881
--- /dev/null
+++ b/drivers/usb/storage/Makefile
@@ -0,0 +1,23 @@
+#
+# Makefile for the USB Mass Storage device drivers.
+#
+# 15 Aug 2000, Christoph Hellwig <hch@infradead.org>
+# Rewritten to use lists instead of if-statements.
+#
+
+EXTRA_CFLAGS := -Idrivers/scsi
+
+obj-$(CONFIG_USB_STORAGE) += usb-storage.o
+
+usb-storage-obj-$(CONFIG_USB_STORAGE_DEBUG) += debug.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_USBAT) += shuttle_usbat.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR09) += sddr09.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR55) += sddr55.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_FREECOM) += freecom.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_DPCM) += dpcm.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_ISD200) += isd200.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_DATAFAB) += datafab.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += jumpshot.o
+
+usb-storage-objs := scsiglue.o protocol.o transport.o usb.o \
+ initializers.o $(usb-storage-obj-y)
diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c
new file mode 100644
index 000000000000..54e3e6c7ecd8
--- /dev/null
+++ b/drivers/usb/storage/datafab.c
@@ -0,0 +1,669 @@
+/* Driver for Datafab USB Compact Flash reader
+ *
+ * $Id: datafab.c,v 1.7 2002/02/25 00:40:13 mdharm Exp $
+ *
+ * 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 <stern@rowland.org>
+ *
+ * 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 <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+
+#include "usb.h"
+#include "transport.h"
+#include "protocol.h"
+#include "debug.h"
+#include "datafab.h"
+
+static int datafab_determine_lun(struct us_data *us,
+ struct datafab_info *info);
+
+
+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_idx = 0, sg_offset = 0;
+
+ // 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_idx, &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_idx = 0, sg_offset = 0;
+
+ // 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_idx, &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 (!us || !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 (!us || !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
+//
+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 = kmalloc(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;
+ }
+ memset(us->extra, 0, sizeof(struct datafab_info));
+ 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;
+}
diff --git a/drivers/usb/storage/datafab.h b/drivers/usb/storage/datafab.h
new file mode 100644
index 000000000000..32e3f271e582
--- /dev/null
+++ b/drivers/usb/storage/datafab.h
@@ -0,0 +1,40 @@
+/* Driver for Datafab MDCFE-B USB Compact Flash reader
+ * Header File
+ *
+ * Current development and maintenance by:
+ * (c) 2000 Jimmie Mayfield (mayfield+datafab@sackheads.org)
+ *
+ * See datafab.c for more explanation
+ *
+ * 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 _USB_DATAFAB_MDCFE_B_H
+#define _USB_DATAFAB_MDCFE_B_H
+
+extern int datafab_transport(struct scsi_cmnd *srb, struct us_data *us);
+
+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
+};
+
+#endif
diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c
new file mode 100644
index 000000000000..d76483706bc9
--- /dev/null
+++ b/drivers/usb/storage/debug.c
@@ -0,0 +1,177 @@
+/* Driver for USB Mass Storage compliant devices
+ * Debugging Functions Source Code File
+ *
+ * $Id: debug.c,v 1.9 2002/04/22 03:39:43 mdharm Exp $
+ *
+ * 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 <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 <linux/cdrom.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+
+#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 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 000000000000..cd2096acc723
--- /dev/null
+++ b/drivers/usb/storage/debug.h
@@ -0,0 +1,65 @@
+/* Driver for USB Mass Storage compliant devices
+ * Debugging Functions Header File
+ *
+ * $Id: debug.h,v 1.6 2001/01/12 23:51:04 mdharm Exp $
+ *
+ * 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 <linux/config.h>
+#include <linux/kernel.h>
+
+#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/dpcm.c b/drivers/usb/storage/dpcm.c
new file mode 100644
index 000000000000..92b69e4c8047
--- /dev/null
+++ b/drivers/usb/storage/dpcm.c
@@ -0,0 +1,89 @@
+/* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader
+ *
+ * $Id: dpcm.c,v 1.4 2001/06/11 02:54:25 mdharm Exp $
+ *
+ * DPCM driver v0.1:
+ *
+ * First release
+ *
+ * Current development and maintenance by:
+ * (c) 2000 Brian Webb (webbb@earthlink.net)
+ *
+ * This device contains both a CompactFlash card reader, which
+ * uses the Control/Bulk w/o Interrupt protocol and
+ * a SmartMedia card reader that uses the same protocol
+ * as the SDDR09.
+ *
+ * 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 <linux/config.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+
+#include "usb.h"
+#include "transport.h"
+#include "protocol.h"
+#include "debug.h"
+#include "dpcm.h"
+#include "sddr09.h"
+
+/*
+ * Transport for the Microtech DPCM-USB
+ *
+ */
+int dpcm_transport(struct scsi_cmnd *srb, struct us_data *us)
+{
+ int ret;
+
+ if(srb == NULL)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ 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;
+
+#ifdef CONFIG_USB_STORAGE_SDDR09
+ case 1:
+
+ /*
+ * LUN 1 corresponds to the SmartMedia card reader.
+ */
+
+ /*
+ * Set the LUN to 0 (just in case).
+ */
+ srb->device->lun = 0; us->srb->device->lun = 0;
+ ret = sddr09_transport(srb, us);
+ srb->device->lun = 1; us->srb->device->lun = 1;
+ break;
+
+#endif
+
+ default:
+ US_DEBUGP("dpcm_transport: Invalid LUN %d\n", srb->device->lun);
+ ret = USB_STOR_TRANSPORT_ERROR;
+ break;
+ }
+ return ret;
+}
diff --git a/drivers/usb/storage/dpcm.h b/drivers/usb/storage/dpcm.h
new file mode 100644
index 000000000000..81b464cfcc1e
--- /dev/null
+++ b/drivers/usb/storage/dpcm.h
@@ -0,0 +1,34 @@
+/* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader
+ *
+ * $Id: dpcm.h,v 1.2 2000/08/25 00:13:51 mdharm Exp $
+ *
+ * DPCM driver v0.1:
+ *
+ * First release
+ *
+ * Current development and maintenance by:
+ * (c) 2000 Brian Webb (webbb@earthlink.net)
+ *
+ * See dpcm.c for more explanation
+ *
+ * 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 _MICROTECH_DPCM_USB_H
+#define _MICROTECH_DPCM_USB_H
+
+extern int dpcm_transport(struct scsi_cmnd *srb, struct us_data *us);
+
+#endif
diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c
new file mode 100644
index 000000000000..30e96050fe0c
--- /dev/null
+++ b/drivers/usb/storage/freecom.c
@@ -0,0 +1,488 @@
+/* Driver for Freecom USB/IDE adaptor
+ *
+ * $Id: freecom.c,v 1.22 2002/04/22 03:39:43 mdharm Exp $
+ *
+ * Freecom v0.1:
+ *
+ * First release
+ *
+ * Current development and maintenance by:
+ * (C) 2000 David Brown <usb-storage@davidb.org>
+ *
+ * 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 <linux/config.h>
+#include <linux/hdreg.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+
+#include "usb.h"
+#include "transport.h"
+#include "protocol.h"
+#include "debug.h"
+#include "freecom.h"
+
+#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
+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_transfer_sg(us, ipipe, srb->request_buffer,
+ count, srb->use_sg, &srb->resid);
+ 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_transfer_sg(us, opipe, srb->request_buffer,
+ count, srb->use_sg, &srb->resid);
+
+ 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.
+ *
+ */
+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", srb->request_bufflen);
+
+ /* 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 = srb->request_bufflen;
+ }
+
+ /* verify that this amount is legal */
+ if (length > srb->request_bufflen) {
+ length = srb->request_bufflen;
+ 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;
+}
+
+int
+freecom_init (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;
+}
+
+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
+
diff --git a/drivers/usb/storage/freecom.h b/drivers/usb/storage/freecom.h
new file mode 100644
index 000000000000..1b012d62d0a8
--- /dev/null
+++ b/drivers/usb/storage/freecom.h
@@ -0,0 +1,36 @@
+/* Driver for Freecom USB/IDE adaptor
+ *
+ * $Id: freecom.h,v 1.4 2000/08/29 14:49:15 dlbrown Exp $
+ *
+ * Freecom v0.1:
+ *
+ * First release
+ *
+ * Current development and maintenance by:
+ * (c) 2000 David Brown <usb-storage@davidb.org>
+ *
+ * See freecom.c for more explanation
+ *
+ * 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 _FREECOM_USB_H
+#define _FREECOM_USB_H
+
+extern int freecom_transport(struct scsi_cmnd *srb, struct us_data *us);
+extern int usb_stor_freecom_reset(struct us_data *us);
+extern int freecom_init (struct us_data *us);
+
+#endif
diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c
new file mode 100644
index 000000000000..5b06f9240d05
--- /dev/null
+++ b/drivers/usb/storage/initializers.c
@@ -0,0 +1,93 @@
+/* Special Initializers for certain USB Mass Storage devices
+ *
+ * $Id: initializers.c,v 1.2 2000/09/06 22:35:57 mdharm Exp $
+ *
+ * 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 <linux/sched.h>
+#include <linux/errno.h>
+
+#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, 5*HZ);
+ 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, 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 res;
+
+ US_DEBUGP("Getting status packet...\n");
+ res = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, bcs,
+ US_BULK_CS_WRAP_LEN, &partial);
+
+ return (res ? -1 : 0);
+}
diff --git a/drivers/usb/storage/initializers.h b/drivers/usb/storage/initializers.h
new file mode 100644
index 000000000000..7372386f33d5
--- /dev/null
+++ b/drivers/usb/storage/initializers.h
@@ -0,0 +1,54 @@
+/* Header file for Special Initializers for certain USB Mass Storage devices
+ *
+ * $Id: initializers.h,v 1.1 2000/08/29 23:07:02 mdharm Exp $
+ *
+ * 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 <linux/config.h>
+#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);
+
+#ifdef CONFIG_USB_STORAGE_SDDR09
+int sddr09_init(struct us_data *us);
+#endif
+
+/* This function is required to activate all four slots on the UCR-61S2B
+ * flash reader */
+int usb_stor_ucr61s2b_init(struct us_data *us);
diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c
new file mode 100644
index 000000000000..ecb328aa9ea1
--- /dev/null
+++ b/drivers/usb/storage/isd200.c
@@ -0,0 +1,1442 @@
+/* Transport & Protocol Driver for In-System Design, Inc. ISD200 ASIC
+ *
+ * $Id: isd200.c,v 1.16 2002/04/22 03:39:43 mdharm Exp $
+ *
+ * Current development and maintenance:
+ * (C) 2001-2002 Björn Stenberg (bjorn@haxx.se)
+ *
+ * Developed with the assistance of:
+ * (C) 2002 Alan Stern <stern@rowland.org>
+ *
+ * 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 <stern@rowland.harvard.edu>)
+ * 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 <luc@saillard.org>)
+ * 2002-01-17: All bitfields removed.
+ * (bjorn@haxx.se)
+ */
+
+
+/* Include files */
+
+#include <linux/jiffies.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+
+#include "usb.h"
+#include "transport.h"
+#include "protocol.h"
+#include "debug.h"
+#include "scsiglue.h"
+#include "isd200.h"
+
+
+/* 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 error definitions not in <linux/hdreg.h> */
+#define ATA_ERROR_MEDIA_CHANGE 0x20
+
+/* ATA command definitions not in <linux/hdreg.h> */
+#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;
+ struct hd_driveid *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;
+ struct scsi_cmnd srb;
+};
+
+
+/*
+ * 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[IDE_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 & MCR_ERR) {
+ buf->ErrorCode = 0x70 | SENSE_ERRCODE_VALID;
+ buf->AdditionalSenseLength = 0xb;
+ buf->Flags = UNIT_ATTENTION;
+ buf->AdditionalSenseCode = 0;
+ buf->AdditionalSenseCodeQualifier = 0;
+ } else if(error & TRK0_ERR) {
+ buf->ErrorCode = 0x70 | SENSE_ERRCODE_VALID;
+ buf->AdditionalSenseLength = 0xb;
+ buf->Flags = NOT_READY;
+ buf->AdditionalSenseCode = 0;
+ buf->AdditionalSenseCodeQualifier = 0;
+ } else if(error & ECC_ERR) {
+ 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_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;
+ 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));
+ memset(&srb_dev, 0, sizeof(srb_dev));
+ srb->device = &srb_dev;
+ ++srb->serial_number;
+
+ 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;
+ srb->sc_data_direction = DMA_FROM_DEVICE;
+ srb->request_buffer = pointer;
+ srb->request_bufflen = 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;
+ srb->sc_data_direction = DMA_NONE;
+ 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;
+ srb->sc_data_direction = DMA_NONE;
+ 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;
+ srb->sc_data_direction = DMA_NONE;
+ 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 = WIN_SRST;
+ srb->sc_data_direction = DMA_NONE;
+ break;
+
+ case ACTION_IDENTIFY:
+ US_DEBUGP(" isd200_action(IDENTIFY)\n");
+ ata.generic.RegisterSelect = REG_COMMAND;
+ ata.write.CommandByte = WIN_IDENTIFY;
+ srb->sc_data_direction = DMA_FROM_DEVICE;
+ srb->request_buffer = (void *) info->id;
+ srb->request_bufflen = sizeof(struct hd_driveid);
+ break;
+
+ default:
+ US_DEBUGP("Error: Undefined action %d\n",action);
+ break;
+ }
+
+ 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[IDE_ERROR_OFFSET] = 0x%x\n",
+ info->ATARegs[IDE_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->flags)) {
+ 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 ((srb->resid > 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->flags)) {
+ 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->flags);
+ /* 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[IDE_STATUS_OFFSET] & BUSY_STAT ) {
+ 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 BUSY_STAT and */
+ /* WRERR_STAT (workaround ATA Zip drive) and */
+ /* ERR_STAT (workaround for Archos CD-ROM) */
+ else if (regs[IDE_STATUS_OFFSET] &
+ (BUSY_STAT | WRERR_STAT | ERR_STAT )) {
+ US_DEBUGP(" Status indicates it is not ready, try again...\n");
+ }
+ /* check for DRDY, ATA devices set DRDY after SRST */
+ else if (regs[IDE_STATUS_OFFSET] & READY_STAT) {
+ 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[IDE_HCYL_OFFSET] == 0xEB) &&
+ (regs[IDE_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);
+}
+
+
+/**************************************************************************
+ * 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;
+ struct hd_driveid *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,
+ sizeof(struct hd_driveid) );
+ 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;
+ ide_fix_driveid(id);
+
+ US_DEBUGP(" Identify Data Structure:\n");
+ US_DEBUGP(" config = 0x%x\n", id->config);
+ US_DEBUGP(" cyls = 0x%x\n", id->cyls);
+ US_DEBUGP(" heads = 0x%x\n", id->heads);
+ US_DEBUGP(" track_bytes = 0x%x\n", id->track_bytes);
+ US_DEBUGP(" sector_bytes = 0x%x\n", id->sector_bytes);
+ US_DEBUGP(" sectors = 0x%x\n", id->sectors);
+ US_DEBUGP(" serial_no[0] = 0x%x\n", id->serial_no[0]);
+ US_DEBUGP(" buf_type = 0x%x\n", id->buf_type);
+ US_DEBUGP(" buf_size = 0x%x\n", id->buf_size);
+ US_DEBUGP(" ecc_bytes = 0x%x\n", id->ecc_bytes);
+ US_DEBUGP(" fw_rev[0] = 0x%x\n", id->fw_rev[0]);
+ US_DEBUGP(" model[0] = 0x%x\n", id->model[0]);
+ US_DEBUGP(" max_multsect = 0x%x\n", id->max_multsect);
+ US_DEBUGP(" dword_io = 0x%x\n", id->dword_io);
+ US_DEBUGP(" capability = 0x%x\n", id->capability);
+ US_DEBUGP(" tPIO = 0x%x\n", id->tPIO);
+ US_DEBUGP(" tDMA = 0x%x\n", id->tDMA);
+ US_DEBUGP(" field_valid = 0x%x\n", id->field_valid);
+ US_DEBUGP(" cur_cyls = 0x%x\n", id->cur_cyls);
+ US_DEBUGP(" cur_heads = 0x%x\n", id->cur_heads);
+ US_DEBUGP(" cur_sectors = 0x%x\n", id->cur_sectors);
+ US_DEBUGP(" cur_capacity = 0x%x\n", (id->cur_capacity1 << 16) + id->cur_capacity0 );
+ US_DEBUGP(" multsect = 0x%x\n", id->multsect);
+ US_DEBUGP(" lba_capacity = 0x%x\n", id->lba_capacity);
+ US_DEBUGP(" command_set_1 = 0x%x\n", id->command_set_1);
+ US_DEBUGP(" command_set_2 = 0x%x\n", id->command_set_2);
+
+ 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->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->model;
+ dest = (__u16*)info->InquiryData.VendorId;
+ for (i=0;i<4;i++)
+ dest[i] = be16_to_cpu(src[i]);
+
+ src = (__be16*)(id->model+8);
+ dest = (__u16*)info->InquiryData.ProductId;
+ for (i=0;i<8;i++)
+ dest[i] = be16_to_cpu(src[i]);
+
+ src = (__be16*)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->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);
+ 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;
+ struct hd_driveid *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;
+ srb->request_bufflen = 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;
+ srb->request_bufflen = 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 (id->capability & CAPABILITY_LBA ) {
+ capacity = id->lba_capacity - 1;
+ } else {
+ capacity = (id->heads *
+ id->cyls *
+ 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 (id->capability & CAPABILITY_LBA) {
+ sectnum = (unsigned char)(lba);
+ cylinder = (unsigned short)(lba>>8);
+ head = ATA_ADDRESS_DEVHEAD_LBA_MODE | (unsigned char)(lba>>24 & 0x0F);
+ } else {
+ sectnum = (unsigned char)((lba % id->sectors) + 1);
+ cylinder = (unsigned short)(lba / (id->sectors *
+ id->heads));
+ head = (unsigned char)((lba / id->sectors) %
+ 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 = WIN_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 (id->capability & CAPABILITY_LBA) {
+ sectnum = (unsigned char)(lba);
+ cylinder = (unsigned short)(lba>>8);
+ head = ATA_ADDRESS_DEVHEAD_LBA_MODE | (unsigned char)(lba>>24 & 0x0F);
+ } else {
+ sectnum = (unsigned char)((lba % id->sectors) + 1);
+ cylinder = (unsigned short)(lba / (id->sectors * id->heads));
+ head = (unsigned char)((lba / id->sectors) % 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 = WIN_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) ?
+ WIN_DOORLOCK : WIN_DOORUNLOCK;
+ srb->request_bufflen = 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;
+ srb->request_bufflen = 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);
+ }
+}
+
+/**************************************************************************
+ * 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 = (struct isd200_info *)
+ kmalloc(sizeof(struct isd200_info), GFP_KERNEL);
+ if (!info)
+ retStatus = ISD200_ERROR;
+ else {
+ memset(info, 0, sizeof(struct isd200_info));
+ info->id = (struct hd_driveid *)
+ kmalloc(sizeof(struct hd_driveid), GFP_KERNEL);
+ info->RegsBuf = (unsigned char *)
+ kmalloc(sizeof(info->ATARegs), GFP_KERNEL);
+ if (!info->id || !info->RegsBuf) {
+ isd200_free_info_ptrs(info);
+ kfree(info);
+ retStatus = ISD200_ERROR;
+ } else
+ memset(info->id, 0, sizeof(struct hd_driveid));
+ }
+
+ 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
+ */
+
+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 conected as a slave device will be
+ * detected in the driver initialization function and the protocol will
+ * be changed to an ATAPI protocol (Transparent SCSI).
+ *
+ */
+
+void isd200_ata_command(struct scsi_cmnd *srb, struct us_data *us)
+{
+ int sendToTransport = 1;
+ union ata_cdb ataCdb;
+
+ /* Make sure driver was initialized */
+
+ if (us->extra == NULL)
+ US_DEBUGP("ERROR Driver not initialized\n");
+
+ /* Convert command */
+ srb->resid = 0;
+ sendToTransport = isd200_scsi_to_ata(srb, us, &ataCdb);
+
+ /* send the command to the transport layer */
+ if (sendToTransport)
+ isd200_invoke_transport(us, srb, &ataCdb);
+}
diff --git a/drivers/usb/storage/isd200.h b/drivers/usb/storage/isd200.h
new file mode 100644
index 000000000000..0a35f4fa78f8
--- /dev/null
+++ b/drivers/usb/storage/isd200.h
@@ -0,0 +1,31 @@
+/* Header File for In-System Design, Inc. ISD200 ASIC
+ *
+ * First release
+ *
+ * Current development and maintenance by:
+ * (c) 2000 In-System Design, Inc. (support@in-system.com)
+ *
+ * See isd200.c for more information.
+ *
+ * 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 _USB_ISD200_H
+#define _USB_ISD200_H
+
+extern void isd200_ata_command(struct scsi_cmnd *srb, struct us_data *us);
+extern int isd200_Initialization(struct us_data *us);
+
+#endif
diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c
new file mode 100644
index 000000000000..aff9d51c327c
--- /dev/null
+++ b/drivers/usb/storage/jumpshot.c
@@ -0,0 +1,596 @@
+/* Driver for Lexar "Jumpshot" Compact Flash reader
+ *
+ * $Id: jumpshot.c,v 1.7 2002/02/25 00:40:13 mdharm Exp $
+ *
+ * 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 <stern@rowland.org>
+ *
+ * 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 <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+
+#include "usb.h"
+#include "transport.h"
+#include "protocol.h"
+#include "debug.h"
+#include "jumpshot.h"
+
+
+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_idx = 0, sg_offset = 0;
+
+ // 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_idx, &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_idx = 0, sg_offset = 0;
+
+ // 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_idx, &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 (!us || !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'
+//
+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 = kmalloc(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;
+ }
+ memset(us->extra, 0, sizeof(struct jumpshot_info));
+ 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;
+}
diff --git a/drivers/usb/storage/jumpshot.h b/drivers/usb/storage/jumpshot.h
new file mode 100644
index 000000000000..19bac9d1558f
--- /dev/null
+++ b/drivers/usb/storage/jumpshot.h
@@ -0,0 +1,39 @@
+/* Driver for Lexar "Jumpshot" USB Compact Flash reader
+ * Header File
+ *
+ * Current development and maintenance by:
+ * (c) 2000 Jimmie Mayfield (mayfield+usb@sackheads.org)
+ *
+ * See jumpshot.c for more explanation
+ *
+ * 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 _USB_JUMPSHOT_H
+#define _USB_JUMPSHOT_H
+
+extern int jumpshot_transport(struct scsi_cmnd *srb, struct us_data *us);
+
+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
+};
+
+#endif
diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c
new file mode 100644
index 000000000000..9ad30428d2dd
--- /dev/null
+++ b/drivers/usb/storage/protocol.c
@@ -0,0 +1,254 @@
+/* Driver for USB Mass Storage compliant devices
+ *
+ * $Id: protocol.c,v 1.14 2002/04/22 03:39:43 mdharm Exp $
+ *
+ * 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 <linux/highmem.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+
+#include "usb.h"
+#include "protocol.h"
+#include "debug.h"
+#include "scsiglue.h"
+#include "transport.h"
+
+/***********************************************************************
+ * Protocol routines
+ ***********************************************************************/
+
+void usb_stor_qic157_command(struct scsi_cmnd *srb, struct us_data *us)
+{
+ /* Pad the ATAPI command with zeros
+ *
+ * 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;
+
+ /* set command length to 12 bytes */
+ srb->cmd_len = 12;
+
+ /* send the command to the transport layer */
+ usb_stor_invoke_transport(srb, us);
+}
+
+void usb_stor_ATAPI_command(struct scsi_cmnd *srb, struct us_data *us)
+{
+ /* Pad the ATAPI command with zeros
+ *
+ * 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 */
+ srb->cmd_len = 12;
+
+ /* 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);
+}
+
+/***********************************************************************
+ * Scatter-gather transfer buffer access routines
+ ***********************************************************************/
+
+/* Copy a buffer of length buflen to/from the srb's transfer buffer.
+ * (Note: for scatter-gather transfers (srb->use_sg > 0), srb->request_buffer
+ * points to a list of s-g entries and we ignore srb->request_bufflen.
+ * For non-scatter-gather transfers, srb->request_buffer points to the
+ * transfer buffer itself and srb->request_bufflen is the buffer's length.)
+ * Update the *index 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, unsigned int *index,
+ unsigned int *offset, enum xfer_buf_dir dir)
+{
+ unsigned int cnt;
+
+ /* If not using scatter-gather, just transfer the data directly.
+ * Make certain it will fit in the available buffer space. */
+ if (srb->use_sg == 0) {
+ if (*offset >= srb->request_bufflen)
+ return 0;
+ cnt = min(buflen, srb->request_bufflen - *offset);
+ if (dir == TO_XFER_BUF)
+ memcpy((unsigned char *) srb->request_buffer + *offset,
+ buffer, cnt);
+ else
+ memcpy(buffer, (unsigned char *) srb->request_buffer +
+ *offset, cnt);
+ *offset += cnt;
+
+ /* Using scatter-gather. 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. */
+ } else {
+ struct scatterlist *sg =
+ (struct scatterlist *) srb->request_buffer
+ + *index;
+
+ /* 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 *index values for the next loop. */
+ cnt = 0;
+ while (cnt < buflen && *index < srb->use_sg) {
+ struct page *page = sg->page +
+ ((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;
+ ++*index;
+ ++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;
+ }
+ }
+ }
+
+ /* Return the amount actually transferred */
+ return cnt;
+}
+
+/* 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 index = 0, offset = 0;
+
+ usb_stor_access_xfer_buf(buffer, buflen, srb, &index, &offset,
+ TO_XFER_BUF);
+ if (buflen < srb->request_bufflen)
+ srb->resid = srb->request_bufflen - buflen;
+}
diff --git a/drivers/usb/storage/protocol.h b/drivers/usb/storage/protocol.h
new file mode 100644
index 000000000000..02bff01ab09c
--- /dev/null
+++ b/drivers/usb/storage/protocol.h
@@ -0,0 +1,74 @@
+/* Driver for USB Mass Storage compliant devices
+ * Protocol Functions Header File
+ *
+ * $Id: protocol.h,v 1.4 2001/02/13 07:10:03 mdharm Exp $
+ *
+ * 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_
+
+/* Sub Classes */
+
+#define US_SC_RBC 0x01 /* Typically, flash devices */
+#define US_SC_8020 0x02 /* CD-ROM */
+#define US_SC_QIC 0x03 /* QIC-157 Tapes */
+#define US_SC_UFI 0x04 /* Floppy */
+#define US_SC_8070 0x05 /* Removable media */
+#define US_SC_SCSI 0x06 /* Transparent */
+#define US_SC_ISD200 0x07 /* ISD200 ATA */
+#define US_SC_MIN US_SC_RBC
+#define US_SC_MAX US_SC_ISD200
+
+#define US_SC_DEVICE 0xff /* Use device's value */
+
+/* Protocol handling routines */
+extern void usb_stor_ATAPI_command(struct scsi_cmnd*, struct us_data*);
+extern void usb_stor_qic157_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, unsigned int *index,
+ 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/scsiglue.c b/drivers/usb/storage/scsiglue.c
new file mode 100644
index 000000000000..22e48a2b0bd1
--- /dev/null
+++ b/drivers/usb/storage/scsiglue.c
@@ -0,0 +1,500 @@
+/* Driver for USB Mass Storage compliant devices
+ * SCSI layer glue code
+ *
+ * $Id: scsiglue.c,v 1.26 2002/04/22 03:39:43 mdharm Exp $
+ *
+ * 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 <linux/slab.h>
+#include <linux/module.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_devinfo.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+
+#include "usb.h"
+#include "scsiglue.h"
+#include "debug.h"
+#include "transport.h"
+#include "protocol.h"
+
+/***********************************************************************
+ * Host functions
+ ***********************************************************************/
+
+static const char* host_info(struct Scsi_Host *host)
+{
+ return "SCSI emulation for USB Mass Storage devices";
+}
+
+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;
+ return 0;
+}
+
+static int slave_configure(struct scsi_device *sdev)
+{
+ struct us_data *us = host_to_us(sdev->host);
+
+ /* Scatter-gather buffers (all but the last) must have a length
+ * divisible by the bulk maxpacket size. Otherwise a data packet
+ * would end up being short, causing a premature end to the data
+ * transfer. Since high-speed bulk pipes have a maxpacket size
+ * of 512, we'll use that as the scsi device queue's DMA alignment
+ * mask. Guaranteeing proper alignment of the first buffer will
+ * have the desired effect because, except at the beginning and
+ * the end, scatter-gather buffers follow page boundaries. */
+ blk_queue_dma_alignment(sdev->request_queue, (512 - 1));
+
+ /* Set the SCSI level to at least 2. We'll leave it at 3 if that's
+ * what is originally reported. We need this to avoid confusing
+ * the SCSI layer with devices that report 0 or 1, but need 10-byte
+ * commands (ala ATAPI devices behind certain bridges, or devices
+ * which simply have broken INQUIRY data).
+ *
+ * NOTE: This means /dev/sg programs (ala cdrecord) will get the
+ * actual information. This seems to be the preference for
+ * programs like that.
+ *
+ * NOTE: This also means that /proc/scsi/scsi and sysfs may report
+ * the actual value or the modified one, depending on where the
+ * data comes from.
+ */
+ if (sdev->scsi_level < SCSI_2)
+ sdev->scsi_level = SCSI_2;
+
+ /* According to the technical support people at Genesys Logic,
+ * devices using their chips have problems transferring more than
+ * 32 KB at a time. In practice people have found that 64 KB
+ * works okay and that's what Windows does. But we'll be
+ * conservative; people can always use the sysfs interface to
+ * increase max_sectors. */
+ if (le16_to_cpu(us->pusb_dev->descriptor.idVendor) == USB_VENDOR_ID_GENESYS &&
+ sdev->request_queue->max_sectors > 64)
+ blk_queue_max_sectors(sdev->request_queue, 64);
+
+ /* 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) {
+
+ /* Disk-type devices use MODE SENSE(6) if the protocol
+ * (SubClass) is Transparent SCSI, otherwise they use
+ * MODE SENSE(10). */
+ if (us->subclass != US_SC_SCSI)
+ 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->flags & 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 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->flags & US_FL_FIX_CAPACITY)
+ sdev->fix_capacity = 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 devices choke when they receive a PREVENT-ALLOW MEDIUM
+ * REMOVAL command, so suppress those commands. */
+ if (us->flags & 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;
+}
+
+/* queue a command */
+/* This is always called with scsi_lock(host) held */
+static int queuecommand(struct scsi_cmnd *srb,
+ void (*done)(struct scsi_cmnd *))
+{
+ struct us_data *us = host_to_us(srb->device->host);
+
+ US_DEBUGP("%s called\n", __FUNCTION__);
+
+ /* check for state-transition errors */
+ if (us->srb != NULL) {
+ printk(KERN_ERR USB_STORAGE "Error in %s: us->srb = %p\n",
+ __FUNCTION__, us->srb);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ /* fail the command if we are disconnecting */
+ if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+ 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;
+ up(&(us->sema));
+
+ return 0;
+}
+
+/***********************************************************************
+ * Error handling functions
+ ***********************************************************************/
+
+/* Command timeout and abort */
+/* This is always called with scsi_lock(host) held */
+static int command_abort(struct scsi_cmnd *srb)
+{
+ struct us_data *us = host_to_us(srb->device->host);
+
+ US_DEBUGP("%s called\n", __FUNCTION__);
+
+ /* Is this command still active? */
+ if (us->srb != srb) {
+ 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). To prevent races with auto-reset, we must
+ * stop any ongoing USB transfers while still holding the host
+ * lock. */
+ set_bit(US_FLIDX_TIMED_OUT, &us->flags);
+ if (!test_bit(US_FLIDX_RESETTING, &us->flags)) {
+ set_bit(US_FLIDX_ABORTING, &us->flags);
+ usb_stor_stop_transport(us);
+ }
+ scsi_unlock(us_to_host(us));
+
+ /* Wait for the aborted command to finish */
+ wait_for_completion(&us->notify);
+
+ /* Reacquire the lock and allow USB transfers to resume */
+ scsi_lock(us_to_host(us));
+ clear_bit(US_FLIDX_ABORTING, &us->flags);
+ clear_bit(US_FLIDX_TIMED_OUT, &us->flags);
+ return SUCCESS;
+}
+
+/* This invokes the transport reset mechanism to reset the state of the
+ * device */
+/* This is always called with scsi_lock(host) held */
+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", __FUNCTION__);
+
+ scsi_unlock(us_to_host(us));
+
+ /* lock the device pointers and do the reset */
+ down(&(us->dev_semaphore));
+ if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+ result = FAILED;
+ US_DEBUGP("No reset during disconnect\n");
+ } else
+ result = us->transport_reset(us);
+ up(&(us->dev_semaphore));
+
+ /* lock the host for the return */
+ scsi_lock(us_to_host(us));
+ return result;
+}
+
+/* This resets the device's USB port. */
+/* It refuses to work if there's more than one interface in
+ * the device, so that other users are not affected. */
+/* This is always called with scsi_lock(host) held */
+static int bus_reset(struct scsi_cmnd *srb)
+{
+ struct us_data *us = host_to_us(srb->device->host);
+ int result, rc;
+
+ US_DEBUGP("%s called\n", __FUNCTION__);
+
+ scsi_unlock(us_to_host(us));
+
+ /* The USB subsystem doesn't handle synchronisation between
+ * a device's several drivers. Therefore we reset only devices
+ * with just one interface, which we of course own. */
+
+ down(&(us->dev_semaphore));
+ if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+ result = -EIO;
+ US_DEBUGP("No reset during disconnect\n");
+ } else if (us->pusb_dev->actconfig->desc.bNumInterfaces != 1) {
+ result = -EBUSY;
+ US_DEBUGP("Refusing to reset a multi-interface device\n");
+ } else {
+ rc = usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf);
+ if (rc < 0) {
+ US_DEBUGP("unable to lock device for reset: %d\n", rc);
+ result = rc;
+ } else {
+ result = usb_reset_device(us->pusb_dev);
+ if (rc)
+ usb_unlock_device(us->pusb_dev);
+ US_DEBUGP("usb_reset_device returns %d\n", result);
+ }
+ }
+ up(&(us->dev_semaphore));
+
+ /* lock the host for the return */
+ scsi_lock(us_to_host(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->flags & US_FL_SCM_MULT_TARG) {
+ for (i = 1; i < host->max_id; ++i)
+ scsi_report_device_reset(host, 0, i);
+ }
+}
+
+/***********************************************************************
+ * /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->flags & 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, char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+
+ return sprintf(buf, "%u\n", sdev->request_queue->max_sectors);
+}
+
+/* Input routine for the sysfs max_sectors file */
+static ssize_t store_max_sectors(struct device *dev, const char *buf,
+ size_t count)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ unsigned short ms;
+
+ if (sscanf(buf, "%hu", &ms) > 0 && ms <= SCSI_DEFAULT_MAX_SECTORS) {
+ blk_queue_max_sectors(sdev->request_queue, ms);
+ return strlen(buf);
+ }
+ 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,
+
+ /* lots of sg segments can be handled */
+ .sg_tablesize = SG_ALL,
+
+ /* 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 */
+};
+
diff --git a/drivers/usb/storage/scsiglue.h b/drivers/usb/storage/scsiglue.h
new file mode 100644
index 000000000000..d0a49af026c4
--- /dev/null
+++ b/drivers/usb/storage/scsiglue.h
@@ -0,0 +1,49 @@
+/* Driver for USB Mass Storage compliant devices
+ * SCSI Connecting Glue Header File
+ *
+ * $Id: scsiglue.h,v 1.4 2000/08/25 00:13:51 mdharm Exp $
+ *
+ * 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 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 000000000000..0ea2f5ab66ba
--- /dev/null
+++ b/drivers/usb/storage/sddr09.c
@@ -0,0 +1,1608 @@
+/* Driver for SanDisk SDDR-09 SmartMedia reader
+ *
+ * $Id: sddr09.c,v 1.24 2002/04/22 03:39:43 mdharm Exp $
+ * (c) 2000, 2001 Robert Baruch (autophile@starband.net)
+ * (c) 2002 Andries Brouwer (aeb@cwi.nl)
+ * Developed with the assistance of:
+ * (c) 2002 Alan Stern <stern@rowland.org>
+ *
+ * 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 <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+
+#include "usb.h"
+#include "transport.h"
+#include "protocol.h"
+#include "debug.h"
+#include "sddr09.h"
+
+
+#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 <linux/mtd/nand_ids.h>.
+ */
+
+struct nand_flash_dev {
+ int model_id;
+ int chipshift; /* 1<<cs bytes total capacity */
+ char pageshift; /* 1<<ps bytes in a page */
+ char blockshift; /* 1<<bs pages in an erase block */
+ char zoneshift; /* 1<<zs blocks in a zone */
+ /* # of logical blocks is 125/128 of this */
+ char pageadrlen; /* length of an address in bytes - 1 */
+};
+
+/*
+ * NAND Flash Manufacturer ID Codes
+ */
+#define NAND_MFR_AMD 0x01
+#define NAND_MFR_NATSEMI 0x8f
+#define NAND_MFR_TOSHIBA 0x98
+#define NAND_MFR_SAMSUNG 0xec
+
+static inline char *nand_flash_manufacturer(int manuf_id) {
+ switch(manuf_id) {
+ case NAND_MFR_AMD:
+ return "AMD";
+ case NAND_MFR_NATSEMI:
+ return "NATSEMI";
+ case NAND_MFR_TOSHIBA:
+ return "Toshiba";
+ case NAND_MFR_SAMSUNG:
+ return "Samsung";
+ default:
+ return "unknown";
+ }
+}
+
+/*
+ * It looks like it is unnecessary to attach manufacturer to the
+ * remaining data: SSFDC prescribes manufacturer-independent id codes.
+ *
+ * 256 MB NAND flash has a 5-byte ID with 2nd byte 0xaa, 0xba, 0xca or 0xda.
+ */
+
+static struct nand_flash_dev nand_flash_ids[] = {
+ /* NAND flash */
+ { 0x6e, 20, 8, 4, 8, 2}, /* 1 MB */
+ { 0xe8, 20, 8, 4, 8, 2}, /* 1 MB */
+ { 0xec, 20, 8, 4, 8, 2}, /* 1 MB */
+ { 0x64, 21, 8, 4, 9, 2}, /* 2 MB */
+ { 0xea, 21, 8, 4, 9, 2}, /* 2 MB */
+ { 0x6b, 22, 9, 4, 9, 2}, /* 4 MB */
+ { 0xe3, 22, 9, 4, 9, 2}, /* 4 MB */
+ { 0xe5, 22, 9, 4, 9, 2}, /* 4 MB */
+ { 0xe6, 23, 9, 4, 10, 2}, /* 8 MB */
+ { 0x73, 24, 9, 5, 10, 2}, /* 16 MB */
+ { 0x75, 25, 9, 5, 10, 2}, /* 32 MB */
+ { 0x76, 26, 9, 5, 10, 3}, /* 64 MB */
+ { 0x79, 27, 9, 5, 10, 3}, /* 128 MB */
+
+ /* MASK ROM */
+ { 0x5d, 21, 9, 4, 8, 2}, /* 2 MB */
+ { 0xd5, 22, 9, 4, 9, 2}, /* 4 MB */
+ { 0xd6, 23, 9, 4, 10, 2}, /* 8 MB */
+ { 0x57, 24, 9, 4, 11, 2}, /* 16 MB */
+ { 0x58, 25, 9, 4, 12, 2}, /* 32 MB */
+ { 0,}
+};
+
+#define SIZE(a) (sizeof(a)/sizeof((a)[0]))
+
+static struct nand_flash_dev *
+nand_find_id(unsigned char id) {
+ int i;
+
+ for (i = 0; i < SIZE(nand_flash_ids); i++)
+ if (nand_flash_ids[i].model_id == id)
+ return &(nand_flash_ids[i]);
+ return NULL;
+}
+
+/*
+ * ECC computation.
+ */
+static unsigned char parity[256];
+static unsigned char ecc2[256];
+
+static void nand_init_ecc(void) {
+ int i, j, a;
+
+ parity[0] = 0;
+ for (i = 1; i < 256; i++)
+ parity[i] = (parity[i&(i-1)] ^ 1);
+
+ for (i = 0; i < 256; i++) {
+ a = 0;
+ for (j = 0; j < 8; j++) {
+ if (i & (1<<j)) {
+ if ((j & 1) == 0)
+ a ^= 0x04;
+ if ((j & 2) == 0)
+ a ^= 0x10;
+ if ((j & 4) == 0)
+ a ^= 0x40;
+ }
+ }
+ ecc2[i] = ~(a ^ (a<<1) ^ (parity[i] ? 0xa8 : 0));
+ }
+}
+
+/* compute 3-byte ecc on 256 bytes */
+static void nand_compute_ecc(unsigned char *data, unsigned char *ecc) {
+ int i, j, a;
+ unsigned char par, bit, bits[8];
+
+ par = 0;
+ for (j = 0; j < 8; j++)
+ bits[j] = 0;
+
+ /* collect 16 checksum bits */
+ for (i = 0; i < 256; i++) {
+ par ^= data[i];
+ bit = parity[data[i]];
+ for (j = 0; j < 8; j++)
+ if ((i & (1<<j)) == 0)
+ bits[j] ^= bit;
+ }
+
+ /* put 4+4+4 = 12 bits in the ecc */
+ a = (bits[3] << 6) + (bits[2] << 4) + (bits[1] << 2) + bits[0];
+ ecc[0] = ~(a ^ (a<<1) ^ (parity[par] ? 0xaa : 0));
+
+ a = (bits[7] << 6) + (bits[6] << 4) + (bits[5] << 2) + bits[4];
+ ecc[1] = ~(a ^ (a<<1) ^ (parity[par] ? 0xaa : 0));
+
+ ecc[2] = ecc2[par];
+}
+
+static int nand_compare_ecc(unsigned char *data, unsigned char *ecc) {
+ return (data[0] == ecc[0] && data[1] == ecc[1] && data[2] == ecc[2]);
+}
+
+static void nand_store_ecc(unsigned char *data, unsigned char *ecc) {
+ memcpy(data, ecc, 3);
+}
+
+/*
+ * The actual driver starts here.
+ */
+
+/*
+ * On my 16MB card, control blocks have size 64 (16 real control bytes,
+ * and 48 junk bytes). In reality of course the card uses 16 control bytes,
+ * so the reader makes up the remaining 48. Don't know whether these numbers
+ * depend on the card. For now a constant.
+ */
+#define CONTROL_SHIFT 6
+
+/*
+ * On my Combo CF/SM reader, the SM reader has LUN 1.
+ * (and things fail with LUN 0).
+ * It seems LUN is irrelevant for others.
+ */
+#define LUN 1
+#define LUNBITS (LUN << 5)
+
+/*
+ * LBA and PBA are unsigned ints. Special values.
+ */
+#define UNDEF 0xffffffff
+#define SPARE 0xfffffffe
+#define UNUSABLE 0xfffffffd
+
+static int erase_bad_lba_entries = 0;
+
+/* send vendor interface command (0x41) */
+/* called for requests 0, 1, 8 */
+static int
+sddr09_send_command(struct us_data *us,
+ unsigned char request,
+ unsigned char direction,
+ unsigned char *xfer_data,
+ unsigned int xfer_len) {
+ unsigned int pipe;
+ unsigned char requesttype = (0x41 | direction);
+ int rc;
+
+ // Get the receive or send control pipe number
+
+ if (direction == USB_DIR_IN)
+ pipe = us->recv_ctrl_pipe;
+ else
+ pipe = us->send_ctrl_pipe;
+
+ rc = usb_stor_ctrl_transfer(us, pipe, request, requesttype,
+ 0, 0, xfer_data, xfer_len);
+ return (rc == USB_STOR_XFER_GOOD ? USB_STOR_TRANSPORT_GOOD :
+ USB_STOR_TRANSPORT_ERROR);
+}
+
+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 != USB_STOR_TRANSPORT_GOOD) {
+ US_DEBUGP("request sense failed\n");
+ return result;
+ }
+
+ result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
+ sensebuf, buflen, NULL);
+ if (result != USB_STOR_XFER_GOOD) {
+ US_DEBUGP("request sense bulk in failed\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ } else {
+ US_DEBUGP("request sense worked\n");
+ return USB_STOR_TRANSPORT_GOOD;
+ }
+}
+
+/*
+ * 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<<pageshift, 32 = 1<<blockshift, 64 is constant?]
+ *
+ * (1 MB and 2 MB cards are a bit different, but I have only a 16 MB card.)
+ */
+
+static int
+sddr09_readX(struct us_data *us, int x, unsigned long fromaddress,
+ int nr_of_pages, int bulklen, unsigned char *buf,
+ int use_sg) {
+
+ unsigned char *command = us->iobuf;
+ int result;
+
+ command[0] = 0xE8;
+ command[1] = LUNBITS | x;
+ command[2] = MSB_of(fromaddress>>16);
+ command[3] = LSB_of(fromaddress>>16);
+ command[4] = MSB_of(fromaddress & 0xFFFF);
+ command[5] = LSB_of(fromaddress & 0xFFFF);
+ command[6] = 0;
+ command[7] = 0;
+ command[8] = 0;
+ command[9] = 0;
+ command[10] = MSB_of(nr_of_pages);
+ command[11] = LSB_of(nr_of_pages);
+
+ result = sddr09_send_scsi_command(us, command, 12);
+
+ if (result != USB_STOR_TRANSPORT_GOOD) {
+ 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 USB_STOR_TRANSPORT_ERROR;
+ }
+ return USB_STOR_TRANSPORT_GOOD;
+}
+
+/*
+ * 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 != USB_STOR_TRANSPORT_GOOD)
+ 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 != USB_STOR_TRANSPORT_GOOD) {
+ 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 USB_STOR_TRANSPORT_ERROR;
+ }
+ return USB_STOR_TRANSPORT_GOOD;
+}
+
+/* erase address, write same address */
+static int
+sddr09_write_inplace(struct us_data *us, unsigned long address,
+ int nr_of_pages, int pageshift, unsigned char *buf,
+ int use_sg) {
+ int bulklen = (nr_of_pages << pageshift) + (nr_of_pages << CONTROL_SHIFT);
+ return sddr09_writeX(us, address, address, nr_of_pages, bulklen,
+ buf, use_sg);
+}
+
+#if 0
+/*
+ * Read Scatter Gather Command: 3+4n bytes.
+ * byte 0: opcode E7
+ * byte 2: n
+ * bytes 4i-1,4i,4i+1: page address
+ * byte 4i+2: page count
+ * (i=1..n)
+ *
+ * This reads several pages from the card to a single memory buffer.
+ * The last two bits of byte 1 have the same meaning as for E8.
+ */
+static int
+sddr09_read_sg_test_only(struct us_data *us) {
+ unsigned char *command = us->iobuf;
+ int result, bulklen, nsg, ct;
+ unsigned char *buf;
+ unsigned long address;
+
+ nsg = bulklen = 0;
+ command[0] = 0xE7;
+ command[1] = LUNBITS;
+ command[2] = 0;
+ address = 040000; ct = 1;
+ nsg++;
+ bulklen += (ct << 9);
+ command[4*nsg+2] = ct;
+ command[4*nsg+1] = ((address >> 9) & 0xFF);
+ command[4*nsg+0] = ((address >> 17) & 0xFF);
+ command[4*nsg-1] = ((address >> 25) & 0xFF);
+
+ address = 0340000; ct = 1;
+ nsg++;
+ bulklen += (ct << 9);
+ command[4*nsg+2] = ct;
+ command[4*nsg+1] = ((address >> 9) & 0xFF);
+ command[4*nsg+0] = ((address >> 17) & 0xFF);
+ command[4*nsg-1] = ((address >> 25) & 0xFF);
+
+ address = 01000000; ct = 2;
+ nsg++;
+ bulklen += (ct << 9);
+ command[4*nsg+2] = ct;
+ command[4*nsg+1] = ((address >> 9) & 0xFF);
+ command[4*nsg+0] = ((address >> 17) & 0xFF);
+ command[4*nsg-1] = ((address >> 25) & 0xFF);
+
+ command[2] = nsg;
+
+ result = sddr09_send_scsi_command(us, command, 4*nsg+3);
+
+ if (result != USB_STOR_TRANSPORT_GOOD) {
+ US_DEBUGP("Result for send_control in sddr09_read_sg %d\n",
+ result);
+ return result;
+ }
+
+ buf = (unsigned char *) kmalloc(bulklen, GFP_NOIO);
+ if (!buf)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ 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 USB_STOR_TRANSPORT_ERROR;
+ }
+
+ return USB_STOR_TRANSPORT_GOOD;
+}
+#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 != USB_STOR_TRANSPORT_GOOD)
+ 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 ?
+ USB_STOR_TRANSPORT_GOOD : USB_STOR_TRANSPORT_ERROR);
+}
+
+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, index, offset;
+ 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.
+
+ len = min(sectors, (unsigned int) info->blocksize) * info->pagesize;
+ buffer = kmalloc(len, GFP_NOIO);
+ if (buffer == NULL) {
+ printk("sddr09_read_data: Out of memory\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ // Figure out the initial LBA and page
+ lba = address >> info->blockshift;
+ page = (address & info->blockmask);
+ maxlba = info->capacity >> (info->pageshift + info->blockshift);
+
+ // This could be made much more efficient by checking for
+ // contiguous LBA's. Another exercise left to the student.
+
+ result = USB_STOR_TRANSPORT_GOOD;
+ index = offset = 0;
+
+ 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 = USB_STOR_TRANSPORT_ERROR;
+ 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 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);
+
+ address = ((pba << info->blockshift) + page) <<
+ info->pageshift;
+
+ result = sddr09_read20(us, address>>1,
+ pages, info->pageshift, buffer, 0);
+ if (result != USB_STOR_TRANSPORT_GOOD)
+ break;
+ }
+
+ // Store the data in the transfer buffer
+ usb_stor_access_xfer_buf(buffer, len, us->srb,
+ &index, &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("sddr09_write_lba: Out of unused blocks\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+ 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("sddr09: avoid writing to pba 1\n");
+ return USB_STOR_TRANSPORT_GOOD;
+ }
+
+ 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 != USB_STOR_TRANSPORT_GOOD)
+ 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 != USB_STOR_TRANSPORT_GOOD)
+ 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, page, pages;
+ unsigned int pagelen, blocklen;
+ unsigned char *blockbuffer;
+ unsigned char *buffer;
+ unsigned int len, index, offset;
+ int result;
+
+ // 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("sddr09_write_data: Out of memory\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ // 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("sddr09_write_data: Out of memory\n");
+ kfree(blockbuffer);
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ // Figure out the initial LBA and page
+ lba = address >> info->blockshift;
+ page = (address & info->blockmask);
+
+ result = USB_STOR_TRANSPORT_GOOD;
+ index = offset = 0;
+
+ while (sectors > 0) {
+
+ // Write as many sectors as possible in this block
+
+ pages = min(sectors, info->blocksize - page);
+ len = (pages << info->pageshift);
+
+ // Get the data from the transfer buffer
+ usb_stor_access_xfer_buf(buffer, len, us->srb,
+ &index, &offset, FROM_XFER_BUF);
+
+ result = sddr09_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;
+}
+
+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 != USB_STOR_TRANSPORT_GOOD)
+ 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 ?
+ USB_STOR_TRANSPORT_GOOD : USB_STOR_TRANSPORT_ERROR);
+}
+
+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 != USB_STOR_TRANSPORT_GOOD) {
+ 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 USB_STOR_TRANSPORT_GOOD;
+}
+
+#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 != USB_STOR_TRANSPORT_GOOD) {
+ US_DEBUGP("Result of read_deviceID is %d\n", result);
+ printk("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("%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("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("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 != USB_STOR_TRANSPORT_GOOD) {
+ 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("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("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("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("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("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("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 void
+sddr09_init_card_info(struct us_data *us) {
+ if (!us->extra) {
+ us->extra = kmalloc(sizeof(struct sddr09_card_info), GFP_NOIO);
+ if (us->extra) {
+ memset(us->extra, 0, sizeof(struct sddr09_card_info));
+ us->extra_destructor = sddr09_card_info_destructor;
+ }
+ }
+}
+
+/*
+ * 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.
+ */
+int
+sddr09_init(struct us_data *us) {
+ int result;
+ unsigned char *data = us->iobuf;
+
+ result = sddr09_send_command(us, 0x01, USB_DIR_IN, data, 2);
+ if (result != USB_STOR_TRANSPORT_GOOD) {
+ 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 != USB_STOR_TRANSPORT_GOOD) {
+ 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 == USB_STOR_TRANSPORT_GOOD && 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 USB_STOR_TRANSPORT_GOOD; /* not result */
+}
+
+/*
+ * Transport for the Sandisk SDDR-09
+ */
+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 (!info) {
+ nand_init_ecc();
+ sddr09_init_card_info(us);
+ info = (struct sddr09_card_info *)us->extra;
+ if (!info)
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ 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);
+
+ return sddr09_read_data(us, page, pages);
+ }
+
+ 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);
+
+ return sddr09_write_data(us, page, pages);
+ }
+
+ /* 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 != USB_STOR_TRANSPORT_GOOD) {
+ US_DEBUGP("sddr09_transport: sddr09_send_scsi_command "
+ "returns %d\n", result);
+ return result;
+ }
+
+ if (srb->request_bufflen == 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",
+ srb->request_bufflen);
+
+ result = usb_stor_bulk_transfer_sg(us, pipe,
+ srb->request_buffer,
+ srb->request_bufflen,
+ srb->use_sg, &srb->resid);
+
+ return (result == USB_STOR_XFER_GOOD ?
+ USB_STOR_TRANSPORT_GOOD : USB_STOR_TRANSPORT_ERROR);
+ }
+
+ return USB_STOR_TRANSPORT_GOOD;
+}
+
diff --git a/drivers/usb/storage/sddr09.h b/drivers/usb/storage/sddr09.h
new file mode 100644
index 000000000000..c9d78d6188b1
--- /dev/null
+++ b/drivers/usb/storage/sddr09.h
@@ -0,0 +1,48 @@
+/* Driver for SanDisk SDDR-09 SmartMedia reader
+ * Header File
+ *
+ * $Id: sddr09.h,v 1.5 2000/08/25 00:13:51 mdharm Exp $
+ *
+ * Current development and maintenance by:
+ * (c) 2000 Robert Baruch (autophile@dol.net)
+ * (c) 2002 Andries Brouwer (aeb@cwi.nl)
+ *
+ * See sddr09.c for more explanation
+ *
+ * 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 _USB_SHUTTLE_EUSB_SDDR09_H
+#define _USB_SHUTTLE_EUSB_SDDR09_H
+
+/* Sandisk SDDR-09 stuff */
+
+extern int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us);
+
+struct sddr09_card_info {
+ unsigned long capacity; /* Size of card in bytes */
+ int pagesize; /* Size of page in bytes */
+ int pageshift; /* log2 of pagesize */
+ int blocksize; /* Size of block in pages */
+ int blockshift; /* log2 of blocksize */
+ int blockmask; /* 2^blockshift - 1 */
+ int *lba_to_pba; /* logical to physical map */
+ int *pba_to_lba; /* physical to logical map */
+ int lbact; /* number of available pages */
+ int flags;
+#define SDDR09_WP 1 /* write protected */
+};
+
+#endif
diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c
new file mode 100644
index 000000000000..229ca181716b
--- /dev/null
+++ b/drivers/usb/storage/sddr55.c
@@ -0,0 +1,938 @@
+/* Driver for SanDisk SDDR-55 SmartMedia reader
+ *
+ * $Id:$
+ *
+ * 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 <linux/jiffies.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+
+#include "usb.h"
+#include "transport.h"
+#include "protocol.h"
+#include "debug.h"
+#include "sddr55.h"
+
+
+#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 */
+ if (info->lba_to_pba)
+ kfree(info->lba_to_pba);
+ if (info->pba_to_lba)
+ 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, index, offset;
+
+ // 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 */
+ index = offset = 0;
+
+ 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,
+ &index, &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, index, offset;
+
+ /* 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;
+ index = offset = 0;
+
+ 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,
+ &index, &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;
+}
+
+
+int sddr55_reset(struct us_data *us) {
+ return 0;
+}
+
+
+static unsigned long sddr55_get_capacity(struct us_data *us) {
+
+ unsigned char manufacturerID;
+ unsigned char deviceID;
+ int result;
+ struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra;
+
+ 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;
+ }
+
+ if (info->lba_to_pba)
+ kfree(info->lba_to_pba);
+ if (info->pba_to_lba)
+ 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) {
+ if (info->lba_to_pba != NULL)
+ kfree(info->lba_to_pba);
+ if (info->pba_to_lba != NULL)
+ 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; i<numblocks; i++) {
+ int zone = i / 1024;
+
+ lba = short_pack(buffer[i * 2], buffer[i * 2 + 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. Yes, this wastes 24
+ * physical blocks per zone. Go figure.
+ * These devices can have blocks go bad, so there
+ * are 24 spare blocks to use when blocks do go bad.
+ */
+
+ /* SDDR55 returns 0xffff for a bad block, and 0x400 for the
+ * CIS block. (Is this true for cards 8MB or less??)
+ * Record these in the physical to logical map
+ */
+
+ info->pba_to_lba[i] = lba;
+
+ if (lba >= max_lba) {
+ continue;
+ }
+
+ if (info->lba_to_pba[lba + zone * 1000] != NOT_ALLOCATED &&
+ !info->force_read_only) {
+ printk("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;
+
+ if (info->lba_to_pba)
+ kfree(info->lba_to_pba);
+ if (info->pba_to_lba)
+ kfree(info->pba_to_lba);
+}
+
+
+/*
+ * Transport for the Sandisk SDDR-55
+ */
+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 = kmalloc(
+ sizeof(struct sddr55_card_info), GFP_NOIO);
+ if (!us->extra)
+ return USB_STOR_TRANSPORT_ERROR;
+ memset(us->extra, 0, sizeof(struct sddr55_card_info));
+ 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?
+}
+
diff --git a/drivers/usb/storage/sddr55.h b/drivers/usb/storage/sddr55.h
new file mode 100644
index 000000000000..d6bd32f6c9f3
--- /dev/null
+++ b/drivers/usb/storage/sddr55.h
@@ -0,0 +1,34 @@
+/* Driver for SanDisk SDDR-55 SmartMedia reader
+ * Header File
+ *
+ * $Id:$
+ *
+ * Current development and maintenance by:
+ * (c) 2002 Simon Munton
+ *
+ * See sddr55.c for more explanation
+ *
+ * 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 _USB_SHUTTLE_EUSB_SDDR55_H
+#define _USB_SHUTTLE_EUSB_SDDR55_H
+
+/* Sandisk SDDR-55 stuff */
+
+extern int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us);
+extern int sddr55_reset(struct us_data *us);
+
+#endif
diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c
new file mode 100644
index 000000000000..7eff03d9b041
--- /dev/null
+++ b/drivers/usb/storage/shuttle_usbat.c
@@ -0,0 +1,1712 @@
+/* Driver for SCM Microsystems USB-ATAPI cable
+ *
+ * $Id: shuttle_usbat.c,v 1.17 2002/04/22 03:39:43 mdharm Exp $
+ *
+ * Current development and maintenance by:
+ * (c) 2000, 2001 Robert Baruch (autophile@starband.net)
+ * (c) 2004, 2005 Daniel Drake <dsd@gentoo.org>
+ *
+ * Developed with the assistance of:
+ * (c) 2002 Alan Stern <stern@rowland.org>
+ *
+ * Flash support based on earlier work by:
+ * (c) 2002 Thomas Kreiling <usbdev@sm04.de>
+ *
+ * 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 <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/cdrom.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+
+#include "usb.h"
+#include "transport.h"
+#include "protocol.h"
+#include "debug.h"
+#include "shuttle_usbat.h"
+
+#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);
+
+/*
+ * Convenience function to produce an ATAPI read/write sectors command
+ * Use cmd=0x20 for read, cmd=0x30 for write
+ */
+static void usbat_pack_atapi_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,
+ unsigned char *data,
+ unsigned int len)
+{
+ if (len == 0)
+ return USB_STOR_XFER_GOOD;
+
+ US_DEBUGP("usbat_bulk_read: len = %d\n", len);
+ return usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, data, len, NULL);
+}
+
+/*
+ * Convenience function to perform a bulk write
+ */
+static int usbat_bulk_write(struct us_data *us,
+ unsigned char *data,
+ unsigned int len)
+{
+ if (len == 0)
+ return USB_STOR_XFER_GOOD;
+
+ US_DEBUGP("usbat_bulk_write: len = %d\n", len);
+ return usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, data, len, 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;
+
+ if (!us)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ rc = usbat_get_status(us, reply);
+ if (rc != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_FAILED;
+
+ if (*reply & 0x01 && *reply != 0x51) // error/check condition (0x51 is ok)
+ return USB_STOR_TRANSPORT_FAILED;
+
+ if (*reply & 0x20) // device fault
+ return USB_STOR_TRANSPORT_FAILED;
+
+ return USB_STOR_TRANSPORT_GOOD;
+}
+
+/*
+ * Stores critical information in internal registers in prepartion 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,
+ unsigned char *content,
+ unsigned short len)
+{
+ 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, content, len);
+ 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,
+ unsigned char *content,
+ unsigned short len,
+ int minutes)
+{
+ 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, content, len);
+ 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,
+ unsigned char *content,
+ 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; j<num_registers; j++) {
+ data[j<<1] = registers[j];
+ data[1+(j<<1)] = data_out[j];
+ }
+
+ result = usbat_bulk_write(us, data, num_registers*2);
+ if (result != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ }
+
+
+ //US_DEBUGP("Transfer %s %d bytes, sg buffers %d\n",
+ // direction == DMA_TO_DEVICE ? "out" : "in",
+ // len, use_sg);
+
+ result = usb_stor_bulk_transfer_sg(us,
+ pipe, content, len, use_sg, NULL);
+
+ /*
+ * If we get a stall on the bulk download, we'll retry
+ * the bulk download -- but not the SCSI command because
+ * in some sense the SCSI command is still 'active' and
+ * waiting for the data. Don't ask me why this should be;
+ * I'm only following what the Windoze driver did.
+ *
+ * Note that a stall for the test-and-read/write command means
+ * that the test failed. In this case we're testing to make
+ * sure that the device is error-free
+ * (i.e. bit 0 -- CHK -- of status is 0). The most likely
+ * hypothesis is that the USBAT chip somehow knows what
+ * the device will accept, but doesn't give the device any
+ * data until all data is received. Thus, the device would
+ * still be waiting for the first byte of data if a stall
+ * occurs, even if the stall implies that some data was
+ * transferred.
+ */
+
+ if (result == USB_STOR_XFER_SHORT ||
+ result == USB_STOR_XFER_STALLED) {
+
+ /*
+ * If we're reading and we stalled, then clear
+ * the bulk output pipe only the first time.
+ */
+
+ if (direction==DMA_FROM_DEVICE && i==0) {
+ if (usb_stor_clear_halt(us,
+ us->send_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; i<num_registers; i++) {
+ data[i<<1] = registers[i];
+ data[1+(i<<1)] = data_out[i];
+ }
+
+ // Send the data
+ result = usbat_bulk_write(us, data, num_registers*2);
+ if (result != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ if (usbat_get_device_type(us) == USBAT_DEV_HP8200)
+ return usbat_wait_not_busy(us, 0);
+ else
+ return USB_STOR_TRANSPORT_GOOD;
+}
+
+/*
+ * Conditionally read blocks from device:
+ * Allows us to read blocks from 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 read 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_read_blocks(struct us_data *us,
+ unsigned char *buffer,
+ int len)
+{
+ int result;
+ unsigned char *command = us->iobuf;
+
+ 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);
+ 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,
+ unsigned char *buffer,
+ int len)
+{
+ 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);
+ 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 existance
+ 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;
+
+ /*
+ * By examining the device signature after a reset, we can identify
+ * whether the device supports the ATAPI packet interface.
+ * The flash-devices do not support this, whereas the HP CDRW's obviously
+ * do.
+ *
+ * This method is not ideal, but works because no other devices have been
+ * produced based on the USBAT/USBAT02.
+ *
+ * Section 9.1 of the ATAPI-4 spec states (amongst other things) that
+ * after a device reset, a Cylinder low of 0x14 indicates that the device
+ * does support packet commands.
+ */
+ rc = usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_ME, &status);
+ if (rc != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ US_DEBUGP("usbat_identify_device: Cylinder low is %02X\n", status);
+
+ if (status == 0x14) {
+ // 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 rc;
+
+ if (!info->devicetype) {
+ rc = usbat_identify_device(us, info);
+ if (rc != USB_STOR_TRANSPORT_GOOD) {
+ US_DEBUGP("usbat_set_transport: Could not identify device\n");
+ return 1;
+ }
+ }
+
+ if (usbat_get_device_type(us) == USBAT_DEV_HP8200)
+ us->transport = usbat_hp8200e_transport;
+ else if (usbat_get_device_type(us) == USBAT_DEV_FLASH)
+ us->transport = usbat_flash_transport;
+
+ 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;
+
+ // ATAPI 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);
+ 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_idx = 0, sg_offset = 0;
+
+ 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;
+
+ // ATAPI command 0x20 (READ SECTORS)
+ usbat_pack_atapi_sector_cmd(command, thistime, sector, 0x20);
+
+ // Write/execute ATAPI 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);
+ 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_idx, &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_idx = 0, sg_offset = 0;
+
+ 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 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_idx, &sg_offset, FROM_XFER_BUF);
+
+ // ATAPI command 0x30 (WRITE SECTORS)
+ usbat_pack_atapi_sector_cmd(command, thistime, sector, 0x30);
+
+ // Write/execute ATAPI 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);
+ 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_segment = 0;
+ unsigned int sg_offset = 0;
+
+ US_DEBUGP("handle_read10: transfersize %d\n",
+ srb->transfersize);
+
+ if (srb->request_bufflen < 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,
+ srb->request_buffer,
+ srb->request_bufflen, srb->use_sg, 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 = srb->request_bufflen/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, srb->request_bufflen);
+ 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;
+
+ sg_segment = 0; // for keeping track of where we are in
+ sg_offset = 0; // the scatter/gather list
+
+ while (transferred != srb->request_bufflen) {
+
+ if (len > srb->request_bufflen - transferred)
+ len = srb->request_bufflen - 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_segment, &sg_offset, TO_XFER_BUF);
+
+ // Update the amount transferred and the sector number
+
+ transferred += len;
+ sector += len / srb->transfersize;
+
+ } // while transferred != srb->request_bufflen
+
+ kfree(buffer);
+ return result;
+}
+
+static int usbat_select_and_test_registers(struct us_data *us)
+{
+ int selector;
+ unsigned char *status = us->iobuf;
+ unsigned char max_selector = 0xB0;
+ if (usbat_get_device_type(us) == USBAT_DEV_FLASH)
+ max_selector = 0xA0;
+
+ // try device = master, then device = slave.
+
+ for (selector = 0xA0; selector <= max_selector; selector += 0x10) {
+
+ if (usbat_get_device_type(us) == USBAT_DEV_HP8200 &&
+ 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
+ */
+int init_usbat(struct us_data *us)
+{
+ 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 = kmalloc(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;
+ }
+ memset(us->extra, 0, sizeof(struct usbat_info));
+ 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");
+
+ // At this point, we need to detect which device we are using
+ if (usbat_set_transport(us, info))
+ return USB_STOR_TRANSPORT_ERROR;
+
+ US_DEBUGP("INIT 4\n");
+
+ if (usbat_get_device_type(us) == USBAT_DEV_HP8200) {
+ msleep(250);
+
+ // Write 0x80 to ISA port 0x3F
+ rc = usbat_write(us, USBAT_ISA, 0x3F, 0x80);
+ if (rc != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ US_DEBUGP("INIT 5\n");
+
+ // Read ISA port 0x27
+ rc = usbat_read(us, USBAT_ISA, 0x27, status);
+ if (rc != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ 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");
+ }
+
+ rc = usbat_select_and_test_registers(us);
+ if (rc != USB_STOR_TRANSPORT_GOOD)
+ return rc;
+
+ US_DEBUGP("INIT 8\n");
+
+ rc = usbat_read_user_io(us, status);
+ if (rc != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ US_DEBUGP("INIT 9\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 10\n");
+
+ rc = usbat_read_user_io(us, status);
+ if (rc != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ US_DEBUGP("INIT 11\n");
+
+ msleep(1400);
+
+ rc = usbat_read_user_io(us, status);
+ if (rc != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ US_DEBUGP("INIT 12\n");
+
+ rc = usbat_select_and_test_registers(us);
+ if (rc != USB_STOR_TRANSPORT_GOOD)
+ return rc;
+
+ US_DEBUGP("INIT 13\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 14\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;
+ char string[64];
+
+ len = srb->request_bufflen;
+
+ /* 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,
+ srb->request_buffer,
+ len, srb->use_sg, 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;
+ }
+
+ if ( (result = usbat_multiple_write(us,
+ registers, data, 7)) != 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!!!
+
+ if ( (result = usbat_write_block(us,
+ USBAT_ATA, srb->cmnd, 12,
+ srb->cmnd[0]==GPCMD_BLANK ? 75 : 10)) !=
+ 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, srb->request_buffer, len);
+
+ /* Debug-print the first 32 bytes of the transfer */
+
+ if (!srb->use_sg) {
+ string[0] = 0;
+ for (i=0; i<len && i<32; i++) {
+ sprintf(string+strlen(string), "%02X ",
+ ((unsigned char *)srb->request_buffer)[i]);
+ if ((i%16)==15) {
+ US_DEBUGP("%s\n", string);
+ string[0] = 0;
+ }
+ }
+ if (string[0]!=0)
+ US_DEBUGP("%s\n", string);
+ }
+ }
+
+ 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;
+
+ info->ssize = 0x200; // hard coded 512 byte sectors as per ATA spec
+ 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;
+}
+
+/*
+ * Default transport function. Attempts to detect which transport function
+ * should be called, makes it the new default, and calls it.
+ *
+ * This function should never be called. Our usbat_init() function detects the
+ * device type and changes the us->transport ptr to the transport function
+ * relevant to the device.
+ * However, we'll support this impossible(?) case anyway.
+ */
+int usbat_transport(struct scsi_cmnd *srb, struct us_data *us)
+{
+ struct usbat_info *info = (struct usbat_info*) (us->extra);
+
+ if (usbat_set_transport(us, info))
+ return USB_STOR_TRANSPORT_ERROR;
+
+ return us->transport(srb, us);
+}
+
diff --git a/drivers/usb/storage/shuttle_usbat.h b/drivers/usb/storage/shuttle_usbat.h
new file mode 100644
index 000000000000..5b8e867e2ae5
--- /dev/null
+++ b/drivers/usb/storage/shuttle_usbat.h
@@ -0,0 +1,123 @@
+/* Driver for SCM Microsystems USB-ATAPI cable
+ * Header File
+ *
+ * $Id: shuttle_usbat.h,v 1.5 2000/09/17 14:44:52 groovyjava Exp $
+ *
+ * Current development and maintenance by:
+ * (c) 2000 Robert Baruch (autophile@dol.net)
+ * (c) 2004, 2005 Daniel Drake <dsd@gentoo.org>
+ *
+ * See shuttle_usbat.c for more explanation
+ *
+ * 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 _USB_SHUTTLE_USBAT_H
+#define _USB_SHUTTLE_USBAT_H
+
+/* 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
+
+extern int usbat_transport(struct scsi_cmnd *srb, struct us_data *us);
+extern int init_usbat(struct us_data *us);
+
+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
+};
+
+#endif
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
new file mode 100644
index 000000000000..d2c3d2fa082e
--- /dev/null
+++ b/drivers/usb/storage/transport.c
@@ -0,0 +1,1215 @@
+/* Driver for USB Mass Storage compliant devices
+ *
+ * $Id: transport.c,v 1.47 2002/04/22 03:39:43 mdharm Exp $
+ *
+ * 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 <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 <linux/config.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+
+#include "usb.h"
+#include "transport.h"
+#include "protocol.h"
+#include "scsiglue.h"
+#include "debug.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->flags 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->flags
+ * 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->flags 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 because the URB_ASYNC_UNLINK flag is set, 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 pt_regs *regs)
+{
+ struct completion *urb_done_ptr = (struct completion *)urb->context;
+
+ complete(urb_done_ptr);
+}
+
+/* This is the timeout handler which will cancel an URB when its timeout
+ * expires.
+ */
+static void timeout_handler(unsigned long us_)
+{
+ struct us_data *us = (struct us_data *) us_;
+
+ if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->flags)) {
+ US_DEBUGP("Timeout -- cancelling URB\n");
+ usb_unlink_urb(us->current_urb);
+ }
+}
+
+/* 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;
+ struct timer_list to_timer;
+ int status;
+
+ /* don't submit URBs during abort/disconnect processing */
+ if (us->flags & ABORTING_OR_DISCONNECTING)
+ 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->actual_length = 0;
+ us->current_urb->error_count = 0;
+ us->current_urb->status = 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. */
+ us->current_urb->transfer_flags =
+ URB_ASYNC_UNLINK | URB_NO_SETUP_DMA_MAP;
+ 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;
+ us->current_urb->setup_dma = us->cr_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->flags);
+
+ /* did an abort/disconnect occur during the submission? */
+ if (us->flags & ABORTING_OR_DISCONNECTING) {
+
+ /* cancel the URB, if it hasn't been cancelled already */
+ if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->flags)) {
+ US_DEBUGP("-- cancelling URB\n");
+ usb_unlink_urb(us->current_urb);
+ }
+ }
+
+ /* submit the timeout timer, if a timeout was requested */
+ if (timeout > 0) {
+ init_timer(&to_timer);
+ to_timer.expires = jiffies + timeout;
+ to_timer.function = timeout_handler;
+ to_timer.data = (unsigned long) us;
+ add_timer(&to_timer);
+ }
+
+ /* wait for the completion of the URB */
+ wait_for_completion(&urb_done);
+ clear_bit(US_FLIDX_URB_ACTIVE, &us->flags);
+
+ /* clean up the timeout timer */
+ if (timeout > 0)
+ del_timer_sync(&to_timer);
+
+ /* 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",
+ __FUNCTION__, 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;
+}
+
+/* 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);
+
+ /* reset the endpoint toggle */
+ usb_settoggle(us->pusb_dev, usb_pipeendpoint(pipe),
+ usb_pipeout(pipe), 0);
+
+ US_DEBUGP("%s: result = %d\n", __FUNCTION__, result);
+ return result;
+}
+
+
+/*
+ * 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;
+
+ /* timeout or excessively long NAK */
+ case -ETIMEDOUT:
+ US_DEBUGP("-- timeout or NAK\n");
+ return USB_STOR_XFER_ERROR;
+
+ /* 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",
+ __FUNCTION__, 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);
+}
+
+/*
+ * 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", __FUNCTION__, 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", __FUNCTION__, 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);
+}
+
+/*
+ * 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/disconnect processing */
+ if (us->flags & ABORTING_OR_DISCONNECTING)
+ return USB_STOR_XFER_ERROR;
+
+ /* initialize the scatter-gather request block */
+ US_DEBUGP("%s: xfer %u bytes, %d entries\n", __FUNCTION__,
+ length, num_sg);
+ result = usb_sg_init(&us->current_sg, us->pusb_dev, pipe, 0,
+ sg, num_sg, length, SLAB_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->flags);
+
+ /* did an abort/disconnect occur during the submission? */
+ if (us->flags & ABORTING_OR_DISCONNECTING) {
+
+ /* cancel the request, if it hasn't been cancelled already */
+ if (test_and_clear_bit(US_FLIDX_SG_ACTIVE, &us->flags)) {
+ 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->flags);
+
+ 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);
+}
+
+/*
+ * 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;
+}
+
+/***********************************************************************
+ * Transport routines
+ ***********************************************************************/
+
+/* 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 */
+ srb->resid = 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->flags)) {
+ US_DEBUGP("-- command was aborted\n");
+ goto Handle_Abort;
+ }
+
+ /* 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");
+ us->transport_reset(us);
+ srb->result = DID_ERROR << 16;
+ return;
+ }
+
+ /* 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;
+ 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 == US_PR_CB || us->protocol == US_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;
+ }
+
+ /*
+ * 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 ((srb->resid > 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;
+ void* old_request_buffer;
+ unsigned short old_sg;
+ unsigned old_request_bufflen;
+ unsigned char old_sc_data_direction;
+ unsigned char old_cmd_len;
+ unsigned char old_cmnd[MAX_COMMAND_SIZE];
+ unsigned long old_serial_number;
+ int old_resid;
+
+ US_DEBUGP("Issuing auto-REQUEST_SENSE\n");
+
+ /* save the old command */
+ memcpy(old_cmnd, srb->cmnd, MAX_COMMAND_SIZE);
+ old_cmd_len = srb->cmd_len;
+
+ /* set the command and the LUN */
+ memset(srb->cmnd, 0, MAX_COMMAND_SIZE);
+ srb->cmnd[0] = REQUEST_SENSE;
+ srb->cmnd[1] = old_cmnd[1] & 0xE0;
+ srb->cmnd[4] = 18;
+
+ /* FIXME: we must do the protocol translation here */
+ if (us->subclass == US_SC_RBC || us->subclass == US_SC_SCSI)
+ srb->cmd_len = 6;
+ else
+ srb->cmd_len = 12;
+
+ /* set the transfer direction */
+ old_sc_data_direction = srb->sc_data_direction;
+ srb->sc_data_direction = DMA_FROM_DEVICE;
+
+ /* use the new buffer we have */
+ old_request_buffer = srb->request_buffer;
+ srb->request_buffer = srb->sense_buffer;
+
+ /* set the buffer length for transfer */
+ old_request_bufflen = srb->request_bufflen;
+ srb->request_bufflen = 18;
+
+ /* set up for no scatter-gather use */
+ old_sg = srb->use_sg;
+ srb->use_sg = 0;
+
+ /* change the serial number -- toggle the high bit*/
+ old_serial_number = srb->serial_number;
+ srb->serial_number ^= 0x80000000;
+
+ /* issue the auto-sense command */
+ old_resid = srb->resid;
+ srb->resid = 0;
+ temp_result = us->transport(us->srb, us);
+
+ /* let's clean up right away */
+ srb->resid = old_resid;
+ srb->request_buffer = old_request_buffer;
+ srb->request_bufflen = old_request_bufflen;
+ srb->use_sg = old_sg;
+ srb->serial_number = old_serial_number;
+ srb->sc_data_direction = old_sc_data_direction;
+ srb->cmd_len = old_cmd_len;
+ memcpy(srb->cmnd, old_cmnd, MAX_COMMAND_SIZE);
+
+ if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) {
+ US_DEBUGP("-- auto-sense aborted\n");
+ goto Handle_Abort;
+ }
+ 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
+ */
+ if (!(us->flags & US_FL_SCM_MULT_TARG))
+ us->transport_reset(us);
+ srb->result = DID_ERROR << 16;
+ return;
+ }
+
+ 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",
+ srb->sense_buffer[0],
+ srb->sense_buffer[2] & 0xf,
+ srb->sense_buffer[12],
+ srb->sense_buffer[13]);
+#ifdef CONFIG_USB_STORAGE_DEBUG
+ usb_stor_show_sense(
+ srb->sense_buffer[2] & 0xf,
+ srb->sense_buffer[12],
+ srb->sense_buffer[13]);
+#endif
+
+ /* set the result so the higher layers expect this data */
+ srb->result = SAM_STAT_CHECK_CONDITION;
+
+ /* 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 &&
+ /* Filemark 0, ignore EOM, ILI 0, no sense */
+ (srb->sense_buffer[2] & 0xaf) == 0 &&
+ /* No ASC or ASCQ */
+ srb->sense_buffer[12] == 0 &&
+ srb->sense_buffer[13] == 0) {
+ srb->result = SAM_STAT_GOOD;
+ srb->sense_buffer[0] = 0x0;
+ }
+ }
+
+ /* Did we transfer less than the minimum amount required? */
+ if (srb->result == SAM_STAT_GOOD &&
+ srb->request_bufflen - srb->resid < srb->underflow)
+ srb->result = (DID_ERROR << 16) | (SUGGEST_RETRY << 24);
+
+ return;
+
+ /* abort processing: the bulk-only transport requires a reset
+ * following an abort */
+ Handle_Abort:
+ srb->result = DID_ABORT << 16;
+ if (us->protocol == US_PR_BULK)
+ us->transport_reset(us);
+}
+
+/* Stop the current URB transfer */
+void usb_stor_stop_transport(struct us_data *us)
+{
+ US_DEBUGP("%s called\n", __FUNCTION__);
+
+ /* 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->flags)) {
+ 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->flags)) {
+ US_DEBUGP("-- cancelling sg request\n");
+ usb_sg_cancel(&us->current_sg);
+ }
+}
+
+/*
+ * Control/Bulk/Interrupt transport
+ */
+
+int usb_stor_CBI_transport(struct scsi_cmnd *srb, struct us_data *us)
+{
+ unsigned int transfer_length = srb->request_bufflen;
+ 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_transfer_sg(us, pipe,
+ srb->request_buffer, transfer_length,
+ srb->use_sg, &srb->resid);
+ 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 */
+ 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 == US_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;
+}
+
+/*
+ * Control/Bulk transport
+ */
+int usb_stor_CB_transport(struct scsi_cmnd *srb, struct us_data *us)
+{
+ unsigned int transfer_length = srb->request_bufflen;
+ 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) {
+ unsigned int pipe = srb->sc_data_direction == DMA_FROM_DEVICE ?
+ us->recv_bulk_pipe : us->send_bulk_pipe;
+ result = usb_stor_bulk_transfer_sg(us, pipe,
+ srb->request_buffer, transfer_length,
+ srb->use_sg, &srb->resid);
+ US_DEBUGP("CB 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.
+ */
+ return USB_STOR_TRANSPORT_GOOD;
+}
+
+/*
+ * 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 */
+ 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, 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 (i.e. Iomega Zip100) need this -- apparently
+ * the bulk pipes get STALLed when the GetMaxLUN request is
+ * processed. This is, in theory, harmless to all other devices
+ * (regardless of if they stall or not).
+ */
+ if (result == -EPIPE) {
+ usb_stor_clear_halt(us, us->recv_bulk_pipe);
+ usb_stor_clear_halt(us, us->send_bulk_pipe);
+ }
+
+ /*
+ * 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 = srb->request_bufflen;
+ 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->flags & 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 ? 1 << 7 : 0;
+ bcb->Tag = srb->serial_number;
+ bcb->Lun = srb->device->lun;
+ if (us->flags & 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->flags & US_FL_GO_SLOW))
+ udelay(110);
+
+ 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_transfer_sg(us, pipe,
+ srb->request_buffer, transfer_length,
+ srb->use_sg, &srb->resid);
+ 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 != srb->serial_number || 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) {
+ if (!(us->flags & US_FL_IGNORE_RESIDUE)) {
+ residue = min(residue, transfer_length);
+ srb->resid = max(srb->resid, (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;
+}
+
+/***********************************************************************
+ * 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 20-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;
+ int rc = FAILED;
+
+ /* Let the SCSI layer know we are doing a reset, set the
+ * RESETTING bit, and clear the ABORTING bit so that the reset
+ * may proceed.
+ */
+ scsi_lock(us_to_host(us));
+ usb_stor_report_device_reset(us);
+ set_bit(US_FLIDX_RESETTING, &us->flags);
+ clear_bit(US_FLIDX_ABORTING, &us->flags);
+ scsi_unlock(us_to_host(us));
+
+ /* A 20-second timeout may seem rather long, but a LaCie
+ * StudioDrive USB2 device takes 16+ seconds to get going
+ * following a powerup or USB attach event.
+ */
+ result = usb_stor_control_msg(us, us->send_ctrl_pipe,
+ request, requesttype, value, index, data, size,
+ 20*HZ);
+ if (result < 0) {
+ US_DEBUGP("Soft reset failed: %d\n", result);
+ goto Done;
+ }
+
+ /* 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->flags),
+ HZ*6);
+ if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+ US_DEBUGP("Reset interrupted by disconnect\n");
+ goto Done;
+ }
+
+ 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 control message */
+ if (result < 0 || result2 < 0) {
+ US_DEBUGP("Soft reset failed\n");
+ goto Done;
+ }
+ US_DEBUGP("Soft reset done\n");
+ rc = SUCCESS;
+
+ Done:
+ clear_bit(US_FLIDX_RESETTING, &us->flags);
+ return rc;
+}
+
+/* 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", __FUNCTION__);
+
+ 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);
+}
+
+/* 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", __FUNCTION__);
+
+ return usb_stor_reset_common(us, US_BULK_RESET_REQUEST,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, us->ifnum, NULL, 0);
+}
diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h
new file mode 100644
index 000000000000..e25f8d8fc741
--- /dev/null
+++ b/drivers/usb/storage/transport.h
@@ -0,0 +1,174 @@
+/* Driver for USB Mass Storage compliant devices
+ * Transport Functions Header File
+ *
+ * $Id: transport.h,v 1.18 2002/04/21 02:57:59 mdharm Exp $
+ *
+ * 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 <linux/config.h>
+#include <linux/blkdev.h>
+
+/* Protocols */
+
+#define US_PR_CBI 0x00 /* Control/Bulk/Interrupt */
+#define US_PR_CB 0x01 /* Control/Bulk w/o interrupt */
+#define US_PR_BULK 0x50 /* bulk only */
+#ifdef CONFIG_USB_STORAGE_USBAT
+#define US_PR_SCM_ATAPI 0x80 /* SCM-ATAPI bridge */
+#endif
+#ifdef CONFIG_USB_STORAGE_SDDR09
+#define US_PR_EUSB_SDDR09 0x81 /* SCM-SCSI bridge for SDDR-09 */
+#endif
+#ifdef CONFIG_USB_STORAGE_SDDR55
+#define US_PR_SDDR55 0x82 /* SDDR-55 (made up) */
+#endif
+#define US_PR_DPCM_USB 0xf0 /* Combination CB/SDDR09 */
+
+#ifdef CONFIG_USB_STORAGE_FREECOM
+#define US_PR_FREECOM 0xf1 /* Freecom */
+#endif
+
+#ifdef CONFIG_USB_STORAGE_DATAFAB
+#define US_PR_DATAFAB 0xf2 /* Datafab chipsets */
+#endif
+
+#ifdef CONFIG_USB_STORAGE_JUMPSHOT
+#define US_PR_JUMPSHOT 0xf3 /* Lexar Jumpshot */
+#endif
+
+#define US_PR_DEVICE 0xff /* Use device's value */
+
+/*
+ * Bulk only data structures
+ */
+
+/* command block wrapper */
+struct bulk_cb_wrap {
+ __le32 Signature; /* contains 'USBC' */
+ __u32 Tag; /* unique per command id */
+ __le32 DataTransferLength; /* size of data */
+ __u8 Flags; /* direction in bit 0 */
+ __u8 Lun; /* LUN normally 0 */
+ __u8 Length; /* of of the CDB */
+ __u8 CDB[16]; /* max command */
+};
+
+#define US_BULK_CB_WRAP_LEN 31
+#define US_BULK_CB_SIGN 0x43425355 /*spells out USBC */
+#define US_BULK_FLAG_IN 1
+#define US_BULK_FLAG_OUT 0
+
+/* command status wrapper */
+struct bulk_cs_wrap {
+ __le32 Signature; /* should = 'USBS' */
+ __u32 Tag; /* same as original command */
+ __le32 Residue; /* amount not transferred */
+ __u8 Status; /* see below */
+ __u8 Filler[18];
+};
+
+#define US_BULK_CS_WRAP_LEN 13
+#define US_BULK_CS_SIGN 0x53425355 /* spells out 'USBS' */
+#define US_BULK_STAT_OK 0
+#define US_BULK_STAT_FAIL 1
+#define US_BULK_STAT_PHASE 2
+
+/* bulk-only class specific requests */
+#define US_BULK_RESET_REQUEST 0xff
+#define US_BULK_GET_MAX_LUN 0xfe
+
+/*
+ * 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_CBI_transport(struct scsi_cmnd *, struct us_data*);
+
+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);
+
+#endif
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
new file mode 100644
index 000000000000..d53f777c4f50
--- /dev/null
+++ b/drivers/usb/storage/unusual_devs.h
@@ -0,0 +1,986 @@
+/* Driver for USB Mass Storage compliant devices
+ * Ununsual Devices File
+ *
+ * $Id: unusual_devs.h,v 1.32 2002/02/25 02:41:24 mdharm Exp $
+ *
+ * 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 macro UNUSUAL_DEV() must be defined before this file is included
+ */
+#include <linux/config.h>
+
+/* 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 <phil@ipom.com> or
+ * Alan Stern <stern@rowland.harvard.edu>, and don't forget to CC: the
+ * USB development list <linux-usb-devel@lists.sourceforge.net>.
+ */
+
+UNUSUAL_DEV( 0x03ee, 0x6901, 0x0000, 0x0100,
+ "Mitsumi",
+ "USB FDD",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_SINGLE_LUN ),
+
+UNUSUAL_DEV( 0x03f0, 0x0107, 0x0200, 0x0200,
+ "HP",
+ "CD-Writer+",
+ US_SC_8070, US_PR_CB, NULL, 0),
+
+#ifdef CONFIG_USB_STORAGE_USBAT
+UNUSUAL_DEV( 0x03f0, 0x0207, 0x0001, 0x0001,
+ "HP",
+ "CD-Writer+ 8200e",
+ US_SC_8070, US_PR_SCM_ATAPI, init_usbat, 0),
+
+UNUSUAL_DEV( 0x03f0, 0x0307, 0x0001, 0x0001,
+ "HP",
+ "CD-Writer+ CD-4e",
+ US_SC_8070, US_PR_SCM_ATAPI, init_usbat, 0),
+#endif
+
+/* Deduced by Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
+ * 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",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_INQUIRY ),
+
+#ifdef CONFIG_USB_STORAGE_DPCM
+UNUSUAL_DEV( 0x0436, 0x0005, 0x0100, 0x0100,
+ "Microtech",
+ "CameraMate (DPCM_USB)",
+ US_SC_SCSI, US_PR_DPCM_USB, NULL, 0 ),
+#endif
+
+/* Patch submitted by Philipp Friedrich <philipp@void.at> */
+UNUSUAL_DEV( 0x0482, 0x0100, 0x0100, 0x0100,
+ "Kyocera",
+ "Finecam S3x",
+ US_SC_8070, US_PR_CB, NULL, US_FL_FIX_INQUIRY),
+
+/* Patch submitted by Philipp Friedrich <philipp@void.at> */
+UNUSUAL_DEV( 0x0482, 0x0101, 0x0100, 0x0100,
+ "Kyocera",
+ "Finecam S4",
+ US_SC_8070, US_PR_CB, NULL, US_FL_FIX_INQUIRY),
+
+/* Patch submitted by Stephane Galles <stephane.galles@free.fr> */
+UNUSUAL_DEV( 0x0482, 0x0103, 0x0100, 0x0100,
+ "Kyocera",
+ "Finecam S5",
+ US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY),
+
+/* Patch for Kyocera Finecam L3
+ * Submitted by Michael Krauth <michael.krauth@web.de>
+ * and Alessandro Fracchetti <al.fracchetti@tin.it>
+ */
+UNUSUAL_DEV( 0x0482, 0x0105, 0x0100, 0x0100,
+ "Kyocera",
+ "Finecam L3",
+ US_SC_SCSI, US_PR_BULK, NULL,
+ US_FL_FIX_INQUIRY),
+
+/* Reported by Paul Stewart <stewart@wetlogic.net>
+ * This entry is needed because the device reports Sub=ff */
+UNUSUAL_DEV( 0x04a4, 0x0004, 0x0001, 0x0001,
+ "Hitachi",
+ "DVD-CAM DZ-MV100A Camcorder",
+ US_SC_SCSI, US_PR_CB, NULL, US_FL_SINGLE_LUN),
+
+/* Reported by Andreas Bockhold <andreas@bockionline.de> */
+UNUSUAL_DEV( 0x04b0, 0x0405, 0x0100, 0x0100,
+ "NIKON",
+ "NIKON DSC D70",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY),
+
+/* BENQ DC5330
+ * Reported by Manuel Fombuena <mfombuena@ya.com> and
+ * Frank Copeland <fjc@thingy.apana.org.au> */
+UNUSUAL_DEV( 0x04a5, 0x3010, 0x0100, 0x0100,
+ "Tekom Technologies, Inc",
+ "300_CAMERA",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
+/* Reported by Simon Levitt <simon@whattf.com>
+ * This entry needs Sub and Proto fields */
+UNUSUAL_DEV( 0x04b8, 0x0601, 0x0100, 0x0100,
+ "Epson",
+ "875DC Storage",
+ US_SC_SCSI, US_PR_CB, NULL, US_FL_FIX_INQUIRY),
+
+/* Reported by Khalid Aziz <khalid@gonehiking.org>
+ * This entry is needed because the device reports Sub=ff */
+UNUSUAL_DEV( 0x04b8, 0x0602, 0x0110, 0x0110,
+ "Epson",
+ "785EPX Storage",
+ US_SC_SCSI, US_PR_BULK, NULL, US_FL_SINGLE_LUN),
+
+/* Not sure who reported this originally but
+ * Pavel Machek <pavel@ucw.cz> reported that the extra US_FL_SINGLE_LUN
+ * flag be added */
+UNUSUAL_DEV( 0x04cb, 0x0100, 0x0000, 0x2210,
+ "Fujifilm",
+ "FinePix 1400Zoom",
+ US_SC_UFI, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY | US_FL_SINGLE_LUN),
+
+/* Reported by Peter Wächtler <pwaechtler@loewe-komp.de>
+ * The device needs the flags only.
+ */
+UNUSUAL_DEV( 0x04ce, 0x0002, 0x0074, 0x0074,
+ "ScanLogic",
+ "SL11R-IDE",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_INQUIRY),
+
+/* Reported by Kriston Fincher <kriston@airmail.net>
+ * Patch submitted by Sean Millichamp <sean@bruenor.org>
+ * 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",
+ US_SC_UFI, US_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",
+ US_SC_8070, US_PR_CB, NULL, 0),
+
+/* Reported by Adriaan Penning <a.penning@luon.net> */
+UNUSUAL_DEV( 0x04da, 0x2372, 0x0000, 0x9999,
+ "Panasonic",
+ "DMC-LCx Camera",
+ US_SC_DEVICE, US_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",
+ US_SC_8020, US_PR_CB, NULL, 0),
+
+UNUSUAL_DEV( 0x04e6, 0x0002, 0x0100, 0x0100,
+ "Shuttle",
+ "eUSCSI Bridge",
+ US_SC_DEVICE, US_PR_DEVICE, usb_stor_euscsi_init,
+ US_FL_SCM_MULT_TARG ),
+
+#ifdef CONFIG_USB_STORAGE_SDDR09
+UNUSUAL_DEV( 0x04e6, 0x0003, 0x0000, 0x9999,
+ "Sandisk",
+ "ImageMate SDDR09",
+ US_SC_SCSI, US_PR_EUSB_SDDR09, NULL,
+ US_FL_SINGLE_LUN ),
+
+/* This entry is from Andries.Brouwer@cwi.nl */
+UNUSUAL_DEV( 0x04e6, 0x0005, 0x0100, 0x0208,
+ "SCM Microsystems",
+ "eUSB SmartMedia / CompactFlash Adapter",
+ US_SC_SCSI, US_PR_DPCM_USB, sddr09_init,
+ 0),
+#endif
+
+/* Reported by Markus Demleitner <msdemlei@cl.uni-heidelberg.de> */
+UNUSUAL_DEV( 0x04e6, 0x0006, 0x0100, 0x0100,
+ "SCM Microsystems Inc.",
+ "eUSB MMC Adapter",
+ US_SC_SCSI, US_PR_CB, NULL,
+ US_FL_SINGLE_LUN),
+
+/* Reported by Daniel Nouri <dpunktnpunkt@web.de> */
+UNUSUAL_DEV( 0x04e6, 0x0006, 0x0205, 0x0205,
+ "Shuttle",
+ "eUSB MMC Adapter",
+ US_SC_SCSI, US_PR_DEVICE, NULL,
+ US_FL_SINGLE_LUN),
+
+UNUSUAL_DEV( 0x04e6, 0x0007, 0x0100, 0x0200,
+ "Sony",
+ "Hifd",
+ US_SC_SCSI, US_PR_CB, NULL,
+ US_FL_SINGLE_LUN),
+
+UNUSUAL_DEV( 0x04e6, 0x0009, 0x0200, 0x0200,
+ "Shuttle",
+ "eUSB ATA/ATAPI Adapter",
+ US_SC_8020, US_PR_CB, NULL, 0),
+
+UNUSUAL_DEV( 0x04e6, 0x000a, 0x0200, 0x0200,
+ "Shuttle",
+ "eUSB CompactFlash Adapter",
+ US_SC_8020, US_PR_CB, NULL, 0),
+
+UNUSUAL_DEV( 0x04e6, 0x000B, 0x0100, 0x0100,
+ "Shuttle",
+ "eUSCSI Bridge",
+ US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init,
+ US_FL_SCM_MULT_TARG ),
+
+UNUSUAL_DEV( 0x04e6, 0x000C, 0x0100, 0x0100,
+ "Shuttle",
+ "eUSCSI Bridge",
+ US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init,
+ US_FL_SCM_MULT_TARG ),
+
+UNUSUAL_DEV( 0x04e6, 0x0101, 0x0200, 0x0200,
+ "Shuttle",
+ "CD-RW Device",
+ US_SC_8020, US_PR_CB, NULL, 0),
+
+/* Entry and supporting patch by Theodore Kilgore <kilgota@auburn.edu>.
+ * 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",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_BULK32),
+
+#ifdef CONFIG_USB_STORAGE_USBAT
+UNUSUAL_DEV( 0x04e6, 0x1010, 0x0000, 0x9999,
+ "SCM",
+ "SCM USBAT-02",
+ US_SC_SCSI, US_PR_SCM_ATAPI, init_usbat,
+ US_FL_SINGLE_LUN),
+#endif
+
+/* Reported by Bob Sass <rls@vectordb.com> -- only rev 1.33 tested */
+UNUSUAL_DEV( 0x050d, 0x0115, 0x0133, 0x0133,
+ "Belkin",
+ "USB SCSI Adaptor",
+ US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init,
+ US_FL_SCM_MULT_TARG ),
+
+/* Iomega Clik! Drive
+ * Reported by David Chatenay <dchatenay@hotmail.com>
+ * The reason this is needed is not fully known.
+ */
+UNUSUAL_DEV( 0x0525, 0xa140, 0x0100, 0x0100,
+ "Iomega",
+ "USB Clik! 40",
+ US_SC_8070, US_PR_BULK, NULL,
+ US_FL_FIX_INQUIRY ),
+
+/* Yakumo Mega Image 37
+ * Submitted by Stephan Fuhrmann <atomenergie@t-online.de> */
+UNUSUAL_DEV( 0x052b, 0x1801, 0x0100, 0x0100,
+ "Tekom Technologies, Inc",
+ "300_CAMERA",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
+/* Another Yakumo camera.
+ * Reported by Michele Alzetta <michele.alzetta@aliceposta.it> */
+UNUSUAL_DEV( 0x052b, 0x1804, 0x0100, 0x0100,
+ "Tekom Technologies, Inc",
+ "300_CAMERA",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
+/* Reported by Iacopo Spalletti <avvisi@spalletti.it> */
+UNUSUAL_DEV( 0x052b, 0x1807, 0x0100, 0x0100,
+ "Tekom Technologies, Inc",
+ "300_CAMERA",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
+/* Yakumo Mega Image 47
+ * Reported by Bjoern Paetzel <kolrabi@kolrabi.de> */
+UNUSUAL_DEV( 0x052b, 0x1905, 0x0100, 0x0100,
+ "Tekom Technologies, Inc",
+ "400_CAMERA",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
+/* Reported by Paul Ortyl <ortylp@3miasto.net>
+ * Note that it's similar to the device above, only different prodID */
+UNUSUAL_DEV( 0x052b, 0x1911, 0x0100, 0x0100,
+ "Tekom Technologies, Inc",
+ "400_CAMERA",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
+UNUSUAL_DEV( 0x054c, 0x0010, 0x0106, 0x0450,
+ "Sony",
+ "DSC-S30/S70/S75/505V/F505/F707/F717/P8",
+ US_SC_SCSI, US_PR_DEVICE, NULL,
+ US_FL_SINGLE_LUN | US_FL_NOT_LOCKABLE | US_FL_NO_WP_DETECT ),
+
+/* This entry is needed because the device reports Sub=ff */
+UNUSUAL_DEV( 0x054c, 0x0010, 0x0500, 0x0500,
+ "Sony",
+ "DSC-T1",
+ US_SC_8070, US_PR_DEVICE, NULL,
+ US_FL_SINGLE_LUN ),
+
+
+/* Reported by wim@geeks.nl */
+UNUSUAL_DEV( 0x054c, 0x0025, 0x0100, 0x0100,
+ "Sony",
+ "Memorystick NW-MS7",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_SINGLE_LUN ),
+
+#ifdef CONFIG_USB_STORAGE_ISD200
+UNUSUAL_DEV( 0x054c, 0x002b, 0x0100, 0x0110,
+ "Sony",
+ "Portable USB Harddrive V2",
+ US_SC_ISD200, US_PR_BULK, isd200_Initialization,
+ 0 ),
+#endif
+
+/* Submitted by Olaf Hering, <olh@suse.de> SuSE Bugzilla #49049 */
+UNUSUAL_DEV( 0x054c, 0x002c, 0x0501, 0x0501,
+ "Sony",
+ "USB Floppy Drive",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_SINGLE_LUN ),
+
+UNUSUAL_DEV( 0x054c, 0x002d, 0x0100, 0x0100,
+ "Sony",
+ "Memorystick MSAC-US1",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_SINGLE_LUN ),
+
+/* Submitted by Klaus Mueller <k.mueller@intershop.de> */
+UNUSUAL_DEV( 0x054c, 0x002e, 0x0106, 0x0310,
+ "Sony",
+ "Handycam",
+ US_SC_SCSI, US_PR_DEVICE, NULL,
+ US_FL_SINGLE_LUN ),
+
+/* Submitted by Rajesh Kumble Nayak <nayak@obs-nice.fr> */
+UNUSUAL_DEV( 0x054c, 0x002e, 0x0500, 0x0500,
+ "Sony",
+ "Handycam HC-85",
+ US_SC_UFI, US_PR_DEVICE, NULL,
+ US_FL_SINGLE_LUN ),
+
+UNUSUAL_DEV( 0x054c, 0x0032, 0x0000, 0x9999,
+ "Sony",
+ "Memorystick MSC-U01N",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_SINGLE_LUN ),
+
+/* Submitted by Michal Mlotek <mlotek@foobar.pl> */
+UNUSUAL_DEV( 0x054c, 0x0058, 0x0000, 0x9999,
+ "Sony",
+ "PEG N760c Memorystick",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_INQUIRY ),
+
+UNUSUAL_DEV( 0x054c, 0x0069, 0x0000, 0x9999,
+ "Sony",
+ "Memorystick MSC-U03",
+ US_SC_UFI, US_PR_CB, NULL,
+ US_FL_SINGLE_LUN ),
+
+/* Submitted by Nathan Babb <nathan@lexi.com> */
+UNUSUAL_DEV( 0x054c, 0x006d, 0x0000, 0x9999,
+ "Sony",
+ "PEG Mass Storage",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_INQUIRY ),
+
+/* Submitted by Mike Alborn <malborn@deandra.homeip.net> */
+UNUSUAL_DEV( 0x054c, 0x016a, 0x0000, 0x9999,
+ "Sony",
+ "PEG Mass Storage",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_INQUIRY ),
+
+/* Submitted by Frank Engel <frankie@cse.unsw.edu.au> */
+UNUSUAL_DEV( 0x054c, 0x0099, 0x0000, 0x9999,
+ "Sony",
+ "PEG Mass Storage",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_INQUIRY ),
+
+
+UNUSUAL_DEV( 0x057b, 0x0000, 0x0000, 0x0299,
+ "Y-E Data",
+ "Flashbuster-U",
+ US_SC_DEVICE, US_PR_CB, NULL,
+ US_FL_SINGLE_LUN),
+
+UNUSUAL_DEV( 0x057b, 0x0000, 0x0300, 0x9999,
+ "Y-E Data",
+ "Flashbuster-U",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_SINGLE_LUN),
+
+/* Reported by Johann Cardon <johann.cardon@free.fr>
+ * This entry is needed only because the device reports
+ * bInterfaceClass = 0xff (vendor-specific)
+ */
+UNUSUAL_DEV( 0x057b, 0x0022, 0x0000, 0x9999,
+ "Y-E Data",
+ "Silicon Media R/W",
+ US_SC_DEVICE, US_PR_DEVICE, NULL, 0),
+
+/* Fabrizio Fellini <fello@libero.it> */
+UNUSUAL_DEV( 0x0595, 0x4343, 0x0000, 0x2210,
+ "Fujifilm",
+ "Digital Camera EX-20 DSC",
+ US_SC_8070, US_PR_DEVICE, NULL, 0 ),
+
+/* The entry was here before I took over, and had US_SC_RBC. It turns
+ * out that isn't needed. Additionally, Torsten Eriksson
+ * <Torsten.Eriksson@bergianska.se> is able to use his device fine
+ * without this entry at all - but I don't suspect that will be true
+ * for all users (the protocol is likely needed), so is staying at
+ * this time. - Phil Dibowitz <phil@ipom.com>
+ */
+UNUSUAL_DEV( 0x059f, 0xa601, 0x0200, 0x0200,
+ "LaCie",
+ "USB Hard Disk",
+ US_SC_DEVICE, US_PR_CB, NULL, 0 ),
+
+/* Submitted by Joel Bourquard <numlock@freesurf.ch>
+ * Some versions of this device need the SubClass and Protocol overrides
+ * while others don't.
+ */
+UNUSUAL_DEV( 0x05ab, 0x0060, 0x1104, 0x1110,
+ "In-System",
+ "PyroGate External CD-ROM Enclosure (FCD-523)",
+ US_SC_SCSI, US_PR_BULK, NULL,
+ US_FL_NEED_OVERRIDE ),
+
+#ifdef CONFIG_USB_STORAGE_ISD200
+UNUSUAL_DEV( 0x05ab, 0x0031, 0x0100, 0x0110,
+ "In-System",
+ "USB/IDE Bridge (ATA/ATAPI)",
+ US_SC_ISD200, US_PR_BULK, isd200_Initialization,
+ 0 ),
+
+UNUSUAL_DEV( 0x05ab, 0x0301, 0x0100, 0x0110,
+ "In-System",
+ "Portable USB Harddrive V2",
+ US_SC_ISD200, US_PR_BULK, isd200_Initialization,
+ 0 ),
+
+UNUSUAL_DEV( 0x05ab, 0x0351, 0x0100, 0x0110,
+ "In-System",
+ "Portable USB Harddrive V2",
+ US_SC_ISD200, US_PR_BULK, isd200_Initialization,
+ 0 ),
+
+UNUSUAL_DEV( 0x05ab, 0x5701, 0x0100, 0x0110,
+ "In-System",
+ "USB Storage Adapter V2",
+ US_SC_ISD200, US_PR_BULK, isd200_Initialization,
+ 0 ),
+#endif
+
+/* Reported by Avi Kivity <avi@argo.co.il> */
+UNUSUAL_DEV( 0x05ac, 0x1203, 0x0001, 0x0001,
+ "Apple",
+ "iPod",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY ),
+
+UNUSUAL_DEV( 0x05ac, 0x1205, 0x0001, 0x0001,
+ "Apple",
+ "iPod",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY ),
+
+#ifdef CONFIG_USB_STORAGE_JUMPSHOT
+UNUSUAL_DEV( 0x05dc, 0x0001, 0x0000, 0x0001,
+ "Lexar",
+ "Jumpshot USB CF Reader",
+ US_SC_SCSI, US_PR_JUMPSHOT, NULL,
+ US_FL_NEED_OVERRIDE ),
+#endif
+
+/* Reported by Blake Matheny <bmatheny@purdue.edu> */
+UNUSUAL_DEV( 0x05dc, 0xb002, 0x0000, 0x0113,
+ "Lexar",
+ "USB CF Reader",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_INQUIRY ),
+
+/* The following two entries are for a Genesys USB to IDE
+ * converter chip, but it changes its ProductId depending
+ * on whether or not a disk or an optical device is enclosed
+ * They were originally reported by Alexander Oltu
+ * <alexander@all-2.com> and Peter Marks <peter.marks@turner.com>
+ * respectively.
+ */
+UNUSUAL_DEV( 0x05e3, 0x0701, 0x0000, 0xffff,
+ "Genesys Logic",
+ "USB to IDE Optical",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_GO_SLOW ),
+
+UNUSUAL_DEV( 0x05e3, 0x0702, 0x0000, 0xffff,
+ "Genesys Logic",
+ "USB to IDE Disk",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_GO_SLOW ),
+
+/* Reported by Hanno Boeck <hanno@gmx.de>
+ * Taken from the Lycoris Kernel */
+UNUSUAL_DEV( 0x0636, 0x0003, 0x0000, 0x9999,
+ "Vivitar",
+ "Vivicam 35Xx",
+ US_SC_SCSI, US_PR_BULK, NULL,
+ US_FL_FIX_INQUIRY ),
+
+UNUSUAL_DEV( 0x0644, 0x0000, 0x0100, 0x0100,
+ "TEAC",
+ "Floppy Drive",
+ US_SC_UFI, US_PR_CB, NULL, 0 ),
+
+#ifdef CONFIG_USB_STORAGE_SDDR09
+UNUSUAL_DEV( 0x066b, 0x0105, 0x0100, 0x0100,
+ "Olympus",
+ "Camedia MAUSB-2",
+ US_SC_SCSI, US_PR_EUSB_SDDR09, NULL,
+ US_FL_SINGLE_LUN ),
+#endif
+
+/* Reported by Darsen Lu <darsen@micro.ee.nthu.edu.tw> */
+UNUSUAL_DEV( 0x066f, 0x8000, 0x0001, 0x0001,
+ "SigmaTel",
+ "USBMSC Audio Player",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY ),
+
+/* Reported by Richard -=[]=- <micro_flyer@hotmail.com> */
+UNUSUAL_DEV( 0x067b, 0x2507, 0x0100, 0x0100,
+ "Prolific Technology Inc.",
+ "Mass Storage Device",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY | US_FL_GO_SLOW ),
+
+/* Reported by Alex Butcher <alex.butcher@assursys.co.uk> */
+UNUSUAL_DEV( 0x067b, 0x3507, 0x0001, 0x0001,
+ "Prolific Technology Inc.",
+ "ATAPI-6 Bridge Controller",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY | US_FL_GO_SLOW ),
+
+/* Submitted by Benny Sjostrand <benny@hostmobility.com> */
+UNUSUAL_DEV( 0x0686, 0x4011, 0x0001, 0x0001,
+ "Minolta",
+ "Dimage F300",
+ US_SC_SCSI, US_PR_BULK, NULL, 0 ),
+
+/* Reported by Miguel A. Fosas <amn3s1a@ono.com> */
+UNUSUAL_DEV( 0x0686, 0x4017, 0x0001, 0x0001,
+ "Minolta",
+ "DIMAGE E223",
+ US_SC_SCSI, US_PR_DEVICE, NULL, 0 ),
+
+UNUSUAL_DEV( 0x0693, 0x0002, 0x0100, 0x0100,
+ "Hagiwara",
+ "FlashGate SmartMedia",
+ US_SC_SCSI, US_PR_BULK, NULL, 0 ),
+
+UNUSUAL_DEV( 0x0693, 0x0005, 0x0100, 0x0100,
+ "Hagiwara",
+ "Flashgate",
+ US_SC_SCSI, US_PR_BULK, NULL, 0 ),
+
+UNUSUAL_DEV( 0x0781, 0x0001, 0x0200, 0x0200,
+ "Sandisk",
+ "ImageMate SDDR-05a",
+ US_SC_SCSI, US_PR_CB, NULL,
+ US_FL_SINGLE_LUN ),
+
+UNUSUAL_DEV( 0x0781, 0x0100, 0x0100, 0x0100,
+ "Sandisk",
+ "ImageMate SDDR-12",
+ US_SC_SCSI, US_PR_CB, NULL,
+ US_FL_SINGLE_LUN ),
+
+#ifdef CONFIG_USB_STORAGE_SDDR09
+UNUSUAL_DEV( 0x0781, 0x0200, 0x0000, 0x9999,
+ "Sandisk",
+ "ImageMate SDDR-09",
+ US_SC_SCSI, US_PR_EUSB_SDDR09, NULL,
+ US_FL_SINGLE_LUN ),
+#endif
+
+#ifdef CONFIG_USB_STORAGE_FREECOM
+UNUSUAL_DEV( 0x07ab, 0xfc01, 0x0000, 0x9999,
+ "Freecom",
+ "USB-IDE",
+ US_SC_QIC, US_PR_FREECOM, freecom_init, 0),
+#endif
+
+/* Reported by Eero Volotinen <eero@ping-viini.org> */
+UNUSUAL_DEV( 0x07ab, 0xfccd, 0x0406, 0x0406,
+ "Freecom Technologies",
+ "FHD-Classic",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY),
+
+UNUSUAL_DEV( 0x07af, 0x0004, 0x0100, 0x0133,
+ "Microtech",
+ "USB-SCSI-DB25",
+ US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init,
+ US_FL_SCM_MULT_TARG ),
+
+UNUSUAL_DEV( 0x07af, 0x0005, 0x0100, 0x0100,
+ "Microtech",
+ "USB-SCSI-HD50",
+ US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init,
+ US_FL_SCM_MULT_TARG ),
+
+#ifdef CONFIG_USB_STORAGE_DPCM
+UNUSUAL_DEV( 0x07af, 0x0006, 0x0100, 0x0100,
+ "Microtech",
+ "CameraMate (DPCM_USB)",
+ US_SC_SCSI, US_PR_DPCM_USB, NULL, 0 ),
+#endif
+
+#ifdef CONFIG_USB_STORAGE_DATAFAB
+UNUSUAL_DEV( 0x07c4, 0xa000, 0x0000, 0x0015,
+ "Datafab",
+ "MDCFE-B USB CF Reader",
+ US_SC_SCSI, US_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",
+ US_SC_SCSI, US_PR_DATAFAB, NULL,
+ 0 ),
+
+/* Reported by Josef Reisinger <josef.reisinger@netcologne.de> */
+UNUSUAL_DEV( 0x07c4, 0xa002, 0x0000, 0xffff,
+ "Datafab/Unknown",
+ "MD2/MD3 Disk enclosure",
+ US_SC_SCSI, US_PR_DATAFAB, NULL,
+ US_FL_SINGLE_LUN ),
+
+UNUSUAL_DEV( 0x07c4, 0xa003, 0x0000, 0xffff,
+ "Datafab/Unknown",
+ "Datafab-based Reader",
+ US_SC_SCSI, US_PR_DATAFAB, NULL,
+ 0 ),
+
+UNUSUAL_DEV( 0x07c4, 0xa004, 0x0000, 0xffff,
+ "Datafab/Unknown",
+ "Datafab-based Reader",
+ US_SC_SCSI, US_PR_DATAFAB, NULL,
+ 0 ),
+
+UNUSUAL_DEV( 0x07c4, 0xa005, 0x0000, 0xffff,
+ "PNY/Datafab",
+ "PNY/Datafab CF+SM Reader",
+ US_SC_SCSI, US_PR_DATAFAB, NULL,
+ 0 ),
+
+UNUSUAL_DEV( 0x07c4, 0xa006, 0x0000, 0xffff,
+ "Simple Tech/Datafab",
+ "Simple Tech/Datafab CF+SM Reader",
+ US_SC_SCSI, US_PR_DATAFAB, NULL,
+ 0 ),
+#endif
+
+#ifdef CONFIG_USB_STORAGE_SDDR55
+/* Contributed by Peter Waechtler */
+UNUSUAL_DEV( 0x07c4, 0xa103, 0x0000, 0x9999,
+ "Datafab",
+ "MDSM-B reader",
+ US_SC_SCSI, US_PR_SDDR55, NULL,
+ US_FL_FIX_INQUIRY ),
+#endif
+
+#ifdef CONFIG_USB_STORAGE_DATAFAB
+/* Submitted by Olaf Hering <olh@suse.de> */
+UNUSUAL_DEV( 0x07c4, 0xa109, 0x0000, 0xffff,
+ "Datafab Systems, Inc.",
+ "USB to CF + SM Combo (LC1)",
+ US_SC_SCSI, US_PR_DATAFAB, NULL,
+ 0 ),
+#endif
+#ifdef CONFIG_USB_STORAGE_SDDR55
+/* SM part - aeb <Andries.Brouwer@cwi.nl> */
+UNUSUAL_DEV( 0x07c4, 0xa109, 0x0000, 0xffff,
+ "Datafab Systems, Inc.",
+ "USB to CF + SM Combo (LC1)",
+ US_SC_SCSI, US_PR_SDDR55, NULL,
+ US_FL_SINGLE_LUN ),
+#endif
+
+/* Datafab KECF-USB / Sagatek DCS-CF / Simpletech Flashlink UCF-100
+ * Only revision 1.13 tested (same for all of the above devices,
+ * based on the Datafab DF-UG-07 chip). Needed for US_FL_FIX_INQUIRY.
+ * Submitted by Marek Michalkiewicz <marekm@amelek.gda.pl>.
+ * See also http://martin.wilck.bei.t-online.de/#kecf .
+ */
+UNUSUAL_DEV( 0x07c4, 0xa400, 0x0000, 0xffff,
+ "Datafab",
+ "KECF-USB",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_INQUIRY ),
+
+/* Casio QV 2x00/3x00/4000/8000 digital still cameras are not conformant
+ * to the USB storage specification in two ways:
+ * - They tell us they are using transport protocol CBI. In reality they
+ * are using transport protocol CB.
+ * - They don't like the INQUIRY command. So we must handle this command
+ * of the SCSI layer ourselves.
+ * - Some cameras with idProduct=0x1001 and bcdDevice=0x1000 have
+ * bInterfaceProtocol=0x00 (US_PR_CBI) while others have 0x01 (US_PR_CB).
+ * So don't remove the US_PR_CB override!
+ * - Cameras with bcdDevice=0x9009 require the US_SC_8070 override.
+ */
+UNUSUAL_DEV( 0x07cf, 0x1001, 0x1000, 0x9999,
+ "Casio",
+ "QV DigitalCamera",
+ US_SC_8070, US_PR_CB, NULL,
+ US_FL_NEED_OVERRIDE | US_FL_FIX_INQUIRY ),
+
+/* Submitted by Hartmut Wahl <hwahl@hwahl.de>*/
+UNUSUAL_DEV( 0x0839, 0x000a, 0x0001, 0x0001,
+ "Samsung",
+ "Digimax 410",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_INQUIRY),
+
+/* Entry and supporting patch by Theodore Kilgore <kilgota@auburn.edu>.
+ * Flag will support Bulk devices which use a standards-violating 32-byte
+ * Command Block Wrapper. Here, the "DC2MEGA" cameras (several brands) with
+ * Grandtech GT892x chip, which request "Proprietary SCSI Bulk" support.
+ */
+
+UNUSUAL_DEV( 0x084d, 0x0011, 0x0110, 0x0110,
+ "Grandtech",
+ "DC2MEGA",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_BULK32),
+
+
+/* Entry needed for flags. Moreover, all devices with this ID use
+ * bulk-only transport, but _some_ falsely report Control/Bulk instead.
+ * One example is "Trumpion Digital Research MYMP3".
+ * Submitted by Bjoern Brill <brill(at)fs.math.uni-frankfurt.de>
+ */
+UNUSUAL_DEV( 0x090a, 0x1001, 0x0100, 0x0100,
+ "Trumpion",
+ "t33520 USB Flash Card Controller",
+ US_SC_DEVICE, US_PR_BULK, NULL,
+ US_FL_NEED_OVERRIDE ),
+
+/* Trumpion Microelectronics MP3 player (felipe_alfaro@linuxmail.org) */
+UNUSUAL_DEV( 0x090a, 0x1200, 0x0000, 0x9999,
+ "Trumpion",
+ "MP3 player",
+ US_SC_RBC, US_PR_BULK, NULL,
+ 0 ),
+
+/* aeb */
+UNUSUAL_DEV( 0x090c, 0x1132, 0x0000, 0xffff,
+ "Feiya",
+ "5-in-1 Card Reader",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY ),
+
+/* This Pentax still camera is not conformant
+ * to the USB storage specification: -
+ * - It does not like the INQUIRY command. So we must handle this command
+ * of the SCSI layer ourselves.
+ * Tested on Rev. 10.00 (0x1000)
+ * Submitted by James Courtier-Dutton <James@superbug.demon.co.uk>
+ */
+UNUSUAL_DEV( 0x0a17, 0x0004, 0x1000, 0x1000,
+ "Pentax",
+ "Optio 2/3/400",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_INQUIRY ),
+
+
+/* Submitted by Per Winkvist <per.winkvist@uk.com> */
+UNUSUAL_DEV( 0x0a17, 0x006, 0x0000, 0xffff,
+ "Pentax",
+ "Optio S/S4",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_INQUIRY ),
+
+#ifdef CONFIG_USB_STORAGE_ISD200
+UNUSUAL_DEV( 0x0bf6, 0xa001, 0x0100, 0x0110,
+ "ATI",
+ "USB Cable 205",
+ US_SC_ISD200, US_PR_BULK, isd200_Initialization,
+ 0 ),
+#endif
+
+#ifdef CONFIG_USB_STORAGE_DATAFAB
+UNUSUAL_DEV( 0x0c0b, 0xa109, 0x0000, 0xffff,
+ "Acomdata",
+ "CF",
+ US_SC_SCSI, US_PR_DATAFAB, NULL,
+ US_FL_SINGLE_LUN ),
+#endif
+#ifdef CONFIG_USB_STORAGE_SDDR55
+UNUSUAL_DEV( 0x0c0b, 0xa109, 0x0000, 0xffff,
+ "Acomdata",
+ "SM",
+ US_SC_SCSI, US_PR_SDDR55, NULL,
+ US_FL_SINGLE_LUN ),
+#endif
+
+/* Submitted by Joris Struyve <joris@struyve.be> */
+UNUSUAL_DEV( 0x0d96, 0x410a, 0x0001, 0xffff,
+ "Medion",
+ "MD 7425",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_INQUIRY),
+
+/*
+ * Entry for Jenoptik JD 5200z3
+ *
+ * email: car.busse@gmx.de
+ */
+UNUSUAL_DEV( 0x0d96, 0x5200, 0x0001, 0x0200,
+ "Jenoptik",
+ "JD 5200 z3",
+ US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY),
+
+/* Reported by Lubomir Blaha <tritol@trilogic.cz>
+ * I _REALLY_ don't know what 3rd, 4th number and all defines mean, but this
+ * works for me. Can anybody correct these values? (I able to test corrected
+ * version.)
+ */
+UNUSUAL_DEV( 0x0dd8, 0x1060, 0x0000, 0xffff,
+ "Netac",
+ "USB-CF-Card",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_INQUIRY ),
+
+/* Patch by Stephan Walter <stephan.walter@epfl.ch>
+ * I don't know why, but it works... */
+UNUSUAL_DEV( 0x0dda, 0x0001, 0x0012, 0x0012,
+ "WINWARD",
+ "Music Disk",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
+/* Reported by Ian McConnell <ian at emit.demon.co.uk> */
+UNUSUAL_DEV( 0x0dda, 0x0301, 0x0012, 0x0012,
+ "PNP_MP3",
+ "PNP_MP3 PLAYER",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
+/* Submitted by Antoine Mairesse <antoine.mairesse@free.fr> */
+UNUSUAL_DEV( 0x0ed1, 0x6660, 0x0100, 0x0300,
+ "USB",
+ "Solid state disk",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_INQUIRY ),
+
+/* Submitted by Daniel Drake <dsd@gentoo.org>
+ * Reported by dayul on the Gentoo Forums */
+UNUSUAL_DEV( 0x0ea0, 0x2168, 0x0110, 0x0110,
+ "Ours Technology",
+ "Flash Disk",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
+/* Reported by Rastislav Stanik <rs_kernel@yahoo.com> */
+UNUSUAL_DEV( 0x0ea0, 0x6828, 0x0110, 0x0110,
+ "USB",
+ "Flash Disk",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
+/* Reported by Michael Stattmann <michael@stattmann.com> */
+UNUSUAL_DEV( 0x0fce, 0xd008, 0x0000, 0x0000,
+ "Sony Ericsson",
+ "V800-Vodafone 802",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_NO_WP_DETECT ),
+
+/* Reported by Kevin Cernekee <kpc-usbdev@gelato.uiuc.edu>
+ * Tested on hardware version 1.10.
+ * Entry is needed only for the initializer function override.
+ */
+UNUSUAL_DEV( 0x1019, 0x0c55, 0x0000, 0x9999,
+ "Desknote",
+ "UCR-61S2B",
+ US_SC_DEVICE, US_PR_DEVICE, usb_stor_ucr61s2b_init,
+ 0 ),
+
+/* Reported by Kotrla Vitezslav <kotrla@ceb.cz> */
+UNUSUAL_DEV( 0x1370, 0x6828, 0x0110, 0x0110,
+ "SWISSBIT",
+ "Black Silver",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
+/* Reported by Radovan Garabik <garabik@kassiopeia.juls.savba.sk> */
+UNUSUAL_DEV( 0x2735, 0x100b, 0x0000, 0x9999,
+ "MPIO",
+ "HS200",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_GO_SLOW ),
+
+#ifdef CONFIG_USB_STORAGE_SDDR55
+UNUSUAL_DEV( 0x55aa, 0xa103, 0x0000, 0x9999,
+ "Sandisk",
+ "ImageMate SDDR55",
+ US_SC_SCSI, US_PR_SDDR55, NULL,
+ US_FL_SINGLE_LUN),
+#endif
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
new file mode 100644
index 000000000000..35c1ca6b5a8e
--- /dev/null
+++ b/drivers/usb/storage/usb.c
@@ -0,0 +1,1051 @@
+/* Driver for USB Mass Storage compliant devices
+ *
+ * $Id: usb.c,v 1.75 2002/04/22 03:39:43 mdharm Exp $
+ *
+ * Current development and maintenance by:
+ * (c) 1999-2003 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+ * Developed with the assistance of:
+ * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org)
+ * (c) 2003 Alan Stern (stern@rowland.harvard.edu)
+ *
+ * Initial work by:
+ * (c) 1999 Michael Gee (michael@linuxspecific.com)
+ *
+ * usb_device_id support by Adam J. Richter (adam@yggdrasil.com):
+ * (c) 2000 Yggdrasil Computing, Inc.
+ *
+ * This driver is based on the 'USB Mass Storage Class' document. This
+ * describes in detail the protocol used to communicate with such
+ * devices. Clearly, the designers had SCSI and ATAPI commands in
+ * mind when they created this document. The commands are all very
+ * similar to commands in the SCSI-II and ATAPI specifications.
+ *
+ * It is important to note that in a number of cases this class
+ * exhibits class-specific exemptions from the USB specification.
+ * Notably the usage of NAK, STALL and ACK differs from the norm, in
+ * that they are used to communicate wait, failed and OK on commands.
+ *
+ * Also, for certain devices, the interrupt endpoint is used to convey
+ * status of a command.
+ *
+ * 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 <linux/config.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/suspend.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+
+#include "usb.h"
+#include "scsiglue.h"
+#include "transport.h"
+#include "protocol.h"
+#include "debug.h"
+#include "initializers.h"
+
+#ifdef CONFIG_USB_STORAGE_USBAT
+#include "shuttle_usbat.h"
+#endif
+#ifdef CONFIG_USB_STORAGE_SDDR09
+#include "sddr09.h"
+#endif
+#ifdef CONFIG_USB_STORAGE_SDDR55
+#include "sddr55.h"
+#endif
+#ifdef CONFIG_USB_STORAGE_DPCM
+#include "dpcm.h"
+#endif
+#ifdef CONFIG_USB_STORAGE_FREECOM
+#include "freecom.h"
+#endif
+#ifdef CONFIG_USB_STORAGE_ISD200
+#include "isd200.h"
+#endif
+#ifdef CONFIG_USB_STORAGE_DATAFAB
+#include "datafab.h"
+#endif
+#ifdef CONFIG_USB_STORAGE_JUMPSHOT
+#include "jumpshot.h"
+#endif
+
+
+/* Some informational data */
+MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
+MODULE_DESCRIPTION("USB Mass Storage driver for Linux");
+MODULE_LICENSE("GPL");
+
+static unsigned int delay_use = 5;
+module_param(delay_use, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device");
+
+
+/* These are used to make sure the module doesn't unload before all the
+ * threads have exited.
+ */
+static atomic_t total_threads = ATOMIC_INIT(0);
+static DECLARE_COMPLETION(threads_gone);
+
+
+static int storage_probe(struct usb_interface *iface,
+ const struct usb_device_id *id);
+
+static void storage_disconnect(struct usb_interface *iface);
+
+/* The entries in this table, except for final ones here
+ * (USB_MASS_STORAGE_CLASS and the empty entry), correspond,
+ * line for line with the entries of us_unsuaul_dev_list[].
+ */
+
+#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
+ vendorName, productName,useProtocol, useTransport, \
+ initFunction, flags) \
+{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin,bcdDeviceMax) }
+
+static struct usb_device_id storage_usb_ids [] = {
+
+# include "unusual_devs.h"
+#undef UNUSUAL_DEV
+ /* Control/Bulk transport for all SubClass values */
+ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_CB) },
+ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8020, US_PR_CB) },
+ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_QIC, US_PR_CB) },
+ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_UFI, US_PR_CB) },
+ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8070, US_PR_CB) },
+ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_CB) },
+
+ /* Control/Bulk/Interrupt transport for all SubClass values */
+ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_CBI) },
+ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8020, US_PR_CBI) },
+ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_QIC, US_PR_CBI) },
+ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_UFI, US_PR_CBI) },
+ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8070, US_PR_CBI) },
+ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_CBI) },
+
+ /* Bulk-only transport for all SubClass values */
+ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_BULK) },
+ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8020, US_PR_BULK) },
+ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_QIC, US_PR_BULK) },
+ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_UFI, US_PR_BULK) },
+ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8070, US_PR_BULK) },
+ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_BULK) },
+
+ /* Terminating entry */
+ { }
+};
+
+MODULE_DEVICE_TABLE (usb, storage_usb_ids);
+
+/* This is the list of devices we recognize, along with their flag data */
+
+/* The vendor name should be kept at eight characters or less, and
+ * the product name should be kept at 16 characters or less. If a device
+ * has the US_FL_FIX_INQUIRY flag, then the vendor and product names
+ * normally generated by a device thorugh the INQUIRY response will be
+ * taken from this list, and this is the reason for the above size
+ * restriction. However, if the flag is not present, then you
+ * are free to use as many characters as you like.
+ */
+
+#undef UNUSUAL_DEV
+#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, \
+ .flags = Flags, \
+}
+
+static struct us_unusual_dev us_unusual_dev_list[] = {
+# include "unusual_devs.h"
+# undef UNUSUAL_DEV
+ /* Control/Bulk transport for all SubClass values */
+ { .useProtocol = US_SC_RBC,
+ .useTransport = US_PR_CB},
+ { .useProtocol = US_SC_8020,
+ .useTransport = US_PR_CB},
+ { .useProtocol = US_SC_QIC,
+ .useTransport = US_PR_CB},
+ { .useProtocol = US_SC_UFI,
+ .useTransport = US_PR_CB},
+ { .useProtocol = US_SC_8070,
+ .useTransport = US_PR_CB},
+ { .useProtocol = US_SC_SCSI,
+ .useTransport = US_PR_CB},
+
+ /* Control/Bulk/Interrupt transport for all SubClass values */
+ { .useProtocol = US_SC_RBC,
+ .useTransport = US_PR_CBI},
+ { .useProtocol = US_SC_8020,
+ .useTransport = US_PR_CBI},
+ { .useProtocol = US_SC_QIC,
+ .useTransport = US_PR_CBI},
+ { .useProtocol = US_SC_UFI,
+ .useTransport = US_PR_CBI},
+ { .useProtocol = US_SC_8070,
+ .useTransport = US_PR_CBI},
+ { .useProtocol = US_SC_SCSI,
+ .useTransport = US_PR_CBI},
+
+ /* Bulk-only transport for all SubClass values */
+ { .useProtocol = US_SC_RBC,
+ .useTransport = US_PR_BULK},
+ { .useProtocol = US_SC_8020,
+ .useTransport = US_PR_BULK},
+ { .useProtocol = US_SC_QIC,
+ .useTransport = US_PR_BULK},
+ { .useProtocol = US_SC_UFI,
+ .useTransport = US_PR_BULK},
+ { .useProtocol = US_SC_8070,
+ .useTransport = US_PR_BULK},
+ { .useProtocol = US_SC_SCSI,
+ .useTransport = US_PR_BULK},
+
+ /* Terminating entry */
+ { NULL }
+};
+
+static struct usb_driver usb_storage_driver = {
+ .owner = THIS_MODULE,
+ .name = "usb-storage",
+ .probe = storage_probe,
+ .disconnect = storage_disconnect,
+ .id_table = storage_usb_ids,
+};
+
+/*
+ * fill_inquiry_response takes an unsigned char array (which must
+ * be at least 36 characters) and populates the vendor name,
+ * product name, and revision fields. Then the array is copied
+ * into the SCSI command's response buffer (oddly enough
+ * called request_buffer). data_len contains the length of the
+ * data array, which again must be at least 36.
+ */
+
+void fill_inquiry_response(struct us_data *us, unsigned char *data,
+ unsigned int data_len)
+{
+ if (data_len<36) // You lose.
+ return;
+
+ if(data[0]&0x20) { /* USB device currently not connected. Return
+ peripheral qualifier 001b ("...however, the
+ physical device is not currently connected
+ to this logical unit") and leave vendor and
+ product identification empty. ("If the target
+ does store some of the INQUIRY data on the
+ device, it may return zeros or ASCII spaces
+ (20h) in those fields until the data is
+ available from the device."). */
+ memset(data+8,0,28);
+ } else {
+ u16 bcdDevice = le16_to_cpu(us->pusb_dev->descriptor.bcdDevice);
+ memcpy(data+8, us->unusual_dev->vendorName,
+ strlen(us->unusual_dev->vendorName) > 8 ? 8 :
+ strlen(us->unusual_dev->vendorName));
+ memcpy(data+16, us->unusual_dev->productName,
+ strlen(us->unusual_dev->productName) > 16 ? 16 :
+ strlen(us->unusual_dev->productName));
+ data[32] = 0x30 + ((bcdDevice>>12) & 0x0F);
+ data[33] = 0x30 + ((bcdDevice>>8) & 0x0F);
+ data[34] = 0x30 + ((bcdDevice>>4) & 0x0F);
+ data[35] = 0x30 + ((bcdDevice) & 0x0F);
+ }
+
+ usb_stor_set_xfer_buf(data, data_len, us->srb);
+}
+
+static int usb_stor_control_thread(void * __us)
+{
+ struct us_data *us = (struct us_data *)__us;
+ struct Scsi_Host *host = us_to_host(us);
+
+ lock_kernel();
+
+ /*
+ * This thread doesn't need any user-level access,
+ * so get rid of all our resources.
+ */
+ daemonize("usb-storage");
+ current->flags |= PF_NOFREEZE;
+ unlock_kernel();
+
+ /* acquire a reference to the host, so it won't be deallocated
+ * until we're ready to exit */
+ scsi_host_get(host);
+
+ /* signal that we've started the thread */
+ complete(&(us->notify));
+
+ for(;;) {
+ US_DEBUGP("*** thread sleeping.\n");
+ if(down_interruptible(&us->sema))
+ break;
+
+ US_DEBUGP("*** thread awakened.\n");
+
+ /* lock the device pointers */
+ down(&(us->dev_semaphore));
+
+ /* if the device has disconnected, we are free to exit */
+ if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+ US_DEBUGP("-- exiting\n");
+ up(&(us->dev_semaphore));
+ break;
+ }
+
+ /* lock access to the state */
+ scsi_lock(host);
+
+ /* has the command timed out *already* ? */
+ if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) {
+ us->srb->result = DID_ABORT << 16;
+ goto SkipForAbort;
+ }
+
+ scsi_unlock(host);
+
+ /* reject the command if the direction indicator
+ * is UNKNOWN
+ */
+ if (us->srb->sc_data_direction == DMA_BIDIRECTIONAL) {
+ US_DEBUGP("UNKNOWN data direction\n");
+ us->srb->result = DID_ERROR << 16;
+ }
+
+ /* reject if target != 0 or if LUN is higher than
+ * the maximum known LUN
+ */
+ else if (us->srb->device->id &&
+ !(us->flags & US_FL_SCM_MULT_TARG)) {
+ US_DEBUGP("Bad target number (%d:%d)\n",
+ us->srb->device->id, us->srb->device->lun);
+ us->srb->result = DID_BAD_TARGET << 16;
+ }
+
+ else if (us->srb->device->lun > us->max_lun) {
+ US_DEBUGP("Bad LUN (%d:%d)\n",
+ us->srb->device->id, us->srb->device->lun);
+ us->srb->result = DID_BAD_TARGET << 16;
+ }
+
+ /* Handle those devices which need us to fake
+ * their inquiry data */
+ else if ((us->srb->cmnd[0] == INQUIRY) &&
+ (us->flags & US_FL_FIX_INQUIRY)) {
+ unsigned char data_ptr[36] = {
+ 0x00, 0x80, 0x02, 0x02,
+ 0x1F, 0x00, 0x00, 0x00};
+
+ US_DEBUGP("Faking INQUIRY command\n");
+ fill_inquiry_response(us, data_ptr, 36);
+ us->srb->result = SAM_STAT_GOOD;
+ }
+
+ /* we've got a command, let's do it! */
+ else {
+ US_DEBUG(usb_stor_show_command(us->srb));
+ us->proto_handler(us->srb, us);
+ }
+
+ /* lock access to the state */
+ scsi_lock(host);
+
+ /* indicate that the command is done */
+ if (us->srb->result != DID_ABORT << 16) {
+ US_DEBUGP("scsi cmd done, result=0x%x\n",
+ us->srb->result);
+ us->srb->scsi_done(us->srb);
+ } else {
+SkipForAbort:
+ US_DEBUGP("scsi command aborted\n");
+ }
+
+ /* If an abort request was received we need to signal that
+ * the abort has finished. The proper test for this is
+ * the TIMED_OUT flag, not srb->result == DID_ABORT, because
+ * a timeout/abort request might be received after all the
+ * USB processing was complete. */
+ if (test_bit(US_FLIDX_TIMED_OUT, &us->flags))
+ complete(&(us->notify));
+
+ /* finished working on this command */
+ us->srb = NULL;
+ scsi_unlock(host);
+
+ /* unlock the device pointers */
+ up(&(us->dev_semaphore));
+ } /* for (;;) */
+
+ scsi_host_put(host);
+
+ /* notify the exit routine that we're actually exiting now
+ *
+ * complete()/wait_for_completion() is similar to up()/down(),
+ * except that complete() is safe in the case where the structure
+ * is getting deleted in a parallel mode of execution (i.e. just
+ * after the down() -- that's necessary for the thread-shutdown
+ * case.
+ *
+ * complete_and_exit() goes even further than this -- it is safe in
+ * the case that the thread of the caller is going away (not just
+ * the structure) -- this is necessary for the module-remove case.
+ * This is important in preemption kernels, which transfer the flow
+ * of execution immediately upon a complete().
+ */
+ complete_and_exit(&threads_gone, 0);
+}
+
+/***********************************************************************
+ * Device probing and disconnecting
+ ***********************************************************************/
+
+/* Associate our private data with the USB device */
+static int associate_dev(struct us_data *us, struct usb_interface *intf)
+{
+ US_DEBUGP("-- %s\n", __FUNCTION__);
+
+ /* Fill in the device-related fields */
+ us->pusb_dev = interface_to_usbdev(intf);
+ us->pusb_intf = intf;
+ us->ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
+ US_DEBUGP("Vendor: 0x%04x, Product: 0x%04x, Revision: 0x%04x\n",
+ le16_to_cpu(us->pusb_dev->descriptor.idVendor),
+ le16_to_cpu(us->pusb_dev->descriptor.idProduct),
+ le16_to_cpu(us->pusb_dev->descriptor.bcdDevice));
+ US_DEBUGP("Interface Subclass: 0x%02x, Protocol: 0x%02x\n",
+ intf->cur_altsetting->desc.bInterfaceSubClass,
+ intf->cur_altsetting->desc.bInterfaceProtocol);
+
+ /* Store our private data in the interface */
+ usb_set_intfdata(intf, us);
+
+ /* Allocate the device-related DMA-mapped buffers */
+ us->cr = usb_buffer_alloc(us->pusb_dev, sizeof(*us->cr),
+ GFP_KERNEL, &us->cr_dma);
+ if (!us->cr) {
+ US_DEBUGP("usb_ctrlrequest allocation failed\n");
+ return -ENOMEM;
+ }
+
+ us->iobuf = usb_buffer_alloc(us->pusb_dev, US_IOBUF_SIZE,
+ GFP_KERNEL, &us->iobuf_dma);
+ if (!us->iobuf) {
+ US_DEBUGP("I/O buffer allocation failed\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+/* Get the unusual_devs entries and the string descriptors */
+static void get_device_info(struct us_data *us, int id_index)
+{
+ struct usb_device *dev = us->pusb_dev;
+ struct usb_interface_descriptor *idesc =
+ &us->pusb_intf->cur_altsetting->desc;
+ struct us_unusual_dev *unusual_dev = &us_unusual_dev_list[id_index];
+ struct usb_device_id *id = &storage_usb_ids[id_index];
+
+ /* Store the entries */
+ us->unusual_dev = unusual_dev;
+ us->subclass = (unusual_dev->useProtocol == US_SC_DEVICE) ?
+ idesc->bInterfaceSubClass :
+ unusual_dev->useProtocol;
+ us->protocol = (unusual_dev->useTransport == US_PR_DEVICE) ?
+ idesc->bInterfaceProtocol :
+ unusual_dev->useTransport;
+ us->flags = unusual_dev->flags;
+
+ /*
+ * This flag is only needed when we're in high-speed, so let's
+ * disable it if we're in full-speed
+ */
+ if (dev->speed != USB_SPEED_HIGH)
+ us->flags &= ~US_FL_GO_SLOW;
+
+ /* Log a message if a non-generic unusual_dev entry contains an
+ * unnecessary subclass or protocol override. This may stimulate
+ * reports from users that will help us remove unneeded entries
+ * from the unusual_devs.h table.
+ */
+ if (id->idVendor || id->idProduct) {
+ static char *msgs[3] = {
+ "an unneeded SubClass entry",
+ "an unneeded Protocol entry",
+ "unneeded SubClass and Protocol entries"};
+ struct usb_device_descriptor *ddesc = &dev->descriptor;
+ int msg = -1;
+
+ if (unusual_dev->useProtocol != US_SC_DEVICE &&
+ us->subclass == idesc->bInterfaceSubClass)
+ msg += 1;
+ if (unusual_dev->useTransport != US_PR_DEVICE &&
+ us->protocol == idesc->bInterfaceProtocol)
+ msg += 2;
+ if (msg >= 0 && !(unusual_dev->flags & US_FL_NEED_OVERRIDE))
+ printk(KERN_NOTICE USB_STORAGE "This device "
+ "(%04x,%04x,%04x S %02x P %02x)"
+ " has %s in unusual_devs.h\n"
+ " Please send a copy of this message to "
+ "<linux-usb-devel@lists.sourceforge.net>\n",
+ le16_to_cpu(ddesc->idVendor),
+ le16_to_cpu(ddesc->idProduct),
+ le16_to_cpu(ddesc->bcdDevice),
+ idesc->bInterfaceSubClass,
+ idesc->bInterfaceProtocol,
+ msgs[msg]);
+ }
+}
+
+/* Get the transport settings */
+static int get_transport(struct us_data *us)
+{
+ switch (us->protocol) {
+ case US_PR_CB:
+ us->transport_name = "Control/Bulk";
+ us->transport = usb_stor_CB_transport;
+ us->transport_reset = usb_stor_CB_reset;
+ us->max_lun = 7;
+ break;
+
+ case US_PR_CBI:
+ us->transport_name = "Control/Bulk/Interrupt";
+ us->transport = usb_stor_CBI_transport;
+ us->transport_reset = usb_stor_CB_reset;
+ us->max_lun = 7;
+ break;
+
+ case US_PR_BULK:
+ us->transport_name = "Bulk";
+ us->transport = usb_stor_Bulk_transport;
+ us->transport_reset = usb_stor_Bulk_reset;
+ break;
+
+#ifdef CONFIG_USB_STORAGE_USBAT
+ case US_PR_SCM_ATAPI:
+ us->transport_name = "SCM/ATAPI";
+ us->transport = usbat_transport;
+ us->transport_reset = usb_stor_CB_reset;
+ us->max_lun = 1;
+ break;
+#endif
+
+#ifdef CONFIG_USB_STORAGE_SDDR09
+ case US_PR_EUSB_SDDR09:
+ us->transport_name = "EUSB/SDDR09";
+ us->transport = sddr09_transport;
+ us->transport_reset = usb_stor_CB_reset;
+ us->max_lun = 0;
+ break;
+#endif
+
+#ifdef CONFIG_USB_STORAGE_SDDR55
+ case US_PR_SDDR55:
+ us->transport_name = "SDDR55";
+ us->transport = sddr55_transport;
+ us->transport_reset = sddr55_reset;
+ us->max_lun = 0;
+ break;
+#endif
+
+#ifdef CONFIG_USB_STORAGE_DPCM
+ case US_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;
+ break;
+#endif
+
+#ifdef CONFIG_USB_STORAGE_FREECOM
+ case US_PR_FREECOM:
+ us->transport_name = "Freecom";
+ us->transport = freecom_transport;
+ us->transport_reset = usb_stor_freecom_reset;
+ us->max_lun = 0;
+ break;
+#endif
+
+#ifdef CONFIG_USB_STORAGE_DATAFAB
+ case US_PR_DATAFAB:
+ us->transport_name = "Datafab Bulk-Only";
+ us->transport = datafab_transport;
+ us->transport_reset = usb_stor_Bulk_reset;
+ us->max_lun = 1;
+ break;
+#endif
+
+#ifdef CONFIG_USB_STORAGE_JUMPSHOT
+ case US_PR_JUMPSHOT:
+ us->transport_name = "Lexar Jumpshot Control/Bulk";
+ us->transport = jumpshot_transport;
+ us->transport_reset = usb_stor_Bulk_reset;
+ us->max_lun = 1;
+ break;
+#endif
+
+ default:
+ return -EIO;
+ }
+ US_DEBUGP("Transport: %s\n", us->transport_name);
+
+ /* fix for single-lun devices */
+ if (us->flags & US_FL_SINGLE_LUN)
+ us->max_lun = 0;
+ return 0;
+}
+
+/* Get the protocol settings */
+static int get_protocol(struct us_data *us)
+{
+ switch (us->subclass) {
+ case US_SC_RBC:
+ us->protocol_name = "Reduced Block Commands (RBC)";
+ us->proto_handler = usb_stor_transparent_scsi_command;
+ break;
+
+ case US_SC_8020:
+ us->protocol_name = "8020i";
+ us->proto_handler = usb_stor_ATAPI_command;
+ us->max_lun = 0;
+ break;
+
+ case US_SC_QIC:
+ us->protocol_name = "QIC-157";
+ us->proto_handler = usb_stor_qic157_command;
+ us->max_lun = 0;
+ break;
+
+ case US_SC_8070:
+ us->protocol_name = "8070i";
+ us->proto_handler = usb_stor_ATAPI_command;
+ us->max_lun = 0;
+ break;
+
+ case US_SC_SCSI:
+ us->protocol_name = "Transparent SCSI";
+ us->proto_handler = usb_stor_transparent_scsi_command;
+ break;
+
+ case US_SC_UFI:
+ us->protocol_name = "Uniform Floppy Interface (UFI)";
+ us->proto_handler = usb_stor_ufi_command;
+ break;
+
+#ifdef CONFIG_USB_STORAGE_ISD200
+ case US_SC_ISD200:
+ us->protocol_name = "ISD200 ATA/ATAPI";
+ us->proto_handler = isd200_ata_command;
+ break;
+#endif
+
+ default:
+ return -EIO;
+ }
+ US_DEBUGP("Protocol: %s\n", us->protocol_name);
+ return 0;
+}
+
+/* Get the pipe settings */
+static int get_pipes(struct us_data *us)
+{
+ struct usb_host_interface *altsetting =
+ us->pusb_intf->cur_altsetting;
+ int i;
+ struct usb_endpoint_descriptor *ep;
+ struct usb_endpoint_descriptor *ep_in = NULL;
+ struct usb_endpoint_descriptor *ep_out = NULL;
+ struct usb_endpoint_descriptor *ep_int = NULL;
+
+ /*
+ * Find the endpoints we need.
+ * We are expecting a minimum of 2 endpoints - in and out (bulk).
+ * An optional interrupt is OK (necessary for CBI protocol).
+ * We will ignore any others.
+ */
+ for (i = 0; i < altsetting->desc.bNumEndpoints; i++) {
+ ep = &altsetting->endpoint[i].desc;
+
+ /* Is it a BULK endpoint? */
+ if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_BULK) {
+ /* BULK in or out? */
+ if (ep->bEndpointAddress & USB_DIR_IN)
+ ep_in = ep;
+ else
+ ep_out = ep;
+ }
+
+ /* Is it an interrupt endpoint? */
+ else if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_INT) {
+ ep_int = ep;
+ }
+ }
+
+ if (!ep_in || !ep_out || (us->protocol == US_PR_CBI && !ep_int)) {
+ US_DEBUGP("Endpoint sanity check failed! Rejecting dev.\n");
+ return -EIO;
+ }
+
+ /* Calculate and store the pipe values */
+ us->send_ctrl_pipe = usb_sndctrlpipe(us->pusb_dev, 0);
+ us->recv_ctrl_pipe = usb_rcvctrlpipe(us->pusb_dev, 0);
+ us->send_bulk_pipe = usb_sndbulkpipe(us->pusb_dev,
+ ep_out->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ us->recv_bulk_pipe = usb_rcvbulkpipe(us->pusb_dev,
+ ep_in->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ if (ep_int) {
+ us->recv_intr_pipe = usb_rcvintpipe(us->pusb_dev,
+ ep_int->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ us->ep_bInterval = ep_int->bInterval;
+ }
+ return 0;
+}
+
+/* Initialize all the dynamic resources we need */
+static int usb_stor_acquire_resources(struct us_data *us)
+{
+ int p;
+
+ us->current_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!us->current_urb) {
+ US_DEBUGP("URB allocation failed\n");
+ return -ENOMEM;
+ }
+
+ /* Lock the device while we carry out the next two operations */
+ down(&us->dev_semaphore);
+
+ /* For bulk-only devices, determine the max LUN value */
+ if (us->protocol == US_PR_BULK) {
+ p = usb_stor_Bulk_max_lun(us);
+ if (p < 0) {
+ up(&us->dev_semaphore);
+ return p;
+ }
+ us->max_lun = p;
+ }
+
+ /* Just before we start our control thread, initialize
+ * the device if it needs initialization */
+ if (us->unusual_dev->initFunction)
+ us->unusual_dev->initFunction(us);
+
+ up(&us->dev_semaphore);
+
+ /* Start up our control thread */
+ p = kernel_thread(usb_stor_control_thread, us, CLONE_VM);
+ if (p < 0) {
+ printk(KERN_WARNING USB_STORAGE
+ "Unable to start control thread\n");
+ return p;
+ }
+ us->pid = p;
+ atomic_inc(&total_threads);
+
+ /* Wait for the thread to start */
+ wait_for_completion(&(us->notify));
+
+ return 0;
+}
+
+/* Release all our dynamic resources */
+static void usb_stor_release_resources(struct us_data *us)
+{
+ US_DEBUGP("-- %s\n", __FUNCTION__);
+
+ /* Tell the control thread to exit. The SCSI host must
+ * already have been removed so it won't try to queue
+ * any more commands.
+ */
+ US_DEBUGP("-- sending exit command to thread\n");
+ up(&us->sema);
+
+ /* Call the destructor routine, if it exists */
+ if (us->extra_destructor) {
+ US_DEBUGP("-- calling extra_destructor()\n");
+ us->extra_destructor(us->extra);
+ }
+
+ /* Free the extra data and the URB */
+ kfree(us->extra);
+ usb_free_urb(us->current_urb);
+}
+
+/* Dissociate from the USB device */
+static void dissociate_dev(struct us_data *us)
+{
+ US_DEBUGP("-- %s\n", __FUNCTION__);
+
+ /* Free the device-related DMA-mapped buffers */
+ if (us->cr)
+ usb_buffer_free(us->pusb_dev, sizeof(*us->cr), us->cr,
+ us->cr_dma);
+ if (us->iobuf)
+ usb_buffer_free(us->pusb_dev, US_IOBUF_SIZE, us->iobuf,
+ us->iobuf_dma);
+
+ /* Remove our private data from the interface */
+ usb_set_intfdata(us->pusb_intf, NULL);
+}
+
+/* Thread to carry out delayed SCSI-device scanning */
+static int usb_stor_scan_thread(void * __us)
+{
+ struct us_data *us = (struct us_data *)__us;
+
+ /*
+ * This thread doesn't need any user-level access,
+ * so get rid of all our resources.
+ */
+ lock_kernel();
+ daemonize("usb-stor-scan");
+ unlock_kernel();
+
+ /* Acquire a reference to the host, so it won't be deallocated
+ * until we're ready to exit */
+ scsi_host_get(us_to_host(us));
+
+ /* Signal that we've started the thread */
+ complete(&(us->notify));
+
+ printk(KERN_DEBUG
+ "usb-storage: device found at %d\n", us->pusb_dev->devnum);
+
+ /* Wait for the timeout to expire or for a disconnect */
+ if (delay_use > 0) {
+ printk(KERN_DEBUG "usb-storage: waiting for device "
+ "to settle before scanning\n");
+retry:
+ wait_event_interruptible_timeout(us->delay_wait,
+ test_bit(US_FLIDX_DISCONNECTING, &us->flags),
+ delay_use * HZ);
+ if (current->flags & PF_FREEZE) {
+ refrigerator(PF_FREEZE);
+ goto retry;
+ }
+ }
+
+ /* If the device is still connected, perform the scanning */
+ if (!test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+ scsi_scan_host(us_to_host(us));
+ printk(KERN_DEBUG "usb-storage: device scan complete\n");
+
+ /* Should we unbind if no devices were detected? */
+ }
+
+ scsi_host_put(us_to_host(us));
+ complete_and_exit(&threads_gone, 0);
+}
+
+
+/* Probe to see if we can drive a newly-connected USB device */
+static int storage_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct Scsi_Host *host;
+ struct us_data *us;
+ const int id_index = id - storage_usb_ids;
+ int result;
+
+ US_DEBUGP("USB Mass Storage device detected\n");
+
+ /*
+ * Ask the SCSI layer to allocate a host structure, with extra
+ * space at the end for our private us_data structure.
+ */
+ host = scsi_host_alloc(&usb_stor_host_template, sizeof(*us));
+ if (!host) {
+ printk(KERN_WARNING USB_STORAGE
+ "Unable to allocate the scsi host\n");
+ return -ENOMEM;
+ }
+
+ us = host_to_us(host);
+ memset(us, 0, sizeof(struct us_data));
+ init_MUTEX(&(us->dev_semaphore));
+ init_MUTEX_LOCKED(&(us->sema));
+ init_completion(&(us->notify));
+ init_waitqueue_head(&us->delay_wait);
+
+ /* Associate the us_data structure with the USB device */
+ result = associate_dev(us, intf);
+ if (result)
+ goto BadDevice;
+
+ /*
+ * Get the unusual_devs entries and the descriptors
+ *
+ * id_index is calculated in the declaration to be the index number
+ * of the match from the usb_device_id table, so we can find the
+ * corresponding entry in the private table.
+ */
+ get_device_info(us, id_index);
+
+#ifdef CONFIG_USB_STORAGE_SDDR09
+ if (us->protocol == US_PR_EUSB_SDDR09 ||
+ us->protocol == US_PR_DPCM_USB) {
+ /* 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);
+ goto BadDevice;
+ }
+ 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");
+ goto BadDevice;
+ }
+ }
+#endif
+
+ /* Get the transport, protocol, and pipe settings */
+ result = get_transport(us);
+ if (result)
+ goto BadDevice;
+ result = get_protocol(us);
+ if (result)
+ goto BadDevice;
+ result = get_pipes(us);
+ if (result)
+ goto BadDevice;
+
+ /* Acquire all the other resources and add the host */
+ result = usb_stor_acquire_resources(us);
+ if (result)
+ goto BadDevice;
+ result = scsi_add_host(host, &intf->dev);
+ if (result) {
+ printk(KERN_WARNING USB_STORAGE
+ "Unable to add the scsi host\n");
+ goto BadDevice;
+ }
+
+ /* Start up the thread for delayed SCSI-device scanning */
+ result = kernel_thread(usb_stor_scan_thread, us, CLONE_VM);
+ if (result < 0) {
+ printk(KERN_WARNING USB_STORAGE
+ "Unable to start the device-scanning thread\n");
+ scsi_remove_host(host);
+ goto BadDevice;
+ }
+ atomic_inc(&total_threads);
+
+ /* Wait for the thread to start */
+ wait_for_completion(&(us->notify));
+
+ return 0;
+
+ /* We come here if there are any problems */
+BadDevice:
+ US_DEBUGP("storage_probe() failed\n");
+ set_bit(US_FLIDX_DISCONNECTING, &us->flags);
+ usb_stor_release_resources(us);
+ dissociate_dev(us);
+ scsi_host_put(host);
+ return result;
+}
+
+/* Handle a disconnect event from the USB core */
+static void storage_disconnect(struct usb_interface *intf)
+{
+ struct us_data *us = usb_get_intfdata(intf);
+
+ US_DEBUGP("storage_disconnect() called\n");
+
+ /* Prevent new USB transfers, stop the current command, and
+ * interrupt a SCSI-scan or device-reset delay */
+ set_bit(US_FLIDX_DISCONNECTING, &us->flags);
+ usb_stor_stop_transport(us);
+ wake_up(&us->delay_wait);
+
+ /* It doesn't matter if the SCSI-scanning thread is still running.
+ * The thread will exit when it sees the DISCONNECTING flag. */
+
+ /* Wait for the current command to finish, then remove the host */
+ down(&us->dev_semaphore);
+ up(&us->dev_semaphore);
+ scsi_remove_host(us_to_host(us));
+
+ /* Wait for everything to become idle and release all our resources */
+ usb_stor_release_resources(us);
+ dissociate_dev(us);
+
+ /* Drop our reference to the host; the SCSI core will free it
+ * (and "us" along with it) when the refcount becomes 0. */
+ scsi_host_put(us_to_host(us));
+}
+
+/***********************************************************************
+ * Initialization and registration
+ ***********************************************************************/
+
+static int __init usb_stor_init(void)
+{
+ int retval;
+ printk(KERN_INFO "Initializing USB Mass Storage driver...\n");
+
+ /* register the driver, return usb_register return code if error */
+ retval = usb_register(&usb_storage_driver);
+ if (retval == 0)
+ printk(KERN_INFO "USB Mass Storage support registered.\n");
+
+ return retval;
+}
+
+static void __exit usb_stor_exit(void)
+{
+ US_DEBUGP("usb_stor_exit() called\n");
+
+ /* Deregister the driver
+ * This will cause disconnect() to be called for each
+ * attached unit
+ */
+ US_DEBUGP("-- calling usb_deregister()\n");
+ usb_deregister(&usb_storage_driver) ;
+
+ /* Don't return until all of our control and scanning threads
+ * have exited. Since each thread signals threads_gone as its
+ * last act, we have to call wait_for_completion the right number
+ * of times.
+ */
+ while (atomic_read(&total_threads) > 0) {
+ wait_for_completion(&threads_gone);
+ atomic_dec(&total_threads);
+ }
+}
+
+module_init(usb_stor_init);
+module_exit(usb_stor_exit);
diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
new file mode 100644
index 000000000000..625b7aa98074
--- /dev/null
+++ b/drivers/usb/storage/usb.h
@@ -0,0 +1,204 @@
+/* Driver for USB Mass Storage compliant devices
+ * Main Header File
+ *
+ * $Id: usb.h,v 1.21 2002/04/21 02:57:59 mdharm Exp $
+ *
+ * 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 _USB_H_
+#define _USB_H_
+
+#include <linux/usb.h>
+#include <linux/blkdev.h>
+#include <linux/smp_lock.h>
+#include <linux/completion.h>
+#include <scsi/scsi_host.h>
+
+struct us_data;
+struct scsi_cmnd;
+
+/*
+ * Unusual device list definitions
+ */
+
+struct us_unusual_dev {
+ const char* vendorName;
+ const char* productName;
+ __u8 useProtocol;
+ __u8 useTransport;
+ int (*initFunction)(struct us_data *);
+ unsigned int flags;
+};
+
+/*
+ * Static flag definitions. We use this roundabout technique so that the
+ * proc_info() routine can automatically display a message for each flag.
+ */
+#define US_DO_ALL_FLAGS \
+ US_FLAG(SINGLE_LUN, 0x00000001) \
+ /* allow access to only LUN 0 */ \
+ US_FLAG(NEED_OVERRIDE, 0x00000002) \
+ /* unusual_devs entry is necessary */ \
+ US_FLAG(SCM_MULT_TARG, 0x00000004) \
+ /* supports multiple targets */ \
+ US_FLAG(FIX_INQUIRY, 0x00000008) \
+ /* INQUIRY response needs faking */ \
+ US_FLAG(FIX_CAPACITY, 0x00000010) \
+ /* READ CAPACITY response too big */ \
+ US_FLAG(IGNORE_RESIDUE, 0x00000020) \
+ /* reported residue is wrong */ \
+ US_FLAG(BULK32, 0x00000040) \
+ /* Uses 32-byte CBW length */ \
+ US_FLAG(NOT_LOCKABLE, 0x00000080) \
+ /* PREVENT/ALLOW not supported */ \
+ US_FLAG(GO_SLOW, 0x00000100) \
+ /* Need delay after Command phase */ \
+ US_FLAG(NO_WP_DETECT, 0x00000200) \
+ /* Don't check for write-protect */ \
+
+#define US_FLAG(name, value) US_FL_##name = value ,
+enum { US_DO_ALL_FLAGS };
+#undef US_FLAG
+
+/* Dynamic flag definitions: used in set_bit() etc. */
+#define US_FLIDX_URB_ACTIVE 18 /* 0x00040000 current_urb is in use */
+#define US_FLIDX_SG_ACTIVE 19 /* 0x00080000 current_sg is in use */
+#define US_FLIDX_ABORTING 20 /* 0x00100000 abort is in progress */
+#define US_FLIDX_DISCONNECTING 21 /* 0x00200000 disconnect in progress */
+#define ABORTING_OR_DISCONNECTING ((1UL << US_FLIDX_ABORTING) | \
+ (1UL << US_FLIDX_DISCONNECTING))
+#define US_FLIDX_RESETTING 22 /* 0x00400000 device reset in progress */
+#define US_FLIDX_TIMED_OUT 23 /* 0x00800000 SCSI midlayer timed out */
+
+
+#define USB_STOR_STRING_LEN 32
+
+/*
+ * We provide a DMA-mapped I/O buffer for use with small USB transfers.
+ * It turns out that CB[I] needs a 12-byte buffer and Bulk-only needs a
+ * 31-byte buffer. But Freecom needs a 64-byte buffer, so that's the
+ * size we'll allocate.
+ */
+
+#define US_IOBUF_SIZE 64 /* Size of the DMA-mapped I/O buffer */
+
+typedef int (*trans_cmnd)(struct scsi_cmnd *, struct us_data*);
+typedef int (*trans_reset)(struct us_data*);
+typedef void (*proto_cmnd)(struct scsi_cmnd*, struct us_data*);
+typedef void (*extra_data_destructor)(void *); /* extra data destructor */
+
+/* we allocate one of these for every device that we remember */
+struct us_data {
+ /* The device we're working with
+ * It's important to note:
+ * (o) you must hold dev_semaphore to change pusb_dev
+ */
+ struct semaphore dev_semaphore; /* protect pusb_dev */
+ struct usb_device *pusb_dev; /* this usb_device */
+ struct usb_interface *pusb_intf; /* this interface */
+ struct us_unusual_dev *unusual_dev; /* device-filter entry */
+ unsigned long flags; /* from filter initially */
+ unsigned int send_bulk_pipe; /* cached pipe values */
+ unsigned int recv_bulk_pipe;
+ unsigned int send_ctrl_pipe;
+ unsigned int recv_ctrl_pipe;
+ unsigned int recv_intr_pipe;
+
+ /* information about the device */
+ char *transport_name;
+ char *protocol_name;
+ __le32 bcs_signature;
+ u8 subclass;
+ u8 protocol;
+ u8 max_lun;
+
+ u8 ifnum; /* interface number */
+ u8 ep_bInterval; /* interrupt interval */
+
+ /* function pointers for this device */
+ trans_cmnd transport; /* transport function */
+ trans_reset transport_reset; /* transport device reset */
+ proto_cmnd proto_handler; /* protocol handler */
+
+ /* SCSI interfaces */
+ struct scsi_cmnd *srb; /* current srb */
+
+ /* thread information */
+ int pid; /* control thread */
+
+ /* control and bulk communications data */
+ struct urb *current_urb; /* USB requests */
+ struct usb_ctrlrequest *cr; /* control requests */
+ struct usb_sg_request current_sg; /* scatter-gather req. */
+ unsigned char *iobuf; /* I/O buffer */
+ dma_addr_t cr_dma; /* buffer DMA addresses */
+ dma_addr_t iobuf_dma;
+
+ /* mutual exclusion and synchronization structures */
+ struct semaphore sema; /* to sleep thread on */
+ struct completion notify; /* thread begin/end */
+ wait_queue_head_t delay_wait; /* wait during scan, reset */
+
+ /* subdriver information */
+ void *extra; /* Any extra data */
+ extra_data_destructor extra_destructor;/* extra data destructor */
+};
+
+/* Convert between us_data and the corresponding Scsi_Host */
+static struct Scsi_Host inline *us_to_host(struct us_data *us) {
+ return container_of((void *) us, struct Scsi_Host, hostdata);
+}
+static struct us_data inline *host_to_us(struct Scsi_Host *host) {
+ return (struct us_data *) host->hostdata;
+}
+
+/* Function to fill an inquiry response. See usb.c for details */
+extern void fill_inquiry_response(struct us_data *us,
+ unsigned char *data, unsigned int data_len);
+
+/* The scsi_lock() and scsi_unlock() macros protect the sm_state and the
+ * single queue element srb for write access */
+#define scsi_unlock(host) spin_unlock_irq(host->host_lock)
+#define scsi_lock(host) spin_lock_irq(host->host_lock)
+
+
+/* Vendor ID list for devices that require special handling */
+#define USB_VENDOR_ID_GENESYS 0x05e3 /* Genesys Logic */
+
+#endif