libusb 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,127 @@
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 libusb session.
20
+ class Context
21
+ # Initialize libusb context.
22
+ def initialize
23
+ m = FFI::MemoryPointer.new :pointer
24
+ Call.libusb_init(m)
25
+ @ctx = m.read_pointer
26
+ end
27
+
28
+ # Deinitialize libusb.
29
+ #
30
+ # Should be called after closing all open devices and before your application terminates.
31
+ def exit
32
+ Call.libusb_exit(@ctx)
33
+ end
34
+
35
+ # Set message verbosity.
36
+ #
37
+ # * Level 0: no messages ever printed by the library (default)
38
+ # * Level 1: error messages are printed to stderr
39
+ # * Level 2: warning and error messages are printed to stderr
40
+ # * Level 3: informational messages are printed to stdout, warning and
41
+ # error messages are printed to stderr
42
+ #
43
+ # The default level is 0, which means no messages are ever printed. If you
44
+ # choose to increase the message verbosity level, ensure that your
45
+ # application does not close the stdout/stderr file descriptors.
46
+ #
47
+ # You are advised to set level 3. libusb is conservative with its message
48
+ # logging and most of the time, will only log messages that explain error
49
+ # conditions and other oddities. This will help you debug your software.
50
+ #
51
+ # If the LIBUSB_DEBUG environment variable was set when libusb was
52
+ # initialized, this method does nothing: the message verbosity is
53
+ # fixed to the value in the environment variable.
54
+ #
55
+ # If libusb was compiled without any message logging, this method
56
+ # does nothing: you'll never get any messages.
57
+ #
58
+ # If libusb was compiled with verbose debug message logging, this
59
+ # method does nothing: you'll always get messages from all levels.
60
+ #
61
+ # @param [Fixnum] level debug level to set
62
+ def debug=(level)
63
+ Call.libusb_set_debug(@ctx, level)
64
+ end
65
+
66
+ def device_list
67
+ pppDevs = FFI::MemoryPointer.new :pointer
68
+ size = Call.libusb_get_device_list(@ctx, pppDevs)
69
+ ppDevs = pppDevs.read_pointer
70
+ pDevs = []
71
+ size.times do |devi|
72
+ pDev = ppDevs.get_pointer(devi*FFI.type_size(:pointer))
73
+ pDevs << Device.new(self, pDev)
74
+ end
75
+ Call.libusb_free_device_list(ppDevs, 1)
76
+ pDevs
77
+ end
78
+ private :device_list
79
+
80
+ # Handle any pending events in blocking mode.
81
+ #
82
+ # This method must be called when libusb is running asynchronous transfers.
83
+ # This gives libusb the opportunity to reap pending transfers,
84
+ # invoke callbacks, etc.
85
+ def handle_events
86
+ res = Call.libusb_handle_events(@ctx)
87
+ LIBUSB.raise_error res, "in libusb_handle_events" if res<0
88
+ end
89
+
90
+ # Obtain a list of devices currently attached to the USB system, optionally matching certain criteria.
91
+ #
92
+ # @param [Hash] filter_hash A number of criteria can be defined in key-value pairs.
93
+ # Only devices that equal all given criterions will be returned. If a criterion is
94
+ # not specified or its value is +nil+, any device will match that criterion.
95
+ # The following criteria can be filtered:
96
+ # * <tt>:idVendor</tt>, <tt>:idProduct</tt> (+FixNum+) for matching vendor/product ID,
97
+ # * <tt>:bClass</tt>, <tt>:bSubClass</tt>, <tt>:bProtocol</tt> (+FixNum+) for the device type -
98
+ # Devices using CLASS_PER_INTERFACE will match, if any of the interfaces match.
99
+ # * <tt>:bcdUSB</tt>, <tt>:bcdDevice</tt>, <tt>:bMaxPacketSize0</tt> (+FixNum+) for the
100
+ # USB and device release numbers.
101
+ # Criteria can also specified as Array of several alternative values.
102
+ #
103
+ # @example
104
+ # # Return all devices of vendor 0x0ab1 where idProduct is 3 or 4:
105
+ # context.device :idVendor=>0x0ab1, :idProduct=>[0x0003, 0x0004]
106
+ #
107
+ # @return [Array<LIBUSB::Device>]
108
+ def devices(filter_hash={})
109
+ device_list.select do |dev|
110
+ ( !filter_hash[:bClass] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
111
+ dev.settings.map(&:bInterfaceClass).&([filter_hash[:bClass]].flatten).any? :
112
+ [filter_hash[:bClass]].flatten.include?(dev.bDeviceClass))) &&
113
+ ( !filter_hash[:bSubClass] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
114
+ dev.settings.map(&:bInterfaceSubClass).&([filter_hash[:bSubClass]].flatten).any? :
115
+ [filter_hash[:bSubClass]].flatten.include?(dev.bDeviceSubClass))) &&
116
+ ( !filter_hash[:bProtocol] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
117
+ dev.settings.map(&:bInterfaceProtocol).&([filter_hash[:bProtocol]].flatten).any? :
118
+ [filter_hash[:bProtocol]].flatten.include?(dev.bDeviceProtocol))) &&
119
+ ( !filter_hash[:bMaxPacketSize0] || [filter_hash[:bMaxPacketSize0]].flatten.include?(dev.bMaxPacketSize0) ) &&
120
+ ( !filter_hash[:idVendor] || [filter_hash[:idVendor]].flatten.include?(dev.idVendor) ) &&
121
+ ( !filter_hash[:idProduct] || [filter_hash[:idProduct]].flatten.include?(dev.idProduct) ) &&
122
+ ( !filter_hash[:bcdUSB] || [filter_hash[:bcdUSB]].flatten.include?(dev.bcdUSB) ) &&
123
+ ( !filter_hash[:bcdDevice] || [filter_hash[:bcdDevice]].flatten.include?(dev.bcdDevice) )
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,399 @@
1
+ # This file is part of Libusb for Ruby.
2
+ #
3
+ # Libusb for Ruby is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # Libusb for Ruby is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public License
14
+ # along with Libusb for Ruby. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'libusb/call'
17
+
18
+ module LIBUSB
19
+ # Class representing a handle on a USB device.
20
+ #
21
+ # A device handle is used to perform I/O and other operations. When finished
22
+ # with a device handle, you should call DevHandle#close .
23
+ class DevHandle
24
+ # @private
25
+ attr_reader :pHandle
26
+ # @return [Device] the device this handle belongs to.
27
+ attr_reader :device
28
+
29
+ def initialize device, pHandle
30
+ @device = device
31
+ @pHandle = pHandle
32
+ @bulk_transfer = @control_transfer = @interrupt_transfer = nil
33
+ end
34
+
35
+ # Close a device handle.
36
+ #
37
+ # Should be called on all open handles before your application exits.
38
+ #
39
+ # Internally, this function destroys the reference that was added by {Device#open}
40
+ # on the given device.
41
+ #
42
+ # This is a non-blocking function; no requests are sent over the bus.
43
+ def close
44
+ Call.libusb_close(@pHandle)
45
+ end
46
+
47
+ def string_descriptor_ascii(index)
48
+ pString = FFI::MemoryPointer.new 0x100
49
+ res = Call.libusb_get_string_descriptor_ascii(@pHandle, index, pString, pString.size)
50
+ LIBUSB.raise_error res, "in libusb_get_string_descriptor_ascii" unless res>=0
51
+ pString.read_string(res)
52
+ end
53
+
54
+ # Claim an interface on a given device handle.
55
+ #
56
+ # You must claim the interface you wish to use before you can perform I/O on any
57
+ # of its endpoints.
58
+ #
59
+ # It is legal to attempt to claim an already-claimed interface, in which case
60
+ # libusb just returns without doing anything.
61
+ #
62
+ # Claiming of interfaces is a purely logical operation; it does not cause any
63
+ # requests to be sent over the bus. Interface claiming is used to instruct the
64
+ # underlying operating system that your application wishes to take ownership of
65
+ # the interface.
66
+ #
67
+ # This is a non-blocking function.
68
+ #
69
+ # If called with a block, the device handle is passed through to the block
70
+ # and the interface is released when the block has finished.
71
+ #
72
+ # @param [Interface, Fixnum] interface the interface or it's bInterfaceNumber you wish to claim
73
+ def claim_interface(interface)
74
+ interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
75
+ res = Call.libusb_claim_interface(@pHandle, interface)
76
+ LIBUSB.raise_error res, "in libusb_claim_interface" if res!=0
77
+ return self unless block_given?
78
+ begin
79
+ yield self
80
+ ensure
81
+ release_interface(interface)
82
+ end
83
+ end
84
+
85
+ # Release an interface previously claimed with {DevHandle#claim_interface}.
86
+ #
87
+ # You should release all claimed interfaces before closing a device handle.
88
+ #
89
+ # This is a blocking function. A SET_INTERFACE control request will be sent to the
90
+ # device, resetting interface state to the first alternate setting.
91
+ #
92
+ # @param [Interface, Fixnum] interface the interface or it's bInterfaceNumber you
93
+ # claimed previously
94
+ def release_interface(interface)
95
+ interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
96
+ res = Call.libusb_release_interface(@pHandle, interface)
97
+ LIBUSB.raise_error res, "in libusb_release_interface" if res!=0
98
+ end
99
+
100
+ # Set the active configuration for a device.
101
+ #
102
+ # The operating system may or may not have already set an active configuration on
103
+ # the device. It is up to your application to ensure the correct configuration is
104
+ # selected before you attempt to claim interfaces and perform other operations.
105
+ #
106
+ # If you call this function on a device already configured with the selected
107
+ # configuration, then this function will act as a lightweight device reset: it
108
+ # will issue a SET_CONFIGURATION request using the current configuration, causing
109
+ # most USB-related device state to be reset (altsetting reset to zero, endpoint
110
+ # halts cleared, toggles reset).
111
+ #
112
+ # You cannot change/reset configuration if your application has claimed interfaces -
113
+ # you should free them with {DevHandle#release_interface} first. You cannot
114
+ # change/reset configuration if other applications or drivers have claimed
115
+ # interfaces.
116
+ #
117
+ # A configuration value of +nil+ will put the device in unconfigured state. The USB
118
+ # specifications state that a configuration value of 0 does this, however buggy
119
+ # devices exist which actually have a configuration 0.
120
+ #
121
+ # You should always use this function rather than formulating your own
122
+ # SET_CONFIGURATION control request. This is because the underlying operating
123
+ # system needs to know when such changes happen.
124
+ #
125
+ # This is a blocking function.
126
+ #
127
+ # @param [Configuration, Fixnum] configuration the configuration or it's
128
+ # bConfigurationValue you wish to activate, or +nil+ if you wish to put
129
+ # the device in unconfigured state
130
+ def set_configuration(configuration)
131
+ configuration = configuration.bConfigurationValue if configuration.respond_to? :bConfigurationValue
132
+ res = Call.libusb_set_configuration(@pHandle, configuration || -1)
133
+ LIBUSB.raise_error res, "in libusb_set_configuration" if res!=0
134
+ end
135
+ alias configuration= set_configuration
136
+
137
+ # Activate an alternate setting for an interface.
138
+ #
139
+ # The interface must have been previously claimed with {DevHandle#claim_interface}.
140
+ #
141
+ # You should always use this function rather than formulating your own
142
+ # SET_INTERFACE control request. This is because the underlying operating system
143
+ # needs to know when such changes happen.
144
+ #
145
+ # This is a blocking function.
146
+ #
147
+ # @param [Setting, Fixnum] setting_or_interface_number the alternate setting
148
+ # to activate or the bInterfaceNumber of the previously-claimed interface
149
+ # @param [Fixnum, nil] alternate_setting the bAlternateSetting of the alternate setting to activate
150
+ # (only if first param is a Fixnum)
151
+ def set_interface_alt_setting(setting_or_interface_number, alternate_setting=nil)
152
+ alternate_setting ||= setting_or_interface_number.bAlternateSetting if setting_or_interface_number.respond_to? :bAlternateSetting
153
+ setting_or_interface_number = setting_or_interface_number.bInterfaceNumber if setting_or_interface_number.respond_to? :bInterfaceNumber
154
+ res = Call.libusb_set_interface_alt_setting(@pHandle, setting_or_interface_number, alternate_setting)
155
+ LIBUSB.raise_error res, "in libusb_set_interface_alt_setting" if res!=0
156
+ end
157
+
158
+ # Clear the halt/stall condition for an endpoint.
159
+ #
160
+ # Endpoints with halt status are unable to receive or transmit
161
+ # data until the halt condition is stalled.
162
+ #
163
+ # You should cancel all pending transfers before attempting to
164
+ # clear the halt condition.
165
+ #
166
+ # This is a blocking function.
167
+ #
168
+ # @param [Endpoint, Fixnum] endpoint the endpoint in question or it's bEndpointAddress
169
+ def clear_halt(endpoint)
170
+ endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
171
+ res = Call.libusb_clear_halt(@pHandle, endpoint)
172
+ LIBUSB.raise_error res, "in libusb_clear_halt" if res!=0
173
+ end
174
+
175
+ # Perform a USB port reset to reinitialize a device.
176
+ #
177
+ # The system will attempt to restore the previous configuration and
178
+ # alternate settings after the reset has completed.
179
+ #
180
+ # If the reset fails, the descriptors change, or the previous
181
+ # state cannot be restored, the device will appear to be disconnected
182
+ # and reconnected. This means that the device handle is no longer
183
+ # valid (you should close it) and rediscover the device. A Exception
184
+ # of LIBUSB::ERROR_NOT_FOUND indicates when this is the case.
185
+ #
186
+ # This is a blocking function which usually incurs a noticeable delay.
187
+ def reset_device
188
+ res = Call.libusb_reset_device(@pHandle)
189
+ LIBUSB.raise_error res, "in libusb_reset_device" if res!=0
190
+ end
191
+
192
+ # Determine if a kernel driver is active on an interface.
193
+ #
194
+ # If a kernel driver is active, you cannot claim the interface,
195
+ # and libusb will be unable to perform I/O.
196
+ #
197
+ # @param [Interface, Fixnum] interface the interface to check or it's bInterfaceNumber
198
+ # @return [Boolean]
199
+ def kernel_driver_active?(interface)
200
+ interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
201
+ res = Call.libusb_kernel_driver_active(@pHandle, interface)
202
+ LIBUSB.raise_error res, "in libusb_kernel_driver_active" unless res>=0
203
+ return res==1
204
+ end
205
+
206
+ # Detach a kernel driver from an interface.
207
+ #
208
+ # If successful, you will then be able to claim the interface and perform I/O.
209
+ #
210
+ # @param [Interface, Fixnum] interface the interface to detach the driver
211
+ # from or it's bInterfaceNumber
212
+ def detach_kernel_driver(interface)
213
+ interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
214
+ res = Call.libusb_detach_kernel_driver(@pHandle, interface)
215
+ LIBUSB.raise_error res, "in libusb_detach_kernel_driver" if res!=0
216
+ end
217
+
218
+ # Re-attach an interface's kernel driver, which was previously detached
219
+ # using {DevHandle#detach_kernel_driver}.
220
+ #
221
+ # @param [Interface, Fixnum] interface the interface to attach the driver to
222
+ def attach_kernel_driver(interface)
223
+ interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
224
+ res = Call.libusb_attach_kernel_driver(@pHandle, interface)
225
+ LIBUSB.raise_error res, "in libusb_attach_kernel_driver" if res!=0
226
+ end
227
+
228
+
229
+ # Perform a USB bulk transfer.
230
+ #
231
+ # The direction of the transfer is inferred from the direction bits of the
232
+ # endpoint address.
233
+ #
234
+ # For bulk reads, the +:dataIn+ param indicates the maximum length of data you are
235
+ # expecting to receive. If less data arrives than expected, this function will
236
+ # return that data.
237
+ #
238
+ # You should also check the returned number of bytes for bulk writes. Not all of the
239
+ # data may have been written.
240
+ #
241
+ # Also check transferred bytes when dealing with a timeout error code. libusb may have
242
+ # to split your transfer into a number of chunks to satisfy underlying O/S
243
+ # requirements, meaning that the timeout may expire after the first few chunks
244
+ # have completed. libusb is careful not to lose any data that may have been
245
+ # transferred; do not assume that timeout conditions indicate a complete lack of
246
+ # I/O.
247
+ #
248
+ # @param [Endpoint, Fixnum] :endpoint the (address of a) valid endpoint to communicate with
249
+ # @param [String] :dataOut the data to send with an outgoing transfer
250
+ # @param [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
251
+ # @param [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
252
+ # up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
253
+ #
254
+ # @return [Fixnum] Number of bytes sent for an outgoing transfer
255
+ # @return [String] Received data for an ingoing transfer
256
+ def bulk_transfer(args={})
257
+ timeout = args.delete(:timeout) || 1000
258
+ endpoint = args.delete(:endpoint) || raise(ArgumentError, "no endpoint given")
259
+ endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
260
+ if endpoint&ENDPOINT_IN != 0
261
+ dataIn = args.delete(:dataIn) || raise(ArgumentError, "no :dataIn given for bulk read")
262
+ else
263
+ dataOut = args.delete(:dataOut) || raise(ArgumentError, "no :dataOut given for bulk write")
264
+ end
265
+ raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
266
+
267
+ # reuse transfer struct to speed up transfer
268
+ @bulk_transfer ||= BulkTransfer.new :dev_handle => self
269
+ tr = @bulk_transfer
270
+ tr.endpoint = endpoint
271
+ tr.timeout = timeout
272
+ if dataOut
273
+ tr.buffer = dataOut
274
+ else
275
+ tr.alloc_buffer(dataIn)
276
+ end
277
+
278
+ tr.submit_and_wait!
279
+
280
+ if dataOut
281
+ tr.actual_length
282
+ else
283
+ tr.actual_buffer
284
+ end
285
+ end
286
+
287
+ # Perform a USB interrupt transfer.
288
+ #
289
+ # The direction of the transfer is inferred from the direction bits of the
290
+ # endpoint address.
291
+ #
292
+ # For interrupt reads, the +:dataIn+ param indicates the maximum length of data you
293
+ # are expecting to receive. If less data arrives than expected, this function will
294
+ # return that data.
295
+ #
296
+ # You should also check the returned number of bytes for interrupt writes. Not all of
297
+ # the data may have been written.
298
+ #
299
+ # Also check transferred when dealing with a timeout error code. libusb may have
300
+ # to split your transfer into a number of chunks to satisfy underlying O/S
301
+ # requirements, meaning that the timeout may expire after the first few chunks
302
+ # have completed. libusb is careful not to lose any data that may have been
303
+ # transferred; do not assume that timeout conditions indicate a complete lack of
304
+ # I/O.
305
+ #
306
+ # The default endpoint bInterval value is used as the polling interval.
307
+ #
308
+ # @param [Endpoint, Fixnum] :endpoint the (address of a) valid endpoint to communicate with
309
+ # @param [String] :dataOut the data to send with an outgoing transfer
310
+ # @param [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
311
+ # @param [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
312
+ # up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
313
+ #
314
+ # @return [Fixnum] Number of bytes sent for an outgoing transfer
315
+ # @return [String] Received data for an ingoing transfer
316
+ def interrupt_transfer(args={})
317
+ timeout = args.delete(:timeout) || 1000
318
+ endpoint = args.delete(:endpoint) || raise(ArgumentError, "no endpoint given")
319
+ endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
320
+ if endpoint&ENDPOINT_IN != 0
321
+ dataIn = args.delete(:dataIn) || raise(ArgumentError, "no :dataIn given for interrupt read")
322
+ else
323
+ dataOut = args.delete(:dataOut) || raise(ArgumentError, "no :dataOut given for interrupt write")
324
+ end
325
+ raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
326
+
327
+ # reuse transfer struct to speed up transfer
328
+ @interrupt_transfer ||= InterruptTransfer.new :dev_handle => self
329
+ tr = @interrupt_transfer
330
+ tr.endpoint = endpoint
331
+ tr.timeout = timeout
332
+ if dataOut
333
+ tr.buffer = dataOut
334
+ else
335
+ tr.alloc_buffer(dataIn)
336
+ end
337
+
338
+ tr.submit_and_wait!
339
+
340
+ if dataOut
341
+ tr.actual_length
342
+ else
343
+ tr.actual_buffer
344
+ end
345
+ end
346
+
347
+ # Perform a USB control transfer.
348
+ #
349
+ # The direction of the transfer is inferred from the +:bmRequestType+ field of the
350
+ # setup packet.
351
+ #
352
+ # @param [Fixnum] :bmRequestType the request type field for the setup packet
353
+ # @param [Fixnum] :bRequest the request field for the setup packet
354
+ # @param [Fixnum] :wValue the value field for the setup packet
355
+ # @param [Fixnum] :wIndex the index field for the setup packet
356
+ # @param [String] :dataOut the data to send with an outgoing transfer, it
357
+ # is appended to the setup packet
358
+ # @param [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
359
+ # (excluding setup packet)
360
+ # @param [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
361
+ # up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
362
+ #
363
+ # @return [Fixnum] Number of bytes sent (excluding setup packet) for outgoing transfer
364
+ # @return [String] Received data (without setup packet) for ingoing transfer
365
+ def control_transfer(args={})
366
+ bmRequestType = args.delete(:bmRequestType) || raise(ArgumentError, "param :bmRequestType not given")
367
+ bRequest = args.delete(:bRequest) || raise(ArgumentError, "param :bRequest not given")
368
+ wValue = args.delete(:wValue) || raise(ArgumentError, "param :wValue not given")
369
+ wIndex = args.delete(:wIndex) || raise(ArgumentError, "param :wIndex not given")
370
+ timeout = args.delete(:timeout) || 1000
371
+ if bmRequestType&ENDPOINT_IN != 0
372
+ dataIn = args.delete(:dataIn) || 0
373
+ dataOut = ''
374
+ else
375
+ dataOut = args.delete(:dataOut) || ''
376
+ end
377
+ raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
378
+
379
+ # reuse transfer struct to speed up transfer
380
+ @control_transfer ||= ControlTransfer.new :dev_handle => self
381
+ tr = @control_transfer
382
+ tr.timeout = timeout
383
+ if dataIn
384
+ setup_data = [bmRequestType, bRequest, wValue, wIndex, dataIn].pack('CCvvv')
385
+ tr.alloc_buffer( dataIn + CONTROL_SETUP_SIZE, setup_data )
386
+ else
387
+ tr.buffer = [bmRequestType, bRequest, wValue, wIndex, dataOut.bytesize, dataOut].pack('CCvvva*')
388
+ end
389
+
390
+ tr.submit_and_wait!
391
+
392
+ if dataIn
393
+ tr.actual_buffer(CONTROL_SETUP_SIZE)
394
+ else
395
+ tr.actual_length
396
+ end
397
+ end
398
+ end
399
+ end