Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add notification support for device class USBTMC #2494

Merged
merged 6 commits into from Apr 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -57,6 +57,7 @@ hw/mcu/st/cmsis_device_f4
hw/mcu/st/cmsis_device_f7
hw/mcu/st/cmsis_device_g0
hw/mcu/st/cmsis_device_g4
hw/mcu/st/cmsis_device_h5
hw/mcu/st/cmsis_device_h7
hw/mcu/st/cmsis_device_l0
hw/mcu/st/cmsis_device_l1
Expand All @@ -72,6 +73,7 @@ hw/mcu/st/stm32f4xx_hal_driver
hw/mcu/st/stm32f7xx_hal_driver
hw/mcu/st/stm32g0xx_hal_driver
hw/mcu/st/stm32g4xx_hal_driver
hw/mcu/st/stm32h5xx_hal_driver
hw/mcu/st/stm32h7xx_hal_driver
hw/mcu/st/stm32l0xx_hal_driver
hw/mcu/st/stm32l1xx_hal_driver
Expand Down
25 changes: 25 additions & 0 deletions src/class/usbtmc/usbtmc.h
Expand Up @@ -183,6 +183,23 @@ typedef enum {

} usmtmc_request_type_enum;

typedef enum {
// The last and first valid bNotify1 for use by the USBTMC class specification.
USBTMC_bNOTIFY1_USBTMC_FIRST = 0x00,
USBTMC_bNOTIFY1_USBTMC_LAST = 0x3F,

// The last and first valid bNotify1 for use by vendors.
USBTMC_bNOTIFY1_VENDOR_SPECIFIC_FIRST = 0x40,
USBTMC_bNOTIFY1_VENDOR_SPECIFIC_LAST = 0x7F,

// The last and first valid bNotify1 for use by USBTMC subclass specifications.
USBTMC_bNOTIFY1_SUBCLASS_FIRST = 0x80,
USBTMC_bNOTIFY1_SUBCLASS_LAST = 0xFF,

// From the USB488 Subclass Specification, Section 3.4.
USB488_bNOTIFY1_SRQ = 0x81,
} usbtmc_int_in_payload_format;

typedef enum {
USBTMC_STATUS_SUCCESS = 0x01,
USBTMC_STATUS_PENDING = 0x02,
Expand Down Expand Up @@ -303,6 +320,14 @@ typedef struct TU_ATTR_PACKED

TU_VERIFY_STATIC(sizeof(usbtmc_read_stb_rsp_488_t) == 3u, "struct wrong length");

typedef struct TU_ATTR_PACKED
{
uint8_t bNotify1; // Must be USB488_bNOTIFY1_SRQ
uint8_t StatusByte;
} usbtmc_srq_interrupt_488_t;

TU_VERIFY_STATIC(sizeof(usbtmc_srq_interrupt_488_t) == 2u, "struct wrong length");

typedef struct TU_ATTR_PACKED
{
struct TU_ATTR_PACKED
Expand Down
33 changes: 28 additions & 5 deletions src/class/usbtmc/usbtmc_device.c
Expand Up @@ -86,6 +86,11 @@ tu_static char logMsg[150];
// imposes a minimum buffer size of 32 bytes.
#define USBTMCD_BUFFER_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)

// Interrupt endpoint buffer size, default to 2 bytes as USB488 specification.
#ifndef CFG_TUD_USBTMC_INT_EP_SIZE
#define CFG_TUD_USBTMC_INT_EP_SIZE 2
#endif

/*
* The state machine does not allow simultaneous reading and writing. This is
* consistent with USBTMC.
Expand Down Expand Up @@ -124,13 +129,15 @@ typedef struct
uint8_t ep_bulk_in;
uint8_t ep_bulk_out;
uint8_t ep_int_in;
uint32_t ep_bulk_in_wMaxPacketSize;
uint32_t ep_bulk_out_wMaxPacketSize;
// IN buffer is only used for first packet, not the remainder
// in order to deal with prepending header
CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_in_buf[USBTMCD_BUFFER_SIZE];
uint32_t ep_bulk_in_wMaxPacketSize;
// OUT buffer receives one packet at a time
CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_out_buf[USBTMCD_BUFFER_SIZE];
uint32_t ep_bulk_out_wMaxPacketSize;
// Buffer int msg to ensure alignment and placement correctness
CFG_TUSB_MEM_ALIGN uint8_t ep_int_in_buf[CFG_TUD_USBTMC_INT_EP_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)
Expand Down Expand Up @@ -240,6 +247,19 @@ bool tud_usbtmc_transmit_dev_msg_data(
return true;
}

bool tud_usbtmc_transmit_notification_data(const void * data, size_t len)
{
#ifndef NDEBUG
TU_ASSERT(len > 0);
TU_ASSERT(usbtmc_state.ep_int_in != 0);
#endif
TU_VERIFY(usbd_edpt_busy(usbtmc_state.rhport, usbtmc_state.ep_int_in));

TU_VERIFY(tu_memcpy_s(usbtmc_state.ep_int_in_buf, sizeof(usbtmc_state.ep_int_in_buf), data, len) == 0);
TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_int_in, usbtmc_state.ep_int_in_buf, (uint16_t)len));
return true;
}

void usbtmcd_init_cb(void)
{
usbtmc_state.capabilities = tud_usbtmc_get_capabilities_cb();
Expand Down Expand Up @@ -540,9 +560,10 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
case STATE_TX_INITIATED:
if(usbtmc_state.transfer_size_remaining >= sizeof(usbtmc_state.ep_bulk_in_buf))
{
// FIXME! This removes const below!
// Copy buffer to ensure alignment correctness
memcpy(usbtmc_state.ep_bulk_in_buf, usbtmc_state.devInBuffer, sizeof(usbtmc_state.ep_bulk_in_buf));
TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in,
(void*)(uintptr_t) usbtmc_state.devInBuffer, sizeof(usbtmc_state.ep_bulk_in_buf)));
usbtmc_state.ep_bulk_in_buf, 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);
Expand Down Expand Up @@ -578,7 +599,9 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
}
}
else if (ep_addr == usbtmc_state.ep_int_in) {
// Good?
if (tud_usbtmc_notification_complete_cb) {
TU_VERIFY(tud_usbtmc_notification_complete_cb());
}
return true;
}
return false;
Expand Down
29 changes: 17 additions & 12 deletions src/class/usbtmc/usbtmc_device.h
Expand Up @@ -73,6 +73,10 @@ 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);

// The interrupt-IN endpoint buffer was transmitted to the host. Use
// tud_usbtmc_transmit_notification_data to send another notification.
TU_ATTR_WEAK bool tud_usbtmc_notification_complete_cb(void);

// 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);

Expand All @@ -82,17 +86,23 @@ 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.
******************************************/

// 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);

// Buffers a notification to be sent to the host. The data starts
// with the bNotify1 field, see the USBTMC Specification, Table 13.
//
// If the previous notification data has not yet been sent, this
// returns false.
//
// Requires an interrupt endpoint in the interface.
bool tud_usbtmc_transmit_notification_data(const void * data, size_t len);

bool tud_usbtmc_start_bus_read(void);


Expand All @@ -104,9 +114,4 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result,
bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
void usbtmcd_init_cb(void);

/************************************************************
* USBTMC Descriptor Templates
*************************************************************/


#endif /* CLASS_USBTMC_USBTMC_DEVICE_H_ */