diff --git a/samples/subsys/dap/CMakeLists.txt b/samples/subsys/dap/CMakeLists.txt index 72ea1953b3c1841..fbac52a732ef885 100644 --- a/samples/subsys/dap/CMakeLists.txt +++ b/samples/subsys/dap/CMakeLists.txt @@ -1,9 +1,10 @@ -# Copyright (c) 2023 Nordic Semiconductor ASA # SPDX-License-Identifier: Apache-2.0 cmake_minimum_required(VERSION 3.20.0) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) -project(dap) +project(webusb) + +target_include_directories(app PRIVATE ${ZEPHYR_BASE}/subsys/usb) FILE(GLOB app_sources src/*.c) target_sources(app PRIVATE ${app_sources}) diff --git a/samples/subsys/dap/README.rst b/samples/subsys/dap/README.rst index a528a6dff2b9da1..a78d1711e8b49ba 100644 --- a/samples/subsys/dap/README.rst +++ b/samples/subsys/dap/README.rst @@ -7,14 +7,20 @@ Overview ******** This sample app demonstrates use of a SWDP interface driver and CMSIS DAP -controller through USB HID interface. +controller through USB Bulk interface. Requirements ************ -This sample requires simple circuit consisting of a few Dual-Supply Bus -Transceivers connected to Arduino GPIOs, look for ARM microcontroller-based -Hardware Interface Circuits (HICs) for detailed information. +This sample supports multiple hardware configurations: + +The simplest configuration would be to connect `SWDIO` to `dio`, `SWDCLK` to `clk` +and optionally `nRESET` to `reset`. +The optional `noe` pin is used to enable the port, e.g. if the SWD connections are multiplexed. + +If you are using bus transceivers and require separate +input and output pins along with an output-enable pin, set `CONFIG_SWDP_BITBANG_DRIVER_SEPARATE_IN_OUT=y`. +In this case the `din`, `dout` and `dnoe` pins are used. Building and Running ******************** diff --git a/samples/subsys/dap/boards/nrf52840dk_nrf52840.conf b/samples/subsys/dap/boards/nrf52840dk_nrf52840.conf new file mode 100644 index 000000000000000..a3bb5d2614e9fdd --- /dev/null +++ b/samples/subsys/dap/boards/nrf52840dk_nrf52840.conf @@ -0,0 +1,4 @@ +CONFIG_SWDP_BITBANG_DRIVER_SEPARATE_IN_OUT=n + +CONFIG_CMSIS_DAP_DEVICE_VENDOR="Raspberry Pi" +CONFIG_CMSIS_DAP_DEVICE_NAME="rp2040" diff --git a/samples/subsys/dap/boards/nrf52840dk_nrf52840.overlay b/samples/subsys/dap/boards/nrf52840dk_nrf52840.overlay new file mode 100644 index 000000000000000..9289215f27b9c17 --- /dev/null +++ b/samples/subsys/dap/boards/nrf52840dk_nrf52840.overlay @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + + / { + dp0 { + compatible = "zephyr,swdp-gpio"; + status = "okay"; + clk-gpios = <&arduino_header 10 GPIO_ACTIVE_HIGH>; /* D4 */ + dout-gpios = <&arduino_header 9 GPIO_ACTIVE_HIGH>; /* D3 */ + din-gpios = <&arduino_header 8 GPIO_PULL_UP>; /* D2 */ + dio-gpios = <&arduino_header 8 GPIO_PULL_UP>; /* D2 */ + dnoe-gpios = <&arduino_header 12 GPIO_ACTIVE_HIGH>; /* D6 */ + noe-gpios = <&arduino_header 11 GPIO_ACTIVE_HIGH>; /* D5 */ + reset-gpios = <&arduino_header 13 GPIO_ACTIVE_HIGH>; /* D7 */ + port-write-cycles = <2>; + }; +}; + +&zephyr_udc0 { + cdc_acm_uart0 { + compatible = "zephyr,cdc-acm-uart"; + }; +}; diff --git a/samples/subsys/dap/boards/nrf7002dk_nrf5340_cpuapp.conf b/samples/subsys/dap/boards/nrf7002dk_nrf5340_cpuapp.conf new file mode 100644 index 000000000000000..0c3cc6af78c6ada --- /dev/null +++ b/samples/subsys/dap/boards/nrf7002dk_nrf5340_cpuapp.conf @@ -0,0 +1,3 @@ +CONFIG_SWDP_BITBANG_DRIVER_SEPARATE_IN_OUT=n +CONFIG_CMSIS_DAP_DEVICE_VENDOR="Nordic Semiconductor" +CONFIG_CMSIS_DAP_DEVICE_NAME="nrf9160_xxaa" diff --git a/samples/subsys/dap/app.overlay b/samples/subsys/dap/boards/nrf7002dk_nrf5340_cpuapp.overlay similarity index 89% rename from samples/subsys/dap/app.overlay rename to samples/subsys/dap/boards/nrf7002dk_nrf5340_cpuapp.overlay index ac6a4df3464170a..c180fbdbfee3a55 100644 --- a/samples/subsys/dap/app.overlay +++ b/samples/subsys/dap/boards/nrf7002dk_nrf5340_cpuapp.overlay @@ -4,13 +4,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -/ { + / { dp0 { compatible = "zephyr,swdp-gpio"; status = "okay"; clk-gpios = <&arduino_header 10 GPIO_ACTIVE_HIGH>; /* D4 */ dout-gpios = <&arduino_header 9 GPIO_ACTIVE_HIGH>; /* D3 */ din-gpios = <&arduino_header 8 GPIO_PULL_UP>; /* D2 */ + dio-gpios = <&arduino_header 8 GPIO_PULL_UP>; /* D2 */ dnoe-gpios = <&arduino_header 12 GPIO_ACTIVE_HIGH>; /* D6 */ noe-gpios = <&arduino_header 11 GPIO_ACTIVE_HIGH>; /* D5 */ reset-gpios = <&arduino_header 13 GPIO_ACTIVE_HIGH>; /* D7 */ diff --git a/samples/subsys/dap/boards/thingy91_nrf52840.conf b/samples/subsys/dap/boards/thingy91_nrf52840.conf new file mode 100644 index 000000000000000..302fe41a3cfc4c8 --- /dev/null +++ b/samples/subsys/dap/boards/thingy91_nrf52840.conf @@ -0,0 +1,5 @@ +CONFIG_USB_DEVICE_STACK=y +CONFIG_USB_DEVICE_REMOTE_WAKEUP=n +CONFIG_USB_MAX_POWER=250 +CONFIG_CMSIS_DAP_DEVICE_VENDOR="Nordic Semiconductor" +CONFIG_CMSIS_DAP_DEVICE_NAME="nrf9160_xxaa" diff --git a/samples/subsys/dap/boards/thingy91_nrf52840.overlay b/samples/subsys/dap/boards/thingy91_nrf52840.overlay new file mode 100644 index 000000000000000..7569226daa8041c --- /dev/null +++ b/samples/subsys/dap/boards/thingy91_nrf52840.overlay @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +/ { + dp0 { + compatible = "zephyr,swdp-gpio"; + status = "okay"; + clk-gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>; + dio-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>; + noe-gpios = <&gpio0 17 GPIO_ACTIVE_HIGH>; + port-write-cycles = <2>; + }; +}; diff --git a/samples/subsys/dap/prj.conf b/samples/subsys/dap/prj.conf index 406a57125e7b915..39d640fccf4d21c 100644 --- a/samples/subsys/dap/prj.conf +++ b/samples/subsys/dap/prj.conf @@ -1,19 +1,21 @@ +CONFIG_STDOUT_CONSOLE=y CONFIG_USB_DEVICE_STACK=y +CONFIG_USB_DEVICE_BOS=y +CONFIG_SERIAL=y CONFIG_USB_DEVICE_PRODUCT="Zephyr CMSIS-DAP" +CONFIG_USB_DEVICE_PID=0x0204 +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_UART_LINE_CTRL=y CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=n -CONFIG_USB_DEVICE_HID=y -CONFIG_ENABLE_HID_INT_OUT_EP=y -CONFIG_HID_INTERRUPT_EP_MPS=64 -CONFIG_USB_HID_POLL_INTERVAL_MS=1 +CONFIG_LOG=y -# Change to VID/PID from a known HIC or it will not be recognized. -CONFIG_USB_DEVICE_VID=0x2fe3 -CONFIG_USB_DEVICE_PID=0x0204 +CONFIG_USB_COMPOSITE_DEVICE=y -CONFIG_DAP=y -CONFIG_NET_BUF=y - -CONFIG_LOG=y +CONFIG_USB_DRIVER_LOG_LEVEL_INF=y +CONFIG_USB_DEVICE_LOG_LEVEL_INF=y CONFIG_DAP_LOG_LEVEL_INF=y CONFIG_DP_DRIVER_LOG_LEVEL_INF=y +CONFIG_DAP=y +CONFIG_GPIO=y +CONFIG_NET_BUF=y diff --git a/samples/subsys/dap/sample.yaml b/samples/subsys/dap/sample.yaml index 7aa5536a95285d9..f74a21d05104ad1 100644 --- a/samples/subsys/dap/sample.yaml +++ b/samples/subsys/dap/sample.yaml @@ -1,5 +1,5 @@ sample: - name: DAP HID + name: DAP USB tests: sample.dap.hid: build_only: true diff --git a/samples/subsys/dap/src/cmsis_dap_usb.c b/samples/subsys/dap/src/cmsis_dap_usb.c new file mode 100644 index 000000000000000..f757887d550b459 --- /dev/null +++ b/samples/subsys/dap/src/cmsis_dap_usb.c @@ -0,0 +1,409 @@ + +#include +#include +#include +#include +#include +#include +#include + +#include "cmsis_dap_usb.h" +#include "msosv2_defs.h" + +uint8_t rx_buf[CONFIG_CMSIS_DAP_PACKET_SIZE]; +uint8_t tx_buf[CONFIG_CMSIS_DAP_PACKET_SIZE]; + +NET_BUF_POOL_FIXED_DEFINE(ep_out_pool, CONFIG_CMSIS_DAP_PACKET_COUNT, + CONFIG_CMSIS_DAP_PACKET_SIZE, 0, NULL); + +K_FIFO_DEFINE(ep_out_queue); + +static struct usb_cfg_data *cfg = NULL; + +#define WEBUSB_VENDOR_CODE 0x21 +#define WINUSB_VENDOR_CODE 0x20 + +#define BULK_INTF_STRDESC "CMSIS-DAP v2" + +#define LOG_LEVEL CONFIG_DAP_LOG_LEVEL +#include +LOG_MODULE_REGISTER(cmsis_dap); + +/* {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_t { + struct ms_os_20_descriptor_set_header_descriptor header; +#if defined(CONFIG_USB_COMPOSITE_DEVICE) + struct ms_os_20_function_subset_header_descriptor cmsis_dap_v2_subset_header; +#endif + struct ms_os_20_compatible_id_descriptor cmsis_dap_v2_compatible_id; + struct ms_os_20_guids_property_descriptor cmsis_dap_v2_guids_property; +} __packed msosv2_descriptor = { + /* 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 ms_os_20_descriptor_set_header_descriptor), + .wDescriptorType = MS_OS_20_SET_HEADER_DESCRIPTOR, + .dwWindowsVersion = 0x06030000, + .wTotalLength = sizeof(struct msosv2_descriptor_t), + }, +#if defined(CONFIG_USB_COMPOSITE_DEVICE) + .cmsis_dap_v2_subset_header = { + .wLength = sizeof(struct ms_os_20_function_subset_header_descriptor), + .wDescriptorType = MS_OS_20_SUBSET_HEADER_FUNCTION, + .wSubsetLength = sizeof(struct ms_os_20_function_subset_header_descriptor) + + sizeof(struct ms_os_20_compatible_id_descriptor) + + sizeof(struct ms_os_20_guids_property_descriptor), + }, +#endif + .cmsis_dap_v2_compatible_id = { + .wLength = sizeof(struct ms_os_20_compatible_id_descriptor), + .wDescriptorType = MS_OS_20_FEATURE_COMPATIBLE_ID, + .CompatibleID = {COMPATIBLE_ID_WINUSB}, + }, + .cmsis_dap_v2_guids_property = { + .wLength = sizeof(struct ms_os_20_guids_property_descriptor), + .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 + * See https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/ + * microsoft-defined-usb-descriptors + * Adapted from the source: + * https://github.com/sowbug/weblight/blob/master/firmware/webusb.c + * (BSD-2) Thanks http://janaxelson.com/files/ms_os_20_descriptors.c + */ + .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_descriptor)), + .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 */ + 0x0F, 0x03, 0x01, + 'o', 's', '.', 'm', 'b', 'e', 'd', '.', 'c', 'o', 'm', '/', +}; + + +/** + * @brief Handler called for vendor specific commands. This includes + * WebUSB GET_URL and MS OS 2.0 descriptors. + * + * @param pSetup Information about the request to execute. + * @param len Size of the buffer. + * @param data Buffer containing the request result. + * + * @return 0 on success, negative errno code on fail. + */ +static int msosv2_vendor_handle_req(struct usb_setup_packet *pSetup, int32_t *len, uint8_t **data) +{ + if (usb_reqtype_is_to_device(pSetup)) { + return -ENOTSUP; + } + + if (pSetup->bRequest == WEBUSB_VENDOR_CODE && pSetup->wIndex == 0x02) { + /* WebUSB: Get URL request */ + *data = (uint8_t *)(&webusb_origin_url); + *len = sizeof(webusb_origin_url); + + LOG_DBG("Get webusb_origin_url"); + + return 0; + } else if (pSetup->bRequest == WINUSB_VENDOR_CODE && pSetup->wIndex == 0x07) { + /* Get MS OS 2.0 Descriptors request */ + /* 0x07 means "MS_OS_20_DESCRIPTOR_INDEX" */ + *data = (uint8_t *)(&msosv2_descriptor); + *len = sizeof(msosv2_descriptor); + + LOG_DBG("Get MS OS Descriptors v2"); + + return 0; + } + return -ENOTSUP; +} + +void bulk_read_cb(uint8_t ep, int size, void *priv) +{ + struct net_buf *buf; + cfg = priv; + + LOG_DBG("cfg %p ep %x size %u", cfg, ep, size); + + if (size <= 0) { + goto done; + } + + buf = net_buf_alloc(&ep_out_pool, K_FOREVER); + memcpy(buf->data, rx_buf, CONFIG_CMSIS_DAP_PACKET_SIZE); + net_buf_add(buf, size); + k_fifo_put(&ep_out_queue, buf); +done: + /* keep on reading */ + usb_transfer(ep, rx_buf, sizeof(rx_buf), USB_TRANS_READ, + bulk_read_cb, cfg); +} + +#define INITIALIZER_IF(num_ep, iface_class) \ + { \ + .bLength = sizeof(struct usb_if_descriptor), \ + .bDescriptorType = USB_DESC_INTERFACE, \ + .bInterfaceNumber = 0, \ + .bAlternateSetting = 0, \ + .bNumEndpoints = num_ep, \ + .bInterfaceClass = iface_class, \ + .bInterfaceSubClass = 0, \ + .bInterfaceProtocol = 0, \ + .iInterface = 0, \ + } + +#define INITIALIZER_IF_EP(addr, attr, mps, interval) \ + { \ + .bLength = sizeof(struct usb_ep_descriptor), \ + .bDescriptorType = USB_DESC_ENDPOINT, \ + .bEndpointAddress = addr, \ + .bmAttributes = attr, \ + .wMaxPacketSize = sys_cpu_to_le16(mps), \ + .bInterval = interval, \ + } + +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 = INITIALIZER_IF(2, USB_BCC_VENDOR), + .if0_out_ep = INITIALIZER_IF_EP(AUTO_EP_OUT, USB_DC_EP_BULK, + CONFIG_CMSIS_DAP_PACKET_SIZE, 0), + .if0_in_ep = INITIALIZER_IF_EP(AUTO_EP_IN, USB_DC_EP_BULK, + CONFIG_CMSIS_DAP_PACKET_SIZE, 0), +}; + +/* Describe EndPoints configuration */ +static struct usb_ep_cfg_data dapusb_ep_data[] = { + { + .ep_cb = usb_transfer_ep_callback, + .ep_addr = AUTO_EP_OUT + }, + { + .ep_cb = usb_transfer_ep_callback, + .ep_addr = AUTO_EP_IN + } +}; + +#define BULK_OUT_EP_IDX 0 +#define BULK_IN_EP_IDX 1 + +static bool usb_bulk_ready; + +static void dapusb_dev_status_cb(struct usb_cfg_data *cfg, + enum usb_dc_status_code status, + const uint8_t *param) +{ + ARG_UNUSED(param); + ARG_UNUSED(cfg); + + /* Check the USB status and do needed action if required */ + switch (status) { + case USB_DC_ERROR: + LOG_DBG("USB device error"); + break; + case USB_DC_RESET: + LOG_DBG("USB device reset detected"); + break; + case USB_DC_CONNECTED: + LOG_DBG("USB device connected"); + break; + case USB_DC_CONFIGURED: + LOG_DBG("USB device configured"); + bulk_read_cb(cfg->endpoint[BULK_OUT_EP_IDX].ep_addr, + 0, cfg); + break; + case USB_DC_DISCONNECTED: + LOG_DBG("USB device disconnected"); + break; + case USB_DC_SUSPEND: + LOG_DBG("USB device suspended"); + break; + case USB_DC_RESUME: + LOG_DBG("USB device resumed"); + break; + case USB_DC_UNKNOWN: + default: + LOG_DBG("USB unknown state"); + break; + } +} + +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_descriptor.cmsis_dap_v2_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 +}; + +struct bulk_iface_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bString[USB_BSTRING_LENGTH(BULK_INTF_STRDESC)]; +} __packed; + +USBD_STRING_DESCR_USER_DEFINE(primary) struct bulk_iface_descriptor bulk_string_descr = { + .bLength = USB_STRING_DESCRIPTOR_LENGTH(BULK_INTF_STRDESC), + .bDescriptorType = USB_DESC_STRING, + .bString = BULK_INTF_STRDESC +}; + +/** + * @brief Set string descriptor index for bulk interface + * + * @param bulk_cfg USB config struct of bulk interface + */ +static void bulk_str_descr_init(struct usb_cfg_data *bulk_cfg) +{ + int idx = usb_get_str_descriptor_idx(&bulk_string_descr); + if (idx != 0) { + struct usb_if_descriptor *bulk_if = bulk_cfg->interface_descriptor; + bulk_if->iInterface = idx; + } +} + +int cmsis_dap_usb_init(const struct device *swd_dev) +{ + int 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 */ + bulk_str_descr_init(&dapusb_config); + + /* set up the underlaying DAP device that will process the commands */ + 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; + } + + return 0; +} + +void cmsis_dap_usb_process(k_timeout_t timeout) +{ + struct net_buf *buf; + size_t len; + int ret; + + buf = k_fifo_get(&ep_out_queue, timeout); + + 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); + +send: + ret = usb_transfer_sync(cfg->endpoint[BULK_IN_EP_IDX].ep_addr, tx_buf, len, + USB_TRANS_WRITE); + if (ret == -EAGAIN) { + LOG_WRN("got EAGAIN, retransmitting"); + k_sleep(K_MSEC(1)); + goto send; + } else if (ret != len) { + LOG_ERR("usb_transfer_sync failed: %d", ret); + } +} diff --git a/samples/subsys/dap/src/cmsis_dap_usb.h b/samples/subsys/dap/src/cmsis_dap_usb.h new file mode 100644 index 000000000000000..21af68921b0abf4 --- /dev/null +++ b/samples/subsys/dap/src/cmsis_dap_usb.h @@ -0,0 +1,19 @@ +#include +#include + +/** + * @brief Initialize the CMSIS-DAP probe including USB setup. + * + * @param swd_dev underlying SWD device + * @return int 0 on success, negative number on error + */ +int cmsis_dap_usb_init(const struct device *swd_dev); + +/** + * @brief Process DAP commands. + * + * This needs to be called continuously, but is exported so you can choose where. + * + * @param timeout + */ +void cmsis_dap_usb_process(k_timeout_t timeout); diff --git a/samples/subsys/dap/src/main.c b/samples/subsys/dap/src/main.c index 93be53f74f1b31d..c90f0bec71620e4 100644 --- a/samples/subsys/dap/src/main.c +++ b/samples/subsys/dap/src/main.c @@ -4,140 +4,45 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include -#include -#include -#include -#include -#include - -#include -LOG_MODULE_REGISTER(dap_hid, LOG_LEVEL_INF); - -#include - -#define DAP_PACKET_SIZE CONFIG_HID_INTERRUPT_EP_MPS - -#if (DAP_PACKET_SIZE < 64U) -#error "Minimum packet size is 64" -#endif -#if (DAP_PACKET_SIZE > 32768U) -#error "Maximum packet size is 32768" -#endif - -const static struct device *hid0_dev; - -NET_BUF_POOL_FIXED_DEFINE(ep_out_pool, CONFIG_CMSIS_DAP_PACKET_COUNT, - DAP_PACKET_SIZE, 0, NULL); - -K_SEM_DEFINE(hid_epin_sem, 0, 1); -K_FIFO_DEFINE(ep_out_queue); - -static const uint8_t hid_report_desc[] = { - HID_ITEM(HID_ITEM_TAG_USAGE_PAGE, HID_ITEM_TYPE_GLOBAL, 2), 0x00, 0xFF, - HID_USAGE(HID_USAGE_GEN_DESKTOP_POINTER), - HID_COLLECTION(HID_COLLECTION_APPLICATION), - HID_LOGICAL_MIN8(0x00), - HID_LOGICAL_MAX16(0xFF, 0x00), - HID_REPORT_SIZE(8), - HID_REPORT_COUNT(64), - HID_USAGE(HID_USAGE_GEN_DESKTOP_POINTER), - HID_INPUT(0x02), - HID_REPORT_COUNT(64), - HID_USAGE(HID_USAGE_GEN_DESKTOP_POINTER), - HID_OUTPUT(0x02), - HID_REPORT_COUNT(0x01), - HID_USAGE(HID_USAGE_GEN_DESKTOP_POINTER), - HID_FEATURE(0x02), - HID_END_COLLECTION, -}; - -static void int_in_ready_cb(const struct device *dev) -{ - ARG_UNUSED(dev); - - k_sem_give(&hid_epin_sem); -} - -void int_out_ready_cb(const struct device *dev) -{ - struct net_buf *buf; - size_t len = 0; +/** + * @file + * @brief Sample app for implementing a CMSIS-DAP debug probe. + * + * Sample app for implementing a CMSIS-DAP debug probe. + * This should be usable by systems that support CMSIS-DAP probes. + * It includes the necessary Windows USB descriptors to be installed automatically on + * Windows 8.1 and newer. + */ - buf = net_buf_alloc(&ep_out_pool, K_FOREVER); - hid_int_ep_read(dev, buf->data, buf->size, &len); - net_buf_add(buf, len); - if (len == 0) { - LOG_WRN("drop empty packet"); - net_buf_unref(buf); - return; - } +#include - k_fifo_put(&ep_out_queue, buf); -} +#include "cmsis_dap_usb.h" -static const struct hid_ops ops = { - .int_in_ready = int_in_ready_cb, - .int_out_ready = int_out_ready_cb, -}; +#include +LOG_MODULE_REGISTER(main); int main(void) { - const struct device *const swd_dev = DEVICE_DT_GET_ONE(zephyr_swdp_gpio); - uint8_t response_buf[DAP_PACKET_SIZE]; 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; + const struct device *const swd_dev = DEVICE_DT_GET_ONE(zephyr_swdp_gpio); + + ret = cmsis_dap_usb_init(swd_dev); + if (ret != 0) { + LOG_ERR("Failed to init CMSIS-DAP"); + return 0; } ret = usb_enable(NULL); - if (ret) { - LOG_ERR("Failed to enable USB (%d)", ret); - return ret; + if (ret != 0) { + LOG_ERR("Failed to enable USB"); + return 0; } - while (1) { - struct net_buf *buf; - size_t len; - - buf = k_fifo_get(&ep_out_queue, K_FOREVER); - - len = dap_execute_cmd(buf->data, response_buf); - LOG_DBG("response length %u", len); - net_buf_unref(buf); - - if (hid_int_ep_write(hid0_dev, response_buf, len, NULL)) { - LOG_ERR("Failed to send a response"); - continue; - } - - k_sem_take(&hid_epin_sem, K_FOREVER); + while (true) + { + cmsis_dap_usb_process(K_FOREVER); } - return 0; } - -static int hid_dap_preinit(void) -{ - hid0_dev = device_get_binding("HID_0"); - if (hid0_dev == NULL) { - LOG_ERR("Cannot get HID_0"); - return -ENODEV; - } - - usb_hid_register_device(hid0_dev, hid_report_desc, - sizeof(hid_report_desc), &ops); - - return usb_hid_init(hid0_dev); -} - -SYS_INIT(hid_dap_preinit, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); diff --git a/samples/subsys/dap/src/msosv2_defs.h b/samples/subsys/dap/src/msosv2_defs.h new file mode 100644 index 000000000000000..c5550fd7ba47c8f --- /dev/null +++ b/samples/subsys/dap/src/msosv2_defs.h @@ -0,0 +1,71 @@ +#include + +enum ms_os_20_type_t +{ + MS_OS_20_SET_HEADER_DESCRIPTOR = 0x00, + MS_OS_20_SUBSET_HEADER_CONFIGURATION = 0x01, + MS_OS_20_SUBSET_HEADER_FUNCTION = 0x02, + MS_OS_20_FEATURE_COMPATIBLE_ID = 0x03, + MS_OS_20_FEATURE_REG_PROPERTY = 0x04, + MS_OS_20_FEATURE_MIN_RESUME_TIME = 0x05, + MS_OS_20_FEATURE_MODEL_ID = 0x06, + MS_OS_20_FEATURE_CCGP_DEVICE = 0x07, + MS_OS_20_FEATURE_VENDOR_REVISION = 0x08 +}; + +enum ms_os_20_property_data_type_t +{ + MS_OS_20_PROPERTY_DATA_RESERVED = 0, + MS_OS_20_PROPERTY_DATA_REG_SZ = 1, + MS_OS_20_PROPERTY_DATA_REG_EXPAND_SZ = 2, + MS_OS_20_PROPERTY_DATA_REG_BINARY = 3, + MS_OS_20_PROPERTY_DATA_REG_DWORD_LITTLE_ENDIAN = 4, + MS_OS_20_PROPERTY_DATA_REG_DWORD_BIG_ENDIAN = 5, + MS_OS_20_PROPERTY_DATA_REG_LINK = 6, + MS_OS_20_PROPERTY_DATA_REG_MULTI_SZ = 7 +}; + +/* Microsoft OS 2.0 descriptor set header */ +struct ms_os_20_descriptor_set_header_descriptor { + uint16_t wLength; /* 10 */ + uint16_t wDescriptorType; /* MS_OS_20_SET_HEADER_DESCRIPTOR */ + uint32_t dwWindowsVersion; /* Windows 8.1: 0x06030000 */ + uint16_t wTotalLength; /* The size of entire MS OS 2.0 descriptor set. */ +} __packed; + +/* Microsoft OS 2.0 function subset header + * Note: This must be used if your device has multiple interfaces and cannot be used otherwise. + */ +struct ms_os_20_function_subset_header_descriptor { + uint16_t wLength; /* 8 */ + uint16_t wDescriptorType; /* MS_OS_20_SUBSET_HEADER_FUNCTION */ + uint8_t bFirstInterface; /* The interface number for the first interface of the function to which this subset applies. */ + uint8_t bReserved; /* 0 */ + uint16_t wSubsetLength; /* The size of entire function subset including this header.*/ +} __packed; + +/* Microsoft OS 2.0 compatible ID descriptor */ +struct ms_os_20_compatible_id_descriptor { + uint16_t wLength; /* 20 */ + uint16_t wDescriptorType; /* MS_OS_20_FEATURE_COMPATIBLE_ID */ + uint8_t CompatibleID[8]; /* Compatible ID String */ + uint8_t SubCompatibleID[8]; /* Sub-compatible ID String */ +} __packed; + +/* Microsoft OS 2.0 Registry property descriptor: DeviceInterfaceGUIDs */ +struct ms_os_20_guids_property_descriptor { + uint16_t wLength; /* The length of this descriptor in bytes. */ + uint16_t wDescriptorType; /* MS_OS_20_FEATURE_REG_PROPERTY */ + uint16_t wPropertyDataType; /* MS_OS_20_PROPERTY_DATA_REG_MULTI_SZ */ + uint16_t wPropertyNameLength; /* 42 */ + uint8_t PropertyName[42]; /* "DeviceInterfaceGUIDs\0" in UTF-16 */ + uint16_t wPropertyDataLength; /* 80 */ + uint8_t bPropertyData[80]; /* “{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\0” */ +} __packed; + +/* DeviceInterfaceGUIDs */ +#define DEVICE_INTERFACE_GUIDS_PROPERTY_NAME \ + 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, \ + 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00, \ + 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, \ + 'D', 0x00, 's', 0x00, 0x00, 0x00