libusb 0.3.3-x64-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- 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
|