summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/class/usbtmc/usbtmc.h109
-rw-r--r--src/class/usbtmc/usbtmc_device.c417
-rw-r--r--src/class/usbtmc/usbtmc_device.h18
3 files changed, 401 insertions, 143 deletions
diff --git a/src/class/usbtmc/usbtmc.h b/src/class/usbtmc/usbtmc.h
index ecc16a315..6893c3743 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];
@@ -203,38 +219,58 @@ TU_VERIFY_STATIC(sizeof(usbtmc_response_capabilities_t) == 0x18, "struct wrong l
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");
+
+// Used for both abort bulk IN and bulk OUT
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t USBTMC_status;
+ uint8_t bTag;
+} usbtmc_initiate_abort_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;
@@ -252,15 +288,14 @@ 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;
- } bNotify1Struct;
- uint8_t bNotify1;
- };
+ struct TU_ATTR_PACKED
+ {
+ unsigned int bTag : 7;
+ unsigned int one : 1;
+ } 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..6d562641e 100644
--- a/src/class/usbtmc/usbtmc_device.c
+++ b/src/class/usbtmc/usbtmc_device.c
@@ -8,7 +8,7 @@
/*
* The MIT License (MIT)
*
- * Copyright (c) 2019 N Conrad
+ * Copyright (c) 2019 Nathan Conrad
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -33,31 +33,53 @@
#include "tusb_option.h"
-// We don't do any cross-task anything here (everything is in tud or interrupt context).
-// You must ensure thread safety in your own app.
+// Synchronization is needed in some spots.
+// These functions should NOT be called from interrupts.
+/* The library is designed that its functions can be called by any user task, with need for
+ * additional locking. In the case of "no OS", this task is never preempted other than by
+ * interrupts, and the USBTMC code isn't called by interrupts, so all is OK. In the case
+ * of an OS, this class driver uses the OSAL to perform locking. The code uses a single lock
+ * and does not call outside of this class with a lock held, so deadlocks won't happen.
+ *
+ * This module's application-facing functions are not reentrant. The application must
+ * only call them from a single thread (or implement its own locking).
+ */
-//Limitations (not planned to be implemented):
-// "vendor-specific" commands are not handled
+
+//Limitations:
+// "vendor-specific" commands are not handled.
// Dealing with "termchar" must be handled by the application layer,
// though additional error checking is does in this module.
+// talkOnly and listenOnly are NOT supported. They're no permitted
+// in USB488, anyway.
+
+/* Supported:
+ *
+ * Notification pulse
+ * Trigger
+ * Read status byte (both by interrupt endpoint and control message)
+ *
+ */
+
// TODO:
// USBTMC 3.2.2 error conditions not strictly followed
// No local lock-out, REN, or GTL.
// Cannot handle clear.
-// Not all "capabilities" supported
// Clear message available status byte at the correct time? (488 4.3.1.3)
-// Split transfers
+// Abort bulk in/out
// No CLEAR_FEATURE/HALT no EP (yet)
-// No aborting transfers.
#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_USBTMC)
+#include <string.h>
#include "usbtmc.h"
#include "usbtmc_device.h"
#include "device/dcd.h"
#include "device/usbd.h"
+#include "uart_util.h"
+static char logMsg[150];
// FIXME: I shouldn't need to include _pvt headers.
#include "device/usbd_pvt.h"
@@ -70,21 +92,31 @@ typedef enum
STATE_IDLE,
STATE_RCV,
STATE_TX_REQUESTED,
- STATE_TX_INITIATED
+ STATE_TX_INITIATED,
+ STATE_CLEARING,
+ STATE_ABORTING_BULK_IN,
+ STATE_ABORTING_BULK_OUT,
+ STATE_NUM_STATES
} usbtmcd_state_enum;
typedef struct
{
- usbtmcd_state_enum state;
+ volatile usbtmcd_state_enum state;
+
uint8_t itf_id;
uint8_t ep_bulk_in;
uint8_t ep_bulk_out;
uint8_t ep_int_in;
- uint8_t ep_bulk_in_buf[64];
- uint8_t ep_bulk_out_buf[64];
- uint8_t lastTag;
+ // IN buffer is only used for first packet, not the remainder
+ // in order to deal with prepending header
+ uint8_t ep_bulk_in_buf[USBTMCD_MAX_PACKET_SIZE];
+ // OUT buffer receives one packet at a time
+ uint8_t ep_bulk_out_buf[USBTMCD_MAX_PACKET_SIZE];
+ uint32_t transfer_size_remaining; // also used for requested length for bulk IN.
+
+ uint8_t lastBulkOutTag; // used for aborts (mostly)
+ uint8_t lastBulkInTag; // used for aborts (mostly)
- uint32_t transfer_size_remaining;
uint8_t const * devInBuffer;
} usbtmc_interface_state_t;
@@ -97,38 +129,63 @@ static usbtmc_interface_state_t usbtmc_state =
.ep_int_in = 0
};
-// We want everything to fit nicely in a single packet, so lets require EP size >32
-// I'm not sure if this is really necessary, though.
+// We need all headers to fit in a single packet in this implementation.
TU_VERIFY_STATIC(USBTMCD_MAX_PACKET_SIZE >= 32u,"USBTMC dev EP packet size too small");
-
+TU_VERIFY_STATIC(
+ (sizeof(usbtmc_state.ep_bulk_in_buf) % USBTMCD_MAX_PACKET_SIZE) == 0,
+ "packet buffer must be a multiple of the packet size");
static bool handle_devMsgOutStart(uint8_t rhport, void *data, size_t len);
static bool handle_devMsgOut(uint8_t rhport, void *data, size_t len, size_t packetLen);
+
+osal_mutex_def_t usbtmcLockBuffer;
+static osal_mutex_t usbtmcLock;
+
+// Our own private lock, mostly for the state variable.
+#define criticalEnter() do {osal_mutex_lock(usbtmcLock,OSAL_TIMEOUT_WAIT_FOREVER); } while (0)
+#define criticalLeave() do {osal_mutex_unlock(usbtmcLock); } while (0)
+
// called from app
// We keep a reference to the buffer, so it MUST not change until the app is
// notified that the transfer is complete.
// length of data is specified in the hdr.
+
+// We can't just send the whole thing at once because we need to concatanate the
+// header with the data.
bool usbtmcd_transmit_dev_msg_data(
uint8_t rhport,
- usbtmc_msg_dev_dep_msg_in_header_t const * hdr,
- const void *data)
+ const void * data, size_t len,
+ bool usingTermChar)
{
- TU_ASSERT(usbtmc_state.state == STATE_TX_REQUESTED);
- TU_ASSERT(hdr->TransferSize > 0u);
+ const unsigned int txBufLen = sizeof(usbtmc_state.ep_bulk_in_buf);
- if(hdr->bmTransferAttributes.UsingTermChar)
+#ifndef NDEBUG
+ TU_ASSERT(len > 0u);
+ TU_ASSERT(len <= usbtmc_state.transfer_size_remaining);
+ if(usingTermChar)
{
TU_ASSERT(usbtmcd_app_capabilities.bmDevCapabilities.canEndBulkInOnTermChar);
TU_ASSERT(termCharRequested);
- TU_ASSERT(((uint8_t*)data)[hdr->TransferSize-1] == termChar);
+ TU_ASSERT(((uint8_t*)data)[len-1] == termChar);
}
+#endif
+
+ TU_VERIFY(usbtmc_state.state == STATE_TX_REQUESTED);
+ usbtmc_msg_dev_dep_msg_in_header_t *hdr = (usbtmc_msg_dev_dep_msg_in_header_t*)usbtmc_state.ep_bulk_in_buf;
+ memset(hdr, 0x00, sizeof(*hdr));
+ hdr->header.MsgID = USBTMC_MSGID_DEV_DEP_MSG_IN;
+ hdr->header.bTag = usbtmc_state.lastBulkInTag;
+ hdr->header.bTagInverse = (uint8_t)~(usbtmc_state.lastBulkInTag);
+ hdr->TransferSize = len;
+ hdr->bmTransferAttributes.EOM = 1u;
+ hdr->bmTransferAttributes.UsingTermChar = usingTermChar;
// Copy in the header
- memcpy(usbtmc_state.ep_bulk_in_buf, hdr, sizeof(*hdr));
- uint packetLen = sizeof(*hdr);
- // Single-packet transfer
- if((packetLen + hdr->TransferSize) <= USBTMCD_MAX_PACKET_SIZE)
+ size_t packetLen = sizeof(*hdr);
+
+ // If it fits in a single trasnmission:
+ if((packetLen + hdr->TransferSize) <= txBufLen)
{
memcpy((uint8_t*)(usbtmc_state.ep_bulk_in_buf) + packetLen, data, hdr->TransferSize);
packetLen = (uint16_t)(packetLen+ hdr->TransferSize);
@@ -141,27 +198,41 @@ bool usbtmcd_transmit_dev_msg_data(
usbtmc_state.transfer_size_remaining = 0;
usbtmc_state.devInBuffer = NULL;
}
- else
+ else /* partial packet */
+ {
+ memcpy((uint8_t*)(usbtmc_state.ep_bulk_in_buf) + packetLen, data, txBufLen - packetLen);
+ usbtmc_state.devInBuffer += txBufLen - packetLen;
+ usbtmc_state.transfer_size_remaining = hdr->TransferSize - (txBufLen - packetLen);
+ packetLen = txBufLen;
+ }
+
+
+ criticalEnter();
{
- memcpy((uint8_t*)(usbtmc_state.ep_bulk_in_buf) + packetLen, data, USBTMCD_MAX_PACKET_SIZE - packetLen);
- usbtmc_state.transfer_size_remaining = hdr->TransferSize - (USBTMCD_MAX_PACKET_SIZE - packetLen);
- usbtmc_state.devInBuffer += (USBTMCD_MAX_PACKET_SIZE - packetLen);
- packetLen = USBTMCD_MAX_PACKET_SIZE;
+ TU_VERIFY(usbtmc_state.state == STATE_TX_REQUESTED);
+ usbtmc_state.state = STATE_TX_INITIATED;
}
- usbtmc_state.state = STATE_TX_INITIATED;
- TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf,(uint16_t)packetLen));
+ criticalLeave();
+
+ TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf, (uint16_t)packetLen));
return true;
}
void usbtmcd_init(void)
{
-#if USBTMC_CFG_ENABLE_488
+#ifndef NDEBUG
+# if USBTMC_CFG_ENABLE_488
if(usbtmcd_app_capabilities.bmIntfcCapabilities488.supportsTrigger)
TU_ASSERT(&usbtmcd_app_msg_trigger != NULL,);
+ // Per USB488 spec: table 8
+ TU_ASSERT(!usbtmcd_app_capabilities.bmIntfcCapabilities.listenOnly,);
+ TU_ASSERT(!usbtmcd_app_capabilities.bmIntfcCapabilities.talkOnly,);
+# endif
+ if(usbtmcd_app_capabilities.bmIntfcCapabilities.supportsIndicatorPulse)
+ TU_ASSERT(&usbtmcd_app_indicator_pluse != NULL,);
#endif
- if(usbtmcd_app_capabilities.bmIntfcCapabilities.supportsIndicatorPulse)
- TU_ASSERT(&usbtmcd_app_indicator_pluse != NULL,);
+ usbtmcLock = osal_mutex_create(&usbtmcLockBuffer);
}
bool usbtmcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length)
@@ -170,6 +241,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;
@@ -201,8 +275,10 @@ bool usbtmcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16
break;
case TUSB_XFER_INTERRUPT:
+#ifndef NDEBUG
TU_ASSERT(tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN);
TU_ASSERT(usbtmc_state.ep_int_in == 0);
+#endif
usbtmc_state.ep_int_in = ep_desc->bEndpointAddress;
break;
default:
@@ -216,15 +292,24 @@ bool usbtmcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16
}
// bulk endpoints are required, but interrupt IN is optional
+#ifndef NDEBUG
TU_ASSERT(usbtmc_state.ep_bulk_in != 0);
TU_ASSERT(usbtmc_state.ep_bulk_out != 0);
- if (itf_desc->bNumEndpoints == 2) {
+ if (itf_desc->bNumEndpoints == 2)
+ {
TU_ASSERT(usbtmc_state.ep_int_in == 0);
}
- else if (itf_desc->bNumEndpoints == 2)
+ else if (itf_desc->bNumEndpoints == 3)
+ {
+ TU_ASSERT(usbtmc_state.ep_int_in != 0);
+ }
+
+ if(usbtmcd_app_capabilities.bmIntfcCapabilities488.is488_2 ||
+ usbtmcd_app_capabilities.bmDevCapabilities488.SR1)
{
TU_ASSERT(usbtmc_state.ep_int_in != 0);
}
+#endif
TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_out, usbtmc_state.ep_bulk_out_buf, 64));
return true;
@@ -232,6 +317,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;
}
@@ -271,12 +362,22 @@ static bool handle_devMsgIn(uint8_t rhport, void *data, size_t len)
{
TU_VERIFY(len == sizeof(usbtmc_msg_request_dev_dep_in));
usbtmc_msg_request_dev_dep_in *msg = (usbtmc_msg_request_dev_dep_in*)data;
- TU_VERIFY(usbtmc_state.state == STATE_IDLE);
- usbtmc_state.state = STATE_TX_REQUESTED;
- usbtmc_state.transfer_size_remaining = msg->TransferSize;
+
+ sprintf(logMsg," handle_devMsgIn len=%ul\r\n",len);
+ uart_tx_str_sync(logMsg);
+
+ criticalEnter();
+ {
+ TU_VERIFY(usbtmc_state.state == STATE_IDLE);
+ usbtmc_state.state = STATE_TX_REQUESTED;
+ usbtmc_state.lastBulkInTag = msg->header.bTag;
+ usbtmc_state.transfer_size_remaining = msg->TransferSize;
+ }
+ criticalLeave();
termCharRequested = msg->bmTransferAttributes.TermCharEnabled;
termChar = msg->TermChar;
+
if(termCharRequested)
TU_VERIFY(usbtmcd_app_capabilities.bmDevCapabilities.canEndBulkInOnTermChar);
@@ -287,8 +388,17 @@ static bool handle_devMsgIn(uint8_t rhport, void *data, size_t len)
bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
{
TU_VERIFY(result == XFER_RESULT_SUCCESS);
+ uart_tx_str_sync("USBTMC Xfer CB" );
+ sprintf(logMsg," STATE=%lu ", (uint32_t)usbtmc_state.state);
+ uart_tx_str_sync(logMsg);
+
+ if(usbtmc_state.state == STATE_CLEARING) {
+ return true; /* I think we can ignore everything here */
+ }
+
if(ep_addr == usbtmc_state.ep_bulk_out)
{
+ uart_tx_str_sync("OUT");
switch(usbtmc_state.state)
{
case STATE_IDLE:
@@ -297,15 +407,19 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
uint8_t invInvTag = (uint8_t)~(msg->header.bTagInverse);
TU_VERIFY(msg->header.bTag == invInvTag);
TU_VERIFY(msg->header.bTag != 0x00);
- usbtmc_state.lastTag = msg->header.bTag;
+
+ sprintf(logMsg," type=%lu\r\n",(uint32_t)msg->header.MsgID);
+ uart_tx_str_sync(logMsg);
switch(msg->header.MsgID) {
case USBTMC_MSGID_DEV_DEP_MSG_OUT:
TU_VERIFY(handle_devMsgOutStart(rhport, msg, xferred_bytes));
TU_VERIFY(usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_out, usbtmc_state.ep_bulk_out_buf, USBTMCD_MAX_PACKET_SIZE));
+ usbtmc_state.lastBulkOutTag = msg->header.bTag;
break;
case USBTMC_MSGID_DEV_DEP_MSG_IN:
+ uart_tx_sync("Handling msg in req\r\n", 21);
TU_VERIFY(handle_devMsgIn(rhport, msg, xferred_bytes));
break;
@@ -321,6 +435,7 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
case USBTMC_MSGID_VENDOR_SPECIFIC_MSG_OUT:
case USBTMC_MSGID_VENDOR_SPECIFIC_IN:
default:
+
TU_VERIFY(false);
return false;
}
@@ -332,12 +447,22 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
case STATE_TX_REQUESTED:
case STATE_TX_INITIATED:
+ case STATE_ABORTING_BULK_IN:
+ case STATE_ABORTING_BULK_OUT:
default:
+ if(msg == NULL)
+ sprintf(logMsg," Unknown received control?\r\n ");
+ else {
+ sprintf(logMsg," msg=%lu\r\n ", (uint32_t)msg->header.MsgID);
+ }
+ uart_tx_str_sync(logMsg);
TU_VERIFY(false);
}
}
else if(ep_addr == usbtmc_state.ep_bulk_in)
{
+ sprintf(logMsg,"IN\r\n");
+ uart_tx_str_sync(logMsg);
TU_ASSERT(usbtmc_state.state == STATE_TX_INITIATED);
if(usbtmc_state.transfer_size_remaining == 0)
{
@@ -345,16 +470,16 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
TU_VERIFY(usbtmcd_app_msgBulkIn_complete(rhport));
TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_out, usbtmc_state.ep_bulk_out_buf, USBTMCD_MAX_PACKET_SIZE));
}
- else if(usbtmc_state.transfer_size_remaining >= USBTMCD_MAX_PACKET_SIZE)
+ else if(usbtmc_state.transfer_size_remaining > sizeof(usbtmc_state.devInBuffer))
{
- memcpy(usbtmc_state.ep_bulk_in_buf, usbtmc_state.devInBuffer, USBTMCD_MAX_PACKET_SIZE);
- usbtmc_state.devInBuffer += USBTMCD_MAX_PACKET_SIZE;
- usbtmc_state.transfer_size_remaining -= USBTMCD_MAX_PACKET_SIZE;
- TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf,USBTMCD_MAX_PACKET_SIZE));
+ memcpy(usbtmc_state.ep_bulk_in_buf, usbtmc_state.devInBuffer, sizeof(usbtmc_state.ep_bulk_in_buf));
+ usbtmc_state.devInBuffer += sizeof(usbtmc_state.devInBuffer);
+ usbtmc_state.transfer_size_remaining -= sizeof(usbtmc_state.devInBuffer);
+ TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf,sizeof(usbtmc_state.devInBuffer)));
}
- else // short packet
+ else // last packet
{
- uint packetLen = usbtmc_state.transfer_size_remaining;
+ size_t packetLen = usbtmc_state.transfer_size_remaining;
memcpy(usbtmc_state.ep_bulk_in_buf, usbtmc_state.devInBuffer, usbtmc_state.transfer_size_remaining);
while((packetLen % 4) != 0)
{
@@ -376,94 +501,188 @@ 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;
+ uint8_t bTag;
#endif
+
+ if((request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) &&
+ (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_ENDPOINT) &&
+ (request->bRequest == TUSB_REQ_CLEAR_FEATURE) &&
+ (request->wValue == TUSB_REQ_FEATURE_EDPT_HALT))
+ {
+ uart_tx_str_sync("feature clear\r\n");
+ if((request->wIndex) == usbtmc_state.ep_bulk_out)
+ {
+ usmtmcd_app_bulkOut_clearFeature(rhport);
+ }
+ else if ((request->wIndex) == usbtmc_state.ep_bulk_in)
+ {
+ usmtmcd_app_bulkIn_clearFeature(rhport);
+ }
+ return false; // We want USBD core to handle sending the status response, and clear the stall condition
+ }
+
// 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
case USBTMC_bREQUEST_INITIATE_ABORT_BULK_OUT:
case USBTMC_bREQUEST_CHECK_ABORT_BULK_OUT_STATUS:
- case USBTMC_bREQUEST_INITIATE_ABORT_BULK_IN:
+ {
+ TU_VERIFY(request->bmRequestType == 0xA2); // in,class,EP
+ TU_VERIFY(request->wLength == 1u);
+ tmcStatusCode = USBTMC_STATUS_FAILED;
+ usbd_edpt_xfer(rhport, 0u, (void*)&tmcStatusCode,sizeof(tmcStatusCode));
+ return true;
+ }
case USBTMC_bREQUEST_CHECK_ABORT_BULK_IN_STATUS:
+ {
TU_VERIFY(request->bmRequestType == 0xA2); // in,class,EP
- TU_VERIFY(false);
- break;
+ TU_VERIFY(request->wLength == 1u);
+ usbtmc_get_clear_status_rsp_t clearStatusRsp = {0};
+ tmcStatusCode = USBTMC_STATUS_FAILED;
+ usbd_edpt_xfer(rhport, 0u, (void*)&tmcStatusCode,sizeof(tmcStatusCode));
+ return true;
+ }
+
+ case USBTMC_bREQUEST_INITIATE_ABORT_BULK_IN:
+ {
+ usbtmc_initiate_abort_rsp_t rsp = {0};
+ uart_tx_str_sync("init abort bulk in\r\n");
+ TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
+ TU_VERIFY(request->wLength == sizeof(tmcStatusCode));
+ TU_VERIFY(request->wIndex == usbtmc_state.ep_int_in);
+ // wValue is the requested bTag to abort
+ usbtmc_state.transfer_size_remaining = 0;
+ usbtmc_state.state = STATE_ABORTING_BULK_IN;
+ TU_VERIFY(usbtmcd_app_initiate_clear(rhport, &tmcStatusCode));
+ TU_VERIFY(tud_control_xfer(rhport, request, (void*)&tmcStatusCode,sizeof(tmcStatusCode)));
+ return true;
+ }
case USBTMC_bREQUEST_INITIATE_CLEAR:
+ {
+ uart_tx_str_sync("init clear\r\n");
+ 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);
+ usbtmc_state.transfer_size_remaining = 0;
+ usbtmc_state.state = STATE_CLEARING;
+ TU_VERIFY(usbtmcd_app_initiate_clear(rhport, &tmcStatusCode));
+ TU_VERIFY(tud_control_xfer(rhport, request, (void*)&tmcStatusCode,sizeof(tmcStatusCode)));
+ return true;
+ }
+
case USBTMC_bREQUEST_CHECK_CLEAR_STATUS:
- TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
- TU_VERIFY(false);
- break;
+ {
+ uart_tx_str_sync("check clear\r\n");
+ TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
+ usbtmc_get_clear_status_rsp_t clearStatusRsp = {0};
+ TU_VERIFY(request->wLength == sizeof(clearStatusRsp));
+
+ if(usbd_edpt_busy(rhport, usbtmc_state.ep_bulk_in))
+ {
+ // Stuff stuck in TX buffer?
+ clearStatusRsp.bmClear.BulkInFifoBytes = 1;
+ clearStatusRsp.USBTMC_status = USBTMC_STATUS_PENDING;
+ }
+ else
+ {
+ // Let app check if it's clear
+ TU_VERIFY(usbtmcd_app_get_clear_status(rhport, &clearStatusRsp));
+ }
+ if(clearStatusRsp.USBTMC_status == USBTMC_STATUS_SUCCESS)
+ usbtmc_state.state = STATE_IDLE;
+ TU_VERIFY(tud_control_xfer(rhport, request, (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;
+ {
+ uart_tx_str_sync("get capabilities\r\n");
+ 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;
-
+ {
+ uart_tx_str_sync("indicate\r\n");
+ 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.
+ uart_tx_str_sync("read stb\r\n");
+ 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.
+ usbtmc_read_stb_interrupt_488_t intMsg =
+ {
+ .bNotify1 = {
+ .one = 1,
+ .bTag = bTag & 0x7Fu,
+ },
+ .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;
}
- 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;
+ {
+ uart_tx_str_sync("Unsupported REN/GTL/LLO\r\n");
+ TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
+ TU_VERIFY(false);
+ return false;
+ }
#endif
default:
+ uart_tx_str_sync("Default CTRL handler\r\n");
TU_VERIFY(false);
+ return false;
}
TU_VERIFY(false);
}
diff --git a/src/class/usbtmc/usbtmc_device.h b/src/class/usbtmc/usbtmc_device.h
index 84640b136..a0ba82e30 100644
--- a/src/class/usbtmc/usbtmc_device.h
+++ b/src/class/usbtmc/usbtmc_device.h
@@ -56,13 +56,17 @@ extern usbtmc_response_capabilities_t const usbtmcd_app_capabilities;
#endif
bool usbtmcd_app_msgBulkOut_start(uint8_t rhport, usbtmc_msg_request_dev_dep_out const * msgHeader);
-
// transfer_complete does not imply that a message is complete.
bool usbtmcd_app_msg_data(uint8_t rhport, void *data, size_t len, bool transfer_complete);
+void usmtmcd_app_bulkOut_clearFeature(uint8_t rhport); // Notice to clear and abort the pending BULK out transfer
bool usbtmcd_app_msgBulkIn_request(uint8_t rhport, usbtmc_msg_request_dev_dep_in const * request);
-
bool usbtmcd_app_msgBulkIn_complete(uint8_t rhport);
+void usmtmcd_app_bulkIn_clearFeature(uint8_t rhport); // Notice to clear and abort the pending BULK out transfer
+
+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);
@@ -82,8 +86,8 @@ TU_ATTR_WEAK bool usbtmcd_app_msg_trigger(uint8_t rhport, usbtmc_msg_generic_t*
bool usbtmcd_transmit_dev_msg_data(
uint8_t rhport,
- usbtmc_msg_dev_dep_msg_in_header_t const * hdr,
- const void *data);
+ const void * data, size_t len,
+ bool usingTermChar);
/* "callbacks" from USB device core */
@@ -100,10 +104,10 @@ void usbtmcd_init(void);
*************************************************************/
#define USBTMC_APP_CLASS TUSB_CLASS_APPLICATION_SPECIFIC
-#define USBTMC_APP_SUBCLASS 0x03
+#define USBTMC_APP_SUBCLASS 0x03u
-#define USBTMC_PROTOCOL_STD 0x00
-#define USBTMC_PROTOCOL_USB488 0x01
+#define USBTMC_PROTOCOL_STD 0x00u
+#define USBTMC_PROTOCOL_USB488 0x01u
// Interface number, number of endpoints, EP string index, USB_TMC_PROTOCOL*, bulk-out endpoint ID,
// bulk-in endpoint ID