libusb 0.7.0-x64-mingw-ucrt
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.appveyor.yml +33 -0
- data/.github/workflows/ci.yml +185 -0
- data/.gitignore +9 -0
- data/.travis.yml +26 -0
- data/.yardopts +6 -0
- data/COPYING +165 -0
- data/Gemfile +19 -0
- data/History.md +193 -0
- data/README.md +184 -0
- data/Rakefile +79 -0
- data/lib/libusb/bos.rb +362 -0
- data/lib/libusb/call.rb +622 -0
- data/lib/libusb/compat.rb +376 -0
- data/lib/libusb/configuration.rb +154 -0
- data/lib/libusb/constants.rb +170 -0
- data/lib/libusb/context.rb +576 -0
- data/lib/libusb/context_reference.rb +38 -0
- data/lib/libusb/dependencies.rb +7 -0
- data/lib/libusb/dev_handle.rb +574 -0
- data/lib/libusb/device.rb +407 -0
- data/lib/libusb/endpoint.rb +195 -0
- data/lib/libusb/eventmachine.rb +187 -0
- data/lib/libusb/gem_helper.rb +151 -0
- data/lib/libusb/interface.rb +60 -0
- data/lib/libusb/libusb_recipe.rb +29 -0
- data/lib/libusb/setting.rb +132 -0
- data/lib/libusb/ss_companion.rb +72 -0
- data/lib/libusb/stdio.rb +25 -0
- data/lib/libusb/transfer.rb +418 -0
- data/lib/libusb/version_gem.rb +19 -0
- data/lib/libusb/version_struct.rb +63 -0
- data/lib/libusb-1.0.dll +0 -0
- data/lib/libusb.rb +146 -0
- data/libusb.gemspec +28 -0
- data/test/test_libusb.rb +42 -0
- data/test/test_libusb_bos.rb +140 -0
- data/test/test_libusb_bulk_stream_transfer.rb +61 -0
- data/test/test_libusb_compat.rb +78 -0
- data/test/test_libusb_compat_mass_storage.rb +81 -0
- data/test/test_libusb_context.rb +88 -0
- data/test/test_libusb_descriptors.rb +245 -0
- data/test/test_libusb_event_machine.rb +118 -0
- data/test/test_libusb_gc.rb +52 -0
- data/test/test_libusb_hotplug.rb +129 -0
- data/test/test_libusb_iso_transfer.rb +56 -0
- data/test/test_libusb_mass_storage.rb +268 -0
- data/test/test_libusb_mass_storage2.rb +96 -0
- data/test/test_libusb_structs.rb +87 -0
- data/test/test_libusb_threads.rb +89 -0
- data/wireshark-usb-sniffer.png +0 -0
- metadata +112 -0
data/README.md
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
<!-- -*- coding: utf-8 -*- -->
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.com/larskanis/libusb.svg?branch=master)](https://travis-ci.com/larskanis/libusb)
|
4
|
+
[![Build status](https://ci.appveyor.com/api/projects/status/mdfnfdwu4mil42o3/branch/master?svg=true)](https://ci.appveyor.com/project/larskanis/libusb/branch/master)
|
5
|
+
|
6
|
+
Access USB devices from Ruby
|
7
|
+
============================
|
8
|
+
|
9
|
+
LIBUSB is a Ruby binding that gives Ruby programmers access to arbitrary USB devices.
|
10
|
+
|
11
|
+
* [libusb](http://libusb.info) 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.
|
12
|
+
* This Ruby binding supports the API version 1.0 of [libusb](http://libusb.info). 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](http://www.a-k-r.org/ruby-usb/) .
|
13
|
+
|
14
|
+
|
15
|
+
LIBUSB for Ruby is covered by the GNU Lesser General Public License version 3.
|
16
|
+
|
17
|
+
Features
|
18
|
+
--------
|
19
|
+
|
20
|
+
* Access to descriptors of devices, configurations, interfaces, settings and endpoints
|
21
|
+
* Synchronous and asynchronous communication for bulk, control, interrupt and isochronous transfers
|
22
|
+
* Support for USB-3.0 descriptors and bulk streams
|
23
|
+
* Compatibility layer for [ruby-usb](http://www.a-k-r.org/ruby-usb/) (API based on libusb-0.1). See {::USB} for description.
|
24
|
+
|
25
|
+
Synopsis
|
26
|
+
--------
|
27
|
+
```ruby
|
28
|
+
require "libusb"
|
29
|
+
|
30
|
+
usb = LIBUSB::Context.new
|
31
|
+
device = usb.devices(idVendor: 0x04b4, idProduct: 0x8613).first
|
32
|
+
device.open_interface(0) do |handle|
|
33
|
+
handle.control_transfer(bmRequestType: 0x40, bRequest: 0xa0, wValue: 0xe600, wIndex: 0x0000, dataOut: 1.chr)
|
34
|
+
end
|
35
|
+
```
|
36
|
+
{LIBUSB::Context#devices} is used to get all or only particular devices.
|
37
|
+
After {LIBUSB::Device#open_interface opening and claiming} the {LIBUSB::Device} the resulting {LIBUSB::DevHandle} can be
|
38
|
+
used to communicate with the connected USB device
|
39
|
+
by {LIBUSB::DevHandle#control_transfer}, {LIBUSB::DevHandle#bulk_transfer},
|
40
|
+
{LIBUSB::DevHandle#interrupt_transfer} or by using the {LIBUSB::Transfer} classes.
|
41
|
+
|
42
|
+
A {LIBUSB::Device} can also be used to retrieve information about it,
|
43
|
+
by using the device descriptor attributes.
|
44
|
+
A {LIBUSB::Device} could have several configurations. You can then decide of which
|
45
|
+
configuration to enable. You can only enable one configuration at a time.
|
46
|
+
|
47
|
+
Each {LIBUSB::Configuration} has one or more interfaces. These can be seen as functional group
|
48
|
+
performing a single feature of the device.
|
49
|
+
|
50
|
+
Each {LIBUSB::Interface} has at least one {LIBUSB::Setting}. The first setting is always default.
|
51
|
+
An alternate setting can be used independent on each interface.
|
52
|
+
|
53
|
+
Each {LIBUSB::Setting} specifies it's own set of communication endpoints.
|
54
|
+
Each {LIBUSB::Endpoint} specifies the type of transfer, direction, polling interval and
|
55
|
+
maximum packet size.
|
56
|
+
|
57
|
+
See [the documentation](http://rubydoc.info/gems/libusb/frames) for a full API description.
|
58
|
+
|
59
|
+
Prerequisites
|
60
|
+
-------------
|
61
|
+
|
62
|
+
* Linux, MacOS or Windows system with Ruby MRI 2.x/3.x, JRuby or recent version of Rubinius
|
63
|
+
* Optionally: [libusb](http://libusb.info) C-library version 1.0.8 or any newer version.
|
64
|
+
The system libusb library can be installed like so:
|
65
|
+
* Debian or Ubuntu:
|
66
|
+
|
67
|
+
```
|
68
|
+
$ sudo apt-get install libusb-1.0-0
|
69
|
+
```
|
70
|
+
* MacOS: install with homebrew:
|
71
|
+
|
72
|
+
```
|
73
|
+
$ brew install libusb
|
74
|
+
```
|
75
|
+
or macports:
|
76
|
+
|
77
|
+
```
|
78
|
+
$ port install libusb
|
79
|
+
```
|
80
|
+
* Windows: libusb.gem already comes with a precompiled `libusb.dll`, but you need to install a device driver (see [below](#usage-on-windows))
|
81
|
+
|
82
|
+
Install
|
83
|
+
-------
|
84
|
+
|
85
|
+
$ gem install libusb
|
86
|
+
|
87
|
+
While ```gem install``` the system is checked for a usable libusb library installation.
|
88
|
+
If none could be found, a bundled libusb version is built and used, instead.
|
89
|
+
|
90
|
+
Latest code can be used in this way:
|
91
|
+
|
92
|
+
$ git clone git://github.com/larskanis/libusb.git
|
93
|
+
$ bundle
|
94
|
+
$ rake install_gem
|
95
|
+
|
96
|
+
Troubleshooting
|
97
|
+
------------------------
|
98
|
+
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](https://www.wireshark.org) has builtin capabilities to sniff USB traffic. On Linux you possibly need to load the usbmon kernel module before start:
|
99
|
+
```
|
100
|
+
sudo modprobe usbmon
|
101
|
+
```
|
102
|
+
On Windows it's possible to sniff USB, if the USB kernel driver was installed by the Wireshark setup.
|
103
|
+
|
104
|
+
![Wireshark](wireshark-usb-sniffer.png?raw=true "Wireshark sniffing USB packets")
|
105
|
+
|
106
|
+
Device hotplug support
|
107
|
+
----------------------
|
108
|
+
|
109
|
+
Support for device hotplugging can be used, if ```LIBUSB.has_capability?(:CAP_HAS_HOTPLUG)``` returns ```true```.
|
110
|
+
This requires libusb-1.0.16 or newer on Linux or MacOS. Windows support is [still on the way](https://github.com/libusbx/libusbx/issues/9).
|
111
|
+
|
112
|
+
A hotplug event handler can be registered with {LIBUSB::Context#on_hotplug_event}.
|
113
|
+
You then need to call {LIBUSB::Context#handle_events} in order to receive any events.
|
114
|
+
This can be done as blocking calls (possibly in it's own thread) or by using {LIBUSB::Context#pollfds} to
|
115
|
+
detect any events to handle.
|
116
|
+
|
117
|
+
|
118
|
+
Usage on Windows
|
119
|
+
----------------
|
120
|
+
|
121
|
+
In contrast to Linux, any access to an USB device by LIBUSB on Windows requires a proper driver
|
122
|
+
installed in the system. Fortunately creating such a driver is quite easy with
|
123
|
+
[Zadig](http://zadig.akeo.ie/). Select the interesting USB device,
|
124
|
+
choose WinUSB driver and press "Install Driver". That's it. You may take the generated output directory
|
125
|
+
with it's INI-file and use it for driver installations on other 32 or 64 bit Windows
|
126
|
+
systems.
|
127
|
+
|
128
|
+
|
129
|
+
Binary gems for Windows and Linux
|
130
|
+
---------------------------
|
131
|
+
|
132
|
+
The Libusb gem is provided as source gem and as binary gems for Windows and Linux operating systems on [rubygems.org](https://rubygems.org/gems/libusb).
|
133
|
+
The binary version is usually preferred, but the source version of the gem can be enforced by:
|
134
|
+
|
135
|
+
$ gem install libusb --platform ruby
|
136
|
+
|
137
|
+
Libusb gem can be cross built for Windows and Linux, using the [rake-compiler-dock](https://github.com/larskanis/rake-compiler-dock) .
|
138
|
+
Just run:
|
139
|
+
|
140
|
+
$ rake gem:native
|
141
|
+
|
142
|
+
If everything works, there are several platform specific gem files (like `libusb-VERSION-x64-mingw32.gem`) in the pkg
|
143
|
+
directory.
|
144
|
+
|
145
|
+
EventMachine integration
|
146
|
+
------------------------
|
147
|
+
|
148
|
+
Libusb for Ruby comes with an experimental integration to [EventMachine](http://rubyeventmachine.com/).
|
149
|
+
That API is currently proof of concept - see {LIBUSB::Context#eventmachine_register}.
|
150
|
+
If you're experienced with EventMachine, please leave a comment.
|
151
|
+
|
152
|
+
|
153
|
+
Testing LIBUSB gem
|
154
|
+
------------------
|
155
|
+
|
156
|
+
Libusb for Ruby has a bundled test suite which verifies proper working of many functions of the library.
|
157
|
+
Only a small subset of these tests are executed on Github Actions due to the missing USB functions in the CI environments.
|
158
|
+
They just verify that the libusb library can be installed and called and that very basic functions are working.
|
159
|
+
|
160
|
+
To run the tests against real devices the following procedure should be done:
|
161
|
+
|
162
|
+
```sh
|
163
|
+
$ # Connect a USB mass strorage device. It is used read-only.
|
164
|
+
$ sudo chown $USER /dev/bus/usb/*/*
|
165
|
+
$ rake test
|
166
|
+
```
|
167
|
+
|
168
|
+
While the tests are running a second arbitrary USB device is requested to be connected and shortly after disconnected again.
|
169
|
+
There are only 5 seconds timeout for connecting and disconnecting, so that the device should have be ready.
|
170
|
+
Some USB mass storage devices are not compatible to the tests, so that it's best to try out different models to find some that doesn't fail.
|
171
|
+
|
172
|
+
|
173
|
+
Resources
|
174
|
+
---------
|
175
|
+
|
176
|
+
* Project's home page: http://github.com/larskanis/libusb
|
177
|
+
* API documentation: http://rubydoc.info/gems/libusb/frames
|
178
|
+
* Mailinglist: http://rubyforge.org/mailman/listinfo/libusb-hackers
|
179
|
+
* Overall introduction to USB: http://www.usbmadesimple.co.uk
|
180
|
+
|
181
|
+
Todo
|
182
|
+
----
|
183
|
+
|
184
|
+
* stabilize EventMachine interface
|
data/Rakefile
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# -*- ruby -*-
|
3
|
+
|
4
|
+
require 'bundler/gem_helper'
|
5
|
+
require 'rubygems/package_task'
|
6
|
+
require 'pathname'
|
7
|
+
require 'uri'
|
8
|
+
require 'ostruct'
|
9
|
+
require 'rake/clean'
|
10
|
+
require_relative 'lib/libusb/libusb_recipe'
|
11
|
+
require_relative 'lib/libusb/gem_helper'
|
12
|
+
|
13
|
+
CLOBBER.include 'pkg'
|
14
|
+
CLEAN.include 'ports'
|
15
|
+
CLEAN.include 'tmp'
|
16
|
+
CLEAN.include 'ext/tmp'
|
17
|
+
CLEAN.include 'lib/*.a'
|
18
|
+
CLEAN.include 'lib/*.so*'
|
19
|
+
CLEAN.include 'lib/*.dll*'
|
20
|
+
|
21
|
+
task :build do
|
22
|
+
require_relative 'lib/libusb/libusb_recipe'
|
23
|
+
recipe = LIBUSB::LibusbRecipe.new
|
24
|
+
recipe.download
|
25
|
+
end
|
26
|
+
|
27
|
+
task :gem => :build
|
28
|
+
task :compile do
|
29
|
+
sh "ruby -C ext extconf.rb --disable-system-libusb"
|
30
|
+
sh "make -C ext install RUBYARCHDIR=../lib"
|
31
|
+
end
|
32
|
+
|
33
|
+
task :gemfile_libusb_gem do
|
34
|
+
gf = File.read("Gemfile")
|
35
|
+
gf.gsub!(/^(gemspec)$/, "# \\1")
|
36
|
+
gf << "\ngem 'libusb'\n"
|
37
|
+
File.write("Gemfile_libusb_gem", gf)
|
38
|
+
puts "Gemfile_libusb_gem written"
|
39
|
+
end
|
40
|
+
|
41
|
+
task :test do
|
42
|
+
sh "ruby -w -W2 -I.:lib -e \"#{Dir["test/test_*.rb"].map{|f| "require '#{f}';"}.join}\" -- -v"
|
43
|
+
end
|
44
|
+
task :default => :test
|
45
|
+
|
46
|
+
ci_tests = %w[test_libusb.rb test_libusb_structs.rb]
|
47
|
+
task :ci do
|
48
|
+
sh "ruby -w -W2 -I. -e \"#{ci_tests.map{|f| "require 'test/#{f}';"}.join}\" -- -v"
|
49
|
+
end
|
50
|
+
|
51
|
+
CrossLibraries = [
|
52
|
+
['x86-mingw32', 'i686-w64-mingw32', 'bin/libusb-1.0.dll'],
|
53
|
+
['x64-mingw32', 'x86_64-w64-mingw32', 'bin/libusb-1.0.dll'],
|
54
|
+
['x64-mingw-ucrt', 'x86_64-w64-mingw32', 'bin/libusb-1.0.dll'],
|
55
|
+
['x86-linux', 'i686-linux-gnu', 'lib/libusb-1.0.so'],
|
56
|
+
['x86_64-linux', 'x86_64-linux-gnu', 'lib/libusb-1.0.so'],
|
57
|
+
].map do |ruby_platform, host_platform, libusb_dll|
|
58
|
+
LIBUSB::CrossLibrary.new ruby_platform, host_platform, libusb_dll
|
59
|
+
end
|
60
|
+
|
61
|
+
LIBUSB::GemHelper.install_tasks
|
62
|
+
Bundler::GemHelper.instance.cross_platforms = CrossLibraries.map(&:ruby_platform)
|
63
|
+
|
64
|
+
CrossLibraries.map(&:ruby_platform).each do |platform|
|
65
|
+
desc "Build windows and linux fat binary gems"
|
66
|
+
multitask 'gem:native' => "gem:native:#{platform}"
|
67
|
+
|
68
|
+
task "gem:native:#{platform}" do
|
69
|
+
require 'rake_compiler_dock'
|
70
|
+
sh "bundle package"
|
71
|
+
RakeCompilerDock.sh <<-EOT, platform: platform
|
72
|
+
bundle --local &&
|
73
|
+
#{ "sudo yum install -y libudev-devel &&" if platform=~/linux/ }
|
74
|
+
bundle exec rake --trace cross:#{platform} gem "MAKE=make V=1 -j`nproc`" || cat tmp/*/ports/libusb/*/*.log
|
75
|
+
EOT
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# vim: syntax=ruby
|
data/lib/libusb/bos.rb
ADDED
@@ -0,0 +1,362 @@
|
|
1
|
+
# This file is part of Libusb for Ruby.
|
2
|
+
#
|
3
|
+
# Libusb for Ruby is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# Libusb for Ruby is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public License
|
14
|
+
# along with Libusb for Ruby. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require 'libusb/call'
|
17
|
+
|
18
|
+
module LIBUSB
|
19
|
+
# A structure representing the Binary Device Object Store (BOS) descriptor.
|
20
|
+
# This descriptor is documented in section 9.6.2 of the USB 3.0 specification.
|
21
|
+
# All multiple-byte fields are represented in host-endian format.
|
22
|
+
class Bos < FFI::Struct
|
23
|
+
|
24
|
+
module GenericMethods
|
25
|
+
# @return [Integer] Size of this descriptor (in bytes)
|
26
|
+
def bLength
|
27
|
+
self[:bLength]
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [Integer] Descriptor type. Will have value LIBUSB::DT_DEVICE_CAPABILITY
|
31
|
+
# in this context.
|
32
|
+
def bDescriptorType
|
33
|
+
self[:bDescriptorType]
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [Integer] Device Capability type
|
37
|
+
def bDevCapabilityType
|
38
|
+
self[:bDevCapabilityType]
|
39
|
+
end
|
40
|
+
|
41
|
+
def inspect
|
42
|
+
"\#<#{self.class} cap: #{bDevCapabilityType} data: #{dev_capability_data.unpack1("H*")}>"
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [String] Device Capability data (bLength - 3 bytes)
|
46
|
+
def dev_capability_data
|
47
|
+
pointer.read_bytes(bLength - 3)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# A generic representation of a BOS Device Capability descriptor.
|
52
|
+
class DeviceCapability < FFI::Struct
|
53
|
+
include GenericMethods
|
54
|
+
|
55
|
+
layout :bLength, :uint8,
|
56
|
+
:bDescriptorType, :uint8,
|
57
|
+
:bDevCapabilityType, :uint8
|
58
|
+
|
59
|
+
def initialize( bos, *args)
|
60
|
+
# Avoid that the bos struct is GC'ed before this instance
|
61
|
+
@bos = bos
|
62
|
+
super(*args)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# A structure representing the USB 2.0 Extension descriptor
|
67
|
+
# This descriptor is documented in section 9.6.2.1 of the USB 3.0 specification.
|
68
|
+
# All multiple-byte fields are represented in host-endian format.
|
69
|
+
class Usb20Extension < FFI::Struct
|
70
|
+
include GenericMethods
|
71
|
+
include ContextReference
|
72
|
+
|
73
|
+
layout :bLength, :uint8,
|
74
|
+
:bDescriptorType, :uint8,
|
75
|
+
:bDevCapabilityType, :uint8,
|
76
|
+
:bmAttributes, :uint32
|
77
|
+
|
78
|
+
def initialize(ctx, *args)
|
79
|
+
super(*args)
|
80
|
+
|
81
|
+
register_context(ctx, :libusb_free_usb_2_0_extension_descriptor)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Bitmap encoding of supported device level features.
|
85
|
+
# A value of one in a bit location indicates a feature is
|
86
|
+
# supported; a value of zero indicates it is not supported.
|
87
|
+
# @see Call::Usb20ExtensionAttributes
|
88
|
+
def bmAttributes
|
89
|
+
self[:bmAttributes]
|
90
|
+
end
|
91
|
+
|
92
|
+
# @return [Boolean] Supports Link Power Management (LPM)
|
93
|
+
def bm_lpm_support?
|
94
|
+
(bmAttributes & BM_LPM_SUPPORT) != 0
|
95
|
+
end
|
96
|
+
|
97
|
+
def inspect
|
98
|
+
attrs = Call::Usb20ExtensionAttributes.to_h.map do |k, v|
|
99
|
+
(bmAttributes & v) ? k.to_s : nil
|
100
|
+
end
|
101
|
+
"\#<#{self.class} #{attrs.compact.join(",")}>"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# A structure representing the SuperSpeed USB Device Capability descriptor
|
106
|
+
# This descriptor is documented in section 9.6.2.2 of the USB 3.0 specification.
|
107
|
+
# All multiple-byte fields are represented in host-endian format.
|
108
|
+
class SsUsbDeviceCapability < FFI::Struct
|
109
|
+
include GenericMethods
|
110
|
+
include ContextReference
|
111
|
+
|
112
|
+
layout :bLength, :uint8,
|
113
|
+
:bDescriptorType, :uint8,
|
114
|
+
:bDevCapabilityType, :uint8,
|
115
|
+
:bmAttributes, :uint8,
|
116
|
+
:wSpeedSupported, :uint16,
|
117
|
+
:bFunctionalitySupport, :uint8,
|
118
|
+
:bU1DevExitLat, :uint8,
|
119
|
+
:bU2DevExitLat, :uint16
|
120
|
+
|
121
|
+
def initialize(ctx, *args)
|
122
|
+
super(*args)
|
123
|
+
|
124
|
+
register_context(ctx, :libusb_free_ss_usb_device_capability_descriptor)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Bitmap encoding of supported device level features.
|
128
|
+
# A value of one in a bit location indicates a feature is
|
129
|
+
# supported; a value of zero indicates it is not supported.
|
130
|
+
#
|
131
|
+
# @return [Integer]
|
132
|
+
# @see Call::SsUsbDeviceCapabilityAttributes
|
133
|
+
def bmAttributes
|
134
|
+
self[:bmAttributes]
|
135
|
+
end
|
136
|
+
|
137
|
+
# @return [Boolean] Supports Latency Tolerance Messages (LTM)
|
138
|
+
def bm_ltm_support?
|
139
|
+
(bmAttributes & BM_LTM_SUPPORT) != 0
|
140
|
+
end
|
141
|
+
|
142
|
+
def inspect
|
143
|
+
attrs = Call::SsUsbDeviceCapabilityAttributes.to_h.map do |k,v|
|
144
|
+
(bmAttributes & v) != 0 ? k.to_s : nil
|
145
|
+
end
|
146
|
+
"\#<#{self.class} #{attrs.compact.join(",")} #{supported_speeds.join(",")}>"
|
147
|
+
end
|
148
|
+
|
149
|
+
# Bitmap encoding of the speed supported by this device when
|
150
|
+
# operating in SuperSpeed mode.
|
151
|
+
#
|
152
|
+
# @return [Integer]
|
153
|
+
# @see Call::SupportedSpeeds
|
154
|
+
def wSpeedSupported
|
155
|
+
self[:wSpeedSupported]
|
156
|
+
end
|
157
|
+
|
158
|
+
# @return [Array<Symbol>] speeds supported by this device when
|
159
|
+
# operating in SuperSpeed mode {Call::SupportedSpeeds}
|
160
|
+
def supported_speeds
|
161
|
+
speeds = Call::SupportedSpeeds.to_h.map do |k,v|
|
162
|
+
(wSpeedSupported & v) != 0 ? k : nil
|
163
|
+
end
|
164
|
+
speeds.compact
|
165
|
+
end
|
166
|
+
|
167
|
+
# The lowest speed at which all the functionality supported
|
168
|
+
# by the device is available to the user. For example if the
|
169
|
+
# device supports all its functionality when connected at
|
170
|
+
# full speed and above then it sets this value to 1.
|
171
|
+
#
|
172
|
+
# 0 - low speed
|
173
|
+
# 1 - full speed
|
174
|
+
# 2 - high speed
|
175
|
+
# 3 - super speed
|
176
|
+
# @return [Integer]
|
177
|
+
def bFunctionalitySupport
|
178
|
+
self[:bFunctionalitySupport]
|
179
|
+
end
|
180
|
+
|
181
|
+
# @return [Integer] U1 Device Exit Latency.
|
182
|
+
def bU1DevExitLat
|
183
|
+
self[:bU1DevExitLat]
|
184
|
+
end
|
185
|
+
|
186
|
+
# @return [Integer] U2 Device Exit Latency.
|
187
|
+
def bU2DevExitLat
|
188
|
+
self[:bU2DevExitLat]
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# A structure representing the Container ID descriptor.
|
193
|
+
# This descriptor is documented in section 9.6.2.3 of the USB 3.0 specification.
|
194
|
+
# All multiple-byte fields, except UUIDs, are represented in host-endian format.
|
195
|
+
class ContainerId < FFI::Struct
|
196
|
+
include GenericMethods
|
197
|
+
include ContextReference
|
198
|
+
|
199
|
+
layout :bLength, :uint8,
|
200
|
+
:bDescriptorType, :uint8,
|
201
|
+
:bDevCapabilityType, :uint8,
|
202
|
+
:bReserved, :uint8,
|
203
|
+
:ContainerID, [:uint8, 16]
|
204
|
+
|
205
|
+
def initialize(ctx, *args)
|
206
|
+
super(*args)
|
207
|
+
|
208
|
+
register_context(ctx, :libusb_free_container_id_descriptor)
|
209
|
+
end
|
210
|
+
|
211
|
+
# Reserved field
|
212
|
+
def bReserved
|
213
|
+
self[:bReserved]
|
214
|
+
end
|
215
|
+
|
216
|
+
# @return [String] 128 bit UUID
|
217
|
+
def container_id
|
218
|
+
self[:ContainerID].to_ptr.read_bytes(16)
|
219
|
+
end
|
220
|
+
|
221
|
+
def inspect
|
222
|
+
"\#<#{self.class} #{container_id.unpack1("H*")}>"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
|
227
|
+
# A structure representing a Platform descriptor.
|
228
|
+
# This descriptor is documented in section 9.6.2.4 of the USB 3.2 specification.
|
229
|
+
class PlatformDescriptor < FFI::Struct
|
230
|
+
include GenericMethods
|
231
|
+
include ContextReference
|
232
|
+
|
233
|
+
layout :bLength, :uint8,
|
234
|
+
:bDescriptorType, :uint8,
|
235
|
+
# Capability type. Will have value
|
236
|
+
# libusb_capability_type::LIBUSB_BT_PLATFORM_DESCRIPTOR
|
237
|
+
# LIBUSB_BT_CONTAINER_ID in this context.
|
238
|
+
:bDevCapabilityType, :uint8,
|
239
|
+
# Reserved field
|
240
|
+
:bReserved, :uint8,
|
241
|
+
# 128 bit UUID
|
242
|
+
:PlatformCapabilityUUID, [:uint8, 16],
|
243
|
+
# Capability data (bLength - 20)
|
244
|
+
:CapabilityData, [:uint8, 0]
|
245
|
+
|
246
|
+
def initialize(ctx, *args)
|
247
|
+
super(*args)
|
248
|
+
|
249
|
+
register_context(ctx, :libusb_free_platform_descriptor)
|
250
|
+
end
|
251
|
+
|
252
|
+
# Reserved field
|
253
|
+
def bReserved
|
254
|
+
self[:bReserved]
|
255
|
+
end
|
256
|
+
|
257
|
+
# @return [String] 128 bit UUID
|
258
|
+
def platformCapabilityUUID
|
259
|
+
self[:PlatformCapabilityUUID].to_ptr.read_bytes(16)
|
260
|
+
end
|
261
|
+
|
262
|
+
# This is a variable-length field containing data associated with the platform specific capability.
|
263
|
+
# This field may be zero bytes in length.
|
264
|
+
# @return [String]
|
265
|
+
def capabilityData
|
266
|
+
self[:CapabilityData].to_ptr.read_bytes(bLength - 20)
|
267
|
+
end
|
268
|
+
|
269
|
+
def inspect
|
270
|
+
"\#<#{self.class} #{platformCapabilityUUID.unpack1("H*")} (#{capabilityData.unpack1("H*")})>"
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
include ContextReference
|
275
|
+
|
276
|
+
def initialize(ctx, *args)
|
277
|
+
@ctx = ctx
|
278
|
+
super(*args)
|
279
|
+
|
280
|
+
register_context(ctx, :libusb_free_bos_descriptor)
|
281
|
+
end
|
282
|
+
|
283
|
+
layout :bLength, :uint8,
|
284
|
+
:bDescriptorType, :uint8,
|
285
|
+
:wTotalLength, :uint16,
|
286
|
+
:bNumDeviceCaps, :uint8,
|
287
|
+
:dev_capability, [:pointer, 0]
|
288
|
+
|
289
|
+
# @return [Integer] Size of this descriptor (in bytes)
|
290
|
+
def bLength
|
291
|
+
self[:bLength]
|
292
|
+
end
|
293
|
+
|
294
|
+
# @return [Integer] Descriptor type. Will have value LIBUSB::DT_BOS LIBUSB_DT_BOS
|
295
|
+
# in this context.
|
296
|
+
def bDescriptorType
|
297
|
+
self[:bDescriptorType]
|
298
|
+
end
|
299
|
+
|
300
|
+
# @return [Integer] Length of this descriptor and all of its sub descriptors
|
301
|
+
def wTotalLength
|
302
|
+
self[:wTotalLength]
|
303
|
+
end
|
304
|
+
|
305
|
+
# @return [Integer] The number of separate device capability descriptors in
|
306
|
+
# the BOS
|
307
|
+
def bNumDeviceCaps
|
308
|
+
self[:bNumDeviceCaps]
|
309
|
+
end
|
310
|
+
|
311
|
+
# bNumDeviceCap Device Capability Descriptors
|
312
|
+
#
|
313
|
+
# @return [Array<Bos::DeviceCapability, Bos::Usb20Extension, Bos::SsUsbDeviceCapability, Bos::ContainerId>]
|
314
|
+
def device_capabilities
|
315
|
+
pp_ext = FFI::MemoryPointer.new :pointer
|
316
|
+
caps = []
|
317
|
+
# Capabilities are appended to the bos header
|
318
|
+
ptr = pointer + offset_of(:dev_capability)
|
319
|
+
bNumDeviceCaps.times do
|
320
|
+
cap = DeviceCapability.new self, ptr.read_pointer
|
321
|
+
case cap.bDevCapabilityType
|
322
|
+
when LIBUSB::BT_WIRELESS_USB_DEVICE_CAPABILITY
|
323
|
+
# no struct defined in libusb -> use generic DeviceCapability
|
324
|
+
when LIBUSB::BT_USB_2_0_EXTENSION
|
325
|
+
res = Call.libusb_get_usb_2_0_extension_descriptor(@ctx, cap.pointer, pp_ext)
|
326
|
+
cap = Usb20Extension.new(@ctx, pp_ext.read_pointer) if res==0
|
327
|
+
when LIBUSB::BT_SS_USB_DEVICE_CAPABILITY
|
328
|
+
res = Call.libusb_get_ss_usb_device_capability_descriptor(@ctx, cap.pointer, pp_ext)
|
329
|
+
cap = SsUsbDeviceCapability.new(@ctx, pp_ext.read_pointer) if res==0
|
330
|
+
when LIBUSB::BT_CONTAINER_ID
|
331
|
+
res = Call.libusb_get_container_id_descriptor(@ctx, cap.pointer, pp_ext)
|
332
|
+
cap = ContainerId.new(@ctx, pp_ext.read_pointer) if res==0
|
333
|
+
when LIBUSB::BT_PLATFORM_DESCRIPTOR
|
334
|
+
res = Call.libusb_get_platform_descriptor(@ctx, cap.pointer, pp_ext)
|
335
|
+
cap = PlatformDescriptor.new(@ctx, pp_ext.read_pointer) if res==0
|
336
|
+
else
|
337
|
+
# unknown capability -> use generic DeviceCapability
|
338
|
+
end
|
339
|
+
ptr += FFI.type_size(:pointer)
|
340
|
+
caps << cap
|
341
|
+
end
|
342
|
+
caps
|
343
|
+
end
|
344
|
+
|
345
|
+
# @return [Array<Symbol>] Types of Capabilities
|
346
|
+
#
|
347
|
+
# @see Call::BosTypes
|
348
|
+
def device_capability_types
|
349
|
+
# Capabilities are appended to the bos header
|
350
|
+
ptr = pointer + offset_of(:dev_capability)
|
351
|
+
bNumDeviceCaps.times.map do
|
352
|
+
cap = DeviceCapability.new self, ptr.read_pointer
|
353
|
+
ptr += FFI.type_size(:pointer)
|
354
|
+
Call::BosTypes.find cap.bDevCapabilityType
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
def inspect
|
359
|
+
"\#<#{self.class} #{device_capability_types.join(", ")}>"
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|