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

1992/1006/pc/devfloppy.c (diff list | history)

1992/1006/sys/src/9/pc/devfloppy.c:1,10491992/1007/sys/src/9/pc/devfloppy.c:1,1055 (short | long | prev | next)
1991/0727    
#include	"u.h" 
1992/0321    
#include	"../port/lib.h" 
1991/0727    
#include	"mem.h" 
#include	"dat.h" 
#include	"fns.h" 
#include	"io.h" 
1992/0111    
#include	"../port/error.h" 
1991/0727    
 
1991/0924    
/* Intel 82077A (8272A compatible) floppy controller */ 
1991/0920    
 
1991/0802    
typedef	struct Drive		Drive; 
1991/0727    
typedef	struct Controller	Controller; 
1991/0802    
typedef struct Type		Type; 
1991/0727    
 
1992/0901    
#define DPRINT if(0)print 
 
1991/0924    
/* bits in the registers */ 
1991/0727    
enum 
{ 
1991/0924    
	/* digital output register */ 
	Pdor=		0x3f2, 
	Fintena=	0x8,	/* enable floppy interrupt */ 
	Fena=		0x4,	/* 0 == reset controller */ 
1991/0727    
 
1991/0924    
	/* main status register */ 
	Pmsr=		0x3f4, 
	Fready=		0x80,	/* ready to be touched */ 
	Ffrom=		0x40,	/* data from controller */ 
	Fbusy=		0x10,	/* operation not over */ 
1991/0727    
 
1991/0924    
	/* data register */ 
	Pdata=		0x3f5, 
	Frecal=		0x07,	/* recalibrate cmd */ 
	Fseek=		0x0f,	/* seek cmd */ 
	Fsense=		0x08,	/* sense cmd */ 
	Fread=		0x66,	/* read cmd */ 
	Freadid=	0x4a,	/* read track id */ 
	Fspec=		0x03,	/* set hold times */ 
	Fwrite=		0x45,	/* write cmd */ 
1992/1006    
	Fformat=	0x4d,	/* format cmd */ 
1991/0924    
	Fmulti=		0x80,	/* or'd with Fread or Fwrite for multi-head */ 
	Fdumpreg=	0x0e,	/* dump internal registers */ 
1991/0727    
 
1991/0924    
	/* digital input register */ 
1991/0925    
	Pdir=		0x3F7,	/* disk changed port (read only) */ 
	Pdsr=		0x3F7,	/* data rate select port (write only) */ 
1991/0924    
	Fchange=	0x80,	/* disk has changed */ 
1991/0831    
 
1991/0727    
	/* status 0 byte */ 
	Drivemask=	3<<0, 
	Seekend=	1<<5, 
	Codemask=	(3<<6)|(3<<3), 
1991/0728    
 
	/* file types */ 
	Qdir=		0, 
1991/0802    
	Qdata=		(1<<2), 
1991/0920    
	Qctl=		(2<<2), 
1991/0802    
	Qmask=		(3<<2), 
1991/0924    
 
	DMAchan=	2,	/* floppy dma channel */ 
1991/0727    
}; 
 
/* 
1992/1006    
 *  types of drive (from PC equipment byte) 
 */ 
enum 
{ 
	Tnone=		0, 
	T360kb=		1, 
	T1200kb=	2, 
	T720kb=		3, 
	T1440kb=	4, 
}; 
 
/* 
1992/1003    
 *  floppy types (all MFM encoding) 
1991/0727    
 */ 
struct Type 
{ 
	char	*name; 
1992/1006    
	int	dt;		/* compatible drive type */ 
1991/0727    
	int	bytes;		/* bytes/sector */ 
	int	sectors;	/* sectors/track */ 
	int	heads;		/* number of heads */ 
	int	steps;		/* steps per cylinder */ 
	int	tracks;		/* tracks/disk */ 
	int	gpl;		/* intersector gap length for read/write */	 
1991/0731    
	int	fgpl;		/* intersector gap length for format */ 
1992/1003    
	int	rate;		/* rate code */ 
1991/0731    
 
	/* 
	 *  these depend on previous entries and are set filled in 
	 *  by floppyinit 
	 */ 
	int	bcode;		/* coded version of bytes for the controller */ 
1991/0727    
	long	cap;		/* drive capacity in bytes */ 
1991/0924    
	long	tsize;		/* track size in bytes */ 
1991/0727    
}; 
Type floppytype[] = 
{ 
1992/1006    
 { "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, }, 
 { "5¼DD",	T360kb,  512,  9, 2, 1, 40, 0x2A, 0x50, 2, }, 
1991/0727    
}; 
1991/0731    
#define NTYPES (sizeof(floppytype)/sizeof(Type)) 
 
/* 
1991/0802    
 *  bytes per sector encoding for the controller. 
 *  - index for b2c is is (bytes per sector/128). 
 *  - index for c2b is code from b2c 
1991/0731    
 */ 
static int b2c[] = 
1991/0727    
{ 
1991/0731    
[1]	0, 
[2]	1, 
[4]	2, 
[8]	3, 
}; 
static int c2b[] = 
{ 
1991/0727    
	128, 
	256, 
	512, 
1991/0731    
	1024, 
1991/0727    
}; 
 
/* 
 *  a floppy drive 
 */ 
struct Drive 
{ 
1992/1006    
	Type	*t;		/* floppy type */ 
	int	dt;		/* drive type */ 
1991/0727    
	int	dev; 
 
	ulong	lasttouched;	/* time last touched */ 
1992/1006    
	int	cyl;		/* current arm position */ 
1991/0924    
	int	confused;	/* needs to be recalibrated */ 
1991/0925    
	int	vers; 
1991/0727    
 
	int	tcyl;		/* target cylinder */ 
	int	thead;		/* target head */ 
	int	tsec;		/* target sector */ 
1991/0731    
	long	len;		/* size of xfer */ 
1991/0727    
 
1991/0924    
	uchar	*cache;	/* track cache */ 
	int	ccyl; 
	int	chead; 
1991/0809    
 
1991/0924    
	Rendez	r;		/* waiting here for motor to spin up */ 
1991/0727    
}; 
 
/* 
1991/0924    
 *  controller for 4 floppys 
1991/0727    
 */ 
struct Controller 
{ 
1991/0731    
	QLock;			/* exclusive access to the contoller */ 
 
1991/0809    
	Drive	*d;		/* the floppy drives */ 
1991/0925    
	Drive	*selected; 
1992/1003    
	int	rate;		/* current rate selected */ 
1991/0924    
	uchar	cmd[14];	/* command */ 
	int	ncmd;		  /* # command bytes */ 
	uchar	stat[14];	/* command status */ 
	int	nstat;		  /* # status bytes */ 
	int	confused;	/* controler needs to be reset */ 
1991/0731    
	Rendez	r;		/* wait here for command termination */ 
1991/0924    
	int	motor;		/* bit mask of spinning disks */ 
1991/0802    
	Rendez	kr;		/* for motor watcher */ 
1991/0727    
}; 
 
1991/0924    
Controller	fl; 
1991/0727    
 
1991/0921    
#define MOTORBIT(i)	(1<<((i)+4)) 
 
1991/0727    
/* 
1991/0731    
 *  predeclared 
 */ 
1991/0924    
static int	floppycmd(void); 
static void	floppyeject(Drive*); 
1991/0925    
static void	floppyintr(Ureg*); 
1991/0731    
static void	floppykproc(void*); 
1991/0924    
static void	floppyon(Drive*); 
static void	floppyoff(Drive*); 
1991/0802    
static void	floppypos(Drive*,long); 
1991/0731    
static int	floppyrecal(Drive*); 
1991/0924    
static int	floppyresult(void); 
1991/0731    
static void	floppyrevive(void); 
1991/0924    
static long	floppyseek(Drive*, long); 
static int	floppysense(void); 
static void	floppywait(void); 
1991/0802    
static long	floppyxfer(Drive*, int, void*, long, long); 
1992/1006    
static int	floppyformat(Chan*, Drive*, int, char); 
1992/1007    
static int	floppyformat(Drive*, ulong, ulong); 
1991/1006    
static long	floppythrice(Drive*, int, void*, long, long); 
1991/0924    
static int	cmddone(void*); 
void Xdelay(int); 
1991/0731    
 
1991/0802    
Dirtab floppydir[]={ 
1991/1112    
	"fd0disk",		{Qdata + 0},	0,	0666, 
	"fd0ctl",		{Qctl + 0},	0,	0666, 
	"fd1disk",		{Qdata + 1},	0,	0666, 
	"fd1ctl",		{Qctl + 1},	0,	0666, 
	"fd2disk",		{Qdata + 2},	0,	0666, 
	"fd2ctl",		{Qctl + 2},	0,	0666, 
	"fd3disk",		{Qdata + 3},	0,	0666, 
	"fd3ctl",		{Qctl + 3},	0,	0666, 
1991/0802    
}; 
1991/0811    
#define NFDIR	2	/* directory entries/drive */ 
1991/0731    
 
1992/1006    
/* 
 *  set floppy drive to its default type 
 */ 
static void 
setdef(Drive *dp) 
{ 
	Type *t; 
 
	for(t = floppytype; t < &floppytype[NTYPES]; t++) 
		if(dp->dt == t->dt){ 
			dp->t = t; 
			floppydir[NFDIR*dp->dev].length = dp->t->cap; 
			break; 
		} 
} 
 
1991/0731    
void 
floppyreset(void) 
{ 
	Drive *dp; 
1991/0802    
	Type *t; 
1992/1006    
	uchar equip; 
1991/0731    
 
1991/0802    
	/* 
	 *  init dependent parameters 
	 */ 
	for(t = floppytype; t < &floppytype[NTYPES]; t++){ 
		t->cap = t->bytes * t->heads * t->sectors * t->tracks; 
		t->bcode = b2c[t->bytes/128]; 
1991/0924    
		t->tsize = t->bytes * t->sectors; 
1991/0802    
	} 
 
	/* 
1991/0809    
	 *  allocate the drive storage 
	 */ 
1992/0625    
	fl.d = xalloc(conf.nfloppy*sizeof(Drive)); 
1991/0925    
	fl.selected = fl.d; 
1991/0809    
 
	/* 
1991/0802    
	 *  stop the motors 
	 */ 
1991/0924    
	fl.motor = 0; 
	delay(10); 
	outb(Pdor, fl.motor | Fintena | Fena); 
	delay(10); 
 
	/* 
	 *  init drives 
	 */ 
	for(dp = fl.d; dp < &fl.d[conf.nfloppy]; dp++){ 
		dp->dev = dp - fl.d; 
1992/1006    
		dp->dt = T1440kb; 
		setdef(dp); 
		dp->cyl = -1;			/* because we don't know */ 
1992/0625    
		dp->cache = (uchar*)xspanalloc(dp->t->tsize, BY2PG, 0); 
1991/0924    
		dp->ccyl = -1; 
1991/0925    
		dp->vers = 1; 
1991/0731    
	} 
1991/0809    
 
	/* 
1992/1006    
	 *  read nvram for types of floppies 0 & 1 
	 */ 
	equip = nvramread(0x10); 
	if(conf.nfloppy > 0){ 
		fl.d[0].dt = (equip >> 4) & 0xf; 
		setdef(&fl.d[0]); 
	} 
	if(conf.nfloppy > 1){ 
		fl.d[1].dt = equip & 0xf; 
		setdef(&fl.d[1]); 
	} 
 
	/* 
1991/0924    
	 *  first operation will recalibrate 
1991/0809    
	 */ 
1991/0924    
	fl.confused = 1; 
1991/0925    
	setvec(Floppyvec, floppyintr); 
1991/0731    
} 
 
void 
floppyinit(void) 
{ 
} 
1991/0802    
 
Chan* 
floppyattach(char *spec) 
{ 
1991/0920    
	static int kstarted; 
 
	if(kstarted == 0){ 
		/* 
		 *  watchdog to turn off the motors 
		 */ 
		kstarted = 1; 
		kproc("floppy", floppykproc, 0); 
	} 
1991/0802    
	return devattach('f', spec); 
} 
 
Chan* 
floppyclone(Chan *c, Chan *nc) 
{ 
	return devclone(c, nc); 
} 
 
int 
floppywalk(Chan *c, char *name) 
{ 
1991/0811    
	return devwalk(c, name, floppydir, conf.nfloppy*NFDIR, devgen); 
1991/0802    
} 
 
void 
floppystat(Chan *c, char *dp) 
{ 
1991/0811    
	devstat(c, dp, floppydir, conf.nfloppy*NFDIR, devgen); 
1991/0802    
} 
 
Chan* 
floppyopen(Chan *c, int omode) 
{ 
1991/1203    
	return devopen(c, omode, floppydir, conf.nfloppy*NFDIR, devgen); 
1991/0802    
} 
 
void 
floppycreate(Chan *c, char *name, int omode, ulong perm) 
{ 
1992/0711    
	USED(c, name, omode, perm); 
1991/0802    
	error(Eperm); 
} 
 
void 
floppyclose(Chan *c) 
{ 
1992/0711    
	USED(c); 
1991/0802    
} 
 
void 
floppyremove(Chan *c) 
{ 
1992/0711    
	USED(c); 
1991/0802    
	error(Eperm); 
} 
 
void 
floppywstat(Chan *c, char *dp) 
{ 
1992/0711    
	USED(c, dp); 
1991/0802    
	error(Eperm); 
} 
 
1991/0925    
static void 
islegal(Chan *c, long n, Drive *dp) 
{ 
	if(c->offset % dp->t->bytes) 
1992/0114    
		error(Ebadarg); 
1991/0925    
	if(n % dp->t->bytes) 
1992/0114    
		error(Ebadarg); 
1991/0925    
} 
 
/* 
1992/1002    
 *  check if the floppy has been replaced under foot.  cause a read error if it has. 
1991/0925    
 * 
 *  a seek and a read clears the condition.  this was determined experimentally, 
 *  there has to be a better way. 
 */ 
static void 
changed(Chan *c, Drive *dp) 
{ 
	ulong old; 
 
1991/1003    
	if(inb(Pdir)&Fchange){ 
1992/1006    
		setdef(dp); 
1991/0925    
		dp->vers++; 
1992/1006    
		dp->confused = 1; 
		floppyon(dp); 
		if(dp->cyl) 
1992/1002    
			floppythrice(dp, Fread, dp->cache, 0, dp->t->tsize); 
1991/0925    
		else 
1992/1003    
			floppythrice(dp, Fread, dp->cache, dp->t->heads*dp->t->tsize, 
					dp->t->tsize); 
1991/0925    
	} 
1991/1003    
	old = c->qid.vers; 
	c->qid.vers = dp->vers; 
1992/1003    
	if(old && old!=dp->vers){ 
1992/0114    
		error(Eio); 
1992/1003    
	} 
1991/0925    
} 
 
1991/0731    
long 
floppyread(Chan *c, void *a, long n) 
{ 
	Drive *dp; 
1991/0924    
	long rv, i; 
	int nn, sec, head, cyl; 
	long len; 
	uchar *aa; 
1991/0731    
 
1991/0802    
	if(c->qid.path == CHDIR) 
1991/0811    
		return devdirread(c, a, n, floppydir, conf.nfloppy*NFDIR, devgen); 
1991/0802    
 
	rv = 0; 
1991/0924    
	dp = &fl.d[c->qid.path & ~Qmask]; 
1991/0802    
	switch ((int)(c->qid.path & Qmask)) { 
	case Qdata: 
1991/0925    
		islegal(c, n, dp); 
1991/0924    
		aa = a; 
1991/0925    
 
		qlock(&fl); 
		if(waserror()){ 
			qunlock(&fl); 
			nexterror(); 
		} 
		floppyon(dp); 
		changed(c, dp); 
1991/0809    
		for(rv = 0; rv < n; rv += len){ 
			/* 
1991/0924    
			 *  truncate xfer at track boundary 
1991/0809    
			 */ 
			dp->len = n - rv; 
			floppypos(dp, c->offset+rv); 
			cyl = dp->tcyl; 
1991/0924    
			head = dp->thead; 
1991/0809    
			len = dp->len; 
1991/0924    
			sec = dp->tsec; 
1992/1003    
			nn = dp->t->tsize; 
1991/0809    
 
			/* 
1991/0924    
			 *  read the track 
1991/0809    
			 */ 
1991/0924    
			if(dp->ccyl!=cyl || dp->chead!=head){ 
1991/0809    
				dp->ccyl = -1; 
1991/1006    
				i = floppythrice(dp, Fread, dp->cache, 
1991/0924    
					(cyl*dp->t->heads+head)*nn, nn); 
				if(i != nn){ 
					if(i == 0) 
						break; 
1992/1003    
					len = 0; 
					continue; 
1991/0924    
				} 
1991/0809    
				dp->ccyl = cyl; 
1991/0924    
				dp->chead = head; 
1991/0809    
			} 
1991/0924    
			memmove(aa+rv, dp->cache + (sec-1)*dp->t->bytes, len); 
1991/0802    
		} 
1991/0925    
		qunlock(&fl); 
		poperror(); 
 
1991/0802    
		break; 
1991/0924    
	case Qctl: 
		break; 
1992/1007    
		return readstr(offset, a, n, dp->t->name); 
1991/0802    
	default: 
		panic("floppyread: bad qid"); 
1991/0731    
	} 
1991/0924    
 
1991/0731    
	return rv; 
} 
 
1991/0924    
#define SNCMP(a, b) strncmp(a, b, sizeof(b)-1) 
1991/0731    
long 
floppywrite(Chan *c, void *a, long n) 
{ 
	Drive *dp; 
	long rv, i; 
1991/0924    
	char *aa = a; 
1992/1006    
	char *f[3]; 
	char ctlmsg[64]; 
1991/0731    
 
1991/0802    
	rv = 0; 
1991/0924    
	dp = &fl.d[c->qid.path & ~Qmask]; 
1991/0802    
	switch ((int)(c->qid.path & Qmask)) { 
	case Qdata: 
1991/0925    
		islegal(c, n, dp); 
		qlock(&fl); 
		if(waserror()){ 
			qunlock(&fl); 
			nexterror(); 
		} 
		floppyon(dp); 
		changed(c, dp); 
1991/0802    
		for(rv = 0; rv < n; rv += i){ 
1991/0924    
			floppypos(dp, c->offset+rv); 
			if(dp->tcyl == dp->ccyl) 
				dp->ccyl = -1; 
1992/1006    
			i = floppythrice(dp, Fwrite, aa+rv, c->offset+rv, n-rv); 
1991/1006    
			if(i < 0) 
1991/0802    
				break; 
1991/1006    
			if(i == 0) 
				error(Eio); 
1991/0802    
		} 
1991/0925    
		qunlock(&fl); 
		poperror(); 
1991/0802    
		break; 
1991/0924    
	case Qctl: 
1991/0925    
		qlock(&fl); 
1992/1007    
		if(waserror()){ 
			qunlock(&fl); 
			nexterror(); 
		} 
1992/1006    
		if(n >= sizeof(ctlmsg)) 
			n = sizeof(ctlmsg) - 1; 
		memmove(ctlmsg, aa, n); 
		ctlmsg[n] = 0; 
		if(SNCMP(ctlmsg, "eject") == 0){ 
1991/0924    
			floppyeject(dp); 
1992/1006    
		} else if(SNCMP(ctlmsg, "reset") == 0){ 
1991/0925    
			fl.confused = 1; 
1991/0924    
			floppyon(dp); 
1992/1006    
		} else if(SNCMP(ctlmsg, "format") == 0){ 
			if(getfields(ctlmsg, f, 3, ' ') != 3) 
				error(Ebadarg); 
			rv = n*floppyformat(c, dp, atoi(f[1]), *f[2]); 
1992/1007    
			rv = n*floppyformat(dp, strtoul(f[1], 0, 0), strtoul(f[2], 0, 0)); 
1991/0924    
		} 
1992/1007    
		poperror(); 
1991/0925    
		qunlock(&fl); 
1991/0924    
		break; 
1991/0802    
	default: 
		panic("floppywrite: bad qid"); 
1991/0731    
	} 
1991/0924    
 
1991/0731    
	return rv; 
} 
 
static void 
floppykproc(void *a) 
1991/0727    
{ 
	Drive *dp; 
 
1992/0711    
	USED(a); 
1991/0919    
	while(waserror()) 
		; 
1991/0802    
	for(;;){ 
1991/0924    
		for(dp = fl.d; dp < &fl.d[conf.nfloppy]; dp++){ 
			if((fl.motor&MOTORBIT(dp->dev)) 
1991/0921    
			&& TK2SEC(m->ticks - dp->lasttouched) > 5 
1991/0925    
			&& canqlock(&fl)){ 
1991/0802    
				if(TK2SEC(m->ticks - dp->lasttouched) > 5) 
1991/0924    
					floppyoff(dp); 
1991/0925    
				qunlock(&fl); 
1991/0802    
			} 
1991/0728    
		} 
1991/0925    
		tsleep(&fl.kr, return0, 0, 1000); 
1991/0727    
	} 
} 
 
1991/0731    
/* 
1991/0924    
 *  start a floppy drive's motor. 
1991/0921    
 */ 
static void 
1991/0924    
floppyon(Drive *dp) 
1991/0921    
{ 
	int alreadyon; 
1991/0924    
	int tries; 
1991/0921    
 
1991/0924    
	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); 
1991/0921    
	if(!alreadyon) 
		tsleep(&dp->r, return0, 0, 750); 
1991/0924    
 
1992/1003    
	/* set transfer rate */ 
	if(fl.rate != dp->t->rate){ 
		fl.rate = dp->t->rate; 
		outb(Pdsr, fl.rate); 
	} 
 
1991/0924    
	/* get drive to a known cylinder */ 
	if(dp->confused) 
		for(tries = 0; tries < 4; tries++) 
			if(floppyrecal(dp) >= 0) 
				break; 
1991/0921    
	dp->lasttouched = m->ticks; 
1991/0925    
	fl.selected = dp; 
1991/0921    
} 
 
/* 
 *  stop the floppy if it hasn't been used in 5 seconds 
 */ 
static void 
1991/0924    
floppyoff(Drive *dp) 
1991/0921    
{ 
1991/0924    
	fl.motor &= ~MOTORBIT(dp->dev); 
	outb(Pdor, fl.motor | Fintena | Fena | dp->dev); 
1991/0925    
	fl.selected = dp; 
1991/0921    
} 
 
/* 
1991/0924    
 *  eject disk ( unknown on safari ) 
1991/0731    
 */ 
1991/0924    
static void 
floppyeject(Drive *dp) 
1991/0727    
{ 
1991/0924    
	floppyon(dp); 
1991/0925    
	dp->vers++; 
1991/0924    
	floppyoff(dp); 
1991/0727    
} 
 
1991/0731    
/* 
1991/0924    
 *  send a command to the floppy 
1991/0731    
 */ 
static int 
1991/0924    
floppycmd(void) 
1991/0727    
{ 
1991/0924    
	int i; 
1991/0727    
	int tries; 
 
1991/0924    
	for(i = 0; i < fl.ncmd; i++){ 
		for(tries = 0; ; tries++){ 
			if(tries > 1000){ 
1992/0901    
			DPRINT("cmd %ux can't be sent (%d %ux)\n", fl.cmd[0], i, inb(Pmsr)); 
1991/0924    
				fl.confused = 1; 
				return -1; 
			} 
			if((inb(Pmsr)&(Ffrom|Fready)) == Fready) 
				break; 
		} 
		outb(Pdata, fl.cmd[i]); 
1991/0727    
	} 
1991/0924    
	return 0; 
1991/0727    
} 
 
1991/0731    
/* 
1991/0924    
 *  get a command result from the floppy 
 * 
 *  when the controller goes ready waiting for a command 
 *  (instead of sending results), we're done 
 *  
1991/0731    
 */ 
static int 
1991/0924    
floppyresult(void) 
1991/0727    
{ 
1991/0924    
	int i, s; 
	int tries; 
1991/0727    
 
1991/0924    
	for(i = 0; i < sizeof(fl.stat); i++){ 
		for(tries = 0; ; tries++){ 
			if(tries > 1000){ 
				fl.confused = 1; 
				return -1; 
			} 
			s = inb(Pmsr)&(Ffrom|Fready); 
			if(s == Fready){ 
				fl.nstat = i; 
				return i; 
			} 
			if(s == (Ffrom|Fready)) 
				break; 
		} 
		fl.stat[i] = inb(Pdata); 
1991/0727    
	} 
1991/0924    
	fl.nstat = i; 
	return i; 
1991/0727    
} 
 
1991/0731    
/* 
 *  calculate physical address of a logical byte offset into the disk 
 * 
1991/0924    
 *  truncate dp->length if it crosses a track boundary 
1991/0731    
 */ 
static void 
1991/0802    
floppypos(Drive *dp, long off) 
1991/0727    
{ 
	int lsec; 
1991/0924    
	int ltrack; 
1991/0727    
	int end; 
 
1991/0802    
	lsec = off/dp->t->bytes; 
1991/0924    
	ltrack = lsec/dp->t->sectors; 
	dp->tcyl = ltrack/dp->t->heads; 
1991/0727    
	dp->tsec = (lsec % dp->t->sectors) + 1; 
	dp->thead = (lsec/dp->t->sectors) % dp->t->heads; 
 
	/* 
1991/0924    
	 *  can't read across track boundaries. 
1991/0727    
	 *  if so, decrement the bytes to be read. 
	 */ 
1991/0924    
	end = (ltrack+1)*dp->t->sectors*dp->t->bytes; 
	if(off+dp->len > end) 
		dp->len = end - off; 
1991/0727    
} 
 
1991/0731    
/* 
1991/0924    
 *  get the interrupt cause from the floppy. 
1991/0731    
 */ 
static int 
1991/0924    
floppysense(void) 
1991/0727    
{ 
1991/0924    
	fl.ncmd = 0; 
	fl.cmd[fl.ncmd++] = Fsense; 
	if(floppycmd() < 0) 
1991/0727    
		return -1; 
1991/0924    
	if(floppyresult() < 2){ 
1992/0901    
		DPRINT("can't read sense response\n"); 
1991/0924    
		fl.confused = 1; 
1991/0727    
		return -1; 
	} 
	return 0; 
} 
 
1991/0924    
static int 
cmddone(void *a) 
{ 
1992/0711    
	USED(a); 
1991/0924    
	return fl.ncmd == 0; 
} 
 
1991/0731    
/* 
1991/0924    
 *  wait for a floppy interrupt 
1991/0731    
 */ 
1991/0924    
static void 
floppywait(void) 
1991/0731    
{ 
1991/0924    
	tsleep(&fl.r, cmddone, 0, 2000); 
1991/0731    
} 
 
/* 
 *  we've lost the floppy position, go to cylinder 0. 
 */ 
static int 
1991/0727    
floppyrecal(Drive *dp) 
{ 
1991/0924    
	dp->ccyl = -1; 
1991/0727    
 
1991/0924    
	fl.ncmd = 0; 
	fl.cmd[fl.ncmd++] = Frecal; 
	fl.cmd[fl.ncmd++] = dp->dev; 
	if(floppycmd() < 0) 
		return -1; 
	floppywait(); 
	if(fl.nstat < 2){ 
		fl.confused = 1; 
		return -1; 
	} 
	if((fl.stat[0] & (Codemask|Seekend)) != Seekend){ 
1991/0727    
		dp->confused = 1; 
		return -1; 
	} 
1992/1006    
	dp->cyl = fl.stat[1]; 
	if(dp->cyl != 0){ 
		DPRINT("recalibrate went to wrong cylinder %d\n", dp->cyl); 
1991/0727    
		dp->confused = 1; 
		return -1; 
	} 
 
	dp->confused = 0; 
	return 0; 
} 
 
1991/0731    
/* 
 *  if the controller or a specific drive is in a confused state, 
 *  reset it and get back to a kown state 
 */ 
1991/0727    
void 
1991/0731    
floppyrevive(void) 
1991/0727    
{ 
	Drive *dp; 
 
	/* 
1991/0924    
	 *  reset the controller if it's confused 
1991/0727    
	 */ 
1991/0924    
	if(fl.confused){ 
1991/0727    
		/* reset controller and turn all motors off */ 
		splhi(); 
1991/0924    
		fl.cmd[0] = 0; 
		outb(Pdor, 0); 
1991/0727    
		delay(1); 
1991/0924    
		outb(Pdor, Fintena|Fena); 
1991/0727    
		spllo(); 
1991/0924    
		for(dp = fl.d; dp < &fl.d[conf.nfloppy]; dp++) 
1991/0727    
			dp->confused = 1; 
1991/0924    
		fl.motor = 0; 
		floppywait(); 
		fl.confused = 0; 
1991/0925    
		outb(Pdsr, 0); 
1992/1003    
		fl.rate = 0; 
1991/0727    
	} 
} 
 
1991/0924    
/* 
 *  seek to the target cylinder 
 * 
 *	interrupt, no results 
 */ 
1991/0731    
static long 
1991/0924    
floppyseek(Drive *dp, long off) 
1991/0727    
{ 
1991/0924    
	floppypos(dp, off); 
1992/1006    
	if(dp->cyl == dp->tcyl) 
1992/1003    
		return dp->tcyl; 
1991/0727    
 
1992/0901    
	DPRINT("seeking tcyl %d, thead %d\n", dp->tcyl, dp->thead); 
1991/0924    
	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){ 
1992/0901    
		DPRINT("seek cmd failed\n"); 
1991/0727    
		return -1; 
	} 
1991/0924    
	floppywait(); 
	if(fl.nstat < 2){ 
1992/1003    
		DPRINT("seek cmd confused\n"); 
1991/0924    
		fl.confused = 1; 
1991/0727    
		return -1; 
1991/0924    
	} 
	if((fl.stat[0] & (Codemask|Seekend)) != Seekend){ 
1992/0901    
		DPRINT("seek failed\n"); 
1991/0727    
		dp->confused = 1; 
		return -1; 
	} 
 
1992/1006    
	dp->cyl = dp->tcyl; 
1992/1003    
	return dp->tcyl; 
1991/0727    
} 
 
1991/1006    
/* 
1992/1002    
 *  since floppies are so flakey, automaticly retry failed attempts. 
 *  every 3 tries switch to a different density 
1991/1006    
 */ 
1991/0731    
static long 
1991/1006    
floppythrice(Drive *dp, int cmd, void *a, long off, long n) 
{ 
	int tries; 
	long rv; 
1992/1003    
	Type *start; 
1991/1006    
 
1992/1003    
	start = dp->t; 
1991/1006    
	for(tries = 0; ; tries++){ 
		if(waserror()){ 
1992/1003    
			if(cmd != Fread || strcmp(u->error, Eintr)==0) 
1991/1006    
				nexterror(); 
1992/1003    
 
			/* walk through the compatible types */ 
			if(tries == 3){ 
1992/1006    
				while(++dp->t){ 
					if(dp->t == &floppytype[NTYPES]) 
						dp->t = floppytype; 
					if(dp->dt == Tnone) 
						break; 
					if(dp->dt == dp->t->dt) 
						break; 
				} 
1992/1003    
				floppydir[NFDIR*dp->dev].length = dp->t->cap; 
				if(dp->t == start) 
					nexterror(); 
				tries = 0; 
				floppyon(dp); 
			} 
1991/1006    
		} else { 
			rv = floppyxfer(dp, cmd, a, off, n); 
			poperror(); 
			return rv; 
		} 
	} 
} 
 
static long 
1991/0802    
floppyxfer(Drive *dp, int cmd, void *a, long off, long n) 
1991/0727    
{ 
	long offset; 
 
1991/0924    
	if(off >= dp->t->cap) 
		return 0; 
	if(off + n > dp->t->cap) 
		n = dp->t->cap - off; 
 
1991/0727    
	/* 
1991/0731    
	 *  calculate new position and seek to it (dp->len may be trimmed) 
	 */ 
1991/0727    
	dp->len = n; 
1991/0924    
	if(floppyseek(dp, off) < 0) 
1992/0114    
		error(Eio); 
1991/0925    
 
1992/0901    
	DPRINT("tcyl %d, thead %d, tsec %d, addr %lux, n %d\n", 
	dp->tcyl, dp->thead, dp->tsec, a, dp->len); 
1991/0727    
 
	/* 
1991/0731    
	 *  set up the dma (dp->len may be trimmed) 
1991/0727    
	 */ 
1991/0924    
	dp->len = dmasetup(DMAchan, a, dp->len, cmd==Fread); 
1991/0727    
 
	/* 
1991/0731    
	 *  start operation 
1991/0727    
	 */ 
1991/0925    
	cmd = cmd | (dp->t->heads > 1 ? Fmulti : 0); 
1991/0924    
	fl.ncmd = 0; 
	fl.cmd[fl.ncmd++] = cmd; 
	fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev; 
1992/1006    
	fl.cmd[fl.ncmd++] = dp->tcyl; 
1991/0924    
	fl.cmd[fl.ncmd++] = dp->thead; 
	fl.cmd[fl.ncmd++] = dp->tsec; 
	fl.cmd[fl.ncmd++] = dp->t->bcode; 
1991/1001    
	fl.cmd[fl.ncmd++] = dp->t->sectors; 
1991/0924    
	fl.cmd[fl.ncmd++] = dp->t->gpl; 
	fl.cmd[fl.ncmd++] = 0xFF; 
	if(floppycmd() < 0){ 
		spllo(); 
1992/0901    
		DPRINT("xfer cmd failed\n"); 
1992/0114    
		error(Eio); 
1991/0727    
	} 
 
	/* 
1991/0924    
	 *  give bus to DMA, floppyintr() will read result 
	 */ 
	floppywait(); 
	dmaend(DMAchan); 
 
	/* 
	 *  check for errors 
	 */ 
	if(fl.nstat < 7){ 
1992/0901    
		DPRINT("xfer result failed %lux\n", inb(Pmsr)); 
1991/0924    
		fl.confused = 1; 
1992/0114    
		error(Eio); 
1991/0727    
	} 
1991/0924    
	if((fl.stat[0] & Codemask)!=0 || fl.stat[1] || fl.stat[2]){ 
1992/0901    
		DPRINT("xfer failed %lux %lux %lux\n", fl.stat[0], 
1991/0925    
			fl.stat[1], fl.stat[2]); 
1992/0901    
		DPRINT("offset %lud len %d\n", off, dp->len); 
1991/0925    
		dp->confused = 1; 
1992/0114    
		error(Eio); 
1991/0727    
	} 
 
1991/0924    
	/* 
	 *  check for correct cylinder 
	 */ 
1992/1006    
	offset = fl.stat[3] * dp->t->heads + fl.stat[4]; 
1991/0924    
	offset = offset*dp->t->sectors + fl.stat[5] - 1; 
	offset = offset * c2b[fl.stat[6]]; 
1991/0802    
	if(offset != off+dp->len){ 
1992/1003    
		DPRINT("xfer ends on wrong cyl\n"); 
1991/0727    
		dp->confused = 1; 
1992/0114    
		error(Eio); 
1991/0727    
	} 
 
1991/0802    
	dp->lasttouched = m->ticks; 
1991/0727    
	return dp->len; 
} 
 
1992/1006    
/* 
 *  format a track 
 */ 
static int 
floppyformat(Chan *c, Drive *dp, int track, char filler) 
1992/1007    
floppyformat(Drive *dp, ulong track, ulong filler) 
1992/1006    
{ 
 	int cyl, h, sec; 
	uchar *buf, *bp; 
	Type *t; 
 
	t = dp->t; 
	cyl = track/t->heads; 
	h = track % t->heads; 
	if(track >= t->tracks * t->heads) 
		return 0; 
1992/1007    
	setdef(dp); 
1992/1006    
	buf = smalloc(t->sectors*4); 
                 
	qlock(&fl); 
	if(waserror()){ 
		qunlock(&fl); 
		free(buf); 
		nexterror(); 
	} 
	floppyon(dp); 
	changed(c, dp); 
	if(floppyseek(dp, track*t->tsize) < 0) 
		error(Eio); 
1992/1007    
	if(!waserror()){ 
		floppyon(dp); 
		poperror(); 
	} 
	floppyseek(dp, track*t->tsize); 
	dp->cyl = cyl; 
	dp->confused = 0; 
1992/1006    
 
	/* 
	 *  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; 
	} 
	dmasetup(DMAchan, buf, bp-buf, 0); 
 
	/* 
	 *  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++] = filler; 
	if(floppycmd() < 0){ 
		DPRINT("xfer cmd failed\n"); 
		error(Eio); 
	} 
 
	/* 
	 *  give bus to DMA, floppyintr() will read result 
	 */ 
	floppywait(); 
	dmaend(DMAchan); 
 
	/* 
	 *  check for errors 
	 */ 
	if(fl.nstat < 7){ 
		DPRINT("format result failed %lux\n", inb(Pmsr)); 
		fl.confused = 1; 
		error(Eio); 
	} 
	if((fl.stat[0]&Codemask)!=0 || fl.stat[1] || fl.stat[2]){ 
		DPRINT("format failed %lux %lux %lux\n", 
			fl.stat[0], fl.stat[1], fl.stat[2]); 
		dp->confused = 1; 
		error(Eio); 
	} 
	qunlock(&fl); 
	free(buf); 
1992/1007    
	poperror(); 
1992/1006    
	return 1; 
} 
 
1991/0925    
static void 
floppyintr(Ureg *ur) 
1991/0727    
{ 
1992/0711    
	USED(ur); 
1991/0924    
	switch(fl.cmd[0]&~Fmulti){ 
	case Fread: 
	case Fwrite: 
1992/1006    
	case Fformat: 
1991/0924    
		floppyresult(); 
		break; 
	case Fseek: 
	case Frecal: 
	default: 
		floppysense();	/* to clear interrupt */ 
		break; 
	} 
	fl.ncmd = 0; 
	wakeup(&fl.r); 
1991/0727    
} 


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