|  | /* | 
|  | * 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]; | 
|  |  | 
|  | #define CMSIS_DAP_PACKET_MIN_SIZE 64 | 
|  |  | 
|  | BUILD_ASSERT(sizeof(CONFIG_CMSIS_DAP_PROBE_VENDOR) <= | 
|  | MIN(CMSIS_DAP_PACKET_MIN_SIZE - 2, UINT8_MAX - 2), | 
|  | "PROBE_VENDOR string is too long."); | 
|  | BUILD_ASSERT(sizeof(CONFIG_CMSIS_DAP_PROBE_NAME) <= | 
|  | MIN(CMSIS_DAP_PACKET_MIN_SIZE - 2, UINT8_MAX - 2), | 
|  | "PROBE_NAME string is too long."); | 
|  | BUILD_ASSERT(sizeof(CONFIG_CMSIS_DAP_BOARD_VENDOR) <= | 
|  | MIN(CMSIS_DAP_PACKET_MIN_SIZE - 2, UINT8_MAX - 2), | 
|  | "BOARD_VENDOR string is too long."); | 
|  | BUILD_ASSERT(sizeof(CONFIG_CMSIS_DAP_BOARD_NAME) <= | 
|  | MIN(CMSIS_DAP_PACKET_MIN_SIZE - 2, UINT8_MAX - 2), | 
|  | "BOARD_NAME string is too long."); | 
|  | BUILD_ASSERT(sizeof(CONFIG_CMSIS_DAP_DEVICE_VENDOR) <= | 
|  | MIN(CMSIS_DAP_PACKET_MIN_SIZE - 2, UINT8_MAX - 2), | 
|  | "DEVICE_VENDOR string is too long."); | 
|  | BUILD_ASSERT(sizeof(CONFIG_CMSIS_DAP_DEVICE_NAME) <= | 
|  | MIN(CMSIS_DAP_PACKET_MIN_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); | 
|  | } | 
|  |  | 
|  | void dap_update_pkt_size(const uint16_t pkt_size) | 
|  | { | 
|  | dap_ctx[0].pkt_size = pkt_size; | 
|  | LOG_INF("New packet size %u", dap_ctx[0].pkt_size); | 
|  | } | 
|  |  | 
|  | 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 = CMSIS_DAP_PACKET_MIN_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; | 
|  | } |