# 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 # A structure representing the Binary Device Object Store (BOS) descriptor. # This descriptor is documented in section 9.6.2 of the USB 3.0 specification. # All multiple-byte fields are represented in host-endian format. class Bos < FFI::ManagedStruct module GenericMethods # @return [Integer] Size of this descriptor (in bytes) def bLength self[:bLength] end # @return [Integer] Descriptor type. Will have value LIBUSB::DT_DEVICE_CAPABILITY # in this context. def bDescriptorType self[:bDescriptorType] end # @return [Integer] Device Capability type def bDevCapabilityType self[:bDevCapabilityType] end def inspect "\#<#{self.class} cap: #{bDevCapabilityType} data: #{dev_capability_data.unpack("H*")[0]}>" end # @return [String] Device Capability data (bLength - 3 bytes) def dev_capability_data pointer.read_bytes(bLength - 3) end end # A generic representation of a BOS Device Capability descriptor. class DeviceCapability < FFI::Struct include GenericMethods layout :bLength, :uint8, :bDescriptorType, :uint8, :bDevCapabilityType, :uint8 def initialize( bos, *args) # Avoid that the bos struct is GC'ed before this instance @bos = bos super(*args) end end # A structure representing the USB 2.0 Extension descriptor # This descriptor is documented in section 9.6.2.1 of the USB 3.0 specification. # All multiple-byte fields are represented in host-endian format. class Usb20Extension < FFI::ManagedStruct include GenericMethods layout :bLength, :uint8, :bDescriptorType, :uint8, :bDevCapabilityType, :uint8, :bmAttributes, :uint32 # Bitmap encoding of supported device level features. # A value of one in a bit location indicates a feature is # supported; a value of zero indicates it is not supported. # @see Call::Usb20ExtensionAttributes def bmAttributes self[:bmAttributes] end # @return [Boolean] Supports Link Power Management (LPM) def bm_lpm_support? (bmAttributes & BM_LPM_SUPPORT) != 0 end def inspect attrs = Call::Usb20ExtensionAttributes.to_h.map do |k, v| (bmAttributes & v) ? k.to_s : nil end "\#<#{self.class} #{attrs.compact.join(",")}>" end # @private def self.release(ptr) Call.libusb_free_usb_2_0_extension_descriptor(ptr) end end # A structure representing the SuperSpeed USB Device Capability descriptor # This descriptor is documented in section 9.6.2.2 of the USB 3.0 specification. # All multiple-byte fields are represented in host-endian format. class SsUsbDeviceCapability < FFI::ManagedStruct include GenericMethods layout :bLength, :uint8, :bDescriptorType, :uint8, :bDevCapabilityType, :uint8, :bmAttributes, :uint32, :wSpeedSupported, :uint16, :bFunctionalitySupport, :uint8, :bU1DevExitLat, :uint8, :bU2DevExitLat, :uint16 # Bitmap encoding of supported device level features. # A value of one in a bit location indicates a feature is # supported; a value of zero indicates it is not supported. # # @return [Integer] # @see Call::SsUsbDeviceCapabilityAttributes def bmAttributes self[:bmAttributes] end # @return [Boolean] Supports Latency Tolerance Messages (LTM) def bm_ltm_support? (bmAttributes & BM_LTM_SUPPORT) != 0 end def inspect attrs = Call::SsUsbDeviceCapabilityAttributes.to_h.map do |k,v| (bmAttributes & v) != 0 ? k.to_s : nil end "\#<#{self.class} #{attrs.compact.join(",")} #{supported_speeds.join(",")}>" end # Bitmap encoding of the speed supported by this device when # operating in SuperSpeed mode. # # @return [Integer] # @see Call::SupportedSpeeds def wSpeedSupported self[:wSpeedSupported] end # @return [Array] speeds supported by this device when # operating in SuperSpeed mode {Call::SupportedSpeeds} def supported_speeds speeds = Call::SupportedSpeeds.to_h.map do |k,v| (wSpeedSupported & v) != 0 ? k : nil end speeds.compact end # The lowest speed at which all the functionality supported # by the device is available to the user. For example if the # device supports all its functionality when connected at # full speed and above then it sets this value to 1. # # 0 - low speed # 1 - full speed # 2 - high speed # 3 - super speed # @return [Integer] def bFunctionalitySupport self[:bFunctionalitySupport] end # @return [Integer] U1 Device Exit Latency. def bU1DevExitLat self[:bU1DevExitLat] end # @return [Integer] U2 Device Exit Latency. def bU2DevExitLat self[:bU2DevExitLat] end # @private def self.release(ptr) Call.libusb_free_ss_usb_device_capability_descriptor(ptr) end end # A structure representing the Container ID descriptor. # This descriptor is documented in section 9.6.2.3 of the USB 3.0 specification. # All multiple-byte fields, except UUIDs, are represented in host-endian format. class ContainerId < FFI::ManagedStruct include GenericMethods layout :bLength, :uint8, :bDescriptorType, :uint8, :bDevCapabilityType, :uint8, :bReserved, :uint8, :ContainerID, [:uint8, 16] # Reserved field def bReserved self[:bReserved] end # @return [String] 128 bit UUID def container_id self[:ContainerID].to_ptr.read_bytes(16) end def inspect "\#<#{self.class} #{container_id.unpack("H*")[0]}>" end # @private def self.release(ptr) Call.libusb_free_container_id_descriptor(ptr) end end def initialize( ctx, *args) @ctx = ctx super(*args) end layout :bLength, :uint8, :bDescriptorType, :uint8, :wTotalLength, :uint16, :bNumDeviceCaps, :uint8, :dev_capability, [:pointer, 0] # @return [Integer] Size of this descriptor (in bytes) def bLength self[:bLength] end # @return [Integer] Descriptor type. Will have value LIBUSB::DT_BOS LIBUSB_DT_BOS # in this context. def bDescriptorType self[:bDescriptorType] end # @return [Integer] Length of this descriptor and all of its sub descriptors def wTotalLength self[:wTotalLength] end # @return [Integer] The number of separate device capability descriptors in # the BOS def bNumDeviceCaps self[:bNumDeviceCaps] end # bNumDeviceCap Device Capability Descriptors # # @return [Array] def device_capabilities pp_ext = FFI::MemoryPointer.new :pointer caps = [] # Capabilities are appended to the bos header ptr = pointer + offset_of(:dev_capability) bNumDeviceCaps.times do cap = DeviceCapability.new self, ptr.read_pointer case cap.bDevCapabilityType when LIBUSB::BT_WIRELESS_USB_DEVICE_CAPABILITY # no struct defined in libusb -> use generic DeviceCapability when LIBUSB::BT_USB_2_0_EXTENSION res = Call.libusb_get_usb_2_0_extension_descriptor(@ctx, cap.pointer, pp_ext) cap = Usb20Extension.new(pp_ext.read_pointer) if res==0 when LIBUSB::BT_SS_USB_DEVICE_CAPABILITY res = Call.libusb_get_ss_usb_device_capability_descriptor(@ctx, cap.pointer, pp_ext) cap = SsUsbDeviceCapability.new(pp_ext.read_pointer) if res==0 when LIBUSB::BT_CONTAINER_ID res = Call.libusb_get_container_id_descriptor(@ctx, cap.pointer, pp_ext) cap = ContainerId.new(pp_ext.read_pointer) if res==0 else # unknown capability -> use generic DeviceCapability end ptr += FFI.type_size(:pointer) caps << cap end caps end # @return [Array] Types of Capabilities # # @see Call::BosTypes def device_capability_types # Capabilities are appended to the bos header ptr = pointer + offset_of(:dev_capability) bNumDeviceCaps.times.map do cap = DeviceCapability.new self, ptr.read_pointer ptr += FFI.type_size(:pointer) Call::BosTypes.find cap.bDevCapabilityType end end def inspect "\#<#{self.class} #{device_capability_types.join(", ")}>" end # @private def self.release(ptr) Call.libusb_free_bos_descriptor(ptr) end end end