1061 lines
26 KiB
C
1061 lines
26 KiB
C
/*
|
|
* Copyright (c) 2018-2019 PHYTEC Messtechnik GmbH
|
|
* Copyright (c) 2023 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/*
|
|
* This file is based on DAP.c from CMSIS-DAP Source (Revision: V2.0.0)
|
|
* https://github.com/ARM-software/CMSIS_5/tree/develop/CMSIS/DAP/Firmware
|
|
* Copyright (c) 2013-2017 ARM Limited. All rights reserved.
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/init.h>
|
|
#include <zephyr/sys/byteorder.h>
|
|
#include <zephyr/drivers/swdp.h>
|
|
#include <stdint.h>
|
|
|
|
#include <cmsis_dap.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(dap, CONFIG_DAP_LOG_LEVEL);
|
|
|
|
#define DAP_STATE_CONNECTED 0
|
|
|
|
struct dap_context {
|
|
struct device *swdp_dev;
|
|
atomic_t state;
|
|
uint8_t debug_port;
|
|
uint8_t capabilities;
|
|
uint16_t pkt_size;
|
|
struct {
|
|
/* Idle cycles after transfer */
|
|
uint8_t idle_cycles;
|
|
/* Number of retries after WAIT response */
|
|
uint16_t retry_count;
|
|
/* Number of retries if read value does not match */
|
|
uint16_t match_retry;
|
|
/* Match Mask */
|
|
uint32_t match_mask;
|
|
} transfer;
|
|
};
|
|
|
|
static struct dap_context dap_ctx[1];
|
|
|
|
BUILD_ASSERT(sizeof(CONFIG_CMSIS_DAP_PROBE_VENDOR) <=
|
|
MIN(CONFIG_CMSIS_DAP_PACKET_SIZE - 2, UINT8_MAX - 2),
|
|
"PROBE_VENDOR string is too long.");
|
|
BUILD_ASSERT(sizeof(CONFIG_CMSIS_DAP_PROBE_NAME) <=
|
|
MIN(CONFIG_CMSIS_DAP_PACKET_SIZE - 2, UINT8_MAX - 2),
|
|
"PROBE_NAME string is too long.");
|
|
BUILD_ASSERT(sizeof(CONFIG_CMSIS_DAP_BOARD_VENDOR) <=
|
|
MIN(CONFIG_CMSIS_DAP_PACKET_SIZE - 2, UINT8_MAX - 2),
|
|
"BOARD_VENDOR string is too long.");
|
|
BUILD_ASSERT(sizeof(CONFIG_CMSIS_DAP_BOARD_NAME) <=
|
|
MIN(CONFIG_CMSIS_DAP_PACKET_SIZE - 2, UINT8_MAX - 2),
|
|
"BOARD_NAME string is too long.");
|
|
BUILD_ASSERT(sizeof(CONFIG_CMSIS_DAP_DEVICE_VENDOR) <=
|
|
MIN(CONFIG_CMSIS_DAP_PACKET_SIZE - 2, UINT8_MAX - 2),
|
|
"DEVICE_VENDOR string is too long.");
|
|
BUILD_ASSERT(sizeof(CONFIG_CMSIS_DAP_DEVICE_NAME) <=
|
|
MIN(CONFIG_CMSIS_DAP_PACKET_SIZE - 2, UINT8_MAX - 2),
|
|
"DEVICE_NAME string is too long.");
|
|
|
|
/* Get DAP Information */
|
|
static uint16_t dap_info(struct dap_context *const ctx,
|
|
const uint8_t *const request,
|
|
uint8_t *const response)
|
|
{
|
|
uint8_t *info = response + 1;
|
|
uint8_t id = request[0];
|
|
uint8_t length = 0U;
|
|
|
|
switch (id) {
|
|
case DAP_ID_VENDOR:
|
|
LOG_DBG("ID_VENDOR");
|
|
memcpy(info, CONFIG_CMSIS_DAP_PROBE_VENDOR,
|
|
sizeof(CONFIG_CMSIS_DAP_PROBE_VENDOR));
|
|
length = sizeof(CONFIG_CMSIS_DAP_PROBE_VENDOR);
|
|
break;
|
|
case DAP_ID_PRODUCT:
|
|
LOG_DBG("ID_PRODUCT");
|
|
memcpy(info, CONFIG_CMSIS_DAP_PROBE_NAME,
|
|
sizeof(CONFIG_CMSIS_DAP_PROBE_NAME));
|
|
length = sizeof(CONFIG_CMSIS_DAP_PROBE_NAME);
|
|
break;
|
|
case DAP_ID_SER_NUM:
|
|
/* optional to implement */
|
|
LOG_DBG("ID_SER_NUM unsupported");
|
|
break;
|
|
case DAP_ID_FW_VER:
|
|
LOG_DBG("ID_FW_VER");
|
|
memcpy(info, DAP_FW_VER, sizeof(DAP_FW_VER));
|
|
length = sizeof(DAP_FW_VER);
|
|
break;
|
|
case DAP_ID_DEVICE_VENDOR:
|
|
LOG_DBG("ID_DEVICE_VENDOR");
|
|
memcpy(info, CONFIG_CMSIS_DAP_DEVICE_VENDOR,
|
|
sizeof(CONFIG_CMSIS_DAP_DEVICE_VENDOR));
|
|
length = sizeof(CONFIG_CMSIS_DAP_DEVICE_VENDOR);
|
|
break;
|
|
case DAP_ID_DEVICE_NAME:
|
|
LOG_DBG("ID_DEVICE_NAME");
|
|
memcpy(info, CONFIG_CMSIS_DAP_DEVICE_NAME,
|
|
sizeof(CONFIG_CMSIS_DAP_DEVICE_NAME));
|
|
length = sizeof(CONFIG_CMSIS_DAP_DEVICE_NAME);
|
|
break;
|
|
case DAP_ID_BOARD_VENDOR:
|
|
LOG_DBG("ID_BOARD_VENDOR");
|
|
memcpy(info, CONFIG_CMSIS_DAP_BOARD_VENDOR,
|
|
sizeof(CONFIG_CMSIS_DAP_BOARD_VENDOR));
|
|
length = sizeof(CONFIG_CMSIS_DAP_BOARD_VENDOR);
|
|
break;
|
|
case DAP_ID_BOARD_NAME:
|
|
memcpy(info, CONFIG_CMSIS_DAP_BOARD_NAME,
|
|
sizeof(CONFIG_CMSIS_DAP_BOARD_NAME));
|
|
length = sizeof(CONFIG_CMSIS_DAP_BOARD_NAME);
|
|
LOG_DBG("ID_BOARD_NAME");
|
|
break;
|
|
case DAP_ID_PRODUCT_FW_VER:
|
|
/* optional to implement */
|
|
LOG_DBG("ID_PRODUCT_FW_VER unsupported");
|
|
break;
|
|
case DAP_ID_CAPABILITIES:
|
|
info[0] = ctx->capabilities;
|
|
LOG_DBG("ID_CAPABILITIES 0x%0x", info[0]);
|
|
length = 1U;
|
|
break;
|
|
case DAP_ID_TIMESTAMP_CLOCK:
|
|
LOG_DBG("ID_TIMESTAMP_CLOCK unsupported");
|
|
break;
|
|
case DAP_ID_UART_RX_BUFFER_SIZE:
|
|
LOG_DBG("ID_UART_RX_BUFFER_SIZE unsupported");
|
|
break;
|
|
case DAP_ID_UART_TX_BUFFER_SIZE:
|
|
LOG_DBG("ID_UART_TX_BUFFER_SIZE unsupported");
|
|
break;
|
|
case DAP_ID_SWO_BUFFER_SIZE:
|
|
LOG_DBG("ID_SWO_BUFFER_SIZE unsupported");
|
|
break;
|
|
case DAP_ID_PACKET_SIZE:
|
|
LOG_DBG("ID_PACKET_SIZE");
|
|
sys_put_le16(ctx->pkt_size, &info[0]);
|
|
length = 2U;
|
|
break;
|
|
case DAP_ID_PACKET_COUNT:
|
|
LOG_DBG("ID_PACKET_COUNT");
|
|
info[0] = CONFIG_CMSIS_DAP_PACKET_COUNT;
|
|
length = 1U;
|
|
break;
|
|
default:
|
|
LOG_DBG("unsupported ID");
|
|
break;
|
|
}
|
|
|
|
response[0] = length;
|
|
|
|
return length + 1U;
|
|
}
|
|
|
|
/* Process Host Status command and prepare response */
|
|
static uint16_t dap_host_status(struct dap_context *const ctx,
|
|
const uint8_t *const request,
|
|
uint8_t *const response)
|
|
{
|
|
switch (request[0]) {
|
|
case DAP_DEBUGGER_CONNECTED:
|
|
if (request[1]) {
|
|
LOG_INF("Debugger connected");
|
|
} else {
|
|
LOG_INF("Debugger disconnected");
|
|
}
|
|
break;
|
|
case DAP_TARGET_RUNNING:
|
|
LOG_DBG("unsupported");
|
|
break;
|
|
default:
|
|
*response = DAP_ERROR;
|
|
return 1U;
|
|
}
|
|
|
|
response[0] = DAP_OK;
|
|
return 1U;
|
|
}
|
|
|
|
/* Process Connect command and prepare response */
|
|
static uint16_t dap_connect(struct dap_context *const ctx,
|
|
const uint8_t *const request,
|
|
uint8_t *const response)
|
|
{
|
|
const struct swdp_api *api = ctx->swdp_dev->api;
|
|
uint8_t port;
|
|
|
|
if (request[0] == DAP_PORT_AUTODETECT) {
|
|
port = DAP_PORT_SWD;
|
|
} else {
|
|
port = request[0];
|
|
}
|
|
|
|
switch (port) {
|
|
case DAP_PORT_SWD:
|
|
LOG_INF("port swd");
|
|
ctx->debug_port = DAP_PORT_SWD;
|
|
|
|
if (atomic_test_and_set_bit(&ctx->state,
|
|
DAP_STATE_CONNECTED)) {
|
|
LOG_ERR("DAP device is already connected");
|
|
break;
|
|
}
|
|
|
|
api->swdp_port_on(ctx->swdp_dev);
|
|
break;
|
|
case DAP_PORT_JTAG:
|
|
LOG_ERR("port unsupported");
|
|
port = DAP_ERROR;
|
|
break;
|
|
default:
|
|
LOG_DBG("port disabled");
|
|
port = DAP_PORT_DISABLED;
|
|
break;
|
|
}
|
|
|
|
response[0] = port;
|
|
return 1U;
|
|
}
|
|
|
|
/* Process Disconnect command and prepare response */
|
|
static uint16_t dap_disconnect(struct dap_context *const ctx,
|
|
uint8_t *const response)
|
|
{
|
|
const struct swdp_api *api = ctx->swdp_dev->api;
|
|
|
|
LOG_DBG("");
|
|
|
|
ctx->debug_port = DAP_PORT_DISABLED;
|
|
|
|
if (atomic_test_bit(&ctx->state, DAP_STATE_CONNECTED)) {
|
|
api->swdp_port_off(ctx->swdp_dev);
|
|
} else {
|
|
LOG_WRN("DAP device is not connected");
|
|
}
|
|
|
|
response[0] = DAP_OK;
|
|
atomic_clear_bit(&ctx->state, DAP_STATE_CONNECTED);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Process Delay command and prepare response */
|
|
static uint16_t dap_delay(struct dap_context *const ctx,
|
|
const uint8_t *const request,
|
|
uint8_t *const response)
|
|
{
|
|
uint16_t delay = sys_get_le16(&request[0]);
|
|
|
|
LOG_DBG("dap delay %u ms", delay);
|
|
|
|
k_busy_wait(delay * USEC_PER_MSEC);
|
|
response[0] = DAP_OK;
|
|
|
|
return 1U;
|
|
}
|
|
|
|
/* Process Reset Target command and prepare response */
|
|
static uint16_t dap_reset_target(struct dap_context *const ctx,
|
|
uint8_t *const response)
|
|
{
|
|
response[0] = DAP_OK;
|
|
response[1] = 0U;
|
|
LOG_WRN("unsupported");
|
|
|
|
return 2;
|
|
}
|
|
|
|
/* Process SWJ Pins command and prepare response */
|
|
static uint16_t dap_swj_pins(struct dap_context *const ctx,
|
|
const uint8_t *const request,
|
|
uint8_t *const response)
|
|
{
|
|
const struct swdp_api *api = ctx->swdp_dev->api;
|
|
uint8_t value = request[0];
|
|
uint8_t select = request[1];
|
|
uint32_t wait = sys_get_le32(&request[2]);
|
|
k_timepoint_t end = sys_timepoint_calc(K_USEC(wait));
|
|
uint8_t state;
|
|
|
|
if (!atomic_test_bit(&ctx->state, DAP_STATE_CONNECTED)) {
|
|
LOG_ERR("DAP device is not connected");
|
|
response[0] = DAP_ERROR;
|
|
return 1U;
|
|
}
|
|
|
|
/* Skip if nothing selected. */
|
|
if (select) {
|
|
api->swdp_set_pins(ctx->swdp_dev, select, value);
|
|
}
|
|
|
|
do {
|
|
api->swdp_get_pins(ctx->swdp_dev, &state);
|
|
LOG_INF("select 0x%02x, value 0x%02x, wait %u, state 0x%02x",
|
|
select, value, wait, state);
|
|
if ((value & select) == (state & select)) {
|
|
LOG_DBG("swdp_get_pins succeeded before timeout");
|
|
break;
|
|
}
|
|
} while (!sys_timepoint_expired(end));
|
|
|
|
response[0] = state;
|
|
|
|
return sizeof(state);
|
|
}
|
|
|
|
/* Process SWJ Clock command and prepare response */
|
|
static uint16_t dap_swj_clock(struct dap_context *const ctx,
|
|
const uint8_t *const request,
|
|
uint8_t *const response)
|
|
{
|
|
const struct swdp_api *api = ctx->swdp_dev->api;
|
|
uint32_t clk = sys_get_le32(&request[0]);
|
|
|
|
LOG_DBG("clock %d", clk);
|
|
|
|
if (atomic_test_bit(&ctx->state, DAP_STATE_CONNECTED)) {
|
|
if (clk) {
|
|
api->swdp_set_clock(ctx->swdp_dev, clk);
|
|
response[0] = DAP_OK;
|
|
} else {
|
|
response[0] = DAP_ERROR;
|
|
}
|
|
} else {
|
|
LOG_WRN("DAP device is not connected");
|
|
response[0] = DAP_OK;
|
|
}
|
|
|
|
return 1U;
|
|
}
|
|
|
|
/* Process SWJ Sequence command and prepare response */
|
|
static uint16_t dap_swj_sequence(struct dap_context *const ctx,
|
|
const uint8_t *const request,
|
|
uint8_t *const response)
|
|
{
|
|
const struct swdp_api *api = ctx->swdp_dev->api;
|
|
uint16_t count = request[0];
|
|
|
|
LOG_DBG("count %u", count);
|
|
|
|
if (count == 0U) {
|
|
count = 256U;
|
|
}
|
|
|
|
if (!atomic_test_bit(&ctx->state, DAP_STATE_CONNECTED)) {
|
|
LOG_ERR("DAP device is not connected");
|
|
response[0] = DAP_ERROR;
|
|
return 1U;
|
|
}
|
|
|
|
api->swdp_output_sequence(ctx->swdp_dev, count, &request[1]);
|
|
response[0] = DAP_OK;
|
|
|
|
return 1U;
|
|
}
|
|
|
|
/* Process SWD Configure command and prepare response */
|
|
static uint16_t dap_swdp_configure(struct dap_context *const ctx,
|
|
const uint8_t *const request,
|
|
uint8_t *const response)
|
|
{
|
|
const struct swdp_api *api = ctx->swdp_dev->api;
|
|
uint8_t turnaround = (request[0] & 0x03U) + 1U;
|
|
bool data_phase = (request[0] & 0x04U) ? true : false;
|
|
|
|
if (!atomic_test_bit(&ctx->state, DAP_STATE_CONNECTED)) {
|
|
LOG_ERR("DAP device is not connected");
|
|
response[0] = DAP_ERROR;
|
|
return 1U;
|
|
}
|
|
|
|
api->swdp_configure(ctx->swdp_dev, turnaround, data_phase);
|
|
response[0] = DAP_OK;
|
|
|
|
return 1U;
|
|
}
|
|
|
|
/* Process Transfer Configure command and prepare response */
|
|
static uint16_t dap_transfer_cfg(struct dap_context *const ctx,
|
|
const uint8_t *const request,
|
|
uint8_t *const response)
|
|
{
|
|
|
|
ctx->transfer.idle_cycles = request[0];
|
|
ctx->transfer.retry_count = sys_get_le16(&request[1]);
|
|
ctx->transfer.match_retry = sys_get_le16(&request[3]);
|
|
LOG_DBG("idle_cycles %d, retry_count %d, match_retry %d",
|
|
ctx->transfer.idle_cycles,
|
|
ctx->transfer.retry_count,
|
|
ctx->transfer.match_retry);
|
|
|
|
response[0] = DAP_OK;
|
|
return 1U;
|
|
}
|
|
|
|
static inline uint8_t do_swdp_transfer(struct dap_context *const ctx,
|
|
const uint8_t req_val,
|
|
uint32_t *data)
|
|
{
|
|
const struct swdp_api *api = ctx->swdp_dev->api;
|
|
uint32_t retry = ctx->transfer.retry_count;
|
|
uint8_t rspns_val;
|
|
|
|
do {
|
|
api->swdp_transfer(ctx->swdp_dev,
|
|
req_val,
|
|
data,
|
|
ctx->transfer.idle_cycles,
|
|
&rspns_val);
|
|
} while ((rspns_val == SWDP_ACK_WAIT) && retry--);
|
|
|
|
return rspns_val;
|
|
}
|
|
|
|
static uint8_t swdp_transfer_match(struct dap_context *const ctx,
|
|
const uint8_t req_val,
|
|
const uint32_t match_val)
|
|
{
|
|
uint32_t match_retry = ctx->transfer.match_retry;
|
|
uint32_t data;
|
|
uint8_t rspns_val;
|
|
|
|
if (req_val & SWDP_REQUEST_APnDP) {
|
|
/* Post AP read, result will be returned on the next transfer */
|
|
rspns_val = do_swdp_transfer(ctx, req_val, NULL);
|
|
if (rspns_val != SWDP_ACK_OK) {
|
|
return rspns_val;
|
|
}
|
|
}
|
|
|
|
do {
|
|
/*
|
|
* Read register until its value matches
|
|
* or retry counter expires
|
|
*/
|
|
rspns_val = do_swdp_transfer(ctx, req_val, &data);
|
|
if (rspns_val != SWDP_ACK_OK) {
|
|
return rspns_val;
|
|
}
|
|
|
|
} while (((data & ctx->transfer.match_mask) != match_val) &&
|
|
match_retry--);
|
|
|
|
if ((data & ctx->transfer.match_mask) != match_val) {
|
|
rspns_val |= DAP_TRANSFER_MISMATCH;
|
|
}
|
|
|
|
return rspns_val;
|
|
}
|
|
|
|
/*
|
|
* Process SWD Transfer command and prepare response
|
|
* pyOCD counterpart is _encode_transfer_data.
|
|
* Packet format: one byte DAP_index (ignored)
|
|
* one bytes transfer_count
|
|
* following by number transfer_count pairs of
|
|
* one byte request (register)
|
|
* four byte data (for write request only)
|
|
*/
|
|
static uint16_t dap_swdp_transfer(struct dap_context *const ctx,
|
|
const uint8_t *const request,
|
|
uint8_t *const response)
|
|
{
|
|
uint8_t *rspns_buf;
|
|
const uint8_t *req_buf;
|
|
uint8_t rspns_cnt = 0;
|
|
uint8_t rspns_val = 0;
|
|
bool post_read = false;
|
|
uint32_t check_write = 0;
|
|
uint8_t req_cnt;
|
|
uint8_t req_val;
|
|
uint32_t match_val;
|
|
uint32_t data;
|
|
|
|
/* Ignore DAP index request[0] */
|
|
req_cnt = request[1];
|
|
req_buf = request + sizeof(req_cnt) + 1;
|
|
rspns_buf = response + (sizeof(rspns_cnt) + sizeof(rspns_val));
|
|
|
|
for (; req_cnt; req_cnt--) {
|
|
req_val = *req_buf++;
|
|
if (req_val & SWDP_REQUEST_RnW) {
|
|
/* Read register */
|
|
if (post_read) {
|
|
/*
|
|
* Read was posted before, read previous AP
|
|
* data or post next AP read.
|
|
*/
|
|
if ((req_val & (SWDP_REQUEST_APnDP |
|
|
DAP_TRANSFER_MATCH_VALUE)) !=
|
|
SWDP_REQUEST_APnDP) {
|
|
req_val = DP_RDBUFF | SWDP_REQUEST_RnW;
|
|
post_read = false;
|
|
}
|
|
|
|
rspns_val = do_swdp_transfer(ctx, req_val, &data);
|
|
if (rspns_val != SWDP_ACK_OK) {
|
|
break;
|
|
}
|
|
|
|
/* Store previous AP data */
|
|
sys_put_le32(data, rspns_buf);
|
|
rspns_buf += sizeof(data);
|
|
}
|
|
if (req_val & DAP_TRANSFER_MATCH_VALUE) {
|
|
LOG_INF("match value read");
|
|
/* Read with value match */
|
|
match_val = sys_get_le32(req_buf);
|
|
req_buf += sizeof(match_val);
|
|
|
|
rspns_val = swdp_transfer_match(ctx, req_val, match_val);
|
|
if (rspns_val != SWDP_ACK_OK) {
|
|
break;
|
|
}
|
|
|
|
} else if (req_val & SWDP_REQUEST_APnDP) {
|
|
/* Normal read */
|
|
if (!post_read) {
|
|
/* Post AP read */
|
|
rspns_val = do_swdp_transfer(ctx, req_val, NULL);
|
|
if (rspns_val != SWDP_ACK_OK) {
|
|
break;
|
|
}
|
|
post_read = true;
|
|
}
|
|
} else {
|
|
/* Read DP register */
|
|
rspns_val = do_swdp_transfer(ctx, req_val, &data);
|
|
if (rspns_val != SWDP_ACK_OK) {
|
|
break;
|
|
}
|
|
/* Store data */
|
|
sys_put_le32(data, rspns_buf);
|
|
rspns_buf += sizeof(data);
|
|
}
|
|
check_write = 0U;
|
|
} else {
|
|
/* Write register */
|
|
if (post_read) {
|
|
/* Read previous data */
|
|
rspns_val = do_swdp_transfer(ctx,
|
|
DP_RDBUFF | SWDP_REQUEST_RnW,
|
|
&data);
|
|
if (rspns_val != SWDP_ACK_OK) {
|
|
break;
|
|
}
|
|
|
|
/* Store previous data */
|
|
sys_put_le32(data, rspns_buf);
|
|
rspns_buf += sizeof(data);
|
|
post_read = false;
|
|
}
|
|
/* Load data */
|
|
data = sys_get_le32(req_buf);
|
|
req_buf += sizeof(data);
|
|
if (req_val & DAP_TRANSFER_MATCH_MASK) {
|
|
/* Write match mask */
|
|
ctx->transfer.match_mask = data;
|
|
rspns_val = SWDP_ACK_OK;
|
|
} else {
|
|
/* Write DP/AP register */
|
|
rspns_val = do_swdp_transfer(ctx, req_val, &data);
|
|
if (rspns_val != SWDP_ACK_OK) {
|
|
break;
|
|
}
|
|
|
|
check_write = 1U;
|
|
}
|
|
}
|
|
rspns_cnt++;
|
|
}
|
|
|
|
if (rspns_val == SWDP_ACK_OK) {
|
|
if (post_read) {
|
|
/* Read previous data */
|
|
rspns_val = do_swdp_transfer(ctx,
|
|
DP_RDBUFF | SWDP_REQUEST_RnW,
|
|
&data);
|
|
if (rspns_val != SWDP_ACK_OK) {
|
|
goto end;
|
|
}
|
|
|
|
/* Store previous data */
|
|
sys_put_le32(data, rspns_buf);
|
|
rspns_buf += sizeof(data);
|
|
} else if (check_write) {
|
|
/* Check last write */
|
|
rspns_val = do_swdp_transfer(ctx,
|
|
DP_RDBUFF | SWDP_REQUEST_RnW,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
end:
|
|
response[0] = rspns_cnt;
|
|
response[1] = rspns_val;
|
|
|
|
return (rspns_buf - response);
|
|
}
|
|
|
|
/* Delegate DAP Transfer command */
|
|
static uint16_t dap_transfer(struct dap_context *const ctx,
|
|
const uint8_t *const request,
|
|
uint8_t *const response)
|
|
{
|
|
uint16_t retval;
|
|
|
|
if (!atomic_test_bit(&ctx->state, DAP_STATE_CONNECTED)) {
|
|
LOG_ERR("DAP device is not connected");
|
|
response[0] = DAP_ERROR;
|
|
return 1U;
|
|
}
|
|
|
|
switch (ctx->debug_port) {
|
|
case DAP_PORT_SWD:
|
|
retval = dap_swdp_transfer(ctx, request, response);
|
|
break;
|
|
case DAP_PORT_JTAG:
|
|
default:
|
|
LOG_ERR("port unsupported");
|
|
response[0] = DAP_ERROR;
|
|
retval = 1U;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static uint16_t dap_swdp_sequence(struct dap_context *const ctx,
|
|
const uint8_t *const request,
|
|
uint8_t *const response)
|
|
{
|
|
const struct swdp_api *api = ctx->swdp_dev->api;
|
|
const uint8_t *request_data = request + 1;
|
|
uint8_t *response_data = response + 1;
|
|
uint8_t count = request[0];
|
|
uint8_t num_cycles;
|
|
uint32_t num_bytes;
|
|
bool input;
|
|
|
|
switch (ctx->debug_port) {
|
|
case DAP_PORT_SWD:
|
|
response[0] = DAP_OK;
|
|
break;
|
|
case DAP_PORT_JTAG:
|
|
default:
|
|
LOG_ERR("port unsupported");
|
|
response[0] = DAP_ERROR;
|
|
return 1U;
|
|
}
|
|
|
|
for (size_t i = 0; i < count; ++i) {
|
|
input = *request_data & BIT(7);
|
|
num_cycles = *request_data & BIT_MASK(7);
|
|
num_bytes = (num_cycles + 7) >> 3; /* rounded up to full bytes */
|
|
|
|
if (num_cycles == 0) {
|
|
num_cycles = 64;
|
|
}
|
|
|
|
request_data += 1;
|
|
|
|
if (input) {
|
|
api->swdp_input_sequence(ctx->swdp_dev, num_cycles, response_data);
|
|
response_data += num_bytes;
|
|
} else {
|
|
api->swdp_output_sequence(ctx->swdp_dev, num_cycles, request_data);
|
|
request_data += num_bytes;
|
|
}
|
|
}
|
|
|
|
return response_data - response;
|
|
}
|
|
|
|
/*
|
|
* Process SWD DAP_TransferBlock command and prepare response.
|
|
* pyOCD counterpart is _encode_transfer_block_data.
|
|
* Packet format: one byte DAP_index (ignored)
|
|
* two bytes transfer_count
|
|
* one byte block_request (register)
|
|
* data[transfer_count * sizeof(uint32_t)]
|
|
*/
|
|
static uint16_t dap_swdp_transferblock(struct dap_context *const ctx,
|
|
const uint8_t *const request,
|
|
uint8_t *const response)
|
|
{
|
|
uint32_t data;
|
|
uint8_t *rspns_buf;
|
|
const uint8_t *req_buf;
|
|
uint16_t rspns_cnt = 0;
|
|
uint16_t req_cnt;
|
|
uint8_t rspns_val = 0;
|
|
uint8_t req_val;
|
|
|
|
req_cnt = sys_get_le16(&request[1]);
|
|
req_val = request[3];
|
|
req_buf = request + (sizeof(req_cnt) + sizeof(req_val) + 1);
|
|
rspns_buf = response + (sizeof(rspns_cnt) + sizeof(rspns_val));
|
|
|
|
if (req_cnt == 0U) {
|
|
goto end;
|
|
}
|
|
|
|
if (req_val & SWDP_REQUEST_RnW) {
|
|
/* Read register block */
|
|
if (req_val & SWDP_REQUEST_APnDP) {
|
|
/* Post AP read */
|
|
rspns_val = do_swdp_transfer(ctx, req_val, NULL);
|
|
if (rspns_val != SWDP_ACK_OK) {
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
while (req_cnt--) {
|
|
/* Read DP/AP register */
|
|
if ((req_cnt == 0U) &&
|
|
(req_val & SWDP_REQUEST_APnDP)) {
|
|
/* Last AP read */
|
|
req_val = DP_RDBUFF | SWDP_REQUEST_RnW;
|
|
}
|
|
|
|
rspns_val = do_swdp_transfer(ctx, req_val, &data);
|
|
if (rspns_val != SWDP_ACK_OK) {
|
|
goto end;
|
|
}
|
|
|
|
/* Store data */
|
|
sys_put_le32(data, rspns_buf);
|
|
rspns_buf += sizeof(data);
|
|
rspns_cnt++;
|
|
}
|
|
} else {
|
|
/* Write register block */
|
|
while (req_cnt--) {
|
|
/* Load data */
|
|
data = sys_get_le32(req_buf);
|
|
req_buf += sizeof(data);
|
|
/* Write DP/AP register */
|
|
rspns_val = do_swdp_transfer(ctx, req_val, &data);
|
|
if (rspns_val != SWDP_ACK_OK) {
|
|
goto end;
|
|
}
|
|
|
|
rspns_cnt++;
|
|
}
|
|
/* Check last write */
|
|
rspns_val = do_swdp_transfer(ctx, DP_RDBUFF | SWDP_REQUEST_RnW, NULL);
|
|
}
|
|
|
|
end:
|
|
sys_put_le16(rspns_cnt, &response[0]);
|
|
response[2] = rspns_val;
|
|
|
|
LOG_DBG("Received %u, to transmit %u, response count %u",
|
|
req_buf - request,
|
|
rspns_buf - response,
|
|
rspns_cnt * 4);
|
|
|
|
return (rspns_buf - response);
|
|
}
|
|
|
|
/* Delegate Transfer Block command */
|
|
static uint16_t dap_transferblock(struct dap_context *const ctx,
|
|
const uint8_t *const request,
|
|
uint8_t *const response)
|
|
{
|
|
uint16_t retval;
|
|
|
|
if (!atomic_test_bit(&ctx->state, DAP_STATE_CONNECTED)) {
|
|
LOG_ERR("DAP device is not connected");
|
|
/* Clear response count */
|
|
sys_put_le16(0U, &response[0]);
|
|
/* Clear DAP response (ACK) value */
|
|
response[2] = 0U;
|
|
return 3U;
|
|
}
|
|
|
|
switch (ctx->debug_port) {
|
|
case DAP_PORT_SWD:
|
|
retval = dap_swdp_transferblock(ctx, request, response);
|
|
break;
|
|
case DAP_PORT_JTAG:
|
|
default:
|
|
LOG_ERR("port unsupported");
|
|
/* Clear response count */
|
|
sys_put_le16(0U, &response[0]);
|
|
/* Clear DAP response (ACK) value */
|
|
response[2] = 0U;
|
|
retval = 3U;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
/* Process SWD Write ABORT command and prepare response */
|
|
static uint16_t dap_swdp_writeabort(struct dap_context *const ctx,
|
|
const uint8_t *const request,
|
|
uint8_t *const response)
|
|
{
|
|
const struct swdp_api *api = ctx->swdp_dev->api;
|
|
/* Load data (Ignore DAP index in request[0]) */
|
|
uint32_t data = sys_get_le32(&request[1]);
|
|
|
|
/* Write Abort register */
|
|
api->swdp_transfer(ctx->swdp_dev, DP_ABORT, &data,
|
|
ctx->transfer.idle_cycles, NULL);
|
|
|
|
response[0] = DAP_OK;
|
|
return 1U;
|
|
}
|
|
|
|
/* Delegate DAP Write ABORT command */
|
|
static uint16_t dap_writeabort(struct dap_context *const ctx,
|
|
const uint8_t *const request,
|
|
uint8_t *const response)
|
|
{
|
|
uint16_t retval;
|
|
|
|
if (!atomic_test_bit(&ctx->state, DAP_STATE_CONNECTED)) {
|
|
LOG_ERR("DAP device is not connected");
|
|
response[0] = DAP_ERROR;
|
|
return 1U;
|
|
}
|
|
|
|
switch (ctx->debug_port) {
|
|
case DAP_PORT_SWD:
|
|
retval = dap_swdp_writeabort(ctx, request, response);
|
|
break;
|
|
case DAP_PORT_JTAG:
|
|
default:
|
|
LOG_ERR("port unsupported");
|
|
response[0] = DAP_ERROR;
|
|
retval = 1U;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
/* Process DAP Vendor command request */
|
|
static uint16_t dap_process_vendor_cmd(struct dap_context *const ctx,
|
|
const uint8_t *const request,
|
|
uint8_t *const response)
|
|
{
|
|
response[0] = ID_DAP_INVALID;
|
|
return 1U;
|
|
}
|
|
|
|
/*
|
|
* Process DAP command request and prepare response
|
|
* request: pointer to request data
|
|
* response: pointer to response data
|
|
* return: number of bytes in response
|
|
*
|
|
* All the subsequent command functions have the same parameter
|
|
* and return value structure.
|
|
*/
|
|
static uint16_t dap_process_cmd(struct dap_context *const ctx,
|
|
const uint8_t *request,
|
|
uint8_t *response)
|
|
{
|
|
uint16_t retval;
|
|
|
|
LOG_HEXDUMP_DBG(request, 8, "req");
|
|
|
|
if ((*request >= ID_DAP_VENDOR0) && (*request <= ID_DAP_VENDOR31)) {
|
|
return dap_process_vendor_cmd(ctx, request, response);
|
|
}
|
|
|
|
*response++ = *request;
|
|
LOG_DBG("request 0x%02x", *request);
|
|
|
|
switch (*request++) {
|
|
case ID_DAP_INFO:
|
|
retval = dap_info(ctx, request, response);
|
|
break;
|
|
case ID_DAP_HOST_STATUS:
|
|
retval = dap_host_status(ctx, request, response);
|
|
break;
|
|
case ID_DAP_CONNECT:
|
|
retval = dap_connect(ctx, request, response);
|
|
break;
|
|
case ID_DAP_DISCONNECT:
|
|
retval = dap_disconnect(ctx, response);
|
|
break;
|
|
case ID_DAP_DELAY:
|
|
retval = dap_delay(ctx, request, response);
|
|
break;
|
|
case ID_DAP_RESET_TARGET:
|
|
retval = dap_reset_target(ctx, response);
|
|
break;
|
|
case ID_DAP_SWJ_PINS:
|
|
retval = dap_swj_pins(ctx, request, response);
|
|
break;
|
|
case ID_DAP_SWJ_CLOCK:
|
|
retval = dap_swj_clock(ctx, request, response);
|
|
break;
|
|
case ID_DAP_SWJ_SEQUENCE:
|
|
retval = dap_swj_sequence(ctx, request, response);
|
|
break;
|
|
case ID_DAP_SWDP_CONFIGURE:
|
|
retval = dap_swdp_configure(ctx, request, response);
|
|
break;
|
|
case ID_DAP_SWDP_SEQUENCE:
|
|
retval = dap_swdp_sequence(ctx, request, response);
|
|
break;
|
|
case ID_DAP_JTAG_SEQUENCE:
|
|
LOG_ERR("JTAG sequence unsupported");
|
|
retval = 1;
|
|
*response = DAP_ERROR;
|
|
break;
|
|
case ID_DAP_JTAG_CONFIGURE:
|
|
LOG_ERR("JTAG configure unsupported");
|
|
retval = 1;
|
|
*response = DAP_ERROR;
|
|
break;
|
|
case ID_DAP_JTAG_IDCODE:
|
|
LOG_ERR("JTAG IDCODE unsupported");
|
|
retval = 1;
|
|
*response = DAP_ERROR;
|
|
break;
|
|
case ID_DAP_TRANSFER_CONFIGURE:
|
|
retval = dap_transfer_cfg(ctx, request, response);
|
|
break;
|
|
case ID_DAP_TRANSFER:
|
|
retval = dap_transfer(ctx, request, response);
|
|
break;
|
|
case ID_DAP_TRANSFER_BLOCK:
|
|
retval = dap_transferblock(ctx, request, response);
|
|
break;
|
|
case ID_DAP_WRITE_ABORT:
|
|
retval = dap_writeabort(ctx, request, response);
|
|
break;
|
|
case ID_DAP_SWO_TRANSPORT:
|
|
LOG_ERR("SWO Transport unsupported");
|
|
retval = 1;
|
|
*response = DAP_ERROR;
|
|
break;
|
|
case ID_DAP_SWO_MODE:
|
|
LOG_ERR("SWO Mode unsupported");
|
|
retval = 1;
|
|
*response = DAP_ERROR;
|
|
break;
|
|
case ID_DAP_SWO_BAUDRATE:
|
|
LOG_ERR("SWO Baudrate unsupported");
|
|
retval = 1;
|
|
*response = DAP_ERROR;
|
|
break;
|
|
case ID_DAP_SWO_CONTROL:
|
|
LOG_ERR("SWO Control unsupported");
|
|
retval = 1;
|
|
*response = DAP_ERROR;
|
|
break;
|
|
case ID_DAP_SWO_STATUS:
|
|
LOG_ERR("SWO Status unsupported");
|
|
retval = 1;
|
|
*response = DAP_ERROR;
|
|
break;
|
|
case ID_DAP_SWO_DATA:
|
|
LOG_ERR("SWO Data unsupported");
|
|
retval = 1;
|
|
*response = DAP_ERROR;
|
|
break;
|
|
case ID_DAP_UART_TRANSPORT:
|
|
LOG_ERR("UART Transport unsupported");
|
|
retval = 1;
|
|
*response = DAP_ERROR;
|
|
break;
|
|
case ID_DAP_UART_CONFIGURE:
|
|
LOG_ERR("UART Configure unsupported");
|
|
retval = 1;
|
|
*response = DAP_ERROR;
|
|
break;
|
|
case ID_DAP_UART_CONTROL:
|
|
LOG_ERR("UART Control unsupported");
|
|
retval = 1;
|
|
*response = DAP_ERROR;
|
|
break;
|
|
case ID_DAP_UART_STATUS:
|
|
LOG_ERR("UART Status unsupported");
|
|
retval = 1;
|
|
*response = DAP_ERROR;
|
|
break;
|
|
case ID_DAP_UART_TRANSFER:
|
|
LOG_ERR("UART Transfer unsupported");
|
|
retval = 1;
|
|
*response = DAP_ERROR;
|
|
break;
|
|
|
|
default:
|
|
*(response - 1) = ID_DAP_INVALID;
|
|
return 1U;
|
|
}
|
|
|
|
return (1U + retval);
|
|
}
|
|
|
|
/*
|
|
* Execute DAP command (process request and prepare response)
|
|
* request: pointer to request data
|
|
* response: pointer to response data
|
|
* return: number of bytes in response
|
|
*/
|
|
uint32_t dap_execute_cmd(const uint8_t *request,
|
|
uint8_t *response)
|
|
{
|
|
uint32_t retval;
|
|
uint16_t n;
|
|
uint8_t count;
|
|
|
|
if (request[0] == ID_DAP_EXECUTE_COMMANDS) {
|
|
/* copy command and increment */
|
|
*response++ = *request++;
|
|
count = request[0];
|
|
request += sizeof(count);
|
|
response[0] = count;
|
|
response += sizeof(count);
|
|
retval = sizeof(count) + 1U;
|
|
LOG_WRN("(untested) ID DAP EXECUTE_COMMANDS count %u", count);
|
|
while (count--) {
|
|
n = dap_process_cmd(&dap_ctx[0], request, response);
|
|
retval += n;
|
|
request += n;
|
|
response += n;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
return dap_process_cmd(&dap_ctx[0], request, response);
|
|
}
|
|
|
|
int dap_setup(const struct device *const dev)
|
|
{
|
|
dap_ctx[0].swdp_dev = (void *)dev;
|
|
|
|
if (!device_is_ready(dap_ctx[0].swdp_dev)) {
|
|
LOG_ERR("SWD driver not ready");
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* Default settings */
|
|
dap_ctx[0].pkt_size = CONFIG_CMSIS_DAP_PACKET_SIZE;
|
|
dap_ctx[0].debug_port = 0U;
|
|
dap_ctx[0].transfer.idle_cycles = 0U;
|
|
dap_ctx[0].transfer.retry_count = 100U;
|
|
dap_ctx[0].transfer.match_retry = 0U;
|
|
dap_ctx[0].transfer.match_mask = 0U;
|
|
dap_ctx[0].capabilities = DAP_SUPPORTS_ATOMIC_COMMANDS |
|
|
DAP_DP_SUPPORTS_SWD;
|
|
|
|
return 0;
|
|
}
|