summaryrefslogtreecommitdiff
path: root/class/tmc
diff options
context:
space:
mode:
Diffstat (limited to 'class/tmc')
-rw-r--r--class/tmc/usb_tmc.h149
-rw-r--r--class/tmc/usbd_tmc.c258
-rw-r--r--class/tmc/usbd_tmc.h27
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 */