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