diff options
Diffstat (limited to 'class/tmc')
| -rw-r--r-- | class/tmc/usb_tmc.h | 149 | ||||
| -rw-r--r-- | class/tmc/usbd_tmc.c | 258 | ||||
| -rw-r--r-- | class/tmc/usbd_tmc.h | 27 |
3 files changed, 434 insertions, 0 deletions
diff --git a/class/tmc/usb_tmc.h b/class/tmc/usb_tmc.h new file mode 100644 index 00000000..de03d297 --- /dev/null +++ b/class/tmc/usb_tmc.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef USB_TMC_H +#define USB_TMC_H + +#define TMC_VERSION 0x0100 + +/* Table 43: TMC Class Code */ +#define TMC_CLASS_APPLICATION 0xfe +#define TMC_APPLICATION_SUBCLASS_TMC 0x03 + +/* Table 44 */ +#define TMC_PROTOCOL_NONE 0 +#define TMC_PROTOCOL_USB488 1 + +/* USB TMC Class Specific Requests, Table 15 sec 4.2.1 */ +#define TMC_REQUEST_INITIATE_ABORT_BULK_OUT 1 +#define TMC_REQUEST_CHECK_ABORT_BULK_OUT_STATUS 2 +#define TMC_REQUEST_INITIATE_ABORT_BULK_IN 3 +#define TMC_REQUEST_CHECK_ABORT_BULK_IN_STATUS 4 +#define TMC_REQUEST_INITIATE_CLEAR 5 +#define TMC_REQUEST_CHECK_CLEAR_STATUS 6 +#define TMC_REQUEST_GET_CAPABILITIES 7 +#define TMC_REQUEST_INDICATOR_PULSE 64 //optional + +/* USB TMC status values Table 16 */ +#define TMC_STATUS_SUCCESS 1 +#define TMC_STATUS_PENDING 2 +#define TMC_STATUS_FAILED 0x80 +#define TMC_STATUS_TRANSFER_NOT_IN_PROGRESS 0x81 +#define TMC_STATUS_SPLIT_NOT_IN_PROGRESS 0x82 +#define TMC_STATUS_SPLIT_IN_PROGRESS 0x83 + +struct tmc_bulk_header { + uint8_t MsgID; + uint8_t bTag; + uint8_t bTagInverse; + uint8_t reserved; + + union { + struct _dev_dep_msg_out { + uint32_t transferSize; + uint8_t bmTransferAttributes; + uint8_t reserved[3]; + } dev_dep_msg_out; + + struct _req_dev_dep_msg_in { + uint32_t transferSize; + uint8_t bmTransferAttributes; + uint8_t TermChar; + uint8_t reserved[2]; + } req_dev_dep_msg_in; + + struct _dev_dep_msg_in { + uint32_t transferSize; + uint8_t bmTransferAttributes; + uint8_t reserved[3]; + } dev_dep_msg_in; + + struct _vendor_specific_out { + uint32_t transferSize; + uint8_t reserved[4]; + } vendor_specific_out; + + struct _req_vendor_specific_in { + uint32_t transferSize; + uint8_t reserved[4]; + } req_vendor_specific_in; + + struct _vendor_specific_in { + uint32_t transferSize; + uint8_t reserved[4]; + } vendor_specific_in; + + uint8_t data[8]; + } msg_specific; +} __attribute__((packed)); + +/* Table 2, MsgId values */ +#define TMC_MSGID_OUT_DEV_DEP_MSG_OUT 1 +#define TMC_MSGID_OUT_REQUEST_DEV_DEP_MSG_IN 2 +#define TMC_MSGID_IN_DEV_DEP_MSG_IN 2 +/* 3-125 Reserved for USBTMC */ +#define TMC_MSGID_OUT_VENDOR_SPECIFIC_OUT 126 +#define TMC_MSGID_OUT_REQUEST_VENDOR_SPECIFIC_IN 127 +#define TMC_MSGID_IN_VENDOR_SPECIFIC_IN 127 +/* 128-255 Reserved for USBTMC subclass and VISA */ + +/** The last USBTMC message data byte in the transfer is the last byte of the + * USBTMC message. */ +#define TMC_BULK_HEADER_TRANSFER_ATTR_EOM (1 << 0) +/** The Bulk-IN transfer must terminate on the specified TermChar. The Host may + * only set this bit if the USBTMC interface indicates it supports TermChar in + * the GET_CAPABILITIES response packet */ +#define TMC_BULK_HEADER_TRANSFER_ATTR_TERMCHAR (1 << 1) + +#define TMC_INTERFACE_CAPABILITY_INDICATOR_PULSE (1 << 2) +#define TMC_INTERFACE_CAPABILITY_TALK_ONLY (1 << 1) +#define TMC_INTERFACE_CAPABILITY_LISTEN_ONLY (1 << 0) +#define TMC_DEVICE_CAPABILITY_TERMCHAR (1 << 0) + +struct tmc_capabilities_response { + uint8_t USBTMC_status; + uint8_t reserved0; + uint16_t bcdUSBTMC; + uint8_t InterfaceCapabilities; + uint8_t DeviceCapabilities; + uint8_t reserved1[6]; + uint16_t bcdUSB488; + uint8_t InterfaceCapabilities488; + uint8_t DeviceCapabilities488; + uint8_t reserved_subclass[8]; +} __attribute__((packed)); + +struct tmc_initiate_abort_response { + uint8_t USBTMC_status; + uint8_t bTag; +} __attribute__((packed)); + +struct tmc_check_abort_bulk_response { + uint8_t USBTMC_status; + uint8_t bmAbortBulkIn; // D0: BulkInFifoBytes/BulkOutFifoBytes + uint8_t reserved[2]; + uint32_t NBYTES_RXD_TXD; +} __attribute__((packed)); + +struct tmc_check_clear_status_response { + uint8_t USBTMC_status; + uint8_t bmClear; // D0: BulkInFifoBytes/BulkOutFifoBytes +} __attribute__((packed)); + +// clang-format off +#define TMC_DESCRIPTOR_INIT(bInterfaceNumber, out_ep, in_ep, wMaxPacketSize, str_idx) \ + USB_INTERFACE_DESCRIPTOR_INIT(bInterfaceNumber, 0x00, 0x02, 0xFE, 0x03, 0x00, str_idx), \ + USB_ENDPOINT_DESCRIPTOR_INIT(out_ep, USB_ENDPOINT_TYPE_BULK, wMaxPacketSize, 0x00), \ + USB_ENDPOINT_DESCRIPTOR_INIT(in_ep, USB_ENDPOINT_TYPE_BULK, wMaxPacketSize, 0x00) +// clang-format on + +// clang-format off +#define TMC488_DESCRIPTOR_INIT(bInterfaceNumber, out_ep, in_ep, wMaxPacketSize, str_idx) \ + USB_INTERFACE_DESCRIPTOR_INIT(bInterfaceNumber, 0x00, 0x02, 0xFE, 0x03, 0x01, str_idx), \ + USB_ENDPOINT_DESCRIPTOR_INIT(out_ep, USB_ENDPOINT_TYPE_BULK, wMaxPacketSize, 0x00), \ + USB_ENDPOINT_DESCRIPTOR_INIT(in_ep, USB_ENDPOINT_TYPE_BULK, wMaxPacketSize, 0x00) +// clang-format on + +#endif /* USB_TMC_H */
\ No newline at end of file diff --git a/class/tmc/usbd_tmc.c b/class/tmc/usbd_tmc.c new file mode 100644 index 00000000..7f351e99 --- /dev/null +++ b/class/tmc/usbd_tmc.c @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "usbd_core.h" +#include "usbd_tmc.h" + +#define TMC_OUT_EP_IDX 0 +#define TMC_IN_EP_IDX 1 + +/* Describe EndPoints configuration */ +static struct usbd_endpoint tmc_ep_data[2]; + +#ifndef CONFIG_USBDEV_TMC_MAX_BUFSIZE +#define CONFIG_USBDEV_TMC_MAX_BUFSIZE 1024 +#endif + +USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t tmc_read_buffer[CONFIG_USBDEV_TMC_MAX_BUFSIZE]; +USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t tmc_write_buffer[CONFIG_USBDEV_TMC_MAX_BUFSIZE]; + +static volatile uint32_t g_tmc_read_offset = 0; +static volatile uint32_t g_tmc_read_total = 0; + +static scpi_t scpi_context; + +static char scpi_input_buffer[CONFIG_USBDEV_TMC_MAX_BUFSIZE]; + +#define SCPI_ERROR_QUEUE_SIZE 4 +static scpi_error_t scpi_error_queue_data[SCPI_ERROR_QUEUE_SIZE]; + +static size_t SCPI_Write(scpi_t *context, const char *data, size_t len) +{ + (void)context; + + struct tmc_bulk_header *header = (struct tmc_bulk_header *)tmc_write_buffer; + + USB_ASSERT_MSG((header->msg_specific.dev_dep_msg_in.transferSize + len) <= (CONFIG_USBDEV_TMC_MAX_BUFSIZE - sizeof(struct tmc_bulk_header)), "SCPI_Write: buffer overflow\r\n"); + + memcpy(tmc_write_buffer + sizeof(struct tmc_bulk_header) + header->msg_specific.dev_dep_msg_in.transferSize, data, len); + header->msg_specific.dev_dep_msg_in.transferSize += len; + + return len; +} + +static scpi_result_t SCPI_Flush(scpi_t *context) +{ + (void)context; + + return SCPI_RES_OK; +} + +static int SCPI_Error(scpi_t *context, int_fast16_t err) +{ + (void)context; + + return SCPI_RES_OK; +} + +static scpi_result_t SCPI_Control(scpi_t *context, scpi_ctrl_name_t ctrl, scpi_reg_val_t val) +{ + (void)context; + + return SCPI_RES_OK; +} + +static scpi_result_t SCPI_Reset(scpi_t *context) +{ + (void)context; + + return SCPI_RES_OK; +} + +static scpi_interface_t scpi_interface = { + .error = SCPI_Error, + .write = SCPI_Write, + .control = SCPI_Control, + .flush = SCPI_Flush, + .reset = SCPI_Reset, +}; + +static int tmc_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len) +{ + USB_LOG_DBG("TMC Class request: " + "bRequest 0x%02x\r\n", + setup->bRequest); + + struct tmc_capabilities_response s_tmc_capabilities = { + .USBTMC_status = TMC_STATUS_SUCCESS, + .bcdUSBTMC = 0x0100, + .reserved0 = 0, + .reserved1 = { 0 }, + .reserved_subclass = { 0 }, + .InterfaceCapabilities = 0, + .DeviceCapabilities = 0 + }; + + switch (setup->bRequest) { + case TMC_REQUEST_INITIATE_ABORT_BULK_OUT: + (*data)[0] = TMC_STATUS_SUCCESS; + *len = 2; + break; + case TMC_REQUEST_CHECK_ABORT_BULK_OUT_STATUS: + (*data)[0] = TMC_STATUS_SUCCESS; + (*data)[1] = 0; // bmAbortBulkIn + (*data)[2] = 0; // reserved + (*data)[3] = 0; // reserved + *len = 4; + break; + case TMC_REQUEST_INITIATE_ABORT_BULK_IN: + (*data)[0] = TMC_STATUS_SUCCESS; + *len = 2; + break; + case TMC_REQUEST_CHECK_ABORT_BULK_IN_STATUS: + (*data)[0] = TMC_STATUS_SUCCESS; + (*data)[1] = 0; // bmAbortBulkIn + (*data)[2] = 0; // reserved + (*data)[3] = 0; // reserved + *len = 4; + break; + case TMC_REQUEST_INITIATE_CLEAR: + (*data)[0] = TMC_STATUS_SUCCESS; + *len = 1; + break; + case TMC_REQUEST_CHECK_CLEAR_STATUS: + (*data)[0] = TMC_STATUS_SUCCESS; + (*data)[1] = 0; + *len = 2; + break; + case TMC_REQUEST_GET_CAPABILITIES: + memcpy(*data, &s_tmc_capabilities, sizeof(s_tmc_capabilities)); + *len = sizeof(s_tmc_capabilities); + break; + case TMC_REQUEST_INDICATOR_PULSE: + (*data)[0] = TMC_STATUS_SUCCESS; + *len = 1; + break; + default: + USB_LOG_WRN("Unhandled TMC Class bRequest 0x%02x\r\n", setup->bRequest); + return -1; + } + + return 0; +} + +void tmc_notify_handler(uint8_t busid, uint8_t event, void *arg) +{ + (void)arg; + + switch (event) { + case USBD_EVENT_RESET: + break; + case USBD_EVENT_CONFIGURED: + g_tmc_read_offset = 0; + g_tmc_read_total = 0; + usbd_ep_start_read(busid, tmc_ep_data[TMC_OUT_EP_IDX].ep_addr, tmc_read_buffer + g_tmc_read_offset, usbd_get_ep_mps(busid, tmc_ep_data[TMC_OUT_EP_IDX].ep_addr)); + break; + + default: + break; + } +} + +void tmc_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes) +{ + struct tmc_bulk_header *out_header = (struct tmc_bulk_header *)tmc_read_buffer; + struct tmc_bulk_header *in_header = (struct tmc_bulk_header *)tmc_write_buffer; + + g_tmc_read_offset += nbytes; + + if (g_tmc_read_total == 0) { + USB_ASSERT_MSG(nbytes >= sizeof(struct tmc_bulk_header), "tmc header size error\r\n"); + + switch (out_header->MsgID) { + case TMC_MSGID_OUT_DEV_DEP_MSG_OUT: + g_tmc_read_total = ((out_header->msg_specific.dev_dep_msg_out.transferSize + 3u) & ~3u) + sizeof(struct tmc_bulk_header); + break; + case TMC_MSGID_OUT_REQUEST_DEV_DEP_MSG_IN: + g_tmc_read_total = sizeof(struct tmc_bulk_header); + break; + default: + USB_LOG_WRN("Unhandled TMC MsgID 0x%02x\r\n", out_header->MsgID); + break; + } + USB_ASSERT_MSG(g_tmc_read_total <= CONFIG_USBDEV_TMC_MAX_BUFSIZE, "tmc read buffer overflow\r\n"); + } + + if (g_tmc_read_offset < g_tmc_read_total) { + usbd_ep_start_read(busid, tmc_ep_data[TMC_OUT_EP_IDX].ep_addr, tmc_read_buffer + g_tmc_read_offset, usbd_get_ep_mps(busid, tmc_ep_data[TMC_OUT_EP_IDX].ep_addr)); + return; + } + + switch (out_header->MsgID) { + case TMC_MSGID_OUT_DEV_DEP_MSG_OUT: + SCPI_Input(&scpi_context, + (const char *)(tmc_read_buffer + sizeof(struct tmc_bulk_header)), + g_tmc_read_total); + break; + case TMC_MSGID_OUT_REQUEST_DEV_DEP_MSG_IN: + + in_header->MsgID = TMC_MSGID_IN_DEV_DEP_MSG_IN; + in_header->bTag = out_header->bTag; + in_header->bTagInverse = out_header->bTagInverse; + in_header->reserved = 0; + in_header->msg_specific.dev_dep_msg_in.bmTransferAttributes = TMC_BULK_HEADER_TRANSFER_ATTR_TERMCHAR; + + usbd_ep_start_write(busid, tmc_ep_data[TMC_IN_EP_IDX].ep_addr, tmc_write_buffer, sizeof(struct tmc_bulk_header) + in_header->msg_specific.dev_dep_msg_in.transferSize); + break; + default: + USB_LOG_WRN("Unhandled TMC MsgID 0x%02x\r\n", out_header->MsgID); + break; + } + + g_tmc_read_offset = 0; + g_tmc_read_total = 0; + usbd_ep_start_read(busid, tmc_ep_data[TMC_OUT_EP_IDX].ep_addr, tmc_read_buffer + g_tmc_read_offset, usbd_get_ep_mps(busid, tmc_ep_data[TMC_OUT_EP_IDX].ep_addr)); +} + +void tmc_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes) +{ + if ((nbytes % usbd_get_ep_mps(busid, ep)) == 0 && nbytes) { + /* send zlp */ + usbd_ep_start_write(busid, ep, NULL, 0); + } else { + } +} + +struct usbd_interface *usbd_tmc_init_intf(uint8_t busid, + struct usbd_interface *intf, + const uint8_t out_ep, const uint8_t in_ep, + const scpi_command_t *scpi_commands, + const char **idn) +{ + (void)busid; + + intf->class_interface_handler = tmc_class_interface_request_handler; + intf->class_endpoint_handler = NULL; + intf->vendor_handler = NULL; + intf->notify_handler = tmc_notify_handler; + + tmc_ep_data[TMC_OUT_EP_IDX].ep_addr = out_ep; + tmc_ep_data[TMC_OUT_EP_IDX].ep_cb = tmc_bulk_out; + tmc_ep_data[TMC_IN_EP_IDX].ep_addr = in_ep; + tmc_ep_data[TMC_IN_EP_IDX].ep_cb = tmc_bulk_in; + + usbd_add_endpoint(busid, &tmc_ep_data[TMC_OUT_EP_IDX]); + usbd_add_endpoint(busid, &tmc_ep_data[TMC_IN_EP_IDX]); + + SCPI_Init(&scpi_context, + scpi_commands, + &scpi_interface, + scpi_units_def, + idn[0], idn[1], idn[2], idn[3], + scpi_input_buffer, CONFIG_USBDEV_TMC_MAX_BUFSIZE, + scpi_error_queue_data, SCPI_ERROR_QUEUE_SIZE); + + return intf; +}
\ No newline at end of file diff --git a/class/tmc/usbd_tmc.h b/class/tmc/usbd_tmc.h new file mode 100644 index 00000000..ca17d0d6 --- /dev/null +++ b/class/tmc/usbd_tmc.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef USBD_TMC_H +#define USBD_TMC_H + +#include "usb_tmc.h" +#include "scpi/scpi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Init tmc interface driver */ +struct usbd_interface *usbd_tmc_init_intf(uint8_t busid, + struct usbd_interface *intf, + const uint8_t out_ep, const uint8_t in_ep, + const scpi_command_t *scpi_commands, + const char **idn); + +#ifdef __cplusplus +} +#endif + +#endif /* USBD_TMC_H */ |
