diff options
Diffstat (limited to 'src/class')
| -rw-r--r-- | src/class/usbtmc/usbtmc.h | 109 | ||||
| -rw-r--r-- | src/class/usbtmc/usbtmc_device.c | 417 | ||||
| -rw-r--r-- | src/class/usbtmc/usbtmc_device.h | 18 |
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 |
