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,407 @@
|
|
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 representing a USB device detected on the system.
|
20
|
+
#
|
21
|
+
# Devices of the system can be obtained with {Context#devices} .
|
22
|
+
class Device
|
23
|
+
include Comparable
|
24
|
+
include ContextReference
|
25
|
+
|
26
|
+
# @return [Context] the context this device belongs to.
|
27
|
+
attr_reader :context
|
28
|
+
|
29
|
+
def initialize context, pDev
|
30
|
+
@context = context
|
31
|
+
@pDev = pDev
|
32
|
+
register_context(context.instance_variable_get(:@ctx), :libusb_unref_device)
|
33
|
+
Call.libusb_ref_device(pDev)
|
34
|
+
|
35
|
+
@pDevDesc = Call::DeviceDescriptor.new
|
36
|
+
res = Call.libusb_get_device_descriptor(@pDev, @pDevDesc)
|
37
|
+
LIBUSB.raise_error res, "in libusb_get_device_descriptor" if res!=0
|
38
|
+
end
|
39
|
+
|
40
|
+
# The pointer for ContextReference
|
41
|
+
private def pointer
|
42
|
+
@pDev
|
43
|
+
end
|
44
|
+
|
45
|
+
# Open the device and obtain a device handle.
|
46
|
+
#
|
47
|
+
# A handle allows you to perform I/O on the device in question.
|
48
|
+
# This is a non-blocking function; no requests are sent over the bus.
|
49
|
+
#
|
50
|
+
# If called with a block, the handle is passed to the block
|
51
|
+
# and is closed when the block has finished.
|
52
|
+
#
|
53
|
+
# You need proper device access:
|
54
|
+
# * Linux: read+write permissions to <tt>/dev/bus/usb/<bus>/<dev></tt>
|
55
|
+
# * Windows: by installing a WinUSB-driver for the device (see {file:README.rdoc#Usage_on_Windows} )
|
56
|
+
#
|
57
|
+
# @return [DevHandle] Handle to the device.
|
58
|
+
def open
|
59
|
+
ppHandle = FFI::MemoryPointer.new :pointer
|
60
|
+
res = Call.libusb_open(@pDev, ppHandle)
|
61
|
+
LIBUSB.raise_error res, "in libusb_open" if res!=0
|
62
|
+
handle = DevHandle.new self, ppHandle.read_pointer
|
63
|
+
return handle unless block_given?
|
64
|
+
begin
|
65
|
+
yield handle
|
66
|
+
ensure
|
67
|
+
handle.close
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Open the device and claim an interface.
|
72
|
+
#
|
73
|
+
# This is a convenience method to {Device#open} and {DevHandle#claim_interface}.
|
74
|
+
# Must be called with a block. When the block has finished, the interface
|
75
|
+
# will be released and the device will be closed.
|
76
|
+
#
|
77
|
+
# @param [Interface, Fixnum] interface the interface or it's bInterfaceNumber you wish to claim
|
78
|
+
def open_interface(interface)
|
79
|
+
open do |dev|
|
80
|
+
dev.claim_interface(interface) do
|
81
|
+
yield dev
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Get the number of the bus that a device is connected to.
|
87
|
+
def bus_number
|
88
|
+
Call.libusb_get_bus_number(@pDev)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Get the address of the device on the bus it is connected to.
|
92
|
+
def device_address
|
93
|
+
Call.libusb_get_device_address(@pDev)
|
94
|
+
end
|
95
|
+
|
96
|
+
if Call.respond_to?(:libusb_get_port_number)
|
97
|
+
# Get the number of the port that a device is connected to.
|
98
|
+
# Available since libusb-1.0.12.
|
99
|
+
#
|
100
|
+
# @return [Fixnum, nil] the port number (+nil+ if not available)
|
101
|
+
# @see #port_numbers
|
102
|
+
def port_number
|
103
|
+
r = Call.libusb_get_port_number(@pDev)
|
104
|
+
r==0 ? nil : r
|
105
|
+
end
|
106
|
+
|
107
|
+
# Get the the parent from the specified device [EXPERIMENTAL].
|
108
|
+
# Available since libusb-1.0.12.
|
109
|
+
#
|
110
|
+
# @return [Device, nil] the device parent or +nil+ if not available
|
111
|
+
# @see #port_numbers
|
112
|
+
def parent
|
113
|
+
pppDevs = FFI::MemoryPointer.new :pointer
|
114
|
+
Call.libusb_get_device_list(@context.instance_variable_get(:@ctx), pppDevs)
|
115
|
+
ppDevs = pppDevs.read_pointer
|
116
|
+
pParent = Call.libusb_get_parent(@pDev)
|
117
|
+
parent = pParent.null? ? nil : Device.new(@context, pParent)
|
118
|
+
Call.libusb_free_device_list(ppDevs, 1)
|
119
|
+
parent
|
120
|
+
end
|
121
|
+
|
122
|
+
# Get the list of all port numbers from root for the specified device.
|
123
|
+
# Available since libusb-1.0.12.
|
124
|
+
#
|
125
|
+
# @return [Array<Fixnum>]
|
126
|
+
# @see #parent
|
127
|
+
# @see #port_number
|
128
|
+
def port_numbers
|
129
|
+
# As per the USB 3.0 specs, the current maximum limit for the depth is 7.
|
130
|
+
path_len = 7
|
131
|
+
pPath = FFI::MemoryPointer.new :pointer, path_len
|
132
|
+
|
133
|
+
l = if Call.respond_to?(:libusb_get_port_numbers)
|
134
|
+
Call.libusb_get_port_numbers(@pDev, pPath, path_len)
|
135
|
+
else
|
136
|
+
Call.libusb_get_port_path(@context.instance_variable_get(:@ctx), @pDev, pPath, path_len)
|
137
|
+
end
|
138
|
+
pPath.read_array_of_uint8(l)
|
139
|
+
end
|
140
|
+
alias port_path port_numbers
|
141
|
+
end
|
142
|
+
|
143
|
+
if Call.respond_to?(:libusb_get_device_speed)
|
144
|
+
# Get the negotiated connection speed for a device.
|
145
|
+
# Available since libusb-1.0.9.
|
146
|
+
#
|
147
|
+
# @return [Symbol] a {Call::Speeds Speeds} symbol, where +:SPEED_UNKNOWN+ means that
|
148
|
+
# the OS doesn't know or doesn't support returning the negotiated speed.
|
149
|
+
def device_speed
|
150
|
+
Call.libusb_get_device_speed(@pDev)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Convenience function to retrieve the wMaxPacketSize value for a
|
155
|
+
# particular endpoint in the active device configuration.
|
156
|
+
#
|
157
|
+
# @param [Endpoint, Fixnum] endpoint (address of) the endpoint in question
|
158
|
+
# @return [Fixnum] the wMaxPacketSize value
|
159
|
+
def max_packet_size(endpoint)
|
160
|
+
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
161
|
+
res = Call.libusb_get_max_packet_size(@pDev, endpoint)
|
162
|
+
LIBUSB.raise_error res, "in libusb_get_max_packet_size" unless res>=0
|
163
|
+
res
|
164
|
+
end
|
165
|
+
|
166
|
+
if Call.respond_to?(:libusb_get_max_alt_packet_size)
|
167
|
+
|
168
|
+
# Calculate the maximum packet size which a specific endpoint is capable of
|
169
|
+
# sending or receiving in the duration of 1 microframe
|
170
|
+
#
|
171
|
+
# Only the active configuration is examined. The calculation is based on the
|
172
|
+
# wMaxPacketSize field in the endpoint descriptor as described in section
|
173
|
+
# 9.6.6 in the USB 2.0 specifications.
|
174
|
+
#
|
175
|
+
# If acting on an isochronous or interrupt endpoint, this function will
|
176
|
+
# multiply the value found in bits 0:10 by the number of transactions per
|
177
|
+
# microframe (determined by bits 11:12). Otherwise, this function just
|
178
|
+
# returns the numeric value found in bits 0:10. For USB 3.0 device, it
|
179
|
+
# will attempts to retrieve the Endpoint Companion Descriptor to return
|
180
|
+
# wBytesPerInterval.
|
181
|
+
#
|
182
|
+
# This function is useful for setting up isochronous transfers, for example
|
183
|
+
# you might pass the return value from this function to
|
184
|
+
# +IsochronousTransfer.packet_lengths=+ in order to set the length field of every
|
185
|
+
# isochronous packet in a transfer.
|
186
|
+
#
|
187
|
+
# Available since libusb-1.0.27.
|
188
|
+
#
|
189
|
+
# @param [Interface, Fixnum] interface the interface or its bInterfaceNumber of the interface the endpoint belongs to
|
190
|
+
# @param [Setting, Fixnum] alternate_setting the alternate setting or its bAlternateSetting
|
191
|
+
# @param [Endpoint, Fixnum] endpoint (address of) the endpoint in question
|
192
|
+
# @return [Fixnum] the maximum packet size which can be sent/received on this endpoint
|
193
|
+
# @see max_iso_packet_size
|
194
|
+
def max_alt_packet_size(interface, alternate_setting, endpoint)
|
195
|
+
interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
|
196
|
+
alternate_setting = alternate_setting.bAlternateSetting if alternate_setting.respond_to? :bAlternateSetting
|
197
|
+
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
198
|
+
res = Call.libusb_get_max_alt_packet_size(@pDev, interface, alternate_setting, endpoint)
|
199
|
+
LIBUSB.raise_error res, "in libusb_get_max_alt_packet_size" unless res>=0
|
200
|
+
res
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# Calculate the maximum packet size which a specific endpoint is capable is
|
205
|
+
# sending or receiving in the duration of 1 microframe.
|
206
|
+
#
|
207
|
+
# Only the active configution is examined. The calculation is based on the
|
208
|
+
# wMaxPacketSize field in the endpoint descriptor as described in section 9.6.6
|
209
|
+
# in the USB 2.0 specifications.
|
210
|
+
#
|
211
|
+
# If acting on an isochronous or interrupt endpoint, this function will
|
212
|
+
# multiply the value found in bits 0:10 by the number of transactions per
|
213
|
+
# microframe (determined by bits 11:12). Otherwise, this function just returns
|
214
|
+
# the numeric value found in bits 0:10.
|
215
|
+
#
|
216
|
+
# This function is useful for setting up isochronous transfers, for example
|
217
|
+
# you might use the return value from this function to call
|
218
|
+
# IsoPacket#alloc_buffer in order to set the length field
|
219
|
+
# of an isochronous packet in a transfer.
|
220
|
+
#
|
221
|
+
# @param [Endpoint, Fixnum] endpoint (address of) the endpoint in question
|
222
|
+
# @return [Fixnum] the maximum packet size which can be sent/received on this endpoint
|
223
|
+
# @see max_alt_packet_size
|
224
|
+
def max_iso_packet_size(endpoint)
|
225
|
+
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
226
|
+
res = Call.libusb_get_max_iso_packet_size(@pDev, endpoint)
|
227
|
+
LIBUSB.raise_error res, "in libusb_get_max_iso_packet_size" unless res>=0
|
228
|
+
res
|
229
|
+
end
|
230
|
+
|
231
|
+
# Obtain a config descriptor of the device.
|
232
|
+
#
|
233
|
+
# @param [Fixnum] index number of the config descriptor
|
234
|
+
# @return Configuration
|
235
|
+
def config_descriptor(index)
|
236
|
+
ppConfig = FFI::MemoryPointer.new :pointer
|
237
|
+
res = Call.libusb_get_config_descriptor(@pDev, index, ppConfig)
|
238
|
+
LIBUSB.raise_error res, "in libusb_get_config_descriptor" if res!=0
|
239
|
+
pConfig = ppConfig.read_pointer
|
240
|
+
config = Configuration.new(self, pConfig)
|
241
|
+
config
|
242
|
+
end
|
243
|
+
|
244
|
+
# Size of the Descriptor in Bytes (18 bytes)
|
245
|
+
def bLength
|
246
|
+
@pDevDesc[:bLength]
|
247
|
+
end
|
248
|
+
|
249
|
+
# Device Descriptor (0x01)
|
250
|
+
def bDescriptorType
|
251
|
+
@pDevDesc[:bDescriptorType]
|
252
|
+
end
|
253
|
+
|
254
|
+
# USB specification release number which device complies too
|
255
|
+
#
|
256
|
+
# @return [Integer] in binary-coded decimal
|
257
|
+
def bcdUSB
|
258
|
+
@pDevDesc[:bcdUSB]
|
259
|
+
end
|
260
|
+
|
261
|
+
# USB-IF class code for the device (Assigned by USB Org)
|
262
|
+
#
|
263
|
+
# * If equal to 0x00, each interface specifies it's own class code
|
264
|
+
# * If equal to 0xFF, the class code is vendor specified
|
265
|
+
# * Otherwise field is valid Class Code
|
266
|
+
def bDeviceClass
|
267
|
+
@pDevDesc[:bDeviceClass]
|
268
|
+
end
|
269
|
+
|
270
|
+
# USB-IF subclass code for the device, qualified by the {Device#bDeviceClass}
|
271
|
+
# value (Assigned by USB Org)
|
272
|
+
def bDeviceSubClass
|
273
|
+
@pDevDesc[:bDeviceSubClass]
|
274
|
+
end
|
275
|
+
|
276
|
+
# USB-IF protocol code for the device, qualified by the {Device#bDeviceClass}
|
277
|
+
# and {Device#bDeviceSubClass} values (Assigned by USB Org)
|
278
|
+
def bDeviceProtocol
|
279
|
+
@pDevDesc[:bDeviceProtocol]
|
280
|
+
end
|
281
|
+
|
282
|
+
# Maximum Packet Size for Endpoint 0. Valid Sizes are 8, 16, 32, 64
|
283
|
+
def bMaxPacketSize0
|
284
|
+
@pDevDesc[:bMaxPacketSize0]
|
285
|
+
end
|
286
|
+
|
287
|
+
# USB-IF vendor ID (Assigned by USB Org)
|
288
|
+
def idVendor
|
289
|
+
@pDevDesc[:idVendor]
|
290
|
+
end
|
291
|
+
|
292
|
+
# USB-IF product ID (Assigned by Manufacturer)
|
293
|
+
def idProduct
|
294
|
+
@pDevDesc[:idProduct]
|
295
|
+
end
|
296
|
+
|
297
|
+
# Device release number in binary-coded decimal.
|
298
|
+
def bcdDevice
|
299
|
+
@pDevDesc[:bcdDevice]
|
300
|
+
end
|
301
|
+
|
302
|
+
# Index of string descriptor describing manufacturer.
|
303
|
+
def iManufacturer
|
304
|
+
@pDevDesc[:iManufacturer]
|
305
|
+
end
|
306
|
+
|
307
|
+
# Index of string descriptor describing product.
|
308
|
+
def iProduct
|
309
|
+
@pDevDesc[:iProduct]
|
310
|
+
end
|
311
|
+
|
312
|
+
# Index of string descriptor containing device serial number.
|
313
|
+
def iSerialNumber
|
314
|
+
@pDevDesc[:iSerialNumber]
|
315
|
+
end
|
316
|
+
|
317
|
+
# Number of Possible Configurations
|
318
|
+
def bNumConfigurations
|
319
|
+
@pDevDesc[:bNumConfigurations]
|
320
|
+
end
|
321
|
+
|
322
|
+
|
323
|
+
def inspect
|
324
|
+
attrs = []
|
325
|
+
attrs << "#{self.bus_number}/#{self.device_address}"
|
326
|
+
attrs << ("%04x:%04x" % [self.idVendor, self.idProduct])
|
327
|
+
attrs << self.manufacturer
|
328
|
+
attrs << self.product
|
329
|
+
attrs << self.serial_number
|
330
|
+
if self.bDeviceClass == LIBUSB::CLASS_PER_INTERFACE
|
331
|
+
devclass = self.settings.map {|i|
|
332
|
+
LIBUSB.dev_string(i.bInterfaceClass, i.bInterfaceSubClass, i.bInterfaceProtocol)
|
333
|
+
}.join(", ")
|
334
|
+
else
|
335
|
+
devclass = LIBUSB.dev_string(self.bDeviceClass, self.bDeviceSubClass, self.bDeviceProtocol)
|
336
|
+
end
|
337
|
+
attrs << "(#{devclass})"
|
338
|
+
attrs.compact!
|
339
|
+
"\#<#{self.class} #{attrs.join(' ')}>"
|
340
|
+
end
|
341
|
+
|
342
|
+
def try_string_descriptor_ascii(i)
|
343
|
+
begin
|
344
|
+
open{|h| h.string_descriptor_ascii(i) }
|
345
|
+
rescue
|
346
|
+
"?"
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
# Return manufacturer of the device
|
351
|
+
# @return String
|
352
|
+
def manufacturer
|
353
|
+
return @manufacturer if defined? @manufacturer
|
354
|
+
@manufacturer = try_string_descriptor_ascii(self.iManufacturer)
|
355
|
+
@manufacturer = @manufacturer.strip if @manufacturer
|
356
|
+
@manufacturer
|
357
|
+
end
|
358
|
+
|
359
|
+
# Return product name of the device.
|
360
|
+
# @return String
|
361
|
+
def product
|
362
|
+
return @product if defined? @product
|
363
|
+
@product = try_string_descriptor_ascii(self.iProduct)
|
364
|
+
@product = @product.strip if @product
|
365
|
+
@product
|
366
|
+
end
|
367
|
+
|
368
|
+
# Return serial number of the device.
|
369
|
+
# @return String
|
370
|
+
def serial_number
|
371
|
+
return @serial_number if defined? @serial_number
|
372
|
+
@serial_number = try_string_descriptor_ascii(self.iSerialNumber)
|
373
|
+
@serial_number = @serial_number.strip if @serial_number
|
374
|
+
@serial_number
|
375
|
+
end
|
376
|
+
|
377
|
+
# Return configurations of the device.
|
378
|
+
# @return [Array<Configuration>]
|
379
|
+
def configurations
|
380
|
+
configs = []
|
381
|
+
bNumConfigurations.times do |config_index|
|
382
|
+
begin
|
383
|
+
configs << config_descriptor(config_index)
|
384
|
+
rescue RuntimeError
|
385
|
+
# On Windows some devices don't return it's configuration.
|
386
|
+
end
|
387
|
+
end
|
388
|
+
configs
|
389
|
+
end
|
390
|
+
|
391
|
+
# Return all interfaces of this device.
|
392
|
+
# @return [Array<Interface>]
|
393
|
+
def interfaces() self.configurations.map {|d| d.interfaces }.flatten end
|
394
|
+
# Return all interface decriptions of this device.
|
395
|
+
# @return [Array<Setting>]
|
396
|
+
def settings() self.interfaces.map {|d| d.settings }.flatten end
|
397
|
+
# Return all endpoints of all interfaces of this device.
|
398
|
+
# @return [Array<Endpoint>]
|
399
|
+
def endpoints() self.settings.map {|d| d.endpoints }.flatten end
|
400
|
+
|
401
|
+
def <=>(o)
|
402
|
+
t = bus_number<=>o.bus_number
|
403
|
+
t = device_address<=>o.device_address if t==0
|
404
|
+
t
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|
@@ -0,0 +1,195 @@
|
|
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 Endpoint < FFI::Struct
|
20
|
+
include Comparable
|
21
|
+
|
22
|
+
layout :bLength, :uint8,
|
23
|
+
:bDescriptorType, :uint8,
|
24
|
+
:bEndpointAddress, :uint8,
|
25
|
+
:bmAttributes, :uint8,
|
26
|
+
:wMaxPacketSize, :uint16,
|
27
|
+
:bInterval, :uint8,
|
28
|
+
:bRefresh, :uint8,
|
29
|
+
:bSynchAddress, :uint8,
|
30
|
+
:extra, :pointer,
|
31
|
+
:extra_length, :int
|
32
|
+
|
33
|
+
# Size of Descriptor in Bytes (7 bytes)
|
34
|
+
def bLength
|
35
|
+
self[:bLength]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Descriptor type (0x05)
|
39
|
+
def bDescriptorType
|
40
|
+
self[:bDescriptorType]
|
41
|
+
end
|
42
|
+
|
43
|
+
# The address of the endpoint described by this descriptor.
|
44
|
+
#
|
45
|
+
# * Bits 0..3: Endpoint Number.
|
46
|
+
# * Bits 4..6: Reserved. Set to Zero
|
47
|
+
# * Bits 7: Direction 0 = Out, 1 = In (Ignored for Control Endpoints)
|
48
|
+
#
|
49
|
+
# @return [Integer]
|
50
|
+
#
|
51
|
+
# @see #endpoint_number
|
52
|
+
# @see #direction
|
53
|
+
def bEndpointAddress
|
54
|
+
self[:bEndpointAddress]
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [Integer]
|
58
|
+
def endpoint_number
|
59
|
+
bEndpointAddress & 0b1111
|
60
|
+
end
|
61
|
+
|
62
|
+
# @return [Symbol] Either +:in+ or +:out+
|
63
|
+
def direction
|
64
|
+
bEndpointAddress & ENDPOINT_IN == 0 ? :out : :in
|
65
|
+
end
|
66
|
+
|
67
|
+
# Attributes which apply to the endpoint when it is configured using the {Configuration#bConfigurationValue}.
|
68
|
+
#
|
69
|
+
# * Bits 1..0: Transfer Type
|
70
|
+
# * 00 = Control
|
71
|
+
# * 01 = Isochronous
|
72
|
+
# * 10 = Bulk
|
73
|
+
# * 11 = Interrupt
|
74
|
+
# * Bits 7..2: are reserved. If Isochronous endpoint,
|
75
|
+
# * Bits 3..2: Synchronisation Type (Iso Mode)
|
76
|
+
# * 00 = No Synchonisation
|
77
|
+
# * 01 = Asynchronous
|
78
|
+
# * 10 = Adaptive
|
79
|
+
# * 11 = Synchronous
|
80
|
+
# * Bits 5..4: Usage Type (Iso Mode)
|
81
|
+
# * 00 = Data Endpoint
|
82
|
+
# * 01 = Feedback Endpoint
|
83
|
+
# * 10 = Explicit Feedback Data Endpoint
|
84
|
+
# * 11 = Reserved
|
85
|
+
#
|
86
|
+
# @return [Integer]
|
87
|
+
#
|
88
|
+
# @see #transfer_type
|
89
|
+
# @see #usage_type
|
90
|
+
# @see #synchronization_type
|
91
|
+
def bmAttributes
|
92
|
+
self[:bmAttributes]
|
93
|
+
end
|
94
|
+
|
95
|
+
TransferTypes = [:control, :isochronous, :bulk, :interrupt]
|
96
|
+
# @return [Symbol] One of {TransferTypes}
|
97
|
+
def transfer_type
|
98
|
+
TransferTypes[bmAttributes & 0b11]
|
99
|
+
end
|
100
|
+
|
101
|
+
SynchronizationTypes = [:no_synchronization, :asynchronous, :adaptive, :synchronous]
|
102
|
+
# @return [Symbol] One of {SynchronizationTypes}
|
103
|
+
def synchronization_type
|
104
|
+
return unless transfer_type == :isochronous
|
105
|
+
SynchronizationTypes[(bmAttributes & 0b1100) >> 2]
|
106
|
+
end
|
107
|
+
|
108
|
+
UsageTypes = [:data, :feedback, :implicit_feedback, :unknown]
|
109
|
+
# @return [Symbol] One of {UsageTypes}
|
110
|
+
def usage_type
|
111
|
+
return unless transfer_type == :isochronous
|
112
|
+
UsageTypes[(bmAttributes & 0b110000) >> 4]
|
113
|
+
end
|
114
|
+
|
115
|
+
# Maximum Packet Size this endpoint is capable of sending or receiving
|
116
|
+
def wMaxPacketSize
|
117
|
+
self[:wMaxPacketSize]
|
118
|
+
end
|
119
|
+
|
120
|
+
# Interval for polling endpoint data transfers. Value in frame counts.
|
121
|
+
# Ignored for Bulk & Control Endpoints. Isochronous must equal 1 and field
|
122
|
+
# may range from 1 to 255 for interrupt endpoints.
|
123
|
+
#
|
124
|
+
# The interval is respected by the kernel driver, so user mode processes
|
125
|
+
# using libusb don't need to care about it.
|
126
|
+
def bInterval
|
127
|
+
self[:bInterval]
|
128
|
+
end
|
129
|
+
|
130
|
+
# For audio devices only: the rate at which synchronization feedback is provided.
|
131
|
+
def bRefresh
|
132
|
+
self[:bRefresh]
|
133
|
+
end
|
134
|
+
|
135
|
+
# For audio devices only: the address if the synch endpoint.
|
136
|
+
def bSynchAddress
|
137
|
+
self[:bSynchAddress]
|
138
|
+
end
|
139
|
+
|
140
|
+
# Extra descriptors.
|
141
|
+
#
|
142
|
+
# @return [String]
|
143
|
+
def extra
|
144
|
+
return if self[:extra].null?
|
145
|
+
self[:extra].read_string(self[:extra_length])
|
146
|
+
end
|
147
|
+
|
148
|
+
def initialize(setting, *args)
|
149
|
+
@setting = setting
|
150
|
+
super(*args)
|
151
|
+
end
|
152
|
+
|
153
|
+
# @return [Setting] the setting this endpoint belongs to.
|
154
|
+
attr_reader :setting
|
155
|
+
|
156
|
+
def inspect
|
157
|
+
type = [transfer_type, synchronization_type, usage_type].compact
|
158
|
+
"\#<#{self.class} #{endpoint_number} #{direction} #{type.join(" ")}>"
|
159
|
+
end
|
160
|
+
|
161
|
+
# The {Device} this Endpoint belongs to.
|
162
|
+
def device() self.setting.interface.configuration.device end
|
163
|
+
# The {Configuration} this Endpoint belongs to.
|
164
|
+
def configuration() self.setting.interface.configuration end
|
165
|
+
# The {Interface} this Endpoint belongs to.
|
166
|
+
def interface() self.setting.interface end
|
167
|
+
|
168
|
+
def <=>(o)
|
169
|
+
t = setting<=>o.setting
|
170
|
+
t = bEndpointAddress<=>o.bEndpointAddress if t==0
|
171
|
+
t
|
172
|
+
end
|
173
|
+
|
174
|
+
if Call.respond_to?(:libusb_get_ss_endpoint_companion_descriptor)
|
175
|
+
|
176
|
+
# @method ss_companion
|
177
|
+
# Get the endpoints superspeed endpoint companion descriptor (if any).
|
178
|
+
#
|
179
|
+
# Since libusb version 1.0.16.
|
180
|
+
#
|
181
|
+
# @return [SsCompanion]
|
182
|
+
def ss_companion
|
183
|
+
ep_comp = FFI::MemoryPointer.new :pointer
|
184
|
+
ctx = device.context.instance_variable_get(:@ctx)
|
185
|
+
res = Call.libusb_get_ss_endpoint_companion_descriptor(
|
186
|
+
ctx,
|
187
|
+
pointer,
|
188
|
+
ep_comp
|
189
|
+
)
|
190
|
+
LIBUSB.raise_error res, "in libusb_get_ss_endpoint_companion_descriptor" if res!=0
|
191
|
+
SsCompanion.new ctx, ep_comp.read_pointer
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|