libusb 0.7.0-x64-mingw-ucrt

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.appveyor.yml +33 -0
  3. data/.github/workflows/ci.yml +185 -0
  4. data/.gitignore +9 -0
  5. data/.travis.yml +26 -0
  6. data/.yardopts +6 -0
  7. data/COPYING +165 -0
  8. data/Gemfile +19 -0
  9. data/History.md +193 -0
  10. data/README.md +184 -0
  11. data/Rakefile +79 -0
  12. data/lib/libusb/bos.rb +362 -0
  13. data/lib/libusb/call.rb +622 -0
  14. data/lib/libusb/compat.rb +376 -0
  15. data/lib/libusb/configuration.rb +154 -0
  16. data/lib/libusb/constants.rb +170 -0
  17. data/lib/libusb/context.rb +576 -0
  18. data/lib/libusb/context_reference.rb +38 -0
  19. data/lib/libusb/dependencies.rb +7 -0
  20. data/lib/libusb/dev_handle.rb +574 -0
  21. data/lib/libusb/device.rb +407 -0
  22. data/lib/libusb/endpoint.rb +195 -0
  23. data/lib/libusb/eventmachine.rb +187 -0
  24. data/lib/libusb/gem_helper.rb +151 -0
  25. data/lib/libusb/interface.rb +60 -0
  26. data/lib/libusb/libusb_recipe.rb +29 -0
  27. data/lib/libusb/setting.rb +132 -0
  28. data/lib/libusb/ss_companion.rb +72 -0
  29. data/lib/libusb/stdio.rb +25 -0
  30. data/lib/libusb/transfer.rb +418 -0
  31. data/lib/libusb/version_gem.rb +19 -0
  32. data/lib/libusb/version_struct.rb +63 -0
  33. data/lib/libusb-1.0.dll +0 -0
  34. data/lib/libusb.rb +146 -0
  35. data/libusb.gemspec +28 -0
  36. data/test/test_libusb.rb +42 -0
  37. data/test/test_libusb_bos.rb +140 -0
  38. data/test/test_libusb_bulk_stream_transfer.rb +61 -0
  39. data/test/test_libusb_compat.rb +78 -0
  40. data/test/test_libusb_compat_mass_storage.rb +81 -0
  41. data/test/test_libusb_context.rb +88 -0
  42. data/test/test_libusb_descriptors.rb +245 -0
  43. data/test/test_libusb_event_machine.rb +118 -0
  44. data/test/test_libusb_gc.rb +52 -0
  45. data/test/test_libusb_hotplug.rb +129 -0
  46. data/test/test_libusb_iso_transfer.rb +56 -0
  47. data/test/test_libusb_mass_storage.rb +268 -0
  48. data/test/test_libusb_mass_storage2.rb +96 -0
  49. data/test/test_libusb_structs.rb +87 -0
  50. data/test/test_libusb_threads.rb +89 -0
  51. data/wireshark-usb-sniffer.png +0 -0
  52. 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