Access USB devices from Ruby via libusb-1.x

Related tags

Miscellaneous libusb
Overview

Build Status Build status

Access USB devices from Ruby

LIBUSB is a Ruby binding that gives Ruby programmers access to arbitrary USB devices.

  • libusb is a library that gives full access to devices connected via the USB bus. No special kernel driver is thus necessary for accessing USB devices.
  • This Ruby binding supports the API version 1.0 of libusb. Note that the old "legacy" version 0.1.x of libusb uses a completely different API that is covered by the ruby extension ruby-usb .

LIBUSB for Ruby is covered by the GNU Lesser General Public License version 3.

Features

  • Access to descriptors of devices, configurations, interfaces, settings and endpoints
  • Synchronous and asynchronous communication for bulk, control, interrupt and isochronous transfers
  • Support for USB-3.0 descriptors and bulk streams
  • Compatibility layer for ruby-usb (API based on libusb-0.1). See {::USB} for description.

Synopsis

require "libusb"

usb = LIBUSB::Context.new
device = usb.devices(idVendor: 0x04b4, idProduct: 0x8613).first
device.open_interface(0) do |handle|
  handle.control_transfer(bmRequestType: 0x40, bRequest: 0xa0, wValue: 0xe600, wIndex: 0x0000, dataOut: 1.chr)
end

{LIBUSB::Context#devices} is used to get all or only particular devices. After {LIBUSB::Device#open_interface opening and claiming} the {LIBUSB::Device} the resulting {LIBUSB::DevHandle} can be used to communicate with the connected USB device by {LIBUSB::DevHandle#control_transfer}, {LIBUSB::DevHandle#bulk_transfer}, {LIBUSB::DevHandle#interrupt_transfer} or by using the {LIBUSB::Transfer} classes.

A {LIBUSB::Device} can also be used to retrieve information about it, by using the device descriptor attributes. A {LIBUSB::Device} could have several configurations. You can then decide of which configuration to enable. You can only enable one configuration at a time.

Each {LIBUSB::Configuration} has one or more interfaces. These can be seen as functional group performing a single feature of the device.

Each {LIBUSB::Interface} has at least one {LIBUSB::Setting}. The first setting is always default. An alternate setting can be used independent on each interface.

Each {LIBUSB::Setting} specifies it's own set of communication endpoints. Each {LIBUSB::Endpoint} specifies the type of transfer, direction, polling interval and maximum packet size.

See the documentation for a full API description.

Prerequisites

  • Linux, MacOS or Windows system with Ruby MRI 2.x/3.x, JRuby or recent version of Rubinius
  • Optionally: libusb C-library version 1.0.8 or any newer version. The system libusb library can be installed like so:
    • Debian or Ubuntu:

      $ sudo apt-get install libusb-1.0-0
      
    • MacOS: install with homebrew:

      $ brew install libusb
      

      or macports:

      $ port install libusb
      
    • Windows: libusb.gem already comes with a precompiled libusb.dll, but you need to install a device driver (see below)

Install

$ gem install libusb

While gem install the system is checked for a usable libusb library installation. If none could be found, a bundled libusb version is built and used, instead.

Latest code can be used in this way:

$ git clone git://github.com/larskanis/libusb.git
$ bundle
$ rake install_gem

Troubleshooting

In order to implement a driver for a USB device, it's essential to have a look at the packets that are send to and received back from the USB device. Wireshark has builtin capabilities to sniff USB traffic. On Linux you possibly need to load the usbmon kernel module before start:

    sudo modprobe usbmon

On Windows it's possible to sniff USB, if the USB kernel driver was installed by the Wireshark setup.

Wireshark

Device hotplug support

Support for device hotplugging can be used, if LIBUSB.has_capability?(:CAP_HAS_HOTPLUG) returns true. This requires libusb-1.0.16 or newer on Linux or MacOS. Windows support is still on the way.

A hotplug event handler can be registered with {LIBUSB::Context#on_hotplug_event}. You then need to call {LIBUSB::Context#handle_events} in order to receive any events. This can be done as blocking calls (possibly in it's own thread) or by using {LIBUSB::Context#pollfds} to detect any events to handle.

Usage on Windows

In contrast to Linux, any access to an USB device by LIBUSB on Windows requires a proper driver installed in the system. Fortunately creating such a driver is quite easy with Zadig. Select the interesting USB device, choose WinUSB driver and press "Install Driver". That's it. You may take the generated output directory with it's INI-file and use it for driver installations on other 32 or 64 bit Windows systems.

Binary gems for Windows and Linux

The Libusb gem is provided as source gem and as binary gems for Windows and Linux operating systems on rubygems.org. The binary version is usually preferred, but the source version of the gem can be enforced by:

$ gem install libusb --platform ruby

Libusb gem can be cross built for Windows and Linux, using the rake-compiler-dock . Just run:

$ rake gem:native

If everything works, there are several platform specific gem files (like libusb-VERSION-x64-mingw32.gem) in the pkg directory.

EventMachine integration

Libusb for Ruby comes with an experimental integration to EventMachine. That API is currently proof of concept - see {LIBUSB::Context#eventmachine_register}. If you're experienced with EventMachine, please leave a comment.

Resources

Todo

  • stabilize EventMachine interface
Comments
  • result of control_transfer

    result of control_transfer

    Based on the docs I came up with this code

    require "libusb"
    
    usb = LIBUSB::Context.new
    
    device = usb.devices(:idVendor => 0x16d0, :idProduct => 0x0753).first
    
    raise "device not found" if device.nil?
    
    device.open_interface(0) do |handle|
      handle.control_transfer({
        :bmRequestType => LIBUSB::ENDPOINT_IN|LIBUSB::REQUEST_TYPE_CLASS|LIBUSB::RECIPIENT_INTERFACE,
        :bRequest => 0x01,
        :wIndex => 0,
        :wValue => 0,
        :dataIn => 1,
        :timeout => 0
      }) do |result|
        puts "result #{result}"
      end
    end
    

    but it never returns. Does the libusb gem require eventmachine? or what am I doing wrong?

    opened by tcurdt 9
  • ERROR_IO when writing to device, but only sometimes?

    ERROR_IO when writing to device, but only sometimes?

    I'm working on a little driver to set key colors and actuation heights on a Realforce RGB keyboard, but am seeing inconsistent results, which I'm sure is something I'm missing, but I cannot figure out what it is.

    Most of the time when I run the code below, it fails with LIBUSB::ERROR_IO, and does not make it far enough to be seen by Wireshark. I see the same behavior whether running as root or normal user, and with both Ruby 2.3.3 and 2.4.1.

    It fails far more often than it works, but when it gets into a working state, it stays that way until I unplug and replug the device.

    Is there anything obvious I'm missing here? It's clear from various searches that getting devices into the correct state for reading and writing is something that others struggle with as well.

    system uname (Ubuntu 17.04):

    Linux vatican 4.10.0-26-generic #30-Ubuntu SMP Tue Jun 27 09:30:12 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
    

    the code:

    require 'libusb'
    require 'pry'
    
    VENDOR_ID = 0x0853
    PRODUCT_ID = 0x013a
    #ENDPOINT = 0x04
    data = "\xaa\xaa\x62\x00\x04\x00\xff\x00\x00".force_encoding(Encoding::BINARY)
    
    usb = LIBUSB::Context.new
    usb.debug = 10
    
    devices = usb.devices(idVendor: VENDOR_ID, idProduct: PRODUCT_ID)
    device = devices.first
    
    raise 'No device found' unless device
    
    begin
      endpoint_in = device.endpoints.find { |ep| !(ep.bEndpointAddress & LIBUSB::ENDPOINT_IN).zero? }
      endpoint_out = device.endpoints.find { |ep| (ep.bEndpointAddress & LIBUSB::ENDPOINT_IN).zero? }
      # binding.pry
    
      h = device.open
      h.auto_detach_kernel_driver = true
      h.claim_interface(0)
      h.clear_halt endpoint_in
      response = h.interrupt_transfer(
        endpoint: endpoint_out,
        dataOut: data,
        timeout: 10000
      )
    
      p response
    ensure
      h.release_interface(0)
      h.close
    end
    
    

    When it fails:

    libusb: debug [libusb_get_device_list]
    libusb: debug [libusb_get_device_descriptor]
    libusb: debug [libusb_get_device_descriptor]
    libusb: debug [libusb_get_device_descriptor]
    libusb: debug [libusb_get_device_descriptor]
    libusb: debug [libusb_get_device_descriptor]
    libusb: debug [libusb_get_device_descriptor]
    libusb: debug [libusb_get_device_descriptor]
    libusb: debug [libusb_get_config_descriptor] index 0
    libusb: debug [libusb_get_config_descriptor] index 0
    libusb: debug [libusb_open] open 1.47
    libusb: debug [usbi_add_pollfd] add fd 14 events 4
    libusb: debug [libusb_claim_interface] interface 0
    libusb: debug [libusb_clear_halt] endpoint 81
    libusb: debug [libusb_alloc_transfer] transfer 0x55d35fc5d7e0
    libusb: debug [libusb_submit_transfer] transfer 0x55d35fc5d7e0
    libusb: debug [add_to_flying_list] arm timerfd for timeout in 10000ms (first in line)
    libusb: debug [submit_bulk_transfer] need 1 urbs for new transfer with length 9
    libusb: error [submit_bulk_transfer] submiturb failed error -1 errno=16
    libusb: debug [submit_bulk_transfer] first URB failed, easy peasy
    libusb: debug [disarm_timerfd]
    libusb: debug [libusb_release_interface] interface 0
    libusb: debug [libusb_close]
    libusb: debug [usbi_remove_pollfd] remove fd 14
    /home/mpd/.rvm/gems/ruby-2.3.3/gems/libusb-0.6.2-x86_64-linux/lib/libusb/constants.rb:62:in `raise_error': LIBUSB::ERROR_IO in libusb_submit_transfer (LIBUSB::ERROR_IO)
            from /home/mpd/.rvm/gems/ruby-2.3.3/gems/libusb-0.6.2-x86_64-linux/lib/libusb/transfer.rb:202:in `submit!'
            from /home/mpd/.rvm/gems/ruby-2.3.3/gems/libusb-0.6.2-x86_64-linux/lib/libusb/transfer.rb:229:in `submit_and_wait'
            from /home/mpd/.rvm/gems/ruby-2.3.3/gems/libusb-0.6.2-x86_64-linux/lib/libusb/dev_handle.rb:544:in `submit_transfer'
            from /home/mpd/.rvm/gems/ruby-2.3.3/gems/libusb-0.6.2-x86_64-linux/lib/libusb/dev_handle.rb:465:in `interrupt_transfer'
            from usb.rb:35:in `<main>'
    libusb: debug [libusb_free_transfer] transfer 0x55d35fc5d7e0
    

    When it works:

    libusb: debug [libusb_get_device_list]
    libusb: debug [libusb_get_device_descriptor]
    libusb: debug [libusb_get_device_descriptor]
    libusb: debug [libusb_get_device_descriptor]
    libusb: debug [libusb_get_device_descriptor]
    libusb: debug [libusb_get_device_descriptor]
    libusb: debug [libusb_get_device_descriptor]
    libusb: debug [libusb_get_device_descriptor]
    libusb: debug [libusb_get_config_descriptor] index 0
    libusb: debug [libusb_get_config_descriptor] index 0
    libusb: debug [libusb_open] open 1.43
    libusb: debug [usbi_add_pollfd] add fd 14 events 4
    libusb: debug [libusb_claim_interface] interface 0
    libusb: debug [libusb_clear_halt] endpoint 81
    libusb: debug [libusb_alloc_transfer] transfer 0x563855794c20
    libusb: debug [libusb_submit_transfer] transfer 0x563855794c20
    libusb: debug [add_to_flying_list] arm timerfd for timeout in 10000ms (first in line)
    libusb: debug [submit_bulk_transfer] need 1 urbs for new transfer with length 9
    libusb: debug [libusb_handle_events_timeout_completed] doing our own event handling
    libusb: debug [handle_events] poll fds modified, reallocating
    libusb: debug [handle_events] poll() 3 fds with timeout in 60000ms
    libusb: debug [handle_events] poll() returned 1
    libusb: debug [reap_for_handle] urb type=1 status=0 transferred=9
    libusb: debug [handle_bulk_completion] handling completion status 0 of bulk urb 1/1
    libusb: debug [handle_bulk_completion] last URB in transfer --> complete!
    libusb: debug [disarm_timerfd]
    libusb: debug [usbi_handle_transfer_completion] transfer 0x563855794c20 has callback 0x7f3bdb7b5000
    libusb: debug [libusb_release_interface] interface 0
    libusb: debug [libusb_close]
    libusb: debug [usbi_remove_pollfd] remove fd 14
    libusb: debug [libusb_free_transfer] transfer 0x563855794c20
    

    Full lsusb output for this device:

    Bus 001 Device 016: ID 0853:013a Topre Corporation 
    Device Descriptor:
      bLength                18
      bDescriptorType         1
      bcdUSB               1.01
      bDeviceClass            0 (Defined at Interface level)
      bDeviceSubClass         0 
      bDeviceProtocol         0 
      bMaxPacketSize0         8
      idVendor           0x0853 Topre Corporation
      idProduct          0x013a 
      bcdDevice            0.01
      iManufacturer           1 Topre
      iProduct                2 BackLightKB
      iSerial                 0 
      bNumConfigurations      1
      Configuration Descriptor:
        bLength                 9
        bDescriptorType         2
        wTotalLength           91
        bNumInterfaces          3
        bConfigurationValue     1
        iConfiguration          0 
        bmAttributes         0xa0
          (Bus Powered)
          Remote Wakeup
        MaxPower              500mA
        Interface Descriptor:
          bLength                 9
          bDescriptorType         4
          bInterfaceNumber        0
          bAlternateSetting       0
          bNumEndpoints           1
          bInterfaceClass         3 Human Interface Device
          bInterfaceSubClass      1 Boot Interface Subclass
          bInterfaceProtocol      1 Keyboard
          iInterface              3 (error)
            HID Device Descriptor:
              bLength                 9
              bDescriptorType        33
              bcdHID               1.11
              bCountryCode            0 Not supported
              bNumDescriptors         1
              bDescriptorType        34 Report
              wDescriptorLength      65
             Report Descriptors: 
               ** UNAVAILABLE **
          Endpoint Descriptor:
            bLength                 7
            bDescriptorType         5
            bEndpointAddress     0x81  EP 1 IN
            bmAttributes            3
              Transfer Type            Interrupt
              Synch Type               None
              Usage Type               Data
            wMaxPacketSize     0x0008  1x 8 bytes
            bInterval               1
        Interface Descriptor:
          bLength                 9
          bDescriptorType         4
          bInterfaceNumber        1
          bAlternateSetting       0
          bNumEndpoints           1
          bInterfaceClass         3 Human Interface Device
          bInterfaceSubClass      0 No Subclass
          bInterfaceProtocol      0 None
          iInterface              4 (error)
            HID Device Descriptor:
              bLength                 9
              bDescriptorType        33
              bcdHID               1.11
              bCountryCode            0 Not supported
              bNumDescriptors         1
              bDescriptorType        34 Report
              wDescriptorLength     119
             Report Descriptors: 
               ** UNAVAILABLE **
          Endpoint Descriptor:
            bLength                 7
            bDescriptorType         5
            bEndpointAddress     0x82  EP 2 IN
            bmAttributes            3
              Transfer Type            Interrupt
              Synch Type               None
              Usage Type               Data
            wMaxPacketSize     0x0020  1x 32 bytes
            bInterval               1
        Interface Descriptor:
          bLength                 9
          bDescriptorType         4
          bInterfaceNumber        2
          bAlternateSetting       0
          bNumEndpoints           2
          bInterfaceClass         3 Human Interface Device
          bInterfaceSubClass      0 No Subclass
          bInterfaceProtocol      0 None
          iInterface              0 
            HID Device Descriptor:
              bLength                 9
              bDescriptorType        33
              bcdHID               1.11
              bCountryCode            0 Not supported
              bNumDescriptors         1
              bDescriptorType        34 Report
              wDescriptorLength      34
             Report Descriptors: 
               ** UNAVAILABLE **
          Endpoint Descriptor:
            bLength                 7
            bDescriptorType         5
            bEndpointAddress     0x83  EP 3 IN
            bmAttributes            3
              Transfer Type            Interrupt
              Synch Type               None
              Usage Type               Data
            wMaxPacketSize     0x0040  1x 64 bytes
            bInterval               1
          Endpoint Descriptor:
            bLength                 7
            bDescriptorType         5
            bEndpointAddress     0x04  EP 4 OUT
            bmAttributes            3
              Transfer Type            Interrupt
              Synch Type               None
              Usage Type               Data
            wMaxPacketSize     0x0040  1x 64 bytes
            bInterval               1
    Device Status:     0x0000
      (Bus Powered)
    
    opened by xxx 7
  • Not available on FreeBSD

    Not available on FreeBSD

    FreeBSD as a system version of libusb (/usr/lib/libusb.so):

    The libusb library contains interfaces for directly managing a usb device. The current implementation supports v1.0 of the libusb API.

    opened by sdalu 5
  • LIBUSB::ERROR_ACCESS in libusb_claim_interface

    LIBUSB::ERROR_ACCESS in libusb_claim_interface

    whenever I try to claim an interface on my OSX machine I get the above error. Below is an irb log showing the problem. Any idea whats wrong? TIA.

     > usb = LIBUSB::Context.new
     => #<LIBUSB::Context:0x007fcb428cb080 @ctx=#<FFI::Pointer address=0x007fcb4228a7a0>> 
    > device = usb.devices(:idVendor => 0x1294, :idProduct =>0x1320).first
     => #<LIBUSB::Device 250/6 1294:1320 MAIL MAIL ? (HID (00,00))> 
    > handle = device.open_interface(0)
    LIBUSB::ERROR_ACCESS: LIBUSB::ERROR_ACCESS in libusb_claim_interface
        from /Users/perrynfowler/.rvm/gems/ruby-1.9.3-p286/gems/libusb-0.2.2/lib/libusb/constants.rb:46:in `raise_error'
        from /Users/perrynfowler/.rvm/gems/ruby-1.9.3-p286/gems/libusb-0.2.2/lib/libusb/dev_handle.rb:76:in `claim_interface'
        from /Users/perrynfowler/.rvm/gems/ruby-1.9.3-p286/gems/libusb-0.2.2/lib/libusb/device.rb:77:in `block in open_interface'
        from /Users/perrynfowler/.rvm/gems/ruby-1.9.3-p286/gems/libusb-0.2.2/lib/libusb/device.rb:62:in `open'
        from /Users/perrynfowler/.rvm/gems/ruby-1.9.3-p286/gems/libusb-0.2.2/lib/libusb/device.rb:76:in `open_interface'
        from (irb):5
        from /Users/perrynfowler/.rvm/rubies/ruby-1.9.3-p286/bin/irb:16:in `<main>'
    
    opened by perryn 5
  • No transfer_type or synchronization_type methods for endpoint?

    No transfer_type or synchronization_type methods for endpoint?

    I can see the transfer method "Interrupt" if I do:

    device.interfaces[9].endpoints[0].inspect
    => #<LIBUSB::Endpoint 10 IN  Interrupt>
    

    However I can't seem to get this "Interrupt" string any other way other than inspect.

    The inspect source is figuring quite a lot of very useful stuff like transfer_type, synchronization_type and usage_type but this info cannot be accessed any other way but parsing the inspect string, why is this?

    Can we please have transport_type, synchronization_type and usage_type methods that return a string like "Interrupt"?

    Here is the method in question that can be found in /lib/libusb/endpoint.rb

    def inspect
      endpoint_address = self.bEndpointAddress
      num = endpoint_address & 0b00001111
      inout = (endpoint_address & 0b10000000) == 0 ? "OUT" : "IN "
      bits = self.bmAttributes
      transfer_type = %w[Control Isochronous Bulk Interrupt][0b11 & bits]
      type = [transfer_type]
      if transfer_type == 'Isochronous'
        synchronization_type = %w[NoSynchronization Asynchronous Adaptive Synchronous][(0b1100 & bits) >> 2]
        usage_type = %w[Data Feedback ImplicitFeedback ?][(0b110000 & bits) >> 4]
        type << synchronization_type << usage_type
      end
      "\#<#{self.class} #{num} #{inout} #{type.join(" ")}>"
    end
    
    opened by tetherit 5
  • uninitialized constant LIBUSB::Call::FFI (NameError)

    uninitialized constant LIBUSB::Call::FFI (NameError)

    Hi, I have this problem under osx lion: I've installed ffi and libusb gem:

    port install libusb
    gem install ffi
    gem install libusb
    

    When I run this code

    require 'libusb'
    

    I've this error:

    /Users/pioz/.rvm/gems/ruby-1.9.3-p0/gems/libusb-0.1.3/lib/libusb.rb:24:in `<module:Call>': uninitialized constant LIBUSB::Call::FFI (NameError)
    from /Users/pioz/.rvm/gems/ruby-1.9.3-p0/gems/libusb-0.1.3/lib/libusb.rb:23:in `<module:LIBUSB>'
    from /Users/pioz/.rvm/gems/ruby-1.9.3-p0/gems/libusb-0.1.3/lib/libusb.rb:20:in `<top (required)>'
    from /Users/pioz/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:59:in `require'
    from /Users/pioz/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:59:in `rescue in require'
    from /Users/pioz/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:35:in `require'
    from main.rb:1:in `<main>'
    

    Ruby version 1.9.3, libusb version 1.0.9, libusb gem version 0.1.3, ffi gem version 1.0.11.

    Any idea?

    opened by pioz 5
  • Lib crashes after plugging in a usb device and `context.eventmachine_register`

    Lib crashes after plugging in a usb device and `context.eventmachine_register`

    Code sample:

    #!/usr/bin/env ruby
    require 'libusb'
    require 'libusb/eventmachine'
    
    context = LIBUSB::Context.new
    
    EventMachine.run do
      context.eventmachine_register
    
      puts "Started!"
    end
    

    Error in the console after plugging in / out a usb device:

    Starter!
    HOTPLUG_EVENT_DEVICE_ARRIVED: #<LIBUSB::Device 20/9 04b8:0e04 EPSON TM-T801FV 535739460006190000 (Printer (01,02))>
    /Users/mark/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/libusb-0.5.1/lib/libusb/context.rb:394:in `block in on_hotplug_event': hotplug event handler must return :finish or :repeat (ArgumentError)
        from /Users/mark/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/libusb-0.5.1/lib/libusb/context.rb:192:in `libusb_handle_events_timeout_completed'
        from /Users/mark/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/libusb-0.5.1/lib/libusb/context.rb:192:in `handle_events'
        from /Users/mark/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/libusb-0.5.1/lib/libusb/eventmachine.rb:97:in `block in eventmachine_add_pollfd'
        from /Users/mark/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/libusb-0.5.1/lib/libusb/eventmachine.rb:79:in `block in need_handle_events'
        from /Users/mark/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/libusb-0.5.1/lib/libusb/eventmachine.rb:78:in `each'
        from /Users/mark/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/libusb-0.5.1/lib/libusb/eventmachine.rb:78:in `need_handle_events'
        from /Users/mark/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/eventmachine-1.2.0.1/lib/eventmachine.rb:194:in `run_machine'
        from /Users/mark/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/eventmachine-1.2.0.1/lib/eventmachine.rb:194:in `run'
        from app/main.rb:22:in `<main>'
    
    opened by mareczek 4
  • TRANSFER_TIMED_OUT issue

    TRANSFER_TIMED_OUT issue

    I am attempting to port the code from this blog post to ruby and libusb. It can be found here. I seem to be running into TRANSFER_TIMED_OUT on the first interrupt_read on line 28. Unfortunately, the error does not contain transferred so I think there's nothing to read. Since the C code from the blog post seems to work properly I assume either I am doing something wrong (likely) or there's a bug in ruby-libusb. Any thoughts or suggestions?

    opened by joewilliams 4
  • Send data in hex using the parameter dataOut

    Send data in hex using the parameter dataOut

    Asked by Simone per Email:

    I'm trying to use the 'libusb' gem in Ruby under Windows 7. I can connect to the device but when I try to send the command to the device I get the following error:

    C:\>ruby rubyusb2.rb
    C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/libusb-0.6.2-x86-mingw32/li
    b/libusb/transfer.rb:76:in `buffer=': undefined method `bytesize' for 131072:Fix
    num (NoMethodError)
            from C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/libusb-0.6.2-x
    86-mingw32/lib/libusb/dev_handle.rb:460:in `interrupt_transfer'
            from rubyusb2.rb:18:in `<main>'
    

    This is my code:

    require "libusb"
    
    usb = LIBUSB::Context.new
    device = usb.devices(idVendor: 0x0452, idProduct: 0xFFFF).first
    
    handle = device.open
    
    begin
      handle.claim_interface(0)
    rescue LIBUSB::ERROR_BUSY
      handle.detach_kernel_driver(0)
      handle.claim_interface(0)
    end
    
    handle.interrupt_transfer(endpoint: 0x02, dataOut: 0x020000)
    
    handle.close
    

    If I enter a string (es. "02") instead of a hexadecimal parameter in the :dataOut parameter, there are no problems. There is a way to send directly data in hex using the parameter: dataOut?

    opened by larskanis 3
  • no _data_dump for FFI::Pointer

    no _data_dump for FFI::Pointer

    Thanks for making edits to the LIBUSB library. Our design project would be of the question without it.

    I realize this stems from an issue in FFI. Any ideas how to fix this?

    I try to store a LIBUSB::Context object in the Rack::Session while using Sinatra. I can store the object in the Rack Session fine, but when I bring it back down, I get this error.

    https://twitter.com/#!/westonplatter/status/180213943380348928/photo/1 TypeError at /session no _dump_data is defined for class FFI::Pointer file: cookie.rb location: dump line: 60

    https://github.com/ffi/ffi/blob/master/lib/ffi/pointer.rb

    Any help is appreciated.

    opened by westonplatter 3
  • naive question regarding libusb

    naive question regarding libusb

    Rich Morin asked per mail:

    First, thanks for writing and supporting libusb.  Using it, I was able to detect and characterize a iKKEGOL 10633 three-pedal USB Foot Switch which I recently purchased.

    I've been thinking about building a "smart" USB switch, based on a RasPi 3B.  Basically, it would act as a programmable crossbar switch, allowing any device to talk to any computer.  For example, it might let a braille display be used by several computers, controlled by a foot switch.

    My naive question has to do with things like plug gender, protocol roles, etc.  Is there a relatively easy way (in hardware and software) for a computer to act as a USB device?

    opened by larskanis 2
  • More explicit example in the main README?

    More explicit example in the main README?

    Hey there Lars,

    Right now the samep code shows this:

    usb = LIBUSB::Context.new
    device = usb.devices(idVendor: 0x04b4, idProduct: 0x8613).first
    

    I don't know how to obtain these 0x04b4 entries.

    When I do this I get "nil" value.

    Is there some way to obtain a list of all ids currently plugged in? I have an USB stick plugged in.

    Not sure how to get the vendor id.

    Could the example show this for when you insert a new USB device, how to find out the ID and then use that to query content, or auto-mount at a later time? Right now this is an obstacle as I don't know how to get the vendor id as such. Perhaps the example can be extended to find out the IDs somehow. Thanks.

    opened by rubyFeedback 1
  • control_transfert give an extra 8 bytes of random data

    control_transfert give an extra 8 bytes of random data

    When calling #control_transfer with :dataIn the returned string as an extra 8 bytes (due to CONTROL_SETUP_SIZE ?) Tested the same query in C using libusb_control_transfer, I've got the right result

    opened by sdalu 0
  • interrupt_transfer on two endpoints?

    interrupt_transfer on two endpoints?

    I sniffed communication between software on Windows and a sonometer. Now I want to mimic it on Linux. Software sends URB_INTERRUPT out to endpoint 2, with some capture data that probably means "give me current state". Then there is another URB_INTERRUPT in, to endpoint 1. And this interruption has capture data with current state I need. I tried to mimic it and I failed. I described it on stackoverflow. If you have a second I would be greatfull for help.

    https://stackoverflow.com/questions/56360704/i-cannot-mimic-sniffed-urb-interruption-using-libusb-for-ruby

    opened by ciembor 0
  • Bug: Segmentation fault when exiting

    Bug: Segmentation fault when exiting

    When running this simple script

    require "libusb"
    
    usb = LIBUSB::Context.new
    
    device = usb.devices(idVendor: 0x0100, idProduct: 0x0001).first
    
    device.open do |dev|
      if dev.kernel_driver_active?(0)
        dev.detach_kernel_driver(0)
      end
      dev.claim_interface(0) {}
    end
    
    puts 'exiting'
    

    to access my Generic HID device (a custom AVR ATMEGA32U4), in 20% of the cases I'm getting a segfault:

    exiting
    segfault-demo.rb: [BUG] Segmentation fault at 0x00007f9bff0be7ef
    ruby 2.4.5p335 (2018-10-18) [x86_64-linux]
    
    -- Control frame information -----------------------------------------------
    c:0001 p:0000 s:0003 E:001710 (none) [FINISH]
    
    
    -- Machine register context ------------------------------------------------
     RIP: 0x00007f9bff0be7ef RBP: 0x00007f9bff0aced0 RSP: 0x00007f9bff0acec0
     RAX: 0x0000000000000001 RBX: 0x00000000ffffffff RCX: 0x00007f9c07708501
     RDX: 0x00000000ffffffff RDI: 0x0000000000000000 RSI: 0x0000000000000002
      R8: 0x0000000000000000  R9: 0x00007f9bff0ad700 R10: 0x00007f9bff0ad9d0
     R11: 0x0000000000000000 R12: 0x00007ffd3f7dad4e R13: 0x00007ffd3f7dad4f
     R14: 0x00007f9bff0ad700 R15: 0x00007f9c08afd000 EFL: 0x0000000000010206
    
    -- C level backtrace information -------------------------------------------
    /nix/store/n8i8zc2max1cq7nz50lwrry2w6i3gfgi-ruby-2.4.5/lib/libruby.so.2.4(rb_vm_bugreport+0x769) [0x7f9c085e93d9]
    /nix/store/n8i8zc2max1cq7nz50lwrry2w6i3gfgi-ruby-2.4.5/lib/libruby.so.2.4 [0x7f9c084685d4]
    /nix/store/n8i8zc2max1cq7nz50lwrry2w6i3gfgi-ruby-2.4.5/lib/libruby.so.2.4 [0x7f9c0855d02e]
    /nix/store/fg4yq8i8wd08xg3fy58l6q73cjy8hjr2-glibc-2.27/lib/libpthread.so.0 [0x7f9c081b6f10]
    /nix/store/fg4yq8i8wd08xg3fy58l6q73cjy8hjr2-glibc-2.27/lib/libgcc_s.so.1(__getf2+0x3f) [0x7f9bff0be7ef]
    /nix/store/fg4yq8i8wd08xg3fy58l6q73cjy8hjr2-glibc-2.27/lib/libpthread.so.0 [0x7f9c081ac5a7]
    /nix/store/fg4yq8i8wd08xg3fy58l6q73cjy8hjr2-glibc-2.27/lib/libc.so.6(clone+0x3f) [0x7f9c0771222f]
    

    (Full diagnostic output)

    The error doesn't happen when using libusb in a similar way via C or Python. Note that the error appears while exiting, after the last code statement. The error disappears when running sleep 0.01 before exiting. (But not sleep 0.001, which seems to be a too short sleep duration). Besides this error, other features of this library, like transfers, are properly functioning.

    Environment:

    Tested Ruby versions: 2.5.3p105, 2.4.5p335
    ffi (1.10.0)
    libusb (0.6.4)
    Linux 4.14.83 (NixOS)
    

    Let me know if's there's anything more I can do to help debugging this.

    opened by erikarvstedt 1
  • Rookie Question

    Rookie Question

    Hello there,

    Firstly, apologies if this is not the appropriate forum for implementation questions. I'm trying to use the library to create a ruby translation of the following C++ library:

    void UsbDownloader::run()
    {
        bool hasError = true;
    #ifndef USB_READ_DUMP
        libusb_device_handle* handle = 0;
    
        do { // Error loop
            int r;
    
            // Open USB device
            handle = libusb_open_device_with_vid_pid(ctx, BSM_VID, BSM_PID);
            if (!handle) {
                qCritical() << "Failed to open the device";
                break;
            }
            qDebug() << "USB device opened";
    
            // Detach kernel driver
            if (libusb_kernel_driver_active(handle, USB_INTERFACE_IN)) {
                qDebug() << "Detaching kernel driver...";
                r = libusb_detach_kernel_driver(handle, USB_INTERFACE_IN);
                if (r < 0) {
                    qCritical() << "libusb_detach_kernel_driver error" << r;
                    break;
                }
                qDebug() << "Kernel driver detached";
            }
    
            // Claim interface
            qDebug() << "Claiming interface...";
            r = libusb_claim_interface(handle, USB_INTERFACE_IN);
            if (r < 0) {
                qCritical() << "usb_claim_interface error" << r;
                break;
            }
            qDebug() << "Interface claimed";
    
            // Prepare to receive data
            qDebug() << "Register for interrupt data";
            libusb_transfer *transfer_receive = libusb_alloc_transfer(0);
            unsigned char buffer_receive[8];
            UsbDownloaderData usb_data;
    #ifdef USB_WRITE_DUMP
            usb_data.dump.setFileName(USB_WRITE_DUMP);
            usb_data.dump.open(QIODevice::WriteOnly | QIODevice::Truncate);
    #endif
            libusb_fill_interrupt_transfer(transfer_receive, handle, LIBUSB_ENDPOINT_IN | USB_INTERFACE_OUT, buffer_receive, sizeof(buffer_receive), cb_in, &usb_data, 30000);
            r = libusb_submit_transfer(transfer_receive);
            if (r < 0) {
                qCritical() << "libusb_submit_transfer error" << r;
                break;
            }
    
            // Prepare to send request
            qDebug() << "Send control request";
            libusb_transfer *transfer_send = libusb_alloc_transfer(0);
            unsigned char buffer_send[LIBUSB_CONTROL_SETUP_SIZE + USB_CTRL_DATA_LEN] __attribute__ ((aligned (2)));
            libusb_fill_control_setup(buffer_send, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, USB_CTRL_REQUEST, USB_CTRL_VALUE, 0, USB_CTRL_DATA_LEN);
            buffer_send[LIBUSB_CONTROL_SETUP_SIZE] = USB_CTRL_DATA_FIRST;
            memset(buffer_send + LIBUSB_CONTROL_SETUP_SIZE + 1, 0, USB_CTRL_DATA_LEN - 1);
            libusb_fill_control_transfer(transfer_send, handle, buffer_send, cb_out, 0, 3000);
            r = libusb_submit_transfer(transfer_send);
            if (r < 0) {
                qCritical() << "libusb_submit_transfer error" << r;
                break;
            }
    
            // Wait for completion
            while (!usb_data.completed) {
                r = libusb_handle_events_completed(ctx, 0);
                emit progress(100 * usb_data.data.size() / USB_EXPECTED_LEN);
                if (r < 0)
                    break;
            }
    
    #ifdef USB_WRITE_DUMP
            if (usb_data.dump.isOpen())
                usb_data.dump.close();
    #endif
    
            // Emit completion signal
            if (usb_data.completed) {
                emit completed(usb_data.data);
                hasError = false;
            }
        } while(false);
    
        // Close USB device
        if (handle) {
            libusb_release_interface(handle, USB_INTERFACE_IN);
            qDebug() << "Released interface";
            libusb_close(handle);
            handle = 0;
            qDebug() << "Closed USB device";
        }
    #else
        do { // Error loop
            QFile usb_data_file(USB_READ_DUMP);
            if (!usb_data_file.open(QIODevice::ReadOnly | QIODevice::Text)) {
                qCritical() << "Failed to open" << USB_READ_DUMP;
                break;
            }
    
            UsbDownloaderData usb_data;
    #ifdef USB_WRITE_DUMP
            usb_data.dump.setFileName(USB_WRITE_DUMP);
            usb_data.dump.open(QIODevice::WriteOnly | QIODevice::Truncate);
    #endif
    
            while (!usb_data_file.atEnd()) {
                char buff[20];
                qint64 s = usb_data_file.readLine(buff, 20);
                if (s < 16 || s > 17)
                    break;
    
                if (s == 17 && buff[16] != '\n')
                    break;
    
                libusb_transfer t;
                unsigned char b[8];
                bool ok;
                t.buffer = b;
                t.actual_length = 8;
                t.status = LIBUSB_TRANSFER_ERROR; // ignored as error, to avoid resubmit
                t.user_data = &usb_data;
                for(int i = 0; i < 8; ++i) {
                    b[i] = (unsigned char) QString("%1%2").arg(buff[i * 2]).arg(buff[i * 2 + 1]).toUShort(&ok, 16);
                    if (!ok)
                        break;
                }
                if (!ok)
                    break;
    
                cb_in(&t);
                emit progress(100 * usb_data.data.size() / USB_EXPECTED_LEN);
            }
    
    #ifdef USB_WRITE_DUMP
            if (usb_data.dump.isOpen())
                usb_data.dump.close();
    #endif
    
            if (usb_data_file.atEnd()) {
                emit completed(usb_data.data);
                hasError = false;
            }
    
            usb_data_file.close();
        } while(false);
    #endif
        // Emit error signal
        if (hasError)
            emit error();
    }
    

    I understand that I need to initiate a control_transfer to set up the transfer, and then use interrupt_transfer for the passing of data from my device. My current implementation looks something like this:

    usb = LIBUSB::Context.new
    @device = usb.devices(idVendor: BSM_VID, idProduct: BSM_PID).first
    @device.open do |device|
    	# setup interrupt transfer (ready to receive)
    	device.claim_interface(0) do |handle|
    		handle.control_transfer(
    			bmRequestType: 0x21,
    			bRequest: USB_CTRL_REQUEST,
    			wValue: USB_CTRL_VALUE,
    			wIndex: 0x000,
    			dataOut: '\x10\x00\x00\x00\x00\x00\x00\x00'
    		) do |result|
    			puts "control_transfer result: #{result}"
    		end
    
    		handle.interrupt_transfer(
    			endpoint: @device.endpoints.first,
    			dataIn: 8
    		)
    	end
    end
    

    This yields a TRANSFER_STALL in the control_transfer block and raises error TRANSFER_TIMED_OUT (LIBUSB::ERROR_TIMEOUT).

    I'm hoping someone might be able to shed some light on how to correctly use the control_transfer and interrupt_transfer methods of LIBUSB::DevHandle in tandem?

    Really appreciate the help!

    opened by fredkelly 0
A USB proxy based on raw-gadget and libusb

usb-proxy This software is a USB proxy based on raw-gadget and libusb. It is recommended to run this repo on a computer that has an USB OTG port, such

Aristo Chen 39 Dec 7, 2022
ESP32 software USB host through general IO pins. We can connect up to 4 USB-LS HID (keyboard mouse joystick) devices simultaneously.

esp32_usb_soft_host esp32 USB-LS pure software host thru general IO pins. Up to 4 HID devices simultaneously. board ~$3 :https://www.aliexpress.com/pr

Samsonov Dima 313 Jan 1, 2023
ESP8266 software USB host through general IO pins. We can connect up to 2 USB-LS HID (keyboard mouse joystick) devices simultaneously.

esp8266_usb_soft_host Test for esp8266 usb host . Works with ESP-IDF v3.4 80MHz and 160MHz. WorkInProgress Test run with mouse & combined mouse & keyb

Samsonov Dima 28 Sep 1, 2022
Anotter USB temperature logger that can record up to four channels with thermocouple or NTCs connected via CDC directly or SCPI to USB.

temperature-logger Anotter USB temperature logger that can record up to four channels with thermocouple or NTCs connected via CDC directly or SCPI to

Jana Marie Hemsing 50 Nov 24, 2022
A simple utility for loading custom firmware onto the PS5 camera, using libusb.

A simple utility for loading custom firmware onto the PS5 camera, using libusb.

Raleigh Littles 21 Nov 10, 2022
Exploits the Wii U's bluetooth stack to gain IOSU kernel access via bluetooth.

BluuBomb Exploits the Wii U's bluetooth stack to gain IOSU kernel access via bluetooth. For a more detailed write-up see WRITEUP.md. Not to be confuse

null 99 Dec 17, 2022
Tiny and portable usb host and device stack for mcu with usb ip

Tiny and portable usb host and device stack for mcu with usb ip

sakumisu 545 Jan 2, 2023
USB-C_PCB_experiments - USB-C ports made from a flex PCB and an ATtiny84A

USB-C PCB Experiments This is part of an ongoing series of projects involving creative interpretations of the USB mechanical standards. You've probabl

Sam Ettinger 23 Sep 5, 2022
Identify I2C devices from a database of the most popular I2C sensors and other devices

I2C Detective Identify I2C devices from a database of the most popular I2C sensors and other devices. For more information see http://www.technoblogy.

David Johnson-Davies 21 Nov 29, 2022
Calleree helps to analyze Ruby's caller-callee relationships.

Calleree Calleree helps to analyze Ruby's caller-callee relationships. Note that this tool consumes memory and introduces additional overhead because

Koichi Sasada 18 Oct 3, 2022
Breakout detection for Ruby

Breakout ?? BreakoutDetection for Ruby Learn how it works Installation Add this line to your application’s Gemfile: gem 'breakout-detection' Getting S

Andrew Kane 37 Dec 14, 2022
Seasonal-trend decomposition for Ruby

STL Ruby Seasonal-trend decomposition for Ruby Installation Add this line to your application’s Gemfile: gem 'stl-rb' Getting Started Decompose a time

Andrew Kane 15 Mar 14, 2022
List of Persian Colors and hex colors for CSS, SCSS, PHP, JS, Python, and Ruby.

Persian Colors (Iranian colors) List of Persian Colors and hex colors for CSS, SCSS, PHP, C++, QML, JS, Python, Ruby and CSharp. Persian colors Name H

Max Base 12 Sep 3, 2022
Examples of C extensions in Ruby gems

Ruby C Extensions, Explained Background How To Use This Repository Strategies Strategy 0, "isolated" Strategy 1, "system" Strategy 2a, "packaged_sourc

Mike Dalessio 62 Dec 30, 2022
This is a simple filter that will block any attempt to access streams beginning with

Triggering the notification only requires that you visit a particular path on an NTFS volume.

OSR Open Systems Resources, Inc. 73 Nov 1, 2022
Corsair LL Access driver abuse

CorsairLLeak Map physical addresses into userspace (RW), read/write MSRs, send/recieve data on I/O ports, and query/set bus configuration data with th

Arush Agarampur 21 Jun 27, 2022
OffensivePH - use old Process Hacker driver to bypass several user-mode access controls

offensiveph OffensivePH is a post-exploitation tool that utilizes an old Process Hacker driver to bypass several user-mode access controls. Usage Comp

Red Section 291 Dec 29, 2022
FastPath_MP: An FPGA-based multi-path architecture for direct access from FPGA to NVMe SSD

FastPath_MP Description This repository stores the source code of FastPath_MP, an FPGA-based multi-path architecture for direct access from FPGA to NV

Beehive lab 21 Sep 12, 2022
Access to the native OS clipboard from NodeJS

Read / Write from the native OS clipboard in Node.js Features Simple API usage and Error Handling Fast / Direct OS Calls using C Full Unicode Support

Caden Parker 5 Oct 13, 2022