├── 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 | --------------------------------------------------------------------------------