|
@@ -11,6 +11,7 @@
|
|
|
#include "io.h"
|
|
|
#include "abcio.h"
|
|
|
#include "console.h"
|
|
|
+#include "sdcard.h"
|
|
|
#include "ff.h"
|
|
|
|
|
|
/* Option flags, mostly for debugging */
|
|
@@ -20,9 +21,9 @@
|
|
|
#define FORMAT_SUPPORT 0
|
|
|
|
|
|
enum drive_flags {
|
|
|
- DF_MOUNTED, /* Host file open, drive exported */
|
|
|
- DF_READONLY, /* Drive is readonly */
|
|
|
- DF_DISABLED /* Drive is disabled */
|
|
|
+ DF_MOUNTED = 1, /* Host file open, drive exported */
|
|
|
+ DF_READONLY = 2, /* Drive is readonly */
|
|
|
+ DF_DISABLED = 4 /* Drive is disabled */
|
|
|
};
|
|
|
|
|
|
enum pending {
|
|
@@ -30,6 +31,10 @@ enum pending {
|
|
|
PEND_RESET = 2
|
|
|
};
|
|
|
|
|
|
+/* Number of fragments (extents) we can memoize */
|
|
|
+#define MAX_FAST_FRAGMENTS 16
|
|
|
+#define CLTBL_SIZE ((MAX_FAST_FRAGMENTS+1)*2)
|
|
|
+
|
|
|
/* Per-drive state */
|
|
|
struct drive_state {
|
|
|
FIL file; /* Host file */
|
|
@@ -40,6 +45,7 @@ struct drive_state {
|
|
|
#endif
|
|
|
enum drive_flags flags;
|
|
|
enum drive_flags force; /* Option to force flags */
|
|
|
+ DWORD cltbl[CLTBL_SIZE]; /* Extent map */
|
|
|
};
|
|
|
|
|
|
/* Fixed parameters for each controller */
|
|
@@ -278,9 +284,13 @@ bool valid_drive_name(const char *drive)
|
|
|
static int mount_drive(struct drive_state *drv, struct ctl_state *state,
|
|
|
const char *filename)
|
|
|
{
|
|
|
+ FRESULT rv;
|
|
|
+
|
|
|
if (mounted(drv)) {
|
|
|
- f_close(&drv->file);
|
|
|
+ if (!(sdc.fsstatus & STA_NOINIT))
|
|
|
+ f_close(&drv->file);
|
|
|
drv->flags &= ~DF_MOUNTED;
|
|
|
+ state->drives--;
|
|
|
}
|
|
|
|
|
|
if (drv->force & DF_DISABLED)
|
|
@@ -297,25 +307,31 @@ static int mount_drive(struct drive_state *drv, struct ctl_state *state,
|
|
|
if (!(drv->flags & DF_READONLY))
|
|
|
mode |= FA_WRITE;
|
|
|
|
|
|
- FRESULT rv = f_open(&drv->file, filename, mode);
|
|
|
+ rv = f_open(&drv->file, filename, mode);
|
|
|
|
|
|
if (rv == FR_WRITE_PROTECTED && (mode & FA_WRITE)) {
|
|
|
drv->flags |= DF_READONLY;
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- if (rv != FR_OK)
|
|
|
- drv->flags |= DF_DISABLED;
|
|
|
- else
|
|
|
- drv->flags |= DF_MOUNTED;
|
|
|
-
|
|
|
+ drv->flags |= (rv == FR_OK) ? DF_MOUNTED : DF_DISABLED;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
if (!(drv->flags & DF_MOUNTED))
|
|
|
return -1;
|
|
|
+ }
|
|
|
|
|
|
- con_printf("abcdisk: %-3s = %s\n", drv->name, filename);
|
|
|
+ con_printf("abcdisk: %-3s = %s\n", drv->name, filename);
|
|
|
+
|
|
|
+ /* Try to memoize extents for fast seek */
|
|
|
+ drv->cltbl[0] = CLTBL_SIZE;
|
|
|
+ drv->file.cltbl = drv->cltbl;
|
|
|
+
|
|
|
+ rv = f_lseek(&drv->file, CREATE_LINKMAP);
|
|
|
+ if (rv != FR_OK) {
|
|
|
+ con_printf("abcdisk: %-3s ! file too fragmented, will be slow\n",
|
|
|
+ drv->name);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -327,11 +343,23 @@ static int mount_drive(struct drive_state *drv, struct ctl_state *state,
|
|
|
|
|
|
/* Interleaving parameters */
|
|
|
#if INTERLEAVE
|
|
|
- drv->ilfac = state->params->ilfac;
|
|
|
- drv->ilmsk = state->params->ilmsk;
|
|
|
+ drv->ilfac = state->params->ilfac;
|
|
|
+ drv->ilmsk = state->params->ilmsk;
|
|
|
#endif
|
|
|
|
|
|
- return 0;
|
|
|
+ state->drives++;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void unmount_drives(struct ctl_state *state)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (int i = 0; i < 8; i++) {
|
|
|
+ struct drive_state *drv = &state->drv[i];
|
|
|
+ if (drv->flags & DF_MOUNTED)
|
|
|
+ mount_drive(drv, state, NULL);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
#define IDLE_CALLBACK_MASK (1 << 4)
|
|
@@ -361,50 +389,43 @@ static void abcdisk_callback_cmd(struct abc_dev *dev, uint8_t data, uint8_t addr
|
|
|
state->pending |= PEND_RESET;
|
|
|
}
|
|
|
|
|
|
-static char * const disk_pfx[] = {
|
|
|
- "/abcdisk.800/", "/abcdisk/", "/abcdisk.", "/", NULL
|
|
|
+static char abcdisk_80_800[] = "/abcdisk.800/";
|
|
|
+
|
|
|
+static const char * const disk_pfx[] = {
|
|
|
+ abcdisk_80_800, "/abcdisk/", "/abcdisk.", "/", NULL
|
|
|
};
|
|
|
|
|
|
static void init_drives(struct ctl_state *state)
|
|
|
{
|
|
|
- static bool disk_initialized;
|
|
|
- static int disk_init_status;
|
|
|
uint32_t abc_status = ABC_STATUS;
|
|
|
- const char * const *pfx_list;
|
|
|
+ char *p80;
|
|
|
int i;
|
|
|
|
|
|
- if (!disk_initialized) {
|
|
|
- disk_init_status = disk_init();
|
|
|
- disk_initialized = true;
|
|
|
- }
|
|
|
-
|
|
|
/* .80/ or .800/ for the first entry */
|
|
|
- strcpy(disk_pfx[0] + 10 + !!(abc_status & ABC_STATUS_800), "0/");
|
|
|
-
|
|
|
- /* If any of these don't exist we simply report device not ready */
|
|
|
- state->drives = 0;
|
|
|
-
|
|
|
- if (!disk_init_status) {
|
|
|
- for (i = 0; i < 8; i++) {
|
|
|
- struct drive_state *drv = &state->drv[i];
|
|
|
- unsigned int filesec;
|
|
|
- char * const *pfx;
|
|
|
-
|
|
|
- snprintf(drv->name, sizeof drv->name, "%-.2s%c",
|
|
|
- state->params->name, i + '0');
|
|
|
-
|
|
|
- for (pfx = disk_pfx; *pfx; pfx++) {
|
|
|
- char filename_buf[64];
|
|
|
- snprintf(filename_buf, sizeof filename_buf,
|
|
|
- "%s%s", *pfx, drv->name);
|
|
|
-
|
|
|
- if (!mount_drive(drv, state, filename_buf)) {
|
|
|
- state->drives++;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
+ p80 = abcdisk_80_800 + 10 + !!(abc_status & ABC_STATUS_800);
|
|
|
+ p80[0] = '0';
|
|
|
+ p80[1] = '/';
|
|
|
+ p80[2] = '\0';
|
|
|
+
|
|
|
+ for (i = 0; i < 8; i++) {
|
|
|
+ struct drive_state *drv = &state->drv[i];
|
|
|
+ unsigned int filesec;
|
|
|
+ char * const *pfx;
|
|
|
+
|
|
|
+ snprintf(drv->name, sizeof drv->name, "%-.2s%c",
|
|
|
+ state->params->name, i + '0');
|
|
|
+
|
|
|
+ for (pfx = disk_pfx; *pfx; pfx++) {
|
|
|
+ char filename_buf[64];
|
|
|
+ snprintf(filename_buf, sizeof filename_buf,
|
|
|
+ "%s%s", *pfx, drv->name);
|
|
|
+
|
|
|
+ if (!mount_drive(drv, state, filename_buf))
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ state->initialized = true;
|
|
|
}
|
|
|
|
|
|
static void do_next_command(struct ctl_state *state)
|
|
@@ -574,39 +595,108 @@ static void do_next_command(struct ctl_state *state)
|
|
|
disk_start_command(state);
|
|
|
}
|
|
|
|
|
|
-#define SYNC_TIME (2*CPU_HZ)
|
|
|
+static FATFS sd_fs;
|
|
|
+
|
|
|
+static int mount_disk(void)
|
|
|
+{
|
|
|
+ FRESULT rv;
|
|
|
+ char label[128];
|
|
|
+ uint32_t volid, freeclust;
|
|
|
+ FATFS *fs;
|
|
|
+
|
|
|
+ rv = f_mount(&sd_fs, "", 1);
|
|
|
+ if (rv != FR_OK) {
|
|
|
+ con_printf("sdcard: no volume found\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ label[0] = '\0';
|
|
|
+ volid = 0;
|
|
|
+
|
|
|
+ f_getlabel("", label, &volid);
|
|
|
+ con_printf("sdcard: volume found, label \"%s\", volid %08x\n", label, volid);
|
|
|
+
|
|
|
+ freeclust = 0;
|
|
|
+ f_getfree("", &freeclust, &fs);
|
|
|
+
|
|
|
+ con_printf("sdcard: %u/%u clusters free, clusters = %u bytes\n",
|
|
|
+ freeclust, fs->n_fatent - 2, fs->csize << 9);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+#define SYNC_TIME (2*TIMER_HZ) /* 2 s */
|
|
|
|
|
|
/* Called from the main loop */
|
|
|
void abcdisk_io_poll(void)
|
|
|
{
|
|
|
static uint32_t last_sync;
|
|
|
- uint32_t now = rdtime();
|
|
|
+ uint32_t now = timer_count();
|
|
|
bool need_sync = (now - last_sync) >= SYNC_TIME;
|
|
|
last_sync = now;
|
|
|
+ uint32_t abc_status = ABC_STATUS;
|
|
|
+ static uint32_t prev_abc_status = -1U;
|
|
|
+ bool unmount_all = false;
|
|
|
+ bool reset_all = false;
|
|
|
+ static uint32_t last_timer = -1U;
|
|
|
+
|
|
|
+ if (now != last_timer) {
|
|
|
+ /*
|
|
|
+ * Periodically try to initialize the SD card if we are waiting
|
|
|
+ * for it. The disk cache, however, will be initialized when fatfs
|
|
|
+ * tries to mount the device.
|
|
|
+ */
|
|
|
+ if (sdc.status & STA_NOINIT) {
|
|
|
+ if (!(sdcard_init() & STA_NOINIT))
|
|
|
+ mount_disk();
|
|
|
+ unmount_all = true;
|
|
|
+ } else if (sdcard_present_poll() & STA_NOINIT) {
|
|
|
+ unmount_all = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ last_timer = now;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((abc_status ^ prev_abc_status) & (ABC_STATUS_LIVE|ABC_STATUS_800)) {
|
|
|
+ const char *host;
|
|
|
+
|
|
|
+ prev_abc_status = abc_status;
|
|
|
+
|
|
|
+ con_puts("ABC-bus host: ");
|
|
|
+ con_puts(abc_status & ABC_STATUS_800 ? "ABC800" : "ABC80");
|
|
|
+ con_puts(abc_status & ABC_STATUS_LIVE ? " (online)\n" : " (offline)\n");
|
|
|
+
|
|
|
+ unmount_all = true;
|
|
|
+ reset_all = true;
|
|
|
+ }
|
|
|
|
|
|
for (int i = 0; i < CONTROLLER_TYPES; i++) {
|
|
|
struct ctl_state *state = &controllers[i];
|
|
|
enum pending pending;
|
|
|
|
|
|
+ if (unmount_all && state->initialized) {
|
|
|
+ unmount_drives(state);
|
|
|
+ state->initialized = false;
|
|
|
+ } else if (!state->initialized && !(sdc.status & STA_NOINIT)) {
|
|
|
+ init_drives(state);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (reset_all)
|
|
|
+ disk_reset_state(state);
|
|
|
+
|
|
|
+ if (!(abc_status & ABC_STATUS_LIVE))
|
|
|
+ continue;
|
|
|
+
|
|
|
mask_irq(ABC_IRQ);
|
|
|
pending = state->pending;
|
|
|
state->pending = 0;
|
|
|
unmask_irq(ABC_IRQ);
|
|
|
|
|
|
- if (!pending)
|
|
|
- continue;
|
|
|
-
|
|
|
- if (pending & PEND_RESET) {
|
|
|
- if (state->initialized)
|
|
|
- disk_reset_state(state);
|
|
|
- }
|
|
|
-
|
|
|
- if (pending & PEND_IO) {
|
|
|
- if (!state->initialized)
|
|
|
- init_drives(state);
|
|
|
+ if (pending & PEND_RESET)
|
|
|
+ disk_reset_state(state);
|
|
|
|
|
|
+ if (pending & PEND_IO)
|
|
|
do_next_command(state);
|
|
|
- }
|
|
|
|
|
|
if (need_sync)
|
|
|
sync_drives(state);
|