Amiga-Request@cs.odu.edu (Amiga Sources/Binaries Moderator) (02/21/90)
Submitted-by: Olaf 'Rhialto' Seibert <U211344%HNYKUN11.BITNET@CUNYVM.CUNY.EDU> Posting-number: Volume 90, Issue 081 Archive-name: devices/msh-1.5/part03 #!/bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 3 (of 6)." # Contents: src/device.c src/hansec.c # Wrapped by tadguy@xanth on Tue Feb 20 20:57:10 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'src/device.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'src/device.c'\" else echo shar: Extracting \"'src/device.c'\" \(18725 characters\) sed "s/^X//" >'src/device.c' <<'END_OF_FILE' X/*- X * $Id: device.c,v 1.5 90/01/27 20:34:43 Rhialto Exp $ X * $Log: device.c,v $ X * Revision 1.5 90/01/27 20:34:43 Rhialto X * Commented out #undef DEBUG X * X * Revision 1.4 90/01/23 00:24:50 Rhialto X * io_Error=0 for immediate commands X * X * Revision 1.3 89/12/17 21:29:37 Rhialto X * Revision 1.1 89/12/17 20:03:55 Rhialto X * X * DEVICE.C X * X * The messydisk.device code that makes it a real Exec .device. X * Mostly based on the 1.1 RKM example and Matt Dillon's library code. X * X * This code is (C) Copyright 1989 by Olaf Seibert. All rights reserved. May X * not be used or copied without a licence. X-*/ X X#include "dev.h" X#include "device.h" X X/*#undef DEBUG /**/ X#ifdef DEBUG X# define debug(x) dbprintf x X#else X# define debug(x) X#endif X X/* X * The first executable location. This should return an error in case X * someone tried to run you as a program (instead of loading you as a X * device) X */ X/* INDENT OFF */ X#asm X moveq.l #20,d0 X rts X#endasm X/* INDENT ON */ X X/* X * A romtag structure. Both "exec" and "ramlib" look for this structure to X * discover magic constants about you (such as where to start running you X * from...). X */ X/* INDENT OFF */ X#asm X public __H0_end X_EndCode equ __H0_end X public _RomTag X_RomTag: X dc.w $4AFC ; RTC_MATCHWORD X dc.l _RomTag ; rt_MatchTag X dc.l __H0_end ; rt_EndSkip X dc.b 0 ; rt_Flags (no RTF_AUTOINIT) X dc.b VERSION ; rt_Version X dc.b 3 ; rt_Type NT_DEVICE X dc.b RTPRI ; rt_Pri X dc.l _DevName ; rt_Name X dc.l _idString ; rt_IdString X dc.l _Init ; rt_Init X#endasm X/* INDENT ON */ X Xchar DevName[] = "messydisk.device"; Xchar idString[] = "messydisk.device $Revision: 1.5 $ $Date: 90/01/27 20:34:43 $\r\n"; X X/* X * -30-6*X Library vectors: X */ X Xvoid (*LibVectors[]) () = X{ X _DevOpen, _DevClose, _DevExpunge, _LibNull, X X _DevBeginIO, X _DevAbortIO, X (void (*) ()) -1 X}; X X/* X * Device commands: X */ X Xvoid (*funcTable[]) () = { X CMD_Invalid, CMD_Reset, CMD_Read, CMD_Write, CMD_Update, CMD_Clear, X CMD_Stop, CMD_Start, CMD_Flush, TD_Motor, TD_Seek, TD_Format, X TD_Remove, TD_Changenum, TD_Changestate, TD_Protstatus, TD_Rawread, X TD_Rawwrite, TD_Getdrivetype, TD_Getnumtracks, TD_Addchangeint, X TD_Remchangeint, X}; X X/* X * Here begin the system interface commands. When the user calls X * OpenDevice/CloseDevice/RemDevice, this eventually gets trahslated into X * a call to the following routines (Open/Close/Expunge). Exec has X * already put our device pointer in A6 for us. Exec has turned off task X * switching while in these routines (via Forbid/Permit), so we should not X * take too long in them. X */ X/* INDENT OFF */ X#asm X public _Init X_Init: ;a0=segment list X movem.l D2-D3/A0/A6,-(sp) X jsr _CInit X movem.l (sp)+,D2-D3/A0/A6 X rts X X public __DevOpen X__DevOpen: ;d0=unitnum,d1=flags,a1=ioreq,a6=device X movem.l D0-D3/A1/A6,-(sp) X jsr _DevOpen X movem.l (sp)+,D0-D3/A1/A6 X rts X X public __DevClose X__DevClose: ;a1=ioreq,a6=device X movem.l D2-D3/A1/A6,-(sp) X jsr _DevClose X movem.l (sp)+,D2-D3/A1/A6 X rts X X public __DevExpunge X__DevExpunge: ;a6=device X movem.l D2-D3/A6,-(sp) X jsr _DevExpunge X movem.l (sp)+,D2-D3/A6 X rts X X public __LibNull X__LibNull: X clr.l d0 X rts X X public __DevBeginIO X__DevBeginIO: ;a1=ioreq,a6=device X movem.l D2-D3/A1/A6,-(sp) X jsr _DevBeginIO X movem.l (sp)+,D2-D3/A1/A6 X rts X X public __DevAbortIO X__DevAbortIO: ;a1=ioreq,a6=device X movem.l D2-D3/A1/A6,-(sp) X jsr _DevAbortIO X movem.l (sp)+,D2-D3/A1/A6 X rts X#endasm X X#ifdef HANDLE_IO_QUICK X#asm X;;;; X; X; C interface to the atomic set bit and test old value instruction. X; X; Called as BSET_ACTIVE(byte *address). X; X; Old value of the bit returned all over d0.w X X_BSET_ACTIVE: X move.l 4(sp),a0 X bset #0,(a0) ; UNITB_ACTIVE X sne d0 X rts X X#endasm X#endif X/* INDENT ON */ X Xlong SysBase; /* Argh! A global variable! */ X X/* X * The Initialization routine is given only a seglist pointer. Since we X * are NOT AUTOINIT we must construct and add the device ourselves and X * return either NULL or the device pointer. Exec has Forbid() for us X * during the call. X * X * If you have an extended device structure you must specify the size of the X * extended structure in MakeLibrary(). X */ X XDEV * XCInit(D2, D3, segment) Xulong D2, X D3; Xlong segment; X{ X DEV *dev; X X SysBase = *(long *) 4; X#ifdef DEBUG X dbinit(); X#endif X dev = MakeLibrary(LibVectors, NULL, NULL, (long) sizeof (DEV), NULL); X if (DevInit(dev)) { X dev->dev_Node.ln_Type = NT_DEVICE; X dev->dev_Node.ln_Name = DevName; X dev->dev_Flags = LIBF_CHANGED | LIBF_SUMUSED; X dev->dev_Version = VERSION; X dev->dev_Revision = REVISION; X dev->dev_IdString = (APTR) idString; X dev->md_Seglist = segment; X AddDevice(dev); X return (dev); X } X FreeMem((char *) dev - dev->dev_NegSize, dev->dev_NegSize + dev->dev_PosSize); X return NULL; X} X X/* X * Open is given the device pointer, unitno and flags. Either return the X * device pointer or NULL. Remove the DELAYED-EXPUNGE flag. Exec has X * Forbid() for us during the call. X */ X Xvoid XDevOpen(unitno, flags, D2, D3, ioreq, dev) Xulong unitno; Xulong flags; Xulong D2, X D3; Xstruct IOStdReq *ioreq; XDEV *dev; X{ X UNIT *unit; X X debug(("OpenDevice unit %ld, flags %lx\n", unitno, flags)); X if (unitno >= MD_NUMUNITS) X goto error; X X if ((unit = dev->md_Unit[unitno]) == NULL) { X if ((unit = UnitInit(dev, unitno)) == NULL) X goto error; X dev->md_Unit[unitno] = unit; X } X ioreq->io_Unit = (struct Unit *) unit; X X ++unit->mu_OpenCnt; X ++dev->dev_OpenCnt; X dev->dev_Flags &= ~LIBF_DELEXP; X X return; X Xerror: X ioreq->io_Error = IOERR_OPENFAIL; X} X X/* X * Close is given the device pointer and the io request. Be sure not to X * decrement the open count if already zero. If the open count is or X * becomes zero AND there is a LIBF_DELEXP, we expunge the device and X * return the seglist. Otherwise we return NULL. X * X * Note that this routine never sets LIBF_DELEXP on its own. X * X * Exec has Forbid() for us during the call. X */ X Xlong XDevClose(D2, D3, ioreq, dev) Xulong D2, X D3; Xstruct IOStdReq *ioreq; XDEV *dev; X{ X UNIT *unit; X X unit = (UNIT *) ioreq->io_Unit; X debug(("CloseDevice io %08lx unit %08lx\n", ioreq, unit)); X X /* X * See if the unit is still in use. If not, close it down. This may X * need to do an update, which requires the ioreq. X */ X X if (unit->mu_OpenCnt && --unit->mu_OpenCnt == 0) { X dev->md_Unit[unit->mu_UnitNr] = NULL; X UnitCloseDown(ioreq, dev, unit); X } X /* X * Make sure the ioreq is not used again. X */ X ioreq->io_Unit = (void *) -1; X ioreq->io_Device = (void *) -1; X X if (dev->dev_OpenCnt && --dev->dev_OpenCnt) X return (NULL); X if (dev->dev_Flags & LIBF_DELEXP) X return (DevExpunge(D2, D3, dev)); X return (NULL); X} X X/* X * We expunge the device and return the Seglist ONLY if the open count is X * zero. If the open count is not zero we set the DELAYED-EXPUNGE X * flag and return NULL. X * X * Exec has Forbid() for us during the call. NOTE ALSO that Expunge might be X * called from the memory allocator and thus we CANNOT DO A Wait() or X * otherwise take a long time to complete (straight from RKM). X * X * Apparently RemLibrary(lib) calls our expunge routine and would therefore X * freeze if we called it ourselves. As far as I can tell from RKM, X * DevExpunge(lib) must remove the device itself as shown below. X */ X Xlong XDevExpunge(D2, D3, dev) Xulong D2, X D3; XDEV *dev; X{ X long Seglist; X X if (dev->dev_OpenCnt) { X dev->dev_Flags |= LIBF_DELEXP; X return (NULL); X } X Remove(dev); X DevCloseDown(dev); /* Should be quick! */ X#ifdef DEBUG X dbuninit(); X#endif X Seglist = dev->md_Seglist; X FreeMem((char *) dev - dev->dev_NegSize, X (long) dev->dev_NegSize + dev->dev_PosSize); X return (Seglist); X} X X/* X * BeginIO entry point. We don't handle any QUICK requests, we just send X * the request to the proper unit to handle. X */ X Xvoid XDevBeginIO(D2, D3, ioreq, dev) Xulong D2, X D3; Xregister struct IOStdReq *ioreq; XDEV *dev; X{ X UNIT *unit; X X /* X * Bookkeeping. X */ X unit = (UNIT *) ioreq->io_Unit; X debug(("BeginIO: io %08lx dev %08lx u %08lx\n", ioreq, dev, unit)); X X /* X * See if the io command is within range. X */ X if (STRIP(ioreq->io_Command) > TD_LASTCOMM) X goto NoCmd; X X#ifdef HANDLE_IO_QUICK X Forbid(); /* Disable(); is a bit too strong for us. */ X#endif X X /* X * Process all immediate commands no matter what. Don't even require X * an exclusive lock on the unit. X */ X if (IMMEDIATE & (1L << STRIP(ioreq->io_Command))) X goto Immediate; X X /* X * We don't handle any QUICK I/O since that only gives trouble with X * message ports and so. Other devices normally would include the code X * below. X */ X#ifdef HANDLE_IO_QUICK X /* X * See if the user does not request QUICK IO. If not, it is likely to X * be async and therefore we don't do it sync. X */ X if (!(ioreq->io_Flags & IOF_QUICK)) X goto NoQuickRequested; X X /* X * See if the unit is STOPPED. If so, queue the msg. X */ X if (unit->mu_Flags & UNITF_STOPPED) X goto QueueMsg; X X /* X * This is not an immediate command. See if the device is busy. If X * not, process the action in this (the caller's) context. X */ X if (!BSET_ACTIVE(&unit->mu_Flags)) X goto Immediate; X#endif X X /* X * We need to queue the device. Clear the QUICK flag. X */ XQueueMsg: X ioreq->io_Flags &= ~IOF_QUICK; XNoQuickRequested: X#ifdef HANDLE_IO_QUICK X Permit(); /* Enable(); is a bit too strong for us. */ X#endif X PutMsg(&unit->mu_Port, ioreq); X X return; X XImmediate: X#ifdef HANDLE_IO_QUICK X Permit(); /* Enable(); is a bit too strong for us. */ X#endif X debug(("BeginIO: Immediate\n")); X ioreq->io_Error = TDERR_NoError; X PerformIO(ioreq, unit); X return; X XNoCmd: X ioreq->io_Error = IOERR_NOCMD; X TermIO(ioreq); X return; X X} X X/* X * Terminate an io request. Called (normally) for every BeginIO. 'Funny' X * commands that don't call TermIO, or call it multiple times, may not be X * properly handled unless you are careful. TD_ADDCHANGEINT and X * TD_REMCHANGEINT are obvious examples. X */ X Xvoid XTermIO(ioreq) Xregister struct IOStdReq *ioreq; X{ X register UNIT *unit; X X unit = (UNIT *) ioreq->io_Unit; X debug(("TermIO: io %08lx u %08lx %ld %d\n", ioreq, unit, X ioreq->io_Actual, ioreq->io_Error)); X X#ifdef HANDLE_IO_QUICK X /* X * Since immediate commands don't even require an exclusive lock on X * the unit, don't unlock it. X */ X if (IMMEDIATE & (1L << STRIP(ioreq->io_Command))) X goto Immediate; X X /* X * We may need to turn the active (lock) bit off, but not if we are X * within the task. X */ X if (unit->mu_Flags & UNITF_INTASK) X goto Immediate; X X unit->mu_Flags &= ~UNITF_ACTIVE; X X /* X * The task may have work to do that came in while we were processing X * in the caller's context. X */ X if (unit->mu_Flags & UNITF_WAKETASK) { X unit->mu_Flags &= ~UNITF_WAKETASK; X WakePort(&unit->mu_Port); X } X#endif X XImmediate: X /* X * If the quick bit is still set then wen don't need to reply the msg X * -- just return to the user. X */ X X if (!(ioreq->io_Flags & IOF_QUICK)) X ReplyMsg(&ioreq->io_Message); X X return; X} X X/* X * AbortIO entry point. We don't abort IO here. X */ X Xlong XDevAbortIO(D2, D3, ioreq, dev) Xulong D2, X D3; XDEV *dev; Xstruct IOStdReq *ioreq; X{ X return 1; X} X Xvoid XWakePort(port) Xregister struct MsgPort *port; X{ X Signal(port->mp_SigTask, 1L << port->mp_SigBit); X} X X/* X * This is the main loop of the Unit tasks. It must be very careful with X * global data. X */ X Xvoid XUnitTask() X{ X /* DEV *dev; */ X UNIT *unit; X long waitmask; X struct IOExtTD *ioreq; X X { X struct Task *task, X *FindTask(); X X task = FindTask(NULL); X unit = (UNIT *) task->tc_UserData; X /* dev = unit->mu_Dev; */ X task->tc_UserData = NULL; X } X X /* X * Now finish initializing the message ports and other signal things X */ X X { X byte sigbit; X X unit->mu_DiskReplyPort.mp_SigBit = AllocSignal(-1L); X unit->mu_DiskReplyPort.mp_Flags = PA_SIGNAL; X X sigbit = AllocSignal(-1L); X unit->mu_Port.mp_SigBit = sigbit; X unit->mu_Port.mp_Flags = PA_SIGNAL; X waitmask = 1L << sigbit; X X unit->mu_DmaSignal = AllocSignal(-1L); X } X X for (;;) { X debug(("Task: Waiting... ")); X Wait(waitmask); X X /* X * See if we are stopped. X */ X if (unit->mu_Flags & UNITF_STOPPED) X continue; X X#ifdef HANDLE_IO_QUICK X /* X * Lock the device. If it fails, we have set a flag such that the X * TermIO wakes us again. X */ X unit->mu_Flags |= UNITF_WAKETASK; X if (BSET_ACTIVE(&unit->mu_Flags)) X continue; X X unit->mu_Flags |= UNITF_INTASK; X#endif X X while (ioreq = (struct IOExtTD *) GetMsg(&unit->mu_Port)) { X debug(("Task: io %08lx %x\n", ioreq, ioreq->iotd_Req.io_Command)); X ioreq->iotd_Req.io_Error = 0; X PerformIO((&ioreq->iotd_Req), unit); X } X X#ifdef HANDLE_IO_QUICK X unit->mu_Flags &= ~(UNITF_ACTIVE | UNITF_INTASK | UNITF_WAKETASK); X#endif X } X} X Xvoid XCMD_Invalid(ioreq, unit) Xstruct IOStdReq *ioreq; XUNIT *unit; X{ X ioreq->io_Error = IOERR_NOCMD; X TermIO(ioreq); X} X Xvoid XCMD_Stop(ioreq, unit) Xstruct IOExtTD *ioreq; XUNIT *unit; X{ X unit->mu_Flags |= UNITF_STOPPED; X TermIO(ioreq); X} X Xvoid XCMD_Start(ioreq, unit) Xstruct IOExtTD *ioreq; XUNIT *unit; X{ X unit->mu_Flags &= ~UNITF_STOPPED; X WakePort(&unit->mu_Port); X TermIO(ioreq); X} X Xvoid XCMD_Flush(ioreq, unit) Xstruct IOExtTD *ioreq; XUNIT *unit; X{ X register struct IOStdReq *req; X X /* Flush our own command queue */ X Forbid(); X while (req = (struct IOStdReq *) GetMsg(unit->mu_Port)) { X req->io_Error = IOERR_ABORTED; X ReplyMsg(&req->io_Message); X } X Permit(); X X WakePort(&unit->mu_Port); X TermIO(ioreq); X} X Xvoid XTrackdiskGateway(ioreq, unit) Xregister struct IOExtTD *ioreq; XUNIT *unit; X{ X register struct IOExtTD *tdioreq; X X debug(("Trackdisk: %x ", ioreq->iotd_Req.io_Command)); X tdioreq = unit->mu_DiskIOReq; X X /* X * Clone almost the entire io request to relay to the X * trackdisk.device. X */ X X tdioreq->iotd_Req.io_Command = ioreq->iotd_Req.io_Command; X tdioreq->iotd_Req.io_Flags = ioreq->iotd_Req.io_Flags | IOF_QUICK; X tdioreq->iotd_Req.io_Length = ioreq->iotd_Req.io_Length; X tdioreq->iotd_Req.io_Data = ioreq->iotd_Req.io_Data; X tdioreq->iotd_Req.io_Offset = ioreq->iotd_Req.io_Offset; X if (ioreq->iotd_Req.io_Command & TDF_EXTCOM) { X tdioreq->iotd_Count = ioreq->iotd_Count; X tdioreq->iotd_SecLabel = ioreq->iotd_SecLabel; X } X BeginIO(tdioreq); X WaitIO(tdioreq); X X ioreq->iotd_Req.io_Error = tdioreq->iotd_Req.io_Error; X ioreq->iotd_Req.io_Actual = tdioreq->iotd_Req.io_Actual; X X TermIO(ioreq); X} X X#ifdef DEBUG X/* DEBUGGING */ Xstruct MsgPort *Dbport; /* owned by the debug process */ Xstruct MsgPort *Dback; /* owned by the DOS device driver */ Xshort DBEnable; Xstruct SignalSemaphore PortUse; X X#define CTOB(x) (void *)(((long)(x))>>2) /* BCPL conversion */ X X/* X * DEBUGGING CODE. You cannot make DOS library calls that access X * other devices from within a device driver because the caller may not be X * a process. If you need to make such calls you must create a port and X * construct the DOS messages yourself. I do not do this. To get X * debugging info out another PROCESS is created to which debugging X * messages can be sent. The replyport gets a new SigTask for every X * dbprintf call, therefore the semaphore. X */ X Xextern void debugproc(); Xstruct Library *DOSBase, X *OpenLibrary(); X Xdbinit() X{ X struct Task *task = FindTask(NULL); X X DOSBase = OpenLibrary("dos.library", 0L); X Dback = CreatePort("Dback", -1L); X FreeSignal((long) Dback->mp_SigBit); X Dback->mp_SigBit = 2; X InitSemaphore(&PortUse); X CreateProc("messydisk_DB", (long) TASKPRI + 1, CTOB(debugproc), 2000L); X WaitPort(Dback); /* handshake startup */ X GetMsg(Dback); /* remove dummy msg */ X DBEnable = 1; X dbprintf("Debugger running V1.11\n"); X} X Xdbuninit() X{ X struct Message killmsg; X X if (Dbport) { X killmsg.mn_Length = 0; /* 0 means die */ X ObtainSemaphore(&PortUse); X Dback->mp_SigTask = FindTask(NULL); X PutMsg(Dbport, &killmsg); X WaitPort(Dback); /* He's dead jim! */ X GetMsg(Dback); X ReleaseSemaphore(&PortUse); X Dback->mp_SigBit = -1; X DeletePort(Dback); X X /* X * Since the debug process is running at a greater priority, I am X * pretty sure that it is guarenteed to be completely removed X * before this task gets control again. X */ X } X CloseLibrary(DOSBase); X} X Xdbprintf(a, b, c, d, e, f, g, h, i, j) Xlong a, b, c, d, e, f, g, h, i, j; X{ X struct { X struct Message msg; X char buf[256]; X } msgbuf; X register struct Message *msg = &msgbuf.msg; X register long len; X X if (Dbport && DBEnable) { X ObtainSemaphore(&PortUse); /* sprintf is not re-entrant */ X sprintf(msgbuf.buf, a, b, c, d, e, f, g, h, i, j); X len = strlen(msgbuf.buf) + 1; X msg->mn_Length = len; /* Length NEVER 0 */ X Dback->mp_SigTask = FindTask(NULL); X PutMsg(Dbport, msg); X WaitPort(Dback); X GetMsg(Dback); X ReleaseSemaphore(&PortUse); X } X} X X/* X * BTW, the DOS library used by debugmain() was actually opened by the X * opener of the device driver. X */ X Xdebugmain() X{ X register struct Message *msg; X register long len; X register void *fh, X *Open(); X void *fh2; X struct Message DummyMsg; X X Dbport = CreatePort("Dbport", -1L); X fh = Open("CON:0/20/640/101/Device debug", MODE_NEWFILE); X fh2 = Open("PAR:", MODE_OLDFILE); X PutMsg(Dback, &DummyMsg); X for (;;) { X WaitPort(Dbport); X msg = GetMsg(Dbport); X len = msg->mn_Length; X if (len == 0) X break; X --len; /* Fix length up */ X if (DBEnable & 1) X Write(fh, msg + 1, len); X if (DBEnable & 2) X Write(fh2, msg + 1, len); X PutMsg(Dback, msg); X } X Close(fh); X Close(fh2); X DeletePort(Dbport); X PutMsg(Dback, msg); /* Kill handshake */ X} X X/* X * The assembly tag for the DOS process: CNOP causes alignment problems X * with the Aztec assembler for some reason. I assume then, that the X * alignment is unknown. Since the BCPL conversion basically zero's the X * lower two bits of the address the actual code may start anywhere within X * 8 bytes of address (remember the first longword is a segment pointer X * and skipped). Sigh.... (see CreateProc() above). X */ X/* INDENT OFF */ X#asm X public _debugproc X public _debugmain X X cseg X_debugproc: X nop X nop X nop X nop X nop X movem.l D2-D7/A2-A6,-(sp) X jsr _debugmain X movem.l (sp)+,D2-D7/A2-A6 X rts X#endasm X X#endif /* DEBUG */ END_OF_FILE if test 18725 -ne `wc -c <'src/device.c'`; then echo shar: \"'src/device.c'\" unpacked with wrong size! fi # end of 'src/device.c' fi if test -f 'src/hansec.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'src/hansec.c'\" else echo shar: Extracting \"'src/hansec.c'\" \(19261 characters\) sed "s/^X//" >'src/hansec.c' <<'END_OF_FILE' X/*- X * $Id: hansec.c,v 1.4 90/02/10 21:30:54 Rhialto Exp $ X * $Log: hansec.c,v $ X * Revision 1.4 90/02/10 21:30:54 Rhialto X * Tuned cache a bit. X * X * Revision 1.3 90/01/27 20:20:16 Rhialto X * Sorted sectors when flushing cache X * X * Revision 1.2 90/01/23 02:31:50 Rhialto X * Add 16-bit FAT support. X * X * Revision 1.1 89/12/17 20:02:49 Rhialto X * Initial revision X * X * HANSEC.C X * X * The code for the messydos file system handler. X * X * Sector-level stuff: read, write, cache, unit conversion. X * Other interactions (via MyDoIO) with messydisk.device. X * X * This code is (C) Copyright 1989,1990 by Olaf Seibert. All rights reserved. X * May not be used or copied without a licence. X-*/ X X#include "han.h" X#include "dos.h" X X/*#undef DEBUG /**/ X#ifdef DEBUG X# define debug(x) dbprintf x X#else X# define debug(x) X#endif X Xstruct MsgPort *DiskReplyPort; Xstruct IOExtTD *DiskIOReq; Xstruct IOStdReq *DiskChangeReq; X Xstruct DiskParam Disk; Xbyte *Fat; Xshort FatDirty; /* Fat must be written to disk */ X Xshort error; /* To put the error value; for Result2 */ Xlong IDDiskState; /* InfoData.id_DiskState */ Xlong IDDiskType; /* InfoData.id_DiskType */ Xstruct timerequest *TimeIOReq; /* For motor-off delay */ Xstruct MinList CacheList; /* Sector cache */ Xint CurrentCache; /* How many cached buffers do we have */ Xint MaxCache = 5; /* Maximum amount of cached buffers */ Xlong CacheBlockSize; /* Size of disk block + overhead */ Xulong BufMemType; Xint DelayState; X Xbyte *Word8086; X Xword XGet8086Word(offset) Xregister int offset; X{ X return Word8086[offset] | Word8086[offset + 1] << 8; X} X Xword XOtherEndianWord(oew) Xword oew; X{ X/* INDENT OFF */ X#asm X move.w 8(a5),d0 X rol.w #8,d0 X#endasm X /* INDENT ON */ X /* X * return (oew << 8) | ((oew >> 8) & 0xff); X */ X} X Xulong XOtherEndianLong(oel) Xulong oel; X{ X/* INDENT OFF */ X#asm X move.l 8(a5),d0 X rol.w #8,d0 X swap d0 X rol.w #8,d0 X#endasm X /* INDENT ON */ X /* X * return ((oel & 0xff) << 24) | ((oel & 0xff00) << 8) | X * ((oel & 0xff0000) >> 8) | ((oel & 0xff000000) >> 24); X */ X} X Xvoid XOtherEndianMsd(msd) Xregister struct MsDirEntry *msd; X{ X msd->msd_Date = OtherEndianWord(msd->msd_Date); X msd->msd_Time = OtherEndianWord(msd->msd_Time); X msd->msd_Cluster = OtherEndianWord(msd->msd_Cluster); X msd->msd_Filesize = OtherEndianLong(msd->msd_Filesize); X} X Xword XClusterToSector(cluster) Xregister word cluster; X{ X return cluster ? Disk.start + cluster * Disk.spc X : 0; X} X Xword XClusterOffsetToSector(cluster, offset) Xregister word cluster; Xregister word offset; X{ X return cluster ? Disk.start + cluster * Disk.spc + offset / Disk.bps X : 0; X} X Xword XDirClusterToSector(cluster) Xregister word cluster; X{ X return cluster ? Disk.start + cluster * Disk.spc X : Disk.rootdir; X} X Xword XSectorToCluster(sector) Xregister word sector; X{ X return sector ? (sector - Disk.start) / Disk.spc X : 0; X} X X/* X * Get the next cluster in a chain. Sort-of checks for special entries. X */ X Xword XNextCluster(cluster) Xword cluster; X{ X register word entry; X X return (entry = GetFatEntry(cluster)) >= 0xFFF0 ? FAT_EOF : entry; X} X Xword XNextClusteredSector(sector) Xword sector; X{ X word next = (sector + 1 - Disk.start) % Disk.spc; X X if (next == 0) { X next = NextCluster(SectorToCluster(sector)); X return next != FAT_EOF ? ClusterToSector(next) X : SEC_EOF; X } else X return sector + 1; X} X X#ifndef READONLY X Xword XFindFreeSector(prev) Xword prev; X{ X word freecluster = FindFreeCluster(SectorToCluster(prev)); X X return freecluster == FAT_EOF ? SEC_EOF : ClusterToSector(freecluster); X} X X#endif X X/* X * Find a specific sector. The cache list is a Least Recently Used stack: X * Put it on the head of the cache list. So if it is not used anymore in a X * long time, it bubbles to the end of the list, getting a higher chance X * of being trashed for re-use. X */ X Xstruct CacheSec * XFindSecByNumber(number) Xregister int number; X{ X register struct CacheSec *sec; X register struct CacheSec *nextsec; X X debug(("FindSecByNumber %d", number)); X X for (sec = (void *) CacheList.mlh_Head; X nextsec = (void *) sec->sec_Node.mln_Succ; sec = nextsec) { X if (sec->sec_Number == number) { X debug((" (%x) %lx\n", sec->sec_Refcount, sec)); X Remove(sec); X AddHead(&CacheList, &sec->sec_Node); X return sec; X } X } X X debug(("; ")); X return NULL; X} X Xstruct CacheSec * XFindSecByBuffer(buffer) Xbyte *buffer; X{ X return (struct CacheSec *) (buffer - OFFSETOF(CacheSec, sec_Data)); X} X X/* X * Get a fresh cache buffer. If we are allowed more cache, we just X * allocate memory. Otherwise, we try to find a currently unused buffer. X * We start looking at the end of the list, which is the bottom of the LRU X * stack. If that fails, allocate more memory anyway. Not that is likely X * anyway, since we currently lock only one sector at a time. X */ X Xstruct CacheSec * XNewCacheSector() X{ X register struct CacheSec *sec; X register struct CacheSec *nextsec; X X debug(("NewCacheSector\n")); X X if (CurrentCache < MaxCache) { X if (sec = AllocMem(CacheBlockSize, BufMemType)) { X goto add; X } X } X for (sec = (void *) CacheList.mlh_TailPred; X nextsec = (void *) sec->sec_Node.mln_Pred; sec = nextsec) { X if ((CurrentCache >= MaxCache) && (sec->sec_Refcount == SEC_DIRTY)) { X FreeCacheSector(sec); /* Also writes it to disk */ X continue; X } X if (sec->sec_Refcount == 0) /* Implies not SEC_DIRTY */ X return sec; X } X X sec = AllocMem(CacheBlockSize, BufMemType); X X if (sec) { Xadd: X CurrentCache++; X AddHead(&CacheList, &sec->sec_Node); X } else X error = ERROR_NO_FREE_STORE; X X return sec; X} X X/* X * Dispose a cached sector, even if it has a non-zero refcount. If it is X * dirty, write it out. X */ X Xvoid XFreeCacheSector(sec) Xregister struct CacheSec *sec; X{ X debug(("FreeCacheSector %d\n", sec->sec_Number)); X Remove(sec); X#ifndef READONLY X if (sec->sec_Refcount & SEC_DIRTY) { X PutSec(sec->sec_Number, sec->sec_Data); X } X#endif X FreeMem(sec, CacheBlockSize); X CurrentCache--; X} X X/* X * Create an empty cache list X */ X Xvoid XInitCacheList() X{ X extern struct CacheSec *sec; /* Of course this does not exist... */ X X NewList(&CacheList); X CurrentCache = 0; X CacheBlockSize = Disk.bps + sizeof (*sec) - sizeof (sec->sec_Data); X} X X/* X * Dispose all cached sectors, possibly writing them to disk. X */ X Xvoid XFreeCacheList() X{ X register struct CacheSec *sec; X X debug(("FreeCacheList, %d\n", CurrentCache)); X while (sec = GetHead(&CacheList)) { X FreeCacheSector(sec); X } X} X X/* X * Do an insertion sort on tosort in the CacheList. Since it changes the X * location in the list, you must fetch it before calling this routine. X * The list will become ascending. X */ X Xvoid XSortSec(tosort) Xregister struct CacheSec *tosort; X{ X register struct CacheSec *sec; X struct CacheSec *nextsec; X register word secno; X X secno = tosort->sec_Number; X debug(("SortSec %d: ", secno)); X X for (sec = (void *) CacheList.mlh_Head; X nextsec = (void *) sec->sec_Node.mln_Succ; sec = nextsec) { X debug(("%d, ", sec->sec_Number)); X if (sec == tosort) { X debug(("\n")); X return; /* No need to move it away */ X } X if (sec->sec_Number > secno) X break; X } X /* Insert before sec */ X Remove(tosort); X Insert(&CacheList, tosort, sec->sec_Node.mln_Pred); X debug(("\n")); X} X X/* X * Write all dirty cache buffers to disk. They are written from highest to X * lowest, and then the FAT is written out. X */ X Xvoid XMSUpdate(immediate) Xint immediate; X{ X register struct CacheSec *sec; X register struct CacheSec *nextsec; X X debug(("MSUpdate\n")); X X#ifndef READONLY X if (DelayState & DELAY_DIRTY) { X /* X * First sort all dirty sectors on block number X */ X for (sec = (void *) CacheList.mlh_Head; X nextsec = (void *) sec->sec_Node.mln_Succ; sec = nextsec) { X if (sec->sec_Refcount & SEC_DIRTY) { X SortSec(sec); X } X } X /* X * Then do a second (backward) scan to write them out. X */ X for (sec = (void *) CacheList.mlh_TailPred; X nextsec = (void *) sec->sec_Node.mln_Pred; sec = nextsec) { X if (sec->sec_Refcount & SEC_DIRTY) { X PutSec(sec->sec_Number, sec->sec_Data); X sec->sec_Refcount &= ~SEC_DIRTY; X } X } X DelayState &= ~DELAY_DIRTY; X } X if (FatDirty) { X WriteFat(); X } X#endif X X if (immediate) X DelayState = DELAY_RUNNING1; X X if (DelayState & DELAY_RUNNING2) { X StartTimer(); X DelayState &= ~DELAY_RUNNING2; X } else { /* DELAY_RUNNING1 */ X#ifndef READONLY X while (TDUpdate() != 0 && RetryRwError(DiskIOReq)) X ; X#endif X TDMotorOff(); X DelayState = DELAY_OFF; X } X} X X/* X * Start the timer which triggers cache writing and stopping the disk X * motor. X */ X Xvoid XStartTimer() X{ X DelayState |= DELAY_RUNNING1 | DELAY_RUNNING2; X X if (CheckIO(TimeIOReq)) { X WaitIO(TimeIOReq); X TimeIOReq->tr_node.io_Command = TR_ADDREQUEST; X TimeIOReq->tr_time.tv_secs = 3; X TimeIOReq->tr_time.tv_micro = 0; X SendIO(TimeIOReq); X } X} X X/* X * Get a pointer to a logical sector { 0, ..., MS_SECTCNT - 1}. We X * allocate a buffer and copy the data in, and lock the buffer until X * FreeSec() is called. X */ X Xbyte * XGetSec(sector) Xint sector; X{ X struct CacheSec *sec; X X if (sec = FindSecByNumber(sector)) { X sec->sec_Refcount++; X X return sec->sec_Data; X } X if (sec = NewCacheSector()) { X register struct IOExtTD *req; X X sec->sec_Number = sector; X sec->sec_Refcount = 1; X X debug(("GetSec %d\n", sector)); X X req = DiskIOReq; X do { X req->iotd_Req.io_Command = ETD_READ; X req->iotd_Req.io_Data = (APTR)sec->sec_Data; X req->iotd_Req.io_Offset = Disk.lowcyl + (long) sector * Disk.bps; X req->iotd_Req.io_Length = Disk.bps; X MyDoIO(req); X } while (req->iotd_Req.io_Error != 0 && RetryRwError(req)); X X StartTimer(); X X if (req->iotd_Req.io_Error == 0) { X return sec->sec_Data; X } X error = ERROR_NOT_A_DOS_DISK; X FreeCacheSector(sec); X } X return NULL; X} X X#ifndef READONLY X Xbyte * XEmptySec(sector) Xint sector; X{ X byte *buffer; X register struct CacheSec *sec; X X if (sec = FindSecByNumber(sector)) { X sec->sec_Refcount++; X X return sec->sec_Data; X } X if (sec = NewCacheSector()) { X sec->sec_Number = sector; X sec->sec_Refcount = 1; X X return sec->sec_Data; X } X X return NULL; X} X Xvoid XPutSec(sector, data) Xint sector; Xbyte *data; X{ X register struct IOExtTD *req; X X debug(("PutSec %d\n", sector)); X X req = DiskIOReq; X do { X req->iotd_Req.io_Command = ETD_WRITE; X req->iotd_Req.io_Data = (APTR) data; X req->iotd_Req.io_Offset = Disk.lowcyl + (long) sector * Disk.bps; X req->iotd_Req.io_Length = Disk.bps; X MyDoIO(req); X } while (req->iotd_Req.io_Error != 0 && RetryRwError(req)); X X StartTimer(); X} X X#endif X X/* X * Unlock a cached sector. When the usage count drops to zero, which X * implies it is not dirty, and we are over our cache quota, the sector is X * freed. Otherwise we keep it for re-use. X */ X Xvoid XFreeSec(buffer) Xbyte *buffer; X{ X register struct CacheSec *sec; X X if (sec = FindSecByBuffer(buffer)) { X if (--sec->sec_Refcount == 0) { /* Implies not SEC_DIRTY */ X if (CurrentCache > MaxCache) { X FreeCacheSector(sec); X } X } X } X} X X#ifndef READONLY X Xvoid XMarkSecDirty(buffer) Xbyte *buffer; X{ X register struct CacheSec *sec; X X if (sec = FindSecByBuffer(buffer)) { X sec->sec_Refcount |= SEC_DIRTY; X DelayState |= DELAY_DIRTY; X StartTimer(); X } X} X X/* X * Write out the FAT. Called from MSUpdate(), so don't call it again from X * here. Don't use precious cache space for it; you could say it has its X * own private cache already. X */ X Xvoid XWriteFat() X{ X register int fat, X sec; X int disksec = Disk.res; /* First FAT, first sector */ X X /* Write all FATs */ X for (fat = 0; fat < Disk.nfats; fat++) { X for (sec = 0; sec < Disk.spf; sec++) { X PutSec(disksec++, Fat + sec * Disk.bps); X /* return; /* Fat STILL dirty! */ X } X } X FatDirty = FALSE; X} X X#endif X Xint XReadBootBlock() X{ X int protstatus; X X debug(("ReadBootBlock\n")); X FreeFat(); /* before disk parameters change */ X TDClear(); X X if ((protstatus = TDProtStatus()) >= 0) { X TDChangeNum(); X debug(("Changenumber = %ld\n", DiskIOReq->iotd_Count)); X if (Word8086 = GetSec(0)) { X word bps; X X /* 8086 ml for a jump */ X if (Word8086[0] != 0xE9 && Word8086[0] != 0xEB) { X goto nodisk; X } X bps = Get8086Word(0x0b); X Disk.spc = Word8086[0x0d]; X Disk.res = Get8086Word(0x0e); X Disk.nfats = Word8086[0x10]; X Disk.ndirs = Get8086Word(0x11); X Disk.nsects = Get8086Word(0x13); X Disk.media = Word8086[0x15]; X Disk.spf = Get8086Word(0x16); X Disk.spt = Get8086Word(0x18); X Disk.nsides = Get8086Word(0x1a); X Disk.nhid = Get8086Word(0x1c); X FreeSec(Word8086); X X /* X * Maybe the sector size just changed. Who knows? X */ X if (Disk.bps != bps) { X FreeCacheList(); X Disk.bps = bps; X InitCacheList(); X } X X Disk.ndirsects = (Disk.ndirs * MS_DIRENTSIZE) / Disk.bps; X Disk.rootdir = Disk.res + Disk.spf * Disk.nfats; X Disk.datablock = Disk.rootdir + Disk.ndirsects; X Disk.start = Disk.datablock - MS_FIRSTCLUST * Disk.spc; X /* Available clusters are 2..maxclust in secs start..nsects-1 */ X Disk.maxclst = (Disk.nsects - Disk.start) / Disk.spc - 1; X Disk.bpc = Disk.bps * Disk.spc; X Disk.vollabel = FakeRootDirEntry; X/* Disk.fat16bits = Disk.nsects > 20740; /* DOS3.2 magic value */ X Disk.fat16bits = Disk.maxclst > 0xFF6; /* DOS3.0 magic value */ X X debug(("%x\tbytes per sector\n", Disk.bps)); X debug(("%x\tsectors per cluster\n", Disk.spc)); X debug(("%x\treserved blocks\n", Disk.res)); X debug(("%x\tfats\n", Disk.nfats)); X debug(("%x\tdirectory entries\n", Disk.ndirs)); X debug(("%x\tsectors\n", Disk.nsects)); X debug(("%x\tmedia byte\n", Disk.media)); X debug(("%x\tsectors per FAT\n", Disk.spf)); X debug(("%x\tsectors per track\n", Disk.spt)); X debug(("%x\tsides\n", Disk.nsides)); X debug(("%x\thidden sectors\n", Disk.nhid)); X X debug(("%x\tdirectory sectors\n", Disk.ndirsects)); X debug(("%x\troot dir block\n", Disk.rootdir)); X debug(("%x\tblock for (imaginary) cluster 0\n", Disk.start)); X debug(("%x\tfirst data block\n", Disk.datablock)); X debug(("%x\tclusters total\n", Disk.maxclst)); X debug(("%x\tbytes per cluster\n", Disk.bpc)); X debug(("%x\t16-bits FAT?\n", Disk.fat16bits)); X X IDDiskType = ID_DOS_DISK; X#ifdef READONLY X IDDiskState = ID_WRITE_PROTECTED; X#else X if (protstatus > 0) X IDDiskState = ID_WRITE_PROTECTED; X else X IDDiskState = ID_VALIDATED; X#endif X X if (Disk.nsects / (MS_SPT * Disk.nsides) <= 40) X DiskIOReq->iotd_Req.io_Flags |= IOMDF_40TRACKS; X else X DiskIOReq->iotd_Req.io_Flags &= ~IOMDF_40TRACKS; X X GetFat(); X } else { X debug(("Can't read %d.\n", DiskIOReq->iotd_Req.io_Error)); X nodisk: X FreeCacheList(); X error = ERROR_NO_DISK; X IDDiskType = ID_UNREADABLE_DISK; X IDDiskState = ID_WRITE_PROTECTED; X } X } X#ifdef DEBUG X else debug(("No disk inserted %d.\n", DiskIOReq->iotd_Req.io_Error)); X#endif X return 1; X} X X/* X * We try to identify the disk currently in the drive, trying to find the X * volume label in the first directory block. X */ X Xint XIdentifyDisk(name, date) Xchar *name; /* Should be at least 32 characters */ Xstruct DateStamp *date; X{ X debug(("IdentifyDisk\n")); X ReadBootBlock(); /* Also sets default vollabel */ X X if (IDDiskType == ID_DOS_DISK) { X byte *dirblock; X register struct MsDirEntry *dirent; X X if (dirblock = GetSec(Disk.rootdir)) { X dirent = (struct MsDirEntry *) dirblock; X X while ((byte *) dirent < &dirblock[Disk.bps]) { X if (dirent->msd_Attributes & ATTR_VOLUMELABEL) { X Disk.vollabel.de_Msd = *dirent; X Disk.vollabel.de_Sector = Disk.rootdir; X Disk.vollabel.de_Offset = (byte *) dirent - dirblock; X OtherEndianMsd(&Disk.vollabel.de_Msd); X Disk.vollabel.de_Msd.msd_Cluster = 0; /* to be sure */ X break; X } X dirent++; X } X strncpy(name, Disk.vollabel.de_Msd.msd_Name, 8 + 3); X name[8 + 3] = '\0'; X ZapSpaces(name, name + 8 + 3); X ToDateStamp(date, Disk.vollabel.de_Msd.msd_Date, X Disk.vollabel.de_Msd.msd_Time); X debug(("Disk is called '%s'\n", name)); X X FreeSec(dirblock); X X return 0; X } X } X return 1; X} X X/* X * Remove the disk change SoftInt. The V1.2 / V1.3 version is broken, so X * we use a workaround. The correct thing to do is shown but not used. X */ X Xvoid XTDRemChangeInt() X{ X if (DiskChangeReq) { X register struct IOExtTD *req = DiskIOReq; X X#if 0 /* V1.2 and V1.3 have a broken X * TD_REMCHANGEINT */ X req->iotd_Req.io_Command = TD_REMCHANGEINT; X req->iotd_Req.io_Data = (void *) DiskChangeReq; X MyDoIO(req); X WaitIO(DiskChangeReq); X#else X Forbid(); X Remove(DiskChangeReq); X Permit(); X#endif X DeleteExtIO(DiskChangeReq); X DiskChangeReq = NULL; X } X} X X/* X * Set the disk change SoftInt. Return nonzero on failure. X */ X Xint XTDAddChangeInt(interrupt) Xstruct Interrupt *interrupt; X{ X register struct IOExtTD *req = DiskIOReq; X X if (DiskChangeReq) { X TDRemChangeInt(); X } X DiskChangeReq = (void *)CreateExtIO(DiskReplyPort, X (long) sizeof (*DiskChangeReq)); X if (DiskChangeReq) { X /* Clone IO request part */ X DiskChangeReq->io_Device = req->iotd_Req.io_Device; X DiskChangeReq->io_Unit = req->iotd_Req.io_Unit; X DiskChangeReq->io_Command = TD_ADDCHANGEINT; X DiskChangeReq->io_Data = (void *) interrupt; X SendIO(DiskChangeReq); X X return 0; X } X return 1; X} X X/* X * Get the current disk change number. Necessary for ETD_ commands. Makes X * absolutely sure nobody can change the disk without us noticing it. X */ X Xint XTDChangeNum() X{ X register struct IOExtTD *req = DiskIOReq; X X req->iotd_Req.io_Command = TD_CHANGENUM; X MyDoIO(req); X req->iotd_Count = req->iotd_Req.io_Actual; X X return req->iotd_Req.io_Actual; X} X X/* X * Get the current write protection state. X * X * Zero means writable, one means write protected, minus one means X * no disk in drive. X */ X Xint XTDProtStatus() X{ X register struct IOExtTD *req = DiskIOReq; X X req->iotd_Req.io_Command = TD_PROTSTATUS; X MyDoIO(req); X X if (req->iotd_Req.io_Error) X return -1; X X return req->iotd_Req.io_Actual != 0; X} X X/* X * Switch the drive motor off. Return previous state. Don't use this when X * you have allocated the disk via GetDrive(). X */ X Xint XTDMotorOff() X{ X register struct IOExtTD *req = DiskIOReq; X X req->iotd_Req.io_Command = TD_MOTOR; X req->iotd_Req.io_Length = 0; X MyDoIO(req); X X return req->iotd_Req.io_Actual; X} X X/* X * Clear all internal messydisk buffers. X */ X Xint XTDClear() X{ X register struct IOExtTD *req = DiskIOReq; X X req->iotd_Req.io_Command = CMD_CLEAR; X X return MyDoIO(req); X} X X#ifndef READONLY X/* X * Write out all internal messydisk buffers to the disk. X */ X Xint XTDUpdate() X{ X register struct IOExtTD *req = DiskIOReq; X X req->iotd_Req.io_Command = ETD_UPDATE; X X return MyDoIO(req); X} X#endif X Xint XMyDoIO(ioreq) Xregister struct IOStdReq *ioreq; X{ X ioreq->io_Flags |= IOF_QUICK; /* Preserve IOMDF_40TRACKS */ X BeginIO(ioreq); X return WaitIO(ioreq); X} END_OF_FILE if test 19261 -ne `wc -c <'src/hansec.c'`; then echo shar: \"'src/hansec.c'\" unpacked with wrong size! fi # end of 'src/hansec.c' fi echo shar: End of archive 3 \(of 6\). cp /dev/null ark3isdone MISSING="" for I in 1 2 3 4 5 6 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 6 archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 -- Mail submissions (sources or binaries) to <amiga@cs.odu.edu>. Mail comments to the moderator at <amiga-request@cs.odu.edu>. Post requests for sources, and general discussion to comp.sys.amiga.