| /* |
| * Copyright (c) 2023 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/device.h> |
| #include <zephyr/sys/byteorder.h> |
| #include <zephyr/usb/usb_device.h> |
| #include <zephyr/usb/bos.h> |
| #include <zephyr/usb/msos_desc.h> |
| #include <zephyr/net_buf.h> |
| |
| #include <usb_descriptor.h> |
| #include <cmsis_dap.h> |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(dap_sample, LOG_LEVEL_INF); |
| |
| NET_BUF_POOL_FIXED_DEFINE(dapusb_rx_pool, CONFIG_CMSIS_DAP_PACKET_COUNT, |
| CONFIG_CMSIS_DAP_PACKET_SIZE, 0, NULL); |
| |
| static uint8_t rx_buf[CONFIG_CMSIS_DAP_PACKET_SIZE]; |
| static uint8_t tx_buf[CONFIG_CMSIS_DAP_PACKET_SIZE]; |
| |
| static K_FIFO_DEFINE(dap_rx_queue); |
| |
| #define DAP_IFACE_STR_DESC "CMSIS-DAP v2" |
| |
| struct dap_iface_descriptor { |
| uint8_t bLength; |
| uint8_t bDescriptorType; |
| uint8_t bString[USB_BSTRING_LENGTH(DAP_IFACE_STR_DESC)]; |
| } __packed; |
| |
| USBD_STRING_DESCR_USER_DEFINE(primary) struct dap_iface_descriptor dap_iface_desc = { |
| .bLength = USB_STRING_DESCRIPTOR_LENGTH(DAP_IFACE_STR_DESC), |
| .bDescriptorType = USB_DESC_STRING, |
| .bString = DAP_IFACE_STR_DESC |
| }; |
| |
| #define DAP_USB_EP_IN 0x81 |
| #define DAP_USB_EP_OUT 0x01 |
| #define DAP_USB_EP_IN_IDX 0 |
| #define DAP_USB_EP_OUT_IDX 1 |
| |
| #define WEBUSB_VENDOR_CODE 0x21 |
| #define WINUSB_VENDOR_CODE 0x20 |
| |
| /* {CDB3B5AD-293B-4663-AA36-1AAE46463776} */ |
| #define CMSIS_DAP_V2_DEVICE_INTERFACE_GUID \ |
| '{', 0x00, 'C', 0x00, 'D', 0x00, 'B', 0x00, '3', 0x00, 'B', 0x00, \ |
| '5', 0x00, 'A', 0x00, 'D', 0x00, '-', 0x00, '2', 0x00, '9', 0x00, \ |
| '3', 0x00, 'B', 0x00, '-', 0x00, '4', 0x00, '6', 0x00, '6', 0x00, \ |
| '3', 0x00, '-', 0x00, 'A', 0x00, 'A', 0x00, '3', 0x00, '6', 0x00, \ |
| '-', 0x00, '1', 0x00, 'A', 0x00, 'A', 0x00, 'E', 0x00, '4', 0x00, \ |
| '6', 0x00, '4', 0x00, '6', 0x00, '3', 0x00, '7', 0x00, '7', 0x00, \ |
| '6', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00 |
| |
| #define COMPATIBLE_ID_WINUSB \ |
| 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00 |
| |
| static struct msosv2_descriptor { |
| struct msosv2_descriptor_set_header header; |
| #if defined(CONFIG_USB_COMPOSITE_DEVICE) |
| struct msosv2_function_subset_header subset_header; |
| #endif |
| struct msosv2_compatible_id compatible_id; |
| struct msosv2_guids_property guids_property; |
| } __packed msosv2_cmsis_dap_desc = { |
| /* |
| * Microsoft OS 2.0 descriptor set. This tells Windows what kind |
| * of device this is and to install the WinUSB driver. |
| */ |
| .header = { |
| .wLength = sizeof(struct msosv2_descriptor_set_header), |
| .wDescriptorType = MS_OS_20_SET_HEADER_DESCRIPTOR, |
| .dwWindowsVersion = 0x06030000, |
| .wTotalLength = sizeof(struct msosv2_descriptor), |
| }, |
| #if defined(CONFIG_USB_COMPOSITE_DEVICE) |
| .subset_header = { |
| .wLength = sizeof(struct msosv2_function_subset_header), |
| .wDescriptorType = MS_OS_20_SUBSET_HEADER_FUNCTION, |
| .wSubsetLength = sizeof(struct msosv2_function_subset_header) |
| + sizeof(struct msosv2_compatible_id) |
| + sizeof(struct msosv2_guids_property), |
| }, |
| #endif |
| .compatible_id = { |
| .wLength = sizeof(struct msosv2_compatible_id), |
| .wDescriptorType = MS_OS_20_FEATURE_COMPATIBLE_ID, |
| .CompatibleID = {COMPATIBLE_ID_WINUSB}, |
| }, |
| .guids_property = { |
| .wLength = sizeof(struct msosv2_guids_property), |
| .wDescriptorType = MS_OS_20_FEATURE_REG_PROPERTY, |
| .wPropertyDataType = MS_OS_20_PROPERTY_DATA_REG_MULTI_SZ, |
| .wPropertyNameLength = 42, |
| .PropertyName = {DEVICE_INTERFACE_GUIDS_PROPERTY_NAME}, |
| .wPropertyDataLength = 80, |
| .bPropertyData = {CMSIS_DAP_V2_DEVICE_INTERFACE_GUID}, |
| }, |
| }; |
| |
| USB_DEVICE_BOS_DESC_DEFINE_CAP struct usb_bos_msosv2_desc { |
| struct usb_bos_platform_descriptor platform; |
| struct usb_bos_capability_msos cap; |
| } __packed bos_cap_msosv2 = { |
| /* Microsoft OS 2.0 Platform Capability Descriptor */ |
| .platform = { |
| .bLength = sizeof(struct usb_bos_platform_descriptor) |
| + sizeof(struct usb_bos_capability_msos), |
| .bDescriptorType = USB_DESC_DEVICE_CAPABILITY, |
| .bDevCapabilityType = USB_BOS_CAPABILITY_PLATFORM, |
| .bReserved = 0, |
| .PlatformCapabilityUUID = { |
| /** |
| * MS OS 2.0 Platform Capability ID |
| * D8DD60DF-4589-4CC7-9CD2-659D9E648A9F |
| */ |
| 0xDF, 0x60, 0xDD, 0xD8, |
| 0x89, 0x45, |
| 0xC7, 0x4C, |
| 0x9C, 0xD2, |
| 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F, |
| }, |
| }, |
| .cap = { |
| /* Windows version (8.1) (0x06030000) */ |
| .dwWindowsVersion = sys_cpu_to_le32(0x06030000), |
| .wMSOSDescriptorSetTotalLength = |
| sys_cpu_to_le16(sizeof(msosv2_cmsis_dap_desc)), |
| .bMS_VendorCode = WINUSB_VENDOR_CODE, |
| .bAltEnumCode = 0x00 |
| }, |
| }; |
| |
| USB_DEVICE_BOS_DESC_DEFINE_CAP struct usb_bos_webusb_desc { |
| struct usb_bos_platform_descriptor platform; |
| struct usb_bos_capability_webusb cap; |
| } __packed bos_cap_webusb = { |
| /* WebUSB Platform Capability Descriptor: |
| * https://wicg.github.io/webusb/#webusb-platform-capability-descriptor |
| */ |
| .platform = { |
| .bLength = sizeof(struct usb_bos_platform_descriptor) |
| + sizeof(struct usb_bos_capability_webusb), |
| .bDescriptorType = USB_DESC_DEVICE_CAPABILITY, |
| .bDevCapabilityType = USB_BOS_CAPABILITY_PLATFORM, |
| .bReserved = 0, |
| /* WebUSB Platform Capability UUID |
| * 3408b638-09a9-47a0-8bfd-a0768815b665 |
| */ |
| .PlatformCapabilityUUID = { |
| 0x38, 0xB6, 0x08, 0x34, |
| 0xA9, 0x09, |
| 0xA0, 0x47, |
| 0x8B, 0xFD, |
| 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65, |
| }, |
| }, |
| .cap = { |
| .bcdVersion = sys_cpu_to_le16(0x0100), |
| .bVendorCode = WEBUSB_VENDOR_CODE, |
| .iLandingPage = 0x01 |
| } |
| }; |
| |
| /* URL Descriptor: https://wicg.github.io/webusb/#url-descriptor */ |
| static const uint8_t webusb_origin_url[] = { |
| /* Length, DescriptorType, Scheme */ |
| 24, 0x03, 0x01, |
| 'w', 'w', 'w', '.', |
| 'z', 'e', 'p', 'h', 'y', 'r', 'p', 'r', 'o', 'j', 'e', 'c', 't', '.', |
| 'o', 'r', 'g', '/', |
| }; |
| |
| static int msosv2_vendor_handle_req(struct usb_setup_packet *setup, |
| int32_t *len, uint8_t **data) |
| { |
| if (usb_reqtype_is_to_device(setup)) { |
| return -ENOTSUP; |
| } |
| |
| if (setup->bRequest == WEBUSB_VENDOR_CODE && setup->wIndex == 0x02) { |
| *data = (uint8_t *)(&webusb_origin_url); |
| *len = sizeof(webusb_origin_url); |
| |
| LOG_DBG("Get URL request"); |
| |
| return 0; |
| } |
| |
| if (setup->bRequest == WINUSB_VENDOR_CODE && |
| setup->wIndex == MS_OS_20_DESCRIPTOR_INDEX) { |
| *data = (uint8_t *)(&msosv2_cmsis_dap_desc); |
| *len = sizeof(msosv2_cmsis_dap_desc); |
| |
| LOG_DBG("Get MS OS Descriptors v2"); |
| |
| return 0; |
| } |
| |
| return -ENOTSUP; |
| } |
| |
| USBD_CLASS_DESCR_DEFINE(primary, 0) struct { |
| struct usb_if_descriptor if0; |
| struct usb_ep_descriptor if0_out_ep; |
| struct usb_ep_descriptor if0_in_ep; |
| } __packed dapusb_desc = { |
| .if0 = { |
| .bLength = sizeof(struct usb_if_descriptor), |
| .bDescriptorType = USB_DESC_INTERFACE, |
| .bInterfaceNumber = 0, |
| .bAlternateSetting = 0, |
| .bNumEndpoints = 2, |
| .bInterfaceClass = USB_BCC_VENDOR, |
| .bInterfaceSubClass = 0, |
| .bInterfaceProtocol = 0, |
| .iInterface = 0, |
| }, |
| .if0_out_ep = { |
| .bLength = sizeof(struct usb_ep_descriptor), |
| .bDescriptorType = USB_DESC_ENDPOINT, |
| .bEndpointAddress = DAP_USB_EP_OUT, |
| .bmAttributes = USB_DC_EP_BULK, |
| .wMaxPacketSize = sys_cpu_to_le16(CONFIG_CMSIS_DAP_PACKET_SIZE), |
| .bInterval = 0, |
| }, |
| .if0_in_ep = { |
| .bLength = sizeof(struct usb_ep_descriptor), |
| .bDescriptorType = USB_DESC_ENDPOINT, |
| .bEndpointAddress = DAP_USB_EP_IN, |
| .bmAttributes = USB_DC_EP_BULK, |
| .wMaxPacketSize = sys_cpu_to_le16(CONFIG_CMSIS_DAP_PACKET_SIZE), |
| .bInterval = 0, |
| }, |
| }; |
| |
| static struct usb_ep_cfg_data dapusb_ep_data[] = { |
| { |
| .ep_cb = usb_transfer_ep_callback, |
| .ep_addr = DAP_USB_EP_OUT |
| }, |
| { |
| .ep_cb = usb_transfer_ep_callback, |
| .ep_addr = DAP_USB_EP_IN |
| } |
| }; |
| |
| static void iface_string_desc_init(struct usb_cfg_data *bulk_cfg) |
| { |
| struct usb_if_descriptor *bulk_if = bulk_cfg->interface_descriptor; |
| |
| bulk_if->iInterface = usb_get_str_descriptor_idx(&dap_iface_desc); |
| } |
| |
| static void dapusb_read_cb(uint8_t ep, int size, void *priv) |
| { |
| struct usb_cfg_data *cfg = priv; |
| struct net_buf *buf; |
| |
| LOG_DBG("cfg %p ep %x size %u", cfg, ep, size); |
| |
| if (size <= 0) { |
| goto read_cb_done; |
| } |
| |
| buf = net_buf_alloc(&dapusb_rx_pool, K_FOREVER); |
| net_buf_add_mem(buf, rx_buf, MIN(size, CONFIG_CMSIS_DAP_PACKET_SIZE)); |
| k_fifo_put(&dap_rx_queue, buf); |
| |
| read_cb_done: |
| usb_transfer(ep, rx_buf, sizeof(rx_buf), USB_TRANS_READ, dapusb_read_cb, cfg); |
| } |
| |
| static void dapusb_dev_status_cb(struct usb_cfg_data *cfg, |
| enum usb_dc_status_code status, |
| const uint8_t *param) |
| { |
| ARG_UNUSED(param); |
| |
| if (status == USB_DC_CONFIGURED) { |
| dapusb_read_cb(cfg->endpoint[DAP_USB_EP_IN_IDX].ep_addr, 0, cfg); |
| } |
| } |
| |
| static void dapusb_interface_config(struct usb_desc_header *head, |
| uint8_t bInterfaceNumber) |
| { |
| ARG_UNUSED(head); |
| |
| dapusb_desc.if0.bInterfaceNumber = bInterfaceNumber; |
| #if defined(CONFIG_USB_COMPOSITE_DEVICE) |
| msosv2_cmsis_dap_desc.subset_header.bFirstInterface = bInterfaceNumber; |
| #endif |
| } |
| |
| USBD_DEFINE_CFG_DATA(dapusb_config) = { |
| .usb_device_description = NULL, |
| .interface_config = dapusb_interface_config, |
| .interface_descriptor = &dapusb_desc.if0, |
| .cb_usb_status = dapusb_dev_status_cb, |
| .interface = { |
| .class_handler = NULL, |
| .custom_handler = NULL, |
| .vendor_handler = msosv2_vendor_handle_req, |
| }, |
| .num_endpoints = ARRAY_SIZE(dapusb_ep_data), |
| .endpoint = dapusb_ep_data |
| }; |
| |
| static int dap_usb_process(void) |
| { |
| uint8_t ep = dapusb_config.endpoint[DAP_USB_EP_OUT_IDX].ep_addr; |
| struct net_buf *buf; |
| size_t len; |
| int err; |
| |
| buf = k_fifo_get(&dap_rx_queue, K_FOREVER); |
| |
| len = dap_execute_cmd(buf->data, tx_buf); |
| LOG_DBG("response length %u, starting with [0x%02X, 0x%02X]", |
| len, tx_buf[0], tx_buf[1]); |
| net_buf_unref(buf); |
| |
| err = usb_transfer_sync(ep, tx_buf, len, USB_TRANS_WRITE | USB_TRANS_NO_ZLP); |
| if (err < 0 || err != len) { |
| LOG_ERR("usb_transfer_sync failed, %d", err); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| int main(void) |
| { |
| const struct device *const swd_dev = DEVICE_DT_GET_ONE(zephyr_swdp_gpio); |
| int ret; |
| |
| if (!device_is_ready(swd_dev)) { |
| LOG_ERR("SWD device is not ready"); |
| return -ENODEV; |
| } |
| |
| ret = dap_setup(swd_dev); |
| if (ret) { |
| LOG_ERR("Failed to initialize DAP controller, %d", ret); |
| return ret; |
| } |
| |
| /* Add MS OS 2.0 BOS descriptor to BOS structure */ |
| usb_bos_register_cap((void *)&bos_cap_msosv2); |
| /* Point interface index to string descriptor */ |
| iface_string_desc_init(&dapusb_config); |
| |
| ret = usb_enable(NULL); |
| if (ret != 0) { |
| LOG_ERR("Failed to enable USB"); |
| return 0; |
| } |
| |
| while (!dap_usb_process()) { |
| } |
| |
| return usb_disable(); |
| } |