libusb 0.7.0-x64-mingw-ucrt
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 +7 -0
- data/.appveyor.yml +33 -0
- data/.github/workflows/ci.yml +185 -0
- data/.gitignore +9 -0
- data/.travis.yml +26 -0
- data/.yardopts +6 -0
- data/COPYING +165 -0
- data/Gemfile +19 -0
- data/History.md +193 -0
- data/README.md +184 -0
- data/Rakefile +79 -0
- data/lib/libusb/bos.rb +362 -0
- data/lib/libusb/call.rb +622 -0
- data/lib/libusb/compat.rb +376 -0
- data/lib/libusb/configuration.rb +154 -0
- data/lib/libusb/constants.rb +170 -0
- data/lib/libusb/context.rb +576 -0
- data/lib/libusb/context_reference.rb +38 -0
- data/lib/libusb/dependencies.rb +7 -0
- data/lib/libusb/dev_handle.rb +574 -0
- data/lib/libusb/device.rb +407 -0
- data/lib/libusb/endpoint.rb +195 -0
- data/lib/libusb/eventmachine.rb +187 -0
- data/lib/libusb/gem_helper.rb +151 -0
- data/lib/libusb/interface.rb +60 -0
- data/lib/libusb/libusb_recipe.rb +29 -0
- data/lib/libusb/setting.rb +132 -0
- data/lib/libusb/ss_companion.rb +72 -0
- data/lib/libusb/stdio.rb +25 -0
- data/lib/libusb/transfer.rb +418 -0
- data/lib/libusb/version_gem.rb +19 -0
- data/lib/libusb/version_struct.rb +63 -0
- data/lib/libusb-1.0.dll +0 -0
- data/lib/libusb.rb +146 -0
- data/libusb.gemspec +28 -0
- data/test/test_libusb.rb +42 -0
- data/test/test_libusb_bos.rb +140 -0
- data/test/test_libusb_bulk_stream_transfer.rb +61 -0
- data/test/test_libusb_compat.rb +78 -0
- data/test/test_libusb_compat_mass_storage.rb +81 -0
- data/test/test_libusb_context.rb +88 -0
- data/test/test_libusb_descriptors.rb +245 -0
- data/test/test_libusb_event_machine.rb +118 -0
- data/test/test_libusb_gc.rb +52 -0
- data/test/test_libusb_hotplug.rb +129 -0
- data/test/test_libusb_iso_transfer.rb +56 -0
- data/test/test_libusb_mass_storage.rb +268 -0
- data/test/test_libusb_mass_storage2.rb +96 -0
- data/test/test_libusb_structs.rb +87 -0
- data/test/test_libusb_threads.rb +89 -0
- data/wireshark-usb-sniffer.png +0 -0
- metadata +112 -0
@@ -0,0 +1,576 @@
|
|
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
|
+
# Initialize libusb context.
|
99
|
+
#
|
100
|
+
# @param [Hash{Call::Options => Object}] options Options are available since libusb-1.0.27
|
101
|
+
# @option options [Integer, Symbol] :OPTION_LOG_LEVEL The log Level as a Integer or Symbol. See Call::LogLevels
|
102
|
+
# @option options [nil] :OPTION_USE_USBDK Enable the use of USBDK driver. Pass a +nil+ as value like so: +OPTION_USE_USBDK: nil+
|
103
|
+
# @option options [nil] :OPTION_NO_DEVICE_DISCOVERY Disable device discovery for use with libusb_wrap_sys_device(). Pass a +nil+ as value like so: +OPTION_NO_DEVICE_DISCOVERY: nil+
|
104
|
+
# @option options [Proc] :OPTION_LOG_CB Set a context related log callback Proc. It is called with parameters (context, level, logstring).
|
105
|
+
def initialize(options={})
|
106
|
+
m = FFI::MemoryPointer.new :pointer
|
107
|
+
if options.empty?
|
108
|
+
res = Call.libusb_init(m)
|
109
|
+
LIBUSB.raise_error res, "in libusb_init" if res!=0
|
110
|
+
else
|
111
|
+
raise ArgumentError, "options require libusb-1.0.27+" unless Call.respond_to?(:libusb_init_context)
|
112
|
+
i_opts = options.size
|
113
|
+
p_opts = FFI::MemoryPointer.new(Call::InitOption, i_opts)
|
114
|
+
options.each_with_index do |(k, v), i|
|
115
|
+
opt = Call::InitOption.new(p_opts + i * Call::InitOption.size)
|
116
|
+
opt[:option] = k
|
117
|
+
|
118
|
+
ffitype, ffival = LIBUSB.send(:option_args_to_ffi, k, Array(v), self)
|
119
|
+
case ffitype
|
120
|
+
when NilClass then nil
|
121
|
+
when :libusb_log_level then
|
122
|
+
opt[:value][:ival] = ffival
|
123
|
+
when :libusb_log_cb then
|
124
|
+
opt[:value][:log_cbval] = ffival
|
125
|
+
else raise ArgumentError, "internal error: unexpected ffitype: #{ffitype.inspect}"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
res = Call.libusb_init_context(m, p_opts, i_opts)
|
129
|
+
LIBUSB.raise_error res, "in libusb_init_context" if res!=0
|
130
|
+
end
|
131
|
+
@ctx = m.read_pointer
|
132
|
+
@on_pollfd_added = nil
|
133
|
+
@on_pollfd_removed = nil
|
134
|
+
@hotplug_callbacks = {}
|
135
|
+
|
136
|
+
def @ctx.free_context(id)
|
137
|
+
@free_context = true
|
138
|
+
if @refs == 0
|
139
|
+
# puts "final libusb_exit #{to_i} #{id} #{caller[0]}"
|
140
|
+
if id # Is Context is about to be garbage collected?
|
141
|
+
# In GC mode there's no way to call the registered collbacks, since they are GC'ed as well.
|
142
|
+
# So disable callbacks now.
|
143
|
+
Call.libusb_set_log_cb(self, nil, LOG_CB_CONTEXT)
|
144
|
+
Call.libusb_set_pollfd_notifiers(self, nil, nil, nil)
|
145
|
+
end
|
146
|
+
Call.libusb_exit(self)
|
147
|
+
@refs = nil
|
148
|
+
end
|
149
|
+
end
|
150
|
+
def @ctx.ref_context
|
151
|
+
@refs += 1
|
152
|
+
# puts "ref_context #{to_i} #{@refs} #{caller[1]}"
|
153
|
+
self
|
154
|
+
end
|
155
|
+
def @ctx.unref_context
|
156
|
+
@refs -= 1
|
157
|
+
# puts "unref_context #{to_i} #{@refs}"
|
158
|
+
raise "more unref_context than ref_context" if @refs < 0
|
159
|
+
free_context(true) if @refs == 0 && @free_context
|
160
|
+
self
|
161
|
+
end
|
162
|
+
def @ctx.setref_context
|
163
|
+
@refs = 0
|
164
|
+
@free_context = false
|
165
|
+
end
|
166
|
+
def @ctx.refs
|
167
|
+
@refs
|
168
|
+
end
|
169
|
+
@ctx.setref_context
|
170
|
+
ObjectSpace.define_finalizer(self, @ctx.method(:free_context))
|
171
|
+
end
|
172
|
+
|
173
|
+
# Deinitialize libusb.
|
174
|
+
#
|
175
|
+
# Should be called after closing all open devices and before your application terminates.
|
176
|
+
def exit
|
177
|
+
raise RemainingReferencesError, "#{@ctx.refs} remaining references to LIBUSB::Context" if @ctx.refs.to_i > 0
|
178
|
+
@ctx.free_context nil
|
179
|
+
end
|
180
|
+
|
181
|
+
# @deprecated Use {Context#set_option} instead using the +:OPTION_LOG_LEVEL+ option.
|
182
|
+
def debug=(level)
|
183
|
+
Call.libusb_set_debug(@ctx, level)
|
184
|
+
end
|
185
|
+
|
186
|
+
private def wrap_log_cb(block, mode)
|
187
|
+
if block
|
188
|
+
cb_proc = proc do |p_ctx, lev, str|
|
189
|
+
ctx = case p_ctx
|
190
|
+
when FFI::Pointer::NULL then nil
|
191
|
+
when @ctx then self
|
192
|
+
else p_ctx.to_i
|
193
|
+
end
|
194
|
+
block.call(ctx, lev, str)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Avoid garbage collection of the proc, since only the function pointer is given to libusb
|
199
|
+
if Call::LogCbMode.to_native(mode, nil) & LOG_CB_GLOBAL != 0
|
200
|
+
@@log_cb_proc = cb_proc
|
201
|
+
end
|
202
|
+
if Call::LogCbMode.to_native(mode, nil) & LOG_CB_CONTEXT != 0
|
203
|
+
@log_cb_proc = cb_proc
|
204
|
+
end
|
205
|
+
cb_proc
|
206
|
+
end
|
207
|
+
|
208
|
+
# Set a context related libusb option from the {Call::Options option list}.
|
209
|
+
#
|
210
|
+
# @param [Symbol, Fixnum] option
|
211
|
+
# @param args Zero or more arguments depending on +option+
|
212
|
+
# @see set_options
|
213
|
+
def set_option(option, *args)
|
214
|
+
if Call.respond_to?(:libusb_set_option)
|
215
|
+
# Available since libusb-1.0.22
|
216
|
+
ffi_args = LIBUSB.send(:option_args_to_ffi, option, args, self)
|
217
|
+
res = Call.libusb_set_option(@ctx, option, *ffi_args)
|
218
|
+
LIBUSB.raise_error res, "in libusb_set_option" if res<0
|
219
|
+
|
220
|
+
else
|
221
|
+
# Fallback to deprecated function, if the gem is linked to an older libusb.
|
222
|
+
|
223
|
+
raise ArgumentError, "unknown option #{option.inspect}" unless [:OPTION_LOG_LEVEL, LIBUSB::OPTION_LOG_LEVEL].include?(option)
|
224
|
+
Call.libusb_set_debug(@ctx, *args)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# Convenience function to set context related options in the libusb library.
|
229
|
+
#
|
230
|
+
# Use this function to configure any number of options within the library.
|
231
|
+
# It takes a Hash the same way as given to {Context.initialize}.
|
232
|
+
# See also {Call::Options option list}.
|
233
|
+
#
|
234
|
+
# @param [Hash{Call::Options => Object}] options Option hash
|
235
|
+
# @see set_option
|
236
|
+
def set_options(options={})
|
237
|
+
options.each do |k, v|
|
238
|
+
set_option(k, *Array(v))
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
if Call.respond_to?(:libusb_set_log_cb)
|
243
|
+
# Set log handler.
|
244
|
+
#
|
245
|
+
# libusb will redirect its log messages to the provided method block.
|
246
|
+
# libusb supports redirection of per context and global log messages.
|
247
|
+
# Log messages sent to the context will be sent to the global log handler too.
|
248
|
+
#
|
249
|
+
# If libusb is compiled without message logging or USE_SYSTEM_LOGGING_FACILITY
|
250
|
+
# is defined then global callback function will never be called.
|
251
|
+
# If ENABLE_DEBUG_LOGGING is defined then per context callback function will
|
252
|
+
# never be called.
|
253
|
+
#
|
254
|
+
# To disable the log callback, execute set_log_cb without a block.
|
255
|
+
#
|
256
|
+
# Available since libusb-1.0.23, LIBUSB_API_VERSION >= 0x01000107
|
257
|
+
#
|
258
|
+
# @param [Symbol, Integer] mode mode of callback function operation.
|
259
|
+
# Several modes can be selected for a single callback function, see Call::LogCbMode for a description.
|
260
|
+
#
|
261
|
+
# @yieldparam [Context, nil] context The context which is related to the log message, or +nil+ if it is a global log message.
|
262
|
+
# @yieldparam [Symbol] level The log level, see Call::LogLevels for a description.
|
263
|
+
# @yieldparam [String] str The log message.
|
264
|
+
#
|
265
|
+
# @see Call::LogCbMode
|
266
|
+
#
|
267
|
+
def set_log_cb(mode, &block)
|
268
|
+
cb_proc = wrap_log_cb(block, mode)
|
269
|
+
Call.libusb_set_log_cb(@ctx, cb_proc, mode)
|
270
|
+
self
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
if Call.respond_to?(:libusb_wrap_sys_device)
|
275
|
+
def wrap_sys_device(dev_io)
|
276
|
+
dev_io = dev_io.is_a?(IO) ? dev_io.fileno : dev_io
|
277
|
+
|
278
|
+
ppHandle = FFI::MemoryPointer.new :pointer
|
279
|
+
res = Call.libusb_wrap_sys_device(@ctx, dev_io, ppHandle)
|
280
|
+
LIBUSB.raise_error res, "in libusb_wrap_sys_device" if res!=0
|
281
|
+
handle = DevHandle.new self, ppHandle.read_pointer
|
282
|
+
return handle unless block_given?
|
283
|
+
begin
|
284
|
+
yield handle
|
285
|
+
ensure
|
286
|
+
handle.close
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def device_list
|
292
|
+
pppDevs = FFI::MemoryPointer.new :pointer
|
293
|
+
size = Call.libusb_get_device_list(@ctx, pppDevs)
|
294
|
+
LIBUSB.raise_error size, "in libusb_get_device_list" if size<0
|
295
|
+
ppDevs = pppDevs.read_pointer
|
296
|
+
pDevs = []
|
297
|
+
size.times do |devi|
|
298
|
+
pDev = ppDevs.get_pointer(devi*FFI.type_size(:pointer))
|
299
|
+
pDevs << Device.new(self, pDev)
|
300
|
+
end
|
301
|
+
Call.libusb_free_device_list(ppDevs, 1)
|
302
|
+
pDevs
|
303
|
+
end
|
304
|
+
private :device_list
|
305
|
+
|
306
|
+
# Handle any pending events in blocking mode.
|
307
|
+
#
|
308
|
+
# This method must be called when libusb is running asynchronous transfers.
|
309
|
+
# This gives libusb the opportunity to reap pending transfers,
|
310
|
+
# invoke callbacks, etc.
|
311
|
+
#
|
312
|
+
# If a zero timeout is passed, this function will handle any already-pending
|
313
|
+
# events and then immediately return in non-blocking style.
|
314
|
+
#
|
315
|
+
# If a non-zero timeout is passed and no events are currently pending, this
|
316
|
+
# method will block waiting for events to handle up until the specified timeout.
|
317
|
+
# If an event arrives or a signal is raised, this method will return early.
|
318
|
+
#
|
319
|
+
# If the parameter completion_flag is used, then after obtaining the event
|
320
|
+
# handling lock this function will return immediately if the flag is set to completed.
|
321
|
+
# This allows for race free waiting for the completion of a specific transfer.
|
322
|
+
# See source of {Transfer#submit_and_wait} for a use case of completion_flag.
|
323
|
+
#
|
324
|
+
# @param [Integer, nil] timeout the maximum time (in millseconds) to block waiting for
|
325
|
+
# events, or 0 for non-blocking mode
|
326
|
+
# @param [Context::CompletionFlag, nil] completion_flag CompletionFlag to check
|
327
|
+
#
|
328
|
+
# @see interrupt_event_handler
|
329
|
+
def handle_events(timeout=nil, completion_flag=nil)
|
330
|
+
if completion_flag && !completion_flag.is_a?(Context::CompletionFlag)
|
331
|
+
raise ArgumentError, "completion_flag is not a CompletionFlag"
|
332
|
+
end
|
333
|
+
if timeout
|
334
|
+
timeval = Call::Timeval.new
|
335
|
+
timeval.in_ms = timeout
|
336
|
+
res = if Call.respond_to?(:libusb_handle_events_timeout_completed)
|
337
|
+
Call.libusb_handle_events_timeout_completed(@ctx, timeval, completion_flag)
|
338
|
+
else
|
339
|
+
Call.libusb_handle_events_timeout(@ctx, timeval)
|
340
|
+
end
|
341
|
+
else
|
342
|
+
res = if Call.respond_to?(:libusb_handle_events_completed)
|
343
|
+
Call.libusb_handle_events_completed(@ctx, completion_flag )
|
344
|
+
else
|
345
|
+
Call.libusb_handle_events(@ctx)
|
346
|
+
end
|
347
|
+
end
|
348
|
+
LIBUSB.raise_error res, "in libusb_handle_events" if res<0
|
349
|
+
end
|
350
|
+
|
351
|
+
if Call.respond_to?(:libusb_interrupt_event_handler)
|
352
|
+
# Interrupt any active thread that is handling events.
|
353
|
+
#
|
354
|
+
# This is mainly useful for interrupting a dedicated event handling thread when an application wishes to call {Context#exit}.
|
355
|
+
#
|
356
|
+
# Available since libusb-1.0.21.
|
357
|
+
#
|
358
|
+
# @see handle_events
|
359
|
+
def interrupt_event_handler
|
360
|
+
Call.libusb_interrupt_event_handler(@ctx)
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
# Obtain a list of devices currently attached to the USB system, optionally matching certain criteria.
|
365
|
+
#
|
366
|
+
# @param [Hash] filter_hash A number of criteria can be defined in key-value pairs.
|
367
|
+
# Only devices that equal all given criterions will be returned. If a criterion is
|
368
|
+
# not specified or its value is +nil+, any device will match that criterion.
|
369
|
+
# The following criteria can be filtered:
|
370
|
+
# * <tt>:idVendor</tt>, <tt>:idProduct</tt> (+FixNum+) for matching vendor/product ID,
|
371
|
+
# * <tt>:bClass</tt>, <tt>:bSubClass</tt>, <tt>:bProtocol</tt> (+FixNum+) for the device type -
|
372
|
+
# Devices using CLASS_PER_INTERFACE will match, if any of the interfaces match.
|
373
|
+
# * <tt>:bcdUSB</tt>, <tt>:bcdDevice</tt>, <tt>:bMaxPacketSize0</tt> (+FixNum+) for the
|
374
|
+
# USB and device release numbers.
|
375
|
+
# Criteria can also specified as Array of several alternative values.
|
376
|
+
#
|
377
|
+
# @example
|
378
|
+
# # Return all devices of vendor 0x0ab1 where idProduct is 3 or 4:
|
379
|
+
# context.device idVendor: 0x0ab1, idProduct: [0x0003, 0x0004]
|
380
|
+
#
|
381
|
+
# @return [Array<LIBUSB::Device>]
|
382
|
+
def devices(filter_hash={})
|
383
|
+
device_list.select do |dev|
|
384
|
+
( !filter_hash[:bClass] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
|
385
|
+
dev.settings.map(&:bInterfaceClass).&([filter_hash[:bClass]].flatten).any? :
|
386
|
+
[filter_hash[:bClass]].flatten.include?(dev.bDeviceClass))) &&
|
387
|
+
( !filter_hash[:bSubClass] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
|
388
|
+
dev.settings.map(&:bInterfaceSubClass).&([filter_hash[:bSubClass]].flatten).any? :
|
389
|
+
[filter_hash[:bSubClass]].flatten.include?(dev.bDeviceSubClass))) &&
|
390
|
+
( !filter_hash[:bProtocol] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
|
391
|
+
dev.settings.map(&:bInterfaceProtocol).&([filter_hash[:bProtocol]].flatten).any? :
|
392
|
+
[filter_hash[:bProtocol]].flatten.include?(dev.bDeviceProtocol))) &&
|
393
|
+
( !filter_hash[:bMaxPacketSize0] || [filter_hash[:bMaxPacketSize0]].flatten.include?(dev.bMaxPacketSize0) ) &&
|
394
|
+
( !filter_hash[:idVendor] || [filter_hash[:idVendor]].flatten.include?(dev.idVendor) ) &&
|
395
|
+
( !filter_hash[:idProduct] || [filter_hash[:idProduct]].flatten.include?(dev.idProduct) ) &&
|
396
|
+
( !filter_hash[:bcdUSB] || [filter_hash[:bcdUSB]].flatten.include?(dev.bcdUSB) ) &&
|
397
|
+
( !filter_hash[:bcdDevice] || [filter_hash[:bcdDevice]].flatten.include?(dev.bcdDevice) )
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
|
402
|
+
# Retrieve a list of file descriptors that should be polled by your main
|
403
|
+
# loop as libusb event sources.
|
404
|
+
#
|
405
|
+
# As file descriptors are a Unix-specific concept, this function is not
|
406
|
+
# available on Windows and will always return +nil+.
|
407
|
+
#
|
408
|
+
# @return [Array<Pollfd>] list of Pollfd objects,
|
409
|
+
# +nil+ on error,
|
410
|
+
# +nil+ on platforms where the functionality is not available
|
411
|
+
def pollfds
|
412
|
+
ppPollfds = Call.libusb_get_pollfds(@ctx)
|
413
|
+
return nil if ppPollfds.null?
|
414
|
+
offs = 0
|
415
|
+
pollfds = []
|
416
|
+
while !(pPollfd=ppPollfds.get_pointer(offs)).null?
|
417
|
+
pollfd = Call::Pollfd.new pPollfd
|
418
|
+
pollfds << Pollfd.new(pollfd[:fd], pollfd[:events])
|
419
|
+
offs += FFI.type_size :pointer
|
420
|
+
end
|
421
|
+
if Call.respond_to?(:libusb_free_pollfds)
|
422
|
+
Call.libusb_free_pollfds(ppPollfds)
|
423
|
+
else
|
424
|
+
Stdio.free(ppPollfds)
|
425
|
+
end
|
426
|
+
pollfds
|
427
|
+
end
|
428
|
+
|
429
|
+
# Determine the next internal timeout that libusb needs to handle.
|
430
|
+
#
|
431
|
+
# You only need to use this function if you are calling poll() or select() or
|
432
|
+
# similar on libusb's file descriptors yourself - you do not need to use it if
|
433
|
+
# you are calling {#handle_events} directly.
|
434
|
+
#
|
435
|
+
# You should call this function in your main loop in order to determine how long
|
436
|
+
# to wait for select() or poll() to return results. libusb needs to be called
|
437
|
+
# into at this timeout, so you should use it as an upper bound on your select() or
|
438
|
+
# poll() call.
|
439
|
+
#
|
440
|
+
# When the timeout has expired, call into {#handle_events} (perhaps
|
441
|
+
# in non-blocking mode) so that libusb can handle the timeout.
|
442
|
+
#
|
443
|
+
# This function may return zero. If this is the
|
444
|
+
# case, it indicates that libusb has a timeout that has already expired so you
|
445
|
+
# should call {#handle_events} immediately. A return code
|
446
|
+
# of +nil+ indicates that there are no pending timeouts.
|
447
|
+
#
|
448
|
+
# On some platforms, this function will always returns +nil+ (no pending timeouts).
|
449
|
+
# See libusb's notes on time-based events.
|
450
|
+
#
|
451
|
+
# @return [Float, nil] the timeout in seconds
|
452
|
+
def next_timeout
|
453
|
+
timeval = Call::Timeval.new
|
454
|
+
res = Call.libusb_get_next_timeout @ctx, timeval
|
455
|
+
LIBUSB.raise_error res, "in libusb_get_next_timeout" if res<0
|
456
|
+
res == 1 ? timeval.in_s : nil
|
457
|
+
end
|
458
|
+
|
459
|
+
# Register a notification block for file descriptor additions.
|
460
|
+
#
|
461
|
+
# This block will be invoked for every new file descriptor that
|
462
|
+
# libusb uses as an event source.
|
463
|
+
#
|
464
|
+
# To disable the notification callback, execute on_pollfd_added without a block.
|
465
|
+
#
|
466
|
+
# Note that file descriptors may have been added even before you register these
|
467
|
+
# notifiers (e.g. at {Context#initialize} time).
|
468
|
+
#
|
469
|
+
# @yieldparam [Pollfd] pollfd The added file descriptor is yielded to the block
|
470
|
+
def on_pollfd_added &block
|
471
|
+
@on_pollfd_added = if block
|
472
|
+
proc do |fd, events, _|
|
473
|
+
pollfd = Pollfd.new fd, events
|
474
|
+
block.call pollfd
|
475
|
+
end
|
476
|
+
end
|
477
|
+
Call.libusb_set_pollfd_notifiers @ctx, @on_pollfd_added, @on_pollfd_removed, nil
|
478
|
+
end
|
479
|
+
|
480
|
+
# Register a notification block for file descriptor removals.
|
481
|
+
#
|
482
|
+
# This block will be invoked for every removed file descriptor that
|
483
|
+
# libusb uses as an event source.
|
484
|
+
#
|
485
|
+
# To disable the notification callback, execute on_pollfd_removed without a block.
|
486
|
+
#
|
487
|
+
# Note that the removal notifier may be called during {Context#exit}
|
488
|
+
# (e.g. when it is closing file descriptors that were opened and added to the poll
|
489
|
+
# set at {Context#initialize} time). If you don't want this, overwrite the notifier
|
490
|
+
# immediately before calling {Context#exit}.
|
491
|
+
#
|
492
|
+
# @yieldparam [Pollfd] pollfd The removed file descriptor is yielded to the block
|
493
|
+
def on_pollfd_removed &block
|
494
|
+
@on_pollfd_removed = if block
|
495
|
+
proc do |fd, _|
|
496
|
+
pollfd = Pollfd.new fd
|
497
|
+
block.call pollfd
|
498
|
+
end
|
499
|
+
end
|
500
|
+
Call.libusb_set_pollfd_notifiers @ctx, @on_pollfd_added, @on_pollfd_removed, nil
|
501
|
+
end
|
502
|
+
|
503
|
+
# Register a hotplug event notification.
|
504
|
+
#
|
505
|
+
# Register a callback with the {LIBUSB::Context}. The callback will fire
|
506
|
+
# when a matching event occurs on a matching device. The callback is armed
|
507
|
+
# until either it is deregistered with {HotplugCallback#deregister} or the
|
508
|
+
# supplied block returns +:finish+ to indicate it is finished processing events.
|
509
|
+
#
|
510
|
+
# If the flag {Call::HotplugFlags HOTPLUG_ENUMERATE} is passed the callback will be
|
511
|
+
# called with a {Call::HotplugEvents :HOTPLUG_EVENT_DEVICE_ARRIVED} for all devices
|
512
|
+
# already plugged into the machine. Note that libusb modifies its internal
|
513
|
+
# device list from a separate thread, while calling hotplug callbacks from
|
514
|
+
# {#handle_events}, so it is possible for a device to already be present
|
515
|
+
# on, or removed from, its internal device list, while the hotplug callbacks
|
516
|
+
# still need to be dispatched. This means that when using
|
517
|
+
# {Call::HotplugFlags HOTPLUG_ENUMERATE}, your callback may be called twice for the arrival
|
518
|
+
# of the same device, once from {#on_hotplug_event} and once
|
519
|
+
# from {#handle_events}; and/or your callback may be called for the
|
520
|
+
# removal of a device for which an arrived call was never made.
|
521
|
+
#
|
522
|
+
# Since libusb version 1.0.16.
|
523
|
+
#
|
524
|
+
# @param [Hash] args
|
525
|
+
# @option args [Fixnum,Symbol] :events bitwise or of events that will trigger this callback.
|
526
|
+
# Default is +LIBUSB::HOTPLUG_EVENT_DEVICE_ARRIVED|LIBUSB::HOTPLUG_EVENT_DEVICE_LEFT+ .
|
527
|
+
# See {Call::HotplugEvents HotplugEvents}
|
528
|
+
# @option args [Fixnum,Symbol] :flags hotplug callback flags. Default is 0. See {Call::HotplugFlags HotplugFlags}
|
529
|
+
# @option args [Fixnum] :vendor_id the vendor id to match. Default is {HOTPLUG_MATCH_ANY}.
|
530
|
+
# @option args [Fixnum] :product_id the product id to match. Default is {HOTPLUG_MATCH_ANY}.
|
531
|
+
# @option args [Fixnum] :dev_class the device class to match. Default is {HOTPLUG_MATCH_ANY}.
|
532
|
+
# @return [HotplugCallback] The handle to the registered callback.
|
533
|
+
#
|
534
|
+
# @yieldparam [Device] device the attached or removed {Device} is yielded to the block
|
535
|
+
# @yieldparam [Symbol] event a {Call::HotplugEvents HotplugEvents} symbol
|
536
|
+
# @yieldreturn [Symbol] +:finish+ to deregister the callback, +:repeat+ to receive additional events
|
537
|
+
# @raise [ArgumentError, LIBUSB::Error] in case of failure
|
538
|
+
def on_hotplug_event(events: HOTPLUG_EVENT_DEVICE_ARRIVED | HOTPLUG_EVENT_DEVICE_LEFT,
|
539
|
+
flags: 0,
|
540
|
+
vendor_id: HOTPLUG_MATCH_ANY,
|
541
|
+
product_id: HOTPLUG_MATCH_ANY,
|
542
|
+
dev_class: HOTPLUG_MATCH_ANY,
|
543
|
+
&block)
|
544
|
+
|
545
|
+
handle = HotplugCallback.new self, @ctx, @hotplug_callbacks
|
546
|
+
|
547
|
+
block2 = proc do |ctx, pDevice, event, _user_data|
|
548
|
+
raise "internal error: unexpected context" unless @ctx==ctx
|
549
|
+
dev = Device.new self, pDevice
|
550
|
+
|
551
|
+
blres = block.call(dev, event)
|
552
|
+
|
553
|
+
case blres
|
554
|
+
when :finish
|
555
|
+
1
|
556
|
+
when :repeat
|
557
|
+
0
|
558
|
+
else
|
559
|
+
raise ArgumentError, "hotplug event handler must return :finish or :repeat"
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
res = Call.libusb_hotplug_register_callback(@ctx,
|
564
|
+
events, flags,
|
565
|
+
vendor_id, product_id, dev_class,
|
566
|
+
block2, nil, handle)
|
567
|
+
|
568
|
+
LIBUSB.raise_error res, "in libusb_hotplug_register_callback" if res<0
|
569
|
+
|
570
|
+
# Avoid GC'ing of the block:
|
571
|
+
@hotplug_callbacks[handle[:handle]] = block2
|
572
|
+
|
573
|
+
return handle
|
574
|
+
end
|
575
|
+
end
|
576
|
+
end
|
@@ -0,0 +1,38 @@
|
|
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
|
+
module LIBUSB
|
17
|
+
module ContextReference
|
18
|
+
def register_context(ctx, free_sym)
|
19
|
+
ptr = pointer
|
20
|
+
def ptr.free_struct(id)
|
21
|
+
return unless @ctx
|
22
|
+
Call.send(@free_sym, self)
|
23
|
+
@ctx.unref_context
|
24
|
+
end
|
25
|
+
ptr.instance_variable_set(:@free_sym, free_sym)
|
26
|
+
ptr.instance_variable_set(:@ctx, ctx.ref_context)
|
27
|
+
ObjectSpace.define_finalizer(self, ptr.method(:free_struct))
|
28
|
+
end
|
29
|
+
|
30
|
+
def free
|
31
|
+
ptr = pointer
|
32
|
+
ptr.free_struct nil
|
33
|
+
ptr.instance_variable_set(:@ctx, nil)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private_constant :ContextReference
|
38
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
module LIBUSB
|
2
|
+
LIBUSB_VERSION = ENV['LIBUSB_VERSION'] || '1.0.27'
|
3
|
+
LIBUSB_SOURCE_URI = "https://github.com/libusb/libusb/releases/download/v#{LIBUSB_VERSION}/libusb-#{LIBUSB_VERSION}.tar.bz2"
|
4
|
+
LIBUSB_SOURCE_SHA256 = 'ffaa41d741a8a3bee244ac8e54a72ea05bf2879663c098c82fc5757853441575'
|
5
|
+
|
6
|
+
MINI_PORTILE_VERSION = '~> 2.1'
|
7
|
+
end
|