Răsfoiți Sursa

floppy: Overhaul the USB command protocol a bit.

Keir Fraser 5 ani în urmă
părinte
comite
37c0a273bb
3 a modificat fișierele cu 185 adăugiri și 116 ștergeri
  1. 49 8
      inc/cdc_acm_protocol.h
  2. 56 33
      scripts/gw.py
  3. 80 75
      src/floppy.c

+ 49 - 8
inc/cdc_acm_protocol.h

@@ -9,19 +9,24 @@
  * See the file COPYING for more details, or visit <http://unlicense.org>.
  */
 
-/* CMD_GET_INFO, length=3, 0. Returns 32 bytes after ACK. */
+
+/*
+ * GREASEWEAZLE COMMAND SET
+ */
+
+/* CMD_GET_INFO, length=4, 0, nr_bytes. Returns nr_bytes after ACK. */
 #define CMD_GET_INFO        0
 /* CMD_SEEK, length=3, cyl# */
 #define CMD_SEEK            1
 /* CMD_SIDE, length=3, side# (0=bottom) */
 #define CMD_SIDE            2
-/* CMD_SET_DELAYS, length=2+4*2, <delay_params> */
-#define CMD_SET_DELAYS      3
-/* CMD_GET_DELAYS, length=2. Returns 4*2 bytes after ACK. */
-#define CMD_GET_DELAYS      4
-/* CMD_MOTOR, length=3, motor_state */
+/* CMD_SET_PARAMS, length=3+nr, idx, <nr bytes> */
+#define CMD_SET_PARAMS      3
+/* CMD_GET_PARAMS, length=4, idx, nr_bytes. Returns nr_bytes after ACK. */
+#define CMD_GET_PARAMS      4
+/* CMD_MOTOR, length=3, motor_mask */
 #define CMD_MOTOR           5
-/* CMD_READ_FLUX, length=3, #index_pulses.
+/* CMD_READ_FLUX, length=2-3. Optionally include all or part of gw_read_flux.
  * Returns flux readings until EOStream. */
 #define CMD_READ_FLUX       6
 /* CMD_WRITE_FLUX, length=2-7. Optionally include all or part of gw_write_flux.
@@ -29,14 +34,21 @@
 #define CMD_WRITE_FLUX      7
 /* CMD_GET_FLUX_STATUS, length=2. Last read/write status returned in ACK. */
 #define CMD_GET_FLUX_STATUS 8
-/* CMD_GET_INDEX_TIMES, length=2. Returns 15*4 bytes after ACK. */
+/* CMD_GET_INDEX_TIMES, length=4, first, nr.
+ * Returns nr*4 bytes after ACK. */
 #define CMD_GET_INDEX_TIMES 9
+/* CMD_SELECT, length=3, select_mask */
+#define CMD_SELECT         10
 
 /* [BOOTLOADER] CMD_UPDATE, length=6, <update_len>. 
  * Host follows with <update_len> bytes.
  * Bootloader finally returns a status byte, 0 on success. */
 #define CMD_UPDATE          1
 
+
+/*
+ * ACK RETURN CODES
+ */
 #define ACK_OKAY            0
 #define ACK_BAD_COMMAND     1
 #define ACK_NO_INDEX        2
@@ -45,8 +57,21 @@
 #define ACK_FLUX_UNDERFLOW  5
 #define ACK_WRPROT          6
 
+
+/*
+ * CONTROL-CHANNEL COMMAND SET:
+ * We abuse SET_LINE_CODING requests over endpoint 0, stashing a command
+ * in the baud-rate field.
+ */
+#define BAUD_NORMAL        9600
 #define BAUD_CLEAR_COMMS  10000
 
+
+/*
+ * COMMAND PACKETS
+ */
+
+/* CMD_GET_INFO, index 0 */
 struct __packed gw_info {
     uint8_t fw_major;
     uint8_t fw_minor;
@@ -55,11 +80,27 @@ struct __packed gw_info {
     uint32_t sample_freq;
 };
 
+/* CMD_READ_FLUX */
+struct __packed gw_read_flux {
+    uint8_t nr_idx; /* default: 2 */
+};
+
+/* CMD_WRITE_FLUX */
 struct __packed gw_write_flux {
     uint32_t index_delay_ticks; /* default: 0 */
     uint8_t terminate_at_index; /* default: 0 */
 };
 
+/* CMD_{GET,SET}_PARAMS, index 0 */
+#define PARAMS_DELAYS 0
+struct __packed gw_delay {
+    uint16_t select_delay; /* usec */
+    uint16_t step_delay;   /* usec */
+    uint16_t seek_settle;  /* msec */
+    uint16_t motor_delay;  /* msec */
+    uint16_t auto_off;     /* msec */
+};
+
 /*
  * Local variables:
  * mode: C

+ 56 - 33
scripts/gw.py

@@ -19,16 +19,17 @@ scp_freq = 40000000
 BAUD_CLEAR_COMMS    = 10000
 BAUD_NORMAL         = 9600
 
-CMD_GET_INFO        = 0
-CMD_SEEK            = 1
-CMD_SIDE            = 2
-CMD_SET_DELAYS      = 3
-CMD_GET_DELAYS      = 4
-CMD_MOTOR           = 5
-CMD_READ_FLUX       = 6
-CMD_WRITE_FLUX      = 7
-CMD_GET_FLUX_STATUS = 8
-CMD_GET_INDEX_TIMES = 9
+CMD_GET_INFO        =  0
+CMD_SEEK            =  1
+CMD_SIDE            =  2
+CMD_SET_PARAMS      =  3
+CMD_GET_PARAMS      =  4
+CMD_MOTOR           =  5
+CMD_READ_FLUX       =  6
+CMD_WRITE_FLUX      =  7
+CMD_GET_FLUX_STATUS =  8
+CMD_GET_INDEX_TIMES =  9
+CMD_SELECT          = 10
 
 # Bootloader-specific:
 CMD_UPDATE          = 1
@@ -42,6 +43,9 @@ ACK_FLUX_UNDERFLOW  = 5
 ACK_WRPROT          = 6
 ACK_MAX             = 6
 
+# CMD_{GET,SET}_PARAMS:
+PARAMS_DELAYS       = 0
+
 ack_str = [
   "Okay", "Bad Command", "No Index", "Track 0 not found",
   "Flux Overflow", "Flux Underflow", "Disk is Write Protected" ]
@@ -63,9 +67,8 @@ def send_cmd(cmd):
     raise CmdError(c,r)
 
 def get_fw_info():
-  send_cmd(struct.pack("3B", CMD_GET_INFO, 3, 0))
-  x = struct.unpack("<4BI24x", ser.read(32))
-  return x
+  send_cmd(struct.pack("4B", CMD_GET_INFO, 4, 0, 8))
+  return struct.unpack("<4BI", ser.read(8))
 
 def print_fw_info(info):
   (major, minor, max_index, max_cmd, freq) = info
@@ -79,34 +82,39 @@ def seek(cyl, side):
   send_cmd(struct.pack("3B", CMD_SIDE, 3, side))
 
 def get_delays():
-  send_cmd(struct.pack("2B", CMD_GET_DELAYS, 2))
-  return struct.unpack("<4H", ser.read(4*2))
+  send_cmd(struct.pack("4B", CMD_GET_PARAMS, 4, PARAMS_DELAYS, 5*2))
+  return struct.unpack("<5H", ser.read(5*2))
   
 def print_delays(x):
-  (step_delay, seek_settle, motor_delay, auto_off) = x
-  print("Step Delay: %ums" % step_delay)
+  (select_delay, step_delay, seek_settle, motor_delay, auto_off) = x
+  print("Select Delay: %uus" % select_delay)
+  print("Step Delay: %uus" % step_delay)
   print("Settle Time: %ums" % seek_settle)
   print("Motor Delay: %ums" % motor_delay)
   print("Auto Off: %ums" % auto_off)
 
-def set_delays(step_delay = None, seek_settle = None,
+def set_delays(seek_delay = None, step_delay = None, seek_settle = None,
                motor_delay = None, auto_off = None):
-  (_step_delay, _seek_settle, _motor_delay, _auto_off) = get_delays()
+  (_seek_delay, _step_delay, _seek_settle,
+   _motor_delay, _auto_off) = get_delays()
+  if not seek_delay: seek_delay = _seek_delay
   if not step_delay: step_delay = _step_delay
   if not seek_settle: seek_settle = _seek_settle
   if not motor_delay: motor_delay = _motor_delay
   if not auto_off: auto_off = _auto_off
-  send_cmd(struct.pack("<2B4H", CMD_SET_DELAYS, 10,
-                            step_delay, seek_settle, motor_delay, auto_off))
+  send_cmd(struct.pack("<3B5H", CMD_SET_PARAMS, 3+5*2, PARAMS_DELAYS,
+                       seek_delay, step_delay, seek_settle,
+                       motor_delay, auto_off))
 
-def motor(state):
+def drive_select(state):
+  send_cmd(struct.pack("3B", CMD_SELECT, 3, int(state)))
+
+def drive_motor(state):
   send_cmd(struct.pack("3B", CMD_MOTOR, 3, int(state)))
 
-def get_index_times():
-  send_cmd(struct.pack("2B", CMD_GET_INDEX_TIMES, 2))
-  x = []
-  for i in range(15):
-    x.append(struct.unpack("<I", ser.read(4))[0])
+def get_index_times(nr):
+  send_cmd(struct.pack("4B", CMD_GET_INDEX_TIMES, 4, 0, nr))
+  x = struct.unpack("<%dI" % nr, ser.read(4*nr))
   return x
 
 def print_index_times(index_times):
@@ -221,7 +229,7 @@ def flux_to_scp(flux, track, nr_revs):
 
   factor = scp_freq / sample_freq
 
-  index_times = get_index_times()[:nr_revs+1]
+  index_times = get_index_times(nr_revs+1)
   tdh = struct.pack("<3sB", b"TRK", track)
   dat = bytearray()
 
@@ -466,13 +474,28 @@ def _main(argv):
     print(" To \"update\" you must install the Update Jumper")
     return
   
-  #set_delays(step_delay=3)
-  #print_delays(get_delays())
-
-  actions[args.action](args)
+  set_delays(step_delay=5000)
+  print_delays(get_delays())
 
   if not update_mode:
-    motor(False)
+    drive_select(True)
+    drive_motor(True)
+  
+  try:
+    actions[args.action](args)
+  except:
+    if not update_mode:
+      ser.reset_output_buffer()
+      ser.baudrate = BAUD_CLEAR_COMMS
+      ser.baudrate = BAUD_NORMAL
+      ser.reset_input_buffer()
+      drive_motor(False)
+      drive_select(False)
+    raise
+  else:
+    if not update_mode:
+      drive_motor(False)
+      drive_select(False)
 
 
 def main(argv):

+ 80 - 75
src/floppy.c

@@ -111,13 +111,9 @@ static uint8_t u_buf[8192];
 static uint32_t u_cons, u_prod;
 #define U_MASK(x) ((x)&(sizeof(u_buf)-1))
 
-static struct delay_params {
-    uint16_t step_delay;
-    uint16_t seek_settle;
-    uint16_t motor_delay;
-    uint16_t auto_off;
-} delay_params = {
-    .step_delay = 3,
+static struct gw_delay delay_params = {
+    .select_delay = 10,
+    .step_delay = 3000,
     .seek_settle = 15,
     .motor_delay = 750,
     .auto_off = 10000
@@ -130,7 +126,7 @@ static void step_one_out(void)
     write_pin(step, TRUE);
     delay_us(10);
     write_pin(step, FALSE);
-    delay_ms(delay_params.step_delay);
+    delay_us(delay_params.step_delay);
 }
 
 static void step_one_in(void)
@@ -140,27 +136,22 @@ static void step_one_in(void)
     write_pin(step, TRUE);
     delay_us(10);
     write_pin(step, FALSE);
-    delay_ms(delay_params.step_delay);
+    delay_us(delay_params.step_delay);
 }
 
 static int cur_cyl = -1;
 
-static void drive_select(void)
+static void drive_select(bool_t on)
 {
-    write_pin(sel0, TRUE);
-    delay_us(10);
-}
-
-static void drive_deselect(void)
-{
-    delay_us(10);
-    write_pin(sel0, FALSE);
+    if (read_pin(sel0) == on)
+        return;
+    write_pin(sel0, on);
+    if (on)
+        delay_us(delay_params.select_delay);
 }
 
 static bool_t floppy_seek(unsigned int cyl)
 {
-    drive_select();
-
     if ((cyl == 0) || (cur_cyl < 0)) {
 
         unsigned int i;
@@ -171,7 +162,6 @@ static bool_t floppy_seek(unsigned int cyl)
         }
         cur_cyl = 0;
         if (get_trk0() == HIGH) {
-            drive_deselect();
             cur_cyl = -1;
             return FALSE;
         }
@@ -194,15 +184,13 @@ static bool_t floppy_seek(unsigned int cyl)
 
     }
 
-    drive_deselect();
-
     delay_ms(delay_params.seek_settle);
     cur_cyl = cyl;
 
     return TRUE;
 }
 
-static void floppy_motor(bool_t on)
+static void drive_motor(bool_t on)
 {
     if (read_pin(motor) == on)
         return;
@@ -318,7 +306,7 @@ void floppy_init(void)
 }
 
 static struct gw_info gw_info = {
-    .max_index = 15, .max_cmd = CMD_GET_INDEX_TIMES,
+    .max_index = 15, .max_cmd = CMD_SELECT,
     .sample_freq = SYSCLK_MHZ * 1000000u
 };
 
@@ -433,8 +421,11 @@ static void rdata_encode_flux(void)
     rw.ticks_since_index = ticks_since_index;
 }
 
-static void floppy_read_prep(unsigned int nr_idx)
+static uint8_t floppy_read_prep(struct gw_read_flux *rf)
 {
+    if ((rf->nr_idx == 0) || (rf->nr_idx > gw_info.max_index))
+        return ACK_BAD_COMMAND;
+
     /* Start DMA. */
     dma_rdata.cndtr = ARRAY_SIZE(dma.buf);
     dma_rdata.ccr = (DMA_CCR_PL_HIGH |
@@ -449,9 +440,6 @@ static void floppy_read_prep(unsigned int nr_idx)
     dma.cons = 0;
     dma.prev_sample = tim_rdata->cnt;
 
-    drive_select();
-    floppy_motor(TRUE);
-
     /* Start timer. */
     tim_rdata->ccer = TIM_CCER_CC2E | TIM_CCER_CC2P;
     tim_rdata->cr1 = TIM_CR1_CEN;
@@ -459,9 +447,11 @@ static void floppy_read_prep(unsigned int nr_idx)
     index.count = 0;
     floppy_state = ST_read_flux;
     memset(&rw, 0, sizeof(rw));
-    rw.nr_idx = nr_idx;
+    rw.nr_idx = rf->nr_idx;
     rw.start = time_now();
     rw.status = ACK_OKAY;
+
+    return ACK_OKAY;
 }
 
 static void make_read_packet(unsigned int n)
@@ -524,7 +514,6 @@ static void floppy_read(void)
         make_read_packet(avail);
         floppy_state = ST_command_wait;
         floppy_end_command(rw.packet, avail+1);
-        drive_deselect();
         return; /* FINISHED */
 
     }
@@ -641,27 +630,23 @@ static void floppy_process_write_packet(void)
     }
 }
 
-static void floppy_write_prep(struct gw_write_flux *wf)
+static uint8_t floppy_write_prep(struct gw_write_flux *wf)
 {
+    if (get_wrprot() == LOW)
+        return ACK_WRPROT;
+
     /* Initialise DMA ring indexes (consumer index is implicit). */
     dma_wdata.cndtr = ARRAY_SIZE(dma.buf);
     dma.prod = 0;
 
-    drive_select();
-    floppy_motor(TRUE);
-
     floppy_state = ST_write_flux_wait_data;
     memset(&rw, 0, sizeof(rw));
     rw.status = ACK_OKAY;
 
-    if (get_wrprot() == LOW) {
-        floppy_flux_end();
-        rw.status = ACK_WRPROT;
-        floppy_state = ST_write_flux_drain;
-    }
-
     index.delay = time_sysclk(wf->index_delay_ticks);
     rw.terminate_at_index = wf->terminate_at_index;
+
+    return ACK_OKAY;
 }
 
 static void floppy_write_wait_data(void)
@@ -787,7 +772,6 @@ static void floppy_write_drain(void)
     u_buf[0] = rw.status;
     floppy_state = ST_command_wait;
     floppy_end_command(u_buf, 1);
-    drive_deselect();
 
     /* Reset INDEX handling. */
     IRQ_global_disable();
@@ -808,70 +792,91 @@ static void process_command(void)
     switch (cmd) {
     case CMD_GET_INFO: {
         uint8_t idx = u_buf[2];
-        if (len != 3) goto bad_command;
-        if (idx != 0) goto bad_command;
-        memset(&u_buf[2], 0, 32);
+        uint8_t nr = u_buf[3];
+        if ((len != 4) || (idx != 0) || (nr > 32))
+            goto bad_command;
+        memset(&u_buf[2], 0, nr);
         gw_info.fw_major = fw_major;
         gw_info.fw_minor = fw_minor;
         memcpy(&u_buf[2], &gw_info, sizeof(gw_info));
-        resp_sz += 32;
+        resp_sz += nr;
         break;
     }
     case CMD_SEEK: {
         uint8_t cyl = u_buf[2];
-        if (len != 3) goto bad_command;
-        if (cyl > 85) goto bad_command;
+        if ((len != 3) || (cyl > 85))
+            goto bad_command;
         u_buf[1] = floppy_seek(cyl) ? ACK_OKAY : ACK_NO_TRK0;
         goto out;
     }
     case CMD_SIDE: {
         uint8_t side = u_buf[2];
-        if (len != 3) goto bad_command;
-        if (side > 1) goto bad_command;
+        if ((len != 3) || (side > 1))
+            goto bad_command;
         write_pin(side, side);
         break;
     }
-    case CMD_SET_DELAYS: {
-        if (len != (2+sizeof(delay_params))) goto bad_command;
-        memcpy(&delay_params, &u_buf[2], sizeof(delay_params));
+    case CMD_SET_PARAMS: {
+        uint8_t idx = u_buf[2];
+        if ((len < 3) || (idx != PARAMS_DELAYS)
+            || (len > (3 + sizeof(delay_params))))
+            goto bad_command;
+        memcpy(&delay_params, &u_buf[3], len-3);
         break;
     }
-    case CMD_GET_DELAYS: {
-        if (len != 2) goto bad_command;
-        memcpy(&u_buf[2], &delay_params, sizeof(delay_params));
-        resp_sz += sizeof(delay_params);
+    case CMD_GET_PARAMS: {
+        uint8_t idx = u_buf[2];
+        uint8_t nr = u_buf[3];
+        if ((len != 4) || (idx != PARAMS_DELAYS)
+            || (nr > sizeof(delay_params)))
+            goto bad_command;
+        memcpy(&u_buf[2], &delay_params, nr);
+        resp_sz += nr;
         break;
     }
     case CMD_MOTOR: {
-        uint8_t state = u_buf[2];
-        if (len != 3) goto bad_command;
-        if (state > 1) goto bad_command;
-        floppy_motor(state);
+        uint8_t mask = u_buf[2];
+        if ((len != 3) || (mask & ~1))
+            goto bad_command;
+        drive_motor(mask & 1);
         break;
     }
     case CMD_READ_FLUX: {
-        uint8_t nr_idx = u_buf[2];
-        if (len != 3) goto bad_command;
-        if ((nr_idx == 0) || (nr_idx > gw_info.max_index)) goto bad_command;
-        floppy_read_prep(nr_idx);
-        break;
+        struct gw_read_flux rf = { .nr_idx = 2 };
+        if ((len < 2) || (len > (2 + sizeof(rf))))
+            goto bad_command;
+        memcpy(&rf, &u_buf[2], len-2);
+        u_buf[1] = floppy_read_prep(&rf);
+        goto out;
     }
     case CMD_WRITE_FLUX: {
         struct gw_write_flux wf = { 0 };
-        if ((len < 2) || (len > (2 + sizeof(wf)))) goto bad_command;
+        if ((len < 2) || (len > (2 + sizeof(wf))))
+            goto bad_command;
         memcpy(&wf, &u_buf[2], len-2);
-        floppy_write_prep(&wf);
-        break;
+        u_buf[1] = floppy_write_prep(&wf);
+        goto out;
+
     }
     case CMD_GET_FLUX_STATUS: {
-        if (len != 2) goto bad_command;
+        if (len != 2)
+            goto bad_command;
         u_buf[1] = rw.status;
         goto out;
     }
     case CMD_GET_INDEX_TIMES: {
-        if (len != 2) goto bad_command;
-        memcpy(&u_buf[2], rw.index_ticks, sizeof(rw.index_ticks));
-        resp_sz += sizeof(rw.index_ticks);
+        uint8_t f = u_buf[2], n = u_buf[3];
+        if ((len != 4) || (n > 15) || ((f+n) > gw_info.max_index))
+            goto bad_command;
+        memcpy(&u_buf[2], rw.index_ticks+f, n*4);
+        resp_sz += n*4;
+        break;
+    }
+    case CMD_SELECT: {
+        uint8_t mask = u_buf[2];
+        if ((len != 3) || (mask & ~1))
+            goto bad_command;
+        drive_select(mask & 1);
         break;
     }
     default:
@@ -900,8 +905,8 @@ void floppy_process(void)
 
     if (auto_off.armed && (time_since(auto_off.deadline) >= 0)) {
         floppy_flux_end();
-        floppy_motor(FALSE);
-        drive_deselect();
+        drive_motor(FALSE);
+        drive_select(FALSE);
         auto_off.armed = FALSE;
         //gpio_write_pin(gpioa, 0, LOW);
         //delay_ms(100); /* force disconnect */