diff --git a/devices/common/ata/atabasedevice.h b/devices/common/ata/atabasedevice.h index 7bcbe7c..497783c 100644 --- a/devices/common/ata/atabasedevice.h +++ b/devices/common/ata/atabasedevice.h @@ -27,6 +27,7 @@ along with this program. If not, see . #include #include #include +#include "endianswap.h" #include #include @@ -55,6 +56,14 @@ public: void device_control(const uint8_t new_ctrl); void update_intrq(uint8_t new_intrq_state); + bool has_data() { + return data_ptr && xfer_cnt; + } + + uint16_t get_data() { + return BYTESWAP_16(*this->data_ptr++); + } + protected: bool is_selected() { return ((this->r_dev_head >> 4) & 1) == this->my_dev_id; }; diff --git a/devices/common/ata/atapibasedevice.cpp b/devices/common/ata/atapibasedevice.cpp index 95e376c..e34c73f 100644 --- a/devices/common/ata/atapibasedevice.cpp +++ b/devices/common/ata/atapibasedevice.cpp @@ -50,28 +50,26 @@ void AtapiBaseDevice::device_set_signature() { uint16_t AtapiBaseDevice::read(const uint8_t reg_addr) { switch (reg_addr) { case ATA_Reg::DATA: - if (this->data_ptr && this->xfer_cnt) { + if (has_data()) { + uint16_t ret_data = get_data(); this->xfer_cnt -= 2; if (this->xfer_cnt <= 0) { this->r_status &= ~DRQ; if ((this->r_int_reason & ATAPI_Int_Reason::IO) && - !(this->r_int_reason & ATAPI_Int_Reason::CoD)) { - uint16_t ret_data = BYTESWAP_16(*this->data_ptr++); + !(this->r_int_reason & ATAPI_Int_Reason::CoD) + ) { if (this->data_available()) { this->r_status &= ~DRQ; this->r_status |= BSY; - this->update_intrq(1); - return ret_data; + this->update_intrq(1); // Is this going to work here? The interrupt happens before returning ret_data. } - - if (this->status_expected) { + else if (this->status_expected) { this->present_status(); } - return ret_data; } } - return BYTESWAP_16(*this->data_ptr++); + return ret_data; } else { return 0xFFFFU; } @@ -105,7 +103,7 @@ uint16_t AtapiBaseDevice::read(const uint8_t reg_addr) { void AtapiBaseDevice::write(const uint8_t reg_addr, const uint16_t value) { switch (reg_addr) { case ATA_Reg::DATA: - if (this->data_ptr && this->xfer_cnt) { + if (has_data()) { *this->data_ptr++ = BYTESWAP_16(value); this->xfer_cnt -= 2; if (this->xfer_cnt <= 0) { diff --git a/devices/common/ata/atapicdrom.cpp b/devices/common/ata/atapicdrom.cpp index 94e4e67..2cb9e34 100644 --- a/devices/common/ata/atapicdrom.cpp +++ b/devices/common/ata/atapicdrom.cpp @@ -68,6 +68,11 @@ void AtapiCdrom::perform_packet_command() { uint32_t lba, xfer_len; this->r_status |= BSY; + this->sector_areas = 0; + if (this->doing_sector_areas) { + this->doing_sector_areas = false; + LOG_F(WARNING, "%s: doing_sector_areas reset", this->name.c_str()); + } switch (this->cmd_pkt[0]) { case ScsiCommand::TEST_UNIT_READY: @@ -183,20 +188,70 @@ void AtapiCdrom::perform_packet_command() { this->present_status(); break; case ScsiCommand::READ_CD: - lba = READ_DWORD_BE_U(&this->cmd_pkt[2]); + { + lba = READ_DWORD_BE_U(&this->cmd_pkt[2]); xfer_len = (this->cmd_pkt[6] << 16) | READ_WORD_BE_U(&this->cmd_pkt[7]); - if (this->cmd_pkt[1] || this->cmd_pkt[9] != 0x10 || this->cmd_pkt[10]) - LOG_F(WARNING, "%s: unsupported READ_CD params", this->name.c_str()); + if (this->cmd_pkt[1] || (this->cmd_pkt[9] & ~0xf8) || ((this->cmd_pkt[9] & 0xf8) == 0) || this->cmd_pkt[10]) + LOG_F(WARNING, "%s: unsupported READ_CD params: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", + this->name.c_str(), + this->cmd_pkt[0], this->cmd_pkt[1], this->cmd_pkt[2], this->cmd_pkt[3], this->cmd_pkt[4], this->cmd_pkt[5], + this->cmd_pkt[6], this->cmd_pkt[7], this->cmd_pkt[8], this->cmd_pkt[9], this->cmd_pkt[10], this->cmd_pkt[11] + ); if (this->r_features & ATAPI_Features::DMA) { LOG_F(WARNING, "ATAPI DMA transfer requsted"); } this->set_fpos(lba); - this->xfer_cnt = this->read_begin(xfer_len, this->r_byte_count); - this->r_byte_count = this->xfer_cnt; + this->sector_areas = cmd_pkt[9]; + this->doing_sector_areas = true; + this->current_block = lba; + this->current_block_byte = 0; + + int bytes_prolog = 0; + int bytes_data = 0; + int bytes_epilog = 0; + + // For Mode 1 CD-ROMs: + if (this->sector_areas & 0x80) bytes_prolog += 12; // Sync + if (this->sector_areas & 0x20) bytes_prolog += 4; // Header + if (this->sector_areas & 0x40) bytes_prolog += 0; // SubHeader + if (this->sector_areas & 0x10) bytes_data += 2048; // User + if (this->sector_areas & 0x08) bytes_epilog += 288; // Auxiliary + if (this->sector_areas & 0x02) bytes_epilog += 294; // ErrorFlags + if (this->sector_areas & 0x01) bytes_epilog += 96; // SubChannel + + int bytes_per_block = bytes_prolog + bytes_data + bytes_epilog; + + int disk_image_byte_count; + + if (bytes_per_block == 0) { + disk_image_byte_count = 0xffff; + } + else { + disk_image_byte_count = (this->r_byte_count / bytes_per_block) * this->block_size; // whole blocks + if ((this->r_byte_count % bytes_per_block) > bytes_prolog) { // partial block + int disk_image_byte_count_partial_block = (this->r_byte_count % bytes_per_block) - bytes_prolog; // remove prolog from partial block + if (disk_image_byte_count_partial_block > this->block_size) { // partial block includes some epilog? + disk_image_byte_count_partial_block = this->block_size; // // remove epilog from partial block + } + // add partial block + disk_image_byte_count += disk_image_byte_count_partial_block; + } + } + + int disk_image_bytes_received = this->read_begin(xfer_len, disk_image_byte_count); + + int bytes_received = (disk_image_bytes_received / this->block_size) * bytes_per_block + bytes_prolog + (disk_image_bytes_received % this->block_size); // whole blocks + prolog + partial block + if (bytes_received > this->r_byte_count) { // if partial epilog or partial prolog + bytes_received = this->r_byte_count; // confine to r_byte_count + } + this->r_byte_count = bytes_received; + this->xfer_cnt = bytes_received; + this->data_ptr = (uint16_t*)this->data_cache.get(); this->status_good(); this->data_out_phase(); break; + } default: LOG_F(ERROR, "%s: unsupported ATAPI command 0x%X", this->name.c_str(), this->cmd_pkt[0]); @@ -205,6 +260,101 @@ void AtapiCdrom::perform_packet_command() { } } +int AtapiCdrom::request_data() { + // continuation of READ_CD above + + this->data_ptr = (uint16_t*)this->data_cache.get(); + + this->xfer_cnt = this->read_more(); + + return this->xfer_cnt; +} + +static const uint8_t mode_1_sync[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; + +uint16_t AtapiCdrom::get_data() { + uint16_t ret_data; + + if (doing_sector_areas) { + // For Mode 1 CD-ROMs: + int area_start; + int area_end = 0; + + ret_data = 0xffff; + + if (this->sector_areas & 0x80) { + area_start = area_end; + area_end += 12; // Sync + if (this->current_block_byte >= area_start && this->current_block_byte < area_end) { + ret_data = BYTESWAP_16(*((uint16_t*)(&mode_1_sync[current_block_byte - area_start]))); + } + } + + if (this->sector_areas & 0x20) { + area_start = area_end; + area_end += 4; // Header + if (this->current_block_byte >= area_start && this->current_block_byte < area_end) { + AddrMsf msf = lba_to_msf(this->current_block + 150); + uint8_t header[4]; + header[0] = msf.min; + header[1] = msf.sec; + header[2] = msf.frm; + header[3] = 0x01; // Mode 1 + ret_data = BYTESWAP_16(*((uint16_t*)(&header[current_block_byte - area_start]))); + } + } + + if (this->sector_areas & 0x40) { + area_start = area_end; + area_end += 0; // SubHeader + if (this->current_block_byte >= area_start && this->current_block_byte < area_end) { + ret_data = 0; + } + } + + if (this->sector_areas & 0x10) { + area_start = area_end; + area_end += 2048; // User + if (this->current_block_byte >= area_start && this->current_block_byte < area_end) { + ret_data = AtaBaseDevice::get_data(); + } + } + + if (this->sector_areas & 0x08) { + area_start = area_end; + area_end += 288; // Auxiliary + if (this->current_block_byte >= area_start && this->current_block_byte < area_end) { + ret_data = 0; + } + } + + if (this->sector_areas & 0x02) { + area_start = area_end; + area_end += 294; // ErrorFlags + if (this->current_block_byte >= area_start && this->current_block_byte < area_end) { + ret_data = 0; + } + } + + if (this->sector_areas & 0x01) { + area_start = area_end; + area_end += 96; // SubChannel + if (this->current_block_byte >= area_start && this->current_block_byte < area_end) { + ret_data = 0; + } + } + + current_block_byte += 2; + if (current_block_byte >= area_end) + current_block_byte = 0; + } + else { + ret_data = AtaBaseDevice::get_data(); + } + + return ret_data; +} + void AtapiCdrom::status_good() { this->status_expected = true; this->r_error = 0; diff --git a/devices/common/ata/atapicdrom.h b/devices/common/ata/atapicdrom.h index 84bd50b..c67b8a8 100644 --- a/devices/common/ata/atapicdrom.h +++ b/devices/common/ata/atapicdrom.h @@ -42,11 +42,7 @@ public: void perform_packet_command() override; - int request_data() override { - this->data_ptr = (uint16_t*)this->data_cache.get(); - this->xfer_cnt = this->read_more(); - return this->xfer_cnt; - } + int request_data() override; bool data_available() override { return this->data_left() != 0; @@ -55,10 +51,16 @@ public: void status_good(); void status_error(uint8_t sense_key, uint8_t asc); + uint16_t get_data(); private: uint8_t sense_key = 0; uint8_t asc = 0; uint8_t ascq = 0; + + bool doing_sector_areas = false; + uint8_t sector_areas; + uint32_t current_block; + uint16_t current_block_byte; }; #endif // ATAPI_CDROM_H diff --git a/devices/storage/cdromdrive.h b/devices/storage/cdromdrive.h index 60b20ee..b5f59ad 100644 --- a/devices/storage/cdromdrive.h +++ b/devices/storage/cdromdrive.h @@ -67,7 +67,6 @@ public: uint32_t report_capacity(uint8_t *data_ptr); uint32_t read_toc(uint8_t *cmd_ptr, uint8_t *data_ptr); -private: AddrMsf lba_to_msf(const int lba); protected: