/* * Intel 82077AA Floppy disc device driver */#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "../port/error.h"#include "io.h" #include "devtab.h" #define min(a, b) (a < b ? a : b) /* Registers on the 82077 */ typedef struct Controller Controller;struct Controller{ uchar sra; /* (R/O) status register A */ uchar srb; /* (R/O) status register B */ uchar dor; /* (R/W) digital output register */ uchar rsvd3; /* reserved */ uchar msr; /* (R/O) main status register */#define dsr msr uchar fifo; /* (R/W) data FIFO */ uchar rsvd6; /* reserved */ uchar dir; /* (R/O) digital input register */ uchar flpctl; /* (W/O) control (not in 82077) */}; enum /* Useful numbers */{ Eject_disc = 0x80, /* Eject the floppy */ Sel_82077 = 0x40, /* Scsi/82077 select */ Drv_id = 0x04, /* Drive present flag */ Mid1 = 0x02, /* Media id bits */ Mid2 = 0x01, Maxdrive = 4, Ok = 0, Error = -1, S512 = 512,}; enum /* Command set */{ Recal = 0x07, Seek = 0x0F, Readdat = 0x06, Writedat= 0x05, Sis = 0x80,}; enum /* Main status register */{ Drv0_bsy= 0x01, Drv1_bsy= 0x02, Drv2_bsy= 0x04, Drv3_bsy= 0x08, Cmd_bsy = 0x10, Non_bsy = 0x20, Dio = 0x40, Rqm = 0x80,}; enum /* Dor register bits */{ Moten3 = 0x80, Moten2 = 0x40, Moten1 = 0x20, Moten0 = 0x10, Dmagate = 0x08, Reset = 0x04, Drvsel1 = 0x02, Drvsel0 = 0x01,}; /* * floppy types */typedef struct Type Type;struct Type{ char *name; 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 */ int fgpl; /* intersector gap length for format */ }; Type floppytype[] ={ "MF2HD", S512, 18, 2, 1, 80, 0x1B, 0x54, "MF2DD", S512, 9, 2, 1, 80, 0x1B, 0x54, "F2HD", S512, 15, 2, 1, 80, 0x2A, 0x50, "F2DD", S512, 8, 2, 1, 40, 0x2A, 0x50, "F1DD", S512, 8, 1, 1, 40, 0x2A, 0x50,}; typedef struct Drive Drive;struct Drive{ Type *t; uchar dor; /* Digital output register (Drive select/Motor on) */ int synced; /* Drive is synced with computer */ int tcyl; /* Current cylinder */ int tsec; /* Current sector */ char thead; /* Current head */ Rendez r; /* Place to wait for interrupts */ QLock use; /* Exclusive use lock */ char ist; /* Interrupt status register */ char pcn; /* Physical cylinder from Sis command */ char gotint; uchar cmd[32]; /* Command and return status */ int cmdlen;}; Drive fl[Maxdrive]; static int secbytes[] ={ 128, 256, 512, 1024}; struct FLOPdma{ ulong csr; uchar pad1[0x4010-0x14]; ulong base; ulong limit; ulong chainbase; ulong chainlimit;}; void Xdelay(int);int issue(Drive*);int recalibrate(Drive*);int gotfdintr(void*);void waitforint(Drive *); enum{ ffddirqid, ffddataqid, ffdctlqid,}; Dirtab ffdtab[]={ "data", {ffddataqid}, 0, 0666, "ctl", {ffdctlqid}, 0, 0666,}; #define Nffdtab (sizeof(ffdtab)/sizeof(Dirtab)) voidffdreset(void){ Controller *fdc = (Controller*)(FDCTLRL); print("ffdreset\n"); fdc->flpctl = Sel_82077; print("fd: (sra=%lux srb=%lux msr=%lux)\n", fdc->sra, fdc->srb, fdc->msr); fdc->dsr = 0x80; print("fd: (sra=%lux srb=%lux msr=%lux)\n", fdc->sra, fdc->srb, fdc->msr); fl[0].dor = Moten0|Dmagate|Reset;} voidffdinit(void){} Chan *ffdattach(char *spec){ return devattach('f', spec);} Chan *ffdclone(Chan *c, Chan *nc){ return devclone(c, nc);} intffdwalk(Chan *c, char *name){ return devwalk(c, name, ffdtab, (long)Nffdtab, devgen);} voidffdstat(Chan *c, char *db){ devstat(c, db, ffdtab, (long)Nffdtab, devgen);} Chan *ffdopen(Chan *c, int omode){ if(c->qid.path == CHDIR){ if(omode != OREAD) error(Eperm); } if(fl[0].synced == 0) recalibrate(fl); c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; return c;} voidffdcreate(Chan *c, char *name, int omode, ulong perm){ error(Eperm);} voidffdremove(Chan *c){ error(Eperm);} voidffdwstat(Chan *c, char *dp){ error(Eperm);} voidffdclose(Chan *c){} longffdread(Chan *c, void *a, long n, ulong offset){ switch((int)(c->qid.path&~CHDIR)){ case ffddirqid: return devdirread(c, a, n, ffdtab, Nffdtab, devgen); case ffddataqid: break; case ffdctlqid: return n; default: n=0; break; } return n;} longffdwrite(Chan *c, char *a, long n, ulong offset){ Controller *fdc = (Controller*)(FDCTLRL); char buf[32]; int i; switch((int)(c->qid.path&~CHDIR)){ case ffddataqid: break; case ffdctlqid: strncpy(buf, a, min(sizeof(buf), n)); if(strncmp(buf, "eject", 5) == 0) { fdc->dor = 0x1c; fdc->flpctl = Sel_82077; Xdelay(100); fdc->flpctl = Eject_disc|Sel_82077; Xdelay(100); fdc->flpctl = Sel_82077; } else error(Ebadarg); default: error(Ebadusefd); } return n;} intrecalibrate(Drive *dp){ dp->cmd[0] = Recal; dp->cmd[1] = dp - fl; dp->cmdlen = 2; if(issue(dp) != Ok) return Error; waitforint(dp); dp->synced = 1; /* Drive in sync with controller */} voidwaitforint(Drive *dp){ Controller *fdc = (Controller*)(FDCTLRL); tsleep(&dp->r, gotfdintr, dp, 2000); if(dp->gotint == 0) { print("fd%d: operation timed out (sra=%lux srb=%lux msr=%lux)\n", dp-fl, fdc->sra, fdc->srb, fdc->msr); } dp->gotint = 0;} intissue(Drive *dp){ Controller *fdc = (Controller*)(FDCTLRL); uchar *cdb = dp->cmd; int i; print("fd: (msr=%lux)\n", fdc->msr); for(i = 0; i < 1000; i++) { if((fdc->msr&(Cmd_bsy|Rqm|Dio)) != Rqm) continue; fdc->dor = dp->dor; /* Motor on and select */ for(i = 0; i < dp->cmdlen; i++) { /* Load command fifo */ while((fdc->msr&Rqm) == 0) ; if(i == 1 && (fdc->msr&(Dio|Cmd_bsy) == (Dio|Cmd_bsy))) print("fd: illegal cmd %lux (msr=%lux)\n", cdb[-1], fdc->msr); fdc->fifo = *cdb++; } return Ok; } print("fd: controller cmd timed out (msr=%lux)\n", fdc->msr); return Error;} voidblock2phys(Drive *dp, ulong off){ int lsec; lsec = off/secbytes[dp->t->bytes]; dp->tcyl = lsec/(dp->t->sectors*dp->t->heads); dp->tsec = (lsec % dp->t->sectors) + 1; dp->thead = (lsec/dp->t->sectors) % dp->t->heads;} intgotfdintr(void *dp){ return ((Drive*)dp)->gotint;} voidfloppyintr(void){ Controller *fdc = (Controller*)(FDCTLRL); Drive *dp = &fl[fdc->dor&3]; print("floppy intr %d\n", dp-fl); dp->cmd[0] = Sis; dp->cmdlen = 1; if(issue(dp) != Ok) print("fd%d: Sis issue failed\n", dp-fl); dp->ist = fdc->fifo; dp->pcn = fdc->fifo; print("ist %lux %lux\n", dp->ist, dp->pcn); if(u) { fl[0].gotint = 1; wakeup(&fl[0].r); }}
/*
* Intel 82077AA Floppy disc device driver
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"
#include "devtab.h"
#define min(a, b) (a < b ? a : b)
/* Registers on the 82077 */
typedef struct Controller Controller;
struct Controller
{
uchar sra; /* (R/O) status register A */
uchar srb; /* (R/O) status register B */
uchar dor; /* (R/W) digital output register */
uchar rsvd3; /* reserved */
uchar msr; /* (R/O) main status register */
#define dsr msr
uchar fifo; /* (R/W) data FIFO */
uchar rsvd6; /* reserved */
uchar dir; /* (R/O) digital input register */
uchar flpctl; /* (W/O) control (not in 82077) */
};
enum /* Useful numbers */
{
Eject_disc = 0x80, /* Eject the floppy */
Sel_82077 = 0x40, /* Scsi/82077 select */
Drv_id = 0x04, /* Drive present flag */
Mid1 = 0x02, /* Media id bits */
Mid2 = 0x01,
Maxdrive = 4,
Ok = 0,
Error = -1,
S512 = 512,
};
enum /* Command set */
{
Recal = 0x07,
Seek = 0x0F,
Readdat = 0x06,
Writedat= 0x05,
Sis = 0x80,
};
enum /* Main status register */
{
Drv0_bsy= 0x01,
Drv1_bsy= 0x02,
Drv2_bsy= 0x04,
Drv3_bsy= 0x08,
Cmd_bsy = 0x10,
Non_bsy = 0x20,
Dio = 0x40,
Rqm = 0x80,
};
enum /* Dor register bits */
{
Moten3 = 0x80,
Moten2 = 0x40,
Moten1 = 0x20,
Moten0 = 0x10,
Dmagate = 0x08,
Reset = 0x04,
Drvsel1 = 0x02,
Drvsel0 = 0x01,
};
/*
* floppy types
*/
typedef struct Type Type;
struct Type
{
char *name;
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 */
int fgpl; /* intersector gap length for format */
};
Type floppytype[] =
{
"MF2HD", S512, 18, 2, 1, 80, 0x1B, 0x54,
"MF2DD", S512, 9, 2, 1, 80, 0x1B, 0x54,
"F2HD", S512, 15, 2, 1, 80, 0x2A, 0x50,
"F2DD", S512, 8, 2, 1, 40, 0x2A, 0x50,
"F1DD", S512, 8, 1, 1, 40, 0x2A, 0x50,
};
typedef struct Drive Drive;
struct Drive
{
Type *t;
uchar dor; /* Digital output register (Drive select/Motor on) */
int synced; /* Drive is synced with computer */
int tcyl; /* Current cylinder */
int tsec; /* Current sector */
char thead; /* Current head */
Rendez r; /* Place to wait for interrupts */
QLock use; /* Exclusive use lock */
char ist; /* Interrupt status register */
char pcn; /* Physical cylinder from Sis command */
char gotint;
uchar cmd[32]; /* Command and return status */
int cmdlen;
};
Drive fl[Maxdrive];
static int secbytes[] =
{
128,
256,
512,
1024
};
struct FLOPdma
{
ulong csr;
uchar pad1[0x4010-0x14];
ulong base;
ulong limit;
ulong chainbase;
ulong chainlimit;
};
void Xdelay(int);
int issue(Drive*);
int recalibrate(Drive*);
int gotfdintr(void*);
void waitforint(Drive *);
enum
{
ffddirqid,
ffddataqid,
ffdctlqid,
};
Dirtab ffdtab[]={
"data", {ffddataqid}, 0, 0666,
"ctl", {ffdctlqid}, 0, 0666,
};
#define Nffdtab (sizeof(ffdtab)/sizeof(Dirtab))
void
ffdreset(void)
{
Controller *fdc = (Controller*)(FDCTLRL);
print("ffdreset\n");
fdc->flpctl = Sel_82077;
print("fd: (sra=%lux srb=%lux msr=%lux)\n", fdc->sra, fdc->srb, fdc->msr);
fdc->dsr = 0x80;
print("fd: (sra=%lux srb=%lux msr=%lux)\n", fdc->sra, fdc->srb, fdc->msr);
fl[0].dor = Moten0|Dmagate|Reset;
}
void
ffdinit(void)
{
}
Chan *
ffdattach(char *spec)
{
return devattach('f', spec);
}
Chan *
ffdclone(Chan *c, Chan *nc)
{
return devclone(c, nc);
}
int
ffdwalk(Chan *c, char *name)
{
return devwalk(c, name, ffdtab, (long)Nffdtab, devgen);
}
void
ffdstat(Chan *c, char *db)
{
devstat(c, db, ffdtab, (long)Nffdtab, devgen);
}
Chan *
ffdopen(Chan *c, int omode)
{
if(c->qid.path == CHDIR){
if(omode != OREAD)
error(Eperm);
}
if(fl[0].synced == 0)
recalibrate(fl);
c->mode = openmode(omode);
c->flag |= COPEN;
c->offset = 0;
return c;
}
void
ffdcreate(Chan *c, char *name, int omode, ulong perm)
{
error(Eperm);
}
void
ffdremove(Chan *c)
{
error(Eperm);
}
void
ffdwstat(Chan *c, char *dp)
{
error(Eperm);
}
void
ffdclose(Chan *c)
{
}
long
ffdread(Chan *c, void *a, long n, ulong offset)
{
switch((int)(c->qid.path&~CHDIR)){
case ffddirqid:
return devdirread(c, a, n, ffdtab, Nffdtab, devgen);
case ffddataqid:
break;
case ffdctlqid:
return n;
default:
n=0;
break;
}
return n;
}
long
ffdwrite(Chan *c, char *a, long n, ulong offset)
{
Controller *fdc = (Controller*)(FDCTLRL);
char buf[32];
int i;
switch((int)(c->qid.path&~CHDIR)){
case ffddataqid:
break;
case ffdctlqid:
strncpy(buf, a, min(sizeof(buf), n));
if(strncmp(buf, "eject", 5) == 0) {
fdc->dor = 0x1c;
fdc->flpctl = Sel_82077;
Xdelay(100);
fdc->flpctl = Eject_disc|Sel_82077;
Xdelay(100);
fdc->flpctl = Sel_82077;
}
else
error(Ebadarg);
default:
error(Ebadusefd);
}
return n;
}
int
recalibrate(Drive *dp)
{
dp->cmd[0] = Recal;
dp->cmd[1] = dp - fl;
dp->cmdlen = 2;
if(issue(dp) != Ok)
return Error;
waitforint(dp);
dp->synced = 1; /* Drive in sync with controller */
}
void
waitforint(Drive *dp)
{
Controller *fdc = (Controller*)(FDCTLRL);
tsleep(&dp->r, gotfdintr, dp, 2000);
if(dp->gotint == 0) {
print("fd%d: operation timed out (sra=%lux srb=%lux msr=%lux)\n",
dp-fl, fdc->sra, fdc->srb, fdc->msr);
}
dp->gotint = 0;
}
int
issue(Drive *dp)
{
Controller *fdc = (Controller*)(FDCTLRL);
uchar *cdb = dp->cmd;
int i;
print("fd: (msr=%lux)\n", fdc->msr);
for(i = 0; i < 1000; i++) {
if((fdc->msr&(Cmd_bsy|Rqm|Dio)) != Rqm)
continue;
fdc->dor = dp->dor; /* Motor on and select */
for(i = 0; i < dp->cmdlen; i++) { /* Load command fifo */
while((fdc->msr&Rqm) == 0)
;
if(i == 1 && (fdc->msr&(Dio|Cmd_bsy) == (Dio|Cmd_bsy)))
print("fd: illegal cmd %lux (msr=%lux)\n", cdb[-1], fdc->msr);
fdc->fifo = *cdb++;
}
return Ok;
}
print("fd: controller cmd timed out (msr=%lux)\n", fdc->msr);
return Error;
}
void
block2phys(Drive *dp, ulong off)
{
int lsec;
lsec = off/secbytes[dp->t->bytes];
dp->tcyl = lsec/(dp->t->sectors*dp->t->heads);
dp->tsec = (lsec % dp->t->sectors) + 1;
dp->thead = (lsec/dp->t->sectors) % dp->t->heads;
}
int
gotfdintr(void *dp)
{
return ((Drive*)dp)->gotint;
}
void
floppyintr(void)
{
Controller *fdc = (Controller*)(FDCTLRL);
Drive *dp = &fl[fdc->dor&3];
print("floppy intr %d\n", dp-fl);
dp->cmd[0] = Sis;
dp->cmdlen = 1;
if(issue(dp) != Ok)
print("fd%d: Sis issue failed\n", dp-fl);
dp->ist = fdc->fifo;
dp->pcn = fdc->fifo;
print("ist %lux %lux\n", dp->ist, dp->pcn);
if(u) {
fl[0].gotint = 1;
wakeup(&fl[0].r);
}
}