📄 hd.c
字号:
// Kernel
// Default Driver IDE
#include <string.h>
#include <memory.h>
#include <gates.h>
#include <knlsched.h>
#include <knlcon.h>
#include <knllib.h>
#include <devices.h>
#include <vfs.h>
#include "hd.h"
#define IDE_TIMEOUT 5000
#define DR_CMDMASK 0x0000000f
#define DR_READ 0x00000001
#define DR_WRITE 0x00000002
#define MAX_CMDBYTES 16
#define DEF_SECTORSIZE 512
#define DEF_CACHELIMIT 64
typedef struct _DEVREQUEST
{
HDEVICE device;
_u32 buffer;
_u32 blocks;
_u32 flags;
_u32 errcode;
int bytes; // cmd bytes
int ports[MAX_CMDBYTES]; // cmd ports
int values[MAX_CMDBYTES]; // cmd data
void *data; // external data
} DEVREQUEST;
extern void irq_hd(void);
static HDRIVER _this_driver;
static DRIVE _hdd_drives[4]; // 0x80-0x83
static REQUESTQUEUE _hdd_requestqueue;
static int wait_ide(int pri_sec)
{
_u32 ticks;
int r;
ticks = GetSystemTicks();
while(((r=_inb(pri_sec+HDPA_STATUS))&0x80) && GetSystemTicks()-ticks<IDE_TIMEOUT);
return r;
}
static int ide_ready(int pri_sec)
{
_u32 ticks;
int r;
ticks = GetSystemTicks();
while((r=_inb(pri_sec+HDPA_STATUS)&0xc0)!=0x40 && GetSystemTicks()-ticks<IDE_TIMEOUT);
return r;
}
static void reset_ide(int pri_sec)
{
_outb(pri_sec+HDPA_CMD, 0x08);
wait_ide(pri_sec);
}
static inline void reset_drive(int pri_sec, int drv)
{
reset_ide(pri_sec);
_outb(pri_sec+HDPA_HEAD, (0xa0|(drv<<4)));
_outb(pri_sec+HDPA_CMD, 0x91);
wait_ide(pri_sec);
}
static inline int lba_to_chs(int id, _u32 sector, _u32 *cyl, _u32 *head, _u32 *sec)
{
if(!DRIVE_VALID(_hdd_drives[id])) return -1;
if(sec) *sec = (sector+1)%_hdd_drives[id].n_sectors;
if(head) *head = ((sector+1)/_hdd_drives[id].n_sectors)%_hdd_drives[id].n_heads;
if(cyl) *cyl = ((sector+1)/_hdd_drives[id].n_sectors)/_hdd_drives[id].n_heads;
return 0;
}
static inline _u32 chs_to_lba(int id, _u32 cyl, _u32 head, _u32 sec)
{
if(!DRIVE_VALID(_hdd_drives[id])) return -1;
return (cyl*_hdd_drives[id].n_heads+head)*_hdd_drives[id].n_sectors+sec-1;
}
static int hdd_read(int id, _u32 addr, void* buf, int nr)
{
_u32 c, h, s;
int chs, port, r;
if(!DRIVE_VALID(_hdd_drives[id])) return HDE_INVALID;
if(!(_hdd_drives[id].capbilities&HDC_LBA))
{
if(lba_to_chs(id, addr, &c, &h, &s)!=0) return HDE_ADDRESS;
if(c>=_hdd_drives[id].n_cylinders || h>=_hdd_drives[id].n_heads
|| s>_hdd_drives[id].n_sectors) return HDE_ADDRESS;
chs = 1;
}
else
{
if(addr>=_hdd_drives[id].n_LBAsectors) return HDE_ADDRESS;
chs = 0;
}
port = _hdd_drives[id].port_base;
reset_drive(port, _hdd_drives[id].drive_id);
r = ide_ready(port);
if(r!=0x40) return HDE_READY|r;
_outb(++port, 0);
_outb(++port, nr);
if(chs)
{
_outb(++port, s);
_outb(++port, c);
_outb(++port, (c>>8));
_outb(++port, (0xa0|(_hdd_drives[id].drive_id<<4)|h));
_outb(++port, 0x20);
}
else
{
_outb(++port, addr);
_outb(++port, (addr>>8));
_outb(++port, (addr>>16));
_outb(++port, (0xe0|(_hdd_drives[id].drive_id<<4)|((addr>>24)&0x0f)));
_outb(++port, 0x20);
}
port = _hdd_drives[id].port_base;
// Wait until job is done
if((wait_ide(port)&0x58)!=0x58)
{
int r = _inb(port+HDPA_ERR)&0x00ff;
reset_drive(port, _hdd_drives[id].drive_id);
return IDE_TIMEOUT|r;
}
// Read data
_insw(port, buf, nr*256);
return 0;
}
static int build_cmds(DEVREQUEST *req, _u32 sector, int cmd)
{
_u32 c, h, s;
int chs, port;
HDDEVICE *hddev = (HDDEVICE*)GetDeviceExtraMem(req->device);
if(sector>=hddev->length) return HDE_ADDRESS;
if(req->blocks>hddev->length-sector) req->blocks = hddev->length-sector;
sector+=hddev->base;
if(!(_hdd_drives[hddev->drive].capbilities&HDC_LBA))
{
if(lba_to_chs(hddev->drive, sector, &c, &h, &s)!=0) return HDE_ADDRESS;
if(c>=_hdd_drives[hddev->drive].n_cylinders
|| h>=_hdd_drives[hddev->drive].n_heads
|| s>_hdd_drives[hddev->drive].n_sectors) return HDE_ADDRESS;
chs = 1;
}
else
{
if(sector>=_hdd_drives[hddev->drive].n_LBAsectors) return HDE_ADDRESS;
chs = 0;
}
port = _hdd_drives[hddev->drive].port_base;
req->ports[0] = ++port; req->values[0] = 0;
req->ports[1] = ++port; req->values[1] = req->blocks;
if(chs)
{
req->ports[2] = ++port; req->values[2] = s;
req->ports[3] = ++port; req->values[3] = c;
req->ports[4] = ++port; req->values[4] = c>>8;
req->ports[5] = ++port; req->values[5] = 0xa0|(_hdd_drives[hddev->drive].drive_id<<4)|h;
}
else
{
req->ports[2] = ++port; req->values[2] = sector;
req->ports[3] = ++port; req->values[3] = sector>>8;
req->ports[4] = ++port; req->values[4] = sector>>16;
req->ports[5] = ++port; req->values[5] = 0xe0|(_hdd_drives[hddev->drive].drive_id<<4)|((sector>>24)&0x0f);
}
req->ports[6] = ++port; req->values[6] = cmd;
req->bytes = 7;
return 0;
}
struct buf_request
{
BUFFERITEM *pItem;
HDEVICE hDev;
int op;
int result;
};
static long __syscall hd_queue_proc(REQUESTQUEUE *pQueue, _u32 cmd, _u32 param1, _u32 param2)
{
switch(cmd)
{
case RQ_CREATEREQUEST:
{
REQUEST *r = (REQUEST*)param1;
struct buf_request *pbr = (struct buf_request*)param2;
DEVREQUEST *req = rqGetRequestExtraMem(r);
KASSERT(req, "HD RQ_CREATEREQUEST: null RequestExtraMem");
req->device = pbr->hDev;
req->buffer = (_u32)pbr->pItem->buffer;
req->blocks = GetDeviceBufferList(pbr->hDev)->item_size/DEF_SECTORSIZE;
req->flags = pbr->op;
req->errcode = HDE_NOTDONE;
req->data = pbr;
pbr->result = build_cmds(req, pbr->pItem->base, pbr->op==DR_READ?0x20:0x30);
return pbr->result==0 ? ERR_SUCCESS : ERR_DEVICE;
}
break;
case RQ_PENDREQUEST:
{
REQUEST *r = (REQUEST*)param1;
DEVREQUEST *req = (DEVREQUEST*)param2;
HDDEVICE *hddev;
int n;
KASSERT(req, "HD RQ_PENDREQUEST: null RequestExtraMem");
hddev = GetDeviceExtraMem(req->device);
KASSERT(hddev, "HD RQ_PENDREQUEST: null DeviceExtraMem");
SysLock();
// Reset drive
EnableIRQ(0x0e, 0);
reset_drive(_hdd_drives[hddev->drive].port_base,
_hdd_drives[hddev->drive].drive_id);
n = ide_ready(_hdd_drives[hddev->drive].port_base);
if(n!=0x40)
{
req->errcode = HDE_READY|n;
r->status = RS_COMPLETE;
EnableIRQ(0x0e, 1);
SysUnlock();
return req->errcode;
}
// Write commands, HD interrupt should be masked
for(n=0; n<req->bytes; n++)
_outb(req->ports[n], req->values[n]);
EnableIRQ(0x0e, 1);
SysUnlock();
}
break;
case RQ_SUBMIT:
{
DEVREQUEST *req = rqGetRequestExtraMem((REQUEST*)param1);
KASSERT(req, "HD RQ_SUBMIT: null req");
KASSERT(req->data, "HD RQ_SUBMIT: null req->data");
((struct buf_request*)req->data)->result = req->errcode;
}
break;
}
return 0;
}
static long __syscall hd_buffers_proc(BUFFERLIST *pList, _u32 cmd, _u32 param1, _u32 param2)
{
switch(cmd)
{
case BL_CREATE:
{
HDEVICE hDev = (HDEVICE)param2;
HDDEVICE *pDev = GetDeviceExtraMem(hDev);
KASSERT2(pDev, "HD BL_CREATE: null DeviceExtraMem (handle: %08x, name: %s", hDev, hDev->caption);
*(HDEVICE*)param1 = hDev;
pList->flags = BLF_FIXED | BLF_CACHELIMIT;
pList->item_size = /*(_hdd_drives[pDev->drive].n_sectors+1) */ DEF_SECTORSIZE;
pList->cache_size = DEF_CACHELIMIT;
}
break;
case BL_DESTROY: break;
case BL_LOAD:
case BL_FLUSH:
{
struct buf_request r;
REQUEST *p;
r.hDev = *(HDEVICE*)bufsGetExtraMem(pList);
r.pItem = (BUFFERITEM*)param1;
r.op = cmd==BL_LOAD ? DR_READ : DR_WRITE;
p = rqNewRequest(&_hdd_requestqueue, (_u32)&r);
if(!p) return DEVICE_MAKEERR(DEVERR_PARAMS);
SysLock();
rqAdd(p);
RescheduleReleaseLock();
// after taskWakeup ...
rqFree(p);
return r.result;
}
break;
}
return 0;
}
static long doTransfer(HDEVICE dev, DDTPACKET *pack, _u32 *len, int dr_cmd)
{
BUFFERLIST *pList;
BUFFERITEM *pItem;
HDDEVICE *pDev;
_u32 pointer, base, offset, byteoff;
char *buffer;
long l0, l1, ret;
int l;
pList = GetDeviceBufferList(dev);
pDev = GetDeviceExtraMem(dev);
KASSERT(pList, "doTransfer: null pList");
KASSERT(pDev, "doTransfer: null pDev");
KASSERT1(pDev->drive>=0 && pDev->drive<4, "doTransfer: invalid drive %d", pDev->drive);
pointer = pack->pointer; l0 = pack->blocks * DEF_SECTORSIZE; l1 = 0;
buffer = (char*)pack->buffer;
if(len) *len = 0;
while(l0>0)
{
KASSERT(pList->item_size, "doTransfer: pList->item_size = 0");
offset = pointer%(pList->item_size/DEF_SECTORSIZE);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -