From c755aee7d0c1e2486055e02ac353878d8da3ba59 Mon Sep 17 00:00:00 2001
From: Nathan Conrad <pigrew@gmail.com>
Date: Sat, 14 Sep 2019 22:55:42 -0400
Subject: [PATCH] Clear

---
 examples/device/usbtmc/src/usbtmc_app.c |  44 +++++---
 src/class/usbtmc/usbtmc.h               |  94 +++++++++++------
 src/class/usbtmc/usbtmc_device.c        | 135 +++++++++++++++---------
 src/class/usbtmc/usbtmc_device.h        |   4 +
 4 files changed, 182 insertions(+), 95 deletions(-)

diff --git a/examples/device/usbtmc/src/usbtmc_app.c b/examples/device/usbtmc/src/usbtmc_app.c
index af97939b9..a41d911d2 100644
--- a/examples/device/usbtmc/src/usbtmc_app.c
+++ b/examples/device/usbtmc/src/usbtmc_app.c
@@ -97,7 +97,9 @@ bool usbtmcd_app_msg_trigger(uint8_t rhport, usbtmc_msg_generic_t* msg) {
 bool usbtmcd_app_msg_data(uint8_t rhport, void *data, size_t len, bool transfer_complete)
 {
   (void)rhport;
-  (void)transfer_complete;
+
+  // If transfer isn't finished, we just ignore it (for now)
+
   if(transfer_complete && (len >=4) && !strncasecmp("*idn?",data,4)) {
     queryState = 1;
   }
@@ -107,26 +109,24 @@ bool usbtmcd_app_msg_data(uint8_t rhport, void *data, size_t len, bool transfer_
 bool usbtmcd_app_msgBulkIn_complete(uint8_t rhport)
 {
   (void)rhport;
+
+  status &= (uint8_t)~(0x10u); // clear MAV
+
   return true;
 }
 
-static uint8_t noQueryMsg[] = "ERR: No query\n";
-
 bool usbtmcd_app_msgBulkIn_request(uint8_t rhport, usbtmc_msg_request_dev_dep_in const * request)
 {
   rspMsg.header.MsgID = request->header.MsgID,
   rspMsg.header.bTag = request->header.bTag,
   rspMsg.header.bTagInverse = request->header.bTagInverse;
-  if(queryState != 0)
-  {
-    TU_ASSERT(bulkInStarted == 0);
-    bulkInStarted = 1;
-  }
-  else
-  {
-    rspMsg.TransferSize = sizeof(noQueryMsg)-1;
-    usbtmcd_transmit_dev_msg_data(rhport, &rspMsg, noQueryMsg);
-  }
+
+  TU_ASSERT(bulkInStarted == 0);
+  bulkInStarted = 1;
+
+  // > If a USBTMC interface receives a Bulk-IN request prior to receiving a USBTMC command message
+  //   that expects a response, the device must NAK the request
+
   // Always return true indicating not to stall the EP.
   return true;
 }
@@ -157,12 +157,28 @@ void usbtmc_app_task_iter(void) {
       bulkInStarted = 0;
       rspMsg.TransferSize = sizeof(idn)-1;
       usbtmcd_transmit_dev_msg_data(rhport, &rspMsg, idn);
-      status &= ~(0x10u); // MAV
+      // MAV is cleared in the transfer complete callback.
     }
     break;
+  default:
+    TU_ASSERT(false,);
+    return;
   }
 }
 
+bool usbtmcd_app_initiate_clear(uint8_t rhport, uint8_t *tmcResult) {
+  (void)rhport;
+  *tmcResult = USBTMC_STATUS_SUCCESS;
+  return true;
+}
+
+bool usbtmcd_app_get_clear_status(uint8_t rhport, usbtmc_get_clear_status_rsp_t *rsp) {
+  (void)rhport;
+  rsp->USBTMC_status = USBTMC_STATUS_SUCCESS;
+  rsp->bmClear.BulkInFifoBytes = 0u;
+  return true;
+}
+
 // Return status byte, but put the transfer result status code in the rspResult argument.
 uint8_t usbtmcd_app_get_stb(uint8_t rhport, uint8_t *tmcResult)
 {
diff --git a/src/class/usbtmc/usbtmc.h b/src/class/usbtmc/usbtmc.h
index ecc16a315..d24a72cb6 100644
--- a/src/class/usbtmc/usbtmc.h
+++ b/src/class/usbtmc/usbtmc.h
@@ -73,53 +73,67 @@ typedef struct TU_ATTR_PACKED
 typedef struct TU_ATTR_PACKED {
 	usbtmc_msg_header_t header ; ///< Header
 	uint32_t TransferSize      ; ///< Transfer size; LSB first
-	struct {
-      uint8_t EOM  : 1         ; ///< EOM set on last byte
+	struct TU_ATTR_PACKED
+	{
+	  unsigned int EOM  : 1         ; ///< EOM set on last byte
   } bmTransferAttributes;
   uint8_t _reserved[3];
 } usbtmc_msg_request_dev_dep_out;
 
+TU_VERIFY_STATIC(sizeof(usbtmc_msg_request_dev_dep_out) == 12u, "struct wrong length");
+
 // Next 8 bytes are message-specific
-typedef struct TU_ATTR_PACKED {
+typedef struct TU_ATTR_PACKED
+{
   usbtmc_msg_header_t header ; ///< Header
   uint32_t TransferSize      ; ///< Transfer size; LSB first
-  struct {
-      uint8_t : 0;
-      uint8_t TermCharEnabled  : 1 ; ///< "The Bulk-IN transfer must terminate on the specified TermChar."; CAPABILITIES must list TermChar
+  struct TU_ATTR_PACKED
+  {
+    unsigned int TermCharEnabled  : 1 ; ///< "The Bulk-IN transfer must terminate on the specified TermChar."; CAPABILITIES must list TermChar
   } bmTransferAttributes;
   uint8_t TermChar;
   uint8_t _reserved[2];
 } usbtmc_msg_request_dev_dep_in;
 
+TU_VERIFY_STATIC(sizeof(usbtmc_msg_request_dev_dep_in) == 12u, "struct wrong length");
+
 /* Bulk-in headers */
 
 typedef struct TU_ATTR_PACKED
 {
   usbtmc_msg_header_t header;
   uint32_t TransferSize;
-  struct {
+  struct TU_ATTR_PACKED
+  {
     uint8_t EOM: 1;           ///< Last byte of transfer is the end of the message
     uint8_t UsingTermChar: 1; ///< Support TermChar && Request.TermCharEnabled && last char in transfer is TermChar
   } bmTransferAttributes;
   uint8_t _reserved[3];
 } usbtmc_msg_dev_dep_msg_in_header_t;
 
+TU_VERIFY_STATIC(sizeof(usbtmc_msg_dev_dep_msg_in_header_t) == 12u, "struct wrong length");
 
 /* Unsupported vendor things.... Are these ever used?*/
 
-typedef struct TU_ATTR_PACKED {
+typedef struct TU_ATTR_PACKED
+{
   usbtmc_msg_header_t header ; ///< Header
   uint32_t TransferSize      ; ///< Transfer size; LSB first
   uint8_t _reserved[4];
 } usbtmc_msg_request_vendor_specific_out;
 
 
-typedef struct TU_ATTR_PACKED {
+TU_VERIFY_STATIC(sizeof(usbtmc_msg_request_vendor_specific_out) == 12u, "struct wrong length");
+
+typedef struct TU_ATTR_PACKED
+{
   usbtmc_msg_header_t header ; ///< Header
   uint32_t TransferSize      ; ///< Transfer size; LSB first
   uint8_t _reserved[4];
 } usbtmc_msg_request_vendor_specific_in;
 
+TU_VERIFY_STATIC(sizeof(usbtmc_msg_request_vendor_specific_in) == 12u, "struct wrong length");
+
 // Control request type should use tusb_control_request_t
 
 /*
@@ -187,13 +201,15 @@ typedef struct TU_ATTR_PACKED {
   uint8_t _reserved;
   uint16_t bcdUSBTMC;                    ///< USBTMC_VERSION
 
-  struct {
-    uint8_t listenOnly :1;
-    uint8_t talkOnly :1;
-    uint8_t supportsIndicatorPulse :1;
+  struct TU_ATTR_PACKED
+  {
+    unsigned int listenOnly :1;
+    unsigned int talkOnly :1;
+    unsigned int supportsIndicatorPulse :1;
   } bmIntfcCapabilities;
-  struct {
-    uint8_t canEndBulkInOnTermChar :1;
+  struct TU_ATTR_PACKED
+  {
+    unsigned int canEndBulkInOnTermChar :1;
   } bmDevCapabilities;
   uint8_t _reserved2[6];
   uint8_t _reserved3[12];
@@ -201,40 +217,51 @@ typedef struct TU_ATTR_PACKED {
 
 TU_VERIFY_STATIC(sizeof(usbtmc_response_capabilities_t) == 0x18, "struct wrong length");
 
+typedef struct TU_ATTR_PACKED
+{
+  uint8_t USBTMC_status;
+  struct TU_ATTR_PACKED
+  {
+    unsigned int BulkInFifoBytes :1;
+  } bmClear;
+} usbtmc_get_clear_status_rsp_t;
+
+TU_VERIFY_STATIC(sizeof(usbtmc_get_clear_status_rsp_t) == 2u, "struct wrong length");
+
 typedef struct TU_ATTR_PACKED
 {
   uint8_t USBTMC_status;                 ///< usbtmc_status_enum
   uint8_t _reserved;
   uint16_t bcdUSBTMC;                    ///< USBTMC_VERSION
 
-  struct
+  struct TU_ATTR_PACKED
   {
-    uint8_t listenOnly :1;
-    uint8_t talkOnly :1;
-    uint8_t supportsIndicatorPulse :1;
+    unsigned int listenOnly :1;
+    unsigned int talkOnly :1;
+    unsigned int supportsIndicatorPulse :1;
   } bmIntfcCapabilities;
 
-  struct
+  struct TU_ATTR_PACKED
   {
-    uint8_t canEndBulkInOnTermChar :1;
+    unsigned int canEndBulkInOnTermChar :1;
   } bmDevCapabilities;
 
   uint8_t _reserved2[6];
   uint16_t bcdUSB488;
 
-  struct
+  struct TU_ATTR_PACKED
   {
-    uint8_t is488_2 :1;
-    uint8_t supportsREN_GTL_LLO :1;
-    uint8_t supportsTrigger :1;
+    unsigned int is488_2 :1;
+    unsigned int supportsREN_GTL_LLO :1;
+    unsigned int supportsTrigger :1;
   } bmIntfcCapabilities488;
 
-  struct
+  struct TU_ATTR_PACKED
   {
-    uint8_t SCPI :1;
-    uint8_t SR1 :1;
-    uint8_t RL1 :1;
-    uint8_t DT1 :1;
+    unsigned int SCPI :1;
+    unsigned int SR1 :1;
+    unsigned int RL1 :1;
+    unsigned int DT1 :1;
   } bmDevCapabilities488;
   uint8_t _reserved3[8];
 } usbtmc_response_capabilities_488_t;
@@ -253,14 +280,15 @@ TU_VERIFY_STATIC(sizeof(usbtmc_read_stb_rsp_488_t) == 3u, "struct wrong length")
 typedef struct TU_ATTR_PACKET
 {
   union {
-    struct {
-      uint8_t bTag : 7;
-      uint8_t one  : 1;
+    struct TU_ATTR_PACKED {
+      unsigned int bTag : 7;
+      unsigned int one  : 1;
     } bNotify1Struct;
     uint8_t bNotify1;
   };
   uint8_t StatusByte;
 } usbtmc_read_stb_interrupt_488_t;
+
 TU_VERIFY_STATIC(sizeof(usbtmc_read_stb_interrupt_488_t) == 2u, "struct wrong length");
 
 #endif
diff --git a/src/class/usbtmc/usbtmc_device.c b/src/class/usbtmc/usbtmc_device.c
index db0c95a32..7264125bb 100644
--- a/src/class/usbtmc/usbtmc_device.c
+++ b/src/class/usbtmc/usbtmc_device.c
@@ -170,6 +170,9 @@ bool usbtmcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16
   uint8_t const * p_desc;
   uint8_t found_endpoints = 0;
 
+
+  usbtmcd_reset(rhport);
+
   // Perhaps there are other application specific class drivers, so don't assert here.
   if( itf_desc->bInterfaceClass != USBTMC_APP_CLASS)
     return false;
@@ -232,6 +235,12 @@ bool usbtmcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16
 void usbtmcd_reset(uint8_t rhport)
 {
   // FIXME: Do endpoints need to be closed here?
+  usbtmc_state.state = STATE_IDLE;
+  usbtmc_state.itf_id = 0xFF;
+  usbtmc_state.ep_bulk_in = 0;
+  usbtmc_state.ep_bulk_out = 0;
+  usbtmc_state.ep_int_in = 0;
+
   (void)rhport;
 }
 
@@ -376,15 +385,19 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
 
 bool usbtmcd_control_request(uint8_t rhport, tusb_control_request_t const * request) {
 
+  uint8_t tmcStatusCode = USBTMC_STATUS_FAILED;
 #if (USBTMC_CFG_ENABLE_488)
   ushort bTag;
 #endif
   // We only handle class requests, IN direction.
+  // (for now)
   if(request->bmRequestType_bit.type != TUSB_REQ_TYPE_CLASS)
   {
     return false;
   }
 
+  // Verification that we own the interface is unneeded since it's been routed to us specifically.
+
   switch(request->bRequest)
   {
   // USBTMC required requests
@@ -392,74 +405,100 @@ bool usbtmcd_control_request(uint8_t rhport, tusb_control_request_t const * requ
   case USBTMC_bREQUEST_CHECK_ABORT_BULK_OUT_STATUS:
   case USBTMC_bREQUEST_INITIATE_ABORT_BULK_IN:
   case USBTMC_bREQUEST_CHECK_ABORT_BULK_IN_STATUS:
-    TU_VERIFY(request->bmRequestType == 0xA2); // in,class,EP
-    TU_VERIFY(false);
-    break;
+    {
+      TU_VERIFY(request->bmRequestType == 0xA2); // in,class,EP
+      TU_VERIFY(request->wLength == 1u);
+      tmcStatusCode = USBTMC_STATUS_FAILED;
+      usbd_edpt_xfer(rhport, usbtmc_state.ep_int_in, (void*)&tmcStatusCode,sizeof(tmcStatusCode));
+      return true;
+    }
 
   case USBTMC_bREQUEST_INITIATE_CLEAR:
+    {
+      TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
+      TU_VERIFY(request->wLength == sizeof(tmcStatusCode));
+      // After receiving an INITIATE_CLEAR request, the device must Halt the Bulk-OUT endpoint, queue the
+      // control endpoint response shown in Table 31, and clear all input buffers and output buffers.
+      usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
+      TU_VERIFY(usbtmcd_app_initiate_clear(rhport, &tmcStatusCode));
+      TU_VERIFY(usbd_edpt_xfer(rhport, usbtmc_state.ep_int_in, (void*)&tmcStatusCode,sizeof(tmcStatusCode)));
+      return true;
+    }
+
   case USBTMC_bREQUEST_CHECK_CLEAR_STATUS:
-    TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
-    TU_VERIFY(false);
-    break;
+    {
+      usbtmc_get_clear_status_rsp_t clearStatusRsp = {0};
+      TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
+      TU_VERIFY(request->wLength == sizeof(clearStatusRsp));
+      TU_VERIFY(usbtmcd_app_get_clear_status(rhport, &clearStatusRsp));
+
+      TU_VERIFY(usbd_edpt_xfer(rhport, usbtmc_state.ep_int_in, (void*)&clearStatusRsp,sizeof(clearStatusRsp)));
+      return true;
+    }
 
   case USBTMC_bREQUEST_GET_CAPABILITIES:
-    TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
-    TU_VERIFY(request->wValue == 0x0000);
-    TU_VERIFY(request->wIndex == usbtmc_state.itf_id);
-    TU_VERIFY(request->wLength == sizeof(usbtmcd_app_capabilities));
-    TU_VERIFY(tud_control_xfer(rhport, request, (void*)&usbtmcd_app_capabilities, sizeof(usbtmcd_app_capabilities)));
-    return true;
+    {
+      TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
+      TU_VERIFY(request->wLength == sizeof(usbtmcd_app_capabilities));
+      TU_VERIFY(tud_control_xfer(rhport, request, (void*)&usbtmcd_app_capabilities, sizeof(usbtmcd_app_capabilities)));
+      return true;
+    }
   // USBTMC Optional Requests
 
   case USBTMC_bREQUEST_INDICATOR_PULSE: // Optional
-    TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
-    TU_VERIFY(usbtmcd_app_capabilities.bmIntfcCapabilities.supportsIndicatorPulse);
-    uint8_t tmcResult;
-    TU_VERIFY(usbtmcd_app_indicator_pluse(rhport, request, &tmcResult));
-    TU_VERIFY(tud_control_xfer(rhport, request, (void*)&tmcResult, sizeof(tmcResult)));
-
-    return true;
-
+    {
+      TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
+      TU_VERIFY(request->wLength == sizeof(tmcStatusCode));
+      TU_VERIFY(usbtmcd_app_capabilities.bmIntfcCapabilities.supportsIndicatorPulse);
+      TU_VERIFY(usbtmcd_app_indicator_pluse(rhport, request, &tmcStatusCode));
+      TU_VERIFY(tud_control_xfer(rhport, request, (void*)&tmcStatusCode, sizeof(tmcStatusCode)));
+      return true;
+    }
 #if (USBTMC_CFG_ENABLE_488)
+
     // USB488 required requests
   case USBTMC488_bREQUEST_READ_STATUS_BYTE:
-    TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
-
-    bTag = request->wValue & 0x7F;
-    TU_VERIFY(request->bmRequestType == 0xA1);
-    TU_VERIFY((request->wValue & (~0x7F)) == 0u); // Other bits are required to be zero
-    TU_VERIFY(bTag >= 0x02 && bTag <= 127);
-    TU_VERIFY(request->wIndex == usbtmc_state.itf_id);
-    TU_VERIFY(request->wLength == 0x0003);
-    usbtmc_read_stb_rsp_488_t rsp;
-    rsp.bTag = (uint8_t)bTag;
-    if(usbtmc_state.ep_int_in != 0)
     {
-      rsp.USBTMC_status = USBTMC_STATUS_SUCCESS;
-      rsp.statusByte = 0x00; // Use interrupt endpoint, instead.
+      usbtmc_read_stb_rsp_488_t rsp;
+      TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
+      TU_VERIFY(request->wLength == sizeof(rsp)); // in,class,interface
 
-      usbtmc_read_stb_interrupt_488_t intMsg =
+      bTag = request->wValue & 0x7F;
+      TU_VERIFY(request->bmRequestType == 0xA1);
+      TU_VERIFY((request->wValue & (~0x7F)) == 0u); // Other bits are required to be zero
+      TU_VERIFY(bTag >= 0x02 && bTag <= 127);
+      TU_VERIFY(request->wIndex == usbtmc_state.itf_id);
+      TU_VERIFY(request->wLength == 0x0003);
+      rsp.bTag = (uint8_t)bTag;
+      if(usbtmc_state.ep_int_in != 0)
       {
-          .bNotify1   = (uint8_t)(0x80 | bTag),
-          .StatusByte = usbtmcd_app_get_stb(rhport, &(rsp.USBTMC_status))
-      };
-      usbd_edpt_xfer(rhport, usbtmc_state.ep_int_in, (void*)&intMsg,sizeof(intMsg));
+        rsp.USBTMC_status = USBTMC_STATUS_SUCCESS;
+        rsp.statusByte = 0x00; // Use interrupt endpoint, instead.
 
-    }
-    else
-    {
-      rsp.statusByte = usbtmcd_app_get_stb(rhport, &(rsp.USBTMC_status));
-    }
-    TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp, sizeof(rsp)));
-    return true;
+        usbtmc_read_stb_interrupt_488_t intMsg =
+        {
+            .bNotify1   = (uint8_t)(0x80 | bTag),
+            .StatusByte = usbtmcd_app_get_stb(rhport, &(rsp.USBTMC_status))
+        };
+        usbd_edpt_xfer(rhport, usbtmc_state.ep_int_in, (void*)&intMsg,sizeof(intMsg));
 
+      }
+      else
+      {
+        rsp.statusByte = usbtmcd_app_get_stb(rhport, &(rsp.USBTMC_status));
+      }
+      TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp, sizeof(rsp)));
+      return true;
+    }
     // USB488 optional requests
   case USBTMC488_bREQUEST_REN_CONTROL:
   case USBTMC488_bREQUEST_GO_TO_LOCAL:
   case USBTMC488_bREQUEST_LOCAL_LOCKOUT:
-    TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
-    TU_VERIFY(false);
-    return false;
+    {
+      TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
+      TU_VERIFY(false);
+      return false;
+    }
 #endif
 
   default:
diff --git a/src/class/usbtmc/usbtmc_device.h b/src/class/usbtmc/usbtmc_device.h
index 84640b136..702f595e6 100644
--- a/src/class/usbtmc/usbtmc_device.h
+++ b/src/class/usbtmc/usbtmc_device.h
@@ -64,6 +64,10 @@ bool usbtmcd_app_msgBulkIn_request(uint8_t rhport, usbtmc_msg_request_dev_dep_in
 
 bool usbtmcd_app_msgBulkIn_complete(uint8_t rhport);
 
+bool usbtmcd_app_initiate_clear(uint8_t rhport, uint8_t *tmcResult);
+
+bool usbtmcd_app_get_clear_status(uint8_t rhport, usbtmc_get_clear_status_rsp_t *rsp);
+
 // Indicator pulse should be 0.5 to 1.0 seconds long
 TU_ATTR_WEAK bool usbtmcd_app_indicator_pluse(uint8_t rhport, tusb_control_request_t const * msg, uint8_t *tmcResult);