libusb 0.7.0-x64-mingw-ucrt
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
@@ -0,0 +1,376 @@
|
|
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
|
+
# This file is for compatibility with ruby-usb and libusb-0.1.
|
17
|
+
#
|
18
|
+
# Please visit the project website at http://github.com/larskanis/libusb
|
19
|
+
# for support.
|
20
|
+
|
21
|
+
require 'libusb'
|
22
|
+
require 'forwardable'
|
23
|
+
|
24
|
+
# Compatibility layer for ruby-usb[http://www.a-k-r.org/ruby-usb/] (API based on libusb-0.1)
|
25
|
+
#
|
26
|
+
# This module provides some limited compatibility to ruby-usb.
|
27
|
+
#
|
28
|
+
# Usage example:
|
29
|
+
# begin
|
30
|
+
# require 'usb'
|
31
|
+
# rescue LoadError
|
32
|
+
# require 'libusb/compat'
|
33
|
+
# end
|
34
|
+
# p USB.devices => [#<USB::Device ...>]
|
35
|
+
#
|
36
|
+
# Known issues:
|
37
|
+
# * Exceptions are different between ruby-usb and libusb and they don't get converted
|
38
|
+
# * libusb-1.0 doesn't explicitly manage USB-buses, so only one Bus is used currently
|
39
|
+
module USB
|
40
|
+
@default_context = nil
|
41
|
+
def self.default_context
|
42
|
+
@default_context ||= LIBUSB::Context.new
|
43
|
+
end
|
44
|
+
|
45
|
+
@default_bus = nil
|
46
|
+
def self.default_bus
|
47
|
+
@default_bus ||= Bus.new(default_context)
|
48
|
+
end
|
49
|
+
|
50
|
+
USB_CLASS_PER_INTERFACE = LIBUSB::CLASS_PER_INTERFACE
|
51
|
+
USB_CLASS_AUDIO = LIBUSB::CLASS_AUDIO
|
52
|
+
USB_CLASS_COMM = LIBUSB::CLASS_COMM
|
53
|
+
USB_CLASS_HID = LIBUSB::CLASS_HID
|
54
|
+
USB_CLASS_PRINTER = LIBUSB::CLASS_PRINTER
|
55
|
+
USB_CLASS_PTP = LIBUSB::CLASS_PTP
|
56
|
+
USB_CLASS_MASS_STORAGE = LIBUSB::CLASS_MASS_STORAGE
|
57
|
+
USB_CLASS_HUB = LIBUSB::CLASS_HUB
|
58
|
+
USB_CLASS_DATA = LIBUSB::CLASS_DATA
|
59
|
+
USB_CLASS_VENDOR_SPEC = LIBUSB::CLASS_VENDOR_SPEC
|
60
|
+
|
61
|
+
USB_DT_DEVICE = LIBUSB::DT_DEVICE
|
62
|
+
USB_DT_CONFIG = LIBUSB::DT_CONFIG
|
63
|
+
USB_DT_STRING = LIBUSB::DT_STRING
|
64
|
+
USB_DT_INTERFACE = LIBUSB::DT_INTERFACE
|
65
|
+
USB_DT_ENDPOINT = LIBUSB::DT_ENDPOINT
|
66
|
+
USB_DT_HID = LIBUSB::DT_HID
|
67
|
+
USB_DT_REPORT = LIBUSB::DT_REPORT
|
68
|
+
USB_DT_PHYSICAL = LIBUSB::DT_PHYSICAL
|
69
|
+
USB_DT_HUB = LIBUSB::DT_HUB
|
70
|
+
USB_DT_DEVICE_SIZE = LIBUSB::DT_DEVICE_SIZE
|
71
|
+
USB_DT_CONFIG_SIZE = LIBUSB::DT_CONFIG_SIZE
|
72
|
+
USB_DT_INTERFACE_SIZE = LIBUSB::DT_INTERFACE_SIZE
|
73
|
+
USB_DT_ENDPOINT_SIZE = LIBUSB::DT_ENDPOINT_SIZE
|
74
|
+
USB_DT_ENDPOINT_AUDIO_SIZE = LIBUSB::DT_ENDPOINT_AUDIO_SIZE
|
75
|
+
USB_DT_HUB_NONVAR_SIZE = LIBUSB::DT_HUB_NONVAR_SIZE
|
76
|
+
|
77
|
+
USB_ENDPOINT_ADDRESS_MASK = LIBUSB::ENDPOINT_ADDRESS_MASK
|
78
|
+
USB_ENDPOINT_DIR_MASK = LIBUSB::ENDPOINT_DIR_MASK
|
79
|
+
USB_ENDPOINT_IN = LIBUSB::ENDPOINT_IN
|
80
|
+
USB_ENDPOINT_OUT = LIBUSB::ENDPOINT_OUT
|
81
|
+
|
82
|
+
USB_ENDPOINT_TYPE_MASK = LIBUSB::TRANSFER_TYPE_MASK
|
83
|
+
USB_ENDPOINT_TYPE_CONTROL = LIBUSB::TRANSFER_TYPE_CONTROL
|
84
|
+
USB_ENDPOINT_TYPE_ISOCHRONOUS = LIBUSB::TRANSFER_TYPE_ISOCHRONOUS
|
85
|
+
USB_ENDPOINT_TYPE_BULK = LIBUSB::TRANSFER_TYPE_BULK
|
86
|
+
USB_ENDPOINT_TYPE_INTERRUPT = LIBUSB::TRANSFER_TYPE_INTERRUPT
|
87
|
+
|
88
|
+
USB_REQ_GET_STATUS = LIBUSB::REQUEST_GET_STATUS
|
89
|
+
USB_REQ_CLEAR_FEATURE = LIBUSB::REQUEST_CLEAR_FEATURE
|
90
|
+
USB_REQ_SET_FEATURE = LIBUSB::REQUEST_SET_FEATURE
|
91
|
+
USB_REQ_SET_ADDRESS = LIBUSB::REQUEST_SET_ADDRESS
|
92
|
+
USB_REQ_GET_DESCRIPTOR = LIBUSB::REQUEST_GET_DESCRIPTOR
|
93
|
+
USB_REQ_SET_DESCRIPTOR = LIBUSB::REQUEST_SET_DESCRIPTOR
|
94
|
+
USB_REQ_GET_CONFIGURATION = LIBUSB::REQUEST_GET_CONFIGURATION
|
95
|
+
USB_REQ_SET_CONFIGURATION = LIBUSB::REQUEST_SET_CONFIGURATION
|
96
|
+
USB_REQ_GET_INTERFACE = LIBUSB::REQUEST_GET_INTERFACE
|
97
|
+
USB_REQ_SET_INTERFACE = LIBUSB::REQUEST_SET_INTERFACE
|
98
|
+
USB_REQ_SYNCH_FRAME = LIBUSB::REQUEST_SYNCH_FRAME
|
99
|
+
USB_TYPE_STANDARD = LIBUSB::REQUEST_TYPE_STANDARD
|
100
|
+
USB_TYPE_CLASS = LIBUSB::REQUEST_TYPE_CLASS
|
101
|
+
USB_TYPE_VENDOR = LIBUSB::REQUEST_TYPE_VENDOR
|
102
|
+
USB_TYPE_RESERVED = LIBUSB::REQUEST_TYPE_RESERVED
|
103
|
+
USB_RECIP_DEVICE = LIBUSB::RECIPIENT_DEVICE
|
104
|
+
USB_RECIP_INTERFACE = LIBUSB::RECIPIENT_INTERFACE
|
105
|
+
USB_RECIP_ENDPOINT = LIBUSB::RECIPIENT_ENDPOINT
|
106
|
+
USB_RECIP_OTHER = LIBUSB::RECIPIENT_OTHER
|
107
|
+
|
108
|
+
HAS_GET_DRIVER_NP = !FFI::Platform.windows?
|
109
|
+
HAS_DETACH_KERNEL_DRIVER_NP = !FFI::Platform.windows?
|
110
|
+
|
111
|
+
# not defined by libusb-1.0, but typical values are:
|
112
|
+
USB_MAXENDPOINTS = 32
|
113
|
+
USB_MAXINTERFACES = 32
|
114
|
+
USB_MAXALTSETTING = 128
|
115
|
+
USB_MAXCONFIG = 8
|
116
|
+
|
117
|
+
|
118
|
+
def USB.busses
|
119
|
+
[default_bus]
|
120
|
+
end
|
121
|
+
|
122
|
+
def USB.devices; default_context.devices.map{|c| Device.new(c) }; end
|
123
|
+
def USB.configurations() USB.devices.map {|d| d.configurations }.flatten end
|
124
|
+
def USB.interfaces() USB.configurations.map {|d| d.interfaces }.flatten end
|
125
|
+
def USB.settings() USB.interfaces.map {|d| d.settings }.flatten end
|
126
|
+
def USB.endpoints() USB.settings.map {|d| d.endpoints }.flatten end
|
127
|
+
|
128
|
+
def USB.find_bus(n)
|
129
|
+
default_bus
|
130
|
+
end
|
131
|
+
|
132
|
+
def USB.each_device_by_class(devclass, subclass=nil, protocol=nil)
|
133
|
+
devs = default_context.devices bClass: devclass, bSubClass: subclass, bProtocol: protocol
|
134
|
+
devs.each do |dev|
|
135
|
+
yield Device.new(dev)
|
136
|
+
end
|
137
|
+
nil
|
138
|
+
end
|
139
|
+
|
140
|
+
class Bus
|
141
|
+
def initialize(context)
|
142
|
+
@ct = context
|
143
|
+
end
|
144
|
+
def devices
|
145
|
+
@ct.devices.map{|d| Device.new(d) }
|
146
|
+
end
|
147
|
+
|
148
|
+
def configurations() self.devices.map{|d| d.configurations }.flatten end
|
149
|
+
def interfaces() self.configurations.map {|d| d.interfaces }.flatten end
|
150
|
+
def settings() self.interfaces.map {|d| d.settings }.flatten end
|
151
|
+
def endpoints() self.settings.map {|d| d.endpoints }.flatten end
|
152
|
+
|
153
|
+
def find_device(n)
|
154
|
+
raise NotImplementedError
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def USB.dev_string(base_class, sub_class, protocol)
|
159
|
+
LIBUSB.dev_string(base_class, sub_class, protocol)
|
160
|
+
end
|
161
|
+
|
162
|
+
class Device
|
163
|
+
extend Forwardable
|
164
|
+
include Comparable
|
165
|
+
|
166
|
+
def initialize(dev)
|
167
|
+
@dev = dev
|
168
|
+
end
|
169
|
+
|
170
|
+
def_delegators :@dev, :bLength, :bDescriptorType, :bcdUSB, :bDeviceClass,
|
171
|
+
:bDeviceSubClass, :bDeviceProtocol, :bMaxPacketSize0, :idVendor, :idProduct,
|
172
|
+
:bcdDevice, :iManufacturer, :iProduct, :iSerialNumber, :bNumConfigurations,
|
173
|
+
:manufacturer, :product, :serial_number,
|
174
|
+
:inspect
|
175
|
+
|
176
|
+
def <=>(o)
|
177
|
+
@dev<=>o.instance_variable_get(:@dev)
|
178
|
+
end
|
179
|
+
|
180
|
+
def open
|
181
|
+
h = DevHandle.new(@dev.open)
|
182
|
+
if block_given?
|
183
|
+
begin
|
184
|
+
yield h
|
185
|
+
ensure
|
186
|
+
h.usb_close
|
187
|
+
end
|
188
|
+
else
|
189
|
+
h
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def bus; default_bus; end
|
194
|
+
def configurations; @dev.configurations.map{|c| Configuration.new(c) }; end
|
195
|
+
def interfaces; @dev.interfaces.map{|c| Interface.new(c) }; end
|
196
|
+
def settings; @dev.settings.map{|c| Setting.new(c) }; end
|
197
|
+
def endpoints; @dev.endpoints.map{|c| Endpoint.new(c) }; end
|
198
|
+
end
|
199
|
+
|
200
|
+
class Configuration
|
201
|
+
extend Forwardable
|
202
|
+
include Comparable
|
203
|
+
|
204
|
+
def initialize(cd)
|
205
|
+
@cd = cd
|
206
|
+
end
|
207
|
+
|
208
|
+
def_delegators :@cd, :bLength, :bDescriptorType, :wTotalLength, :bNumInterfaces,
|
209
|
+
:bConfigurationValue, :iConfiguration, :bmAttributes, :bMaxPower,
|
210
|
+
:inspect
|
211
|
+
|
212
|
+
def <=>(o)
|
213
|
+
@cd<=>o.instance_variable_get(:@cd)
|
214
|
+
end
|
215
|
+
|
216
|
+
def bus; default_bus; end
|
217
|
+
def device() Device.new(@cd.device) end
|
218
|
+
def interfaces; @cd.interfaces.map{|c| Interface.new(c) }; end
|
219
|
+
def settings() self.interfaces.map {|d| d.settings }.flatten end
|
220
|
+
def endpoints() self.settings.map {|d| d.endpoints }.flatten end
|
221
|
+
end
|
222
|
+
|
223
|
+
class Interface
|
224
|
+
extend Forwardable
|
225
|
+
include Comparable
|
226
|
+
|
227
|
+
def initialize(i)
|
228
|
+
@i = i
|
229
|
+
end
|
230
|
+
|
231
|
+
def_delegators :@i, :inspect
|
232
|
+
|
233
|
+
def <=>(o)
|
234
|
+
@i<=>o.instance_variable_get(:@i)
|
235
|
+
end
|
236
|
+
|
237
|
+
def bus() self.configuration.device.bus end
|
238
|
+
def device() self.configuration.device end
|
239
|
+
def configuration; Configuration.new(@i.configuration); end
|
240
|
+
def settings; @i.alt_settings.map{|c| Setting.new(c) }; end
|
241
|
+
def endpoints() self.settings.map {|d| d.endpoints }.flatten end
|
242
|
+
end
|
243
|
+
|
244
|
+
class Setting
|
245
|
+
extend Forwardable
|
246
|
+
include Comparable
|
247
|
+
|
248
|
+
def initialize(id)
|
249
|
+
@id = id
|
250
|
+
end
|
251
|
+
|
252
|
+
def_delegators :@id, :bLength, :bDescriptorType, :bInterfaceNumber, :bAlternateSetting,
|
253
|
+
:bNumEndpoints, :bInterfaceClass, :bInterfaceSubClass, :bInterfaceProtocol,
|
254
|
+
:iInterface, :inspect
|
255
|
+
|
256
|
+
def <=>(o)
|
257
|
+
@id<=>o.instance_variable_get(:@id)
|
258
|
+
end
|
259
|
+
|
260
|
+
def bus() self.interface.configuration.device.bus end
|
261
|
+
def device() self.interface.configuration.device end
|
262
|
+
def configuration() self.interface.configuration end
|
263
|
+
def interface; Interface.new(@id.interface); end
|
264
|
+
def endpoints() @id.endpoints.map {|d| Endpoint.new(d) }.flatten end
|
265
|
+
end
|
266
|
+
|
267
|
+
class Endpoint
|
268
|
+
extend Forwardable
|
269
|
+
include Comparable
|
270
|
+
|
271
|
+
def initialize(ep)
|
272
|
+
@ep = ep
|
273
|
+
end
|
274
|
+
|
275
|
+
def_delegators :@ep, :bLength, :bDescriptorType, :bEndpointAddress, :bmAttributes,
|
276
|
+
:wMaxPacketSize, :bInterval, :bRefresh, :bSynchAddress,
|
277
|
+
:inspect
|
278
|
+
|
279
|
+
def <=>(o)
|
280
|
+
@ep<=>o.instance_variable_get(:@ep)
|
281
|
+
end
|
282
|
+
|
283
|
+
def bus() self.setting.interface.configuration.device.bus end
|
284
|
+
def device() self.setting.interface.configuration.device end
|
285
|
+
def configuration() self.setting.interface.configuration end
|
286
|
+
def interface() self.setting.interface end
|
287
|
+
def setting; Setting.new(@ep.setting); end
|
288
|
+
end
|
289
|
+
|
290
|
+
class DevHandle
|
291
|
+
def initialize(dev)
|
292
|
+
@dev = dev
|
293
|
+
end
|
294
|
+
|
295
|
+
def usb_close; @dev.close; end
|
296
|
+
def usb_set_configuration(c); @dev.configuration=c; end
|
297
|
+
def usb_set_altinterface(c); @dev.set_interface_alt_setting=c; end
|
298
|
+
def usb_clear_halt(c); @dev.clear_halt(c); end
|
299
|
+
def usb_reset; @dev.reset_device; end
|
300
|
+
def usb_claim_interface(c); @dev.claim_interface(c); end
|
301
|
+
def usb_release_interface(c); @dev.release_interface(c); end
|
302
|
+
def usb_get_string(index, langid, buffer)
|
303
|
+
t = @dev.string_descriptor(index, langid)
|
304
|
+
buffer[0, t.length] = t
|
305
|
+
t.length
|
306
|
+
end
|
307
|
+
def usb_get_string_simple(index, buffer)
|
308
|
+
t = @dev.string_descriptor_ascii(index)
|
309
|
+
buffer[0, t.length] = t
|
310
|
+
t.length
|
311
|
+
end
|
312
|
+
|
313
|
+
def usb_control_msg(requesttype, request, value, index, bytes, timeout)
|
314
|
+
if requesttype&LIBUSB::ENDPOINT_IN != 0
|
315
|
+
# transfer direction in
|
316
|
+
res = @dev.control_transfer(bmRequestType: requesttype, bRequest: request,
|
317
|
+
wValue: value, wIndex: index, dataIn: bytes.bytesize, timeout: timeout)
|
318
|
+
bytes[0, res.bytesize] = res
|
319
|
+
res.bytesize
|
320
|
+
else
|
321
|
+
# transfer direction out
|
322
|
+
@dev.control_transfer(bmRequestType: requesttype, bRequest: request, wValue: value,
|
323
|
+
wIndex: index, dataOut: bytes, timeout: timeout)
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
def usb_bulk_write(endpoint, bytes, timeout)
|
328
|
+
@dev.bulk_transfer(endpoint: endpoint, dataOut: bytes, timeout: timeout)
|
329
|
+
end
|
330
|
+
def usb_bulk_read(endpoint, bytes, timeout)
|
331
|
+
res = @dev.bulk_transfer(endpoint: endpoint, dataIn: bytes.bytesize, timeout: timeout)
|
332
|
+
bytes[0, res.bytesize] = res
|
333
|
+
res.bytesize
|
334
|
+
end
|
335
|
+
|
336
|
+
def usb_interrupt_write(endpoint, bytes, timeout)
|
337
|
+
@dev.interrupt_transfer(endpoint: endpoint, dataOut: bytes, timeout: timeout)
|
338
|
+
end
|
339
|
+
def usb_interrupt_read(endpoint, bytes, timeout)
|
340
|
+
res = @dev.interrupt_transfer(endpoint: endpoint, dataIn: bytes.bytesize, timeout: timeout)
|
341
|
+
bytes[0, res.bytesize] = res
|
342
|
+
res.bytesize
|
343
|
+
end
|
344
|
+
|
345
|
+
# rb_define_method(rb_cUSB_DevHandle, "usb_get_descriptor", rusb_get_descriptor, 3);
|
346
|
+
# rb_define_method(rb_cUSB_DevHandle, "usb_get_descriptor_by_endpoint", rusb_get_descriptor_by_endpoint, 4);
|
347
|
+
|
348
|
+
if HAS_DETACH_KERNEL_DRIVER_NP
|
349
|
+
def usb_detach_kernel_driver_np(interface, dummy=nil)
|
350
|
+
@dev.detach_kernel_driver(interface)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
if HAS_GET_DRIVER_NP
|
355
|
+
def usb_get_driver_np(interface, buffer)
|
356
|
+
if @dev.kernel_driver_active?(interface)
|
357
|
+
t = "unknown driver"
|
358
|
+
buffer[0, t.length] = t
|
359
|
+
else
|
360
|
+
raise Errno::ENODATA, "No data available"
|
361
|
+
end
|
362
|
+
nil
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
alias set_configuration usb_set_configuration
|
367
|
+
alias set_altinterface usb_set_altinterface
|
368
|
+
alias clear_halt usb_clear_halt
|
369
|
+
alias claim_interface usb_claim_interface
|
370
|
+
alias release_interface usb_release_interface
|
371
|
+
|
372
|
+
def get_string_simple(index)
|
373
|
+
@dev.string_descriptor_ascii(index)
|
374
|
+
end
|
375
|
+
end
|
376
|
+
end
|
@@ -0,0 +1,154 @@
|
|
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
|
+
class Configuration < FFI::Struct
|
20
|
+
include Comparable
|
21
|
+
include ContextReference
|
22
|
+
|
23
|
+
layout :bLength, :uint8,
|
24
|
+
:bDescriptorType, :uint8,
|
25
|
+
:wTotalLength, :uint16,
|
26
|
+
:bNumInterfaces, :uint8,
|
27
|
+
:bConfigurationValue, :uint8,
|
28
|
+
:iConfiguration, :uint8,
|
29
|
+
:bmAttributes, :uint8,
|
30
|
+
:bMaxPower, :uint8,
|
31
|
+
:interface, :pointer,
|
32
|
+
:extra, :pointer,
|
33
|
+
:extra_length, :int
|
34
|
+
|
35
|
+
# Size of this descriptor (in bytes).
|
36
|
+
def bLength
|
37
|
+
self[:bLength]
|
38
|
+
end
|
39
|
+
|
40
|
+
# Descriptor type (0x02)
|
41
|
+
def bDescriptorType
|
42
|
+
self[:bDescriptorType]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Total length of data returned for this configuration.
|
46
|
+
def wTotalLength
|
47
|
+
self[:wTotalLength]
|
48
|
+
end
|
49
|
+
|
50
|
+
# Number of interfaces supported by this configuration.
|
51
|
+
def bNumInterfaces
|
52
|
+
self[:bNumInterfaces]
|
53
|
+
end
|
54
|
+
|
55
|
+
# Identifier value for this configuration.
|
56
|
+
def bConfigurationValue
|
57
|
+
self[:bConfigurationValue]
|
58
|
+
end
|
59
|
+
|
60
|
+
# Index of string descriptor describing this configuration.
|
61
|
+
def iConfiguration
|
62
|
+
self[:iConfiguration]
|
63
|
+
end
|
64
|
+
|
65
|
+
# Configuration characteristics.
|
66
|
+
#
|
67
|
+
# * Bit 7: Reserved, set to 1. (USB 1.0 Bus Powered)
|
68
|
+
# * Bit 6: Self Powered
|
69
|
+
# * Bit 5: Remote Wakeup
|
70
|
+
# * Bit 4..0: Reserved, set to 0.
|
71
|
+
#
|
72
|
+
# @return [Integer]
|
73
|
+
#
|
74
|
+
# @see #self_powered?
|
75
|
+
# @see #remote_wakeup?
|
76
|
+
def bmAttributes
|
77
|
+
self[:bmAttributes]
|
78
|
+
end
|
79
|
+
|
80
|
+
# @return [Boolean]
|
81
|
+
def self_powered?
|
82
|
+
bmAttributes & 0b1000000 != 0
|
83
|
+
end
|
84
|
+
|
85
|
+
# @return [Boolean]
|
86
|
+
def remote_wakeup?
|
87
|
+
bmAttributes & 0b100000 != 0
|
88
|
+
end
|
89
|
+
|
90
|
+
# Maximum power consumption of the USB device from this bus in this configuration when the device is fully opreation.
|
91
|
+
#
|
92
|
+
# @return [Integer] Maximum Power Consumption in 2mA units
|
93
|
+
def bMaxPower
|
94
|
+
self[:bMaxPower]
|
95
|
+
end
|
96
|
+
|
97
|
+
# @deprecated Use {#bMaxPower} instead.
|
98
|
+
alias maxPower bMaxPower
|
99
|
+
|
100
|
+
|
101
|
+
# Extra descriptors.
|
102
|
+
#
|
103
|
+
# @return [String]
|
104
|
+
def extra
|
105
|
+
return if self[:extra].null?
|
106
|
+
self[:extra].read_string(self[:extra_length])
|
107
|
+
end
|
108
|
+
|
109
|
+
def initialize(device, *args)
|
110
|
+
@device = device
|
111
|
+
super(*args)
|
112
|
+
|
113
|
+
register_context(device.context.instance_variable_get(:@ctx), :libusb_free_config_descriptor)
|
114
|
+
end
|
115
|
+
|
116
|
+
# @return [Device] the device this configuration belongs to.
|
117
|
+
attr_reader :device
|
118
|
+
|
119
|
+
def interfaces
|
120
|
+
ifs = []
|
121
|
+
self[:bNumInterfaces].times do |i|
|
122
|
+
ifs << Interface.new(self, self[:interface] + i*Interface.size)
|
123
|
+
end
|
124
|
+
return ifs
|
125
|
+
end
|
126
|
+
|
127
|
+
def inspect
|
128
|
+
attrs = []
|
129
|
+
attrs << self.bConfigurationValue.to_s
|
130
|
+
attrs << "SelfPowered" if self_powered?
|
131
|
+
attrs << "RemoteWakeup" if remote_wakeup?
|
132
|
+
desc = self.description
|
133
|
+
attrs << desc if desc != '?'
|
134
|
+
"\#<#{self.class} #{attrs.join(' ')}>"
|
135
|
+
end
|
136
|
+
|
137
|
+
# Return name of this configuration as String.
|
138
|
+
def description
|
139
|
+
return @description if defined? @description
|
140
|
+
@description = device.try_string_descriptor_ascii(self.iConfiguration)
|
141
|
+
end
|
142
|
+
|
143
|
+
# Return all interface decriptions of the configuration as Array of {Setting}s.
|
144
|
+
def settings() self.interfaces.map {|d| d.settings }.flatten end
|
145
|
+
# Return all endpoints of all interfaces of the configuration as Array of {Endpoint}s.
|
146
|
+
def endpoints() self.settings.map {|d| d.endpoints }.flatten end
|
147
|
+
|
148
|
+
def <=>(o)
|
149
|
+
t = device<=>o.device
|
150
|
+
t = bConfigurationValue<=>o.bConfigurationValue if t==0
|
151
|
+
t
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,170 @@
|
|
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
|
+
[
|
20
|
+
Call::ClassCodes,
|
21
|
+
Call::TransferTypes,
|
22
|
+
Call::StandardRequests,
|
23
|
+
Call::RequestTypes,
|
24
|
+
Call::DescriptorTypes,
|
25
|
+
Call::EndpointDirections,
|
26
|
+
Call::RequestRecipients,
|
27
|
+
Call::IsoSyncTypes,
|
28
|
+
Call::Speeds,
|
29
|
+
Call::Capabilities,
|
30
|
+
Call::SupportedSpeeds,
|
31
|
+
Call::Usb20ExtensionAttributes,
|
32
|
+
Call::SsUsbDeviceCapabilityAttributes,
|
33
|
+
Call::BosTypes,
|
34
|
+
Call::HotplugEvents,
|
35
|
+
Call::HotplugFlags,
|
36
|
+
Call::LogLevels,
|
37
|
+
Call::LogCbMode,
|
38
|
+
Call::Options,
|
39
|
+
].each do |enum|
|
40
|
+
enum.to_h.each{|k,v| const_set(k,v) }
|
41
|
+
end
|
42
|
+
|
43
|
+
# Base class of libusb errors
|
44
|
+
class Error < RuntimeError
|
45
|
+
# The data already transferred before the exception was raised
|
46
|
+
# @return [Fixnum] Number of bytes sent for an outgoing transfer
|
47
|
+
# @return [String] Received data for an ingoing transfer
|
48
|
+
attr_reader :transferred
|
49
|
+
|
50
|
+
def initialize(msg=nil, transferred=nil)
|
51
|
+
super(msg)
|
52
|
+
@transferred = transferred
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class RemainingReferencesError < Error
|
57
|
+
end
|
58
|
+
|
59
|
+
# @private
|
60
|
+
ErrorClassForResult = {}
|
61
|
+
|
62
|
+
# define an exception class for each error code
|
63
|
+
Call::Errors.to_h.each do |k,v|
|
64
|
+
klass = Class.new(Error)
|
65
|
+
klass.send(:define_method, :code){ v }
|
66
|
+
const_set(k, klass)
|
67
|
+
ErrorClassForResult[v] = klass
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.raise_error(res, text)
|
71
|
+
klass = ErrorClassForResult[res]
|
72
|
+
raise klass, "#{klass} #{text}"
|
73
|
+
end
|
74
|
+
|
75
|
+
CONTROL_SETUP_SIZE = 8
|
76
|
+
DT_DEVICE_SIZE = 18
|
77
|
+
DT_CONFIG_SIZE = 9
|
78
|
+
DT_INTERFACE_SIZE = 9
|
79
|
+
DT_ENDPOINT_SIZE = 7
|
80
|
+
DT_ENDPOINT_AUDIO_SIZE = 9 # Audio extension
|
81
|
+
DT_HUB_NONVAR_SIZE = 7
|
82
|
+
|
83
|
+
ENDPOINT_ADDRESS_MASK = 0x0f # in bEndpointAddress
|
84
|
+
ENDPOINT_DIR_MASK = 0x80
|
85
|
+
TRANSFER_TYPE_MASK = 0x03 # in bmAttributes
|
86
|
+
ISO_SYNC_TYPE_MASK = 0x0C
|
87
|
+
ISO_USAGE_TYPE_MASK = 0x30
|
88
|
+
|
89
|
+
POLLIN = 1
|
90
|
+
POLLOUT = 4
|
91
|
+
|
92
|
+
# Wildcard matching for hotplug events.
|
93
|
+
HOTPLUG_MATCH_ANY = -1
|
94
|
+
|
95
|
+
|
96
|
+
# http://www.usb.org/developers/defined_class
|
97
|
+
# @private
|
98
|
+
CLASS_CODES = [
|
99
|
+
[0x01, nil, nil, "Audio"],
|
100
|
+
[0x02, nil, nil, "Comm"],
|
101
|
+
[0x03, nil, nil, "HID"],
|
102
|
+
[0x05, nil, nil, "Physical"],
|
103
|
+
[0x06, 0x01, 0x01, "StillImaging"],
|
104
|
+
[0x06, nil, nil, "Image"],
|
105
|
+
[0x07, nil, nil, "Printer"],
|
106
|
+
[0x08, 0x01, nil, "MassStorage RBC Bulk-Only"],
|
107
|
+
[0x08, 0x02, 0x50, "MassStorage ATAPI Bulk-Only"],
|
108
|
+
[0x08, 0x03, 0x50, "MassStorage QIC-157 Bulk-Only"],
|
109
|
+
[0x08, 0x04, nil, "MassStorage UFI"],
|
110
|
+
[0x08, 0x05, 0x50, "MassStorage SFF-8070i Bulk-Only"],
|
111
|
+
[0x08, 0x06, 0x50, "MassStorage SCSI Bulk-Only"],
|
112
|
+
[0x08, nil, nil, "MassStorage"],
|
113
|
+
[0x09, 0x00, 0x00, "Full speed Hub"],
|
114
|
+
[0x09, 0x00, 0x01, "Hi-speed Hub with single TT"],
|
115
|
+
[0x09, 0x00, 0x02, "Hi-speed Hub with multiple TTs"],
|
116
|
+
[0x09, nil, nil, "Hub"],
|
117
|
+
[0x0a, nil, nil, "CDC"],
|
118
|
+
[0x0b, nil, nil, "SmartCard"],
|
119
|
+
[0x0d, 0x00, 0x00, "ContentSecurity"],
|
120
|
+
[0x0e, nil, nil, "Video"],
|
121
|
+
[0xdc, 0x01, 0x01, "Diagnostic USB2"],
|
122
|
+
[0xdc, nil, nil, "Diagnostic"],
|
123
|
+
[0xe0, 0x01, 0x01, "Bluetooth"],
|
124
|
+
[0xe0, 0x01, 0x02, "UWB"],
|
125
|
+
[0xe0, 0x01, 0x03, "RemoteNDIS"],
|
126
|
+
[0xe0, 0x02, 0x01, "Host Wire Adapter Control/Data"],
|
127
|
+
[0xe0, 0x02, 0x02, "Device Wire Adapter Control/Data"],
|
128
|
+
[0xe0, 0x02, 0x03, "Device Wire Adapter Isochronous"],
|
129
|
+
[0xe0, nil, nil, "Wireless Controller"],
|
130
|
+
[0xef, 0x01, 0x01, "Active Sync"],
|
131
|
+
[0xef, 0x01, 0x02, "Palm Sync"],
|
132
|
+
[0xef, 0x02, 0x01, "Interface Association Descriptor"],
|
133
|
+
[0xef, 0x02, 0x02, "Wire Adapter Multifunction Peripheral"],
|
134
|
+
[0xef, 0x03, 0x01, "Cable Based Association Framework"],
|
135
|
+
[0xef, nil, nil, "Miscellaneous"],
|
136
|
+
[0xfe, 0x01, 0x01, "Device Firmware Upgrade"],
|
137
|
+
[0xfe, 0x02, 0x00, "IRDA Bridge"],
|
138
|
+
[0xfe, 0x03, 0x00, "USB Test and Measurement"],
|
139
|
+
[0xfe, 0x03, 0x01, "USB Test and Measurement (USBTMC USB488)"],
|
140
|
+
[0xfe, nil, nil, "Application Specific"],
|
141
|
+
[0xff, nil, nil, "Vendor specific"],
|
142
|
+
]
|
143
|
+
# @private
|
144
|
+
CLASS_CODES_HASH1 = {}
|
145
|
+
# @private
|
146
|
+
CLASS_CODES_HASH2 = {}
|
147
|
+
# @private
|
148
|
+
CLASS_CODES_HASH3 = {}
|
149
|
+
CLASS_CODES.each {|base_class, sub_class, protocol, desc|
|
150
|
+
if protocol
|
151
|
+
CLASS_CODES_HASH3[[base_class, sub_class, protocol]] = desc
|
152
|
+
elsif sub_class
|
153
|
+
CLASS_CODES_HASH2[[base_class, sub_class]] = desc
|
154
|
+
else
|
155
|
+
CLASS_CODES_HASH1[base_class] = desc
|
156
|
+
end
|
157
|
+
}
|
158
|
+
|
159
|
+
def self.dev_string(base_class, sub_class, protocol)
|
160
|
+
if desc = CLASS_CODES_HASH3[[base_class, sub_class, protocol]]
|
161
|
+
desc
|
162
|
+
elsif desc = CLASS_CODES_HASH2[[base_class, sub_class]]
|
163
|
+
desc + " (%02x)" % [protocol]
|
164
|
+
elsif desc = CLASS_CODES_HASH1[base_class]
|
165
|
+
desc + " (%02x,%02x)" % [sub_class, protocol]
|
166
|
+
else
|
167
|
+
"Unknown(%02x,%02x,%02x)" % [base_class, sub_class, protocol]
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|