From d1c1e56bd154c5c53d849fe9941e36ba09633e17 Mon Sep 17 00:00:00 2001 From: Johan Date: Fri, 22 Nov 2013 14:42:02 +0100 Subject: [PATCH 1/7] hidapi added as dependency library Former-commit-id: 067d8ed87d0e416fb8518e05344597a00fc263c2 --- CMakeLists.txt | 13 +- CrossCompileHowto.txt | 2 +- cmake/FindUDev.cmake | 53 + dependencies/build/CMakeLists.txt | 1 + dependencies/build/hidapi/.gitignore | 8 + dependencies/build/hidapi/CMakeLists.txt | 18 + dependencies/build/hidapi/hid-libusb.c | 1427 ++++++++++++++++++++++ dependencies/hidapi-master-20131122.zip | Bin 0 -> 122317 bytes dependencies/include/hidapi/hidapi.h | 387 ++++++ libsrc/hyperion/CMakeLists.txt | 14 +- libsrc/protoserver/CMakeLists.txt | 3 + src/hyperion-remote/CMakeLists.txt | 4 + 12 files changed, 1915 insertions(+), 15 deletions(-) create mode 100644 cmake/FindUDev.cmake create mode 100644 dependencies/build/hidapi/.gitignore create mode 100644 dependencies/build/hidapi/CMakeLists.txt create mode 100644 dependencies/build/hidapi/hid-libusb.c create mode 100644 dependencies/hidapi-master-20131122.zip create mode 100644 dependencies/include/hidapi/hidapi.h diff --git a/CMakeLists.txt b/CMakeLists.txt index eaf2690b..16f73167 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,5 @@ -project(hyperion) +# Define the main-project name +project(Hyperion) # define the minimum cmake version (as required by cmake) cmake_minimum_required(VERSION 2.8) @@ -15,9 +16,6 @@ message(STATUS "ENABLE_DISPMANX = " ${ENABLE_DISPMANX}) configure_file ("${PROJECT_SOURCE_DIR}/HyperionConfig.h.in" "${PROJECT_BINARY_DIR}/HyperionConfig.h") include_directories("${PROJECT_BINARY_DIR}") -# Define the main-project name -project(Hyperion) - # Add project specific cmake modules (find, etc) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) # Add specific cmake modules to find qt4 (default version finds first available QT which might not be qt4) @@ -45,13 +43,6 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wall") # Configure the use of QT4 find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork REQUIRED QUIET) -# add protocol buffers -find_package(Protobuf REQUIRED) - -#add libusb and pthreads -find_package(libusb-1.0 REQUIRED) -find_package(Threads REQUIRED) - #SET(QT_DONT_USE_QTGUI TRUE) #SET(QT_USE_QTCONSOLE TRUE) include(${QT_USE_FILE}) diff --git a/CrossCompileHowto.txt b/CrossCompileHowto.txt index 5d5150f5..aed7c627 100644 --- a/CrossCompileHowto.txt +++ b/CrossCompileHowto.txt @@ -1,6 +1,6 @@ ON RASPBERRY -------------- -sudo apt-get install libprotobuf-dev libQt4-dev libusb-1.0-0-dev rsync +sudo apt-get install libprotobuf-dev libQt4-dev libusb-1.0-0-dev libudev-dev rsync ON HOST --------- diff --git a/cmake/FindUDev.cmake b/cmake/FindUDev.cmake new file mode 100644 index 00000000..866608dc --- /dev/null +++ b/cmake/FindUDev.cmake @@ -0,0 +1,53 @@ +# razor-de: Configure libudev environment +# +# UDEV_FOUND - system has a libudev +# UDEV_INCLUDE_DIR - where to find header files +# UDEV_LIBRARIES - the libraries to link against udev +# UDEV_STABLE - it's true when is the version greater or equals to 143 - version when the libudev was stabilized in its API +# +# copyright (c) 2011 Petr Vanek +# Redistribution and use is allowed according to the terms of the BSD license. +# + +FIND_PATH( + UDEV_INCLUDE_DIR + libudev.h + /usr/include + /usr/local/include + ${UDEV_PATH_INCLUDES} +) + +FIND_LIBRARY( + UDEV_LIBRARIES + NAMES udev libudev + PATHS + /usr/lib${LIB_SUFFIX} + /usr/local/lib${LIB_SUFFIX} + ${UDEV_PATH_LIB} +) + +IF (UDEV_LIBRARIES AND UDEV_INCLUDE_DIR) + SET(UDEV_FOUND "YES") + execute_process(COMMAND pkg-config --atleast-version=143 libudev RESULT_VARIABLE UDEV_STABLE) + # retvale is 0 of the condition is "true" so we need to negate the value... + if (UDEV_STABLE) + set(UDEV_STABLE 0) + else (UDEV_STABLE) + set(UDEV_STABLE 1) + endif (UDEV_STABLE) + message(STATUS "libudev stable: ${UDEV_STABLE}") +ENDIF (UDEV_LIBRARIES AND UDEV_INCLUDE_DIR) + +IF (UDEV_FOUND) + MESSAGE(STATUS "Found UDev: ${UDEV_LIBRARIES}") + MESSAGE(STATUS " include: ${UDEV_INCLUDE_DIR}") +ELSE (UDEV_FOUND) + MESSAGE(STATUS "UDev not found.") + MESSAGE(STATUS "UDev: You can specify includes: -DUDEV_PATH_INCLUDES=/opt/udev/include") + MESSAGE(STATUS " currently found includes: ${UDEV_INCLUDE_DIR}") + MESSAGE(STATUS "UDev: You can specify libs: -DUDEV_PATH_LIB=/opt/udev/lib") + MESSAGE(STATUS " currently found libs: ${UDEV_LIBRARIES}") + IF (UDev_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find UDev library") + ENDIF (UDev_FIND_REQUIRED) +ENDIF (UDEV_FOUND) diff --git a/dependencies/build/CMakeLists.txt b/dependencies/build/CMakeLists.txt index 8edf5773..88f6ca5f 100644 --- a/dependencies/build/CMakeLists.txt +++ b/dependencies/build/CMakeLists.txt @@ -2,3 +2,4 @@ add_subdirectory(jsoncpp) add_subdirectory(getoptPlusPlus) add_subdirectory(serial) +add_subdirectory(hidapi) diff --git a/dependencies/build/hidapi/.gitignore b/dependencies/build/hidapi/.gitignore new file mode 100644 index 00000000..67460db9 --- /dev/null +++ b/dependencies/build/hidapi/.gitignore @@ -0,0 +1,8 @@ +*.o +*.so +*.la +*.lo +*.a +.libs +.deps +hidtest-libusb diff --git a/dependencies/build/hidapi/CMakeLists.txt b/dependencies/build/hidapi/CMakeLists.txt new file mode 100644 index 00000000..8aa655d3 --- /dev/null +++ b/dependencies/build/hidapi/CMakeLists.txt @@ -0,0 +1,18 @@ +project(hidapi) + +#add libusb and pthreads +find_package(UDev REQUIRED) +find_package(libusb-1.0 REQUIRED) +find_package(Threads REQUIRED) + +include_directories( + ../../include/hidapi + ${UDEV_INCLUDE_DIR} + ${LIBUSB_1_INCLUDE_DIRS}) + +add_library(hidapi-libusb hid-libusb.c) + +target_link_libraries(hidapi-libusb + ${LIBUSB_1_LIBRARIES} #apt-get install libusb-1.0-0-dev + ${CMAKE_THREAD_LIBS_INIT} + ${UDEV_LIBRARIES}) # apt-get install libudev-dev diff --git a/dependencies/build/hidapi/hid-libusb.c b/dependencies/build/hidapi/hid-libusb.c new file mode 100644 index 00000000..6c1d2471 --- /dev/null +++ b/dependencies/build/hidapi/hid-libusb.c @@ -0,0 +1,1427 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + Linux Version - 6/2/2010 + Libusb Version - 8/13/2010 + FreeBSD Version - 11/1/2011 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +#define _GNU_SOURCE /* needed for wcsdup() before glibc 2.10 */ + +/* C */ +#include +#include +#include +#include +#include +#include + +/* Unix */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* GNU / LibUSB */ +#include "libusb.h" +#include "iconv.h" + +#include "hidapi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef DEBUG_PRINTF +#define LOG(...) fprintf(stderr, __VA_ARGS__) +#else +#define LOG(...) do {} while (0) +#endif + +#ifndef __FreeBSD__ +#define DETACH_KERNEL_DRIVER +#endif + +/* Uncomment to enable the retrieval of Usage and Usage Page in +hid_enumerate(). Warning, on platforms different from FreeBSD +this is very invasive as it requires the detach +and re-attach of the kernel driver. See comments inside hid_enumerate(). +libusb HIDAPI programs are encouraged to use the interface number +instead to differentiate between interfaces on a composite HID device. */ +/*#define INVASIVE_GET_USAGE*/ + +/* Linked List of input reports received from the device. */ +struct input_report { + uint8_t *data; + size_t len; + struct input_report *next; +}; + + +struct hid_device_ { + /* Handle to the actual device. */ + libusb_device_handle *device_handle; + + /* Endpoint information */ + int input_endpoint; + int output_endpoint; + int input_ep_max_packet_size; + + /* The interface number of the HID */ + int interface; + + /* Indexes of Strings */ + int manufacturer_index; + int product_index; + int serial_index; + + /* Whether blocking reads are used */ + int blocking; /* boolean */ + + /* Read thread objects */ + pthread_t thread; + pthread_mutex_t mutex; /* Protects input_reports */ + pthread_cond_t condition; + pthread_barrier_t barrier; /* Ensures correct startup sequence */ + int shutdown_thread; + int cancelled; + struct libusb_transfer *transfer; + + /* List of received input reports. */ + struct input_report *input_reports; +}; + +static libusb_context *usb_context = NULL; + +uint16_t get_usb_code_for_current_locale(void); +static int return_data(hid_device *dev, unsigned char *data, size_t length); + +static hid_device *new_hid_device(void) +{ + hid_device *dev = calloc(1, sizeof(hid_device)); + dev->blocking = 1; + + pthread_mutex_init(&dev->mutex, NULL); + pthread_cond_init(&dev->condition, NULL); + pthread_barrier_init(&dev->barrier, NULL, 2); + + return dev; +} + +static void free_hid_device(hid_device *dev) +{ + /* Clean up the thread objects */ + pthread_barrier_destroy(&dev->barrier); + pthread_cond_destroy(&dev->condition); + pthread_mutex_destroy(&dev->mutex); + + /* Free the device itself */ + free(dev); +} + +#if 0 +/*TODO: Implement this funciton on hidapi/libusb.. */ +static void register_error(hid_device *device, const char *op) +{ + +} +#endif + +#ifdef INVASIVE_GET_USAGE +/* Get bytes from a HID Report Descriptor. + Only call with a num_bytes of 0, 1, 2, or 4. */ +static uint32_t get_bytes(uint8_t *rpt, size_t len, size_t num_bytes, size_t cur) +{ + /* Return if there aren't enough bytes. */ + if (cur + num_bytes >= len) + return 0; + + if (num_bytes == 0) + return 0; + else if (num_bytes == 1) { + return rpt[cur+1]; + } + else if (num_bytes == 2) { + return (rpt[cur+2] * 256 + rpt[cur+1]); + } + else if (num_bytes == 4) { + return (rpt[cur+4] * 0x01000000 + + rpt[cur+3] * 0x00010000 + + rpt[cur+2] * 0x00000100 + + rpt[cur+1] * 0x00000001); + } + else + return 0; +} + +/* Retrieves the device's Usage Page and Usage from the report + descriptor. The algorithm is simple, as it just returns the first + Usage and Usage Page that it finds in the descriptor. + The return value is 0 on success and -1 on failure. */ +static int get_usage(uint8_t *report_descriptor, size_t size, + unsigned short *usage_page, unsigned short *usage) +{ + unsigned int i = 0; + int size_code; + int data_len, key_size; + int usage_found = 0, usage_page_found = 0; + + while (i < size) { + int key = report_descriptor[i]; + int key_cmd = key & 0xfc; + + //printf("key: %02hhx\n", key); + + if ((key & 0xf0) == 0xf0) { + /* This is a Long Item. The next byte contains the + length of the data section (value) for this key. + See the HID specification, version 1.11, section + 6.2.2.3, titled "Long Items." */ + if (i+1 < size) + data_len = report_descriptor[i+1]; + else + data_len = 0; /* malformed report */ + key_size = 3; + } + else { + /* This is a Short Item. The bottom two bits of the + key contain the size code for the data section + (value) for this key. Refer to the HID + specification, version 1.11, section 6.2.2.2, + titled "Short Items." */ + size_code = key & 0x3; + switch (size_code) { + case 0: + case 1: + case 2: + data_len = size_code; + break; + case 3: + data_len = 4; + break; + default: + /* Can't ever happen since size_code is & 0x3 */ + data_len = 0; + break; + }; + key_size = 1; + } + + if (key_cmd == 0x4) { + *usage_page = get_bytes(report_descriptor, size, data_len, i); + usage_page_found = 1; + //printf("Usage Page: %x\n", (uint32_t)*usage_page); + } + if (key_cmd == 0x8) { + *usage = get_bytes(report_descriptor, size, data_len, i); + usage_found = 1; + //printf("Usage: %x\n", (uint32_t)*usage); + } + + if (usage_page_found && usage_found) + return 0; /* success */ + + /* Skip over this key and it's associated data */ + i += data_len + key_size; + } + + return -1; /* failure */ +} +#endif /* INVASIVE_GET_USAGE */ + +#ifdef __FreeBSD__ +/* The FreeBSD version of libusb doesn't have this funciton. In mainline + libusb, it's inlined in libusb.h. This function will bear a striking + resemblence to that one, because there's about one way to code it. + + Note that the data parameter is Unicode in UTF-16LE encoding. + Return value is the number of bytes in data, or LIBUSB_ERROR_*. + */ +static inline int libusb_get_string_descriptor(libusb_device_handle *dev, + uint8_t descriptor_index, uint16_t lang_id, + unsigned char *data, int length) +{ + return libusb_control_transfer(dev, + LIBUSB_ENDPOINT_IN | 0x0, /* Endpoint 0 IN */ + LIBUSB_REQUEST_GET_DESCRIPTOR, + (LIBUSB_DT_STRING << 8) | descriptor_index, + lang_id, data, (uint16_t) length, 1000); +} + +#endif + + +/* Get the first language the device says it reports. This comes from + USB string #0. */ +static uint16_t get_first_language(libusb_device_handle *dev) +{ + uint16_t buf[32]; + int len; + + /* Get the string from libusb. */ + len = libusb_get_string_descriptor(dev, + 0x0, /* String ID */ + 0x0, /* Language */ + (unsigned char*)buf, + sizeof(buf)); + if (len < 4) + return 0x0; + + return buf[1]; /* First two bytes are len and descriptor type. */ +} + +static int is_language_supported(libusb_device_handle *dev, uint16_t lang) +{ + uint16_t buf[32]; + int len; + int i; + + /* Get the string from libusb. */ + len = libusb_get_string_descriptor(dev, + 0x0, /* String ID */ + 0x0, /* Language */ + (unsigned char*)buf, + sizeof(buf)); + if (len < 4) + return 0x0; + + + len /= 2; /* language IDs are two-bytes each. */ + /* Start at index 1 because there are two bytes of protocol data. */ + for (i = 1; i < len; i++) { + if (buf[i] == lang) + return 1; + } + + return 0; +} + + +/* This function returns a newly allocated wide string containing the USB + device string numbered by the index. The returned string must be freed + by using free(). */ +static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx) +{ + char buf[512]; + int len; + wchar_t *str = NULL; + wchar_t wbuf[256]; + + /* iconv variables */ + iconv_t ic; + size_t inbytes; + size_t outbytes; + size_t res; +#ifdef __FreeBSD__ + const char *inptr; +#else + char *inptr; +#endif + char *outptr; + + /* Determine which language to use. */ + uint16_t lang; + lang = get_usb_code_for_current_locale(); + if (!is_language_supported(dev, lang)) + lang = get_first_language(dev); + + /* Get the string from libusb. */ + len = libusb_get_string_descriptor(dev, + idx, + lang, + (unsigned char*)buf, + sizeof(buf)); + if (len < 0) + return NULL; + + /* buf does not need to be explicitly NULL-terminated because + it is only passed into iconv() which does not need it. */ + + /* Initialize iconv. */ + ic = iconv_open("WCHAR_T", "UTF-16LE"); + if (ic == (iconv_t)-1) { + LOG("iconv_open() failed\n"); + return NULL; + } + + /* Convert to native wchar_t (UTF-32 on glibc/BSD systems). + Skip the first character (2-bytes). */ + inptr = buf+2; + inbytes = len-2; + outptr = (char*) wbuf; + outbytes = sizeof(wbuf); + res = iconv(ic, &inptr, &inbytes, &outptr, &outbytes); + if (res == (size_t)-1) { + LOG("iconv() failed\n"); + goto err; + } + + /* Write the terminating NULL. */ + wbuf[sizeof(wbuf)/sizeof(wbuf[0])-1] = 0x00000000; + if (outbytes >= sizeof(wbuf[0])) + *((wchar_t*)outptr) = 0x00000000; + + /* Allocate and copy the string. */ + str = wcsdup(wbuf); + +err: + iconv_close(ic); + + return str; +} + +static char *make_path(libusb_device *dev, int interface_number) +{ + char str[64]; + snprintf(str, sizeof(str), "%04x:%04x:%02x", + libusb_get_bus_number(dev), + libusb_get_device_address(dev), + interface_number); + str[sizeof(str)-1] = '\0'; + + return strdup(str); +} + + +int HID_API_EXPORT hid_init(void) +{ + if (!usb_context) { + const char *locale; + + /* Init Libusb */ + if (libusb_init(&usb_context)) + return -1; + + /* Set the locale if it's not set. */ + locale = setlocale(LC_CTYPE, NULL); + if (!locale) + setlocale(LC_CTYPE, ""); + } + + return 0; +} + +int HID_API_EXPORT hid_exit(void) +{ + if (usb_context) { + libusb_exit(usb_context); + usb_context = NULL; + } + + return 0; +} + +struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + libusb_device **devs; + libusb_device *dev; + libusb_device_handle *handle; + ssize_t num_devs; + int i = 0; + + struct hid_device_info *root = NULL; /* return object */ + struct hid_device_info *cur_dev = NULL; + + if(hid_init() < 0) + return NULL; + + num_devs = libusb_get_device_list(usb_context, &devs); + if (num_devs < 0) + return NULL; + while ((dev = devs[i++]) != NULL) { + struct libusb_device_descriptor desc; + struct libusb_config_descriptor *conf_desc = NULL; + int j, k; + int interface_num = 0; + + int res = libusb_get_device_descriptor(dev, &desc); + unsigned short dev_vid = desc.idVendor; + unsigned short dev_pid = desc.idProduct; + + res = libusb_get_active_config_descriptor(dev, &conf_desc); + if (res < 0) + libusb_get_config_descriptor(dev, 0, &conf_desc); + if (conf_desc) { + for (j = 0; j < conf_desc->bNumInterfaces; j++) { + const struct libusb_interface *intf = &conf_desc->interface[j]; + for (k = 0; k < intf->num_altsetting; k++) { + const struct libusb_interface_descriptor *intf_desc; + intf_desc = &intf->altsetting[k]; + if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { + interface_num = intf_desc->bInterfaceNumber; + + /* Check the VID/PID against the arguments */ + if ((vendor_id == 0x0 || vendor_id == dev_vid) && + (product_id == 0x0 || product_id == dev_pid)) { + struct hid_device_info *tmp; + + /* VID/PID match. Create the record. */ + tmp = calloc(1, sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + cur_dev = tmp; + + /* Fill out the record */ + cur_dev->next = NULL; + cur_dev->path = make_path(dev, interface_num); + + res = libusb_open(dev, &handle); + + if (res >= 0) { + /* Serial Number */ + if (desc.iSerialNumber > 0) + cur_dev->serial_number = + get_usb_string(handle, desc.iSerialNumber); + + /* Manufacturer and Product strings */ + if (desc.iManufacturer > 0) + cur_dev->manufacturer_string = + get_usb_string(handle, desc.iManufacturer); + if (desc.iProduct > 0) + cur_dev->product_string = + get_usb_string(handle, desc.iProduct); + +#ifdef INVASIVE_GET_USAGE +{ + /* + This section is removed because it is too + invasive on the system. Getting a Usage Page + and Usage requires parsing the HID Report + descriptor. Getting a HID Report descriptor + involves claiming the interface. Claiming the + interface involves detaching the kernel driver. + Detaching the kernel driver is hard on the system + because it will unclaim interfaces (if another + app has them claimed) and the re-attachment of + the driver will sometimes change /dev entry names. + It is for these reasons that this section is + #if 0. For composite devices, use the interface + field in the hid_device_info struct to distinguish + between interfaces. */ + unsigned char data[256]; +#ifdef DETACH_KERNEL_DRIVER + int detached = 0; + /* Usage Page and Usage */ + res = libusb_kernel_driver_active(handle, interface_num); + if (res == 1) { + res = libusb_detach_kernel_driver(handle, interface_num); + if (res < 0) + LOG("Couldn't detach kernel driver, even though a kernel driver was attached."); + else + detached = 1; + } +#endif + res = libusb_claim_interface(handle, interface_num); + if (res >= 0) { + /* Get the HID Report Descriptor. */ + res = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8)|interface_num, 0, data, sizeof(data), 5000); + if (res >= 0) { + unsigned short page=0, usage=0; + /* Parse the usage and usage page + out of the report descriptor. */ + get_usage(data, res, &page, &usage); + cur_dev->usage_page = page; + cur_dev->usage = usage; + } + else + LOG("libusb_control_transfer() for getting the HID report failed with %d\n", res); + + /* Release the interface */ + res = libusb_release_interface(handle, interface_num); + if (res < 0) + LOG("Can't release the interface.\n"); + } + else + LOG("Can't claim interface %d\n", res); +#ifdef DETACH_KERNEL_DRIVER + /* Re-attach kernel driver if necessary. */ + if (detached) { + res = libusb_attach_kernel_driver(handle, interface_num); + if (res < 0) + LOG("Couldn't re-attach kernel driver.\n"); + } +#endif +} +#endif /* INVASIVE_GET_USAGE */ + + libusb_close(handle); + } + /* VID/PID */ + cur_dev->vendor_id = dev_vid; + cur_dev->product_id = dev_pid; + + /* Release Number */ + cur_dev->release_number = desc.bcdDevice; + + /* Interface Number */ + cur_dev->interface_number = interface_num; + } + } + } /* altsettings */ + } /* interfaces */ + libusb_free_config_descriptor(conf_desc); + } + } + + libusb_free_device_list(devs, 1); + + return root; +} + +void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) +{ + struct hid_device_info *d = devs; + while (d) { + struct hid_device_info *next = d->next; + free(d->path); + free(d->serial_number); + free(d->manufacturer_string); + free(d->product_string); + free(d); + d = next; + } +} + +hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) +{ + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + devs = hid_enumerate(vendor_id, product_id); + cur_dev = devs; + while (cur_dev) { + if (cur_dev->vendor_id == vendor_id && + cur_dev->product_id == product_id) { + if (serial_number) { + if (wcscmp(serial_number, cur_dev->serial_number) == 0) { + path_to_open = cur_dev->path; + break; + } + } + else { + path_to_open = cur_dev->path; + break; + } + } + cur_dev = cur_dev->next; + } + + if (path_to_open) { + /* Open the device */ + handle = hid_open_path(path_to_open); + } + + hid_free_enumeration(devs); + + return handle; +} + +static void read_callback(struct libusb_transfer *transfer) +{ + hid_device *dev = transfer->user_data; + int res; + + if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { + + struct input_report *rpt = malloc(sizeof(*rpt)); + rpt->data = malloc(transfer->actual_length); + memcpy(rpt->data, transfer->buffer, transfer->actual_length); + rpt->len = transfer->actual_length; + rpt->next = NULL; + + pthread_mutex_lock(&dev->mutex); + + /* Attach the new report object to the end of the list. */ + if (dev->input_reports == NULL) { + /* The list is empty. Put it at the root. */ + dev->input_reports = rpt; + pthread_cond_signal(&dev->condition); + } + else { + /* Find the end of the list and attach. */ + struct input_report *cur = dev->input_reports; + int num_queued = 0; + while (cur->next != NULL) { + cur = cur->next; + num_queued++; + } + cur->next = rpt; + + /* Pop one off if we've reached 30 in the queue. This + way we don't grow forever if the user never reads + anything from the device. */ + if (num_queued > 30) { + return_data(dev, NULL, 0); + } + } + pthread_mutex_unlock(&dev->mutex); + } + else if (transfer->status == LIBUSB_TRANSFER_CANCELLED) { + dev->shutdown_thread = 1; + dev->cancelled = 1; + return; + } + else if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) { + dev->shutdown_thread = 1; + dev->cancelled = 1; + return; + } + else if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT) { + //LOG("Timeout (normal)\n"); + } + else { + LOG("Unknown transfer code: %d\n", transfer->status); + } + + /* Re-submit the transfer object. */ + res = libusb_submit_transfer(transfer); + if (res != 0) { + LOG("Unable to submit URB. libusb error code: %d\n", res); + dev->shutdown_thread = 1; + dev->cancelled = 1; + } +} + + +static void *read_thread(void *param) +{ + hid_device *dev = param; + unsigned char *buf; + const size_t length = dev->input_ep_max_packet_size; + + /* Set up the transfer object. */ + buf = malloc(length); + dev->transfer = libusb_alloc_transfer(0); + libusb_fill_interrupt_transfer(dev->transfer, + dev->device_handle, + dev->input_endpoint, + buf, + length, + read_callback, + dev, + 5000/*timeout*/); + + /* Make the first submission. Further submissions are made + from inside read_callback() */ + libusb_submit_transfer(dev->transfer); + + /* Notify the main thread that the read thread is up and running. */ + pthread_barrier_wait(&dev->barrier); + + /* Handle all the events. */ + while (!dev->shutdown_thread) { + int res; + res = libusb_handle_events(usb_context); + if (res < 0) { + /* There was an error. */ + LOG("read_thread(): libusb reports error # %d\n", res); + + /* Break out of this loop only on fatal error.*/ + if (res != LIBUSB_ERROR_BUSY && + res != LIBUSB_ERROR_TIMEOUT && + res != LIBUSB_ERROR_OVERFLOW && + res != LIBUSB_ERROR_INTERRUPTED) { + break; + } + } + } + + /* Cancel any transfer that may be pending. This call will fail + if no transfers are pending, but that's OK. */ + libusb_cancel_transfer(dev->transfer); + + while (!dev->cancelled) + libusb_handle_events_completed(usb_context, &dev->cancelled); + + /* Now that the read thread is stopping, Wake any threads which are + waiting on data (in hid_read_timeout()). Do this under a mutex to + make sure that a thread which is about to go to sleep waiting on + the condition acutally will go to sleep before the condition is + signaled. */ + pthread_mutex_lock(&dev->mutex); + pthread_cond_broadcast(&dev->condition); + pthread_mutex_unlock(&dev->mutex); + + /* The dev->transfer->buffer and dev->transfer objects are cleaned up + in hid_close(). They are not cleaned up here because this thread + could end either due to a disconnect or due to a user + call to hid_close(). In both cases the objects can be safely + cleaned up after the call to pthread_join() (in hid_close()), but + since hid_close() calls libusb_cancel_transfer(), on these objects, + they can not be cleaned up here. */ + + return NULL; +} + + +hid_device * HID_API_EXPORT hid_open_path(const char *path) +{ + hid_device *dev = NULL; + + libusb_device **devs; + libusb_device *usb_dev; + int res; + int d = 0; + int good_open = 0; + + if(hid_init() < 0) + return NULL; + + dev = new_hid_device(); + + libusb_get_device_list(usb_context, &devs); + while ((usb_dev = devs[d++]) != NULL) { + struct libusb_device_descriptor desc; + struct libusb_config_descriptor *conf_desc = NULL; + int i,j,k; + libusb_get_device_descriptor(usb_dev, &desc); + + if (libusb_get_active_config_descriptor(usb_dev, &conf_desc) < 0) + continue; + for (j = 0; j < conf_desc->bNumInterfaces; j++) { + const struct libusb_interface *intf = &conf_desc->interface[j]; + for (k = 0; k < intf->num_altsetting; k++) { + const struct libusb_interface_descriptor *intf_desc; + intf_desc = &intf->altsetting[k]; + if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { + char *dev_path = make_path(usb_dev, intf_desc->bInterfaceNumber); + if (!strcmp(dev_path, path)) { + /* Matched Paths. Open this device */ + + /* OPEN HERE */ + res = libusb_open(usb_dev, &dev->device_handle); + if (res < 0) { + LOG("can't open device\n"); + free(dev_path); + break; + } + good_open = 1; +#ifdef DETACH_KERNEL_DRIVER + /* Detach the kernel driver, but only if the + device is managed by the kernel */ + if (libusb_kernel_driver_active(dev->device_handle, intf_desc->bInterfaceNumber) == 1) { + res = libusb_detach_kernel_driver(dev->device_handle, intf_desc->bInterfaceNumber); + if (res < 0) { + libusb_close(dev->device_handle); + LOG("Unable to detach Kernel Driver\n"); + free(dev_path); + good_open = 0; + break; + } + } +#endif + res = libusb_claim_interface(dev->device_handle, intf_desc->bInterfaceNumber); + if (res < 0) { + LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res); + free(dev_path); + libusb_close(dev->device_handle); + good_open = 0; + break; + } + + /* Store off the string descriptor indexes */ + dev->manufacturer_index = desc.iManufacturer; + dev->product_index = desc.iProduct; + dev->serial_index = desc.iSerialNumber; + + /* Store off the interface number */ + dev->interface = intf_desc->bInterfaceNumber; + + /* Find the INPUT and OUTPUT endpoints. An + OUTPUT endpoint is not required. */ + for (i = 0; i < intf_desc->bNumEndpoints; i++) { + const struct libusb_endpoint_descriptor *ep + = &intf_desc->endpoint[i]; + + /* Determine the type and direction of this + endpoint. */ + int is_interrupt = + (ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) + == LIBUSB_TRANSFER_TYPE_INTERRUPT; + int is_output = + (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) + == LIBUSB_ENDPOINT_OUT; + int is_input = + (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) + == LIBUSB_ENDPOINT_IN; + + /* Decide whether to use it for intput or output. */ + if (dev->input_endpoint == 0 && + is_interrupt && is_input) { + /* Use this endpoint for INPUT */ + dev->input_endpoint = ep->bEndpointAddress; + dev->input_ep_max_packet_size = ep->wMaxPacketSize; + } + if (dev->output_endpoint == 0 && + is_interrupt && is_output) { + /* Use this endpoint for OUTPUT */ + dev->output_endpoint = ep->bEndpointAddress; + } + } + + pthread_create(&dev->thread, NULL, read_thread, dev); + + /* Wait here for the read thread to be initialized. */ + pthread_barrier_wait(&dev->barrier); + + } + free(dev_path); + } + } + } + libusb_free_config_descriptor(conf_desc); + + } + + libusb_free_device_list(devs, 1); + + /* If we have a good handle, return it. */ + if (good_open) { + return dev; + } + else { + /* Unable to open any devices. */ + free_hid_device(dev); + return NULL; + } +} + + +int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + int res; + int report_number = data[0]; + int skipped_report_id = 0; + + if (report_number == 0x0) { + data++; + length--; + skipped_report_id = 1; + } + + + if (dev->output_endpoint <= 0) { + /* No interrput out endpoint. Use the Control Endpoint */ + res = libusb_control_transfer(dev->device_handle, + LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT, + 0x09/*HID Set_Report*/, + (2/*HID output*/ << 8) | report_number, + dev->interface, + (unsigned char *)data, length, + 1000/*timeout millis*/); + + if (res < 0) + return -1; + + if (skipped_report_id) + length++; + + return length; + } + else { + /* Use the interrupt out endpoint */ + int actual_length; + res = libusb_interrupt_transfer(dev->device_handle, + dev->output_endpoint, + (unsigned char*)data, + length, + &actual_length, 1000); + + if (res < 0) + return -1; + + if (skipped_report_id) + actual_length++; + + return actual_length; + } +} + +/* Helper function, to simplify hid_read(). + This should be called with dev->mutex locked. */ +static int return_data(hid_device *dev, unsigned char *data, size_t length) +{ + /* Copy the data out of the linked list item (rpt) into the + return buffer (data), and delete the liked list item. */ + struct input_report *rpt = dev->input_reports; + size_t len = (length < rpt->len)? length: rpt->len; + if (len > 0) + memcpy(data, rpt->data, len); + dev->input_reports = rpt->next; + free(rpt->data); + free(rpt); + return len; +} + +static void cleanup_mutex(void *param) +{ + hid_device *dev = param; + pthread_mutex_unlock(&dev->mutex); +} + + +int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + int bytes_read = -1; + +#if 0 + int transferred; + int res = libusb_interrupt_transfer(dev->device_handle, dev->input_endpoint, data, length, &transferred, 5000); + LOG("transferred: %d\n", transferred); + return transferred; +#endif + + pthread_mutex_lock(&dev->mutex); + pthread_cleanup_push(&cleanup_mutex, dev); + + /* There's an input report queued up. Return it. */ + if (dev->input_reports) { + /* Return the first one */ + bytes_read = return_data(dev, data, length); + goto ret; + } + + if (dev->shutdown_thread) { + /* This means the device has been disconnected. + An error code of -1 should be returned. */ + bytes_read = -1; + goto ret; + } + + if (milliseconds == -1) { + /* Blocking */ + while (!dev->input_reports && !dev->shutdown_thread) { + pthread_cond_wait(&dev->condition, &dev->mutex); + } + if (dev->input_reports) { + bytes_read = return_data(dev, data, length); + } + } + else if (milliseconds > 0) { + /* Non-blocking, but called with timeout. */ + int res; + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += milliseconds / 1000; + ts.tv_nsec += (milliseconds % 1000) * 1000000; + if (ts.tv_nsec >= 1000000000L) { + ts.tv_sec++; + ts.tv_nsec -= 1000000000L; + } + + while (!dev->input_reports && !dev->shutdown_thread) { + res = pthread_cond_timedwait(&dev->condition, &dev->mutex, &ts); + if (res == 0) { + if (dev->input_reports) { + bytes_read = return_data(dev, data, length); + break; + } + + /* If we're here, there was a spurious wake up + or the read thread was shutdown. Run the + loop again (ie: don't break). */ + } + else if (res == ETIMEDOUT) { + /* Timed out. */ + bytes_read = 0; + break; + } + else { + /* Error. */ + bytes_read = -1; + break; + } + } + } + else { + /* Purely non-blocking */ + bytes_read = 0; + } + +ret: + pthread_mutex_unlock(&dev->mutex); + pthread_cleanup_pop(0); + + return bytes_read; +} + +int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) +{ + return hid_read_timeout(dev, data, length, dev->blocking ? -1 : 0); +} + +int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) +{ + dev->blocking = !nonblock; + + return 0; +} + + +int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + int res = -1; + int skipped_report_id = 0; + int report_number = data[0]; + + if (report_number == 0x0) { + data++; + length--; + skipped_report_id = 1; + } + + res = libusb_control_transfer(dev->device_handle, + LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT, + 0x09/*HID set_report*/, + (3/*HID feature*/ << 8) | report_number, + dev->interface, + (unsigned char *)data, length, + 1000/*timeout millis*/); + + if (res < 0) + return -1; + + /* Account for the report ID */ + if (skipped_report_id) + length++; + + return length; +} + +int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + int res = -1; + int skipped_report_id = 0; + int report_number = data[0]; + + if (report_number == 0x0) { + /* Offset the return buffer by 1, so that the report ID + will remain in byte 0. */ + data++; + length--; + skipped_report_id = 1; + } + res = libusb_control_transfer(dev->device_handle, + LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_IN, + 0x01/*HID get_report*/, + (3/*HID feature*/ << 8) | report_number, + dev->interface, + (unsigned char *)data, length, + 1000/*timeout millis*/); + + if (res < 0) + return -1; + + if (skipped_report_id) + res++; + + return res; +} + + +void HID_API_EXPORT hid_close(hid_device *dev) +{ + if (!dev) + return; + + /* Cause read_thread() to stop. */ + dev->shutdown_thread = 1; + libusb_cancel_transfer(dev->transfer); + + /* Wait for read_thread() to end. */ + pthread_join(dev->thread, NULL); + + /* Clean up the Transfer objects allocated in read_thread(). */ + free(dev->transfer->buffer); + libusb_free_transfer(dev->transfer); + + /* release the interface */ + libusb_release_interface(dev->device_handle, dev->interface); + + /* Close the handle */ + libusb_close(dev->device_handle); + + /* Clear out the queue of received reports. */ + pthread_mutex_lock(&dev->mutex); + while (dev->input_reports) { + return_data(dev, NULL, 0); + } + pthread_mutex_unlock(&dev->mutex); + + free_hid_device(dev); +} + + +int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return hid_get_indexed_string(dev, dev->manufacturer_index, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return hid_get_indexed_string(dev, dev->product_index, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return hid_get_indexed_string(dev, dev->serial_index, string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) +{ + wchar_t *str; + + str = get_usb_string(dev->device_handle, string_index); + if (str) { + wcsncpy(string, str, maxlen); + string[maxlen-1] = L'\0'; + free(str); + return 0; + } + else + return -1; +} + + +HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) +{ + return NULL; +} + + +struct lang_map_entry { + const char *name; + const char *string_code; + uint16_t usb_code; +}; + +#define LANG(name,code,usb_code) { name, code, usb_code } +static struct lang_map_entry lang_map[] = { + LANG("Afrikaans", "af", 0x0436), + LANG("Albanian", "sq", 0x041C), + LANG("Arabic - United Arab Emirates", "ar_ae", 0x3801), + LANG("Arabic - Bahrain", "ar_bh", 0x3C01), + LANG("Arabic - Algeria", "ar_dz", 0x1401), + LANG("Arabic - Egypt", "ar_eg", 0x0C01), + LANG("Arabic - Iraq", "ar_iq", 0x0801), + LANG("Arabic - Jordan", "ar_jo", 0x2C01), + LANG("Arabic - Kuwait", "ar_kw", 0x3401), + LANG("Arabic - Lebanon", "ar_lb", 0x3001), + LANG("Arabic - Libya", "ar_ly", 0x1001), + LANG("Arabic - Morocco", "ar_ma", 0x1801), + LANG("Arabic - Oman", "ar_om", 0x2001), + LANG("Arabic - Qatar", "ar_qa", 0x4001), + LANG("Arabic - Saudi Arabia", "ar_sa", 0x0401), + LANG("Arabic - Syria", "ar_sy", 0x2801), + LANG("Arabic - Tunisia", "ar_tn", 0x1C01), + LANG("Arabic - Yemen", "ar_ye", 0x2401), + LANG("Armenian", "hy", 0x042B), + LANG("Azeri - Latin", "az_az", 0x042C), + LANG("Azeri - Cyrillic", "az_az", 0x082C), + LANG("Basque", "eu", 0x042D), + LANG("Belarusian", "be", 0x0423), + LANG("Bulgarian", "bg", 0x0402), + LANG("Catalan", "ca", 0x0403), + LANG("Chinese - China", "zh_cn", 0x0804), + LANG("Chinese - Hong Kong SAR", "zh_hk", 0x0C04), + LANG("Chinese - Macau SAR", "zh_mo", 0x1404), + LANG("Chinese - Singapore", "zh_sg", 0x1004), + LANG("Chinese - Taiwan", "zh_tw", 0x0404), + LANG("Croatian", "hr", 0x041A), + LANG("Czech", "cs", 0x0405), + LANG("Danish", "da", 0x0406), + LANG("Dutch - Netherlands", "nl_nl", 0x0413), + LANG("Dutch - Belgium", "nl_be", 0x0813), + LANG("English - Australia", "en_au", 0x0C09), + LANG("English - Belize", "en_bz", 0x2809), + LANG("English - Canada", "en_ca", 0x1009), + LANG("English - Caribbean", "en_cb", 0x2409), + LANG("English - Ireland", "en_ie", 0x1809), + LANG("English - Jamaica", "en_jm", 0x2009), + LANG("English - New Zealand", "en_nz", 0x1409), + LANG("English - Phillippines", "en_ph", 0x3409), + LANG("English - Southern Africa", "en_za", 0x1C09), + LANG("English - Trinidad", "en_tt", 0x2C09), + LANG("English - Great Britain", "en_gb", 0x0809), + LANG("English - United States", "en_us", 0x0409), + LANG("Estonian", "et", 0x0425), + LANG("Farsi", "fa", 0x0429), + LANG("Finnish", "fi", 0x040B), + LANG("Faroese", "fo", 0x0438), + LANG("French - France", "fr_fr", 0x040C), + LANG("French - Belgium", "fr_be", 0x080C), + LANG("French - Canada", "fr_ca", 0x0C0C), + LANG("French - Luxembourg", "fr_lu", 0x140C), + LANG("French - Switzerland", "fr_ch", 0x100C), + LANG("Gaelic - Ireland", "gd_ie", 0x083C), + LANG("Gaelic - Scotland", "gd", 0x043C), + LANG("German - Germany", "de_de", 0x0407), + LANG("German - Austria", "de_at", 0x0C07), + LANG("German - Liechtenstein", "de_li", 0x1407), + LANG("German - Luxembourg", "de_lu", 0x1007), + LANG("German - Switzerland", "de_ch", 0x0807), + LANG("Greek", "el", 0x0408), + LANG("Hebrew", "he", 0x040D), + LANG("Hindi", "hi", 0x0439), + LANG("Hungarian", "hu", 0x040E), + LANG("Icelandic", "is", 0x040F), + LANG("Indonesian", "id", 0x0421), + LANG("Italian - Italy", "it_it", 0x0410), + LANG("Italian - Switzerland", "it_ch", 0x0810), + LANG("Japanese", "ja", 0x0411), + LANG("Korean", "ko", 0x0412), + LANG("Latvian", "lv", 0x0426), + LANG("Lithuanian", "lt", 0x0427), + LANG("F.Y.R.O. Macedonia", "mk", 0x042F), + LANG("Malay - Malaysia", "ms_my", 0x043E), + LANG("Malay – Brunei", "ms_bn", 0x083E), + LANG("Maltese", "mt", 0x043A), + LANG("Marathi", "mr", 0x044E), + LANG("Norwegian - Bokml", "no_no", 0x0414), + LANG("Norwegian - Nynorsk", "no_no", 0x0814), + LANG("Polish", "pl", 0x0415), + LANG("Portuguese - Portugal", "pt_pt", 0x0816), + LANG("Portuguese - Brazil", "pt_br", 0x0416), + LANG("Raeto-Romance", "rm", 0x0417), + LANG("Romanian - Romania", "ro", 0x0418), + LANG("Romanian - Republic of Moldova", "ro_mo", 0x0818), + LANG("Russian", "ru", 0x0419), + LANG("Russian - Republic of Moldova", "ru_mo", 0x0819), + LANG("Sanskrit", "sa", 0x044F), + LANG("Serbian - Cyrillic", "sr_sp", 0x0C1A), + LANG("Serbian - Latin", "sr_sp", 0x081A), + LANG("Setsuana", "tn", 0x0432), + LANG("Slovenian", "sl", 0x0424), + LANG("Slovak", "sk", 0x041B), + LANG("Sorbian", "sb", 0x042E), + LANG("Spanish - Spain (Traditional)", "es_es", 0x040A), + LANG("Spanish - Argentina", "es_ar", 0x2C0A), + LANG("Spanish - Bolivia", "es_bo", 0x400A), + LANG("Spanish - Chile", "es_cl", 0x340A), + LANG("Spanish - Colombia", "es_co", 0x240A), + LANG("Spanish - Costa Rica", "es_cr", 0x140A), + LANG("Spanish - Dominican Republic", "es_do", 0x1C0A), + LANG("Spanish - Ecuador", "es_ec", 0x300A), + LANG("Spanish - Guatemala", "es_gt", 0x100A), + LANG("Spanish - Honduras", "es_hn", 0x480A), + LANG("Spanish - Mexico", "es_mx", 0x080A), + LANG("Spanish - Nicaragua", "es_ni", 0x4C0A), + LANG("Spanish - Panama", "es_pa", 0x180A), + LANG("Spanish - Peru", "es_pe", 0x280A), + LANG("Spanish - Puerto Rico", "es_pr", 0x500A), + LANG("Spanish - Paraguay", "es_py", 0x3C0A), + LANG("Spanish - El Salvador", "es_sv", 0x440A), + LANG("Spanish - Uruguay", "es_uy", 0x380A), + LANG("Spanish - Venezuela", "es_ve", 0x200A), + LANG("Southern Sotho", "st", 0x0430), + LANG("Swahili", "sw", 0x0441), + LANG("Swedish - Sweden", "sv_se", 0x041D), + LANG("Swedish - Finland", "sv_fi", 0x081D), + LANG("Tamil", "ta", 0x0449), + LANG("Tatar", "tt", 0X0444), + LANG("Thai", "th", 0x041E), + LANG("Turkish", "tr", 0x041F), + LANG("Tsonga", "ts", 0x0431), + LANG("Ukrainian", "uk", 0x0422), + LANG("Urdu", "ur", 0x0420), + LANG("Uzbek - Cyrillic", "uz_uz", 0x0843), + LANG("Uzbek – Latin", "uz_uz", 0x0443), + LANG("Vietnamese", "vi", 0x042A), + LANG("Xhosa", "xh", 0x0434), + LANG("Yiddish", "yi", 0x043D), + LANG("Zulu", "zu", 0x0435), + LANG(NULL, NULL, 0x0), +}; + +uint16_t get_usb_code_for_current_locale(void) +{ + char *locale; + char search_string[64]; + char *ptr; + struct lang_map_entry *lang; + + /* Get the current locale. */ + locale = setlocale(0, NULL); + if (!locale) + return 0x0; + + /* Make a copy of the current locale string. */ + strncpy(search_string, locale, sizeof(search_string)); + search_string[sizeof(search_string)-1] = '\0'; + + /* Chop off the encoding part, and make it lower case. */ + ptr = search_string; + while (*ptr) { + *ptr = tolower(*ptr); + if (*ptr == '.') { + *ptr = '\0'; + break; + } + ptr++; + } + + /* Find the entry which matches the string code of our locale. */ + lang = lang_map; + while (lang->string_code) { + if (!strcmp(lang->string_code, search_string)) { + return lang->usb_code; + } + lang++; + } + + /* There was no match. Find with just the language only. */ + /* Chop off the variant. Chop it off at the '_'. */ + ptr = search_string; + while (*ptr) { + *ptr = tolower(*ptr); + if (*ptr == '_') { + *ptr = '\0'; + break; + } + ptr++; + } + +#if 0 /* TODO: Do we need this? */ + /* Find the entry which matches the string code of our language. */ + lang = lang_map; + while (lang->string_code) { + if (!strcmp(lang->string_code, search_string)) { + return lang->usb_code; + } + lang++; + } +#endif + + /* Found nothing. */ + return 0x0; +} + +#ifdef __cplusplus +} +#endif diff --git a/dependencies/hidapi-master-20131122.zip b/dependencies/hidapi-master-20131122.zip new file mode 100644 index 0000000000000000000000000000000000000000..64db9f9daef5bef297656a37a307a6e1aef3b4e2 GIT binary patch literal 122317 zcmZ^~V~j5Gvn@QfZ9Ze$wr$(CXOC^$wr%dQJ+sHQ_W0fJ&B;mr_nh0wuhUi+~)O}5dB{tL4Fckup45WR(!tC6d#vz4)%tC`DxzhX(5QmU4+QI3{w^7LSFk!E%h z6qd5&u7oc1q;_kRcLe@(;6!rsBz?7ve`iywp@W`q;F%_5oCA*K(dod@O)F{X*Dunm_a z$L~i-QQjl?kvV{-A1>BzR2G$h`!$3_wG_8%X+#`K(RT(I$DWtDr=*t)w4;*~pDi{x zhv+*BE$;R&yaD^gZmKmG$`3Je5<{Xaknsi{gTD67!Bdb<8MDn3Oa z>tR7SULS)(NGyiVEX4!%5C~;u?{lw1HQQgcY&3Qk-+h;x*Fub0*iymMmv8Gm`Gr!O z;gqdRGk>t^sv117@k6-XhwU}UysG4!VwaW3+khRf_Ggf7=wr${kjgtl6pRV`O17Uc zEOQ|6m_AZKqM6c$?1^I2y4<5$iX%M?ljjBWxtB~FN$wMR<{@fuBMznk1 zvxquPG0Rety+<(~QO6cz|DCafg1*TpkL+Vw2stEJgmjZ>7V9e0KPZhPI!}Q4DB0DO zBP_95t~0w-E#KN|V_=s4<&$_l*&TVsP37)d^hR+4`Ja?!v8(G}{-ey|f2B-PNJK_T zUgAF}lT(ya_$LHDhW?>&y-Pb^{DiROSVrQ(gsvfUF`G@z-|35JF#UP9W|s(Q*X0)d z7U@yi6;d*i8y)NygcW*$6#y(D_Ob+EY#pK+?nQLUst}5r7IO_u%@jM)jdDH=g0~Al z&>?ljJIuUW&Ter{9uvwSNtY{#!m}%-qe-jlzDagl5}#Y)5Jdf}yuG$5E_z3Q@hRWO z&KZL@=lQv^A69m&#Z^k2j66zEz>xkzdI+t|D;Dy8y05iw0^2jG7kk=WASaJU#@{Q9 zLfLTqT!ItCd|`Kv&3c8GTS&FNRv6s^@?Im>k?;SjWE5|&zL7zJfWEYeS$LYSeqXWmZq^*vJFW?%qZ5L~cN>k_G}{e_+FL!}k#J}hxy ze*9fCHKKh$+EOQh3?hF)0OX($OrxC8f~oJogYSallKqmi{+5A-W9Ukc9V1p1%elw*kYdNb{KM-IEL-k( zw~ggmVt-_CEnHF?D;fF}t}Tno_Ant^`_oI5*8eCN&krK6%n+RK{TUp27Ggk{UOudi z%nouE$ArUk83MmjqPQWvy5Dz}{+{;Qq`R9di=GOtWv|=DTeIe-&v>1LU9hPWj4a(% zo&=g}MSJZr1gCW`Sc7v`x&;{2vek%bF;)hugYT+qKZq5B>+33o=4t@==dB^HeCRTj zi2!$Udd^@Xu2M8JnZE-yyf)B!w9Nv66rthC_8eo)v`rIJK6fFEp>fF{*}i2kdyc&S zNw2exWDRAKa#__nkAo~#k)sJCD$%m~4>beTUc&*zMk4Y$Uo~q58{L#ZSIlO>Lx*9> zk*iV@DQXvOhiPY=`>p~PdHB2ZCw!&W?BOpz3(K(IdSMx0Oz0o0hRFQFLL9E4yZoP| zC^=HQl5LS$qe&I!g{*6_<_#!Cl0r1(w_dZ$8kC{ZR%}sb69ra)I3z4|{zI=u_XU| zgI@>HrPP{n$u3EJPSk2Gvg6yNGTKsY@p~A6Fp^cGfl)Em*ENTL9X`y!lBCUax$D(N zfMHpA`7H-vaz7wIH$eJ3LvQY;Ok?u8yj|mQD -A{l;B5y1p-);%RE%h^nT;QAZ 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 z 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 z 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%0DX)z^ahFY5CY(t$!U;elsBR z={cstoP>%RsnUlGsP6a>m6XggvmA?B+LSDwp&PzXGRBG#@-s@aTaY7Gzhkt6pbF2H9XngN=|3y%<0n58!k163Ojfzr04Jc-D|YHpZ#;g z{5;*KS-soqSK7Cv>;ArV9G**M4C(CPUT&L-?(I z|3X(4`hP)yS-C;AY)&k)>v{g#98!LvI-(nic^uD9dJQ?!JE@IOv8PEzWe|A|?`z48 zSc1Qr*yl@5T?8m;!hitZE(CmZ$FfL4rkB$#eCkFqDWN8DIjTomOZ@SF>WNCbN=CiS z&CCWi<`YY4(wW}O8GZ&AWJNhFk>PZw!__E{kjKu;j9R}W`Nf8jIG?8EhJ)mS)(lW3 zGj`lB?QkyRkG&EMhAF!~xyHhuSoEq1cQ{$WG6;IuQ;AE=IL{rp6k zTf)F0454NE3Hi%SV8=i8?7`sY5|b70^Sb`x=#(=s%xwb2x*} zvQ5has7aoX(22W^0m-5nKZho2eA}VQKodb12n&<>W3_-Y*&V8mBzu8>EU({hFvR?m+20*U$RC?&0V zMU;T{-$Jv)x;Qqu`TV!}v9I#GxOp%1pD6J)D-nOEv^&Qr&qXNN?>X)X%~$u*W7Adi zEDR?S-`h|7@C3CrGqc|<9CsUQxMobkK=U$P^OrW2g=^V0fXoWJRIqrS;#K!0TQ)MO zHcU*B<&Wb|)O2~)Z4)=_5(HeG=V@FfuXz|PZ`Q}qZ%)GS7>3O-%(CB!z}Zim3!L$1 zQo%1iSX?8@^&}9wkWGC!1s4Mwy&AqQB^JO_GZUO=<>H9u#*jVIBA3)b*$1F75g+e+ zQOg{N&Xgv{H$49GkxbK!mo{i=QhvUlU{ilmo8k*WXRcT_76h%CFbVQMtoZl}ze7WD zy0@|{3g9{o6SIMUMxi#h6(ArzJ=QK9p>6vT-&Ay`5|@#?yr^x2-uv>ku>K3CS}9V; zRq6sEap}AXl#`lz_#+y*r!xF5qdf#`bD2Bom9lh;d;1z6_t6O5zp>4)Rdvt#n1olq zTpsg*9^>N!w0{#H{`vj;kVp@=`wS08o{nw=$~NB88*u_AyrLF!I&N-zf7Ifv&F6%v zKN?V^%Bf=YtuvT+@n%KNF*Y8x&tuflI<1+_knAxJ4V-RR&I%$IJE^|%0A=UHEsQiK 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%0D=f+fIYPK8hL%ajzf?*;w6Gsj&7yjhC##t*!7^i; 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 literal 0 HcmV?d00001 diff --git a/dependencies/include/hidapi/hidapi.h b/dependencies/include/hidapi/hidapi.h new file mode 100644 index 00000000..5730f867 --- /dev/null +++ b/dependencies/include/hidapi/hidapi.h @@ -0,0 +1,387 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +/** @file + * @defgroup API hidapi API + */ + +#ifndef HIDAPI_H__ +#define HIDAPI_H__ + +#include + +#ifdef _WIN32 + #define HID_API_EXPORT __declspec(dllexport) + #define HID_API_CALL +#else + #define HID_API_EXPORT /**< API export macro */ + #define HID_API_CALL /**< API call macro */ +#endif + +#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/ + +#ifdef __cplusplus +extern "C" { +#endif + struct hid_device_; + typedef struct hid_device_ hid_device; /**< opaque hidapi structure */ + + /** hidapi info structure */ + struct hid_device_info { + /** Platform-specific device path */ + char *path; + /** Device Vendor ID */ + unsigned short vendor_id; + /** Device Product ID */ + unsigned short product_id; + /** Serial Number */ + wchar_t *serial_number; + /** Device Release Number in binary-coded decimal, + also known as Device Version Number */ + unsigned short release_number; + /** Manufacturer String */ + wchar_t *manufacturer_string; + /** Product string */ + wchar_t *product_string; + /** Usage Page for this Device/Interface + (Windows/Mac only). */ + unsigned short usage_page; + /** Usage for this Device/Interface + (Windows/Mac only).*/ + unsigned short usage; + /** The USB interface which this logical device + represents. Valid on both Linux implementations + in all cases, and valid on the Windows implementation + only if the device contains more than one interface. */ + int interface_number; + + /** Pointer to the next device */ + struct hid_device_info *next; + }; + + + /** @brief Initialize the HIDAPI library. + + This function initializes the HIDAPI library. Calling it is not + strictly necessary, as it will be called automatically by + hid_enumerate() and any of the hid_open_*() functions if it is + needed. This function should be called at the beginning of + execution however, if there is a chance of HIDAPI handles + being opened by different threads simultaneously. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_init(void); + + /** @brief Finalize the HIDAPI library. + + This function frees all of the static data associated with + HIDAPI. It should be called at the end of execution to avoid + memory leaks. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_exit(void); + + /** @brief Enumerate the HID Devices. + + This function returns a linked list of all the HID devices + attached to the system which match vendor_id and product_id. + If @p vendor_id is set to 0 then any vendor matches. + If @p product_id is set to 0 then any product matches. + If @p vendor_id and @p product_id are both set to 0, then + all HID devices will be returned. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the types of device + to open. + @param product_id The Product ID (PID) of the types of + device to open. + + @returns + This function returns a pointer to a linked list of type + struct #hid_device, containing information about the HID devices + attached to the system, or NULL in the case of failure. Free + this linked list by calling hid_free_enumeration(). + */ + struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id); + + /** @brief Free an enumeration Linked List + + This function frees a linked list created by hid_enumerate(). + + @ingroup API + @param devs Pointer to a list of struct_device returned from + hid_enumerate(). + */ + void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs); + + /** @brief Open a HID device using a Vendor ID (VID), Product ID + (PID) and optionally a serial number. + + If @p serial_number is NULL, the first device with the + specified VID and PID is opened. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the device to open. + @param product_id The Product ID (PID) of the device to open. + @param serial_number The Serial Number of the device to open + (Optionally NULL). + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number); + + /** @brief Open a HID device by its path name. + + The path name be determined by calling hid_enumerate(), or a + platform-specific path name can be used (eg: /dev/hidraw0 on + Linux). + + @ingroup API + @param path The path name of the device to open + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path); + + /** @brief Write an Output report to a HID device. + + The first byte of @p data[] must contain the Report ID. For + devices which only support a single report, this must be set + to 0x0. The remaining bytes contain the report data. Since + the Report ID is mandatory, calls to hid_write() will always + contain one more byte than the report contains. For example, + if a hid report is 16 bytes long, 17 bytes must be passed to + hid_write(), the Report ID (or 0x0, for devices with a + single report), followed by the report data (16 bytes). In + this example, the length passed in would be 17. + + hid_write() will send the data on the first OUT endpoint, if + one exists. If it does not, it will send the data through + the Control Endpoint (Endpoint 0). + + @ingroup API + @param device A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length); + + /** @brief Read an Input report from a HID device with timeout. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + @param milliseconds timeout in milliseconds or -1 for blocking wait. + + @returns + This function returns the actual number of bytes read and + -1 on error. If no packet was available to be read within + the timeout period, this function returns 0. + */ + int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); + + /** @brief Read an Input report from a HID device. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + + @returns + This function returns the actual number of bytes read and + -1 on error. If no packet was available to be read and + the handle is in non-blocking mode, this function returns 0. + */ + int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length); + + /** @brief Set the device handle to be non-blocking. + + In non-blocking mode calls to hid_read() will return + immediately with a value of 0 if there is no data to be + read. In blocking mode, hid_read() will wait (block) until + there is data to read before returning. + + Nonblocking can be turned on and off at any time. + + @ingroup API + @param device A device handle returned from hid_open(). + @param nonblock enable or not the nonblocking reads + - 1 to enable nonblocking + - 0 to disable nonblocking. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock); + + /** @brief Send a Feature report to the device. + + Feature reports are sent over the Control endpoint as a + Set_Report transfer. The first byte of @p data[] must + contain the Report ID. For devices which only support a + single report, this must be set to 0x0. The remaining bytes + contain the report data. Since the Report ID is mandatory, + calls to hid_send_feature_report() will always contain one + more byte than the report contains. For example, if a hid + report is 16 bytes long, 17 bytes must be passed to + hid_send_feature_report(): the Report ID (or 0x0, for + devices which do not use numbered reports), followed by the + report data (16 bytes). In this example, the length passed + in would be 17. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send, including + the report number. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length); + + /** @brief Get a feature report from a HID device. + + Make sure to set the first byte of @p data[] to the Report + ID of the report to be read. Make sure to allow space for + this extra byte in @p data[]. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into, including + the Report ID. Set the first byte of @p data[] to the + Report ID of the report to be read. + @param length The number of bytes to read, including an + extra byte for the report ID. The buffer can be longer + than the actual report. + + @returns + This function returns the number of bytes read and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length); + + /** @brief Close a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + */ + void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device); + + /** @brief Get The Manufacturer String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get The Product String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get The Serial Number String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get a string from a HID device, based on its string index. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string_index The index of the string to get. + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen); + + /** @brief Get a string describing the last error which occurred. + + @ingroup API + @param device A device handle returned from hid_open(). + + @returns + This function returns a string containing the last error + which occurred or NULL if none has occurred. + */ + HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/libsrc/hyperion/CMakeLists.txt b/libsrc/hyperion/CMakeLists.txt index f87438d6..ef129371 100644 --- a/libsrc/hyperion/CMakeLists.txt +++ b/libsrc/hyperion/CMakeLists.txt @@ -3,7 +3,14 @@ SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/hyperion) SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/hyperion) -include_directories(${LIBUSB_1_INCLUDE_DIRS}) +#add libusb and pthreads (required for the Lighpack usb device) +find_package(UDev REQUIRED) +find_package(libusb-1.0 REQUIRED) +find_package(Threads REQUIRED) + +include_directories( + ${UDEV_INCLUDE_DIR} + ${LIBUSB_1_INCLUDE_DIRS}) # Group the headers that go through the MOC compiler SET(Hyperion_QT_HEADERS @@ -79,5 +86,6 @@ target_link_libraries(hyperion hyperion-utils serialport ${QT_LIBRARIES} - ${LIBUSB_1_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT}) + ${LIBUSB_1_LIBRARIES} #apt-get install libusb-1.0-0-dev + ${CMAKE_THREAD_LIBS_INIT} + ${UDEV_LIBRARIES}) # apt-get install libudev-dev diff --git a/libsrc/protoserver/CMakeLists.txt b/libsrc/protoserver/CMakeLists.txt index a66ef336..58d67564 100644 --- a/libsrc/protoserver/CMakeLists.txt +++ b/libsrc/protoserver/CMakeLists.txt @@ -3,6 +3,9 @@ set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/protoserver) set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/protoserver) +# add protocol buffers +find_package(Protobuf REQUIRED) + include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${PROTOBUF_INCLUDE_DIRS}) diff --git a/src/hyperion-remote/CMakeLists.txt b/src/hyperion-remote/CMakeLists.txt index 2efe938d..aafbf472 100644 --- a/src/hyperion-remote/CMakeLists.txt +++ b/src/hyperion-remote/CMakeLists.txt @@ -2,6 +2,10 @@ cmake_minimum_required(VERSION 2.8) project(hyperion-remote) +# add protocol buffers +find_package(Protobuf REQUIRED) + +# find Qt4 find_package(Qt4 REQUIRED QtCore QtGui QtNetwork) # The following I do not undrstand completely... From b2f52aad89fee047fb95ed3b3d887f9760ba8f9e Mon Sep 17 00:00:00 2001 From: johan Date: Sat, 23 Nov 2013 11:14:27 +0100 Subject: [PATCH 2/7] Lightpack device added based on hidapi Former-commit-id: 4d3d9c01b169991757587a67479094186d52e6e8 --- dependencies/build/hidapi/CMakeLists.txt | 4 +- dependencies/build/hidapi/hid-libusb.c | 30 +- libsrc/hyperion/CMakeLists.txt | 12 +- libsrc/hyperion/Hyperion.cpp | 10 + .../device/LedDeviceLightpack-hidapi.cpp | 270 ++++++++++++++++++ .../device/LedDeviceLightpack-hidapi.h | 107 +++++++ libsrc/hyperion/device/LedDeviceLightpack.cpp | 2 +- 7 files changed, 409 insertions(+), 26 deletions(-) create mode 100644 libsrc/hyperion/device/LedDeviceLightpack-hidapi.cpp create mode 100644 libsrc/hyperion/device/LedDeviceLightpack-hidapi.h diff --git a/dependencies/build/hidapi/CMakeLists.txt b/dependencies/build/hidapi/CMakeLists.txt index 8aa655d3..976ce80b 100644 --- a/dependencies/build/hidapi/CMakeLists.txt +++ b/dependencies/build/hidapi/CMakeLists.txt @@ -1,13 +1,11 @@ project(hidapi) #add libusb and pthreads -find_package(UDev REQUIRED) find_package(libusb-1.0 REQUIRED) find_package(Threads REQUIRED) include_directories( ../../include/hidapi - ${UDEV_INCLUDE_DIR} ${LIBUSB_1_INCLUDE_DIRS}) add_library(hidapi-libusb hid-libusb.c) @@ -15,4 +13,4 @@ add_library(hidapi-libusb hid-libusb.c) target_link_libraries(hidapi-libusb ${LIBUSB_1_LIBRARIES} #apt-get install libusb-1.0-0-dev ${CMAKE_THREAD_LIBS_INIT} - ${UDEV_LIBRARIES}) # apt-get install libudev-dev +) diff --git a/dependencies/build/hidapi/hid-libusb.c b/dependencies/build/hidapi/hid-libusb.c index 6c1d2471..e01e881b 100644 --- a/dependencies/build/hidapi/hid-libusb.c +++ b/dependencies/build/hidapi/hid-libusb.c @@ -20,7 +20,7 @@ files located at the root of the source distribution. These files may also be found in the public source code repository located at: - http://github.com/signal11/hidapi . + http://github.com/signal11/hidapi . ********************************************************/ #define _GNU_SOURCE /* needed for wcsdup() before glibc 2.10 */ @@ -167,9 +167,9 @@ static uint32_t get_bytes(uint8_t *rpt, size_t len, size_t num_bytes, size_t cur } else if (num_bytes == 4) { return (rpt[cur+4] * 0x01000000 + - rpt[cur+3] * 0x00010000 + - rpt[cur+2] * 0x00000100 + - rpt[cur+1] * 0x00000001); + rpt[cur+3] * 0x00010000 + + rpt[cur+2] * 0x00000100 + + rpt[cur+1] * 0x00000001); } else return 0; @@ -180,7 +180,7 @@ static uint32_t get_bytes(uint8_t *rpt, size_t len, size_t num_bytes, size_t cur Usage and Usage Page that it finds in the descriptor. The return value is 0 on success and -1 on failure. */ static int get_usage(uint8_t *report_descriptor, size_t size, - unsigned short *usage_page, unsigned short *usage) + unsigned short *usage_page, unsigned short *usage) { unsigned int i = 0; int size_code; @@ -473,7 +473,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, /* Check the VID/PID against the arguments */ if ((vendor_id == 0x0 || vendor_id == dev_vid) && - (product_id == 0x0 || product_id == dev_pid)) { + (product_id == 0x0 || product_id == dev_pid)) { struct hid_device_info *tmp; /* VID/PID match. Create the record. */ @@ -617,7 +617,7 @@ hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const cur_dev = devs; while (cur_dev) { if (cur_dev->vendor_id == vendor_id && - cur_dev->product_id == product_id) { + cur_dev->product_id == product_id) { if (serial_number) { if (wcscmp(serial_number, cur_dev->serial_number) == 0) { path_to_open = cur_dev->path; @@ -744,9 +744,9 @@ static void *read_thread(void *param) /* Break out of this loop only on fatal error.*/ if (res != LIBUSB_ERROR_BUSY && - res != LIBUSB_ERROR_TIMEOUT && - res != LIBUSB_ERROR_OVERFLOW && - res != LIBUSB_ERROR_INTERRUPTED) { + res != LIBUSB_ERROR_TIMEOUT && + res != LIBUSB_ERROR_OVERFLOW && + res != LIBUSB_ERROR_INTERRUPTED) { break; } } @@ -863,23 +863,23 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) endpoint. */ int is_interrupt = (ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) - == LIBUSB_TRANSFER_TYPE_INTERRUPT; + == LIBUSB_TRANSFER_TYPE_INTERRUPT; int is_output = (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) - == LIBUSB_ENDPOINT_OUT; + == LIBUSB_ENDPOINT_OUT; int is_input = (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) - == LIBUSB_ENDPOINT_IN; + == LIBUSB_ENDPOINT_IN; /* Decide whether to use it for intput or output. */ if (dev->input_endpoint == 0 && - is_interrupt && is_input) { + is_interrupt && is_input) { /* Use this endpoint for INPUT */ dev->input_endpoint = ep->bEndpointAddress; dev->input_ep_max_packet_size = ep->wMaxPacketSize; } if (dev->output_endpoint == 0 && - is_interrupt && is_output) { + is_interrupt && is_output) { /* Use this endpoint for OUTPUT */ dev->output_endpoint = ep->bEndpointAddress; } diff --git a/libsrc/hyperion/CMakeLists.txt b/libsrc/hyperion/CMakeLists.txt index ef129371..fdd33b6b 100644 --- a/libsrc/hyperion/CMakeLists.txt +++ b/libsrc/hyperion/CMakeLists.txt @@ -4,13 +4,10 @@ SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/hyperion) SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/hyperion) #add libusb and pthreads (required for the Lighpack usb device) -find_package(UDev REQUIRED) find_package(libusb-1.0 REQUIRED) -find_package(Threads REQUIRED) include_directories( - ${UDEV_INCLUDE_DIR} - ${LIBUSB_1_INCLUDE_DIRS}) + ${LIBUSB_1_INCLUDE_DIRS}) # for Lightpack device # Group the headers that go through the MOC compiler SET(Hyperion_QT_HEADERS @@ -40,6 +37,7 @@ SET(Hyperion_HEADERS ${CURRENT_SOURCE_DIR}/device/LedDeviceLpd8806.h ${CURRENT_SOURCE_DIR}/device/LedDeviceLightpack.h ${CURRENT_SOURCE_DIR}/device/LedDeviceMultiLightpack.h + ${CURRENT_SOURCE_DIR}/device/LedDeviceLightpack-hidapi.h ) SET(Hyperion_SOURCES @@ -64,6 +62,7 @@ SET(Hyperion_SOURCES ${CURRENT_SOURCE_DIR}/device/LedDeviceAdalight.cpp ${CURRENT_SOURCE_DIR}/device/LedDeviceLightpack.cpp ${CURRENT_SOURCE_DIR}/device/LedDeviceMultiLightpack.cpp + ${CURRENT_SOURCE_DIR}/device/LedDeviceLightpack-hidapi.cpp ) set(Hyperion_RESOURCES @@ -85,7 +84,6 @@ add_library(hyperion target_link_libraries(hyperion hyperion-utils serialport + hidapi-libusb ${QT_LIBRARIES} - ${LIBUSB_1_LIBRARIES} #apt-get install libusb-1.0-0-dev - ${CMAKE_THREAD_LIBS_INIT} - ${UDEV_LIBRARIES}) # apt-get install libudev-dev +) diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index dbfd123d..9f742544 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -20,6 +20,7 @@ #include "device/LedDeviceWs2801.h" #include "device/LedDeviceAdalight.h" #include "device/LedDeviceLightpack.h" +#include "device/LedDeviceLightpack-hidapi.h" #include "device/LedDeviceMultiLightpack.h" #include "LinearColorSmoothing.h" @@ -94,6 +95,15 @@ LedDevice* Hyperion::createDevice(const Json::Value& deviceConfig) device = deviceLightpack; } + else if (type == "lightpack-hidapi") + { + const std::string output = deviceConfig.get("output", "").asString(); + + LedDeviceLightpackHidapi* deviceLightpack = new LedDeviceLightpackHidapi(); + deviceLightpack->open(output); + + device = deviceLightpack; + } else if (type == "multi-lightpack") { LedDeviceMultiLightpack* deviceLightpack = new LedDeviceMultiLightpack(); diff --git a/libsrc/hyperion/device/LedDeviceLightpack-hidapi.cpp b/libsrc/hyperion/device/LedDeviceLightpack-hidapi.cpp new file mode 100644 index 00000000..e23f7148 --- /dev/null +++ b/libsrc/hyperion/device/LedDeviceLightpack-hidapi.cpp @@ -0,0 +1,270 @@ +// stl includes +#include +#include +#include + +// Local Hyperion includes +#include "LedDeviceLightpack-hidapi.h" + +// from USB_ID.h (http://code.google.com/p/light-pack/source/browse/CommonHeaders/USB_ID.h) +#define USB_OLD_VENDOR_ID 0x03EB +#define USB_OLD_PRODUCT_ID 0x204F +#define USB_VENDOR_ID 0x1D50 +#define USB_PRODUCT_ID 0x6022 + +#define LIGHTPACK_INTERFACE 0 + +// from commands.h (http://code.google.com/p/light-pack/source/browse/CommonHeaders/commands.h) +// Commands to device, sends it in first byte of data[] +enum COMMANDS{ + CMD_UPDATE_LEDS = 1, + CMD_OFF_ALL, + CMD_SET_TIMER_OPTIONS, + CMD_SET_PWM_LEVEL_MAX_VALUE, /* deprecated */ + CMD_SET_SMOOTH_SLOWDOWN, + CMD_SET_BRIGHTNESS, + + CMD_NOP = 0x0F +}; + +// from commands.h (http://code.google.com/p/light-pack/source/browse/CommonHeaders/commands.h) +enum DATA_VERSION_INDEXES{ + INDEX_FW_VER_MAJOR = 1, + INDEX_FW_VER_MINOR +}; + +LedDeviceLightpackHidapi::LedDeviceLightpackHidapi() : + LedDevice(), + _deviceHandle(nullptr), + _serialNumber(""), + _firmwareVersion({-1,-1}), + _ledCount(-1), + _bitsPerChannel(-1), + _ledBuffer() +{ +} + +LedDeviceLightpackHidapi::~LedDeviceLightpackHidapi() +{ + if (_deviceHandle != nullptr) + { + hid_close(_deviceHandle); + _deviceHandle = nullptr; + } + + // TODO: Should be called to avoid memory loss, but only at the end of the application + //hid_exit(); +} + +int LedDeviceLightpackHidapi::open(const std::string & serialNumber) +{ + // initialize the usb context + int error = hid_init(); + if (error != 0) + { + std::cerr << "Error while initializing the hidapi context" << std::endl; + return -1; + } + std::cout << "Hidapi initialized" << std::endl; + + // retrieve the list of usb devices + hid_device_info * deviceList = hid_enumerate(0x0, 0x0); + + // iterate the list of devices + for (hid_device_info * deviceInfo = deviceList; deviceInfo != nullptr; deviceInfo = deviceInfo->next) + { + // try to open and initialize the device + error = testAndOpen(deviceInfo, serialNumber); + + if (error == 0) + { + // a device was sucessfully opened. break from list + break; + } + } + + // free the device list + hid_free_enumeration(deviceList); + + if (_deviceHandle == nullptr) + { + if (_serialNumber.empty()) + { + std::cerr << "No Lightpack device has been found" << std::endl; + } + else + { + std::cerr << "No Lightpack device has been found with serial " << _serialNumber << std::endl; + } + } + + return _deviceHandle == nullptr ? -1 : 0; +} + +int LedDeviceLightpackHidapi::testAndOpen(hid_device_info *device, const std::string & requestedSerialNumber) +{ + if ((device->vendor_id == USB_VENDOR_ID && device->product_id == USB_PRODUCT_ID) || + (device->vendor_id == USB_OLD_VENDOR_ID && device->product_id == USB_OLD_PRODUCT_ID)) + { + std::cout << "Found a lightpack device. Retrieving more information..." << std::endl; + + // get the serial number + std::string serialNumber = ""; + if (device->serial_number != nullptr) + { + // the serial number needs to be converted to a char array instead of wchar + size_t size = wcslen(device->serial_number); + serialNumber.resize(size, '.'); + for (size_t i = 0; i < size; ++i) + { + int c = wctob(device->serial_number[i]); + if (c != EOF) + { + serialNumber[i] = c; + } + } + } + else + { + std::cerr << "No serial number for Lightpack device" << std::endl; + } + + std::cout << "Lightpack device found: path=" << device->path << " serial=" << serialNumber << std::endl; + + // check if this is the device we are looking for + if (requestedSerialNumber.empty() || requestedSerialNumber == serialNumber) + { + // This is it! + _deviceHandle = hid_open_path(device->path); + + if (_deviceHandle != nullptr) + { + _serialNumber = serialNumber; + + std::cout << "Lightpack device successfully opened" << std::endl; + + // get the firmware version + uint8_t buffer[256]; + buffer[0] = 0; // report id + int error = hid_get_feature_report(_deviceHandle, buffer, sizeof(buffer)); + if (error < 4) + { + std::cerr << "Unable to retrieve firmware version number from Lightpack device" << std::endl; + } + else + { + _firmwareVersion.majorVersion = buffer[INDEX_FW_VER_MAJOR+1]; + _firmwareVersion.minorVersion = buffer[INDEX_FW_VER_MINOR+1]; + } + + // FOR TESTING PURPOSE: FORCE MAJOR VERSION TO 6 + _firmwareVersion.majorVersion = 6; + + // disable smoothing of the chosen device + disableSmoothing(); + + // determine the number of leds + if (_firmwareVersion.majorVersion == 4) + { + _ledCount = 8; + } + else + { + _ledCount = 10; + } + + // determine the bits per channel + if (_firmwareVersion.majorVersion == 6) + { + // maybe also or version 7? The firmware suggest this is only for 6... (2013-11-13) + _bitsPerChannel = 12; + } + else + { + _bitsPerChannel = 8; + } + + // set the led buffer size (repport id + command + 6 bytes per led) + _ledBuffer = std::vector(2 + _ledCount * 6, 0); + _ledBuffer[0] = 0x0; // report id + _ledBuffer[1] = CMD_UPDATE_LEDS; + + // return success + std::cout << "Lightpack device opened: path=" << device->path << " serial=" << _serialNumber << " version=" << _firmwareVersion.majorVersion << "." << _firmwareVersion.minorVersion << std::endl; + return 0; + } + else + { + std::cerr << "Unable to open Lightpack device. Searching for other device" << std::endl; + } + } + } + + return -1; +} + +int LedDeviceLightpackHidapi::write(const std::vector &ledValues) +{ + return write(ledValues.data(), ledValues.size()); +} + +int LedDeviceLightpackHidapi::write(const ColorRgb * ledValues, int size) +{ + int count = std::min(_ledCount, size); + + for (int i = 0; i < count ; ++i) + { + const ColorRgb & color = ledValues[i]; + + // copy the most significant bits of the rgb values to the first three bytes + // offset 1 to accomodate for the report id and command byte + _ledBuffer[6*i+2] = color.red; + _ledBuffer[6*i+3] = color.green; + _ledBuffer[6*i+4] = color.blue; + + // leave the next three bytes on zero... + // 12-bit values having zeros in the lowest 4 bits which is almost correct, but it saves extra + // switches to determine what to do and some bit shuffling + } + + int error = writeBytes(_ledBuffer.data(), _ledBuffer.size()); + return error >= 0 ? 0 : error; +} + +int LedDeviceLightpackHidapi::switchOff() +{ + unsigned char buf[2] = {0x0, CMD_OFF_ALL}; + return writeBytes(buf, sizeof(buf)) == sizeof(buf); +} + +const std::string &LedDeviceLightpackHidapi::getSerialNumber() const +{ + return _serialNumber; +} + +int LedDeviceLightpackHidapi::getLedCount() const +{ + return _ledCount; +} + +int LedDeviceLightpackHidapi::writeBytes(uint8_t *data, int size) +{ +// std::cout << "Writing " << size << " bytes: "; +// for (int i = 0; i < size ; ++i) printf("%02x ", data[i]); +// std::cout << std::endl; + + int error = hid_send_feature_report(_deviceHandle, data, size); + if (error == size) + { + return 0; + } + + std::cerr << "Unable to write " << size << " bytes to Lightpack device(" << error << ")" << std::endl; + return error; +} + +int LedDeviceLightpackHidapi::disableSmoothing() +{ + unsigned char buf[2] = {CMD_SET_SMOOTH_SLOWDOWN, 0}; + return writeBytes(buf, sizeof(buf)) == sizeof(buf); +} diff --git a/libsrc/hyperion/device/LedDeviceLightpack-hidapi.h b/libsrc/hyperion/device/LedDeviceLightpack-hidapi.h new file mode 100644 index 00000000..7b25dfb9 --- /dev/null +++ b/libsrc/hyperion/device/LedDeviceLightpack-hidapi.h @@ -0,0 +1,107 @@ +#pragma once + +// stl includes +#include +#include +#include + +// libusb include +#include + +// Hyperion includes +#include + +/// +/// LedDevice implementation for a lightpack device (http://code.google.com/p/light-pack/) +/// +class LedDeviceLightpackHidapi : public LedDevice +{ +public: + /// + /// Constructs the LedDeviceLightpack + /// + LedDeviceLightpackHidapi(); + + /// + /// Destructor of the LedDevice; closes the output device if it is open + /// + virtual ~LedDeviceLightpackHidapi(); + + /// + /// Opens and configures the output device + /// + /// @return Zero on succes else negative + /// + int open(const std::string & serialNumber = ""); + + /// + /// Writes the RGB-Color values to the leds. + /// + /// @param[in] ledValues The RGB-color per led + /// + /// @return Zero on success else negative + /// + virtual int write(const std::vector& ledValues); + + /// + /// Writes the RGB-Color values to the leds. + /// + /// @param[in] ledValues Array of RGB values + /// @param[in] size The number of RGB values + /// + /// @return Zero on success else negative + /// + int write(const ColorRgb * ledValues, int size); + + /// + /// Switch the leds off + /// + /// @return Zero on success else negative + /// + virtual int switchOff(); + + /// Get the serial of the Lightpack + const std::string & getSerialNumber() const; + + /// Get the number of leds + int getLedCount() const; + +private: + /// + /// Test if the device is a (or the) lightpack we are looking for + /// + /// @return Zero on succes else negative + /// + int testAndOpen(hid_device_info * device, const std::string & requestedSerialNumber); + + /// write bytes to the device + int writeBytes(uint8_t *data, int size); + + /// Disable the internal smoothing on the Lightpack device + int disableSmoothing(); + + struct Version + { + int majorVersion; + int minorVersion; + }; + +private: + /// libusb device handle + hid_device * _deviceHandle; + + /// device serial number + std::string _serialNumber; + + /// firmware version of the device + Version _firmwareVersion; + + /// the number of leds of the device + int _ledCount; + + /// the number of bits per channel + int _bitsPerChannel; + + /// buffer for led data + std::vector _ledBuffer; +}; diff --git a/libsrc/hyperion/device/LedDeviceLightpack.cpp b/libsrc/hyperion/device/LedDeviceLightpack.cpp index 1dd262ff..0deac582 100644 --- a/libsrc/hyperion/device/LedDeviceLightpack.cpp +++ b/libsrc/hyperion/device/LedDeviceLightpack.cpp @@ -279,7 +279,7 @@ int LedDeviceLightpack::writeBytes(uint8_t *data, int size) int error = libusb_control_transfer(_deviceHandle, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, - LIBUSB_REQUEST_SET_CONFIGURATION, + 0x09, (2 << 8), 0x00, data, size, 1000); From 2bbd4cf0814d24ae114b17dbc6ca65f289c76378 Mon Sep 17 00:00:00 2001 From: johan Date: Sat, 23 Nov 2013 12:24:37 +0100 Subject: [PATCH 3/7] New release with Lightpack hidapi device Former-commit-id: 2dcce72d8e7714819df600c21d44dc3989dcee84 --- deploy/hyperion-remote | Bin 244044 -> 244044 bytes deploy/hyperiond.REMOVED.git-id | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/hyperion-remote b/deploy/hyperion-remote index e1720d47dc107d9bd4dfaf620527cf585efc76bf..2d4a01e9655c2a4eb25d667d629e5741aaf5e1c2 100755 GIT binary patch delta 40 wcmX@}iSNuOzJ?aYElf*h2pAbF7#SEEGZ+|H8ChBxnznD5!L)tL4CWaM05;_gBLDyZ delta 40 wcmX@}iSNuOzJ?aYElf*h2pC!_7#SEEGZ-0J8Jk!c8n Date: Sun, 24 Nov 2013 13:37:30 +0100 Subject: [PATCH 4/7] Added XBian install Former-commit-id: 5e00e5ff99c57a31a1861cb2ebe6e03da2bc6e2f --- bin/install_hyperion.sh | 28 +++++++++++++++++++--------- bin/update_hyperion.sh | 38 ++++++++++++++++++++++++++++---------- deploy/hyperion.xbian.conf | 16 ++++++++++++++++ 3 files changed, 63 insertions(+), 19 deletions(-) create mode 100644 deploy/hyperion.xbian.conf diff --git a/bin/install_hyperion.sh b/bin/install_hyperion.sh index ad6b5080..7d59d68d 100755 --- a/bin/install_hyperion.sh +++ b/bin/install_hyperion.sh @@ -2,6 +2,9 @@ # Script for downloading and installing the latest Hyperion release +# Find out if we are on XBian +IS_XBIAN=`cat /etc/issue | grep XBian | wc -l` + # Make sure that the boblight daemon is no longer running BOBLIGHT_PROCNR=$(ps -e | grep "boblight" | wc -l) if [ $BOBLIGHT_PROCNR -eq 1 ]; @@ -11,24 +14,31 @@ then fi # Stop hyperion daemon if it is running -initctl stop hyperion +/sbin/initctl stop hyperion +# Get the Hyperion executable wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperiond -P /usr/bin/ +chmod +x /usr/bin/hyperiond + +# Get the Hyperion command line utility wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperion-remote -P /usr/bin/ +chmod +x /usr/bin/hyperion-remote # Copy the gpio changer (gpio->spi) to the /usr/bin -wget -N github.com/tvdzwan/hyperion/raw/master/deploy/gpio2spi -P /usr/bin/ +if [ $IS_XBIAN -eq 0 ]; then + wget -N github.com/tvdzwan/hyperion/raw/master/deploy/gpio2spi -P /usr/bin/ + chmod +x /usr/bin/gpio2spi +fi # Copy the hyperion configuration file to /etc wget -N github.com/tvdzwan/hyperion/raw/master/config/hyperion.config.json -P /etc/ # Copy the service control configuration to /etc/int -wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperion.conf -P /etc/init/ - -# Set permissions -chmod +x /usr/bin/hyperiond -chmod +x /usr/bin/hyperion-remote -chmod +x /usr/bin/gpio2spi +if [ $IS_XBIAN -eq 0 ]; then + wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperion.conf -P /etc/init/ +else + wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperion.xbian.conf -P /etc/init/ -O hyperion.conf +fi # Start the hyperion daemon -initctl start hyperion +/sbin/initctl start hyperion diff --git a/bin/update_hyperion.sh b/bin/update_hyperion.sh index 21d039ef..8e1996b5 100755 --- a/bin/update_hyperion.sh +++ b/bin/update_hyperion.sh @@ -2,22 +2,40 @@ # Script for downloading and installing the latest Hyperion release -# Stop hyperion daemon if it is running -initctl stop hyperion +# Find out if we are on XBian +IS_XBIAN=`cat /etc/issue | grep XBian | wc -l` +# Make sure that the boblight daemon is no longer running +BOBLIGHT_PROCNR=$(ps -e | grep "boblight" | wc -l) +if [ $BOBLIGHT_PROCNR -eq 1 ]; +then + echo 'Found running instance of boblight. Please stop boblight via XBMC menu before installing hyperion' + exit +fi + +# Stop hyperion daemon if it is running +/sbin/initctl stop hyperion + +# Get the Hyperion executable wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperiond -P /usr/bin/ +chmod +x /usr/bin/hyperiond + +# Get the Hyperion command line utility wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperion-remote -P /usr/bin/ +chmod +x /usr/bin/hyperion-remote # Copy the gpio changer (gpio->spi) to the /usr/bin -wget -N github.com/tvdzwan/hyperion/raw/master/deploy/gpio2spi -P /usr/bin/ +if [ $IS_XBIAN -eq 0 ]; then + wget -N github.com/tvdzwan/hyperion/raw/master/deploy/gpio2spi -P /usr/bin/ + chmod +x /usr/bin/gpio2spi +fi # Copy the service control configuration to /etc/int -wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperion.conf -P /etc/init/ - -# Set permissions -chmod +x /usr/bin/hyperiond -chmod +x /usr/bin/hyperion-remote -chmod +x /usr/bin/gpio2spi +if [ $IS_XBIAN -eq 0 ]; then + wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperion.conf -P /etc/init/ +else + wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperion.xbian.conf -P /etc/init/ -O hyperion.conf +fi # Start the hyperion daemon -initctl start hyperion +/sbin/initctl start hyperion diff --git a/deploy/hyperion.xbian.conf b/deploy/hyperion.xbian.conf new file mode 100644 index 00000000..fa14a327 --- /dev/null +++ b/deploy/hyperion.xbian.conf @@ -0,0 +1,16 @@ +## Hyperion daemon + +description "hyperion" +author "poljvd & tvdzwan" + +start on (runlevel [2345]) +stop on (runlevel [!2345]) + +respawn + +pre-start script +modprobe spi-bcm2708 +end script + +exec /usr/bin/hyperiond /etc/hyperion.config.json + From f892a8c09abdb2b4a69f816d6f5d95d4352262ee Mon Sep 17 00:00:00 2001 From: johan Date: Sun, 24 Nov 2013 14:25:22 +0100 Subject: [PATCH 5/7] Fix XBian install Former-commit-id: bd674eb7413005ebbf06e4e967f28d2b611b74b4 --- bin/install_hyperion.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/install_hyperion.sh b/bin/install_hyperion.sh index 7d59d68d..a0d6bd5c 100755 --- a/bin/install_hyperion.sh +++ b/bin/install_hyperion.sh @@ -37,7 +37,7 @@ wget -N github.com/tvdzwan/hyperion/raw/master/config/hyperion.config.json -P /e if [ $IS_XBIAN -eq 0 ]; then wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperion.conf -P /etc/init/ else - wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperion.xbian.conf -P /etc/init/ -O hyperion.conf + wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperion.xbian.conf -O /etc/init/hyperion.conf fi # Start the hyperion daemon From 983ce303314f339928a093d47fd90a48b082f904 Mon Sep 17 00:00:00 2001 From: Johan Date: Mon, 25 Nov 2013 16:55:59 +0100 Subject: [PATCH 6/7] Removed Lightpack based on hidapi from build (not working) Former-commit-id: b3882807daf5993073a7af811db50fe3d0a45c28 --- libsrc/hyperion/CMakeLists.txt | 11 ++++++++--- libsrc/hyperion/Hyperion.cpp | 10 ---------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/libsrc/hyperion/CMakeLists.txt b/libsrc/hyperion/CMakeLists.txt index fdd33b6b..bd0aa06a 100644 --- a/libsrc/hyperion/CMakeLists.txt +++ b/libsrc/hyperion/CMakeLists.txt @@ -4,9 +4,14 @@ SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/hyperion) SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/hyperion) #add libusb and pthreads (required for the Lighpack usb device) +#find_package(UDev REQUIRED) find_package(libusb-1.0 REQUIRED) +find_package(Threads REQUIRED) +#find_package(UDev REQUIRED) include_directories( + ../../include/hidapi +# ${UDEV_INCLUDE_DIR} ${LIBUSB_1_INCLUDE_DIRS}) # for Lightpack device # Group the headers that go through the MOC compiler @@ -37,7 +42,6 @@ SET(Hyperion_HEADERS ${CURRENT_SOURCE_DIR}/device/LedDeviceLpd8806.h ${CURRENT_SOURCE_DIR}/device/LedDeviceLightpack.h ${CURRENT_SOURCE_DIR}/device/LedDeviceMultiLightpack.h - ${CURRENT_SOURCE_DIR}/device/LedDeviceLightpack-hidapi.h ) SET(Hyperion_SOURCES @@ -62,7 +66,6 @@ SET(Hyperion_SOURCES ${CURRENT_SOURCE_DIR}/device/LedDeviceAdalight.cpp ${CURRENT_SOURCE_DIR}/device/LedDeviceLightpack.cpp ${CURRENT_SOURCE_DIR}/device/LedDeviceMultiLightpack.cpp - ${CURRENT_SOURCE_DIR}/device/LedDeviceLightpack-hidapi.cpp ) set(Hyperion_RESOURCES @@ -84,6 +87,8 @@ add_library(hyperion target_link_libraries(hyperion hyperion-utils serialport - hidapi-libusb + ${LIBUSB_1_LIBRARIES} #apt-get install libusb-1.0-0-dev + ${CMAKE_THREAD_LIBS_INIT} +# ${UDEV_LIBRARIES} # apt-get install libudev-dev ${QT_LIBRARIES} ) diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index 9f742544..dbfd123d 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -20,7 +20,6 @@ #include "device/LedDeviceWs2801.h" #include "device/LedDeviceAdalight.h" #include "device/LedDeviceLightpack.h" -#include "device/LedDeviceLightpack-hidapi.h" #include "device/LedDeviceMultiLightpack.h" #include "LinearColorSmoothing.h" @@ -95,15 +94,6 @@ LedDevice* Hyperion::createDevice(const Json::Value& deviceConfig) device = deviceLightpack; } - else if (type == "lightpack-hidapi") - { - const std::string output = deviceConfig.get("output", "").asString(); - - LedDeviceLightpackHidapi* deviceLightpack = new LedDeviceLightpackHidapi(); - deviceLightpack->open(output); - - device = deviceLightpack; - } else if (type == "multi-lightpack") { LedDeviceMultiLightpack* deviceLightpack = new LedDeviceMultiLightpack(); From 6525e8b479eb96ba9502024cdb93afb5c6fc071c Mon Sep 17 00:00:00 2001 From: Johan Date: Thu, 28 Nov 2013 11:06:49 +0100 Subject: [PATCH 7/7] Added option to enable libudev in build (gives erros on RPi, but need on Ubuntu 13.10) Former-commit-id: e3654fc4d9ff1e129c21247f1be21ad9599053fe --- libsrc/hyperion/CMakeLists.txt | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/libsrc/hyperion/CMakeLists.txt b/libsrc/hyperion/CMakeLists.txt index bd0aa06a..5462fc85 100644 --- a/libsrc/hyperion/CMakeLists.txt +++ b/libsrc/hyperion/CMakeLists.txt @@ -4,16 +4,22 @@ SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/hyperion) SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/hyperion) #add libusb and pthreads (required for the Lighpack usb device) -#find_package(UDev REQUIRED) find_package(libusb-1.0 REQUIRED) find_package(Threads REQUIRED) -#find_package(UDev REQUIRED) include_directories( ../../include/hidapi -# ${UDEV_INCLUDE_DIR} ${LIBUSB_1_INCLUDE_DIRS}) # for Lightpack device +# set the build options +option (LIBUDEV_REQUIRED "Add libudev to the build (needed on Ubuntu 13.10, but gives errors when added on the RPi) :-S" OFF) +message(STATUS "LIBUDEV_REQUIRED = " ${LIBUDEV_REQUIRED}) + +if (LIBUDEV_REQUIRED) + find_package(UDev REQUIRED) + include_directories(${UDEV_INCLUDE_DIR}) +endif (LIBUDEV_REQUIRED) + # Group the headers that go through the MOC compiler SET(Hyperion_QT_HEADERS ${CURRENT_HEADER_DIR}/Hyperion.h @@ -89,6 +95,11 @@ target_link_libraries(hyperion serialport ${LIBUSB_1_LIBRARIES} #apt-get install libusb-1.0-0-dev ${CMAKE_THREAD_LIBS_INIT} -# ${UDEV_LIBRARIES} # apt-get install libudev-dev ${QT_LIBRARIES} ) + +if (LIBUDEV_REQUIRED) + target_link_libraries(hyperion + ${UDEV_LIBRARIES} # apt-get install libudev-dev + ) +endif (LIBUDEV_REQUIRED)