From: Aurelien Jacobs Date: Sun, 2 Feb 2014 20:47:45 +0000 (+0100) Subject: scpi: add a generic scan API and implement it in usbtmc and serial transport X-Git-Tag: libsigrok-0.3.0~155 X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=b541f8376d8fadb1377e3140c3d79304f5d680ec;p=libsigrok.git scpi: add a generic scan API and implement it in usbtmc and serial transport note that sr_usb_find_serial() is copied from the hameg-hmo driver --- diff --git a/hardware/common/scpi.c b/hardware/common/scpi.c index 2307a8b5..618ff058 100644 --- a/hardware/common/scpi.c +++ b/hardware/common/scpi.c @@ -87,6 +87,78 @@ static const struct sr_scpi_dev_inst *scpi_devs[] = { #endif }; +static GSList *sr_scpi_scan_resource(struct drv_context *drvc, + const char *resource, const char *serialcomm, + struct sr_dev_inst *(*probe_device)(struct sr_scpi_dev_inst *scpi)) +{ + struct sr_scpi_dev_inst *scpi; + struct sr_dev_inst *sdi; + + if (!(scpi = scpi_dev_inst_new(drvc, resource, serialcomm))) + return NULL; + + if (sr_scpi_open(scpi) != SR_OK) { + sr_info("Couldn't open SCPI device."); + sr_scpi_free(scpi); + return NULL; + }; + + if ((sdi = probe_device(scpi))) + return g_slist_append(NULL, sdi); + + sr_scpi_close(scpi); + sr_scpi_free(scpi); + return NULL; +} + +SR_PRIV GSList *sr_scpi_scan(struct drv_context *drvc, GSList *options, + struct sr_dev_inst *(*probe_device)(struct sr_scpi_dev_inst *scpi)) +{ + GSList *resources, *l, *d, *devices = NULL; + const char *resource = NULL; + const char *serialcomm = NULL; + gchar **res; + unsigned i; + + for (l = options; l; l = l->next) { + struct sr_config *src = l->data; + switch (src->key) { + case SR_CONF_CONN: + resource = g_variant_get_string(src->data, NULL); + break; + case SR_CONF_SERIALCOMM: + serialcomm = g_variant_get_string(src->data, NULL); + break; + } + } + + for (i = 0; i < ARRAY_SIZE(scpi_devs); i++) { + if ((resource && strcmp(resource, scpi_devs[i]->prefix)) + || !scpi_devs[i]->scan) + continue; + resources = scpi_devs[i]->scan(drvc); + for (l = resources; l; l = l->next) { + res = g_strsplit(l->data, ":", 2); + if (res[0] && (d = sr_scpi_scan_resource(drvc, res[0], + serialcomm ? serialcomm : res[1], probe_device))) + devices = g_slist_concat(devices, d); + g_strfreev(res); + } + g_slist_free_full(resources, g_free); + } + + if (!devices && resource) + devices = sr_scpi_scan_resource(drvc, resource, serialcomm, + probe_device); + + /* Tack a copy of the newly found devices onto the driver list. */ + if (devices) + drvc->instances = g_slist_concat(drvc->instances, + g_slist_copy(devices)); + + return devices; +} + SR_PRIV struct sr_scpi_dev_inst *scpi_dev_inst_new(struct drv_context *drvc, const char *resource, const char *serialcomm) { diff --git a/hardware/common/scpi_serial.c b/hardware/common/scpi_serial.c index 6e73c894..e0936c01 100644 --- a/hardware/common/scpi_serial.c +++ b/hardware/common/scpi_serial.c @@ -22,6 +22,7 @@ #include "libsigrok-internal.h" #include +#include #include #define LOG_PREFIX "scpi_serial" @@ -35,6 +36,41 @@ struct scpi_serial { size_t read; }; +static struct { + uint16_t vendor_id; + uint16_t product_id; + const char *serialcomm; +} scpi_serial_usb_ids[] = { + { 0x0403, 0xed72, "115200/8n1/flow=1" }, /* Hameg HO720 */ + { 0x0403, 0xed73, "115200/8n1/flow=1" }, /* Hameg HO730 */ +}; + +static GSList *scpi_serial_scan(struct drv_context *drvc) +{ + GSList *l, *r, *resources = NULL; + gchar *res; + unsigned i; + + (void)drvc; + + for (i = 0; i < ARRAY_SIZE(scpi_serial_usb_ids); i++) { + if ((l = sr_serial_find_usb(scpi_serial_usb_ids[i].vendor_id, + scpi_serial_usb_ids[i].product_id)) == NULL) + continue; + for (r = l; r; r = r->next) { + if (scpi_serial_usb_ids[i].serialcomm) + res = g_strdup_printf("%s:%s", (char *) r->data, + scpi_serial_usb_ids[i].serialcomm); + else + res = g_strdup(r->data); + resources = g_slist_append(resources, res); + } + g_slist_free_full(l, g_free); + } + + return resources; +} + static int scpi_serial_dev_inst_new(void *priv, struct drv_context *drvc, const char *resource, char **params, const char *serialcomm) { @@ -190,6 +226,7 @@ SR_PRIV const struct sr_scpi_dev_inst scpi_serial_dev = { .name = "serial", .prefix = "", .priv_size = sizeof(struct scpi_serial), + .scan = scpi_serial_scan, .dev_inst_new = scpi_serial_dev_inst_new, .open = scpi_serial_open, .source_add = scpi_serial_source_add, diff --git a/hardware/common/scpi_usbtmc.c b/hardware/common/scpi_usbtmc.c index 6cd79641..1ffc34ae 100644 --- a/hardware/common/scpi_usbtmc.c +++ b/hardware/common/scpi_usbtmc.c @@ -37,6 +37,29 @@ struct usbtmc_scpi { int response_bytes_read; }; +static GSList *scpi_usbtmc_scan(struct drv_context *drvc) +{ + GSList *resources = NULL; + GDir *dir; + const char *dev_name; + char *resource; + + (void)drvc; + + if (!(dir = g_dir_open("/sys/class/usbmisc/", 0, NULL))) + if (!(dir = g_dir_open("/sys/class/usb/", 0, NULL))) + return NULL; + while ((dev_name = g_dir_read_name(dir))) { + if (strncmp(dev_name, "usbtmc", 6)) + continue; + resource = g_strconcat("/dev/", dev_name, NULL); + resources = g_slist_append(resources, resource); + } + g_dir_close(dir); + + return resources; +} + static int scpi_usbtmc_dev_inst_new(void *priv, struct drv_context *drvc, const char *resource, char **params, const char *serialcomm) { @@ -190,6 +213,7 @@ SR_PRIV const struct sr_scpi_dev_inst scpi_usbtmc_dev = { .name = "USBTMC", .prefix = "/dev/usbtmc", .priv_size = sizeof(struct usbtmc_scpi), + .scan = scpi_usbtmc_scan, .dev_inst_new = scpi_usbtmc_dev_inst_new, .open = scpi_usbtmc_open, .source_add = scpi_usbtmc_source_add, diff --git a/hardware/common/serial.c b/hardware/common/serial.c index a267d6fb..32a6d23c 100644 --- a/hardware/common/serial.c +++ b/hardware/common/serial.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "libsigrok.h" #include "libsigrok-internal.h" @@ -742,3 +743,142 @@ SR_PRIV int serial_source_remove(struct sr_serial_dev_inst *serial) return SR_OK; } + +/** + * Find USB serial devices via the USB vendor ID and product ID. + * + * @param vendor_id Vendor ID of the USB device. + * @param product_id Product ID of the USB device. + * + * @return A GSList of strings containing the path of the serial device or + * NULL if no serial device is found. The returned list must be freed + * by the caller. + */ +SR_PRIV GSList *sr_serial_find_usb(uint16_t vendor_id, uint16_t product_id) +{ +#ifdef __linux__ + const gchar *usb_dev; + const char device_tree[] = "/sys/bus/usb/devices/"; + GDir *devices_dir, *device_dir; + GSList *l = NULL; + GSList *tty_devs; + GSList *matched_paths; + FILE *fd; + char tmp[5]; + gchar *vendor_path, *product_path, *path_copy; + gchar *prefix, *subdir_path, *device_path, *tty_path; + unsigned long read_vendor_id, read_product_id; + const char *file; + + l = NULL; + tty_devs = NULL; + matched_paths = NULL; + + if (!(devices_dir = g_dir_open(device_tree, 0, NULL))) + return NULL; + + /* + * Find potential candidates using the vendor ID and product ID + * and store them in matched_paths. + */ + while ((usb_dev = g_dir_read_name(devices_dir))) { + vendor_path = g_strconcat(device_tree, + usb_dev, "/idVendor", NULL); + product_path = g_strconcat(device_tree, + usb_dev, "/idProduct", NULL); + + if (!g_file_test(vendor_path, G_FILE_TEST_EXISTS) || + !g_file_test(product_path, G_FILE_TEST_EXISTS)) + goto skip_device; + + if ((fd = g_fopen(vendor_path, "r")) == NULL) + goto skip_device; + + if (fgets(tmp, sizeof(tmp), fd) == NULL) { + fclose(fd); + goto skip_device; + } + read_vendor_id = strtoul(tmp, NULL, 16); + + fclose(fd); + + if ((fd = g_fopen(product_path, "r")) == NULL) + goto skip_device; + + if (fgets(tmp, sizeof(tmp), fd) == NULL) { + fclose(fd); + goto skip_device; + } + read_product_id = strtoul(tmp, NULL, 16); + + fclose(fd); + + if (vendor_id == read_vendor_id && + product_id == read_product_id) { + path_copy = g_strdup(usb_dev); + matched_paths = g_slist_prepend(matched_paths, + path_copy); + } + +skip_device: + g_free(vendor_path); + g_free(product_path); + } + g_dir_close(devices_dir); + + /* For every matched device try to find a ttyUSBX subfolder. */ + for (l = matched_paths; l; l = l->next) { + subdir_path = NULL; + + device_path = g_strconcat(device_tree, l->data, NULL); + + if (!(device_dir = g_dir_open(device_path, 0, NULL))) { + g_free(device_path); + continue; + } + + prefix = g_strconcat(l->data, ":", NULL); + + while ((file = g_dir_read_name(device_dir))) { + if (g_str_has_prefix(file, prefix)) { + subdir_path = g_strconcat(device_path, + "/", file, NULL); + break; + } + } + g_dir_close(device_dir); + + g_free(prefix); + g_free(device_path); + + if (subdir_path) { + if (!(device_dir = g_dir_open(subdir_path, 0, NULL))) { + g_free(subdir_path); + continue; + } + g_free(subdir_path); + + while ((file = g_dir_read_name(device_dir))) { + if (g_str_has_prefix(file, "ttyUSB")) { + tty_path = g_strconcat("/dev/", + file, NULL); + sr_dbg("Found USB device %04x:%04x attached to %s.", + vendor_id, product_id, tty_path); + tty_devs = g_slist_prepend(tty_devs, + tty_path); + break; + } + } + g_dir_close(device_dir); + } + } + g_slist_free_full(matched_paths, g_free); + + return tty_devs; +#else + (void)vendor_id; + (void)product_id; + + return NULL; +#endif +} diff --git a/libsigrok-internal.h b/libsigrok-internal.h index 951d91d9..90e40342 100644 --- a/libsigrok-internal.h +++ b/libsigrok-internal.h @@ -374,6 +374,7 @@ SR_PRIV int sr_serial_extract_options(GSList *options, const char **serial_devic SR_PRIV int serial_source_add(struct sr_serial_dev_inst *serial, int events, int timeout, sr_receive_data_callback_t cb, void *cb_data); SR_PRIV int serial_source_remove(struct sr_serial_dev_inst *serial); +SR_PRIV GSList *sr_serial_find_usb(uint16_t vendor_id, uint16_t product_id); #endif /*--- hardware/common/ezusb.c -----------------------------------------------*/ @@ -438,6 +439,7 @@ struct sr_scpi_dev_inst { const char *name; const char *prefix; int priv_size; + GSList *(*scan)(struct drv_context *drvc); int (*dev_inst_new)(void *priv, struct drv_context *drvc, const char *resource, char **params, const char *serialcomm); int (*open)(void *priv); @@ -453,6 +455,8 @@ struct sr_scpi_dev_inst { void *priv; }; +SR_PRIV GSList *sr_scpi_scan(struct drv_context *drvc, GSList *options, + struct sr_dev_inst *(*probe_device)(struct sr_scpi_dev_inst *scpi)); SR_PRIV struct sr_scpi_dev_inst *scpi_dev_inst_new(struct drv_context *drvc, const char *resource, const char *serialcomm); SR_PRIV int sr_scpi_open(struct sr_scpi_dev_inst *scpi);