plan 9 kernel history: overview | file list | diff list

1998/0922/pc/devata.c (diff list | history)

pc/devata.c on 1995/1206
1995/1206    
/* 
 * This has gotten a bit messy with the addition of multiple controller 
 * and ATAPI support; needs a rewrite before adding any 'ctl' functions. 
1998/0510    
 * The register locking needs looked at. 
1995/1206    
 */ 
1995/0213    
#include	"u.h" 
#include	"../port/lib.h" 
#include	"mem.h" 
#include	"dat.h" 
#include	"fns.h" 
#include	"io.h" 
#include	"../port/error.h" 
 
1998/0510    
#define DEBUG		0 
#define DPRINT 		if(DEBUG)print 
#define XPRINT 		if(DEBUG)print 
#define ILOCK(x)	ilock(x) 
#define IUNLOCK(x)	iunlock(x) 
1995/0213    
 
typedef	struct Drive		Drive; 
typedef	struct Ident		Ident; 
typedef	struct Controller	Controller; 
typedef struct Partition	Partition; 
typedef struct Repl		Repl; 
1998/0831    
typedef struct Atapicmd		Atapicmd; 
1995/0213    
 
enum 
{ 
	/* ports */ 
1995/1206    
	Pbase0=		0x1F0,	/* primary */ 
	Pbase1=		0x170,	/* secondary */ 
	Pbase2=		0x1E8,	/* tertiary */ 
	Pbase3=		0x168,	/* quaternary */ 
1995/0213    
	Pdata=		0,	/* data port (16 bits) */ 
	Perror=		1,	/* error port (read) */ 
1995/1206    
	 Eabort=	(1<<2), 
	Pfeature=	1,	/* buffer mode port (write) */ 
1995/0213    
	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 */ 
1995/1206    
	 DHmagic=	0xA0, 
	 DHslave=	0x10, 
1995/0213    
	Pstatus=	7,	/* status port (read) */ 
	 Sbusy=		 (1<<7), 
	 Sready=	 (1<<6), 
1995/1206    
	 Sdf=		 (1<<5), 
1995/0213    
	 Sdrq=		 (1<<3), 
	 Serr=		 (1<<0), 
	Pcmd=		7,	/* cmd port (write) */ 
 
1996/0210    
	Pctrl=		0x206,	/* device control, alternate status */ 
	 nIEN=		(1<<1), 
	 Srst=		(1<<2), 
 
1995/0213    
	/* commands */ 
1995/1206    
	Cfirst=		0xFF,	/* pseudo command for initialisation */ 
1995/0213    
	Cread=		0x20, 
	Cwrite=		0x30, 
1995/1206    
	Cedd=		0x90,	/* execute device diagnostics */ 
1995/0213    
	Cident=		0xEC, 
1995/1206    
	Cident2=	0xFE,	/* pseudo command for post Cident interrupt */ 
	Cfeature=	0xEF, 
1995/0213    
 
	Cstandby=	0xE2, 
 
1995/1206    
	Cpktcmd=	0xA0, 
	Cidentd=	0xA1, 
1996/0112    
	Ctur=		0x00, 
	Creqsense=	0x03, 
1995/1206    
	Ccapacity=	0x25, 
	Cread2=		0x28, 
1998/0219    
	Cwrite2=	0x2A, 
1995/1206    
 
1995/0213    
	/* disk states */ 
	Sspinning, 
	Sstandby, 
 
	/* file types */ 
	Qdir=		0, 
 
	Maxxfer=	BY2PG,		/* maximum transfer size/cmd */ 
1995/1206    
	Npart=		20+2,		/* 8 sub partitions, disk, and partition */ 
1995/0213    
	Nrepl=		64,		/* maximum replacement blocks */ 
1995/0818    
 
1995/1208    
	Hardtimeout=	6000,		/* disk access timeout (ms) */ 
1998/0831    
	Atapitimeout=	10000,		/* disk access timeout (ms) */ 
1995/1206    
	NCtlr=		4, 
	NDrive=		NCtlr*2, 
1998/0831    
 
	/* cd files */ 
	CDdisk = 0, 
	CDcmd, 
	CDdata, 
	CDmax, 
1995/0213    
}; 
1995/1206    
 
1995/0213    
#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" 
 
/* 
1998/0831    
 * the result of the last user-invoked atapi cmd 
 */ 
struct Atapicmd 
{ 
	QLock; 
	int	pid; 
	ushort	status; 
	ushort	error; 
	uchar	cmdblk[12]; 
}; 
 
/* 
1995/0213    
 *  an ata drive 
 */ 
struct Drive 
{ 
	QLock; 
 
	Controller *cp; 
1995/1206    
	uchar	driveno; 
	uchar	dh;		/* DHmagic|Am-I-A-Slave */ 
	uchar	atapi; 
	uchar	online; 
 
1995/0213    
	int	npart;		/* number of real partitions */ 
1995/1206    
	int	partok; 
1995/0213    
	Partition p[Npart]; 
	Repl	repl; 
	ulong	usetime; 
	int	state; 
	char	vol[NAMELEN]; 
 
1998/0327    
	vlong	cap;		/* total bytes */ 
1995/0213    
	int	bytes;		/* bytes/sector */ 
	int	sectors;	/* sectors/track */ 
	int	heads;		/* heads/cyl */ 
	long	cyl;		/* cylinders/drive */ 
1995/1206    
	ulong	lbasecs; 
1995/0213    
 
1995/1206    
	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; 
1998/0831    
	Atapicmd atapicmd; 
1995/0213    
}; 
 
/* 
 *  a controller for 2 drives 
 */ 
struct Controller 
{ 
1995/1206    
	QLock*	ctlrlock;	/* exclusive access to the controller */ 
1995/0213    
 
1995/0214    
	Lock	reglock;	/* exclusive access to the registers */ 
1995/0213    
 
	int	pbase;		/* base port */ 
1995/1206    
	uchar	ctlrno; 
1997/0327    
	int	tbdf; 
1995/0213    
 
	/* 
	 *  current operation 
	 */ 
	Rendez	r;		/* wait here for command termination */ 
1995/1206    
	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 */ 
1995/0213    
	int	nsecs;		/* length of transfer (sectors) */ 
	int	sofar;		/* sectors transferred so far */ 
1995/1206    
	Drive*	dp;		/* drive being accessed */ 
1995/0213    
}; 
 
1995/1206    
static QLock ataprobelock; 
static int ataprobedone; 
static Controller *atactlr[NCtlr]; 
static QLock atactlrlock[NCtlr]; 
static Drive *atadrive[NDrive]; 
static int spindownmask; 
1997/0327    
static int have640b; 
1995/1206    
static int pbase[NCtlr] = { 
	Pbase0, Pbase1, Pbase2, Pbase3, 
}; 
static int defirq[NCtlr] = { 
	14, 15, 0, 0, 
}; 
1995/0213    
 
static void	ataintr(Ureg*, void*); 
1998/0327    
static long	ataxfer(Drive*, Partition*, int, vlong, long, uchar*); 
1995/0213    
static void	ataident(Drive*); 
1995/1206    
static void	atafeature(Drive*, uchar); 
1995/0213    
static void	ataparams(Drive*); 
static void	atapart(Drive*); 
static int	ataprobe(Drive*, int, int, int); 
1995/1209    
static void	atasleep(Controller*, int); 
1997/0327    
static void	ataclock(void); 
1995/0213    
 
1995/1206    
static int	isatapi(Drive*); 
1998/0327    
static long	atapirwio(Chan*, uchar*, ulong, vlong, int); 
1995/1206    
static void	atapipart(Drive*); 
static void	atapiintr(Controller*); 
1998/0831    
static void	atapiexec(Drive*); 
1995/1206    
 
1995/0213    
static int 
1997/0327    
atagen(Chan* c, Dirtab*, int, int s, Dir* dirp) 
1995/0213    
{ 
	Qid qid; 
	int drive; 
	char name[NAMELEN+4]; 
	Drive *dp; 
	Partition *pp; 
1998/0327    
	vlong l; 
1995/0213    
 
	qid.vers = 0; 
	drive = s/Npart; 
	s = s % Npart; 
1995/1206    
 
	if(drive >= NDrive) 
1995/0213    
		return -1; 
1995/1206    
	if(atadrive[drive] == 0) 
		return 0; 
	dp = atadrive[drive]; 
1995/0213    
 
1995/0215    
	if(dp->online == 0 || s >= dp->npart) 
1995/0213    
		return 0; 
 
	pp = &dp->p[s]; 
	sprint(name, "%s%s", dp->vol, pp->name); 
	name[NAMELEN] = 0; 
	qid.path = MKQID(drive, s); 
1998/0327    
	l = (pp->end - pp->start) * (vlong)dp->bytes; 
1995/0213    
	devdir(c, qid, name, l, eve, 0660, dirp); 
	return 1; 
} 
 
1995/1206    
static void 
cmd640b(void) 
1995/0213    
{ 
1997/0327    
	Pcidev *p; 
	int r; 
1995/0213    
 
1995/1206    
	/* 
	 * 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. 
	 */ 
1997/0327    
	p = 0; 
	while(p = pcimatch(p, 0x1095, 0x0640)){ 
		have640b++; 
1995/1206    
		/* 
		 * 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 
		 */ 
1997/0327    
		r = pcicfgr8(p, 0x51); 
		r |= 0xC8; 
		pcicfgw8(p, 0x51, r); 
		r = pcicfgr8(p, 0x57); 
		r |= 0x0C; 
		pcicfgw8(p, 0x57, r); 
1995/1206    
	} 
} 
1995/0213    
 
1995/1206    
static void 
rz1000(void) 
{ 
1997/0327    
	Pcidev *p; 
	ulong r; 
1995/0215    
 
1995/1206    
	/* 
	 * Look for PC-Tech RZ1000 controllers and turn off prefetch. 
	 * This is overkill, but cheap. 
	 */ 
1997/0327    
	p = 0; 
	while(p = pcimatch(p, 0x1042, 0)){ 
		if(p->did != 0x1000 && p->did != 0x1001) 
1995/1206    
			continue; 
1997/0327    
		r = pcicfgr32(p, 0x40); 
		r &= ~0x2000; 
		pcicfgw32(p, 0x40, r); 
1995/1206    
	} 
} 
1995/0215    
 
1995/1206    
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; 
1995/0213    
	} 
1995/1206    
	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; 
1995/1208    
	if(atapi){ 
		sprint(drive->vol, "atapi%d", drive->driveno); 
1995/1206    
		drive->atapi = 1; 
1995/1208    
	} 
1995/1206    
 
	atadrive[driveno] = drive; 
} 
 
static int 
1997/0815    
atactlrprobe(int ctlrno, int irq, int resetok) 
1995/1206    
{ 
	Controller *ctlr; 
1998/0331    
	int atapi, mask, once, port; 
1996/0210    
	uchar error, status, msb, lsb; 
1995/1206    
 
	/* 
	 * 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); 
1997/0815    
	status = inb(port+Pdh) & 0xFF; 
	if(status != DHmagic){ 
		DPRINT("ata%d: DHmagic not ok == %ux\n", ctlrno, status); 
1995/1206    
		return -1; 
1995/0215    
	} 
1995/1206    
	DPRINT("ata%d: DHmagic ok\n", ctlrno); 
	if((ctlr = xalloc(sizeof(Controller))) == 0) 
		return -1; 
	ctlr->pbase = port; 
	ctlr->ctlrno = ctlrno; 
1997/0327    
	ctlr->tbdf = BUSUNKNOWN; 
1995/1206    
	ctlr->lastcmd = 0xFF; 
 
1996/0210    
 
1995/1206    
	/* 
	 * 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 
1996/0210    
	 * seem to implement the control-block registers; do it if requested. 
1995/1206    
	 * 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 
1995/1208    
	 * to the Cedd (Micronics M54Li + Sanyo CRD-254P) so let's check for the 
1995/1206    
	 * ATAPI signature straight off. If we find it there will be no probe 
	 * done for a slave. Tough. 
	 */ 
1997/0815    
	if(resetok){ 
1996/0210    
		outb(port+Pctrl, Srst|nIEN); 
1996/0215    
		delay(10); 
1996/0210    
		outb(port+Pctrl, 0); 
1996/0216    
		if(atactlrwait(ctlr, DHmagic, 0, MS2TK(20))){ 
1996/0210    
			DPRINT("ata%d: Srst status %ux/%ux/%ux\n", ctlrno, 
				inb(port+Pstatus), inb(port+Pcylmsb), inb(port+Pcyllsb)); 
			xfree(ctlr); 
		} 
	} 
 
1998/0331    
	once = 1; 
retry: 
1995/1206    
	atapi = 0; 
	mask = 0; 
	DPRINT("ata%d: ATAPI %uX %uX %uX\n", ctlrno, status, 
		inb(port+Pcylmsb), inb(port+Pcyllsb)); 
1998/0331    
	if(/*status == 0 &&*/ inb(port+Pcylmsb) == 0xEB && inb(port+Pcyllsb) == 0x14){ 
1995/1206    
		DPRINT("ata%d: ATAPI ok\n", ctlrno); 
1998/0331    
		if(once) 
			intrenable(irq, ataintr, ctlr, ctlr->tbdf); 
1995/1206    
		atapi |= 0x01; 
		mask |= 0x01; 
1998/0219    
		goto atapislave; 
1995/1206    
	} 
	if(atactlrwait(ctlr, DHmagic, 0, MS2TK(1)) || waserror()){ 
1996/0210    
		DPRINT("ata%d: Cedd status %ux/%ux/%ux\n", ctlrno, 
			inb(port+Pstatus), inb(port+Pcylmsb), inb(port+Pcyllsb)); 
1998/0331    
		if(once){ 
			once = 0; 
			ctlr->cmd = 0; 
			goto retry; 
		} 
1995/1206    
		xfree(ctlr); 
		return -1; 
	} 
1998/0331    
	if(once) 
		intrenable(irq, ataintr, ctlr, ctlr->tbdf); 
1995/1206    
	ctlr->cmd = Cedd; 
	outb(port+Pcmd, Cedd); 
1995/1209    
	atasleep(ctlr, Hardtimeout); 
1995/1206    
	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. 
1998/0219    
	 * When checking status, mask off the IDX bit. 
1995/1206    
	 */ 
	error = inb(port+Perror); 
1998/0219    
	DPRINT("ata%d: master diag status %uX, error %ux\n", 
		ctlr->ctlrno, inb(port+Pstatus), error); 
1995/1206    
	if((error & ~0x80) == 0x01) 
		mask |= 0x01; 
 
1998/0106    
atapislave: 
1995/1206    
	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); 
1998/0219    
	if((status & ~0x02) && (status & (Sbusy|Serr)) == 0 && (error & ~0x80) == 0x01) 
1995/1206    
		mask |= 0x02; 
1996/0210    
	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; 
		} 
1995/1206    
	} 
 
skipedd: 
	if(mask == 0){ 
		xfree(ctlr); 
		return -1; 
	} 
	atactlr[ctlrno] = ctlr; 
 
1997/0327    
	if(have640b && (ctlrno & 0x01)) 
1995/1206    
		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; 
} 
 
1997/0327    
static void 
1995/1206    
atactlrreset(void) 
{ 
1997/0815    
	int ctlrno, driveno, i, resetok, slave, spindown; 
1995/1206    
	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; 
 
1997/0815    
		driveno = resetok = spindown = 0; 
1995/1206    
		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; 
				if((spindown = strtol(&isa.opt[i][10], 0, 0)) == 0) 
					continue; 
				if(spindown < (Hardtimeout+2000)/1000) 
					spindown = (Hardtimeout+2000)/1000; 
1997/0815    
				driveno = ctlrno*2+slave; 
1995/1206    
			} 
1996/0210    
			else if(strcmp(isa.opt[i], "reset") == 0) 
1997/0815    
				resetok = 1; 
1995/1206    
		} 
1997/0815    
 
1998/0910    
		if(atactlrprobe(ctlrno, isa.irq, resetok)) 
1997/0815    
			continue; 
 
		if(spindown == 0 || atadrive[driveno] == 0) 
			continue; 
		atadrive[driveno]->spindown = spindown; 
		spindownmask |= (1<<driveno); 
		DPRINT("ata%d: opt spindownmask %ux\n", ctlrno, spindownmask); 
1995/1206    
	} 
1995/0213    
 
1997/0327    
	if(spindownmask) 
		addclock0link(ataclock); 
1995/1206    
} 
 
1995/0213    
/* 
 *  Get the characteristics of each drive.  Mark unresponsive ones 
 *  off line. 
 */ 
1997/0327    
static Chan* 
ataattach(char* spec) 
1995/0213    
{ 
1995/1206    
	int driveno; 
1995/0213    
	Drive *dp; 
 
1995/1206    
	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; 
1995/0213    
		if(waserror()){ 
			dp->online = 0; 
			qunlock(dp); 
			continue; 
		} 
		qlock(dp); 
		if(!dp->online){ 
			ataparams(dp); 
			dp->online = 1; 
1995/1206    
			atafeature(dp, 0xAA);	/* read look ahead */ 
1995/0213    
		} 
 
		/* 
		 *  read Plan 9 partition table 
		 */ 
1995/1206    
		if(dp->partok == 0){ 
			if(dp->atapi) 
				atapipart(dp); 
			else 
				atapart(dp); 
		} 
1995/0213    
		qunlock(dp); 
		poperror(); 
	} 
	return devattach('H', spec); 
} 
 
1997/0327    
static int 
atawalk(Chan* c, char* name) 
1995/0213    
{ 
	return devwalk(c, name, 0, 0, atagen); 
} 
 
1997/0327    
static void 
atastat(Chan* c, char* dp) 
1995/0213    
{ 
	devstat(c, dp, 0, 0, atagen); 
} 
 
1997/0327    
static Chan* 
ataopen(Chan* c, int omode) 
1995/0213    
{ 
	return devopen(c, omode, 0, 0, atagen); 
} 
 
void 
1997/0327    
ataclose(Chan* c) 
1995/0213    
{ 
1995/1206    
	Drive *dp; 
1995/0213    
	Partition *p; 
1998/0831    
	Atapicmd *acmd; 
1995/0213    
 
	if(c->mode != OWRITE && c->mode != ORDWR) 
		return; 
 
1995/1206    
	dp = atadrive[DRIVE(c->qid.path)]; 
1996/0803    
	if(dp == 0) 
		return; 
1995/1206    
	p = &dp->p[PART(c->qid.path)]; 
1998/0831    
	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; 
	} 
1995/0213    
	if(strcmp(p->name, "partition") != 0) 
		return; 
 
	if(waserror()){ 
1995/1206    
		qunlock(dp); 
1995/0213    
		nexterror(); 
	} 
1995/1206    
	qlock(dp); 
	dp->partok = 0; 
	atapart(dp); 
	qunlock(dp); 
1995/0213    
	poperror(); 
} 
 
1997/0327    
static long 
1998/0319    
ataread(Chan* c, void* a, long n, vlong off) 
1995/0213    
{ 
	Drive *dp; 
	long rv, i; 
	int skip; 
	uchar *aa = a; 
	Partition *pp; 
1995/1206    
	uchar *buf; 
1998/0831    
	Atapicmd *acmd; 
	Controller *cp; 
1995/0213    
 
	if(c->qid.path == CHDIR) 
		return devdirread(c, a, n, 0, 0, atagen); 
 
1995/1206    
	dp = atadrive[DRIVE(c->qid.path)]; 
	if(dp->atapi){ 
1998/0831    
		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); 
1995/1206    
			qunlock(dp); 
1998/0831    
			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; 
1995/1206    
		} 
	} 
	pp = &dp->p[PART(c->qid.path)]; 
 
1995/0213    
	buf = smalloc(Maxxfer); 
	if(waserror()){ 
		free(buf); 
		nexterror(); 
	} 
 
1998/0327    
	skip = off % dp->bytes; 
1995/0213    
	for(rv = 0; rv < n; rv += i){ 
1998/0516    
		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); 
1995/0213    
		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; 
} 
 
1997/0327    
static long 
1998/0319    
atawrite(Chan *c, void *a, long n, vlong off) 
1995/0213    
{ 
	Drive *dp; 
	long rv, i, partial; 
	uchar *aa = a; 
	Partition *pp; 
1995/1206    
	uchar *buf; 
1998/0831    
	Atapicmd *acmd; 
1995/0213    
 
	if(c->qid.path == CHDIR) 
		error(Eisdir); 
 
1995/1206    
	dp = atadrive[DRIVE(c->qid.path)]; 
1998/0219    
	if(dp->atapi){ 
1998/0831    
		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); 
1998/0219    
			qunlock(dp); 
1998/0831    
			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); 
1998/0219    
		} 
	} 
1995/0213    
	pp = &dp->p[PART(c->qid.path)]; 
1995/1206    
 
1995/0213    
	buf = smalloc(Maxxfer); 
	if(waserror()){ 
		free(buf); 
		nexterror(); 
	} 
 
	/* 
	 *  if not starting on a sector boundary, 
	 *  read in the first sector before writing 
	 *  it out. 
	 */ 
1998/0327    
	partial = off % dp->bytes; 
1995/0213    
	if(partial){ 
1998/0219    
		if(dp->atapi) 
1998/0327    
			atapirwio(c, buf, dp->bytes, off-partial, Cread2); 
1998/0219    
		else 
1998/0327    
			ataxfer(dp, pp, Cread, off-partial, dp->bytes, buf); 
1995/0213    
		if(partial+n > dp->bytes) 
			rv = dp->bytes - partial; 
		else 
			rv = n; 
		memmove(buf+partial, aa, rv); 
1998/0219    
		if(dp->atapi) 
1998/0327    
			atapirwio(c, buf, dp->bytes, off-partial, Cwrite2); 
1998/0219    
		else 
1998/0327    
			ataxfer(dp, pp, Cwrite, off-partial, dp->bytes, buf); 
1995/0213    
	} 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); 
1998/0219    
		if(dp->atapi) 
1998/0327    
			i = atapirwio(c, buf, i, off+rv, Cwrite2); 
1998/0219    
		else 
1998/0327    
			i = ataxfer(dp, pp, Cwrite, off+rv, i, buf); 
1995/0213    
		if(i == 0) 
			break; 
	} 
 
	/* 
	 *  if not ending on a sector boundary, 
	 *  read in the last sector before writing 
	 *  it out. 
	 */ 
	if(partial){ 
1998/0219    
		if(dp->atapi) 
1998/0327    
			atapirwio(c, buf, dp->bytes, off+rv, Cread2); 
1998/0219    
		else 
1998/0327    
			ataxfer(dp, pp, Cread, off+rv, dp->bytes, buf); 
1995/0213    
		memmove(buf, aa+rv, partial); 
1998/0219    
		if(dp->atapi) 
1998/0327    
			atapirwio(c, buf, dp->bytes, off+rv, Cwrite2); 
1998/0219    
		else 
1998/0327    
			ataxfer(dp, pp, Cwrite, off+rv, dp->bytes, buf); 
1995/0213    
		rv += partial; 
	} 
 
	free(buf); 
	poperror(); 
 
	return rv; 
} 
 
/* 
 *  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. 
 */ 
1995/1206    
static int 
1995/0213    
cmdreadywait(Drive *dp) 
{ 
1995/1206    
	ulong ticks; 
	uchar ready; 
1995/0213    
 
	/* give it 2 seconds to spin down and up */ 
1995/1206    
	dp->usetime = m->ticks; 
1995/0213    
	if(dp->state == Sspinning) 
1995/1206    
		ticks = MS2TK(10); 
1995/0213    
	else 
1995/1206    
		ticks = MS2TK(2000); 
1995/0213    
 
1995/1206    
	if(dp->atapi) 
		ready = 0; 
	else 
		ready = Sready; 
 
	return atactlrwait(dp->cp, dp->dh, ready, ticks); 
1995/0213    
} 
 
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) 
1998/0825    
			DPRINT("%s: found bblk %ld at offset %d\n", 
1998/0327    
			dp->vol, bblk, i); 
1995/0213    
	} 
} 
 
1995/0818    
static void 
1995/1209    
atasleep(Controller *cp, int ms) 
1995/0818    
{ 
1995/1209    
	tsleep(&cp->r, cmddone, cp, ms); 
1995/0818    
	if(cp->cmd && cp->cmd != Cident2){ 
1995/1206    
		DPRINT("ata%d: cmd 0x%uX timeout\n", cp->ctlrno, cp->cmd); 
1995/0818    
		error("ata drive timeout"); 
	} 
} 
 
 
1995/0213    
/* 
 *  transfer a number of sectors.  ataintr will perform all the iterative 
 *  parts. 
 */ 
static long 
1998/0327    
ataxfer(Drive *dp, Partition *pp, int cmd, vlong off, long len, uchar *buf) 
1995/0213    
{ 
	Controller *cp; 
	long lblk; 
	int cyl, sec, head; 
	int loop, stat; 
1998/0327    
	ulong start; 
1995/0213    
 
	if(dp->online == 0) 
		error(Eio); 
 
	/* 
	 *  cut transfer size down to disk buffer size 
	 */ 
1998/0327    
	start = off / dp->bytes; 
1995/0213    
	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); 
	} 
 
1998/0825    
	XPRINT("%s: ataxfer cyl %d sec %d head %d len %ld\n", 
1998/0327    
		dp->vol, cyl, sec, head, len); 
1995/0818    
 
1995/0213    
	cp = dp->cp; 
1995/1206    
	qlock(cp->ctlrlock); 
1995/0213    
	if(waserror()){ 
1995/1206    
		cp->dp = 0; 
1995/0213    
		cp->buf = 0; 
1995/1206    
		qunlock(cp->ctlrlock); 
1995/0213    
		nexterror(); 
	} 
 
1995/1206    
	if(cmdreadywait(dp)){ 
		error(Eio); 
	} 
1995/0213    
 
1995/1206    
	ILOCK(&cp->reglock); 
1995/0213    
	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); 
1995/1206    
	outb(cp->pbase+Pdh, dp->dh | (dp->lba<<6) | head); 
1995/0213    
	outb(cp->pbase+Pcyllsb, cyl); 
	outb(cp->pbase+Pcylmsb, cyl>>8); 
	outb(cp->pbase+Pcmd, cmd); 
 
	if(cmd == Cwrite){ 
		loop = 0; 
1995/1206    
		microdelay(1); 
1995/0213    
		while((stat = inb(cp->pbase+Pstatus) & (Serr|Sdrq)) == 0) 
			if(++loop > 10000) 
1995/1206    
				panic("%s: ataxfer", dp->vol); 
1995/0213    
		outss(cp->pbase+Pdata, cp->buf, dp->bytes/2); 
	} else 
		stat = 0; 
1995/1206    
	IUNLOCK(&cp->reglock); 
1995/0213    
 
	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()){ 
1995/1206    
		DPRINT("%s: interrupted ataxfer\n", dp->vol); 
1995/0213    
		if(loop++ > 10){ 
1995/1206    
			print("%s: disk error\n", dp->vol); 
1995/0213    
			nexterror(); 
		} 
	} 
1995/1209    
	atasleep(cp, Hardtimeout); 
1995/0213    
	dp->usetime = m->ticks; 
1995/1206    
	dp->state = Sspinning; 
1995/0213    
	poperror(); 
	if(loop) 
		nexterror(); 
 
	if(cp->status & Serr){ 
1998/0825    
		DPRINT("%s err: lblk %ld status %ux, err %ux\n", 
1995/1206    
			dp->vol, lblk, cp->status, cp->error); 
1995/0213    
		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); 
	} 
1995/1206    
	cp->dp = 0; 
1995/0213    
	cp->buf = 0; 
	len = cp->sofar*dp->bytes; 
1995/1206    
	qunlock(cp->ctlrlock); 
1995/0213    
	poperror(); 
 
	return len; 
} 
 
/* 
 *  set read ahead mode 
 */ 
static void 
1995/1206    
atafeature(Drive *dp, uchar arg) 
1995/0213    
{ 
	Controller *cp = dp->cp; 
 
1995/1206    
	if(dp->atapi) 
		return; 
 
	qlock(cp->ctlrlock); 
1995/0213    
	if(waserror()){ 
1995/1206    
		cp->dp = 0; 
		qunlock(cp->ctlrlock); 
1995/0213    
		nexterror(); 
	} 
 
1995/1206    
	if(cmdreadywait(dp)){ 
		error(Eio); 
	} 
1995/0213    
 
1995/1206    
	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); 
1995/0213    
 
1995/1209    
	atasleep(cp, Hardtimeout); 
1995/0213    
 
1995/1206    
	if(cp->status & Serr) 
1998/0825    
		DPRINT("%s: setbuf err: status %ux, err %ux\n", 
1995/1206    
			dp->vol, cp->status, cp->error); 
1995/0213    
 
1995/1206    
	cp->dp = 0; 
1995/0213    
	poperror(); 
1995/1206    
	qunlock(cp->ctlrlock); 
1995/0213    
} 
 
/* 
 *  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; 
1995/1206    
	uchar *buf; 
1995/0213    
	Ident *ip; 
	char id[21]; 
1995/0818    
	ulong lbasecs; 
1995/1206    
	uchar cmd; 
1995/0213    
 
	cp = dp->cp; 
	buf = smalloc(Maxxfer); 
1995/1206    
	qlock(cp->ctlrlock); 
1995/0213    
	if(waserror()){ 
1995/1206    
		cp->dp = 0; 
		free(buf); 
		qunlock(cp->ctlrlock); 
1995/0213    
		nexterror(); 
	} 
 
1996/0112    
	if(dp->atapi) 
		cmd = Cidentd; 
	else 
		cmd = Cident; 
1995/1206    
retryatapi: 
	ILOCK(&cp->reglock); 
1995/0213    
	cp->nsecs = 1; 
	cp->sofar = 0; 
1995/1206    
	cp->cmd = cmd; 
1995/0213    
	cp->dp = dp; 
	cp->buf = buf; 
1995/1206    
	outb(cp->pbase+Pdh, dp->dh); 
	outb(cp->pbase+Pcmd, cmd); 
	IUNLOCK(&cp->reglock); 
1995/0213    
 
1995/1208    
	DPRINT("%s: ident command %ux sent\n", dp->vol, cmd); 
1996/0210    
	if(cmd == Cident) 
		atasleep(cp, 3000); 
	else 
1996/0215    
		atasleep(cp, 10000); 
1995/0818    
 
1995/0213    
	if(cp->status & Serr){ 
1995/1206    
		DPRINT("%s: bad disk ident status\n", dp->vol); 
1995/1208    
		if(cp->error & Eabort){ 
1995/1206    
			if(isatapi(dp)){ 
				cmd = Cidentd; 
				goto retryatapi; 
			} 
		} 
1995/0213    
		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) 
1995/0818    
		tsleep(&cp->r, return0, 0, Hardtimeout); 
1995/0213    
 
	memmove(id, ip->model, sizeof(id)-1); 
	id[sizeof(id)-1] = 0; 
 
1995/1206    
	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; 
1998/0219    
		if((ip->config & 0x1F00) == 0x0000) 
			dp->atapi = 2; 
1995/1206    
	} 
	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; 
 
	if(ip->cvalid&(1<<0)){ 
		/* use current settings */ 
		dp->cyl = ip->ccyls; 
		dp->heads = ip->cheads; 
		dp->sectors = ip->cs2t; 
1998/0825    
		XPRINT("%s: changed to %ld cyl %d head %d sec\n", 
1995/1206    
			dp->vol, dp->cyl, dp->heads, dp->sectors); 
	} 
 
1995/0818    
	lbasecs = (ip->lbasecs[0]) | (ip->lbasecs[1]<<16); 
	if((ip->capabilities & (1<<9)) && (lbasecs & 0xf0000000) == 0){ 
1995/0213    
		dp->lba = 1; 
1995/1206    
		dp->lbasecs = lbasecs; 
1998/0327    
		dp->cap = (vlong)dp->bytes * dp->lbasecs; 
1998/0825    
		XPRINT("%s: LBA: %s %lud sectors %lld bytes\n", 
1998/0327    
			dp->vol, id, dp->lbasecs, 
			dp->cap); 
1995/0213    
	} else { 
		dp->lba = 0; 
1995/1206    
		dp->lbasecs = 0; 
1998/0327    
		dp->cap = (vlong)dp->bytes * dp->cyl * dp->heads * dp->sectors; 
1995/0213    
	} 
1998/0825    
	XPRINT("%s: %s %ld/%d/%d CHS %lld bytes\n", 
1998/0510    
		dp->vol, id, dp->cyl, dp->heads, dp->sectors, 
		dp->cap); 
1995/1206    
 
	if(cp->cmd){ 
		cp->lastcmd = cp->cmd; 
		cp->cmd = 0; 
	} 
	cp->dp = 0; 
1995/0213    
	cp->buf = 0; 
	free(buf); 
	poperror(); 
1995/1206    
	qunlock(cp->ctlrlock); 
1995/0213    
} 
 
/* 
 *  probe the given sector to see if it exists 
 */ 
static int 
ataprobe(Drive *dp, int cyl, int sec, int head) 
{ 
	Controller *cp; 
1995/1206    
	uchar *buf; 
1995/0213    
	int rv; 
 
	cp = dp->cp; 
	buf = smalloc(Maxxfer); 
1995/1206    
	qlock(cp->ctlrlock); 
1995/0213    
	if(waserror()){ 
1995/1206    
		cp->dp = 0; 
1995/0213    
		free(buf); 
1995/1206    
		qunlock(cp->ctlrlock); 
1995/0213    
		nexterror(); 
	} 
 
1995/1206    
	if(cmdreadywait(dp)){ 
		error(Eio); 
	} 
1995/0213    
 
1995/1206    
	ILOCK(&cp->reglock); 
1995/0213    
	cp->cmd = Cread; 
	cp->dp = dp; 
	cp->status = 0; 
	cp->nsecs = 1; 
	cp->sofar = 0; 
1995/0728    
	cp->buf = buf; 
1995/0213    
 
	outb(cp->pbase+Pcount, 1); 
	outb(cp->pbase+Psector, sec+1); 
1995/1206    
	outb(cp->pbase+Pdh, dp->dh | (dp->lba<<6) | head); 
1995/0213    
	outb(cp->pbase+Pcyllsb, cyl); 
	outb(cp->pbase+Pcylmsb, cyl>>8); 
	outb(cp->pbase+Pcmd, Cread); 
1995/1206    
	IUNLOCK(&cp->reglock); 
1995/0213    
 
1995/1209    
	atasleep(cp, Hardtimeout); 
1995/0213    
 
1995/1206    
	if(cp->status & Serr){ 
1998/0825    
		DPRINT("%s: probe err: status %ux, err %ux\n", 
1995/1206    
			dp->vol, cp->status, cp->error); 
1995/0213    
		rv = -1; 
1995/1206    
	} 
1995/0213    
	else 
		rv = 0; 
 
1995/1206    
	cp->dp = 0; 
1995/0213    
	cp->buf = 0; 
	free(buf); 
	poperror(); 
1995/1206    
	qunlock(cp->ctlrlock); 
1995/0213    
	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); 
1995/1206    
	if(dp->atapi) 
		return; 
1995/0213    
	if(dp->lba){ 
1995/1206    
		i = dp->lbasecs - 1; 
1995/0215    
		if(ataprobe(dp, (i>>8)&0xffff, (i&0xff)-1, (i>>24)&0xf) == 0) 
1995/0213    
			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. 
	 */ 
	dp->lba = 0; 
1995/0908    
	for(i = 0; i < 16; i++) 
1995/0213    
		if(ataprobe(dp, 0, 0, i) < 0) 
			break; 
	dp->heads = i; 
1995/0908    
	for(i = 0; i < 64; i++) 
1995/0213    
		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; 
1998/0327    
	dp->cap = (vlong)dp->bytes * dp->cyl * dp->heads * dp->sectors; 
1998/0825    
	DPRINT("%s: probed: %ld/%d/%d CHS %lld bytes\n", 
1998/0327    
		dp->vol, dp->cyl, dp->heads, dp->sectors, 
		dp->cap); 
1995/1206    
	if(dp->cyl == 0 || dp->heads == 0 || dp->sectors == 0) 
		error(Eio); 
1995/0213    
} 
 
/* 
 *  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; 
1995/1206    
	uchar *buf; 
1995/0213    
 
	/* 
	 *  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. 
	 */ 
1996/0315    
	n = parsefields((char*)buf, line, Nrepl+1, "\n"); 
1995/0213    
	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++){ 
1996/0315    
			if(parsefields(line[i], field, 1, " ") != 1) 
1995/0213    
				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]; 
1995/1206    
	char *field[3], namebuf[NAMELEN], *p; 
1995/0213    
	ulong n; 
	int i; 
1995/1206    
	uchar *buf; 
1995/0213    
 
1995/1206    
	DPRINT("%s: partok %d\n", dp->vol, dp->partok); 
1995/0213    
 
1995/1206    
	if(dp->partok) 
		return; 
 
1995/0213    
	/* 
	 *  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; 
1995/1206    
	pp++; 
1995/0213    
	dp->npart = 2; 
 
	/* 
	 * initialise the bad-block replacement info 
	 */ 
	dp->repl.p = 0; 
 
	buf = smalloc(Maxxfer); 
	if(waserror()){ 
1995/1206    
		DPRINT("%s: atapart error\n", dp->vol); 
1995/0213    
		free(buf); 
		nexterror(); 
	} 
 
1995/1206    
 
1995/0213    
	/* 
1995/1206    
	 * Check if the partitions are described in plan9.ini. 
	 * If not, read the disc. 
1995/0213    
	 */ 
1995/1206    
	sprint(namebuf, "%spartition", dp->vol); 
	if((p = getconf(namebuf)) == 0){	 
		/* 
1995/1218    
		 *  Read second last sector from disk, null terminate. 
		 *  The last sector used to hold the partition tables. 
1995/1206    
		 *  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 
1995/1218    
		 *  we still check if there is a valid partition table in 
		 *  the last sector if none is found in the second last. 
1995/1206    
		 */ 
1995/1221    
		i = 0; 
1998/0331    
		ataxfer(dp, &dp->p[0], Cread, (dp->p[0].end-2)* 
			(vlong)dp->bytes, dp->bytes, buf); 
1995/0213    
		buf[dp->bytes-1] = 0; 
1996/0315    
		n = parsefields((char*)buf, line, Npart+1, "\n"); 
1995/1221    
		if(n > 0 && strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1) == 0) 
			i = 1; 
1995/1218    
		else{ 
1995/1206    
			ataxfer(dp, &dp->p[1], Cread, 0, dp->bytes, buf); 
			buf[dp->bytes-1] = 0; 
1996/0315    
			n = parsefields((char*)buf, line, Npart+1, "\n"); 
1995/1221    
			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--; 
1995/1206    
		} 
1995/0213    
	} 
1995/1206    
	else{ 
		strcpy((char*)buf, p); 
1996/0315    
		n = parsefields((char*)buf, line, Npart+1, "\n"); 
1995/1206    
	} 
1995/0213    
 
	/* 
	 *  parse partition table. 
	 */ 
1995/1206    
	if(n > 0 && strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1) == 0){ 
1995/0213    
		for(i = 1; i < n; i++){ 
1996/0315    
			if(parsefields(line[i], field, 3, " ") != 3) 
1995/1206    
				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; 
1995/0213    
			pp++; 
		} 
	} 
1995/1206    
	dp->npart = pp - dp->p; 
1995/0213    
	free(buf); 
	poperror(); 
 
1995/1206    
	dp->partok = 1; 
 
1995/0213    
	if(dp->repl.p) 
		atareplinit(dp); 
} 
 
1995/0215    
enum 
{ 
1995/0818    
	Maxloop=	1000000, 
1995/0215    
}; 
 
1995/0213    
/* 
 *  we get an interrupt for every sector transferred 
 */ 
static void 
1995/1206    
ataintr(Ureg*, void* arg) 
1995/0213    
{ 
	Controller *cp; 
	Drive *dp; 
1995/1206    
	int loop; 
	uchar *addr; 
1995/0213    
 
1995/1206    
	cp = arg; 
	if((dp = cp->dp) == 0 && cp->cmd != Cedd) 
		return; 
1995/0213    
 
1995/1206    
	ILOCK(&cp->reglock); 
1995/0213    
 
	loop = 0; 
	while((cp->status = inb(cp->pbase+Pstatus)) & Sbusy){ 
1995/1206    
		if(++loop > Maxloop){ 
1998/0825    
			print("ata%d: cmd=%ux, lastcmd=%ux status=%ux\n", 
1995/1206    
				cp->ctlrno, cp->cmd, cp->lastcmd, inb(cp->pbase+Pstatus)); 
			panic("%s: wait busy\n", dp->vol); 
		} 
1995/0213    
	} 
 
	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) 
1995/0818    
				if(++loop > Maxloop) 
1995/1206    
					panic("%s: write cmd=%lux status=%lux\n", 
						dp->vol, cp->cmd, inb(cp->pbase+Pstatus)); 
1995/0213    
			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: 
1995/1206    
	case Cidentd: 
1995/0213    
		loop = 0; 
		while((cp->status & (Serr|Sdrq)) == 0){ 
1995/0818    
			if(++loop > Maxloop){ 
1998/0825    
				print("%s: read/ident cmd=%ux status=%ux\n", 
1995/1206    
					dp->vol, cp->cmd, inb(cp->pbase+Pstatus)); 
1995/0818    
				cp->status |= Serr; 
				break; 
1995/0213    
			} 
			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) 
1995/1206    
			print("%s: intr %d %d\n", dp->vol, cp->sofar, cp->nsecs); 
1995/0213    
		if(cp->sofar >= cp->nsecs){ 
			cp->lastcmd = cp->cmd; 
1995/1209    
			if(cp->cmd != Cread) 
1995/0213    
				cp->cmd = Cident2; 
1995/1206    
			else 
				cp->cmd = 0; 
			inb(cp->pbase+Pstatus); 
1995/0213    
			wakeup(&cp->r); 
		} 
		break; 
1995/1206    
	case Cfeature: 
1995/0213    
	case Cstandby: 
1995/1206    
	case Cedd: 
1995/0213    
		cp->lastcmd = cp->cmd; 
		cp->cmd = 0; 
		wakeup(&cp->r); 
		break; 
	case Cident2: 
		cp->lastcmd = cp->cmd; 
		cp->cmd = 0; 
		break; 
1995/1206    
	case Cpktcmd: 
		atapiintr(cp); 
		break; 
1995/0213    
	default: 
1998/0831    
		if(cp->cmd == 0 && cp->lastcmd == Cpktcmd) 
1995/1206    
			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)); 
1995/0213    
		break; 
	} 
 
1995/1206    
	IUNLOCK(&cp->reglock); 
1995/0213    
} 
 
1997/0327    
static void 
ataclock(void) 
1995/0213    
{ 
1995/1206    
	int driveno, mask; 
1995/0213    
	Drive *dp; 
	Controller *cp; 
	int diff; 
 
1995/1206    
	if((mask = spindownmask) == 0) 
1995/0213    
		return; 
 
1995/1206    
	for(driveno = 0; driveno < NDrive && mask; driveno++){ 
		mask &= ~(1<<driveno); 
		if((dp = atadrive[driveno]) == 0) 
			continue; 
1995/0213    
		cp = dp->cp; 
 
		diff = TK2SEC(m->ticks - dp->usetime); 
1995/1206    
		if((dp->state == Sspinning) && (diff >= dp->spindown)){ 
			DPRINT("%s: spindown\n", dp->vol); 
			ILOCK(&cp->reglock); 
1995/0213    
			cp->cmd = Cstandby; 
			outb(cp->pbase+Pcount, 0); 
1995/1206    
			outb(cp->pbase+Pdh, dp->dh); 
1995/0213    
			outb(cp->pbase+Pcmd, cp->cmd); 
1995/1206    
			IUNLOCK(&cp->reglock); 
1995/0213    
			dp->state = Sstandby; 
		} 
1995/1206    
	} 
} 
 
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); 
1995/1208    
	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; 
1995/1206    
	} 
	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)){ 
1998/0831    
		print("cmdreadywait fails"); 
1995/1206    
		error(Eio); 
	} 
	 
	ILOCK(&cp->reglock); 
1998/0516    
	cp->count = 0; 
1995/1206    
	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(); 
		} 
	} 
1998/0831    
	atasleep(cp, Atapitimeout); 
1995/1206    
	poperror(); 
	if(loop) 
		nexterror(); 
 
	if(cp->status & Serr){ 
1998/0922    
		DPRINT("%s: Bad packet command 0x%ux, error 0x%ux\n", 
			dp->vol, cp->cmdblk[0], cp->error); 
1995/1206    
		error(Eio); 
	} 
} 
 
1998/0510    
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; 
	} 
 
1998/0516    
	cp->nsecs = 1; 
1998/0510    
	cp->len = 18; 
	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); 
} 
 
1995/1206    
static long 
1998/0516    
atapiio(Drive *dp, uchar *buf, ulong len, vlong off, int cmd) 
1995/1206    
{ 
1998/0516    
	ulong start; 
1995/1206    
	Controller *cp; 
	int retrycount; 
 
1998/0516    
	/* 
	 *  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; 
1995/1206    
 
1998/0516    
	cp = dp->cp; 
1995/1206    
	qlock(cp->ctlrlock); 
1995/1208    
	retrycount = 2; 
1998/0327    
 
1995/1206    
retry: 
	if(waserror()){ 
1998/0510    
		DPRINT("atapiio: cmd %uX error %uX\n", cp->cmdblk[0], cp->error); 
1995/1206    
		dp->partok = 0; 
		if((cp->status & Serr) && (cp->error & 0xF0) == 0x60){ 
1998/0510    
			atapireqsense(dp); 
1995/1206    
			dp->vers++; 
			if(retrycount){ 
				retrycount--; 
				goto retry; 
			} 
		} 
		cp->dp = 0; 
1998/0516    
		cp->buf = 0; 
1995/1206    
		qunlock(cp->ctlrlock); 
		nexterror(); 
	} 
 
1998/0516    
	cp->buf = buf; 
	cp->nsecs = len; 
	cp->len = len*dp->bytes; 
	cp->cmd = cmd; 
1995/1206    
	cp->dp = dp; 
 
1998/0516    
	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"); 
1995/1206    
	cp->dp = 0; 
1998/0516    
	cp->buf = 0; 
	len = cp->count; 
1995/1206    
	qunlock(cp->ctlrlock); 
1998/0516    
	poperror(); 
 
	return len; 
1995/1206    
} 
 
static long 
1998/0327    
atapirwio(Chan *c, uchar *a, ulong len, vlong off, int cmd) 
1995/1206    
{ 
	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); 
1998/0327    
	rv = atapiio(dp, a, len, off, cmd); 
1995/1206    
 
	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; 
1998/0831    
 
	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; 
	} 
1995/1206    
 
	buf = smalloc(Maxxfer); 
	qlock(cp->ctlrlock); 
1995/1208    
	retrycount = 2; 
1995/1206    
retry: 
	if(waserror()){ 
1998/0922    
		DPRINT("atapipart: cmd 0x%uX error 0x%uX\n", cp->cmdblk[0], cp->error); 
1995/1206    
		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) 
1995/1208    
			err = cp->error & 0xF0; 
1995/1206    
		else 
			err = 0; 
		qunlock(cp->ctlrlock); 
		if(err == 0x20) 
			return; 
		nexterror(); 
	} 
 
	cp->buf = buf; 
	cp->dp = dp; 
1996/0112    
 
1998/0516    
	cp->nsecs = 1; 
1996/0112    
	cp->len = 18; 
	memset(cp->cmdblk, 0, sizeof(cp->cmdblk)); 
	cp->cmdblk[0] = Creqsense; 
	cp->cmdblk[4] = 18; 
	atapiexec(dp); 
	if(cp->count != 18){ 
1998/0922    
		print("cmd=0x%2.2uX, lastcmd=0x%2.2uX ", cp->cmd, cp->lastcmd); 
1996/0112    
		print("cdsize count %d, status 0x%2.2uX, error 0x%2.2uX\n", 
			cp->count, cp->status, cp->error); 
		error(Eio); 
	} 
1995/1206    
 
1998/0516    
	cp->nsecs = 1; 
1995/1206    
	cp->len = 8; 
	memset(cp->cmdblk, 0, sizeof(cp->cmdblk)); 
	cp->cmdblk[0] = Ccapacity; 
	atapiexec(dp); 
	if(cp->count != 8){ 
1998/0922    
		print("cmd=0x%2.2uX, lastcmd=0x%2.2uX ", cp->cmd, cp->lastcmd); 
1995/1206    
		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]; 
1997/1230    
	dp->bytes = (cp->buf[4]<<24)|(cp->buf[5]<<16)|(cp->buf[6]<<8)|cp->buf[7]; 
1998/0106    
	if(dp->bytes > 2048 && dp->bytes <= 2352) 
		dp->bytes = 2048; 
1995/1206    
	dp->cap = dp->lbasecs*dp->bytes; 
1998/0825    
	DPRINT("%s: atapipart secs %lud, bytes %ud, cap %lld\n", 
1997/1230    
		dp->vol, dp->lbasecs, dp->bytes, dp->cap); 
1995/1206    
	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; 
 
	pbase = cp->pbase; 
	cause = inb(pbase+Pcount) & 0x03; 
	DPRINT("%s: atapiintr %uX\n", cp->dp->vol, cause); 
	switch(cause){ 
 
	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; 
 
1998/0219    
	case 0:						/* data out */ 
1995/1206    
	case 2:						/* data in */ 
1998/0516    
		if(cp->buf == 0){ 
1995/1206    
			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); 
1998/0516    
		if(cp->count+count > Maxxfer) 
			panic("hd%d: count %d, already %d\n", count, cp->count); 
1998/0219    
		if(cause == 0) 
1998/0516    
			outss(pbase+Pdata, cp->buf+cp->count, count/2); 
1998/0219    
		else 
1998/0516    
			inss(pbase+Pdata, cp->buf+cp->count, count/2); 
		cp->count += count; 
1995/1206    
		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; 
1995/0213    
	} 
} 
1997/0327    
 
Dev atadevtab = { 
1997/0408    
	'H', 
	"ata", 
 
1997/0327    
	devreset, 
	devinit, 
	ataattach, 
	devclone, 
	atawalk, 
	atastat, 
	ataopen, 
	devcreate, 
	ataclose, 
	ataread, 
	devbread, 
	atawrite, 
	devbwrite, 
	devremove, 
	devwstat, 
}; 


source code copyright © 1990-2005 Lucent Technologies; see license
Plan 9 distribution
comments to russ cox (rsc@swtch.com)