libusb 0.6.0-x86-linux

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.travis.yml +17 -0
  4. data/.yardopts +6 -0
  5. data/COPYING +165 -0
  6. data/Gemfile +11 -0
  7. data/History.md +124 -0
  8. data/README.md +159 -0
  9. data/Rakefile +145 -0
  10. data/appveyor.yml +23 -0
  11. data/lib/libusb.rb +58 -0
  12. data/lib/libusb/bos.rb +306 -0
  13. data/lib/libusb/call.rb +446 -0
  14. data/lib/libusb/compat.rb +376 -0
  15. data/lib/libusb/configuration.rb +155 -0
  16. data/lib/libusb/constants.rb +160 -0
  17. data/lib/libusb/context.rb +426 -0
  18. data/lib/libusb/dependencies.rb +7 -0
  19. data/lib/libusb/dev_handle.rb +564 -0
  20. data/lib/libusb/device.rb +365 -0
  21. data/lib/libusb/endpoint.rb +194 -0
  22. data/lib/libusb/eventmachine.rb +183 -0
  23. data/lib/libusb/interface.rb +60 -0
  24. data/lib/libusb/setting.rb +132 -0
  25. data/lib/libusb/ss_companion.rb +69 -0
  26. data/lib/libusb/stdio.rb +25 -0
  27. data/lib/libusb/transfer.rb +377 -0
  28. data/lib/libusb/version_gem.rb +19 -0
  29. data/lib/libusb/version_struct.rb +63 -0
  30. data/libusb.gemspec +30 -0
  31. data/test/test_libusb_bos.rb +118 -0
  32. data/test/test_libusb_bulk_stream_transfer.rb +50 -0
  33. data/test/test_libusb_capability.rb +23 -0
  34. data/test/test_libusb_compat.rb +78 -0
  35. data/test/test_libusb_compat_mass_storage.rb +81 -0
  36. data/test/test_libusb_descriptors.rb +212 -0
  37. data/test/test_libusb_event_machine.rb +118 -0
  38. data/test/test_libusb_gc.rb +37 -0
  39. data/test/test_libusb_hotplug.rb +127 -0
  40. data/test/test_libusb_iso_transfer.rb +50 -0
  41. data/test/test_libusb_mass_storage.rb +268 -0
  42. data/test/test_libusb_mass_storage2.rb +96 -0
  43. data/test/test_libusb_structs.rb +58 -0
  44. data/test/test_libusb_threads.rb +89 -0
  45. data/test/test_libusb_version.rb +40 -0
  46. data/wireshark-usb-sniffer.png +0 -0
  47. metadata +150 -0
data/Rakefile ADDED
@@ -0,0 +1,145 @@
1
+ # -*- coding: utf-8 -*-
2
+ # -*- ruby -*-
3
+
4
+ require 'bundler/gem_tasks'
5
+ require 'rubygems/package_task'
6
+ require 'pathname'
7
+ require 'uri'
8
+ require 'ostruct'
9
+ require 'rake/clean'
10
+ require 'rake_compiler_dock'
11
+ require_relative 'ext/libusb_recipe'
12
+
13
+ task :gem => :build
14
+ task :compile do
15
+ sh "ruby ext/extconf.rb --disable-system-libusb"
16
+ end
17
+
18
+ task :test=>:compile do
19
+ sh "ruby -w -W2 -I. -Ilib -e \"#{Dir["test/test_*.rb"].map{|f| "require '#{f}';"}.join}\" -- -v"
20
+ end
21
+
22
+ travis_tests = %w[test_libusb_capability.rb test_libusb_structs.rb test_libusb_version.rb]
23
+ task :travis=>:compile do
24
+ sh "ruby -w -W2 -I. -Ilib -e \"#{travis_tests.map{|f| "require 'test/#{f}';"}.join}\" -- -v"
25
+ end
26
+ task :default => :test
27
+
28
+ task "release:tag" do
29
+ hfile = "History.md"
30
+ version = LIBUSB::VERSION
31
+ reldate = Time.now.strftime("%Y-%m-%d")
32
+ headline = '([^\w]*)(\d+\.\d+\.\d+)([^\w]+)([2Y][0Y][0-9Y][0-9Y]-[0-1M][0-9M]-[0-3D][0-9D])([^\w]*|$)'
33
+
34
+ hin = File.read(hfile)
35
+ hout = hin.sub(/#{headline}/) do
36
+ raise "#{hfile} isn't up-to-date for version #{version}" unless $2==version
37
+ $1 + $2 + $3 + reldate + $5
38
+ end
39
+ if hout != hin
40
+ Bundler.ui.confirm "Updating #{hfile} for release."
41
+ File.write(hfile, hout)
42
+ sh "git", "commit", hfile, "-m", "Update release date in #{hfile}"
43
+ end
44
+
45
+ Bundler.ui.confirm "Tag release with annotation:"
46
+ m = hout.match(/(?<annotation>#{headline}.*?)#{headline}/m) || raise("Unable to find release notes in #{hfile}")
47
+ Bundler.ui.info(m[:annotation].gsub(/^/, " "))
48
+ IO.popen(["git", "tag", "--file=-", version], "w") do |fd|
49
+ fd.write m[:annotation]
50
+ end
51
+ end
52
+
53
+ task "release:guard_clean" => "release:tag"
54
+
55
+ task "release:rubygem_push" => "gem:native" do
56
+ CrossLibraries.each do |ruby_platform, _|
57
+ gh = Bundler::GemHelper.new
58
+ gh.rubygem_push(gh.spec_path.gsub(".gem", "-#{ruby_platform}.gem"))
59
+ end
60
+ end
61
+
62
+ task 'gem:native' do
63
+ sh "bundle package"
64
+ RakeCompilerDock.sh <<-EOT
65
+ bundle --local &&
66
+ rake cross gem
67
+ EOT
68
+ end
69
+
70
+ class CrossLibrary < OpenStruct
71
+ include Rake::DSL
72
+
73
+ def initialize(ruby_platform, host_platform, libusb_dllname)
74
+ super()
75
+
76
+ self.ruby_platform = ruby_platform
77
+ self.recipe = LibusbRecipe.new
78
+ recipe.host = host_platform
79
+ recipe.configure_options << "--host=#{recipe.host}"
80
+ self.libusb_dll = Pathname.new(recipe.path) + libusb_dllname
81
+
82
+ file libusb_dll do
83
+ recipe.cook
84
+ end
85
+
86
+ task "libusb_dll:#{ruby_platform}" => libusb_dll
87
+
88
+ desc 'Cross compile libusb for win32'
89
+ task :cross => [ "libusb_dll:#{ruby_platform}" ] do |t|
90
+ spec = Gem::Specification::load("libusb.gemspec").dup
91
+ spec.platform = Gem::Platform.new(ruby_platform)
92
+ spec.extensions = []
93
+
94
+ # Remove files unnecessary for native gems
95
+ spec.files -= `git ls-files ext`.split("\n")
96
+ spec.files.reject!{|f| f.start_with?('ports') }
97
+ spec_text_files = spec.files.dup
98
+
99
+ # Add native libusb-dll
100
+ spec.files << "lib/#{libusb_dll.basename}"
101
+
102
+ # MiniPortile isn't required for native gems
103
+ spec.dependencies.reject!{|d| d.name=="mini_portile2" }
104
+
105
+ # Generate a package for this gem
106
+ pkg = Gem::PackageTask.new(spec) do |pkg|
107
+ pkg.need_zip = false
108
+ pkg.need_tar = false
109
+ # Do not copy any files per PackageTask, because
110
+ # we need the files from the platform specific directory
111
+ pkg.package_files.clear
112
+ end
113
+
114
+ # copy files of the gem to pkg directory
115
+ file pkg.package_dir_path => spec_text_files do
116
+ spec_text_files.each do |fn|
117
+ f = File.join(pkg.package_dir_path, fn)
118
+ fdir = File.dirname(f)
119
+ mkdir_p(fdir) if !File.exist?(fdir)
120
+ rm_f f
121
+ safe_ln(fn, f)
122
+ end
123
+
124
+ # copy libusb.dll to pkg directory
125
+ f = "#{pkg.package_dir_path}/lib/#{libusb_dll.basename}"
126
+ mkdir_p File.dirname(f)
127
+ rm_f f
128
+ safe_ln libusb_dll.realpath, f
129
+ end
130
+
131
+ file "lib/#{libusb_dll.basename}" => [libusb_dll]
132
+ end
133
+ end
134
+ end
135
+
136
+ CrossLibraries = [
137
+ ['i386-mingw32', 'i686-w64-mingw32', 'bin/libusb-1.0.dll'],
138
+ ['x64-mingw32', 'x86_64-w64-mingw32', 'bin/libusb-1.0.dll'],
139
+ ['x86-linux', 'i686-linux-gnu', 'lib/libusb-1.0.so'],
140
+ ['x86_64-linux', 'x86_64-linux-gnu', 'lib/libusb-1.0.so'],
141
+ ].each do |ruby_platform, host_platform, libusb_dll|
142
+ CrossLibrary.new ruby_platform, host_platform, libusb_dll
143
+ end
144
+
145
+ # vim: syntax=ruby
data/appveyor.yml ADDED
@@ -0,0 +1,23 @@
1
+ init:
2
+ - SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
3
+ - SET PATH=C:\MinGW\msys\1.0\bin;%PATH%
4
+ - SET RAKEOPT=-rdevkit
5
+ install:
6
+ - ruby --version
7
+ - gem --version
8
+ - bundle install
9
+ build_script:
10
+ - bundle exec rake compile
11
+ test_script:
12
+ - bundle exec rake travis
13
+ environment:
14
+ matrix:
15
+ - ruby_version: "193"
16
+ - ruby_version: "200"
17
+ - ruby_version: "200-x64"
18
+ - ruby_version: "21"
19
+ - ruby_version: "21-x64"
20
+ - ruby_version: "22"
21
+ - ruby_version: "22-x64"
22
+ - ruby_version: "23"
23
+ - ruby_version: "23-x64"
data/lib/libusb.rb ADDED
@@ -0,0 +1,58 @@
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
+ module LIBUSB
17
+ require 'libusb/call'
18
+ require 'libusb/constants'
19
+ require 'libusb/context'
20
+ autoload :VERSION, 'libusb/version_gem'
21
+ autoload :Version, 'libusb/version_struct'
22
+ autoload :Configuration, 'libusb/configuration'
23
+ autoload :DevHandle, 'libusb/dev_handle'
24
+ autoload :Device, 'libusb/device'
25
+ autoload :Endpoint, 'libusb/endpoint'
26
+ autoload :Interface, 'libusb/interface'
27
+ autoload :Setting, 'libusb/setting'
28
+ autoload :SsCompanion, 'libusb/ss_companion'
29
+ autoload :Stdio, 'libusb/stdio'
30
+ autoload :Bos, 'libusb/bos'
31
+ %w[ Transfer BulkTransfer BulkStreamTransfer ControlTransfer InterruptTransfer IsoPacket IsochronousTransfer ].each do |klass|
32
+ autoload klass, 'libusb/transfer'
33
+ end
34
+
35
+ if Call.respond_to?(:libusb_get_version)
36
+ # Get version of the underlying libusb library.
37
+ # Available since libusb-1.0.10.
38
+ # @return [Version] version object
39
+ def self.version
40
+ Version.new(Call.libusb_get_version)
41
+ end
42
+ end
43
+
44
+ if Call.respond_to?(:libusb_has_capability)
45
+ # Check at runtime if the loaded library has a given capability.
46
+ # Available since libusb-1.0.9.
47
+ # @param [Symbol] capability the {Call::Capabilities Capabilities} symbol to check for
48
+ # @return [Boolean] +true+ if the running library has the capability, +false+ otherwise
49
+ def self.has_capability?(capability)
50
+ r = Call.libusb_has_capability(capability)
51
+ return r != 0
52
+ end
53
+ else
54
+ def self.has_capability?(capability)
55
+ false
56
+ end
57
+ end
58
+ end
data/lib/libusb/bos.rb ADDED
@@ -0,0 +1,306 @@
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::ManagedStruct
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.unpack("H*")[0]}>"
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::ManagedStruct
70
+ include GenericMethods
71
+
72
+ layout :bLength, :uint8,
73
+ :bDescriptorType, :uint8,
74
+ :bDevCapabilityType, :uint8,
75
+ :bmAttributes, :uint32
76
+
77
+ # Bitmap encoding of supported device level features.
78
+ # A value of one in a bit location indicates a feature is
79
+ # supported; a value of zero indicates it is not supported.
80
+ # @see Call::Usb20ExtensionAttributes
81
+ def bmAttributes
82
+ self[:bmAttributes]
83
+ end
84
+
85
+ # @return [Boolean] Supports Link Power Management (LPM)
86
+ def bm_lpm_support?
87
+ (bmAttributes & BM_LPM_SUPPORT) != 0
88
+ end
89
+
90
+ def inspect
91
+ attrs = Call::Usb20ExtensionAttributes.to_h.map do |k, v|
92
+ (bmAttributes & v) ? k.to_s : nil
93
+ end
94
+ "\#<#{self.class} #{attrs.compact.join(",")}>"
95
+ end
96
+
97
+ # @private
98
+ def self.release(ptr)
99
+ Call.libusb_free_usb_2_0_extension_descriptor(ptr)
100
+ end
101
+ end
102
+
103
+ # A structure representing the SuperSpeed USB Device Capability descriptor
104
+ # This descriptor is documented in section 9.6.2.2 of the USB 3.0 specification.
105
+ # All multiple-byte fields are represented in host-endian format.
106
+ class SsUsbDeviceCapability < FFI::ManagedStruct
107
+ include GenericMethods
108
+
109
+ layout :bLength, :uint8,
110
+ :bDescriptorType, :uint8,
111
+ :bDevCapabilityType, :uint8,
112
+ :bmAttributes, :uint32,
113
+ :wSpeedSupported, :uint16,
114
+ :bFunctionalitySupport, :uint8,
115
+ :bU1DevExitLat, :uint8,
116
+ :bU2DevExitLat, :uint16
117
+
118
+ # Bitmap encoding of supported device level features.
119
+ # A value of one in a bit location indicates a feature is
120
+ # supported; a value of zero indicates it is not supported.
121
+ #
122
+ # @return [Integer]
123
+ # @see Call::SsUsbDeviceCapabilityAttributes
124
+ def bmAttributes
125
+ self[:bmAttributes]
126
+ end
127
+
128
+ # @return [Boolean] Supports Latency Tolerance Messages (LTM)
129
+ def bm_ltm_support?
130
+ (bmAttributes & BM_LTM_SUPPORT) != 0
131
+ end
132
+
133
+ def inspect
134
+ attrs = Call::SsUsbDeviceCapabilityAttributes.to_h.map do |k,v|
135
+ (bmAttributes & v) != 0 ? k.to_s : nil
136
+ end
137
+ "\#<#{self.class} #{attrs.compact.join(",")} #{supported_speeds.join(",")}>"
138
+ end
139
+
140
+ # Bitmap encoding of the speed supported by this device when
141
+ # operating in SuperSpeed mode.
142
+ #
143
+ # @return [Integer]
144
+ # @see Call::SupportedSpeeds
145
+ def wSpeedSupported
146
+ self[:wSpeedSupported]
147
+ end
148
+
149
+ # @return [Array<Symbol>] speeds supported by this device when
150
+ # operating in SuperSpeed mode {Call::SupportedSpeeds}
151
+ def supported_speeds
152
+ speeds = Call::SupportedSpeeds.to_h.map do |k,v|
153
+ (wSpeedSupported & v) != 0 ? k : nil
154
+ end
155
+ speeds.compact
156
+ end
157
+
158
+ # The lowest speed at which all the functionality supported
159
+ # by the device is available to the user. For example if the
160
+ # device supports all its functionality when connected at
161
+ # full speed and above then it sets this value to 1.
162
+ #
163
+ # 0 - low speed
164
+ # 1 - full speed
165
+ # 2 - high speed
166
+ # 3 - super speed
167
+ # @return [Integer]
168
+ def bFunctionalitySupport
169
+ self[:bFunctionalitySupport]
170
+ end
171
+
172
+ # @return [Integer] U1 Device Exit Latency.
173
+ def bU1DevExitLat
174
+ self[:bU1DevExitLat]
175
+ end
176
+
177
+ # @return [Integer] U2 Device Exit Latency.
178
+ def bU2DevExitLat
179
+ self[:bU2DevExitLat]
180
+ end
181
+
182
+ # @private
183
+ def self.release(ptr)
184
+ Call.libusb_free_ss_usb_device_capability_descriptor(ptr)
185
+ end
186
+ end
187
+
188
+ # A structure representing the Container ID descriptor.
189
+ # This descriptor is documented in section 9.6.2.3 of the USB 3.0 specification.
190
+ # All multiple-byte fields, except UUIDs, are represented in host-endian format.
191
+ class ContainerId < FFI::ManagedStruct
192
+ include GenericMethods
193
+
194
+ layout :bLength, :uint8,
195
+ :bDescriptorType, :uint8,
196
+ :bDevCapabilityType, :uint8,
197
+ :bReserved, :uint8,
198
+ :ContainerID, [:uint8, 16]
199
+
200
+ # Reserved field
201
+ def bReserved
202
+ self[:bReserved]
203
+ end
204
+
205
+ # @return [String] 128 bit UUID
206
+ def container_id
207
+ self[:ContainerID].to_ptr.read_bytes(16)
208
+ end
209
+
210
+ def inspect
211
+ "\#<#{self.class} #{container_id.unpack("H*")[0]}>"
212
+ end
213
+
214
+ # @private
215
+ def self.release(ptr)
216
+ Call.libusb_free_container_id_descriptor(ptr)
217
+ end
218
+ end
219
+
220
+ def initialize( ctx, *args)
221
+ @ctx = ctx
222
+ super(*args)
223
+ end
224
+
225
+ layout :bLength, :uint8,
226
+ :bDescriptorType, :uint8,
227
+ :wTotalLength, :uint16,
228
+ :bNumDeviceCaps, :uint8,
229
+ :dev_capability, [:pointer, 0]
230
+
231
+ # @return [Integer] Size of this descriptor (in bytes)
232
+ def bLength
233
+ self[:bLength]
234
+ end
235
+
236
+ # @return [Integer] Descriptor type. Will have value LIBUSB::DT_BOS LIBUSB_DT_BOS
237
+ # in this context.
238
+ def bDescriptorType
239
+ self[:bDescriptorType]
240
+ end
241
+
242
+ # @return [Integer] Length of this descriptor and all of its sub descriptors
243
+ def wTotalLength
244
+ self[:wTotalLength]
245
+ end
246
+
247
+ # @return [Integer] The number of separate device capability descriptors in
248
+ # the BOS
249
+ def bNumDeviceCaps
250
+ self[:bNumDeviceCaps]
251
+ end
252
+
253
+ # bNumDeviceCap Device Capability Descriptors
254
+ #
255
+ # @return [Array<Bos::DeviceCapability, Bos::Usb20Extension, Bos::SsUsbDeviceCapability, Bos::ContainerId>]
256
+ def device_capabilities
257
+ pp_ext = FFI::MemoryPointer.new :pointer
258
+ caps = []
259
+ # Capabilities are appended to the bos header
260
+ ptr = pointer + offset_of(:dev_capability)
261
+ bNumDeviceCaps.times do
262
+ cap = DeviceCapability.new self, ptr.read_pointer
263
+ case cap.bDevCapabilityType
264
+ when LIBUSB::BT_WIRELESS_USB_DEVICE_CAPABILITY
265
+ # no struct defined in libusb -> use generic DeviceCapability
266
+ when LIBUSB::BT_USB_2_0_EXTENSION
267
+ res = Call.libusb_get_usb_2_0_extension_descriptor(@ctx, cap.pointer, pp_ext)
268
+ cap = Usb20Extension.new(pp_ext.read_pointer) if res==0
269
+ when LIBUSB::BT_SS_USB_DEVICE_CAPABILITY
270
+ res = Call.libusb_get_ss_usb_device_capability_descriptor(@ctx, cap.pointer, pp_ext)
271
+ cap = SsUsbDeviceCapability.new(pp_ext.read_pointer) if res==0
272
+ when LIBUSB::BT_CONTAINER_ID
273
+ res = Call.libusb_get_container_id_descriptor(@ctx, cap.pointer, pp_ext)
274
+ cap = ContainerId.new(pp_ext.read_pointer) if res==0
275
+ else
276
+ # unknown capability -> use generic DeviceCapability
277
+ end
278
+ ptr += FFI.type_size(:pointer)
279
+ caps << cap
280
+ end
281
+ caps
282
+ end
283
+
284
+ # @return [Array<Symbol>] Types of Capabilities
285
+ #
286
+ # @see Call::BosTypes
287
+ def device_capability_types
288
+ # Capabilities are appended to the bos header
289
+ ptr = pointer + offset_of(:dev_capability)
290
+ bNumDeviceCaps.times.map do
291
+ cap = DeviceCapability.new self, ptr.read_pointer
292
+ ptr += FFI.type_size(:pointer)
293
+ Call::BosTypes.find cap.bDevCapabilityType
294
+ end
295
+ end
296
+
297
+ def inspect
298
+ "\#<#{self.class} #{device_capability_types.join(", ")}>"
299
+ end
300
+
301
+ # @private
302
+ def self.release(ptr)
303
+ Call.libusb_free_bos_descriptor(ptr)
304
+ end
305
+ end
306
+ end