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

2001/1211/alphapc/devfloppy.c (diff list | history)

1999/0415/sys/src/9/alphapc/devfloppy.c:118,1271999/0423/sys/src/9/alphapc/devfloppy.c:118,125 (short | long)
1999/0415    
static void 
fldump(void) 
{ 
mb(); 
	DPRINT("sra %ux srb %ux dor %ux msr %ux dir %ux\n", inb(Psra), inb(Psrb), 
		inb(Pdor), inb(Pmsr), inb(Pdir)); 
mb(); 
} 
 
/* 
1999/0415/sys/src/9/alphapc/devfloppy.c:146,1531999/0423/sys/src/9/alphapc/devfloppy.c:144,149
1999/0415    
	FDrive *dp; 
	FType *t; 
	ulong maxtsize; 
                 
	dmainit(DMAchan); 
	 
	floppysetup0(&fl); 
 
1999/0415/sys/src/9/alphapc/devfloppy.c:163,1681999/0423/sys/src/9/alphapc/devfloppy.c:159,166
1999/0415    
			maxtsize = t->tsize; 
	} 
 
1999/0423    
	dmainit(DMAchan, maxtsize); 
 
1999/0415    
	/* 
	 *  allocate the drive storage 
	 */ 
1999/0415/sys/src/9/alphapc/devfloppy.c:174,1821999/0423/sys/src/9/alphapc/devfloppy.c:172,178
1999/0415    
	 */ 
	fl.motor = 0; 
	delay(10); 
mb(); 
	outb(Pdor, fl.motor | Fintena | Fena); 
mb(); 
	delay(10); 
 
	/* 
1999/0415/sys/src/9/alphapc/devfloppy.c:267,2751999/0423/sys/src/9/alphapc/devfloppy.c:263,269
1999/0415    
	/* 
	 *  if floppy has changed or first time through 
	 */ 
mb(); 
	if((inb(Pdir)&Fchange) || dp->vers == 0){ 
mb(); 
		DPRINT("changed\n"); 
		fldump(); 
		dp->vers++; 
1999/0415/sys/src/9/alphapc/devfloppy.c:276,2891999/0423/sys/src/9/alphapc/devfloppy.c:270,277
1999/0415    
		floppysetdef(dp); 
		start = dp->t; 
		dp->confused = 1;	/* make floppyon recal */ 
DPRINT("b4 floppyon:\n"); 
fldump(); 
		floppyon(dp); 
DPRINT("after floppyon:\n"); 
fldump(); 
		floppyseek(dp, dp->t->heads*dp->t->tsize); 
DPRINT("after floppyseek:\n"); 
fldump(); 
		while(waserror()){ 
			while(++dp->t){ 
				if(dp->t == &floppytype[nelem(floppytype)]) 
1999/0415/sys/src/9/alphapc/devfloppy.c:486,4941999/0423/sys/src/9/alphapc/devfloppy.c:474,480
1999/0415    
	/* start motor and select drive */ 
	alreadyon = fl.motor & MOTORBIT(dp->dev); 
	fl.motor |= MOTORBIT(dp->dev); 
mb(); 
	outb(Pdor, fl.motor | Fintena | Fena | dp->dev); 
mb(); 
	if(!alreadyon){ 
		/* wait for drive to spin up */ 
		tsleep(&dp->r, return0, 0, 750); 
1999/0415/sys/src/9/alphapc/devfloppy.c:500,5081999/0423/sys/src/9/alphapc/devfloppy.c:486,492
1999/0415    
	/* set transfer rate */ 
	if(fl.rate != dp->t->rate){ 
		fl.rate = dp->t->rate; 
mb(); 
		outb(Pdsr, fl.rate); 
mb(); 
	} 
 
	/* get drive to a known cylinder */ 
1999/0415/sys/src/9/alphapc/devfloppy.c:521,5291999/0423/sys/src/9/alphapc/devfloppy.c:505,511
1999/0415    
floppyoff(FDrive *dp) 
{ 
	fl.motor &= ~MOTORBIT(dp->dev); 
mb(); 
	outb(Pdor, fl.motor | Fintena | Fena | dp->dev); 
mb(); 
} 
 
/* 
1999/0415/sys/src/9/alphapc/devfloppy.c:538,5471999/0423/sys/src/9/alphapc/devfloppy.c:520,527
1999/0415    
	fl.nstat = 0; 
	for(i = 0; i < fl.ncmd; i++){ 
		for(tries = 0; ; tries++){ 
mb(); 
			if((inb(Pmsr)&(Ffrom|Fready)) == Fready) 
				break; 
mb(); 
			if(tries > 1000){ 
				DPRINT("cmd %ux can't be sent (%d)\n", fl.cmd[0], i); 
				fldump(); 
1999/0415/sys/src/9/alphapc/devfloppy.c:552,5601999/0423/sys/src/9/alphapc/devfloppy.c:532,538
1999/0415    
			} 
			microdelay(8);	/* for machine independence */ 
		} 
mb(); 
		outb(Pfdata, fl.cmd[i]); 
mb(); 
	} 
	return 0; 
} 
1999/0415/sys/src/9/alphapc/devfloppy.c:576,5841999/0423/sys/src/9/alphapc/devfloppy.c:554,560
1999/0415    
	for(i = 0; i < sizeof(fl.stat); i++){ 
		/* wait for status byte */ 
		for(tries = 0; ; tries++){ 
mb(); 
			s = inb(Pmsr)&(Ffrom|Fready); 
mb(); 
			if(s == Fready){ 
				fl.nstat = i; 
				return fl.nstat; 
1999/0415/sys/src/9/alphapc/devfloppy.c:593,6021999/0423/sys/src/9/alphapc/devfloppy.c:569,575
1999/0415    
			} 
			microdelay(8);	/* for machine independence */ 
		} 
mb(); 
		fl.stat[i] = inb(Pfdata); 
mb(); 
// print("stat[%d]: %.2ux\n", i, fl.stat[i]); 
	} 
	fl.nstat = sizeof(fl.stat); 
	return fl.nstat; 
1999/0415/sys/src/9/alphapc/devfloppy.c:686,6941999/0423/sys/src/9/alphapc/devfloppy.c:659,665
1999/0415    
		return -1; 
	floppywait(); 
	if(fl.nstat < 2){ 
mb(); 
		DPRINT("recalibrate: confused %ux\n", inb(Pmsr)); 
mb(); 
		fl.confused = 1; 
		return -1; 
	} 
1999/0415/sys/src/9/alphapc/devfloppy.c:729,7411999/0423/sys/src/9/alphapc/devfloppy.c:700,708
1999/0415    
		splhi(); 
		fl.ncmd = 1; 
		fl.cmd[0] = 0; 
mb(); 
		outb(Pdor, 0); 
mb(); 
		delay(10); 
mb(); 
		outb(Pdor, Fintena|Fena); 
mb(); 
		delay(10); 
		spllo(); 
		fl.motor = 0; 
1999/0415/sys/src/9/alphapc/devfloppy.c:747,7551999/0423/sys/src/9/alphapc/devfloppy.c:714,720
1999/0415    
			dp->confused = 1; 
 
		/* set rate to a known value */ 
mb(); 
		outb(Pdsr, 0); 
mb(); 
		fl.rate = 0; 
 
		DPRINT("floppyrevive out\n"); 
1999/0423/sys/src/9/alphapc/devfloppy.c:39,451999/0501/sys/src/9/alphapc/devfloppy.c:39,45 (short | long)
1999/0415    
}; 
 
#define DPRINT if(floppydebug)print 
int floppydebug = 0; 
1999/0501    
int floppydebug = 1; 
1999/0415    
 
/* 
 *  types of drive (from PC equipment byte) 
1999/0423/sys/src/9/alphapc/devfloppy.c:56,611999/0501/sys/src/9/alphapc/devfloppy.c:56,62
1999/0415    
FType floppytype[] = 
{ 
 { "3½HD",	T1440kb, 512, 18, 2, 1, 80, 0x1B, 0x54,	0, }, 
1999/0501    
/* 
1999/0415    
 { "3½DD",	T1440kb, 512,  9, 2, 1, 80, 0x1B, 0x54, 2, }, 
 { "3½DD",	T720kb,  512,  9, 2, 1, 80, 0x1B, 0x54, 2, }, 
 { "5¼HD",	T1200kb, 512, 15, 2, 1, 80, 0x2A, 0x50, 0, }, 
1999/0423/sys/src/9/alphapc/devfloppy.c:62,671999/0501/sys/src/9/alphapc/devfloppy.c:63,69
1999/0415    
 { "5¼DD",	T1200kb, 512,  9, 2, 2, 40, 0x2A, 0x50, 1, }, 
 { "ATT3B1",	T1200kb, 512,  8, 2, 2, 48, 0x2A, 0x50, 1, }, 
 { "5¼DD",	T360kb,  512,  9, 2, 1, 40, 0x2A, 0x50, 2, }, 
1999/0501    
 */ 
1999/0415    
}; 
 
/* 
1999/0423/sys/src/9/alphapc/devfloppy.c:533,5381999/0501/sys/src/9/alphapc/devfloppy.c:535,541
1999/0415    
			microdelay(8);	/* for machine independence */ 
		} 
		outb(Pfdata, fl.cmd[i]); 
1999/0501    
microdelay(8);	/* for machine independence */ 
1999/0415    
	} 
	return 0; 
} 
1999/0423/sys/src/9/alphapc/devfloppy.c:636,6421999/0501/sys/src/9/alphapc/devfloppy.c:639,649
1999/0415    
static void 
floppywait(void) 
{ 
1999/0501    
vlong t0, t1; 
t0 = fastticks(nil); 
1999/0415    
	tsleep(&fl.r, cmddone, 0, 5000); 
1999/0501    
t1 = fastticks(nil); 
print("wait %lld ticks\n", t1-t0); 
1999/0415    
	if(!cmddone(0)){ 
		floppyintr(0); 
		fl.confused = 1; 
1999/0423/sys/src/9/alphapc/devfloppy.c:680,6881999/0501/sys/src/9/alphapc/devfloppy.c:687,708
1999/0415    
	return 0; 
} 
 
1999/0501    
static void 
specify(void) 
{ 
	fl.ncmd = 0; 
	fl.cmd[fl.ncmd++] = Fspec; 
	fl.cmd[fl.ncmd++] = 0; 
	fl.cmd[fl.ncmd++] = 1; 
	if(floppycmd() < 0) 
		return; 
	floppywait(); 
	fldump(); 
} 
 
1999/0415    
/* 
 *  if the controller or a specific drive is in a confused state, 
 *  reset it and get back to a kown state 
1999/0501    
 *  reset it and get back to a known state 
1999/0415    
 */ 
static void 
floppyrevive(void) 
1999/0423/sys/src/9/alphapc/devfloppy.c:774,7801999/0501/sys/src/9/alphapc/devfloppy.c:794,800
1999/0415    
	/* retry on error (until it gets ridiculous) */ 
	tries = 0; 
	while(waserror()){ 
		if(tries++ > 20) 
1999/0501    
		if(tries++ > 2/*0*/) 
1999/0415    
			nexterror(); 
		DPRINT("floppyxfer: retrying\n"); 
		/*floppyon(dp);*/ 
1999/0423/sys/src/9/alphapc/devfloppy.c:820,8251999/0501/sys/src/9/alphapc/devfloppy.c:840,847
1999/0415    
	/* 
	 *  give bus to DMA, floppyintr() will read result 
	 */ 
1999/0501    
delay(10); 
print("msr 0x%2.2uX\n", inb(Pmsr)); 
1999/0415    
	floppywait(); 
	dmaend(DMAchan); 
	poperror(); 
1999/0501/sys/src/9/alphapc/devfloppy.c:842,8471999/0504/sys/src/9/alphapc/devfloppy.c:842,848 (short | long)
1999/0415    
	 */ 
1999/0501    
delay(10); 
print("msr 0x%2.2uX\n", inb(Pmsr)); 
1999/0504    
xdmastatus(DMAchan); 
1999/0415    
	floppywait(); 
	dmaend(DMAchan); 
	poperror(); 
1999/0504/sys/src/9/alphapc/devfloppy.c:39,451999/0506/sys/src/9/alphapc/devfloppy.c:39,45 (short | long)
1999/0415    
}; 
 
#define DPRINT if(floppydebug)print 
1999/0501    
int floppydebug = 1; 
1999/0506    
int floppydebug = 0; 
1999/0415    
 
/* 
 *  types of drive (from PC equipment byte) 
1999/0504/sys/src/9/alphapc/devfloppy.c:106,1121999/0506/sys/src/9/alphapc/devfloppy.c:106,113
1999/0415    
static long	floppyxfer(FDrive*, int, void*, long, long); 
 
Dirtab floppydir[]={ 
	"fd0disk",		{Qdata + 0},	0,	0660, 
1999/0506    
//	"fd0disk",		{Qdata + 0},	0,	0660, 
	"fd0disk",		{Qdata + 0},	0,	0666, 
1999/0415    
	"fd0ctl",		{Qctl + 0},	0,	0660, 
	"fd1disk",		{Qdata + 1},	0,	0660, 
	"fd1ctl",		{Qctl + 1},	0,	0660, 
1999/0504/sys/src/9/alphapc/devfloppy.c:535,5411999/0506/sys/src/9/alphapc/devfloppy.c:536,541
1999/0415    
			microdelay(8);	/* for machine independence */ 
		} 
		outb(Pfdata, fl.cmd[i]); 
1999/0501    
microdelay(8);	/* for machine independence */ 
1999/0415    
	} 
	return 0; 
} 
1999/0504/sys/src/9/alphapc/devfloppy.c:639,6491999/0506/sys/src/9/alphapc/devfloppy.c:639,645
1999/0415    
static void 
floppywait(void) 
{ 
1999/0501    
vlong t0, t1; 
t0 = fastticks(nil); 
1999/0415    
	tsleep(&fl.r, cmddone, 0, 5000); 
1999/0501    
t1 = fastticks(nil); 
print("wait %lld ticks\n", t1-t0); 
1999/0415    
	if(!cmddone(0)){ 
		floppyintr(0); 
		fl.confused = 1; 
1999/0504/sys/src/9/alphapc/devfloppy.c:688,6931999/0506/sys/src/9/alphapc/devfloppy.c:684,709
1999/0415    
} 
 
1999/0501    
static void 
1999/0506    
dumpreg(void) 
{ 
	int i; 
 
	fl.ncmd = 0; 
	fl.cmd[fl.ncmd++] = Fdumpreg; 
	if(floppycmd() < 0) 
		return; 
	floppywait(); 
	if(fl.nstat < 0){ 
		print("dumpreg bad %d\n", fl.nstat); 
		fldump(); 
		return; 
	} 
	for(i = 0; i < fl.nstat; i++) 
		print(" %2.2uX", fl.stat[i]); 
	print("\n"); 
} 
 
static void 
1999/0501    
specify(void) 
{ 
	fl.ncmd = 0; 
1999/0504/sys/src/9/alphapc/devfloppy.c:794,8001999/0506/sys/src/9/alphapc/devfloppy.c:810,816
1999/0415    
	/* retry on error (until it gets ridiculous) */ 
	tries = 0; 
	while(waserror()){ 
1999/0501    
		if(tries++ > 2/*0*/) 
1999/0506    
		if(tries++ > 20) 
1999/0415    
			nexterror(); 
		DPRINT("floppyxfer: retrying\n"); 
		/*floppyon(dp);*/ 
1999/0504/sys/src/9/alphapc/devfloppy.c:840,8481999/0506/sys/src/9/alphapc/devfloppy.c:856,861
1999/0415    
	/* 
	 *  give bus to DMA, floppyintr() will read result 
	 */ 
1999/0501    
delay(10); 
print("msr 0x%2.2uX\n", inb(Pmsr)); 
1999/0504    
xdmastatus(DMAchan); 
1999/0415    
	floppywait(); 
	dmaend(DMAchan); 
	poperror(); 
1999/0506/sys/src/9/alphapc/devfloppy.c:56,621999/0507/sys/src/9/alphapc/devfloppy.c:56,61 (short | long)
1999/0415    
FType floppytype[] = 
{ 
 { "3½HD",	T1440kb, 512, 18, 2, 1, 80, 0x1B, 0x54,	0, }, 
1999/0501    
/* 
1999/0415    
 { "3½DD",	T1440kb, 512,  9, 2, 1, 80, 0x1B, 0x54, 2, }, 
 { "3½DD",	T720kb,  512,  9, 2, 1, 80, 0x1B, 0x54, 2, }, 
 { "5¼HD",	T1200kb, 512, 15, 2, 1, 80, 0x2A, 0x50, 0, }, 
1999/0506/sys/src/9/alphapc/devfloppy.c:63,691999/0507/sys/src/9/alphapc/devfloppy.c:62,67
1999/0415    
 { "5¼DD",	T1200kb, 512,  9, 2, 2, 40, 0x2A, 0x50, 1, }, 
 { "ATT3B1",	T1200kb, 512,  8, 2, 2, 48, 0x2A, 0x50, 1, }, 
 { "5¼DD",	T360kb,  512,  9, 2, 1, 40, 0x2A, 0x50, 2, }, 
1999/0501    
 */ 
1999/0415    
}; 
 
/* 
1999/0506/sys/src/9/alphapc/devfloppy.c:806,8111999/0507/sys/src/9/alphapc/devfloppy.c:804,811
1999/0415    
		return 0; 
	if(off + n > dp->t->cap) 
		n = dp->t->cap - off; 
1999/0507    
if(cmd == Fread) 
    memset(a, 0x55, n); 
1999/0415    
 
	/* retry on error (until it gets ridiculous) */ 
	tries = 0; 
1999/0507/sys/src/9/alphapc/devfloppy.c:911,9172000/0308/sys/src/9/alphapc/devfloppy.c:911,917 (short | long)
1999/0415    
	/* 
	 *  set the type 
	 */ 
	if(parsefields(params, f, 3, " ") > 1){ 
2000/0308    
	if(getfields(params, f, 3, 1, " ") > 1){ 
1999/0415    
		for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){ 
			if(strcmp(f[1], t->name)==0 && t->dt==dp->dt){ 
				dp->t = t; 
2000/0308/sys/src/9/alphapc/devfloppy.c:104,1102001/0727/sys/src/9/alphapc/devfloppy.c:104,110 (short | long)
1999/0415    
static long	floppyxfer(FDrive*, int, void*, long, long); 
 
Dirtab floppydir[]={ 
1999/0506    
//	"fd0disk",		{Qdata + 0},	0,	0660, 
2001/0727    
	".",		{Qdir, 0, QTDIR},	0,	0550, 
1999/0506    
	"fd0disk",		{Qdata + 0},	0,	0666, 
1999/0415    
	"fd0ctl",		{Qctl + 0},	0,	0660, 
	"fd1disk",		{Qdata + 1},	0,	0660, 
2000/0308/sys/src/9/alphapc/devfloppy.c:134,1402001/0727/sys/src/9/alphapc/devfloppy.c:134,140
1999/0415    
	for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++) 
		if(dp->dt == t->dt){ 
			dp->t = t; 
			floppydir[NFDIR*dp->dev].length = dp->t->cap; 
2001/0727    
			floppydir[1+NFDIR*dp->dev].length = dp->t->cap; 
1999/0415    
			break; 
		} 
} 
2000/0308/sys/src/9/alphapc/devfloppy.c:212,2332001/0727/sys/src/9/alphapc/devfloppy.c:212,233
1999/0415    
	return devattach('f', spec); 
} 
 
static int 
floppywalk(Chan *c, char *name) 
2001/0727    
static Walkqid* 
floppywalk(Chan *c, Chan *nc, char **name, int nname) 
1999/0415    
{ 
	return devwalk(c, name, floppydir, fl.ndrive*NFDIR, devgen); 
2001/0727    
	return devwalk(c, nc, name, nname, floppydir, 1+fl.ndrive*NFDIR, devgen); 
1999/0415    
} 
 
static void 
floppystat(Chan *c, char *dp) 
2001/0727    
static int 
floppystat(Chan *c, uchar *dp, int n) 
1999/0415    
{ 
	devstat(c, dp, floppydir, fl.ndrive*NFDIR, devgen); 
2001/0727    
	return devstat(c, dp, n, floppydir, 1+fl.ndrive*NFDIR, devgen); 
1999/0415    
} 
 
static Chan* 
floppyopen(Chan *c, int omode) 
{ 
	return devopen(c, omode, floppydir, fl.ndrive*NFDIR, devgen); 
2001/0727    
	return devopen(c, omode, floppydir, 1+fl.ndrive*NFDIR, devgen); 
1999/0415    
} 
 
static void 
2000/0308/sys/src/9/alphapc/devfloppy.c:280,2862001/0727/sys/src/9/alphapc/devfloppy.c:280,286
1999/0415    
				if(dp->dt == dp->t->dt) 
					break; 
			} 
			floppydir[NFDIR*dp->dev].length = dp->t->cap; 
2001/0727    
			floppydir[1+NFDIR*dp->dev].length = dp->t->cap; 
1999/0415    
			floppyon(dp); 
			DPRINT("changed: trying %s\n", dp->t->name); 
			fldump(); 
2000/0308/sys/src/9/alphapc/devfloppy.c:328,3352001/0727/sys/src/9/alphapc/devfloppy.c:328,335
1999/0415    
	uchar *aa; 
	ulong offset = off; 
 
	if(c->qid.path == CHDIR) 
		return devdirread(c, a, n, floppydir, fl.ndrive*NFDIR, devgen); 
2001/0727    
	if(c->qid.type & QTDIR) 
		return devdirread(c, a, n, floppydir, 1+fl.ndrive*NFDIR, devgen); 
1999/0415    
 
	rv = 0; 
	dp = &fl.d[c->qid.path & ~Qmask]; 
2000/0308/sys/src/9/alphapc/devfloppy.c:915,9212001/0727/sys/src/9/alphapc/devfloppy.c:915,921
1999/0415    
		for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){ 
			if(strcmp(f[1], t->name)==0 && t->dt==dp->dt){ 
				dp->t = t; 
				floppydir[NFDIR*dp->dev].length = dp->t->cap; 
2001/0727    
				floppydir[1+NFDIR*dp->dev].length = dp->t->cap; 
1999/0415    
				break; 
			} 
		} 
2000/0308/sys/src/9/alphapc/devfloppy.c:1043,10492001/0727/sys/src/9/alphapc/devfloppy.c:1043,1048
1999/0415    
	floppyreset, 
	devinit, 
	floppyattach, 
	devclone, 
	floppywalk, 
	floppystat, 
	floppyopen, 
2001/0727/sys/src/9/alphapc/devfloppy.c:911,9172001/1117/sys/src/9/alphapc/devfloppy.c:911,917 (short | long)
1999/0415    
	/* 
	 *  set the type 
	 */ 
2000/0308    
	if(getfields(params, f, 3, 1, " ") > 1){ 
2001/1117    
	if(tokenize(params, f, 3) > 1){ 
1999/0415    
		for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){ 
			if(strcmp(f[1], t->name)==0 && t->dt==dp->dt){ 
				dp->t = t; 
2001/1117/sys/src/9/alphapc/devfloppy.c:92,982001/1122/sys/src/9/alphapc/devfloppy.c:92,98 (short | long)
1999/0415    
 *  predeclared 
 */ 
static int	cmddone(void*); 
static void	floppyformat(FDrive*, char*); 
2001/1122    
static void	floppyformat(FDrive*, Cmdbuf*); 
1999/0415    
static void	floppykproc(void*); 
static void	floppypos(FDrive*,long); 
static int	floppyrecal(FDrive*); 
2001/1117/sys/src/9/alphapc/devfloppy.c:100,1112001/1122/sys/src/9/alphapc/devfloppy.c:100,111
1999/0415    
static void	floppyrevive(void); 
static long	floppyseek(FDrive*, long); 
static int	floppysense(void); 
static void	floppywait(void); 
2001/1122    
static void	floppywait(int); 
1999/0415    
static long	floppyxfer(FDrive*, int, void*, long, long); 
 
Dirtab floppydir[]={ 
2001/0727    
	".",		{Qdir, 0, QTDIR},	0,	0550, 
1999/0506    
	"fd0disk",		{Qdata + 0},	0,	0666, 
2001/1122    
	"fd0disk",		{Qdata + 0},	0,	0660, 
1999/0415    
	"fd0ctl",		{Qctl + 0},	0,	0660, 
	"fd1disk",		{Qdata + 1},	0,	0660, 
	"fd1ctl",		{Qctl + 1},	0,	0660, 
2001/1117/sys/src/9/alphapc/devfloppy.c:116,1212001/1122/sys/src/9/alphapc/devfloppy.c:116,137
1999/0415    
}; 
#define NFDIR	2	/* directory entries/drive */ 
 
2001/1122    
enum 
{ 
	CMdebug, 
	CMeject, 
	CMformat, 
	CMreset, 
}; 
 
static Cmdtab floppyctlmsg[] = 
{ 
	CMdebug,	"debug",	1, 
	CMeject,	"eject",	1, 
	CMformat,	"format",	0, 
	CMreset,	"reset",	1, 
}; 
 
1999/0415    
static void 
fldump(void) 
{ 
2001/1117/sys/src/9/alphapc/devfloppy.c:147,1522001/1122/sys/src/9/alphapc/devfloppy.c:163,170
1999/0415    
	ulong maxtsize; 
	 
	floppysetup0(&fl); 
2001/1122    
	if(fl.ndrive == 0) 
		return; 
1999/0415    
 
	/* 
	 *  init dependent parameters 
2001/1117/sys/src/9/alphapc/devfloppy.c:202,2072001/1122/sys/src/9/alphapc/devfloppy.c:220,228
1999/0415    
{ 
	static int kstarted; 
 
2001/1122    
	if(fl.ndrive == 0) 
		error(Enodev); 
 
1999/0415    
	if(kstarted == 0){ 
		/* 
		 *  watchdog to turn off the motors 
2001/1117/sys/src/9/alphapc/devfloppy.c:268,2792001/1122/sys/src/9/alphapc/devfloppy.c:289,312
1999/0415    
		DPRINT("changed\n"); 
		fldump(); 
		dp->vers++; 
		floppysetdef(dp); 
		start = dp->t; 
2001/1122    
		dp->maxtries = 3;	/* limit it when we're probing */ 
 
		/* floppyon will fail if there's a controller but no drive */ 
1999/0415    
		dp->confused = 1;	/* make floppyon recal */ 
		floppyon(dp); 
2001/1122    
		if(floppyon(dp) < 0) 
			error(Eio); 
 
		/* seek to the first track */ 
1999/0415    
		floppyseek(dp, dp->t->heads*dp->t->tsize); 
		while(waserror()){ 
2001/1122    
			/* 
			 *  if first attempt doesn't reset changed bit, there's 
			 *  no floppy there 
			 */ 
			if(inb(Pdir)&Fchange) 
				nexterror(); 
 
1999/0415    
			while(++dp->t){ 
				if(dp->t == &floppytype[nelem(floppytype)]) 
					dp->t = floppytype; 
2001/1117/sys/src/9/alphapc/devfloppy.c:281,2942001/1122/sys/src/9/alphapc/devfloppy.c:314,334
1999/0415    
					break; 
			} 
2001/0727    
			floppydir[1+NFDIR*dp->dev].length = dp->t->cap; 
1999/0415    
			floppyon(dp); 
2001/1122    
 
			/* floppyon will fail if there's a controller but no drive */ 
			if(floppyon(dp) < 0) 
				error(Eio); 
 
1999/0415    
			DPRINT("changed: trying %s\n", dp->t->name); 
			fldump(); 
			if(dp->t == start) 
				nexterror(); 
		} 
2001/1122    
 
		/* if the read succeeds, we've got the density right */ 
1999/0415    
		floppyxfer(dp, Fread, dp->cache, 0, dp->t->tsize); 
		poperror(); 
2001/1122    
		dp->maxtries = 20; 
1999/0415    
	} 
 
	old = c->qid.vers; 
2001/1117/sys/src/9/alphapc/devfloppy.c:372,3782001/1122/sys/src/9/alphapc/devfloppy.c:412,417
1999/0415    
	return rv; 
} 
 
#define SNCMP(a, b) strncmp(a, b, sizeof(b)-1) 
static long 
floppywrite(Chan *c, void *a, long n, vlong off) 
{ 
2001/1117/sys/src/9/alphapc/devfloppy.c:379,3852001/1122/sys/src/9/alphapc/devfloppy.c:418,425
1999/0415    
	FDrive *dp; 
	long rv, i; 
	char *aa = a; 
	char ctlmsg[64]; 
2001/1122    
	Cmdbuf *cb; 
	Cmdtab *ct; 
1999/0415    
	ulong offset = off; 
 
	rv = 0; 
2001/1117/sys/src/9/alphapc/devfloppy.c:409,4362001/1122/sys/src/9/alphapc/devfloppy.c:449,484
1999/0415    
		break; 
	case Qctl: 
		rv = n; 
2001/1122    
		cb = parsecmd(a, n); 
		if(waserror()){ 
			free(cb); 
			nexterror(); 
		} 
1999/0415    
		qlock(&fl); 
		if(waserror()){ 
			qunlock(&fl); 
			nexterror(); 
		} 
		if(n >= sizeof(ctlmsg)) 
			n = sizeof(ctlmsg) - 1; 
		memmove(ctlmsg, aa, n); 
		ctlmsg[n] = 0; 
		if(SNCMP(ctlmsg, "eject") == 0){ 
2001/1122    
		ct = lookupcmd(cb, floppyctlmsg, nelem(floppyctlmsg)); 
		switch(ct->index){ 
		case CMeject: 
1999/0415    
			floppyeject(dp); 
		} else if(SNCMP(ctlmsg, "reset") == 0){ 
2001/1122    
			break; 
		case CMformat: 
			floppyformat(dp, cb); 
			break; 
		case CMreset: 
1999/0415    
			fl.confused = 1; 
			floppyon(dp); 
		} else if(SNCMP(ctlmsg, "format") == 0){ 
			floppyformat(dp, ctlmsg); 
		} else if(SNCMP(ctlmsg, "debug") == 0){ 
2001/1122    
			break; 
		case CMdebug: 
1999/0415    
			floppydebug = 1; 
		} else 
			error(Ebadctl); 
2001/1122    
			break; 
		} 
1999/0415    
		poperror(); 
		qunlock(&fl); 
2001/1122    
		poperror(); 
		free(cb); 
1999/0415    
		break; 
	default: 
		panic("floppywrite: bad qid"); 
2001/1117/sys/src/9/alphapc/devfloppy.c:463,4692001/1122/sys/src/9/alphapc/devfloppy.c:511,517
1999/0415    
/* 
 *  start a floppy drive's motor. 
 */ 
static void 
2001/1122    
static int 
1999/0415    
floppyon(FDrive *dp) 
{ 
	int alreadyon; 
2001/1117/sys/src/9/alphapc/devfloppy.c:497,5022001/1122/sys/src/9/alphapc/devfloppy.c:545,555
1999/0415    
				break; 
	dp->lasttouched = m->ticks; 
	fl.selected = dp; 
2001/1122    
 
	/* return -1 if this didn't work */ 
	if(dp->confused) 
		return -1; 
	return 0; 
1999/0415    
} 
 
/* 
2001/1117/sys/src/9/alphapc/devfloppy.c:635,6432001/1122/sys/src/9/alphapc/devfloppy.c:688,696
1999/0415    
 *  routine to try to clear any conditions. 
 */ 
static void 
floppywait(void) 
2001/1122    
floppywait(int slow) 
1999/0415    
{ 
	tsleep(&fl.r, cmddone, 0, 5000); 
2001/1122    
	tsleep(&fl.r, cmddone, 0, slow ? 5000 : 1000); 
1999/0415    
	if(!cmddone(0)){ 
		floppyintr(0); 
		fl.confused = 1; 
2001/1117/sys/src/9/alphapc/devfloppy.c:658,6642001/1122/sys/src/9/alphapc/devfloppy.c:711,717
1999/0415    
	fl.cmd[fl.ncmd++] = dp->dev; 
	if(floppycmd() < 0) 
		return -1; 
	floppywait(); 
2001/1122    
	floppywait(1); 
1999/0415    
	if(fl.nstat < 2){ 
		DPRINT("recalibrate: confused %ux\n", inb(Pmsr)); 
		fl.confused = 1; 
2001/1117/sys/src/9/alphapc/devfloppy.c:681,7192001/1122/sys/src/9/alphapc/devfloppy.c:734,739
1999/0415    
	return 0; 
} 
 
1999/0501    
static void 
1999/0506    
dumpreg(void) 
{ 
	int i; 
                 
	fl.ncmd = 0; 
	fl.cmd[fl.ncmd++] = Fdumpreg; 
	if(floppycmd() < 0) 
		return; 
	floppywait(); 
	if(fl.nstat < 0){ 
		print("dumpreg bad %d\n", fl.nstat); 
		fldump(); 
		return; 
	} 
	for(i = 0; i < fl.nstat; i++) 
		print(" %2.2uX", fl.stat[i]); 
	print("\n"); 
} 
                 
static void 
1999/0501    
specify(void) 
{ 
	fl.ncmd = 0; 
	fl.cmd[fl.ncmd++] = Fspec; 
	fl.cmd[fl.ncmd++] = 0; 
	fl.cmd[fl.ncmd++] = 1; 
	if(floppycmd() < 0) 
		return; 
	floppywait(); 
	fldump(); 
} 
                 
1999/0415    
/* 
 *  if the controller or a specific drive is in a confused state, 
1999/0501    
 *  reset it and get back to a known state 
2001/1117/sys/src/9/alphapc/devfloppy.c:741,7472001/1122/sys/src/9/alphapc/devfloppy.c:761,767
1999/0415    
		spllo(); 
		fl.motor = 0; 
		fl.confused = 0; 
		floppywait(); 
2001/1122    
		floppywait(0); 
1999/0415    
 
		/* mark all drives in an unknown state */ 
		for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++) 
2001/1117/sys/src/9/alphapc/devfloppy.c:775,7812001/1122/sys/src/9/alphapc/devfloppy.c:795,801
1999/0415    
	fl.cmd[fl.ncmd++] = dp->tcyl * dp->t->steps; 
	if(floppycmd() < 0) 
		return -1; 
	floppywait(); 
2001/1122    
	floppywait(1); 
1999/0415    
	if(fl.nstat < 2){ 
		DPRINT("seek: confused\n"); 
		fl.confused = 1; 
2001/1117/sys/src/9/alphapc/devfloppy.c:804,8192001/1122/sys/src/9/alphapc/devfloppy.c:824,836
1999/0415    
		return 0; 
	if(off + n > dp->t->cap) 
		n = dp->t->cap - off; 
1999/0507    
if(cmd == Fread) 
    memset(a, 0x55, n); 
1999/0415    
 
	/* retry on error (until it gets ridiculous) */ 
	tries = 0; 
	while(waserror()){ 
1999/0506    
		if(tries++ > 20) 
2001/1122    
		if(tries++ >= dp->maxtries) 
1999/0415    
			nexterror(); 
		DPRINT("floppyxfer: retrying\n"); 
		/*floppyon(dp);*/ 
	} 
 
	dp->len = n; 
2001/1117/sys/src/9/alphapc/devfloppy.c:856,8622001/1122/sys/src/9/alphapc/devfloppy.c:873,879
1999/0415    
	/* 
	 *  give bus to DMA, floppyintr() will read result 
	 */ 
	floppywait(); 
2001/1122    
	floppywait(0); 
1999/0415    
	dmaend(DMAchan); 
	poperror(); 
 
2001/1117/sys/src/9/alphapc/devfloppy.c:900,9192001/1122/sys/src/9/alphapc/devfloppy.c:917,935
1999/0415    
 *  format a track 
 */ 
static void 
floppyformat(FDrive *dp, char *params) 
2001/1122    
floppyformat(FDrive *dp, Cmdbuf *cb) 
1999/0415    
{ 
 	int cyl, h, sec; 
	ulong track; 
	uchar *buf, *bp; 
	FType *t; 
	char *f[3]; 
 
	/* 
	 *  set the type 
	 */ 
2001/1117    
	if(tokenize(params, f, 3) > 1){ 
2001/1122    
	if(cb->nf == 2){ 
1999/0415    
		for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){ 
			if(strcmp(f[1], t->name)==0 && t->dt==dp->dt){ 
2001/1122    
			if(strcmp(cb->f[1], t->name)==0 && t->dt==dp->dt){ 
1999/0415    
				dp->t = t; 
2001/0727    
				floppydir[1+NFDIR*dp->dev].length = dp->t->cap; 
1999/0415    
				break; 
2001/1117/sys/src/9/alphapc/devfloppy.c:921,9292001/1122/sys/src/9/alphapc/devfloppy.c:937,948
1999/0415    
		} 
		if(t >= &floppytype[nelem(floppytype)]) 
			error(Ebadarg); 
	} else { 
2001/1122    
	} else if(cb->nf == 1){ 
1999/0415    
		floppysetdef(dp); 
		t = dp->t; 
2001/1122    
	} else { 
		cmderror(cb, "invalid floppy format command"); 
		SET(t); 
1999/0415    
	} 
 
	/* 
2001/1117/sys/src/9/alphapc/devfloppy.c:992,9982001/1122/sys/src/9/alphapc/devfloppy.c:1011,1017
1999/0415    
		/* 
		 *  give bus to DMA, floppyintr() will read result 
		 */ 
		floppywait(); 
2001/1122    
		floppywait(1); 
1999/0415    
		dmaend(DMAchan); 
		poperror(); 
 
2001/1122/sys/src/9/alphapc/devfloppy.c:1,10762001/1211/sys/src/9/alphapc/devfloppy.c:1 (short | long)
1999/0415    
#include	"u.h" 
#include	"../port/lib.h" 
#include	"mem.h" 
#include	"dat.h" 
#include	"fns.h" 
#include	"io.h" 
#include	"../port/error.h" 
                 
#include	"floppy.h" 
                 
/* Intel 82077A (8272A compatible) floppy controller */ 
                 
/* This module expects the following functions to be defined 
 * elsewhere:  
 *  
 * inb() 
 * outb() 
 * floppyexec() 
 * floppyeject()  
 * floppysetup0() 
 * floppysetup1() 
 * dmainit() 
 * dmasetup() 
 * dmaend() 
 *  
 * On DMA systems, floppyexec() should be an empty function;  
 * on non-DMA systems, dmaend() should be an empty function;  
 * dmasetup() may enforce maximum transfer sizes.  
 */ 
                 
enum { 
	/* file types */ 
	Qdir=		0,  
	Qdata=		(1<<2), 
	Qctl=		(2<<2), 
	Qmask=		(3<<2), 
                 
	DMAchan=	2,	/* floppy dma channel */ 
}; 
                 
#define DPRINT if(floppydebug)print 
1999/0506    
int floppydebug = 0; 
1999/0415    
                 
/* 
 *  types of drive (from PC equipment byte) 
 */ 
enum 
{ 
	Tnone=		0, 
	T360kb=		1, 
	T1200kb=	2, 
	T720kb=		3, 
	T1440kb=	4, 
}; 
                 
FType floppytype[] = 
{ 
 { "3½HD",	T1440kb, 512, 18, 2, 1, 80, 0x1B, 0x54,	0, }, 
 { "3½DD",	T1440kb, 512,  9, 2, 1, 80, 0x1B, 0x54, 2, }, 
 { "3½DD",	T720kb,  512,  9, 2, 1, 80, 0x1B, 0x54, 2, }, 
 { "5¼HD",	T1200kb, 512, 15, 2, 1, 80, 0x2A, 0x50, 0, }, 
 { "5¼DD",	T1200kb, 512,  9, 2, 2, 40, 0x2A, 0x50, 1, }, 
 { "ATT3B1",	T1200kb, 512,  8, 2, 2, 48, 0x2A, 0x50, 1, }, 
 { "5¼DD",	T360kb,  512,  9, 2, 1, 40, 0x2A, 0x50, 2, }, 
}; 
                 
/* 
 *  bytes per sector encoding for the controller. 
 *  - index for b2c is is (bytes per sector/128). 
 *  - index for c2b is code from b2c 
 */ 
static int b2c[] = 
{ 
[1]	0, 
[2]	1, 
[4]	2, 
[8]	3, 
}; 
static int c2b[] = 
{ 
	128, 
	256, 
	512, 
	1024, 
}; 
                 
FController	fl; 
                 
#define MOTORBIT(i)	(1<<((i)+4)) 
                 
/* 
 *  predeclared 
 */ 
static int	cmddone(void*); 
2001/1122    
static void	floppyformat(FDrive*, Cmdbuf*); 
1999/0415    
static void	floppykproc(void*); 
static void	floppypos(FDrive*,long); 
static int	floppyrecal(FDrive*); 
static int	floppyresult(void); 
static void	floppyrevive(void); 
static long	floppyseek(FDrive*, long); 
static int	floppysense(void); 
2001/1122    
static void	floppywait(int); 
1999/0415    
static long	floppyxfer(FDrive*, int, void*, long, long); 
                 
Dirtab floppydir[]={ 
2001/0727    
	".",		{Qdir, 0, QTDIR},	0,	0550, 
2001/1122    
	"fd0disk",		{Qdata + 0},	0,	0660, 
1999/0415    
	"fd0ctl",		{Qctl + 0},	0,	0660, 
	"fd1disk",		{Qdata + 1},	0,	0660, 
	"fd1ctl",		{Qctl + 1},	0,	0660, 
	"fd2disk",		{Qdata + 2},	0,	0660, 
	"fd2ctl",		{Qctl + 2},	0,	0660, 
	"fd3disk",		{Qdata + 3},	0,	0660, 
	"fd3ctl",		{Qctl + 3},	0,	0660, 
}; 
#define NFDIR	2	/* directory entries/drive */ 
                 
2001/1122    
enum 
{ 
	CMdebug, 
	CMeject, 
	CMformat, 
	CMreset, 
}; 
                 
static Cmdtab floppyctlmsg[] = 
{ 
	CMdebug,	"debug",	1, 
	CMeject,	"eject",	1, 
	CMformat,	"format",	0, 
	CMreset,	"reset",	1, 
}; 
                 
1999/0415    
static void 
fldump(void) 
{ 
	DPRINT("sra %ux srb %ux dor %ux msr %ux dir %ux\n", inb(Psra), inb(Psrb), 
		inb(Pdor), inb(Pmsr), inb(Pdir)); 
} 
                 
/* 
 *  set floppy drive to its default type 
 */ 
static void 
floppysetdef(FDrive *dp) 
{ 
	FType *t; 
                 
	for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++) 
		if(dp->dt == t->dt){ 
			dp->t = t; 
2001/0727    
			floppydir[1+NFDIR*dp->dev].length = dp->t->cap; 
1999/0415    
			break; 
		} 
} 
                 
static void 
floppyreset(void) 
{ 
	FDrive *dp; 
	FType *t; 
	ulong maxtsize; 
	                 
	floppysetup0(&fl); 
2001/1122    
	if(fl.ndrive == 0) 
		return; 
1999/0415    
                 
	/* 
	 *  init dependent parameters 
	 */ 
	maxtsize = 0; 
	for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){ 
		t->cap = t->bytes * t->heads * t->sectors * t->tracks; 
		t->bcode = b2c[t->bytes/128]; 
		t->tsize = t->bytes * t->sectors; 
		if(maxtsize < t->tsize) 
			maxtsize = t->tsize; 
	} 
                 
1999/0423    
	dmainit(DMAchan, maxtsize); 
                 
1999/0415    
	/* 
	 *  allocate the drive storage 
	 */ 
	fl.d = xalloc(fl.ndrive*sizeof(FDrive)); 
	fl.selected = fl.d; 
                 
	/* 
	 *  stop the motors 
	 */ 
	fl.motor = 0; 
	delay(10); 
	outb(Pdor, fl.motor | Fintena | Fena); 
	delay(10); 
                 
	/* 
	 *  init drives 
	 */ 
	for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){ 
		dp->dev = dp - fl.d; 
		dp->dt = T1440kb; 
		floppysetdef(dp); 
		dp->cyl = -1;			/* because we don't know */ 
		dp->cache = (uchar*)xspanalloc(maxtsize, BY2PG, 64*1024); 
		dp->ccyl = -1; 
		dp->vers = 0; 
	} 
                 
	/* 
	 *  first operation will recalibrate 
	 */ 
	fl.confused = 1; 
                 
	floppysetup1(&fl); 
} 
                 
static Chan* 
floppyattach(char *spec) 
{ 
	static int kstarted; 
                 
2001/1122    
	if(fl.ndrive == 0) 
		error(Enodev); 
                 
1999/0415    
	if(kstarted == 0){ 
		/* 
		 *  watchdog to turn off the motors 
		 */ 
		kstarted = 1; 
		kproc("floppy", floppykproc, 0); 
	} 
	return devattach('f', spec); 
} 
                 
2001/0727    
static Walkqid* 
floppywalk(Chan *c, Chan *nc, char **name, int nname) 
1999/0415    
{ 
2001/0727    
	return devwalk(c, nc, name, nname, floppydir, 1+fl.ndrive*NFDIR, devgen); 
1999/0415    
} 
                 
2001/0727    
static int 
floppystat(Chan *c, uchar *dp, int n) 
1999/0415    
{ 
2001/0727    
	return devstat(c, dp, n, floppydir, 1+fl.ndrive*NFDIR, devgen); 
1999/0415    
} 
                 
static Chan* 
floppyopen(Chan *c, int omode) 
{ 
2001/0727    
	return devopen(c, omode, floppydir, 1+fl.ndrive*NFDIR, devgen); 
1999/0415    
} 
                 
static void 
floppyclose(Chan *) 
{ 
} 
                 
static void 
islegal(ulong offset, long n, FDrive *dp) 
{ 
	if(offset % dp->t->bytes) 
		error(Ebadarg); 
	if(n % dp->t->bytes) 
		error(Ebadarg); 
} 
                 
/* 
 *  check if the floppy has been replaced under foot.  cause 
 *  an error if it has. 
 * 
 *  a seek and a read clears the condition.  this was determined 
 *  experimentally, there has to be a better way. 
 * 
 *  if the read fails, cycle through the possible floppy 
 *  density till one works or we've cycled through all 
 *  possibilities for this drive. 
 */ 
static void 
changed(Chan *c, FDrive *dp) 
{ 
	ulong old; 
	FType *start; 
                 
	/* 
	 *  if floppy has changed or first time through 
	 */ 
	if((inb(Pdir)&Fchange) || dp->vers == 0){ 
		DPRINT("changed\n"); 
		fldump(); 
		dp->vers++; 
		start = dp->t; 
2001/1122    
		dp->maxtries = 3;	/* limit it when we're probing */ 
                 
		/* floppyon will fail if there's a controller but no drive */ 
1999/0415    
		dp->confused = 1;	/* make floppyon recal */ 
2001/1122    
		if(floppyon(dp) < 0) 
			error(Eio); 
                 
		/* seek to the first track */ 
1999/0415    
		floppyseek(dp, dp->t->heads*dp->t->tsize); 
		while(waserror()){ 
2001/1122    
			/* 
			 *  if first attempt doesn't reset changed bit, there's 
			 *  no floppy there 
			 */ 
			if(inb(Pdir)&Fchange) 
				nexterror(); 
                 
1999/0415    
			while(++dp->t){ 
				if(dp->t == &floppytype[nelem(floppytype)]) 
					dp->t = floppytype; 
				if(dp->dt == dp->t->dt) 
					break; 
			} 
2001/0727    
			floppydir[1+NFDIR*dp->dev].length = dp->t->cap; 
2001/1122    
                 
			/* floppyon will fail if there's a controller but no drive */ 
			if(floppyon(dp) < 0) 
				error(Eio); 
                 
1999/0415    
			DPRINT("changed: trying %s\n", dp->t->name); 
			fldump(); 
			if(dp->t == start) 
				nexterror(); 
		} 
2001/1122    
                 
		/* if the read succeeds, we've got the density right */ 
1999/0415    
		floppyxfer(dp, Fread, dp->cache, 0, dp->t->tsize); 
		poperror(); 
2001/1122    
		dp->maxtries = 20; 
1999/0415    
	} 
                 
	old = c->qid.vers; 
	c->qid.vers = dp->vers; 
	if(old && old != dp->vers) 
		error(Eio); 
} 
                 
static int 
readtrack(FDrive *dp, int cyl, int head) 
{ 
	int i, nn, sofar; 
	ulong pos; 
                 
	nn = dp->t->tsize; 
	if(dp->ccyl==cyl && dp->chead==head) 
		return nn; 
	pos = (cyl*dp->t->heads+head) * nn; 
	for(sofar = 0; sofar < nn; sofar += i){ 
		dp->ccyl = -1; 
		i = floppyxfer(dp, Fread, dp->cache + sofar, pos + sofar, nn - sofar); 
		if(i <= 0) 
			return -1; 
	} 
	dp->ccyl = cyl; 
	dp->chead = head; 
	return nn; 
} 
                 
static long 
floppyread(Chan *c, void *a, long n, vlong off) 
{ 
	FDrive *dp; 
	long rv; 
	int sec, head, cyl; 
	long len; 
	uchar *aa; 
	ulong offset = off; 
                 
2001/0727    
	if(c->qid.type & QTDIR) 
		return devdirread(c, a, n, floppydir, 1+fl.ndrive*NFDIR, devgen); 
1999/0415    
                 
	rv = 0; 
	dp = &fl.d[c->qid.path & ~Qmask]; 
	switch ((int)(c->qid.path & Qmask)) { 
	case Qdata: 
		islegal(offset, n, dp); 
		aa = a; 
                 
		qlock(&fl); 
		if(waserror()){ 
			qunlock(&fl); 
			nexterror(); 
		} 
		floppyon(dp); 
		changed(c, dp); 
		for(rv = 0; rv < n; rv += len){ 
			/* 
			 *  all xfers come out of the track cache 
			 */ 
			dp->len = n - rv; 
			floppypos(dp, offset+rv); 
			cyl = dp->tcyl; 
			head = dp->thead; 
			len = dp->len; 
			sec = dp->tsec; 
			if(readtrack(dp, cyl, head) < 0) 
				break; 
			memmove(aa+rv, dp->cache + (sec-1)*dp->t->bytes, len); 
		} 
		qunlock(&fl); 
		poperror(); 
                 
		break; 
	case Qctl: 
		return readstr(offset, a, n, dp->t->name); 
	default: 
		panic("floppyread: bad qid"); 
	} 
                 
	return rv; 
} 
                 
static long 
floppywrite(Chan *c, void *a, long n, vlong off) 
{ 
	FDrive *dp; 
	long rv, i; 
	char *aa = a; 
2001/1122    
	Cmdbuf *cb; 
	Cmdtab *ct; 
1999/0415    
	ulong offset = off; 
                 
	rv = 0; 
	dp = &fl.d[c->qid.path & ~Qmask]; 
	switch ((int)(c->qid.path & Qmask)) { 
	case Qdata: 
		islegal(offset, n, dp); 
		qlock(&fl); 
		if(waserror()){ 
			qunlock(&fl); 
			nexterror(); 
		} 
		floppyon(dp); 
		changed(c, dp); 
		for(rv = 0; rv < n; rv += i){ 
			floppypos(dp, offset+rv); 
			if(dp->tcyl == dp->ccyl) 
				dp->ccyl = -1; 
			i = floppyxfer(dp, Fwrite, aa+rv, offset+rv, n-rv); 
			if(i < 0) 
				break; 
			if(i == 0) 
				error(Eio); 
		} 
		qunlock(&fl); 
		poperror(); 
		break; 
	case Qctl: 
		rv = n; 
2001/1122    
		cb = parsecmd(a, n); 
		if(waserror()){ 
			free(cb); 
			nexterror(); 
		} 
1999/0415    
		qlock(&fl); 
		if(waserror()){ 
			qunlock(&fl); 
			nexterror(); 
		} 
2001/1122    
		ct = lookupcmd(cb, floppyctlmsg, nelem(floppyctlmsg)); 
		switch(ct->index){ 
		case CMeject: 
1999/0415    
			floppyeject(dp); 
2001/1122    
			break; 
		case CMformat: 
			floppyformat(dp, cb); 
			break; 
		case CMreset: 
1999/0415    
			fl.confused = 1; 
			floppyon(dp); 
2001/1122    
			break; 
		case CMdebug: 
1999/0415    
			floppydebug = 1; 
2001/1122    
			break; 
		} 
1999/0415    
		poperror(); 
		qunlock(&fl); 
2001/1122    
		poperror(); 
		free(cb); 
1999/0415    
		break; 
	default: 
		panic("floppywrite: bad qid"); 
	} 
                 
	return rv; 
} 
                 
static void 
floppykproc(void *) 
{ 
	FDrive *dp; 
                 
	while(waserror()) 
		; 
	for(;;){ 
		for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){ 
			if((fl.motor&MOTORBIT(dp->dev)) 
			&& TK2SEC(m->ticks - dp->lasttouched) > 5 
			&& canqlock(&fl)){ 
				if(TK2SEC(m->ticks - dp->lasttouched) > 5) 
					floppyoff(dp); 
				qunlock(&fl); 
			} 
		} 
		tsleep(&fl.kr, return0, 0, 1000); 
	} 
} 
                 
/* 
 *  start a floppy drive's motor. 
 */ 
2001/1122    
static int 
1999/0415    
floppyon(FDrive *dp) 
{ 
	int alreadyon; 
	int tries; 
                 
	if(fl.confused) 
		floppyrevive(); 
                 
	/* start motor and select drive */ 
	alreadyon = fl.motor & MOTORBIT(dp->dev); 
	fl.motor |= MOTORBIT(dp->dev); 
	outb(Pdor, fl.motor | Fintena | Fena | dp->dev); 
	if(!alreadyon){ 
		/* wait for drive to spin up */ 
		tsleep(&dp->r, return0, 0, 750); 
                 
		/* clear any pending interrupts */ 
		floppysense(); 
	} 
                 
	/* set transfer rate */ 
	if(fl.rate != dp->t->rate){ 
		fl.rate = dp->t->rate; 
		outb(Pdsr, fl.rate); 
	} 
                 
	/* get drive to a known cylinder */ 
	if(dp->confused) 
		for(tries = 0; tries < 4; tries++) 
			if(floppyrecal(dp) >= 0) 
				break; 
	dp->lasttouched = m->ticks; 
	fl.selected = dp; 
2001/1122    
                 
	/* return -1 if this didn't work */ 
	if(dp->confused) 
		return -1; 
	return 0; 
1999/0415    
} 
                 
/* 
 *  stop the floppy if it hasn't been used in 5 seconds 
 */ 
static void 
floppyoff(FDrive *dp) 
{ 
	fl.motor &= ~MOTORBIT(dp->dev); 
	outb(Pdor, fl.motor | Fintena | Fena | dp->dev); 
} 
                 
/* 
 *  send a command to the floppy 
 */ 
static int 
floppycmd(void) 
{ 
	int i; 
	int tries; 
                 
	fl.nstat = 0; 
	for(i = 0; i < fl.ncmd; i++){ 
		for(tries = 0; ; tries++){ 
			if((inb(Pmsr)&(Ffrom|Fready)) == Fready) 
				break; 
			if(tries > 1000){ 
				DPRINT("cmd %ux can't be sent (%d)\n", fl.cmd[0], i); 
				fldump(); 
                 
				/* empty fifo, might have been a bad command */ 
				floppyresult(); 
				return -1; 
			} 
			microdelay(8);	/* for machine independence */ 
		} 
		outb(Pfdata, fl.cmd[i]); 
	} 
	return 0; 
} 
                 
/* 
 *  get a command result from the floppy 
 * 
 *  when the controller goes ready waiting for a command 
 *  (instead of sending results), we're done 
 *  
 */ 
static int 
floppyresult(void) 
{ 
	int i, s; 
	int tries; 
                 
	/* get the result of the operation */ 
	for(i = 0; i < sizeof(fl.stat); i++){ 
		/* wait for status byte */ 
		for(tries = 0; ; tries++){ 
			s = inb(Pmsr)&(Ffrom|Fready); 
			if(s == Fready){ 
				fl.nstat = i; 
				return fl.nstat; 
			} 
			if(s == (Ffrom|Fready)) 
				break; 
			if(tries > 1000){ 
				DPRINT("floppyresult: %d stats\n", i); 
				fldump(); 
				fl.confused = 1; 
				return -1; 
			} 
			microdelay(8);	/* for machine independence */ 
		} 
		fl.stat[i] = inb(Pfdata); 
	} 
	fl.nstat = sizeof(fl.stat); 
	return fl.nstat; 
} 
                 
/* 
 *  calculate physical address of a logical byte offset into the disk 
 * 
 *  truncate dp->length if it crosses a track boundary 
 */ 
static void 
floppypos(FDrive *dp, long off) 
{ 
	int lsec; 
	int ltrack; 
	int end; 
                 
	lsec = off/dp->t->bytes; 
	ltrack = lsec/dp->t->sectors; 
	dp->tcyl = ltrack/dp->t->heads; 
	dp->tsec = (lsec % dp->t->sectors) + 1; 
	dp->thead = (lsec/dp->t->sectors) % dp->t->heads; 
                 
	/* 
	 *  can't read across track boundaries. 
	 *  if so, decrement the bytes to be read. 
	 */ 
	end = (ltrack+1)*dp->t->sectors*dp->t->bytes; 
	if(off+dp->len > end) 
		dp->len = end - off; 
} 
                 
/* 
 *  get the interrupt cause from the floppy. 
 */ 
static int 
floppysense(void) 
{ 
	fl.ncmd = 0; 
	fl.cmd[fl.ncmd++] = Fsense; 
	if(floppycmd() < 0) 
		return -1; 
	if(floppyresult() < 2){ 
		DPRINT("can't read sense response\n"); 
		fldump(); 
		fl.confused = 1; 
		return -1; 
	} 
	return 0; 
} 
                 
static int 
cmddone(void *) 
{ 
	return fl.ncmd == 0; 
} 
                 
/* 
 *  Wait for a floppy interrupt.  If none occurs in 5 seconds, we 
 *  may have missed one.  This only happens on some portables which 
 *  do power management behind our backs.  Call the interrupt 
 *  routine to try to clear any conditions. 
 */ 
static void 
2001/1122    
floppywait(int slow) 
1999/0415    
{ 
2001/1122    
	tsleep(&fl.r, cmddone, 0, slow ? 5000 : 1000); 
1999/0415    
	if(!cmddone(0)){ 
		floppyintr(0); 
		fl.confused = 1; 
	} 
} 
                 
/* 
 *  we've lost the floppy position, go to cylinder 0. 
 */ 
static int 
floppyrecal(FDrive *dp) 
{ 
	dp->ccyl = -1; 
	dp->cyl = -1; 
                 
	fl.ncmd = 0; 
	fl.cmd[fl.ncmd++] = Frecal; 
	fl.cmd[fl.ncmd++] = dp->dev; 
	if(floppycmd() < 0) 
		return -1; 
2001/1122    
	floppywait(1); 
1999/0415    
	if(fl.nstat < 2){ 
		DPRINT("recalibrate: confused %ux\n", inb(Pmsr)); 
		fl.confused = 1; 
		return -1; 
	} 
	if((fl.stat[0] & (Codemask|Seekend)) != Seekend){ 
		DPRINT("recalibrate: failed\n"); 
		dp->confused = 1; 
		return -1; 
	} 
	dp->cyl = fl.stat[1]; 
	if(dp->cyl != 0){ 
		DPRINT("recalibrate: wrong cylinder %d\n", dp->cyl); 
		dp->cyl = -1; 
		dp->confused = 1; 
		return -1; 
	} 
                 
	dp->confused = 0; 
	return 0; 
} 
                 
/* 
 *  if the controller or a specific drive is in a confused state, 
1999/0501    
 *  reset it and get back to a known state 
1999/0415    
 */ 
static void 
floppyrevive(void) 
{ 
	FDrive *dp; 
                 
	/* 
	 *  reset the controller if it's confused 
	 */ 
	if(fl.confused){ 
		DPRINT("floppyrevive in\n"); 
		fldump(); 
                 
		/* reset controller and turn all motors off */ 
		splhi(); 
		fl.ncmd = 1; 
		fl.cmd[0] = 0; 
		outb(Pdor, 0); 
		delay(10); 
		outb(Pdor, Fintena|Fena); 
		delay(10); 
		spllo(); 
		fl.motor = 0; 
		fl.confused = 0; 
2001/1122    
		floppywait(0); 
1999/0415    
                 
		/* mark all drives in an unknown state */ 
		for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++) 
			dp->confused = 1; 
                 
		/* set rate to a known value */ 
		outb(Pdsr, 0); 
		fl.rate = 0; 
                 
		DPRINT("floppyrevive out\n"); 
		fldump(); 
	} 
} 
                 
/* 
 *  seek to the target cylinder 
 * 
 *	interrupt, no results 
 */ 
static long 
floppyseek(FDrive *dp, long off) 
{ 
	floppypos(dp, off); 
	if(dp->cyl == dp->tcyl) 
		return dp->tcyl; 
	dp->cyl = -1; 
                 
	fl.ncmd = 0; 
	fl.cmd[fl.ncmd++] = Fseek; 
	fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev; 
	fl.cmd[fl.ncmd++] = dp->tcyl * dp->t->steps; 
	if(floppycmd() < 0) 
		return -1; 
2001/1122    
	floppywait(1); 
1999/0415    
	if(fl.nstat < 2){ 
		DPRINT("seek: confused\n"); 
		fl.confused = 1; 
		return -1; 
	} 
	if((fl.stat[0] & (Codemask|Seekend)) != Seekend){ 
		DPRINT("seek: failed\n"); 
		dp->confused = 1; 
		return -1; 
	} 
                 
	dp->cyl = dp->tcyl; 
	return dp->tcyl; 
} 
                 
/* 
 *  read or write to floppy.  try up to three times. 
 */ 
static long 
floppyxfer(FDrive *dp, int cmd, void *a, long off, long n) 
{ 
	long offset; 
	int tries; 
                 
	if(off >= dp->t->cap) 
		return 0; 
	if(off + n > dp->t->cap) 
		n = dp->t->cap - off; 
                 
	/* retry on error (until it gets ridiculous) */ 
	tries = 0; 
	while(waserror()){ 
2001/1122    
		if(tries++ >= dp->maxtries) 
1999/0415    
			nexterror(); 
		DPRINT("floppyxfer: retrying\n"); 
	} 
                 
	dp->len = n; 
	if(floppyseek(dp, off) < 0){ 
		DPRINT("xfer: seek failed\n"); 
		dp->confused = 1; 
		error(Eio); 
	} 
                 
	/* 
	 *  set up the dma (dp->len may be trimmed) 
	 */ 
	if(waserror()){ 
		dmaend(DMAchan); 
		nexterror(); 
	} 
	dp->len = dmasetup(DMAchan, a, dp->len, cmd==Fread); 
	if(dp->len < 0) 
		error(Eio); 
                 
	/* 
	 *  start operation 
	 */ 
	fl.ncmd = 0; 
	fl.cmd[fl.ncmd++] = cmd | (dp->t->heads > 1 ? Fmulti : 0); 
	fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev; 
	fl.cmd[fl.ncmd++] = dp->tcyl; 
	fl.cmd[fl.ncmd++] = dp->thead; 
	fl.cmd[fl.ncmd++] = dp->tsec; 
	fl.cmd[fl.ncmd++] = dp->t->bcode; 
	fl.cmd[fl.ncmd++] = dp->t->sectors; 
	fl.cmd[fl.ncmd++] = dp->t->gpl; 
	fl.cmd[fl.ncmd++] = 0xFF; 
	if(floppycmd() < 0) 
		error(Eio); 
                 
	/* Poll ready bits and transfer data */ 
	floppyexec((char*)a, dp->len, cmd==Fread); 
                 
	/* 
	 *  give bus to DMA, floppyintr() will read result 
	 */ 
2001/1122    
	floppywait(0); 
1999/0415    
	dmaend(DMAchan); 
	poperror(); 
                 
	/* 
	 *  check for errors 
	 */ 
	if(fl.nstat < 7){ 
		DPRINT("xfer: confused\n"); 
		fl.confused = 1; 
		error(Eio); 
	} 
	if((fl.stat[0] & Codemask)!=0 || fl.stat[1] || fl.stat[2]){ 
		DPRINT("xfer: failed %ux %ux %ux\n", fl.stat[0], 
			fl.stat[1], fl.stat[2]); 
		DPRINT("offset %lud len %ld\n", off, dp->len); 
		if((fl.stat[0]&Codemask)==Cmdexec && fl.stat[1]==Overrun){ 
			DPRINT("DMA overrun: retry\n"); 
		} else 
			dp->confused = 1; 
		error(Eio); 
	} 
                 
	/* 
	 *  check for correct cylinder 
	 */ 
	offset = fl.stat[3] * dp->t->heads + fl.stat[4]; 
	offset = offset*dp->t->sectors + fl.stat[5] - 1; 
	offset = offset * c2b[fl.stat[6]]; 
	if(offset != off+dp->len){ 
		DPRINT("xfer: ends on wrong cyl\n"); 
		dp->confused = 1; 
		error(Eio); 
	} 
	poperror(); 
                 
	dp->lasttouched = m->ticks; 
	return dp->len; 
} 
                 
/* 
 *  format a track 
 */ 
static void 
2001/1122    
floppyformat(FDrive *dp, Cmdbuf *cb) 
1999/0415    
{ 
 	int cyl, h, sec; 
	ulong track; 
	uchar *buf, *bp; 
	FType *t; 
                 
	/* 
	 *  set the type 
	 */ 
2001/1122    
	if(cb->nf == 2){ 
1999/0415    
		for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){ 
2001/1122    
			if(strcmp(cb->f[1], t->name)==0 && t->dt==dp->dt){ 
1999/0415    
				dp->t = t; 
2001/0727    
				floppydir[1+NFDIR*dp->dev].length = dp->t->cap; 
1999/0415    
				break; 
			} 
		} 
		if(t >= &floppytype[nelem(floppytype)]) 
			error(Ebadarg); 
2001/1122    
	} else if(cb->nf == 1){ 
1999/0415    
		floppysetdef(dp); 
		t = dp->t; 
2001/1122    
	} else { 
		cmderror(cb, "invalid floppy format command"); 
		SET(t); 
1999/0415    
	} 
                 
	/* 
	 *  buffer for per track info 
	 */ 
	buf = smalloc(t->sectors*4); 
	if(waserror()){ 
		free(buf); 
		nexterror(); 
	} 
                 
	/* force a recalibrate to cylinder 0 */ 
	dp->confused = 1; 
	if(!waserror()){ 
		floppyon(dp); 
		poperror(); 
	} 
                 
	/* 
	 *  format a track at time 
	 */ 
	for(track = 0; track < t->tracks*t->heads; track++){ 
		cyl = track/t->heads; 
		h = track % t->heads; 
                 
		/* 
		 *  seek to track, ignore errors 
		 */ 
		floppyseek(dp, track*t->tsize); 
		dp->cyl = cyl; 
		dp->confused = 0; 
                 
		/* 
		 *  set up the dma (dp->len may be trimmed) 
		 */ 
		bp = buf; 
		for(sec = 1; sec <= t->sectors; sec++){ 
			*bp++ = cyl; 
			*bp++ = h; 
			*bp++ = sec; 
			*bp++ = t->bcode; 
		} 
		if(waserror()){ 
			dmaend(DMAchan); 
			nexterror(); 
		} 
		if(dmasetup(DMAchan, buf, bp-buf, 0) < 0) 
			error(Eio); 
                 
		/* 
		 *  start operation 
		 */ 
		fl.ncmd = 0; 
		fl.cmd[fl.ncmd++] = Fformat; 
		fl.cmd[fl.ncmd++] = (h<<2) | dp->dev; 
		fl.cmd[fl.ncmd++] = t->bcode; 
		fl.cmd[fl.ncmd++] = t->sectors; 
		fl.cmd[fl.ncmd++] = t->fgpl; 
		fl.cmd[fl.ncmd++] = 0x5a; 
		if(floppycmd() < 0) 
			error(Eio); 
                 
		/* Poll ready bits and transfer data */ 
		floppyexec((char *)buf, bp-buf, 0); 
                 
		/* 
		 *  give bus to DMA, floppyintr() will read result 
		 */ 
2001/1122    
		floppywait(1); 
1999/0415    
		dmaend(DMAchan); 
		poperror(); 
                 
		/* 
		 *  check for errors 
		 */ 
		if(fl.nstat < 7){ 
			DPRINT("format: confused\n"); 
			fl.confused = 1; 
			error(Eio); 
		} 
		if((fl.stat[0]&Codemask)!=0 || fl.stat[1]|| fl.stat[2]){ 
			DPRINT("format: failed %ux %ux %ux\n", 
				fl.stat[0], fl.stat[1], fl.stat[2]); 
			dp->confused = 1; 
			error(Eio); 
		} 
	} 
	free(buf); 
	dp->confused = 1; 
	poperror(); 
} 
                 
static void 
floppyintr(Ureg *) 
{ 
	switch(fl.cmd[0]&~Fmulti){ 
	case Fread: 
	case Fwrite: 
	case Fformat: 
	case Fdumpreg:  
		floppyresult(); 
		break; 
	case Fseek: 
	case Frecal: 
	default: 
		floppysense();	/* to clear interrupt */ 
		break; 
	} 
	fl.ncmd = 0; 
	wakeup(&fl.r); 
} 
                 
Dev floppydevtab = { 
	'f', 
	"floppy", 
                 
	floppyreset, 
	devinit, 
	floppyattach, 
	floppywalk, 
	floppystat, 
	floppyopen, 
	devcreate, 
	floppyclose, 
	floppyread, 
	devbread, 
	floppywrite, 
	devbwrite, 
	devremove, 
	devwstat, 
}; 
2001/1211    
#include "../pc/devfloppy.c" 


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