Plan 9 from Bell Labs’s /usr/web/sources/extra/9hist/pc/devata.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


## diffname pc/devata.c 1995/0213
## diff -e /dev/null /n/fornaxdump/1995/0213/sys/src/brazil/pc/devata.c
0a
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"../port/error.h"

#include	"devtab.h"

#define DPRINT if(1)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,

	/* 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 */

	Lock;			/* exclusive access to the registers */

	int	confused;	/* needs to be recalibrated (or worse) */
	int	pbase;		/* base port */

	/*
	 *  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 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 int
atagen(Chan *c, Dirtab *tab, long ntab, long s, Dir *dirp)
{
	Qid qid;
	int drive;
	char name[NAMELEN+4];
	Drive *dp;
	Partition *pp;
	ulong l;

	USED(tab, ntab);
	qid.vers = 0;
	drive = s/Npart;
	s = s % Npart;
	if(drive >= conf.nhard)
		return -1;
	dp = &ata[drive];

	if(s >= dp->npart)
		return 0;

	pp = &dp->p[s];
	sprint(name, "%s%s", dp->vol, pp->name);
	name[NAMELEN] = 0;
	qid.path = MKQID(drive, s);
	l = (pp->end - pp->start) * dp->bytes;
	devdir(c, qid, name, l, eve, 0660, dirp);
	return 1;
}

/*
 *  we assume drives 0 and 1 are on the first controller, 2 and 3 on the
 *  second, etc.
 */
void
atareset(void)
{
	Drive *dp;
	Controller *cp;
	int drive;
	uchar equip;
	char *p;

	ata = xalloc(conf.nhard * sizeof(Drive));
	atac = xalloc(((conf.nhard+1)/2) * sizeof(Controller));
	
	/*
	 *  read nvram for number of ata drives (2 max)
	 */
	equip = nvramread(0x12);
	if(conf.nhard > 0 && (equip>>4) == 0)
		conf.nhard = 0;
	if(conf.nhard > 1 && (equip&0xf) == 0)
		conf.nhard = 1;
	if(conf.nhard > 2)
		conf.nhard = 2;

	for(drive = 0; drive < conf.nhard; drive++){
		dp = &ata[drive];
		cp = &atac[drive/2];
		dp->drive = drive&1;
		dp->online = 0;
		dp->cp = cp;
		if((drive&1) == 0){
			cp->buf = 0;
			cp->lastcmd = cp->cmd;
			cp->cmd = 0;
			cp->pbase = Pbase;
			setvec(Hardvec, ataintr, 0);
		}
	}
	
	if(conf.nhard && (p = getconf("spindowntime")))
		spindowntime = atoi(p);
}

void
atainit(void)
{
}

/*
 *  Get the characteristics of each drive.  Mark unresponsive ones
 *  off line.
 */
Chan*
ataattach(char *spec)
{
	Drive *dp;

	for(dp = ata; dp < &ata[conf.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);
}

Chan*
ataclone(Chan *c, Chan *nc)
{
	return devclone(c, nc);
}

int
atawalk(Chan *c, char *name)
{
	return devwalk(c, name, 0, 0, atagen);
}

void
atastat(Chan *c, char *dp)
{
	devstat(c, dp, 0, 0, atagen);
}

Chan*
ataopen(Chan *c, int omode)
{
	return devopen(c, omode, 0, 0, atagen);
}

void
atacreate(Chan *c, char *name, int omode, ulong perm)
{
	USED(c, name, omode, perm);
	error(Eperm);
}

void
ataclose(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();
}

void
ataremove(Chan *c)
{
	USED(c);
	error(Eperm);
}

void
atawstat(Chan *c, char *dp)
{
	USED(c, dp);
	error(Eperm);
}

long
ataread(Chan *c, void *a, long n, ulong 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;
}

Block*
atabread(Chan *c, long n, ulong offset)
{
	return devbread(c, n, offset);
}

long
atawrite(Chan *c, void *a, long n, ulong 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;
}

long
atabwrite(Chan *c, Block *bp, ulong offset)
{
	return devbwrite(c, bp, offset);
}

/*
 *  did an interrupt happen?
 */
static int
cmddone(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 void
cmdreadywait(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 void
atarepl(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);
	}
}

/*
 *  transfer a number of sectors.  ataintr will perform all the iterative
 *  parts.
 */
static long
ataxfer(Drive *dp, Partition *pp, int cmd, long start, long len, char *buf)
{
	Controller *cp;
	long lblk;
	int cyl, sec, head;
	int loop, stat;

	if(dp->online == 0)
		error(Eio);

	/*
	 *  cut transfer size down to disk buffer size
	 */
	start = start / dp->bytes;
	if(len > Maxxfer)
		len = Maxxfer;
	len = (len + dp->bytes - 1) / dp->bytes;
	if(len == 0)
		return 0;

	/*
	 *  calculate physical address
	 */
	lblk = start + pp->start;
	if(lblk >= pp->end)
		return 0;
	if(lblk+len > pp->end)
		len = pp->end - lblk;
	if(dp->lba){
		sec = lblk & 0xff;
		cyl = (lblk>>8) & 0xffff;
		head = (lblk>>24) & 0xf;
	} else {
		cyl = lblk/(dp->sectors*dp->heads);
		sec = (lblk % dp->sectors) + 1;
		head = ((lblk/dp->sectors) % dp->heads);
	}

	cp = dp->cp;
	qlock(cp);
	if(waserror()){
		cp->buf = 0;
		qunlock(cp);
		nexterror();
	}

	/*
	 * Make sure hardclock() doesn't
	 * interfere.
	 */
	dp->usetime = m->ticks;
	cmdreadywait(dp);

	ilock(cp);
	cp->sofar = 0;
	cp->buf = buf;
	cp->nsecs = len;
	cp->cmd = cmd;
	cp->dp = dp;
	cp->status = 0;

	outb(cp->pbase+Pcount, cp->nsecs);
	outb(cp->pbase+Psector, sec);
	outb(cp->pbase+Pdh, 0x20 | (dp->drive<<4) | (dp->lba<<6) | head);
	outb(cp->pbase+Pcyllsb, cyl);
	outb(cp->pbase+Pcylmsb, cyl>>8);
	outb(cp->pbase+Pcmd, cmd);

	if(cmd == Cwrite){
		loop = 0;
		while((stat = inb(cp->pbase+Pstatus) & (Serr|Sdrq)) == 0)
			if(++loop > 10000)
				panic("ataxfer");
		outss(cp->pbase+Pdata, cp->buf, dp->bytes/2);
	} else
		stat = 0;
	iunlock(cp);

	if(stat & Serr)
		error(Eio);

	/*
	 *  wait for command to complete.  if we get a note,
	 *  remember it but keep waiting to let the disk finish
	 *  the current command.
	 */
	loop = 0;
	while(waserror()){
		DPRINT("interrupted ataxfer\n");
		if(loop++ > 10){
			print("ata disk error\n");
			nexterror();
		}
	}
	sleep(&cp->r, cmddone, cp);
	dp->state = Sspinning;
	dp->usetime = m->ticks;
	poperror();
	if(loop)
		nexterror();

	if(cp->status & Serr){
		DPRINT("hd%d err: lblk %ld status %lux, err %lux\n",
			dp-ata, lblk, cp->status, cp->error);
		DPRINT("\tcyl %d, sec %d, head %d\n", cyl, sec, head);
		DPRINT("\tnsecs %d, sofar %d\n", cp->nsecs, cp->sofar);
		atarepl(dp, lblk+cp->sofar);
		error(Eio);
	}
	cp->buf = 0;
	len = cp->sofar*dp->bytes;
	qunlock(cp);
	poperror();

	return len;
}

/*
 *  set read ahead mode
 */
static void
atasetbuf(Drive *dp, int on)
{
	Controller *cp = dp->cp;

	qlock(cp);
	if(waserror()){
		qunlock(cp);
		nexterror();
	}

	cmdreadywait(dp);

	ilock(cp);
	cp->cmd = Csetbuf;
	outb(cp->pbase+Pprecomp, on ? 0xAA : 0x55);	/* read look ahead */
	outb(cp->pbase+Pdh, 0x20 | (dp->drive<<4));
	outb(cp->pbase+Pcmd, Csetbuf);
	iunlock(cp);

	sleep(&cp->r, cmddone, cp);

/*	if(cp->status & Serr)
		DPRINT("hd%d setbuf err: status %lux, err %lux\n",
			dp-ata, cp->status, cp->error);/**/

	poperror();
	qunlock(cp);
}

/*
 *  ident sector from drive.  this is from ANSI X3.221-1994
 */
struct Ident
{
	ushort	config;		/* general configuration info */
	ushort	cyls;		/* # of cylinders (default) */
	ushort	reserved0;
	ushort	heads;		/* # of heads (default) */
	ushort	b2t;		/* unformatted bytes/track */
	ushort	b2s;		/* unformated bytes/sector */
	ushort	s2t;		/* sectors/track (default) */
	ushort	reserved1[3];
/* 10 */
	ushort	serial[10];	/* serial number */
	ushort	type;		/* buffer type */
	ushort	bsize;		/* buffer size/512 */
	ushort	ecc;		/* ecc bytes returned by read long */
	ushort	firm[4];	/* firmware revision */
	ushort	model[20];	/* model number */
/* 47 */
	ushort	s2i;		/* number of sectors/interrupt */
	ushort	dwtf;		/* double word transfer flag */
	ushort	capabilities;
	ushort	reserved2;
	ushort	piomode;
	ushort	dmamode;
	ushort	cvalid;		/* (cvald&1) if next 4 words are valid */
	ushort	ccyls;		/* current # cylinders */
	ushort	cheads;		/* current # heads */
	ushort	cs2t;		/* current sectors/track */
	ushort	ccap[2];	/* current capacity in sectors */
	ushort	cs2i;		/* current number of sectors/interrupt */
/* 60 */
	ushort	lbasecs[2];	/* # LBA user addressable sectors */
	ushort	dmasingle;
	ushort	dmadouble;
/* 64 */
	ushort	reserved3[64];
	ushort	vendor[32];	/* vendor specific */
	ushort	reserved4[96];
};

/*
 *  get parameters from the drive
 */
static void
ataident(Drive *dp)
{
	Controller *cp;
	char *buf;
	Ident *ip;
	char id[21];

	cp = dp->cp;
	buf = smalloc(Maxxfer);
	qlock(cp);
	if(waserror()){
		qunlock(cp);
		nexterror();
	}

	cmdreadywait(dp);

	ilock(cp);
	cp->nsecs = 1;
	cp->sofar = 0;
	cp->cmd = Cident;
	cp->dp = dp;
	cp->buf = buf;
	outb(cp->pbase+Pdh, 0x20 | (dp->drive<<4));
	outb(cp->pbase+Pcmd, Cident);
	iunlock(cp);

	sleep(&cp->r, cmddone, cp);
	if(cp->status & Serr){
		DPRINT("bad disk ident status\n");
		error(Eio);
	}
	ip = (Ident*)buf;

	/*
	 * this function appears to respond with an extra interrupt after
	 * the ident information is read, except on the safari.  The following
	 * delay gives this extra interrupt a chance to happen while we are quiet.
	 * Otherwise, the interrupt may come during a subsequent read or write,
	 * causing a panic and much confusion.
	 */
	if (cp->cmd == Cident2)
		tsleep(&cp->r, return0, 0, 10);

	memmove(id, ip->model, sizeof(id)-1);
	id[sizeof(id)-1] = 0;

	if(ip->capabilities & (1<<9)){
		dp->lba = 1;
		dp->sectors = (ip->lbasecs[0]) | (ip->lbasecs[1]<<16);
		dp->cap = dp->bytes * dp->sectors;
		print("ata%d model %s with %d lba sectors\n", dp->drive,
			id, dp->sectors);
	} else {
		dp->lba = 0;

		/* use default (unformatted) settings */
		dp->cyl = ip->cyls;
		dp->heads = ip->heads;
		dp->sectors = ip->s2t;
		print("ata%d model %s with default %d cyl %d head %d sec\n", dp->drive,
			id, dp->cyl, dp->heads, dp->sectors);

		if(ip->cvalid&(1<<0)){
			/* use current settings */
			dp->cyl = ip->ccyls;
			dp->heads = ip->cheads;
			dp->sectors = ip->cs2t;
			print("\tchanged to %d cyl %d head %d sec\n", dp->cyl,
				dp->heads, dp->sectors);
		}
		dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors;
	}
	cp->lastcmd = cp->cmd;
	cp->cmd = 0;
	cp->buf = 0;
	free(buf);
	poperror();
	qunlock(cp);
}

/*
 *  probe the given sector to see if it exists
 */
static int
ataprobe(Drive *dp, int cyl, int sec, int head)
{
	Controller *cp;
	char *buf;
	int rv;

	cp = dp->cp;
	buf = smalloc(Maxxfer);
	qlock(cp);
	if(waserror()){
		free(buf);
		qunlock(cp);
		nexterror();
	}

	cmdreadywait(dp);

	ilock(cp);
	cp->cmd = Cread;
	cp->dp = dp;
	cp->status = 0;
	cp->nsecs = 1;
	cp->sofar = 0;

	outb(cp->pbase+Pcount, 1);
	outb(cp->pbase+Psector, sec+1);
	outb(cp->pbase+Pdh, 0x20 | head | (dp->drive<<4));
	outb(cp->pbase+Pcyllsb, cyl);
	outb(cp->pbase+Pcylmsb, cyl>>8);
	outb(cp->pbase+Pcmd, Cread);
	iunlock(cp);

	sleep(&cp->r, cmddone, cp);

	if(cp->status & Serr)
		rv = -1;
	else
		rv = 0;

	cp->buf = 0;
	free(buf);
	poperror();
	qunlock(cp);
	return rv;
}

/*
 *  figure out the drive parameters
 */
static void
ataparams(Drive *dp)
{
	int i, hi, lo;

	/*
	 *  first try the easy way, ask the drive and make sure it
	 *  isn't lying.
	 */
	dp->bytes = 512;
	ataident(dp);
	if(dp->lba){
		i = dp->sectors - 1;
		if(ataprobe(dp, (i>>8)&0xffff, i&0xff, (i>>24)&0xf) == 0)
			return;
	} else {
		if(ataprobe(dp, dp->cyl-1, dp->sectors-1, dp->heads-1) == 0)
			return;
	}

	/*
	 *  the drive lied, determine parameters by seeing which ones
	 *  work to read sectors.
	 */
print("ata%d lied.  probing...\n", dp->drive);
	dp->lba = 0;
	for(i = 0; i < 32; i++)
		if(ataprobe(dp, 0, 0, i) < 0)
			break;
	dp->heads = i;
	for(i = 0; i < 128; i++)
		if(ataprobe(dp, 0, i, 0) < 0)
			break;
	dp->sectors = i;
	for(i = 512; ; i += 512)
		if(ataprobe(dp, i, dp->sectors-1, dp->heads-1) < 0)
			break;
	lo = i - 512;
	hi = i;
	for(; hi-lo > 1;){
		i = lo + (hi - lo)/2;
		if(ataprobe(dp, i, dp->sectors-1, dp->heads-1) < 0)
			hi = i;
		else
			lo = i;
	}
	dp->cyl = lo + 1;
	dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors;
}

/*
 *  Read block replacement table.
 *  The table is just ascii block numbers.
 */
static void
atareplinit(Drive *dp)
{
	char *line[Nrepl+1];
	char *field[1];
	ulong n;
	int i;
	char *buf;

	/*
	 *  check the partition is big enough
	 */
	if(dp->repl.p->end - dp->repl.p->start < Nrepl+1){
		dp->repl.p = 0;
		return;
	}

	buf = smalloc(Maxxfer);
	if(waserror()){
		free(buf);
		nexterror();
	}

	/*
	 *  read replacement table from disk, null terminate
	 */
	ataxfer(dp, dp->repl.p, Cread, 0, dp->bytes, buf);
	buf[dp->bytes-1] = 0;

	/*
	 *  parse replacement table.
	 */
	n = getfields(buf, line, Nrepl+1, "\n");
	if(strncmp(line[0], REPLMAGIC, sizeof(REPLMAGIC)-1)){
		dp->repl.p = 0;
	} else {
		for(dp->repl.nrepl = 0, i = 1; i < n; i++, dp->repl.nrepl++){
			if(getfields(line[i], field, 1, " ") != 1)
				break;
			dp->repl.blk[dp->repl.nrepl] = strtoul(field[0], 0, 0);
			if(dp->repl.blk[dp->repl.nrepl] <= 0)
				break;
		}
	}
	free(buf);
	poperror();
}

/*
 *  read partition table.  The partition table is just ascii strings.
 */
static void
atapart(Drive *dp)
{
	Partition *pp;
	char *line[Npart+1];
	char *field[3];
	ulong n;
	int i;
	char *buf;

	sprint(dp->vol, "hd%d", dp - ata);

	/*
	 *  we always have a partition for the whole disk
	 *  and one for the partition table
	 */
	pp = &dp->p[0];
	strcpy(pp->name, "disk");
	pp->start = 0;
	pp->end = dp->cap / dp->bytes;
	pp++;
	strcpy(pp->name, "partition");
	pp->start = dp->p[0].end - 1;
	pp->end = dp->p[0].end;
	dp->npart = 2;

	/*
	 * initialise the bad-block replacement info
	 */
	dp->repl.p = 0;

	buf = smalloc(Maxxfer);
	if(waserror()){
		free(buf);
		nexterror();
	}

	/*
	 *  read last sector from disk, null terminate.  This used
	 *  to be the sector we used for the partition tables.
	 *  However, this sector is special on some PC's so we've
	 *  started to use the second last sector as the partition
	 *  table instead.  To avoid reconfiguring all our old systems
	 *  we first look to see if there is a valid partition
	 *  table in the last sector.  If so, we use it.  Otherwise
	 *  we switch to the second last.
	 */
	ataxfer(dp, pp, Cread, 0, dp->bytes, buf);
	buf[dp->bytes-1] = 0;
	n = getfields(buf, line, Npart+1, "\n");
	if(n == 0 || strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1)){
		dp->p[0].end--;
		dp->p[1].start--;
		dp->p[1].end--;
		ataxfer(dp, pp, Cread, 0, dp->bytes, buf);
		buf[dp->bytes-1] = 0;
		n = getfields(buf, line, Npart+1, "\n");
	}

	/*
	 *  parse partition table.
	 */
	if(n && strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1) == 0){
		for(i = 1; i < n; i++){
			pp++;
			switch(getfields(line[i], field, 3, " ")) {
			case 2:
				if(strcmp(field[0], "unit") == 0)
					strncpy(dp->vol, field[1], NAMELEN);
				break;	
			case 3:
				strncpy(pp->name, field[0], NAMELEN);
				if(strncmp(pp->name, "repl", NAMELEN) == 0)
					dp->repl.p = pp;
				pp->start = strtoul(field[1], 0, 0);
				pp->end = strtoul(field[2], 0, 0);
				if(pp->start > pp->end || pp->start >= dp->p[0].end)
					break;
				dp->npart++;
			}
		}
	}
	free(buf);
	poperror();

	if(dp->repl.p)
		atareplinit(dp);
}

/*
 *  we get an interrupt for every sector transferred
 */
static void
ataintr(Ureg *ur, void *arg)
{
	Controller *cp;
	Drive *dp;
	long loop;
	char *addr;

	USED(ur, arg);

	/*
 	 *  BUG!! if there is ever more than one controller, we need a way to
	 *	  distinguish which interrupted (use arg).
	 */
	cp = &atac[0];
	dp = cp->dp;

	ilock(cp);

	loop = 0;
	while((cp->status = inb(cp->pbase+Pstatus)) & Sbusy){
		if(++loop > 100) {
			DPRINT("cmd=%lux status=%lux\n",
				cp->cmd, inb(cp->pbase+Pstatus));
			panic("ataintr: wait busy");
		}
	}

	switch(cp->cmd){
	case Cwrite:
		if(cp->status & Serr){
			cp->lastcmd = cp->cmd;
			cp->cmd = 0;
			cp->error = inb(cp->pbase+Perror);
			wakeup(&cp->r);
			break;
		}
		cp->sofar++;
		if(cp->sofar < cp->nsecs){
			loop = 0;
			while(((cp->status = inb(cp->pbase+Pstatus)) & Sdrq) == 0)
				if(++loop > 100) {
					DPRINT("cmd=%lux status=%lux\n",
						cp->cmd, inb(cp->pbase+Pstatus));
					panic("ataintr: write");
				}
			addr = cp->buf;
			if(addr){
				addr += cp->sofar*dp->bytes;
				outss(cp->pbase+Pdata, addr, dp->bytes/2);
			}
		} else{
			cp->lastcmd = cp->cmd;
			cp->cmd = 0;
			wakeup(&cp->r);
		}
		break;
	case Cread:
	case Cident:
		loop = 0;
		while((cp->status & (Serr|Sdrq)) == 0){
			if(++loop > 10000) {
				DPRINT("cmd=%lux status=%lux\n",
					cp->cmd, inb(cp->pbase+Pstatus));
				panic("ataintr: read/ident");
			}
			cp->status = inb(cp->pbase+Pstatus);
		}
		if(cp->status & Serr){
			cp->lastcmd = cp->cmd;
			cp->cmd = 0;
			cp->error = inb(cp->pbase+Perror);
			wakeup(&cp->r);
			break;
		}
		addr = cp->buf;
		if(addr){
			addr += cp->sofar*dp->bytes;
			inss(cp->pbase+Pdata, addr, dp->bytes/2);
		}
		cp->sofar++;
		if(cp->sofar > cp->nsecs)
			print("ataintr %d %d\n", cp->sofar, cp->nsecs);
		if(cp->sofar >= cp->nsecs){
			cp->lastcmd = cp->cmd;
			if (cp->cmd == Cread)
				cp->cmd = 0;
			else
				cp->cmd = Cident2;
			wakeup(&cp->r);
		}
		break;
	case Cinitparam:
	case Csetbuf:
	case Cidle:
	case Cstandby:
	case Cpowerdown:
		cp->lastcmd = cp->cmd;
		cp->cmd = 0;
		wakeup(&cp->r);
		break;
	case Cident2:
		cp->lastcmd = cp->cmd;
		cp->cmd = 0;
		break;
	default:
		print("weird disk interrupt, cmd=%.2ux, lastcmd= %.2ux status=%.2ux\n",
			cp->cmd, cp->lastcmd, cp->status);
		break;
	}

	iunlock(cp);
}

void
hardclock(void)
{
	int drive;
	Drive *dp;
	Controller *cp;
	int diff;

	if(spindowntime <= 0)
		return;

	for(drive = 0; drive < conf.nhard; drive++){
		dp = &ata[drive];
		cp = dp->cp;

		diff = TK2SEC(m->ticks - dp->usetime);
		if((dp->state == Sspinning) && (diff >= spindowntime)){
			ilock(cp);
			cp->cmd = Cstandby;
			outb(cp->pbase+Pcount, 0);
			outb(cp->pbase+Pdh, 0x20 | (dp->drive<<4) | 0);
			outb(cp->pbase+Pcmd, cp->cmd);
			iunlock(cp);
			dp->state = Sstandby;
		}
	}
}
.
## diffname pc/devata.c 1995/0214
## diff -e /n/fornaxdump/1995/0213/sys/src/brazil/pc/devata.c /n/fornaxdump/1995/0214/sys/src/brazil/pc/devata.c
1180c
			iunlock(&cp->reglock);
.
1175c
			ilock(&cp->reglock);
.
1155c
	iunlock(&cp->reglock);
.
1061c
	ilock(&cp->reglock);
.
828c
	iunlock(&cp->reglock);
.
815c
	ilock(&cp->reglock);
.
738c
	iunlock(&cp->reglock);
.
730c
	ilock(&cp->reglock);
.
654c
	iunlock(&cp->reglock);
.
649c
	ilock(&cp->reglock);
.
592c
	iunlock(&cp->reglock);
.
569c
	ilock(&cp->reglock);
.
121c
	Lock	reglock;	/* exclusive access to the registers */
.
## diffname pc/devata.c 1995/0215
## diff -e /n/fornaxdump/1995/0214/sys/src/brazil/pc/devata.c /n/fornaxdump/1995/0215/sys/src/brazil/pc/devata.c
1178c
			outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4) | 0);
.
1105c
			if(++loop > Maxloop) {
.
1085c
				if(++loop > Maxloop) {
.
1065c
		if(++loop > Maxloop) {
.
1040a
enum
{
	Maxloop=	10000,
};

.
871c
print("ata%d probing...\n", dp->drive);
.
860c
		if(ataprobe(dp, (i>>8)&0xffff, (i&0xff)-1, (i>>24)&0xf) == 0)
.
824c
	outb(cp->pbase+Pdh, DHmagic | head | (dp->lba<<6) | (dp->drive<<4));
.
781,782c
/*print("\tchanged to %d cyl %d head %d sec\n", dp->cyl, dp->heads, dp->sectors);/**/
.
773,774c
/*print("\nata%d model %s with default %d cyl %d head %d sec\n", dp->drive,
			id, dp->cyl, dp->heads, dp->sectors);/**/
.
764,765c
print("\nata%d model %s with %d lba sectors\n", dp->drive, id, dp->sectors);
.
736c
	outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4));
.
652c
	outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4));
.
579c
	outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4) | (dp->lba<<6) | head);
.
222a
	if((equip & 0x0f)){
		dp->drive = 1;
		dp->online = 0;
		dp->cp = cp;
		dp++;
	}
	conf.nhard = dp - ata;
.
215,221c
		dp++;
.
209,212c
	ata = xalloc(2 * sizeof(Drive));
	atac = xalloc(sizeof(Controller));

	cp = atac;
	cp->buf = 0;
	cp->lastcmd = cp->cmd;
	cp->cmd = 0;
	cp->pbase = Pbase;
	setvec(Hardvec, ataintr, 0);

	dp = ata;
	if(equip & 0xf0){
		dp->drive = 0;
.
202,207c
	if(equip == 0)
		equip = 0x10;		/* the Globalyst 250 lies */
.
195,200d
191d
182,185d
170c
	if(dp->online == 0 || s >= dp->npart)
.
57a
	/* something we have to or into the drive/head reg */
	DHmagic=	0xA0,

.
11c
#define DPRINT if(0)print
.
## diffname pc/devata.c 1995/0222
## diff -e /n/fornaxdump/1995/0215/sys/src/brazil/pc/devata.c /n/fornaxdump/1995/0222/sys/src/brazil/pc/devata.c
762c
/*print("\nata%d model %s with %d lba sectors\n", dp->drive, id, dp->sectors);/**/
.
## diffname pc/devata.c 1995/0726
## diff -e /n/fornaxdump/1995/0222/sys/src/brazil/pc/devata.c /n/fornaxdump/1995/0726/sys/src/brazil/pc/devata.c
295d
293c
atacreate(Chan*, char*, int, ulong)
.
## diffname pc/devata.c 1995/0728
## diff -e /n/fornaxdump/1995/0726/sys/src/brazil/pc/devata.c /n/fornaxdump/1995/0728/sys/src/brazil/pc/devata.c
1122a
		} else {
			/* old drives/controllers hang unless the buffer is emptied */
			for(loop = dp->bytes/2; --loop >= 0;)
				ins(cp->pbase+Pdata);
.
815a
	cp->buf = buf;
.
## diffname pc/devata.c 1995/0818
## diff -e /n/fornaxdump/1995/0728/sys/src/brazil/pc/devata.c /n/fornaxdump/1995/0818/sys/src/brazil/pc/devata.c
1124,1127d
1109c
				cp->status |= Serr;
				break;
.
1106,1107c
			if(++loop > Maxloop){
				print("ataintr: read/ident cmd=%lux status=%lux\n",
.
1089,1090d
1086,1087c
				if(++loop > Maxloop)
					panic("ataintr: write cmd=%lux status=%lux\n",
.
1069,1070d
1066,1067c
		if(++loop > Maxloop)
			panic("ataintr: wait busy cmd=%lux status=%lux",
.
1039c
	Maxloop=	1000000,
.
867d
826c
	atasleep(cp);
.
759c
		dp->sectors = lbasecs;
.
757c
	lbasecs = (ip->lbasecs[0]) | (ip->lbasecs[1]<<16);
	if((ip->capabilities & (1<<9)) && (lbasecs & 0xf0000000) == 0){
.
752c
		tsleep(&cp->r, return0, 0, Hardtimeout);
.
737c
	atasleep(cp);

.
715a
	ulong lbasecs;
.
653c
	atasleep(cp);
.
607c
	atasleep(cp);
.
550a
/*print("ataxfer cyl %d sec %d head %d\n", cyl, sec, head);/**/

.
507a
static void
atasleep(Controller *cp)
{
	tsleep(&cp->r, cmddone, cp, Hardtimeout);
	if(cp->cmd && cp->cmd != Cident2){
		DPRINT("hard drive timeout\n");
		error("ata drive timeout");
	}
}


.
294a
	USED(c, name, omode, perm);
.
293c
atacreate(Chan *c, char *name, int omode, ulong perm)
.
204,205c
	cp->pbase = Pbase0;
	setvec(ATAvec0, ataintr, 0);
.
66a

	Hardtimeout=	4000,		/* disk access timeout */
.
22c
	Pbase0=		0x1F0,
	Pbase1=		0x170,
.
## diffname pc/devata.c 1995/0908
## diff -e /n/fornaxdump/1995/0818/sys/src/brazil/pc/devata.c /n/fornaxdump/1995/0908/sys/src/brazil/pc/devata.c
892c
	for(i = 0; i < 64; i++)
.
888c
	for(i = 0; i < 16; i++)
.
## diffname pc/devata.c 1995/1206
## diff -e /n/fornaxdump/1995/0908/sys/src/brazil/pc/devata.c /n/fornaxdump/1995/1206/sys/src/brazil/pc/devata.c
1199a
	}
}

static int
isatapi(Drive *dp)
{
	Controller *cp;

	cp = dp->cp;
	outb(cp->pbase+Pdh, dp->dh);
	DPRINT("%s: isatapi %d\n", dp->vol, dp->atapi);
	if(dp->atapi){
		outb(cp->pbase+Pcmd, 0x08);
		delay(20);
	}
	dp->atapi = 0;
	dp->bytes = 512;
	microdelay(1);
	if(inb(cp->pbase+Pstatus)){
		DPRINT("%s: isatapi status %ux\n", dp->vol, inb(cp->pbase+Pstatus));
		return 0;
	}
	if(inb(cp->pbase+Pcylmsb) != 0xEB || inb(cp->pbase+Pcyllsb) != 0x14){
		DPRINT("%s: isatapi cyl %ux %ux\n",
			dp->vol, inb(cp->pbase+Pcylmsb), inb(cp->pbase+Pcyllsb));
		return 0;
	}
	dp->atapi = 1;
	sprint(dp->vol, "atapi%d", dp->driveno);
	dp->spindown = 0;
	spindownmask &= ~(1<<dp->driveno);
	return 1;
}

static void
atapiexec(Drive *dp)
{
	Controller *cp;
	int loop;

	cp = dp->cp;

	if(cmdreadywait(dp)){
		error(Eio);
	}
	
	ILOCK(&cp->reglock);
	cp->nsecs = 1;
	cp->sofar = 0;
	cp->error = 0;
	cp->cmd = Cpktcmd;
	outb(cp->pbase+Pcount, 0);
	outb(cp->pbase+Psector, 0);
	outb(cp->pbase+Pfeature, 0);
	outb(cp->pbase+Pcyllsb, cp->len);
	outb(cp->pbase+Pcylmsb, cp->len>>8);
	outb(cp->pbase+Pdh, dp->dh);
	outb(cp->pbase+Pcmd, cp->cmd);

	if(dp->drqintr == 0){
		microdelay(1);
		for(loop = 0; (inb(cp->pbase+Pstatus) & (Serr|Sdrq)) == 0; loop++){
			if(loop < 10000)
				continue;
			panic("%s: cmddrqwait: cmd=%lux status=%lux\n",
				dp->vol, cp->cmd, inb(cp->pbase+Pstatus));
		}
		outss(cp->pbase+Pdata, cp->cmdblk, sizeof(cp->cmdblk)/2);
	}
	IUNLOCK(&cp->reglock);

	loop = 0;
	while(waserror()){
		DPRINT("%s: interrupted atapiexec\n", dp->vol);
		if(loop++ > 10){
			print("%s: disk error\n", dp->vol);
			nexterror();
		}
	}
	atasleep(cp);
	poperror();
	if(loop)
		nexterror();

	if(cp->status & Serr){
		DPRINT("%s: Bad packet command %ux\n", dp->vol, cp->error);
		error(Eio);
	}
}

static long
atapiio(Drive *dp, char *a, ulong len, ulong offset)
{
	ulong bn, n, o, m;
	Controller *cp;
	uchar *buf;
	int retrycount;

	cp = dp->cp;

	buf = smalloc(Maxxfer);
	qlock(cp->ctlrlock);
	retrycount = 1;
retry:
	if(waserror()){
		dp->partok = 0;
		if((cp->status & Serr) && (cp->error & 0xF0) == 0x60){
			dp->vers++;
			if(retrycount){
				retrycount--;
				goto retry;
			}
		}
		cp->dp = 0;
		free(buf);
		qunlock(cp->ctlrlock);
		nexterror();
	}

	cp->buf = buf;
	cp->dp = dp;
	cp->len = dp->bytes;

	n = len;
	while(n > 0){
		bn = offset / dp->bytes;
		if(offset > dp->cap-dp->bytes)
			break;
		o = offset % dp->bytes;
		m = dp->bytes - o;
		if(m > n)
			m = n;
		memset(cp->cmdblk, 0, 12);
		cp->cmdblk[0] = Cread2;
		cp->cmdblk[2] = bn >> 24;
		cp->cmdblk[3] = bn >> 16;
		cp->cmdblk[4] = bn >> 8;
		cp->cmdblk[5] = bn;
		cp->cmdblk[7] = 0;
		cp->cmdblk[8] = 1;
		atapiexec(dp);
		if(cp->count != dp->bytes){
			print("short read\n");
			break;
		}
		memmove(a, cp->buf + o, m);
		n -= m;
		offset += m;
		a += m;
	}
	poperror();
	free(buf);
	cp->dp = 0;
	qunlock(cp->ctlrlock);
	return len-n;
}

static long
atapirwio(Chan *c, char *a, ulong len, ulong offset)
{
	Drive *dp;
	ulong vers;
	long rv;

	dp = atadrive[DRIVE(c->qid.path)];

	qlock(dp);
	if(waserror()){
		qunlock(dp);
		nexterror();
	}

	vers = c->qid.vers;
	c->qid.vers = dp->vers;
	if(vers && vers != dp->vers)
		error(Eio);
	rv = atapiio(dp, a, len, offset);

	poperror();
	qunlock(dp);
	return rv;
}

static void
atapipart(Drive *dp)
{
	Controller *cp;
	uchar *buf, err;
	Partition *pp;
	int retrycount;

	cp = dp->cp;

	pp = &dp->p[0];
	strcpy(pp->name, "disk");
	pp->start = 0;
	pp->end = 0;
	dp->npart = 1;

	buf = smalloc(Maxxfer);
	qlock(cp->ctlrlock);
	retrycount = 1;
retry:
	if(waserror()){
		if((cp->status & Serr) && (cp->error & 0xF0) == 0x60){
			dp->vers++;
			if(retrycount){
				retrycount--;
				goto retry;
			}
		}
		cp->dp = 0;
		free(buf);
		if((cp->status & Serr) && (cp->error & 0xF0) == 0x20)
			err = cp->error;
		else
			err = 0;
		qunlock(cp->ctlrlock);
		if(err == 0x20)
			return;
		nexterror();
	}

	cp->buf = buf;
	cp->dp = dp;

	cp->len = 8;
	cp->count = 0;
	memset(cp->cmdblk, 0, sizeof(cp->cmdblk));
	cp->cmdblk[0] = Ccapacity;
	atapiexec(dp);
	if(cp->count != 8){
		print("cmd=%2.2uX, lastcmd=%2.2uX ", cp->cmd, cp->lastcmd);
		print("cdsize count %d, status 0x%2.2uX, error 0x%2.2uX\n",
			cp->count, cp->status, cp->error);
		error(Eio);
	}
	dp->lbasecs = (cp->buf[0]<<24)|(cp->buf[1]<<16)|(cp->buf[2]<<8)|cp->buf[3];
	dp->cap = dp->lbasecs*dp->bytes;
	cp->dp = 0;
	free(cp->buf);
	poperror();
	qunlock(cp->ctlrlock);

	pp->end = dp->cap / dp->bytes;
	dp->partok = 1;
}

static void
atapiintr(Controller *cp)
{
	uchar cause;
	int count, loop, pbase;
	uchar *addr;

	pbase = cp->pbase;
	cause = inb(pbase+Pcount) & 0x03;
	DPRINT("%s: atapiintr %uX\n", cp->dp->vol, cause);
	switch(cause){

	case 0:						/* data out */
		cp->status |= Serr;
		/*FALLTHROUGH*/
	case 1:						/* command */
		if(cp->status & Serr){
			cp->lastcmd = cp->cmd;
			cp->cmd = 0;
			cp->error = inb(pbase+Perror);
			wakeup(&cp->r); 
			break;
		}
		outss(pbase+Pdata, cp->cmdblk, sizeof(cp->cmdblk)/2);
		break;

	case 2:						/* data in */
		addr = cp->buf;
		if(addr == 0){
			cp->lastcmd = cp->cmd;
			cp->cmd = 0;
			if(cp->status & Serr)
				cp->error = inb(pbase+Perror);
			wakeup(&cp->r);	 
			break;	
		}
		loop = 0;
		while((cp->status & (Serr|Sdrq)) == 0){
			if(++loop > Maxloop){
				cp->status |= Serr;
				break;
			}
			cp->status = inb(pbase+Pstatus);
		}
		if(cp->status & Serr){
			cp->lastcmd = cp->cmd;
			cp->cmd = 0;
			cp->error = inb(pbase+Perror);
			print("%s: Cpktcmd status=%uX, error=%uX\n",
				cp->dp->vol, cp->status, cp->error);
			wakeup(&cp->r);
			break;
		}
		count = inb(pbase+Pcyllsb)|(inb(pbase+Pcylmsb)<<8);
		if (count > Maxxfer) 
			count = Maxxfer;
		inss(pbase+Pdata, addr, count/2);
		cp->count = count;
		cp->lastcmd = cp->cmd; 
		break;

	case 3:						/* status */
		cp->lastcmd = cp->cmd;
		cp->cmd = 0;
		if(cp->status & Serr)
			cp->error = inb(cp->pbase+Perror);
		wakeup(&cp->r);	
		break;
.
1197c
			IUNLOCK(&cp->reglock);
.
1195c
			outb(cp->pbase+Pdh, dp->dh);
.
1191,1192c
		if((dp->state == Sspinning) && (diff >= dp->spindown)){
			DPRINT("%s: spindown\n", dp->vol);
			ILOCK(&cp->reglock);
.
1186,1187c
	for(driveno = 0; driveno < NDrive && mask; driveno++){
		mask &= ~(1<<driveno);
		if((dp = atadrive[driveno]) == 0)
			continue;
.
1183c
	if((mask = spindownmask) == 0)
.
1178c
	int driveno, mask;
.
1172c
	IUNLOCK(&cp->reglock);
.
1167,1168c
		if(cp->cmd == 0 && cp->lastcmd == Cpktcmd && cp->cmdblk[0] == Ccapacity)
			break;
		if(cp->status & Serr)
			cp->error = inb(cp->pbase+Perror);
		print("%s: weird interrupt, cmd=%.2ux, lastcmd=%.2ux, ",
			dp->vol, cp->cmd, cp->lastcmd);
		print("status=%.2ux, error=%.2ux, count=%.2ux\n",
			cp->ctlrno, cp->error, inb(cp->pbase+Pcount));
.
1165a
	case Cpktcmd:
		atapiintr(cp);
		break;
.
1157c
	case Cedd:
.
1153,1155c
	case Cfeature:
.
1149a
			else if(cp->cmd == Cident && (cp->status & Sready) == 0)
				cp->cmd = Cident2;
			else
				cp->cmd = 0;
			inb(cp->pbase+Pstatus);
			DPRINT("status %uX, alt %uX\n",
				inb(cp->pbase+Pstatus), inb(cp->pbase+0x206));
.
1146,1148c
			if(cp->cmd == Cidentd)
.
1143c
			print("%s: intr %d %d\n", dp->vol, cp->sofar, cp->nsecs);
.
1122,1123c
				print("%s: read/ident cmd=%lux status=%lux\n",
					dp->vol, cp->cmd, inb(cp->pbase+Pstatus));
.
1118a
	case Cidentd:
.
1104,1105c
					panic("%s: write cmd=%lux status=%lux\n",
						dp->vol, cp->cmd, inb(cp->pbase+Pstatus));
.
1085,1087c
		if(++loop > Maxloop){
			print("ata%d: cmd=%lux, lastcmd=%lux status=%lux\n",
				cp->ctlrno, cp->cmd, cp->lastcmd, inb(cp->pbase+Pstatus));
			panic("%s: wait busy\n", dp->vol);
		}
.
1081,1082d
1074,1079c
	ILOCK(&cp->reglock);
.
1072c
	cp = arg;
	if((dp = cp->dp) == 0 && cp->cmd != Cedd)
		return;
.
1069,1070c
	int loop;
	uchar *addr;
.
1065c
ataintr(Ureg*, void* arg)
.
1051a
	dp->partok = 1;

.
1048a
	dp->npart = pp - dp->p;
.
1032,1046d
1030a
			if(getfields(line[i], field, 3, " ") != 3)
				break;
			if(pp >= &dp->p[Npart])
				break;
			strncpy(pp->name, field[0], NAMELEN);
			if(strncmp(pp->name, "repl", NAMELEN) == 0)
				dp->repl.p = pp;
			pp->start = strtoul(field[1], 0, 0);
			pp->end = strtoul(field[2], 0, 0);
			if(pp->start > pp->end || pp->start >= dp->p[0].end)
				break;
.
1029c
	if(n > 0 && strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1) == 0){
.
1024a
	else{
		strcpy((char*)buf, p);
		n = getfields((char*)buf, line, Npart+1, "\n");
	}
.
1023c
		n = getfields((char*)buf, line, Npart+1, "\n");
		if(n == 0 || strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1)){
			dp->p[0].end--;
			dp->p[1].start--;
			dp->p[1].end--;
			ataxfer(dp, &dp->p[1], Cread, 0, dp->bytes, buf);
			buf[dp->bytes-1] = 0;
			n = getfields((char*)buf, line, Npart+1, "\n");
		}
.
1014,1021c
	sprint(namebuf, "%spartition", dp->vol);
	if((p = getconf(namebuf)) == 0){	
		/*
		 *  read last sector from disk, null terminate.  This used
		 *  to be the sector we used for the partition tables.
		 *  However, this sector is special on some PC's so we've
		 *  started to use the second last sector as the partition
		 *  table instead.  To avoid reconfiguring all our old systems
		 *  we first look to see if there is a valid partition
		 *  table in the last sector.  If so, we use it.  Otherwise
		 *  we switch to the second last.
		 */
		ataxfer(dp, &dp->p[1], Cread, 0, dp->bytes, buf);
.
1005,1012c
	 * Check if the partitions are described in plan9.ini.
	 * If not, read the disc.
.
1003a

.
999a
		DPRINT("%s: atapart error\n", dp->vol);
.
990a
	pp++;
.
978a
	if(dp->partok)
		return;

.
977c
	DPRINT("%s: partok %d\n", dp->vol, dp->partok);
.
975c
	uchar *buf;
.
972c
	char *field[3], namebuf[NAMELEN], *p;
.
948c
	n = getfields((char*)buf, line, Nrepl+1, "\n");
.
923c
	uchar *buf;
.
909a
	DPRINT("%s: probed: %d/%d/%d CHS %d bytes\n",
		dp->vol, dp->cyl, dp->heads, dp->sectors, dp->cap);
	if(dp->cyl == 0 || dp->heads == 0 || dp->sectors == 0)
		error(Eio);
.
875c
		i = dp->lbasecs - 1;
.
873a
	if(dp->atapi)
		return;
.
856c
	qunlock(cp->ctlrlock);
.
852a
	cp->dp = 0;
.
849a
	}
.
848c
	if(cp->status & Serr){
		DPRINT("%s: probe err: status %lux, err %lux\n",
			dp->vol, cp->status, cp->error);
.
844c
	IUNLOCK(&cp->reglock);
.
840c
	outb(cp->pbase+Pdh, dp->dh | (dp->lba<<6) | head);
.
830c
	ILOCK(&cp->reglock);
.
828c
	if(cmdreadywait(dp)){
		error(Eio);
	}
.
824c
		qunlock(cp->ctlrlock);
.
822a
		cp->dp = 0;
.
821c
	qlock(cp->ctlrlock);
.
816c
	uchar *buf;
.
806c
	qunlock(cp->ctlrlock);
.
801,802c

	if(cp->cmd){
		cp->lastcmd = cp->cmd;
		cp->cmd = 0;
	}
	cp->dp = 0;
.
784,798c
		dp->lbasecs = 0;
.
779,781c
		dp->lbasecs = lbasecs;
		dp->cap = dp->bytes * dp->lbasecs;
		DPRINT("%s: LBA: %s %d sectors %d bytes\n",
			dp->vol, id, dp->lbasecs, dp->cap);
.
775a
	DPRINT("%s: config 0x%uX capabilities 0x%uX\n",
		dp->vol, ip->config, ip->capabilities);
	if(dp->atapi){
		dp->bytes = 2048;
		if((ip->config & 0x0060) == 0x0020)
			dp->drqintr = 1;
	}
	if(dp->spindown && (ip->capabilities & (1<<13)))
		dp->spindown /= 5;

	/* use default (unformatted) settings */
	dp->cyl = ip->cyls;
	dp->heads = ip->heads;
	dp->sectors = ip->s2t;
	DPRINT("%s: %s %d/%d/%d CHS %d bytes\n",
		dp->vol, id, dp->cyl, dp->heads, dp->sectors, dp->cap);

	if(ip->cvalid&(1<<0)){
		/* use current settings */
		dp->cyl = ip->ccyls;
		dp->heads = ip->cheads;
		dp->sectors = ip->cs2t;
		DPRINT("%s: changed to %d cyl %d head %d sec\n",
			dp->vol, dp->cyl, dp->heads, dp->sectors);
	}

.
758c
		DPRINT("%s: bad disk ident status\n", dp->vol);
		if(dp->atapi == 0 && (cp->error & Eabort)){
			dp->atapi = 1;
			if(isatapi(dp)){
				cmd = Cidentd;
				goto retryatapi;
			}
		}
.
751,753c
	outb(cp->pbase+Pdh, dp->dh);
	outb(cp->pbase+Pcmd, cmd);
	IUNLOCK(&cp->reglock);
.
748c
	cp->cmd = cmd;
.
745c
retryatapi:
	ILOCK(&cp->reglock);
.
743c
	if(dp->atapi || isatapi(dp))
		cmd = Cidentd;
	else{
		cmd = Cident;
		if(cmdreadywait(dp)){
			dp->atapi = 1;
			if(isatapi(dp) == 0)
				error(Eio);
			cmd = Cidentd;
		}
	}
.
739c
		cp->dp = 0;
		free(buf);
		qunlock(cp->ctlrlock);
.
737c
	qlock(cp->ctlrlock);
.
733a
	uchar cmd;
.
730c
	uchar *buf;
.
677c
	qunlock(cp->ctlrlock);
.
675a
	cp->dp = 0;
.
672,674c
	if(cp->status & Serr)
		DPRINT("%s: setbuf err: status %lux, err %lux\n",
			dp->vol, cp->status, cp->error);
.
663,668c
	ILOCK(&cp->reglock);
	cp->cmd = Cfeature;
	cp->dp = dp;
	outb(cp->pbase+Pfeature, arg);
	outb(cp->pbase+Pdh, dp->dh);
	outb(cp->pbase+Pcmd, Cfeature);
	IUNLOCK(&cp->reglock);
.
661c
	if(cmdreadywait(dp)){
		error(Eio);
	}
.
657c
		cp->dp = 0;
		qunlock(cp->ctlrlock);
.
655c
	if(dp->atapi)
		return;

	qlock(cp->ctlrlock);
.
651c
atafeature(Drive *dp, uchar arg)
.
641c
	qunlock(cp->ctlrlock);
.
638a
	cp->dp = 0;
.
632,633c
		DPRINT("%s err: lblk %ld status %lux, err %lux\n",
			dp->vol, lblk, cp->status, cp->error);
.
626a
	dp->state = Sspinning;
.
625d
620c
			print("%s: disk error\n", dp->vol);
.
618c
		DPRINT("%s: interrupted ataxfer\n", dp->vol);
.
606c
	IUNLOCK(&cp->reglock);
.
602c
				panic("%s: ataxfer", dp->vol);
.
599a
		microdelay(1);
.
593c
	outb(cp->pbase+Pdh, dp->dh | (dp->lba<<6) | head);
.
583c
	ILOCK(&cp->reglock);
.
576,581c
	if(cmdreadywait(dp)){
		error(Eio);
	}
.
572c
		qunlock(cp->ctlrlock);
.
570a
		cp->dp = 0;
.
569c
	qlock(cp->ctlrlock);
.
566c
	XPRINT("%s: ataxfer cyl %d sec %d head %d len %d\n", dp->vol, cyl, sec, head, len);
.
528c
ataxfer(Drive *dp, Partition *pp, int cmd, long start, long len, uchar *buf)
.
517c
		DPRINT("ata%d: cmd 0x%uX timeout\n", cp->ctlrno, cp->cmd);
.
508c
			DPRINT("%s: found bblk %ld at offset %ld\n", dp->vol, bblk, i);
.
491,496c
	if(dp->atapi)
		ready = 0;
	else
		ready = Sready;

	return atactlrwait(dp->cp, dp->dh, ready, ticks);
.
489c
		ticks = MS2TK(2000);
.
487c
		ticks = MS2TK(10);
.
485a
	dp->usetime = m->ticks;
.
481,483c
	ulong ticks;
	uchar ready;
.
478c
static int
.
475,476d
399a

.
398c
	dp = atadrive[DRIVE(c->qid.path)];
	if(dp->atapi)
		error(Eperm);
.
393c
	uchar *buf;
.
359,361d
352a
	dp = atadrive[DRIVE(c->qid.path)];
	if(dp->atapi){
		if(dp->online == 0)
			error(Eio);
		if(waserror()){
			qunlock(dp);
			nexterror();
		}
		qlock(dp);
		if(dp->partok == 0)
			atapipart(dp);
		qunlock(dp);
		poperror();
		return atapirwio(c, a, n, offset);
	}
	pp = &dp->p[PART(c->qid.path)];

.
348c
	uchar *buf;
.
336d
334c
atawstat(Chan*, char*)
.
329d
327c
ataremove(Chan*)
.
320,322c
	qlock(dp);
	dp->partok = 0;
	atapart(dp);
	qunlock(dp);
.
317c
		qunlock(dp);
.
311,312c
	dp = atadrive[DRIVE(c->qid.path)];
	p = &dp->p[PART(c->qid.path)];
.
305c
	Drive *dp;
.
298d
296c
atacreate(Chan*, char*, int, ulong)
.
264c
		if(dp->partok == 0){
			if(dp->atapi)
				atapipart(dp);
			else
				atapart(dp);
		}
.
258c
			atafeature(dp, 0xAA);	/* read look ahead */
.
251,255d
243c
	DPRINT("ataattach\n");

	qlock(&ataprobelock);
	if(ataprobedone == 0){
		atactlrreset();
		ataprobedone = 1;
	}
	qunlock(&ataprobelock);

	for(driveno = 0; driveno < NDrive; driveno++){
		if((dp = atadrive[driveno]) == 0)
			continue;
.
240a
	int driveno;
.
229a
atareset(void)
{
}

void
.
225,226c
		for(i = 0; i < isa.nopt; i++){
			DPRINT("ata%d: opt %s\n", ctlrno, isa.opt[i]);
			if(strncmp(isa.opt[i], "spindown", 8) == 0){
				if(isa.opt[i][9] != '=')
					continue;
				if(isa.opt[i][8] == '0')
					slave = 0;
				else if(isa.opt[i][8] == '1')
					slave = 1;
				else
					continue;
				driveno = ctlrno*2+slave;
				if(atadrive[driveno] == 0)
					continue;
				if((spindown = strtol(&isa.opt[i][10], 0, 0)) == 0)
					continue;
				if(spindown < (Hardtimeout+2000)/1000)
					spindown = (Hardtimeout+2000)/1000;
				atadrive[driveno]->spindown = spindown;
				spindownmask |= (1<<driveno);
				DPRINT("ata%d: opt spindownmask %ux\n",
					ctlrno, spindownmask);
			}
			else if(strcmp(isa.opt[i], "noatapi") == 0)
				atactlr[ctlrno]->noatapi = 1;
		}
	}
.
223c
	DPRINT("ata%d: DHmagic ok\n", ctlrno);
	if((ctlr = xalloc(sizeof(Controller))) == 0)
		return -1;
	ctlr->pbase = port;
	ctlr->ctlrno = ctlrno;
	ctlr->lastcmd = 0xFF;

	/*
	 * Attempt to check the existence of drives on the controller
	 * by issuing a 'check device diagnostics' command.
	 * Issuing a device reset here would possibly destroy any BIOS
	 * drive remapping and, anyway, some controllers (Vibra16) don't
	 * seem to implement the control-block registers.
	 * Unfortunately the vector must be set at this point as the Cedd
	 * command will generate an interrupt, which means the ataintr routine
	 * will be left on the interrupt call chain even if there are no
	 * drives found.
	 * At least one controller/ATAPI-drive combination doesn't respond
	 * to the Edd (Micronics M54Li + Sanyo CDR-XXX) so let's check for the
	 * ATAPI signature straight off. If we find it there will be no probe
	 * done for a slave. Tough.
	 */
	atapi = 0;
	mask = 0;
	status = inb(port+Pstatus);
	DPRINT("ata%d: ATAPI %uX %uX %uX\n", ctlrno, status,
		inb(port+Pcylmsb), inb(port+Pcyllsb));
	if(status == 0 && inb(port+Pcylmsb) == 0xEB && inb(port+Pcyllsb) == 0x14){
		DPRINT("ata%d: ATAPI ok\n", ctlrno);
		setvec(irq, ataintr, ctlr);
		atapi |= 0x01;
		mask |= 0x01;
		goto skipedd;
	}
	if(atactlrwait(ctlr, DHmagic, 0, MS2TK(1)) || waserror()){
		xfree(ctlr);
		return -1;
	}
	setvec(irq, ataintr, ctlr);
	ctlr->cmd = Cedd;
	outb(port+Pcmd, Cedd);
	atasleep(ctlr);
	poperror();

	/*
	 * The diagnostic returns a code in the error register, good
	 * status is bits 6-0 == 0x01.
	 * The existence of the slave is more difficult to determine,
	 * different generations of controllers may respond in different
	 * ways. The standards here offer little light but only more and
	 * more heat:
	 *   1) the slave must be done and have dropped Sbusy by now (six
	 *	seconds for the master, 5 seconds for the slave). If it
	 *	hasn't, then it has either failed or the controller is
	 *	broken in some way (e.g. Vibra16 returns status of 0xFF);
	 *   2) theory says the status of a non-existent slave should be 0.
	 *	Of course, it's valid for all the bits to be 0 for a slave
	 *	that exists too...
	 *   3) a valid ATAPI drive can have status 0 and the ATAPI signature
	 *	in the cylinder registers after reset. Of course, if the drive
	 *	has been messed about by the BIOS or some other O/S then the
	 *	signature may be gone.
	 */
	error = inb(port+Perror);
	DPRINT("ata%d: master diag error %ux\n", ctlr->ctlrno, error);
	if((error & ~0x80) == 0x01)
		mask |= 0x01;

	outb(port+Pdh, DHmagic|DHslave);
	microdelay(1);
	status = inb(port+Pstatus);
	error = inb(port+Perror);
	DPRINT("ata%d: slave diag status %ux, error %ux\n", ctlr->ctlrno, status, error);
	if(status && (status & (Sbusy|Serr)) == 0 && (error & ~0x80) == 0x01)
		mask |= 0x02;
	else if(status == 0 && inb(port+Pcylmsb) == 0xEB && inb(port+Pcyllsb) == 0x14){
		atapi |= 0x02;
		mask |= 0x02;
	}

skipedd:
	if(mask == 0){
		xfree(ctlr);
		return -1;
	}
	atactlr[ctlrno] = ctlr;

	if(have640b >= 0 && (ctlrno & 0x01))
		ctlr->ctlrlock = &atactlrlock[ctlrno-1];
	else
		ctlr->ctlrlock = &atactlrlock[ctlrno];

	if(mask & 0x01)
		atadrivealloc(ctlr, ctlrno*2, atapi & 0x01);
	if(mask & 0x02)
		atadrivealloc(ctlr, ctlrno*2+1, atapi & 0x02);

	return 0;
}

void
atactlrreset(void)
{
	int ctlrno, driveno, i, slave, spindown;
	ISAConf isa;

	cmd640b();
	rz1000();

	for(ctlrno = 0; ctlrno < NCtlr; ctlrno++){
		memset(&isa, 0, sizeof(ISAConf));
		if(isaconfig("ata", ctlrno, &isa) == 0 && ctlrno)
			continue;
		if(isa.irq == 0 && (isa.irq = defirq[ctlrno]) == 0)
			continue;

		if(atactlrprobe(ctlrno, Int0vec+isa.irq))
			continue;
.
217,221c
	drive->cp = ctlr;
	drive->driveno = driveno;
	sprint(drive->vol, "hd%d", drive->driveno);
	drive->dh = DHmagic;
	if(driveno & 0x01)
		drive->dh |= DHslave;
	drive->vers = 1;
	if(atapi)
		drive->atapi = 1;

	atadrive[driveno] = drive;
}

static int
atactlrprobe(int ctlrno, int irq)
{
	Controller *ctlr;
	int atapi, mask, port;
	uchar error, status;

	/*
	 * Check the existence of a controller by verifying a sensible
	 * value can be written to and read from the drive/head register.
	 * We define the primary/secondary/tertiary and quaternary controller
	 * port addresses to be at fixed values.
	 * If it's OK, allocate and initialise a Controller structure.
	 */
	port = pbase[ctlrno];
	outb(port+Pdh, DHmagic);
	microdelay(1);
	if((inb(port+Pdh) & 0xFF) != DHmagic){
		DPRINT("ata%d: DHmagic not ok\n", ctlrno);
		return -1;
.
210,215c
static int
atactlrwait(Controller* ctlr, uchar pdh, uchar ready, ulong ticks)
{
	int port;
	uchar dh, status;

	port = ctlr->pbase;
	dh = (inb(port+Pdh) & DHslave)^(pdh & DHslave);
	ticks += m->ticks+1;

	do{
		status = inb(port+Pstatus);
		if(status & Sbusy)
			continue;
		if(dh){
			outb(port+Pdh, pdh);
			dh = 0;
			continue;
		}
		if((status & ready) == ready)
			return 0;
	}while(m->ticks < ticks);

	DPRINT("ata%d: ctlrwait failed %uX\n", ctlr->ctlrno, status);
	outb(port+Pdh, DHmagic);
	return 1;
}

static void
atadrivealloc(Controller* ctlr, int driveno, int atapi)
{
	Drive *drive;

	if((drive = xalloc(sizeof(Drive))) == 0){
		DPRINT("ata%d: can't xalloc drive0\n", ctlr->ctlrno);
		return;
.
203,208c
	/*
	 * Look for PC-Tech RZ1000 controllers and turn off prefetch.
	 * This is overkill, but cheap.
	 */
	pcicfg = malloc(sizeof(PCIcfg));
	pcicfg->vid = 0x1042;
	pcicfg->did = 0;
	devno = 0;
	while((devno = pcimatch(0, devno, pcicfg)) != -1){
		if(pcicfg->did != 0x1000 && pcicfg->did != 0x1001)
			continue;
		pcicfgr(0, devno-1, 0, 0x40, &r40, sizeof(r40));
		r40 &= ~0x2000;
		pcicfgw(0, devno-1, 0, 0x40, &r40, sizeof(r40));
	}
	free(pcicfg);
}
.
200,201c
static void
rz1000(void)
{
	PCIcfg* pcicfg;
	ulong r40;
	int devno;
.
196,198c
	/*
	 * Look for CMD640B dual PCI controllers. Amongst other
	 * bugs only one of the controllers can be active at a time.
	 * Unfortunately there's no way to tell which pair of
	 * controllers this is, so if one is found then all controller
	 * pairs are synchronised.
	 */
	pcicfg = malloc(sizeof(PCIcfg));
	pcicfg->vid = 0x1095;
	pcicfg->did = 0x0640;
	devno = 0;
	while((devno = pcimatch(0, devno, pcicfg)) != -1){
		have640b = devno-1;
		/*
		 * If one is found, make sure read-ahead is disabled on all
		 * drives and that the 2nd controller is enabled:
		 *   reg 0x51:	bit 7 - drive 1 read ahead disable
		 *  		bit 6 - drive 0 read ahead disable
		 *  		bit 3 - 2nd controller enable
		 *   reg 0x57:	bit 3 - drive 1 read ahead disable
		 *  		bit 2 - drive 0 read ahead disable
		 * Doing byte-writes to PCI configuration space is not in the
		 * spec...
		 */
		pcicfgr(0, have640b, 0, 0x50, r50, sizeof(r50));
		r50[0x01] |= 0xC8;
		pcicfgw8(0, have640b, 0, 0x51, &r50[0x01], sizeof(r50[0x01]));
		r50[0x07] |= 0x0C;
		pcicfgw8(0, have640b, 0, 0x57, &r50[0x07], sizeof(r50[0x07]));
	}
	free(pcicfg);
}
.
191,194c
	PCIcfg* pcicfg;
	uchar r50[12];
	int devno;
	extern void pcicfgw8(int, int, int, int, void*, int);
.
188,189c
static void
cmd640b(void)
.
174c
	if(atadrive[drive] == 0)
		return 0;
	dp = atadrive[drive];
.
172c

	if(drive >= NDrive)
.
168d
159c
atagen(Chan *c, Dirtab*, long, long s, Dir *dirp)
.
157a
static int	isatapi(Drive*);
static long	atapirwio(Chan*, char*, ulong, ulong);
static void	atapipart(Drive*);
static void	atapiintr(Controller*);

.
156a
static void	atasleep(Controller*);
.
153c
static void	atafeature(Drive*, uchar);
.
151c
static long	ataxfer(Drive*, Partition*, int, long, long, uchar*);
.
146,148c
static QLock ataprobelock;
static int ataprobedone;
static Controller *atactlr[NCtlr];
static QLock atactlrlock[NCtlr];
static Drive *atadrive[NDrive];
static int spindownmask;
static int have640b = -1;
static int pbase[NCtlr] = {
	Pbase0, Pbase1, Pbase2, Pbase3,
};
static int defirq[NCtlr] = {
	14, 15, 0, 0,
};
.
141,143c
	Drive*	dp;		/* drive being accessed */
.
138c
	uchar	cmd;		/* current command */
	uchar	cmdblk[12];	/* ATAPI */
	int	len;		/* ATAPI */
	int	count;		/* ATAPI */
	uchar	lastcmd;	/* debugging info */
	uchar	status;
	uchar	error;
	uchar*	buf;		/* xfer buffer */
.
135,136d
130a
	uchar	ctlrno;
	uchar	noatapi;
.
129d
125c
	QLock*	ctlrlock;	/* exclusive access to the controller */
.
116,117c
	uchar	lba;		/* true if drive has logical block addressing */
	uchar	multi;		/* true if drive can do multiple block xfers (unused) */
	uchar	drqintr;	/* ATAPI */
	ulong	vers;		/* ATAPI */

	int	spindown;
.
114a
	ulong	lbasecs;
.
103a
	int	partok;
.
100,102c
	uchar	driveno;
	uchar	dh;		/* DHmagic|Am-I-A-Slave */
	uchar	atapi;
	uchar	online;

.
70a

.
69c
	Hardtimeout=	30000,		/* disk access timeout (ms) */

	NCtlr=		4,
	NDrive=		NCtlr*2,
.
66c
	Npart=		20+2,		/* 8 sub partitions, disk, and partition */
.
59,61d
56,57d
52a
	Cpktcmd=	0xA0,
	Cidentd=	0xA1,
	Ccapacity=	0x25,
	Cread2=		0x28,

.
50,51d
48d
44,46c
	Cident2=	0xFE,	/* pseudo command for post Cident interrupt */
	Cfeature=	0xEF,
.
42a
	Cedd=		0x90,	/* execute device diagnostics */
.
40c
	Cfirst=		0xFF,	/* pseudo command for initialisation */
.
34a
	 Sdf=		 (1<<5),
.
31a
	 DHmagic=	0xA0,
	 DHslave=	0x10,
.
26c
	 Eabort=	(1<<2),
	Pfeature=	1,	/* buffer mode port (write) */
.
22,23c
	Pbase0=		0x1F0,	/* primary */
	Pbase1=		0x170,	/* secondary */
	Pbase2=		0x1E8,	/* tertiary */
	Pbase3=		0x168,	/* quaternary */
.
11a
#define XPRINT if(0)print
#define ILOCK(x)
#define IUNLOCK(x)
.
0a
/*
 * This has gotten a bit messy with the addition of multiple controller
 * and ATAPI support; needs a rewrite before adding any 'ctl' functions.
 */
.
## diffname pc/devata.c 1995/1208
## diff -e /n/fornaxdump/1995/1206/sys/src/brazil/pc/devata.c /n/fornaxdump/1995/1208/sys/src/brazil/pc/devata.c
1820c
			err = cp->error & 0xF0;
.
1807c
	retrycount = 2;
.
1708c
	retrycount = 2;
.
1691c
		DPRINT("%s: Bad packet command %ux, error %ux\n", dp->vol, cp->cmdblk[0], cp->error);
.
1617,1619c
	outb(cp->pbase+Pcmd, 0x08);
	if(atactlrwait(dp->cp, DHmagic, 0, MS2TK(100))){
		DPRINT("%s: isatapi ctlrwait status %ux\n", dp->vol, inb(cp->pbase+Pstatus));
		return 0;
.
1544,1545d
1540a
#else
			if(cp->cmd != Cread && (cp->status & (Sbusy|Sready)) != Sready)
				cp->cmd = Cident2;
#endif /* notdef */
.
1536a
#ifdef notdef
.
1154c
		XPRINT("%s: LBA: %s %d sectors %d bytes\n",
.
1145c
		XPRINT("%s: changed to %d cyl %d head %d sec\n",
.
1137c
	XPRINT("%s: %s %d/%d/%d CHS %d bytes\n",
.
1099,1100c
		if(cp->error & Eabort){
.
1094a
	DPRINT("%s: ident command %ux sent\n", dp->vol, cmd);
.
1072,1083c
	cmd = Cident;
.
403a
		DPRINT("ata%d: Cedd status %ux/%ux/%ux\n",
			ctlrno, inb(port+Pstatus), inb(port+Pcylmsb), inb(port+Pcyllsb));
.
387c
	 * to the Cedd (Micronics M54Li + Sanyo CRD-254P) so let's check for the
.
343a
	}
.
342c
	if(atapi){
		sprint(drive->vol, "atapi%d", drive->driveno);
.
79c
	Hardtimeout=	6000,		/* disk access timeout (ms) */
.
## diffname pc/devata.c 1995/1209
## diff -e /n/fornaxdump/1995/1208/sys/src/brazil/pc/devata.c /n/fornaxdump/1995/1209/sys/src/brazil/pc/devata.c
1682c
	atasleep(cp, Hardtimeout);
.
1533,1538d
1530,1531c
			if(cp->cmd != Cread)
.
1206c
	atasleep(cp, Hardtimeout);
.
1089c
	atasleep(cp, 1000);
.
999c
	atasleep(cp, Hardtimeout);
.
945c
	atasleep(cp, Hardtimeout);
.
837c
	tsleep(&cp->r, cmddone, cp, ms);
.
835c
atasleep(Controller *cp, int ms)
.
414c
	atasleep(ctlr, Hardtimeout);
.
193c
static void	atasleep(Controller*, int);
.
## diffname pc/devata.c 1995/1218
## diff -e /n/fornaxdump/1995/1209/sys/src/brazil/pc/devata.c /n/fornaxdump/1995/1218/sys/src/brazil/pc/devata.c
1405a
			if(n == 0 || strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1)){
				dp->p[0].end--;
				dp->p[1].start--;
				dp->p[1].end--;
			}
.
1402a
		}
		else{
.
1399c
		if(n > 0 && strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1) == 0){
.
1396c
		ataxfer(dp, &dp->p[0], Cread, dp->p[0].end-1, dp->bytes, buf);
.
1392,1394c
		 *  we still check if there is a valid partition table in
		 *  the last sector if none is found in the second last.
.
1387,1388c
		 *  Read second last sector from disk, null terminate.
		 *  The last sector used to hold the partition tables.
.
## diffname pc/devata.c 1995/1219
## diff -e /n/fornaxdump/1995/1218/sys/src/brazil/pc/devata.c /n/fornaxdump/1995/1219/sys/src/brazil/pc/devata.c
1411a
else print("ok%d|", dp->p[1].start);
.
1410a
print("nok%d|", dp->p[1].start);
.
1403a
print("r%d|", dp->p[1].start);
.
1401a
print("OK%d|", dp->p[1].start);
.
1395c
print("R%d|", dp->p[0].end-2);
		ataxfer(dp, &dp->p[0], Cread, dp->p[0].end-2, dp->bytes, buf);
.
## diffname pc/devata.c 1995/1221
## diff -e /n/fornaxdump/1995/1219/sys/src/brazil/pc/devata.c /n/fornaxdump/1995/1221/sys/src/brazil/pc/devata.c
1410,1416c
			if(n == 0 || strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1))
				i = 1;
		}
		if(i){
			dp->p[0].end--;
			dp->p[1].start--;
			dp->p[1].end--;
.
1406d
1399,1404c
		if(n > 0 && strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1) == 0)
			i = 1;
.
1395,1396c
		i = 0;
		ataxfer(dp, &dp->p[0], Cread, (dp->p[0].end-2)*dp->bytes, dp->bytes, buf);
.
## diffname pc/devata.c 1996/0112
## diff -e /n/fornaxdump/1995/1221/sys/src/brazil/pc/devata.c /n/fornaxdump/1996/0112/sys/src/brazil/pc/devata.c
1826a

	cp->len = 18;
	cp->count = 0;
	memset(cp->cmdblk, 0, sizeof(cp->cmdblk));
	cp->cmdblk[0] = Creqsense;
	cp->cmdblk[4] = 18;
	atapiexec(dp);
	if(cp->count != 18){
		print("cmd=%2.2uX, lastcmd=%2.2uX ", cp->cmd, cp->lastcmd);
		print("cdsize count %d, status 0x%2.2uX, error 0x%2.2uX\n",
			cp->count, cp->status, cp->error);
		error(Eio);
	}
.
1805a
		DPRINT("atapipart: cmd %uX error %uX\n", cp->cmd, cp->error);
.
1076c
	if(dp->atapi)
		cmd = Cidentd;
	else
		cmd = Cident;
.
64a
	Ctur=		0x00,
	Creqsense=	0x03,
.
## diffname pc/devata.c 1996/0210
## diff -e /n/fornaxdump/1996/0112/sys/src/brazil/pc/devata.c /n/fornaxdump/1996/0210/sys/src/brazil/pc/devata.c
1094c
	if(cmd == Cident)
		atasleep(cp, 3000);
	else
		atasleep(cp, 30000);
.
517,518c
			else if(strcmp(isa.opt[i], "reset") == 0)
				atactlr[ctlrno]->resetok = 1;
.
450,452c
	else if(status == 0){
		msb = inb(port+Pcylmsb);
		lsb = inb(port+Pcyllsb);
		DPRINT("ata%d: ATAPI slave %uX %uX %uX\n", ctlrno, status,
			inb(port+Pcylmsb), inb(port+Pcyllsb));
		if(msb == 0xEB && lsb == 0x14){
			atapi |= 0x02;
			mask |= 0x02;
		}
.
408,409c
		DPRINT("ata%d: Cedd status %ux/%ux/%ux\n", ctlrno,
			inb(port+Pstatus), inb(port+Pcylmsb), inb(port+Pcyllsb));
.
394a
	if(ctlr->resetok){
		outb(port+Pctrl, Srst|nIEN);
		microdelay(1);
		outb(port+Pctrl, 0);
		if(atactlrwait(ctlr, DHmagic, 0, 100)){
			DPRINT("ata%d: Srst status %ux/%ux/%ux\n", ctlrno,
				inb(port+Pstatus), inb(port+Pcylmsb), inb(port+Pcyllsb));
			xfree(ctlr);
		}
	}

.
385c
	 * seem to implement the control-block registers; do it if requested.
.
379a

.
357c
	uchar error, status, msb, lsb;
.
155c
	uchar	resetok;
.
51a
	Pctrl=		0x206,	/* device control, alternate status */
	 nIEN=		(1<<1),
	 Srst=		(1<<2),

.
## diffname pc/devata.c 1996/0215
## diff -e /n/fornaxdump/1996/0210/sys/src/brazil/pc/devata.c /n/fornaxdump/1996/0215/sys/src/brazil/pc/devata.c
1119c
		atasleep(cp, 10000);
.
404c
		if(atactlrwait(ctlr, DHmagic, 0, MS2TK(20)){
.
402c
		delay(10);
.
## diffname pc/devata.c 1996/0216
## diff -e /n/fornaxdump/1996/0215/sys/src/brazil/pc/devata.c /n/fornaxdump/1996/0216/sys/src/brazil/pc/devata.c
404c
		if(atactlrwait(ctlr, DHmagic, 0, MS2TK(20))){
.
## diffname pc/devata.c 1996/0223
## diff -e /n/fornaxdump/1996/0216/sys/src/brazil/pc/devata.c /n/fornaxdump/1996/0223/sys/src/brazil/pc/devata.c
13d
## diffname pc/devata.c 1996/0315
## diff -e /n/fornaxdump/1996/0223/sys/src/brazil/pc/devata.c /n/fornaxdump/1996/0315/sys/src/brazil/pc/devata.c
1453c
			if(parsefields(line[i], field, 3, " ") != 3)
.
1445c
		n = parsefields((char*)buf, line, Npart+1, "\n");
.
1433c
			n = parsefields((char*)buf, line, Npart+1, "\n");
.
1427c
		n = parsefields((char*)buf, line, Npart+1, "\n");
.
1352c
			if(parsefields(line[i], field, 1, " ") != 1)
.
1347c
	n = parsefields((char*)buf, line, Nrepl+1, "\n");
.
## diffname pc/devata.c 1996/0803
## diff -e /n/fornaxdump/1996/0315/sys/src/brazil/pc/devata.c /n/fornaxdump/1996/0803/sys/src/brazil/pc/devata.c
642a
	if(dp == 0)
		return;
.
## diffname pc/devata.c 1996/1225
## diff -e /n/fornaxdump/1996/0803/sys/src/brazil/pc/devata.c /n/fornaxdump/1996/1225/sys/src/brazil/pc/devata.c
206c
atagen(Chan *c, Dirtab*, int, int s, Dir *dirp)
.
## diffname pc/devata.c 1997/0327
## diff -e /n/fornaxdump/1996/1225/sys/src/brazil/pc/devata.c /n/emeliedump/1997/0327/sys/src/brazil/pc/devata.c
1964a

Dev atadevtab = {
	devreset,
	devinit,
	ataattach,
	devclone,
	atawalk,
	atastat,
	ataopen,
	devcreate,
	ataclose,
	ataread,
	devbread,
	atawrite,
	devbwrite,
	devremove,
	devwstat,
};
.
1604,1605c
static void
ataclock(void)
.
805,810d
726,732c
static long
.
663,674d
660,661c
static long
ataread(Chan* c, void* a, long n, ulong offset)
.
630,635d
628c
ataclose(Chan* c)
.
621,622c
static Chan*
ataopen(Chan* c, int omode)
.
615,616c
static void
atastat(Chan* c, char* dp)
.
606,611d
603,604c
static int
atawalk(Chan* c, char* name)
.
558,559c
static Chan*
ataattach(char* spec)
.
549,553d
544,546c
	if(spindownmask)
		addclock0link(ataclock);
.
542d
512c
		if(atactlrprobe(ctlrno, VectorPIC+isa.irq))
.
496c
static void
.
483c
	if(have640b && (ctlrno & 0x01))
.
428c
	intrenable(irq, ataintr, ctlr, ctlr->tbdf);
.
417c
		intrenable(irq, ataintr, ctlr, ctlr->tbdf);
.
380a
	ctlr->tbdf = BUSUNKNOWN;
.
300d
296,298c
		r = pcicfgr32(p, 0x40);
		r &= ~0x2000;
		pcicfgw32(p, 0x40, r);
.
289,294c
	p = 0;
	while(p = pcimatch(p, 0x1042, 0)){
		if(p->did != 0x1000 && p->did != 0x1001)
.
281,283c
	Pcidev *p;
	ulong r;
.
275d
269,273c
		r = pcicfgr8(p, 0x51);
		r |= 0xC8;
		pcicfgw8(p, 0x51, r);
		r = pcicfgr8(p, 0x57);
		r |= 0x0C;
		pcicfgw8(p, 0x57, r);
.
266,267d
252,257c
	p = 0;
	while(p = pcimatch(p, 0x1095, 0x0640)){
		have640b++;
.
240,243c
	Pcidev *p;
	int r;
.
206c
atagen(Chan* c, Dirtab*, int, int s, Dir* dirp)
.
198a
static void	ataclock(void);
.
183c
static int have640b;
.
157a
	int	tbdf;
.
## diffname pc/devata.c 1997/0408
## diff -e /n/emeliedump/1997/0327/sys/src/brazil/pc/devata.c /n/emeliedump/1997/0408/sys/src/brazil/pc/devata.c
1914a
	'H',
	"ata",

.
## diffname pc/devata.c 1997/0812
## diff -e /n/emeliedump/1997/0408/sys/src/brazil/pc/devata.c /n/emeliedump/1997/0812/sys/src/brazil/pc/devata.c
823c
ataxfer(Drive *dp, Partition *pp, int cmd, ulong start, long len, uchar *buf)
.
193c
static long	ataxfer(Drive*, Partition*, int, ulong, long, uchar*);
.
## diffname pc/devata.c 1997/0815
## diff -e /n/emeliedump/1997/0812/sys/src/brazil/pc/devata.c /n/emeliedump/1997/0815/sys/src/brazil/pc/devata.c
531a

		if(atactlrprobe(ctlrno, VectorPIC+isa.irq, resetok))
			continue;

		if(spindown == 0 || atadrive[driveno] == 0)
			continue;
		atadrive[driveno]->spindown = spindown;
		spindownmask |= (1<<driveno);
		DPRINT("ata%d: opt spindownmask %ux\n", ctlrno, spindownmask);
.
530c
				resetok = 1;
.
524,527c
				driveno = ctlrno*2+slave;
.
517,519d
503,505c
		driveno = resetok = spindown = 0;
.
490c
	int ctlrno, driveno, i, resetok, slave, spindown;
.
390c
	if(resetok){
.
362,363c
	status = inb(port+Pdh) & 0xFF;
	if(status != DHmagic){
		DPRINT("ata%d: DHmagic not ok == %ux\n", ctlrno, status);
.
346c
atactlrprobe(int ctlrno, int irq, int resetok)
.
159d
14c
#define DPRINT if(1)print
.
## diffname pc/devata.c 1997/0818
## diff -e /n/emeliedump/1997/0815/sys/src/brazil/pc/devata.c /n/emeliedump/1997/0818/sys/src/brazil/pc/devata.c
14c
#define DPRINT if(0)print
.
## diffname pc/devata.c 1997/1230
## diff -e /n/emeliedump/1997/0818/sys/src/brazil/pc/devata.c /n/emeliedump/1997/1230/sys/src/brazil/pc/devata.c
1834a
	DPRINT("%s: atapipart secs %ud, bytes %ud, cap %ud\n",
		dp->vol, dp->lbasecs, dp->bytes, dp->cap);
.
1833a
	dp->bytes = (cp->buf[4]<<24)|(cp->buf[5]<<16)|(cp->buf[6]<<8)|cp->buf[7];
.
1820a
{ int i;
  for(i = 0; i < 18; i++)
    print("%2.2uX ", buf[i]);
  print("\n");
}
.
14,15c
#define DPRINT if(1)print
#define XPRINT if(1)print
.
## diffname pc/devata.c 1998/0106
## diff -e /n/emeliedump/1997/1230/sys/src/brazil/pc/devata.c /n/emeliedump/1998/0106/sys/src/brazil/pc/devata.c
1839a
	if(dp->bytes > 2048 && dp->bytes <= 2352)
		dp->bytes = 2048;
.
448a
atapislave:
.
411c
//		goto skipedd;
goto atapislave;
.
14,15c
#define DPRINT if(0)print
#define XPRINT if(0)print
.
## diffname pc/devata.c 1998/0115
## diff -e /n/emeliedump/1998/0106/sys/src/brazil/pc/devata.c /n/emeliedump/1998/0115/sys/src/brazil/pc/devata.c
1823,1827d
## diffname pc/devata.c 1998/0219
## diff -e /n/emeliedump/1998/0115/sys/src/brazil/pc/devata.c /n/emeliedump/1998/0219/sys/src/brazil/pc/devata.c
1907c
		if(cause == 0)
			outss(pbase+Pdata, addr, count/2);
		else
			inss(pbase+Pdata, addr, count/2);
.
1876a
	case 0:						/* data out */
.
1863,1865d
1760c
	rv = atapiio(dp, a, len, offset, cmd);
.
1742c
atapirwio(Chan *c, uchar *a, ulong len, ulong offset, int cmd)
.
1735c
	if(cmd == Cread2)
		free(buf);
.
1717c
		cp->cmdblk[0] = cmd;
.
1703c
	if(cmd == Cread2)
		cp->buf = buf;
	else
		cp->buf = a;
.
1698c
		if(cmd == Cread2)
			free(buf);
.
1684c
	if(cmd == Cread2)
		buf = smalloc(Maxxfer);
	else
		buf = 0;
.
1675c
atapiio(Drive *dp, uchar *a, ulong len, ulong offset, int cmd)
.
1103a
		if((ip->config & 0x1F00) == 0x0000)
			dp->atapi = 2;
.
752c
		if(dp->atapi)
			atapirwio(c, buf, dp->bytes, offset+rv, Cwrite2);
		else
			ataxfer(dp, pp, Cwrite, offset+rv, dp->bytes, buf);
.
750c
		if(dp->atapi)
			atapirwio(c, buf, dp->bytes, offset+rv, Cread2);
		else
			ataxfer(dp, pp, Cread, offset+rv, dp->bytes, buf);
.
739c
		if(dp->atapi)
			i = atapirwio(c, buf, i, offset+rv, Cwrite2);
		else
			i = ataxfer(dp, pp, Cwrite, offset+rv, i, buf);
.
725c
		if(dp->atapi)
			atapirwio(c, buf, dp->bytes, offset-partial, Cwrite2);
		else
			ataxfer(dp, pp, Cwrite, offset-partial, dp->bytes, buf);
.
719c
		if(dp->atapi)
			atapirwio(c, buf, dp->bytes, offset-partial, Cread2);
		else
			ataxfer(dp, pp, Cread, offset-partial, dp->bytes, buf);
.
702,703c
	if(dp->atapi){
		if(dp->online == 0 || dp->atapi == 1)
			error(Eio);
		if(waserror()){
			qunlock(dp);
			nexterror();
		}
		qlock(dp);
		if(dp->partok == 0)
			atapipart(dp);
		qunlock(dp);
		poperror();
	}
.
661c
		return atapirwio(c, a, n, offset, Cread2);
.
456c
	if((status & ~0x02) && (status & (Sbusy|Serr)) == 0 && (error & ~0x80) == 0x01)
.
446c
	DPRINT("ata%d: master diag status %uX, error %ux\n",
		ctlr->ctlrno, inb(port+Pstatus), error);
.
443a
	 * When checking status, mask off the IDX bit.
.
411,412c
		goto atapislave;
.
403d
202c
static long	atapirwio(Chan*, uchar*, ulong, ulong, int);
.
71a
	Cwrite2=	0x2A,
.
13,15c
#define DEBUG	0
#define DPRINT if(DEBUG)print
#define XPRINT if(DEBUG)print
.
## diffname pc/devata.c 1998/0319
## diff -e /n/emeliedump/1998/0219/sys/src/brazil/pc/devata.c /n/emeliedump/1998/0319/sys/src/brazil/pc/devata.c
697a
	ulong offset = off;
.
691c
atawrite(Chan *c, void *a, long n, vlong off)
.
644a
	ulong offset = off;
.
637c
ataread(Chan* c, void* a, long n, vlong off)
.
## diffname pc/devata.c 1998/0327
## diff -e /n/emeliedump/1998/0319/sys/src/brazil/pc/devata.c /n/emeliedump/1998/0327/sys/src/brazil/pc/devata.c
1799c
	rv = atapiio(dp, a, len, off, cmd);
.
1781c
atapirwio(Chan *c, uchar *a, ulong len, vlong off, int cmd)
.
1769c
		off += m;
.
1750c
		o = off % dp->bytes;
.
1747,1748c
		bn = off / dp->bytes;
		if(off > dp->cap-dp->bytes)
.
1720a

.
1706c
atapiio(Drive *dp, uchar *a, ulong len, vlong off, int cmd)
.
1288,1290c
	dp->cap = (vlong)dp->bytes * dp->cyl * dp->heads * dp->sectors;
	DPRINT("%s: probed: %d/%d/%d CHS %lld bytes\n",
		dp->vol, dp->cyl, dp->heads, dp->sectors,
		dp->cap);
.
1165c
		dp->cap = (vlong)dp->bytes * dp->cyl * dp->heads * dp->sectors;
.
1159,1161c
		dp->cap = (vlong)dp->bytes * dp->lbasecs;
		XPRINT("%s: LBA: %s %d sectors %lld bytes\n",
			dp->vol, id, dp->lbasecs,
			dp->cap);
.
1143,1144c
	XPRINT("%s: %s %d/%d/%d CHS %lld bytes\n",
		dp->vol, id, dp->cyl, dp->heads, dp->sectors,
		dp->cap);
.
893c
	XPRINT("%s: ataxfer cyl %d sec %d head %d len %d\n",
		dp->vol, cyl, sec, head, len);
.
868c
	start = off / dp->bytes;
.
860a
	ulong start;
.
855c
ataxfer(Drive *dp, Partition *pp, int cmd, vlong off, long len, uchar *buf)
.
835c
			DPRINT("%s: found bblk %ld at offset %ld\n",
			dp->vol, bblk, i);
.
781c
			ataxfer(dp, pp, Cwrite, off+rv, dp->bytes, buf);
.
779c
			atapirwio(c, buf, dp->bytes, off+rv, Cwrite2);
.
776c
			ataxfer(dp, pp, Cread, off+rv, dp->bytes, buf);
.
774c
			atapirwio(c, buf, dp->bytes, off+rv, Cread2);
.
762c
			i = ataxfer(dp, pp, Cwrite, off+rv, i, buf);
.
760c
			i = atapirwio(c, buf, i, off+rv, Cwrite2);
.
745c
			ataxfer(dp, pp, Cwrite, off-partial, dp->bytes, buf);
.
743c
			atapirwio(c, buf, dp->bytes, off-partial, Cwrite2);
.
736c
			ataxfer(dp, pp, Cread, off-partial, dp->bytes, buf);
.
734c
			atapirwio(c, buf, dp->bytes, off-partial, Cread2);
.
731c
	partial = off % dp->bytes;
.
699d
675c
		i = ataxfer(dp, pp, Cread, off+rv-skip, n-rv+skip, buf);
.
673c
	skip = off % dp->bytes;
.
663c
		return atapirwio(c, a, n, off, Cread2);
.
645d
234c
	l = (pp->end - pp->start) * (vlong)dp->bytes;
.
215c
	vlong l;
.
203c
static long	atapirwio(Chan*, uchar*, ulong, vlong, int);
.
193c
static long	ataxfer(Drive*, Partition*, int, vlong, long, uchar*);
.
133c
	vlong	cap;		/* total bytes */
.
## diffname pc/devata.c 1998/0331
## diff -e /n/emeliedump/1998/0327/sys/src/brazil/pc/devata.c /n/emeliedump/1998/0331/sys/src/brazil/pc/devata.c
1413c
		ataxfer(dp, &dp->p[0], Cread, (dp->p[0].end-2)*
			(vlong)dp->bytes, dp->bytes, buf);
.
419c
	if(once)
		intrenable(irq, ataintr, ctlr, ctlr->tbdf);
.
415a
		if(once){
			once = 0;
			ctlr->cmd = 0;
			goto retry;
		}
.
408c
		if(once)
			intrenable(irq, ataintr, ctlr, ctlr->tbdf);
.
406c
	if(/*status == 0 &&*/ inb(port+Pcylmsb) == 0xEB && inb(port+Pcyllsb) == 0x14){
.
401a
	once = 1;
retry:
.
349c
	int atapi, mask, once, port;
.
## diffname pc/devata.c 1998/0501
## diff -e /n/emeliedump/1998/0331/sys/src/brazil/pc/devata.c /n/emeliedump/1998/0501/sys/src/brazil/pc/devata.c
1782c
		if(cmd == Cread2)
			memmove(a, cp->buf + o, m);
		else
			cp->buf += m;
.
## diffname pc/devata.c 1998/0510
## diff -e /n/emeliedump/1998/0501/sys/src/brazil/pc/devata.c /n/emeliedump/1998/0510/sys/src/brazil/pc/devata.c
1845c
		DPRINT("atapipart: cmd %uX error %uX\n", cp->cmdblk[0], cp->error);
.
1739a
			atapireqsense(dp);
.
1737a
		DPRINT("atapiio: cmd %uX error %uX\n", cp->cmdblk[0], cp->error);
.
1718a
static void
atapireqsense(Drive* dp)
{
	Controller *cp;
	uchar *buf;

	cp = dp->cp;

	buf = smalloc(Maxxfer);
	cp->buf = buf;
	cp->dp = dp;

	if(waserror()){
		free(buf);
		return;
	}

	cp->len = 18;
	cp->count = 0;
	memset(cp->cmdblk, 0, sizeof(cp->cmdblk));
	cp->cmdblk[0] = Creqsense;
	cp->cmdblk[4] = 18;
	atapiexec(dp);
	if(cp->count != 18){
		print("cmd=%2.2uX, lastcmd=%2.2uX ", cp->cmd, cp->lastcmd);
		print("cdsize count %d, status 0x%2.2uX, error 0x%2.2uX\n",
			cp->count, cp->status, cp->error);
	}

	poperror();
	free(buf);
}

.
1178a
	XPRINT("%s: %s %d/%d/%d CHS %lld bytes\n",
		dp->vol, id, dp->cyl, dp->heads, dp->sectors,
		dp->cap);
.
1153,1155d
13,17c
#define DEBUG		0
#define DPRINT 		if(DEBUG)print
#define XPRINT 		if(DEBUG)print
#define ILOCK(x)	ilock(x)
#define IUNLOCK(x)	iunlock(x)
.
3a
 * The register locking needs looked at.
.
## diffname pc/devata.c 1998/0516
## diff -e /n/emeliedump/1998/0510/sys/src/brazil/pc/devata.c /n/emeliedump/1998/0516/sys/src/brazil/pc/devata.c
2001,2003c
			inss(pbase+Pdata, cp->buf+cp->count, count/2);
		cp->count += count;
.
1999c
			outss(pbase+Pdata, cp->buf+cp->count, count/2);
.
1996,1997c
		if(cp->count+count > Maxxfer)
			panic("hd%d: count %d, already %d\n", count, cp->count);
.
1969,1970c
		if(cp->buf == 0){
.
1949d
1918d
1916a
	cp->nsecs = 1;
.
1905d
1903a
	cp->nsecs = 1;
.
1831c
	poperror();

	return len;
.
1829a
	cp->buf = 0;
	len = cp->count;
.
1796,1828c
	memset(cp->cmdblk, 0, 12);
	cp->cmdblk[0] = cmd;
	cp->cmdblk[2] = start>>24;
	cp->cmdblk[3] = start>>16;
	cp->cmdblk[4] = start>>8;
	cp->cmdblk[5] = start;
	cp->cmdblk[7] = cp->nsecs>>8;
	cp->cmdblk[8] = cp->nsecs;
	atapiexec(dp);
	if(cp->count != cp->len)
		print("short read\n");
.
1794d
1789,1792c
	cp->buf = buf;
	cp->nsecs = len;
	cp->len = len*dp->bytes;
	cp->cmd = cmd;
.
1783,1784c
		cp->buf = 0;
.
1763,1766c
	cp = dp->cp;
.
1761c
	/*
	 *  cut transfer size down to disk buffer size
	 */
	start = off / dp->bytes;
	if(len > Maxxfer)
		len = Maxxfer;
	len = (len + dp->bytes - 1) / dp->bytes;
	if(len == 0)
		return 0;
.
1758d
1756c
	ulong start;
.
1754c
atapiio(Drive *dp, uchar *buf, ulong len, vlong off, int cmd)
.
1738d
1736a
	cp->nsecs = 1;
.
1677c
	cp->count = 0;
.
684c
		if(dp->atapi)
			i = atapirwio(c, buf, n-rv+skip, off+rv-skip, Cread2);
		else
			i = ataxfer(dp, pp, Cread, off+rv-skip, n-rv+skip, buf);
.
672d
## diffname pc/devata.c 1998/0825
## diff -e /n/emeliedump/1998/0516/sys/src/brazil/pc/devata.c /n/emeliedump/1998/0825/sys/src/brazil/pc/devata.c
1919c
	DPRINT("%s: atapipart secs %lud, bytes %ud, cap %lld\n",
.
1542c
				print("%s: read/ident cmd=%ux status=%ux\n",
.
1503c
			print("ata%d: cmd=%ux, lastcmd=%ux status=%ux\n",
.
1304c
	DPRINT("%s: probed: %ld/%d/%d CHS %lld bytes\n",
.
1237c
		DPRINT("%s: probe err: status %ux, err %ux\n",
.
1179c
	XPRINT("%s: %s %ld/%d/%d CHS %lld bytes\n",
.
1171c
		XPRINT("%s: LBA: %s %lud sectors %lld bytes\n",
.
1162c
		XPRINT("%s: changed to %ld cyl %d head %d sec\n",
.
1020c
		DPRINT("%s: setbuf err: status %ux, err %ux\n",
.
971c
		DPRINT("%s err: lblk %ld status %ux, err %ux\n",
.
905c
	XPRINT("%s: ataxfer cyl %d sec %d head %d len %ld\n",
.
845c
			DPRINT("%s: found bblk %ld at offset %d\n",
.
## diffname pc/devata.c 1998/0831
## diff -e /n/emeliedump/1998/0825/sys/src/brazil/pc/devata.c /n/emeliedump/1998/0831/sys/src/brazil/pc/devata.c
1860a

	if(dp->atapi == 1) { /* cd-rom */ 
		dp->npart = CDmax;
		pp = &dp->p[CDcmd];
		strcpy(pp->name, "cmd");
		pp->start = pp->end = 0;
		pp = &dp->p[CDdata];
		strcpy(pp->name, "data");
		pp->start = pp->end = 0;
	}
.
1711c
	atasleep(cp, Atapitimeout);
.
1674a
		print("cmdreadywait fails");
.
1589c
		if(cp->cmd == 0 && cp->lastcmd == Cpktcmd)
.
722,726d
720c
			poperror();
			break;
		case CDcmd:
			if(n != 12)
				error(Ebadarg);
			acmd = &dp->atapicmd;
			qlock(acmd);
			acmd->pid = up->pid;
			memmove(acmd->cmdblk, a, n);
			return n;
		case CDdata:
			error(Egreg);
.
716,718c
		switch(PART(c->qid.path)) {
		case CDdisk:
			if(dp->online == 0 || dp->atapi == 1)
				error(Eio);
			if(waserror()){
				qunlock(dp);
				nexterror();
			}
			qlock(dp);
			if(dp->partok == 0)
				atapipart(dp);
.
709a
	Atapicmd *acmd;
.
667,671d
665c
			poperror();
			break;
		case CDcmd:
			acmd = &dp->atapicmd;
			if(n < 4)
				error(Ebadarg);
			if(canqlock(acmd)) {
				qunlock(acmd);
				error(Egreg);
			}
			if(acmd->pid != up->pid)
				error(Egreg);
			n = 4;
			*aa++ = 0;
			*aa++ = 0;
			*aa++ = acmd->error;
			*aa   = acmd->status;
			qunlock(acmd);
			return n;
		case CDdata:
			acmd = &dp->atapicmd;
			if(canqlock(acmd)) {
				qunlock(acmd);
				error(Egreg);
			}
			if(acmd->pid != up->pid)
				error(Egreg);
			if(n > Maxxfer)
				error(Ebadarg);

			cp = dp->cp;
			qlock(cp->ctlrlock);
			cp->len = 0;
			cp->buf = 0;
			cp->dp = dp;
			if(waserror()) {
				if(cp->buf)
					free(cp->buf);
				cp->buf = 0;
				cp->dp = 0;
				acmd->status = cp->status;
				acmd->error = cp->error;
				qunlock(cp->ctlrlock);
				nexterror();
			}
			if(n)
				cp->buf = smalloc(Maxxfer);
			cp->len = n;
			memmove(cp->cmdblk, acmd->cmdblk, sizeof cp->cmdblk);
			atapiexec(dp);
			memmove(a, cp->buf, cp->count);
			poperror();
			if(cp->buf)
				free(cp->buf);
			acmd->status = cp->status;
			acmd->error = cp->error;
			n = cp->count;
			qunlock(cp->ctlrlock);
			return n;
.
661,663c
		switch(PART(c->qid.path)){
		case CDdisk:
			if(dp->online == 0)
				error(Eio);
			if(waserror()){
				qunlock(dp);
				nexterror();
			}
			qlock(dp);
			if(dp->partok == 0)
				atapipart(dp);
.
654a
	Atapicmd *acmd;
	Controller *cp;
.
631a
	if(dp->atapi == 1 && strcmp(p->name, "cmd") == 0) {
		acmd = &dp->atapicmd;
		if(canqlock(acmd)) {
			qunlock(acmd);
			return;
		}
		if(acmd->pid == up->pid)
			qunlock(acmd);
		return;
	}
.
623a
	Atapicmd *acmd;
.
206a
static void	atapiexec(Drive*);
.
146a
	Atapicmd atapicmd;
.
113a
 * the result of the last user-invoked atapi cmd
 */
struct Atapicmd
{
	QLock;
	int	pid;
	ushort	status;
	ushort	error;
	uchar	cmdblk[12];
};

/*
.
89a

	/* cd files */
	CDdisk = 0,
	CDcmd,
	CDdata,
	CDmax,
.
87c
	Atapitimeout=	10000,		/* disk access timeout (ms) */
.
24a
typedef struct Atapicmd		Atapicmd;
.
## diffname pc/devata.c 1998/0910
## diff -e /n/emeliedump/1998/0831/sys/src/brazil/pc/devata.c /n/emeliedump/1998/0910/sys/src/brazil/pc/devata.c
559c
		if(atactlrprobe(ctlrno, isa.irq, resetok))
.
## diffname pc/devata.c 1998/0922
## diff -e /n/emeliedump/1998/0910/sys/src/brazil/pc/devata.c /n/emeliedump/1998/0922/sys/src/brazil/pc/devata.c
2030c
		print("cmd=0x%2.2uX, lastcmd=0x%2.2uX ", cp->cmd, cp->lastcmd);
.
2018c
		print("cmd=0x%2.2uX, lastcmd=0x%2.2uX ", cp->cmd, cp->lastcmd);
.
1988c
		DPRINT("atapipart: cmd 0x%uX error 0x%uX\n", cp->cmdblk[0], cp->error);
.
1828c
		DPRINT("%s: Bad packet command 0x%ux, error 0x%ux\n",
			dp->vol, cp->cmdblk[0], cp->error);
.
## diffname pc/devata.c 1998/1006
## diff -e /n/emeliedump/1998/0922/sys/src/brazil/pc/devata.c /n/emeliedump/1998/1006/sys/src/brazil/pc/devata.c
1615c
			panic("ata%d: wait busy\n", cp->ctlrno);
.
397d
## diffname pc/devata.c 1998/1007
## diff -e /n/emeliedump/1998/1006/sys/src/brazil/pc/devata.c /n/emeliedump/1998/1007/sys/src/brazil/pc/devata.c
2095c
			print("%s: Cpktcmd status=0x%uX, error=0x%uX\n",
.
2088a
			microdelay(1);
.
2059c
	DPRINT("%s: atapiintr 0x%uX\n", cp->dp->vol, cause);
.
1889c
		DPRINT("atapiio: cmd 0x%uX error 0x%uX\n", cp->cmdblk[0], cp->error);
.
1827c
		DPRINT("%s: Bad packet command 0x%uX, error 0x%uX\n",
.
1803a
			microdelay(1);
.
1764c
		DPRINT("%s: isatapi cyl 0x%uX 0x%uX\n",
.
1760c
		DPRINT("%s: isatapi status 0x%uX\n", dp->vol, inb(cp->pbase+Pstatus));
.
1753c
		DPRINT("%s: isatapi ctlrwait status 0x%uX\n", dp->vol, inb(cp->pbase+Pstatus));
.
1690a
		DPRINT("Cident2\n");
.
1675,1677c
			//if(cp->cmd != Cread)
			//	cp->cmd = Cident2;
			//else
.
1655a
			microdelay(1);
.
1651c
				print("%s: read/ident cmd=0x%uX status=0x%uX\n",
.
1633a
				microdelay(1);
			}
.
1630c
			while(((cp->status = inb(cp->pbase+Pstatus)) & Sdrq) == 0){
.
1615a
		microdelay(1);
.
1614c
			panic("%s: wait busy\n", dp->vol);
.
1612c
			print("ata%d: cmd=0x%uX, lastcmd=0x%uX status=0x%uX\n",
.
1346c
		DPRINT("%s: probe err: status 0x%uX, err 0x%uX\n",
.
1243,1244c
	//if (cp->cmd == Cident2)
	//	tsleep(&cp->r, return0, 0, Hardtimeout);
.
1218c
	DPRINT("%s: ident command 0x%uX sent\n", dp->vol, cmd);
.
1129c
		DPRINT("%s: setbuf err: status 0x%uX, err 0x%uX\n",
.
1080c
		DPRINT("%s err: lblk %ld status 0x%uX, err 0x%uX\n",
.
1050a
		}
.
1048c
		while((stat = inb(cp->pbase+Pstatus) & (Serr|Sdrq)) == 0){
			microdelay(1);
.
565c
		DPRINT("ata%d: opt spindownmask 0x%uX\n", ctlrno, spindownmask);
.
500c
//skipedd:
.
492c
		DPRINT("ata%d: ATAPI slave 0x%uX 0x%uX 0x%uX\n", ctlrno, status,
.
486c
	DPRINT("ata%d: slave diag status 0x%uX, error 0x%uX\n",
		ctlr->ctlrno, status, error);
.
476c
	DPRINT("ata%d: master diag status 0x%uX, error 0x%uX\n",
.
438c
		DPRINT("ata%d: Cedd status 0x%uX/0x%uX/0x%uX\n", ctlrno,
.
427c
	DPRINT("ata%d: ATAPI 0x%uX 0x%uX 0x%uX\n", ctlrno, status,
.
417c
			DPRINT("ata%d: Srst status 0x%uX/0x%uX/0x%uX\n", ctlrno,
.
396a

.
386c
		DPRINT("ata%d: DHmagic not ok == 0x%uX\n", ctlrno, status);
.
338c
	DPRINT("ata%d: ctlrwait failed 0x%uX\n", ctlr->ctlrno, status);
.
## diffname pc/devata.c 1998/1101
## diff -e /n/emeliedump/1998/1007/sys/src/brazil/pc/devata.c /n/emeliedump/1998/1101/sys/src/brazil/pc/devata.c
427a
	status = inb(port+Pstatus);
.
420a
			return -1;
.
397d
## diffname pc/devata.c 1998/1126
## diff -e /n/emeliedump/1998/1101/sys/src/brazil/pc/devata.c /n/emeliedump/1998/1126/sys/src/brazil/pc/devata.c
2136c
	atareset,
.
2126c
			cp->error = inb(cp->cmdport+Perror);
.
2118c
			inss(cmdport+Pdata, cp->buf+cp->count, count/2);
.
2116c
			outss(cmdport+Pdata, cp->buf+cp->count, count/2);
.
2112c
		count = inb(cmdport+Pcyllsb)|(inb(cmdport+Pcylmsb)<<8);
.
2106c
			cp->error = inb(cmdport+Perror);
.
2101c
			cp->status = inb(cmdport+Pstatus);
.
2090c
				cp->error = inb(cmdport+Perror);
.
2081c
		outss(cmdport+Pdata, cp->cmdblk, sizeof(cp->cmdblk)/2);
.
2077c
			cp->error = inb(cmdport+Perror);
.
2068,2069c
	cmdport = cp->cmdport;
	cause = inb(cmdport+Pcount) & 0x03;
.
2066c
	int count, loop, cmdport;
.
1820c
		outss(cp->cmdport+Pdata, cp->cmdblk, sizeof(cp->cmdblk)/2);
.
1818c
				dp->vol, cp->cmd, inb(cp->cmdport+Pstatus));
.
1813c
		for(loop = 0; (inb(cp->cmdport+Pstatus) & (Serr|Sdrq)) == 0; loop++){
.
1803,1809c
	outb(cp->cmdport+Pcount, 0);
	outb(cp->cmdport+Psector, 0);
	outb(cp->cmdport+Pfeature, 0);
	outb(cp->cmdport+Pcyllsb, cp->len);
	outb(cp->cmdport+Pcylmsb, cp->len>>8);
	outb(cp->cmdport+Pdh, dp->dh);
	outb(cp->cmdport+Pcmd, cp->cmd);
.
1775c
			dp->vol, inb(cp->cmdport+Pcylmsb), inb(cp->cmdport+Pcyllsb));
.
1773c
	if(inb(cp->cmdport+Pcylmsb) != 0xEB || inb(cp->cmdport+Pcyllsb) != 0x14){
.
1769,1770c
	if(inb(cp->cmdport+Pstatus)){
		DPRINT("%s: isatapi status 0x%uX\n", dp->vol, inb(cp->cmdport+Pstatus));
.
1763c
		DPRINT("%s: isatapi ctlrwait status 0x%uX\n", dp->vol, inb(cp->cmdport+Pstatus));
.
1761c
	outb(cp->cmdport+Pcmd, 0x08);
.
1759c
	outb(cp->cmdport+Pdh, dp->dh);
.
1744,1746c
			outb(cp->cmdport+Pcount, 0);
			outb(cp->cmdport+Pdh, dp->dh);
			outb(cp->cmdport+Pcmd, cp->cmd);
.
1715c
			cp->ctlrno, cp->error, inb(cp->cmdport+Pcount));
.
1711c
			cp->error = inb(cp->cmdport+Perror);
.
1688c
			inb(cp->cmdport+Pstatus);
.
1677c
			inss(cp->cmdport+Pdata, addr, dp->bytes/2);
.
1670c
			cp->error = inb(cp->cmdport+Perror);
.
1665c
			cp->status = inb(cp->cmdport+Pstatus);
.
1660c
					dp->vol, cp->cmd, inb(cp->cmdport+Pstatus));
.
1645c
				outss(cp->cmdport+Pdata, addr, dp->bytes/2);
.
1639c
						dp->vol, cp->cmd, inb(cp->cmdport+Pstatus));
.
1636c
			while(((cp->status = inb(cp->cmdport+Pstatus)) & Sdrq) == 0){
.
1629c
			cp->error = inb(cp->cmdport+Perror);
.
1618c
				cp->ctlrno, cp->cmd, cp->lastcmd, inb(cp->cmdport+Pstatus));
.
1615c
	while((cp->status = inb(cp->cmdport+Pstatus)) & Sbusy){
.
1340,1345c
	outb(cp->cmdport+Pcount, 1);
	outb(cp->cmdport+Psector, sec+1);
	outb(cp->cmdport+Pdh, dp->dh | (dp->lba<<6) | head);
	outb(cp->cmdport+Pcyllsb, cyl);
	outb(cp->cmdport+Pcylmsb, cyl>>8);
	outb(cp->cmdport+Pcmd, Cread);
.
1276,1277c
		XPRINT("%s: %d/%d/%d changed to %ld/%d/%d CHS\n",
			dp->vol,
			ip->cyls, ip->heads, ip->s2t,
			dp->cyl, dp->heads, dp->sectors);
.
1219,1220c
	outb(cp->cmdport+Pdh, dp->dh);
	outb(cp->cmdport+Pcmd, cmd);
.
1126,1128c
	outb(cp->cmdport+Pfeature, arg);
	outb(cp->cmdport+Pdh, dp->dh);
	outb(cp->cmdport+Pcmd, Cfeature);
.
1056c
		outss(cp->cmdport+Pdata, cp->buf, dp->bytes/2);
.
1051c
		while((stat = inb(cp->cmdport+Pstatus) & (Serr|Sdrq)) == 0){
.
1041,1046c
	outb(cp->cmdport+Pcount, cp->nsecs);
	outb(cp->cmdport+Psector, sec);
	outb(cp->cmdport+Pdh, dp->dh | (dp->lba<<6) | head);
	outb(cp->cmdport+Pcyllsb, cyl);
	outb(cp->cmdport+Pcylmsb, cyl>>8);
	outb(cp->cmdport+Pcmd, cmd);
.
562a
		ctlrno++;
.
561c
		if(atactlrprobe(ctlrno, devp, isa.irq, resetok))
.
557c
			else if(cistrcmp(isa.opt[i], "reset") == 0)
.
542c
			if(cistrncmp(isa.opt[i], "spindown", 8) == 0){
.
536c
		if(isa.irq == 0 && (isa.irq = devp->irq) == 0)
.
534c
		isaconfig("ata", ctlrno, &isa);
		if(isa.port && isa.port != devp->cmdport)
.
529,532c
	ctlrno = 0;
	for(devno = 0; devno < natadev; devno++){
		devp = &atadev[devno];
.
527a
	Atadev *devp;
.
526c
	int ctlrno, devno, driveno, i, resetok, slave, spindown;
.
519a
	print("#H%d: cmdport 0x%uX ctlport 0x%uX irq %d mask 0x%uX atapi 0x%uX\n",
		ctlrno, cmdport, ctlport,  irq, mask, atapi);
	intrenable(irq, ataintr, ctlr, ctlr->tbdf);
	inb(cmdport+Pstatus);
	outb(ctlport+Pctl, 0);
	if(devp->ienable)
		devp->ienable(devp);

.
503c
//skipslave:
.
501a
	outb(cmdport+Pdh, DHmagic);
.
496c
			inb(cmdport+Pcylmsb), inb(cmdport+Pcyllsb));
.
493,494c
		msb = inb(cmdport+Pcylmsb);
		lsb = inb(cmdport+Pcyllsb);
.
486,487c
	status = inb(cmdport+Pstatus);
	error = inb(cmdport+Perror);
.
484c
	outb(cmdport+Pdh, DHmagic|DHslave);
.
479c
		ctlr->ctlrno, inb(cmdport+Pstatus), error);
.
477c
	error = inb(cmdport+Perror);
.
453,455c
	outb(cmdport+Pcmd, Cedd);
	microdelay(1);
	status = inb(cmdport+Pstatus);
	if(!(status & Sbusy)){
		DPRINT("ata%d: !busy 1 0x%uX\n", ctlrno, status);
		xfree(ctlr);
		return -1;
	}
	for(timo = 6000; timo; timo--){
		status = inb(cmdport+Pstatus);
		if(!(status & Sbusy))
			break;
		delay(1);
	}
	DPRINT("ata%d: timo %d\n", ctlrno, 6000-timo);
	status = inb(cmdport+Pstatus);
	if(status & Sbusy){
		DPRINT("ata%d: busy 2 0x%uX\n", ctlrno, status);
		xfree(ctlr);
		return -1;
	}
.
450,451c

	/*
	 * Can only get here if controller is not busy.
	 * If there are drives Sbusy will be set within 400nS.
	 * Wait for the command to complete (6 seconds max).
	 */
.
441c
			inb(cmdport+Pstatus), inb(cmdport+Pcylmsb), inb(cmdport+Pcyllsb));
.
439c
	if(atactlrwait(ctlr, DHmagic, 0, MS2TK(1))){
.
433,434d
430,431c
		inb(cmdport+Pcylmsb), inb(cmdport+Pcyllsb));
	USED(status);
	if(/*status == 0 &&*/ inb(cmdport+Pcylmsb) == 0xEB && inb(cmdport+Pcyllsb) == 0x14){
.
428c
	status = inb(cmdport+Pstatus);
.
423a
	/*
	 * Disable interrupts.
	 */
	outb(ctlport+Pctl, nIEN);

.
418c
				inb(cmdport+Pstatus), inb(cmdport+Pcylmsb), inb(cmdport+Pcyllsb));
.
415c
		outb(ctlport+Pctl, 0);
.
412,413c
	if(resetok && ctlport){
		outb(ctlport+Pctl, Srst|nIEN);
.
403,406d
392c
	ctlr->cmdport = cmdport;
	ctlr->ctlport = ctlport;
.
386c
		DPRINT("ata%d: DHmagic not ok == 0x%uX, 0x%uX\n",
			ctlrno, status, inb(cmdport+Pstatus));
.
381,384c
	DPRINT("ata%d: port 0x%uX\n", ctlrno, cmdport);
	outb(cmdport+Pdh, DHmagic);
	for(timo = 30000; timo; timo--){
		microdelay(1);
		status = inb(cmdport+Pdh);
		if(status == DHmagic)
			break;
	}
	status = inb(cmdport+Pdh);
.
377,378d
373a
	cmdport = devp->cmdport;
	ctlport = devp->ctlport;

.
371c
	int atapi, cmdport, ctlport, mask, once, timo;
.
368c
atactlrprobe(int ctlrno, Atadev* devp, int irq, int resetok)
.
321c
	port = ctlr->cmdport;
.
301,311c
	p = nil;
	while(p = pcimatch(p, 0, 0)){
		if(p->vid == 0x1095 && p->did == 0x0640){
			/*
			 * CMD640B dual PCI controllers. Amongst other
			 * bugs only one of the controllers can be active at a time.
			 * Unfortunately there's no way to tell which pair of
			 * controllers this is, so if one is found then all controller
			 * pairs are synchronised.
			 */
			have640b++;

			/*
			 * Make sure read-ahead is disabled on all
			 * drives and that the 2nd controller is enabled:
			 *   reg 0x51:	bit 7 - drive 1 read ahead disable
			 *  		bit 6 - drive 0 read ahead disable
			 *  		bit 3 - 2nd controller enable
			 *   reg 0x57:	bit 3 - drive 1 read ahead disable
			 *  		bit 2 - drive 0 read ahead disable
			 */
			r = pcicfgr8(p, 0x51);
			r |= 0xC8;
			pcicfgw8(p, 0x51, r);
			r = pcicfgr8(p, 0x57);
			r |= 0x0C;
			pcicfgw8(p, 0x57, r);
		}
		else if(p->vid == 0x1042 && (p->did == 0x1000 || p->did == 0x1001)){
			/*
			 * PC-Tech RZ1000 controllers.
			 * Turn off prefetch.
			 * This is overkill, but cheap.
			 */
			r = pcicfgr32(p, 0x40);
			r &= ~0x2000;
			pcicfgw32(p, 0x40, r);
		}
		else if(p->vid == 0x100B && p->did == 0x0002){
			/*
			 * National Semiconductor PC87415.
			 * Disable interrupts on both channels until
			 * after they are probed for drives.
			 * This must be called before interrupts are
			 * enabled in case the IRQ is being shared.
			 */
			pcicfgw32(p, 0x40, 0x00000300);

			/*
			 * Add any native-mode channels to the list to
			 * be probed.
			 */
			ccrp = pcicfgr8(p, PciCCRp);
			if((ccrp & 0x01) && natadev < nelem(atadev)){
				atadev[natadev].cmdport = p->mem[0].bar & ~0x01;
				atadev[natadev].ctlport = p->mem[1].bar & ~0x01;
				atadev[natadev].irq = p->intl;
				atadev[natadev].p = p;
				atadev[natadev].ienable = pc87415ienable;
				natadev++;
			}
			if((ccrp & 0x04) && natadev < nelem(atadev)){
				atadev[natadev].cmdport = p->mem[2].bar & ~0x01;
				atadev[natadev].ctlport = p->mem[3].bar & ~0x01;
				atadev[natadev].irq = p->intl;
				atadev[natadev].p = p;
				atadev[natadev].ienable = pc87415ienable;
				natadev++;
			}
		}
.
299c
	int ccrp, r;
.
296c
atareset(void)
.
267,292c
	p = devp->p;
	if(p == nil)
		return;

	x = pcicfgr32(p, 0x40);
	if(devp->cmdport == (p->mem[0].bar & ~0x01))
		x &= ~0x00000100;
	else
		x &= ~0x00000200;
	pcicfgw32(p, 0x40, x);
.
265c
	int x;
.
262c
pc87415ienable(Atadev* devp)
.
209,211c
static int natadev = 4;
.
206,207c

typedef struct Atadev Atadev;
typedef struct Atadev {
	int	cmdport;
	int	ctlport;
	int	irq;

	Pcidev*	p;
	void	(*ienable)(Atadev*);
} Atadev;

static void pc87415ienable(Atadev*);

static Atadev atadev[NCtlr] = {
	{ 0x1F0, 0x3F4, 14, },	/* primary */
	{ 0x170, 0x374, 15, },	/* secondary */
	{ 0x1E8, 0x3EC,  0, },	/* tertiary */
	{ 0x168, 0x36C,  0, },	/* quaternary */
.
178c
	int	cmdport;	/* base port */
	int	ctlport;
.
89c
	NCtlr=		8,		/* not really */
.
53c
	Pctl=		2,	/* device control, alternate status */
.
30,33d
4a
 * The PCI hacks are truly awful.
.
## diffname pc/devata.c 1999/0324
## diff -e /n/emeliedump/1998/1126/sys/src/brazil/pc/devata.c /n/emeliedump/1999/0324/sys/src/brazil/pc/devata.c
936a

	if(off < 0)
		error(Ebadarg);
.
867a
	if(off < 0)
		error(Ebadarg);

.
## diffname pc/devata.c 1999/0714
## diff -e /n/emeliedump/1999/0324/sys/src/brazil/pc/devata.c /n/emeliedump/1999/0714/sys/src/brazil/pc/devata.c
590a
		xfree(ctlr);
		return -1;
	}
	sprint(name, "ata%dcmd", ctlrno);
	if(ioalloc(devp->cmdport, 0x8, 0, name) < 0){
		print("#H%d: cmd port %d in use", ctlrno, devp->cmdport);
		xfree(ctlr);
		return -1;
	}
	sprint(name, "ata%dctl", ctlrno);
	if(ioalloc(devp->ctlport+Pctl, 1, 0, name) < 0){
		iofree(devp->cmdport);
		print("#H%d: ctl port %d in use", ctlrno, devp->ctlport);
.
427a
	char name[13];
.
## diffname pc/devata.c 1999/0819
## diff -e /n/emeliedump/1999/0714/sys/src/brazil/pc/devata.c /n/emeliedump/1999/0819/sys/src/brazil/pc/devata.c
622c
	snprint(name, sizeof name, "ata%d", ctlrno);
	intrenable(irq, ataintr, ctlr, ctlr->tbdf, name);
.
## diffname pc/devata.c 1999/1230
## diff -e /n/emeliedump/1999/0819/sys/src/brazil/pc/devata.c /n/emeliedump/1999/1230/sys/src/9/pc/devata.c
250a
	if(s == DEVDOTDOT){
		devdir(c, (Qid){CHDIR, 0}, "#H", 0, eve, 0555, dirp);
		return 1;
	}

.
## diffname pc/devata.c 2000/0308
## diff -e /n/emeliedump/1999/1230/sys/src/9/pc/devata.c /n/emeliedump/2000/0308/sys/src/9/pc/devata.c
1692c
			if(getfields(line[i], field, 3, 1, " ") != 3)
.
1684c
		n = getfields((char*)buf, line, Npart+1, 1, "\n");
.
1672c
			n = getfields((char*)buf, line, Npart+1, 1, "\n");
.
1666c
		n = getfields((char*)buf, line, Npart+1, 1, "\n");
.
1590c
			if(getfields(line[i], field, 1, 1, " ") != 1)
.
1585c
	n = getfields((char*)buf, line, Nrepl+1, 1, "\n");
.
## diffname pc/devata.c 2000/0517 # deleted
## diff -e /n/emeliedump/2000/0308/sys/src/9/pc/devata.c /n/emeliedump/2000/0517/sys/src/9/pc/devata.c
1,2275d

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].