libusb 0.1.0-x86-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.
- data/.autotest +23 -0
- data/.gemtest +0 -0
- data/COPYING +165 -0
- data/History.txt +15 -0
- data/Manifest.txt +15 -0
- data/README.rdoc +110 -0
- data/Rakefile +144 -0
- data/lib/libusb/compat.rb +370 -0
- data/lib/libusb-1.0.dll +0 -0
- data/lib/libusb.rb +1523 -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 +122 -0
- data/test/test_libusb_gc.rb +37 -0
- data/test/test_libusb_iso_transfer.rb +45 -0
- data/test/test_libusb_mass_storage.rb +271 -0
- metadata +107 -0
data/lib/libusb.rb
ADDED
@@ -0,0 +1,1523 @@
|
|
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 'rubygems'
|
17
|
+
require 'ffi'
|
18
|
+
|
19
|
+
|
20
|
+
module LIBUSB
|
21
|
+
VERSION = "0.1.0"
|
22
|
+
|
23
|
+
module Call
|
24
|
+
extend FFI::Library
|
25
|
+
if RUBY_PLATFORM=~/mingw|mswin/i
|
26
|
+
bundled_dll = File.join(File.dirname(__FILE__), 'libusb-1.0.dll')
|
27
|
+
ffi_lib(['libusb-1.0', bundled_dll])
|
28
|
+
else
|
29
|
+
ffi_lib 'libusb-1.0'
|
30
|
+
end
|
31
|
+
|
32
|
+
ClassCodes = enum :libusb_class_code, [
|
33
|
+
:CLASS_PER_INTERFACE, 0,
|
34
|
+
:CLASS_AUDIO, 1,
|
35
|
+
:CLASS_COMM, 2,
|
36
|
+
:CLASS_HID, 3,
|
37
|
+
:CLASS_PRINTER, 7,
|
38
|
+
:CLASS_PTP, 6,
|
39
|
+
:CLASS_MASS_STORAGE, 8,
|
40
|
+
:CLASS_HUB, 9,
|
41
|
+
:CLASS_DATA, 10,
|
42
|
+
:CLASS_WIRELESS, 0xe0,
|
43
|
+
:CLASS_APPLICATION, 0xfe,
|
44
|
+
:CLASS_VENDOR_SPEC, 0xff
|
45
|
+
]
|
46
|
+
|
47
|
+
Errors = enum :libusb_error, [
|
48
|
+
:SUCCESS, 0,
|
49
|
+
:ERROR_IO, -1,
|
50
|
+
:ERROR_INVALID_PARAM, -2,
|
51
|
+
:ERROR_ACCESS, -3,
|
52
|
+
:ERROR_NO_DEVICE, -4,
|
53
|
+
:ERROR_NOT_FOUND, -5,
|
54
|
+
:ERROR_BUSY, -6,
|
55
|
+
:ERROR_TIMEOUT, -7,
|
56
|
+
:ERROR_OVERFLOW, -8,
|
57
|
+
:ERROR_PIPE, -9,
|
58
|
+
:ERROR_INTERRUPTED, -10,
|
59
|
+
:ERROR_NO_MEM, -11,
|
60
|
+
:ERROR_NOT_SUPPORTED, -12,
|
61
|
+
:ERROR_OTHER, -99,
|
62
|
+
]
|
63
|
+
|
64
|
+
# Transfer status codes
|
65
|
+
TransferStatus = enum :libusb_transfer_status, [
|
66
|
+
:TRANSFER_COMPLETED,
|
67
|
+
:TRANSFER_ERROR,
|
68
|
+
:TRANSFER_TIMED_OUT,
|
69
|
+
:TRANSFER_CANCELLED,
|
70
|
+
:TRANSFER_STALL,
|
71
|
+
:TRANSFER_NO_DEVICE,
|
72
|
+
:TRANSFER_OVERFLOW,
|
73
|
+
]
|
74
|
+
|
75
|
+
# libusb_transfer.flags values
|
76
|
+
TransferFlags = enum :libusb_transfer_flags, [
|
77
|
+
:TRANSFER_SHORT_NOT_OK, 1 << 0,
|
78
|
+
:TRANSFER_FREE_BUFFER, 1 << 1,
|
79
|
+
:TRANSFER_FREE_TRANSFER, 1 << 2,
|
80
|
+
]
|
81
|
+
|
82
|
+
TransferTypes = enum :libusb_transfer_type, [
|
83
|
+
:TRANSFER_TYPE_CONTROL, 0,
|
84
|
+
:TRANSFER_TYPE_ISOCHRONOUS, 1,
|
85
|
+
:TRANSFER_TYPE_BULK, 2,
|
86
|
+
:TRANSFER_TYPE_INTERRUPT, 3,
|
87
|
+
]
|
88
|
+
|
89
|
+
StandardRequests = enum :libusb_standard_request, [
|
90
|
+
:REQUEST_GET_STATUS, 0x00,
|
91
|
+
:REQUEST_CLEAR_FEATURE, 0x01,
|
92
|
+
:REQUEST_SET_FEATURE, 0x03,
|
93
|
+
:REQUEST_SET_ADDRESS, 0x05,
|
94
|
+
:REQUEST_GET_DESCRIPTOR, 0x06,
|
95
|
+
:REQUEST_SET_DESCRIPTOR, 0x07,
|
96
|
+
:REQUEST_GET_CONFIGURATION, 0x08,
|
97
|
+
:REQUEST_SET_CONFIGURATION, 0x09,
|
98
|
+
:REQUEST_GET_INTERFACE, 0x0A,
|
99
|
+
:REQUEST_SET_INTERFACE, 0x0B,
|
100
|
+
:REQUEST_SYNCH_FRAME, 0x0C,
|
101
|
+
]
|
102
|
+
|
103
|
+
EndpointDirections = enum :libusb_endpoint_direction, [
|
104
|
+
:ENDPOINT_IN, 0x80,
|
105
|
+
:ENDPOINT_OUT, 0x00,
|
106
|
+
]
|
107
|
+
|
108
|
+
DescriptorTypes = enum :libusb_descriptor_type, [
|
109
|
+
:DT_DEVICE, 0x01,
|
110
|
+
:DT_CONFIG, 0x02,
|
111
|
+
:DT_STRING, 0x03,
|
112
|
+
:DT_INTERFACE, 0x04,
|
113
|
+
:DT_ENDPOINT, 0x05,
|
114
|
+
:DT_HID, 0x21,
|
115
|
+
:DT_REPORT, 0x22,
|
116
|
+
:DT_PHYSICAL, 0x23,
|
117
|
+
:DT_HUB, 0x29,
|
118
|
+
]
|
119
|
+
|
120
|
+
RequestTypes = enum :libusb_request_type, [
|
121
|
+
:REQUEST_TYPE_STANDARD, (0x00 << 5),
|
122
|
+
:REQUEST_TYPE_CLASS, (0x01 << 5),
|
123
|
+
:REQUEST_TYPE_VENDOR, (0x02 << 5),
|
124
|
+
:REQUEST_TYPE_RESERVED, (0x03 << 5),
|
125
|
+
]
|
126
|
+
|
127
|
+
RequestRecipients = enum :libusb_request_recipient, [
|
128
|
+
:RECIPIENT_DEVICE, 0x00,
|
129
|
+
:RECIPIENT_INTERFACE, 0x01,
|
130
|
+
:RECIPIENT_ENDPOINT, 0x02,
|
131
|
+
:RECIPIENT_OTHER, 0x03,
|
132
|
+
]
|
133
|
+
|
134
|
+
IsoSyncTypes = enum :libusb_iso_sync_type, [
|
135
|
+
:ISO_SYNC_TYPE_NONE, 0,
|
136
|
+
:ISO_SYNC_TYPE_ASYNC, 1,
|
137
|
+
:ISO_SYNC_TYPE_ADAPTIVE, 2,
|
138
|
+
:ISO_SYNC_TYPE_SYNC, 3,
|
139
|
+
]
|
140
|
+
|
141
|
+
|
142
|
+
typedef :pointer, :libusb_context
|
143
|
+
typedef :pointer, :libusb_device_handle
|
144
|
+
|
145
|
+
attach_function 'libusb_init', [ :pointer ], :int
|
146
|
+
attach_function 'libusb_exit', [ :pointer ], :void
|
147
|
+
attach_function 'libusb_set_debug', [:pointer, :int], :void
|
148
|
+
|
149
|
+
attach_function 'libusb_get_device_list', [:pointer, :pointer], :size_t
|
150
|
+
attach_function 'libusb_free_device_list', [:pointer, :int], :void
|
151
|
+
attach_function 'libusb_ref_device', [:pointer], :pointer
|
152
|
+
attach_function 'libusb_unref_device', [:pointer], :void
|
153
|
+
|
154
|
+
attach_function 'libusb_get_device_descriptor', [:pointer, :pointer], :int
|
155
|
+
attach_function 'libusb_get_active_config_descriptor', [:pointer, :pointer], :int
|
156
|
+
attach_function 'libusb_get_config_descriptor', [:pointer, :uint8, :pointer], :int
|
157
|
+
attach_function 'libusb_get_config_descriptor_by_value', [:pointer, :uint8, :pointer], :int
|
158
|
+
attach_function 'libusb_free_config_descriptor', [:pointer], :void
|
159
|
+
attach_function 'libusb_get_bus_number', [:pointer], :uint8
|
160
|
+
attach_function 'libusb_get_device_address', [:pointer], :uint8
|
161
|
+
attach_function 'libusb_get_max_packet_size', [:pointer, :uint8], :int
|
162
|
+
attach_function 'libusb_get_max_iso_packet_size', [:pointer, :uint8], :int
|
163
|
+
|
164
|
+
attach_function 'libusb_open', [:pointer, :pointer], :int
|
165
|
+
attach_function 'libusb_close', [:pointer], :void
|
166
|
+
attach_function 'libusb_get_device', [:libusb_device_handle], :pointer
|
167
|
+
|
168
|
+
attach_function 'libusb_set_configuration', [:libusb_device_handle, :int], :int
|
169
|
+
attach_function 'libusb_claim_interface', [:libusb_device_handle, :int], :int
|
170
|
+
attach_function 'libusb_release_interface', [:libusb_device_handle, :int], :int
|
171
|
+
|
172
|
+
attach_function 'libusb_open_device_with_vid_pid', [:pointer, :int, :int], :pointer
|
173
|
+
|
174
|
+
attach_function 'libusb_set_interface_alt_setting', [:libusb_device_handle, :int, :int], :int
|
175
|
+
attach_function 'libusb_clear_halt', [:libusb_device_handle, :int], :int
|
176
|
+
attach_function 'libusb_reset_device', [:libusb_device_handle], :int
|
177
|
+
|
178
|
+
attach_function 'libusb_kernel_driver_active', [:libusb_device_handle, :int], :int
|
179
|
+
attach_function 'libusb_detach_kernel_driver', [:libusb_device_handle, :int], :int
|
180
|
+
attach_function 'libusb_attach_kernel_driver', [:libusb_device_handle, :int], :int
|
181
|
+
|
182
|
+
attach_function 'libusb_get_string_descriptor_ascii', [:pointer, :uint8, :pointer, :int], :int
|
183
|
+
|
184
|
+
attach_function 'libusb_alloc_transfer', [:int], :pointer
|
185
|
+
attach_function 'libusb_submit_transfer', [:pointer], :int
|
186
|
+
attach_function 'libusb_cancel_transfer', [:pointer], :int
|
187
|
+
attach_function 'libusb_free_transfer', [:pointer], :void
|
188
|
+
|
189
|
+
attach_function 'libusb_handle_events', [:libusb_context], :int, :blocking=>true
|
190
|
+
|
191
|
+
|
192
|
+
callback :libusb_transfer_cb_fn, [:pointer], :void
|
193
|
+
|
194
|
+
class IsoPacketDescriptor < FFI::Struct
|
195
|
+
layout :length, :uint,
|
196
|
+
:actual_length, :uint,
|
197
|
+
:status, :libusb_transfer_status
|
198
|
+
end
|
199
|
+
|
200
|
+
# Setup packet for control transfers.
|
201
|
+
class ControlSetup < FFI::Struct
|
202
|
+
layout :bmRequestType, :uint8,
|
203
|
+
:bRequest, :uint8,
|
204
|
+
:wValue, :uint16,
|
205
|
+
:wIndex, :uint16,
|
206
|
+
:wLength, :uint16
|
207
|
+
end
|
208
|
+
|
209
|
+
class Transfer < FFI::ManagedStruct
|
210
|
+
layout :dev_handle, :libusb_device_handle,
|
211
|
+
:flags, :uint8,
|
212
|
+
:endpoint, :uchar,
|
213
|
+
:type, :uchar,
|
214
|
+
:timeout, :uint,
|
215
|
+
:status, :libusb_transfer_status,
|
216
|
+
:length, :int,
|
217
|
+
:actual_length, :int,
|
218
|
+
:callback, :libusb_transfer_cb_fn,
|
219
|
+
:user_data, :pointer,
|
220
|
+
:buffer, :pointer,
|
221
|
+
:num_iso_packets, :int
|
222
|
+
|
223
|
+
def self.release(ptr)
|
224
|
+
Call.libusb_free_transfer(ptr)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
Call::ClassCodes.to_h.each{|k,v| const_set(k,v) }
|
230
|
+
Call::TransferTypes.to_h.each{|k,v| const_set(k,v) }
|
231
|
+
Call::StandardRequests.to_h.each{|k,v| const_set(k,v) }
|
232
|
+
Call::RequestTypes.to_h.each{|k,v| const_set(k,v) }
|
233
|
+
Call::DescriptorTypes.to_h.each{|k,v| const_set(k,v) }
|
234
|
+
Call::EndpointDirections.to_h.each{|k,v| const_set(k,v) }
|
235
|
+
Call::RequestRecipients.to_h.each{|k,v| const_set(k,v) }
|
236
|
+
Call::IsoSyncTypes.to_h.each{|k,v| const_set(k,v) }
|
237
|
+
|
238
|
+
class Error < RuntimeError
|
239
|
+
end
|
240
|
+
ErrorClassForResult = {}
|
241
|
+
|
242
|
+
# define an exception class for each error code
|
243
|
+
Call::Errors.to_h.each do |k,v|
|
244
|
+
klass = Class.new(Error)
|
245
|
+
klass.send(:define_method, :code){ v }
|
246
|
+
const_set(k, klass)
|
247
|
+
ErrorClassForResult[v] = klass
|
248
|
+
end
|
249
|
+
|
250
|
+
def self.raise_error(res, text)
|
251
|
+
klass = ErrorClassForResult[res]
|
252
|
+
raise klass, "#{klass} #{text}"
|
253
|
+
end
|
254
|
+
|
255
|
+
CONTROL_SETUP_SIZE = 8
|
256
|
+
DT_DEVICE_SIZE = 18
|
257
|
+
DT_CONFIG_SIZE = 9
|
258
|
+
DT_INTERFACE_SIZE = 9
|
259
|
+
DT_ENDPOINT_SIZE = 7
|
260
|
+
DT_ENDPOINT_AUDIO_SIZE = 9 # Audio extension
|
261
|
+
DT_HUB_NONVAR_SIZE = 7
|
262
|
+
|
263
|
+
ENDPOINT_ADDRESS_MASK = 0x0f # in bEndpointAddress
|
264
|
+
ENDPOINT_DIR_MASK = 0x80
|
265
|
+
TRANSFER_TYPE_MASK = 0x03 # in bmAttributes
|
266
|
+
ISO_SYNC_TYPE_MASK = 0x0C
|
267
|
+
ISO_USAGE_TYPE_MASK = 0x30
|
268
|
+
|
269
|
+
|
270
|
+
# :stopdoc:
|
271
|
+
# http://www.usb.org/developers/defined_class
|
272
|
+
CLASS_CODES = [
|
273
|
+
[0x01, nil, nil, "Audio"],
|
274
|
+
[0x02, nil, nil, "Comm"],
|
275
|
+
[0x03, nil, nil, "HID"],
|
276
|
+
[0x05, nil, nil, "Physical"],
|
277
|
+
[0x06, 0x01, 0x01, "StillImaging"],
|
278
|
+
[0x06, nil, nil, "Image"],
|
279
|
+
[0x07, nil, nil, "Printer"],
|
280
|
+
[0x08, 0x01, nil, "MassStorage RBC Bluk-Only"],
|
281
|
+
[0x08, 0x02, 0x50, "MassStorage ATAPI Bluk-Only"],
|
282
|
+
[0x08, 0x03, 0x50, "MassStorage QIC-157 Bluk-Only"],
|
283
|
+
[0x08, 0x04, nil, "MassStorage UFI"],
|
284
|
+
[0x08, 0x05, 0x50, "MassStorage SFF-8070i Bluk-Only"],
|
285
|
+
[0x08, 0x06, 0x50, "MassStorage SCSI Bluk-Only"],
|
286
|
+
[0x08, nil, nil, "MassStorage"],
|
287
|
+
[0x09, 0x00, 0x00, "Full speed Hub"],
|
288
|
+
[0x09, 0x00, 0x01, "Hi-speed Hub with single TT"],
|
289
|
+
[0x09, 0x00, 0x02, "Hi-speed Hub with multiple TTs"],
|
290
|
+
[0x09, nil, nil, "Hub"],
|
291
|
+
[0x0a, nil, nil, "CDC"],
|
292
|
+
[0x0b, nil, nil, "SmartCard"],
|
293
|
+
[0x0d, 0x00, 0x00, "ContentSecurity"],
|
294
|
+
[0x0e, nil, nil, "Video"],
|
295
|
+
[0xdc, 0x01, 0x01, "Diagnostic USB2"],
|
296
|
+
[0xdc, nil, nil, "Diagnostic"],
|
297
|
+
[0xe0, 0x01, 0x01, "Bluetooth"],
|
298
|
+
[0xe0, 0x01, 0x02, "UWB"],
|
299
|
+
[0xe0, 0x01, 0x03, "RemoteNDIS"],
|
300
|
+
[0xe0, 0x02, 0x01, "Host Wire Adapter Control/Data"],
|
301
|
+
[0xe0, 0x02, 0x02, "Device Wire Adapter Control/Data"],
|
302
|
+
[0xe0, 0x02, 0x03, "Device Wire Adapter Isochronous"],
|
303
|
+
[0xe0, nil, nil, "Wireless Controller"],
|
304
|
+
[0xef, 0x01, 0x01, "Active Sync"],
|
305
|
+
[0xef, 0x01, 0x02, "Palm Sync"],
|
306
|
+
[0xef, 0x02, 0x01, "Interface Association Descriptor"],
|
307
|
+
[0xef, 0x02, 0x02, "Wire Adapter Multifunction Peripheral"],
|
308
|
+
[0xef, 0x03, 0x01, "Cable Based Association Framework"],
|
309
|
+
[0xef, nil, nil, "Miscellaneous"],
|
310
|
+
[0xfe, 0x01, 0x01, "Device Firmware Upgrade"],
|
311
|
+
[0xfe, 0x02, 0x00, "IRDA Bridge"],
|
312
|
+
[0xfe, 0x03, 0x00, "USB Test and Measurement"],
|
313
|
+
[0xfe, 0x03, 0x01, "USB Test and Measurement (USBTMC USB488)"],
|
314
|
+
[0xfe, nil, nil, "Application Specific"],
|
315
|
+
[0xff, nil, nil, "Vendor specific"],
|
316
|
+
]
|
317
|
+
CLASS_CODES_HASH1 = {}
|
318
|
+
CLASS_CODES_HASH2 = {}
|
319
|
+
CLASS_CODES_HASH3 = {}
|
320
|
+
CLASS_CODES.each {|base_class, sub_class, protocol, desc|
|
321
|
+
if protocol
|
322
|
+
CLASS_CODES_HASH3[[base_class, sub_class, protocol]] = desc
|
323
|
+
elsif sub_class
|
324
|
+
CLASS_CODES_HASH2[[base_class, sub_class]] = desc
|
325
|
+
else
|
326
|
+
CLASS_CODES_HASH1[base_class] = desc
|
327
|
+
end
|
328
|
+
}
|
329
|
+
|
330
|
+
def self.dev_string(base_class, sub_class, protocol)
|
331
|
+
if desc = CLASS_CODES_HASH3[[base_class, sub_class, protocol]]
|
332
|
+
desc
|
333
|
+
elsif desc = CLASS_CODES_HASH2[[base_class, sub_class]]
|
334
|
+
desc + " (%02x)" % [protocol]
|
335
|
+
elsif desc = CLASS_CODES_HASH1[base_class]
|
336
|
+
desc + " (%02x,%02x)" % [sub_class, protocol]
|
337
|
+
else
|
338
|
+
"Unkonwn(%02x,%02x,%02x)" % [base_class, sub_class, protocol]
|
339
|
+
end
|
340
|
+
end
|
341
|
+
# :startdoc:
|
342
|
+
|
343
|
+
|
344
|
+
# Abstract base class for USB transfers. Use
|
345
|
+
# {ControlTransfer}, {BulkTransfer}, {InterruptTransfer}, {IsochronousTransfer}
|
346
|
+
# to do transfers.
|
347
|
+
class Transfer
|
348
|
+
def initialize(args={})
|
349
|
+
args.each{|k,v| send("#{k}=", v) }
|
350
|
+
@buffer = nil
|
351
|
+
end
|
352
|
+
private :initialize
|
353
|
+
|
354
|
+
# Set the handle for the device to communicate with.
|
355
|
+
def dev_handle=(dev)
|
356
|
+
@dev_handle = dev
|
357
|
+
@transfer[:dev_handle] = @dev_handle.pHandle
|
358
|
+
end
|
359
|
+
|
360
|
+
# Timeout for this transfer in millseconds.
|
361
|
+
#
|
362
|
+
# A value of 0 indicates no timeout.
|
363
|
+
def timeout=(value)
|
364
|
+
@transfer[:timeout] = value
|
365
|
+
end
|
366
|
+
|
367
|
+
# Set the address of a valid endpoint to communicate with.
|
368
|
+
def endpoint=(endpoint)
|
369
|
+
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
370
|
+
@transfer[:endpoint] = endpoint
|
371
|
+
end
|
372
|
+
|
373
|
+
# Set output data that should be sent.
|
374
|
+
def buffer=(data)
|
375
|
+
if !@buffer || data.bytesize>@buffer.size
|
376
|
+
free_buffer
|
377
|
+
@buffer = FFI::MemoryPointer.new(data.bytesize, 1, false)
|
378
|
+
end
|
379
|
+
@buffer.put_bytes(0, data)
|
380
|
+
@transfer[:buffer] = @buffer
|
381
|
+
@transfer[:length] = data.bytesize
|
382
|
+
end
|
383
|
+
|
384
|
+
# Retrieve the current data buffer.
|
385
|
+
def buffer
|
386
|
+
@transfer[:buffer].read_string(@transfer[:length])
|
387
|
+
end
|
388
|
+
|
389
|
+
# Clear the current data buffer.
|
390
|
+
def free_buffer
|
391
|
+
if @buffer
|
392
|
+
@buffer.free
|
393
|
+
@buffer = nil
|
394
|
+
@transfer[:buffer] = nil
|
395
|
+
@transfer[:length] = 0
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
# Allocate +len+ bytes of data buffer for input transfer.
|
400
|
+
#
|
401
|
+
# @param [Fixnum] len Number of bytes to allocate
|
402
|
+
# @param [String, nil] data some data to initialize the buffer with
|
403
|
+
def alloc_buffer(len, data=nil)
|
404
|
+
if !@buffer || len>@buffer.size
|
405
|
+
free_buffer
|
406
|
+
@buffer = FFI::MemoryPointer.new(len, 1, false)
|
407
|
+
end
|
408
|
+
@buffer.put_bytes(0, data) if data
|
409
|
+
@transfer[:buffer] = @buffer
|
410
|
+
@transfer[:length] = len
|
411
|
+
end
|
412
|
+
|
413
|
+
# The number of bytes actually transferred.
|
414
|
+
def actual_length
|
415
|
+
@transfer[:actual_length]
|
416
|
+
end
|
417
|
+
|
418
|
+
# Retrieve the data actually transferred.
|
419
|
+
#
|
420
|
+
# @param [Fixnum] offset optional offset of the retrieved data in the buffer.
|
421
|
+
def actual_buffer(offset=0)
|
422
|
+
@transfer[:buffer].get_bytes(offset, @transfer[:actual_length])
|
423
|
+
end
|
424
|
+
|
425
|
+
# Set the block that will be invoked when the transfer completes,
|
426
|
+
# fails, or is cancelled.
|
427
|
+
#
|
428
|
+
# @param [Proc] proc The block that should be called
|
429
|
+
def callback=(proc)
|
430
|
+
# Save proc to instance variable so that GC doesn't free
|
431
|
+
# the proc object before the transfer.
|
432
|
+
@callback_proc = proc do |pTrans|
|
433
|
+
proc.call(self)
|
434
|
+
end
|
435
|
+
@transfer[:callback] = @callback_proc
|
436
|
+
end
|
437
|
+
|
438
|
+
# The status of the transfer.
|
439
|
+
#
|
440
|
+
# Only for use within transfer callback function or after the callback was called.
|
441
|
+
#
|
442
|
+
# If this is an isochronous transfer, this field may read :TRANSFER_COMPLETED even if there
|
443
|
+
# were errors in the frames. Use the status field in each packet to determine if
|
444
|
+
# errors occurred.
|
445
|
+
def status
|
446
|
+
@transfer[:status]
|
447
|
+
end
|
448
|
+
|
449
|
+
# Submit a transfer.
|
450
|
+
#
|
451
|
+
# This function will fire off the USB transfer and then return immediately.
|
452
|
+
# This method can be called with block. It is called when the transfer completes,
|
453
|
+
# fails, or is cancelled.
|
454
|
+
def submit!(&block)
|
455
|
+
self.callback = block if block_given?
|
456
|
+
|
457
|
+
# puts "submit transfer #{@transfer.inspect} buffer: #{@transfer[:buffer].inspect} length: #{@transfer[:length].inspect} status: #{@transfer[:status].inspect} callback: #{@transfer[:callback].inspect} dev_handle: #{@transfer[:dev_handle].inspect}"
|
458
|
+
|
459
|
+
res = Call.libusb_submit_transfer( @transfer )
|
460
|
+
LIBUSB.raise_error res, "in libusb_submit_transfer" if res!=0
|
461
|
+
end
|
462
|
+
|
463
|
+
# Asynchronously cancel a previously submitted transfer.
|
464
|
+
#
|
465
|
+
# This function returns immediately, but this does not indicate cancellation is
|
466
|
+
# complete. Your callback function will be invoked at some later time with a
|
467
|
+
# transfer status of :TRANSFER_CANCELLED.
|
468
|
+
def cancel!
|
469
|
+
res = Call.libusb_cancel_transfer( @transfer )
|
470
|
+
LIBUSB.raise_error res, "in libusb_cancel_transfer" if res!=0
|
471
|
+
end
|
472
|
+
|
473
|
+
TransferStatusToError = {
|
474
|
+
:TRANSFER_ERROR => LIBUSB::ERROR_IO,
|
475
|
+
:TRANSFER_TIMED_OUT => LIBUSB::ERROR_TIMEOUT,
|
476
|
+
:TRANSFER_CANCELLED => LIBUSB::ERROR_INTERRUPTED,
|
477
|
+
:TRANSFER_STALL => LIBUSB::ERROR_PIPE,
|
478
|
+
:TRANSFER_NO_DEVICE => LIBUSB::ERROR_NO_DEVICE,
|
479
|
+
:TRANSFER_OVERFLOW => LIBUSB::ERROR_OVERFLOW,
|
480
|
+
}
|
481
|
+
|
482
|
+
# Submit the transfer and wait until the transfer completes or fails.
|
483
|
+
#
|
484
|
+
# A proper {LIBUSB::Error} is raised, in case the transfer did not complete.
|
485
|
+
def submit_and_wait!
|
486
|
+
completed = false
|
487
|
+
submit! do |tr2|
|
488
|
+
completed = true
|
489
|
+
end
|
490
|
+
|
491
|
+
until completed
|
492
|
+
begin
|
493
|
+
@dev_handle.device.context.handle_events
|
494
|
+
rescue ERROR_INTERRUPTED
|
495
|
+
next
|
496
|
+
rescue LIBUSB::Error
|
497
|
+
cancel!
|
498
|
+
until completed
|
499
|
+
@dev_handle.device.context.handle_events
|
500
|
+
end
|
501
|
+
raise
|
502
|
+
end
|
503
|
+
end
|
504
|
+
|
505
|
+
raise( TransferStatusToError[status] || ERROR_OTHER, "error #{status}") unless status==:TRANSFER_COMPLETED
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
class BulkTransfer < Transfer
|
510
|
+
def initialize(args={})
|
511
|
+
@transfer = Call::Transfer.new Call.libusb_alloc_transfer(0)
|
512
|
+
@transfer[:type] = TRANSFER_TYPE_BULK
|
513
|
+
@transfer[:timeout] = 1000
|
514
|
+
super
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
class ControlTransfer < Transfer
|
519
|
+
def initialize(args={})
|
520
|
+
@transfer = Call::Transfer.new Call.libusb_alloc_transfer(0)
|
521
|
+
@transfer[:type] = TRANSFER_TYPE_CONTROL
|
522
|
+
@transfer[:timeout] = 1000
|
523
|
+
super
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
class InterruptTransfer < Transfer
|
528
|
+
def initialize(args={})
|
529
|
+
@transfer = Call::Transfer.new Call.libusb_alloc_transfer(0)
|
530
|
+
@transfer[:type] = TRANSFER_TYPE_INTERRUPT
|
531
|
+
@transfer[:timeout] = 1000
|
532
|
+
super
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
class IsoPacket
|
537
|
+
def initialize(ptr, pkg_nr)
|
538
|
+
@packet = Call::IsoPacketDescriptor.new ptr
|
539
|
+
@pkg_nr = pkg_nr
|
540
|
+
end
|
541
|
+
|
542
|
+
def status
|
543
|
+
@packet[:status]
|
544
|
+
end
|
545
|
+
|
546
|
+
def length
|
547
|
+
@packet[:length]
|
548
|
+
end
|
549
|
+
def length=(len)
|
550
|
+
@packet[:length] = len
|
551
|
+
end
|
552
|
+
|
553
|
+
def actual_length
|
554
|
+
@packet[:actual_length]
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
class IsochronousTransfer < Transfer
|
559
|
+
def initialize(num_packets, args={})
|
560
|
+
@ptr = Call.libusb_alloc_transfer(num_packets)
|
561
|
+
@transfer = Call::Transfer.new @ptr
|
562
|
+
@transfer[:type] = TRANSFER_TYPE_ISOCHRONOUS
|
563
|
+
@transfer[:timeout] = 1000
|
564
|
+
@transfer[:num_iso_packets] = num_packets
|
565
|
+
super(args)
|
566
|
+
end
|
567
|
+
|
568
|
+
def num_packets
|
569
|
+
@transfer[:num_iso_packets]
|
570
|
+
end
|
571
|
+
def num_packets=(number)
|
572
|
+
@transfer[:num_iso_packets] = number
|
573
|
+
end
|
574
|
+
|
575
|
+
def [](nr)
|
576
|
+
IsoPacket.new( @ptr + Call::Transfer.size + nr*Call::IsoPacketDescriptor.size, nr)
|
577
|
+
end
|
578
|
+
|
579
|
+
# Convenience function to set the length of all packets in an
|
580
|
+
# isochronous transfer, based on {IsochronousTransfer#num_packets}.
|
581
|
+
def packet_lengths=(len)
|
582
|
+
ptr = @ptr + Call::Transfer.size
|
583
|
+
num_packets.times do
|
584
|
+
ptr.write_uint(len)
|
585
|
+
ptr += Call::IsoPacketDescriptor.size
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
# The actual_length field of the transfer is meaningless and should not
|
590
|
+
# be examined; instead you must refer to the actual_length field of
|
591
|
+
# each individual packet.
|
592
|
+
private :actual_length, :actual_buffer
|
593
|
+
end
|
594
|
+
|
595
|
+
class DeviceDescriptor < FFI::Struct
|
596
|
+
include Comparable
|
597
|
+
|
598
|
+
layout :bLength, :uint8,
|
599
|
+
:bDescriptorType, :uint8,
|
600
|
+
:bcdUSB, :uint16,
|
601
|
+
:bDeviceClass, :uint8,
|
602
|
+
:bDeviceSubClass, :uint8,
|
603
|
+
:bDeviceProtocol, :uint8,
|
604
|
+
:bMaxPacketSize0, :uint8,
|
605
|
+
:idVendor, :uint16,
|
606
|
+
:idProduct, :uint16,
|
607
|
+
:bcdDevice, :uint16,
|
608
|
+
:iManufacturer, :uint8,
|
609
|
+
:iProduct, :uint8,
|
610
|
+
:iSerialNumber, :uint8,
|
611
|
+
:bNumConfigurations, :uint8
|
612
|
+
end
|
613
|
+
|
614
|
+
class Configuration < FFI::ManagedStruct
|
615
|
+
include Comparable
|
616
|
+
|
617
|
+
layout :bLength, :uint8,
|
618
|
+
:bDescriptorType, :uint8,
|
619
|
+
:wTotalLength, :uint16,
|
620
|
+
:bNumInterfaces, :uint8,
|
621
|
+
:bConfigurationValue, :uint8,
|
622
|
+
:iConfiguration, :uint8,
|
623
|
+
:bmAttributes, :uint8,
|
624
|
+
:maxPower, :uint8,
|
625
|
+
:interface, :pointer,
|
626
|
+
:extra, :pointer,
|
627
|
+
:extra_length, :int
|
628
|
+
|
629
|
+
members.each do |member|
|
630
|
+
define_method(member) do
|
631
|
+
self[member]
|
632
|
+
end
|
633
|
+
end
|
634
|
+
|
635
|
+
def initialize(device, *args)
|
636
|
+
@device = device
|
637
|
+
super(*args)
|
638
|
+
end
|
639
|
+
|
640
|
+
def self.release(ptr)
|
641
|
+
Call.libusb_free_config_descriptor(ptr)
|
642
|
+
end
|
643
|
+
|
644
|
+
# @return [Device] the device this configuration belongs to.
|
645
|
+
attr_reader :device
|
646
|
+
|
647
|
+
def interfaces
|
648
|
+
ifs = []
|
649
|
+
self[:bNumInterfaces].times do |i|
|
650
|
+
ifs << Interface.new(self, self[:interface] + i*Interface.size)
|
651
|
+
end
|
652
|
+
return ifs
|
653
|
+
end
|
654
|
+
|
655
|
+
def inspect
|
656
|
+
attrs = []
|
657
|
+
attrs << self.bConfigurationValue.to_s
|
658
|
+
bits = self.bmAttributes
|
659
|
+
attrs << "SelfPowered" if (bits & 0b1000000) != 0
|
660
|
+
attrs << "RemoteWakeup" if (bits & 0b100000) != 0
|
661
|
+
desc = self.description
|
662
|
+
attrs << desc if desc != '?'
|
663
|
+
"\#<#{self.class} #{attrs.join(' ')}>"
|
664
|
+
end
|
665
|
+
|
666
|
+
# Return name of the configuration as String.
|
667
|
+
def description
|
668
|
+
return @description if defined? @description
|
669
|
+
@description = device.try_string_descriptor_ascii(self.iConfiguration)
|
670
|
+
end
|
671
|
+
|
672
|
+
# Return all interface decriptions of the configuration as Array of InterfaceDescriptor s.
|
673
|
+
def settings() self.interfaces.map {|d| d.settings }.flatten end
|
674
|
+
# Return all endpoints of all interfaces of the configuration as Array of EndpointDescriptor s.
|
675
|
+
def endpoints() self.settings.map {|d| d.endpoints }.flatten end
|
676
|
+
|
677
|
+
def <=>(o)
|
678
|
+
t = device<=>o.device
|
679
|
+
t = bConfigurationValue<=>o.bConfigurationValue if t==0
|
680
|
+
t
|
681
|
+
end
|
682
|
+
end
|
683
|
+
|
684
|
+
class Interface < FFI::Struct
|
685
|
+
include Comparable
|
686
|
+
|
687
|
+
layout :altsetting, :pointer,
|
688
|
+
:num_altsetting, :int
|
689
|
+
|
690
|
+
def initialize(configuration, *args)
|
691
|
+
@configuration = configuration
|
692
|
+
super(*args)
|
693
|
+
end
|
694
|
+
|
695
|
+
# @return [Configuration] the configuration this interface belongs to.
|
696
|
+
attr_reader :configuration
|
697
|
+
|
698
|
+
def alt_settings
|
699
|
+
ifs = []
|
700
|
+
self[:num_altsetting].times do |i|
|
701
|
+
ifs << Setting.new(self, self[:altsetting] + i*Setting.size)
|
702
|
+
end
|
703
|
+
return ifs
|
704
|
+
end
|
705
|
+
alias settings alt_settings
|
706
|
+
|
707
|
+
# The Device the Interface belongs to.
|
708
|
+
def device() self.configuration.device end
|
709
|
+
# Return all endpoints of all alternative settings as Array of EndpointDescriptor s.
|
710
|
+
def endpoints() self.alt_settings.map {|d| d.endpoints }.flatten end
|
711
|
+
|
712
|
+
def <=>(o)
|
713
|
+
configuration<=>o.configuration
|
714
|
+
end
|
715
|
+
end
|
716
|
+
|
717
|
+
class Setting < FFI::Struct
|
718
|
+
include Comparable
|
719
|
+
|
720
|
+
layout :bLength, :uint8,
|
721
|
+
:bDescriptorType, :uint8,
|
722
|
+
:bInterfaceNumber, :uint8,
|
723
|
+
:bAlternateSetting, :uint8,
|
724
|
+
:bNumEndpoints, :uint8,
|
725
|
+
:bInterfaceClass, :uint8,
|
726
|
+
:bInterfaceSubClass, :uint8,
|
727
|
+
:bInterfaceProtocol, :uint8,
|
728
|
+
:iInterface, :uint8,
|
729
|
+
:endpoint, :pointer,
|
730
|
+
:extra, :pointer,
|
731
|
+
:extra_length, :int
|
732
|
+
|
733
|
+
members.each do |member|
|
734
|
+
define_method(member) do
|
735
|
+
self[member]
|
736
|
+
end
|
737
|
+
end
|
738
|
+
|
739
|
+
def initialize(interface, *args)
|
740
|
+
@interface = interface
|
741
|
+
super(*args)
|
742
|
+
end
|
743
|
+
|
744
|
+
# @return [Interface] the interface this setting belongs to.
|
745
|
+
attr_reader :interface
|
746
|
+
|
747
|
+
def endpoints
|
748
|
+
ifs = []
|
749
|
+
self[:bNumEndpoints].times do |i|
|
750
|
+
ifs << Endpoint.new(self, self[:endpoint] + i*Endpoint.size)
|
751
|
+
end
|
752
|
+
return ifs
|
753
|
+
end
|
754
|
+
|
755
|
+
def inspect
|
756
|
+
attrs = []
|
757
|
+
attrs << self.bAlternateSetting.to_s
|
758
|
+
devclass = LIBUSB.dev_string(self.bInterfaceClass, self.bInterfaceSubClass, self.bInterfaceProtocol)
|
759
|
+
attrs << devclass
|
760
|
+
desc = self.description
|
761
|
+
attrs << desc if desc != '?'
|
762
|
+
"\#<#{self.class} #{attrs.join(' ')}>"
|
763
|
+
end
|
764
|
+
|
765
|
+
# Return name of the Interface as String.
|
766
|
+
def description
|
767
|
+
return @description if defined? @description
|
768
|
+
@description = device.try_string_descriptor_ascii(self.iInterface)
|
769
|
+
end
|
770
|
+
|
771
|
+
# The Device the InterfaceDescriptor belongs to.
|
772
|
+
def device() self.interface.configuration.device end
|
773
|
+
# The ConfigDescriptor the InterfaceDescriptor belongs to.
|
774
|
+
def configuration() self.interface.configuration end
|
775
|
+
|
776
|
+
def <=>(o)
|
777
|
+
t = interface<=>o.interface
|
778
|
+
t = bInterfaceNumber<=>o.bInterfaceNumber if t==0
|
779
|
+
t = bAlternateSetting<=>o.bAlternateSetting if t==0
|
780
|
+
t
|
781
|
+
end
|
782
|
+
end
|
783
|
+
|
784
|
+
class Endpoint < FFI::Struct
|
785
|
+
include Comparable
|
786
|
+
|
787
|
+
layout :bLength, :uint8,
|
788
|
+
:bDescriptorType, :uint8,
|
789
|
+
:bEndpointAddress, :uint8,
|
790
|
+
:bmAttributes, :uint8,
|
791
|
+
:wMaxPacketSize, :uint16,
|
792
|
+
:bInterval, :uint8,
|
793
|
+
:bRefresh, :uint8,
|
794
|
+
:bSynchAddress, :uint8,
|
795
|
+
:extra, :pointer,
|
796
|
+
:extra_length, :int
|
797
|
+
|
798
|
+
members.each do |member|
|
799
|
+
define_method(member) do
|
800
|
+
self[member]
|
801
|
+
end
|
802
|
+
end
|
803
|
+
|
804
|
+
def initialize(setting, *args)
|
805
|
+
@setting = setting
|
806
|
+
super(*args)
|
807
|
+
end
|
808
|
+
|
809
|
+
# @return [Setting] the setting this endpoint belongs to.
|
810
|
+
attr_reader :setting
|
811
|
+
|
812
|
+
def inspect
|
813
|
+
endpoint_address = self.bEndpointAddress
|
814
|
+
num = endpoint_address & 0b00001111
|
815
|
+
inout = (endpoint_address & 0b10000000) == 0 ? "OUT" : "IN "
|
816
|
+
bits = self.bmAttributes
|
817
|
+
transfer_type = %w[Control Isochronous Bulk Interrupt][0b11 & bits]
|
818
|
+
type = [transfer_type]
|
819
|
+
if transfer_type == 'Isochronous'
|
820
|
+
synchronization_type = %w[NoSynchronization Asynchronous Adaptive Synchronous][(0b1100 & bits) >> 2]
|
821
|
+
usage_type = %w[Data Feedback ImplicitFeedback ?][(0b110000 & bits) >> 4]
|
822
|
+
type << synchronization_type << usage_type
|
823
|
+
end
|
824
|
+
"\#<#{self.class} #{num} #{inout} #{type.join(" ")}>"
|
825
|
+
end
|
826
|
+
|
827
|
+
# The Device the EndpointDescriptor belongs to.
|
828
|
+
def device() self.setting.interface.configuration.device end
|
829
|
+
# The ConfigDescriptor the EndpointDescriptor belongs to.
|
830
|
+
def configuration() self.setting.interface.configuration end
|
831
|
+
# The Interface the EndpointDescriptor belongs to.
|
832
|
+
def interface() self.setting.interface end
|
833
|
+
|
834
|
+
def <=>(o)
|
835
|
+
t = setting<=>o.setting
|
836
|
+
t = bEndpointAddress<=>o.bEndpointAddress if t==0
|
837
|
+
t
|
838
|
+
end
|
839
|
+
end
|
840
|
+
|
841
|
+
|
842
|
+
# Class representing a libusb session.
|
843
|
+
class Context
|
844
|
+
# Initialize libusb context.
|
845
|
+
def initialize
|
846
|
+
m = FFI::MemoryPointer.new :pointer
|
847
|
+
Call.libusb_init(m)
|
848
|
+
@ctx = m.read_pointer
|
849
|
+
end
|
850
|
+
|
851
|
+
# Deinitialize libusb.
|
852
|
+
#
|
853
|
+
# Should be called after closing all open devices and before your application terminates.
|
854
|
+
def exit
|
855
|
+
Call.libusb_exit(@ctx)
|
856
|
+
end
|
857
|
+
|
858
|
+
# Set message verbosity.
|
859
|
+
#
|
860
|
+
# * Level 0: no messages ever printed by the library (default)
|
861
|
+
# * Level 1: error messages are printed to stderr
|
862
|
+
# * Level 2: warning and error messages are printed to stderr
|
863
|
+
# * Level 3: informational messages are printed to stdout, warning and
|
864
|
+
# error messages are printed to stderr
|
865
|
+
#
|
866
|
+
# The default level is 0, which means no messages are ever printed. If you
|
867
|
+
# choose to increase the message verbosity level, ensure that your
|
868
|
+
# application does not close the stdout/stderr file descriptors.
|
869
|
+
#
|
870
|
+
# You are advised to set level 3. libusb is conservative with its message
|
871
|
+
# logging and most of the time, will only log messages that explain error
|
872
|
+
# conditions and other oddities. This will help you debug your software.
|
873
|
+
#
|
874
|
+
# If the LIBUSB_DEBUG environment variable was set when libusb was
|
875
|
+
# initialized, this method does nothing: the message verbosity is
|
876
|
+
# fixed to the value in the environment variable.
|
877
|
+
#
|
878
|
+
# If libusb was compiled without any message logging, this method
|
879
|
+
# does nothing: you'll never get any messages.
|
880
|
+
#
|
881
|
+
# If libusb was compiled with verbose debug message logging, this
|
882
|
+
# method does nothing: you'll always get messages from all levels.
|
883
|
+
#
|
884
|
+
# @param [Fixnum] level debug level to set
|
885
|
+
def debug=(level)
|
886
|
+
Call.libusb_set_debug(@ctx, level)
|
887
|
+
end
|
888
|
+
|
889
|
+
def device_list
|
890
|
+
pppDevs = FFI::MemoryPointer.new :pointer
|
891
|
+
size = Call.libusb_get_device_list(@ctx, pppDevs)
|
892
|
+
ppDevs = pppDevs.read_pointer
|
893
|
+
pDevs = []
|
894
|
+
size.times do |devi|
|
895
|
+
pDev = ppDevs.get_pointer(devi*FFI.type_size(:pointer))
|
896
|
+
pDevs << Device.new(self, pDev)
|
897
|
+
end
|
898
|
+
Call.libusb_free_device_list(ppDevs, 1)
|
899
|
+
pDevs
|
900
|
+
end
|
901
|
+
private :device_list
|
902
|
+
|
903
|
+
# Handle any pending events in blocking mode.
|
904
|
+
#
|
905
|
+
# This method must be called when libusb is running asynchronous transfers.
|
906
|
+
# This gives libusb the opportunity to reap pending transfers,
|
907
|
+
# invoke callbacks, etc.
|
908
|
+
def handle_events
|
909
|
+
res = Call.libusb_handle_events(@ctx)
|
910
|
+
LIBUSB.raise_error res, "in libusb_handle_events" if res<0
|
911
|
+
end
|
912
|
+
|
913
|
+
# Obtain a list of devices currently attached to the USB system, optionally matching certain criteria.
|
914
|
+
#
|
915
|
+
# @param [Hash] filter_hash A number of criteria can be defined in key-value pairs.
|
916
|
+
# Only devices that equal all given criterions will be returned. If a criterion is
|
917
|
+
# not specified or its value is +nil+, any device will match that criterion.
|
918
|
+
# The following criteria can be filtered:
|
919
|
+
# * <tt>:idVendor</tt>, <tt>:idProduct</tt> (+FixNum+) for matching vendor/product ID,
|
920
|
+
# * <tt>:bClass</tt>, <tt>:bSubClass</tt>, <tt>:bProtocol</tt> (+FixNum+) for the device type -
|
921
|
+
# Devices using CLASS_PER_INTERFACE will match, if any of the interfaces match.
|
922
|
+
# * <tt>:bcdUSB</tt>, <tt>:bcdDevice</tt>, <tt>:bMaxPacketSize0</tt> (+FixNum+) for the
|
923
|
+
# USB and device release numbers.
|
924
|
+
# Criteria can also specified as Array of several alternative values.
|
925
|
+
#
|
926
|
+
# @example
|
927
|
+
# # Return all devices of vendor 0x0ab1 where idProduct is 3 or 4:
|
928
|
+
# context.device :idVendor=>0x0ab1, :idProduct=>[0x0003, 0x0004]
|
929
|
+
#
|
930
|
+
# @return [Array<LIBUSB::Device>]
|
931
|
+
def devices(filter_hash={})
|
932
|
+
device_list.select do |dev|
|
933
|
+
( !filter_hash[:bClass] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
|
934
|
+
dev.settings.map(&:bInterfaceClass).&([filter_hash[:bClass]].flatten).any? :
|
935
|
+
[filter_hash[:bClass]].flatten.include?(dev.bDeviceClass))) &&
|
936
|
+
( !filter_hash[:bSubClass] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
|
937
|
+
dev.settings.map(&:bInterfaceSubClass).&([filter_hash[:bSubClass]].flatten).any? :
|
938
|
+
[filter_hash[:bSubClass]].flatten.include?(dev.bDeviceSubClass))) &&
|
939
|
+
( !filter_hash[:bProtocol] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
|
940
|
+
dev.settings.map(&:bInterfaceProtocol).&([filter_hash[:bProtocol]].flatten).any? :
|
941
|
+
[filter_hash[:bProtocol]].flatten.include?(dev.bDeviceProtocol))) &&
|
942
|
+
( !filter_hash[:bMaxPacketSize0] || [filter_hash[:bMaxPacketSize0]].flatten.include?(dev.bMaxPacketSize0) ) &&
|
943
|
+
( !filter_hash[:idVendor] || [filter_hash[:idVendor]].flatten.include?(dev.idVendor) ) &&
|
944
|
+
( !filter_hash[:idProduct] || [filter_hash[:idProduct]].flatten.include?(dev.idProduct) ) &&
|
945
|
+
( !filter_hash[:bcdUSB] || [filter_hash[:bcdUSB]].flatten.include?(dev.bcdUSB) ) &&
|
946
|
+
( !filter_hash[:bcdDevice] || [filter_hash[:bcdDevice]].flatten.include?(dev.bcdDevice) )
|
947
|
+
end
|
948
|
+
end
|
949
|
+
end
|
950
|
+
|
951
|
+
# Class representing a USB device detected on the system.
|
952
|
+
#
|
953
|
+
# Devices of the system can be obtained with {Context#devices} .
|
954
|
+
class Device
|
955
|
+
include Comparable
|
956
|
+
|
957
|
+
# @return [Context] the context this device belongs to.
|
958
|
+
attr_reader :context
|
959
|
+
|
960
|
+
def initialize context, pDev
|
961
|
+
@context = context
|
962
|
+
class << pDev
|
963
|
+
def unref_device(id)
|
964
|
+
Call.libusb_unref_device(self)
|
965
|
+
end
|
966
|
+
end
|
967
|
+
ObjectSpace.define_finalizer(self, pDev.method(:unref_device))
|
968
|
+
Call.libusb_ref_device(pDev)
|
969
|
+
@pDev = pDev
|
970
|
+
|
971
|
+
@pDevDesc = DeviceDescriptor.new
|
972
|
+
res = Call.libusb_get_device_descriptor(@pDev, @pDevDesc)
|
973
|
+
LIBUSB.raise_error res, "in libusb_get_device_descriptor" if res!=0
|
974
|
+
end
|
975
|
+
|
976
|
+
# Open a device and obtain a device handle.
|
977
|
+
#
|
978
|
+
# A handle allows you to perform I/O on the device in question.
|
979
|
+
# This is a non-blocking function; no requests are sent over the bus.
|
980
|
+
#
|
981
|
+
# If called with a block, the handle is passed to the block
|
982
|
+
# and is closed when the block has finished.
|
983
|
+
#
|
984
|
+
# You need proper access permissions on:
|
985
|
+
# * Linux: <tt>/dev/bus/usb/<bus>/<dev></tt>
|
986
|
+
#
|
987
|
+
# @return [DevHandle] Handle to the device.
|
988
|
+
def open
|
989
|
+
ppHandle = FFI::MemoryPointer.new :pointer
|
990
|
+
res = Call.libusb_open(@pDev, ppHandle)
|
991
|
+
LIBUSB.raise_error res, "in libusb_open" if res!=0
|
992
|
+
handle = DevHandle.new self, ppHandle.read_pointer
|
993
|
+
return handle unless block_given?
|
994
|
+
begin
|
995
|
+
yield handle
|
996
|
+
ensure
|
997
|
+
handle.close
|
998
|
+
end
|
999
|
+
end
|
1000
|
+
|
1001
|
+
# Get the number of the bus that a device is connected to.
|
1002
|
+
def bus_number
|
1003
|
+
Call.libusb_get_bus_number(@pDev)
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
# Get the address of the device on the bus it is connected to.
|
1007
|
+
def device_address
|
1008
|
+
Call.libusb_get_device_address(@pDev)
|
1009
|
+
end
|
1010
|
+
|
1011
|
+
# Convenience function to retrieve the wMaxPacketSize value for a
|
1012
|
+
# particular endpoint in the active device configuration.
|
1013
|
+
#
|
1014
|
+
# @param [Endpoint, Fixnum] endpoint (address of) the endpoint in question
|
1015
|
+
# @return [Fixnum] the wMaxPacketSize value
|
1016
|
+
def max_packet_size(endpoint)
|
1017
|
+
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
1018
|
+
res = Call.libusb_get_max_packet_size(@pDev, endpoint)
|
1019
|
+
LIBUSB.raise_error res, "in libusb_get_max_packet_size" unless res>=0
|
1020
|
+
res
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
# Calculate the maximum packet size which a specific endpoint is capable is
|
1024
|
+
# sending or receiving in the duration of 1 microframe.
|
1025
|
+
#
|
1026
|
+
# Only the active configution is examined. The calculation is based on the
|
1027
|
+
# wMaxPacketSize field in the endpoint descriptor as described in section 9.6.6
|
1028
|
+
# in the USB 2.0 specifications.
|
1029
|
+
#
|
1030
|
+
# If acting on an isochronous or interrupt endpoint, this function will
|
1031
|
+
# multiply the value found in bits 0:10 by the number of transactions per
|
1032
|
+
# microframe (determined by bits 11:12). Otherwise, this function just returns
|
1033
|
+
# the numeric value found in bits 0:10.
|
1034
|
+
#
|
1035
|
+
# This function is useful for setting up isochronous transfers, for example
|
1036
|
+
# you might use the return value from this function to call
|
1037
|
+
# IsoPacket#alloc_buffer in order to set the length field
|
1038
|
+
# of an isochronous packet in a transfer.
|
1039
|
+
#
|
1040
|
+
# @param [Endpoint, Fixnum] endpoint (address of) the endpoint in question
|
1041
|
+
# @return [Fixnum] the maximum packet size which can be sent/received on this endpoint
|
1042
|
+
def max_iso_packet_size(endpoint)
|
1043
|
+
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
1044
|
+
res = Call.libusb_get_max_iso_packet_size(@pDev, endpoint)
|
1045
|
+
LIBUSB.raise_error res, "in libusb_get_max_iso_packet_size" unless res>=0
|
1046
|
+
res
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
# Obtain a config descriptor of the device.
|
1050
|
+
#
|
1051
|
+
# @param [Fixnum] index number of the config descriptor
|
1052
|
+
# @return Configuration
|
1053
|
+
def config_descriptor(index)
|
1054
|
+
ppConfig = FFI::MemoryPointer.new :pointer
|
1055
|
+
res = Call.libusb_get_config_descriptor(@pDev, index, ppConfig)
|
1056
|
+
LIBUSB.raise_error res, "in libusb_get_config_descriptor" if res!=0
|
1057
|
+
pConfig = ppConfig.read_pointer
|
1058
|
+
config = Configuration.new(self, pConfig)
|
1059
|
+
config
|
1060
|
+
end
|
1061
|
+
|
1062
|
+
# allow access to Descriptor members on Device
|
1063
|
+
DeviceDescriptor.members.each do |member|
|
1064
|
+
define_method(member) do
|
1065
|
+
@pDevDesc[member]
|
1066
|
+
end
|
1067
|
+
end
|
1068
|
+
|
1069
|
+
def inspect
|
1070
|
+
attrs = []
|
1071
|
+
attrs << "#{self.bus_number}/#{self.device_address}"
|
1072
|
+
attrs << ("%04x:%04x" % [self.idVendor, self.idProduct])
|
1073
|
+
attrs << self.manufacturer
|
1074
|
+
attrs << self.product
|
1075
|
+
attrs << self.serial_number
|
1076
|
+
if self.bDeviceClass == LIBUSB::CLASS_PER_INTERFACE
|
1077
|
+
devclass = self.settings.map {|i|
|
1078
|
+
LIBUSB.dev_string(i.bInterfaceClass, i.bInterfaceSubClass, i.bInterfaceProtocol)
|
1079
|
+
}.join(", ")
|
1080
|
+
else
|
1081
|
+
devclass = LIBUSB.dev_string(self.bDeviceClass, self.bDeviceSubClass, self.bDeviceProtocol)
|
1082
|
+
end
|
1083
|
+
attrs << "(#{devclass})"
|
1084
|
+
attrs.compact!
|
1085
|
+
"\#<#{self.class} #{attrs.join(' ')}>"
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
def try_string_descriptor_ascii(i)
|
1089
|
+
begin
|
1090
|
+
open{|h| h.string_descriptor_ascii(i) }
|
1091
|
+
rescue
|
1092
|
+
"?"
|
1093
|
+
end
|
1094
|
+
end
|
1095
|
+
|
1096
|
+
# Return manufacturer of the device
|
1097
|
+
# @return String
|
1098
|
+
def manufacturer
|
1099
|
+
return @manufacturer if defined? @manufacturer
|
1100
|
+
@manufacturer = try_string_descriptor_ascii(self.iManufacturer)
|
1101
|
+
@manufacturer.strip! if @manufacturer
|
1102
|
+
@manufacturer
|
1103
|
+
end
|
1104
|
+
|
1105
|
+
# Return product name of the device.
|
1106
|
+
# @return String
|
1107
|
+
def product
|
1108
|
+
return @product if defined? @product
|
1109
|
+
@product = try_string_descriptor_ascii(self.iProduct)
|
1110
|
+
@product.strip! if @product
|
1111
|
+
@product
|
1112
|
+
end
|
1113
|
+
|
1114
|
+
# Return serial number of the device.
|
1115
|
+
# @return String
|
1116
|
+
def serial_number
|
1117
|
+
return @serial_number if defined? @serial_number
|
1118
|
+
@serial_number = try_string_descriptor_ascii(self.iSerialNumber)
|
1119
|
+
@serial_number.strip! if @serial_number
|
1120
|
+
@serial_number
|
1121
|
+
end
|
1122
|
+
|
1123
|
+
# Return configurations of the device.
|
1124
|
+
# @return [Array<Configuration>]
|
1125
|
+
def configurations
|
1126
|
+
configs = []
|
1127
|
+
bNumConfigurations.times do |config_index|
|
1128
|
+
begin
|
1129
|
+
configs << config_descriptor(config_index)
|
1130
|
+
rescue RuntimeError
|
1131
|
+
# On Windows some devices don't return it's configuration.
|
1132
|
+
end
|
1133
|
+
end
|
1134
|
+
configs
|
1135
|
+
end
|
1136
|
+
|
1137
|
+
# Return all interfaces of the device.
|
1138
|
+
# @return [Array<Interface>]
|
1139
|
+
def interfaces() self.configurations.map {|d| d.interfaces }.flatten end
|
1140
|
+
# Return all interface decriptions of the device.
|
1141
|
+
# @return [Array<Setting>]
|
1142
|
+
def settings() self.interfaces.map {|d| d.settings }.flatten end
|
1143
|
+
# Return all endpoints of all interfaces of the device.
|
1144
|
+
# @return [Array<Endpoint>]
|
1145
|
+
def endpoints() self.settings.map {|d| d.endpoints }.flatten end
|
1146
|
+
|
1147
|
+
def <=>(o)
|
1148
|
+
t = bus_number<=>o.bus_number
|
1149
|
+
t = device_address<=>o.device_address if t==0
|
1150
|
+
t
|
1151
|
+
end
|
1152
|
+
end
|
1153
|
+
|
1154
|
+
# Class representing a handle on a USB device.
|
1155
|
+
#
|
1156
|
+
# A device handle is used to perform I/O and other operations. When finished
|
1157
|
+
# with a device handle, you should call DevHandle#close .
|
1158
|
+
class DevHandle
|
1159
|
+
# @private
|
1160
|
+
attr_reader :pHandle
|
1161
|
+
# @return [Device] the device this handle belongs to.
|
1162
|
+
attr_reader :device
|
1163
|
+
|
1164
|
+
def initialize device, pHandle
|
1165
|
+
@device = device
|
1166
|
+
@pHandle = pHandle
|
1167
|
+
@bulk_transfer = @control_transfer = @interrupt_transfer = nil
|
1168
|
+
end
|
1169
|
+
|
1170
|
+
# Close a device handle.
|
1171
|
+
#
|
1172
|
+
# Should be called on all open handles before your application exits.
|
1173
|
+
#
|
1174
|
+
# Internally, this function destroys the reference that was added by {Device#open}
|
1175
|
+
# on the given device.
|
1176
|
+
#
|
1177
|
+
# This is a non-blocking function; no requests are sent over the bus.
|
1178
|
+
def close
|
1179
|
+
Call.libusb_close(@pHandle)
|
1180
|
+
end
|
1181
|
+
|
1182
|
+
def string_descriptor_ascii(index)
|
1183
|
+
pString = FFI::MemoryPointer.new 0x100
|
1184
|
+
res = Call.libusb_get_string_descriptor_ascii(@pHandle, index, pString, pString.size)
|
1185
|
+
LIBUSB.raise_error res, "in libusb_get_string_descriptor_ascii" unless res>=0
|
1186
|
+
pString.read_string(res)
|
1187
|
+
end
|
1188
|
+
|
1189
|
+
# Claim an interface on a given device handle.
|
1190
|
+
#
|
1191
|
+
# You must claim the interface you wish to use before you can perform I/O on any
|
1192
|
+
# of its endpoints.
|
1193
|
+
#
|
1194
|
+
# It is legal to attempt to claim an already-claimed interface, in which case
|
1195
|
+
# libusb just returns without doing anything.
|
1196
|
+
#
|
1197
|
+
# Claiming of interfaces is a purely logical operation; it does not cause any
|
1198
|
+
# requests to be sent over the bus. Interface claiming is used to instruct the
|
1199
|
+
# underlying operating system that your application wishes to take ownership of
|
1200
|
+
# the interface.
|
1201
|
+
#
|
1202
|
+
# This is a non-blocking function.
|
1203
|
+
#
|
1204
|
+
# @param [Interface, Fixnum] interface the interface or it's bInterfaceNumber you wish to claim
|
1205
|
+
def claim_interface(interface)
|
1206
|
+
interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
|
1207
|
+
res = Call.libusb_claim_interface(@pHandle, interface)
|
1208
|
+
LIBUSB.raise_error res, "in libusb_claim_interface" if res!=0
|
1209
|
+
end
|
1210
|
+
|
1211
|
+
# Release an interface previously claimed with {DevHandle#claim_interface}.
|
1212
|
+
#
|
1213
|
+
# You should release all claimed interfaces before closing a device handle.
|
1214
|
+
#
|
1215
|
+
# This is a blocking function. A SET_INTERFACE control request will be sent to the
|
1216
|
+
# device, resetting interface state to the first alternate setting.
|
1217
|
+
#
|
1218
|
+
# @param [Interface, Fixnum] interface the interface or it's bInterfaceNumber you
|
1219
|
+
# claimed previously
|
1220
|
+
def release_interface(interface)
|
1221
|
+
interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
|
1222
|
+
res = Call.libusb_release_interface(@pHandle, interface)
|
1223
|
+
LIBUSB.raise_error res, "in libusb_release_interface" if res!=0
|
1224
|
+
end
|
1225
|
+
|
1226
|
+
# Set the active configuration for a device.
|
1227
|
+
#
|
1228
|
+
# The operating system may or may not have already set an active configuration on
|
1229
|
+
# the device. It is up to your application to ensure the correct configuration is
|
1230
|
+
# selected before you attempt to claim interfaces and perform other operations.
|
1231
|
+
#
|
1232
|
+
# If you call this function on a device already configured with the selected
|
1233
|
+
# configuration, then this function will act as a lightweight device reset: it
|
1234
|
+
# will issue a SET_CONFIGURATION request using the current configuration, causing
|
1235
|
+
# most USB-related device state to be reset (altsetting reset to zero, endpoint
|
1236
|
+
# halts cleared, toggles reset).
|
1237
|
+
#
|
1238
|
+
# You cannot change/reset configuration if your application has claimed interfaces -
|
1239
|
+
# you should free them with {DevHandle#release_interface} first. You cannot
|
1240
|
+
# change/reset configuration if other applications or drivers have claimed
|
1241
|
+
# interfaces.
|
1242
|
+
#
|
1243
|
+
# A configuration value of +nil+ will put the device in unconfigured state. The USB
|
1244
|
+
# specifications state that a configuration value of 0 does this, however buggy
|
1245
|
+
# devices exist which actually have a configuration 0.
|
1246
|
+
#
|
1247
|
+
# You should always use this function rather than formulating your own
|
1248
|
+
# SET_CONFIGURATION control request. This is because the underlying operating
|
1249
|
+
# system needs to know when such changes happen.
|
1250
|
+
#
|
1251
|
+
# This is a blocking function.
|
1252
|
+
#
|
1253
|
+
# @param [Configuration, Fixnum] configuration the configuration or it's
|
1254
|
+
# bConfigurationValue you wish to activate, or +nil+ if you wish to put
|
1255
|
+
# the device in unconfigured state
|
1256
|
+
def set_configuration(configuration)
|
1257
|
+
configuration = configuration.bConfigurationValue if configuration.respond_to? :bConfigurationValue
|
1258
|
+
res = Call.libusb_set_configuration(@pHandle, configuration || -1)
|
1259
|
+
LIBUSB.raise_error res, "in libusb_set_configuration" if res!=0
|
1260
|
+
end
|
1261
|
+
alias configuration= set_configuration
|
1262
|
+
|
1263
|
+
# Activate an alternate setting for an interface.
|
1264
|
+
#
|
1265
|
+
# The interface must have been previously claimed with {DevHandle#claim_interface}.
|
1266
|
+
#
|
1267
|
+
# You should always use this function rather than formulating your own
|
1268
|
+
# SET_INTERFACE control request. This is because the underlying operating system
|
1269
|
+
# needs to know when such changes happen.
|
1270
|
+
#
|
1271
|
+
# This is a blocking function.
|
1272
|
+
#
|
1273
|
+
# @param [Setting, Fixnum] setting_or_interface_number the alternate setting
|
1274
|
+
# to activate or the bInterfaceNumber of the previously-claimed interface
|
1275
|
+
# @param [Fixnum, nil] alternate_setting the bAlternateSetting of the alternate setting to activate
|
1276
|
+
# (only if first param is a Fixnum)
|
1277
|
+
def set_interface_alt_setting(setting_or_interface_number,
|
1278
|
+
alternate_setting=nil)
|
1279
|
+
alternate_setting ||= setting_or_interface_number.bAlternateSetting if setting_or_interface_number.respond_to? :bAlternateSetting
|
1280
|
+
setting_or_interface_number = setting_or_interface_number.bInterfaceNumber if setting_or_interface_number.respond_to? :bInterfaceNumber
|
1281
|
+
res = Call.libusb_set_interface_alt_setting(@pHandle, setting_or_interface_number, alternate_setting)
|
1282
|
+
LIBUSB.raise_error res, "in libusb_set_interface_alt_setting" if res!=0
|
1283
|
+
end
|
1284
|
+
|
1285
|
+
# Clear the halt/stall condition for an endpoint.
|
1286
|
+
#
|
1287
|
+
# Endpoints with halt status are unable to receive or transmit
|
1288
|
+
# data until the halt condition is stalled.
|
1289
|
+
#
|
1290
|
+
# You should cancel all pending transfers before attempting to
|
1291
|
+
# clear the halt condition.
|
1292
|
+
#
|
1293
|
+
# This is a blocking function.
|
1294
|
+
#
|
1295
|
+
# @param [Endpoint, Fixnum] endpoint the endpoint in question or it's bEndpointAddress
|
1296
|
+
def clear_halt(endpoint)
|
1297
|
+
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
1298
|
+
res = Call.libusb_clear_halt(@pHandle, endpoint)
|
1299
|
+
LIBUSB.raise_error res, "in libusb_clear_halt" if res!=0
|
1300
|
+
end
|
1301
|
+
|
1302
|
+
# Perform a USB port reset to reinitialize a device.
|
1303
|
+
#
|
1304
|
+
# The system will attempt to restore the previous configuration and
|
1305
|
+
# alternate settings after the reset has completed.
|
1306
|
+
#
|
1307
|
+
# If the reset fails, the descriptors change, or the previous
|
1308
|
+
# state cannot be restored, the device will appear to be disconnected
|
1309
|
+
# and reconnected. This means that the device handle is no longer
|
1310
|
+
# valid (you should close it) and rediscover the device. A Exception
|
1311
|
+
# of LIBUSB::ERROR_NOT_FOUND indicates when this is the case.
|
1312
|
+
#
|
1313
|
+
# This is a blocking function which usually incurs a noticeable delay.
|
1314
|
+
def reset_device
|
1315
|
+
res = Call.libusb_reset_device(@pHandle)
|
1316
|
+
LIBUSB.raise_error res, "in libusb_reset_device" if res!=0
|
1317
|
+
end
|
1318
|
+
|
1319
|
+
# Determine if a kernel driver is active on an interface.
|
1320
|
+
#
|
1321
|
+
# If a kernel driver is active, you cannot claim the interface,
|
1322
|
+
# and libusb will be unable to perform I/O.
|
1323
|
+
#
|
1324
|
+
# @param [Interface, Fixnum] interface the interface to check or it's bInterfaceNumber
|
1325
|
+
# @return [Boolean]
|
1326
|
+
def kernel_driver_active?(interface)
|
1327
|
+
interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
|
1328
|
+
res = Call.libusb_kernel_driver_active(@pHandle, interface)
|
1329
|
+
LIBUSB.raise_error res, "in libusb_kernel_driver_active" unless res>=0
|
1330
|
+
return res==1
|
1331
|
+
end
|
1332
|
+
|
1333
|
+
# Detach a kernel driver from an interface.
|
1334
|
+
#
|
1335
|
+
# If successful, you will then be able to claim the interface and perform I/O.
|
1336
|
+
#
|
1337
|
+
# @param [Interface, Fixnum] interface the interface to detach the driver
|
1338
|
+
# from or it's bInterfaceNumber
|
1339
|
+
def detach_kernel_driver(interface)
|
1340
|
+
interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
|
1341
|
+
res = Call.libusb_detach_kernel_driver(@pHandle, interface)
|
1342
|
+
LIBUSB.raise_error res, "in libusb_detach_kernel_driver" if res!=0
|
1343
|
+
end
|
1344
|
+
|
1345
|
+
# Re-attach an interface's kernel driver, which was previously detached
|
1346
|
+
# using {DevHandle#detach_kernel_driver}.
|
1347
|
+
#
|
1348
|
+
# @param [Interface, Fixnum] interface the interface to attach the driver to
|
1349
|
+
def attach_kernel_driver(interface)
|
1350
|
+
interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
|
1351
|
+
res = Call.libusb_attach_kernel_driver(@pHandle, interface)
|
1352
|
+
LIBUSB.raise_error res, "in libusb_attach_kernel_driver" if res!=0
|
1353
|
+
end
|
1354
|
+
|
1355
|
+
|
1356
|
+
# Perform a USB bulk transfer.
|
1357
|
+
#
|
1358
|
+
# The direction of the transfer is inferred from the direction bits of the
|
1359
|
+
# endpoint address.
|
1360
|
+
#
|
1361
|
+
# For bulk reads, the :dataIn param indicates the maximum length of data you are
|
1362
|
+
# expecting to receive. If less data arrives than expected, this function will
|
1363
|
+
# return that data.
|
1364
|
+
#
|
1365
|
+
# You should also check the returned number of bytes for bulk writes. Not all of the
|
1366
|
+
# data may have been written.
|
1367
|
+
#
|
1368
|
+
# Also check transferred bytes when dealing with a timeout error code. libusb may have
|
1369
|
+
# to split your transfer into a number of chunks to satisfy underlying O/S
|
1370
|
+
# requirements, meaning that the timeout may expire after the first few chunks
|
1371
|
+
# have completed. libusb is careful not to lose any data that may have been
|
1372
|
+
# transferred; do not assume that timeout conditions indicate a complete lack of
|
1373
|
+
# I/O.
|
1374
|
+
#
|
1375
|
+
# @param [Endpoint, Fixnum] :endpoint the (address of a) valid endpoint to communicate with
|
1376
|
+
# @param [String] :dataOut the data to send with an outgoing transfer
|
1377
|
+
# @param [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
|
1378
|
+
#
|
1379
|
+
# @return [Fixnum] Number of bytes sent for an outgoing transfer
|
1380
|
+
# @return [String] Received data for an ingoing transfer
|
1381
|
+
def bulk_transfer(args={})
|
1382
|
+
timeout = args.delete(:timeout) || 1000
|
1383
|
+
endpoint = args.delete(:endpoint) || raise(ArgumentError, "no endpoint given")
|
1384
|
+
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
1385
|
+
if endpoint&ENDPOINT_IN != 0
|
1386
|
+
dataIn = args.delete(:dataIn) || raise(ArgumentError, "no :dataIn given for bulk read")
|
1387
|
+
else
|
1388
|
+
dataOut = args.delete(:dataOut) || raise(ArgumentError, "no :dataOut given for bulk write")
|
1389
|
+
end
|
1390
|
+
raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
|
1391
|
+
|
1392
|
+
# reuse transfer struct to speed up transfer
|
1393
|
+
@bulk_transfer ||= BulkTransfer.new :dev_handle => self
|
1394
|
+
tr = @bulk_transfer
|
1395
|
+
tr.endpoint = endpoint
|
1396
|
+
tr.timeout = timeout
|
1397
|
+
if dataOut
|
1398
|
+
tr.buffer = dataOut
|
1399
|
+
else
|
1400
|
+
tr.alloc_buffer(dataIn)
|
1401
|
+
end
|
1402
|
+
|
1403
|
+
tr.submit_and_wait!
|
1404
|
+
|
1405
|
+
if dataOut
|
1406
|
+
tr.actual_length
|
1407
|
+
else
|
1408
|
+
tr.actual_buffer
|
1409
|
+
end
|
1410
|
+
end
|
1411
|
+
|
1412
|
+
# Perform a USB interrupt transfer.
|
1413
|
+
#
|
1414
|
+
# The direction of the transfer is inferred from the direction bits of the
|
1415
|
+
# endpoint address.
|
1416
|
+
#
|
1417
|
+
# For interrupt reads, the :dataIn param indicates the maximum length of data you
|
1418
|
+
# are expecting to receive. If less data arrives than expected, this function will
|
1419
|
+
# return that data.
|
1420
|
+
#
|
1421
|
+
# You should also check the returned number of bytes for interrupt writes. Not all of
|
1422
|
+
# the data may have been written.
|
1423
|
+
#
|
1424
|
+
# Also check transferred when dealing with a timeout error code. libusb may have
|
1425
|
+
# to split your transfer into a number of chunks to satisfy underlying O/S
|
1426
|
+
# requirements, meaning that the timeout may expire after the first few chunks
|
1427
|
+
# have completed. libusb is careful not to lose any data that may have been
|
1428
|
+
# transferred; do not assume that timeout conditions indicate a complete lack of
|
1429
|
+
# I/O.
|
1430
|
+
#
|
1431
|
+
# The default endpoint bInterval value is used as the polling interval.
|
1432
|
+
#
|
1433
|
+
# @param [Endpoint, Fixnum] :endpoint the (address of a) valid endpoint to communicate with
|
1434
|
+
# @param [String] :dataOut the data to send with an outgoing transfer
|
1435
|
+
# @param [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
|
1436
|
+
#
|
1437
|
+
# @return [Fixnum] Number of bytes sent for an outgoing transfer
|
1438
|
+
# @return [String] Received data for an ingoing transfer
|
1439
|
+
def interrupt_transfer(args={})
|
1440
|
+
timeout = args.delete(:timeout) || 1000
|
1441
|
+
endpoint = args.delete(:endpoint) || raise(ArgumentError, "no endpoint given")
|
1442
|
+
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
1443
|
+
if endpoint&ENDPOINT_IN != 0
|
1444
|
+
dataIn = args.delete(:dataIn) || raise(ArgumentError, "no :dataIn given for interrupt read")
|
1445
|
+
else
|
1446
|
+
dataOut = args.delete(:dataOut) || raise(ArgumentError, "no :dataOut given for interrupt write")
|
1447
|
+
end
|
1448
|
+
raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
|
1449
|
+
|
1450
|
+
# reuse transfer struct to speed up transfer
|
1451
|
+
@interrupt_transfer ||= InterruptTransfer.new :dev_handle => self
|
1452
|
+
tr = @interrupt_transfer
|
1453
|
+
tr.endpoint = endpoint
|
1454
|
+
tr.timeout = timeout
|
1455
|
+
if dataOut
|
1456
|
+
tr.buffer = dataOut
|
1457
|
+
else
|
1458
|
+
tr.alloc_buffer(dataIn)
|
1459
|
+
end
|
1460
|
+
|
1461
|
+
tr.submit_and_wait!
|
1462
|
+
|
1463
|
+
if dataOut
|
1464
|
+
tr.actual_length
|
1465
|
+
else
|
1466
|
+
tr.actual_buffer
|
1467
|
+
end
|
1468
|
+
end
|
1469
|
+
|
1470
|
+
# Perform a USB control transfer.
|
1471
|
+
#
|
1472
|
+
# The direction of the transfer is inferred from the bmRequestType field of the
|
1473
|
+
# setup packet.
|
1474
|
+
#
|
1475
|
+
# @param [Fixnum] :bmRequestType the request type field for the setup packet
|
1476
|
+
# @param [Fixnum] :bRequest the request field for the setup packet
|
1477
|
+
# @param [Fixnum] :wValue the value field for the setup packet
|
1478
|
+
# @param [Fixnum] :wIndex the index field for the setup packet
|
1479
|
+
# @param [String] :dataOut the data to send with an outgoing transfer, it
|
1480
|
+
# is appended to the setup packet
|
1481
|
+
# @param [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
|
1482
|
+
# (excluding setup packet)
|
1483
|
+
# @param [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
|
1484
|
+
# up due to no response being received. For an unlimited timeout, use value 0.
|
1485
|
+
#
|
1486
|
+
# @return [Fixnum] Number of bytes sent (excluding setup packet) for outgoing transfer
|
1487
|
+
# @return [String] Received data (without setup packet) for ingoing transfer
|
1488
|
+
def control_transfer(args={})
|
1489
|
+
bmRequestType = args.delete(:bmRequestType) || raise(ArgumentError, "param :bmRequestType not given")
|
1490
|
+
bRequest = args.delete(:bRequest) || raise(ArgumentError, "param :bRequest not given")
|
1491
|
+
wValue = args.delete(:wValue) || raise(ArgumentError, "param :wValue not given")
|
1492
|
+
wIndex = args.delete(:wIndex) || raise(ArgumentError, "param :wIndex not given")
|
1493
|
+
timeout = args.delete(:timeout) || 1000
|
1494
|
+
if bmRequestType&ENDPOINT_IN != 0
|
1495
|
+
dataIn = args.delete(:dataIn) || 0
|
1496
|
+
dataOut = ''
|
1497
|
+
else
|
1498
|
+
dataOut = args.delete(:dataOut) || ''
|
1499
|
+
end
|
1500
|
+
raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
|
1501
|
+
|
1502
|
+
# reuse transfer struct to speed up transfer
|
1503
|
+
@control_transfer ||= ControlTransfer.new :dev_handle => self
|
1504
|
+
tr = @control_transfer
|
1505
|
+
tr.timeout = timeout
|
1506
|
+
if dataIn
|
1507
|
+
setup_data = [bmRequestType, bRequest, wValue, wIndex, dataIn].pack('CCvvv')
|
1508
|
+
tr.alloc_buffer( dataIn + CONTROL_SETUP_SIZE, setup_data )
|
1509
|
+
else
|
1510
|
+
tr.buffer = [bmRequestType, bRequest, wValue, wIndex, dataOut.bytesize, dataOut].pack('CCvvva*')
|
1511
|
+
end
|
1512
|
+
|
1513
|
+
tr.submit_and_wait!
|
1514
|
+
|
1515
|
+
if dataIn
|
1516
|
+
tr.actual_buffer(CONTROL_SETUP_SIZE)
|
1517
|
+
else
|
1518
|
+
tr.actual_length
|
1519
|
+
end
|
1520
|
+
end
|
1521
|
+
end
|
1522
|
+
|
1523
|
+
end
|