libusb 0.6.0-x86-linux

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.travis.yml +17 -0
  4. data/.yardopts +6 -0
  5. data/COPYING +165 -0
  6. data/Gemfile +11 -0
  7. data/History.md +124 -0
  8. data/README.md +159 -0
  9. data/Rakefile +145 -0
  10. data/appveyor.yml +23 -0
  11. data/lib/libusb.rb +58 -0
  12. data/lib/libusb/bos.rb +306 -0
  13. data/lib/libusb/call.rb +446 -0
  14. data/lib/libusb/compat.rb +376 -0
  15. data/lib/libusb/configuration.rb +155 -0
  16. data/lib/libusb/constants.rb +160 -0
  17. data/lib/libusb/context.rb +426 -0
  18. data/lib/libusb/dependencies.rb +7 -0
  19. data/lib/libusb/dev_handle.rb +564 -0
  20. data/lib/libusb/device.rb +365 -0
  21. data/lib/libusb/endpoint.rb +194 -0
  22. data/lib/libusb/eventmachine.rb +183 -0
  23. data/lib/libusb/interface.rb +60 -0
  24. data/lib/libusb/setting.rb +132 -0
  25. data/lib/libusb/ss_companion.rb +69 -0
  26. data/lib/libusb/stdio.rb +25 -0
  27. data/lib/libusb/transfer.rb +377 -0
  28. data/lib/libusb/version_gem.rb +19 -0
  29. data/lib/libusb/version_struct.rb +63 -0
  30. data/libusb.gemspec +30 -0
  31. data/test/test_libusb_bos.rb +118 -0
  32. data/test/test_libusb_bulk_stream_transfer.rb +50 -0
  33. data/test/test_libusb_capability.rb +23 -0
  34. data/test/test_libusb_compat.rb +78 -0
  35. data/test/test_libusb_compat_mass_storage.rb +81 -0
  36. data/test/test_libusb_descriptors.rb +212 -0
  37. data/test/test_libusb_event_machine.rb +118 -0
  38. data/test/test_libusb_gc.rb +37 -0
  39. data/test/test_libusb_hotplug.rb +127 -0
  40. data/test/test_libusb_iso_transfer.rb +50 -0
  41. data/test/test_libusb_mass_storage.rb +268 -0
  42. data/test/test_libusb_mass_storage2.rb +96 -0
  43. data/test/test_libusb_structs.rb +58 -0
  44. data/test/test_libusb_threads.rb +89 -0
  45. data/test/test_libusb_version.rb +40 -0
  46. data/wireshark-usb-sniffer.png +0 -0
  47. metadata +150 -0
@@ -0,0 +1,7 @@
1
+ module LIBUSB
2
+ LIBUSB_VERSION = ENV['LIBUSB_VERSION'] || '1.0.21'
3
+ LIBUSB_SOURCE_URI = "https://github.com/libusb/libusb/releases/download/v#{LIBUSB_VERSION}/libusb-#{LIBUSB_VERSION}.tar.bz2"
4
+ LIBUSB_SOURCE_SHA1 = '54d71841542eb1a6f0b0420878a4d5434efe8d28'
5
+
6
+ MINI_PORTILE_VERSION = '~> 2.1'
7
+ end
@@ -0,0 +1,564 @@
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
+ @bulk_transfer.free_buffer if @bulk_transfer
45
+ @interrupt_transfer.free_buffer if @interrupt_transfer
46
+ @control_transfer.free_buffer if @control_transfer
47
+ Call.libusb_close(@pHandle)
48
+ end
49
+
50
+ def string_descriptor_ascii(index)
51
+ pString = FFI::MemoryPointer.new 0x100
52
+ res = Call.libusb_get_string_descriptor_ascii(@pHandle, index, pString, pString.size)
53
+ LIBUSB.raise_error res, "in libusb_get_string_descriptor_ascii" unless res>=0
54
+ pString.read_string(res)
55
+ end
56
+
57
+ # Claim an interface on a given device handle.
58
+ #
59
+ # You must claim the interface you wish to use before you can perform I/O on any
60
+ # of its endpoints.
61
+ #
62
+ # It is legal to attempt to claim an already-claimed interface, in which case
63
+ # libusb just returns without doing anything.
64
+ #
65
+ # Claiming of interfaces is a purely logical operation; it does not cause any
66
+ # requests to be sent over the bus. Interface claiming is used to instruct the
67
+ # underlying operating system that your application wishes to take ownership of
68
+ # the interface.
69
+ #
70
+ # This is a non-blocking function.
71
+ #
72
+ # If called with a block, the device handle is passed through to the block
73
+ # and the interface is released when the block has finished.
74
+ #
75
+ # @param [Interface, Fixnum] interface the interface or it's bInterfaceNumber you wish to claim
76
+ def claim_interface(interface)
77
+ interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
78
+ res = Call.libusb_claim_interface(@pHandle, interface)
79
+ LIBUSB.raise_error res, "in libusb_claim_interface" if res!=0
80
+ return self unless block_given?
81
+ begin
82
+ yield self
83
+ ensure
84
+ release_interface(interface)
85
+ end
86
+ end
87
+
88
+ # Release an interface previously claimed with {DevHandle#claim_interface}.
89
+ #
90
+ # You should release all claimed interfaces before closing a device handle.
91
+ #
92
+ # This is a blocking function. A SET_INTERFACE control request will be sent to the
93
+ # device, resetting interface state to the first alternate setting.
94
+ #
95
+ # @param [Interface, Fixnum] interface the interface or it's bInterfaceNumber you
96
+ # claimed previously
97
+ def release_interface(interface)
98
+ interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
99
+ res = Call.libusb_release_interface(@pHandle, interface)
100
+ LIBUSB.raise_error res, "in libusb_release_interface" if res!=0
101
+ end
102
+
103
+ # Set the active configuration for a device.
104
+ #
105
+ # The operating system may or may not have already set an active configuration on
106
+ # the device. It is up to your application to ensure the correct configuration is
107
+ # selected before you attempt to claim interfaces and perform other operations.
108
+ #
109
+ # If you call this function on a device already configured with the selected
110
+ # configuration, then this function will act as a lightweight device reset: it
111
+ # will issue a SET_CONFIGURATION request using the current configuration, causing
112
+ # most USB-related device state to be reset (altsetting reset to zero, endpoint
113
+ # halts cleared, toggles reset).
114
+ #
115
+ # You cannot change/reset configuration if your application has claimed interfaces -
116
+ # you should free them with {DevHandle#release_interface} first. You cannot
117
+ # change/reset configuration if other applications or drivers have claimed
118
+ # interfaces.
119
+ #
120
+ # A configuration value of +nil+ will put the device in unconfigured state. The USB
121
+ # specifications state that a configuration value of 0 does this, however buggy
122
+ # devices exist which actually have a configuration 0.
123
+ #
124
+ # You should always use this function rather than formulating your own
125
+ # SET_CONFIGURATION control request. This is because the underlying operating
126
+ # system needs to know when such changes happen.
127
+ #
128
+ # This is a blocking function.
129
+ #
130
+ # @param [Configuration, Fixnum] configuration the configuration or it's
131
+ # bConfigurationValue you wish to activate, or +nil+ if you wish to put
132
+ # the device in unconfigured state
133
+ def set_configuration(configuration)
134
+ configuration = configuration.bConfigurationValue if configuration.respond_to? :bConfigurationValue
135
+ res = Call.libusb_set_configuration(@pHandle, configuration || -1)
136
+ LIBUSB.raise_error res, "in libusb_set_configuration" if res!=0
137
+ end
138
+ alias configuration= set_configuration
139
+
140
+ # Activate an alternate setting for an interface.
141
+ #
142
+ # The interface must have been previously claimed with {DevHandle#claim_interface}.
143
+ #
144
+ # You should always use this function rather than formulating your own
145
+ # SET_INTERFACE control request. This is because the underlying operating system
146
+ # needs to know when such changes happen.
147
+ #
148
+ # This is a blocking function.
149
+ #
150
+ # @param [Setting, Fixnum] setting_or_interface_number the alternate setting
151
+ # to activate or the bInterfaceNumber of the previously-claimed interface
152
+ # @param [Fixnum, nil] alternate_setting the bAlternateSetting of the alternate setting to activate
153
+ # (only if first param is a Fixnum)
154
+ def set_interface_alt_setting(setting_or_interface_number, alternate_setting=nil)
155
+ alternate_setting ||= setting_or_interface_number.bAlternateSetting if setting_or_interface_number.respond_to? :bAlternateSetting
156
+ setting_or_interface_number = setting_or_interface_number.bInterfaceNumber if setting_or_interface_number.respond_to? :bInterfaceNumber
157
+ res = Call.libusb_set_interface_alt_setting(@pHandle, setting_or_interface_number, alternate_setting)
158
+ LIBUSB.raise_error res, "in libusb_set_interface_alt_setting" if res!=0
159
+ end
160
+
161
+ # Clear the halt/stall condition for an endpoint.
162
+ #
163
+ # Endpoints with halt status are unable to receive or transmit
164
+ # data until the halt condition is stalled.
165
+ #
166
+ # You should cancel all pending transfers before attempting to
167
+ # clear the halt condition.
168
+ #
169
+ # This is a blocking function.
170
+ #
171
+ # @param [Endpoint, Fixnum] endpoint the endpoint in question or it's bEndpointAddress
172
+ def clear_halt(endpoint)
173
+ endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
174
+ res = Call.libusb_clear_halt(@pHandle, endpoint)
175
+ LIBUSB.raise_error res, "in libusb_clear_halt" if res!=0
176
+ end
177
+
178
+ # Perform a USB port reset to reinitialize a device.
179
+ #
180
+ # The system will attempt to restore the previous configuration and
181
+ # alternate settings after the reset has completed.
182
+ #
183
+ # If the reset fails, the descriptors change, or the previous
184
+ # state cannot be restored, the device will appear to be disconnected
185
+ # and reconnected. This means that the device handle is no longer
186
+ # valid (you should close it) and rediscover the device. A Exception
187
+ # of LIBUSB::ERROR_NOT_FOUND indicates when this is the case.
188
+ #
189
+ # This is a blocking function which usually incurs a noticeable delay.
190
+ def reset_device
191
+ res = Call.libusb_reset_device(@pHandle)
192
+ LIBUSB.raise_error res, "in libusb_reset_device" if res!=0
193
+ end
194
+
195
+ if Call.respond_to?(:libusb_alloc_streams)
196
+
197
+ # @method alloc_streams
198
+ #
199
+ # Allocate up to num_streams usb bulk streams on the specified endpoints. This
200
+ # function takes an array of endpoints rather then a single endpoint because
201
+ # some protocols require that endpoints are setup with similar stream ids.
202
+ # All endpoints passed in must belong to the same interface.
203
+ #
204
+ # Note this function may return less streams then requested. Also note that the
205
+ # same number of streams are allocated for each endpoint in the endpoint array.
206
+ #
207
+ # Stream id 0 is reserved, and should not be used to communicate with devices.
208
+ # If {alloc_streams} returns with a value of N, you may use stream ids
209
+ # 1 to N.
210
+ #
211
+ # Available since libusb-1.0.19.
212
+ #
213
+ # @param [Fixnum] num_streams number of streams to try to allocate
214
+ # @param [Array<Fixnum>, Array<Endpoint>] endpoints array of endpoints to allocate streams on
215
+ # @return [Fixnum] number of streams allocated
216
+ # @see #free_streams
217
+ # @see BulkStreamTransfer
218
+ def alloc_streams(num_streams, endpoints)
219
+ pEndpoints = endpoints_as_ffi_bytes(endpoints)
220
+ res = Call.libusb_alloc_streams(@pHandle, num_streams, pEndpoints, endpoints.length)
221
+ LIBUSB.raise_error res, "in libusb_alloc_streams" unless res>=0
222
+ res
223
+ end
224
+
225
+ # @method free_streams
226
+ #
227
+ # Free usb bulk streams allocated with {alloc_streams}
228
+ #
229
+ # Note streams are automatically free-ed when releasing an interface.
230
+ #
231
+ # Available since libusb-1.0.19.
232
+ #
233
+ # @param [Array<Fixnum>, Array<Endpoint>] endpoints array of endpoints to free streams on
234
+ # @see #alloc_streams
235
+ def free_streams(endpoints)
236
+ pEndpoints = endpoints_as_ffi_bytes(endpoints)
237
+ res = Call.libusb_free_streams(@pHandle, pEndpoints, endpoints.length)
238
+ LIBUSB.raise_error res, "in libusb_free_streams" unless res>=0
239
+ nil
240
+ end
241
+
242
+ else
243
+
244
+ def alloc_streams(num_streams, endpoints)
245
+ raise NotImplementedError, "libusb-1.0.19+ is required for bulk stream transfers"
246
+ end
247
+
248
+ end
249
+
250
+ # Determine if a kernel driver is active on an interface.
251
+ #
252
+ # If a kernel driver is active, you cannot claim the interface,
253
+ # and libusb will be unable to perform I/O.
254
+ #
255
+ # @param [Interface, Fixnum] interface the interface to check or it's bInterfaceNumber
256
+ # @return [Boolean]
257
+ def kernel_driver_active?(interface)
258
+ interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
259
+ res = Call.libusb_kernel_driver_active(@pHandle, interface)
260
+ LIBUSB.raise_error res, "in libusb_kernel_driver_active" unless res>=0
261
+ return res==1
262
+ end
263
+
264
+ # Detach a kernel driver from an interface.
265
+ #
266
+ # If successful, you will then be able to claim the interface and perform I/O.
267
+ #
268
+ # @param [Interface, Fixnum] interface the interface to detach the driver
269
+ # from or it's bInterfaceNumber
270
+ def detach_kernel_driver(interface)
271
+ interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
272
+ res = Call.libusb_detach_kernel_driver(@pHandle, interface)
273
+ LIBUSB.raise_error res, "in libusb_detach_kernel_driver" if res!=0
274
+ end
275
+
276
+ # Re-attach an interface's kernel driver, which was previously detached
277
+ # using {DevHandle#detach_kernel_driver}.
278
+ #
279
+ # @param [Interface, Fixnum] interface the interface to attach the driver to
280
+ def attach_kernel_driver(interface)
281
+ interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
282
+ res = Call.libusb_attach_kernel_driver(@pHandle, interface)
283
+ LIBUSB.raise_error res, "in libusb_attach_kernel_driver" if res!=0
284
+ end
285
+
286
+ # @private
287
+ if Call.respond_to?(:libusb_set_auto_detach_kernel_driver)
288
+
289
+ # @method auto_detach_kernel_driver=
290
+ # Enable/disable libusb's automatic kernel driver detachment.
291
+ #
292
+ # When this is enabled libusb will automatically detach the kernel driver on an
293
+ # interface when claiming the interface, and attach it when releasing the
294
+ # interface.
295
+ #
296
+ # Automatic kernel driver detachment is disabled on newly opened device handles by
297
+ # default.
298
+ #
299
+ # On platforms which do not have CAP_SUPPORTS_DETACH_KERNEL_DRIVER this
300
+ # function will return ERROR_NOT_SUPPORTED, and libusb will continue as if
301
+ # this function was never called.
302
+ #
303
+ # Available since libusb-1.0.16.
304
+ #
305
+ # @param [Boolean] enable whether to enable or disable auto kernel driver detachment
306
+ #
307
+ # @see LIBUSB.has_capability?
308
+ def auto_detach_kernel_driver=(enable)
309
+ res = Call.libusb_set_auto_detach_kernel_driver(@pHandle, enable ? 1 : 0)
310
+ LIBUSB.raise_error res, "in libusb_set_auto_detach_kernel_driver" if res!=0
311
+ end
312
+ end
313
+
314
+ # @private
315
+ if Call.respond_to?(:libusb_get_bos_descriptor)
316
+
317
+ # @method bos
318
+ # Get a Binary Object Store (BOS) descriptor.
319
+ #
320
+ # This is a BLOCKING function, which will send requests to the device.
321
+ #
322
+ # Since libusb version 1.0.16.
323
+ #
324
+ # @return [Bos]
325
+ def bos
326
+ ctx = device.context.instance_variable_get(:@ctx)
327
+ pp_desc = FFI::MemoryPointer.new :pointer
328
+ res = Call.libusb_get_bos_descriptor(@pHandle, pp_desc)
329
+ LIBUSB.raise_error res, "in libusb_get_bos_descriptor" if res!=0
330
+ Bos.new(ctx, pp_desc.read_pointer)
331
+ end
332
+ end
333
+
334
+ # Perform a USB bulk transfer.
335
+ #
336
+ # When called without a block, the transfer is done synchronously - so all events are handled
337
+ # internally and the sent/received data will be returned after completion or an exception will be raised.
338
+ #
339
+ # When called with a block, the method returns immediately after submitting the transfer.
340
+ # You then have to ensure, that {Context#handle_events} is called properly. As soon as the
341
+ # transfer is completed, the block is called with the sent/received data in case of success
342
+ # or the exception instance in case of failure.
343
+ #
344
+ # The direction of the transfer is inferred from the direction bits of the
345
+ # endpoint address.
346
+ #
347
+ # For bulk reads, the +:dataIn+ param indicates the maximum length of data you are
348
+ # expecting to receive. If less data arrives than expected, this function will
349
+ # return that data.
350
+ #
351
+ # You should check the returned number of bytes for bulk writes. Not all of the
352
+ # data may have been written.
353
+ #
354
+ # Also check {Error#transferred} when dealing with a timeout exception. libusb may have
355
+ # to split your transfer into a number of chunks to satisfy underlying O/S
356
+ # requirements, meaning that the timeout may expire after the first few chunks
357
+ # have completed. libusb is careful not to lose any data that may have been
358
+ # transferred; do not assume that timeout conditions indicate a complete lack of
359
+ # I/O.
360
+ #
361
+ # @param [Hash] args
362
+ # @option args [Endpoint, Fixnum] :endpoint the (address of a) valid endpoint to communicate with
363
+ # @option args [String] :dataOut the data to send with an outgoing transfer
364
+ # @option args [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
365
+ # @option args [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
366
+ # up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
367
+ #
368
+ # @return [Fixnum] Number of bytes sent for an outgoing transfer
369
+ # @return [String] Received data for an ingoing transfer
370
+ # @return [self] When called with a block
371
+ #
372
+ # @yieldparam [String, Integer, LIBUSB::Error] result result of the transfer is yielded to the block,
373
+ # when the asynchronous transfer has finished
374
+ # @raise [ArgumentError, LIBUSB::Error] in case of failure
375
+ def bulk_transfer(args={}, &block)
376
+ timeout = args.delete(:timeout) || 1000
377
+ endpoint = args.delete(:endpoint) || raise(ArgumentError, "no endpoint given")
378
+ endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
379
+ if endpoint&ENDPOINT_IN != 0
380
+ dataIn = args.delete(:dataIn) || raise(ArgumentError, "no :dataIn given for bulk read")
381
+ else
382
+ dataOut = args.delete(:dataOut) || raise(ArgumentError, "no :dataOut given for bulk write")
383
+ end
384
+ raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
385
+
386
+ # reuse transfer struct to speed up transfer
387
+ @bulk_transfer ||= BulkTransfer.new dev_handle: self, allow_device_memory: true
388
+ tr = @bulk_transfer
389
+ tr.endpoint = endpoint
390
+ tr.timeout = timeout
391
+ if dataOut
392
+ tr.buffer = dataOut
393
+ else
394
+ tr.alloc_buffer(dataIn)
395
+ end
396
+
397
+ submit_transfer(tr, dataIn, 0, &block)
398
+ end
399
+
400
+ # Perform a USB interrupt transfer.
401
+ #
402
+ # When called without a block, the transfer is done synchronously - so all events are handled
403
+ # internally and the sent/received data will be returned after completion or an exception will be raised.
404
+ #
405
+ # When called with a block, the method returns immediately after submitting the transfer.
406
+ # You then have to ensure, that {Context#handle_events} is called properly. As soon as the
407
+ # transfer is completed, the block is called with the sent/received data in case of success
408
+ # or the exception instance in case of failure.
409
+ #
410
+ # The direction of the transfer is inferred from the direction bits of the
411
+ # endpoint address.
412
+ #
413
+ # For interrupt reads, the +:dataIn+ param indicates the maximum length of data you
414
+ # are expecting to receive. If less data arrives than expected, this function will
415
+ # return that data.
416
+ #
417
+ # You should check the returned number of bytes for interrupt writes. Not all of
418
+ # the data may have been written.
419
+ #
420
+ # Also check {Error#transferred} when dealing with a timeout exception. libusb may have
421
+ # to split your transfer into a number of chunks to satisfy underlying O/S
422
+ # requirements, meaning that the timeout may expire after the first few chunks
423
+ # have completed. libusb is careful not to lose any data that may have been
424
+ # transferred; do not assume that timeout conditions indicate a complete lack of
425
+ # I/O.
426
+ #
427
+ # The default endpoint bInterval value is used as the polling interval.
428
+ #
429
+ # @param [Hash] args
430
+ # @option args [Endpoint, Fixnum] :endpoint the (address of a) valid endpoint to communicate with
431
+ # @option args [String] :dataOut the data to send with an outgoing transfer
432
+ # @option args [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
433
+ # @option args [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
434
+ # up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
435
+ #
436
+ # @return [Fixnum] Number of bytes sent for an outgoing transfer
437
+ # @return [String] Received data for an ingoing transfer
438
+ # @return [self] When called with a block
439
+ #
440
+ # @yieldparam [String, Integer, LIBUSB::Error] result result of the transfer is yielded to the block,
441
+ # when the asynchronous transfer has finished
442
+ # @raise [ArgumentError, LIBUSB::Error] in case of failure
443
+ def interrupt_transfer(args={}, &block)
444
+ timeout = args.delete(:timeout) || 1000
445
+ endpoint = args.delete(:endpoint) || raise(ArgumentError, "no endpoint given")
446
+ endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
447
+ if endpoint&ENDPOINT_IN != 0
448
+ dataIn = args.delete(:dataIn) || raise(ArgumentError, "no :dataIn given for interrupt read")
449
+ else
450
+ dataOut = args.delete(:dataOut) || raise(ArgumentError, "no :dataOut given for interrupt write")
451
+ end
452
+ raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
453
+
454
+ # reuse transfer struct to speed up transfer
455
+ @interrupt_transfer ||= InterruptTransfer.new dev_handle: self, allow_device_memory: true
456
+ tr = @interrupt_transfer
457
+ tr.endpoint = endpoint
458
+ tr.timeout = timeout
459
+ if dataOut
460
+ tr.buffer = dataOut
461
+ else
462
+ tr.alloc_buffer(dataIn)
463
+ end
464
+
465
+ submit_transfer(tr, dataIn, 0, &block)
466
+ end
467
+
468
+ # Perform a USB control transfer.
469
+ #
470
+ # When called without a block, the transfer is done synchronously - so all events are handled
471
+ # internally and the sent/received data will be returned after completion or an exception will be raised.
472
+ #
473
+ # When called with a block, the method returns immediately after submitting the transfer.
474
+ # You then have to ensure, that {Context#handle_events} is called properly. As soon as the
475
+ # transfer is completed, the block is called with the sent/received data in case of success
476
+ # or the exception instance in case of failure.
477
+ #
478
+ # The direction of the transfer is inferred from the +:bmRequestType+ field of the
479
+ # setup packet.
480
+ #
481
+ # @param [Hash] args
482
+ # @option args [Fixnum] :bmRequestType the request type field for the setup packet
483
+ # @option args [Fixnum] :bRequest the request field for the setup packet
484
+ # @option args [Fixnum] :wValue the value field for the setup packet
485
+ # @option args [Fixnum] :wIndex the index field for the setup packet
486
+ # @option args [String] :dataOut the data to send with an outgoing transfer, it
487
+ # is appended to the setup packet
488
+ # @option args [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
489
+ # (excluding setup packet)
490
+ # @option args [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
491
+ # up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
492
+ #
493
+ # @return [Fixnum] Number of bytes sent (excluding setup packet) for outgoing transfer
494
+ # @return [String] Received data (without setup packet) for ingoing transfer
495
+ # @return [self] When called with a block
496
+ #
497
+ # @yieldparam [String, Integer, LIBUSB::Error] result result of the transfer is yielded to the block,
498
+ # when the asynchronous transfer has finished
499
+ # @raise [ArgumentError, LIBUSB::Error] in case of failure
500
+ def control_transfer(args={}, &block)
501
+ bmRequestType = args.delete(:bmRequestType) || raise(ArgumentError, "param :bmRequestType not given")
502
+ bRequest = args.delete(:bRequest) || raise(ArgumentError, "param :bRequest not given")
503
+ wValue = args.delete(:wValue) || raise(ArgumentError, "param :wValue not given")
504
+ wIndex = args.delete(:wIndex) || raise(ArgumentError, "param :wIndex not given")
505
+ timeout = args.delete(:timeout) || 1000
506
+ if bmRequestType&ENDPOINT_IN != 0
507
+ dataIn = args.delete(:dataIn) || 0
508
+ dataOut = ''
509
+ else
510
+ dataOut = args.delete(:dataOut) || ''
511
+ end
512
+ raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
513
+
514
+ # reuse transfer struct to speed up transfer
515
+ @control_transfer ||= ControlTransfer.new dev_handle: self, allow_device_memory: true
516
+ tr = @control_transfer
517
+ tr.timeout = timeout
518
+ if dataIn
519
+ setup_data = [bmRequestType, bRequest, wValue, wIndex, dataIn].pack('CCvvv')
520
+ tr.alloc_buffer( dataIn + CONTROL_SETUP_SIZE, setup_data )
521
+ else
522
+ tr.buffer = [bmRequestType, bRequest, wValue, wIndex, dataOut.bytesize, dataOut].pack('CCvvva*')
523
+ end
524
+
525
+ submit_transfer(tr, dataIn, CONTROL_SETUP_SIZE, &block)
526
+ end
527
+
528
+ private
529
+ def submit_transfer(tr, dataIn, offset)
530
+ if block_given?
531
+ tr.submit! do
532
+ res = dataIn ? tr.actual_buffer(offset) : tr.actual_length
533
+
534
+ if tr.status==:TRANSFER_COMPLETED
535
+ yield res
536
+ else
537
+ exception = Transfer::TransferStatusToError[tr.status] || ERROR_OTHER
538
+
539
+ yield exception.new("error #{tr.status}", res)
540
+ end
541
+ end
542
+ self
543
+ else
544
+ tr.submit_and_wait
545
+
546
+ res = dataIn ? tr.actual_buffer(offset) : tr.actual_length
547
+
548
+ unless tr.status==:TRANSFER_COMPLETED
549
+ raise((Transfer::TransferStatusToError[tr.status] || ERROR_OTHER).new("error #{tr.status}", res))
550
+ end
551
+ res
552
+ end
553
+ end
554
+
555
+ def endpoints_as_ffi_bytes(endpoints)
556
+ pEndpoints = FFI::MemoryPointer.new :char, endpoints.length
557
+ endpoints.each_with_index do |ep, epi|
558
+ ep = ep.bEndpointAddress if ep.respond_to? :bEndpointAddress
559
+ pEndpoints.put_uchar(epi, ep)
560
+ end
561
+ pEndpoints
562
+ end
563
+ end
564
+ end