From d4bd1050ffc2c6876af3acf8de636b29c1c74cef Mon Sep 17 00:00:00 2001 From: Matt Harlum Date: Fri, 27 Jun 2025 10:31:38 +0000 Subject: [PATCH] Don't use RTF_AUTOINIT When using autoinit the mounter would mount the devices before the device is added to execs Device list Before DOS is started (like rom boot) this is not a problem because the device will be added to the list before DOS is started After DOS is started though it becomes a problem, dos will try to OpenDevice() the device before it is even on the Device List --- examples/spisd/bootrom/spisd.rom | Bin 0 -> 32768 bytes examples/spisd/device.c | 42 ++++++++++++++++++++++--------- examples/spisd/mount.S | 40 +++++++++++++++-------------- examples/spisd/romtag.c | 7 +++--- examples/spisd/spisd.device | Bin 0 -> 9044 bytes 5 files changed, 55 insertions(+), 34 deletions(-) create mode 100755 examples/spisd/bootrom/spisd.rom create mode 100755 examples/spisd/spisd.device diff --git a/examples/spisd/bootrom/spisd.rom b/examples/spisd/bootrom/spisd.rom new file mode 100755 index 0000000000000000000000000000000000000000..718fcd5f51020f8f3d0612f259e6bae9ff56e17c GIT binary patch literal 32768 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 + volatile struct Task *parent = (struct Task *)self->tc_UserData; + + Forbid(); if ((AllocSignal(SIGB_CARD_CHANGE)) == -1) Alert(0xBADBEEF); if ((AllocSignal(SIGB_OP_REQUEST)) == -1) Alert(0xBADBEEF); @@ -187,7 +189,9 @@ static void task_run() if (card_present && sd_open() == 0) card_opened = TRUE; - Signal(parent,SIGF_SINGLE); + Permit(); + + Signal((struct Task *)parent,SIGF_SINGLE); while (1) { @@ -342,6 +346,8 @@ static struct Library *init_device(__reg("a6") struct ExecBase *sys_base, __reg( SysBase = *(struct ExecBase **)4; saved_seg_list = seg_list; + card_opened = FALSE; + dev->lib_Node.ln_Type = NT_DEVICE; dev->lib_Node.ln_Name = device_name; dev->lib_Flags = LIBF_SUMUSED | LIBF_CHANGED; @@ -370,8 +376,6 @@ static struct Library *init_device(__reg("a6") struct ExecBase *sys_base, __reg( Permit(); Wait(SIGF_SINGLE); // Wait for task to be ready - - mount(SysBase,NULL,dev); return dev; fail3: @@ -446,10 +450,24 @@ static ULONG device_vectors[] = -1, }; -ULONG auto_init_tables[] = -{ - sizeof(struct Library), - (ULONG)device_vectors, - 0, - (ULONG)init_device, -}; +/** + * init + * + * Make the Device, add it and then call mounter + * + * Since Mounter uses OpenDevice() we cannot use RTF_AUTOINIT + */ +struct Library *init(__reg("a0") BPTR seglist) { + struct ExecBase *SysBase = *(struct ExecBase **)4UL; + struct Library *mydev = MakeLibrary((ULONG *)&device_vectors, + NULL, + (APTR)init_device, + sizeof(struct Library), + seglist); + + if (mydev) { + AddDevice((struct Device *)mydev); + mount(SysBase,NULL,mydev->lib_Node.ln_Name); + } + return mydev; +} \ No newline at end of file diff --git a/examples/spisd/mount.S b/examples/spisd/mount.S index 73463b9..568c9c6 100644 --- a/examples/spisd/mount.S +++ b/examples/spisd/mount.S @@ -20,9 +20,8 @@ ; Frame pointer vars MB_CD EQU -4 ; ConfigDev -MB_DEVICE EQU MB_CD-4 ; Device pointer -MB_DEVNAME EQU MB_DEVICE-4 -MB_IOREQ EQU MB_DEVNAME-IOSTD_SIZE ; IORequest structure (48 bytes) +MB_DEVICE EQU MB_CD-4 ; Device Name +MB_IOREQ EQU MB_DEVICE-IOSTD_SIZE ; IORequest structure (48 bytes) MB_MP EQU MB_IOREQ-MP_SIZE ; MsgPort structure (34 bytes) MB_BLOCKSHIFT EQU MB_MP-2 MB_BLOCKSIZE EQU MB_BLOCKSHIFT-4 ; Blocksize @@ -60,7 +59,7 @@ MB_SIZE EQU -(MB_FSRES) ; Total frame size ; The function handles both Kickstart 1.3 and 2.x+ mounting protocols. ; ; INPUTS: -; A0.L - Device base +; A0.L - Device name (null-terminated string, e.g., "scsi.device") ; A1.L - ConfigDev structure pointer (from expansion board, NULL to create fake) ; A6.L - Pointer to exec.library base ; @@ -70,11 +69,21 @@ MB_SIZE EQU -(MB_FSRES) ; Total frame size ; REGISTER USAGE DURING EXECUTION: ; D2.L - Current unit number being processed ; D3.L - Return code accumulator -; A2.L - Device base pointer +; A2.L - Device name string pointer ; A3.L - ConfigDev structure pointer ; A4.L - ExpansionBase pointer (when expansion.library is open) ; A5.L - Frame pointer for local variable access ; A6.L - SysBase (exec.library base) +; +; STACK FRAME LAYOUT: +; MB_CD(-4): ConfigDev pointer +; MB_DEVICE(-8): Device name pointer +; MB_IOREQ(-56): IOStdReq structure for device I/O +; MB_MP(-90): MsgPort structure for async I/O +; MB_BLOCKSHIFT(-92): Block size shift value for address calculations +; MB_BLOCKSIZE(-96): Device block size in bytes +; MB_FSHB(-100): First filesystem header block pointer +; MB_FSRES(-104): FileSystem.resource pointer ;****************************************************************************** _mount: movem.l d2-d7/a2-a6,-(sp) move.l a0,a2 @@ -89,14 +98,13 @@ _mount: movem.l d2-d7/a2-a6,-(sp) .zero clr.l -(a0) dbra d0,.zero - move.l a2,MB_DEVICE(a5) ; Save Device pointer - move.l LN_NAME(a2),MB_DEVNAME(a5) + move.l a2,MB_DEVICE(a5) ; Save Device name ; Initialize MsgPort lea.l MB_MP(a5),a0 bsr.w InitMP tst.b d0 - bmi.w .end + bmi.s .end ; Init IOReq lea.l MB_IOREQ(a5),a0 @@ -114,25 +122,19 @@ _mount: movem.l d2-d7/a2-a6,-(sp) move.l a3,d0 ; Was a configDev provided? beq .noconfigdev move.l a3,MB_CD(a5) - bra.s .start + bra .start .noconfigdev: bsr FakeConfigDev ; No configDev, create a fake one move.l a1,MB_CD(a5) .start: moveq #-1,d2 ; Start scanning units .unitloop: add.l #1,d2 ; Start at unit 0 - ; Open the device + move.l a2,a0 ; Device name move.l d2,d0 ; Unit lea.l MB_IOREQ(a5),a1 ; IOStdReq - move.l MB_DEVICE(a5),IO_DEVICE(a1) ; Populate IO_DEVICE (required when calling open directly) clr.l d1 ; Flags - exg a2,a6 ; Device <==> SysBase - jsr LIB_OPEN(a6) ; Call the devices Open() vector directly - exg a2,a6 ; SysBase <==> Device - - lea.l MB_IOREQ(a5),a0 - move.b IO_ERROR(a0),d0 ; Check IO Error + jsr _LVOOpenDevice(a6) tst.b d0 beq.s .OpenOK cmp.b #TDERR_BadUnitNum,d0 ; No unit here but there are others @@ -564,7 +566,7 @@ ProcessPart: movem.l a2-a3/d2-d5,-(sp) move.b (a1)+,d0 ; Convert to C str move.b #0,0(a1,d0.w) ; Null Terminate move.l a1,(a0) ; Set drive name in param packet - move.l MB_DEVNAME(a5),4(a0) ; Set Device name + move.l MB_DEVICE(a5),4(a0) ; Set Device name move.l d5,8(a0) ; Set the unit number move.l #0,12(a0) ; Set flags @@ -995,7 +997,7 @@ LoadFS: movem.l d2/a2/a4,-(sp) moveq #11,d1 ; Copy relevant fields .copy: move.l (a0)+,(a4)+ dbf d1,.copy - move.l MB_DEVNAME(a5),LN_NAME(a1) + move.l MB_DEVICE(a5),LN_NAME(a1) move.l d2,fse_SegList(a1) ; Patch in the new seglist lea.l fsr_FileSysEntries(a2),a0 jsr _LVOAddHead(a6) ; Add it to the head of FileSysEntries diff --git a/examples/spisd/romtag.c b/examples/spisd/romtag.c index 568e88c..4a1927b 100644 --- a/examples/spisd/romtag.c +++ b/examples/spisd/romtag.c @@ -1,10 +1,11 @@ #include #include #include +#include #include "version.h" -extern ULONG auto_init_tables[]; +extern struct Library *init(__reg("a1") BPTR seg_list); LONG noexec(void) { return -1; @@ -16,11 +17,11 @@ const struct Resident romtag = .rt_MatchWord = RTC_MATCHWORD, .rt_MatchTag = (void *)&romtag, .rt_EndSkip = &romtag + 1, - .rt_Flags = RTF_AUTOINIT, + .rt_Flags = RTF_COLDSTART, .rt_Version = VERSION, .rt_Type = NT_DEVICE, .rt_Pri = 10, .rt_Name = device_name, .rt_IdString = id_string, - .rt_Init = auto_init_tables + .rt_Init = (APTR)init, }; diff --git a/examples/spisd/spisd.device b/examples/spisd/spisd.device new file mode 100755 index 0000000000000000000000000000000000000000..954389006da90b8872319af91838637a76727f9c GIT binary patch literal 9044 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 literal 0 HcmV?d00001