# 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 . # # This file is for compatibility with ruby-usb and libusb-0.1. # # Please visit the project website at http://github.com/larskanis/libusb # for support. require 'libusb' require 'forwardable' # Compatibility layer for ruby-usb[http://www.a-k-r.org/ruby-usb/] (API based on libusb-0.1) # # This module provides some limited compatibility to ruby-usb. # # Usage example: # begin # require 'usb' # rescue LoadError # require 'libusb/compat' # end # p USB.devices => [#] # # Known issues: # * Exceptions are different between ruby-usb and libusb and they don't get converted # * libusb-1.0 doesn't explicitly manage USB-buses, so only one Bus is used currently module USB DefaultContext = LIBUSB::Context.new USB_CLASS_PER_INTERFACE = LIBUSB::CLASS_PER_INTERFACE USB_CLASS_AUDIO = LIBUSB::CLASS_AUDIO USB_CLASS_COMM = LIBUSB::CLASS_COMM USB_CLASS_HID = LIBUSB::CLASS_HID USB_CLASS_PRINTER = LIBUSB::CLASS_PRINTER USB_CLASS_PTP = LIBUSB::CLASS_PTP USB_CLASS_MASS_STORAGE = LIBUSB::CLASS_MASS_STORAGE USB_CLASS_HUB = LIBUSB::CLASS_HUB USB_CLASS_DATA = LIBUSB::CLASS_DATA USB_CLASS_VENDOR_SPEC = LIBUSB::CLASS_VENDOR_SPEC USB_DT_DEVICE = LIBUSB::DT_DEVICE USB_DT_CONFIG = LIBUSB::DT_CONFIG USB_DT_STRING = LIBUSB::DT_STRING USB_DT_INTERFACE = LIBUSB::DT_INTERFACE USB_DT_ENDPOINT = LIBUSB::DT_ENDPOINT USB_DT_HID = LIBUSB::DT_HID USB_DT_REPORT = LIBUSB::DT_REPORT USB_DT_PHYSICAL = LIBUSB::DT_PHYSICAL USB_DT_HUB = LIBUSB::DT_HUB USB_DT_DEVICE_SIZE = LIBUSB::DT_DEVICE_SIZE USB_DT_CONFIG_SIZE = LIBUSB::DT_CONFIG_SIZE USB_DT_INTERFACE_SIZE = LIBUSB::DT_INTERFACE_SIZE USB_DT_ENDPOINT_SIZE = LIBUSB::DT_ENDPOINT_SIZE USB_DT_ENDPOINT_AUDIO_SIZE = LIBUSB::DT_ENDPOINT_AUDIO_SIZE USB_DT_HUB_NONVAR_SIZE = LIBUSB::DT_HUB_NONVAR_SIZE USB_ENDPOINT_ADDRESS_MASK = LIBUSB::ENDPOINT_ADDRESS_MASK USB_ENDPOINT_DIR_MASK = LIBUSB::ENDPOINT_DIR_MASK USB_ENDPOINT_IN = LIBUSB::ENDPOINT_IN USB_ENDPOINT_OUT = LIBUSB::ENDPOINT_OUT USB_ENDPOINT_TYPE_MASK = LIBUSB::TRANSFER_TYPE_MASK USB_ENDPOINT_TYPE_CONTROL = LIBUSB::TRANSFER_TYPE_CONTROL USB_ENDPOINT_TYPE_ISOCHRONOUS = LIBUSB::TRANSFER_TYPE_ISOCHRONOUS USB_ENDPOINT_TYPE_BULK = LIBUSB::TRANSFER_TYPE_BULK USB_ENDPOINT_TYPE_INTERRUPT = LIBUSB::TRANSFER_TYPE_INTERRUPT USB_REQ_GET_STATUS = LIBUSB::REQUEST_GET_STATUS USB_REQ_CLEAR_FEATURE = LIBUSB::REQUEST_CLEAR_FEATURE USB_REQ_SET_FEATURE = LIBUSB::REQUEST_SET_FEATURE USB_REQ_SET_ADDRESS = LIBUSB::REQUEST_SET_ADDRESS USB_REQ_GET_DESCRIPTOR = LIBUSB::REQUEST_GET_DESCRIPTOR USB_REQ_SET_DESCRIPTOR = LIBUSB::REQUEST_SET_DESCRIPTOR USB_REQ_GET_CONFIGURATION = LIBUSB::REQUEST_GET_CONFIGURATION USB_REQ_SET_CONFIGURATION = LIBUSB::REQUEST_SET_CONFIGURATION USB_REQ_GET_INTERFACE = LIBUSB::REQUEST_GET_INTERFACE USB_REQ_SET_INTERFACE = LIBUSB::REQUEST_SET_INTERFACE USB_REQ_SYNCH_FRAME = LIBUSB::REQUEST_SYNCH_FRAME USB_TYPE_STANDARD = LIBUSB::REQUEST_TYPE_STANDARD USB_TYPE_CLASS = LIBUSB::REQUEST_TYPE_CLASS USB_TYPE_VENDOR = LIBUSB::REQUEST_TYPE_VENDOR USB_TYPE_RESERVED = LIBUSB::REQUEST_TYPE_RESERVED USB_RECIP_DEVICE = LIBUSB::RECIPIENT_DEVICE USB_RECIP_INTERFACE = LIBUSB::RECIPIENT_INTERFACE USB_RECIP_ENDPOINT = LIBUSB::RECIPIENT_ENDPOINT USB_RECIP_OTHER = LIBUSB::RECIPIENT_OTHER HAS_GET_DRIVER_NP = !FFI::Platform.windows? HAS_DETACH_KERNEL_DRIVER_NP = !FFI::Platform.windows? # not defined by libusb-1.0, but typical values are: USB_MAXENDPOINTS = 32 USB_MAXINTERFACES = 32 USB_MAXALTSETTING = 128 USB_MAXCONFIG = 8 def USB.busses [DefaultBus] end def USB.devices; DefaultContext.devices.map{|c| Device.new(c) }; end def USB.configurations() USB.devices.map {|d| d.configurations }.flatten end def USB.interfaces() USB.configurations.map {|d| d.interfaces }.flatten end def USB.settings() USB.interfaces.map {|d| d.settings }.flatten end def USB.endpoints() USB.settings.map {|d| d.endpoints }.flatten end def USB.find_bus(n) DefaultBus end def USB.each_device_by_class(devclass, subclass=nil, protocol=nil) devs = DefaultContext.devices :bClass=>devclass, :bSubClass=>subclass, :bProtocol=>protocol devs.each do |dev| yield Device.new(dev) end nil end class Bus def initialize(context) @ct = context end def devices @ct.devices.map{|d| Device.new(d) } end def configurations() self.devices.map{|d| d.configurations }.flatten end def interfaces() self.configurations.map {|d| d.interfaces }.flatten end def settings() self.interfaces.map {|d| d.settings }.flatten end def endpoints() self.settings.map {|d| d.endpoints }.flatten end def find_device(n) raise NotImplementedError end end DefaultBus = Bus.new(DefaultContext) def USB.dev_string(base_class, sub_class, protocol) LIBUSB.dev_string(base_class, sub_class, protocol) end class Device extend Forwardable include Comparable def initialize(dev) @dev = dev end def_delegators :@dev, :bLength, :bDescriptorType, :bcdUSB, :bDeviceClass, :bDeviceSubClass, :bDeviceProtocol, :bMaxPacketSize0, :idVendor, :idProduct, :bcdDevice, :iManufacturer, :iProduct, :iSerialNumber, :bNumConfigurations, :manufacturer, :product, :serial_number, :inspect def <=>(o) @dev<=>o.instance_variable_get(:@dev) end def open h = DevHandle.new(@dev.open) if block_given? begin yield h ensure h.usb_close end else h end end def bus; DefaultBus; end def configurations; @dev.configurations.map{|c| Configuration.new(c) }; end def interfaces; @dev.interfaces.map{|c| Interface.new(c) }; end def settings; @dev.settings.map{|c| Setting.new(c) }; end def endpoints; @dev.endpoints.map{|c| Endpoint.new(c) }; end end class Configuration extend Forwardable include Comparable def initialize(cd) @cd = cd end def_delegators :@cd, :bLength, :bDescriptorType, :wTotalLength, :bNumInterfaces, :bConfigurationValue, :iConfiguration, :bmAttributes, :bMaxPower, :inspect def <=>(o) @cd<=>o.instance_variable_get(:@cd) end def bus; DefaultBus; end def device() Device.new(@cd.device) end def interfaces; @cd.interfaces.map{|c| Interface.new(c) }; end def settings() self.interfaces.map {|d| d.settings }.flatten end def endpoints() self.settings.map {|d| d.endpoints }.flatten end end class Interface extend Forwardable include Comparable def initialize(i) @i = i end def_delegators :@i, :inspect def <=>(o) @i<=>o.instance_variable_get(:@i) end def bus() self.configuration.device.bus end def device() self.configuration.device end def configuration; Configuration.new(@i.configuration); end def settings; @i.alt_settings.map{|c| Setting.new(c) }; end def endpoints() self.settings.map {|d| d.endpoints }.flatten end end class Setting extend Forwardable include Comparable def initialize(id) @id = id end def_delegators :@id, :bLength, :bDescriptorType, :bInterfaceNumber, :bAlternateSetting, :bNumEndpoints, :bInterfaceClass, :bInterfaceSubClass, :bInterfaceProtocol, :iInterface, :inspect def <=>(o) @id<=>o.instance_variable_get(:@id) end def bus() self.interface.configuration.device.bus end def device() self.interface.configuration.device end def configuration() self.interface.configuration end def interface; Interface.new(@id.interface); end def endpoints() @id.endpoints.map {|d| Endpoint.new(d) }.flatten end end class Endpoint extend Forwardable include Comparable def initialize(ep) @ep = ep end def_delegators :@ep, :bLength, :bDescriptorType, :bEndpointAddress, :bmAttributes, :wMaxPacketSize, :bInterval, :bRefresh, :bSynchAddress, :inspect def <=>(o) @ep<=>o.instance_variable_get(:@ep) end def bus() self.setting.interface.configuration.device.bus end def device() self.setting.interface.configuration.device end def configuration() self.setting.interface.configuration end def interface() self.setting.interface end def setting; Setting.new(@ep.setting); end end class DevHandle def initialize(dev) @dev = dev end def usb_close; @dev.close; end def usb_set_configuration(c); @dev.configuration=c; end def usb_set_altinterface(c); @dev.set_interface_alt_setting=c; end def usb_clear_halt(c); @dev.clear_halt(c); end def usb_reset; @dev.reset_device; end def usb_claim_interface(c); @dev.claim_interface(c); end def usb_release_interface(c); @dev.release_interface(c); end def usb_get_string(index, langid, buffer) t = @dev.string_descriptor(index, langid) buffer[0, t.length] = t t.length end def usb_get_string_simple(index, buffer) t = @dev.string_descriptor_ascii(index) buffer[0, t.length] = t t.length end def usb_control_msg(requesttype, request, value, index, bytes, timeout) if requesttype&LIBUSB::ENDPOINT_IN != 0 # transfer direction in res = @dev.control_transfer(:bmRequestType=>requesttype, :bRequest=>request, :wValue=>value, :wIndex=>index, :dataIn=>bytes.bytesize, :timeout=>timeout) bytes[0, res.bytesize] = res res.bytesize else # transfer direction out @dev.control_transfer(:bmRequestType=>requesttype, :bRequest=>request, :wValue=>value, :wIndex=>index, :dataOut=>bytes, :timeout=>timeout) end end def usb_bulk_write(endpoint, bytes, timeout) @dev.bulk_transfer(:endpoint=>endpoint, :dataOut=>bytes, :timeout=>timeout) end def usb_bulk_read(endpoint, bytes, timeout) res = @dev.bulk_transfer(:endpoint=>endpoint, :dataIn=>bytes.bytesize, :timeout=>timeout) bytes[0, res.bytesize] = res res.bytesize end def usb_interrupt_write(endpoint, bytes, timeout) @dev.interrupt_transfer(:endpoint=>endpoint, :dataOut=>bytes, :timeout=>timeout) end def usb_interrupt_read(endpoint, bytes, timeout) res = @dev.interrupt_transfer(:endpoint=>endpoint, :dataIn=>bytes.bytesize, :timeout=>timeout) bytes[0, res.bytesize] = res res.bytesize end # rb_define_method(rb_cUSB_DevHandle, "usb_get_descriptor", rusb_get_descriptor, 3); # rb_define_method(rb_cUSB_DevHandle, "usb_get_descriptor_by_endpoint", rusb_get_descriptor_by_endpoint, 4); if HAS_DETACH_KERNEL_DRIVER_NP def usb_detach_kernel_driver_np(interface, dummy=nil) @dev.detach_kernel_driver(interface) end end if HAS_GET_DRIVER_NP def usb_get_driver_np(interface, buffer) if @dev.kernel_driver_active?(interface) t = "unknown driver" buffer[0, t.length] = t else raise Errno::ENODATA, "No data available" end nil end end alias set_configuration usb_set_configuration alias set_altinterface usb_set_altinterface alias clear_halt usb_clear_halt alias claim_interface usb_claim_interface alias release_interface usb_release_interface def get_string_simple(index) @dev.string_descriptor_ascii(index) end end end