Why does the following SATAPI inquiry function is showing "Task File Error"?
Posted: Fri Sep 26, 2025 6:48 am
My long vison is to read the sector from AHCI SATAPI CD/DVD Disk. I get HBA_PORT *port for corresponding SATAPI Disk from a AHCI Controller by using PCI Scan. I know SATAPI will manage ATAPI PACKET instead ATA Commands which is using in SATA Disk. I have written following function to manage SATAPI Device:
Now I have written following inquiry function
When I am calling `satapi_inquiry(satapi_disks[0]);` is showing
How could I solve this issue?
Code: Select all
void AtpiPortRebase(HBA_MEM_T *abar, int port_no)
{
HBA_PORT_T *port = &abar->ports[port_no];
stopCMD(port);
// Allocate a physically contiguous region for this port:
// Make it large enough for CLB (1K), FB (1K), CTBA area (8K), plus margin.
const size_t ALLOC_SIZE = 64 * 1024; // 64 KiB to be safe
void *base_virt = (void *) kheap_alloc(ALLOC_SIZE, ALLOCATE_DATA);
if (!base_virt) {
printf(" portRebase: kmalloc failed\n");
return;
}
// Convert to physical base (what HBA will use)
uintptr_t base_phys = vir_to_phys((uintptr_t)base_virt);
if (base_phys == 0) {
printf(" portRebase: vir_to_phys returned 0\n");
return;
}
// Layout within the allocated block (choose simple contiguous layout)
// CLB: offset 0
uintptr_t clb_phys = base_phys + 0x0;
void *clb_virt = (void *)((uintptr_t)base_virt + 0x0); // virtual pointer for CPU
memset(clb_virt, 0, 0x400); // 1 KiB
// FB: offset 4K (use 4 KiB aligned area)
uintptr_t fb_phys = base_phys + 0x1000;
void *fb_virt = (void *)((uintptr_t)base_virt + 0x1000);
memset(fb_virt, 0, 0x100); // 256 bytes (FIS receive area), zeroed
// Command tables area: offset 8K (we'll allocate 8 KiB per port for command tables)
uintptr_t ctba_base_phys = base_phys + 0x2000;
void *ctba_base_virt = (void *)((uintptr_t)base_virt + 0x2000);
// Zero the whole CTBA area (32 * 256 = 8192)
memset(ctba_base_virt, 0, 0x2000);
// Program registers (hardware uses physical addresses)
port->clb = (uint32_t)(clb_phys & 0xFFFFFFFF);
port->clbu = (uint32_t)(clb_phys >> 32);
port->fb = (uint32_t)(fb_phys & 0xFFFFFFFF);
port->fbu = (uint32_t)(fb_phys >> 32);
// Now initialize each command header to point into the CTBA area
HBA_CMD_HEADER_T* cmd_header = (HBA_CMD_HEADER_T*) clb_virt; // CPU-side pointer into CLB area
for (int i = 0; i < 32; ++i) {
// Each command table sized 256 bytes (0x100) at sequential offsets
uintptr_t phys_ctba = ctba_base_phys + (i * 0x100);
cmd_header[i].ctba = (uint32_t)(phys_ctba & 0xFFFFFFFF);
cmd_header[i].ctbau = (uint32_t)(phys_ctba >> 32);
cmd_header[i].prdtl = 8; // example default
// Clear command table memory (use virtual)
void* virt_ctba = (void*)((uintptr_t)ctba_base_virt + (i * 0x100));
memset(virt_ctba, 0, 0x100);
}
port->cmd |= HBA_PxCMD_ATAPI; // Set bit 24 for ATAPI devices
// Wait a bit for ATAPI mode to take effect
for (volatile int i = 0; i < 1000; i++);
// Start command engine after setting CLB/FB/CTBA
startCMD(port);
// printf("[AHCI] portRebase: done for port %d (phys base %x)\n", port_no, (unsigned long)base_phys);
}
Code: Select all
static bool runAtapiCommand(HBA_PORT_T *port, uint8_t *cdb, size_t cdb_len, uintptr_t buf_phys, uint32_t buf_size, bool write)
{
port->is = (uint32_t)-1; // clear pending interrupts
int slot = findCMDSlot(port, 32);
if (slot == -1) return false;
// CLB → command header base
uintptr_t clb_phys = ((uint64_t)port->clb) | ((uint64_t)port->clbu << 32);
HBA_CMD_HEADER_T *cmd_header_base = (HBA_CMD_HEADER_T*)(uintptr_t)phys_to_vir(clb_phys);
HBA_CMD_HEADER_T *cmd_header = &cmd_header_base[slot];
// set up header
cmd_header->cfl = sizeof(FIS_REG_H2D_T) / 4;
cmd_header->w = write ? 1 : 0;
cmd_header->a = 1; // ATAPI
cmd_header->prdtl = (buf_size > 0) ? 1 : 0; // single PRDT
// Command table
uint64_t ctba_phys = ((uint64_t)cmd_header->ctbau << 32) | cmd_header->ctba;
HBA_CMD_TBL_T *cmd_tbl = (HBA_CMD_TBL_T*)(uintptr_t)phys_to_vir(ctba_phys);
memset(cmd_tbl, 0, sizeof(HBA_CMD_TBL_T) + (cmd_header->prdtl-1)*sizeof(HBA_PRDT_ENTRY_T));
// Fill PRDT if data buffer
if (buf_size > 0) {
cmd_tbl->prdt_entry[0].dba = (uint32_t)(buf_phys & 0xFFFFFFFF);
cmd_tbl->prdt_entry[0].dbau = (uint32_t)(buf_phys >> 32);
cmd_tbl->prdt_entry[0].dbc = buf_size - 1;
cmd_tbl->prdt_entry[0].i = 1;
}
// Fill CFIS
FIS_REG_H2D_T *cfis = (FIS_REG_H2D_T*)&cmd_tbl->cfis;
memset(cfis, 0, sizeof(FIS_REG_H2D_T));
cfis->fis_type = FIS_TYPE_REG_H2D;
cfis->c = 1;
cfis->command = ATA_CMD_PACKET;
cfis->device = 0;
// Copy CDB into acmd
memset(cmd_tbl->acmd, 0, 16);
memcpy(cmd_tbl->acmd, cdb, (cdb_len > 16 ? 16 : cdb_len));
// Wait until port not busy
int spin = 0;
while ((port->tfd & (ATA_DEV_BUSY | ATA_DEV_DRQ)) && spin < 1000000) spin++;
if (spin == 1000000) {
printf(" [SATAPI] Port hung\n");
return false;
}
// Issue command
port->ci = 1 << slot;
// Wait for completion
while (true) {
if (!(port->ci & (1 << slot))) break;
if (port->is & HBA_PxIS_TFES) {
printf(" [SATAPI] Task File Error\n");
return false;
}
}
if (port->is & HBA_PxIS_TFES) {
printf(" [SATAPI] TFES after completion\n");
return false;
}
return true;
}
Code: Select all
bool satapi_inquiry(HBA_PORT_T *port) {
if(!port) return false;
uint8_t cdb[12] = {0};
cdb[0] = ATAPI_CMD_INQUIRY;
cdb[4] = 36; // allocation length
void *buf = kheap_alloc(36, ALLOCATE_DATA);
if (!buf) return false;
uintptr_t buf_phys = vir_to_phys((uintptr_t)buf);
if (!runAtapiCommand(port, cdb, 12, buf_phys, 36, false)) {
printf(" Inquiry failed\n");
return false;
}
uint8_t *resp = (uint8_t*)buf;
printf(" Vendor: %.8s, Product: %.16s, Revision: %.4s\n",
&resp[8], &resp[16], &resp[32]);
return true;
}
Code: Select all
[SATAPI] Task File Error
Inquiry failed