/*- * $Id: pack.c,v 1.3 90/02/03 17:02:05 Rhialto Exp $ * $Log: pack.c,v $ * Revision 1.3 90/02/03 17:02:05 Rhialto * Add error checking wrt dosalloc() * * Revision 1.2 89/12/17 23:06:54 Rhialto * Add ACTION_SET_PROTECT * * Revision 1.1 89/12/17 19:53:24 Rhialto * Initial revision * * * Originally: * * DOSDEVICE.C V1.10 2 November 1987 * * EXAMPLE DOS DEVICE DRIVER FOR AZTEC.C PUBLIC DOMAIN. * * By Matthew Dillon. * * This has been stripped and refilled with messydos code * by Olaf Seibert. * * This code is (C) Copyright 1989 by Olaf Seibert. All rights reserved. May * not be used or copied without a licence. * * Please note that we are NOT pure, so if you wish to mount * multiple MSDOS units, you must use different copies of this driver. * * This file forms the interface between the actual handler code and all * AmigaDOS requirements. It shields it from ugly stuff like BPTRs, BSTRs, * FileLocks, FileHandles and VolumeNodes (in the form of DeviceLists). * Also, most protection against non-inserted disks is done here. -*/ #include "dos.h" #include "han.h" #ifdef DEBUG # define debug(x) dbprintf x #else # define debug(x) #endif /* * Since this code might be called several times in a row without being * unloaded, you CANNOT ASSUME GLOBALS HAVE BEEN ZERO'D!! This also goes * for any global/static assignments that might be changed by running the * code. */ PORT *DosPort; /* Our DOS port... */ DEVNODE *DevNode; /* Our DOS node.. created by DOS for us */ DEVLIST *VolNode; /* Device List structure for our volume * node */ void *SysBase; /* EXEC library base */ DOSLIB *DOSBase; /* DOS library base for debug process */ long PortMask; /* The signal mask for our DosPort */ long WaitMask; /* The signal mask to wait for */ short DiskChanged; /* Set by disk change interrupt */ short Inhibited; /* Are we inhibited (ACTION_INHIBIT)? */ long UnitNr; /* From */ char *DevName; /* the */ ulong DevFlags; /* mountlist */ PACKET *DosPacket; /* For the SystemRequest pr_WindowPtr */ void ChangeIntHand(), DiskChange(); void NewVolNodeName(), NewVolNodeDate(); struct Interrupt ChangeInt = { { 0 }, /* is_Node */ 0, /* is_Data */ ChangeIntHand, /* is_Code */ }; /* * Don't call the entry point main(). This way, if you make a mistake * with the compile options you'll get a link error. */ void messydoshandler() { register PACKET *packet; MSG *msg; byte notdone; long OpenCount; /* How many open files/locks there are */ /* * Initialize all global variables. SysBase MUST be initialized * before we can make Exec calls. AbsExecBase is a library symbol * referencing absolute memory location 4. */ SysBase = AbsExecBase; DOSBase = OpenLibrary("dos.library", 0L); #ifdef DEBUG /* * Initialize debugging code as soon as possible. Only SysBase and * DOSBase are required. */ dbinit(); #endif /* DEBUG */ DosPort = &((struct Process *)FindTask(NULL))->pr_MsgPort; WaitPort(DosPort); /* Get Startup Packet */ msg = GetMsg(DosPort); packet = (PACKET *) msg->mn_Node.ln_Name; DevNode = BTOC(PArg3); { struct FileSysStartupMsg *fssm; ulong *environ; DevName = "messydisk.device"; UnitNr = 0; DevFlags = 0; MaxCache = 5; BufMemType = MEMF_PUBLIC; Disk.nsides = MS_NSIDES; Disk.spt = MS_SPT; Disk.bps = MS_BPS; Disk.lowcyl = 0; if (fssm = (struct FileSysStartupMsg *)BTOC(DevNode->dn_Startup)) { /* Same as BTOC(packet->dp_Arg2) */ UnitNr = fssm->fssm_Unit; if (fssm->fssm_Device) DevName = (char *)BTOC(fssm->fssm_Device)+1; DevFlags = fssm->fssm_Flags; if (environ = BTOC(fssm->fssm_Environ)) { debug(("environ size %ld\n", environ[0])); #define get(xx,yy) if (environ[0] >= yy) xx = environ[yy]; get(MaxCache, DE_NUMBUFFERS); get(BufMemType, DE_MEMBUFTYPE); get(Disk.nsides, DE_NUMHEADS); get(Disk.spt, DE_BLKSPERTRACK); get(Disk.bps, DE_SIZEBLOCK); Disk.bps *= 4; debug(("Disk.bps %d\n", Disk.bps)); get(Disk.lowcyl, DE_LOWCYL); #undef get } } Disk.lowcyl *= (long)MS_BPS * Disk.spt * Disk.nsides; } if (DOSBase && HanOpenUp()) { /* * Loading DevNode->dn_Task causes DOS *NOT* to startup a new * instance of the device driver for every reference. E.G. if * you were writing a CON: device you would want this field to be * NULL. */ DevNode->dn_Task = DosPort; PRes1 = DOSTRUE; PRes2 = 0; } else { /* couldn't open dos.library */ PRes1 = DOSFALSE; PRes2 = ERROR_NO_FREE_STORE; /* no better message available */ returnpacket(packet); goto exit; /* exit process */ } returnpacket(packet); /* Initialize some more global variables */ PortMask = 1L << DosPort->mp_SigBit; VolNode = NULL; OpenCount = 0; Inhibited = 0; /* Get the first real packet */ WaitPort(DosPort); msg = GetMsg(DosPort); notdone = 1; WaitMask = PortMask | (1L << DiskReplyPort->mp_SigBit); TDAddChangeInt(&ChangeInt); DiskInserted(WhichDiskInserted()); goto entry; /* * Here begins the endless loop, waiting for requests over our message * port and executing them. Since requests are sent over the message * port in our device and volume nodes, we must not use our Process * message port for this: this precludes being able to call DOS * functions ourselves. */ top: for (notdone = 1; notdone;) { Wait(WaitMask); if (DiskChanged) DiskChange(); while (msg = GetMsg(DosPort)) { byte buf[256]; /* Max length of BCPL strings is * 255 + 1 for \0. */ entry: if (DiskChanged) DiskChange(); packet = (PACKET *) msg->mn_Node.ln_Name; PRes1 = DOSFALSE; PRes2 = 0; error = 0; debug(("Packet: %3ld %08lx %08lx %08lx %10s\n", PType, PArg1, PArg2, PArg3, typetostr(PType))); DosPacket = packet; /* For the System Requesters */ switch (PType) { case ACTION_DIE: /* attempt to die? */ notdone = 0; /* try to die */ break; case ACTION_CURRENT_VOLUME: /* - VolNode,UnitNr */ PRes1 = (long) CTOB(VolNode); PRes2 = UnitNr; break; case ACTION_LOCATE_OBJECT: /* Lock,Name,Mode Lock */ { register struct FileLock *newlock; struct FileLock *lock; struct MSFileLock *msfl; long lockmode; lock = BTOC(PArg1); if (CheckRead(lock)) break; btos(PArg2, buf); if ((lockmode = PArg3) != EXCLUSIVE_LOCK) lockmode = SHARED_LOCK; msfl = MSLock(lock ? lock->fl_Key : NULL, buf, lockmode); if (msfl) { if (newlock = NewFileLock(msfl, lock)) { newlock->fl_Access = lockmode; PRes1 = (long) CTOB(newlock); OpenCount++; } else MSUnLock(msfl); } } break; case ACTION_RENAME_DISK: /* BSTR:NewName Bool */ if (CheckWrite(NULL)) break; btos(PArg1, buf); buf[31] = '\0'; if (PRes1 = MSRelabel(buf)) NewVolNodeName(); break; case ACTION_FREE_LOCK: /* Lock Bool */ { struct FileLock *lock; struct MSFileLock *msfl; PRes1 = DOSTRUE; lock = BTOC(PArg1); if (lock == NULL) break; msfl = (struct MSFileLock *)lock->fl_Key; FreeFileLock(lock); /* may remove last lock on volume */ MSUnLock(msfl); /* may call MayFreeVolNode */ OpenCount--; } break; case ACTION_DELETE_OBJECT: /* Lock,Name Bool */ { struct FileLock *lock; lock = BTOC(PArg1); if (CheckWrite(lock)) break; btos(PArg2, buf); PRes1 = MSDeleteFile(lock ? lock->fl_Key : NULL, buf); } break; case ACTION_RENAME_OBJECT: /* SLock,SName,DLock,DName Bool */ { struct FileLock *slock, *dlock; char buf2[256]; slock = BTOC(PArg1); dlock = BTOC(PArg3); if (CheckWrite(slock) || CheckWrite(dlock)) break; btos(PArg2, buf); btos(PArg4, buf2); PRes1 = MSRename(slock ? slock->fl_Key : NULL, buf, dlock ? dlock->fl_Key : NULL, buf2); } break; case ACTION_MORECACHE: /* #BufsToAdd Bool */ if ((MaxCache += (short) PArg1) <= 0) { MaxCache = 1; } else PRes1 = DOSTRUE; debug(("Now %d cache sectors\n", MaxCache)); break; case ACTION_COPY_DIR: /* Lock Lock */ { register struct FileLock *newlock; struct FileLock *lock; struct MSFileLock *msfl; lock = BTOC(PArg1); msfl = MSDupLock(lock ? lock->fl_Key : NULL); if (msfl) { if (newlock = NewFileLock(msfl, lock)) { newlock->fl_Access = lock ? lock->fl_Access : SHARED_LOCK; PRes1 = (long) CTOB(newlock); OpenCount++; } else MSUnLock(msfl); } } break; case ACTION_SET_PROTECT: /* -,Lock,Name,Mask Bool */ { struct FileLock *lock; lock = BTOC(PArg2); if (CheckWrite(lock)) break; btos(PArg3, buf); PRes1 = MSSetProtect(lock ? lock->fl_Key : NULL, buf, PArg4); } break; case ACTION_CREATE_DIR: /* Lock,Name Lock */ { register struct FileLock *newlock; struct FileLock *lock; struct MSFileLock *msfl; lock = BTOC(PArg1); if (CheckWrite(lock)) break; btos(PArg2, buf); msfl = MSCreateDir(lock ? lock->fl_Key : NULL, buf); if (msfl) { if (newlock = NewFileLock(msfl, lock)) { newlock->fl_Access = SHARED_LOCK; PRes1 = (long) CTOB(newlock); OpenCount++; } else MSUnLock(msfl); } } break; case ACTION_EXAMINE_OBJECT: /* Lock,Fib Bool */ { struct FileLock *lock; lock = BTOC(PArg1); if (CheckRead(lock)) break; PRes1 = MSExamine(lock ? lock->fl_Key : NULL, BTOC(PArg2)); } break; case ACTION_EXAMINE_NEXT: /* Lock,Fib Bool */ { struct FileLock *lock; lock = BTOC(PArg1); if (CheckRead(lock)) break; PRes1 = MSExNext(lock ? lock->fl_Key : NULL, BTOC(PArg2)); } break; case ACTION_DISK_INFO: /* InfoData Bool:TRUE */ PRes1 = MSDiskInfo(BTOC(PArg1)); break; case ACTION_INFO: /* Lock,InfoData Bool:TRUE */ if (CheckRead(BTOC(PArg1))) break; PRes1 = MSDiskInfo(BTOC(PArg2)); break; case ACTION_FLUSH: /* writeout bufs, disk motor off */ MSUpdate(1); break; /* case ACTION_SET_COMMENT: /* -,Lock,Name,Comment Bool */ case ACTION_PARENT: /* Lock ParentLock */ { register struct FileLock *newlock; struct FileLock *lock; struct MSFileLock *msfl; long mode; lock = BTOC(PArg1); msfl = MSParentDir(lock ? lock->fl_Key : NULL); if (msfl) { if (newlock = NewFileLock(msfl, lock)) { newlock->fl_Access = SHARED_LOCK; PRes1 = (long) CTOB(newlock); OpenCount++; } else MSUnLock(msfl); } } break; case ACTION_INHIBIT: /* Bool Bool */ if (Inhibited = PArg1) { DiskRemoved(); } else { /* Fall through to ACTION_DISK_CHANGE: */ case ACTION_DISK_CHANGE: /* ? ? */ DiskChange(); } PRes1 = DOSTRUE; break; case ACTION_SET_DATE: /* -,Lock,Name,CPTRDateStamp Bool */ { struct FileLock *lock; lock = BTOC(PArg2); if (CheckWrite(lock)) break; btos(PArg3, buf); PRes1 = MSSetDate(lock ? lock->fl_Key : NULL, buf, PArg4); } break; case ACTION_READ: /* FHArg1,CPTRBuffer,Length ActLength */ if (CheckRead(NULL)) { PRes1 = -1; } else PRes1 = MSRead(PArg1, PArg2, PArg3); break; case ACTION_WRITE: /* FHArg1,CPTRBuffer,Length ActLength */ if (CheckWrite(NULL)) { PRes1 = -1; } else PRes1 = MSWrite(PArg1, PArg2, PArg3); break; case ACTION_OPENNEW: /* FileHandle,Lock,Name Bool */ { struct MSFileHandle *msfh; struct FileHandle *fh; struct FileLock *lock; if (CheckWrite(BTOC(PArg2))) break; case ACTION_OPENRW: /* FileHandle,Lock,Name Bool */ case ACTION_OPENOLD: /* FileHandle,Lock,Name Bool */ fh = BTOC(PArg1); lock = BTOC(PArg2); if (CheckRead(lock)) break; btos(PArg3, buf); debug(("'%s' ", buf)); msfh = MSOpen(lock ? lock->fl_Key : NULL, buf, PType); if (msfh) { fh->fh_Arg1 = (long) msfh; PRes1 = DOSTRUE; OpenCount++; } } break; case ACTION_CLOSE: /* FHArg1 Bool:TRUE */ MSClose(PArg1); PRes1 = DOSTRUE; OpenCount--; break; case ACTION_SEEK: /* FHArg1,Position,Mode OldPosition */ if (CheckRead(NULL)) { PRes1 = -1; } else PRes1 = MSSeek(PArg1, PArg2, PArg3); break; /* * A few other packet types which we do not support */ /* case ACTION_WAIT_CHAR: /* Timeout, ticks Bool */ /* case ACTION_RAWMODE: /* Bool(-1:RAW 0:CON) OldState */ default: error = ERROR_ACTION_NOT_KNOWN; break; } /* end switch */ if (packet) { if (error) { debug(("ERR=%d\n", error)); PRes2 = error; } #ifdef DEBUG else { debug(("RES=%06lx\n", PRes1)); } #endif returnpacket(packet); DosPacket = NULL; } #ifdef DEBUG else { debug(("NOREP\n")); } #endif } /* end while (GetMsg()) */ /* * Now check for an other cause of events: timer IO. * Unfortunately we cannot be sure that we always get a signal * when the timeout has elapsed, since the same message port is * used for other IO. */ if (CheckIO(TimeIOReq)) { /* Timer finished? */ debug(("TimeIOReq is finished\n")); if (DelayState != DELAY_OFF) { MSUpdate(0); /* Also may switch off motor */ } } } /* end for (;notdone) */ #ifdef DEBUG debug(("Can we remove ourselves? ")); Delay(50L); /* I wanna even see the debug message! */ #endif /* DEBUG */ Forbid(); if (OpenCount || packetsqueued()) { Permit(); debug((" .. not yet!\n")); goto top; /* sorry... can't exit */ } debug((" .. yes!\n")); /* * Causes a new process to be created on next reference. */ DevNode->dn_Task = NULL; TDRemChangeInt(); DiskRemoved(); HanCloseDown(); debug(("HanCloseDown returned. dbuninit in 2 seconds:\n")); /* * Remove debug window, closedown, fall of the end of the world. */ exit: #ifdef DEBUG Delay(100L); /* This is dangerous! */ dbuninit(); #endif /* DEBUG */ #if 1 UnLoadSeg(DevNode->dn_SegList); /* This is real fun. We are still */ DevNode->dn_SegList = NULL; /* Forbid()den, fortunately */ #endif CloseLibrary(DOSBase); /* Fall off the end of the world. Implicit Permit(). */ } void ChangeIntHand() { /* INDENT OFF */ #asm move.l a6,-(sp) #endasm DiskChanged = 1; Signal(DosPort->mp_SigTask, PortMask); #asm move.l (sp)+,a6 #endasm /* INDENT ON */ } /* * Make a new struct FileLock, for DOS use. It is put on a singly linked * list, which is attached to the same VolumeNode the old lock was on. * * Also note that we must ALWAYS be prepared to UnLock() or DupLock() * any FileLocks we ever made, even if the volume in question has been * removed and/or inserted into another drive with another FileSystem * handler! * * DOS makes certain assumptions about LOCKS. A lock must minimally be a * FileLock structure, with additional private information after the * FileLock structure. The longword before the beginning of the structure * must contain the length of structure + 4. * * NOTE!!!!! The workbench does not follow the rules and assumes it can copy * lock structures. This means that if you want to be workbench * compatible, your lock structures must be EXACTLY sizeof(struct * FileLock). Also, it sometimes uses uninitialized values for the lock mode... */ struct FileLock * NewFileLock(msfl, fl) struct MSFileLock *msfl; struct FileLock *fl; { struct FileLock *newlock; DEVLIST *volnode; if (fl) { volnode = BTOC(fl->fl_Volume); } else { volnode = VolNode; } if (newlock = dosalloc((ulong)sizeof (*newlock))) { newlock->fl_Key = (ulong) msfl; newlock->fl_Task = DosPort; newlock->fl_Volume = (BPTR) CTOB(volnode); Forbid(); newlock->fl_Link = volnode->dl_LockList; volnode->dl_LockList = (BPTR) CTOB(newlock); Permit(); } else error = ERROR_NO_FREE_STORE; return newlock; } /* * This should be called before MSUnLock(), so that it may call * MayFreeVolNode() which then calls FreeVolNode(). A bit tricky, * I'm sorry for that. */ long FreeFileLock(lock) struct FileLock *lock; { register struct FileLock *fl; register struct FileLock **flp; DEVLIST *volnode; volnode = (DEVLIST *)BTOC(lock->fl_Volume); flp = (struct FileLock **) &volnode->dl_LockList; for (fl = BTOC(*flp); fl && fl != lock; fl = BTOC(fl->fl_Link)) flp = (struct FileLock **)&fl->fl_Link; if (fl == lock) { *(BPTR *)flp = fl->fl_Link; dosfree(fl); return DOSTRUE; } else { debug(("Huh?? Could not find filelock!\n")); return DOSFALSE; } } /* * Create Volume node and add to the device list. This will * cause the WORKBENCH to recognize us as a disk. If we * don't create a Volume node, Wb will not recognize us. * However, we are a MESSYDOS: disk, Volume node or not. */ DEVLIST * NewVolNode(name, date) struct DateStamp *date; char *name; { DOSINFO *di; register DEVLIST *volnode; char *volname; /* This is my volume name */ di = BTOC(((ROOTNODE *) DOSBase->dl_Root)->rn_Info); if (volnode = dosalloc((ulong)sizeof (DEVLIST))) { if (volname = dosalloc(32L)) { volname[0] = strlen(name); strcpy(volname + 1, name); /* Make sure \0 terminated */ volnode->dl_Type = DLT_VOLUME; volnode->dl_Task = DosPort; volnode->dl_DiskType = IDDiskType; volnode->dl_Name = CTOB(volname); volnode->dl_VolumeDate = *date; volnode->dl_MSFileLockList = NULL; Forbid(); volnode->dl_Next = di->di_DevInfo; di->di_DevInfo = (long) CTOB(volnode); Permit(); } else { dosfree(volnode); volnode = NULL; } } else { error = ERROR_NO_FREE_STORE; } return volnode; } /* * Get the current VolNode a new name from the volume label. */ void NewVolNodeName() { if (VolNode) { register char *volname = BTOC(VolNode->dl_Name); strncpy(volname + 1, Disk.vollabel.de_Msd.msd_Name, 8+3); volname[1+8+3] = '\0'; /* Make sure \0 terminated */ ZapSpaces(volname + 1, volname + 1 + 8+3); volname[0] = strlen(volname+1); } } /* * Get the current VolNode a new date, from the last root directory. */ void NewVolNodeDate() { if (VolNode) { ToDateStamp(&VolNode->dl_VolumeDate, Disk.vollabel.de_Msd.msd_Date, Disk.vollabel.de_Msd.msd_Time); } } /* * Remove Volume entry. Since DOS uses singly linked lists, we must * (ugg) search it manually to find the link before our Volume entry. */ void FreeVolNode(volnode) DEVLIST *volnode; { DOSINFO *di = BTOC(((ROOTNODE *) DOSBase->dl_Root)->rn_Info); register DEVLIST *dl; register void *dlp; debug(("FreeVolNode %08lx\n", volnode)); if (volnode == NULL) return; dlp = &di->di_DevInfo; Forbid(); for (dl = BTOC(di->di_DevInfo); dl && dl != volnode; dl = BTOC(dl->dl_Next)) dlp = &dl->dl_Next; if (dl == volnode) { *(BPTR *) dlp = dl->dl_Next; dosfree(BTOC(dl->dl_Name)); dosfree(dl); } #ifdef DEBUG else { debug(("****PANIC: Unable to find volume node\n")); } #endif /* DEBUG */ Permit(); if (volnode == VolNode) VolNode = NULL; } /* * This is also called from the real handler when the last lock on a * volume is UnLock()ed, or the last file has been Close()d. */ int MayFreeVolNode(volnode) DEVLIST *volnode; { if (volnode->dl_LockList == NULL) { FreeVolNode(volnode); return 1; } return 0; } /* * Our disk has been removed. Save the FileLocks in the dl_LockList, * and let the handler save its MSFileLocks in the dl_MSFileLockList field. * If it has nothing to save, forget about the volume, and return * DOSTRUE. * There is one subtlety that MSDiskRemoved must know about: * If it MSUnLock()s the last lock on the volume, the VolNode is * deleted via FreeLockList().. MayFreeVolNode().. FreeVolNode(). * But then there is no place anymore to put NULL in, so that needs * to be done first. */ int DiskRemoved() { debug(("DiskRemoved %08lx\n", VolNode)); if (VolNode == NULL) { IDDiskType = ID_NO_DISK_PRESENT;/* really business of MSDiskRemoved */ return DOSTRUE; } VolNode->dl_Task = NULL; MSDiskRemoved(&VolNode->dl_MSFileLockList); if (VolNode == NULL) { /* Could happen via MSDiskRemoved() */ return DOSTRUE; } NewVolNodeDate(); /* Fetch new date of root directory */ VolNode = NULL; return DOSFALSE; } /* * Reconstruct everything from a Volume node */ void DiskInserted(volnode) register DEVLIST *volnode; { debug(("DiskInserted %08lx\n", volnode)); VolNode = volnode; if (volnode) { volnode->dl_Task = DosPort; MSDiskInserted(&volnode->dl_MSFileLockList, volnode); volnode->dl_MSFileLockList = NULL; } } DEVLIST * WhichDiskInserted() { char name[34]; struct DateStamp date; register DEVLIST *dl = NULL; if (!Inhibited && IdentifyDisk(name, &date) == 0) { DOSINFO *di = BTOC(((ROOTNODE *) DOSBase->dl_Root)->rn_Info); byte *nodename; int namelen = strlen(name); for (dl = BTOC(di->di_DevInfo); dl; dl = BTOC(dl->dl_Next)) { nodename = BTOC(dl->dl_Name); if (nodename[0] != namelen || strncmp(nodename+1,name,namelen)) continue; if (dl->dl_VolumeDate == date) /* Non-standard! Structure compare! */ break; } name[31] = '\0'; if (dl == NULL) dl = NewVolNode(name, &date); } return dl; } void DiskChange() { debug(("DiskChange\n")); DiskChanged = 0; DiskRemoved(); DiskInserted(WhichDiskInserted()); } int CheckRead(lock) struct FileLock *lock; { if (lock && BTOC(lock->fl_Volume) != VolNode) error = ERROR_DEVICE_NOT_MOUNTED; else if (IDDiskType == ID_NO_DISK_PRESENT) error = ERROR_NO_DISK; else if (IDDiskType != ID_DOS_DISK) error = ERROR_NOT_A_DOS_DISK; return error; } int CheckWrite(lock) struct FileLock *lock; { if (lock && BTOC(lock->fl_Volume) != VolNode) error = ERROR_DEVICE_NOT_MOUNTED; else if (IDDiskType == ID_NO_DISK_PRESENT) error = ERROR_NO_DISK; else if (IDDiskType != ID_DOS_DISK) error = ERROR_NOT_A_DOS_DISK; else if (IDDiskState == ID_VALIDATING) error = ERROR_DISK_NOT_VALIDATED; else if (IDDiskState != ID_VALIDATED) error = ERROR_DISK_WRITE_PROTECTED; return error; } #ifdef DEBUG /* DEBUGGING */ PORT *Dbport; /* owned by the debug process */ PORT *Dback; /* owned by the DOS device driver */ short DBEnable; /* * DEBUGGING CODE. You cannot make DOS library calls that access other * devices from within a DOS device driver because they use the same * message port as the driver. 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. */ extern void debugproc(); dbinit() { TASK *task = FindTask(NULL); Dback = CreatePort("MSH:Dback", -1L); CreateProc("MSH_DB", (long)task->tc_Node.ln_Pri+1, CTOB(debugproc), 4096L); WaitPort(Dback); /* handshake startup */ GetMsg(Dback); /* remove dummy msg */ DBEnable = 1; dbprintf("Debugger running V1.10\n"); } dbuninit() { MSG killmsg; if (Dbport) { killmsg.mn_Length = 0; /* 0 means die */ PutMsg(Dbport, &killmsg); WaitPort(Dback); /* He's dead jim! */ GetMsg(Dback); 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. Still, it doesn't hurt... */ Delay(50L); /* ensure he's dead */ } } dbprintf(a,b,c,d,e,f,g,h,i,j) long a,b,c,d,e,f,g,h,i,j; { struct { MSG msg; char buf[256]; } msgbuf; register MSG *msg = &msgbuf.msg; register long len; if (Dbport && DBEnable) { 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 */ PutMsg(Dbport, msg); WaitPort(Dback); GetMsg(Dback); } } /* * BTW, the DOS library used by debugmain() was actually opened by * the device driver. */ debugmain() { register MSG *msg; register long len; register void *fh; void *fh2; MSG DummyMsg; Dbport = CreatePort("MSH:Dbport", -1L); fh = Open("CON:0/10/640/190/FileSystem 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). */ #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 */