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.
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