#include "u.h"
#include "lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "errno.h"
typedef struct Drive Drive;
typedef struct Ident Ident;
typedef struct Controller Controller;
enum
{
/* ports */
Pbase= 0x10,
Pdata= Pbase+0, /* data port (16 bits) */
Perror= Pbase+1, /* error port */
Pcount= Pbase+2, /* sector count port */
Psector= Pbase+3, /* sector number port */
Pcyllsb= Pbase+4, /* least significant byte cylinder # */
Pcylmsb= Pbase+5, /* most significant byte cylinder # */
Pdh= Pbase+6, /* drive/head port */
Pstatus= Pbase+7, /* status port */
Pcmd= Pbase+7, /* cmd port */
/* commands */
Crecal= 0x10,
Cread= 0x20,
Cwrite= 0x30,
Cident= 0xEC,
/* file types */
Qdir= 0,
Qdata= (1<<1),
Qstruct= (2<<1),
Qmask= (3<<1),
};
/*
* ident sector from drive
*/
struct Ident
{
ushort magic; /* must be 0x0A5A */
ushort lcyls; /* logical number of cylinders */
ushort rcyl; /* number of removable cylinders */
ushort lheads; /* logical number of heads */
ushort b2t; /* unformatted bytes/track */
ushort b2s; /* unformated bytes/sector */
ushort ls2t; /* logical sectors/track */
ushort gap; /* bytes in inter-sector gaps */
ushort sync; /* bytes in sync fields */
ushort magic2; /* must be 0x0000 */
ushort serial[10]; /* serial number */
ushort type; /* controller type (0x0003) */
ushort bsize; /* buffer size/512 */
ushort ecc; /* ecc bytes returned by read long */
ushort firm[4]; /* firmware revision */
ushort model[20]; /* model number */
ushort s2i; /* number of sectors/interrupt */
ushort dwtf; /* double word transfer flag */
ushort alernate;
ushort piomode;
ushort dmamode;
ushort reserved[76];
ushort ncyls; /* native number of cylinders */
ushort nheads; /* native number of heads, sectors */
ushort dlcyls; /* default logical number of cyinders */
ushort dlheads; /* default logical number of heads, sectors */
ushort interface;
ushort power; /* 0xFFFF if power commands supported */
ushort flags;
ushort ageprog; /* MSB = age, LSB = program */
ushort reserved2[120];
};
/*
* a hard drive
*/
struct Drive
{
int dev;
ulong cap; /* drive capacity */
int bytes; /* bytes/sector */
int cyl; /* current cylinder */
int confused; /* needs to be recalibrated (or worse) */
int tcyl; /* target cylinder */
int thead; /* target head */
int tsec; /* target sector */
long len; /* size of xfer */
Ident id;
};
/*
* a controller for 2 drives
*/
struct Controller
{
QLock; /* exclusive access to the drive */
int intr; /* true if interrupt occured */
Rendez r; /* wait here for command termination */
int confused; /* needs to be recalibrated (or worse) */
Drive d[2];
};
Controller hard;
Dirtab harddir[]={
"hddata", {Qdata}, 0, 0600,
"hdstruct", {Qstruct}, 8, 0600,
};
#define NHDIR (sizeof(harddir)/sizeof(Dirtab))
static void hardintr(Ureg*);
static long hardxfer(Drive*, int, void*, long, long);
static long hardident(Drive*);
void
hardreset(void)
{
hard.d[0].dev = 0;
hard.d[1].dev = 1;
setvec(Hardvec, hardintr);
}
void
hardinit(void)
{
}
Chan*
hardattach(char *spec)
{
return devattach('f', spec);
}
Chan*
hardclone(Chan *c, Chan *nc)
{
return devclone(c, nc);
}
int
hardwalk(Chan *c, char *name)
{
return devwalk(c, name, harddir, NHDIR, devgen);
}
void
hardstat(Chan *c, char *dp)
{
devstat(c, dp, harddir, NHDIR, devgen);
}
Chan*
hardopen(Chan *c, int omode)
{
return devopen(c, omode, harddir, NHDIR, devgen);
}
void
hardcreate(Chan *c, char *name, int omode, ulong perm)
{
error(Eperm);
}
void
hardclose(Chan *c)
{
}
void
hardremove(Chan *c)
{
error(Eperm);
}
void
hardwstat(Chan *c, char *dp)
{
error(Eperm);
}
static void
ul2user(uchar *a, ulong x)
{
a[0] = x >> 24;
a[1] = x >> 16;
a[2] = x >> 8;
a[3] = x;
}
long
hardread(Chan *c, void *a, long n)
{
Drive *dp;
long rv, i;
uchar *aa = a;
if(c->qid.path == CHDIR)
return devdirread(c, a, n, harddir, NHDIR, devgen);
rv = 0;
dp = &hard.d[c->qid.path & ~Qmask];
switch ((int)(c->qid.path & Qmask)) {
case Qdata:
for(rv = 0; rv < n; rv += i){
i = hardxfer(dp, Fread, aa+rv, c->offset+rv, n-rv);
if(i <= 0)
break;
}
break;
case Qstruct:
hardident(dp);
dp->cap = 512 * dp->id->lcyls * dp->id->lheads *dp->id->ls2t;
dp->bytes = 512;
if (n < 2*sizeof(ulong))
error(Ebadarg);
if (c->offset >= 2*sizeof(ulong))
return 0;
rv = 2*sizeof(ulong);
ul2user((uchar*)a, dp->cap);
ul2user((uchar*)a+sizeof(ulong), dp->bytes);
break;
default:
panic("hardread: bad qid");
}
return rv;
}
long
hardwrite(Chan *c, void *a, long n)
{
Drive *dp;
long rv, i;
uchar *aa = a;
rv = 0;
dp = &hard.d[c->qid.path & ~Qmask];
switch ((int)(c->qid.path & Qmask)) {
case Qdata:
for(rv = 0; rv < n; rv += i){
i = hardxfer(dp, Fwrite, aa+rv, c->offset+rv, n-rv);
if(i <= 0)
break;
}
break;
case Qstruct:
error(Eperm);
break;
default:
panic("hardwrite: bad qid");
}
return rv;
}
/*
* did an interrupt happen?
*/
static int
interrupted(void *a)
{
return hard.intr;
}
/*
* get parameters from the drive
*/
static long
hardident(Drive *dp)
{
qlock(&hard);
hard.intr = 0;
outb(Pdh, dp->dev<<4);
outb(Pcmd, Cident);
sleep(&hard.r, interrupted, 0);
insw(Pdata, &hard.id, 512);
qunlock(&hard);
}
static long
hardxfer(Drive *dp, int cmd, void *va, long off, long len)
{
errors("not implemented");
}
/*
* take/clear a disk interrupt
*/
static void
hardintr(Ureg *ur)
{
hard.status = inb(Pstatus);
hard.intr = 1;
print("hardintr\n");
wakeup(&hard.r);
}