Merge branch 'master' into develop

This commit is contained in:
hathach 2019-09-30 14:45:34 +07:00
commit d27291f288
35 changed files with 2436 additions and 21 deletions

36
docs/concurrency.md Normal file
View File

@ -0,0 +1,36 @@
# Concurrency
The TinyUSB library is designed to operate on single-core MCUs with multi-threaded applications in mind. Interaction with interrupts is especially important to pay attention to.
It is compatible with optionally using a RTOS.
## General
When writing code, keep in mind that the OS (if using a RTOS) may swap out your code at any time. Also, your code can be preempted by an interrupt at any time.
## Application Code
The USB core does not execute application callbacks while in an interrupt context. Calls to application code are from within the USB core task context. Note that the application core will call class drivers from within their own task.
## Class Drivers
Class driver code should never be called from an interrupt context by the USB core, though the application is allowed to call class driver functions from interrupts. USB core functions may be called simultaneously by multiple tasks. Use care that proper locking is used to guard the USBD core functions from this case.
Class drivers are allowed to call `usbd_*` functions, but not `dcd_*` functions.
## USB Core
All functions that may be called from an (USB core) interrupt context have a `bool in_isr` parameter to remind the implementer that special care must be taken.
Interrupt handlers must not directly call class driver code, they must pass a message to the USB core's task.
`usbd_*` functions may be called from interrupts without any notice. They may also be called simultaneously by multiple tasks.
## Device Drivers
Much of the processing of the USB stack is done in an interrupt context, and care must be taken in order to ensure variables are handled in the appropriate ways by the compiler and optimizer.
In particular:
- Ensure that all memory-mapped registers (including packet memory) are marked as volatile. GCC's optimizer will even combine memory access (like two 16-bit to be a 32-bit) if you don't mark the pointers as volatile. On some architectures, this can use macros like `_I`, `_O`, or `_IO'.
- All defined global variables are marked as `static`.

View File

@ -64,7 +64,7 @@
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#define CFG_TUD_ENDOINT0_SIZE 64
#define CFG_TUD_ENDPOINT0_SIZE 64
//------------- CLASS -------------//
#define CFG_TUD_CDC 0

View File

@ -68,8 +68,9 @@
//--------------------------------------------------------------------
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#define CFG_TUD_ENDOINT0_SIZE 64
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
//------------- CLASS -------------//
#define CFG_TUD_CDC 1

View File

@ -50,7 +50,7 @@ tusb_desc_device_t const desc_device =
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0xCafe,
.idProduct = USB_PID,

View File

@ -1,4 +1,4 @@
/*
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@ -69,7 +69,9 @@
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#define CFG_TUD_ENDOINT0_SIZE 64
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
//------------- CLASS -------------//
#define CFG_TUD_CDC 1

View File

@ -57,7 +57,7 @@ tusb_desc_device_t const desc_device =
.bDeviceProtocol = 0x00,
#endif
.bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0xCafe,
.idProduct = USB_PID,

View File

@ -69,7 +69,9 @@
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#define CFG_TUD_ENDOINT0_SIZE 64
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
//------------- CLASS -------------//
#define CFG_TUD_HID 1

View File

@ -47,7 +47,7 @@ tusb_desc_device_t const desc_device =
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0xCafe,
.idProduct = USB_PID,

View File

@ -69,7 +69,9 @@
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#define CFG_TUD_ENDOINT0_SIZE 64
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
//------------- CLASS -------------//
#define CFG_TUD_CDC 0

View File

@ -46,7 +46,7 @@ tusb_desc_device_t const desc_device =
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0xCafe,
.idProduct = USB_PID,

View File

@ -69,7 +69,9 @@
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#define CFG_TUD_ENDOINT0_SIZE 64
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
//------------- CLASS -------------//
#define CFG_TUD_CDC 0

View File

@ -46,7 +46,7 @@ tusb_desc_device_t const desc_device =
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0xCafe,
.idProduct = USB_PID,

View File

@ -69,7 +69,9 @@
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#define CFG_TUD_ENDOINT0_SIZE 64
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
//------------- CLASS -------------//
#define CFG_TUD_CDC 0

View File

@ -46,7 +46,7 @@ tusb_desc_device_t const desc_device =
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0xCafe,
.idProduct = USB_PID,

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,143 @@
/*
* 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"
#include "usbtmc_app.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
/* Blink pattern
* - 250 ms : device not mounted
* - 0 ms : device mounted
* - 2500 ms : device is suspended
*/
enum {
BLINK_NOT_MOUNTED = 250,
BLINK_MOUNTED = 0,
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();
usbtmc_app_task_iter();
}
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 + Indicator pulse
//--------------------------------------------------------------------+
volatile uint8_t doPulse = false;
// called from USB context
void led_indicator_pulse(void) {
doPulse = true;
}
void led_blinking_task(void)
{
static uint32_t start_ms = 0;
static bool led_state = false;
if(blink_interval_ms == BLINK_MOUNTED) // Mounted
{
if(doPulse)
{
led_state = true;
board_led_write(true);
start_ms = board_millis();
doPulse = false;
}
else if (led_state == true)
{
if ( board_millis() - start_ms < 750) //Spec says blink must be between 500 and 1000 ms.
{
return; // not enough time
}
led_state = false;
board_led_write(false);
}
}
else
{
// 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,5 @@
#ifndef MAIN_H
#define MAIN_H
void led_indicator_pulse(void);
#endif

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_ENDPOINT0_SIZE 64
//------------- CLASS -------------//
#define CFG_TUD_USBTMC 1
#define CFG_TUD_USBTMC_ENABLE_INT_EP 1
#define CFG_TUD_USBTMC_ENABLE_488 1
#ifdef __cplusplus
}
#endif
#endif /* TUSB_CONFIG_H_ */

View File

@ -0,0 +1,252 @@
/*
* 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_ENDPOINT0_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 TUD_USBTMC_DESC_MAIN(_itfnum,_bNumEndpoints) \
TUD_USBTMC_IF_DESCRIPTOR(_itfnum, _bNumEndpoints, /*_stridx = */ 4u, TUD_USBTMC_PROTOCOL_USB488), \
TUD_USBTMC_BULK_DESCRIPTORS(/* OUT = */0x01, /* IN = */ 0x81, /* packet size = */USBTMCD_MAX_PACKET_SIZE)
#if defined(CFG_TUD_USBTMC_ENABLE_INT_EP)
// Interrupt endpoint should be 2 bytes on a FS USB link
# define TUD_USBTMC_DESC(_itfnum) \
TUD_USBTMC_DESC_MAIN(_itfnum, /* _epCount = */ 3), \
TUD_USBTMC_INT_DESCRIPTOR(/* INT ep # */ 0x82, /* epMaxSize = */ 2, /* bInterval = */16u )
# define USBTMC_DESC_LEN (TUD_USBTMC_IF_DESCRIPTOR_LEN + TUD_USBTMC_BULK_DESCRIPTORS_LEN + TUD_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
TUD_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 USBTMC", // 4: 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)
{
size_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] = (uint16_t)((((uint16_t)TUSB_DESC_STRING) << 8 ) | (2u*chr_count + 2u));
return _desc_str;
}

View File

@ -0,0 +1,329 @@
/*
* The MIT License (MIT)
*
* 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
* 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 <stdlib.h> /* atoi */
#include "tusb.h"
#include "bsp/board.h"
#include "main.h"
#if (CFG_TUD_USBTMC_ENABLE_488)
static usbtmc_response_capabilities_488_t const
#else
static usbtmc_response_capabilities_t const
#endif
tud_usbtmc_app_capabilities =
{
.USBTMC_status = USBTMC_STATUS_SUCCESS,
.bcdUSBTMC = USBTMC_VERSION,
.bmIntfcCapabilities =
{
.listenOnly = 0,
.talkOnly = 0,
.supportsIndicatorPulse = 1
},
.bmDevCapabilities = {
.canEndBulkInOnTermChar = 0
},
#if (CFG_TUD_USBTMC_ENABLE_488)
.bcdUSB488 = USBTMC_488_VERSION,
.bmIntfcCapabilities488 =
{
.supportsTrigger = 1,
.supportsREN_GTL_LLO = 0,
.is488_2 = 1
},
.bmDevCapabilities488 =
{
.SCPI = 1,
.SR1 = 0,
.RL1 = 0,
.DT1 =0,
}
#endif
};
#define IEEE4882_STB_QUESTIONABLE (0x08u)
#define IEEE4882_STB_MAV (0x10u)
#define IEEE4882_STB_SER (0x20u)
#define IEEE4882_STB_SRQ (0x40u)
static const char idn[] = "TinyUSB,ModelNumber,SerialNumber,FirmwareVer123456\r\n";
//static const char idn[] = "TinyUSB,ModelNumber,SerialNumber,FirmwareVer and a bunch of other text to make it longer than a packet, perhaps? lets make it three transfers...\n";
static volatile uint8_t status;
// 0=not query, 1=queried, 2=delay,set(MAV), 3=delay 4=ready?
// (to simulate delay)
static volatile uint16_t queryState = 0;
static volatile uint32_t queryDelayStart;
static volatile uint32_t bulkInStarted;
static volatile uint32_t idnQuery;
static uint32_t resp_delay = 125u; // Adjustable delay, to allow for better testing
static size_t buffer_len;
static size_t buffer_tx_ix; // for transmitting using multiple transfers
static uint8_t buffer[225]; // A few packets long should be enough.
static usbtmc_msg_dev_dep_msg_in_header_t rspMsg = {
.bmTransferAttributes =
{
.EOM = 1,
.UsingTermChar = 0
}
};
void tud_usbtmc_open_cb(uint8_t interface_id)
{
(void)interface_id;
tud_usbtmc_start_bus_read();
}
#if (CFG_TUD_USBTMC_ENABLE_488)
usbtmc_response_capabilities_488_t const *
#else
usbtmc_response_capabilities_t const *
#endif
tud_usbtmc_get_capabilities_cb()
{
return &tud_usbtmc_app_capabilities;
}
bool tud_usbtmc_msg_trigger_cb(usbtmc_msg_generic_t* msg) {
(void)msg;
// Let trigger set the SRQ
status |= IEEE4882_STB_SRQ;
return true;
}
bool tud_usbtmc_msgBulkOut_start_cb(usbtmc_msg_request_dev_dep_out const * msgHeader)
{
(void)msgHeader;
buffer_len = 0;
if(msgHeader->TransferSize > sizeof(buffer))
{
return false;
}
return true;
}
bool tud_usbtmc_msg_data_cb(void *data, size_t len, bool transfer_complete)
{
// If transfer isn't finished, we just ignore it (for now)
if(len + buffer_len < sizeof(buffer))
{
memcpy(&(buffer[buffer_len]), data, len);
buffer_len += len;
}
else
{
return false; // buffer overflow!
}
queryState = transfer_complete;
idnQuery = 0;
if(transfer_complete && (len >=4) && !strncasecmp("*idn?",data,4))
{
idnQuery = 1;
}
if(transfer_complete && !strncasecmp("delay ",data,5))
{
queryState = 0;
int d = atoi((char*)data + 5);
if(d > 10000)
d = 10000;
if(d<0)
d=0;
resp_delay = (uint32_t)d;
}
tud_usbtmc_start_bus_read();
return true;
}
bool tud_usbtmc_msgBulkIn_complete_cb()
{
if((buffer_tx_ix == buffer_len) || idnQuery) // done
{
status &= (uint8_t)~(IEEE4882_STB_MAV); // clear MAV
queryState = 0;
bulkInStarted = 0;
buffer_tx_ix = 0;
}
tud_usbtmc_start_bus_read();
return true;
}
static unsigned int msgReqLen;
bool tud_usbtmc_msgBulkIn_request_cb(usbtmc_msg_request_dev_dep_in const * request)
{
rspMsg.header.MsgID = request->header.MsgID,
rspMsg.header.bTag = request->header.bTag,
rspMsg.header.bTagInverse = request->header.bTagInverse;
msgReqLen = request->TransferSize;
#ifdef xDEBUG
uart_tx_str_sync("MSG_IN_DATA: Requested!\r\n");
#endif
if(queryState == 0 || (buffer_tx_ix == 0))
{
TU_ASSERT(bulkInStarted == 0);
bulkInStarted = 1;
// > If a USBTMC interface receives a Bulk-IN request prior to receiving a USBTMC command message
// that expects a response, the device must NAK the request (*not stall*)
}
else
{
size_t txlen = tu_min32(buffer_len-buffer_tx_ix,msgReqLen);
tud_usbtmc_transmit_dev_msg_data(&buffer[buffer_tx_ix], txlen,
(buffer_tx_ix+txlen) == buffer_len, false);
buffer_tx_ix += txlen;
}
// Always return true indicating not to stall the EP.
return true;
}
void usbtmc_app_task_iter(void) {
switch(queryState) {
case 0:
break;
case 1:
queryDelayStart = board_millis();
queryState = 2;
break;
case 2:
if( (board_millis() - queryDelayStart) > resp_delay) {
queryDelayStart = board_millis();
queryState=3;
status |= 0x10u; // MAV
status |= 0x40u; // SRQ
}
break;
case 3:
if( (board_millis() - queryDelayStart) > resp_delay) {
queryState = 4;
}
break;
case 4: // time to transmit;
if(bulkInStarted && (buffer_tx_ix == 0)) {
if(idnQuery)
{
tud_usbtmc_transmit_dev_msg_data(idn, tu_min32(sizeof(idn)-1,msgReqLen),true,false);
queryState = 0;
bulkInStarted = 0;
}
else
{
buffer_tx_ix = tu_min32(buffer_len,msgReqLen);
tud_usbtmc_transmit_dev_msg_data(buffer, buffer_tx_ix, buffer_tx_ix == buffer_len, false);
}
// MAV is cleared in the transfer complete callback.
}
break;
default:
TU_ASSERT(false,);
return;
}
}
bool tud_usbtmc_initiate_clear_cb(uint8_t *tmcResult)
{
*tmcResult = USBTMC_STATUS_SUCCESS;
queryState = 0;
bulkInStarted = false;
status = 0;
return true;
}
bool tud_usbtmc_check_clear_cb(usbtmc_get_clear_status_rsp_t *rsp)
{
queryState = 0;
bulkInStarted = false;
status = 0;
buffer_tx_ix = 0u;
buffer_len = 0u;
rsp->USBTMC_status = USBTMC_STATUS_SUCCESS;
rsp->bmClear.BulkInFifoBytes = 0u;
return true;
}
bool tud_usbtmc_initiate_abort_bulk_in_cb(uint8_t *tmcResult)
{
bulkInStarted = 0;
*tmcResult = USBTMC_STATUS_SUCCESS;
return true;
}
bool tud_usbtmc_check_abort_bulk_in_cb(usbtmc_check_abort_bulk_rsp_t *rsp)
{
(void)rsp;
tud_usbtmc_start_bus_read();
return true;
}
bool tud_usbtmc_initiate_abort_bulk_out_cb(uint8_t *tmcResult)
{
*tmcResult = USBTMC_STATUS_SUCCESS;
return true;
}
bool tud_usbtmc_check_abort_bulk_out_cb(usbtmc_check_abort_bulk_rsp_t *rsp)
{
(void)rsp;
tud_usbtmc_start_bus_read();
return true;
}
void tud_usbtmc_bulkIn_clearFeature_cb(void)
{
}
void tud_usmtmc_bulkOut_clearFeature_cb(void)
{
tud_usbtmc_start_bus_read();
}
// Return status byte, but put the transfer result status code in the rspResult argument.
uint8_t tud_usbtmc_get_stb_cb(uint8_t *tmcResult)
{
uint8_t old_status = status;
status = (uint8_t)(status & ~(IEEE4882_STB_SRQ)); // clear SRQ
*tmcResult = USBTMC_STATUS_SUCCESS;
// Increment status so that we see different results on each read...
return old_status;
}
bool tud_usbtmc_indicator_pulse_cb(tusb_control_request_t const * msg, uint8_t *tmcResult)
{
(void)msg;
led_indicator_pulse();
*tmcResult = USBTMC_STATUS_SUCCESS;
return true;
}

View File

@ -0,0 +1,7 @@
#ifndef USBTMC_APP_H
#define USBTMC_APP_H
void usbtmc_app_task_iter(void);
#endif

View File

@ -0,0 +1,193 @@
import visa
import time
import sys
def test_idn():
idn = inst.query("*idn?");
assert (idn == "TinyUSB,ModelNumber,SerialNumber,FirmwareVer123456\r\n")
assert (inst.is_4882_compliant)
def test_echo(m,n):
longstr = "0123456789abcdefghijklmnopqrstuvwxyz" * 50
t0 = time.monotonic()
#Next try echo from 1 to 175 characters (200 is max buffer size on DUT)
for i in range(m,n):
#print(i)
x = longstr[0:i]
xt = x + inst.write_termination
y = inst.query(x)
#print(x)
#print (":".join("{:02x}".format(ord(c)) for c in xt))
#print (":".join("{:02x}".format(ord(c)) for c in y))
assert(xt == y), f"failed i={i}"
#inst.read_stb();# Just to make USB logging easier by sending a control query (bad thing is that this made things slow)
t = time.monotonic() - t0
print(f" elapsed: {t:0.3} sec")
def test_trig():
# clear SRQ
inst.read_stb()
assert (inst.read_stb() == 0)
inst.assert_trigger()
time.sleep(0.3) # SRQ may have some delay
assert (inst.read_stb() & 0x40), "SRQ not set after 0.3 seconds"
assert (inst.read_stb() == 0)
def test_mav():
inst.write("delay 50")
inst.read_stb() # clear STB
assert (inst.read_stb() == 0)
inst.write("123")
time.sleep(0.3)
assert (inst.read_stb() & 0x10), "MAV not set after 0.5 seconds"
rsp = inst.read()
assert(rsp == "123\r\n")
def test_srq():
assert (inst.read_stb() == 0)
inst.write("123")
#inst.enable_event(visa.constants.VI_EVENT_SERVICE_REQ, visa.constants.VI_QUEUE)
#waitrsp = inst.wait_on_event(visa.constants.VI_EVENT_SERVICE_REQ, 5000)
#inst.discard_events(visa.constants.VI_EVENT_SERVICE_REQ, visa.constants.VI_QUEUE)
#inst.wait_for_srq()
time.sleep(0.3)
stb = inst.read_stb()
msg = "SRQ not set after 0.5 seconds, was {:02x}".format(stb)
assert (stb == 0x50),msg
assert (inst.read_stb() == 0x10), "SRQ set at second read!"
rsp = inst.read()
assert(rsp == "123\r\n")
def test_read_timeout():
inst.timeout = 500
# First read with no MAV
inst.read_stb()
assert (inst.read_stb() == 0)
inst.write("delay 500")
t0 = time.monotonic()
try:
rsp = inst.read()
assert(false), "Read should have resulted in timeout"
except visa.VisaIOError:
print(" Got expected exception")
t = time.monotonic() - t0
assert ((t*1000.0) > (inst.timeout - 300))
assert ((t*1000.0) < (inst.timeout + 300))
print(f"Delay was {t:0.3}")
# Response is still in queue, so send a clear (to be more helpful to the next test)
inst.clear()
time.sleep(0.3)
assert(0 == (inst.read_stb() & 0x10)), "MAV not reset after clear"
def test_abort_in():
inst.timeout = 200
# First read with no MAV
inst.read_stb()
assert (inst.read_stb() == 0)
inst.write("delay 500")
inst.write("xxx")
t0 = time.monotonic()
try:
rsp = inst.read()
assert(false), "Read should have resulted in timeout"
except visa.VisaIOError:
print(" Got expected exception")
t = time.monotonic() - t0
assert ((t*1000.0) > (inst.timeout - 300))
assert ((t*1000.0) < (inst.timeout + 300))
print(f" Delay was {t:0.3}")
# Response is still in queue, so send a clear (to be more helpful to the next test)
inst.timeout = 800
y = inst.read()
assert(y == "xxx\r\n")
def test_indicate():
# perform indicator pulse
usb_iface = inst.get_visa_attribute(visa.constants.VI_ATTR_USB_INTFC_NUM)
retv = inst.control_in(request_type_bitmap_field=0xA1, request_id=64, request_value=0x0000, index=usb_iface, length=0x0001)
assert((retv[1] == visa.constants.StatusCode(0)) and (retv[0] == b'\x01')), f"indicator pulse failed: retv={retv}"
def test_multi_read():
old_chunk_size = inst.chunk_size
longstr = "0123456789abcdefghijklmnopqrstuvwxyz" * 10
timeout = 10
x = longstr[0:174]
inst.chunk_size = 50 # Seems chunk size only applies to read but not write
inst.write(x)
# I'm not sure how to request just the remaining bit using a max count... so just read it all.
y = inst.read()
assert (x + "\r\n" == y)
#inst.chunk_size = old_chunk_size
rm = visa.ResourceManager("/c/Windows/system32/visa64.dll")
reslist = rm.list_resources("USB?::?*::INSTR")
print(reslist)
if (len(reslist) == 0):
sys.exit()
inst = rm.open_resource(reslist[0]);
inst.timeout = 3000
inst.clear()
print("+ IDN")
test_idn()
print("+test abort in")
test_abort_in()
inst.timeout = 2000
print("+ multi read")
test_multi_read()
print("+ echo delay=0")
inst.write("delay 0")
test_echo(1,175)
print("+ echo delay=2")
inst.write("delay 2")
test_echo(1,175)
print("+ echo delay=150")
inst.write("delay 150")
test_echo(53,76)
test_echo(165,170)
print("+ Read timeout (no MAV)")
test_read_timeout()
print("+ MAV")
test_mav()
print("+ SRQ")
test_srq()
print("+ indicate")
test_indicate()
print("+ TRIG")
test_trig()
# Untested:
# abort bulk out
# LLO, GTL, etc
# Throughput rate?
# Transmitting a message using multiple transfers
inst.close()
print("Test complete")

View File

@ -69,7 +69,9 @@
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#define CFG_TUD_ENDOINT0_SIZE 64
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
//------------- CLASS -------------//
#define CFG_TUD_CDC 1

View File

@ -50,7 +50,7 @@ tusb_desc_device_t const desc_device =
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0xCafe,
.idProduct = USB_PID,

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

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

@ -0,0 +1,316 @@
/*
* 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 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
{
usbtmc_msg_header_t header ; ///< Header
uint32_t TransferSize ; ///< Transfer size; LSB first
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 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
{
usbtmc_msg_header_t header ; ///< Header
uint32_t TransferSize ; ///< Transfer size; LSB first
uint8_t _reserved[4];
} usbtmc_msg_request_vendor_specific_out;
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
/*
typedef struct TU_ATTR_PACKED {
struct {
unsigned int Recipient : 5 ; ///< EOM set on last byte
unsigned int Type : 2 ; ///< EOM set on last byte
unsigned int 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
/****** USBTMC 488 *************/
USB488_bREQUEST_READ_STATUS_BYTE = 128u,
USB488_bREQUEST_REN_CONTROL = 160u,
USB488_bREQUEST_GO_TO_LOCAL = 161u,
USB488_bREQUEST_LOCAL_LOCKOUT = 162u,
} usmtmc_request_type_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 TU_ATTR_PACKED
{
unsigned int listenOnly :1;
unsigned int talkOnly :1;
unsigned int supportsIndicatorPulse :1;
} bmIntfcCapabilities;
struct TU_ATTR_PACKED
{
unsigned int 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;
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");
// Used for both check_abort_bulk_in_status and check_abort_bulk_out_status
typedef struct TU_ATTR_PACKED
{
uint8_t USBTMC_status;
struct TU_ATTR_PACKED
{
unsigned int BulkInFifoBytes : 1; ///< Has queued data or a short packet that is queued
} bmAbortBulkIn;
uint8_t _reserved[2]; ///< Must be zero
uint32_t NBYTES_RXD_TXD;
} usbtmc_check_abort_bulk_rsp_t;
TU_VERIFY_STATIC(sizeof(usbtmc_check_abort_bulk_rsp_t) == 8u, "struct wrong length");
typedef struct TU_ATTR_PACKED
{
uint8_t USBTMC_status; ///< usbtmc_status_enum
uint8_t _reserved;
uint16_t bcdUSBTMC; ///< USBTMC_VERSION
struct TU_ATTR_PACKED
{
unsigned int listenOnly :1;
unsigned int talkOnly :1;
unsigned int supportsIndicatorPulse :1;
} bmIntfcCapabilities;
struct TU_ATTR_PACKED
{
unsigned int canEndBulkInOnTermChar :1;
} bmDevCapabilities;
uint8_t _reserved2[6];
uint16_t bcdUSB488;
struct TU_ATTR_PACKED
{
unsigned int is488_2 :1;
unsigned int supportsREN_GTL_LLO :1;
unsigned int supportsTrigger :1;
} bmIntfcCapabilities488;
struct TU_ATTR_PACKED
{
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;
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_PACKED
{
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

View File

@ -0,0 +1,863 @@
/*
* usbtmc.c
*
* Created on: Sep 9, 2019
* Author: nconrad
*/
/*
* The MIT License (MIT)
*
* 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
* 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.
*/
/*
* This library is not fully reentrant, though it is reentrant from the view
* of either the application layer or the USB stack. Due to its locking,
* it is not safe to call its functions from interrupts.
*
* The one exception is that its functions may not be called from the application
* until the USB stack is initialized. This should not be a problem since the
* device shouldn't be sending messages until it receives a request from the
* host.
*/
/*
* In the case of single-CPU "no OS", this task is never preempted other than by
* interrupts, and the USBTMC code isn't called by interrupts, so all is OK. For "no OS",
* the mutex structure's main effect is to disable the USB interrupts.
* With 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.
*/
//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 not 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.
// Clear message available status byte at the correct time? (488 4.3.1.3)
#include "tusb_option.h"
#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 "osal/osal.h"
// FIXME: I shouldn't need to include _pvt headers, but it is necessary for usbd_edpt_xfer, _stall, and _busy
#include "device/usbd_pvt.h"
#ifdef xDEBUG
#include "uart_util.h"
static char logMsg[150];
#endif
/*
* The state machine does not allow simultaneous reading and writing. This is
* consistent with USBTMC.
*/
typedef enum
{
STATE_CLOSED, // Endpoints have not yet been opened since USB reset
STATE_NAK, // Bulk-out endpoint is in NAK state.
STATE_IDLE, // Bulk-out endpoint is waiting for CMD.
STATE_RCV, // Bulk-out is receiving DEV_DEP message
STATE_TX_REQUESTED,
STATE_TX_INITIATED,
STATE_TX_SHORTED,
STATE_CLEARING,
STATE_ABORTING_BULK_IN,
STATE_ABORTING_BULK_IN_SHORTED, // aborting, and short packet has been queued for transmission
STATE_ABORTING_BULK_IN_ABORTED, // aborting, and short packet has been transmitted
STATE_ABORTING_BULK_OUT,
STATE_NUM_STATES
} usbtmcd_state_enum;
#if (CFG_TUD_USBTMC_ENABLE_488)
typedef usbtmc_response_capabilities_488_t usbtmc_capabilities_specific_t;
#else
typedef usbtmc_response_capabilities_t usbtmc_capabilities_specific_t;
#endif
typedef struct
{
volatile usbtmcd_state_enum state;
uint8_t itf_id;
uint8_t rhport;
uint8_t ep_bulk_in;
uint8_t ep_bulk_out;
uint8_t ep_int_in;
// 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.
uint32_t transfer_size_sent; // To keep track of data bytes that have been queued in FIFO (not header bytes)
uint8_t lastBulkOutTag; // used for aborts (mostly)
uint8_t lastBulkInTag; // used for aborts (mostly)
uint8_t const * devInBuffer; // pointer to application-layer used for transmissions
usbtmc_capabilities_specific_t const * capabilities;
} usbtmc_interface_state_t;
static usbtmc_interface_state_t usbtmc_state =
{
.itf_id = 0xFF,
};
// 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);
static uint8_t termChar;
static uint8_t termCharRequested = false;
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)
bool atomicChangeState(usbtmcd_state_enum expectedState, usbtmcd_state_enum newState)
{
bool ret = true;
criticalEnter();
usbtmcd_state_enum oldState = usbtmc_state.state;
if (oldState == expectedState)
{
usbtmc_state.state = newState;
}
else
{
ret = false;
}
criticalLeave();
return ret;
}
// 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 tud_usbtmc_transmit_dev_msg_data(
const void * data, size_t len,
bool endOfMessage,
bool usingTermChar)
{
const unsigned int txBufLen = sizeof(usbtmc_state.ep_bulk_in_buf);
#ifndef NDEBUG
TU_ASSERT(len > 0u);
TU_ASSERT(len <= usbtmc_state.transfer_size_remaining);
TU_ASSERT(usbtmc_state.transfer_size_sent == 0u);
if(usingTermChar)
{
TU_ASSERT(usbtmc_state.capabilities->bmDevCapabilities.canEndBulkInOnTermChar);
TU_ASSERT(termCharRequested);
TU_ASSERT(((uint8_t*)data)[len-1u] == 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;
tu_varclr(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 = endOfMessage;
hdr->bmTransferAttributes.UsingTermChar = usingTermChar;
// Copy in the header
const size_t headerLen = sizeof(*hdr);
const size_t dataLen = ((headerLen + hdr->TransferSize) <= txBufLen) ?
len : (txBufLen - headerLen);
const size_t packetLen = headerLen + dataLen;
memcpy((uint8_t*)(usbtmc_state.ep_bulk_in_buf) + headerLen, data, dataLen);
usbtmc_state.transfer_size_remaining = len - dataLen;
usbtmc_state.transfer_size_sent = dataLen;
usbtmc_state.devInBuffer = (uint8_t*)data + (dataLen);
bool stateChanged =
atomicChangeState(STATE_TX_REQUESTED, (packetLen >= txBufLen) ? STATE_TX_INITIATED : STATE_TX_SHORTED);
TU_VERIFY(stateChanged);
TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf, (uint16_t)packetLen));
return true;
}
void usbtmcd_init_cb(void)
{
usbtmc_state.capabilities = tud_usbtmc_get_capabilities_cb();
#ifndef NDEBUG
# if CFG_TUD_USBTMC_ENABLE_488
if(usbtmc_state.capabilities->bmIntfcCapabilities488.supportsTrigger)
TU_ASSERT(&tud_usbtmc_msg_trigger_cb != NULL,);
// Per USB488 spec: table 8
TU_ASSERT(!usbtmc_state.capabilities->bmIntfcCapabilities.listenOnly,);
TU_ASSERT(!usbtmc_state.capabilities->bmIntfcCapabilities.talkOnly,);
# endif
if(usbtmc_state.capabilities->bmIntfcCapabilities.supportsIndicatorPulse)
TU_ASSERT(&tud_usbtmc_indicator_pulse_cb != NULL,);
#endif
usbtmcLock = osal_mutex_create(&usbtmcLockBuffer);
}
bool usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length)
{
(void)rhport;
TU_ASSERT(usbtmc_state.state == STATE_CLOSED);
uint8_t const * p_desc;
uint8_t found_endpoints = 0;
#ifndef NDEBUG
TU_ASSERT(itf_desc->bInterfaceClass == TUD_USBTMC_APP_CLASS);
TU_ASSERT(itf_desc->bInterfaceSubClass == TUD_USBTMC_APP_SUBCLASS);
// Only 2 or 3 endpoints are allowed for USBTMC.
TU_ASSERT((itf_desc->bNumEndpoints == 2) || (itf_desc->bNumEndpoints ==3));
#endif
// Interface
(*p_length) = 0u;
p_desc = (uint8_t const *) itf_desc;
usbtmc_state.itf_id = itf_desc->bInterfaceNumber;
usbtmc_state.rhport = rhport;
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:
TU_ASSERT(ep_desc->wMaxPacketSize.size == USBTMCD_MAX_PACKET_SIZE);
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:
#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:
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
#ifndef NDEBUG
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 == 3)
{
TU_ASSERT(usbtmc_state.ep_int_in != 0);
}
#if (CFG_TUD_USBTMC_ENABLE_488)
if(usbtmc_state.capabilities->bmIntfcCapabilities488.is488_2 ||
usbtmc_state.capabilities->bmDevCapabilities488.SR1)
{
TU_ASSERT(usbtmc_state.ep_int_in != 0);
}
#endif
#endif
atomicChangeState(STATE_CLOSED, STATE_NAK);
tud_usbtmc_open_cb(itf_desc->iInterface);
return true;
}
// Tell USBTMC class to set its bulk-in EP to ACK so that it can
// receive USBTMC commands.
// Returns false if it was already in an ACK state or is busy
// processing a command (such as a clear). Returns true if it was
// in the NAK state and successfully transitioned to the ACK wait
// state.
bool tud_usbtmc_start_bus_read()
{
usbtmcd_state_enum oldState = usbtmc_state.state;
switch(oldState)
{
// These may transition to IDLE
case STATE_NAK:
case STATE_ABORTING_BULK_IN_ABORTED:
TU_VERIFY(atomicChangeState(oldState, STATE_IDLE));
break;
// When receiving, let it remain receiving
case STATE_RCV:
break;
default:
TU_VERIFY(false);
}
TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_bulk_out, usbtmc_state.ep_bulk_out_buf, 64));
return true;
}
void usbtmcd_reset_cb(uint8_t rhport)
{
(void)rhport;
usbtmc_capabilities_specific_t const * capabilities = tud_usbtmc_get_capabilities_cb();
criticalEnter();
tu_varclr(&usbtmc_state);
usbtmc_state.capabilities = capabilities;
usbtmc_state.itf_id = 0xFFu;
criticalLeave();
}
static bool handle_devMsgOutStart(uint8_t rhport, void *data, size_t len)
{
(void)rhport;
// return true upon failure, as we can assume error is being handled elsewhere.
TU_VERIFY(atomicChangeState(STATE_IDLE, STATE_RCV), true);
usbtmc_state.transfer_size_sent = 0u;
// 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(tud_usbtmc_msgBulkOut_start_cb(msg));
TU_VERIFY(handle_devMsgOut(rhport, (uint8_t*)data + sizeof(*msg), len - sizeof(*msg), len));
usbtmc_state.lastBulkOutTag = msg->header.bTag;
return true;
}
static bool handle_devMsgOut(uint8_t rhport, void *data, size_t len, size_t packetLen)
{
(void)rhport;
// return true upon failure, as we can assume error is being handled elsewhere.
TU_VERIFY(usbtmc_state.state == STATE_RCV,true);
bool shortPacket = (packetLen < USBTMCD_MAX_PACKET_SIZE);
// 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;
TU_VERIFY(atomicChangeState(STATE_RCV, STATE_NAK));
}
len = tu_min32(len, usbtmc_state.transfer_size_remaining);
usbtmc_state.transfer_size_remaining -= len;
usbtmc_state.transfer_size_sent += len;
// App may (should?) call the wait_for_bus() command at this point
if(!tud_usbtmc_msg_data_cb(data, len, atEnd))
{
// TODO: Go to an error state upon failure other than just stalling the EP?
return false;
}
return true;
}
static bool handle_devMsgIn(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;
bool stateChanged = atomicChangeState(STATE_IDLE, STATE_TX_REQUESTED);
TU_VERIFY(stateChanged);
usbtmc_state.lastBulkInTag = msg->header.bTag;
usbtmc_state.transfer_size_remaining = msg->TransferSize;
usbtmc_state.transfer_size_sent = 0u;
termCharRequested = msg->bmTransferAttributes.TermCharEnabled;
termChar = msg->TermChar;
if(termCharRequested)
TU_VERIFY(usbtmc_state.capabilities->bmDevCapabilities.canEndBulkInOnTermChar);
TU_VERIFY(tud_usbtmc_msgBulkIn_request_cb(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);
//uart_tx_str_sync("TMC XFER CB\r\n");
if(usbtmc_state.state == STATE_CLEARING) {
return true; /* I think we can ignore everything here */
}
if(ep_addr == usbtmc_state.ep_bulk_out)
{
usbtmc_msg_generic_t *msg = NULL;
switch(usbtmc_state.state)
{
case STATE_IDLE:
TU_VERIFY(xferred_bytes >= sizeof(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);
switch(msg->header.MsgID) {
case USBTMC_MSGID_DEV_DEP_MSG_OUT:
if(!handle_devMsgOutStart(rhport, msg, xferred_bytes))
{
usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
TU_VERIFY(false);
}
break;
case USBTMC_MSGID_DEV_DEP_MSG_IN:
TU_VERIFY(handle_devMsgIn(msg, xferred_bytes));
break;
#if (CFG_TUD_USBTMC_ENABLE_488)
case USBTMC_MSGID_USB488_TRIGGER:
// Spec says we halt the EP if we didn't declare we support it.
TU_VERIFY(usbtmc_state.capabilities->bmIntfcCapabilities488.supportsTrigger);
TU_VERIFY(tud_usbtmc_msg_trigger_cb(msg));
break;
#endif
case USBTMC_MSGID_VENDOR_SPECIFIC_MSG_OUT:
case USBTMC_MSGID_VENDOR_SPECIFIC_IN:
default:
usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
TU_VERIFY(false);
return false;
}
return true;
case STATE_RCV:
if(!handle_devMsgOut(rhport, usbtmc_state.ep_bulk_out_buf, xferred_bytes, xferred_bytes))
{
usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
TU_VERIFY(false);
}
return true;
case STATE_ABORTING_BULK_OUT:
TU_VERIFY(false);
return false; // Should be stalled by now, shouldn't have received a packet.
case STATE_TX_REQUESTED:
case STATE_TX_INITIATED:
case STATE_ABORTING_BULK_IN:
case STATE_ABORTING_BULK_IN_SHORTED:
case STATE_ABORTING_BULK_IN_ABORTED:
default:
TU_VERIFY(false);
}
}
else if(ep_addr == usbtmc_state.ep_bulk_in)
{
switch(usbtmc_state.state) {
case STATE_TX_SHORTED:
TU_VERIFY(atomicChangeState(STATE_TX_SHORTED, STATE_NAK));
TU_VERIFY(tud_usbtmc_msgBulkIn_complete_cb());
break;
case STATE_TX_INITIATED:
if(usbtmc_state.transfer_size_remaining >=sizeof(usbtmc_state.ep_bulk_in_buf))
{
// FIXME! This removes const below!
TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in,
(void*)usbtmc_state.devInBuffer,sizeof(usbtmc_state.ep_bulk_in_buf)));
usbtmc_state.devInBuffer += sizeof(usbtmc_state.ep_bulk_in_buf);
usbtmc_state.transfer_size_remaining -= sizeof(usbtmc_state.ep_bulk_in_buf);
usbtmc_state.transfer_size_sent += sizeof(usbtmc_state.ep_bulk_in_buf);
}
else // last packet
{
size_t packetLen = usbtmc_state.transfer_size_remaining;
memcpy(usbtmc_state.ep_bulk_in_buf, usbtmc_state.devInBuffer, usbtmc_state.transfer_size_remaining);
usbtmc_state.transfer_size_sent += sizeof(usbtmc_state.transfer_size_remaining);
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));
if(((packetLen % USBTMCD_MAX_PACKET_SIZE) != 0) || (packetLen == 0 ))
{
usbtmc_state.state = STATE_TX_SHORTED;
}
}
return true;
case STATE_ABORTING_BULK_IN:
// need to send short packet (ZLP?)
TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf,(uint16_t)0u));
usbtmc_state.state = STATE_ABORTING_BULK_IN_SHORTED;
return true;
case STATE_ABORTING_BULK_IN_SHORTED:
/* Done. :)*/
usbtmc_state.state = STATE_ABORTING_BULK_IN_ABORTED;
return true;
default:
TU_ASSERT(false);
return false;
}
}
else if (ep_addr == usbtmc_state.ep_int_in) {
// Good?
return true;
}
return false;
}
bool usbtmcd_control_request_cb(uint8_t rhport, tusb_control_request_t const * request) {
uint8_t tmcStatusCode = USBTMC_STATUS_FAILED;
#if (CFG_TUD_USBTMC_ENABLE_488)
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))
{
uint32_t ep_addr = (request->wIndex);
if(ep_addr == usbtmc_state.ep_bulk_out)
{
criticalEnter();
usbtmc_state.state = STATE_NAK; // USBD core has placed EP in NAK state for us
criticalLeave();
tud_usmtmc_bulkOut_clearFeature_cb();
}
else if (ep_addr == usbtmc_state.ep_bulk_in)
{
tud_usbtmc_bulkIn_clearFeature_cb();
}
else
{
return false;
}
return true;
}
// Otherwise, we only handle class requests.
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:
{
usbtmc_initiate_abort_rsp_t rsp = {
.bTag = usbtmc_state.lastBulkOutTag,
};
TU_VERIFY(request->bmRequestType == 0xA2); // in,class,interface
TU_VERIFY(request->wLength == sizeof(rsp));
TU_VERIFY(request->wIndex == usbtmc_state.ep_bulk_out);
// wValue is the requested bTag to abort
if(usbtmc_state.state != STATE_RCV)
{
rsp.USBTMC_status = USBTMC_STATUS_FAILED;
}
else if(usbtmc_state.lastBulkOutTag == (request->wValue & 0x7Fu))
{
rsp.USBTMC_status = USBTMC_STATUS_TRANSFER_NOT_IN_PROGRESS;
}
else
{
rsp.USBTMC_status = USBTMC_STATUS_SUCCESS;
// Check if we've queued a short packet
criticalEnter();
usbtmc_state.state = STATE_ABORTING_BULK_OUT;
criticalLeave();
TU_VERIFY(tud_usbtmc_initiate_abort_bulk_out_cb(&(rsp.USBTMC_status)));
usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
}
TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp,sizeof(rsp)));
return true;
}
case USBTMC_bREQUEST_CHECK_ABORT_BULK_OUT_STATUS:
{
usbtmc_check_abort_bulk_rsp_t rsp = {
.USBTMC_status = USBTMC_STATUS_SUCCESS,
.NBYTES_RXD_TXD = usbtmc_state.transfer_size_sent
};
TU_VERIFY(request->bmRequestType == 0xA2); // in,class,EP
TU_VERIFY(request->wLength == sizeof(rsp));
TU_VERIFY(request->wIndex == usbtmc_state.ep_bulk_out);
TU_VERIFY(tud_usbtmc_check_abort_bulk_out_cb(&rsp));
TU_VERIFY(usbd_edpt_xfer(rhport, 0u, (void*)&rsp,sizeof(rsp)));
return true;
}
case USBTMC_bREQUEST_INITIATE_ABORT_BULK_IN:
{
usbtmc_initiate_abort_rsp_t rsp = {
.bTag = usbtmc_state.lastBulkInTag,
};
TU_VERIFY(request->bmRequestType == 0xA2); // in,class,interface
TU_VERIFY(request->wLength == sizeof(rsp));
TU_VERIFY(request->wIndex == usbtmc_state.ep_bulk_in);
// wValue is the requested bTag to abort
if((usbtmc_state.state == STATE_TX_REQUESTED || usbtmc_state.state == STATE_TX_INITIATED) &&
usbtmc_state.lastBulkInTag == (request->wValue & 0x7Fu))
{
rsp.USBTMC_status = USBTMC_STATUS_SUCCESS;
usbtmc_state.transfer_size_remaining = 0u;
// Check if we've queued a short packet
criticalEnter();
usbtmc_state.state = ((usbtmc_state.transfer_size_sent % USBTMCD_MAX_PACKET_SIZE) == 0) ?
STATE_ABORTING_BULK_IN : STATE_ABORTING_BULK_IN_SHORTED;
criticalLeave();
if(usbtmc_state.transfer_size_sent == 0)
{
// Send short packet, nothing is in the buffer yet
TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf,(uint16_t)0u));
usbtmc_state.state = STATE_ABORTING_BULK_IN_SHORTED;
}
TU_VERIFY(tud_usbtmc_initiate_abort_bulk_in_cb(&(rsp.USBTMC_status)));
}
else if((usbtmc_state.state == STATE_TX_REQUESTED || usbtmc_state.state == STATE_TX_INITIATED))
{ // FIXME: Unsure how to check if the OUT endpoint fifo is non-empty....
rsp.USBTMC_status = USBTMC_STATUS_TRANSFER_NOT_IN_PROGRESS;
}
else
{
rsp.USBTMC_status = USBTMC_STATUS_FAILED;
}
TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp,sizeof(rsp)));
return true;
}
case USBTMC_bREQUEST_CHECK_ABORT_BULK_IN_STATUS:
{
TU_VERIFY(request->bmRequestType == 0xA2); // in,class,EP
TU_VERIFY(request->wLength == 8u);
usbtmc_check_abort_bulk_rsp_t rsp =
{
.USBTMC_status = USBTMC_STATUS_FAILED,
.bmAbortBulkIn =
{
.BulkInFifoBytes = (usbtmc_state.state != STATE_ABORTING_BULK_IN_ABORTED)
},
.NBYTES_RXD_TXD = usbtmc_state.transfer_size_sent,
};
TU_VERIFY(tud_usbtmc_check_abort_bulk_in_cb(&rsp));
criticalEnter();
switch(usbtmc_state.state)
{
case STATE_ABORTING_BULK_IN_ABORTED:
rsp.USBTMC_status = USBTMC_STATUS_SUCCESS;
usbtmc_state.state = STATE_IDLE;
break;
case STATE_ABORTING_BULK_IN:
case STATE_ABORTING_BULK_OUT:
rsp.USBTMC_status = USBTMC_STATUS_PENDING;
break;
default:
break;
}
criticalLeave();
TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp,sizeof(rsp)));
return true;
}
case USBTMC_bREQUEST_INITIATE_CLEAR:
{
TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
TU_VERIFY(request->wLength == sizeof(tmcStatusCode));
// After receiving an INITIATE_CLEAR request, the device must Halt the Bulk-OUT endpoint, queue the
// control endpoint response shown in Table 31, and clear all input buffers and output buffers.
usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
usbtmc_state.transfer_size_remaining = 0;
criticalEnter();
usbtmc_state.state = STATE_CLEARING;
criticalLeave();
TU_VERIFY(tud_usbtmc_initiate_clear_cb(&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
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(tud_usbtmc_check_clear_cb(&clearStatusRsp));
}
if(clearStatusRsp.USBTMC_status == USBTMC_STATUS_SUCCESS)
{
criticalEnter();
usbtmc_state.state = STATE_IDLE;
criticalLeave();
}
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->wLength == sizeof(*(usbtmc_state.capabilities)));
TU_VERIFY(tud_control_xfer(rhport, request, (void*)usbtmc_state.capabilities, sizeof(*usbtmc_state.capabilities)));
return true;
}
// USBTMC Optional Requests
case USBTMC_bREQUEST_INDICATOR_PULSE: // Optional
{
TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
TU_VERIFY(request->wLength == sizeof(tmcStatusCode));
TU_VERIFY(usbtmc_state.capabilities->bmIntfcCapabilities.supportsIndicatorPulse);
TU_VERIFY(tud_usbtmc_indicator_pulse_cb(request, &tmcStatusCode));
TU_VERIFY(tud_control_xfer(rhport, request, (void*)&tmcStatusCode, sizeof(tmcStatusCode)));
return true;
}
#if (CFG_TUD_USBTMC_ENABLE_488)
// USB488 required requests
case USB488_bREQUEST_READ_STATUS_BYTE:
{
usbtmc_read_stb_rsp_488_t rsp;
TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
TU_VERIFY(request->wLength == sizeof(rsp)); // 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);
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 = {
.one = 1,
.bTag = bTag & 0x7Fu,
},
.StatusByte = tud_usbtmc_get_stb_cb(&(rsp.USBTMC_status))
};
usbd_edpt_xfer(rhport, usbtmc_state.ep_int_in, (void*)&intMsg, sizeof(intMsg));
}
else
{
rsp.statusByte = tud_usbtmc_get_stb_cb(&(rsp.USBTMC_status));
}
TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp, sizeof(rsp)));
return true;
}
// USB488 optional requests
case USB488_bREQUEST_REN_CONTROL:
case USB488_bREQUEST_GO_TO_LOCAL:
case USB488_bREQUEST_LOCAL_LOCKOUT:
{
TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
TU_VERIFY(false);
return false;
}
#endif
default:
TU_VERIFY(false);
return false;
}
TU_VERIFY(false);
}
bool usbtmcd_control_complete_cb(uint8_t rhport, tusb_control_request_t const * request)
{
(void)rhport;
//------------- Class Specific Request -------------//
TU_ASSERT (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
return true;
}
#endif /* CFG_TUD_TSMC */

View File

@ -0,0 +1,123 @@
/*
* 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(CFG_TUD_USBTMC_ENABLE_488)
#define CFG_TUD_USBTMC_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
*/
// In order to proceed, app must call call tud_usbtmc_start_bus_read(rhport) during or soon after:
// * tud_usbtmc_open_cb
// * tud_usbtmc_msg_data_cb
// * tud_usbtmc_msgBulkIn_complete_cb
// * tud_usbtmc_msg_trigger_cb
// * (successful) tud_usbtmc_check_abort_bulk_out_cb
// * (successful) tud_usbtmc_check_abort_bulk_in_cb
// * (successful) tud_usmtmc_bulkOut_clearFeature_cb
#if (CFG_TUD_USBTMC_ENABLE_488)
usbtmc_response_capabilities_488_t const * tud_usbtmc_get_capabilities_cb(void);
#else
usbtmc_response_capabilities_t const * tud_usbtmc_get_capabilities_cb(void);
#endif
void tud_usbtmc_open_cb(uint8_t interface_id);
bool tud_usbtmc_msgBulkOut_start_cb(usbtmc_msg_request_dev_dep_out const * msgHeader);
// transfer_complete does not imply that a message is complete.
bool tud_usbtmc_msg_data_cb( void *data, size_t len, bool transfer_complete);
void tud_usmtmc_bulkOut_clearFeature_cb(void); // Notice to clear and abort the pending BULK out transfer
bool tud_usbtmc_msgBulkIn_request_cb(usbtmc_msg_request_dev_dep_in const * request);
bool tud_usbtmc_msgBulkIn_complete_cb(void);
void tud_usbtmc_bulkIn_clearFeature_cb(void); // Notice to clear and abort the pending BULK out transfer
bool tud_usbtmc_initiate_abort_bulk_in_cb(uint8_t *tmcResult);
bool tud_usbtmc_initiate_abort_bulk_out_cb(uint8_t *tmcResult);
bool tud_usbtmc_initiate_clear_cb(uint8_t *tmcResult);
bool tud_usbtmc_check_abort_bulk_in_cb(usbtmc_check_abort_bulk_rsp_t *rsp);
bool tud_usbtmc_check_abort_bulk_out_cb(usbtmc_check_abort_bulk_rsp_t *rsp);
bool tud_usbtmc_check_clear_cb(usbtmc_get_clear_status_rsp_t *rsp);
// Indicator pulse should be 0.5 to 1.0 seconds long
TU_ATTR_WEAK bool tud_usbtmc_indicator_pulse_cb(tusb_control_request_t const * msg, uint8_t *tmcResult);
#if (CFG_TUD_USBTMC_ENABLE_488)
uint8_t tud_usbtmc_get_stb_cb(uint8_t *tmcResult);
TU_ATTR_WEAK bool tud_usbtmc_msg_trigger_cb(usbtmc_msg_generic_t* msg);
//TU_ATTR_WEAK bool tud_usbtmc_app_go_to_local_cb();
#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 tud_usbtmc_transmit_dev_msg_data(
const void * data, size_t len,
bool endOfMessage, bool usingTermChar);
bool tud_usbtmc_start_bus_read(void);
/* "callbacks" from USB device core */
bool usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
void usbtmcd_reset_cb(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_cb(uint8_t rhport, tusb_control_request_t const * request);
bool usbtmcd_control_complete_cb(uint8_t rhport, tusb_control_request_t const * request);
void usbtmcd_init_cb(void);
/************************************************************
* USBTMC Descriptor Templates
*************************************************************/
#endif /* CLASS_USBTMC_USBTMC_DEVICE_H_ */

View File

@ -145,6 +145,23 @@ 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 = TUD_USBTMC_APP_CLASS,
//.subclass_code = TUD_USBTMC_APP_SUBCLASS
.init = usbtmcd_init_cb,
.reset = usbtmcd_reset_cb,
.open = usbtmcd_open_cb,
.control_request = usbtmcd_control_request_cb,
.control_complete = usbtmcd_control_complete_cb,
.xfer_cb = usbtmcd_xfer_cb,
.sof = NULL
},
#endif
};
enum { USBD_CLASS_DRIVER_COUNT = TU_ARRAY_SIZE(usbd_class_drivers) };

View File

@ -261,6 +261,37 @@ TU_ATTR_WEAK bool tud_vendor_control_complete_cb(uint8_t rhport, tusb_control_re
/* MS Endpoint (connected to embedded jack out) */\
5, TUSB_DESC_CS_ENDPOINT, MIDI_CS_ENDPOINT_GENERAL, 1, 3
//------------- TUD_USBTMC/USB488 -------------//
#define TUD_USBTMC_APP_CLASS (TUSB_CLASS_APPLICATION_SPECIFIC)
#define TUD_USBTMC_APP_SUBCLASS 0x03u
#define TUD_USBTMC_PROTOCOL_STD 0x00u
#define TUD_USBTMC_PROTOCOL_USB488 0x01u
// Interface number, number of endpoints, EP string index, USB_TMC_PROTOCOL*, bulk-out endpoint ID,
// bulk-in endpoint ID
#define TUD_USBTMC_IF_DESCRIPTOR(_itfnum, _bNumEndpoints, _stridx, _itfProtocol) \
/* Interface */ \
0x09, TUSB_DESC_INTERFACE, _itfnum, 0x00, _bNumEndpoints, TUD_USBTMC_APP_CLASS, TUD_USBTMC_APP_SUBCLASS, _itfProtocol, _stridx
#define TUD_USBTMC_IF_DESCRIPTOR_LEN 9u
#define TUD_USBTMC_BULK_DESCRIPTORS(_epout, _epin, _bulk_epsize) \
/* Endpoint Out */ \
7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_bulk_epsize), 0u, \
/* Endpoint In */ \
7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_bulk_epsize), 0u
#define TUD_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 TUD_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 TUD_USBTMC_INT_DESCRIPTOR_LEN (7u)
//------------- Vendor -------------//
#define TUD_VENDOR_DESC_LEN (9+7+7)

View File

@ -124,7 +124,7 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result
_control_state.total_transferred += xferred_bytes;
_control_state.buffer = ((uint8_t*)_control_state.buffer) + xferred_bytes;
if ( _control_state.total_len == _control_state.total_transferred || xferred_bytes < CFG_TUD_ENDOINT0_SIZE )
if ( _control_state.total_len == _control_state.total_transferred || xferred_bytes < CFG_TUD_ENDPOINT0_SIZE )
{
// DATA stage is complete
bool is_ok = true;

View File

@ -172,8 +172,8 @@ void dcd_init(uint8_t rhport)
//------------- user manual 11.13 usb device controller initialization -------------//
// step 6 : set up control endpoint
set_ep_size(0, CFG_TUD_ENDOINT0_SIZE);
set_ep_size(1, CFG_TUD_ENDOINT0_SIZE);
set_ep_size(0, CFG_TUD_ENDPOINT0_SIZE);
set_ep_size(1, CFG_TUD_ENDPOINT0_SIZE);
bus_reset();

View File

@ -115,7 +115,7 @@ static void bus_reset(uint8_t rhport)
//------------- Set up Control Endpoints (0 OUT, 1 IN) -------------//
p_dcd->qhd[0].zero_length_termination = p_dcd->qhd[1].zero_length_termination = 1;
p_dcd->qhd[0].max_package_size = p_dcd->qhd[1].max_package_size = CFG_TUD_ENDOINT0_SIZE;
p_dcd->qhd[0].max_package_size = p_dcd->qhd[1].max_package_size = CFG_TUD_ENDPOINT0_SIZE;
p_dcd->qhd[0].qtd_overlay.next = p_dcd->qhd[1].qtd_overlay.next = QTD_NEXT_INVALID;
p_dcd->qhd[0].int_on_setup = 1; // OUT only

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