Unable to read data CD via ATAPI READ(12) command
Posted: Sat Feb 22, 2025 9:07 am
I'm trying to develop a custom board for using an IDE CD drive as an audio player.
While using MMC to make it play on its own was easy enough, I figured I also make the firmware understand iso9660 — even though it's slow to be practically useful (PIO over I2C..!), it could be sufficient for tiny files such as firmware updates or MOD/XM files
As of now, I have a set of ATA primitives to read/write registers and am able to play CDs, read TOCs and CD TEXTs, so I presume hardware wise everything is fine and endianness et al is correct.
So I proceeded to define the ATAPI READ(12) command as follows:
(full commit including this and other code: https://github.com/vladkorotnev/cd-play ... d603a563ba)
Then I attempt to read the very first sector of the CD:
Where `send_packet` is defined as:
After the `send_packet` function completes, I hear my drive spin up to some insane speed in comparison to audio playback, so it is trying to do something. The error flag is not being raised either. Then I go to read the sector, and lo and behold the DRQ is not set either and I get no data.
I feel like I'm missing something simple, but what could it be?
Thanks so much in advance
While using MMC to make it play on its own was easy enough, I figured I also make the firmware understand iso9660 — even though it's slow to be practically useful (PIO over I2C..!), it could be sufficient for tiny files such as firmware updates or MOD/XM files

As of now, I have a set of ATA primitives to read/write registers and am able to play CDs, read TOCs and CD TEXTs, so I presume hardware wise everything is fine and endianness et al is correct.
So I proceeded to define the ATAPI READ(12) command as follows:
Code: Select all
struct ATAPI_PKT Read {
uint8_t opcode;
uint8_t padding0;
uint32_t lba;
uint32_t sectors;
uint8_t padding1;
ReqPktFooter footer;
};
Then I attempt to read the very first sector of the CD:
Code: Select all
const Requests::Read req2 = {
.opcode = OperationCodes::READ_12,
.lba = htobe32(0),
.sectors = htobe32(1),
};
uint8_t sector[2048] = { 0 };
ide->write(IDE::Register::Feature, {{.low = 0, .high = 0xFF}});
ide->write(IDE::Register::CylinderLow, {{.low = (2048 & 0xFF), .high = 0xFF}});
ide->write(IDE::Register::CylinderHigh, {{.low = (2048 >> 8), .high = 0xFF}});
send_packet(&req2, sizeof(req2), true);
delay(1000);
wait_not_busy();
if(read_sts_regi().ERR) {
ESP_LOGE(LOG_TAG, "Sector test read fail?"); //<- this does not get logged (unless I do something stupid, e.g. put an Audio CD in)
}
data16 lba_hi = ide->read(IDE::Register::CylinderHigh);
data16 lba_lo = ide->read(IDE::Register::CylinderLow);
ESP_LOGW(LOG_TAG, "HI = %04x, LO = %04x", lba_hi.value, lba_lo.value); //<- logs as expected, HI = ff08, LO = ff00
read_response(§or, sizeof(sector), true);
Code: Select all
void Device::send_packet(const void * buf, size_t bufLen, bool pad) {
// Need to set nIEN before sending the PACKET command
ide->write(IDE::Register::DeviceControl, {{ .low = (DeviceControlRegister {{ .nIEN = true }}).value, .high = 0xFF }});
delayMicroseconds(100);
ide->write(IDE::Register::Command, {{ .low = Command::WRITE_PACKET, .high = 0xFF }});
const uint8_t* data = (const uint8_t*) buf;
int i = 0;
for(i = 0; i < bufLen; i+=2) {
delayMicroseconds(10);
ide->write(IDE::Register::Data, {{ .low = data[i], .high = data[i + 1] }});
}
if(pad) {
for(; i < packet_size; i+=2) {
delayMicroseconds(10);
ide->write(IDE::Register::Data, { 0x00, 0x00 });
}
}
wait_not_busy();
}
I feel like I'm missing something simple, but what could it be?
Thanks so much in advance