Reading data by ATAPI using DMA failed
Posted: Sat Jan 18, 2014 2:12 am
I'm trying to use DMA instead of PIO to read data from an ATAPI device (CD-ROM).
First,I write the code below to check DMA support.
The 'data' is gotten by IDENTIFY_PACKET command.
I run in VirtualBox,QEMU and Bochs.All of them support DMA.
So I write code to transfer data using DMA.
My Interrupt Code:
If the argument 'dma' is 0,nothing will be wrong.
But if the argument 'dma' is 1 (using DMA),there will be something wrong:
1. In VirtualBox and Bochs,there won't be anything wrong.
2. But in QEMU,nothing in 'buf' will be changed.
QEMU will produce an IRQ, and the 'status' and 'busMasterIDEStatus' don't have any error bits.
It seems that everything is true,but the 'buf' never changes.
I try to debug my code.
The addresses of the 'prdt','buf' and io ports are all true. (Including physics addresses and virtual addresses.)
Then I try QEMU's 'compat_monitor0' tab:
What's the problems?Thanks!
First,I write the code below to check DMA support.
Code: Select all
device->dma = !!(data[49] & (1 << 8));
I run in VirtualBox,QEMU and Bochs.All of them support DMA.
So I write code to transfer data using DMA.
Code: Select all
static int ideSendCommandATAPI(IDEDevice *device,u8 *cmd,
u16 cmdSize,u16 transSize,void *buf,u8 dma)
{
int sizeRead = transSize;
Task *current = getCurrentTask();
const u8 primary = device->primary;
const u8 master = device->master;
IDEInterruptWait wait = {.task = current,.primary = primary};
u8 irq =
(primary == IDE_PRIMARY) ? IDE_PRIMARY_IRQ : IDE_SECONDARY_IRQ;
dma &= device->dma;
downSemaphore(&ideSemaphores[primary]);
ideOutb(primary,IDE_REG_CONTROL,IDE_ENABLE_IRQ); /*Enable IRQ.*/
if(dma) /*Setup DMA.*/
{
u64 *prdt = getPhysicsPageAddress(device->prdt);
pointer address = va2pa(buf); /*Get the physics address of buf.*/
u64 data = (u32)address; /*This buffer is 4KB aligned: 0xXXXXXXXXX000 .*/
data |= ((u64)transSize) << 32; /*It's 2048 bytes.*/
data |= 0x8000000000000000ul; /*EOT. Last PRD.*/
*prdt = data; /*Set the PRD.*/
address = va2pa(prdt); /*Get the physics address of prdt.*/
ideOutb(primary,IDE_REG_BMCOMMAND,0); /*Stop.*/
ideOutb(primary,IDE_REG_BMSTATUS,ideInb(primary,IDE_REG_BMSTATUS)); /*Clear all bits of the status register.*/
ideOutl(primary,IDE_REG_BMPRDT,address); /*Set the prdt address.*/
ideOutb(primary,IDE_REG_BMCOMMAND,IDE_BMCMD_READ); /*Read.*/
ideOutb(primary,IDE_REG_BMCOMMAND,IDE_BMCMD_START | IDE_BMCMD_READ); /*Start the transfer.*/
}
ideWaitReady(primary);
ideOutb(primary,IDE_REG_DEV_SEL,(master << 4) | 0xa0); /*Select drive.*/
ideWaitReady(primary);
if(!dma)
{
ideOutb(primary,IDE_REG_FEATURES,0x0);
ideOutb(primary,IDE_REG_LBA1,(transSize & 0xff));
ideOutb(primary,IDE_REG_LBA2,(transSize >> 8) & 0xff);
}else{
ideOutb(primary,IDE_REG_FEATURES,0x1); /*DMA Mode.*/
ideOutb(primary,IDE_REG_LBA1,0); /*It's ignored by device.*/
ideOutb(primary,IDE_REG_LBA2,0);
}
ideOutb(primary,IDE_REG_COMMAND,IDE_CMD_PACKET);/*Send command.*/
if(ideWaitDRQ(primary))
goto failed;
current->state = TaskStopping;
setIRQData(irq,&wait);
ideOutsw(primary,IDE_REG_DATA,cmdSize / 2,cmd); /*Send Command.*/
schedule(); /*Wait for an interrupt.*/
setIRQData(irq,0);
if(dma)
goto out;
/*Something about PIO transfer is left out.*/
out:
upSemaphore(&ideSemaphores[primary]);
return 0;
failed:
upSemaphore(&ideSemaphores[primary]);
return -1;
}
Code: Select all
static int ideIRQCommon(int primary,IDEInterruptWait *wait)
{
if(wait)
if(wait->primary != primary)
return -1; /*It should never arrive here.*/
u8 busMasterIDEStatus = ideInb(primary,IDE_REG_BMSTATUS);
u8 status = ideInb(primary,IDE_REG_STATUS); /*Get status.*/
if(!(busMasterIDEStatus & IDE_BMSTATUS_INTERRUPT))
return -1;
ideOutb(primary,IDE_REG_BMSTATUS,IDE_BMSTATUS_INTERRUPT);
ideOutb(primary,IDE_REG_BMCOMMAND,0x0); /*Stop the transfer.*/
if(wait)
wakeUpTask(wait->task);
return 0;
}
But if the argument 'dma' is 1 (using DMA),there will be something wrong:
1. In VirtualBox and Bochs,there won't be anything wrong.
2. But in QEMU,nothing in 'buf' will be changed.
QEMU will produce an IRQ, and the 'status' and 'busMasterIDEStatus' don't have any error bits.
It seems that everything is true,but the 'buf' never changes.
I try to debug my code.
The addresses of the 'prdt','buf' and io ports are all true. (Including physics addresses and virtual addresses.)
Then I try QEMU's 'compat_monitor0' tab:
Code: Select all
(qemu) i 0xc04c (PRDT Address Register)
portl[0xc04c] - 0x7ffc000 (The physics address of 'prdt')
(qemu) xp 0x7ffc000 (The physics address of 'prdt')
0000000007ffc000: 0x07fe0000 (The physics address of 'buf')
(qemu) xp 0x07fc004 (The physics address of 'prdt' + 4)
0000000007ffc004: 0x80000800 (EOT: 1,Byte Count:0x800 [2048])
(qemu) xp 0x7fe0000 (The physics address of 'buf')
0000000007fe0000: 0x00000000 (Nothing???? [If I read data using PIO,there is some data!!!])