libusb 0.7.0-x64-mingw-ucrt

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.appveyor.yml +33 -0
  3. data/.github/workflows/ci.yml +185 -0
  4. data/.gitignore +9 -0
  5. data/.travis.yml +26 -0
  6. data/.yardopts +6 -0
  7. data/COPYING +165 -0
  8. data/Gemfile +19 -0
  9. data/History.md +193 -0
  10. data/README.md +184 -0
  11. data/Rakefile +79 -0
  12. data/lib/libusb/bos.rb +362 -0
  13. data/lib/libusb/call.rb +622 -0
  14. data/lib/libusb/compat.rb +376 -0
  15. data/lib/libusb/configuration.rb +154 -0
  16. data/lib/libusb/constants.rb +170 -0
  17. data/lib/libusb/context.rb +576 -0
  18. data/lib/libusb/context_reference.rb +38 -0
  19. data/lib/libusb/dependencies.rb +7 -0
  20. data/lib/libusb/dev_handle.rb +574 -0
  21. data/lib/libusb/device.rb +407 -0
  22. data/lib/libusb/endpoint.rb +195 -0
  23. data/lib/libusb/eventmachine.rb +187 -0
  24. data/lib/libusb/gem_helper.rb +151 -0
  25. data/lib/libusb/interface.rb +60 -0
  26. data/lib/libusb/libusb_recipe.rb +29 -0
  27. data/lib/libusb/setting.rb +132 -0
  28. data/lib/libusb/ss_companion.rb +72 -0
  29. data/lib/libusb/stdio.rb +25 -0
  30. data/lib/libusb/transfer.rb +418 -0
  31. data/lib/libusb/version_gem.rb +19 -0
  32. data/lib/libusb/version_struct.rb +63 -0
  33. data/lib/libusb-1.0.dll +0 -0
  34. data/lib/libusb.rb +146 -0
  35. data/libusb.gemspec +28 -0
  36. data/test/test_libusb.rb +42 -0
  37. data/test/test_libusb_bos.rb +140 -0
  38. data/test/test_libusb_bulk_stream_transfer.rb +61 -0
  39. data/test/test_libusb_compat.rb +78 -0
  40. data/test/test_libusb_compat_mass_storage.rb +81 -0
  41. data/test/test_libusb_context.rb +88 -0
  42. data/test/test_libusb_descriptors.rb +245 -0
  43. data/test/test_libusb_event_machine.rb +118 -0
  44. data/test/test_libusb_gc.rb +52 -0
  45. data/test/test_libusb_hotplug.rb +129 -0
  46. data/test/test_libusb_iso_transfer.rb +56 -0
  47. data/test/test_libusb_mass_storage.rb +268 -0
  48. data/test/test_libusb_mass_storage2.rb +96 -0
  49. data/test/test_libusb_structs.rb +87 -0
  50. data/test/test_libusb_threads.rb +89 -0
  51. data/wireshark-usb-sniffer.png +0 -0
  52. metadata +112 -0
@@ -0,0 +1,574 @@
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(timeout: 1000,
376
+ endpoint:,
377
+ dataIn: nil,
378
+ dataOut: nil,
379
+ allow_device_memory: false,
380
+ &block)
381
+
382
+ endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
383
+ if endpoint&ENDPOINT_IN != 0
384
+ dataIn || raise(ArgumentError, "no :dataIn given for bulk read")
385
+ else
386
+ dataOut || raise(ArgumentError, "no :dataOut given for bulk write")
387
+ end
388
+
389
+ # reuse transfer struct to speed up transfer
390
+ @bulk_transfer ||= BulkTransfer.new dev_handle: self, allow_device_memory: allow_device_memory
391
+ tr = @bulk_transfer
392
+ tr.endpoint = endpoint
393
+ tr.timeout = timeout
394
+ if dataOut
395
+ tr.buffer = dataOut
396
+ else
397
+ tr.alloc_buffer(dataIn)
398
+ end
399
+
400
+ submit_transfer(tr, dataIn, 0, &block)
401
+ end
402
+
403
+ # Perform a USB interrupt transfer.
404
+ #
405
+ # When called without a block, the transfer is done synchronously - so all events are handled
406
+ # internally and the sent/received data will be returned after completion or an exception will be raised.
407
+ #
408
+ # When called with a block, the method returns immediately after submitting the transfer.
409
+ # You then have to ensure, that {Context#handle_events} is called properly. As soon as the
410
+ # transfer is completed, the block is called with the sent/received data in case of success
411
+ # or the exception instance in case of failure.
412
+ #
413
+ # The direction of the transfer is inferred from the direction bits of the
414
+ # endpoint address.
415
+ #
416
+ # For interrupt reads, the +:dataIn+ param indicates the maximum length of data you
417
+ # are expecting to receive. If less data arrives than expected, this function will
418
+ # return that data.
419
+ #
420
+ # You should check the returned number of bytes for interrupt writes. Not all of
421
+ # the data may have been written.
422
+ #
423
+ # Also check {Error#transferred} when dealing with a timeout exception. libusb may have
424
+ # to split your transfer into a number of chunks to satisfy underlying O/S
425
+ # requirements, meaning that the timeout may expire after the first few chunks
426
+ # have completed. libusb is careful not to lose any data that may have been
427
+ # transferred; do not assume that timeout conditions indicate a complete lack of
428
+ # I/O.
429
+ #
430
+ # The default endpoint bInterval value is used as the polling interval.
431
+ #
432
+ # @param [Hash] args
433
+ # @option args [Endpoint, Fixnum] :endpoint the (address of a) valid endpoint to communicate with
434
+ # @option args [String] :dataOut the data to send with an outgoing transfer
435
+ # @option args [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
436
+ # @option args [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
437
+ # up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
438
+ #
439
+ # @return [Fixnum] Number of bytes sent for an outgoing transfer
440
+ # @return [String] Received data for an ingoing transfer
441
+ # @return [self] When called with a block
442
+ #
443
+ # @yieldparam [String, Integer, LIBUSB::Error] result result of the transfer is yielded to the block,
444
+ # when the asynchronous transfer has finished
445
+ # @raise [ArgumentError, LIBUSB::Error] in case of failure
446
+ def interrupt_transfer(timeout: 1000,
447
+ endpoint:,
448
+ dataIn: nil,
449
+ dataOut: nil,
450
+ allow_device_memory: false,
451
+ &block)
452
+ endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
453
+ if endpoint&ENDPOINT_IN != 0
454
+ dataIn || raise(ArgumentError, "no :dataIn given for interrupt read")
455
+ else
456
+ dataOut || raise(ArgumentError, "no :dataOut given for interrupt write")
457
+ end
458
+
459
+ # reuse transfer struct to speed up transfer
460
+ @interrupt_transfer ||= InterruptTransfer.new dev_handle: self, allow_device_memory: allow_device_memory
461
+ tr = @interrupt_transfer
462
+ tr.endpoint = endpoint
463
+ tr.timeout = timeout
464
+ if dataOut
465
+ tr.buffer = dataOut
466
+ else
467
+ tr.alloc_buffer(dataIn)
468
+ end
469
+
470
+ submit_transfer(tr, dataIn, 0, &block)
471
+ end
472
+
473
+ # Perform a USB control transfer.
474
+ #
475
+ # When called without a block, the transfer is done synchronously - so all events are handled
476
+ # internally and the sent/received data will be returned after completion or an exception will be raised.
477
+ #
478
+ # When called with a block, the method returns immediately after submitting the transfer.
479
+ # You then have to ensure, that {Context#handle_events} is called properly. As soon as the
480
+ # transfer is completed, the block is called with the sent/received data in case of success
481
+ # or the exception instance in case of failure.
482
+ #
483
+ # The direction of the transfer is inferred from the +:bmRequestType+ field of the
484
+ # setup packet.
485
+ #
486
+ # @param [Hash] args
487
+ # @option args [Fixnum] :bmRequestType the request type field for the setup packet
488
+ # @option args [Fixnum] :bRequest the request field for the setup packet
489
+ # @option args [Fixnum] :wValue the value field for the setup packet
490
+ # @option args [Fixnum] :wIndex the index field for the setup packet
491
+ # @option args [String] :dataOut the data to send with an outgoing transfer, it
492
+ # is appended to the setup packet
493
+ # @option args [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
494
+ # (excluding setup packet)
495
+ # @option args [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
496
+ # up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
497
+ #
498
+ # @return [Fixnum] Number of bytes sent (excluding setup packet) for outgoing transfer
499
+ # @return [String] Received data (without setup packet) for ingoing transfer
500
+ # @return [self] When called with a block
501
+ #
502
+ # @yieldparam [String, Integer, LIBUSB::Error] result result of the transfer is yielded to the block,
503
+ # when the asynchronous transfer has finished
504
+ # @raise [ArgumentError, LIBUSB::Error] in case of failure
505
+ def control_transfer(bmRequestType:,
506
+ bRequest:,
507
+ wValue:,
508
+ wIndex:,
509
+ timeout: 1000,
510
+ dataIn: nil,
511
+ dataOut: nil,
512
+ allow_device_memory: false,
513
+ &block)
514
+
515
+ if bmRequestType&ENDPOINT_IN != 0
516
+ raise ArgumentError, "invalid param :dataOut" unless dataOut.nil?
517
+ dataIn ||= 0
518
+ dataOut = ''
519
+ else
520
+ raise ArgumentError, "invalid param :dataIn" unless dataIn.nil?
521
+ dataOut ||= ''
522
+ end
523
+
524
+ # reuse transfer struct to speed up transfer
525
+ @control_transfer ||= ControlTransfer.new dev_handle: self, allow_device_memory: allow_device_memory
526
+ tr = @control_transfer
527
+ tr.timeout = timeout
528
+ if dataIn
529
+ setup_data = [bmRequestType, bRequest, wValue, wIndex, dataIn].pack('CCvvv')
530
+ tr.alloc_buffer( dataIn + CONTROL_SETUP_SIZE, setup_data )
531
+ else
532
+ tr.buffer = [bmRequestType, bRequest, wValue, wIndex, dataOut.bytesize, dataOut].pack('CCvvva*')
533
+ end
534
+
535
+ submit_transfer(tr, dataIn, CONTROL_SETUP_SIZE, &block)
536
+ end
537
+
538
+ private
539
+ def submit_transfer(tr, dataIn, offset)
540
+ if block_given?
541
+ tr.submit! do
542
+ res = dataIn ? tr.actual_buffer(offset) : tr.actual_length
543
+
544
+ if tr.status==:TRANSFER_COMPLETED
545
+ yield res
546
+ else
547
+ exception = Transfer::TransferStatusToError[tr.status] || ERROR_OTHER
548
+
549
+ yield exception.new("error #{tr.status}", res)
550
+ end
551
+ end
552
+ self
553
+ else
554
+ tr.submit_and_wait
555
+
556
+ res = dataIn ? tr.actual_buffer(offset) : tr.actual_length
557
+
558
+ unless tr.status==:TRANSFER_COMPLETED
559
+ raise((Transfer::TransferStatusToError[tr.status] || ERROR_OTHER).new("error #{tr.status}", res))
560
+ end
561
+ res
562
+ end
563
+ end
564
+
565
+ def endpoints_as_ffi_bytes(endpoints)
566
+ pEndpoints = FFI::MemoryPointer.new :char, endpoints.length
567
+ endpoints.each_with_index do |ep, epi|
568
+ ep = ep.bEndpointAddress if ep.respond_to? :bEndpointAddress
569
+ pEndpoints.put_uchar(epi, ep)
570
+ end
571
+ pEndpoints
572
+ end
573
+ end
574
+ end