├── .gitattributes ├── .gitignore ├── AUTHORS.txt ├── HACKING.txt ├── LICENSE-bsd.txt ├── LICENSE-gpl3.txt ├── LICENSE-orig.txt ├── LICENSE.txt ├── Makefile.am ├── README.txt ├── android └── jni │ └── Android.mk ├── bootstrap ├── configure.ac ├── doxygen └── Doxyfile ├── hidapi └── hidapi.h ├── hidtest ├── .gitignore ├── Makefile.am └── hidtest.cpp ├── libusb ├── .gitignore ├── Makefile-manual ├── Makefile.am ├── Makefile.freebsd ├── Makefile.linux └── hid.c ├── linux ├── .gitignore ├── Makefile-manual ├── Makefile.am ├── README.txt └── hid.c ├── m4 ├── .gitignore ├── ax_pthread.m4 └── pkg.m4 ├── mac ├── .gitignore ├── Makefile-manual ├── Makefile.am └── hid.c ├── pc ├── .gitignore ├── hidapi-hidraw.pc.in ├── hidapi-libusb.pc.in └── hidapi.pc.in ├── testgui ├── .gitignore ├── Makefile-manual ├── Makefile.am ├── Makefile.freebsd ├── Makefile.linux ├── Makefile.mac ├── Makefile.mingw ├── TestGUI.app.in │ └── Contents │ │ ├── Info.plist │ │ ├── PkgInfo │ │ └── Resources │ │ ├── English.lproj │ │ └── InfoPlist.strings │ │ └── Signal11.icns ├── copy_to_bundle.sh ├── mac_support.cpp ├── mac_support.h ├── mac_support_cocoa.m ├── start.sh ├── test.cpp ├── testgui.sln └── testgui.vcproj ├── udev └── 99-hid.rules └── windows ├── .gitignore ├── Makefile-manual ├── Makefile.am ├── Makefile.mingw ├── ddk_build ├── .gitignore ├── hidapi.def ├── makefile └── sources ├── hid.c ├── hidapi.sln ├── hidapi.vcproj └── hidtest.vcproj /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | *.sln text eol=crlf 4 | *.vcproj text eol=crlf 5 | 6 | bootstrap text eol=lf 7 | configure.ac text eol=lf 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Autotools-added generated files 3 | Makefile.in 4 | aclocal.m4 5 | autom4te.cache/ 6 | config.* 7 | configure 8 | depcomp 9 | install-sh 10 | libusb/Makefile.in 11 | linux/Makefile.in 12 | ltmain.sh 13 | mac/Makefile.in 14 | missing 15 | testgui/Makefile.in 16 | windows/Makefile.in 17 | 18 | Makefile 19 | stamp-h1 20 | libtool 21 | -------------------------------------------------------------------------------- /AUTHORS.txt: -------------------------------------------------------------------------------- 1 | 2 | HIDAPI Authors: 3 | 4 | Alan Ott : 5 | Original Author and Maintainer 6 | Linux, Windows, and Mac implementations 7 | 8 | Ludovic Rousseau : 9 | Formatting for Doxygen documentation 10 | Bug fixes 11 | Correctness fixes 12 | 13 | 14 | For a comprehensive list of contributions, see the commit list at github: 15 | http://github.com/signal11/hidapi/commits/master 16 | 17 | -------------------------------------------------------------------------------- /HACKING.txt: -------------------------------------------------------------------------------- 1 | This file is mostly for the maintainer. 2 | 3 | 1. Build hidapi.dll 4 | 2. Build hidtest.exe in DEBUG and RELEASE 5 | 3. Commit all 6 | 7 | 4. Run the Following 8 | export VERSION=0.1.0 9 | export TAG_NAME=hidapi-$VERSION 10 | git tag $TAG_NAME 11 | git archive --format zip --prefix $TAG_NAME/ $TAG_NAME >../$TAG_NAME.zip 12 | 5. Test the zip file. 13 | 6. Run the following: 14 | git push origin $TAG_NAME 15 | 16 | -------------------------------------------------------------------------------- /LICENSE-bsd.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Alan Ott, Signal 11 Software 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of Signal 11 Software nor the names of its 13 | contributors may be used to endorse or promote products derived from 14 | this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /LICENSE-orig.txt: -------------------------------------------------------------------------------- 1 | HIDAPI - Multi-Platform library for 2 | communication with HID devices. 3 | 4 | Copyright 2009, Alan Ott, Signal 11 Software. 5 | All Rights Reserved. 6 | 7 | This software may be used by anyone for any reason so 8 | long as the copyright notice in the source files 9 | remains intact. 10 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | HIDAPI can be used under one of three licenses. 2 | 3 | 1. The GNU General Public License, version 3.0, in LICENSE-gpl3.txt 4 | 2. A BSD-Style License, in LICENSE-bsd.txt. 5 | 3. The more liberal original HIDAPI license. LICENSE-orig.txt 6 | 7 | The license chosen is at the discretion of the user of HIDAPI. For example: 8 | 1. An author of GPL software would likely use HIDAPI under the terms of the 9 | GPL. 10 | 11 | 2. An author of commercial closed-source software would likely use HIDAPI 12 | under the terms of the BSD-style license or the original HIDAPI license. 13 | 14 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | ACLOCAL_AMFLAGS = -I m4 3 | 4 | if OS_FREEBSD 5 | pkgconfigdir=$(prefix)/libdata/pkgconfig 6 | else 7 | pkgconfigdir=$(libdir)/pkgconfig 8 | endif 9 | 10 | if OS_LINUX 11 | pkgconfig_DATA=pc/hidapi-hidraw.pc pc/hidapi-libusb.pc 12 | else 13 | pkgconfig_DATA=pc/hidapi.pc 14 | endif 15 | 16 | SUBDIRS= 17 | 18 | if OS_LINUX 19 | SUBDIRS += linux libusb 20 | endif 21 | 22 | if OS_DARWIN 23 | SUBDIRS += mac 24 | endif 25 | 26 | if OS_FREEBSD 27 | SUBDIRS += libusb 28 | endif 29 | 30 | if OS_KFREEBSD 31 | SUBDIRS += libusb 32 | endif 33 | 34 | if OS_WINDOWS 35 | SUBDIRS += windows 36 | endif 37 | 38 | SUBDIRS += hidtest 39 | 40 | if BUILD_TESTGUI 41 | SUBDIRS += testgui 42 | endif 43 | 44 | EXTRA_DIST = udev doxygen 45 | 46 | dist_doc_DATA = \ 47 | README.txt \ 48 | AUTHORS.txt \ 49 | LICENSE-bsd.txt \ 50 | LICENSE-gpl3.txt \ 51 | LICENSE-orig.txt \ 52 | LICENSE.txt 53 | 54 | SCMCLEAN_TARGETS= \ 55 | aclocal.m4 \ 56 | config.guess \ 57 | config.sub \ 58 | configure \ 59 | config.h.in \ 60 | depcomp \ 61 | install-sh \ 62 | ltmain.sh \ 63 | missing \ 64 | mac/Makefile.in \ 65 | testgui/Makefile.in \ 66 | libusb/Makefile.in \ 67 | Makefile.in \ 68 | linux/Makefile.in \ 69 | windows/Makefile.in \ 70 | m4/libtool.m4 \ 71 | m4/lt~obsolete.m4 \ 72 | m4/ltoptions.m4 \ 73 | m4/ltsugar.m4 \ 74 | m4/ltversion.m4 75 | 76 | SCMCLEAN_DIR_TARGETS = \ 77 | autom4te.cache 78 | 79 | scm-clean: distclean 80 | rm -f $(SCMCLEAN_TARGETS) 81 | rm -Rf $(SCMCLEAN_DIR_TARGETS) 82 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | HIDAPI library for Windows, Linux, FreeBSD and Mac OS X 2 | ========================================================= 3 | 4 | About 5 | ====== 6 | 7 | HIDAPI is a multi-platform library which allows an application to interface 8 | with USB and Bluetooth HID-Class devices on Windows, Linux, FreeBSD, and Mac 9 | OS X. HIDAPI can be either built as a shared library (.so or .dll) or 10 | can be embedded directly into a target application by adding a single source 11 | file (per platform) and a single header. 12 | 13 | HIDAPI has four back-ends: 14 | * Windows (using hid.dll) 15 | * Linux/hidraw (using the Kernel's hidraw driver) 16 | * Linux/libusb (using libusb-1.0) 17 | * FreeBSD (using libusb-1.0) 18 | * Mac (using IOHidManager) 19 | 20 | On Linux, either the hidraw or the libusb back-end can be used. There are 21 | tradeoffs, and the functionality supported is slightly different. 22 | 23 | Linux/hidraw (linux/hid.c): 24 | This back-end uses the hidraw interface in the Linux kernel. While this 25 | back-end will support both USB and Bluetooth, it has some limitations on 26 | kernels prior to 2.6.39, including the inability to send or receive feature 27 | reports. In addition, it will only communicate with devices which have 28 | hidraw nodes associated with them. Keyboards, mice, and some other devices 29 | which are blacklisted from having hidraw nodes will not work. Fortunately, 30 | for nearly all the uses of hidraw, this is not a problem. 31 | 32 | Linux/FreeBSD/libusb (libusb/hid.c): 33 | This back-end uses libusb-1.0 to communicate directly to a USB device. This 34 | back-end will of course not work with Bluetooth devices. 35 | 36 | HIDAPI also comes with a Test GUI. The Test GUI is cross-platform and uses 37 | Fox Toolkit (http://www.fox-toolkit.org). It will build on every platform 38 | which HIDAPI supports. Since it relies on a 3rd party library, building it 39 | is optional but recommended because it is so useful when debugging hardware. 40 | 41 | What Does the API Look Like? 42 | ============================= 43 | The API provides the the most commonly used HID functions including sending 44 | and receiving of input, output, and feature reports. The sample program, 45 | which communicates with a heavily hacked up version of the Microchip USB 46 | Generic HID sample looks like this (with error checking removed for 47 | simplicity): 48 | 49 | #ifdef WIN32 50 | #include 51 | #endif 52 | #include 53 | #include 54 | #include "hidapi.h" 55 | 56 | #define MAX_STR 255 57 | 58 | int main(int argc, char* argv[]) 59 | { 60 | int res; 61 | unsigned char buf[65]; 62 | wchar_t wstr[MAX_STR]; 63 | hid_device *handle; 64 | int i; 65 | 66 | // Initialize the hidapi library 67 | res = hid_init(); 68 | 69 | // Open the device using the VID, PID, 70 | // and optionally the Serial number. 71 | handle = hid_open(0x4d8, 0x3f, NULL); 72 | 73 | // Read the Manufacturer String 74 | res = hid_get_manufacturer_string(handle, wstr, MAX_STR); 75 | wprintf(L"Manufacturer String: %s\n", wstr); 76 | 77 | // Read the Product String 78 | res = hid_get_product_string(handle, wstr, MAX_STR); 79 | wprintf(L"Product String: %s\n", wstr); 80 | 81 | // Read the Serial Number String 82 | res = hid_get_serial_number_string(handle, wstr, MAX_STR); 83 | wprintf(L"Serial Number String: (%d) %s\n", wstr[0], wstr); 84 | 85 | // Read Indexed String 1 86 | res = hid_get_indexed_string(handle, 1, wstr, MAX_STR); 87 | wprintf(L"Indexed String 1: %s\n", wstr); 88 | 89 | // Toggle LED (cmd 0x80). The first byte is the report number (0x0). 90 | buf[0] = 0x0; 91 | buf[1] = 0x80; 92 | res = hid_write(handle, buf, 65); 93 | 94 | // Request state (cmd 0x81). The first byte is the report number (0x0). 95 | buf[0] = 0x0; 96 | buf[1] = 0x81; 97 | res = hid_write(handle, buf, 65); 98 | 99 | // Read requested state 100 | res = hid_read(handle, buf, 65); 101 | 102 | // Print out the returned buffer. 103 | for (i = 0; i < 4; i++) 104 | printf("buf[%d]: %d\n", i, buf[i]); 105 | 106 | // Finalize the hidapi library 107 | res = hid_exit(); 108 | 109 | return 0; 110 | } 111 | 112 | If you have your own simple test programs which communicate with standard 113 | hardware development boards (such as those from Microchip, TI, Atmel, 114 | FreeScale and others), please consider sending me something like the above 115 | for inclusion into the HIDAPI source. This will help others who have the 116 | same hardware as you do. 117 | 118 | License 119 | ======== 120 | HIDAPI may be used by one of three licenses as outlined in LICENSE.txt. 121 | 122 | Download 123 | ========= 124 | HIDAPI can be downloaded from github 125 | git clone git://github.com/signal11/hidapi.git 126 | 127 | Build Instructions 128 | =================== 129 | 130 | This section is long. Don't be put off by this. It's not long because it's 131 | complicated to build HIDAPI; it's quite the opposite. This section is long 132 | because of the flexibility of HIDAPI and the large number of ways in which 133 | it can be built and used. You will likely pick a single build method. 134 | 135 | HIDAPI can be built in several different ways. If you elect to build a 136 | shared library, you will need to build it from the HIDAPI source 137 | distribution. If you choose instead to embed HIDAPI directly into your 138 | application, you can skip the building and look at the provided platform 139 | Makefiles for guidance. These platform Makefiles are located in linux/ 140 | libusb/ mac/ and windows/ and are called Makefile-manual. In addition, 141 | Visual Studio projects are provided. Even if you're going to embed HIDAPI 142 | into your project, it is still beneficial to build the example programs. 143 | 144 | 145 | Prerequisites: 146 | --------------- 147 | 148 | Linux: 149 | ------- 150 | On Linux, you will need to install development packages for libudev, 151 | libusb and optionally Fox-toolkit (for the test GUI). On 152 | Debian/Ubuntu systems these can be installed by running: 153 | sudo apt-get install libudev-dev libusb-1.0-0-dev libfox-1.6-dev 154 | 155 | If you downloaded the source directly from the git repository (using 156 | git clone), you'll need Autotools: 157 | sudo apt-get install autotools-dev autoconf automake libtool 158 | 159 | FreeBSD: 160 | --------- 161 | On FreeBSD you will need to install GNU make, libiconv, and 162 | optionally Fox-Toolkit (for the test GUI). This is done by running 163 | the following: 164 | pkg_add -r gmake libiconv fox16 165 | 166 | If you downloaded the source directly from the git repository (using 167 | git clone), you'll need Autotools: 168 | pkg_add -r autotools 169 | 170 | Mac: 171 | ----- 172 | On Mac, you will need to install Fox-Toolkit if you wish to build 173 | the Test GUI. There are two ways to do this, and each has a slight 174 | complication. Which method you use depends on your use case. 175 | 176 | If you wish to build the Test GUI just for your own testing on your 177 | own computer, then the easiest method is to install Fox-Toolkit 178 | using ports: 179 | sudo port install fox 180 | 181 | If you wish to build the TestGUI app bundle to redistribute to 182 | others, you will need to install Fox-toolkit from source. This is 183 | because the version of fox that gets installed using ports uses the 184 | ports X11 libraries which are not compatible with the Apple X11 185 | libraries. If you install Fox with ports and then try to distribute 186 | your built app bundle, it will simply fail to run on other systems. 187 | To install Fox-Toolkit manually, download the source package from 188 | http://www.fox-toolkit.org, extract it, and run the following from 189 | within the extracted source: 190 | ./configure && make && make install 191 | 192 | Windows: 193 | --------- 194 | On Windows, if you want to build the test GUI, you will need to get 195 | the hidapi-externals.zip package from the download site. This 196 | contains pre-built binaries for Fox-toolkit. Extract 197 | hidapi-externals.zip just outside of hidapi, so that 198 | hidapi-externals and hidapi are on the same level, as shown: 199 | 200 | Parent_Folder 201 | | 202 | +hidapi 203 | +hidapi-externals 204 | 205 | Again, this step is not required if you do not wish to build the 206 | test GUI. 207 | 208 | 209 | Building HIDAPI into a shared library on Unix Platforms: 210 | --------------------------------------------------------- 211 | 212 | On Unix-like systems such as Linux, FreeBSD, Mac, and even Windows, using 213 | Mingw or Cygwin, the easiest way to build a standard system-installed shared 214 | library is to use the GNU Autotools build system. If you checked out the 215 | source from the git repository, run the following: 216 | 217 | ./bootstrap 218 | ./configure 219 | make 220 | make install <----- as root, or using sudo 221 | 222 | If you downloaded a source package (ie: if you did not run git clone), you 223 | can skip the ./bootstrap step. 224 | 225 | ./configure can take several arguments which control the build. The two most 226 | likely to be used are: 227 | --enable-testgui 228 | Enable build of the Test GUI. This requires Fox toolkit to 229 | be installed. Instructions for installing Fox-Toolkit on 230 | each platform are in the Prerequisites section above. 231 | 232 | --prefix=/usr 233 | Specify where you want the output headers and libraries to 234 | be installed. The example above will put the headers in 235 | /usr/include and the binaries in /usr/lib. The default is to 236 | install into /usr/local which is fine on most systems. 237 | 238 | Building the manual way on Unix platforms: 239 | ------------------------------------------- 240 | 241 | Manual Makefiles are provided mostly to give the user and idea what it takes 242 | to build a program which embeds HIDAPI directly inside of it. These should 243 | really be used as examples only. If you want to build a system-wide shared 244 | library, use the Autotools method described above. 245 | 246 | To build HIDAPI using the manual makefiles, change to the directory 247 | of your platform and run make. For example, on Linux run: 248 | cd linux/ 249 | make -f Makefile-manual 250 | 251 | To build the Test GUI using the manual makefiles: 252 | cd testgui/ 253 | make -f Makefile-manual 254 | 255 | Building on Windows: 256 | --------------------- 257 | 258 | To build the HIDAPI DLL on Windows using Visual Studio, build the .sln file 259 | in the windows/ directory. 260 | 261 | To build the Test GUI on windows using Visual Studio, build the .sln file in 262 | the testgui/ directory. 263 | 264 | To build HIDAPI using MinGW or Cygwin using Autotools, use the instructions 265 | in the section titled "Building HIDAPI into a shared library on Unix 266 | Platforms" above. Note that building the Test GUI with MinGW or Cygwin will 267 | require the Windows procedure in the Prerequisites section above (ie: 268 | hidapi-externals.zip). 269 | 270 | To build HIDAPI using MinGW using the Manual Makefiles, see the section 271 | "Building the manual way on Unix platforms" above. 272 | 273 | HIDAPI can also be built using the Windows DDK (now also called the Windows 274 | Driver Kit or WDK). This method was originally required for the HIDAPI build 275 | but not anymore. However, some users still prefer this method. It is not as 276 | well supported anymore but should still work. Patches are welcome if it does 277 | not. To build using the DDK: 278 | 279 | 1. Install the Windows Driver Kit (WDK) from Microsoft. 280 | 2. From the Start menu, in the Windows Driver Kits folder, select Build 281 | Environments, then your operating system, then the x86 Free Build 282 | Environment (or one that is appropriate for your system). 283 | 3. From the console, change directory to the windows/ddk_build/ directory, 284 | which is part of the HIDAPI distribution. 285 | 4. Type build. 286 | 5. You can find the output files (DLL and LIB) in a subdirectory created 287 | by the build system which is appropriate for your environment. On 288 | Windows XP, this directory is objfre_wxp_x86/i386. 289 | 290 | Cross Compiling 291 | ================ 292 | 293 | This section talks about cross compiling HIDAPI for Linux using autotools. 294 | This is useful for using HIDAPI on embedded Linux targets. These 295 | instructions assume the most raw kind of embedded Linux build, where all 296 | prerequisites will need to be built first. This process will of course vary 297 | based on your embedded Linux build system if you are using one, such as 298 | OpenEmbedded or Buildroot. 299 | 300 | For the purpose of this section, it will be assumed that the following 301 | environment variables are exported. 302 | 303 | $ export STAGING=$HOME/out 304 | $ export HOST=arm-linux 305 | 306 | STAGING and HOST can be modified to suit your setup. 307 | 308 | Prerequisites 309 | -------------- 310 | 311 | Note that the build of libudev is the very basic configuration. 312 | 313 | Build Libusb. From the libusb source directory, run: 314 | ./configure --host=$HOST --prefix=$STAGING 315 | make 316 | make install 317 | 318 | Build libudev. From the libudev source directory, run: 319 | ./configure --disable-gudev --disable-introspection --disable-hwdb \ 320 | --host=$HOST --prefix=$STAGING 321 | make 322 | make install 323 | 324 | Building HIDAPI 325 | ---------------- 326 | 327 | Build HIDAPI: 328 | 329 | PKG_CONFIG_DIR= \ 330 | PKG_CONFIG_LIBDIR=$STAGING/lib/pkgconfig:$STAGING/share/pkgconfig \ 331 | PKG_CONFIG_SYSROOT_DIR=$STAGING \ 332 | ./configure --host=$HOST --prefix=$STAGING 333 | 334 | 335 | Signal 11 Software - 2010-04-11 336 | 2010-07-28 337 | 2011-09-10 338 | 2012-05-01 339 | 2012-07-03 340 | -------------------------------------------------------------------------------- /android/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH:= $(call my-dir) 2 | 3 | HIDAPI_ROOT_REL:= ../.. 4 | HIDAPI_ROOT_ABS:= $(LOCAL_PATH)/../.. 5 | 6 | include $(CLEAR_VARS) 7 | 8 | LOCAL_SRC_FILES := \ 9 | $(HIDAPI_ROOT_REL)/libusb/hid.c 10 | 11 | LOCAL_C_INCLUDES += \ 12 | $(HIDAPI_ROOT_ABS)/hidapi \ 13 | $(HIDAPI_ROOT_ABS)/android 14 | 15 | LOCAL_SHARED_LIBRARIES := libusb1.0 16 | 17 | LOCAL_MODULE := libhidapi 18 | 19 | include $(BUILD_SHARED_LIBRARY) 20 | -------------------------------------------------------------------------------- /bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh -x 2 | autoreconf --install --verbose --force 3 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ(2.63) 2 | 3 | # Version number. This is currently the only place. 4 | m4_define([HIDAPI_MAJOR], 0) 5 | m4_define([HIDAPI_MINOR], 8) 6 | m4_define([HIDAPI_RELEASE], 0) 7 | m4_define([HIDAPI_RC], -rc1) 8 | m4_define([VERSION_STRING], HIDAPI_MAJOR[.]HIDAPI_MINOR[.]HIDAPI_RELEASE[]HIDAPI_RC) 9 | 10 | AC_INIT([hidapi],[VERSION_STRING],[alan@signal11.us]) 11 | 12 | # Library soname version 13 | # Follow the following rules (particularly the ones in the second link): 14 | # http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html 15 | # http://sourceware.org/autobook/autobook/autobook_91.html 16 | lt_current="0" 17 | lt_revision="0" 18 | lt_age="0" 19 | LTLDFLAGS="-version-info ${lt_current}:${lt_revision}:${lt_age}" 20 | 21 | AC_CONFIG_MACRO_DIR([m4]) 22 | AM_INIT_AUTOMAKE([foreign -Wall -Werror]) 23 | AC_CONFIG_MACRO_DIR([m4]) 24 | 25 | m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) 26 | LT_INIT 27 | 28 | AC_PROG_CC 29 | AC_PROG_CXX 30 | AC_PROG_OBJC 31 | PKG_PROG_PKG_CONFIG 32 | 33 | 34 | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) 35 | 36 | hidapi_lib_error() { 37 | echo "" 38 | echo " Library $1 was not found on this system." 39 | echo " Please install it and re-run ./configure" 40 | echo "" 41 | exit 1 42 | } 43 | 44 | hidapi_prog_error() { 45 | echo "" 46 | echo " Program $1 was not found on this system." 47 | echo " This program is part of $2." 48 | echo " Please install it and re-run ./configure" 49 | echo "" 50 | exit 1 51 | } 52 | 53 | AC_MSG_CHECKING([operating system]) 54 | AC_MSG_RESULT($host) 55 | case $host in 56 | *-linux*) 57 | AC_MSG_RESULT([ (Linux back-end)]) 58 | AC_DEFINE(OS_LINUX, 1, [Linux implementations]) 59 | AC_SUBST(OS_LINUX) 60 | backend="linux" 61 | os="linux" 62 | threads="pthreads" 63 | 64 | # HIDAPI/hidraw libs 65 | PKG_CHECK_MODULES([libudev], [libudev], true, [hidapi_lib_error libudev]) 66 | LIBS_HIDRAW_PR+=" $libudev_LIBS" 67 | CFLAGS_HIDRAW+=" $libudev_CFLAGS" 68 | 69 | # HIDAPI/libusb libs 70 | AC_CHECK_LIB([rt], [clock_gettime], [LIBS_LIBUSB_PRIVATE+=" -lrt"], [hidapi_lib_error librt]) 71 | PKG_CHECK_MODULES([libusb], [libusb-1.0 >= 1.0.9], true, [hidapi_lib_error libusb-1.0]) 72 | LIBS_LIBUSB_PRIVATE+=" $libusb_LIBS" 73 | CFLAGS_LIBUSB+=" $libusb_CFLAGS" 74 | ;; 75 | *-darwin*) 76 | AC_MSG_RESULT([ (Mac OS X back-end)]) 77 | AC_DEFINE(OS_DARWIN, 1, [Mac implementation]) 78 | AC_SUBST(OS_DARWIN) 79 | backend="mac" 80 | os="darwin" 81 | threads="pthreads" 82 | LIBS="${LIBS} -framework IOKit -framework CoreFoundation" 83 | ;; 84 | *-freebsd*) 85 | AC_MSG_RESULT([ (FreeBSD back-end)]) 86 | AC_DEFINE(OS_FREEBSD, 1, [FreeBSD implementation]) 87 | AC_SUBST(OS_FREEBSD) 88 | backend="libusb" 89 | os="freebsd" 90 | threads="pthreads" 91 | 92 | CFLAGS="$CFLAGS -I/usr/local/include" 93 | LDFLAGS="$LDFLAGS -L/usr/local/lib" 94 | LIBS="${LIBS}" 95 | AC_CHECK_LIB([usb], [libusb_init], [LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} -lusb"], [hidapi_lib_error libusb]) 96 | AC_CHECK_LIB([iconv], [iconv_open], [LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} -liconv"], [hidapi_lib_error libiconv]) 97 | echo libs_priv: $LIBS_LIBUSB_PRIVATE 98 | ;; 99 | *-kfreebsd*) 100 | AC_MSG_RESULT([ (kFreeBSD back-end)]) 101 | AC_DEFINE(OS_KFREEBSD, 1, [kFreeBSD implementation]) 102 | AC_SUBST(OS_KFREEBSD) 103 | backend="libusb" 104 | os="kfreebsd" 105 | threads="pthreads" 106 | 107 | AC_CHECK_LIB([usb], [libusb_init], [LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} -lusb"], [hidapi_lib_error libusb]) 108 | echo libs_priv: $LIBS_LIBUSB_PRIVATE 109 | ;; 110 | *-mingw*) 111 | AC_MSG_RESULT([ (Windows back-end, using MinGW)]) 112 | backend="windows" 113 | os="windows" 114 | threads="windows" 115 | win_implementation="mingw" 116 | ;; 117 | *-cygwin*) 118 | AC_MSG_RESULT([ (Windows back-end, using Cygwin)]) 119 | backend="windows" 120 | os="windows" 121 | threads="windows" 122 | win_implementation="cygwin" 123 | ;; 124 | *) 125 | AC_MSG_ERROR([HIDAPI is not supported on your operating system yet]) 126 | esac 127 | 128 | LIBS_HIDRAW="${LIBS} ${LIBS_HIDRAW_PR}" 129 | LIBS_LIBUSB="${LIBS} ${LIBS_LIBUSB_PRIVATE}" 130 | AC_SUBST([LIBS_HIDRAW]) 131 | AC_SUBST([LIBS_LIBUSB]) 132 | AC_SUBST([CFLAGS_LIBUSB]) 133 | AC_SUBST([CFLAGS_HIDRAW]) 134 | 135 | if test "x$os" = xwindows; then 136 | AC_DEFINE(OS_WINDOWS, 1, [Windows implementations]) 137 | AC_SUBST(OS_WINDOWS) 138 | LDFLAGS="${LDFLAGS} -no-undefined" 139 | LIBS="${LIBS} -lsetupapi" 140 | fi 141 | 142 | if test "x$threads" = xpthreads; then 143 | AX_PTHREAD([found_pthreads=yes], [found_pthreads=no]) 144 | 145 | if test "x$found_pthreads" = xyes; then 146 | if test "x$os" = xlinux; then 147 | # Only use pthreads for libusb implementation on Linux. 148 | LIBS_LIBUSB="$PTHREAD_LIBS $LIBS_LIBUSB" 149 | CFLAGS_LIBUSB="$CFLAGS_LIBUSB $PTHREAD_CFLAGS" 150 | # There's no separate CC on Linux for threading, 151 | # so it's ok that both implementations use $PTHREAD_CC 152 | CC="$PTHREAD_CC" 153 | else 154 | LIBS="$PTHREAD_LIBS $LIBS" 155 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS" 156 | CC="$PTHREAD_CC" 157 | fi 158 | fi 159 | fi 160 | 161 | # Test GUI 162 | AC_ARG_ENABLE([testgui], 163 | [AS_HELP_STRING([--enable-testgui], 164 | [enable building of test GUI (default n)])], 165 | [testgui_enabled=$enableval], 166 | [testgui_enabled='no']) 167 | AM_CONDITIONAL([BUILD_TESTGUI], [test "x$testgui_enabled" != "xno"]) 168 | 169 | # Configure the MacOS TestGUI app bundle 170 | rm -Rf testgui/TestGUI.app 171 | mkdir -p testgui/TestGUI.app 172 | cp -R ${srcdir}/testgui/TestGUI.app.in/* testgui/TestGUI.app 173 | chmod -R u+w testgui/TestGUI.app 174 | mkdir testgui/TestGUI.app/Contents/MacOS/ 175 | 176 | if test "x$testgui_enabled" != "xno"; then 177 | if test "x$os" = xdarwin; then 178 | # On Mac OS, don't use pkg-config. 179 | AC_CHECK_PROG([foxconfig], [fox-config], [fox-config], false) 180 | if test "x$foxconfig" = "xfalse"; then 181 | hidapi_prog_error fox-config "FOX Toolkit" 182 | fi 183 | LIBS_TESTGUI+=`$foxconfig --libs` 184 | LIBS_TESTGUI+=" -framework Cocoa -L/usr/X11R6/lib" 185 | CFLAGS_TESTGUI+=`$foxconfig --cflags` 186 | OBJCFLAGS+=" -x objective-c++" 187 | elif test "x$os" = xwindows; then 188 | # On Windows, just set the paths for Fox toolkit 189 | if test "x$win_implementation" = xmingw; then 190 | CFLAGS_TESTGUI="-I\$(srcdir)/../../hidapi-externals/fox/include -g -c" 191 | LIBS_TESTGUI=" -mwindows \$(srcdir)/../../hidapi-externals/fox/lib/libFOX-1.6.a -lgdi32 -Wl,--enable-auto-import -static-libgcc -static-libstdc++ -lkernel32" 192 | else 193 | # Cygwin 194 | CFLAGS_TESTGUI="-DWIN32 -I\$(srcdir)/../../hidapi-externals/fox/include -g -c" 195 | LIBS_TESTGUI="\$(srcdir)/../../hidapi-externals/fox/lib/libFOX-cygwin-1.6.a -lgdi32 -Wl,--enable-auto-import -static-libgcc -static-libstdc++ -lkernel32" 196 | fi 197 | else 198 | # On Linux and FreeBSD platforms, use pkg-config to find fox. 199 | PKG_CHECK_MODULES([fox], [fox17], [], [PKG_CHECK_MODULES([fox], [fox])]) 200 | LIBS_TESTGUI="${LIBS_TESTGUI} $fox_LIBS" 201 | if test "x$os" = xfreebsd; then 202 | LIBS_TESTGUI="${LIBS_TESTGUI} -L/usr/local/lib" 203 | fi 204 | CFLAGS_TESTGUI="${CFLAGS_TESTGUI} $fox_CFLAGS" 205 | fi 206 | fi 207 | AC_SUBST([LIBS_TESTGUI]) 208 | AC_SUBST([CFLAGS_TESTGUI]) 209 | AC_SUBST([backend]) 210 | 211 | # OS info for Automake 212 | AM_CONDITIONAL(OS_LINUX, test "x$os" = xlinux) 213 | AM_CONDITIONAL(OS_DARWIN, test "x$os" = xdarwin) 214 | AM_CONDITIONAL(OS_FREEBSD, test "x$os" = xfreebsd) 215 | AM_CONDITIONAL(OS_KFREEBSD, test "x$os" = xkfreebsd) 216 | AM_CONDITIONAL(OS_WINDOWS, test "x$os" = xwindows) 217 | 218 | AC_CONFIG_HEADERS([config.h]) 219 | 220 | if test "x$os" = "xlinux"; then 221 | AC_CONFIG_FILES([pc/hidapi-hidraw.pc]) 222 | AC_CONFIG_FILES([pc/hidapi-libusb.pc]) 223 | else 224 | AC_CONFIG_FILES([pc/hidapi.pc]) 225 | fi 226 | 227 | AC_SUBST(LTLDFLAGS) 228 | 229 | AC_CONFIG_FILES([Makefile \ 230 | hidtest/Makefile \ 231 | libusb/Makefile \ 232 | linux/Makefile \ 233 | mac/Makefile \ 234 | testgui/Makefile \ 235 | windows/Makefile]) 236 | AC_OUTPUT 237 | -------------------------------------------------------------------------------- /hidapi/hidapi.h: -------------------------------------------------------------------------------- 1 | /******************************************************* 2 | HIDAPI - Multi-Platform library for 3 | communication with HID devices. 4 | 5 | Alan Ott 6 | Signal 11 Software 7 | 8 | 8/22/2009 9 | 10 | Copyright 2009, All Rights Reserved. 11 | 12 | At the discretion of the user of this library, 13 | this software may be licensed under the terms of the 14 | GNU General Public License v3, a BSD-Style license, or the 15 | original HIDAPI license as outlined in the LICENSE.txt, 16 | LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt 17 | files located at the root of the source distribution. 18 | These files may also be found in the public source 19 | code repository located at: 20 | http://github.com/signal11/hidapi . 21 | ********************************************************/ 22 | 23 | /** @file 24 | * @defgroup API hidapi API 25 | */ 26 | 27 | #ifndef HIDAPI_H__ 28 | #define HIDAPI_H__ 29 | 30 | #include 31 | 32 | #ifdef _WIN32 33 | #define HID_API_EXPORT __declspec(dllexport) 34 | #define HID_API_CALL 35 | #else 36 | #define HID_API_EXPORT /**< API export macro */ 37 | #define HID_API_CALL /**< API call macro */ 38 | #endif 39 | 40 | #define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/ 41 | 42 | #ifdef __cplusplus 43 | extern "C" { 44 | #endif 45 | struct hid_device_; 46 | typedef struct hid_device_ hid_device; /**< opaque hidapi structure */ 47 | 48 | /** hidapi info structure */ 49 | struct hid_device_info { 50 | /** Platform-specific device path */ 51 | char *path; 52 | /** Device Vendor ID */ 53 | unsigned short vendor_id; 54 | /** Device Product ID */ 55 | unsigned short product_id; 56 | /** Serial Number */ 57 | wchar_t *serial_number; 58 | /** Device Release Number in binary-coded decimal, 59 | also known as Device Version Number */ 60 | unsigned short release_number; 61 | /** Manufacturer String */ 62 | wchar_t *manufacturer_string; 63 | /** Product string */ 64 | wchar_t *product_string; 65 | /** Usage Page for this Device/Interface 66 | (Windows/Mac only). */ 67 | unsigned short usage_page; 68 | /** Usage for this Device/Interface 69 | (Windows/Mac only).*/ 70 | unsigned short usage; 71 | /** The USB interface which this logical device 72 | represents. Valid on both Linux implementations 73 | in all cases, and valid on the Windows implementation 74 | only if the device contains more than one interface. */ 75 | int interface_number; 76 | 77 | /** Pointer to the next device */ 78 | struct hid_device_info *next; 79 | }; 80 | 81 | 82 | /** @brief Initialize the HIDAPI library. 83 | 84 | This function initializes the HIDAPI library. Calling it is not 85 | strictly necessary, as it will be called automatically by 86 | hid_enumerate() and any of the hid_open_*() functions if it is 87 | needed. This function should be called at the beginning of 88 | execution however, if there is a chance of HIDAPI handles 89 | being opened by different threads simultaneously. 90 | 91 | @ingroup API 92 | 93 | @returns 94 | This function returns 0 on success and -1 on error. 95 | */ 96 | int HID_API_EXPORT HID_API_CALL hid_init(void); 97 | 98 | /** @brief Finalize the HIDAPI library. 99 | 100 | This function frees all of the static data associated with 101 | HIDAPI. It should be called at the end of execution to avoid 102 | memory leaks. 103 | 104 | @ingroup API 105 | 106 | @returns 107 | This function returns 0 on success and -1 on error. 108 | */ 109 | int HID_API_EXPORT HID_API_CALL hid_exit(void); 110 | 111 | /** @brief Enumerate the HID Devices. 112 | 113 | This function returns a linked list of all the HID devices 114 | attached to the system which match vendor_id and product_id. 115 | If @p vendor_id is set to 0 then any vendor matches. 116 | If @p product_id is set to 0 then any product matches. 117 | If @p vendor_id and @p product_id are both set to 0, then 118 | all HID devices will be returned. 119 | 120 | @ingroup API 121 | @param vendor_id The Vendor ID (VID) of the types of device 122 | to open. 123 | @param product_id The Product ID (PID) of the types of 124 | device to open. 125 | 126 | @returns 127 | This function returns a pointer to a linked list of type 128 | struct #hid_device, containing information about the HID devices 129 | attached to the system, or NULL in the case of failure. Free 130 | this linked list by calling hid_free_enumeration(). 131 | */ 132 | struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id); 133 | 134 | /** @brief Free an enumeration Linked List 135 | 136 | This function frees a linked list created by hid_enumerate(). 137 | 138 | @ingroup API 139 | @param devs Pointer to a list of struct_device returned from 140 | hid_enumerate(). 141 | */ 142 | void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs); 143 | 144 | /** @brief Open a HID device using a Vendor ID (VID), Product ID 145 | (PID) and optionally a serial number. 146 | 147 | If @p serial_number is NULL, the first device with the 148 | specified VID and PID is opened. 149 | 150 | @ingroup API 151 | @param vendor_id The Vendor ID (VID) of the device to open. 152 | @param product_id The Product ID (PID) of the device to open. 153 | @param serial_number The Serial Number of the device to open 154 | (Optionally NULL). 155 | 156 | @returns 157 | This function returns a pointer to a #hid_device object on 158 | success or NULL on failure. 159 | */ 160 | HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number); 161 | 162 | /** @brief Open a HID device by its path name. 163 | 164 | The path name be determined by calling hid_enumerate(), or a 165 | platform-specific path name can be used (eg: /dev/hidraw0 on 166 | Linux). 167 | 168 | @ingroup API 169 | @param path The path name of the device to open 170 | 171 | @returns 172 | This function returns a pointer to a #hid_device object on 173 | success or NULL on failure. 174 | */ 175 | HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path); 176 | 177 | /** @brief Write an Output report to a HID device. 178 | 179 | The first byte of @p data[] must contain the Report ID. For 180 | devices which only support a single report, this must be set 181 | to 0x0. The remaining bytes contain the report data. Since 182 | the Report ID is mandatory, calls to hid_write() will always 183 | contain one more byte than the report contains. For example, 184 | if a hid report is 16 bytes long, 17 bytes must be passed to 185 | hid_write(), the Report ID (or 0x0, for devices with a 186 | single report), followed by the report data (16 bytes). In 187 | this example, the length passed in would be 17. 188 | 189 | hid_write() will send the data on the first OUT endpoint, if 190 | one exists. If it does not, it will send the data through 191 | the Control Endpoint (Endpoint 0). 192 | 193 | @ingroup API 194 | @param device A device handle returned from hid_open(). 195 | @param data The data to send, including the report number as 196 | the first byte. 197 | @param length The length in bytes of the data to send. 198 | 199 | @returns 200 | This function returns the actual number of bytes written and 201 | -1 on error. 202 | */ 203 | int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length); 204 | 205 | /** @brief Read an Input report from a HID device with timeout. 206 | 207 | Input reports are returned 208 | to the host through the INTERRUPT IN endpoint. The first byte will 209 | contain the Report number if the device uses numbered reports. 210 | 211 | @ingroup API 212 | @param device A device handle returned from hid_open(). 213 | @param data A buffer to put the read data into. 214 | @param length The number of bytes to read. For devices with 215 | multiple reports, make sure to read an extra byte for 216 | the report number. 217 | @param milliseconds timeout in milliseconds or -1 for blocking wait. 218 | 219 | @returns 220 | This function returns the actual number of bytes read and 221 | -1 on error. If no packet was available to be read within 222 | the timeout period, this function returns 0. 223 | */ 224 | int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); 225 | 226 | /** @brief Read an Input report from a HID device. 227 | 228 | Input reports are returned 229 | to the host through the INTERRUPT IN endpoint. The first byte will 230 | contain the Report number if the device uses numbered reports. 231 | 232 | @ingroup API 233 | @param device A device handle returned from hid_open(). 234 | @param data A buffer to put the read data into. 235 | @param length The number of bytes to read. For devices with 236 | multiple reports, make sure to read an extra byte for 237 | the report number. 238 | 239 | @returns 240 | This function returns the actual number of bytes read and 241 | -1 on error. If no packet was available to be read and 242 | the handle is in non-blocking mode, this function returns 0. 243 | */ 244 | int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length); 245 | 246 | /** @brief Set the device handle to be non-blocking. 247 | 248 | In non-blocking mode calls to hid_read() will return 249 | immediately with a value of 0 if there is no data to be 250 | read. In blocking mode, hid_read() will wait (block) until 251 | there is data to read before returning. 252 | 253 | Nonblocking can be turned on and off at any time. 254 | 255 | @ingroup API 256 | @param device A device handle returned from hid_open(). 257 | @param nonblock enable or not the nonblocking reads 258 | - 1 to enable nonblocking 259 | - 0 to disable nonblocking. 260 | 261 | @returns 262 | This function returns 0 on success and -1 on error. 263 | */ 264 | int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock); 265 | 266 | /** @brief Send a Feature report to the device. 267 | 268 | Feature reports are sent over the Control endpoint as a 269 | Set_Report transfer. The first byte of @p data[] must 270 | contain the Report ID. For devices which only support a 271 | single report, this must be set to 0x0. The remaining bytes 272 | contain the report data. Since the Report ID is mandatory, 273 | calls to hid_send_feature_report() will always contain one 274 | more byte than the report contains. For example, if a hid 275 | report is 16 bytes long, 17 bytes must be passed to 276 | hid_send_feature_report(): the Report ID (or 0x0, for 277 | devices which do not use numbered reports), followed by the 278 | report data (16 bytes). In this example, the length passed 279 | in would be 17. 280 | 281 | @ingroup API 282 | @param device A device handle returned from hid_open(). 283 | @param data The data to send, including the report number as 284 | the first byte. 285 | @param length The length in bytes of the data to send, including 286 | the report number. 287 | 288 | @returns 289 | This function returns the actual number of bytes written and 290 | -1 on error. 291 | */ 292 | int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length); 293 | 294 | /** @brief Get a feature report from a HID device. 295 | 296 | Set the first byte of @p data[] to the Report ID of the 297 | report to be read. Make sure to allow space for this 298 | extra byte in @p data[]. Upon return, the first byte will 299 | still contain the Report ID, and the report data will 300 | start in data[1]. 301 | 302 | @ingroup API 303 | @param device A device handle returned from hid_open(). 304 | @param data A buffer to put the read data into, including 305 | the Report ID. Set the first byte of @p data[] to the 306 | Report ID of the report to be read, or set it to zero 307 | if your device does not use numbered reports. 308 | @param length The number of bytes to read, including an 309 | extra byte for the report ID. The buffer can be longer 310 | than the actual report. 311 | 312 | @returns 313 | This function returns the number of bytes read plus 314 | one for the report ID (which is still in the first 315 | byte), or -1 on error. 316 | */ 317 | int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length); 318 | 319 | /** @brief Close a HID device. 320 | 321 | @ingroup API 322 | @param device A device handle returned from hid_open(). 323 | */ 324 | void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device); 325 | 326 | /** @brief Get The Manufacturer String from a HID device. 327 | 328 | @ingroup API 329 | @param device A device handle returned from hid_open(). 330 | @param string A wide string buffer to put the data into. 331 | @param maxlen The length of the buffer in multiples of wchar_t. 332 | 333 | @returns 334 | This function returns 0 on success and -1 on error. 335 | */ 336 | int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen); 337 | 338 | /** @brief Get The Product String from a HID device. 339 | 340 | @ingroup API 341 | @param device A device handle returned from hid_open(). 342 | @param string A wide string buffer to put the data into. 343 | @param maxlen The length of the buffer in multiples of wchar_t. 344 | 345 | @returns 346 | This function returns 0 on success and -1 on error. 347 | */ 348 | int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen); 349 | 350 | /** @brief Get The Serial Number String from a HID device. 351 | 352 | @ingroup API 353 | @param device A device handle returned from hid_open(). 354 | @param string A wide string buffer to put the data into. 355 | @param maxlen The length of the buffer in multiples of wchar_t. 356 | 357 | @returns 358 | This function returns 0 on success and -1 on error. 359 | */ 360 | int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen); 361 | 362 | /** @brief Get a string from a HID device, based on its string index. 363 | 364 | @ingroup API 365 | @param device A device handle returned from hid_open(). 366 | @param string_index The index of the string to get. 367 | @param string A wide string buffer to put the data into. 368 | @param maxlen The length of the buffer in multiples of wchar_t. 369 | 370 | @returns 371 | This function returns 0 on success and -1 on error. 372 | */ 373 | int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen); 374 | 375 | /** @brief Get a string describing the last error which occurred. 376 | 377 | @ingroup API 378 | @param device A device handle returned from hid_open(). 379 | 380 | @returns 381 | This function returns a string containing the last error 382 | which occurred or NULL if none has occurred. 383 | */ 384 | HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device); 385 | 386 | #ifdef __cplusplus 387 | } 388 | #endif 389 | 390 | #endif 391 | 392 | -------------------------------------------------------------------------------- /hidtest/.gitignore: -------------------------------------------------------------------------------- 1 | Debug 2 | Release 3 | *.exp 4 | *.ilk 5 | *.lib 6 | *.suo 7 | *.vcproj.* 8 | *.ncb 9 | *.suo 10 | *.dll 11 | *.pdb 12 | *.o 13 | .deps/ 14 | .libs/ 15 | hidtest-hidraw 16 | hidtest-libusb 17 | hidtest 18 | -------------------------------------------------------------------------------- /hidtest/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CPPFLAGS = -I$(top_srcdir)/hidapi/ 2 | 3 | ## Linux 4 | if OS_LINUX 5 | noinst_PROGRAMS = hidtest-libusb hidtest-hidraw 6 | 7 | hidtest_hidraw_SOURCES = hidtest.cpp 8 | hidtest_hidraw_LDADD = $(top_builddir)/linux/libhidapi-hidraw.la 9 | 10 | hidtest_libusb_SOURCES = hidtest.cpp 11 | hidtest_libusb_LDADD = $(top_builddir)/libusb/libhidapi-libusb.la 12 | else 13 | 14 | # Other OS's 15 | noinst_PROGRAMS = hidtest 16 | 17 | hidtest_SOURCES = hidtest.cpp 18 | hidtest_LDADD = $(top_builddir)/$(backend)/libhidapi.la 19 | 20 | endif 21 | -------------------------------------------------------------------------------- /hidtest/hidtest.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************* 2 | Windows HID simplification 3 | 4 | Alan Ott 5 | Signal 11 Software 6 | 7 | 8/22/2009 8 | 9 | Copyright 2009 10 | 11 | This contents of this file may be used by anyone 12 | for any reason without any conditions and may be 13 | used as a starting point for your own applications 14 | which use HIDAPI. 15 | ********************************************************/ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "hidapi.h" 22 | 23 | // Headers needed for sleeping. 24 | #ifdef _WIN32 25 | #include 26 | #else 27 | #include 28 | #endif 29 | 30 | int main(int argc, char* argv[]) 31 | { 32 | int res; 33 | unsigned char buf[256]; 34 | #define MAX_STR 255 35 | wchar_t wstr[MAX_STR]; 36 | hid_device *handle; 37 | int i; 38 | 39 | #ifdef WIN32 40 | UNREFERENCED_PARAMETER(argc); 41 | UNREFERENCED_PARAMETER(argv); 42 | #endif 43 | 44 | struct hid_device_info *devs, *cur_dev; 45 | 46 | if (hid_init()) 47 | return -1; 48 | 49 | devs = hid_enumerate(0x0, 0x0); 50 | cur_dev = devs; 51 | while (cur_dev) { 52 | 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); 53 | printf("\n"); 54 | printf(" Manufacturer: %ls\n", cur_dev->manufacturer_string); 55 | printf(" Product: %ls\n", cur_dev->product_string); 56 | printf(" Release: %hx\n", cur_dev->release_number); 57 | printf(" Interface: %d\n", cur_dev->interface_number); 58 | printf("\n"); 59 | cur_dev = cur_dev->next; 60 | } 61 | hid_free_enumeration(devs); 62 | 63 | // Set up the command buffer. 64 | memset(buf,0x00,sizeof(buf)); 65 | buf[0] = 0x01; 66 | buf[1] = 0x81; 67 | 68 | 69 | // Open the device using the VID, PID, 70 | // and optionally the Serial number. 71 | ////handle = hid_open(0x4d8, 0x3f, L"12345"); 72 | handle = hid_open(0x4d8, 0x3f, NULL); 73 | if (!handle) { 74 | printf("unable to open device\n"); 75 | return 1; 76 | } 77 | 78 | // Read the Manufacturer String 79 | wstr[0] = 0x0000; 80 | res = hid_get_manufacturer_string(handle, wstr, MAX_STR); 81 | if (res < 0) 82 | printf("Unable to read manufacturer string\n"); 83 | printf("Manufacturer String: %ls\n", wstr); 84 | 85 | // Read the Product String 86 | wstr[0] = 0x0000; 87 | res = hid_get_product_string(handle, wstr, MAX_STR); 88 | if (res < 0) 89 | printf("Unable to read product string\n"); 90 | printf("Product String: %ls\n", wstr); 91 | 92 | // Read the Serial Number String 93 | wstr[0] = 0x0000; 94 | res = hid_get_serial_number_string(handle, wstr, MAX_STR); 95 | if (res < 0) 96 | printf("Unable to read serial number string\n"); 97 | printf("Serial Number String: (%d) %ls", wstr[0], wstr); 98 | printf("\n"); 99 | 100 | // Read Indexed String 1 101 | wstr[0] = 0x0000; 102 | res = hid_get_indexed_string(handle, 1, wstr, MAX_STR); 103 | if (res < 0) 104 | printf("Unable to read indexed string 1\n"); 105 | printf("Indexed String 1: %ls\n", wstr); 106 | 107 | // Set the hid_read() function to be non-blocking. 108 | hid_set_nonblocking(handle, 1); 109 | 110 | // Try to read from the device. There shoud be no 111 | // data here, but execution should not block. 112 | res = hid_read(handle, buf, 17); 113 | 114 | // Send a Feature Report to the device 115 | buf[0] = 0x2; 116 | buf[1] = 0xa0; 117 | buf[2] = 0x0a; 118 | buf[3] = 0x00; 119 | buf[4] = 0x00; 120 | res = hid_send_feature_report(handle, buf, 17); 121 | if (res < 0) { 122 | printf("Unable to send a feature report.\n"); 123 | } 124 | 125 | memset(buf,0,sizeof(buf)); 126 | 127 | // Read a Feature Report from the device 128 | buf[0] = 0x2; 129 | res = hid_get_feature_report(handle, buf, sizeof(buf)); 130 | if (res < 0) { 131 | printf("Unable to get a feature report.\n"); 132 | printf("%ls", hid_error(handle)); 133 | } 134 | else { 135 | // Print out the returned buffer. 136 | printf("Feature Report\n "); 137 | for (i = 0; i < res; i++) 138 | printf("%02hhx ", buf[i]); 139 | printf("\n"); 140 | } 141 | 142 | memset(buf,0,sizeof(buf)); 143 | 144 | // Toggle LED (cmd 0x80). The first byte is the report number (0x1). 145 | buf[0] = 0x1; 146 | buf[1] = 0x80; 147 | res = hid_write(handle, buf, 17); 148 | if (res < 0) { 149 | printf("Unable to write()\n"); 150 | printf("Error: %ls\n", hid_error(handle)); 151 | } 152 | 153 | 154 | // Request state (cmd 0x81). The first byte is the report number (0x1). 155 | buf[0] = 0x1; 156 | buf[1] = 0x81; 157 | hid_write(handle, buf, 17); 158 | if (res < 0) 159 | printf("Unable to write() (2)\n"); 160 | 161 | // Read requested state. hid_read() has been set to be 162 | // non-blocking by the call to hid_set_nonblocking() above. 163 | // This loop demonstrates the non-blocking nature of hid_read(). 164 | res = 0; 165 | while (res == 0) { 166 | res = hid_read(handle, buf, sizeof(buf)); 167 | if (res == 0) 168 | printf("waiting...\n"); 169 | if (res < 0) 170 | printf("Unable to read()\n"); 171 | #ifdef WIN32 172 | Sleep(500); 173 | #else 174 | usleep(500*1000); 175 | #endif 176 | } 177 | 178 | printf("Data read:\n "); 179 | // Print out the returned buffer. 180 | for (i = 0; i < res; i++) 181 | printf("%02hhx ", buf[i]); 182 | printf("\n"); 183 | 184 | hid_close(handle); 185 | 186 | /* Free static HIDAPI objects. */ 187 | hid_exit(); 188 | 189 | #ifdef WIN32 190 | system("pause"); 191 | #endif 192 | 193 | return 0; 194 | } 195 | -------------------------------------------------------------------------------- /libusb/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.la 4 | *.lo 5 | *.a 6 | .libs 7 | .deps 8 | hidtest-libusb 9 | -------------------------------------------------------------------------------- /libusb/Makefile-manual: -------------------------------------------------------------------------------- 1 | 2 | 3 | OS=$(shell uname) 4 | 5 | ifeq ($(OS), Linux) 6 | FILE=Makefile.linux 7 | endif 8 | 9 | ifeq ($(OS), FreeBSD) 10 | FILE=Makefile.freebsd 11 | endif 12 | 13 | ifeq ($(FILE), ) 14 | all: 15 | $(error Your platform ${OS} is not supported by hidapi/libusb at this time.) 16 | endif 17 | 18 | include $(FILE) 19 | -------------------------------------------------------------------------------- /libusb/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CPPFLAGS = -I$(top_srcdir)/hidapi $(CFLAGS_LIBUSB) 2 | 3 | if OS_LINUX 4 | lib_LTLIBRARIES = libhidapi-libusb.la 5 | libhidapi_libusb_la_SOURCES = hid.c 6 | libhidapi_libusb_la_LDFLAGS = $(LTLDFLAGS) $(PTHREAD_CFLAGS) 7 | libhidapi_libusb_la_LIBADD = $(LIBS_LIBUSB) 8 | endif 9 | 10 | if OS_FREEBSD 11 | lib_LTLIBRARIES = libhidapi.la 12 | libhidapi_la_SOURCES = hid.c 13 | libhidapi_la_LDFLAGS = $(LTLDFLAGS) 14 | libhidapi_la_LIBADD = $(LIBS_LIBUSB) 15 | endif 16 | 17 | if OS_KFREEBSD 18 | lib_LTLIBRARIES = libhidapi.la 19 | libhidapi_la_SOURCES = hid.c 20 | libhidapi_la_LDFLAGS = $(LTLDFLAGS) 21 | libhidapi_la_LIBADD = $(LIBS_LIBUSB) 22 | endif 23 | 24 | hdrdir = $(includedir)/hidapi 25 | hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h 26 | 27 | EXTRA_DIST = Makefile-manual 28 | -------------------------------------------------------------------------------- /libusb/Makefile.freebsd: -------------------------------------------------------------------------------- 1 | ########################################### 2 | # Simple Makefile for HIDAPI test program 3 | # 4 | # Alan Ott 5 | # Signal 11 Software 6 | # 2010-06-01 7 | ########################################### 8 | 9 | all: hidtest libs 10 | 11 | libs: libhidapi.so 12 | 13 | CC ?= cc 14 | CFLAGS ?= -Wall -g -fPIC 15 | 16 | CXX ?= c++ 17 | CXXFLAGS ?= -Wall -g 18 | 19 | COBJS = hid.o 20 | CPPOBJS = ../hidtest/hidtest.o 21 | OBJS = $(COBJS) $(CPPOBJS) 22 | INCLUDES = -I../hidapi -I/usr/local/include 23 | LDFLAGS = -L/usr/local/lib 24 | LIBS = -lusb -liconv -pthread 25 | 26 | 27 | # Console Test Program 28 | hidtest: $(OBJS) 29 | $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) 30 | 31 | # Shared Libs 32 | libhidapi.so: $(COBJS) 33 | $(CC) $(LDFLAGS) -shared -Wl,-soname,$@.0 $^ -o $@ $(LIBS) 34 | 35 | # Objects 36 | $(COBJS): %.o: %.c 37 | $(CC) $(CFLAGS) -c $(INCLUDES) $< -o $@ 38 | 39 | $(CPPOBJS): %.o: %.cpp 40 | $(CXX) $(CXXFLAGS) -c $(INCLUDES) $< -o $@ 41 | 42 | 43 | clean: 44 | rm -f $(OBJS) hidtest libhidapi.so ../hidtest/hidtest.o 45 | 46 | .PHONY: clean libs 47 | -------------------------------------------------------------------------------- /libusb/Makefile.linux: -------------------------------------------------------------------------------- 1 | ########################################### 2 | # Simple Makefile for HIDAPI test program 3 | # 4 | # Alan Ott 5 | # Signal 11 Software 6 | # 2010-06-01 7 | ########################################### 8 | 9 | all: hidtest-libusb libs 10 | 11 | libs: libhidapi-libusb.so 12 | 13 | CC ?= gcc 14 | CFLAGS ?= -Wall -g -fpic 15 | 16 | CXX ?= g++ 17 | CXXFLAGS ?= -Wall -g -fpic 18 | 19 | LDFLAGS ?= -Wall -g 20 | 21 | COBJS_LIBUSB = hid.o 22 | COBJS = $(COBJS_LIBUSB) 23 | CPPOBJS = ../hidtest/hidtest.o 24 | OBJS = $(COBJS) $(CPPOBJS) 25 | LIBS_USB = `pkg-config libusb-1.0 --libs` -lrt -lpthread 26 | LIBS = $(LIBS_USB) 27 | INCLUDES ?= -I../hidapi `pkg-config libusb-1.0 --cflags` 28 | 29 | 30 | # Console Test Program 31 | hidtest-libusb: $(COBJS_LIBUSB) $(CPPOBJS) 32 | $(CXX) $(LDFLAGS) $^ $(LIBS_USB) -o $@ 33 | 34 | # Shared Libs 35 | libhidapi-libusb.so: $(COBJS_LIBUSB) 36 | $(CC) $(LDFLAGS) $(LIBS_USB) -shared -fpic -Wl,-soname,$@.0 $^ -o $@ 37 | 38 | # Objects 39 | $(COBJS): %.o: %.c 40 | $(CC) $(CFLAGS) -c $(INCLUDES) $< -o $@ 41 | 42 | $(CPPOBJS): %.o: %.cpp 43 | $(CXX) $(CXXFLAGS) -c $(INCLUDES) $< -o $@ 44 | 45 | 46 | clean: 47 | rm -f $(OBJS) hidtest-libusb libhidapi-libusb.so ../hidtest/hidtest.o 48 | 49 | .PHONY: clean libs 50 | -------------------------------------------------------------------------------- /linux/.gitignore: -------------------------------------------------------------------------------- 1 | Debug 2 | Release 3 | *.exp 4 | *.ilk 5 | *.lib 6 | *.suo 7 | *.vcproj.* 8 | *.ncb 9 | *.suo 10 | *.dll 11 | *.pdb 12 | *.o 13 | *.so 14 | hidtest-hidraw 15 | .deps 16 | .libs 17 | *.lo 18 | *.la 19 | -------------------------------------------------------------------------------- /linux/Makefile-manual: -------------------------------------------------------------------------------- 1 | ########################################### 2 | # Simple Makefile for HIDAPI test program 3 | # 4 | # Alan Ott 5 | # Signal 11 Software 6 | # 2010-06-01 7 | ########################################### 8 | 9 | all: hidtest-hidraw libs 10 | 11 | libs: libhidapi-hidraw.so 12 | 13 | CC ?= gcc 14 | CFLAGS ?= -Wall -g -fpic 15 | 16 | CXX ?= g++ 17 | CXXFLAGS ?= -Wall -g -fpic 18 | 19 | LDFLAGS ?= -Wall -g 20 | 21 | 22 | COBJS = hid.o 23 | CPPOBJS = ../hidtest/hidtest.o 24 | OBJS = $(COBJS) $(CPPOBJS) 25 | LIBS_UDEV = `pkg-config libudev --libs` -lrt 26 | LIBS = $(LIBS_UDEV) 27 | INCLUDES ?= -I../hidapi `pkg-config libusb-1.0 --cflags` 28 | 29 | 30 | # Console Test Program 31 | hidtest-hidraw: $(COBJS) $(CPPOBJS) 32 | $(CXX) $(LDFLAGS) $^ $(LIBS_UDEV) -o $@ 33 | 34 | # Shared Libs 35 | libhidapi-hidraw.so: $(COBJS) 36 | $(CC) $(LDFLAGS) $(LIBS_UDEV) -shared -fpic -Wl,-soname,$@.0 $^ -o $@ 37 | 38 | # Objects 39 | $(COBJS): %.o: %.c 40 | $(CC) $(CFLAGS) -c $(INCLUDES) $< -o $@ 41 | 42 | $(CPPOBJS): %.o: %.cpp 43 | $(CXX) $(CXXFLAGS) -c $(INCLUDES) $< -o $@ 44 | 45 | 46 | clean: 47 | rm -f $(OBJS) hidtest-hidraw libhidapi-hidraw.so ../hidtest/hidtest.o 48 | 49 | .PHONY: clean libs 50 | -------------------------------------------------------------------------------- /linux/Makefile.am: -------------------------------------------------------------------------------- 1 | lib_LTLIBRARIES = libhidapi-hidraw.la 2 | libhidapi_hidraw_la_SOURCES = hid.c 3 | libhidapi_hidraw_la_LDFLAGS = $(LTLDFLAGS) 4 | AM_CPPFLAGS = -I$(top_srcdir)/hidapi/ $(CFLAGS_HIDRAW) 5 | libhidapi_hidraw_la_LIBADD = $(LIBS_HIDRAW) 6 | 7 | hdrdir = $(includedir)/hidapi 8 | hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h 9 | 10 | EXTRA_DIST = Makefile-manual 11 | -------------------------------------------------------------------------------- /linux/README.txt: -------------------------------------------------------------------------------- 1 | 2 | There are two implementations of HIDAPI for Linux. One (linux/hid.c) uses the 3 | Linux hidraw driver, and the other (libusb/hid.c) uses libusb. Which one you 4 | use depends on your application. Complete functionality of the hidraw 5 | version depends on patches to the Linux kernel which are not currently in 6 | the mainline. These patches have to do with sending and receiving feature 7 | reports. The libusb implementation uses libusb to talk directly to the 8 | device, bypassing any Linux HID driver. The disadvantage of the libusb 9 | version is that it will only work with USB devices, while the hidraw 10 | implementation will work with Bluetooth devices as well. 11 | 12 | To use HIDAPI, simply drop either linux/hid.c or libusb/hid.c into your 13 | application and build using the build parameters in the Makefile. 14 | 15 | 16 | Libusb Implementation notes 17 | ---------------------------- 18 | For the libusb implementation, libusb-1.0 must be installed. Libusb 1.0 is 19 | different than the legacy libusb 0.1 which is installed on many systems. To 20 | install libusb-1.0 on Ubuntu and other Debian-based systems, run: 21 | sudo apt-get install libusb-1.0-0-dev 22 | 23 | 24 | Hidraw Implementation notes 25 | ---------------------------- 26 | For the hidraw implementation, libudev headers and libraries are required to 27 | build hidapi programs. To install libudev libraries on Ubuntu, 28 | and other Debian-based systems, run: 29 | sudo apt-get install libudev-dev 30 | 31 | On Redhat-based systems, run the following as root: 32 | yum install libudev-devel 33 | 34 | Unfortunately, the hidraw driver, which the linux version of hidapi is based 35 | on, contains bugs in kernel versions < 2.6.36, which the client application 36 | should be aware of. 37 | 38 | Bugs (hidraw implementation only): 39 | ----------------------------------- 40 | On Kernel versions < 2.6.34, if your device uses numbered reports, an extra 41 | byte will be returned at the beginning of all reports returned from read() 42 | for hidraw devices. This is worked around in the libary. No action should be 43 | necessary in the client library. 44 | 45 | On Kernel versions < 2.6.35, reports will only be sent using a Set_Report 46 | transfer on the CONTROL endpoint. No data will ever be sent on an Interrupt 47 | Out endpoint if one exists. This is fixed in 2.6.35. In 2.6.35, OUTPUT 48 | reports will be sent to the device on the first INTERRUPT OUT endpoint if it 49 | exists; If it does not exist, OUTPUT reports will be sent on the CONTROL 50 | endpoint. 51 | 52 | On Kernel versions < 2.6.36, add an extra byte containing the report number 53 | to sent reports if numbered reports are used, and the device does not 54 | contain an INTERRPUT OUT endpoint for OUTPUT transfers. For example, if 55 | your device uses numbered reports and wants to send {0x2 0xff 0xff 0xff} to 56 | the device (0x2 is the report number), you must send {0x2 0x2 0xff 0xff 57 | 0xff}. If your device has the optional Interrupt OUT endpoint, this does not 58 | apply (but really on 2.6.35 only, because 2.6.34 won't use the interrupt 59 | out endpoint). 60 | -------------------------------------------------------------------------------- /linux/hid.c: -------------------------------------------------------------------------------- 1 | /******************************************************* 2 | HIDAPI - Multi-Platform library for 3 | communication with HID devices. 4 | 5 | Alan Ott 6 | Signal 11 Software 7 | 8 | 8/22/2009 9 | Linux Version - 6/2/2009 10 | 11 | Copyright 2009, All Rights Reserved. 12 | 13 | At the discretion of the user of this library, 14 | this software may be licensed under the terms of the 15 | GNU General Public License v3, a BSD-Style license, or the 16 | original HIDAPI license as outlined in the LICENSE.txt, 17 | LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt 18 | files located at the root of the source distribution. 19 | These files may also be found in the public source 20 | code repository located at: 21 | http://github.com/signal11/hidapi . 22 | ********************************************************/ 23 | 24 | /* C */ 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | /* Unix */ 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | /* Linux */ 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #include "hidapi.h" 47 | 48 | /* Definitions from linux/hidraw.h. Since these are new, some distros 49 | may not have header files which contain them. */ 50 | #ifndef HIDIOCSFEATURE 51 | #define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len) 52 | #endif 53 | #ifndef HIDIOCGFEATURE 54 | #define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len) 55 | #endif 56 | 57 | 58 | /* USB HID device property names */ 59 | const char *device_string_names[] = { 60 | "manufacturer", 61 | "product", 62 | "serial", 63 | }; 64 | 65 | /* Symbolic names for the properties above */ 66 | enum device_string_id { 67 | DEVICE_STRING_MANUFACTURER, 68 | DEVICE_STRING_PRODUCT, 69 | DEVICE_STRING_SERIAL, 70 | 71 | DEVICE_STRING_COUNT, 72 | }; 73 | 74 | struct hid_device_ { 75 | int device_handle; 76 | int blocking; 77 | int uses_numbered_reports; 78 | }; 79 | 80 | 81 | static __u32 kernel_version = 0; 82 | 83 | static __u32 detect_kernel_version(void) 84 | { 85 | struct utsname name; 86 | int major, minor, release; 87 | int ret; 88 | 89 | uname(&name); 90 | ret = sscanf(name.release, "%d.%d.%d", &major, &minor, &release); 91 | if (ret == 3) { 92 | return KERNEL_VERSION(major, minor, release); 93 | } 94 | 95 | ret = sscanf(name.release, "%d.%d", &major, &minor); 96 | if (ret == 2) { 97 | return KERNEL_VERSION(major, minor, 0); 98 | } 99 | 100 | printf("Couldn't determine kernel version from version string \"%s\"\n", name.release); 101 | return 0; 102 | } 103 | 104 | static hid_device *new_hid_device(void) 105 | { 106 | hid_device *dev = calloc(1, sizeof(hid_device)); 107 | dev->device_handle = -1; 108 | dev->blocking = 1; 109 | dev->uses_numbered_reports = 0; 110 | 111 | return dev; 112 | } 113 | 114 | 115 | /* The caller must free the returned string with free(). */ 116 | static wchar_t *utf8_to_wchar_t(const char *utf8) 117 | { 118 | wchar_t *ret = NULL; 119 | 120 | if (utf8) { 121 | size_t wlen = mbstowcs(NULL, utf8, 0); 122 | if ((size_t) -1 == wlen) { 123 | return wcsdup(L""); 124 | } 125 | ret = calloc(wlen+1, sizeof(wchar_t)); 126 | mbstowcs(ret, utf8, wlen+1); 127 | ret[wlen] = 0x0000; 128 | } 129 | 130 | return ret; 131 | } 132 | 133 | /* Get an attribute value from a udev_device and return it as a whar_t 134 | string. The returned string must be freed with free() when done.*/ 135 | static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name) 136 | { 137 | return utf8_to_wchar_t(udev_device_get_sysattr_value(dev, udev_name)); 138 | } 139 | 140 | /* uses_numbered_reports() returns 1 if report_descriptor describes a device 141 | which contains numbered reports. */ 142 | static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) { 143 | unsigned int i = 0; 144 | int size_code; 145 | int data_len, key_size; 146 | 147 | while (i < size) { 148 | int key = report_descriptor[i]; 149 | 150 | /* Check for the Report ID key */ 151 | if (key == 0x85/*Report ID*/) { 152 | /* This device has a Report ID, which means it uses 153 | numbered reports. */ 154 | return 1; 155 | } 156 | 157 | //printf("key: %02hhx\n", key); 158 | 159 | if ((key & 0xf0) == 0xf0) { 160 | /* This is a Long Item. The next byte contains the 161 | length of the data section (value) for this key. 162 | See the HID specification, version 1.11, section 163 | 6.2.2.3, titled "Long Items." */ 164 | if (i+1 < size) 165 | data_len = report_descriptor[i+1]; 166 | else 167 | data_len = 0; /* malformed report */ 168 | key_size = 3; 169 | } 170 | else { 171 | /* This is a Short Item. The bottom two bits of the 172 | key contain the size code for the data section 173 | (value) for this key. Refer to the HID 174 | specification, version 1.11, section 6.2.2.2, 175 | titled "Short Items." */ 176 | size_code = key & 0x3; 177 | switch (size_code) { 178 | case 0: 179 | case 1: 180 | case 2: 181 | data_len = size_code; 182 | break; 183 | case 3: 184 | data_len = 4; 185 | break; 186 | default: 187 | /* Can't ever happen since size_code is & 0x3 */ 188 | data_len = 0; 189 | break; 190 | }; 191 | key_size = 1; 192 | } 193 | 194 | /* Skip over this key and it's associated data */ 195 | i += data_len + key_size; 196 | } 197 | 198 | /* Didn't find a Report ID key. Device doesn't use numbered reports. */ 199 | return 0; 200 | } 201 | 202 | /* 203 | * The caller is responsible for free()ing the (newly-allocated) character 204 | * strings pointed to by serial_number_utf8 and product_name_utf8 after use. 205 | */ 206 | static int 207 | parse_uevent_info(const char *uevent, int *bus_type, 208 | unsigned short *vendor_id, unsigned short *product_id, 209 | char **serial_number_utf8, char **product_name_utf8) 210 | { 211 | char *tmp = strdup(uevent); 212 | char *saveptr = NULL; 213 | char *line; 214 | char *key; 215 | char *value; 216 | 217 | int found_id = 0; 218 | int found_serial = 0; 219 | int found_name = 0; 220 | 221 | line = strtok_r(tmp, "\n", &saveptr); 222 | while (line != NULL) { 223 | /* line: "KEY=value" */ 224 | key = line; 225 | value = strchr(line, '='); 226 | if (!value) { 227 | goto next_line; 228 | } 229 | *value = '\0'; 230 | value++; 231 | 232 | if (strcmp(key, "HID_ID") == 0) { 233 | /** 234 | * type vendor product 235 | * HID_ID=0003:000005AC:00008242 236 | **/ 237 | int ret = sscanf(value, "%x:%hx:%hx", bus_type, vendor_id, product_id); 238 | if (ret == 3) { 239 | found_id = 1; 240 | } 241 | } else if (strcmp(key, "HID_NAME") == 0) { 242 | /* The caller has to free the product name */ 243 | *product_name_utf8 = strdup(value); 244 | found_name = 1; 245 | } else if (strcmp(key, "HID_UNIQ") == 0) { 246 | /* The caller has to free the serial number */ 247 | *serial_number_utf8 = strdup(value); 248 | found_serial = 1; 249 | } 250 | 251 | next_line: 252 | line = strtok_r(NULL, "\n", &saveptr); 253 | } 254 | 255 | free(tmp); 256 | return (found_id && found_name && found_serial); 257 | } 258 | 259 | 260 | static int get_device_string(hid_device *dev, enum device_string_id key, wchar_t *string, size_t maxlen) 261 | { 262 | struct udev *udev; 263 | struct udev_device *udev_dev, *parent, *hid_dev; 264 | struct stat s; 265 | int ret = -1; 266 | char *serial_number_utf8 = NULL; 267 | char *product_name_utf8 = NULL; 268 | 269 | /* Create the udev object */ 270 | udev = udev_new(); 271 | if (!udev) { 272 | printf("Can't create udev\n"); 273 | return -1; 274 | } 275 | 276 | /* Get the dev_t (major/minor numbers) from the file handle. */ 277 | ret = fstat(dev->device_handle, &s); 278 | if (-1 == ret) 279 | return ret; 280 | /* Open a udev device from the dev_t. 'c' means character device. */ 281 | udev_dev = udev_device_new_from_devnum(udev, 'c', s.st_rdev); 282 | if (udev_dev) { 283 | hid_dev = udev_device_get_parent_with_subsystem_devtype( 284 | udev_dev, 285 | "hid", 286 | NULL); 287 | if (hid_dev) { 288 | unsigned short dev_vid; 289 | unsigned short dev_pid; 290 | int bus_type; 291 | size_t retm; 292 | 293 | ret = parse_uevent_info( 294 | udev_device_get_sysattr_value(hid_dev, "uevent"), 295 | &bus_type, 296 | &dev_vid, 297 | &dev_pid, 298 | &serial_number_utf8, 299 | &product_name_utf8); 300 | 301 | if (bus_type == BUS_BLUETOOTH) { 302 | switch (key) { 303 | case DEVICE_STRING_MANUFACTURER: 304 | wcsncpy(string, L"", maxlen); 305 | ret = 0; 306 | break; 307 | case DEVICE_STRING_PRODUCT: 308 | retm = mbstowcs(string, product_name_utf8, maxlen); 309 | ret = (retm == (size_t)-1)? -1: 0; 310 | break; 311 | case DEVICE_STRING_SERIAL: 312 | retm = mbstowcs(string, serial_number_utf8, maxlen); 313 | ret = (retm == (size_t)-1)? -1: 0; 314 | break; 315 | case DEVICE_STRING_COUNT: 316 | default: 317 | ret = -1; 318 | break; 319 | } 320 | } 321 | else { 322 | /* This is a USB device. Find its parent USB Device node. */ 323 | parent = udev_device_get_parent_with_subsystem_devtype( 324 | udev_dev, 325 | "usb", 326 | "usb_device"); 327 | if (parent) { 328 | const char *str; 329 | const char *key_str = NULL; 330 | 331 | if (key >= 0 && key < DEVICE_STRING_COUNT) { 332 | key_str = device_string_names[key]; 333 | } else { 334 | ret = -1; 335 | goto end; 336 | } 337 | 338 | str = udev_device_get_sysattr_value(parent, key_str); 339 | if (str) { 340 | /* Convert the string from UTF-8 to wchar_t */ 341 | retm = mbstowcs(string, str, maxlen); 342 | ret = (retm == (size_t)-1)? -1: 0; 343 | goto end; 344 | } 345 | } 346 | } 347 | } 348 | } 349 | 350 | end: 351 | free(serial_number_utf8); 352 | free(product_name_utf8); 353 | 354 | udev_device_unref(udev_dev); 355 | /* parent and hid_dev don't need to be (and can't be) unref'd. 356 | I'm not sure why, but they'll throw double-free() errors. */ 357 | udev_unref(udev); 358 | 359 | return ret; 360 | } 361 | 362 | int HID_API_EXPORT hid_init(void) 363 | { 364 | const char *locale; 365 | 366 | /* Set the locale if it's not set. */ 367 | locale = setlocale(LC_CTYPE, NULL); 368 | if (!locale) 369 | setlocale(LC_CTYPE, ""); 370 | 371 | kernel_version = detect_kernel_version(); 372 | 373 | return 0; 374 | } 375 | 376 | int HID_API_EXPORT hid_exit(void) 377 | { 378 | /* Nothing to do for this in the Linux/hidraw implementation. */ 379 | return 0; 380 | } 381 | 382 | 383 | struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) 384 | { 385 | struct udev *udev; 386 | struct udev_enumerate *enumerate; 387 | struct udev_list_entry *devices, *dev_list_entry; 388 | 389 | struct hid_device_info *root = NULL; /* return object */ 390 | struct hid_device_info *cur_dev = NULL; 391 | struct hid_device_info *prev_dev = NULL; /* previous device */ 392 | 393 | hid_init(); 394 | 395 | /* Create the udev object */ 396 | udev = udev_new(); 397 | if (!udev) { 398 | printf("Can't create udev\n"); 399 | return NULL; 400 | } 401 | 402 | /* Create a list of the devices in the 'hidraw' subsystem. */ 403 | enumerate = udev_enumerate_new(udev); 404 | udev_enumerate_add_match_subsystem(enumerate, "hidraw"); 405 | udev_enumerate_scan_devices(enumerate); 406 | devices = udev_enumerate_get_list_entry(enumerate); 407 | /* For each item, see if it matches the vid/pid, and if so 408 | create a udev_device record for it */ 409 | udev_list_entry_foreach(dev_list_entry, devices) { 410 | const char *sysfs_path; 411 | const char *dev_path; 412 | const char *str; 413 | struct udev_device *raw_dev; /* The device's hidraw udev node. */ 414 | struct udev_device *hid_dev; /* The device's HID udev node. */ 415 | struct udev_device *usb_dev; /* The device's USB udev node. */ 416 | struct udev_device *intf_dev; /* The device's interface (in the USB sense). */ 417 | unsigned short dev_vid; 418 | unsigned short dev_pid; 419 | char *serial_number_utf8 = NULL; 420 | char *product_name_utf8 = NULL; 421 | int bus_type; 422 | int result; 423 | 424 | /* Get the filename of the /sys entry for the device 425 | and create a udev_device object (dev) representing it */ 426 | sysfs_path = udev_list_entry_get_name(dev_list_entry); 427 | raw_dev = udev_device_new_from_syspath(udev, sysfs_path); 428 | dev_path = udev_device_get_devnode(raw_dev); 429 | 430 | hid_dev = udev_device_get_parent_with_subsystem_devtype( 431 | raw_dev, 432 | "hid", 433 | NULL); 434 | 435 | if (!hid_dev) { 436 | /* Unable to find parent hid device. */ 437 | goto next; 438 | } 439 | 440 | result = parse_uevent_info( 441 | udev_device_get_sysattr_value(hid_dev, "uevent"), 442 | &bus_type, 443 | &dev_vid, 444 | &dev_pid, 445 | &serial_number_utf8, 446 | &product_name_utf8); 447 | 448 | if (!result) { 449 | /* parse_uevent_info() failed for at least one field. */ 450 | goto next; 451 | } 452 | 453 | if (bus_type != BUS_USB && bus_type != BUS_BLUETOOTH) { 454 | /* We only know how to handle USB and BT devices. */ 455 | goto next; 456 | } 457 | 458 | /* Check the VID/PID against the arguments */ 459 | if ((vendor_id == 0x0 || vendor_id == dev_vid) && 460 | (product_id == 0x0 || product_id == dev_pid)) { 461 | struct hid_device_info *tmp; 462 | 463 | /* VID/PID match. Create the record. */ 464 | tmp = malloc(sizeof(struct hid_device_info)); 465 | if (cur_dev) { 466 | cur_dev->next = tmp; 467 | } 468 | else { 469 | root = tmp; 470 | } 471 | prev_dev = cur_dev; 472 | cur_dev = tmp; 473 | 474 | /* Fill out the record */ 475 | cur_dev->next = NULL; 476 | cur_dev->path = dev_path? strdup(dev_path): NULL; 477 | 478 | /* VID/PID */ 479 | cur_dev->vendor_id = dev_vid; 480 | cur_dev->product_id = dev_pid; 481 | 482 | /* Serial Number */ 483 | cur_dev->serial_number = utf8_to_wchar_t(serial_number_utf8); 484 | 485 | /* Release Number */ 486 | cur_dev->release_number = 0x0; 487 | 488 | /* Interface Number */ 489 | cur_dev->interface_number = -1; 490 | 491 | switch (bus_type) { 492 | case BUS_USB: 493 | /* The device pointed to by raw_dev contains information about 494 | the hidraw device. In order to get information about the 495 | USB device, get the parent device with the 496 | subsystem/devtype pair of "usb"/"usb_device". This will 497 | be several levels up the tree, but the function will find 498 | it. */ 499 | usb_dev = udev_device_get_parent_with_subsystem_devtype( 500 | raw_dev, 501 | "usb", 502 | "usb_device"); 503 | 504 | if (!usb_dev) { 505 | /* Free this device */ 506 | free(cur_dev->serial_number); 507 | free(cur_dev->path); 508 | free(cur_dev); 509 | 510 | /* Take it off the device list. */ 511 | if (prev_dev) { 512 | prev_dev->next = NULL; 513 | cur_dev = prev_dev; 514 | } 515 | else { 516 | cur_dev = root = NULL; 517 | } 518 | 519 | goto next; 520 | } 521 | 522 | /* Manufacturer and Product strings */ 523 | cur_dev->manufacturer_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_MANUFACTURER]); 524 | cur_dev->product_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_PRODUCT]); 525 | 526 | /* Release Number */ 527 | str = udev_device_get_sysattr_value(usb_dev, "bcdDevice"); 528 | cur_dev->release_number = (str)? strtol(str, NULL, 16): 0x0; 529 | 530 | /* Get a handle to the interface's udev node. */ 531 | intf_dev = udev_device_get_parent_with_subsystem_devtype( 532 | raw_dev, 533 | "usb", 534 | "usb_interface"); 535 | if (intf_dev) { 536 | str = udev_device_get_sysattr_value(intf_dev, "bInterfaceNumber"); 537 | cur_dev->interface_number = (str)? strtol(str, NULL, 16): -1; 538 | } 539 | 540 | break; 541 | 542 | case BUS_BLUETOOTH: 543 | /* Manufacturer and Product strings */ 544 | cur_dev->manufacturer_string = wcsdup(L""); 545 | cur_dev->product_string = utf8_to_wchar_t(product_name_utf8); 546 | 547 | break; 548 | 549 | default: 550 | /* Unknown device type - this should never happen, as we 551 | * check for USB and Bluetooth devices above */ 552 | break; 553 | } 554 | } 555 | 556 | next: 557 | free(serial_number_utf8); 558 | free(product_name_utf8); 559 | udev_device_unref(raw_dev); 560 | /* hid_dev, usb_dev and intf_dev don't need to be (and can't be) 561 | unref()d. It will cause a double-free() error. I'm not 562 | sure why. */ 563 | } 564 | /* Free the enumerator and udev objects. */ 565 | udev_enumerate_unref(enumerate); 566 | udev_unref(udev); 567 | 568 | return root; 569 | } 570 | 571 | void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) 572 | { 573 | struct hid_device_info *d = devs; 574 | while (d) { 575 | struct hid_device_info *next = d->next; 576 | free(d->path); 577 | free(d->serial_number); 578 | free(d->manufacturer_string); 579 | free(d->product_string); 580 | free(d); 581 | d = next; 582 | } 583 | } 584 | 585 | hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) 586 | { 587 | struct hid_device_info *devs, *cur_dev; 588 | const char *path_to_open = NULL; 589 | hid_device *handle = NULL; 590 | 591 | devs = hid_enumerate(vendor_id, product_id); 592 | cur_dev = devs; 593 | while (cur_dev) { 594 | if (cur_dev->vendor_id == vendor_id && 595 | cur_dev->product_id == product_id) { 596 | if (serial_number) { 597 | if (wcscmp(serial_number, cur_dev->serial_number) == 0) { 598 | path_to_open = cur_dev->path; 599 | break; 600 | } 601 | } 602 | else { 603 | path_to_open = cur_dev->path; 604 | break; 605 | } 606 | } 607 | cur_dev = cur_dev->next; 608 | } 609 | 610 | if (path_to_open) { 611 | /* Open the device */ 612 | handle = hid_open_path(path_to_open); 613 | } 614 | 615 | hid_free_enumeration(devs); 616 | 617 | return handle; 618 | } 619 | 620 | hid_device * HID_API_EXPORT hid_open_path(const char *path) 621 | { 622 | hid_device *dev = NULL; 623 | 624 | hid_init(); 625 | 626 | dev = new_hid_device(); 627 | 628 | /* OPEN HERE */ 629 | dev->device_handle = open(path, O_RDWR); 630 | 631 | /* If we have a good handle, return it. */ 632 | if (dev->device_handle > 0) { 633 | 634 | /* Get the report descriptor */ 635 | int res, desc_size = 0; 636 | struct hidraw_report_descriptor rpt_desc; 637 | 638 | memset(&rpt_desc, 0x0, sizeof(rpt_desc)); 639 | 640 | /* Get Report Descriptor Size */ 641 | res = ioctl(dev->device_handle, HIDIOCGRDESCSIZE, &desc_size); 642 | if (res < 0) 643 | perror("HIDIOCGRDESCSIZE"); 644 | 645 | 646 | /* Get Report Descriptor */ 647 | rpt_desc.size = desc_size; 648 | res = ioctl(dev->device_handle, HIDIOCGRDESC, &rpt_desc); 649 | if (res < 0) { 650 | perror("HIDIOCGRDESC"); 651 | } else { 652 | /* Determine if this device uses numbered reports. */ 653 | dev->uses_numbered_reports = 654 | uses_numbered_reports(rpt_desc.value, 655 | rpt_desc.size); 656 | } 657 | 658 | return dev; 659 | } 660 | else { 661 | /* Unable to open any devices. */ 662 | free(dev); 663 | return NULL; 664 | } 665 | } 666 | 667 | 668 | int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) 669 | { 670 | int bytes_written; 671 | 672 | bytes_written = write(dev->device_handle, data, length); 673 | 674 | return bytes_written; 675 | } 676 | 677 | 678 | int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) 679 | { 680 | int bytes_read; 681 | 682 | if (milliseconds >= 0) { 683 | /* Milliseconds is either 0 (non-blocking) or > 0 (contains 684 | a valid timeout). In both cases we want to call poll() 685 | and wait for data to arrive. Don't rely on non-blocking 686 | operation (O_NONBLOCK) since some kernels don't seem to 687 | properly report device disconnection through read() when 688 | in non-blocking mode. */ 689 | int ret; 690 | struct pollfd fds; 691 | 692 | fds.fd = dev->device_handle; 693 | fds.events = POLLIN; 694 | fds.revents = 0; 695 | ret = poll(&fds, 1, milliseconds); 696 | if (ret == -1 || ret == 0) { 697 | /* Error or timeout */ 698 | return ret; 699 | } 700 | else { 701 | /* Check for errors on the file descriptor. This will 702 | indicate a device disconnection. */ 703 | if (fds.revents & (POLLERR | POLLHUP | POLLNVAL)) 704 | return -1; 705 | } 706 | } 707 | 708 | bytes_read = read(dev->device_handle, data, length); 709 | if (bytes_read < 0 && (errno == EAGAIN || errno == EINPROGRESS)) 710 | bytes_read = 0; 711 | 712 | if (bytes_read >= 0 && 713 | kernel_version != 0 && 714 | kernel_version < KERNEL_VERSION(2,6,34) && 715 | dev->uses_numbered_reports) { 716 | /* Work around a kernel bug. Chop off the first byte. */ 717 | memmove(data, data+1, bytes_read); 718 | bytes_read--; 719 | } 720 | 721 | return bytes_read; 722 | } 723 | 724 | int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) 725 | { 726 | return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); 727 | } 728 | 729 | int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) 730 | { 731 | /* Do all non-blocking in userspace using poll(), since it looks 732 | like there's a bug in the kernel in some versions where 733 | read() will not return -1 on disconnection of the USB device */ 734 | 735 | dev->blocking = !nonblock; 736 | return 0; /* Success */ 737 | } 738 | 739 | 740 | int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) 741 | { 742 | int res; 743 | 744 | res = ioctl(dev->device_handle, HIDIOCSFEATURE(length), data); 745 | if (res < 0) 746 | perror("ioctl (SFEATURE)"); 747 | 748 | return res; 749 | } 750 | 751 | int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) 752 | { 753 | int res; 754 | 755 | res = ioctl(dev->device_handle, HIDIOCGFEATURE(length), data); 756 | if (res < 0) 757 | perror("ioctl (GFEATURE)"); 758 | 759 | 760 | return res; 761 | } 762 | 763 | 764 | void HID_API_EXPORT hid_close(hid_device *dev) 765 | { 766 | if (!dev) 767 | return; 768 | close(dev->device_handle); 769 | free(dev); 770 | } 771 | 772 | 773 | int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) 774 | { 775 | return get_device_string(dev, DEVICE_STRING_MANUFACTURER, string, maxlen); 776 | } 777 | 778 | int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) 779 | { 780 | return get_device_string(dev, DEVICE_STRING_PRODUCT, string, maxlen); 781 | } 782 | 783 | int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) 784 | { 785 | return get_device_string(dev, DEVICE_STRING_SERIAL, string, maxlen); 786 | } 787 | 788 | int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) 789 | { 790 | return -1; 791 | } 792 | 793 | 794 | HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) 795 | { 796 | return NULL; 797 | } 798 | -------------------------------------------------------------------------------- /m4/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore All, except pkg.m4, and of course this file. 2 | * 3 | !.gitignore 4 | !pkg.m4 5 | !ax_pthread.m4 6 | -------------------------------------------------------------------------------- /m4/ax_pthread.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # http://www.gnu.org/software/autoconf-archive/ax_pthread.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # This macro figures out how to build C programs using POSIX threads. It 12 | # sets the PTHREAD_LIBS output variable to the threads library and linker 13 | # flags, and the PTHREAD_CFLAGS output variable to any special C compiler 14 | # flags that are needed. (The user can also force certain compiler 15 | # flags/libs to be tested by setting these environment variables.) 16 | # 17 | # Also sets PTHREAD_CC to any special C compiler that is needed for 18 | # multi-threaded programs (defaults to the value of CC otherwise). (This 19 | # is necessary on AIX to use the special cc_r compiler alias.) 20 | # 21 | # NOTE: You are assumed to not only compile your program with these flags, 22 | # but also link it with them as well. e.g. you should link with 23 | # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS 24 | # 25 | # If you are only building threads programs, you may wish to use these 26 | # variables in your default LIBS, CFLAGS, and CC: 27 | # 28 | # LIBS="$PTHREAD_LIBS $LIBS" 29 | # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" 30 | # CC="$PTHREAD_CC" 31 | # 32 | # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant 33 | # has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name 34 | # (e.g. PTHREAD_CREATE_UNDETACHED on AIX). 35 | # 36 | # Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the 37 | # PTHREAD_PRIO_INHERIT symbol is defined when compiling with 38 | # PTHREAD_CFLAGS. 39 | # 40 | # ACTION-IF-FOUND is a list of shell commands to run if a threads library 41 | # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it 42 | # is not found. If ACTION-IF-FOUND is not specified, the default action 43 | # will define HAVE_PTHREAD. 44 | # 45 | # Please let the authors know if this macro fails on any platform, or if 46 | # you have any other suggestions or comments. This macro was based on work 47 | # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help 48 | # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by 49 | # Alejandro Forero Cuervo to the autoconf macro repository. We are also 50 | # grateful for the helpful feedback of numerous users. 51 | # 52 | # Updated for Autoconf 2.68 by Daniel Richard G. 53 | # 54 | # LICENSE 55 | # 56 | # Copyright (c) 2008 Steven G. Johnson 57 | # Copyright (c) 2011 Daniel Richard G. 58 | # 59 | # This program is free software: you can redistribute it and/or modify it 60 | # under the terms of the GNU General Public License as published by the 61 | # Free Software Foundation, either version 3 of the License, or (at your 62 | # option) any later version. 63 | # 64 | # This program is distributed in the hope that it will be useful, but 65 | # WITHOUT ANY WARRANTY; without even the implied warranty of 66 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 67 | # Public License for more details. 68 | # 69 | # You should have received a copy of the GNU General Public License along 70 | # with this program. If not, see . 71 | # 72 | # As a special exception, the respective Autoconf Macro's copyright owner 73 | # gives unlimited permission to copy, distribute and modify the configure 74 | # scripts that are the output of Autoconf when processing the Macro. You 75 | # need not follow the terms of the GNU General Public License when using 76 | # or distributing such scripts, even though portions of the text of the 77 | # Macro appear in them. The GNU General Public License (GPL) does govern 78 | # all other use of the material that constitutes the Autoconf Macro. 79 | # 80 | # This special exception to the GPL applies to versions of the Autoconf 81 | # Macro released by the Autoconf Archive. When you make and distribute a 82 | # modified version of the Autoconf Macro, you may extend this special 83 | # exception to the GPL to apply to your modified version as well. 84 | 85 | #serial 18 86 | 87 | AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) 88 | AC_DEFUN([AX_PTHREAD], [ 89 | AC_REQUIRE([AC_CANONICAL_HOST]) 90 | AC_LANG_PUSH([C]) 91 | ax_pthread_ok=no 92 | 93 | # We used to check for pthread.h first, but this fails if pthread.h 94 | # requires special compiler flags (e.g. on True64 or Sequent). 95 | # It gets checked for in the link test anyway. 96 | 97 | # First of all, check if the user has set any of the PTHREAD_LIBS, 98 | # etcetera environment variables, and if threads linking works using 99 | # them: 100 | if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then 101 | save_CFLAGS="$CFLAGS" 102 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS" 103 | save_LIBS="$LIBS" 104 | LIBS="$PTHREAD_LIBS $LIBS" 105 | AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) 106 | AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes) 107 | AC_MSG_RESULT($ax_pthread_ok) 108 | if test x"$ax_pthread_ok" = xno; then 109 | PTHREAD_LIBS="" 110 | PTHREAD_CFLAGS="" 111 | fi 112 | LIBS="$save_LIBS" 113 | CFLAGS="$save_CFLAGS" 114 | fi 115 | 116 | # We must check for the threads library under a number of different 117 | # names; the ordering is very important because some systems 118 | # (e.g. DEC) have both -lpthread and -lpthreads, where one of the 119 | # libraries is broken (non-POSIX). 120 | 121 | # Create a list of thread flags to try. Items starting with a "-" are 122 | # C compiler flags, and other items are library names, except for "none" 123 | # which indicates that we try without any flags at all, and "pthread-config" 124 | # which is a program returning the flags for the Pth emulation library. 125 | 126 | ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" 127 | 128 | # The ordering *is* (sometimes) important. Some notes on the 129 | # individual items follow: 130 | 131 | # pthreads: AIX (must check this before -lpthread) 132 | # none: in case threads are in libc; should be tried before -Kthread and 133 | # other compiler flags to prevent continual compiler warnings 134 | # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) 135 | # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) 136 | # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) 137 | # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) 138 | # -pthreads: Solaris/gcc 139 | # -mthreads: Mingw32/gcc, Lynx/gcc 140 | # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it 141 | # doesn't hurt to check since this sometimes defines pthreads too; 142 | # also defines -D_REENTRANT) 143 | # ... -mt is also the pthreads flag for HP/aCC 144 | # pthread: Linux, etcetera 145 | # --thread-safe: KAI C++ 146 | # pthread-config: use pthread-config program (for GNU Pth library) 147 | 148 | case ${host_os} in 149 | solaris*) 150 | 151 | # On Solaris (at least, for some versions), libc contains stubbed 152 | # (non-functional) versions of the pthreads routines, so link-based 153 | # tests will erroneously succeed. (We need to link with -pthreads/-mt/ 154 | # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather 155 | # a function called by this macro, so we could check for that, but 156 | # who knows whether they'll stub that too in a future libc.) So, 157 | # we'll just look for -pthreads and -lpthread first: 158 | 159 | ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" 160 | ;; 161 | 162 | darwin*) 163 | ax_pthread_flags="-pthread $ax_pthread_flags" 164 | ;; 165 | esac 166 | 167 | if test x"$ax_pthread_ok" = xno; then 168 | for flag in $ax_pthread_flags; do 169 | 170 | case $flag in 171 | none) 172 | AC_MSG_CHECKING([whether pthreads work without any flags]) 173 | ;; 174 | 175 | -*) 176 | AC_MSG_CHECKING([whether pthreads work with $flag]) 177 | PTHREAD_CFLAGS="$flag" 178 | ;; 179 | 180 | pthread-config) 181 | AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no) 182 | if test x"$ax_pthread_config" = xno; then continue; fi 183 | PTHREAD_CFLAGS="`pthread-config --cflags`" 184 | PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" 185 | ;; 186 | 187 | *) 188 | AC_MSG_CHECKING([for the pthreads library -l$flag]) 189 | PTHREAD_LIBS="-l$flag" 190 | ;; 191 | esac 192 | 193 | save_LIBS="$LIBS" 194 | save_CFLAGS="$CFLAGS" 195 | LIBS="$PTHREAD_LIBS $LIBS" 196 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS" 197 | 198 | # Check for various functions. We must include pthread.h, 199 | # since some functions may be macros. (On the Sequent, we 200 | # need a special flag -Kthread to make this header compile.) 201 | # We check for pthread_join because it is in -lpthread on IRIX 202 | # while pthread_create is in libc. We check for pthread_attr_init 203 | # due to DEC craziness with -lpthreads. We check for 204 | # pthread_cleanup_push because it is one of the few pthread 205 | # functions on Solaris that doesn't have a non-functional libc stub. 206 | # We try pthread_create on general principles. 207 | AC_LINK_IFELSE([AC_LANG_PROGRAM([#include 208 | static void routine(void *a) { a = 0; } 209 | static void *start_routine(void *a) { return a; }], 210 | [pthread_t th; pthread_attr_t attr; 211 | pthread_create(&th, 0, start_routine, 0); 212 | pthread_join(th, 0); 213 | pthread_attr_init(&attr); 214 | pthread_cleanup_push(routine, 0); 215 | pthread_cleanup_pop(0) /* ; */])], 216 | [ax_pthread_ok=yes], 217 | []) 218 | 219 | LIBS="$save_LIBS" 220 | CFLAGS="$save_CFLAGS" 221 | 222 | AC_MSG_RESULT($ax_pthread_ok) 223 | if test "x$ax_pthread_ok" = xyes; then 224 | break; 225 | fi 226 | 227 | PTHREAD_LIBS="" 228 | PTHREAD_CFLAGS="" 229 | done 230 | fi 231 | 232 | # Various other checks: 233 | if test "x$ax_pthread_ok" = xyes; then 234 | save_LIBS="$LIBS" 235 | LIBS="$PTHREAD_LIBS $LIBS" 236 | save_CFLAGS="$CFLAGS" 237 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS" 238 | 239 | # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. 240 | AC_MSG_CHECKING([for joinable pthread attribute]) 241 | attr_name=unknown 242 | for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do 243 | AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], 244 | [int attr = $attr; return attr /* ; */])], 245 | [attr_name=$attr; break], 246 | []) 247 | done 248 | AC_MSG_RESULT($attr_name) 249 | if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then 250 | AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, 251 | [Define to necessary symbol if this constant 252 | uses a non-standard name on your system.]) 253 | fi 254 | 255 | AC_MSG_CHECKING([if more special flags are required for pthreads]) 256 | flag=no 257 | case ${host_os} in 258 | aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";; 259 | osf* | hpux*) flag="-D_REENTRANT";; 260 | solaris*) 261 | if test "$GCC" = "yes"; then 262 | flag="-D_REENTRANT" 263 | else 264 | flag="-mt -D_REENTRANT" 265 | fi 266 | ;; 267 | esac 268 | AC_MSG_RESULT(${flag}) 269 | if test "x$flag" != xno; then 270 | PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" 271 | fi 272 | 273 | AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], 274 | ax_cv_PTHREAD_PRIO_INHERIT, [ 275 | AC_LINK_IFELSE([ 276 | AC_LANG_PROGRAM([[#include ]], [[int i = PTHREAD_PRIO_INHERIT;]])], 277 | [ax_cv_PTHREAD_PRIO_INHERIT=yes], 278 | [ax_cv_PTHREAD_PRIO_INHERIT=no]) 279 | ]) 280 | AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"], 281 | AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.])) 282 | 283 | LIBS="$save_LIBS" 284 | CFLAGS="$save_CFLAGS" 285 | 286 | # More AIX lossage: must compile with xlc_r or cc_r 287 | if test x"$GCC" != xyes; then 288 | AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) 289 | else 290 | PTHREAD_CC=$CC 291 | fi 292 | else 293 | PTHREAD_CC="$CC" 294 | fi 295 | 296 | AC_SUBST(PTHREAD_LIBS) 297 | AC_SUBST(PTHREAD_CFLAGS) 298 | AC_SUBST(PTHREAD_CC) 299 | 300 | # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: 301 | if test x"$ax_pthread_ok" = xyes; then 302 | ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) 303 | : 304 | else 305 | ax_pthread_ok=no 306 | $2 307 | fi 308 | AC_LANG_POP 309 | ])dnl AX_PTHREAD 310 | -------------------------------------------------------------------------------- /m4/pkg.m4: -------------------------------------------------------------------------------- 1 | # pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- 2 | # 3 | # Copyright © 2004 Scott James Remnant . 4 | # 5 | # This program is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 2 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, but 11 | # WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | # 19 | # As a special exception to the GNU General Public License, if you 20 | # distribute this file as part of a program that contains a 21 | # configuration script generated by Autoconf, you may include it under 22 | # the same distribution terms that you use for the rest of that program. 23 | 24 | # PKG_PROG_PKG_CONFIG([MIN-VERSION]) 25 | # ---------------------------------- 26 | AC_DEFUN([PKG_PROG_PKG_CONFIG], 27 | [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) 28 | m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) 29 | AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl 30 | if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then 31 | AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) 32 | fi 33 | if test -n "$PKG_CONFIG"; then 34 | _pkg_min_version=m4_default([$1], [0.9.0]) 35 | AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) 36 | if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then 37 | AC_MSG_RESULT([yes]) 38 | else 39 | AC_MSG_RESULT([no]) 40 | PKG_CONFIG="" 41 | fi 42 | 43 | fi[]dnl 44 | ])# PKG_PROG_PKG_CONFIG 45 | 46 | # PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) 47 | # 48 | # Check to see whether a particular set of modules exists. Similar 49 | # to PKG_CHECK_MODULES(), but does not set variables or print errors. 50 | # 51 | # 52 | # Similar to PKG_CHECK_MODULES, make sure that the first instance of 53 | # this or PKG_CHECK_MODULES is called, or make sure to call 54 | # PKG_CHECK_EXISTS manually 55 | # -------------------------------------------------------------- 56 | AC_DEFUN([PKG_CHECK_EXISTS], 57 | [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl 58 | if test -n "$PKG_CONFIG" && \ 59 | AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then 60 | m4_ifval([$2], [$2], [:]) 61 | m4_ifvaln([$3], [else 62 | $3])dnl 63 | fi]) 64 | 65 | 66 | # _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) 67 | # --------------------------------------------- 68 | m4_define([_PKG_CONFIG], 69 | [if test -n "$PKG_CONFIG"; then 70 | if test -n "$$1"; then 71 | pkg_cv_[]$1="$$1" 72 | else 73 | PKG_CHECK_EXISTS([$3], 74 | [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`], 75 | [pkg_failed=yes]) 76 | fi 77 | else 78 | pkg_failed=untried 79 | fi[]dnl 80 | ])# _PKG_CONFIG 81 | 82 | # _PKG_SHORT_ERRORS_SUPPORTED 83 | # ----------------------------- 84 | AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], 85 | [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) 86 | if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then 87 | _pkg_short_errors_supported=yes 88 | else 89 | _pkg_short_errors_supported=no 90 | fi[]dnl 91 | ])# _PKG_SHORT_ERRORS_SUPPORTED 92 | 93 | 94 | # PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], 95 | # [ACTION-IF-NOT-FOUND]) 96 | # 97 | # 98 | # Note that if there is a possibility the first call to 99 | # PKG_CHECK_MODULES might not happen, you should be sure to include an 100 | # explicit call to PKG_PROG_PKG_CONFIG in your configure.ac 101 | # 102 | # 103 | # -------------------------------------------------------------- 104 | AC_DEFUN([PKG_CHECK_MODULES], 105 | [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl 106 | AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl 107 | AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl 108 | 109 | pkg_failed=no 110 | AC_MSG_CHECKING([for $1]) 111 | 112 | _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) 113 | _PKG_CONFIG([$1][_LIBS], [libs], [$2]) 114 | 115 | m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS 116 | and $1[]_LIBS to avoid the need to call pkg-config. 117 | See the pkg-config man page for more details.]) 118 | 119 | if test $pkg_failed = yes; then 120 | _PKG_SHORT_ERRORS_SUPPORTED 121 | if test $_pkg_short_errors_supported = yes; then 122 | $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"` 123 | else 124 | $1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"` 125 | fi 126 | # Put the nasty error message in config.log where it belongs 127 | echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD 128 | 129 | ifelse([$4], , [AC_MSG_ERROR(dnl 130 | [Package requirements ($2) were not met: 131 | 132 | $$1_PKG_ERRORS 133 | 134 | Consider adjusting the PKG_CONFIG_PATH environment variable if you 135 | installed software in a non-standard prefix. 136 | 137 | _PKG_TEXT 138 | ])], 139 | [AC_MSG_RESULT([no]) 140 | $4]) 141 | elif test $pkg_failed = untried; then 142 | ifelse([$4], , [AC_MSG_FAILURE(dnl 143 | [The pkg-config script could not be found or is too old. Make sure it 144 | is in your PATH or set the PKG_CONFIG environment variable to the full 145 | path to pkg-config. 146 | 147 | _PKG_TEXT 148 | 149 | To get pkg-config, see .])], 150 | [$4]) 151 | else 152 | $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS 153 | $1[]_LIBS=$pkg_cv_[]$1[]_LIBS 154 | AC_MSG_RESULT([yes]) 155 | ifelse([$3], , :, [$3]) 156 | fi[]dnl 157 | ])# PKG_CHECK_MODULES 158 | -------------------------------------------------------------------------------- /mac/.gitignore: -------------------------------------------------------------------------------- 1 | Debug 2 | Release 3 | *.exp 4 | *.ilk 5 | *.lib 6 | *.suo 7 | *.vcproj.* 8 | *.ncb 9 | *.suo 10 | *.dll 11 | *.pdb 12 | *.o 13 | hidapi-hidtest 14 | .deps 15 | .libs 16 | *.la 17 | *.lo 18 | -------------------------------------------------------------------------------- /mac/Makefile-manual: -------------------------------------------------------------------------------- 1 | ########################################### 2 | # Simple Makefile for HIDAPI test program 3 | # 4 | # Alan Ott 5 | # Signal 11 Software 6 | # 2010-07-03 7 | ########################################### 8 | 9 | all: hidtest 10 | 11 | CC=gcc 12 | CXX=g++ 13 | COBJS=hid.o 14 | CPPOBJS=../hidtest/hidtest.o 15 | OBJS=$(COBJS) $(CPPOBJS) 16 | CFLAGS+=-I../hidapi -Wall -g -c 17 | LIBS=-framework IOKit -framework CoreFoundation 18 | 19 | 20 | hidtest: $(OBJS) 21 | g++ -Wall -g $^ $(LIBS) -o hidtest 22 | 23 | $(COBJS): %.o: %.c 24 | $(CC) $(CFLAGS) $< -o $@ 25 | 26 | $(CPPOBJS): %.o: %.cpp 27 | $(CXX) $(CFLAGS) $< -o $@ 28 | 29 | clean: 30 | rm -f *.o hidtest $(CPPOBJS) 31 | 32 | .PHONY: clean 33 | -------------------------------------------------------------------------------- /mac/Makefile.am: -------------------------------------------------------------------------------- 1 | lib_LTLIBRARIES = libhidapi.la 2 | libhidapi_la_SOURCES = hid.c 3 | libhidapi_la_LDFLAGS = $(LTLDFLAGS) 4 | AM_CPPFLAGS = -I$(top_srcdir)/hidapi/ 5 | 6 | hdrdir = $(includedir)/hidapi 7 | hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h 8 | 9 | EXTRA_DIST = Makefile-manual 10 | -------------------------------------------------------------------------------- /pc/.gitignore: -------------------------------------------------------------------------------- 1 | *.pc 2 | -------------------------------------------------------------------------------- /pc/hidapi-hidraw.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: hidapi-hidraw 7 | Description: C Library for USB/Bluetooth HID device access from Linux, Mac OS X, FreeBSD, and Windows. This is the hidraw implementation. 8 | Version: @VERSION@ 9 | Libs: -L${libdir} -lhidapi-hidraw 10 | Cflags: -I${includedir}/hidapi 11 | -------------------------------------------------------------------------------- /pc/hidapi-libusb.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: hidapi-libusb 7 | Description: C Library for USB HID device access from Linux, Mac OS X, FreeBSD, and Windows. This is the libusb implementation. 8 | Version: @VERSION@ 9 | Libs: -L${libdir} -lhidapi-libusb 10 | Cflags: -I${includedir}/hidapi 11 | -------------------------------------------------------------------------------- /pc/hidapi.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: hidapi 7 | Description: C Library for USB/Bluetooth HID device access from Linux, Mac OS X, FreeBSD, and Windows. 8 | Version: @VERSION@ 9 | Libs: -L${libdir} -lhidapi 10 | Cflags: -I${includedir}/hidapi 11 | -------------------------------------------------------------------------------- /testgui/.gitignore: -------------------------------------------------------------------------------- 1 | Debug 2 | Release 3 | *.exp 4 | *.ilk 5 | *.lib 6 | *.suo 7 | *.vcproj.* 8 | *.ncb 9 | *.suo 10 | *.dll 11 | *.pdb 12 | *.o 13 | hidapi-testgui 14 | hidapi-hidraw-testgui 15 | hidapi-libusb-testgui 16 | .deps 17 | .libs 18 | *.la 19 | *.lo 20 | TestGUI.app 21 | -------------------------------------------------------------------------------- /testgui/Makefile-manual: -------------------------------------------------------------------------------- 1 | 2 | 3 | OS=$(shell uname) 4 | 5 | ifeq ($(OS), Darwin) 6 | FILE=Makefile.mac 7 | endif 8 | 9 | ifneq (,$(findstring MINGW,$(OS))) 10 | FILE=Makefile.mingw 11 | endif 12 | 13 | ifeq ($(OS), Linux) 14 | FILE=Makefile.linux 15 | endif 16 | 17 | ifeq ($(OS), FreeBSD) 18 | FILE=Makefile.freebsd 19 | endif 20 | 21 | ifeq ($(FILE), ) 22 | all: 23 | $(error Your platform ${OS} is not supported at this time.) 24 | endif 25 | 26 | include $(FILE) 27 | -------------------------------------------------------------------------------- /testgui/Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | AM_CPPFLAGS = -I$(top_srcdir)/hidapi/ $(CFLAGS_TESTGUI) 3 | 4 | if OS_LINUX 5 | ## Linux 6 | bin_PROGRAMS = hidapi-hidraw-testgui hidapi-libusb-testgui 7 | 8 | hidapi_hidraw_testgui_SOURCES = test.cpp 9 | hidapi_hidraw_testgui_LDADD = $(top_builddir)/linux/libhidapi-hidraw.la $(LIBS_TESTGUI) 10 | 11 | hidapi_libusb_testgui_SOURCES = test.cpp 12 | hidapi_libusb_testgui_LDADD = $(top_builddir)/libusb/libhidapi-libusb.la $(LIBS_TESTGUI) 13 | else 14 | ## Other OS's 15 | bin_PROGRAMS = hidapi-testgui 16 | 17 | hidapi_testgui_SOURCES = test.cpp 18 | hidapi_testgui_LDADD = $(top_builddir)/$(backend)/libhidapi.la $(LIBS_TESTGUI) 19 | endif 20 | 21 | if OS_DARWIN 22 | hidapi_testgui_SOURCES = test.cpp mac_support_cocoa.m mac_support.h 23 | # Rules for copying the binary and its dependencies into the app bundle. 24 | TestGUI.app/Contents/MacOS/hidapi-testgui$(EXEEXT): hidapi-testgui$(EXEEXT) 25 | $(srcdir)/copy_to_bundle.sh 26 | 27 | all: all-am TestGUI.app/Contents/MacOS/hidapi-testgui$(EXEEXT) 28 | 29 | endif 30 | 31 | EXTRA_DIST = \ 32 | copy_to_bundle.sh \ 33 | Makefile-manual \ 34 | Makefile.freebsd \ 35 | Makefile.linux \ 36 | Makefile.mac \ 37 | Makefile.mingw \ 38 | TestGUI.app.in \ 39 | testgui.sln \ 40 | testgui.vcproj 41 | 42 | distclean-local: 43 | rm -rf TestGUI.app 44 | -------------------------------------------------------------------------------- /testgui/Makefile.freebsd: -------------------------------------------------------------------------------- 1 | ########################################### 2 | # Simple Makefile for HIDAPI test program 3 | # 4 | # Alan Ott 5 | # Signal 11 Software 6 | # 2010-06-01 7 | ########################################### 8 | 9 | all: testgui 10 | 11 | CC=cc 12 | CXX=c++ 13 | COBJS=../libusb/hid.o 14 | CPPOBJS=test.o 15 | OBJS=$(COBJS) $(CPPOBJS) 16 | CFLAGS=-I../hidapi -I/usr/local/include `fox-config --cflags` -Wall -g -c 17 | LDFLAGS= -L/usr/local/lib 18 | LIBS= -lusb -liconv `fox-config --libs` -pthread 19 | 20 | 21 | testgui: $(OBJS) 22 | $(CXX) -Wall -g $^ $(LDFLAGS) -o $@ $(LIBS) 23 | 24 | $(COBJS): %.o: %.c 25 | $(CC) $(CFLAGS) $< -o $@ 26 | 27 | $(CPPOBJS): %.o: %.cpp 28 | $(CXX) $(CFLAGS) $< -o $@ 29 | 30 | clean: 31 | rm *.o testgui 32 | 33 | .PHONY: clean 34 | -------------------------------------------------------------------------------- /testgui/Makefile.linux: -------------------------------------------------------------------------------- 1 | ########################################### 2 | # Simple Makefile for HIDAPI test program 3 | # 4 | # Alan Ott 5 | # Signal 11 Software 6 | # 2010-06-01 7 | ########################################### 8 | 9 | all: testgui 10 | 11 | CC=gcc 12 | CXX=g++ 13 | COBJS=../libusb/hid.o 14 | CPPOBJS=test.o 15 | OBJS=$(COBJS) $(CPPOBJS) 16 | CFLAGS=-I../hidapi -Wall -g -c `fox-config --cflags` `pkg-config libusb-1.0 --cflags` 17 | LIBS=-ludev -lrt -lpthread `fox-config --libs` `pkg-config libusb-1.0 --libs` 18 | 19 | 20 | testgui: $(OBJS) 21 | g++ -Wall -g $^ $(LIBS) -o testgui 22 | 23 | $(COBJS): %.o: %.c 24 | $(CC) $(CFLAGS) $< -o $@ 25 | 26 | $(CPPOBJS): %.o: %.cpp 27 | $(CXX) $(CFLAGS) $< -o $@ 28 | 29 | clean: 30 | rm *.o testgui 31 | 32 | .PHONY: clean 33 | -------------------------------------------------------------------------------- /testgui/Makefile.mac: -------------------------------------------------------------------------------- 1 | ########################################### 2 | # Simple Makefile for HIDAPI test program 3 | # 4 | # Alan Ott 5 | # Signal 11 Software 6 | # 2010-07-03 7 | ########################################### 8 | 9 | all: hidapi-testgui 10 | 11 | CC=gcc 12 | CXX=g++ 13 | COBJS=../mac/hid.o 14 | CPPOBJS=test.o 15 | OBJCOBJS=mac_support_cocoa.o 16 | OBJS=$(COBJS) $(CPPOBJS) $(OBJCOBJS) 17 | CFLAGS=-I../hidapi -Wall -g -c `fox-config --cflags` 18 | LDFLAGS=-L/usr/X11R6/lib 19 | LIBS=`fox-config --libs` -framework IOKit -framework CoreFoundation -framework Cocoa 20 | 21 | 22 | hidapi-testgui: $(OBJS) TestGUI.app 23 | g++ -Wall -g $(OBJS) $(LIBS) $(LDFLAGS) -o hidapi-testgui 24 | ./copy_to_bundle.sh 25 | #cp TestGUI.app/Contents/MacOS/hidapi-testgui TestGUI.app/Contents/MacOS/tg 26 | #cp start.sh TestGUI.app/Contents/MacOS/hidapi-testgui 27 | 28 | $(COBJS): %.o: %.c 29 | $(CC) $(CFLAGS) $< -o $@ 30 | 31 | $(CPPOBJS): %.o: %.cpp 32 | $(CXX) $(CFLAGS) $< -o $@ 33 | 34 | $(OBJCOBJS): %.o: %.m 35 | $(CXX) $(CFLAGS) -x objective-c++ $< -o $@ 36 | 37 | TestGUI.app: TestGUI.app.in 38 | rm -Rf TestGUI.app 39 | mkdir -p TestGUI.app 40 | cp -R TestGUI.app.in/ TestGUI.app 41 | 42 | clean: 43 | rm -f $(OBJS) hidapi-testgui 44 | rm -Rf TestGUI.app 45 | 46 | .PHONY: clean 47 | -------------------------------------------------------------------------------- /testgui/Makefile.mingw: -------------------------------------------------------------------------------- 1 | ########################################### 2 | # Simple Makefile for HIDAPI test program 3 | # 4 | # Alan Ott 5 | # Signal 11 Software 6 | # 2010-06-01 7 | ########################################### 8 | 9 | all: hidapi-testgui 10 | 11 | CC=gcc 12 | CXX=g++ 13 | COBJS=../windows/hid.o 14 | CPPOBJS=test.o 15 | OBJS=$(COBJS) $(CPPOBJS) 16 | CFLAGS=-I../hidapi -I../../hidapi-externals/fox/include -g -c 17 | LIBS= -mwindows -lsetupapi -L../../hidapi-externals/fox/lib -Wl,-Bstatic -lFOX-1.6 -Wl,-Bdynamic -lgdi32 -Wl,--enable-auto-import -static-libgcc -static-libstdc++ -lkernel32 18 | 19 | 20 | hidapi-testgui: $(OBJS) 21 | g++ -g $^ $(LIBS) -o hidapi-testgui 22 | 23 | $(COBJS): %.o: %.c 24 | $(CC) $(CFLAGS) $< -o $@ 25 | 26 | $(CPPOBJS): %.o: %.cpp 27 | $(CXX) $(CFLAGS) $< -o $@ 28 | 29 | clean: 30 | rm -f *.o hidapi-testgui.exe 31 | 32 | .PHONY: clean 33 | -------------------------------------------------------------------------------- /testgui/TestGUI.app.in/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleDisplayName 8 | 9 | CFBundleExecutable 10 | hidapi-testgui 11 | CFBundleIconFile 12 | Signal11.icns 13 | CFBundleIdentifier 14 | us.signal11.hidtestgui 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | testgui 19 | CFBundlePackageType 20 | APPL 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | CSResourcesFileMapped 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /testgui/TestGUI.app.in/Contents/PkgInfo: -------------------------------------------------------------------------------- 1 | APPL???? -------------------------------------------------------------------------------- /testgui/TestGUI.app.in/Contents/Resources/English.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signal11/hidapi/a6a622ffb680c55da0de787ff93b80280498330f/testgui/TestGUI.app.in/Contents/Resources/English.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /testgui/TestGUI.app.in/Contents/Resources/Signal11.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signal11/hidapi/a6a622ffb680c55da0de787ff93b80280498330f/testgui/TestGUI.app.in/Contents/Resources/Signal11.icns -------------------------------------------------------------------------------- /testgui/copy_to_bundle.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #### Configuration: 4 | # The name of the executable. It is assumed 5 | # that it is in the current working directory. 6 | EXE_NAME=hidapi-testgui 7 | # Path to the executable directory inside the bundle. 8 | # This must be an absolute path, so use $PWD. 9 | EXEPATH=$PWD/TestGUI.app/Contents/MacOS 10 | # Libraries to explicitly bundle, even though they 11 | # may not be in /opt/local. One per line. These 12 | # are used with grep, so only a portion of the name 13 | # is required. eg: libFOX, libz, etc. 14 | LIBS_TO_BUNDLE=libFOX 15 | 16 | 17 | function copydeps { 18 | local file=$1 19 | # echo "Copying deps for $file...." 20 | local BASE_OF_EXE=`basename $file` 21 | 22 | # A will contain the dependencies of this library 23 | local A=`otool -LX $file |cut -f 1 -d " "` 24 | local i 25 | for i in $A; do 26 | local BASE=`basename $i` 27 | 28 | # See if it's a lib we specifically want to bundle 29 | local bundle_this_lib=0 30 | local j 31 | for j in $LIBS_TO_BUNDLE; do 32 | echo $i |grep -q $j 33 | if [ $? -eq 0 ]; then 34 | bundle_this_lib=1 35 | echo "bundling $i because it's in the list." 36 | break; 37 | fi 38 | done 39 | 40 | # See if it's in /opt/local. Bundle all in /opt/local 41 | local isOptLocal=0 42 | echo $i |grep -q /opt/local 43 | if [ $? -eq 0 ]; then 44 | isOptLocal=1 45 | echo "bundling $i because it's in /opt/local." 46 | fi 47 | 48 | # Bundle the library 49 | if [ $isOptLocal -ne 0 ] || [ $bundle_this_lib -ne 0 ]; then 50 | 51 | # Copy the file into the bundle if it exists. 52 | if [ -f $EXEPATH/$BASE ]; then 53 | z=0 54 | else 55 | cp $i $EXEPATH 56 | chmod 755 $EXEPATH/$BASE 57 | fi 58 | 59 | 60 | # echo "$BASE_OF_EXE depends on $BASE" 61 | 62 | # Fix the paths using install_name_tool and then 63 | # call this function recursively for each dependency 64 | # of this library. 65 | if [ $BASE_OF_EXE != $BASE ]; then 66 | 67 | # Fix the paths 68 | install_name_tool -id @executable_path/$BASE $EXEPATH/$BASE 69 | install_name_tool -change $i @executable_path/$BASE $EXEPATH/$BASE_OF_EXE 70 | 71 | # Call this function (recursive) on 72 | # on each dependency of this library. 73 | copydeps $EXEPATH/$BASE 74 | fi 75 | fi 76 | done 77 | } 78 | 79 | rm -f $EXEPATH/* 80 | 81 | # Copy the binary into the bundle. Use ../libtool to do this if it's 82 | # available beacuse if $EXE_NAME was built with autotools, it will be 83 | # necessary. If ../libtool not available, just use cp to do the copy, but 84 | # only if $EXE_NAME is a binary. 85 | if [ -x ../libtool ]; then 86 | ../libtool --mode=install cp $EXE_NAME $EXEPATH 87 | else 88 | file -bI $EXE_NAME |grep binary 89 | if [ $? -ne 0 ]; then 90 | echo "There is no ../libtool and $EXE_NAME is not a binary." 91 | echo "I'm not sure what to do." 92 | exit 1 93 | else 94 | cp $EXE_NAME $EXEPATH 95 | fi 96 | fi 97 | copydeps $EXEPATH/$EXE_NAME 98 | -------------------------------------------------------------------------------- /testgui/mac_support.cpp: -------------------------------------------------------------------------------- 1 | /******************************* 2 | Mac support for HID Test GUI 3 | 4 | Alan Ott 5 | Signal 11 Software 6 | 7 | Some of this code is from Apple Documentation, most notably 8 | http://developer.apple.com/legacy/mac/library/documentation/AppleScript/Conceptual/AppleEvents/AppleEvents.pdf 9 | *******************************/ 10 | 11 | #include 12 | #include 13 | 14 | 15 | extern FXMainWindow *g_main_window; 16 | 17 | static pascal OSErr HandleQuitMessage(const AppleEvent *theAppleEvent, AppleEvent 18 | *reply, long handlerRefcon) 19 | { 20 | puts("Quitting\n"); 21 | FXApp::instance()->exit(); 22 | return 0; 23 | } 24 | 25 | static pascal OSErr HandleReopenMessage(const AppleEvent *theAppleEvent, AppleEvent 26 | *reply, long handlerRefcon) 27 | { 28 | puts("Showing"); 29 | g_main_window->show(); 30 | return 0; 31 | } 32 | 33 | static pascal OSErr HandleWildCardMessage(const AppleEvent *theAppleEvent, AppleEvent 34 | *reply, long handlerRefcon) 35 | { 36 | puts("WildCard\n"); 37 | return 0; 38 | } 39 | 40 | OSStatus AEHandler(EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon) 41 | { 42 | Boolean release = false; 43 | EventRecord eventRecord; 44 | OSErr ignoreErrForThisSample; 45 | 46 | // Events of type kEventAppleEvent must be removed from the queue 47 | // before being passed to AEProcessAppleEvent. 48 | if (IsEventInQueue(GetMainEventQueue(), inEvent)) 49 | { 50 | // RemoveEventFromQueue will release the event, which will 51 | // destroy it if we don't retain it first. 52 | RetainEvent(inEvent); 53 | release = true; 54 | RemoveEventFromQueue(GetMainEventQueue(), inEvent); 55 | } 56 | // Convert the event ref to the type AEProcessAppleEvent expects. 57 | ConvertEventRefToEventRecord(inEvent, &eventRecord); 58 | ignoreErrForThisSample = AEProcessAppleEvent(&eventRecord); 59 | if (release) 60 | ReleaseEvent(inEvent); 61 | // This Carbon event has been handled, even if no AppleEvent handlers 62 | // were installed for the Apple event. 63 | return noErr; 64 | } 65 | 66 | static void HandleEvent(EventRecord *event) 67 | { 68 | //printf("What: %d message %x\n", event->what, event->message); 69 | if (event->what == osEvt) { 70 | if (((event->message >> 24) & 0xff) == suspendResumeMessage) { 71 | if (event->message & resumeFlag) { 72 | g_main_window->show(); 73 | } 74 | } 75 | } 76 | 77 | #if 0 78 | switch (event->what) 79 | { 80 | case mouseDown: 81 | //HandleMouseDown(event); 82 | break; 83 | case keyDown: 84 | case autoKey: 85 | //HandleKeyPress(event); 86 | break; 87 | case kHighLevelEvent: 88 | puts("Calling ProcessAppleEvent\n"); 89 | AEProcessAppleEvent(event); 90 | break; 91 | } 92 | #endif 93 | } 94 | 95 | void 96 | init_apple_message_system() 97 | { 98 | OSErr err; 99 | static const EventTypeSpec appleEvents[] = 100 | { 101 | { kEventClassAppleEvent, kEventAppleEvent } 102 | }; 103 | 104 | /* Install the handler for Apple Events */ 105 | InstallApplicationEventHandler(NewEventHandlerUPP(AEHandler), 106 | GetEventTypeCount(appleEvents), appleEvents, 0, NULL); 107 | 108 | /* Install handlers for the individual Apple Events that come 109 | from the Dock icon: the Reopen (click), and the Quit messages. */ 110 | err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, 111 | NewAEEventHandlerUPP(HandleQuitMessage), 0, false); 112 | err = AEInstallEventHandler(kCoreEventClass, kAEReopenApplication, 113 | NewAEEventHandlerUPP(HandleReopenMessage), 0, false); 114 | #if 0 115 | // Left as an example of a wild card match. 116 | err = AEInstallEventHandler(kCoreEventClass, typeWildCard, 117 | NewAEEventHandlerUPP(HandleWildMessage), 0, false); 118 | #endif 119 | } 120 | 121 | void 122 | check_apple_events() 123 | { 124 | RgnHandle cursorRgn = NULL; 125 | Boolean gotEvent=TRUE; 126 | EventRecord event; 127 | 128 | while (gotEvent) { 129 | gotEvent = WaitNextEvent(everyEvent, &event, 0L/*timeout*/, cursorRgn); 130 | if (gotEvent) { 131 | HandleEvent(&event); 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /testgui/mac_support.h: -------------------------------------------------------------------------------- 1 | /******************************* 2 | Mac support for HID Test GUI 3 | 4 | Alan Ott 5 | Signal 11 Software 6 | 7 | *******************************/ 8 | 9 | #ifndef MAC_SUPPORT_H__ 10 | #define MAC_SUPPORT_H__ 11 | 12 | extern "C" { 13 | void init_apple_message_system(); 14 | void check_apple_events(); 15 | } 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /testgui/mac_support_cocoa.m: -------------------------------------------------------------------------------- 1 | /******************************* 2 | Mac support for HID Test GUI 3 | 4 | Alan Ott 5 | Signal 11 Software 6 | *******************************/ 7 | 8 | #include 9 | #import 10 | 11 | extern FXMainWindow *g_main_window; 12 | 13 | 14 | @interface MyAppDelegate : NSObject 15 | { 16 | } 17 | @end 18 | 19 | @implementation MyAppDelegate 20 | - (void) applicationWillBecomeActive:(NSNotification*)notif 21 | { 22 | printf("WillBecomeActive\n"); 23 | g_main_window->show(); 24 | 25 | } 26 | 27 | - (void) applicationWillTerminate:(NSNotification*)notif 28 | { 29 | /* Doesn't get called. Not sure why */ 30 | printf("WillTerminate\n"); 31 | FXApp::instance()->exit(); 32 | } 33 | 34 | - (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication*)sender 35 | { 36 | /* Doesn't get called. Not sure why */ 37 | printf("ShouldTerminate\n"); 38 | return YES; 39 | } 40 | 41 | - (void) applicationWillHide:(NSNotification*)notif 42 | { 43 | printf("WillHide\n"); 44 | g_main_window->hide(); 45 | } 46 | 47 | - (void) handleQuitEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAppleEventDescriptor*)replyEvent 48 | { 49 | printf("QuitEvent\n"); 50 | FXApp::instance()->exit(); 51 | } 52 | 53 | @end 54 | 55 | extern "C" { 56 | 57 | void 58 | init_apple_message_system() 59 | { 60 | static MyAppDelegate *d = [MyAppDelegate new]; 61 | 62 | [[NSApplication sharedApplication] setDelegate:d]; 63 | 64 | /* Register for Apple Events. */ 65 | /* This is from 66 | http://stackoverflow.com/questions/1768497/application-exit-event */ 67 | NSAppleEventManager *aem = [NSAppleEventManager sharedAppleEventManager]; 68 | [aem setEventHandler:d 69 | andSelector:@selector(handleQuitEvent:withReplyEvent:) 70 | forEventClass:kCoreEventClass andEventID:kAEQuitApplication]; 71 | } 72 | 73 | void 74 | check_apple_events() 75 | { 76 | NSApplication *app = [NSApplication sharedApplication]; 77 | 78 | NSAutoreleasePool *pool = [NSAutoreleasePool new]; 79 | while (1) { 80 | NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask 81 | untilDate:nil 82 | inMode:NSDefaultRunLoopMode 83 | dequeue:YES]; 84 | if (event == NULL) 85 | break; 86 | else { 87 | //printf("Event happened: Type: %d\n", event->_type); 88 | [app sendEvent: event]; 89 | } 90 | } 91 | [pool release]; 92 | } 93 | 94 | } /* extern "C" */ 95 | -------------------------------------------------------------------------------- /testgui/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | xterm -e /Users/alan/work/hidapi/testgui/TestGUI.app/Contents/MacOS/tg 3 | -------------------------------------------------------------------------------- /testgui/test.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************* 2 | Demo Program for HIDAPI 3 | 4 | Alan Ott 5 | Signal 11 Software 6 | 7 | 2010-07-20 8 | 9 | Copyright 2010, All Rights Reserved 10 | 11 | This contents of this file may be used by anyone 12 | for any reason without any conditions and may be 13 | used as a starting point for your own applications 14 | which use HIDAPI. 15 | ********************************************************/ 16 | 17 | 18 | #include 19 | 20 | #include "hidapi.h" 21 | #include "mac_support.h" 22 | #include 23 | #include 24 | #include 25 | 26 | #ifdef _WIN32 27 | // Thanks Microsoft, but I know how to use strncpy(). 28 | #pragma warning(disable:4996) 29 | #endif 30 | 31 | class MainWindow : public FXMainWindow { 32 | FXDECLARE(MainWindow) 33 | 34 | public: 35 | enum { 36 | ID_FIRST = FXMainWindow::ID_LAST, 37 | ID_CONNECT, 38 | ID_DISCONNECT, 39 | ID_RESCAN, 40 | ID_SEND_OUTPUT_REPORT, 41 | ID_SEND_FEATURE_REPORT, 42 | ID_GET_FEATURE_REPORT, 43 | ID_CLEAR, 44 | ID_TIMER, 45 | ID_MAC_TIMER, 46 | ID_LAST, 47 | }; 48 | 49 | private: 50 | FXList *device_list; 51 | FXButton *connect_button; 52 | FXButton *disconnect_button; 53 | FXButton *rescan_button; 54 | FXButton *output_button; 55 | FXLabel *connected_label; 56 | FXTextField *output_text; 57 | FXTextField *output_len; 58 | FXButton *feature_button; 59 | FXButton *get_feature_button; 60 | FXTextField *feature_text; 61 | FXTextField *feature_len; 62 | FXTextField *get_feature_text; 63 | FXText *input_text; 64 | FXFont *title_font; 65 | 66 | struct hid_device_info *devices; 67 | hid_device *connected_device; 68 | size_t getDataFromTextField(FXTextField *tf, char *buf, size_t len); 69 | int getLengthFromTextField(FXTextField *tf); 70 | 71 | 72 | protected: 73 | MainWindow() {}; 74 | public: 75 | MainWindow(FXApp *a); 76 | ~MainWindow(); 77 | virtual void create(); 78 | 79 | long onConnect(FXObject *sender, FXSelector sel, void *ptr); 80 | long onDisconnect(FXObject *sender, FXSelector sel, void *ptr); 81 | long onRescan(FXObject *sender, FXSelector sel, void *ptr); 82 | long onSendOutputReport(FXObject *sender, FXSelector sel, void *ptr); 83 | long onSendFeatureReport(FXObject *sender, FXSelector sel, void *ptr); 84 | long onGetFeatureReport(FXObject *sender, FXSelector sel, void *ptr); 85 | long onClear(FXObject *sender, FXSelector sel, void *ptr); 86 | long onTimeout(FXObject *sender, FXSelector sel, void *ptr); 87 | long onMacTimeout(FXObject *sender, FXSelector sel, void *ptr); 88 | }; 89 | 90 | // FOX 1.7 changes the timeouts to all be nanoseconds. 91 | // Fox 1.6 had all timeouts as milliseconds. 92 | #if (FOX_MINOR >= 7) 93 | const int timeout_scalar = 1000*1000; 94 | #else 95 | const int timeout_scalar = 1; 96 | #endif 97 | 98 | FXMainWindow *g_main_window; 99 | 100 | 101 | FXDEFMAP(MainWindow) MainWindowMap [] = { 102 | FXMAPFUNC(SEL_COMMAND, MainWindow::ID_CONNECT, MainWindow::onConnect ), 103 | FXMAPFUNC(SEL_COMMAND, MainWindow::ID_DISCONNECT, MainWindow::onDisconnect ), 104 | FXMAPFUNC(SEL_COMMAND, MainWindow::ID_RESCAN, MainWindow::onRescan ), 105 | FXMAPFUNC(SEL_COMMAND, MainWindow::ID_SEND_OUTPUT_REPORT, MainWindow::onSendOutputReport ), 106 | FXMAPFUNC(SEL_COMMAND, MainWindow::ID_SEND_FEATURE_REPORT, MainWindow::onSendFeatureReport ), 107 | FXMAPFUNC(SEL_COMMAND, MainWindow::ID_GET_FEATURE_REPORT, MainWindow::onGetFeatureReport ), 108 | FXMAPFUNC(SEL_COMMAND, MainWindow::ID_CLEAR, MainWindow::onClear ), 109 | FXMAPFUNC(SEL_TIMEOUT, MainWindow::ID_TIMER, MainWindow::onTimeout ), 110 | FXMAPFUNC(SEL_TIMEOUT, MainWindow::ID_MAC_TIMER, MainWindow::onMacTimeout ), 111 | }; 112 | 113 | FXIMPLEMENT(MainWindow, FXMainWindow, MainWindowMap, ARRAYNUMBER(MainWindowMap)); 114 | 115 | MainWindow::MainWindow(FXApp *app) 116 | : FXMainWindow(app, "HIDAPI Test Application", NULL, NULL, DECOR_ALL, 200,100, 425,700) 117 | { 118 | devices = NULL; 119 | connected_device = NULL; 120 | 121 | FXVerticalFrame *vf = new FXVerticalFrame(this, LAYOUT_FILL_Y|LAYOUT_FILL_X); 122 | 123 | FXLabel *label = new FXLabel(vf, "HIDAPI Test Tool"); 124 | title_font = new FXFont(getApp(), "Arial", 14, FXFont::Bold); 125 | label->setFont(title_font); 126 | 127 | new FXLabel(vf, 128 | "Select a device and press Connect.", NULL, JUSTIFY_LEFT); 129 | new FXLabel(vf, 130 | "Output data bytes can be entered in the Output section, \n" 131 | "separated by space, comma or brackets. Data starting with 0x\n" 132 | "is treated as hex. Data beginning with a 0 is treated as \n" 133 | "octal. All other data is treated as decimal.", NULL, JUSTIFY_LEFT); 134 | new FXLabel(vf, 135 | "Data received from the device appears in the Input section.", 136 | NULL, JUSTIFY_LEFT); 137 | new FXLabel(vf, 138 | "Optionally, a report length may be specified. Extra bytes are\n" 139 | "padded with zeros. If no length is specified, the length is \n" 140 | "inferred from the data.", 141 | NULL, JUSTIFY_LEFT); 142 | new FXLabel(vf, ""); 143 | 144 | // Device List and Connect/Disconnect buttons 145 | FXHorizontalFrame *hf = new FXHorizontalFrame(vf, LAYOUT_FILL_X); 146 | //device_list = new FXList(new FXHorizontalFrame(hf,FRAME_SUNKEN|FRAME_THICK, 0,0,0,0, 0,0,0,0), NULL, 0, LISTBOX_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0,0,300,200); 147 | device_list = new FXList(new FXHorizontalFrame(hf,FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_Y, 0,0,0,0, 0,0,0,0), NULL, 0, LISTBOX_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_Y, 0,0,300,200); 148 | FXVerticalFrame *buttonVF = new FXVerticalFrame(hf); 149 | connect_button = new FXButton(buttonVF, "Connect", NULL, this, ID_CONNECT, BUTTON_NORMAL|LAYOUT_FILL_X); 150 | disconnect_button = new FXButton(buttonVF, "Disconnect", NULL, this, ID_DISCONNECT, BUTTON_NORMAL|LAYOUT_FILL_X); 151 | disconnect_button->disable(); 152 | rescan_button = new FXButton(buttonVF, "Re-Scan devices", NULL, this, ID_RESCAN, BUTTON_NORMAL|LAYOUT_FILL_X); 153 | new FXHorizontalFrame(buttonVF, 0, 0,0,0,0, 0,0,50,0); 154 | 155 | connected_label = new FXLabel(vf, "Disconnected"); 156 | 157 | new FXHorizontalFrame(vf); 158 | 159 | // Output Group Box 160 | FXGroupBox *gb = new FXGroupBox(vf, "Output", FRAME_GROOVE|LAYOUT_FILL_X); 161 | FXMatrix *matrix = new FXMatrix(gb, 3, MATRIX_BY_COLUMNS|LAYOUT_FILL_X); 162 | new FXLabel(matrix, "Data"); 163 | new FXLabel(matrix, "Length"); 164 | new FXLabel(matrix, ""); 165 | 166 | //hf = new FXHorizontalFrame(gb, LAYOUT_FILL_X); 167 | output_text = new FXTextField(matrix, 30, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN); 168 | output_text->setText("1 0x81 0"); 169 | output_len = new FXTextField(matrix, 5, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN); 170 | output_button = new FXButton(matrix, "Send Output Report", NULL, this, ID_SEND_OUTPUT_REPORT, BUTTON_NORMAL|LAYOUT_FILL_X); 171 | output_button->disable(); 172 | //new FXHorizontalFrame(matrix, LAYOUT_FILL_X); 173 | 174 | //hf = new FXHorizontalFrame(gb, LAYOUT_FILL_X); 175 | feature_text = new FXTextField(matrix, 30, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN); 176 | feature_len = new FXTextField(matrix, 5, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN); 177 | feature_button = new FXButton(matrix, "Send Feature Report", NULL, this, ID_SEND_FEATURE_REPORT, BUTTON_NORMAL|LAYOUT_FILL_X); 178 | feature_button->disable(); 179 | 180 | get_feature_text = new FXTextField(matrix, 30, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN); 181 | new FXWindow(matrix); 182 | get_feature_button = new FXButton(matrix, "Get Feature Report", NULL, this, ID_GET_FEATURE_REPORT, BUTTON_NORMAL|LAYOUT_FILL_X); 183 | get_feature_button->disable(); 184 | 185 | 186 | // Input Group Box 187 | gb = new FXGroupBox(vf, "Input", FRAME_GROOVE|LAYOUT_FILL_X|LAYOUT_FILL_Y); 188 | FXVerticalFrame *innerVF = new FXVerticalFrame(gb, LAYOUT_FILL_X|LAYOUT_FILL_Y); 189 | input_text = new FXText(new FXHorizontalFrame(innerVF,LAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_SUNKEN|FRAME_THICK, 0,0,0,0, 0,0,0,0), NULL, 0, LAYOUT_FILL_X|LAYOUT_FILL_Y); 190 | input_text->setEditable(false); 191 | new FXButton(innerVF, "Clear", NULL, this, ID_CLEAR, BUTTON_NORMAL|LAYOUT_RIGHT); 192 | 193 | 194 | } 195 | 196 | MainWindow::~MainWindow() 197 | { 198 | if (connected_device) 199 | hid_close(connected_device); 200 | hid_exit(); 201 | delete title_font; 202 | } 203 | 204 | void 205 | MainWindow::create() 206 | { 207 | FXMainWindow::create(); 208 | show(); 209 | 210 | onRescan(NULL, 0, NULL); 211 | 212 | 213 | #ifdef __APPLE__ 214 | init_apple_message_system(); 215 | #endif 216 | 217 | getApp()->addTimeout(this, ID_MAC_TIMER, 218 | 50 * timeout_scalar /*50ms*/); 219 | } 220 | 221 | long 222 | MainWindow::onConnect(FXObject *sender, FXSelector sel, void *ptr) 223 | { 224 | if (connected_device != NULL) 225 | return 1; 226 | 227 | FXint cur_item = device_list->getCurrentItem(); 228 | if (cur_item < 0) 229 | return -1; 230 | FXListItem *item = device_list->getItem(cur_item); 231 | if (!item) 232 | return -1; 233 | struct hid_device_info *device_info = (struct hid_device_info*) item->getData(); 234 | if (!device_info) 235 | return -1; 236 | 237 | connected_device = hid_open_path(device_info->path); 238 | 239 | if (!connected_device) { 240 | FXMessageBox::error(this, MBOX_OK, "Device Error", "Unable To Connect to Device"); 241 | return -1; 242 | } 243 | 244 | hid_set_nonblocking(connected_device, 1); 245 | 246 | getApp()->addTimeout(this, ID_TIMER, 247 | 5 * timeout_scalar /*5ms*/); 248 | 249 | FXString s; 250 | s.format("Connected to: %04hx:%04hx -", device_info->vendor_id, device_info->product_id); 251 | s += FXString(" ") + device_info->manufacturer_string; 252 | s += FXString(" ") + device_info->product_string; 253 | connected_label->setText(s); 254 | output_button->enable(); 255 | feature_button->enable(); 256 | get_feature_button->enable(); 257 | connect_button->disable(); 258 | disconnect_button->enable(); 259 | input_text->setText(""); 260 | 261 | 262 | return 1; 263 | } 264 | 265 | long 266 | MainWindow::onDisconnect(FXObject *sender, FXSelector sel, void *ptr) 267 | { 268 | hid_close(connected_device); 269 | connected_device = NULL; 270 | connected_label->setText("Disconnected"); 271 | output_button->disable(); 272 | feature_button->disable(); 273 | get_feature_button->disable(); 274 | connect_button->enable(); 275 | disconnect_button->disable(); 276 | 277 | getApp()->removeTimeout(this, ID_TIMER); 278 | 279 | return 1; 280 | } 281 | 282 | long 283 | MainWindow::onRescan(FXObject *sender, FXSelector sel, void *ptr) 284 | { 285 | struct hid_device_info *cur_dev; 286 | 287 | device_list->clearItems(); 288 | 289 | // List the Devices 290 | hid_free_enumeration(devices); 291 | devices = hid_enumerate(0x0, 0x0); 292 | cur_dev = devices; 293 | while (cur_dev) { 294 | // Add it to the List Box. 295 | FXString s; 296 | FXString usage_str; 297 | s.format("%04hx:%04hx -", cur_dev->vendor_id, cur_dev->product_id); 298 | s += FXString(" ") + cur_dev->manufacturer_string; 299 | s += FXString(" ") + cur_dev->product_string; 300 | usage_str.format(" (usage: %04hx:%04hx) ", cur_dev->usage_page, cur_dev->usage); 301 | s += usage_str; 302 | FXListItem *li = new FXListItem(s, NULL, cur_dev); 303 | device_list->appendItem(li); 304 | 305 | cur_dev = cur_dev->next; 306 | } 307 | 308 | if (device_list->getNumItems() == 0) 309 | device_list->appendItem("*** No Devices Connected ***"); 310 | else { 311 | device_list->selectItem(0); 312 | } 313 | 314 | return 1; 315 | } 316 | 317 | size_t 318 | MainWindow::getDataFromTextField(FXTextField *tf, char *buf, size_t len) 319 | { 320 | const char *delim = " ,{}\t\r\n"; 321 | FXString data = tf->getText(); 322 | const FXchar *d = data.text(); 323 | size_t i = 0; 324 | 325 | // Copy the string from the GUI. 326 | size_t sz = strlen(d); 327 | char *str = (char*) malloc(sz+1); 328 | strcpy(str, d); 329 | 330 | // For each token in the string, parse and store in buf[]. 331 | char *token = strtok(str, delim); 332 | while (token) { 333 | char *endptr; 334 | long int val = strtol(token, &endptr, 0); 335 | buf[i++] = val; 336 | token = strtok(NULL, delim); 337 | } 338 | 339 | free(str); 340 | return i; 341 | } 342 | 343 | /* getLengthFromTextField() 344 | Returns length: 345 | 0: empty text field 346 | >0: valid length 347 | -1: invalid length */ 348 | int 349 | MainWindow::getLengthFromTextField(FXTextField *tf) 350 | { 351 | long int len; 352 | FXString str = tf->getText(); 353 | size_t sz = str.length(); 354 | 355 | if (sz > 0) { 356 | char *endptr; 357 | len = strtol(str.text(), &endptr, 0); 358 | if (endptr != str.text() && *endptr == '\0') { 359 | if (len <= 0) { 360 | FXMessageBox::error(this, MBOX_OK, "Invalid length", "Enter a length greater than zero."); 361 | return -1; 362 | } 363 | return len; 364 | } 365 | else 366 | return -1; 367 | } 368 | 369 | return 0; 370 | } 371 | 372 | long 373 | MainWindow::onSendOutputReport(FXObject *sender, FXSelector sel, void *ptr) 374 | { 375 | char buf[256]; 376 | size_t data_len, len; 377 | int textfield_len; 378 | 379 | memset(buf, 0x0, sizeof(buf)); 380 | textfield_len = getLengthFromTextField(output_len); 381 | data_len = getDataFromTextField(output_text, buf, sizeof(buf)); 382 | 383 | if (textfield_len < 0) { 384 | FXMessageBox::error(this, MBOX_OK, "Invalid length", "Length field is invalid. Please enter a number in hex, octal, or decimal."); 385 | return 1; 386 | } 387 | 388 | if (textfield_len > sizeof(buf)) { 389 | FXMessageBox::error(this, MBOX_OK, "Invalid length", "Length field is too long."); 390 | return 1; 391 | } 392 | 393 | len = (textfield_len)? textfield_len: data_len; 394 | 395 | int res = hid_write(connected_device, (const unsigned char*)buf, len); 396 | if (res < 0) { 397 | FXMessageBox::error(this, MBOX_OK, "Error Writing", "Could not write to device. Error reported was: %ls", hid_error(connected_device)); 398 | } 399 | 400 | return 1; 401 | } 402 | 403 | long 404 | MainWindow::onSendFeatureReport(FXObject *sender, FXSelector sel, void *ptr) 405 | { 406 | char buf[256]; 407 | size_t data_len, len; 408 | int textfield_len; 409 | 410 | memset(buf, 0x0, sizeof(buf)); 411 | textfield_len = getLengthFromTextField(feature_len); 412 | data_len = getDataFromTextField(feature_text, buf, sizeof(buf)); 413 | 414 | if (textfield_len < 0) { 415 | FXMessageBox::error(this, MBOX_OK, "Invalid length", "Length field is invalid. Please enter a number in hex, octal, or decimal."); 416 | return 1; 417 | } 418 | 419 | if (textfield_len > sizeof(buf)) { 420 | FXMessageBox::error(this, MBOX_OK, "Invalid length", "Length field is too long."); 421 | return 1; 422 | } 423 | 424 | len = (textfield_len)? textfield_len: data_len; 425 | 426 | int res = hid_send_feature_report(connected_device, (const unsigned char*)buf, len); 427 | if (res < 0) { 428 | FXMessageBox::error(this, MBOX_OK, "Error Writing", "Could not send feature report to device. Error reported was: %ls", hid_error(connected_device)); 429 | } 430 | 431 | return 1; 432 | } 433 | 434 | long 435 | MainWindow::onGetFeatureReport(FXObject *sender, FXSelector sel, void *ptr) 436 | { 437 | char buf[256]; 438 | size_t len; 439 | 440 | memset(buf, 0x0, sizeof(buf)); 441 | len = getDataFromTextField(get_feature_text, buf, sizeof(buf)); 442 | 443 | if (len != 1) { 444 | FXMessageBox::error(this, MBOX_OK, "Too many numbers", "Enter only a single report number in the text field"); 445 | } 446 | 447 | int res = hid_get_feature_report(connected_device, (unsigned char*)buf, sizeof(buf)); 448 | if (res < 0) { 449 | FXMessageBox::error(this, MBOX_OK, "Error Getting Report", "Could not get feature report from device. Error reported was: %ls", hid_error(connected_device)); 450 | } 451 | 452 | if (res > 0) { 453 | FXString s; 454 | s.format("Returned Feature Report. %d bytes:\n", res); 455 | for (int i = 0; i < res; i++) { 456 | FXString t; 457 | t.format("%02hhx ", buf[i]); 458 | s += t; 459 | if ((i+1) % 4 == 0) 460 | s += " "; 461 | if ((i+1) % 16 == 0) 462 | s += "\n"; 463 | } 464 | s += "\n"; 465 | input_text->appendText(s); 466 | input_text->setBottomLine(INT_MAX); 467 | } 468 | 469 | return 1; 470 | } 471 | 472 | long 473 | MainWindow::onClear(FXObject *sender, FXSelector sel, void *ptr) 474 | { 475 | input_text->setText(""); 476 | return 1; 477 | } 478 | 479 | long 480 | MainWindow::onTimeout(FXObject *sender, FXSelector sel, void *ptr) 481 | { 482 | unsigned char buf[256]; 483 | int res = hid_read(connected_device, buf, sizeof(buf)); 484 | 485 | if (res > 0) { 486 | FXString s; 487 | s.format("Received %d bytes:\n", res); 488 | for (int i = 0; i < res; i++) { 489 | FXString t; 490 | t.format("%02hhx ", buf[i]); 491 | s += t; 492 | if ((i+1) % 4 == 0) 493 | s += " "; 494 | if ((i+1) % 16 == 0) 495 | s += "\n"; 496 | } 497 | s += "\n"; 498 | input_text->appendText(s); 499 | input_text->setBottomLine(INT_MAX); 500 | } 501 | if (res < 0) { 502 | input_text->appendText("hid_read() returned error\n"); 503 | input_text->setBottomLine(INT_MAX); 504 | } 505 | 506 | getApp()->addTimeout(this, ID_TIMER, 507 | 5 * timeout_scalar /*5ms*/); 508 | return 1; 509 | } 510 | 511 | long 512 | MainWindow::onMacTimeout(FXObject *sender, FXSelector sel, void *ptr) 513 | { 514 | #ifdef __APPLE__ 515 | check_apple_events(); 516 | 517 | getApp()->addTimeout(this, ID_MAC_TIMER, 518 | 50 * timeout_scalar /*50ms*/); 519 | #endif 520 | 521 | return 1; 522 | } 523 | 524 | int main(int argc, char **argv) 525 | { 526 | FXApp app("HIDAPI Test Application", "Signal 11 Software"); 527 | app.init(argc, argv); 528 | g_main_window = new MainWindow(&app); 529 | app.create(); 530 | app.run(); 531 | return 0; 532 | } 533 | -------------------------------------------------------------------------------- /testgui/testgui.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 10.00 3 | # Visual C++ Express 2008 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testgui", "testgui.vcproj", "{08769AC3-785A-4DDC-BFC7-1775414B7AB7}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Debug|Win32.Build.0 = Debug|Win32 14 | {08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Release|Win32.ActiveCfg = Release|Win32 15 | {08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /testgui/testgui.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 26 | 29 | 32 | 35 | 38 | 41 | 53 | 56 | 59 | 62 | 73 | 76 | 79 | 82 | 85 | 88 | 91 | 95 | 96 | 104 | 107 | 110 | 113 | 116 | 119 | 131 | 134 | 137 | 140 | 153 | 156 | 159 | 162 | 165 | 168 | 171 | 175 | 176 | 177 | 178 | 179 | 180 | 185 | 188 | 189 | 192 | 193 | 194 | 199 | 202 | 203 | 204 | 209 | 210 | 213 | 214 | 215 | 216 | 217 | 218 | -------------------------------------------------------------------------------- /udev/99-hid.rules: -------------------------------------------------------------------------------- 1 | # This is a sample udev file for HIDAPI devices which changes the permissions 2 | # to 0666 (world readable/writable) for a specified device on Linux systems. 3 | 4 | 5 | # If you are using the libusb implementation of hidapi (libusb/hid.c), then 6 | # use something like the following line, substituting the VID and PID with 7 | # those of your device. Note that for kernels before 2.6.24, you will need 8 | # to substitute "usb" with "usb_device". It shouldn't hurt to use two lines 9 | # (one each way) for compatibility with older systems. 10 | 11 | # HIDAPI/libusb 12 | SUBSYSTEM=="usb", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="003f", MODE="0666" 13 | 14 | 15 | # If you are using the hidraw implementation (linux/hid.c), then do something 16 | # like the following, substituting the VID and PID with your device. Busnum 1 17 | # is USB. 18 | 19 | # HIDAPI/hidraw 20 | KERNEL=="hidraw*", ATTRS{busnum}=="1", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="003f", MODE="0666" 21 | 22 | # Once done, optionally rename this file for your device, and drop it into 23 | # /etc/udev/rules.d and unplug and re-plug your device. This is all that is 24 | # necessary to see the new permissions. Udev does not have to be restarted. 25 | 26 | # Note that the hexadecimal values for VID and PID are case sensitive and 27 | # must be lower case. 28 | 29 | # If you think permissions of 0666 are too loose, then see: 30 | # http://reactivated.net/writing_udev_rules.html for more information on finer 31 | # grained permission setting. For example, it might be sufficient to just 32 | # set the group or user owner for specific devices (for example the plugdev 33 | # group on some systems). 34 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | Debug 2 | Release 3 | *.exp 4 | *.ilk 5 | *.lib 6 | *.suo 7 | *.vcproj.* 8 | *.ncb 9 | *.suo 10 | *.dll 11 | *.pdb 12 | .deps 13 | .libs 14 | *.lo 15 | *.la 16 | -------------------------------------------------------------------------------- /windows/Makefile-manual: -------------------------------------------------------------------------------- 1 | 2 | 3 | OS=$(shell uname) 4 | 5 | ifneq (,$(findstring MINGW,$(OS))) 6 | FILE=Makefile.mingw 7 | endif 8 | 9 | ifeq ($(FILE), ) 10 | all: 11 | $(error Your platform ${OS} is not supported at this time.) 12 | endif 13 | 14 | include $(FILE) 15 | -------------------------------------------------------------------------------- /windows/Makefile.am: -------------------------------------------------------------------------------- 1 | lib_LTLIBRARIES = libhidapi.la 2 | libhidapi_la_SOURCES = hid.c 3 | libhidapi_la_LDFLAGS = $(LTLDFLAGS) 4 | AM_CPPFLAGS = -I$(top_srcdir)/hidapi/ 5 | libhidapi_la_LIBADD = $(LIBS) 6 | 7 | hdrdir = $(includedir)/hidapi 8 | hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h 9 | 10 | EXTRA_DIST = \ 11 | ddk_build \ 12 | hidapi.vcproj \ 13 | hidtest.vcproj \ 14 | Makefile-manual \ 15 | Makefile.mingw \ 16 | hidapi.sln 17 | -------------------------------------------------------------------------------- /windows/Makefile.mingw: -------------------------------------------------------------------------------- 1 | ########################################### 2 | # Simple Makefile for HIDAPI test program 3 | # 4 | # Alan Ott 5 | # Signal 11 Software 6 | # 2010-06-01 7 | ########################################### 8 | 9 | all: hidtest libhidapi.dll 10 | 11 | CC=gcc 12 | CXX=g++ 13 | COBJS=hid.o 14 | CPPOBJS=../hidtest/hidtest.o 15 | OBJS=$(COBJS) $(CPPOBJS) 16 | CFLAGS=-I../hidapi -g -c 17 | LIBS= -lsetupapi 18 | DLL_LDFLAGS = -mwindows -lsetupapi 19 | 20 | hidtest: $(OBJS) 21 | g++ -g $^ $(LIBS) -o hidtest 22 | 23 | libhidapi.dll: $(OBJS) 24 | $(CC) -g $^ $(DLL_LDFLAGS) -o libhidapi.dll 25 | 26 | $(COBJS): %.o: %.c 27 | $(CC) $(CFLAGS) $< -o $@ 28 | 29 | $(CPPOBJS): %.o: %.cpp 30 | $(CXX) $(CFLAGS) $< -o $@ 31 | 32 | clean: 33 | rm *.o ../hidtest/*.o hidtest.exe 34 | 35 | .PHONY: clean 36 | -------------------------------------------------------------------------------- /windows/ddk_build/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | obj*_*_* -------------------------------------------------------------------------------- /windows/ddk_build/hidapi.def: -------------------------------------------------------------------------------- 1 | LIBRARY hidapi 2 | EXPORTS 3 | hid_open @1 4 | hid_write @2 5 | hid_read @3 6 | hid_close @4 7 | hid_get_product_string @5 8 | hid_get_manufacturer_string @6 9 | hid_get_serial_number_string @7 10 | hid_get_indexed_string @8 11 | hid_error @9 12 | hid_set_nonblocking @10 13 | hid_enumerate @11 14 | hid_open_path @12 15 | hid_send_feature_report @13 16 | hid_get_feature_report @14 17 | -------------------------------------------------------------------------------- /windows/ddk_build/makefile: -------------------------------------------------------------------------------- 1 | ############################################################################# 2 | # 3 | # Copyright (C) Microsoft Corporation 1995, 1996 4 | # All Rights Reserved. 5 | # 6 | # MAKEFILE for HID directory 7 | # 8 | ############################################################################# 9 | 10 | !IFDEF WIN95_BUILD 11 | 12 | ROOT=..\..\..\.. 13 | 14 | VERSIONLIST = debug retail 15 | IS_32 = TRUE 16 | IS_SDK = TRUE 17 | IS_PRIVATE = TRUE 18 | IS_SDK = TRUE 19 | IS_DDK = TRUE 20 | WIN32 = TRUE 21 | COMMONMKFILE = hidapi.mk 22 | 23 | !include $(ROOT)\dev\master.mk 24 | 25 | 26 | !ELSE 27 | 28 | # 29 | # DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source 30 | # file to this component. This file merely indirects to the real make file 31 | # that is shared by all the driver components of the Windows NT DDK 32 | # 33 | 34 | !IF DEFINED(_NT_TARGET_VERSION) 35 | ! IF $(_NT_TARGET_VERSION)>=0x501 36 | ! INCLUDE $(NTMAKEENV)\makefile.def 37 | ! ELSE 38 | # Only warn once per directory 39 | ! INCLUDE $(NTMAKEENV)\makefile.plt 40 | ! IF "$(BUILD_PASS)"=="PASS1" 41 | ! message BUILDMSG: Warning : The sample "$(MAKEDIR)" is not valid for the current OS target. 42 | ! ENDIF 43 | ! ENDIF 44 | !ELSE 45 | ! INCLUDE $(NTMAKEENV)\makefile.def 46 | !ENDIF 47 | 48 | !ENDIF 49 | 50 | -------------------------------------------------------------------------------- /windows/ddk_build/sources: -------------------------------------------------------------------------------- 1 | TARGETNAME=hidapi 2 | TARGETTYPE=DYNLINK 3 | UMTYPE=console 4 | UMENTRY=main 5 | 6 | MSC_WARNING_LEVEL=/W3 /WX 7 | 8 | TARGETLIBS=$(SDK_LIB_PATH)\hid.lib \ 9 | $(SDK_LIB_PATH)\setupapi.lib \ 10 | $(SDK_LIB_PATH)\kernel32.lib \ 11 | $(SDK_LIB_PATH)\comdlg32.lib 12 | 13 | USE_MSVCRT=1 14 | 15 | INCLUDES= ..\..\hidapi 16 | SOURCES= ..\hid.c \ 17 | 18 | 19 | TARGET_DESTINATION=retail 20 | 21 | MUI=0 22 | MUI_COMMENT="HID Interface DLL" 23 | 24 | -------------------------------------------------------------------------------- /windows/hid.c: -------------------------------------------------------------------------------- 1 | /******************************************************* 2 | HIDAPI - Multi-Platform library for 3 | communication with HID devices. 4 | 5 | Alan Ott 6 | Signal 11 Software 7 | 8 | 8/22/2009 9 | 10 | Copyright 2009, All Rights Reserved. 11 | 12 | At the discretion of the user of this library, 13 | this software may be licensed under the terms of the 14 | GNU General Public License v3, a BSD-Style license, or the 15 | original HIDAPI license as outlined in the LICENSE.txt, 16 | LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt 17 | files located at the root of the source distribution. 18 | These files may also be found in the public source 19 | code repository located at: 20 | http://github.com/signal11/hidapi . 21 | ********************************************************/ 22 | 23 | #include 24 | 25 | #ifndef _NTDEF_ 26 | typedef LONG NTSTATUS; 27 | #endif 28 | 29 | #ifdef __MINGW32__ 30 | #include 31 | #include 32 | #endif 33 | 34 | #ifdef __CYGWIN__ 35 | #include 36 | #define _wcsdup wcsdup 37 | #endif 38 | 39 | /* The maximum number of characters that can be passed into the 40 | HidD_Get*String() functions without it failing.*/ 41 | #define MAX_STRING_WCHARS 0xFFF 42 | 43 | /*#define HIDAPI_USE_DDK*/ 44 | 45 | #ifdef __cplusplus 46 | extern "C" { 47 | #endif 48 | #include 49 | #include 50 | #ifdef HIDAPI_USE_DDK 51 | #include 52 | #endif 53 | 54 | /* Copied from inc/ddk/hidclass.h, part of the Windows DDK. */ 55 | #define HID_OUT_CTL_CODE(id) \ 56 | CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS) 57 | #define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100) 58 | 59 | #ifdef __cplusplus 60 | } /* extern "C" */ 61 | #endif 62 | 63 | #include 64 | #include 65 | 66 | 67 | #include "hidapi.h" 68 | 69 | #undef MIN 70 | #define MIN(x,y) ((x) < (y)? (x): (y)) 71 | 72 | #ifdef _MSC_VER 73 | /* Thanks Microsoft, but I know how to use strncpy(). */ 74 | #pragma warning(disable:4996) 75 | #endif 76 | 77 | #ifdef __cplusplus 78 | extern "C" { 79 | #endif 80 | 81 | #ifndef HIDAPI_USE_DDK 82 | /* Since we're not building with the DDK, and the HID header 83 | files aren't part of the SDK, we have to define all this 84 | stuff here. In lookup_functions(), the function pointers 85 | defined below are set. */ 86 | typedef struct _HIDD_ATTRIBUTES{ 87 | ULONG Size; 88 | USHORT VendorID; 89 | USHORT ProductID; 90 | USHORT VersionNumber; 91 | } HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; 92 | 93 | typedef USHORT USAGE; 94 | typedef struct _HIDP_CAPS { 95 | USAGE Usage; 96 | USAGE UsagePage; 97 | USHORT InputReportByteLength; 98 | USHORT OutputReportByteLength; 99 | USHORT FeatureReportByteLength; 100 | USHORT Reserved[17]; 101 | USHORT fields_not_used_by_hidapi[10]; 102 | } HIDP_CAPS, *PHIDP_CAPS; 103 | typedef void* PHIDP_PREPARSED_DATA; 104 | #define HIDP_STATUS_SUCCESS 0x110000 105 | 106 | typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib); 107 | typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len); 108 | typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); 109 | typedef BOOLEAN (__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); 110 | typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length); 111 | typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length); 112 | typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len); 113 | typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data); 114 | typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data); 115 | typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, HIDP_CAPS *caps); 116 | typedef BOOLEAN (__stdcall *HidD_SetNumInputBuffers_)(HANDLE handle, ULONG number_buffers); 117 | 118 | static HidD_GetAttributes_ HidD_GetAttributes; 119 | static HidD_GetSerialNumberString_ HidD_GetSerialNumberString; 120 | static HidD_GetManufacturerString_ HidD_GetManufacturerString; 121 | static HidD_GetProductString_ HidD_GetProductString; 122 | static HidD_SetFeature_ HidD_SetFeature; 123 | static HidD_GetFeature_ HidD_GetFeature; 124 | static HidD_GetIndexedString_ HidD_GetIndexedString; 125 | static HidD_GetPreparsedData_ HidD_GetPreparsedData; 126 | static HidD_FreePreparsedData_ HidD_FreePreparsedData; 127 | static HidP_GetCaps_ HidP_GetCaps; 128 | static HidD_SetNumInputBuffers_ HidD_SetNumInputBuffers; 129 | 130 | static HMODULE lib_handle = NULL; 131 | static BOOLEAN initialized = FALSE; 132 | #endif /* HIDAPI_USE_DDK */ 133 | 134 | struct hid_device_ { 135 | HANDLE device_handle; 136 | BOOL blocking; 137 | USHORT output_report_length; 138 | size_t input_report_length; 139 | void *last_error_str; 140 | DWORD last_error_num; 141 | BOOL read_pending; 142 | char *read_buf; 143 | OVERLAPPED ol; 144 | }; 145 | 146 | static hid_device *new_hid_device() 147 | { 148 | hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device)); 149 | dev->device_handle = INVALID_HANDLE_VALUE; 150 | dev->blocking = TRUE; 151 | dev->output_report_length = 0; 152 | dev->input_report_length = 0; 153 | dev->last_error_str = NULL; 154 | dev->last_error_num = 0; 155 | dev->read_pending = FALSE; 156 | dev->read_buf = NULL; 157 | memset(&dev->ol, 0, sizeof(dev->ol)); 158 | dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial state f=nonsignaled*/, NULL); 159 | 160 | return dev; 161 | } 162 | 163 | static void free_hid_device(hid_device *dev) 164 | { 165 | CloseHandle(dev->ol.hEvent); 166 | CloseHandle(dev->device_handle); 167 | LocalFree(dev->last_error_str); 168 | free(dev->read_buf); 169 | free(dev); 170 | } 171 | 172 | static void register_error(hid_device *device, const char *op) 173 | { 174 | WCHAR *ptr, *msg; 175 | 176 | FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | 177 | FORMAT_MESSAGE_FROM_SYSTEM | 178 | FORMAT_MESSAGE_IGNORE_INSERTS, 179 | NULL, 180 | GetLastError(), 181 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 182 | (LPVOID)&msg, 0/*sz*/, 183 | NULL); 184 | 185 | /* Get rid of the CR and LF that FormatMessage() sticks at the 186 | end of the message. Thanks Microsoft! */ 187 | ptr = msg; 188 | while (*ptr) { 189 | if (*ptr == '\r') { 190 | *ptr = 0x0000; 191 | break; 192 | } 193 | ptr++; 194 | } 195 | 196 | /* Store the message off in the Device entry so that 197 | the hid_error() function can pick it up. */ 198 | LocalFree(device->last_error_str); 199 | device->last_error_str = msg; 200 | } 201 | 202 | #ifndef HIDAPI_USE_DDK 203 | static int lookup_functions() 204 | { 205 | lib_handle = LoadLibraryA("hid.dll"); 206 | if (lib_handle) { 207 | #define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1; 208 | RESOLVE(HidD_GetAttributes); 209 | RESOLVE(HidD_GetSerialNumberString); 210 | RESOLVE(HidD_GetManufacturerString); 211 | RESOLVE(HidD_GetProductString); 212 | RESOLVE(HidD_SetFeature); 213 | RESOLVE(HidD_GetFeature); 214 | RESOLVE(HidD_GetIndexedString); 215 | RESOLVE(HidD_GetPreparsedData); 216 | RESOLVE(HidD_FreePreparsedData); 217 | RESOLVE(HidP_GetCaps); 218 | RESOLVE(HidD_SetNumInputBuffers); 219 | #undef RESOLVE 220 | } 221 | else 222 | return -1; 223 | 224 | return 0; 225 | } 226 | #endif 227 | 228 | static HANDLE open_device(const char *path, BOOL enumerate) 229 | { 230 | HANDLE handle; 231 | DWORD desired_access = (enumerate)? 0: (GENERIC_WRITE | GENERIC_READ); 232 | DWORD share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE; 233 | 234 | handle = CreateFileA(path, 235 | desired_access, 236 | share_mode, 237 | NULL, 238 | OPEN_EXISTING, 239 | FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/ 240 | 0); 241 | 242 | return handle; 243 | } 244 | 245 | int HID_API_EXPORT hid_init(void) 246 | { 247 | #ifndef HIDAPI_USE_DDK 248 | if (!initialized) { 249 | if (lookup_functions() < 0) { 250 | hid_exit(); 251 | return -1; 252 | } 253 | initialized = TRUE; 254 | } 255 | #endif 256 | return 0; 257 | } 258 | 259 | int HID_API_EXPORT hid_exit(void) 260 | { 261 | #ifndef HIDAPI_USE_DDK 262 | if (lib_handle) 263 | FreeLibrary(lib_handle); 264 | lib_handle = NULL; 265 | initialized = FALSE; 266 | #endif 267 | return 0; 268 | } 269 | 270 | struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id) 271 | { 272 | BOOL res; 273 | struct hid_device_info *root = NULL; /* return object */ 274 | struct hid_device_info *cur_dev = NULL; 275 | 276 | /* Windows objects for interacting with the driver. */ 277 | GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} }; 278 | SP_DEVINFO_DATA devinfo_data; 279 | SP_DEVICE_INTERFACE_DATA device_interface_data; 280 | SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL; 281 | HDEVINFO device_info_set = INVALID_HANDLE_VALUE; 282 | int device_index = 0; 283 | int i; 284 | 285 | if (hid_init() < 0) 286 | return NULL; 287 | 288 | /* Initialize the Windows objects. */ 289 | memset(&devinfo_data, 0x0, sizeof(devinfo_data)); 290 | devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA); 291 | device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); 292 | 293 | /* Get information for all the devices belonging to the HID class. */ 294 | device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); 295 | 296 | /* Iterate over each device in the HID class, looking for the right one. */ 297 | 298 | for (;;) { 299 | HANDLE write_handle = INVALID_HANDLE_VALUE; 300 | DWORD required_size = 0; 301 | HIDD_ATTRIBUTES attrib; 302 | 303 | res = SetupDiEnumDeviceInterfaces(device_info_set, 304 | NULL, 305 | &InterfaceClassGuid, 306 | device_index, 307 | &device_interface_data); 308 | 309 | if (!res) { 310 | /* A return of FALSE from this function means that 311 | there are no more devices. */ 312 | break; 313 | } 314 | 315 | /* Call with 0-sized detail size, and let the function 316 | tell us how long the detail struct needs to be. The 317 | size is put in &required_size. */ 318 | res = SetupDiGetDeviceInterfaceDetailA(device_info_set, 319 | &device_interface_data, 320 | NULL, 321 | 0, 322 | &required_size, 323 | NULL); 324 | 325 | /* Allocate a long enough structure for device_interface_detail_data. */ 326 | device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(required_size); 327 | device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); 328 | 329 | /* Get the detailed data for this device. The detail data gives us 330 | the device path for this device, which is then passed into 331 | CreateFile() to get a handle to the device. */ 332 | res = SetupDiGetDeviceInterfaceDetailA(device_info_set, 333 | &device_interface_data, 334 | device_interface_detail_data, 335 | required_size, 336 | NULL, 337 | NULL); 338 | 339 | if (!res) { 340 | /* register_error(dev, "Unable to call SetupDiGetDeviceInterfaceDetail"); 341 | Continue to the next device. */ 342 | goto cont; 343 | } 344 | 345 | /* Make sure this device is of Setup Class "HIDClass" and has a 346 | driver bound to it. */ 347 | for (i = 0; ; i++) { 348 | char driver_name[256]; 349 | 350 | /* Populate devinfo_data. This function will return failure 351 | when there are no more interfaces left. */ 352 | res = SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data); 353 | if (!res) 354 | goto cont; 355 | 356 | res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, 357 | SPDRP_CLASS, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); 358 | if (!res) 359 | goto cont; 360 | 361 | if (strcmp(driver_name, "HIDClass") == 0) { 362 | /* See if there's a driver bound. */ 363 | res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, 364 | SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); 365 | if (res) 366 | break; 367 | } 368 | } 369 | 370 | //wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath); 371 | 372 | /* Open a handle to the device */ 373 | write_handle = open_device(device_interface_detail_data->DevicePath, TRUE); 374 | 375 | /* Check validity of write_handle. */ 376 | if (write_handle == INVALID_HANDLE_VALUE) { 377 | /* Unable to open the device. */ 378 | //register_error(dev, "CreateFile"); 379 | goto cont_close; 380 | } 381 | 382 | 383 | /* Get the Vendor ID and Product ID for this device. */ 384 | attrib.Size = sizeof(HIDD_ATTRIBUTES); 385 | HidD_GetAttributes(write_handle, &attrib); 386 | //wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID); 387 | 388 | /* Check the VID/PID to see if we should add this 389 | device to the enumeration list. */ 390 | if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) && 391 | (product_id == 0x0 || attrib.ProductID == product_id)) { 392 | 393 | #define WSTR_LEN 512 394 | const char *str; 395 | struct hid_device_info *tmp; 396 | PHIDP_PREPARSED_DATA pp_data = NULL; 397 | HIDP_CAPS caps; 398 | BOOLEAN res; 399 | NTSTATUS nt_res; 400 | wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */ 401 | size_t len; 402 | 403 | /* VID/PID match. Create the record. */ 404 | tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); 405 | if (cur_dev) { 406 | cur_dev->next = tmp; 407 | } 408 | else { 409 | root = tmp; 410 | } 411 | cur_dev = tmp; 412 | 413 | /* Get the Usage Page and Usage for this device. */ 414 | res = HidD_GetPreparsedData(write_handle, &pp_data); 415 | if (res) { 416 | nt_res = HidP_GetCaps(pp_data, &caps); 417 | if (nt_res == HIDP_STATUS_SUCCESS) { 418 | cur_dev->usage_page = caps.UsagePage; 419 | cur_dev->usage = caps.Usage; 420 | } 421 | 422 | HidD_FreePreparsedData(pp_data); 423 | } 424 | 425 | /* Fill out the record */ 426 | cur_dev->next = NULL; 427 | str = device_interface_detail_data->DevicePath; 428 | if (str) { 429 | len = strlen(str); 430 | cur_dev->path = (char*) calloc(len+1, sizeof(char)); 431 | strncpy(cur_dev->path, str, len+1); 432 | cur_dev->path[len] = '\0'; 433 | } 434 | else 435 | cur_dev->path = NULL; 436 | 437 | /* Serial Number */ 438 | res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr)); 439 | wstr[WSTR_LEN-1] = 0x0000; 440 | if (res) { 441 | cur_dev->serial_number = _wcsdup(wstr); 442 | } 443 | 444 | /* Manufacturer String */ 445 | res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr)); 446 | wstr[WSTR_LEN-1] = 0x0000; 447 | if (res) { 448 | cur_dev->manufacturer_string = _wcsdup(wstr); 449 | } 450 | 451 | /* Product String */ 452 | res = HidD_GetProductString(write_handle, wstr, sizeof(wstr)); 453 | wstr[WSTR_LEN-1] = 0x0000; 454 | if (res) { 455 | cur_dev->product_string = _wcsdup(wstr); 456 | } 457 | 458 | /* VID/PID */ 459 | cur_dev->vendor_id = attrib.VendorID; 460 | cur_dev->product_id = attrib.ProductID; 461 | 462 | /* Release Number */ 463 | cur_dev->release_number = attrib.VersionNumber; 464 | 465 | /* Interface Number. It can sometimes be parsed out of the path 466 | on Windows if a device has multiple interfaces. See 467 | http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or 468 | search for "Hardware IDs for HID Devices" at MSDN. If it's not 469 | in the path, it's set to -1. */ 470 | cur_dev->interface_number = -1; 471 | if (cur_dev->path) { 472 | char *interface_component = strstr(cur_dev->path, "&mi_"); 473 | if (interface_component) { 474 | char *hex_str = interface_component + 4; 475 | char *endptr = NULL; 476 | cur_dev->interface_number = strtol(hex_str, &endptr, 16); 477 | if (endptr == hex_str) { 478 | /* The parsing failed. Set interface_number to -1. */ 479 | cur_dev->interface_number = -1; 480 | } 481 | } 482 | } 483 | } 484 | 485 | cont_close: 486 | CloseHandle(write_handle); 487 | cont: 488 | /* We no longer need the detail data. It can be freed */ 489 | free(device_interface_detail_data); 490 | 491 | device_index++; 492 | 493 | } 494 | 495 | /* Close the device information handle. */ 496 | SetupDiDestroyDeviceInfoList(device_info_set); 497 | 498 | return root; 499 | 500 | } 501 | 502 | void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs) 503 | { 504 | /* TODO: Merge this with the Linux version. This function is platform-independent. */ 505 | struct hid_device_info *d = devs; 506 | while (d) { 507 | struct hid_device_info *next = d->next; 508 | free(d->path); 509 | free(d->serial_number); 510 | free(d->manufacturer_string); 511 | free(d->product_string); 512 | free(d); 513 | d = next; 514 | } 515 | } 516 | 517 | 518 | HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) 519 | { 520 | /* TODO: Merge this functions with the Linux version. This function should be platform independent. */ 521 | struct hid_device_info *devs, *cur_dev; 522 | const char *path_to_open = NULL; 523 | hid_device *handle = NULL; 524 | 525 | devs = hid_enumerate(vendor_id, product_id); 526 | cur_dev = devs; 527 | while (cur_dev) { 528 | if (cur_dev->vendor_id == vendor_id && 529 | cur_dev->product_id == product_id) { 530 | if (serial_number) { 531 | if (wcscmp(serial_number, cur_dev->serial_number) == 0) { 532 | path_to_open = cur_dev->path; 533 | break; 534 | } 535 | } 536 | else { 537 | path_to_open = cur_dev->path; 538 | break; 539 | } 540 | } 541 | cur_dev = cur_dev->next; 542 | } 543 | 544 | if (path_to_open) { 545 | /* Open the device */ 546 | handle = hid_open_path(path_to_open); 547 | } 548 | 549 | hid_free_enumeration(devs); 550 | 551 | return handle; 552 | } 553 | 554 | HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) 555 | { 556 | hid_device *dev; 557 | HIDP_CAPS caps; 558 | PHIDP_PREPARSED_DATA pp_data = NULL; 559 | BOOLEAN res; 560 | NTSTATUS nt_res; 561 | 562 | if (hid_init() < 0) { 563 | return NULL; 564 | } 565 | 566 | dev = new_hid_device(); 567 | 568 | /* Open a handle to the device */ 569 | dev->device_handle = open_device(path, FALSE); 570 | 571 | /* Check validity of write_handle. */ 572 | if (dev->device_handle == INVALID_HANDLE_VALUE) { 573 | /* Unable to open the device. */ 574 | register_error(dev, "CreateFile"); 575 | goto err; 576 | } 577 | 578 | /* Set the Input Report buffer size to 64 reports. */ 579 | res = HidD_SetNumInputBuffers(dev->device_handle, 64); 580 | if (!res) { 581 | register_error(dev, "HidD_SetNumInputBuffers"); 582 | goto err; 583 | } 584 | 585 | /* Get the Input Report length for the device. */ 586 | res = HidD_GetPreparsedData(dev->device_handle, &pp_data); 587 | if (!res) { 588 | register_error(dev, "HidD_GetPreparsedData"); 589 | goto err; 590 | } 591 | nt_res = HidP_GetCaps(pp_data, &caps); 592 | if (nt_res != HIDP_STATUS_SUCCESS) { 593 | register_error(dev, "HidP_GetCaps"); 594 | goto err_pp_data; 595 | } 596 | dev->output_report_length = caps.OutputReportByteLength; 597 | dev->input_report_length = caps.InputReportByteLength; 598 | HidD_FreePreparsedData(pp_data); 599 | 600 | dev->read_buf = (char*) malloc(dev->input_report_length); 601 | 602 | return dev; 603 | 604 | err_pp_data: 605 | HidD_FreePreparsedData(pp_data); 606 | err: 607 | free_hid_device(dev); 608 | return NULL; 609 | } 610 | 611 | int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length) 612 | { 613 | DWORD bytes_written; 614 | BOOL res; 615 | 616 | OVERLAPPED ol; 617 | unsigned char *buf; 618 | memset(&ol, 0, sizeof(ol)); 619 | 620 | /* Make sure the right number of bytes are passed to WriteFile. Windows 621 | expects the number of bytes which are in the _longest_ report (plus 622 | one for the report number) bytes even if the data is a report 623 | which is shorter than that. Windows gives us this value in 624 | caps.OutputReportByteLength. If a user passes in fewer bytes than this, 625 | create a temporary buffer which is the proper size. */ 626 | if (length >= dev->output_report_length) { 627 | /* The user passed the right number of bytes. Use the buffer as-is. */ 628 | buf = (unsigned char *) data; 629 | } else { 630 | /* Create a temporary buffer and copy the user's data 631 | into it, padding the rest with zeros. */ 632 | buf = (unsigned char *) malloc(dev->output_report_length); 633 | memcpy(buf, data, length); 634 | memset(buf + length, 0, dev->output_report_length - length); 635 | length = dev->output_report_length; 636 | } 637 | 638 | res = WriteFile(dev->device_handle, buf, length, NULL, &ol); 639 | 640 | if (!res) { 641 | if (GetLastError() != ERROR_IO_PENDING) { 642 | /* WriteFile() failed. Return error. */ 643 | register_error(dev, "WriteFile"); 644 | bytes_written = -1; 645 | goto end_of_function; 646 | } 647 | } 648 | 649 | /* Wait here until the write is done. This makes 650 | hid_write() synchronous. */ 651 | res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/); 652 | if (!res) { 653 | /* The Write operation failed. */ 654 | register_error(dev, "WriteFile"); 655 | bytes_written = -1; 656 | goto end_of_function; 657 | } 658 | 659 | end_of_function: 660 | if (buf != data) 661 | free(buf); 662 | 663 | return bytes_written; 664 | } 665 | 666 | 667 | int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) 668 | { 669 | DWORD bytes_read = 0; 670 | size_t copy_len = 0; 671 | BOOL res; 672 | 673 | /* Copy the handle for convenience. */ 674 | HANDLE ev = dev->ol.hEvent; 675 | 676 | if (!dev->read_pending) { 677 | /* Start an Overlapped I/O read. */ 678 | dev->read_pending = TRUE; 679 | memset(dev->read_buf, 0, dev->input_report_length); 680 | ResetEvent(ev); 681 | res = ReadFile(dev->device_handle, dev->read_buf, dev->input_report_length, &bytes_read, &dev->ol); 682 | 683 | if (!res) { 684 | if (GetLastError() != ERROR_IO_PENDING) { 685 | /* ReadFile() has failed. 686 | Clean up and return error. */ 687 | CancelIo(dev->device_handle); 688 | dev->read_pending = FALSE; 689 | goto end_of_function; 690 | } 691 | } 692 | } 693 | 694 | if (milliseconds >= 0) { 695 | /* See if there is any data yet. */ 696 | res = WaitForSingleObject(ev, milliseconds); 697 | if (res != WAIT_OBJECT_0) { 698 | /* There was no data this time. Return zero bytes available, 699 | but leave the Overlapped I/O running. */ 700 | return 0; 701 | } 702 | } 703 | 704 | /* Either WaitForSingleObject() told us that ReadFile has completed, or 705 | we are in non-blocking mode. Get the number of bytes read. The actual 706 | data has been copied to the data[] array which was passed to ReadFile(). */ 707 | res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/); 708 | 709 | /* Set pending back to false, even if GetOverlappedResult() returned error. */ 710 | dev->read_pending = FALSE; 711 | 712 | if (res && bytes_read > 0) { 713 | if (dev->read_buf[0] == 0x0) { 714 | /* If report numbers aren't being used, but Windows sticks a report 715 | number (0x0) on the beginning of the report anyway. To make this 716 | work like the other platforms, and to make it work more like the 717 | HID spec, we'll skip over this byte. */ 718 | bytes_read--; 719 | copy_len = length > bytes_read ? bytes_read : length; 720 | memcpy(data, dev->read_buf+1, copy_len); 721 | } 722 | else { 723 | /* Copy the whole buffer, report number and all. */ 724 | copy_len = length > bytes_read ? bytes_read : length; 725 | memcpy(data, dev->read_buf, copy_len); 726 | } 727 | } 728 | 729 | end_of_function: 730 | if (!res) { 731 | register_error(dev, "GetOverlappedResult"); 732 | return -1; 733 | } 734 | 735 | return copy_len; 736 | } 737 | 738 | int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length) 739 | { 740 | return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); 741 | } 742 | 743 | int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock) 744 | { 745 | dev->blocking = !nonblock; 746 | return 0; /* Success */ 747 | } 748 | 749 | int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) 750 | { 751 | BOOL res = HidD_SetFeature(dev->device_handle, (PVOID)data, length); 752 | if (!res) { 753 | register_error(dev, "HidD_SetFeature"); 754 | return -1; 755 | } 756 | 757 | return length; 758 | } 759 | 760 | 761 | int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) 762 | { 763 | BOOL res; 764 | #if 0 765 | res = HidD_GetFeature(dev->device_handle, data, length); 766 | if (!res) { 767 | register_error(dev, "HidD_GetFeature"); 768 | return -1; 769 | } 770 | return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */ 771 | #else 772 | DWORD bytes_returned; 773 | 774 | OVERLAPPED ol; 775 | memset(&ol, 0, sizeof(ol)); 776 | 777 | res = DeviceIoControl(dev->device_handle, 778 | IOCTL_HID_GET_FEATURE, 779 | data, length, 780 | data, length, 781 | &bytes_returned, &ol); 782 | 783 | if (!res) { 784 | if (GetLastError() != ERROR_IO_PENDING) { 785 | /* DeviceIoControl() failed. Return error. */ 786 | register_error(dev, "Send Feature Report DeviceIoControl"); 787 | return -1; 788 | } 789 | } 790 | 791 | /* Wait here until the write is done. This makes 792 | hid_get_feature_report() synchronous. */ 793 | res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/); 794 | if (!res) { 795 | /* The operation failed. */ 796 | register_error(dev, "Send Feature Report GetOverLappedResult"); 797 | return -1; 798 | } 799 | 800 | /* bytes_returned does not include the first byte which contains the 801 | report ID. The data buffer actually contains one more byte than 802 | bytes_returned. */ 803 | bytes_returned++; 804 | 805 | return bytes_returned; 806 | #endif 807 | } 808 | 809 | void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev) 810 | { 811 | if (!dev) 812 | return; 813 | CancelIo(dev->device_handle); 814 | free_hid_device(dev); 815 | } 816 | 817 | int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) 818 | { 819 | BOOL res; 820 | 821 | res = HidD_GetManufacturerString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); 822 | if (!res) { 823 | register_error(dev, "HidD_GetManufacturerString"); 824 | return -1; 825 | } 826 | 827 | return 0; 828 | } 829 | 830 | int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) 831 | { 832 | BOOL res; 833 | 834 | res = HidD_GetProductString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); 835 | if (!res) { 836 | register_error(dev, "HidD_GetProductString"); 837 | return -1; 838 | } 839 | 840 | return 0; 841 | } 842 | 843 | int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) 844 | { 845 | BOOL res; 846 | 847 | res = HidD_GetSerialNumberString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); 848 | if (!res) { 849 | register_error(dev, "HidD_GetSerialNumberString"); 850 | return -1; 851 | } 852 | 853 | return 0; 854 | } 855 | 856 | int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) 857 | { 858 | BOOL res; 859 | 860 | res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); 861 | if (!res) { 862 | register_error(dev, "HidD_GetIndexedString"); 863 | return -1; 864 | } 865 | 866 | return 0; 867 | } 868 | 869 | 870 | HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) 871 | { 872 | return (wchar_t*)dev->last_error_str; 873 | } 874 | 875 | 876 | /*#define PICPGM*/ 877 | /*#define S11*/ 878 | #define P32 879 | #ifdef S11 880 | unsigned short VendorID = 0xa0a0; 881 | unsigned short ProductID = 0x0001; 882 | #endif 883 | 884 | #ifdef P32 885 | unsigned short VendorID = 0x04d8; 886 | unsigned short ProductID = 0x3f; 887 | #endif 888 | 889 | 890 | #ifdef PICPGM 891 | unsigned short VendorID = 0x04d8; 892 | unsigned short ProductID = 0x0033; 893 | #endif 894 | 895 | 896 | #if 0 897 | int __cdecl main(int argc, char* argv[]) 898 | { 899 | int res; 900 | unsigned char buf[65]; 901 | 902 | UNREFERENCED_PARAMETER(argc); 903 | UNREFERENCED_PARAMETER(argv); 904 | 905 | /* Set up the command buffer. */ 906 | memset(buf,0x00,sizeof(buf)); 907 | buf[0] = 0; 908 | buf[1] = 0x81; 909 | 910 | 911 | /* Open the device. */ 912 | int handle = open(VendorID, ProductID, L"12345"); 913 | if (handle < 0) 914 | printf("unable to open device\n"); 915 | 916 | 917 | /* Toggle LED (cmd 0x80) */ 918 | buf[1] = 0x80; 919 | res = write(handle, buf, 65); 920 | if (res < 0) 921 | printf("Unable to write()\n"); 922 | 923 | /* Request state (cmd 0x81) */ 924 | buf[1] = 0x81; 925 | write(handle, buf, 65); 926 | if (res < 0) 927 | printf("Unable to write() (2)\n"); 928 | 929 | /* Read requested state */ 930 | read(handle, buf, 65); 931 | if (res < 0) 932 | printf("Unable to read()\n"); 933 | 934 | /* Print out the returned buffer. */ 935 | for (int i = 0; i < 4; i++) 936 | printf("buf[%d]: %d\n", i, buf[i]); 937 | 938 | return 0; 939 | } 940 | #endif 941 | 942 | #ifdef __cplusplus 943 | } /* extern "C" */ 944 | #endif 945 | -------------------------------------------------------------------------------- /windows/hidapi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 10.00 3 | # Visual C++ Express 2008 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hidapi", "hidapi.vcproj", "{A107C21C-418A-4697-BB10-20C3AA60E2E4}" 5 | EndProject 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hidtest", "hidtest.vcproj", "{23E9FF6A-49D1-4993-B2B5-BBB992C6C712}" 7 | ProjectSection(ProjectDependencies) = postProject 8 | {A107C21C-418A-4697-BB10-20C3AA60E2E4} = {A107C21C-418A-4697-BB10-20C3AA60E2E4} 9 | EndProjectSection 10 | EndProject 11 | Global 12 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 13 | Debug|Win32 = Debug|Win32 14 | Release|Win32 = Release|Win32 15 | EndGlobalSection 16 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 17 | {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug|Win32.ActiveCfg = Debug|Win32 18 | {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug|Win32.Build.0 = Debug|Win32 19 | {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release|Win32.ActiveCfg = Release|Win32 20 | {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release|Win32.Build.0 = Release|Win32 21 | {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Debug|Win32.ActiveCfg = Debug|Win32 22 | {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Debug|Win32.Build.0 = Debug|Win32 23 | {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Release|Win32.ActiveCfg = Release|Win32 24 | {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Release|Win32.Build.0 = Release|Win32 25 | EndGlobalSection 26 | GlobalSection(SolutionProperties) = preSolution 27 | HideSolutionNode = FALSE 28 | EndGlobalSection 29 | EndGlobal 30 | -------------------------------------------------------------------------------- /windows/hidapi.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 26 | 29 | 32 | 35 | 38 | 41 | 53 | 56 | 59 | 62 | 70 | 73 | 76 | 79 | 82 | 85 | 88 | 91 | 92 | 100 | 103 | 106 | 109 | 112 | 115 | 127 | 130 | 133 | 136 | 146 | 149 | 152 | 155 | 158 | 161 | 164 | 167 | 168 | 169 | 170 | 171 | 172 | 177 | 180 | 181 | 182 | 187 | 190 | 191 | 192 | 197 | 198 | 199 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /windows/hidtest.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 25 | 28 | 31 | 34 | 37 | 40 | 50 | 53 | 56 | 59 | 67 | 70 | 73 | 76 | 79 | 82 | 85 | 90 | 91 | 99 | 102 | 105 | 108 | 111 | 114 | 124 | 127 | 130 | 133 | 143 | 146 | 149 | 152 | 155 | 158 | 161 | 166 | 167 | 168 | 169 | 170 | 171 | 176 | 179 | 180 | 181 | 186 | 187 | 192 | 193 | 194 | 195 | 196 | 197 | --------------------------------------------------------------------------------