libusb 0.1.3-x86-mingw32 → 0.2.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.
@@ -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