Add usbtmc class driver.

This commit is contained in:
Nathan Conrad 2019-09-14 12:13:11 -04:00
parent ac8c343fef
commit 1cae96951f
12 changed files with 1420 additions and 0 deletions

View File

@ -0,0 +1,12 @@
include ../../../tools/top.mk
include ../../make.mk
INC += \
src \
$(TOP)/hw \
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
include ../../rules.mk

View File

@ -0,0 +1,113 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* 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.
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "bsp/board.h"
#include "tusb.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
/* Blink pattern
* - 250 ms : device not mounted
* - 1000 ms : device mounted
* - 2500 ms : device is suspended
*/
enum {
BLINK_NOT_MOUNTED = 250,
BLINK_MOUNTED = 1000,
BLINK_SUSPENDED = 2500,
};
static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
void led_blinking_task(void);
/*------------- MAIN -------------*/
int main(void)
{
board_init();
tusb_init();
while (1)
{
tud_task(); // tinyusb device task
led_blinking_task();
}
return 0;
}
//--------------------------------------------------------------------+
// Device callbacks
//--------------------------------------------------------------------+
// Invoked when device is mounted
void tud_mount_cb(void)
{
blink_interval_ms = BLINK_MOUNTED;
}
// Invoked when device is unmounted
void tud_umount_cb(void)
{
blink_interval_ms = BLINK_NOT_MOUNTED;
}
// Invoked when usb bus is suspended
// remote_wakeup_en : if host allow us to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
void tud_suspend_cb(bool remote_wakeup_en)
{
(void) remote_wakeup_en;
blink_interval_ms = BLINK_SUSPENDED;
}
// Invoked when usb bus is resumed
void tud_resume_cb(void)
{
blink_interval_ms = BLINK_MOUNTED;
}
//--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+
void led_blinking_task(void)
{
static uint32_t start_ms = 0;
static bool led_state = false;
// Blink every interval ms
if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
start_ms += blink_interval_ms;
board_led_write(led_state);
led_state = 1 - led_state; // toggle
}

View File

@ -0,0 +1,66 @@
/*
* tusb_config.h
*
* Created on: Sep 5, 2019
* Author: nconrad
*/
#ifndef TUSB_CONFIG_H_
#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------
// COMMON CONFIGURATION
//--------------------------------------------------------------------
// defined by compiler flags for flexibility
#ifndef CFG_TUSB_MCU
#error CFG_TUSB_MCU must be defined
#endif
#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED)
#else
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE
#endif
#define CFG_TUSB_OS OPT_OS_NONE
// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
// #define CFG_TUSB_DEBUG 0
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
* e.g
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
*/
#ifndef CFG_TUSB_MEM_SECTION
#define CFG_TUSB_MEM_SECTION
#endif
#ifndef CFG_TUSB_MEM_ALIGN
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
#endif
//--------------------------------------------------------------------
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#define CFG_TUD_ENDOINT0_SIZE 64
//------------- CLASS -------------//
#define CFG_TUD_USBTMC 1
#define CFG_TUD_USBTMC_ENABLE_INT_EP
//#define USBTMC_CFG_ENABLE_488 0
#ifdef __cplusplus
}
#endif
#endif /* TUSB_CONFIG_H_ */

View File

@ -0,0 +1,254 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* 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.
*
*/
#include "tusb.h"
#include "class/usbtmc/usbtmc.h"
#include "class/usbtmc/usbtmc_device.h"
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
*
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
_PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
#if CFG_TUD_CDC
// Use Interface Association Descriptor (IAD) for CDC
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
#else
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
#endif
.bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE,
.idVendor = 0xCafe,
.idProduct = USB_PID,
.bcdDevice = 0x0100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
};
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
uint8_t const * tud_descriptor_device_cb(void)
{
return (uint8_t const *) &desc_device;
}
//--------------------------------------------------------------------+
// HID Report Descriptor
//--------------------------------------------------------------------+
#if CFG_TUD_HID
uint8_t const desc_hid_report[] =
{
TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(REPORT_ID_KEYBOARD), ),
TUD_HID_REPORT_DESC_MOUSE ( HID_REPORT_ID(REPORT_ID_MOUSE), )
};
// Invoked when received GET HID REPORT DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const * tud_hid_descriptor_report_cb(void)
{
return desc_hid_report;
}
#endif
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
#if defined(CFG_TUD_USBTMC)
# define USBTMC_DESC_MAIN(_itfnum,_bNumEndpoints) \
USBTMC_IF_DESCRIPTOR(_itfnum, _bNumEndpoints, /*_stridx = */ 7u, USBTMC_PROTOCOL_USB488), \
USBTMC_BULK_DESCRIPTORS(/* OUT = */0x03, /* IN = */ 0x83)
#if defined(CFG_TUD_USBTMC_ENABLE_INT_EP)
# define USBTMC_DESC(_itfnum) \
USBTMC_DESC_MAIN(_itfnum, /* _epCount = */ 3), \
USBTMC_INT_DESCRIPTOR(/* INT ep # */ 0x84, /* epMaxSize = */ 64, /* bInterval = */16u )
# define USBTMC_DESC_LEN (USBTMC_IF_DESCRIPTOR_LEN + USBTMC_BULK_DESCRIPTORS_LEN + USBTMC_INT_DESCRIPTOR_LEN)
#else
# define USBTMC_DESC(_itfnum) \
USBTMC_DESC_MAIN(_itfnum, /* _epCount = */ 2u)
# define USBTMC_DESC_LEN (USBTMC_IF_DESCRIPTOR_LEN + USBTMC_BULK_DESCRIPTORS_LEN)
#endif /* CFG_TUD_USBTMC_ENABLE_INT_EP */
#else
# define USBTMC_DESC_LEN (0)
#endif /* CFG_TUD_USBTMC */
enum
{
#if CFG_TUD_CDC
ITF_NUM_CDC = 0,
ITF_NUM_CDC_DATA,
#endif
#if CFG_TUD_MSC
ITF_NUM_MSC,
#endif
#if CFG_TUD_HID
ITF_NUM_HID,
#endif
#if CFG_TUD_USBTMC
ITF_NUM_USBTMC,
#endif
ITF_NUM_TOTAL
};
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_CDC*TUD_CDC_DESC_LEN + CFG_TUD_MSC*TUD_MSC_DESC_LEN + \
CFG_TUD_HID*TUD_HID_DESC_LEN + (CFG_TUD_USBTMC)*USBTMC_DESC_LEN)
#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
// LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
// 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ...
// Note: since CDC EP ( 1 & 2), HID (4) are spot-on, thus we only need to force
// endpoint number for MSC to 5
#define EPNUM_MSC 0x05
#else
#define EPNUM_MSC 0x03
#endif
uint8_t const desc_configuration[] =
{
// Interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
#if CFG_TUD_CDC
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 1, 0x81, 8, 0x02, 0x82, 64),
#endif
#if CFG_TUD_USBTMC
USBTMC_DESC(ITF_NUM_USBTMC),
#endif
#if CFG_TUD_MSC
// Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC, 0x80 | EPNUM_MSC, (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 512 : 64),
#endif
#if CFG_TUD_HID
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
TUD_HID_DESCRIPTOR(ITF_NUM_HID, 6, HID_PROTOCOL_NONE, sizeof(desc_hid_report), 0x84, 16, 10)
#endif
};
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
{
(void) index; // for multiple configurations
return desc_configuration;
}
//--------------------------------------------------------------------+
// String Descriptors
//--------------------------------------------------------------------+
// array of pointer to string descriptors
char const* string_desc_arr [] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product
"123456", // 3: Serials, should use chip ID
"TinyUSB CDC", // 4: CDC Interface
"TinyUSB MSC", // 5: MSC Interface
"TinyUSB HID", // 6: HID
"TinyUSB USBTMC", // 7: USBTMC
};
static uint16_t _desc_str[32];
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
uint16_t const* tud_descriptor_string_cb(uint8_t index)
{
uint8_t chr_count;
if ( index == 0)
{
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
}else
{
// Convert ASCII string into UTF-16
if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL;
const char* str = string_desc_arr[index];
// Cap at max char
chr_count = strlen(str);
if ( chr_count > 31 ) {
chr_count = 31;
}
for(uint8_t i=0; i<chr_count; i++)
{
_desc_str[1+i] = str[i];
}
}
// first byte is length (including header), second byte is string type
_desc_str[0] = (TUSB_DESC_STRING << 8 ) | (2*chr_count + 2);
return _desc_str;
}

View File

@ -0,0 +1,133 @@
/*
* 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.
*
*/
#include <strings.h>
#include "class/usbtmc/usbtmc_device.h"
#if (USBTMC_CFG_ENABLE_488)
usbtmc_response_capabilities_488_t const
#else
usbtmc_response_capabilities_t const
#endif
usbtmcd_app_capabilities =
{
.USBTMC_status = USBTMC_STATUS_SUCCESS,
.bcdUSBTMC = USBTMC_VERSION,
.bmIntfcCapabilities =
{
.listenOnly = 0,
.talkOnly = 0,
.supportsIndicatorPulse = 0
},
.bmDevCapabilities = {
.canEndBulkInOnTermChar = 0
},
#if (USBTMC_CFG_ENABLE_488)
.bcdUSB488 = USBTMC_488_VERSION,
.bmIntfcCapabilities488 =
{
.supportsTrigger = 0,
.supportsREN_GTL_LLO = 0,
.is488_2 = 1
},
.bmDevCapabilities488 =
{
.SCPI = 1,
.SR1 = 0,
.RL1 = 0,
.DT1 =0,
}
#endif
};
static const char idn[] = "TinyUSB,ModelNumber,SerialNumber,FirmwareVer";
static uint8_t status;
static bool queryReceived = false;
bool usbtmcd_app_msgBulkOut_start(usbtmc_msg_request_dev_dep_out const * msgHeader)
{
(void)msgHeader;
return true;
}
bool usbtmcd_app_msg_data(void *data, size_t len, bool transfer_complete)
{
(void)transfer_complete;
if(transfer_complete && (len >=4) && !strncasecmp("*idn?",data,4)) {
queryReceived = true;
}
return true;
}
bool usbtmcd_app_msgBulkIn_complete(uint8_t rhport)
{
(void)rhport;
return true;
}
static uint8_t noQueryMsg[] = "ERR: No query";
bool usbtmcd_app_msgBulkIn_request(uint8_t rhport, usbtmc_msg_request_dev_dep_in const * request)
{
usbtmc_msg_dev_dep_msg_in_header_t hdr = {
.header =
{
.MsgID = request->header.MsgID,
.bTag = request->header.bTag,
.bTagInverse = request->header.bTagInverse
},
.TransferSize = sizeof(idn)-1,
.bmTransferAttributes =
{
.EOM = 1,
.UsingTermChar = 0
}
};
if(queryReceived)
{
usbtmcd_transmit_dev_msg_data(rhport, &hdr, idn);
}
else
{
hdr.TransferSize = sizeof(noQueryMsg)-1;
usbtmcd_transmit_dev_msg_data(rhport, &hdr, noQueryMsg);
}
queryReceived = false;
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 *rspResult)
{
(void)rhport;
*rspResult = USBTMC_STATUS_SUCCESS;
// Increment status so that we see different results on each read...
status++;
return status;
}

View File

@ -15,6 +15,7 @@ SRC_C += \
src/class/cdc/cdc_device.c \
src/class/hid/hid_device.c \
src/class/midi/midi_device.c \
src/class/usbtmc/usbtmc_device.c \
src/class/vendor/vendor_device.c \
src/portable/$(VENDOR)/$(CHIP_FAMILY)/dcd_$(CHIP_FAMILY).c

267
src/class/usbtmc/usbtmc.h Normal file
View File

@ -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

View File

@ -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 */

View File

@ -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_ */

View File

@ -145,6 +145,22 @@ static usbd_class_driver_t const usbd_class_drivers[] =
.sof = NULL
},
#endif
#if CFG_TUD_USBTMC
// Presently USBTMC is the only defined class with the APP_SPECIFIC class code.
// We maybe need to add subclass codes here, or a callback to ask if a driver can
// handle a particular interface.
{
.class_code = TUSB_CLASS_APPLICATION_SPECIFIC,
.init = usbtmcd_init,
.reset = usbtmcd_reset,
.open = usbtmcd_open,
.control_request = usbtmcd_control_request,
.control_complete = usbtmcd_control_complete,
.xfer_cb = usbtmcd_xfer_cb,
.sof = NULL
},
#endif
};
enum { USBD_CLASS_DRIVER_COUNT = TU_ARRAY_SIZE(usbd_class_drivers) };

View File

@ -83,6 +83,10 @@
#if CFG_TUD_VENDOR
#include "class/vendor/vendor_device.h"
#endif
#if CFG_TUD_USBTMC
#include "class/usbtmc/usbtmc_device.h"
#endif
#endif

View File

@ -181,6 +181,10 @@
#define CFG_TUD_VENDOR 0
#endif
#ifndef CFG_TUD_USBTMC
#define CFG_TUD_USBTMC 0
#endif
//--------------------------------------------------------------------
// HOST OPTIONS