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
Instance Method Summary collapse
-
#debug=(level) ⇒ Object
Set message verbosity.
-
#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 ⇒ Context
constructor
Initialize libusb context.
-
#next_timeout ⇒ Float?
Determine the next internal timeout that libusb needs to handle.
-
#on_hotplug_event(args = {}) {|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.
Constructor Details
#initialize ⇒ Context
Initialize libusb context.
100 101 102 103 104 105 106 107 108 |
# File 'lib/libusb/context.rb', line 100 def initialize m = FFI::MemoryPointer.new :pointer res = Call.libusb_init(m) LIBUSB.raise_error res, "in libusb_init" if res!=0 @ctx = m.read_pointer @on_pollfd_added = nil @on_pollfd_removed = nil @hotplug_callbacks = {} end |
Instance Method Details
#debug=(level) ⇒ Object
Set message verbosity.
-
Level 0: no messages ever printed by the library (default)
-
Level 1: error messages are printed to stderr
-
Level 2: warning and error messages are printed to stderr
-
Level 3: informational messages are printed to stdout, warning and error messages are printed to stderr
The default level is 0, which means no messages are ever printed. If you choose to increase the message verbosity level, ensure that your application does not close the stdout/stderr file descriptors.
You are advised to set level 3. libusb is conservative with its message logging and most of the time, will only log messages that explain error conditions and other oddities. This will help you debug your software.
If the LIBUSB_DEBUG environment variable was set when libusb was initialized, this method does nothing: the message verbosity is fixed to the value in the environment variable.
If libusb was compiled without any message logging, this method does nothing: you’ll never get any messages.
If libusb was compiled with verbose debug message logging, this method does nothing: you’ll always get messages from all levels.
144 145 146 |
# File 'lib/libusb/context.rb', line 144 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.
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/libusb/context.rb', line 224 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 |
# 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 end |
#exit ⇒ Object
Deinitialize libusb.
Should be called after closing all open devices and before your application terminates.
113 114 115 |
# File 'lib/libusb/context.rb', line 113 def exit Call.libusb_exit(@ctx) 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.
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/libusb/context.rb', line 184 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 |
#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.
291 292 293 294 295 296 |
# File 'lib/libusb/context.rb', line 291 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(args = {}) {|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.
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 |
# File 'lib/libusb/context.rb', line 369 def on_hotplug_event(args={}, &block) events = args.delete(:events) || (HOTPLUG_EVENT_DEVICE_ARRIVED | HOTPLUG_EVENT_DEVICE_LEFT) flags = args.delete(:flags) || 0 vendor_id = args.delete(:vendor_id) || HOTPLUG_MATCH_ANY product_id = args.delete(:product_id) || HOTPLUG_MATCH_ANY dev_class = args.delete(:dev_class) || HOTPLUG_MATCH_ANY raise ArgumentError, "invalid params #{args.inspect}" unless args.empty? 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 @ctx, 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.
Note that file descriptors may have been added even before you register these notifiers (e.g. at #initialize time).
307 308 309 310 311 312 313 |
# File 'lib/libusb/context.rb', line 307 def on_pollfd_added &block @on_pollfd_added = proc do |fd, events, _| pollfd = Pollfd.new fd, events block.call pollfd 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.
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.
326 327 328 329 330 331 332 |
# File 'lib/libusb/context.rb', line 326 def on_pollfd_removed &block @on_pollfd_removed = proc do |fd, _| pollfd = Pollfd.new fd block.call pollfd 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.
253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
# File 'lib/libusb/context.rb', line 253 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 # ppPollfds has to be released by free() -> give the GC this job ppPollfds.autorelease = true pollfds end |