Skip to content
Snippets Groups Projects
Select Git revision
  • 5b7f8cbe7cda336b3a23c1b16fa36ef282b13803
  • master default protected
2 results

ide.c

Blame
  • user avatar
    rtm authored
    fbd8857d
    History
    ide.c 3.13 KiB
    // Simple PIO-based (non-DMA) IDE driver code.
    
    #include "types.h"
    #include "defs.h"
    #include "param.h"
    #include "mmu.h"
    #include "proc.h"
    #include "x86.h"
    #include "traps.h"
    #include "spinlock.h"
    #include "buf.h"
    
    #define IDE_BSY       0x80
    #define IDE_DRDY      0x40
    #define IDE_DF        0x20
    #define IDE_ERR       0x01
    
    #define IDE_CMD_READ  0x20
    #define IDE_CMD_WRITE 0x30
    
    // ide_queue points to the buf now being read/written to the disk.
    // ide_queue->qnext points to the next buf to be processed.
    // You must hold ide_lock while manipulating queue.
    
    static struct spinlock ide_lock;
    static struct buf *ide_queue;
    
    static int disk_1_present;
    static void ide_start_request();
    
    // Wait for IDE disk to become ready.
    static int
    ide_wait_ready(int check_error)
    {
      int r;
    
      while(((r = inb(0x1f7)) & IDE_BSY) || !(r & IDE_DRDY))
        ;
      if(check_error && (r & (IDE_DF|IDE_ERR)) != 0)
        return -1;
      return 0;
    }
    
    void
    ide_init(void)
    {
      int i;
    
      initlock(&ide_lock, "ide");
      pic_enable(IRQ_IDE);
      ioapic_enable(IRQ_IDE, ncpu - 1);
      ide_wait_ready(0);
      
      // Check if disk 1 is present
      outb(0x1f6, 0xe0 | (1<<4));
      for(i=0; i<1000; i++){
        if(inb(0x1f7) != 0){
          disk_1_present = 1;
          break;
        }
      }
      
      // Switch back to disk 0.
      outb(0x1f6, 0xe0 | (0<<4));
    }
    
    // Start the request for b.  Caller must hold ide_lock.
    static void
    ide_start_request(struct buf *b)
    {
      if(b == 0)
        panic("ide_start_request");
    
      ide_wait_ready(0);
      outb(0x3f6, 0);  // generate interrupt
      outb(0x1f2, 1);  // number of sectors
      outb(0x1f3, b->sector & 0xff);
      outb(0x1f4, (b->sector >> 8) & 0xff);
      outb(0x1f5, (b->sector >> 16) & 0xff);
      outb(0x1f6, 0xe0 | ((b->dev&1)<<4) | ((b->sector>>24)&0x0f));
      if(b->flags & B_DIRTY){
        outb(0x1f7, IDE_CMD_WRITE);
        outsl(0x1f0, b->data, 512/4);
      } else {
        outb(0x1f7, IDE_CMD_READ);
      }
    }
    
    // Interrupt handler.
    void
    ide_intr(void)
    {
      struct buf *b;
    
      acquire(&ide_lock);
      if((b = ide_queue) == 0){
        release(&ide_lock);
        return;
      }
    
      // Read data if needed.
      if(!(b->flags & B_DIRTY) && ide_wait_ready(1) >= 0)
        insl(0x1f0, b->data, 512/4);
      
      // Wake process waiting for this buf.
      b->flags |= B_VALID;
      b->flags &= ~B_DIRTY;
      wakeup(b);
      
      // Start disk on next buf in queue.
      if((ide_queue = b->qnext) != 0)
        ide_start_request(ide_queue);
    
      release(&ide_lock);
    }
    
    //PAGEBREAK!
    // Sync buf with disk. 
    // If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID.
    // Else if B_VALID is not set, read buf from disk, set B_VALID.
    void
    ide_rw(struct buf *b)
    {
      struct buf **pp;
    
      if(!(b->flags & B_BUSY))
        panic("ide_rw: buf not busy");
      if((b->flags & (B_VALID|B_DIRTY)) == B_VALID)
        panic("ide_rw: nothing to do");
      if(b->dev != 0 && !disk_1_present)
        panic("ide disk 1 not present");
    
      acquire(&ide_lock);
    
      // Append b to ide_queue.
      b->qnext = 0;
      for(pp=&ide_queue; *pp; pp=&(*pp)->qnext)
        ;
      *pp = b;
      
      // Start disk if necessary.
      if(ide_queue == b)
        ide_start_request(b);
      
      // Wait for request to finish.
      // Assuming will not sleep too long: ignore cp->killed.
      while((b->flags & (B_VALID|B_DIRTY)) != B_VALID)
        sleep(b, &ide_lock);
    
      release(&ide_lock);
    }