diff options
| author | Nathan Conrad <[email protected]> | 2019-09-14 12:13:11 -0400 |
|---|---|---|
| committer | Nathan Conrad <[email protected]> | 2019-09-14 13:35:08 -0400 |
| commit | 1cae96951f59e7549ff1a2d5643cda66784c22a1 (patch) | |
| tree | 501556a1f3915c88c2019d06a8b455dee13a9fc6 /src/class | |
| parent | ac8c343fef785c9aa19ba628f746e72774b9e8c8 (diff) | |
Add usbtmc class driver.
Diffstat (limited to 'src/class')
| -rw-r--r-- | src/class/usbtmc/usbtmc.h | 267 | ||||
| -rw-r--r-- | src/class/usbtmc/usbtmc_device.c | 420 | ||||
| -rw-r--r-- | src/class/usbtmc/usbtmc_device.h | 130 |
3 files changed, 817 insertions, 0 deletions
diff --git a/src/class/usbtmc/usbtmc.h b/src/class/usbtmc/usbtmc.h new file mode 100644 index 000000000..846c0e38d --- /dev/null +++ b/src/class/usbtmc/usbtmc.h @@ -0,0 +1,267 @@ + +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 N 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 + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_USBTMC_H__ +#define _TUSB_USBTMC_H__ + +#include "common/tusb_common.h" + + +/* Implements USBTMC Revision 1.0, April 14, 2003 + + String descriptors must have a "LANGID=0x409"/US English string. + Characters must be 0x20 (' ') to 0x7E ('~') ASCII, + But MUST not contain: "/:?\* + Also must not have leading or trailing space (' ') + Device descriptor must state USB version 0x0200 or greater + + If USB488DeviceCapabilites.D2 = 1 (SR1), then there must be a INT endpoint. +*/ + +#define USBTMC_VERSION 0x0100 +#define USBTMC_488_VERSION 0x0100 + +typedef enum { + USBTMC_MSGID_DEV_DEP_MSG_OUT = 1u, + USBTMC_MSGID_DEV_DEP_MSG_IN = 2u, + USBTMC_MSGID_VENDOR_SPECIFIC_MSG_OUT = 126u, + USBTMC_MSGID_VENDOR_SPECIFIC_IN = 127u, + USBTMC_MSGID_USB488_TRIGGER = 128u, +} usbtmc_msgid_enum; + +/// \brief Message header (For BULK OUT and BULK IN); 4 bytes +typedef struct TU_ATTR_PACKED +{ + uint8_t MsgID ; ///< Message type ID (usbtmc_msgid_enum) + uint8_t bTag ; ///< Transfer ID 1<=bTag<=255 + uint8_t bTagInverse ; ///< Complement of the tag + uint8_t _reserved ; ///< Must be 0x00 +} usbtmc_msg_header_t; + +typedef struct TU_ATTR_PACKED +{ + usbtmc_msg_header_t header; + uint8_t data[8]; +} usbtmc_msg_generic_t; + +/* Uses on the bulk-out endpoint: */ +// Next 8 bytes are message-specific +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 + } bmTransferAttributes; + uint8_t _reserved[3]; +} usbtmc_msg_request_dev_dep_out; + +// Next 8 bytes are message-specific +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 + } bmTransferAttributes; + uint8_t TermChar; + uint8_t _reserved[2]; +} usbtmc_msg_request_dev_dep_in; + +/* Bulk-in headers */ + +typedef struct TU_ATTR_PACKED +{ + usbtmc_msg_header_t header; + uint32_t TransferSize; + struct { + 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; + + +/* Unsupported vendor things.... Are these ever used?*/ + +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 { + usbtmc_msg_header_t header ; ///< Header + uint32_t TransferSize ; ///< Transfer size; LSB first + uint8_t _reserved[4]; +} usbtmc_msg_request_vendor_specific_in; + +// Control request type should use tusb_control_request_t + +/* +typedef struct TU_ATTR_PACKED { + struct { + uint8_t Recipient : 5 ; ///< EOM set on last byte + uint8_t Type : 2 ; ///< EOM set on last byte + uint8_t DirectionToHost : 1 ; ///< 0 is OUT, 1 is IN + } bmRequestType; + uint8_t bRequest ; ///< If bmRequestType.Type = Class, see usmtmc_request_type_enum + uint16_t wValue ; + uint16_t wIndex ; + uint16_t wLength ; // Number of bytes in data stage +} usbtmc_class_specific_control_req; + +*/ +// bulk-in protocol errors +enum { + USBTMC_BULK_IN_ERR_INCOMPLETE_HEADER = 1u, + USBTMC_BULK_IN_ERR_UNSUPPORTED = 2u, + USBTMC_BULK_IN_ERR_BAD_PARAMETER = 3u, + USBTMC_BULK_IN_ERR_DATA_TOO_SHORT = 4u, + USBTMC_BULK_IN_ERR_DATA_TOO_LONG = 5u, +}; +// bult-in halt errors +enum { + USBTMC_BULK_IN_ERR = 1u, ///< receives a USBTMC command message that expects a response while a + /// Bulk-IN transfer is in progress +}; + +typedef enum { + USBTMC_bREQUEST_INITIATE_ABORT_BULK_OUT = 1u, + USBTMC_bREQUEST_CHECK_ABORT_BULK_OUT_STATUS = 2u, + USBTMC_bREQUEST_INITIATE_ABORT_BULK_IN = 3u, + USBTMC_bREQUEST_CHECK_ABORT_BULK_IN_STATUS = 4u, + USBTMC_bREQUEST_INITIATE_CLEAR = 5u, + USBTMC_bREQUEST_CHECK_CLEAR_STATUS = 6u, + USBTMC_bREQUEST_GET_CAPABILITIES = 7u, + + USBTMC_bREQUEST_INDICATOR_PULSE = 64u, // Optional +} usmtmc_request_type_enum; + +typedef enum { + USBTMC488_bREQUEST_READ_STATUS_BYTE = 128u, + USBTMC488_bREQUEST_REN_CONTROL = 160u, + USBTMC488_bREQUEST_GO_TO_LOCAL = 161u, + USBTMC488_bREQUEST_LOCAL_LOCKOUT = 162u, +} usbtmc_request_type_488_enum; + +typedef enum { + USBTMC_STATUS_SUCCESS = 0x01, + USBTMC_STATUS_PENDING = 0x02, + USBTMC_STATUS_FAILED = 0x80, + USBTMC_STATUS_TRANSFER_NOT_IN_PROGRESS = 0x81, + USBTMC_STATUS_SPLIT_NOT_IN_PROGRESS = 0x82, + USBTMC_STATUS_SPLIT_IN_PROGRESS = 0x83 +} usbtmc_status_enum; + +/************************************************************ + * Control Responses + */ + +typedef struct TU_ATTR_PACKED { + uint8_t USBTMC_status; ///< usbtmc_status_enum + uint8_t _reserved; + uint16_t bcdUSBTMC; ///< USBTMC_VERSION + + struct { + uint8_t listenOnly :1; + uint8_t talkOnly :1; + uint8_t supportsIndicatorPulse :1; + } bmIntfcCapabilities; + struct { + uint8_t canEndBulkInOnTermChar :1; + } bmDevCapabilities; + uint8_t _reserved2[6]; + uint8_t _reserved3[12]; +} usbtmc_response_capabilities_t; + +TU_VERIFY_STATIC(sizeof(usbtmc_response_capabilities_t) == 0x18, "struct wrong length"); + +typedef struct TU_ATTR_PACKED +{ + uint8_t USBTMC_status; ///< usbtmc_status_enum + uint8_t _reserved; + uint16_t bcdUSBTMC; ///< USBTMC_VERSION + + struct + { + uint8_t listenOnly :1; + uint8_t talkOnly :1; + uint8_t supportsIndicatorPulse :1; + } bmIntfcCapabilities; + + struct + { + uint8_t canEndBulkInOnTermChar :1; + } bmDevCapabilities; + + uint8_t _reserved2[6]; + uint16_t bcdUSB488; + + struct + { + uint8_t is488_2 :1; + uint8_t supportsREN_GTL_LLO :1; + uint8_t supportsTrigger :1; + } bmIntfcCapabilities488; + + struct + { + uint8_t SCPI :1; + uint8_t SR1 :1; + uint8_t RL1 :1; + uint8_t DT1 :1; + } bmDevCapabilities488; + uint8_t _reserved3[8]; +} usbtmc_response_capabilities_488_t; + +TU_VERIFY_STATIC(sizeof(usbtmc_response_capabilities_488_t) == 0x18, "struct wrong length"); + +typedef struct TU_ATTR_PACKED +{ + uint8_t USBTMC_status; + uint8_t bTag; + uint8_t statusByte; +} usbtmc_read_stb_rsp_488_t; + +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; + }; + 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 new file mode 100644 index 000000000..4bb21f354 --- /dev/null +++ b/src/class/usbtmc/usbtmc_device.c @@ -0,0 +1,420 @@ +/* + * usbtmc.c + * + * Created on: Sep 9, 2019 + * Author: nconrad + */ + +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 N 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 + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#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. + + +//Limitations (not planned to be implemented): +// "vendor-specific" commands are not handled + +// TODO: +// USBTMC 3.2.2 error conditions not strictly followed +// No local lock-out, REN, or GTL. +// Cannot issue clear. +// No "capabilities" supported +// Interrupt-IN endpoint +// 488 MsgID=Trigger +// Clear message available status byte at the correct time? (488 4.3.1.3) +// Split transfers +// No CLEAR_FEATURE/HALT (yet) + +#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_USBTMC) + +#include "usbtmc.h" +#include "usbtmc_device.h" +#include "device/dcd.h" +#include "device/usbd.h" + +// FIXME: I shouldn't need to include _pvt headers. +#include "device/usbd_pvt.h" + +typedef enum +{ + STATE_IDLE, + STATE_RCV, + STATE_TX_REQUESTED, + STATE_TX_INITIATED +} usbtmcd_state_enum; + +typedef struct +{ + 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; + + uint32_t transfer_size_remaining; + uint8_t const * devInBuffer; +} usbtmc_interface_state_t; + +static usbtmc_interface_state_t usbtmc_state = +{ + .state = STATE_IDLE, + .itf_id = 0xFF, + .ep_bulk_in = 0, + .ep_bulk_out = 0, + .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. +TU_VERIFY_STATIC(USBTMCD_MAX_PACKET_SIZE >= 32u,"USBTMC dev EP packet size too small"); + +// 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. +bool usbtmcd_transmit_dev_msg_data( + uint8_t rhport, + usbtmc_msg_dev_dep_msg_in_header_t const * hdr, + const void *data) +{ + TU_ASSERT(usbtmc_state.state == STATE_TX_REQUESTED); + TU_ASSERT(hdr->TransferSize > 0u); + + // 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) + { + memcpy((uint8_t*)(usbtmc_state.ep_bulk_in_buf) + packetLen, data, hdr->TransferSize); + packetLen = (uint16_t)(packetLen+ hdr->TransferSize); + // Pad up to multiple of 4 bytes + while((packetLen % 4) != 0) + { + usbtmc_state.ep_bulk_in_buf[packetLen] = 0; + packetLen++; + } + usbtmc_state.transfer_size_remaining = 0; + usbtmc_state.devInBuffer = NULL; + } + else + { + 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; + } + 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)); + return true; +} + +void usbtmcd_init(void) +{ + +} + +bool usbtmcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length) +{ + (void)rhport; + uint8_t const * p_desc; + uint8_t found_endpoints = 0; + + // Perhaps there are other application specific class drivers, so don't assert here. + if( itf_desc->bInterfaceClass != USBTMC_APP_CLASS) + return false; + if( itf_desc->bInterfaceSubClass != USBTMC_APP_SUBCLASS) + return false; + + // Only 2 or 3 endpoints are allowed for USBTMC. + TU_ASSERT((itf_desc->bNumEndpoints == 2) || (itf_desc->bNumEndpoints ==3)); + + // Interface + (*p_length) = 0u; + p_desc = (uint8_t const *) itf_desc; + + usbtmc_state.itf_id = itf_desc->bInterfaceNumber; + + while (found_endpoints < itf_desc->bNumEndpoints) + { + if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE]) + { + tusb_desc_endpoint_t const *ep_desc = (tusb_desc_endpoint_t const *)p_desc; + switch(ep_desc->bmAttributes.xfer) { + case TUSB_XFER_BULK: + if (tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN) + { + usbtmc_state.ep_bulk_in = ep_desc->bEndpointAddress; + } else { + usbtmc_state.ep_bulk_out = ep_desc->bEndpointAddress; + } + + break; + case TUSB_XFER_INTERRUPT: + TU_ASSERT(tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN); + TU_ASSERT(usbtmc_state.ep_int_in == 0); + usbtmc_state.ep_int_in = ep_desc->bEndpointAddress; + break; + default: + TU_ASSERT(false); + } + TU_VERIFY( dcd_edpt_open(rhport, ep_desc)); + found_endpoints++; + } + (*p_length) = (uint8_t)((*p_length) + p_desc[DESC_OFFSET_LEN]); + p_desc = tu_desc_next(p_desc); + } + + // bulk endpoints are required, but interrupt IN is optional + TU_ASSERT(usbtmc_state.ep_bulk_in != 0); + TU_ASSERT(usbtmc_state.ep_bulk_out != 0); + if (itf_desc->bNumEndpoints == 2) { + TU_ASSERT(usbtmc_state.ep_int_in == 0); + } + else if (itf_desc->bNumEndpoints == 2) + { + TU_ASSERT(usbtmc_state.ep_int_in != 0); + } + TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_out, usbtmc_state.ep_bulk_out_buf, 64)); + + return true; +} +void usbtmcd_reset(uint8_t rhport) +{ + // FIXME: Do endpoints need to be closed here? + (void)rhport; +} +static bool handle_devMsgOut(uint8_t rhport, void *data, size_t len) +{ + (void)rhport; + bool shortPacket = (len < USBTMCD_MAX_PACKET_SIZE); + if(usbtmc_state.state == STATE_IDLE) + { + // must be a header, should have been confirmed before calling here. + usbtmc_msg_request_dev_dep_out *msg = (usbtmc_msg_request_dev_dep_out*)data; + usbtmc_state.transfer_size_remaining = msg->TransferSize; + TU_VERIFY(usbtmcd_app_msgBulkOut_start(msg)); + len -= sizeof(*msg); + data = (uint8_t*)data + sizeof(*msg); + } + // Packet is to be considered complete when we get enough data or at a short packet. + bool atEnd = false; + if(len >= usbtmc_state.transfer_size_remaining || shortPacket) + atEnd = true; + if(len > usbtmc_state.transfer_size_remaining) + len = usbtmc_state.transfer_size_remaining; + usbtmcd_app_msg_data(data, len, atEnd); + if(atEnd) + usbtmc_state.state = STATE_IDLE; + else + usbtmc_state.state = STATE_RCV; + return true; +} +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; + TU_VERIFY(usbtmcd_app_msgBulkIn_request(rhport, msg)); + return true; +} + +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); + if(ep_addr == usbtmc_state.ep_bulk_out) + { + switch(usbtmc_state.state) + { + case STATE_IDLE: + TU_VERIFY(xferred_bytes >= sizeof(usbtmc_msg_generic_t)); + usbtmc_msg_generic_t *msg = (usbtmc_msg_generic_t*)(usbtmc_state.ep_bulk_out_buf); + 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; + + switch(msg->header.MsgID) { + case USBTMC_MSGID_DEV_DEP_MSG_OUT: + TU_VERIFY(handle_devMsgOut(rhport, msg, xferred_bytes)); + TU_VERIFY(usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_out, usbtmc_state.ep_bulk_out_buf, 64)); + break; + case USBTMC_MSGID_DEV_DEP_MSG_IN: + TU_VERIFY(handle_devMsgIn(rhport, msg, xferred_bytes)); + break; + case USBTMC_MSGID_VENDOR_SPECIFIC_MSG_OUT: + case USBTMC_MSGID_VENDOR_SPECIFIC_IN: + case USBTMC_MSGID_USB488_TRIGGER: + default: + TU_VERIFY(false); + } + return true; + + case STATE_RCV: + TU_VERIFY(handle_devMsgOut(rhport, usbtmc_state.ep_bulk_out_buf, xferred_bytes)); + TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_out, usbtmc_state.ep_bulk_out_buf, 64)); + return true; + break; + + default: + TU_VERIFY(false); + } + } + else if(ep_addr == usbtmc_state.ep_bulk_in) + { + TU_ASSERT(usbtmc_state.state == STATE_TX_INITIATED); + if(usbtmc_state.transfer_size_remaining == 0) + { + usbtmc_state.state = STATE_IDLE; + TU_VERIFY(usbtmcd_app_msgBulkIn_complete(rhport)); + TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_out, usbtmc_state.ep_bulk_out_buf, 64)); + } + else if(usbtmc_state.transfer_size_remaining >= USBTMCD_MAX_PACKET_SIZE) + { + 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)); + } + else // short packet + { + uint 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) + { + usbtmc_state.ep_bulk_in_buf[packetLen] = 0; + packetLen++; + } + usbtmc_state.transfer_size_remaining = 0; + usbtmc_state.devInBuffer = NULL; + TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf,(uint16_t)packetLen)); + } + return true; + } + else if (ep_addr == usbtmc_state.ep_int_in) { + // Good? + return true; + } + return false; +} + +bool usbtmcd_control_request(uint8_t rhport, tusb_control_request_t const * request) { + +#if (USBTMC_CFG_ENABLE_488) + ushort bTag; +#endif + // We only handle class requests. + if(request->bmRequestType_bit.type != TUSB_REQ_TYPE_CLASS) + return false; + + 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: + case USBTMC_bREQUEST_CHECK_ABORT_BULK_IN_STATUS: + case USBTMC_bREQUEST_INITIATE_CLEAR: + case USBTMC_bREQUEST_CHECK_CLEAR_STATUS: + TU_VERIFY(false); + break; + + case USBTMC_bREQUEST_GET_CAPABILITIES: + TU_VERIFY(request->bmRequestType == 0xA1); + 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; + // USBTMC Optional Requests + case USBTMC_bREQUEST_INDICATOR_PULSE: // Optional + TU_VERIFY(false); + return false; + +#if (USBTMC_CFG_ENABLE_488) + // USB488 required requests + case USBTMC488_bREQUEST_READ_STATUS_BYTE: + + 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_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(false); + return false; +#endif + + default: + TU_VERIFY(false); + } + TU_VERIFY(false); +} + +bool usbtmcd_control_complete(uint8_t rhport, tusb_control_request_t const * request) +{ + (void)rhport; + //------------- Class Specific Request -------------// + TU_VERIFY (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); + + return true; +} + +#endif /* CFG_TUD_TSMC */ diff --git a/src/class/usbtmc/usbtmc_device.h b/src/class/usbtmc/usbtmc_device.h new file mode 100644 index 000000000..c03ca9461 --- /dev/null +++ b/src/class/usbtmc/usbtmc_device.h @@ -0,0 +1,130 @@ +/* + * usbtmc_device.h + * + * Created on: Sep 10, 2019 + * Author: nconrad + */ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 N 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 + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + + +#ifndef CLASS_USBTMC_USBTMC_DEVICE_H_ +#define CLASS_USBTMC_USBTMC_DEVICE_H_ + +#include "usbtmc.h" + +// Enable 488 mode by default +#if !defined(USBTMC_CFG_ENABLE_488) +#define USBTMC_CFG_ENABLE_488 (1) +#endif + +// USB spec says that full-speed must be 8,16,32, or 64. +// However, this driver implementation requires it to be >=32 +#define USBTMCD_MAX_PACKET_SIZE (64u) + +/*********************************************** + * Functions to be implemeted by the class implementation + */ + +#if (USBTMC_CFG_ENABLE_488) +extern usbtmc_response_capabilities_488_t const usbtmcd_app_capabilities; +#else +extern usbtmc_response_capabilities_t const usbtmcd_app_capabilities; +#endif + +bool usbtmcd_app_msgBulkOut_start(usbtmc_msg_request_dev_dep_out const * msgHeader); + +// transfer_complete does not imply that a message is complete. +bool usbtmcd_app_msg_data(void *data, size_t len, bool transfer_complete); + +bool usbtmcd_app_msgBulkIn_request(uint8_t rhport, usbtmc_msg_request_dev_dep_in const * request); + +bool usbtmcd_app_msgBulkIn_complete(uint8_t rhport); + +#if (USBTMC_CFG_ENABLE_488) +uint8_t usbtmcd_app_get_stb(uint8_t rhport, uint8_t *rspResult); + +//TU_ATTR_WEAK bool usbtmcd_app_go_to_local(uint8_t rhport); +#endif + +/******************************************* + * 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. + ******************************************/ + +bool usbtmcd_transmit_dev_msg_data( + uint8_t rhport, + usbtmc_msg_dev_dep_msg_in_header_t const * hdr, + const void *data); + + +/* "callbacks" from USB device core */ + +bool usbtmcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length); +void usbtmcd_reset(uint8_t rhport); +bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); +bool usbtmcd_control_request(uint8_t rhport, tusb_control_request_t const * request); +bool usbtmcd_control_complete(uint8_t rhport, tusb_control_request_t const * request); +void usbtmcd_init(void); + +/************************************************************ + * USBTMC Descriptor Templates + *************************************************************/ + +#define USBTMC_APP_CLASS TUSB_CLASS_APPLICATION_SPECIFIC +#define USBTMC_APP_SUBCLASS 0x03 + +#define USBTMC_PROTOCOL_STD 0x00 +#define USBTMC_PROTOCOL_USB488 0x01 + +// Interface number, number of endpoints, EP string index, USB_TMC_PROTOCOL*, bulk-out endpoint ID, +// bulk-in endpoint ID +#define USBTMC_IF_DESCRIPTOR(_itfnum, _bNumEndpoints, _stridx, _itfProtocol) \ +/* Interface */ \ + 0x09, TUSB_DESC_INTERFACE, _itfnum, 0x00, _bNumEndpoints, USBTMC_APP_CLASS, USBTMC_APP_SUBCLASS, _itfProtocol, _stridx + +#define USBTMC_IF_DESCRIPTOR_LEN 9u + +// bulk-out Size must be a multiple of 4 bytes +#define USBTMC_BULK_DESCRIPTORS(_epout, _epin) \ +/* Endpoint Out */ \ +7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(USBTMCD_MAX_PACKET_SIZE), 0u, \ +/* Endpoint In */ \ +7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(USBTMCD_MAX_PACKET_SIZE), 0u + +#define USBTMC_BULK_DESCRIPTORS_LEN (7u+7u) + +/* optional interrupt endpoint */ \ +// _int_pollingInterval : for LS/FS, expressed in frames (1ms each). 16 may be a good number? +#define USBTMC_INT_DESCRIPTOR(_ep_interrupt, _ep_interrupt_size, _int_pollingInterval ) \ +7, TUSB_DESC_ENDPOINT, _ep_interrupt, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_interrupt_size), 0x16 + +#define USBTMC_INT_DESCRIPTOR_LEN (7u) + + +#endif /* CLASS_USBTMC_USBTMC_DEVICE_H_ */ |
