Plan 9 from Bell Labs’s /usr/web/sources/extra/9hist/alphapc/devfloppy.c

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


## diffname alphapc/devfloppy.c 1999/0415
## diff -e /dev/null /n/emeliedump/1999/0415/sys/src/brazil/alphapc/devfloppy.c
0a
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"../port/error.h"

#include	"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
int floppydebug = 0;

/*
 *  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*);
static void	floppyformat(FDrive*, char*);
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);
static void	floppywait(void);
static long	floppyxfer(FDrive*, int, void*, long, long);

Dirtab floppydir[]={
	"fd0disk",		{Qdata + 0},	0,	0660,
	"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 */

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

/*
 *  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;
			floppydir[NFDIR*dp->dev].length = dp->t->cap;
			break;
		}
}

static void
floppyreset(void)
{
	FDrive *dp;
	FType *t;
	ulong maxtsize;

	dmainit(DMAchan);
	
	floppysetup0(&fl);

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

	/*
	 *  allocate the drive storage
	 */
	fl.d = xalloc(fl.ndrive*sizeof(FDrive));
	fl.selected = fl.d;

	/*
	 *  stop the motors
	 */
	fl.motor = 0;
	delay(10);
mb();
	outb(Pdor, fl.motor | Fintena | Fena);
mb();
	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;

	if(kstarted == 0){
		/*
		 *  watchdog to turn off the motors
		 */
		kstarted = 1;
		kproc("floppy", floppykproc, 0);
	}
	return devattach('f', spec);
}

static int
floppywalk(Chan *c, char *name)
{
	return devwalk(c, name, floppydir, fl.ndrive*NFDIR, devgen);
}

static void
floppystat(Chan *c, char *dp)
{
	devstat(c, dp, floppydir, fl.ndrive*NFDIR, devgen);
}

static Chan*
floppyopen(Chan *c, int omode)
{
	return devopen(c, omode, floppydir, fl.ndrive*NFDIR, devgen);
}

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
	 */
mb();
	if((inb(Pdir)&Fchange) || dp->vers == 0){
mb();
		DPRINT("changed\n");
		fldump();
		dp->vers++;
		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)])
					dp->t = floppytype;
				if(dp->dt == dp->t->dt)
					break;
			}
			floppydir[NFDIR*dp->dev].length = dp->t->cap;
			floppyon(dp);
			DPRINT("changed: trying %s\n", dp->t->name);
			fldump();
			if(dp->t == start)
				nexterror();
		}
		floppyxfer(dp, Fread, dp->cache, 0, dp->t->tsize);
		poperror();
	}

	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;

	if(c->qid.path == CHDIR)
		return devdirread(c, a, n, floppydir, fl.ndrive*NFDIR, devgen);

	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;
}

#define SNCMP(a, b) strncmp(a, b, sizeof(b)-1)
static long
floppywrite(Chan *c, void *a, long n, vlong off)
{
	FDrive *dp;
	long rv, i;
	char *aa = a;
	char ctlmsg[64];
	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;
		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){
			floppyeject(dp);
		} else if(SNCMP(ctlmsg, "reset") == 0){
			fl.confused = 1;
			floppyon(dp);
		} else if(SNCMP(ctlmsg, "format") == 0){
			floppyformat(dp, ctlmsg);
		} else if(SNCMP(ctlmsg, "debug") == 0){
			floppydebug = 1;
		} else
			error(Ebadctl);
		poperror();
		qunlock(&fl);
		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.
 */
static void
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);
mb();
	outb(Pdor, fl.motor | Fintena | Fena | dp->dev);
mb();
	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;
mb();
		outb(Pdsr, fl.rate);
mb();
	}

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

/*
 *  stop the floppy if it hasn't been used in 5 seconds
 */
static void
floppyoff(FDrive *dp)
{
	fl.motor &= ~MOTORBIT(dp->dev);
mb();
	outb(Pdor, fl.motor | Fintena | Fena | dp->dev);
mb();
}

/*
 *  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++){
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();

				/* empty fifo, might have been a bad command */
				floppyresult();
				return -1;
			}
			microdelay(8);	/* for machine independence */
		}
mb();
		outb(Pfdata, fl.cmd[i]);
mb();
	}
	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++){
mb();
			s = inb(Pmsr)&(Ffrom|Fready);
mb();
			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 */
		}
mb();
		fl.stat[i] = inb(Pfdata);
mb();
// print("stat[%d]: %.2ux\n", i, fl.stat[i]);
	}
	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
floppywait(void)
{
	tsleep(&fl.r, cmddone, 0, 5000);
	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;
	floppywait();
	if(fl.nstat < 2){
mb();
		DPRINT("recalibrate: confused %ux\n", inb(Pmsr));
mb();
		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,
 *  reset it and get back to a kown state
 */
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;
mb();
		outb(Pdor, 0);
mb();
		delay(10);
mb();
		outb(Pdor, Fintena|Fena);
mb();
		delay(10);
		spllo();
		fl.motor = 0;
		fl.confused = 0;
		floppywait();

		/* 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 */
mb();
		outb(Pdsr, 0);
mb();
		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;
	floppywait();
	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()){
		if(tries++ > 20)
			nexterror();
		DPRINT("floppyxfer: retrying\n");
		/*floppyon(dp);*/
	}

	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
	 */
	floppywait();
	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
floppyformat(FDrive *dp, char *params)
{
 	int cyl, h, sec;
	ulong track;
	uchar *buf, *bp;
	FType *t;
	char *f[3];

	/*
	 *  set the type
	 */
	if(parsefields(params, f, 3, " ") > 1){
		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;
				break;
			}
		}
		if(t >= &floppytype[nelem(floppytype)])
			error(Ebadarg);
	} else {
		floppysetdef(dp);
		t = dp->t;
	}

	/*
	 *  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
		 */
		floppywait();
		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,
	devclone,
	floppywalk,
	floppystat,
	floppyopen,
	devcreate,
	floppyclose,
	floppyread,
	devbread,
	floppywrite,
	devbwrite,
	devremove,
	devwstat,
};
.
## diffname alphapc/devfloppy.c 1999/0423
## diff -e /n/emeliedump/1999/0415/sys/src/brazil/alphapc/devfloppy.c /n/emeliedump/1999/0423/sys/src/brazil/alphapc/devfloppy.c
752d
750d
738d
736d
734d
732d
691d
689d
598,599d
596d
581d
579d
557d
555d
544d
541d
526d
524d
505d
503d
491d
489d
285,286d
282,283d
279,280d
272d
270d
179d
177d
165a
	dmainit(DMAchan, maxtsize);

.
149,150d
124d
121d
## diffname alphapc/devfloppy.c 1999/0501
## diff -e /n/emeliedump/1999/0423/sys/src/brazil/alphapc/devfloppy.c /n/emeliedump/1999/0501/sys/src/brazil/alphapc/devfloppy.c
822a
delay(10);
print("msr 0x%2.2uX\n", inb(Pmsr));
.
777c
		if(tries++ > 2/*0*/)
.
685c
 *  reset it and get back to a known state
.
682a
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();
}

.
639a
t1 = fastticks(nil);
print("wait %lld ticks\n", t1-t0);
.
638a
vlong t0, t1;
t0 = fastticks(nil);
.
535a
microdelay(8);	/* for machine independence */
.
64a
 */
.
58a
/*
.
42c
int floppydebug = 1;
.
## diffname alphapc/devfloppy.c 1999/0504
## diff -e /n/emeliedump/1999/0501/sys/src/brazil/alphapc/devfloppy.c /n/emeliedump/1999/0504/sys/src/brazil/alphapc/devfloppy.c
844a
xdmastatus(DMAchan);
.
## diffname alphapc/devfloppy.c 1999/0506
## diff -e /n/emeliedump/1999/0504/sys/src/brazil/alphapc/devfloppy.c /n/emeliedump/1999/0506/sys/src/brazil/alphapc/devfloppy.c
843,845d
797c
		if(tries++ > 20)
.
690a
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
.
645,646d
642,643d
538d
109c
//	"fd0disk",		{Qdata + 0},	0,	0660,
	"fd0disk",		{Qdata + 0},	0,	0666,
.
42c
int floppydebug = 0;
.
## diffname alphapc/devfloppy.c 1999/0507
## diff -e /n/emeliedump/1999/0506/sys/src/brazil/alphapc/devfloppy.c /n/emeliedump/1999/0507/sys/src/brazil/alphapc/devfloppy.c
808a
if(cmd == Fread)
    memset(a, 0x55, n);
.
66d
59d
## diffname alphapc/devfloppy.c 2000/0308
## diff -e /n/emeliedump/1999/0507/sys/src/brazil/alphapc/devfloppy.c /n/emeliedump/2000/0308/sys/src/9/alphapc/devfloppy.c
914c
	if(getfields(params, f, 3, 1, " ") > 1){
.
## diffname alphapc/devfloppy.c 2001/0727
## diff -e /n/emeliedump/2000/0308/sys/src/9/alphapc/devfloppy.c /n/emeliedump/2001/0727/sys/src/9/alphapc/devfloppy.c
1046d
918c
				floppydir[1+NFDIR*dp->dev].length = dp->t->cap;
.
331,332c
	if(c->qid.type & QTDIR)
		return devdirread(c, a, n, floppydir, 1+fl.ndrive*NFDIR, devgen);
.
283c
			floppydir[1+NFDIR*dp->dev].length = dp->t->cap;
.
230c
	return devopen(c, omode, floppydir, 1+fl.ndrive*NFDIR, devgen);
.
224c
	return devstat(c, dp, n, floppydir, 1+fl.ndrive*NFDIR, devgen);
.
221,222c
static int
floppystat(Chan *c, uchar *dp, int n)
.
218c
	return devwalk(c, nc, name, nname, floppydir, 1+fl.ndrive*NFDIR, devgen);
.
215,216c
static Walkqid*
floppywalk(Chan *c, Chan *nc, char **name, int nname)
.
137c
			floppydir[1+NFDIR*dp->dev].length = dp->t->cap;
.
107c
	".",		{Qdir, 0, QTDIR},	0,	0550,
.
## diffname alphapc/devfloppy.c 2001/1117
## diff -e /n/emeliedump/2001/0727/sys/src/9/alphapc/devfloppy.c /n/emeliedump/2001/1117/sys/src/9/alphapc/devfloppy.c
914c
	if(tokenize(params, f, 3) > 1){
.
## diffname alphapc/devfloppy.c 2001/1122
## diff -e /n/emeliedump/2001/1117/sys/src/9/alphapc/devfloppy.c /n/emeliedump/2001/1122/sys/src/9/alphapc/devfloppy.c
995c
		floppywait(1);
.
926a
	} else {
		cmderror(cb, "invalid floppy format command");
		SET(t);
.
924c
	} else if(cb->nf == 1){
.
916c
			if(strcmp(cb->f[1], t->name)==0 && t->dt==dp->dt){
.
914c
	if(cb->nf == 2){
.
909d
903c
floppyformat(FDrive *dp, Cmdbuf *cb)
.
859c
	floppywait(0);
.
816d
813c
		if(tries++ >= dp->maxtries)
.
807,808d
778c
	floppywait(1);
.
744c
		floppywait(0);
.
684,716d
661c
	floppywait(1);
.
640c
	tsleep(&fl.r, cmddone, 0, slow ? 5000 : 1000);
.
638c
floppywait(int slow)
.
499a

	/* return -1 if this didn't work */
	if(dp->confused)
		return -1;
	return 0;
.
466c
static int
.
433a
		poperror();
		free(cb);
.
430,431c
			break;
		}
.
426,428c
			break;
		case CMdebug:
.
423c
			break;
		case CMformat:
			floppyformat(dp, cb);
			break;
		case CMreset:
.
417,421c
		ct = lookupcmd(cb, floppyctlmsg, nelem(floppyctlmsg));
		switch(ct->index){
		case CMeject:
.
411a
		cb = parsecmd(a, n);
		if(waserror()){
			free(cb);
			nexterror();
		}
.
382c
	Cmdbuf *cb;
	Cmdtab *ct;
.
375d
291a
		dp->maxtries = 20;
.
289a

		/* if the read succeeds, we've got the density right */
.
284c

			/* floppyon will fail if there's a controller but no drive */
			if(floppyon(dp) < 0)
				error(Eio);

.
276a
			/*
			 *  if first attempt doesn't reset changed bit, there's
			 *  no floppy there
			 */
			if(inb(Pdir)&Fchange)
				nexterror();

.
274c
		if(floppyon(dp) < 0)
			error(Eio);

		/* seek to the first track */
.
272a
		dp->maxtries = 3;	/* limit it when we're probing */

		/* floppyon will fail if there's a controller but no drive */
.
271d
204a
	if(fl.ndrive == 0)
		error(Enodev);

.
149a
	if(fl.ndrive == 0)
		return;
.
118a
enum
{
	CMdebug,
	CMeject,
	CMformat,
	CMreset,
};

static Cmdtab floppyctlmsg[] =
{
	CMdebug,	"debug",	1,
	CMeject,	"eject",	1,
	CMformat,	"format",	0,
	CMreset,	"reset",	1,
};

.
108c
	"fd0disk",		{Qdata + 0},	0,	0660,
.
103c
static void	floppywait(int);
.
95c
static void	floppyformat(FDrive*, Cmdbuf*);
.
## diffname alphapc/devfloppy.c 2001/1211
## diff -e /n/emeliedump/2001/1122/sys/src/9/alphapc/devfloppy.c /n/emeliedump/2001/1211/sys/src/9/alphapc/devfloppy.c
1,1076c
#include "../pc/devfloppy.c"
.

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.