Pārlūkot izejas kodu

usb: Fix host-receive hang (thanks to Charlie Smurthwaite!)
Implement the drive auto-off feature.

Keir Fraser 5 gadi atpakaļ
vecāks
revīzija
e56421266d
7 mainītis faili ar 83 papildinājumiem un 62 dzēšanām
  1. 5 11
      scripts/49-greaseweazle.rules
  2. 0 20
      scripts/foop
  3. 46 10
      src/floppy.c
  4. 3 3
      src/usb/cdc_acm.c
  5. 1 1
      src/usb/config.c
  6. 4 0
      src/usb/defs.h
  7. 24 17
      src/usb/hw_f1.c

+ 5 - 11
scripts/49-greaseweazle.rules

@@ -1,23 +1,17 @@
 # UDEV Rules for Greaseweazle
 #
-# This file must be placed at:
-#
-# /etc/udev/rules.d/49-greaseweazle.rules    (preferred location)
-#   or
-# /lib/udev/rules.d/49-greaseweazle.rules    (req'd on some broken systems)
-#
 # To install, type this command in a terminal:
-#   sudo cp 49-greaseweazle.rules /etc/udev/rules.d/49-greaseweazle.rules
+#   sudo cp 49-greaseweazle.rules /etc/udev/rules.d/.
 #
 # After this file is installed, physically unplug and reconnect Greaseweazle.
 #
-ATTRS{manufacturer}=="Keir Fraser", ATTRS{product}=="GreaseWeazle", \
+ATTRS{manufacturer}=="Keir Fraser", ATTRS{product}=="Greaseweazle", \
     ENV{ID_MM_DEVICE_IGNORE}="1"
-ATTRS{manufacturer}=="Keir Fraser", ATTRS{product}=="GreaseWeazle", \
+ATTRS{manufacturer}=="Keir Fraser", ATTRS{product}=="Greaseweazle", \
     ENV{MTP_NO_PROBE}="1"
-ATTRS{manufacturer}=="Keir Fraser", ATTRS{product}=="GreaseWeazle", \
+ATTRS{manufacturer}=="Keir Fraser", ATTRS{product}=="Greaseweazle", \
     SUBSYSTEMS=="usb", MODE:="0666"
-ATTRS{manufacturer}=="Keir Fraser", ATTRS{product}=="GreaseWeazle", \
+ATTRS{manufacturer}=="Keir Fraser", ATTRS{product}=="Greaseweazle", \
     KERNEL=="ttyACM*", MODE:="0666"
 #
 # If you share your linux system with other users, or just don't like the

+ 0 - 20
scripts/foop

@@ -1,20 +0,0 @@
-# UDEV Rules for Greaseweazle
-#
-# To install, type this command in a terminal:
-#   sudo cp 49-greaseweazle.rules /etc/udev/rules.d/.
-#
-# After this file is installed, physically unplug and reconnect Greaseweazle.
-#
-ATTRS{manufacturer}=="Keir Fraser", ATTRS{product}=="GreaseWeazle", \
-    ENV{ID_MM_DEVICE_IGNORE}="1"
-ATTRS{manufacturer}=="Keir Fraser", ATTRS{product}=="GreaseWeazle", \
-    ENV{MTP_NO_PROBE}="1"
-ATTRS{manufacturer}=="Keir Fraser", ATTRS{product}=="GreaseWeazle", \
-    SUBSYSTEMS=="usb", MODE:="0666"
-ATTRS{manufacturer}=="Keir Fraser", ATTRS{product}=="GreaseWeazle", \
-    KERNEL=="ttyACM*", MODE:="0666"
-#
-# If you share your linux system with other users, or just don't like the
-# idea of write permission for everybody, you can replace MODE:="0666" with
-# OWNER:="yourusername" to create the device owned by you, or with
-# GROUP:="somegroupname" and mange access using standard unix groups.

+ 46 - 10
src/floppy.c

@@ -82,9 +82,15 @@ static struct dma_ring {
     uint16_t buf[512];
 } dma;
 
+static struct {
+    time_t deadline;
+    bool_t armed;
+} auto_off;
+
 static enum {
     ST_inactive,
     ST_command_wait,
+    ST_zlp,
     ST_read_flux_wait_index,
     ST_read_flux,
     ST_read_flux_drain,
@@ -110,7 +116,7 @@ static struct delay_params {
     .step_delay = 3,
     .seek_settle = 15,
     .motor_delay = 750,
-    .auto_off = 3000
+    .auto_off = 10000
 };
 
 static void step_one_out(void)
@@ -224,6 +230,7 @@ void floppy_reset(void)
     unsigned int i;
 
     floppy_state = ST_inactive;
+    auto_off.armed = FALSE;
 
     floppy_flux_end();
 
@@ -345,6 +352,23 @@ const static struct __packed gw_info {
     .sample_freq = SYSCLK_MHZ * 1000000u
 };
 
+static void auto_off_arm(void)
+{
+    auto_off.armed = TRUE;
+    auto_off.deadline = time_now() + time_ms(delay_params.auto_off);
+}
+
+static void floppy_end_command(void *ack, unsigned int ack_len)
+{
+    auto_off_arm();
+    usb_write(FLOPPY_EP, ack, ack_len);
+    u_cons = u_prod = 0;
+    if (ack_len == FLOPPY_MPS) {
+        ASSERT(floppy_state == ST_command_wait);
+        floppy_state = ST_zlp;
+    }
+}
+
 /*
  * READ PATH
  */
@@ -504,8 +528,8 @@ static void floppy_read(void)
 
         memset(rw.packet, 0, FLOPPY_MPS);
         make_read_packet(avail);
-        usb_write(FLOPPY_EP, rw.packet, avail+1);
-        floppy_configured();
+        floppy_state = ST_command_wait;
+        floppy_end_command(rw.packet, avail+1);
         drive_deselect();
         return; /* FINISHED */
 
@@ -761,10 +785,8 @@ static void floppy_write_drain(void)
 
     /* ACK with Status byte. */
     u_buf[0] = rw.status;
-    usb_write(FLOPPY_EP, u_buf, 1);
-
-    /* Reset for next command. */
-    floppy_configured();
+    floppy_state = ST_command_wait;
+    floppy_end_command(u_buf, 1);
     drive_deselect();
 }
 
@@ -774,6 +796,8 @@ static void process_command(void)
     uint8_t len = u_buf[1];
     uint8_t resp_sz = 2;
 
+    auto_off_arm();
+
     switch (cmd) {
     case CMD_GET_INFO: {
         uint8_t idx = u_buf[2];
@@ -845,7 +869,7 @@ static void process_command(void)
 
     u_buf[1] = ACK_OKAY;
 out:
-    usb_write(FLOPPY_EP, u_buf, resp_sz);
+    floppy_end_command(u_buf, resp_sz);
     return;
 
 bad_command:
@@ -863,6 +887,13 @@ void floppy_process(void)
 {
     int len;
 
+    if (auto_off.armed && (time_since(auto_off.deadline) >= 0)) {
+        floppy_flux_end();
+        floppy_motor(FALSE);
+        drive_deselect();
+        auto_off.armed = FALSE;
+    }
+
     switch (floppy_state) {
 
     case ST_command_wait:
@@ -874,13 +905,18 @@ void floppy_process(void)
         }
 
         if ((u_prod >= 2) && (u_prod >= u_buf[1]) && ep_tx_ready(FLOPPY_EP)) {
-            /* Process command and reset for next command. */
             process_command();
-            u_cons = u_prod = 0;
         }
 
         break;
 
+    case ST_zlp:
+        if (ep_tx_ready(FLOPPY_EP)) {
+            usb_write(FLOPPY_EP, NULL, 0);
+            floppy_state = ST_command_wait;
+        }
+        break;
+
     case ST_read_flux_wait_index:
         floppy_read_wait_index();
         break;

+ 3 - 3
src/usb/cdc_acm.c

@@ -93,9 +93,9 @@ bool_t cdc_acm_handle_class_request(void)
 bool_t cdc_acm_set_configuration(void)
 {
     /* Set up endpoints. */
-    usb_configure_ep(0x81, 0,  0); /* Notification Element (D->H) */
-    usb_configure_ep(0x02, 0, 64); /* Bulk Pipe (H->D) */
-    usb_configure_ep(0x82, 0, 64); /* Bulk Pipe (D->H) */
+    usb_configure_ep(0x81, 0,          0); /* Notification Element (D->H) */
+    usb_configure_ep(0x02, 0, USB_FS_MPS); /* Bulk Pipe (H->D) */
+    usb_configure_ep(0x82, 0, USB_FS_MPS); /* Bulk Pipe (D->H) */
 
     floppy_configured();
 

+ 1 - 1
src/usb/config.c

@@ -100,7 +100,7 @@ const uint8_t config_descriptor[] __aligned(2) = {
 const char *string_descriptors[] = {
     "\x09\x04", /* LANGID: US English */
     "Keir Fraser",
-    "GreaseWeazle",
+    "Greaseweazle",
 };
 
 /*

+ 4 - 0
src/usb/defs.h

@@ -9,6 +9,9 @@
  * See the file COPYING for more details, or visit <http://unlicense.org>.
  */
 
+/* Full Speed Max Packet Size */
+#define USB_FS_MPS 64
+
 /* bRequest: Standard Request Codes */
 #define GET_STATUS          0
 #define CLEAR_FEATURE       1
@@ -56,6 +59,7 @@ extern struct ep0 {
     struct {
         const uint8_t *p;
         int todo;
+        bool_t trunc;
     } tx;
 } ep0;
 #define ep0_data_out() (!(ep0.req.bmRequestType & 0x80))

+ 24 - 17
src/usb/hw_f1.c

@@ -113,26 +113,26 @@ void usb_write(uint8_t ep, const void *buf, uint32_t len)
     _ep_tx_ready[ep] = FALSE;
 }
 
-static void usb_continue_write_ep0(void)
+static void usb_write_ep0(void)
 {
     uint32_t len;
 
     if ((ep0.tx.todo < 0) || !ep_tx_ready(0))
         return;
 
-    len = min_t(uint32_t, ep0.tx.todo, 64);
+    len = min_t(uint32_t, ep0.tx.todo, USB_FS_MPS);
     usb_write(0, ep0.tx.p, len);
 
-    if ((ep0.tx.todo -= len) == 0)
-        ep0.tx.todo = -1;
     ep0.tx.p += len;
-}
-
-static void usb_start_write_ep0(const void *buf, uint32_t len)
-{
-    ep0.tx.p = buf;
-    ep0.tx.todo = len;
-    usb_continue_write_ep0();
+    ep0.tx.todo -= len;
+
+    if (ep0.tx.todo == 0) {
+        /* USB Spec 1.1, Section 5.5.3: Data stage of a control transfer is
+         * complete when we have transferred the exact amount of data specified
+         * during Setup *or* transferred a short/zero packet. */
+        if (!ep0.tx.trunc || (len < USB_FS_MPS))
+            ep0.tx.todo = -1;
+    }
 }
 
 static void usb_stall(uint8_t ep)
@@ -171,7 +171,7 @@ void usb_configure_ep(uint8_t ep, uint8_t type, uint32_t size)
     if (!in) {
         usb_bufd[ep].addr_rx = buf_end;
         buf_end += size;
-        usb_bufd[ep].count_rx = 0x8400; /* 64 bytes */
+        usb_bufd[ep].count_rx = 0x8400; /* USB_FS_MPS = 64 bytes */
         /* RX: Clears data toggle and sets status to VALID. */
         new_epr |= (old_epr & 0x7000) ^ USB_EPR_STAT_RX(USB_STAT_VALID);
         /* OUT Endpoint must wait for a packet from the Host. */
@@ -197,7 +197,7 @@ static void handle_reset(void)
     /* Prepare for Enumeration: Set up Endpoint 0 at Address 0. */
     pending_addr = 0;
     buf_end = 64;
-    usb_configure_ep(0, USB_EP_TYPE_CONTROL, 64);
+    usb_configure_ep(0, USB_EP_TYPE_CONTROL, USB_FS_MPS);
     usb->daddr = USB_DADDR_EF | USB_DADDR_ADD(0);
     usb->istr &= ~USB_ISTR_RESET;
 }
@@ -253,6 +253,7 @@ static void handle_rx_transfer(uint8_t ep)
 
         /* IN Control Transfer: Status from Host. */
         usb_read(ep, NULL, 0);
+        ep0.tx.todo = -1;
         ep0.data_len = -1; /* Complete */
 
     }
@@ -271,12 +272,18 @@ static void handle_rx_transfer(uint8_t ep)
     } else if (ep0_data_in()) {
 
         /* IN Control Transfer: Send Data to Host. */
-        usb_start_write_ep0(ep0.data, ep0.data_len);
+        ep0.tx.p = ep0.data;
+        ep0.tx.todo = ep0.data_len;
+        ep0.tx.trunc = (ep0.data_len < ep0.req.wLength);
+        usb_write_ep0();
 
     } else {
 
         /* OUT Control Transfer: Send Status to Host. */
-        usb_start_write_ep0(NULL, 0);
+        ep0.tx.p = NULL;
+        ep0.tx.todo = 0;
+        ep0.tx.trunc = FALSE;
+        usb_write_ep0();
         ep0.data_len = -1; /* Complete */
 
     }
@@ -297,9 +304,9 @@ static void handle_tx_transfer(uint8_t ep)
     if (ep != 0)
         return;
 
-    usb_continue_write_ep0();
+    usb_write_ep0();
 
-    if (pending_addr) {
+    if (pending_addr && (ep0.tx.todo == -1)) {
         /* We have just completed the Status stage of a SET_ADDRESS request. 
          * Now is the time to apply the address update. */
         usb->daddr = USB_DADDR_EF | USB_DADDR_ADD(pending_addr);