| { | ||
| // Use IntelliSense to learn about possible attributes. | ||
| // Hover to view descriptions of existing attributes. | ||
| // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||
| "version": "0.2.0", | ||
| "configurations": [ | ||
| { | ||
| "type": "lldb", | ||
| "request": "launch", | ||
| "name": "Debug unit tests in library 'hidapi'", | ||
| "cargo": { | ||
| "args": [ | ||
| "test", | ||
| "--no-run", | ||
| "--lib", | ||
| "--package=hidapi" | ||
| ], | ||
| "filter": { | ||
| "name": "hidapi", | ||
| "kind": "lib" | ||
| } | ||
| }, | ||
| "args": [], | ||
| "cwd": "${workspaceFolder}" | ||
| } | ||
| ] | ||
| } |
| { | ||
| // See https://go.microsoft.com/fwlink/?LinkId=733558 | ||
| // for the documentation about the tasks.json format | ||
| "version": "2.0.0", | ||
| "tasks": [ | ||
| { | ||
| "label": "Check", | ||
| "type": "shell", | ||
| "command": "echo Hello" | ||
| }, | ||
| { | ||
| "type": "cargo", | ||
| "subcommand": "check", | ||
| "problemMatcher": [ | ||
| "$rustc" | ||
| ], | ||
| "group": { | ||
| "kind": "build", | ||
| "isDefault": true | ||
| } | ||
| } | ||
| ] | ||
| } |
| os: Visual Studio 2015 | ||
| environment: | ||
| matrix: | ||
| - BUILD_ENV: msbuild | ||
| arch: x64 | ||
| - BUILD_ENV: msbuild | ||
| arch: Win32 | ||
| - BUILD_ENV: cygwin | ||
| install: | ||
| - cmd: if %BUILD_ENV%==cygwin ( | ||
| C:\cygwin64\setup-x86_64.exe --quiet-mode --no-shortcuts --upgrade-also --packages autoconf,automake ) | ||
| build_script: | ||
| - cmd: if %BUILD_ENV%==msbuild ( | ||
| msbuild .\windows\hidapi.sln /p:Configuration=Release /p:Platform=%arch% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" ) | ||
| - cmd: if %BUILD_ENV%==cygwin ( | ||
| C:\cygwin64\bin\bash -exlc "cd $APPVEYOR_BUILD_FOLDER; ./bootstrap; ./configure; make" ) | ||
| artifacts: | ||
| # Win32 artifacts | ||
| - path: .\windows\Release\hidapi.dll | ||
| - path: .\windows\Release\hidapi.lib | ||
| - path: .\windows\Release\hidapi.pdb | ||
| - path: .\windows\Release\hidtest.exe | ||
| - path: .\windows\Release\hidtest.pdb | ||
| # x64 artifacts | ||
| - path: .\windows\x64\Release\hidapi.dll | ||
| - path: .\windows\x64\Release\hidapi.lib | ||
| - path: .\windows\x64\Release\hidapi.pdb | ||
| - path: .\windows\x64\Release\hidtest.exe | ||
| - path: .\windows\x64\Release\hidtest.pdb |
| image: alpine/edge | ||
| packages: | ||
| - autoconf | ||
| - automake | ||
| - libtool | ||
| - eudev-dev | ||
| - libusb-dev | ||
| - linux-headers | ||
| sources: | ||
| - https://github.com/libusb/hidapi | ||
| tasks: | ||
| - setup: | | ||
| cd hidapi | ||
| ./bootstrap | ||
| ./configure | ||
| - build: | | ||
| cd hidapi | ||
| make | ||
| make DESTDIR=$PWD/root install | ||
| make clean | ||
| - build-manual: | | ||
| cd hidapi/linux | ||
| make -f Makefile-manual | ||
| cd ../libusb | ||
| make -f Makefile-manual |
| image: archlinux | ||
| sources: | ||
| - https://github.com/libusb/hidapi | ||
| tasks: | ||
| - setup: | | ||
| cd hidapi | ||
| ./bootstrap | ||
| ./configure | ||
| - build: | | ||
| cd hidapi | ||
| make | ||
| make DESTDIR=$PWD/root install | ||
| make clean | ||
| - build-manual: | | ||
| cd hidapi/linux | ||
| make -f Makefile-manual | ||
| cd ../libusb | ||
| make -f Makefile-manual |
| image: fedora/latest | ||
| packages: | ||
| - autoconf | ||
| - automake | ||
| - libtool | ||
| - mingw64-gcc | ||
| - mingw64-gcc-c++ | ||
| sources: | ||
| - https://github.com/libusb/hidapi | ||
| tasks: | ||
| - setup: | | ||
| cd hidapi | ||
| ./bootstrap | ||
| mingw64-configure | ||
| - build: | | ||
| cd hidapi | ||
| make | ||
| make DESTDIR=$PWD/root install | ||
| make clean | ||
| - build-manual: | | ||
| cd hidapi/windows | ||
| make -f Makefile-manual OS=MINGW CC=x86_64-w64-mingw32-gcc |
| image: freebsd/latest | ||
| packages: | ||
| - autoconf | ||
| - automake | ||
| - gmake | ||
| - libiconv | ||
| - libtool | ||
| - pkgconf | ||
| sources: | ||
| - https://github.com/libusb/hidapi | ||
| tasks: | ||
| - setup: | | ||
| cd hidapi | ||
| ./bootstrap | ||
| ./configure | ||
| - build: | | ||
| cd hidapi | ||
| make | ||
| make DESTDIR=$PWD/root install | ||
| make clean | ||
| - build-manual: | | ||
| cd hidapi/libusb | ||
| gmake -f Makefile-manual |
| alpine_task: | ||
| container: | ||
| image: alpine:latest | ||
| install_script: apk add autoconf automake g++ gcc libusb-dev libtool linux-headers eudev-dev make musl-dev | ||
| script: | ||
| - ./bootstrap | ||
| - ./configure || { cat config.log; exit 1; } | ||
| - make | ||
| - make install | ||
| freebsd11_task: | ||
| freebsd_instance: | ||
| image: freebsd-11-2-release-amd64 | ||
| install_script: | ||
| - pkg install -y | ||
| autoconf automake libiconv libtool pkgconf | ||
| script: | ||
| - ./bootstrap | ||
| - ./configure || { cat config.log; exit 1; } | ||
| - make | ||
| - make install | ||
| freebsd12_task: | ||
| freebsd_instance: | ||
| image: freebsd-12-1-release-amd64 | ||
| install_script: | ||
| - pkg install -y | ||
| autoconf automake libiconv libtool pkgconf | ||
| script: | ||
| - ./bootstrap | ||
| - ./configure || { cat config.log; exit 1; } | ||
| - make | ||
| - make install |
| language: c | ||
| os: osx | ||
| osx_image: xcode10.2 | ||
| script: | ||
| - ./bootstrap | ||
| - ./configure | ||
| - make | ||
| - make DESTDIR=$PWD/root install | ||
| - make clean | ||
| - cd mac | ||
| - make -f Makefile-manual |
Sorry, the diff of this file is not supported yet
| /******************************************************* | ||
| Windows HID simplification | ||
| Alan Ott | ||
| Signal 11 Software | ||
| 8/22/2009 | ||
| Copyright 2009 | ||
| This contents of this file may be used by anyone | ||
| for any reason without any conditions and may be | ||
| used as a starting point for your own applications | ||
| which use HIDAPI. | ||
| ********************************************************/ | ||
| #include <stdio.h> | ||
| #include <wchar.h> | ||
| #include <string.h> | ||
| #include <stdlib.h> | ||
| #include "hidapi.h" | ||
| // Headers needed for sleeping. | ||
| #ifdef _WIN32 | ||
| #include <windows.h> | ||
| #else | ||
| #include <unistd.h> | ||
| #endif | ||
| int main(int argc, char* argv[]) | ||
| { | ||
| int res; | ||
| unsigned char buf[256]; | ||
| #define MAX_STR 255 | ||
| wchar_t wstr[MAX_STR]; | ||
| hid_device *handle; | ||
| int i; | ||
| #ifdef WIN32 | ||
| UNREFERENCED_PARAMETER(argc); | ||
| UNREFERENCED_PARAMETER(argv); | ||
| #endif | ||
| struct hid_device_info *devs, *cur_dev; | ||
| if (hid_init()) | ||
| return -1; | ||
| devs = hid_enumerate(0x0, 0x0); | ||
| cur_dev = devs; | ||
| while (cur_dev) { | ||
| printf("Device Found\n type: %04hx %04hx\n path: %s\n serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number); | ||
| printf("\n"); | ||
| printf(" Manufacturer: %ls\n", cur_dev->manufacturer_string); | ||
| printf(" Product: %ls\n", cur_dev->product_string); | ||
| printf(" Release: %hx\n", cur_dev->release_number); | ||
| printf(" Interface: %d\n", cur_dev->interface_number); | ||
| printf(" Usage (page): 0x%hx (0x%hx)\n", cur_dev->usage, cur_dev->usage_page); | ||
| printf("\n"); | ||
| cur_dev = cur_dev->next; | ||
| } | ||
| hid_free_enumeration(devs); | ||
| // Set up the command buffer. | ||
| memset(buf,0x00,sizeof(buf)); | ||
| buf[0] = 0x01; | ||
| buf[1] = 0x81; | ||
| // Open the device using the VID, PID, | ||
| // and optionally the Serial number. | ||
| ////handle = hid_open(0x4d8, 0x3f, L"12345"); | ||
| handle = hid_open(0x4d8, 0x3f, NULL); | ||
| if (!handle) { | ||
| printf("unable to open device\n"); | ||
| return 1; | ||
| } | ||
| // Read the Manufacturer String | ||
| wstr[0] = 0x0000; | ||
| res = hid_get_manufacturer_string(handle, wstr, MAX_STR); | ||
| if (res < 0) | ||
| printf("Unable to read manufacturer string\n"); | ||
| printf("Manufacturer String: %ls\n", wstr); | ||
| // Read the Product String | ||
| wstr[0] = 0x0000; | ||
| res = hid_get_product_string(handle, wstr, MAX_STR); | ||
| if (res < 0) | ||
| printf("Unable to read product string\n"); | ||
| printf("Product String: %ls\n", wstr); | ||
| // Read the Serial Number String | ||
| wstr[0] = 0x0000; | ||
| res = hid_get_serial_number_string(handle, wstr, MAX_STR); | ||
| if (res < 0) | ||
| printf("Unable to read serial number string\n"); | ||
| printf("Serial Number String: (%d) %ls", wstr[0], wstr); | ||
| printf("\n"); | ||
| // Read Indexed String 1 | ||
| wstr[0] = 0x0000; | ||
| res = hid_get_indexed_string(handle, 1, wstr, MAX_STR); | ||
| if (res < 0) | ||
| printf("Unable to read indexed string 1\n"); | ||
| printf("Indexed String 1: %ls\n", wstr); | ||
| // Set the hid_read() function to be non-blocking. | ||
| hid_set_nonblocking(handle, 1); | ||
| // Try to read from the device. There should be no | ||
| // data here, but execution should not block. | ||
| res = hid_read(handle, buf, 17); | ||
| // Send a Feature Report to the device | ||
| buf[0] = 0x2; | ||
| buf[1] = 0xa0; | ||
| buf[2] = 0x0a; | ||
| buf[3] = 0x00; | ||
| buf[4] = 0x00; | ||
| res = hid_send_feature_report(handle, buf, 17); | ||
| if (res < 0) { | ||
| printf("Unable to send a feature report.\n"); | ||
| } | ||
| memset(buf,0,sizeof(buf)); | ||
| // Read a Feature Report from the device | ||
| buf[0] = 0x2; | ||
| res = hid_get_feature_report(handle, buf, sizeof(buf)); | ||
| if (res < 0) { | ||
| printf("Unable to get a feature report.\n"); | ||
| printf("%ls", hid_error(handle)); | ||
| } | ||
| else { | ||
| // Print out the returned buffer. | ||
| printf("Feature Report\n "); | ||
| for (i = 0; i < res; i++) | ||
| printf("%02hhx ", buf[i]); | ||
| printf("\n"); | ||
| } | ||
| memset(buf,0,sizeof(buf)); | ||
| // Toggle LED (cmd 0x80). The first byte is the report number (0x1). | ||
| buf[0] = 0x1; | ||
| buf[1] = 0x80; | ||
| res = hid_write(handle, buf, 17); | ||
| if (res < 0) { | ||
| printf("Unable to write()\n"); | ||
| printf("Error: %ls\n", hid_error(handle)); | ||
| } | ||
| // Request state (cmd 0x81). The first byte is the report number (0x1). | ||
| buf[0] = 0x1; | ||
| buf[1] = 0x81; | ||
| hid_write(handle, buf, 17); | ||
| if (res < 0) | ||
| printf("Unable to write() (2)\n"); | ||
| // Read requested state. hid_read() has been set to be | ||
| // non-blocking by the call to hid_set_nonblocking() above. | ||
| // This loop demonstrates the non-blocking nature of hid_read(). | ||
| res = 0; | ||
| while (res == 0) { | ||
| res = hid_read(handle, buf, sizeof(buf)); | ||
| if (res == 0) | ||
| printf("waiting...\n"); | ||
| if (res < 0) | ||
| printf("Unable to read()\n"); | ||
| #ifdef WIN32 | ||
| Sleep(500); | ||
| #else | ||
| usleep(500*1000); | ||
| #endif | ||
| } | ||
| printf("Data read:\n "); | ||
| // Print out the returned buffer. | ||
| for (i = 0; i < res; i++) | ||
| printf("%02hhx ", buf[i]); | ||
| printf("\n"); | ||
| hid_close(handle); | ||
| /* Free static HIDAPI objects. */ | ||
| hid_exit(); | ||
| #ifdef WIN32 | ||
| system("pause"); | ||
| #endif | ||
| return 0; | ||
| } |
| ## HIDAPI library for Windows, Linux, FreeBSD and macOS | ||
| | CI instance | Status | | ||
| |----------------------|--------| | ||
| | `macOS master` | [](https://travis-ci.org/libusb/hidapi) | | ||
| | `Windows master` | [](https://ci.appveyor.com/project/Youw/hidapi/branch/master) | | ||
| | `Linux/BSD, last build (branch/PR)` | [](https://builds.sr.ht/~qbicz/hidapi?) | | ||
| HIDAPI is a multi-platform library which allows an application to interface | ||
| with USB and Bluetooth HID-Class devices on Windows, Linux, FreeBSD, and macOS. | ||
| HIDAPI can be either built as a shared library (`.so`, `.dll` or `.dylib`) or | ||
| can be embedded directly into a target application by adding a single source | ||
| file (per platform) and a single header. | ||
| HIDAPI library was originally developed by Alan Ott ([signal11](https://github.com/signal11)). | ||
| It was moved to [libusb/hidapi](https://github.com/libusb/hidapi) on June 4th, 2019, in order to merge important bugfixes and continue development of the library. | ||
| ## Table of Contents | ||
| * [About](#about) | ||
| * [What Does the API Look Like?](#what-does-the-api-look-like) | ||
| * [License](#license) | ||
| * [Download](#download) | ||
| * [Build Instructions](#build-instructions) | ||
| * [Prerequisites](#prerequisites) | ||
| * [Linux](#linux) | ||
| * [FreeBSD](#freebsd) | ||
| * [Mac](#mac) | ||
| * [Windows](#windows) | ||
| * [Building HIDAPI into a shared library on Unix Platforms](#building-hidapi-into-a-shared-library-on-unix-platforms) | ||
| * [Building the manual way on Unix platforms](#building-the-manual-way-on-unix-platforms) | ||
| * [Building on Windows](#building-on-windows) | ||
| * [Cross Compiling](#cross-compiling) | ||
| * [Prerequisites](#prerequisites-1) | ||
| * [Building HIDAPI](#building-hidapi) | ||
| ## About | ||
| HIDAPI has five back-ends: | ||
| * Windows (using `hid.dll`) | ||
| * Linux/hidraw (using the Kernel's hidraw driver) | ||
| * Linux/libusb (using libusb-1.0) | ||
| * FreeBSD (using libusb-1.0) | ||
| * Mac (using IOHidManager) | ||
| On Linux, either the hidraw or the libusb back-end can be used. There are | ||
| tradeoffs, and the functionality supported is slightly different. | ||
| __Linux/hidraw__ (`linux/hid.c`): | ||
| This back-end uses the hidraw interface in the Linux kernel, and supports | ||
| both USB and Bluetooth HID devices. It requires kernel version at least 2.6.39 | ||
| to build. In addition, it will only communicate with devices which have hidraw | ||
| nodes associated with them. | ||
| Keyboards, mice, and some other devices which are blacklisted from having | ||
| hidraw nodes will not work. Fortunately, for nearly all the uses of hidraw, | ||
| this is not a problem. | ||
| __Linux/FreeBSD/libusb__ (`libusb/hid.c`): | ||
| This back-end uses libusb-1.0 to communicate directly to a USB device. This | ||
| back-end will of course not work with Bluetooth devices. | ||
| HIDAPI also comes with a Test GUI. The Test GUI is cross-platform and uses | ||
| Fox Toolkit <http://www.fox-toolkit.org>. It will build on every platform | ||
| which HIDAPI supports. Since it relies on a 3rd party library, building it | ||
| is optional but recommended because it is so useful when debugging hardware. | ||
| ## What Does the API Look Like? | ||
| The API provides the most commonly used HID functions including sending | ||
| and receiving of input, output, and feature reports. The sample program, | ||
| which communicates with a heavily hacked up version of the Microchip USB | ||
| Generic HID sample looks like this (with error checking removed for | ||
| simplicity): | ||
| **Warning: Only run the code you understand, and only when it conforms to the | ||
| device spec. Writing data at random to your HID devices can break them.** | ||
| ```c | ||
| #ifdef WIN32 | ||
| #include <windows.h> | ||
| #endif | ||
| #include <stdio.h> | ||
| #include <stdlib.h> | ||
| #include "hidapi.h" | ||
| #define MAX_STR 255 | ||
| int main(int argc, char* argv[]) | ||
| { | ||
| int res; | ||
| unsigned char buf[65]; | ||
| wchar_t wstr[MAX_STR]; | ||
| hid_device *handle; | ||
| int i; | ||
| // Initialize the hidapi library | ||
| res = hid_init(); | ||
| // Open the device using the VID, PID, | ||
| // and optionally the Serial number. | ||
| handle = hid_open(0x4d8, 0x3f, NULL); | ||
| // Read the Manufacturer String | ||
| res = hid_get_manufacturer_string(handle, wstr, MAX_STR); | ||
| wprintf(L"Manufacturer String: %s\n", wstr); | ||
| // Read the Product String | ||
| res = hid_get_product_string(handle, wstr, MAX_STR); | ||
| wprintf(L"Product String: %s\n", wstr); | ||
| // Read the Serial Number String | ||
| res = hid_get_serial_number_string(handle, wstr, MAX_STR); | ||
| wprintf(L"Serial Number String: (%d) %s\n", wstr[0], wstr); | ||
| // Read Indexed String 1 | ||
| res = hid_get_indexed_string(handle, 1, wstr, MAX_STR); | ||
| wprintf(L"Indexed String 1: %s\n", wstr); | ||
| // Toggle LED (cmd 0x80). The first byte is the report number (0x0). | ||
| buf[0] = 0x0; | ||
| buf[1] = 0x80; | ||
| res = hid_write(handle, buf, 65); | ||
| // Request state (cmd 0x81). The first byte is the report number (0x0). | ||
| buf[0] = 0x0; | ||
| buf[1] = 0x81; | ||
| res = hid_write(handle, buf, 65); | ||
| // Read requested state | ||
| res = hid_read(handle, buf, 65); | ||
| // Print out the returned buffer. | ||
| for (i = 0; i < 4; i++) | ||
| printf("buf[%d]: %d\n", i, buf[i]); | ||
| // Close the device | ||
| hid_close(handle); | ||
| // Finalize the hidapi library | ||
| res = hid_exit(); | ||
| return 0; | ||
| } | ||
| ``` | ||
| You can also use [hidtest/test.c](hidtest/test.c) | ||
| as a starting point for your applications. | ||
| ## License | ||
| HIDAPI may be used by one of three licenses as outlined in [LICENSE.txt](LICENSE.txt). | ||
| ## Download | ||
| HIDAPI can be downloaded from GitHub | ||
| ```sh | ||
| git clone git://github.com/libusb/hidapi.git | ||
| ``` | ||
| ## Build Instructions | ||
| This section is long. Don't be put off by this. It's not long because it's | ||
| complicated to build HIDAPI; it's quite the opposite. This section is long | ||
| because of the flexibility of HIDAPI and the large number of ways in which | ||
| it can be built and used. You will likely pick a single build method. | ||
| HIDAPI can be built in several different ways. If you elect to build a | ||
| shared library, you will need to build it from the HIDAPI source | ||
| distribution. If you choose instead to embed HIDAPI directly into your | ||
| application, you can skip the building and look at the provided platform | ||
| Makefiles for guidance. These platform Makefiles are located in `linux/`, | ||
| `libusb/`, `mac/` and `windows/` and are called `Makefile-manual`. In addition, | ||
| Visual Studio projects are provided. Even if you're going to embed HIDAPI | ||
| into your project, it is still beneficial to build the example programs. | ||
| ### Prerequisites: | ||
| #### Linux: | ||
| On Linux, you will need to install development packages for libudev, | ||
| libusb and optionally Fox-toolkit (for the test GUI). On | ||
| Debian/Ubuntu systems these can be installed by running: | ||
| ```sh | ||
| sudo apt-get install libudev-dev libusb-1.0-0-dev libfox-1.6-dev | ||
| ``` | ||
| If you downloaded the source directly from the git repository (using | ||
| git clone), you'll need Autotools: | ||
| ```sh | ||
| sudo apt-get install autotools-dev autoconf automake libtool | ||
| ``` | ||
| #### FreeBSD: | ||
| On FreeBSD you will need to install GNU make, libiconv, and | ||
| optionally Fox-Toolkit (for the test GUI). This is done by running | ||
| the following: | ||
| ```sh | ||
| pkg_add -r gmake libiconv fox16 | ||
| ``` | ||
| If you downloaded the source directly from the git repository (using | ||
| git clone), you'll need Autotools: | ||
| ```sh | ||
| pkg_add -r autotools | ||
| ``` | ||
| #### Mac: | ||
| On Mac, you will need to install Fox-Toolkit if you wish to build | ||
| the Test GUI. There are two ways to do this, and each has a slight | ||
| complication. Which method you use depends on your use case. | ||
| If you wish to build the Test GUI just for your own testing on your | ||
| own computer, then the easiest method is to install Fox-Toolkit | ||
| using ports: | ||
| ```sh | ||
| sudo port install fox | ||
| ``` | ||
| If you wish to build the TestGUI app bundle to redistribute to | ||
| others, you will need to install Fox-toolkit from source. This is | ||
| because the version of fox that gets installed using ports uses the | ||
| ports X11 libraries which are not compatible with the Apple X11 | ||
| libraries. If you install Fox with ports and then try to distribute | ||
| your built app bundle, it will simply fail to run on other systems. | ||
| To install Fox-Toolkit manually, download the source package from | ||
| <http://www.fox-toolkit.org>, extract it, and run the following from | ||
| within the extracted source: | ||
| ```sh | ||
| ./configure && make && make install | ||
| ``` | ||
| #### Windows: | ||
| On Windows, if you want to build the test GUI, you will need to get | ||
| the `hidapi-externals.zip` package from the download site. This | ||
| contains pre-built binaries for Fox-toolkit. Extract | ||
| `hidapi-externals.zip` just outside of hidapi, so that | ||
| hidapi-externals and hidapi are on the same level, as shown: | ||
| ``` | ||
| Parent_Folder | ||
| | | ||
| +hidapi | ||
| +hidapi-externals | ||
| ``` | ||
| Again, this step is not required if you do not wish to build the | ||
| test GUI. | ||
| ### Building HIDAPI into a shared library on Unix Platforms: | ||
| On Unix-like systems such as Linux, FreeBSD, macOS, and even Windows, using | ||
| MinGW or Cygwin, the easiest way to build a standard system-installed shared | ||
| library is to use the GNU Autotools build system. If you checked out the | ||
| source from the git repository, run the following: | ||
| ```sh | ||
| ./bootstrap | ||
| ./configure | ||
| make | ||
| make install # as root, or using sudo | ||
| ``` | ||
| If you downloaded a source package (i.e.: if you did not run git clone), you | ||
| can skip the `./bootstrap` step. | ||
| `./configure` can take several arguments which control the build. The two most | ||
| likely to be used are: | ||
| ```sh | ||
| --enable-testgui | ||
| Enable build of the Test GUI. This requires Fox toolkit to | ||
| be installed. Instructions for installing Fox-Toolkit on | ||
| each platform are in the Prerequisites section above. | ||
| --prefix=/usr | ||
| Specify where you want the output headers and libraries to | ||
| be installed. The example above will put the headers in | ||
| /usr/include and the binaries in /usr/lib. The default is to | ||
| install into /usr/local which is fine on most systems. | ||
| ``` | ||
| ### Building the manual way on Unix platforms: | ||
| Manual Makefiles are provided mostly to give the user and idea what it takes | ||
| to build a program which embeds HIDAPI directly inside of it. These should | ||
| really be used as examples only. If you want to build a system-wide shared | ||
| library, use the Autotools method described above. | ||
| To build HIDAPI using the manual Makefiles, change to the directory | ||
| of your platform and run make. For example, on Linux run: | ||
| ```sh | ||
| cd linux/ | ||
| make -f Makefile-manual | ||
| ``` | ||
| To build the Test GUI using the manual makefiles: | ||
| ```sh | ||
| cd testgui/ | ||
| make -f Makefile-manual | ||
| ``` | ||
| ### Building on Windows: | ||
| To build the HIDAPI DLL on Windows using Visual Studio, build the `.sln` file | ||
| in the `windows/` directory. | ||
| To build the Test GUI on windows using Visual Studio, build the `.sln` file in | ||
| the `testgui/` directory. | ||
| To build HIDAPI using MinGW or Cygwin using Autotools, use the instructions | ||
| in the section [Building HIDAPI into a shared library on Unix Platforms](#building-hidapi-into-a-shared-library-on-unix-platforms) | ||
| above. Note that building the Test GUI with MinGW or Cygwin will | ||
| require the Windows procedure in the [Prerequisites](#prerequisites-1) section | ||
| above (i.e.: `hidapi-externals.zip`). | ||
| To build HIDAPI using MinGW using the Manual Makefiles, see the section | ||
| [Building the manual way on Unix platforms](#building-the-manual-way-on-unix-platforms) | ||
| above. | ||
| HIDAPI can also be built using the Windows DDK (now also called the Windows | ||
| Driver Kit or WDK). This method was originally required for the HIDAPI build | ||
| but not anymore. However, some users still prefer this method. It is not as | ||
| well supported anymore but should still work. Patches are welcome if it does | ||
| not. To build using the DDK: | ||
| 1. Install the Windows Driver Kit (WDK) from Microsoft. | ||
| 2. From the Start menu, in the Windows Driver Kits folder, select Build | ||
| Environments, then your operating system, then the x86 Free Build | ||
| Environment (or one that is appropriate for your system). | ||
| 3. From the console, change directory to the `windows/ddk_build/` directory, | ||
| which is part of the HIDAPI distribution. | ||
| 4. Type build. | ||
| 5. You can find the output files (DLL and LIB) in a subdirectory created | ||
| by the build system which is appropriate for your environment. On | ||
| Windows XP, this directory is `objfre_wxp_x86/i386`. | ||
| ## Cross Compiling | ||
| This section talks about cross compiling HIDAPI for Linux using Autotools. | ||
| This is useful for using HIDAPI on embedded Linux targets. These | ||
| instructions assume the most raw kind of embedded Linux build, where all | ||
| prerequisites will need to be built first. This process will of course vary | ||
| based on your embedded Linux build system if you are using one, such as | ||
| OpenEmbedded or Buildroot. | ||
| For the purpose of this section, it will be assumed that the following | ||
| environment variables are exported. | ||
| ```sh | ||
| $ export STAGING=$HOME/out | ||
| $ export HOST=arm-linux | ||
| ``` | ||
| `STAGING` and `HOST` can be modified to suit your setup. | ||
| ### Prerequisites | ||
| Note that the build of libudev is the very basic configuration. | ||
| Build libusb. From the libusb source directory, run: | ||
| ```sh | ||
| ./configure --host=$HOST --prefix=$STAGING | ||
| make | ||
| make install | ||
| ``` | ||
| Build libudev. From the libudev source directory, run: | ||
| ```sh | ||
| ./configure --disable-gudev --disable-introspection --disable-hwdb \ | ||
| --host=$HOST --prefix=$STAGING | ||
| make | ||
| make install | ||
| ``` | ||
| ### Building HIDAPI | ||
| Build HIDAPI: | ||
| ``` | ||
| PKG_CONFIG_DIR= \ | ||
| PKG_CONFIG_LIBDIR=$STAGING/lib/pkgconfig:$STAGING/share/pkgconfig \ | ||
| PKG_CONFIG_SYSROOT_DIR=$STAGING \ | ||
| ./configure --host=$HOST --prefix=$STAGING | ||
| ``` |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
+1
-1
| [submodule "etc/hidapi"] | ||
| path = etc/hidapi | ||
| url = https://github.com/signal11/hidapi.git | ||
| url = https://github.com/libusb/hidapi.git |
+10
-10
@@ -5,3 +5,3 @@ # This file is automatically @generated by Cargo. | ||
| name = "cc" | ||
| version = "1.0.47" | ||
| version = "1.0.36" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
@@ -11,7 +11,7 @@ | ||
| name = "hidapi" | ||
| version = "1.1.0" | ||
| version = "1.1.1" | ||
| dependencies = [ | ||
| "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", | ||
| "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", | ||
| "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", | ||
| "cc 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)", | ||
| "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", | ||
| "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", | ||
| ] | ||
@@ -21,3 +21,3 @@ | ||
| name = "libc" | ||
| version = "0.2.66" | ||
| version = "0.2.54" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
@@ -27,8 +27,8 @@ | ||
| name = "pkg-config" | ||
| version = "0.3.17" | ||
| version = "0.3.14" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| [metadata] | ||
| "checksum cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)" = "aa87058dce70a3ff5621797f1506cb837edd02ac4c0ae642b4542dce802908b8" | ||
| "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" | ||
| "checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" | ||
| "checksum cc 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)" = "a0c56216487bb80eec9c4516337b2588a4f2a2290d72a1416d930e4dcdb0c90d" | ||
| "checksum libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)" = "c6785aa7dd976f5fbf3b71cfd9cd49d7f783c1ff565a858d71031c6c313aa5c6" | ||
| "checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" |
+1
-1
@@ -15,3 +15,3 @@ # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO | ||
| name = "hidapi" | ||
| version = "1.1.0" | ||
| version = "1.1.1" | ||
| authors = ["Roland Ruckerbauer <roland.rucky@gmail.com>", "Osspial <osspial@gmail.com>", "Artyom Pavlov <newpavlov@gmail.com>", "mberndt123", "niklasad1"] | ||
@@ -18,0 +18,0 @@ build = "build.rs" |
@@ -5,5 +5,7 @@ | ||
| aclocal.m4 | ||
| ar-lib | ||
| autom4te.cache/ | ||
| config.* | ||
| configure | ||
| compile | ||
| depcomp | ||
@@ -10,0 +12,0 @@ install-sh |
@@ -13,5 +13,7 @@ | ||
| libusb/hidapi Team: | ||
| Development/maintainance since June 4th 2019 | ||
| For a comprehensive list of contributions, see the commit list at github: | ||
| http://github.com/signal11/hidapi/commits/master | ||
| https://github.com/libusb/hidapi/commits/master | ||
@@ -20,3 +20,3 @@ /******************************************************* | ||
| code repository located at: | ||
| http://github.com/signal11/hidapi . | ||
| https://github.com/libusb/hidapi . | ||
| ********************************************************/ | ||
@@ -73,5 +73,9 @@ | ||
| /** The USB interface which this logical device | ||
| represents. Valid on both Linux implementations | ||
| in all cases, and valid on the Windows implementation | ||
| only if the device contains more than one interface. */ | ||
| represents. | ||
| * Valid on both Linux implementations in all cases. | ||
| * Valid on the Windows implementation only if the device | ||
| contains more than one interface. | ||
| * Valid on the Mac implementation if and only if the device | ||
| is a USB HID device. */ | ||
| int interface_number; | ||
@@ -130,3 +134,3 @@ | ||
| This function returns a pointer to a linked list of type | ||
| struct #hid_device, containing information about the HID devices | ||
| struct #hid_device_info, containing information about the HID devices | ||
| attached to the system, or NULL in the case of failure. Free | ||
@@ -153,2 +157,4 @@ this linked list by calling hid_free_enumeration(). | ||
| This function sets the return value of hid_error(). | ||
| @ingroup API | ||
@@ -172,2 +178,4 @@ @param vendor_id The Vendor ID (VID) of the device to open. | ||
| This function sets the return value of hid_error(). | ||
| @ingroup API | ||
@@ -198,4 +206,6 @@ @param path The path name of the device to open | ||
| This function sets the return value of hid_error(). | ||
| @ingroup API | ||
| @param device A device handle returned from hid_open(). | ||
| @param dev A device handle returned from hid_open(). | ||
| @param data The data to send, including the report number as | ||
@@ -209,3 +219,3 @@ the first byte. | ||
| */ | ||
| int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length); | ||
| int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length); | ||
@@ -218,4 +228,6 @@ /** @brief Read an Input report from a HID device with timeout. | ||
| This function sets the return value of hid_error(). | ||
| @ingroup API | ||
| @param device A device handle returned from hid_open(). | ||
| @param dev A device handle returned from hid_open(). | ||
| @param data A buffer to put the read data into. | ||
@@ -240,4 +252,6 @@ @param length The number of bytes to read. For devices with | ||
| This function sets the return value of hid_error(). | ||
| @ingroup API | ||
| @param device A device handle returned from hid_open(). | ||
| @param dev A device handle returned from hid_open(). | ||
| @param data A buffer to put the read data into. | ||
@@ -253,3 +267,3 @@ @param length The number of bytes to read. For devices with | ||
| */ | ||
| int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length); | ||
| int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length); | ||
@@ -266,3 +280,3 @@ /** @brief Set the device handle to be non-blocking. | ||
| @ingroup API | ||
| @param device A device handle returned from hid_open(). | ||
| @param dev A device handle returned from hid_open(). | ||
| @param nonblock enable or not the nonblocking reads | ||
@@ -275,3 +289,3 @@ - 1 to enable nonblocking | ||
| */ | ||
| int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock); | ||
| int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock); | ||
@@ -293,4 +307,6 @@ /** @brief Send a Feature report to the device. | ||
| This function sets the return value of hid_error(). | ||
| @ingroup API | ||
| @param device A device handle returned from hid_open(). | ||
| @param dev A device handle returned from hid_open(). | ||
| @param data The data to send, including the report number as | ||
@@ -305,3 +321,3 @@ the first byte. | ||
| */ | ||
| int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length); | ||
| int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length); | ||
@@ -316,3 +332,30 @@ /** @brief Get a feature report from a HID device. | ||
| This function sets the return value of hid_error(). | ||
| @ingroup API | ||
| @param dev A device handle returned from hid_open(). | ||
| @param data A buffer to put the read data into, including | ||
| the Report ID. Set the first byte of @p data[] to the | ||
| Report ID of the report to be read, or set it to zero | ||
| if your device does not use numbered reports. | ||
| @param length The number of bytes to read, including an | ||
| extra byte for the report ID. The buffer can be longer | ||
| than the actual report. | ||
| @returns | ||
| This function returns the number of bytes read plus | ||
| one for the report ID (which is still in the first | ||
| byte), or -1 on error. | ||
| */ | ||
| int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length); | ||
| /** @brief Get a input report from a HID device. | ||
| Set the first byte of @p data[] to the Report ID of the | ||
| report to be read. Make sure to allow space for this | ||
| extra byte in @p data[]. Upon return, the first byte will | ||
| still contain the Report ID, and the report data will | ||
| start in data[1]. | ||
| @ingroup API | ||
| @param device A device handle returned from hid_open(). | ||
@@ -332,10 +375,12 @@ @param data A buffer to put the read data into, including | ||
| */ | ||
| int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length); | ||
| int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length); | ||
| /** @brief Close a HID device. | ||
| This function sets the return value of hid_error(). | ||
| @ingroup API | ||
| @param device A device handle returned from hid_open(). | ||
| @param dev A device handle returned from hid_open(). | ||
| */ | ||
| void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device); | ||
| void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev); | ||
@@ -345,3 +390,3 @@ /** @brief Get The Manufacturer String from a HID device. | ||
| @ingroup API | ||
| @param device A device handle returned from hid_open(). | ||
| @param dev A device handle returned from hid_open(). | ||
| @param string A wide string buffer to put the data into. | ||
@@ -353,3 +398,3 @@ @param maxlen The length of the buffer in multiples of wchar_t. | ||
| */ | ||
| int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen); | ||
| int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen); | ||
@@ -359,3 +404,3 @@ /** @brief Get The Product String from a HID device. | ||
| @ingroup API | ||
| @param device A device handle returned from hid_open(). | ||
| @param dev A device handle returned from hid_open(). | ||
| @param string A wide string buffer to put the data into. | ||
@@ -367,3 +412,3 @@ @param maxlen The length of the buffer in multiples of wchar_t. | ||
| */ | ||
| int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen); | ||
| int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen); | ||
@@ -373,3 +418,3 @@ /** @brief Get The Serial Number String from a HID device. | ||
| @ingroup API | ||
| @param device A device handle returned from hid_open(). | ||
| @param dev A device handle returned from hid_open(). | ||
| @param string A wide string buffer to put the data into. | ||
@@ -381,3 +426,3 @@ @param maxlen The length of the buffer in multiples of wchar_t. | ||
| */ | ||
| int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen); | ||
| int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen); | ||
@@ -387,3 +432,3 @@ /** @brief Get a string from a HID device, based on its string index. | ||
| @ingroup API | ||
| @param device A device handle returned from hid_open(). | ||
| @param dev A device handle returned from hid_open(). | ||
| @param string_index The index of the string to get. | ||
@@ -396,8 +441,18 @@ @param string A wide string buffer to put the data into. | ||
| */ | ||
| int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen); | ||
| int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen); | ||
| /** @brief Get a string describing the last error which occurred. | ||
| Whether a function sets the last error is noted in its | ||
| documentation. These functions will reset the last error | ||
| to NULL before their execution. | ||
| Strings returned from hid_error() must not be freed by the user! | ||
| This function is thread-safe, and error messages are thread-local. | ||
| @ingroup API | ||
| @param device A device handle returned from hid_open(). | ||
| @param dev A device handle returned from hid_open(), | ||
| or NULL to get the last non-device-specific error | ||
| (e.g. for errors in hid_open() itself). | ||
@@ -408,3 +463,3 @@ @returns | ||
| */ | ||
| HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device); | ||
| HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *dev); | ||
@@ -411,0 +466,0 @@ #ifdef __cplusplus |
+61
-17
@@ -23,3 +23,3 @@ /******************************************************* | ||
| code repository located at: | ||
| http://github.com/signal11/hidapi . | ||
| https://github.com/libusb/hidapi . | ||
| ********************************************************/ | ||
@@ -55,3 +55,3 @@ | ||
| #ifdef __ANDROID__ | ||
| #if defined(__ANDROID__) && __ANDROID_API__ < __ANDROID_API_N__ | ||
@@ -178,2 +178,7 @@ /* Barrier implementation because Android/Bionic don't have pthread_barrier. | ||
| struct input_report *input_reports; | ||
| /* Was kernel driver detached by libusb */ | ||
| #ifdef DETACH_KERNEL_DRIVER | ||
| int is_driver_detached; | ||
| #endif | ||
| }; | ||
@@ -188,3 +193,3 @@ | ||
| { | ||
| hid_device *dev = calloc(1, sizeof(hid_device)); | ||
| hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device)); | ||
| dev->blocking = 1; | ||
@@ -211,4 +216,4 @@ | ||
| #if 0 | ||
| /*TODO: Implement this funciton on hidapi/libusb.. */ | ||
| static void register_error(hid_device *device, const char *op) | ||
| /*TODO: Implement this function on hidapi/libusb.. */ | ||
| static void register_error(hid_device *dev, const char *op) | ||
| { | ||
@@ -405,7 +410,3 @@ | ||
| size_t res; | ||
| #ifdef __FreeBSD__ | ||
| const char *inptr; | ||
| #else | ||
| char *inptr; | ||
| #endif | ||
| char *outptr; | ||
@@ -439,3 +440,3 @@ #endif | ||
| len -= 2; | ||
| str = malloc((len / 2 + 1) * sizeof(wchar_t)); | ||
| str = (wchar_t*) malloc((len / 2 + 1) * sizeof(wchar_t)); | ||
| int i; | ||
@@ -573,3 +574,3 @@ for (i = 0; i < len / 2; i++) { | ||
| /* VID/PID match. Create the record. */ | ||
| tmp = calloc(1, sizeof(struct hid_device_info)); | ||
| tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); | ||
| if (cur_dev) { | ||
@@ -747,4 +748,4 @@ cur_dev->next = tmp; | ||
| struct input_report *rpt = malloc(sizeof(*rpt)); | ||
| rpt->data = malloc(transfer->actual_length); | ||
| struct input_report *rpt = (struct input_report*) malloc(sizeof(*rpt)); | ||
| rpt->data = (uint8_t*) malloc(transfer->actual_length); | ||
| memcpy(rpt->data, transfer->buffer, transfer->actual_length); | ||
@@ -811,7 +812,7 @@ rpt->len = transfer->actual_length; | ||
| hid_device *dev = param; | ||
| unsigned char *buf; | ||
| uint8_t *buf; | ||
| const size_t length = dev->input_ep_max_packet_size; | ||
| /* Set up the transfer object. */ | ||
| buf = malloc(length); | ||
| buf = (uint8_t*) malloc(length); | ||
| dev->transfer = libusb_alloc_transfer(0); | ||
@@ -925,2 +926,3 @@ libusb_fill_interrupt_transfer(dev->transfer, | ||
| device is managed by the kernel */ | ||
| dev->is_driver_detached = 0; | ||
| if (libusb_kernel_driver_active(dev->device_handle, intf_desc->bInterfaceNumber) == 1) { | ||
@@ -935,2 +937,6 @@ res = libusb_detach_kernel_driver(dev->device_handle, intf_desc->bInterfaceNumber); | ||
| } | ||
| else { | ||
| dev->is_driver_detached = 1; | ||
| LOG("Driver successfully detached from kernel.\n"); | ||
| } | ||
| } | ||
@@ -1246,3 +1252,32 @@ #endif | ||
| int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length) | ||
| { | ||
| int res = -1; | ||
| int skipped_report_id = 0; | ||
| int report_number = data[0]; | ||
| if (report_number == 0x0) { | ||
| /* Offset the return buffer by 1, so that the report ID | ||
| will remain in byte 0. */ | ||
| data++; | ||
| length--; | ||
| skipped_report_id = 1; | ||
| } | ||
| res = libusb_control_transfer(dev->device_handle, | ||
| LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_IN, | ||
| 0x01/*HID get_report*/, | ||
| (1/*HID Input*/ << 8) | report_number, | ||
| dev->interface, | ||
| (unsigned char *)data, length, | ||
| 1000/*timeout millis*/); | ||
| if (res < 0) | ||
| return -1; | ||
| if (skipped_report_id) | ||
| res++; | ||
| return res; | ||
| } | ||
| void HID_API_EXPORT hid_close(hid_device *dev) | ||
@@ -1267,2 +1302,11 @@ { | ||
| /* reattach the kernel driver if it was detached */ | ||
| #ifdef DETACH_KERNEL_DRIVER | ||
| if (dev->is_driver_detached) { | ||
| int res = libusb_attach_kernel_driver(dev->device_handle, dev->interface); | ||
| if (res < 0) | ||
| LOG("Failed to reattach the driver to kernel.\n"); | ||
| } | ||
| #endif | ||
| /* Close the handle */ | ||
@@ -1315,3 +1359,3 @@ libusb_close(dev->device_handle); | ||
| { | ||
| return NULL; | ||
| return L"hid_error is not implemented yet"; | ||
| } | ||
@@ -1370,3 +1414,3 @@ | ||
| LANG("English - New Zealand", "en_nz", 0x1409), | ||
| LANG("English - Phillippines", "en_ph", 0x3409), | ||
| LANG("English - Philippines", "en_ph", 0x3409), | ||
| LANG("English - Southern Africa", "en_za", 0x1C09), | ||
@@ -1373,0 +1417,0 @@ LANG("English - Trinidad", "en_tt", 0x2C09), |
+112
-61
@@ -21,3 +21,3 @@ /******************************************************* | ||
| code repository located at: | ||
| http://github.com/signal11/hidapi . | ||
| https://github.com/libusb/hidapi . | ||
| ********************************************************/ | ||
@@ -49,12 +49,3 @@ | ||
| /* Definitions from linux/hidraw.h. Since these are new, some distros | ||
| may not have header files which contain them. */ | ||
| #ifndef HIDIOCSFEATURE | ||
| #define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len) | ||
| #endif | ||
| #ifndef HIDIOCGFEATURE | ||
| #define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len) | ||
| #endif | ||
| /* USB HID device property names */ | ||
@@ -80,34 +71,16 @@ const char *device_string_names[] = { | ||
| int uses_numbered_reports; | ||
| wchar_t *last_error_str; | ||
| }; | ||
| /* Global error message that is not specific to a device, e.g. for | ||
| hid_open(). It is thread-local like errno. */ | ||
| __thread wchar_t *last_global_error_str = NULL; | ||
| static __u32 kernel_version = 0; | ||
| static __u32 detect_kernel_version(void) | ||
| { | ||
| struct utsname name; | ||
| int major, minor, release; | ||
| int ret; | ||
| uname(&name); | ||
| ret = sscanf(name.release, "%d.%d.%d", &major, &minor, &release); | ||
| if (ret == 3) { | ||
| return KERNEL_VERSION(major, minor, release); | ||
| } | ||
| ret = sscanf(name.release, "%d.%d", &major, &minor); | ||
| if (ret == 2) { | ||
| return KERNEL_VERSION(major, minor, 0); | ||
| } | ||
| printf("Couldn't determine kernel version from version string \"%s\"\n", name.release); | ||
| return 0; | ||
| } | ||
| static hid_device *new_hid_device(void) | ||
| { | ||
| hid_device *dev = calloc(1, sizeof(hid_device)); | ||
| hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device)); | ||
| dev->device_handle = -1; | ||
| dev->blocking = 1; | ||
| dev->uses_numbered_reports = 0; | ||
| dev->last_error_str = NULL; | ||
@@ -128,3 +101,3 @@ return dev; | ||
| } | ||
| ret = calloc(wlen+1, sizeof(wchar_t)); | ||
| ret = (wchar_t*) calloc(wlen+1, sizeof(wchar_t)); | ||
| mbstowcs(ret, utf8, wlen+1); | ||
@@ -137,2 +110,44 @@ ret[wlen] = 0x0000; | ||
| /* Set the last global error to be reported by hid_error(NULL). | ||
| * The given error message will be copied (and decoded according to the | ||
| * currently locale, so do not pass in string constants). | ||
| * The last stored global error message is freed. | ||
| * Use register_global_error(NULL) to indicate "no error". */ | ||
| static void register_global_error(const char *msg) | ||
| { | ||
| if (last_global_error_str) | ||
| free(last_global_error_str); | ||
| last_global_error_str = utf8_to_wchar_t(msg); | ||
| } | ||
| /* Set the last error for a device to be reported by hid_error(device). | ||
| * The given error message will be copied (and decoded according to the | ||
| * currently locale, so do not pass in string constants). | ||
| * The last stored global error message is freed. | ||
| * Use register_device_error(device, NULL) to indicate "no error". */ | ||
| static void register_device_error(hid_device *dev, const char *msg) | ||
| { | ||
| if (dev->last_error_str) | ||
| free(dev->last_error_str); | ||
| dev->last_error_str = utf8_to_wchar_t(msg); | ||
| } | ||
| /* See register_device_error, but you can pass a format string into this function. */ | ||
| static void register_device_error_format(hid_device *dev, const char *format, ...) | ||
| { | ||
| va_list args; | ||
| va_start(args, format); | ||
| char msg[100]; | ||
| vsnprintf(msg, sizeof(msg), format, args); | ||
| va_end(args); | ||
| register_device_error(dev, msg); | ||
| } | ||
| /* Get an attribute value from a udev_device and return it as a whar_t | ||
@@ -277,3 +292,3 @@ string. The returned string must be freed with free() when done.*/ | ||
| if (!udev) { | ||
| printf("Can't create udev\n"); | ||
| register_global_error("Couldn't create udev context"); | ||
| return -1; | ||
@@ -377,4 +392,2 @@ } | ||
| kernel_version = detect_kernel_version(); | ||
| return 0; | ||
@@ -385,3 +398,5 @@ } | ||
| { | ||
| /* Nothing to do for this in the Linux/hidraw implementation. */ | ||
| /* Free global error message */ | ||
| register_global_error(NULL); | ||
| return 0; | ||
@@ -406,3 +421,3 @@ } | ||
| if (!udev) { | ||
| printf("Can't create udev\n"); | ||
| register_global_error("Couldn't create udev context"); | ||
| return NULL; | ||
@@ -473,3 +488,3 @@ } | ||
| /* VID/PID match. Create the record. */ | ||
| tmp = malloc(sizeof(struct hid_device_info)); | ||
| tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); | ||
| if (cur_dev) { | ||
@@ -597,2 +612,5 @@ cur_dev->next = tmp; | ||
| { | ||
| /* Set global error to none */ | ||
| register_global_error(NULL); | ||
| struct hid_device_info *devs, *cur_dev; | ||
@@ -624,2 +642,4 @@ const char *path_to_open = NULL; | ||
| handle = hid_open_path(path_to_open); | ||
| } else { | ||
| register_global_error("No such device"); | ||
| } | ||
@@ -634,2 +654,5 @@ | ||
| { | ||
| /* Set global error to none */ | ||
| register_global_error(NULL); | ||
| hid_device *dev = NULL; | ||
@@ -646,2 +669,4 @@ | ||
| if (dev->device_handle > 0) { | ||
| /* Set device error to none */ | ||
| register_device_error(dev, NULL); | ||
@@ -657,5 +682,4 @@ /* Get the report descriptor */ | ||
| if (res < 0) | ||
| perror("HIDIOCGRDESCSIZE"); | ||
| register_device_error_format(dev, "ioctl (GRDESCSIZE): %s", strerror(errno)); | ||
| /* Get Report Descriptor */ | ||
@@ -665,3 +689,3 @@ rpt_desc.size = desc_size; | ||
| if (res < 0) { | ||
| perror("HIDIOCGRDESC"); | ||
| register_device_error_format(dev, "ioctl (GRDESC): %s", strerror(errno)); | ||
| } else { | ||
@@ -678,2 +702,3 @@ /* Determine if this device uses numbered reports. */ | ||
| /* Unable to open any devices. */ | ||
| register_global_error(strerror(errno)); | ||
| free(dev); | ||
@@ -691,2 +716,4 @@ return NULL; | ||
| register_device_error(dev, (bytes_written == -1)? strerror(errno): NULL); | ||
| return bytes_written; | ||
@@ -698,2 +725,5 @@ } | ||
| { | ||
| /* Set device error to none */ | ||
| register_device_error(dev, NULL); | ||
| int bytes_read; | ||
@@ -715,6 +745,11 @@ | ||
| ret = poll(&fds, 1, milliseconds); | ||
| if (ret == -1 || ret == 0) { | ||
| /* Error or timeout */ | ||
| if (ret == 0) { | ||
| /* Timeout */ | ||
| return ret; | ||
| } | ||
| if (ret == -1) { | ||
| /* Error */ | ||
| register_device_error(dev, strerror(errno)); | ||
| return ret; | ||
| } | ||
| else { | ||
@@ -724,2 +759,3 @@ /* Check for errors on the file descriptor. This will | ||
| if (fds.revents & (POLLERR | POLLHUP | POLLNVAL)) | ||
| // We cannot use strerror() here as no -1 was returned from poll(). | ||
| return -1; | ||
@@ -730,12 +766,7 @@ } | ||
| bytes_read = read(dev->device_handle, data, length); | ||
| if (bytes_read < 0 && (errno == EAGAIN || errno == EINPROGRESS)) | ||
| bytes_read = 0; | ||
| if (bytes_read >= 0 && | ||
| kernel_version != 0 && | ||
| kernel_version < KERNEL_VERSION(2,6,34) && | ||
| dev->uses_numbered_reports) { | ||
| /* Work around a kernel bug. Chop off the first byte. */ | ||
| memmove(data, data+1, bytes_read); | ||
| bytes_read--; | ||
| if (bytes_read < 0) { | ||
| if (errno == EAGAIN || errno == EINPROGRESS) | ||
| bytes_read = 0; | ||
| else | ||
| register_device_error(dev, strerror(errno)); | ||
| } | ||
@@ -768,3 +799,3 @@ | ||
| if (res < 0) | ||
| perror("ioctl (SFEATURE)"); | ||
| register_device_error_format(dev, "ioctl (SFEATURE): %s", strerror(errno)); | ||
@@ -780,8 +811,12 @@ return res; | ||
| if (res < 0) | ||
| perror("ioctl (GFEATURE)"); | ||
| register_device_error_format(dev, "ioctl (GFEATURE): %s", strerror(errno)); | ||
| return res; | ||
| } | ||
| // Not supported by Linux HidRaw yet | ||
| int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length) | ||
| { | ||
| return -1; | ||
| } | ||
@@ -792,3 +827,10 @@ void HID_API_EXPORT hid_close(hid_device *dev) | ||
| return; | ||
| close(dev->device_handle); | ||
| int ret = close(dev->device_handle); | ||
| register_global_error((ret == -1)? strerror(errno): NULL); | ||
| /* Free the device error message */ | ||
| register_device_error(dev, NULL); | ||
| free(dev); | ||
@@ -819,5 +861,14 @@ } | ||
| /* Passing in NULL means asking for the last global error message. */ | ||
| HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) | ||
| { | ||
| return NULL; | ||
| if (dev) { | ||
| if (dev->last_error_str == NULL) | ||
| return L"Success"; | ||
| return dev->last_error_str; | ||
| } | ||
| if (last_global_error_str == NULL) | ||
| return L"Success"; | ||
| return last_global_error_str; | ||
| } |
+252
-123
@@ -20,3 +20,3 @@ /******************************************************* | ||
| code repository located at: | ||
| http://github.com/signal11/hidapi . | ||
| https://github.com/libusb/hidapi . | ||
| ********************************************************/ | ||
@@ -29,2 +29,3 @@ | ||
| #include <IOKit/IOKitLib.h> | ||
| #include <IOKit/usb/USBSpec.h> | ||
| #include <CoreFoundation/CoreFoundation.h> | ||
@@ -129,3 +130,3 @@ #include <wchar.h> | ||
| { | ||
| hid_device *dev = calloc(1, sizeof(hid_device)); | ||
| hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device)); | ||
| dev->device_handle = NULL; | ||
@@ -188,3 +189,3 @@ dev->blocking = 1; | ||
| #if 0 | ||
| static void register_error(hid_device *device, const char *op) | ||
| static void register_error(hid_device *dev, const char *op) | ||
| { | ||
@@ -195,2 +196,11 @@ | ||
| static CFArrayRef get_array_property(IOHIDDeviceRef device, CFStringRef key) | ||
| { | ||
| CFTypeRef ref = IOHIDDeviceGetProperty(device, key); | ||
| if (ref != NULL && CFGetTypeID(ref) == CFArrayGetTypeID()) { | ||
| return (CFArrayRef)ref; | ||
| } else { | ||
| return NULL; | ||
| } | ||
| } | ||
@@ -212,2 +222,7 @@ static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key) | ||
| static CFArrayRef get_usage_pairs(IOHIDDeviceRef device) | ||
| { | ||
| return get_array_property(device, CFSTR(kIOHIDDeviceUsagePairsKey)); | ||
| } | ||
| static unsigned short get_vendor_id(IOHIDDeviceRef device) | ||
@@ -235,3 +250,3 @@ { | ||
| str = IOHIDDeviceGetProperty(device, prop); | ||
| str = (CFStringRef) IOHIDDeviceGetProperty(device, prop); | ||
@@ -249,7 +264,7 @@ buf[0] = 0; | ||
| range.location = 0; | ||
| range.length = ((size_t)str_len > len)? len: (size_t)str_len; | ||
| range.length = ((size_t) str_len > len)? len: (size_t) str_len; | ||
| chars_copied = CFStringGetBytes(str, | ||
| range, | ||
| kCFStringEncodingUTF32LE, | ||
| (char)'?', | ||
| (char) '?', | ||
| FALSE, | ||
@@ -292,3 +307,3 @@ (UInt8*)buf, | ||
| size_t len = wcslen(s); | ||
| wchar_t *ret = malloc((len+1)*sizeof(wchar_t)); | ||
| wchar_t *ret = (wchar_t*) malloc((len+1)*sizeof(wchar_t)); | ||
| wcscpy(ret, s); | ||
@@ -308,3 +323,4 @@ | ||
| static void *iokit_framework = NULL; | ||
| static io_service_t (*dynamic_IOHIDDeviceGetService)(IOHIDDeviceRef device) = NULL; | ||
| typedef io_service_t (*dynamic_IOHIDDeviceGetService_t)(IOHIDDeviceRef device); | ||
| static dynamic_IOHIDDeviceGetService_t dynamic_IOHIDDeviceGetService = NULL; | ||
@@ -316,6 +332,6 @@ /* Use dlopen()/dlsym() to get a pointer to IOHIDDeviceGetService() if it exists. | ||
| if (iokit_framework == NULL) { | ||
| iokit_framework = dlopen("/System/Library/IOKit.framework/IOKit", RTLD_LAZY); | ||
| iokit_framework = dlopen("/System/Library/Frameworks/IOKit.framework/IOKit", RTLD_LAZY); | ||
| if (iokit_framework != NULL) | ||
| dynamic_IOHIDDeviceGetService = dlsym(iokit_framework, "IOHIDDeviceGetService"); | ||
| dynamic_IOHIDDeviceGetService = (dynamic_IOHIDDeviceGetService_t) dlsym(iokit_framework, "IOHIDDeviceGetService"); | ||
| } | ||
@@ -352,3 +368,3 @@ | ||
| }; | ||
| struct IOHIDDevice_internal *tmp = (struct IOHIDDevice_internal *)device; | ||
| struct IOHIDDevice_internal *tmp = (struct IOHIDDevice_internal *) device; | ||
@@ -405,2 +421,121 @@ return tmp->service; | ||
| static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev, int32_t usage_page, int32_t usage) | ||
| { | ||
| unsigned short dev_vid; | ||
| unsigned short dev_pid; | ||
| int BUF_LEN = 256; | ||
| wchar_t buf[BUF_LEN]; | ||
| struct hid_device_info *cur_dev; | ||
| io_object_t iokit_dev; | ||
| kern_return_t res; | ||
| io_string_t path; | ||
| if (dev == NULL) { | ||
| return NULL; | ||
| } | ||
| cur_dev = (struct hid_device_info *)calloc(1, sizeof(struct hid_device_info)); | ||
| if (cur_dev == NULL) { | ||
| return NULL; | ||
| } | ||
| dev_vid = get_vendor_id(dev); | ||
| dev_pid = get_product_id(dev); | ||
| cur_dev->usage_page = usage_page; | ||
| cur_dev->usage = usage; | ||
| /* Fill out the record */ | ||
| cur_dev->next = NULL; | ||
| /* Fill in the path (IOService plane) */ | ||
| iokit_dev = hidapi_IOHIDDeviceGetService(dev); | ||
| res = IORegistryEntryGetPath(iokit_dev, kIOServicePlane, path); | ||
| if (res == KERN_SUCCESS) | ||
| cur_dev->path = strdup(path); | ||
| else | ||
| cur_dev->path = strdup(""); | ||
| /* Serial Number */ | ||
| get_serial_number(dev, buf, BUF_LEN); | ||
| cur_dev->serial_number = dup_wcs(buf); | ||
| /* Manufacturer and Product strings */ | ||
| get_manufacturer_string(dev, buf, BUF_LEN); | ||
| cur_dev->manufacturer_string = dup_wcs(buf); | ||
| get_product_string(dev, buf, BUF_LEN); | ||
| cur_dev->product_string = dup_wcs(buf); | ||
| /* VID/PID */ | ||
| cur_dev->vendor_id = dev_vid; | ||
| cur_dev->product_id = dev_pid; | ||
| /* Release Number */ | ||
| cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey)); | ||
| /* Interface Number */ | ||
| /* We can only retrieve the interface number for USB HID devices. | ||
| * IOKit always seems to return 0 when querying a standard USB device | ||
| * for its interface. */ | ||
| bool is_usb_hid = get_int_property(dev, CFSTR(kUSBInterfaceClass)) == kUSBHIDClass; | ||
| if (is_usb_hid) { | ||
| /* Get the interface number */ | ||
| cur_dev->interface_number = get_int_property(dev, CFSTR(kUSBInterfaceNumber)); | ||
| } else { | ||
| cur_dev->interface_number = -1; | ||
| } | ||
| return cur_dev; | ||
| } | ||
| static struct hid_device_info *create_device_info(IOHIDDeviceRef device) | ||
| { | ||
| struct hid_device_info *root = NULL; | ||
| CFArrayRef usage_pairs = get_usage_pairs(device); | ||
| if (usage_pairs != NULL) { | ||
| struct hid_device_info *cur = NULL; | ||
| struct hid_device_info *next = NULL; | ||
| for (CFIndex i = 0; i < CFArrayGetCount(usage_pairs); i++) { | ||
| CFTypeRef dict = CFArrayGetValueAtIndex(usage_pairs, i); | ||
| if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) { | ||
| continue; | ||
| } | ||
| CFTypeRef usage_page_ref, usage_ref; | ||
| int32_t usage_page, usage; | ||
| if (!CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR(kIOHIDDeviceUsagePageKey), &usage_page_ref) || | ||
| !CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR(kIOHIDDeviceUsageKey), &usage_ref) || | ||
| CFGetTypeID(usage_page_ref) != CFNumberGetTypeID() || | ||
| CFGetTypeID(usage_ref) != CFNumberGetTypeID() || | ||
| !CFNumberGetValue((CFNumberRef)usage_page_ref, kCFNumberSInt32Type, &usage_page) || | ||
| !CFNumberGetValue((CFNumberRef)usage_ref, kCFNumberSInt32Type, &usage)) { | ||
| continue; | ||
| } | ||
| next = create_device_info_with_usage(device, usage_page, usage); | ||
| if (cur == NULL) { | ||
| root = next; | ||
| } | ||
| else { | ||
| cur->next = next; | ||
| } | ||
| if (next != NULL) { | ||
| cur = next; | ||
| } | ||
| } | ||
| } | ||
| if (root == NULL) { | ||
| /* error when generating or parsing usage pairs */ | ||
| int32_t usage_page = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey)); | ||
| int32_t usage = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey)); | ||
| root = create_device_info_with_usage(device, usage_page, usage); | ||
| } | ||
| return root; | ||
| } | ||
| struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) | ||
@@ -421,3 +556,23 @@ { | ||
| /* Get a list of the Devices */ | ||
| IOHIDManagerSetDeviceMatching(hid_mgr, NULL); | ||
| CFMutableDictionaryRef matching = NULL; | ||
| if (vendor_id != 0 || product_id != 0) { | ||
| matching = CFDictionaryCreateMutable(kCFAllocatorDefault, kIOHIDOptionsTypeNone, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | ||
| if (matching && vendor_id != 0) { | ||
| CFNumberRef v = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &vendor_id); | ||
| CFDictionarySetValue(matching, CFSTR(kIOHIDVendorIDKey), v); | ||
| CFRelease(v); | ||
| } | ||
| if (matching && product_id != 0) { | ||
| CFNumberRef p = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &product_id); | ||
| CFDictionarySetValue(matching, CFSTR(kIOHIDProductIDKey), p); | ||
| CFRelease(p); | ||
| } | ||
| } | ||
| IOHIDManagerSetDeviceMatching(hid_mgr, matching); | ||
| if (matching != NULL) { | ||
| CFRelease(matching); | ||
| } | ||
| CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr); | ||
@@ -427,3 +582,3 @@ | ||
| num_devices = CFSetGetCount(device_set); | ||
| IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); | ||
| IOHIDDeviceRef *device_array = (IOHIDDeviceRef*) calloc(num_devices, sizeof(IOHIDDeviceRef)); | ||
| CFSetGetValues(device_set, (const void **) device_array); | ||
@@ -433,67 +588,24 @@ | ||
| for (i = 0; i < num_devices; i++) { | ||
| unsigned short dev_vid; | ||
| unsigned short dev_pid; | ||
| #define BUF_LEN 256 | ||
| wchar_t buf[BUF_LEN]; | ||
| IOHIDDeviceRef dev = device_array[i]; | ||
| if (!dev) { | ||
| continue; | ||
| } | ||
| if (!dev) { | ||
| continue; | ||
| } | ||
| dev_vid = get_vendor_id(dev); | ||
| dev_pid = get_product_id(dev); | ||
| struct hid_device_info *tmp = create_device_info(dev); | ||
| if (tmp == NULL) { | ||
| continue; | ||
| } | ||
| /* Check the VID/PID against the arguments */ | ||
| if ((vendor_id == 0x0 || vendor_id == dev_vid) && | ||
| (product_id == 0x0 || product_id == dev_pid)) { | ||
| struct hid_device_info *tmp; | ||
| io_object_t iokit_dev; | ||
| kern_return_t res; | ||
| io_string_t path; | ||
| if (cur_dev) { | ||
| cur_dev->next = tmp; | ||
| } | ||
| else { | ||
| root = tmp; | ||
| } | ||
| cur_dev = tmp; | ||
| /* VID/PID match. Create the record. */ | ||
| tmp = malloc(sizeof(struct hid_device_info)); | ||
| if (cur_dev) { | ||
| cur_dev->next = tmp; | ||
| } | ||
| else { | ||
| root = tmp; | ||
| } | ||
| cur_dev = tmp; | ||
| /* Get the Usage Page and Usage for this device. */ | ||
| cur_dev->usage_page = get_int_property(dev, CFSTR(kIOHIDPrimaryUsagePageKey)); | ||
| cur_dev->usage = get_int_property(dev, CFSTR(kIOHIDPrimaryUsageKey)); | ||
| /* Fill out the record */ | ||
| cur_dev->next = NULL; | ||
| /* Fill in the path (IOService plane) */ | ||
| iokit_dev = hidapi_IOHIDDeviceGetService(dev); | ||
| res = IORegistryEntryGetPath(iokit_dev, kIOServicePlane, path); | ||
| if (res == KERN_SUCCESS) | ||
| cur_dev->path = strdup(path); | ||
| else | ||
| cur_dev->path = strdup(""); | ||
| /* Serial Number */ | ||
| get_serial_number(dev, buf, BUF_LEN); | ||
| cur_dev->serial_number = dup_wcs(buf); | ||
| /* Manufacturer and Product strings */ | ||
| get_manufacturer_string(dev, buf, BUF_LEN); | ||
| cur_dev->manufacturer_string = dup_wcs(buf); | ||
| get_product_string(dev, buf, BUF_LEN); | ||
| cur_dev->product_string = dup_wcs(buf); | ||
| /* VID/PID */ | ||
| cur_dev->vendor_id = dev_vid; | ||
| cur_dev->product_id = dev_pid; | ||
| /* Release Number */ | ||
| cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey)); | ||
| /* Interface Number (Unsupported on Mac)*/ | ||
| cur_dev->interface_number = -1; | ||
| /* move the pointer to the tail of returnd list */ | ||
| while (cur_dev->next != NULL) { | ||
| cur_dev = cur_dev->next; | ||
| } | ||
@@ -563,3 +675,3 @@ } | ||
| /* Stop the Run Loop for this device. */ | ||
| hid_device *d = context; | ||
| hid_device *d = (hid_device*) context; | ||
@@ -578,7 +690,7 @@ d->disconnected = 1; | ||
| struct input_report *rpt; | ||
| hid_device *dev = context; | ||
| hid_device *dev = (hid_device*) context; | ||
| /* Make a new Input Report object */ | ||
| rpt = calloc(1, sizeof(struct input_report)); | ||
| rpt->data = calloc(1, report_length); | ||
| rpt = (struct input_report*) calloc(1, sizeof(struct input_report)); | ||
| rpt->data = (uint8_t*) calloc(1, report_length); | ||
| memcpy(rpt->data, report, report_length); | ||
@@ -626,3 +738,3 @@ rpt->len = report_length; | ||
| { | ||
| hid_device *dev = context; | ||
| hid_device *dev = (hid_device*) context; | ||
| CFRunLoopStop(dev->run_loop); /*TODO: CFRunLoopGetCurrent()*/ | ||
@@ -633,3 +745,3 @@ } | ||
| { | ||
| hid_device *dev = param; | ||
| hid_device *dev = (hid_device*) param; | ||
| SInt32 code; | ||
@@ -705,9 +817,10 @@ | ||
| io_registry_entry_t entry = MACH_PORT_NULL; | ||
| IOReturn ret = kIOReturnInvalid; | ||
| dev = new_hid_device(); | ||
| /* Set up the HID Manager if it hasn't been done */ | ||
| if (hid_init() < 0) | ||
| return NULL; | ||
| goto return_error; | ||
| dev = new_hid_device(); | ||
| /* Get the IORegistry entry for the given path */ | ||
@@ -728,3 +841,3 @@ entry = IORegistryEntryFromPath(kIOMasterPortDefault, path); | ||
| /* Open the IOHIDDevice */ | ||
| IOReturn ret = IOHIDDeviceOpen(dev->device_handle, kIOHIDOptionsTypeSeizeDevice); | ||
| ret = IOHIDDeviceOpen(dev->device_handle, kIOHIDOptionsTypeSeizeDevice); | ||
| if (ret == kIOReturnSuccess) { | ||
@@ -735,3 +848,3 @@ char str[32]; | ||
| dev->max_input_report_len = (CFIndex) get_max_report_length(dev->device_handle); | ||
| dev->input_report_buf = calloc(dev->max_input_report_len, sizeof(uint8_t)); | ||
| dev->input_report_buf = (uint8_t*) calloc(dev->max_input_report_len, sizeof(uint8_t)); | ||
@@ -776,11 +889,8 @@ /* Create the Run Loop Mode for this device. | ||
| { | ||
| const unsigned char *data_to_send; | ||
| size_t length_to_send; | ||
| const unsigned char *data_to_send = data; | ||
| CFIndex length_to_send = length; | ||
| IOReturn res; | ||
| const unsigned char report_id = data[0]; | ||
| /* Return if the device has been disconnected. */ | ||
| if (dev->disconnected) | ||
| return -1; | ||
| if (data[0] == 0x0) { | ||
| if (report_id == 0x0) { | ||
| /* Not using numbered Reports. | ||
@@ -791,20 +901,49 @@ Don't send the report number. */ | ||
| } | ||
| else { | ||
| /* Using numbered Reports. | ||
| Send the Report Number */ | ||
| data_to_send = data; | ||
| length_to_send = length; | ||
| /* Avoid crash if the device has been unplugged. */ | ||
| if (dev->disconnected) { | ||
| return -1; | ||
| } | ||
| if (!dev->disconnected) { | ||
| res = IOHIDDeviceSetReport(dev->device_handle, | ||
| type, | ||
| data[0], /* Report ID*/ | ||
| data_to_send, length_to_send); | ||
| res = IOHIDDeviceSetReport(dev->device_handle, | ||
| type, | ||
| report_id, | ||
| data_to_send, length_to_send); | ||
| if (res == kIOReturnSuccess) { | ||
| return length; | ||
| if (res == kIOReturnSuccess) { | ||
| return length; | ||
| } | ||
| return -1; | ||
| } | ||
| static int get_report(hid_device *dev, IOHIDReportType type, unsigned char *data, size_t length) | ||
| { | ||
| unsigned char *report = data; | ||
| CFIndex report_length = length; | ||
| IOReturn res = kIOReturnSuccess; | ||
| const unsigned char report_id = data[0]; | ||
| if (report_id == 0x0) { | ||
| /* Not using numbered Reports. | ||
| Don't send the report number. */ | ||
| report = data+1; | ||
| report_length = length-1; | ||
| } | ||
| /* Avoid crash if the device has been unplugged. */ | ||
| if (dev->disconnected) { | ||
| return -1; | ||
| } | ||
| res = IOHIDDeviceGetReport(dev->device_handle, | ||
| type, | ||
| report_id, | ||
| report, &report_length); | ||
| if (res == kIOReturnSuccess) { | ||
| if (report_id == 0x0) { // 0 report number still present at the beginning | ||
| report_length++; | ||
| } | ||
| else | ||
| return -1; | ||
| return report_length; | ||
| } | ||
@@ -842,3 +981,3 @@ | ||
| /* A res of 0 means we may have been signaled or it may | ||
| be a spurious wakeup. Check to see that there's acutally | ||
| be a spurious wakeup. Check to see that there's actually | ||
| data in the queue before returning, and if not, go back | ||
@@ -863,3 +1002,3 @@ to sleep. See the pthread_cond_timedwait() man page for | ||
| /* A res of 0 means we may have been signaled or it may | ||
| be a spurious wakeup. Check to see that there's acutally | ||
| be a spurious wakeup. Check to see that there's actually | ||
| data in the queue before returning, and if not, go back | ||
@@ -971,20 +1110,10 @@ to sleep. See the pthread_cond_timedwait() man page for | ||
| { | ||
| CFIndex len = length; | ||
| IOReturn res; | ||
| return get_report(dev, kIOHIDReportTypeFeature, data, length); | ||
| } | ||
| /* Return if the device has been unplugged. */ | ||
| if (dev->disconnected) | ||
| return -1; | ||
| res = IOHIDDeviceGetReport(dev->device_handle, | ||
| kIOHIDReportTypeFeature, | ||
| data[0], /* Report ID */ | ||
| data, &len); | ||
| if (res == kIOReturnSuccess) | ||
| return len; | ||
| else | ||
| return -1; | ||
| int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length) | ||
| { | ||
| return get_report(dev, kIOHIDReportTypeInput, data, length); | ||
| } | ||
| void HID_API_EXPORT hid_close(hid_device *dev) | ||
@@ -1063,3 +1192,3 @@ { | ||
| return NULL; | ||
| return L"hid_error is not implemented yet"; | ||
| } | ||
@@ -1066,0 +1195,0 @@ |
@@ -82,3 +82,3 @@ #!/bin/bash | ||
| # Copy the binary into the bundle. Use ../libtool to do this if it's | ||
| # available beacuse if $EXE_NAME was built with autotools, it will be | ||
| # available because if $EXE_NAME was built with autotools, it will be | ||
| # necessary. If ../libtool not available, just use cp to do the copy, but | ||
@@ -85,0 +85,0 @@ # only if $EXE_NAME is a binary. |
@@ -11,6 +11,15 @@ /******************************* | ||
| #ifndef MAC_OS_X_VERSION_10_12 | ||
| #define MAC_OS_X_VERSION_10_12 101200 | ||
| #endif | ||
| // macOS 10.12 deprecated NSAnyEventMask in favor of NSEventMaskAny | ||
| #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 | ||
| #define NSEventMaskAny NSAnyEventMask | ||
| #endif | ||
| extern FXMainWindow *g_main_window; | ||
| @interface MyAppDelegate : NSObject | ||
| @interface MyAppDelegate : NSObject<NSApplicationDelegate> | ||
| { | ||
@@ -81,3 +90,3 @@ } | ||
| while (1) { | ||
| NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask | ||
| NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny | ||
| untilDate:nil | ||
@@ -84,0 +93,0 @@ inMode:NSDefaultRunLoopMode |
| Debug | ||
| Release | ||
| .vs/ | ||
| *.exp | ||
@@ -8,2 +9,3 @@ *.ilk | ||
| *.vcproj.* | ||
| *.vcxproj.* | ||
| *.ncb | ||
@@ -10,0 +12,0 @@ *.suo |
+88
-13
@@ -20,3 +20,3 @@ /******************************************************* | ||
| code repository located at: | ||
| http://github.com/signal11/hidapi . | ||
| https://github.com/libusb/hidapi . | ||
| ********************************************************/ | ||
@@ -59,2 +59,3 @@ | ||
| #define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100) | ||
| #define IOCTL_HID_GET_INPUT_REPORT HID_OUT_CTL_CODE(104) | ||
@@ -114,2 +115,3 @@ #ifdef __cplusplus | ||
| typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length); | ||
| typedef BOOLEAN (__stdcall *HidD_GetInputReport_)(HANDLE handle, PVOID data, ULONG length); | ||
| typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len); | ||
@@ -127,2 +129,3 @@ typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data); | ||
| static HidD_GetFeature_ HidD_GetFeature; | ||
| static HidD_GetInputReport_ HidD_GetInputReport; | ||
| static HidD_GetIndexedString_ HidD_GetIndexedString; | ||
@@ -176,3 +179,3 @@ static HidD_GetPreparsedData_ HidD_GetPreparsedData; | ||
| static void register_error(hid_device *device, const char *op) | ||
| static void register_error(hid_device *dev, const char *op) | ||
| { | ||
@@ -203,4 +206,4 @@ WCHAR *ptr, *msg; | ||
| the hid_error() function can pick it up. */ | ||
| LocalFree(device->last_error_str); | ||
| device->last_error_str = msg; | ||
| LocalFree(dev->last_error_str); | ||
| dev->last_error_str = msg; | ||
| } | ||
@@ -220,2 +223,3 @@ | ||
| RESOLVE(HidD_GetFeature); | ||
| RESOLVE(HidD_GetInputReport); | ||
| RESOLVE(HidD_GetIndexedString); | ||
@@ -235,6 +239,6 @@ RESOLVE(HidD_GetPreparsedData); | ||
| static HANDLE open_device(const char *path, BOOL enumerate) | ||
| static HANDLE open_device(const char *path, BOOL open_rw) | ||
| { | ||
| HANDLE handle; | ||
| DWORD desired_access = (enumerate)? 0: (GENERIC_WRITE | GENERIC_READ); | ||
| DWORD desired_access = (open_rw)? (GENERIC_WRITE | GENERIC_READ): 0; | ||
| DWORD share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE; | ||
@@ -369,3 +373,5 @@ | ||
| if (strcmp(driver_name, "HIDClass") == 0) { | ||
| if ((strcmp(driver_name, "HIDClass") == 0) || | ||
| (strcmp(driver_name, "Mouse") == 0) || | ||
| (strcmp(driver_name, "Keyboard") == 0)) { | ||
| /* See if there's a driver bound. */ | ||
@@ -382,3 +388,3 @@ res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, | ||
| /* Open a handle to the device */ | ||
| write_handle = open_device(device_interface_detail_data->DevicePath, TRUE); | ||
| write_handle = open_device(device_interface_detail_data->DevicePath, FALSE); | ||
@@ -448,2 +454,3 @@ /* Check validity of write_handle. */ | ||
| /* Serial Number */ | ||
| wstr[0]= 0x0000; | ||
| res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr)); | ||
@@ -456,2 +463,3 @@ wstr[WSTR_LEN-1] = 0x0000; | ||
| /* Manufacturer String */ | ||
| wstr[0]= 0x0000; | ||
| res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr)); | ||
@@ -464,2 +472,3 @@ wstr[WSTR_LEN-1] = 0x0000; | ||
| /* Product String */ | ||
| wstr[0]= 0x0000; | ||
| res = HidD_GetProductString(write_handle, wstr, sizeof(wstr)); | ||
@@ -582,9 +591,19 @@ wstr[WSTR_LEN-1] = 0x0000; | ||
| /* Open a handle to the device */ | ||
| dev->device_handle = open_device(path, FALSE); | ||
| dev->device_handle = open_device(path, TRUE); | ||
| /* Check validity of write_handle. */ | ||
| if (dev->device_handle == INVALID_HANDLE_VALUE) { | ||
| /* Unable to open the device. */ | ||
| register_error(dev, "CreateFile"); | ||
| goto err; | ||
| /* System devices, such as keyboards and mice, cannot be opened in | ||
| read-write mode, because the system takes exclusive control over | ||
| them. This is to prevent keyloggers. However, feature reports | ||
| can still be sent and received. Retry opening the device, but | ||
| without read/write access. */ | ||
| dev->device_handle = open_device(path, FALSE); | ||
| /* Check the validity of the limited device_handle. */ | ||
| if (dev->device_handle == INVALID_HANDLE_VALUE) { | ||
| /* Unable to open the device, even without read-write mode. */ | ||
| register_error(dev, "CreateFile"); | ||
| goto err; | ||
| } | ||
| } | ||
@@ -823,2 +842,51 @@ | ||
| int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length) | ||
| { | ||
| #if 0 | ||
| BOOL res; | ||
| res = HidD_GetInputReport(dev->device_handle, data, length); | ||
| if (!res) { | ||
| register_error(dev, "HidD_GetInputReport"); | ||
| return -1; | ||
| } | ||
| return length; | ||
| #else | ||
| DWORD bytes_returned; | ||
| OVERLAPPED ol; | ||
| memset(&ol, 0, sizeof(ol)); | ||
| BOOL res = DeviceIoControl(dev->device_handle, | ||
| IOCTL_HID_GET_INPUT_REPORT, | ||
| data, length, | ||
| data, length, | ||
| &bytes_returned, &ol); | ||
| if (!res) { | ||
| if (GetLastError() != ERROR_IO_PENDING) { | ||
| /* DeviceIoControl() failed. Return error. */ | ||
| register_error(dev, "Send Input Report DeviceIoControl"); | ||
| return -1; | ||
| } | ||
| } | ||
| /* Wait here until the write is done. This makes | ||
| hid_get_feature_report() synchronous. */ | ||
| res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/); | ||
| if (!res) { | ||
| /* The operation failed. */ | ||
| register_error(dev, "Send Input Report GetOverLappedResult"); | ||
| return -1; | ||
| } | ||
| /* bytes_returned does not include the first byte which contains the | ||
| report ID. The data buffer actually contains one more byte than | ||
| bytes_returned. */ | ||
| bytes_returned++; | ||
| return bytes_returned; | ||
| #endif | ||
| } | ||
| void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev) | ||
@@ -887,3 +955,10 @@ { | ||
| { | ||
| return (wchar_t*)dev->last_error_str; | ||
| if (dev) { | ||
| if (dev->last_error_str == NULL) | ||
| return L"Success"; | ||
| return (wchar_t*)dev->last_error_str; | ||
| } | ||
| // Global error messages are not (yet) implemented on Windows. | ||
| return L"hid_error for global errors is not implemented yet"; | ||
| } | ||
@@ -890,0 +965,0 @@ |
| | ||
| Microsoft Visual Studio Solution File, Format Version 10.00 | ||
| # Visual C++ Express 2008 | ||
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hidapi", "hidapi.vcproj", "{A107C21C-418A-4697-BB10-20C3AA60E2E4}" | ||
| Microsoft Visual Studio Solution File, Format Version 12.00 | ||
| # Visual Studio 15 | ||
| VisualStudioVersion = 15.0.28307.136 | ||
| MinimumVisualStudioVersion = 10.0.40219.1 | ||
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hidapi", "hidapi.vcxproj", "{A107C21C-418A-4697-BB10-20C3AA60E2E4}" | ||
| EndProject | ||
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hidtest", "hidtest.vcproj", "{23E9FF6A-49D1-4993-B2B5-BBB992C6C712}" | ||
| ProjectSection(ProjectDependencies) = postProject | ||
| {A107C21C-418A-4697-BB10-20C3AA60E2E4} = {A107C21C-418A-4697-BB10-20C3AA60E2E4} | ||
| EndProjectSection | ||
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hidtest", "hidtest.vcxproj", "{23E9FF6A-49D1-4993-B2B5-BBB992C6C712}" | ||
| EndProject | ||
@@ -14,3 +13,5 @@ Global | ||
| Debug|Win32 = Debug|Win32 | ||
| Debug|x64 = Debug|x64 | ||
| Release|Win32 = Release|Win32 | ||
| Release|x64 = Release|x64 | ||
| EndGlobalSection | ||
@@ -20,8 +21,16 @@ GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
| {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug|Win32.Build.0 = Debug|Win32 | ||
| {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug|x64.ActiveCfg = Debug|x64 | ||
| {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug|x64.Build.0 = Debug|x64 | ||
| {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release|Win32.ActiveCfg = Release|Win32 | ||
| {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release|Win32.Build.0 = Release|Win32 | ||
| {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release|x64.ActiveCfg = Release|x64 | ||
| {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release|x64.Build.0 = Release|x64 | ||
| {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Debug|Win32.ActiveCfg = Debug|Win32 | ||
| {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Debug|Win32.Build.0 = Debug|Win32 | ||
| {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Debug|x64.ActiveCfg = Debug|x64 | ||
| {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Debug|x64.Build.0 = Debug|x64 | ||
| {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Release|Win32.ActiveCfg = Release|Win32 | ||
| {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Release|Win32.Build.0 = Release|Win32 | ||
| {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Release|x64.ActiveCfg = Release|x64 | ||
| {23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Release|x64.Build.0 = Release|x64 | ||
| EndGlobalSection | ||
@@ -31,2 +40,5 @@ GlobalSection(SolutionProperties) = preSolution | ||
| EndGlobalSection | ||
| GlobalSection(ExtensibilityGlobals) = postSolution | ||
| SolutionGuid = {8749E535-9C65-4A89-840E-78D7578C7866} | ||
| EndGlobalSection | ||
| EndGlobal |
| { | ||
| "git": { | ||
| "sha1": "8da2a18387b812bbdf138f0b67f277ea5b5d8efe" | ||
| } | ||
| } |
| /******************************************************* | ||
| Windows HID simplification | ||
| Alan Ott | ||
| Signal 11 Software | ||
| 8/22/2009 | ||
| Copyright 2009 | ||
| This contents of this file may be used by anyone | ||
| for any reason without any conditions and may be | ||
| used as a starting point for your own applications | ||
| which use HIDAPI. | ||
| ********************************************************/ | ||
| #include <stdio.h> | ||
| #include <wchar.h> | ||
| #include <string.h> | ||
| #include <stdlib.h> | ||
| #include "hidapi.h" | ||
| // Headers needed for sleeping. | ||
| #ifdef _WIN32 | ||
| #include <windows.h> | ||
| #else | ||
| #include <unistd.h> | ||
| #endif | ||
| int main(int argc, char* argv[]) | ||
| { | ||
| int res; | ||
| unsigned char buf[256]; | ||
| #define MAX_STR 255 | ||
| wchar_t wstr[MAX_STR]; | ||
| hid_device *handle; | ||
| int i; | ||
| #ifdef WIN32 | ||
| UNREFERENCED_PARAMETER(argc); | ||
| UNREFERENCED_PARAMETER(argv); | ||
| #endif | ||
| struct hid_device_info *devs, *cur_dev; | ||
| if (hid_init()) | ||
| return -1; | ||
| devs = hid_enumerate(0x0, 0x0); | ||
| cur_dev = devs; | ||
| while (cur_dev) { | ||
| printf("Device Found\n type: %04hx %04hx\n path: %s\n serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number); | ||
| printf("\n"); | ||
| printf(" Manufacturer: %ls\n", cur_dev->manufacturer_string); | ||
| printf(" Product: %ls\n", cur_dev->product_string); | ||
| printf(" Release: %hx\n", cur_dev->release_number); | ||
| printf(" Interface: %d\n", cur_dev->interface_number); | ||
| printf("\n"); | ||
| cur_dev = cur_dev->next; | ||
| } | ||
| hid_free_enumeration(devs); | ||
| // Set up the command buffer. | ||
| memset(buf,0x00,sizeof(buf)); | ||
| buf[0] = 0x01; | ||
| buf[1] = 0x81; | ||
| // Open the device using the VID, PID, | ||
| // and optionally the Serial number. | ||
| ////handle = hid_open(0x4d8, 0x3f, L"12345"); | ||
| handle = hid_open(0x4d8, 0x3f, NULL); | ||
| if (!handle) { | ||
| printf("unable to open device\n"); | ||
| return 1; | ||
| } | ||
| // Read the Manufacturer String | ||
| wstr[0] = 0x0000; | ||
| res = hid_get_manufacturer_string(handle, wstr, MAX_STR); | ||
| if (res < 0) | ||
| printf("Unable to read manufacturer string\n"); | ||
| printf("Manufacturer String: %ls\n", wstr); | ||
| // Read the Product String | ||
| wstr[0] = 0x0000; | ||
| res = hid_get_product_string(handle, wstr, MAX_STR); | ||
| if (res < 0) | ||
| printf("Unable to read product string\n"); | ||
| printf("Product String: %ls\n", wstr); | ||
| // Read the Serial Number String | ||
| wstr[0] = 0x0000; | ||
| res = hid_get_serial_number_string(handle, wstr, MAX_STR); | ||
| if (res < 0) | ||
| printf("Unable to read serial number string\n"); | ||
| printf("Serial Number String: (%d) %ls", wstr[0], wstr); | ||
| printf("\n"); | ||
| // Read Indexed String 1 | ||
| wstr[0] = 0x0000; | ||
| res = hid_get_indexed_string(handle, 1, wstr, MAX_STR); | ||
| if (res < 0) | ||
| printf("Unable to read indexed string 1\n"); | ||
| printf("Indexed String 1: %ls\n", wstr); | ||
| // Set the hid_read() function to be non-blocking. | ||
| hid_set_nonblocking(handle, 1); | ||
| // Try to read from the device. There shoud be no | ||
| // data here, but execution should not block. | ||
| res = hid_read(handle, buf, 17); | ||
| // Send a Feature Report to the device | ||
| buf[0] = 0x2; | ||
| buf[1] = 0xa0; | ||
| buf[2] = 0x0a; | ||
| buf[3] = 0x00; | ||
| buf[4] = 0x00; | ||
| res = hid_send_feature_report(handle, buf, 17); | ||
| if (res < 0) { | ||
| printf("Unable to send a feature report.\n"); | ||
| } | ||
| memset(buf,0,sizeof(buf)); | ||
| // Read a Feature Report from the device | ||
| buf[0] = 0x2; | ||
| res = hid_get_feature_report(handle, buf, sizeof(buf)); | ||
| if (res < 0) { | ||
| printf("Unable to get a feature report.\n"); | ||
| printf("%ls", hid_error(handle)); | ||
| } | ||
| else { | ||
| // Print out the returned buffer. | ||
| printf("Feature Report\n "); | ||
| for (i = 0; i < res; i++) | ||
| printf("%02hhx ", buf[i]); | ||
| printf("\n"); | ||
| } | ||
| memset(buf,0,sizeof(buf)); | ||
| // Toggle LED (cmd 0x80). The first byte is the report number (0x1). | ||
| buf[0] = 0x1; | ||
| buf[1] = 0x80; | ||
| res = hid_write(handle, buf, 17); | ||
| if (res < 0) { | ||
| printf("Unable to write()\n"); | ||
| printf("Error: %ls\n", hid_error(handle)); | ||
| } | ||
| // Request state (cmd 0x81). The first byte is the report number (0x1). | ||
| buf[0] = 0x1; | ||
| buf[1] = 0x81; | ||
| hid_write(handle, buf, 17); | ||
| if (res < 0) | ||
| printf("Unable to write() (2)\n"); | ||
| // Read requested state. hid_read() has been set to be | ||
| // non-blocking by the call to hid_set_nonblocking() above. | ||
| // This loop demonstrates the non-blocking nature of hid_read(). | ||
| res = 0; | ||
| while (res == 0) { | ||
| res = hid_read(handle, buf, sizeof(buf)); | ||
| if (res == 0) | ||
| printf("waiting...\n"); | ||
| if (res < 0) | ||
| printf("Unable to read()\n"); | ||
| #ifdef WIN32 | ||
| Sleep(500); | ||
| #else | ||
| usleep(500*1000); | ||
| #endif | ||
| } | ||
| printf("Data read:\n "); | ||
| // Print out the returned buffer. | ||
| for (i = 0; i < res; i++) | ||
| printf("%02hhx ", buf[i]); | ||
| printf("\n"); | ||
| hid_close(handle); | ||
| /* Free static HIDAPI objects. */ | ||
| hid_exit(); | ||
| #ifdef WIN32 | ||
| system("pause"); | ||
| #endif | ||
| return 0; | ||
| } |
| There are two implementations of HIDAPI for Linux. One (linux/hid.c) uses the | ||
| Linux hidraw driver, and the other (libusb/hid.c) uses libusb. Which one you | ||
| use depends on your application. Complete functionality of the hidraw | ||
| version depends on patches to the Linux kernel which are not currently in | ||
| the mainline. These patches have to do with sending and receiving feature | ||
| reports. The libusb implementation uses libusb to talk directly to the | ||
| device, bypassing any Linux HID driver. The disadvantage of the libusb | ||
| version is that it will only work with USB devices, while the hidraw | ||
| implementation will work with Bluetooth devices as well. | ||
| To use HIDAPI, simply drop either linux/hid.c or libusb/hid.c into your | ||
| application and build using the build parameters in the Makefile. | ||
| Libusb Implementation notes | ||
| ---------------------------- | ||
| For the libusb implementation, libusb-1.0 must be installed. Libusb 1.0 is | ||
| different than the legacy libusb 0.1 which is installed on many systems. To | ||
| install libusb-1.0 on Ubuntu and other Debian-based systems, run: | ||
| sudo apt-get install libusb-1.0-0-dev | ||
| Hidraw Implementation notes | ||
| ---------------------------- | ||
| For the hidraw implementation, libudev headers and libraries are required to | ||
| build hidapi programs. To install libudev libraries on Ubuntu, | ||
| and other Debian-based systems, run: | ||
| sudo apt-get install libudev-dev | ||
| On Redhat-based systems, run the following as root: | ||
| yum install libudev-devel | ||
| Unfortunately, the hidraw driver, which the linux version of hidapi is based | ||
| on, contains bugs in kernel versions < 2.6.36, which the client application | ||
| should be aware of. | ||
| Bugs (hidraw implementation only): | ||
| ----------------------------------- | ||
| On Kernel versions < 2.6.34, if your device uses numbered reports, an extra | ||
| byte will be returned at the beginning of all reports returned from read() | ||
| for hidraw devices. This is worked around in the libary. No action should be | ||
| necessary in the client library. | ||
| On Kernel versions < 2.6.35, reports will only be sent using a Set_Report | ||
| transfer on the CONTROL endpoint. No data will ever be sent on an Interrupt | ||
| Out endpoint if one exists. This is fixed in 2.6.35. In 2.6.35, OUTPUT | ||
| reports will be sent to the device on the first INTERRUPT OUT endpoint if it | ||
| exists; If it does not exist, OUTPUT reports will be sent on the CONTROL | ||
| endpoint. | ||
| On Kernel versions < 2.6.36, add an extra byte containing the report number | ||
| to sent reports if numbered reports are used, and the device does not | ||
| contain an INTERRPUT OUT endpoint for OUTPUT transfers. For example, if | ||
| your device uses numbered reports and wants to send {0x2 0xff 0xff 0xff} to | ||
| the device (0x2 is the report number), you must send {0x2 0x2 0xff 0xff | ||
| 0xff}. If your device has the optional Interrupt OUT endpoint, this does not | ||
| apply (but really on 2.6.35 only, because 2.6.34 won't use the interrupt | ||
| out endpoint). |
| HIDAPI library for Windows, Linux, FreeBSD and Mac OS X | ||
| ========================================================= | ||
| About | ||
| ====== | ||
| HIDAPI is a multi-platform library which allows an application to interface | ||
| with USB and Bluetooth HID-Class devices on Windows, Linux, FreeBSD, and Mac | ||
| OS X. HIDAPI can be either built as a shared library (.so or .dll) or | ||
| can be embedded directly into a target application by adding a single source | ||
| file (per platform) and a single header. | ||
| HIDAPI has four back-ends: | ||
| * Windows (using hid.dll) | ||
| * Linux/hidraw (using the Kernel's hidraw driver) | ||
| * Linux/libusb (using libusb-1.0) | ||
| * FreeBSD (using libusb-1.0) | ||
| * Mac (using IOHidManager) | ||
| On Linux, either the hidraw or the libusb back-end can be used. There are | ||
| tradeoffs, and the functionality supported is slightly different. | ||
| Linux/hidraw (linux/hid.c): | ||
| This back-end uses the hidraw interface in the Linux kernel. While this | ||
| back-end will support both USB and Bluetooth, it has some limitations on | ||
| kernels prior to 2.6.39, including the inability to send or receive feature | ||
| reports. In addition, it will only communicate with devices which have | ||
| hidraw nodes associated with them. Keyboards, mice, and some other devices | ||
| which are blacklisted from having hidraw nodes will not work. Fortunately, | ||
| for nearly all the uses of hidraw, this is not a problem. | ||
| Linux/FreeBSD/libusb (libusb/hid.c): | ||
| This back-end uses libusb-1.0 to communicate directly to a USB device. This | ||
| back-end will of course not work with Bluetooth devices. | ||
| HIDAPI also comes with a Test GUI. The Test GUI is cross-platform and uses | ||
| Fox Toolkit (http://www.fox-toolkit.org). It will build on every platform | ||
| which HIDAPI supports. Since it relies on a 3rd party library, building it | ||
| is optional but recommended because it is so useful when debugging hardware. | ||
| What Does the API Look Like? | ||
| ============================= | ||
| The API provides the the most commonly used HID functions including sending | ||
| and receiving of input, output, and feature reports. The sample program, | ||
| which communicates with a heavily hacked up version of the Microchip USB | ||
| Generic HID sample looks like this (with error checking removed for | ||
| simplicity): | ||
| #ifdef WIN32 | ||
| #include <windows.h> | ||
| #endif | ||
| #include <stdio.h> | ||
| #include <stdlib.h> | ||
| #include "hidapi.h" | ||
| #define MAX_STR 255 | ||
| int main(int argc, char* argv[]) | ||
| { | ||
| int res; | ||
| unsigned char buf[65]; | ||
| wchar_t wstr[MAX_STR]; | ||
| hid_device *handle; | ||
| int i; | ||
| // Initialize the hidapi library | ||
| res = hid_init(); | ||
| // Open the device using the VID, PID, | ||
| // and optionally the Serial number. | ||
| handle = hid_open(0x4d8, 0x3f, NULL); | ||
| // Read the Manufacturer String | ||
| res = hid_get_manufacturer_string(handle, wstr, MAX_STR); | ||
| wprintf(L"Manufacturer String: %s\n", wstr); | ||
| // Read the Product String | ||
| res = hid_get_product_string(handle, wstr, MAX_STR); | ||
| wprintf(L"Product String: %s\n", wstr); | ||
| // Read the Serial Number String | ||
| res = hid_get_serial_number_string(handle, wstr, MAX_STR); | ||
| wprintf(L"Serial Number String: (%d) %s\n", wstr[0], wstr); | ||
| // Read Indexed String 1 | ||
| res = hid_get_indexed_string(handle, 1, wstr, MAX_STR); | ||
| wprintf(L"Indexed String 1: %s\n", wstr); | ||
| // Toggle LED (cmd 0x80). The first byte is the report number (0x0). | ||
| buf[0] = 0x0; | ||
| buf[1] = 0x80; | ||
| res = hid_write(handle, buf, 65); | ||
| // Request state (cmd 0x81). The first byte is the report number (0x0). | ||
| buf[0] = 0x0; | ||
| buf[1] = 0x81; | ||
| res = hid_write(handle, buf, 65); | ||
| // Read requested state | ||
| res = hid_read(handle, buf, 65); | ||
| // Print out the returned buffer. | ||
| for (i = 0; i < 4; i++) | ||
| printf("buf[%d]: %d\n", i, buf[i]); | ||
| // Finalize the hidapi library | ||
| res = hid_exit(); | ||
| return 0; | ||
| } | ||
| If you have your own simple test programs which communicate with standard | ||
| hardware development boards (such as those from Microchip, TI, Atmel, | ||
| FreeScale and others), please consider sending me something like the above | ||
| for inclusion into the HIDAPI source. This will help others who have the | ||
| same hardware as you do. | ||
| License | ||
| ======== | ||
| HIDAPI may be used by one of three licenses as outlined in LICENSE.txt. | ||
| Download | ||
| ========= | ||
| HIDAPI can be downloaded from github | ||
| git clone git://github.com/signal11/hidapi.git | ||
| Build Instructions | ||
| =================== | ||
| This section is long. Don't be put off by this. It's not long because it's | ||
| complicated to build HIDAPI; it's quite the opposite. This section is long | ||
| because of the flexibility of HIDAPI and the large number of ways in which | ||
| it can be built and used. You will likely pick a single build method. | ||
| HIDAPI can be built in several different ways. If you elect to build a | ||
| shared library, you will need to build it from the HIDAPI source | ||
| distribution. If you choose instead to embed HIDAPI directly into your | ||
| application, you can skip the building and look at the provided platform | ||
| Makefiles for guidance. These platform Makefiles are located in linux/ | ||
| libusb/ mac/ and windows/ and are called Makefile-manual. In addition, | ||
| Visual Studio projects are provided. Even if you're going to embed HIDAPI | ||
| into your project, it is still beneficial to build the example programs. | ||
| Prerequisites: | ||
| --------------- | ||
| Linux: | ||
| ------- | ||
| On Linux, you will need to install development packages for libudev, | ||
| libusb and optionally Fox-toolkit (for the test GUI). On | ||
| Debian/Ubuntu systems these can be installed by running: | ||
| sudo apt-get install libudev-dev libusb-1.0-0-dev libfox-1.6-dev | ||
| If you downloaded the source directly from the git repository (using | ||
| git clone), you'll need Autotools: | ||
| sudo apt-get install autotools-dev autoconf automake libtool | ||
| FreeBSD: | ||
| --------- | ||
| On FreeBSD you will need to install GNU make, libiconv, and | ||
| optionally Fox-Toolkit (for the test GUI). This is done by running | ||
| the following: | ||
| pkg_add -r gmake libiconv fox16 | ||
| If you downloaded the source directly from the git repository (using | ||
| git clone), you'll need Autotools: | ||
| pkg_add -r autotools | ||
| Mac: | ||
| ----- | ||
| On Mac, you will need to install Fox-Toolkit if you wish to build | ||
| the Test GUI. There are two ways to do this, and each has a slight | ||
| complication. Which method you use depends on your use case. | ||
| If you wish to build the Test GUI just for your own testing on your | ||
| own computer, then the easiest method is to install Fox-Toolkit | ||
| using ports: | ||
| sudo port install fox | ||
| If you wish to build the TestGUI app bundle to redistribute to | ||
| others, you will need to install Fox-toolkit from source. This is | ||
| because the version of fox that gets installed using ports uses the | ||
| ports X11 libraries which are not compatible with the Apple X11 | ||
| libraries. If you install Fox with ports and then try to distribute | ||
| your built app bundle, it will simply fail to run on other systems. | ||
| To install Fox-Toolkit manually, download the source package from | ||
| http://www.fox-toolkit.org, extract it, and run the following from | ||
| within the extracted source: | ||
| ./configure && make && make install | ||
| Windows: | ||
| --------- | ||
| On Windows, if you want to build the test GUI, you will need to get | ||
| the hidapi-externals.zip package from the download site. This | ||
| contains pre-built binaries for Fox-toolkit. Extract | ||
| hidapi-externals.zip just outside of hidapi, so that | ||
| hidapi-externals and hidapi are on the same level, as shown: | ||
| Parent_Folder | ||
| | | ||
| +hidapi | ||
| +hidapi-externals | ||
| Again, this step is not required if you do not wish to build the | ||
| test GUI. | ||
| Building HIDAPI into a shared library on Unix Platforms: | ||
| --------------------------------------------------------- | ||
| On Unix-like systems such as Linux, FreeBSD, Mac, and even Windows, using | ||
| Mingw or Cygwin, the easiest way to build a standard system-installed shared | ||
| library is to use the GNU Autotools build system. If you checked out the | ||
| source from the git repository, run the following: | ||
| ./bootstrap | ||
| ./configure | ||
| make | ||
| make install <----- as root, or using sudo | ||
| If you downloaded a source package (ie: if you did not run git clone), you | ||
| can skip the ./bootstrap step. | ||
| ./configure can take several arguments which control the build. The two most | ||
| likely to be used are: | ||
| --enable-testgui | ||
| Enable build of the Test GUI. This requires Fox toolkit to | ||
| be installed. Instructions for installing Fox-Toolkit on | ||
| each platform are in the Prerequisites section above. | ||
| --prefix=/usr | ||
| Specify where you want the output headers and libraries to | ||
| be installed. The example above will put the headers in | ||
| /usr/include and the binaries in /usr/lib. The default is to | ||
| install into /usr/local which is fine on most systems. | ||
| Building the manual way on Unix platforms: | ||
| ------------------------------------------- | ||
| Manual Makefiles are provided mostly to give the user and idea what it takes | ||
| to build a program which embeds HIDAPI directly inside of it. These should | ||
| really be used as examples only. If you want to build a system-wide shared | ||
| library, use the Autotools method described above. | ||
| To build HIDAPI using the manual makefiles, change to the directory | ||
| of your platform and run make. For example, on Linux run: | ||
| cd linux/ | ||
| make -f Makefile-manual | ||
| To build the Test GUI using the manual makefiles: | ||
| cd testgui/ | ||
| make -f Makefile-manual | ||
| Building on Windows: | ||
| --------------------- | ||
| To build the HIDAPI DLL on Windows using Visual Studio, build the .sln file | ||
| in the windows/ directory. | ||
| To build the Test GUI on windows using Visual Studio, build the .sln file in | ||
| the testgui/ directory. | ||
| To build HIDAPI using MinGW or Cygwin using Autotools, use the instructions | ||
| in the section titled "Building HIDAPI into a shared library on Unix | ||
| Platforms" above. Note that building the Test GUI with MinGW or Cygwin will | ||
| require the Windows procedure in the Prerequisites section above (ie: | ||
| hidapi-externals.zip). | ||
| To build HIDAPI using MinGW using the Manual Makefiles, see the section | ||
| "Building the manual way on Unix platforms" above. | ||
| HIDAPI can also be built using the Windows DDK (now also called the Windows | ||
| Driver Kit or WDK). This method was originally required for the HIDAPI build | ||
| but not anymore. However, some users still prefer this method. It is not as | ||
| well supported anymore but should still work. Patches are welcome if it does | ||
| not. To build using the DDK: | ||
| 1. Install the Windows Driver Kit (WDK) from Microsoft. | ||
| 2. From the Start menu, in the Windows Driver Kits folder, select Build | ||
| Environments, then your operating system, then the x86 Free Build | ||
| Environment (or one that is appropriate for your system). | ||
| 3. From the console, change directory to the windows/ddk_build/ directory, | ||
| which is part of the HIDAPI distribution. | ||
| 4. Type build. | ||
| 5. You can find the output files (DLL and LIB) in a subdirectory created | ||
| by the build system which is appropriate for your environment. On | ||
| Windows XP, this directory is objfre_wxp_x86/i386. | ||
| Cross Compiling | ||
| ================ | ||
| This section talks about cross compiling HIDAPI for Linux using autotools. | ||
| This is useful for using HIDAPI on embedded Linux targets. These | ||
| instructions assume the most raw kind of embedded Linux build, where all | ||
| prerequisites will need to be built first. This process will of course vary | ||
| based on your embedded Linux build system if you are using one, such as | ||
| OpenEmbedded or Buildroot. | ||
| For the purpose of this section, it will be assumed that the following | ||
| environment variables are exported. | ||
| $ export STAGING=$HOME/out | ||
| $ export HOST=arm-linux | ||
| STAGING and HOST can be modified to suit your setup. | ||
| Prerequisites | ||
| -------------- | ||
| Note that the build of libudev is the very basic configuration. | ||
| Build Libusb. From the libusb source directory, run: | ||
| ./configure --host=$HOST --prefix=$STAGING | ||
| make | ||
| make install | ||
| Build libudev. From the libudev source directory, run: | ||
| ./configure --disable-gudev --disable-introspection --disable-hwdb \ | ||
| --host=$HOST --prefix=$STAGING | ||
| make | ||
| make install | ||
| Building HIDAPI | ||
| ---------------- | ||
| Build HIDAPI: | ||
| PKG_CONFIG_DIR= \ | ||
| PKG_CONFIG_LIBDIR=$STAGING/lib/pkgconfig:$STAGING/share/pkgconfig \ | ||
| PKG_CONFIG_SYSROOT_DIR=$STAGING \ | ||
| ./configure --host=$HOST --prefix=$STAGING | ||
| Signal 11 Software - 2010-04-11 | ||
| 2010-07-28 | ||
| 2011-09-10 | ||
| 2012-05-01 | ||
| 2012-07-03 |
| /******************************* | ||
| Mac support for HID Test GUI | ||
| Alan Ott | ||
| Signal 11 Software | ||
| Some of this code is from Apple Documentation, most notably | ||
| http://developer.apple.com/legacy/mac/library/documentation/AppleScript/Conceptual/AppleEvents/AppleEvents.pdf | ||
| *******************************/ | ||
| #include <Carbon/Carbon.h> | ||
| #include <fx.h> | ||
| extern FXMainWindow *g_main_window; | ||
| static pascal OSErr HandleQuitMessage(const AppleEvent *theAppleEvent, AppleEvent | ||
| *reply, long handlerRefcon) | ||
| { | ||
| puts("Quitting\n"); | ||
| FXApp::instance()->exit(); | ||
| return 0; | ||
| } | ||
| static pascal OSErr HandleReopenMessage(const AppleEvent *theAppleEvent, AppleEvent | ||
| *reply, long handlerRefcon) | ||
| { | ||
| puts("Showing"); | ||
| g_main_window->show(); | ||
| return 0; | ||
| } | ||
| static pascal OSErr HandleWildCardMessage(const AppleEvent *theAppleEvent, AppleEvent | ||
| *reply, long handlerRefcon) | ||
| { | ||
| puts("WildCard\n"); | ||
| return 0; | ||
| } | ||
| OSStatus AEHandler(EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon) | ||
| { | ||
| Boolean release = false; | ||
| EventRecord eventRecord; | ||
| OSErr ignoreErrForThisSample; | ||
| // Events of type kEventAppleEvent must be removed from the queue | ||
| // before being passed to AEProcessAppleEvent. | ||
| if (IsEventInQueue(GetMainEventQueue(), inEvent)) | ||
| { | ||
| // RemoveEventFromQueue will release the event, which will | ||
| // destroy it if we don't retain it first. | ||
| RetainEvent(inEvent); | ||
| release = true; | ||
| RemoveEventFromQueue(GetMainEventQueue(), inEvent); | ||
| } | ||
| // Convert the event ref to the type AEProcessAppleEvent expects. | ||
| ConvertEventRefToEventRecord(inEvent, &eventRecord); | ||
| ignoreErrForThisSample = AEProcessAppleEvent(&eventRecord); | ||
| if (release) | ||
| ReleaseEvent(inEvent); | ||
| // This Carbon event has been handled, even if no AppleEvent handlers | ||
| // were installed for the Apple event. | ||
| return noErr; | ||
| } | ||
| static void HandleEvent(EventRecord *event) | ||
| { | ||
| //printf("What: %d message %x\n", event->what, event->message); | ||
| if (event->what == osEvt) { | ||
| if (((event->message >> 24) & 0xff) == suspendResumeMessage) { | ||
| if (event->message & resumeFlag) { | ||
| g_main_window->show(); | ||
| } | ||
| } | ||
| } | ||
| #if 0 | ||
| switch (event->what) | ||
| { | ||
| case mouseDown: | ||
| //HandleMouseDown(event); | ||
| break; | ||
| case keyDown: | ||
| case autoKey: | ||
| //HandleKeyPress(event); | ||
| break; | ||
| case kHighLevelEvent: | ||
| puts("Calling ProcessAppleEvent\n"); | ||
| AEProcessAppleEvent(event); | ||
| break; | ||
| } | ||
| #endif | ||
| } | ||
| void | ||
| init_apple_message_system() | ||
| { | ||
| OSErr err; | ||
| static const EventTypeSpec appleEvents[] = | ||
| { | ||
| { kEventClassAppleEvent, kEventAppleEvent } | ||
| }; | ||
| /* Install the handler for Apple Events */ | ||
| InstallApplicationEventHandler(NewEventHandlerUPP(AEHandler), | ||
| GetEventTypeCount(appleEvents), appleEvents, 0, NULL); | ||
| /* Install handlers for the individual Apple Events that come | ||
| from the Dock icon: the Reopen (click), and the Quit messages. */ | ||
| err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, | ||
| NewAEEventHandlerUPP(HandleQuitMessage), 0, false); | ||
| err = AEInstallEventHandler(kCoreEventClass, kAEReopenApplication, | ||
| NewAEEventHandlerUPP(HandleReopenMessage), 0, false); | ||
| #if 0 | ||
| // Left as an example of a wild card match. | ||
| err = AEInstallEventHandler(kCoreEventClass, typeWildCard, | ||
| NewAEEventHandlerUPP(HandleWildMessage), 0, false); | ||
| #endif | ||
| } | ||
| void | ||
| check_apple_events() | ||
| { | ||
| RgnHandle cursorRgn = NULL; | ||
| Boolean gotEvent=TRUE; | ||
| EventRecord event; | ||
| while (gotEvent) { | ||
| gotEvent = WaitNextEvent(everyEvent, &event, 0L/*timeout*/, cursorRgn); | ||
| if (gotEvent) { | ||
| HandleEvent(&event); | ||
| } | ||
| } | ||
| } |
| #!/bin/bash | ||
| xterm -e /Users/alan/work/hidapi/testgui/TestGUI.app/Contents/MacOS/tg |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet