Compare commits

...

13 Commits

Author SHA1 Message Date
89e3b82f6c Store mounter in-tree
Mounter repo: https://github.com/A4091/mounter
Mounter commit: a0d87fe11a2113afa2bc4aec970fd9963f062846
2025-08-01 21:56:47 +12:00
8f3e51db7a Remove mounter submodule 2025-08-01 21:55:21 +12:00
9acc2025c3 Update mounter 2025-07-29 07:18:47 +00:00
c3a7d12653 lideflash: print old/new version info 2025-07-29 07:18:35 +00:00
6f1d844802 lideflash: when erasing the prompt will now ask if the user wants to Erase the flash, not Update 2025-07-29 07:08:43 +00:00
19307ccdac reloc: store ReadHandle and pHunks pointers in frame pointer 2025-07-17 11:03:52 +00:00
1cf84db951 Fixup for mode pages & read_capacity PMI bit 2025-07-15 12:27:15 +00:00
244e137d68 Compact Unit struct some more 2025-07-05 04:26:38 +00:00
6696cadfd2 Remove unused/unneeded parts of IDEUnit struct 2025-07-05 04:23:44 +00:00
657151b6e8 Mounter: Switch out mounter code with the mounter code upstream from A4091.device
This will make it easier to share improvements to the mounter code
2025-07-05 03:49:46 +00:00
e1fa8190d4 Remove function attributes (used/saveds)
lide doesn't use fbaserel and the used attribute is not needed
2025-07-05 03:28:11 +00:00
af8d03e0b9 Move BOOTXX boot priority adjustment from mounter to device.c
I am preparing to rebase mounter on shared codebase with a4091.device
2025-07-01 07:06:50 +00:00
972c6f2c11 Whitespace fixes 2025-07-01 07:06:14 +00:00
18 changed files with 1354 additions and 658 deletions

View File

@ -17,7 +17,6 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
with:
token: ${{ secrets.ACCESS_TOKEN }}
submodules: true
- name: Build

View File

@ -11,7 +11,7 @@ export BUILD_DATE
export GIT_REF
CC=m68k-amigaos-gcc
CFLAGS+=-nostartfiles -nostdlib -mcpu=68000 -Wall -Wno-multichar -Wno-pointer-sign -Wno-attributes -Wno-unused-value -s -Os -fomit-frame-pointer -DCDBOOT=1 -DNO_RDBLAST=1
CFLAGS+=-nostartfiles -nostdlib -mcpu=68000 -Wall -Wno-multichar -Wno-pointer-sign -Wno-unused-value -s -Os -fomit-frame-pointer -DCDBOOT=1 -DNO_RDBLAST=1
CFLAGS+=-DGIT_REF=$(GIT_REF) -DBUILD_DATE=$(BUILD_DATE)
LDFLAGS=-lgcc -lc
AS=m68k-amigaos-as
@ -71,7 +71,7 @@ OBJ = device.o \
scsi.o \
idetask.o \
lide_alib.o \
mounter.o \
mounter/mounter.o \
debug.o
ASMOBJ = endskip.o

100
ata.c
View File

@ -34,7 +34,7 @@ static BYTE write_taskfile_chs(struct IDEUnit *unit, UBYTE command, ULONG lba, U
*
* @param unit Pointer to an IDEUnit struct
*/
static void __attribute__((always_inline)) ata_status_reg_delay(struct IDEUnit *unit) {
static void ata_status_reg_delay(struct IDEUnit *unit) {
asm volatile (
".rep 4 \n\t"
"tst.l (%0) \n\t" // Use tst.l so we don't need to save/restore some other register
@ -68,7 +68,7 @@ static void ata_save_error(struct IDEUnit *unit) {
* @param unit Pointer to an IDEUnit struct
* @returns True if error is indicated
*/
static bool __attribute__((always_inline)) ata_check_error(struct IDEUnit *unit) {
static bool ata_check_error(struct IDEUnit *unit) {
return (*unit->drive.status_command & (ata_flag_error | ata_flag_df));
}
@ -160,7 +160,7 @@ bool ata_select(struct IDEUnit *unit, UBYTE select, bool wait)
bool changed = false;
volatile UBYTE *shadowDevHead = unit->shadowDevHead;
if (!unit->lba) select &= ~(0x40);
if (!unit->flags.lba) select &= ~(0x40);
if (*shadowDevHead == select) {
return false;
@ -193,7 +193,7 @@ bool ata_select(struct IDEUnit *unit, UBYTE select, bool wait)
*/
bool ata_identify(struct IDEUnit *unit, UWORD *buffer)
{
UBYTE drvSel = (unit->primary) ? 0xE0 : 0xF0; // Select drive
UBYTE drvSel = (unit->flags.primary) ? 0xE0 : 0xF0; // Select drive
ata_select(unit,drvSel,false);
@ -342,41 +342,39 @@ void ata_set_xfer(struct IDEUnit *unit, enum xfer method) {
*
* Initialize a unit, check if it is there and responding
* @param unit Pointer to an IDEUnit struct
* @param base Base address of the drive registers
* @returns false on error
*/
bool ata_init_unit(struct IDEUnit *unit) {
bool ata_init_unit(struct IDEUnit *unit, void *base) {
struct ExecBase *SysBase = unit->SysBase;
unit->cylinders = 0;
unit->heads = 0;
unit->sectorsPerTrack = 0;
unit->blockSize = 0;
unit->present = false;
unit->mediumPresent = false;
unit->cylinders = 0;
unit->heads = 0;
unit->sectorsPerTrack = 0;
unit->blockSize = 0;
unit->flags.present = false;
unit->flags.mediumPresent = false;
ULONG offset;
UWORD *buf;
bool dev_found = false;
unit->drive.data = (UWORD*) (base + ata_reg_data);
unit->drive.error_features = (UBYTE*) (base + ata_reg_error);
unit->drive.sectorCount = (UBYTE*) (base + ata_reg_sectorCount);
unit->drive.lbaLow = (UBYTE*) (base + ata_reg_lbaLow);
unit->drive.lbaMid = (UBYTE*) (base + ata_reg_lbaMid);
unit->drive.lbaHigh = (UBYTE*) (base + ata_reg_lbaHigh);
unit->drive.devHead = (UBYTE*) (base + ata_reg_devHead);
unit->drive.status_command = (UBYTE*) (base + ata_reg_status);
offset = (unit->channel == 0) ? CHANNEL_0 : CHANNEL_1;
unit->drive.data = (UWORD*) ((void *)unit->cd->cd_BoardAddr + offset + ata_reg_data);
unit->drive.error_features = (UBYTE*) ((void *)unit->cd->cd_BoardAddr + offset + ata_reg_error);
unit->drive.sectorCount = (UBYTE*) ((void *)unit->cd->cd_BoardAddr + offset + ata_reg_sectorCount);
unit->drive.lbaLow = (UBYTE*) ((void *)unit->cd->cd_BoardAddr + offset + ata_reg_lbaLow);
unit->drive.lbaMid = (UBYTE*) ((void *)unit->cd->cd_BoardAddr + offset + ata_reg_lbaMid);
unit->drive.lbaHigh = (UBYTE*) ((void *)unit->cd->cd_BoardAddr + offset + ata_reg_lbaHigh);
unit->drive.devHead = (UBYTE*) ((void *)unit->cd->cd_BoardAddr + offset + ata_reg_devHead);
unit->drive.status_command = (UBYTE*) ((void *)unit->cd->cd_BoardAddr + offset + ata_reg_status);
*unit->shadowDevHead = *unit->drive.devHead = (unit->primary) ? 0xE0 : 0xF0; // Select drive
*unit->shadowDevHead = *unit->drive.devHead = (unit->flags.primary) ? 0xE0 : 0xF0; // Select drive
enum xfer method = ata_autoselect_xfer(unit);
ata_set_xfer(unit,method);
for (int i=0; i<(8*NEXT_REG); i+=NEXT_REG) {
// Check if the bus is floating (D7/6 pulled-up with resistors)
if ((i != ata_reg_devHead) && (*((volatile UBYTE *)unit->cd->cd_BoardAddr + offset + i) & 0xC0) != 0xC0) {
if ((i != ata_reg_devHead) && (*((volatile UBYTE *)base + i) & 0xC0) != 0xC0) {
dev_found = true;
Trace("INIT: Unit base: %08lx; Drive base %08lx\n",unit, unit->drive);
break;
@ -395,20 +393,20 @@ bool ata_init_unit(struct IDEUnit *unit) {
if (ata_identify(unit,buf) == true) {
Info("INIT: ATA Drive found!\n");
unit->lba = (buf[ata_identify_capabilities] & ata_capability_lba) != 0;
unit->cylinders = buf[ata_identify_cylinders];
unit->heads = buf[ata_identify_heads];
unit->sectorsPerTrack = buf[ata_identify_sectors];
unit->blockSize = 512;
unit->logicalSectors = buf[ata_identify_logical_sectors+1] << 16 | buf[ata_identify_logical_sectors];
unit->blockShift = 0;
unit->mediumPresent = true;
unit->multipleCount = buf[ata_identify_multiple] & 0xFF;
unit->flags.lba = (buf[ata_identify_capabilities] & ata_capability_lba) != 0;
unit->cylinders = buf[ata_identify_cylinders];
unit->heads = buf[ata_identify_heads];
unit->sectorsPerTrack = buf[ata_identify_sectors];
unit->blockSize = 512;
unit->logicalSectors = buf[ata_identify_logical_sectors+1] << 16 | buf[ata_identify_logical_sectors];
unit->blockShift = 0;
unit->flags.mediumPresent = true;
unit->multipleCount = buf[ata_identify_multiple] & 0xFF;
if (unit->multipleCount > 0 && (ata_set_multiple(unit,unit->multipleCount) == 0)) {
unit->xferMultiple = true;
unit->flags.xferMultiple = true;
} else {
unit->xferMultiple = false;
unit->flags.xferMultiple = false;
unit->multipleCount = 1;
}
@ -420,13 +418,13 @@ bool ata_init_unit(struct IDEUnit *unit) {
goto ident_failed;
}
unit->lba48 = true;
unit->flags.lba48 = true;
Info("INIT: Drive supports LBA48 mode \n");
unit->logicalSectors = (buf[ata_identify_lba48_sectors + 1] << 16 |
buf[ata_identify_lba48_sectors]);
unit->write_taskfile = &write_taskfile_lba48;
} else if (unit->lba == true) {
} else if (unit->flags.lba == true) {
// LBA-28 up to 127GB
unit->write_taskfile = &write_taskfile_lba;
@ -464,7 +462,7 @@ bool ata_init_unit(struct IDEUnit *unit) {
Info("INIT: ATAPI Drive found!\n");
unit->deviceType = (buf[0] >> 8) & 0x1F;
unit->atapi = true;
unit->flags.atapi = true;
atapi_test_unit_ready(unit,true); // Clear the Unit attention check condition
} else {
@ -476,14 +474,14 @@ ident_failed:
}
}
if (unit->atapi == false && unit->blockSize == 0) {
if (unit->flags.atapi == false && unit->blockSize == 0) {
Warn("INIT: Error! blockSize is 0\n");
if (buf) FreeMem(buf,512);
return false;
}
Info("INIT: Blockshift: %ld\n",unit->blockShift);
unit->present = true;
unit->flags.present = true;
Info("INIT: LBAs %ld Blocksize: %ld\n",unit->logicalSectors,unit->blockSize);
@ -500,7 +498,7 @@ ident_failed:
* @return non-zero on error
*/
bool ata_set_multiple(struct IDEUnit *unit, BYTE multiple) {
UBYTE drvSel = (unit->primary) ? 0xE0 : 0xF0; // Select drive
UBYTE drvSel = (unit->flags.primary) ? 0xE0 : 0xF0; // Select drive
ata_select(unit,drvSel,true);
@ -552,10 +550,10 @@ BYTE ata_read(void *buffer, ULONG lba, ULONG count, struct IDEUnit *unit) {
UBYTE multipleCount = unit->multipleCount;
volatile void *dataRegister = unit->drive.data;
if (unit->lba48) {
if (unit->flags.lba48) {
command = ATA_CMD_READ_MULTIPLE_EXT;
} else {
command = (unit->xferMultiple) ? ATA_CMD_READ_MULTIPLE : ATA_CMD_READ;
command = (unit->flags.xferMultiple) ? ATA_CMD_READ_MULTIPLE : ATA_CMD_READ;
}
ata_xfer_func ata_xfer;
@ -567,7 +565,7 @@ BYTE ata_read(void *buffer, ULONG lba, ULONG count, struct IDEUnit *unit) {
ata_xfer = unit->read_fast;
}
UBYTE drvSel = (unit->primary) ? 0xE0 : 0xF0;
UBYTE drvSel = (unit->flags.primary) ? 0xE0 : 0xF0;
ata_select(unit,drvSel,true);
@ -641,10 +639,10 @@ BYTE ata_write(void *buffer, ULONG lba, ULONG count, struct IDEUnit *unit) {
UBYTE multipleCount = unit->multipleCount;
volatile void *dataRegister = unit->drive.data;
if (unit->lba48) {
if (unit->flags.lba48) {
command = ATA_CMD_WRITE_MULTIPLE_EXT;
} else {
command = (unit->xferMultiple) ? ATA_CMD_WRITE_MULTIPLE : ATA_CMD_WRITE;
command = (unit->flags.xferMultiple) ? ATA_CMD_WRITE_MULTIPLE : ATA_CMD_WRITE;
}
ata_xfer_func ata_xfer;
@ -656,7 +654,7 @@ BYTE ata_write(void *buffer, ULONG lba, ULONG count, struct IDEUnit *unit) {
ata_xfer = unit->write_fast;
}
UBYTE drvSel = (unit->primary) ? 0xE0 : 0xF0;
UBYTE drvSel = (unit->flags.primary) ? 0xE0 : 0xF0;
ata_select(unit,drvSel,true);
@ -760,7 +758,7 @@ static BYTE write_taskfile_chs(struct IDEUnit *unit, UBYTE command, ULONG lba, U
if (!ata_wait_ready(unit,ATA_RDY_WAIT_COUNT))
return HFERR_SelTimeout;
devHead = ((unit->primary) ? 0xA0 : 0xB0) | (head & 0x0F);
devHead = ((unit->flags.primary) ? 0xA0 : 0xB0) | (head & 0x0F);
*unit->shadowDevHead = devHead;
*unit->drive.devHead = devHead;
@ -786,7 +784,7 @@ static BYTE write_taskfile_lba(struct IDEUnit *unit, UBYTE command, ULONG lba, U
if (!ata_wait_ready(unit,ATA_RDY_WAIT_COUNT))
return HFERR_SelTimeout;
devHead = ((unit->primary) ? 0xE0 : 0xF0) | ((lba >> 24) & 0x0F);
devHead = ((unit->flags.primary) ? 0xE0 : 0xF0) | ((lba >> 24) & 0x0F);
*unit->shadowDevHead = devHead;
*unit->drive.devHead = devHead;
@ -920,7 +918,7 @@ BYTE scsi_ata_passthrough(struct IDEUnit *unit, struct SCSICmd *cmd) {
count += (count & 1); // Ensure byte count is even
UBYTE drvSel = (unit->primary) ? 0xE0 : 0xF0;
UBYTE drvSel = (unit->flags.primary) ? 0xE0 : 0xF0;
ata_select(unit,drvSel,true);

2
ata.h
View File

@ -104,7 +104,7 @@ enum xfer_dir {
#define ATA_RDY_WAIT_COUNT (ATA_RDY_WAIT_S * 1000 * (1000 / ATA_RDY_WAIT_LOOP_US))
bool ata_init_unit(struct IDEUnit *);
bool ata_init_unit(struct IDEUnit *unit, void *base);
bool ata_select(struct IDEUnit *unit, UBYTE select, bool wait);
bool ata_identify(struct IDEUnit *, UWORD *);
bool ata_set_multiple(struct IDEUnit *unit, BYTE multiple);

16
atapi.c
View File

@ -31,7 +31,7 @@
*
* @param unit Pointer to an IDEUnit struct
*/
static void __attribute__((always_inline)) atapi_status_reg_delay() {
static void atapi_status_reg_delay() {
asm volatile (
"tst.b 0xBFE001"
);
@ -158,7 +158,7 @@ static bool atapi_check_ir(struct IDEUnit *unit, UBYTE mask, UBYTE value, UWORD
* @param unit Pointer to an IDEUnit struct
* @returns True if error is indicated
*/
static bool __attribute__((always_inline)) atapi_check_error(struct IDEUnit *unit) {
static bool atapi_check_error(struct IDEUnit *unit) {
atapi_status_reg_delay();
return (*unit->drive.status_command & (ata_flag_error | ata_flag_df));
}
@ -206,7 +206,7 @@ bool atapi_check_signature(struct IDEUnit *unit) {
*/
bool atapi_identify(struct IDEUnit *unit, UWORD *buffer) {
UBYTE drvSel = (unit->primary) ? 0xE0 : 0xF0; // Select drive
UBYTE drvSel = (unit->flags.primary) ? 0xE0 : 0xF0; // Select drive
// Only update the devHead register if absolutely necessary to save time
ata_select(unit,drvSel,false);
@ -410,7 +410,7 @@ BYTE atapi_packet(struct SCSICmd *cmd, struct IDEUnit *unit) {
cmd->scsi_Actual = 0;
UBYTE drvSelHead = ((unit->primary) ? 0xE0 : 0xF0);
UBYTE drvSelHead = ((unit->flags.primary) ? 0xE0 : 0xF0);
// Only update the devHead register if absolutely necessary to save time
ata_select(unit,drvSelHead,true);
@ -1119,14 +1119,14 @@ BYTE atapi_check_wp(struct IDEUnit *unit) {
*/
bool atapi_update_presence(struct IDEUnit *unit, bool present) {
bool ret = false;
if (present && unit->mediumPresent == false) {
if (present && unit->flags.mediumPresent == false) {
unit->changeCount++;
unit->mediumPresent = true;
unit->flags.mediumPresent = true;
atapi_get_capacity(unit);
ret = true;
} else if (!present && unit->mediumPresent == true) {
} else if (!present && unit->flags.mediumPresent == true) {
unit->changeCount++;
unit->mediumPresent = false;
unit->flags.mediumPresent = false;
unit->logicalSectors = 0;
unit->blockShift = 0;
unit->blockSize = 0;

View File

@ -1,5 +1,5 @@
; reloc.S
; Modified from Open Source A4091.device by Stefan Reinauer and Chris Hooper - https://github.com/A4091/a4091-software
; Modified from Open Source A4091.device by Stefan Reinauer and Chris Hooper - https://github.com/A4091/a3091-software
INCLUDE "exec/memory.i"
INCLUDE "lvo/exec_lib.i"
@ -9,6 +9,12 @@
NUM_ENTRIES EQU 5
; Frame pointer offsets
PHUNKS EQU -4
READHANDLE_TYPE EQU -8
READHANDLE_CURRENT EQU -12
FP_SIZE EQU READHANDLE_CURRENT
; API:
; Zorro ROM access:
; a0: rombase
@ -19,6 +25,7 @@ NUM_ENTRIES EQU 5
public _relocate
_relocate
movem.l d1-d7/a0-a6,-(sp)
link a4,#FP_SIZE
bsr InitHandle
@ -39,7 +46,7 @@ _relocate
; Go through hunk header and allocate all segments
move.l 4,a6 ; execbase
move.l pHunks(pc),a2 ; segment pointers
move.l PHUNKS(a4),a2 ; segment pointers
lea.l 4*NUM_ENTRIES(a2),a3 ; segment lengths
.AllocateLoop
moveq.l #MEMF_PUBLIC,d1
@ -54,7 +61,7 @@ _relocate
move.l d0,(a2)+ ; segment pointer
dbra d7,.AllocateLoop
move.l pHunks(pc),a5
move.l PHUNKS(a4),a5
.HunkLoop
move.l (a5),a0 ; current hunk
bsr RomFetch32
@ -85,6 +92,7 @@ _relocate
ENDIF
bsr .RelocateCleanup
moveq.l #0,d0 ; NULL = Failure
unlk a4
movem.l (sp)+,d1-d7/a0-a6
rts
@ -129,16 +137,16 @@ _relocate
move.l d0,d2 ; num ; number of the hunk the offsets are to point into
lsl.l #2,d2 ; *4 ; offset from Hunks
move.l pHunks(pc),a2
move.l PHUNKS(a4),a2
add.l d2,a2 ; hunk number num
move.l (a2),d3 ; base address of hunk
.Reloc32Tight
bsr RomFetch32
; get baseaddr + d0, add d3 to the data there.
move.l a0,a4
add.l d0,a4
add.l d3,(a4)
move.l a0,a3
add.l d0,a3
add.l d3,(a3)
dbra d1,.Reloc32Tight
bra .HunkReloc32
@ -158,21 +166,21 @@ _relocate
move.l d0,d2 ; num ; number of the hunk the offsets are to point into
lsl.l #2,d2 ; *4 ; offset from Hunks
move.l pHunks(pc),a2
move.l PHUNKS(a4),a2
add.l d2,a2 ; hunk number num
move.l (a2),d3 ; base address of hunk
.Reloc32ShortTight
bsr RomFetch16
; get baseaddr + d0, add d3 to the data there.
move.l a0,a4
add.l d0,a4
add.l d3,(a4)
move.l a0,a3
add.l d0,a3
add.l d3,(a3)
dbra d1,.Reloc32ShortTight
bra .HunkReloc32Short
.HunkLoopPrep
lea ReadHandle(pc),a2
lea READHANDLE_CURRENT(a4),a2
move.l (a2),d0 ; align pointer to longword
addq.l #2,d0 ; in case we had an odd number
and.l #$fffffffc,d0 ; of relocations
@ -202,17 +210,18 @@ _relocate
bsr.s CreateSegList
move.l pHunks(pc),a0
move.l PHUNKS(a4),a0
move.l (a0),d2
subq.l #4,d2
bsr.s .RelocateCleanup
move.l d2,d0
unlk a4
movem.l (sp)+,d1-d7/a0-a6
rts
.RelocateCleanup
move.l pHunks(pc),a1
move.l PHUNKS(a4),a1
moveq.l #(8*NUM_ENTRIES),d0
jsr _LVOFreeMem(a6)
rts
@ -233,7 +242,7 @@ _relocate
; +->| | | | 0| | | | | 0|
; +--+--+--+--+ +--+--+--+--+
CreateSegList:
move.l pHunks(pc),a0
move.l PHUNKS(a4),a0
.NextSeg
move.l (a0),a1 ; Hunks[x]
@ -269,10 +278,10 @@ RomFetch32
RomFetch
movem.l a0-a1/d1-d3,-(sp)
lea ReadHandle(pc),a1
lea READHANDLE_CURRENT(a4),a1
move.l (a1),a0
tst.l 4(a1) ; access type Zorro?
tst.l READHANDLE_TYPE(a4) ; access type Zorro?
bne.s .RomFetchZ
cmp.b #3,d0
@ -310,7 +319,7 @@ RomFetch
rts
InitHandle
; initialize readhandle to beginning of device driver
; initialize READHANDLE_CURRENT to beginning of device driver
; ROM_OFFSET needs to be multiplied by 4 because of the
; nibble mapped nature of the AutoConfig ROM.
; ROM_ADDRESS is passed in a0
@ -320,24 +329,15 @@ InitHandle
ENDIF
add.l d0,a0
lea ReadHandle(pc),a1
move.l a0,(a1) ; memory location passed in a0
move.l d0,4(a1) ; Set access type: 0 = memory, Zorro otherwise
move.l a0,READHANDLE_CURRENT(a4) ; memory location passed in a0
move.l d0,READHANDLE_TYPE(a4) ; Set access type: 0 = memory, Zorro otherwise
moveq.l #(8*NUM_ENTRIES),d0
move.l #MEMF_PUBLIC|MEMF_CLEAR,d1
jsr _LVOAllocMem(a6)
lea pHunks(pc),a1
move.l d0,(a1)
move.l d0,PHUNKS(a4)
rts
ReadHandle
dc.l 0 ; Current address
dc.l 0 ; 0=memory, otherwise Zorro
pHunks
dc.l 0
IFD HAVE_ERRNO
public _rErrno
_rErrno

222
device.c
View File

@ -8,6 +8,7 @@
#include <exec/errors.h>
#include <exec/execbase.h>
#include <exec/resident.h>
#include <hardware/cia.h>
#include <proto/exec.h>
#include <proto/expansion.h>
#include <resources/filesysres.h>
@ -21,9 +22,9 @@
#include "idetask.h"
#include "newstyle.h"
#include "td64.h"
#include "mounter.h"
#include "debug.h"
#include "lide_alib.h"
#include "mounter/mounter.h"
#ifdef NO_AUTOCONFIG
extern UBYTE bootblock, bootblock_end;
@ -81,7 +82,7 @@ char * set_dev_name(struct DeviceBase *dev) {
if (devName == NULL) return NULL;
strcpy(devName + 4,device_name);
}
switch (i) {
case 0:
*(ULONG *)devName = device_prefix[0];
@ -111,33 +112,33 @@ char * set_dev_name(struct DeviceBase *dev) {
* CreateFakeConfigDev
* Create fake ConfigDev and DiagArea to support autoboot without requiring real autoconfig device.
* Adapted from mounter.c by Toni Wilen
*
*
* @param SysBase Pointer to SysBase
* @param ExpansionBase Pointer to ExpansionBase
* @returns Pointer to a ConfigDev struct
*/
struct ConfigDev *CreateFakeConfigDev(struct ExecBase *SysBase, struct Library *ExpansionBase)
{
struct ConfigDev *cd;
struct ConfigDev *cd;
cd = AllocConfigDev();
if (cd) {
cd->cd_BoardAddr = NULL;
cd->cd_BoardSize = 0;
cd->cd_Rom.er_Type = ERTF_DIAGVALID;
ULONG bbSize = &bootblock_end - &bootblock;
ULONG daSize = sizeof(struct DiagArea) + bbSize;
struct DiagArea *diagArea = AllocMem(daSize, MEMF_CLEAR | MEMF_PUBLIC);
if (diagArea) {
diagArea->da_Config = DAC_CONFIGTIME;
diagArea->da_BootPoint = sizeof(struct DiagArea);
diagArea->da_Size = (UWORD)daSize;
cd = AllocConfigDev();
if (cd) {
cd->cd_BoardAddr = NULL;
cd->cd_BoardSize = 0;
cd->cd_Rom.er_Type = ERTF_DIAGVALID;
ULONG bbSize = &bootblock_end - &bootblock;
ULONG daSize = sizeof(struct DiagArea) + bbSize;
struct DiagArea *diagArea = AllocMem(daSize, MEMF_CLEAR | MEMF_PUBLIC);
if (diagArea) {
diagArea->da_Config = DAC_CONFIGTIME;
diagArea->da_BootPoint = sizeof(struct DiagArea);
diagArea->da_Size = (UWORD)daSize;
CopyMem(&bootblock, diagArea+1, bbSize);
// cd_Rom.er_Reserved0c is used as a pointer to diagArea by strap
ULONG *da_Pointer = (ULONG *)&cd->cd_Rom.er_Reserved0c;
*da_Pointer = (ULONG)diagArea;
}
} else {
// cd_Rom.er_Reserved0c is used as a pointer to diagArea by strap
ULONG *da_Pointer = (ULONG *)&cd->cd_Rom.er_Reserved0c;
*da_Pointer = (ULONG)diagArea;
}
} else {
FreeConfigDev(cd);
cd = NULL;
}
@ -235,22 +236,6 @@ static void Cleanup(struct DeviceBase *dev) {
Info("Cleaning up...\n");
struct ExecBase *SysBase = *(struct ExecBase **)4UL;
char *devName = dev->lib.lib_Node.ln_Name;
struct IDEUnit *unit;
if (SysBase->LibNode.lib_Version >= 36) {
ObtainSemaphoreShared(&dev->ulSem);
} else {
ObtainSemaphore(&dev->ulSem);
}
for (unit = (struct IDEUnit *)dev->units.mlh_Head;
unit->mn_Node.mln_Succ != NULL;
unit = (struct IDEUnit *)unit->mn_Node.mln_Succ)
{
unit->cd->cd_Flags |= CDF_CONFIGME;
}
ReleaseSemaphore(&dev->ulSem);
if (dev->ExpansionBase) CloseLibrary((struct Library *)dev->ExpansionBase);
@ -260,11 +245,12 @@ static void Cleanup(struct DeviceBase *dev) {
itask->mn_Node.mln_Succ != NULL;
itask = (struct IDETask *)itask->mn_Node.mln_Succ)
{
itask->cd->cd_Flags |= CDF_CONFIGME;
FreeMem(itask,sizeof(struct IDETask));
}
// if devName doesn't point to the const device_name then we need to free up that memory
if (devName != device_name) {
if (devName != device_name) {
FreeMem(devName,sizeof(device_name)+4);
devName = NULL;
}
@ -338,7 +324,7 @@ static BYTE detectChannels(struct ConfigDev *cd) {
*
* Scan for drives and initialize the driver if any are found
*/
struct Library __attribute__((used, saveds)) * init_device(struct ExecBase *SysBase asm("a6"), BPTR seg_list asm("a0"), struct DeviceBase *dev asm("d0"))
struct Library * init_device(struct ExecBase *SysBase asm("a6"), BPTR seg_list asm("a0"), struct DeviceBase *dev asm("d0"))
{
dev->SysBase = SysBase;
Trace("Init dev, base: %08lx\n",dev);
@ -485,16 +471,16 @@ struct Library __attribute__((used, saveds)) * init_device(struct ExecBase *SysB
}
/*
/*
* device dependent expunge function
* !!! CAUTION: This function runs in a forbidden state !!!
* This call is guaranteed to be single-threaded; only one task
* will execute your Expunge at a time.
*
*
* IMPORTANT: because Expunge is called from the memory allocator,
* it may NEVER Wait() or otherwise take long time to complete.
*/
static BPTR __attribute__((used, saveds)) expunge(struct DeviceBase *dev asm("a6"))
static BPTR expunge(struct DeviceBase *dev asm("a6"))
{
Trace((CONST_STRPTR) "running expunge()\n");
@ -509,13 +495,13 @@ static BPTR __attribute__((used, saveds)) expunge(struct DeviceBase *dev asm("a6
}
/*
/*
* device dependent open function
* !!! CAUTION: This function runs in a forbidden state !!!
* This call is guaranteed to be single-threaded; only one task
* will execute your Open at a time.
* will execute your Open at a time.
*/
static void __attribute__((used, saveds)) open(struct DeviceBase *dev asm("a6"), struct IORequest *ioreq asm("a1"), ULONG unitnum asm("d0"), ULONG flags asm("d1"))
static void open(struct DeviceBase *dev asm("a6"), struct IORequest *ioreq asm("a1"), ULONG unitnum asm("d0"), ULONG flags asm("d1"))
{
struct ExecBase *SysBase = dev->SysBase;
struct IDEUnit *unit = NULL;
@ -563,7 +549,7 @@ static void __attribute__((used, saveds)) open(struct DeviceBase *dev asm("a6"),
ReleaseSemaphore(&dev->ulSem);
if (found == false || unit->present == false) {
if (found == false || unit->flags.present == false) {
error = TDERR_BadUnitNum;
goto exit;
}
@ -622,19 +608,19 @@ static void td_get_geometry(struct IOStdReq *ioreq) {
geometry->dg_TrackSectors = unit->sectorsPerTrack;
geometry->dg_BufMemType = MEMF_PUBLIC;
geometry->dg_DeviceType = unit->deviceType;
geometry->dg_Flags = (unit->atapi) ? DGF_REMOVABLE : 0;
geometry->dg_Flags = (unit->flags.atapi) ? DGF_REMOVABLE : 0;
ioreq->io_Actual = sizeof(struct DriveGeometry);
}
/*
/*
* device dependent close function
* !!! CAUTION: This function runs in a forbidden state !!!
* This call is guaranteed to be single-threaded; only one task
* will execute your Close at a time.
* will execute your Close at a time.
*/
static BPTR __attribute__((used, saveds)) close(struct DeviceBase *dev asm("a6"), struct IORequest *ioreq asm("a1"))
static BPTR close(struct DeviceBase *dev asm("a6"), struct IORequest *ioreq asm("a1"))
{
if (ioreq_is_valid(dev,ioreq)) {
struct IDEUnit *unit = (struct IDEUnit *)ioreq->io_Unit;
@ -695,7 +681,7 @@ const UWORD supported_commands[] =
*
* Handle immediate requests and send any others to ide_task
*/
static void __attribute__((used, saveds)) begin_io(struct DeviceBase *dev asm("a6"), struct IOStdReq *ioreq asm("a1"))
static void begin_io(struct DeviceBase *dev asm("a6"), struct IOStdReq *ioreq asm("a1"))
{
struct ExecBase *SysBase = dev->SysBase;
@ -805,7 +791,7 @@ static void __attribute__((used, saveds)) begin_io(struct DeviceBase *dev asm("a
case CMD_START:
case CMD_STOP:
// Don't pass it to the task if it's not an atapi device
if (!unit->atapi) {
if (!unit->flags.atapi) {
error = 0;
break;
}
@ -878,7 +864,7 @@ sendToTask:
*
* Abort io request
*/
static ULONG __attribute__((used, saveds)) abort_io(struct DeviceBase *dev asm("a6"), struct IOStdReq *ioreq asm("a1"))
static ULONG abort_io(struct DeviceBase *dev asm("a6"), struct IOStdReq *ioreq asm("a1"))
{
struct ExecBase *SysBase = dev->SysBase;
@ -925,69 +911,117 @@ static const ULONG device_vectors[] =
-1 //function table end marker
};
/**
* AdjustBootPriority
*
* Adjusts the boot priority of the first matching device in a mount list. Searches
* for a device node with the specified name and increases its priority by the given
* increment. Optionally checks gameport 1 button state before applying changes.
* Re-orders the device in the priority list after modification.
*
* @param bootname BSTR name to match against device nodes
* @param MountList Pointer to the mount list to traverse
* @param checkFire If true, only modify when gameport 1 button is pressed
* @param increment Amount to increase both device and boot node priorities
*/
void AdjustBootPriority(struct ExecBase *SysBase, char *bootname, struct List *MountList, bool checkFire, int increment) {
volatile struct CIA *ciaa = (struct CIA *)0x0bfe001;
struct BootNode *bn;
struct DeviceNode *dn;
for (bn = (struct BootNode *)MountList->lh_Head;
bn->bn_Node.ln_Succ;
bn = (struct BootNode *)bn->bn_Node.ln_Succ)
{
dn = bn->bn_DeviceNode;
if (dn->dn_Priority != -128)
{
if (L_CompareBSTR(BADDR(dn->dn_Name),bootname)) {
if(!checkFire || (ciaa->ciapra & CIAF_GAMEPORT1)==0) {
dn->dn_Priority+=increment;
bn->bn_Node.ln_Pri+=increment;
Remove((struct Node *)bn);
Enqueue(MountList,(struct Node *)bn);
break;
}
}
}
}
}
/**
* TweakBootList
*
* Modifies boot device priorities in the expansion library mount list. Traverses all
* boot nodes and increases priority (+1) for devices matching "BOOTxx" names (where xx
* is the expansion library major version). Additionally boosts priority (+2) for
* "BOOT00" devices when gameport 1 button is pressed. Skips devices with priority -128.
*
* @param SysBase Pointer to the ExecBase system library base
*/
void TweakBootList(struct ExecBase *SysBase) {
struct ExpansionBase *ExpansionBase;
if (ExpansionBase = (struct ExpansionBase *)OpenLibrary("expansion.library",0)) {
char bootname[] = "\6BOOT00"; // BSTR
UWORD major = (ExpansionBase->LibNode.lib_Version)%100; // we assume version number is under 100, but better safe than sorry
Forbid();
AdjustBootPriority(SysBase,bootname,&ExpansionBase->MountList,true,2);
bootname[5]=0x30+(major/10);
bootname[6]=0x30+(major%10);
AdjustBootPriority(SysBase,bootname,&ExpansionBase->MountList,false,1);
Permit();
CloseLibrary((struct Library *)ExpansionBase);
}
}
/**
* init
*
* Create the device and add it to the system if init_device succeeds
*/
static struct Library __attribute__((used)) * init(BPTR seg_list asm("a0"))
static struct Library * init(BPTR seg_list asm("a0"))
{
struct ExecBase *SysBase = *(struct ExecBase **)4UL;
Info("Init driver.\n");
struct MountStruct *ms = NULL;
struct DeviceBase *mydev = (struct DeviceBase *)MakeLibrary((ULONG *)&device_vectors, // Vectors
NULL, // InitStruct data
(APTR)init_device, // Init function
sizeof(struct DeviceBase), // Library data size
seg_list); // Segment list
BOOL CDBoot = FindCDFS();
if (mydev != NULL) {
ULONG ms_size = (sizeof(struct MountStruct) + (MAX_UNITS * sizeof(struct UnitStruct)));
Info("Add Device.\n");
AddDevice((struct Device *)mydev);
if ((ms = AllocMem(ms_size,MEMF_ANY|MEMF_PUBLIC)) == NULL) goto done;
struct IDETask *itask = (struct IDETask *)mydev->ideTasks.mlh_Head;
ms->deviceName = mydev->lib.lib_Node.ln_Name;
ms->creatorName = NULL;
ms->numUnits = 0;
ms->SysBase = SysBase;
if (!itask->mn_Node.mln_Succ) goto done;
UWORD index = 0;
#if CDBOOT
BOOL CDBoot = FindCDFS();
#endif
struct IDEUnit *unit;
struct MountStruct ms = {
.deviceName = mydev->lib.lib_Node.ln_Name,
.creatorName = mydev->lib.lib_Node.ln_Name,
.SysBase = SysBase,
.cdBoot = CDBoot,
.luns = false,
.slowSpinup = false,
.ignoreLast = true,
.configDev = itask->cd
};
if (SysBase->LibNode.lib_Version >= 36) {
ObtainSemaphoreShared(&mydev->ulSem);
} else {
ObtainSemaphore(&mydev->ulSem);
}
MountDrive(&ms);
for (unit = (struct IDEUnit *)mydev->units.mlh_Head;
unit->mn_Node.mln_Succ != NULL;
unit = (struct IDEUnit *)unit->mn_Node.mln_Succ)
{
if (unit->present == true) {
#if CDBOOT
// If CDFS not resident don't bother adding the CDROM to the mountlist
if (unit->deviceType == DG_CDROM && !CDBoot) continue;
#endif
ms->Units[index].unitNum = unit->unitNum;
ms->Units[index].configDev = unit->cd;
index++;
}
}
ms->numUnits = index;
ReleaseSemaphore(&mydev->ulSem);
if (ms->numUnits > 0) {
MountDrive(ms);
}
FreeMem(ms,ms_size);
if (!seg_list) // Only tweak if we're in boot
TweakBootList(SysBase);
}
done:
return (struct Library *)mydev;

View File

@ -44,8 +44,6 @@ struct Drive {
struct IDEUnit {
struct MinNode mn_Node;
struct Unit io_unit;
struct ConfigDev *cd;
struct ExecBase *SysBase;
struct IDETask *itask;
struct Drive drive;
@ -58,17 +56,8 @@ struct IDEUnit {
volatile UBYTE *shadowDevHead;
volatile void *changeInt;
UBYTE unitNum;
UBYTE channel;
UBYTE deviceType;
UBYTE last_error[6];
bool primary;
bool present;
bool atapi;
bool mediumPresent;
bool mediumPresentPrev;
bool xferMultiple;
bool lba;
bool lba48;
UWORD openCount;
UWORD changeCount;
UWORD heads;
@ -79,6 +68,16 @@ struct IDEUnit {
ULONG logicalSectors;
struct MinList changeInts;
UBYTE multipleCount;
struct {
unsigned char primary : 1;
unsigned char present : 1;
unsigned char atapi : 1;
unsigned char mediumPresent : 1;
unsigned char mediumPresentPrev : 1;
unsigned char xferMultiple : 1;
unsigned char lba : 1;
unsigned char lba48 : 1;
} flags;
};
struct DeviceBase {

View File

@ -165,7 +165,7 @@ static BYTE scsi_read_capacity_ata(struct IDEUnit *unit, struct SCSICmd *scsi_co
if (cdb->flags & 0x01) {
// Partial Medium Indicator - Return end of cylinder
// Implement this so HDToolbox stops moaning about track size
ULONG spc = unit->cylinders * unit->heads;
ULONG spc = unit->sectorsPerTrack * unit->heads;
data->lba = (((cdb->lba / spc) + 1) * spc) - 1;
} else {
data->lba = (unit->logicalSectors) - 1;
@ -213,7 +213,7 @@ static BYTE scsi_mode_sense_ata(struct IDEUnit *unit, struct SCSICmd *scsi_comma
UBYTE idx = 4;
if (page == 0x3F || page == 0x03) {
data[idx++] = 0x03; // Page Code: Format Parameters
data[idx++] = 0x18; // Page length
data[idx++] = 0x16; // Page length
for (int i=0; i <8; i++) {
data[idx++] = 0;
}
@ -221,19 +221,19 @@ static BYTE scsi_mode_sense_ata(struct IDEUnit *unit, struct SCSICmd *scsi_comma
data[idx++] = unit->sectorsPerTrack;
data[idx++] = (unit->blockSize >> 8);
data[idx++] = unit->blockSize;
for (int i=0; i<12; i++) {
for (int i=0; i<10; i++) {
data[idx++] = 0;
}
}
if (page == 0x3F || page == 0x04) {
data[idx++] = 0x04; // Page code: Rigid Drive Geometry Parameters
data[idx++] = 0x17; // Page length
data[idx++] = 0x16; // Page length
data[idx++] = (unit->cylinders >> 16);
data[idx++] = (unit->cylinders >> 8);
data[idx++] = unit->cylinders;
data[idx++] = unit->heads;
for (int i=0; i<19; i++) {
for (int i=0; i<18; i++) {
data[idx++] = 0;
}
}
@ -267,7 +267,7 @@ static BYTE handle_scsi_command(struct IOStdReq *ioreq) {
Trace("SCSI: Command %lx\n",*scsi_command->scsi_Command);
if (unit->atapi == false)
if (unit->flags.atapi == false)
{
// Non-ATAPI drives - Translate SCSI CMD to ATA
switch (scsi_command->scsi_Command[0]) {
@ -422,25 +422,24 @@ static BYTE init_units(struct IDETask *itask) {
for (BYTE i=0; i < 2; i++) {
struct IDEUnit *unit = AllocMem(sizeof(struct IDEUnit),MEMF_ANY|MEMF_CLEAR);
if (unit != NULL) {
// Setup each unit structure
unit->itask = itask;
unit->unitNum = ((itask->boardNum * 4) + (itask->channel << 1) + i);
unit->SysBase = SysBase;
unit->cd = itask->cd;
unit->primary = ((i%2) == 1) ? false : true;
unit->channel = itask->channel;
unit->openCount = 0;
unit->changeCount = 1;
unit->deviceType = DG_DIRECT_ACCESS;
unit->mediumPresent = false;
unit->mediumPresentPrev = false;
unit->present = false;
unit->atapi = false;
unit->xferMultiple = false;
unit->multipleCount = 0;
unit->shadowDevHead = &itask->shadowDevHead;
*unit->shadowDevHead = 0;
unit->itask = itask;
unit->unitNum = ((itask->boardNum * 4) + (itask->channel << 1) + i);
unit->SysBase = SysBase;
unit->flags.primary = ((i%2) == 1) ? false : true;
unit->openCount = 0;
unit->changeCount = 1;
unit->deviceType = DG_DIRECT_ACCESS;
unit->flags.mediumPresent = false;
unit->flags.mediumPresentPrev = false;
unit->flags.present = false;
unit->flags.atapi = false;
unit->flags.xferMultiple = false;
unit->multipleCount = 0;
unit->shadowDevHead = &itask->shadowDevHead;
*unit->shadowDevHead = 0;
// Initialize the change int list
unit->changeInts.mlh_Tail = NULL;
@ -449,8 +448,11 @@ static BYTE init_units(struct IDETask *itask) {
Warn("testing unit %ld\n",unit->unitNum);
if (ata_init_unit(unit)) {
if (unit->atapi) itask->hasRemovables = true;
void *base = itask->cd->cd_BoardAddr;
base += (itask->channel == 0) ? CHANNEL_0 : CHANNEL_1;
if (ata_init_unit(unit,base)) {
if (unit->flags.atapi) itask->hasRemovables = true;
num_units++;
itask->dev->numUnits++;
dev->highestUnit = unit->unitNum;
@ -524,11 +526,11 @@ static void diskchange_check(struct IDETask *itask) {
unit->mn_Node.mln_Succ != NULL;
unit = (struct IDEUnit *)unit->mn_Node.mln_Succ)
{
if (unit->present && unit->atapi) {
if (unit->flags.present && unit->flags.atapi) {
present = (atapi_test_unit_ready(unit,true) == 0) ? true : false;
if (present != unit->mediumPresentPrev) {
if (present != unit->flags.mediumPresentPrev) {
Forbid();
if (unit->changeInt != NULL)
@ -545,7 +547,7 @@ static void diskchange_check(struct IDETask *itask) {
Permit();
}
unit->mediumPresentPrev = present;
unit->flags.mediumPresentPrev = present;
}
@ -585,23 +587,23 @@ static void process_ioreq(struct IDETask *itask, struct IOStdReq *ioreq) {
return;
case CMD_START:
if (unit->atapi) {
if (unit->flags.atapi) {
error = atapi_start_stop_unit(unit,true,false,false);
}
break;
case CMD_STOP:
if (unit->atapi) {
if (unit->flags.atapi) {
error = atapi_start_stop_unit(unit,false,false,false);
}
break;
case TD_EJECT:
if (!unit->atapi) {
if (!unit->flags.atapi) {
error = IOERR_NOCMD;
break;
}
ioreq->io_Actual = (unit->mediumPresent) ? 0 : 1; // io_Actual reflects the previous state
ioreq->io_Actual = (unit->flags.mediumPresent) ? 0 : 1; // io_Actual reflects the previous state
bool insert = (ioreq->io_Length == 0) ? true : false;
@ -611,7 +613,7 @@ static void process_ioreq(struct IDETask *itask, struct IOStdReq *ioreq) {
case TD_CHANGESTATE:
error = 0;
ioreq->io_Actual = 0;
if (unit->atapi) {
if (unit->flags.atapi) {
ioreq->io_Actual = (atapi_test_unit_ready(unit,false) != 0);
break;
}
@ -619,7 +621,7 @@ static void process_ioreq(struct IDETask *itask, struct IOStdReq *ioreq) {
case TD_PROTSTATUS:
error = 0;
if (unit->atapi) {
if (unit->flags.atapi) {
if ((error = atapi_check_wp(unit)) == TDERR_WriteProt) {
error = 0;
ioreq->io_Actual = 1;
@ -660,7 +662,7 @@ validate_etd:
case NSCMD_TD_FORMAT64:
direction = WRITE;
transfer:
if (unit->atapi == true && unit->mediumPresent == false) {
if (unit->flags.atapi == true && unit->flags.mediumPresent == false) {
Trace("Access attempt without media\n");
error = TDERR_DiskChanged;
break;
@ -681,7 +683,7 @@ transfer:
break;
}
if (unit->atapi == true) {
if (unit->flags.atapi == true) {
error = atapi_translate(ioreq->io_Data, lba, count, &ioreq->io_Actual, unit, direction);
} else {
if (direction == READ) {

View File

@ -7,6 +7,7 @@
#include <exec/nodes.h>
#include <exec/ports.h>
#include <proto/exec.h>
#include <stdbool.h>
#include <string.h>
#include "debug.h"
@ -18,9 +19,9 @@
/**
* L_NewList
*
*
* Initialize a new list
*
*
* @param new_list Pointer to a new list
*/
void L_NewList(struct List *new_list) {
@ -31,9 +32,9 @@ void L_NewList(struct List *new_list) {
/**
* L_CreatePort
*
*
* Create a new MsgPort
*
*
* @param name (optional) name of the port
* @param priority priority of the port
* @returns pointer to a MsgPort
@ -49,7 +50,7 @@ struct MsgPort *L_CreatePort(STRPTR name, LONG pri) {
mp->mp_Node.ln_Name = name;
mp->mp_SigBit = sigNum;
mp->mp_SigTask = FindTask(0);
L_NewList(&mp->mp_MsgList);
if (mp->mp_Node.ln_Name)
@ -62,9 +63,9 @@ struct MsgPort *L_CreatePort(STRPTR name, LONG pri) {
/**
* L_DeletePort
*
*
* Delete a MsgPort
*
*
* @param mp Pointer to a MsgPort
*/
void L_DeletePort(struct MsgPort *mp) {
@ -73,7 +74,7 @@ void L_DeletePort(struct MsgPort *mp) {
if (mp) {
if (mp->mp_Node.ln_Name)
RemPort(mp);
FreeSignal(mp->mp_SigBit);
FreeMem(mp,sizeof(struct MsgPort));
}
@ -81,9 +82,9 @@ void L_DeletePort(struct MsgPort *mp) {
/**
* L_CreateExtIO
*
*
* Create an Extended IO Request
*
*
* @param mp Pointer to the reply port
* @param size Size of the IO request
* @return pointer to the IO request
@ -105,9 +106,9 @@ struct IORequest* L_CreateExtIO(struct MsgPort *mp, ULONG size) {
/**
* L_CreateStdIO
*
*
* Create a standard IO Request
*
*
* @param mp Pointer to the reply port
* @return Pointer to an IOStdReq
*/
@ -117,9 +118,9 @@ struct IOStdReq* L_CreateStdIO(struct MsgPort *mp) {
/**
* L_DeleteExtIO
*
*
* Delete an Extended IO Request
*
*
* @param ior Pointer to an IORequest
*/
void L_DeleteExtIO(struct IORequest *ior) {
@ -133,9 +134,9 @@ void L_DeleteExtIO(struct IORequest *ior) {
/**
* L_DeleteStdIO
*
*
* Delete a Standard IO Requesrt
*
*
* @param ior Pointer to an IOStdReq
*/
void L_DeleteStdIO(struct IOStdReq *ior) {
@ -192,4 +193,43 @@ struct Task *L_CreateTask(char * taskName, LONG priority, APTR funcEntry, ULONG
AddTask(task,funcEntry,NULL);
return task;
}
/**
* L_Uppercase
* Convert lowercase char to uppercase
*
* @param c char to uppercase
* @returns char
*/
char L_UpperCase(char c) {
if (c >= 'a' && c <= 'z')
c -= ('a'-'A');
return c;
}
/**
* L_CompareBSTR
* Compare two BSTRs
*
* @param str1 String 1
* @param str2 String 2
* @returns bool Equality
*/
bool L_CompareBSTR(char *str1, char *str2) {
UBYTE len1 = str1[0];
UBYTE len2 = str2[0];
if (len1 != len2) {
return false;
}
for (int i=1; i<=len1; i++) {
if (L_UpperCase(str1[i]) != L_UpperCase(str2[i]))
return false;
}
return true;
}

View File

@ -19,4 +19,6 @@ struct IOStdReq* L_CreateStdIO(struct MsgPort *mp);
void L_DeleteExtIO(struct IORequest *ior);
void L_DeleteStdIO(struct IOStdReq *ior);
struct Task *L_CreateTask(char * taskName, LONG priority, APTR funcEntry, ULONG stackSize, APTR userData);
bool L_CompareBSTR(char *str1, char *str2);
char L_UpperCase(char c);
#endif

View File

@ -304,15 +304,20 @@ static void setup_liv2_board(struct ideBoard *board) {
/**
* promptUser
*
* Ask if the user wants to update this board
* Ask if the user wants to update/erase this board
* @param config pointer to the config struct
* @return boolean true / false
*/
static bool promptUser(struct Config *config) {
static bool promptUser(struct Config *config, bool update) {
int c;
char answer = 'y'; // Default to yes
printf("Update this device? (Y)es/(n)o/(a)ll: ");
if (update) {
printf("Update this device?");
} else {
printf("Erase flash?");
}
printf(" (Y)es/(n)o/(a)ll: ");
if (config->assumeYes) {
printf("y\n");
@ -334,7 +339,7 @@ static bool promptUser(struct Config *config) {
/**
* assemble_rom
*
* Given either lide.rom or lide-atbus.rom - adjust the file for the currebt board
* Given either lide.rom or lide-atbus.rom - adjust the file for the current board
* This is needed because lideflash will update all compatible boards, and can only be supplied with one filename
*/
static void assemble_rom(char *src_buffer, char *dst_buffer, ULONG bufSize, enum BOOTROM dstRomType) {
@ -367,6 +372,90 @@ static void assemble_rom(char *src_buffer, char *dst_buffer, ULONG bufSize, enum
}
}
/**
* find_lide_version
*
* Search for the id string in the buffer and return the offset if found
* @param buffer the buffer to search
* @param romSize Size of the ROM
* @returns offset. negative if not found
*/
static int find_lide_version(const unsigned char *buffer, ULONG romSize) {
const char *needle = "lide ";
size_t needle_len = strlen(needle);
// We can only search up to (romSize - needle_len)
for (size_t i = 0; i <= romSize - needle_len; i++) {
if (memcmp(buffer + i, needle, needle_len) == 0) {
return (int)i;
}
}
return -1; // Not found
}
/**
* printVersion
*
* Print the version string without the git information
*
* @param version Pointer to the version string
*/
static void printVersion(char *version) {
char *temp;
int len = strcspn(version,")");
len++; // Leave space for null terminator
if ((temp = AllocMem(len,MEMF_ANY))) {
temp[len] = 0;
strncpy(temp,version,len);
printf("%s\n",temp);
FreeMem(temp,len);
}
}
/**
* printCurrentVersion
*
* Print the current version of lide the board is running
* It first tries to get this using the cd_Driver pointer in the struct
*
* Older driver versions didn't set this, in that case:
* 1. Traverse the eb_Mountlist looking for a device bound to this configDev
* 2. Peek into it's BootNode->DeviceNode->FSSM to get the device name
* 3. Find the device in SysBase->DeviceList
*
* @param cd ConfigDev pointer
*/
void printCurrentVersion(struct ConfigDev *cd) {
struct BootNode *bn;
struct DeviceNode *dn;
struct FileSysStartupMsg *fssm;
struct Device *device;
char *deviceName = NULL;
if ((device = cd->cd_Driver) == NULL) {
// Old version of lide didn't set cd_Driver
// Get it the hard way
for (bn = (struct BootNode *)ExpansionBase->MountList.lh_Head;
bn->bn_Node.ln_Succ;
bn = (struct BootNode *)bn->bn_Node.ln_Succ)
{
if (cd == (struct ConfigDev *)bn->bn_Node.ln_Name) {
dn = bn->bn_DeviceNode;
fssm = BADDR(dn->dn_Startup);
deviceName = (char *)BADDR(fssm->fssm_Device) + 1; // NULL terminated BSTR
device = (struct Device *)FindName(&SysBase->DeviceList,deviceName);
break;
}
}
}
if (device) {
printVersion(device->dd_Library.lib_IdString);
} else {
printf("Unknown\n");
}
}
int main(int argc, char *argv[])
{
SysBase = *((struct ExecBase **)4UL);
@ -379,6 +468,8 @@ int main(int argc, char *argv[])
void *driver_buffer2 = NULL;
void *misc_buffer = NULL;
char *ver = NULL;
ULONG romSize = 0;
ULONG miscSize = 0;
@ -424,6 +515,15 @@ int main(int argc, char *argv[])
rc = 5;
goto exit;
}
int ver_offset = find_lide_version(driver_buffer,romSize);
if (ver_offset > 0) {
ver = (char *)driver_buffer + ver_offset;
if (ver) {
printf("New version: ");
printVersion(ver);
}
}
}
if (config->misc_filename) {
@ -463,11 +563,12 @@ int main(int argc, char *argv[])
devsInhibited = true;
printf("\n");
if ((ExpansionBase = (struct ExpansionBase *)OpenLibrary("expansion.library",0)) != NULL) {
struct ConfigDev *cd = NULL;
struct ideBoard board;
while ((cd = FindConfigDev(cd,-1,-1)) != NULL) {
board.cd = cd;
@ -575,11 +676,19 @@ int main(int argc, char *argv[])
continue; // Skip this board
}
printf(" at Address 0x%06x\n",(int)cd->cd_BoardAddr);
printf(" at Address 0x%06X\n",(int)cd->cd_BoardAddr);
boards_found++;
if (config->ide_rom_filename) {
printf("Current version: ");
printCurrentVersion(cd);
}
printf("\n");
bool update = (config->ide_rom_filename != NULL|| config->misc_filename != NULL);
// Ask the user if they wish to update this board
if (!promptUser(config)) continue;
if (!promptUser(config, update)) continue;
suspend_lide(true);

View File

@ -161,14 +161,14 @@ static void DumpUnit(struct IOStdReq *req) {
printf("Device Type: %d\n", unit->deviceType);
printf("Transfer method: %d\n", unit->xferMethod);
printf("Primary: %s\n", (unit->primary) ? "Yes" : "No");
printf("ATAPI: %s\n", (unit->atapi) ? "Yes" : "No");
printf("Medium Present: %s\n", (unit->mediumPresent) ? "Yes" : "No");
printf("Supports LBA: %s\n", (unit->lba) ? "Yes" : "No");
printf("Supports LBA48: %s\n", (unit->lba48) ? "Yes" : "No");
printf("Primary: %s\n", (unit->flags.primary) ? "Yes" : "No");
printf("ATAPI: %s\n", (unit->flags.atapi) ? "Yes" : "No");
printf("Medium Present: %s\n", (unit->flags.mediumPresent) ? "Yes" : "No");
printf("Supports LBA: %s\n", (unit->flags.lba) ? "Yes" : "No");
printf("Supports LBA48: %s\n", (unit->flags.lba48) ? "Yes" : "No");
printf("C/H/S: %d/%d/%d\n", unit->cylinders, unit->heads, unit->sectorsPerTrack);
printf("Logical Sectors: %ld\n", (long int)unit->logicalSectors);
printf("READ/WRITE Multiple: %s\n", (unit->xferMultiple) ? "Yes" : "No");
printf("READ/WRITE Multiple: %s\n", (unit->flags.xferMultiple) ? "Yes" : "No");
printf("Multiple count: %d\n", unit->multipleCount);
printf("Last Error: ");
for (int i=0; i<6; i++) {
@ -298,12 +298,12 @@ static void setMultiple(struct IOStdReq *req, int multiple) {
struct IDEUnit *unit = (struct IDEUnit *)req->io_Unit;
if (multiple > 0) {
unit->xferMultiple = true;
unit->flags.xferMultiple = true;
unit->multipleCount = multiple;
printf("set multiple transfer: %d\n",multiple);
} else {
printf("Transfer multiple disabled.\n");
unit->xferMultiple = false;
unit->flags.xferMultiple = false;
unit->multipleCount = 1;
}

212
mounter/README.md Normal file
View File

@ -0,0 +1,212 @@
# Mounter: AmigaOS Generic Mounter
Generic autoboot / automount RDB parser and mounter.
This is the mounter code from a4091.device.
## 1. Introduction
This Mounter software is a generic, autoboot/automount Rigid Disk Block (RDB)
parser and mounter for AmigaOS. Originally developed by Toni Wilen and extended
by Stefan Reinauer and Matt Harlum. It is used by the `a4091.device` and
'lide.device' drivers. This software is engineered to be a robust and highly
compatible solution for device drivers that need to mount partitions and
filesystems at boot time.
It is designed to be highly portable, 68000-compatible, and capable of
operating in diverse environments, from a Kickstart 1.3 Boot ROM to a modern
AmigaOS system. Its primary function is to scan for storage devices, interpret
their partition maps (RDB, MBR, GPT), load the necessary filesystems, and make
them available to the operating system.
---
## 2. Core Features
The Mounter provides a comprehensive set of features making it a versatile
solution:
* **Broad OS Compatibility**: Full support for Kickstart 1.3 and newer,
including its specific autoboot mechanisms.
* **CPU Compatibility**: Compatible with the Motorola 68000 processor, ensuring
it runs on all classic Amiga models.
* **Autoboot Capability**: Can participate in the Amiga's autoconfig boot
process to mount bootable partitions
* **Full Automount Support**: Automatically finds and mounts all valid
partitions it discovers.
* **Rich Filesystem Support**:
* **RDB**: Full support for the Amiga Rigid Disk Block (RDB) standard.
* **MBR & GPT**: Support for PC-style Master Boot Record and GUID Partition
Table layouts, enhancing cross-platform compatibility.
* **CD-ROM**: Capable of booting from CD-ROMs following the ISO 9660
standard with Amiga-specific "Amiga Boot" or "CDTV" system identifiers.
* **LUN Support**: Can scan for and mount devices on multiple Logical Unit
Numbers (LUNs).
---
## 3. High-Level Architecture
The Mounter's architecture is centered around a single primary entry point,
`MountDrive()`, which orchestrates the entire mounting process. It operates
by opening a specified device driver, scanning its units, and attempting to
identify and mount partitions.
### 3.1. Key Data Structures
* **`struct MountStruct` (`mounter.h`)**: This is the main input structure
passed to the `MountDrive` function. It defines the parameters for a mounting
session.
* `deviceName`: The name of the device driver to use (e.g., `scsi.device`).
* `unitNum`: A pointer to an array of unit numbers to scan.
* `creatorName`: A string to identify the creator of the filesystem entries.
* `configDev`: A pointer to the `ConfigDev` structure for an autoconfig
board, essential for autobooting.
* `luns`, `slowSpinup`, `cdBoot`, `ignoreLast`: Boolean flags to control
behavior like LUN scanning, spin-up delays, CD booting, and handling of
the RDB `RDBFF_LAST` flag.
* `SysBase`: A pointer to the Exec library base.
* **`struct MountData` (`mounter.c`)**: An internal state-management structure
used during the `MountDrive` execution. It holds pointers to opened
libraries, the I/O request, device geometry, and state variables for the
scanning process.
### 3.2. Control Flow
The logical flow of the `MountDrive` function is as follows:
1. **Initialization**:
* Allocate a `MountData` structure to hold operational state.
* Open required system libraries (`expansion.library`, `dos.library`).
* Create a message port and an I/O request for communicating with the
device driver.
2. **Device Scanning Loop**:
* Iterate through the specified SCSI targets (and LUNs, if enabled).
* For each unit, attempt to `OpenDevice()`.
3. **Partition Scheme Identification**:
* If a device is opened successfully, get its geometry using
`TD_GETGEOMETRY`.
* Based on the device type (`DG_DIRECT_ACCESS`, `DG_CDROM`), determine
the scanning strategy.
* **For Direct Access Devices**:
1. Attempt to find an RDB by scanning the first `RDB_LOCATION_LIMIT`
blocks (`ScanRDSK`).
2. If no RDB is found, check for a GUID Partition Table (GPT) at
block 1 (`ScanMBR`).
3. If no GPT is found, check for a Master Boot Record (MBR) at block
0 (`ScanMBR`).
* **For CD-ROM Devices**:
1. Check if the unit is ready and contains a data CD (`ScanCDROM`).
2. Check the Primary Volume Descriptor (PVD) for "AMIGA BOOT" or
"CDTV" identifiers (`CheckPVD`).
3. If not an Amiga bootable CD, fall back to scanning for an RDB
(`ScanRDSK`).
4. **Partition Processing**:
* Call the appropriate parsing function (`ParseRDSK`, `ParseGPT`,
`ParseMBR`, `ScanCDROM`).
* These functions iterate through partition entries.
5. **Filesystem Loading & Mounting**:
* For each valid partition, `ParsePART` is called.
* It reads the partition's `DosEnvec` to determine the required filesystem
`DosType`.
* It calls `ParseFSHD` to find or load the required filesystem. `ParseFSHD`
searches `FileSystem.resource` and, if not found or if the version is
older, loads the filesystem from the disk's FileSystem Header Blocks
(`FSHD`).
* The loaded filesystem code is relocated in memory using the `fsrelocate`
function.
* A `DeviceNode` is created using `MakeDosNode()` and populated with the
partition's parameters.
* The `DeviceNode` is added to the system's mount list using `AddDosNode()`
or `AddBootNode()` (for bootable partitions).
6. **Cleanup**:
* The device is closed.
* The loop continues to the next unit/target.
* After the loop, all allocated resources (I/O requests, ports, library
bases) are freed.
---
## 4. Core Functionality Details
### 4.1. Filesystem Relocation (`fsrelocate`)
A critical function is `fsrelocate`, which loads Amiga HUNK-formatted
filesystem binaries from disk into memory.
1. It starts by reading a `HUNK_HEADER`.
2. It pre-allocates memory for all hunks (`HUNK_CODE`, `HUNK_DATA`,
`HUNK_BSS`) defined in the header.
3. It reads each hunk's data from the LSEG (Load Segment) blocks on disk.
4. It processes relocation hunks (`HUNK_RELOC32`) to resolve memory address
pointers within the loaded code and data, making the filesystem executable
from its new location in RAM.
5. Finally, it performs a `CacheClearU()` to ensure instruction caches are
flushed before the OS attempts to execute the newly loaded code.
### 4.2. Filesystem Resource Management (`FSHDProcess`, `FSHDAdd`)
The mounter interacts carefully with the central `FileSystem.resource`.
* `FSHDProcess` is responsible for checking if a required filesystem
(identified by `DosType`) is already present in the resource list.
* If a filesystem is not present, or if the version on disk is newer than the
one in memory, it allocates a new `FileSysEntry`.
* After a filesystem is successfully loaded and relocated by `fsrelocate`,
`FSHDAdd` adds the new `FileSysEntry` to `FileSystem.resource`,
making it available for all subsequent mounting operations
system-wide.
* If `FileSystem.resource` does not exist (common on KS 1.3), it is created.
### 4.3. Kickstart 1.3 Compatibility
To maintain compatibility with Kickstart 1.3, which lacks certain OS functions,
the mounter includes its own implementations:
* `W_CreateMsgPort` / `W_DeleteMsgPort`
* `W_CreateIORequest` / `W_DeleteIORequest`
These functions use `AllocMem()` to create the necessary structures and manage
signals manually, mimicking the behavior of their modern counterparts. For
autobooting, it manually creates and inserts a `BootNode` into the
`expansion.library`'s `MountList`, as `AddBootNode` is not fully featured on KS
1.3.
### 4.4. Autoboot Process
When a bootable partition is found and the mounter is operating in an autoboot
context (i.e., a `configDev` is provided), it uses `AddBootNode()` instead of
`AddDosNode()`. This function links the mountable partition directly to the
autoconfig hardware board, signaling to the OS that this is a candidate for
booting. If no `configDev` is provided, the mounter can create a "fake" one to
enable autobooting from devices that are not on a standard autoconfig chain.
---
## 5. Dependencies
The Mounter relies on the following standard AmigaOS libraries:
* `exec.library` (v34+): For memory management, tasking, ports, and core system
functions.
* `expansion.library` (v34+): For adding boot nodes and interacting with the
autoconfig process.
* `dos.library` (v34+): For creating `DeviceNode` structures and adding them
to the DOS list.
---
## 6. License
Copyright 2021-2022 Toni Wilen
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,16 @@
#ifndef MOUNTER_H
#define MOUNTER_H
struct UnitStruct
{
ULONG unitNum;
struct ConfigDev *configDev;
};
struct MountStruct
{
// Device name. ("myhddriver.device")
// Offset 0.
UBYTE *deviceName;
// Number of units
UWORD numUnits;
const UBYTE *deviceName;
// Unit number pointer or single integer value.
// if >= 0x100 (256), pointer to array of ULONGs, first ULONG is number of unit numbers followed (for example { 2, 0, 1 }. 2 units, unit numbers 0 and 1).
// if < 0x100 (256): used as a single unit number value.
// Offset 4.
ULONG *unitNum;
// Name string used to set Creator field in FileSystem.resource (if KS 1.3) and in FileSystem.resource entries.
// If NULL: use device name.
// Offset 8.
@ -25,17 +22,23 @@ struct MountStruct
// SysBase.
// Offset 16.
struct ExecBase *SysBase;
// Array of UnitStructs
struct UnitStruct Units[];
// LUNs
// Offset 20.
BOOL luns;
// Short/Long Spinup
// Offset 22.
BOOL slowSpinup;
// Enahle CD Boot
BOOL cdBoot;
// Ignore RDBFF_LAST flag
BOOL ignoreLast;
};
APTR W_CreateIORequest(struct MsgPort *ioReplyPort, ULONG size, struct ExecBase *SysBase);
void W_DeleteIORequest(APTR iorequest, struct ExecBase *SysBase);
struct MsgPort *W_CreateMsgPort(struct ExecBase *SysBase);
void W_DeleteMsgPort(struct MsgPort *port, struct ExecBase *SysBase);
int mount_drives(struct ConfigDev *cd, char *devName, struct MountStruct *ms);
LONG MountDrive(struct MountStruct *ms);
#endif
#endif

31
mounter/ndkcompat.h Normal file
View File

@ -0,0 +1,31 @@
//
// Copyright 2022-2025 Stefan Reinauer
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
#ifndef __NDK_COMPAT_H
#define __NDK_COMPAT_H
#include <inttypes.h>
/* ULONG has changed from NDK 3.9 to NDK 3.2.
* However, PRI*32 did not. What is the right way to implement this?
*/
#if INCLUDE_VERSION < 47
#undef PRIu32
#define PRIu32 "lu"
#undef PRId32
#define PRId32 "ld"
#undef PRIx32
#define PRIx32 "lx"
#endif
#endif

View File

@ -1,18 +0,0 @@
#ifndef __NDK_COMPAT_H
#define __NDK_COMPAT_H
#include <inttypes.h>
/* ULONG has changed from NDK 3.9 to NDK 3.2.
* However, PRI*32 did not. What is the right way to implement this?
*/
#if INCLUDE_VERSION < 47
#undef PRIu32
#define PRIu32 "lu"
#undef PRId32
#define PRId32 "ld"
#undef PRIx32
#define PRIx32 "lx"
#endif
#endif