hidapi 0.1.4 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +51 -4
- data/lib/hidapi/device.rb +28 -4
- data/lib/hidapi/engine.rb +50 -5
- data/lib/hidapi/errors.rb +4 -0
- data/lib/hidapi/setup_task_helper.rb +7 -2
- data/lib/hidapi/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4ac8b4c330d8100c012e526918bbbb565a263b33
|
4
|
+
data.tar.gz: bfe231f2734ac2ba14a96189399d839444ff6d15
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f812b49152605ce2d9d2d1b504d7e414dee1e25f67d7567ff82d0f51136b39ee098c2d1bccb86d2c49ac8c9242367c9df81e6dfb7b9f3a187e52d40a5dd6e905
|
7
|
+
data.tar.gz: 3d7f09c0fc38b0d19c0fc1fc96a7eed0c4ea57ff7edff4cfe8824a2683c6a0b22715b4502d0afc7c6ad5d8de6c32f8e3756bdefb0b68451af727570d4a3343d2
|
data/README.md
CHANGED
@@ -50,7 +50,6 @@ my_dev.write "\x01\x02\x03\x04\x05"
|
|
50
50
|
input = my_dev.read
|
51
51
|
my_dev.close
|
52
52
|
```
|
53
|
-
|
54
53
|
The `write` method takes data in any of the 3 forms shown above. Individual arguments, an array of arguments, or a string of arguments.
|
55
54
|
Internally the first two are converted into the 3rd form using `pack("C*")`. If you have a custom data set your are sending,
|
56
55
|
such as 16 or 32 bit values, then you will likely want to pack the string yourself to prevent issues.
|
@@ -58,13 +57,61 @@ such as 16 or 32 bit values, then you will likely want to pack the string yourse
|
|
58
57
|
The `read` method returns a packed string from the device. For instance it may return "\x10\x01\x00". Your application
|
59
58
|
needs to know how to handle the values returned.
|
60
59
|
|
60
|
+
There are multiple methods to open a device.
|
61
|
+
```ruby
|
62
|
+
vendor_id = 0x4d4d
|
63
|
+
product_id = 0xc0c0
|
64
|
+
serial_number = '123456'
|
65
|
+
bus_number = 1
|
66
|
+
device_address = 12
|
67
|
+
interface = 0
|
68
|
+
dev_hidapi_path = "/dev/hidapi/my-dev@1-2.3"
|
69
|
+
dev_raw_path = "/dev/bus/usb/001/00c"
|
70
|
+
|
71
|
+
# open with just the vendor and product id. first match is returned.
|
72
|
+
my_dev = HIDAPI::open(vendor_id, product_id)
|
73
|
+
|
74
|
+
# extend that by also including the serial number. first match is returned,
|
75
|
+
# but this time it should definitely be unique
|
76
|
+
my_dev = HIDAPI::open(vendor_id, product_id, serial_number)
|
77
|
+
|
78
|
+
# (linux only) open the device using the hidapi path.
|
79
|
+
my_dev = HIDAPI::open_path(dev_hidapi_path)
|
80
|
+
|
81
|
+
# (linux only) open the device using the raw dev path.
|
82
|
+
my_dev = HIDAPI::open_path(dev_raw_path)
|
83
|
+
|
84
|
+
# open the device using the BUS:ADDRESS:INTERFACE path.
|
85
|
+
# the components of the path must be in hexadecimal.
|
86
|
+
my_dev = HIDAPI::open_path("#{bus_number.to_s(16)}:#{device_address.to_s(16)}:#{interface.to_s(16)}")
|
87
|
+
```
|
88
|
+
|
89
|
+
Because USB is hot-pluggable, you may want to avoid the `open_path` method unless your device is guaranteed to be plugged
|
90
|
+
into the same port all the time. For instance, a device plugged into a port inside the computer case. Devices plugged
|
91
|
+
into external ports, or hubs, are not necessarily good candidates for `open_path` because they can be unplugged and plugged
|
92
|
+
into a different port at any time. A device at "001:00c:00" may be at "001:00b:00" the next time because the user swapped
|
93
|
+
the plug into another port.
|
94
|
+
|
95
|
+
If you will only have one instance of the device plugged in, it is best to use the `open` method with the vendor_id and
|
96
|
+
product_id of the device. If you have multiple instances and they each have unique serial numbers, then you would want
|
97
|
+
to use the `open` method with the vendor_id, product_id, and serial_number. If you have multiple instances with the same
|
98
|
+
serial number (because it is hardcoded into the firmware for example), then you will need to use dedicated ports and
|
99
|
+
`open_path`.
|
100
|
+
|
101
|
+
|
61
102
|
In order to use a USB device in Linux, udev needs to grant access to the user running the application. If run as root,
|
62
103
|
then it should just work. However, you'd be running it as root. A better option is to have udev grant the appropriate permissions.
|
63
104
|
|
64
105
|
In order to use a USB device in OS X, the system needs a kernel extension telling the OS not to map the device to its own
|
65
106
|
HID drivers.
|
66
107
|
|
67
|
-
|
108
|
+
In order to use a USB device in Windows, the system needs the WinUSB driver installed for the device. Please use the
|
109
|
+
[Zadig tool](http://zadig.akeo.ie/) to install the driver for your device. Even then Windows seems quirky with libusb.
|
110
|
+
The biggest problem I have noticed is the inability to close a device without exiting the program. I haven't thoroughly
|
111
|
+
investigated it yet because Windows has not been my primary development environment. I would appreciate any feedback related
|
112
|
+
to this issue.
|
113
|
+
|
114
|
+
The `HIDAPI::SetupTaskHelper` handles Linux and OS X situations. The gem includes a rake task `setup_hid_device` that
|
68
115
|
calls this class. You can also execute the `lib/hidapi/setup_task_helper.rb` file directly. However, in your application,
|
69
116
|
both of these may be too cumbersome. You can create an instance of the SetupTaskHelper class with the appropriate arguments
|
70
117
|
and just run it yourself.
|
@@ -81,8 +128,8 @@ HIDAPI::SetupTaskHelper.new(
|
|
81
128
|
|
82
129
|
This will take the appropriate action on your OS to make the USB device available for use. On linux, it will also add
|
83
130
|
convenient symlinks to the /dev filesystem. For instance, the above setup could give you something like `/dev/hidapi/pico-lcd-graphic@1-4`
|
84
|
-
that points to the correct USB device. The library
|
85
|
-
directory would be a clear indicator that the device has been
|
131
|
+
that points to the correct USB device. The library can use these links to open the device, and the presence of the links in the`/dev/hidapi`
|
132
|
+
directory would be a clear indicator that the device has been recognized and configured.
|
86
133
|
|
87
134
|
|
88
135
|
## Contributing
|
data/lib/hidapi/device.rb
CHANGED
@@ -413,19 +413,42 @@ module HIDAPI
|
|
413
413
|
|
414
414
|
##
|
415
415
|
# Generates a path for a device.
|
416
|
-
def self.make_path(usb_dev, interface)
|
417
|
-
|
418
|
-
|
416
|
+
def self.make_path(usb_dev, interface = 0)
|
417
|
+
if usb_dev.is_a?(Hash)
|
418
|
+
bus = usb_dev[:bus] || usb_dev['bus']
|
419
|
+
address = usb_dev[:device_address] || usb_dev['device_address']
|
420
|
+
else
|
421
|
+
bus = usb_dev.bus_number
|
422
|
+
address = usb_dev.device_address
|
423
|
+
end
|
419
424
|
"#{bus.to_hex(4)}:#{address.to_hex(4)}:#{interface.to_hex(2)}"
|
420
425
|
end
|
421
426
|
|
427
|
+
##
|
428
|
+
# Validates a device path.
|
429
|
+
def self.validate_path(path)
|
430
|
+
match = /(?<BUS>\d+):(?<ADDR>\d+):(?<IFACE>\d+)/.match(path)
|
431
|
+
return nil unless match
|
432
|
+
make_path(
|
433
|
+
{
|
434
|
+
bus: match['BUS'].to_i(16),
|
435
|
+
device_address: match['ADDR'].to_i(16)
|
436
|
+
},
|
437
|
+
match['IFACE'].to_i(16)
|
438
|
+
)
|
439
|
+
end
|
440
|
+
|
422
441
|
|
423
442
|
##
|
424
443
|
# Reads a string descriptor from the USB device.
|
425
444
|
def read_string(index, on_failure = '')
|
426
445
|
begin
|
427
446
|
# does not require an interface, so open from the usb_dev instead of using our open method.
|
428
|
-
data =
|
447
|
+
data = if open?
|
448
|
+
handle.string_descriptor_ascii(index)
|
449
|
+
else
|
450
|
+
usb_device.open { |handle| handle.string_descriptor_ascii(index) }
|
451
|
+
end
|
429
452
|
HIDAPI.debug("read string at index #{index} for device #{path}: #{data.inspect}")
|
430
453
|
data
|
431
454
|
rescue =>e
|
@@ -554,6 +577,7 @@ module HIDAPI
|
|
554
577
|
until shutdown_thread
|
555
578
|
begin
|
556
579
|
context.handle_events 0
|
580
|
+
sleep 0
|
557
581
|
rescue LIBUSB::ERROR_BUSY, LIBUSB::ERROR_TIMEOUT, LIBUSB::ERROR_OVERFLOW, LIBUSB::ERROR_INTERRUPTED => e
|
558
582
|
# non fatal errors.
|
559
583
|
HIDAPI.debug "non-fatal error for read_thread on device #{path}: #{e.inspect}"
|
data/lib/hidapi/engine.rb
CHANGED
@@ -23,6 +23,17 @@ module HIDAPI
|
|
23
23
|
def enumerate(vendor_id = 0, product_id = 0, options = {})
|
24
24
|
raise HIDAPI::HidApiError, 'not initialized' unless @context
|
25
25
|
|
26
|
+
if vendor_id.is_a?(Hash) || (vendor_id.is_a?(String) && options.empty?)
|
27
|
+
options = vendor_id
|
28
|
+
vendor_id = 0
|
29
|
+
product_id = 0
|
30
|
+
end
|
31
|
+
|
32
|
+
if product_id.is_a?(Hash) || (product_id.is_a?(String) && options.empty?)
|
33
|
+
options = product_id
|
34
|
+
product_id = 0
|
35
|
+
end
|
36
|
+
|
26
37
|
if options.is_a?(String) || options.is_a?(Symbol)
|
27
38
|
options = { as: options }
|
28
39
|
end
|
@@ -58,6 +69,11 @@ module HIDAPI
|
|
58
69
|
raise ArgumentError, 'vendor_id must be provided' if vendor_id.to_i == 0
|
59
70
|
raise ArgumentError, 'product_id must be provided' if product_id.to_i == 0
|
60
71
|
|
72
|
+
if serial_number.is_a?(Hash)
|
73
|
+
options = serial_number
|
74
|
+
serial_number = nil
|
75
|
+
end
|
76
|
+
|
61
77
|
klass = (options || {}).delete(:as) || 'HIDAPI::Device'
|
62
78
|
klass = Object.const_get(klass) unless klass == :no_mapping
|
63
79
|
|
@@ -84,21 +100,50 @@ module HIDAPI
|
|
84
100
|
|
85
101
|
##
|
86
102
|
# Opens the first device with the specified vendor_id, product_id, and optionally serial_number.
|
87
|
-
def open(vendor_id, product_id, serial_number = nil)
|
88
|
-
dev = get_device(vendor_id, product_id, serial_number)
|
103
|
+
def open(vendor_id, product_id, serial_number = nil, options = {})
|
104
|
+
dev = get_device(vendor_id, product_id, serial_number, options)
|
89
105
|
dev.open if dev
|
90
106
|
end
|
91
107
|
|
92
108
|
##
|
93
109
|
# Gets the device with the specified path.
|
94
110
|
def get_device_by_path(path, options = {})
|
111
|
+
|
112
|
+
# Our linux setup routine creates convenient /dev/hidapi/* links.
|
113
|
+
# If the user wants to open one of those, we can simple parse the link to generate
|
114
|
+
# the path that the library expects.
|
115
|
+
if File.exist?(path)
|
116
|
+
|
117
|
+
hidapi_regex = /^\/dev\/hidapi\//
|
118
|
+
usb_bus_regex = /^\/dev\/bus\/usb\/(?<BUS>\d+)\/(?<ADDR>\d+)$/
|
119
|
+
|
120
|
+
if hidapi_regex.match(path)
|
121
|
+
path = File.expand_path(File.readlink(path), File.dirname(path))
|
122
|
+
elsif !usb_bus_regex.match(path)
|
123
|
+
raise HIDAPI::DevicePathInvalid, 'Cannot open file paths other than /dev/hidapi/XXX or /dev/bus/usb/XXX/XXX paths.'
|
124
|
+
end
|
125
|
+
|
126
|
+
# path should now be in the form /dev/bus/usb/AAA/BBB
|
127
|
+
match = usb_bus_regex.match(path)
|
128
|
+
|
129
|
+
raise HIDAPI::DevicePathInvalid, "Link target does not appear valid (#{path})." unless match
|
130
|
+
|
131
|
+
interface = (options.delete(:interface) || 0).to_s(16)
|
132
|
+
|
133
|
+
path = HIDAPI::Device.validate_path("#{match['BUS']}:#{match['ADDR']}:#{interface}")
|
134
|
+
end
|
135
|
+
|
136
|
+
valid_path = HIDAPI::Device.validate_path(path)
|
137
|
+
raise HIDAPI::DevicePathInvalid, "Path should be in BUS:ADDRESS:INTERFACE format with each value being in hexadecimal (ie - 0001:01A:00), not #{path}." unless valid_path
|
138
|
+
path = valid_path
|
139
|
+
|
95
140
|
klass = (options || {}).delete(:as) || 'HIDAPI::Device'
|
96
141
|
klass = Object.const_get(klass) unless klass == :no_mapping
|
97
142
|
|
98
143
|
enumerate(as: :no_mapping).each do |usb_dev|
|
99
144
|
usb_dev.settings.each do |intf_desc|
|
100
145
|
if intf_desc.bInterfaceClass == HID_CLASS
|
101
|
-
dev_path = HIDAPI::make_path(usb_dev, intf_desc.bInterfaceNumber)
|
146
|
+
dev_path = HIDAPI::Device.make_path(usb_dev, intf_desc.bInterfaceNumber)
|
102
147
|
if dev_path == path
|
103
148
|
if klass != :no_mapping
|
104
149
|
return klass.new(usb_dev, intf_desc.bInterfaceNumber)
|
@@ -113,8 +158,8 @@ module HIDAPI
|
|
113
158
|
|
114
159
|
##
|
115
160
|
# Opens the device with the specified path.
|
116
|
-
def open_path(path)
|
117
|
-
dev = get_device_by_path(path)
|
161
|
+
def open_path(path, options = {})
|
162
|
+
dev = get_device_by_path(path, options)
|
118
163
|
dev.open if dev
|
119
164
|
end
|
120
165
|
|
data/lib/hidapi/errors.rb
CHANGED
@@ -8,8 +8,8 @@ module HIDAPI
|
|
8
8
|
attr_reader :vendor_id, :product_id, :simple_name, :interface
|
9
9
|
|
10
10
|
def initialize(vendor_id, product_id, simple_name, interface)
|
11
|
-
@vendor_id = vendor_id
|
12
|
-
@product_id = product_id
|
11
|
+
@vendor_id = interpret_id(vendor_id)
|
12
|
+
@product_id = interpret_id(product_id)
|
13
13
|
@simple_name = simple_name.to_s.strip.gsub(' ', '_').gsub(/[^A-Za-z0-9_\-]/, '')
|
14
14
|
@interface = interface.to_s.strip.to_i
|
15
15
|
@temp_dir = Dir.mktmpdir('hidapi_setup')
|
@@ -229,6 +229,11 @@ LABEL="hidapi_rules_end"
|
|
229
229
|
true
|
230
230
|
end
|
231
231
|
|
232
|
+
private
|
233
|
+
|
234
|
+
def interpret_id(id)
|
235
|
+
id.is_a?(Integer) ? id : id.to_s.strip.to_i(16)
|
236
|
+
end
|
232
237
|
end
|
233
238
|
end
|
234
239
|
|
data/lib/hidapi/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hidapi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Beau Barker
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-08-
|
11
|
+
date: 2016-08-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: libusb
|