├── .gitignore ├── .rspec ├── .ruby-gemset ├── .travis.yml ├── Gemfile ├── Guardfile ├── LICENSE ├── README.rdoc ├── Rakefile ├── ext └── blink1 │ ├── blink1-lib.c │ ├── blink1-lib.h │ ├── blink1.c │ ├── color_funcs.h │ ├── extconf.rb │ ├── hid.c.libusb │ ├── hid.c.mac │ ├── hid.c.windows │ ├── hidapi.h │ ├── osccal.h │ └── usbconfig.h ├── lib ├── blink1.rb └── blink1 │ └── version.rb ├── rb-blink1.gemspec ├── spec ├── blink1_spec.rb └── spec_helper.rb └── wercker.yml /.gitignore: -------------------------------------------------------------------------------- 1 | pkg/* 2 | *.gem 3 | *.bundle 4 | *.so 5 | Gemfile.lock 6 | ext/blink1/hid.c 7 | html 8 | .ruby-version 9 | 10 | 11 | ## MAC OS 12 | .DS_Store 13 | .Trashes 14 | .com.apple.timemachine.supported 15 | .fseventsd 16 | Desktop DB 17 | Desktop DF 18 | 19 | # /bin/fsevent_watch 20 | /ext/blink1/Makefile 21 | 22 | ## editor/IDE 23 | .idea 24 | 25 | ################# 26 | ## Eclipse 27 | ################# 28 | 29 | *.pydevproject 30 | .project 31 | .metadata 32 | tmp/ 33 | *.tmp 34 | *.bak 35 | *.swp 36 | *~.nib 37 | local.properties 38 | .classpath 39 | .settings/ 40 | .loadpath 41 | 42 | # External tool builders 43 | .externalToolBuilders/ 44 | 45 | # Locally stored "Eclipse launch configurations" 46 | *.launch 47 | 48 | # CDT-specific 49 | .cproject 50 | 51 | # PDT-specific 52 | .buildpath 53 | 54 | 55 | ################# 56 | ## Visual Studio 57 | ################# 58 | 59 | ## Ignore Visual Studio temporary files, build results, and 60 | ## files generated by popular Visual Studio add-ons. 61 | 62 | # User-specific files 63 | *.suo 64 | *.user 65 | *.sln.docstates 66 | 67 | # Build results 68 | *_i.c 69 | *_p.c 70 | *.ilk 71 | *.meta 72 | *.obj 73 | *.pch 74 | *.pdb 75 | *.pgc 76 | *.pgd 77 | *.rsp 78 | *.sbr 79 | *.tlb 80 | *.tli 81 | *.tlh 82 | *.tmp 83 | *.vspscc 84 | .builds 85 | *.dotCover 86 | 87 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 88 | #packages/ 89 | 90 | # Visual C++ cache files 91 | ipch/ 92 | *.aps 93 | *.ncb 94 | *.opensdf 95 | *.sdf 96 | 97 | # Visual Studio profiler 98 | *.psess 99 | *.vsp 100 | 101 | # ReSharper is a .NET coding add-in 102 | _ReSharper* 103 | 104 | # Installshield output folder 105 | [Ee]xpress 106 | 107 | # DocProject is a documentation generator add-in 108 | DocProject/buildhelp/ 109 | DocProject/Help/*.HxT 110 | DocProject/Help/*.HxC 111 | DocProject/Help/*.hhc 112 | DocProject/Help/*.hhk 113 | DocProject/Help/*.hhp 114 | DocProject/Help/Html2 115 | DocProject/Help/html 116 | 117 | # Click-Once directory 118 | publish 119 | obj 120 | 121 | # Others 122 | sql 123 | TestResults 124 | *.Cache 125 | ClientBin 126 | stylecop.* 127 | ~$* 128 | *.dbmdl 129 | Generated_Code #added for RIA/Silverlight projects 130 | 131 | # Backup & report files from converting an old project file to a newer 132 | # Visual Studio version. Backup files are not needed, because we have git ;-) 133 | _UpgradeReport_Files/ 134 | Backup*/ 135 | UpgradeLog*.XML 136 | 137 | 138 | 139 | ############ 140 | ## Windows 141 | ############ 142 | 143 | # Windows image file caches 144 | Thumbs.db 145 | 146 | # Folder config file 147 | Desktop.ini 148 | 149 | 150 | ############# 151 | ## Python 152 | ############# 153 | 154 | *.py[co] 155 | 156 | # Packages 157 | *.egg 158 | *.egg-info 159 | dist 160 | eggs 161 | parts 162 | var 163 | sdist 164 | develop-eggs 165 | .installed.cfg 166 | 167 | # Installer logs 168 | pip-log.txt 169 | 170 | # Unit test / coverage reports 171 | .coverage 172 | .tox 173 | 174 | #Translations 175 | *.mo 176 | 177 | #Mr Developer 178 | .mr.developer.cfg 179 | 180 | # Mac crap 181 | .DS_Store 182 | 183 | # tod stuff 184 | # emacs 185 | *~ 186 | # c build results 187 | *.o 188 | # java build results 189 | *.class 190 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | -------------------------------------------------------------------------------- /.ruby-gemset: -------------------------------------------------------------------------------- 1 | blink1-devel 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | env: CODECLIMATE_REPO_TOKEN=ea524e4acf0abd2a5d396cc239710de238314e5bfe3af472baca56a88f7b7ec1 SPEC_OPTS='--format documentation' 3 | rvm: 4 | - "1.9.3" 5 | - "2.0.0" 6 | - "2.1.0" 7 | before_install: 8 | - sudo apt-get install -qq gcc-avr avr-libc 9 | - sudo apt-get install -qq libusb-1.0-0-dev 10 | 11 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gemspec 4 | 5 | if RUBY_VERSION >= '1.9.3' 6 | gem "codeclimate-test-reporter", group: :test, require: nil 7 | end 8 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | notification :tmux, 2 | :display_message => true, 3 | :default_message_color => '#000000', 4 | :success => '#006600', 5 | :failed => '#660000', 6 | :pending => '#660066', 7 | :default => '#000066' 8 | 9 | guard 'spork' do 10 | watch('Gemfile') 11 | watch('Gemfile.lock') 12 | watch('spec/spec_helper.rb') 13 | watch(%r{^spec/.+_spec\.rb$}) 14 | watch(%r{^ext/blink1/blink1.c$}) 15 | watch(%r{^ext/blink1/extconf.rb$}) 16 | watch('spec/spec_helper.rb') 17 | end 18 | 19 | guard 'rake', :task => 'build' do 20 | watch(%r{^lib/blink1.rb$}) 21 | watch(%r{^ext/blink1/blink1.c$}) 22 | watch(%r{^ext/blink1/extconf.rb$}) 23 | end 24 | 25 | guard 'rspec' do 26 | watch(%r{^lib/blink1.rb$}) { |m| "spec/blink1_spec.rb" } 27 | watch(%r{^ext/blink1/blink1.c$}) { |m| "spec/blink1_spec.rb" } 28 | watch(%r{^ext/blink1/extconf.rb$}) { |m| "spec/blink1_spec.rb" } 29 | watch(%r{^spec/blink1_spec.rb$}) { |m| "spec/blink1_spec.rb" } 30 | end 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2012 Atsushi Nagase 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = rb-blink1 2 | 3 | The Ruby interface for blink(1) 4 | 5 | {Build Status}[https://travis-ci.org/ngs/rb-blink1] 6 | {}[https://codeclimate.com/repos/526fcb48f3ea00043902e584/feed] 7 | 8 | == Install 9 | 10 | gem install rb-blink1 11 | 12 | == Usage 13 | 14 | === Play in a block 15 | 16 | require 'blink1' 17 | 18 | Blink1.open do |blink1| 19 | blink1.set_rgb(255, 255, 255) 20 | end 21 | 22 | === Open and close manually 23 | 24 | require 'blink1' 25 | 26 | blink1 = Blink1.new 27 | blink1.open 28 | blink1.set_rgb(255, 255, 255) 29 | blink1.close 30 | 31 | === Set RGB 32 | 33 | blink1.set_rgb(255, 255, 255) 34 | 35 | 36 | === Fade to RGB 37 | 38 | blink1.fade_to_rgb(100, 255, 255, 255) 39 | 40 | === Create and play pattern line 41 | 42 | blink1.write_pattern_line(100, 255, 255, 255, 0) 43 | blink1.write_pattern_line(100, 0, 255, 255, 1) 44 | blink1.write_pattern_line(100, 255, 255, 0, 2) 45 | blink1.write_pattern_line(100, 255, 0, 255, 3) 46 | blink1.write_pattern_line(100, 255, 255, 255, 4) 47 | blink1.write_pattern_line(100, 0, 255, 255, 5) 48 | blink1.write_pattern_line(100, 255, 255, 0, 6) 49 | blink1.write_pattern_line(100, 255, 0, 255, 7) 50 | blink1.write_pattern_line(100, 255, 255, 255, 8) 51 | blink1.write_pattern_line(100, 0, 255, 255, 9) 52 | blink1.write_pattern_line(100, 255, 255, 0, 10) 53 | blink1.play(0) 54 | 55 | === Blink with specified color 56 | 57 | blink1.blink(255, 255, 0, 5) 58 | 59 | === Random color 60 | 61 | blink1.random(25) 62 | 63 | === Turn LED on 64 | 65 | blink1.on 66 | 67 | === Turn LED off 68 | 69 | blink1.off 70 | 71 | == Author 72 | 73 | {Atsushi Nagase}[http://ngs.io/] 74 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | require 'rdoc/task' 3 | require 'rspec/core/rake_task' 4 | 5 | task default: :spec 6 | task spec: :build 7 | 8 | task :build do 9 | Dir.chdir('ext/blink1') do 10 | output = `ruby extconf.rb` 11 | raise output unless $? == 0 12 | output = `make` 13 | raise output unless $? == 0 14 | end 15 | end 16 | 17 | 18 | RSpec::Core::RakeTask.new 19 | 20 | RDoc::Task.new do |rdoc| 21 | rdoc.rdoc_files.include("README.rdoc", "lib/**/*.rb", "ext/blink1/blink1.c") 22 | rdoc.generator = 'bootstrap' 23 | rdoc.main = "README.rdoc" 24 | rdoc.rdoc_dir = 'html' 25 | rdoc.title = 'rb-blink1' 26 | rdoc.options << '--line-numbers' 27 | end 28 | -------------------------------------------------------------------------------- /ext/blink1/blink1-lib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * blink(1) C library -- 3 | * 4 | * 5 | * 2012, Tod E. Kurt, http://todbot.com/blog/ , http://thingm.com/ 6 | * 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include // for toupper() 13 | 14 | #ifdef _WIN32 15 | #include 16 | #else 17 | #include // for usleep() 18 | #endif 19 | 20 | #include "blink1-lib.h" 21 | 22 | 23 | #ifdef DEBUG_PRINTF 24 | #define LOG(...) fprintf(stderr, __VA_ARGS__) 25 | #else 26 | #define LOG(...) do {} while (0) 27 | #endif 28 | 29 | #define blink1_report_id 1 30 | #define blink1_report_size 8 31 | #define blink1_buf_size (blink1_report_size+1) 32 | 33 | // addresses in EEPROM 34 | #define blink1_eeaddr_osccal 0 35 | #define blink1_eeaddr_bootmode 1 36 | #define blink1_eeaddr_serialnum 2 37 | #define blink1_serialnum_len 4 38 | #define blink1_eeaddr_patternstart (blink1_eeaddr_serialnum + blink1_serialnum_len) 39 | 40 | #define pathmax 16 41 | #define pathstrmax 128 42 | #define serialmax (8 + 1) 43 | 44 | 45 | // FIXME: use hid_device_info instead with custom sorter on serial or path 46 | static char blink1_cached_paths[pathmax][pathstrmax]; 47 | static int blink1_cached_count = 0; 48 | static wchar_t blink1_cached_serials[pathmax][serialmax]; 49 | 50 | static int blink1_enable_degamma = 1; 51 | 52 | //---------------------------------------------------------------------------- 53 | 54 | // 55 | int blink1_enumerate(void) 56 | { 57 | return blink1_enumerateByVidPid( blink1_vid(), blink1_pid() ); 58 | } 59 | 60 | // get all matching devices by VID/PID pair 61 | int blink1_enumerateByVidPid(int vid, int pid) 62 | { 63 | struct hid_device_info *devs, *cur_dev; 64 | 65 | int p = 0; 66 | devs = hid_enumerate(vid, pid); 67 | cur_dev = devs; 68 | while (cur_dev) { 69 | if( (cur_dev->vendor_id != 0 && cur_dev->product_id != 0) && 70 | (cur_dev->vendor_id == vid && cur_dev->product_id == pid) ) { 71 | if( cur_dev->serial_number != NULL ) { // can happen if not root 72 | strcpy( blink1_cached_paths[p], cur_dev->path ); 73 | wcscpy( blink1_cached_serials[p], cur_dev->serial_number ); 74 | p++; 75 | } 76 | } 77 | cur_dev = cur_dev->next; 78 | } 79 | hid_free_enumeration(devs); 80 | 81 | blink1_cached_count = p; 82 | 83 | blink1_sortSerials(); 84 | 85 | return p; 86 | } 87 | 88 | // 89 | int blink1_getCachedCount(void) 90 | { 91 | return blink1_cached_count; 92 | } 93 | 94 | // 95 | const char* blink1_getCachedPath(int i) 96 | { 97 | return blink1_cached_paths[i]; 98 | } 99 | // 100 | const wchar_t* blink1_getCachedSerial(int i) 101 | { 102 | return blink1_cached_serials[i]; 103 | } 104 | 105 | // 106 | hid_device* blink1_openByPath(const char* path) 107 | { 108 | if( path == NULL || strlen(path) == 0 ) return NULL; 109 | hid_device* handle = hid_open_path( path ); 110 | return handle; 111 | } 112 | 113 | // 114 | hid_device* blink1_openBySerial(const wchar_t* serial) 115 | { 116 | if( serial == NULL || wcslen(serial) == 0 ) return NULL; 117 | int vid = blink1_vid(); 118 | int pid = blink1_pid(); 119 | 120 | LOG("opening %ls at vid/pid %x/%x\n", serial, vid,pid); 121 | hid_device* handle = hid_open(vid,pid, serial ); 122 | return handle; 123 | } 124 | 125 | // 126 | hid_device* blink1_openById( int i ) 127 | { 128 | //return blink1_openByPath( blink1_getCachedPath(i) ); 129 | return blink1_openBySerial( blink1_getCachedSerial(i) ); 130 | } 131 | 132 | // 133 | hid_device* blink1_open(void) 134 | { 135 | int vid = blink1_vid(); 136 | int pid = blink1_pid(); 137 | 138 | hid_device* handle = hid_open(vid,pid, NULL); // FIXME? 139 | 140 | return handle; 141 | } 142 | 143 | // 144 | // FIXME: search through blink1s list to zot it too? 145 | void blink1_close( hid_device* dev ) 146 | { 147 | if( dev != NULL ) 148 | hid_close(dev); 149 | dev = NULL; 150 | hid_exit(); // FIXME: this cleans up libusb in a way that hid_close doesn't 151 | } 152 | 153 | // 154 | int blink1_write( hid_device* dev, void* buf, int len) 155 | { 156 | if( dev==NULL ) { 157 | return -1; // BLINK1_ERR_NOTOPEN; 158 | } 159 | int rc = hid_send_feature_report( dev, buf, len ); 160 | // FIXME: put this in an ifdef? 161 | if( rc==-1 ) { 162 | fprintf(stderr, "blink1_write error: %ls\n", hid_error(dev)); 163 | } 164 | return rc; 165 | } 166 | 167 | // len should contain length of buf 168 | // after call, len will contain actual len of buf read 169 | int blink1_read( hid_device* dev, void* buf, int len) 170 | { 171 | if( dev==NULL ) { 172 | return -1; // BLINK1_ERR_NOTOPEN; 173 | } 174 | int rc = hid_send_feature_report(dev, buf, len); // FIXME: check rc 175 | 176 | if( (rc = hid_get_feature_report(dev, buf, len) == -1) ) { 177 | LOG("error reading data: %s\n",blink1_error_msg(rc)); 178 | } 179 | return rc; 180 | } 181 | 182 | 183 | // ------------------------------------------------------------------------- 184 | // everything below here doesn't need to know about USB details 185 | // except for a "hid_device*" 186 | // ------------------------------------------------------------------------- 187 | 188 | #include 189 | 190 | // 191 | int blink1_getSerialNumber(hid_device *dev, char* buf) 192 | { 193 | if( dev == NULL ) return -1; 194 | /* 195 | wchar_t* wbuf = dev->serial_number; 196 | int i=0; 197 | while( wbuf ) { 198 | buf[i++] = *wbuf; 199 | } 200 | return i; 201 | */ 202 | return -1; 203 | } 204 | 205 | // 206 | int blink1_getVersion(hid_device *dev) 207 | { 208 | char buf[blink1_buf_size] = {blink1_report_id, 'v' }; 209 | int len = sizeof(buf); 210 | 211 | //hid_set_nonblocking(dev, 0); 212 | int rc = blink1_write(dev, buf, sizeof(buf)); 213 | blink1_sleep( 50 ); //FIXME: 214 | if( rc != -1 ) // no error 215 | rc = blink1_read(dev, buf, len); 216 | if( rc != -1 ) // also no error 217 | rc = ((buf[3]-'0') * 100) + (buf[4]-'0'); 218 | // rc is now version number or error 219 | // FIXME: we don't know vals of errcodes 220 | return rc; 221 | } 222 | 223 | // 224 | int blink1_eeread(hid_device *dev, uint16_t addr, uint8_t* val) 225 | { 226 | char buf[blink1_buf_size] = {blink1_report_id, 'e', addr }; 227 | int len = sizeof(buf); 228 | 229 | int rc = blink1_write(dev, buf, len ); 230 | blink1_sleep( 50 ); // FIXME: 231 | if( rc != -1 ) // no error 232 | rc = blink1_read(dev, buf, len ); 233 | if( rc != -1 ) 234 | *val = buf[3]; 235 | return rc; 236 | } 237 | 238 | // 239 | int blink1_eewrite(hid_device *dev, uint16_t addr, uint8_t val) 240 | { 241 | char buf[blink1_buf_size] = {blink1_report_id, 'E', addr, val }; 242 | 243 | int rc = blink1_write(dev, buf, sizeof(buf) ); 244 | 245 | return rc; 246 | } 247 | 248 | // FIXME: this doesn't work 249 | int blink1_serialnumread(hid_device *dev, uint8_t** serialnum) 250 | { 251 | int rc = 0; 252 | for( int i=0; i= '0' && c <= '9') return (c - '0'); 263 | if (c >= 'A' && c <= 'F') return (c - 'A')+10; 264 | return 0; 265 | } 266 | 267 | // serialnum comes in as an ascii set of 8 characters representing 268 | // 4-bytes 269 | int blink1_serialnumwrite(hid_device *dev, uint8_t* serialnumstr) 270 | { 271 | uint8_t serialnum[4]; 272 | serialnum[0] = parseHex( serialnumstr[0] )*16 + parseHex( serialnumstr[1] ); 273 | serialnum[1] = parseHex( serialnumstr[2] )*16 + parseHex( serialnumstr[3] ); 274 | serialnum[2] = parseHex( serialnumstr[4] )*16 + parseHex( serialnumstr[5] ); 275 | serialnum[3] = parseHex( serialnumstr[6] )*16 + parseHex( serialnumstr[7] ); 276 | 277 | int rc = 0; 278 | for( int i=0; i> 8); 308 | buf[6] = dms % 0xff; 309 | buf[7] = n; 310 | 311 | int rc = blink1_write(dev, buf, sizeof(buf) ); 312 | 313 | return rc; 314 | } 315 | 316 | 317 | // 318 | int blink1_fadeToRGB(hid_device *dev, uint16_t fadeMillis, 319 | uint8_t r, uint8_t g, uint8_t b) 320 | { 321 | int dms = fadeMillis/10; // millis_divided_by_10 322 | 323 | uint8_t buf[9]; 324 | 325 | buf[0] = blink1_report_id; // report id 326 | buf[1] = 'c'; // command code for 'fade to rgb' 327 | buf[2] = ((blink1_enable_degamma) ? blink1_degamma(r) : r ); 328 | buf[3] = ((blink1_enable_degamma) ? blink1_degamma(g) : g ); 329 | buf[4] = ((blink1_enable_degamma) ? blink1_degamma(b) : b ); 330 | buf[5] = (dms >> 8); 331 | buf[6] = dms % 0xff; 332 | buf[7] = 0; 333 | 334 | int rc = blink1_write(dev, buf, sizeof(buf) ); 335 | 336 | return rc; 337 | } 338 | 339 | // 340 | int blink1_setRGB(hid_device *dev, uint8_t r, uint8_t g, uint8_t b ) 341 | { 342 | uint8_t buf[blink1_buf_size]; 343 | 344 | buf[0] = blink1_report_id; // report id 345 | buf[1] = 'n'; // command code for "set rgb now" 346 | buf[2] = ((blink1_enable_degamma) ? blink1_degamma(r) : r ); // red 347 | buf[3] = ((blink1_enable_degamma) ? blink1_degamma(g) : g ); // grn 348 | buf[4] = ((blink1_enable_degamma) ? blink1_degamma(b) : b ); // blu 349 | buf[5] = 0; 350 | buf[6] = 0; 351 | buf[7] = 0; 352 | 353 | int rc = blink1_write(dev, buf, sizeof(buf) ); 354 | 355 | return rc; 356 | } 357 | 358 | 359 | // 360 | int blink1_serverdown(hid_device *dev, uint8_t on, uint16_t millis) 361 | { 362 | int dms = millis/10; // millis_divided_by_10 363 | 364 | char buf[blink1_buf_size] = {blink1_report_id, 'D', on, (dms>>8), (dms % 0xff) }; 365 | 366 | int rc = blink1_write(dev, buf, sizeof(buf) ); 367 | return rc; 368 | } 369 | 370 | // 371 | int blink1_play(hid_device *dev, uint8_t play, uint8_t pos) 372 | { 373 | char buf[blink1_buf_size] = {blink1_report_id, 'p', play, pos }; 374 | int rc = blink1_write(dev, buf, sizeof(buf) ); 375 | return rc; 376 | } 377 | 378 | // 379 | int blink1_writePatternLine(hid_device *dev, uint16_t fadeMillis, 380 | uint8_t r, uint8_t g, uint8_t b, 381 | uint8_t pos) 382 | { 383 | int dms = fadeMillis/10; // millis_divided_by_10 384 | r = (blink1_enable_degamma) ? blink1_degamma(r) : r ; 385 | g = (blink1_enable_degamma) ? blink1_degamma(g) : g ; 386 | b = (blink1_enable_degamma) ? blink1_degamma(b) : b ; 387 | 388 | char buf[blink1_buf_size] = {blink1_report_id, 'P', r,g,b, (dms>>8), (dms % 0xff), pos }; 389 | int rc = blink1_write(dev, buf, sizeof(buf) ); 390 | return rc; 391 | } 392 | 393 | // 394 | int blink1_readPatternLine(hid_device *dev, uint16_t* fadeMillis, 395 | uint8_t* r, uint8_t* g, uint8_t* b, 396 | uint8_t pos) 397 | { 398 | char buf[blink1_buf_size] = {blink1_report_id, 'R', 0,0,0, 0,0, pos }; 399 | int rc = blink1_write(dev, buf, sizeof(buf) ); 400 | blink1_sleep( 50 ); // FIXME: 401 | if( rc != -1 ) // no error 402 | rc = blink1_read(dev, buf, sizeof(buf) ); 403 | if( rc != -1 ) { 404 | *r = buf[2]; 405 | *g = buf[3]; 406 | *b = buf[4]; 407 | *fadeMillis = ((buf[5]<<8) + (buf[6] &0xff)) * 10; 408 | } 409 | return rc; 410 | } 411 | 412 | 413 | // FIXME: 414 | int readUUID( hid_device* dev, uint8_t** uuid ) 415 | { 416 | return -1; 417 | } 418 | // FIXME: 419 | int setUUID( hid_device* dev, uint8_t* uuid ) 420 | { 421 | return -1; 422 | } 423 | 424 | 425 | /* ------------------------------------------------------------------------- */ 426 | 427 | // FIXME: this is wrong 428 | // FIXME: provide function that generated this 429 | uint8_t degamma_lookup[256] = { 430 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, 431 | 1,1,1,1,1,1,2,2,2,2,2,3,3,3,3,4, 432 | 4,4,4,5,5,5,5,6,6,6,7,7,7,8,8,9, 433 | 9,9,10,10,11,11,11,12,12,13,13,14,14,15,15,16, 434 | 16,17,17,18,18,19,19,20,20,21,22,22,23,23,24,25, 435 | 25,26,27,27,28,29,29,30,31,31,32,33,33,34,35,36, 436 | 36,37,38,39,40,40,41,42,43,44,44,45,46,47,48,49, 437 | 50,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64, 438 | 65,66,67,68,69,70,71,72,73,74,75,76,77,79,80,81, 439 | 82,83,84,85,87,88,89,90,91,93,94,95,96,97,99,100, 440 | 101,102,104,105,106,108,109,110,112,113,114,116,117,118,120,121, 441 | 122,124,125,127,128,129,131,132,134,135,137,138,140,141,143,144, 442 | 146,147,149,150,152,153,155,156,158,160,161,163,164,166,168,169, 443 | 171,172,174,176,177,179,181,182,184,186,188,189,191,193,195,196, 444 | 198,200,202,203,205,207,209,211,212,214,216,218,220,222,224,225, 445 | 227,229,231,233,235,237,239,241,243,245,247,249,251,253,255,255, 446 | }; 447 | 448 | void blink1_enableDegamma() 449 | { 450 | blink1_enable_degamma = 1; 451 | } 452 | void blink1_disableDegamma() 453 | { 454 | blink1_enable_degamma = 0; 455 | } 456 | 457 | // a simple logarithmic -> linear mapping as a sort of gamma correction 458 | // maps from 0-255 to 0-255 459 | static int blink1_degamma_log2lin( int n ) 460 | { 461 | //return (int)(1.0* (n * 0.707 )); // 1/sqrt(2) 462 | return (((1<<(n/32))-1) + ((1<<(n/32))*((n%32)+1)+15)/32); 463 | } 464 | 465 | // 466 | int blink1_degamma( int n ) 467 | { 468 | //return degamma_lookup[n]; 469 | return blink1_degamma_log2lin(n); 470 | } 471 | 472 | 473 | // qsort C-string comparison function 474 | int cmp_path(const void *a, const void *b) 475 | { 476 | return strncmp( (const char *)a, (const char *)b, pathstrmax); 477 | } 478 | // qsort wchar_t string comparison function 479 | int cmp_serial(const void *a, const void *b) 480 | { 481 | return wcsncmp( (const wchar_t *)a, (const wchar_t *)b, serialmax); 482 | } 483 | 484 | // 485 | void blink1_sortPaths(void) 486 | { 487 | size_t elemsize = sizeof( blink1_cached_paths[0] ); // 128 488 | //size_t count = sizeof(blink1_cached_paths) / elemsize; // 16 489 | 490 | return qsort( blink1_cached_paths, blink1_cached_count,elemsize,cmp_path); 491 | } 492 | 493 | // 494 | void blink1_sortSerials(void) 495 | { 496 | size_t elemsize = sizeof( blink1_cached_serials[0] ); // 497 | //size_t count = sizeof(blink1_cached_serials) / elemsize; // 498 | 499 | qsort( blink1_cached_serials, 500 | blink1_cached_count, 501 | elemsize, 502 | cmp_serial); 503 | } 504 | 505 | // 506 | int blink1_vid(void) 507 | { 508 | uint8_t rawVid[2] = {USB_CFG_VENDOR_ID}; 509 | int vid = rawVid[0] + 256 * rawVid[1]; 510 | return vid; 511 | } 512 | // 513 | int blink1_pid(void) 514 | { 515 | uint8_t rawPid[2] = {USB_CFG_DEVICE_ID}; 516 | int pid = rawPid[0] + 256 * rawPid[1]; 517 | return pid; 518 | } 519 | 520 | // simple cross-platform millis sleep func 521 | void blink1_sleep(uint16_t millis) 522 | { 523 | #ifdef WIN32 524 | Sleep(millis); 525 | #else 526 | usleep( millis * 1000); 527 | #endif 528 | } 529 | 530 | // 531 | char *blink1_error_msg(int errCode) 532 | { 533 | /* 534 | static char buf[80]; 535 | 536 | switch(errCode){ 537 | case USBOPEN_ERR_ACCESS: return "Access to device denied"; 538 | case USBOPEN_ERR_NOTFOUND: return "The specified device was not found"; 539 | case USBOPEN_ERR_IO: return "Communication error with device"; 540 | default: 541 | sprintf(buf, "Unknown USB error %d", errCode); 542 | return buf; 543 | } 544 | */ 545 | return NULL; /* not reached */ 546 | } 547 | 548 | 549 | /* 550 | 551 | // 552 | int blink1_nightlight(hid_device *dev, uint8_t on) 553 | { 554 | char buf[8] = { blink1_report_id, 'N', on }; 555 | 556 | int rc = blink1_write(dev, buf, sizeof(buf) ); 557 | 558 | return rc; 559 | } 560 | 561 | 562 | // 563 | int blink1_command(hid_device* dev, int num_send, int num_recv, 564 | uint8_t* buf_send, uint8_t* buf_recv ) 565 | { 566 | if( dev==NULL ) { 567 | return -1; // BLINK1_ERR_NOTOPEN; 568 | } 569 | int err = 0; 570 | if( (err = usbhidSetReport(dev, (char*)buf_send, num_send)) != 0) { 571 | fprintf(stderr,"error writing data: %s\n",blink1_error_msg(err)); 572 | return err; 573 | } 574 | 575 | if( num_recv > 0 ) { 576 | int len = num_recv; 577 | if((err = usbhidGetReport(dev, 0, (char*)buf_recv, &len)) != 0) { 578 | fprintf(stderr,"error reading data: %s\n",blink1_error_msg(err)); 579 | } else { // it was good 580 | } 581 | } 582 | return err; 583 | } 584 | */ -------------------------------------------------------------------------------- /ext/blink1/blink1-lib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * blink(1) C library -- 3 | * 4 | * 2012, Tod E. Kurt, http://todbot.com/blog/ , http://thingm.com/ 5 | * 6 | */ 7 | 8 | 9 | #ifndef __BLINK1_LIB_H__ 10 | #define __BLINK1_LIB_H__ 11 | 12 | #include 13 | 14 | #include "hidapi.h" 15 | #include "usbconfig.h" // from firmware, for VID,PID,vendor name & product name 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | #define blink1_max_devices 16 22 | 23 | 24 | int blink1_vid(void); 25 | int blink1_pid(void); 26 | void blink1_sortPaths(void); 27 | void blink1_sortSerials(void); 28 | 29 | int blink1_enumerate(); 30 | int blink1_enumerateByVidPid(int vid, int pid); 31 | const char* blink1_getCachedPath(int i); 32 | const wchar_t* blink1_getCachedSerial(int i); 33 | int blink1_getCachedCount(void); 34 | 35 | hid_device* blink1_open(void); 36 | hid_device* blink1_openByPath(const char* path); 37 | hid_device* blink1_openBySerial(const wchar_t* serial); 38 | hid_device* blink1_openById( int i ); 39 | 40 | void blink1_close( hid_device* dev ); 41 | 42 | int blink1_write( hid_device* dev, void* buf, int len); 43 | int blink1_read( hid_device* dev, void* buf, int len); 44 | 45 | int blink1_getSerialNumber(hid_device *dev, char* buf); 46 | int blink1_getVersion(hid_device *dev); 47 | 48 | int blink1_fadeToRGB(hid_device *dev, uint16_t fadeMillis, 49 | uint8_t r, uint8_t g, uint8_t b ); 50 | int blink1_fadeToRGBN(hid_device *dev, uint16_t fadeMillis, 51 | uint8_t r, uint8_t g, uint8_t b, uint8_t n ); 52 | 53 | int blink1_setRGB(hid_device *dev, uint8_t r, uint8_t g, uint8_t b ); 54 | 55 | int blink1_eeread(hid_device *dev, uint16_t addr, uint8_t* val); 56 | int blink1_eewrite(hid_device *dev, uint16_t addr, uint8_t val); 57 | 58 | int blink1_serialnumread(hid_device *dev, uint8_t** serialnumstr); 59 | int blink1_serialnumwrite(hid_device *dev, uint8_t* serialnumstr); 60 | 61 | //int blink1_nightlight(hid_device *dev, uint8_t on); 62 | int blink1_serverdown(hid_device *dev, uint8_t on, uint16_t millis); 63 | 64 | int blink1_play(hid_device *dev, uint8_t play, uint8_t pos); 65 | int blink1_writePatternLine(hid_device *dev, uint16_t fadeMillis, 66 | uint8_t r, uint8_t g, uint8_t b, 67 | uint8_t pos); 68 | int blink1_readPatternLine(hid_device *dev, uint16_t* fadeMillis, 69 | uint8_t* r, uint8_t* g, uint8_t* b, 70 | uint8_t pos); 71 | //int blink1_playPattern(hid_device *dev,,); 72 | 73 | char *blink1_error_msg(int errCode); 74 | 75 | void blink1_enableDegamma(); 76 | void blink1_disableDegamma(); 77 | int blink1_degamma(int n); 78 | 79 | void blink1_sleep(uint16_t delayMillis); 80 | 81 | #ifdef __cplusplus 82 | } 83 | #endif 84 | 85 | #endif -------------------------------------------------------------------------------- /ext/blink1/blink1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Blink1 3 | * Ruby interface for blink(1). 4 | * Wraps methods in blink1-lib.c. 5 | */ 6 | #include 7 | #include "ruby.h" 8 | #include "blink1-lib.h" 9 | #include 10 | 11 | struct Blink1Instance { 12 | hid_device *dev; 13 | int opened; 14 | }; 15 | 16 | static int degamma = 1; 17 | 18 | #pragma mark - Static methods 19 | 20 | /** Return vendor ID */ 21 | static VALUE rb_blink1_vid(VALUE self) { 22 | return INT2NUM(blink1_vid()); 23 | } 24 | 25 | /** Return product ID */ 26 | static VALUE rb_blink1_pid(VALUE self) { 27 | return INT2NUM(blink1_pid()); 28 | } 29 | 30 | /** Sort cached device by path. */ 31 | static VALUE rb_blink1_sortPaths(VALUE self) { 32 | blink1_sortPaths(); 33 | return Qnil; 34 | } 35 | 36 | /** Sort cached device by serial id. */ 37 | static VALUE rb_blink1_sortSerials(VALUE self) { 38 | blink1_sortSerials(); 39 | return Qnil; 40 | } 41 | 42 | /** Get all devices by default +product_id+, +vendor_id+. */ 43 | static VALUE rb_blink1_enumerate(VALUE self) { 44 | return INT2NUM(blink1_enumerate()); 45 | } 46 | 47 | /** 48 | * :call-seq: 49 | * enumerate_by_vid_pid (vid, pid) -> integer 50 | * 51 | * Get all matching devices by VID/PID pair. 52 | * 53 | * Return number of devices. 54 | */ 55 | static VALUE rb_blink1_enumerateByVidPid(VALUE self, VALUE vid, VALUE pid) { 56 | return INT2NUM(blink1_enumerateByVidPid(FIX2INT(vid), FIX2INT(pid))); 57 | } 58 | 59 | /** 60 | * :call-seq: 61 | * cached_path (index) -> string 62 | * 63 | * Return cached device path by index. 64 | */ 65 | static VALUE rb_blink1_blink1_getCachedPath(VALUE self, VALUE i) { 66 | return rb_str_new2(blink1_getCachedPath(FIX2INT(i))); 67 | } 68 | 69 | /** 70 | * :call-seq: 71 | * cached_serial (index) -> string 72 | * 73 | * Return cached device serial id by index. 74 | */ 75 | static VALUE rb_blink1_getCachedSerial(VALUE self, VALUE i) { 76 | const wchar_t *ret = blink1_getCachedSerial(FIX2INT(i)); 77 | char dest[16] = {"\0"}; 78 | int t = wcstombs(dest, ret, sizeof(ret)); 79 | return rb_str_new2(dest); 80 | } 81 | 82 | /** 83 | * :call-seq: 84 | * cached_count -> integer 85 | * 86 | * Return number of cached devices. 87 | */ 88 | static VALUE rb_blink1_getCachedCount(VALUE self) { 89 | return INT2NUM(blink1_getCachedCount()); 90 | } 91 | 92 | 93 | /* :nodoc: */ 94 | static VALUE rb_blink1_error_msg(VALUE self, VALUE code) { 95 | char *msg = blink1_error_msg(FIX2INT(code)); 96 | return msg == NULL ? Qnil : rb_str_new2(msg); 97 | } 98 | 99 | /** Return degamma enabled. */ 100 | static VALUE rb_blink1_getDegammaEnabled(VALUE self) { 101 | return degamma == 1 ? Qtrue : Qfalse; 102 | } 103 | 104 | /** Set degamma enabled. */ 105 | static VALUE rb_blink1_setDegammaEnabled(VALUE self, VALUE enabled) { 106 | if(RTEST(enabled)) { 107 | degamma = 1; 108 | blink1_enableDegamma(); 109 | } else { 110 | degamma = 0; 111 | blink1_disableDegamma(); 112 | } 113 | return Qnil; 114 | } 115 | 116 | /** Return gamma corrected value for a RGB component. */ 117 | static VALUE rb_blink1_degamma(VALUE self, VALUE i) { 118 | return INT2NUM(blink1_degamma(FIX2INT(i))); 119 | } 120 | 121 | /** Sleeps for milliseconds. */ 122 | static VALUE rb_blink1_sleep(VALUE self, VALUE delayMillis) { 123 | blink1_sleep(FIX2UINT(delayMillis)); 124 | return Qnil; 125 | } 126 | 127 | #pragma mark - Instance methods 128 | 129 | /** :nodoc: */ 130 | void rb_blink1_free(struct Blink1Instance *ins) { 131 | if(ins->opened == 1) { 132 | blink1_close(ins->dev); 133 | ins->opened = 0; 134 | } 135 | // free(ins); 136 | ruby_xfree(ins); 137 | } 138 | 139 | /** :nodoc: */ 140 | static VALUE rb_blink1_allocate(VALUE self) { 141 | struct Blink1Instance *ins = malloc(sizeof(struct Blink1Instance)); 142 | ins->opened = 0; 143 | return Data_Wrap_Struct(self, 0, rb_blink1_free, ins); 144 | } 145 | 146 | /** Return the device is opened. */ 147 | static VALUE rb_blink1_opened(VALUE self) { 148 | struct Blink1Instance *ins; 149 | Data_Get_Struct(self, struct Blink1Instance, ins); 150 | return ins->opened == 1 ? Qtrue : Qfalse; 151 | } 152 | 153 | /** Open device by default +vendor_id+, +product_id+. */ 154 | static VALUE rb_blink1_open(VALUE self) { 155 | struct Blink1Instance *ins; 156 | Data_Get_Struct(self, struct Blink1Instance, ins); 157 | if(ins->opened == 0) { 158 | ins->dev = blink1_open(); 159 | ins->opened = 1; 160 | return Qtrue; 161 | } 162 | return Qfalse; 163 | } 164 | 165 | /** Open device by device path. */ 166 | static VALUE rb_blink1_openByPath(VALUE self, VALUE path) { 167 | struct Blink1Instance *ins; 168 | Data_Get_Struct(self, struct Blink1Instance, ins); 169 | if(ins->opened == 0) { 170 | ins->dev = blink1_open(); 171 | ins->opened = 1; 172 | return Qtrue; 173 | } 174 | return Qfalse; 175 | } 176 | 177 | /** Open device by serial id. */ 178 | static VALUE rb_blink1_openBySerial(VALUE self, VALUE serial) { 179 | struct Blink1Instance *ins; 180 | Data_Get_Struct(self, struct Blink1Instance, ins); 181 | if(ins->opened == 0) { 182 | ins->dev = blink1_open(); 183 | ins->opened = 1; 184 | return Qtrue; 185 | } 186 | return Qfalse; 187 | } 188 | 189 | /** Open device by id. */ 190 | static VALUE rb_blink1_openById(VALUE self, VALUE id) { 191 | struct Blink1Instance *ins; 192 | Data_Get_Struct(self, struct Blink1Instance, ins); 193 | if(ins->opened == 0) { 194 | ins->dev = blink1_open(); 195 | ins->opened = 1; 196 | return Qtrue; 197 | } 198 | return Qfalse; 199 | } 200 | 201 | /** Closes the device. */ 202 | static VALUE rb_blink1_close(VALUE self) { 203 | struct Blink1Instance *ins; 204 | Data_Get_Struct(self, struct Blink1Instance, ins); 205 | if(ins->opened == 1) { 206 | blink1_close(ins->dev); 207 | ins->opened = 0; 208 | } 209 | return Qnil; 210 | } 211 | 212 | /** Return blink(1) version. */ 213 | static VALUE rb_blink1_getVersion(VALUE self) { 214 | struct Blink1Instance *ins; 215 | Data_Get_Struct(self, struct Blink1Instance, ins); 216 | return INT2NUM(blink1_getVersion(ins->dev)); 217 | } 218 | 219 | /** 220 | * :call-seq: 221 | * fade_to_rgb (fade_millis, r, g, b) -> integer 222 | * 223 | * Fade LED color to RGB in +fade_millis+. 224 | * 225 | * Return the actual number of bytes written and -1 on error. 226 | */ 227 | static VALUE rb_blink1_fadeToRGB(VALUE self, VALUE fadeMillis, VALUE r, VALUE g, VALUE b) { 228 | struct Blink1Instance *ins; 229 | Data_Get_Struct(self, struct Blink1Instance, ins); 230 | return INT2NUM(blink1_fadeToRGB(ins->dev, FIX2UINT(fadeMillis), FIX2UINT(r), FIX2UINT(g), FIX2UINT(b))); 231 | } 232 | 233 | /** 234 | * :call-seq: 235 | * fade_to_rgbn (fade_millis, r, g, b, n) -> integer 236 | * 237 | * Fade a specific (blink(1) mk2) LED LED color to RGB in +fade_millis+. 238 | * 239 | * Return the actual number of bytes written and -1 on error. 240 | */ 241 | static VALUE rb_blink1_fadeToRGBN(VALUE self, VALUE fadeMillis, VALUE r, VALUE g, VALUE b, VALUE n) { 242 | struct Blink1Instance *ins; 243 | Data_Get_Struct(self, struct Blink1Instance, ins); 244 | return INT2NUM(blink1_fadeToRGBN(ins->dev, FIX2UINT(fadeMillis), FIX2UINT(r), FIX2UINT(g), FIX2UINT(b), FIX2UINT(n))); 245 | } 246 | 247 | /** 248 | * :call-seq: 249 | * set_rgb (r, g, b) -> integer 250 | * 251 | * Set LED color to RGB. 252 | * 253 | * Return the actual number of bytes written and -1 on error. 254 | */ 255 | static VALUE rb_blink1_setRGB(VALUE self, VALUE r, VALUE g, VALUE b) { 256 | struct Blink1Instance *ins; 257 | Data_Get_Struct(self, struct Blink1Instance, ins); 258 | return INT2NUM(blink1_setRGB(ins->dev, FIX2UINT(r), FIX2UINT(g), FIX2UINT(b))); 259 | } 260 | 261 | /** 262 | * :call-seq: 263 | * eeread (addr) -> integer 264 | * 265 | * Returns an EEPROM byte. 266 | */ 267 | static VALUE rb_blink1_eeread(VALUE self, VALUE addr) { 268 | struct Blink1Instance *ins; 269 | uint8_t val = 0; 270 | Data_Get_Struct(self, struct Blink1Instance, ins); 271 | blink1_eeread(ins->dev, FIX2UINT(addr), &val); 272 | return UINT2NUM(val); 273 | } 274 | 275 | /** 276 | * :call-seq: 277 | * eewrite (addr, val) -> integer 278 | * 279 | * Write an EEPROM byte. 280 | * 281 | * Return the actual number of bytes written and -1 on error. 282 | */ 283 | static VALUE rb_blink1_eewrite(VALUE self, VALUE addr, VALUE val) { 284 | struct Blink1Instance *ins; 285 | Data_Get_Struct(self, struct Blink1Instance, ins); 286 | return INT2NUM(blink1_eewrite(ins->dev, FIX2UINT(addr), FIX2UINT(val))); 287 | } 288 | 289 | /** :nodoc: */ 290 | static VALUE rb_blink1_serialnumread(VALUE self) { 291 | struct Blink1Instance *ins; 292 | Data_Get_Struct(self, struct Blink1Instance, ins); 293 | uint8_t *serialnum; 294 | blink1_serialnumread(ins->dev, &serialnum); 295 | return UINT2NUM(serialnum); 296 | } 297 | 298 | /** :nodoc: */ 299 | static VALUE rb_blink1_serialnumwrite(VALUE self, VALUE serialnumstr) { 300 | struct Blink1Instance *ins; 301 | Data_Get_Struct(self, struct Blink1Instance, ins); 302 | char *serialnumc = RSTRING_PTR(serialnumstr); 303 | char serialnum[100]; 304 | strcpy(serialnumc, serialnum); 305 | return INT2NUM(blink1_serialnumwrite(ins->dev, (uint8_t *)serialnum)); 306 | } 307 | 308 | /** 309 | * :call-seq: 310 | * serverdown (on, millisecond) -> integer 311 | * 312 | * Turn on/off servertickle 313 | * 314 | * Return the actual number of bytes written and -1 on error. 315 | */ 316 | static VALUE rb_blink1_serverdown(VALUE self, VALUE on, VALUE millis) { 317 | struct Blink1Instance *ins; 318 | Data_Get_Struct(self, struct Blink1Instance, ins); 319 | return INT2NUM(blink1_serverdown(ins->dev, RTEST(on) ? 1 : 0, FIX2UINT(millis))); 320 | } 321 | 322 | /** 323 | * :call-seq: 324 | * play (pos) -> integer 325 | * 326 | * Start playing color sequence (at pos) 327 | * 328 | * Return the actual number of bytes written and -1 on error. 329 | */ 330 | static VALUE rb_blink1_play(VALUE self, VALUE pos) { 331 | struct Blink1Instance *ins; 332 | Data_Get_Struct(self, struct Blink1Instance, ins); 333 | return INT2NUM(blink1_play(ins->dev, 1, FIX2UINT(pos))); 334 | } 335 | 336 | /** 337 | * :call-seq: 338 | * stop (pos) -> integer 339 | * 340 | * Stop playing color sequence (at pos) 341 | * 342 | * Return the actual number of bytes written and -1 on error. 343 | */ 344 | static VALUE rb_blink1_stop(VALUE self, VALUE pos) { 345 | struct Blink1Instance *ins; 346 | Data_Get_Struct(self, struct Blink1Instance, ins); 347 | return INT2NUM(blink1_play(ins->dev, 0, FIX2UINT(pos))); 348 | } 349 | 350 | /** 351 | * :call-seq: 352 | * write_pattern_line (fade_millis, r, g, b, pos) -> integer 353 | * 354 | * Write pattern RGB value at pos 355 | * 356 | * Return the actual number of bytes written and -1 on error. 357 | */ 358 | static VALUE rb_blink1_writePatternLine(VALUE self, VALUE fadeMillis, VALUE r, VALUE g, VALUE b, VALUE pos) { 359 | struct Blink1Instance *ins; 360 | Data_Get_Struct(self, struct Blink1Instance, ins); 361 | return INT2NUM(blink1_writePatternLine(ins->dev, FIX2UINT(fadeMillis), FIX2UINT(r), FIX2UINT(g), FIX2UINT(b), FIX2UINT(pos))); 362 | } 363 | 364 | /** 365 | * :call-seq: 366 | * read_pattern_line (pos) -> hash 367 | * 368 | * Read pattern RGB value at pos 369 | * 370 | * Return hash with value with key +"fade_millis"+, +"r"+, +"g"+, +"b"+ 371 | */ 372 | static VALUE rb_blink1_readPatternLine(VALUE self, VALUE pos) { 373 | struct Blink1Instance *ins; 374 | Data_Get_Struct(self, struct Blink1Instance, ins); 375 | uint16_t fadeMillis; uint8_t r; uint8_t g; uint8_t b; 376 | blink1_readPatternLine(ins->dev, &fadeMillis, &r, &g, &b, FIX2UINT(pos)); 377 | VALUE hash = rb_hash_new(); 378 | rb_hash_aset(hash, rb_str_new2("fade_millis"), UINT2NUM(fadeMillis)); 379 | rb_hash_aset(hash, rb_str_new2("r"), UINT2NUM(r)); 380 | rb_hash_aset(hash, rb_str_new2("g"), UINT2NUM(g)); 381 | rb_hash_aset(hash, rb_str_new2("b"), UINT2NUM(b)); 382 | return hash; 383 | } 384 | 385 | // UNSPPORT 386 | // int blink1_write( hid_device* dev, void* buf, int len); 387 | // int blink1_read( hid_device* dev, void* buf, int len); 388 | // int blink1_getSerialNumber(hid_device *dev, char* buf); 389 | // int blink1_playPattern(hid_device *dev,,); 390 | 391 | 392 | void Init_blink1() { 393 | VALUE module; 394 | VALUE klass = rb_define_class("Blink1", rb_cObject); 395 | 396 | rb_define_singleton_method(klass, "vendor_id", rb_blink1_vid, 0); 397 | rb_define_singleton_method(klass, "product_id", rb_blink1_pid, 0); 398 | rb_define_singleton_method(klass, "sort_paths", rb_blink1_sortPaths, 0); 399 | rb_define_singleton_method(klass, "sort_serials", rb_blink1_sortSerials, 0); 400 | rb_define_singleton_method(klass, "enumerate", rb_blink1_enumerate, 0); 401 | rb_define_singleton_method(klass, "enumerate_vid_pid", rb_blink1_enumerateByVidPid, 2); 402 | rb_define_singleton_method(klass, "cached_path", rb_blink1_blink1_getCachedPath, 1); 403 | rb_define_singleton_method(klass, "cached_serial", rb_blink1_getCachedSerial, 1); 404 | rb_define_singleton_method(klass, "cached_count", rb_blink1_getCachedCount, 0); 405 | rb_define_singleton_method(klass, "error_message", rb_blink1_error_msg, 1); // Not implemented in the library 406 | rb_define_singleton_method(klass, "degamma_enabled", rb_blink1_getDegammaEnabled, 0); 407 | rb_define_singleton_method(klass, "degamma_enabled=", rb_blink1_setDegammaEnabled, 1); 408 | rb_define_singleton_method(klass, "degamma", rb_blink1_degamma, 1); 409 | rb_define_singleton_method(klass, "sleep", rb_blink1_sleep, 1); 410 | 411 | rb_define_alloc_func(klass, rb_blink1_allocate); 412 | 413 | rb_define_method(klass, "opened?", rb_blink1_opened, 0); 414 | rb_define_method(klass, "open", rb_blink1_open, 0); 415 | rb_define_method(klass, "open_by_path", rb_blink1_openByPath, 1); 416 | rb_define_method(klass, "open_by_serial", rb_blink1_openBySerial, 1); 417 | rb_define_method(klass, "open_by_id", rb_blink1_openById, 1); 418 | rb_define_method(klass, "close", rb_blink1_close, 0); 419 | rb_define_method(klass, "version", rb_blink1_getVersion, 0); 420 | rb_define_method(klass, "fade_to_rgb", rb_blink1_fadeToRGB, 4); 421 | rb_define_method(klass, "fade_to_rgbn", rb_blink1_fadeToRGBN, 5); 422 | rb_define_method(klass, "set_rgb", rb_blink1_setRGB, 3); 423 | rb_define_method(klass, "eeread", rb_blink1_eeread, 1); 424 | rb_define_method(klass, "eewrite", rb_blink1_eewrite, 2); 425 | rb_define_method(klass, "serialnum=", rb_blink1_serialnumwrite, 1); 426 | rb_define_method(klass, "serialnum", rb_blink1_serialnumread, 0); 427 | rb_define_method(klass, "serverdown", rb_blink1_serverdown, 2); 428 | rb_define_method(klass, "play", rb_blink1_play, 1); 429 | rb_define_method(klass, "stop", rb_blink1_stop, 1); 430 | rb_define_method(klass, "read_pattern_line", rb_blink1_readPatternLine, 1); 431 | rb_define_method(klass, "write_pattern_line", rb_blink1_writePatternLine, 5); 432 | } 433 | 434 | -------------------------------------------------------------------------------- /ext/blink1/color_funcs.h: -------------------------------------------------------------------------------- 1 | // 2 | // color_funcs.h -- color sliding 3 | // 4 | // 2012, Tod E. Kurt, http://todbot.com/blog/ 5 | // 6 | // 7 | // also see: 8 | // http://meyerweb.com/eric/tools/color-blend/ 9 | // 10 | // 11 | 12 | #ifndef RGB_FUNCS_H 13 | #define RGB_FUNCS_H 14 | 15 | #include 16 | 17 | // RGB triplet of 8-bit vals for input/output use 18 | typedef struct { 19 | uint8_t r; 20 | uint8_t g; 21 | uint8_t b; 22 | } rgb_t; 23 | 24 | // RGB triplet unsigned ints for internal use of 100x scale 25 | // used instead of floating point 26 | typedef struct { 27 | int r; 28 | int g; 29 | int b; 30 | } rgbint_t; 31 | 32 | // 33 | typedef struct { 34 | rgb_t color; 35 | uint16_t dmillis; // hundreths of a sec 36 | } patternline_t; 37 | 38 | #define setRGBt(rgbt,x,y,z) { rgbt.r=x; rgbt.g=y; rgbt.b=z; } 39 | 40 | rgbint_t dest100x; // the eventual destination color we want to hit 41 | rgbint_t step100x; // the amount of to move each tick 42 | rgbint_t curr100x; // the current color, times 10 (to lessen int trunc issue) 43 | int stepcnt; 44 | 45 | #ifndef setRGBOut 46 | #error "setRGBOut(r,g,b) not defined" 47 | #endif 48 | 49 | // set the current color 50 | void rgb_setCurr( rgb_t* newcolor ) 51 | { 52 | curr100x.r = newcolor->r * 100; 53 | curr100x.g = newcolor->g * 100; 54 | curr100x.b = newcolor->b * 100; 55 | 56 | dest100x.r = curr100x.r; 57 | dest100x.g = curr100x.g; 58 | dest100x.b = curr100x.b; 59 | stepcnt = 0; 60 | 61 | setRGBOut( newcolor->r, newcolor->g, newcolor->b ); 62 | } 63 | 64 | // set a new destination color 65 | void rgb_setDest( rgb_t* newcolor, int steps ) 66 | { 67 | dest100x.r = newcolor->r*100; 68 | dest100x.g = newcolor->g*100; 69 | dest100x.b = newcolor->b*100; 70 | 71 | stepcnt = steps+1; 72 | 73 | step100x.r = (dest100x.r - curr100x.r ) / steps; 74 | step100x.g = (dest100x.g - curr100x.g ) / steps; 75 | step100x.b = (dest100x.b - curr100x.b ) / steps; 76 | } 77 | 78 | // call at every tick 79 | void rgb_updateCurrent(void) 80 | { 81 | if( !stepcnt ) { 82 | return; 83 | } 84 | stepcnt--; 85 | if( stepcnt ) { 86 | curr100x.r += step100x.r; 87 | curr100x.g += step100x.g; 88 | curr100x.b += step100x.b; 89 | } else { 90 | curr100x.r = dest100x.r; 91 | curr100x.g = dest100x.g; 92 | curr100x.b = dest100x.b; 93 | } 94 | 95 | setRGBOut( curr100x.r/100, curr100x.g/100, curr100x.b/100 ); 96 | } 97 | 98 | 99 | #endif 100 | 101 | 102 | -------------------------------------------------------------------------------- /ext/blink1/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'mkmf' 2 | 3 | uname = `uname -s`.chop.split 4 | 5 | dir_config 'blink1' 6 | 7 | case uname[0] 8 | when 'Darwin' 9 | 10 | $CFLAGS << 11 | ' -arch x86_64 -pthread ' 12 | $LIBS << 13 | ' -framework IOKit -framework CoreFoundation ' 14 | 15 | # RbConfig::MAKEFILE_CONFIG['CC'] = 'gcc' 16 | 17 | $HID_C = "#{$srcdir}/hid.c.mac" 18 | 19 | when 'Windows_NT' 20 | 21 | $CFLAGS << 22 | ' -arch i386 -arch x86_64 ' << 23 | ' -pthread ' 24 | 25 | $LIBS << 26 | ' -lsetupapi -Wl,--enable-auto-import -static-libgcc -static-libstdc++ ' 27 | 28 | $HID_C = "#{$srcdir}/hid.c.windows" 29 | 30 | when 'Linux' 31 | 32 | $CFLAGS << 33 | " #{ `pkg-config libusb-1.0 --cflags`.strip } " 34 | 35 | $LIBS << 36 | " #{ `pkg-config libusb-1.0 --libs`.strip }" 37 | ' -lrt -lpthread -ldl -static ' 38 | 39 | $HID_C = "#{$srcdir}/hid.c.libusb" 40 | 41 | when 'FreeBSD' 42 | 43 | $LIBS << 44 | ' -L/usr/local/lib -lusb -lrt -lpthread -liconv -static ' 45 | 46 | $HID_C = "#{$srcdir}/hid.c.libusb" 47 | 48 | end 49 | 50 | FileUtils.copy $HID_C, "#{$srcdir}/hid.c" 51 | 52 | $CFLAGS << " -std=gnu99 -I#{$srcdir}/include" 53 | 54 | create_makefile('blink1') 55 | -------------------------------------------------------------------------------- /ext/blink1/hid.c.libusb: -------------------------------------------------------------------------------- 1 | /******************************************************* 2 | HIDAPI - Multi-Platform library for 3 | communication with HID devices. 4 | 5 | Alan Ott 6 | Signal 11 Software 7 | 8 | 8/22/2009 9 | Linux Version - 6/2/2010 10 | Libusb Version - 8/13/2010 11 | FreeBSD Version - 11/1/2011 12 | 13 | Copyright 2009, All Rights Reserved. 14 | 15 | At the discretion of the user of this library, 16 | this software may be licensed under the terms of the 17 | GNU Public License v3, a BSD-Style license, or the 18 | original HIDAPI license as outlined in the LICENSE.txt, 19 | LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt 20 | files located at the root of the source distribution. 21 | These files may also be found in the public source 22 | code repository located at: 23 | http://github.com/signal11/hidapi . 24 | ********************************************************/ 25 | 26 | #define _GNU_SOURCE /* needed for wcsdup() before glibc 2.10 */ 27 | 28 | /* C */ 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | /* Unix */ 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | /* GNU / LibUSB */ 47 | #include "libusb.h" 48 | #include "iconv.h" 49 | 50 | #include "hidapi.h" 51 | 52 | #ifdef __cplusplus 53 | extern "C" { 54 | #endif 55 | 56 | #ifdef DEBUG_PRINTF 57 | #define LOG(...) fprintf(stderr, __VA_ARGS__) 58 | #else 59 | #define LOG(...) do {} while (0) 60 | #endif 61 | 62 | #ifndef __FreeBSD__ 63 | #define DETACH_KERNEL_DRIVER 64 | #endif 65 | 66 | /* Uncomment to enable the retrieval of Usage and Usage Page in 67 | hid_enumerate(). Warning, on platforms different from FreeBSD 68 | this is very invasive as it requires the detach 69 | and re-attach of the kernel driver. See comments inside hid_enumerate(). 70 | libusb HIDAPI programs are encouraged to use the interface number 71 | instead to differentiate between interfaces on a composite HID device. */ 72 | /*#define INVASIVE_GET_USAGE*/ 73 | 74 | /* Linked List of input reports received from the device. */ 75 | struct input_report { 76 | uint8_t *data; 77 | size_t len; 78 | struct input_report *next; 79 | }; 80 | 81 | 82 | struct hid_device_ { 83 | /* Handle to the actual device. */ 84 | libusb_device_handle *device_handle; 85 | 86 | /* Endpoint information */ 87 | int input_endpoint; 88 | int output_endpoint; 89 | int input_ep_max_packet_size; 90 | 91 | /* The interface number of the HID */ 92 | int interface; 93 | 94 | /* Indexes of Strings */ 95 | int manufacturer_index; 96 | int product_index; 97 | int serial_index; 98 | 99 | /* Whether blocking reads are used */ 100 | int blocking; /* boolean */ 101 | 102 | /* Read thread objects */ 103 | pthread_t thread; 104 | pthread_mutex_t mutex; /* Protects input_reports */ 105 | pthread_cond_t condition; 106 | pthread_barrier_t barrier; /* Ensures correct startup sequence */ 107 | int shutdown_thread; 108 | struct libusb_transfer *transfer; 109 | 110 | /* List of received input reports. */ 111 | struct input_report *input_reports; 112 | }; 113 | 114 | static libusb_context *usb_context = NULL; 115 | 116 | uint16_t get_usb_code_for_current_locale(void); 117 | static int return_data(hid_device *dev, unsigned char *data, size_t length); 118 | 119 | static hid_device *new_hid_device(void) 120 | { 121 | hid_device *dev = calloc(1, sizeof(hid_device)); 122 | dev->blocking = 1; 123 | 124 | pthread_mutex_init(&dev->mutex, NULL); 125 | pthread_cond_init(&dev->condition, NULL); 126 | pthread_barrier_init(&dev->barrier, NULL, 2); 127 | 128 | return dev; 129 | } 130 | 131 | static void free_hid_device(hid_device *dev) 132 | { 133 | /* Clean up the thread objects */ 134 | pthread_barrier_destroy(&dev->barrier); 135 | pthread_cond_destroy(&dev->condition); 136 | pthread_mutex_destroy(&dev->mutex); 137 | 138 | /* Free the device itself */ 139 | free(dev); 140 | } 141 | 142 | #if 0 143 | /*TODO: Implement this funciton on hidapi/libusb.. */ 144 | static void register_error(hid_device *device, const char *op) 145 | { 146 | 147 | } 148 | #endif 149 | 150 | #ifdef INVASIVE_GET_USAGE 151 | /* Get bytes from a HID Report Descriptor. 152 | Only call with a num_bytes of 0, 1, 2, or 4. */ 153 | static uint32_t get_bytes(uint8_t *rpt, size_t len, size_t num_bytes, size_t cur) 154 | { 155 | /* Return if there aren't enough bytes. */ 156 | if (cur + num_bytes >= len) 157 | return 0; 158 | 159 | if (num_bytes == 0) 160 | return 0; 161 | else if (num_bytes == 1) { 162 | return rpt[cur+1]; 163 | } 164 | else if (num_bytes == 2) { 165 | return (rpt[cur+2] * 256 + rpt[cur+1]); 166 | } 167 | else if (num_bytes == 4) { 168 | return (rpt[cur+4] * 0x01000000 + 169 | rpt[cur+3] * 0x00010000 + 170 | rpt[cur+2] * 0x00000100 + 171 | rpt[cur+1] * 0x00000001); 172 | } 173 | else 174 | return 0; 175 | } 176 | 177 | /* Retrieves the device's Usage Page and Usage from the report 178 | descriptor. The algorithm is simple, as it just returns the first 179 | Usage and Usage Page that it finds in the descriptor. 180 | The return value is 0 on success and -1 on failure. */ 181 | static int get_usage(uint8_t *report_descriptor, size_t size, 182 | unsigned short *usage_page, unsigned short *usage) 183 | { 184 | unsigned int i = 0; 185 | int size_code; 186 | int data_len, key_size; 187 | int usage_found = 0, usage_page_found = 0; 188 | 189 | while (i < size) { 190 | int key = report_descriptor[i]; 191 | int key_cmd = key & 0xfc; 192 | 193 | //printf("key: %02hhx\n", key); 194 | 195 | if ((key & 0xf0) == 0xf0) { 196 | /* This is a Long Item. The next byte contains the 197 | length of the data section (value) for this key. 198 | See the HID specification, version 1.11, section 199 | 6.2.2.3, titled "Long Items." */ 200 | if (i+1 < size) 201 | data_len = report_descriptor[i+1]; 202 | else 203 | data_len = 0; /* malformed report */ 204 | key_size = 3; 205 | } 206 | else { 207 | /* This is a Short Item. The bottom two bits of the 208 | key contain the size code for the data section 209 | (value) for this key. Refer to the HID 210 | specification, version 1.11, section 6.2.2.2, 211 | titled "Short Items." */ 212 | size_code = key & 0x3; 213 | switch (size_code) { 214 | case 0: 215 | case 1: 216 | case 2: 217 | data_len = size_code; 218 | break; 219 | case 3: 220 | data_len = 4; 221 | break; 222 | default: 223 | /* Can't ever happen since size_code is & 0x3 */ 224 | data_len = 0; 225 | break; 226 | }; 227 | key_size = 1; 228 | } 229 | 230 | if (key_cmd == 0x4) { 231 | *usage_page = get_bytes(report_descriptor, size, data_len, i); 232 | usage_page_found = 1; 233 | //printf("Usage Page: %x\n", (uint32_t)*usage_page); 234 | } 235 | if (key_cmd == 0x8) { 236 | *usage = get_bytes(report_descriptor, size, data_len, i); 237 | usage_found = 1; 238 | //printf("Usage: %x\n", (uint32_t)*usage); 239 | } 240 | 241 | if (usage_page_found && usage_found) 242 | return 0; /* success */ 243 | 244 | /* Skip over this key and it's associated data */ 245 | i += data_len + key_size; 246 | } 247 | 248 | return -1; /* failure */ 249 | } 250 | #endif /* INVASIVE_GET_USAGE */ 251 | 252 | #ifdef __FreeBSD__ 253 | /* The FreeBSD version of libusb doesn't have this funciton. In mainline 254 | libusb, it's inlined in libusb.h. This function will bear a striking 255 | resemblence to that one, because there's about one way to code it. 256 | 257 | Note that the data parameter is Unicode in UTF-16LE encoding. 258 | Return value is the number of bytes in data, or LIBUSB_ERROR_*. 259 | */ 260 | static inline int libusb_get_string_descriptor(libusb_device_handle *dev, 261 | uint8_t descriptor_index, uint16_t lang_id, 262 | unsigned char *data, int length) 263 | { 264 | return libusb_control_transfer(dev, 265 | LIBUSB_ENDPOINT_IN | 0x0, /* Endpoint 0 IN */ 266 | LIBUSB_REQUEST_GET_DESCRIPTOR, 267 | (LIBUSB_DT_STRING << 8) | descriptor_index, 268 | lang_id, data, (uint16_t) length, 1000); 269 | } 270 | 271 | #endif 272 | 273 | 274 | /* Get the first language the device says it reports. This comes from 275 | USB string #0. */ 276 | static uint16_t get_first_language(libusb_device_handle *dev) 277 | { 278 | uint16_t buf[32]; 279 | int len; 280 | 281 | /* Get the string from libusb. */ 282 | len = libusb_get_string_descriptor(dev, 283 | 0x0, /* String ID */ 284 | 0x0, /* Language */ 285 | (unsigned char*)buf, 286 | sizeof(buf)); 287 | if (len < 4) 288 | return 0x0; 289 | 290 | return buf[1]; /* First two bytes are len and descriptor type. */ 291 | } 292 | 293 | static int is_language_supported(libusb_device_handle *dev, uint16_t lang) 294 | { 295 | uint16_t buf[32]; 296 | int len; 297 | int i; 298 | 299 | /* Get the string from libusb. */ 300 | len = libusb_get_string_descriptor(dev, 301 | 0x0, /* String ID */ 302 | 0x0, /* Language */ 303 | (unsigned char*)buf, 304 | sizeof(buf)); 305 | if (len < 4) 306 | return 0x0; 307 | 308 | 309 | len /= 2; /* language IDs are two-bytes each. */ 310 | /* Start at index 1 because there are two bytes of protocol data. */ 311 | for (i = 1; i < len; i++) { 312 | if (buf[i] == lang) 313 | return 1; 314 | } 315 | 316 | return 0; 317 | } 318 | 319 | 320 | /* This function returns a newly allocated wide string containing the USB 321 | device string numbered by the index. The returned string must be freed 322 | by using free(). */ 323 | static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx) 324 | { 325 | char buf[512]; 326 | int len; 327 | wchar_t *str = NULL; 328 | wchar_t wbuf[256]; 329 | 330 | /* iconv variables */ 331 | iconv_t ic; 332 | size_t inbytes; 333 | size_t outbytes; 334 | size_t res; 335 | #ifdef __FreeBSD__ 336 | const char *inptr; 337 | #else 338 | char *inptr; 339 | #endif 340 | char *outptr; 341 | 342 | /* Determine which language to use. */ 343 | uint16_t lang; 344 | lang = get_usb_code_for_current_locale(); 345 | if (!is_language_supported(dev, lang)) 346 | lang = get_first_language(dev); 347 | 348 | /* Get the string from libusb. */ 349 | len = libusb_get_string_descriptor(dev, 350 | idx, 351 | lang, 352 | (unsigned char*)buf, 353 | sizeof(buf)); 354 | if (len < 0) 355 | return NULL; 356 | 357 | /* buf does not need to be explicitly NULL-terminated because 358 | it is only passed into iconv() which does not need it. */ 359 | 360 | /* Initialize iconv. */ 361 | ic = iconv_open("WCHAR_T", "UTF-16LE"); 362 | if (ic == (iconv_t)-1) { 363 | LOG("iconv_open() failed\n"); 364 | return NULL; 365 | } 366 | 367 | /* Convert to native wchar_t (UTF-32 on glibc/BSD systems). 368 | Skip the first character (2-bytes). */ 369 | inptr = buf+2; 370 | inbytes = len-2; 371 | outptr = (char*) wbuf; 372 | outbytes = sizeof(wbuf); 373 | res = iconv(ic, &inptr, &inbytes, &outptr, &outbytes); 374 | if (res == (size_t)-1) { 375 | LOG("iconv() failed\n"); 376 | goto err; 377 | } 378 | 379 | /* Write the terminating NULL. */ 380 | wbuf[sizeof(wbuf)/sizeof(wbuf[0])-1] = 0x00000000; 381 | if (outbytes >= sizeof(wbuf[0])) 382 | *((wchar_t*)outptr) = 0x00000000; 383 | 384 | /* Allocate and copy the string. */ 385 | str = wcsdup(wbuf); 386 | 387 | err: 388 | iconv_close(ic); 389 | 390 | return str; 391 | } 392 | 393 | static char *make_path(libusb_device *dev, int interface_number) 394 | { 395 | char str[64]; 396 | snprintf(str, sizeof(str), "%04x:%04x:%02x", 397 | libusb_get_bus_number(dev), 398 | libusb_get_device_address(dev), 399 | interface_number); 400 | str[sizeof(str)-1] = '\0'; 401 | 402 | return strdup(str); 403 | } 404 | 405 | 406 | int HID_API_EXPORT hid_init(void) 407 | { 408 | if (!usb_context) { 409 | const char *locale; 410 | 411 | /* Init Libusb */ 412 | if (libusb_init(&usb_context)) 413 | return -1; 414 | 415 | /* Set the locale if it's not set. */ 416 | locale = setlocale(LC_CTYPE, NULL); 417 | if (!locale) 418 | setlocale(LC_CTYPE, ""); 419 | } 420 | 421 | return 0; 422 | } 423 | 424 | int HID_API_EXPORT hid_exit(void) 425 | { 426 | if (usb_context) { 427 | libusb_exit(usb_context); 428 | usb_context = NULL; 429 | } 430 | 431 | return 0; 432 | } 433 | 434 | struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) 435 | { 436 | libusb_device **devs; 437 | libusb_device *dev; 438 | libusb_device_handle *handle; 439 | ssize_t num_devs; 440 | int i = 0; 441 | 442 | struct hid_device_info *root = NULL; /* return object */ 443 | struct hid_device_info *cur_dev = NULL; 444 | 445 | if(hid_init() < 0) 446 | return NULL; 447 | 448 | num_devs = libusb_get_device_list(usb_context, &devs); 449 | if (num_devs < 0) 450 | return NULL; 451 | while ((dev = devs[i++]) != NULL) { 452 | struct libusb_device_descriptor desc; 453 | struct libusb_config_descriptor *conf_desc = NULL; 454 | int j, k; 455 | int interface_num = 0; 456 | 457 | int res = libusb_get_device_descriptor(dev, &desc); 458 | unsigned short dev_vid = desc.idVendor; 459 | unsigned short dev_pid = desc.idProduct; 460 | 461 | res = libusb_get_active_config_descriptor(dev, &conf_desc); 462 | if (res < 0) 463 | libusb_get_config_descriptor(dev, 0, &conf_desc); 464 | if (conf_desc) { 465 | for (j = 0; j < conf_desc->bNumInterfaces; j++) { 466 | const struct libusb_interface *intf = &conf_desc->interface[j]; 467 | for (k = 0; k < intf->num_altsetting; k++) { 468 | const struct libusb_interface_descriptor *intf_desc; 469 | intf_desc = &intf->altsetting[k]; 470 | if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { 471 | interface_num = intf_desc->bInterfaceNumber; 472 | 473 | /* Check the VID/PID against the arguments */ 474 | if ((vendor_id == 0x0 || vendor_id == dev_vid) && 475 | (product_id == 0x0 || product_id == dev_pid)) { 476 | struct hid_device_info *tmp; 477 | 478 | /* VID/PID match. Create the record. */ 479 | tmp = calloc(1, sizeof(struct hid_device_info)); 480 | if (cur_dev) { 481 | cur_dev->next = tmp; 482 | } 483 | else { 484 | root = tmp; 485 | } 486 | cur_dev = tmp; 487 | 488 | /* Fill out the record */ 489 | cur_dev->next = NULL; 490 | cur_dev->path = make_path(dev, interface_num); 491 | 492 | res = libusb_open(dev, &handle); 493 | 494 | if (res >= 0) { 495 | /* Serial Number */ 496 | if (desc.iSerialNumber > 0) 497 | cur_dev->serial_number = 498 | get_usb_string(handle, desc.iSerialNumber); 499 | 500 | /* Manufacturer and Product strings */ 501 | if (desc.iManufacturer > 0) 502 | cur_dev->manufacturer_string = 503 | get_usb_string(handle, desc.iManufacturer); 504 | if (desc.iProduct > 0) 505 | cur_dev->product_string = 506 | get_usb_string(handle, desc.iProduct); 507 | 508 | #ifdef INVASIVE_GET_USAGE 509 | { 510 | /* 511 | This section is removed because it is too 512 | invasive on the system. Getting a Usage Page 513 | and Usage requires parsing the HID Report 514 | descriptor. Getting a HID Report descriptor 515 | involves claiming the interface. Claiming the 516 | interface involves detaching the kernel driver. 517 | Detaching the kernel driver is hard on the system 518 | because it will unclaim interfaces (if another 519 | app has them claimed) and the re-attachment of 520 | the driver will sometimes change /dev entry names. 521 | It is for these reasons that this section is 522 | #if 0. For composite devices, use the interface 523 | field in the hid_device_info struct to distinguish 524 | between interfaces. */ 525 | unsigned char data[256]; 526 | #ifdef DETACH_KERNEL_DRIVER 527 | int detached = 0; 528 | /* Usage Page and Usage */ 529 | res = libusb_kernel_driver_active(handle, interface_num); 530 | if (res == 1) { 531 | res = libusb_detach_kernel_driver(handle, interface_num); 532 | if (res < 0) 533 | LOG("Couldn't detach kernel driver, even though a kernel driver was attached."); 534 | else 535 | detached = 1; 536 | } 537 | #endif 538 | res = libusb_claim_interface(handle, interface_num); 539 | if (res >= 0) { 540 | /* Get the HID Report Descriptor. */ 541 | res = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8)|interface_num, 0, data, sizeof(data), 5000); 542 | if (res >= 0) { 543 | unsigned short page=0, usage=0; 544 | /* Parse the usage and usage page 545 | out of the report descriptor. */ 546 | get_usage(data, res, &page, &usage); 547 | cur_dev->usage_page = page; 548 | cur_dev->usage = usage; 549 | } 550 | else 551 | LOG("libusb_control_transfer() for getting the HID report failed with %d\n", res); 552 | 553 | /* Release the interface */ 554 | res = libusb_release_interface(handle, interface_num); 555 | if (res < 0) 556 | LOG("Can't release the interface.\n"); 557 | } 558 | else 559 | LOG("Can't claim interface %d\n", res); 560 | #ifdef DETACH_KERNEL_DRIVER 561 | /* Re-attach kernel driver if necessary. */ 562 | if (detached) { 563 | res = libusb_attach_kernel_driver(handle, interface_num); 564 | if (res < 0) 565 | LOG("Couldn't re-attach kernel driver.\n"); 566 | } 567 | #endif 568 | } 569 | #endif /* INVASIVE_GET_USAGE */ 570 | 571 | libusb_close(handle); 572 | } 573 | /* VID/PID */ 574 | cur_dev->vendor_id = dev_vid; 575 | cur_dev->product_id = dev_pid; 576 | 577 | /* Release Number */ 578 | cur_dev->release_number = desc.bcdDevice; 579 | 580 | /* Interface Number */ 581 | cur_dev->interface_number = interface_num; 582 | } 583 | } 584 | } /* altsettings */ 585 | } /* interfaces */ 586 | libusb_free_config_descriptor(conf_desc); 587 | } 588 | } 589 | 590 | libusb_free_device_list(devs, 1); 591 | 592 | return root; 593 | } 594 | 595 | void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) 596 | { 597 | struct hid_device_info *d = devs; 598 | while (d) { 599 | struct hid_device_info *next = d->next; 600 | free(d->path); 601 | free(d->serial_number); 602 | free(d->manufacturer_string); 603 | free(d->product_string); 604 | free(d); 605 | d = next; 606 | } 607 | } 608 | 609 | hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) 610 | { 611 | struct hid_device_info *devs, *cur_dev; 612 | const char *path_to_open = NULL; 613 | hid_device *handle = NULL; 614 | 615 | devs = hid_enumerate(vendor_id, product_id); 616 | cur_dev = devs; 617 | while (cur_dev) { 618 | if (cur_dev->vendor_id == vendor_id && 619 | cur_dev->product_id == product_id) { 620 | if (serial_number) { 621 | if (wcscmp(serial_number, cur_dev->serial_number) == 0) { 622 | path_to_open = cur_dev->path; 623 | break; 624 | } 625 | } 626 | else { 627 | path_to_open = cur_dev->path; 628 | break; 629 | } 630 | } 631 | cur_dev = cur_dev->next; 632 | } 633 | 634 | if (path_to_open) { 635 | /* Open the device */ 636 | handle = hid_open_path(path_to_open); 637 | } 638 | 639 | hid_free_enumeration(devs); 640 | 641 | return handle; 642 | } 643 | 644 | static void read_callback(struct libusb_transfer *transfer) 645 | { 646 | hid_device *dev = transfer->user_data; 647 | int res; 648 | 649 | if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { 650 | 651 | struct input_report *rpt = malloc(sizeof(*rpt)); 652 | rpt->data = malloc(transfer->actual_length); 653 | memcpy(rpt->data, transfer->buffer, transfer->actual_length); 654 | rpt->len = transfer->actual_length; 655 | rpt->next = NULL; 656 | 657 | pthread_mutex_lock(&dev->mutex); 658 | 659 | /* Attach the new report object to the end of the list. */ 660 | if (dev->input_reports == NULL) { 661 | /* The list is empty. Put it at the root. */ 662 | dev->input_reports = rpt; 663 | pthread_cond_signal(&dev->condition); 664 | } 665 | else { 666 | /* Find the end of the list and attach. */ 667 | struct input_report *cur = dev->input_reports; 668 | int num_queued = 0; 669 | while (cur->next != NULL) { 670 | cur = cur->next; 671 | num_queued++; 672 | } 673 | cur->next = rpt; 674 | 675 | /* Pop one off if we've reached 30 in the queue. This 676 | way we don't grow forever if the user never reads 677 | anything from the device. */ 678 | if (num_queued > 30) { 679 | return_data(dev, NULL, 0); 680 | } 681 | } 682 | pthread_mutex_unlock(&dev->mutex); 683 | } 684 | else if (transfer->status == LIBUSB_TRANSFER_CANCELLED) { 685 | dev->shutdown_thread = 1; 686 | return; 687 | } 688 | else if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) { 689 | dev->shutdown_thread = 1; 690 | return; 691 | } 692 | else if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT) { 693 | //LOG("Timeout (normal)\n"); 694 | } 695 | else { 696 | LOG("Unknown transfer code: %d\n", transfer->status); 697 | } 698 | 699 | /* Re-submit the transfer object. */ 700 | res = libusb_submit_transfer(transfer); 701 | if (res != 0) { 702 | LOG("Unable to submit URB. libusb error code: %d\n", res); 703 | dev->shutdown_thread = 1; 704 | } 705 | } 706 | 707 | 708 | static void *read_thread(void *param) 709 | { 710 | hid_device *dev = param; 711 | unsigned char *buf; 712 | const size_t length = dev->input_ep_max_packet_size; 713 | 714 | /* Set up the transfer object. */ 715 | buf = malloc(length); 716 | dev->transfer = libusb_alloc_transfer(0); 717 | libusb_fill_interrupt_transfer(dev->transfer, 718 | dev->device_handle, 719 | dev->input_endpoint, 720 | buf, 721 | length, 722 | read_callback, 723 | dev, 724 | 5000/*timeout*/); 725 | 726 | /* Make the first submission. Further submissions are made 727 | from inside read_callback() */ 728 | libusb_submit_transfer(dev->transfer); 729 | 730 | /* Notify the main thread that the read thread is up and running. */ 731 | pthread_barrier_wait(&dev->barrier); 732 | 733 | /* Handle all the events. */ 734 | while (!dev->shutdown_thread) { 735 | int res; 736 | res = libusb_handle_events(usb_context); 737 | if (res < 0) { 738 | /* There was an error. */ 739 | LOG("read_thread(): libusb reports error # %d\n", res); 740 | 741 | /* Break out of this loop only on fatal error.*/ 742 | if (res != LIBUSB_ERROR_BUSY && 743 | res != LIBUSB_ERROR_TIMEOUT && 744 | res != LIBUSB_ERROR_OVERFLOW && 745 | res != LIBUSB_ERROR_INTERRUPTED) { 746 | break; 747 | } 748 | } 749 | } 750 | 751 | /* Cancel any transfer that may be pending. This call will fail 752 | if no transfers are pending, but that's OK. */ 753 | if (libusb_cancel_transfer(dev->transfer) == 0) { 754 | /* The transfer was cancelled, so wait for its completion. */ 755 | libusb_handle_events(usb_context); 756 | } 757 | 758 | /* Now that the read thread is stopping, Wake any threads which are 759 | waiting on data (in hid_read_timeout()). Do this under a mutex to 760 | make sure that a thread which is about to go to sleep waiting on 761 | the condition acutally will go to sleep before the condition is 762 | signaled. */ 763 | pthread_mutex_lock(&dev->mutex); 764 | pthread_cond_broadcast(&dev->condition); 765 | pthread_mutex_unlock(&dev->mutex); 766 | 767 | /* The dev->transfer->buffer and dev->transfer objects are cleaned up 768 | in hid_close(). They are not cleaned up here because this thread 769 | could end either due to a disconnect or due to a user 770 | call to hid_close(). In both cases the objects can be safely 771 | cleaned up after the call to pthread_join() (in hid_close()), but 772 | since hid_close() calls libusb_cancel_transfer(), on these objects, 773 | they can not be cleaned up here. */ 774 | 775 | return NULL; 776 | } 777 | 778 | 779 | hid_device * HID_API_EXPORT hid_open_path(const char *path) 780 | { 781 | hid_device *dev = NULL; 782 | 783 | libusb_device **devs; 784 | libusb_device *usb_dev; 785 | int res; 786 | int d = 0; 787 | int good_open = 0; 788 | 789 | dev = new_hid_device(); 790 | 791 | if(hid_init() < 0) 792 | return NULL; 793 | 794 | libusb_get_device_list(usb_context, &devs); 795 | while ((usb_dev = devs[d++]) != NULL) { 796 | struct libusb_device_descriptor desc; 797 | struct libusb_config_descriptor *conf_desc = NULL; 798 | int i,j,k; 799 | libusb_get_device_descriptor(usb_dev, &desc); 800 | 801 | if (libusb_get_active_config_descriptor(usb_dev, &conf_desc) < 0) 802 | continue; 803 | for (j = 0; j < conf_desc->bNumInterfaces; j++) { 804 | const struct libusb_interface *intf = &conf_desc->interface[j]; 805 | for (k = 0; k < intf->num_altsetting; k++) { 806 | const struct libusb_interface_descriptor *intf_desc; 807 | intf_desc = &intf->altsetting[k]; 808 | if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { 809 | char *dev_path = make_path(usb_dev, intf_desc->bInterfaceNumber); 810 | if (!strcmp(dev_path, path)) { 811 | /* Matched Paths. Open this device */ 812 | 813 | /* OPEN HERE */ 814 | res = libusb_open(usb_dev, &dev->device_handle); 815 | if (res < 0) { 816 | LOG("can't open device\n"); 817 | free(dev_path); 818 | break; 819 | } 820 | good_open = 1; 821 | #ifdef DETACH_KERNEL_DRIVER 822 | /* Detach the kernel driver, but only if the 823 | device is managed by the kernel */ 824 | if (libusb_kernel_driver_active(dev->device_handle, intf_desc->bInterfaceNumber) == 1) { 825 | res = libusb_detach_kernel_driver(dev->device_handle, intf_desc->bInterfaceNumber); 826 | if (res < 0) { 827 | libusb_close(dev->device_handle); 828 | LOG("Unable to detach Kernel Driver\n"); 829 | free(dev_path); 830 | good_open = 0; 831 | break; 832 | } 833 | } 834 | #endif 835 | res = libusb_claim_interface(dev->device_handle, intf_desc->bInterfaceNumber); 836 | if (res < 0) { 837 | LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res); 838 | free(dev_path); 839 | libusb_close(dev->device_handle); 840 | good_open = 0; 841 | break; 842 | } 843 | 844 | /* Store off the string descriptor indexes */ 845 | dev->manufacturer_index = desc.iManufacturer; 846 | dev->product_index = desc.iProduct; 847 | dev->serial_index = desc.iSerialNumber; 848 | 849 | /* Store off the interface number */ 850 | dev->interface = intf_desc->bInterfaceNumber; 851 | 852 | /* Find the INPUT and OUTPUT endpoints. An 853 | OUTPUT endpoint is not required. */ 854 | for (i = 0; i < intf_desc->bNumEndpoints; i++) { 855 | const struct libusb_endpoint_descriptor *ep 856 | = &intf_desc->endpoint[i]; 857 | 858 | /* Determine the type and direction of this 859 | endpoint. */ 860 | int is_interrupt = 861 | (ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) 862 | == LIBUSB_TRANSFER_TYPE_INTERRUPT; 863 | int is_output = 864 | (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) 865 | == LIBUSB_ENDPOINT_OUT; 866 | int is_input = 867 | (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) 868 | == LIBUSB_ENDPOINT_IN; 869 | 870 | /* Decide whether to use it for intput or output. */ 871 | if (dev->input_endpoint == 0 && 872 | is_interrupt && is_input) { 873 | /* Use this endpoint for INPUT */ 874 | dev->input_endpoint = ep->bEndpointAddress; 875 | dev->input_ep_max_packet_size = ep->wMaxPacketSize; 876 | } 877 | if (dev->output_endpoint == 0 && 878 | is_interrupt && is_output) { 879 | /* Use this endpoint for OUTPUT */ 880 | dev->output_endpoint = ep->bEndpointAddress; 881 | } 882 | } 883 | 884 | pthread_create(&dev->thread, NULL, read_thread, dev); 885 | 886 | /* Wait here for the read thread to be initialized. */ 887 | pthread_barrier_wait(&dev->barrier); 888 | 889 | } 890 | free(dev_path); 891 | } 892 | } 893 | } 894 | libusb_free_config_descriptor(conf_desc); 895 | 896 | } 897 | 898 | libusb_free_device_list(devs, 1); 899 | 900 | /* If we have a good handle, return it. */ 901 | if (good_open) { 902 | return dev; 903 | } 904 | else { 905 | /* Unable to open any devices. */ 906 | free_hid_device(dev); 907 | return NULL; 908 | } 909 | } 910 | 911 | 912 | int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) 913 | { 914 | int res; 915 | int report_number = data[0]; 916 | int skipped_report_id = 0; 917 | 918 | if (report_number == 0x0) { 919 | data++; 920 | length--; 921 | skipped_report_id = 1; 922 | } 923 | 924 | 925 | if (dev->output_endpoint <= 0) { 926 | /* No interrput out endpoint. Use the Control Endpoint */ 927 | res = libusb_control_transfer(dev->device_handle, 928 | LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT, 929 | 0x09/*HID Set_Report*/, 930 | (2/*HID output*/ << 8) | report_number, 931 | dev->interface, 932 | (unsigned char *)data, length, 933 | 1000/*timeout millis*/); 934 | 935 | if (res < 0) 936 | return -1; 937 | 938 | if (skipped_report_id) 939 | length++; 940 | 941 | return length; 942 | } 943 | else { 944 | /* Use the interrupt out endpoint */ 945 | int actual_length; 946 | res = libusb_interrupt_transfer(dev->device_handle, 947 | dev->output_endpoint, 948 | (unsigned char*)data, 949 | length, 950 | &actual_length, 1000); 951 | 952 | if (res < 0) 953 | return -1; 954 | 955 | if (skipped_report_id) 956 | actual_length++; 957 | 958 | return actual_length; 959 | } 960 | } 961 | 962 | /* Helper function, to simplify hid_read(). 963 | This should be called with dev->mutex locked. */ 964 | static int return_data(hid_device *dev, unsigned char *data, size_t length) 965 | { 966 | /* Copy the data out of the linked list item (rpt) into the 967 | return buffer (data), and delete the liked list item. */ 968 | struct input_report *rpt = dev->input_reports; 969 | size_t len = (length < rpt->len)? length: rpt->len; 970 | if (len > 0) 971 | memcpy(data, rpt->data, len); 972 | dev->input_reports = rpt->next; 973 | free(rpt->data); 974 | free(rpt); 975 | return len; 976 | } 977 | 978 | static void cleanup_mutex(void *param) 979 | { 980 | hid_device *dev = param; 981 | pthread_mutex_unlock(&dev->mutex); 982 | } 983 | 984 | 985 | int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) 986 | { 987 | int bytes_read = -1; 988 | 989 | #if 0 990 | int transferred; 991 | int res = libusb_interrupt_transfer(dev->device_handle, dev->input_endpoint, data, length, &transferred, 5000); 992 | LOG("transferred: %d\n", transferred); 993 | return transferred; 994 | #endif 995 | 996 | pthread_mutex_lock(&dev->mutex); 997 | pthread_cleanup_push(&cleanup_mutex, dev); 998 | 999 | /* There's an input report queued up. Return it. */ 1000 | if (dev->input_reports) { 1001 | /* Return the first one */ 1002 | bytes_read = return_data(dev, data, length); 1003 | goto ret; 1004 | } 1005 | 1006 | if (dev->shutdown_thread) { 1007 | /* This means the device has been disconnected. 1008 | An error code of -1 should be returned. */ 1009 | bytes_read = -1; 1010 | goto ret; 1011 | } 1012 | 1013 | if (milliseconds == -1) { 1014 | /* Blocking */ 1015 | while (!dev->input_reports && !dev->shutdown_thread) { 1016 | pthread_cond_wait(&dev->condition, &dev->mutex); 1017 | } 1018 | if (dev->input_reports) { 1019 | bytes_read = return_data(dev, data, length); 1020 | } 1021 | } 1022 | else if (milliseconds > 0) { 1023 | /* Non-blocking, but called with timeout. */ 1024 | int res; 1025 | struct timespec ts; 1026 | clock_gettime(CLOCK_REALTIME, &ts); 1027 | ts.tv_sec += milliseconds / 1000; 1028 | ts.tv_nsec += (milliseconds % 1000) * 1000000; 1029 | if (ts.tv_nsec >= 1000000000L) { 1030 | ts.tv_sec++; 1031 | ts.tv_nsec -= 1000000000L; 1032 | } 1033 | 1034 | while (!dev->input_reports && !dev->shutdown_thread) { 1035 | res = pthread_cond_timedwait(&dev->condition, &dev->mutex, &ts); 1036 | if (res == 0) { 1037 | if (dev->input_reports) { 1038 | bytes_read = return_data(dev, data, length); 1039 | break; 1040 | } 1041 | 1042 | /* If we're here, there was a spurious wake up 1043 | or the read thread was shutdown. Run the 1044 | loop again (ie: don't break). */ 1045 | } 1046 | else if (res == ETIMEDOUT) { 1047 | /* Timed out. */ 1048 | bytes_read = 0; 1049 | break; 1050 | } 1051 | else { 1052 | /* Error. */ 1053 | bytes_read = -1; 1054 | break; 1055 | } 1056 | } 1057 | } 1058 | else { 1059 | /* Purely non-blocking */ 1060 | bytes_read = 0; 1061 | } 1062 | 1063 | ret: 1064 | pthread_mutex_unlock(&dev->mutex); 1065 | pthread_cleanup_pop(0); 1066 | 1067 | return bytes_read; 1068 | } 1069 | 1070 | int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) 1071 | { 1072 | return hid_read_timeout(dev, data, length, dev->blocking ? -1 : 0); 1073 | } 1074 | 1075 | int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) 1076 | { 1077 | dev->blocking = !nonblock; 1078 | 1079 | return 0; 1080 | } 1081 | 1082 | 1083 | int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) 1084 | { 1085 | int res = -1; 1086 | int skipped_report_id = 0; 1087 | int report_number = data[0]; 1088 | 1089 | if (report_number == 0x0) { 1090 | data++; 1091 | length--; 1092 | skipped_report_id = 1; 1093 | } 1094 | 1095 | res = libusb_control_transfer(dev->device_handle, 1096 | LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT, 1097 | 0x09/*HID set_report*/, 1098 | (3/*HID feature*/ << 8) | report_number, 1099 | dev->interface, 1100 | (unsigned char *)data, length, 1101 | 1000/*timeout millis*/); 1102 | 1103 | if (res < 0) 1104 | return -1; 1105 | 1106 | /* Account for the report ID */ 1107 | if (skipped_report_id) 1108 | length++; 1109 | 1110 | return length; 1111 | } 1112 | 1113 | int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) 1114 | { 1115 | int res = -1; 1116 | int skipped_report_id = 0; 1117 | int report_number = data[0]; 1118 | 1119 | if (report_number == 0x0) { 1120 | /* Offset the return buffer by 1, so that the report ID 1121 | will remain in byte 0. */ 1122 | data++; 1123 | length--; 1124 | skipped_report_id = 1; 1125 | } 1126 | res = libusb_control_transfer(dev->device_handle, 1127 | LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_IN, 1128 | 0x01/*HID get_report*/, 1129 | (3/*HID feature*/ << 8) | report_number, 1130 | dev->interface, 1131 | (unsigned char *)data, length, 1132 | 1000/*timeout millis*/); 1133 | 1134 | if (res < 0) 1135 | return -1; 1136 | 1137 | if (skipped_report_id) 1138 | res++; 1139 | 1140 | return res; 1141 | } 1142 | 1143 | 1144 | void HID_API_EXPORT hid_close(hid_device *dev) 1145 | { 1146 | if (!dev) 1147 | return; 1148 | 1149 | /* Cause read_thread() to stop. */ 1150 | dev->shutdown_thread = 1; 1151 | libusb_cancel_transfer(dev->transfer); 1152 | 1153 | /* Wait for read_thread() to end. */ 1154 | pthread_join(dev->thread, NULL); 1155 | 1156 | /* Clean up the Transfer objects allocated in read_thread(). */ 1157 | free(dev->transfer->buffer); 1158 | libusb_free_transfer(dev->transfer); 1159 | 1160 | /* release the interface */ 1161 | libusb_release_interface(dev->device_handle, dev->interface); 1162 | 1163 | /* Close the handle */ 1164 | libusb_close(dev->device_handle); 1165 | 1166 | /* Clear out the queue of received reports. */ 1167 | pthread_mutex_lock(&dev->mutex); 1168 | while (dev->input_reports) { 1169 | return_data(dev, NULL, 0); 1170 | } 1171 | pthread_mutex_unlock(&dev->mutex); 1172 | 1173 | free_hid_device(dev); 1174 | } 1175 | 1176 | 1177 | int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) 1178 | { 1179 | return hid_get_indexed_string(dev, dev->manufacturer_index, string, maxlen); 1180 | } 1181 | 1182 | int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) 1183 | { 1184 | return hid_get_indexed_string(dev, dev->product_index, string, maxlen); 1185 | } 1186 | 1187 | int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) 1188 | { 1189 | return hid_get_indexed_string(dev, dev->serial_index, string, maxlen); 1190 | } 1191 | 1192 | int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) 1193 | { 1194 | wchar_t *str; 1195 | 1196 | str = get_usb_string(dev->device_handle, string_index); 1197 | if (str) { 1198 | wcsncpy(string, str, maxlen); 1199 | string[maxlen-1] = L'\0'; 1200 | free(str); 1201 | return 0; 1202 | } 1203 | else 1204 | return -1; 1205 | } 1206 | 1207 | 1208 | HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) 1209 | { 1210 | return NULL; 1211 | } 1212 | 1213 | 1214 | struct lang_map_entry { 1215 | const char *name; 1216 | const char *string_code; 1217 | uint16_t usb_code; 1218 | }; 1219 | 1220 | #define LANG(name,code,usb_code) { name, code, usb_code } 1221 | static struct lang_map_entry lang_map[] = { 1222 | LANG("Afrikaans", "af", 0x0436), 1223 | LANG("Albanian", "sq", 0x041C), 1224 | LANG("Arabic - United Arab Emirates", "ar_ae", 0x3801), 1225 | LANG("Arabic - Bahrain", "ar_bh", 0x3C01), 1226 | LANG("Arabic - Algeria", "ar_dz", 0x1401), 1227 | LANG("Arabic - Egypt", "ar_eg", 0x0C01), 1228 | LANG("Arabic - Iraq", "ar_iq", 0x0801), 1229 | LANG("Arabic - Jordan", "ar_jo", 0x2C01), 1230 | LANG("Arabic - Kuwait", "ar_kw", 0x3401), 1231 | LANG("Arabic - Lebanon", "ar_lb", 0x3001), 1232 | LANG("Arabic - Libya", "ar_ly", 0x1001), 1233 | LANG("Arabic - Morocco", "ar_ma", 0x1801), 1234 | LANG("Arabic - Oman", "ar_om", 0x2001), 1235 | LANG("Arabic - Qatar", "ar_qa", 0x4001), 1236 | LANG("Arabic - Saudi Arabia", "ar_sa", 0x0401), 1237 | LANG("Arabic - Syria", "ar_sy", 0x2801), 1238 | LANG("Arabic - Tunisia", "ar_tn", 0x1C01), 1239 | LANG("Arabic - Yemen", "ar_ye", 0x2401), 1240 | LANG("Armenian", "hy", 0x042B), 1241 | LANG("Azeri - Latin", "az_az", 0x042C), 1242 | LANG("Azeri - Cyrillic", "az_az", 0x082C), 1243 | LANG("Basque", "eu", 0x042D), 1244 | LANG("Belarusian", "be", 0x0423), 1245 | LANG("Bulgarian", "bg", 0x0402), 1246 | LANG("Catalan", "ca", 0x0403), 1247 | LANG("Chinese - China", "zh_cn", 0x0804), 1248 | LANG("Chinese - Hong Kong SAR", "zh_hk", 0x0C04), 1249 | LANG("Chinese - Macau SAR", "zh_mo", 0x1404), 1250 | LANG("Chinese - Singapore", "zh_sg", 0x1004), 1251 | LANG("Chinese - Taiwan", "zh_tw", 0x0404), 1252 | LANG("Croatian", "hr", 0x041A), 1253 | LANG("Czech", "cs", 0x0405), 1254 | LANG("Danish", "da", 0x0406), 1255 | LANG("Dutch - Netherlands", "nl_nl", 0x0413), 1256 | LANG("Dutch - Belgium", "nl_be", 0x0813), 1257 | LANG("English - Australia", "en_au", 0x0C09), 1258 | LANG("English - Belize", "en_bz", 0x2809), 1259 | LANG("English - Canada", "en_ca", 0x1009), 1260 | LANG("English - Caribbean", "en_cb", 0x2409), 1261 | LANG("English - Ireland", "en_ie", 0x1809), 1262 | LANG("English - Jamaica", "en_jm", 0x2009), 1263 | LANG("English - New Zealand", "en_nz", 0x1409), 1264 | LANG("English - Phillippines", "en_ph", 0x3409), 1265 | LANG("English - Southern Africa", "en_za", 0x1C09), 1266 | LANG("English - Trinidad", "en_tt", 0x2C09), 1267 | LANG("English - Great Britain", "en_gb", 0x0809), 1268 | LANG("English - United States", "en_us", 0x0409), 1269 | LANG("Estonian", "et", 0x0425), 1270 | LANG("Farsi", "fa", 0x0429), 1271 | LANG("Finnish", "fi", 0x040B), 1272 | LANG("Faroese", "fo", 0x0438), 1273 | LANG("French - France", "fr_fr", 0x040C), 1274 | LANG("French - Belgium", "fr_be", 0x080C), 1275 | LANG("French - Canada", "fr_ca", 0x0C0C), 1276 | LANG("French - Luxembourg", "fr_lu", 0x140C), 1277 | LANG("French - Switzerland", "fr_ch", 0x100C), 1278 | LANG("Gaelic - Ireland", "gd_ie", 0x083C), 1279 | LANG("Gaelic - Scotland", "gd", 0x043C), 1280 | LANG("German - Germany", "de_de", 0x0407), 1281 | LANG("German - Austria", "de_at", 0x0C07), 1282 | LANG("German - Liechtenstein", "de_li", 0x1407), 1283 | LANG("German - Luxembourg", "de_lu", 0x1007), 1284 | LANG("German - Switzerland", "de_ch", 0x0807), 1285 | LANG("Greek", "el", 0x0408), 1286 | LANG("Hebrew", "he", 0x040D), 1287 | LANG("Hindi", "hi", 0x0439), 1288 | LANG("Hungarian", "hu", 0x040E), 1289 | LANG("Icelandic", "is", 0x040F), 1290 | LANG("Indonesian", "id", 0x0421), 1291 | LANG("Italian - Italy", "it_it", 0x0410), 1292 | LANG("Italian - Switzerland", "it_ch", 0x0810), 1293 | LANG("Japanese", "ja", 0x0411), 1294 | LANG("Korean", "ko", 0x0412), 1295 | LANG("Latvian", "lv", 0x0426), 1296 | LANG("Lithuanian", "lt", 0x0427), 1297 | LANG("F.Y.R.O. Macedonia", "mk", 0x042F), 1298 | LANG("Malay - Malaysia", "ms_my", 0x043E), 1299 | LANG("Malay – Brunei", "ms_bn", 0x083E), 1300 | LANG("Maltese", "mt", 0x043A), 1301 | LANG("Marathi", "mr", 0x044E), 1302 | LANG("Norwegian - Bokml", "no_no", 0x0414), 1303 | LANG("Norwegian - Nynorsk", "no_no", 0x0814), 1304 | LANG("Polish", "pl", 0x0415), 1305 | LANG("Portuguese - Portugal", "pt_pt", 0x0816), 1306 | LANG("Portuguese - Brazil", "pt_br", 0x0416), 1307 | LANG("Raeto-Romance", "rm", 0x0417), 1308 | LANG("Romanian - Romania", "ro", 0x0418), 1309 | LANG("Romanian - Republic of Moldova", "ro_mo", 0x0818), 1310 | LANG("Russian", "ru", 0x0419), 1311 | LANG("Russian - Republic of Moldova", "ru_mo", 0x0819), 1312 | LANG("Sanskrit", "sa", 0x044F), 1313 | LANG("Serbian - Cyrillic", "sr_sp", 0x0C1A), 1314 | LANG("Serbian - Latin", "sr_sp", 0x081A), 1315 | LANG("Setsuana", "tn", 0x0432), 1316 | LANG("Slovenian", "sl", 0x0424), 1317 | LANG("Slovak", "sk", 0x041B), 1318 | LANG("Sorbian", "sb", 0x042E), 1319 | LANG("Spanish - Spain (Traditional)", "es_es", 0x040A), 1320 | LANG("Spanish - Argentina", "es_ar", 0x2C0A), 1321 | LANG("Spanish - Bolivia", "es_bo", 0x400A), 1322 | LANG("Spanish - Chile", "es_cl", 0x340A), 1323 | LANG("Spanish - Colombia", "es_co", 0x240A), 1324 | LANG("Spanish - Costa Rica", "es_cr", 0x140A), 1325 | LANG("Spanish - Dominican Republic", "es_do", 0x1C0A), 1326 | LANG("Spanish - Ecuador", "es_ec", 0x300A), 1327 | LANG("Spanish - Guatemala", "es_gt", 0x100A), 1328 | LANG("Spanish - Honduras", "es_hn", 0x480A), 1329 | LANG("Spanish - Mexico", "es_mx", 0x080A), 1330 | LANG("Spanish - Nicaragua", "es_ni", 0x4C0A), 1331 | LANG("Spanish - Panama", "es_pa", 0x180A), 1332 | LANG("Spanish - Peru", "es_pe", 0x280A), 1333 | LANG("Spanish - Puerto Rico", "es_pr", 0x500A), 1334 | LANG("Spanish - Paraguay", "es_py", 0x3C0A), 1335 | LANG("Spanish - El Salvador", "es_sv", 0x440A), 1336 | LANG("Spanish - Uruguay", "es_uy", 0x380A), 1337 | LANG("Spanish - Venezuela", "es_ve", 0x200A), 1338 | LANG("Southern Sotho", "st", 0x0430), 1339 | LANG("Swahili", "sw", 0x0441), 1340 | LANG("Swedish - Sweden", "sv_se", 0x041D), 1341 | LANG("Swedish - Finland", "sv_fi", 0x081D), 1342 | LANG("Tamil", "ta", 0x0449), 1343 | LANG("Tatar", "tt", 0X0444), 1344 | LANG("Thai", "th", 0x041E), 1345 | LANG("Turkish", "tr", 0x041F), 1346 | LANG("Tsonga", "ts", 0x0431), 1347 | LANG("Ukrainian", "uk", 0x0422), 1348 | LANG("Urdu", "ur", 0x0420), 1349 | LANG("Uzbek - Cyrillic", "uz_uz", 0x0843), 1350 | LANG("Uzbek – Latin", "uz_uz", 0x0443), 1351 | LANG("Vietnamese", "vi", 0x042A), 1352 | LANG("Xhosa", "xh", 0x0434), 1353 | LANG("Yiddish", "yi", 0x043D), 1354 | LANG("Zulu", "zu", 0x0435), 1355 | LANG(NULL, NULL, 0x0), 1356 | }; 1357 | 1358 | uint16_t get_usb_code_for_current_locale(void) 1359 | { 1360 | char *locale; 1361 | char search_string[64]; 1362 | char *ptr; 1363 | struct lang_map_entry *lang; 1364 | 1365 | /* Get the current locale. */ 1366 | locale = setlocale(0, NULL); 1367 | if (!locale) 1368 | return 0x0; 1369 | 1370 | /* Make a copy of the current locale string. */ 1371 | strncpy(search_string, locale, sizeof(search_string)); 1372 | search_string[sizeof(search_string)-1] = '\0'; 1373 | 1374 | /* Chop off the encoding part, and make it lower case. */ 1375 | ptr = search_string; 1376 | while (*ptr) { 1377 | *ptr = tolower(*ptr); 1378 | if (*ptr == '.') { 1379 | *ptr = '\0'; 1380 | break; 1381 | } 1382 | ptr++; 1383 | } 1384 | 1385 | /* Find the entry which matches the string code of our locale. */ 1386 | lang = lang_map; 1387 | while (lang->string_code) { 1388 | if (!strcmp(lang->string_code, search_string)) { 1389 | return lang->usb_code; 1390 | } 1391 | lang++; 1392 | } 1393 | 1394 | /* There was no match. Find with just the language only. */ 1395 | /* Chop off the variant. Chop it off at the '_'. */ 1396 | ptr = search_string; 1397 | while (*ptr) { 1398 | *ptr = tolower(*ptr); 1399 | if (*ptr == '_') { 1400 | *ptr = '\0'; 1401 | break; 1402 | } 1403 | ptr++; 1404 | } 1405 | 1406 | #if 0 /* TODO: Do we need this? */ 1407 | /* Find the entry which matches the string code of our language. */ 1408 | lang = lang_map; 1409 | while (lang->string_code) { 1410 | if (!strcmp(lang->string_code, search_string)) { 1411 | return lang->usb_code; 1412 | } 1413 | lang++; 1414 | } 1415 | #endif 1416 | 1417 | /* Found nothing. */ 1418 | return 0x0; 1419 | } 1420 | 1421 | #ifdef __cplusplus 1422 | } 1423 | #endif -------------------------------------------------------------------------------- /ext/blink1/hid.c.mac: -------------------------------------------------------------------------------- 1 | /******************************************************* 2 | HIDAPI - Multi-Platform library for 3 | communication with HID devices. 4 | 5 | Alan Ott 6 | Signal 11 Software 7 | 8 | 2010-07-03 9 | 10 | Copyright 2010, 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 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 | /* See Apple Technical Note TN2187 for details on IOHidManager. */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "hidapi.h" 35 | 36 | /* Barrier implementation because Mac OSX doesn't have pthread_barrier. 37 | It also doesn't have clock_gettime(). So much for POSIX and SUSv2. 38 | This implementation came from Brent Priddy and was posted on 39 | StackOverflow. It is used with his permission. */ 40 | typedef int pthread_barrierattr_t; 41 | typedef struct pthread_barrier { 42 | pthread_mutex_t mutex; 43 | pthread_cond_t cond; 44 | int count; 45 | int trip_count; 46 | } pthread_barrier_t; 47 | 48 | static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) 49 | { 50 | if(count == 0) { 51 | errno = EINVAL; 52 | return -1; 53 | } 54 | 55 | if(pthread_mutex_init(&barrier->mutex, 0) < 0) { 56 | return -1; 57 | } 58 | if(pthread_cond_init(&barrier->cond, 0) < 0) { 59 | pthread_mutex_destroy(&barrier->mutex); 60 | return -1; 61 | } 62 | barrier->trip_count = count; 63 | barrier->count = 0; 64 | 65 | return 0; 66 | } 67 | 68 | static int pthread_barrier_destroy(pthread_barrier_t *barrier) 69 | { 70 | pthread_cond_destroy(&barrier->cond); 71 | pthread_mutex_destroy(&barrier->mutex); 72 | return 0; 73 | } 74 | 75 | static int pthread_barrier_wait(pthread_barrier_t *barrier) 76 | { 77 | pthread_mutex_lock(&barrier->mutex); 78 | ++(barrier->count); 79 | if(barrier->count >= barrier->trip_count) 80 | { 81 | barrier->count = 0; 82 | pthread_cond_broadcast(&barrier->cond); 83 | pthread_mutex_unlock(&barrier->mutex); 84 | return 1; 85 | } 86 | else 87 | { 88 | pthread_cond_wait(&barrier->cond, &(barrier->mutex)); 89 | pthread_mutex_unlock(&barrier->mutex); 90 | return 0; 91 | } 92 | } 93 | 94 | static int return_data(hid_device *dev, unsigned char *data, size_t length); 95 | 96 | /* Linked List of input reports received from the device. */ 97 | struct input_report { 98 | uint8_t *data; 99 | size_t len; 100 | struct input_report *next; 101 | }; 102 | 103 | struct hid_device_ { 104 | IOHIDDeviceRef device_handle; 105 | int blocking; 106 | int uses_numbered_reports; 107 | int disconnected; 108 | CFStringRef run_loop_mode; 109 | CFRunLoopRef run_loop; 110 | CFRunLoopSourceRef source; 111 | uint8_t *input_report_buf; 112 | CFIndex max_input_report_len; 113 | struct input_report *input_reports; 114 | 115 | pthread_t thread; 116 | pthread_mutex_t mutex; /* Protects input_reports */ 117 | pthread_cond_t condition; 118 | pthread_barrier_t barrier; /* Ensures correct startup sequence */ 119 | pthread_barrier_t shutdown_barrier; /* Ensures correct shutdown sequence */ 120 | int shutdown_thread; 121 | }; 122 | 123 | static hid_device *new_hid_device(void) 124 | { 125 | hid_device *dev = calloc(1, sizeof(hid_device)); 126 | dev->device_handle = NULL; 127 | dev->blocking = 1; 128 | dev->uses_numbered_reports = 0; 129 | dev->disconnected = 0; 130 | dev->run_loop_mode = NULL; 131 | dev->run_loop = NULL; 132 | dev->source = NULL; 133 | dev->input_report_buf = NULL; 134 | dev->input_reports = NULL; 135 | dev->shutdown_thread = 0; 136 | 137 | /* Thread objects */ 138 | pthread_mutex_init(&dev->mutex, NULL); 139 | pthread_cond_init(&dev->condition, NULL); 140 | pthread_barrier_init(&dev->barrier, NULL, 2); 141 | pthread_barrier_init(&dev->shutdown_barrier, NULL, 2); 142 | 143 | return dev; 144 | } 145 | 146 | static void free_hid_device(hid_device *dev) 147 | { 148 | if (!dev) 149 | return; 150 | 151 | /* Delete any input reports still left over. */ 152 | struct input_report *rpt = dev->input_reports; 153 | while (rpt) { 154 | struct input_report *next = rpt->next; 155 | free(rpt->data); 156 | free(rpt); 157 | rpt = next; 158 | } 159 | 160 | /* Free the string and the report buffer. The check for NULL 161 | is necessary here as CFRelease() doesn't handle NULL like 162 | free() and others do. */ 163 | if (dev->run_loop_mode) 164 | CFRelease(dev->run_loop_mode); 165 | if (dev->source) 166 | CFRelease(dev->source); 167 | free(dev->input_report_buf); 168 | 169 | /* Clean up the thread objects */ 170 | pthread_barrier_destroy(&dev->shutdown_barrier); 171 | pthread_barrier_destroy(&dev->barrier); 172 | pthread_cond_destroy(&dev->condition); 173 | pthread_mutex_destroy(&dev->mutex); 174 | 175 | /* Free the structure itself. */ 176 | free(dev); 177 | } 178 | 179 | static IOHIDManagerRef hid_mgr = 0x0; 180 | 181 | 182 | #if 0 183 | static void register_error(hid_device *device, const char *op) 184 | { 185 | 186 | } 187 | #endif 188 | 189 | 190 | static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key) 191 | { 192 | CFTypeRef ref; 193 | int32_t value; 194 | 195 | ref = IOHIDDeviceGetProperty(device, key); 196 | if (ref) { 197 | if (CFGetTypeID(ref) == CFNumberGetTypeID()) { 198 | CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &value); 199 | return value; 200 | } 201 | } 202 | return 0; 203 | } 204 | 205 | static unsigned short get_vendor_id(IOHIDDeviceRef device) 206 | { 207 | return get_int_property(device, CFSTR(kIOHIDVendorIDKey)); 208 | } 209 | 210 | static unsigned short get_product_id(IOHIDDeviceRef device) 211 | { 212 | return get_int_property(device, CFSTR(kIOHIDProductIDKey)); 213 | } 214 | 215 | static int32_t get_location_id(IOHIDDeviceRef device) 216 | { 217 | return get_int_property(device, CFSTR(kIOHIDLocationIDKey)); 218 | } 219 | 220 | static int32_t get_max_report_length(IOHIDDeviceRef device) 221 | { 222 | return get_int_property(device, CFSTR(kIOHIDMaxInputReportSizeKey)); 223 | } 224 | 225 | static int get_string_property(IOHIDDeviceRef device, CFStringRef prop, wchar_t *buf, size_t len) 226 | { 227 | CFStringRef str; 228 | 229 | if (!len) 230 | return 0; 231 | 232 | str = IOHIDDeviceGetProperty(device, prop); 233 | 234 | buf[0] = 0; 235 | 236 | if (str) { 237 | CFIndex str_len = CFStringGetLength(str); 238 | CFRange range; 239 | CFIndex used_buf_len; 240 | CFIndex chars_copied; 241 | 242 | len --; 243 | 244 | range.location = 0; 245 | range.length = ((size_t)str_len > len)? len: (size_t)str_len; 246 | chars_copied = CFStringGetBytes(str, 247 | range, 248 | kCFStringEncodingUTF32LE, 249 | (char)'?', 250 | FALSE, 251 | (UInt8*)buf, 252 | len * sizeof(wchar_t), 253 | &used_buf_len); 254 | 255 | if (chars_copied == len) 256 | buf[len] = 0; /* len is decremented above */ 257 | else 258 | buf[chars_copied] = 0; 259 | 260 | return 0; 261 | } 262 | else 263 | return -1; 264 | 265 | } 266 | 267 | static int get_string_property_utf8(IOHIDDeviceRef device, CFStringRef prop, char *buf, size_t len) 268 | { 269 | CFStringRef str; 270 | if (!len) 271 | return 0; 272 | 273 | str = IOHIDDeviceGetProperty(device, prop); 274 | 275 | buf[0] = 0; 276 | 277 | if (str) { 278 | len--; 279 | 280 | CFIndex str_len = CFStringGetLength(str); 281 | CFRange range; 282 | range.location = 0; 283 | range.length = str_len; 284 | CFIndex used_buf_len; 285 | CFIndex chars_copied; 286 | chars_copied = CFStringGetBytes(str, 287 | range, 288 | kCFStringEncodingUTF8, 289 | (char)'?', 290 | FALSE, 291 | (UInt8*)buf, 292 | len, 293 | &used_buf_len); 294 | 295 | if (used_buf_len == len) 296 | buf[len] = 0; /* len is decremented above */ 297 | else 298 | buf[used_buf_len] = 0; 299 | 300 | return used_buf_len; 301 | } 302 | else 303 | return 0; 304 | } 305 | 306 | 307 | static int get_serial_number(IOHIDDeviceRef device, wchar_t *buf, size_t len) 308 | { 309 | return get_string_property(device, CFSTR(kIOHIDSerialNumberKey), buf, len); 310 | } 311 | 312 | static int get_manufacturer_string(IOHIDDeviceRef device, wchar_t *buf, size_t len) 313 | { 314 | return get_string_property(device, CFSTR(kIOHIDManufacturerKey), buf, len); 315 | } 316 | 317 | static int get_product_string(IOHIDDeviceRef device, wchar_t *buf, size_t len) 318 | { 319 | return get_string_property(device, CFSTR(kIOHIDProductKey), buf, len); 320 | } 321 | 322 | 323 | /* Implementation of wcsdup() for Mac. */ 324 | static wchar_t *dup_wcs(const wchar_t *s) 325 | { 326 | size_t len = wcslen(s); 327 | wchar_t *ret = malloc((len+1)*sizeof(wchar_t)); 328 | wcscpy(ret, s); 329 | 330 | return ret; 331 | } 332 | 333 | 334 | static int make_path(IOHIDDeviceRef device, char *buf, size_t len) 335 | { 336 | int res; 337 | unsigned short vid, pid; 338 | char transport[32]; 339 | int32_t location; 340 | 341 | buf[0] = '\0'; 342 | 343 | res = get_string_property_utf8( 344 | device, CFSTR(kIOHIDTransportKey), 345 | transport, sizeof(transport)); 346 | 347 | if (!res) 348 | return -1; 349 | 350 | location = get_location_id(device); 351 | vid = get_vendor_id(device); 352 | pid = get_product_id(device); 353 | 354 | res = snprintf(buf, len, "%s_%04hx_%04hx_%x", 355 | transport, vid, pid, location); 356 | 357 | 358 | buf[len-1] = '\0'; 359 | return res+1; 360 | } 361 | 362 | /* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */ 363 | static int init_hid_manager(void) 364 | { 365 | /* Initialize all the HID Manager Objects */ 366 | hid_mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); 367 | if (hid_mgr) { 368 | IOHIDManagerSetDeviceMatching(hid_mgr, NULL); 369 | IOHIDManagerScheduleWithRunLoop(hid_mgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); 370 | return 0; 371 | } 372 | 373 | return -1; 374 | } 375 | 376 | /* Initialize the IOHIDManager if necessary. This is the public function, and 377 | it is safe to call this function repeatedly. Return 0 for success and -1 378 | for failure. */ 379 | int HID_API_EXPORT hid_init(void) 380 | { 381 | if (!hid_mgr) { 382 | return init_hid_manager(); 383 | } 384 | 385 | /* Already initialized. */ 386 | return 0; 387 | } 388 | 389 | int HID_API_EXPORT hid_exit(void) 390 | { 391 | if (hid_mgr) { 392 | /* Close the HID manager. */ 393 | IOHIDManagerClose(hid_mgr, kIOHIDOptionsTypeNone); 394 | CFRelease(hid_mgr); 395 | hid_mgr = NULL; 396 | } 397 | 398 | return 0; 399 | } 400 | 401 | static void process_pending_events(void) { 402 | SInt32 res; 403 | do { 404 | res = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, FALSE); 405 | } while(res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut); 406 | } 407 | 408 | struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) 409 | { 410 | struct hid_device_info *root = NULL; /* return object */ 411 | struct hid_device_info *cur_dev = NULL; 412 | CFIndex num_devices; 413 | int i; 414 | 415 | /* Set up the HID Manager if it hasn't been done */ 416 | if (hid_init() < 0) 417 | return NULL; 418 | 419 | /* give the IOHIDManager a chance to update itself */ 420 | process_pending_events(); 421 | 422 | /* Get a list of the Devices */ 423 | CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr); 424 | 425 | /* Convert the list into a C array so we can iterate easily. */ 426 | num_devices = CFSetGetCount(device_set); 427 | IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); 428 | CFSetGetValues(device_set, (const void **) device_array); 429 | 430 | /* Iterate over each device, making an entry for it. */ 431 | for (i = 0; i < num_devices; i++) { 432 | unsigned short dev_vid; 433 | unsigned short dev_pid; 434 | #define BUF_LEN 256 435 | wchar_t buf[BUF_LEN]; 436 | char cbuf[BUF_LEN]; 437 | 438 | IOHIDDeviceRef dev = device_array[i]; 439 | 440 | if (!dev) { 441 | continue; 442 | } 443 | dev_vid = get_vendor_id(dev); 444 | dev_pid = get_product_id(dev); 445 | 446 | /* Check the VID/PID against the arguments */ 447 | if ((vendor_id == 0x0 || vendor_id == dev_vid) && 448 | (product_id == 0x0 || product_id == dev_pid)) { 449 | struct hid_device_info *tmp; 450 | size_t len; 451 | 452 | /* VID/PID match. Create the record. */ 453 | tmp = malloc(sizeof(struct hid_device_info)); 454 | if (cur_dev) { 455 | cur_dev->next = tmp; 456 | } 457 | else { 458 | root = tmp; 459 | } 460 | cur_dev = tmp; 461 | 462 | /* Get the Usage Page and Usage for this device. */ 463 | cur_dev->usage_page = get_int_property(dev, CFSTR(kIOHIDPrimaryUsagePageKey)); 464 | cur_dev->usage = get_int_property(dev, CFSTR(kIOHIDPrimaryUsageKey)); 465 | 466 | /* Fill out the record */ 467 | cur_dev->next = NULL; 468 | len = make_path(dev, cbuf, sizeof(cbuf)); 469 | cur_dev->path = strdup(cbuf); 470 | 471 | /* Serial Number */ 472 | get_serial_number(dev, buf, BUF_LEN); 473 | cur_dev->serial_number = dup_wcs(buf); 474 | 475 | /* Manufacturer and Product strings */ 476 | get_manufacturer_string(dev, buf, BUF_LEN); 477 | cur_dev->manufacturer_string = dup_wcs(buf); 478 | get_product_string(dev, buf, BUF_LEN); 479 | cur_dev->product_string = dup_wcs(buf); 480 | 481 | /* VID/PID */ 482 | cur_dev->vendor_id = dev_vid; 483 | cur_dev->product_id = dev_pid; 484 | 485 | /* Release Number */ 486 | cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey)); 487 | 488 | /* Interface Number (Unsupported on Mac)*/ 489 | cur_dev->interface_number = -1; 490 | } 491 | } 492 | 493 | free(device_array); 494 | CFRelease(device_set); 495 | 496 | return root; 497 | } 498 | 499 | void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) 500 | { 501 | /* This function is identical to the Linux version. Platform independent. */ 502 | struct hid_device_info *d = devs; 503 | while (d) { 504 | struct hid_device_info *next = d->next; 505 | free(d->path); 506 | free(d->serial_number); 507 | free(d->manufacturer_string); 508 | free(d->product_string); 509 | free(d); 510 | d = next; 511 | } 512 | } 513 | 514 | hid_device * HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) 515 | { 516 | /* This function is identical to the Linux version. Platform independent. */ 517 | struct hid_device_info *devs, *cur_dev; 518 | const char *path_to_open = NULL; 519 | hid_device * handle = NULL; 520 | 521 | devs = hid_enumerate(vendor_id, product_id); 522 | cur_dev = devs; 523 | while (cur_dev) { 524 | if (cur_dev->vendor_id == vendor_id && 525 | cur_dev->product_id == product_id) { 526 | if (serial_number) { 527 | if (wcscmp(serial_number, cur_dev->serial_number) == 0) { 528 | path_to_open = cur_dev->path; 529 | break; 530 | } 531 | } 532 | else { 533 | path_to_open = cur_dev->path; 534 | break; 535 | } 536 | } 537 | cur_dev = cur_dev->next; 538 | } 539 | 540 | if (path_to_open) { 541 | /* Open the device */ 542 | handle = hid_open_path(path_to_open); 543 | } 544 | 545 | hid_free_enumeration(devs); 546 | 547 | return handle; 548 | } 549 | 550 | static void hid_device_removal_callback(void *context, IOReturn result, 551 | void *sender) 552 | { 553 | /* Stop the Run Loop for this device. */ 554 | hid_device *d = context; 555 | 556 | d->disconnected = 1; 557 | CFRunLoopStop(d->run_loop); 558 | } 559 | 560 | /* The Run Loop calls this function for each input report received. 561 | This function puts the data into a linked list to be picked up by 562 | hid_read(). */ 563 | static void hid_report_callback(void *context, IOReturn result, void *sender, 564 | IOHIDReportType report_type, uint32_t report_id, 565 | uint8_t *report, CFIndex report_length) 566 | { 567 | struct input_report *rpt; 568 | hid_device *dev = context; 569 | 570 | /* Make a new Input Report object */ 571 | rpt = calloc(1, sizeof(struct input_report)); 572 | rpt->data = calloc(1, report_length); 573 | memcpy(rpt->data, report, report_length); 574 | rpt->len = report_length; 575 | rpt->next = NULL; 576 | 577 | /* Lock this section */ 578 | pthread_mutex_lock(&dev->mutex); 579 | 580 | /* Attach the new report object to the end of the list. */ 581 | if (dev->input_reports == NULL) { 582 | /* The list is empty. Put it at the root. */ 583 | dev->input_reports = rpt; 584 | } 585 | else { 586 | /* Find the end of the list and attach. */ 587 | struct input_report *cur = dev->input_reports; 588 | int num_queued = 0; 589 | while (cur->next != NULL) { 590 | cur = cur->next; 591 | num_queued++; 592 | } 593 | cur->next = rpt; 594 | 595 | /* Pop one off if we've reached 30 in the queue. This 596 | way we don't grow forever if the user never reads 597 | anything from the device. */ 598 | if (num_queued > 30) { 599 | return_data(dev, NULL, 0); 600 | } 601 | } 602 | 603 | /* Signal a waiting thread that there is data. */ 604 | pthread_cond_signal(&dev->condition); 605 | 606 | /* Unlock */ 607 | pthread_mutex_unlock(&dev->mutex); 608 | 609 | } 610 | 611 | /* This gets called when the read_thred's run loop gets signaled by 612 | hid_close(), and serves to stop the read_thread's run loop. */ 613 | static void perform_signal_callback(void *context) 614 | { 615 | hid_device *dev = context; 616 | CFRunLoopStop(dev->run_loop); /*TODO: CFRunLoopGetCurrent()*/ 617 | } 618 | 619 | static void *read_thread(void *param) 620 | { 621 | hid_device *dev = param; 622 | SInt32 code; 623 | 624 | /* Move the device's run loop to this thread. */ 625 | IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetCurrent(), dev->run_loop_mode); 626 | 627 | /* Create the RunLoopSource which is used to signal the 628 | event loop to stop when hid_close() is called. */ 629 | CFRunLoopSourceContext ctx; 630 | memset(&ctx, 0, sizeof(ctx)); 631 | ctx.version = 0; 632 | ctx.info = dev; 633 | ctx.perform = &perform_signal_callback; 634 | dev->source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0/*order*/, &ctx); 635 | CFRunLoopAddSource(CFRunLoopGetCurrent(), dev->source, dev->run_loop_mode); 636 | 637 | /* Store off the Run Loop so it can be stopped from hid_close() 638 | and on device disconnection. */ 639 | dev->run_loop = CFRunLoopGetCurrent(); 640 | 641 | /* Notify the main thread that the read thread is up and running. */ 642 | pthread_barrier_wait(&dev->barrier); 643 | 644 | /* Run the Event Loop. CFRunLoopRunInMode() will dispatch HID input 645 | reports into the hid_report_callback(). */ 646 | while (!dev->shutdown_thread && !dev->disconnected) { 647 | code = CFRunLoopRunInMode(dev->run_loop_mode, 1000/*sec*/, FALSE); 648 | /* Return if the device has been disconnected */ 649 | if (code == kCFRunLoopRunFinished) { 650 | dev->disconnected = 1; 651 | break; 652 | } 653 | 654 | 655 | /* Break if The Run Loop returns Finished or Stopped. */ 656 | if (code != kCFRunLoopRunTimedOut && 657 | code != kCFRunLoopRunHandledSource) { 658 | /* There was some kind of error. Setting 659 | shutdown seems to make sense, but 660 | there may be something else more appropriate */ 661 | dev->shutdown_thread = 1; 662 | break; 663 | } 664 | } 665 | 666 | /* Now that the read thread is stopping, Wake any threads which are 667 | waiting on data (in hid_read_timeout()). Do this under a mutex to 668 | make sure that a thread which is about to go to sleep waiting on 669 | the condition acutally will go to sleep before the condition is 670 | signaled. */ 671 | pthread_mutex_lock(&dev->mutex); 672 | pthread_cond_broadcast(&dev->condition); 673 | pthread_mutex_unlock(&dev->mutex); 674 | 675 | /* Wait here until hid_close() is called and makes it past 676 | the call to CFRunLoopWakeUp(). This thread still needs to 677 | be valid when that function is called on the other thread. */ 678 | pthread_barrier_wait(&dev->shutdown_barrier); 679 | 680 | return NULL; 681 | } 682 | 683 | hid_device * HID_API_EXPORT hid_open_path(const char *path) 684 | { 685 | int i; 686 | hid_device *dev = NULL; 687 | CFIndex num_devices; 688 | 689 | dev = new_hid_device(); 690 | 691 | /* Set up the HID Manager if it hasn't been done */ 692 | if (hid_init() < 0) 693 | return NULL; 694 | 695 | /* give the IOHIDManager a chance to update itself */ 696 | process_pending_events(); 697 | 698 | CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr); 699 | 700 | num_devices = CFSetGetCount(device_set); 701 | IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); 702 | CFSetGetValues(device_set, (const void **) device_array); 703 | for (i = 0; i < num_devices; i++) { 704 | char cbuf[BUF_LEN]; 705 | size_t len; 706 | IOHIDDeviceRef os_dev = device_array[i]; 707 | 708 | len = make_path(os_dev, cbuf, sizeof(cbuf)); 709 | if (!strcmp(cbuf, path)) { 710 | /* Matched Paths. Open this Device. */ 711 | IOReturn ret = IOHIDDeviceOpen(os_dev, kIOHIDOptionsTypeSeizeDevice); 712 | if (ret == kIOReturnSuccess) { 713 | char str[32]; 714 | 715 | free(device_array); 716 | CFRetain(os_dev); 717 | CFRelease(device_set); 718 | dev->device_handle = os_dev; 719 | 720 | /* Create the buffers for receiving data */ 721 | dev->max_input_report_len = (CFIndex) get_max_report_length(os_dev); 722 | dev->input_report_buf = calloc(dev->max_input_report_len, sizeof(uint8_t)); 723 | 724 | /* Create the Run Loop Mode for this device. 725 | printing the reference seems to work. */ 726 | sprintf(str, "HIDAPI_%p", os_dev); 727 | dev->run_loop_mode = 728 | CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII); 729 | 730 | /* Attach the device to a Run Loop */ 731 | IOHIDDeviceRegisterInputReportCallback( 732 | os_dev, dev->input_report_buf, dev->max_input_report_len, 733 | &hid_report_callback, dev); 734 | IOHIDDeviceRegisterRemovalCallback(dev->device_handle, hid_device_removal_callback, dev); 735 | 736 | /* Start the read thread */ 737 | pthread_create(&dev->thread, NULL, read_thread, dev); 738 | 739 | /* Wait here for the read thread to be initialized. */ 740 | pthread_barrier_wait(&dev->barrier); 741 | 742 | return dev; 743 | } 744 | else { 745 | goto return_error; 746 | } 747 | } 748 | } 749 | 750 | return_error: 751 | free(device_array); 752 | CFRelease(device_set); 753 | free_hid_device(dev); 754 | return NULL; 755 | } 756 | 757 | static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char *data, size_t length) 758 | { 759 | const unsigned char *data_to_send; 760 | size_t length_to_send; 761 | IOReturn res; 762 | 763 | /* Return if the device has been disconnected. */ 764 | if (dev->disconnected) 765 | return -1; 766 | 767 | if (data[0] == 0x0) { 768 | /* Not using numbered Reports. 769 | Don't send the report number. */ 770 | data_to_send = data+1; 771 | length_to_send = length-1; 772 | } 773 | else { 774 | /* Using numbered Reports. 775 | Send the Report Number */ 776 | data_to_send = data; 777 | length_to_send = length; 778 | } 779 | 780 | if (!dev->disconnected) { 781 | res = IOHIDDeviceSetReport(dev->device_handle, 782 | type, 783 | data[0], /* Report ID*/ 784 | data_to_send, length_to_send); 785 | 786 | if (res == kIOReturnSuccess) { 787 | return length; 788 | } 789 | else 790 | return -1; 791 | } 792 | 793 | return -1; 794 | } 795 | 796 | int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) 797 | { 798 | return set_report(dev, kIOHIDReportTypeOutput, data, length); 799 | } 800 | 801 | /* Helper function, so that this isn't duplicated in hid_read(). */ 802 | static int return_data(hid_device *dev, unsigned char *data, size_t length) 803 | { 804 | /* Copy the data out of the linked list item (rpt) into the 805 | return buffer (data), and delete the liked list item. */ 806 | struct input_report *rpt = dev->input_reports; 807 | size_t len = (length < rpt->len)? length: rpt->len; 808 | memcpy(data, rpt->data, len); 809 | dev->input_reports = rpt->next; 810 | free(rpt->data); 811 | free(rpt); 812 | return len; 813 | } 814 | 815 | static int cond_wait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex) 816 | { 817 | while (!dev->input_reports) { 818 | int res = pthread_cond_wait(cond, mutex); 819 | if (res != 0) 820 | return res; 821 | 822 | /* A res of 0 means we may have been signaled or it may 823 | be a spurious wakeup. Check to see that there's acutally 824 | data in the queue before returning, and if not, go back 825 | to sleep. See the pthread_cond_timedwait() man page for 826 | details. */ 827 | 828 | if (dev->shutdown_thread || dev->disconnected) 829 | return -1; 830 | } 831 | 832 | return 0; 833 | } 834 | 835 | static int cond_timedwait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) 836 | { 837 | while (!dev->input_reports) { 838 | int res = pthread_cond_timedwait(cond, mutex, abstime); 839 | if (res != 0) 840 | return res; 841 | 842 | /* A res of 0 means we may have been signaled or it may 843 | be a spurious wakeup. Check to see that there's acutally 844 | data in the queue before returning, and if not, go back 845 | to sleep. See the pthread_cond_timedwait() man page for 846 | details. */ 847 | 848 | if (dev->shutdown_thread || dev->disconnected) 849 | return -1; 850 | } 851 | 852 | return 0; 853 | 854 | } 855 | 856 | int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) 857 | { 858 | int bytes_read = -1; 859 | 860 | /* Lock the access to the report list. */ 861 | pthread_mutex_lock(&dev->mutex); 862 | 863 | /* There's an input report queued up. Return it. */ 864 | if (dev->input_reports) { 865 | /* Return the first one */ 866 | bytes_read = return_data(dev, data, length); 867 | goto ret; 868 | } 869 | 870 | /* Return if the device has been disconnected. */ 871 | if (dev->disconnected) { 872 | bytes_read = -1; 873 | goto ret; 874 | } 875 | 876 | if (dev->shutdown_thread) { 877 | /* This means the device has been closed (or there 878 | has been an error. An error code of -1 should 879 | be returned. */ 880 | bytes_read = -1; 881 | goto ret; 882 | } 883 | 884 | /* There is no data. Go to sleep and wait for data. */ 885 | 886 | if (milliseconds == -1) { 887 | /* Blocking */ 888 | int res; 889 | res = cond_wait(dev, &dev->condition, &dev->mutex); 890 | if (res == 0) 891 | bytes_read = return_data(dev, data, length); 892 | else { 893 | /* There was an error, or a device disconnection. */ 894 | bytes_read = -1; 895 | } 896 | } 897 | else if (milliseconds > 0) { 898 | /* Non-blocking, but called with timeout. */ 899 | int res; 900 | struct timespec ts; 901 | struct timeval tv; 902 | gettimeofday(&tv, NULL); 903 | TIMEVAL_TO_TIMESPEC(&tv, &ts); 904 | ts.tv_sec += milliseconds / 1000; 905 | ts.tv_nsec += (milliseconds % 1000) * 1000000; 906 | if (ts.tv_nsec >= 1000000000L) { 907 | ts.tv_sec++; 908 | ts.tv_nsec -= 1000000000L; 909 | } 910 | 911 | res = cond_timedwait(dev, &dev->condition, &dev->mutex, &ts); 912 | if (res == 0) 913 | bytes_read = return_data(dev, data, length); 914 | else if (res == ETIMEDOUT) 915 | bytes_read = 0; 916 | else 917 | bytes_read = -1; 918 | } 919 | else { 920 | /* Purely non-blocking */ 921 | bytes_read = 0; 922 | } 923 | 924 | ret: 925 | /* Unlock */ 926 | pthread_mutex_unlock(&dev->mutex); 927 | return bytes_read; 928 | } 929 | 930 | int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) 931 | { 932 | return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); 933 | } 934 | 935 | int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) 936 | { 937 | /* All Nonblocking operation is handled by the library. */ 938 | dev->blocking = !nonblock; 939 | 940 | return 0; 941 | } 942 | 943 | int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) 944 | { 945 | return set_report(dev, kIOHIDReportTypeFeature, data, length); 946 | } 947 | 948 | int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) 949 | { 950 | CFIndex len = length; 951 | IOReturn res; 952 | 953 | /* Return if the device has been unplugged. */ 954 | if (dev->disconnected) 955 | return -1; 956 | 957 | res = IOHIDDeviceGetReport(dev->device_handle, 958 | kIOHIDReportTypeFeature, 959 | data[0], /* Report ID */ 960 | data, &len); 961 | if (res == kIOReturnSuccess) 962 | return len; 963 | else 964 | return -1; 965 | } 966 | 967 | 968 | void HID_API_EXPORT hid_close(hid_device *dev) 969 | { 970 | if (!dev) 971 | return; 972 | 973 | /* Disconnect the report callback before close. */ 974 | if (!dev->disconnected) { 975 | IOHIDDeviceRegisterInputReportCallback( 976 | dev->device_handle, dev->input_report_buf, dev->max_input_report_len, 977 | NULL, dev); 978 | IOHIDDeviceRegisterRemovalCallback(dev->device_handle, NULL, dev); 979 | IOHIDDeviceUnscheduleFromRunLoop(dev->device_handle, dev->run_loop, dev->run_loop_mode); 980 | IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetMain(), kCFRunLoopDefaultMode); 981 | } 982 | 983 | /* Cause read_thread() to stop. */ 984 | dev->shutdown_thread = 1; 985 | 986 | /* Wake up the run thread's event loop so that the thread can exit. */ 987 | CFRunLoopSourceSignal(dev->source); 988 | CFRunLoopWakeUp(dev->run_loop); 989 | 990 | /* Notify the read thread that it can shut down now. */ 991 | pthread_barrier_wait(&dev->shutdown_barrier); 992 | 993 | /* Wait for read_thread() to end. */ 994 | pthread_join(dev->thread, NULL); 995 | 996 | /* Close the OS handle to the device, but only if it's not 997 | been unplugged. If it's been unplugged, then calling 998 | IOHIDDeviceClose() will crash. */ 999 | if (!dev->disconnected) { 1000 | IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeSeizeDevice); 1001 | } 1002 | 1003 | /* Clear out the queue of received reports. */ 1004 | pthread_mutex_lock(&dev->mutex); 1005 | while (dev->input_reports) { 1006 | return_data(dev, NULL, 0); 1007 | } 1008 | pthread_mutex_unlock(&dev->mutex); 1009 | CFRelease(dev->device_handle); 1010 | 1011 | free_hid_device(dev); 1012 | } 1013 | 1014 | int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) 1015 | { 1016 | return get_manufacturer_string(dev->device_handle, string, maxlen); 1017 | } 1018 | 1019 | int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) 1020 | { 1021 | return get_product_string(dev->device_handle, string, maxlen); 1022 | } 1023 | 1024 | int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) 1025 | { 1026 | return get_serial_number(dev->device_handle, string, maxlen); 1027 | } 1028 | 1029 | int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) 1030 | { 1031 | /* TODO: */ 1032 | 1033 | return 0; 1034 | } 1035 | 1036 | 1037 | HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) 1038 | { 1039 | /* TODO: */ 1040 | 1041 | return NULL; 1042 | } 1043 | 1044 | 1045 | 1046 | 1047 | 1048 | 1049 | 1050 | #if 0 1051 | static int32_t get_usage(IOHIDDeviceRef device) 1052 | { 1053 | int32_t res; 1054 | res = get_int_property(device, CFSTR(kIOHIDDeviceUsageKey)); 1055 | if (!res) 1056 | res = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey)); 1057 | return res; 1058 | } 1059 | 1060 | static int32_t get_usage_page(IOHIDDeviceRef device) 1061 | { 1062 | int32_t res; 1063 | res = get_int_property(device, CFSTR(kIOHIDDeviceUsagePageKey)); 1064 | if (!res) 1065 | res = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey)); 1066 | return res; 1067 | } 1068 | 1069 | static int get_transport(IOHIDDeviceRef device, wchar_t *buf, size_t len) 1070 | { 1071 | return get_string_property(device, CFSTR(kIOHIDTransportKey), buf, len); 1072 | } 1073 | 1074 | 1075 | int main(void) 1076 | { 1077 | IOHIDManagerRef mgr; 1078 | int i; 1079 | 1080 | mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); 1081 | IOHIDManagerSetDeviceMatching(mgr, NULL); 1082 | IOHIDManagerOpen(mgr, kIOHIDOptionsTypeNone); 1083 | 1084 | CFSetRef device_set = IOHIDManagerCopyDevices(mgr); 1085 | 1086 | CFIndex num_devices = CFSetGetCount(device_set); 1087 | IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); 1088 | CFSetGetValues(device_set, (const void **) device_array); 1089 | 1090 | for (i = 0; i < num_devices; i++) { 1091 | IOHIDDeviceRef dev = device_array[i]; 1092 | printf("Device: %p\n", dev); 1093 | printf(" %04hx %04hx\n", get_vendor_id(dev), get_product_id(dev)); 1094 | 1095 | wchar_t serial[256], buf[256]; 1096 | char cbuf[256]; 1097 | get_serial_number(dev, serial, 256); 1098 | 1099 | 1100 | printf(" Serial: %ls\n", serial); 1101 | printf(" Loc: %ld\n", get_location_id(dev)); 1102 | get_transport(dev, buf, 256); 1103 | printf(" Trans: %ls\n", buf); 1104 | make_path(dev, cbuf, 256); 1105 | printf(" Path: %s\n", cbuf); 1106 | 1107 | } 1108 | 1109 | return 0; 1110 | } 1111 | #endif -------------------------------------------------------------------------------- /ext/blink1/hid.c.windows: -------------------------------------------------------------------------------- 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 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 | 110 | static HidD_GetAttributes_ HidD_GetAttributes; 111 | static HidD_GetSerialNumberString_ HidD_GetSerialNumberString; 112 | static HidD_GetManufacturerString_ HidD_GetManufacturerString; 113 | static HidD_GetProductString_ HidD_GetProductString; 114 | static HidD_SetFeature_ HidD_SetFeature; 115 | static HidD_GetFeature_ HidD_GetFeature; 116 | static HidD_GetIndexedString_ HidD_GetIndexedString; 117 | static HidD_GetPreparsedData_ HidD_GetPreparsedData; 118 | static HidD_FreePreparsedData_ HidD_FreePreparsedData; 119 | static HidP_GetCaps_ HidP_GetCaps; 120 | 121 | static HMODULE lib_handle = NULL; 122 | static BOOLEAN initialized = FALSE; 123 | #endif /* HIDAPI_USE_DDK */ 124 | 125 | struct hid_device_ { 126 | HANDLE device_handle; 127 | BOOL blocking; 128 | USHORT output_report_length; 129 | size_t input_report_length; 130 | void *last_error_str; 131 | DWORD last_error_num; 132 | BOOL read_pending; 133 | char *read_buf; 134 | OVERLAPPED ol; 135 | }; 136 | 137 | static hid_device *new_hid_device() 138 | { 139 | hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device)); 140 | dev->device_handle = INVALID_HANDLE_VALUE; 141 | dev->blocking = TRUE; 142 | dev->output_report_length = 0; 143 | dev->input_report_length = 0; 144 | dev->last_error_str = NULL; 145 | dev->last_error_num = 0; 146 | dev->read_pending = FALSE; 147 | dev->read_buf = NULL; 148 | memset(&dev->ol, 0, sizeof(dev->ol)); 149 | dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*inital state f=nonsignaled*/, NULL); 150 | 151 | return dev; 152 | } 153 | 154 | static void free_hid_device(hid_device *dev) 155 | { 156 | CloseHandle(dev->ol.hEvent); 157 | CloseHandle(dev->device_handle); 158 | LocalFree(dev->last_error_str); 159 | free(dev->read_buf); 160 | free(dev); 161 | } 162 | 163 | static void register_error(hid_device *device, const char *op) 164 | { 165 | WCHAR *ptr, *msg; 166 | 167 | FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | 168 | FORMAT_MESSAGE_FROM_SYSTEM | 169 | FORMAT_MESSAGE_IGNORE_INSERTS, 170 | NULL, 171 | GetLastError(), 172 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 173 | (LPVOID)&msg, 0/*sz*/, 174 | NULL); 175 | 176 | /* Get rid of the CR and LF that FormatMessage() sticks at the 177 | end of the message. Thanks Microsoft! */ 178 | ptr = msg; 179 | while (*ptr) { 180 | if (*ptr == '\r') { 181 | *ptr = 0x0000; 182 | break; 183 | } 184 | ptr++; 185 | } 186 | 187 | /* Store the message off in the Device entry so that 188 | the hid_error() function can pick it up. */ 189 | LocalFree(device->last_error_str); 190 | device->last_error_str = msg; 191 | } 192 | 193 | #ifndef HIDAPI_USE_DDK 194 | static int lookup_functions() 195 | { 196 | lib_handle = LoadLibraryA("hid.dll"); 197 | if (lib_handle) { 198 | #define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1; 199 | RESOLVE(HidD_GetAttributes); 200 | RESOLVE(HidD_GetSerialNumberString); 201 | RESOLVE(HidD_GetManufacturerString); 202 | RESOLVE(HidD_GetProductString); 203 | RESOLVE(HidD_SetFeature); 204 | RESOLVE(HidD_GetFeature); 205 | RESOLVE(HidD_GetIndexedString); 206 | RESOLVE(HidD_GetPreparsedData); 207 | RESOLVE(HidD_FreePreparsedData); 208 | RESOLVE(HidP_GetCaps); 209 | #undef RESOLVE 210 | } 211 | else 212 | return -1; 213 | 214 | return 0; 215 | } 216 | #endif 217 | 218 | static HANDLE open_device(const char *path, BOOL enumerate) 219 | { 220 | HANDLE handle; 221 | DWORD desired_access = (enumerate)? 0: (GENERIC_WRITE | GENERIC_READ); 222 | DWORD share_mode = (enumerate)? 223 | FILE_SHARE_READ|FILE_SHARE_WRITE: 224 | FILE_SHARE_READ; 225 | 226 | handle = CreateFileA(path, 227 | desired_access, 228 | share_mode, 229 | NULL, 230 | OPEN_EXISTING, 231 | FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/ 232 | 0); 233 | 234 | return handle; 235 | } 236 | 237 | int HID_API_EXPORT hid_init(void) 238 | { 239 | #ifndef HIDAPI_USE_DDK 240 | if (!initialized) { 241 | if (lookup_functions() < 0) { 242 | hid_exit(); 243 | return -1; 244 | } 245 | initialized = TRUE; 246 | } 247 | #endif 248 | return 0; 249 | } 250 | 251 | int HID_API_EXPORT hid_exit(void) 252 | { 253 | #ifndef HIDAPI_USE_DDK 254 | if (lib_handle) 255 | FreeLibrary(lib_handle); 256 | lib_handle = NULL; 257 | initialized = FALSE; 258 | #endif 259 | return 0; 260 | } 261 | 262 | struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id) 263 | { 264 | BOOL res; 265 | struct hid_device_info *root = NULL; /* return object */ 266 | struct hid_device_info *cur_dev = NULL; 267 | 268 | /* Windows objects for interacting with the driver. */ 269 | GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} }; 270 | SP_DEVINFO_DATA devinfo_data; 271 | SP_DEVICE_INTERFACE_DATA device_interface_data; 272 | SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL; 273 | HDEVINFO device_info_set = INVALID_HANDLE_VALUE; 274 | int device_index = 0; 275 | int i; 276 | 277 | if (hid_init() < 0) 278 | return NULL; 279 | 280 | /* Initialize the Windows objects. */ 281 | memset(&devinfo_data, 0x0, sizeof(devinfo_data)); 282 | devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA); 283 | device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); 284 | 285 | /* Get information for all the devices belonging to the HID class. */ 286 | device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); 287 | 288 | /* Iterate over each device in the HID class, looking for the right one. */ 289 | 290 | for (;;) { 291 | HANDLE write_handle = INVALID_HANDLE_VALUE; 292 | DWORD required_size = 0; 293 | HIDD_ATTRIBUTES attrib; 294 | 295 | res = SetupDiEnumDeviceInterfaces(device_info_set, 296 | NULL, 297 | &InterfaceClassGuid, 298 | device_index, 299 | &device_interface_data); 300 | 301 | if (!res) { 302 | /* A return of FALSE from this function means that 303 | there are no more devices. */ 304 | break; 305 | } 306 | 307 | /* Call with 0-sized detail size, and let the function 308 | tell us how long the detail struct needs to be. The 309 | size is put in &required_size. */ 310 | res = SetupDiGetDeviceInterfaceDetailA(device_info_set, 311 | &device_interface_data, 312 | NULL, 313 | 0, 314 | &required_size, 315 | NULL); 316 | 317 | /* Allocate a long enough structure for device_interface_detail_data. */ 318 | device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(required_size); 319 | device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); 320 | 321 | /* Get the detailed data for this device. The detail data gives us 322 | the device path for this device, which is then passed into 323 | CreateFile() to get a handle to the device. */ 324 | res = SetupDiGetDeviceInterfaceDetailA(device_info_set, 325 | &device_interface_data, 326 | device_interface_detail_data, 327 | required_size, 328 | NULL, 329 | NULL); 330 | 331 | if (!res) { 332 | /* register_error(dev, "Unable to call SetupDiGetDeviceInterfaceDetail"); 333 | Continue to the next device. */ 334 | goto cont; 335 | } 336 | 337 | /* Make sure this device is of Setup Class "HIDClass" and has a 338 | driver bound to it. */ 339 | for (i = 0; ; i++) { 340 | char driver_name[256]; 341 | 342 | /* Populate devinfo_data. This function will return failure 343 | when there are no more interfaces left. */ 344 | res = SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data); 345 | if (!res) 346 | goto cont; 347 | 348 | res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, 349 | SPDRP_CLASS, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); 350 | if (!res) 351 | goto cont; 352 | 353 | if (strcmp(driver_name, "HIDClass") == 0) { 354 | /* See if there's a driver bound. */ 355 | res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, 356 | SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); 357 | if (res) 358 | break; 359 | } 360 | } 361 | 362 | //wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath); 363 | 364 | /* Open a handle to the device */ 365 | write_handle = open_device(device_interface_detail_data->DevicePath, TRUE); 366 | 367 | /* Check validity of write_handle. */ 368 | if (write_handle == INVALID_HANDLE_VALUE) { 369 | /* Unable to open the device. */ 370 | //register_error(dev, "CreateFile"); 371 | goto cont_close; 372 | } 373 | 374 | 375 | /* Get the Vendor ID and Product ID for this device. */ 376 | attrib.Size = sizeof(HIDD_ATTRIBUTES); 377 | HidD_GetAttributes(write_handle, &attrib); 378 | //wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID); 379 | 380 | /* Check the VID/PID to see if we should add this 381 | device to the enumeration list. */ 382 | if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) && 383 | (product_id == 0x0 || attrib.ProductID == product_id)) { 384 | 385 | #define WSTR_LEN 512 386 | const char *str; 387 | struct hid_device_info *tmp; 388 | PHIDP_PREPARSED_DATA pp_data = NULL; 389 | HIDP_CAPS caps; 390 | BOOLEAN res; 391 | NTSTATUS nt_res; 392 | wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */ 393 | size_t len; 394 | 395 | /* VID/PID match. Create the record. */ 396 | tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); 397 | if (cur_dev) { 398 | cur_dev->next = tmp; 399 | } 400 | else { 401 | root = tmp; 402 | } 403 | cur_dev = tmp; 404 | 405 | /* Get the Usage Page and Usage for this device. */ 406 | res = HidD_GetPreparsedData(write_handle, &pp_data); 407 | if (res) { 408 | nt_res = HidP_GetCaps(pp_data, &caps); 409 | if (nt_res == HIDP_STATUS_SUCCESS) { 410 | cur_dev->usage_page = caps.UsagePage; 411 | cur_dev->usage = caps.Usage; 412 | } 413 | 414 | HidD_FreePreparsedData(pp_data); 415 | } 416 | 417 | /* Fill out the record */ 418 | cur_dev->next = NULL; 419 | str = device_interface_detail_data->DevicePath; 420 | if (str) { 421 | len = strlen(str); 422 | cur_dev->path = (char*) calloc(len+1, sizeof(char)); 423 | strncpy(cur_dev->path, str, len+1); 424 | cur_dev->path[len] = '\0'; 425 | } 426 | else 427 | cur_dev->path = NULL; 428 | 429 | /* Serial Number */ 430 | res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr)); 431 | wstr[WSTR_LEN-1] = 0x0000; 432 | if (res) { 433 | cur_dev->serial_number = _wcsdup(wstr); 434 | } 435 | 436 | /* Manufacturer String */ 437 | res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr)); 438 | wstr[WSTR_LEN-1] = 0x0000; 439 | if (res) { 440 | cur_dev->manufacturer_string = _wcsdup(wstr); 441 | } 442 | 443 | /* Product String */ 444 | res = HidD_GetProductString(write_handle, wstr, sizeof(wstr)); 445 | wstr[WSTR_LEN-1] = 0x0000; 446 | if (res) { 447 | cur_dev->product_string = _wcsdup(wstr); 448 | } 449 | 450 | /* VID/PID */ 451 | cur_dev->vendor_id = attrib.VendorID; 452 | cur_dev->product_id = attrib.ProductID; 453 | 454 | /* Release Number */ 455 | cur_dev->release_number = attrib.VersionNumber; 456 | 457 | /* Interface Number. It can sometimes be parsed out of the path 458 | on Windows if a device has multiple interfaces. See 459 | http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or 460 | search for "Hardware IDs for HID Devices" at MSDN. If it's not 461 | in the path, it's set to -1. */ 462 | cur_dev->interface_number = -1; 463 | if (cur_dev->path) { 464 | char *interface_component = strstr(cur_dev->path, "&mi_"); 465 | if (interface_component) { 466 | char *hex_str = interface_component + 4; 467 | char *endptr = NULL; 468 | cur_dev->interface_number = strtol(hex_str, &endptr, 16); 469 | if (endptr == hex_str) { 470 | /* The parsing failed. Set interface_number to -1. */ 471 | cur_dev->interface_number = -1; 472 | } 473 | } 474 | } 475 | } 476 | 477 | cont_close: 478 | CloseHandle(write_handle); 479 | cont: 480 | /* We no longer need the detail data. It can be freed */ 481 | free(device_interface_detail_data); 482 | 483 | device_index++; 484 | 485 | } 486 | 487 | /* Close the device information handle. */ 488 | SetupDiDestroyDeviceInfoList(device_info_set); 489 | 490 | return root; 491 | 492 | } 493 | 494 | void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs) 495 | { 496 | /* TODO: Merge this with the Linux version. This function is platform-independent. */ 497 | struct hid_device_info *d = devs; 498 | while (d) { 499 | struct hid_device_info *next = d->next; 500 | free(d->path); 501 | free(d->serial_number); 502 | free(d->manufacturer_string); 503 | free(d->product_string); 504 | free(d); 505 | d = next; 506 | } 507 | } 508 | 509 | 510 | HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) 511 | { 512 | /* TODO: Merge this functions with the Linux version. This function should be platform independent. */ 513 | struct hid_device_info *devs, *cur_dev; 514 | const char *path_to_open = NULL; 515 | hid_device *handle = NULL; 516 | 517 | devs = hid_enumerate(vendor_id, product_id); 518 | cur_dev = devs; 519 | while (cur_dev) { 520 | if (cur_dev->vendor_id == vendor_id && 521 | cur_dev->product_id == product_id) { 522 | if (serial_number) { 523 | if (wcscmp(serial_number, cur_dev->serial_number) == 0) { 524 | path_to_open = cur_dev->path; 525 | break; 526 | } 527 | } 528 | else { 529 | path_to_open = cur_dev->path; 530 | break; 531 | } 532 | } 533 | cur_dev = cur_dev->next; 534 | } 535 | 536 | if (path_to_open) { 537 | /* Open the device */ 538 | handle = hid_open_path(path_to_open); 539 | } 540 | 541 | hid_free_enumeration(devs); 542 | 543 | return handle; 544 | } 545 | 546 | HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) 547 | { 548 | hid_device *dev; 549 | HIDP_CAPS caps; 550 | PHIDP_PREPARSED_DATA pp_data = NULL; 551 | BOOLEAN res; 552 | NTSTATUS nt_res; 553 | 554 | if (hid_init() < 0) { 555 | return NULL; 556 | } 557 | 558 | dev = new_hid_device(); 559 | 560 | /* Open a handle to the device */ 561 | dev->device_handle = open_device(path, FALSE); 562 | 563 | /* Check validity of write_handle. */ 564 | if (dev->device_handle == INVALID_HANDLE_VALUE) { 565 | /* Unable to open the device. */ 566 | register_error(dev, "CreateFile"); 567 | goto err; 568 | } 569 | 570 | /* Get the Input Report length for the device. */ 571 | res = HidD_GetPreparsedData(dev->device_handle, &pp_data); 572 | if (!res) { 573 | register_error(dev, "HidD_GetPreparsedData"); 574 | goto err; 575 | } 576 | nt_res = HidP_GetCaps(pp_data, &caps); 577 | if (nt_res != HIDP_STATUS_SUCCESS) { 578 | register_error(dev, "HidP_GetCaps"); 579 | goto err_pp_data; 580 | } 581 | dev->output_report_length = caps.OutputReportByteLength; 582 | dev->input_report_length = caps.InputReportByteLength; 583 | HidD_FreePreparsedData(pp_data); 584 | 585 | dev->read_buf = (char*) malloc(dev->input_report_length); 586 | 587 | return dev; 588 | 589 | err_pp_data: 590 | HidD_FreePreparsedData(pp_data); 591 | err: 592 | free_hid_device(dev); 593 | return NULL; 594 | } 595 | 596 | int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length) 597 | { 598 | DWORD bytes_written; 599 | BOOL res; 600 | 601 | OVERLAPPED ol; 602 | unsigned char *buf; 603 | memset(&ol, 0, sizeof(ol)); 604 | 605 | /* Make sure the right number of bytes are passed to WriteFile. Windows 606 | expects the number of bytes which are in the _longest_ report (plus 607 | one for the report number) bytes even if the data is a report 608 | which is shorter than that. Windows gives us this value in 609 | caps.OutputReportByteLength. If a user passes in fewer bytes than this, 610 | create a temporary buffer which is the proper size. */ 611 | if (length >= dev->output_report_length) { 612 | /* The user passed the right number of bytes. Use the buffer as-is. */ 613 | buf = (unsigned char *) data; 614 | } else { 615 | /* Create a temporary buffer and copy the user's data 616 | into it, padding the rest with zeros. */ 617 | buf = (unsigned char *) malloc(dev->output_report_length); 618 | memcpy(buf, data, length); 619 | memset(buf + length, 0, dev->output_report_length - length); 620 | length = dev->output_report_length; 621 | } 622 | 623 | res = WriteFile(dev->device_handle, buf, length, NULL, &ol); 624 | 625 | if (!res) { 626 | if (GetLastError() != ERROR_IO_PENDING) { 627 | /* WriteFile() failed. Return error. */ 628 | register_error(dev, "WriteFile"); 629 | bytes_written = -1; 630 | goto end_of_function; 631 | } 632 | } 633 | 634 | /* Wait here until the write is done. This makes 635 | hid_write() synchronous. */ 636 | res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/); 637 | if (!res) { 638 | /* The Write operation failed. */ 639 | register_error(dev, "WriteFile"); 640 | bytes_written = -1; 641 | goto end_of_function; 642 | } 643 | 644 | end_of_function: 645 | if (buf != data) 646 | free(buf); 647 | 648 | return bytes_written; 649 | } 650 | 651 | 652 | int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) 653 | { 654 | DWORD bytes_read = 0; 655 | BOOL res; 656 | 657 | /* Copy the handle for convenience. */ 658 | HANDLE ev = dev->ol.hEvent; 659 | 660 | if (!dev->read_pending) { 661 | /* Start an Overlapped I/O read. */ 662 | dev->read_pending = TRUE; 663 | memset(dev->read_buf, 0, dev->input_report_length); 664 | ResetEvent(ev); 665 | res = ReadFile(dev->device_handle, dev->read_buf, dev->input_report_length, &bytes_read, &dev->ol); 666 | 667 | if (!res) { 668 | if (GetLastError() != ERROR_IO_PENDING) { 669 | /* ReadFile() has failed. 670 | Clean up and return error. */ 671 | CancelIo(dev->device_handle); 672 | dev->read_pending = FALSE; 673 | goto end_of_function; 674 | } 675 | } 676 | } 677 | 678 | if (milliseconds >= 0) { 679 | /* See if there is any data yet. */ 680 | res = WaitForSingleObject(ev, milliseconds); 681 | if (res != WAIT_OBJECT_0) { 682 | /* There was no data this time. Return zero bytes available, 683 | but leave the Overlapped I/O running. */ 684 | return 0; 685 | } 686 | } 687 | 688 | /* Either WaitForSingleObject() told us that ReadFile has completed, or 689 | we are in non-blocking mode. Get the number of bytes read. The actual 690 | data has been copied to the data[] array which was passed to ReadFile(). */ 691 | res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/); 692 | 693 | /* Set pending back to false, even if GetOverlappedResult() returned error. */ 694 | dev->read_pending = FALSE; 695 | 696 | if (res && bytes_read > 0) { 697 | if (dev->read_buf[0] == 0x0) { 698 | /* If report numbers aren't being used, but Windows sticks a report 699 | number (0x0) on the beginning of the report anyway. To make this 700 | work like the other platforms, and to make it work more like the 701 | HID spec, we'll skip over this byte. */ 702 | size_t copy_len; 703 | bytes_read--; 704 | copy_len = length > bytes_read ? bytes_read : length; 705 | memcpy(data, dev->read_buf+1, copy_len); 706 | } 707 | else { 708 | /* Copy the whole buffer, report number and all. */ 709 | size_t copy_len = length > bytes_read ? bytes_read : length; 710 | memcpy(data, dev->read_buf, copy_len); 711 | } 712 | } 713 | 714 | end_of_function: 715 | if (!res) { 716 | register_error(dev, "GetOverlappedResult"); 717 | return -1; 718 | } 719 | 720 | return bytes_read; 721 | } 722 | 723 | int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length) 724 | { 725 | return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); 726 | } 727 | 728 | int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock) 729 | { 730 | dev->blocking = !nonblock; 731 | return 0; /* Success */ 732 | } 733 | 734 | int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) 735 | { 736 | BOOL res = HidD_SetFeature(dev->device_handle, (PVOID)data, length); 737 | if (!res) { 738 | register_error(dev, "HidD_SetFeature"); 739 | return -1; 740 | } 741 | 742 | return length; 743 | } 744 | 745 | 746 | int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) 747 | { 748 | BOOL res; 749 | #if 0 750 | res = HidD_GetFeature(dev->device_handle, data, length); 751 | if (!res) { 752 | register_error(dev, "HidD_GetFeature"); 753 | return -1; 754 | } 755 | return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */ 756 | #else 757 | DWORD bytes_returned; 758 | 759 | OVERLAPPED ol; 760 | memset(&ol, 0, sizeof(ol)); 761 | 762 | res = DeviceIoControl(dev->device_handle, 763 | IOCTL_HID_GET_FEATURE, 764 | data, length, 765 | data, length, 766 | &bytes_returned, &ol); 767 | 768 | if (!res) { 769 | if (GetLastError() != ERROR_IO_PENDING) { 770 | /* DeviceIoControl() failed. Return error. */ 771 | register_error(dev, "Send Feature Report DeviceIoControl"); 772 | return -1; 773 | } 774 | } 775 | 776 | /* Wait here until the write is done. This makes 777 | hid_get_feature_report() synchronous. */ 778 | res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/); 779 | if (!res) { 780 | /* The operation failed. */ 781 | register_error(dev, "Send Feature Report GetOverLappedResult"); 782 | return -1; 783 | } 784 | return bytes_returned; 785 | #endif 786 | } 787 | 788 | void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev) 789 | { 790 | if (!dev) 791 | return; 792 | CancelIo(dev->device_handle); 793 | free_hid_device(dev); 794 | } 795 | 796 | int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) 797 | { 798 | BOOL res; 799 | 800 | res = HidD_GetManufacturerString(dev->device_handle, string, sizeof(wchar_t) * maxlen); 801 | if (!res) { 802 | register_error(dev, "HidD_GetManufacturerString"); 803 | return -1; 804 | } 805 | 806 | return 0; 807 | } 808 | 809 | int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) 810 | { 811 | BOOL res; 812 | 813 | res = HidD_GetProductString(dev->device_handle, string, sizeof(wchar_t) * maxlen); 814 | if (!res) { 815 | register_error(dev, "HidD_GetProductString"); 816 | return -1; 817 | } 818 | 819 | return 0; 820 | } 821 | 822 | int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) 823 | { 824 | BOOL res; 825 | 826 | res = HidD_GetSerialNumberString(dev->device_handle, string, sizeof(wchar_t) * maxlen); 827 | if (!res) { 828 | register_error(dev, "HidD_GetSerialNumberString"); 829 | return -1; 830 | } 831 | 832 | return 0; 833 | } 834 | 835 | int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) 836 | { 837 | BOOL res; 838 | 839 | res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * maxlen); 840 | if (!res) { 841 | register_error(dev, "HidD_GetIndexedString"); 842 | return -1; 843 | } 844 | 845 | return 0; 846 | } 847 | 848 | 849 | HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) 850 | { 851 | return (wchar_t*)dev->last_error_str; 852 | } 853 | 854 | 855 | /*#define PICPGM*/ 856 | /*#define S11*/ 857 | #define P32 858 | #ifdef S11 859 | unsigned short VendorID = 0xa0a0; 860 | unsigned short ProductID = 0x0001; 861 | #endif 862 | 863 | #ifdef P32 864 | unsigned short VendorID = 0x04d8; 865 | unsigned short ProductID = 0x3f; 866 | #endif 867 | 868 | 869 | #ifdef PICPGM 870 | unsigned short VendorID = 0x04d8; 871 | unsigned short ProductID = 0x0033; 872 | #endif 873 | 874 | 875 | #if 0 876 | int __cdecl main(int argc, char* argv[]) 877 | { 878 | int res; 879 | unsigned char buf[65]; 880 | 881 | UNREFERENCED_PARAMETER(argc); 882 | UNREFERENCED_PARAMETER(argv); 883 | 884 | /* Set up the command buffer. */ 885 | memset(buf,0x00,sizeof(buf)); 886 | buf[0] = 0; 887 | buf[1] = 0x81; 888 | 889 | 890 | /* Open the device. */ 891 | int handle = open(VendorID, ProductID, L"12345"); 892 | if (handle < 0) 893 | printf("unable to open device\n"); 894 | 895 | 896 | /* Toggle LED (cmd 0x80) */ 897 | buf[1] = 0x80; 898 | res = write(handle, buf, 65); 899 | if (res < 0) 900 | printf("Unable to write()\n"); 901 | 902 | /* Request state (cmd 0x81) */ 903 | buf[1] = 0x81; 904 | write(handle, buf, 65); 905 | if (res < 0) 906 | printf("Unable to write() (2)\n"); 907 | 908 | /* Read requested state */ 909 | read(handle, buf, 65); 910 | if (res < 0) 911 | printf("Unable to read()\n"); 912 | 913 | /* Print out the returned buffer. */ 914 | for (int i = 0; i < 4; i++) 915 | printf("buf[%d]: %d\n", i, buf[i]); 916 | 917 | return 0; 918 | } 919 | #endif 920 | 921 | #ifdef __cplusplus 922 | } /* extern "C" */ 923 | #endif -------------------------------------------------------------------------------- /ext/blink1/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 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. 222 | */ 223 | int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); 224 | 225 | /** @brief Read an Input report from a HID device. 226 | 227 | Input reports are returned 228 | to the host through the INTERRUPT IN endpoint. The first byte will 229 | contain the Report number if the device uses numbered reports. 230 | 231 | @ingroup API 232 | @param device A device handle returned from hid_open(). 233 | @param data A buffer to put the read data into. 234 | @param length The number of bytes to read. For devices with 235 | multiple reports, make sure to read an extra byte for 236 | the report number. 237 | 238 | @returns 239 | This function returns the actual number of bytes read and 240 | -1 on error. 241 | */ 242 | int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length); 243 | 244 | /** @brief Set the device handle to be non-blocking. 245 | 246 | In non-blocking mode calls to hid_read() will return 247 | immediately with a value of 0 if there is no data to be 248 | read. In blocking mode, hid_read() will wait (block) until 249 | there is data to read before returning. 250 | 251 | Nonblocking can be turned on and off at any time. 252 | 253 | @ingroup API 254 | @param device A device handle returned from hid_open(). 255 | @param nonblock enable or not the nonblocking reads 256 | - 1 to enable nonblocking 257 | - 0 to disable nonblocking. 258 | 259 | @returns 260 | This function returns 0 on success and -1 on error. 261 | */ 262 | int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock); 263 | 264 | /** @brief Send a Feature report to the device. 265 | 266 | Feature reports are sent over the Control endpoint as a 267 | Set_Report transfer. The first byte of @p data[] must 268 | contain the Report ID. For devices which only support a 269 | single report, this must be set to 0x0. The remaining bytes 270 | contain the report data. Since the Report ID is mandatory, 271 | calls to hid_send_feature_report() will always contain one 272 | more byte than the report contains. For example, if a hid 273 | report is 16 bytes long, 17 bytes must be passed to 274 | hid_send_feature_report(): the Report ID (or 0x0, for 275 | devices which do not use numbered reports), followed by the 276 | report data (16 bytes). In this example, the length passed 277 | in would be 17. 278 | 279 | @ingroup API 280 | @param device A device handle returned from hid_open(). 281 | @param data The data to send, including the report number as 282 | the first byte. 283 | @param length The length in bytes of the data to send, including 284 | the report number. 285 | 286 | @returns 287 | This function returns the actual number of bytes written and 288 | -1 on error. 289 | */ 290 | int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length); 291 | 292 | /** @brief Get a feature report from a HID device. 293 | 294 | Make sure to set the first byte of @p data[] to the Report 295 | ID of the report to be read. Make sure to allow space for 296 | this extra byte in @p data[]. 297 | 298 | @ingroup API 299 | @param device A device handle returned from hid_open(). 300 | @param data A buffer to put the read data into, including 301 | the Report ID. Set the first byte of @p data[] to the 302 | Report ID of the report to be read. 303 | @param length The number of bytes to read, including an 304 | extra byte for the report ID. The buffer can be longer 305 | than the actual report. 306 | 307 | @returns 308 | This function returns the number of bytes read and 309 | -1 on error. 310 | */ 311 | int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length); 312 | 313 | /** @brief Close a HID device. 314 | 315 | @ingroup API 316 | @param device A device handle returned from hid_open(). 317 | */ 318 | void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device); 319 | 320 | /** @brief Get The Manufacturer String from a HID device. 321 | 322 | @ingroup API 323 | @param device A device handle returned from hid_open(). 324 | @param string A wide string buffer to put the data into. 325 | @param maxlen The length of the buffer in multiples of wchar_t. 326 | 327 | @returns 328 | This function returns 0 on success and -1 on error. 329 | */ 330 | int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen); 331 | 332 | /** @brief Get The Product String from a HID device. 333 | 334 | @ingroup API 335 | @param device A device handle returned from hid_open(). 336 | @param string A wide string buffer to put the data into. 337 | @param maxlen The length of the buffer in multiples of wchar_t. 338 | 339 | @returns 340 | This function returns 0 on success and -1 on error. 341 | */ 342 | int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen); 343 | 344 | /** @brief Get The Serial Number String from a HID device. 345 | 346 | @ingroup API 347 | @param device A device handle returned from hid_open(). 348 | @param string A wide string buffer to put the data into. 349 | @param maxlen The length of the buffer in multiples of wchar_t. 350 | 351 | @returns 352 | This function returns 0 on success and -1 on error. 353 | */ 354 | int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen); 355 | 356 | /** @brief Get a string from a HID device, based on its string index. 357 | 358 | @ingroup API 359 | @param device A device handle returned from hid_open(). 360 | @param string_index The index of the string to get. 361 | @param string A wide string buffer to put the data into. 362 | @param maxlen The length of the buffer in multiples of wchar_t. 363 | 364 | @returns 365 | This function returns 0 on success and -1 on error. 366 | */ 367 | int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen); 368 | 369 | /** @brief Get a string describing the last error which occurred. 370 | 371 | @ingroup API 372 | @param device A device handle returned from hid_open(). 373 | 374 | @returns 375 | This function returns a string containing the last error 376 | which occurred or NULL if none has occurred. 377 | */ 378 | HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device); 379 | 380 | #ifdef __cplusplus 381 | } 382 | #endif 383 | 384 | #endif 385 | -------------------------------------------------------------------------------- /ext/blink1/osccal.h: -------------------------------------------------------------------------------- 1 | #ifndef __OSCAL_H__ 2 | #define __OSCAL_H__ 3 | 4 | #include 5 | #include 6 | 7 | const uint8_t EEPROM_ADDR_OSCCAL = 0; 8 | 9 | /* ------------------------------------------------------------------------- */ 10 | /* ------------------------ Oscillator Calibration ------------------------- */ 11 | /* ------------------------------------------------------------------------- */ 12 | 13 | // put this in main() before usbInit 14 | static void calibrationLoad(void) 15 | { 16 | uint8_t calibrationValue; 17 | // calibration value from last time 18 | calibrationValue = eeprom_read_byte(EEPROM_ADDR_OSCCAL); 19 | if(calibrationValue != 0xff){ 20 | OSCCAL = calibrationValue; 21 | } 22 | } 23 | 24 | /* Calibrate the RC oscillator to 8.25 MHz. The core clock of 16.5 MHz is 25 | * derived from the 66 MHz peripheral clock by dividing. Our timing reference 26 | * is the Start Of Frame signal (a single SE0 bit) available immediately after 27 | * a USB RESET. We first do a binary search for the OSCCAL value and then 28 | * optimize this value with a neighboorhod search. 29 | * This algorithm may also be used to calibrate the RC oscillator directly to 30 | * 12 MHz (no PLL involved, can therefore be used on almost ALL AVRs), but this 31 | * is wide outside the spec for the OSCCAL value and the required precision for 32 | * the 12 MHz clock! Use the RC oscillator calibrated to 12 MHz for 33 | * experimental purposes only! 34 | */ 35 | static void calibrateOscillator(void) 36 | { 37 | uchar step = 128; 38 | uchar trialValue = 0, optimumValue; 39 | int x, optimumDev; 40 | int targetValue = (unsigned)(1499 * (double)F_CPU / 10.5e6 + 0.5); 41 | 42 | /* do a binary search: */ 43 | do{ 44 | OSCCAL = trialValue + step; 45 | x = usbMeasureFrameLength(); // proportional to current real frequency 46 | if(x < targetValue) // frequency still too low 47 | trialValue += step; 48 | step >>= 1; 49 | }while(step > 0); 50 | /* We have a precision of +/- 1 for optimum OSCCAL here */ 51 | /* now do a neighborhood search for optimum value */ 52 | optimumValue = trialValue; 53 | optimumDev = x; /* this is certainly far away from optimum */ 54 | for(OSCCAL = trialValue - 1; OSCCAL <= trialValue + 1; OSCCAL++){ 55 | x = usbMeasureFrameLength() - targetValue; 56 | if(x < 0) 57 | x = -x; 58 | if(x < optimumDev){ 59 | optimumDev = x; 60 | optimumValue = OSCCAL; 61 | } 62 | } 63 | OSCCAL = optimumValue; 64 | } 65 | /* 66 | Note: This calibration algorithm may try OSCCAL values of up to 192 even if 67 | the optimum value is far below 192. It may therefore exceed the allowed clock 68 | frequency of the CPU in low voltage designs! 69 | You may replace this search algorithm with any other algorithm you like if 70 | you have additional constraints such as a maximum CPU clock. 71 | For version 5.x RC oscillators (those with a split range of 2x128 steps, e.g. 72 | ATTiny25, ATTiny45, ATTiny85), it may be useful to search for the optimum in 73 | both regions. 74 | */ 75 | 76 | void usbEventResetReady(void) 77 | { 78 | calibrateOscillator(); 79 | // store the calibrated value in EEPROM 80 | eeprom_write_byte(EEPROM_ADDR_OSCCAL, OSCCAL); 81 | 82 | usbHasBeenSetup++; 83 | } 84 | 85 | #endif -------------------------------------------------------------------------------- /ext/blink1/usbconfig.h: -------------------------------------------------------------------------------- 1 | /* Name: usbconfig.h 2 | * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers 3 | * Author: Christian Starkjohann 4 | * Creation Date: 2005-04-01 5 | * Tabsize: 4 6 | * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH 7 | * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) 8 | * This Revision: $Id$ 9 | */ 10 | 11 | #ifndef __usbconfig_h_included__ 12 | #define __usbconfig_h_included__ 13 | 14 | //#include 15 | 16 | /* 17 | General Description: 18 | This file is an example configuration (with inline documentation) for the USB 19 | driver. It configures V-USB for USB D+ connected to Port D bit 2 (which is 20 | also hardware interrupt 0 on many devices) and USB D- to Port D bit 4. You may 21 | wire the lines to any other port, as long as D+ is also wired to INT0 (or any 22 | other hardware interrupt, as long as it is the highest level interrupt, see 23 | section at the end of this file). 24 | */ 25 | 26 | /* ---------------------------- Hardware Config ---------------------------- */ 27 | 28 | #define USB_CFG_IOPORTNAME B 29 | /* This is the port where the USB bus is connected. When you configure it to 30 | * "B", the registers PORTB, PINB and DDRB will be used. 31 | */ 32 | #define USB_CFG_DMINUS_BIT 3 33 | /* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected. 34 | * This may be any bit in the port. 35 | */ 36 | #define USB_CFG_DPLUS_BIT 2 37 | /* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected. 38 | * This may be any bit in the port. Please note that D+ must also be connected 39 | * to interrupt pin INT0! [You can also use other interrupts, see section 40 | * "Optional MCU Description" below, or you can connect D- to the interrupt, as 41 | * it is required if you use the USB_COUNT_SOF feature. If you use D- for the 42 | * interrupt, the USB interrupt will also be triggered at Start-Of-Frame 43 | * markers every millisecond.] 44 | */ 45 | #define USB_CFG_CLOCK_KHZ (F_CPU/1000) 46 | /* Clock rate of the AVR in kHz. Legal values are 12000, 12800, 15000, 16000, 47 | * 16500, 18000 and 20000. The 12.8 MHz and 16.5 MHz versions of the code 48 | * require no crystal, they tolerate +/- 1% deviation from the nominal 49 | * frequency. All other rates require a precision of 2000 ppm and thus a 50 | * crystal! 51 | * Since F_CPU should be defined to your actual clock rate anyway, you should 52 | * not need to modify this setting. 53 | */ 54 | #define USB_CFG_CHECK_CRC 0 55 | /* Define this to 1 if you want that the driver checks integrity of incoming 56 | * data packets (CRC checks). CRC checks cost quite a bit of code size and are 57 | * currently only available for 18 MHz crystal clock. You must choose 58 | * USB_CFG_CLOCK_KHZ = 18000 if you enable this option. 59 | */ 60 | 61 | /* ----------------------- Optional Hardware Config ------------------------ */ 62 | 63 | /* #define USB_CFG_PULLUP_IOPORTNAME D */ 64 | /* If you connect the 1.5k pullup resistor from D- to a port pin instead of 65 | * V+, you can connect and disconnect the device from firmware by calling 66 | * the macros usbDeviceConnect() and usbDeviceDisconnect() (see usbdrv.h). 67 | * This constant defines the port on which the pullup resistor is connected. 68 | */ 69 | /* #define USB_CFG_PULLUP_BIT 4 */ 70 | /* This constant defines the bit number in USB_CFG_PULLUP_IOPORT (defined 71 | * above) where the 1.5k pullup resistor is connected. See description 72 | * above for details. 73 | */ 74 | 75 | /* --------------------------- Functional Range ---------------------------- */ 76 | 77 | #define USB_CFG_HAVE_INTRIN_ENDPOINT 1 78 | /* Define this to 1 if you want to compile a version with two endpoints: The 79 | * default control endpoint 0 and an interrupt-in endpoint (any other endpoint 80 | * number). 81 | */ 82 | #define USB_CFG_HAVE_INTRIN_ENDPOINT3 0 83 | /* Define this to 1 if you want to compile a version with three endpoints: The 84 | * default control endpoint 0, an interrupt-in endpoint 3 (or the number 85 | * configured below) and a catch-all default interrupt-in endpoint as above. 86 | * You must also define USB_CFG_HAVE_INTRIN_ENDPOINT to 1 for this feature. 87 | */ 88 | #define USB_CFG_EP3_NUMBER 3 89 | /* If the so-called endpoint 3 is used, it can now be configured to any other 90 | * endpoint number (except 0) with this macro. Default if undefined is 3. 91 | */ 92 | /* #define USB_INITIAL_DATATOKEN USBPID_DATA1 */ 93 | /* The above macro defines the startup condition for data toggling on the 94 | * interrupt/bulk endpoints 1 and 3. Defaults to USBPID_DATA1. 95 | * Since the token is toggled BEFORE sending any data, the first packet is 96 | * sent with the oposite value of this configuration! 97 | */ 98 | #define USB_CFG_IMPLEMENT_HALT 0 99 | /* Define this to 1 if you also want to implement the ENDPOINT_HALT feature 100 | * for endpoint 1 (interrupt endpoint). Although you may not need this feature, 101 | * it is required by the standard. We have made it a config option because it 102 | * bloats the code considerably. 103 | */ 104 | #define USB_CFG_SUPPRESS_INTR_CODE 0 105 | /* Define this to 1 if you want to declare interrupt-in endpoints, but don't 106 | * want to send any data over them. If this macro is defined to 1, functions 107 | * usbSetInterrupt() and usbSetInterrupt3() are omitted. This is useful if 108 | * you need the interrupt-in endpoints in order to comply to an interface 109 | * (e.g. HID), but never want to send any data. This option saves a couple 110 | * of bytes in flash memory and the transmit buffers in RAM. 111 | */ 112 | #define USB_CFG_INTR_POLL_INTERVAL 50 113 | /* If you compile a version with endpoint 1 (interrupt-in), this is the poll 114 | * interval. The value is in milliseconds and must not be less than 10 ms for 115 | * low speed devices. 116 | */ 117 | #define USB_CFG_IS_SELF_POWERED 0 118 | /* Define this to 1 if the device has its own power supply. Set it to 0 if the 119 | * device is powered from the USB bus. 120 | */ 121 | #define USB_CFG_MAX_BUS_POWER 100 122 | /* Set this variable to the maximum USB bus power consumption of your device. 123 | * The value is in milliamperes. [It will be divided by two since USB 124 | * communicates power requirements in units of 2 mA.] 125 | */ 126 | #define USB_CFG_IMPLEMENT_FN_WRITE 1 127 | /* Set this to 1 if you want usbFunctionWrite() to be called for control-out 128 | * transfers. Set it to 0 if you don't need it and want to save a couple of 129 | * bytes. 130 | */ 131 | #define USB_CFG_IMPLEMENT_FN_READ 1 132 | /* Set this to 1 if you need to send control replies which are generated 133 | * "on the fly" when usbFunctionRead() is called. If you only want to send 134 | * data from a static buffer, set it to 0 and return the data from 135 | * usbFunctionSetup(). This saves a couple of bytes. 136 | */ 137 | #define USB_CFG_IMPLEMENT_FN_WRITEOUT 0 138 | /* Define this to 1 if you want to use interrupt-out (or bulk out) endpoints. 139 | * You must implement the function usbFunctionWriteOut() which receives all 140 | * interrupt/bulk data sent to any endpoint other than 0. The endpoint number 141 | * can be found in 'usbRxToken'. 142 | */ 143 | #define USB_CFG_HAVE_FLOWCONTROL 0 144 | /* Define this to 1 if you want flowcontrol over USB data. See the definition 145 | * of the macros usbDisableAllRequests() and usbEnableAllRequests() in 146 | * usbdrv.h. 147 | */ 148 | #define USB_CFG_DRIVER_FLASH_PAGE 0 149 | /* If the device has more than 64 kBytes of flash, define this to the 64 k page 150 | * where the driver's constants (descriptors) are located. Or in other words: 151 | * Define this to 1 for boot loaders on the ATMega128. 152 | */ 153 | #define USB_CFG_LONG_TRANSFERS 0 154 | /* Define this to 1 if you want to send/receive blocks of more than 254 bytes 155 | * in a single control-in or control-out transfer. Note that the capability 156 | * for long transfers increases the driver size. 157 | */ 158 | /* #define USB_RX_USER_HOOK(data, len) if(usbRxToken == (uchar)USBPID_SETUP) blinkLED(); */ 159 | /* This macro is a hook if you want to do unconventional things. If it is 160 | * defined, it's inserted at the beginning of received message processing. 161 | * If you eat the received message and don't want default processing to 162 | * proceed, do a return after doing your things. One possible application 163 | * (besides debugging) is to flash a status LED on each packet. 164 | */ 165 | /* #define USB_RESET_HOOK(resetStarts) if(!resetStarts){hadUsbReset();} */ 166 | /* This macro is a hook if you need to know when an USB RESET occurs. It has 167 | * one parameter which distinguishes between the start of RESET state and its 168 | * end. 169 | */ 170 | /* #define USB_SET_ADDRESS_HOOK() hadAddressAssigned(); */ 171 | /* This macro (if defined) is executed when a USB SET_ADDRESS request was 172 | * received. 173 | */ 174 | #define USB_COUNT_SOF 0 175 | /* define this macro to 1 if you need the global variable "usbSofCount" which 176 | * counts SOF packets. This feature requires that the hardware interrupt is 177 | * connected to D- instead of D+. 178 | */ 179 | /* #ifdef __ASSEMBLER__ 180 | * macro myAssemblerMacro 181 | * in YL, TCNT0 182 | * sts timer0Snapshot, YL 183 | * endm 184 | * #endif 185 | * #define USB_SOF_HOOK myAssemblerMacro 186 | * This macro (if defined) is executed in the assembler module when a 187 | * Start Of Frame condition is detected. It is recommended to define it to 188 | * the name of an assembler macro which is defined here as well so that more 189 | * than one assembler instruction can be used. The macro may use the register 190 | * YL and modify SREG. If it lasts longer than a couple of cycles, USB messages 191 | * immediately after an SOF pulse may be lost and must be retried by the host. 192 | * What can you do with this hook? Since the SOF signal occurs exactly every 193 | * 1 ms (unless the host is in sleep mode), you can use it to tune OSCCAL in 194 | * designs running on the internal RC oscillator. 195 | * Please note that Start Of Frame detection works only if D- is wired to the 196 | * interrupt, not D+. THIS IS DIFFERENT THAN MOST EXAMPLES! 197 | */ 198 | #define USB_CFG_CHECK_DATA_TOGGLING 0 199 | /* define this macro to 1 if you want to filter out duplicate data packets 200 | * sent by the host. Duplicates occur only as a consequence of communication 201 | * errors, when the host does not receive an ACK. Please note that you need to 202 | * implement the filtering yourself in usbFunctionWriteOut() and 203 | * usbFunctionWrite(). Use the global usbCurrentDataToken and a static variable 204 | * for each control- and out-endpoint to check for duplicate packets. 205 | */ 206 | #ifndef __ASSEMBLER__ 207 | extern void usbEventResetReady(void); 208 | #endif 209 | #define USB_RESET_HOOK(isReset) if(!isReset){usbEventResetReady();} 210 | //#define USB_RESET_HOOK(isReset) if(!isReset){cli(); usbEventResetReady();sei();} 211 | #define USB_CFG_HAVE_MEASURE_FRAME_LENGTH 1 212 | /* define this macro to 1 if you want the function usbMeasureFrameLength() 213 | * compiled in. This function can be used to calibrate the AVR's RC oscillator. 214 | */ 215 | 216 | #define USB_USE_FAST_CRC 1 217 | /* The assembler module has two implementations for the CRC algorithm. One is 218 | * faster, the other is smaller. This CRC routine is only used for transmitted 219 | * messages where timing is not critical. The faster routine needs 31 cycles 220 | * per byte while the smaller one needs 61 to 69 cycles. The faster routine 221 | * may be worth the 32 bytes bigger code size if you transmit lots of data and 222 | * run the AVR close to its limit. 223 | */ 224 | 225 | /* -------------------------- Device Description --------------------------- */ 226 | 227 | #define USB_CFG_VENDOR_ID 0xB8, 0x27 /* = 0x27B8 = 10168 = thingm */ 228 | //#define USB_CFG_VENDOR_ID 0xA0, 0x20 /* = 0x20A0 = 8352 = obdev.at */ 229 | //#define USB_CFG_VENDOR_ID 0xc0, 0x16 /* = 0x16c0 = 5824 = voti.nl */ 230 | /* USB vendor ID for the device, low byte first. If you have registered your 231 | * own Vendor ID, define it here. Otherwise you may use one of obdev's free 232 | * shared VID/PID pairs. Be sure to read USB-IDs-for-free.txt for rules! 233 | * *** IMPORTANT NOTE *** 234 | * This template uses obdev's shared VID/PID pair for Vendor Class devices 235 | * with libusb: 0x16c0/0x5dc. Use this VID/PID pair ONLY if you understand 236 | * the implications! 237 | */ 238 | #define USB_CFG_DEVICE_ID 0xED, 0x01 /* = 0x01ED */ 239 | //#define USB_CFG_DEVICE_ID 0x11, 0x41 /* = 0x4111 = 16657 = ThingM#2 */ 240 | //#define USB_CFG_DEVICE_ID 0xdf, 0x05 /* obdev's shared PID for HIDs */ 241 | /* This is the ID of the product, low byte first. It is interpreted in the 242 | * scope of the vendor ID. If you have registered your own VID with usb.org 243 | * or if you have licensed a PID from somebody else, define it here. Otherwise 244 | * you may use one of obdev's free shared VID/PID pairs. See the file 245 | * USB-IDs-for-free.txt for details! 246 | * *** IMPORTANT NOTE *** 247 | * This template uses obdev's shared VID/PID pair for Vendor Class devices 248 | * with libusb: 0x16c0/0x5dc. Use this VID/PID pair ONLY if you understand 249 | * the implications! 250 | */ 251 | #define USB_CFG_DEVICE_VERSION 0x00, 0x01 252 | /* Version number of the device: Minor number first, then major number. 253 | */ 254 | #define USB_CFG_VENDOR_NAME 'T', 'h', 'i', 'n', 'g', 'M' 255 | #define USB_CFG_VENDOR_NAME_LEN 6 256 | /* These two values define the vendor name returned by the USB device. The name 257 | * must be given as a list of characters under single quotes. The characters 258 | * are interpreted as Unicode (UTF-16) entities. 259 | * If you don't want a vendor name string, undefine these macros. 260 | * ALWAYS define a vendor name containing your Internet domain name if you use 261 | * obdev's free shared VID/PID pair. See the file USB-IDs-for-free.txt for 262 | * details. 263 | */ 264 | #define USB_CFG_DEVICE_NAME 'b', 'l', 'i', 'n', 'k', '(', '1', ')' 265 | #define USB_CFG_DEVICE_NAME_LEN 8 266 | /* Same as above for the device name. If you don't want a device name, undefine 267 | * the macros. See the file USB-IDs-for-free.txt before you assign a name if 268 | * you use a shared VID/PID. 269 | */ 270 | /*#define USB_CFG_SERIAL_NUMBER 'N', 'o', 'n', 'e' */ 271 | /*#define USB_CFG_SERIAL_NUMBER_LEN 0 */ 272 | #define USB_CFG_SERIAL_NUMBER 'N', 'o', 'n', 't','h','i','n','g' 273 | #define USB_CFG_SERIAL_NUMBER_LEN 8 274 | /* Same as above for the serial number. If you don't want a serial number, 275 | * undefine the macros. 276 | * It may be useful to provide the serial number through other means than at 277 | * compile time. See the section about descriptor properties below for how 278 | * to fine tune control over USB descriptors such as the string descriptor 279 | * for the serial number. 280 | */ 281 | #define USB_CFG_DEVICE_CLASS 0 282 | #define USB_CFG_DEVICE_SUBCLASS 0 283 | /* See USB specification if you want to conform to an existing device class. 284 | * Class 0xff is "vendor specific". 285 | */ 286 | #define USB_CFG_INTERFACE_CLASS 3 287 | #define USB_CFG_INTERFACE_SUBCLASS 0 288 | #define USB_CFG_INTERFACE_PROTOCOL 0 289 | /* See USB specification if you want to conform to an existing device class or 290 | * protocol. The following classes must be set at interface level: 291 | * HID class is 3, no subclass and protocol required (but may be useful!) 292 | * CDC class is 2, use subclass 2 and protocol 1 for ACM 293 | */ 294 | #define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 24 295 | /* Define this to the length of the HID report descriptor, if you implement 296 | * an HID device. Otherwise don't define it or define it to 0. 297 | * If you use this define, you must add a PROGMEM character array named 298 | * "usbHidReportDescriptor" to your code which contains the report descriptor. 299 | * Don't forget to keep the array and this define in sync! 300 | */ 301 | 302 | /* #define USB_PUBLIC static */ 303 | /* Use the define above if you #include usbdrv.c instead of linking against it. 304 | * This technique saves a couple of bytes in flash memory. 305 | */ 306 | 307 | /* ------------------- Fine Control over USB Descriptors ------------------- */ 308 | /* If you don't want to use the driver's default USB descriptors, you can 309 | * provide our own. These can be provided as (1) fixed length static data in 310 | * flash memory, (2) fixed length static data in RAM or (3) dynamically at 311 | * runtime in the function usbFunctionDescriptor(). See usbdrv.h for more 312 | * information about this function. 313 | * Descriptor handling is configured through the descriptor's properties. If 314 | * no properties are defined or if they are 0, the default descriptor is used. 315 | * Possible properties are: 316 | * + USB_PROP_IS_DYNAMIC: The data for the descriptor should be fetched 317 | * at runtime via usbFunctionDescriptor(). If the usbMsgPtr mechanism is 318 | * used, the data is in FLASH by default. Add property USB_PROP_IS_RAM if 319 | * you want RAM pointers. 320 | * + USB_PROP_IS_RAM: The data returned by usbFunctionDescriptor() or found 321 | * in static memory is in RAM, not in flash memory. 322 | * + USB_PROP_LENGTH(len): If the data is in static memory (RAM or flash), 323 | * the driver must know the descriptor's length. The descriptor itself is 324 | * found at the address of a well known identifier (see below). 325 | * List of static descriptor names (must be declared PROGMEM if in flash): 326 | * char usbDescriptorDevice[]; 327 | * char usbDescriptorConfiguration[]; 328 | * char usbDescriptorHidReport[]; 329 | * char usbDescriptorString0[]; 330 | * int usbDescriptorStringVendor[]; 331 | * int usbDescriptorStringDevice[]; 332 | * int usbDescriptorStringSerialNumber[]; 333 | * Other descriptors can't be provided statically, they must be provided 334 | * dynamically at runtime. 335 | * 336 | * Descriptor properties are or-ed or added together, e.g.: 337 | * #define USB_CFG_DESCR_PROPS_DEVICE (USB_PROP_IS_RAM | USB_PROP_LENGTH(18)) 338 | * 339 | * The following descriptors are defined: 340 | * USB_CFG_DESCR_PROPS_DEVICE 341 | * USB_CFG_DESCR_PROPS_CONFIGURATION 342 | * USB_CFG_DESCR_PROPS_STRINGS 343 | * USB_CFG_DESCR_PROPS_STRING_0 344 | * USB_CFG_DESCR_PROPS_STRING_VENDOR 345 | * USB_CFG_DESCR_PROPS_STRING_PRODUCT 346 | * USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 347 | * USB_CFG_DESCR_PROPS_HID 348 | * USB_CFG_DESCR_PROPS_HID_REPORT 349 | * USB_CFG_DESCR_PROPS_UNKNOWN (for all descriptors not handled by the driver) 350 | * 351 | * Note about string descriptors: String descriptors are not just strings, they 352 | * are Unicode strings prefixed with a 2 byte header. Example: 353 | * int serialNumberDescriptor[] = { 354 | * USB_STRING_DESCRIPTOR_HEADER(6), 355 | * 'S', 'e', 'r', 'i', 'a', 'l' 356 | * }; 357 | */ 358 | 359 | #define USB_CFG_DESCR_PROPS_DEVICE 0 360 | #define USB_CFG_DESCR_PROPS_CONFIGURATION 0 361 | #define USB_CFG_DESCR_PROPS_STRINGS 0 362 | #define USB_CFG_DESCR_PROPS_STRING_0 0 363 | #define USB_CFG_DESCR_PROPS_STRING_VENDOR 0 364 | #define USB_CFG_DESCR_PROPS_STRING_PRODUCT 0 365 | //#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 0 366 | #define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER (USB_PROP_IS_RAM | (2 * USB_PROP_LENGTH(8)+2)) 367 | #define USB_CFG_DESCR_PROPS_HID 0 368 | #define USB_CFG_DESCR_PROPS_HID_REPORT 0 369 | #define USB_CFG_DESCR_PROPS_UNKNOWN 0 370 | 371 | /* ----------------------- Optional MCU Description ------------------------ */ 372 | 373 | /* The following configurations have working defaults in usbdrv.h. You 374 | * usually don't need to set them explicitly. Only if you want to run 375 | * the driver on a device which is not yet supported or with a compiler 376 | * which is not fully supported (such as IAR C) or if you use a differnt 377 | * interrupt than INT0, you may have to define some of these. 378 | */ 379 | /* #define USB_INTR_CFG MCUCR */ 380 | /* #define USB_INTR_CFG_SET ((1 << ISC00) | (1 << ISC01)) */ 381 | /* #define USB_INTR_CFG_CLR 0 */ 382 | /* #define USB_INTR_ENABLE GIMSK */ 383 | /* #define USB_INTR_ENABLE_BIT INT0 */ 384 | /* #define USB_INTR_PENDING GIFR */ 385 | /* #define USB_INTR_PENDING_BIT INTF0 */ 386 | /* #define USB_INTR_VECTOR INT0_vect */ 387 | 388 | #endif /* __usbconfig_h_included__ */ -------------------------------------------------------------------------------- /lib/blink1.rb: -------------------------------------------------------------------------------- 1 | require 'blink1/blink1' 2 | require 'blink1/version' 3 | 4 | # 5 | # A Ruby interface for blink(1)[http://blink1.thingm.com/]. 6 | # 7 | class Blink1 8 | 9 | # Fade duration in millisecond. 10 | attr_accessor :millis 11 | # Delay to next color in millisecond. 12 | attr_accessor :delay_millis 13 | 14 | # :call-seq: 15 | # new ( {Fixnum} id ) 16 | # new ( {Boolean} auto_open ) 17 | # new ( {String} serial_id ) 18 | # new ( path: device_path ) 19 | # new ( serial: serial_id ) 20 | # 21 | # Returns new instance of +Blink1+ 22 | # 23 | def initialize(option = nil) 24 | case option 25 | when Fixnum 26 | open_by_id(option) 27 | when Hash 28 | path = option[:path] 29 | serial = option[:serial] 30 | 31 | if path 32 | open_by_path(path) 33 | elsif serial 34 | open_by_serial(serial) 35 | end 36 | when String 37 | open_by_serial(option) 38 | else 39 | open if option == true 40 | end 41 | 42 | @millis ||= 300 43 | @delay_millis ||= 500 44 | end 45 | 46 | # 47 | # :call-seq: 48 | # open ( {Fixnum} id ) { |blink1| } 49 | # open ( {Boolean} autoopen ) { |blink1| } 50 | # open ( {String} serial_id ) { |blink1| } 51 | # open ( path: device_path ) { |blink1| } 52 | # open ( serial: serial_id ) { |blink1| } 53 | # 54 | # If block given, yields new instance of +Blink1+. 55 | # 56 | # If not, returns new +Blink1+ 57 | # 58 | def self.open(option = nil, &block) 59 | b = new(option) 60 | b.open if option.nil? 61 | 62 | if block 63 | begin 64 | b.instance_eval(&block) 65 | ensure 66 | b.close 67 | end 68 | else 69 | b 70 | end 71 | end 72 | 73 | # 74 | # Blink with RGB value for +times+. 75 | # 76 | def blink(r, g, b, times) 77 | times.times do 78 | fade_to_rgb(millis, r, g, b) 79 | self.class.sleep(delay_millis) 80 | fade_to_rgb(millis, 0, 0, 0) 81 | self.class.sleep(delay_millis) 82 | end 83 | end 84 | 85 | # 86 | # Flash random color for +times+. 87 | # 88 | def random(times) 89 | times.times do 90 | r = rand(0xff) 91 | g = rand(0xff) 92 | b = rand(0xff) 93 | fade_to_rgb(millis, r, g, b) 94 | self.class.sleep(delay_millis) 95 | end 96 | end 97 | 98 | # 99 | # Turn LED white. 100 | # 101 | def on 102 | fade_to_rgb(millis, 0xff, 0xff, 0xff) 103 | end 104 | 105 | # 106 | # Turn LED off. 107 | # 108 | def off 109 | fade_to_rgb(millis, 0, 0, 0) 110 | end 111 | 112 | # 113 | # Alias for +read_pattern_line+. 114 | # 115 | def [](index) 116 | read_pattern_line(index) 117 | end 118 | 119 | # 120 | # Write pattern line with hash with key +fade_millis+, +r+, +g+, +b+. 121 | # 122 | def []=(index, prop) 123 | fade_millis = prop[:fade_millis] 124 | r = prop[:r] 125 | g = prop[:g] 126 | b = prop[:b] 127 | 128 | write_pattern_line(index, fade_millis, r, g, b) 129 | end 130 | 131 | # 132 | # Returns array of hash with keys +:id+, +:serial+, +:path+ 133 | # 134 | def self.list 135 | count = enumerate_vid_pid(vendor_id, product_id) 136 | i = 0 137 | devs = [] 138 | 139 | while i < count 140 | devs << { 141 | id: i, 142 | serial: cached_serial(i), 143 | path: cached_path(i) 144 | } 145 | i += 1 146 | end 147 | 148 | devs 149 | end 150 | end 151 | -------------------------------------------------------------------------------- /lib/blink1/version.rb: -------------------------------------------------------------------------------- 1 | class Blink1 2 | # Version of this module. 3 | VERSION = "0.0.7" 4 | end 5 | -------------------------------------------------------------------------------- /rb-blink1.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | require "blink1/version" 4 | 5 | Gem::Specification.new do |s| 6 | s.name = "rb-blink1" 7 | s.version = Blink1::VERSION 8 | s.platform = Gem::Platform::RUBY 9 | s.authors = ['Atsushi Nagase'] 10 | s.email = ['a@ngs.io'] 11 | s.homepage = "http://ngs.github.com/rb-blink1" 12 | s.summary = "A Ruby interface for blink(1)" 13 | s.description = "Controls blink(1)" 14 | s.requirements << "libusb, version 1.0 or greater" 15 | 16 | s.rubyforge_project = "blink1" 17 | 18 | s.add_development_dependency 'bundler' 19 | s.add_development_dependency 'guard-rake' 20 | s.add_development_dependency 'guard-rspec' 21 | s.add_development_dependency 'guard-spork' 22 | s.add_development_dependency 'hanna-bootstrap', '>= 0.0.3' 23 | s.add_development_dependency 'rb-fsevent' 24 | s.add_development_dependency 'rdoc' 25 | s.add_development_dependency 'rspec' 26 | s.add_development_dependency 'spork' 27 | 28 | s.files = `git ls-files`.split("\n").reject{|f| f =~ /^(\..+|Gemfile.*|Guardfile|)$/} 29 | s.extensions = ["ext/blink1/extconf.rb"] 30 | s.require_paths = ["lib", "ext"] 31 | end 32 | 33 | -------------------------------------------------------------------------------- /spec/blink1_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Blink1 do 4 | 5 | describe 'native extention methods' do 6 | 7 | describe 'vendor_id' do 8 | subject { Blink1.vendor_id } 9 | it { should be 10168 } 10 | end 11 | 12 | describe 'product_id' do 13 | subject { Blink1.product_id } 14 | it { should be 493 } 15 | end 16 | 17 | end 18 | 19 | context 'class methods', device: true do 20 | 21 | describe 'list' do 22 | subject { Blink1.list } 23 | it { should be_a_kind_of Array } 24 | end 25 | 26 | describe 'random' do 27 | subject { 28 | ret = nil 29 | Blink1.open do|b1| 30 | ret = b1.random 20 31 | end 32 | ret 33 | } 34 | it { should be_a_kind_of Fixnum } 35 | end 36 | 37 | end 38 | 39 | end 40 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'spork' 3 | require 'rspec' 4 | require "codeclimate-test-reporter" 5 | # require 'spork/ext/ruby-debug' 6 | 7 | CodeClimate::TestReporter.start 8 | 9 | Spork.prefork do; end 10 | 11 | Spork.each_run do; end 12 | 13 | require 'blink1' 14 | 15 | RSpec.configure do |config| 16 | if ENV['CI'] 17 | config.filter_run_excluding device: true 18 | end 19 | end 20 | 21 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 22 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 23 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'ext')) 24 | 25 | -------------------------------------------------------------------------------- /wercker.yml: -------------------------------------------------------------------------------- 1 | box: wercker/ruby 2 | # Build definition 3 | build: 4 | # The steps that will be executed on build 5 | steps: 6 | # A step that executes `bundle install` command 7 | - bundle-install 8 | 9 | # A custom script step, name value is used in the UI 10 | # and the code value contains the command that get executed 11 | - script: 12 | name: echo ruby information 13 | code: | 14 | echo "ruby version $(ruby --version) running" 15 | echo "from location $(which ruby)" 16 | echo -p "gem list: $(gem list)" 17 | 18 | # Add more steps here: 19 | #- script: 20 | # name: rspec 21 | # script: bundle exec rspec 22 | 23 | 24 | --------------------------------------------------------------------------------