moved DAP code away from main
This commit is contained in:
@@ -138,7 +138,6 @@ endif()
|
||||
|
||||
|
||||
add_executable(${PROJECT}
|
||||
src/dap_util.c
|
||||
src/get_config.c
|
||||
src/led.c
|
||||
src/main.c
|
||||
@@ -148,6 +147,8 @@ add_executable(${PROJECT}
|
||||
src/sw_dp_pio.c
|
||||
src/sw_lock.c
|
||||
src/usb_descriptors.c
|
||||
src/cmsis-dap/dap_server.c
|
||||
src/cmsis-dap/dap_util.c
|
||||
)
|
||||
|
||||
target_sources(${PROJECT} PRIVATE
|
||||
|
||||
6
src/cmsis-dap/README.adoc
Executable file
6
src/cmsis-dap/README.adoc
Executable file
@@ -0,0 +1,6 @@
|
||||
:imagesdir: doc/png
|
||||
:source-highlighter: rouge
|
||||
:toc:
|
||||
:toclevels: 5
|
||||
|
||||
# CMSIS-DAP Server
|
||||
380
src/cmsis-dap/dap_server.c
Executable file
380
src/cmsis-dap/dap_server.c
Executable file
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Raspberry Pi (Trading) 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
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <pico/stdlib.h>
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "event_groups.h"
|
||||
#include "task.h"
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
#include "picoprobe_config.h"
|
||||
#include "dap_server.h"
|
||||
#include "dap_util.h"
|
||||
#include "DAP_config.h"
|
||||
#include "DAP.h"
|
||||
#include "led.h"
|
||||
#include "sw_lock.h"
|
||||
|
||||
|
||||
#if OPT_CMSIS_DAPV2
|
||||
static TaskHandle_t dap_taskhandle = NULL;
|
||||
static EventGroupHandle_t dap_events;
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* The following is part of a hack to make DAP_PACKET_COUNT a variable.
|
||||
* CMSIS-DAPv2 has better performance with 2 packets while
|
||||
* CMSIS-DAPv1 only works with one packet, at least with openocd which throws a
|
||||
* "CMSIS-DAP transfer count mismatch: expected 12, got 8" on flashing.
|
||||
* The correct packet count has to be set on connection.
|
||||
*
|
||||
* More notes: pyocd works with large packets only, if the packet count is one.
|
||||
* Additionally pyocd is instable if packet count > 1. Valid for pyocd 0.34.3.
|
||||
*/
|
||||
//#define _DAP_PACKET_COUNT_OPENOCD 2
|
||||
//#define _DAP_PACKET_SIZE_OPENOCD CFG_TUD_VENDOR_RX_BUFSIZE
|
||||
#define _DAP_PACKET_COUNT_OPENOCD 1 // TODO check optimizations
|
||||
#define _DAP_PACKET_SIZE_OPENOCD 256
|
||||
#define _DAP_PACKET_COUNT_PYOCD 1
|
||||
#define _DAP_PACKET_SIZE_PYOCD 1024 // pyocd does not like packets > 128 if COUNT != 1
|
||||
#define _DAP_PACKET_COUNT_UNKNOWN 1
|
||||
#define _DAP_PACKET_SIZE_UNKNOWN 64
|
||||
|
||||
#define _DAP_PACKET_COUNT_HID 1
|
||||
#define _DAP_PACKET_SIZE_HID 64
|
||||
|
||||
uint8_t dap_packet_count = _DAP_PACKET_COUNT_UNKNOWN;
|
||||
uint16_t dap_packet_size = _DAP_PACKET_SIZE_UNKNOWN;
|
||||
|
||||
#if OPT_CMSIS_DAPV1 || OPT_CMSIS_DAPV2
|
||||
static uint8_t TxDataBuffer[_DAP_PACKET_COUNT_OPENOCD * CFG_TUD_VENDOR_RX_BUFSIZE]; // maximum required size
|
||||
#endif
|
||||
#if OPT_CMSIS_DAPV2
|
||||
static uint8_t RxDataBuffer[_DAP_PACKET_COUNT_OPENOCD * CFG_TUD_VENDOR_RX_BUFSIZE]; // maximum required size
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if OPT_CMSIS_DAPV2
|
||||
void tud_vendor_rx_cb(uint8_t itf)
|
||||
{
|
||||
if (itf == 0) {
|
||||
xEventGroupSetBits(dap_events, 0x01);
|
||||
}
|
||||
} // tud_vendor_rx_cb
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if OPT_CMSIS_DAPV2
|
||||
/**
|
||||
* CMSIS-DAP task.
|
||||
* Receive DAP requests, execute them via DAP_ExecuteCommand() and transmit the response.
|
||||
*
|
||||
* Problem zones:
|
||||
* - connect / disconnect: pyOCD does not send permanently requests if in gdbserver mode, OpenOCD does.
|
||||
* As a consequence "disconnect" has to be detected via the command stream. If the tool on host side
|
||||
* fails without a disconnect, the SWD connection is not freed (for MSC or RTT). To recover from this
|
||||
* situation either reset the probe or issue something like "pyocd reset -t rp2040"
|
||||
* - fingerprinting the host tool: this is for optimization of the OpenOCD connection, because OpenOCD
|
||||
* can handle big DAP packets and thus transfer is faster.
|
||||
* - ID_DAP_Disconnect / ID_DAP_Info / ID_DAP_HostStatus leads to an SWD disconnect if there is no other
|
||||
* command following within 1s. This is required, because "pyocd list" leads to tool detection without
|
||||
* connect/disconnect and thus otherwise tool detection would be stuck to "pyocd" for the next connection.
|
||||
*/
|
||||
void dap_task(void *ptr)
|
||||
{
|
||||
bool swd_connected = false;
|
||||
bool swd_disconnect_requested = false;
|
||||
uint32_t last_request_us = 0;
|
||||
uint32_t rx_len = 0;
|
||||
daptool_t tool = E_DAPTOOL_UNKNOWN;
|
||||
|
||||
dap_packet_count = _DAP_PACKET_COUNT_UNKNOWN;
|
||||
dap_packet_size = _DAP_PACKET_SIZE_UNKNOWN;
|
||||
for (;;) {
|
||||
// disconnect after 1s without data
|
||||
if (swd_disconnect_requested && time_us_32() - last_request_us > 1000000) {
|
||||
if (swd_connected) {
|
||||
swd_connected = false;
|
||||
picoprobe_info("=================================== DAPv2 disconnect target\n");
|
||||
led_state(LS_DAPV2_DISCONNECTED);
|
||||
sw_unlock("DAPv2");
|
||||
}
|
||||
swd_disconnect_requested = false;
|
||||
dap_packet_count = _DAP_PACKET_COUNT_UNKNOWN;
|
||||
dap_packet_size = _DAP_PACKET_SIZE_UNKNOWN;
|
||||
tool = DAP_FingerprintTool(NULL, 0);
|
||||
}
|
||||
|
||||
xEventGroupWaitBits(dap_events, 0x01, pdTRUE, pdFALSE, pdMS_TO_TICKS(100)); // TODO "pyocd reset -f 500000" does otherwise not disconnect
|
||||
|
||||
if (tud_vendor_available())
|
||||
{
|
||||
rx_len += tud_vendor_read(RxDataBuffer + rx_len, sizeof(RxDataBuffer));
|
||||
|
||||
if (rx_len != 0)
|
||||
{
|
||||
uint32_t request_len;
|
||||
|
||||
request_len = DAP_GetCommandLength(RxDataBuffer, rx_len);
|
||||
if (rx_len >= request_len)
|
||||
{
|
||||
last_request_us = time_us_32();
|
||||
// picoprobe_info("<<<(%lx) %d %d\n", request_len, RxDataBuffer[0], RxDataBuffer[1]);
|
||||
|
||||
//
|
||||
// try to find out which tool is connecting
|
||||
//
|
||||
if (tool == E_DAPTOOL_UNKNOWN) {
|
||||
tool = DAP_FingerprintTool(RxDataBuffer, request_len);
|
||||
if (tool == E_DAPTOOL_OPENOCD) {
|
||||
dap_packet_count = _DAP_PACKET_COUNT_OPENOCD;
|
||||
dap_packet_size = _DAP_PACKET_SIZE_OPENOCD;
|
||||
}
|
||||
else if (tool == E_DAPTOOL_PYOCD) {
|
||||
dap_packet_count = _DAP_PACKET_COUNT_PYOCD;
|
||||
dap_packet_size = _DAP_PACKET_SIZE_PYOCD;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// initiate SWD connect / disconnect
|
||||
//
|
||||
if ( !swd_connected && RxDataBuffer[0] != ID_DAP_Info) {
|
||||
if (sw_lock("DAPv2", true)) {
|
||||
swd_connected = true;
|
||||
picoprobe_info("=================================== DAPv2 connect target, host %s\n",
|
||||
(tool == E_DAPTOOL_OPENOCD) ? "OpenOCD with two big buffers" :
|
||||
((tool == E_DAPTOOL_PYOCD) ? "pyOCD with single big buffer" : "UNKNOWN"));
|
||||
led_state(LS_DAPV2_CONNECTED);
|
||||
}
|
||||
}
|
||||
if (RxDataBuffer[0] == ID_DAP_Disconnect || RxDataBuffer[0] == ID_DAP_Info || RxDataBuffer[0] == ID_DAP_HostStatus) {
|
||||
swd_disconnect_requested = true;
|
||||
}
|
||||
else {
|
||||
swd_disconnect_requested = false;
|
||||
}
|
||||
|
||||
//
|
||||
// execute request and send back response
|
||||
//
|
||||
if (swd_connected || DAP_OfflineCommand(RxDataBuffer))
|
||||
{
|
||||
uint32_t resp_len;
|
||||
|
||||
resp_len = DAP_ExecuteCommand(RxDataBuffer, TxDataBuffer);
|
||||
// picoprobe_info(">>>(%lx) %d %d %d %d\n", resp_len, TxDataBuffer[0], TxDataBuffer[1], TxDataBuffer[2], TxDataBuffer[3]);
|
||||
|
||||
tud_vendor_write(TxDataBuffer, resp_len & 0xffff);
|
||||
tud_vendor_flush();
|
||||
|
||||
if (request_len != (resp_len >> 16))
|
||||
{
|
||||
// there is a bug in CMSIS-DAP, see https://github.com/ARM-software/CMSIS_5/pull/1503
|
||||
// but we trust our own length calculation
|
||||
picoprobe_error(" !!!!!!!! request (%u) and executed length (%u) differ\n",
|
||||
(unsigned)request_len, (unsigned)(resp_len >> 16));
|
||||
}
|
||||
|
||||
if (rx_len == request_len)
|
||||
{
|
||||
rx_len = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
memmove(RxDataBuffer, RxDataBuffer + request_len, rx_len - request_len);
|
||||
rx_len -= request_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // dap_task
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if OPT_CMSIS_DAPV2
|
||||
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;
|
||||
} // tud_vendor_control_xfer_cb
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if OPT_CMSIS_DAPV1
|
||||
static bool hid_swd_connected;
|
||||
static bool hid_swd_disconnect_requested;
|
||||
static TimerHandle_t timer_hid_disconnect = NULL;
|
||||
static void *timer_hid_disconnect_id;
|
||||
|
||||
|
||||
static void hid_disconnect(TimerHandle_t xTimer)
|
||||
{
|
||||
if (hid_swd_disconnect_requested && hid_swd_connected) {
|
||||
hid_swd_connected = false;
|
||||
picoprobe_info("=================================== DAPv1 disconnect target\n");
|
||||
led_state(LS_DAPV1_DISCONNECTED);
|
||||
sw_unlock("DAPv1");
|
||||
}
|
||||
} // hid_disconnect
|
||||
|
||||
|
||||
|
||||
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;
|
||||
|
||||
if (timer_hid_disconnect == NULL) {
|
||||
timer_hid_disconnect = xTimerCreate("timer_hid_disconnect", pdMS_TO_TICKS(1000), pdFALSE, timer_hid_disconnect_id,
|
||||
hid_disconnect);
|
||||
if (timer_hid_disconnect == NULL) {
|
||||
picoprobe_error("tud_hid_set_report_cb: cannot create timer_hid_disconnect\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
xTimerReset(timer_hid_disconnect, pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
//
|
||||
// initiate SWD connect / disconnect
|
||||
//
|
||||
if ( !hid_swd_connected && RxDataBuffer[0] != ID_DAP_Info) {
|
||||
if (sw_lock("DAPv1", true)) {
|
||||
hid_swd_connected = true;
|
||||
picoprobe_info("=================================== DAPv1 connect target\n");
|
||||
led_state(LS_DAPV1_CONNECTED);
|
||||
}
|
||||
}
|
||||
if (RxDataBuffer[0] == ID_DAP_Disconnect || RxDataBuffer[0] == ID_DAP_Info || RxDataBuffer[0] == ID_DAP_HostStatus) {
|
||||
hid_swd_disconnect_requested = true;
|
||||
|
||||
// this is the minimum version which should always work
|
||||
dap_packet_count = _DAP_PACKET_COUNT_HID;
|
||||
dap_packet_size = _DAP_PACKET_SIZE_HID;
|
||||
}
|
||||
else {
|
||||
hid_swd_disconnect_requested = false;
|
||||
}
|
||||
|
||||
//
|
||||
// execute request and send back response
|
||||
//
|
||||
if (hid_swd_connected || DAP_OfflineCommand(RxDataBuffer)) {
|
||||
#if 0
|
||||
// heavy debug output, set dap_packet_count=2 to stumble into the bug
|
||||
uint32_t request_len = DAP_GetCommandLength(RxDataBuffer, bufsize);
|
||||
picoprobe_info("< ");
|
||||
for (int i = 0; i < bufsize; ++i) {
|
||||
picoprobe_info_out(" %02x", RxDataBuffer[i]);
|
||||
if (i == request_len - 1) {
|
||||
picoprobe_info_out(" !!!!");
|
||||
}
|
||||
}
|
||||
picoprobe_info_out("\n");
|
||||
vTaskDelay(pdMS_TO_TICKS(30));
|
||||
uint32_t res = DAP_ExecuteCommand(RxDataBuffer, TxDataBuffer);
|
||||
picoprobe_info("> %lu %lu\n", res >> 16, res & 0xffff);
|
||||
#else
|
||||
DAP_ExecuteCommand(RxDataBuffer, TxDataBuffer);
|
||||
#endif
|
||||
tud_hid_report(0, TxDataBuffer, response_size);
|
||||
}
|
||||
} // tud_hid_set_report_cb
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
void dap_server_init(uint32_t task_prio)
|
||||
{
|
||||
picoprobe_debug("dap_server_init(%u)\n", (unsigned)task_prio);
|
||||
|
||||
#if OPT_CMSIS_DAPV2
|
||||
dap_events = xEventGroupCreate();
|
||||
xTaskCreate(dap_task, "CMSIS-DAPv2", configMINIMAL_STACK_SIZE, NULL, task_prio, &dap_taskhandle);
|
||||
#endif
|
||||
} // rtt_console_init
|
||||
45
src/cmsis-dap/dap_server.h
Executable file
45
src/cmsis-dap/dap_server.h
Executable file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Raspberry Pi (Trading) 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
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DAP_SERVER_H
|
||||
#define DAP_SERVER_H
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
void dap_server_init(uint32_t task_prio);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
8
src/daplink-pico/README.adoc
Executable file
8
src/daplink-pico/README.adoc
Executable file
@@ -0,0 +1,8 @@
|
||||
:imagesdir: doc/png
|
||||
:source-highlighter: rouge
|
||||
:toc:
|
||||
:toclevels: 5
|
||||
|
||||
# DAPLink adoptions for the probe
|
||||
|
||||
These modules fit the probe into the DAPLink infrastructure.
|
||||
332
src/main.c
332
src/main.c
@@ -29,7 +29,6 @@
|
||||
#include "timers.h"
|
||||
#include "event_groups.h"
|
||||
|
||||
#include <pico/stdlib.h>
|
||||
#ifdef TARGET_BOARD_PICO_W
|
||||
#include <pico/cyw43_arch.h>
|
||||
#endif
|
||||
@@ -61,12 +60,15 @@
|
||||
#if OPT_CDC_SYSVIEW
|
||||
#include "cdc/cdc_sysview.h"
|
||||
#endif
|
||||
#include "dap_util.h"
|
||||
#if OPT_CMSIS_DAPV2
|
||||
#include "cmsis-dap/dap_server.h"
|
||||
#endif
|
||||
#include "get_config.h"
|
||||
#include "led.h"
|
||||
#include "sw_lock.h"
|
||||
|
||||
#include "DAP_config.h"
|
||||
#include "DAP.h"
|
||||
#include "sw_lock.h"
|
||||
|
||||
#include "target_board.h" // DAPLink
|
||||
|
||||
@@ -116,34 +118,6 @@
|
||||
// maximum number of expected FreeRTOS task (used for uxTaskGetSystemState())
|
||||
#define TASK_MAX_CNT 15
|
||||
|
||||
/*
|
||||
* The following is part of a hack to make DAP_PACKET_COUNT a variable.
|
||||
* CMSIS-DAPv2 has better performance with 2 packets while
|
||||
* CMSIS-DAPv1 only works with one packet, at least with openocd which throws a
|
||||
* "CMSIS-DAP transfer count mismatch: expected 12, got 8" on flashing.
|
||||
* The correct packet count has to be set on connection.
|
||||
*
|
||||
* More notes: pyocd works with large packets only, if the packet count is one.
|
||||
* Additionally pyocd is instable if packet count > 1. Valid for pyocd 0.34.3.
|
||||
*/
|
||||
#define _DAP_PACKET_COUNT_OPENOCD 2
|
||||
#define _DAP_PACKET_SIZE_OPENOCD CFG_TUD_VENDOR_RX_BUFSIZE
|
||||
#define _DAP_PACKET_COUNT_PYOCD 1
|
||||
#define _DAP_PACKET_SIZE_PYOCD 1024 // pyocd does not like packets > 128 if COUNT != 1
|
||||
#define _DAP_PACKET_COUNT_UNKNOWN 1
|
||||
#define _DAP_PACKET_SIZE_UNKNOWN 64
|
||||
|
||||
#define _DAP_PACKET_COUNT_HID 1
|
||||
#define _DAP_PACKET_SIZE_HID 64
|
||||
|
||||
uint8_t dap_packet_count = _DAP_PACKET_COUNT_UNKNOWN;
|
||||
uint16_t dap_packet_size = _DAP_PACKET_SIZE_UNKNOWN;
|
||||
|
||||
#if OPT_CMSIS_DAPV1 || OPT_CMSIS_DAPV2
|
||||
static uint8_t TxDataBuffer[_DAP_PACKET_COUNT_OPENOCD * CFG_TUD_VENDOR_RX_BUFSIZE]; // maximum required size
|
||||
static uint8_t RxDataBuffer[_DAP_PACKET_COUNT_OPENOCD * CFG_TUD_VENDOR_RX_BUFSIZE]; // maximum required size
|
||||
#endif
|
||||
|
||||
// prios are critical and determine throughput
|
||||
#define LED_TASK_PRIO (tskIDLE_PRIORITY + 30) // simple task which may interrupt everything else for periodic blinking
|
||||
#define TUD_TASK_PRIO (tskIDLE_PRIORITY + 28) // high prio for TinyUSB
|
||||
@@ -158,10 +132,6 @@ uint16_t dap_packet_size = _DAP_PACKET_SIZE_UNKNOWN;
|
||||
#define RTT_CONSOLE_TASK_PRIO (tskIDLE_PRIORITY + 1) // target -> host via RTT, ATTENTION: this task can fully load the CPU depending on target RTT output
|
||||
|
||||
static TaskHandle_t tud_taskhandle;
|
||||
#if OPT_CMSIS_DAPV2
|
||||
static TaskHandle_t dap_taskhandle;
|
||||
#endif
|
||||
static EventGroupHandle_t dap_events;
|
||||
|
||||
|
||||
|
||||
@@ -256,148 +226,6 @@ void tud_cdc_tx_complete_cb(uint8_t itf)
|
||||
|
||||
|
||||
|
||||
#if OPT_CMSIS_DAPV2
|
||||
void tud_vendor_rx_cb(uint8_t itf)
|
||||
{
|
||||
if (itf == 0) {
|
||||
xEventGroupSetBits(dap_events, 0x01);
|
||||
}
|
||||
} // tud_vendor_rx_cb
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if OPT_CMSIS_DAPV2
|
||||
/**
|
||||
* CMSIS-DAP task.
|
||||
* Receive DAP requests, execute them via DAP_ExecuteCommand() and transmit the response.
|
||||
*
|
||||
* Problem zones:
|
||||
* - connect / disconnect: pyOCD does not send permanently requests if in gdbserver mode, OpenOCD does.
|
||||
* As a consequence "disconnect" has to be detected via the command stream. If the tool on host side
|
||||
* fails without a disconnect, the SWD connection is not freed (for MSC or RTT). To recover from this
|
||||
* situation either reset the probe or issue something like "pyocd reset -t rp2040"
|
||||
* - fingerprinting the host tool: this is for optimization of the OpenOCD connection, because OpenOCD
|
||||
* can handle big DAP packets and thus transfer is faster.
|
||||
* - ID_DAP_Disconnect / ID_DAP_Info / ID_DAP_HostStatus leads to an SWD disconnect if there is no other
|
||||
* command following within 1s. This is required, because "pyocd list" leads to tool detection without
|
||||
* connect/disconnect and thus otherwise tool detection would be stuck to "pyocd" for the next connection.
|
||||
*/
|
||||
void dap_task(void *ptr)
|
||||
{
|
||||
bool swd_connected = false;
|
||||
bool swd_disconnect_requested = false;
|
||||
uint32_t last_request_us = 0;
|
||||
uint32_t rx_len = 0;
|
||||
daptool_t tool = E_DAPTOOL_UNKNOWN;
|
||||
|
||||
dap_packet_count = _DAP_PACKET_COUNT_UNKNOWN;
|
||||
dap_packet_size = _DAP_PACKET_SIZE_UNKNOWN;
|
||||
for (;;) {
|
||||
// disconnect after 1s without data
|
||||
if (swd_disconnect_requested && time_us_32() - last_request_us > 1000000) {
|
||||
if (swd_connected) {
|
||||
swd_connected = false;
|
||||
picoprobe_info("=================================== DAPv2 disconnect target\n");
|
||||
led_state(LS_DAPV2_DISCONNECTED);
|
||||
sw_unlock("DAPv2");
|
||||
}
|
||||
swd_disconnect_requested = false;
|
||||
dap_packet_count = _DAP_PACKET_COUNT_UNKNOWN;
|
||||
dap_packet_size = _DAP_PACKET_SIZE_UNKNOWN;
|
||||
tool = DAP_FingerprintTool(NULL, 0);
|
||||
}
|
||||
|
||||
xEventGroupWaitBits(dap_events, 0x01, pdTRUE, pdFALSE, pdMS_TO_TICKS(100)); // TODO "pyocd reset -f 500000" does otherwise not disconnect
|
||||
|
||||
if (tud_vendor_available())
|
||||
{
|
||||
rx_len += tud_vendor_read(RxDataBuffer + rx_len, sizeof(RxDataBuffer));
|
||||
|
||||
if (rx_len != 0)
|
||||
{
|
||||
uint32_t request_len;
|
||||
|
||||
request_len = DAP_GetCommandLength(RxDataBuffer, rx_len);
|
||||
if (rx_len >= request_len)
|
||||
{
|
||||
last_request_us = time_us_32();
|
||||
// picoprobe_info("<<<(%lx) %d %d\n", request_len, RxDataBuffer[0], RxDataBuffer[1]);
|
||||
|
||||
//
|
||||
// try to find out which tool is connecting
|
||||
//
|
||||
if (tool == E_DAPTOOL_UNKNOWN) {
|
||||
tool = DAP_FingerprintTool(RxDataBuffer, request_len);
|
||||
if (tool == E_DAPTOOL_OPENOCD) {
|
||||
dap_packet_count = _DAP_PACKET_COUNT_OPENOCD;
|
||||
dap_packet_size = _DAP_PACKET_SIZE_OPENOCD;
|
||||
}
|
||||
else if (tool == E_DAPTOOL_PYOCD) {
|
||||
dap_packet_count = _DAP_PACKET_COUNT_PYOCD;
|
||||
dap_packet_size = _DAP_PACKET_SIZE_PYOCD;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// initiate SWD connect / disconnect
|
||||
//
|
||||
if ( !swd_connected && RxDataBuffer[0] != ID_DAP_Info) {
|
||||
if (sw_lock("DAPv2", true)) {
|
||||
swd_connected = true;
|
||||
picoprobe_info("=================================== DAPv2 connect target, host %s\n",
|
||||
(tool == E_DAPTOOL_OPENOCD) ? "OpenOCD with two big buffers" :
|
||||
((tool == E_DAPTOOL_PYOCD) ? "pyOCD with single big buffer" : "UNKNOWN"));
|
||||
led_state(LS_DAPV2_CONNECTED);
|
||||
}
|
||||
}
|
||||
if (RxDataBuffer[0] == ID_DAP_Disconnect || RxDataBuffer[0] == ID_DAP_Info || RxDataBuffer[0] == ID_DAP_HostStatus) {
|
||||
swd_disconnect_requested = true;
|
||||
}
|
||||
else {
|
||||
swd_disconnect_requested = false;
|
||||
}
|
||||
|
||||
//
|
||||
// execute request and send back response
|
||||
//
|
||||
if (swd_connected || DAP_OfflineCommand(RxDataBuffer))
|
||||
{
|
||||
uint32_t resp_len;
|
||||
|
||||
resp_len = DAP_ExecuteCommand(RxDataBuffer, TxDataBuffer);
|
||||
// picoprobe_info(">>>(%lx) %d %d %d %d\n", resp_len, TxDataBuffer[0], TxDataBuffer[1], TxDataBuffer[2], TxDataBuffer[3]);
|
||||
|
||||
tud_vendor_write(TxDataBuffer, resp_len & 0xffff);
|
||||
tud_vendor_flush();
|
||||
|
||||
if (request_len != (resp_len >> 16))
|
||||
{
|
||||
// there is a bug in CMSIS-DAP, see https://github.com/ARM-software/CMSIS_5/pull/1503
|
||||
// but we trust our own length calculation
|
||||
picoprobe_error(" !!!!!!!! request (%u) and executed length (%u) differ\n",
|
||||
(unsigned)request_len, (unsigned)(resp_len >> 16));
|
||||
}
|
||||
|
||||
if (rx_len == request_len)
|
||||
{
|
||||
rx_len = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
memmove(RxDataBuffer, RxDataBuffer + request_len, rx_len - request_len);
|
||||
rx_len -= request_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // dap_task
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if configGENERATE_RUN_TIME_STATS
|
||||
static uint32_t tusb_count;
|
||||
static TimerHandle_t timer_task_stat;
|
||||
@@ -585,7 +413,7 @@ void usb_thread(void *ptr)
|
||||
#endif
|
||||
|
||||
#if OPT_CMSIS_DAPV2
|
||||
xTaskCreate(dap_task, "CMSIS-DAPv2", configMINIMAL_STACK_SIZE, NULL, DAPV2_TASK_PRIO, &dap_taskhandle);
|
||||
dap_server_init(DAPV2_TASK_PRIO);
|
||||
#endif
|
||||
|
||||
#if configGENERATE_RUN_TIME_STATS
|
||||
@@ -704,8 +532,6 @@ int main(void)
|
||||
#endif
|
||||
picoprobe_info("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
|
||||
|
||||
dap_events = xEventGroupCreate();
|
||||
|
||||
// it seems that TinyUSB does not like affinity setting in its thread, so the affinity of the USB thread is corrected in the task itself
|
||||
xTaskCreate(usb_thread, "TinyUSB Main", 4096, NULL, TUD_TASK_PRIO, &tud_taskhandle);
|
||||
vTaskStartScheduler();
|
||||
@@ -715,148 +541,6 @@ int main(void)
|
||||
|
||||
|
||||
|
||||
#if OPT_CMSIS_DAPV1
|
||||
static bool hid_swd_connected;
|
||||
static bool hid_swd_disconnect_requested;
|
||||
static TimerHandle_t timer_hid_disconnect = NULL;
|
||||
static void *timer_hid_disconnect_id;
|
||||
|
||||
|
||||
static void hid_disconnect(TimerHandle_t xTimer)
|
||||
{
|
||||
if (hid_swd_disconnect_requested && hid_swd_connected) {
|
||||
hid_swd_connected = false;
|
||||
picoprobe_info("=================================== DAPv1 disconnect target\n");
|
||||
led_state(LS_DAPV1_DISCONNECTED);
|
||||
sw_unlock("DAPv1");
|
||||
}
|
||||
} // hid_disconnect
|
||||
|
||||
|
||||
|
||||
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;
|
||||
|
||||
if (timer_hid_disconnect == NULL) {
|
||||
timer_hid_disconnect = xTimerCreate("timer_hid_disconnect", pdMS_TO_TICKS(1000), pdFALSE, timer_hid_disconnect_id,
|
||||
hid_disconnect);
|
||||
if (timer_hid_disconnect == NULL) {
|
||||
picoprobe_error("tud_hid_set_report_cb: cannot create timer_hid_disconnect\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
xTimerReset(timer_hid_disconnect, pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
//
|
||||
// initiate SWD connect / disconnect
|
||||
//
|
||||
if ( !hid_swd_connected && RxDataBuffer[0] != ID_DAP_Info) {
|
||||
if (sw_lock("DAPv1", true)) {
|
||||
hid_swd_connected = true;
|
||||
picoprobe_info("=================================== DAPv1 connect target\n");
|
||||
led_state(LS_DAPV1_CONNECTED);
|
||||
}
|
||||
}
|
||||
if (RxDataBuffer[0] == ID_DAP_Disconnect || RxDataBuffer[0] == ID_DAP_Info || RxDataBuffer[0] == ID_DAP_HostStatus) {
|
||||
hid_swd_disconnect_requested = true;
|
||||
|
||||
// this is the minimum version which should always work
|
||||
dap_packet_count = _DAP_PACKET_COUNT_HID;
|
||||
dap_packet_size = _DAP_PACKET_SIZE_HID;
|
||||
}
|
||||
else {
|
||||
hid_swd_disconnect_requested = false;
|
||||
}
|
||||
|
||||
//
|
||||
// execute request and send back response
|
||||
//
|
||||
if (hid_swd_connected || DAP_OfflineCommand(RxDataBuffer)) {
|
||||
#if 0
|
||||
// heavy debug output, set dap_packet_count=2 to stumble into the bug
|
||||
uint32_t request_len = DAP_GetCommandLength(RxDataBuffer, bufsize);
|
||||
picoprobe_info("< ");
|
||||
for (int i = 0; i < bufsize; ++i) {
|
||||
picoprobe_info_out(" %02x", RxDataBuffer[i]);
|
||||
if (i == request_len - 1) {
|
||||
picoprobe_info_out(" !!!!");
|
||||
}
|
||||
}
|
||||
picoprobe_info_out("\n");
|
||||
vTaskDelay(pdMS_TO_TICKS(30));
|
||||
uint32_t res = DAP_ExecuteCommand(RxDataBuffer, TxDataBuffer);
|
||||
picoprobe_info("> %lu %lu\n", res >> 16, res & 0xffff);
|
||||
#else
|
||||
DAP_ExecuteCommand(RxDataBuffer, TxDataBuffer);
|
||||
#endif
|
||||
tud_hid_report(0, TxDataBuffer, response_size);
|
||||
}
|
||||
} // tud_hid_set_report_cb
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if OPT_CMSIS_DAPV2
|
||||
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;
|
||||
} // tud_vendor_control_xfer_cb
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
void vApplicationTickHook (void)
|
||||
{
|
||||
}
|
||||
@@ -865,12 +549,12 @@ void vApplicationTickHook (void)
|
||||
|
||||
void vApplicationStackOverflowHook(TaskHandle_t Task, char *pcTaskName)
|
||||
{
|
||||
panic("stack overflow (not the helpful kind) for %s\n", *pcTaskName);
|
||||
panic("stack overflow (not the helpful kind) for %s\n", *pcTaskName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void vApplicationMallocFailedHook(void)
|
||||
{
|
||||
panic("Malloc Failed\n");
|
||||
panic("Malloc Failed\n");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user