# This file is part of Libusb for Ruby. # # Libusb for Ruby is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Libusb for Ruby is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with Libusb for Ruby. If not, see . require 'libusb/call' module LIBUSB # Class representing a libusb session. class Context # Initialize libusb context. def initialize m = FFI::MemoryPointer.new :pointer Call.libusb_init(m) @ctx = m.read_pointer end # Deinitialize libusb. # # Should be called after closing all open devices and before your application terminates. def exit Call.libusb_exit(@ctx) end # 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. # # @param [Fixnum] level debug level to set def debug=(level) Call.libusb_set_debug(@ctx, level) end def device_list pppDevs = FFI::MemoryPointer.new :pointer size = Call.libusb_get_device_list(@ctx, pppDevs) ppDevs = pppDevs.read_pointer pDevs = [] size.times do |devi| pDev = ppDevs.get_pointer(devi*FFI.type_size(:pointer)) pDevs << Device.new(self, pDev) end Call.libusb_free_device_list(ppDevs, 1) pDevs end private :device_list # 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. def handle_events res = Call.libusb_handle_events(@ctx) LIBUSB.raise_error res, "in libusb_handle_events" if res<0 end # Obtain a list of devices currently attached to the USB system, optionally matching certain criteria. # # @param [Hash] filter_hash A number of criteria can be defined in key-value pairs. # Only devices that equal all given criterions will be returned. If a criterion is # not specified or its value is +nil+, any device will match that criterion. # The following criteria can be filtered: # * :idVendor, :idProduct (+FixNum+) for matching vendor/product ID, # * :bClass, :bSubClass, :bProtocol (+FixNum+) for the device type - # Devices using CLASS_PER_INTERFACE will match, if any of the interfaces match. # * :bcdUSB, :bcdDevice, :bMaxPacketSize0 (+FixNum+) for the # USB and device release numbers. # Criteria can also specified as Array of several alternative values. # # @example # # Return all devices of vendor 0x0ab1 where idProduct is 3 or 4: # context.device :idVendor=>0x0ab1, :idProduct=>[0x0003, 0x0004] # # @return [Array] 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 end end