libusb 0.3.3-x64-mingw32
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/.gitignore +8 -0
- data/.travis.yml +10 -0
- data/.yardopts +6 -0
- data/COPYING +165 -0
- data/Gemfile +16 -0
- data/History.md +77 -0
- data/README.md +144 -0
- data/Rakefile +185 -0
- data/lib/libusb.rb +51 -0
- data/lib/libusb/call.rb +316 -0
- data/lib/libusb/compat.rb +376 -0
- data/lib/libusb/configuration.rb +155 -0
- data/lib/libusb/constants.rb +151 -0
- data/lib/libusb/context.rb +305 -0
- data/lib/libusb/dev_handle.rb +450 -0
- data/lib/libusb/device.rb +359 -0
- data/lib/libusb/endpoint.rb +174 -0
- data/lib/libusb/eventmachine.rb +183 -0
- data/lib/libusb/interface.rb +60 -0
- data/lib/libusb/setting.rb +132 -0
- data/lib/libusb/transfer.rb +282 -0
- data/lib/libusb/version_gem.rb +19 -0
- data/lib/libusb/version_struct.rb +63 -0
- data/libusb.gemspec +31 -0
- data/test/test_libusb_capability.rb +23 -0
- data/test/test_libusb_compat.rb +78 -0
- data/test/test_libusb_compat_mass_storage.rb +81 -0
- data/test/test_libusb_descriptors.rb +181 -0
- data/test/test_libusb_event_machine.rb +118 -0
- data/test/test_libusb_gc.rb +37 -0
- data/test/test_libusb_iso_transfer.rb +50 -0
- data/test/test_libusb_mass_storage.rb +278 -0
- data/test/test_libusb_mass_storage2.rb +73 -0
- data/test/test_libusb_structs.rb +45 -0
- data/test/test_libusb_threads.rb +89 -0
- data/test/test_libusb_version.rb +40 -0
- metadata +126 -0
@@ -0,0 +1,450 @@
|
|
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 handle on a USB device.
|
20
|
+
#
|
21
|
+
# A device handle is used to perform I/O and other operations. When finished
|
22
|
+
# with a device handle, you should call DevHandle#close .
|
23
|
+
class DevHandle
|
24
|
+
# @private
|
25
|
+
attr_reader :pHandle
|
26
|
+
# @return [Device] the device this handle belongs to.
|
27
|
+
attr_reader :device
|
28
|
+
|
29
|
+
def initialize device, pHandle
|
30
|
+
@device = device
|
31
|
+
@pHandle = pHandle
|
32
|
+
@bulk_transfer = @control_transfer = @interrupt_transfer = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
# Close a device handle.
|
36
|
+
#
|
37
|
+
# Should be called on all open handles before your application exits.
|
38
|
+
#
|
39
|
+
# Internally, this function destroys the reference that was added by {Device#open}
|
40
|
+
# on the given device.
|
41
|
+
#
|
42
|
+
# This is a non-blocking function; no requests are sent over the bus.
|
43
|
+
def close
|
44
|
+
Call.libusb_close(@pHandle)
|
45
|
+
end
|
46
|
+
|
47
|
+
def string_descriptor_ascii(index)
|
48
|
+
pString = FFI::MemoryPointer.new 0x100
|
49
|
+
res = Call.libusb_get_string_descriptor_ascii(@pHandle, index, pString, pString.size)
|
50
|
+
LIBUSB.raise_error res, "in libusb_get_string_descriptor_ascii" unless res>=0
|
51
|
+
pString.read_string(res)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Claim an interface on a given device handle.
|
55
|
+
#
|
56
|
+
# You must claim the interface you wish to use before you can perform I/O on any
|
57
|
+
# of its endpoints.
|
58
|
+
#
|
59
|
+
# It is legal to attempt to claim an already-claimed interface, in which case
|
60
|
+
# libusb just returns without doing anything.
|
61
|
+
#
|
62
|
+
# Claiming of interfaces is a purely logical operation; it does not cause any
|
63
|
+
# requests to be sent over the bus. Interface claiming is used to instruct the
|
64
|
+
# underlying operating system that your application wishes to take ownership of
|
65
|
+
# the interface.
|
66
|
+
#
|
67
|
+
# This is a non-blocking function.
|
68
|
+
#
|
69
|
+
# If called with a block, the device handle is passed through to the block
|
70
|
+
# and the interface is released when the block has finished.
|
71
|
+
#
|
72
|
+
# @param [Interface, Fixnum] interface the interface or it's bInterfaceNumber you wish to claim
|
73
|
+
def claim_interface(interface)
|
74
|
+
interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
|
75
|
+
res = Call.libusb_claim_interface(@pHandle, interface)
|
76
|
+
LIBUSB.raise_error res, "in libusb_claim_interface" if res!=0
|
77
|
+
return self unless block_given?
|
78
|
+
begin
|
79
|
+
yield self
|
80
|
+
ensure
|
81
|
+
release_interface(interface)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Release an interface previously claimed with {DevHandle#claim_interface}.
|
86
|
+
#
|
87
|
+
# You should release all claimed interfaces before closing a device handle.
|
88
|
+
#
|
89
|
+
# This is a blocking function. A SET_INTERFACE control request will be sent to the
|
90
|
+
# device, resetting interface state to the first alternate setting.
|
91
|
+
#
|
92
|
+
# @param [Interface, Fixnum] interface the interface or it's bInterfaceNumber you
|
93
|
+
# claimed previously
|
94
|
+
def release_interface(interface)
|
95
|
+
interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
|
96
|
+
res = Call.libusb_release_interface(@pHandle, interface)
|
97
|
+
LIBUSB.raise_error res, "in libusb_release_interface" if res!=0
|
98
|
+
end
|
99
|
+
|
100
|
+
# Set the active configuration for a device.
|
101
|
+
#
|
102
|
+
# The operating system may or may not have already set an active configuration on
|
103
|
+
# the device. It is up to your application to ensure the correct configuration is
|
104
|
+
# selected before you attempt to claim interfaces and perform other operations.
|
105
|
+
#
|
106
|
+
# If you call this function on a device already configured with the selected
|
107
|
+
# configuration, then this function will act as a lightweight device reset: it
|
108
|
+
# will issue a SET_CONFIGURATION request using the current configuration, causing
|
109
|
+
# most USB-related device state to be reset (altsetting reset to zero, endpoint
|
110
|
+
# halts cleared, toggles reset).
|
111
|
+
#
|
112
|
+
# You cannot change/reset configuration if your application has claimed interfaces -
|
113
|
+
# you should free them with {DevHandle#release_interface} first. You cannot
|
114
|
+
# change/reset configuration if other applications or drivers have claimed
|
115
|
+
# interfaces.
|
116
|
+
#
|
117
|
+
# A configuration value of +nil+ will put the device in unconfigured state. The USB
|
118
|
+
# specifications state that a configuration value of 0 does this, however buggy
|
119
|
+
# devices exist which actually have a configuration 0.
|
120
|
+
#
|
121
|
+
# You should always use this function rather than formulating your own
|
122
|
+
# SET_CONFIGURATION control request. This is because the underlying operating
|
123
|
+
# system needs to know when such changes happen.
|
124
|
+
#
|
125
|
+
# This is a blocking function.
|
126
|
+
#
|
127
|
+
# @param [Configuration, Fixnum] configuration the configuration or it's
|
128
|
+
# bConfigurationValue you wish to activate, or +nil+ if you wish to put
|
129
|
+
# the device in unconfigured state
|
130
|
+
def set_configuration(configuration)
|
131
|
+
configuration = configuration.bConfigurationValue if configuration.respond_to? :bConfigurationValue
|
132
|
+
res = Call.libusb_set_configuration(@pHandle, configuration || -1)
|
133
|
+
LIBUSB.raise_error res, "in libusb_set_configuration" if res!=0
|
134
|
+
end
|
135
|
+
alias configuration= set_configuration
|
136
|
+
|
137
|
+
# Activate an alternate setting for an interface.
|
138
|
+
#
|
139
|
+
# The interface must have been previously claimed with {DevHandle#claim_interface}.
|
140
|
+
#
|
141
|
+
# You should always use this function rather than formulating your own
|
142
|
+
# SET_INTERFACE control request. This is because the underlying operating system
|
143
|
+
# needs to know when such changes happen.
|
144
|
+
#
|
145
|
+
# This is a blocking function.
|
146
|
+
#
|
147
|
+
# @param [Setting, Fixnum] setting_or_interface_number the alternate setting
|
148
|
+
# to activate or the bInterfaceNumber of the previously-claimed interface
|
149
|
+
# @param [Fixnum, nil] alternate_setting the bAlternateSetting of the alternate setting to activate
|
150
|
+
# (only if first param is a Fixnum)
|
151
|
+
def set_interface_alt_setting(setting_or_interface_number, alternate_setting=nil)
|
152
|
+
alternate_setting ||= setting_or_interface_number.bAlternateSetting if setting_or_interface_number.respond_to? :bAlternateSetting
|
153
|
+
setting_or_interface_number = setting_or_interface_number.bInterfaceNumber if setting_or_interface_number.respond_to? :bInterfaceNumber
|
154
|
+
res = Call.libusb_set_interface_alt_setting(@pHandle, setting_or_interface_number, alternate_setting)
|
155
|
+
LIBUSB.raise_error res, "in libusb_set_interface_alt_setting" if res!=0
|
156
|
+
end
|
157
|
+
|
158
|
+
# Clear the halt/stall condition for an endpoint.
|
159
|
+
#
|
160
|
+
# Endpoints with halt status are unable to receive or transmit
|
161
|
+
# data until the halt condition is stalled.
|
162
|
+
#
|
163
|
+
# You should cancel all pending transfers before attempting to
|
164
|
+
# clear the halt condition.
|
165
|
+
#
|
166
|
+
# This is a blocking function.
|
167
|
+
#
|
168
|
+
# @param [Endpoint, Fixnum] endpoint the endpoint in question or it's bEndpointAddress
|
169
|
+
def clear_halt(endpoint)
|
170
|
+
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
171
|
+
res = Call.libusb_clear_halt(@pHandle, endpoint)
|
172
|
+
LIBUSB.raise_error res, "in libusb_clear_halt" if res!=0
|
173
|
+
end
|
174
|
+
|
175
|
+
# Perform a USB port reset to reinitialize a device.
|
176
|
+
#
|
177
|
+
# The system will attempt to restore the previous configuration and
|
178
|
+
# alternate settings after the reset has completed.
|
179
|
+
#
|
180
|
+
# If the reset fails, the descriptors change, or the previous
|
181
|
+
# state cannot be restored, the device will appear to be disconnected
|
182
|
+
# and reconnected. This means that the device handle is no longer
|
183
|
+
# valid (you should close it) and rediscover the device. A Exception
|
184
|
+
# of LIBUSB::ERROR_NOT_FOUND indicates when this is the case.
|
185
|
+
#
|
186
|
+
# This is a blocking function which usually incurs a noticeable delay.
|
187
|
+
def reset_device
|
188
|
+
res = Call.libusb_reset_device(@pHandle)
|
189
|
+
LIBUSB.raise_error res, "in libusb_reset_device" if res!=0
|
190
|
+
end
|
191
|
+
|
192
|
+
# Determine if a kernel driver is active on an interface.
|
193
|
+
#
|
194
|
+
# If a kernel driver is active, you cannot claim the interface,
|
195
|
+
# and libusb will be unable to perform I/O.
|
196
|
+
#
|
197
|
+
# @param [Interface, Fixnum] interface the interface to check or it's bInterfaceNumber
|
198
|
+
# @return [Boolean]
|
199
|
+
def kernel_driver_active?(interface)
|
200
|
+
interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
|
201
|
+
res = Call.libusb_kernel_driver_active(@pHandle, interface)
|
202
|
+
LIBUSB.raise_error res, "in libusb_kernel_driver_active" unless res>=0
|
203
|
+
return res==1
|
204
|
+
end
|
205
|
+
|
206
|
+
# Detach a kernel driver from an interface.
|
207
|
+
#
|
208
|
+
# If successful, you will then be able to claim the interface and perform I/O.
|
209
|
+
#
|
210
|
+
# @param [Interface, Fixnum] interface the interface to detach the driver
|
211
|
+
# from or it's bInterfaceNumber
|
212
|
+
def detach_kernel_driver(interface)
|
213
|
+
interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
|
214
|
+
res = Call.libusb_detach_kernel_driver(@pHandle, interface)
|
215
|
+
LIBUSB.raise_error res, "in libusb_detach_kernel_driver" if res!=0
|
216
|
+
end
|
217
|
+
|
218
|
+
# Re-attach an interface's kernel driver, which was previously detached
|
219
|
+
# using {DevHandle#detach_kernel_driver}.
|
220
|
+
#
|
221
|
+
# @param [Interface, Fixnum] interface the interface to attach the driver to
|
222
|
+
def attach_kernel_driver(interface)
|
223
|
+
interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
|
224
|
+
res = Call.libusb_attach_kernel_driver(@pHandle, interface)
|
225
|
+
LIBUSB.raise_error res, "in libusb_attach_kernel_driver" if res!=0
|
226
|
+
end
|
227
|
+
|
228
|
+
|
229
|
+
# Perform a USB bulk transfer.
|
230
|
+
#
|
231
|
+
# When called without a block, the transfer is done synchronously - so all events are handled
|
232
|
+
# internally and the sent/received data will be returned after completion or an exception will be raised.
|
233
|
+
#
|
234
|
+
# When called with a block, the method returns immediately after submitting the transfer.
|
235
|
+
# You then have to ensure, that {Context#handle_events} is called properly. As soon as the
|
236
|
+
# transfer is completed, the block is called with the sent/received data in case of success
|
237
|
+
# or the exception instance in case of failure.
|
238
|
+
#
|
239
|
+
# The direction of the transfer is inferred from the direction bits of the
|
240
|
+
# endpoint address.
|
241
|
+
#
|
242
|
+
# For bulk reads, the +:dataIn+ param indicates the maximum length of data you are
|
243
|
+
# expecting to receive. If less data arrives than expected, this function will
|
244
|
+
# return that data.
|
245
|
+
#
|
246
|
+
# You should check the returned number of bytes for bulk writes. Not all of the
|
247
|
+
# data may have been written.
|
248
|
+
#
|
249
|
+
# Also check {Error#transferred} when dealing with a timeout exception. libusb may have
|
250
|
+
# to split your transfer into a number of chunks to satisfy underlying O/S
|
251
|
+
# requirements, meaning that the timeout may expire after the first few chunks
|
252
|
+
# have completed. libusb is careful not to lose any data that may have been
|
253
|
+
# transferred; do not assume that timeout conditions indicate a complete lack of
|
254
|
+
# I/O.
|
255
|
+
#
|
256
|
+
# @param [Hash] args
|
257
|
+
# @option args [Endpoint, Fixnum] :endpoint the (address of a) valid endpoint to communicate with
|
258
|
+
# @option args [String] :dataOut the data to send with an outgoing transfer
|
259
|
+
# @option args [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
|
260
|
+
# @option args [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
|
261
|
+
# up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
|
262
|
+
#
|
263
|
+
# @return [Fixnum] Number of bytes sent for an outgoing transfer
|
264
|
+
# @return [String] Received data for an ingoing transfer
|
265
|
+
# @return [self] When called with a block
|
266
|
+
#
|
267
|
+
# @yieldparam [String, Integer, LIBUSB::Error] result result of the transfer is yielded to the block,
|
268
|
+
# when the asynchronous transfer has finished
|
269
|
+
# @raise [ArgumentError, LIBUSB::Error] in case of failure
|
270
|
+
def bulk_transfer(args={}, &block)
|
271
|
+
timeout = args.delete(:timeout) || 1000
|
272
|
+
endpoint = args.delete(:endpoint) || raise(ArgumentError, "no endpoint given")
|
273
|
+
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
274
|
+
if endpoint&ENDPOINT_IN != 0
|
275
|
+
dataIn = args.delete(:dataIn) || raise(ArgumentError, "no :dataIn given for bulk read")
|
276
|
+
else
|
277
|
+
dataOut = args.delete(:dataOut) || raise(ArgumentError, "no :dataOut given for bulk write")
|
278
|
+
end
|
279
|
+
raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
|
280
|
+
|
281
|
+
# reuse transfer struct to speed up transfer
|
282
|
+
@bulk_transfer ||= BulkTransfer.new :dev_handle => self
|
283
|
+
tr = @bulk_transfer
|
284
|
+
tr.endpoint = endpoint
|
285
|
+
tr.timeout = timeout
|
286
|
+
if dataOut
|
287
|
+
tr.buffer = dataOut
|
288
|
+
else
|
289
|
+
tr.alloc_buffer(dataIn)
|
290
|
+
end
|
291
|
+
|
292
|
+
submit_transfer(tr, dataIn, 0, &block)
|
293
|
+
end
|
294
|
+
|
295
|
+
# Perform a USB interrupt transfer.
|
296
|
+
#
|
297
|
+
# When called without a block, the transfer is done synchronously - so all events are handled
|
298
|
+
# internally and the sent/received data will be returned after completion or an exception will be raised.
|
299
|
+
#
|
300
|
+
# When called with a block, the method returns immediately after submitting the transfer.
|
301
|
+
# You then have to ensure, that {Context#handle_events} is called properly. As soon as the
|
302
|
+
# transfer is completed, the block is called with the sent/received data in case of success
|
303
|
+
# or the exception instance in case of failure.
|
304
|
+
#
|
305
|
+
# The direction of the transfer is inferred from the direction bits of the
|
306
|
+
# endpoint address.
|
307
|
+
#
|
308
|
+
# For interrupt reads, the +:dataIn+ param indicates the maximum length of data you
|
309
|
+
# are expecting to receive. If less data arrives than expected, this function will
|
310
|
+
# return that data.
|
311
|
+
#
|
312
|
+
# You should check the returned number of bytes for interrupt writes. Not all of
|
313
|
+
# the data may have been written.
|
314
|
+
#
|
315
|
+
# Also check {Error#transferred} when dealing with a timeout exception. libusb may have
|
316
|
+
# to split your transfer into a number of chunks to satisfy underlying O/S
|
317
|
+
# requirements, meaning that the timeout may expire after the first few chunks
|
318
|
+
# have completed. libusb is careful not to lose any data that may have been
|
319
|
+
# transferred; do not assume that timeout conditions indicate a complete lack of
|
320
|
+
# I/O.
|
321
|
+
#
|
322
|
+
# The default endpoint bInterval value is used as the polling interval.
|
323
|
+
#
|
324
|
+
# @param [Hash] args
|
325
|
+
# @option args [Endpoint, Fixnum] :endpoint the (address of a) valid endpoint to communicate with
|
326
|
+
# @option args [String] :dataOut the data to send with an outgoing transfer
|
327
|
+
# @option args [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
|
328
|
+
# @option args [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
|
329
|
+
# up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
|
330
|
+
#
|
331
|
+
# @return [Fixnum] Number of bytes sent for an outgoing transfer
|
332
|
+
# @return [String] Received data for an ingoing transfer
|
333
|
+
# @return [self] When called with a block
|
334
|
+
#
|
335
|
+
# @yieldparam [String, Integer, LIBUSB::Error] result result of the transfer is yielded to the block,
|
336
|
+
# when the asynchronous transfer has finished
|
337
|
+
# @raise [ArgumentError, LIBUSB::Error] in case of failure
|
338
|
+
def interrupt_transfer(args={}, &block)
|
339
|
+
timeout = args.delete(:timeout) || 1000
|
340
|
+
endpoint = args.delete(:endpoint) || raise(ArgumentError, "no endpoint given")
|
341
|
+
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
342
|
+
if endpoint&ENDPOINT_IN != 0
|
343
|
+
dataIn = args.delete(:dataIn) || raise(ArgumentError, "no :dataIn given for interrupt read")
|
344
|
+
else
|
345
|
+
dataOut = args.delete(:dataOut) || raise(ArgumentError, "no :dataOut given for interrupt write")
|
346
|
+
end
|
347
|
+
raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
|
348
|
+
|
349
|
+
# reuse transfer struct to speed up transfer
|
350
|
+
@interrupt_transfer ||= InterruptTransfer.new :dev_handle => self
|
351
|
+
tr = @interrupt_transfer
|
352
|
+
tr.endpoint = endpoint
|
353
|
+
tr.timeout = timeout
|
354
|
+
if dataOut
|
355
|
+
tr.buffer = dataOut
|
356
|
+
else
|
357
|
+
tr.alloc_buffer(dataIn)
|
358
|
+
end
|
359
|
+
|
360
|
+
submit_transfer(tr, dataIn, 0, &block)
|
361
|
+
end
|
362
|
+
|
363
|
+
# Perform a USB control transfer.
|
364
|
+
#
|
365
|
+
# When called without a block, the transfer is done synchronously - so all events are handled
|
366
|
+
# internally and the sent/received data will be returned after completion or an exception will be raised.
|
367
|
+
#
|
368
|
+
# When called with a block, the method returns immediately after submitting the transfer.
|
369
|
+
# You then have to ensure, that {Context#handle_events} is called properly. As soon as the
|
370
|
+
# transfer is completed, the block is called with the sent/received data in case of success
|
371
|
+
# or the exception instance in case of failure.
|
372
|
+
#
|
373
|
+
# The direction of the transfer is inferred from the +:bmRequestType+ field of the
|
374
|
+
# setup packet.
|
375
|
+
#
|
376
|
+
# @param [Hash] args
|
377
|
+
# @option args [Fixnum] :bmRequestType the request type field for the setup packet
|
378
|
+
# @option args [Fixnum] :bRequest the request field for the setup packet
|
379
|
+
# @option args [Fixnum] :wValue the value field for the setup packet
|
380
|
+
# @option args [Fixnum] :wIndex the index field for the setup packet
|
381
|
+
# @option args [String] :dataOut the data to send with an outgoing transfer, it
|
382
|
+
# is appended to the setup packet
|
383
|
+
# @option args [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
|
384
|
+
# (excluding setup packet)
|
385
|
+
# @option args [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
|
386
|
+
# up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
|
387
|
+
#
|
388
|
+
# @return [Fixnum] Number of bytes sent (excluding setup packet) for outgoing transfer
|
389
|
+
# @return [String] Received data (without setup packet) for ingoing transfer
|
390
|
+
# @return [self] When called with a block
|
391
|
+
#
|
392
|
+
# @yieldparam [String, Integer, LIBUSB::Error] result result of the transfer is yielded to the block,
|
393
|
+
# when the asynchronous transfer has finished
|
394
|
+
# @raise [ArgumentError, LIBUSB::Error] in case of failure
|
395
|
+
def control_transfer(args={}, &block)
|
396
|
+
bmRequestType = args.delete(:bmRequestType) || raise(ArgumentError, "param :bmRequestType not given")
|
397
|
+
bRequest = args.delete(:bRequest) || raise(ArgumentError, "param :bRequest not given")
|
398
|
+
wValue = args.delete(:wValue) || raise(ArgumentError, "param :wValue not given")
|
399
|
+
wIndex = args.delete(:wIndex) || raise(ArgumentError, "param :wIndex not given")
|
400
|
+
timeout = args.delete(:timeout) || 1000
|
401
|
+
if bmRequestType&ENDPOINT_IN != 0
|
402
|
+
dataIn = args.delete(:dataIn) || 0
|
403
|
+
dataOut = ''
|
404
|
+
else
|
405
|
+
dataOut = args.delete(:dataOut) || ''
|
406
|
+
end
|
407
|
+
raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
|
408
|
+
|
409
|
+
# reuse transfer struct to speed up transfer
|
410
|
+
@control_transfer ||= ControlTransfer.new :dev_handle => self
|
411
|
+
tr = @control_transfer
|
412
|
+
tr.timeout = timeout
|
413
|
+
if dataIn
|
414
|
+
setup_data = [bmRequestType, bRequest, wValue, wIndex, dataIn].pack('CCvvv')
|
415
|
+
tr.alloc_buffer( dataIn + CONTROL_SETUP_SIZE, setup_data )
|
416
|
+
else
|
417
|
+
tr.buffer = [bmRequestType, bRequest, wValue, wIndex, dataOut.bytesize, dataOut].pack('CCvvva*')
|
418
|
+
end
|
419
|
+
|
420
|
+
submit_transfer(tr, dataIn, CONTROL_SETUP_SIZE, &block)
|
421
|
+
end
|
422
|
+
|
423
|
+
private
|
424
|
+
def submit_transfer(tr, dataIn, offset)
|
425
|
+
if block_given?
|
426
|
+
tr.submit! do
|
427
|
+
res = dataIn ? tr.actual_buffer(offset) : tr.actual_length
|
428
|
+
|
429
|
+
if tr.status==:TRANSFER_COMPLETED
|
430
|
+
yield res
|
431
|
+
else
|
432
|
+
exception = Transfer::TransferStatusToError[tr.status] || ERROR_OTHER
|
433
|
+
|
434
|
+
yield exception.new("error #{tr.status}", res)
|
435
|
+
end
|
436
|
+
end
|
437
|
+
self
|
438
|
+
else
|
439
|
+
tr.submit_and_wait
|
440
|
+
|
441
|
+
res = dataIn ? tr.actual_buffer(offset) : tr.actual_length
|
442
|
+
|
443
|
+
unless tr.status==:TRANSFER_COMPLETED
|
444
|
+
raise((Transfer::TransferStatusToError[tr.status] || ERROR_OTHER).new("error #{tr.status}", res))
|
445
|
+
end
|
446
|
+
res
|
447
|
+
end
|
448
|
+
end
|
449
|
+
end
|
450
|
+
end
|
@@ -0,0 +1,359 @@
|
|
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
|
+
|
25
|
+
# @return [Context] the context this device belongs to.
|
26
|
+
attr_reader :context
|
27
|
+
|
28
|
+
def initialize context, pDev
|
29
|
+
@context = context
|
30
|
+
def pDev.unref_device(id)
|
31
|
+
Call.libusb_unref_device(self)
|
32
|
+
end
|
33
|
+
ObjectSpace.define_finalizer(self, pDev.method(:unref_device))
|
34
|
+
Call.libusb_ref_device(pDev)
|
35
|
+
@pDev = pDev
|
36
|
+
|
37
|
+
@pDevDesc = Call::DeviceDescriptor.new
|
38
|
+
res = Call.libusb_get_device_descriptor(@pDev, @pDevDesc)
|
39
|
+
LIBUSB.raise_error res, "in libusb_get_device_descriptor" if res!=0
|
40
|
+
end
|
41
|
+
|
42
|
+
# Open the device and obtain a device handle.
|
43
|
+
#
|
44
|
+
# A handle allows you to perform I/O on the device in question.
|
45
|
+
# This is a non-blocking function; no requests are sent over the bus.
|
46
|
+
#
|
47
|
+
# If called with a block, the handle is passed to the block
|
48
|
+
# and is closed when the block has finished.
|
49
|
+
#
|
50
|
+
# You need proper device access:
|
51
|
+
# * Linux: read+write permissions to <tt>/dev/bus/usb/<bus>/<dev></tt>
|
52
|
+
# * Windows: by installing a WinUSB-driver for the device (see {file:README.rdoc#Usage_on_Windows} )
|
53
|
+
#
|
54
|
+
# @return [DevHandle] Handle to the device.
|
55
|
+
def open
|
56
|
+
ppHandle = FFI::MemoryPointer.new :pointer
|
57
|
+
res = Call.libusb_open(@pDev, ppHandle)
|
58
|
+
LIBUSB.raise_error res, "in libusb_open" if res!=0
|
59
|
+
handle = DevHandle.new self, ppHandle.read_pointer
|
60
|
+
return handle unless block_given?
|
61
|
+
begin
|
62
|
+
yield handle
|
63
|
+
ensure
|
64
|
+
handle.close
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Open the device and claim an interface.
|
69
|
+
#
|
70
|
+
# This is a convenience method to {Device#open} and {DevHandle#claim_interface}.
|
71
|
+
# Must be called with a block. When the block has finished, the interface
|
72
|
+
# will be released and the device will be closed.
|
73
|
+
#
|
74
|
+
# @param [Interface, Fixnum] interface the interface or it's bInterfaceNumber you wish to claim
|
75
|
+
def open_interface(interface)
|
76
|
+
open do |dev|
|
77
|
+
dev.claim_interface(interface) do
|
78
|
+
yield dev
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Get the number of the bus that a device is connected to.
|
84
|
+
def bus_number
|
85
|
+
Call.libusb_get_bus_number(@pDev)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Get the address of the device on the bus it is connected to.
|
89
|
+
def device_address
|
90
|
+
Call.libusb_get_device_address(@pDev)
|
91
|
+
end
|
92
|
+
|
93
|
+
if Call.respond_to?(:libusb_get_port_number)
|
94
|
+
# Get the number of the port that a device is connected to.
|
95
|
+
# Available since libusb-1.0.12.
|
96
|
+
#
|
97
|
+
# @return [Fixnum, nil] the port number (+nil+ if not available)
|
98
|
+
# @see #port_path
|
99
|
+
def port_number
|
100
|
+
r = Call.libusb_get_port_number(@pDev)
|
101
|
+
r==0 ? nil : r
|
102
|
+
end
|
103
|
+
|
104
|
+
# Get the the parent from the specified device [EXPERIMENTAL].
|
105
|
+
# Available since libusb-1.0.12.
|
106
|
+
#
|
107
|
+
# @return [Device, nil] the device parent or +nil+ if not available
|
108
|
+
# @see #port_path
|
109
|
+
def parent
|
110
|
+
pppDevs = FFI::MemoryPointer.new :pointer
|
111
|
+
Call.libusb_get_device_list(@context.instance_variable_get(:@ctx), pppDevs)
|
112
|
+
ppDevs = pppDevs.read_pointer
|
113
|
+
pParent = Call.libusb_get_parent(@pDev)
|
114
|
+
parent = pParent.null? ? nil : Device.new(@context, pParent)
|
115
|
+
Call.libusb_free_device_list(ppDevs, 1)
|
116
|
+
parent
|
117
|
+
end
|
118
|
+
|
119
|
+
# Get the list of all port numbers from root for the specified device.
|
120
|
+
# Available since libusb-1.0.12.
|
121
|
+
#
|
122
|
+
# @return [Array<Fixnum>]
|
123
|
+
# @see #parent
|
124
|
+
# @see #port_number
|
125
|
+
def port_path
|
126
|
+
# As per the USB 3.0 specs, the current maximum limit for the depth is 7.
|
127
|
+
path_len = 7
|
128
|
+
pPath = FFI::MemoryPointer.new :pointer, path_len
|
129
|
+
l = Call.libusb_get_port_path(@context.instance_variable_get(:@ctx), @pDev, pPath, path_len)
|
130
|
+
pPath.read_array_of_uint8(l)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
if Call.respond_to?(:libusb_get_device_speed)
|
135
|
+
# Get the negotiated connection speed for a device.
|
136
|
+
# Available since libusb-1.0.9.
|
137
|
+
#
|
138
|
+
# @return [Symbol] a {Call::Speeds Speeds} symbol, where +:SPEED_UNKNOWN+ means that
|
139
|
+
# the OS doesn't know or doesn't support returning the negotiated speed.
|
140
|
+
def device_speed
|
141
|
+
Call.libusb_get_device_speed(@pDev)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Convenience function to retrieve the wMaxPacketSize value for a
|
146
|
+
# particular endpoint in the active device configuration.
|
147
|
+
#
|
148
|
+
# @param [Endpoint, Fixnum] endpoint (address of) the endpoint in question
|
149
|
+
# @return [Fixnum] the wMaxPacketSize value
|
150
|
+
def max_packet_size(endpoint)
|
151
|
+
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
152
|
+
res = Call.libusb_get_max_packet_size(@pDev, endpoint)
|
153
|
+
LIBUSB.raise_error res, "in libusb_get_max_packet_size" unless res>=0
|
154
|
+
res
|
155
|
+
end
|
156
|
+
|
157
|
+
# Calculate the maximum packet size which a specific endpoint is capable is
|
158
|
+
# sending or receiving in the duration of 1 microframe.
|
159
|
+
#
|
160
|
+
# Only the active configution is examined. The calculation is based on the
|
161
|
+
# wMaxPacketSize field in the endpoint descriptor as described in section 9.6.6
|
162
|
+
# in the USB 2.0 specifications.
|
163
|
+
#
|
164
|
+
# If acting on an isochronous or interrupt endpoint, this function will
|
165
|
+
# multiply the value found in bits 0:10 by the number of transactions per
|
166
|
+
# microframe (determined by bits 11:12). Otherwise, this function just returns
|
167
|
+
# the numeric value found in bits 0:10.
|
168
|
+
#
|
169
|
+
# This function is useful for setting up isochronous transfers, for example
|
170
|
+
# you might use the return value from this function to call
|
171
|
+
# IsoPacket#alloc_buffer in order to set the length field
|
172
|
+
# of an isochronous packet in a transfer.
|
173
|
+
#
|
174
|
+
# @param [Endpoint, Fixnum] endpoint (address of) the endpoint in question
|
175
|
+
# @return [Fixnum] the maximum packet size which can be sent/received on this endpoint
|
176
|
+
def max_iso_packet_size(endpoint)
|
177
|
+
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
178
|
+
res = Call.libusb_get_max_iso_packet_size(@pDev, endpoint)
|
179
|
+
LIBUSB.raise_error res, "in libusb_get_max_iso_packet_size" unless res>=0
|
180
|
+
res
|
181
|
+
end
|
182
|
+
|
183
|
+
# Obtain a config descriptor of the device.
|
184
|
+
#
|
185
|
+
# @param [Fixnum] index number of the config descriptor
|
186
|
+
# @return Configuration
|
187
|
+
def config_descriptor(index)
|
188
|
+
ppConfig = FFI::MemoryPointer.new :pointer
|
189
|
+
res = Call.libusb_get_config_descriptor(@pDev, index, ppConfig)
|
190
|
+
LIBUSB.raise_error res, "in libusb_get_config_descriptor" if res!=0
|
191
|
+
pConfig = ppConfig.read_pointer
|
192
|
+
config = Configuration.new(self, pConfig)
|
193
|
+
config
|
194
|
+
end
|
195
|
+
|
196
|
+
# Size of the Descriptor in Bytes (18 bytes)
|
197
|
+
def bLength
|
198
|
+
@pDevDesc[:bLength]
|
199
|
+
end
|
200
|
+
|
201
|
+
# Device Descriptor (0x01)
|
202
|
+
def bDescriptorType
|
203
|
+
@pDevDesc[:bDescriptorType]
|
204
|
+
end
|
205
|
+
|
206
|
+
# USB specification release number which device complies too
|
207
|
+
#
|
208
|
+
# @return [Integer] in binary-coded decimal
|
209
|
+
def bcdUSB
|
210
|
+
@pDevDesc[:bcdUSB]
|
211
|
+
end
|
212
|
+
|
213
|
+
# USB-IF class code for the device (Assigned by USB Org)
|
214
|
+
#
|
215
|
+
# * If equal to 0x00, each interface specifies it's own class code
|
216
|
+
# * If equal to 0xFF, the class code is vendor specified
|
217
|
+
# * Otherwise field is valid Class Code
|
218
|
+
def bDeviceClass
|
219
|
+
@pDevDesc[:bDeviceClass]
|
220
|
+
end
|
221
|
+
|
222
|
+
# USB-IF subclass code for the device, qualified by the {Device#bDeviceClass}
|
223
|
+
# value (Assigned by USB Org)
|
224
|
+
def bDeviceSubClass
|
225
|
+
@pDevDesc[:bDeviceSubClass]
|
226
|
+
end
|
227
|
+
|
228
|
+
# USB-IF protocol code for the device, qualified by the {Device#bDeviceClass}
|
229
|
+
# and {Device#bDeviceSubClass} values (Assigned by USB Org)
|
230
|
+
def bDeviceProtocol
|
231
|
+
@pDevDesc[:bDeviceProtocol]
|
232
|
+
end
|
233
|
+
|
234
|
+
# Maximum Packet Size for Endpoint 0. Valid Sizes are 8, 16, 32, 64
|
235
|
+
def bMaxPacketSize0
|
236
|
+
@pDevDesc[:bMaxPacketSize0]
|
237
|
+
end
|
238
|
+
|
239
|
+
# USB-IF vendor ID (Assigned by USB Org)
|
240
|
+
def idVendor
|
241
|
+
@pDevDesc[:idVendor]
|
242
|
+
end
|
243
|
+
|
244
|
+
# USB-IF product ID (Assigned by Manufacturer)
|
245
|
+
def idProduct
|
246
|
+
@pDevDesc[:idProduct]
|
247
|
+
end
|
248
|
+
|
249
|
+
# Device release number in binary-coded decimal.
|
250
|
+
def bcdDevice
|
251
|
+
@pDevDesc[:bcdDevice]
|
252
|
+
end
|
253
|
+
|
254
|
+
# Index of string descriptor describing manufacturer.
|
255
|
+
def iManufacturer
|
256
|
+
@pDevDesc[:iManufacturer]
|
257
|
+
end
|
258
|
+
|
259
|
+
# Index of string descriptor describing product.
|
260
|
+
def iProduct
|
261
|
+
@pDevDesc[:iProduct]
|
262
|
+
end
|
263
|
+
|
264
|
+
# Index of string descriptor containing device serial number.
|
265
|
+
def iSerialNumber
|
266
|
+
@pDevDesc[:iSerialNumber]
|
267
|
+
end
|
268
|
+
|
269
|
+
# Number of Possible Configurations
|
270
|
+
def bNumConfigurations
|
271
|
+
@pDevDesc[:bNumConfigurations]
|
272
|
+
end
|
273
|
+
|
274
|
+
|
275
|
+
def inspect
|
276
|
+
attrs = []
|
277
|
+
attrs << "#{self.bus_number}/#{self.device_address}"
|
278
|
+
attrs << ("%04x:%04x" % [self.idVendor, self.idProduct])
|
279
|
+
attrs << self.manufacturer
|
280
|
+
attrs << self.product
|
281
|
+
attrs << self.serial_number
|
282
|
+
if self.bDeviceClass == LIBUSB::CLASS_PER_INTERFACE
|
283
|
+
devclass = self.settings.map {|i|
|
284
|
+
LIBUSB.dev_string(i.bInterfaceClass, i.bInterfaceSubClass, i.bInterfaceProtocol)
|
285
|
+
}.join(", ")
|
286
|
+
else
|
287
|
+
devclass = LIBUSB.dev_string(self.bDeviceClass, self.bDeviceSubClass, self.bDeviceProtocol)
|
288
|
+
end
|
289
|
+
attrs << "(#{devclass})"
|
290
|
+
attrs.compact!
|
291
|
+
"\#<#{self.class} #{attrs.join(' ')}>"
|
292
|
+
end
|
293
|
+
|
294
|
+
def try_string_descriptor_ascii(i)
|
295
|
+
begin
|
296
|
+
open{|h| h.string_descriptor_ascii(i) }
|
297
|
+
rescue
|
298
|
+
"?"
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
# Return manufacturer of the device
|
303
|
+
# @return String
|
304
|
+
def manufacturer
|
305
|
+
return @manufacturer if defined? @manufacturer
|
306
|
+
@manufacturer = try_string_descriptor_ascii(self.iManufacturer)
|
307
|
+
@manufacturer.strip! if @manufacturer
|
308
|
+
@manufacturer
|
309
|
+
end
|
310
|
+
|
311
|
+
# Return product name of the device.
|
312
|
+
# @return String
|
313
|
+
def product
|
314
|
+
return @product if defined? @product
|
315
|
+
@product = try_string_descriptor_ascii(self.iProduct)
|
316
|
+
@product.strip! if @product
|
317
|
+
@product
|
318
|
+
end
|
319
|
+
|
320
|
+
# Return serial number of the device.
|
321
|
+
# @return String
|
322
|
+
def serial_number
|
323
|
+
return @serial_number if defined? @serial_number
|
324
|
+
@serial_number = try_string_descriptor_ascii(self.iSerialNumber)
|
325
|
+
@serial_number.strip! if @serial_number
|
326
|
+
@serial_number
|
327
|
+
end
|
328
|
+
|
329
|
+
# Return configurations of the device.
|
330
|
+
# @return [Array<Configuration>]
|
331
|
+
def configurations
|
332
|
+
configs = []
|
333
|
+
bNumConfigurations.times do |config_index|
|
334
|
+
begin
|
335
|
+
configs << config_descriptor(config_index)
|
336
|
+
rescue RuntimeError
|
337
|
+
# On Windows some devices don't return it's configuration.
|
338
|
+
end
|
339
|
+
end
|
340
|
+
configs
|
341
|
+
end
|
342
|
+
|
343
|
+
# Return all interfaces of this device.
|
344
|
+
# @return [Array<Interface>]
|
345
|
+
def interfaces() self.configurations.map {|d| d.interfaces }.flatten end
|
346
|
+
# Return all interface decriptions of this device.
|
347
|
+
# @return [Array<Setting>]
|
348
|
+
def settings() self.interfaces.map {|d| d.settings }.flatten end
|
349
|
+
# Return all endpoints of all interfaces of this device.
|
350
|
+
# @return [Array<Endpoint>]
|
351
|
+
def endpoints() self.settings.map {|d| d.endpoints }.flatten end
|
352
|
+
|
353
|
+
def <=>(o)
|
354
|
+
t = bus_number<=>o.bus_number
|
355
|
+
t = device_address<=>o.device_address if t==0
|
356
|
+
t
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end
|