|  | @@ -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);
 |