├── .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 | {
}[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 |
--------------------------------------------------------------------------------