probe: allow for selection of CMSIS-DAP v1 and v2 protocols

CMSIS-DAP v1 is a pseudo-HID device which allows "driver-less" use on
Windows. DAP v2 is a vendor-specific bulk protocol, so there needs to be
a companion MS OS 2.0 descriptor for the debug interface with
DeviceInterfaceGUID set to the Keil GUID.

Allow for selection between these and the openocd protocol at compile time.

Parts of the v1 implementation are derived from the DapperMime project, so
add credit where due.

Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
This commit is contained in:
Jonathan Bell
2022-07-05 16:47:09 +01:00
parent 592b8da71a
commit ee4d575bf7
4 changed files with 210 additions and 12 deletions

View File

@@ -2,6 +2,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
* Copyright (c) 2021 Peter Lawrence
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -35,17 +36,27 @@
#include "cdc_uart.h"
#include "get_serial.h"
#include "led.h"
#include "DAP.h"
// UART0 for Picoprobe debug
// UART1 for picoprobe to target device
static uint8_t TxDataBuffer[CFG_TUD_HID_EP_BUFSIZE];
static uint8_t RxDataBuffer[CFG_TUD_HID_EP_BUFSIZE];
void dap_task(void);
int main(void) {
board_init();
usb_serial_init();
cdc_uart_init();
tusb_init();
#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_OPENOCD_CUSTOM)
probe_init();
#else
DAP_Setup();
#endif
led_init();
picoprobe_info("Welcome to Picoprobe!\n");
@@ -53,9 +64,87 @@ int main(void) {
while (1) {
tud_task(); // tinyusb device task
cdc_task();
#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_OPENOCD_CUSTOM)
probe_task();
led_task();
#elif (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V2)
dap_task();
#endif
}
return 0;
}
}
void dap_task(void)
{
uint32_t count;
uint32_t resp_len;
if (tud_vendor_available()) {
count = tud_vendor_read(RxDataBuffer, sizeof(RxDataBuffer));
resp_len = DAP_ProcessCommand(RxDataBuffer, TxDataBuffer);
tud_vendor_write(TxDataBuffer, resp_len);
}
}
uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen)
{
// TODO not Implemented
(void) itf;
(void) report_id;
(void) report_type;
(void) buffer;
(void) reqlen;
return 0;
}
void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const* RxDataBuffer, uint16_t bufsize)
{
uint32_t response_size = TU_MIN(CFG_TUD_HID_EP_BUFSIZE, bufsize);
// This doesn't use multiple report and report ID
(void) itf;
(void) report_id;
(void) report_type;
DAP_ProcessCommand(RxDataBuffer, TxDataBuffer);
tud_hid_report(0, TxDataBuffer, response_size);
}
#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V2)
extern uint8_t const desc_ms_os_20[];
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
{
// nothing to with DATA & ACK stage
if (stage != CONTROL_STAGE_SETUP) return true;
switch (request->bmRequestType_bit.type)
{
case TUSB_REQ_TYPE_VENDOR:
switch (request->bRequest)
{
case 1:
if ( request->wIndex == 7 )
{
// Get Microsoft OS 2.0 compatible descriptor
uint16_t total_len;
memcpy(&total_len, desc_ms_os_20+8, 2);
return tud_control_xfer(rhport, request, (void*) desc_ms_os_20, total_len);
}else
{
return false;
}
default: break;
}
break;
default: break;
}
// stall unknown request
return false;
}
#endif

View File

@@ -72,6 +72,15 @@
#define PICOPROBE_LED PICO_DEFAULT_LED_PIN
#endif
#define PROTO_OPENOCD_CUSTOM 0
#define PROTO_DAP_V1 1
#define PROTO_DAP_V2 2
// Interface config
#ifndef PICOPROBE_DEBUG_PROTOCOL
#define PICOPROBE_DEBUG_PROTOCOL PROTO_OPENOCD_CUSTOM
#endif
#endif
#endif

View File

@@ -62,7 +62,7 @@
#endif
//------------- CLASS -------------//
#define CFG_TUD_HID 0
#define CFG_TUD_HID 1
#define CFG_TUD_CDC 1
#define CFG_TUD_MSC 0
#define CFG_TUD_MIDI 0

View File

@@ -2,6 +2,8 @@
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
* Copyright (c) 2021 Peter Lawrence
* Copyright (c) 2022 Raspberry Pi Ltd
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -25,7 +27,7 @@
#include "tusb.h"
#include "get_serial.h"
#include "picoprobe_config.h"
//--------------------------------------------------------------------+
// Device Descriptors
@@ -34,14 +36,22 @@ tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0110, // // USB Specification version 1.1
#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V2)
.bcdUSB = 0x0210, // USB Specification version 2.1 for BOS
#else
.bcdUSB = 0x0110,
#endif
.bDeviceClass = 0x00, // Each interface specifies its own
.bDeviceSubClass = 0x00, // Each interface specifies its own
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0x2E8A, // Pi
#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_OPENOCD_CUSTOM)
.idProduct = 0x0004, // Picoprobe
#else
.idProduct = 0x000c, // CMSIS-DAP adapter
#endif
.bcdDevice = 0x0100, // Version 01.00
.iManufacturer = 0x01,
.iProduct = 0x02,
@@ -62,9 +72,9 @@ uint8_t const * tud_descriptor_device_cb(void)
enum
{
ITF_NUM_PROBE, // Old versions of Keil MDK only look at interface 0
ITF_NUM_CDC_COM,
ITF_NUM_CDC_DATA,
ITF_NUM_PROBE,
ITF_NUM_TOTAL
};
@@ -74,18 +84,39 @@ enum
#define PROBE_OUT_EP_NUM 0x04
#define PROBE_IN_EP_NUM 0x85
#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V1)
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_HID_INOUT_DESC_LEN)
#else
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_VENDOR_DESC_LEN)
#endif
static uint8_t const desc_hid_report[] =
{
TUD_HID_REPORT_DESC_GENERIC_INOUT(CFG_TUD_HID_EP_BUFSIZE)
};
uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf)
{
(void) itf;
return desc_hid_report;
}
uint8_t const desc_configuration[] =
{
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
// Interface 0 + 1
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_COM, 0, CDC_NOTIFICATION_EP_NUM, 64, CDC_DATA_OUT_EP_NUM, CDC_DATA_IN_EP_NUM, 64),
// Interface 2
TUD_VENDOR_DESCRIPTOR(ITF_NUM_PROBE, 0, PROBE_OUT_EP_NUM, PROBE_IN_EP_NUM, 64)
// Interface 0
#if (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V1)
// HID (named interface)
TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_PROBE, 4, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), PROBE_OUT_EP_NUM, PROBE_IN_EP_NUM, CFG_TUD_HID_EP_BUFSIZE, 1),
#elif (PICOPROBE_DEBUG_PROTOCOL == PROTO_DAP_V2)
// Bulk (named interface)
TUD_VENDOR_DESCRIPTOR(ITF_NUM_PROBE, 5, PROBE_OUT_EP_NUM, PROBE_IN_EP_NUM, 64),
#elif (PICOPROBE_DEBUG_PROTOCOL == PROTO_OPENOCD_CUSTOM)
// Bulk
TUD_VENDOR_DESCRIPTOR(ITF_NUM_PROBE, 0, PROBE_OUT_EP_NUM, PROBE_IN_EP_NUM, 64),
#endif
// Interface 1 + 2
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_COM, 6, CDC_NOTIFICATION_EP_NUM, 64, CDC_DATA_OUT_EP_NUM, CDC_DATA_IN_EP_NUM, 64),
};
// Invoked when received GET CONFIGURATION DESCRIPTOR
@@ -108,6 +139,9 @@ char const* string_desc_arr [] =
"Raspberry Pi", // 1: Manufacturer
"Picoprobe", // 2: Product
usb_serial, // 3: Serial, uses flash unique ID
"Picoprobe CMSIS-DAP v1", // Interface descriptor for HID transport
"Picoprobe CMSIS-DAP v2", // Interface descriptor for Bulk transport
"Picoprobe CDC-ACM UART", // Interface descriptor for CDC
};
static uint16_t _desc_str[32];
@@ -147,3 +181,69 @@ uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
return _desc_str;
}
/* [incoherent gibbering to make Windows happy] */
//--------------------------------------------------------------------+
// BOS Descriptor
//--------------------------------------------------------------------+
/* Microsoft OS 2.0 registry property descriptor
Per MS requirements https://msdn.microsoft.com/en-us/library/windows/hardware/hh450799(v=vs.85).aspx
device should create DeviceInterfaceGUIDs. It can be done by driver and
in case of real PnP solution device should expose MS "Microsoft OS 2.0
registry property descriptor". Such descriptor can insert any record
into Windows registry per device/configuration/interface. In our case it
will insert "DeviceInterfaceGUIDs" multistring property.
https://developers.google.com/web/fundamentals/native-hardware/build-for-webusb/
(Section Microsoft OS compatibility descriptors)
*/
#define MS_OS_20_DESC_LEN 0xB2
#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
uint8_t const desc_bos[] =
{
// total length, number of device caps
TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 1),
// Microsoft OS 2.0 descriptor
TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, 1)
};
uint8_t const desc_ms_os_20[] =
{
// Set header: length, type, windows version, total length
U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN),
// Configuration subset header: length, type, configuration index, reserved, configuration total length
U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_CONFIGURATION), 0, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A),
// Function Subset header: length, type, first interface, reserved, subset length
U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), ITF_NUM_PROBE, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08),
// MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID
U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sub-compatible
// MS OS 2.0 Registry property descriptor: length, type
U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08-0x08-0x14), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY),
U16_TO_U8S_LE(0x0007), U16_TO_U8S_LE(0x002A), // wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16
'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,
U16_TO_U8S_LE(0x0050), // wPropertyDataLength
// bPropertyData "{CDB3B5AD-293B-4663-AA36-1AAE46463776}" as a UTF-16 string (b doesn't mean bytes)
'{', 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
};
TU_VERIFY_STATIC(sizeof(desc_ms_os_20) == MS_OS_20_DESC_LEN, "Incorrect size");
uint8_t const * tud_descriptor_bos_cb(void)
{
return desc_bos;
}