diff --git a/.gitignore b/.gitignore index bc0410f0..b43ca58c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,7 @@ Makefile.in aclocal.m4 autom4te.cache/ -config.guess -config.h.in -config.sub +config.* configure depcomp install-sh @@ -18,8 +16,5 @@ testgui/Makefile.in windows/Makefile.in Makefile -config.h -config.log -config.status stamp-h1 libtool diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..82193921 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,74 @@ +cmake_minimum_required(VERSION 2.8) + +project(hidapi) + +set(VERSION_MAJOR "0") +set(VERSION_MINOR "7") +set(VERSION_PATCH "1") +set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") + + +option(DEBUG_PARSER "verbose parser debuggint output" OFF) + +option(LIBUSB "use libusb backend" OFF) +option(HIDRAW "use hidraw backend (linux/freebsd)" ON) + +option(EXAMPLE_TEST "build test example" ON) +option(EXAMPLE_OSC "build osc example" ON) + + +# add our own cmake-modules +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake_modules/") + +if( DEBUG_PARSER ) + add_definitions( -DDEBUG_PARSER ) +endif() + +# some default libraries +if (NOT WIN32) + find_package(Pthreads) + if (NOT PTHREADS_FOUND) + message(SEND_ERROR "cannot find libpthreads") + endif() +endif() + + +# directories +add_subdirectory(hidapi) + +if(LIBUSB) + add_subdirectory(libusb) +# set( hidapi_source ${hidapi_SOURCE_DIR}/libusb ) + link_directories( ${LIBUSB_1_LIBRARIES} ${PTHREADS_LIBRARIES} ) +endif() + +if(${CMAKE_SYSTEM_NAME} MATCHES "Linux" AND HIDRAW) + add_subdirectory(linux) +# set( hidapi_source ${hidapi_SOURCE_DIR}/linux ) + link_directories( ${UDEV_LIBRARIES} ) +endif() + +if(WIN32) + add_subdirectory(windows) +# set( hidapi_source ${hidapi_SOURCE_DIR}/windows ) + #todo: add library dependencies +endif() +if(APPLE) + add_subdirectory(mac) +# set( hidapi_source ${hidapi_SOURCE_DIR}/mac ) + #todo: add library dependencies - TEST + link_directories("-framework IOKit -framework CoreFoundation") +endif() + +add_subdirectory(hidapi_parser) + +# message( "main: hidapi source dir: ${hidapi_source}" ) + +if( EXAMPLE_TEST ) + add_subdirectory(hidtest) + add_subdirectory(hidparsertest) +endif() + +if( EXAMPLE_OSC ) + add_subdirectory(hidapi2osc) +endif() \ No newline at end of file diff --git a/Makefile.am b/Makefile.am index cf4f7ca4..51c02f64 100644 --- a/Makefile.am +++ b/Makefile.am @@ -31,8 +31,12 @@ if OS_WINDOWS SUBDIRS += windows endif +# SUBDIRS += hidapi_parser +SUBDIRS += hidparsertest +SUBDIRS += hidapi2osc SUBDIRS += hidtest + if BUILD_TESTGUI SUBDIRS += testgui endif diff --git a/README.txt b/README.txt index 9297fb6e..1012493a 100644 --- a/README.txt +++ b/README.txt @@ -1,3 +1,34 @@ + HIDAPI + parser library for Windows, Linux, FreeBSD and Mac OS X + ========================================================= +This version [1] is an extension of the original HIDAPI library, based on Tony Roggs fork [2], +which reads the report descriptor of the device. + +The main addition is a parser which parses the retrieved report descriptor, populates a struct +with the element descriptors, and finally provides a data parsing function based on this. + +In addition, a CMake build system has been made. + +Some test examples are available: +* hidparsertest will list all devices and optionally open one, displaying the element information, and the incoming data +* hidapi2osc will send out the data via OSC (OpenSoundControl), and provides an OSC interface for listing, opening and closing devices (see the supercollider script for testing the interface) + +[1] https://github.com/sensestage/hidapi +[2] https://github.com/tonyrog/hidapi + +TODO: +- creating + sending output reports +- creating + sending/reading + parsing feature reports +- reading windows report descriptor +- cmake lists for OSX, Windows and FreeBSD + + + Copyright (C) 2013, Marije Baalman + This work was funded by a crowd-funding initiative for SuperCollider's [1] HID implementation + including a substantial donation from BEK, Bergen Center for Electronic Arts, Norway + + [1] http://supercollider.sourceforge.net + [2] http://www.bek.no + HIDAPI library for Windows, Linux, FreeBSD and Mac OS X ========================================================= @@ -154,7 +185,7 @@ Prerequisites: If you downloaded the source directly from the git repository (using git clone), you'll need Autotools: - sudo apt-get install autotools-dev + sudo apt-get install autotools-dev autoconf automake libtool FreeBSD: --------- diff --git a/cmake_modules/FindIConv.cmake b/cmake_modules/FindIConv.cmake new file mode 100644 index 00000000..76f27696 --- /dev/null +++ b/cmake_modules/FindIConv.cmake @@ -0,0 +1,43 @@ +# Find iconv library +# +# Author: Eddy Xu +# +# Released under BSD license +# +# ICONV_INCLUDE_DIRS - where to find iconv.h, etc +# ICONV_LIBRARIES - Lists of libraries when using iconv +# ICONV_FOUND - True if iconv found + + +# Look for the header file +FIND_PATH( ICONV_INCLUDE_DIR NAMES iconv.h ) +MARK_AS_ADVANCED( ICONV_INCLUDE_DIR ) + +# Look for the library +FIND_LIBRARY( ICONV_LIBRARY NAMES iconv ) +MARK_AS_ADVANCED( ICONV_LIBRARY ) + +# Copy the result to output variables +IF(ICONV_INCLUDE_DIR AND ICONV_LIBRARY) + SET(ICONV_FOUND 1) + SET(ICONV_LIBRARIES ${ICONV_LIBRARY}) + SET(ICONV_INCLUDE_DIRS ${ICONV_INCLUDE_DIR}) +ELSE(ICONV_INCLUDE_DIR AND ICONV_LIBRARY) + SET(ICONV_FOUND 0) + SET(ICONV_LIBRARIES) + SET(ICONV_INCLUDE_DIRS) +ENDIF(ICONV_INCLUDE_DIR AND ICONV_LIBRARY) + +# Report results +IF(NOT ICONV_FOUND) + SET(ICONV_DIR_MESSAGE + "Iconv was not found. Make sure ICONV_LIBRARY and ICONV_INCLUDE_DIR are +set.") + IF(NOT ICONV_FIND_QUIETLY) + MESSAGE(STATUS ${ICONV_DIR_MESSAGE}) + ELSE(NOT ICONV_FIND_QUIETLY) + IF(ICONV_FIND_REQUIRED) + MESSAGE(FETAL_ERROR ${ICONV_DIR_MESSAGE}) + ENDIF(ICONV_FIND_REQUIRED) + ENDIF(NOT ICONV_FIND_QUIETLY) +ENDIF(NOT ICONV_FOUND) diff --git a/cmake_modules/FindPthreads.cmake b/cmake_modules/FindPthreads.cmake new file mode 100644 index 00000000..1b5683c5 --- /dev/null +++ b/cmake_modules/FindPthreads.cmake @@ -0,0 +1,104 @@ +# - Find the Pthreads library +# This module searches for the Pthreads library (including the +# pthreads-win32 port). +# +# This module defines these variables: +# +# PTHREADS_FOUND +# True if the Pthreads library was found +# PTHREADS_LIBRARY +# The location of the Pthreads library +# PTHREADS_INCLUDE_DIR +# The include directory of the Pthreads library +# PTHREADS_DEFINITIONS +# Preprocessor definitions to define (HAVE_PTHREAD_H is a fairly common +# one) +# +# This module responds to the PTHREADS_EXCEPTION_SCHEME +# variable on Win32 to allow the user to control the +# library linked against. The Pthreads-win32 port +# provides the ability to link against a version of the +# library with exception handling. IT IS NOT RECOMMENDED +# THAT YOU CHANGE PTHREADS_EXCEPTION_SCHEME TO ANYTHING OTHER THAN +# "C" because most POSIX thread implementations do not support stack +# unwinding. +# +# PTHREADS_EXCEPTION_SCHEME +# C = no exceptions (default) +# (NOTE: This is the default scheme on most POSIX thread +# implementations and what you should probably be using) +# CE = C++ Exception Handling +# SE = Structure Exception Handling (MSVC only) +# + +# +# Define a default exception scheme to link against +# and validate user choice. +# + +IF(PTHREADS_INCLUDE_DIR AND PTHREADS_LIBRARY) + SET(PTHREADS_DEFINITIONS -DHAVE_PTHREAD_H) + SET(PTHREADS_INCLUDE_DIRS ${PTHREADS_INCLUDE_DIR}) + SET(PTHREADS_LIBRARIES ${PTHREADS_LIBRARY}) + SET(FOUND_PTHREADS TRUE) +ENDIF() + +IF(NOT DEFINED PTHREADS_EXCEPTION_SCHEME) + # Assign default if needed + SET(PTHREADS_EXCEPTION_SCHEME "C") +ELSE(NOT DEFINED PTHREADS_EXCEPTION_SCHEME) + # Validate + IF(NOT PTHREADS_EXCEPTION_SCHEME STREQUAL "C" AND + NOT PTHREADS_EXCEPTION_SCHEME STREQUAL "CE" AND + NOT PTHREADS_EXCEPTION_SCHEME STREQUAL "SE") + + MESSAGE(FATAL_ERROR "See documentation for FindPthreads.cmake, only C, CE, and SE modes are allowed") + + ENDIF(NOT PTHREADS_EXCEPTION_SCHEME STREQUAL "C" AND + NOT PTHREADS_EXCEPTION_SCHEME STREQUAL "CE" AND + NOT PTHREADS_EXCEPTION_SCHEME STREQUAL "SE") + + IF(NOT MSVC AND PTHREADS_EXCEPTION_SCHEME STREQUAL "SE") + MESSAGE(FATAL_ERROR "Structured Exception Handling is only allowed for MSVC") + ENDIF(NOT MSVC AND PTHREADS_EXCEPTION_SCHEME STREQUAL "SE") + +ENDIF(NOT DEFINED PTHREADS_EXCEPTION_SCHEME) + +# +# Find the header file +# +FIND_PATH(PTHREADS_INCLUDE_DIR pthread.h) + +# +# Find the library +# +SET(names) +IF(MSVC) + SET(names + pthreadV${PTHREADS_EXCEPTION_SCHEME}2 + pthread + ) +ELSEIF(MINGW) + SET(names + pthreadG${PTHREADS_EXCEPTION_SCHEME}2 + pthread + ) +ELSE(MSVC) # Unix / Cygwin / Apple / Etc. + SET(names pthread) +ENDIF(MSVC) + +FIND_LIBRARY(PTHREADS_LIBRARY NAMES ${names} + DOC "The Portable Threads Library") + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Pthreads DEFAULT_MSG + PTHREADS_LIBRARY PTHREADS_INCLUDE_DIR) + +IF(PTHREADS_INCLUDE_DIR AND PTHREADS_LIBRARY) + SET(PTHREADS_DEFINITIONS -DHAVE_PTHREAD_H) + SET(PTHREADS_INCLUDE_DIRS ${PTHREADS_INCLUDE_DIR}) + SET(PTHREADS_LIBRARIES ${PTHREADS_LIBRARY}) +ENDIF(PTHREADS_INCLUDE_DIR AND PTHREADS_LIBRARY) + +MARK_AS_ADVANCED(PTHREADS_INCLUDE_DIR) +MARK_AS_ADVANCED(PTHREADS_LIBRARY) diff --git a/cmake_modules/FindUDev.cmake b/cmake_modules/FindUDev.cmake new file mode 100644 index 00000000..9d68dda6 --- /dev/null +++ b/cmake_modules/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) \ No newline at end of file diff --git a/cmake_modules/Findlibusb-1.0.cmake b/cmake_modules/Findlibusb-1.0.cmake new file mode 100644 index 00000000..405ed516 --- /dev/null +++ b/cmake_modules/Findlibusb-1.0.cmake @@ -0,0 +1,98 @@ +# - Try to find libusb-1.0 +# Once done this will define +# +# LIBUSB_1_FOUND - system has libusb +# LIBUSB_1_INCLUDE_DIRS - the libusb include directory +# LIBUSB_1_LIBRARIES - Link these to use libusb +# LIBUSB_1_DEFINITIONS - Compiler switches required for using libusb +# +# Adapted from cmake-modules Google Code project +# +# Copyright (c) 2006 Andreas Schneider +# +# (Changes for libusb) Copyright (c) 2008 Kyle Machulis +# +# Redistribution and use is allowed according to the terms of the New BSD license. +# +# CMake-Modules Project New BSD License +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of the CMake-Modules Project nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + + +if (LIBUSB_1_LIBRARIES AND LIBUSB_1_INCLUDE_DIRS) + # in cache already + set(LIBUSB_FOUND TRUE) +else (LIBUSB_1_LIBRARIES AND LIBUSB_1_INCLUDE_DIRS) + find_path(LIBUSB_1_INCLUDE_DIR + NAMES + libusb-1.0/libusb.h + PATHS + /usr/include + /usr/local/include + /opt/local/include + /sw/include + PATH_SUFFIXES + libusb-1.0 + ) + + find_library(LIBUSB_1_LIBRARY + NAMES + usb-1.0 + PATHS + /usr/lib + /usr/local/lib + /opt/local/lib + /sw/lib + ) + + set(LIBUSB_1_INCLUDE_DIRS + ${LIBUSB_1_INCLUDE_DIR} + ) + set(LIBUSB_1_LIBRARIES + ${LIBUSB_1_LIBRARY} +) + + if (LIBUSB_1_INCLUDE_DIRS AND LIBUSB_1_LIBRARIES) + set(LIBUSB_1_FOUND TRUE) + endif (LIBUSB_1_INCLUDE_DIRS AND LIBUSB_1_LIBRARIES) + + if (LIBUSB_1_FOUND) + if (NOT libusb_1_FIND_QUIETLY) + message(STATUS "Found libusb-1.0:") + message(STATUS " - Includes: ${LIBUSB_1_INCLUDE_DIRS}") + message(STATUS " - Libraries: ${LIBUSB_1_LIBRARIES}") + endif (NOT libusb_1_FIND_QUIETLY) + else (LIBUSB_1_FOUND) + if (libusb_1_FIND_REQUIRED) + message(FATAL_ERROR "Could not find libusb") + endif (libusb_1_FIND_REQUIRED) + endif (LIBUSB_1_FOUND) + + # show the LIBUSB_1_INCLUDE_DIRS and LIBUSB_1_LIBRARIES variables only in the advanced view + mark_as_advanced(LIBUSB_1_INCLUDE_DIRS LIBUSB_1_LIBRARIES) + +endif (LIBUSB_1_LIBRARIES AND LIBUSB_1_INCLUDE_DIRS) \ No newline at end of file diff --git a/configure.ac b/configure.ac index f926f8e0..5e69e988 100644 --- a/configure.ac +++ b/configure.ac @@ -68,7 +68,7 @@ case $host in # HIDAPI/libusb libs AC_CHECK_LIB([rt], [clock_gettime], [LIBS_LIBUSB_PRIVATE+=" -lrt"], [hidapi_lib_error librt]) - PKG_CHECK_MODULES([libusb], [libusb-1.0], true, [hidapi_lib_error libusb-1.0]) + PKG_CHECK_MODULES([libusb], [libusb-1.0 >= 1.0.9], true, [hidapi_lib_error libusb-1.0]) LIBS_LIBUSB_PRIVATE+=" $libusb_LIBS" CFLAGS_LIBUSB+=" $libusb_CFLAGS" ;; @@ -217,8 +217,12 @@ fi AC_SUBST(LTLDFLAGS) +PKG_CHECK_MODULES( LIBLO, liblo) + AC_CONFIG_FILES([Makefile \ hidtest/Makefile \ + hidparsertest/Makefile \ + hidapi2osc/Makefile \ libusb/Makefile \ linux/Makefile \ mac/Makefile \ diff --git a/hidapi/CMakeLists.txt b/hidapi/CMakeLists.txt new file mode 100644 index 00000000..56136376 --- /dev/null +++ b/hidapi/CMakeLists.txt @@ -0,0 +1 @@ +message( "hidapi cmakelists" ) \ No newline at end of file diff --git a/hidapi2osc/CMakeLists.txt b/hidapi2osc/CMakeLists.txt new file mode 100644 index 00000000..2a45a1da --- /dev/null +++ b/hidapi2osc/CMakeLists.txt @@ -0,0 +1,36 @@ +message( "===hidapi2osc cmakelists===" ) + +include(FindPkgConfig) +# pkg_check_modules +pkg_check_modules(liblo REQUIRED liblo) + +# add_subdirectory( ${hidapi_source} ) + +# message( "osc: hidapi include dirs are: ${hidapi_INCLUDE_DIRS}" ) +# message( "osc: hidapi parser include dirs are: ${hidapi_parser_INCLUDE_DIRS}" ) + +include_directories( + ${CMAKE_BINARY_DIR} + ${liblo_INCLUDE_DIRS} + ${hidapi_SOURCE_DIR}/hidapi/ + ${hidapi_SOURCE_DIR}/hidapi_parser/ +) + +link_directories( + ${liblo_LIBRARY_DIRS} +) + + +########################################################################## +# sdl2osc +########################################################################## + +set(hidapi2osc_SRCS + hidapi2osc.cpp +) + +add_executable( hidapi2osc ${hidapi2osc_SRCS} ) + +target_link_libraries(hidapi2osc ${liblo_LIBRARIES} hidapi hidapi_parser ) + +install(TARGETS hidapi2osc DESTINATION bin) \ No newline at end of file diff --git a/hidapi2osc/Makefile.am b/hidapi2osc/Makefile.am new file mode 100644 index 00000000..e4b09cab --- /dev/null +++ b/hidapi2osc/Makefile.am @@ -0,0 +1,20 @@ +AM_CFLAGS = $(LIBLO_CFLAGS) -I$(top_srcdir)/hidapi/ -I$(top_srcdir)/hidapi_parser/ +AM_CPPFLAGS = $(LIBLO_CFLAGS) -I$(top_srcdir)/hidapi/ -I$(top_srcdir)/hidapi_parser/ +## Linux +if OS_LINUX +noinst_PROGRAMS = hidapi2osc-libusb hidapi2osc-hidraw + +hidapi2osc_hidraw_SOURCES = $(top_srcdir)/hidapi_parser/hidapi_parser.c hidapi2osc.cpp +hidapi2osc_hidraw_LDADD = $(top_builddir)/linux/libhidapi-hidraw.la $(LIBLO_LIBS) + +hidapi2osc_libusb_SOURCES = $(top_srcdir)/hidapi_parser/hidapi_parser.c hidapi2osc.cpp +hidapi2osc_libusb_LDADD = $(top_builddir)/libusb/libhidapi-libusb.la $(LIBLO_LIBS) +else + +noinst_PROGRAMS = hidapi2osc + +hidapi2osc_SOURCES = $(top_srcdir)/hidapi_parser/hidapi_parser.c hidapi2osc.cpp +# hidapi_parser_HEADERS = hidapi_parser.h +hidapi2osc_LDADD = $(top_builddir)/$(backend)/libhidapi.la $(LIBLO_LIBS) + +endif diff --git a/hidapi2osc/hidapi2osc.cpp b/hidapi2osc/hidapi2osc.cpp new file mode 100644 index 00000000..33cfaff8 --- /dev/null +++ b/hidapi2osc/hidapi2osc.cpp @@ -0,0 +1,595 @@ +/* hidapi_parser $ + * + * Copyright (C) 2013, Marije Baalman + * This work was funded by a crowd-funding initiative for SuperCollider's [1] HID implementation + * including a substantial donation from BEK, Bergen Center for Electronic Arts, Norway + * + * [1] http://supercollider.sourceforge.net + * [2] http://www.bek.no + * + * 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 3 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +// #include +#include +#include + +#include +#include +#include +#include + +#include + + +#include +#include + +// Headers needed for sleeping. +#ifdef _WIN32 + #include +#else + #include +#endif + + +#include + +typedef std::map hid_map_t; + +hid_map_t hiddevices; // declares a vector of integers +int number_of_hids = 0; + +int done = 0; + +#define MAX_STR 255 + + +lo_address t; +lo_server s; +lo_server_thread st; + +static void osc_element_cb( struct hid_device_element *el, void *data) +{ + lo_message m1 = lo_message_new(); + lo_message_add_int32( m1, *((int*) data) ); + lo_message_add_int32( m1, el->index ); + lo_message_add_int32( m1, el->usage_page ); + lo_message_add_int32( m1, el->usage ); + lo_message_add_int32( m1, el->value ); + lo_message_add_float( m1, hid_element_map_logical( el ) ); // TODO: this one is not found??? + lo_send_message_from( t, s, "/hid/element/data", m1 ); + lo_message_free(m1); +} + +static void osc_descriptor_cb( struct hid_device_descriptor *dd, void *data) +{ + lo_message m1 = lo_message_new(); + lo_message_add_int32( m1, *((int*) data) ); + lo_message_add_int32( m1, dd->num_elements ); + lo_send_message_from( t, s, "/hid/device/data", m1 ); + lo_message_free(m1); +} + +void close_all_devices(){ + hid_map_t::const_iterator it; + for(it=hiddevices.begin(); it!=hiddevices.end(); ++it){ + struct hid_dev_desc * devdesc = it->second; + hid_close_device( devdesc ); + } + hiddevices.clear(); +} + +void open_device( unsigned short vendor, unsigned short product, const wchar_t *serial_number=NULL ){ + + struct hid_dev_desc * newdevdesc = hid_open_device( vendor, product, serial_number ); + + if (!newdevdesc){ + fprintf(stderr, "Unable to open device %d, %d\n", vendor, product ); + if ( serial_number != NULL ){ + lo_send_from( t, s, LO_TT_IMMEDIATE, "/hid/open/error", "iis", vendor, product, serial_number ); + } else { + lo_send_from( t, s, LO_TT_IMMEDIATE, "/hid/open/error", "ii", vendor, product ); + } + return; + } else { + hiddevices[ number_of_hids ] = newdevdesc; + if ( serial_number != NULL ){ + lo_send_from( t, s, LO_TT_IMMEDIATE, "/hid/open", "iiis", number_of_hids, product, vendor, serial_number ); + } else { + lo_send_from( t, s, LO_TT_IMMEDIATE, "/hid/open", "iii", number_of_hids, product, vendor ); + } + + newdevdesc->index = number_of_hids; + + hid_set_descriptor_callback( newdevdesc->descriptor, (hid_descriptor_callback) osc_descriptor_cb, &newdevdesc->index ); + hid_set_element_callback( newdevdesc->descriptor, (hid_element_callback) osc_element_cb, &newdevdesc->index ); + + number_of_hids++; + } +} + +void close_device( int joy_idx ){ +// hid_map_t::const_iterator it = ; + struct hid_dev_desc * hidtoclose = hiddevices.find( joy_idx )->second; + if ( hidtoclose == NULL ){ + lo_send_from( t, s, LO_TT_IMMEDIATE, "/hid/close/error", "i", joy_idx ); + } else { + lo_send_from( t, s, LO_TT_IMMEDIATE, "/hid/closed", "iii", joy_idx, hidtoclose->info->vendor_id, hidtoclose->info->product_id ); + hid_close_device( hidtoclose ); + hiddevices.erase( joy_idx ); + } +} + + +/// OSC bits + +void error(int num, const char *m, const char *path); +int info_handler(const char *path, const char *types, lo_arg **argv, + int argc, void *data, void *user_data); + +// int openjoystick_handler(const char *path, const char *types, lo_arg **argv, +// int argc, void *data, void *user_data); + +int hid_open_handler(const char *path, const char *types, lo_arg **argv, + int argc, void *data, void *user_data); +int hid_close_handler(const char *path, const char *types, lo_arg **argv, + int argc, void *data, void *user_data); +int hid_info_handler(const char *path, const char *types, lo_arg **argv, + int argc, void *data, void *user_data); + +int hid_element_info_handler(const char *path, const char *types, lo_arg **argv, + int argc, void *data, void *user_data); + +int generic_handler(const char *path, const char *types, lo_arg **argv, + int argc, void *data, void *user_data); +int quit_handler(const char *path, const char *types, lo_arg **argv, int argc, + void *data, void *user_data); + +int init_osc( char * ip, char *outport, char * port ){ + /* create liblo addres */ + t = lo_address_new(ip, outport); // change later to use other host + + lo_server_thread st = lo_server_thread_new(port, error); + + lo_server_thread_add_method(st, "/hid/open", "ii", hid_open_handler, NULL); + lo_server_thread_add_method(st, "/hid/element/info", "i", hid_element_info_handler, NULL); + lo_server_thread_add_method(st, "/hid/info", "i", hid_info_handler, NULL); + lo_server_thread_add_method(st, "/hid/close", "i", hid_close_handler, NULL); + + lo_server_thread_add_method(st, "/hidapi2osc/info", "", info_handler, NULL); + lo_server_thread_add_method(st, "/hidapi2osc/quit", "", quit_handler, NULL); + lo_server_thread_add_method(st, NULL, NULL, generic_handler, NULL); + + lo_server_thread_start(st); + + lo_server s = lo_server_thread_get_server( st ); + + lo_send_from( t, s, LO_TT_IMMEDIATE, "/hidapi2osc/started", "" ); +} + +lo_message get_hid_info_msg( struct hid_device_info * info ) +{ + lo_message m1 = lo_message_new(); + lo_message_add_int32( m1, info->vendor_id ); + lo_message_add_int32( m1, info->product_id ); + lo_message_add_string( m1, info->path ); + + //TODO: fix for proper conversion of unicode to ascii + wchar_t* wstr = info->serial_number; + char* ascii; + if ( wstr != NULL ){ + ascii = new char[wcslen(wstr) + 1]; + wcstombs( ascii, wstr, wcslen(wstr) ); + lo_message_add_string( m1, ascii ); + delete ascii; + } else { + lo_message_add_string( m1, "" ); + } + + wstr = info->manufacturer_string; + if ( wstr != NULL ){ + ascii = new char[wcslen(wstr) + 1]; + wcstombs( ascii, wstr, wcslen(wstr) ); + lo_message_add_string( m1, ascii ); + delete ascii; + } else { + lo_message_add_string( m1, "" ); + } + + + wstr = info->product_string; + if ( wstr != NULL ){ + ascii = new char[wcslen(wstr) + 1]; + wcstombs( ascii, wstr, wcslen(wstr) ); + lo_message_add_string( m1, ascii ); + delete ascii; + } else { + lo_message_add_string( m1, "" ); + } + +// lo_message_add_string( m1, info->serial_number ); +// lo_message_add_string( m1, info->manufacturer_string ); +// lo_message_add_string( m1, info->product_string ); + + lo_message_add_int32( m1, info->release_number ); + lo_message_add_int32( m1, info->interface_number ); + return m1; +} + +lo_message get_hid_element_info_msg( hid_device_element * el, int devid ) +{ + lo_message m1 = lo_message_new(); + lo_message_add_int32( m1, devid ); + lo_message_add_int32( m1, el->index ); + lo_message_add_int32( m1, el->usage_page ); + lo_message_add_int32( m1, el->usage ); + lo_message_add_int32( m1, el->io_type ); + lo_message_add_int32( m1, el->type ); + lo_message_add_int32( m1, el->logical_min ); + lo_message_add_int32( m1, el->logical_max ); + lo_message_add_int32( m1, el->phys_min ); + lo_message_add_int32( m1, el->phys_max ); + lo_message_add_int32( m1, el->unit_exponent ); + lo_message_add_int32( m1, el->unit ); + lo_message_add_int32( m1, el->report_size ); + lo_message_add_int32( m1, el->report_id ); + lo_message_add_int32( m1, el->report_index ); + return m1; +} + + +void error(int num, const char *msg, const char *path) +{ + printf("liblo server error %d in path %s: %s\n", num, path, msg); + fflush(stdout); +} + +/* catch any incoming messages and display them. returning 1 means that the + * message has not been fully handled and the server should try other methods */ +int generic_handler(const char *path, const char *types, lo_arg **argv, + int argc, void *data, void *user_data) +{ + int i; + + printf("path: <%s>\n", path); + for (i=0; inext; + } + + lo_bundle b = lo_bundle_new( LO_TT_IMMEDIATE ); + + lo_message m1 = lo_message_new(); + lo_message_add_int32( m1, count ); + lo_bundle_add_message( b, "/hid/number", m1 ); + + cur_dev = devs; + while (cur_dev) { + lo_message m2 = get_hid_info_msg( cur_dev ); + lo_bundle_add_message( b, "/hid/info", m2 ); + cur_dev = cur_dev->next; + } + + if ( lo_send_bundle_from( t, s, b ) == -1 ){ + printf("hidapi2osc/info: OSC error %d: %s\n", lo_address_errno(t), lo_address_errstr(t)); + } + lo_bundle_free( b ); + hid_free_enumeration(devs); + + fflush(stdout); + return 0; +} + +int quit_handler(const char *path, const char *types, lo_arg **argv, int argc, + void *data, void *user_data) +{ + done = 1; + printf("hidapi2osc: allright, that's it, I quit\n"); + fflush(stdout); + + return 0; +} + +void send_elements_hid_info(int joy_idx) +{ + hid_dev_desc * hid = hiddevices.find( joy_idx )->second; + if ( hid == NULL ){ + lo_send_from( t, s, LO_TT_IMMEDIATE, "/hid/element/info/error", "i", joy_idx ); + return; + } + lo_bundle b = lo_bundle_new( LO_TT_IMMEDIATE ); + + lo_message m1 = lo_message_new(); + lo_message_add_int32( m1, joy_idx ); + lo_message_add_int32( m1, hid->descriptor->num_elements ); + lo_bundle_add_message( b, "/hid/element/number", m1 ); + + hid_device_element * cur_element = hid->descriptor->first; + + while (cur_element) { + lo_message m2 = get_hid_element_info_msg( cur_element, joy_idx ); + lo_bundle_add_message( b, "/hid/element/info", m2 ); + cur_element = cur_element->next; + } + + if ( lo_send_bundle_from( t, s, b ) == -1 ){ + printf("hidapi2osc/info: OSC error %d: %s\n", lo_address_errno(t), lo_address_errstr(t)); + } + lo_bundle_free( b ); +} + +void send_hid_info(int joy_idx) +{ + hid_dev_desc * hid = hiddevices.find( joy_idx )->second; + if ( hid != NULL ){ + lo_message m1 = get_hid_info_msg( hid->info ); + lo_send_message_from( t, s, "/hid/info", m1 ); + lo_message_free(m1); + } else { + lo_send_from( t, s, LO_TT_IMMEDIATE, "/hid/info/error", "i", joy_idx ); + } +} + +int hid_info_handler(const char *path, const char *types, lo_arg **argv, int argc, + void *data, void *user_data) +{ + printf("hidapi2osc: joystick info handler\n"); + + send_hid_info( argv[0]->i ); + return 0; +} + +int hid_element_info_handler(const char *path, const char *types, lo_arg **argv, int argc, + void *data, void *user_data) +{ + printf("hidapi2osc: joystick elements info handler\n"); + + send_elements_hid_info( argv[0]->i ); + return 0; +} + +int hid_open_handler(const char *path, const char *types, lo_arg **argv, int argc, + void *data, void *user_data) +{ +// printf("hidapi2osc: joystick open handler\n"); + open_device( argv[0]->i, argv[1]->i, NULL ); + return 0; +} + +int hid_close_handler(const char *path, const char *types, lo_arg **argv, int argc, + void *data, void *user_data) +{ + printf("hidapi2osc: joystick close handler, %i\n", argv[0]->i ); + close_device( argv[0]->i ); + return 0; +} + + + +/// end OSC stuff + + +int str2int(const char* str, int* val) +{ + char* endptr; + errno = 0; /* To distinguish success/failure after call */ + + *val = strtol(str, &endptr, 10); + + /* Check for various possible errors */ + if ((errno == ERANGE && (*val == LONG_MAX || *val == LONG_MIN)) + || (errno != 0 && *val == 0)) { + return 0; + } + + if (endptr == str) { + return 0; + } + + return 1; +} + +void print_device_info( hid_device *handle ){ + wchar_t wstr[MAX_STR]; + int res; + // Read the Manufacturer String + wstr[0] = 0x0000; + res = hid_get_manufacturer_string(handle, wstr, MAX_STR); + if (res < 0) + printf("Unable to read manufacturer string\n"); + printf("Manufacturer String: %ls\n", wstr); + + // Read the Product String + wstr[0] = 0x0000; + res = hid_get_product_string(handle, wstr, MAX_STR); + if (res < 0) + printf("Unable to read product string\n"); + printf("Product String: %ls\n", wstr); + + // Read the Serial Number String + wstr[0] = 0x0000; + res = hid_get_serial_number_string(handle, wstr, MAX_STR); + if (res < 0) + printf("Unable to read serial number string\n"); + printf("Serial Number String: (%d) %ls", wstr[0], wstr); + printf("\n"); + + // Read Indexed String 1 + wstr[0] = 0x0000; + res = hid_get_indexed_string(handle, 1, wstr, MAX_STR); + if (res < 0) + printf("Unable to read indexed string 1\n"); + printf("Indexed String 1: %ls\n", wstr); +} + +void list_devices( void ){ + struct hid_device_info *devs, *cur_dev; + devs = hid_enumerate(0x0, 0x0); + cur_dev = devs; + while (cur_dev) { + printf("Device Found\n type: %04hx %04hx\n path: %s\n serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number); + printf("\n"); + printf(" Manufacturer: %ls\n", cur_dev->manufacturer_string); + printf(" Product: %ls\n", cur_dev->product_string); + printf(" Release: %hx\n", cur_dev->release_number); + printf(" Interface: %d\n", cur_dev->interface_number); + printf("\n"); + cur_dev = cur_dev->next; + } + hid_free_enumeration(devs); +} + +void print_help(const char* prg) +{ + printf("Usage: %s [OPTION]\n", prg); +// printf("List available joysticks or test a joystick.\n"); +// printf("This programm uses SDL for doing its test instead of using the raw\n" +// "/dev/input/jsX interface\n"); + printf("\n"); + printf("Options:\n"); + printf(" --help Print this help\n"); + printf(" --version Print version number and exit\n"); + printf(" --list Search for available joysticks and list their properties\n"); +// printf(" --event JOYNUM Display the events that are received from the joystick\n"); + printf(" --osc Send the events that are received from the joystick\n"); + printf("\n"); + printf("Examples:\n"); + printf(" %s --list\n", prg); +// printf(" %s --test 1\n", prg); + printf(" %s --osc\n", prg); +} + + + +int main(int argc, char** argv) +{ + if (argc == 1) + { + print_help(argv[0]); + exit(1); + } + + if (argc == 2 && (strcmp(argv[1], "--help") == 0 || + strcmp(argv[1], "-h") == 0)) + { + print_help(argv[0]); + } + if (argc == 2 && (strcmp(argv[1], "--version") == 0)) + { + printf("hidapi2osc 0.1.0\n"); + exit(EXIT_SUCCESS); + } + else if (argc == 2 && (strcmp(argv[1], "--list") == 0 || + (strcmp(argv[1], "-l") == 0))) + { + if (hid_init()) + return -1; + list_devices(); + } +// else if (argc == 3 && (strcmp(argv[1], "--event") == 0 || +// strcmp(argv[1], "-e") == 0)) +// { +// int joy_idx; +// if (!str2int(argv[2], &joy_idx)) +// { +// fprintf(stderr, "Error: JOYSTICKNUM argument must be a number, but was '%s'\n", argv[2]); +// exit(1); +// } +// +// } +// fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError()); +// } + else if (argc == 2 && (strcmp(argv[1], "--osc") == 0 || + strcmp(argv[1], "-o") == 0)) + { + + char *port = "57151"; + char *outport = "57120"; + char *ip = "127.0.0.1"; + + if ( argc == 5 ) + { + ip = argv[4]; + port = argv[3]; + outport = argv[2]; + } + else if ( argc == 4 ) + { + port = argv[3]; + outport = argv[2]; + } + else if ( argc == 3 ) + { + outport = argv[2]; + } + + init_osc( ip, outport, port ); + + if (hid_init()) + return -1; + + printf("Entering hid read loop, press Ctrl-c to exit\n"); + + int res = 0; + hid_map_t::const_iterator it; + unsigned char buf[256]; + while(!done){ + for(it=hiddevices.begin(); it!=hiddevices.end(); ++it){ + res = hid_read( it->second->device, buf, sizeof(buf)); + if ( res > 0 ) { + hid_parse_input_report( buf, res, it->second->descriptor ); + } + } + #ifdef WIN32 + Sleep(50); + #else + usleep(500*10); + #endif + } + close_all_devices(); + + lo_send_from( t, s, LO_TT_IMMEDIATE, "/hidapi2osc/quit", "s", "nothing more to do, quitting" ); + lo_server_thread_free( st ); + lo_address_free( t ); + } + else + { + fprintf(stderr, "%s: unknown arguments\n", argv[0]); + fprintf(stderr, "Try '%s --help' for more informations\n", argv[0]); + } +} + +/* EOF */ diff --git a/hidapi2osc/supercollider/hidapi2osc-test.scd b/hidapi2osc/supercollider/hidapi2osc-test.scd new file mode 100644 index 00000000..d6a651d2 --- /dev/null +++ b/hidapi2osc/supercollider/hidapi2osc-test.scd @@ -0,0 +1,37 @@ +OSCFunc.trace( true ) +OSCFunc.trace( false ) + +OSCdef( \hidStarted, { |msg| msg.postln; }, '/hidapi2osc/started' ); +OSCdef( \hidQuit, { |msg| msg.postln; }, '/hidapi2osc/quit' ); + +OSCdef( \hidOpen, { |msg| msg.postln; }, '/hid/open' ); +OSCdef( \hidOpenError, { |msg| msg.postln; }, '/hid/open/error' ); +OSCdef( \hidClose, { |msg| msg.postln; }, '/hid/closed' ); +OSCdef( \hidCloseError, { |msg| msg.postln; }, '/hid/close/error' ); + +OSCdef( \hidNumber, { |msg| msg.postln; }, '/hid/number' ); +OSCdef( \hidInfo, { |msg| msg.postln; }, '/hid/info' ); +OSCdef( \hidInfoError, { |msg| msg.postln; }, '/hid/info/error' ); + + +OSCdef( \elementNumber, { |msg| msg.postln; }, '/hid/element/number' ); +OSCdef( \elementInfo, { |msg| msg.postln; }, '/hid/element/info' ); +OSCdef( \elementInfoError, { |msg| msg.postln; }, '/hid/element/info/error' ); + + +OSCdef( \elementData, { |msg| msg.postln; }, '/hid/element/data' ); +OSCdef( \deviceData, { |msg| msg.postln; }, '/hid/device/data' ); + +n = NetAddr.new( "127.0.0.1", 57151 ); + +n.sendMsg( "/hidapi2osc/info" ); + +n.sendMsg( "/hid/open", 1103, 53251 ); + +n.sendMsg( "/hid/info", 0); + +n.sendMsg( "/hid/element/info", 0); + +n.sendMsg( "/hid/close", 0); + +n.sendMsg( "/hidapi2osc/quit" ); \ No newline at end of file diff --git a/hidapi_parser/CMakeLists.txt b/hidapi_parser/CMakeLists.txt new file mode 100644 index 00000000..6ff107c7 --- /dev/null +++ b/hidapi_parser/CMakeLists.txt @@ -0,0 +1,14 @@ +message( "===hidapi_parser cmakelists===" ) + + +# message( "hidapi parser source dir is: ${hidapi_SOURCE_DIR}" ) +# +# set( hidapi_parser_INCLUDE_DIRS ${hidapi_SOURCE_DIR}/hidapi_parser/) +# # set( hidapi_LIBS ${UDEV_LIBRARIES}) +# set( hidapi_parser_SRCS hidapi_parser.c ) +# +# message( "hidapi_parser include dirs are: ${hidapi_parser_INCLUDE_DIRS}" ) + +include_directories( ${hidapi_SOURCE_DIR}/hidapi/ ) +add_library( hidapi_parser STATIC hidapi_parser.c ) +target_link_libraries( hidapi ) \ No newline at end of file diff --git a/hidapi_parser/Makefile.am b/hidapi_parser/Makefile.am new file mode 100644 index 00000000..c4c130d7 --- /dev/null +++ b/hidapi_parser/Makefile.am @@ -0,0 +1,20 @@ +AM_CFLAGS = -I$(top_srcdir)/hidapi/ +AM_CPPFLAGS = -I$(top_srcdir)/hidapi/ +## Linux +if OS_LINUX +noinst_PROGRAMS = hidapi_parser-libusb hidapi_parser-hidraw + +hidapi_parser_hidraw_SOURCES = hidapi_parser.c main.c +hidapi_parser_hidraw_LDADD = $(top_builddir)/linux/libhidapi-hidraw.la + +hidapi_parser_libusb_SOURCES = hidapi_parser.c main.c +hidapi_parser_libusb_LDADD = $(top_builddir)/libusb/libhidapi-libusb.la +else + +noinst_PROGRAMS = hidapi_parser + +hidapi_parser_SOURCES = hidapi_parser.c main.c +# hidapi_parser_HEADERS = hidapi_parser.h +hidapi_parser_LDADD = $(top_builddir)/$(backend)/libhidapi.la + +endif diff --git a/hidapi_parser/hidapi_parser.c b/hidapi_parser/hidapi_parser.c new file mode 100644 index 00000000..3eedfb6d --- /dev/null +++ b/hidapi_parser/hidapi_parser.c @@ -0,0 +1,705 @@ +/* hidapi_parser $ + * + * Copyright (C) 2013, Marije Baalman + * This work was funded by a crowd-funding initiative for SuperCollider's [1] HID implementation + * including a substantial donation from BEK, Bergen Center for Electronic Arts, Norway + * + * [1] http://supercollider.sourceforge.net + * [2] http://www.bek.no + * + * 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 3 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +// #include + +#include "hidapi_parser.h" + +// SET IN CMAKE +// #define DEBUG_PARSER + +//// ---------- HID descriptor parser + +// main items +#define HID_INPUT 0x80 +#define HID_OUTPUT 0x90 +#define HID_COLLECTION 0xA0 +#define HID_FEATURE 0xB0 +#define HID_END_COLLECTION 0xC0 + +// HID Report Items from HID 1.11 Section 6.2.2 +#define HID_USAGE_PAGE 0x04 +#define HID_USAGE 0x08 +#define HID_USAGE_MIN 0x18 +#define HID_USAGE_MAX 0x28 + +#define HID_DESIGNATOR_INDEX 0x38 +#define HID_DESIGNATOR_MIN 0x48 +#define HID_DESIGNATOR_MAX 0x58 + +#define HID_STRING_INDEX 0x78 +#define HID_STRING_MIN 0x88 +#define HID_STRING_MAX 0x98 + +#define HID_DELIMITER 0xA8 + +#define HID_LOGICAL_MIN 0x14 +#define HID_LOGICAL_MAX 0x24 + +#define HID_PHYSICAL_MIN 0x34 +#define HID_PHYSICAL_MAX 0x44 + +#define HID_UNIT_EXPONENT 0x54 +#define HID_UNIT 0x64 + +#define HID_REPORT_SIZE 0x74 +#define HID_REPORT_ID 0x84 + +#define HID_REPORT_COUNT 0x94 + +#define HID_PUSH 0xA4 +#define HID_POP 0xB4 + +#define HID_RESERVED 0xC4 // above this it is all reserved + + +// HID Report Usage Pages from HID Usage Tables 1.12 Section 3, Table 1 +// #define HID_USAGE_PAGE_GENERICDESKTOP 0x01 +// #define HID_USAGE_PAGE_KEY_CODES 0x07 +// #define HID_USAGE_PAGE_LEDS 0x08 +// #define HID_USAGE_PAGE_BUTTONS 0x09 + +// HID Report Usages from HID Usage Tables 1.12 Section 4, Table 6 +// #define HID_USAGE_POINTER 0x01 +// #define HID_USAGE_MOUSE 0x02 +// #define HID_USAGE_JOYSTICK 0x04 +// #define HID_USAGE_KEYBOARD 0x06 +// #define HID_USAGE_X 0x30 +// #define HID_USAGE_Y 0x31 +// #define HID_USAGE_Z 0x32 +// #define HID_USAGE_RX 0x33 +// #define HID_USAGE_RY 0x34 +// #define HID_USAGE_RZ 0x35 +// #define HID_USAGE_SLIDER 0x36 +// #define HID_USAGE_DIAL 0x37 +// #define HID_USAGE_WHEEL 0x38 + + +// HID Report Collection Types from HID 1.12 6.2.2.6 +#define HID_COLLECTION_PHYSICAL 0x00 +#define HID_COLLECTION_APPLICATION 0x01 +#define HID_COLLECTION_LOGICAL 0x02 +#define HID_COLLECTION_REPORT 0x03 +#define HID_COLLECTION_NAMED_ARRAY 0x04 +#define HID_COLLECTION_USAGE_SWITCH 0x05 +#define HID_COLLECTION_USAGE_MODIFIER 0x06 +#define HID_COLLECTION_RESERVED 0x07 +#define HID_COLLECTION_VENDOR 0x80 + +// HID Input/Output/Feature Item Data (attributes) from HID 1.11 6.2.2.5 +/// more like flags - for input, output, and feature +#define HID_ITEM_CONSTANT 0x1 // data(0), constant(1) +#define HID_ITEM_VARIABLE 0x2 // array(0), variable(1) +#define HID_ITEM_RELATIVE 0x4 // absolute(0), relative(1) +#define HID_ITEM_WRAP 0x8 // no wrap(0), wrap(1) +#define HID_ITEM_LINEAR 0x10 // linear(0), non linear(1) +#define HID_ITEM_PREFERRED 0x20 // no preferred(0), preferred(1) +#define HID_ITEM_NULL 0x40 // no null(0), null(1) +#define HID_ITEM_VOLATILE 0x60 // non volatile(0), volatile(1) +#define HID_ITEM_BITFIELD 0x80 // bit field(0), buffered bytes(1) + +// Report Types from HID 1.11 Section 7.2.1 +#define HID_REPORT_TYPE_INPUT 1 +#define HID_REPORT_TYPE_OUTPUT 2 +#define HID_REPORT_TYPE_FEATURE 3 + + +#define BITMASK1(n) ((1ULL << (n)) - 1ULL) + + +struct hid_device_descriptor * hid_new_descriptor(){ + struct hid_device_descriptor * descriptor; + descriptor = (struct hid_device_descriptor *) malloc( sizeof( struct hid_device_descriptor) ); +// hid_descriptor_init( descriptor ); + + descriptor->first = NULL; + hid_set_descriptor_callback(descriptor, NULL, NULL); + hid_set_element_callback(descriptor, NULL, NULL); + return descriptor; +} + +struct hid_device_element * hid_new_element(){ + struct hid_device_element * element = (struct hid_device_element *) malloc( sizeof( struct hid_device_element ) ); + element->next = NULL; + return element; +} + +void hid_free_element( struct hid_device_element * ele ){ + free( ele ); +} + +// void hid_descriptor_init( struct hid_device_descriptor * devd){ +// devd->first = NULL; +// hid_set_descriptor_callback(devd, NULL, NULL); +// hid_set_element_callback(devd, NULL, NULL); +// } + +void hid_free_descriptor( struct hid_device_descriptor * devd){ + struct hid_device_element * cur_element = devd->first; + struct hid_device_element * next_element; + while (cur_element != NULL ) { + next_element = cur_element->next; + free( cur_element ); + cur_element = next_element; + } + free( devd ); +// hid_set_descriptor_callback(devd, NULL, NULL); +// hid_set_element_callback(devd, NULL, NULL); +} + +void hid_set_descriptor_callback( struct hid_device_descriptor * devd, hid_descriptor_callback cb, void *user_data ){ + devd->_descriptor_callback = cb; + devd->_descriptor_data = user_data; +} + +void hid_set_element_callback( struct hid_device_descriptor * devd, hid_element_callback cb, void *user_data ){ + devd->_element_callback = cb; + devd->_element_data = user_data; +} + +int hid_parse_report_descriptor( char* descr_buf, int size, struct hid_device_descriptor * descriptor ){ + struct hid_device_element * prev_element; + int current_usage_page; + int current_usage; + int current_usages[256]; + int current_usage_index = 0; + int current_usage_min = -1; + int current_usage_max = -1; + int current_logical_min = 0; + int current_logical_max = 0; + int current_physical_min = 0; + int current_physical_max = 0; + int current_report_count; + int current_report_id; + int current_report_size; + int current_unit = 0; + int current_unit_exponent = 0; + char current_input; + char current_output; + int collection_nesting = 0; + + int next_byte_tag = -1; + int next_byte_size = 0; + int next_byte_type = 0; + int next_val = 0; + + unsigned char toadd = 0; + int byte_count = 0; + + int i,j; + + descriptor->num_elements = 0; + for ( i = 0; i < size; i++){ +#ifdef DEBUG_PARSER + printf("\n%02hhx ", descr_buf[i]); + printf("\tbyte_type %i, %i, %i \t", next_byte_tag, next_byte_size, next_val); +#endif + if ( next_byte_tag != -1 ){ +// toadd = (unsigned char) descr_buf[i]; + int shift = byte_count*8; + next_val |= (int)(((unsigned char)(descr_buf[i])) << shift); +#ifdef DEBUG_PARSER + printf("\t nextval shift: %i", next_val); +#endif + byte_count++; + if ( byte_count == next_byte_size ){ + switch( next_byte_tag ){ + case HID_USAGE_PAGE: + current_usage_page = next_val; +#ifdef DEBUG_PARSER + printf("usage page: 0x%02hhx", current_usage_page); +#endif + break; + case HID_USAGE: + current_usage = next_val; + current_usages[ current_usage_index ] = next_val; +#ifdef DEBUG_PARSER + printf("usage: 0x%02hhx, %i", current_usages[ current_usage_index ], current_usage_index ); +#endif + current_usage_index++; + break; + case HID_COLLECTION: + //TODO: COULD ALSO READ WHICH KIND OF COLLECTION + collection_nesting++; +#ifdef DEBUG_PARSER + printf("collection: %i, %i", collection_nesting, next_val ); +#endif + break; + case HID_USAGE_MIN: + current_usage_min = next_val; +#ifdef DEBUG_PARSER + printf("usage min: %i", current_usage_min); +#endif + break; + case HID_USAGE_MAX: + current_usage_max = next_val; +#ifdef DEBUG_PARSER + printf("usage max: %i", current_usage_max); +#endif + break; + case HID_LOGICAL_MIN: + current_logical_min = next_val; +#ifdef DEBUG_PARSER + printf("logical min: %i", current_logical_min); +#endif + break; + case HID_LOGICAL_MAX: + current_logical_max = next_val; +#ifdef DEBUG_PARSER + printf("logical max: %i", current_logical_max); +#endif + break; + case HID_PHYSICAL_MIN: + current_physical_min = next_val; +#ifdef DEBUG_PARSER + printf("physical min: %i", current_physical_min); +#endif + break; + case HID_PHYSICAL_MAX: + current_physical_max = next_val; +#ifdef DEBUG_PARSER + printf("physical max: %i", current_physical_min); +#endif + break; + case HID_REPORT_COUNT: + current_report_count = next_val; +#ifdef DEBUG_PARSER + printf("report count: %i", current_report_count); +#endif + break; + case HID_REPORT_SIZE: + current_report_size = next_val; +#ifdef DEBUG_PARSER + printf("report size: %i", current_report_size); +#endif + break; + case HID_REPORT_ID: + current_report_id = next_val; +#ifdef DEBUG_PARSER + printf("report id: %i", current_report_id); +#endif + break; + case HID_POP: + // TODO: something useful with pop +#ifdef DEBUG_PARSER + printf("pop: %i", next_val ); +#endif + break; + case HID_PUSH: + // TODO: something useful with push +#ifdef DEBUG_PARSER + printf("pop: %i", next_val ); +#endif + break; + case HID_UNIT: + current_unit = next_val; +#ifdef DEBUG_PARSER + printf("unit: %i", next_val ); +#endif + break; + case HID_UNIT_EXPONENT: + current_unit_exponent = next_val; +#ifdef DEBUG_PARSER + printf("unit exponent: %i", next_val ); +#endif + break; + case HID_INPUT: +#ifdef DEBUG_PARSER + printf("input: %i", next_val); +#endif + // add the elements for this report + for ( j=0; jindex = descriptor->num_elements; + new_element->io_type = 1; + new_element->type = next_val; //TODO: parse this for more detailed info + + new_element->usage_page = current_usage_page; + if ( current_usage_min != -1 ){ + new_element->usage = current_usage_min + j; + } else { + new_element->usage = current_usages[j]; + } + new_element->logical_min = current_logical_min; + new_element->logical_max = current_logical_max; + if ( (current_physical_min == 0) && (current_physical_max == 0) ){ + new_element->phys_min = current_logical_min; + new_element->phys_max = current_logical_max; + + } else { + new_element->phys_min = current_physical_min; + new_element->phys_max = current_physical_max; + } + new_element->unit = current_unit; + new_element->unit_exponent = current_unit_exponent; + + new_element->report_size = current_report_size; + new_element->report_id = current_report_id; + new_element->report_index = j; + + new_element->value = 0; + if ( descriptor->num_elements == 0 ){ + descriptor->first = new_element; + } else { + prev_element->next = new_element; + } + descriptor->num_elements++; + prev_element = new_element; + } +// current_usage_min = -1; +// current_usage_max = -1; +// current_usage_index = 0; +// current_physical_min = 0; +// current_physical_max = 0; +// current_unit_exponent = 0; + break; + case HID_OUTPUT: +#ifdef DEBUG_PARSER + printf("output: %i", next_val); +#endif + // add the elements for this report + for ( j=0; jindex = descriptor->num_elements; + new_element->io_type = 2; + new_element->type = next_val; //TODO: parse this for more detailed info + + new_element->usage_page = current_usage_page; + if ( current_usage_min != -1 ){ + new_element->usage = current_usage_min + j; + } else { + new_element->usage = current_usages[j]; + } + new_element->logical_min = current_logical_min; + new_element->logical_max = current_logical_max; + if ( (current_physical_min == 0) && (current_physical_max == 0) ){ + new_element->phys_min = current_logical_min; + new_element->phys_max = current_logical_max; + + } else { + new_element->phys_min = current_physical_min; + new_element->phys_max = current_physical_max; + } + new_element->unit = current_unit; + new_element->unit_exponent = current_unit_exponent; + + new_element->report_size = current_report_size; + new_element->report_id = current_report_id; + new_element->report_index = j; + + new_element->value = 0; + if ( descriptor->num_elements == 0 ){ + descriptor->first = new_element; + } else { + prev_element->next = new_element; + } + descriptor->num_elements++; + prev_element = new_element; + } +// current_usage_min = -1; +// current_usage_max = -1; +// current_usage_index = 0; +// current_physical_min = 0; +// current_physical_max = 0; +// current_unit_exponent = 0; + break; + case HID_FEATURE: +#ifdef DEBUG_PARSER + printf("feature: %i", next_val); +#endif + // add the elements for this report + for ( j=0; jindex = descriptor->num_elements; + new_element->io_type = 3; + new_element->type = next_val; //TODO: parse this for more detailed info + + new_element->usage_page = current_usage_page; + if ( current_usage_min != -1 ){ + new_element->usage = current_usage_min + j; + } else { + new_element->usage = current_usages[j]; + } + new_element->logical_min = current_logical_min; + new_element->logical_max = current_logical_max; + if ( (current_physical_min == 0) && (current_physical_max == 0) ){ + new_element->phys_min = current_logical_min; + new_element->phys_max = current_logical_max; + + } else { + new_element->phys_min = current_physical_min; + new_element->phys_max = current_physical_max; + } + new_element->unit = current_unit; + new_element->unit_exponent = current_unit_exponent; + + new_element->report_size = current_report_size; + new_element->report_id = current_report_id; + new_element->report_index = j; + + new_element->value = 0; + if ( descriptor->num_elements == 0 ){ + descriptor->first = new_element; + } else { + prev_element->next = new_element; + } + descriptor->num_elements++; + prev_element = new_element; + } +// current_usage_min = -1; +// current_usage_max = -1; +// current_usage_index = 0; +// current_physical_min = 0; +// current_physical_max = 0; +// current_unit_exponent = 0; + break; +#ifdef DEBUG_PARSER + default: + if ( next_byte_tag >= HID_RESERVED ){ + printf("reserved bytes 0x%02hhx, %i", next_byte_tag, next_val ); + } else { + printf("undefined byte type 0x%02hhx, %i", next_byte_tag, next_val ); + } +#endif + } + next_byte_tag = -1; + } + } else { +#ifdef DEBUG_PARSER + printf("\tsetting next byte type: %i, 0x%02hhx ", descr_buf[i], descr_buf[i] ); +#endif + if ( descr_buf[i] == HID_END_COLLECTION ){ // JUST one byte + collection_nesting--; +#ifdef DEBUG_PARSER + printf("\tend collection: %i, %i\n", collection_nesting, descr_buf[i] ); +#endif + } else { + byte_count = 0; + next_val = 0; + next_byte_tag = descr_buf[i] & 0xFC; + next_byte_type = descr_buf[i] & 0x0C; + next_byte_size = descr_buf[i] & 0x03; + if ( next_byte_size == 3 ){ + next_byte_size = 4; + } +#ifdef DEBUG_PARSER + printf("\t next byte type: 0x%02hhx, %i, %i ", next_byte_tag, next_byte_type, next_byte_size ); +#endif + } + } + } + return 0; +} + +float hid_element_map_logical( struct hid_device_element * element ){ + float result = (float) element->logical_min + ( (float) element->value/( (float) element->logical_max - (float) element->logical_min ) ); + return result; +} + +float hid_element_resolution( struct hid_device_element * element ){ + float result = 0; +// ( element->logical_max - element->logical_min) / ( ( element->phys_max - element->phys_min) * pow(10, element->unit_exponent) ); + return result; +} + +float hid_element_map_physical( struct hid_device_element * element ){ + float result = 0; + return result; +} + +struct hid_device_element * hid_get_next_input_element( struct hid_device_element * curel ){ + + struct hid_device_element * nextel = curel->next; + while ( nextel != NULL ){ + if ( nextel->io_type == 1 ){ + return nextel; + } else { + nextel = nextel->next; + } + } + return curel; // return the previous element + // is NULL +} + +int hid_parse_input_report( unsigned char* buf, int size, struct hid_device_descriptor * descriptor ){ + ///TODO: parse input from descriptors with report size like 12 correctly + + // Print out the returned buffer. + struct hid_device_element * cur_element = descriptor->first; + int i; + int next_byte_size; + int next_mod_bit_size; + int byte_count = 0; + int next_val = 0; + +// cur_element = hid_get_next_input_element( cur_element ); +// if ( cur_element == NULL ){ return 0; } + if ( cur_element->io_type != 1 ){ + cur_element = hid_get_next_input_element(cur_element); + } + next_byte_size = cur_element->report_size/8; + next_mod_bit_size = cur_element->report_size%8; + +#ifdef DEBUG_PARSER + printf("report_size %i, bytesize %i, bitsize %i \t", cur_element->report_size, next_byte_size, next_mod_bit_size ); +#endif + +#ifdef DEBUG_PARSER + printf("-----------------------\n"); +#endif + for ( i = 0; i < size; i++){ + unsigned char curbyte = buf[i]; +#ifdef DEBUG_PARSER + printf("byte %02hhx \t", buf[i]); +#endif + // read byte: + if ( cur_element->report_size < 8 ){ + int bitindex = 0; + while ( bitindex < 8 ){ + // read bit + cur_element->value = (curbyte >> bitindex) & BITMASK1( cur_element->report_size ); +#ifdef DEBUG_PARSER + printf("element page %i, usage %i, type %i, index %i, value %i\n", cur_element->usage_page, cur_element->usage, cur_element->type, cur_element->index, cur_element->value ); +#endif + bitindex += cur_element->report_size; + if ( descriptor->_element_callback != NULL ){ + descriptor->_element_callback( cur_element, descriptor->_element_data ); + } + cur_element = hid_get_next_input_element( cur_element ); +// if ( cur_element == NULL ){ return 0; } + next_byte_size = cur_element->report_size/8; + next_mod_bit_size = cur_element->report_size%8; + +#ifdef DEBUG_PARSER + printf("report_size %i, bytesize %i, bitsize %i \t", cur_element->report_size, next_byte_size, next_mod_bit_size ); +#endif + } + } else if ( cur_element->report_size == 8 ){ + cur_element->value = curbyte; +#ifdef DEBUG_PARSER + printf("element page %i, usage %i, type %i, index %i, value %i\n", cur_element->usage_page, cur_element->usage, cur_element->type, cur_element->index,cur_element->value ); +#endif + if ( descriptor->_element_callback != NULL ){ + descriptor->_element_callback( cur_element, descriptor->_element_data ); + } + cur_element = hid_get_next_input_element( cur_element ); +// if ( cur_element == NULL ){ return 0; } + next_byte_size = cur_element->report_size/8; + next_mod_bit_size = cur_element->report_size%8; + +#ifdef DEBUG_PARSER + printf("report_size %i, bytesize %i, bitsize %i \t", cur_element->report_size, next_byte_size, next_mod_bit_size ); +#endif + } else if ( cur_element->report_size == 16 ){ + int shift = byte_count*8; + next_val |= (int)(((unsigned char)(curbyte)) << shift); +#ifdef DEBUG_PARSER + printf("\t nextval shift: %i", next_val); +#endif + byte_count++; + if ( byte_count == next_byte_size ){ + cur_element->value = next_val; + +#ifdef DEBUG_PARSER + printf("element page %i, usage %i, type %i, index %i, value %i\n", cur_element->usage_page, cur_element->usage, cur_element->type, cur_element->index,cur_element->value ); +#endif + if ( descriptor->_element_callback != NULL ){ + descriptor->_element_callback( cur_element, descriptor->_element_data ); + } + cur_element = hid_get_next_input_element( cur_element ); +// if ( cur_element == NULL ){ break; } + next_byte_size = cur_element->report_size/8; + next_mod_bit_size = cur_element->report_size%8; + +#ifdef DEBUG_PARSER + printf("report_size %i, bytesize %i, bitsize %i \t", cur_element->report_size, next_byte_size, next_mod_bit_size ); +#endif + byte_count = 0; + next_val = 0; + } + } + } + +#ifdef DEBUG_PARSER + printf("\n"); +#endif + if ( descriptor->_descriptor_callback != NULL ){ + descriptor->_descriptor_callback( descriptor, descriptor->_descriptor_data ); + } + return 0; +} + + + +struct hid_device_descriptor * hid_read_descriptor( hid_device * devd ){ + struct hid_device_descriptor * descriptor; + unsigned char descr_buf[HIDAPI_MAX_DESCRIPTOR_SIZE]; + int res; + res = hid_get_report_descriptor( devd, descr_buf, HIDAPI_MAX_DESCRIPTOR_SIZE ); + if (res < 0){ + printf("Unable to read report descriptor\n"); + return NULL; + } else { + descriptor = hid_new_descriptor(); +// descriptor = (struct hid_device_descriptor *) malloc( sizeof( struct hid_device_descriptor) ); +// hid_descriptor_init( descriptor ); + hid_parse_report_descriptor( descr_buf, res, descriptor ); + return descriptor; + } +} + +struct hid_dev_desc * hid_open_device( unsigned short vendor, unsigned short product, const wchar_t *serial_number ){ + hid_device * handle = hid_open( vendor, product, serial_number ); + if (!handle){ + return NULL; + } + struct hid_device_descriptor * newdesc = hid_read_descriptor( handle ); + if ( newdesc == NULL ){ + hid_close( handle ); + return NULL; + } + struct hid_dev_desc * newdevdesc = (struct hid_dev_desc *) malloc( sizeof( struct hid_dev_desc ) ); + struct hid_device_info * newinfo = hid_enumerate(vendor,product); + newdevdesc->device = handle; + //TODO: if serial_number is given, the info descriptor should also point to that one! + newdevdesc->info = newinfo; + newdevdesc->descriptor = newdesc; + + // Set the hid_read() function to be non-blocking. + hid_set_nonblocking( handle, 1); + + return newdevdesc; +} + +void hid_close_device( struct hid_dev_desc * devdesc ){ + hid_close( devdesc->device ); + hid_free_enumeration( devdesc->info ); + hid_free_descriptor( devdesc->descriptor ); + //TODO: more memory freeing? +} diff --git a/hidapi_parser/hidapi_parser.h b/hidapi_parser/hidapi_parser.h new file mode 100644 index 00000000..0dbd216f --- /dev/null +++ b/hidapi_parser/hidapi_parser.h @@ -0,0 +1,141 @@ +/* hidapi_parser $ + * + * Copyright (C) 2013, Marije Baalman + * This work was funded by a crowd-funding initiative for SuperCollider's [1] HID implementation + * including a substantial donation from BEK, Bergen Center for Electronic Arts, Norway + * + * [1] http://supercollider.sourceforge.net + * [2] http://www.bek.no + * + * 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 3 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef HIDAPI_PARSER_H__ +#define HIDAPI_PARSER_H__ + + +#define HIDAPI_MAX_DESCRIPTOR_SIZE 4096 + +#ifdef __cplusplus /* If this is a C++ compiler, use C linkage */ +extern "C" { +#endif + +#include + +// typedef struct _hid_device_element hid_device_element; +// typedef struct _hid_device_descriptor hid_device_descriptor; +// typedef struct _hid_dev_desc hid_dev_desc; + +struct hid_device_element; +struct hid_device_descriptor; +struct hid_dev_desc; + +// struct hid_element_cb; +// struct hid_descriptor_cb; + +typedef void (*hid_element_callback) ( struct hid_device_element *element, void *user_data); +typedef void (*hid_descriptor_callback) ( struct hid_device_descriptor *descriptor, void *user_data); + +// typedef struct _hid_element_cb { +// hid_element_callback cb; +// void *data; +// } hid_element_cb; +// +// typedef struct _hid_descriptor_cb { +// hid_descriptor_callback cb; +// void *data; +// } hid_descriptor_cb; + +struct hid_dev_desc { + int index; + hid_device *device; + struct hid_device_descriptor *descriptor; + struct hid_device_info *info; +}; + +struct hid_device_element { + int index; + + int io_type; // input(1), output(2), feature(3) + int type; // flags from the input/output report +// int vartype; // abs/relative + int usage_page; // usage page + int usage; // some kind of index (as from descriptor) + + int logical_min; + int logical_max; + + int phys_min; + int phys_max; + + int unit_exponent; + int unit; + + int report_size; + int report_id; + int report_index; // index into the report + + int value; + + /** Pointer to the next element */ + struct hid_device_element *next; +}; + +struct hid_device_descriptor { + int num_elements; + + /** Pointer to the first element */ + struct hid_device_element *first; + + /** pointers to callback function */ + hid_element_callback _element_callback; + void *_element_data; + hid_descriptor_callback _descriptor_callback; + void *_descriptor_data; +}; + +// higher level functions: +struct hid_device_descriptor * hid_read_descriptor( hid_device *devd ); +struct hid_dev_desc * hid_open_device( unsigned short vendor, unsigned short product, const wchar_t *serial_number ); +extern void hid_close_device( struct hid_dev_desc * devdesc ); + +struct hid_device_descriptor * hid_new_descriptor(); +void hid_free_descriptor( struct hid_device_descriptor * devd); +struct hid_device_element * hid_new_element(); +void hid_free_element( struct hid_device_element * ele); + +// void hid_descriptor_init( struct hid_device_descriptor * devd); + +void hid_set_descriptor_callback( struct hid_device_descriptor * devd, hid_descriptor_callback cb, void *user_data ); +void hid_set_element_callback( struct hid_device_descriptor * devd, hid_element_callback cb, void *user_data ); + +int hid_parse_report_descriptor( char* descr_buf, int size, struct hid_device_descriptor * descriptor ); + +struct hid_device_element * hid_get_next_input_element( struct hid_device_element * curel ); + +int hid_parse_input_report( unsigned char* buf, int size, struct hid_device_descriptor * descriptor ); + +float hid_element_resolution( struct hid_device_element * element ); +float hid_element_map_logical( struct hid_device_element * element ); +float hid_element_map_physical( struct hid_device_element * element ); + +// int hid_parse_feature_report( char* buf, int size, hid_device_descriptor * descriptor ); + +#ifdef __cplusplus /* If this is a C++ compiler, end C linkage */ +} +#endif + +#endif + diff --git a/hidapi_parser/main.c b/hidapi_parser/main.c new file mode 100644 index 00000000..7545e7fe --- /dev/null +++ b/hidapi_parser/main.c @@ -0,0 +1,202 @@ +#include +#include +#include +#include + +#include +#include "hidapi_parser.h" + + +// Headers needed for sleeping. +#ifdef _WIN32 + #include +#else + #include +#endif + +void list_devices( void ){ + struct hid_device_info *devs, *cur_dev; + devs = hid_enumerate(0x0, 0x0); + cur_dev = devs; + while (cur_dev) { + printf("Device Found\n type: %04hx %04hx\n path: %s\n serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number); + printf("\n"); + printf(" Manufacturer: %ls\n", cur_dev->manufacturer_string); + printf(" Product: %ls\n", cur_dev->product_string); + printf(" Release: %hx\n", cur_dev->release_number); + printf(" Interface: %d\n", cur_dev->interface_number); + printf("\n"); + cur_dev = cur_dev->next; + } + hid_free_enumeration(devs); +} + +// static hid_device * open_device( char vendor_id, char product_id ){ +// handle = +// return handle; +// } + +#define MAX_STR 255 + +void print_element_info( struct hid_device_element *element ){ + + printf( "index: %i, usage_page: %i, usage: %i, iotype: %i, type: %i, \n \ + \tlogical_min: %i, logical_max: %i, \n \ + \tphys_min: %i, phys_max: %i, unit_exponent: %i, unit: %i, \n \ + \treport_size: %i, report_id: %i, report_index: %i \n", + element->index, element->usage_page, element->usage, element->io_type, element->type, + element->logical_min, element->logical_max, + element->phys_min, element->phys_max, + element->unit_exponent, element->unit, + element->report_size, element->report_id, element->report_index ); + +} + +void print_device_info( hid_device *handle ){ + wchar_t wstr[MAX_STR]; + int res; + // Read the Manufacturer String + wstr[0] = 0x0000; + res = hid_get_manufacturer_string(handle, wstr, MAX_STR); + if (res < 0) + printf("Unable to read manufacturer string\n"); + printf("Manufacturer String: %ls\n", wstr); + + // Read the Product String + wstr[0] = 0x0000; + res = hid_get_product_string(handle, wstr, MAX_STR); + if (res < 0) + printf("Unable to read product string\n"); + printf("Product String: %ls\n", wstr); + + // Read the Serial Number String + wstr[0] = 0x0000; + res = hid_get_serial_number_string(handle, wstr, MAX_STR); + if (res < 0) + printf("Unable to read serial number string\n"); + printf("Serial Number String: (%d) %ls", wstr[0], wstr); + printf("\n"); + + // Read Indexed String 1 + wstr[0] = 0x0000; + res = hid_get_indexed_string(handle, 1, wstr, MAX_STR); + if (res < 0) + printf("Unable to read indexed string 1\n"); + printf("Indexed String 1: %ls\n", wstr); +} + +static void my_element_cb(const struct hid_device_element *el, void *data) +{ + printf("in %s\t", __func__); + printf("element: usage %i, value %i, index %i\t", el->usage, el->value, el->index ); + printf("user_data: %s\n", (const char *)data); +} + +static void my_descriptor_cb(const struct hid_device_descriptor *dd, void *data) +{ + printf("in %s\t", __func__); +// printf("element: usage %i, value %i, index %i\n", el->usage, el->value, el->index ); + printf("user_data: %s\n", (const char *)data); +} + +int main(int argc, char* argv[]){ + + int res; + unsigned char buf[256]; + unsigned char descr_buf[HIDAPI_MAX_DESCRIPTOR_SIZE]; + + struct hid_dev_desc *devdesc; +// struct hid_device_descriptor *descriptor; +// hid_device *handle; + +#ifdef WIN32 + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); +#endif + + if (hid_init()) + return -1; + list_devices(); + + devdesc = hid_open_device( 0x044f, 0xd003, NULL ); + if (!devdesc){ + fprintf(stderr, "Unable to open device %d, %d\n", 0x044f, 0xd003 ); + return 1; + } +// handle = hid_open( 0x044f, 0xd003, NULL); +// if (!handle) { +// printf("unable to open device\n"); +// return 1; +// } +// print_device_info( handle ); + + print_device_info( devdesc->device ); + + char my_custom_data[40] = "Hello!"; + +// descriptor = hid_read_descriptor( handle ); +// if ( descriptor == NULL ){ +// printf("unable to read descriptor\n"); +// return 1; +// } + +// res = hid_get_report_descriptor( handle, descr_buf, HIDAPI_MAX_DESCRIPTOR_SIZE ); +// if (res < 0){ +// printf("Unable to read report descriptor\n"); +// return 1; +// } else { +// descriptor = (hid_device_descriptor *) malloc( sizeof( hid_device_descriptor) ); +// hid_descriptor_init( descriptor ); +// hid_parse_report_descriptor( descr_buf, res, descriptor ); +// +// } + + hid_set_descriptor_callback( devdesc->descriptor, (hid_descriptor_callback) my_descriptor_cb, my_custom_data ); + hid_set_element_callback( devdesc->descriptor, (hid_element_callback) my_element_cb, my_custom_data ); + + // Set the hid_read() function to be non-blocking. +// hid_set_nonblocking(handle, 1); + + struct hid_device_element * cur_element = devdesc->descriptor->first; + + while (cur_element) { + print_element_info( cur_element ); + cur_element = cur_element->next; + } + +// Request state (cmd 0x81). The first byte is the report number (0x1). +// buf[0] = 0x1; +// buf[1] = 0x81; +// hid_write(handle, buf, 17); +// if (res < 0) +// printf("Unable to write() (2)\n"); + + // Read requested state. hid_read() has been set to be + // non-blocking by the call to hid_set_nonblocking() above. + // This loop demonstrates the non-blocking nature of hid_read(). + res = 0; + while (1) { + res = hid_read(devdesc->device, buf, sizeof(buf)); + if ( res > 0 ) { + hid_parse_input_report( buf, res, devdesc->descriptor ); + } + #ifdef WIN32 + Sleep(500); + #else + usleep(500*100); + #endif + } + + hid_close_device( devdesc ); +// hid_close(handle); + + /* Free static HIDAPI objects. */ + hid_exit(); + +#ifdef WIN32 + system("pause"); +#endif + + return 0; + +} \ No newline at end of file diff --git a/hidparsertest/CMakeLists.txt b/hidparsertest/CMakeLists.txt new file mode 100644 index 00000000..367426b2 --- /dev/null +++ b/hidparsertest/CMakeLists.txt @@ -0,0 +1,13 @@ +message( "===hidparsertest cmakelists===" ) + +include_directories( + ${CMAKE_BINARY_DIR} + ${hidapi_SOURCE_DIR}/hidapi/ + ${hidapi_SOURCE_DIR}/hidapi_parser/ +) + +add_executable( hidparsertest hidparsertest.c ) + +target_link_libraries(hidparsertest hidapi hidapi_parser ) + +install(TARGETS hidparsertest DESTINATION bin) \ No newline at end of file diff --git a/hidparsertest/hidparsertest.c b/hidparsertest/hidparsertest.c new file mode 100644 index 00000000..19106bf5 --- /dev/null +++ b/hidparsertest/hidparsertest.c @@ -0,0 +1,245 @@ +/* hidapi_parser $ + * + * Copyright (C) 2013, Marije Baalman + * This work was funded by a crowd-funding initiative for SuperCollider's [1] HID implementation + * including a substantial donation from BEK, Bergen Center for Electronic Arts, Norway + * + * [1] http://supercollider.sourceforge.net + * [2] http://www.bek.no + * + * 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 3 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +// #include + +#include +#include "hidapi_parser.h" + + +// Headers needed for sleeping. +#ifdef _WIN32 + #include +#else + #include +#endif + +void list_devices( void ){ + struct hid_device_info *devs, *cur_dev; + devs = hid_enumerate(0x0, 0x0); + cur_dev = devs; + while (cur_dev) { + printf("Device Found\n type: 0x%04hx 0x%04hx\n path: %s\n serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number); + printf("\n"); + printf(" Manufacturer: %ls\n", cur_dev->manufacturer_string); + printf(" Product: %ls\n", cur_dev->product_string); + printf(" Release: %hx\n", cur_dev->release_number); + printf(" Interface: %d\n", cur_dev->interface_number); + printf("\n"); + cur_dev = cur_dev->next; + } + hid_free_enumeration(devs); +} + +// static hid_device * open_device( char vendor_id, char product_id ){ +// handle = +// return handle; +// } + +#define MAX_STR 255 + +void print_element_info( struct hid_device_element *element ){ + + printf( "index: %i, usage_page: %i, usage: %i, iotype: %i, type: %i, \n \ + \tlogical_min: %i, logical_max: %i, \n \ + \tphys_min: %i, phys_max: %i, unit_exponent: %i, unit: %i, \n \ + \treport_size: %i, report_id: %i, report_index: %i \n", + element->index, element->usage_page, element->usage, element->io_type, element->type, + element->logical_min, element->logical_max, + element->phys_min, element->phys_max, + element->unit_exponent, element->unit, + element->report_size, element->report_id, element->report_index ); +} + +void print_device_info( hid_device *handle ){ + wchar_t wstr[MAX_STR]; + int res; + // Read the Manufacturer String + wstr[0] = 0x0000; + res = hid_get_manufacturer_string(handle, wstr, MAX_STR); + if (res < 0) + printf("Unable to read manufacturer string\n"); + printf("Manufacturer String: %ls\n", wstr); + + // Read the Product String + wstr[0] = 0x0000; + res = hid_get_product_string(handle, wstr, MAX_STR); + if (res < 0) + printf("Unable to read product string\n"); + printf("Product String: %ls\n", wstr); + + // Read the Serial Number String + wstr[0] = 0x0000; + res = hid_get_serial_number_string(handle, wstr, MAX_STR); + if (res < 0) + printf("Unable to read serial number string\n"); + printf("Serial Number String: (%d) %ls", wstr[0], wstr); + printf("\n"); + + // Read Indexed String 1 + wstr[0] = 0x0000; + res = hid_get_indexed_string(handle, 1, wstr, MAX_STR); + if (res < 0) + printf("Unable to read indexed string 1\n"); + printf("Indexed String 1: %ls\n", wstr); +} + +static void my_element_cb(const struct hid_device_element *el, void *data) +{ + printf("in %s\t", __func__); + printf("element: usage %i, value %i, index %i\t", el->usage, el->value, el->index ); + printf("user_data: %s\n", (const char *)data); +} + +static void my_descriptor_cb(const struct hid_device_descriptor *dd, void *data) +{ + printf("in %s\t", __func__); +// printf("element: usage %i, value %i, index %i\n", el->usage, el->value, el->index ); + printf("user_data: %s\n", (const char *)data); +} + +int main(int argc, char* argv[]){ + + int res; + unsigned char buf[256]; + unsigned char descr_buf[HIDAPI_MAX_DESCRIPTOR_SIZE]; + + struct hid_dev_desc *devdesc; +// struct hid_device_descriptor *descriptor; +// hid_device *handle; + +#ifdef WIN32 + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); +#endif + + if (hid_init()) + return -1; + list_devices(); + + int vendor_id; + int product_id; + + if (argc == 3 ){ + vendor_id = atoi( argv[1] ); + product_id = atoi( argv[1] ); + } else { + printf( "please run again with vendor and product id to open specified device, e.g. hidparsertest 0x044f 0xd003\n" ); + return 0; + } + printf( "vendor %i, product %i", vendor_id, product_id ); + + + devdesc = hid_open_device( 0x044f, 0xd003, NULL ); + if (!devdesc){ + fprintf(stderr, "Unable to open device %d, %d\n", 0x044f, 0xd003 ); + return 1; + } +// handle = hid_open( 0x044f, 0xd003, NULL); +// if (!handle) { +// printf("unable to open device\n"); +// return 1; +// } +// print_device_info( handle ); + + print_device_info( devdesc->device ); + +// descriptor = hid_read_descriptor( handle ); +// if ( descriptor == NULL ){ +// printf("unable to read descriptor\n"); +// return 1; +// } + +// res = hid_get_report_descriptor( handle, descr_buf, HIDAPI_MAX_DESCRIPTOR_SIZE ); +// if (res < 0){ +// printf("Unable to read report descriptor\n"); +// return 1; +// } else { +// descriptor = (hid_device_descriptor *) malloc( sizeof( hid_device_descriptor) ); +// hid_descriptor_init( descriptor ); +// hid_parse_report_descriptor( descr_buf, res, descriptor ); +// +// } + + // Set the hid_read() function to be non-blocking. +// hid_set_nonblocking(handle, 1); + + struct hid_device_element * cur_element = devdesc->descriptor->first; + + printf( "number of elements in device: %i\n", devdesc->descriptor->num_elements ); + while (cur_element != NULL ) { + printf("cur_element %i\n", cur_element ); + print_element_info( cur_element ); +// printf("press key to continue\n" ); +// getchar(); + cur_element = cur_element->next; + } + + printf("press key to continue\n" ); + getchar(); + + char my_custom_data[40] = "Hello!"; + hid_set_descriptor_callback( devdesc->descriptor, (hid_descriptor_callback) my_descriptor_cb, my_custom_data ); + hid_set_element_callback( devdesc->descriptor, (hid_element_callback) my_element_cb, my_custom_data ); + +// Request state (cmd 0x81). The first byte is the report number (0x1). +// buf[0] = 0x1; +// buf[1] = 0x81; +// hid_write(handle, buf, 17); +// if (res < 0) +// printf("Unable to write() (2)\n"); + + // Read requested state. hid_read() has been set to be + // non-blocking by the call to hid_set_nonblocking() above. + // This loop demonstrates the non-blocking nature of hid_read(). + res = 0; + while (1) { + res = hid_read(devdesc->device, buf, sizeof(buf)); + if ( res > 0 ) { + hid_parse_input_report( buf, res, devdesc->descriptor ); + } + #ifdef WIN32 + Sleep(500); + #else + usleep(500*100); + #endif + } + + hid_close_device( devdesc ); +// hid_close(handle); + + /* Free static HIDAPI objects. */ + hid_exit(); + +#ifdef WIN32 + system("pause"); +#endif + + return 0; + +} \ No newline at end of file diff --git a/hidtest/CMakeLists.txt b/hidtest/CMakeLists.txt new file mode 100644 index 00000000..75e71855 --- /dev/null +++ b/hidtest/CMakeLists.txt @@ -0,0 +1,18 @@ +message( "===hidtest cmakelists===" ) + +include_directories( + ${CMAKE_BINARY_DIR} + ${hidapi_SOURCE_DIR}/hidapi/ + ${hidapi_SOURCE_DIR}/hidapi_parser/ +) + + +set(hidtest_SRCS + hidtest.cpp +) + +add_executable( hidtest ${hidtest_SRCS} ) + +target_link_libraries(hidtest hidapi hidapi_parser ) + +install(TARGETS hidtest DESTINATION bin) \ No newline at end of file diff --git a/hidtest/hidtest.cpp b/hidtest/hidtest.cpp index 7027a101..2f8aa982 100644 --- a/hidtest/hidtest.cpp +++ b/hidtest/hidtest.cpp @@ -1,3 +1,27 @@ +/* hidapi_parser $ + * + * Copyright (C) 2013, Marije Baalman + * This work was funded by a crowd-funding initiative for SuperCollider's [1] HID implementation + * including a substantial donation from BEK, Bergen Center for Electronic Arts, Norway + * + * [1] http://supercollider.sourceforge.net + * [2] http://www.bek.no + * + * 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 3 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + /******************************************************* Windows HID simplification @@ -14,6 +38,8 @@ which use HIDAPI. ********************************************************/ +//TODO: add copyright notice + #include #include #include @@ -27,14 +53,165 @@ #include #endif + +//// ---------- HID descriptor parser + +#define MAX_DESCRIPTOR 4096 + + +// main items +#define HID_INPUT 0x80 +#define HID_OUTPUT 0x90 +#define HID_COLLECTION 0xA0 +#define HID_FEATURE 0xB0 +// #define HID_COLLECTION 0xa1 +#define HID_END_COLLECTION 0xC0 + +// HID Report Items from HID 1.11 Section 6.2.2 +#define HID_USAGE_PAGE 0x04 +// #define HID_USAGE_PAGE 0x05 +// #define HID_USAGE_PAGE_EXT 0x06 + +#define HID_USAGE 0x08 +// #define HID_USAGE 0x09 +// #define HID_USAGE_EXT 0x0a + +#define HID_USAGE_MIN 0x18 +// #define HID_USAGE_MIN 0x19 +#define HID_USAGE_MAX 0x28 +// #define HID_USAGE_MAX 0x29 + +#define HID_DESIGNATOR_INDEX 0x38 +#define HID_DESIGNATOR_MIN 0x48 +#define HID_DESIGNATOR_MAX 0x58 + +#define HID_STRING_INDEX 0x78 +#define HID_STRING_MIN 0x88 +#define HID_STRING_MAX 0x98 + +#define HID_DELIMITER 0xA8 + +#define HID_LOGICAL_MIN 0x14 +// #define HID_LOGICAL_MIN 0x15 +// #define HID_LOGICAL_MIN_2 0x16 +#define HID_LOGICAL_MAX 0x24 +// #define HID_LOGICAL_MAX 0x25 +// #define HID_LOGICAL_MAX_2 0x26 + +#define HID_PHYSICAL_MIN 0x34 +// #define HID_PHYSICAL_MIN 0x35 +// #define HID_PHYSICAL_MIN_2 0x36 +// #define HID_PHYSICAL_MIN_3 0x37 +#define HID_PHYSICAL_MAX 0x44 +// #define HID_PHYSICAL_MAX 0x45 +// #define HID_PHYSICAL_MAX_2 0x46 + +#define HID_UNIT_EXPONENT 0x54 +// #define HID_UNIT 0x55 +#define HID_UNIT 0x64 +// #define HID_UNIT 0x65 + +#define HID_REPORT_SIZE 0x74 +// #define HID_REPORT_SIZE 0x75 +#define HID_REPORT_ID 0x84 + +#define HID_REPORT_COUNT 0x94 +// #define HID_REPORT_COUNT 0x95 + +#define HID_PUSH 0xA4 +#define HID_POP 0xB4 + +#define HID_RESERVED 0xC4 // above this it is all reserved + + +// HID Report Usage Pages from HID Usage Tables 1.12 Section 3, Table 1 +#define HID_USAGE_PAGE_GENERICDESKTOP 0x01 +#define HID_USAGE_PAGE_KEY_CODES 0x07 +#define HID_USAGE_PAGE_LEDS 0x08 +#define HID_USAGE_PAGE_BUTTONS 0x09 + +// HID Report Usages from HID Usage Tables 1.12 Section 4, Table 6 +#define HID_USAGE_POINTER 0x01 +#define HID_USAGE_MOUSE 0x02 +#define HID_USAGE_JOYSTICK 0x04 +#define HID_USAGE_KEYBOARD 0x06 +#define HID_USAGE_X 0x30 +#define HID_USAGE_Y 0x31 +#define HID_USAGE_Z 0x32 +#define HID_USAGE_RX 0x33 +#define HID_USAGE_RY 0x34 +#define HID_USAGE_RZ 0x35 +#define HID_USAGE_SLIDER 0x36 +#define HID_USAGE_DIAL 0x37 +#define HID_USAGE_WHEEL 0x38 + + +// HID Report Collection Types from HID 1.12 6.2.2.6 +#define HID_COLLECTION_PHYSICAL 0 +#define HID_COLLECTION_APPLICATION 1 + +// HID Input/Output/Feature Item Data (attributes) from HID 1.11 6.2.2.5 +/// more like flags +#define HID_ITEM_CONSTANT 0x1 +#define HID_ITEM_VARIABLE 0x2 +#define HID_ITEM_RELATIVE 0x4 +#define HID_ITEM_WRAP 0x8 +#define HID_ITEM_LINEAR 0x10 +#define HID_ITEM_PREFERRED 0x20 +#define HID_ITEM_NULL 0x40 +#define HID_ITEM_BITFIELD 0x80 + +// Report Types from HID 1.11 Section 7.2.1 +#define HID_REPORT_TYPE_INPUT 1 +#define HID_REPORT_TYPE_OUTPUT 2 +#define HID_REPORT_TYPE_FEATURE 3 + +struct hid_device_descriptor { + int num_elements; +// int usage_page; +// int usage; + /** Pointer to the first element */ + struct hid_device_element *first; +}; + +struct hid_device_element { + int index; + + int type; // button/axis + int vartype; // abs/relative + int usage_page; // usage page + int usage; // some kind of index (as from descriptor) + + int logical_min; + int logical_max; + + int phys_min; + int phys_max; + + int resolution; // in bits + + int value; + + /** Pointer to the next element */ + struct hid_device_element *next; +}; + +struct hid_device_descriptor *descriptor; + +#define BITMASK1(n) ((1ULL << (n)) - 1ULL) + int main(int argc, char* argv[]) { int res; unsigned char buf[256]; + unsigned char descr_buf[MAX_DESCRIPTOR]; #define MAX_STR 255 wchar_t wstr[MAX_STR]; hid_device *handle; int i; + + descriptor = (struct hid_device_descriptor *) malloc( sizeof( struct hid_device_descriptor) ); + descriptor->num_elements = 0; #ifdef WIN32 UNREFERENCED_PARAMETER(argc); @@ -69,7 +246,11 @@ int main(int argc, char* argv[]) // Open the device using the VID, PID, // and optionally the Serial number. ////handle = hid_open(0x4d8, 0x3f, L"12345"); - handle = hid_open(0x4d8, 0x3f, NULL); +// handle = hid_open(0x4d8, 0x3f, NULL); + // mouse: +// handle = hid_open(0x1241, 0x1166, NULL); + // run'n'drive + handle = hid_open(0x044f, 0xd003, NULL); if (!handle) { printf("unable to open device\n"); return 1; @@ -104,82 +285,383 @@ int main(int argc, char* argv[]) printf("Unable to read indexed string 1\n"); printf("Indexed String 1: %ls\n", wstr); - // Set the hid_read() function to be non-blocking. - hid_set_nonblocking(handle, 1); - - // Try to read from the device. There shoud be no - // data here, but execution should not block. - res = hid_read(handle, buf, 17); - - // Send a Feature Report to the device - buf[0] = 0x2; - buf[1] = 0xa0; - buf[2] = 0x0a; - buf[3] = 0x00; - buf[4] = 0x00; - res = hid_send_feature_report(handle, buf, 17); - if (res < 0) { - printf("Unable to send a feature report.\n"); - } - - memset(buf,0,sizeof(buf)); - // Read a Feature Report from the device - buf[0] = 0x2; - res = hid_get_feature_report(handle, buf, sizeof(buf)); - if (res < 0) { - printf("Unable to get a feature report.\n"); - printf("%ls", hid_error(handle)); - } - else { - // Print out the returned buffer. - printf("Feature Report\n "); - for (i = 0; i < res; i++) - printf("%02hhx ", buf[i]); - printf("\n"); + //// PARSING THE DESCRIPTOR + res = hid_get_report_descriptor( handle, descr_buf, MAX_DESCRIPTOR ); + if (res < 0) + printf("Unable to read report descriptor\n"); + printf("Data read: %i\n ", res); + // Print out the returned buffer. + + struct hid_device_element * prev_element; + int current_usage_page; + int current_usage; + int current_usages[256]; + int current_usage_index = 0; + int current_usage_min = -1; + int current_usage_max = -1; + int current_logical_min; + int current_logical_max; + int current_physical_min; + int current_physical_max; + int current_report_count; + int current_report_id; + int current_report_size; + char current_input; + char current_output; + int collection_nesting = 0; + + int next_byte_type = -1; + int next_byte_size = 0; + int next_val = 0; + + int toadd = 0; + int byte_count = 0; + +// for (i = 0; i < res; i++){ +// printf("\n%02hhx ", descr_buf[i]); +// } + for (i = 0; i < res; i++){ + printf("\n%02hhx ", descr_buf[i]); + printf("\tbyte_type %i, %i, %i \t", next_byte_type, next_byte_size, next_val); + if ( next_byte_type != -1 ){ + toadd = descr_buf[i]; + for ( int j=0; jindex = descriptor->num_elements; + new_element->type = current_input; // & HID_ITEM_VARIABLE); + // new_element->vartype = (current_input & HID_ITEM_RELATIVE); + new_element->usage_page = current_usage_page; + if ( current_usage_min != -1 ){ + new_element->usage = current_usage_min + j; + } else { + new_element->usage = current_usages[j]; + } + new_element->logical_min = current_logical_min; + new_element->logical_max = current_logical_max; + new_element->phys_min = current_physical_min; + new_element->phys_max = current_physical_max; + new_element->resolution = current_report_size; + new_element->value = 0; + if ( descriptor->num_elements == 0 ){ + descriptor->first = new_element; + } else { + prev_element->next = new_element; + } + descriptor->num_elements++; + prev_element = new_element; + } + current_usage_min = -1; + current_usage_max = -1; + current_usage_index = 0; + break; + case HID_OUTPUT: + current_output = next_val; + printf("output: %i", current_output); + printf("should create output element"); + // next_byte_type = -1; + current_usage_min = -1; + current_usage_max = -1; + current_usage_index = 0; + break; + case HID_FEATURE: + // current_output = next_val; + printf("feature: %i", next_val); + printf("should create feature element"); + // next_byte_type = -1; + current_usage_min = -1; + current_usage_max = -1; + current_usage_index = 0; + break; + default: + if ( next_byte_type >= HID_RESERVED ){ + printf("reserved bytes 0x%02hhx, %i", next_byte_type, next_val ); + } else { + printf("undefined byte type 0x%02hhx, %i", next_byte_type, next_val ); + } + } + next_byte_type = -1; + } + } else { +// printf("\n"); + printf("\tsetting next byte type: %i, 0x%02hhx ", descr_buf[i], descr_buf[i] ); + if ( descr_buf[i] == HID_END_COLLECTION ){ // JUST one byte + collection_nesting--; + printf("\tend collection: %i, %i\n", collection_nesting, descr_buf[i] ); + } else { + byte_count = 0; + toadd = 0; + next_val = 0; + next_byte_type = descr_buf[i] & 0xFC; + next_byte_size = descr_buf[i] & 0x03; + if ( next_byte_size == 3 ){ + next_byte_size = 4; + } + printf("\t next byte type: 0x%02hhx, %i ", next_byte_type, next_byte_size ); + } + } } + printf("\n"); + printf("number of elements, %i\n", descriptor->num_elements ); + + // Set the hid_read() function to be non-blocking. + hid_set_nonblocking(handle, 1); +// +// // Try to read from the device. There shoud be no +// // data here, but execution should not block. +// res = hid_read(handle, buf, 17); +// +// // Send a Feature Report to the device +// buf[0] = 0x2; +// buf[1] = 0xa0; +// buf[2] = 0x0a; +// buf[3] = 0x00; +// buf[4] = 0x00; +// res = hid_send_feature_report(handle, buf, 17); +// if (res < 0) { +// printf("Unable to send a feature report.\n"); +// } +// +// memset(buf,0,sizeof(buf)); +// +// // Read a Feature Report from the device +// buf[0] = 0x2; +// res = hid_get_feature_report(handle, buf, sizeof(buf)); +// if (res < 0) { +// printf("Unable to get a feature report.\n"); +// printf("%ls", hid_error(handle)); +// } +// else { +// // Print out the returned buffer. +// printf("Feature Report\n "); +// for (i = 0; i < res; i++) +// printf("%02hhx ", buf[i]); +// printf("\n"); +// } +// +// memset(buf,0,sizeof(buf)); +// +// // Toggle LED (cmd 0x80). The first byte is the report number (0x1). +// buf[0] = 0x1; +// buf[1] = 0x80; +// res = hid_write(handle, buf, 17); +// if (res < 0) { +// printf("Unable to write()\n"); +// printf("Error: %ls\n", hid_error(handle)); +// } +// +// +// // Request state (cmd 0x81). The first byte is the report number (0x1). +// buf[0] = 0x1; +// buf[1] = 0x81; +// hid_write(handle, buf, 17); +// if (res < 0) +// printf("Unable to write() (2)\n"); - memset(buf,0,sizeof(buf)); - - // Toggle LED (cmd 0x80). The first byte is the report number (0x1). - buf[0] = 0x1; - buf[1] = 0x80; - res = hid_write(handle, buf, 17); - if (res < 0) { - printf("Unable to write()\n"); - printf("Error: %ls\n", hid_error(handle)); - } - - // Request state (cmd 0x81). The first byte is the report number (0x1). - buf[0] = 0x1; - buf[1] = 0x81; - hid_write(handle, buf, 17); - if (res < 0) - printf("Unable to write() (2)\n"); +// for ( int i=0; i<8; i++ ){ +// printf("bitmask test, %i, %i, %02hhx\n", i, BITMASK1(i), BITMASK1(i) ); +// } // Read requested state. hid_read() has been set to be // non-blocking by the call to hid_set_nonblocking() above. // This loop demonstrates the non-blocking nature of hid_read(). res = 0; - while (res == 0) { + while (true) { res = hid_read(handle, buf, sizeof(buf)); - if (res == 0) - printf("waiting...\n"); - if (res < 0) - printf("Unable to read()\n"); +// if (res == 0) +// printf("waiting...\n"); +// if (res < 0) +// printf("Unable to read()\n"); + if ( res > 0 ) { +// printf("Data read:\n"); + // Print out the returned buffer. + hid_device_element * cur_element = descriptor->first; + for ( int i = 0; i < res; i++){ + char curbyte = buf[i]; + printf("byte %02hhx \t", buf[i]); + // read byte: + if ( cur_element->resolution < 8 ){ + int bitindex = 0; + while ( bitindex < 8 ){ + // read bit + cur_element->value = (curbyte >> bitindex) & BITMASK1( cur_element->resolution ); + printf("element page %i, usage %i, type %i, index %i, value %i\n", cur_element->usage_page, cur_element->usage, cur_element->type, cur_element->index, cur_element->value ); + bitindex += cur_element->resolution; + cur_element = cur_element->next; + } + } else if ( cur_element->resolution == 8 ){ + cur_element->value = curbyte; + printf("element page %i, usage %i, type %i, index %i, value %i\n", cur_element->usage_page, cur_element->usage, cur_element->type, cur_element->index,cur_element->value ); + cur_element = cur_element->next; + } // else: larger than 8 bits + } + printf("\n"); + } #ifdef WIN32 Sleep(500); #else - usleep(500*1000); + usleep(500*100); #endif } - printf("Data read:\n "); - // Print out the returned buffer. - for (i = 0; i < res; i++) - printf("%02hhx ", buf[i]); - printf("\n"); hid_close(handle); diff --git a/libusb/CMakeLists.txt b/libusb/CMakeLists.txt new file mode 100644 index 00000000..5b2ee16c --- /dev/null +++ b/libusb/CMakeLists.txt @@ -0,0 +1,32 @@ +message( "===libusb cmakelists===" ) + + +#rt - clock_gettime +# may not be needed anymore, as of glibc 2.17 + +# include(CheckLibraryExists) +# CHECK_LIBRARY_EXISTS(rt clock_gettime "time.h" HAVE_RT) + +# if( NOT HAVE_RT ) +# message( "--cannot find rt library" ) +# endif() + +# target_link_libraries( hid-usb -lrt) + +#libusb 1.0 + +find_package( libusb-1.0 ) + +## NOT TESTED: +IF(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") + find_package( IConv ) +ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") + +#TODO: ADD ICONV +# set( hidapi_INCLUDE_DIRS ${LIBUSB_1_INCLUDE_DIRS} ${hidapi_SOURCE_DIR}/hidapi/ ${PTHREADS_INCLUDE_DIR}) +# set( hidapi_LIBS ${LIBUSB_1_LIBRARIES} ${PTHREADS_LIBRARIES}) +# set( hidapi_SRCS hid.c ) + +include_directories( ${LIBUSB_1_INCLUDE_DIRS} ${PTHREADS_INCLUDE_DIR} ${hidapi_SOURCE_DIR}/hidapi/ ) +add_library( hidapi STATIC hid.c ) +target_link_libraries( hidapi ${LIBUSB_1_LIBRARIES} ${PTHREADS_LIBRARIES} ) diff --git a/libusb/hid.c b/libusb/hid.c index 72d44c4f..52fabdc3 100644 --- a/libusb/hid.c +++ b/libusb/hid.c @@ -108,6 +108,7 @@ struct hid_device_ { 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. */ @@ -690,10 +691,12 @@ static void read_callback(struct libusb_transfer *transfer) } 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) { @@ -708,6 +711,7 @@ static void read_callback(struct libusb_transfer *transfer) if (res != 0) { LOG("Unable to submit URB. libusb error code: %d\n", res); dev->shutdown_thread = 1; + dev->cancelled = 1; } } @@ -757,10 +761,10 @@ static void *read_thread(void *param) /* Cancel any transfer that may be pending. This call will fail if no transfers are pending, but that's OK. */ - if (libusb_cancel_transfer(dev->transfer) == 0) { - /* The transfer was cancelled, so wait for its completion. */ - libusb_handle_events(usb_context); - } + 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 diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt new file mode 100644 index 00000000..b03febce --- /dev/null +++ b/linux/CMakeLists.txt @@ -0,0 +1,20 @@ +message( "===linux hidraw cmakelists===" ) + +#udev +find_package(UDev) + +# include_directories(${UDEV_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/hidapi/) +# link_libraries(${UDEV_LIBRARIES}) + +# message( "hidapi source dir is: ${hidapi_SOURCE_DIR}" ) + +# set( hidapi_INCLUDE_DIRS ${UDEV_INCLUDE_DIR} ${hidapi_SOURCE_DIR}/hidapi/) +# set( hidapi_LIBS ${UDEV_LIBRARIES}) +# set( hidapi_SRCS hid.c ) + +# message( "hidapi include dirs are: ${hidapi_INCLUDE_DIRS}" ) + +include_directories( ${UDEV_INCLUDE_DIR} ${hidapi_SOURCE_DIR}/hidapi/ ) +add_library( hidapi STATIC hid.c ) +target_link_libraries( hidapi ${UDEV_LIBRARIES} ) +# link_directories( hidapi ${UDEV_LIBRARIES} ) \ No newline at end of file diff --git a/mac/CMakeLists.txt b/mac/CMakeLists.txt new file mode 100644 index 00000000..48ae217b --- /dev/null +++ b/mac/CMakeLists.txt @@ -0,0 +1,10 @@ +message( "===mac cmakelists===" ) + +#pthreads +#-framework IOKit -framework CoreFoundation + +include_directories( ${UDEV_INCLUDE_DIR} ${hidapi_SOURCE_DIR}/hidapi/ ) +add_library( hidapi STATIC hid.c ) +# target_link_libraries( hidapi ${UDEV_LIBRARIES} ) + +target_link_libraries(hidapi "-framework IOKit -framework CoreFoundation") \ No newline at end of file diff --git a/mac/hid.c b/mac/hid.c index cbffeed3..c25fe3e1 100644 --- a/mac/hid.c +++ b/mac/hid.c @@ -433,6 +433,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, process_pending_events(); /* Get a list of the Devices */ + IOHIDManagerSetDeviceMatching(hid_mgr, NULL); CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr); /* Convert the list into a C array so we can iterate easily. */ diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt new file mode 100644 index 00000000..3545d7db --- /dev/null +++ b/windows/CMakeLists.txt @@ -0,0 +1,3 @@ +message( "===windows cmakelists===" ) + +# target_link_libraries( -lsetupapi ) \ No newline at end of file