libusb 0.6.0-x86-linux

Sign up to get free protection for your applications and to get access to all the features.
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