From c983cef053f4e64f5c8311dba541b35fa9af0d47 Mon Sep 17 00:00:00 2001 From: Alex Taradov Date: Mon, 15 Jan 2024 17:11:21 -0800 Subject: [PATCH] Added CMSIS-DAP v2 support on Windows --- Makefile | 2 +- dbg.h | 2 + dbg_lin.c | 6 + dbg_win.c | 406 +++++++++++++++++++++++++++++++++++++++++------------- edbg.c | 16 ++- 5 files changed, 331 insertions(+), 101 deletions(-) diff --git a/Makefile b/Makefile index 0299861..54123ea 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ else else BIN = edbg.exe SRCS += dbg_win.c - LIBS += -lhid -lsetupapi + LIBS += -lhid -lwinusb -lsetupapi endif endif diff --git a/dbg.h b/dbg.h index 28aa9b9..248cfb5 100644 --- a/dbg.h +++ b/dbg.h @@ -29,11 +29,13 @@ typedef struct int versions; bool use_v2; + char *v1_path; int v1_interface; int v1_ep_size; int v1_tx_ep; int v1_rx_ep; + char *v2_path; int v2_interface; int v2_ep_size; int v2_tx_ep; diff --git a/dbg_lin.c b/dbg_lin.c index 61a5185..f2b7621 100644 --- a/dbg_lin.c +++ b/dbg_lin.c @@ -131,6 +131,9 @@ static void parse_descriptors(debugger_t *debugger, int fd, uint8_t *desc, int s if (USB_CLASS_HID == interface->bInterfaceClass) { + if (!usb_endpoint_xfer_int(ep0) || !usb_endpoint_xfer_int(ep1)) + continue; + if (usb_endpoint_dir_in(ep0)) { debugger->v1_tx_ep = ep1->bEndpointAddress; @@ -150,6 +153,9 @@ static void parse_descriptors(debugger_t *debugger, int fd, uint8_t *desc, int s if (USB_CLASS_VENDOR_SPEC == interface->bInterfaceClass) { + if (!usb_endpoint_xfer_bulk(ep0) || !usb_endpoint_xfer_bulk(ep1)) + continue; + if (usb_endpoint_dir_in(ep0)) continue; diff --git a/dbg_win.c b/dbg_win.c index cd05ae5..2327576 100644 --- a/dbg_win.c +++ b/dbg_win.c @@ -1,182 +1,392 @@ // SPDX-License-Identifier: BSD-3-Clause -// Copyright (c) 2013-2022, Alex Taradov . All rights reserved. +// Copyright (c) 2013-2024, Alex Taradov . All rights reserved. /*- Includes ----------------------------------------------------------------*/ #include #include #include #include -// NOTE: Different versions of MinGW seem to place those files in differnet -// locations. If you see build errors here, then use includes with -// 'ddk/' prefix. The no-prefix version seems to be the best moving -// forward, so consider updating your version of MinGW. MinGW-W64 -// appears to have most recent vestions of the tools, and that's -// what I'm currently using. -#if 0 -#include -#include -#else +#include +#include #include #include -#endif #include "dbg.h" #include "edbg.h" +#undef interface + /*- Definitions -------------------------------------------------------------*/ -#define MAX_STRING_SIZE 256 +DEFINE_GUID(GUID_CMSIS_DAP_V2, 0xcdb3b5adL, 0x293b, 0x4663, 0xaa, 0x36, 0x1a, 0xae, 0x46, 0x46, 0x37, 0x76); + +#define MAX_STRING_SIZE 256 +#define LANGID_US_ENGLISH 0x0409 /*- Variables ---------------------------------------------------------------*/ -static HANDLE debugger_handle = INVALID_HANDLE_VALUE; -static uint8_t hid_buffer[DBG_MAX_EP_SIZE + 1]; -static int report_size = 0; +static debugger_t *g_debugger; +static HANDLE g_handle = INVALID_HANDLE_VALUE; +static WINUSB_INTERFACE_HANDLE g_winusb_handle = INVALID_HANDLE_VALUE; /*- Implementations ---------------------------------------------------------*/ //----------------------------------------------------------------------------- -int dbg_enumerate(debugger_t *debuggers, int size) +static void get_device_descriptor(WINUSB_INTERFACE_HANDLE handle, USB_DEVICE_DESCRIPTOR *desc) { - GUID hid_guid; - HDEVINFO hid_dev_info; - HIDD_ATTRIBUTES hid_attr; - SP_DEVICE_INTERFACE_DATA dev_info_data; - PSP_DEVICE_INTERFACE_DETAIL_DATA detail_data; + WINUSB_SETUP_PACKET packet = {0}; + ULONG size = 0; + + packet.RequestType = 0x80; // IN, DEVICE, STANDARD + packet.Request = USB_REQUEST_GET_DESCRIPTOR; + packet.Value = USB_DESCRIPTOR_MAKE_TYPE_AND_INDEX(USB_DEVICE_DESCRIPTOR_TYPE, 0); + packet.Index = 0; + packet.Length = sizeof(USB_DEVICE_DESCRIPTOR); + + WINBOOL res = WinUsb_ControlTransfer(handle, packet, (uint8_t *)desc, packet.Length, &size, NULL); + check(res, "[bulk] WinUsb_ControlTransfer() failed"); + check(size == sizeof(USB_DEVICE_DESCRIPTOR), "incomplete control transfer"); +} + +//----------------------------------------------------------------------------- +static char *get_string(WINUSB_INTERFACE_HANDLE handle, int index) +{ + uint16_t buf[128] = {0}; + ULONG size = 0; + + WINBOOL res = WinUsb_GetDescriptor(handle, USB_STRING_DESCRIPTOR_TYPE, index, + LANGID_US_ENGLISH, (uint8_t *)buf, sizeof(buf), &size); + check(res, "[bulk] WinUsb_GetDescriptor(STRING) failed"); + + check(size >= 2 && 0 == (size % 2), "invalid string descriptor response"); + + int len = size/2 - 1; + char *str = ""; + + if (len > 0) + { + str = buf_alloc(len + 1); + + for (int i = 0; i < len; i++) + str[i] = isprint(buf[i+1]) ? buf[i+1] : '?'; + } + + return str; +} + +//----------------------------------------------------------------------------- +static void check_ep_size(char *name, int in_size, int out_size) +{ + if (in_size != out_size) + { + error_exit("[%s] input and output endpoint sizes do not match (%d and %d)", + name, in_size, out_size); + } + + if (64 != in_size && 512 != in_size && 1024 != in_size) + error_exit("[%s] endpoint size %s is not 64, 512 or 1024", name, in_size); +} + +//----------------------------------------------------------------------------- +static int dbg_enumerate_bulk(debugger_t *debuggers, int size, int rsize) +{ + HDEVINFO dev_info; + SP_DEVICE_INTERFACE_DATA dev_int_data; + PSP_DEVICE_INTERFACE_DETAIL_DATA dev_int_detail; DWORD detail_size; + WINBOOL res; + + dev_info = SetupDiGetClassDevs(&GUID_CMSIS_DAP_V2, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + check(INVALID_HANDLE_VALUE != dev_info, "[bulk] SetupDiGetClassDevs() failed"); + + dev_int_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + for (int index = 0; rsize < size; index++) + { + if (FALSE == SetupDiEnumDeviceInterfaces(dev_info, 0, &GUID_CMSIS_DAP_V2, index, &dev_int_data)) + break; + + SetupDiGetDeviceInterfaceDetail(dev_info, &dev_int_data, NULL, 0, &detail_size, NULL); + + dev_int_detail = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, detail_size); + dev_int_detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + res = SetupDiGetDeviceInterfaceDetail(dev_info, &dev_int_data, dev_int_detail, + detail_size, NULL, NULL); + check(res, "[bulk] SetupDiGetDeviceInterfaceDetail() failed"); + + HANDLE device_handle = CreateFile(dev_int_detail->DevicePath, + GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + check(INVALID_HANDLE_VALUE != device_handle, "[bulk] CreateFile() failed"); + + WINUSB_INTERFACE_HANDLE handle; + + res = WinUsb_Initialize(device_handle, &handle); + check(res, "[bulk] WinUsb_Initialize() failed"); + + USB_DEVICE_DESCRIPTOR device; + get_device_descriptor(handle, &device); + + struct + { + USB_CONFIGURATION_DESCRIPTOR configuration; + USB_INTERFACE_DESCRIPTOR interface; + USB_ENDPOINT_DESCRIPTOR out; + USB_ENDPOINT_DESCRIPTOR in; + } config; + + ULONG size = 0; + + res = WinUsb_GetDescriptor(handle, USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0, + (PUCHAR)&config, sizeof(config), &size); + check(res, "[bulk] WinUsb_GetDescriptor(CONFIGURATION) failed"); + check(size == sizeof(config), "incomplete configuration descriptor"); + + if (USB_CONFIGURATION_DESCRIPTOR_TYPE != config.configuration.bDescriptorType || + USB_INTERFACE_DESCRIPTOR_TYPE != config.interface.bDescriptorType || + USB_ENDPOINT_DESCRIPTOR_TYPE != config.out.bDescriptorType || + USB_ENDPOINT_TYPE_BULK != config.out.bmAttributes || + !USB_ENDPOINT_DIRECTION_OUT(config.out.bEndpointAddress) || + USB_ENDPOINT_DESCRIPTOR_TYPE != config.in.bDescriptorType || + USB_ENDPOINT_TYPE_BULK != config.in.bmAttributes || + !USB_ENDPOINT_DIRECTION_IN(config.in.bEndpointAddress)) + error_exit("invalid configuration descriptor"); + + check_ep_size("bulk", config.in.wMaxPacketSize, config.out.wMaxPacketSize); + + char *serial = get_string(handle, device.iSerialNumber); + char *manufacturer = get_string(handle, device.iManufacturer); + char *product = get_string(handle, device.iProduct); + int idx = -1; + + for (int i = 0; i < rsize; i++) + { + if (debuggers[i].vid == device.idVendor && + debuggers[i].pid == device.idProduct && + 0 == strcmp(debuggers[i].serial, serial) && + 0 == strcmp(debuggers[i].manufacturer, manufacturer)) + { + idx = i; + break; + } + } + + if (-1 == idx) + { + idx = rsize; + rsize++; + } + + debuggers[idx].v2_path = strdup(dev_int_detail->DevicePath); + debuggers[idx].serial = serial; + debuggers[idx].manufacturer = manufacturer; + debuggers[idx].product = product; + debuggers[idx].vid = device.idVendor; + debuggers[idx].pid = device.idProduct; + debuggers[idx].v2_ep_size = config.in.wMaxPacketSize; + debuggers[idx].v2_tx_ep = config.out.bEndpointAddress; + debuggers[idx].v2_rx_ep = config.in.bEndpointAddress; + + debuggers[idx].versions |= DBG_CMSIS_DAP_V2; + + WinUsb_Free(handle); + CloseHandle(device_handle); + HeapFree(GetProcessHeap(), 0, dev_int_detail); + } + + SetupDiDestroyDeviceInfoList(dev_info); + + return rsize; +} + +//----------------------------------------------------------------------------- +static int dbg_enumerate_hid(debugger_t *debuggers, int size) +{ + HDEVINFO dev_info; + SP_DEVICE_INTERFACE_DATA dev_int_data; + PSP_DEVICE_INTERFACE_DETAIL_DATA dev_int_detail; + HIDD_ATTRIBUTES hid_attr; HANDLE handle; + GUID hid_guid; + DWORD detail_size; int rsize = 0; + WINBOOL res; HidD_GetHidGuid(&hid_guid); - hid_dev_info = SetupDiGetClassDevs(&hid_guid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE); + dev_info = SetupDiGetClassDevs(&hid_guid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE); + check(INVALID_HANDLE_VALUE != dev_info, "[hid] SetupDiGetClassDevs() failed"); - dev_info_data.cbSize = sizeof(dev_info_data); + dev_int_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); - for (int i = 0; rsize < size; i++) + for (int index = 0; rsize < size; index++) { - if (FALSE == SetupDiEnumDeviceInterfaces(hid_dev_info, 0, &hid_guid, i, &dev_info_data)) + if (FALSE == SetupDiEnumDeviceInterfaces(dev_info, 0, &hid_guid, index, &dev_int_data)) break; - SetupDiGetDeviceInterfaceDetail(hid_dev_info, &dev_info_data, NULL, 0, - &detail_size, NULL); + SetupDiGetDeviceInterfaceDetail(dev_info, &dev_int_data, NULL, 0, &detail_size, NULL); - detail_data = (PSP_DEVICE_INTERFACE_DETAIL_DATA)buf_alloc(detail_size); - detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + dev_int_detail = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, detail_size); + dev_int_detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); - SetupDiGetDeviceInterfaceDetail(hid_dev_info, &dev_info_data, detail_data, + res = SetupDiGetDeviceInterfaceDetail(dev_info, &dev_int_data, dev_int_detail, detail_size, NULL, NULL); + check(res, "[hid] SetupDiGetDeviceInterfaceDetail() failed"); - handle = CreateFile(detail_data->DevicePath, GENERIC_READ | GENERIC_WRITE, + handle = CreateFile(dev_int_detail->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - if (INVALID_HANDLE_VALUE != handle) + if (INVALID_HANDLE_VALUE == handle) { - wchar_t wstr[MAX_STRING_SIZE]; - char str[MAX_STRING_SIZE]; + HeapFree(GetProcessHeap(), 0, dev_int_detail); + continue; + } - wstr[0] = 0; - str[0] = 0; + wchar_t wstr[MAX_STRING_SIZE] = {0}; + char str[MAX_STRING_SIZE] = {0}; - hid_attr.Size = sizeof(hid_attr); - HidD_GetAttributes(handle, &hid_attr); + HidD_GetProductString(handle, (PVOID)wstr, MAX_STRING_SIZE); + wcstombs(str, wstr, MAX_STRING_SIZE); - debuggers[rsize].path = strdup(detail_data->DevicePath); + if (NULL == strstr(str, "CMSIS-DAP")) + { + CloseHandle(handle); + HeapFree(GetProcessHeap(), 0, dev_int_detail); + continue; + } - HidD_GetSerialNumberString(handle, (PVOID)wstr, MAX_STRING_SIZE); - wcstombs(str, wstr, MAX_STRING_SIZE); - debuggers[rsize].serial = strdup(str); + debuggers[rsize].v1_path = strdup(dev_int_detail->DevicePath); - HidD_GetManufacturerString(handle, (PVOID)wstr, MAX_STRING_SIZE); - wcstombs(str, wstr, MAX_STRING_SIZE); - debuggers[rsize].manufacturer = strdup(str); + debuggers[rsize].product = strdup(str); - HidD_GetProductString(handle, (PVOID)wstr, MAX_STRING_SIZE); - wcstombs(str, wstr, MAX_STRING_SIZE); - debuggers[rsize].product = strdup(str); + HidD_GetManufacturerString(handle, (PVOID)wstr, MAX_STRING_SIZE); + wcstombs(str, wstr, MAX_STRING_SIZE); + debuggers[rsize].manufacturer = strdup(str); - debuggers[rsize].vid = hid_attr.VendorID; - debuggers[rsize].pid = hid_attr.ProductID; + HidD_GetSerialNumberString(handle, (PVOID)wstr, MAX_STRING_SIZE); + wcstombs(str, wstr, MAX_STRING_SIZE); + debuggers[rsize].serial = strdup(str); - debuggers[rsize].versions = DBG_CMSIS_DAP_V1; + hid_attr.Size = sizeof(HIDD_ATTRIBUTES); + HidD_GetAttributes(handle, &hid_attr); - if (strstr(debuggers[rsize].product, "CMSIS-DAP")) - rsize++; + debuggers[rsize].vid = hid_attr.VendorID; + debuggers[rsize].pid = hid_attr.ProductID; - CloseHandle(handle); - } + PHIDP_PREPARSED_DATA prep; + HidD_GetPreparsedData(handle, &prep); - buf_free(detail_data); + HIDP_CAPS caps; + HidP_GetCaps(prep, &caps); + + check_ep_size("hid", caps.InputReportByteLength-1, caps.OutputReportByteLength-1); + + debuggers[rsize].v1_ep_size = caps.InputReportByteLength-1; + + debuggers[rsize].versions = DBG_CMSIS_DAP_V1; + + if (strstr(debuggers[rsize].product, "CMSIS-DAP")) + rsize++; + + CloseHandle(handle); + HeapFree(GetProcessHeap(), 0, dev_int_detail); } - SetupDiDestroyDeviceInfoList(hid_dev_info); + SetupDiDestroyDeviceInfoList(dev_info); return rsize; } //----------------------------------------------------------------------------- -void dbg_open(debugger_t *debugger, int version) +int dbg_enumerate(debugger_t *debuggers, int size) { - HIDP_CAPS caps; - PHIDP_PREPARSED_DATA prep; - int input, output; - - debugger_handle = CreateFile(debugger->path, GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - - if (INVALID_HANDLE_VALUE == debugger_handle) - error_exit("unable to open device %s: 0x%08x", debugger->path, GetLastError()); - - HidD_GetPreparsedData(debugger_handle, &prep); - HidP_GetCaps(prep, &caps); - - input = caps.InputReportByteLength - 1; - output = caps.OutputReportByteLength - 1; + int rsize = dbg_enumerate_hid(debuggers, size); + return dbg_enumerate_bulk(debuggers, size, rsize); +} - if (input != output) - error_exit("input and output report sizes do not match"); +//----------------------------------------------------------------------------- +void dbg_open(debugger_t *debugger, int version) +{ + g_debugger = debugger; - if (64 != input && 512 != input && 1024 != input) - error_exit("detected report size (%d) is not 64, 512 or 1024", input); + g_debugger->use_v2 = (DBG_CMSIS_DAP_V2 == version); - report_size = input; + if (g_debugger->use_v2) + { + g_handle = CreateFile(g_debugger->v2_path, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL); + check(INVALID_HANDLE_VALUE != g_handle, "[bulk] CreateFile() failed"); - (void)version; + WINBOOL res = WinUsb_Initialize(g_handle, &g_winusb_handle); + check(res, "[bulk] WinUsb_Initialize() failed"); + } + else + { + g_handle = CreateFile(g_debugger->v1_path, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + check(INVALID_HANDLE_VALUE != g_handle, "[hid] CreateFile() failed"); + } } //----------------------------------------------------------------------------- void dbg_close(void) { - if (INVALID_HANDLE_VALUE != debugger_handle) - CloseHandle(debugger_handle); + if (g_debugger->use_v2) + WinUsb_Free(g_winusb_handle); + + CloseHandle(g_handle); } //----------------------------------------------------------------------------- int dbg_get_packet_size(void) { - return report_size; + return g_debugger->use_v2 ? g_debugger->v2_ep_size : g_debugger->v1_ep_size; } //----------------------------------------------------------------------------- int dbg_dap_cmd(uint8_t *data, int resp_size, int req_size) { - uint8_t cmd = data[0]; - DWORD res; + uint8_t buf[DBG_MAX_EP_SIZE + 1]; + int cmd = data[0]; + int resp = 0; + int size; + WINBOOL res; + + memset(buf, 0xff, sizeof(buf)); + + if (g_debugger->use_v2) + { + ULONG actual_size = 0; - memset(hid_buffer, 0xff, report_size + 1); + res = WinUsb_WritePipe(g_winusb_handle, g_debugger->v2_tx_ep, data, req_size, &actual_size, NULL); + check(res && (int)actual_size == req_size, "WinUsb_WritePipe() failed"); - hid_buffer[0] = 0x00; // Report ID - memcpy(&hid_buffer[1], data, req_size); + res = WinUsb_ReadPipe(g_winusb_handle, g_debugger->v2_rx_ep, buf, g_debugger->v2_ep_size, &actual_size, NULL); + check(res, "WinUsb_WritePipe() failed"); - if (FALSE == WriteFile(debugger_handle, (LPCVOID)hid_buffer, report_size + 1, &res, NULL)) - error_exit("debugger write()"); + resp = buf[0]; + size = actual_size - 1; + memcpy(data, &buf[1], (resp_size < size) ? resp_size : size); + } + else + { + DWORD actual_size = 0; - if (FALSE == ReadFile(debugger_handle, (LPVOID)hid_buffer, report_size + 1, &res, NULL)) - error_exit("debugger read()"); + buf[0] = 0x00; // Report ID + memcpy(&buf[1], data, req_size); - if (hid_buffer[1] != cmd) - error_exit("invalid response received: request = 0x%02x, response = 0x%02x", cmd, hid_buffer[1]); + res = WriteFile(g_handle, (LPCVOID)buf, g_debugger->v1_ep_size + 1, &actual_size, NULL); + check(res && (int)actual_size == (g_debugger->v1_ep_size + 1), "WriteFile() failed"); - res -= 2; - memcpy(data, &hid_buffer[2], (resp_size < (int)res) ? resp_size : (int)res); + res = ReadFile(g_handle, (LPVOID)buf, g_debugger->v1_ep_size + 1, &actual_size, NULL); + check(res, "ReadFile() failed"); - return res; -} + resp = buf[1]; + size = actual_size - 2; + memcpy(data, &buf[2], (resp_size < size) ? resp_size : size); + } + + if (resp != cmd) + error_exit("invalid response received: request = 0x%02x, response = 0x%02x", cmd, resp); + return size; +} diff --git a/edbg.c b/edbg.c index 57ad36e..ba672a4 100644 --- a/edbg.c +++ b/edbg.c @@ -474,7 +474,7 @@ static void parse_command_line(int argc, char **argv) //----------------------------------------------------------------------------- int main(int argc, char **argv) { - debugger_t debuggers[MAX_DEBUGGERS]; + debugger_t debuggers[MAX_DEBUGGERS] = {0}; int n_debuggers = 0; int debugger = -1; target_ops_t *target_ops; @@ -498,8 +498,20 @@ int main(int argc, char **argv) if (g_list) { message("Attached debuggers:\n"); + for (int i = 0; i < n_debuggers; i++) - message(" %d: %s - %s %s\n", i, debuggers[i].serial, debuggers[i].manufacturer, debuggers[i].product); + { + char ver[8] = ""; + + if (debuggers[i].versions & DBG_CMSIS_DAP_V1) + strcat(ver, "1"); + + if (debuggers[i].versions & DBG_CMSIS_DAP_V2) + strcat(ver, "2"); + + message(" %d: %s - %s %s (%s)\n", i, debuggers[i].serial, debuggers[i].manufacturer, debuggers[i].product, ver); + } + return 0; }