diff --git a/CMakeLists.txt b/CMakeLists.txt index 78d6243..72549b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(CMAKE_C_STANDARD 90) set(MAIN_SOURCES main.c list_devices.c device.c scsi.c hex2bin.c usb.c ieee1394.c pcmcia.c ata.c) if("${CMAKE_SYSTEM}" MATCHES "Linux") - set(PLATFORM_SOURCES linux/list_devices.c linux/linux.h linux/device.c linux/scsi.c linux/usb.c linux/ieee1394.c linux/pcmcia.c) + set(PLATFORM_SOURCES linux/list_devices.c linux/linux.h linux/device.c linux/scsi.c linux/usb.c linux/ieee1394.c linux/pcmcia.c linux/ata.c) endif() add_executable(dicremote ${MAIN_SOURCES} ${PLATFORM_SOURCES}) diff --git a/ata.c b/ata.c index dac2359..15fd7b5 100644 --- a/ata.c +++ b/ata.c @@ -19,6 +19,10 @@ #include +#if defined(__linux__) && !defined(__ANDROID__) +#include "linux/linux.h" +#endif + int32_t SendAtaChsCommand(int device_fd, AtaRegistersChs registers, AtaErrorRegistersChs* errorRegisters, @@ -28,9 +32,24 @@ int32_t SendAtaChsCommand(int device_fd, uint32_t timeout, uint8_t transferBlocks, uint32_t* duration, - uint32_t* sense) + uint32_t* sense, + uint32_t* buf_len) { +#if defined(__linux__) && !defined(__ANDROID__) + return linux_send_ata_chs_command(device_fd, + registers, + errorRegisters, + protocol, + transferRegister, + buffer, + timeout, + transferBlocks, + duration, + sense, + buf_len); +#else return -1; +#endif } int32_t SendAtaLba28Command(int device_fd, @@ -42,9 +61,24 @@ int32_t SendAtaLba28Command(int device_fd, uint32_t timeout, uint8_t transferBlocks, uint32_t* duration, - uint32_t* sense) + uint32_t* sense, + uint32_t* buf_len) { +#if defined(__linux__) && !defined(__ANDROID__) + return linux_send_ata_lba28_command(device_fd, + registers, + errorRegisters, + protocol, + transferRegister, + buffer, + timeout, + transferBlocks, + duration, + sense, + buf_len); +#else return -1; +#endif } int32_t SendAtaLba48Command(int device_fd, @@ -56,7 +90,22 @@ int32_t SendAtaLba48Command(int device_fd, uint32_t timeout, uint8_t transferBlocks, uint32_t* duration, - uint32_t* sense) + uint32_t* sense, + uint32_t* buf_len) { +#if defined(__linux__) && !defined(__ANDROID__) + return linux_send_ata_lba48_command(device_fd, + registers, + errorRegisters, + protocol, + transferRegister, + buffer, + timeout, + transferBlocks, + duration, + sense, + buf_len); +#else return -1; +#endif } diff --git a/dicmote.h b/dicmote.h index 1e09855..ce9be25 100644 --- a/dicmote.h +++ b/dicmote.h @@ -82,6 +82,7 @@ #define DICMOTE_ATA_PROTOCOL_UDMA_IN 10 #define DICMOTE_ATA_PROTOCOL_UDMA_OUT 11 #define DICMOTE_ATA_PROTOCOL_FPDMA 12 +#define DICMOTE_ATA_PROTOCOL_RETURN_RESPONSE 15 #define DICMOTE_ATA_TRANSFER_REGISTER_NONE 0 #define DICMOTE_ATA_TRANSFER_REGISTER_FEATURE 1 #define DICMOTE_ATA_TRANSFER_REGISTER_SECTOR_COUNT 2 @@ -458,7 +459,8 @@ int32_t SendAtaChsCommand(int device_fd, uint32_t timeout, uint8_t transferBlocks, uint32_t* duration, - uint32_t* sense); + uint32_t* sense, + uint32_t* buf_len); int32_t SendAtaLba28Command(int device_fd, AtaRegistersLba28 registers, AtaErrorRegistersLba28* errorRegisters, @@ -468,7 +470,8 @@ int32_t SendAtaLba28Command(int device_fd, uint32_t timeout, uint8_t transferBlocks, uint32_t* duration, - uint32_t* sense); + uint32_t* sense, + uint32_t* buf_len); int32_t SendAtaLba48Command(int device_fd, AtaRegistersLba48 registers, AtaErrorRegistersLba48* errorRegisters, @@ -478,6 +481,7 @@ int32_t SendAtaLba48Command(int device_fd, uint32_t timeout, uint8_t transferBlocks, uint32_t* duration, - uint32_t* sense); + uint32_t* sense, + uint32_t* buf_len); #endif diff --git a/linux/ata.c b/linux/ata.c new file mode 100644 index 0000000..2bdecac --- /dev/null +++ b/linux/ata.c @@ -0,0 +1,265 @@ +/* + * This file is part of the DiscImageChef Remote Server. + * Copyright (c) 2019 Natalia Portillo. + * + * 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 3. + * + * 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, see . + */ + +#include "linux.h" + +#include +#include +#include + +int32_t ata_protocol_to_scsi_direction(uint8_t protocol) +{ + switch(protocol) + { + case DICMOTE_ATA_PROTOCOL_DEVICE_DIAGNOSTIC: + case DICMOTE_ATA_PROTOCOL_DEVICE_RESET: + case DICMOTE_ATA_PROTOCOL_HARD_RESET: + case DICMOTE_ATA_PROTOCOL_NO_DATA: + case DICMOTE_ATA_PROTOCOL_SOFT_RESET: + case DICMOTE_ATA_PROTOCOL_RETURN_RESPONSE: return DICMOTE_SCSI_DIRECTION_NONE; + case DICMOTE_ATA_PROTOCOL_PIO_IN: + case DICMOTE_ATA_PROTOCOL_UDMA_IN: return DICMOTE_SCSI_DIRECTION_IN; + case DICMOTE_ATA_PROTOCOL_PIO_OUT: + case DICMOTE_ATA_PROTOCOL_UDMA_OUT: return DICMOTE_SCSI_DIRECTION_OUT; + default: return DICMOTE_SCSI_DIRECTION_UNSPECIFIED; + } +} + +int32_t linux_send_ata_chs_command(int device_fd, + AtaRegistersChs registers, + AtaErrorRegistersChs* errorRegisters, + uint8_t protocol, + uint8_t transferRegister, + char* buffer, + uint32_t timeout, + uint8_t transferBlocks, + uint32_t* duration, + uint32_t* sense, + uint32_t* buf_len) +{ + duration = 0; + sense = 0; + unsigned char cdb[16]; + char* sense_buf; + uint32_t sense_len; + + memset(&cdb, 0, 16); + + cdb[0] = 0x85; + cdb[1] = (protocol << 1) & 0x1E; + if(transferRegister != DICMOTE_ATA_TRANSFER_REGISTER_NONE && protocol != DICMOTE_ATA_PROTOCOL_NO_DATA) + { + switch(protocol) + { + case DICMOTE_ATA_PROTOCOL_PIO_IN: + case DICMOTE_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; + + int error = linux_send_scsi_command(device_fd, + (char*)cdb, + buffer, + &sense_buf, + timeout, + ata_protocol_to_scsi_direction(protocol), + duration, + sense, + 16, + buf_len, + &sense_len); + + if(sense_len < 22 || (sense_buf[8] != 0x09 && sense_buf[9] != 0x0C)) return error; + + 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]; + + *sense = errorRegisters->Error != 0 || (errorRegisters->Status & 0xA5) != 0; + + return error; +} + +int32_t linux_send_ata_lba28_command(int device_fd, + AtaRegistersLba28 registers, + AtaErrorRegistersLba28* errorRegisters, + uint8_t protocol, + uint8_t transferRegister, + char* buffer, + uint32_t timeout, + uint8_t transferBlocks, + uint32_t* duration, + uint32_t* sense, + uint32_t* buf_len) +{ + duration = 0; + sense = 0; + unsigned char cdb[16]; + char* sense_buf; + uint32_t sense_len; + + memset(&cdb, 0, 16); + + cdb[0] = 0x85; + cdb[1] = (protocol << 1) & 0x1E; + if(transferRegister != DICMOTE_ATA_TRANSFER_REGISTER_NONE && protocol != DICMOTE_ATA_PROTOCOL_NO_DATA) + { + switch(protocol) + { + case DICMOTE_ATA_PROTOCOL_PIO_IN: + case DICMOTE_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; + + int error = linux_send_scsi_command(device_fd, + (char*)cdb, + buffer, + &sense_buf, + timeout, + ata_protocol_to_scsi_direction(protocol), + duration, + sense, + 16, + buf_len, + &sense_len); + + if(sense_len < 22 || (sense_buf[8] != 0x09 && sense_buf[9] != 0x0C)) return error; + + 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]; + + *sense = errorRegisters->Error != 0 || (errorRegisters->Status & 0xA5) != 0; + + return error; +} +int32_t linux_send_ata_lba48_command(int device_fd, + AtaRegistersLba48 registers, + AtaErrorRegistersLba48* errorRegisters, + uint8_t protocol, + uint8_t transferRegister, + char* buffer, + uint32_t timeout, + uint8_t transferBlocks, + uint32_t* duration, + uint32_t* sense, + uint32_t* buf_len) +{ + duration = 0; + sense = 0; + unsigned char cdb[16]; + char* sense_buf; + uint32_t sense_len; + + memset(&cdb, 0, 16); + + cdb[0] = 0x85; + cdb[1] = (protocol << 1) & 0x1E; + cdb[1] |= 0x01; + if(transferRegister != DICMOTE_ATA_TRANSFER_REGISTER_NONE && protocol != DICMOTE_ATA_PROTOCOL_NO_DATA) + { + switch(protocol) + { + case DICMOTE_ATA_PROTOCOL_PIO_IN: + case DICMOTE_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] = ((registers.Feature & 0xFF00) >> 8); + cdb[4] = (registers.Feature & 0xFF); + cdb[5] = ((registers.SectorCount & 0xFF00) >> 8); + cdb[6] = (registers.SectorCount & 0xFF); + cdb[7] = ((registers.LbaLow & 0xFF00) >> 8); + cdb[8] = (registers.LbaLow & 0xFF); + cdb[9] = ((registers.LbaMid & 0xFF00) >> 8); + cdb[10] = (registers.LbaMid & 0xFF); + cdb[11] = ((registers.LbaHigh & 0xFF00) >> 8); + cdb[12] = (registers.LbaHigh & 0xFF); + cdb[13] = registers.DeviceHead; + cdb[14] = registers.Command; + + int error = linux_send_scsi_command(device_fd, + (char*)cdb, + buffer, + &sense_buf, + timeout, + ata_protocol_to_scsi_direction(protocol), + duration, + sense, + 16, + buf_len, + &sense_len); + + if(sense_len < 22 || (sense_buf[8] != 0x09 && sense_buf[9] != 0x0C)) return error; + + errorRegisters->Error = sense_buf[11]; + + 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]; + + *sense = errorRegisters->Error != 0 || (errorRegisters->Status & 0xA5) != 0; + + return error; +} \ No newline at end of file diff --git a/linux/linux.h b/linux/linux.h index d8fa039..31a4d57 100644 --- a/linux/linux.h +++ b/linux/linux.h @@ -59,5 +59,38 @@ uint8_t linux_get_ieee1394_data(const char* devicePath, char* vendor, char* model); uint8_t linux_get_pcmcia_data(const char* devicePath, uint16_t* cisLen, char* cis); +int32_t linux_send_ata_chs_command(int device_fd, + AtaRegistersChs registers, + AtaErrorRegistersChs* errorRegisters, + uint8_t protocol, + uint8_t transferRegister, + char* buffer, + uint32_t timeout, + uint8_t transferBlocks, + uint32_t* duration, + uint32_t* sense, + uint32_t* buf_len); +int32_t linux_send_ata_lba28_command(int device_fd, + AtaRegistersLba28 registers, + AtaErrorRegistersLba28* errorRegisters, + uint8_t protocol, + uint8_t transferRegister, + char* buffer, + uint32_t timeout, + uint8_t transferBlocks, + uint32_t* duration, + uint32_t* sense, + uint32_t* buf_len); +int32_t linux_send_ata_lba48_command(int device_fd, + AtaRegistersLba48 registers, + AtaErrorRegistersLba48* errorRegisters, + uint8_t protocol, + uint8_t transferRegister, + char* buffer, + uint32_t timeout, + uint8_t transferBlocks, + uint32_t* duration, + uint32_t* sense, + uint32_t* buf_len); #endif // DICREMOTE_LINUX_H diff --git a/linux/scsi.c b/linux/scsi.c index 9710b9d..6c9cdff 100644 --- a/linux/scsi.c +++ b/linux/scsi.c @@ -48,9 +48,9 @@ int32_t linux_send_scsi_command(int device_fd, { case DICMOTE_SCSI_DIRECTION_IN: dir = SG_DXFER_FROM_DEV; break; case DICMOTE_SCSI_DIRECTION_OUT: dir = SG_DXFER_TO_DEV; break; - case DICMOTE_SCSI_DIRECTION_INOUT: dir = SG_DXFER_TO_FROM_DEV; break; + case DICMOTE_SCSI_DIRECTION_INOUT: + case DICMOTE_SCSI_DIRECTION_UNSPECIFIED: dir = SG_DXFER_TO_FROM_DEV; break; case DICMOTE_SCSI_DIRECTION_NONE: - case DICMOTE_SCSI_DIRECTION_UNSPECIFIED: default: dir = SG_DXFER_NONE; break; } diff --git a/main.c b/main.c index b92108f..6e4abee 100644 --- a/main.c +++ b/main.c @@ -781,7 +781,8 @@ int main() pkt_cmd_ata_chs->timeout, pkt_cmd_ata_chs->transferBlocks, &duration, - &sense); + &sense, + &pkt_cmd_ata_chs->buf_len); out_buf = malloc(sizeof(DicPacketResAtaChs) + pkt_cmd_ata_chs->buf_len); @@ -848,7 +849,8 @@ int main() pkt_cmd_ata_lba28->timeout, pkt_cmd_ata_lba28->transferBlocks, &duration, - &sense); + &sense, + &pkt_cmd_ata_chs->buf_len); out_buf = malloc(sizeof(DicPacketResAtaLba28) + pkt_cmd_ata_lba28->buf_len); @@ -915,7 +917,8 @@ int main() pkt_cmd_ata_lba48->timeout, pkt_cmd_ata_lba48->transferBlocks, &duration, - &sense); + &sense, + &pkt_cmd_ata_chs->buf_len); out_buf = malloc(sizeof(DicPacketResAtaLba48) + pkt_cmd_ata_lba48->buf_len);