|
@@ -298,9 +298,14 @@ static struct {
|
|
|
bool_t packet_ready;
|
|
|
bool_t write_finished;
|
|
|
bool_t terminate_at_index;
|
|
|
- bool_t no_flux_area;
|
|
|
+ uint32_t astable_period;
|
|
|
unsigned int packet_len;
|
|
|
- uint32_t ticks_since_flux;
|
|
|
+ uint32_t ticks;
|
|
|
+ enum {
|
|
|
+ FLUXMODE_idle, /* generating no flux (awaiting next command) */
|
|
|
+ FLUXMODE_oneshot, /* generating a single flux */
|
|
|
+ FLUXMODE_astable /* generating a region of oscillating flux */
|
|
|
+ } flux_mode;
|
|
|
uint8_t packet[USB_HS_MPS];
|
|
|
} rw;
|
|
|
|
|
@@ -507,24 +512,62 @@ static uint32_t _read_28bit(void)
|
|
|
|
|
|
static unsigned int _wdata_decode_flux(timcnt_t *tbuf, unsigned int nr)
|
|
|
{
|
|
|
+#define MIN_PULSE sample_ns(800)
|
|
|
+
|
|
|
unsigned int todo = nr;
|
|
|
- uint32_t x, ticks = rw.ticks_since_flux;
|
|
|
+ uint32_t x, ticks = rw.ticks;
|
|
|
|
|
|
if (todo == 0)
|
|
|
return 0;
|
|
|
|
|
|
- if (rw.no_flux_area) {
|
|
|
- unsigned int nfa_pulse = sample_ns(1250);
|
|
|
- while (ticks >= nfa_pulse) {
|
|
|
- *tbuf++ = nfa_pulse - 1;
|
|
|
- ticks -= nfa_pulse;
|
|
|
+ switch (rw.flux_mode) {
|
|
|
+
|
|
|
+ case FLUXMODE_astable: {
|
|
|
+ /* Produce flux transitions at the specified period. */
|
|
|
+ uint32_t pulse = rw.astable_period;
|
|
|
+ while (ticks >= pulse) {
|
|
|
+ *tbuf++ = pulse - 1;
|
|
|
+ ticks -= pulse;
|
|
|
if (!--todo)
|
|
|
goto out;
|
|
|
}
|
|
|
- rw.no_flux_area = FALSE;
|
|
|
+ rw.flux_mode = FLUXMODE_idle;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ case FLUXMODE_oneshot:
|
|
|
+ /* If ticks to next flux would overflow the hardware counter, insert
|
|
|
+ * extra fluxes as necessary to get us to the proper next flux. */
|
|
|
+ while (ticks != (timcnt_t)ticks) {
|
|
|
+ uint32_t pulse = (timcnt_t)-1 + 1;
|
|
|
+ *tbuf++ = pulse - 1;
|
|
|
+ ticks -= pulse;
|
|
|
+ if (!--todo)
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Process the one-shot unless it's too short, in which case
|
|
|
+ * it will be merged into the next region. */
|
|
|
+ if (ticks > MIN_PULSE) {
|
|
|
+ *tbuf++ = ticks - 1;
|
|
|
+ ticks = 0;
|
|
|
+ if (!--todo)
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ rw.flux_mode = FLUXMODE_idle;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case FLUXMODE_idle:
|
|
|
+ /* Nothing to do (waiting for a flux command). */
|
|
|
+ break;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
while (u_cons != u_prod) {
|
|
|
+
|
|
|
+ ASSERT(rw.flux_mode == FLUXMODE_idle);
|
|
|
+
|
|
|
x = u_buf[U_MASK(u_cons)];
|
|
|
if (x == 0) {
|
|
|
/* 0: Terminate */
|
|
@@ -532,10 +575,10 @@ static unsigned int _wdata_decode_flux(timcnt_t *tbuf, unsigned int nr)
|
|
|
rw.write_finished = TRUE;
|
|
|
goto out;
|
|
|
} else if (x < 250) {
|
|
|
- /* 1-249: One byte */
|
|
|
+ /* 1-249: One byte. Time to next flux.*/
|
|
|
u_cons++;
|
|
|
} else if (x < 255) {
|
|
|
- /* 250-254: Two bytes */
|
|
|
+ /* 250-254: Two bytes. Time to next flux. */
|
|
|
if ((uint32_t)(u_prod - u_cons) < 2)
|
|
|
goto out;
|
|
|
u_cons++;
|
|
@@ -543,19 +586,43 @@ static unsigned int _wdata_decode_flux(timcnt_t *tbuf, unsigned int nr)
|
|
|
x += u_buf[U_MASK(u_cons++)] - 1;
|
|
|
} else {
|
|
|
/* 255: Six bytes */
|
|
|
+ uint8_t op;
|
|
|
if ((uint32_t)(u_prod - u_cons) < 6)
|
|
|
goto out;
|
|
|
- u_cons += 2; /* skip 255, FLUXOP_SPACE */
|
|
|
- ticks += _read_28bit();
|
|
|
- continue;
|
|
|
+ op = u_buf[U_MASK(u_cons+1)];
|
|
|
+ u_cons += 2;
|
|
|
+ switch (op) {
|
|
|
+ case FLUXOP_SPACE:
|
|
|
+ ticks += _read_28bit();
|
|
|
+ continue;
|
|
|
+ case FLUXOP_ASTABLE:
|
|
|
+ rw.astable_period = _read_28bit();
|
|
|
+ if ((rw.astable_period < MIN_PULSE)
|
|
|
+ || (rw.astable_period != (timcnt_t)rw.astable_period)) {
|
|
|
+ /* Bad period value: underflow or overflow. */
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ rw.flux_mode = FLUXMODE_astable;
|
|
|
+ goto out;
|
|
|
+ default:
|
|
|
+ /* Invalid opcode */
|
|
|
+ u_cons += 4;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+ /* We're now implicitly in FLUXMODE_oneshot, but we don't register it
|
|
|
+ * explicitly as we usually switch straight back to FLUXMODE_idle. */
|
|
|
ticks += x;
|
|
|
- if (ticks < sample_ns(800))
|
|
|
+
|
|
|
+ /* This sample too small? Then ignore this flux transition. */
|
|
|
+ if (ticks < MIN_PULSE)
|
|
|
continue;
|
|
|
|
|
|
- if (ticks > sample_us(150)) {
|
|
|
- rw.no_flux_area = TRUE;
|
|
|
+ /* This sample overflows the hardware timer's counter width?
|
|
|
+ * Then bail, and we'll split it into chunks. */
|
|
|
+ if (ticks != (timcnt_t)ticks) {
|
|
|
+ rw.flux_mode = FLUXMODE_oneshot;
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
@@ -566,8 +633,14 @@ static unsigned int _wdata_decode_flux(timcnt_t *tbuf, unsigned int nr)
|
|
|
}
|
|
|
|
|
|
out:
|
|
|
- rw.ticks_since_flux = ticks;
|
|
|
+ rw.ticks = ticks;
|
|
|
return nr - todo;
|
|
|
+
|
|
|
+error:
|
|
|
+ floppy_flux_end();
|
|
|
+ rw.status = ACK_BAD_COMMAND;
|
|
|
+ floppy_state = ST_write_flux_drain;
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
static void wdata_decode_flux(void)
|
|
@@ -634,6 +707,7 @@ static uint8_t floppy_write_prep(const struct gw_write_flux *wf)
|
|
|
|
|
|
floppy_state = ST_write_flux_wait_data;
|
|
|
memset(&rw, 0, sizeof(rw));
|
|
|
+ rw.flux_mode = FLUXMODE_idle;
|
|
|
rw.status = ACK_OKAY;
|
|
|
|
|
|
rw.terminate_at_index = wf->terminate_at_index;
|
|
@@ -648,6 +722,8 @@ static void floppy_write_wait_data(void)
|
|
|
|
|
|
floppy_process_write_packet();
|
|
|
wdata_decode_flux();
|
|
|
+ if (rw.status != ACK_OKAY)
|
|
|
+ return;
|
|
|
|
|
|
/* We don't wait for the massive F7 u_buf[] to fill at Full Speed. */
|
|
|
u_buf_threshold = ((U_BUF_SZ > 16384) && !usb_is_highspeed())
|
|
@@ -727,6 +803,8 @@ static void floppy_write(void)
|
|
|
|
|
|
floppy_process_write_packet();
|
|
|
wdata_decode_flux();
|
|
|
+ if (rw.status != ACK_OKAY)
|
|
|
+ return;
|
|
|
|
|
|
/* Early termination on index pulse? */
|
|
|
if (rw.terminate_at_index && (index.count != 0))
|