libusb 0.6.0-x86-linux

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.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.travis.yml +17 -0
  4. data/.yardopts +6 -0
  5. data/COPYING +165 -0
  6. data/Gemfile +11 -0
  7. data/History.md +124 -0
  8. data/README.md +159 -0
  9. data/Rakefile +145 -0
  10. data/appveyor.yml +23 -0
  11. data/lib/libusb.rb +58 -0
  12. data/lib/libusb/bos.rb +306 -0
  13. data/lib/libusb/call.rb +446 -0
  14. data/lib/libusb/compat.rb +376 -0
  15. data/lib/libusb/configuration.rb +155 -0
  16. data/lib/libusb/constants.rb +160 -0
  17. data/lib/libusb/context.rb +426 -0
  18. data/lib/libusb/dependencies.rb +7 -0
  19. data/lib/libusb/dev_handle.rb +564 -0
  20. data/lib/libusb/device.rb +365 -0
  21. data/lib/libusb/endpoint.rb +194 -0
  22. data/lib/libusb/eventmachine.rb +183 -0
  23. data/lib/libusb/interface.rb +60 -0
  24. data/lib/libusb/setting.rb +132 -0
  25. data/lib/libusb/ss_companion.rb +69 -0
  26. data/lib/libusb/stdio.rb +25 -0
  27. data/lib/libusb/transfer.rb +377 -0
  28. data/lib/libusb/version_gem.rb +19 -0
  29. data/lib/libusb/version_struct.rb +63 -0
  30. data/libusb.gemspec +30 -0
  31. data/test/test_libusb_bos.rb +118 -0
  32. data/test/test_libusb_bulk_stream_transfer.rb +50 -0
  33. data/test/test_libusb_capability.rb +23 -0
  34. data/test/test_libusb_compat.rb +78 -0
  35. data/test/test_libusb_compat_mass_storage.rb +81 -0
  36. data/test/test_libusb_descriptors.rb +212 -0
  37. data/test/test_libusb_event_machine.rb +118 -0
  38. data/test/test_libusb_gc.rb +37 -0
  39. data/test/test_libusb_hotplug.rb +127 -0
  40. data/test/test_libusb_iso_transfer.rb +50 -0
  41. data/test/test_libusb_mass_storage.rb +268 -0
  42. data/test/test_libusb_mass_storage2.rb +96 -0
  43. data/test/test_libusb_structs.rb +58 -0
  44. data/test/test_libusb_threads.rb +89 -0
  45. data/test/test_libusb_version.rb +40 -0
  46. data/wireshark-usb-sniffer.png +0 -0
  47. metadata +150 -0
@@ -0,0 +1,155 @@
1
+ # This file is part of Libusb for Ruby.
2
+ #
3
+ # Libusb for Ruby is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # Libusb for Ruby is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public License
14
+ # along with Libusb for Ruby. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'libusb/call'
17
+
18
+ module LIBUSB
19
+ class Configuration < FFI::ManagedStruct
20
+ include Comparable
21
+
22
+ layout :bLength, :uint8,
23
+ :bDescriptorType, :uint8,
24
+ :wTotalLength, :uint16,
25
+ :bNumInterfaces, :uint8,
26
+ :bConfigurationValue, :uint8,
27
+ :iConfiguration, :uint8,
28
+ :bmAttributes, :uint8,
29
+ :bMaxPower, :uint8,
30
+ :interface, :pointer,
31
+ :extra, :pointer,
32
+ :extra_length, :int
33
+
34
+ # Size of this descriptor (in bytes).
35
+ def bLength
36
+ self[:bLength]
37
+ end
38
+
39
+ # Descriptor type (0x02)
40
+ def bDescriptorType
41
+ self[:bDescriptorType]
42
+ end
43
+
44
+ # Total length of data returned for this configuration.
45
+ def wTotalLength
46
+ self[:wTotalLength]
47
+ end
48
+
49
+ # Number of interfaces supported by this configuration.
50
+ def bNumInterfaces
51
+ self[:bNumInterfaces]
52
+ end
53
+
54
+ # Identifier value for this configuration.
55
+ def bConfigurationValue
56
+ self[:bConfigurationValue]
57
+ end
58
+
59
+ # Index of string descriptor describing this configuration.
60
+ def iConfiguration
61
+ self[:iConfiguration]
62
+ end
63
+
64
+ # Configuration characteristics.
65
+ #
66
+ # * Bit 7: Reserved, set to 1. (USB 1.0 Bus Powered)
67
+ # * Bit 6: Self Powered
68
+ # * Bit 5: Remote Wakeup
69
+ # * Bit 4..0: Reserved, set to 0.
70
+ #
71
+ # @return [Integer]
72
+ #
73
+ # @see #self_powered?
74
+ # @see #remote_wakeup?
75
+ def bmAttributes
76
+ self[:bmAttributes]
77
+ end
78
+
79
+ # @return [Boolean]
80
+ def self_powered?
81
+ bmAttributes & 0b1000000 != 0
82
+ end
83
+
84
+ # @return [Boolean]
85
+ def remote_wakeup?
86
+ bmAttributes & 0b100000 != 0
87
+ end
88
+
89
+ # Maximum power consumption of the USB device from this bus in this configuration when the device is fully opreation.
90
+ #
91
+ # @return [Integer] Maximum Power Consumption in 2mA units
92
+ def bMaxPower
93
+ self[:bMaxPower]
94
+ end
95
+
96
+ # @deprecated Use {#bMaxPower} instead.
97
+ alias maxPower bMaxPower
98
+
99
+
100
+ # Extra descriptors.
101
+ #
102
+ # @return [String]
103
+ def extra
104
+ return if self[:extra].null?
105
+ self[:extra].read_string(self[:extra_length])
106
+ end
107
+
108
+ def initialize(device, *args)
109
+ @device = device
110
+ super(*args)
111
+ end
112
+
113
+ def self.release(ptr)
114
+ Call.libusb_free_config_descriptor(ptr)
115
+ end
116
+
117
+ # @return [Device] the device this configuration belongs to.
118
+ attr_reader :device
119
+
120
+ def interfaces
121
+ ifs = []
122
+ self[:bNumInterfaces].times do |i|
123
+ ifs << Interface.new(self, self[:interface] + i*Interface.size)
124
+ end
125
+ return ifs
126
+ end
127
+
128
+ def inspect
129
+ attrs = []
130
+ attrs << self.bConfigurationValue.to_s
131
+ attrs << "SelfPowered" if self_powered?
132
+ attrs << "RemoteWakeup" if remote_wakeup?
133
+ desc = self.description
134
+ attrs << desc if desc != '?'
135
+ "\#<#{self.class} #{attrs.join(' ')}>"
136
+ end
137
+
138
+ # Return name of this configuration as String.
139
+ def description
140
+ return @description if defined? @description
141
+ @description = device.try_string_descriptor_ascii(self.iConfiguration)
142
+ end
143
+
144
+ # Return all interface decriptions of the configuration as Array of {Setting}s.
145
+ def settings() self.interfaces.map {|d| d.settings }.flatten end
146
+ # Return all endpoints of all interfaces of the configuration as Array of {Endpoint}s.
147
+ def endpoints() self.settings.map {|d| d.endpoints }.flatten end
148
+
149
+ def <=>(o)
150
+ t = device<=>o.device
151
+ t = bConfigurationValue<=>o.bConfigurationValue if t==0
152
+ t
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,160 @@
1
+ # This file is part of Libusb for Ruby.
2
+ #
3
+ # Libusb for Ruby is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # Libusb for Ruby is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public License
14
+ # along with Libusb for Ruby. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'libusb/call'
17
+
18
+ module LIBUSB
19
+ Call::ClassCodes.to_h.each{|k,v| const_set(k,v) }
20
+ Call::TransferTypes.to_h.each{|k,v| const_set(k,v) }
21
+ Call::StandardRequests.to_h.each{|k,v| const_set(k,v) }
22
+ Call::RequestTypes.to_h.each{|k,v| const_set(k,v) }
23
+ Call::DescriptorTypes.to_h.each{|k,v| const_set(k,v) }
24
+ Call::EndpointDirections.to_h.each{|k,v| const_set(k,v) }
25
+ Call::RequestRecipients.to_h.each{|k,v| const_set(k,v) }
26
+ Call::IsoSyncTypes.to_h.each{|k,v| const_set(k,v) }
27
+ Call::Speeds.to_h.each{|k,v| const_set(k,v) }
28
+ Call::Capabilities.to_h.each{|k,v| const_set(k,v) }
29
+ Call::SupportedSpeeds.to_h.each{|k,v| const_set(k,v) }
30
+ Call::Usb20ExtensionAttributes.to_h.each{|k,v| const_set(k,v) }
31
+ Call::SsUsbDeviceCapabilityAttributes.to_h.each{|k,v| const_set(k,v) }
32
+ Call::BosTypes.to_h.each{|k,v| const_set(k,v) }
33
+ Call::HotplugEvents.to_h.each{|k,v| const_set(k,v) }
34
+ Call::HotplugFlags.to_h.each{|k,v| const_set(k,v) }
35
+
36
+ # Base class of libusb errors
37
+ class Error < RuntimeError
38
+ # The data already transferred before the exception was raised
39
+ # @return [Fixnum] Number of bytes sent for an outgoing transfer
40
+ # @return [String] Received data for an ingoing transfer
41
+ attr_reader :transferred
42
+
43
+ def initialize(msg=nil, transferred=nil)
44
+ super(msg)
45
+ @transferred = transferred
46
+ end
47
+ end
48
+
49
+ # @private
50
+ ErrorClassForResult = {}
51
+
52
+ # define an exception class for each error code
53
+ Call::Errors.to_h.each do |k,v|
54
+ klass = Class.new(Error)
55
+ klass.send(:define_method, :code){ v }
56
+ const_set(k, klass)
57
+ ErrorClassForResult[v] = klass
58
+ end
59
+
60
+ def self.raise_error(res, text)
61
+ klass = ErrorClassForResult[res]
62
+ raise klass, "#{klass} #{text}"
63
+ end
64
+
65
+ CONTROL_SETUP_SIZE = 8
66
+ DT_DEVICE_SIZE = 18
67
+ DT_CONFIG_SIZE = 9
68
+ DT_INTERFACE_SIZE = 9
69
+ DT_ENDPOINT_SIZE = 7
70
+ DT_ENDPOINT_AUDIO_SIZE = 9 # Audio extension
71
+ DT_HUB_NONVAR_SIZE = 7
72
+
73
+ ENDPOINT_ADDRESS_MASK = 0x0f # in bEndpointAddress
74
+ ENDPOINT_DIR_MASK = 0x80
75
+ TRANSFER_TYPE_MASK = 0x03 # in bmAttributes
76
+ ISO_SYNC_TYPE_MASK = 0x0C
77
+ ISO_USAGE_TYPE_MASK = 0x30
78
+
79
+ POLLIN = 1
80
+ POLLOUT = 4
81
+
82
+ # Wildcard matching for hotplug events.
83
+ HOTPLUG_MATCH_ANY = -1
84
+
85
+
86
+ # http://www.usb.org/developers/defined_class
87
+ # @private
88
+ CLASS_CODES = [
89
+ [0x01, nil, nil, "Audio"],
90
+ [0x02, nil, nil, "Comm"],
91
+ [0x03, nil, nil, "HID"],
92
+ [0x05, nil, nil, "Physical"],
93
+ [0x06, 0x01, 0x01, "StillImaging"],
94
+ [0x06, nil, nil, "Image"],
95
+ [0x07, nil, nil, "Printer"],
96
+ [0x08, 0x01, nil, "MassStorage RBC Bulk-Only"],
97
+ [0x08, 0x02, 0x50, "MassStorage ATAPI Bulk-Only"],
98
+ [0x08, 0x03, 0x50, "MassStorage QIC-157 Bulk-Only"],
99
+ [0x08, 0x04, nil, "MassStorage UFI"],
100
+ [0x08, 0x05, 0x50, "MassStorage SFF-8070i Bulk-Only"],
101
+ [0x08, 0x06, 0x50, "MassStorage SCSI Bulk-Only"],
102
+ [0x08, nil, nil, "MassStorage"],
103
+ [0x09, 0x00, 0x00, "Full speed Hub"],
104
+ [0x09, 0x00, 0x01, "Hi-speed Hub with single TT"],
105
+ [0x09, 0x00, 0x02, "Hi-speed Hub with multiple TTs"],
106
+ [0x09, nil, nil, "Hub"],
107
+ [0x0a, nil, nil, "CDC"],
108
+ [0x0b, nil, nil, "SmartCard"],
109
+ [0x0d, 0x00, 0x00, "ContentSecurity"],
110
+ [0x0e, nil, nil, "Video"],
111
+ [0xdc, 0x01, 0x01, "Diagnostic USB2"],
112
+ [0xdc, nil, nil, "Diagnostic"],
113
+ [0xe0, 0x01, 0x01, "Bluetooth"],
114
+ [0xe0, 0x01, 0x02, "UWB"],
115
+ [0xe0, 0x01, 0x03, "RemoteNDIS"],
116
+ [0xe0, 0x02, 0x01, "Host Wire Adapter Control/Data"],
117
+ [0xe0, 0x02, 0x02, "Device Wire Adapter Control/Data"],
118
+ [0xe0, 0x02, 0x03, "Device Wire Adapter Isochronous"],
119
+ [0xe0, nil, nil, "Wireless Controller"],
120
+ [0xef, 0x01, 0x01, "Active Sync"],
121
+ [0xef, 0x01, 0x02, "Palm Sync"],
122
+ [0xef, 0x02, 0x01, "Interface Association Descriptor"],
123
+ [0xef, 0x02, 0x02, "Wire Adapter Multifunction Peripheral"],
124
+ [0xef, 0x03, 0x01, "Cable Based Association Framework"],
125
+ [0xef, nil, nil, "Miscellaneous"],
126
+ [0xfe, 0x01, 0x01, "Device Firmware Upgrade"],
127
+ [0xfe, 0x02, 0x00, "IRDA Bridge"],
128
+ [0xfe, 0x03, 0x00, "USB Test and Measurement"],
129
+ [0xfe, 0x03, 0x01, "USB Test and Measurement (USBTMC USB488)"],
130
+ [0xfe, nil, nil, "Application Specific"],
131
+ [0xff, nil, nil, "Vendor specific"],
132
+ ]
133
+ # @private
134
+ CLASS_CODES_HASH1 = {}
135
+ # @private
136
+ CLASS_CODES_HASH2 = {}
137
+ # @private
138
+ CLASS_CODES_HASH3 = {}
139
+ CLASS_CODES.each {|base_class, sub_class, protocol, desc|
140
+ if protocol
141
+ CLASS_CODES_HASH3[[base_class, sub_class, protocol]] = desc
142
+ elsif sub_class
143
+ CLASS_CODES_HASH2[[base_class, sub_class]] = desc
144
+ else
145
+ CLASS_CODES_HASH1[base_class] = desc
146
+ end
147
+ }
148
+
149
+ def self.dev_string(base_class, sub_class, protocol)
150
+ if desc = CLASS_CODES_HASH3[[base_class, sub_class, protocol]]
151
+ desc
152
+ elsif desc = CLASS_CODES_HASH2[[base_class, sub_class]]
153
+ desc + " (%02x)" % [protocol]
154
+ elsif desc = CLASS_CODES_HASH1[base_class]
155
+ desc + " (%02x,%02x)" % [sub_class, protocol]
156
+ else
157
+ "Unknown(%02x,%02x,%02x)" % [base_class, sub_class, protocol]
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,426 @@
1
+ # This file is part of Libusb for Ruby.
2
+ #
3
+ # Libusb for Ruby is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # Libusb for Ruby is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public License
14
+ # along with Libusb for Ruby. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'libusb/call'
17
+
18
+ module LIBUSB
19
+ # Class representing a libusb session.
20
+ class Context
21
+ class Pollfd
22
+ include Comparable
23
+
24
+ def initialize(fd, events=0)
25
+ @fd, @events = fd, events
26
+ end
27
+
28
+ def <=>(other)
29
+ @fd <=> other.fd
30
+ end
31
+
32
+ # @return [IO] IO object bound to the file descriptor.
33
+ def io
34
+ rio = IO.new @fd
35
+ # autoclose is available in Ruby-1.9+ only
36
+ rio.autoclose = false if rio.respond_to?( :autoclose= )
37
+ rio
38
+ end
39
+
40
+ # @return [Integer] Numeric file descriptor
41
+ attr_reader :fd
42
+
43
+ # @return [Integer] Event flags to poll for
44
+ attr_reader :events
45
+
46
+ # @return [Boolean] True if the file descriptor has to be observed for incoming/readable data
47
+ def pollin?
48
+ @events & POLLIN != 0
49
+ end
50
+
51
+ # @return [Boolean] True if the file descriptor has to be observed for outgoing/writeable data
52
+ def pollout?
53
+ @events & POLLOUT != 0
54
+ end
55
+
56
+ def inspect
57
+ "\#<#{self.class} fd:#{@fd}#{' POLLIN' if pollin?}#{' POLLOUT' if pollout?}>"
58
+ end
59
+ end
60
+
61
+ class CompletionFlag < FFI::Struct
62
+ layout :completed, :int
63
+
64
+ def completed?
65
+ self[:completed] != 0
66
+ end
67
+
68
+ def completed=(flag)
69
+ self[:completed] = flag ? 1 : 0
70
+ end
71
+ end
72
+
73
+ class HotplugCallback < FFI::Struct
74
+ layout :handle, :int
75
+
76
+ attr_reader :context
77
+
78
+ # @private
79
+ def initialize(context, ctx, callbacks)
80
+ super()
81
+ @context = context
82
+ @ctx = ctx
83
+ @callbacks = callbacks
84
+ end
85
+
86
+ # Deregisters the hotplug callback.
87
+ #
88
+ # Deregister a callback from a {Context}. This function is safe to call from within
89
+ # a hotplug callback.
90
+ #
91
+ # Since libusb version 1.0.16.
92
+ def deregister
93
+ Call.libusb_hotplug_deregister_callback(@ctx, self[:handle])
94
+ @callbacks.delete(self[:handle])
95
+ end
96
+ end
97
+
98
+
99
+ # Initialize libusb context.
100
+ def initialize
101
+ m = FFI::MemoryPointer.new :pointer
102
+ res = Call.libusb_init(m)
103
+ LIBUSB.raise_error res, "in libusb_init" if res!=0
104
+ @ctx = m.read_pointer
105
+ @on_pollfd_added = nil
106
+ @on_pollfd_removed = nil
107
+ @hotplug_callbacks = {}
108
+ end
109
+
110
+ # Deinitialize libusb.
111
+ #
112
+ # Should be called after closing all open devices and before your application terminates.
113
+ def exit
114
+ Call.libusb_exit(@ctx)
115
+ end
116
+
117
+ # Set message verbosity.
118
+ #
119
+ # * Level 0: no messages ever printed by the library (default)
120
+ # * Level 1: error messages are printed to stderr
121
+ # * Level 2: warning and error messages are printed to stderr
122
+ # * Level 3: informational messages are printed to stdout, warning and
123
+ # error messages are printed to stderr
124
+ #
125
+ # The default level is 0, which means no messages are ever printed. If you
126
+ # choose to increase the message verbosity level, ensure that your
127
+ # application does not close the stdout/stderr file descriptors.
128
+ #
129
+ # You are advised to set level 3. libusb is conservative with its message
130
+ # logging and most of the time, will only log messages that explain error
131
+ # conditions and other oddities. This will help you debug your software.
132
+ #
133
+ # If the LIBUSB_DEBUG environment variable was set when libusb was
134
+ # initialized, this method does nothing: the message verbosity is
135
+ # fixed to the value in the environment variable.
136
+ #
137
+ # If libusb was compiled without any message logging, this method
138
+ # does nothing: you'll never get any messages.
139
+ #
140
+ # If libusb was compiled with verbose debug message logging, this
141
+ # method does nothing: you'll always get messages from all levels.
142
+ #
143
+ # @param [Fixnum] level debug level to set
144
+ def debug=(level)
145
+ Call.libusb_set_debug(@ctx, level)
146
+ end
147
+
148
+ def device_list
149
+ pppDevs = FFI::MemoryPointer.new :pointer
150
+ size = Call.libusb_get_device_list(@ctx, pppDevs)
151
+ LIBUSB.raise_error size, "in libusb_get_device_list" if size<0
152
+ ppDevs = pppDevs.read_pointer
153
+ pDevs = []
154
+ size.times do |devi|
155
+ pDev = ppDevs.get_pointer(devi*FFI.type_size(:pointer))
156
+ pDevs << Device.new(self, pDev)
157
+ end
158
+ Call.libusb_free_device_list(ppDevs, 1)
159
+ pDevs
160
+ end
161
+ private :device_list
162
+
163
+ # Handle any pending events in blocking mode.
164
+ #
165
+ # This method must be called when libusb is running asynchronous transfers.
166
+ # This gives libusb the opportunity to reap pending transfers,
167
+ # invoke callbacks, etc.
168
+ #
169
+ # If a zero timeout is passed, this function will handle any already-pending
170
+ # events and then immediately return in non-blocking style.
171
+ #
172
+ # If a non-zero timeout is passed and no events are currently pending, this
173
+ # method will block waiting for events to handle up until the specified timeout.
174
+ # If an event arrives or a signal is raised, this method will return early.
175
+ #
176
+ # If the parameter completion_flag is used, then after obtaining the event
177
+ # handling lock this function will return immediately if the flag is set to completed.
178
+ # This allows for race free waiting for the completion of a specific transfer.
179
+ # See source of {Transfer#submit_and_wait} for a use case of completion_flag.
180
+ #
181
+ # @param [Integer, nil] timeout the maximum time (in millseconds) to block waiting for
182
+ # events, or 0 for non-blocking mode
183
+ # @param [Context::CompletionFlag, nil] completion_flag CompletionFlag to check
184
+ #
185
+ # @see interrupt_event_handler
186
+ def handle_events(timeout=nil, completion_flag=nil)
187
+ if completion_flag && !completion_flag.is_a?(Context::CompletionFlag)
188
+ raise ArgumentError, "completion_flag is not a CompletionFlag"
189
+ end
190
+ if timeout
191
+ timeval = Call::Timeval.new
192
+ timeval.in_ms = timeout
193
+ res = if Call.respond_to?(:libusb_handle_events_timeout_completed)
194
+ Call.libusb_handle_events_timeout_completed(@ctx, timeval, completion_flag)
195
+ else
196
+ Call.libusb_handle_events_timeout(@ctx, timeval)
197
+ end
198
+ else
199
+ res = if Call.respond_to?(:libusb_handle_events_completed)
200
+ Call.libusb_handle_events_completed(@ctx, completion_flag )
201
+ else
202
+ Call.libusb_handle_events(@ctx)
203
+ end
204
+ end
205
+ LIBUSB.raise_error res, "in libusb_handle_events" if res<0
206
+ end
207
+
208
+ if Call.respond_to?(:libusb_interrupt_event_handler)
209
+ # Interrupt any active thread that is handling events.
210
+ #
211
+ # This is mainly useful for interrupting a dedicated event handling thread when an application wishes to call {Context#exit}.
212
+ #
213
+ # Available since libusb-1.0.21.
214
+ #
215
+ # @see handle_events
216
+ def interrupt_event_handler
217
+ Call.libusb_interrupt_event_handler(@ctx)
218
+ end
219
+ end
220
+
221
+ # Obtain a list of devices currently attached to the USB system, optionally matching certain criteria.
222
+ #
223
+ # @param [Hash] filter_hash A number of criteria can be defined in key-value pairs.
224
+ # Only devices that equal all given criterions will be returned. If a criterion is
225
+ # not specified or its value is +nil+, any device will match that criterion.
226
+ # The following criteria can be filtered:
227
+ # * <tt>:idVendor</tt>, <tt>:idProduct</tt> (+FixNum+) for matching vendor/product ID,
228
+ # * <tt>:bClass</tt>, <tt>:bSubClass</tt>, <tt>:bProtocol</tt> (+FixNum+) for the device type -
229
+ # Devices using CLASS_PER_INTERFACE will match, if any of the interfaces match.
230
+ # * <tt>:bcdUSB</tt>, <tt>:bcdDevice</tt>, <tt>:bMaxPacketSize0</tt> (+FixNum+) for the
231
+ # USB and device release numbers.
232
+ # Criteria can also specified as Array of several alternative values.
233
+ #
234
+ # @example
235
+ # # Return all devices of vendor 0x0ab1 where idProduct is 3 or 4:
236
+ # context.device idVendor: 0x0ab1, idProduct: [0x0003, 0x0004]
237
+ #
238
+ # @return [Array<LIBUSB::Device>]
239
+ def devices(filter_hash={})
240
+ device_list.select do |dev|
241
+ ( !filter_hash[:bClass] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
242
+ dev.settings.map(&:bInterfaceClass).&([filter_hash[:bClass]].flatten).any? :
243
+ [filter_hash[:bClass]].flatten.include?(dev.bDeviceClass))) &&
244
+ ( !filter_hash[:bSubClass] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
245
+ dev.settings.map(&:bInterfaceSubClass).&([filter_hash[:bSubClass]].flatten).any? :
246
+ [filter_hash[:bSubClass]].flatten.include?(dev.bDeviceSubClass))) &&
247
+ ( !filter_hash[:bProtocol] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
248
+ dev.settings.map(&:bInterfaceProtocol).&([filter_hash[:bProtocol]].flatten).any? :
249
+ [filter_hash[:bProtocol]].flatten.include?(dev.bDeviceProtocol))) &&
250
+ ( !filter_hash[:bMaxPacketSize0] || [filter_hash[:bMaxPacketSize0]].flatten.include?(dev.bMaxPacketSize0) ) &&
251
+ ( !filter_hash[:idVendor] || [filter_hash[:idVendor]].flatten.include?(dev.idVendor) ) &&
252
+ ( !filter_hash[:idProduct] || [filter_hash[:idProduct]].flatten.include?(dev.idProduct) ) &&
253
+ ( !filter_hash[:bcdUSB] || [filter_hash[:bcdUSB]].flatten.include?(dev.bcdUSB) ) &&
254
+ ( !filter_hash[:bcdDevice] || [filter_hash[:bcdDevice]].flatten.include?(dev.bcdDevice) )
255
+ end
256
+ end
257
+
258
+
259
+ # Retrieve a list of file descriptors that should be polled by your main
260
+ # loop as libusb event sources.
261
+ #
262
+ # As file descriptors are a Unix-specific concept, this function is not
263
+ # available on Windows and will always return +nil+.
264
+ #
265
+ # @return [Array<Pollfd>] list of Pollfd objects,
266
+ # +nil+ on error,
267
+ # +nil+ on platforms where the functionality is not available
268
+ def pollfds
269
+ ppPollfds = Call.libusb_get_pollfds(@ctx)
270
+ return nil if ppPollfds.null?
271
+ offs = 0
272
+ pollfds = []
273
+ while !(pPollfd=ppPollfds.get_pointer(offs)).null?
274
+ pollfd = Call::Pollfd.new pPollfd
275
+ pollfds << Pollfd.new(pollfd[:fd], pollfd[:events])
276
+ offs += FFI.type_size :pointer
277
+ end
278
+ if Call.respond_to?(:libusb_free_pollfds)
279
+ Call.libusb_free_pollfds(ppPollfds)
280
+ else
281
+ Stdio.free(ppPollfds)
282
+ end
283
+ pollfds
284
+ end
285
+
286
+ # Determine the next internal timeout that libusb needs to handle.
287
+ #
288
+ # You only need to use this function if you are calling poll() or select() or
289
+ # similar on libusb's file descriptors yourself - you do not need to use it if
290
+ # you are calling {#handle_events} directly.
291
+ #
292
+ # You should call this function in your main loop in order to determine how long
293
+ # to wait for select() or poll() to return results. libusb needs to be called
294
+ # into at this timeout, so you should use it as an upper bound on your select() or
295
+ # poll() call.
296
+ #
297
+ # When the timeout has expired, call into {#handle_events} (perhaps
298
+ # in non-blocking mode) so that libusb can handle the timeout.
299
+ #
300
+ # This function may return zero. If this is the
301
+ # case, it indicates that libusb has a timeout that has already expired so you
302
+ # should call {#handle_events} immediately. A return code
303
+ # of +nil+ indicates that there are no pending timeouts.
304
+ #
305
+ # On some platforms, this function will always returns +nil+ (no pending timeouts).
306
+ # See libusb's notes on time-based events.
307
+ #
308
+ # @return [Float, nil] the timeout in seconds
309
+ def next_timeout
310
+ timeval = Call::Timeval.new
311
+ res = Call.libusb_get_next_timeout @ctx, timeval
312
+ LIBUSB.raise_error res, "in libusb_get_next_timeout" if res<0
313
+ res == 1 ? timeval.in_s : nil
314
+ end
315
+
316
+ # Register a notification block for file descriptor additions.
317
+ #
318
+ # This block will be invoked for every new file descriptor that
319
+ # libusb uses as an event source.
320
+ #
321
+ # Note that file descriptors may have been added even before you register these
322
+ # notifiers (e.g. at {Context#initialize} time).
323
+ #
324
+ # @yieldparam [Pollfd] pollfd The added file descriptor is yielded to the block
325
+ def on_pollfd_added &block
326
+ @on_pollfd_added = proc do |fd, events, _|
327
+ pollfd = Pollfd.new fd, events
328
+ block.call pollfd
329
+ end
330
+ Call.libusb_set_pollfd_notifiers @ctx, @on_pollfd_added, @on_pollfd_removed, nil
331
+ end
332
+
333
+ # Register a notification block for file descriptor removals.
334
+ #
335
+ # This block will be invoked for every removed file descriptor that
336
+ # libusb uses as an event source.
337
+ #
338
+ # Note that the removal notifier may be called during {Context#exit}
339
+ # (e.g. when it is closing file descriptors that were opened and added to the poll
340
+ # set at {Context#initialize} time). If you don't want this, overwrite the notifier
341
+ # immediately before calling {Context#exit}.
342
+ #
343
+ # @yieldparam [Pollfd] pollfd The removed file descriptor is yielded to the block
344
+ def on_pollfd_removed &block
345
+ @on_pollfd_removed = proc do |fd, _|
346
+ pollfd = Pollfd.new fd
347
+ block.call pollfd
348
+ end
349
+ Call.libusb_set_pollfd_notifiers @ctx, @on_pollfd_added, @on_pollfd_removed, nil
350
+ end
351
+
352
+ # Register a hotplug event notification.
353
+ #
354
+ # Register a callback with the {LIBUSB::Context}. The callback will fire
355
+ # when a matching event occurs on a matching device. The callback is armed
356
+ # until either it is deregistered with {HotplugCallback#deregister} or the
357
+ # supplied block returns +:finish+ to indicate it is finished processing events.
358
+ #
359
+ # If the flag {Call::HotplugFlags HOTPLUG_ENUMERATE} is passed the callback will be
360
+ # called with a {Call::HotplugEvents :HOTPLUG_EVENT_DEVICE_ARRIVED} for all devices
361
+ # already plugged into the machine. Note that libusb modifies its internal
362
+ # device list from a separate thread, while calling hotplug callbacks from
363
+ # {#handle_events}, so it is possible for a device to already be present
364
+ # on, or removed from, its internal device list, while the hotplug callbacks
365
+ # still need to be dispatched. This means that when using
366
+ # {Call::HotplugFlags HOTPLUG_ENUMERATE}, your callback may be called twice for the arrival
367
+ # of the same device, once from {#on_hotplug_event} and once
368
+ # from {#handle_events}; and/or your callback may be called for the
369
+ # removal of a device for which an arrived call was never made.
370
+ #
371
+ # Since libusb version 1.0.16.
372
+ #
373
+ # @param [Hash] args
374
+ # @option args [Fixnum,Symbol] :events bitwise or of events that will trigger this callback.
375
+ # Default is +LIBUSB::HOTPLUG_EVENT_DEVICE_ARRIVED|LIBUSB::HOTPLUG_EVENT_DEVICE_LEFT+ .
376
+ # See {Call::HotplugEvents HotplugEvents}
377
+ # @option args [Fixnum,Symbol] :flags hotplug callback flags. Default is 0. See {Call::HotplugFlags HotplugFlags}
378
+ # @option args [Fixnum] :vendor_id the vendor id to match. Default is {HOTPLUG_MATCH_ANY}.
379
+ # @option args [Fixnum] :product_id the product id to match. Default is {HOTPLUG_MATCH_ANY}.
380
+ # @option args [Fixnum] :dev_class the device class to match. Default is {HOTPLUG_MATCH_ANY}.
381
+ # @return [HotplugCallback] The handle to the registered callback.
382
+ #
383
+ # @yieldparam [Device] device the attached or removed {Device} is yielded to the block
384
+ # @yieldparam [Symbol] event a {Call::HotplugEvents HotplugEvents} symbol
385
+ # @yieldreturn [Symbol] +:finish+ to deregister the callback, +:repeat+ to receive additional events
386
+ # @raise [ArgumentError, LIBUSB::Error] in case of failure
387
+ def on_hotplug_event(args={}, &block)
388
+ events = args.delete(:events) || (HOTPLUG_EVENT_DEVICE_ARRIVED | HOTPLUG_EVENT_DEVICE_LEFT)
389
+ flags = args.delete(:flags) || 0
390
+ vendor_id = args.delete(:vendor_id) || HOTPLUG_MATCH_ANY
391
+ product_id = args.delete(:product_id) || HOTPLUG_MATCH_ANY
392
+ dev_class = args.delete(:dev_class) || HOTPLUG_MATCH_ANY
393
+ raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
394
+
395
+ handle = HotplugCallback.new self, @ctx, @hotplug_callbacks
396
+
397
+ block2 = proc do |ctx, pDevice, event, _user_data|
398
+ raise "internal error: unexpected context" unless @ctx==ctx
399
+ dev = Device.new @ctx, pDevice
400
+
401
+ blres = block.call(dev, event)
402
+
403
+ case blres
404
+ when :finish
405
+ 1
406
+ when :repeat
407
+ 0
408
+ else
409
+ raise ArgumentError, "hotplug event handler must return :finish or :repeat"
410
+ end
411
+ end
412
+
413
+ res = Call.libusb_hotplug_register_callback(@ctx,
414
+ events, flags,
415
+ vendor_id, product_id, dev_class,
416
+ block2, nil, handle)
417
+
418
+ LIBUSB.raise_error res, "in libusb_hotplug_register_callback" if res<0
419
+
420
+ # Avoid GC'ing of the block:
421
+ @hotplug_callbacks[handle[:handle]] = block2
422
+
423
+ return handle
424
+ end
425
+ end
426
+ end