/* * Copyright (C) 2011-2016 - EATON * * 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 */ /*! \file scan_usb.c \brief detect NUT supported USB devices \author Frederic Bohe \author Arnaud Quette */ #include "common.h" #include "nut-scan.h" #ifdef WITH_USB #include "upsclient.h" #include "nutscan-usb.h" #include #include #include /* dynamic link library stuff */ static lt_dlhandle dl_handle = NULL; static const char *dl_error = NULL; static int (*nut_usb_close)(libusb_device_handle *dev); static int (*nut_usb_get_string_simple)(libusb_device_handle *dev, int index, char *buf, size_t buflen); /* 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); int nutscan_load_usb_library(const char *libname_path) { if (dl_handle != NULL) { /* if previous init failed */ if (dl_handle == (void *)1) { 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; } if (lt_dlinit() != 0) { fprintf(stderr, "Error initializing lt_init\n"); return 0; } dl_handle = lt_dlopen(libname_path); if (!dl_handle) { dl_error = lt_dlerror(); goto err; } *(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; } lt_dlerror(); /* Clear any existing error */ *(void **) (&nut_usb_close) = lt_dlsym(dl_handle, USB_CLOSE_SYMBOL); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(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) { goto err; } *(void **) (&nut_usb_get_device_list) = lt_dlsym(dl_handle, "libusb_get_device_list"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(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) { goto err; } *(void **) (&nut_usb_get_string_simple) = lt_dlsym(dl_handle, "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) { goto err; } *(void **) (&nut_usb_busses) = lt_dlsym(dl_handle, "usb_busses"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **)(&nut_usb_find_devices) = lt_dlsym(dl_handle, "usb_find_devices"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_usb_get_string_simple) = lt_dlsym(dl_handle, "usb_get_string_simple"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } #endif /* WITH_LIBUSB_1_0 */ return 1; err: fprintf(stderr, "Cannot load USB library (%s) : %s. USB search disabled.\n", libname_path, dl_error); dl_handle = (void *)1; lt_dlexit(); return 0; } /* end of dynamic link library stuff */ 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; for (usbdev = usb_device_id_list; usbdev->driver_name != NULL; usbdev++) { if ((usbdev->vendorID == dev_VendorID) && (usbdev->productID == dev_ProductID) ) { 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; 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 */ struct usb_device *dev; struct usb_bus *bus; #endif /* WITH_LIBUSB_1_0 */ libusb_device_handle *udev; nutscan_device_t * nut_dev = NULL; nutscan_device_t * current_nut_dev = NULL; if (!nutscan_avail_usb) { return NULL; } /* libusb base init */ /* 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 */ (*nut_usb_init)(); (*nut_usb_find_busses)(); (*nut_usb_find_devices)(); #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++) { 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 */ for (bus = (*nut_usb_busses); bus; bus = bus->next) { for (dev = bus->devices; dev; dev = dev->next) { VendorID = dev->descriptor.idVendor; ProductID = dev->descriptor.idProduct; iManufacturer = dev->descriptor.iManufacturer; iProduct = dev->descriptor.iProduct; iSerialNumber = dev->descriptor.iSerialNumber; busname = bus->dirname; #endif if ((driver_name = is_usb_device_supported(usb_device_table, VendorID, ProductID)) != NULL) { /* open the device */ #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 */ udev = (*nut_usb_open)(dev); if (!udev) { /* TOTHINK: any errno or similar to test? */ fprintf(stderr, "Failed to open device " "bus '%s',skipping: %s\n", busname, (*nut_usb_strerror)()); continue; } #endif /* get serial number */ if (iSerialNumber) { ret = (*nut_usb_get_string_simple)(udev, iSerialNumber, string, sizeof(string)); if (ret > 0) { serialnumber = strdup(str_rtrim(string, ' ')); 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"); } } } /* get product name */ if (iProduct) { ret = (*nut_usb_get_string_simple)(udev, iProduct, string, sizeof(string)); if (ret > 0) { device_name = strdup(str_rtrim(string, ' ')); 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"); } } } /* get vendor name */ if (iManufacturer) { ret = (*nut_usb_get_string_simple)(udev, iManufacturer, string, sizeof(string)); if (ret > 0) { vendor_name = strdup(str_rtrim(string, ' ')); 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"); } } } nut_dev = nutscan_new_device(); if (nut_dev == NULL) { fprintf(stderr, "Memory allocation error\n"); nutscan_free_device(current_nut_dev); free(serialnumber); free(device_name); free(vendor_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 */ return NULL; } nut_dev->type = TYPE_USB; if (driver_name) { nut_dev->driver = strdup(driver_name); } nut_dev->port = strdup("auto"); 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) { nutscan_add_option_to_device(nut_dev, "product", device_name); free(device_name); device_name = NULL; } if (serialnumber) { nutscan_add_option_to_device(nut_dev, "serial", serialnumber); free(serialnumber); serialnumber = NULL; } if (vendor_name) { nutscan_add_option_to_device(nut_dev, "vendor", vendor_name); free(vendor_name); vendor_name = NULL; } nutscan_add_option_to_device(nut_dev, "bus", busname); current_nut_dev = nutscan_add_device_to_device( current_nut_dev, nut_dev); memset (string, 0, sizeof(string)); (*nut_usb_close)(udev); } #if WITH_LIBUSB_0_1 } } #else /* not WITH_LIBUSB_0_1 */ free(busname); } (*nut_usb_free_device_list)(devlist, 1); (*nut_usb_exit)(NULL); #endif /* WITH_LIBUSB_0_1 */ return nutscan_rewind_device(current_nut_dev); } #else /* not WITH_USB */ nutscan_device_t * nutscan_scan_usb() { return NULL; } #endif /* WITH_USB */