├── libs
└── HIDAPI
│ ├── hid.c.vsspell
│ ├── hidapi.h
│ └── hid.c
├── example
├── main.cpp
├── ofApp.h
└── ofApp.cpp
├── README.md
├── addon_config.mk
└── src
├── ofxDS4.h
└── ofxDS4.cpp
/libs/HIDAPI/hid.c.vsspell:
--------------------------------------------------------------------------------
1 |
2 |
4 |
--------------------------------------------------------------------------------
/example/main.cpp:
--------------------------------------------------------------------------------
1 | #include "ofMain.h"
2 | #include "ofApp.h"
3 |
4 | //========================================================================
5 | int main( ){
6 | ofSetupOpenGL(1024,768,OF_WINDOW); // <-------- setup the GL context
7 |
8 | // this kicks off the running of my app
9 | // can be OF_WINDOW or OF_FULLSCREEN
10 | // pass in width and height too:
11 | ofRunApp(new ofApp());
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/example/ofApp.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "ofMain.h"
4 | #include "ofxDS4.h"
5 |
6 | class ofApp : public ofBaseApp{
7 |
8 | public:
9 | void setup();
10 | void update();
11 | void draw();
12 |
13 | void keyPressed(int key);
14 | void keyReleased(int key);
15 | void mouseMoved(int x, int y );
16 | void mouseDragged(int x, int y, int button);
17 | void mousePressed(int x, int y, int button);
18 | void mouseReleased(int x, int y, int button);
19 | void mouseEntered(int x, int y);
20 | void mouseExited(int x, int y);
21 | void windowResized(int w, int h);
22 | void dragEvent(ofDragInfo dragInfo);
23 | void gotMessage(ofMessage msg);
24 |
25 | ofxDS4 _ds4;
26 | };
27 |
--------------------------------------------------------------------------------
/example/ofApp.cpp:
--------------------------------------------------------------------------------
1 | #include "ofApp.h"
2 |
3 | //--------------------------------------------------------------
4 | void ofApp::setup(){
5 | _ds4.setup();
6 | }
7 |
8 | //--------------------------------------------------------------
9 | void ofApp::update(){
10 | _ds4.update();
11 | }
12 |
13 | //--------------------------------------------------------------
14 | void ofApp::draw(){
15 |
16 | ofQuaternion q = _ds4.getQuaternions();
17 | ofVec3f axis;
18 | float angle;
19 | q.getRotate(angle, axis);
20 |
21 | string gyroVals = "Gyro : " + ofToString(_ds4.getGyro(), 4);
22 | string accelVals = "Accel : " + ofToString(_ds4.getAccel(), 4);
23 | string instructions = "Rotation of the sphere approximates rotation of the controller.";
24 |
25 | ofPushStyle();
26 | ofPushView();
27 | ofTranslate(ofGetWindowWidth() / 2., ofGetWindowHeight() / 2.);
28 | ofClear(ofColor::black);
29 | ofRotate(angle, axis.x, axis.y, axis.z);
30 | ofSetLineWidth(5);
31 | ofDrawAxis(150);
32 | ofNoFill();
33 | ofSetLineWidth(0.5);
34 | ofSetColor(255, 255, 255, 127);
35 | ofDrawSphere(0, 0, 0, 200);
36 | ofPopView();
37 | ofPopStyle();
38 |
39 |
40 | ofPushStyle();
41 | ofPushView();
42 | ofTranslate(100, 100);
43 | ofSetColor(ofColor::white);
44 | ofDrawBitmapString(gyroVals, 0, 0);
45 | ofDrawBitmapString(accelVals, 0, 25);
46 | ofDrawBitmapString(instructions, 0, 50);
47 | ofPopView();
48 | ofPopStyle();
49 |
50 | }
51 |
52 | //--------------------------------------------------------------
53 | void ofApp::keyPressed(int key){
54 |
55 | }
56 |
57 | //--------------------------------------------------------------
58 | void ofApp::keyReleased(int key){
59 |
60 | }
61 |
62 | //--------------------------------------------------------------
63 | void ofApp::mouseMoved(int x, int y ){
64 |
65 | }
66 |
67 | //--------------------------------------------------------------
68 | void ofApp::mouseDragged(int x, int y, int button){
69 |
70 | }
71 |
72 | //--------------------------------------------------------------
73 | void ofApp::mousePressed(int x, int y, int button){
74 |
75 | }
76 |
77 | //--------------------------------------------------------------
78 | void ofApp::mouseReleased(int x, int y, int button){
79 |
80 | }
81 |
82 | //--------------------------------------------------------------
83 | void ofApp::mouseEntered(int x, int y){
84 |
85 | }
86 |
87 | //--------------------------------------------------------------
88 | void ofApp::mouseExited(int x, int y){
89 |
90 | }
91 |
92 | //--------------------------------------------------------------
93 | void ofApp::windowResized(int w, int h){
94 |
95 | }
96 |
97 | //--------------------------------------------------------------
98 | void ofApp::gotMessage(ofMessage msg){
99 |
100 | }
101 |
102 | //--------------------------------------------------------------
103 | void ofApp::dragEvent(ofDragInfo dragInfo){
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ofxDualshock4 #
2 | ofxDualShock4 is an addon which accesses the gyroscope and accelerometer data from a PS4 controller, and uses them to estimate the controller’s rotation. Currently the addon does not allow access to the state of controller buttons / joysticks, however extending it to provide this would not be particularly challenging. The controller can also only be connected via bluetooth as it stands - again it is possible to extend to suppot a controller connected via USB.
3 |
4 | PS4 controllers present as HID devices on PCs and this addon uses the cross platform HIDAPI libary (http://www.signal11.us/oss/hidapi/) to access sensor data. Currently the addon only supports windows, but it’s just a matter of tweaking compiler settings to get it to compile on OSX / Linux.
5 |
6 | To build up an estimate estimate of controller orientation from raw accelerometer and gyroscope data, Seb Madgwick's IMU sensor fusion algorithm (http://www.x-io.co.uk/open-source-ahrs-with-x-imu/).
7 |
8 | Igloo Vision (http://www.igloovision.com/) are intrested in this technology because of its potential to enable the intuative exploration of VR environments.
9 |
10 | ## Installation ##
11 | Under windows and VS2015 the this addon can be included using the openframeworks project generator. Once you've generated your project do need to tweek project settings slightly:
12 | - Using the solution explorer navigate to /addons/ofxDualshock4/libs/HIDAPI/hid.c
13 | - Right click on hid.c in the solution explorer and select 'properties'
14 | - Check all configurations is selected in the top right hand box
15 | - In the box on the left, click C/C++ -> all options
16 | - In the search bar type 'compile as'
17 | - Select to compile as select C code
18 |
19 | For other systems you'll have to manually add the hidapi source files to the linker and flag hid.c to compile as C code but it should still work okay
20 |
21 | ## Pairing a PS4 controller to a PC via bluetooth ##
22 | To pair the device:
23 | - Open up Bluetooth settings on the computer
24 | - Press and hold the share button and the button with the PS logo simultaneously, and unti the light bar starts to flash white
25 | - At this point a game controller just pop up in the list of devices on your computer - click pair and you should be good to go!
26 |
27 | ## Limitations / TODO ##
28 | - Provide access to state of controller buttons / joysticks
29 | - Setup Linux / OSX support
30 | - Look into supporting multiple controllers (currently you can only use 1 at a time)
31 | - Implement a switch to select bluetooth / USB at setup
32 |
33 | ## Compatibility ##
34 | Written for of openFrameworks 0.9.3, haven't tested with other versions
35 |
36 | ## Troubleshooting ##
37 |
38 | ### Slow update rate ###
39 | If the addon works but there's a large delay between device updates, it's possible a Windows power setting is to blaim. To ensure this is not the case, go to cotrol panel -> power settings, and check that high performance mode is selected. If your on balanced mode you might have problems (even on a desktop PC).
--------------------------------------------------------------------------------
/addon_config.mk:
--------------------------------------------------------------------------------
1 | # All variables and this file are optional, if they are not present the PG and the
2 | # makefiles will try to parse the correct values from the file system.
3 | #
4 | # Variables that specify exclusions can use % as a wildcard to specify that anything in
5 | # that position will match. A partial path can also be specified to, for example, exclude
6 | # a whole folder from the parsed paths from the file system
7 | #
8 | # Variables can be specified using = or +=
9 | # = will clear the contents of that variable both specified from the file or the ones parsed
10 | # from the file system
11 | # += will add the values to the previous ones in the file or the ones parsed from the file
12 | # system
13 | #
14 | # The PG can be used to detect errors in this file, just create a new project with this addon
15 | # and the PG will write to the console the kind of error and in which line it is
16 |
17 | meta:
18 | ADDON_NAME = ofxDS4
19 | ADDON_DESCRIPTION = Addon for interfacing with sony DS4 controller
20 | ADDON_AUTHOR = Jack Halliday, Igloo Vision LTD
21 | ADDON_TAGS =
22 | ADDON_URL =
23 |
24 | common:
25 | # dependencies with other addons, a list of them separated by spaces
26 | # or use += in several lines
27 | # ADDON_DEPENDENCIES =
28 |
29 | # include search paths, this will be usually parsed from the file system
30 | # but if the addon or addon libraries need special search paths they can be
31 | # specified here separated by spaces or one per line using +=
32 | # ADDON_INCLUDES =
33 |
34 | # any special flag that should be passed to the compiler when using this
35 | # addon
36 | # ADDON_CFLAGS =
37 |
38 | # any special flag that should be passed to the linker when using this
39 | # addon, also used for system libraries with -lname
40 | # ADDON_LDFLAGS =
41 |
42 | # linux only, any library that should be included in the project using
43 | # pkg-config
44 | # ADDON_PKG_CONFIG_LIBRARIES =
45 |
46 | # osx/iOS only, any framework that should be included in the project
47 | # ADDON_FRAMEWORKS =
48 |
49 | # source files, these will be usually parsed from the file system looking
50 | # in the src folders in libs and the root of the addon. if your addon needs
51 | # to include files in different places or a different set of files per platform
52 | # they can be specified here
53 | # ADDON_SOURCES =
54 |
55 | # some addons need resources to be copied to the bin/data folder of the project
56 | # specify here any files that need to be copied, you can use wildcards like * and ?
57 | # ADDON_DATA =
58 |
59 | # when parsing the file system looking for libraries exclude this for all or
60 | # a specific platform
61 | # ADDON_LIBS_EXCLUDE =
62 |
63 | # when parsing the file system looking for sources exclude this for all or
64 | # a specific platform
65 | # ADDON_SOURCES_EXCLUDE =
66 |
67 | # when parsing the file system looking for include paths exclude this for all or
68 | # a specific platform
69 | # ADDON_INCLUDES_EXCLUDE =
70 |
71 | vs:
72 | # source files, these will be usually parsed from the file system looking
73 | # in the src folders in libs and the root of the addon. if your addon needs
74 | # to include files in different places or a different set of files per platform
75 | # they can be specified here
76 |
77 | # include search paths, this will be usually parsed from the file system
78 | # but if the addon or addon libraries need special search paths they can be
79 | # specified here separated by spaces or one per line using +=
80 | ADDON_INCLUDES += libs/HIDAPI
81 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/src/ofxDS4.h:
--------------------------------------------------------------------------------
1 | // App to phrase IMU data supplied via HID from a PS4 controller, and estimate the orientation of the controller
2 | // Written by Jack Halliday (jack.halliday12@imperial.ac.uk) for Igloo Vision LTD (http://www.igloovision.com/).
3 |
4 | #pragma once
5 | #include "hidapi.h"
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include "ofMain.h"
12 |
13 | //**************************** DEVICE ID ****************************//
14 | /*
15 | // Address the correct HID using it's vendor and product IDs
16 | */
17 | #define VENDOR_ID 1356 // The vendor & product ID of HID device to be used:
18 | #define PRODUCT_ID 1476 // (Currently setup for PS4 controller)
19 |
20 | //**************************** SENSOR DETAILS ****************************//
21 | /*
22 | // Values for the ranges of sensors, and factors to map sensor units into SI units
23 | // These have been worked out from a combination of guess work and info contained
24 | // at:
25 | // 1) https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMI055-DS000-08.pdf (IMU datasheet)
26 | // 2) http://gamedev.stackexchange.com/questions/87106/accessing-dualshock-4-motion-sensor-in-windows-ideally-unity
27 | // 3) https://github.com/Flafla2/Unity-DS4
28 | */
29 | #define ACC_FACTOR 9.8 / 8100. // No' of m/s2 per arb unit of acceleration
30 |
31 | #define GYRO_FACTOR 1. / 1024. // No of rad/s per arb unit of angular velocity
32 | #define GYRO_ERROR 0.001 // Estimated error on gyroscope measurement in rad/s
33 |
34 | #define TIME_FACTOR 0.00125 / 188. // No' of secs per arb unit of time
35 |
36 |
37 | //**************************** MEMORY ADDRESSES ****************************//
38 | /*
39 | // The location of relevant bytes within the HID read buffer. All the motion sensors
40 | // use 16bit memory space, and values here refer to the byte with smaller index. Note that
41 | // byte order is reversed for all of the IMU outputs. Values here have come from:
42 | // 1) https://gist.github.com/johndrinkwater/7708901
43 | // 2) http://eleccelerator.com/wiki/index.php?title=DualShock_4
44 | // 3) https://github.com/ehd/node-ds4
45 | // The coordinate sys used by the IMU is right handed, and oriented as shown in
46 | // - \GyroProject\docs\DS4-axis.pdf
47 | */
48 |
49 |
50 | // Indices for controller connected via USB
51 | #define GX_INDEX_USB 14
52 | #define GY_INDEX_USB 16
53 | #define GZ_INDEX_USB 18
54 | #define DX_INDEX_USB 20
55 | #define DY_INDEX_USB 22
56 | #define DZ_INDEX_USB 24
57 | #define TIME_INDEX_USB 11 //Index of the timestamp byte
58 |
59 | // Indices for controller connected via Bluetooth
60 | #define GX_INDEX_BLUETOOTH 16
61 | #define GY_INDEX_BLUETOOTH 18
62 | #define GZ_INDEX_BLUETOOTH 20
63 | #define DX_INDEX_BLUETOOTH 22
64 | #define DY_INDEX_BLUETOOTH 24
65 | #define DZ_INDEX_BLUETOOTH 26
66 | #define TIME_INDEX_BLUETOOTH 13
67 |
68 |
69 | //**************************** ALGO' SETTING ****************************//
70 | /*
71 | // We use Seb Madgwick's IMU sensor fusion algo' to couple the gro' and accelerometer
72 | // sensor readings from the controller. For more detail, refer to Seb's website:
73 | // http://www.x-io.co.uk/open-source-ahrs-with-x-imu/.
74 | //
75 | // My understanding of what Beta does is to dictate how much attention is paid to the
76 | // accelerometer data in the sensor fusion process. This define specifies it's default
77 | // value - it can also be changed via a set method within the class.
78 | */
79 | #define BETA 0.5
80 |
81 | enum ConectionMode {USB, BLUETOOTH};
82 |
83 | class ofxDS4{
84 |
85 | public:
86 | void setup(ConectionMode mode = BLUETOOTH);
87 | void update();
88 | void close();
89 | void setBeta(float beta) { _beta = beta; }
90 | bool getDS4Found() { return _hidDevice; }
91 |
92 | int getRawGyroX() { return _readInt16LE(_buffer, GX_Index); }
93 | int getRawGyroY() { return _readInt16LE(_buffer, GY_Index); }
94 | int getRawGyroZ() { return _readInt16LE(_buffer, GZ_Index); }
95 |
96 | int getRawAccelX() { return _readInt16LE(_buffer, DX_Index); }
97 | int getRawAccelY() { return _readInt16LE(_buffer, DY_Index); }
98 | int getRawAccelZ() { return _readInt16LE(_buffer, DZ_Index); }
99 |
100 | int getRawTimestamp() { return _readInt16LE(_buffer, TIME_Index); }
101 | float getTimestamp() { return float(getRawTimestamp() * TIME_FACTOR); }
102 | float getTime() { return _time; }
103 |
104 | float getGyroX() { return float( getRawGyroX() ) * GYRO_FACTOR; }
105 | float getGyroY() { return float ( getRawGyroY() ) * GYRO_FACTOR; }
106 | float getGyroZ() { return float ( getRawGyroZ() ) * GYRO_FACTOR; }
107 |
108 | float getAccelX() { return getRawAccelX() * ACC_FACTOR; }
109 | float getAccelY() { return getRawAccelY() * ACC_FACTOR; }
110 | float getAccelZ() { return getRawAccelZ() * ACC_FACTOR; }
111 |
112 | ofVec3f getGyro() { return ofVec3f(getGyroX(), getGyroY(), getGyroZ()); }
113 | ofVec3f getAccel() { return ofVec3f(getAccelX(), getAccelY(), getAccelZ()); }
114 | ofQuaternion getQuaternions();
115 |
116 |
117 | int getArbPair(int index) { return _readInt16LE(_buffer, index); }
118 | int getArbSingle(int index) { return _readInt8(_buffer, index); }
119 |
120 | private:
121 | int _readInt16LE(unsigned char buffer[], int index);
122 | int _readInt16LEUnsigned(unsigned char buffer[], int index);
123 | int _readInt8(unsigned char buffer[], int index);
124 |
125 | void _updateHID();
126 | void _updateTimeStep();
127 | void _updateAHRS(float dt, float gx, float gy, float gz, float ax, float ay, float az);
128 |
129 | hid_device* _hidDevice;
130 | unsigned char _buffer[256];
131 | int _resultFlag;
132 |
133 | float _previousTimestamp;
134 | float _timeStep;
135 | float _time;
136 |
137 | float _quaternion[4];
138 | float _beta;
139 |
140 | int GX_Index;
141 | int GY_Index;
142 | int GZ_Index;
143 | int DX_Index;
144 | int DY_Index;
145 | int DZ_Index;
146 | int TIME_Index;
147 | };
148 |
--------------------------------------------------------------------------------
/src/ofxDS4.cpp:
--------------------------------------------------------------------------------
1 | #include "ofxDS4.h"
2 |
3 | void ofxDS4::setup(ConectionMode mode) {
4 |
5 | if (mode == USB) {
6 | GX_Index = GX_INDEX_USB;
7 | GY_Index = GY_INDEX_USB;
8 | GZ_Index = GZ_INDEX_USB;
9 | DX_Index = DX_INDEX_USB;
10 | DY_Index = DY_INDEX_USB;
11 | DZ_Index = DZ_INDEX_USB;
12 | TIME_Index = TIME_INDEX_USB;
13 | }
14 | else {
15 | GX_Index = GX_INDEX_BLUETOOTH;
16 | GY_Index = GY_INDEX_BLUETOOTH;
17 | GZ_Index = GZ_INDEX_BLUETOOTH;
18 | DX_Index = DX_INDEX_BLUETOOTH;
19 | DY_Index = DY_INDEX_BLUETOOTH;
20 | DZ_Index = DZ_INDEX_BLUETOOTH;
21 | TIME_Index = TIME_INDEX_BLUETOOTH;
22 | }
23 |
24 | _hidDevice = hid_open(VENDOR_ID, PRODUCT_ID, NULL);
25 | if (!_hidDevice) {
26 | ofLogError("Unable to find a Gyro");
27 | }
28 |
29 | else {
30 | _updateHID();
31 | _previousTimestamp = getTimestamp();
32 | _buffer[0] = 0x2;
33 | hid_get_feature_report(_hidDevice, _buffer, sizeof(_buffer)); // When you first pair a controller via bluetooth
34 | if (_resultFlag < 0) { ofLogNotice("unable to sent report"); }
35 | _time = 0.; // starts in low energy mode where IMU data not sent
36 | _quaternion[0] = 1.0; // requesting feature report puts it into full mode. Source:
37 | _quaternion[1] = 0.; // (https://github.com/torvalds/linux/blob/master/drivers/hid/hid-sony.c)
38 | _quaternion[2] = 0.;
39 | _quaternion[3] = 0.;
40 | _beta = BETA;
41 | }
42 | }
43 |
44 | void ofxDS4::update() {
45 | if (_hidDevice) {
46 | ofResetElapsedTimeCounter();
47 | _updateHID();
48 | _updateTimeStep();
49 | ofVec3f a = getAccel();
50 | ofVec3f g = getGyro();
51 | _updateAHRS(_timeStep, g.x, g.y, g.z, a.x, a.y, a.z);
52 | }
53 | }
54 |
55 | void ofxDS4::close()
56 | {
57 | if (_hidDevice) {
58 | hid_close(_hidDevice);
59 | hid_exit();
60 | }
61 | }
62 |
63 | ofQuaternion ofxDS4::getQuaternions()
64 | {
65 | ofQuaternion quaternion;
66 | quaternion.set(_quaternion[1], _quaternion[2], _quaternion[3], _quaternion[0]);
67 | return quaternion;
68 | }
69 |
70 | int ofxDS4::_readInt16LE(unsigned char buffer[], int index){
71 |
72 | unsigned short highByte = buffer[index]; //Byte order swapped in ofxDS4 io buffer
73 | unsigned short lowByte = buffer[index++];
74 | short combined = lowByte << 8 | highByte ;
75 | return int(combined);
76 | }
77 |
78 | int ofxDS4::_readInt16LEUnsigned(unsigned char buffer[], int index)
79 | {
80 | unsigned short highByte = buffer[index];
81 | unsigned short lowByte = buffer[index++];
82 | unsigned short combined = lowByte << 8 | highByte;
83 | return int(combined);
84 | }
85 |
86 | int ofxDS4::_readInt8(unsigned char buffer[], int index)
87 | {
88 | return int(buffer[index]);
89 | }
90 |
91 | void ofxDS4::_updateHID()
92 | {
93 | _resultFlag = 0;
94 | while (_resultFlag == 0) {
95 | _resultFlag = hid_read(_hidDevice, _buffer, sizeof(_buffer));
96 | if (_resultFlag < 0) ofLogError("Unable to read data from the Gyro");
97 | }
98 | }
99 |
100 | void ofxDS4::_updateTimeStep()
101 | {
102 | float timestamp = getTimestamp();
103 | float dif = timestamp - _previousTimestamp;
104 | if (dif > 0) { _timeStep = dif; }
105 | else { _timeStep = 0.00125; }
106 | _previousTimestamp = timestamp;
107 | _time += _timeStep;
108 | }
109 |
110 | void ofxDS4::_updateAHRS(float dt, float gx, float gy, float gz, float ax, float ay, float az)
111 | {
112 | /*
113 | // This is an implementation of Seb Madgwick's AHRS filter.
114 | // (http://www.x-io.co.uk/open-source-imu-and-ahrs-algorithms/)
115 | // TODO: This assumes that the initial orientation of the controller
116 | // is such that gravity points in the -z direction, but in reality the
117 | // controller is usually initially oriented so that gravity points in the
118 | // -y direction. This is why the sphere in the test app processes pi/2 radians
119 | // about the x axis in the ~5 secs after the test app is started. It's not ideal
120 | // and could be fixed, either by re-writing the algo' with the correct
121 | // initial condition, or (possibly) by altering the axis order of the controller.
122 | */
123 |
124 | float recipNorm;
125 | float s0, s1, s2, s3;
126 | float qDot1, qDot2, qDot3, qDot4;
127 | float _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2, _8q1, _8q2, q0q0, q1q1, q2q2, q3q3;
128 |
129 | // Rate of change of quaternion from gyroscope
130 | qDot1 = 0.5f * (-_quaternion[1] * gx - _quaternion[2] * gy - _quaternion[3] * gz);
131 | qDot2 = 0.5f * (_quaternion[0] * gx + _quaternion[2] * gz - _quaternion[3] * gy);
132 | qDot3 = 0.5f * (_quaternion[0] * gy - _quaternion[1] * gz + _quaternion[3] * gx);
133 | qDot4 = 0.5f * (_quaternion[0] * gz + _quaternion[1] * gy - _quaternion[2] * gx);
134 |
135 | // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation)
136 | if (!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) {
137 |
138 | // Normalise accelerometer measurement
139 | recipNorm = 1. / sqrt(ax * ax + ay * ay + az * az);
140 | ax *= recipNorm;
141 | ay *= recipNorm;
142 | az *= recipNorm;
143 |
144 | // Auxiliary variables to avoid repeated arithmetic
145 | _2q0 = 2.0f * _quaternion[0];
146 | _2q1 = 2.0f * _quaternion[1];
147 | _2q2 = 2.0f * _quaternion[2];
148 | _2q3 = 2.0f * _quaternion[3];
149 | _4q0 = 4.0f * _quaternion[0];
150 | _4q1 = 4.0f * _quaternion[1];
151 | _4q2 = 4.0f * _quaternion[2];
152 | _8q1 = 8.0f * _quaternion[1];
153 | _8q2 = 8.0f * _quaternion[2];
154 | q0q0 = _quaternion[0] * _quaternion[0];
155 | q1q1 = _quaternion[1] * _quaternion[1];
156 | q2q2 = _quaternion[2] * _quaternion[2];
157 | q3q3 = _quaternion[3] * _quaternion[3];
158 |
159 | // Gradient decent algorithm corrective step
160 | s0 = _4q0 * q2q2 + _2q2 * ax + _4q0 * q1q1 - _2q1 * ay;
161 | s1 = _4q1 * q3q3 - _2q3 * ax + 4.0f * q0q0 * _quaternion[1] - _2q0 * ay - _4q1 + _8q1 * q1q1 + _8q1 * q2q2 + _4q1 * az;
162 | s2 = 4.0f * q0q0 * _quaternion[2] + _2q0 * ax + _4q2 * q3q3 - _2q3 * ay - _4q2 + _8q2 * q1q1 + _8q2 * q2q2 + _4q2 * az;
163 | s3 = 4.0f * q1q1 * _quaternion[3] - _2q1 * ax + 4.0f * q2q2 * _quaternion[3] - _2q2 * ay;
164 | recipNorm = 1. / sqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude
165 | s0 *= recipNorm;
166 | s1 *= recipNorm;
167 | s2 *= recipNorm;
168 | s3 *= recipNorm;
169 |
170 | // Apply feedback step
171 | qDot1 -= _beta * s0;
172 | qDot2 -= _beta * s1;
173 | qDot3 -= _beta * s2;
174 | qDot4 -= _beta * s3;
175 | }
176 |
177 | // Integrate rate of change of quaternion to yield quaternion
178 | _quaternion[0] += qDot1 * dt;
179 | _quaternion[1] += qDot2 * dt;
180 | _quaternion[2] += qDot3 * dt;
181 | _quaternion[3] += qDot4 * dt;
182 |
183 | // Normalise quaternion
184 | recipNorm = 1. / sqrt(_quaternion[0] * _quaternion[0] + _quaternion[1] * _quaternion[1] + _quaternion[2] * _quaternion[2] + _quaternion[3] * _quaternion[3]);
185 | _quaternion[0] *= recipNorm;
186 | _quaternion[0] *= recipNorm;
187 | _quaternion[2] *= recipNorm;
188 | _quaternion[3] *= recipNorm;
189 | }
190 |
191 |
--------------------------------------------------------------------------------
/libs/HIDAPI/hidapi.h:
--------------------------------------------------------------------------------
1 | /*******************************************************
2 | HIDAPI - Multi-Platform library for
3 | communication with HID devices.
4 |
5 | Alan Ott
6 | Signal 11 Software
7 |
8 | 8/22/2009
9 |
10 | Copyright 2009, All Rights Reserved.
11 |
12 | At the discretion of the user of this library,
13 | this software may be licensed under the terms of the
14 | GNU General Public License v3, a BSD-Style license, or the
15 | original HIDAPI license as outlined in the LICENSE.txt,
16 | LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
17 | files located at the root of the source distribution.
18 | These files may also be found in the public source
19 | code repository located at:
20 | http://github.com/signal11/hidapi .
21 | ********************************************************/
22 |
23 | /** @file
24 | * @defgroup API hidapi API
25 | */
26 |
27 | #ifndef HIDAPI_H__
28 | #define HIDAPI_H__
29 |
30 | #include
31 |
32 | #ifdef _WIN32
33 | #define HID_API_EXPORT __declspec(dllexport)
34 | #define HID_API_CALL
35 | #else
36 | #define HID_API_EXPORT /**< API export macro */
37 | #define HID_API_CALL /**< API call macro */
38 | #endif
39 |
40 | #define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/
41 |
42 | #ifdef __cplusplus
43 | extern "C" {
44 | #endif
45 | struct hid_device_;
46 | typedef struct hid_device_ hid_device; /**< opaque hidapi structure */
47 |
48 | /** hidapi info structure */
49 | struct hid_device_info {
50 | /** Platform-specific device path */
51 | char *path;
52 | /** Device Vendor ID */
53 | unsigned short vendor_id;
54 | /** Device Product ID */
55 | unsigned short product_id;
56 | /** Serial Number */
57 | wchar_t *serial_number;
58 | /** Device Release Number in binary-coded decimal,
59 | also known as Device Version Number */
60 | unsigned short release_number;
61 | /** Manufacturer String */
62 | wchar_t *manufacturer_string;
63 | /** Product string */
64 | wchar_t *product_string;
65 | /** Usage Page for this Device/Interface
66 | (Windows/Mac only). */
67 | unsigned short usage_page;
68 | /** Usage for this Device/Interface
69 | (Windows/Mac only).*/
70 | unsigned short usage;
71 | /** The USB interface which this logical device
72 | represents. Valid on both Linux implementations
73 | in all cases, and valid on the Windows implementation
74 | only if the device contains more than one interface. */
75 | int interface_number;
76 |
77 | /** Pointer to the next device */
78 | struct hid_device_info *next;
79 | };
80 |
81 |
82 | /** @brief Initialize the HIDAPI library.
83 |
84 | This function initializes the HIDAPI library. Calling it is not
85 | strictly necessary, as it will be called automatically by
86 | hid_enumerate() and any of the hid_open_*() functions if it is
87 | needed. This function should be called at the beginning of
88 | execution however, if there is a chance of HIDAPI handles
89 | being opened by different threads simultaneously.
90 |
91 | @ingroup API
92 |
93 | @returns
94 | This function returns 0 on success and -1 on error.
95 | */
96 | int HID_API_EXPORT HID_API_CALL hid_init(void);
97 |
98 | /** @brief Finalize the HIDAPI library.
99 |
100 | This function frees all of the static data associated with
101 | HIDAPI. It should be called at the end of execution to avoid
102 | memory leaks.
103 |
104 | @ingroup API
105 |
106 | @returns
107 | This function returns 0 on success and -1 on error.
108 | */
109 | int HID_API_EXPORT HID_API_CALL hid_exit(void);
110 |
111 | /** @brief Enumerate the HID Devices.
112 |
113 | This function returns a linked list of all the HID devices
114 | attached to the system which match vendor_id and product_id.
115 | If @p vendor_id is set to 0 then any vendor matches.
116 | If @p product_id is set to 0 then any product matches.
117 | If @p vendor_id and @p product_id are both set to 0, then
118 | all HID devices will be returned.
119 |
120 | @ingroup API
121 | @param vendor_id The Vendor ID (VID) of the types of device
122 | to open.
123 | @param product_id The Product ID (PID) of the types of
124 | device to open.
125 |
126 | @returns
127 | This function returns a pointer to a linked list of type
128 | struct #hid_device, containing information about the HID devices
129 | attached to the system, or NULL in the case of failure. Free
130 | this linked list by calling hid_free_enumeration().
131 | */
132 | struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id);
133 |
134 | /** @brief Free an enumeration Linked List
135 |
136 | This function frees a linked list created by hid_enumerate().
137 |
138 | @ingroup API
139 | @param devs Pointer to a list of struct_device returned from
140 | hid_enumerate().
141 | */
142 | void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs);
143 |
144 | /** @brief Open a HID device using a Vendor ID (VID), Product ID
145 | (PID) and optionally a serial number.
146 |
147 | If @p serial_number is NULL, the first device with the
148 | specified VID and PID is opened.
149 |
150 | @ingroup API
151 | @param vendor_id The Vendor ID (VID) of the device to open.
152 | @param product_id The Product ID (PID) of the device to open.
153 | @param serial_number The Serial Number of the device to open
154 | (Optionally NULL).
155 |
156 | @returns
157 | This function returns a pointer to a #hid_device object on
158 | success or NULL on failure.
159 | */
160 | HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number);
161 |
162 | /** @brief Open a HID device by its path name.
163 |
164 | The path name be determined by calling hid_enumerate(), or a
165 | platform-specific path name can be used (eg: /dev/hidraw0 on
166 | Linux).
167 |
168 | @ingroup API
169 | @param path The path name of the device to open
170 |
171 | @returns
172 | This function returns a pointer to a #hid_device object on
173 | success or NULL on failure.
174 | */
175 | HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path);
176 |
177 | /** @brief Write an Output report to a HID device.
178 |
179 | The first byte of @p data[] must contain the Report ID. For
180 | devices which only support a single report, this must be set
181 | to 0x0. The remaining bytes contain the report data. Since
182 | the Report ID is mandatory, calls to hid_write() will always
183 | contain one more byte than the report contains. For example,
184 | if a hid report is 16 bytes long, 17 bytes must be passed to
185 | hid_write(), the Report ID (or 0x0, for devices with a
186 | single report), followed by the report data (16 bytes). In
187 | this example, the length passed in would be 17.
188 |
189 | hid_write() will send the data on the first OUT endpoint, if
190 | one exists. If it does not, it will send the data through
191 | the Control Endpoint (Endpoint 0).
192 |
193 | @ingroup API
194 | @param device A device handle returned from hid_open().
195 | @param data The data to send, including the report number as
196 | the first byte.
197 | @param length The length in bytes of the data to send.
198 |
199 | @returns
200 | This function returns the actual number of bytes written and
201 | -1 on error.
202 | */
203 | int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length);
204 |
205 | /** @brief Read an Input report from a HID device with timeout.
206 |
207 | Input reports are returned
208 | to the host through the INTERRUPT IN endpoint. The first byte will
209 | contain the Report number if the device uses numbered reports.
210 |
211 | @ingroup API
212 | @param device A device handle returned from hid_open().
213 | @param data A buffer to put the read data into.
214 | @param length The number of bytes to read. For devices with
215 | multiple reports, make sure to read an extra byte for
216 | the report number.
217 | @param milliseconds timeout in milliseconds or -1 for blocking wait.
218 |
219 | @returns
220 | This function returns the actual number of bytes read and
221 | -1 on error. If no packet was available to be read within
222 | the timeout period, this function returns 0.
223 | */
224 | int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds);
225 |
226 | /** @brief Read an Input report from a HID device.
227 |
228 | Input reports are returned
229 | to the host through the INTERRUPT IN endpoint. The first byte will
230 | contain the Report number if the device uses numbered reports.
231 |
232 | @ingroup API
233 | @param device A device handle returned from hid_open().
234 | @param data A buffer to put the read data into.
235 | @param length The number of bytes to read. For devices with
236 | multiple reports, make sure to read an extra byte for
237 | the report number.
238 |
239 | @returns
240 | This function returns the actual number of bytes read and
241 | -1 on error. If no packet was available to be read and
242 | the handle is in non-blocking mode, this function returns 0.
243 | */
244 | int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length);
245 |
246 | /** @brief Set the device handle to be non-blocking.
247 |
248 | In non-blocking mode calls to hid_read() will return
249 | immediately with a value of 0 if there is no data to be
250 | read. In blocking mode, hid_read() will wait (block) until
251 | there is data to read before returning.
252 |
253 | Nonblocking can be turned on and off at any time.
254 |
255 | @ingroup API
256 | @param device A device handle returned from hid_open().
257 | @param nonblock enable or not the nonblocking reads
258 | - 1 to enable nonblocking
259 | - 0 to disable nonblocking.
260 |
261 | @returns
262 | This function returns 0 on success and -1 on error.
263 | */
264 | int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock);
265 |
266 | /** @brief Send a Feature report to the device.
267 |
268 | Feature reports are sent over the Control endpoint as a
269 | Set_Report transfer. The first byte of @p data[] must
270 | contain the Report ID. For devices which only support a
271 | single report, this must be set to 0x0. The remaining bytes
272 | contain the report data. Since the Report ID is mandatory,
273 | calls to hid_send_feature_report() will always contain one
274 | more byte than the report contains. For example, if a hid
275 | report is 16 bytes long, 17 bytes must be passed to
276 | hid_send_feature_report(): the Report ID (or 0x0, for
277 | devices which do not use numbered reports), followed by the
278 | report data (16 bytes). In this example, the length passed
279 | in would be 17.
280 |
281 | @ingroup API
282 | @param device A device handle returned from hid_open().
283 | @param data The data to send, including the report number as
284 | the first byte.
285 | @param length The length in bytes of the data to send, including
286 | the report number.
287 |
288 | @returns
289 | This function returns the actual number of bytes written and
290 | -1 on error.
291 | */
292 | int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length);
293 |
294 | /** @brief Get a feature report from a HID device.
295 |
296 | Set the first byte of @p data[] to the Report ID of the
297 | report to be read. Make sure to allow space for this
298 | extra byte in @p data[]. Upon return, the first byte will
299 | still contain the Report ID, and the report data will
300 | start in data[1].
301 |
302 | @ingroup API
303 | @param device A device handle returned from hid_open().
304 | @param data A buffer to put the read data into, including
305 | the Report ID. Set the first byte of @p data[] to the
306 | Report ID of the report to be read, or set it to zero
307 | if your device does not use numbered reports.
308 | @param length The number of bytes to read, including an
309 | extra byte for the report ID. The buffer can be longer
310 | than the actual report.
311 |
312 | @returns
313 | This function returns the number of bytes read plus
314 | one for the report ID (which is still in the first
315 | byte), or -1 on error.
316 | */
317 | int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length);
318 |
319 | /** @brief Close a HID device.
320 |
321 | @ingroup API
322 | @param device A device handle returned from hid_open().
323 | */
324 | void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device);
325 |
326 | /** @brief Get The Manufacturer String from a HID device.
327 |
328 | @ingroup API
329 | @param device A device handle returned from hid_open().
330 | @param string A wide string buffer to put the data into.
331 | @param maxlen The length of the buffer in multiples of wchar_t.
332 |
333 | @returns
334 | This function returns 0 on success and -1 on error.
335 | */
336 | int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen);
337 |
338 | /** @brief Get The Product String from a HID device.
339 |
340 | @ingroup API
341 | @param device A device handle returned from hid_open().
342 | @param string A wide string buffer to put the data into.
343 | @param maxlen The length of the buffer in multiples of wchar_t.
344 |
345 | @returns
346 | This function returns 0 on success and -1 on error.
347 | */
348 | int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen);
349 |
350 | /** @brief Get The Serial Number String from a HID device.
351 |
352 | @ingroup API
353 | @param device A device handle returned from hid_open().
354 | @param string A wide string buffer to put the data into.
355 | @param maxlen The length of the buffer in multiples of wchar_t.
356 |
357 | @returns
358 | This function returns 0 on success and -1 on error.
359 | */
360 | int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen);
361 |
362 | /** @brief Get a string from a HID device, based on its string index.
363 |
364 | @ingroup API
365 | @param device A device handle returned from hid_open().
366 | @param string_index The index of the string to get.
367 | @param string A wide string buffer to put the data into.
368 | @param maxlen The length of the buffer in multiples of wchar_t.
369 |
370 | @returns
371 | This function returns 0 on success and -1 on error.
372 | */
373 | int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen);
374 |
375 | /** @brief Get a string describing the last error which occurred.
376 |
377 | @ingroup API
378 | @param device A device handle returned from hid_open().
379 |
380 | @returns
381 | This function returns a string containing the last error
382 | which occurred or NULL if none has occurred.
383 | */
384 | HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device);
385 |
386 | #ifdef __cplusplus
387 | }
388 | #endif
389 |
390 | #endif
391 |
392 |
--------------------------------------------------------------------------------
/libs/HIDAPI/hid.c:
--------------------------------------------------------------------------------
1 | /*******************************************************
2 | HIDAPI - Multi-Platform library for
3 | communication with HID devices.
4 |
5 | Alan Ott
6 | Signal 11 Software
7 |
8 | 8/22/2009
9 |
10 | Copyright 2009, All Rights Reserved.
11 |
12 | At the discretion of the user of this library,
13 | this software may be licensed under the terms of the
14 | GNU General Public License v3, a BSD-Style license, or the
15 | original HIDAPI license as outlined in the LICENSE.txt,
16 | LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
17 | files located at the root of the source distribution.
18 | These files may also be found in the public source
19 | code repository located at:
20 | http://github.com/signal11/hidapi .
21 | ********************************************************/
22 |
23 | #include
24 |
25 | #ifndef _NTDEF_
26 | typedef LONG NTSTATUS;
27 | #endif
28 |
29 | #ifdef __MINGW32__
30 | #include
31 | #include
32 | #endif
33 |
34 | #ifdef __CYGWIN__
35 | #include
36 | #define _wcsdup wcsdup
37 | #endif
38 |
39 | /*#define HIDAPI_USE_DDK*/
40 |
41 | #ifdef __cplusplus
42 | extern "C" {
43 | #endif
44 | #include
45 | #include
46 | #ifdef HIDAPI_USE_DDK
47 | #include
48 | #endif
49 |
50 | /* Copied from inc/ddk/hidclass.h, part of the Windows DDK. */
51 | #define HID_OUT_CTL_CODE(id) \
52 | CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
53 | #define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100)
54 |
55 | #ifdef __cplusplus
56 | } /* extern "C" */
57 | #endif
58 |
59 | #include
60 | #include
61 |
62 |
63 | #include "hidapi.h"
64 |
65 | #ifdef _MSC_VER
66 | /* Thanks Microsoft, but I know how to use strncpy(). */
67 | #pragma warning(disable:4996)
68 | #endif
69 |
70 | #ifdef __cplusplus
71 | extern "C" {
72 | #endif
73 |
74 | #ifndef HIDAPI_USE_DDK
75 | /* Since we're not building with the DDK, and the HID header
76 | files aren't part of the SDK, we have to define all this
77 | stuff here. In lookup_functions(), the function pointers
78 | defined below are set. */
79 | typedef struct _HIDD_ATTRIBUTES{
80 | ULONG Size;
81 | USHORT VendorID;
82 | USHORT ProductID;
83 | USHORT VersionNumber;
84 | } HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
85 |
86 | typedef USHORT USAGE;
87 | typedef struct _HIDP_CAPS {
88 | USAGE Usage;
89 | USAGE UsagePage;
90 | USHORT InputReportByteLength;
91 | USHORT OutputReportByteLength;
92 | USHORT FeatureReportByteLength;
93 | USHORT Reserved[17];
94 | USHORT fields_not_used_by_hidapi[10];
95 | } HIDP_CAPS, *PHIDP_CAPS;
96 | typedef void* PHIDP_PREPARSED_DATA;
97 | #define HIDP_STATUS_SUCCESS 0x110000
98 |
99 | typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib);
100 | typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len);
101 | typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
102 | typedef BOOLEAN (__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
103 | typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length);
104 | typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length);
105 | typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len);
106 | typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data);
107 | typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data);
108 | typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, HIDP_CAPS *caps);
109 | typedef BOOLEAN (__stdcall *HidD_SetNumInputBuffers_)(HANDLE handle, ULONG number_buffers);
110 |
111 | static HidD_GetAttributes_ HidD_GetAttributes;
112 | static HidD_GetSerialNumberString_ HidD_GetSerialNumberString;
113 | static HidD_GetManufacturerString_ HidD_GetManufacturerString;
114 | static HidD_GetProductString_ HidD_GetProductString;
115 | static HidD_SetFeature_ HidD_SetFeature;
116 | static HidD_GetFeature_ HidD_GetFeature;
117 | static HidD_GetIndexedString_ HidD_GetIndexedString;
118 | static HidD_GetPreparsedData_ HidD_GetPreparsedData;
119 | static HidD_FreePreparsedData_ HidD_FreePreparsedData;
120 | static HidP_GetCaps_ HidP_GetCaps;
121 | static HidD_SetNumInputBuffers_ HidD_SetNumInputBuffers;
122 |
123 | static HMODULE lib_handle = NULL;
124 | static BOOLEAN initialized = FALSE;
125 | #endif /* HIDAPI_USE_DDK */
126 |
127 | struct hid_device_ {
128 | HANDLE device_handle;
129 | BOOL blocking;
130 | USHORT output_report_length;
131 | size_t input_report_length;
132 | void *last_error_str;
133 | DWORD last_error_num;
134 | BOOL read_pending;
135 | char *read_buf;
136 | OVERLAPPED ol;
137 | };
138 |
139 | static hid_device *new_hid_device()
140 | {
141 | hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
142 | dev->device_handle = INVALID_HANDLE_VALUE;
143 | dev->blocking = TRUE;
144 | dev->output_report_length = 0;
145 | dev->input_report_length = 0;
146 | dev->last_error_str = NULL;
147 | dev->last_error_num = 0;
148 | dev->read_pending = FALSE;
149 | dev->read_buf = NULL;
150 | memset(&dev->ol, 0, sizeof(dev->ol));
151 | dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*inital state f=nonsignaled*/, NULL);
152 |
153 | return dev;
154 | }
155 |
156 | static void free_hid_device(hid_device *dev)
157 | {
158 | CloseHandle(dev->ol.hEvent);
159 | CloseHandle(dev->device_handle);
160 | LocalFree(dev->last_error_str);
161 | free(dev->read_buf);
162 | free(dev);
163 | }
164 |
165 | static void register_error(hid_device *device, const char *op)
166 | {
167 | WCHAR *ptr, *msg;
168 |
169 | FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
170 | FORMAT_MESSAGE_FROM_SYSTEM |
171 | FORMAT_MESSAGE_IGNORE_INSERTS,
172 | NULL,
173 | GetLastError(),
174 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
175 | (LPVOID)&msg, 0/*sz*/,
176 | NULL);
177 |
178 | /* Get rid of the CR and LF that FormatMessage() sticks at the
179 | end of the message. Thanks Microsoft! */
180 | ptr = msg;
181 | while (*ptr) {
182 | if (*ptr == '\r') {
183 | *ptr = 0x0000;
184 | break;
185 | }
186 | ptr++;
187 | }
188 |
189 | /* Store the message off in the Device entry so that
190 | the hid_error() function can pick it up. */
191 | LocalFree(device->last_error_str);
192 | device->last_error_str = msg;
193 | }
194 |
195 | #ifndef HIDAPI_USE_DDK
196 | static int lookup_functions()
197 | {
198 | lib_handle = LoadLibraryA("hid.dll");
199 | if (lib_handle) {
200 | #define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1;
201 | RESOLVE(HidD_GetAttributes);
202 | RESOLVE(HidD_GetSerialNumberString);
203 | RESOLVE(HidD_GetManufacturerString);
204 | RESOLVE(HidD_GetProductString);
205 | RESOLVE(HidD_SetFeature);
206 | RESOLVE(HidD_GetFeature);
207 | RESOLVE(HidD_GetIndexedString);
208 | RESOLVE(HidD_GetPreparsedData);
209 | RESOLVE(HidD_FreePreparsedData);
210 | RESOLVE(HidP_GetCaps);
211 | RESOLVE(HidD_SetNumInputBuffers);
212 | #undef RESOLVE
213 | }
214 | else
215 | return -1;
216 |
217 | return 0;
218 | }
219 | #endif
220 |
221 | static HANDLE open_device(const char *path, BOOL enumerate)
222 | {
223 | HANDLE handle;
224 | DWORD desired_access = (enumerate)? 0: (GENERIC_WRITE | GENERIC_READ);
225 | DWORD share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE;
226 |
227 | handle = CreateFileA(path,
228 | desired_access,
229 | share_mode,
230 | NULL,
231 | OPEN_EXISTING,
232 | FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/
233 | 0);
234 |
235 | return handle;
236 | }
237 |
238 | int HID_API_EXPORT hid_init(void)
239 | {
240 | #ifndef HIDAPI_USE_DDK
241 | if (!initialized) {
242 | if (lookup_functions() < 0) {
243 | hid_exit();
244 | return -1;
245 | }
246 | initialized = TRUE;
247 | }
248 | #endif
249 | return 0;
250 | }
251 |
252 | int HID_API_EXPORT hid_exit(void)
253 | {
254 | #ifndef HIDAPI_USE_DDK
255 | if (lib_handle)
256 | FreeLibrary(lib_handle);
257 | lib_handle = NULL;
258 | initialized = FALSE;
259 | #endif
260 | return 0;
261 | }
262 |
263 | struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)
264 | {
265 | BOOL res;
266 | struct hid_device_info *root = NULL; /* return object */
267 | struct hid_device_info *cur_dev = NULL;
268 |
269 | /* Windows objects for interacting with the driver. */
270 | GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} };
271 | SP_DEVINFO_DATA devinfo_data;
272 | SP_DEVICE_INTERFACE_DATA device_interface_data;
273 | SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL;
274 | HDEVINFO device_info_set = INVALID_HANDLE_VALUE;
275 | int device_index = 0;
276 | int i;
277 |
278 | if (hid_init() < 0)
279 | return NULL;
280 |
281 | /* Initialize the Windows objects. */
282 | memset(&devinfo_data, 0x0, sizeof(devinfo_data));
283 | devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA);
284 | device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
285 |
286 | /* Get information for all the devices belonging to the HID class. */
287 | device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
288 |
289 | /* Iterate over each device in the HID class, looking for the right one. */
290 |
291 | for (;;) {
292 | HANDLE write_handle = INVALID_HANDLE_VALUE;
293 | DWORD required_size = 0;
294 | HIDD_ATTRIBUTES attrib;
295 |
296 | res = SetupDiEnumDeviceInterfaces(device_info_set,
297 | NULL,
298 | &InterfaceClassGuid,
299 | device_index,
300 | &device_interface_data);
301 |
302 | if (!res) {
303 | /* A return of FALSE from this function means that
304 | there are no more devices. */
305 | break;
306 | }
307 |
308 | /* Call with 0-sized detail size, and let the function
309 | tell us how long the detail struct needs to be. The
310 | size is put in &required_size. */
311 | res = SetupDiGetDeviceInterfaceDetailA(device_info_set,
312 | &device_interface_data,
313 | NULL,
314 | 0,
315 | &required_size,
316 | NULL);
317 |
318 | /* Allocate a long enough structure for device_interface_detail_data. */
319 | device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(required_size);
320 | device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
321 |
322 | /* Get the detailed data for this device. The detail data gives us
323 | the device path for this device, which is then passed into
324 | CreateFile() to get a handle to the device. */
325 | res = SetupDiGetDeviceInterfaceDetailA(device_info_set,
326 | &device_interface_data,
327 | device_interface_detail_data,
328 | required_size,
329 | NULL,
330 | NULL);
331 |
332 | if (!res) {
333 | /* register_error(dev, "Unable to call SetupDiGetDeviceInterfaceDetail");
334 | Continue to the next device. */
335 | goto cont;
336 | }
337 |
338 | /* Make sure this device is of Setup Class "HIDClass" and has a
339 | driver bound to it. */
340 | for (i = 0; ; i++) {
341 | char driver_name[256];
342 |
343 | /* Populate devinfo_data. This function will return failure
344 | when there are no more interfaces left. */
345 | res = SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data);
346 | if (!res)
347 | goto cont;
348 |
349 | res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data,
350 | SPDRP_CLASS, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL);
351 | if (!res)
352 | goto cont;
353 |
354 | if (strcmp(driver_name, "HIDClass") == 0) {
355 | /* See if there's a driver bound. */
356 | res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data,
357 | SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL);
358 | if (res)
359 | break;
360 | }
361 | }
362 |
363 | //wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath);
364 |
365 | /* Open a handle to the device */
366 | write_handle = open_device(device_interface_detail_data->DevicePath, TRUE);
367 |
368 | /* Check validity of write_handle. */
369 | if (write_handle == INVALID_HANDLE_VALUE) {
370 | /* Unable to open the device. */
371 | //register_error(dev, "CreateFile");
372 | goto cont_close;
373 | }
374 |
375 |
376 | /* Get the Vendor ID and Product ID for this device. */
377 | attrib.Size = sizeof(HIDD_ATTRIBUTES);
378 | HidD_GetAttributes(write_handle, &attrib);
379 | //wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID);
380 |
381 | /* Check the VID/PID to see if we should add this
382 | device to the enumeration list. */
383 | if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) &&
384 | (product_id == 0x0 || attrib.ProductID == product_id)) {
385 |
386 | #define WSTR_LEN 512
387 | const char *str;
388 | struct hid_device_info *tmp;
389 | PHIDP_PREPARSED_DATA pp_data = NULL;
390 | HIDP_CAPS caps;
391 | BOOLEAN res;
392 | NTSTATUS nt_res;
393 | wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */
394 | size_t len;
395 |
396 | /* VID/PID match. Create the record. */
397 | tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info));
398 | if (cur_dev) {
399 | cur_dev->next = tmp;
400 | }
401 | else {
402 | root = tmp;
403 | }
404 | cur_dev = tmp;
405 |
406 | /* Get the Usage Page and Usage for this device. */
407 | res = HidD_GetPreparsedData(write_handle, &pp_data);
408 | if (res) {
409 | nt_res = HidP_GetCaps(pp_data, &caps);
410 | if (nt_res == HIDP_STATUS_SUCCESS) {
411 | cur_dev->usage_page = caps.UsagePage;
412 | cur_dev->usage = caps.Usage;
413 | }
414 |
415 | HidD_FreePreparsedData(pp_data);
416 | }
417 |
418 | /* Fill out the record */
419 | cur_dev->next = NULL;
420 | str = device_interface_detail_data->DevicePath;
421 | if (str) {
422 | len = strlen(str);
423 | cur_dev->path = (char*) calloc(len+1, sizeof(char));
424 | strncpy(cur_dev->path, str, len+1);
425 | cur_dev->path[len] = '\0';
426 | }
427 | else
428 | cur_dev->path = NULL;
429 |
430 | /* Serial Number */
431 | res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr));
432 | wstr[WSTR_LEN-1] = 0x0000;
433 | if (res) {
434 | cur_dev->serial_number = _wcsdup(wstr);
435 | }
436 |
437 | /* Manufacturer String */
438 | res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr));
439 | wstr[WSTR_LEN-1] = 0x0000;
440 | if (res) {
441 | cur_dev->manufacturer_string = _wcsdup(wstr);
442 | }
443 |
444 | /* Product String */
445 | res = HidD_GetProductString(write_handle, wstr, sizeof(wstr));
446 | wstr[WSTR_LEN-1] = 0x0000;
447 | if (res) {
448 | cur_dev->product_string = _wcsdup(wstr);
449 | }
450 |
451 | /* VID/PID */
452 | cur_dev->vendor_id = attrib.VendorID;
453 | cur_dev->product_id = attrib.ProductID;
454 |
455 | /* Release Number */
456 | cur_dev->release_number = attrib.VersionNumber;
457 |
458 | /* Interface Number. It can sometimes be parsed out of the path
459 | on Windows if a device has multiple interfaces. See
460 | http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or
461 | search for "Hardware IDs for HID Devices" at MSDN. If it's not
462 | in the path, it's set to -1. */
463 | cur_dev->interface_number = -1;
464 | if (cur_dev->path) {
465 | char *interface_component = strstr(cur_dev->path, "&mi_");
466 | if (interface_component) {
467 | char *hex_str = interface_component + 4;
468 | char *endptr = NULL;
469 | cur_dev->interface_number = strtol(hex_str, &endptr, 16);
470 | if (endptr == hex_str) {
471 | /* The parsing failed. Set interface_number to -1. */
472 | cur_dev->interface_number = -1;
473 | }
474 | }
475 | }
476 | }
477 |
478 | cont_close:
479 | CloseHandle(write_handle);
480 | cont:
481 | /* We no longer need the detail data. It can be freed */
482 | free(device_interface_detail_data);
483 |
484 | device_index++;
485 |
486 | }
487 |
488 | /* Close the device information handle. */
489 | SetupDiDestroyDeviceInfoList(device_info_set);
490 |
491 | return root;
492 |
493 | }
494 |
495 | void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs)
496 | {
497 | /* TODO: Merge this with the Linux version. This function is platform-independent. */
498 | struct hid_device_info *d = devs;
499 | while (d) {
500 | struct hid_device_info *next = d->next;
501 | free(d->path);
502 | free(d->serial_number);
503 | free(d->manufacturer_string);
504 | free(d->product_string);
505 | free(d);
506 | d = next;
507 | }
508 | }
509 |
510 |
511 | HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
512 | {
513 | /* TODO: Merge this functions with the Linux version. This function should be platform independent. */
514 | struct hid_device_info *devs, *cur_dev;
515 | const char *path_to_open = NULL;
516 | hid_device *handle = NULL;
517 |
518 | devs = hid_enumerate(vendor_id, product_id);
519 | cur_dev = devs;
520 | while (cur_dev) {
521 | if (cur_dev->vendor_id == vendor_id &&
522 | cur_dev->product_id == product_id) {
523 | if (serial_number) {
524 | if (wcscmp(serial_number, cur_dev->serial_number) == 0) {
525 | path_to_open = cur_dev->path;
526 | break;
527 | }
528 | }
529 | else {
530 | path_to_open = cur_dev->path;
531 | break;
532 | }
533 | }
534 | cur_dev = cur_dev->next;
535 | }
536 |
537 | if (path_to_open) {
538 | /* Open the device */
539 | handle = hid_open_path(path_to_open);
540 | }
541 |
542 | hid_free_enumeration(devs);
543 |
544 | return handle;
545 | }
546 |
547 | HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path)
548 | {
549 | hid_device *dev;
550 | HIDP_CAPS caps;
551 | PHIDP_PREPARSED_DATA pp_data = NULL;
552 | BOOLEAN res;
553 | NTSTATUS nt_res;
554 |
555 | if (hid_init() < 0) {
556 | return NULL;
557 | }
558 |
559 | dev = new_hid_device();
560 |
561 | /* Open a handle to the device */
562 | dev->device_handle = open_device(path, FALSE);
563 |
564 | /* Check validity of write_handle. */
565 | if (dev->device_handle == INVALID_HANDLE_VALUE) {
566 | /* Unable to open the device. */
567 | register_error(dev, "CreateFile");
568 | goto err;
569 | }
570 |
571 | /* Set the Input Report buffer size to 64 reports. */
572 | res = HidD_SetNumInputBuffers(dev->device_handle, 64);
573 | if (!res) {
574 | register_error(dev, "HidD_SetNumInputBuffers");
575 | goto err;
576 | }
577 |
578 | /* Get the Input Report length for the device. */
579 | res = HidD_GetPreparsedData(dev->device_handle, &pp_data);
580 | if (!res) {
581 | register_error(dev, "HidD_GetPreparsedData");
582 | goto err;
583 | }
584 | nt_res = HidP_GetCaps(pp_data, &caps);
585 | if (nt_res != HIDP_STATUS_SUCCESS) {
586 | register_error(dev, "HidP_GetCaps");
587 | goto err_pp_data;
588 | }
589 | dev->output_report_length = caps.OutputReportByteLength;
590 | dev->input_report_length = caps.InputReportByteLength;
591 | HidD_FreePreparsedData(pp_data);
592 |
593 | dev->read_buf = (char*) malloc(dev->input_report_length);
594 |
595 | return dev;
596 |
597 | err_pp_data:
598 | HidD_FreePreparsedData(pp_data);
599 | err:
600 | free_hid_device(dev);
601 | return NULL;
602 | }
603 |
604 | int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length)
605 | {
606 | DWORD bytes_written;
607 | BOOL res;
608 |
609 | OVERLAPPED ol;
610 | unsigned char *buf;
611 | memset(&ol, 0, sizeof(ol));
612 |
613 | /* Make sure the right number of bytes are passed to WriteFile. Windows
614 | expects the number of bytes which are in the _longest_ report (plus
615 | one for the report number) bytes even if the data is a report
616 | which is shorter than that. Windows gives us this value in
617 | caps.OutputReportByteLength. If a user passes in fewer bytes than this,
618 | create a temporary buffer which is the proper size. */
619 | if (length >= dev->output_report_length) {
620 | /* The user passed the right number of bytes. Use the buffer as-is. */
621 | buf = (unsigned char *) data;
622 | } else {
623 | /* Create a temporary buffer and copy the user's data
624 | into it, padding the rest with zeros. */
625 | buf = (unsigned char *) malloc(dev->output_report_length);
626 | memcpy(buf, data, length);
627 | memset(buf + length, 0, dev->output_report_length - length);
628 | length = dev->output_report_length;
629 | }
630 |
631 | res = WriteFile(dev->device_handle, buf, length, NULL, &ol);
632 |
633 | if (!res) {
634 | if (GetLastError() != ERROR_IO_PENDING) {
635 | /* WriteFile() failed. Return error. */
636 | register_error(dev, "WriteFile");
637 | bytes_written = -1;
638 | goto end_of_function;
639 | }
640 | }
641 |
642 | /* Wait here until the write is done. This makes
643 | hid_write() synchronous. */
644 | res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/);
645 | if (!res) {
646 | /* The Write operation failed. */
647 | register_error(dev, "WriteFile");
648 | bytes_written = -1;
649 | goto end_of_function;
650 | }
651 |
652 | end_of_function:
653 | if (buf != data)
654 | free(buf);
655 |
656 | return bytes_written;
657 | }
658 |
659 |
660 | int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
661 | {
662 | DWORD bytes_read = 0;
663 | size_t copy_len = 0;
664 | BOOL res;
665 |
666 | /* Copy the handle for convenience. */
667 | HANDLE ev = dev->ol.hEvent;
668 |
669 | if (!dev->read_pending) {
670 | /* Start an Overlapped I/O read. */
671 | dev->read_pending = TRUE;
672 | memset(dev->read_buf, 0, dev->input_report_length);
673 | ResetEvent(ev);
674 | res = ReadFile(dev->device_handle, dev->read_buf, dev->input_report_length, &bytes_read, &dev->ol);
675 |
676 | if (!res) {
677 | if (GetLastError() != ERROR_IO_PENDING) {
678 | /* ReadFile() has failed.
679 | Clean up and return error. */
680 | CancelIo(dev->device_handle);
681 | dev->read_pending = FALSE;
682 | goto end_of_function;
683 | }
684 | }
685 | }
686 |
687 | if (milliseconds >= 0) {
688 | /* See if there is any data yet. */
689 | res = WaitForSingleObject(ev, milliseconds);
690 | if (res != WAIT_OBJECT_0) {
691 | /* There was no data this time. Return zero bytes available,
692 | but leave the Overlapped I/O running. */
693 | return 0;
694 | }
695 | }
696 |
697 | /* Either WaitForSingleObject() told us that ReadFile has completed, or
698 | we are in non-blocking mode. Get the number of bytes read. The actual
699 | data has been copied to the data[] array which was passed to ReadFile(). */
700 | res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/);
701 |
702 | /* Set pending back to false, even if GetOverlappedResult() returned error. */
703 | dev->read_pending = FALSE;
704 |
705 | if (res && bytes_read > 0) {
706 | if (dev->read_buf[0] == 0x0) {
707 | /* If report numbers aren't being used, but Windows sticks a report
708 | number (0x0) on the beginning of the report anyway. To make this
709 | work like the other platforms, and to make it work more like the
710 | HID spec, we'll skip over this byte. */
711 | bytes_read--;
712 | copy_len = length > bytes_read ? bytes_read : length;
713 | memcpy(data, dev->read_buf+1, copy_len);
714 | }
715 | else {
716 | /* Copy the whole buffer, report number and all. */
717 | copy_len = length > bytes_read ? bytes_read : length;
718 | memcpy(data, dev->read_buf, copy_len);
719 | }
720 | }
721 |
722 | end_of_function:
723 | if (!res) {
724 | register_error(dev, "GetOverlappedResult");
725 | return -1;
726 | }
727 |
728 | return copy_len;
729 | }
730 |
731 | int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length)
732 | {
733 | return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0);
734 | }
735 |
736 | int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock)
737 | {
738 | dev->blocking = !nonblock;
739 | return 0; /* Success */
740 | }
741 |
742 | int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
743 | {
744 | BOOL res = HidD_SetFeature(dev->device_handle, (PVOID)data, length);
745 | if (!res) {
746 | register_error(dev, "HidD_SetFeature");
747 | return -1;
748 | }
749 |
750 | return length;
751 | }
752 |
753 |
754 | int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
755 | {
756 | BOOL res;
757 | #if 0
758 | res = HidD_GetFeature(dev->device_handle, data, length);
759 | if (!res) {
760 | register_error(dev, "HidD_GetFeature");
761 | return -1;
762 | }
763 | return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */
764 | #else
765 | DWORD bytes_returned;
766 |
767 | OVERLAPPED ol;
768 | memset(&ol, 0, sizeof(ol));
769 |
770 | res = DeviceIoControl(dev->device_handle,
771 | IOCTL_HID_GET_FEATURE,
772 | data, length,
773 | data, length,
774 | &bytes_returned, &ol);
775 |
776 | if (!res) {
777 | if (GetLastError() != ERROR_IO_PENDING) {
778 | /* DeviceIoControl() failed. Return error. */
779 | register_error(dev, "Send Feature Report DeviceIoControl");
780 | return -1;
781 | }
782 | }
783 |
784 | /* Wait here until the write is done. This makes
785 | hid_get_feature_report() synchronous. */
786 | res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/);
787 | if (!res) {
788 | /* The operation failed. */
789 | register_error(dev, "Send Feature Report GetOverLappedResult");
790 | return -1;
791 | }
792 |
793 | /* bytes_returned does not include the first byte which contains the
794 | report ID. The data buffer actually contains one more byte than
795 | bytes_returned. */
796 | bytes_returned++;
797 |
798 | return bytes_returned;
799 | #endif
800 | }
801 |
802 | void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev)
803 | {
804 | if (!dev)
805 | return;
806 | CancelIo(dev->device_handle);
807 | free_hid_device(dev);
808 | }
809 |
810 | int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
811 | {
812 | BOOL res;
813 |
814 | res = HidD_GetManufacturerString(dev->device_handle, string, sizeof(wchar_t) * maxlen);
815 | if (!res) {
816 | register_error(dev, "HidD_GetManufacturerString");
817 | return -1;
818 | }
819 |
820 | return 0;
821 | }
822 |
823 | int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
824 | {
825 | BOOL res;
826 |
827 | res = HidD_GetProductString(dev->device_handle, string, sizeof(wchar_t) * maxlen);
828 | if (!res) {
829 | register_error(dev, "HidD_GetProductString");
830 | return -1;
831 | }
832 |
833 | return 0;
834 | }
835 |
836 | int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
837 | {
838 | BOOL res;
839 |
840 | res = HidD_GetSerialNumberString(dev->device_handle, string, sizeof(wchar_t) * maxlen);
841 | if (!res) {
842 | register_error(dev, "HidD_GetSerialNumberString");
843 | return -1;
844 | }
845 |
846 | return 0;
847 | }
848 |
849 | int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
850 | {
851 | BOOL res;
852 |
853 | res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * maxlen);
854 | if (!res) {
855 | register_error(dev, "HidD_GetIndexedString");
856 | return -1;
857 | }
858 |
859 | return 0;
860 | }
861 |
862 |
863 | HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
864 | {
865 | return (wchar_t*)dev->last_error_str;
866 | }
867 |
868 |
869 | /*#define PICPGM*/
870 | /*#define S11*/
871 | #define P32
872 | #ifdef S11
873 | unsigned short VendorID = 0xa0a0;
874 | unsigned short ProductID = 0x0001;
875 | #endif
876 |
877 | #ifdef P32
878 | unsigned short VendorID = 0x04d8;
879 | unsigned short ProductID = 0x3f;
880 | #endif
881 |
882 |
883 | #ifdef PICPGM
884 | unsigned short VendorID = 0x04d8;
885 | unsigned short ProductID = 0x0033;
886 | #endif
887 |
888 |
889 | #if 0
890 | int __cdecl main(int argc, char* argv[])
891 | {
892 | int res;
893 | unsigned char buf[65];
894 |
895 | UNREFERENCED_PARAMETER(argc);
896 | UNREFERENCED_PARAMETER(argv);
897 |
898 | /* Set up the command buffer. */
899 | memset(buf,0x00,sizeof(buf));
900 | buf[0] = 0;
901 | buf[1] = 0x81;
902 |
903 |
904 | /* Open the device. */
905 | int handle = open(VendorID, ProductID, L"12345");
906 | if (handle < 0)
907 | printf("unable to open device\n");
908 |
909 |
910 | /* Toggle LED (cmd 0x80) */
911 | buf[1] = 0x80;
912 | res = write(handle, buf, 65);
913 | if (res < 0)
914 | printf("Unable to write()\n");
915 |
916 | /* Request state (cmd 0x81) */
917 | buf[1] = 0x81;
918 | write(handle, buf, 65);
919 | if (res < 0)
920 | printf("Unable to write() (2)\n");
921 |
922 | /* Read requested state */
923 | read(handle, buf, 65);
924 | if (res < 0)
925 | printf("Unable to read()\n");
926 |
927 | /* Print out the returned buffer. */
928 | for (int i = 0; i < 4; i++)
929 | printf("buf[%d]: %d\n", i, buf[i]);
930 |
931 | return 0;
932 | }
933 | #endif
934 |
935 | #ifdef __cplusplus
936 | } /* extern "C" */
937 | #endif
938 |
--------------------------------------------------------------------------------