dream_cheeky 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in dream_cheeky.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # Dream Cheeky
2
+
3
+ World, the time has come to PUSH THE BUTTON!
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/clean'
3
+ require 'rspec/core/rake_task'
4
+
5
+ NAME = 'dream_cheeky'
6
+
7
+ # rule to build the extension: this says
8
+ # that the extension should be rebuilt
9
+ # after any change to the files in ext
10
+ file "lib/#{NAME}/#{NAME}.bundle" => Dir.glob("ext/#{NAME}/*{.rb,.c}") do
11
+ Dir.chdir("ext/#{NAME}") do
12
+ # this does essentially the same thing
13
+ # as what RubyGems does
14
+ ruby "extconf.rb"
15
+ sh "make"
16
+ end
17
+ cp "ext/#{NAME}/#{NAME}.bundle", "lib/#{NAME}"
18
+ end
19
+
20
+ # make the :test task depend on the shared
21
+ # object, so it will be built automatically
22
+ # before running the tests
23
+ task :rspec => "lib/#{NAME}/#{NAME}.bundle"
24
+
25
+ # use 'rake clean' and 'rake clobber' to
26
+ # easily delete generated files
27
+ CLEAN.include('ext/**/*{.o,.log,.bundle}')
28
+ CLEAN.include('ext/**/Makefile')
29
+ CLOBBER.include('lib/**/*.bundle')
30
+
31
+ RSpec::Core::RakeTask.new(:rspec) do |spec|
32
+ spec.pattern = 'spec/**/*_spec.rb'
33
+ spec.rspec_opts = ['-cfs --backtrace']
34
+ end
35
+
36
+ desc "Run specs"
37
+ task :default => :rspec
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "dream_cheeky/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "dream_cheeky"
7
+ s.version = DreamCheeky::VERSION
8
+ s.authors = ["Derrick Spell"]
9
+ s.email = ["derrick.spell@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = "Ruby Inteface to Dream Cheeky(TM) USB Devices"
12
+ s.description = "Ruby Inteface to Dream Cheeky(TM) USB Devices"
13
+
14
+ s.rubyforge_project = "dream_cheeky"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.extensions = ['ext/dream_cheeky/extconf.rb']
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ # specify any dependencies here; for example:
23
+ s.add_development_dependency "rspec"
24
+ end
@@ -0,0 +1,10 @@
1
+ #include <dream_cheeky.h>
2
+
3
+ VALUE mDreamCheeky;
4
+
5
+ void Init_dream_cheeky(void)
6
+ {
7
+ mDreamCheeky = rb_define_module("DreamCheeky");
8
+
9
+ Init_dream_cheeky_big_red_button();
10
+ }
@@ -0,0 +1,11 @@
1
+ #ifndef RUBY_DREAM_CHEEKY
2
+ #define RUBY_DREAM_CHEEKY
3
+
4
+ #include <ruby.h>;
5
+ #include <hidapi.h>;
6
+
7
+ #include <dream_cheeky_big_red_button.h>;
8
+
9
+ extern VALUE mDreamCheeky;
10
+
11
+ #endif
@@ -0,0 +1,57 @@
1
+ #include <dream_cheeky_big_red_button.h>
2
+
3
+
4
+ static void deallocate(void * handle)
5
+ {
6
+ hid_close((hid_device *)handle);
7
+ }
8
+
9
+ static VALUE allocate(VALUE klass)
10
+ {
11
+ hid_device * handle;
12
+
13
+ handle = hid_open(0x1d34, 0x000d, NULL);
14
+ /*handle = hid_open(0x1e54, 0x2030, NULL); // TypeMatrix*/
15
+ if(!handle) {
16
+ rb_exc_raise(rb_exc_new2(rb_eRuntimeError, "No Big Red Button Detected"));
17
+ }
18
+
19
+ hid_set_nonblocking(handle, 1);
20
+
21
+ return Data_Wrap_Struct(klass, NULL, deallocate, handle);
22
+ }
23
+
24
+ static VALUE get_current_state(VALUE self)
25
+ {
26
+ hid_device * handle;
27
+ int res;
28
+ unsigned char buf[8];
29
+
30
+ Data_Get_Struct(self, hid_device, handle);
31
+
32
+ // Set up the command buffer.
33
+ memset(buf,0x00,sizeof(buf));
34
+ buf[0] = 0x08;
35
+ buf[7] = 0x02;
36
+
37
+ hid_write(handle, buf, 8);
38
+ res = 0;
39
+ while (0 == res) {
40
+ res = hid_read(handle, buf, 8);
41
+ if (res == 0) {
42
+ hid_write(handle, buf, 8);
43
+ usleep(10000);
44
+ }
45
+ }
46
+
47
+ return INT2NUM(buf[0]);
48
+ }
49
+
50
+
51
+ void Init_dream_cheeky_big_red_button()
52
+ {
53
+ VALUE cDreamCheekyBigRedButton = rb_define_class_under(mDreamCheeky, "BigRedButton", rb_cObject);
54
+
55
+ rb_define_alloc_func(cDreamCheekyBigRedButton, allocate);
56
+ rb_define_method(cDreamCheekyBigRedButton, "get_current_state", get_current_state, 0);
57
+ }
@@ -0,0 +1,8 @@
1
+ #ifndef RUBY_DREAM_CHEEKY_BIG_RED_BUTTON
2
+ #define RUBY_DREAM_CHEEKY_BIG_RED_BUTTON
3
+
4
+ #include <dream_cheeky.h>
5
+
6
+ void Init_dream_cheeky_big_red_button();
7
+
8
+ #endif
@@ -0,0 +1,37 @@
1
+ require 'mkmf'
2
+
3
+ $CFLAGS << " " << "-ObjC"
4
+
5
+ unless defined?(have_framework)
6
+ def have_framework(fw, &b)
7
+ checking_for fw do
8
+ src = cpp_include("#{fw}/#{fw}.h") << "\n" "int main(void){return 0;}"
9
+ if try_link(src, opt = "-ObjC -framework #{fw}", &b)
10
+ $defs.push(format("-DHAVE_FRAMEWORK_%s", fw.tr_cpp))
11
+ $LDFLAGS << " " << opt
12
+ true
13
+ else
14
+ false
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ if have_framework('CoreFoundation')
21
+
22
+ $LDFLAGS << " -ObjC -framework IOKit"
23
+
24
+ create_makefile('dream_cheeky/dream_cheeky')
25
+
26
+ # workaround for mkmf.rb in 1.9.2
27
+ if RUBY_VERSION < "1.9.3"
28
+ open("Makefile", "a") do |f|
29
+ f.puts <<-EOS
30
+ .m.o:
31
+ $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $<
32
+ EOS
33
+ end
34
+ end
35
+ else
36
+ abort "Could not include required frameworks..."
37
+ end
@@ -0,0 +1,1150 @@
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 <IOKit/hid/IOHIDManager.h>
26
+ #include <IOKit/hid/IOHIDKeys.h>
27
+ #include <CoreFoundation/CoreFoundation.h>
28
+ #include <wchar.h>
29
+ #include <locale.h>
30
+ #include <pthread.h>
31
+ #include <sys/time.h>
32
+ #include <unistd.h>
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
+ hid_device *next;
123
+ };
124
+
125
+ /* Static list of all the devices open. This way when a device gets
126
+ disconnected, its hid_device structure can be marked as disconnected
127
+ from hid_device_removal_callback(). */
128
+ static hid_device *device_list = NULL;
129
+ static pthread_mutex_t device_list_mutex = PTHREAD_MUTEX_INITIALIZER;
130
+
131
+ static hid_device *new_hid_device(void)
132
+ {
133
+ hid_device *dev = calloc(1, sizeof(hid_device));
134
+ dev->device_handle = NULL;
135
+ dev->blocking = 1;
136
+ dev->uses_numbered_reports = 0;
137
+ dev->disconnected = 0;
138
+ dev->run_loop_mode = NULL;
139
+ dev->run_loop = NULL;
140
+ dev->source = NULL;
141
+ dev->input_report_buf = NULL;
142
+ dev->input_reports = NULL;
143
+ dev->shutdown_thread = 0;
144
+ dev->next = NULL;
145
+
146
+ /* Thread objects */
147
+ pthread_mutex_init(&dev->mutex, NULL);
148
+ pthread_cond_init(&dev->condition, NULL);
149
+ pthread_barrier_init(&dev->barrier, NULL, 2);
150
+ pthread_barrier_init(&dev->shutdown_barrier, NULL, 2);
151
+
152
+ /* Add the new record to the device_list. */
153
+ pthread_mutex_lock(&device_list_mutex);
154
+ if (!device_list)
155
+ device_list = dev;
156
+ else {
157
+ hid_device *d = device_list;
158
+ while (d) {
159
+ if (!d->next) {
160
+ d->next = dev;
161
+ break;
162
+ }
163
+ d = d->next;
164
+ }
165
+ }
166
+ pthread_mutex_unlock(&device_list_mutex);
167
+
168
+ return dev;
169
+ }
170
+
171
+ static void free_hid_device(hid_device *dev)
172
+ {
173
+ if (!dev)
174
+ return;
175
+
176
+ /* Delete any input reports still left over. */
177
+ struct input_report *rpt = dev->input_reports;
178
+ while (rpt) {
179
+ struct input_report *next = rpt->next;
180
+ free(rpt->data);
181
+ free(rpt);
182
+ rpt = next;
183
+ }
184
+
185
+ /* Free the string and the report buffer. The check for NULL
186
+ is necessary here as CFRelease() doesn't handle NULL like
187
+ free() and others do. */
188
+ if (dev->run_loop_mode)
189
+ CFRelease(dev->run_loop_mode);
190
+ if (dev->source)
191
+ CFRelease(dev->source);
192
+ free(dev->input_report_buf);
193
+
194
+ /* Clean up the thread objects */
195
+ pthread_barrier_destroy(&dev->shutdown_barrier);
196
+ pthread_barrier_destroy(&dev->barrier);
197
+ pthread_cond_destroy(&dev->condition);
198
+ pthread_mutex_destroy(&dev->mutex);
199
+
200
+ /* Remove it from the device list. */
201
+ pthread_mutex_lock(&device_list_mutex);
202
+ hid_device *d = device_list;
203
+ if (d == dev) {
204
+ device_list = d->next;
205
+ }
206
+ else {
207
+ while (d) {
208
+ if (d->next == dev) {
209
+ d->next = d->next->next;
210
+ break;
211
+ }
212
+
213
+ d = d->next;
214
+ }
215
+ }
216
+ pthread_mutex_unlock(&device_list_mutex);
217
+
218
+ /* Free the structure itself. */
219
+ free(dev);
220
+ }
221
+
222
+ static IOHIDManagerRef hid_mgr = 0x0;
223
+
224
+
225
+ #if 0
226
+ static void register_error(hid_device *device, const char *op)
227
+ {
228
+
229
+ }
230
+ #endif
231
+
232
+
233
+ static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key)
234
+ {
235
+ CFTypeRef ref;
236
+ int32_t value;
237
+
238
+ ref = IOHIDDeviceGetProperty(device, key);
239
+ if (ref) {
240
+ if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
241
+ CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &value);
242
+ return value;
243
+ }
244
+ }
245
+ return 0;
246
+ }
247
+
248
+ static unsigned short get_vendor_id(IOHIDDeviceRef device)
249
+ {
250
+ return get_int_property(device, CFSTR(kIOHIDVendorIDKey));
251
+ }
252
+
253
+ static unsigned short get_product_id(IOHIDDeviceRef device)
254
+ {
255
+ return get_int_property(device, CFSTR(kIOHIDProductIDKey));
256
+ }
257
+
258
+
259
+ static int32_t get_max_report_length(IOHIDDeviceRef device)
260
+ {
261
+ return get_int_property(device, CFSTR(kIOHIDMaxInputReportSizeKey));
262
+ }
263
+
264
+ static int get_string_property(IOHIDDeviceRef device, CFStringRef prop, wchar_t *buf, size_t len)
265
+ {
266
+ CFStringRef str;
267
+
268
+ if (!len)
269
+ return 0;
270
+
271
+ str = IOHIDDeviceGetProperty(device, prop);
272
+
273
+ buf[0] = 0;
274
+
275
+ if (str) {
276
+ len --;
277
+
278
+ CFIndex str_len = CFStringGetLength(str);
279
+ CFRange range;
280
+ range.location = 0;
281
+ range.length = (str_len > len)? len: str_len;
282
+ CFIndex used_buf_len;
283
+ CFIndex chars_copied;
284
+ chars_copied = CFStringGetBytes(str,
285
+ range,
286
+ kCFStringEncodingUTF32LE,
287
+ (char)'?',
288
+ FALSE,
289
+ (UInt8*)buf,
290
+ len,
291
+ &used_buf_len);
292
+
293
+ buf[chars_copied] = 0;
294
+ return 0;
295
+ }
296
+ else
297
+ return -1;
298
+
299
+ }
300
+
301
+ static int get_string_property_utf8(IOHIDDeviceRef device, CFStringRef prop, char *buf, size_t len)
302
+ {
303
+ CFStringRef str;
304
+ if (!len)
305
+ return 0;
306
+
307
+ str = IOHIDDeviceGetProperty(device, prop);
308
+
309
+ buf[0] = 0;
310
+
311
+ if (str) {
312
+ len--;
313
+
314
+ CFIndex str_len = CFStringGetLength(str);
315
+ CFRange range;
316
+ range.location = 0;
317
+ range.length = (str_len > len)? len: str_len;
318
+ CFIndex used_buf_len;
319
+ CFIndex chars_copied;
320
+ chars_copied = CFStringGetBytes(str,
321
+ range,
322
+ kCFStringEncodingUTF8,
323
+ (char)'?',
324
+ FALSE,
325
+ (UInt8*)buf,
326
+ len,
327
+ &used_buf_len);
328
+
329
+ buf[chars_copied] = 0;
330
+ return used_buf_len;
331
+ }
332
+ else
333
+ return 0;
334
+ }
335
+
336
+
337
+ static int get_serial_number(IOHIDDeviceRef device, wchar_t *buf, size_t len)
338
+ {
339
+ return get_string_property(device, CFSTR(kIOHIDSerialNumberKey), buf, len);
340
+ }
341
+
342
+ static int get_manufacturer_string(IOHIDDeviceRef device, wchar_t *buf, size_t len)
343
+ {
344
+ return get_string_property(device, CFSTR(kIOHIDManufacturerKey), buf, len);
345
+ }
346
+
347
+ static int get_product_string(IOHIDDeviceRef device, wchar_t *buf, size_t len)
348
+ {
349
+ return get_string_property(device, CFSTR(kIOHIDProductKey), buf, len);
350
+ }
351
+
352
+
353
+ /* Implementation of wcsdup() for Mac. */
354
+ static wchar_t *dup_wcs(const wchar_t *s)
355
+ {
356
+ size_t len = wcslen(s);
357
+ wchar_t *ret = malloc((len+1)*sizeof(wchar_t));
358
+ wcscpy(ret, s);
359
+
360
+ return ret;
361
+ }
362
+
363
+
364
+ static int make_path(IOHIDDeviceRef device, char *buf, size_t len)
365
+ {
366
+ int res;
367
+ unsigned short vid, pid;
368
+ char transport[32];
369
+
370
+ buf[0] = '\0';
371
+
372
+ res = get_string_property_utf8(
373
+ device, CFSTR(kIOHIDTransportKey),
374
+ transport, sizeof(transport));
375
+
376
+ if (!res)
377
+ return -1;
378
+
379
+ vid = get_vendor_id(device);
380
+ pid = get_product_id(device);
381
+
382
+ res = snprintf(buf, len, "%s_%04hx_%04hx_%p",
383
+ transport, vid, pid, device);
384
+
385
+
386
+ buf[len-1] = '\0';
387
+ return res+1;
388
+ }
389
+
390
+ /* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */
391
+ static int init_hid_manager(void)
392
+ {
393
+ IOReturn res;
394
+
395
+ /* Initialize all the HID Manager Objects */
396
+ hid_mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
397
+ if (hid_mgr) {
398
+ IOHIDManagerSetDeviceMatching(hid_mgr, NULL);
399
+ IOHIDManagerScheduleWithRunLoop(hid_mgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
400
+ return 0;
401
+ }
402
+
403
+ return -1;
404
+ }
405
+
406
+ /* Initialize the IOHIDManager if necessary. This is the public function, and
407
+ it is safe to call this function repeatedly. Return 0 for success and -1
408
+ for failure. */
409
+ int HID_API_EXPORT hid_init(void)
410
+ {
411
+ if (!hid_mgr) {
412
+ return init_hid_manager();
413
+ }
414
+
415
+ /* Already initialized. */
416
+ return 0;
417
+ }
418
+
419
+ int HID_API_EXPORT hid_exit(void)
420
+ {
421
+ if (hid_mgr) {
422
+ /* Close the HID manager. */
423
+ IOHIDManagerClose(hid_mgr, kIOHIDOptionsTypeNone);
424
+ CFRelease(hid_mgr);
425
+ hid_mgr = NULL;
426
+ }
427
+
428
+ return 0;
429
+ }
430
+
431
+ static void process_pending_events() {
432
+ SInt32 res;
433
+ do {
434
+ res = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, FALSE);
435
+ } while(res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut);
436
+ }
437
+
438
+ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id)
439
+ {
440
+ struct hid_device_info *root = NULL; // return object
441
+ struct hid_device_info *cur_dev = NULL;
442
+ CFIndex num_devices;
443
+ int i;
444
+
445
+ /* Set up the HID Manager if it hasn't been done */
446
+ if (hid_init() < 0)
447
+ return NULL;
448
+
449
+ /* give the IOHIDManager a chance to update itself */
450
+ process_pending_events();
451
+
452
+ /* Get a list of the Devices */
453
+ CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr);
454
+
455
+ /* Convert the list into a C array so we can iterate easily. */
456
+ num_devices = CFSetGetCount(device_set);
457
+ IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef));
458
+ CFSetGetValues(device_set, (const void **) device_array);
459
+
460
+ /* Iterate over each device, making an entry for it. */
461
+ for (i = 0; i < num_devices; i++) {
462
+ unsigned short dev_vid;
463
+ unsigned short dev_pid;
464
+ #define BUF_LEN 256
465
+ wchar_t buf[BUF_LEN];
466
+ char cbuf[BUF_LEN];
467
+
468
+ IOHIDDeviceRef dev = device_array[i];
469
+
470
+ if (!dev) {
471
+ continue;
472
+ }
473
+ dev_vid = get_vendor_id(dev);
474
+ dev_pid = get_product_id(dev);
475
+
476
+ /* Check the VID/PID against the arguments */
477
+ if ((vendor_id == 0x0 && product_id == 0x0) ||
478
+ (vendor_id == dev_vid && product_id == dev_pid)) {
479
+ struct hid_device_info *tmp;
480
+ size_t len;
481
+
482
+ /* VID/PID match. Create the record. */
483
+ tmp = malloc(sizeof(struct hid_device_info));
484
+ if (cur_dev) {
485
+ cur_dev->next = tmp;
486
+ }
487
+ else {
488
+ root = tmp;
489
+ }
490
+ cur_dev = tmp;
491
+
492
+ // Get the Usage Page and Usage for this device.
493
+ cur_dev->usage_page = get_int_property(dev, CFSTR(kIOHIDPrimaryUsagePageKey));
494
+ cur_dev->usage = get_int_property(dev, CFSTR(kIOHIDPrimaryUsageKey));
495
+
496
+ /* Fill out the record */
497
+ cur_dev->next = NULL;
498
+ len = make_path(dev, cbuf, sizeof(cbuf));
499
+ cur_dev->path = strdup(cbuf);
500
+
501
+ /* Serial Number */
502
+ get_serial_number(dev, buf, BUF_LEN);
503
+ cur_dev->serial_number = dup_wcs(buf);
504
+
505
+ /* Manufacturer and Product strings */
506
+ get_manufacturer_string(dev, buf, BUF_LEN);
507
+ cur_dev->manufacturer_string = dup_wcs(buf);
508
+ get_product_string(dev, buf, BUF_LEN);
509
+ cur_dev->product_string = dup_wcs(buf);
510
+
511
+ /* VID/PID */
512
+ cur_dev->vendor_id = dev_vid;
513
+ cur_dev->product_id = dev_pid;
514
+
515
+ /* Release Number */
516
+ cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey));
517
+
518
+ /* Interface Number (Unsupported on Mac)*/
519
+ cur_dev->interface_number = -1;
520
+ }
521
+ }
522
+
523
+ free(device_array);
524
+ CFRelease(device_set);
525
+
526
+ return root;
527
+ }
528
+
529
+ void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs)
530
+ {
531
+ /* This function is identical to the Linux version. Platform independent. */
532
+ struct hid_device_info *d = devs;
533
+ while (d) {
534
+ struct hid_device_info *next = d->next;
535
+ free(d->path);
536
+ free(d->serial_number);
537
+ free(d->manufacturer_string);
538
+ free(d->product_string);
539
+ free(d);
540
+ d = next;
541
+ }
542
+ }
543
+
544
+ hid_device * HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short product_id, wchar_t *serial_number)
545
+ {
546
+ /* This function is identical to the Linux version. Platform independent. */
547
+ struct hid_device_info *devs, *cur_dev;
548
+ const char *path_to_open = NULL;
549
+ hid_device * handle = NULL;
550
+
551
+ devs = hid_enumerate(vendor_id, product_id);
552
+ cur_dev = devs;
553
+ while (cur_dev) {
554
+ if (cur_dev->vendor_id == vendor_id &&
555
+ cur_dev->product_id == product_id) {
556
+ if (serial_number) {
557
+ if (wcscmp(serial_number, cur_dev->serial_number) == 0) {
558
+ path_to_open = cur_dev->path;
559
+ break;
560
+ }
561
+ }
562
+ else {
563
+ path_to_open = cur_dev->path;
564
+ break;
565
+ }
566
+ }
567
+ cur_dev = cur_dev->next;
568
+ }
569
+
570
+ if (path_to_open) {
571
+ /* Open the device */
572
+ handle = hid_open_path(path_to_open);
573
+ }
574
+
575
+ hid_free_enumeration(devs);
576
+
577
+ return handle;
578
+ }
579
+
580
+ static void hid_device_removal_callback(void *context, IOReturn result,
581
+ void *sender, IOHIDDeviceRef dev_ref)
582
+ {
583
+ /* Stop the Run Loop for this device. */
584
+ pthread_mutex_lock(&device_list_mutex);
585
+ hid_device *d = device_list;
586
+ while (d) {
587
+ if (d->device_handle == dev_ref) {
588
+ d->disconnected = 1;
589
+ CFRunLoopStop(d->run_loop);
590
+ }
591
+
592
+ d = d->next;
593
+ }
594
+ pthread_mutex_unlock(&device_list_mutex);
595
+ }
596
+
597
+ /* The Run Loop calls this function for each input report received.
598
+ This function puts the data into a linked list to be picked up by
599
+ hid_read(). */
600
+ static void hid_report_callback(void *context, IOReturn result, void *sender,
601
+ IOHIDReportType report_type, uint32_t report_id,
602
+ uint8_t *report, CFIndex report_length)
603
+ {
604
+ struct input_report *rpt;
605
+ hid_device *dev = context;
606
+
607
+ /* Make a new Input Report object */
608
+ rpt = calloc(1, sizeof(struct input_report));
609
+ rpt->data = calloc(1, report_length);
610
+ memcpy(rpt->data, report, report_length);
611
+ rpt->len = report_length;
612
+ rpt->next = NULL;
613
+
614
+ /* Lock this section */
615
+ pthread_mutex_lock(&dev->mutex);
616
+
617
+ /* Attach the new report object to the end of the list. */
618
+ if (dev->input_reports == NULL) {
619
+ /* The list is empty. Put it at the root. */
620
+ dev->input_reports = rpt;
621
+ }
622
+ else {
623
+ /* Find the end of the list and attach. */
624
+ struct input_report *cur = dev->input_reports;
625
+ int num_queued = 0;
626
+ while (cur->next != NULL) {
627
+ cur = cur->next;
628
+ num_queued++;
629
+ }
630
+ cur->next = rpt;
631
+
632
+ /* Pop one off if we've reached 30 in the queue. This
633
+ way we don't grow forever if the user never reads
634
+ anything from the device. */
635
+ if (num_queued > 30) {
636
+ return_data(dev, NULL, 0);
637
+ }
638
+ }
639
+
640
+ /* Signal a waiting thread that there is data. */
641
+ pthread_cond_signal(&dev->condition);
642
+
643
+ /* Unlock */
644
+ pthread_mutex_unlock(&dev->mutex);
645
+
646
+ }
647
+
648
+ /* This gets called when the read_thred's run loop gets signaled by
649
+ hid_close(), and serves to stop the read_thread's run loop. */
650
+ static void perform_signal_callback(void *context)
651
+ {
652
+ hid_device *dev = context;
653
+ CFRunLoopStop(dev->run_loop); //TODO: CFRunLoopGetCurrent()
654
+ }
655
+
656
+ static void *read_thread(void *param)
657
+ {
658
+ hid_device *dev = param;
659
+
660
+ /* Move the device's run loop to this thread. */
661
+ IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetCurrent(), dev->run_loop_mode);
662
+
663
+ /* Create the RunLoopSource which is used to signal the
664
+ event loop to stop when hid_close() is called. */
665
+ CFRunLoopSourceContext ctx;
666
+ memset(&ctx, 0, sizeof(ctx));
667
+ ctx.version = 0;
668
+ ctx.info = dev;
669
+ ctx.perform = &perform_signal_callback;
670
+ dev->source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0/*order*/, &ctx);
671
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), dev->source, dev->run_loop_mode);
672
+
673
+ /* Store off the Run Loop so it can be stopped from hid_close()
674
+ and on device disconnection. */
675
+ dev->run_loop = CFRunLoopGetCurrent();
676
+
677
+ /* Notify the main thread that the read thread is up and running. */
678
+ pthread_barrier_wait(&dev->barrier);
679
+
680
+ /* Run the Event Loop. CFRunLoopRunInMode() will dispatch HID input
681
+ reports into the hid_report_callback(). */
682
+ SInt32 code;
683
+ while (!dev->shutdown_thread && !dev->disconnected) {
684
+ code = CFRunLoopRunInMode(dev->run_loop_mode, 1000/*sec*/, FALSE);
685
+ /* Return if the device has been disconnected */
686
+ if (code == kCFRunLoopRunFinished) {
687
+ dev->disconnected = 1;
688
+ break;
689
+ }
690
+
691
+
692
+ /* Break if The Run Loop returns Finished or Stopped. */
693
+ if (code != kCFRunLoopRunTimedOut &&
694
+ code != kCFRunLoopRunHandledSource) {
695
+ /* There was some kind of error. Setting
696
+ shutdown seems to make sense, but
697
+ there may be something else more appropriate */
698
+ dev->shutdown_thread = 1;
699
+ break;
700
+ }
701
+ }
702
+
703
+ /* Now that the read thread is stopping, Wake any threads which are
704
+ waiting on data (in hid_read_timeout()). Do this under a mutex to
705
+ make sure that a thread which is about to go to sleep waiting on
706
+ the condition acutally will go to sleep before the condition is
707
+ signaled. */
708
+ pthread_mutex_lock(&dev->mutex);
709
+ pthread_cond_broadcast(&dev->condition);
710
+ pthread_mutex_unlock(&dev->mutex);
711
+
712
+ /* Wait here until hid_close() is called and makes it past
713
+ the call to CFRunLoopWakeUp(). This thread still needs to
714
+ be valid when that function is called on the other thread. */
715
+ pthread_barrier_wait(&dev->shutdown_barrier);
716
+
717
+ return NULL;
718
+ }
719
+
720
+ hid_device * HID_API_EXPORT hid_open_path(const char *path)
721
+ {
722
+ int i;
723
+ hid_device *dev = NULL;
724
+ CFIndex num_devices;
725
+
726
+ dev = new_hid_device();
727
+
728
+ /* Set up the HID Manager if it hasn't been done */
729
+ if (hid_init() < 0)
730
+ return NULL;
731
+
732
+ /* give the IOHIDManager a chance to update itself */
733
+ process_pending_events();
734
+
735
+ CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr);
736
+
737
+ num_devices = CFSetGetCount(device_set);
738
+ IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef));
739
+ CFSetGetValues(device_set, (const void **) device_array);
740
+ for (i = 0; i < num_devices; i++) {
741
+ char cbuf[BUF_LEN];
742
+ size_t len;
743
+ IOHIDDeviceRef os_dev = device_array[i];
744
+
745
+ len = make_path(os_dev, cbuf, sizeof(cbuf));
746
+ if (!strcmp(cbuf, path)) {
747
+ // Matched Paths. Open this Device.
748
+ IOReturn ret = IOHIDDeviceOpen(os_dev, kIOHIDOptionsTypeNone);
749
+ if (ret == kIOReturnSuccess) {
750
+ char str[32];
751
+
752
+ free(device_array);
753
+ CFRelease(device_set);
754
+ dev->device_handle = os_dev;
755
+
756
+ /* Create the buffers for receiving data */
757
+ dev->max_input_report_len = (CFIndex) get_max_report_length(os_dev);
758
+ dev->input_report_buf = calloc(dev->max_input_report_len, sizeof(uint8_t));
759
+
760
+ /* Create the Run Loop Mode for this device.
761
+ printing the reference seems to work. */
762
+ sprintf(str, "HIDAPI_%p", os_dev);
763
+ dev->run_loop_mode =
764
+ CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII);
765
+
766
+ /* Attach the device to a Run Loop */
767
+ IOHIDDeviceRegisterInputReportCallback(
768
+ os_dev, dev->input_report_buf, dev->max_input_report_len,
769
+ &hid_report_callback, dev);
770
+ IOHIDManagerRegisterDeviceRemovalCallback(hid_mgr, hid_device_removal_callback, NULL);
771
+
772
+ /* Start the read thread */
773
+ pthread_create(&dev->thread, NULL, read_thread, dev);
774
+
775
+ /* Wait here for the read thread to be initialized. */
776
+ pthread_barrier_wait(&dev->barrier);
777
+
778
+ return dev;
779
+ }
780
+ else {
781
+ goto return_error;
782
+ }
783
+ }
784
+ }
785
+
786
+ return_error:
787
+ free(device_array);
788
+ CFRelease(device_set);
789
+ free_hid_device(dev);
790
+ return NULL;
791
+ }
792
+
793
+ static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char *data, size_t length)
794
+ {
795
+ const unsigned char *data_to_send;
796
+ size_t length_to_send;
797
+ IOReturn res;
798
+
799
+ /* Return if the device has been disconnected. */
800
+ if (dev->disconnected)
801
+ return -1;
802
+
803
+ if (data[0] == 0x0) {
804
+ /* Not using numbered Reports.
805
+ Don't send the report number. */
806
+ data_to_send = data+1;
807
+ length_to_send = length-1;
808
+ }
809
+ else {
810
+ /* Using numbered Reports.
811
+ Send the Report Number */
812
+ data_to_send = data;
813
+ length_to_send = length;
814
+ }
815
+
816
+ if (!dev->disconnected) {
817
+ res = IOHIDDeviceSetReport(dev->device_handle,
818
+ type,
819
+ data[0], /* Report ID*/
820
+ data_to_send, length_to_send);
821
+
822
+ if (res == kIOReturnSuccess) {
823
+ return length;
824
+ }
825
+ else
826
+ return -1;
827
+ }
828
+
829
+ return -1;
830
+ }
831
+
832
+ int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length)
833
+ {
834
+ return set_report(dev, kIOHIDReportTypeOutput, data, length);
835
+ }
836
+
837
+ /* Helper function, so that this isn't duplicated in hid_read(). */
838
+ static int return_data(hid_device *dev, unsigned char *data, size_t length)
839
+ {
840
+ /* Copy the data out of the linked list item (rpt) into the
841
+ return buffer (data), and delete the liked list item. */
842
+ struct input_report *rpt = dev->input_reports;
843
+ size_t len = (length < rpt->len)? length: rpt->len;
844
+ memcpy(data, rpt->data, len);
845
+ dev->input_reports = rpt->next;
846
+ free(rpt->data);
847
+ free(rpt);
848
+ return len;
849
+ }
850
+
851
+ static int cond_wait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex)
852
+ {
853
+ while (!dev->input_reports) {
854
+ int res = pthread_cond_wait(cond, mutex);
855
+ if (res != 0)
856
+ return res;
857
+
858
+ /* A res of 0 means we may have been signaled or it may
859
+ be a spurious wakeup. Check to see that there's acutally
860
+ data in the queue before returning, and if not, go back
861
+ to sleep. See the pthread_cond_timedwait() man page for
862
+ details. */
863
+
864
+ if (dev->shutdown_thread || dev->disconnected)
865
+ return -1;
866
+ }
867
+
868
+ return 0;
869
+ }
870
+
871
+ static int cond_timedwait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
872
+ {
873
+ while (!dev->input_reports) {
874
+ int res = pthread_cond_timedwait(cond, mutex, abstime);
875
+ if (res != 0)
876
+ return res;
877
+
878
+ /* A res of 0 means we may have been signaled or it may
879
+ be a spurious wakeup. Check to see that there's acutally
880
+ data in the queue before returning, and if not, go back
881
+ to sleep. See the pthread_cond_timedwait() man page for
882
+ details. */
883
+
884
+ if (dev->shutdown_thread || dev->disconnected)
885
+ return -1;
886
+ }
887
+
888
+ return 0;
889
+
890
+ }
891
+
892
+ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
893
+ {
894
+ int bytes_read = -1;
895
+
896
+ /* Lock the access to the report list. */
897
+ pthread_mutex_lock(&dev->mutex);
898
+
899
+ /* There's an input report queued up. Return it. */
900
+ if (dev->input_reports) {
901
+ /* Return the first one */
902
+ bytes_read = return_data(dev, data, length);
903
+ goto ret;
904
+ }
905
+
906
+ /* Return if the device has been disconnected. */
907
+ if (dev->disconnected) {
908
+ bytes_read = -1;
909
+ goto ret;
910
+ }
911
+
912
+ if (dev->shutdown_thread) {
913
+ /* This means the device has been closed (or there
914
+ has been an error. An error code of -1 should
915
+ be returned. */
916
+ bytes_read = -1;
917
+ goto ret;
918
+ }
919
+
920
+ /* There is no data. Go to sleep and wait for data. */
921
+
922
+ if (milliseconds == -1) {
923
+ /* Blocking */
924
+ int res;
925
+ res = cond_wait(dev, &dev->condition, &dev->mutex);
926
+ if (res == 0)
927
+ bytes_read = return_data(dev, data, length);
928
+ else {
929
+ /* There was an error, or a device disconnection. */
930
+ bytes_read = -1;
931
+ }
932
+ }
933
+ else if (milliseconds > 0) {
934
+ /* Non-blocking, but called with timeout. */
935
+ int res;
936
+ struct timespec ts;
937
+ struct timeval tv;
938
+ gettimeofday(&tv, NULL);
939
+ TIMEVAL_TO_TIMESPEC(&tv, &ts);
940
+ ts.tv_sec += milliseconds / 1000;
941
+ ts.tv_nsec += (milliseconds % 1000) * 1000000;
942
+ if (ts.tv_nsec >= 1000000000L) {
943
+ ts.tv_sec++;
944
+ ts.tv_nsec -= 1000000000L;
945
+ }
946
+
947
+ res = cond_timedwait(dev, &dev->condition, &dev->mutex, &ts);
948
+ if (res == 0)
949
+ bytes_read = return_data(dev, data, length);
950
+ else if (res == ETIMEDOUT)
951
+ bytes_read = 0;
952
+ else
953
+ bytes_read = -1;
954
+ }
955
+ else {
956
+ /* Purely non-blocking */
957
+ bytes_read = 0;
958
+ }
959
+
960
+ ret:
961
+ /* Unlock */
962
+ pthread_mutex_unlock(&dev->mutex);
963
+ return bytes_read;
964
+ }
965
+
966
+ int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
967
+ {
968
+ return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0);
969
+ }
970
+
971
+ int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock)
972
+ {
973
+ /* All Nonblocking operation is handled by the library. */
974
+ dev->blocking = !nonblock;
975
+
976
+ return 0;
977
+ }
978
+
979
+ int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
980
+ {
981
+ return set_report(dev, kIOHIDReportTypeFeature, data, length);
982
+ }
983
+
984
+ int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
985
+ {
986
+ CFIndex len = length;
987
+ IOReturn res;
988
+
989
+ /* Return if the device has been unplugged. */
990
+ if (dev->disconnected)
991
+ return -1;
992
+
993
+ res = IOHIDDeviceGetReport(dev->device_handle,
994
+ kIOHIDReportTypeFeature,
995
+ data[0], /* Report ID */
996
+ data, &len);
997
+ if (res == kIOReturnSuccess)
998
+ return len;
999
+ else
1000
+ return -1;
1001
+ }
1002
+
1003
+
1004
+ void HID_API_EXPORT hid_close(hid_device *dev)
1005
+ {
1006
+ if (!dev)
1007
+ return;
1008
+
1009
+ /* Disconnect the report callback before close. */
1010
+ if (!dev->disconnected) {
1011
+ IOHIDDeviceRegisterInputReportCallback(
1012
+ dev->device_handle, dev->input_report_buf, dev->max_input_report_len,
1013
+ NULL, dev);
1014
+ IOHIDManagerRegisterDeviceRemovalCallback(hid_mgr, NULL, dev);
1015
+ IOHIDDeviceUnscheduleFromRunLoop(dev->device_handle, dev->run_loop, dev->run_loop_mode);
1016
+ IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
1017
+ }
1018
+
1019
+ /* Cause read_thread() to stop. */
1020
+ dev->shutdown_thread = 1;
1021
+
1022
+ /* Wake up the run thread's event loop so that the thread can exit. */
1023
+ CFRunLoopSourceSignal(dev->source);
1024
+ CFRunLoopWakeUp(dev->run_loop);
1025
+
1026
+ /* Notify the read thread that it can shut down now. */
1027
+ pthread_barrier_wait(&dev->shutdown_barrier);
1028
+
1029
+ /* Wait for read_thread() to end. */
1030
+ pthread_join(dev->thread, NULL);
1031
+
1032
+ /* Close the OS handle to the device, but only if it's not
1033
+ been unplugged. If it's been unplugged, then calling
1034
+ IOHIDDeviceClose() will crash. */
1035
+ if (!dev->disconnected) {
1036
+ IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeNone);
1037
+ }
1038
+
1039
+ /* Clear out the queue of received reports. */
1040
+ pthread_mutex_lock(&dev->mutex);
1041
+ while (dev->input_reports) {
1042
+ return_data(dev, NULL, 0);
1043
+ }
1044
+ pthread_mutex_unlock(&dev->mutex);
1045
+
1046
+ free_hid_device(dev);
1047
+ }
1048
+
1049
+ int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
1050
+ {
1051
+ return get_manufacturer_string(dev->device_handle, string, maxlen);
1052
+ }
1053
+
1054
+ int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
1055
+ {
1056
+ return get_product_string(dev->device_handle, string, maxlen);
1057
+ }
1058
+
1059
+ int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
1060
+ {
1061
+ return get_serial_number(dev->device_handle, string, maxlen);
1062
+ }
1063
+
1064
+ int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
1065
+ {
1066
+ // TODO:
1067
+
1068
+ return 0;
1069
+ }
1070
+
1071
+
1072
+ HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
1073
+ {
1074
+ // TODO:
1075
+
1076
+ return NULL;
1077
+ }
1078
+
1079
+
1080
+
1081
+
1082
+
1083
+
1084
+ #if 0
1085
+ static int32_t get_location_id(IOHIDDeviceRef device)
1086
+ {
1087
+ return get_int_property(device, CFSTR(kIOHIDLocationIDKey));
1088
+ }
1089
+
1090
+ static int32_t get_usage(IOHIDDeviceRef device)
1091
+ {
1092
+ int32_t res;
1093
+ res = get_int_property(device, CFSTR(kIOHIDDeviceUsageKey));
1094
+ if (!res)
1095
+ res = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey));
1096
+ return res;
1097
+ }
1098
+
1099
+ static int32_t get_usage_page(IOHIDDeviceRef device)
1100
+ {
1101
+ int32_t res;
1102
+ res = get_int_property(device, CFSTR(kIOHIDDeviceUsagePageKey));
1103
+ if (!res)
1104
+ res = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey));
1105
+ return res;
1106
+ }
1107
+
1108
+ static int get_transport(IOHIDDeviceRef device, wchar_t *buf, size_t len)
1109
+ {
1110
+ return get_string_property(device, CFSTR(kIOHIDTransportKey), buf, len);
1111
+ }
1112
+
1113
+
1114
+ int main(void)
1115
+ {
1116
+ IOHIDManagerRef mgr;
1117
+ int i;
1118
+
1119
+ mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
1120
+ IOHIDManagerSetDeviceMatching(mgr, NULL);
1121
+ IOHIDManagerOpen(mgr, kIOHIDOptionsTypeNone);
1122
+
1123
+ CFSetRef device_set = IOHIDManagerCopyDevices(mgr);
1124
+
1125
+ CFIndex num_devices = CFSetGetCount(device_set);
1126
+ IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef));
1127
+ CFSetGetValues(device_set, (const void **) device_array);
1128
+
1129
+ for (i = 0; i < num_devices; i++) {
1130
+ IOHIDDeviceRef dev = device_array[i];
1131
+ printf("Device: %p\n", dev);
1132
+ printf(" %04hx %04hx\n", get_vendor_id(dev), get_product_id(dev));
1133
+
1134
+ wchar_t serial[256], buf[256];
1135
+ char cbuf[256];
1136
+ get_serial_number(dev, serial, 256);
1137
+
1138
+
1139
+ printf(" Serial: %ls\n", serial);
1140
+ printf(" Loc: %ld\n", get_location_id(dev));
1141
+ get_transport(dev, buf, 256);
1142
+ printf(" Trans: %ls\n", buf);
1143
+ make_path(dev, cbuf, 256);
1144
+ printf(" Path: %s\n", cbuf);
1145
+
1146
+ }
1147
+
1148
+ return 0;
1149
+ }
1150
+ #endif