Class: LIBUSB::Context
- Inherits:
-
Object
- Object
- LIBUSB::Context
- Defined in:
- lib/libusb/context.rb,
lib/libusb/eventmachine.rb
Overview
Class representing a libusb session.
Defined Under Namespace
Classes: CompletionFlag, EMPollfdHandler, HotplugCallback, Pollfd
Class Method Summary collapse
- .free_context(id) ⇒ Object
- .ref_context ⇒ Object
- .refs ⇒ Object
- .setref_context ⇒ Object
- .unref_context ⇒ Object
Instance Method Summary collapse
-
#debug=(level) ⇒ Object
deprecated
Deprecated.
Use #set_option instead using the
:OPTION_LOG_LEVEL
option. -
#devices(filter_hash = {}) ⇒ Array<LIBUSB::Device>
Obtain a list of devices currently attached to the USB system, optionally matching certain criteria.
-
#eventmachine_register ⇒ Object
Register libusb’s file descriptors and timeouts to EventMachine.
- #eventmachine_unregister ⇒ Object
-
#exit ⇒ Object
Deinitialize libusb.
-
#handle_events(timeout = nil, completion_flag = nil) ⇒ Object
Handle any pending events in blocking mode.
-
#initialize(options = {}) ⇒ Context
constructor
Initialize libusb context.
-
#interrupt_event_handler ⇒ Object
Interrupt any active thread that is handling events.
-
#next_timeout ⇒ Float?
Determine the next internal timeout that libusb needs to handle.
-
#on_hotplug_event(events: HOTPLUG_EVENT_DEVICE_ARRIVED | HOTPLUG_EVENT_DEVICE_LEFT, flags: 0, vendor_id: HOTPLUG_MATCH_ANY, product_id: HOTPLUG_MATCH_ANY, dev_class: HOTPLUG_MATCH_ANY) {|device, event| ... } ⇒ HotplugCallback
Register a hotplug event notification.
-
#on_pollfd_added {|pollfd| ... } ⇒ Object
Register a notification block for file descriptor additions.
-
#on_pollfd_removed {|pollfd| ... } ⇒ Object
Register a notification block for file descriptor removals.
-
#pollfds ⇒ Array<Pollfd>
Retrieve a list of file descriptors that should be polled by your main loop as libusb event sources.
-
#set_log_cb(mode) {|context, level, str| ... } ⇒ Object
Set log handler.
-
#set_option(option, *args) ⇒ Object
Set a context related libusb option from the option list.
-
#set_options(options = {}) ⇒ Object
Convenience function to set context related options in the libusb library.
- #wrap_sys_device(dev_io) ⇒ Object
Constructor Details
#initialize(options = {}) ⇒ Context
Initialize libusb context.
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/libusb/context.rb', line 105 def initialize(={}) m = FFI::MemoryPointer.new :pointer if .empty? res = Call.libusb_init(m) LIBUSB.raise_error res, "in libusb_init" if res!=0 else raise ArgumentError, "options require libusb-1.0.27+" unless Call.respond_to?(:libusb_init_context) i_opts = .size p_opts = FFI::MemoryPointer.new(Call::InitOption, i_opts) .each_with_index do |(k, v), i| opt = Call::InitOption.new(p_opts + i * Call::InitOption.size) opt[:option] = k ffitype, ffival = LIBUSB.send(:option_args_to_ffi, k, Array(v), self) case ffitype when NilClass then nil when :libusb_log_level then opt[:value][:ival] = ffival when :libusb_log_cb then opt[:value][:log_cbval] = ffival else raise ArgumentError, "internal error: unexpected ffitype: #{ffitype.inspect}" end end res = Call.libusb_init_context(m, p_opts, i_opts) LIBUSB.raise_error res, "in libusb_init_context" if res!=0 end @ctx = m.read_pointer @on_pollfd_added = nil @on_pollfd_removed = nil @hotplug_callbacks = {} def @ctx.free_context(id) @free_context = true if @refs == 0 # puts "final libusb_exit #{to_i} #{id} #{caller[0]}" if id # Is Context is about to be garbage collected? # In GC mode there's no way to call the registered collbacks, since they are GC'ed as well. # So disable callbacks now. if Call.respond_to?(:libusb_set_log_cb) Call.libusb_set_log_cb(self, nil, LOG_CB_CONTEXT) end Call.libusb_set_pollfd_notifiers(self, nil, nil, nil) end Call.libusb_exit(self) @refs = nil end end def @ctx.ref_context @refs += 1 # puts "ref_context #{to_i} #{@refs} #{caller[1]}" self end def @ctx.unref_context @refs -= 1 # puts "unref_context #{to_i} #{@refs}" raise "more unref_context than ref_context" if @refs < 0 free_context(true) if @refs == 0 && @free_context self end def @ctx.setref_context @refs = 0 @free_context = false end def @ctx.refs @refs end @ctx.setref_context ObjectSpace.define_finalizer(self, @ctx.method(:free_context)) end |
Class Method Details
.free_context(id) ⇒ Object
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/libusb/context.rb', line 136 def @ctx.free_context(id) @free_context = true if @refs == 0 # puts "final libusb_exit #{to_i} #{id} #{caller[0]}" if id # Is Context is about to be garbage collected? # In GC mode there's no way to call the registered collbacks, since they are GC'ed as well. # So disable callbacks now. if Call.respond_to?(:libusb_set_log_cb) Call.libusb_set_log_cb(self, nil, LOG_CB_CONTEXT) end Call.libusb_set_pollfd_notifiers(self, nil, nil, nil) end Call.libusb_exit(self) @refs = nil end end |
.ref_context ⇒ Object
152 153 154 155 156 |
# File 'lib/libusb/context.rb', line 152 def @ctx.ref_context @refs += 1 # puts "ref_context #{to_i} #{@refs} #{caller[1]}" self end |
.refs ⇒ Object
168 169 170 |
# File 'lib/libusb/context.rb', line 168 def @ctx.refs @refs end |
.setref_context ⇒ Object
164 165 166 167 |
# File 'lib/libusb/context.rb', line 164 def @ctx.setref_context @refs = 0 @free_context = false end |
.unref_context ⇒ Object
157 158 159 160 161 162 163 |
# File 'lib/libusb/context.rb', line 157 def @ctx.unref_context @refs -= 1 # puts "unref_context #{to_i} #{@refs}" raise "more unref_context than ref_context" if @refs < 0 free_context(true) if @refs == 0 && @free_context self end |
Instance Method Details
#debug=(level) ⇒ Object
Use #set_option instead using the :OPTION_LOG_LEVEL
option.
184 185 186 |
# File 'lib/libusb/context.rb', line 184 def debug=(level) Call.libusb_set_debug(@ctx, level) end |
#devices(filter_hash = {}) ⇒ Array<LIBUSB::Device>
Obtain a list of devices currently attached to the USB system, optionally matching certain criteria.
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 |
# File 'lib/libusb/context.rb', line 384 def devices(filter_hash={}) device_list.select do |dev| ( !filter_hash[:bClass] || (dev.bDeviceClass==CLASS_PER_INTERFACE ? dev.settings.map(&:bInterfaceClass).&([filter_hash[:bClass]].flatten).any? : [filter_hash[:bClass]].flatten.include?(dev.bDeviceClass))) && ( !filter_hash[:bSubClass] || (dev.bDeviceClass==CLASS_PER_INTERFACE ? dev.settings.map(&:bInterfaceSubClass).&([filter_hash[:bSubClass]].flatten).any? : [filter_hash[:bSubClass]].flatten.include?(dev.bDeviceSubClass))) && ( !filter_hash[:bProtocol] || (dev.bDeviceClass==CLASS_PER_INTERFACE ? dev.settings.map(&:bInterfaceProtocol).&([filter_hash[:bProtocol]].flatten).any? : [filter_hash[:bProtocol]].flatten.include?(dev.bDeviceProtocol))) && ( !filter_hash[:bMaxPacketSize0] || [filter_hash[:bMaxPacketSize0]].flatten.include?(dev.bMaxPacketSize0) ) && ( !filter_hash[:idVendor] || [filter_hash[:idVendor]].flatten.include?(dev.idVendor) ) && ( !filter_hash[:idProduct] || [filter_hash[:idProduct]].flatten.include?(dev.idProduct) ) && ( !filter_hash[:bcdUSB] || [filter_hash[:bcdUSB]].flatten.include?(dev.bcdUSB) ) && ( !filter_hash[:bcdDevice] || [filter_hash[:bcdDevice]].flatten.include?(dev.bcdDevice) ) end end |
#eventmachine_register ⇒ Object
Register libusb’s file descriptors and timeouts to EventMachine.
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/libusb/eventmachine.rb', line 34 def eventmachine_register @eventmachine_attached_fds = {} @eventmachine_timer = nil pollfds = self.pollfds if pollfds pollfds.each do |pollfd| eventmachine_add_pollfd(pollfd) end self.on_pollfd_added do |pollfd| eventmachine_add_pollfd(pollfd) end self.on_pollfd_removed do |pollfd| eventmachine_rm_pollfd(pollfd) end else # Libusb pollfd API is not available on this platform. # Use simple polling timer, instead: EventMachine.add_periodic_timer(0.01) do @eventmachine_timer = self.handle_events 0 end end end |
#eventmachine_unregister ⇒ Object
60 61 62 63 64 65 66 67 68 69 |
# File 'lib/libusb/eventmachine.rb', line 60 def eventmachine_unregister @eventmachine_timer.cancel if @eventmachine_timer @eventmachine_attached_fds.each do |fd, watcher| watcher.detach end # Deregister callbacks on_pollfd_added on_pollfd_removed end |
#exit ⇒ Object
Deinitialize libusb.
Should be called after closing all open devices and before your application terminates.
178 179 180 181 |
# File 'lib/libusb/context.rb', line 178 def exit raise RemainingReferencesError, "#{@ctx.refs} remaining references to LIBUSB::Context" if @ctx.refs.to_i > 0 @ctx.free_context nil end |
#handle_events(timeout = nil, completion_flag = nil) ⇒ Object
Handle any pending events in blocking mode.
This method must be called when libusb is running asynchronous transfers. This gives libusb the opportunity to reap pending transfers, invoke callbacks, etc.
If a zero timeout is passed, this function will handle any already-pending events and then immediately return in non-blocking style.
If a non-zero timeout is passed and no events are currently pending, this method will block waiting for events to handle up until the specified timeout. If an event arrives or a signal is raised, this method will return early.
If the parameter completion_flag is used, then after obtaining the event handling lock this function will return immediately if the flag is set to completed. This allows for race free waiting for the completion of a specific transfer. See source of Transfer#submit_and_wait for a use case of completion_flag.
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 |
# File 'lib/libusb/context.rb', line 331 def handle_events(timeout=nil, completion_flag=nil) if completion_flag && !completion_flag.is_a?(Context::CompletionFlag) raise ArgumentError, "completion_flag is not a CompletionFlag" end if timeout timeval = Call::Timeval.new timeval.in_ms = timeout res = if Call.respond_to?(:libusb_handle_events_timeout_completed) Call.libusb_handle_events_timeout_completed(@ctx, timeval, completion_flag) else Call.libusb_handle_events_timeout(@ctx, timeval) end else res = if Call.respond_to?(:libusb_handle_events_completed) Call.libusb_handle_events_completed(@ctx, completion_flag ) else Call.libusb_handle_events(@ctx) end end LIBUSB.raise_error res, "in libusb_handle_events" if res<0 end |
#interrupt_event_handler ⇒ Object
Interrupt any active thread that is handling events.
This is mainly useful for interrupting a dedicated event handling thread when an application wishes to call #exit.
Available since libusb-1.0.21.
361 362 363 |
# File 'lib/libusb/context.rb', line 361 def interrupt_event_handler Call.libusb_interrupt_event_handler(@ctx) end |
#next_timeout ⇒ Float?
Determine the next internal timeout that libusb needs to handle.
You only need to use this function if you are calling poll() or select() or similar on libusb’s file descriptors yourself - you do not need to use it if you are calling #handle_events directly.
You should call this function in your main loop in order to determine how long to wait for select() or poll() to return results. libusb needs to be called into at this timeout, so you should use it as an upper bound on your select() or poll() call.
When the timeout has expired, call into #handle_events (perhaps in non-blocking mode) so that libusb can handle the timeout.
This function may return zero. If this is the case, it indicates that libusb has a timeout that has already expired so you should call #handle_events immediately. A return code of nil
indicates that there are no pending timeouts.
On some platforms, this function will always returns nil
(no pending timeouts). See libusb’s notes on time-based events.
454 455 456 457 458 459 |
# File 'lib/libusb/context.rb', line 454 def next_timeout timeval = Call::Timeval.new res = Call.libusb_get_next_timeout @ctx, timeval LIBUSB.raise_error res, "in libusb_get_next_timeout" if res<0 res == 1 ? timeval.in_s : nil end |
#on_hotplug_event(events: HOTPLUG_EVENT_DEVICE_ARRIVED | HOTPLUG_EVENT_DEVICE_LEFT, flags: 0, vendor_id: HOTPLUG_MATCH_ANY, product_id: HOTPLUG_MATCH_ANY, dev_class: HOTPLUG_MATCH_ANY) {|device, event| ... } ⇒ HotplugCallback
Register a hotplug event notification.
Register a callback with the LIBUSB::Context. The callback will fire when a matching event occurs on a matching device. The callback is armed until either it is deregistered with LIBUSB::Context::HotplugCallback#deregister or the supplied block returns :finish
to indicate it is finished processing events.
If the flag HOTPLUG_ENUMERATE is passed the callback will be called with a :HOTPLUG_EVENT_DEVICE_ARRIVED for all devices already plugged into the machine. Note that libusb modifies its internal device list from a separate thread, while calling hotplug callbacks from #handle_events, so it is possible for a device to already be present on, or removed from, its internal device list, while the hotplug callbacks still need to be dispatched. This means that when using HOTPLUG_ENUMERATE, your callback may be called twice for the arrival of the same device, once from #on_hotplug_event and once from #handle_events; and/or your callback may be called for the removal of a device for which an arrived call was never made.
Since libusb version 1.0.16.
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 |
# File 'lib/libusb/context.rb', line 540 def on_hotplug_event(events: HOTPLUG_EVENT_DEVICE_ARRIVED | HOTPLUG_EVENT_DEVICE_LEFT, flags: 0, vendor_id: HOTPLUG_MATCH_ANY, product_id: HOTPLUG_MATCH_ANY, dev_class: HOTPLUG_MATCH_ANY, &block) handle = HotplugCallback.new self, @ctx, @hotplug_callbacks block2 = proc do |ctx, pDevice, event, _user_data| raise "internal error: unexpected context" unless @ctx==ctx dev = Device.new self, pDevice blres = block.call(dev, event) case blres when :finish 1 when :repeat 0 else raise ArgumentError, "hotplug event handler must return :finish or :repeat" end end res = Call.libusb_hotplug_register_callback(@ctx, events, flags, vendor_id, product_id, dev_class, block2, nil, handle) LIBUSB.raise_error res, "in libusb_hotplug_register_callback" if res<0 # Avoid GC'ing of the block: @hotplug_callbacks[handle[:handle]] = block2 return handle end |
#on_pollfd_added {|pollfd| ... } ⇒ Object
Register a notification block for file descriptor additions.
This block will be invoked for every new file descriptor that libusb uses as an event source.
To disable the notification callback, execute on_pollfd_added without a block.
Note that file descriptors may have been added even before you register these notifiers (e.g. at #initialize time).
472 473 474 475 476 477 478 479 480 |
# File 'lib/libusb/context.rb', line 472 def on_pollfd_added &block @on_pollfd_added = if block proc do |fd, events, _| pollfd = Pollfd.new fd, events block.call pollfd end end Call.libusb_set_pollfd_notifiers @ctx, @on_pollfd_added, @on_pollfd_removed, nil end |
#on_pollfd_removed {|pollfd| ... } ⇒ Object
Register a notification block for file descriptor removals.
This block will be invoked for every removed file descriptor that libusb uses as an event source.
To disable the notification callback, execute on_pollfd_removed without a block.
Note that the removal notifier may be called during #exit (e.g. when it is closing file descriptors that were opened and added to the poll set at #initialize time). If you don’t want this, overwrite the notifier immediately before calling #exit.
495 496 497 498 499 500 501 502 503 |
# File 'lib/libusb/context.rb', line 495 def on_pollfd_removed &block @on_pollfd_removed = if block proc do |fd, _| pollfd = Pollfd.new fd block.call pollfd end end Call.libusb_set_pollfd_notifiers @ctx, @on_pollfd_added, @on_pollfd_removed, nil end |
#pollfds ⇒ Array<Pollfd>
Retrieve a list of file descriptors that should be polled by your main loop as libusb event sources.
As file descriptors are a Unix-specific concept, this function is not available on Windows and will always return nil
.
413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 |
# File 'lib/libusb/context.rb', line 413 def pollfds ppPollfds = Call.libusb_get_pollfds(@ctx) return nil if ppPollfds.null? offs = 0 pollfds = [] while !(pPollfd=ppPollfds.get_pointer(offs)).null? pollfd = Call::Pollfd.new pPollfd pollfds << Pollfd.new(pollfd[:fd], pollfd[:events]) offs += FFI.type_size :pointer end if Call.respond_to?(:libusb_free_pollfds) Call.libusb_free_pollfds(ppPollfds) else Stdio.free(ppPollfds) end pollfds end |
#set_log_cb(mode) {|context, level, str| ... } ⇒ Object
Set log handler.
libusb will redirect its log messages to the provided method block. libusb supports redirection of per context and global log messages. Log messages sent to the context will be sent to the global log handler too.
If libusb is compiled without message logging or USE_SYSTEM_LOGGING_FACILITY is defined then global callback function will never be called. If ENABLE_DEBUG_LOGGING is defined then per context callback function will never be called.
To disable the log callback, execute set_log_cb without a block.
Available since libusb-1.0.23, LIBUSB_API_VERSION >= 0x01000107
269 270 271 272 273 |
# File 'lib/libusb/context.rb', line 269 def set_log_cb(mode, &block) cb_proc = wrap_log_cb(block, mode) Call.libusb_set_log_cb(@ctx, cb_proc, mode) self end |
#set_option(option, *args) ⇒ Object
Set a context related libusb option from the option list.
215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/libusb/context.rb', line 215 def set_option(option, *args) if Call.respond_to?(:libusb_set_option) # Available since libusb-1.0.22 ffi_args = LIBUSB.send(:option_args_to_ffi, option, args, self) res = Call.libusb_set_option(@ctx, option, *ffi_args) LIBUSB.raise_error res, "in libusb_set_option" if res<0 else # Fallback to deprecated function, if the gem is linked to an older libusb. raise ArgumentError, "unknown option #{option.inspect}" unless [:OPTION_LOG_LEVEL, LIBUSB::OPTION_LOG_LEVEL].include?(option) Call.libusb_set_debug(@ctx, *args) end end |
#set_options(options = {}) ⇒ Object
Convenience function to set context related options in the libusb library.
Use this function to configure any number of options within the library. It takes a Hash the same way as given to #initialize. See also option list.
238 239 240 241 242 |
# File 'lib/libusb/context.rb', line 238 def (={}) .each do |k, v| set_option(k, *Array(v)) end end |
#wrap_sys_device(dev_io) ⇒ Object
277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
# File 'lib/libusb/context.rb', line 277 def wrap_sys_device(dev_io) dev_io = dev_io.is_a?(IO) ? dev_io.fileno : dev_io ppHandle = FFI::MemoryPointer.new :pointer res = Call.libusb_wrap_sys_device(@ctx, dev_io, ppHandle) LIBUSB.raise_error res, "in libusb_wrap_sys_device" if res!=0 handle = DevHandle.new self, ppHandle.read_pointer return handle unless block_given? begin yield handle ensure handle.close end end |