nut/tools/nut-scanner/scan_usb.c

466 lines
12 KiB
C
Raw Permalink Normal View History

2016-07-18 00:11:41 +00:00
/*
2022-06-29 10:37:36 +00:00
* Copyright (C) 2011-2016 - EATON
2011-09-29 18:14:46 +00:00
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
2016-07-18 00:11:41 +00:00
/*! \file scan_usb.c
\brief detect NUT supported USB devices
\author Frederic Bohe <fredericbohe@eaton.com>
2022-06-29 10:37:36 +00:00
\author Arnaud Quette <ArnaudQuette@Eaton.com>
2016-07-18 00:11:41 +00:00
*/
2011-09-29 18:14:46 +00:00
#include "common.h"
2012-01-24 10:22:33 +00:00
#include "nut-scan.h"
#ifdef WITH_USB
2011-09-29 18:14:46 +00:00
#include "upsclient.h"
#include "nutscan-usb.h"
#include <stdio.h>
#include <string.h>
2012-01-24 10:22:33 +00:00
#include <ltdl.h>
/* dynamic link library stuff */
static lt_dlhandle dl_handle = NULL;
static const char *dl_error = NULL;
2022-06-29 10:37:36 +00:00
static int (*nut_usb_close)(libusb_device_handle *dev);
static int (*nut_usb_get_string_simple)(libusb_device_handle *dev, int index,
2012-01-24 10:22:33 +00:00
char *buf, size_t buflen);
2022-06-29 10:37:36 +00:00
/* Compatibility layer between libusb 0.1 and 1.0 */
#if WITH_LIBUSB_1_0
#define USB_INIT_SYMBOL "libusb_init"
#define USB_OPEN_SYMBOL "libusb_open"
#define USB_CLOSE_SYMBOL "libusb_close"
#define USB_STRERROR_SYMBOL "libusb_strerror"
static int (*nut_usb_open)(libusb_device *dev, libusb_device_handle **handle);
static int (*nut_usb_init)(libusb_context **ctx);
static void (*nut_usb_exit)(libusb_context *ctx);
static char * (*nut_usb_strerror)(enum libusb_error errcode);
static ssize_t (*nut_usb_get_device_list)(libusb_context *ctx, libusb_device ***list);
static void (*nut_usb_free_device_list)(libusb_device **list, int unref_devices);
static uint8_t (*nut_usb_get_bus_number)(libusb_device *dev);
static int (*nut_usb_get_device_descriptor)(libusb_device *dev,
struct libusb_device_descriptor *desc);
#else
#define USB_INIT_SYMBOL "usb_init"
#define USB_OPEN_SYMBOL "usb_open"
#define USB_CLOSE_SYMBOL "usb_close"
#define USB_STRERROR_SYMBOL "usb_strerror"
static libusb_device_handle * (*nut_usb_open)(struct usb_device *dev);
static void (*nut_usb_init)(void);
static int (*nut_usb_find_busses)(void);
static struct usb_bus * (*nut_usb_busses);
static int (*nut_usb_find_devices)(void);
static char * (*nut_usb_strerror)(void);
#endif
/* return 0 on error; visible externally */
int nutscan_load_usb_library(const char *libname_path);
2016-07-18 00:11:41 +00:00
int nutscan_load_usb_library(const char *libname_path)
2012-01-24 10:22:33 +00:00
{
2022-06-29 10:37:36 +00:00
if (dl_handle != NULL) {
2016-07-18 00:11:41 +00:00
/* if previous init failed */
2022-06-29 10:37:36 +00:00
if (dl_handle == (void *)1) {
2016-07-18 00:11:41 +00:00
return 0;
}
/* init has already been done */
return 1;
}
if (libname_path == NULL) {
fprintf(stderr, "USB library not found. USB search disabled.\n");
return 0;
}
2012-01-24 10:22:33 +00:00
2022-06-29 10:37:36 +00:00
if (lt_dlinit() != 0) {
2012-01-24 10:22:33 +00:00
fprintf(stderr, "Error initializing lt_init\n");
return 0;
}
2016-07-18 00:11:41 +00:00
dl_handle = lt_dlopen(libname_path);
if (!dl_handle) {
dl_error = lt_dlerror();
goto err;
}
2022-06-29 10:37:36 +00:00
*(void **) (&nut_usb_init) = lt_dlsym(dl_handle, USB_INIT_SYMBOL);
if ((dl_error = lt_dlerror()) != NULL) {
goto err;
}
*(void **) (&nut_usb_open) = lt_dlsym(dl_handle, USB_OPEN_SYMBOL);
if ((dl_error = lt_dlerror()) != NULL) {
goto err;
}
2016-07-18 00:11:41 +00:00
lt_dlerror(); /* Clear any existing error */
2022-06-29 10:37:36 +00:00
*(void **) (&nut_usb_close) = lt_dlsym(dl_handle, USB_CLOSE_SYMBOL);
if ((dl_error = lt_dlerror()) != NULL) {
2016-07-18 00:11:41 +00:00
goto err;
}
2012-01-24 10:22:33 +00:00
2022-06-29 10:37:36 +00:00
*(void **) (&nut_usb_strerror) = lt_dlsym(dl_handle, USB_STRERROR_SYMBOL);
if ((dl_error = lt_dlerror()) != NULL) {
goto err;
}
#if WITH_LIBUSB_1_0
*(void **) (&nut_usb_exit) = lt_dlsym(dl_handle, "libusb_exit");
if ((dl_error = lt_dlerror()) != NULL) {
2016-07-18 00:11:41 +00:00
goto err;
}
2012-01-24 10:22:33 +00:00
2022-06-29 10:37:36 +00:00
*(void **) (&nut_usb_get_device_list) = lt_dlsym(dl_handle, "libusb_get_device_list");
if ((dl_error = lt_dlerror()) != NULL) {
2016-07-18 00:11:41 +00:00
goto err;
}
2012-01-24 10:22:33 +00:00
2022-06-29 10:37:36 +00:00
*(void **) (&nut_usb_free_device_list) = lt_dlsym(dl_handle, "libusb_free_device_list");
if ((dl_error = lt_dlerror()) != NULL) {
goto err;
}
*(void **) (&nut_usb_get_bus_number) = lt_dlsym(dl_handle, "libusb_get_bus_number");
if ((dl_error = lt_dlerror()) != NULL) {
goto err;
}
*(void **) (&nut_usb_get_device_descriptor) = lt_dlsym(dl_handle, "libusb_get_device_descriptor");
if ((dl_error = lt_dlerror()) != NULL) {
2016-07-18 00:11:41 +00:00
goto err;
}
2012-01-24 10:22:33 +00:00
2016-07-18 00:11:41 +00:00
*(void **) (&nut_usb_get_string_simple) = lt_dlsym(dl_handle,
2022-06-29 10:37:36 +00:00
"libusb_get_string_descriptor_ascii");
if ((dl_error = lt_dlerror()) != NULL) {
goto err;
}
#else /* for libusb 0.1 */
*(void **) (&nut_usb_find_busses) = lt_dlsym(dl_handle, "usb_find_busses");
if ((dl_error = lt_dlerror()) != NULL) {
2016-07-18 00:11:41 +00:00
goto err;
}
2012-01-24 10:22:33 +00:00
2016-07-18 00:11:41 +00:00
*(void **) (&nut_usb_busses) = lt_dlsym(dl_handle, "usb_busses");
2022-06-29 10:37:36 +00:00
if ((dl_error = lt_dlerror()) != NULL) {
2016-07-18 00:11:41 +00:00
goto err;
}
2012-01-24 10:22:33 +00:00
2022-06-29 10:37:36 +00:00
*(void **)(&nut_usb_find_devices) = lt_dlsym(dl_handle, "usb_find_devices");
if ((dl_error = lt_dlerror()) != NULL) {
2016-07-18 00:11:41 +00:00
goto err;
}
2012-01-24 10:22:33 +00:00
2022-06-29 10:37:36 +00:00
*(void **) (&nut_usb_get_string_simple) = lt_dlsym(dl_handle,
"usb_get_string_simple");
if ((dl_error = lt_dlerror()) != NULL) {
2016-07-18 00:11:41 +00:00
goto err;
}
2022-06-29 10:37:36 +00:00
#endif /* WITH_LIBUSB_1_0 */
2012-01-24 10:22:33 +00:00
2016-07-18 00:11:41 +00:00
return 1;
2022-06-29 10:37:36 +00:00
2012-01-24 10:22:33 +00:00
err:
2016-07-18 00:11:41 +00:00
fprintf(stderr, "Cannot load USB library (%s) : %s. USB search disabled.\n", libname_path, dl_error);
dl_handle = (void *)1;
2012-06-01 13:55:19 +00:00
lt_dlexit();
2016-07-18 00:11:41 +00:00
return 0;
2012-01-24 10:22:33 +00:00
}
/* end of dynamic link library stuff */
2011-09-29 18:14:46 +00:00
static char* is_usb_device_supported(usb_device_id_t *usb_device_id_list,
int dev_VendorID, int dev_ProductID)
{
usb_device_id_t *usbdev;
2022-06-29 10:37:36 +00:00
for (usbdev = usb_device_id_list; usbdev->driver_name != NULL; usbdev++) {
if ((usbdev->vendorID == dev_VendorID)
&& (usbdev->productID == dev_ProductID)
) {
2011-09-29 18:14:46 +00:00
return usbdev->driver_name;
}
}
return NULL;
}
/* return NULL if error */
nutscan_device_t * nutscan_scan_usb()
{
int ret;
char string[256];
char *driver_name = NULL;
char *serialnumber = NULL;
char *device_name = NULL;
char *vendor_name = NULL;
2022-06-29 10:37:36 +00:00
uint8_t iManufacturer = 0, iProduct = 0, iSerialNumber = 0;
uint16_t VendorID;
uint16_t ProductID;
char *busname;
#if WITH_LIBUSB_1_0
libusb_device *dev;
libusb_device **devlist;
uint8_t bus;
#else /* => WITH_LIBUSB_0_1 */
2011-09-29 18:14:46 +00:00
struct usb_device *dev;
struct usb_bus *bus;
2022-06-29 10:37:36 +00:00
#endif /* WITH_LIBUSB_1_0 */
libusb_device_handle *udev;
2011-09-29 18:14:46 +00:00
nutscan_device_t * nut_dev = NULL;
nutscan_device_t * current_nut_dev = NULL;
2022-06-29 10:37:36 +00:00
if (!nutscan_avail_usb) {
return NULL;
}
2012-01-24 10:22:33 +00:00
2011-09-29 18:14:46 +00:00
/* libusb base init */
2022-06-29 10:37:36 +00:00
/* Initialize Libusb */
#if WITH_LIBUSB_1_0
if ((*nut_usb_init)(NULL) < 0) {
(*nut_usb_exit)(NULL);
fatal_with_errno(EXIT_FAILURE, "Failed to init libusb 1.0");
}
#else /* => WITH_LIBUSB_0_1 */
2012-01-24 10:22:33 +00:00
(*nut_usb_init)();
(*nut_usb_find_busses)();
(*nut_usb_find_devices)();
2022-06-29 10:37:36 +00:00
#endif /* WITH_LIBUSB_1_0 */
#if WITH_LIBUSB_1_0
ssize_t devcount = 0;
struct libusb_device_descriptor dev_desc;
int i;
devcount = (*nut_usb_get_device_list)(NULL, &devlist);
if (devcount <= 0) {
(*nut_usb_exit)(NULL);
fatal_with_errno(EXIT_FAILURE, "No USB device found");
}
for (i = 0; i < devcount; i++) {
2011-09-29 18:14:46 +00:00
2022-06-29 10:37:36 +00:00
dev = devlist[i];
(*nut_usb_get_device_descriptor)(dev, &dev_desc);
VendorID = dev_desc.idVendor;
ProductID = dev_desc.idProduct;
iManufacturer = dev_desc.iManufacturer;
iProduct = dev_desc.iProduct;
iSerialNumber = dev_desc.iSerialNumber;
bus = (*nut_usb_get_bus_number)(dev);
busname = (char *)malloc(4);
if (busname == NULL) {
(*nut_usb_free_device_list)(devlist, 1);
(*nut_usb_exit)(NULL);
fatal_with_errno(EXIT_FAILURE, "Out of memory");
}
snprintf(busname, 4, "%03d", bus);
#else /* => WITH_LIBUSB_0_1 */
2012-01-24 10:22:33 +00:00
for (bus = (*nut_usb_busses); bus; bus = bus->next) {
2011-09-29 18:14:46 +00:00
for (dev = bus->devices; dev; dev = dev->next) {
2022-06-29 10:37:36 +00:00
VendorID = dev->descriptor.idVendor;
ProductID = dev->descriptor.idProduct;
iManufacturer = dev->descriptor.iManufacturer;
iProduct = dev->descriptor.iProduct;
iSerialNumber = dev->descriptor.iSerialNumber;
busname = bus->dirname;
#endif
2011-09-29 18:14:46 +00:00
if ((driver_name =
is_usb_device_supported(usb_device_table,
2022-06-29 10:37:36 +00:00
VendorID, ProductID)) != NULL) {
2011-09-29 18:14:46 +00:00
/* open the device */
2022-06-29 10:37:36 +00:00
#if WITH_LIBUSB_1_0
ret = (*nut_usb_open)(dev, &udev);
if (!udev || ret != LIBUSB_SUCCESS) {
fprintf(stderr,"Failed to open device "
"bus '%s', skipping: %s\n",
busname,
(*nut_usb_strerror)(ret));
/* Note: closing is not applicable
* it seems, and can even segfault
* (even though an udev is not NULL
* when e.g. permissions problem)
*/
free (busname);
continue;
}
#else /* => WITH_LIBUSB_0_1 */
2012-01-24 10:22:33 +00:00
udev = (*nut_usb_open)(dev);
2011-09-29 18:14:46 +00:00
if (!udev) {
2022-06-29 10:37:36 +00:00
/* TOTHINK: any errno or similar to test? */
fprintf(stderr, "Failed to open device "
"bus '%s',skipping: %s\n",
busname,
2012-01-24 10:22:33 +00:00
(*nut_usb_strerror)());
2011-09-29 18:14:46 +00:00
continue;
}
2022-06-29 10:37:36 +00:00
#endif
2011-09-29 18:14:46 +00:00
/* get serial number */
2022-06-29 10:37:36 +00:00
if (iSerialNumber) {
2012-01-24 10:22:33 +00:00
ret = (*nut_usb_get_string_simple)(udev,
2022-06-29 10:37:36 +00:00
iSerialNumber, string, sizeof(string));
2011-09-29 18:14:46 +00:00
if (ret > 0) {
2016-07-18 00:11:41 +00:00
serialnumber = strdup(str_rtrim(string, ' '));
2022-06-29 10:37:36 +00:00
if (serialnumber == NULL) {
(*nut_usb_close)(udev);
#if WITH_LIBUSB_1_0
free(busname);
(*nut_usb_free_device_list)(devlist, 1);
(*nut_usb_exit)(NULL);
#endif /* WITH_LIBUSB_1_0 */
fatal_with_errno(EXIT_FAILURE, "Out of memory");
}
2011-09-29 18:14:46 +00:00
}
}
2022-06-29 10:37:36 +00:00
2011-09-29 18:14:46 +00:00
/* get product name */
2022-06-29 10:37:36 +00:00
if (iProduct) {
2012-01-24 10:22:33 +00:00
ret = (*nut_usb_get_string_simple)(udev,
2022-06-29 10:37:36 +00:00
iProduct, string, sizeof(string));
2011-09-29 18:14:46 +00:00
if (ret > 0) {
2016-07-18 00:11:41 +00:00
device_name = strdup(str_rtrim(string, ' '));
2022-06-29 10:37:36 +00:00
if (device_name == NULL) {
free(serialnumber);
(*nut_usb_close)(udev);
#if WITH_LIBUSB_1_0
free(busname);
(*nut_usb_free_device_list)(devlist, 1);
(*nut_usb_exit)(NULL);
#endif /* WITH_LIBUSB_1_0 */
fatal_with_errno(EXIT_FAILURE, "Out of memory");
}
2011-09-29 18:14:46 +00:00
}
}
/* get vendor name */
2022-06-29 10:37:36 +00:00
if (iManufacturer) {
2012-01-24 10:22:33 +00:00
ret = (*nut_usb_get_string_simple)(udev,
2022-06-29 10:37:36 +00:00
iManufacturer, string, sizeof(string));
2011-09-29 18:14:46 +00:00
if (ret > 0) {
2016-07-18 00:11:41 +00:00
vendor_name = strdup(str_rtrim(string, ' '));
2022-06-29 10:37:36 +00:00
if (vendor_name == NULL) {
free(serialnumber);
free(device_name);
(*nut_usb_close)(udev);
#if WITH_LIBUSB_1_0
free(busname);
(*nut_usb_free_device_list)(devlist, 1);
(*nut_usb_exit)(NULL);
#endif /* WITH_LIBUSB_1_0 */
fatal_with_errno(EXIT_FAILURE, "Out of memory");
}
2011-09-29 18:14:46 +00:00
}
}
nut_dev = nutscan_new_device();
2022-06-29 10:37:36 +00:00
if (nut_dev == NULL) {
fprintf(stderr,
"Memory allocation error\n");
2011-09-29 18:14:46 +00:00
nutscan_free_device(current_nut_dev);
free(serialnumber);
free(device_name);
free(vendor_name);
2022-06-29 10:37:36 +00:00
(*nut_usb_close)(udev);
#if WITH_LIBUSB_1_0
free(busname);
(*nut_usb_free_device_list)(devlist, 1);
(*nut_usb_exit)(NULL);
#endif /* WITH_LIBUSB_1_0 */
2011-09-29 18:14:46 +00:00
return NULL;
}
nut_dev->type = TYPE_USB;
2022-06-29 10:37:36 +00:00
if (driver_name) {
2011-09-29 18:14:46 +00:00
nut_dev->driver = strdup(driver_name);
}
nut_dev->port = strdup("auto");
2022-06-29 10:37:36 +00:00
sprintf(string, "%04X", VendorID);
nutscan_add_option_to_device(nut_dev,
"vendorid",
string);
sprintf(string, "%04X", ProductID);
nutscan_add_option_to_device(nut_dev,
"productid",
string);
if (device_name) {
2011-09-29 18:14:46 +00:00
nutscan_add_option_to_device(nut_dev,
2022-06-29 10:37:36 +00:00
"product",
device_name);
2011-09-29 18:14:46 +00:00
free(device_name);
2022-06-29 10:37:36 +00:00
device_name = NULL;
2011-09-29 18:14:46 +00:00
}
2022-06-29 10:37:36 +00:00
if (serialnumber) {
2011-09-29 18:14:46 +00:00
nutscan_add_option_to_device(nut_dev,
2022-06-29 10:37:36 +00:00
"serial",
serialnumber);
2011-09-29 18:14:46 +00:00
free(serialnumber);
2022-06-29 10:37:36 +00:00
serialnumber = NULL;
2011-09-29 18:14:46 +00:00
}
2022-06-29 10:37:36 +00:00
if (vendor_name) {
2011-09-29 18:14:46 +00:00
nutscan_add_option_to_device(nut_dev,
2022-06-29 10:37:36 +00:00
"vendor",
vendor_name);
2011-09-29 18:14:46 +00:00
free(vendor_name);
2022-06-29 10:37:36 +00:00
vendor_name = NULL;
2011-09-29 18:14:46 +00:00
}
2022-06-29 10:37:36 +00:00
nutscan_add_option_to_device(nut_dev,
"bus",
busname);
2011-09-29 18:14:46 +00:00
current_nut_dev = nutscan_add_device_to_device(
2022-06-29 10:37:36 +00:00
current_nut_dev,
nut_dev);
2011-09-29 18:14:46 +00:00
memset (string, 0, sizeof(string));
2012-01-24 10:22:33 +00:00
(*nut_usb_close)(udev);
2011-09-29 18:14:46 +00:00
}
2022-06-29 10:37:36 +00:00
#if WITH_LIBUSB_0_1
2011-09-29 18:14:46 +00:00
}
}
2022-06-29 10:37:36 +00:00
#else /* not WITH_LIBUSB_0_1 */
free(busname);
}
(*nut_usb_free_device_list)(devlist, 1);
(*nut_usb_exit)(NULL);
#endif /* WITH_LIBUSB_0_1 */
2011-09-29 18:14:46 +00:00
2013-11-24 15:00:12 +00:00
return nutscan_rewind_device(current_nut_dev);
2011-09-29 18:14:46 +00:00
}
2022-06-29 10:37:36 +00:00
#else /* not WITH_USB */
2012-01-24 10:22:33 +00:00
nutscan_device_t * nutscan_scan_usb()
{
return NULL;
}
#endif /* WITH_USB */