mirror of
https://github.com/LIV2/a4091-software.git
synced 2025-12-05 22:12:44 +00:00
This change brings support for a second filesystem stored in the ROM (if there's enough space) The romfile code is fairly local so there was no point in keeping the files around in a4091_save_t. Now the file state is kept locally in init_romfiles() instead. Since not all filesystem drivers contain a resident init structure, it is now also possible to specify a DosType that is used to register the filesystem in filesystem.resource.
370 lines
11 KiB
C
370 lines
11 KiB
C
#ifdef DEBUG_DEVICE
|
|
#define USE_SERIAL_OUTPUT
|
|
#endif
|
|
#include "port.h"
|
|
#include <devices/trackdisk.h>
|
|
#include <dos/dostags.h>
|
|
#include <exec/errors.h>
|
|
#include <exec/execbase.h>
|
|
#include <exec/resident.h>
|
|
#include <exec/semaphores.h>
|
|
#include <exec/io.h>
|
|
#include <libraries/dos.h>
|
|
#include <proto/dos.h>
|
|
#include <proto/exec.h>
|
|
#include <proto/expansion.h>
|
|
#include <utility/tagitem.h>
|
|
#include <clib/alib_protos.h>
|
|
#include <devices/scsidisk.h>
|
|
#include <string.h>
|
|
|
|
#include "device.h"
|
|
#include "cmdhandler.h"
|
|
#include "version.h"
|
|
#include "mounter.h"
|
|
#include "bootmenu.h"
|
|
#include "romfile.h"
|
|
#include "attach.h"
|
|
|
|
#ifdef DEBUG
|
|
#include <clib/debug_protos.h>
|
|
#endif
|
|
|
|
#define STR(s) #s // Turn s into a string literal without macro expansion
|
|
#define XSTR(s) STR(s) // Turn s into a string literal after macro expansion
|
|
|
|
#define DEVICE_PRIORITY 10 // Fine to leave priority as zero
|
|
|
|
#define DEVICE_NAME "a4091.device"
|
|
|
|
struct ExecBase *SysBase;
|
|
struct MsgPort *myPort;
|
|
|
|
static BPTR saved_seg_list;
|
|
|
|
/*
|
|
* -----------------------------------------------------------
|
|
* A library or device with a romtag should start with moveq #-1,d0
|
|
* (to safely return an error if a user tries to execute the file),
|
|
* followed by a Resident structure.
|
|
* ------------------------------------------------------------
|
|
*/
|
|
int __attribute__((no_reorder)) _start()
|
|
{
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* -----------------------------------------------------------
|
|
* A romtag structure. After the driver is brought in from disk, the
|
|
* disk image will be scanned for this structure to discover magic
|
|
* constants about it (such as where to start running from...).
|
|
*
|
|
* endcode is a marker that shows the end of your code. Make sure it
|
|
* does not span hunks, and is not before the rom tag! It is ok to
|
|
* put it right after the rom tag -- that way you it's always safe.
|
|
* Make sure the program has only a single code hunk if you put it
|
|
* at the end of your code.
|
|
* ------------------------------------------------------------
|
|
*/
|
|
asm("romtag: \n"
|
|
" dc.w "XSTR(RTC_MATCHWORD)" \n"
|
|
" dc.l romtag \n"
|
|
" dc.l endcode \n"
|
|
" dc.b 0 \n"
|
|
" dc.b "XSTR(DEVICE_VERSION)" \n"
|
|
" dc.b "XSTR(NT_DEVICE)" \n"
|
|
" dc.b "XSTR(DEVICE_PRIORITY)" \n"
|
|
" dc.l _device_name \n"
|
|
" dc.l _device_id_string \n"
|
|
" dc.l _init \n"
|
|
"endcode: \n");
|
|
|
|
static const char device_name[] = DEVICE_NAME;
|
|
char real_device_name[17];
|
|
static struct SignalSemaphore entry_sem;
|
|
int romboot = FALSE;
|
|
|
|
/*
|
|
* get_device_name
|
|
* ---------------
|
|
* Creates 2nd.a4091.device, 3rd.a4091.device etc
|
|
*/
|
|
static char *get_device_name(void)
|
|
{
|
|
char *name = real_device_name;
|
|
for (int i=0; i<8; i++) {
|
|
if (i) {
|
|
name[0]='0'+i+1;
|
|
if (i==1) name[1]='n';
|
|
else if (i==2) name[1]='r';
|
|
else name[1]='t';
|
|
if (i==1 || i==2) name[2]='d';
|
|
else name[2]='h';
|
|
name[3]='.';
|
|
name[4]=0;
|
|
} else
|
|
name[0]=0;
|
|
strcat(name, device_name);
|
|
|
|
#ifdef TEST_2ND_DEVICE
|
|
if (i==1)
|
|
#else
|
|
if (FindName(&SysBase->DeviceList, name)==NULL)
|
|
#endif
|
|
break;
|
|
}
|
|
return name;
|
|
}
|
|
|
|
/*
|
|
* ------- init_device ---------------------------------------
|
|
* FOR RTF_AUTOINIT:
|
|
* This routine gets called after the device has been allocated.
|
|
* The device pointer is in d0. The AmigaDOS segment list is in a0.
|
|
* If it returns the device pointer, then the device will be linked
|
|
* into the device list. If it returns NULL, then the device
|
|
* will be unloaded.
|
|
*
|
|
* IMPORTANT:
|
|
* If you don't use the "RTF_AUTOINIT" feature, there is an additional
|
|
* caveat. If you allocate memory in your Open function, remember that
|
|
* allocating memory can cause an Expunge... including an expunge of your
|
|
* device. This must not be fatal. The easy solution is don't add your
|
|
* device to the list until after it is ready for action.
|
|
*
|
|
* CAUTION:
|
|
* This function runs in a forbidden state !!!
|
|
* This call is single-threaded by Exec
|
|
* ------------------------------------------------------------
|
|
*/
|
|
static struct Library __used __saveds *
|
|
init_device(BPTR seg_list asm("a0"), struct Library *dev asm("d0"))
|
|
{
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Warray-bounds="
|
|
/* !!! required !!! save a pointer to exec */
|
|
SysBase = *(struct ExecBase **)4UL;
|
|
#pragma GCC diagnostic pop
|
|
|
|
/* save pointer to our loaded code (the SegList) */
|
|
saved_seg_list = seg_list;
|
|
|
|
get_device_name();
|
|
dev->lib_Node.ln_Type = NT_DEVICE;
|
|
dev->lib_Node.ln_Name = real_device_name;
|
|
dev->lib_Flags = LIBF_SUMUSED | LIBF_CHANGED;
|
|
dev->lib_Version = DEVICE_VERSION;
|
|
dev->lib_Revision = DEVICE_REVISION;
|
|
dev->lib_IdString = (APTR)device_id_string;
|
|
|
|
/* Start thread to manage board and process commands */
|
|
InitSemaphore(&entry_sem);
|
|
ObtainSemaphore(&entry_sem);
|
|
printf("A4091: %s %s\n", device_name, device_id_string);
|
|
dev->lib_OpenCnt++;
|
|
|
|
int board_num = 0;
|
|
if (start_cmd_handler(&board_num)) {
|
|
printf("Start handler failed\n");
|
|
dev->lib_OpenCnt--;
|
|
ReleaseSemaphore(&entry_sem);
|
|
return (NULL);
|
|
}
|
|
|
|
dev->lib_OpenCnt--;
|
|
ReleaseSemaphore(&entry_sem);
|
|
|
|
return (dev);
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
static BPTR __used __saveds
|
|
drv_expunge(struct Library *dev asm("a6"))
|
|
{
|
|
ObtainSemaphore(&entry_sem);
|
|
|
|
if ((dev->lib_OpenCnt != 0) || periph_still_attached()) {
|
|
printf("expunge() device still open\n");
|
|
dev->lib_Flags |= LIBF_DELEXP; // Indicate I'll expunge myself later
|
|
ReleaseSemaphore(&entry_sem);
|
|
return (0);
|
|
}
|
|
|
|
printf("expunge() %s\n", version_str);
|
|
|
|
/* Ask the command handler to shut down */
|
|
stop_cmd_handler();
|
|
|
|
Forbid();
|
|
BPTR seg_list = saved_seg_list;
|
|
Remove(&dev->lib_Node);
|
|
FreeMem((char *)dev - dev->lib_NegSize,
|
|
dev->lib_NegSize + dev->lib_PosSize);
|
|
ReleaseSemaphore(&entry_sem);
|
|
return (seg_list);
|
|
}
|
|
|
|
/*
|
|
* drv_open
|
|
* --------
|
|
* Opens the device for access. The first call of this function
|
|
* will perform an attach (initialize) of the SCSI adapter.
|
|
*
|
|
* ------------------------------------------------------------
|
|
*
|
|
* CAUTION: This function runs in Forbid() state. It is guaranteed
|
|
* to be single-threaded; only one task will execute Open()
|
|
* at a time.
|
|
*/
|
|
void __used __saveds
|
|
drv_open(struct Library *dev asm("a6"), struct IORequest *ioreq asm("a1"),
|
|
uint scsi_unit asm("d0"), ULONG flags asm("d1"))
|
|
{
|
|
int rc;
|
|
ioreq->io_Message.mn_Node.ln_Type = NT_REPLYMSG;
|
|
|
|
if (SysBase->LibNode.lib_Version < 36) {
|
|
ioreq->io_Error = IOERR_OPENFAIL;
|
|
return; /* can only run under 2.0 or greater */
|
|
}
|
|
|
|
ObtainSemaphore(&entry_sem);
|
|
dev->lib_OpenCnt++;
|
|
|
|
if ((rc = open_unit(scsi_unit, (void **) &ioreq->io_Unit, flags)) != 0) {
|
|
printf("Open fail %d.%d\n", scsi_unit % 10, scsi_unit / 10);
|
|
dev->lib_OpenCnt--;
|
|
ioreq->io_Error = rc;
|
|
ReleaseSemaphore(&entry_sem);
|
|
return;
|
|
}
|
|
#if 0
|
|
printf("Open Unit=%p\n", ioreq->io_Unit);
|
|
#endif
|
|
|
|
ioreq->io_Error = 0; // Success
|
|
|
|
ReleaseSemaphore(&entry_sem);
|
|
}
|
|
|
|
/*
|
|
* drv_close
|
|
* ---------
|
|
* Closes the device. The last call of this function will perform an detach
|
|
* (reset) of the SCSI adapter.
|
|
*
|
|
* ------------------------------------------------------------
|
|
*
|
|
* CAUTION: This function runs in Forbid() state. It is guaranteed
|
|
* to be single-threaded; only one task will execute Close()
|
|
* at a time.
|
|
*/
|
|
static BPTR __used __saveds
|
|
drv_close(struct Library *dev asm("a6"), struct IORequest *ioreq asm("a1"))
|
|
{
|
|
ObtainSemaphore(&entry_sem);
|
|
#if 0
|
|
printf("close %u\n", dev->lib_OpenCnt);
|
|
#endif
|
|
|
|
close_unit((void *) ioreq->io_Unit);
|
|
|
|
dev->lib_OpenCnt--;
|
|
ReleaseSemaphore(&entry_sem);
|
|
if (dev->lib_Flags & LIBF_DELEXP)
|
|
return (drv_expunge(dev));
|
|
return (0);
|
|
}
|
|
|
|
/* device dependent beginio function */
|
|
static void __used __saveds
|
|
drv_begin_io(struct Library *dev asm("a6"), struct IORequest *ior asm("a1"))
|
|
{
|
|
/* These commands are forced to always execute in immediate mode */
|
|
switch (ior->io_Command) {
|
|
case TD_REMCHANGEINT:
|
|
printf("TD_REMCHANGEINT\n");
|
|
td_remchangeint(ior);
|
|
return;
|
|
case CMD_START:
|
|
/*
|
|
* This driver doesn't currently disable queue processing
|
|
* on a CMD_STOP, so it does not need to immediately
|
|
* execute a CMD_START.
|
|
*/
|
|
break;
|
|
}
|
|
|
|
/* These commands may optionally execute in immediate mode */
|
|
if (ior->io_Flags & IOF_QUICK) {
|
|
switch (ior->io_Command) {
|
|
case TD_ADDCHANGEINT:
|
|
printf("TD_ADDCHANGEINT\n");
|
|
td_addchangeint(ior);
|
|
return;
|
|
case TD_REMOVE:
|
|
printf("TD_REMOVE\n");
|
|
td_remove(ior);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* All other commands must be pushed to the driver task */
|
|
ior->io_Flags &= ~IOF_QUICK;
|
|
PutMsg(myPort, &ior->io_Message);
|
|
}
|
|
|
|
/* device dependent abortio function */
|
|
static ULONG __used __saveds
|
|
drv_abort_io(struct Library *dev asm("a6"), struct IORequest *ior asm("a1"))
|
|
{
|
|
printf("abort_io(%d)\n", ior->io_Command);
|
|
|
|
return (IOERR_NOCMD);
|
|
}
|
|
|
|
static const ULONG device_vectors[] =
|
|
{
|
|
(ULONG) drv_open,
|
|
(ULONG) drv_close,
|
|
(ULONG) drv_expunge,
|
|
0, // extFunc not used here
|
|
(ULONG) drv_begin_io,
|
|
(ULONG) drv_abort_io,
|
|
-1 // function table end marker
|
|
};
|
|
|
|
static struct Library __used __saveds *
|
|
init(BPTR seg_list asm("a0"), struct Library *dev asm("d0"))
|
|
{
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Warray-bounds="
|
|
SysBase = *(struct ExecBase **)4UL;
|
|
#pragma GCC diagnostic pop
|
|
|
|
if (seg_list == 0)
|
|
romboot = TRUE;
|
|
|
|
struct Library *mydev = MakeLibrary((ULONG *)&device_vectors, NULL,
|
|
(APTR)init_device, sizeof(struct Library), seg_list);
|
|
|
|
if (mydev != NULL) {
|
|
AddDevice((struct Device *)mydev);
|
|
|
|
if (romboot) {
|
|
init_romfiles();
|
|
mount_drives(asave->as_cd, dev);
|
|
boot_menu();
|
|
}
|
|
}
|
|
|
|
return mydev;
|
|
}
|