diff options
Diffstat (limited to 'src/jtag/drivers/libjaylink/libjaylink/swo.c')
m--------- | src/jtag/drivers/libjaylink | 0 | ||||
-rw-r--r-- | src/jtag/drivers/libjaylink/libjaylink/swo.c | 453 |
2 files changed, 453 insertions, 0 deletions
diff --git a/src/jtag/drivers/libjaylink b/src/jtag/drivers/libjaylink deleted file mode 160000 -Subproject 8645845c1abebd004e991ba9a7f808f4fd0c608 diff --git a/src/jtag/drivers/libjaylink/libjaylink/swo.c b/src/jtag/drivers/libjaylink/libjaylink/swo.c new file mode 100644 index 0000000..6037f64 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/swo.c @@ -0,0 +1,453 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2015 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdint.h> +#include <stdbool.h> + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * Serial Wire Output (SWO) functions. + */ + +/** @cond PRIVATE */ +#define CMD_SWO 0xeb + +#define SWO_CMD_START 0x64 +#define SWO_CMD_STOP 0x65 +#define SWO_CMD_READ 0x66 +#define SWO_CMD_GET_SPEEDS 0x6e + +#define SWO_PARAM_MODE 0x01 +#define SWO_PARAM_BAUDRATE 0x02 +#define SWO_PARAM_READ_SIZE 0x03 +#define SWO_PARAM_BUFFER_SIZE 0x04 + +#define SWO_ERR 0x80000000 +/** @endcond */ + +/** + * Start SWO capture. + * + * @note This function must be used only if the device has the + * #JAYLINK_DEV_CAP_SWO capability. + * + * @param[in,out] devh Device handle. + * @param[in] mode Mode to capture data with. + * @param[in] baudrate Baudrate to capture data in bit per second. + * @param[in] size Device internal buffer size in bytes to use for capturing. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR_DEV Unspecified device error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_swo_get_speeds() + * @see jaylink_get_free_memory() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_swo_start(struct jaylink_device_handle *devh, + enum jaylink_swo_mode mode, uint32_t baudrate, uint32_t size) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[32]; + uint32_t status; + + if (!devh || !baudrate || !size) + return JAYLINK_ERR_ARG; + + if (mode != JAYLINK_SWO_MODE_UART) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 21, 4, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_SWO; + buf[1] = SWO_CMD_START; + + buf[2] = 0x04; + buf[3] = SWO_PARAM_MODE; + buffer_set_u32(buf, mode, 4); + + buf[8] = 0x04; + buf[9] = SWO_PARAM_BAUDRATE; + buffer_set_u32(buf, baudrate, 10); + + buf[14] = 0x04; + buf[15] = SWO_PARAM_BUFFER_SIZE; + buffer_set_u32(buf, size, 16); + + buf[20] = 0x00; + + ret = transport_write(devh, buf, 21); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + status = buffer_get_u32(buf, 0); + + if (status > 0) { + log_err(ctx, "Failed to start capture: 0x%x.", status); + return JAYLINK_ERR_DEV; + } + + return JAYLINK_OK; +} + +/** + * Stop SWO capture. + * + * @note This function must be used only if the device has the + * #JAYLINK_DEV_CAP_SWO capability. + * + * @param[in,out] devh Device handle. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR_DEV Unspecified device error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_swo_start() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_swo_stop(struct jaylink_device_handle *devh) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[4]; + uint32_t status; + + if (!devh) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 3, 4, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_SWO; + buf[1] = SWO_CMD_STOP; + buf[2] = 0x00; + + ret = transport_write(devh, buf, 3); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + status = buffer_get_u32(buf, 0); + + if (status > 0) { + log_err(ctx, "Failed to stop capture: 0x%x.", status); + return JAYLINK_ERR_DEV; + } + + return JAYLINK_OK; +} + +/** + * Read SWO trace data. + * + * @note This function must be used only if the device has the + * #JAYLINK_DEV_CAP_SWO capability. + * + * @param[in,out] devh Device handle. + * @param[out] buffer Buffer to store trace data on success. Its content is + * undefined on failure. + * @param[in,out] length Maximum number of bytes to read. On success, the value + * gets updated with the actual number of bytes read. The + * value is undefined on failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_PROTO Protocol violation. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR_DEV Unspecified device error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_swo_start() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_swo_read(struct jaylink_device_handle *devh, + uint8_t *buffer, uint32_t *length) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[32]; + uint32_t status; + uint32_t tmp; + + if (!devh || !buffer || !length) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 9, 8, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_SWO; + buf[1] = SWO_CMD_READ; + + buf[2] = 0x04; + buf[3] = SWO_PARAM_READ_SIZE; + buffer_set_u32(buf, *length, 4); + + buf[8] = 0x00; + + ret = transport_write(devh, buf, 9); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 8); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + status = buffer_get_u32(buf, 0); + tmp = buffer_get_u32(buf, 4); + + if (tmp > *length) { + log_err(ctx, "Received %u bytes but only %u bytes were " + "requested.", tmp, *length); + return JAYLINK_ERR_PROTO; + } + + *length = tmp; + + if (tmp > 0) { + ret = transport_start_read(devh, tmp); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buffer, tmp); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + } + + if (status > 0) { + log_err(ctx, "Failed to read data: 0x%x.", status); + return JAYLINK_ERR_DEV; + } + + return JAYLINK_OK; +} + +/** + * Retrieve SWO speeds. + + * The speeds are calculated as follows: + * + * @par + * <tt>speeds = @a freq / n</tt> with <tt>n >= @a min_div</tt> and + * <tt>n <= @a max_div</tt>, where @p n is an integer + * + * Assuming, for example, a base frequency @a freq of 4500 kHz, a minimum + * divider @a min_div of 1 and a maximum divider @a max_div of 8 then the + * highest possible SWO speed is 4500 kHz / 1 = 4500 kHz. The next highest + * speed is 2250 kHz for a divider of 2, and so on. Accordingly, the lowest + * possible speed is 4500 kHz / 8 = 562.5 kHz. + * + * @note This function must be used only if the device has the + * #JAYLINK_DEV_CAP_SWO capability. + * + * @param[in,out] devh Device handle. + * @param[in] mode Capture mode to retrieve speeds for. + * @param[out] speed Speed information on success, and undefined on failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_PROTO Protocol violation. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR_DEV Unspecified device error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_swo_get_speeds(struct jaylink_device_handle *devh, + enum jaylink_swo_mode mode, struct jaylink_swo_speed *speed) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[24]; + uint32_t tmp; + uint32_t length; + + if (!devh || !speed) + return JAYLINK_ERR_ARG; + + if (mode != JAYLINK_SWO_MODE_UART) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 9, 4, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_SWO; + buf[1] = SWO_CMD_GET_SPEEDS; + + buf[2] = 0x04; + buf[3] = SWO_PARAM_MODE; + buffer_set_u32(buf, mode, 4); + + buf[8] = 0x00; + + ret = transport_write(devh, buf, 9); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + tmp = buffer_get_u32(buf, 0); + + if (tmp & SWO_ERR) { + log_err(ctx, "Failed to retrieve speed information: 0x%x.", + tmp); + return JAYLINK_ERR_DEV; + } + + length = tmp; + + if (length != 28) { + log_err(ctx, "Unexpected number of bytes received: %u.", + length); + return JAYLINK_ERR_PROTO; + } + + length = length - 4; + ret = transport_start_read(devh, length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + speed->freq = buffer_get_u32(buf, 4); + speed->min_div = buffer_get_u32(buf, 8); + + if (!speed->min_div) { + log_err(ctx, "Minimum frequency divider is zero."); + return JAYLINK_ERR_PROTO; + } + + speed->max_div = buffer_get_u32(buf, 12); + + if (speed->max_div < speed->min_div) { + log_err(ctx, "Maximum frequency divider is less than minimum " + "frequency divider."); + return JAYLINK_ERR_PROTO; + } + + speed->min_prescaler = buffer_get_u32(buf, 16); + speed->max_prescaler = buffer_get_u32(buf, 20); + + if (speed->max_prescaler < speed->min_prescaler) { + log_err(ctx, "Maximum prescaler is less than minimum " + "prescaler."); + return JAYLINK_ERR_PROTO; + } + + return JAYLINK_OK; +} |