این کد واسه خوندن سکتور هاس
// DOC: http://wiki.osdev.org/ATA_PIO_Mode
#include <types.h>
#include <defs.h>
#include <param.h>
#include <memlayout.h>
#include <mmu.h>
#include <queue.h>
#include <spinlock.h>
#include <x86.h>
#include <traps.h>
#include <buf.h>
#include <semaphore.h>
#include <cpu.h>
#include <tube.h>
#include <proc.h>
#define ATA_SR_BSY 0x80
#define ATA_SR_DRDY 0x40
#define ATA_SR_DF 0x20
#define ATA_SR_DSC 0x10
#define ATA_SR_DRQ 0x08
#define ATA_SR_CORR 0x04
#define ATA_SR_IDX 0x02
#define ATA_SR_ERR 0x01
#define ATA_ER_BBK 0x80
#define ATA_ER_UNC 0x40
#define ATA_ER_MC 0x20
#define ATA_ER_IDNF 0x10
#define ATA_ER_MCR 0x08
#define ATA_ER_ABRT 0x04
#define ATA_ER_TK0NF 0x02
#define ATA_ER_AMNF 0x01
// ATA-Commands:
#define ATA_CMD_READ_PIO 0x20
#define ATA_CMD_READ_PIO_EXT 0x24
#define ATA_CMD_READ_DMA 0xC8
#define ATA_CMD_READ_DMA_EXT 0x25
#define ATA_CMD_WRITE_PIO 0x30
#define ATA_CMD_WRITE_PIO_EXT 0x34
#define ATA_CMD_WRITE_DMA 0xCA
#define ATA_CMD_WRITE_DMA_EXT 0x35
#define ATA_CMD_CACHE_FLUSH 0xE7
#define ATA_CMD_CACHE_FLUSH_EXT 0xEA
#define ATA_CMD_PACKET 0xA0
#define ATA_CMD_IDENTIFY_PACKET 0xA1
#define ATA_CMD_IDENTIFY 0xEC
#define ATAPI_CMD_READ 0xA8
#define ATAPI_CMD_EJECT 0x1B
#define ATA_IDENT_DEVICETYPE 0
#define ATA_IDENT_CYLINDERS 2
#define ATA_IDENT_HEADS 6
#define ATA_IDENT_SECTORS 12
#define ATA_IDENT_SERIAL 20
#define ATA_IDENT_MODEL 54
#define ATA_IDENT_CAPABILITIES 98
#define ATA_IDENT_FIELDVALID 106
#define ATA_IDENT_MAX_LBA 120
#define ATA_IDENT_COMMANDSETS 164
#define ATA_IDENT_MAX_LBA_EXT 200
#define ATA_MASTER 0x00
#define ATA_SLAVE 0x01
#define IDE_ATA 0x00
#define IDE_ATAPI 0x01
#define IDE_PATA 0x02
#define IDE_SATA 0x03
#define IDE_UNKNOWN 0x04
// ATA-ATAPI Task-File:
#define ATA_REG_DATA 0x00
#define ATA_REG_ERROR 0x01
#define ATA_REG_FEATURES 0x01
#define ATA_REG_SECCOUNT0 0x02
#define ATA_REG_LBA0 0x03
#define ATA_REG_LBA1 0x04
#define ATA_REG_LBA2 0x05
#define ATA_REG_HDDEVSEL 0x06
#define ATA_REG_COMMAND 0x07
#define ATA_REG_STATUS 0x07
#define ATA_REG_SECCOUNT1 0x08
#define ATA_REG_LBA3 0x09
#define ATA_REG_LBA4 0x0A
#define ATA_REG_LBA5 0x0B
#define ATA_REG_CONTROL 0x0C
#define ATA_REG_ALTSTATUS 0x0C
#define ATA_REG_DEVADDRESS 0x0D
// Channels:
#define ATA_PRIMARY 0x00
#define ATA_SECONDARY 0x01
// Directions:
#define ATA_READ 0x00
#define ATA_WRITE 0x01
struct ide_channel {
ushort base; // I/O Base.
ushort ctrl; // Control Base
ushort bmide; // Bus Master IDE
uchar nin; // nin (No Interrupt);
} idec[2];
struct ide_device {
uchar reserved; // 0 (Empty) or 1 (This Drive really exists).
uchar channel; // 0 (Primary Channel) or 1 (Secondary Channel).
uchar drive; // 0 (Master Drive) or 1 (Slave Drive).
ushort type; // 0: ATA, 1:ATAPI.
ushort sign; // Drive Signature
ushort capabilities;// Features.
uint commandsets; // Command Sets Supported.
uint size; // Size in Sectors.
uchar model[41]; // Model in string.
struct spinlock lock;
struct semaphore sem;
} idet[4];
static uchar idebuf[2048] = {0};
static struct buf *idequeue;
static int havedisk1;
static void idestart(struct buf*);
static uchar ide_eject(uchar drive);
// Wait for IDE disk to become ready.
static int
idewait(ushort base, int checkerr)
{
int r;
for(;((r = inb(base + ATA_REG_COMMAND)) & (ATA_SR_BSY|ATA_SR_DRDY)) != ATA_SR_DRDY;);
if(checkerr && (r & (ATA_SR_DF|ATA_SR_ERR)) != 0)
return -1;
return 0;
}
static uchar
idep(ushort bus, uint check) {
// Delay 400 nanosecond for BSY to be set:
// Reading Alternate Status Port wastes 100ns.
inb(bus + ATA_REG_ALTSTATUS);
inb(bus + ATA_REG_ALTSTATUS);
inb(bus + ATA_REG_ALTSTATUS);
inb(bus + ATA_REG_ALTSTATUS);
// Wait for BSY to be cleared:
while (inb(bus + ATA_REG_STATUS) & ATA_SR_BSY); // Wait for BSY to be zero.
if (check) {
uchar state = inb(bus + ATA_REG_STATUS); // Read Status Register.
// Check For Errors:
if (state & ATA_SR_ERR) return 2; // Error.
// Check If Device fault:
if (state & ATA_SR_DF ) return 1; // Device Fault.
// Check DRQ:
// BSY = 0; DF = 0; ERR = 0 so we should check for DRQ now.
if (!(state & ATA_SR_DRQ)) return 3; // DRQ should be set
}
return 0; // No Error.
}
static uchar
ierror(uint drive, uchar err) {
if (err == 0) return err;
cprintf(" IDE:");
if (err == 1) {cprintf("- Device Fault\n "); err = 19;}
else if (err == 2) {
uchar st = inb(idet[drive].channel + ATA_REG_ERROR);
if (st & ATA_ER_AMNF) { cprintf("- No Address Mark Found\n"); err = 7; }
if (st & ATA_ER_TK0NF) { cprintf("- No Media or Media Error\n"); err = 3; }
if (st & ATA_ER_ABRT) { cprintf("- Command Aborted\n"); err = 20; }
if (st & ATA_ER_MCR) { cprintf("- No Media or Media Error\n"); err = 3; }
if (st & ATA_ER_IDNF) { cprintf("- ID mark not Found\n"); err = 21; }
if (st & ATA_ER_MC) { cprintf("- No Media or Media Error\n"); err = 3; }
if (st & ATA_ER_UNC) { cprintf("- Uncorrectable Data Error\n");err = 22; }
if (st & ATA_ER_BBK) { cprintf("- Bad Sectors\n"); err = 13; }
} else if (err == 3) { cprintf("- Reads Nothing\n"); err = 23; }
else if (err == 4) { cprintf("- Write Protected\n"); err = 8; }
cprintf("- [%s %s] %s\n",
(const char *[]){"Primary","Secondary"}[idet[drive].channel],
(const char *[]){"Master", "Slave"}[idet[drive].drive],
idet[drive].model);
return err;
}
void initialize(int dev, ushort base, ushort ctrl, ushort bmi) {
int i, j, k, count, status;
uchar err = 0, type = IDE_ATA;
// 1- Detect I/O Ports which interface IDE Controller:
idec[dev].base = base;
idec[dev].ctrl = ctrl;
idec[dev].bmide = bmi; // Bus Master IDE
// 2- Disable IRQs:
outb(base + ATA_REG_CONTROL, 2);
// Assuming that no drive here.
idet[dev].reserved = 0;
// 3- Detect ATA-ATAPI Devices:
for (i = 0; i < 2; i++) {
count = dev * 2 + i;
// Switch to disk
outb(base + ATA_REG_FEATURES, 4);
outb(base + ATA_REG_HDDEVSEL , ATA_CMD_PACKET | (i<<4));
// Check if disk is present
for(j=0; j<1000; j++){
if(inb(base + ATA_REG_COMMAND) != 0){
// (VI) Read Device Parameters:
idet[count].reserved = 1;
havedisk1 = 1;
break;
}
}
if(!idet[count].reserved) continue;
outb(base + ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
while(1) {
status = inb(base + ATA_REG_STATUS);
if ( (status & ATA_SR_ERR)) {err = 1; break;} // If Err, Device is not ATA.
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ)) break; // Everything is right.
}
if (err) {
uchar cl = inb(base + ATA_REG_LBA1);
uchar ch = inb(base + ATA_REG_LBA2);
if (cl == 0x14 && ch ==0xEB) type = IDE_ATAPI;
else if (cl == 0x69 && ch ==0x96) type = IDE_ATAPI;
else if (cl == 0 && ch == 0) type = IDE_PATA;
else if (cl == 0x3C && ch == 0xC3) type = IDE_SATA;
else {type = IDE_UNKNOWN; continue; };
outb(base + ATA_REG_COMMAND, ATA_CMD_IDENTIFY_PACKET);
}
insl( base + ATA_REG_DATA, idebuf, 128);
idet[count].type = type;
idet[count].channel = dev;
idet[count].drive = i;
idet[count].sign = ((ushort *) (idebuf + ATA_IDENT_DEVICETYPE))[0];
idet[count].capabilities = ((ushort *) (idebuf + ATA_IDENT_CAPABILITIES))[0];
idet[count].commandsets = ((uint *) (idebuf + ATA_IDENT_COMMANDSETS))[0];
// Get Size:
if (idet[count].commandsets & (1<<26)){
// Device uses 48-Bit Addressing:
idet[count].size = ((uint *) (idebuf + ATA_IDENT_MAX_LBA_EXT))[0];
// Note that Quafios is 32-Bit Operating System, So last 2 Words are ignored.
} else {
// Device uses CHS or 28-bit Addressing:
idet[count].size = ((uint *) (idebuf + ATA_IDENT_MAX_LBA))[0];
}
// String indicates model of device (like Western Digital HDD and SONY DVD-RW...):
for(k = ATA_IDENT_MODEL; k < (ATA_IDENT_MODEL+40); k+=2) {
idet[count].model[k - ATA_IDENT_MODEL] = idebuf[k+1];
idet[count].model[(k+1) - ATA_IDENT_MODEL] = idebuf[k];
}
idet[count].model[40] = 0; // Terminate String.
initlock(&idet[count].lock, "ide" + count);
initsem(&idet[count].sem, "ide" + count);
}
// 4- Print Summary:
for (i = 0; i < 2; count = dev * 2 + i++)
if (idet[count].reserved == 1)
{
cprintf(" Found %s Drive %dB - %s\n",
(const char *[]){"ATA", "ATAPI", "PATA", "SATA", "UNKNOWN"}[idet[count].type], /* Type */
idet[count].size, /* Size */
idet[count].model);
}
// Switch back to disk 0.
outb(base + ATA_REG_HDDEVSEL, ATA_CMD_PACKET | (0<<4));
}
void
ideinit(void)
{
initialize(ATA_PRIMARY , 0x1F0, 0x3F6, 0);
initialize(ATA_SECONDARY, 0x170, 0x376, 8);
picenable(IRQ_IDE);
ioapicenable(IRQ_IDE, ncpu - 1);
}
// Start the request for b. Caller must hold idet[i].lock.
static void
idestart( struct buf *b)
{
if(b == 0)
panic("idestart");
uchar mode /* 0: CHS, 1:LBA28, 2: LBA48 */, dma /* 0: No DMA, 1: DMA */, cmd;
uint channel = idet[b->dev].channel; // Read the Channel.
uint slavebit = idet[b->dev].drive; // Read the Drive [Master/Slave]
uint bus = idec[channel].base; // The Bus Base, like [0x1F0] which is also data port.
uint words = 256; // Approximatly all ATA-Drives has sector-size of 512-byte.
ushort ctrl = idec[channel].ctrl;
ushort cyl;
uchar head, sect, err;
// generate interrupt
outb(bus + ATA_REG_CONTROL, idec[channel].nin = 0x02);
outb(ctrl, 0);
// Select one from LBA28, LBA48 or CHS;
// Sure Drive should support LBA in this case, or you are giving a wrong LBA.
if (b->sector >= 0x10000000) {
// LBA48:
mode = 2;
// Lower 4-bits of HDDEVSEL are not used here.
head = 0;
} else if (idet[b->dev].capabilities & 0x200) { // Drive supports LBA?
// LBA28:
mode = 1;
head = (b->sector & 0xF000000)>>24;
} else {
// CHS:
mode = 0;
sect = (b->sector % 63) + 1;
cyl = (b->sector + 1 - sect)/(16*63);
// Head number is written to HDDEVSEL lower 4-bits.
head = (b->sector + 1 - sect)%(16*63)/(63);
}
// See if Drive Supports DMA or not;
dma = 0; // Supports or doesn't, we don't support !!!
idewait(bus, 0);
// Select Drive from the controller;
if (mode == 0) outb(bus + ATA_REG_HDDEVSEL, 0xA0 | (slavebit<<4) | head); // Select Drive CHS.
else outb(bus + ATA_REG_HDDEVSEL, 0xE0 | (slavebit<<4) | head); // Select Drive LBA.
// Write Parameters;
if (mode == 2) {
outb(bus + ATA_REG_SECCOUNT1, 0);
outb(bus + ATA_REG_LBA3, (b->sector & 0xFF000000)>>24);
// We said that we lba is integer, so 32-bit are enough to access 2TB.
outb(bus + ATA_REG_LBA4, 0);
// We said that we lba is integer, so 32-bit are enough to access 2TB.
outb(bus + ATA_REG_LBA5, 0);
}
if (mode == 0) {
outb(bus + ATA_REG_SECCOUNT0, 1);
outb(bus + ATA_REG_LBA0, sect);
outb(bus + ATA_REG_LBA1, (cyl>>0) & 0xFF);
outb(bus + ATA_REG_LBA2, (cyl>>8) & 0xFF);
} else {
outb(bus + ATA_REG_SECCOUNT0, 1);
outb(bus + ATA_REG_LBA0, (b->sector & 0x000000FF)>> 0);
outb(bus + ATA_REG_LBA1, (b->sector & 0x0000FF00)>> 8);
outb(bus + ATA_REG_LBA2, (b->sector & 0x00FF0000)>>16);
}
int direction = b->flags & B_DIRTY;
// !DMA
if (dma == 0 && direction == 0) cmd = (mode == 2)?ATA_CMD_READ_PIO_EXT:ATA_CMD_READ_PIO;
if (dma == 0 && direction == 1) cmd = (mode == 2)?ATA_CMD_WRITE_PIO_EXT:ATA_CMD_WRITE_PIO;
// DMA
if (dma == 1 && direction == 0) cmd = (mode == 2)?ATA_CMD_READ_DMA_EXT:ATA_CMD_READ_DMA;
if (dma == 1 && direction == 1) cmd = (mode == 2)?ATA_CMD_WRITE_DMA_EXT:ATA_CMD_WRITE_DMA;
// Send the Command.
outb(bus + ATA_REG_COMMAND, cmd);
err = ((err = idep(channel, 1))==3)? 0 : err;
ierror(slavebit , err);
/* DMA - Command byte:
DOC:http://wiki.osdev.org/ATA/ATAPI_using_DMA
Function: 0x1F1 on the Primary bus
0xC8 Read DMA (28 bit LBA)
0x25 Read DMA (48 bit LBA)
0xCA Write DMA (28 bit LBA)
0x35 Write DMA (48 bit LBA)
*/
if (dma)
if (direction);
// DMA Write.
else; // DMA Read.
else
if (direction)
// PIO Write.
outsl(bus, b->data, words);
else; // PIO Read.
}
/* 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 idet.lock while manipulating queue.
* Interrupt handler.
*/
void
ideintr(struct trapframe *tf)
{
struct buf *b;
// First queued buffer is the active request.
if((b = idequeue) == 0){
LOG("spurious IDE interrupt\n");
return;
}
sem_wait(&idet[b->dev].sem);
uint channel = idet[b->dev].channel; // Read the Channel.
uint bus = idec[channel].base;
idequeue = b->qnext;
// Read data if needed.
if(!(b->flags & B_DIRTY) && idewait(bus, 1) >= 0)
insl(bus, 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(idequeue != 0)
idestart(idequeue);
sem_signal(&idet[b->dev].sem);
}
/* 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
iderw(struct buf *b)
{
struct buf **pp;
if(!(b->flags & B_BUSY))
panic("iderw: buf not busy");
if((b->flags & (B_VALID|B_DIRTY)) == B_VALID)
panic("iderw: nothing to do");
if(b->dev != 0 && !havedisk1)
panic("iderw: ide disk 1 not present");
acquire(&idet[b->dev].lock); //DOC:acquire-lock
// Append b to idequeue.
b->qnext = 0;
for(pp=&idequeue; *pp; pp=&(*pp)->qnext); //DOC:insert-queue
*pp = b;
// Start disk if necessary.
if(idequeue == b)
idestart(b);
// Wait for request to finish.
for(;(b->flags & (B_VALID|B_DIRTY)) != B_VALID; sleep(b, &idet[b->dev].lock));
release(&idet[b->dev].lock);
}
static uchar
ide_eject(uchar drive) {
uint channel = idet[drive].channel;
uint slavebit = idet[drive].drive;
uint bus = idec[channel].base;
uchar err = 0;
// 1: Check if the drive presents:
if (drive > 3 || idet[drive].reserved == 0) return 0x1; // Drive Not Found!
// 2: Check if drive isn't ATAPI:
else if (idet[drive].type == IDE_ATA) return 20; // Command Aborted.
// 3: Eject ATAPI Driver:
else {
// Enable IRQs:
outb(bus + ATA_REG_CONTROL, (idec[channel].nin = 0x0));
// Select the Drive:
outb(bus + ATA_REG_HDDEVSEL, slavebit<<4);
idewait(bus, 1);
// Send the Packet Command:
// Send the Command.
outb(bus + ATA_REG_COMMAND, ATA_CMD_PACKET);
// Waiting for the driver to finish or invoke an error:
// Polling and stop if error.
if ((err = idep(channel, 1)));
// Sending the packet data:
// Send command Data
outb(bus + ATA_REG_COMMAND, ATAPI_CMD_EJECT);
err = idep(channel, 1); // Polling and get error code.
if (err == 3) err = 0; // DRQ is not needed here.
ierror(drive, err); // Return;
}
}