diff -Naur linux-2.4.24.orig/drivers/usb/Config.in linux-2.4.24/drivers/usb/Config.in --- linux-2.4.24.orig/drivers/usb/Config.in 2003-11-29 03:26:20.000000000 +0900 +++ linux-2.4.24/drivers/usb/Config.in 2004-01-27 07:42:05.000000000 +0900 @@ -42,6 +42,7 @@ dep_mbool ' SanDisk SDDR-09 (and other SmartMedia) support' CONFIG_USB_STORAGE_SDDR09 $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL dep_mbool ' SanDisk SDDR-55 SmartMedia support' CONFIG_USB_STORAGE_SDDR55 $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL dep_mbool ' Lexar Jumpshot Compact Flash Reader' CONFIG_USB_STORAGE_JUMPSHOT $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL + dep_mbool ' USS-725/725C USB/ATA Bridge support' CONFIG_USB_STORAGE_USS725 $CONFIG_USB_STORAGE dep_tristate ' USB Modem (CDC ACM) support' CONFIG_USB_ACM $CONFIG_USB dep_tristate ' USB Printer support' CONFIG_USB_PRINTER $CONFIG_USB diff -Naur linux-2.4.24.orig/drivers/usb/storage/Makefile linux-2.4.24/drivers/usb/storage/Makefile --- linux-2.4.24.orig/drivers/usb/storage/Makefile 2002-11-29 08:53:15.000000000 +0900 +++ linux-2.4.24/drivers/usb/storage/Makefile 2004-01-25 05:59:36.000000000 +0900 @@ -21,6 +21,7 @@ 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-obj-$(CONFIG_USB_STORAGE_USS725) += uss725.o usb-storage-objs := scsiglue.o protocol.o transport.o usb.o \ initializers.o $(usb-storage-obj-y) diff -Naur linux-2.4.24.orig/drivers/usb/storage/transport.h linux-2.4.24/drivers/usb/storage/transport.h --- linux-2.4.24.orig/drivers/usb/storage/transport.h 2003-08-25 20:44:42.000000000 +0900 +++ linux-2.4.24/drivers/usb/storage/transport.h 2004-01-25 06:26:38.000000000 +0900 @@ -75,6 +75,10 @@ #define US_PR_JUMPSHOT 0xf3 /* Lexar Jumpshot */ #endif +#ifdef CONFIG_USB_STORAGE_USS725 +#define US_PR_USS725 0xf4 /* In-Systems USS-725 */ +#endif + #define US_PR_DEVICE 0xff /* Use device's value */ /* diff -Naur linux-2.4.24.orig/drivers/usb/storage/unusual_devs.h linux-2.4.24/drivers/usb/storage/unusual_devs.h --- linux-2.4.24.orig/drivers/usb/storage/unusual_devs.h 2003-11-29 03:26:20.000000000 +0900 +++ linux-2.4.24/drivers/usb/storage/unusual_devs.h 2004-01-25 06:26:39.000000000 +0900 @@ -329,6 +329,23 @@ 0 ), #endif +#ifdef CONFIG_USB_STORAGE_USS725 +UNUSUAL_DEV( 0x05ab, 0x0200, 0x0100, 0x0110, + "In-System", + "USS-725 USB/IDE Bridge (ATA/ATAPI)", + US_SC_SCSI, US_PR_USS725, uss725_init, 0 ), + +UNUSUAL_DEV( 0x05ab, 0x0202, 0x0100, 0x0110, + "In-System", + "USS-725 USB/IDE Bridge (ATA/ATAPI)", + US_SC_SCSI, US_PR_USS725, uss725_init, 0 ), + +UNUSUAL_DEV( 0x05ab, 0x0581, 0x0100, 0x0110, + "In-System", + "USS-725 USB/IDE Bridge (ATA/ATAPI)", + US_SC_SCSI, US_PR_USS725, uss725_init, 0 ), +#endif + #ifdef CONFIG_USB_STORAGE_JUMPSHOT UNUSUAL_DEV( 0x05dc, 0x0001, 0x0000, 0x0001, "Lexar", diff -Naur linux-2.4.24.orig/drivers/usb/storage/usb.c linux-2.4.24/drivers/usb/storage/usb.c --- linux-2.4.24.orig/drivers/usb/storage/usb.c 2003-08-25 20:44:42.000000000 +0900 +++ linux-2.4.24/drivers/usb/storage/usb.c 2004-01-25 06:03:19.000000000 +0900 @@ -78,6 +78,9 @@ #ifdef CONFIG_USB_STORAGE_JUMPSHOT #include "jumpshot.h" #endif +#ifdef CONFIG_USB_STORAGE_USS725 +#include "uss725.h" +#endif #include @@ -906,6 +909,15 @@ break; #endif +#ifdef CONFIG_USB_STORAGE_USS725 + case US_PR_USS725: + ss->transport_name = "USS-725 Control/Bulk"; + ss->transport = uss725_transport; + ss->transport_reset = uss725_reset; + ss->max_lun = 1; + break; +#endif + default: ss->transport_name = "Unknown"; kfree(ss->current_urb); diff -Naur linux-2.4.24.orig/drivers/usb/storage/uss725.c linux-2.4.24/drivers/usb/storage/uss725.c --- linux-2.4.24.orig/drivers/usb/storage/uss725.c 1970-01-01 09:00:00.000000000 +0900 +++ linux-2.4.24/drivers/usb/storage/uss725.c 2004-01-25 06:08:23.000000000 +0900 @@ -0,0 +1,2145 @@ +/* Storage driver for In-System Design, Inc. USS-725 + * + * Current development and maintenance: + * (C) 2002 Nicolas Planel (nplanel@mandrakesoft.com) + * + * The USS-725 was really made for adapting parallell printers to USB, but + * using a fancy code sequencer trick ISD was able to use it for storage + * applications too. The downside is that the driver becomes very complex. + * + * + * 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-11-12: v0.1 First usable version based on ISD200 and + * first work of Björn Stenberg . + * (support only one ATA device) + * (nplanel@mandrakesoft.com) + */ + +/* Include files */ +#include "transport.h" +#include "protocol.h" +#include "usb.h" +#include "debug.h" +#include "scsiglue.h" +#include "uss725.h" +#include "uss725_regs.h" + +#include +#include +#include +#include +#include +#include + +#define BSY_TIMEOUT 10 +#define SEQ_TIMEOUT 5 + +#define ATA_BYTES_PER_BLOCK 512 + +/* Timeout defines (in Seconds) */ +#define USS725_ENUM_BSY_TIMEOUT 35 +#define USS725_ENUM_DETECT_TIMEOUT 30 +#define USS725_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 + +// +// ATAPI State Machine constants +// +#define ATAPI_STATE_COMMAND_START 0x00 +#define ATAPI_STATE_DEVICE_BUSY 0x01 +#define ATAPI_STATE_CHECK_CYLHILO 0x02 +#define ATAPI_STATE_DATA_OPTIMIZED 0x03 +#define ATAPI_STATE_DATA_LOOP_ON_DRQ 0x04 +#define ATAPI_STATE_DATA_NOT_FINISHED 0x05 +#define ATAPI_STATE_DO_CYLHILO 0x06 +#define ATAPI_STATE_COMPLETE_TIMEOUT 0x07 +#define ATAPI_STATE_CHECK_COMMAND_COMPLETE 0x08 +#define ATAPI_STATE_DEVICE_ERROR 0xFD +#define ATAPI_STATE_RESET_ERROR 0xFE +#define ATAPI_STATE_COMMAND_COMPLETE 0xFF + +/* command_setX bit definitions */ +#define COMMANDSET_REMOVABLE 0x02 +#define COMMANDSET_MEDIA_STATUS 0x10 + +// +// ATAPI commands +// +#define ATAPI_COMMAND_MODE_SENSE 0x5A +#define ATAPI_COMMAND_MODE_SELECT 0x55 +#define ATAPI_COMMAND_FORMAT_UNIT 0x24 +#define ATAPI_COMMAND_PACKET 0xA0 +#define ATAPI_COMMAND_IDENTIFY 0xA1 + +// +// IDE constants +// +#define ATA_ADDRESS_ALT_STATUS 0x0e +#define ATA_ADDRESS_COMMAND 0x17 +#define ATA_ADDRESS_CYL_HIGH 0x15 +#define ATA_ADDRESS_CYL_LOW 0x14 +#define ATA_ADDRESS_DATA 0x10 +#define ATA_ADDRESS_DEV_HEAD 0x16 +#define ATA_ADDRESS_DEV_CNTRL 0x0e +#define ATA_ADDRESS_ERROR 0x11 +#define ATA_ADDRESS_FEATURES 0x11 +#define ATA_ADDRESS_SECT_CNT 0x12 +#define ATA_ADDRESS_SECT_NUM 0x13 +#define ATA_ADDRESS_STATUS 0x17 + +// +// ATA_ADDRESS_STATUS (and ATA_ADDRESS_ALT_STATUS) definition +// +#define ATA_STATUS_ERROR 0x01 +#define ATA_STATUS_INDEX 0x02 +#define ATA_STATUS_CORRECTED_ERROR 0x04 +#define ATA_STATUS_DRQ 0x08 +#define ATA_STATUS_DSC 0x10 +#define ATA_STATUS_DEVICE_FAULT 0x20 +#define ATA_STATUS_DRDY 0x40 +#define ATA_STATUS_IDLE 0x50 +#define ATA_STATUS_BUSY 0x80 + + +// +// ATA return state constants +// +#define ATA_STATE_SUCCESS 0x00 +#define ATA_STATE_RESUBMIT 0x01 +#define ATA_STATE_TIMEOUT 0x02 +#define ATA_STATE_INVALID_REQUEST 0xFD +#define ATA_STATE_DEVICE_ERROR 0xFE +#define ATA_STATE_RESET_ERROR 0xFF + +// +// ATA_ADDRESS_DEV_HEAD definition +// +#define ATA_ADDRESS_DEVHEAD_STD 0xa0 // on-bits required for all head-reg writes +#define ATA_ADDRESS_DEVHEAD_LBA_MODE 0x40 +#define ATA_ADDRESS_DEVHEAD_SLAVE 0x10 + +// +// WAIT return state constants +// +#define WAIT_STATE_SUCCESS 0x00 +#define WAIT_STATE_BUSY 0x01 +#define WAIT_STATE_TIMEOUT_ERROR 0xFC +#define WAIT_STATE_DEVICE_DRQ 0xFD +#define WAIT_STATE_DEVICE_ERROR 0xFE +#define WAIT_STATE_RESET_ERROR 0xFF + +#define ATA_REGMASK_FEATURES 0x01 +#define ATA_REGMASK_SECTOR_COUNT 0x02 +#define ATA_REGMASK_SECTOR_NUM 0x04 +#define ATA_REGMASK_CYLINDER 0x08 +#define ATA_REGMASK_HEAD 0x10 + +#define ATA_CMDMASK_NO_CHECK_FOR_BSY_AND_ERR_AFTER_CMD 0x01 +#define ATA_CMDMASK_CHECK_FOR_DRQ_AFTER_CMD 0x02 +#define ATA_CMDMASK_WILL_XFER_DATA 0x04 + +/* ATA drive control definitions */ +#define ATA_DC_DISABLE_INTERRUPTS 0x02 +#define ATA_DC_RESET_CONTROLLER 0x04 +#define ATA_DC_REENABLE_CONTROLLER 0x00 + +// CDB operation codes not normally defined +#define SCSIOP_WRITECONTINUE 0xE1 + +/* + * Inquiry data structure. This is the data returned from the target + * after it receives an 'INQUIRY' SCSI command . + * + * This structure may be extended by the number of bytes specified + * in the field AdditionalLength. The defined size constant only + * includes fields through ProductRevisionLevel. + */ + +#define INQUIRY_SIZE 36 + +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)); + +struct ata_regs { + int regFlags; + int cmdFlags; + + int features; + int sectorCount; + int sectorNum; + int cylinder; + int head; + int cmd; +}; + +/* + * Read Capacity Data - returned in Big Endian format + */ + +struct read_capacity_data { + unsigned long LogicalBlockAddress; + unsigned long BytesPerBlock; +}; + +struct uss725_info { + struct inquiry_data InquiryData; + struct hd_driveid drive; + unsigned char ATARegs[8]; + unsigned char DeviceHead; + unsigned char DeviceFlags; + + char * xfer_buffer; + unsigned int xfer_len_buffer; + int xfer_pipe; + + struct completion notify; + int exit_code; + /* maximum number of LUNs supported */ + unsigned char MaxLUNs; +}; + +/* + * General purpose return codes + */ + +#define USS725_ERROR -1 +#define USS725_GOOD 0 + +/* + * Transport return codes + */ + +#define USS725_TRANSPORT_GOOD USB_STOR_TRANSPORT_GOOD /* Transport good, command good */ +#define USS725_TRANSPORT_FAILED USB_STOR_TRANSPORT_FAILED /* Transport good, command failed */ +#define USS725_TRANSPORT_ERROR USB_STOR_TRANSPORT_ERROR /* Transport bad (i.e. device dead) */ +#define USS725_TRANSPORT_ABORTED USB_STOR_TRANSPORT_ABORTED /* Transport aborted */ +#define USS725_TRANSPORT_SHORT USB_STOR_TRANSPORT_ABORTED+1 /* Transport short transfert */ + +/* driver action codes */ +enum { ACTION_READ_STATUS=0, + ACTION_RESET, + ACTION_REENABLE, + ACTION_SOFT_RESET, + ACTION_ENUM, + ACTION_IDENTIFY }; + +/* + * DeviceType field + */ +#define DIRECT_ACCESS_DEVICE 0x00 /* disks */ + +/* + * 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 + + +/*********************************************************************** + * USS725 specific routines + ***********************************************************************/ + +int uss725_send_seq(struct us_data* us, unsigned char* seq, int len); + +/************************************************************************** + * uss725_get_seq_status + * + * Get sequencer status + */ +int uss725_get_seq_status( struct us_data* us, struct seqstatus* status ) +{ + int rc; + status->seqPos = 0xff; + rc = usb_control_msg(us->pusb_dev, + usb_rcvctrlpipe(us->pusb_dev,0), + GET_SEQ_STATUS, + USB_DIR_IN | USB_TYPE_VENDOR, + 0, + 0, + status, + sizeof(struct seqstatus),3*HZ); + if ( rc >= 0 ) + return USS725_GOOD; + + US_DEBUGP("uss725 seq status failed! (%d)\n",rc); + return USS725_ERROR; +} + +/************************************************************************** + * uss725_abort_seq + * + * Get sequencer status + */ +int uss725_abort_seq( struct us_data* us ) +{ + int rc; + rc = usb_control_msg(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev,0), + SEQ_ABORT, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, NULL, 0, HZ); + if ( rc >= 0 ) + return USS725_GOOD; + + US_DEBUGP("uss725 abort seq failed! (%d)\n",rc); + return USS725_ERROR; +} + +int uss725_seq_reset_regs(struct us_data* us) { + unsigned char seq_reset_regs[] = + { + 0x00,0x00,SEQ_BLOCKING_BIT, // Loop Count (WORD) & Block bit (bit 8 of BYTE) + (SEQ_RRMW|REG_SETU), REG_SETU_CLEAR_BULK_OUT, REG_SETU_CLEAR_BULK_OUT, + (SEQ_RRMW|REG_SETU), REG_SETU_HOLD_BULK, REG_SETU_HOLD_BULK, + (SEQ_RRMW|REG_CCTR), REG_CCTR_MANUAL_REVERSE, REG_CCTR_MANUAL_REVERSE, + }; + + US_DEBUGP("uss725 seq_reset_regs: reset!\n"); + if(uss725_send_seq(us, seq_reset_regs, sizeof(seq_reset_regs)) != USS725_GOOD) { + US_DEBUGP("uss725 seq_reset_regs: reset failed!\n"); + return USS725_ERROR; + } + return USS725_GOOD; +} + +#ifdef CONFIG_USB_STORAGE_DEBUG +void print_seqstatus(struct us_data* us, struct seqstatus status) { + US_DEBUGP("seqPos: %d\n",status.seqPos); + US_DEBUGP("globalLoopCount: %d\n", status.globalLoopCountHigh <<8 | + status.globalLoopCountLow); + US_DEBUGP("status_active: %d\n",status.statusFlags); + US_DEBUGP("instructLoopCount: %d\n",(status.statusFlags & 0x0f)<<16 | + status.instructLoopCountMid<<8 | + status.instructLoopCountLow ); +} + +void regName(char reg, char * name) { + switch(reg & 0x0f) { + case REG_DATA: + strcpy(name, "REG_DATA"); + break; + case REG_STAT: + strcpy(name, "REG_STAT"); + break; + case REG_CTRL: + strcpy(name, "REG_CTRL"); + break; + case REG_EPPA: + strcpy(name, "REG_EPPA"); + break; + case REG_EPPD: + strcpy(name, "REG_EPPD"); + break; + case REG_ECPA: + strcpy(name, "REG_ECPA"); + break; + case REG_ECRR: + strcpy(name, "REG_ECRR"); + break; + case REG_CCTR: + strcpy(name, "REG_CCTR"); + break; + case REG_SETU: + strcpy(name, "REG_SETU"); + break; + case REG_TIMO: + strcpy(name, "REG_TIMO"); + break; + case REG_ATAA: + strcpy(name, "REG_ATTA"); + break; + default: + sprintf(name,"0x%02x",reg); + break; + } +} + +void uss725_debug_seq(char * s, int len, int pos) { + int i=0; + char reg_name[10]; + + do { + if(i==pos) + US_DEBUGP("==> "); + else + US_DEBUGP(" "); + + regName(s[i], reg_name); + switch (s[i] & 0xf0) { + case SEQ_RRMW: + US_DEBUGP("(SEQ_RRMW|%s), 0x%02x, 0x%02x,\n",reg_name,(s[i+1]),(s[i+2])); + i+=3; + break; + case SEQ_RRCM: + US_DEBUGP("(SEQ_RRCM|%s), 0x%02x, 0x%02x, 0x%02x, 0x%02x,\n",reg_name,s[i+1],s[i+2],s[i+3],s[i+4]); + i+=5; + break; + case SEQ_RWRI: + US_DEBUGP("(SEQ_RWRI|%s), 0x%02x,\n",reg_name,s[i+1]); + i+=2; + break; + case SEQ_RREN: + US_DEBUGP("(SEQ_RREN|0x%02x),\n",s[i] & 0x01); + i++; + break; + case SEQ_WBIB: + US_DEBUGP("(SEQ_WBIB|0x%02x), 0x%02x, 0x%02x,\n",s[i]&0x0f,s[i+1],s[i+2]); + i+=3; + break; + case SEQ_WBOB: + US_DEBUGP("(SEQ_WBOB|0x%02x), 0x%02x, 0x%02x,\n",s[i]&0x0f,s[i+1],s[i+2]); + i+=3; + break; + case SEQ_DATI: + US_DEBUGP("(SEQ_DATI|%s),\n",reg_name); + i++; + break; + case SEQ_DATO: + US_DEBUGP("(SEQ_DATO|%s),\n",reg_name); + i++; + break; + case SEQ_EPPI: + US_DEBUGP("(SEQ_EPPI|0x%02x), 0x%02x, 0x%02x,\n",s[i]&0x0f,s[i+1],s[i+2]); + i+=3; + break; + case SEQ_NOOP: + US_DEBUGP("(SEQ_NOOP|0x%02x), 0x%02x,\n",s[i]&0x0f,s[i+1]); + i+=2; + break; + /*case SEQ_SUBR:*/ + case SEQ_RETN: + US_DEBUGP("(SEQ_SUBR|0x%02x), 0x%02x,\n",s[i]&0x0f,s[i+1]); + i+=2; + break; + + default: + US_DEBUGP("unknown op code :(\n"); + return; + } + } + while(i 1) { + printk(KERN_CRIT "uss725 driver unusable!\n"); + return USS725_ERROR; + } + + if ( len < 3 ) { + US_DEBUGP("uss725 Bad microcode sequence! (only %d bytes long)\n",len); + return USS725_ERROR; + } + + memcpy(buf,seq,len); + value = (seq[2] << 8) | seq[0]; + index = seq[1]; + ptr = buf + 3; + + rc = usb_control_msg(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev,0), + SEQ_LOAD, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + ptr, + len-3, SEQ_TIMEOUT * HZ); + if ( rc >= 0 ) { + recursive = 0; + return USS725_GOOD; + } + + US_DEBUGP("uss725 sequence load failed! (%d)\n",rc); + if(uss725_get_seq_status(us,&seqStatus) == USS725_GOOD) { + if (seqStatus.statusFlags & SEQSTATUSF_ACTIVE) { + /* abort sequencer */ + US_DEBUGP("Aborting Sequencer\n"); + uss725_abort_seq(us); + } +#ifdef CONFIG_USB_STORAGE_DEBUG + print_seqstatus(us,seqStatus); + uss725_debug_seq(seq,len,seqStatus.seqPos); +#endif + uss725_seq_reset_regs(us); + } else + US_DEBUGP("Unable to get sequencer status\n"); + + return USS725_ERROR; +} + +int uss725_read_ata_reg(struct us_data* us, int reg, unsigned char* buf) +{ + unsigned char seq_read_ata_reg[] = + { + 0x00,0x00,SEQ_BLOCKING_BIT, + (SEQ_RRMW|REG_CCTR), REG_CCTR_MANUAL_REVERSE, REG_CCTR_MANUAL_REVERSE, + (SEQ_RRMW|REG_SETU), 0x00, REG_SETU_HOLD_BULK, + (SEQ_RWRI|REG_ATAA), reg, + (SEQ_DATI|REG_EPPA), + (SEQ_RRMW|REG_SETU), REG_SETU_HOLD_BULK, REG_SETU_HOLD_BULK, + (SEQ_RRMW|REG_CCTR), 0x00, REG_CCTR_MANUAL_REVERSE + }; + unsigned int partial; + int rc; + + /* send sequence to read a register */ + rc = uss725_send_seq(us, seq_read_ata_reg, sizeof(seq_read_ata_reg)); + if (rc) { + US_DEBUGP("uss725 ATA register read %d failed! (%d)\n", + reg,rc); + return USS725_ERROR; + } + + /* read resulting byte */ + rc = usb_stor_bulk_msg(us, + buf, + usb_rcvbulkpipe(us->pusb_dev,us->ep_in), + 1, &partial); + if (rc) { + US_DEBUGP("uss725 ATA register bulk read failed! (%d)\n", rc); + return USS725_ERROR; + } + US_DEBUGP("uss725 seq: read ata reg %d = 0x%02x\n",reg,*buf); + + return USS725_GOOD; +} + +int uss725_write_ata_reg(struct us_data* us, int reg, int data) +{ + unsigned char seq_write_ata_reg[] = + { + /* Loop Count (WORD) & Block bit (bit 8 of BYTE) */ + 0x00,0x00,SEQ_BLOCKING_BIT, + (SEQ_RWRI|REG_ATAA), reg, + (SEQ_RWRI|REG_EPPA), data + }; + int rc; + + /* send sequence to write a register */ + US_DEBUGP("uss725 seq: write ata reg %d = 0x%02x\n",reg,data); + rc = uss725_send_seq(us, + seq_write_ata_reg, + sizeof(seq_write_ata_reg)); + if (rc) { + US_DEBUGP("uss725 ATA register write %d = %d failed! (%d)\n", + reg,data,rc); + return USS725_ERROR; + } + + return USS725_GOOD; +} + +/************************************************************************** + * uss725_wait_bsy + * + * Wait for device to not be busy + */ +int uss725_wait_bsy( struct us_data* us, int timeout ) +{ + unsigned long start = jiffies; + int state = WAIT_STATE_BUSY; + + US_DEBUGP("uss725: wait_for_bsy()\n"); + + while ( WAIT_STATE_BUSY == state ) { + int rc = USS725_ERROR; + + /* the hands-on version */ + while ( jiffies < (start + timeout*HZ) ) { + unsigned char status; + rc = uss725_read_ata_reg(us,ATA_ADDRESS_STATUS,&status); + if (rc) + break; + if ( status & ATA_STATUS_BUSY ) { + US_DEBUGP("Device still BUSY, resubmit requested\n"); + rc = USS725_ERROR; /* signal error if loop times out */ + wait_ms(100); + } + else if ( status & ATA_STATUS_ERROR ) { + US_DEBUGP("Device ERROR, return device error\n"); + state = WAIT_STATE_DEVICE_ERROR; + break; + } + else if ( status & ATA_STATUS_DRQ ) { + US_DEBUGP("Device DRQ detected\n"); + state = WAIT_STATE_DEVICE_DRQ; + break; + } + else { + state = WAIT_STATE_SUCCESS; + break; + } + } + } + return state; +} + +/************************************************************************** + * uss725_identify + * + * Attempt to process an ATA/ATAPI Identify request + */ +int uss725_identify( struct us_data* us, int atapi ) +{ + /* line 1343 */ + int buflen = sizeof(struct hd_driveid); + struct uss725_info *info = (struct uss725_info *)us->extra; + + unsigned char seq_identify_cmd[] = { + 0x00,0x00,SEQ_BLOCKING_BIT, + (SEQ_RWRI|REG_ATAA), ATA_ADDRESS_DEV_HEAD, + (SEQ_RWRI|REG_EPPA), ATA_ADDRESS_DEVHEAD_STD | info->DeviceHead, + (SEQ_RWRI|REG_ATAA), ATA_ADDRESS_ALT_STATUS, + (SEQ_RRCM|REG_EPPA), 0xFF, 0xFF, 0x00, ATA_STATUS_BUSY, + (SEQ_RWRI|REG_ATAA), ATA_ADDRESS_COMMAND, + (SEQ_RWRI|REG_EPPA), atapi ? WIN_PIDENTIFY : WIN_IDENTIFY + }; + + unsigned char seq_identify_move_data[] = { + 0x00,0x00,0x00, + (SEQ_RRMW|REG_CCTR), 0x80,0x80, + (SEQ_RWRI|REG_ATAA), ATA_ADDRESS_DATA, + (SEQ_RRMW|REG_SETU), 0x00,0x80, + (SEQ_RRMW|REG_CTRL), 0x20,0x20, + (SEQ_WBIB|0x00), (buflen-1)>>8, buflen-1, + (SEQ_RRMW|REG_CTRL), 0x00,0x20, + (SEQ_RRMW|REG_SETU), 0x80,0x80, + (SEQ_RRMW|REG_CCTR), 0x00,0x80 + }; + int rc, rest; + + /* send IDENTIFY command */ + US_DEBUGP("uss725 seq: identify\n"); + rc = uss725_send_seq(us, seq_identify_cmd, sizeof(seq_identify_cmd)); + if (rc) { + US_DEBUGP("uss725 seq_identify_cmd failed! (%d)\n", rc); + return USS725_ERROR; + } + + /* wait for DRQ */ + rc = uss725_wait_bsy(us, BSY_TIMEOUT); + if ( rc != WAIT_STATE_DEVICE_DRQ ) { + US_DEBUGP("Failed waiting for DRQ\n"); + return USS725_ERROR; + } + + /* send "move data" sequence */ + US_DEBUGP("uss725 seq: move identify data\n"); + rc = uss725_send_seq(us, seq_identify_move_data, sizeof(seq_identify_move_data)); + if (rc) { + US_DEBUGP("uss725 seq identify move data failed! (%d)\n", rc); + return USS725_ERROR; + } + + /* read the data */ + rc = usb_stor_bulk_msg(us, + &(((struct uss725_info*)(us->extra))->drive), + usb_rcvbulkpipe(us->pusb_dev,us->ep_in), + sizeof(struct hd_driveid), &rest); + if (rc) { + US_DEBUGP("uss725 failed reading identify data! (%d)\n", rc); + return USS725_ERROR; + } + + /* wait for device to finish */ + rc = uss725_wait_bsy(us, BSY_TIMEOUT); + if (rc) { + US_DEBUGP("uss725 wait_bsy failed! (%d)\n", rc); + return USS725_ERROR; + } + + return USS725_GOOD; +} + +int uss725_cmdseq( struct us_data* us, + struct ata_regs* ataRegs ) +{ + /* line 4548 */ + struct uss725_info* info = (struct uss725_info *)us->extra; + unsigned char buf[128]; + unsigned char* ptr = buf; + int seqBsyBeforeDevSel, seqBsyBeforeCmd, seqBsyAfterCmd, seqErrAfterCmd, seqDrqAfterCmd; + + /* setup sequence header */ + *ptr++ = 0x00; + *ptr++ = 0x00; + *ptr++ = SEQ_BLOCKING_BIT; + + /* set the manual reverse bit */ + *ptr++ = (SEQ_RRMW|REG_CCTR); + *ptr++ = REG_CCTR_MANUAL_REVERSE; + *ptr++ = REG_CCTR_MANUAL_REVERSE; + + /* poll STATUS for !BSY */ + *ptr++ = (SEQ_RWRI|REG_ATAA); + *ptr++ = ATA_ADDRESS_ALT_STATUS; + *ptr++ = (SEQ_RRCM|REG_EPPA); + *ptr++ = 0xFF; + *ptr++ = 0xFF; + *ptr++ = 0x00; + *ptr++ = 0x80; + seqBsyBeforeDevSel = (ptr - buf) - 3; + // -3 for header bytes that the chip doesn't receive + + /* setup head register */ + *ptr++ = (SEQ_RWRI|REG_ATAA); + *ptr++ = ATA_ADDRESS_DEV_HEAD; + *ptr++ = (SEQ_RWRI|REG_EPPA); + *ptr++ = ATA_ADDRESS_DEVHEAD_STD | info->DeviceHead; + + /* poll STATUS for !BSY & DRDY */ + *ptr++ = (SEQ_RWRI|REG_ATAA); + *ptr++ = ATA_ADDRESS_ALT_STATUS; + *ptr++ = (SEQ_RRCM|REG_EPPA); + *ptr++ = 0xFF; + *ptr++ = 0xFF; + *ptr++ = (0x00 | ATA_STATUS_DRDY); + *ptr++ = (ATA_STATUS_BUSY | ATA_STATUS_DRDY); + seqBsyBeforeCmd = (ptr - buf) - 3; + + /* setup required ATA regs */ + if (ataRegs->regFlags & ATA_REGMASK_FEATURES) + { + *ptr++ = (SEQ_RWRI|REG_ATAA); + *ptr++ = ATA_ADDRESS_FEATURES; + *ptr++ = (SEQ_RWRI|REG_EPPA); + *ptr++ = ataRegs->features; + } + if (ataRegs->regFlags & ATA_REGMASK_SECTOR_COUNT) + { + *ptr++ = (SEQ_RWRI|REG_ATAA); + *ptr++ = ATA_ADDRESS_SECT_CNT; + *ptr++ = (SEQ_RWRI|REG_EPPA); + *ptr++ = ataRegs->sectorCount; + } + if (ataRegs->regFlags & ATA_REGMASK_SECTOR_NUM) + { + *ptr++ = (SEQ_RWRI|REG_ATAA); + *ptr++ = ATA_ADDRESS_SECT_NUM; + *ptr++ = (SEQ_RWRI|REG_EPPA); + *ptr++ = ataRegs->sectorNum; + } + if (ataRegs->regFlags & ATA_REGMASK_CYLINDER) + { + *ptr++ = (SEQ_RWRI|REG_ATAA); + *ptr++ = ATA_ADDRESS_CYL_HIGH; + *ptr++ = (SEQ_RWRI|REG_EPPA); + *ptr++ = (unsigned char)(ataRegs->cylinder>>8); + *ptr++ = (SEQ_RWRI|REG_ATAA); + *ptr++ = ATA_ADDRESS_CYL_LOW; + *ptr++ = (SEQ_RWRI|REG_EPPA); + *ptr++ = (unsigned char)ataRegs->cylinder; + } + if (ataRegs->regFlags & ATA_REGMASK_HEAD) + { + *ptr++ = (SEQ_RWRI|REG_ATAA); + *ptr++ = ATA_ADDRESS_DEV_HEAD; + *ptr++ = (SEQ_RWRI|REG_EPPA); + *ptr++ = (ATA_ADDRESS_DEVHEAD_STD | + info->DeviceHead | + ataRegs->head); + } + + /* setup command register */ + *ptr++ = (SEQ_RWRI|REG_ATAA); + *ptr++ = ATA_ADDRESS_COMMAND; + *ptr++ = (SEQ_RWRI|REG_EPPA); + *ptr++ = (unsigned char)(ataRegs->cmd); + + /* check to determine if to wait for !BSY and check !ERR after cmd */ + if (ataRegs->cmdFlags & ATA_CMDMASK_NO_CHECK_FOR_BSY_AND_ERR_AFTER_CMD) + { + US_DEBUGP(" Skipping check for !BSY and ERR\n"); + + /* set to some bogus value */ + seqBsyAfterCmd = seqErrAfterCmd = seqDrqAfterCmd = 0xFF; + } + else + { + /* add check for !BSY */ + if (ataRegs->cmdFlags & ATA_CMDMASK_WILL_XFER_DATA) + { + /* set to Alt Status if tranfering data */ + *ptr++ = (SEQ_RWRI|REG_ATAA); + *ptr++ = ATA_ADDRESS_ALT_STATUS; + } + else + { + *ptr++ = (SEQ_RWRI|REG_ATAA); + *ptr++ = ATA_ADDRESS_STATUS; + } + /* wait !BSY */ + *ptr++ = (SEQ_RRCM|REG_EPPA); + *ptr++ = 0xFF; + *ptr++ = 0xFF; + *ptr++ = 0x00; + *ptr++ = 0x80; + seqBsyAfterCmd = (ptr - buf) - 3; + + *ptr++ = (SEQ_RWRI|REG_ATAA); + *ptr++ = ATA_ADDRESS_STATUS; + + /* add check for !ERR */ + *ptr++ = (SEQ_RRCM|REG_EPPA); + *ptr++ = 0x00; + *ptr++ = 0x00; + *ptr++ = 0x00; + *ptr++ = 0x01; + seqErrAfterCmd = (ptr - buf) - 3; + + /* check to determine if to wait for DRQ after cmd */ + if (ataRegs->cmdFlags & ATA_CMDMASK_CHECK_FOR_DRQ_AFTER_CMD) + { + *ptr++ = (SEQ_RRCM|REG_EPPA); + *ptr++ = 0x00; + *ptr++ = 0x00; + *ptr++ = ATA_STATUS_DRQ; + *ptr++ = ATA_STATUS_DRQ; + seqDrqAfterCmd = (ptr - buf) - 3; + } + else + { + /* set to some bogus value */ + seqDrqAfterCmd = 0xFF; + } + } + /* clear the manual reverse bit */ + *ptr++ = (SEQ_RRMW|REG_CCTR); + *ptr++ = 0x00; + *ptr++ = REG_CCTR_MANUAL_REVERSE; + + return uss725_send_seq(us, buf, ptr - buf); +} + +int uss725_write_reg(struct us_data* us, int reg, int data) +{ + /* line 7583 */ + u16 value = (reg << 8) | data; + + if(usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), + SET_1284_REGISTER, 0x40, value, 0, NULL, 0, + HZ)) { + US_DEBUGP("uss725 register write %d = %d failed!\n", + reg,data); + return USS725_ERROR; + } + US_DEBUGP("uss725 register write: %d = %d\n",reg,data); + + return USS725_GOOD; +} + +/************************************************************************** + * uss725_soft_reset + * + * Perform soft reset + */ +int uss725_soft_reset(struct us_data* us) +{ + if(usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), + SOFT_RESET, 0x21, 0, 0, NULL, 0, HZ) >= 0) { + US_DEBUGP(" USS725 soft reset sent\n"); + return USS725_GOOD; + } + + US_DEBUGP(" Request for USS725 soft reset failed!\n"); + + return USS725_ERROR; +} + +/*********************************************************************** + * Helper routines + ***********************************************************************/ + +/************************************************************************** + * uss725_build_sense + * + * Builds an artificial sense buffer to report the results of a + * failed command. + * + * RETURNS: + * void + */ +void uss725_build_sense(struct us_data *us, Scsi_Cmnd *srb) +{ + struct uss725_info *info = (struct uss725_info *)us->extra; + struct sense_data *buf = (struct sense_data *) &srb->sense_buffer[0]; + unsigned char error = info->ATARegs[IDE_ERROR_OFFSET]; + + if(error & MCR_ERR) { /* media changed */ + 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; + } +} + +/*********************************************************************** + * Data transfer routines + ***********************************************************************/ + +int uss725_data_seq_thread(void * data) { + struct us_data * us = (struct us_data *) data; + struct uss725_info* info = (struct uss725_info *)us->extra; + int rc; + +#ifdef CONFIG_SMP + lock_kernel(); +#endif + daemonize(); + reparent_to_init(); + + spin_lock_irq(¤t->sigmask_lock); + flush_signals(current); + sigfillset(¤t->blocked); + siginitsetinv(¤t->blocked, sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM)); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + /* set our name for identification purposes */ + sprintf(current->comm, "uss725-xfer-%d", us->host_number); + +#ifdef CONFIG_SMP + unlock_kernel(); +#endif + + /* read/write datas */ + rc = usb_stor_bulk_msg(us, + info->xfer_buffer, + info->xfer_pipe, + info->xfer_len_buffer,&info->xfer_len_buffer); + + if (rc) + US_DEBUGP("uss725 data_seq_thread: failed! (%d)\n", rc); + else + US_DEBUGP("uss725 data_seq_thread: data ok\n"); + + info->exit_code = rc; + set_current_state(TASK_INTERRUPTIBLE); + complete(&(info->notify)); + return 0; +} + +int uss725_data_seq(struct us_data* us, unsigned char dataDirection, unsigned int length) +{ + struct uss725_info* info = (struct uss725_info *)us->extra; + unsigned int blocks = length / ATA_BYTES_PER_BLOCK; + unsigned char hiXfer = (ATA_BYTES_PER_BLOCK-1) >> 8; + unsigned char loXfer = (ATA_BYTES_PER_BLOCK-1) & 0xff; + unsigned char seq_data[128]; + unsigned char * ptr = seq_data; + int rc,pid; + + /* seq_data_init */ + *ptr++ = (unsigned char)(blocks-1); + *ptr++ = (unsigned char)((blocks-1)>>8); + *ptr++ = SEQ_BLOCKING_BIT; + + *ptr++ = (SEQ_RRMW|REG_CCTR); *ptr++ = REG_CCTR_MANUAL_REVERSE; *ptr++ = REG_CCTR_MANUAL_REVERSE; + *ptr++ = (SEQ_RWRI|REG_ATAA); *ptr++ = ATA_ADDRESS_ALT_STATUS; + *ptr++ = (SEQ_RRCM|REG_EPPA); *ptr++ = 0x00; *ptr++ = 0x00; *ptr++ = ATA_STATUS_DRQ; *ptr++ = ATA_STATUS_DRQ; + /* enable flow data to buffer */ + *ptr++ = (SEQ_RRMW|REG_SETU); *ptr++ = 0x00; *ptr++ = REG_SETU_HOLD_BULK; + *ptr++ = (SEQ_RWRI|REG_ATAA); *ptr++ = ATA_ADDRESS_DATA; + + if (dataDirection == SCSI_DATA_READ) { + /* seq_data_in */ + /* Set Direction bit to 1 (Input Mode): */ + *ptr++ = (SEQ_RRMW|REG_CTRL); *ptr++ = REG_CTRL_DIRECTION_IN; *ptr++ = REG_CTRL_DIRECTION_IN; + *ptr++ = (SEQ_WBIB|0x00); *ptr++ = hiXfer; *ptr++ = loXfer; + *ptr++ = (SEQ_RRMW|REG_CTRL); *ptr++ = 0x00; *ptr++ = REG_CTRL_DIRECTION_IN; + } else { + /* seq_data_out */ + /* Set Direction bit to 0 (Output Mode): */ + *ptr++ = (SEQ_RRMW|REG_CTRL); *ptr++ = 0x00; *ptr++ = REG_CTRL_DIRECTION_IN; + *ptr++ = (SEQ_WBOB|0x00); *ptr++ = hiXfer; *ptr++ = loXfer; + *ptr++ = (SEQ_RRMW|REG_CTRL); *ptr++ = REG_CTRL_DIRECTION_IN; *ptr++ = REG_CTRL_DIRECTION_IN; + } + /* seq_data_end */ + *ptr++ = (SEQ_RRMW|REG_SETU); *ptr++ = REG_SETU_HOLD_BULK; *ptr++ = REG_SETU_HOLD_BULK; + *ptr++ = (SEQ_RWRI|REG_ATAA); *ptr++ = ATA_ADDRESS_ALT_STATUS; + *ptr++ = (SEQ_RRCM|REG_EPPA); *ptr++ = 0xFF; *ptr++ = 0xFF; *ptr++ = 0x00; *ptr++ = ATA_STATUS_BUSY; + *ptr++ = (SEQ_RWRI|REG_ATAA); *ptr++ = ATA_ADDRESS_STATUS; + *ptr++ = (SEQ_RRCM|REG_EPPA); *ptr++ = 0x00; *ptr++ = 0x00; *ptr++ = 0x00; *ptr++ = ATA_STATUS_ERROR; + *ptr++ = (SEQ_RRMW|REG_CCTR); *ptr++ = 0x00; *ptr++ = REG_CCTR_MANUAL_REVERSE; + + + /* wait for DRQ */ + rc = uss725_wait_bsy(us, BSY_TIMEOUT); + if ( rc != WAIT_STATE_DEVICE_DRQ ) { + US_DEBUGP("uss725 data_seq: Failed waiting for DRQ\n"); + return USS725_ERROR; + } + + init_completion(&(info->notify)); + /* start thread */ + pid = kernel_thread(uss725_data_seq_thread, us, + CLONE_VM); + if (pid < 0) { + US_DEBUGP("uss725 data_seq: Unable to start thread\n"); + return USS725_ERROR; + } + + /* send "move data" sequence */ + rc = uss725_send_seq(us, seq_data, ptr - seq_data); + if (rc) { + US_DEBUGP("uss725 data_seq: seq_data failed! (%d)\n", rc); + return USS725_ERROR; + } + + /* wait thread finish */ + wait_for_completion(&(info->notify)); + US_DEBUGP("uss725 data_seq: wait ok\n"); + + return USS725_GOOD; +} + +/************************************************************************** + * 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. + */ +int uss725_transfer_partial( struct us_data *us, + unsigned char dataDirection, + char *buf, int length ) +{ + struct uss725_info* info = (struct uss725_info *)us->extra; + int result; + int partial; + + US_DEBUGP("uss725_transfer_partial(): xfer %d bytes\n", length); + + info->xfer_buffer = buf; + info->xfer_len_buffer = length; + /* transfer the data */ + if (dataDirection == SCSI_DATA_READ) { + US_DEBUGP("uss725_transfer_partial(): reading ...\n"); + info->xfer_pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); + } else { + US_DEBUGP("uss725_transfer_partial(): writing ...\n"); + info->xfer_pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); + } + + if (uss725_data_seq(us,dataDirection,length) != USS725_GOOD) { + US_DEBUGP("uss725_transfer_partial(): device ERROR\n"); + return USS725_TRANSPORT_ERROR; + } + + partial = info->xfer_len_buffer; + result = info->exit_code; + 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", info->xfer_pipe); + usb_stor_clear_halt(us, info->xfer_pipe); + return USS725_TRANSPORT_FAILED; + } + + /* did we send all the data? */ + if (info->xfer_len_buffer == length) { + US_DEBUGP("uss725_transfer_partial(): transfer complete\n"); + return USS725_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("uss725_transfer_partial(): device NAKed\n"); + return USS725_TRANSPORT_FAILED; + } + + /* -ENOENT -- we canceled this transfer */ + if (result == -ENOENT) { + US_DEBUGP("uss725_transfer_partial(): transfer aborted\n"); + return USS725_TRANSPORT_ABORTED; + } + + /* the catch-all case */ + US_DEBUGP("uss725_transfer_partial(): unknown error\n"); + return USS725_TRANSPORT_FAILED; + } + + /* no error code, so we must have transferred some data, + * just not all of it */ + return USS725_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. + */ +void uss725_transfer( struct us_data *us, Scsi_Cmnd *srb ) +{ + int i; + struct scatterlist *sg; + unsigned int total_transferred = 0; + int result = USS725_TRANSPORT_ERROR; + 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) { + US_DEBUGP(" uss725 : scatter-gathering\n"); + + /* 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 = uss725_transfer_partial(us, + srb->sc_data_direction, + sg[i].address, + sg[i].length); + total_transferred += sg[i].length; + } else + result = uss725_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 = uss725_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 + ***********************************************************************/ + +/************************************************************************** + * uss725_action + * + * Routine for sending commands to the uss725 + * + * RETURNS: + * USS status code + */ +int uss725_action( struct us_data *us, int action, void* pointer, int value) +{ + struct uss725_info *info = (struct uss725_info *)us->extra; + int status = USS725_GOOD; + + switch ( action ) { + case ACTION_READ_STATUS: { + unsigned char regs[8]; + US_DEBUGP(" uss725_action(READ_STATUS)\n"); + if((status = uss725_read_ata_reg(us,ATA_ADDRESS_ALT_STATUS,®s[IDE_STATUS_OFFSET])) != USS725_GOOD) + break; + if((status = uss725_read_ata_reg(us,ATA_ADDRESS_ERROR,®s[IDE_ERROR_OFFSET])) != USS725_GOOD) + break; + if((status = uss725_read_ata_reg(us,ATA_ADDRESS_CYL_LOW,®s[IDE_LCYL_OFFSET])) != USS725_GOOD) + break; + if((status = uss725_read_ata_reg(us,ATA_ADDRESS_CYL_HIGH,®s[IDE_HCYL_OFFSET])) != USS725_GOOD) + break; + memcpy(pointer,regs,8); + break; + } + case ACTION_ENUM: + US_DEBUGP(" uss725_action(ENUM,0x%02x)\n", ATA_ADDRESS_DEVHEAD_STD|value); + status = uss725_write_ata_reg(us,ATA_ADDRESS_DEV_HEAD, ATA_ADDRESS_DEVHEAD_STD|value); + break; + + case ACTION_RESET: + US_DEBUGP(" uss725_action(RESET)\n"); + status = uss725_write_ata_reg(us, + ATA_ADDRESS_DEV_CNTRL, + ATA_DC_RESET_CONTROLLER); + break; + + case ACTION_REENABLE: + US_DEBUGP(" uss725_action(REENABLE)\n"); + status = uss725_write_ata_reg(us, + ATA_ADDRESS_DEV_CNTRL, + ATA_DC_REENABLE_CONTROLLER); + break; + + case ACTION_SOFT_RESET: + US_DEBUGP(" uss725_action(SOFT_RESET)\n"); + if((status = uss725_write_ata_reg(us, + ATA_ADDRESS_DEV_HEAD, + ATA_ADDRESS_DEVHEAD_STD | info->DeviceHead)) != USS725_GOOD) + break; + status = uss725_write_ata_reg(us, + ATA_ADDRESS_COMMAND, + WIN_SRST); + break; + + case ACTION_IDENTIFY: + US_DEBUGP(" uss725_action(IDENTIFY)\n"); + /* Only ATA is supported */ + status = uss725_identify(us,FALSE); + break; + + default: + US_DEBUGP("Error: Undefined action %d\n",action); + break; + } + + if (status != USS725_GOOD) { + US_DEBUGP(" uss725_action(0x%02x) error: %d\n",action,status); + /* need to reset device here */ + } + + return status; +} + +/************************************************************************** + * uss725_read_regs + * + * Read ATA Registers + * + * RETURNS: + * USS status code + */ +int uss725_read_regs( struct us_data *us ) +{ + struct uss725_info *info = (struct uss725_info *)us->extra; + int retStatus = USS725_GOOD; + int transferStatus; + + US_DEBUGP("Entering uss725_IssueATAReadRegs\n"); + + transferStatus = uss725_action(us, ACTION_READ_STATUS, + info->ATARegs, sizeof(info->ATARegs)); + if (transferStatus != USS725_GOOD) { + US_DEBUGP(" Error reading ATA registers\n"); + retStatus = USS725_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 receive the response. + */ +void uss725_invoke_transport( struct us_data *us, + Scsi_Cmnd *srb) +{ + int need_auto_sense = 0; + int transferStatus; + + /* send the command to the transport layer */ + uss725_transfer(us,srb); + + switch (srb->result) { + + case USS725_TRANSPORT_GOOD: + /* Indicate a good result */ + srb->result = GOOD; + break; + + case USS725_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 USS725_TRANSPORT_FAILED: + US_DEBUGP("-- transport indicates command failure\n"); + need_auto_sense = 1; + break; + + case USS725_TRANSPORT_ERROR: + US_DEBUGP("-- transport indicates transport failure\n"); + srb->result = DID_ERROR << 16; + break; + + case USS725_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; + break; + } + + if (need_auto_sense) + if (uss725_read_regs(us) == USS725_GOOD) + uss725_build_sense(us, srb); + + /* Regardless of auto-sense, if we _know_ we have an error + * condition, show that in the result code + */ + if (transferStatus == USS725_TRANSPORT_FAILED) + srb->result = CHECK_CONDITION; +} + + + +/************************************************************************** + * uss725_atapi_soft_reset + * + * Perform an Atapi Soft Reset on the device + * + * RETURNS: + * NT status code + */ +int uss725_atapi_soft_reset( struct us_data *us ) +{ + int retStatus = USS725_GOOD; + int transferStatus; + + US_DEBUGP("Entering uss725_atapi_soft_reset\n"); + + transferStatus = uss725_action( us, ACTION_SOFT_RESET, NULL, 0 ); + if (transferStatus != USS725_TRANSPORT_GOOD) { + US_DEBUGP(" Error issuing Atapi Soft Reset\n"); + retStatus = USS725_ERROR; + } + + US_DEBUGP("Leaving uss725_atapi_soft_reset %08X\n", retStatus); + return retStatus; +} + + +/************************************************************************** + * uss725_srst + * + * Perform an SRST on the device + * + * RETURNS: + * USS status code + */ +int uss725_srst( struct us_data *us ) +{ + int retStatus = USS725_GOOD; + int transferStatus; + + US_DEBUGP("Entering uss725_SRST\n"); + + transferStatus = uss725_action( us, ACTION_RESET, NULL, 0 ); + + /* check to see if this request failed */ + if (transferStatus != USS725_TRANSPORT_GOOD) { + US_DEBUGP(" Error issuing SRST\n"); + retStatus = USS725_ERROR; + } else { + /* delay 10ms to give the drive a chance to see it */ + wait_ms(10); + + transferStatus = uss725_action( us, ACTION_REENABLE, NULL, 0 ); + if (transferStatus != USS725_TRANSPORT_GOOD) { + US_DEBUGP(" Error taking drive out of reset\n"); + retStatus = USS725_ERROR; + } else { + /* delay 50ms to give the drive a chance to recover after SRST */ + wait_ms(50); + } + } + + US_DEBUGP("Leaving uss725_srst %08X\n", retStatus); + return retStatus; +} + + +/************************************************************************** + * uss725_try_enum + * + * Helper function for uss725_manual_enum(). Does ENUM and READ_STATUS + * and tries to analyze the status registers + * + * RETURNS: + * USS status code + */ +int uss725_try_enum(struct us_data *us, unsigned char master_slave, + int detect ) +{ + int status = USS725_GOOD; + unsigned char regs[8]; + unsigned long endTime; + struct uss725_info *info = (struct uss725_info *)us->extra; + int recheckAsMaster = FALSE; + + if ( detect ) + endTime = jiffies + USS725_ENUM_DETECT_TIMEOUT * HZ; + else + endTime = jiffies + USS725_ENUM_BSY_TIMEOUT * HZ; + + /* loop until we detect !BSY or timeout */ + while(TRUE) { +#ifdef CONFIG_USB_STORAGE_DEBUG + char* mstr = master_slave == ATA_ADDRESS_DEVHEAD_STD ? + "Master" : "Slave"; +#endif + + status = uss725_action( us, ACTION_ENUM, NULL, master_slave ); + if ( status != USS725_GOOD ) + break; + + status = uss725_action( us, ACTION_READ_STATUS, + regs, sizeof(regs) ); + if ( status != USS725_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 = uss725_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; + } else { + /* wait a while for response */ + wait_ms(500); + } + } + + return status; +} + +/************************************************************************** + * uss725_manual_enum + * + * Determines if the drive attached is an ATA or ATAPI and if it is a + * master or slave. + * + * RETURNS: + * USS status code + */ +int uss725_manual_enum(struct us_data *us) +{ + int retStatus = USS725_GOOD; + + US_DEBUGP("Entering uss725_manual_enum\n"); + + /* master or slave? */ + retStatus = uss725_try_enum( us, ATA_ADDRESS_DEVHEAD_STD, FALSE ); + if (retStatus == USS725_GOOD) + retStatus = uss725_try_enum( us, ATA_ADDRESS_DEVHEAD_SLAVE, FALSE ); + + if (retStatus == USS725_GOOD) { + retStatus = uss725_srst(us); + if (retStatus == USS725_GOOD) + /* ata or atapi? */ + retStatus = uss725_try_enum( us, ATA_ADDRESS_DEVHEAD_STD, TRUE ); + } + + US_DEBUGP("Leaving uss725_manual_enum %08X\n", retStatus); + return(retStatus); +} + + +/************************************************************************** + * uss725_get_inquiry_data + * + * Get inquiry data + * + * RETURNS: + * USS status code + */ +int uss725_get_inquiry_data( struct us_data *us ) +{ + struct uss725_info *info = (struct uss725_info *)us->extra; + int retStatus = USS725_GOOD; + + US_DEBUGP("Entering uss725_get_inquiry_data\n"); + + /* set default to Master */ + info->DeviceHead = ATA_ADDRESS_DEVHEAD_STD; + + /* attempt to manually enumerate this device */ + retStatus = uss725_manual_enum(us); + if (retStatus == USS725_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 = uss725_action( us, ACTION_IDENTIFY, + &info->drive, + sizeof(struct hd_driveid) ); + if (transferStatus != USS725_TRANSPORT_GOOD) { + /* Error issuing ATA Command Identify */ + US_DEBUGP(" Error issuing ATA Command Identify\n"); + retStatus = USS725_ERROR; + } else { + /* ATA Command Identify successful */ + int i; + __u16 *src, *dest; + ide_fix_driveid(&info->drive); + + 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 */ + src = (__u16*)info->drive.model; + dest = (__u16*)info->InquiryData.VendorId; + for (i=0;i<4;i++) + dest[i] = be16_to_cpu(src[i]); + + src = (__u16*)(info->drive.model+8); + dest = (__u16*)info->InquiryData.ProductId; + for (i=0;i<8;i++) + dest[i] = be16_to_cpu(src[i]); + + src = (__u16*)info->drive.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 (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 + */ + US_DEBUGP("ATAPI device not supported\n"); + retStatus = USS725_ERROR; + } + } + + US_DEBUGP("Leaving uss725_get_inquiry_data %08X\n", retStatus); + + return(retStatus); +} + +/************************************************************************** + * uss725_data_copy + * + * Copy data into the srb request buffer. Use scatter gather if required. + * + * RETURNS: + * void + */ +void uss725_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 && src) + memcpy(srb->request_buffer, src, len); + } +} + +/************************************************************************** + * uss725_transport + * + * Translate SCSI commands to ATA commands, and transport it + * + * RETURNS: + * USB_STOR_TRANSPORT_GOOD + * FALSE otherwise + */ +int uss725_transport(Scsi_Cmnd *srb, struct us_data *us) +{ + struct uss725_info *info = (struct uss725_info *)us->extra; + 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 }; + struct ata_regs ataRegs; + int status = USB_STOR_TRANSPORT_GOOD; + + memset(&ataRegs, 0, sizeof(struct ata_regs)); + + /* 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 */ + uss725_data_copy(srb, (char *) &info->InquiryData, srb->request_bufflen); + srb->result = GOOD; + break; + + case MODE_SENSE: + case TEST_UNIT_READY: + US_DEBUGP(" ATA OUT - SCSIOP_MODE_SENSE,SCSIOP_TEST_UNIT_READY\n"); + + /* Initialize the return buffer */ + uss725_data_copy(srb, (char *) &senseData, 8); + + if (info->DeviceFlags & DF_MEDIA_STATUS_ENABLED) + { + ataRegs.regFlags = 0; + ataRegs.cmdFlags = 0; + ataRegs.cmd = WIN_GETMEDIASTATUS; + status = uss725_cmdseq(us, &ataRegs); + srb->request_bufflen = 0; + } else { + US_DEBUGP(" Media Status not supported, just report okay\n"); + srb->result = GOOD; + } + 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(ATA_BYTES_PER_BLOCK); + + if (srb->request_bufflen > sizeof(struct read_capacity_data)) + srb->request_bufflen = sizeof(struct read_capacity_data); + + uss725_data_copy(srb, (char *) &readCapacityData, srb->request_bufflen); + srb->result = GOOD; + 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); + } + ataRegs.regFlags = + ATA_REGMASK_SECTOR_COUNT | + ATA_REGMASK_SECTOR_NUM | + ATA_REGMASK_CYLINDER | + ATA_REGMASK_HEAD; + ataRegs.cmdFlags = ATA_CMDMASK_WILL_XFER_DATA;// | ATA_CMDMASK_CHECK_FOR_DRQ_AFTER_CMD; + ataRegs.sectorCount = blockCount; + ataRegs.sectorNum = sectnum; + ataRegs.cylinder = cylinder; + ataRegs.head = head; + ataRegs.cmd = WIN_READ; + + if((status = uss725_cmdseq(us, &ataRegs) != USS725_GOOD)) + break; + uss725_invoke_transport(us,srb); + 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); + } + ataRegs.regFlags = + ATA_REGMASK_SECTOR_COUNT | + ATA_REGMASK_SECTOR_NUM | + ATA_REGMASK_CYLINDER | + ATA_REGMASK_HEAD; + ataRegs.cmdFlags = ATA_CMDMASK_WILL_XFER_DATA | ATA_CMDMASK_CHECK_FOR_DRQ_AFTER_CMD; + ataRegs.sectorCount = blockCount; + ataRegs.sectorNum = sectnum; + ataRegs.cylinder = cylinder; + ataRegs.head = head; + ataRegs.cmd = WIN_WRITE; + + if((status = uss725_cmdseq(us, &ataRegs) != USS725_GOOD)) + break; + uss725_invoke_transport(us,srb); + 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]); + ataRegs.regFlags = 0; + ataRegs.cmdFlags = 0; + ataRegs.cmd = (srb->cmnd[4] & 0x1) ? WIN_DOORLOCK : WIN_DOORUNLOCK; + status = uss725_cmdseq(us, &ataRegs); + srb->request_bufflen = 0; + } else { + US_DEBUGP(" Not removeable media, just report okay\n"); + srb->result = GOOD; + } + 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 */ + uss725_data_copy(srb, (char *) &senseData, 8); + + ataRegs.regFlags = 0; + ataRegs.cmdFlags = 0; + if ((srb->cmnd[4] & 0x3) == 0x2) { + US_DEBUGP(" Media Eject\n"); + ataRegs.cmd = WIN_MEDIAEJECT; + status = uss725_cmdseq(us, &ataRegs); + } else if ((srb->cmnd[4] & 0x3) == 0x1) { + US_DEBUGP(" Get Media Status\n"); + ataRegs.cmd = WIN_GETMEDIASTATUS; + status = uss725_cmdseq(us, &ataRegs); + srb->request_bufflen = 0; + } else { + US_DEBUGP(" Nothing to do, just report okay\n"); + srb->result = GOOD; + } + break; + + default: + US_DEBUGP("Unsupported SCSI command - 0x%X\n", srb->cmnd[0]); + srb->result = DID_ERROR << 16; + break; + } + + return(status); +} + +/************************************************************************** + * uss725_init_device + * + * Initialize the USS725 device registers. + * + * RETURNS: + * USS status code + */ +int uss725_init_device(struct us_data* us) { + unsigned char seq_init_falstaff[] = { + 0x00,0x00,SEQ_BLOCKING_BIT, // Loop Count (WORD) & Block bit + (SEQ_RRMW|REG_CTRL),0x00,0x04, // if using nInit as reset, set RESET- bit high + (SEQ_NOOP|0x0F),0xFF, // wait for reset to take affect + (SEQ_RRMW|REG_CTRL),0x04,0x04 // if using nInit as reset, set RESET- bit low + }; + +#if 0 + unsigned char seq_init_falstaff_lite[]={ + 0x00,0x00,SEQ_BLOCKING_BIT, // Loop Count (WORD) & Block bit (bit 8 of BYTE) + (SEQ_RRMW|REG_CTRL),0x04,0x0C, // Put falstaff lite in Status mode + (SEQ_NOOP|0x0F),0xFF, // wait for a while before check status + (SEQ_RRCM|REG_STAT),0xFF,0xFF, 0x18, 0x18, // Wait for status to report Device Opterational + (SEQ_RRMW|REG_CTRL),0x0C,0x0C // Put falstaff lite device in Run/Charge mode + }; +#endif + + unsigned char seq_init_device[] = { + 0x00,0x00,SEQ_BLOCKING_BIT, // Loop Count (WORD) & Block bit (bit 8 of BYTE) + (SEQ_RRMW|REG_CCTR),0x40,0x40, // Set Slow RRCM bit + (SEQ_RRMW|REG_CTRL),0x80,0x80, // Set RESET- bit low + (SEQ_RRMW|REG_ECRR),0xE0,0xE0, // Set into ATA Mode + (SEQ_RRMW|REG_CCTR),0x80,0x80, // Set Reverse Bit + (SEQ_RRMW|REG_SETU),REG_SETU_FILTER_SFT_OVERRIDE, + REG_SETU_FILTER_SFT_OVERRIDE, // Disable digital filtering + (SEQ_RWRI|REG_TIMO),0x00, // Set to Timeout Register to 0 + (SEQ_RRMW|REG_CTRL),0x00,0x20, // Set Direction bit to 0 (Output Mode) + (SEQ_RRMW|REG_SETU),0x80,0x80, // Set Hold Bulk bit to 1 (don't allow bulk transfer) + (SEQ_NOOP|0x04),0xAA, // wait for a while before (approx. 20ms) + }; + + int rc = USS725_ERROR; + + do { + if (uss725_soft_reset(us)) + break; + + if (uss725_write_reg(us, REG_CCTR, 0x58)) + break; + + US_DEBUGP("Init falstaff\n"); + if (uss725_send_seq(us, seq_init_falstaff, + sizeof(seq_init_falstaff))) + break; + + US_DEBUGP("Init device\n"); + if (uss725_send_seq(us, seq_init_device, + sizeof(seq_init_device))) + break; + + rc = USS725_GOOD; + } while (0); + + return rc; +} + +/************************************************************************** + * uss725_init_info + * + * Allocates (if necessary) and initializes the driver structure. + * + * RETURNS: + * USS status code + */ +int uss725_init_info(struct us_data *us) +{ + int retStatus = USS725_GOOD; + + if (!us->extra) { + us->extra = (void *) kmalloc(sizeof(struct uss725_info), GFP_KERNEL); + if (!us->extra) { + US_DEBUGP("ERROR - kmalloc failure\n"); + retStatus = USS725_ERROR; + } + } + + if (retStatus == USS725_GOOD) { + memset(us->extra, 0, sizeof(struct uss725_info)); + } + + return(retStatus); +} + +/************************************************************************** + * Initialization for the USS725 + */ + +int uss725_init(struct us_data *us) +{ + US_DEBUGP("USS725 Initialization...\n"); + + /* Initialize USS725 info struct */ + if (uss725_init_info(us) == USS725_ERROR) { + US_DEBUGP("ERROR Initializing USS725 Info struct\n"); + return USS725_ERROR; + } + + if (usb_set_interface(us->pusb_dev, 0, 2 )) { + US_DEBUGP("Failed setting interface\n"); + return USS725_ERROR; + } + + if (uss725_init_device(us) != USS725_GOOD) { + US_DEBUGP("USS725 init_device Failure\n"); + return USS725_ERROR; + } + + /* Get device specific data */ + if (uss725_get_inquiry_data(us) != USS725_GOOD) { + US_DEBUGP("USS725 Initialization Failure\n"); + return USS725_ERROR; + } + + US_DEBUGP("USS725 Initialization complete\n"); + + return USS725_GOOD; +} + +int uss725_reset(struct us_data *us) +{ + printk(KERN_CRIT "uss725 reset called\n"); + + /* We don't really have this feature. */ + /* needed : reset device, init, wait busy,srst */ + return USS725_ERROR; +} + diff -Naur linux-2.4.24.orig/drivers/usb/storage/uss725.h linux-2.4.24/drivers/usb/storage/uss725.h --- linux-2.4.24.orig/drivers/usb/storage/uss725.h 1970-01-01 09:00:00.000000000 +0900 +++ linux-2.4.24/drivers/usb/storage/uss725.h 2004-01-25 06:08:23.000000000 +0900 @@ -0,0 +1,32 @@ +/* Header File for In-System Design, Inc. USS725 + * + * First release + * + * Current development and maintenance by: + * (c) 2002 Nicolas Planel (nplanel@mandrakesoft.com) + * + * See uss725.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_USS725_H +#define _USB_USS725_H + +extern int uss725_init(struct us_data *us); +extern int uss725_transport(Scsi_Cmnd *srb, struct us_data *us); +extern int uss725_reset(struct us_data *us); + +#endif diff -Naur linux-2.4.24.orig/drivers/usb/storage/uss725_regs.h linux-2.4.24/drivers/usb/storage/uss725_regs.h --- linux-2.4.24.orig/drivers/usb/storage/uss725_regs.h 1970-01-01 09:00:00.000000000 +0900 +++ linux-2.4.24/drivers/usb/storage/uss725_regs.h 2004-01-25 06:08:23.000000000 +0900 @@ -0,0 +1,163 @@ +/* Header File for In-System Design, Inc. USS725 + * + * First release + * + * Current development and maintenance by: + * (c) 2002 Planel Nicolas (nplanel@mandrakesoft.com) + * + * See uss725.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_USS725_REGS_H +#define _USB_USS725_REGS_H + +/********************* + ** sequencing info ** + ********************/ + +/* define general register addresses */ +#define REG_DATA 0x00 +#define REG_STAT 0x01 +#define REG_CTRL 0x02 +#define REG_EPPA 0x03 +#define REG_EPPD 0x04 +#define REG_ECPA 0x05 +#define REG_ECRR 0x06 +#define REG_CCTR 0x07 +#define REG_SETU 0x08 +#define REG_TIMO 0x09 +#define REG_ATAA 0x0A +#define NUMBER_OF_REGS 0x0B + +/* define register addresses for read */ +#define REG_STAT_R 0x00 +#define REG_CTRL_R 0x01 +#define REG_ECRR_R 0x02 +#define REG_CCTR_R 0x03 +#define REG_DATA_R 0x04 +#define REG_EPPA_R 0x05 +#define REG_EPPD_R 0x05 +#define REG_SETU_R 0x06 +#define REG_TIMO_R 0x07 +#define REG_ATAA_R 0x08 +#define NUMBER_OF_REGS_R 0x09 + +// +// REG_STAT register definition +// +#define REG_STAT_PPORTBUSY_OR_IOCS16 (0x80) +#define REG_STAT_PPORTACK_OR_IORDY (0x40) +#define REG_STAT_PPORTERR_OR_PDIAG (0x20) +#define REG_STAT_PPORTSEL_OR_INTRQ (0x10) +#define REG_STAT_PPORTFAULT_OR_DASP (0x08) +#define REG_STAT_1284_DISABLED (0x04) +#define REG_STAT_PPORTPLH_OR_DMARQ (0x02) +#define REG_STAT_EPP_TIMEOUT (0x01) + +// +// REG_SETU register definition +// +#define REG_SETU_HOLD_BULK (0x80) +#define REG_SETU_TERMINATION_WAIT (0x40) +#define REG_SETU_CLEAR_BULK_OUT (0x20) +#define REG_SETU_FORCE_NEGOTIATION (0x10) +#define REG_SETU_FORCE_COMPATIBILITY (0x08) +#define REG_SETU_AUTO_HTR (0x04) +#define REG_SETU_FILTER_SFT_OVERRIDE (0x02) +#define REG_SETU_FILTER_ENABLE (0x01) + +#define REG_SETU_STD (REG_SETU_HOLD_BULK|REG_SETU_FILTER_SFT_OVERRIDE) + +// +// REG_CTRL register definition +// +#define REG_CTRL_PPORTHLH_RESET (0x80) +#define REG_CTRL_EPP_MASK (0x40) +#define REG_CTRL_DIRECTION_IN (0x20) // else, is OUT +#define REG_CTRL_INT_ENABLE (0x10) +#define REG_CTRL_PPORTSELECTIN_CSEL (0x08) +#define REG_CTRL_PPORTNINIT_DMACK (0x04) +#define REG_CTRL_PPORTAUTOFD_DIOR (0x02) +#define REG_CTRL_PPORTSTROBE_DIOW (0x01) + +// +// REG_ECCR register definition +// +#define REG_ECCR_MODE_STANDARD (0x00) +#define REG_ECCR_MODE_BIDIRECTIONAL (0x20) +#define REG_ECCR_MODE_COMPATIBILITY (0x40) +#define REG_ECCR_MODE_ECP (0x60) +#define REG_ECCR_MODE_EPP (0x80) +#define REG_ECCR_MODE_ATA (0xE0) +#define REG_ECCR_NACK_INTR (0x10) +#define REG_ECCR_NFAULT_INTR (0x08) +#define REG_ECCR_BULKIN_INTR (0x04) +#define REG_ECCR_BULKIN_EMPTY (0x02) +#define REG_ECCR_BULKOUT_EMPTY (0x01) + +// +// REG_CCTR register definition +// +#define REG_CCTR_MANUAL_REVERSE (0x80) +#define REG_CCTR_SLOW_RCCM (0x40) +#define REG_CCTR_SLOW_INSTRUCTION (0x20) +#define REG_CCTR_RETURN_ON_NO_DATA (0x10) +#define REG_CCTR_NFAULT_INT_MASK (0x08) +#define REG_CCTR_COMPRESS_ENABLE (0x02) +#define REG_CCTR_AUTO_MODE (0x01) + +#define SEQ_RRMW 0x10 +#define SEQ_RRCM 0x20 +#define SEQ_RWRI 0xa0 +#define SEQ_RREN 0x30 +#define SEQ_WBIB 0x40 +#define SEQ_WBOB 0x50 +#define SEQ_DATI 0x60 +#define SEQ_DATO 0x70 +#define SEQ_EPPI 0x90 +#define SEQ_NOOP 0x80 +#define SEQ_SUBR 0xB0 +#define SEQ_RETN 0xB0 +#define SEQ_BLOCKING_BIT 0x80 + +/* commands/requests */ +#define GET_DEVICE_ID 0 +#define GET_PORT_STATUS 1 +#define SOFT_RESET 2 +#define GET_1284_REGISTER 3 +#define SET_1284_REGISTER 4 +#define GET_SEQ_STATUS 5 +#define SEQ_LOAD 6 +#define SEQ_ABORT 7 +#define LOAD_EEPROM 8 +#define CLEAR_BUFFER 9 + +// SEQUENCER_STATUS.statusBits +#define SEQSTATUSF_ACTIVE 0x80 +#define SEQSTATUSF_STOPPED 0x40 +#define SEQSTATUSF_EXECUTING_SUBROUTINE 0x20 + +struct seqstatus { + unsigned char seqPos; + unsigned char globalLoopCountHigh; + unsigned char globalLoopCountLow; + unsigned char statusFlags; + unsigned char instructLoopCountMid; + unsigned char instructLoopCountLow; +}; + +#endif