diff --git a/src/command.c b/src/command.c index 46709697e54..357c4e33f70 100644 --- a/src/command.c +++ b/src/command.c @@ -54,6 +54,7 @@ static bool cmd_help(target_s *t, int argc, const char **argv); static bool cmd_jtag_scan(target_s *t, int argc, const char **argv); static bool cmd_swdp_scan(target_s *t, int argc, const char **argv); +static bool cmd_rvswd_scan(target_s *t, int argc, const char **argv); static bool cmd_auto_scan(target_s *t, int argc, const char **argv); static bool cmd_frequency(target_s *t, int argc, const char **argv); static bool cmd_targets(target_s *t, int argc, const char **argv); @@ -84,6 +85,7 @@ const command_s cmd_list[] = { {"help", cmd_help, "Display help for monitor commands"}, {"jtag_scan", cmd_jtag_scan, "Scan JTAG chain for devices"}, {"swdp_scan", cmd_swdp_scan, "Scan SW-DP for devices"}, + {"rvswd_scan", cmd_rvswd_scan, "Scan RVSWD for devices"}, {"auto_scan", cmd_auto_scan, "Automatically scan all chain types for devices"}, {"frequency", cmd_frequency, "set minimum high and low times"}, {"targets", cmd_targets, "Display list of available targets"}, @@ -286,6 +288,49 @@ bool cmd_swdp_scan(target_s *t, int argc, const char **argv) return true; } +bool cmd_rvswd_scan(target_s *t, int argc, const char **argv) +{ + (void)t; + (void)argc; + (void)argv; + + if (platform_target_voltage()) + gdb_outf("Target voltage: %s\n", platform_target_voltage()); + + if (connect_assert_nrst) + platform_nrst_set_val(true); /* will be deasserted after attach */ + + uint32_t devs = 0; + volatile exception_s e; + TRY_CATCH (e, EXCEPTION_ALL) { +#if PC_HOSTED == 1 + devs = platform_rvswd_scan(); +#else + devs = 0; +#endif + } + switch (e.type) { + case EXCEPTION_TIMEOUT: + gdb_outf("Timeout during scan.\n"); + break; + case EXCEPTION_ERROR: + gdb_outf("Exception: %s\n", e.msg); + break; + } + + if (devs == 0) { + platform_target_clk_output_enable(false); + platform_nrst_set_val(false); + gdb_out("RVSWD scan failed!\n"); + return false; + } + + cmd_targets(NULL, 0, NULL); + platform_target_clk_output_enable(false); + morse(NULL, false); + return true; +} + bool cmd_auto_scan(target_s *t, int argc, const char **argv) { (void)t; diff --git a/src/include/target.h b/src/include/target.h index 38c601a3653..6552997cf46 100644 --- a/src/include/target.h +++ b/src/include/target.h @@ -37,6 +37,7 @@ typedef struct target_controller target_controller_s; #if PC_HOSTED == 1 uint32_t platform_adiv5_swdp_scan(uint32_t targetid); uint32_t platform_jtag_scan(const uint8_t *lrlens); +uint32_t platform_rvswd_scan(); #endif uint32_t adiv5_swdp_scan(uint32_t targetid); uint32_t jtag_scan(const uint8_t *lrlens); diff --git a/src/platforms/hosted/Makefile.inc b/src/platforms/hosted/Makefile.inc index d81b8fe8214..a2e1121fd45 100644 --- a/src/platforms/hosted/Makefile.inc +++ b/src/platforms/hosted/Makefile.inc @@ -79,6 +79,7 @@ ifneq ($(HOSTED_BMP_ONLY), 1) SRC += bmp_libusb.c stlinkv2.c SRC += ftdi_bmp.c libftdi_swdptap.c libftdi_jtagtap.c SRC += jlink.c jlink_adiv5_swdp.c jlink_jtagtap.c + SRC += wchlink.c riscv_dtm_wchlink.c else SRC += bmp_serial.c endif diff --git a/src/platforms/hosted/bmp_libusb.c b/src/platforms/hosted/bmp_libusb.c index 005b9beaec9..5782d7499c4 100644 --- a/src/platforms/hosted/bmp_libusb.c +++ b/src/platforms/hosted/bmp_libusb.c @@ -29,7 +29,7 @@ void bmp_ident(bmp_info_s *info) { PRINT_INFO("Black Magic Debug App %s\n for Black Magic Probe, ST-Link v2 and v3, CMSIS-DAP," - " JLink and libftdi/MPSSE\n", + " JLink, libftdi/MPSSE and WCH-Link\n", FIRMWARE_VERSION); if (info && info->vid && info->pid) { PRINT_INFO("Using %04x:%04x %s %s\n %s\n", info->vid, info->pid, @@ -261,6 +261,8 @@ int find_debuggers(bmda_cli_options_s *cl_opts, bmp_info_s *info) } } else if (desc.idVendor == VENDOR_ID_SEGGER) type = BMP_TYPE_JLINK; + else if (desc.idVendor == VENDOR_ID_WCH) + type = BMP_TYPE_WCHLINK; else { const cable_desc_s *cable = cable_desc; for (; cable->name; ++cable) { diff --git a/src/platforms/hosted/platform.c b/src/platforms/hosted/platform.c index b8f16f55fbc..0ba3c3d4abc 100644 --- a/src/platforms/hosted/platform.c +++ b/src/platforms/hosted/platform.c @@ -40,6 +40,7 @@ #include "stlinkv2.h" #include "ftdi_bmp.h" #include "jlink.h" +#include "wchlink.h" #include "cmsis_dap.h" bmp_info_s info; @@ -121,6 +122,11 @@ void platform_init(int argc, char **argv) exit(-1); break; + case BMP_TYPE_WCHLINK: + if (!wchlink_init(&info)) + exit(-1); + break; + default: exit(-1); } @@ -230,6 +236,19 @@ bool platform_jtagtap_init(void) } } +uint32_t platform_rvswd_scan() +{ + info.is_jtag = false; + + switch (info.bmp_type) { + case BMP_TYPE_WCHLINK: + return wchlink_rvswd_scan(&info); + + default: + return 0; + } +} + void platform_adiv5_dp_defaults(adiv5_debug_port_s *dp) { switch (info.bmp_type) { @@ -286,6 +305,9 @@ char *platform_ident(void) case BMP_TYPE_JLINK: return "J-Link"; + case BMP_TYPE_WCHLINK: + return "WCH-Link"; + default: return NULL; } @@ -306,6 +328,9 @@ const char *platform_target_voltage(void) case BMP_TYPE_JLINK: return jlink_target_voltage(&info); + case BMP_TYPE_WCHLINK: + return wchlink_target_voltage(&info); + default: return NULL; } @@ -329,6 +354,9 @@ void platform_nrst_set_val(bool assert) case BMP_TYPE_CMSIS_DAP: return dap_nrst_set_val(assert); + case BMP_TYPE_WCHLINK: + return wchlink_nrst_set_val(&info, assert); + default: break; } @@ -349,6 +377,9 @@ bool platform_nrst_get_val(void) case BMP_TYPE_LIBFTDI: return libftdi_nrst_get_val(); + case BMP_TYPE_WCHLINK: + return wchlink_nrst_get_val(&info); + default: return false; } diff --git a/src/platforms/hosted/platform.h b/src/platforms/hosted/platform.h index fc6e9b46c14..db34df68e4e 100644 --- a/src/platforms/hosted/platform.h +++ b/src/platforms/hosted/platform.h @@ -69,13 +69,16 @@ void platform_buffer_flush(void); #define VENDOR_ID_SEGGER 0x1366U +#define VENDOR_ID_WCH 0x1a86U + typedef enum bmp_type_e { BMP_TYPE_NONE = 0, BMP_TYPE_BMP, BMP_TYPE_STLINKV2, BMP_TYPE_LIBFTDI, BMP_TYPE_CMSIS_DAP, - BMP_TYPE_JLINK + BMP_TYPE_JLINK, + BMP_TYPE_WCHLINK } bmp_type_t; void gdb_ident(char *p, int count); diff --git a/src/platforms/hosted/riscv_dtm_wchlink.c b/src/platforms/hosted/riscv_dtm_wchlink.c new file mode 100644 index 00000000000..3e2dab0a52f --- /dev/null +++ b/src/platforms/hosted/riscv_dtm_wchlink.c @@ -0,0 +1,77 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rafael Silva + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "general.h" +#include "bmp_hosted.h" +#include "riscv_debug.h" +#include "target_probe.h" +#include "target.h" +#include "wchlink.h" + +void riscv_dtm_wchlink_init(riscv_dtm_s *dtm); + +static bool riscv_dtm_wchlink_dmi_read(riscv_dtm_s *const dtm, const uint32_t address, uint32_t *const value) +{ + (void)dtm; + return wchlink_riscv_dmi_read(&info, address, value); +} + +static bool riscv_dtm_wchlink_dmi_write(riscv_dtm_s *const dtm, const uint32_t address, const uint32_t value) +{ + (void)dtm; + return wchlink_riscv_dmi_write(&info, address, value); +} + +uint8_t riscv_dtm_wchlink_handler() +{ + riscv_dtm_s *dtm = calloc(1, sizeof(*dtm)); + if (!dtm) { /* calloc failed: heap exhaustion */ + DEBUG_WARN("calloc: failed in %s\n", __func__); + return 0; + } + + dtm->dmi_read = riscv_dtm_wchlink_dmi_read; + dtm->dmi_write = riscv_dtm_wchlink_dmi_write; + + dtm->version = RISCV_DEBUG_0_13; + // dtm->designer_code = 0xaa55; /* WCHLINK deosn't seem to have a jep106 code assigned */ + + /* Call higher level code to discover the DMI bus */ + riscv_dtm_init(dtm); + + /* If we failed to find any DMs or Harts, free the structure */ + if (!dtm->ref_count) + free(dtm); + + return dtm->ref_count; +} diff --git a/src/platforms/hosted/riscv_dtm_wchlink.h b/src/platforms/hosted/riscv_dtm_wchlink.h new file mode 100644 index 00000000000..15a2fea2b6b --- /dev/null +++ b/src/platforms/hosted/riscv_dtm_wchlink.h @@ -0,0 +1,39 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rafael Silva + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PLATFORM_HOSTED_RISCV_DTM_WCHLINK_H +#define PLATFORM_HOSTED_RISCV_DTM_WCHLINK_H + +uint8_t riscv_dtm_wchlink_handler(); + +#endif /* PLATFORM_HOSTED_RISCV_DTM_WCHLINK_H */ diff --git a/src/platforms/hosted/wchlink.c b/src/platforms/hosted/wchlink.c new file mode 100644 index 00000000000..a81985dc460 --- /dev/null +++ b/src/platforms/hosted/wchlink.c @@ -0,0 +1,413 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2022 1BitSquared + * Written by Rafael Silva + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "wchlink.h" + +#include "riscv_debug.h" +#include "riscv_dtm_wchlink.h" + +#define WCHLINK_MODERV_OP_EPT_ADDR 0x1U +#define WCHLINK_MODERV_FLASH_EPT_ADDR 0x2U + +#define TRANSFER_TIMEOUT_MS 100U + +#define WCH_CMD_OUT_HEADER 0x81U +#define WCH_CMD_IN_HEADER 0x82U + +#define WCH_CMD_UNKNOWN_FIELD 0x01U +#define WCH_CMD_UNKNOWN_FIELD_DMI 0x06U + +/* Flash commands */ +#define WCH_FLASH_GRP 0x02U +#define WCH_FLASH_CHIPERASE 0x01U /* Unconfirmed */ +#define WCH_FLASH_TERMINATE 0x08U /* Unconfirmed */ + +/* Flash potection commands */ +#define WCH_PROTECT_GRP 0x06U +#define WCH_PROTECT_CMD_CHECK 0x01U +#define WCH_PROTECT_CMD_FLASH_UNPROTECT 0x02U +#define WCH_PROTECT_CMD_FLASH_PROTECT 0x03U + +/* DMI access commands */ +#define WCH_DMI_GRP 0x08U +#define WCH_DMI_CMD_NOP 0x00U +#define WCH_DMI_CMD_READ 0x01U +#define WCH_DMI_CMD_WRITE 0x02U + +/* System commands */ +#define WCH_SYS_GRP 0x0dU +#define WCH_SYS_CMD_GET_VERSION 0x01U +#define WCH_SYS_CMD_CONNECT 0x02U +#define WCH_SYS_CMD_UNKNOWN 0x03U /* Unknown - no clue */ +#define WCH_SYS_CMD_GET_MEMINFO 0x04U /* RAM size, flash size, addr. */ +#define WCH_SYS_CMD_CLOSE 0xffU /* Terminate connection (unsure what this does) */ + +/* Debug commands */ +#define WCH_DBG_GRP 0x0eU +#define WCH_DBG_CMD_DISABLE_DBG 0x01U + +/* Error ? who knows */ +#define WCH_ERR_GRP 0x55U + +static bool wchlink_bulk_write(bmp_info_s *const info, uint8_t *const data, const size_t length) +{ + int transferred = 0; + const int result = libusb_bulk_transfer(info->usb_link->ul_libusb_device_handle, info->usb_link->ep_tx, data, + length, &transferred, TRANSFER_TIMEOUT_MS); + if (result < 0) { + DEBUG_WARN("WCH-Link write error: %s (%d)\n", libusb_strerror(result), result); + return false; + } + + return true; +} + +static bool wchlink_bulk_read(bmp_info_s *const info, uint8_t *const data, const size_t length) +{ + int transferred = 0; + const int result = libusb_bulk_transfer(info->usb_link->ul_libusb_device_handle, info->usb_link->ep_rx, data, + length, &transferred, TRANSFER_TIMEOUT_MS); + if (result < 0) { + DEBUG_WARN("WCH-Link read error: %s (%d)\n", libusb_strerror(result), result); + return false; + } + + return true; +} + +/* + * On success this copies the endpoint addresses identified into the + * usb_link_s sub-structure of bmp_info_s (info->usb_link) for later use. + * Returns true for success, false for failure. + */ +static bool claim_wchlink_interface(bmp_info_s *info, libusb_device *dev) +{ + libusb_config_descriptor_s *config; + const int result = libusb_get_active_config_descriptor(dev, &config); + if (result != LIBUSB_SUCCESS) { + DEBUG_WARN("Failed to get configuration descriptor: %s\n", libusb_error_name(result)); + return false; + } + + const libusb_interface_descriptor_s *descriptor = NULL; + for (size_t i = 0; i < config->bNumInterfaces; ++i) { + const libusb_interface_s *const interface = &config->interface[i]; + const libusb_interface_descriptor_s *const interface_desc = &interface->altsetting[0]; + if (interface_desc->bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC && interface_desc->bInterfaceSubClass == 128U) { + const int result = libusb_claim_interface(info->usb_link->ul_libusb_device_handle, i); + if (result) { + DEBUG_WARN("Can not claim handle: %s\n", libusb_error_name(result)); + break; + } + + info->usb_link->interface = i; + descriptor = interface_desc; + } + } + if (!descriptor) { + DEBUG_WARN("No suitable interface found\n"); + libusb_free_config_descriptor(config); + return false; + } + + for (size_t i = 0; i < descriptor->bNumEndpoints; i++) { + const libusb_endpoint_descriptor_s *endpoint = &descriptor->endpoint[i]; + + if ((endpoint->bEndpointAddress & LIBUSB_ENDPOINT_ADDRESS_MASK) == WCHLINK_MODERV_OP_EPT_ADDR) { + if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) + info->usb_link->ep_rx = endpoint->bEndpointAddress; + else + info->usb_link->ep_tx = endpoint->bEndpointAddress; + } + } + libusb_free_config_descriptor(config); + return true; +} + +static bool wchlink_read_version(bmp_info_s *info) +{ + uint8_t cmd[] = { + WCH_CMD_OUT_HEADER, + WCH_SYS_GRP, + WCH_CMD_UNKNOWN_FIELD, + WCH_SYS_CMD_GET_VERSION, + }; + if (!wchlink_bulk_write(info, cmd, sizeof(cmd))) + return false; + + uint8_t response[7]; + if (!wchlink_bulk_read(info, response, sizeof(response))) + return false; + + const uint8_t wchlink_major = response[3]; + const uint8_t wchlink_minor = response[4]; + const uint8_t wchlink_id = response[5]; + + const char *wchlink_name = NULL; + switch (wchlink_id) { + case 1: + wchlink_name = "WCH-Link-CH549 (RV)"; + break; + case 2: + wchlink_name = "WCH-LinkE-CH32V307 (RV)"; + break; + case 3: + wchlink_name = "WCH-LinkS-CH32V203 (RV)"; + break; + case 4: + wchlink_name = "WCH-LinkB (RV)"; + break; + default: + wchlink_name = "unknow WCH-LINK"; + break; + } + + DEBUG_INFO("%s (id:%u) v%u.%u\n", wchlink_name, wchlink_id, wchlink_major, wchlink_minor); + + return true; +} + +static bool wchlink_connect(bmp_info_s *info, uint8_t *const device_code, uint32_t *const device_id) +{ + uint8_t cmd[] = { + WCH_CMD_OUT_HEADER, + WCH_SYS_GRP, + WCH_CMD_UNKNOWN_FIELD, + WCH_SYS_CMD_CONNECT, + }; + if (!wchlink_bulk_write(info, cmd, sizeof(cmd))) + return false; + + uint8_t response[8]; + if (!wchlink_bulk_read(info, response, sizeof(response))) + return false; + + if (response[1] != WCH_SYS_GRP) { // response[1] returns WCH_ERR_GRP (0x55) on error + DEBUG_WARN("WCH-Link failed to connect with device\n"); + return false; + } + + *device_code = response[3]; + *device_id = (response[4] << 24U) + (response[5] << 16U) + (response[6] << 8U) + (response[7] & 0x0fU); + + return true; +} + +static bool wchlink_unknown(bmp_info_s *info) +{ + uint8_t cmd[] = { + WCH_CMD_OUT_HEADER, + WCH_SYS_GRP, + WCH_CMD_UNKNOWN_FIELD, + WCH_SYS_CMD_UNKNOWN, + }; + if (!wchlink_bulk_write(info, cmd, sizeof(cmd))) + return false; + + uint8_t response[4]; + if (!wchlink_bulk_read(info, response, sizeof(response))) + return false; + + return true; +} + +bool wchlink_riscv_dmi_read(bmp_info_s *info, const uint32_t address, uint32_t *const value) +{ + uint8_t cmd[] = { + WCH_CMD_OUT_HEADER, + WCH_DMI_GRP, + WCH_CMD_UNKNOWN_FIELD_DMI, + address & 0xffU, + 0U, + 0U, + 0U, + 0U, + WCH_DMI_CMD_READ, + }; + + if (!wchlink_bulk_write(info, cmd, sizeof(cmd))) + return false; + + uint8_t response[9]; + if (!wchlink_bulk_read(info, response, sizeof(response))) + return false; + + if (response[8]) { /* status */ + DEBUG_WARN("WCH-Link failed to read DMI register\n"); + return false; + } + + *value = (response[4] << 24U) + (response[5] << 16U) + (response[6] << 8U) + response[7]; + + return true; +} + +bool wchlink_riscv_dmi_write(bmp_info_s *info, const uint32_t address, const uint32_t value) +{ + uint8_t cmd[] = { + WCH_CMD_OUT_HEADER, + WCH_DMI_GRP, + WCH_CMD_UNKNOWN_FIELD_DMI, + address & 0xffU, + value >> 24U, + value >> 16U, + value >> 8U, + value & 0xffU, + WCH_DMI_CMD_WRITE, + }; + + if (!wchlink_bulk_write(info, cmd, sizeof(cmd))) + return false; + + uint8_t response[9]; + if (!wchlink_bulk_read(info, response, sizeof(response))) + return false; + + if (response[8]) { /* status */ + DEBUG_WARN("WCH-Link failed to read DMI register\n"); + return false; + } + + return true; +} + +bool wchlink_init(bmp_info_s *const info) +{ + usb_link_s *link = calloc(1, sizeof(usb_link_s)); + if (!link) + return false; + + info->usb_link = link; + link->ul_libusb_ctx = info->libusb_ctx; + + libusb_device **device_list = NULL; + const ssize_t device_count = libusb_get_device_list(info->libusb_ctx, &device_list); + if (device_count < 0) { + DEBUG_WARN("libusb_get_device_list() failed"); + return false; + } + + libusb_device *device_wchlink = NULL; + for (ssize_t device_index = 0; device_index < device_count; ++device_index) { + if (!device_list[device_index]) + continue; + + libusb_device *const device = device_list[device_index]; + struct libusb_device_descriptor device_descriptor; + if (libusb_get_device_descriptor(device, &device_descriptor) < 0) { + DEBUG_WARN("libusb_get_device_descriptor() failed"); + libusb_free_device_list(device_list, 1); + return false; + } + + if (device_descriptor.idVendor != info->vid || device_descriptor.idProduct != info->pid) + continue; + + int result = libusb_open(device, &link->ul_libusb_device_handle); + if (result != LIBUSB_SUCCESS) + continue; + + device_wchlink = device; + break; + } + + if (!device_wchlink || !claim_wchlink_interface(info, device_wchlink)) { + libusb_free_device_list(device_list, 1); + return false; + } + + link->req_trans = libusb_alloc_transfer(0); + link->rep_trans = libusb_alloc_transfer(0); + if (!link->req_trans || !link->rep_trans || !link->ep_tx || !link->ep_rx) { + DEBUG_WARN("Device setup failed\n"); + libusb_release_interface(info->usb_link->ul_libusb_device_handle, info->usb_link->interface); + libusb_close(info->usb_link->ul_libusb_device_handle); + libusb_free_device_list(device_list, 1); + return false; + } + + libusb_free_device_list(device_list, 1); + + return wchlink_read_version(info); +} + +const char *wchlink_target_voltage(bmp_info_s *info) +{ + (void)info; + return "Unavailable"; +} + +void wchlink_nrst_set_val(bmp_info_s *info, bool assert) +{ + (void)info; + (void)assert; +} + +bool wchlink_nrst_get_val(bmp_info_s *info) +{ + (void)info; + return true; +} + +uint32_t wchlink_rvswd_scan(bmp_info_s *info) +{ + (void)info; + + target_list_free(); + + uint8_t device_code; + uint32_t device_id; + + if (!wchlink_connect(info, &device_code, &device_id)) { + DEBUG_WARN("WCH-Link failed to connect to target"); + return 0U; + } + + DEBUG_WARN("WCH-Link connected with 0x%0X\n", device_id); + + switch (device_code) { + case 0x1U: + case 0x5U: + case 0x6U: + case 0x9U: + case 0xaU: + wchlink_unknown(info); + break; + + default: + break; + } + + return riscv_dtm_wchlink_handler(); +} diff --git a/src/platforms/hosted/wchlink.h b/src/platforms/hosted/wchlink.h new file mode 100644 index 00000000000..02be000ac34 --- /dev/null +++ b/src/platforms/hosted/wchlink.h @@ -0,0 +1,98 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2022 1BitSquared + * Written by Rafael Silva + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PLATFORMS_HOSTED_WCHLINK_H +#define PLATFORMS_HOSTED_WCHLINK_H + +#include +#include "bmp_hosted.h" + +#if HOSTED_BMP_ONLY == 1 +bool wchlink_init(bmp_info_s *info) +{ + (void)info; + return false; +} + +const char *wchlink_target_voltage(bmp_info_s *info) +{ + (void)info; + return "ERROR"; +} + +void wchlink_nrst_set_val(bmp_info_s *info, bool assert) +{ + (void)info; + (void)assert; +} + +bool wchlink_nrst_get_val(bmp_info_s *info) +{ + (void)info; + return true; +} + +uint32_t wchlink_rvswd_scan(bmp_info_s *info) +{ + (void)info; + return 0; +} + +bool wchlink_riscv_dmi_read(bmp_info_s *info, uint32_t address, uint32_t *value) +{ + (void)info; + (void)address; + (void)value; + return false; +} + +bool wchlink_riscv_dmi_write(bmp_info_s *info, uint32_t address, uint32_t value) +{ + (void)info; + (void)address; + (void)value; + return false; +} + +#else +bool wchlink_init(bmp_info_s *info); +const char *wchlink_target_voltage(bmp_info_s *info); +void wchlink_nrst_set_val(bmp_info_s *info, bool assert); +bool wchlink_nrst_get_val(bmp_info_s *info); +uint32_t wchlink_rvswd_scan(bmp_info_s *info); +bool wchlink_riscv_dmi_read(bmp_info_s *info, uint32_t address, uint32_t *value); +bool wchlink_riscv_dmi_write(bmp_info_s *info, uint32_t address, uint32_t value); +bool wchlink_riscv_dmi_read(bmp_info_s *info, uint32_t address, uint32_t *value); +bool wchlink_riscv_dmi_write(bmp_info_s *info, uint32_t address, uint32_t value); +#endif + +#endif /* PLATFORMS_HOSTED_WCHLINK_H */