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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 75e7069e09a362e591f7edba9bfa9a7763f5f31a
4
- data.tar.gz: 981e48c8d73d8add81d2728e461a060761ce7bae
3
+ metadata.gz: 4ac8b4c330d8100c012e526918bbbb565a263b33
4
+ data.tar.gz: bfe231f2734ac2ba14a96189399d839444ff6d15
5
5
  SHA512:
6
- metadata.gz: 2f20a27445bf2f1052572a72857f74b0d575c486e2c5018dffd9a5b6547a89f3b47adfc93e00c323af92b66fdd01a266f164315cdee1203a3c4c1b0da75d7ca5
7
- data.tar.gz: 829d91b1b8c1f9a7402bd50a8ab82c167a4738542872a91904659ea936767b056f81dc0d27d676e0b1157f03b3973f828016d7fab9ac108dbc261c18d88e2ee8
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
- The `HIDAPI::SetupTaskHelper` handles both of these situations. The gem includes a rake task `setup_hid_device` that
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 doesn't use them, but the presence of the links in the`/dev/hidapi`
85
- directory would be a clear indicator that the device has been recognizes and configured.
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
@@ -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
- bus = usb_dev.bus_number
418
- address = usb_dev.device_address
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 = usb_device.open { |handle| handle.string_descriptor_ascii(index) }
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}"
@@ -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
 
@@ -15,4 +15,8 @@ module HIDAPI
15
15
  ##
16
16
  # An open device is required for the method called.
17
17
  DeviceNotOpen = Class.new(HidApiError)
18
+
19
+ ##
20
+ # Device path invalid.
21
+ DevicePathInvalid = Class.new(HidApiError)
18
22
  end
@@ -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.to_s.strip.to_i(16)
12
- @product_id = product_id.to_s.strip.to_i(16)
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
 
@@ -1,3 +1,3 @@
1
1
  module HIDAPI
2
- VERSION = '0.1.4'
2
+ VERSION = '0.1.8'
3
3
  end
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
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-09 00:00:00.000000000 Z
11
+ date: 2016-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: libusb