From 8c4b24ec5b9f5a3906775434d61fd5323d756d74 Mon Sep 17 00:00:00 2001 From: Marc Schink Date: Thu, 23 Jun 2016 22:24:20 +0200 Subject: [PATCH] Rework device discovery API Signed-off-by: Marc Schink --- Doxyfile | 1 - libjaylink/core.c | 14 ++++- libjaylink/device.c | 78 ++++++++++++++++++++++----- libjaylink/discovery.c | 114 +++++++++++++++++++++++---------------- libjaylink/libjaylink-internal.h | 5 +- libjaylink/libjaylink.h | 15 ++++-- libjaylink/list.c | 12 ++++- 7 files changed, 171 insertions(+), 68 deletions(-) diff --git a/Doxyfile b/Doxyfile index 5630dcf..c94a866 100644 --- a/Doxyfile +++ b/Doxyfile @@ -780,7 +780,6 @@ RECURSIVE = NO EXCLUDE = libjaylink/libjaylink-internal.h \ libjaylink/list.c \ - libjaylink/discovery.c \ libjaylink/transport.c \ libjaylink/buffer.c diff --git a/libjaylink/core.c b/libjaylink/core.c index 9189905..1ab4607 100644 --- a/libjaylink/core.c +++ b/libjaylink/core.c @@ -1,7 +1,7 @@ /* * This file is part of the libjaylink project. * - * Copyright (C) 2014-2015 Marc Schink + * Copyright (C) 2014-2016 Marc Schink * * 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 @@ -61,6 +61,7 @@ JAYLINK_API int jaylink_init(struct jaylink_context **ctx) } context->devs = NULL; + context->discovered_devs = NULL; /* Show error and warning messages by default. */ context->log_level = JAYLINK_LOG_LEVEL_WARNING; @@ -87,10 +88,21 @@ JAYLINK_API int jaylink_init(struct jaylink_context **ctx) */ JAYLINK_API void jaylink_exit(struct jaylink_context *ctx) { + struct list *item; + if (!ctx) return; + 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); + libusb_exit(ctx->usb_ctx); free(ctx); } diff --git a/libjaylink/device.c b/libjaylink/device.c index 9513b21..9ec00a7 100644 --- a/libjaylink/device.c +++ b/libjaylink/device.c @@ -1,7 +1,7 @@ /* * This file is part of the libjaylink project. * - * Copyright (C) 2014-2015 Marc Schink + * Copyright (C) 2014-2016 Marc Schink * * 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 @@ -107,43 +107,93 @@ static void free_device_handle(struct jaylink_device_handle *devh) free(devh); } +/** @private */ +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 a list of available devices. + * Get available devices. * * @param[in,out] ctx libjaylink context. * @param[out] devices 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_device_list(). + * caller with jaylink_free_devices(). + * @param[out] count Number of available devices on success, and undefined on + * failure. Can be NULL. * - * @return The length of the array excluding the trailing NULL-terminator, or a - * negative error code 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. + * + * @see jaylink_discovery_scan() */ -JAYLINK_API ssize_t jaylink_get_device_list(struct jaylink_context *ctx, - struct jaylink_device ***devices) +JAYLINK_API int jaylink_get_devices(struct jaylink_context *ctx, + struct jaylink_device ***devices, size_t *count) { + size_t num; + struct list *item; + struct jaylink_device **devs; + struct jaylink_device *dev; + size_t i; + if (!ctx || !devices) return JAYLINK_ERR_ARG; - return discovery_get_device_list(ctx, devices); + num = list_length(ctx->discovered_devs); + devs = allocate_device_list(num); + + if (!devs) { + 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; + devs[i] = jaylink_ref_device(dev); + item = item->next; + } + + if (count) + *count = num; + + *devices = devs; + + return JAYLINK_OK; } /** - * Free a device list. + * Free devices. * * @param[in,out] devices Array of device instances. Must be NULL-terminated. - * @param[in] unref_devices Determines whether the device instances should be - * unreferenced. + * @param[in] unref Determines whether the device instances should be + * unreferenced. + * + * @see jaylink_get_devices() */ -JAYLINK_API void jaylink_free_device_list(struct jaylink_device **devices, - bool unref_devices) +JAYLINK_API void jaylink_free_devices(struct jaylink_device **devices, + bool unref) { size_t i; if (!devices) return; - if (unref_devices) { + if (unref) { i = 0; while (devices[i]) { diff --git a/libjaylink/discovery.c b/libjaylink/discovery.c index 4a20cdf..23e6d5c 100644 --- a/libjaylink/discovery.c +++ b/libjaylink/discovery.c @@ -1,7 +1,7 @@ /* * This file is part of the libjaylink project. * - * Copyright (C) 2014-2015 Marc Schink + * Copyright (C) 2014-2016 Marc Schink * * 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 @@ -34,6 +34,7 @@ * Device discovery. */ +/** @cond PRIVATE */ /** USB Vendor ID (VID) of SEGGER products. */ #define USB_VENDOR_ID 0x1366 @@ -67,20 +68,7 @@ static const uint16_t pids[][2] = { * serial numbers are allowed with up to 10 digits. */ #define MAX_SERIAL_NUMBER_DIGITS 10 - -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; -} +/** @endcond */ static bool parse_serial_number(const char *str, uint32_t *serial_number) { @@ -246,19 +234,15 @@ static struct jaylink_device *probe_device(struct jaylink_context *ctx, return dev; } -/** @private */ -JAYLINK_PRIV ssize_t discovery_get_device_list(struct jaylink_context *ctx, - struct jaylink_device ***list) +static int discovery_usb_scan(struct jaylink_context *ctx) { ssize_t ret; - struct libusb_device **usb_devs; - struct jaylink_device **devs; + struct libusb_device **devs; struct jaylink_device *dev; - size_t num_usb_devs; - size_t num_devs; + size_t num; size_t i; - ret = libusb_get_device_list(ctx->usb_ctx, &usb_devs); + ret = libusb_get_device_list(ctx->usb_ctx, &devs); if (ret < 0) { log_err(ctx, "Failed to retrieve device list: %s.", @@ -266,37 +250,77 @@ JAYLINK_PRIV ssize_t discovery_get_device_list(struct jaylink_context *ctx, return JAYLINK_ERR; } - num_usb_devs = ret; + num = 0; - /* - * Allocate a device list with the length of the number of all found - * USB devices because they all are possible J-Link devices. - */ - devs = allocate_device_list(num_usb_devs); + for (i = 0; devs[i]; i++) { + dev = probe_device(ctx, devs[i]); - if (!devs) { - libusb_free_device_list(usb_devs, true); - log_err(ctx, "Device list malloc failed."); - return JAYLINK_ERR_MALLOC; + if (!dev) + continue; + + ctx->discovered_devs = list_prepend(ctx->discovered_devs, dev); + num++; } - num_devs = 0; + libusb_free_device_list(devs, true); + log_dbg(ctx, "Found %zu USB device(s).", num); - for (i = 0; i < num_usb_devs; i++) { - dev = probe_device(ctx, usb_devs[i]); + return JAYLINK_OK; +} - if (dev) { - devs[num_devs] = dev; - num_devs++; - } +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); } - devs[num_devs] = NULL; + ctx->discovered_devs = NULL; +} + +/** + * Scan for devices. + * + * @param[in,out] ctx libjaylink context. + * @param[in] hostifs 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 Other error conditions. + * + * @see jaylink_get_devices() + */ +JAYLINK_API int jaylink_discovery_scan(struct jaylink_context *ctx, + uint32_t hostifs) +{ + int ret; + + if (!ctx) + return JAYLINK_ERR_ARG; + + (void)hostifs; - libusb_free_device_list(usb_devs, true); - *list = devs; + clear_discovery_list(ctx); - log_dbg(ctx, "Found %zu device(s).", num_devs); + ret = discovery_usb_scan(ctx); + + if (ret != JAYLINK_OK) { + log_err(ctx, "USB device discovery failed."); + return ret; + } - return num_devs; + return JAYLINK_OK; } diff --git a/libjaylink/libjaylink-internal.h b/libjaylink/libjaylink-internal.h index 65e88f3..9d568df 100644 --- a/libjaylink/libjaylink-internal.h +++ b/libjaylink/libjaylink-internal.h @@ -1,7 +1,7 @@ /* * This file is part of the libjaylink project. * - * Copyright (C) 2014-2015 Marc Schink + * Copyright (C) 2014-2016 Marc Schink * * 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 @@ -53,6 +53,8 @@ struct jaylink_context { * 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. */ @@ -157,6 +159,7 @@ 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 cb, const void *cb_data); +JAYLINK_PRIV size_t list_length(struct list *list); JAYLINK_PRIV void list_free(struct list *list); /*--- log.c -----------------------------------------------------------------*/ diff --git a/libjaylink/libjaylink.h b/libjaylink/libjaylink.h index 461df29..71a92f3 100644 --- a/libjaylink/libjaylink.h +++ b/libjaylink/libjaylink.h @@ -1,7 +1,7 @@ /* * This file is part of the libjaylink project. * - * Copyright (C) 2014-2015 Marc Schink + * Copyright (C) 2014-2016 Marc Schink * * 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 @@ -359,10 +359,10 @@ JAYLINK_API void jaylink_exit(struct jaylink_context *ctx); /*--- device.c --------------------------------------------------------------*/ -JAYLINK_API ssize_t jaylink_get_device_list(struct jaylink_context *ctx, - struct jaylink_device ***devices); -JAYLINK_API void jaylink_free_device_list(struct jaylink_device **devices, - bool unref_devices); +JAYLINK_API int jaylink_get_devices(struct jaylink_context *ctx, + struct jaylink_device ***devices, size_t *count); +JAYLINK_API void jaylink_free_devices(struct jaylink_device **devices, + bool unref); JAYLINK_API int jaylink_device_get_host_interface( const struct jaylink_device *dev, enum jaylink_host_interface *interface); @@ -405,6 +405,11 @@ JAYLINK_API int jaylink_unregister(struct jaylink_device_handle *devh, struct jaylink_connection *connections, size_t *count, uint8_t *info, uint16_t *info_size); +/*--- discovery.c -----------------------------------------------------------*/ + +JAYLINK_API int jaylink_discovery_scan(struct jaylink_context *ctx, + uint32_t hostifs); + /*--- emucom.c --------------------------------------------------------------*/ JAYLINK_API int jaylink_emucom_read(struct jaylink_device_handle *devh, diff --git a/libjaylink/list.c b/libjaylink/list.c index a16ece5..6a1e328 100644 --- a/libjaylink/list.c +++ b/libjaylink/list.c @@ -1,7 +1,7 @@ /* * This file is part of the libjaylink project. * - * Copyright (C) 2014-2015 Marc Schink + * Copyright (C) 2014-2016 Marc Schink * * 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 @@ -88,6 +88,16 @@ JAYLINK_PRIV struct list *list_find_custom(struct list *list, return NULL; } +JAYLINK_PRIV size_t list_length(struct list *list) +{ + size_t n; + + for (n = 0; list; n++) + list = list->next; + + return n; +} + JAYLINK_PRIV void list_free(struct list *list) { struct list *tmp; -- 2.11.4.GIT