libusb 0.7.0-x64-mingw-ucrt

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.appveyor.yml +33 -0
  3. data/.github/workflows/ci.yml +185 -0
  4. data/.gitignore +9 -0
  5. data/.travis.yml +26 -0
  6. data/.yardopts +6 -0
  7. data/COPYING +165 -0
  8. data/Gemfile +19 -0
  9. data/History.md +193 -0
  10. data/README.md +184 -0
  11. data/Rakefile +79 -0
  12. data/lib/libusb/bos.rb +362 -0
  13. data/lib/libusb/call.rb +622 -0
  14. data/lib/libusb/compat.rb +376 -0
  15. data/lib/libusb/configuration.rb +154 -0
  16. data/lib/libusb/constants.rb +170 -0
  17. data/lib/libusb/context.rb +576 -0
  18. data/lib/libusb/context_reference.rb +38 -0
  19. data/lib/libusb/dependencies.rb +7 -0
  20. data/lib/libusb/dev_handle.rb +574 -0
  21. data/lib/libusb/device.rb +407 -0
  22. data/lib/libusb/endpoint.rb +195 -0
  23. data/lib/libusb/eventmachine.rb +187 -0
  24. data/lib/libusb/gem_helper.rb +151 -0
  25. data/lib/libusb/interface.rb +60 -0
  26. data/lib/libusb/libusb_recipe.rb +29 -0
  27. data/lib/libusb/setting.rb +132 -0
  28. data/lib/libusb/ss_companion.rb +72 -0
  29. data/lib/libusb/stdio.rb +25 -0
  30. data/lib/libusb/transfer.rb +418 -0
  31. data/lib/libusb/version_gem.rb +19 -0
  32. data/lib/libusb/version_struct.rb +63 -0
  33. data/lib/libusb-1.0.dll +0 -0
  34. data/lib/libusb.rb +146 -0
  35. data/libusb.gemspec +28 -0
  36. data/test/test_libusb.rb +42 -0
  37. data/test/test_libusb_bos.rb +140 -0
  38. data/test/test_libusb_bulk_stream_transfer.rb +61 -0
  39. data/test/test_libusb_compat.rb +78 -0
  40. data/test/test_libusb_compat_mass_storage.rb +81 -0
  41. data/test/test_libusb_context.rb +88 -0
  42. data/test/test_libusb_descriptors.rb +245 -0
  43. data/test/test_libusb_event_machine.rb +118 -0
  44. data/test/test_libusb_gc.rb +52 -0
  45. data/test/test_libusb_hotplug.rb +129 -0
  46. data/test/test_libusb_iso_transfer.rb +56 -0
  47. data/test/test_libusb_mass_storage.rb +268 -0
  48. data/test/test_libusb_mass_storage2.rb +96 -0
  49. data/test/test_libusb_structs.rb +87 -0
  50. data/test/test_libusb_threads.rb +89 -0
  51. data/wireshark-usb-sniffer.png +0 -0
  52. 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