Charles.Forsyth20060303644188019 years agoHistory
1194 lines · 22.6 KB · C
RawDownload
#include	"u.h"#include	"../port/lib.h"#include	"mem.h"#include	"dat.h"#include	"fns.h"#include	"io.h"#include	"../port/error.h" #include	"pcmcia.h" #define DPRINT if(0)print typedef	struct Drive		Drive;typedef	struct Ident		Ident;typedef	struct Controller	Controller;typedef struct Partition	Partition;typedef struct Repl		Repl; enum{	/* ports */	Pbase=		0x1F0,	Pdata=		0,	/* data port (16 bits) */	Perror=		1,	/* error port (read) */	Pprecomp=	1,	/* buffer mode port (write) */	Pcount=		2,	/* sector count port */	Psector=	3,	/* sector number port */	Pcyllsb=	4,	/* least significant byte cylinder # */	Pcylmsb=	5,	/* most significant byte cylinder # */	Pdh=		6,	/* drive/head port */	Pstatus=	7,	/* status port (read) */	 Sbusy=		 (1<<7),	 Sready=	 (1<<6),	 Sdrq=		 (1<<3),	 Serr=		 (1<<0),	Pcmd=		7,	/* cmd port (write) */ 	/* commands */	Crecal=		0x10,	Cread=		0x20,	Cwrite=		0x30,	Cident=		0xEC,	Cident2=	0xFF,	/* pseudo command for post Cident interrupt */	Csetbuf=	0xEF,	Cinitparam=	0x91, 	/* conner specific commands */	Cstandby=	0xE2,	Cidle=		0xE1,	Cpowerdown=	0xE3, 	/* disk states */	Sspinning,	Sstandby,	Sidle,	Spowerdown, 	/* something we have to or into the drive/head reg */	DHmagic=	0xA0, 	/* file types */	Qdir=		0, 	Maxxfer=	BY2PG,		/* maximum transfer size/cmd */	Npart=		8+2,		/* 8 sub partitions, disk, and partition */	Nrepl=		64,		/* maximum replacement blocks */};#define PART(x)		((x)&0xF)#define DRIVE(x)	(((x)>>4)&0x7)#define MKQID(d,p)	(((d)<<4) | (p)) struct Partition{	ulong	start;	ulong	end;	char	name[NAMELEN+1];}; struct Repl{	Partition *p;	int	nrepl;	ulong	blk[Nrepl];}; #define PARTMAGIC	"plan9 partitions"#define REPLMAGIC	"block replacements" /* *  an ata drive */struct Drive{	QLock; 	Controller *cp;	int	drive;	int	confused;	/* needs to be recalibrated (or worse) */	int	online;	int	npart;		/* number of real partitions */	Partition p[Npart];	Repl	repl;	ulong	usetime;	int	state;	char	vol[NAMELEN]; 	ulong	cap;		/* total bytes */	int	bytes;		/* bytes/sector */	int	sectors;	/* sectors/track */	int	heads;		/* heads/cyl */	long	cyl;		/* cylinders/drive */ 	char	lba;		/* true if drive has logical block addressing */	char	multi;		/* non-zero if drive does multiple block xfers */}; /* *  a controller for 2 drives */struct Controller{	QLock;			/* exclusive access to the controller */	ISAConf;		/* interface to pcmspecial */ 	Lock	reglock;	/* exclusive access to the registers */ 	int	confused;	/* needs to be recalibrated (or worse) */	ulong	pbase;		/* base port (copied from ISAConf) */ 	/*	 *  current operation	 */	int	cmd;		/* current command */	int	lastcmd;	/* debugging info */	Rendez	r;		/* wait here for command termination */	char	*buf;		/* xfer buffer */	int	nsecs;		/* length of transfer (sectors) */	int	sofar;		/* sectors transferred so far */	int	status;	int	error;	Drive	*dp;		/* drive being accessed */}; Controller	*atac;Drive		*ata;static char*	ataerr;static int	nhard;static int	spindowntime; static void	ataintr(Ureg*, void*);static long	ataxfer(Drive*, Partition*, int, long, long, char*);static void	ataident(Drive*);static void	atasetbuf(Drive*, int);static void	ataparams(Drive*);static void	atapart(Drive*);static int	ataprobe(Drive*, int, int, int); static intatagen(Chan *c, Dirtab*, int, int s, Dir *dirp){	Qid qid;	int drive;	Drive *dp;	Partition *pp;	ulong l; 	qid.vers = 0;	drive = s/Npart;	s = s % Npart;	if(drive >= nhard)		return -1;	dp = &ata[drive]; 	if(dp->online == 0 || s >= dp->npart)		return 0; 	pp = &dp->p[s];	sprint(up->genbuf, "%s%s", dp->vol, pp->name);	qid.path = MKQID(drive, s);	l = (pp->end - pp->start) * dp->bytes;	devdir(c, qid, up->genbuf, l, eve, 0660, dirp);	return 1;} static voidatainit(void){	Drive *dp;	Controller *cp;	uchar equip;	int pcmslot; 	if (atac)		return;		/* already done */ 	equip = 0x10;		/* hard coded */ 	print("ata init\n");	cp = malloc(sizeof(*cp));	if (!cp)		error(Enomem); 	cp->port = Pbase;	cp->irq = 14; 	if((pcmslot = pcmspecial("SunDisk", cp)) < 0) {		print("No ATA card\n");		free(cp);		ataerr = Enoifc;		return;	}	ata = malloc(2 * sizeof(*ata));	if(ata == nil) {		pcmspecialclose(pcmslot);		free(cp);		error(Enomem);	} 	atac = cp;	cp->buf = 0;	cp->lastcmd = cp->cmd;	cp->cmd = 0;	cp->pbase = cp->port;	pcmintrenable(pcmslot, ataintr, cp); 	dp = ata;	if(equip & 0xf0){		dp->drive = 0;		dp->online = 0;		dp->cp = cp;		dp++;	}	if((equip & 0x0f)){		dp->drive = 1;		dp->online = 0;		dp->cp = cp;		dp++;	}	nhard = dp - ata;		spindowntime = 1;}  /* *  Get the characteristics of each drive.  Mark unresponsive ones *  off line. */static Chan*ataattach(char *spec){	Drive *dp; 	atainit();	if (!ata)		error(ataerr ? ataerr : Enoifc);	for(dp = ata; dp < &ata[nhard]; dp++){		if(waserror()){			dp->online = 0;			qunlock(dp);			continue;		}		qlock(dp);		if(!dp->online){			/*			 * Make sure ataclock() doesn't			 * interfere.			 */			dp->usetime = m->ticks;			ataparams(dp);			dp->online = 1;			atasetbuf(dp, 1);		} 		/*		 *  read Plan 9 partition table		 */		atapart(dp);		qunlock(dp);		poperror();	}	return devattach('H', spec);} static intatawalk(Chan *c, char *name){	return devwalk(c, name, 0, 0, atagen);} static voidatastat(Chan *c, char *dp){	devstat(c, dp, 0, 0, atagen);} static Chan*ataopen(Chan *c, int omode){	return devopen(c, omode, 0, 0, atagen);} static voidataclose(Chan *c){	Drive *d;	Partition *p; 	if(c->mode != OWRITE && c->mode != ORDWR)		return; 	d = &ata[DRIVE(c->qid.path)];	p = &d->p[PART(c->qid.path)];	if(strcmp(p->name, "partition") != 0)		return; 	if(waserror()){		qunlock(d);		nexterror();	}	qlock(d);	atapart(d);	qunlock(d);	poperror();} static longataread(Chan *c, void *a, long n, vlong offset){	Drive *dp;	long rv, i;	int skip;	uchar *aa = a;	Partition *pp;	char *buf; 	if(c->qid.path == CHDIR)		return devdirread(c, a, n, 0, 0, atagen); 	buf = smalloc(Maxxfer);	if(waserror()){		free(buf);		nexterror();	} 	dp = &ata[DRIVE(c->qid.path)];	pp = &dp->p[PART(c->qid.path)]; 	skip = offset % dp->bytes;	for(rv = 0; rv < n; rv += i){		i = ataxfer(dp, pp, Cread, offset+rv-skip, n-rv+skip, buf);		if(i == 0)			break;		i -= skip;		if(i > n - rv)			i = n - rv;		memmove(aa+rv, buf + skip, i);		skip = 0;	} 	free(buf);	poperror(); 	return rv;} static longatawrite(Chan *c, void *a, long n, vlong offset){	Drive *dp;	long rv, i, partial;	uchar *aa = a;	Partition *pp;	char *buf; 	if(c->qid.path == CHDIR)		error(Eisdir); 	dp = &ata[DRIVE(c->qid.path)];	pp = &dp->p[PART(c->qid.path)];	buf = smalloc(Maxxfer);	if(waserror()){		free(buf);		nexterror();	} 	/*	 *  if not starting on a sector boundary,	 *  read in the first sector before writing	 *  it out.	 */	partial = offset % dp->bytes;	if(partial){		ataxfer(dp, pp, Cread, offset-partial, dp->bytes, buf);		if(partial+n > dp->bytes)			rv = dp->bytes - partial;		else			rv = n;		memmove(buf+partial, aa, rv);		ataxfer(dp, pp, Cwrite, offset-partial, dp->bytes, buf);	} else		rv = 0; 	/*	 *  write out the full sectors	 */	partial = (n - rv) % dp->bytes;	n -= partial;	for(; rv < n; rv += i){		i = n - rv;		if(i > Maxxfer)			i = Maxxfer;		memmove(buf, aa+rv, i);		i = ataxfer(dp, pp, Cwrite, offset+rv, i, buf);		if(i == 0)			break;	} 	/*	 *  if not ending on a sector boundary,	 *  read in the last sector before writing	 *  it out.	 */	if(partial){		ataxfer(dp, pp, Cread, offset+rv, dp->bytes, buf);		memmove(buf, aa+rv, partial);		ataxfer(dp, pp, Cwrite, offset+rv, dp->bytes, buf);		rv += partial;	} 	free(buf);	poperror(); 	return rv;} /* *  did an interrupt happen? */static intcmddone(void *a){	Controller *cp = a; 	return cp->cmd == 0;} /* * Wait for the controller to be ready to accept a command. * This is protected from intereference by ataclock() by * setting dp->usetime before it is called. */static voidcmdreadywait(Drive *dp){	long start;	int period;	Controller *cp = dp->cp; 	/* give it 2 seconds to spin down and up */	if(dp->state == Sspinning)		period = 10;	else		period = 2000; 	start = m->ticks;	while((inb(cp->pbase+Pstatus) & (Sready|Sbusy)) != Sready)		if(TK2MS(m->ticks - start) > period){			DPRINT("cmdreadywait failed\n");			error(Eio);		}} static voidatarepl(Drive *dp, long bblk){	int i; 	if(dp->repl.p == 0)		return;	for(i = 0; i < dp->repl.nrepl; i++){		if(dp->repl.blk[i] == bblk)			DPRINT("found bblk %ld at offset %ld\n", bblk, i);	}} static voidatasleep(Controller *cp, int ms){        tsleep(&cp->r, cmddone, cp, ms);	if(cp->cmd && cp->cmd != Cident2){		DPRINT("ata: cmd 0x%uX timeout, status=%lux\n",				cp->cmd, inb(cp->pbase+Pstatus));		error("ata drive timeout");	}} /* *  transfer a number of sectors.  ataintr will perform all the iterative *  parts.

Lines 1–500 of 1194.