home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
World_Of_Computer_Software-02-385-Vol-1of3.iso
/
d
/
dec92.zip
/
1012063A
< prev
next >
Wrap
Text File
|
1992-10-13
|
16KB
|
726 lines
/*
* mpu.c
* device driver for mpu-401 midi card
*/
#include "../h/param.h"
#include "../h/types.h"
#include "../h/dir.h"
#include "../h/signal.h"
#include "../h/page.h"
#include "../h/seg.h"
#include "../h/user.h"
#include "../h/file.h"
#include "../h/tty.h"
#include "../h/systm.h"
#include "../h/conf.h"
#include "../h/errno.h"
#include "mpu.h"
/* external support prototypes */
int inb(int);
void outb(int, int);
caddr_t cvttoaddr(faddr_t);
int fubyte(faddr_t);
void subyte(char *, int);
int copyin(faddr_t, char *, int);
void printcfg(char *, int, int, int, int, char *, ...);
void printf(char *, ...);
int getchar(void);
void putchar(int);
int sleep(caddr_t, int);
void wakeup(caddr_t);
int timeout(int (*)(), caddr_t, int);
void untimeout(int);
int spl6(void);
void splx(int);
int cpass(void);
int passc(int);
int getc(struct clist *);
int putc(int, struct clist *);
#define DRIVERID 0x00 /* driver version */
/* I/O addresses and vector */
/* if using IRQ 2, set VECTOR to 25 (030+VECTOR-1) */
#define VECTOR 25
#define BASE 0x220 /* I/O address of mpu card */
#define CMD (BASE+1) /* command output port */
#define STATUS (BASE+1) /* mpu status port */
#define DATA BASE /* data input port */
/* Status flags (neg logic) */
#define RR_F 0x40 /* mpu ready to receive */
#define DA_F 0x80 /* data available flag */
/* Test RR and DA flags */
#define RR() (!(INB(STATUS)&RR_F))
#define rr() (!(inb(STATUS)&RR_F))
#define DA() (!(INB(STATUS)&DA_F))
#define da() (!(inb(STATUS)&DA_F))
/* Device interrupt level */
#define SPLINT spl6
/* Busy-wait looping count */
#define SPIN 100000
#define ACK 0xFE /* mpu command acknowledge */
#define YES 1
#define NO 0
#define E_OK 0 /* ok return code */
#define E_TIMEOUT 1 /* timeout return code */
#define E_INTR 2 /* interrupt return code */
#define PRI (PZERO+1) /* sleep/wakeup priority */
static struct clist in_q; /* input queue */
static int busy = NO; /* driver busy flag */
static int exist = NO; /* mpu card found flag */
static int isopen = NO; /* exclusive use flag */
static int debug = 0; /* debugging level */
static int waitda = 0; /* result DA timer */
/* local prototypes */
static int reset(void);
static int waitRR(void);
static int mpucmd(int);
static int mpuinb(int);
static void mpuoutb(int, int);
#define OUTB(addr,byte) mpuoutb((addr),(byte))
#define INB(addr) mpuinb(addr)
/*-----------------------------------------------------
* mpuinit - determine if device exists at BASE address
*/
void mpuinit()
{
int i, ver = 0, rev = 0;
in_q.c_cc = 0; /* input q is empty */
/*
* see if it's there by trying to reset
*/
outb(CMD, MPU_RESET);
for (i = 0; i < SPIN; ++i)
if (da())
break;
if (i == SPIN) {
outb(CMD, MPU_RESET);
for (i = 0; i < SPIN; ++i)
if (da())
break;
if (i == SPIN)
goto NOTFOUND;
}
if (inb(DATA) != ACK)
goto NOTFOUND;
/*
* get firmware version
*/
for (i = 0; i < SPIN; ++i) /* wait for RR */
if (rr())
break;
if (i == SPIN) /* timed out */
goto NOTFOUND;
outb(CMD, MPU_VERSION); /* send request */
for (i = 0; i < SPIN; ++i) /* wait for ack */
if (da() && inb(DATA) == ACK)
break;
if (i == SPIN) /* timed out */
goto NOTFOUND;
for (i = 0; i < SPIN; ++i) /* wait for data */
if (da()) {
ver = inb(DATA); /* read version */
break;
}
if (i == SPIN) /* timed out */
goto NOTFOUND;
/*
* get firmware revision
*/
for (i = 0; i < SPIN; ++i) /* wait for RR */
if (rr())
break;
if (i == SPIN) /* timed out */
goto NOTFOUND;
outb(CMD, MPU_REVISION); /* send request */
for (i = 0; i < SPIN; ++i) /* wait for ACK */
if (da() && inb(DATA) == ACK)
break;
if (i == SPIN) /* timed out */
goto NOTFOUND;
for (i = 0; i < SPIN; ++i) /* wait for data */
if (da()) {
rev = inb(DATA); /* read revision */
break;
}
printcfg("mpu", BASE, 1, VECTOR, -1,
"ver=%d rev=%d did=%d", ver, rev, DRIVERID);
exist = YES;
return;
NOTFOUND:
printf("MPU not found at %x\n", BASE);
}
/*-----------------------------------------------------
* mpuopen - check device availability and
* ensure exclusive access
*/
void mpuopen(dev, flag, id)
int dev, flag, id;
{
if (debug)
printf("open: dev=%x flag=%x id=%x\n", dev, flag,
id);
if (!exist) {
u.u_error = ENXIO;
if (debug)
printf("open: device doesn't exist\n");
return;
}
if (isopen) {
u.u_error = EBUSY;
if (debug)
printf("open: failed EBUSY\n");
return;
}
isopen = YES;
if (reset() != E_OK) {
u.u_error = EIO;
if (debug)
printf("open: failed can't reset mpu\n");
isopen = NO;
}
}
/*-----------------------------------------------------
* mpuclose - reset mpu and give up exclusive access
*/
void mpuclose(dev, flag)
int dev, flag;
{
if (debug)
printf("close: dev=%x flag=%x\n", dev, flag);
(void) reset();
while (getc(&in_q) != -1) /* eat in_q */
;
busy = isopen = NO;
}
/*-----------------------------------------------------
* mpuread - read data from mpu
*/
void mpuread(dev)
int dev;
{
int byte, oldpri;
if (debug)
printf("read: dev=%x u.u_count=%d\n", dev,
u.u_count);
/* wait till it's ok to enter */
while (busy)
sleep((caddr_t) &busy, PRI);
busy = YES;
/* first get bytes from input q */
while (u.u_count) {
byte = getc(&in_q);
if (byte == -1)
break; /* used up input q */
if (passc(byte) == -1) {
busy = NO;
wakeup((caddr_t) &busy);
return; /* satisfied request */
}
}
/* now get straight from device */
while (u.u_count) {
oldpri = SPLINT();
while (!DA())
if (sleep((caddr_t) mpuread, PRI|PCATCH) != 0) {
splx(oldpri);
u.u_error = EINTR;
if (debug)
printf("read: caught software interrupt\n");
busy = NO;
wakeup((caddr_t) &busy);
return;
}
splx(oldpri);
if (passc(INB(DATA)) == -1)
break; /* satisfied request */
}
busy = NO;
wakeup((caddr_t) &busy);
}
/*-----------------------------------------------------
* mpuwrite - write data to mpu
*/
void mpuwrite(dev)
int dev;
{
int ret;
if (debug)
printf("write: dev=%x u.u_count=%d\n", dev,
u.u_count);
/* wait till it's ok to enter */
while (busy)
sleep((caddr_t) &busy, PRI);
busy = YES;
while (u.u_count) {
ret = waitRR();
if (ret == E_TIMEOUT) {
if (debug)
printf("write: waitRR timed out\n");
u.u_error = EIO;
break;
} else if (ret == E_INTR) {
if (debug)
printf("write: waitRR caught software int\n");
u.u_error = EINTR;
break;
} else
OUTB(DATA, cpass());
}
busy = NO;
wakeup((caddr_t) &busy);
}
/*-----------------------------------------------------
* mpuioctl
*/
void mpuioctl(dev, cmd, arg, mode)
int dev, cmd;
faddr_t arg;
int mode;
{
struct mpustuff m;
int byte, ret, got_intr = NO, oldpri, timeouts;
if (debug)
printf("ioctl: dev=%x cmd=%x mode=%x ", dev, cmd,
mode);
/* convert ptr on 286 callers */
if (!IS386())
arg = (faddr_t) cvttoaddr(arg);
/* make local copy of mpustuff in m */
if (copyin(arg, (char *) &m, sizeof(struct mpustuff))
== -1) {
u.u_error = EFAULT;
if (debug)
printf("\nioctl: copyin error EFAULT\n");
return;
}
if (!IS386()) {
m.opbuf = (char *) cvttoaddr(m.opbuf);
m.resbuf = (char *) cvttoaddr(m.resbuf);
}
if (debug)
printf("opsize=%d ressize=%d\n", m.opsize,
m.ressize);
/* handle driver commands */
if (cmd > MPU_RESET) {
switch (cmd) {
case MPU_DRIVERID:
if (m.ressize != 1) {
u.u_error = EINVAL;
return;
}
subyte(m.resbuf, DRIVERID);
return;
case MPU_SETDEBUG:
if (m.opsize != 1) {
u.u_error = EINVAL;
return;
}
byte = fubyte(m.opbuf);
if (byte < 0 || byte > 3) {
u.u_error = EINVAL;
return;
}
debug = byte;
return;
case MPU_GETDEBUG:
if (m.ressize != 1) {
u.u_error = EINVAL;
return;
}
subyte(m.resbuf, debug);
return;
default:
u.u_error = EINVAL;
return;
}
}
got_intr = NO;
/* wait till it's ok to enter */
while (busy)
sleep((caddr_t) &busy, PRI);
busy = YES;
/* handle reset command specially */
if (cmd == MPU_RESET)
ret = reset();
else
ret = mpucmd(cmd);
if (ret == E_TIMEOUT) {
if (debug)
printf("ioctl: reset() or mpucmd() timed out\n");
u.u_error = EIO;
goto done;
} else if (ret == E_INTR) {
if (debug)
printf(
"ioctl: reset() or mpucmd() caught software int\n");
u.u_error = EINTR;
goto done;
}
/* output parameters */
timeouts = 0;
while (m.opsize > 0) {
ret = waitRR();
if (ret == E_TIMEOUT) {
if (debug)
printf("ioctl: waitRR timed out\n");
if (++timeouts > 2) {
u.u_error = EIO;
busy = NO;
wakeup((caddr_t) &busy);
return;
}
} else if (ret == E_INTR) {
if (debug)
printf("ioctl: waitRR caught software int\n");
got_intr = YES;
} else {
OUTB(DATA, fubyte(m.opbuf++));
--m.opsize;
}
}
/* retrieve result bytes */
if (m.ressize > 0) {
oldpri = SPLINT();
while (m.ressize > 0) {
while (!DA()) {
/* wait no longer than */
/* 2 clock ticks */
waitda = 2;
if (sleep((caddr_t)mpuread, PRI|PCATCH) != 0) {
if (debug)
printf(
"ioctl: DA sleep caught software int\n");
got_intr = YES;
}
if (waitda <= 0) {
/* woke up because */
/* of timeout */
if (debug)
printf(
"ioctl: timed out awaiting cmd result\n");
u.u_error = EINVAL;
break;
}
waitda = 0; /* cancel alarm */
}
subyte(m.resbuf++, INB(DATA));
--m.ressize;
}
splx(oldpri);
}
done:
busy = NO;
wakeup((caddr_t) &busy);
if (got_intr)
u.u_error = EINTR;
return;
}
/*-----------------------------------------------------
* mpuintr - interrupt routine; just wakes up reader
*/
void mpuintr(vector)
int vector;
{
if (debug)
printf("intr: vec=%d\n", vector);
wakeup((caddr_t) mpuread);
}
/*-----------------------------------------------------
* mpuhalt - reset mpu before shutdown
*/
void mpuhalt()
{
if (debug)
printf("mpuhalt\n");
if (exist)
(void) reset();
}
/*-----------------------------------------------------
* waitRR - wait for RR or software interrupt
*
* sets up conditions to check RR bit on every clock
* tick and returns on mpu ready to receive, timed out
* or software interrupt
*
* returns:
* 0 - ok, mpu is ready to receive
* 1 - timed out
* 2 - got software interrupt
*/
static int polling = 0;
static int waitRR()
{
int oldpri, retval;
if (debug)
printf("waitRR\n");
oldpri = SPLINT();
polling = HZ * 5;
while (1) {
if (RR()) {
retval = E_OK; /* mpu RR */
break;
}
if (polling <= 0) {
retval = E_TIMEOUT; /* timed out */
break;
}
if (sleep((caddr_t) &polling, PRI|PCATCH) == 1) {
retval = E_INTR; /* software intr */
break;
}
}
polling = 0;
splx(oldpri);
return retval;
}
int mpupoll()
{
if (waitda > 0)
if (--waitda <= 0)
wakeup((caddr_t) mpuread);
if (polling > 0) {
if (--polling <= 0 || RR()) {
if (debug)
if (polling == 0)
printf("mpupoll: expired\n");
else
printf("mpupoll: rr\n");
polling = 0;
wakeup((caddr_t) &polling);
}
}
return 0;
}
/*-----------------------------------------------------
* mpucmd - generic command outputter
*
* outputs command and waits for acknowledge; up to
* caller to output parameters and retrieve results
*
* returns
* 0 - ok, command output
* 1 - timed out
* 2 - interrupt
*
* When mpucmd returns 2, it is ok to assume the mpu is
* still in sync because mpucmd waits the full 1 second
* for ACK even if it receives a software interrupt.
* If mpucmd received ACK and got an interrupt, the mpu
* is in sync. If mpucmd had to wait and never got
* ACK, assum the mpu wouldn't have sent one anyway
* (perhaps it was in uart mode and the command was
* reset).
*
* Another possibility is that the interrupt occurred
* while waiting for the mpu to become ready to receive
* a command. In this case, the mpu is still in sync,
* but the command wasn't sent.
*
* When mpucmd returns 1, that means mpucmd waited a
* full 1 second for ACK or ready to receive and didn't
* get it. OK to assume mpu still in sync, but command
* wasn't necessarily sent.
*/
static int timeup;
static void mpualarm()
{
if (debug)
printf("mpualarm\n");
timeup = YES;
wakeup((caddr_t) mpuread);
}
static int mpucmd(cmd)
int cmd;
{
int ret, id, byte, got_ack, got_intr;
if (debug)
printf("mpucmd: cmd=%x\n", cmd);
/* wait for mpu to be ready to receive cmd */
if ((ret = waitRR()) != E_OK)
return ret;
/* send command */
OUTB(CMD, cmd);
/* wait up to 1 sec for acknowledge */
got_ack = NO;
got_intr = NO;
timeup = NO;
id = timeout(mpualarm, (caddr_t) 0, HZ);
while (!timeup) {
while (!DA() && !timeup)
if (sleep((caddr_t) mpuread, PRI|PCATCH))
got_intr = YES;
if (DA()) {
byte = INB(DATA);
if (byte == ACK) {
got_ack = YES;
break;
} else {
while (putc(byte, &in_q) == -1) {
/* wait a while if */
/* clist shortage */
if (sleep((caddr_t)&lbolt, PRI|PCATCH) == -1)
got_intr = YES;
}
}
}
}
if (!timeup)
untimeout(id);
if (got_intr)
ret = E_INTR;
else if (got_ack)
ret = E_OK;
else
ret = E_TIMEOUT;
return ret;
}
/*-----------------------------------------------------
* reset - special protocol for reset
*
* returns
* 0 - ok
* 1 - timed out
* 2 - software interrupt
*/
static int reset()
{
int ret;
if (debug)
printf("reset\n");
ret = mpucmd(MPU_RESET);
if (ret == E_OK || ret == E_INTR)
return ret;
/* timed out for some reason; zap it */
OUTB(CMD, MPU_RESET);
while (DA()) /* and eat residue */
INB(DATA);
ret = mpucmd(MPU_RESET); /* now try proper reset*/
while (getc(&in_q) != -1)
; /* eat input q */
return ret;
}
/*-----------------------------------------------------
* INB and OUTB - debug versions of inb() and outb()
*/
static void mpuoutb(addr, byte)
int addr, byte;
{
if (debug > 1)
printf("OUTB(%x,%x)\n", addr, byte);
outb(addr, byte);
}
static int mpuinb(addr)
int addr;
{
int byte;
byte = inb(addr);
if (debug > 1)
printf("INB(%x) = %x\n", addr, byte);
return byte;
}
WRAP_EOF