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