Reading data by ATAPI using DMA failed

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
windows8
Member
Member
Posts: 33
Joined: Sat Feb 23, 2013 3:52 am

Reading data by ATAPI using DMA failed

Post by windows8 »

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.

Code: Select all

      device->dma = !!(data[49] & (1 << 8));
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.

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;
}
My Interrupt Code:

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;
}

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:

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!!!])
What's the problems?Thanks!
Post Reply