459 lines
12 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/* This file is part of lidetool
* Copyright (C) 2023 Matthew Harlum <matt@harlum.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <exec/execbase.h>
#include <exec/ports.h>
#include <proto/exec.h>
#include <proto/expansion.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include "../device.h"
#include <devices/scsidisk.h>
#include <devices/trackdisk.h>
#include "main.h"
#include "config.h"
#include "../device.h"
#define CMD_XFER 0x1001
#define BIT(x) (1U << (x))
const char ver[] = VERSION_STRING;
struct ExecBase *SysBase;
struct Config *config;
/**
* MakeSCSICmd
*
* Creates an new SCSICmd struct and CDB
*
* @param cdbSize Size of CDB to create
* @returns Pointer to an initialized SCSICmd struct
*/
struct SCSICmd * MakeSCSICmd(ULONG cdbSize) {
UBYTE *cdb = NULL;
struct SCSICmd *cmd = NULL;
if ((cdb = AllocMem(cdbSize,MEMF_ANY|MEMF_CLEAR)) == NULL) {
return NULL;
}
if ((cmd = AllocMem(sizeof(struct SCSICmd),MEMF_ANY|MEMF_CLEAR)) == NULL) {
FreeMem(cdb,cdbSize);
return NULL;
}
cmd->scsi_Command = (UBYTE *)cdb;
cmd->scsi_CmdLength = cdbSize;
cmd->scsi_Data = NULL;
cmd->scsi_Length = 0;
cmd->scsi_SenseData = NULL;
return cmd;
}
/**
* DeleteSCSICmd
*
* Delete a SCSICmd and its CDB
*
* @param cmd Pointer to a SCSICmd struct to be deleted
*/
void DeleteSCSICmd(struct SCSICmd *cmd) {
if (cmd) {
UBYTE *cdb = cmd->scsi_Command;
if (cdb) FreeMem(cdb,(ULONG)cmd->scsi_CmdLength);
FreeMem(cmd,sizeof(struct SCSICmd));
}
}
/**
* doScsiInquiry
*
* Send an INQUIRY command to the device and print the details
*
* @param req An open IOStdReq
*/
static void doScsiInquiry(struct IOStdReq *req) {
BYTE error = 0;
struct SCSICmd *cmd;
struct SCSI_Inquiry *data;
if ((cmd = MakeSCSICmd(SZ_CDB_10)) != NULL) {
if ((data = AllocMem(100,MEMF_ANY|MEMF_CLEAR)) != NULL) {
char vendor[9];
char product[17];
char revision[5];
char serial[9];
cmd->scsi_Command[0] = SCSI_CMD_INQUIRY;
cmd->scsi_Command[2] = 0;
cmd->scsi_Command[4] = sizeof(struct SCSI_Inquiry);
cmd->scsi_Data = (UWORD *)data;
cmd->scsi_Length = 100;
cmd->scsi_Flags = SCSIF_READ;
req->io_Actual = 0;
req->io_Length = 100;
req->io_Command = HD_SCSICMD;
req->io_Data = cmd;
req->io_Offset = 0;
error = DoIO((struct IORequest *)req);
if (error == 0 && cmd->scsi_Status == 0) {
memset(vendor,0,9);
memset(product,0,17);
memset(revision,0,5);
memset(serial,0,9);
strncpy(vendor, data->vendor, 8);
strncpy(product, data->product, 16);
strncpy(revision, data->revision, 4);
strncpy(serial, data->serial, 8);
printf("Vendor: %s\n",vendor);
printf("Product: %s\n", product);
printf("Revision: %s\n", revision);
printf("Serial: %s\n", serial);
} else {
if (error)
printf("IO Error %d\n", error);
if (cmd->scsi_Status)
printf("SCSI Status: %d\n",cmd->scsi_Status);
}
FreeMem(data,100);
} else {
printf("Failed to allocate memory.\n");
}
DeleteSCSICmd(cmd);
} else {
printf("Failed to allocate memory.\n");
}
}
/**
* DumpUnit
*
* Print various details of the Unit
*
* @param req An open IOStdReq
*/
static void DumpUnit(struct IOStdReq *req) {
struct IDEUnit *unit = (struct IDEUnit *)req->io_Unit;
printf("Device Type: %d\n", unit->deviceType);
printf("Transfer method: %d\n", unit->xferMethod);
printf("Primary: %s\n", (unit->flags.primary) ? "Yes" : "No");
printf("ATAPI: %s\n", (unit->flags.atapi) ? "Yes" : "No");
printf("Medium Present: %s\n", (unit->flags.mediumPresent) ? "Yes" : "No");
printf("Supports LBA: %s\n", (unit->flags.lba) ? "Yes" : "No");
printf("Supports LBA48: %s\n", (unit->flags.lba48) ? "Yes" : "No");
printf("C/H/S: %d/%d/%d\n", unit->cylinders, unit->heads, unit->sectorsPerTrack);
printf("Logical Sectors: %ld\n", (long int)unit->logicalSectors);
printf("READ/WRITE Multiple: %s\n", (unit->flags.xferMultiple) ? "Yes" : "No");
printf("Multiple count: %d\n", unit->multipleCount);
printf("Last Error: ");
for (int i=0; i<6; i++) {
printf("%02x ",unit->last_error[i]);
}
printf("\n");
}
/**
* setTransferMode
*
* Set the requested transfer method on the unit
*
* @param req An open IOStdReq
*/
BYTE setTransferMode(struct IOStdReq *req) {
BYTE error = 0;
req->io_Data = NULL;
req->io_Offset = 0;
req->io_Length = config->Mode;
req->io_Command = CMD_XFER;
error = DoIO((struct IORequest *)req);
if (error == 0) {
printf("Transfer mode configured for unit %d\n",config->Unit);
} else {
printf("IO Error %d\n", error);
}
return error;
}
/**
* setPio
*
* Set the requested pio mode on the unit
*
* @param req An open IOStdReq
*/
BYTE setPio(struct IOStdReq *req,int pio) {
BYTE error = 0;
req->io_Data = NULL;
req->io_Offset = 0;
req->io_Length = pio;
req->io_Command = CMD_PIO;
error = DoIO((struct IORequest *)req);
if (error == 0) {
printf("PIO mode configured for unit %d\n",config->Unit);
} else {
printf("IO Error %d\n", error);
}
return error;
}
static char *trim(char *str, int bytes)
{
char *eptr = &str[bytes - 1];
while (eptr > str) {
if (*eptr == ' ')
*(eptr--) = '\0';
else
break;
}
while (*str == ' ')
str++;
return (str);
}
/**
* identify_decode
*
* Interpret the result data of an IDENTIFY DEVICE command.
*
* @param buf the 512-byte buffer
*/
static void identify_decode(UWORD *buf) {
int bit;
unsigned int atastd;
for (int i=0; i<256; i++)
buf[i] = __bswap16(buf[i]);
printf("ATA%s", (buf[0] & BIT(15)) ? "PI" : "");
printf(" Model=%.40s, FwRev=%.8s, SerialNo=%.20s\n",
trim((char *)&buf[27], 40),
trim((char *)&buf[23], 8),
trim((char *)&buf[10], 20));
printf("Raw C/H/S: %u/%u/%u\n", buf[1], buf[3], buf[6]);
printf("Cur C/H/S: %u/%u/%u\n", buf[54], buf[55], buf[56]);
printf("LBA Capacity: %u sec\n", buf[60] | (buf[61] << 16));
printf("Cur Capacity: %u sec\n", buf[57] | (buf[58] << 16));
printf("MaxMultSect: %u [%s]\n", buf[59] & 0xff,
(buf[59] & BIT(8)) ? "enabled" : "disabled");
printf("PIO modes: ");
for (bit = 0; bit < 8; bit++)
if (buf[64] & BIT(bit))
printf(" pio%u", bit);
if ((buf[64] & 0xff) == 0)
printf("<none>");
printf("\nDMA modes: ");
for (bit = 0; bit < 8; bit++)
if (buf[63] & BIT(bit)) {
printf(" %smdma%u%s",
(buf[63] & BIT(8 + bit)) ? "[" : "",
bit,
(buf[63] & BIT(8 + bit)) ? "]" : "");
}
if ((buf[63] & 0xff) == 0)
printf("<none>");
printf("\nUDMA modes: ");
for (bit = 0; bit < 8; bit++)
if (buf[88] & BIT(bit)) {
printf(" %sudma%u%s",
(buf[88] & BIT(8 + bit)) ? "[" : "",
bit,
(buf[88] & BIT(8 + bit)) ? "]" : "");
}
if ((buf[63] & 0xff) == 0)
printf("<none>");
printf("\nTiming: tPIO=[min:%u,w/IORDY:%u}, tDMA={min:%u,rec:%u}\n",
buf[67], buf[68], buf[65], buf[66]);
atastd = buf[80];
if ((atastd != 0x0000) && (atastd != 0xffff)) {
printf("Standards: ");
for (bit = 2; bit < 7; bit++)
if (atastd & BIT(bit))
printf(" ATA%u", bit);
printf("\n");
}
printf("IORDY: %s\n",
(buf[49] & BIT(11)) ? (buf[49] & BIT(10) ? "on/off" : "on") : "?");
}
/**
* 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);
identify_decode(buf);
FreeMem(buf,512);
}
DeleteSCSICmd(cmd);
}
return error;
}
/**
* setMultiple
*
* @param req An open IOStdReq
* @param multiple Multiple count
*
*/
static void setMultiple(struct IOStdReq *req, int multiple) {
struct IDEUnit *unit = (struct IDEUnit *)req->io_Unit;
if (multiple > 0) {
unit->flags.xferMultiple = true;
unit->multipleCount = multiple;
printf("set multiple transfer: %d\n",multiple);
} else {
printf("Transfer multiple disabled.\n");
unit->flags.xferMultiple = false;
unit->multipleCount = 1;
}
}
int main(int argc, char *argv[])
{
SysBase = *((struct ExecBase **)4UL);
int rc = 0;
int error = 0;
if ((config = configure(argc,argv)) != NULL) {
struct MsgPort *mp = NULL;
struct IOStdReq *req = NULL;
if (mp = CreateMsgPort()) {
if (req = CreateIORequest(mp,sizeof(struct IOStdReq))) {
if ((error = OpenDevice(config->Device,config->Unit,(struct IORequest *)req,0)) == 0) {
if (config->Mode != -1) {
setTransferMode(req);
}
if (config->DumpInfo) {
UWORD driverVersion = req->io_Device->dd_Library.lib_Version;
UWORD driverRevision = req->io_Device->dd_Library.lib_Revision;
if (driverVersion != DEVICE_VERSION ||
driverRevision != DEVICE_REVISION) {
printf("Driver version mismatch.\n");
printf("lidetool version: %d.%d lide.device %d.%d\n", DEVICE_VERSION, DEVICE_REVISION, driverVersion, driverRevision);
} else {
doScsiInquiry(req);
DumpUnit(req);
}
}
if (config->Multiple >= 0) {
setMultiple(req,config->Multiple);
}
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);
}
DeleteIORequest(req);
} else {
printf("Failed to create IO Req.\n");
}
DeleteMsgPort(mp);
} else {
printf("Failed to create Message Port.\n");
}
} else {
usage();
}
if (config) FreeMem(config,sizeof(struct Config));
if (ExpansionBase) CloseLibrary((struct Library *)ExpansionBase);
return (rc);
}