patch-2.4.8 linux/drivers/usb/storage/isd200.c
Next file: linux/drivers/usb/storage/isd200.h
Previous file: linux/drivers/usb/storage/freecom.c
Back to the patch index
Back to the overall index
- Lines: 1748
- Date:
Sun Jul 29 21:11:50 2001
- Orig file:
v2.4.7/linux/drivers/usb/storage/isd200.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.4.7/linux/drivers/usb/storage/isd200.c linux/drivers/usb/storage/isd200.c
@@ -0,0 +1,1747 @@
+/* Transport & Protocol Driver for In-System Design, Inc. ISD200 ASIC
+ *
+ * First release
+ *
+ * Current development and maintenance by:
+ * (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:
+ *
+ * 2001-02-24: Removed lots of duplicate code and simplified the structure.
+ * (bjorn@haxx.se)
+ */
+
+
+/* Include files */
+
+#include "transport.h"
+#include "protocol.h"
+#include "usb.h"
+#include "debug.h"
+#include "scsiglue.h"
+#include "isd200.h"
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+/*
+ * Inquiry defines. Used to interpret data returned from target as result
+ * of inquiry command.
+ *
+ * DeviceType field
+ */
+
+#define DIRECT_ACCESS_DEVICE 0x00 /* disks */
+
+/* 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
+
+/* 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) */
+#define ISD200_TRANSPORT_ABORTED 3 /* Transport aborted */
+#define ISD200_TRANSPORT_SHORT 4 /* Transport short */
+
+/* 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 ReadRegisterAccessBit : 1;
+ unsigned char NoDeviceSelectionBit : 1;
+ unsigned char NoBSYPollBit : 1;
+ unsigned char IgnorePhaseErrorBit : 1;
+ unsigned char IgnoreDeviceErrorBit : 1;
+ unsigned char Reserved0Bit : 3;
+ unsigned char SelectAlternateStatus : 1;
+ unsigned char SelectError : 1;
+ unsigned char SelectSectorCount : 1;
+ unsigned char SelectSectorNumber : 1;
+ unsigned char SelectCylinderLow : 1;
+ unsigned char SelectCylinderHigh : 1;
+ unsigned char SelectDeviceHead : 1;
+ unsigned char SelectStatus : 1;
+ 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 ReadRegisterAccessBit : 1;
+ unsigned char NoDeviceSelectionBit : 1;
+ unsigned char NoBSYPollBit : 1;
+ unsigned char IgnorePhaseErrorBit : 1;
+ unsigned char IgnoreDeviceErrorBit : 1;
+ unsigned char Reserved0Bit : 3;
+ unsigned char SelectDeviceControl : 1;
+ unsigned char SelectFeatures : 1;
+ unsigned char SelectSectorCount : 1;
+ unsigned char SelectSectorNumber : 1;
+ unsigned char SelectCylinderLow : 1;
+ unsigned char SelectCylinderHigh : 1;
+ unsigned char SelectDeviceHead : 1;
+ unsigned char SelectCommand : 1;
+ 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.
+ */
+
+struct inquiry_data {
+ unsigned char DeviceType : 5;
+ unsigned char DeviceTypeQualifier : 3;
+ unsigned char DeviceTypeModifier : 7;
+ unsigned char RemovableMedia : 1;
+ unsigned char Versions;
+ unsigned char ResponseDataFormat : 4;
+ unsigned char HiSupport : 1;
+ unsigned char NormACA : 1;
+ unsigned char ReservedBit : 1;
+ unsigned char AERC : 1;
+ unsigned char AdditionalLength;
+ unsigned char Reserved[2];
+ unsigned char SoftReset : 1;
+ unsigned char CommandQueue : 1;
+ unsigned char Reserved2 : 1;
+ unsigned char LinkedCommands : 1;
+ unsigned char Synchronous : 1;
+ unsigned char Wide16Bit : 1;
+ unsigned char Wide32Bit : 1;
+ unsigned char RelativeAddressing : 1;
+ 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
+ */
+
+struct isd200_config {
+ unsigned char EventNotification;
+ unsigned char ExternalClock;
+ unsigned char ATAInitTimeout;
+ unsigned char ATATiming : 4;
+ unsigned char ATAPIReset : 1;
+ unsigned char MasterSlaveSelection : 1;
+ unsigned char ATAPICommandBlockSize : 2;
+ unsigned char ATAMajorCommand;
+ unsigned char ATAMinorCommand;
+ unsigned char LastLUNIdentifier : 3;
+ unsigned char DescriptOverride : 1;
+ unsigned char ATA3StateSuspend : 1;
+ unsigned char SkipDeviceBoot : 1;
+ unsigned char ConfigDescriptor2 : 1;
+ unsigned char InitStatus : 1;
+ unsigned char SRSTEnable : 1;
+ unsigned char Reserved0 : 7;
+};
+
+
+/*
+ * ISD200 driver information struct
+ */
+
+struct isd200_info {
+ struct inquiry_data InquiryData;
+ struct hd_driveid drive;
+ struct isd200_config ConfigData;
+ unsigned char ATARegs[8];
+ unsigned char DeviceHead;
+ unsigned char DeviceFlags;
+
+ /* maximum number of LUNs supported */
+ unsigned char MaxLUNs;
+};
+
+
+/*
+ * Read Capacity Data - returned in Big Endian format
+ */
+
+struct read_capacity_data {
+ unsigned long LogicalBlockAddress;
+ unsigned long 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
+ */
+
+struct sense_data {
+ unsigned char ErrorCode:7;
+ unsigned char Valid:1;
+ unsigned char SegmentNumber;
+ unsigned char SenseKey:4;
+ unsigned char Reserved:1;
+ unsigned char IncorrectLength:1;
+ unsigned char EndOfMedia:1;
+ unsigned char FileMark:1;
+ 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
+ */
+void isd200_build_sense(struct us_data *us, 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;
+ buf->Valid = 1;
+ buf->AdditionalSenseLength = 0xb;
+ buf->SenseKey = UNIT_ATTENTION;
+ buf->AdditionalSenseCode = 0;
+ buf->AdditionalSenseCodeQualifier = 0;
+ } else if(error & MCR_ERR) {
+ buf->ErrorCode = 0x70;
+ buf->Valid = 1;
+ buf->AdditionalSenseLength = 0xb;
+ buf->SenseKey = UNIT_ATTENTION;
+ buf->AdditionalSenseCode = 0;
+ buf->AdditionalSenseCodeQualifier = 0;
+ } else if(error & TRK0_ERR) {
+ buf->ErrorCode = 0x70;
+ buf->Valid = 1;
+ buf->AdditionalSenseLength = 0xb;
+ buf->SenseKey = NOT_READY;
+ buf->AdditionalSenseCode = 0;
+ buf->AdditionalSenseCodeQualifier = 0;
+ } else if(error & ECC_ERR) {
+ buf->ErrorCode = 0x70;
+ buf->Valid = 1;
+ buf->AdditionalSenseLength = 0xb;
+ buf->SenseKey = DATA_PROTECT;
+ buf->AdditionalSenseCode = 0;
+ buf->AdditionalSenseCodeQualifier = 0;
+ } else {
+ buf->ErrorCode = 0;
+ buf->Valid = 0;
+ buf->AdditionalSenseLength = 0;
+ buf->SenseKey = 0;
+ buf->AdditionalSenseCode = 0;
+ buf->AdditionalSenseCodeQualifier = 0;
+ }
+}
+
+/***********************************************************************
+ * Data transfer routines
+ ***********************************************************************/
+
+
+/**************************************************************************
+ * Transfer one SCSI scatter-gather buffer via bulk transfer
+ *
+ * Note that this function is necessary because we want the ability to
+ * use scatter-gather memory. Good performance is achieved by a combination
+ * of scatter-gather and clustering (which makes each chunk bigger).
+ *
+ * Note that the lower layer will always retry when a NAK occurs, up to the
+ * timeout limit. Thus we don't have to worry about it for individual
+ * packets.
+ */
+static int isd200_transfer_partial( struct us_data *us,
+ unsigned char dataDirection,
+ char *buf, int length )
+{
+ int result;
+ int partial;
+ int pipe;
+
+ /* calculate the appropriate pipe information */
+ if (dataDirection == SCSI_DATA_READ)
+ pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
+ else
+ pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+
+ /* transfer the data */
+ US_DEBUGP("isd200_transfer_partial(): xfer %d bytes\n", length);
+ result = usb_stor_bulk_msg(us, buf, pipe, length, &partial);
+ US_DEBUGP("usb_stor_bulk_msg() returned %d xferred %d/%d\n",
+ result, partial, length);
+
+ /* if we stall, we need to clear it before we go on */
+ if (result == -EPIPE) {
+ US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
+ usb_stor_clear_halt(us->pusb_dev, pipe);
+ }
+
+ /* did we send all the data? */
+ if (partial == length) {
+ US_DEBUGP("isd200_transfer_partial(): transfer complete\n");
+ return ISD200_TRANSPORT_GOOD;
+ }
+
+ /* uh oh... we have an error code, so something went wrong. */
+ if (result) {
+ /* NAK - that means we've retried a few times already */
+ if (result == -ETIMEDOUT) {
+ US_DEBUGP("isd200_transfer_partial(): device NAKed\n");
+ return ISD200_TRANSPORT_FAILED;
+ }
+
+ /* -ENOENT -- we canceled this transfer */
+ if (result == -ENOENT) {
+ US_DEBUGP("isd200_transfer_partial(): transfer aborted\n");
+ return ISD200_TRANSPORT_ABORTED;
+ }
+
+ /* the catch-all case */
+ US_DEBUGP("isd200_transfer_partial(): unknown error\n");
+ return ISD200_TRANSPORT_FAILED;
+ }
+
+ /* no error code, so we must have transferred some data,
+ * just not all of it */
+ return ISD200_TRANSPORT_SHORT;
+}
+
+
+/**************************************************************************
+ * Transfer an entire SCSI command's worth of data payload over the bulk
+ * pipe.
+ *
+ * Note that this uses us_transfer_partial to achieve it's goals -- this
+ * function simply determines if we're going to use scatter-gather or not,
+ * and acts appropriately. For now, it also re-interprets the error codes.
+ */
+static void isd200_transfer( struct us_data *us, Scsi_Cmnd *srb )
+{
+ int i;
+ int result = -1;
+ struct scatterlist *sg;
+ unsigned int total_transferred = 0;
+ unsigned int transfer_amount;
+
+ /* calculate how much we want to transfer */
+ int dir = srb->sc_data_direction;
+ srb->sc_data_direction = SCSI_DATA_WRITE;
+ transfer_amount = usb_stor_transfer_length(srb);
+ srb->sc_data_direction = dir;
+
+ /* was someone foolish enough to request more data than available
+ * buffer space? */
+ if (transfer_amount > srb->request_bufflen)
+ transfer_amount = srb->request_bufflen;
+
+ /* are we scatter-gathering? */
+ if (srb->use_sg) {
+
+ /* loop over all the scatter gather structures and
+ * make the appropriate requests for each, until done
+ */
+ sg = (struct scatterlist *) srb->request_buffer;
+ for (i = 0; i < srb->use_sg; i++) {
+
+ /* transfer the lesser of the next buffer or the
+ * remaining data */
+ if (transfer_amount - total_transferred >=
+ sg[i].length) {
+ result = isd200_transfer_partial(us,
+ srb->sc_data_direction,
+ sg[i].address,
+ sg[i].length);
+ total_transferred += sg[i].length;
+ } else
+ result = isd200_transfer_partial(us,
+ srb->sc_data_direction,
+ sg[i].address,
+ transfer_amount - total_transferred);
+
+ /* if we get an error, end the loop here */
+ if (result)
+ break;
+ }
+ }
+ else
+ /* no scatter-gather, just make the request */
+ result = isd200_transfer_partial(us,
+ srb->sc_data_direction,
+ srb->request_buffer,
+ transfer_amount);
+
+ /* return the result in the data structure itself */
+ srb->result = result;
+}
+
+
+/***********************************************************************
+ * Transport routines
+ ***********************************************************************/
+
+
+/**************************************************************************
+ * ISD200 Bulk Transport
+ *
+ * Note: This routine was copied from the usb_stor_Bulk_transport routine
+ * located in the transport.c source file. The scsi command is limited to
+ * only 12 bytes while the CDB for the ISD200 must be 16 bytes.
+ */
+int isd200_Bulk_transport( struct us_data *us, Scsi_Cmnd *srb,
+ union ata_cdb *AtaCdb, unsigned char AtaCdbLength )
+{
+ struct bulk_cb_wrap bcb;
+ struct bulk_cs_wrap bcs;
+ int result;
+ int pipe;
+ int partial;
+ unsigned int transfer_amount;
+
+ int dir = srb->sc_data_direction;
+ srb->sc_data_direction = SCSI_DATA_WRITE;
+ transfer_amount = usb_stor_transfer_length(srb);
+ srb->sc_data_direction = dir;
+
+ /* set up the command wrapper */
+ bcb.Signature = cpu_to_le32(US_BULK_CB_SIGN);
+ bcb.DataTransferLength = cpu_to_le32(transfer_amount);
+ bcb.Flags = srb->sc_data_direction == SCSI_DATA_READ ? 1 << 7 : 0;
+ bcb.Tag = srb->serial_number;
+ bcb.Lun = srb->cmnd[1] >> 5;
+ if (us->flags & US_FL_SCM_MULT_TARG)
+ bcb.Lun |= srb->target << 4;
+
+ bcb.Length = AtaCdbLength;
+
+ /* construct the pipe handle */
+ pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+
+ /* copy the command payload */
+ memset(bcb.CDB, 0, sizeof(bcb.CDB));
+ memcpy(bcb.CDB, AtaCdb, bcb.Length);
+
+ /* send it to out endpoint */
+ US_DEBUGP("Bulk command S 0x%x T 0x%x Trg %d LUN %d L %d F %d CL %d\n",
+ le32_to_cpu(bcb.Signature), bcb.Tag,
+ (bcb.Lun >> 4), (bcb.Lun & 0xFF),
+ bcb.DataTransferLength, bcb.Flags, bcb.Length);
+ result = usb_stor_bulk_msg(us, &bcb, pipe, US_BULK_CB_WRAP_LEN,
+ &partial);
+ US_DEBUGP("Bulk command transfer result=%d\n", result);
+
+ if (result == -ENOENT)
+ return ISD200_TRANSPORT_ABORTED;
+ else if (result == -EPIPE) {
+ /* if we stall, we need to clear it before we go on */
+ US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
+ usb_stor_clear_halt(us->pusb_dev, pipe);
+ } else if (result)
+ return ISD200_TRANSPORT_ERROR;
+
+ /* if the command transfered well, then we go to the data stage */
+ if (!result && bcb.DataTransferLength) {
+ isd200_transfer(us, srb);
+ US_DEBUGP("Bulk data transfer result 0x%x\n", srb->result);
+
+ if (srb->result == ISD200_TRANSPORT_ABORTED)
+ return ISD200_TRANSPORT_ABORTED;
+ }
+
+ /* See flow chart on pg 15 of the Bulk Only Transport spec for
+ * an explanation of how this code works.
+ */
+
+ /* construct the pipe handle */
+ pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
+
+ /* get CSW for device status */
+ US_DEBUGP("Attempting to get CSW...\n");
+ result = usb_stor_bulk_msg(us, &bcs, pipe, US_BULK_CS_WRAP_LEN,
+ &partial);
+ if (result == -ENOENT)
+ return ISD200_TRANSPORT_ABORTED;
+
+ /* did the attempt to read the CSW fail? */
+ if (result == -EPIPE) {
+ US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
+ usb_stor_clear_halt(us->pusb_dev, pipe);
+
+ /* get the status again */
+ US_DEBUGP("Attempting to get CSW (2nd try)...\n");
+ result = usb_stor_bulk_msg(us, &bcs, pipe,
+ US_BULK_CS_WRAP_LEN, &partial);
+
+ /* if the command was aborted, indicate that */
+ if (result == -ENOENT)
+ return ISD200_TRANSPORT_ABORTED;
+
+ /* if it fails again, we need a reset and return an error*/
+ if (result == -EPIPE) {
+ US_DEBUGP("clearing halt for pipe 0x%x\n", pipe);
+ usb_stor_clear_halt(us->pusb_dev, pipe);
+ return ISD200_TRANSPORT_ERROR;
+ }
+ }
+
+ /* if we still have a failure at this point, we're in trouble */
+ US_DEBUGP("Bulk status result = %d\n", result);
+ if (result)
+ return ISD200_TRANSPORT_ERROR;
+
+ /* check bulk status */
+ US_DEBUGP("Bulk status Sig 0x%x T 0x%x R %d Stat 0x%x\n",
+ le32_to_cpu(bcs.Signature), bcs.Tag,
+ bcs.Residue, bcs.Status);
+ if (bcs.Signature != cpu_to_le32(US_BULK_CS_SIGN) ||
+ bcs.Tag != bcb.Tag ||
+ bcs.Status > US_BULK_STAT_PHASE || partial != 13) {
+ US_DEBUGP("Bulk logical error\n");
+ return ISD200_TRANSPORT_ERROR;
+ }
+
+ /* based on the status code, we report good or bad */
+ switch (bcs.Status) {
+ case US_BULK_STAT_OK:
+ /* command good -- note that we could be short on data */
+ return ISD200_TRANSPORT_GOOD;
+
+ case US_BULK_STAT_FAIL:
+ /* command failed */
+ return ISD200_TRANSPORT_FAILED;
+
+ case US_BULK_STAT_PHASE:
+ /* phase error */
+ usb_stor_Bulk_reset(us);
+ return ISD200_TRANSPORT_ERROR;
+ }
+
+ /* we should never get here, but if we do, we're in trouble */
+ return ISD200_TRANSPORT_ERROR;
+}
+
+
+/**************************************************************************
+ * 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_cmnd srb;
+ struct isd200_info *info = (struct isd200_info *)us->extra;
+ int status;
+
+ memset(&ata, 0, sizeof(ata));
+ memset(&srb, 0, sizeof(srb));
+
+ 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.read.SelectStatus = 1;
+ ata.read.SelectError = 1;
+ ata.read.SelectCylinderHigh = 1;
+ ata.read.SelectCylinderLow = 1;
+ srb.sc_data_direction = SCSI_DATA_READ;
+ 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.write.SelectDeviceHead = 1;
+ ata.write.DeviceHeadByte = value;
+ srb.sc_data_direction = SCSI_DATA_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.write.SelectDeviceControl = 1;
+ ata.write.DeviceControlByte = ATA_DC_RESET_CONTROLLER;
+ srb.sc_data_direction = SCSI_DATA_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.write.SelectDeviceControl = 1;
+ ata.write.DeviceControlByte = ATA_DC_REENABLE_CONTROLLER;
+ srb.sc_data_direction = SCSI_DATA_NONE;
+ break;
+
+ case ACTION_SOFT_RESET:
+ US_DEBUGP(" isd200_action(SOFT_RESET)\n");
+ ata.generic.ActionSelect = ACTION_SELECT_1|ACTION_SELECT_5;
+ ata.write.SelectDeviceHead = 1;
+ ata.write.DeviceHeadByte = info->DeviceHead;
+ ata.write.SelectCommand = 1;
+ ata.write.CommandByte = WIN_SRST;
+ srb.sc_data_direction = SCSI_DATA_NONE;
+ break;
+
+ case ACTION_IDENTIFY:
+ US_DEBUGP(" isd200_action(IDENTIFY)\n");
+ ata.write.SelectCommand = 1;
+ ata.write.CommandByte = WIN_IDENTIFY;
+ srb.sc_data_direction = SCSI_DATA_READ;
+ srb.request_buffer = (void *)&info->drive;
+ srb.request_bufflen = sizeof(struct hd_driveid);
+ break;
+
+ default:
+ US_DEBUGP("Error: Undefined action %d\n",action);
+ break;
+ }
+
+ status = isd200_Bulk_transport(us, &srb, &ata, sizeof(ata.generic));
+ if (status != ISD200_TRANSPORT_GOOD) {
+ 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
+ */
+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->ATARegs, sizeof(info->ATARegs) );
+ if (transferStatus != ISD200_TRANSPORT_GOOD) {
+ US_DEBUGP(" Error reading ATA registers\n");
+ retStatus = ISD200_ERROR;
+ } else {
+ 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 recieve the response.
+ */
+void isd200_invoke_transport( struct us_data *us,
+ Scsi_Cmnd *srb,
+ union ata_cdb *ataCdb )
+{
+ int need_auto_sense = 0;
+ int transferStatus;
+
+ /* send the command to the transport layer */
+ transferStatus = isd200_Bulk_transport(us, srb, ataCdb,
+ sizeof(ataCdb->generic));
+ switch (transferStatus) {
+
+ case ISD200_TRANSPORT_GOOD:
+ /* Indicate a good result */
+ srb->result = GOOD;
+ break;
+
+ case ISD200_TRANSPORT_ABORTED:
+ /* if the command gets aborted by the higher layers, we need to
+ * short-circuit all other processing
+ */
+ US_DEBUGP("-- transport indicates command was aborted\n");
+ srb->result = DID_ABORT << 16;
+ break;
+
+ case ISD200_TRANSPORT_FAILED:
+ US_DEBUGP("-- transport indicates command failure\n");
+ need_auto_sense = 1;
+ break;
+
+ case ISD200_TRANSPORT_ERROR:
+ US_DEBUGP("-- transport indicates transport failure\n");
+ srb->result = DID_ERROR << 16;
+ break;
+
+ case ISD200_TRANSPORT_SHORT:
+ if (!((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;
+ }
+ break;
+
+ default:
+ US_DEBUGP("-- transport indicates unknown failure\n");
+ srb->result = DID_ERROR << 16;
+
+ }
+
+ if (need_auto_sense)
+ if (isd200_read_regs(us) == ISD200_GOOD)
+ isd200_build_sense(us, srb);
+
+ /* Regardless of auto-sense, if we _know_ we have an error
+ * condition, show that in the result code
+ */
+ if (transferStatus == ISD200_TRANSPORT_FAILED)
+ srb->result = CHECK_CONDITION;
+}
+
+
+/**************************************************************************
+ * isd200_write_config
+ *
+ * Write the ISD200 Configuraton data
+ *
+ * RETURNS:
+ * ISD status code
+ */
+int isd200_write_config( struct us_data *us )
+{
+ struct isd200_info *info = (struct isd200_info *)us->extra;
+ int retStatus = ISD200_GOOD;
+ int result;
+
+
+ US_DEBUGP("Entering isd200_write_config\n");
+
+ US_DEBUGP(" Writing the following ISD200 Config Data:\n");
+ 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.ATAPICommandBlockSize);
+ US_DEBUGP(" Master/Slave Selection: 0x%x\n", info->ConfigData.MasterSlaveSelection);
+ US_DEBUGP(" ATAPI Reset: 0x%x\n", info->ConfigData.ATAPIReset);
+ US_DEBUGP(" ATA Timing: 0x%x\n", info->ConfigData.ATATiming);
+ 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.InitStatus);
+ US_DEBUGP(" Config Descriptor 2: 0x%x\n", info->ConfigData.ConfigDescriptor2);
+ US_DEBUGP(" Skip Device Boot: 0x%x\n", info->ConfigData.SkipDeviceBoot);
+ US_DEBUGP(" ATA 3 State Supsend: 0x%x\n", info->ConfigData.ATA3StateSuspend);
+ US_DEBUGP(" Descriptor Override: 0x%x\n", info->ConfigData.DescriptOverride);
+ US_DEBUGP(" Last LUN Identifier: 0x%x\n", info->ConfigData.LastLUNIdentifier);
+ US_DEBUGP(" SRST Enable: 0x%x\n", info->ConfigData.SRSTEnable);
+
+ /* let's send the command via the control pipe */
+ result = usb_stor_control_msg(
+ us,
+ usb_sndctrlpipe(us->pusb_dev,0),
+ 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");
+
+ /* STALL must be cleared when they are detected */
+ if (result == -EPIPE) {
+ US_DEBUGP("-- Stall on control pipe. Clearing\n");
+ result = usb_stor_clear_halt(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev, 0));
+ US_DEBUGP("-- usb_stor_clear_halt() returns %d\n", result);
+
+ }
+ retStatus = ISD200_ERROR;
+ }
+
+ US_DEBUGP("Leaving isd200_write_config %08X\n", retStatus);
+ return retStatus;
+}
+
+
+/**************************************************************************
+ * isd200_read_config
+ *
+ * Reads the ISD200 Configuraton data
+ *
+ * RETURNS:
+ * ISD status code
+ */
+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_control_msg(
+ us,
+ usb_rcvctrlpipe(us->pusb_dev,0),
+ 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");
+ 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.ATAPICommandBlockSize);
+ US_DEBUGP(" Master/Slave Selection: 0x%x\n", info->ConfigData.MasterSlaveSelection);
+ US_DEBUGP(" ATAPI Reset: 0x%x\n", info->ConfigData.ATAPIReset);
+ US_DEBUGP(" ATA Timing: 0x%x\n", info->ConfigData.ATATiming);
+ 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.InitStatus);
+ US_DEBUGP(" Config Descriptor 2: 0x%x\n", info->ConfigData.ConfigDescriptor2);
+ US_DEBUGP(" Skip Device Boot: 0x%x\n", info->ConfigData.SkipDeviceBoot);
+ US_DEBUGP(" ATA 3 State Supsend: 0x%x\n", info->ConfigData.ATA3StateSuspend);
+ US_DEBUGP(" Descriptor Override: 0x%x\n", info->ConfigData.DescriptOverride);
+ US_DEBUGP(" Last LUN Identifier: 0x%x\n", info->ConfigData.LastLUNIdentifier);
+ US_DEBUGP(" SRST Enable: 0x%x\n", info->ConfigData.SRSTEnable);
+ } else {
+ US_DEBUGP(" Request to get ISD200 Config Data failed!\n");
+
+ /* STALL must be cleared when they are detected */
+ if (result == -EPIPE) {
+ US_DEBUGP("-- Stall on control pipe. Clearing\n");
+ result = usb_stor_clear_halt(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev, 0));
+ US_DEBUGP("-- usb_stor_clear_halt() returns %d\n", result);
+
+ }
+ 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
+ */
+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
+ */
+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 */
+ wait_ms(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 */
+ wait_ms(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 char regs[8];
+ unsigned long endTime;
+ struct isd200_info *info = (struct isd200_info *)us->extra;
+ int recheckAsMaster = FALSE;
+
+ 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(TRUE) {
+ char* mstr = master_slave == ATA_ADDRESS_DEVHEAD_STD ?
+ "Master" : "Slave";
+
+ status = isd200_action( us, ACTION_ENUM, NULL, master_slave );
+ if ( status != ISD200_GOOD )
+ break;
+
+ status = isd200_action( us, ACTION_READ_STATUS,
+ regs, sizeof(regs) );
+ 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 == FALSE)) {
+ US_DEBUGP(" Identified ATAPI device as slave. Rechecking again as master\n");
+ recheckAsMaster = TRUE;
+ 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");
+ }
+
+ /* check for timeout on this request */
+ if (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
+ */
+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, FALSE );
+ if (retStatus == ISD200_GOOD)
+ retStatus = isd200_try_enum( us, ATA_ADDRESS_DEVHEAD_SLAVE, FALSE );
+
+ if (retStatus == ISD200_GOOD) {
+ retStatus = isd200_srst(us);
+ if (retStatus == ISD200_GOOD)
+ /* ata or atapi? */
+ retStatus = isd200_try_enum( us, ATA_ADDRESS_DEVHEAD_STD, TRUE );
+ }
+
+ isslave = (info->DeviceHead & ATA_ADDRESS_DEVHEAD_SLAVE) ? 1 : 0;
+ if (info->ConfigData.MasterSlaveSelection != isslave) {
+ US_DEBUGP(" Setting Master/Slave selection to %d\n", isslave);
+ info->ConfigData.MasterSlaveSelection = isslave;
+ 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
+ */
+int isd200_get_inquiry_data( struct us_data *us )
+{
+ struct isd200_info *info = (struct isd200_info *)us->extra;
+ int retStatus = ISD200_GOOD;
+
+ 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 Commmand Identify */
+ transferStatus = isd200_action( us, ACTION_IDENTIFY,
+ &info->drive,
+ 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;
+
+ US_DEBUGP(" Identify Data Structure:\n");
+ US_DEBUGP(" config = 0x%x\n", info->drive.config);
+ US_DEBUGP(" cyls = 0x%x\n", info->drive.cyls);
+ US_DEBUGP(" heads = 0x%x\n", info->drive.heads);
+ US_DEBUGP(" track_bytes = 0x%x\n", info->drive.track_bytes);
+ US_DEBUGP(" sector_bytes = 0x%x\n", info->drive.sector_bytes);
+ US_DEBUGP(" sectors = 0x%x\n", info->drive.sectors);
+ US_DEBUGP(" serial_no[0] = 0x%x\n", info->drive.serial_no[0]);
+ US_DEBUGP(" buf_type = 0x%x\n", info->drive.buf_type);
+ US_DEBUGP(" buf_size = 0x%x\n", info->drive.buf_size);
+ US_DEBUGP(" ecc_bytes = 0x%x\n", info->drive.ecc_bytes);
+ US_DEBUGP(" fw_rev[0] = 0x%x\n", info->drive.fw_rev[0]);
+ US_DEBUGP(" model[0] = 0x%x\n", info->drive.model[0]);
+ US_DEBUGP(" max_multsect = 0x%x\n", info->drive.max_multsect);
+ US_DEBUGP(" dword_io = 0x%x\n", info->drive.dword_io);
+ US_DEBUGP(" capability = 0x%x\n", info->drive.capability);
+ US_DEBUGP(" tPIO = 0x%x\n", info->drive.tPIO);
+ US_DEBUGP(" tDMA = 0x%x\n", info->drive.tDMA);
+ US_DEBUGP(" field_valid = 0x%x\n", info->drive.field_valid);
+ US_DEBUGP(" cur_cyls = 0x%x\n", info->drive.cur_cyls);
+ US_DEBUGP(" cur_heads = 0x%x\n", info->drive.cur_heads);
+ US_DEBUGP(" cur_sectors = 0x%x\n", info->drive.cur_sectors);
+ US_DEBUGP(" cur_capacity = 0x%x\n", (info->drive.cur_capacity1 << 16) + info->drive.cur_capacity0 );
+ US_DEBUGP(" multsect = 0x%x\n", info->drive.multsect);
+ US_DEBUGP(" lba_capacity = 0x%x\n", info->drive.lba_capacity);
+ US_DEBUGP(" command_set_1 = 0x%x\n", info->drive.command_set_1);
+ US_DEBUGP(" command_set_2 = 0x%x\n", info->drive.command_set_2);
+
+ memset(&info->InquiryData, 0, sizeof(info->InquiryData));
+
+ /* Standard IDE interface only supports disks */
+ info->InquiryData.DeviceType = DIRECT_ACCESS_DEVICE;
+
+ /* Fix-up the return data from an INQUIRY command to show
+ * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
+ * in Linux.
+ */
+ info->InquiryData.Versions = 0x2;
+
+ /* The length must be at least 36 (5 + 31) */
+ info->InquiryData.AdditionalLength = 0x1F;
+
+ if (info->drive.command_set_1 & COMMANDSET_MEDIA_STATUS) {
+ /* set the removable bit */
+ info->InquiryData.RemovableMedia = 1;
+ info->DeviceFlags |= DF_REMOVABLE_MEDIA;
+ }
+
+ /* Fill in vendor identification fields */
+ for (i = 0; i < 20; i += 2) {
+ info->InquiryData.VendorId[i] =
+ info->drive.model[i + 1];
+ info->InquiryData.VendorId[i+1] =
+ info->drive.model[i];
+ }
+
+ /* Initialize unused portion of product id */
+ for (i = 0; i < 4; i++) {
+ info->InquiryData.ProductId[12+i] = ' ';
+ }
+
+ /* Move firmware revision from IDENTIFY data to */
+ /* product revision in INQUIRY data */
+ for (i = 0; i < 4; i += 2) {
+ info->InquiryData.ProductRevisionLevel[i] =
+ info->drive.fw_rev[i+1];
+ info->InquiryData.ProductRevisionLevel[i+1] =
+ info->drive.fw_rev[i];
+ }
+
+ /* determine if it supports Media Status Notification */
+ if (info->drive.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 */
+ if (us->extra != NULL) {
+ kfree(us->extra);
+ us->extra = NULL;
+ us->extra_destructor = NULL;
+ }
+ }
+ }
+
+ US_DEBUGP("Leaving isd200_get_inquiry_data %08X\n", retStatus);
+
+ return(retStatus);
+}
+
+
+/**************************************************************************
+ * isd200_data_copy
+ *
+ * Copy data into the srb request buffer. Use scatter gather if required.
+ *
+ * RETURNS:
+ * void
+ */
+void isd200_data_copy(Scsi_Cmnd *srb, char * src, int length)
+{
+ unsigned int len = length;
+ struct scatterlist *sg;
+
+ if (srb->use_sg) {
+ int i;
+ unsigned int total = 0;
+
+ /* Add up the sizes of all the sg segments */
+ sg = (struct scatterlist *) srb->request_buffer;
+ for (i = 0; i < srb->use_sg; i++)
+ total += sg[i].length;
+
+ if (length > total)
+ len = total;
+
+ total = 0;
+
+ /* Copy data into sg buffer(s) */
+ for (i = 0; i < srb->use_sg; i++) {
+ if ((len > total) && (len > 0)) {
+ /* transfer the lesser of the next buffer or the
+ * remaining data */
+ if (len - total >= sg[i].length) {
+ memcpy(sg[i].address, src + total, sg[i].length);
+ total += sg[i].length;
+ } else {
+ memcpy(sg[i].address, src + total, len - total);
+ total = len;
+ }
+ }
+ else
+ break;
+ }
+ } else {
+ /* Make sure length does not exceed buffer length */
+ if (length > srb->request_bufflen)
+ len = srb->request_bufflen;
+
+ if (len > 0)
+ memcpy(srb->request_buffer, src, len);
+ }
+}
+
+
+/**************************************************************************
+ * isd200_scsi_to_ata
+ *
+ * Translate SCSI commands to ATA commands.
+ *
+ * RETURNS:
+ * TRUE if the command needs to be sent to the transport layer
+ * FALSE otherwise
+ */
+int isd200_scsi_to_ata(Scsi_Cmnd *srb, struct us_data *us,
+ union ata_cdb * ataCdb)
+{
+ struct isd200_info *info = (struct isd200_info *)us->extra;
+ int sendToTransport = TRUE;
+ 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");
+
+ if (srb->request_bufflen > sizeof(struct inquiry_data))
+ srb->request_bufflen = sizeof(struct inquiry_data);
+
+ /* copy InquiryData */
+ isd200_data_copy(srb, (char *) &info->InquiryData, srb->request_bufflen);
+ srb->result = GOOD;
+ sendToTransport = FALSE;
+ break;
+
+ case MODE_SENSE:
+ US_DEBUGP(" ATA OUT - SCSIOP_MODE_SENSE\n");
+
+ /* Initialize the return buffer */
+ isd200_data_copy(srb, (char *) &senseData, 8);
+
+ if (info->DeviceFlags & DF_MEDIA_STATUS_ENABLED)
+ {
+ ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand;
+ ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
+ ataCdb->generic.TransferBlockSize = 1;
+ ataCdb->write.SelectCommand = 1;
+ 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 = GOOD;
+ sendToTransport = FALSE;
+ }
+ break;
+
+ case TEST_UNIT_READY:
+ US_DEBUGP(" ATA OUT - SCSIOP_TEST_UNIT_READY\n");
+
+ /* Initialize the return buffer */
+ isd200_data_copy(srb, (char *) &senseData, 8);
+
+ if (info->DeviceFlags & DF_MEDIA_STATUS_ENABLED)
+ {
+ ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand;
+ ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
+ ataCdb->generic.TransferBlockSize = 1;
+ ataCdb->write.SelectCommand = 1;
+ 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 = GOOD;
+ sendToTransport = FALSE;
+ }
+ break;
+
+ case READ_CAPACITY:
+ {
+ unsigned long capacity;
+ struct read_capacity_data readCapacityData;
+
+ US_DEBUGP(" ATA OUT - SCSIOP_READ_CAPACITY\n");
+
+ if (info->drive.capability & CAPABILITY_LBA ) {
+ capacity = info->drive.lba_capacity - 1;
+ } else {
+ capacity = (info->drive.heads *
+ info->drive.cyls *
+ info->drive.sectors) - 1;
+ }
+ readCapacityData.LogicalBlockAddress = cpu_to_be32(capacity);
+ readCapacityData.BytesPerBlock = cpu_to_be32(0x200);
+
+ if (srb->request_bufflen > sizeof(struct read_capacity_data))
+ srb->request_bufflen = sizeof(struct read_capacity_data);
+
+ isd200_data_copy(srb, (char *) &readCapacityData, srb->request_bufflen);
+ srb->result = GOOD;
+ sendToTransport = FALSE;
+ }
+ break;
+
+ case READ_10:
+ US_DEBUGP(" ATA OUT - SCSIOP_READ\n");
+
+ lba = *(unsigned long *)&srb->cmnd[2];
+ lba = cpu_to_be32(lba);
+ blockCount = (unsigned long)srb->cmnd[7]<<8 | (unsigned long)srb->cmnd[8];
+
+ if (info->drive.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 % info->drive.sectors) + 1);
+ cylinder = (unsigned short)(lba / (info->drive.sectors *
+ info->drive.heads));
+ head = (unsigned char)((lba / info->drive.sectors) %
+ info->drive.heads);
+ }
+ ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand;
+ ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
+ ataCdb->generic.TransferBlockSize = 1;
+ ataCdb->write.SelectSectorCount = 1;
+ ataCdb->write.SectorCountByte = (unsigned char)blockCount;
+ ataCdb->write.SelectSectorNumber = 1;
+ ataCdb->write.SectorNumberByte = sectnum;
+ ataCdb->write.SelectCylinderHigh = 1;
+ ataCdb->write.CylinderHighByte = (unsigned char)(cylinder>>8);
+ ataCdb->write.SelectCylinderLow = 1;
+ ataCdb->write.CylinderLowByte = (unsigned char)cylinder;
+ ataCdb->write.SelectDeviceHead = 1;
+ ataCdb->write.DeviceHeadByte = (head | ATA_ADDRESS_DEVHEAD_STD);
+ ataCdb->write.SelectCommand = 1;
+ ataCdb->write.CommandByte = WIN_READ;
+ break;
+
+ case WRITE_10:
+ US_DEBUGP(" ATA OUT - SCSIOP_WRITE\n");
+
+ lba = *(unsigned long *)&srb->cmnd[2];
+ lba = cpu_to_be32(lba);
+ blockCount = (unsigned long)srb->cmnd[7]<<8 | (unsigned long)srb->cmnd[8];
+
+ if (info->drive.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 % info->drive.sectors) + 1);
+ cylinder = (unsigned short)(lba / (info->drive.sectors * info->drive.heads));
+ head = (unsigned char)((lba / info->drive.sectors) % info->drive.heads);
+ }
+ ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand;
+ ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
+ ataCdb->generic.TransferBlockSize = 1;
+ ataCdb->write.SelectSectorCount = 1;
+ ataCdb->write.SectorCountByte = (unsigned char)blockCount;
+ ataCdb->write.SelectSectorNumber = 1;
+ ataCdb->write.SectorNumberByte = sectnum;
+ ataCdb->write.SelectCylinderHigh = 1;
+ ataCdb->write.CylinderHighByte = (unsigned char)(cylinder>>8);
+ ataCdb->write.SelectCylinderLow = 1;
+ ataCdb->write.CylinderLowByte = (unsigned char)cylinder;
+ ataCdb->write.SelectDeviceHead = 1;
+ ataCdb->write.DeviceHeadByte = (head | ATA_ADDRESS_DEVHEAD_STD);
+ ataCdb->write.SelectCommand = 1;
+ 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->write.SelectCommand = 1;
+ 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 = GOOD;
+ sendToTransport = FALSE;
+ }
+ break;
+
+ case START_STOP:
+ US_DEBUGP(" ATA OUT - SCSIOP_START_STOP_UNIT\n");
+ US_DEBUGP(" srb->cmnd[4] = 0x%X\n", srb->cmnd[4]);
+
+ /* Initialize the return buffer */
+ isd200_data_copy(srb, (char *) &senseData, 8);
+
+ 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->write.SelectCommand = 1;
+ 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->write.SelectCommand = 1;
+ ataCdb->write.CommandByte = ATA_COMMAND_GET_MEDIA_STATUS;
+ srb->request_bufflen = 0;
+ } else {
+ US_DEBUGP(" Nothing to do, just report okay\n");
+ srb->result = GOOD;
+ sendToTransport = FALSE;
+ }
+ break;
+
+ default:
+ US_DEBUGP("Unsupported SCSI command - 0x%X\n", srb->cmnd[0]);
+ srb->result = DID_ERROR << 16;
+ sendToTransport = FALSE;
+ break;
+ }
+
+ return(sendToTransport);
+}
+
+
+/**************************************************************************
+ * isd200_init_info
+ *
+ * Allocates (if necessary) and initializes the driver structure.
+ *
+ * RETURNS:
+ * ISD status code
+ */
+int isd200_init_info(struct us_data *us)
+{
+ int retStatus = ISD200_GOOD;
+
+ if (!us->extra) {
+ us->extra = (void *) kmalloc(sizeof(struct isd200_info), GFP_KERNEL);
+ if (!us->extra) {
+ US_DEBUGP("ERROR - kmalloc failure\n");
+ retStatus = ISD200_ERROR;
+ }
+ }
+
+ if (retStatus == ISD200_GOOD) {
+ memset(us->extra, 0, sizeof(struct isd200_info));
+ }
+
+ 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(Scsi_Cmnd *srb, struct us_data *us)
+{
+ int sendToTransport = TRUE;
+ union ata_cdb ataCdb;
+
+ /* Make sure driver was initialized */
+
+ if (us->extra == NULL)
+ US_DEBUGP("ERROR Driver not initialized\n");
+
+ /* Convert command */
+ sendToTransport = isd200_scsi_to_ata(srb, us, &ataCdb);
+
+ /* send the command to the transport layer */
+ if (sendToTransport)
+ isd200_invoke_transport(us, srb, &ataCdb);
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)