Skip to content
Snippets Groups Projects
ide.c 3.21 KiB
Newer Older
rsc's avatar
rsc committed
// Simple PIO-based (non-DMA) IDE driver code.
rtm's avatar
rtm committed

#include "types.h"
rsc's avatar
rsc committed
#include "defs.h"
rtm's avatar
rtm committed
#include "param.h"
Frans Kaashoek's avatar
Frans Kaashoek committed
#include "memlayout.h"
rtm's avatar
rtm committed
#include "mmu.h"
#include "proc.h"
#include "x86.h"
kaashoek's avatar
kaashoek committed
#include "traps.h"
rsc's avatar
rsc committed
#include "spinlock.h"
rtm's avatar
rtm committed
#include "buf.h"
rtm's avatar
rtm committed

rsc's avatar
rsc committed
#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
rtm's avatar
rtm committed

rsc's avatar
rsc committed
// idequeue points to the buf now being read/written to the disk.
// idequeue->qnext points to the next buf to be processed.
// You must hold idelock while manipulating queue.
rsc's avatar
rsc committed

rsc's avatar
rsc committed
static struct spinlock idelock;
static struct buf *idequeue;
rsc's avatar
rsc committed

rsc's avatar
rsc committed
static int havedisk1;
static void idestart(struct buf*);
rtm's avatar
rtm committed

rsc's avatar
rsc committed
// Wait for IDE disk to become ready.
rtm's avatar
rtm committed
static int
rsc's avatar
 
rsc committed
idewait(int checkerr)
rtm's avatar
rtm committed
{
kaashoek's avatar
kaashoek committed
  int r;
rtm's avatar
rtm committed

rsc's avatar
rsc committed
  while(((r = inb(0x1f7)) & (IDE_BSY|IDE_DRDY)) != IDE_DRDY) 
rsc's avatar
rsc committed
    ;
rsc's avatar
 
rsc committed
  if(checkerr && (r & (IDE_DF|IDE_ERR)) != 0)
kaashoek's avatar
kaashoek committed
    return -1;
  return 0;
rtm's avatar
rtm committed
}

kaashoek's avatar
kaashoek committed
void
ideinit(void)
kaashoek's avatar
kaashoek committed
{
rsc's avatar
rsc committed
  int i;

rsc's avatar
rsc committed
  initlock(&idelock, "ide");
  picenable(IRQ_IDE);
  ioapicenable(IRQ_IDE, ncpu - 1);
  idewait(0);
rsc's avatar
rsc committed
  
  // Check if disk 1 is present
rsc's avatar
rsc committed
  outb(0x1f6, 0xe0 | (1<<4));
rsc's avatar
rsc committed
  for(i=0; i<1000; i++){
    if(inb(0x1f7) != 0){
rsc's avatar
rsc committed
      havedisk1 = 1;
rsc's avatar
rsc committed
      break;
    }
  }
  
  // Switch back to disk 0.
rsc's avatar
rsc committed
  outb(0x1f6, 0xe0 | (0<<4));
kaashoek's avatar
kaashoek committed
}

rsc's avatar
rsc committed
// Start the request for b.  Caller must hold idelock.
rsc's avatar
rsc committed
static void
idestart(struct buf *b)
rtm's avatar
rtm committed
{
rsc's avatar
rsc committed
  if(b == 0)
    panic("idestart");
rtm's avatar
rtm committed

  idewait(0);
rsc's avatar
rsc committed
  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);
rsc's avatar
rsc committed
  outb(0x1f6, 0xe0 | ((b->dev&1)<<4) | ((b->sector>>24)&0x0f));
  if(b->flags & B_DIRTY){
rsc's avatar
rsc committed
    outb(0x1f7, IDE_CMD_WRITE);
    outsl(0x1f0, b->data, 512/4);
rsc's avatar
rsc committed
  } else {
rsc's avatar
rsc committed
    outb(0x1f7, IDE_CMD_READ);
  }
rtm's avatar
rtm committed
}

rsc's avatar
rsc committed
// Interrupt handler.
rsc's avatar
rsc committed
void
ideintr(void)
rsc's avatar
rsc committed
{
rsc's avatar
rsc committed
  struct buf *b;

rsc's avatar
rsc committed
  // Take first buffer off queue.
  acquire(&idelock);
  if((b = idequeue) == 0){
    release(&idelock);
    // cprintf("spurious IDE interrupt\n");
rsc's avatar
rsc committed
    return;
rtm's avatar
rtm committed
  }
rsc's avatar
rsc committed
  idequeue = b->qnext;
rsc's avatar
rsc committed

rsc's avatar
rsc committed
  // Read data if needed.
  if(!(b->flags & B_DIRTY) && idewait(1) >= 0)
rsc's avatar
rsc committed
    insl(0x1f0, b->data, 512/4);
  
  // Wake process waiting for this buf.
  b->flags |= B_VALID;
  b->flags &= ~B_DIRTY;
rsc's avatar
rsc committed
  wakeup(b);
  
  // Start disk on next buf in queue.
rsc's avatar
rsc committed
  if(idequeue != 0)
    idestart(idequeue);
rsc's avatar
rsc committed

rsc's avatar
rsc committed
  release(&idelock);
rsc's avatar
rsc committed
//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.
rsc's avatar
rsc committed
void
iderw(struct buf *b)
rtm's avatar
rtm committed
{
rtm's avatar
rtm committed
  struct buf **pp;
  if(!acquired_sleeplock(&b->sleeplock))
    panic("iderw: buf not busy");
  if((b->flags & (B_VALID|B_DIRTY)) == B_VALID)
    panic("iderw: nothing to do");
rsc's avatar
rsc committed
  if(b->dev != 0 && !havedisk1)
Russ Cox's avatar
Russ Cox committed
    panic("iderw: ide disk 1 not present");
kaashoek's avatar
kaashoek committed

  acquire(&idelock);  // DOC:acquire-lock
rsc's avatar
rsc committed
  // Append b to idequeue.
rtm's avatar
rtm committed
  b->qnext = 0;
  for(pp=&idequeue; *pp; pp=&(*pp)->qnext)  // DOC:insert-queue
rsc's avatar
rsc committed
    ;
rtm's avatar
rtm committed
  *pp = b;
  
rsc's avatar
rsc committed
  // Start disk if necessary.
rsc's avatar
rsc committed
  if(idequeue == b)
    idestart(b);
rtm's avatar
rtm committed
  
rsc's avatar
rsc committed
  // Wait for request to finish.
Russ Cox's avatar
Russ Cox committed
  // Assuming will not sleep too long: ignore proc->killed.
Russ Cox's avatar
Russ Cox committed
  while((b->flags & (B_VALID|B_DIRTY)) != B_VALID){
rsc's avatar
rsc committed
    sleep(b, &idelock);
rsc's avatar
rsc committed
  release(&idelock);
rtm's avatar
rtm committed
}