diff --git a/ata.c b/ata.c index ba278f6..f3b5a43 100644 --- a/ata.c +++ b/ata.c @@ -21,9 +21,9 @@ #include "blockcopy.h" #include "wait.h" -static BYTE write_taskfile_lba(struct IDEUnit *unit, UBYTE command, ULONG lba, UBYTE sectorCount); -static BYTE write_taskfile_lba48(struct IDEUnit *unit, UBYTE command, ULONG lba, UBYTE sectorCount); -static BYTE write_taskfile_chs(struct IDEUnit *unit, UBYTE command, ULONG lba, UBYTE sectorCount); +static BYTE write_taskfile_lba(struct IDEUnit *unit, UBYTE command, ULONG lba, UBYTE sectorCount, UBYTE features); +static BYTE write_taskfile_lba48(struct IDEUnit *unit, UBYTE command, ULONG lba, UBYTE sectorCount, UBYTE features); +static BYTE write_taskfile_chs(struct IDEUnit *unit, UBYTE command, ULONG lba, UBYTE sectorCount, UBYTE features); /** * ata_status_reg_delay @@ -487,7 +487,7 @@ BYTE ata_read(void *buffer, ULONG lba, ULONG count, ULONG *actual, struct IDEUni Trace("ATA: XFER Count: %ld, txn_count: %ld\n",count,txn_count); - if ((error = unit->write_taskfile(unit,command,lba,txn_count)) != 0) { + if ((error = unit->write_taskfile(unit,command,lba,txn_count,0)) != 0) { ata_save_error(unit); return error; } @@ -576,7 +576,7 @@ BYTE ata_write(void *buffer, ULONG lba, ULONG count, ULONG *actual, struct IDEUn Trace("ATA: XFER Count: %ld, txn_count: %ld\n",count,txn_count); - if ((error = unit->write_taskfile(unit,command,lba,txn_count)) != 0) { + if ((error = unit->write_taskfile(unit,command,lba,txn_count,0)) != 0) { ata_save_error(unit); return error; } @@ -644,7 +644,7 @@ void ata_write_unaligned_long(void *source, void *destination) { * @param unit Pointer to an IDEUnit struct * @param lba Pointer to the LBA variable */ -static BYTE write_taskfile_chs(struct IDEUnit *unit, UBYTE command, ULONG lba, UBYTE sectorCount) { +static BYTE write_taskfile_chs(struct IDEUnit *unit, UBYTE command, ULONG lba, UBYTE sectorCount, UBYTE features) { UWORD cylinder = (lba / (unit->heads * unit->sectorsPerTrack)); UBYTE head = ((lba / unit->sectorsPerTrack) % unit->heads) & 0xF; UBYTE sector = (lba % unit->sectorsPerTrack) + 1; @@ -662,6 +662,7 @@ static BYTE write_taskfile_chs(struct IDEUnit *unit, UBYTE command, ULONG lba, U *unit->drive->lbaLow = (UBYTE)(sector); *unit->drive->lbaMid = (UBYTE)(cylinder); *unit->drive->lbaHigh = (UBYTE)(cylinder >> 8); + *unit->drive->error_features = features; *unit->drive->status_command = command; return 0; @@ -673,7 +674,7 @@ static BYTE write_taskfile_chs(struct IDEUnit *unit, UBYTE command, ULONG lba, U * @param unit Pointer to an IDEUnit struct * @param lba Pointer to the LBA variable */ -static BYTE write_taskfile_lba(struct IDEUnit *unit, UBYTE command, ULONG lba, UBYTE sectorCount) { +static BYTE write_taskfile_lba(struct IDEUnit *unit, UBYTE command, ULONG lba, UBYTE sectorCount, UBYTE features) { BYTE devHead; if (!ata_wait_ready(unit,ATA_RDY_WAIT_COUNT)) @@ -687,6 +688,7 @@ static BYTE write_taskfile_lba(struct IDEUnit *unit, UBYTE command, ULONG lba, U *unit->drive->lbaLow = (UBYTE)(lba); *unit->drive->lbaMid = (UBYTE)(lba >> 8); *unit->drive->lbaHigh = (UBYTE)(lba >> 16); + *unit->drive->error_features = features; *unit->drive->status_command = command; return 0; @@ -698,7 +700,7 @@ static BYTE write_taskfile_lba(struct IDEUnit *unit, UBYTE command, ULONG lba, U * @param unit Pointer to an IDEUnit struct * @param lba Pointer to the LBA variable */ -static BYTE write_taskfile_lba48(struct IDEUnit *unit, UBYTE command, ULONG lba, UBYTE sectorCount) { +static BYTE write_taskfile_lba48(struct IDEUnit *unit, UBYTE command, ULONG lba, UBYTE sectorCount, UBYTE features) { if (!ata_wait_ready(unit,ATA_RDY_WAIT_COUNT)) return HFERR_SelTimeout; @@ -711,6 +713,7 @@ static BYTE write_taskfile_lba48(struct IDEUnit *unit, UBYTE command, ULONG lba, *unit->drive->lbaHigh = (UBYTE)(lba >> 16); *unit->drive->lbaMid = (UBYTE)(lba >> 8); *unit->drive->lbaLow = (UBYTE)(lba); + *unit->drive->error_features = features; *unit->drive->status_command = command; return 0; @@ -724,11 +727,94 @@ static BYTE write_taskfile_lba48(struct IDEUnit *unit, UBYTE command, ULONG lba, * @param pio pio mode */ BYTE ata_set_pio(struct IDEUnit *unit, UBYTE pio) { - + BYTE error = 0; + if (pio > 4) return IOERR_BADADDRESS; if (pio > 0) pio |= 0x08; + if ((error = write_taskfile_lba(unit,ATA_CMD_SET_FEATURES,0,pio,0x03)) != 0) + return error; + + if ((error = ata_wait_ready(unit,ATA_RDY_WAIT_COUNT))) + return error; + + if (ata_check_error(unit)) return IOERR_BADLENGTH; + + return 0; +} + +/** + * scsi_ata_passthrough + * + * Handle SCSI ATA PASSTHROUGH (12) command to send ATA commands to the drive + * + * @param unit Pointer to an IDEUnit struct + * @param cmd Pointer to a SCSICmd struct + * @return non-zero on error +*/ +BYTE scsi_ata_passthrough(struct IDEUnit *unit, struct SCSICmd *cmd) { + struct SCSI_CDB_ATA *cdb = (struct SCSI_CDB_ATA *)cmd->scsi_Command; + + bool byt_blok = (cdb->length & ATA_BYT_BLOK) ? true : false; + UBYTE protocol = (cdb->protocol >> 1) & 0x0F; + UBYTE t_length = cdb->length & ATA_TLEN_MASK; + + ULONG lba = (cdb->lbaHigh << 16 | cdb->lbaMid << 8 | cdb->lbaLow); + ULONG count = 0; + BYTE error = 0; + + UWORD *src = NULL; + UWORD *dest = NULL; + + cmd->scsi_CmdActual = cmd->scsi_CmdLength; + + switch (t_length) { + case 0x00: // No Data transferred + break; + case 0x01: // Transfer length in feature field + count = cdb->features; + cdb->features = 0; + break; + case 0x02: // Transfer length in sector_count field + count = cdb->sectorCount; + cdb->sectorCount = 0; + break; + default: + return IOERR_BADLENGTH; + } + + if (byt_blok) count *= unit->blockSize; + + if (count > (512*256)) return IOERR_BADLENGTH; // Can't be bothered supporting larger transfers + + if (count > cmd->scsi_Length) return IOERR_BADLENGTH; + + switch (protocol) { + case ATA_NODATA: + break; + + case ATA_PIO_IN: // Data to Host + if (count < 2) return IOERR_BADLENGTH; + src = (UWORD *)unit->drive->data; + dest = cmd->scsi_Data; + break; + + case ATA_PIO_OUT: // Data to Drive + if (count < 2) return IOERR_BADLENGTH; + src = cmd->scsi_Data; + dest = (UWORD *)unit->drive->data; + break; + + default: + return IOERR_NOCMD; + + } + + if (protocol < ATA_NODATA || protocol > ATA_PIO_OUT) return IOERR_BADADDRESS; + + count += (count & 1); // Ensure byte count is even + UBYTE drvSel = (unit->primary) ? 0xE0 : 0xF0; ata_select(unit,drvSel,true); @@ -738,15 +824,35 @@ BYTE ata_set_pio(struct IDEUnit *unit, UBYTE pio) { return HFERR_SelTimeout; } - *unit->drive->devHead = drvSel; - *unit->drive->sectorCount = pio; - *unit->drive->lbaLow = 0; - *unit->drive->lbaMid = 0; - *unit->drive->lbaHigh = 0; - *unit->drive->error_features = 0x03; // Set Transfer Mode - *unit->drive->status_command = ATA_CMD_SET_FEATURES; + if ((error = write_taskfile_lba(unit,cdb->command,lba,cdb->sectorCount,cdb->features)) != 0) { + ata_save_error(unit); + return error; + } - if (ata_check_error(unit)) return IOERR_BADLENGTH; + if (protocol == ATA_PIO_IN || protocol == ATA_PIO_OUT) { + for (int i = 0; i < count/2; i++) { + if (i % 512 == 0) { + if (!ata_wait_drq(unit,ATA_DRQ_WAIT_COUNT)) { + ata_save_error(unit); + return IOERR_UNITBUSY; + } + } + + dest[i] = src[i]; + } + } + + if (!ata_wait_ready(unit,ATA_RDY_WAIT_COUNT)) { + ata_save_error(unit); + return HFERR_BadStatus; + } + + if (ata_check_error(unit)){ + ata_save_error(unit); + return IOERR_ABORTED; + } + + cmd->scsi_Actual = cmd->scsi_Length; return 0; } \ No newline at end of file diff --git a/ata.h b/ata.h index 341add5..3c5d636 100644 --- a/ata.h +++ b/ata.h @@ -98,6 +98,7 @@ void ata_set_xfer(struct IDEUnit *unit, enum xfer method); BYTE ata_read(void *buffer, ULONG lba, ULONG count, ULONG *actual, struct IDEUnit *unit); BYTE ata_write(void *buffer, ULONG lba, ULONG count, ULONG *actual, struct IDEUnit *unit); BYTE ata_set_pio(struct IDEUnit *unit, UBYTE pio); +BYTE scsi_ata_passthrough( struct IDEUnit *unit, struct SCSICmd *cmd); void ata_read_unaligned_long(void *source, void *destination); void ata_write_unaligned_long(void *source, void *destination); diff --git a/device.h b/device.h index cb67148..2f9890b 100644 --- a/device.h +++ b/device.h @@ -44,7 +44,7 @@ struct IDEUnit { struct ExecBase *SysBase; struct IDETask *itask; volatile struct Drive *drive; - BYTE (*write_taskfile)(struct IDEUnit *, UBYTE, ULONG, UBYTE); + BYTE (*write_taskfile)(struct IDEUnit *, UBYTE, ULONG, UBYTE, UBYTE); enum xfer xferMethod; void (*read_fast)(void *, void *); void (*write_fast)(void *, void *); diff --git a/idetask.c b/idetask.c index 0e576f0..df54a42 100644 --- a/idetask.c +++ b/idetask.c @@ -190,6 +190,10 @@ static BYTE handle_scsi_command(struct IOStdReq *ioreq) { { // Non-ATAPI drives - Translate SCSI CMD to ATA switch (scsi_command->scsi_Command[0]) { + case SCSI_CMD_ATA_PASSTHROUGH: + error = scsi_ata_passthrough(unit,scsi_command); + break; + case SCSI_CMD_TEST_UNIT_READY: scsi_command->scsi_Actual = 0; error = 0; diff --git a/lidetool/config.c b/lidetool/config.c index b7a8858..05057d4 100644 --- a/lidetool/config.c +++ b/lidetool/config.c @@ -34,6 +34,8 @@ struct Config* configure(int argc, char *argv[]) { bool error = false; + bool cmd_selected = false; + struct Config *config; config = (struct Config *)AllocMem(sizeof(struct Config),MEMF_CLEAR); @@ -45,14 +47,21 @@ struct Config* configure(int argc, char *argv[]) { config->Pio = -1; config->Device = "lide.device"; config->DumpInfo = false; + config->DumpIdent = false; for (int i=1; iDumpIdent = true; + cmd_selected = true; + break; + case 'P': if (i+1 < argc) { config->Pio = (*argv[i+1])-'0'; i++; + cmd_selected = true; } break; @@ -60,12 +69,15 @@ struct Config* configure(int argc, char *argv[]) { if (i+1 < argc) { config->Multiple = (*argv[i+1])-'0'; i++; + cmd_selected = true; } break; + case 'm': if (i+1 < argc) { config->Mode = (*argv[i+1])-'0'; i++; + cmd_selected = true; } break; @@ -85,13 +97,14 @@ struct Config* configure(int argc, char *argv[]) { case 'p': config->DumpInfo = true; + cmd_selected = true; break; } } } - if (config->Unit == -1 || (config->Pio == -1 && config->Mode == -1 && config->DumpInfo == false && config->Multiple < 0)) { + if (config->Unit == -1 || cmd_selected == false) { error = true; } @@ -107,5 +120,5 @@ struct Config* configure(int argc, char *argv[]) { * @brief Print the usage information */ void usage() { - printf("\nUsage: lidetool -u -m [-d ] [-P ] [-p]\n\n"); + printf("\nUsage: lidetool -u -m [-d ] [-P ] [-p] [-I]\n\n"); } diff --git a/lidetool/config.h b/lidetool/config.h index 842d44b..047381c 100644 --- a/lidetool/config.h +++ b/lidetool/config.h @@ -22,9 +22,10 @@ struct Config { int Unit; int Mode; int Pio; + int Multiple; char *Device; bool DumpInfo; - int Multiple; + bool DumpIdent; }; struct Config* configure(int, char* []); diff --git a/lidetool/main.c b/lidetool/main.c index bbd25a4..a271f74 100644 --- a/lidetool/main.c +++ b/lidetool/main.c @@ -223,6 +223,66 @@ BYTE setPio(struct IOStdReq *req,int pio) { return error; } +/** + * ident + * + * Send an IDENTIFY DEVICE command to the device + * + * @param req An opened IOStdReq + * + * @return non-zero on error + */ +BYTE identify(struct IOStdReq *req) { + BYTE error = 0; + + struct SCSICmd *cmd = MakeSCSICmd(SZ_CDB_12); + UWORD *buf = AllocMem(512,MEMF_ANY|MEMF_CLEAR); + + if (cmd) { + if (buf) { + cmd->scsi_Actual = 0; + cmd->scsi_CmdActual = 0; + cmd->scsi_Flags = SCSIF_READ; + cmd->scsi_Data = buf; + cmd->scsi_Length = 512; + cmd->scsi_Command[0] = SCSI_CMD_ATA_PASSTHROUGH; + cmd->scsi_Command[1] = (4<<1); // PIO IN + cmd->scsi_Command[2] = 4 | 2; //BYT_BLOK | T_LEN etc + cmd->scsi_Command[4] = 1; // 1 sector (512 bytes) + cmd->scsi_Command[9] = 0xEC; // Identify; + + req->io_Offset = 0; + req->io_Actual = 0; + req->io_Length = 12; + req->io_Command = HD_SCSICMD; + req->io_Data = cmd; + + error = DoIO((struct IORequest *)req); + + if (error == 0 && cmd->scsi_Status == 0) { + printf("IDENTIFY DEVICE:"); + for (int i=0; i<256; i++) { + if (i % 8 == 0) printf("\n%04x: ",i << 1); + printf("%04x%s",__bswap16(buf[i]), ((i + 1) % 8) == 0 ? "" : "." ); + } + printf("\n"); + } + + if (error) + printf("IO Error %d\n", error); + + if (cmd->scsi_Status) + printf("SCSI Status: %d\n",cmd->scsi_Status); + + FreeMem(buf,512); + } + DeleteSCSICmd(cmd); + } + + + return error; +} + /** * setMultiple @@ -275,6 +335,11 @@ int main(int argc, char *argv[]) if (config->Pio >= 0) { setPio(req,config->Pio); } + + if (config->DumpIdent) { + identify(req); + } + CloseDevice((struct IORequest *)req); } else { printf("Error %d opening %s", error, config->Device); diff --git a/lidetool/main.h b/lidetool/main.h index 8ac8dc8..6eb1fba 100644 --- a/lidetool/main.h +++ b/lidetool/main.h @@ -43,7 +43,9 @@ struct __attribute__((packed)) SCSI_CDB_10 { }; #define SZ_CDB_10 10 +#define SZ_CDB_12 12 #define SCSI_CMD_INQUIRY 0x12 +#define SCSI_CMD_ATA_PASSTHROUGH 0xA1 #define CMD_XFER 0x1001 #define CMD_PIO (CMD_XFER + 1) diff --git a/scsi.h b/scsi.h index 0cef267..3d305d1 100644 --- a/scsi.h +++ b/scsi.h @@ -21,7 +21,7 @@ #define SCSI_CMD_MODE_SELECT_10 0x55 #define SCSI_CMD_MODE_SENSE_10 0x5A #define SCSI_CMD_START_STOP_UNIT 0x1B - +#define SCSI_CMD_ATA_PASSTHROUGH 0xA1 #define SCSI_CHECK_CONDITION 0x02 #define SZ_CDB_10 10 @@ -113,6 +113,28 @@ struct __attribute__((packed)) SCSI_TRACK_MSF { UBYTE frame; }; +struct __attribute__((packed)) SCSI_CDB_ATA { + UBYTE operation; + UBYTE protocol; // Bits 7-5: MULTIPLE_COUNT, Bits 4-1: Protocol + UBYTE length; + UBYTE features; + UBYTE sectorCount; + UBYTE lbaLow; + UBYTE lbaMid; + UBYTE lbaHigh; + UBYTE devHead; + UBYTE command; + UBYTE reserved; + UBYTE control; +}; + +#define ATA_NODATA 3 +#define ATA_PIO_IN 4 +#define ATA_PIO_OUT 5 + +#define ATA_TLEN_MASK 3 +#define ATA_BYT_BLOK (1<<2) + void scsi_sense(struct SCSICmd* command, ULONG info, ULONG specific, BYTE error); struct SCSICmd * MakeSCSICmd(ULONG cdbSize); void DeleteSCSICmd(struct SCSICmd *cmd);