diff options
Diffstat (limited to 'src/jtag/drivers/libjaylink/libjaylink')
27 files changed, 8540 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/Makefile.am b/src/jtag/drivers/libjaylink/libjaylink/Makefile.am new file mode 100644 index 0000000..62c5489 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/Makefile.am @@ -0,0 +1,62 @@ +## +## This file is part of the libjaylink project. +## +## Copyright (C) 2014-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/>. +## + +if SUBPROJECT_BUILD +noinst_LTLIBRARIES = libjaylink.la +else +lib_LTLIBRARIES = libjaylink.la + +library_includedir = $(includedir)/libjaylink +library_include_HEADERS = libjaylink.h +nodist_library_include_HEADERS = version.h +endif + +libjaylink_la_SOURCES = \ + buffer.c \ + core.c \ + device.c \ + discovery.c \ + discovery_tcp.c \ + emucom.c \ + error.c \ + fileio.c \ + jtag.c \ + list.c \ + log.c \ + socket.c \ + strutil.c \ + swd.c \ + swo.c \ + target.c \ + transport.c \ + transport_tcp.c \ + util.c \ + version.c + +libjaylink_la_CFLAGS = $(JAYLINK_CFLAGS) +libjaylink_la_LDFLAGS = $(JAYLINK_LDFLAGS) -no-undefined +libjaylink_la_LIBADD = $(JAYLINK_LIBS) + +if HAVE_LIBUSB +libjaylink_la_SOURCES += discovery_usb.c transport_usb.c +libjaylink_la_CFLAGS += $(libusb_CFLAGS) +libjaylink_la_LIBADD += $(libusb_LIBS) +endif + +noinst_HEADERS = libjaylink-internal.h diff --git a/src/jtag/drivers/libjaylink/libjaylink/buffer.c b/src/jtag/drivers/libjaylink/libjaylink/buffer.c new file mode 100644 index 0000000..527c25e --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/buffer.c @@ -0,0 +1,140 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-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 <string.h> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "libjaylink-internal.h" + +/** + * @file + * + * Buffer helper functions. + */ + +/** + * Write a 16-bit unsigned integer value to a buffer. + * + * The value is stored in the buffer in device byte order. + * + * @param[out] buffer Buffer to write the value into. + * @param[in] value Value to write into the buffer in host byte order. + * @param[in] offset Offset of the value within the buffer in bytes. + */ +JAYLINK_PRIV void buffer_set_u16(uint8_t *buffer, uint16_t value, + size_t offset) +{ + /* + * Store the value in the buffer and swap byte order depending on the + * host byte order. + */ +#ifdef WORDS_BIGENDIAN + buffer[offset + 0] = value; + buffer[offset + 1] = value >> 8; +#else + memcpy(buffer + offset, &value, sizeof(value)); +#endif +} + +/** + * Read a 16-bit unsigned integer value from a buffer. + * + * The value in the buffer is expected to be stored in device byte order. + * + * @param[in] buffer Buffer to read the value from. + * @param[in] offset Offset of the value within the buffer in bytes. + * + * @return The value read from the buffer in host byte order. + */ +JAYLINK_PRIV uint16_t buffer_get_u16(const uint8_t *buffer, size_t offset) +{ + uint16_t value; + + /* + * Read the value from the buffer and swap byte order depending on the + * host byte order. + */ +#ifdef WORDS_BIGENDIAN + value = (((uint16_t)buffer[offset + 1])) | \ + (((uint16_t)buffer[offset + 0]) << 8); +#else + memcpy(&value, buffer + offset, sizeof(value)); +#endif + + return value; +} + +/** + * Write a 32-bit unsigned integer value to a buffer. + * + * The value is stored in the buffer in device byte order. + * + * @param[out] buffer Buffer to write the value into. + * @param[in] value Value to write into the buffer in host byte order. + * @param[in] offset Offset of the value within the buffer in bytes. + */ +JAYLINK_PRIV void buffer_set_u32(uint8_t *buffer, uint32_t value, + size_t offset) +{ + /* + * Store the value in the buffer and swap byte order depending on the + * host byte order. + */ +#ifdef WORDS_BIGENDIAN + buffer[offset + 0] = value; + buffer[offset + 1] = value >> 8; + buffer[offset + 2] = value >> 16; + buffer[offset + 3] = value >> 24; +#else + memcpy(buffer + offset, &value, sizeof(value)); +#endif +} + +/** + * Read a 32-bit unsigned integer value from a buffer. + * + * The value in the buffer is expected to be stored in device byte order. + * + * @param[in] buffer Buffer to read the value from. + * @param[in] offset Offset of the value within the buffer in bytes. + * + * @return The value read from the buffer in host byte order. + */ +JAYLINK_PRIV uint32_t buffer_get_u32(const uint8_t *buffer, size_t offset) +{ + uint32_t value; + + /* + * Read the value from the buffer and swap byte order depending on the + * host byte order. + */ +#ifdef WORDS_BIGENDIAN + value = (((uint32_t)buffer[offset + 3])) | \ + (((uint32_t)buffer[offset + 2]) << 8) | \ + (((uint32_t)buffer[offset + 1]) << 16) | \ + (((uint32_t)buffer[offset + 0]) << 24); +#else + memcpy(&value, buffer + offset, sizeof(value)); +#endif + + return value; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/core.c b/src/jtag/drivers/libjaylink/libjaylink/core.c new file mode 100644 index 0000000..509b89d --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/core.c @@ -0,0 +1,219 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2016 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/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <stdbool.h> +#ifdef _WIN32 +#include <winsock2.h> +#endif +#ifdef HAVE_LIBUSB +#include <libusb.h> +#endif + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @mainpage + * + * @section sec_intro Introduction + * + * This document describes the API of libjaylink. + * + * libjaylink is a shared library written in C to access SEGGER J-Link and + * compatible devices. + * + * @section sec_error Error handling + * + * The libjaylink functions which can fail use the return value to indicate an + * error. The functions typically return an error code of #jaylink_error. + * For each function, all possible error codes and their detailed descriptions + * are documented. As the possible error codes returned by a function may + * change it is recommended to also always cover unexpected values when + * checking for error codes to be compatible with later versions of libjaylink. + * + * There are a few exceptions where a function directly returns the result + * instead of an error code because it is more convenient from an API + * perspective and because there is only a single reason for failure which is + * clearly distinguishable from the result. + * + * @section sec_license Copyright and license + * + * libjaylink is licensed under the terms of the GNU General Public + * License (GPL), version 2 or later. + * + * @section sec_website Website + * + * <a href="http://git.zapb.de/libjaylink.git">git.zapb.de/libjaylink.git</a> + */ + +/** + * @file + * + * Core library functions. + */ + +/** + * Initialize libjaylink. + * + * This function must be called before any other libjaylink function is called. + * + * @param[out] ctx Newly allocated libjaylink context on success, and undefined + * on failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_MALLOC Memory allocation error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_init(struct jaylink_context **ctx) +{ + int ret; + struct jaylink_context *context; +#ifdef _WIN32 + WSADATA wsa_data; +#endif + + if (!ctx) + return JAYLINK_ERR_ARG; + + context = malloc(sizeof(struct jaylink_context)); + + if (!context) + return JAYLINK_ERR_MALLOC; + +#ifdef HAVE_LIBUSB + if (libusb_init(&context->usb_ctx) != LIBUSB_SUCCESS) { + free(context); + return JAYLINK_ERR; + } +#endif + +#ifdef _WIN32 + ret = WSAStartup(MAKEWORD(2, 2), &wsa_data); + + if (ret != 0) { +#ifdef HAVE_LIBUSB + libusb_exit(context->usb_ctx); +#endif + free(context); + return JAYLINK_ERR; + } + + if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2) { +#ifdef HAVE_LIBUSB + libusb_exit(context->usb_ctx); +#endif + free(context); + return JAYLINK_ERR; + } +#endif + + context->devs = NULL; + context->discovered_devs = NULL; + + /* Show error and warning messages by default. */ + context->log_level = JAYLINK_LOG_LEVEL_WARNING; + + context->log_callback = &log_vprintf; + context->log_callback_data = NULL; + + ret = jaylink_log_set_domain(context, JAYLINK_LOG_DOMAIN_DEFAULT); + + if (ret != JAYLINK_OK) { +#ifdef HAVE_LIBUSB + libusb_exit(context->usb_ctx); +#endif +#ifdef _WIN32 + WSACleanup(); +#endif + free(context); + return ret; + } + + *ctx = context; + + return JAYLINK_OK; +} + +/** + * Shutdown libjaylink. + * + * @param[in,out] ctx libjaylink context. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_exit(struct jaylink_context *ctx) +{ + struct list *item; + + if (!ctx) + return JAYLINK_ERR_ARG; + + item = ctx->discovered_devs; + + while (item) { + jaylink_unref_device((struct jaylink_device *)item->data); + item = item->next; + } + + list_free(ctx->discovered_devs); + list_free(ctx->devs); + +#ifdef HAVE_LIBUSB + libusb_exit(ctx->usb_ctx); +#endif +#ifdef _WIN32 + WSACleanup(); +#endif + free(ctx); + + return JAYLINK_OK; +} + +/** + * Check for a capability of libjaylink. + * + * @param[in] cap Capability to check for. + * + * @retval true Capability is supported. + * @retval false Capability is not supported or invalid argument. + * + * @since 0.1.0 + */ +JAYLINK_API bool jaylink_library_has_cap(enum jaylink_capability cap) +{ + switch (cap) { +#ifdef HAVE_LIBUSB + case JAYLINK_CAP_HIF_USB: + return true; +#endif + default: + return false; + } +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/device.c b/src/jtag/drivers/libjaylink/libjaylink/device.c new file mode 100644 index 0000000..a3bddf6 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/device.c @@ -0,0 +1,1707 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2016 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/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#ifdef _WIN32 +#include <winsock2.h> +#else +#include <sys/socket.h> +#include <arpa/inet.h> +#endif +#ifdef HAVE_LIBUSB +#include <libusb.h> +#endif + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * Device enumeration and handling. + */ + +/** @cond PRIVATE */ +#define CMD_GET_VERSION 0x01 +#define CMD_GET_HW_STATUS 0x07 +#define CMD_REGISTER 0x09 +#define CMD_GET_HW_INFO 0xc1 +#define CMD_GET_COUNTERS 0xc2 +#define CMD_GET_FREE_MEMORY 0xd4 +#define CMD_GET_CAPS 0xe8 +#define CMD_GET_EXT_CAPS 0xed +#define CMD_GET_HW_VERSION 0xf0 +#define CMD_READ_CONFIG 0xf2 +#define CMD_WRITE_CONFIG 0xf3 + +#define REG_CMD_REGISTER 0x64 +#define REG_CMD_UNREGISTER 0x65 + +/** Size of the registration header in bytes. */ +#define REG_HEADER_SIZE 8 +/** Minimum registration information size in bytes. */ +#define REG_MIN_SIZE 0x4c +/** Maximum registration information size in bytes. */ +#define REG_MAX_SIZE 0x200 +/** Size of a connection entry in bytes. */ +#define REG_CONN_INFO_SIZE 16 +/** @endcond */ + +/** @private */ +JAYLINK_PRIV struct jaylink_device *device_allocate( + struct jaylink_context *ctx) +{ + struct jaylink_device *dev; + struct list *list; + + dev = malloc(sizeof(struct jaylink_device)); + + if (!dev) + return NULL; + + list = list_prepend(ctx->devs, dev); + + if (!list) { + free(dev); + return NULL; + } + + ctx->devs = list; + + dev->ctx = ctx; + dev->ref_count = 1; + + return dev; +} + +static struct jaylink_device **allocate_device_list(size_t length) +{ + struct jaylink_device **list; + + list = malloc(sizeof(struct jaylink_device *) * (length + 1)); + + if (!list) + return NULL; + + list[length] = NULL; + + return list; +} + +/** + * Get available devices. + * + * @param[in,out] ctx libjaylink context. + * @param[out] devs Newly allocated array which contains instances of available + * devices on success, and undefined on failure. The array is + * NULL-terminated and must be free'd by the caller with + * jaylink_free_devices(). + * @param[out] count Number of available devices on success, and undefined on + * failure. Can be NULL. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_MALLOC Memory allocation error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_discovery_scan() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_get_devices(struct jaylink_context *ctx, + struct jaylink_device ***devs, size_t *count) +{ + size_t num; + struct list *item; + struct jaylink_device **tmp; + struct jaylink_device *dev; + size_t i; + + if (!ctx || !devs) + return JAYLINK_ERR_ARG; + + num = list_length(ctx->discovered_devs); + tmp = allocate_device_list(num); + + if (!tmp) { + log_err(ctx, "Failed to allocate device list."); + return JAYLINK_ERR_MALLOC; + } + + item = ctx->discovered_devs; + + for (i = 0; i < num; i++) { + dev = (struct jaylink_device *)item->data; + tmp[i] = jaylink_ref_device(dev); + item = item->next; + } + + if (count) + *count = num; + + *devs = tmp; + + return JAYLINK_OK; +} + +/** + * Free devices. + * + * @param[in,out] devs Array of device instances. Must be NULL-terminated. + * @param[in] unref Determines whether the device instances should be + * unreferenced. + * + * @see jaylink_get_devices() + * + * @since 0.1.0 + */ +JAYLINK_API void jaylink_free_devices(struct jaylink_device **devs, bool unref) +{ + size_t i; + + if (!devs) + return; + + if (unref) { + for (i = 0; devs[i]; i++) + jaylink_unref_device(devs[i]); + } + + free(devs); +} + +/** + * Get the host interface of a device. + * + * @param[in] dev Device instance. + * @param[out] iface Host interface of the device on success, and undefined on + * failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_device_get_host_interface( + const struct jaylink_device *dev, + enum jaylink_host_interface *iface) +{ + if (!dev || !iface) + return JAYLINK_ERR_ARG; + + *iface = dev->iface; + + return JAYLINK_OK; +} + +/** + * Get the serial number of a device. + * + * @note This serial number is for enumeration purpose only and might differ + * from the real serial number of the device. + * + * @param[in] dev Device instance. + * @param[out] serial_number Serial number of the device on success, and + * undefined on failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_NOT_AVAILABLE Serial number is not available. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_device_get_serial_number( + const struct jaylink_device *dev, uint32_t *serial_number) +{ + if (!dev || !serial_number) + return JAYLINK_ERR_ARG; + + if (!dev->valid_serial_number) + return JAYLINK_ERR_NOT_AVAILABLE; + + *serial_number = dev->serial_number; + + return JAYLINK_OK; +} + +/** + * Get the USB address of a device. + * + * @note Identification of a device with the USB address is deprecated and the + * serial number should be used instead. + * + * @param[in] dev Device instance. + * @param[out] address USB address of the device on success, and undefined on + * failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_NOT_SUPPORTED Supported for devices with host interface + * #JAYLINK_HIF_USB only. + * + * @see jaylink_device_get_serial_number() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_device_get_usb_address( + const struct jaylink_device *dev, + enum jaylink_usb_address *address) +{ + if (!dev || !address) + return JAYLINK_ERR_ARG; + + if (dev->iface != JAYLINK_HIF_USB) + return JAYLINK_ERR_NOT_SUPPORTED; + +#ifdef HAVE_LIBUSB + *address = dev->usb_address; + + return JAYLINK_OK; +#else + return JAYLINK_ERR_NOT_SUPPORTED; +#endif +} + +/** + * Get the IPv4 address string of a device. + * + * @param[in] dev Device instance. + * @param[out] address IPv4 address string in quad-dotted decimal format of the + * device on success and undefined on failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_NOT_SUPPORTED Supported for devices with host interface + * #JAYLINK_HIF_TCP only. + * + * @since 0.2.0 + */ +JAYLINK_API int jaylink_device_get_ipv4_address( + const struct jaylink_device *dev, char *address) +{ + if (!dev || !address) + return JAYLINK_ERR_ARG; + + if (dev->iface != JAYLINK_HIF_TCP) + return JAYLINK_ERR_NOT_SUPPORTED; + + memcpy(address, dev->ipv4_address, sizeof(dev->ipv4_address)); + + return JAYLINK_OK; +} + +/** + * Get the MAC address of a device. + * + * @param[in] dev Device instance. + * @param[out] address MAC address of the device on success and undefined on + * failure. The length of the MAC address is + * #JAYLINK_MAC_ADDRESS_LENGTH bytes. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_NOT_SUPPORTED Supported for devices with host interface + * #JAYLINK_HIF_TCP only. + * @retval JAYLINK_ERR_NOT_AVAILABLE MAC address is not available. + * + * @since 0.2.0 + */ +JAYLINK_API int jaylink_device_get_mac_address( + const struct jaylink_device *dev, uint8_t *address) +{ + if (!dev || !address) + return JAYLINK_ERR_ARG; + + if (dev->iface != JAYLINK_HIF_TCP) + return JAYLINK_ERR_NOT_SUPPORTED; + + if (!dev->has_mac_address) + return JAYLINK_ERR_NOT_AVAILABLE; + + memcpy(address, dev->mac_address, sizeof(dev->mac_address)); + + return JAYLINK_OK; +} + +/** + * Get the hardware version of a device. + * + * @note The hardware type can not be obtained by this function, use + * jaylink_get_hardware_version() instead. + * + * @param[in] dev Device instance. + * @param[out] version Hardware version of the device on success and undefined + * on failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_NOT_SUPPORTED Supported for devices with host interface + * #JAYLINK_HIF_TCP only. + * @retval JAYLINK_ERR_NOT_AVAILABLE Hardware version is not available. + * + * @since 0.2.0 + */ +JAYLINK_API int jaylink_device_get_hardware_version( + const struct jaylink_device *dev, + struct jaylink_hardware_version *version) +{ + if (!dev || !version) + return JAYLINK_ERR_ARG; + + if (dev->iface != JAYLINK_HIF_TCP) + return JAYLINK_ERR_NOT_SUPPORTED; + + if (!dev->has_hw_version) + return JAYLINK_ERR_NOT_AVAILABLE; + + *version = dev->hw_version; + + return JAYLINK_OK; +} + +/** + * Get the product name of a device. + * + * @param[in] dev Device instance. + * @param[out] name Product name of the device on success and undefined on + * failure. The maximum length of the product name is + * #JAYLINK_PRODUCT_NAME_MAX_LENGTH bytes. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_NOT_SUPPORTED Supported for devices with host interface + * #JAYLINK_HIF_TCP only. + * @retval JAYLINK_ERR_NOT_AVAILABLE Product name is not available. + * + * @since 0.2.0 + */ +JAYLINK_API int jaylink_device_get_product_name( + const struct jaylink_device *dev, char *name) +{ + if (!dev || !name) + return JAYLINK_ERR_ARG; + + if (dev->iface != JAYLINK_HIF_TCP) + return JAYLINK_ERR_NOT_SUPPORTED; + + if (!dev->has_product_name) + return JAYLINK_ERR_NOT_AVAILABLE; + + memcpy(name, dev->product_name, sizeof(dev->product_name)); + + return JAYLINK_OK; +} + +/** + * Get the nickname of a device. + * + * @param[in] dev Device instance. + * @param[out] nickname Nickname of the device on success and undefined on + * failure. The maximum length of the nickname is + * #JAYLINK_NICKNAME_MAX_LENGTH bytes. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_NOT_SUPPORTED Supported for devices with host interface + * #JAYLINK_HIF_TCP only. + * @retval JAYLINK_ERR_NOT_AVAILABLE Nickname is not available. + * + * @since 0.2.0 + */ +JAYLINK_API int jaylink_device_get_nickname(const struct jaylink_device *dev, + char *nickname) +{ + if (!dev || !nickname) + return JAYLINK_ERR_ARG; + + if (dev->iface != JAYLINK_HIF_TCP) + return JAYLINK_ERR_NOT_SUPPORTED; + + if (!dev->has_nickname) + return JAYLINK_ERR_NOT_AVAILABLE; + + memcpy(nickname, dev->nickname, sizeof(dev->nickname)); + + return JAYLINK_OK; +} + +/** + * Increment the reference count of a device. + * + * @param[in,out] dev Device instance. + * + * @return The given device instance on success, or NULL on invalid argument. + * + * @since 0.1.0 + */ +JAYLINK_API struct jaylink_device *jaylink_ref_device( + struct jaylink_device *dev) +{ + if (!dev) + return NULL; + + dev->ref_count++; + + return dev; +} + +/** + * Decrement the reference count of a device. + * + * @param[in,out] dev Device instance. + * + * @since 0.1.0 + */ +JAYLINK_API void jaylink_unref_device(struct jaylink_device *dev) +{ + struct jaylink_context *ctx; + + if (!dev) + return; + + dev->ref_count--; + + if (!dev->ref_count) { + ctx = dev->ctx; + ctx->devs = list_remove(dev->ctx->devs, dev); + + if (dev->iface == JAYLINK_HIF_USB) { +#ifdef HAVE_LIBUSB + log_dbg(ctx, "Device destroyed (bus:address = " + "%03u:%03u).", + libusb_get_bus_number(dev->usb_dev), + libusb_get_device_address(dev->usb_dev)); + + libusb_unref_device(dev->usb_dev); +#endif + } else if (dev->iface == JAYLINK_HIF_TCP) { + log_dbg(ctx, "Device destroyed (IPv4 address = %s).", + dev->ipv4_address); + } else { + log_err(ctx, "BUG: Invalid host interface: %u.", + dev->iface); + } + + free(dev); + } +} + +static struct jaylink_device_handle *allocate_device_handle( + struct jaylink_device *dev) +{ + struct jaylink_device_handle *devh; + + devh = malloc(sizeof(struct jaylink_device_handle)); + + if (!devh) + return NULL; + + devh->dev = jaylink_ref_device(dev); + + return devh; +} + +static void free_device_handle(struct jaylink_device_handle *devh) +{ + jaylink_unref_device(devh->dev); + free(devh); +} + +/** + * Open a device. + * + * @param[in,out] dev Device instance. + * @param[out] devh Newly allocated handle for the opened device 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_MALLOC Memory allocation error. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_open(struct jaylink_device *dev, + struct jaylink_device_handle **devh) +{ + int ret; + struct jaylink_device_handle *handle; + + if (!dev || !devh) + return JAYLINK_ERR_ARG; + + handle = allocate_device_handle(dev); + + if (!handle) { + log_err(dev->ctx, "Device handle malloc failed."); + return JAYLINK_ERR_MALLOC; + } + + ret = transport_open(handle); + + if (ret != JAYLINK_OK) { + free_device_handle(handle); + return ret; + } + + *devh = handle; + + return JAYLINK_OK; +} + +/** + * Close a device. + * + * @param[in,out] devh Device instance. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_close(struct jaylink_device_handle *devh) +{ + int ret; + + if (!devh) + return JAYLINK_ERR_ARG; + + ret = transport_close(devh); + free_device_handle(devh); + + return ret; +} + +/** + * Get the device instance from a device handle. + * + * @note The reference count of the device instance is not increased. + * + * @param[in] devh Device handle. + * + * @return The device instance on success, or NULL on invalid argument. + * + * @since 0.1.0 + */ +JAYLINK_API struct jaylink_device *jaylink_get_device( + struct jaylink_device_handle *devh) +{ + if (!devh) + return NULL; + + return devh->dev; +} + +/** + * Retrieve the firmware version of a device. + * + * @param[in,out] devh Device handle. + * @param[out] version Newly allocated string which contains the firmware + * version on success, and undefined if @p length is zero + * or on failure. The string is null-terminated and must be + * free'd by the caller. + * @param[out] length Length of the firmware version string including trailing + * null-terminator on success, and undefined on failure. + * Zero if no firmware version string is available. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_MALLOC Memory allocation error. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_get_firmware_version( + struct jaylink_device_handle *devh, char **version, + size_t *length) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[2]; + uint16_t dummy; + char *tmp; + + if (!devh || !version || !length) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 1, 2, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_GET_VERSION; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 2); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + dummy = buffer_get_u16(buf, 0); + *length = dummy; + + if (!dummy) + return JAYLINK_OK; + + ret = transport_start_read(devh, dummy); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + tmp = malloc(dummy); + + if (!tmp) { + log_err(ctx, "Firmware version string malloc failed."); + return JAYLINK_ERR_MALLOC; + } + + ret = transport_read(devh, (uint8_t *)tmp, dummy); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + free(tmp); + return ret; + } + + /* Last byte is reserved for null-terminator. */ + tmp[dummy - 1] = 0; + *version = tmp; + + return JAYLINK_OK; +} + +/** + * Retrieve the hardware information of a device. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_GET_HW_INFO capability. + * + * @param[in,out] devh Device handle. + * @param[in] mask A bit field where each set bit represents hardware + * information to request. See #jaylink_hardware_info for a + * description of the hardware information and their bit + * positions. + * @param[out] info Array to store the hardware information on success. Its + * content is undefined on failure. The array must be large + * enough to contain at least as many elements as bits set in + * @a mask. + * + * @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 Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_get_hardware_info(struct jaylink_device_handle *devh, + uint32_t mask, uint32_t *info) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[5]; + unsigned int i; + unsigned int num; + unsigned int length; + + if (!devh || !mask || !info) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + num = 0; + + for (i = 0; i < 32; i++) { + if (mask & (1 << i)) + num++; + } + + length = num * sizeof(uint32_t); + + ret = transport_start_write_read(devh, 5, length, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_GET_HW_INFO; + buffer_set_u32(buf, mask, 1); + + ret = transport_write(devh, buf, 5); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, (uint8_t *)info, length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + for (i = 0; i < num; i++) + info[i] = buffer_get_u32((uint8_t *)info, + i * sizeof(uint32_t)); + + return JAYLINK_OK; +} + +/** + * Retrieve the counter values of a device. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_GET_COUNTERS capability. + * + * @param[in,out] devh Device handle. + * @param[in] mask A bit field where each set bit represents a counter value to + * request. See #jaylink_counter for a description of the + * counters and their bit positions. + * @param[out] values Array to store the counter values on success. Its content + * is undefined on failure. The array must be large enough + * to contain at least as many elements as bits set in @p + * mask. + * + * @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 Other error conditions. + * + * @since 0.2.0 + */ +JAYLINK_API int jaylink_get_counters(struct jaylink_device_handle *devh, + uint32_t mask, uint32_t *values) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[5]; + unsigned int i; + unsigned int num; + unsigned int length; + + if (!devh || !mask || !values) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + num = 0; + + for (i = 0; i < 32; i++) { + if (mask & (1 << i)) + num++; + } + + length = num * sizeof(uint32_t); + ret = transport_start_write_read(devh, 5, length, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_GET_COUNTERS; + buffer_set_u32(buf, mask, 1); + + ret = transport_write(devh, buf, 5); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, (uint8_t *)values, length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + for (i = 0; i < num; i++) + values[i] = buffer_get_u32((uint8_t *)values, + i * sizeof(uint32_t)); + + return JAYLINK_OK; +} + +/** + * Retrieve the hardware version of a device. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_GET_HW_VERSION capability. + * + * @warning This function may return a value for @p version where + * #jaylink_hardware_version::type is not covered by + * #jaylink_hardware_type. + * + * @param[in,out] devh Device handle. + * @param[out] version Hardware version 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_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_get_hardware_version( + struct jaylink_device_handle *devh, + struct jaylink_hardware_version *version) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[4]; + uint32_t tmp; + + if (!devh || !version) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 1, 4, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_GET_HW_VERSION; + + ret = transport_write(devh, buf, 1); + + 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); + + version->type = (tmp / 1000000) % 100; + version->major = (tmp / 10000) % 100; + version->minor = (tmp / 100) % 100; + version->revision = tmp % 100; + + return JAYLINK_OK; +} + +/** + * Retrieve the hardware status of a device. + * + * @param[in,out] devh Device handle. + * @param[out] status Hardware status 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_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_get_hardware_status(struct jaylink_device_handle *devh, + struct jaylink_hardware_status *status) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[8]; + + if (!devh || !status) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 1, 8, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_GET_HW_STATUS; + + ret = transport_write(devh, buf, 1); + + 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->target_voltage = buffer_get_u16(buf, 0); + status->tck = buf[2]; + status->tdi = buf[3]; + status->tdo = buf[4]; + status->tms = buf[5]; + status->tres = buf[6]; + status->trst = buf[7]; + + return JAYLINK_OK; +} + +/** + * Retrieve the capabilities of a device. + * + * The capabilities are stored in a 32-bit bit array consisting of + * #JAYLINK_DEV_CAPS_SIZE bytes where each individual bit represents a + * capability. The first bit of this array is the least significant bit of the + * first byte and the following bits are sequentially numbered in order of + * increasing bit significance and byte index. A set bit indicates a supported + * capability. See #jaylink_device_capability for a description of the + * capabilities and their bit positions. + * + * @param[in,out] devh Device handle. + * @param[out] caps Buffer to store capabilities on success. Its content is + * undefined on failure. The buffer must be large enough to + * contain at least #JAYLINK_DEV_CAPS_SIZE bytes. + * + * @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 Other error conditions. + * + * @see jaylink_get_extended_caps() + * @see jaylink_has_cap() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_get_caps(struct jaylink_device_handle *devh, + uint8_t *caps) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[1]; + + if (!devh || !caps) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 1, JAYLINK_DEV_CAPS_SIZE, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_GET_CAPS; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, caps, JAYLINK_DEV_CAPS_SIZE); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + return JAYLINK_OK; +} + +/** + * Retrieve the extended capabilities of a device. + * + * The extended capabilities are stored in a 256-bit bit array consisting of + * #JAYLINK_DEV_EXT_CAPS_SIZE bytes. See jaylink_get_caps() for a further + * description of how the capabilities are represented in this bit array. For a + * description of the capabilities and their bit positions, see + * #jaylink_device_capability. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_GET_EXT_CAPS capability. + * + * @param[in,out] devh Device handle. + * @param[out] caps Buffer to store capabilities on success. Its content is + * undefined on failure. The buffer must be large enough to + * contain at least #JAYLINK_DEV_EXT_CAPS_SIZE bytes. + * + * @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 Other error conditions. + * + * @see jaylink_get_caps() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_get_extended_caps(struct jaylink_device_handle *devh, + uint8_t *caps) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[1]; + + if (!devh || !caps) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 1, JAYLINK_DEV_EXT_CAPS_SIZE, + true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_GET_EXT_CAPS; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, caps, JAYLINK_DEV_EXT_CAPS_SIZE); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + return JAYLINK_OK; +} + +/** + * Retrieve the size of free memory of a device. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_GET_FREE_MEMORY capability. + * + * @param[in,out] devh Device handle. + * @param[out] size Size of free memory in bytes 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_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_get_free_memory(struct jaylink_device_handle *devh, + uint32_t *size) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[4]; + + if (!devh || !size) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 1, 4, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_GET_FREE_MEMORY; + + ret = transport_write(devh, buf, 1); + + 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; + } + + *size = buffer_get_u32(buf, 0); + + return JAYLINK_OK; +} + +/** + * Read the raw configuration data of a device. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_READ_CONFIG capability. + * + * @param[in,out] devh Device handle. + * @param[out] config Buffer to store configuration data on success. Its + * content is undefined on failure. The buffer must be large + * enough to contain at least + * #JAYLINK_DEV_CONFIG_SIZE bytes. + * + * @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 Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_read_raw_config(struct jaylink_device_handle *devh, + uint8_t *config) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[1]; + + if (!devh || !config) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 1, JAYLINK_DEV_CONFIG_SIZE, + true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_READ_CONFIG; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, config, JAYLINK_DEV_CONFIG_SIZE); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + return JAYLINK_OK; +} + +/** + * Write the raw configuration data of a device. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_WRITE_CONFIG capability. + * + * @param[in,out] devh Device handle. + * @param[in] config Buffer to write configuration data from. The size of the + * configuration data is expected to be + * #JAYLINK_DEV_CONFIG_SIZE bytes. + * + * @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 Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_write_raw_config(struct jaylink_device_handle *devh, + const uint8_t *config) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[1]; + + if (!devh || !config) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 1 + JAYLINK_DEV_CONFIG_SIZE, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_WRITE_CONFIG; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_write(devh, config, JAYLINK_DEV_CONFIG_SIZE); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + return JAYLINK_OK; +} + +static void parse_conn_table(struct jaylink_connection *conns, + const uint8_t *buffer, uint16_t num, uint16_t entry_size) +{ + unsigned int i; + size_t offset; + struct in_addr in; + + offset = 0; + + for (i = 0; i < num; i++) { + conns[i].pid = buffer_get_u32(buffer, offset); + + in.s_addr = buffer_get_u32(buffer, offset + 4); + /* + * Use inet_ntoa() instead of inet_ntop() because the latter + * requires at least Windows Vista. + */ + strcpy(conns[i].hid, inet_ntoa(in)); + + conns[i].iid = buffer[offset + 8]; + conns[i].cid = buffer[offset + 9]; + conns[i].handle = buffer_get_u16(buffer, offset + 10); + conns[i].timestamp = buffer_get_u32(buffer, offset + 12); + offset = offset + entry_size; + } +} + +static bool _inet_pton(const char *str, struct in_addr *in) +{ +#ifdef _WIN32 + int ret; + struct sockaddr_in sock_in; + int length; + + length = sizeof(sock_in); + + /* + * Use WSAStringToAddress() instead of inet_pton() because the latter + * requires at least Windows Vista. + */ + ret = WSAStringToAddress((LPTSTR)str, AF_INET, NULL, + (LPSOCKADDR)&sock_in, &length); + + if (ret != 0) + return false; + + *in = sock_in.sin_addr; +#else + if (inet_pton(AF_INET, str, in) != 1) + return false; +#endif + + return true; +} + +/** + * Register a connection on a device. + * + * A connection can be registered by using 0 as handle. Additional information + * about the connection can be attached whereby the timestamp is a read-only + * value and therefore ignored for registration. On success, a new handle + * greater than 0 is obtained from the device. + * + * However, if an obtained handle does not appear in the list of device + * connections, the connection was not registered because the maximum number of + * connections on the device is reached. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_REGISTER capability. + * + * Example code: + * @code{.c} + * static bool register_connection(struct jaylink_device_handle *devh, + * struct jaylink_connection *conn) + * { + * int ret; + * struct jaylink_connection conns[JAYLINK_MAX_CONNECTIONS]; + * bool found_handle; + * size_t count; + * size_t i; + * + * conn->handle = 0; + * conn->pid = 0; + * strcpy(conn->hid, "0.0.0.0"); + * conn->iid = 0; + * conn->cid = 0; + * + * ret = jaylink_register(devh, conn, conns, &count); + * + * if (ret != JAYLINK_OK) { + * printf("jaylink_register() failed: %s.\n", + * jaylink_strerror(ret)); + * return false; + * } + * + * found_handle = false; + * + * for (i = 0; i < count; i++) { + * if (conns[i].handle == conn->handle) { + * found_handle = true; + * break; + * } + * } + * + * if (!found_handle) { + * printf("Maximum number of connections reached.\n"); + * return false; + * } + * + * printf("Connection successfully registered.\n"); + * + * return true; + * } + * @endcode + * + * @param[in,out] devh Device handle. + * @param[in,out] connection Connection to register on the device. + * @param[out] connections Array to store device connections on success. + * Its content is undefined on failure. The array must + * be large enough to contain at least + * #JAYLINK_MAX_CONNECTIONS elements. + * @param[out] count Number of device connections 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 Other error conditions. + * + * @see jaylink_unregister() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_register(struct jaylink_device_handle *devh, + struct jaylink_connection *connection, + struct jaylink_connection *connections, size_t *count) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[REG_MAX_SIZE]; + uint16_t handle; + uint16_t num; + uint16_t entry_size; + uint32_t size; + uint32_t table_size; + uint16_t info_size; + struct in_addr in; + + if (!devh || !connection || !connections || !count) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + + buf[0] = CMD_REGISTER; + buf[1] = REG_CMD_REGISTER; + buffer_set_u32(buf, connection->pid, 2); + + if (!_inet_pton(connection->hid, &in)) + return JAYLINK_ERR_ARG; + + buffer_set_u32(buf, in.s_addr, 6); + + buf[10] = connection->iid; + buf[11] = connection->cid; + buffer_set_u16(buf, connection->handle, 12); + + ret = transport_start_write_read(devh, 14, REG_MIN_SIZE, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_write(devh, buf, 14); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, REG_MIN_SIZE); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + handle = buffer_get_u16(buf, 0); + num = buffer_get_u16(buf, 2); + entry_size = buffer_get_u16(buf, 4); + info_size = buffer_get_u16(buf, 6); + + if (num > JAYLINK_MAX_CONNECTIONS) { + log_err(ctx, "Maximum number of device connections exceeded: " + "%u.", num); + return JAYLINK_ERR_PROTO; + } + + if (entry_size != REG_CONN_INFO_SIZE) { + log_err(ctx, "Invalid connection entry size: %u bytes.", + entry_size); + return JAYLINK_ERR_PROTO; + } + + table_size = num * entry_size; + size = REG_HEADER_SIZE + table_size + info_size; + + if (size > REG_MAX_SIZE) { + log_err(ctx, "Maximum registration information size exceeded: " + "%u bytes.", size); + return JAYLINK_ERR_PROTO; + } + + if (size > REG_MIN_SIZE) { + ret = transport_start_read(devh, size - REG_MIN_SIZE); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_read() failed: %s.", + jaylink_strerror(ret)); + return JAYLINK_ERR; + } + + ret = transport_read(devh, buf + REG_MIN_SIZE, + size - REG_MIN_SIZE); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return JAYLINK_ERR; + } + } + + if (!handle) { + log_err(ctx, "Obtained invalid connection handle."); + return JAYLINK_ERR_PROTO; + } + + connection->handle = handle; + parse_conn_table(connections, buf + REG_HEADER_SIZE, num, entry_size); + + *count = num; + + return JAYLINK_OK; +} + +/** + * Unregister a connection from a device. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_REGISTER capability. + * + * @param[in,out] devh Device handle. + * @param[in,out] connection Connection to unregister from the device. + * @param[out] connections Array to store device connections on success. + * Its content is undefined on failure. The array must + * be large enough to contain at least + * #JAYLINK_MAX_CONNECTIONS elements. + * @param[out] count Number of device connections 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 Other error conditions. + * + * @see jaylink_register() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_unregister(struct jaylink_device_handle *devh, + const struct jaylink_connection *connection, + struct jaylink_connection *connections, size_t *count) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[REG_MAX_SIZE]; + uint16_t num; + uint16_t entry_size; + uint32_t size; + uint32_t table_size; + uint16_t info_size; + struct in_addr in; + + if (!devh || !connection || !connections || !count) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + + buf[0] = CMD_REGISTER; + buf[1] = REG_CMD_UNREGISTER; + buffer_set_u32(buf, connection->pid, 2); + + if (!_inet_pton(connection->hid, &in)) + return JAYLINK_ERR_ARG; + + buffer_set_u32(buf, in.s_addr, 6); + + buf[10] = connection->iid; + buf[11] = connection->cid; + buffer_set_u16(buf, connection->handle, 12); + + ret = transport_start_write_read(devh, 14, REG_MIN_SIZE, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_write(devh, buf, 14); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, REG_MIN_SIZE); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + num = buffer_get_u16(buf, 2); + entry_size = buffer_get_u16(buf, 4); + info_size = buffer_get_u16(buf, 6); + + if (num > JAYLINK_MAX_CONNECTIONS) { + log_err(ctx, "Maximum number of device connections exceeded: " + "%u.", num); + return JAYLINK_ERR_PROTO; + } + + if (entry_size != REG_CONN_INFO_SIZE) { + log_err(ctx, "Invalid connection entry size: %u bytes.", + entry_size); + return JAYLINK_ERR_PROTO; + } + + table_size = num * entry_size; + size = REG_HEADER_SIZE + table_size + info_size; + + if (size > REG_MAX_SIZE) { + log_err(ctx, "Maximum registration information size exceeded: " + "%u bytes.", size); + return JAYLINK_ERR_PROTO; + } + + if (size > REG_MIN_SIZE) { + ret = transport_start_read(devh, size - REG_MIN_SIZE); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_read() failed: %s.", + jaylink_strerror(ret)); + return JAYLINK_ERR; + } + + ret = transport_read(devh, buf + REG_MIN_SIZE, + size - REG_MIN_SIZE); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return JAYLINK_ERR; + } + } + + parse_conn_table(connections, buf + REG_HEADER_SIZE, num, entry_size); + + *count = num; + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/discovery.c b/src/jtag/drivers/libjaylink/libjaylink/discovery.c new file mode 100644 index 0000000..1ac96e7 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/discovery.c @@ -0,0 +1,106 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2016 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 <stdlib.h> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * Device discovery. + */ + +static void clear_discovery_list(struct jaylink_context *ctx) +{ + struct list *item; + struct list *tmp; + struct jaylink_device *dev; + + item = ctx->discovered_devs; + + while (item) { + dev = (struct jaylink_device *)item->data; + jaylink_unref_device(dev); + + tmp = item; + item = item->next; + free(tmp); + } + + ctx->discovered_devs = NULL; +} + +/** + * Scan for devices. + * + * @param[in,out] ctx libjaylink context. + * @param[in] ifaces Host interfaces to scan for devices. Use bitwise OR to + * specify multiple interfaces, or 0 to use all available + * interfaces. See #jaylink_host_interface for a description + * of the interfaces. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_get_devices() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_discovery_scan(struct jaylink_context *ctx, + uint32_t ifaces) +{ + int ret; + + if (!ctx) + return JAYLINK_ERR_ARG; + + if (!ifaces) + ifaces = JAYLINK_HIF_USB | JAYLINK_HIF_TCP; + + clear_discovery_list(ctx); + +#ifdef HAVE_LIBUSB + if (ifaces & JAYLINK_HIF_USB) { + ret = discovery_usb_scan(ctx); + + if (ret != JAYLINK_OK) { + log_err(ctx, "USB device discovery failed."); + return ret; + } + } +#endif + + if (ifaces & JAYLINK_HIF_TCP) { + ret = discovery_tcp_scan(ctx); + + if (ret != JAYLINK_OK) { + log_err(ctx, "TCP/IP device discovery failed."); + return ret; + } + } + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/discovery_tcp.c b/src/jtag/drivers/libjaylink/libjaylink/discovery_tcp.c new file mode 100644 index 0000000..002fa67 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/discovery_tcp.c @@ -0,0 +1,349 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2015-2017 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 <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <ctype.h> +#ifdef _WIN32 +#include <winsock2.h> +#else +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * Device discovery (TCP/IP). + */ + +/** @cond PRIVATE */ +/** Size of the advertisement message in bytes. */ +#define ADV_MESSAGE_SIZE 128 + +/** Device discovery port number. */ +#define DISC_PORT 19020 + +/** Size of the discovery message in bytes. */ +#define DISC_MESSAGE_SIZE 64 + +/** Discovery timeout in milliseconds. */ +#define DISC_TIMEOUT 20 +/** @endcond */ + +static bool compare_devices(const void *a, const void *b) +{ + const struct jaylink_device *dev; + const struct jaylink_device *new_dev; + + dev = a; + new_dev = b; + + if (dev->iface != JAYLINK_HIF_TCP) + return false; + + if (memcmp(dev->ipv4_address, new_dev->ipv4_address, + sizeof(dev->ipv4_address)) != 0) + return false; + + if (dev->serial_number != new_dev->serial_number) + return false; + + if (memcmp(dev->mac_address, new_dev->mac_address, + sizeof(dev->mac_address)) != 0) + return false; + + if (strcmp(dev->product_name, new_dev->product_name) != 0) + return false; + + if (strcmp(dev->nickname, new_dev->nickname) != 0) + return false; + + if (dev->hw_version.type != new_dev->hw_version.type) + return false; + + if (dev->hw_version.major != new_dev->hw_version.major) + return false; + + if (dev->hw_version.minor != new_dev->hw_version.minor) + return false; + + if (dev->hw_version.revision != new_dev->hw_version.revision) + return false; + + return true; +} + +static struct jaylink_device *find_device(struct list *list, + const struct jaylink_device *dev) +{ + struct list *item; + + item = list_find_custom(list, &compare_devices, dev); + + if (item) + return item->data; + + return NULL; +} + +static bool parse_adv_message(struct jaylink_device *dev, + const uint8_t *buffer) +{ + struct in_addr in; + uint32_t tmp; + + if (memcmp(buffer, "Found", 5) != 0) + return false; + + /* + * Use inet_ntoa() instead of inet_ntop() because the latter requires + * at least Windows Vista. + */ + memcpy(&in, buffer + 16, 4); + memcpy(dev->ipv4_address, inet_ntoa(in), sizeof(dev->ipv4_address)); + + memcpy(dev->mac_address, buffer + 32, sizeof(dev->mac_address)); + dev->has_mac_address = true; + + dev->serial_number = buffer_get_u32(buffer, 48); + dev->valid_serial_number = true; + + tmp = buffer_get_u32(buffer, 52); + dev->hw_version.type = (tmp / 1000000) % 100; + dev->hw_version.major = (tmp / 10000) % 100; + dev->hw_version.minor = (tmp / 100) % 100; + dev->hw_version.revision = tmp % 100; + dev->has_hw_version = true; + + memcpy(dev->product_name, buffer + 64, sizeof(dev->product_name)); + dev->product_name[JAYLINK_PRODUCT_NAME_MAX_LENGTH - 1] = '\0'; + dev->has_product_name = isprint((unsigned char)dev->product_name[0]); + + memcpy(dev->nickname, buffer + 96, sizeof(dev->nickname)); + dev->nickname[JAYLINK_NICKNAME_MAX_LENGTH - 1] = '\0'; + dev->has_nickname = isprint((unsigned char)dev->nickname[0]); + + return true; +} + +static struct jaylink_device *probe_device(struct jaylink_context *ctx, + struct sockaddr_in *addr, const uint8_t *buffer) +{ + struct jaylink_device tmp; + struct jaylink_device *dev; + + /* + * Use inet_ntoa() instead of inet_ntop() because the latter requires + * at least Windows Vista. + */ + log_dbg(ctx, "Received advertisement message (IPv4 address = %s).", + inet_ntoa(addr->sin_addr)); + + if (!parse_adv_message(&tmp, buffer)) { + log_dbg(ctx, "Received invalid advertisement message."); + return NULL; + } + + log_dbg(ctx, "Found device (IPv4 address = %s).", tmp.ipv4_address); + log_dbg(ctx, "Device: MAC address = %02x:%02x:%02x:%02x:%02x:%02x.", + tmp.mac_address[0], tmp.mac_address[1], tmp.mac_address[2], + tmp.mac_address[3], tmp.mac_address[4], tmp.mac_address[5]); + log_dbg(ctx, "Device: Serial number = %u.", tmp.serial_number); + + if (tmp.has_product_name) + log_dbg(ctx, "Device: Product = %s.", tmp.product_name); + + if (tmp.has_nickname) + log_dbg(ctx, "Device: Nickname = %s.", tmp.nickname); + + dev = find_device(ctx->discovered_devs, &tmp); + + if (dev) { + log_dbg(ctx, "Ignoring already discovered device."); + return NULL; + } + + dev = find_device(ctx->devs, &tmp); + + if (dev) { + log_dbg(ctx, "Using existing device instance."); + return jaylink_ref_device(dev); + } + + log_dbg(ctx, "Allocating new device instance."); + + dev = device_allocate(ctx); + + if (!dev) { + log_warn(ctx, "Device instance malloc failed."); + return NULL; + } + + dev->iface = JAYLINK_HIF_TCP; + + dev->serial_number = tmp.serial_number; + dev->valid_serial_number = tmp.valid_serial_number; + + memcpy(dev->ipv4_address, tmp.ipv4_address, sizeof(dev->ipv4_address)); + + memcpy(dev->mac_address, tmp.mac_address, sizeof(dev->mac_address)); + dev->has_mac_address = tmp.has_mac_address; + + memcpy(dev->product_name, tmp.product_name, sizeof(dev->product_name)); + dev->has_product_name = tmp.has_product_name; + + memcpy(dev->nickname, tmp.nickname, sizeof(dev->nickname)); + dev->has_nickname = tmp.has_nickname; + + dev->hw_version = tmp.hw_version; + dev->has_hw_version = tmp.has_hw_version; + + return dev; +} + +/** @private */ +JAYLINK_PRIV int discovery_tcp_scan(struct jaylink_context *ctx) +{ + int ret; + int sock; + int opt_value; + fd_set rfds; + struct sockaddr_in addr; + size_t addr_length; + struct timeval timeout; + uint8_t buf[ADV_MESSAGE_SIZE]; + struct jaylink_device *dev; + size_t length; + size_t num_devs; + + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + if (sock < 0) { + log_err(ctx, "Failed to create discovery socket."); + return JAYLINK_ERR; + } + + opt_value = true; + + if (!socket_set_option(sock, SOL_SOCKET, SO_BROADCAST, &opt_value, + sizeof(opt_value))) { + log_err(ctx, "Failed to enable broadcast option for discovery " + "socket."); + socket_close(sock); + return JAYLINK_ERR; + } + + memset(&addr, 0, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = htons(DISC_PORT); + addr.sin_addr.s_addr = INADDR_ANY; + + if (!socket_bind(sock, (struct sockaddr *)&addr, + sizeof(struct sockaddr_in))) { + log_err(ctx, "Failed to bind discovery socket."); + socket_close(sock); + return JAYLINK_ERR; + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(DISC_PORT); + addr.sin_addr.s_addr = INADDR_BROADCAST; + + memset(buf, 0, DISC_MESSAGE_SIZE); + memcpy(buf, "Discover", 8); + + log_dbg(ctx, "Sending discovery message."); + + length = DISC_MESSAGE_SIZE; + + if (!socket_sendto(sock, (char *)buf, &length, 0, + (const struct sockaddr *)&addr, sizeof(addr))) { + log_err(ctx, "Failed to send discovery message."); + socket_close(sock); + return JAYLINK_ERR_IO; + } + + if (length < DISC_MESSAGE_SIZE) { + log_err(ctx, "Only sent %zu bytes of discovery message.", + length); + socket_close(sock); + return JAYLINK_ERR_IO; + } + + timeout.tv_sec = DISC_TIMEOUT / 1000; + timeout.tv_usec = (DISC_TIMEOUT % 1000) * 1000; + + num_devs = 0; + + while (true) { + FD_ZERO(&rfds); + FD_SET(sock, &rfds); + + ret = select(sock + 1, &rfds, NULL, NULL, &timeout); + + if (ret <= 0) + break; + + if (!FD_ISSET(sock, &rfds)) + continue; + + length = ADV_MESSAGE_SIZE; + addr_length = sizeof(struct sockaddr_in); + + if (!socket_recvfrom(sock, buf, &length, 0, + (struct sockaddr *)&addr, &addr_length)) { + log_warn(ctx, "Failed to receive advertisement " + "message."); + continue; + } + + /* + * Filter out messages with an invalid size. This includes the + * broadcast message we sent before. + */ + if (length != ADV_MESSAGE_SIZE) + continue; + + dev = probe_device(ctx, &addr, buf); + + if (dev) { + ctx->discovered_devs = list_prepend( + ctx->discovered_devs, dev); + num_devs++; + } + } + + socket_close(sock); + + if (ret < 0) { + log_err(ctx, "select() failed."); + return JAYLINK_ERR; + } + + log_dbg(ctx, "Found %zu TCP/IP device(s).", num_devs); + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/discovery_usb.c b/src/jtag/drivers/libjaylink/libjaylink/discovery_usb.c new file mode 100644 index 0000000..48d5322 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/discovery_usb.c @@ -0,0 +1,280 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2016 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 <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include <sys/types.h> + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/* + * libusb.h includes windows.h and therefore must be included after anything + * that includes winsock2.h. + */ +#include <libusb.h> + +/** + * @file + * + * Device discovery (USB). + */ + +/** @cond PRIVATE */ +/** USB Vendor ID (VID) of SEGGER products. */ +#define USB_VENDOR_ID 0x1366 + +/* USB Product IDs (PID) and their corresponding USB addresses. */ +static const uint16_t pids[][2] = { + {0x0101, 0}, + {0x0102, 1}, + {0x0103, 2}, + {0x0104, 3}, + {0x0105, 0}, + {0x0107, 0}, + {0x0108, 0}, + {0x1010, 0}, + {0x1011, 0}, + {0x1012, 0}, + {0x1013, 0}, + {0x1014, 0}, + {0x1015, 0}, + {0x1016, 0}, + {0x1017, 0}, + {0x1018, 0} +}; + +/** Maximum length of the USB string descriptor for the serial number. */ +#define USB_SERIAL_NUMBER_LENGTH 12 + +/** + * Maximum number of digits in a serial number + * + * The serial number of a device consists of at most 9 digits but user defined + * serial numbers are allowed with up to 10 digits. + */ +#define MAX_SERIAL_NUMBER_DIGITS 10 +/** @endcond */ + +static bool parse_serial_number(const char *str, uint32_t *serial_number) +{ + size_t length; + + length = strlen(str); + + /* + * Skip the first digits which are not part of a valid serial number. + * This is necessary because some devices erroneously use random digits + * instead of zeros for padding. + */ + if (length > MAX_SERIAL_NUMBER_DIGITS) + str = str + (length - MAX_SERIAL_NUMBER_DIGITS); + + if (jaylink_parse_serial_number(str, serial_number) != JAYLINK_OK) + return false; + + return true; +} + +static bool compare_devices(const void *a, const void *b) +{ + const struct jaylink_device *dev; + const struct libusb_device *usb_dev; + + dev = a; + usb_dev = b; + + if (dev->iface != JAYLINK_HIF_USB) + return false; + + if (dev->usb_dev == usb_dev) + return true; + + return false; +} + +static struct jaylink_device *find_device(const struct jaylink_context *ctx, + const struct libusb_device *usb_dev) +{ + struct list *item; + + item = list_find_custom(ctx->devs, &compare_devices, usb_dev); + + if (item) + return item->data; + + return NULL; +} + +static struct jaylink_device *probe_device(struct jaylink_context *ctx, + struct libusb_device *usb_dev) +{ + int ret; + struct libusb_device_descriptor desc; + struct libusb_device_handle *usb_devh; + struct jaylink_device *dev; + char buf[USB_SERIAL_NUMBER_LENGTH + 1]; + uint8_t usb_address; + uint32_t serial_number; + bool valid_serial_number; + bool found_device; + size_t i; + + ret = libusb_get_device_descriptor(usb_dev, &desc); + + if (ret != LIBUSB_SUCCESS) { + log_warn(ctx, "Failed to get device descriptor: %s.", + libusb_error_name(ret)); + return NULL; + } + + if (desc.idVendor != USB_VENDOR_ID) + return NULL; + + found_device = false; + + for (i = 0; i < sizeof(pids) / sizeof(pids[0]); i++) { + if (pids[i][0] == desc.idProduct) { + found_device = true; + usb_address = pids[i][1]; + break; + } + } + + if (!found_device) + return NULL; + + log_dbg(ctx, "Found device (VID:PID = %04x:%04x, bus:address = " + "%03u:%03u).", desc.idVendor, desc.idProduct, + libusb_get_bus_number(usb_dev), + libusb_get_device_address(usb_dev)); + + /* + * Search for an already allocated device instance for this device and + * if found return a reference to it. + */ + dev = find_device(ctx, usb_dev); + + if (dev) { + log_dbg(ctx, "Device: USB address = %u.", dev->usb_address); + + if (dev->valid_serial_number) + log_dbg(ctx, "Device: Serial number = %u.", + dev->serial_number); + else + log_dbg(ctx, "Device: Serial number = N/A."); + + log_dbg(ctx, "Using existing device instance."); + return jaylink_ref_device(dev); + } + + /* Open the device to be able to retrieve its serial number. */ + ret = libusb_open(usb_dev, &usb_devh); + + if (ret != LIBUSB_SUCCESS) { + log_warn(ctx, "Failed to open device: %s.", + libusb_error_name(ret)); + return NULL; + } + + serial_number = 0; + valid_serial_number = true; + + ret = libusb_get_string_descriptor_ascii(usb_devh, desc.iSerialNumber, + (unsigned char *)buf, USB_SERIAL_NUMBER_LENGTH + 1); + + libusb_close(usb_devh); + + if (ret < 0) { + log_warn(ctx, "Failed to retrieve serial number: %s.", + libusb_error_name(ret)); + valid_serial_number = false; + } + + if (valid_serial_number) { + if (!parse_serial_number(buf, &serial_number)) { + log_warn(ctx, "Failed to parse serial number."); + return NULL; + } + } + + log_dbg(ctx, "Device: USB address = %u.", usb_address); + + if (valid_serial_number) + log_dbg(ctx, "Device: Serial number = %u.", serial_number); + else + log_dbg(ctx, "Device: Serial number = N/A."); + + log_dbg(ctx, "Allocating new device instance."); + + dev = device_allocate(ctx); + + if (!dev) { + log_warn(ctx, "Device instance malloc failed."); + return NULL; + } + + dev->iface = JAYLINK_HIF_USB; + dev->usb_dev = libusb_ref_device(usb_dev); + dev->usb_address = usb_address; + dev->serial_number = serial_number; + dev->valid_serial_number = valid_serial_number; + + return dev; +} + +JAYLINK_PRIV int discovery_usb_scan(struct jaylink_context *ctx) +{ + ssize_t ret; + struct libusb_device **devs; + struct jaylink_device *dev; + size_t num; + size_t i; + + ret = libusb_get_device_list(ctx->usb_ctx, &devs); + + if (ret == LIBUSB_ERROR_IO) { + log_err(ctx, "Failed to retrieve device list: input/output " + "error."); + return JAYLINK_ERR_IO; + } else if (ret < 0) { + log_err(ctx, "Failed to retrieve device list: %s.", + libusb_error_name(ret)); + return JAYLINK_ERR; + } + + num = 0; + + for (i = 0; devs[i]; i++) { + dev = probe_device(ctx, devs[i]); + + if (!dev) + continue; + + ctx->discovered_devs = list_prepend(ctx->discovered_devs, dev); + num++; + } + + libusb_free_device_list(devs, true); + log_dbg(ctx, "Found %zu USB device(s).", num); + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/emucom.c b/src/jtag/drivers/libjaylink/libjaylink/emucom.c new file mode 100644 index 0000000..035cb99 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/emucom.c @@ -0,0 +1,287 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2015-2016 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 + * + * Emulator communication (EMUCOM). + */ + +/** @cond PRIVATE */ +#define CMD_EMUCOM 0xee + +#define EMUCOM_CMD_READ 0x00 +#define EMUCOM_CMD_WRITE 0x01 + +/** Bitmask for the error indication bit of an EMUCOM status code. */ +#define EMUCOM_ERR 0x80000000 + +/** Error code indicating that the channel is not supported by the device. */ +#define EMUCOM_ERR_NOT_SUPPORTED 0x80000001 + +/** + * Error code indicating that the channel is not available for the requested + * number of bytes to be read. + * + * The number of bytes available on this channel is encoded in the lower + * 24 bits of the EMUCOM status code. + * + * @see EMUCOM_AVAILABLE_BYTES_MASK + */ +#define EMUCOM_ERR_NOT_AVAILABLE 0x81000000 + +/** + * Bitmask to extract the number of available bytes on a channel from an EMUCOM + * status code. + */ +#define EMUCOM_AVAILABLE_BYTES_MASK 0x00ffffff +/** @endcond */ + +/** + * Read from an EMUCOM channel. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_EMUCOM capability. + * + * @param[in,out] devh Device handle. + * @param[in] channel Channel to read data from. + * @param[out] buffer Buffer to store read data on success. Its content is + * undefined on failure. + * @param[in,out] length Number of bytes to read. On success, the value gets + * updated with the actual number of bytes read. Unless + * otherwise specified, 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_NOT_SUPPORTED Channel is not supported by the + * device. + * @retval JAYLINK_ERR_DEV_NOT_AVAILABLE Channel is not available for the + * requested amount of data. @p length is + * updated with the number of bytes + * available on this channel. + * @retval JAYLINK_ERR_DEV Unspecified device error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_emucom_read(struct jaylink_device_handle *devh, + uint32_t channel, uint8_t *buffer, uint32_t *length) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[10]; + uint32_t tmp; + + if (!devh || !buffer || !length) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 10, 4, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_EMUCOM; + buf[1] = EMUCOM_CMD_READ; + + buffer_set_u32(buf, channel, 2); + buffer_set_u32(buf, *length, 6); + + ret = transport_write(devh, buf, 10); + + 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 == EMUCOM_ERR_NOT_SUPPORTED) + return JAYLINK_ERR_DEV_NOT_SUPPORTED; + + if ((tmp & ~EMUCOM_AVAILABLE_BYTES_MASK) == EMUCOM_ERR_NOT_AVAILABLE) { + *length = tmp & EMUCOM_AVAILABLE_BYTES_MASK; + return JAYLINK_ERR_DEV_NOT_AVAILABLE; + } + + if (tmp & EMUCOM_ERR) { + log_err(ctx, "Failed to read from channel 0x%x: 0x%x.", + channel, tmp); + return JAYLINK_ERR_DEV; + } + + if (tmp > *length) { + log_err(ctx, "Requested at most %u bytes but device " + "returned %u bytes.", *length, tmp); + return JAYLINK_ERR_PROTO; + } + + *length = tmp; + + if (!tmp) + return JAYLINK_OK; + + 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; + } + + return JAYLINK_OK; +} + +/** + * Write to an EMUCOM channel. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_EMUCOM capability. + * + * @param[in,out] devh Device handle. + * @param[in] channel Channel to write data to. + * @param[in] buffer Buffer to write data from. + * @param[in,out] length Number of bytes to write. On success, the value gets + * updated with the actual number of bytes written. 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_NOT_SUPPORTED Channel is not supported by the + * device. + * @retval JAYLINK_ERR_DEV Unspecified device error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_emucom_write(struct jaylink_device_handle *devh, + uint32_t channel, const uint8_t *buffer, uint32_t *length) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[10]; + uint32_t tmp; + + if (!devh || !buffer || !length) + return JAYLINK_ERR_ARG; + + if (!*length) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 10, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_EMUCOM; + buf[1] = EMUCOM_CMD_WRITE; + + buffer_set_u32(buf, channel, 2); + buffer_set_u32(buf, *length, 6); + + ret = transport_write(devh, buf, 10); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_start_write_read(devh, *length, 4, false); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_write(devh, buffer, *length); + + 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 == EMUCOM_ERR_NOT_SUPPORTED) + return JAYLINK_ERR_DEV_NOT_SUPPORTED; + + if (tmp & EMUCOM_ERR) { + log_err(ctx, "Failed to write to channel 0x%x: 0x%x.", + channel, tmp); + return JAYLINK_ERR_DEV; + } + + if (tmp > *length) { + log_err(ctx, "Only %u bytes were supposed to be written, but " + "the device reported %u written bytes.", *length, tmp); + return JAYLINK_ERR_PROTO; + } + + *length = tmp; + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/error.c b/src/jtag/drivers/libjaylink/libjaylink/error.c new file mode 100644 index 0000000..2c696fc --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/error.c @@ -0,0 +1,118 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-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 "libjaylink.h" + +/** + * @file + * + * Error handling. + */ + +/** + * Return a human-readable description of a libjaylink error code. + * + * @param[in] error_code A libjaylink error code. See #jaylink_error for valid + * values. + * + * @return A string which describes the given error code, or the string + * <i>unknown error</i> if the error code is not known. The string is + * null-terminated and must not be free'd by the caller. + * + * @since 0.1.0 + */ +JAYLINK_API const char *jaylink_strerror(int error_code) +{ + switch (error_code) { + case JAYLINK_OK: + return "no error"; + case JAYLINK_ERR: + return "unspecified error"; + case JAYLINK_ERR_ARG: + return "invalid argument"; + case JAYLINK_ERR_MALLOC: + return "memory allocation error"; + case JAYLINK_ERR_TIMEOUT: + return "timeout occurred"; + case JAYLINK_ERR_PROTO: + return "protocol violation"; + case JAYLINK_ERR_NOT_AVAILABLE: + return "entity not available"; + case JAYLINK_ERR_NOT_SUPPORTED: + return "operation not supported"; + case JAYLINK_ERR_IO: + return "input/output error"; + case JAYLINK_ERR_DEV: + return "device: unspecified error"; + case JAYLINK_ERR_DEV_NOT_SUPPORTED: + return "device: operation not supported"; + case JAYLINK_ERR_DEV_NOT_AVAILABLE: + return "device: entity not available"; + case JAYLINK_ERR_DEV_NO_MEMORY: + return "device: not enough memory to perform operation"; + default: + return "unknown error"; + } +} + +/** + * Return the name of a libjaylink error code. + * + * @param[in] error_code A libjaylink error code. See #jaylink_error for valid + * values. + * + * @return A string which contains the name for the given error code, or the + * string <i>unknown error code</i> if the error code is not known. The + * string is null-terminated and must not be free'd by the caller. + * + * @since 0.1.0 + */ +JAYLINK_API const char *jaylink_strerror_name(int error_code) +{ + switch (error_code) { + case JAYLINK_OK: + return "JAYLINK_OK"; + case JAYLINK_ERR: + return "JAYLINK_ERR"; + case JAYLINK_ERR_ARG: + return "JAYLINK_ERR_ARG"; + case JAYLINK_ERR_MALLOC: + return "JAYLINK_ERR_MALLOC"; + case JAYLINK_ERR_TIMEOUT: + return "JAYLINK_ERR_TIMEOUT"; + case JAYLINK_ERR_PROTO: + return "JAYLINK_ERR_PROTO"; + case JAYLINK_ERR_NOT_AVAILABLE: + return "JAYLINK_ERR_NOT_AVAILABLE"; + case JAYLINK_ERR_NOT_SUPPORTED: + return "JAYLINK_ERR_NOT_SUPPORTED"; + case JAYLINK_ERR_IO: + return "JAYLINK_ERR_IO"; + case JAYLINK_ERR_DEV: + return "JAYLINK_ERR_DEV"; + case JAYLINK_ERR_DEV_NOT_SUPPORTED: + return "JAYLINK_ERR_DEV_NOT_SUPPORTED"; + case JAYLINK_ERR_DEV_NOT_AVAILABLE: + return "JAYLINK_ERR_DEV_NOT_AVAILABLE"; + case JAYLINK_ERR_DEV_NO_MEMORY: + return "JAYLINK_ERR_DEV_NO_MEMORY"; + default: + return "unknown error code"; + } +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/fileio.c b/src/jtag/drivers/libjaylink/libjaylink/fileio.c new file mode 100644 index 0000000..933c366 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/fileio.c @@ -0,0 +1,499 @@ +/* + * 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 <string.h> + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * File I/O functions. + */ + +/** @cond PRIVATE */ +#define CMD_FILE_IO 0x1e + +#define FILE_IO_CMD_READ 0x64 +#define FILE_IO_CMD_WRITE 0x65 +#define FILE_IO_CMD_GET_SIZE 0x66 +#define FILE_IO_CMD_DELETE 0x67 + +#define FILE_IO_PARAM_FILENAME 0x01 +#define FILE_IO_PARAM_OFFSET 0x02 +#define FILE_IO_PARAM_LENGTH 0x03 + +#define FILE_IO_ERR 0x80000000 +/** @endcond */ + +/** + * Read from a file. + * + * The maximum amount of data that can be read from a file at once is + * #JAYLINK_FILE_MAX_TRANSFER_SIZE bytes. Multiple reads in conjunction with + * the @p offset parameter are needed for larger files. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_FILE_IO capability. + * + * @param[in,out] devh Device handle. + * @param[in] filename Name of the file to read from. The length of the name + * must not exceed #JAYLINK_FILE_NAME_MAX_LENGTH bytes. + * @param[out] buffer Buffer to store read data on success. Its content is + * undefined on failure + * @param[in] offset Offset in bytes relative to the beginning of the file from + * where to start reading. + * @param[in,out] length 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_IO Input/output error. + * @retval JAYLINK_ERR_DEV Unspecified device error, or the file was not found. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_file_read(struct jaylink_device_handle *devh, + const char *filename, uint8_t *buffer, uint32_t offset, + uint32_t *length) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[18 + JAYLINK_FILE_NAME_MAX_LENGTH]; + size_t filename_length; + uint32_t tmp; + + if (!devh || !filename || !buffer || !length) + return JAYLINK_ERR_ARG; + + if (!*length) + return JAYLINK_ERR_ARG; + + if (*length > JAYLINK_FILE_MAX_TRANSFER_SIZE) + return JAYLINK_ERR_ARG; + + filename_length = strlen(filename); + + if (!filename_length) + return JAYLINK_ERR_ARG; + + if (filename_length > JAYLINK_FILE_NAME_MAX_LENGTH) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 18 + filename_length, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_FILE_IO; + buf[1] = FILE_IO_CMD_READ; + buf[2] = 0x00; + + buf[3] = filename_length; + buf[4] = FILE_IO_PARAM_FILENAME; + memcpy(buf + 5, filename, filename_length); + + buf[filename_length + 5] = 0x04; + buf[filename_length + 6] = FILE_IO_PARAM_OFFSET; + buffer_set_u32(buf, offset, filename_length + 7); + + buf[filename_length + 11] = 0x04; + buf[filename_length + 12] = FILE_IO_PARAM_LENGTH; + buffer_set_u32(buf, *length, filename_length + 13); + + buf[filename_length + 17] = 0x00; + + ret = transport_write(devh, buf, 18 + filename_length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + 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, buffer, *length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_start_read(devh, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_read() 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 & FILE_IO_ERR) + return JAYLINK_ERR_DEV; + + *length = tmp; + + return JAYLINK_OK; +} + +/** + * Write to a file. + * + * If a file does not exist, a new file is created. + * + * The maximum amount of data that can be written to a file at once is + * #JAYLINK_FILE_MAX_TRANSFER_SIZE bytes. Multiple writes in conjunction with + * the @p offset parameter are needed for larger files. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_FILE_IO capability. + * + * @param[in,out] devh Device handle. + * @param[in] filename Name of the file to write to. The length of the name + * must not exceed #JAYLINK_FILE_NAME_MAX_LENGTH bytes. + * @param[in] buffer Buffer to write data from. + * @param[in] offset Offset in bytes relative to the beginning of the file from + * where to start writing. + * @param[in,out] length Number of bytes to write. On success, the value gets + * updated with the actual number of bytes written. 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_IO Input/output error. + * @retval JAYLINK_ERR_DEV Unspecified device error, or the file was not found. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_file_write(struct jaylink_device_handle *devh, + const char *filename, const uint8_t *buffer, uint32_t offset, + uint32_t *length) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[18 + JAYLINK_FILE_NAME_MAX_LENGTH]; + size_t filename_length; + uint32_t tmp; + + if (!devh || !filename || !buffer || !length) + return JAYLINK_ERR_ARG; + + if (!*length) + return JAYLINK_ERR_ARG; + + if (*length > JAYLINK_FILE_MAX_TRANSFER_SIZE) + return JAYLINK_ERR_ARG; + + filename_length = strlen(filename); + + if (!filename_length) + return JAYLINK_ERR_ARG; + + if (filename_length > JAYLINK_FILE_NAME_MAX_LENGTH) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 18 + filename_length, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_FILE_IO; + buf[1] = FILE_IO_CMD_WRITE; + buf[2] = 0x00; + + buf[3] = filename_length; + buf[4] = FILE_IO_PARAM_FILENAME; + memcpy(buf + 5, filename, filename_length); + + buf[filename_length + 5] = 0x04; + buf[filename_length + 6] = FILE_IO_PARAM_OFFSET; + buffer_set_u32(buf, offset, filename_length + 7); + + buf[filename_length + 11] = 0x04; + buf[filename_length + 12] = FILE_IO_PARAM_LENGTH; + buffer_set_u32(buf, *length, filename_length + 13); + + buf[filename_length + 17] = 0x00; + + ret = transport_write(devh, buf, 18 + filename_length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_start_write(devh, *length, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_write(devh, buffer, *length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_start_read(devh, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_read() 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 & FILE_IO_ERR) + return JAYLINK_ERR_DEV; + + *length = tmp; + + return JAYLINK_OK; +} + +/** + * Retrieve the size of a file. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_FILE_IO capability. + * + * @param[in,out] devh Device handle. + * @param[in] filename Name of the file to retrieve the size of. The length + * of the name must not exceed + * #JAYLINK_FILE_NAME_MAX_LENGTH bytes. + * @param[out] size Size of the file in bytes 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_IO Input/output error. + * @retval JAYLINK_ERR_DEV Unspecified device error, or the file was not found. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_file_get_size(struct jaylink_device_handle *devh, + const char *filename, uint32_t *size) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[6 + JAYLINK_FILE_NAME_MAX_LENGTH]; + size_t length; + uint32_t tmp; + + if (!devh || !filename || !size) + return JAYLINK_ERR_ARG; + + length = strlen(filename); + + if (!length) + return JAYLINK_ERR_ARG; + + if (length > JAYLINK_FILE_NAME_MAX_LENGTH) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 6 + length, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_FILE_IO; + buf[1] = FILE_IO_CMD_GET_SIZE; + buf[2] = 0x00; + + buf[3] = length; + buf[4] = FILE_IO_PARAM_FILENAME; + memcpy(buf + 5, filename, length); + + buf[length + 5] = 0x00; + + ret = transport_write(devh, buf, 6 + length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_start_read(devh, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_read() 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 & FILE_IO_ERR) + return JAYLINK_ERR_DEV; + + *size = tmp; + + return JAYLINK_OK; +} + +/** + * Delete a file. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_FILE_IO capability. + * + * @param[in,out] devh Device handle. + * @param[in] filename Name of the file to delete. The length of the name + * must not exceed #JAYLINK_FILE_NAME_MAX_LENGTH bytes. + * + * @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, or the file was not found. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_file_delete(struct jaylink_device_handle *devh, + const char *filename) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[6 + JAYLINK_FILE_NAME_MAX_LENGTH]; + size_t length; + uint32_t tmp; + + if (!devh || !filename) + return JAYLINK_ERR_ARG; + + length = strlen(filename); + + if (!length) + return JAYLINK_ERR_ARG; + + if (length > JAYLINK_FILE_NAME_MAX_LENGTH) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 6 + length, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_FILE_IO; + buf[1] = FILE_IO_CMD_DELETE; + buf[2] = 0x00; + + buf[3] = length; + buf[4] = FILE_IO_PARAM_FILENAME; + memcpy(buf + 5, filename, length); + + buf[length + 5] = 0x00; + + ret = transport_write(devh, buf, 6 + length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_start_read(devh, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_read() 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 & FILE_IO_ERR) + return JAYLINK_ERR_DEV; + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/jtag.c b/src/jtag/drivers/libjaylink/libjaylink/jtag.c new file mode 100644 index 0000000..c0c65de --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/jtag.c @@ -0,0 +1,259 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-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 + * + * JTAG functions. + */ + +/** @cond PRIVATE */ +#define CMD_JTAG_IO_V2 0xce +#define CMD_JTAG_IO_V3 0xcf +#define CMD_JTAG_CLEAR_TRST 0xde +#define CMD_JTAG_SET_TRST 0xdf + +/** + * Error code indicating that there is not enough free memory on the device to + * perform the JTAG I/O operation. + */ +#define JTAG_IO_ERR_NO_MEMORY 0x06 +/** @endcond */ + +/** + * Perform a JTAG I/O operation. + * + * @note This function must only be used if the #JAYLINK_TIF_JTAG interface is + * available and selected. Nevertheless, this function can be used if the + * device doesn't have the #JAYLINK_DEV_CAP_SELECT_TIF capability. + * + * @param[in,out] devh Device handle. + * @param[in] tms Buffer to read TMS data from. + * @param[in] tdi Buffer to read TDI data from. + * @param[out] tdo Buffer to store TDO data on success. Its content is + * undefined on failure. The buffer must be large enough to + * contain at least the specified number of bits to transfer. + * @param[in] length Number of bits to transfer. + * @param[in] version Version of the JTAG command to use. + * + * @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_NO_MEMORY Not enough memory on the device to perform + * the operation. + * @retval JAYLINK_ERR_DEV Unspecified device error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_select_interface() + * @see jaylink_set_speed() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_jtag_io(struct jaylink_device_handle *devh, + const uint8_t *tms, const uint8_t *tdi, uint8_t *tdo, + uint16_t length, enum jaylink_jtag_version version) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[4]; + uint16_t num_bytes; + uint16_t read_length; + uint8_t status; + uint8_t cmd; + + if (!devh || !tms || !tdi || !tdo || !length) + return JAYLINK_ERR_ARG; + + num_bytes = (length + 7) / 8; + read_length = num_bytes; + + switch (version) { + case JAYLINK_JTAG_VERSION_2: + cmd = CMD_JTAG_IO_V2; + break; + case JAYLINK_JTAG_VERSION_3: + cmd = CMD_JTAG_IO_V3; + /* In this version, the response includes a status byte. */ + read_length++; + break; + default: + return JAYLINK_ERR_ARG; + } + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 4 + 2 * num_bytes, + read_length, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = cmd; + buf[1] = 0x00; + buffer_set_u16(buf, length, 2); + + ret = transport_write(devh, buf, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_write(devh, tms, num_bytes); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_write(devh, tdi, num_bytes); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, tdo, num_bytes); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + if (version == JAYLINK_JTAG_VERSION_2) + return JAYLINK_OK; + + ret = transport_read(devh, &status, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + if (status == JTAG_IO_ERR_NO_MEMORY) { + return JAYLINK_ERR_DEV_NO_MEMORY; + } else if (status > 0) { + log_err(ctx, "JTAG I/O operation failed: 0x%x.", status); + return JAYLINK_ERR_DEV; + } + + return JAYLINK_OK; +} + +/** + * Clear the JTAG test reset (TRST) signal. + * + * @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 Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_jtag_clear_trst(struct jaylink_device_handle *devh) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[1]; + + if (!devh) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 1, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_JTAG_CLEAR_TRST; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + return JAYLINK_OK; +} + +/** + * Set the JTAG test reset (TRST) signal. + * + * @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 Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_jtag_set_trst(struct jaylink_device_handle *devh) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[1]; + + if (!devh) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 1, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_JTAG_SET_TRST; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/libjaylink-internal.h b/src/jtag/drivers/libjaylink/libjaylink/libjaylink-internal.h new file mode 100644 index 0000000..f97ec14 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/libjaylink-internal.h @@ -0,0 +1,320 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2016 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/>. + */ + +#ifndef LIBJAYLINK_LIBJAYLINK_INTERNAL_H +#define LIBJAYLINK_LIBJAYLINK_INTERNAL_H + +#include <stddef.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdarg.h> +#include <sys/types.h> +#ifdef _WIN32 +#include <ws2tcpip.h> +#else +#include <sys/socket.h> +#include <arpa/inet.h> +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_LIBUSB +#include <libusb.h> +#endif + +#include "libjaylink.h" + +/** + * @file + * + * Internal libjaylink header file. + */ + +/** Macro to mark private libjaylink symbol. */ +#if defined(_WIN32) || defined(__MSYS__) || defined(__CYGWIN__) +#define JAYLINK_PRIV +#else +#define JAYLINK_PRIV __attribute__ ((visibility ("hidden"))) +#endif + +/** Calculate the minimum of two numeric values. */ +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +struct jaylink_context { +#ifdef HAVE_LIBUSB + /** libusb context. */ + struct libusb_context *usb_ctx; +#endif + /** + * List of allocated device instances. + * + * Used to prevent multiple device instances for the same device. + */ + struct list *devs; + /** List of recently discovered devices. */ + struct list *discovered_devs; + /** Current log level. */ + enum jaylink_log_level log_level; + /** Log callback function. */ + jaylink_log_callback log_callback; + /** User data to be passed to the log callback function. */ + void *log_callback_data; + /** Log domain. */ + char log_domain[JAYLINK_LOG_DOMAIN_MAX_LENGTH + 1]; +}; + +struct jaylink_device { + /** libjaylink context. */ + struct jaylink_context *ctx; + /** Number of references held on this device instance. */ + size_t ref_count; + /** Host interface. */ + enum jaylink_host_interface iface; + /** + * Serial number of the device. + * + * This number is for enumeration purpose only and can differ from the + * real serial number of the device. + */ + uint32_t serial_number; + /** Indicates whether the serial number is valid. */ + bool valid_serial_number; +#ifdef HAVE_LIBUSB + /** libusb device instance. */ + struct libusb_device *usb_dev; + /** USB address of the device. */ + uint8_t usb_address; +#endif + /** + * IPv4 address. + * + * The address is encoded as string in quad-dotted decimal format. + * + * This field is used for devices with host interface #JAYLINK_HIF_TCP + * only. + */ + char ipv4_address[INET_ADDRSTRLEN]; + /** + * Media Access Control (MAC) address. + * + * This field is used for devices with host interface #JAYLINK_HIF_TCP + * only. + */ + uint8_t mac_address[JAYLINK_MAC_ADDRESS_LENGTH]; + /** Indicates whether the MAC address is available. */ + bool has_mac_address; + /** + * Product name. + * + * This field is used for devices with host interface #JAYLINK_HIF_TCP + * only. + */ + char product_name[JAYLINK_PRODUCT_NAME_MAX_LENGTH]; + /** Indicates whether the product name is available. */ + bool has_product_name; + /** + * Nickname. + * + * This field is used for devices with host interface #JAYLINK_HIF_TCP + * only. + */ + char nickname[JAYLINK_NICKNAME_MAX_LENGTH]; + /** Indicates whether the nickname is available. */ + bool has_nickname; + /** + * Hardware version. + * + * This field is used for devices with host interface #JAYLINK_HIF_TCP + * only. + */ + struct jaylink_hardware_version hw_version; + /** Indicates whether the hardware version is available. */ + bool has_hw_version; +}; + +struct jaylink_device_handle { + /** Device instance. */ + struct jaylink_device *dev; + /** + * Buffer for write and read operations. + * + * Note that write and read operations are always processed + * consecutively and therefore the same buffer can be used for both. + */ + uint8_t *buffer; + /** Buffer size. */ + size_t buffer_size; + /** Number of bytes left for the read operation. */ + size_t read_length; + /** Number of bytes available in the buffer to be read. */ + size_t bytes_available; + /** Current read position in the buffer. */ + size_t read_pos; + /** + * Number of bytes left to be written before the write operation will + * be performed. + */ + size_t write_length; + /** + * Current write position in the buffer. + * + * This is equivalent to the number of bytes in the buffer and used for + * write operations only. + */ + size_t write_pos; +#ifdef HAVE_LIBUSB + /** libusb device handle. */ + struct libusb_device_handle *usb_devh; + /** USB interface number of the device. */ + uint8_t interface_number; + /** USB interface IN endpoint of the device. */ + uint8_t endpoint_in; + /** USB interface OUT endpoint of the device. */ + uint8_t endpoint_out; +#endif + /** + * Socket descriptor. + * + * This field is used for devices with host interface #JAYLINK_HIF_TCP + * only. + */ + int sock; +}; + +struct list { + void *data; + struct list *next; +}; + +typedef bool (*list_compare_callback)(const void *data, const void *user_data); + +/*--- buffer.c --------------------------------------------------------------*/ + +JAYLINK_PRIV void buffer_set_u16(uint8_t *buffer, uint16_t value, + size_t offset); +JAYLINK_PRIV uint16_t buffer_get_u16(const uint8_t *buffer, size_t offset); +JAYLINK_PRIV void buffer_set_u32(uint8_t *buffer, uint32_t value, + size_t offset); +JAYLINK_PRIV uint32_t buffer_get_u32(const uint8_t *buffer, size_t offset); + +/*--- device.c --------------------------------------------------------------*/ + +JAYLINK_PRIV struct jaylink_device *device_allocate( + struct jaylink_context *ctx); + +/*--- discovery_tcp.c -------------------------------------------------------*/ + +JAYLINK_PRIV int discovery_tcp_scan(struct jaylink_context *ctx); + +/*--- discovery_usb.c -------------------------------------------------------*/ + +JAYLINK_PRIV int discovery_usb_scan(struct jaylink_context *ctx); + +/*--- list.c ----------------------------------------------------------------*/ + +JAYLINK_PRIV struct list *list_prepend(struct list *list, void *data); +JAYLINK_PRIV struct list *list_remove(struct list *list, const void *data); +JAYLINK_PRIV struct list *list_find_custom(struct list *list, + list_compare_callback callback, const void *user_data); +JAYLINK_PRIV size_t list_length(struct list *list); +JAYLINK_PRIV void list_free(struct list *list); + +/*--- log.c -----------------------------------------------------------------*/ + +JAYLINK_PRIV int log_vprintf(const struct jaylink_context *ctx, + enum jaylink_log_level level, const char *format, va_list args, + void *user_data); +JAYLINK_PRIV void log_err(const struct jaylink_context *ctx, + const char *format, ...); +JAYLINK_PRIV void log_warn(const struct jaylink_context *ctx, + const char *format, ...); +JAYLINK_PRIV void log_info(const struct jaylink_context *ctx, + const char *format, ...); +JAYLINK_PRIV void log_dbg(const struct jaylink_context *ctx, + const char *format, ...); +JAYLINK_PRIV void log_dbgio(const struct jaylink_context *ctx, + const char *format, ...); + +/*--- socket.c --------------------------------------------------------------*/ + +JAYLINK_PRIV bool socket_close(int sock); +JAYLINK_PRIV bool socket_bind(int sock, const struct sockaddr *address, + size_t length); +JAYLINK_PRIV bool socket_send(int sock, const void *buffer, size_t *length, + int flags); +JAYLINK_PRIV bool socket_recv(int sock, void *buffer, size_t *length, + int flags); +JAYLINK_PRIV bool socket_sendto(int sock, const void *buffer, size_t *length, + int flags, const struct sockaddr *address, + size_t address_length); +JAYLINK_PRIV bool socket_recvfrom(int sock, void *buffer, size_t *length, + int flags, struct sockaddr *address, size_t *address_length); +JAYLINK_PRIV bool socket_set_option(int sock, int level, int option, + const void *value, size_t length); + +/*--- transport.c -----------------------------------------------------------*/ + +JAYLINK_PRIV int transport_open(struct jaylink_device_handle *devh); +JAYLINK_PRIV int transport_close(struct jaylink_device_handle *devh); +JAYLINK_PRIV int transport_start_write_read(struct jaylink_device_handle *devh, + size_t write_length, size_t read_length, bool has_command); +JAYLINK_PRIV int transport_start_write(struct jaylink_device_handle *devh, + size_t length, bool has_command); +JAYLINK_PRIV int transport_start_read(struct jaylink_device_handle *devh, + size_t length); +JAYLINK_PRIV int transport_write(struct jaylink_device_handle *devh, + const uint8_t *buffer, size_t length); +JAYLINK_PRIV int transport_read(struct jaylink_device_handle *devh, + uint8_t *buffer, size_t length); + +/*--- transport_usb.c -------------------------------------------------------*/ + +JAYLINK_PRIV int transport_usb_open(struct jaylink_device_handle *devh); +JAYLINK_PRIV int transport_usb_close(struct jaylink_device_handle *devh); +JAYLINK_PRIV int transport_usb_start_write_read( + struct jaylink_device_handle *devh, size_t write_length, + size_t read_length, bool has_command); +JAYLINK_PRIV int transport_usb_start_write(struct jaylink_device_handle *devh, + size_t length, bool has_command); +JAYLINK_PRIV int transport_usb_start_read(struct jaylink_device_handle *devh, + size_t length); +JAYLINK_PRIV int transport_usb_write(struct jaylink_device_handle *devh, + const uint8_t *buffer, size_t length); +JAYLINK_PRIV int transport_usb_read(struct jaylink_device_handle *devh, + uint8_t *buffer, size_t length); + +/*--- transport_tcp.c -------------------------------------------------------*/ + +JAYLINK_PRIV int transport_tcp_open(struct jaylink_device_handle *devh); +JAYLINK_PRIV int transport_tcp_close(struct jaylink_device_handle *devh); +JAYLINK_PRIV int transport_tcp_start_write_read( + struct jaylink_device_handle *devh, size_t write_length, + size_t read_length, bool has_command); +JAYLINK_PRIV int transport_tcp_start_write(struct jaylink_device_handle *devh, + size_t length, bool has_command); +JAYLINK_PRIV int transport_tcp_start_read(struct jaylink_device_handle *devh, + size_t length); +JAYLINK_PRIV int transport_tcp_write(struct jaylink_device_handle *devh, + const uint8_t *buffer, size_t length); +JAYLINK_PRIV int transport_tcp_read(struct jaylink_device_handle *devh, + uint8_t *buffer, size_t length); + +#endif /* LIBJAYLINK_LIBJAYLINK_INTERNAL_H */ diff --git a/src/jtag/drivers/libjaylink/libjaylink/libjaylink.h b/src/jtag/drivers/libjaylink/libjaylink/libjaylink.h new file mode 100644 index 0000000..223aa84 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/libjaylink.h @@ -0,0 +1,589 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2016 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/>. + */ + +#ifndef LIBJAYLINK_LIBJAYLINK_H +#define LIBJAYLINK_LIBJAYLINK_H + +#include <stddef.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdarg.h> +#ifdef _WIN32 +#include <ws2tcpip.h> +#else +#include <arpa/inet.h> +#endif + +/** + * @file + * + * Public libjaylink header file to be used by applications. + */ + +/** Error codes returned by libjaylink functions. */ +enum jaylink_error { + /** No error. */ + JAYLINK_OK = 0, + /** Unspecified error. */ + JAYLINK_ERR = -1, + /** Invalid argument. */ + JAYLINK_ERR_ARG = -2, + /** Memory allocation error. */ + JAYLINK_ERR_MALLOC = -3, + /** Timeout occurred. */ + JAYLINK_ERR_TIMEOUT = -4, + /** Protocol violation. */ + JAYLINK_ERR_PROTO = -5, + /** Entity not available. */ + JAYLINK_ERR_NOT_AVAILABLE = -6, + /** Operation not supported. */ + JAYLINK_ERR_NOT_SUPPORTED = -7, + /** Input/output error. */ + JAYLINK_ERR_IO = -8, + /** Device: unspecified error. */ + JAYLINK_ERR_DEV = -1000, + /** Device: operation not supported. */ + JAYLINK_ERR_DEV_NOT_SUPPORTED = -1001, + /** Device: entity not available. */ + JAYLINK_ERR_DEV_NOT_AVAILABLE = -1002, + /** Device: not enough memory to perform operation. */ + JAYLINK_ERR_DEV_NO_MEMORY = -1003 +}; + +/** libjaylink log levels. */ +enum jaylink_log_level { + /** Output no messages. */ + JAYLINK_LOG_LEVEL_NONE = 0, + /** Output error messages. */ + JAYLINK_LOG_LEVEL_ERROR = 1, + /** Output warnings. */ + JAYLINK_LOG_LEVEL_WARNING = 2, + /** Output informational messages. */ + JAYLINK_LOG_LEVEL_INFO = 3, + /** Output debug messages. */ + JAYLINK_LOG_LEVEL_DEBUG = 4, + /** Output I/O debug messages. */ + JAYLINK_LOG_LEVEL_DEBUG_IO = 5 +}; + +/** Default libjaylink log domain. */ +#define JAYLINK_LOG_DOMAIN_DEFAULT "jaylink: " + +/** Maximum length of a libjaylink log domain in bytes. */ +#define JAYLINK_LOG_DOMAIN_MAX_LENGTH 32 + +/** libjaylink capabilities. */ +enum jaylink_capability { + /** Library supports USB as host interface. */ + JAYLINK_CAP_HIF_USB = 0 +}; + +/** Host interfaces. */ +enum jaylink_host_interface { + /** Universal Serial Bus (USB). */ + JAYLINK_HIF_USB = (1 << 0), + /** Transmission Control Protocol (TCP). */ + JAYLINK_HIF_TCP = (1 << 1) +}; + +/** + * USB addresses. + * + * The USB address is a way to identify USB devices and is related to the USB + * Product ID (PID) of a device. + */ +enum jaylink_usb_address { + /** USB address 0 (Product ID 0x0101). */ + JAYLINK_USB_ADDRESS_0 = 0, + /** USB address 1 (Product ID 0x0102). */ + JAYLINK_USB_ADDRESS_1 = 1, + /** USB address 2 (Product ID 0x0103). */ + JAYLINK_USB_ADDRESS_2 = 2, + /** USB address 3 (Product ID 0x0104). */ + JAYLINK_USB_ADDRESS_3 = 3 +}; + +/** Device capabilities. */ +enum jaylink_device_capability { + /** Device supports retrieval of the hardware version. */ + JAYLINK_DEV_CAP_GET_HW_VERSION = 1, + /** Device supports adaptive clocking. */ + JAYLINK_DEV_CAP_ADAPTIVE_CLOCKING = 3, + /** Device supports reading configuration data. */ + JAYLINK_DEV_CAP_READ_CONFIG = 4, + /** Device supports writing configuration data. */ + JAYLINK_DEV_CAP_WRITE_CONFIG = 5, + /** Device supports retrieval of target interface speeds. */ + JAYLINK_DEV_CAP_GET_SPEEDS = 9, + /** Device supports retrieval of free memory size. */ + JAYLINK_DEV_CAP_GET_FREE_MEMORY = 11, + /** Device supports retrieval of hardware information. */ + JAYLINK_DEV_CAP_GET_HW_INFO = 12, + /** Device supports the setting of the target power supply. */ + JAYLINK_DEV_CAP_SET_TARGET_POWER = 13, + /** Device supports target interface selection. */ + JAYLINK_DEV_CAP_SELECT_TIF = 17, + /** Device supports retrieval of counter values. */ + JAYLINK_DEV_CAP_GET_COUNTERS = 19, + /** Device supports capturing of SWO trace data. */ + JAYLINK_DEV_CAP_SWO = 23, + /** Device supports file I/O operations. */ + JAYLINK_DEV_CAP_FILE_IO = 26, + /** Device supports registration of connections. */ + JAYLINK_DEV_CAP_REGISTER = 27, + /** Device supports retrieval of extended capabilities. */ + JAYLINK_DEV_CAP_GET_EXT_CAPS = 31, + /** Device supports EMUCOM. */ + JAYLINK_DEV_CAP_EMUCOM = 33, + /** Device supports ethernet connectivity. */ + JAYLINK_DEV_CAP_ETHERNET = 38 +}; + +/** Hardware information. */ +enum jaylink_hardware_info { + /** + * Status of the target power supply. + * + * This indicates whether the target power supply on pin 19 of the + * 20-pin JTAG / SWD connector is enabled or disabled. + * + * @see jaylink_set_target_power() + */ + JAYLINK_HW_INFO_TARGET_POWER = (1 << 0), + /** Current consumption of the target in mA. */ + JAYLINK_HW_INFO_ITARGET = (1 << 2), + /** Peak current consumption of the target in mA. */ + JAYLINK_HW_INFO_ITARGET_PEAK = (1 << 3) +}; + +/** Device counters. */ +enum jaylink_counter { + /** Time the device is connected to a target in milliseconds. */ + JAYLINK_COUNTER_TARGET_TIME = (1 << 0), + /** + * Number of times the device was connected or disconnected from a + * target. + */ + JAYLINK_COUNTER_TARGET_CONNECTIONS = (1 << 1) +}; + +/** Device hardware types. */ +enum jaylink_hardware_type { + /** J-Link. */ + JAYLINK_HW_TYPE_JLINK = 0, + /** Flasher. */ + JAYLINK_HW_TYPE_FLASHER = 2, + /** J-Link Pro. */ + JAYLINK_HW_TYPE_JLINK_PRO = 3 +}; + +/** Target interfaces. */ +enum jaylink_target_interface { + /** Joint Test Action Group, IEEE 1149.1 (JTAG). */ + JAYLINK_TIF_JTAG = 0, + /** Serial Wire Debug (SWD). */ + JAYLINK_TIF_SWD = 1, + /** Background Debug Mode 3 (BDM3). */ + JAYLINK_TIF_BDM3 = 2, + /** Renesas’ single-wire debug interface (FINE). */ + JAYLINK_TIF_FINE = 3, + /** 2-wire JTAG for PIC32 compliant devices. */ + JAYLINK_TIF_2W_JTAG_PIC32 = 4, +}; + +/** + * JTAG command versions. + * + * The JTAG command version only affects the device and the communication + * protocol. The behaviour of a JTAG operation is not affected at all. + */ +enum jaylink_jtag_version { + /** + * JTAG command version 2. + * + * This version is obsolete for major hardware version 5 and above. Use + * #JAYLINK_JTAG_VERSION_3 for these versions instead. + */ + JAYLINK_JTAG_VERSION_2 = 1, + /** JTAG command version 3. */ + JAYLINK_JTAG_VERSION_3 = 2 +}; + +/** Serial Wire Output (SWO) capture modes. */ +enum jaylink_swo_mode { + /** Universal Asynchronous Receiver Transmitter (UART). */ + JAYLINK_SWO_MODE_UART = 0 +}; + +/** Target interface speed information. */ +struct jaylink_speed { + /** Base frequency in Hz. */ + uint32_t freq; + /** Minimum frequency divider. */ + uint16_t div; +}; + +/** Serial Wire Output (SWO) speed information. */ +struct jaylink_swo_speed { + /** Base frequency in Hz. */ + uint32_t freq; + /** Minimum frequency divider. */ + uint32_t min_div; + /** Maximum frequency divider. */ + uint32_t max_div; + /** Minimum prescaler. */ + uint32_t min_prescaler; + /** Maximum prescaler. */ + uint32_t max_prescaler; +}; + +/** Device hardware version. */ +struct jaylink_hardware_version { + /** Hardware type. */ + enum jaylink_hardware_type type; + /** Major version. */ + uint8_t major; + /** Minor version. */ + uint8_t minor; + /** Revision number. */ + uint8_t revision; +}; + +/** Device hardware status. */ +struct jaylink_hardware_status { + /** Target reference voltage in mV. */ + uint16_t target_voltage; + /** TCK pin state. */ + bool tck; + /** TDI pin state. */ + bool tdi; + /** TDO pin state. */ + bool tdo; + /** TMS pin state. */ + bool tms; + /** TRES pin state. */ + bool tres; + /** TRST pin state. */ + bool trst; +}; + +/** Device connection. */ +struct jaylink_connection { + /** Handle. */ + uint16_t handle; + /** + * Process ID (PID). + * + * Identification of the client process. Usually this is the + * Process ID (PID) of the client process in an arbitrary format. + */ + uint32_t pid; + /** + * Host ID (HID). + * + * IPv4 address string of the client in quad-dotted decimal format + * (e.g. 192.0.2.235). The address 0.0.0.0 should be used for the + * registration of an USB connection. + */ + char hid[INET_ADDRSTRLEN]; + /** IID. */ + uint8_t iid; + /** CID. */ + uint8_t cid; + /** + * Timestamp of the last registration in milliseconds. + * + * The timestamp is relative to the time the device was powered up. + */ + uint32_t timestamp; +}; + +/** Target interface speed value for adaptive clocking. */ +#define JAYLINK_SPEED_ADAPTIVE_CLOCKING 0xffff + +/** Size of the device configuration data in bytes. */ +#define JAYLINK_DEV_CONFIG_SIZE 256 + +/** Number of bytes required to store device capabilities. */ +#define JAYLINK_DEV_CAPS_SIZE 4 + +/** Number of bytes required to store extended device capabilities. */ +#define JAYLINK_DEV_EXT_CAPS_SIZE 32 + +/** Maximum number of connections that can be registered on a device. */ +#define JAYLINK_MAX_CONNECTIONS 16 + +/** Media Access Control (MAC) address length in bytes. */ +#define JAYLINK_MAC_ADDRESS_LENGTH 6 + +/** + * Maximum length of a device's nickname including trailing null-terminator in + * bytes. + */ +#define JAYLINK_NICKNAME_MAX_LENGTH 32 + +/** + * Maximum length of a device's product name including trailing null-terminator + * in bytes. + */ +#define JAYLINK_PRODUCT_NAME_MAX_LENGTH 32 + +/** Maximum length of a filename in bytes. */ +#define JAYLINK_FILE_NAME_MAX_LENGTH 255 + +/** Maximum transfer size for a file in bytes. */ +#define JAYLINK_FILE_MAX_TRANSFER_SIZE 0x100000 + +/** + * EMUCOM channel with the system time of the device in milliseconds. + * + * The channel is read-only and the time is encoded in 4 bytes. The byte order + * is little-endian. + */ +#define JAYLINK_EMUCOM_CHANNEL_TIME 0x0 + +/** + * Offset of EMUCOM user channels. + * + * User channels are available to implement vendor and/or device specific + * functionalities. All channels below are reserved. + */ +#define JAYLINK_EMUCOM_CHANNEL_USER 0x10000 + +/** + * @struct jaylink_context + * + * Opaque structure representing a libjaylink context. + */ +struct jaylink_context; + +/** + * @struct jaylink_device + * + * Opaque structure representing a device. + */ +struct jaylink_device; + +/** + * @struct jaylink_device_handle + * + * Opaque structure representing a handle of a device. + */ +struct jaylink_device_handle; + +/** Macro to mark public libjaylink API symbol. */ +#ifdef _WIN32 +#define JAYLINK_API +#else +#define JAYLINK_API __attribute__ ((visibility ("default"))) +#endif + +/** + * Log callback function type. + * + * @param[in] ctx libjaylink context. + * @param[in] level Log level. + * @param[in] format Message format in printf()-style. + * @param[in] args Message arguments. + * @param[in,out] user_data User data passed to the callback function. + * + * @return Number of characters printed on success, or a negative error code on + * failure. + */ +typedef int (*jaylink_log_callback)(const struct jaylink_context *ctx, + enum jaylink_log_level level, const char *format, va_list args, + void *user_data); + +/*--- core.c ----------------------------------------------------------------*/ + +JAYLINK_API int jaylink_init(struct jaylink_context **ctx); +JAYLINK_API int jaylink_exit(struct jaylink_context *ctx); +JAYLINK_API bool jaylink_library_has_cap(enum jaylink_capability cap); + +/*--- device.c --------------------------------------------------------------*/ + +JAYLINK_API int jaylink_get_devices(struct jaylink_context *ctx, + struct jaylink_device ***devs, size_t *count); +JAYLINK_API void jaylink_free_devices(struct jaylink_device **devs, + bool unref); +JAYLINK_API int jaylink_device_get_host_interface( + const struct jaylink_device *dev, + enum jaylink_host_interface *iface); +JAYLINK_API int jaylink_device_get_serial_number( + const struct jaylink_device *dev, uint32_t *serial_number); +JAYLINK_API int jaylink_device_get_usb_address( + const struct jaylink_device *dev, + enum jaylink_usb_address *address); +JAYLINK_API int jaylink_device_get_ipv4_address( + const struct jaylink_device *dev, char *address); +JAYLINK_API int jaylink_device_get_mac_address( + const struct jaylink_device *dev, uint8_t *address); +JAYLINK_API int jaylink_device_get_hardware_version( + const struct jaylink_device *dev, + struct jaylink_hardware_version *version); +JAYLINK_API int jaylink_device_get_product_name( + const struct jaylink_device *dev, char *name); +JAYLINK_API int jaylink_device_get_nickname(const struct jaylink_device *dev, + char *nickname); +JAYLINK_API struct jaylink_device *jaylink_ref_device( + struct jaylink_device *dev); +JAYLINK_API void jaylink_unref_device(struct jaylink_device *dev); +JAYLINK_API int jaylink_open(struct jaylink_device *dev, + struct jaylink_device_handle **devh); +JAYLINK_API int jaylink_close(struct jaylink_device_handle *devh); +JAYLINK_API struct jaylink_device *jaylink_get_device( + struct jaylink_device_handle *devh); +JAYLINK_API int jaylink_get_firmware_version( + struct jaylink_device_handle *devh, char **version, + size_t *length); +JAYLINK_API int jaylink_get_hardware_info(struct jaylink_device_handle *devh, + uint32_t mask, uint32_t *info); +JAYLINK_API int jaylink_get_counters(struct jaylink_device_handle *devh, + uint32_t mask, uint32_t *values); +JAYLINK_API int jaylink_get_hardware_version( + struct jaylink_device_handle *devh, + struct jaylink_hardware_version *version); +JAYLINK_API int jaylink_get_hardware_status(struct jaylink_device_handle *devh, + struct jaylink_hardware_status *status); +JAYLINK_API int jaylink_get_caps(struct jaylink_device_handle *devh, + uint8_t *caps); +JAYLINK_API int jaylink_get_extended_caps(struct jaylink_device_handle *devh, + uint8_t *caps); +JAYLINK_API int jaylink_get_free_memory(struct jaylink_device_handle *devh, + uint32_t *size); +JAYLINK_API int jaylink_read_raw_config(struct jaylink_device_handle *devh, + uint8_t *config); +JAYLINK_API int jaylink_write_raw_config(struct jaylink_device_handle *devh, + const uint8_t *config); +JAYLINK_API int jaylink_register(struct jaylink_device_handle *devh, + struct jaylink_connection *connection, + struct jaylink_connection *connections, size_t *count); +JAYLINK_API int jaylink_unregister(struct jaylink_device_handle *devh, + const struct jaylink_connection *connection, + struct jaylink_connection *connections, size_t *count); + +/*--- discovery.c -----------------------------------------------------------*/ + +JAYLINK_API int jaylink_discovery_scan(struct jaylink_context *ctx, + uint32_t ifaces); + +/*--- emucom.c --------------------------------------------------------------*/ + +JAYLINK_API int jaylink_emucom_read(struct jaylink_device_handle *devh, + uint32_t channel, uint8_t *buffer, uint32_t *length); +JAYLINK_API int jaylink_emucom_write(struct jaylink_device_handle *devh, + uint32_t channel, const uint8_t *buffer, uint32_t *length); + +/*--- error.c ---------------------------------------------------------------*/ + +JAYLINK_API const char *jaylink_strerror(int error_code); +JAYLINK_API const char *jaylink_strerror_name(int error_code); + +/*--- fileio.c --------------------------------------------------------------*/ + +JAYLINK_API int jaylink_file_read(struct jaylink_device_handle *devh, + const char *filename, uint8_t *buffer, uint32_t offset, + uint32_t *length); +JAYLINK_API int jaylink_file_write(struct jaylink_device_handle *devh, + const char *filename, const uint8_t *buffer, uint32_t offset, + uint32_t *length); +JAYLINK_API int jaylink_file_get_size(struct jaylink_device_handle *devh, + const char *filename, uint32_t *size); +JAYLINK_API int jaylink_file_delete(struct jaylink_device_handle *devh, + const char *filename); + +/*--- jtag.c ----------------------------------------------------------------*/ + +JAYLINK_API int jaylink_jtag_io(struct jaylink_device_handle *devh, + const uint8_t *tms, const uint8_t *tdi, uint8_t *tdo, + uint16_t length, enum jaylink_jtag_version version); +JAYLINK_API int jaylink_jtag_clear_trst(struct jaylink_device_handle *devh); +JAYLINK_API int jaylink_jtag_set_trst(struct jaylink_device_handle *devh); + +/*--- log.c -----------------------------------------------------------------*/ + +JAYLINK_API int jaylink_log_set_level(struct jaylink_context *ctx, + enum jaylink_log_level level); +JAYLINK_API int jaylink_log_get_level(const struct jaylink_context *ctx, + enum jaylink_log_level *level); +JAYLINK_API int jaylink_log_set_callback(struct jaylink_context *ctx, + jaylink_log_callback callback, void *user_data); +JAYLINK_API int jaylink_log_set_domain(struct jaylink_context *ctx, + const char *domain); +JAYLINK_API const char *jaylink_log_get_domain( + const struct jaylink_context *ctx); + +/*--- strutil.c -------------------------------------------------------------*/ + +JAYLINK_API int jaylink_parse_serial_number(const char *str, + uint32_t *serial_number); + +/*--- swd.c -----------------------------------------------------------------*/ + +JAYLINK_API int jaylink_swd_io(struct jaylink_device_handle *devh, + const uint8_t *direction, const uint8_t *out, uint8_t *in, + uint16_t length); + +/*--- swo.c -----------------------------------------------------------------*/ + +JAYLINK_API int jaylink_swo_start(struct jaylink_device_handle *devh, + enum jaylink_swo_mode mode, uint32_t baudrate, uint32_t size); +JAYLINK_API int jaylink_swo_stop(struct jaylink_device_handle *devh); +JAYLINK_API int jaylink_swo_read(struct jaylink_device_handle *devh, + uint8_t *buffer, uint32_t *length); +JAYLINK_API int jaylink_swo_get_speeds(struct jaylink_device_handle *devh, + enum jaylink_swo_mode mode, struct jaylink_swo_speed *speed); + +/*--- target.c --------------------------------------------------------------*/ + +JAYLINK_API int jaylink_set_speed(struct jaylink_device_handle *devh, + uint16_t speed); +JAYLINK_API int jaylink_get_speeds(struct jaylink_device_handle *devh, + struct jaylink_speed *speed); +JAYLINK_API int jaylink_select_interface(struct jaylink_device_handle *devh, + enum jaylink_target_interface iface, + enum jaylink_target_interface *prev_iface); +JAYLINK_API int jaylink_get_available_interfaces( + struct jaylink_device_handle *devh, uint32_t *ifaces); +JAYLINK_API int jaylink_get_selected_interface( + struct jaylink_device_handle *devh, + enum jaylink_target_interface *iface); +JAYLINK_API int jaylink_clear_reset(struct jaylink_device_handle *devh); +JAYLINK_API int jaylink_set_reset(struct jaylink_device_handle *devh); +JAYLINK_API int jaylink_set_target_power(struct jaylink_device_handle *devh, + bool enable); + +/*--- util.c ----------------------------------------------------------------*/ + +JAYLINK_API bool jaylink_has_cap(const uint8_t *caps, uint32_t cap); + +/*--- version.c -------------------------------------------------------------*/ + +JAYLINK_API int jaylink_version_package_get_major(void); +JAYLINK_API int jaylink_version_package_get_minor(void); +JAYLINK_API int jaylink_version_package_get_micro(void); +JAYLINK_API const char *jaylink_version_package_get_string(void); +JAYLINK_API int jaylink_version_library_get_current(void); +JAYLINK_API int jaylink_version_library_get_revision(void); +JAYLINK_API int jaylink_version_library_get_age(void); +JAYLINK_API const char *jaylink_version_library_get_string(void); + +#include "version.h" + +#endif /* LIBJAYLINK_LIBJAYLINK_H */ diff --git a/src/jtag/drivers/libjaylink/libjaylink/list.c b/src/jtag/drivers/libjaylink/libjaylink/list.c new file mode 100644 index 0000000..7c54e50 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/list.c @@ -0,0 +1,115 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2016 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 <stdlib.h> + +#include "libjaylink-internal.h" + +/** + * @file + * + * Singly-linked list functions. + */ + +/** @private */ +JAYLINK_PRIV struct list *list_prepend(struct list *list, void *data) +{ + struct list *item; + + item = malloc(sizeof(struct list)); + + if (!item) + return NULL; + + item->data = data; + item->next = list; + + return item; +} + +/** @private */ +JAYLINK_PRIV struct list *list_remove(struct list *list, const void *data) +{ + struct list *item; + struct list *tmp; + + if (!list) + return NULL; + + item = list; + + if (item->data == data) { + tmp = item->next; + free(item); + return tmp; + } + + while (item->next) { + if (item->next->data == data) { + tmp = item->next; + item->next = item->next->next; + free(tmp); + break; + } + + item = item->next; + } + + return list; +} + +/** @private */ +JAYLINK_PRIV struct list *list_find_custom(struct list *list, + list_compare_callback callback, const void *user_data) +{ + if (!callback) + return NULL; + + while (list) { + if (callback(list->data, user_data)) + return list; + + list = list->next; + } + + return NULL; +} + +/** @private */ +JAYLINK_PRIV size_t list_length(struct list *list) +{ + size_t length; + + for (length = 0; list; length++) + list = list->next; + + return length; +} + +/** @private */ +JAYLINK_PRIV void list_free(struct list *list) +{ + struct list *tmp; + + while (list) { + tmp = list; + list = list->next; + free(tmp); + } +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/log.c b/src/jtag/drivers/libjaylink/libjaylink/log.c new file mode 100644 index 0000000..07ef172 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/log.c @@ -0,0 +1,266 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-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/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdarg.h> + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * Logging functions. + */ + +/** + * Set the libjaylink log level. + * + * @param[in,out] ctx libjaylink context. + * @param[in] level Log level to set. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_log_set_level(struct jaylink_context *ctx, + enum jaylink_log_level level) +{ + if (!ctx) + return JAYLINK_ERR_ARG; + + if (level > JAYLINK_LOG_LEVEL_DEBUG_IO) + return JAYLINK_ERR_ARG; + + ctx->log_level = level; + + return JAYLINK_OK; +} + +/** + * Get the libjaylink log level. + * + * @param[in] ctx libjaylink context. + * @param[out] level Log level on success, and undefined on failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_log_get_level(const struct jaylink_context *ctx, + enum jaylink_log_level *level) +{ + if (!ctx || !level) + return JAYLINK_ERR_ARG; + + *level = ctx->log_level; + + return JAYLINK_OK; +} + +/** + * Set the libjaylink log callback function. + * + * @param[in,out] ctx libjaylink context. + * @param[in] callback Callback function to use, or NULL to use the default log + * function. + * @param[in] user_data User data to be passed to the callback function. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_log_set_callback(struct jaylink_context *ctx, + jaylink_log_callback callback, void *user_data) +{ + if (!ctx) + return JAYLINK_ERR_ARG; + + if (callback) { + ctx->log_callback = callback; + ctx->log_callback_data = user_data; + } else { + ctx->log_callback = &log_vprintf; + ctx->log_callback_data = NULL; + } + + return JAYLINK_OK; +} + +/** + * Set the libjaylink log domain. + * + * The log domain is a string which is used as prefix for all log messages to + * differentiate them from messages of other libraries. + * + * The maximum length of the log domain is #JAYLINK_LOG_DOMAIN_MAX_LENGTH + * bytes, excluding the trailing null-terminator. A log domain which exceeds + * this length will be silently truncated. + * + * @param[in,out] ctx libjaylink context. + * @param[in] domain Log domain to use. To set the default log domain, use + * #JAYLINK_LOG_DOMAIN_DEFAULT. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_log_set_domain(struct jaylink_context *ctx, + const char *domain) +{ + int ret; + + if (!ctx || !domain) + return JAYLINK_ERR_ARG; + + ret = snprintf(ctx->log_domain, JAYLINK_LOG_DOMAIN_MAX_LENGTH + 1, + "%s", domain); + + if (ret < 0) + return JAYLINK_ERR; + + return JAYLINK_OK; +} + +/** + * Get the libjaylink log domain. + * + * @param[in] ctx libjaylink context. + * + * @return A string which contains the current log domain on success, or NULL + * on failure. The string is null-terminated and must not be free'd by + * the caller. + * + * @since 0.1.0 + */ +JAYLINK_API const char *jaylink_log_get_domain( + const struct jaylink_context *ctx) +{ + if (!ctx) + return NULL; + + return ctx->log_domain; +} + +/** @private */ +JAYLINK_PRIV int log_vprintf(const struct jaylink_context *ctx, + enum jaylink_log_level level, const char *format, va_list args, + void *user_data) +{ + (void)user_data; + + /* + * Filter out messages with higher verbosity than the verbosity of the + * current log level. + */ + if (level > ctx->log_level) + return 0; + + if (ctx->log_domain[0] != '\0') + fprintf(stderr, "%s", ctx->log_domain); + + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + + return 0; +} + +/** @private */ +JAYLINK_PRIV void log_err(const struct jaylink_context *ctx, + const char *format, ...) +{ + va_list args; + + if (!ctx) + return; + + va_start(args, format); + ctx->log_callback(ctx, JAYLINK_LOG_LEVEL_ERROR, format, args, + ctx->log_callback_data); + va_end(args); +} + +/** @private */ +JAYLINK_PRIV void log_warn(const struct jaylink_context *ctx, + const char *format, ...) +{ + va_list args; + + if (!ctx) + return; + + va_start(args, format); + ctx->log_callback(ctx, JAYLINK_LOG_LEVEL_WARNING, format, args, + ctx->log_callback_data); + va_end(args); +} + +/** @private */ +JAYLINK_PRIV void log_info(const struct jaylink_context *ctx, + const char *format, ...) +{ + va_list args; + + if (!ctx) + return; + + va_start(args, format); + ctx->log_callback(ctx, JAYLINK_LOG_LEVEL_INFO, format, args, + ctx->log_callback_data); + va_end(args); +} + +/** @private */ +JAYLINK_PRIV void log_dbg(const struct jaylink_context *ctx, + const char *format, ...) +{ + va_list args; + + if (!ctx) + return; + + va_start(args, format); + ctx->log_callback(ctx, JAYLINK_LOG_LEVEL_DEBUG, format, args, + ctx->log_callback_data); + va_end(args); +} + +/** @private */ +JAYLINK_PRIV void log_dbgio(const struct jaylink_context *ctx, + const char *format, ...) +{ + va_list args; + + if (!ctx) + return; + + va_start(args, format); + ctx->log_callback(ctx, JAYLINK_LOG_LEVEL_DEBUG_IO, format, args, + ctx->log_callback_data); + va_end(args); +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/socket.c b/src/jtag/drivers/libjaylink/libjaylink/socket.c new file mode 100644 index 0000000..f2a6588 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/socket.c @@ -0,0 +1,257 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2016-2017 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/>. + */ + +#ifdef _WIN32 +#include <winsock2.h> +#else +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> +#endif + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * Socket abstraction layer. + */ + +/** + * Close a socket. + * + * @param[in] sock Socket descriptor. + * + * @return Whether the socket was successfully closed. + */ +JAYLINK_PRIV bool socket_close(int sock) +{ + int ret; + +#ifdef _WIN32 + ret = closesocket(sock); +#else + ret = close(sock); +#endif + + if (!ret) + return true; + + return false; +} + +/** + * Bind an address to a socket. + * + * @param[in] sock Socket descriptor. + * @param[in] address Address to be bound to the socket. + * @param[in] length Length of the structure pointed to by @p address in bytes. + * + * @return Whether the address was successfully assigned to the socket. + */ +JAYLINK_PRIV bool socket_bind(int sock, const struct sockaddr *address, + size_t length) +{ + int ret; + + ret = bind(sock, address, length); + +#ifdef _WIN32 + if (ret == SOCKET_ERROR) + return false; +#else + if (ret < 0) + return false; +#endif + + return true; +} + +/** + * Send a message on a socket. + * + * @param[in] sock Socket descriptor. + * @param[in] buffer Buffer of the message to be sent. + * @param[in,out] length Length of the message in bytes. On success, the value + * gets updated with the actual number of bytes sent. The + * value is undefined on failure. + * @param[in] flags Flags to modify the function behaviour. Use bitwise OR to + * specify multiple flags. + * + * @return Whether the message was sent successfully. + */ +JAYLINK_PRIV bool socket_send(int sock, const void *buffer, size_t *length, + int flags) +{ + ssize_t ret; + + ret = send(sock, buffer, *length, flags); +#ifdef _WIN32 + if (ret == SOCKET_ERROR) + return false; +#else + if (ret < 0) + return false; +#endif + *length = ret; + + return true; +} + +/** + * Receive a message from a socket. + * + * @param[in] sock Socket descriptor. + * @param[out] buffer Buffer to store the received message on success. Its + * content is undefined on failure. + * @param[in,out] length Maximum length of the message in bytes. On success, + * the value gets updated with the actual number of + * received bytes. The value is undefined on failure. + * @param[in] flags Flags to modify the function behaviour. Use bitwise OR to + * specify multiple flags. + * + * @return Whether a message was successfully received. + */ +JAYLINK_PRIV bool socket_recv(int sock, void *buffer, size_t *length, + int flags) +{ + ssize_t ret; + + ret = recv(sock, buffer, *length, flags); + +#ifdef _WIN32 + if (ret == SOCKET_ERROR) + return false; +#else + if (ret < 0) + return false; +#endif + + *length = ret; + + return true; +} + +/** + * Send a message on a socket. + * + * @param[in] sock Socket descriptor. + * @param[in] buffer Buffer to send message from. + * @param[in,out] length Number of bytes to send. On success, the value gets + * updated with the actual number of bytes sent. The + * value is undefined on failure. + * @param[in] flags Flags to modify the function behaviour. Use bitwise OR to + * specify multiple flags. + * @param[in] address Destination address of the message. + * @param[in] address_length Length of the structure pointed to by @p address + * in bytes. + * + * @return Whether the message was successfully sent. + */ +JAYLINK_PRIV bool socket_sendto(int sock, const void *buffer, size_t *length, + int flags, const struct sockaddr *address, + size_t address_length) +{ + ssize_t ret; + + ret = sendto(sock, buffer, *length, flags, address, address_length); + +#ifdef _WIN32 + if (ret == SOCKET_ERROR) + return false; +#else + if (ret < 0) + return false; +#endif + + *length = ret; + + return true; +} + +/** + * Receive a message from a socket. + * + * @param[in] sock Socket descriptor. + * @param[out] buffer Buffer to store the received message on success. Its + * content is undefined on failure. + * @param[in,out] length Maximum length of the message in bytes. On success, + * the value gets updated with the actual number of + * received bytes. The value is undefined on failure. + * @param[in] flags Flags to modify the function behaviour. Use bitwise OR to + * specify multiple flags. + * @param[out] address Structure to store the source address of the message on + * success. Its content is undefined on failure. + * Can be NULL. + * @param[in,out] address_length Length of the structure pointed to by + * @p address in bytes. On success, the value + * gets updated with the actual length of the + * structure. The value is undefined on failure. + * Should be NULL if @p address is NULL. + * + * @return Whether a message was successfully received. + */ +JAYLINK_PRIV bool socket_recvfrom(int sock, void *buffer, size_t *length, + int flags, struct sockaddr *address, size_t *address_length) +{ + ssize_t ret; +#ifdef _WIN32 + int tmp; + + tmp = *address_length; + ret = recvfrom(sock, buffer, *length, flags, address, &tmp); + + if (ret == SOCKET_ERROR) + return false; +#else + socklen_t tmp; + + tmp = *address_length; + ret = recvfrom(sock, buffer, *length, flags, address, &tmp); + + if (ret < 0) + return false; +#endif + + *address_length = tmp; + *length = ret; + + return true; +} + +/** + * Set an option on a socket. + * + * @param[in] sock Socket descriptor. + * @param[in] level Level at which the option is defined. + * @param[in] option Option to set the value for. + * @param[in] value Buffer of the value to be set. + * @param[in] length Length of the value buffer in bytes. + * + * @return Whether the option was set successfully. + */ +JAYLINK_PRIV bool socket_set_option(int sock, int level, int option, + const void *value, size_t length) +{ + if (!setsockopt(sock, level, option, value, length)) + return true; + + return false; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/strutil.c b/src/jtag/drivers/libjaylink/libjaylink/strutil.c new file mode 100644 index 0000000..283ed17 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/strutil.c @@ -0,0 +1,66 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2016 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 <stdlib.h> +#include <stdint.h> +#include <errno.h> + +#include "libjaylink.h" + +/** + * @file + * + * String utility functions. + */ + +/** + * Convert a string representation of a serial number to an integer. + * + * The string representation of the serial number must be in decimal form. + * + * @param[in] str String representation to convert. + * @param[out] serial_number Serial number on success, and undefined on + * failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR Conversion error. Serial number is invalid or string + * representation contains invalid character(s). + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_parse_serial_number(const char *str, + uint32_t *serial_number) +{ + char *end_ptr; + unsigned long long tmp; + + if (!str || !serial_number) + return JAYLINK_ERR_ARG; + + errno = 0; + tmp = strtoull(str, &end_ptr, 10); + + if (*end_ptr != '\0' || errno != 0 || tmp > UINT32_MAX) + return JAYLINK_ERR; + + *serial_number = tmp; + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/swd.c b/src/jtag/drivers/libjaylink/libjaylink/swd.c new file mode 100644 index 0000000..29265b7 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/swd.c @@ -0,0 +1,148 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-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 Debug (SWD) functions. + */ + +/** @cond PRIVATE */ +#define CMD_SWD_IO 0xcf + +/** + * Error code indicating that there is not enough free memory on the device to + * perform the SWD I/O operation. + */ +#define SWD_IO_ERR_NO_MEMORY 0x06 +/** @endcond */ + +/** + * Perform a SWD I/O operation. + * + * @note This function must only be used if the #JAYLINK_TIF_SWD interface is + * available and selected. + * + * @param[in,out] devh Device handle. + * @param[in] direction Buffer to read the transfer direction from. + * @param[in] out Buffer to read host-to-target data from. + * @param[out] in Buffer to store target-to-host data on success. Its content + * is undefined on failure. The buffer must be large enough to + * contain at least the specified number of bits to transfer. + * @param[in] length Total number of bits to transfer from host to target and + * vice versa. + * + * @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_NO_MEMORY Not enough memory on the device to perform + * the operation. + * @retval JAYLINK_ERR_DEV Unspecified device error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_select_interface() + * @see jaylink_set_speed() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_swd_io(struct jaylink_device_handle *devh, + const uint8_t *direction, const uint8_t *out, uint8_t *in, + uint16_t length) +{ + int ret; + struct jaylink_context *ctx; + uint16_t num_bytes; + uint8_t buf[4]; + uint8_t status; + + if (!devh || !direction || !out || !in || !length) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + num_bytes = (length + 7) / 8; + + ret = transport_start_write_read(devh, 4 + 2 * num_bytes, + num_bytes + 1, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_SWD_IO; + buf[1] = 0x00; + buffer_set_u16(buf, length, 2); + + ret = transport_write(devh, buf, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_write(devh, direction, num_bytes); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_write(devh, out, num_bytes); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, in, num_bytes); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, &status, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + if (status == SWD_IO_ERR_NO_MEMORY) { + return JAYLINK_ERR_DEV_NO_MEMORY; + } else if (status > 0) { + log_err(ctx, "SWD I/O operation failed: 0x%x.", status); + return JAYLINK_ERR_DEV; + } + + return JAYLINK_OK; +} 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; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/target.c b/src/jtag/drivers/libjaylink/libjaylink/target.c new file mode 100644 index 0000000..264335b --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/target.c @@ -0,0 +1,533 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-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 + * + * Target related functions. + */ + +/** @cond PRIVATE */ +#define CMD_SET_SPEED 0x05 +#define CMD_SET_TARGET_POWER 0x08 +#define CMD_GET_SPEEDS 0xc0 +#define CMD_SELECT_TIF 0xc7 +#define CMD_CLEAR_RESET 0xdc +#define CMD_SET_RESET 0xdd + +#define TIF_GET_SELECTED 0xfe +#define TIF_GET_AVAILABLE 0xff +/** @endcond */ + +/** + * Set the target interface speed. + * + * @param[in,out] devh Device handle. + * @param[in] speed Speed in kHz or #JAYLINK_SPEED_ADAPTIVE_CLOCKING for + * adaptive clocking. Speed of 0 kHz is not allowed and + * adaptive clocking must only be used if the device has the + * #JAYLINK_DEV_CAP_ADAPTIVE_CLOCKING capability. + * + * @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 Other error conditions. + * + * @see jaylink_get_speeds() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_set_speed(struct jaylink_device_handle *devh, + uint16_t speed) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[3]; + + if (!devh || !speed) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 3, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_SET_SPEED; + buffer_set_u16(buf, speed, 1); + + ret = transport_write(devh, buf, 3); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + return JAYLINK_OK; +} + +/** + * Retrieve target interface speeds. + * + * The speeds are applicable for the currently selected target interface only + * and calculated as follows: + * + * @par + * <tt>speeds = @a freq / n</tt> with <tt>n >= @a div</tt>, where @p n is an + * integer + * + * Assuming, for example, a base frequency @a freq of 4 MHz and a minimum + * divider @a div of 4 then the highest possible target interface speed is + * 4 MHz / 4 = 1 MHz. The next highest speed is 800 kHz for a divider of 5, and + * so on. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_GET_SPEEDS capability. + * + * @param[in,out] devh Device handle. + * @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 Other error conditions. + * + * @see jaylink_select_interface() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_get_speeds(struct jaylink_device_handle *devh, + struct jaylink_speed *speed) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[6]; + uint16_t div; + + if (!devh || !speed) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 1, 6, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_GET_SPEEDS; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 6); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + div = buffer_get_u16(buf, 4); + + if (!div) { + log_err(ctx, "Minimum frequency divider is zero."); + return JAYLINK_ERR_PROTO; + } + + speed->freq = buffer_get_u32(buf, 0); + speed->div = div; + + return JAYLINK_OK; +} + +/** + * Select the target interface. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_SELECT_TIF capability. + * + * @warning This function may return a value for @p prev_iface which is not + * covered by #jaylink_target_interface. + * + * @param[in,out] devh Device handle. + * @param[in] iface Target interface to select. + * @param[out] prev_iface Previously selected target interface on success, and + * undefined on failure. Can be NULL. + * + * @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 Other error conditions. + * + * @see jaylink_get_available_interfaces() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_select_interface(struct jaylink_device_handle *devh, + enum jaylink_target_interface iface, + enum jaylink_target_interface *prev_iface) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[4]; + + if (!devh) + return JAYLINK_ERR_ARG; + + switch (iface) { + case JAYLINK_TIF_JTAG: + case JAYLINK_TIF_SWD: + case JAYLINK_TIF_BDM3: + case JAYLINK_TIF_FINE: + case JAYLINK_TIF_2W_JTAG_PIC32: + break; + default: + return JAYLINK_ERR_ARG; + } + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 2, 4, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_SELECT_TIF; + buf[1] = iface; + + ret = transport_write(devh, buf, 2); + + 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; + } + + if (prev_iface) + *prev_iface = buffer_get_u32(buf, 0); + + return JAYLINK_OK; +} + +/** + * Retrieve the available target interfaces. + * + * The target interfaces are stored in a 32-bit bit field where each individual + * bit represents a target interface. A set bit indicates an available target + * interface. See #jaylink_target_interface for a description of the target + * interfaces and their bit positions. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_SELECT_TIF capability. + * + * @param[in,out] devh Device handle. + * @param[out] ifaces Target interfaces 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_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_select_interface() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_get_available_interfaces( + struct jaylink_device_handle *devh, uint32_t *ifaces) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[4]; + + if (!devh || !ifaces) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 2, 4, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_SELECT_TIF; + buf[1] = TIF_GET_AVAILABLE; + + ret = transport_write(devh, buf, 2); + + 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; + } + + *ifaces = buffer_get_u32(buf, 0); + + return JAYLINK_OK; +} + +/** + * Retrieve the selected target interface. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_SELECT_TIF capability. + * + * @warning This function may return a value for @p iface which is not covered + * by #jaylink_target_interface. + * + * @param[in,out] devh Device handle. + * @param[out] iface Selected target interface 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_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_select_interface() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_get_selected_interface( + struct jaylink_device_handle *devh, + enum jaylink_target_interface *iface) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[4]; + + if (!devh || !iface) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 2, 4, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_SELECT_TIF; + buf[1] = TIF_GET_SELECTED; + + ret = transport_write(devh, buf, 2); + + 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; + } + + *iface = buffer_get_u32(buf, 0); + + return JAYLINK_OK; +} + +/** + * Clear the target reset signal. + * + * @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 Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_clear_reset(struct jaylink_device_handle *devh) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[1]; + + if (!devh) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 1, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_CLEAR_RESET; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + return JAYLINK_OK; +} + +/** + * Set the target reset signal. + * + * @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 Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_set_reset(struct jaylink_device_handle *devh) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[1]; + + if (!devh) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 1, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_SET_RESET; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + return JAYLINK_OK; +} + +/** + * Set the target power supply. + * + * If enabled, the target is supplied with 5 V from pin 19 of the 20-pin + * JTAG / SWD connector. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_SET_TARGET_POWER capability. + * + * @param[in,out] devh Device handle. + * @param[in] enable Determines whether to enable or disable the target power + * supply. + * + * @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 Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_set_target_power(struct jaylink_device_handle *devh, + bool enable) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[2]; + + if (!devh) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 2, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_wrte() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_SET_TARGET_POWER; + buf[1] = enable; + + ret = transport_write(devh, buf, 2); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/transport.c b/src/jtag/drivers/libjaylink/libjaylink/transport.c new file mode 100644 index 0000000..0c276b3 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/transport.c @@ -0,0 +1,309 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-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 <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * Transport abstraction layer. + */ + +/** + * Open a device. + * + * This function must be called before any other function of the transport + * abstraction layer for the given device handle is called. + * + * @param[in,out] devh Device handle. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + */ +JAYLINK_PRIV int transport_open(struct jaylink_device_handle *devh) +{ + int ret; + + switch (devh->dev->iface) { +#ifdef HAVE_LIBUSB + case JAYLINK_HIF_USB: + ret = transport_usb_open(devh); + break; +#endif + case JAYLINK_HIF_TCP: + ret = transport_tcp_open(devh); + break; + default: + log_err(devh->dev->ctx, "BUG: Invalid host interface: %u.", + devh->dev->iface); + return JAYLINK_ERR; + } + + return ret; +} + +/** + * Close a device. + * + * After this function has been called no other function of the transport + * abstraction layer for the given device handle must be called. + * + * @param[in,out] devh Device handle. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR Other error conditions. + */ +JAYLINK_PRIV int transport_close(struct jaylink_device_handle *devh) +{ + int ret; + + switch (devh->dev->iface) { +#ifdef HAVE_LIBUSB + case JAYLINK_HIF_USB: + ret = transport_usb_close(devh); + break; +#endif + case JAYLINK_HIF_TCP: + ret = transport_tcp_close(devh); + break; + default: + log_err(devh->dev->ctx, "BUG: Invalid host interface: %u.", + devh->dev->iface); + return JAYLINK_ERR; + } + + return ret; +} + +/** + * Start a write operation for a device. + * + * The data of a write operation must be written with at least one call of + * transport_write(). It is required that all data of a write operation is + * written before an other write and/or read operation is started. + * + * @param[in,out] devh Device handle. + * @param[in] length Number of bytes of the write operation. + * @param[in] has_command Determines whether the data of the write operation + * contains the protocol command. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + */ +JAYLINK_PRIV int transport_start_write(struct jaylink_device_handle *devh, + size_t length, bool has_command) +{ + int ret; + + switch (devh->dev->iface) { +#ifdef HAVE_LIBUSB + case JAYLINK_HIF_USB: + ret = transport_usb_start_write(devh, length, has_command); + break; +#endif + case JAYLINK_HIF_TCP: + ret = transport_tcp_start_write(devh, length, has_command); + break; + default: + log_err(devh->dev->ctx, "BUG: Invalid host interface: %u.", + devh->dev->iface); + return JAYLINK_ERR; + } + + return ret; +} + +/** + * Start a read operation for a device. + * + * The data of a read operation must be read with at least one call of + * transport_read(). It is required that all data of a read operation is read + * before an other write and/or read operation is started. + * + * @param[in,out] devh Device handle. + * @param[in] length Number of bytes of the read operation. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + */ +JAYLINK_PRIV int transport_start_read(struct jaylink_device_handle *devh, + size_t length) +{ + int ret; + + switch (devh->dev->iface) { +#ifdef HAVE_LIBUSB + case JAYLINK_HIF_USB: + ret = transport_usb_start_read(devh, length); + break; +#endif + case JAYLINK_HIF_TCP: + ret = transport_tcp_start_read(devh, length); + break; + default: + log_err(devh->dev->ctx, "BUG: Invalid host interface: %u.", + devh->dev->iface); + return JAYLINK_ERR; + } + + return ret; +} + +/** + * Start a write and read operation for a device. + * + * This function starts a write and read operation as the consecutive call of + * transport_start_write() and transport_start_read() but has a different + * meaning from the protocol perspective and can therefore not be replaced by + * these functions and vice versa. + * + * @note The write operation must be completed first before the read operation + * must be processed. + * + * @param[in,out] devh Device handle. + * @param[in] write_length Number of bytes of the write operation. + * @param[in] read_length Number of bytes of the read operation. + * @param[in] has_command Determines whether the data of the write operation + * contains the protocol command. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + */ +JAYLINK_PRIV int transport_start_write_read(struct jaylink_device_handle *devh, + size_t write_length, size_t read_length, bool has_command) +{ + int ret; + + switch (devh->dev->iface) { +#ifdef HAVE_LIBUSB + case JAYLINK_HIF_USB: + ret = transport_usb_start_write_read(devh, write_length, + read_length, has_command); + break; +#endif + case JAYLINK_HIF_TCP: + ret = transport_tcp_start_write_read(devh, write_length, + read_length, has_command); + break; + default: + log_err(devh->dev->ctx, "BUG: Invalid host interface: %u.", + devh->dev->iface); + return JAYLINK_ERR; + } + + return ret; +} + +/** + * Write data to a device. + * + * Before this function is used transport_start_write() or + * transport_start_write_read() must be called to start a write operation. The + * total number of written bytes must not exceed the number of bytes of the + * write operation. + * + * @note A write operation will be performed and the data will be sent to the + * device when the number of written bytes reaches the number of bytes of + * the write operation. Before that the data will be written into a + * buffer. + * + * @param[in,out] devh Device handle. + * @param[in] buffer Buffer to write data from. + * @param[in] length Number of bytes to write. + * + * @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 Other error conditions. + */ +JAYLINK_PRIV int transport_write(struct jaylink_device_handle *devh, + const uint8_t *buffer, size_t length) +{ + int ret; + + switch (devh->dev->iface) { +#ifdef HAVE_LIBUSB + case JAYLINK_HIF_USB: + ret = transport_usb_write(devh, buffer, length); + break; +#endif + case JAYLINK_HIF_TCP: + ret = transport_tcp_write(devh, buffer, length); + break; + default: + log_err(devh->dev->ctx, "BUG: Invalid host interface: %u.", + devh->dev->iface); + return JAYLINK_ERR; + } + + return ret; +} + +/** + * Read data from a device. + * + * Before this function is used transport_start_read() or + * transport_start_write_read() must be called to start a read operation. The + * total number of read bytes must not exceed the number of bytes of the read + * operation. + * + * @param[in,out] devh Device handle. + * @param[out] buffer Buffer to read data into on success. Its content is + * undefined on failure. + * @param[in] length Number of bytes to read. + * + * @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 Other error conditions. + */ +JAYLINK_PRIV int transport_read(struct jaylink_device_handle *devh, + uint8_t *buffer, size_t length) +{ + int ret; + + switch (devh->dev->iface) { +#ifdef HAVE_LIBUSB + case JAYLINK_HIF_USB: + ret = transport_usb_read(devh, buffer, length); + break; +#endif + case JAYLINK_HIF_TCP: + ret = transport_tcp_read(devh, buffer, length); + break; + default: + log_err(devh->dev->ctx, "BUG: Invalid host interface: %u.", + devh->dev->iface); + return JAYLINK_ERR; + } + + return ret; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/transport_tcp.c b/src/jtag/drivers/libjaylink/libjaylink/transport_tcp.c new file mode 100644 index 0000000..7e10179 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/transport_tcp.c @@ -0,0 +1,601 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2015-2017 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 <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <sys/types.h> + +#ifdef _WIN32 +#include <winsock2.h> +#include <ws2tcpip.h> +#else +#include <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#endif + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * Transport abstraction layer (TCP/IP). + */ + +/** @cond PRIVATE */ +#define CMD_SERVER 0x00 +#define CMD_CLIENT 0x07 + +/** + * Response status code indicating that the maximum number of simultaneous + * connections on the device has been reached. + */ +#define RESP_MAX_CONNECTIONS 0xfe + +/** Buffer size in bytes. */ +#define BUFFER_SIZE 2048 + +/** Timeout of a receive operation in milliseconds. */ +#define RECV_TIMEOUT 5000 +/** Timeout of a send operation in milliseconds. */ +#define SEND_TIMEOUT 5000 + +/** String of the port number for the J-Link TCP/IP protocol. */ +#define PORT_STRING "19020" + +/** Size of the server's hello message in bytes. */ +#define SERVER_HELLO_SIZE 4 +/** + * Maximum length of the server name including trailing null-terminator in + * bytes. + */ +#define SERVER_NAME_MAX_LENGTH 256 +/** @endcond */ + +static int initialize_handle(struct jaylink_device_handle *devh) +{ + struct jaylink_context *ctx; + + ctx = devh->dev->ctx; + + devh->buffer_size = BUFFER_SIZE; + devh->buffer = malloc(devh->buffer_size); + + if (!devh->buffer) { + log_err(ctx, "Transport buffer malloc failed."); + return JAYLINK_ERR_MALLOC; + } + + devh->read_length = 0; + devh->bytes_available = 0; + devh->read_pos = 0; + + devh->write_length = 0; + devh->write_pos = 0; + + return JAYLINK_OK; +} + +static void cleanup_handle(struct jaylink_device_handle *devh) +{ + free(devh->buffer); +} + +static int _recv(struct jaylink_device_handle *devh, uint8_t *buffer, + size_t length) +{ + struct jaylink_context *ctx; + size_t tmp; + + ctx = devh->dev->ctx; + + while (length > 0) { + tmp = length; + + if (!socket_recv(devh->sock, buffer, &tmp, 0)) { + log_err(ctx, "Failed to receive data from device."); + return JAYLINK_ERR_IO; + } else if (!tmp) { + log_err(ctx, "Failed to receive data from device: " + "remote connection closed."); + return JAYLINK_ERR_IO; + } + + buffer += tmp; + length -= tmp; + + log_dbgio(ctx, "Received %zu bytes from device.", tmp); + } + + return JAYLINK_OK; +} + +static int handle_server_hello(struct jaylink_device_handle *devh) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[SERVER_HELLO_SIZE]; + char name[SERVER_NAME_MAX_LENGTH]; + uint16_t proto_version; + size_t length; + + ctx = devh->dev->ctx; + + ret = _recv(devh, buf, sizeof(buf)); + + if (ret != JAYLINK_OK) { + log_err(ctx, "Failed to receive hello message."); + return ret; + } + + if (buf[0] == RESP_MAX_CONNECTIONS) { + log_err(ctx, "Maximum number of connections reached."); + return JAYLINK_ERR; + } + + if (buf[0] != CMD_SERVER) { + log_err(ctx, "Invalid hello message received."); + return JAYLINK_ERR_PROTO; + } + + proto_version = buffer_get_u16(buf, 1); + + log_dbg(ctx, "Protocol version: 0x%04x.", proto_version); + + length = buf[3]; + ret = _recv(devh, (uint8_t *)name, length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "Failed to receive server name."); + return ret; + } + + name[length] = '\0'; + + log_dbg(ctx, "Server name: %s.", name); + + return JAYLINK_OK; +} + +static int set_socket_timeouts(struct jaylink_device_handle *devh) +{ + struct jaylink_context *ctx; + + ctx = devh->dev->ctx; +#ifdef _WIN32 + DWORD timeout; + + timeout = RECV_TIMEOUT; + + if (!socket_set_option(devh->sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, + sizeof(timeout))) { + log_err(ctx, "Failed to set socket receive timeout."); + return JAYLINK_ERR; + } + + timeout = SEND_TIMEOUT; + + if (!socket_set_option(devh->sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, + sizeof(timeout))) { + log_err(ctx, "Failed to set socket send timeout."); + return JAYLINK_ERR; + } +#else + struct timeval timeout; + + timeout.tv_sec = RECV_TIMEOUT / 1000; + timeout.tv_usec = (RECV_TIMEOUT % 1000) * 1000; + + if (!socket_set_option(devh->sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, + sizeof(struct timeval))) { + log_err(ctx, "Failed to set socket receive timeout."); + return JAYLINK_ERR; + } + + timeout.tv_sec = SEND_TIMEOUT / 1000; + timeout.tv_usec = (SEND_TIMEOUT % 1000) * 1000; + + if (!socket_set_option(devh->sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, + sizeof(struct timeval))) { + log_err(ctx, "Failed to set socket send timeout."); + return JAYLINK_ERR; + } +#endif + return JAYLINK_OK; +} + +JAYLINK_PRIV int transport_tcp_open(struct jaylink_device_handle *devh) +{ + int ret; + struct jaylink_context *ctx; + struct jaylink_device *dev; + struct addrinfo hints; + struct addrinfo *info; + struct addrinfo *rp; + int sock; + + dev = devh->dev; + ctx = dev->ctx; + + log_dbg(ctx, "Trying to open device (IPv4 address = %s).", + dev->ipv4_address); + + ret = initialize_handle(devh); + + if (ret != JAYLINK_OK) { + log_err(ctx, "Initialize device handle failed."); + return ret; + } + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + ret = getaddrinfo(dev->ipv4_address, PORT_STRING, &hints, &info); + + if (ret != 0) { + log_err(ctx, "Address lookup failed."); + cleanup_handle(devh); + return JAYLINK_ERR; + } + + sock = -1; + + for (rp = info; rp != NULL; rp = rp->ai_next) { + sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + + if (sock < 0) + continue; + + if (!connect(sock, info->ai_addr, info->ai_addrlen)) + break; + + socket_close(sock); + sock = -1; + } + + freeaddrinfo(info); + + if (sock < 0) { + log_err(ctx, "Failed to open device."); + cleanup_handle(devh); + return JAYLINK_ERR; + } + + log_dbg(ctx, "Device opened successfully."); + + devh->sock = sock; + ret = set_socket_timeouts(devh); + + if (ret != JAYLINK_OK) { + socket_close(sock); + cleanup_handle(devh); + return ret; + } + + ret = handle_server_hello(devh); + + if (ret != JAYLINK_OK) { + socket_close(sock); + cleanup_handle(devh); + return ret; + } + + return JAYLINK_OK; +} + +JAYLINK_PRIV int transport_tcp_close(struct jaylink_device_handle *devh) +{ + struct jaylink_context *ctx; + + ctx = devh->dev->ctx; + + log_dbg(ctx, "Closing device (IPv4 address = %s).", + devh->dev->ipv4_address); + + cleanup_handle(devh); + + log_dbg(ctx, "Device closed successfully."); + + return JAYLINK_OK; +} + +JAYLINK_PRIV int transport_tcp_start_write(struct jaylink_device_handle *devh, + size_t length, bool has_command) +{ + struct jaylink_context *ctx; + + if (!length) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + + log_dbgio(ctx, "Starting write operation (length = %zu bytes).", + length); + + if (devh->write_pos > 0) + log_warn(ctx, "Last write operation left %zu bytes in the " + "buffer.", devh->write_pos); + + if (devh->write_length > 0) + log_warn(ctx, "Last write operation was not performed."); + + devh->write_length = length; + devh->write_pos = 0; + + if (has_command) { + devh->buffer[0] = CMD_CLIENT; + devh->write_pos++; + } + + return JAYLINK_OK; +} + +JAYLINK_PRIV int transport_tcp_start_read(struct jaylink_device_handle *devh, + size_t length) +{ + struct jaylink_context *ctx; + + if (!length) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + + log_dbgio(ctx, "Starting read operation (length = %zu bytes).", + length); + + if (devh->bytes_available > 0) + log_dbg(ctx, "Last read operation left %zu bytes in the " + "buffer.", devh->bytes_available); + + if (devh->read_length > 0) + log_warn(ctx, "Last read operation left %zu bytes.", + devh->read_length); + + devh->read_length = length; + + return JAYLINK_OK; +} + +JAYLINK_PRIV int transport_tcp_start_write_read( + struct jaylink_device_handle *devh, size_t write_length, + size_t read_length, bool has_command) +{ + struct jaylink_context *ctx; + + if (!read_length || !write_length) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + + log_dbgio(ctx, "Starting write / read operation (length = " + "%zu / %zu bytes).", write_length, read_length); + + if (devh->write_pos > 0) + log_warn(ctx, "Last write operation left %zu bytes in the " + "buffer.", devh->write_pos); + + if (devh->write_length > 0) + log_warn(ctx, "Last write operation was not performed."); + + if (devh->bytes_available > 0) + log_warn(ctx, "Last read operation left %zu bytes in the " + "buffer.", devh->bytes_available); + + if (devh->read_length > 0) + log_warn(ctx, "Last read operation left %zu bytes.", + devh->read_length); + + devh->write_length = write_length; + devh->write_pos = 0; + + if (has_command) { + devh->buffer[0] = CMD_CLIENT; + devh->write_pos++; + } + + devh->read_length = read_length; + devh->bytes_available = 0; + devh->read_pos = 0; + + return JAYLINK_OK; +} + +static int _send(struct jaylink_device_handle *devh, const uint8_t *buffer, + size_t length) +{ + struct jaylink_context *ctx; + size_t tmp; + + ctx = devh->dev->ctx; + + while (length > 0) { + tmp = length; + + if (!socket_send(devh->sock, buffer, &tmp, 0)) { + log_err(ctx, "Failed to send data to device."); + return JAYLINK_ERR_IO; + } + + buffer += tmp; + length -= tmp; + + log_dbgio(ctx, "Sent %zu bytes to device.", tmp); + } + + return JAYLINK_OK; +} + +static bool adjust_buffer(struct jaylink_device_handle *devh, size_t size) +{ + struct jaylink_context *ctx; + uint8_t *buffer; + size_t num; + + ctx = devh->dev->ctx; + + /* Adjust buffer size to a multiple of BUFFER_SIZE bytes. */ + num = size / BUFFER_SIZE; + + if (size % BUFFER_SIZE > 0) + num++; + + size = num * BUFFER_SIZE; + buffer = realloc(devh->buffer, size); + + if (!buffer) { + log_err(ctx, "Failed to adjust buffer size to %zu bytes.", + size); + return false; + } + + devh->buffer = buffer; + devh->buffer_size = size; + + log_dbg(ctx, "Adjusted buffer size to %zu bytes.", size); + + return true; +} + +JAYLINK_PRIV int transport_tcp_write(struct jaylink_device_handle *devh, + const uint8_t *buffer, size_t length) +{ + int ret; + struct jaylink_context *ctx; + size_t tmp; + + ctx = devh->dev->ctx; + + if (length > devh->write_length) { + log_err(ctx, "Requested to write %zu bytes but only %zu bytes " + "are expected for the write operation.", length, + devh->write_length); + return JAYLINK_ERR_ARG; + } + + /* + * Store data in the buffer if the expected number of bytes for the + * write operation is not reached. + */ + if (length < devh->write_length) { + if (devh->write_pos + length > devh->buffer_size) { + if (!adjust_buffer(devh, devh->write_pos + length)) + return JAYLINK_ERR_MALLOC; + } + + memcpy(devh->buffer + devh->write_pos, buffer, length); + + devh->write_length -= length; + devh->write_pos += length; + + log_dbgio(ctx, "Wrote %zu bytes into buffer.", length); + return JAYLINK_OK; + } + + /* + * Expected number of bytes for this write operation is reached and + * therefore the write operation will be performed. + */ + devh->write_length = 0; + + /* Send data directly to the device if the buffer is empty. */ + if (!devh->write_pos) + return _send(devh, buffer, length); + + tmp = MIN(length, devh->buffer_size - devh->write_pos); + + /* + * Fill up the internal buffer in order to reduce the number of + * messages sent to the device for performance reasons. + */ + memcpy(devh->buffer + devh->write_pos, buffer, tmp); + + length -= tmp; + buffer += tmp; + + log_dbgio(ctx, "Buffer filled up with %zu bytes.", tmp); + + ret = _send(devh, devh->buffer, devh->write_pos + tmp); + + devh->write_pos = 0; + + if (ret != JAYLINK_OK) + return ret; + + if (!length) + return JAYLINK_OK; + + return _send(devh, buffer, length); +} + +JAYLINK_PRIV int transport_tcp_read(struct jaylink_device_handle *devh, + uint8_t *buffer, size_t length) +{ + int ret; + struct jaylink_context *ctx; + + ctx = devh->dev->ctx; + + if (length > devh->read_length) { + log_err(ctx, "Requested to read %zu bytes but only %zu bytes " + "are expected for the read operation.", length, + devh->read_length); + return JAYLINK_ERR_ARG; + } + + if (length <= devh->bytes_available) { + memcpy(buffer, devh->buffer + devh->read_pos, length); + + devh->read_length -= length; + devh->bytes_available -= length; + devh->read_pos += length; + + log_dbgio(ctx, "Read %zu bytes from buffer.", length); + return JAYLINK_OK; + } + + if (devh->bytes_available) { + memcpy(buffer, devh->buffer + devh->read_pos, + devh->bytes_available); + + buffer += devh->bytes_available; + length -= devh->bytes_available; + devh->read_length -= devh->bytes_available; + + log_dbgio(ctx, "Read %zu bytes from buffer to flush it.", + devh->bytes_available); + + devh->bytes_available = 0; + devh->read_pos = 0; + } + + ret = _recv(devh, buffer, length); + + if (ret != JAYLINK_OK) + return ret; + + devh->read_length -= length; + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/transport_usb.c b/src/jtag/drivers/libjaylink/libjaylink/transport_usb.c new file mode 100644 index 0000000..dfe9eac --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/transport_usb.c @@ -0,0 +1,620 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2016 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 <stdlib.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * Transport abstraction layer (USB). + */ + +/** Timeout of an USB transfer in milliseconds. */ +#define USB_TIMEOUT 1000 + +/** + * Number of consecutive timeouts before an USB transfer will be treated as + * timed out. + */ +#define NUM_TIMEOUTS 2 + +/** Chunk size in bytes in which data is transferred. */ +#define CHUNK_SIZE 2048 + +static int initialize_handle(struct jaylink_device_handle *devh) +{ + int ret; + struct jaylink_context *ctx; + struct libusb_config_descriptor *config; + const struct libusb_interface *interface; + const struct libusb_interface_descriptor *desc; + const struct libusb_endpoint_descriptor *epdesc; + bool found_interface; + bool found_endpoint_in; + bool found_endpoint_out; + uint8_t i; + + ctx = devh->dev->ctx; + devh->interface_number = 0; + + /* + * Retrieve active configuration descriptor to determine the endpoints + * for the interface number of the device. + */ + ret = libusb_get_active_config_descriptor(devh->dev->usb_dev, &config); + + if (ret != LIBUSB_SUCCESS) { + log_err(ctx, "Failed to get configuration descriptor: %s.", + libusb_error_name(ret)); + return JAYLINK_ERR; + } + + found_interface = false; + + for (i = 0; i < config->bNumInterfaces; i++) { + interface = &config->interface[i]; + desc = &interface->altsetting[0]; + + if (desc->bInterfaceClass != LIBUSB_CLASS_VENDOR_SPEC) + continue; + + if (desc->bInterfaceSubClass != LIBUSB_CLASS_VENDOR_SPEC) + continue; + + if (desc->bNumEndpoints < 2) + continue; + + found_interface = true; + devh->interface_number = i; + break; + } + + if (!found_interface) { + log_err(ctx, "No suitable interface found."); + libusb_free_config_descriptor(config); + return JAYLINK_ERR; + } + + found_endpoint_in = false; + found_endpoint_out = false; + + for (i = 0; i < desc->bNumEndpoints; i++) { + epdesc = &desc->endpoint[i]; + + if (epdesc->bEndpointAddress & LIBUSB_ENDPOINT_IN) { + devh->endpoint_in = epdesc->bEndpointAddress; + found_endpoint_in = true; + } else { + devh->endpoint_out = epdesc->bEndpointAddress; + found_endpoint_out = true; + } + } + + libusb_free_config_descriptor(config); + + if (!found_endpoint_in) { + log_err(ctx, "Interface IN endpoint not found."); + return JAYLINK_ERR; + } + + if (!found_endpoint_out) { + log_err(ctx, "Interface OUT endpoint not found."); + return JAYLINK_ERR; + } + + log_dbg(ctx, "Using endpoint %02x (IN) and %02x (OUT).", + devh->endpoint_in, devh->endpoint_out); + + /* Buffer size must be a multiple of CHUNK_SIZE bytes. */ + devh->buffer_size = CHUNK_SIZE; + devh->buffer = malloc(devh->buffer_size); + + if (!devh->buffer) { + log_err(ctx, "Transport buffer malloc failed."); + return JAYLINK_ERR_MALLOC; + } + + devh->read_length = 0; + devh->bytes_available = 0; + devh->read_pos = 0; + + devh->write_length = 0; + devh->write_pos = 0; + + return JAYLINK_OK; +} + +static void cleanup_handle(struct jaylink_device_handle *devh) +{ + free(devh->buffer); +} + +JAYLINK_PRIV int transport_usb_open(struct jaylink_device_handle *devh) +{ + int ret; + struct jaylink_device *dev; + struct jaylink_context *ctx; + struct libusb_device_handle *usb_devh; + + dev = devh->dev; + ctx = dev->ctx; + + log_dbg(ctx, "Trying to open device (bus:address = %03u:%03u).", + libusb_get_bus_number(dev->usb_dev), + libusb_get_device_address(dev->usb_dev)); + + ret = initialize_handle(devh); + + if (ret != JAYLINK_OK) { + log_err(ctx, "Initialize device handle failed."); + return ret; + } + + ret = libusb_open(dev->usb_dev, &usb_devh); + + if (ret != LIBUSB_SUCCESS) { + log_err(ctx, "Failed to open device: %s.", + libusb_error_name(ret)); + cleanup_handle(devh); + return JAYLINK_ERR; + } + + ret = libusb_claim_interface(usb_devh, devh->interface_number); + + if (ret != LIBUSB_SUCCESS) { + log_err(ctx, "Failed to claim interface: %s.", + libusb_error_name(ret)); + cleanup_handle(devh); + libusb_close(usb_devh); + return JAYLINK_ERR; + } + + log_dbg(ctx, "Device opened successfully."); + + devh->usb_devh = usb_devh; + + return JAYLINK_OK; +} + +JAYLINK_PRIV int transport_usb_close(struct jaylink_device_handle *devh) +{ + int ret; + struct jaylink_device *dev; + struct jaylink_context *ctx; + + dev = devh->dev; + ctx = dev->ctx; + + log_dbg(ctx, "Closing device (bus:address = %03u:%03u).", + libusb_get_bus_number(dev->usb_dev), + libusb_get_device_address(dev->usb_dev)); + + ret = libusb_release_interface(devh->usb_devh, devh->interface_number); + + libusb_close(devh->usb_devh); + cleanup_handle(devh); + + if (ret != LIBUSB_SUCCESS) { + log_err(ctx, "Failed to release interface: %s.", + libusb_error_name(ret)); + return JAYLINK_ERR; + } + + log_dbg(ctx, "Device closed successfully."); + + return JAYLINK_OK; +} + +JAYLINK_PRIV int transport_usb_start_write(struct jaylink_device_handle *devh, + size_t length, bool has_command) +{ + struct jaylink_context *ctx; + + (void)has_command; + + if (!length) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + + log_dbgio(ctx, "Starting write operation (length = %zu bytes).", length); + + if (devh->write_pos > 0) + log_warn(ctx, "Last write operation left %zu bytes in the " + "buffer.", devh->write_pos); + + if (devh->write_length > 0) + log_warn(ctx, "Last write operation was not performed."); + + devh->write_length = length; + devh->write_pos = 0; + + return JAYLINK_OK; +} + +JAYLINK_PRIV int transport_usb_start_read(struct jaylink_device_handle *devh, + size_t length) +{ + struct jaylink_context *ctx; + + if (!length) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + + log_dbgio(ctx, "Starting read operation (length = %zu bytes).", + length); + + if (devh->bytes_available > 0) + log_dbg(ctx, "Last read operation left %zu bytes in the " + "buffer.", devh->bytes_available); + + if (devh->read_length > 0) + log_warn(ctx, "Last read operation left %zu bytes.", + devh->read_length); + + devh->read_length = length; + + return JAYLINK_OK; +} + +JAYLINK_PRIV int transport_usb_start_write_read( + struct jaylink_device_handle *devh, size_t write_length, + size_t read_length, bool has_command) +{ + struct jaylink_context *ctx; + + (void)has_command; + + if (!read_length || !write_length) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + + log_dbgio(ctx, "Starting write / read operation (length = " + "%zu / %zu bytes).", write_length, read_length); + + if (devh->write_pos > 0) + log_warn(ctx, "Last write operation left %zu bytes in the " + "buffer.", devh->write_pos); + + if (devh->write_length > 0) + log_warn(ctx, "Last write operation was not performed."); + + if (devh->bytes_available > 0) + log_warn(ctx, "Last read operation left %zu bytes in the " + "buffer.", devh->bytes_available); + + if (devh->read_length > 0) + log_warn(ctx, "Last read operation left %zu bytes.", + devh->read_length); + + devh->write_length = write_length; + devh->write_pos = 0; + + devh->read_length = read_length; + devh->bytes_available = 0; + devh->read_pos = 0; + + return JAYLINK_OK; +} + +static int usb_recv(struct jaylink_device_handle *devh, uint8_t *buffer, + size_t *length) +{ + int ret; + struct jaylink_context *ctx; + unsigned int tries; + int transferred; + + ctx = devh->dev->ctx; + + tries = NUM_TIMEOUTS; + transferred = 0; + + while (tries > 0 && !transferred) { + /* Always request CHUNK_SIZE bytes from the device. */ + ret = libusb_bulk_transfer(devh->usb_devh, devh->endpoint_in, + (unsigned char *)buffer, CHUNK_SIZE, &transferred, + USB_TIMEOUT); + + if (ret == LIBUSB_ERROR_TIMEOUT) { + log_warn(ctx, "Failed to receive data from " + "device: %s.", libusb_error_name(ret)); + tries--; + continue; + } else if (ret != LIBUSB_SUCCESS) { + log_err(ctx, "Failed to receive data from " + "device: %s.", libusb_error_name(ret)); + return JAYLINK_ERR; + } + + log_dbgio(ctx, "Received %i bytes from device.", transferred); + } + + /* Ignore a possible timeout if at least one byte was received. */ + if (transferred > 0) { + *length = transferred; + return JAYLINK_OK; + } + + log_err(ctx, "Receiving data from device timed out."); + + return JAYLINK_ERR_TIMEOUT; +} + +static bool adjust_buffer(struct jaylink_device_handle *devh, size_t size) +{ + struct jaylink_context *ctx; + size_t num_chunks; + uint8_t *buffer; + + ctx = devh->dev->ctx; + + /* Adjust buffer size to a multiple of CHUNK_SIZE bytes. */ + num_chunks = size / CHUNK_SIZE; + + if (size % CHUNK_SIZE > 0) + num_chunks++; + + size = num_chunks * CHUNK_SIZE; + buffer = realloc(devh->buffer, size); + + if (!buffer) { + log_err(ctx, "Failed to adjust buffer size to %zu bytes.", + size); + return false; + } + + devh->buffer = buffer; + devh->buffer_size = size; + + log_dbg(ctx, "Adjusted buffer size to %zu bytes.", size); + + return true; +} + +static int usb_send(struct jaylink_device_handle *devh, const uint8_t *buffer, + size_t length) +{ + int ret; + struct jaylink_context *ctx; + unsigned int tries; + int transferred; + + ctx = devh->dev->ctx; + tries = NUM_TIMEOUTS; + + while (tries > 0 && length > 0) { + /* Send data in chunks of CHUNK_SIZE bytes to the device. */ + ret = libusb_bulk_transfer(devh->usb_devh, devh->endpoint_out, + (unsigned char *)buffer, MIN(CHUNK_SIZE, length), + &transferred, USB_TIMEOUT); + + if (ret == LIBUSB_SUCCESS) { + tries = NUM_TIMEOUTS; + } else if (ret == LIBUSB_ERROR_TIMEOUT) { + log_warn(ctx, "Failed to send data to device: %s.", + libusb_error_name(ret)); + tries--; + } else { + log_err(ctx, "Failed to send data to device: %s.", + libusb_error_name(ret)); + return JAYLINK_ERR; + } + + buffer += transferred; + length -= transferred; + + log_dbgio(ctx, "Sent %i bytes to device.", transferred); + } + + if (!length) + return JAYLINK_OK; + + log_err(ctx, "Sending data to device timed out."); + + return JAYLINK_ERR_TIMEOUT; +} + +JAYLINK_PRIV int transport_usb_write(struct jaylink_device_handle *devh, + const uint8_t *buffer, size_t length) +{ + int ret; + struct jaylink_context *ctx; + size_t num_chunks; + size_t fill_bytes; + size_t tmp; + + ctx = devh->dev->ctx; + + if (length > devh->write_length) { + log_err(ctx, "Requested to write %zu bytes but only %zu bytes " + "are expected for the write operation.", length, + devh->write_length); + return JAYLINK_ERR_ARG; + } + + /* + * Store data in the buffer if the expected number of bytes for the + * write operation is not reached. + */ + if (length < devh->write_length) { + if (devh->write_pos + length > devh->buffer_size) { + if (!adjust_buffer(devh, devh->write_pos + length)) + return JAYLINK_ERR_MALLOC; + } + + memcpy(devh->buffer + devh->write_pos, buffer, length); + + devh->write_length -= length; + devh->write_pos += length; + + log_dbgio(ctx, "Wrote %zu bytes into buffer.", length); + return JAYLINK_OK; + } + + /* + * Expected number of bytes for this write operation is reached and + * therefore the write operation will be performed. + */ + devh->write_length = 0; + + /* Send data directly to the device if the buffer is empty. */ + if (!devh->write_pos) + return usb_send(devh, buffer, length); + + /* + * Calculate the number of bytes to fill up the buffer to reach a + * multiple of CHUNK_SIZE bytes. This ensures that the data from the + * buffer will be sent to the device in chunks of CHUNK_SIZE bytes. + * Note that this is why the buffer size must be a multiple of + * CHUNK_SIZE bytes. + */ + num_chunks = devh->write_pos / CHUNK_SIZE; + + if (devh->write_pos % CHUNK_SIZE) + num_chunks++; + + fill_bytes = (num_chunks * CHUNK_SIZE) - devh->write_pos; + tmp = MIN(length, fill_bytes); + + if (tmp > 0) { + memcpy(devh->buffer + devh->write_pos, buffer, tmp); + + length -= tmp; + buffer += tmp; + + log_dbgio(ctx, "Buffer filled up with %zu bytes.", tmp); + } + + /* Send buffered data to the device. */ + ret = usb_send(devh, devh->buffer, devh->write_pos + tmp); + devh->write_pos = 0; + + if (ret != JAYLINK_OK) + return ret; + + if (!length) + return JAYLINK_OK; + + /* Send remaining data to the device. */ + return usb_send(devh, buffer, length); +} + +JAYLINK_PRIV int transport_usb_read(struct jaylink_device_handle *devh, + uint8_t *buffer, size_t length) +{ + int ret; + struct jaylink_context *ctx; + size_t bytes_received; + size_t tmp; + + ctx = devh->dev->ctx; + + if (length > devh->read_length) { + log_err(ctx, "Requested to read %zu bytes but only %zu bytes " + "are expected for the read operation.", length, + devh->read_length); + return JAYLINK_ERR_ARG; + } + + if (length <= devh->bytes_available) { + memcpy(buffer, devh->buffer + devh->read_pos, length); + + devh->read_length -= length; + devh->bytes_available -= length; + devh->read_pos += length; + + log_dbgio(ctx, "Read %zu bytes from buffer.", length); + return JAYLINK_OK; + } + + if (devh->bytes_available) { + memcpy(buffer, devh->buffer + devh->read_pos, + devh->bytes_available); + + buffer += devh->bytes_available; + length -= devh->bytes_available; + devh->read_length -= devh->bytes_available; + + log_dbgio(ctx, "Read %zu bytes from buffer to flush it.", + devh->bytes_available); + + devh->bytes_available = 0; + devh->read_pos = 0; + } + + while (length > 0) { + /* + * If less than CHUNK_SIZE bytes are requested from the device, + * store the received data into the internal buffer instead of + * directly into the user provided buffer. This is necessary to + * prevent a possible buffer overflow because the number of + * requested bytes from the device is always CHUNK_SIZE and + * therefore up to CHUNK_SIZE bytes may be received. + * Note that this is why the internal buffer size must be at + * least CHUNK_SIZE bytes. + */ + if (length < CHUNK_SIZE) { + ret = usb_recv(devh, devh->buffer, &bytes_received); + + if (ret != JAYLINK_OK) + return ret; + + tmp = MIN(bytes_received, length); + memcpy(buffer, devh->buffer, tmp); + + /* + * Setup the buffer for the remaining data if more data + * was received from the device than was requested. + */ + if (bytes_received > length) { + devh->bytes_available = bytes_received - tmp; + devh->read_pos = tmp; + } + + buffer += tmp; + length -= tmp; + devh->read_length -= tmp; + + log_dbgio(ctx, "Read %zu bytes from buffer.", tmp); + } else { + ret = usb_recv(devh, buffer, &bytes_received); + + if (ret != JAYLINK_OK) + return ret; + + buffer += bytes_received; + length -= bytes_received; + devh->read_length -= bytes_received; + + log_dbgio(ctx, "Read %zu bytes from device.", + bytes_received); + } + } + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/util.c b/src/jtag/drivers/libjaylink/libjaylink/util.c new file mode 100644 index 0000000..4862d4e --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/util.c @@ -0,0 +1,56 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-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 <stdbool.h> + +#include "libjaylink.h" + +/** + * @file + * + * Utility functions. + */ + +/** + * Check for a capability. + * + * The capabilities are expected to be stored in a bit array consisting of one + * or more bytes where each individual bit represents a capability. The first + * bit of this array is the least significant bit of the first byte and the + * following bits are sequentially numbered in order of increasing bit + * significance and byte index. A set bit indicates a supported capability. + * + * @param[in] caps Buffer with capabilities. + * @param[in] cap Bit position of the capability to check for. + * + * @retval true Capability is supported. + * @retval false Capability is not supported or invalid argument. + * + * @since 0.1.0 + */ +JAYLINK_API bool jaylink_has_cap(const uint8_t *caps, uint32_t cap) +{ + if (!caps) + return false; + + if (caps[cap / 8] & (1 << (cap % 8))) + return true; + + return false; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/version.c b/src/jtag/drivers/libjaylink/libjaylink/version.c new file mode 100644 index 0000000..88bc023 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/version.c @@ -0,0 +1,128 @@ +/* + * 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 "libjaylink.h" + +/** + * @file + * + * Package and library version functions. + */ + +/** + * Get the major version number of the libjaylink package. + * + * @return The major version number of the libjaylink package. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_version_package_get_major(void) +{ + return JAYLINK_VERSION_PACKAGE_MAJOR; +} + +/** + * Get the minor version number of the libjaylink package. + * + * @return The minor version number of the libjaylink package. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_version_package_get_minor(void) +{ + return JAYLINK_VERSION_PACKAGE_MINOR; +} + +/** + * Get the micro version number of the libjaylink package. + * + * @return The micro version number of the libjaylink package. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_version_package_get_micro(void) +{ + return JAYLINK_VERSION_PACKAGE_MICRO; +} + +/** + * Get the version number string of the libjaylink package. + * + * @return A string which contains the version number of the libjaylink + * package. The string is null-terminated and must not be free'd by the + * caller. + * + * @since 0.1.0 + */ +JAYLINK_API const char *jaylink_version_package_get_string(void) +{ + return JAYLINK_VERSION_PACKAGE_STRING; +} + +/** + * Get the <i>current</i> version number of the libjaylink libtool interface. + * + * @return The <i>current</i> version number of the libjaylink libtool + * interface. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_version_library_get_current(void) +{ + return JAYLINK_VERSION_LIBRARY_CURRENT; +} + +/** + * Get the <i>revision</i> version number of the libjaylink libtool interface. + * + * @return The <i>revision</i> version number of the libjaylink libtool + * interface. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_version_library_get_revision(void) +{ + return JAYLINK_VERSION_LIBRARY_REVISION; +} + +/** + * Get the <i>age</i> version number of the libjaylink libtool interface. + * + * @return The <i>age</i> version number of the libjaylink libtool interface. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_version_library_get_age(void) +{ + return JAYLINK_VERSION_LIBRARY_AGE; +} + +/** + * Get the version number string of the libjaylink libtool interface. + * + * @return A string which contains the version number of the libjaylink libtool + * interface. The string is null-terminated and must not be free'd by + * the caller. + * + * @since 0.1.0 + */ +JAYLINK_API const char *jaylink_version_library_get_string(void) +{ + return JAYLINK_VERSION_LIBRARY_STRING; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/version.h.in b/src/jtag/drivers/libjaylink/libjaylink/version.h.in new file mode 100644 index 0000000..d6a7796 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/version.h.in @@ -0,0 +1,53 @@ +/* + * 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/>. + */ + +#ifndef LIBJAYLINK_VERSION_H +#define LIBJAYLINK_VERSION_H + +/** + * @file + * + * Package and library version macros. + */ + +/** Major version number of the libjaylink package. */ +#define JAYLINK_VERSION_PACKAGE_MAJOR @JAYLINK_VERSION_PACKAGE_MAJOR@ + +/** Minor version number of the libjaylink package. */ +#define JAYLINK_VERSION_PACKAGE_MINOR @JAYLINK_VERSION_PACKAGE_MINOR@ + +/** Micro version number of the libjaylink package. */ +#define JAYLINK_VERSION_PACKAGE_MICRO @JAYLINK_VERSION_PACKAGE_MICRO@ + +/** Version number string of the libjaylink package. */ +#define JAYLINK_VERSION_PACKAGE_STRING "@JAYLINK_VERSION_PACKAGE@" + +/** <i>Current</i> version number of the libjaylink libtool interface. */ +#define JAYLINK_VERSION_LIBRARY_CURRENT @JAYLINK_VERSION_LIBRARY_CURRENT@ + +/** <i>Revision</i> version number of the libjaylink libtool interface. */ +#define JAYLINK_VERSION_LIBRARY_REVISION @JAYLINK_VERSION_LIBRARY_REVISION@ + +/** <i>Age</i> version number of the libjaylink libtool interface. */ +#define JAYLINK_VERSION_LIBRARY_AGE @JAYLINK_VERSION_LIBRARY_AGE@ + +/** Version number string of the libjaylink libtool interface. */ +#define JAYLINK_VERSION_LIBRARY_STRING "@JAYLINK_VERSION_LIBRARY@" + +#endif /* LIBJAYLINK_VERSION_H */ |