xref: /plan9/sys/src/9/pc/devfloppy.c (revision 425afbab47e573297e4abb9165d4d95c66cf3758)
17dd7cddfSDavid du Colombier #include	"u.h"
27dd7cddfSDavid du Colombier #include	"../port/lib.h"
37dd7cddfSDavid du Colombier #include	"mem.h"
47dd7cddfSDavid du Colombier #include	"dat.h"
57dd7cddfSDavid du Colombier #include	"fns.h"
67dd7cddfSDavid du Colombier #include	"io.h"
77dd7cddfSDavid du Colombier #include	"../port/error.h"
87dd7cddfSDavid du Colombier 
97dd7cddfSDavid du Colombier #include	"floppy.h"
107dd7cddfSDavid du Colombier 
117dd7cddfSDavid du Colombier /* Intel 82077A (8272A compatible) floppy controller */
127dd7cddfSDavid du Colombier 
137dd7cddfSDavid du Colombier /* This module expects the following functions to be defined
147dd7cddfSDavid du Colombier  * elsewhere:
157dd7cddfSDavid du Colombier  *
167dd7cddfSDavid du Colombier  * inb()
177dd7cddfSDavid du Colombier  * outb()
187dd7cddfSDavid du Colombier  * floppyexec()
197dd7cddfSDavid du Colombier  * floppyeject()
207dd7cddfSDavid du Colombier  * floppysetup0()
217dd7cddfSDavid du Colombier  * floppysetup1()
227dd7cddfSDavid du Colombier  * dmainit()
237dd7cddfSDavid du Colombier  * dmasetup()
247dd7cddfSDavid du Colombier  * dmaend()
257dd7cddfSDavid du Colombier  *
267dd7cddfSDavid du Colombier  * On DMA systems, floppyexec() should be an empty function;
277dd7cddfSDavid du Colombier  * on non-DMA systems, dmaend() should be an empty function;
287dd7cddfSDavid du Colombier  * dmasetup() may enforce maximum transfer sizes.
297dd7cddfSDavid du Colombier  */
307dd7cddfSDavid du Colombier 
317dd7cddfSDavid du Colombier enum {
327dd7cddfSDavid du Colombier 	/* file types */
337dd7cddfSDavid du Colombier 	Qdir=		0,
347dd7cddfSDavid du Colombier 	Qdata=		(1<<2),
357dd7cddfSDavid du Colombier 	Qctl=		(2<<2),
367dd7cddfSDavid du Colombier 	Qmask=		(3<<2),
377dd7cddfSDavid du Colombier 
387dd7cddfSDavid du Colombier 	DMAchan=	2,	/* floppy dma channel */
397dd7cddfSDavid du Colombier };
407dd7cddfSDavid du Colombier 
417dd7cddfSDavid du Colombier #define DPRINT if(floppydebug)print
427dd7cddfSDavid du Colombier int floppydebug = 0;
437dd7cddfSDavid du Colombier 
447dd7cddfSDavid du Colombier /*
457dd7cddfSDavid du Colombier  *  types of drive (from PC equipment byte)
467dd7cddfSDavid du Colombier  */
477dd7cddfSDavid du Colombier enum
487dd7cddfSDavid du Colombier {
497dd7cddfSDavid du Colombier 	Tnone=		0,
507dd7cddfSDavid du Colombier 	T360kb=		1,
517dd7cddfSDavid du Colombier 	T1200kb=	2,
527dd7cddfSDavid du Colombier 	T720kb=		3,
537dd7cddfSDavid du Colombier 	T1440kb=	4,
547dd7cddfSDavid du Colombier };
557dd7cddfSDavid du Colombier 
567dd7cddfSDavid du Colombier FType floppytype[] =
577dd7cddfSDavid du Colombier {
587dd7cddfSDavid du Colombier  { "3½HD",	T1440kb, 512, 18, 2, 1, 80, 0x1B, 0x54,	0, },
597dd7cddfSDavid du Colombier  { "3½DD",	T1440kb, 512,  9, 2, 1, 80, 0x1B, 0x54, 2, },
607dd7cddfSDavid du Colombier  { "3½DD",	T720kb,  512,  9, 2, 1, 80, 0x1B, 0x54, 2, },
617dd7cddfSDavid du Colombier  { "5¼HD",	T1200kb, 512, 15, 2, 1, 80, 0x2A, 0x50, 0, },
627dd7cddfSDavid du Colombier  { "5¼DD",	T1200kb, 512,  9, 2, 2, 40, 0x2A, 0x50, 1, },
637dd7cddfSDavid du Colombier  { "ATT3B1",	T1200kb, 512,  8, 2, 2, 48, 0x2A, 0x50, 1, },
647dd7cddfSDavid du Colombier  { "5¼DD",	T360kb,  512,  9, 2, 1, 40, 0x2A, 0x50, 2, },
657dd7cddfSDavid du Colombier };
667dd7cddfSDavid du Colombier 
677dd7cddfSDavid du Colombier /*
687dd7cddfSDavid du Colombier  *  bytes per sector encoding for the controller.
697dd7cddfSDavid du Colombier  *  - index for b2c is is (bytes per sector/128).
707dd7cddfSDavid du Colombier  *  - index for c2b is code from b2c
717dd7cddfSDavid du Colombier  */
727dd7cddfSDavid du Colombier static int b2c[] =
737dd7cddfSDavid du Colombier {
747dd7cddfSDavid du Colombier [1]	0,
757dd7cddfSDavid du Colombier [2]	1,
767dd7cddfSDavid du Colombier [4]	2,
777dd7cddfSDavid du Colombier [8]	3,
787dd7cddfSDavid du Colombier };
797dd7cddfSDavid du Colombier static int c2b[] =
807dd7cddfSDavid du Colombier {
817dd7cddfSDavid du Colombier 	128,
827dd7cddfSDavid du Colombier 	256,
837dd7cddfSDavid du Colombier 	512,
847dd7cddfSDavid du Colombier 	1024,
857dd7cddfSDavid du Colombier };
867dd7cddfSDavid du Colombier 
877dd7cddfSDavid du Colombier FController	fl;
887dd7cddfSDavid du Colombier 
897dd7cddfSDavid du Colombier #define MOTORBIT(i)	(1<<((i)+4))
907dd7cddfSDavid du Colombier 
917dd7cddfSDavid du Colombier /*
927dd7cddfSDavid du Colombier  *  predeclared
937dd7cddfSDavid du Colombier  */
947dd7cddfSDavid du Colombier static int	cmddone(void*);
959a747e4fSDavid du Colombier static void	floppyformat(FDrive*, Cmdbuf*);
967dd7cddfSDavid du Colombier static void	floppykproc(void*);
977dd7cddfSDavid du Colombier static void	floppypos(FDrive*,long);
987dd7cddfSDavid du Colombier static int	floppyrecal(FDrive*);
997dd7cddfSDavid du Colombier static int	floppyresult(void);
1007dd7cddfSDavid du Colombier static void	floppyrevive(void);
1017dd7cddfSDavid du Colombier static long	floppyseek(FDrive*, long);
1027dd7cddfSDavid du Colombier static int	floppysense(void);
10359cc4ca5SDavid du Colombier static void	floppywait(int);
1047dd7cddfSDavid du Colombier static long	floppyxfer(FDrive*, int, void*, long, long);
1057dd7cddfSDavid du Colombier 
1067dd7cddfSDavid du Colombier Dirtab floppydir[]={
1079a747e4fSDavid du Colombier 	".",		{Qdir, 0, QTDIR},	0,	0550,
1087dd7cddfSDavid du Colombier 	"fd0disk",		{Qdata + 0},	0,	0660,
1097dd7cddfSDavid du Colombier 	"fd0ctl",		{Qctl + 0},	0,	0660,
1107dd7cddfSDavid du Colombier 	"fd1disk",		{Qdata + 1},	0,	0660,
1117dd7cddfSDavid du Colombier 	"fd1ctl",		{Qctl + 1},	0,	0660,
1127dd7cddfSDavid du Colombier 	"fd2disk",		{Qdata + 2},	0,	0660,
1137dd7cddfSDavid du Colombier 	"fd2ctl",		{Qctl + 2},	0,	0660,
1147dd7cddfSDavid du Colombier 	"fd3disk",		{Qdata + 3},	0,	0660,
1157dd7cddfSDavid du Colombier 	"fd3ctl",		{Qctl + 3},	0,	0660,
1167dd7cddfSDavid du Colombier };
1177dd7cddfSDavid du Colombier #define NFDIR	2	/* directory entries/drive */
1187dd7cddfSDavid du Colombier 
1199a747e4fSDavid du Colombier enum
1209a747e4fSDavid du Colombier {
1219a747e4fSDavid du Colombier 	CMdebug,
122fb7f0c93SDavid du Colombier 	CMnodebug,
1239a747e4fSDavid du Colombier 	CMeject,
1249a747e4fSDavid du Colombier 	CMformat,
1259a747e4fSDavid du Colombier 	CMreset,
1269a747e4fSDavid du Colombier };
1279a747e4fSDavid du Colombier 
1289a747e4fSDavid du Colombier static Cmdtab floppyctlmsg[] =
1299a747e4fSDavid du Colombier {
1309a747e4fSDavid du Colombier 	CMdebug,	"debug",	1,
131fb7f0c93SDavid du Colombier 	CMnodebug,	"nodebug", 1,
1329a747e4fSDavid du Colombier 	CMeject,	"eject",	1,
1339a747e4fSDavid du Colombier 	CMformat,	"format",	0,
1349a747e4fSDavid du Colombier 	CMreset,	"reset",	1,
1359a747e4fSDavid du Colombier };
1369a747e4fSDavid du Colombier 
1377dd7cddfSDavid du Colombier static void
fldump(void)1387dd7cddfSDavid du Colombier fldump(void)
1397dd7cddfSDavid du Colombier {
1407dd7cddfSDavid du Colombier 	DPRINT("sra %ux srb %ux dor %ux msr %ux dir %ux\n", inb(Psra), inb(Psrb),
1417dd7cddfSDavid du Colombier 		inb(Pdor), inb(Pmsr), inb(Pdir));
1427dd7cddfSDavid du Colombier }
1437dd7cddfSDavid du Colombier 
1447dd7cddfSDavid du Colombier /*
1457dd7cddfSDavid du Colombier  *  set floppy drive to its default type
1467dd7cddfSDavid du Colombier  */
1477dd7cddfSDavid du Colombier static void
floppysetdef(FDrive * dp)1487dd7cddfSDavid du Colombier floppysetdef(FDrive *dp)
1497dd7cddfSDavid du Colombier {
1507dd7cddfSDavid du Colombier 	FType *t;
1517dd7cddfSDavid du Colombier 
1527dd7cddfSDavid du Colombier 	for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++)
1537dd7cddfSDavid du Colombier 		if(dp->dt == t->dt){
1547dd7cddfSDavid du Colombier 			dp->t = t;
1559a747e4fSDavid du Colombier 			floppydir[1+NFDIR*dp->dev].length = dp->t->cap;
1567dd7cddfSDavid du Colombier 			break;
1577dd7cddfSDavid du Colombier 		}
1587dd7cddfSDavid du Colombier }
1597dd7cddfSDavid du Colombier 
1607dd7cddfSDavid du Colombier static void
floppyreset(void)1617dd7cddfSDavid du Colombier floppyreset(void)
1627dd7cddfSDavid du Colombier {
1637dd7cddfSDavid du Colombier 	FDrive *dp;
1647dd7cddfSDavid du Colombier 	FType *t;
1657dd7cddfSDavid du Colombier 	ulong maxtsize;
1667dd7cddfSDavid du Colombier 
1677dd7cddfSDavid du Colombier 	floppysetup0(&fl);
1687dd7cddfSDavid du Colombier 	if(fl.ndrive == 0)
1697dd7cddfSDavid du Colombier 		return;
1707dd7cddfSDavid du Colombier 
1717dd7cddfSDavid du Colombier 	/*
1727dd7cddfSDavid du Colombier 	 *  init dependent parameters
1737dd7cddfSDavid du Colombier 	 */
1747dd7cddfSDavid du Colombier 	maxtsize = 0;
1757dd7cddfSDavid du Colombier 	for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){
1767dd7cddfSDavid du Colombier 		t->cap = t->bytes * t->heads * t->sectors * t->tracks;
1777dd7cddfSDavid du Colombier 		t->bcode = b2c[t->bytes/128];
1787dd7cddfSDavid du Colombier 		t->tsize = t->bytes * t->sectors;
1797dd7cddfSDavid du Colombier 		if(maxtsize < t->tsize)
1807dd7cddfSDavid du Colombier 			maxtsize = t->tsize;
1817dd7cddfSDavid du Colombier 	}
1827dd7cddfSDavid du Colombier 
183*425afbabSDavid du Colombier 	/*
184*425afbabSDavid du Colombier 	 * Should check if this fails. Can do so
185*425afbabSDavid du Colombier 	 * if there is no space <= 16MB for the DMA
186*425afbabSDavid du Colombier 	 * bounce buffer.
187*425afbabSDavid du Colombier 	 */
1887dd7cddfSDavid du Colombier 	dmainit(DMAchan, maxtsize);
1897dd7cddfSDavid du Colombier 
1907dd7cddfSDavid du Colombier 	/*
1917dd7cddfSDavid du Colombier 	 *  allocate the drive storage
1927dd7cddfSDavid du Colombier 	 */
1937dd7cddfSDavid du Colombier 	fl.d = xalloc(fl.ndrive*sizeof(FDrive));
1947dd7cddfSDavid du Colombier 	fl.selected = fl.d;
1957dd7cddfSDavid du Colombier 
1967dd7cddfSDavid du Colombier 	/*
1977dd7cddfSDavid du Colombier 	 *  stop the motors
1987dd7cddfSDavid du Colombier 	 */
1997dd7cddfSDavid du Colombier 	fl.motor = 0;
2007dd7cddfSDavid du Colombier 	delay(10);
2017dd7cddfSDavid du Colombier 	outb(Pdor, fl.motor | Fintena | Fena);
2027dd7cddfSDavid du Colombier 	delay(10);
2037dd7cddfSDavid du Colombier 
2047dd7cddfSDavid du Colombier 	/*
2057dd7cddfSDavid du Colombier 	 *  init drives
2067dd7cddfSDavid du Colombier 	 */
2077dd7cddfSDavid du Colombier 	for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){
2087dd7cddfSDavid du Colombier 		dp->dev = dp - fl.d;
2097dd7cddfSDavid du Colombier 		dp->dt = T1440kb;
2107dd7cddfSDavid du Colombier 		floppysetdef(dp);
2117dd7cddfSDavid du Colombier 		dp->cyl = -1;			/* because we don't know */
2127dd7cddfSDavid du Colombier 		dp->cache = (uchar*)xspanalloc(maxtsize, BY2PG, 64*1024);
2137dd7cddfSDavid du Colombier 		dp->ccyl = -1;
2147dd7cddfSDavid du Colombier 		dp->vers = 0;
2157dd7cddfSDavid du Colombier 	}
2167dd7cddfSDavid du Colombier 
2177dd7cddfSDavid du Colombier 	/*
2187dd7cddfSDavid du Colombier 	 *  first operation will recalibrate
2197dd7cddfSDavid du Colombier 	 */
2207dd7cddfSDavid du Colombier 	fl.confused = 1;
2217dd7cddfSDavid du Colombier 
2227dd7cddfSDavid du Colombier 	floppysetup1(&fl);
2237dd7cddfSDavid du Colombier }
2247dd7cddfSDavid du Colombier 
2257dd7cddfSDavid du Colombier static Chan*
floppyattach(char * spec)2267dd7cddfSDavid du Colombier floppyattach(char *spec)
2277dd7cddfSDavid du Colombier {
2287dd7cddfSDavid du Colombier 	static int kstarted;
2297dd7cddfSDavid du Colombier 
2307dd7cddfSDavid du Colombier 	if(fl.ndrive == 0)
2317dd7cddfSDavid du Colombier 		error(Enodev);
2327dd7cddfSDavid du Colombier 
2337dd7cddfSDavid du Colombier 	if(kstarted == 0){
2347dd7cddfSDavid du Colombier 		/*
2357dd7cddfSDavid du Colombier 		 *  watchdog to turn off the motors
2367dd7cddfSDavid du Colombier 		 */
2377dd7cddfSDavid du Colombier 		kstarted = 1;
2387dd7cddfSDavid du Colombier 		kproc("floppy", floppykproc, 0);
2397dd7cddfSDavid du Colombier 	}
2407dd7cddfSDavid du Colombier 	return devattach('f', spec);
2417dd7cddfSDavid du Colombier }
2427dd7cddfSDavid du Colombier 
2439a747e4fSDavid du Colombier static Walkqid*
floppywalk(Chan * c,Chan * nc,char ** name,int nname)2449a747e4fSDavid du Colombier floppywalk(Chan *c, Chan *nc, char **name, int nname)
2457dd7cddfSDavid du Colombier {
2469a747e4fSDavid du Colombier 	return devwalk(c, nc, name, nname, floppydir, 1+fl.ndrive*NFDIR, devgen);
2477dd7cddfSDavid du Colombier }
2487dd7cddfSDavid du Colombier 
2499a747e4fSDavid du Colombier static int
floppystat(Chan * c,uchar * dp,int n)2509a747e4fSDavid du Colombier floppystat(Chan *c, uchar *dp, int n)
2517dd7cddfSDavid du Colombier {
2529a747e4fSDavid du Colombier 	return devstat(c, dp, n, floppydir, 1+fl.ndrive*NFDIR, devgen);
2537dd7cddfSDavid du Colombier }
2547dd7cddfSDavid du Colombier 
2557dd7cddfSDavid du Colombier static Chan*
floppyopen(Chan * c,int omode)2567dd7cddfSDavid du Colombier floppyopen(Chan *c, int omode)
2577dd7cddfSDavid du Colombier {
2589a747e4fSDavid du Colombier 	return devopen(c, omode, floppydir, 1+fl.ndrive*NFDIR, devgen);
2597dd7cddfSDavid du Colombier }
2607dd7cddfSDavid du Colombier 
2617dd7cddfSDavid du Colombier static void
floppyclose(Chan *)2627dd7cddfSDavid du Colombier floppyclose(Chan *)
2637dd7cddfSDavid du Colombier {
2647dd7cddfSDavid du Colombier }
2657dd7cddfSDavid du Colombier 
2667dd7cddfSDavid du Colombier static void
islegal(ulong offset,long n,FDrive * dp)2677dd7cddfSDavid du Colombier islegal(ulong offset, long n, FDrive *dp)
2687dd7cddfSDavid du Colombier {
2697dd7cddfSDavid du Colombier 	if(offset % dp->t->bytes)
2707dd7cddfSDavid du Colombier 		error(Ebadarg);
2717dd7cddfSDavid du Colombier 	if(n % dp->t->bytes)
2727dd7cddfSDavid du Colombier 		error(Ebadarg);
2737dd7cddfSDavid du Colombier }
2747dd7cddfSDavid du Colombier 
2757dd7cddfSDavid du Colombier /*
2767dd7cddfSDavid du Colombier  *  check if the floppy has been replaced under foot.  cause
2777dd7cddfSDavid du Colombier  *  an error if it has.
2787dd7cddfSDavid du Colombier  *
2797dd7cddfSDavid du Colombier  *  a seek and a read clears the condition.  this was determined
2807dd7cddfSDavid du Colombier  *  experimentally, there has to be a better way.
2817dd7cddfSDavid du Colombier  *
2827dd7cddfSDavid du Colombier  *  if the read fails, cycle through the possible floppy
2837dd7cddfSDavid du Colombier  *  density till one works or we've cycled through all
2847dd7cddfSDavid du Colombier  *  possibilities for this drive.
2857dd7cddfSDavid du Colombier  */
2867dd7cddfSDavid du Colombier static void
changed(Chan * c,FDrive * dp)2877dd7cddfSDavid du Colombier changed(Chan *c, FDrive *dp)
2887dd7cddfSDavid du Colombier {
2897dd7cddfSDavid du Colombier 	ulong old;
2907dd7cddfSDavid du Colombier 	FType *start;
2917dd7cddfSDavid du Colombier 
2927dd7cddfSDavid du Colombier 	/*
2937dd7cddfSDavid du Colombier 	 *  if floppy has changed or first time through
2947dd7cddfSDavid du Colombier 	 */
2957dd7cddfSDavid du Colombier 	if((inb(Pdir)&Fchange) || dp->vers == 0){
2967dd7cddfSDavid du Colombier 		DPRINT("changed\n");
2977dd7cddfSDavid du Colombier 		fldump();
2987dd7cddfSDavid du Colombier 		dp->vers++;
2997dd7cddfSDavid du Colombier 		start = dp->t;
30059cc4ca5SDavid du Colombier 		dp->maxtries = 3;	/* limit it when we're probing */
30159cc4ca5SDavid du Colombier 
30259cc4ca5SDavid du Colombier 		/* floppyon will fail if there's a controller but no drive */
3037dd7cddfSDavid du Colombier 		dp->confused = 1;	/* make floppyon recal */
30459cc4ca5SDavid du Colombier 		if(floppyon(dp) < 0)
30559cc4ca5SDavid du Colombier 			error(Eio);
30659cc4ca5SDavid du Colombier 
30759cc4ca5SDavid du Colombier 		/* seek to the first track */
3087dd7cddfSDavid du Colombier 		floppyseek(dp, dp->t->heads*dp->t->tsize);
3097dd7cddfSDavid du Colombier 		while(waserror()){
31059cc4ca5SDavid du Colombier 			/*
31159cc4ca5SDavid du Colombier 			 *  if first attempt doesn't reset changed bit, there's
31259cc4ca5SDavid du Colombier 			 *  no floppy there
31359cc4ca5SDavid du Colombier 			 */
31459cc4ca5SDavid du Colombier 			if(inb(Pdir)&Fchange)
31559cc4ca5SDavid du Colombier 				nexterror();
31659cc4ca5SDavid du Colombier 
3177dd7cddfSDavid du Colombier 			while(++dp->t){
3187dd7cddfSDavid du Colombier 				if(dp->t == &floppytype[nelem(floppytype)])
3197dd7cddfSDavid du Colombier 					dp->t = floppytype;
3207dd7cddfSDavid du Colombier 				if(dp->dt == dp->t->dt)
3217dd7cddfSDavid du Colombier 					break;
3227dd7cddfSDavid du Colombier 			}
3239a747e4fSDavid du Colombier 			floppydir[1+NFDIR*dp->dev].length = dp->t->cap;
32459cc4ca5SDavid du Colombier 
32559cc4ca5SDavid du Colombier 			/* floppyon will fail if there's a controller but no drive */
32659cc4ca5SDavid du Colombier 			if(floppyon(dp) < 0)
32759cc4ca5SDavid du Colombier 				error(Eio);
32859cc4ca5SDavid du Colombier 
3297dd7cddfSDavid du Colombier 			DPRINT("changed: trying %s\n", dp->t->name);
3307dd7cddfSDavid du Colombier 			fldump();
3317dd7cddfSDavid du Colombier 			if(dp->t == start)
3327dd7cddfSDavid du Colombier 				nexterror();
3337dd7cddfSDavid du Colombier 		}
33459cc4ca5SDavid du Colombier 
33559cc4ca5SDavid du Colombier 		/* if the read succeeds, we've got the density right */
3367dd7cddfSDavid du Colombier 		floppyxfer(dp, Fread, dp->cache, 0, dp->t->tsize);
3377dd7cddfSDavid du Colombier 		poperror();
33859cc4ca5SDavid du Colombier 		dp->maxtries = 20;
3397dd7cddfSDavid du Colombier 	}
3407dd7cddfSDavid du Colombier 
3417dd7cddfSDavid du Colombier 	old = c->qid.vers;
3427dd7cddfSDavid du Colombier 	c->qid.vers = dp->vers;
3437dd7cddfSDavid du Colombier 	if(old && old != dp->vers)
3447dd7cddfSDavid du Colombier 		error(Eio);
3457dd7cddfSDavid du Colombier }
3467dd7cddfSDavid du Colombier 
3477dd7cddfSDavid du Colombier static int
readtrack(FDrive * dp,int cyl,int head)3487dd7cddfSDavid du Colombier readtrack(FDrive *dp, int cyl, int head)
3497dd7cddfSDavid du Colombier {
3507dd7cddfSDavid du Colombier 	int i, nn, sofar;
3517dd7cddfSDavid du Colombier 	ulong pos;
3527dd7cddfSDavid du Colombier 
3537dd7cddfSDavid du Colombier 	nn = dp->t->tsize;
3547dd7cddfSDavid du Colombier 	if(dp->ccyl==cyl && dp->chead==head)
3557dd7cddfSDavid du Colombier 		return nn;
3567dd7cddfSDavid du Colombier 	pos = (cyl*dp->t->heads+head) * nn;
3577dd7cddfSDavid du Colombier 	for(sofar = 0; sofar < nn; sofar += i){
3587dd7cddfSDavid du Colombier 		dp->ccyl = -1;
3597dd7cddfSDavid du Colombier 		i = floppyxfer(dp, Fread, dp->cache + sofar, pos + sofar, nn - sofar);
3607dd7cddfSDavid du Colombier 		if(i <= 0)
3617dd7cddfSDavid du Colombier 			return -1;
3627dd7cddfSDavid du Colombier 	}
3637dd7cddfSDavid du Colombier 	dp->ccyl = cyl;
3647dd7cddfSDavid du Colombier 	dp->chead = head;
3657dd7cddfSDavid du Colombier 	return nn;
3667dd7cddfSDavid du Colombier }
3677dd7cddfSDavid du Colombier 
3687dd7cddfSDavid du Colombier static long
floppyread(Chan * c,void * a,long n,vlong off)3697dd7cddfSDavid du Colombier floppyread(Chan *c, void *a, long n, vlong off)
3707dd7cddfSDavid du Colombier {
3717dd7cddfSDavid du Colombier 	FDrive *dp;
3727dd7cddfSDavid du Colombier 	long rv;
3737dd7cddfSDavid du Colombier 	int sec, head, cyl;
3747dd7cddfSDavid du Colombier 	long len;
3757dd7cddfSDavid du Colombier 	uchar *aa;
3767dd7cddfSDavid du Colombier 	ulong offset = off;
3777dd7cddfSDavid du Colombier 
3789a747e4fSDavid du Colombier 	if(c->qid.type & QTDIR)
3799a747e4fSDavid du Colombier 		return devdirread(c, a, n, floppydir, 1+fl.ndrive*NFDIR, devgen);
3807dd7cddfSDavid du Colombier 
3817dd7cddfSDavid du Colombier 	rv = 0;
3827dd7cddfSDavid du Colombier 	dp = &fl.d[c->qid.path & ~Qmask];
3837dd7cddfSDavid du Colombier 	switch ((int)(c->qid.path & Qmask)) {
3847dd7cddfSDavid du Colombier 	case Qdata:
3857dd7cddfSDavid du Colombier 		islegal(offset, n, dp);
3867dd7cddfSDavid du Colombier 		aa = a;
3877dd7cddfSDavid du Colombier 
3887dd7cddfSDavid du Colombier 		qlock(&fl);
3897dd7cddfSDavid du Colombier 		if(waserror()){
3907dd7cddfSDavid du Colombier 			qunlock(&fl);
3917dd7cddfSDavid du Colombier 			nexterror();
3927dd7cddfSDavid du Colombier 		}
3937dd7cddfSDavid du Colombier 		floppyon(dp);
3947dd7cddfSDavid du Colombier 		changed(c, dp);
3957dd7cddfSDavid du Colombier 		for(rv = 0; rv < n; rv += len){
3967dd7cddfSDavid du Colombier 			/*
3977dd7cddfSDavid du Colombier 			 *  all xfers come out of the track cache
3987dd7cddfSDavid du Colombier 			 */
3997dd7cddfSDavid du Colombier 			dp->len = n - rv;
4007dd7cddfSDavid du Colombier 			floppypos(dp, offset+rv);
4017dd7cddfSDavid du Colombier 			cyl = dp->tcyl;
4027dd7cddfSDavid du Colombier 			head = dp->thead;
4037dd7cddfSDavid du Colombier 			len = dp->len;
4047dd7cddfSDavid du Colombier 			sec = dp->tsec;
4057dd7cddfSDavid du Colombier 			if(readtrack(dp, cyl, head) < 0)
4067dd7cddfSDavid du Colombier 				break;
4077dd7cddfSDavid du Colombier 			memmove(aa+rv, dp->cache + (sec-1)*dp->t->bytes, len);
4087dd7cddfSDavid du Colombier 		}
4097dd7cddfSDavid du Colombier 		qunlock(&fl);
4107dd7cddfSDavid du Colombier 		poperror();
4117dd7cddfSDavid du Colombier 
4127dd7cddfSDavid du Colombier 		break;
4137dd7cddfSDavid du Colombier 	case Qctl:
4147dd7cddfSDavid du Colombier 		return readstr(offset, a, n, dp->t->name);
4157dd7cddfSDavid du Colombier 	default:
4167dd7cddfSDavid du Colombier 		panic("floppyread: bad qid");
4177dd7cddfSDavid du Colombier 	}
4187dd7cddfSDavid du Colombier 
4197dd7cddfSDavid du Colombier 	return rv;
4207dd7cddfSDavid du Colombier }
4217dd7cddfSDavid du Colombier 
4227dd7cddfSDavid du Colombier static long
floppywrite(Chan * c,void * a,long n,vlong off)4237dd7cddfSDavid du Colombier floppywrite(Chan *c, void *a, long n, vlong off)
4247dd7cddfSDavid du Colombier {
4257dd7cddfSDavid du Colombier 	FDrive *dp;
4267dd7cddfSDavid du Colombier 	long rv, i;
4277dd7cddfSDavid du Colombier 	char *aa = a;
4289a747e4fSDavid du Colombier 	Cmdbuf *cb;
4299a747e4fSDavid du Colombier 	Cmdtab *ct;
4307dd7cddfSDavid du Colombier 	ulong offset = off;
4317dd7cddfSDavid du Colombier 
4327dd7cddfSDavid du Colombier 	rv = 0;
4337dd7cddfSDavid du Colombier 	dp = &fl.d[c->qid.path & ~Qmask];
4347dd7cddfSDavid du Colombier 	switch ((int)(c->qid.path & Qmask)) {
4357dd7cddfSDavid du Colombier 	case Qdata:
4367dd7cddfSDavid du Colombier 		islegal(offset, n, dp);
4377dd7cddfSDavid du Colombier 		qlock(&fl);
4387dd7cddfSDavid du Colombier 		if(waserror()){
4397dd7cddfSDavid du Colombier 			qunlock(&fl);
4407dd7cddfSDavid du Colombier 			nexterror();
4417dd7cddfSDavid du Colombier 		}
4427dd7cddfSDavid du Colombier 		floppyon(dp);
4437dd7cddfSDavid du Colombier 		changed(c, dp);
4447dd7cddfSDavid du Colombier 		for(rv = 0; rv < n; rv += i){
4457dd7cddfSDavid du Colombier 			floppypos(dp, offset+rv);
4467dd7cddfSDavid du Colombier 			if(dp->tcyl == dp->ccyl)
4477dd7cddfSDavid du Colombier 				dp->ccyl = -1;
4487dd7cddfSDavid du Colombier 			i = floppyxfer(dp, Fwrite, aa+rv, offset+rv, n-rv);
4497dd7cddfSDavid du Colombier 			if(i < 0)
4507dd7cddfSDavid du Colombier 				break;
4517dd7cddfSDavid du Colombier 			if(i == 0)
4527dd7cddfSDavid du Colombier 				error(Eio);
4537dd7cddfSDavid du Colombier 		}
4547dd7cddfSDavid du Colombier 		qunlock(&fl);
4557dd7cddfSDavid du Colombier 		poperror();
4567dd7cddfSDavid du Colombier 		break;
4577dd7cddfSDavid du Colombier 	case Qctl:
4587dd7cddfSDavid du Colombier 		rv = n;
4599a747e4fSDavid du Colombier 		cb = parsecmd(a, n);
4609a747e4fSDavid du Colombier 		if(waserror()){
4619a747e4fSDavid du Colombier 			free(cb);
4629a747e4fSDavid du Colombier 			nexterror();
4639a747e4fSDavid du Colombier 		}
4647dd7cddfSDavid du Colombier 		qlock(&fl);
4657dd7cddfSDavid du Colombier 		if(waserror()){
4667dd7cddfSDavid du Colombier 			qunlock(&fl);
4677dd7cddfSDavid du Colombier 			nexterror();
4687dd7cddfSDavid du Colombier 		}
4699a747e4fSDavid du Colombier 		ct = lookupcmd(cb, floppyctlmsg, nelem(floppyctlmsg));
4709a747e4fSDavid du Colombier 		switch(ct->index){
4719a747e4fSDavid du Colombier 		case CMeject:
4727dd7cddfSDavid du Colombier 			floppyeject(dp);
4739a747e4fSDavid du Colombier 			break;
4749a747e4fSDavid du Colombier 		case CMformat:
4759a747e4fSDavid du Colombier 			floppyformat(dp, cb);
4769a747e4fSDavid du Colombier 			break;
4779a747e4fSDavid du Colombier 		case CMreset:
4787dd7cddfSDavid du Colombier 			fl.confused = 1;
4797dd7cddfSDavid du Colombier 			floppyon(dp);
4809a747e4fSDavid du Colombier 			break;
4819a747e4fSDavid du Colombier 		case CMdebug:
4827dd7cddfSDavid du Colombier 			floppydebug = 1;
4839a747e4fSDavid du Colombier 			break;
484fb7f0c93SDavid du Colombier 		case CMnodebug:
485fb7f0c93SDavid du Colombier 			floppydebug = 0;
486fb7f0c93SDavid du Colombier 			break;
4879a747e4fSDavid du Colombier 		}
4887dd7cddfSDavid du Colombier 		poperror();
4897dd7cddfSDavid du Colombier 		qunlock(&fl);
4909a747e4fSDavid du Colombier 		poperror();
4919a747e4fSDavid du Colombier 		free(cb);
4927dd7cddfSDavid du Colombier 		break;
4937dd7cddfSDavid du Colombier 	default:
4947dd7cddfSDavid du Colombier 		panic("floppywrite: bad qid");
4957dd7cddfSDavid du Colombier 	}
4967dd7cddfSDavid du Colombier 
4977dd7cddfSDavid du Colombier 	return rv;
4987dd7cddfSDavid du Colombier }
4997dd7cddfSDavid du Colombier 
5007dd7cddfSDavid du Colombier static void
floppykproc(void *)5017dd7cddfSDavid du Colombier floppykproc(void *)
5027dd7cddfSDavid du Colombier {
5037dd7cddfSDavid du Colombier 	FDrive *dp;
5047dd7cddfSDavid du Colombier 
5057dd7cddfSDavid du Colombier 	while(waserror())
5067dd7cddfSDavid du Colombier 		;
5077dd7cddfSDavid du Colombier 	for(;;){
5087dd7cddfSDavid du Colombier 		for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){
5097dd7cddfSDavid du Colombier 			if((fl.motor&MOTORBIT(dp->dev))
5107dd7cddfSDavid du Colombier 			&& TK2SEC(m->ticks - dp->lasttouched) > 5
5117dd7cddfSDavid du Colombier 			&& canqlock(&fl)){
5127dd7cddfSDavid du Colombier 				if(TK2SEC(m->ticks - dp->lasttouched) > 5)
5137dd7cddfSDavid du Colombier 					floppyoff(dp);
5147dd7cddfSDavid du Colombier 				qunlock(&fl);
5157dd7cddfSDavid du Colombier 			}
5167dd7cddfSDavid du Colombier 		}
517dc5a79c1SDavid du Colombier 		tsleep(&up->sleep, return0, 0, 1000);
5187dd7cddfSDavid du Colombier 	}
5197dd7cddfSDavid du Colombier }
5207dd7cddfSDavid du Colombier 
5217dd7cddfSDavid du Colombier /*
5227dd7cddfSDavid du Colombier  *  start a floppy drive's motor.
5237dd7cddfSDavid du Colombier  */
52459cc4ca5SDavid du Colombier static int
floppyon(FDrive * dp)5257dd7cddfSDavid du Colombier floppyon(FDrive *dp)
5267dd7cddfSDavid du Colombier {
5277dd7cddfSDavid du Colombier 	int alreadyon;
5287dd7cddfSDavid du Colombier 	int tries;
5297dd7cddfSDavid du Colombier 
5307dd7cddfSDavid du Colombier 	if(fl.confused)
5317dd7cddfSDavid du Colombier 		floppyrevive();
5327dd7cddfSDavid du Colombier 
5337dd7cddfSDavid du Colombier 	/* start motor and select drive */
5347dd7cddfSDavid du Colombier 	alreadyon = fl.motor & MOTORBIT(dp->dev);
5357dd7cddfSDavid du Colombier 	fl.motor |= MOTORBIT(dp->dev);
5367dd7cddfSDavid du Colombier 	outb(Pdor, fl.motor | Fintena | Fena | dp->dev);
5377dd7cddfSDavid du Colombier 	if(!alreadyon){
5387dd7cddfSDavid du Colombier 		/* wait for drive to spin up */
539dc5a79c1SDavid du Colombier 		tsleep(&up->sleep, return0, 0, 750);
5407dd7cddfSDavid du Colombier 
5417dd7cddfSDavid du Colombier 		/* clear any pending interrupts */
5427dd7cddfSDavid du Colombier 		floppysense();
5437dd7cddfSDavid du Colombier 	}
5447dd7cddfSDavid du Colombier 
5457dd7cddfSDavid du Colombier 	/* set transfer rate */
5467dd7cddfSDavid du Colombier 	if(fl.rate != dp->t->rate){
5477dd7cddfSDavid du Colombier 		fl.rate = dp->t->rate;
5487dd7cddfSDavid du Colombier 		outb(Pdsr, fl.rate);
5497dd7cddfSDavid du Colombier 	}
5507dd7cddfSDavid du Colombier 
5517dd7cddfSDavid du Colombier 	/* get drive to a known cylinder */
5527dd7cddfSDavid du Colombier 	if(dp->confused)
5537dd7cddfSDavid du Colombier 		for(tries = 0; tries < 4; tries++)
5547dd7cddfSDavid du Colombier 			if(floppyrecal(dp) >= 0)
5557dd7cddfSDavid du Colombier 				break;
5567dd7cddfSDavid du Colombier 	dp->lasttouched = m->ticks;
5577dd7cddfSDavid du Colombier 	fl.selected = dp;
55859cc4ca5SDavid du Colombier 
55959cc4ca5SDavid du Colombier 	/* return -1 if this didn't work */
56059cc4ca5SDavid du Colombier 	if(dp->confused)
56159cc4ca5SDavid du Colombier 		return -1;
56259cc4ca5SDavid du Colombier 	return 0;
5637dd7cddfSDavid du Colombier }
5647dd7cddfSDavid du Colombier 
5657dd7cddfSDavid du Colombier /*
5667dd7cddfSDavid du Colombier  *  stop the floppy if it hasn't been used in 5 seconds
5677dd7cddfSDavid du Colombier  */
5687dd7cddfSDavid du Colombier static void
floppyoff(FDrive * dp)5697dd7cddfSDavid du Colombier floppyoff(FDrive *dp)
5707dd7cddfSDavid du Colombier {
5717dd7cddfSDavid du Colombier 	fl.motor &= ~MOTORBIT(dp->dev);
5727dd7cddfSDavid du Colombier 	outb(Pdor, fl.motor | Fintena | Fena | dp->dev);
5737dd7cddfSDavid du Colombier }
5747dd7cddfSDavid du Colombier 
5757dd7cddfSDavid du Colombier /*
5767dd7cddfSDavid du Colombier  *  send a command to the floppy
5777dd7cddfSDavid du Colombier  */
5787dd7cddfSDavid du Colombier static int
floppycmd(void)5797dd7cddfSDavid du Colombier floppycmd(void)
5807dd7cddfSDavid du Colombier {
5817dd7cddfSDavid du Colombier 	int i;
5827dd7cddfSDavid du Colombier 	int tries;
5837dd7cddfSDavid du Colombier 
5847dd7cddfSDavid du Colombier 	fl.nstat = 0;
5857dd7cddfSDavid du Colombier 	for(i = 0; i < fl.ncmd; i++){
5867dd7cddfSDavid du Colombier 		for(tries = 0; ; tries++){
5877dd7cddfSDavid du Colombier 			if((inb(Pmsr)&(Ffrom|Fready)) == Fready)
5887dd7cddfSDavid du Colombier 				break;
5897dd7cddfSDavid du Colombier 			if(tries > 1000){
5907dd7cddfSDavid du Colombier 				DPRINT("cmd %ux can't be sent (%d)\n", fl.cmd[0], i);
5917dd7cddfSDavid du Colombier 				fldump();
5927dd7cddfSDavid du Colombier 
5937dd7cddfSDavid du Colombier 				/* empty fifo, might have been a bad command */
5947dd7cddfSDavid du Colombier 				floppyresult();
5957dd7cddfSDavid du Colombier 				return -1;
5967dd7cddfSDavid du Colombier 			}
5977dd7cddfSDavid du Colombier 			microdelay(8);	/* for machine independence */
5987dd7cddfSDavid du Colombier 		}
5997dd7cddfSDavid du Colombier 		outb(Pfdata, fl.cmd[i]);
6007dd7cddfSDavid du Colombier 	}
6017dd7cddfSDavid du Colombier 	return 0;
6027dd7cddfSDavid du Colombier }
6037dd7cddfSDavid du Colombier 
6047dd7cddfSDavid du Colombier /*
6057dd7cddfSDavid du Colombier  *  get a command result from the floppy
6067dd7cddfSDavid du Colombier  *
6077dd7cddfSDavid du Colombier  *  when the controller goes ready waiting for a command
6087dd7cddfSDavid du Colombier  *  (instead of sending results), we're done
6097dd7cddfSDavid du Colombier  *
6107dd7cddfSDavid du Colombier  */
6117dd7cddfSDavid du Colombier static int
floppyresult(void)6127dd7cddfSDavid du Colombier floppyresult(void)
6137dd7cddfSDavid du Colombier {
6147dd7cddfSDavid du Colombier 	int i, s;
6157dd7cddfSDavid du Colombier 	int tries;
6167dd7cddfSDavid du Colombier 
6177dd7cddfSDavid du Colombier 	/* get the result of the operation */
6187dd7cddfSDavid du Colombier 	for(i = 0; i < sizeof(fl.stat); i++){
6197dd7cddfSDavid du Colombier 		/* wait for status byte */
6207dd7cddfSDavid du Colombier 		for(tries = 0; ; tries++){
6217dd7cddfSDavid du Colombier 			s = inb(Pmsr)&(Ffrom|Fready);
6227dd7cddfSDavid du Colombier 			if(s == Fready){
6237dd7cddfSDavid du Colombier 				fl.nstat = i;
6247dd7cddfSDavid du Colombier 				return fl.nstat;
6257dd7cddfSDavid du Colombier 			}
6267dd7cddfSDavid du Colombier 			if(s == (Ffrom|Fready))
6277dd7cddfSDavid du Colombier 				break;
6287dd7cddfSDavid du Colombier 			if(tries > 1000){
6297dd7cddfSDavid du Colombier 				DPRINT("floppyresult: %d stats\n", i);
6307dd7cddfSDavid du Colombier 				fldump();
6317dd7cddfSDavid du Colombier 				fl.confused = 1;
6327dd7cddfSDavid du Colombier 				return -1;
6337dd7cddfSDavid du Colombier 			}
6347dd7cddfSDavid du Colombier 			microdelay(8);	/* for machine independence */
6357dd7cddfSDavid du Colombier 		}
6367dd7cddfSDavid du Colombier 		fl.stat[i] = inb(Pfdata);
6377dd7cddfSDavid du Colombier 	}
6387dd7cddfSDavid du Colombier 	fl.nstat = sizeof(fl.stat);
6397dd7cddfSDavid du Colombier 	return fl.nstat;
6407dd7cddfSDavid du Colombier }
6417dd7cddfSDavid du Colombier 
6427dd7cddfSDavid du Colombier /*
6437dd7cddfSDavid du Colombier  *  calculate physical address of a logical byte offset into the disk
6447dd7cddfSDavid du Colombier  *
6457dd7cddfSDavid du Colombier  *  truncate dp->length if it crosses a track boundary
6467dd7cddfSDavid du Colombier  */
6477dd7cddfSDavid du Colombier static void
floppypos(FDrive * dp,long off)6487dd7cddfSDavid du Colombier floppypos(FDrive *dp, long off)
6497dd7cddfSDavid du Colombier {
6507dd7cddfSDavid du Colombier 	int lsec;
6517dd7cddfSDavid du Colombier 	int ltrack;
6527dd7cddfSDavid du Colombier 	int end;
6537dd7cddfSDavid du Colombier 
6547dd7cddfSDavid du Colombier 	lsec = off/dp->t->bytes;
6557dd7cddfSDavid du Colombier 	ltrack = lsec/dp->t->sectors;
6567dd7cddfSDavid du Colombier 	dp->tcyl = ltrack/dp->t->heads;
6577dd7cddfSDavid du Colombier 	dp->tsec = (lsec % dp->t->sectors) + 1;
6587dd7cddfSDavid du Colombier 	dp->thead = (lsec/dp->t->sectors) % dp->t->heads;
6597dd7cddfSDavid du Colombier 
6607dd7cddfSDavid du Colombier 	/*
6617dd7cddfSDavid du Colombier 	 *  can't read across track boundaries.
6627dd7cddfSDavid du Colombier 	 *  if so, decrement the bytes to be read.
6637dd7cddfSDavid du Colombier 	 */
6647dd7cddfSDavid du Colombier 	end = (ltrack+1)*dp->t->sectors*dp->t->bytes;
6657dd7cddfSDavid du Colombier 	if(off+dp->len > end)
6667dd7cddfSDavid du Colombier 		dp->len = end - off;
6677dd7cddfSDavid du Colombier }
6687dd7cddfSDavid du Colombier 
6697dd7cddfSDavid du Colombier /*
6707dd7cddfSDavid du Colombier  *  get the interrupt cause from the floppy.
6717dd7cddfSDavid du Colombier  */
6727dd7cddfSDavid du Colombier static int
floppysense(void)6737dd7cddfSDavid du Colombier floppysense(void)
6747dd7cddfSDavid du Colombier {
6757dd7cddfSDavid du Colombier 	fl.ncmd = 0;
6767dd7cddfSDavid du Colombier 	fl.cmd[fl.ncmd++] = Fsense;
6777dd7cddfSDavid du Colombier 	if(floppycmd() < 0)
6787dd7cddfSDavid du Colombier 		return -1;
6797dd7cddfSDavid du Colombier 	if(floppyresult() < 2){
6807dd7cddfSDavid du Colombier 		DPRINT("can't read sense response\n");
6817dd7cddfSDavid du Colombier 		fldump();
6827dd7cddfSDavid du Colombier 		fl.confused = 1;
6837dd7cddfSDavid du Colombier 		return -1;
6847dd7cddfSDavid du Colombier 	}
6857dd7cddfSDavid du Colombier 	return 0;
6867dd7cddfSDavid du Colombier }
6877dd7cddfSDavid du Colombier 
6887dd7cddfSDavid du Colombier static int
cmddone(void *)6897dd7cddfSDavid du Colombier cmddone(void *)
6907dd7cddfSDavid du Colombier {
6917dd7cddfSDavid du Colombier 	return fl.ncmd == 0;
6927dd7cddfSDavid du Colombier }
6937dd7cddfSDavid du Colombier 
6947dd7cddfSDavid du Colombier /*
6957dd7cddfSDavid du Colombier  *  Wait for a floppy interrupt.  If none occurs in 5 seconds, we
6967dd7cddfSDavid du Colombier  *  may have missed one.  This only happens on some portables which
6977dd7cddfSDavid du Colombier  *  do power management behind our backs.  Call the interrupt
6987dd7cddfSDavid du Colombier  *  routine to try to clear any conditions.
6997dd7cddfSDavid du Colombier  */
7007dd7cddfSDavid du Colombier static void
floppywait(int slow)70159cc4ca5SDavid du Colombier floppywait(int slow)
7027dd7cddfSDavid du Colombier {
70359cc4ca5SDavid du Colombier 	tsleep(&fl.r, cmddone, 0, slow ? 5000 : 1000);
7047dd7cddfSDavid du Colombier 	if(!cmddone(0)){
7057dd7cddfSDavid du Colombier 		floppyintr(0);
7067dd7cddfSDavid du Colombier 		fl.confused = 1;
7077dd7cddfSDavid du Colombier 	}
7087dd7cddfSDavid du Colombier }
7097dd7cddfSDavid du Colombier 
7107dd7cddfSDavid du Colombier /*
7117dd7cddfSDavid du Colombier  *  we've lost the floppy position, go to cylinder 0.
7127dd7cddfSDavid du Colombier  */
7137dd7cddfSDavid du Colombier static int
floppyrecal(FDrive * dp)7147dd7cddfSDavid du Colombier floppyrecal(FDrive *dp)
7157dd7cddfSDavid du Colombier {
7167dd7cddfSDavid du Colombier 	dp->ccyl = -1;
7177dd7cddfSDavid du Colombier 	dp->cyl = -1;
7187dd7cddfSDavid du Colombier 
7197dd7cddfSDavid du Colombier 	fl.ncmd = 0;
7207dd7cddfSDavid du Colombier 	fl.cmd[fl.ncmd++] = Frecal;
7217dd7cddfSDavid du Colombier 	fl.cmd[fl.ncmd++] = dp->dev;
7227dd7cddfSDavid du Colombier 	if(floppycmd() < 0)
7237dd7cddfSDavid du Colombier 		return -1;
72459cc4ca5SDavid du Colombier 	floppywait(1);
7257dd7cddfSDavid du Colombier 	if(fl.nstat < 2){
7267dd7cddfSDavid du Colombier 		DPRINT("recalibrate: confused %ux\n", inb(Pmsr));
7277dd7cddfSDavid du Colombier 		fl.confused = 1;
7287dd7cddfSDavid du Colombier 		return -1;
7297dd7cddfSDavid du Colombier 	}
7307dd7cddfSDavid du Colombier 	if((fl.stat[0] & (Codemask|Seekend)) != Seekend){
7317dd7cddfSDavid du Colombier 		DPRINT("recalibrate: failed\n");
7327dd7cddfSDavid du Colombier 		dp->confused = 1;
7337dd7cddfSDavid du Colombier 		return -1;
7347dd7cddfSDavid du Colombier 	}
7357dd7cddfSDavid du Colombier 	dp->cyl = fl.stat[1];
7367dd7cddfSDavid du Colombier 	if(dp->cyl != 0){
7377dd7cddfSDavid du Colombier 		DPRINT("recalibrate: wrong cylinder %d\n", dp->cyl);
7387dd7cddfSDavid du Colombier 		dp->cyl = -1;
7397dd7cddfSDavid du Colombier 		dp->confused = 1;
7407dd7cddfSDavid du Colombier 		return -1;
7417dd7cddfSDavid du Colombier 	}
7427dd7cddfSDavid du Colombier 
7437dd7cddfSDavid du Colombier 	dp->confused = 0;
7447dd7cddfSDavid du Colombier 	return 0;
7457dd7cddfSDavid du Colombier }
7467dd7cddfSDavid du Colombier 
7477dd7cddfSDavid du Colombier /*
7487dd7cddfSDavid du Colombier  *  if the controller or a specific drive is in a confused state,
7497dd7cddfSDavid du Colombier  *  reset it and get back to a known state
7507dd7cddfSDavid du Colombier  */
7517dd7cddfSDavid du Colombier static void
floppyrevive(void)7527dd7cddfSDavid du Colombier floppyrevive(void)
7537dd7cddfSDavid du Colombier {
7547dd7cddfSDavid du Colombier 	FDrive *dp;
7557dd7cddfSDavid du Colombier 
7567dd7cddfSDavid du Colombier 	/*
7577dd7cddfSDavid du Colombier 	 *  reset the controller if it's confused
7587dd7cddfSDavid du Colombier 	 */
7597dd7cddfSDavid du Colombier 	if(fl.confused){
7607dd7cddfSDavid du Colombier 		DPRINT("floppyrevive in\n");
7617dd7cddfSDavid du Colombier 		fldump();
7627dd7cddfSDavid du Colombier 
7637dd7cddfSDavid du Colombier 		/* reset controller and turn all motors off */
7647dd7cddfSDavid du Colombier 		splhi();
7657dd7cddfSDavid du Colombier 		fl.ncmd = 1;
7667dd7cddfSDavid du Colombier 		fl.cmd[0] = 0;
7677dd7cddfSDavid du Colombier 		outb(Pdor, 0);
7687dd7cddfSDavid du Colombier 		delay(10);
7697dd7cddfSDavid du Colombier 		outb(Pdor, Fintena|Fena);
7707dd7cddfSDavid du Colombier 		delay(10);
7717dd7cddfSDavid du Colombier 		spllo();
7727dd7cddfSDavid du Colombier 		fl.motor = 0;
7737dd7cddfSDavid du Colombier 		fl.confused = 0;
77459cc4ca5SDavid du Colombier 		floppywait(0);
7757dd7cddfSDavid du Colombier 
7767dd7cddfSDavid du Colombier 		/* mark all drives in an unknown state */
7777dd7cddfSDavid du Colombier 		for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++)
7787dd7cddfSDavid du Colombier 			dp->confused = 1;
7797dd7cddfSDavid du Colombier 
7807dd7cddfSDavid du Colombier 		/* set rate to a known value */
7817dd7cddfSDavid du Colombier 		outb(Pdsr, 0);
7827dd7cddfSDavid du Colombier 		fl.rate = 0;
7837dd7cddfSDavid du Colombier 
7847dd7cddfSDavid du Colombier 		DPRINT("floppyrevive out\n");
7857dd7cddfSDavid du Colombier 		fldump();
7867dd7cddfSDavid du Colombier 	}
7877dd7cddfSDavid du Colombier }
7887dd7cddfSDavid du Colombier 
7897dd7cddfSDavid du Colombier /*
7907dd7cddfSDavid du Colombier  *  seek to the target cylinder
7917dd7cddfSDavid du Colombier  *
7927dd7cddfSDavid du Colombier  *	interrupt, no results
7937dd7cddfSDavid du Colombier  */
7947dd7cddfSDavid du Colombier static long
floppyseek(FDrive * dp,long off)7957dd7cddfSDavid du Colombier floppyseek(FDrive *dp, long off)
7967dd7cddfSDavid du Colombier {
7977dd7cddfSDavid du Colombier 	floppypos(dp, off);
7987dd7cddfSDavid du Colombier 	if(dp->cyl == dp->tcyl)
7997dd7cddfSDavid du Colombier 		return dp->tcyl;
8007dd7cddfSDavid du Colombier 	dp->cyl = -1;
8017dd7cddfSDavid du Colombier 
8027dd7cddfSDavid du Colombier 	fl.ncmd = 0;
8037dd7cddfSDavid du Colombier 	fl.cmd[fl.ncmd++] = Fseek;
8047dd7cddfSDavid du Colombier 	fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev;
8057dd7cddfSDavid du Colombier 	fl.cmd[fl.ncmd++] = dp->tcyl * dp->t->steps;
8067dd7cddfSDavid du Colombier 	if(floppycmd() < 0)
8077dd7cddfSDavid du Colombier 		return -1;
80859cc4ca5SDavid du Colombier 	floppywait(1);
8097dd7cddfSDavid du Colombier 	if(fl.nstat < 2){
8107dd7cddfSDavid du Colombier 		DPRINT("seek: confused\n");
8117dd7cddfSDavid du Colombier 		fl.confused = 1;
8127dd7cddfSDavid du Colombier 		return -1;
8137dd7cddfSDavid du Colombier 	}
8147dd7cddfSDavid du Colombier 	if((fl.stat[0] & (Codemask|Seekend)) != Seekend){
8157dd7cddfSDavid du Colombier 		DPRINT("seek: failed\n");
8167dd7cddfSDavid du Colombier 		dp->confused = 1;
8177dd7cddfSDavid du Colombier 		return -1;
8187dd7cddfSDavid du Colombier 	}
8197dd7cddfSDavid du Colombier 
8207dd7cddfSDavid du Colombier 	dp->cyl = dp->tcyl;
8217dd7cddfSDavid du Colombier 	return dp->tcyl;
8227dd7cddfSDavid du Colombier }
8237dd7cddfSDavid du Colombier 
8247dd7cddfSDavid du Colombier /*
8257dd7cddfSDavid du Colombier  *  read or write to floppy.  try up to three times.
8267dd7cddfSDavid du Colombier  */
8277dd7cddfSDavid du Colombier static long
floppyxfer(FDrive * dp,int cmd,void * a,long off,long n)8287dd7cddfSDavid du Colombier floppyxfer(FDrive *dp, int cmd, void *a, long off, long n)
8297dd7cddfSDavid du Colombier {
8307dd7cddfSDavid du Colombier 	long offset;
8317dd7cddfSDavid du Colombier 	int tries;
8327dd7cddfSDavid du Colombier 
8337dd7cddfSDavid du Colombier 	if(off >= dp->t->cap)
8347dd7cddfSDavid du Colombier 		return 0;
8357dd7cddfSDavid du Colombier 	if(off + n > dp->t->cap)
8367dd7cddfSDavid du Colombier 		n = dp->t->cap - off;
8377dd7cddfSDavid du Colombier 
8387dd7cddfSDavid du Colombier 	/* retry on error (until it gets ridiculous) */
8397dd7cddfSDavid du Colombier 	tries = 0;
8407dd7cddfSDavid du Colombier 	while(waserror()){
84159cc4ca5SDavid du Colombier 		if(tries++ >= dp->maxtries)
8427dd7cddfSDavid du Colombier 			nexterror();
8437dd7cddfSDavid du Colombier 		DPRINT("floppyxfer: retrying\n");
8447dd7cddfSDavid du Colombier 	}
8457dd7cddfSDavid du Colombier 
8467dd7cddfSDavid du Colombier 	dp->len = n;
8477dd7cddfSDavid du Colombier 	if(floppyseek(dp, off) < 0){
8487dd7cddfSDavid du Colombier 		DPRINT("xfer: seek failed\n");
8497dd7cddfSDavid du Colombier 		dp->confused = 1;
8507dd7cddfSDavid du Colombier 		error(Eio);
8517dd7cddfSDavid du Colombier 	}
8527dd7cddfSDavid du Colombier 
8537dd7cddfSDavid du Colombier 	/*
8547dd7cddfSDavid du Colombier 	 *  set up the dma (dp->len may be trimmed)
8557dd7cddfSDavid du Colombier 	 */
8567dd7cddfSDavid du Colombier 	if(waserror()){
8577dd7cddfSDavid du Colombier 		dmaend(DMAchan);
8587dd7cddfSDavid du Colombier 		nexterror();
8597dd7cddfSDavid du Colombier 	}
8607dd7cddfSDavid du Colombier 	dp->len = dmasetup(DMAchan, a, dp->len, cmd==Fread);
8617dd7cddfSDavid du Colombier 	if(dp->len < 0)
8627dd7cddfSDavid du Colombier 		error(Eio);
8637dd7cddfSDavid du Colombier 
8647dd7cddfSDavid du Colombier 	/*
8657dd7cddfSDavid du Colombier 	 *  start operation
8667dd7cddfSDavid du Colombier 	 */
8677dd7cddfSDavid du Colombier 	fl.ncmd = 0;
8687dd7cddfSDavid du Colombier 	fl.cmd[fl.ncmd++] = cmd | (dp->t->heads > 1 ? Fmulti : 0);
8697dd7cddfSDavid du Colombier 	fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev;
8707dd7cddfSDavid du Colombier 	fl.cmd[fl.ncmd++] = dp->tcyl;
8717dd7cddfSDavid du Colombier 	fl.cmd[fl.ncmd++] = dp->thead;
8727dd7cddfSDavid du Colombier 	fl.cmd[fl.ncmd++] = dp->tsec;
8737dd7cddfSDavid du Colombier 	fl.cmd[fl.ncmd++] = dp->t->bcode;
8747dd7cddfSDavid du Colombier 	fl.cmd[fl.ncmd++] = dp->t->sectors;
8757dd7cddfSDavid du Colombier 	fl.cmd[fl.ncmd++] = dp->t->gpl;
8767dd7cddfSDavid du Colombier 	fl.cmd[fl.ncmd++] = 0xFF;
8777dd7cddfSDavid du Colombier 	if(floppycmd() < 0)
8787dd7cddfSDavid du Colombier 		error(Eio);
8797dd7cddfSDavid du Colombier 
8807dd7cddfSDavid du Colombier 	/* Poll ready bits and transfer data */
8817dd7cddfSDavid du Colombier 	floppyexec((char*)a, dp->len, cmd==Fread);
8827dd7cddfSDavid du Colombier 
8837dd7cddfSDavid du Colombier 	/*
8847dd7cddfSDavid du Colombier 	 *  give bus to DMA, floppyintr() will read result
8857dd7cddfSDavid du Colombier 	 */
88659cc4ca5SDavid du Colombier 	floppywait(0);
8877dd7cddfSDavid du Colombier 	dmaend(DMAchan);
8887dd7cddfSDavid du Colombier 	poperror();
8897dd7cddfSDavid du Colombier 
8907dd7cddfSDavid du Colombier 	/*
8917dd7cddfSDavid du Colombier 	 *  check for errors
8927dd7cddfSDavid du Colombier 	 */
8937dd7cddfSDavid du Colombier 	if(fl.nstat < 7){
8947dd7cddfSDavid du Colombier 		DPRINT("xfer: confused\n");
8957dd7cddfSDavid du Colombier 		fl.confused = 1;
8967dd7cddfSDavid du Colombier 		error(Eio);
8977dd7cddfSDavid du Colombier 	}
8987dd7cddfSDavid du Colombier 	if((fl.stat[0] & Codemask)!=0 || fl.stat[1] || fl.stat[2]){
8997dd7cddfSDavid du Colombier 		DPRINT("xfer: failed %ux %ux %ux\n", fl.stat[0],
9007dd7cddfSDavid du Colombier 			fl.stat[1], fl.stat[2]);
9017dd7cddfSDavid du Colombier 		DPRINT("offset %lud len %ld\n", off, dp->len);
9027dd7cddfSDavid du Colombier 		if((fl.stat[0]&Codemask)==Cmdexec && fl.stat[1]==Overrun){
9037dd7cddfSDavid du Colombier 			DPRINT("DMA overrun: retry\n");
9047dd7cddfSDavid du Colombier 		} else
9057dd7cddfSDavid du Colombier 			dp->confused = 1;
9067dd7cddfSDavid du Colombier 		error(Eio);
9077dd7cddfSDavid du Colombier 	}
9087dd7cddfSDavid du Colombier 
9097dd7cddfSDavid du Colombier 	/*
9107dd7cddfSDavid du Colombier 	 *  check for correct cylinder
9117dd7cddfSDavid du Colombier 	 */
9127dd7cddfSDavid du Colombier 	offset = fl.stat[3] * dp->t->heads + fl.stat[4];
9137dd7cddfSDavid du Colombier 	offset = offset*dp->t->sectors + fl.stat[5] - 1;
9147dd7cddfSDavid du Colombier 	offset = offset * c2b[fl.stat[6]];
9157dd7cddfSDavid du Colombier 	if(offset != off+dp->len){
9167dd7cddfSDavid du Colombier 		DPRINT("xfer: ends on wrong cyl\n");
9177dd7cddfSDavid du Colombier 		dp->confused = 1;
9187dd7cddfSDavid du Colombier 		error(Eio);
9197dd7cddfSDavid du Colombier 	}
9207dd7cddfSDavid du Colombier 	poperror();
9217dd7cddfSDavid du Colombier 
9227dd7cddfSDavid du Colombier 	dp->lasttouched = m->ticks;
9237dd7cddfSDavid du Colombier 	return dp->len;
9247dd7cddfSDavid du Colombier }
9257dd7cddfSDavid du Colombier 
9267dd7cddfSDavid du Colombier /*
9277dd7cddfSDavid du Colombier  *  format a track
9287dd7cddfSDavid du Colombier  */
9297dd7cddfSDavid du Colombier static void
floppyformat(FDrive * dp,Cmdbuf * cb)9309a747e4fSDavid du Colombier floppyformat(FDrive *dp, Cmdbuf *cb)
9317dd7cddfSDavid du Colombier {
9327dd7cddfSDavid du Colombier  	int cyl, h, sec;
9337dd7cddfSDavid du Colombier 	ulong track;
9347dd7cddfSDavid du Colombier 	uchar *buf, *bp;
9357dd7cddfSDavid du Colombier 	FType *t;
9367dd7cddfSDavid du Colombier 
9377dd7cddfSDavid du Colombier 	/*
9387dd7cddfSDavid du Colombier 	 *  set the type
9397dd7cddfSDavid du Colombier 	 */
9409a747e4fSDavid du Colombier 	if(cb->nf == 2){
9417dd7cddfSDavid du Colombier 		for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){
9429a747e4fSDavid du Colombier 			if(strcmp(cb->f[1], t->name)==0 && t->dt==dp->dt){
9437dd7cddfSDavid du Colombier 				dp->t = t;
9449a747e4fSDavid du Colombier 				floppydir[1+NFDIR*dp->dev].length = dp->t->cap;
9457dd7cddfSDavid du Colombier 				break;
9467dd7cddfSDavid du Colombier 			}
9477dd7cddfSDavid du Colombier 		}
9487dd7cddfSDavid du Colombier 		if(t >= &floppytype[nelem(floppytype)])
9497dd7cddfSDavid du Colombier 			error(Ebadarg);
9509a747e4fSDavid du Colombier 	} else if(cb->nf == 1){
9517dd7cddfSDavid du Colombier 		floppysetdef(dp);
9527dd7cddfSDavid du Colombier 		t = dp->t;
9539a747e4fSDavid du Colombier 	} else {
9549a747e4fSDavid du Colombier 		cmderror(cb, "invalid floppy format command");
9559a747e4fSDavid du Colombier 		SET(t);
9567dd7cddfSDavid du Colombier 	}
9577dd7cddfSDavid du Colombier 
9587dd7cddfSDavid du Colombier 	/*
9597dd7cddfSDavid du Colombier 	 *  buffer for per track info
9607dd7cddfSDavid du Colombier 	 */
9617dd7cddfSDavid du Colombier 	buf = smalloc(t->sectors*4);
9627dd7cddfSDavid du Colombier 	if(waserror()){
9637dd7cddfSDavid du Colombier 		free(buf);
9647dd7cddfSDavid du Colombier 		nexterror();
9657dd7cddfSDavid du Colombier 	}
9667dd7cddfSDavid du Colombier 
9677dd7cddfSDavid du Colombier 	/* force a recalibrate to cylinder 0 */
9687dd7cddfSDavid du Colombier 	dp->confused = 1;
9697dd7cddfSDavid du Colombier 	if(!waserror()){
9707dd7cddfSDavid du Colombier 		floppyon(dp);
9717dd7cddfSDavid du Colombier 		poperror();
9727dd7cddfSDavid du Colombier 	}
9737dd7cddfSDavid du Colombier 
9747dd7cddfSDavid du Colombier 	/*
9757dd7cddfSDavid du Colombier 	 *  format a track at time
9767dd7cddfSDavid du Colombier 	 */
9777dd7cddfSDavid du Colombier 	for(track = 0; track < t->tracks*t->heads; track++){
9787dd7cddfSDavid du Colombier 		cyl = track/t->heads;
9797dd7cddfSDavid du Colombier 		h = track % t->heads;
9807dd7cddfSDavid du Colombier 
9817dd7cddfSDavid du Colombier 		/*
9827dd7cddfSDavid du Colombier 		 *  seek to track, ignore errors
9837dd7cddfSDavid du Colombier 		 */
9847dd7cddfSDavid du Colombier 		floppyseek(dp, track*t->tsize);
9857dd7cddfSDavid du Colombier 		dp->cyl = cyl;
9867dd7cddfSDavid du Colombier 		dp->confused = 0;
9877dd7cddfSDavid du Colombier 
9887dd7cddfSDavid du Colombier 		/*
9897dd7cddfSDavid du Colombier 		 *  set up the dma (dp->len may be trimmed)
9907dd7cddfSDavid du Colombier 		 */
9917dd7cddfSDavid du Colombier 		bp = buf;
9927dd7cddfSDavid du Colombier 		for(sec = 1; sec <= t->sectors; sec++){
9937dd7cddfSDavid du Colombier 			*bp++ = cyl;
9947dd7cddfSDavid du Colombier 			*bp++ = h;
9957dd7cddfSDavid du Colombier 			*bp++ = sec;
9967dd7cddfSDavid du Colombier 			*bp++ = t->bcode;
9977dd7cddfSDavid du Colombier 		}
9987dd7cddfSDavid du Colombier 		if(waserror()){
9997dd7cddfSDavid du Colombier 			dmaend(DMAchan);
10007dd7cddfSDavid du Colombier 			nexterror();
10017dd7cddfSDavid du Colombier 		}
10027dd7cddfSDavid du Colombier 		if(dmasetup(DMAchan, buf, bp-buf, 0) < 0)
10037dd7cddfSDavid du Colombier 			error(Eio);
10047dd7cddfSDavid du Colombier 
10057dd7cddfSDavid du Colombier 		/*
10067dd7cddfSDavid du Colombier 		 *  start operation
10077dd7cddfSDavid du Colombier 		 */
10087dd7cddfSDavid du Colombier 		fl.ncmd = 0;
10097dd7cddfSDavid du Colombier 		fl.cmd[fl.ncmd++] = Fformat;
10107dd7cddfSDavid du Colombier 		fl.cmd[fl.ncmd++] = (h<<2) | dp->dev;
10117dd7cddfSDavid du Colombier 		fl.cmd[fl.ncmd++] = t->bcode;
10127dd7cddfSDavid du Colombier 		fl.cmd[fl.ncmd++] = t->sectors;
10137dd7cddfSDavid du Colombier 		fl.cmd[fl.ncmd++] = t->fgpl;
10147dd7cddfSDavid du Colombier 		fl.cmd[fl.ncmd++] = 0x5a;
10157dd7cddfSDavid du Colombier 		if(floppycmd() < 0)
10167dd7cddfSDavid du Colombier 			error(Eio);
10177dd7cddfSDavid du Colombier 
10187dd7cddfSDavid du Colombier 		/* Poll ready bits and transfer data */
10197dd7cddfSDavid du Colombier 		floppyexec((char *)buf, bp-buf, 0);
10207dd7cddfSDavid du Colombier 
10217dd7cddfSDavid du Colombier 		/*
10227dd7cddfSDavid du Colombier 		 *  give bus to DMA, floppyintr() will read result
10237dd7cddfSDavid du Colombier 		 */
102459cc4ca5SDavid du Colombier 		floppywait(1);
10257dd7cddfSDavid du Colombier 		dmaend(DMAchan);
10267dd7cddfSDavid du Colombier 		poperror();
10277dd7cddfSDavid du Colombier 
10287dd7cddfSDavid du Colombier 		/*
10297dd7cddfSDavid du Colombier 		 *  check for errors
10307dd7cddfSDavid du Colombier 		 */
10317dd7cddfSDavid du Colombier 		if(fl.nstat < 7){
10327dd7cddfSDavid du Colombier 			DPRINT("format: confused\n");
10337dd7cddfSDavid du Colombier 			fl.confused = 1;
10347dd7cddfSDavid du Colombier 			error(Eio);
10357dd7cddfSDavid du Colombier 		}
10367dd7cddfSDavid du Colombier 		if((fl.stat[0]&Codemask)!=0 || fl.stat[1]|| fl.stat[2]){
10377dd7cddfSDavid du Colombier 			DPRINT("format: failed %ux %ux %ux\n",
10387dd7cddfSDavid du Colombier 				fl.stat[0], fl.stat[1], fl.stat[2]);
10397dd7cddfSDavid du Colombier 			dp->confused = 1;
10407dd7cddfSDavid du Colombier 			error(Eio);
10417dd7cddfSDavid du Colombier 		}
10427dd7cddfSDavid du Colombier 	}
10437dd7cddfSDavid du Colombier 	free(buf);
10447dd7cddfSDavid du Colombier 	dp->confused = 1;
10457dd7cddfSDavid du Colombier 	poperror();
10467dd7cddfSDavid du Colombier }
10477dd7cddfSDavid du Colombier 
10487dd7cddfSDavid du Colombier static void
floppyintr(Ureg *)10497dd7cddfSDavid du Colombier floppyintr(Ureg *)
10507dd7cddfSDavid du Colombier {
10517dd7cddfSDavid du Colombier 	switch(fl.cmd[0]&~Fmulti){
10527dd7cddfSDavid du Colombier 	case Fread:
10537dd7cddfSDavid du Colombier 	case Fwrite:
10547dd7cddfSDavid du Colombier 	case Fformat:
10557dd7cddfSDavid du Colombier 	case Fdumpreg:
10567dd7cddfSDavid du Colombier 		floppyresult();
10577dd7cddfSDavid du Colombier 		break;
10587dd7cddfSDavid du Colombier 	case Fseek:
10597dd7cddfSDavid du Colombier 	case Frecal:
10607dd7cddfSDavid du Colombier 	default:
10617dd7cddfSDavid du Colombier 		floppysense();	/* to clear interrupt */
10627dd7cddfSDavid du Colombier 		break;
10637dd7cddfSDavid du Colombier 	}
10647dd7cddfSDavid du Colombier 	fl.ncmd = 0;
10657dd7cddfSDavid du Colombier 	wakeup(&fl.r);
10667dd7cddfSDavid du Colombier }
10677dd7cddfSDavid du Colombier 
10687dd7cddfSDavid du Colombier Dev floppydevtab = {
10697dd7cddfSDavid du Colombier 	'f',
10707dd7cddfSDavid du Colombier 	"floppy",
10717dd7cddfSDavid du Colombier 
10727dd7cddfSDavid du Colombier 	floppyreset,
10737dd7cddfSDavid du Colombier 	devinit,
10749a747e4fSDavid du Colombier 	devshutdown,
10757dd7cddfSDavid du Colombier 	floppyattach,
10767dd7cddfSDavid du Colombier 	floppywalk,
10777dd7cddfSDavid du Colombier 	floppystat,
10787dd7cddfSDavid du Colombier 	floppyopen,
10797dd7cddfSDavid du Colombier 	devcreate,
10807dd7cddfSDavid du Colombier 	floppyclose,
10817dd7cddfSDavid du Colombier 	floppyread,
10827dd7cddfSDavid du Colombier 	devbread,
10837dd7cddfSDavid du Colombier 	floppywrite,
10847dd7cddfSDavid du Colombier 	devbwrite,
10857dd7cddfSDavid du Colombier 	devremove,
10867dd7cddfSDavid du Colombier 	devwstat,
10877dd7cddfSDavid du Colombier };
1088