libusb 0.7.0-x64-mingw-ucrt
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.appveyor.yml +33 -0
- data/.github/workflows/ci.yml +185 -0
- data/.gitignore +9 -0
- data/.travis.yml +26 -0
- data/.yardopts +6 -0
- data/COPYING +165 -0
- data/Gemfile +19 -0
- data/History.md +193 -0
- data/README.md +184 -0
- data/Rakefile +79 -0
- data/lib/libusb/bos.rb +362 -0
- data/lib/libusb/call.rb +622 -0
- data/lib/libusb/compat.rb +376 -0
- data/lib/libusb/configuration.rb +154 -0
- data/lib/libusb/constants.rb +170 -0
- data/lib/libusb/context.rb +576 -0
- data/lib/libusb/context_reference.rb +38 -0
- data/lib/libusb/dependencies.rb +7 -0
- data/lib/libusb/dev_handle.rb +574 -0
- data/lib/libusb/device.rb +407 -0
- data/lib/libusb/endpoint.rb +195 -0
- data/lib/libusb/eventmachine.rb +187 -0
- data/lib/libusb/gem_helper.rb +151 -0
- data/lib/libusb/interface.rb +60 -0
- data/lib/libusb/libusb_recipe.rb +29 -0
- data/lib/libusb/setting.rb +132 -0
- data/lib/libusb/ss_companion.rb +72 -0
- data/lib/libusb/stdio.rb +25 -0
- data/lib/libusb/transfer.rb +418 -0
- data/lib/libusb/version_gem.rb +19 -0
- data/lib/libusb/version_struct.rb +63 -0
- data/lib/libusb-1.0.dll +0 -0
- data/lib/libusb.rb +146 -0
- data/libusb.gemspec +28 -0
- data/test/test_libusb.rb +42 -0
- data/test/test_libusb_bos.rb +140 -0
- data/test/test_libusb_bulk_stream_transfer.rb +61 -0
- data/test/test_libusb_compat.rb +78 -0
- data/test/test_libusb_compat_mass_storage.rb +81 -0
- data/test/test_libusb_context.rb +88 -0
- data/test/test_libusb_descriptors.rb +245 -0
- data/test/test_libusb_event_machine.rb +118 -0
- data/test/test_libusb_gc.rb +52 -0
- data/test/test_libusb_hotplug.rb +129 -0
- data/test/test_libusb_iso_transfer.rb +56 -0
- data/test/test_libusb_mass_storage.rb +268 -0
- data/test/test_libusb_mass_storage2.rb +96 -0
- data/test/test_libusb_structs.rb +87 -0
- data/test/test_libusb_threads.rb +89 -0
- data/wireshark-usb-sniffer.png +0 -0
- metadata +112 -0
@@ -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
|