hidapi 0.1.4 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|