diff --git a/DiscImageChef.Device.Report/CMakeLists.txt b/DiscImageChef.Device.Report/CMakeLists.txt new file mode 100644 index 00000000..4f1177ab --- /dev/null +++ b/DiscImageChef.Device.Report/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.9) +project(DiscImageChef_Device_Report C) + +set(CMAKE_C_STANDARD 90) + +add_executable(DiscImageChef_Device_Report main.c scsi.c scsi.h main.h ata.h ata.c atapi.c atapi.h) \ No newline at end of file diff --git a/DiscImageChef.Device.Report/ata.c b/DiscImageChef.Device.Report/ata.c new file mode 100644 index 00000000..eaea50e0 --- /dev/null +++ b/DiscImageChef.Device.Report/ata.c @@ -0,0 +1,236 @@ +// +// Created by claunia on 11/12/17. +// + +#include +#include +#include +#include "ata.h" +#include "scsi.h" + +int AtaProtocolToScsiDirection(int protocol) +{ + switch(protocol) + { + case ATA_PROTOCOL_DEVICE_DIAGNOSTICS: + case ATA_PROTOCOL_DEVICE_RESET: + case ATA_PROTOCOL_HARD_RESET: + case ATA_PROTOCOL_NO_DATA: + case ATA_PROTOCOL_SOFT_RESET: + case ATA_PROTOCOL_RETURN_RESPONSE: + return SG_DXFER_NONE; + case ATA_PROTOCOL_PIO_IN: + case ATA_PROTOCOL_UDMA_IN: + return SG_DXFER_FROM_DEV; + case ATA_PROTOCOL_PIO_OUT: + case ATA_PROTOCOL_UDMA_OUT: + return SG_DXFER_TO_DEV; + default: + return SG_DXFER_TO_FROM_DEV; + } +} + +unsigned char *AtaToCString(unsigned char* string, int len) +{ + unsigned char* buffer = malloc(len + 1); + unsigned char* ptr = buffer; + int i; + + for(i = 0; i < len; i+=2) + { + *ptr++ = *(string + i + 1); + *ptr++ = *(string + i); + } + + buffer[len] = 0x00; + *ptr = *(buffer + len); + + for(i = len; i >= 0; i--, *ptr--) + { + if(*ptr == 0x20 || *ptr == 0x00) + *ptr = 0; + else + break; + } + + return buffer; +} + +int SendAtaCommandChs(int fd, AtaRegistersCHS registers, AtaErrorRegistersCHS **errorRegisters, int protocol, int transferRegister, unsigned char *buffer, unsigned int buffer_len, int transferBlocks) +{ + unsigned char cdb[16]; + memset(&cdb, 0, 16); + cdb[0] = SCSI_ATA_PASSTHROUGH_16; + cdb[1] = (unsigned char)((protocol << 1) & 0x1E); + if(transferRegister != ATA_TRANSFER_NONE && protocol != ATA_PROTOCOL_NO_DATA) + { + switch(protocol) + { + case ATA_PROTOCOL_PIO_IN: + case ATA_PROTOCOL_UDMA_IN: + cdb[2] = 0x08; + break; + default: + cdb[2] = 0x00; + break; + } + + if(transferBlocks) + cdb[2] |= 0x04; + + cdb[2] |= (transferRegister & 0x03); + } + + cdb[4] = registers.feature; + cdb[6] = registers.sectorCount; + cdb[8] = registers.sector; + cdb[10] = registers.cylinderLow; + cdb[12] = registers.cylinderHigh; + cdb[13] = registers.deviceHead; + cdb[14] = registers.command; + + unsigned char *sense_buf; + int error = SendScsiCommand(fd, &cdb, 16, buffer, buffer_len, &sense_buf, AtaProtocolToScsiDirection(protocol)); + + *errorRegisters = malloc(sizeof(AtaErrorRegistersCHS)); + memset(*errorRegisters, 0, sizeof(AtaErrorRegistersCHS)); + (*errorRegisters)->error = sense_buf[11]; + (*errorRegisters)->sectorCount = sense_buf[13]; + (*errorRegisters)->sector = sense_buf[15]; + (*errorRegisters)->cylinderLow = sense_buf[17]; + (*errorRegisters)->cylinderHigh = sense_buf[19]; + (*errorRegisters)->deviceHead = sense_buf[20]; + (*errorRegisters)->status = sense_buf[21]; + + if(error != 0) + return error; + + return (*errorRegisters)->error; +} + +int SendAtaCommandLba28(int fd, AtaRegistersLBA28 registers, AtaErrorRegistersLBA28 **errorRegisters, int protocol, int transferRegister, unsigned char *buffer, unsigned int buffer_len, int transferBlocks) +{ + unsigned char cdb[16]; + memset(&cdb, 0, 16); + cdb[0] = SCSI_ATA_PASSTHROUGH_16; + cdb[1] = (unsigned char)((protocol << 1) & 0x1E); + if(transferRegister != ATA_TRANSFER_NONE && protocol != ATA_PROTOCOL_NO_DATA) + { + switch(protocol) + { + case ATA_PROTOCOL_PIO_IN: + case ATA_PROTOCOL_UDMA_IN: + cdb[2] = 0x08; + break; + default: + cdb[2] = 0x00; + break; + } + + if(transferBlocks) + cdb[2] |= 0x04; + + cdb[2] |= (transferRegister & 0x03); + } + + cdb[2] |= 0x20; + + cdb[4] = registers.feature; + cdb[6] = registers.sectorCount; + cdb[8] = registers.lbaLow; + cdb[10] = registers.lbaMid; + cdb[12] = registers.lbaHigh; + cdb[13] = registers.deviceHead; + cdb[14] = registers.command; + + unsigned char *sense_buf; + int error = SendScsiCommand(fd, &cdb, 16, buffer, buffer_len, &sense_buf, AtaProtocolToScsiDirection(protocol)); + + *errorRegisters = malloc(sizeof(AtaErrorRegistersLBA28)); + memset(*errorRegisters, 0, sizeof(AtaErrorRegistersLBA28)); + (*errorRegisters)->error = sense_buf[11]; + (*errorRegisters)->sectorCount = sense_buf[13]; + (*errorRegisters)->lbaLow = sense_buf[15]; + (*errorRegisters)->lbaMid= sense_buf[17]; + (*errorRegisters)->lbaHigh = sense_buf[19]; + (*errorRegisters)->deviceHead = sense_buf[20]; + (*errorRegisters)->status = sense_buf[21]; + + if(error != 0) + return error; + + return (*errorRegisters)->error; +} + +int SendAtaCommandLba48(int fd, AtaRegistersLBA48 registers, AtaErrorRegistersLBA48 **errorRegisters, int protocol, int transferRegister, unsigned char *buffer, unsigned int buffer_len, int transferBlocks) +{ + unsigned char cdb[16]; + memset(&cdb, 0, 16); + cdb[0] = SCSI_ATA_PASSTHROUGH_16; + cdb[1] = (unsigned char)((protocol << 1) & 0x1E); + cdb[1] |= 0x01; + if(transferRegister != ATA_TRANSFER_NONE && protocol != ATA_PROTOCOL_NO_DATA) + { + switch(protocol) + { + case ATA_PROTOCOL_PIO_IN: + case ATA_PROTOCOL_UDMA_IN: + cdb[2] = 0x08; + break; + default: + cdb[2] = 0x00; + break; + } + + if(transferBlocks) + cdb[2] |= 0x04; + + cdb[2] |= (transferRegister & 0x03); + } + + cdb[2] |= 0x20; + + cdb[3] = (uint8_t)((registers.feature & 0xFF00) >> 8); + cdb[4] = (uint8_t)(registers.feature & 0xFF); + cdb[5] = (uint8_t)((registers.sectorCount & 0xFF00) >> 8); + cdb[6] = (uint8_t)(registers.sectorCount & 0xFF); + cdb[7] = (uint8_t)((registers.lbaLow & 0xFF00) >> 8); + cdb[8] = (uint8_t)(registers.lbaLow & 0xFF); + cdb[9] = (uint8_t)((registers.lbaMid & 0xFF00) >> 8); + cdb[10] = (uint8_t)(registers.lbaMid & 0xFF); + cdb[11] = (uint8_t)((registers.lbaHigh & 0xFF00) >> 8); + cdb[12] = (uint8_t)(registers.lbaHigh & 0xFF); + cdb[13] = registers.deviceHead; + cdb[14] = registers.command; + + unsigned char *sense_buf; + int error = SendScsiCommand(fd, &cdb, 16, buffer, buffer_len, &sense_buf, AtaProtocolToScsiDirection(protocol)); + + *errorRegisters = malloc(sizeof(AtaErrorRegistersLBA48)); + memset(*errorRegisters, 0, sizeof(AtaErrorRegistersLBA48)); + (*errorRegisters)->sectorCount = (uint16_t)((sense_buf[12] << 8) + sense_buf[13]); + (*errorRegisters)->lbaLow = (uint16_t)((sense_buf[14] << 8) + sense_buf[15]); + (*errorRegisters)->lbaMid = (uint16_t)((sense_buf[16] << 8) + sense_buf[17]); + (*errorRegisters)->lbaHigh = (uint16_t)((sense_buf[18] << 8) + sense_buf[19]); + (*errorRegisters)->deviceHead = sense_buf[20]; + (*errorRegisters)->status = sense_buf[21]; + + if(error != 0) + return error; + + return (*errorRegisters)->error; +} + +int Identify(int fd, unsigned char **buffer, AtaErrorRegistersCHS **errorRegisters) +{ + *buffer = malloc(512); + memset(*buffer, 0, 512); + AtaRegistersCHS registers; + memset(®isters, 0, sizeof(AtaRegistersCHS)); + + registers.command = ATA_IDENTIFY_DEVICE; + + int error = SendAtaCommandChs(fd, registers, errorRegisters, ATA_PROTOCOL_PIO_IN, ATA_TRANSFER_NONE, *buffer, 512, 0); + + return error; +} \ No newline at end of file diff --git a/DiscImageChef.Device.Report/ata.h b/DiscImageChef.Device.Report/ata.h new file mode 100644 index 00000000..7c6f9b00 --- /dev/null +++ b/DiscImageChef.Device.Report/ata.h @@ -0,0 +1,114 @@ +// +// Created by claunia on 11/12/17. +// + +#ifndef DISCIMAGECHEF_DEVICE_REPORT_ATA_H +#define DISCIMAGECHEF_DEVICE_REPORT_ATA_H + +#include + +typedef struct +{ + uint8_t feature; + uint8_t sectorCount; + uint8_t sector; + uint8_t cylinderLow; + uint8_t cylinderHigh; + uint8_t deviceHead; + uint8_t command; +} AtaRegistersCHS; + +typedef struct +{ + uint8_t feature; + uint8_t sectorCount; + uint8_t lbaLow; + uint8_t lbaMid; + uint8_t lbaHigh; + uint8_t deviceHead; + uint8_t command; +}AtaRegistersLBA28; + +typedef struct +{ + uint16_t feature; + uint16_t sectorCount; + uint16_t lbaLow; + uint16_t lbaMid; + uint16_t lbaHigh; + uint8_t deviceHead; + uint8_t command; +} AtaRegistersLBA48; + +typedef struct +{ + uint8_t status; + uint8_t error; + uint8_t sectorCount; + uint8_t sector; + uint8_t cylinderLow; + uint8_t cylinderHigh; + uint8_t deviceHead; + uint8_t command; +}AtaErrorRegistersCHS; + +typedef struct +{ + uint8_t status; + uint8_t error; + uint8_t sectorCount; + uint8_t lbaLow; + uint8_t lbaMid; + uint8_t lbaHigh; + uint8_t deviceHead; + uint8_t command; +} AtaErrorRegistersLBA28; + +typedef struct +{ + uint8_t status; + uint8_t error; + uint16_t sectorCount; + uint16_t lbaLow; + uint16_t lbaMid; + uint16_t lbaHigh; + uint8_t deviceHead; + uint8_t command; +} AtaErrorRegistersLBA48; + +typedef enum +{ + ATA_TRANSFER_NONE = 0, + ATA_TRANSFER_FEATURE, + ATA_TRANSFER_SECTORCOUNT, + ATA_TRANSFTER_SPTSIU +} AtaTransferRegister; + +typedef enum { + ATA_PROTOCOL_HARD_RESET = 0, + ATA_PROTOCOL_SOFT_RESET = 1, + ATA_PROTOCOL_NO_DATA = 3, + ATA_PROTOCOL_PIO_IN = 4, + ATA_PROTOCOL_PIO_OUT = 5, + ATA_PROTOCOL_DMA = 6, + ATA_PROTOCOL_DMA_QUEUED = 7, + ATA_PROTOCOL_DEVICE_DIAGNOSTICS = 8, + ATA_PROTOCOL_DEVICE_RESET = 9, + ATA_PROTOCOL_UDMA_IN = 10, + ATA_PROTOCOL_UDMA_OUT = 11, + ATA_PROTOCOL_FPDMA = 12, + ATA_PROTOCOL_RETURN_RESPONSE = 15 +} AtaProtocol; + +typedef enum +{ + ATA_IDENTIFY_PACKET_DEVICE = 0xA1, + ATA_IDENTIFY_DEVICE = 0xEC +} AtaCommands; + +unsigned char *AtaToCString(unsigned char* string, int len); +int SendAtaCommandChs(int fd, AtaRegistersCHS registers, AtaErrorRegistersCHS **errorRegisters, int protocol, int transferRegister, unsigned char *buffer, unsigned int buffer_len, int transferBlocks); +int SendAtaCommandLba28(int fd, AtaRegistersLBA28 registers, AtaErrorRegistersLBA28 **errorRegisters, int protocol, int transferRegister, unsigned char *buffer, unsigned int buffer_len, int transferBlocks); +int SendAtaCommandLba48(int fd, AtaRegistersLBA48 registers, AtaErrorRegistersLBA48 **errorRegisters, int protocol, int transferRegister, unsigned char *buffer, unsigned int buffer_len, int transferBlocks); +int Identify(int fd, unsigned char **buffer, AtaErrorRegistersCHS **errorRegisters); +#endif //DISCIMAGECHEF_DEVICE_REPORT_ATA_H diff --git a/DiscImageChef.Device.Report/atapi.c b/DiscImageChef.Device.Report/atapi.c new file mode 100644 index 00000000..09f7f65f --- /dev/null +++ b/DiscImageChef.Device.Report/atapi.c @@ -0,0 +1,22 @@ +// +// Created by claunia on 12/12/17. +// + +#include +#include +#include "ata.h" +#include "atapi.h" + +int IdentifyPacket(int fd, unsigned char **buffer, AtaErrorRegistersCHS **errorRegisters) +{ + *buffer = malloc(512); + memset(*buffer, 0, 512); + AtaRegistersCHS registers; + memset(®isters, 0, sizeof(AtaRegistersCHS)); + + registers.command = ATA_IDENTIFY_PACKET_DEVICE; + + int error = SendAtaCommandChs(fd, registers, errorRegisters, ATA_PROTOCOL_PIO_IN, ATA_TRANSFER_NONE, *buffer, 512, 0); + + return error; +} \ No newline at end of file diff --git a/DiscImageChef.Device.Report/atapi.h b/DiscImageChef.Device.Report/atapi.h new file mode 100644 index 00000000..d0b1ccdc --- /dev/null +++ b/DiscImageChef.Device.Report/atapi.h @@ -0,0 +1,8 @@ +// +// Created by claunia on 12/12/17. +// + +#ifndef DISCIMAGECHEF_DEVICE_REPORT_ATAPI_H +#define DISCIMAGECHEF_DEVICE_REPORT_ATAPI_H +int IdentifyPacket(int fd, unsigned char **buffer, AtaErrorRegistersCHS **errorRegisters); +#endif //DISCIMAGECHEF_DEVICE_REPORT_ATAPI_H diff --git a/DiscImageChef.Device.Report/main.c b/DiscImageChef.Device.Report/main.c new file mode 100644 index 00000000..fd975caa --- /dev/null +++ b/DiscImageChef.Device.Report/main.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include "ata.h" +#include "main.h" +#include "atapi.h" + +#define DIC_VERSION "3.99.6.0" +#define DIC_COPYRIGHT "Copyright © 2011-2017 Natalia Portillo" + +int main(int argc, void *argv[]) +{ + int fd; + unsigned char *scsi_sense = NULL; + unsigned char *scsi_inq_data = NULL; + unsigned char *ata_ident = NULL; + unsigned char *atapi_ident = NULL; + AtaErrorRegistersCHS *ata_error_chs; + int scsi_error, ata_error; + unsigned char* manufacturer; + unsigned char* product; + unsigned char* revision; + int deviceType = DEVICE_TYPE_UNKNOWN; + + printf("The Disc Image Chef Device Reporter for Linux %s\n", DIC_VERSION); + printf("%s\n", DIC_COPYRIGHT); + + if(argc != 2) + { + printf("Usage:\n"); + printf("%s \n", argv[0]); + return 1; + } + + fd = open(argv[1], O_RDONLY | O_NONBLOCK); + + if(fd < 0) + { + printf("Error opening device: %s\n", strerror(errno)); + return 2; + } + + // TODO: Support MMC, USB, FireWire, PCMCIA + + scsi_error = Inquiry(fd, &scsi_inq_data, &scsi_sense); + + if(scsi_error) + scsi_inq_data = NULL; + + if(scsi_inq_data != NULL) + { + manufacturer = malloc(9); + manufacturer[8] = 0; + product = malloc(17); + product[16] = 0; + revision = malloc(5); + revision[4] = 0; + + strncpy(manufacturer, scsi_inq_data + 8, 8); + strncpy(product, scsi_inq_data + 16, 16); + strncpy(revision, scsi_inq_data + 32, 4); + + deviceType = DEVICE_TYPE_SCSI; + + ata_error = IdentifyPacket(fd, &atapi_ident, &ata_error_chs); + + if(!ata_error) + deviceType = DEVICE_TYPE_ATAPI; + } + + if(scsi_inq_data == NULL || strcmp(manufacturer,"ATA")) + { + ata_error = Identify(fd, &ata_ident, &ata_error_chs); + + if(!ata_error) + { + deviceType = DEVICE_TYPE_ATA; + revision = AtaToCString(ata_ident + (23*2), 8); + product = AtaToCString(ata_ident + (27*2), 40); + } + } + + printf("Device type: %s\n", DeviceType[deviceType]); + printf("Manufacturer: %s\n", manufacturer); + printf("Product: %s\n", product); + printf("Revision: %s\n", revision); + + close(fd); + + return 0; +} \ No newline at end of file diff --git a/DiscImageChef.Device.Report/main.h b/DiscImageChef.Device.Report/main.h new file mode 100644 index 00000000..37c3bfb2 --- /dev/null +++ b/DiscImageChef.Device.Report/main.h @@ -0,0 +1,21 @@ +// +// Created by claunia on 11/12/17. +// + +#ifndef DISCIMAGECHEF_DEVICE_REPORT_MAIN_H +#define DISCIMAGECHEF_DEVICE_REPORT_MAIN_H +typedef enum +{ + DEVICE_TYPE_UNKNOWN, + DEVICE_TYPE_SCSI, + DEVICE_TYPE_ATA, + DEVICE_TYPE_ATAPI, + DEVICE_TYPE_USB, + DEVICE_TYPE_FIREWIRE, + DEVICE_TYPE_PCMCIA, + DEVICE_TYPE_MMC, + DEVICE_TYPE_SD +} DeviceTypes; + +const char* DeviceType[] = { "Unknown", "SCSI", "ATA", "ATAPI", "USB", "FireWire", "PCMCIA", "MultiMediaCard", "SecureDigital" }; +#endif //DISCIMAGECHEF_DEVICE_REPORT_MAIN_H diff --git a/DiscImageChef.Device.Report/scsi.c b/DiscImageChef.Device.Report/scsi.c new file mode 100644 index 00000000..04c7d31d --- /dev/null +++ b/DiscImageChef.Device.Report/scsi.c @@ -0,0 +1,66 @@ +// +// Created by claunia on 11/12/17. +// + +#include +#include +#include +#include +#include +#include +#include "scsi.h" + +int SendScsiCommand(int fd, void *cdb, unsigned char cdb_len, unsigned char *buffer, unsigned int buffer_len, unsigned char **senseBuffer, int direction) +{ + if(buffer == NULL || cdb == NULL) + return -1; + + *senseBuffer = malloc(32); + memset(*senseBuffer, 0, 32); + + sg_io_hdr_t io_hdr; + memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); + + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = cdb_len; + io_hdr.mx_sb_len = 32; + io_hdr.dxfer_direction = direction; + io_hdr.dxfer_len = buffer_len; + io_hdr.dxferp = buffer; + io_hdr.cmdp = cdb; + io_hdr.sbp = *senseBuffer; + io_hdr.timeout = 10000; + + int error = ioctl(fd, SG_IO, &io_hdr); + + if(error < 0) + error = errno; + else + free(*senseBuffer); + + return error; +} + +int Inquiry(int fd, unsigned char **buffer, unsigned char **senseBuffer) +{ + unsigned char cmd_len = 6; + *buffer = malloc(36); + memset(*buffer, 0, 36); + char cdb[] = {SCSI_INQUIRY, 0, 0, 0, 36, 0}; + + int error = SendScsiCommand(fd, &cdb, cmd_len, *buffer, 36, senseBuffer, SG_DXFER_FROM_DEV); + + if(error) + return error; + + unsigned char pagesLength = *(*buffer + 4) + 5; + + free(*buffer); + *buffer = malloc(pagesLength); + memset(*buffer, 0, pagesLength); + + cdb[4] = pagesLength; + error = SendScsiCommand(fd, &cdb, cmd_len, *buffer, pagesLength, senseBuffer, SG_DXFER_FROM_DEV); + + return error; +} \ No newline at end of file diff --git a/DiscImageChef.Device.Report/scsi.h b/DiscImageChef.Device.Report/scsi.h new file mode 100644 index 00000000..0247ade8 --- /dev/null +++ b/DiscImageChef.Device.Report/scsi.h @@ -0,0 +1,17 @@ +// +// Created by claunia on 11/12/17. +// + +#ifndef DISCIMAGECHEF_DEVICE_REPORT_SCSI_H +#define DISCIMAGECHEF_DEVICE_REPORT_SCSI_H + +int SendScsiCommand(int fd, void *cdb, unsigned char cdb_len, unsigned char *buffer, unsigned int buffer_len, unsigned char **senseBuffer, int direction); +int Inquiry(int fd, unsigned char **buffer, unsigned char **senseBuffer); + +typedef enum +{ + SCSI_INQUIRY = 0x12, + SCSI_ATA_PASSTHROUGH_16 = 0x85 +} ScsiCommands; + +#endif //DISCIMAGECHEF_DEVICE_REPORT_SCSI_H