home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Club Amiga de Montreal - CAM
/
CAM_CD_1.iso
/
files
/
354.lha
/
MSH_v1.5
/
src
/
device.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-03-12
|
19KB
|
809 lines
/*-
* $Id: device.c,v 1.5 90/01/27 20:34:43 Rhialto Exp $
* $Log: device.c,v $
* Revision 1.5 90/01/27 20:34:43 Rhialto
* Commented out #undef DEBUG
*
* Revision 1.4 90/01/23 00:24:50 Rhialto
* io_Error=0 for immediate commands
*
* Revision 1.3 89/12/17 21:29:37 Rhialto
* Revision 1.1 89/12/17 20:03:55 Rhialto
*
* DEVICE.C
*
* The messydisk.device code that makes it a real Exec .device.
* Mostly based on the 1.1 RKM example and Matt Dillon's library code.
*
* This code is (C) Copyright 1989 by Olaf Seibert. All rights reserved. May
* not be used or copied without a licence.
-*/
#include "dev.h"
#include "device.h"
/*#undef DEBUG /**/
#ifdef DEBUG
# define debug(x) dbprintf x
#else
# define debug(x)
#endif
/*
* The first executable location. This should return an error in case
* someone tried to run you as a program (instead of loading you as a
* device)
*/
/* INDENT OFF */
#asm
moveq.l #20,d0
rts
#endasm
/* INDENT ON */
/*
* A romtag structure. Both "exec" and "ramlib" look for this structure to
* discover magic constants about you (such as where to start running you
* from...).
*/
/* INDENT OFF */
#asm
public __H0_end
_EndCode equ __H0_end
public _RomTag
_RomTag:
dc.w $4AFC ; RTC_MATCHWORD
dc.l _RomTag ; rt_MatchTag
dc.l __H0_end ; rt_EndSkip
dc.b 0 ; rt_Flags (no RTF_AUTOINIT)
dc.b VERSION ; rt_Version
dc.b 3 ; rt_Type NT_DEVICE
dc.b RTPRI ; rt_Pri
dc.l _DevName ; rt_Name
dc.l _idString ; rt_IdString
dc.l _Init ; rt_Init
#endasm
/* INDENT ON */
char DevName[] = "messydisk.device";
char idString[] = "messydisk.device $Revision: 1.5 $ $Date: 90/01/27 20:34:43 $\r\n";
/*
* -30-6*X Library vectors:
*/
void (*LibVectors[]) () =
{
_DevOpen, _DevClose, _DevExpunge, _LibNull,
_DevBeginIO,
_DevAbortIO,
(void (*) ()) -1
};
/*
* Device commands:
*/
void (*funcTable[]) () = {
CMD_Invalid, CMD_Reset, CMD_Read, CMD_Write, CMD_Update, CMD_Clear,
CMD_Stop, CMD_Start, CMD_Flush, TD_Motor, TD_Seek, TD_Format,
TD_Remove, TD_Changenum, TD_Changestate, TD_Protstatus, TD_Rawread,
TD_Rawwrite, TD_Getdrivetype, TD_Getnumtracks, TD_Addchangeint,
TD_Remchangeint,
};
/*
* Here begin the system interface commands. When the user calls
* OpenDevice/CloseDevice/RemDevice, this eventually gets trahslated into
* a call to the following routines (Open/Close/Expunge). Exec has
* already put our device pointer in A6 for us. Exec has turned off task
* switching while in these routines (via Forbid/Permit), so we should not
* take too long in them.
*/
/* INDENT OFF */
#asm
public _Init
_Init: ;a0=segment list
movem.l D2-D3/A0/A6,-(sp)
jsr _CInit
movem.l (sp)+,D2-D3/A0/A6
rts
public __DevOpen
__DevOpen: ;d0=unitnum,d1=flags,a1=ioreq,a6=device
movem.l D0-D3/A1/A6,-(sp)
jsr _DevOpen
movem.l (sp)+,D0-D3/A1/A6
rts
public __DevClose
__DevClose: ;a1=ioreq,a6=device
movem.l D2-D3/A1/A6,-(sp)
jsr _DevClose
movem.l (sp)+,D2-D3/A1/A6
rts
public __DevExpunge
__DevExpunge: ;a6=device
movem.l D2-D3/A6,-(sp)
jsr _DevExpunge
movem.l (sp)+,D2-D3/A6
rts
public __LibNull
__LibNull:
clr.l d0
rts
public __DevBeginIO
__DevBeginIO: ;a1=ioreq,a6=device
movem.l D2-D3/A1/A6,-(sp)
jsr _DevBeginIO
movem.l (sp)+,D2-D3/A1/A6
rts
public __DevAbortIO
__DevAbortIO: ;a1=ioreq,a6=device
movem.l D2-D3/A1/A6,-(sp)
jsr _DevAbortIO
movem.l (sp)+,D2-D3/A1/A6
rts
#endasm
#ifdef HANDLE_IO_QUICK
#asm
;;;;
;
; C interface to the atomic set bit and test old value instruction.
;
; Called as BSET_ACTIVE(byte *address).
;
; Old value of the bit returned all over d0.w
_BSET_ACTIVE:
move.l 4(sp),a0
bset #0,(a0) ; UNITB_ACTIVE
sne d0
rts
#endasm
#endif
/* INDENT ON */
long SysBase; /* Argh! A global variable! */
/*
* The Initialization routine is given only a seglist pointer. Since we
* are NOT AUTOINIT we must construct and add the device ourselves and
* return either NULL or the device pointer. Exec has Forbid() for us
* during the call.
*
* If you have an extended device structure you must specify the size of the
* extended structure in MakeLibrary().
*/
DEV *
CInit(D2, D3, segment)
ulong D2,
D3;
long segment;
{
DEV *dev;
SysBase = *(long *) 4;
#ifdef DEBUG
dbinit();
#endif
dev = MakeLibrary(LibVectors, NULL, NULL, (long) sizeof (DEV), NULL);
if (DevInit(dev)) {
dev->dev_Node.ln_Type = NT_DEVICE;
dev->dev_Node.ln_Name = DevName;
dev->dev_Flags = LIBF_CHANGED | LIBF_SUMUSED;
dev->dev_Version = VERSION;
dev->dev_Revision = REVISION;
dev->dev_IdString = (APTR) idString;
dev->md_Seglist = segment;
AddDevice(dev);
return (dev);
}
FreeMem((char *) dev - dev->dev_NegSize, dev->dev_NegSize + dev->dev_PosSize);
return NULL;
}
/*
* Open is given the device pointer, unitno and flags. Either return the
* device pointer or NULL. Remove the DELAYED-EXPUNGE flag. Exec has
* Forbid() for us during the call.
*/
void
DevOpen(unitno, flags, D2, D3, ioreq, dev)
ulong unitno;
ulong flags;
ulong D2,
D3;
struct IOStdReq *ioreq;
DEV *dev;
{
UNIT *unit;
debug(("OpenDevice unit %ld, flags %lx\n", unitno, flags));
if (unitno >= MD_NUMUNITS)
goto error;
if ((unit = dev->md_Unit[unitno]) == NULL) {
if ((unit = UnitInit(dev, unitno)) == NULL)
goto error;
dev->md_Unit[unitno] = unit;
}
ioreq->io_Unit = (struct Unit *) unit;
++unit->mu_OpenCnt;
++dev->dev_OpenCnt;
dev->dev_Flags &= ~LIBF_DELEXP;
return;
error:
ioreq->io_Error = IOERR_OPENFAIL;
}
/*
* Close is given the device pointer and the io request. Be sure not to
* decrement the open count if already zero. If the open count is or
* becomes zero AND there is a LIBF_DELEXP, we expunge the device and
* return the seglist. Otherwise we return NULL.
*
* Note that this routine never sets LIBF_DELEXP on its own.
*
* Exec has Forbid() for us during the call.
*/
long
DevClose(D2, D3, ioreq, dev)
ulong D2,
D3;
struct IOStdReq *ioreq;
DEV *dev;
{
UNIT *unit;
unit = (UNIT *) ioreq->io_Unit;
debug(("CloseDevice io %08lx unit %08lx\n", ioreq, unit));
/*
* See if the unit is still in use. If not, close it down. This may
* need to do an update, which requires the ioreq.
*/
if (unit->mu_OpenCnt && --unit->mu_OpenCnt == 0) {
dev->md_Unit[unit->mu_UnitNr] = NULL;
UnitCloseDown(ioreq, dev, unit);
}
/*
* Make sure the ioreq is not used again.
*/
ioreq->io_Unit = (void *) -1;
ioreq->io_Device = (void *) -1;
if (dev->dev_OpenCnt && --dev->dev_OpenCnt)
return (NULL);
if (dev->dev_Flags & LIBF_DELEXP)
return (DevExpunge(D2, D3, dev));
return (NULL);
}
/*
* We expunge the device and return the Seglist ONLY if the open count is
* zero. If the open count is not zero we set the DELAYED-EXPUNGE
* flag and return NULL.
*
* Exec has Forbid() for us during the call. NOTE ALSO that Expunge might be
* called from the memory allocator and thus we CANNOT DO A Wait() or
* otherwise take a long time to complete (straight from RKM).
*
* Apparently RemLibrary(lib) calls our expunge routine and would therefore
* freeze if we called it ourselves. As far as I can tell from RKM,
* DevExpunge(lib) must remove the device itself as shown below.
*/
long
DevExpunge(D2, D3, dev)
ulong D2,
D3;
DEV *dev;
{
long Seglist;
if (dev->dev_OpenCnt) {
dev->dev_Flags |= LIBF_DELEXP;
return (NULL);
}
Remove(dev);
DevCloseDown(dev); /* Should be quick! */
#ifdef DEBUG
dbuninit();
#endif
Seglist = dev->md_Seglist;
FreeMem((char *) dev - dev->dev_NegSize,
(long) dev->dev_NegSize + dev->dev_PosSize);
return (Seglist);
}
/*
* BeginIO entry point. We don't handle any QUICK requests, we just send
* the request to the proper unit to handle.
*/
void
DevBeginIO(D2, D3, ioreq, dev)
ulong D2,
D3;
register struct IOStdReq *ioreq;
DEV *dev;
{
UNIT *unit;
/*
* Bookkeeping.
*/
unit = (UNIT *) ioreq->io_Unit;
debug(("BeginIO: io %08lx dev %08lx u %08lx\n", ioreq, dev, unit));
/*
* See if the io command is within range.
*/
if (STRIP(ioreq->io_Command) > TD_LASTCOMM)
goto NoCmd;
#ifdef HANDLE_IO_QUICK
Forbid(); /* Disable(); is a bit too strong for us. */
#endif
/*
* Process all immediate commands no matter what. Don't even require
* an exclusive lock on the unit.
*/
if (IMMEDIATE & (1L << STRIP(ioreq->io_Command)))
goto Immediate;
/*
* We don't handle any QUICK I/O since that only gives trouble with
* message ports and so. Other devices normally would include the code
* below.
*/
#ifdef HANDLE_IO_QUICK
/*
* See if the user does not request QUICK IO. If not, it is likely to
* be async and therefore we don't do it sync.
*/
if (!(ioreq->io_Flags & IOF_QUICK))
goto NoQuickRequested;
/*
* See if the unit is STOPPED. If so, queue the msg.
*/
if (unit->mu_Flags & UNITF_STOPPED)
goto QueueMsg;
/*
* This is not an immediate command. See if the device is busy. If
* not, process the action in this (the caller's) context.
*/
if (!BSET_ACTIVE(&unit->mu_Flags))
goto Immediate;
#endif
/*
* We need to queue the device. Clear the QUICK flag.
*/
QueueMsg:
ioreq->io_Flags &= ~IOF_QUICK;
NoQuickRequested:
#ifdef HANDLE_IO_QUICK
Permit(); /* Enable(); is a bit too strong for us. */
#endif
PutMsg(&unit->mu_Port, ioreq);
return;
Immediate:
#ifdef HANDLE_IO_QUICK
Permit(); /* Enable(); is a bit too strong for us. */
#endif
debug(("BeginIO: Immediate\n"));
ioreq->io_Error = TDERR_NoError;
PerformIO(ioreq, unit);
return;
NoCmd:
ioreq->io_Error = IOERR_NOCMD;
TermIO(ioreq);
return;
}
/*
* Terminate an io request. Called (normally) for every BeginIO. 'Funny'
* commands that don't call TermIO, or call it multiple times, may not be
* properly handled unless you are careful. TD_ADDCHANGEINT and
* TD_REMCHANGEINT are obvious examples.
*/
void
TermIO(ioreq)
register struct IOStdReq *ioreq;
{
register UNIT *unit;
unit = (UNIT *) ioreq->io_Unit;
debug(("TermIO: io %08lx u %08lx %ld %d\n", ioreq, unit,
ioreq->io_Actual, ioreq->io_Error));
#ifdef HANDLE_IO_QUICK
/*
* Since immediate commands don't even require an exclusive lock on
* the unit, don't unlock it.
*/
if (IMMEDIATE & (1L << STRIP(ioreq->io_Command)))
goto Immediate;
/*
* We may need to turn the active (lock) bit off, but not if we are
* within the task.
*/
if (unit->mu_Flags & UNITF_INTASK)
goto Immediate;
unit->mu_Flags &= ~UNITF_ACTIVE;
/*
* The task may have work to do that came in while we were processing
* in the caller's context.
*/
if (unit->mu_Flags & UNITF_WAKETASK) {
unit->mu_Flags &= ~UNITF_WAKETASK;
WakePort(&unit->mu_Port);
}
#endif
Immediate:
/*
* If the quick bit is still set then wen don't need to reply the msg
* -- just return to the user.
*/
if (!(ioreq->io_Flags & IOF_QUICK))
ReplyMsg(&ioreq->io_Message);
return;
}
/*
* AbortIO entry point. We don't abort IO here.
*/
long
DevAbortIO(D2, D3, ioreq, dev)
ulong D2,
D3;
DEV *dev;
struct IOStdReq *ioreq;
{
return 1;
}
void
WakePort(port)
register struct MsgPort *port;
{
Signal(port->mp_SigTask, 1L << port->mp_SigBit);
}
/*
* This is the main loop of the Unit tasks. It must be very careful with
* global data.
*/
void
UnitTask()
{
/* DEV *dev; */
UNIT *unit;
long waitmask;
struct IOExtTD *ioreq;
{
struct Task *task,
*FindTask();
task = FindTask(NULL);
unit = (UNIT *) task->tc_UserData;
/* dev = unit->mu_Dev; */
task->tc_UserData = NULL;
}
/*
* Now finish initializing the message ports and other signal things
*/
{
byte sigbit;
unit->mu_DiskReplyPort.mp_SigBit = AllocSignal(-1L);
unit->mu_DiskReplyPort.mp_Flags = PA_SIGNAL;
sigbit = AllocSignal(-1L);
unit->mu_Port.mp_SigBit = sigbit;
unit->mu_Port.mp_Flags = PA_SIGNAL;
waitmask = 1L << sigbit;
unit->mu_DmaSignal = AllocSignal(-1L);
}
for (;;) {
debug(("Task: Waiting... "));
Wait(waitmask);
/*
* See if we are stopped.
*/
if (unit->mu_Flags & UNITF_STOPPED)
continue;
#ifdef HANDLE_IO_QUICK
/*
* Lock the device. If it fails, we have set a flag such that the
* TermIO wakes us again.
*/
unit->mu_Flags |= UNITF_WAKETASK;
if (BSET_ACTIVE(&unit->mu_Flags))
continue;
unit->mu_Flags |= UNITF_INTASK;
#endif
while (ioreq = (struct IOExtTD *) GetMsg(&unit->mu_Port)) {
debug(("Task: io %08lx %x\n", ioreq, ioreq->iotd_Req.io_Command));
ioreq->iotd_Req.io_Error = 0;
PerformIO((&ioreq->iotd_Req), unit);
}
#ifdef HANDLE_IO_QUICK
unit->mu_Flags &= ~(UNITF_ACTIVE | UNITF_INTASK | UNITF_WAKETASK);
#endif
}
}
void
CMD_Invalid(ioreq, unit)
struct IOStdReq *ioreq;
UNIT *unit;
{
ioreq->io_Error = IOERR_NOCMD;
TermIO(ioreq);
}
void
CMD_Stop(ioreq, unit)
struct IOExtTD *ioreq;
UNIT *unit;
{
unit->mu_Flags |= UNITF_STOPPED;
TermIO(ioreq);
}
void
CMD_Start(ioreq, unit)
struct IOExtTD *ioreq;
UNIT *unit;
{
unit->mu_Flags &= ~UNITF_STOPPED;
WakePort(&unit->mu_Port);
TermIO(ioreq);
}
void
CMD_Flush(ioreq, unit)
struct IOExtTD *ioreq;
UNIT *unit;
{
register struct IOStdReq *req;
/* Flush our own command queue */
Forbid();
while (req = (struct IOStdReq *) GetMsg(unit->mu_Port)) {
req->io_Error = IOERR_ABORTED;
ReplyMsg(&req->io_Message);
}
Permit();
WakePort(&unit->mu_Port);
TermIO(ioreq);
}
void
TrackdiskGateway(ioreq, unit)
register struct IOExtTD *ioreq;
UNIT *unit;
{
register struct IOExtTD *tdioreq;
debug(("Trackdisk: %x ", ioreq->iotd_Req.io_Command));
tdioreq = unit->mu_DiskIOReq;
/*
* Clone almost the entire io request to relay to the
* trackdisk.device.
*/
tdioreq->iotd_Req.io_Command = ioreq->iotd_Req.io_Command;
tdioreq->iotd_Req.io_Flags = ioreq->iotd_Req.io_Flags | IOF_QUICK;
tdioreq->iotd_Req.io_Length = ioreq->iotd_Req.io_Length;
tdioreq->iotd_Req.io_Data = ioreq->iotd_Req.io_Data;
tdioreq->iotd_Req.io_Offset = ioreq->iotd_Req.io_Offset;
if (ioreq->iotd_Req.io_Command & TDF_EXTCOM) {
tdioreq->iotd_Count = ioreq->iotd_Count;
tdioreq->iotd_SecLabel = ioreq->iotd_SecLabel;
}
BeginIO(tdioreq);
WaitIO(tdioreq);
ioreq->iotd_Req.io_Error = tdioreq->iotd_Req.io_Error;
ioreq->iotd_Req.io_Actual = tdioreq->iotd_Req.io_Actual;
TermIO(ioreq);
}
#ifdef DEBUG
/* DEBUGGING */
struct MsgPort *Dbport; /* owned by the debug process */
struct MsgPort *Dback; /* owned by the DOS device driver */
short DBEnable;
struct SignalSemaphore PortUse;
#define CTOB(x) (void *)(((long)(x))>>2) /* BCPL conversion */
/*
* DEBUGGING CODE. You cannot make DOS library calls that access
* other devices from within a device driver because the caller may not be
* a process. If you need to make such calls you must create a port and
* construct the DOS messages yourself. I do not do this. To get
* debugging info out another PROCESS is created to which debugging
* messages can be sent. The replyport gets a new SigTask for every
* dbprintf call, therefore the semaphore.
*/
extern void debugproc();
struct Library *DOSBase,
*OpenLibrary();
dbinit()
{
struct Task *task = FindTask(NULL);
DOSBase = OpenLibrary("dos.library", 0L);
Dback = CreatePort("Dback", -1L);
FreeSignal((long) Dback->mp_SigBit);
Dback->mp_SigBit = 2;
InitSemaphore(&PortUse);
CreateProc("messydisk_DB", (long) TASKPRI + 1, CTOB(debugproc), 2000L);
WaitPort(Dback); /* handshake startup */
GetMsg(Dback); /* remove dummy msg */
DBEnable = 1;
dbprintf("Debugger running V1.11\n");
}
dbuninit()
{
struct Message killmsg;
if (Dbport) {
killmsg.mn_Length = 0; /* 0 means die */
ObtainSemaphore(&PortUse);
Dback->mp_SigTask = FindTask(NULL);
PutMsg(Dbport, &killmsg);
WaitPort(Dback); /* He's dead jim! */
GetMsg(Dback);
ReleaseSemaphore(&PortUse);
Dback->mp_SigBit = -1;
DeletePort(Dback);
/*
* Since the debug process is running at a greater priority, I am
* pretty sure that it is guarenteed to be completely removed
* before this task gets control again.
*/
}
CloseLibrary(DOSBase);
}
dbprintf(a, b, c, d, e, f, g, h, i, j)
long a, b, c, d, e, f, g, h, i, j;
{
struct {
struct Message msg;
char buf[256];
} msgbuf;
register struct Message *msg = &msgbuf.msg;
register long len;
if (Dbport && DBEnable) {
ObtainSemaphore(&PortUse); /* sprintf is not re-entrant */
sprintf(msgbuf.buf, a, b, c, d, e, f, g, h, i, j);
len = strlen(msgbuf.buf) + 1;
msg->mn_Length = len; /* Length NEVER 0 */
Dback->mp_SigTask = FindTask(NULL);
PutMsg(Dbport, msg);
WaitPort(Dback);
GetMsg(Dback);
ReleaseSemaphore(&PortUse);
}
}
/*
* BTW, the DOS library used by debugmain() was actually opened by the
* opener of the device driver.
*/
debugmain()
{
register struct Message *msg;
register long len;
register void *fh,
*Open();
void *fh2;
struct Message DummyMsg;
Dbport = CreatePort("Dbport", -1L);
fh = Open("CON:0/20/640/101/Device debug", MODE_NEWFILE);
fh2 = Open("PAR:", MODE_OLDFILE);
PutMsg(Dback, &DummyMsg);
for (;;) {
WaitPort(Dbport);
msg = GetMsg(Dbport);
len = msg->mn_Length;
if (len == 0)
break;
--len; /* Fix length up */
if (DBEnable & 1)
Write(fh, msg + 1, len);
if (DBEnable & 2)
Write(fh2, msg + 1, len);
PutMsg(Dback, msg);
}
Close(fh);
Close(fh2);
DeletePort(Dbport);
PutMsg(Dback, msg); /* Kill handshake */
}
/*
* The assembly tag for the DOS process: CNOP causes alignment problems
* with the Aztec assembler for some reason. I assume then, that the
* alignment is unknown. Since the BCPL conversion basically zero's the
* lower two bits of the address the actual code may start anywhere within
* 8 bytes of address (remember the first longword is a segment pointer
* and skipped). Sigh.... (see CreateProc() above).
*/
/* INDENT OFF */
#asm
public _debugproc
public _debugmain
cseg
_debugproc:
nop
nop
nop
nop
nop
movem.l D2-D7/A2-A6,-(sp)
jsr _debugmain
movem.l (sp)+,D2-D7/A2-A6
rts
#endasm
#endif /* DEBUG */