libusb 0.3.3-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,450 @@
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
+ # When called without a block, the transfer is done synchronously - so all events are handled
232
+ # internally and the sent/received data will be returned after completion or an exception will be raised.
233
+ #
234
+ # When called with a block, the method returns immediately after submitting the transfer.
235
+ # You then have to ensure, that {Context#handle_events} is called properly. As soon as the
236
+ # transfer is completed, the block is called with the sent/received data in case of success
237
+ # or the exception instance in case of failure.
238
+ #
239
+ # The direction of the transfer is inferred from the direction bits of the
240
+ # endpoint address.
241
+ #
242
+ # For bulk reads, the +:dataIn+ param indicates the maximum length of data you are
243
+ # expecting to receive. If less data arrives than expected, this function will
244
+ # return that data.
245
+ #
246
+ # You should check the returned number of bytes for bulk writes. Not all of the
247
+ # data may have been written.
248
+ #
249
+ # Also check {Error#transferred} when dealing with a timeout exception. libusb may have
250
+ # to split your transfer into a number of chunks to satisfy underlying O/S
251
+ # requirements, meaning that the timeout may expire after the first few chunks
252
+ # have completed. libusb is careful not to lose any data that may have been
253
+ # transferred; do not assume that timeout conditions indicate a complete lack of
254
+ # I/O.
255
+ #
256
+ # @param [Hash] args
257
+ # @option args [Endpoint, Fixnum] :endpoint the (address of a) valid endpoint to communicate with
258
+ # @option args [String] :dataOut the data to send with an outgoing transfer
259
+ # @option args [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
260
+ # @option args [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
261
+ # up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
262
+ #
263
+ # @return [Fixnum] Number of bytes sent for an outgoing transfer
264
+ # @return [String] Received data for an ingoing transfer
265
+ # @return [self] When called with a block
266
+ #
267
+ # @yieldparam [String, Integer, LIBUSB::Error] result result of the transfer is yielded to the block,
268
+ # when the asynchronous transfer has finished
269
+ # @raise [ArgumentError, LIBUSB::Error] in case of failure
270
+ def bulk_transfer(args={}, &block)
271
+ timeout = args.delete(:timeout) || 1000
272
+ endpoint = args.delete(:endpoint) || raise(ArgumentError, "no endpoint given")
273
+ endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
274
+ if endpoint&ENDPOINT_IN != 0
275
+ dataIn = args.delete(:dataIn) || raise(ArgumentError, "no :dataIn given for bulk read")
276
+ else
277
+ dataOut = args.delete(:dataOut) || raise(ArgumentError, "no :dataOut given for bulk write")
278
+ end
279
+ raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
280
+
281
+ # reuse transfer struct to speed up transfer
282
+ @bulk_transfer ||= BulkTransfer.new :dev_handle => self
283
+ tr = @bulk_transfer
284
+ tr.endpoint = endpoint
285
+ tr.timeout = timeout
286
+ if dataOut
287
+ tr.buffer = dataOut
288
+ else
289
+ tr.alloc_buffer(dataIn)
290
+ end
291
+
292
+ submit_transfer(tr, dataIn, 0, &block)
293
+ end
294
+
295
+ # Perform a USB interrupt transfer.
296
+ #
297
+ # When called without a block, the transfer is done synchronously - so all events are handled
298
+ # internally and the sent/received data will be returned after completion or an exception will be raised.
299
+ #
300
+ # When called with a block, the method returns immediately after submitting the transfer.
301
+ # You then have to ensure, that {Context#handle_events} is called properly. As soon as the
302
+ # transfer is completed, the block is called with the sent/received data in case of success
303
+ # or the exception instance in case of failure.
304
+ #
305
+ # The direction of the transfer is inferred from the direction bits of the
306
+ # endpoint address.
307
+ #
308
+ # For interrupt reads, the +:dataIn+ param indicates the maximum length of data you
309
+ # are expecting to receive. If less data arrives than expected, this function will
310
+ # return that data.
311
+ #
312
+ # You should check the returned number of bytes for interrupt writes. Not all of
313
+ # the data may have been written.
314
+ #
315
+ # Also check {Error#transferred} when dealing with a timeout exception. libusb may have
316
+ # to split your transfer into a number of chunks to satisfy underlying O/S
317
+ # requirements, meaning that the timeout may expire after the first few chunks
318
+ # have completed. libusb is careful not to lose any data that may have been
319
+ # transferred; do not assume that timeout conditions indicate a complete lack of
320
+ # I/O.
321
+ #
322
+ # The default endpoint bInterval value is used as the polling interval.
323
+ #
324
+ # @param [Hash] args
325
+ # @option args [Endpoint, Fixnum] :endpoint the (address of a) valid endpoint to communicate with
326
+ # @option args [String] :dataOut the data to send with an outgoing transfer
327
+ # @option args [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
328
+ # @option args [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
329
+ # up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
330
+ #
331
+ # @return [Fixnum] Number of bytes sent for an outgoing transfer
332
+ # @return [String] Received data for an ingoing transfer
333
+ # @return [self] When called with a block
334
+ #
335
+ # @yieldparam [String, Integer, LIBUSB::Error] result result of the transfer is yielded to the block,
336
+ # when the asynchronous transfer has finished
337
+ # @raise [ArgumentError, LIBUSB::Error] in case of failure
338
+ def interrupt_transfer(args={}, &block)
339
+ timeout = args.delete(:timeout) || 1000
340
+ endpoint = args.delete(:endpoint) || raise(ArgumentError, "no endpoint given")
341
+ endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
342
+ if endpoint&ENDPOINT_IN != 0
343
+ dataIn = args.delete(:dataIn) || raise(ArgumentError, "no :dataIn given for interrupt read")
344
+ else
345
+ dataOut = args.delete(:dataOut) || raise(ArgumentError, "no :dataOut given for interrupt write")
346
+ end
347
+ raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
348
+
349
+ # reuse transfer struct to speed up transfer
350
+ @interrupt_transfer ||= InterruptTransfer.new :dev_handle => self
351
+ tr = @interrupt_transfer
352
+ tr.endpoint = endpoint
353
+ tr.timeout = timeout
354
+ if dataOut
355
+ tr.buffer = dataOut
356
+ else
357
+ tr.alloc_buffer(dataIn)
358
+ end
359
+
360
+ submit_transfer(tr, dataIn, 0, &block)
361
+ end
362
+
363
+ # Perform a USB control transfer.
364
+ #
365
+ # When called without a block, the transfer is done synchronously - so all events are handled
366
+ # internally and the sent/received data will be returned after completion or an exception will be raised.
367
+ #
368
+ # When called with a block, the method returns immediately after submitting the transfer.
369
+ # You then have to ensure, that {Context#handle_events} is called properly. As soon as the
370
+ # transfer is completed, the block is called with the sent/received data in case of success
371
+ # or the exception instance in case of failure.
372
+ #
373
+ # The direction of the transfer is inferred from the +:bmRequestType+ field of the
374
+ # setup packet.
375
+ #
376
+ # @param [Hash] args
377
+ # @option args [Fixnum] :bmRequestType the request type field for the setup packet
378
+ # @option args [Fixnum] :bRequest the request field for the setup packet
379
+ # @option args [Fixnum] :wValue the value field for the setup packet
380
+ # @option args [Fixnum] :wIndex the index field for the setup packet
381
+ # @option args [String] :dataOut the data to send with an outgoing transfer, it
382
+ # is appended to the setup packet
383
+ # @option args [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
384
+ # (excluding setup packet)
385
+ # @option args [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
386
+ # up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
387
+ #
388
+ # @return [Fixnum] Number of bytes sent (excluding setup packet) for outgoing transfer
389
+ # @return [String] Received data (without setup packet) for ingoing transfer
390
+ # @return [self] When called with a block
391
+ #
392
+ # @yieldparam [String, Integer, LIBUSB::Error] result result of the transfer is yielded to the block,
393
+ # when the asynchronous transfer has finished
394
+ # @raise [ArgumentError, LIBUSB::Error] in case of failure
395
+ def control_transfer(args={}, &block)
396
+ bmRequestType = args.delete(:bmRequestType) || raise(ArgumentError, "param :bmRequestType not given")
397
+ bRequest = args.delete(:bRequest) || raise(ArgumentError, "param :bRequest not given")
398
+ wValue = args.delete(:wValue) || raise(ArgumentError, "param :wValue not given")
399
+ wIndex = args.delete(:wIndex) || raise(ArgumentError, "param :wIndex not given")
400
+ timeout = args.delete(:timeout) || 1000
401
+ if bmRequestType&ENDPOINT_IN != 0
402
+ dataIn = args.delete(:dataIn) || 0
403
+ dataOut = ''
404
+ else
405
+ dataOut = args.delete(:dataOut) || ''
406
+ end
407
+ raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
408
+
409
+ # reuse transfer struct to speed up transfer
410
+ @control_transfer ||= ControlTransfer.new :dev_handle => self
411
+ tr = @control_transfer
412
+ tr.timeout = timeout
413
+ if dataIn
414
+ setup_data = [bmRequestType, bRequest, wValue, wIndex, dataIn].pack('CCvvv')
415
+ tr.alloc_buffer( dataIn + CONTROL_SETUP_SIZE, setup_data )
416
+ else
417
+ tr.buffer = [bmRequestType, bRequest, wValue, wIndex, dataOut.bytesize, dataOut].pack('CCvvva*')
418
+ end
419
+
420
+ submit_transfer(tr, dataIn, CONTROL_SETUP_SIZE, &block)
421
+ end
422
+
423
+ private
424
+ def submit_transfer(tr, dataIn, offset)
425
+ if block_given?
426
+ tr.submit! do
427
+ res = dataIn ? tr.actual_buffer(offset) : tr.actual_length
428
+
429
+ if tr.status==:TRANSFER_COMPLETED
430
+ yield res
431
+ else
432
+ exception = Transfer::TransferStatusToError[tr.status] || ERROR_OTHER
433
+
434
+ yield exception.new("error #{tr.status}", res)
435
+ end
436
+ end
437
+ self
438
+ else
439
+ tr.submit_and_wait
440
+
441
+ res = dataIn ? tr.actual_buffer(offset) : tr.actual_length
442
+
443
+ unless tr.status==:TRANSFER_COMPLETED
444
+ raise((Transfer::TransferStatusToError[tr.status] || ERROR_OTHER).new("error #{tr.status}", res))
445
+ end
446
+ res
447
+ end
448
+ end
449
+ end
450
+ end
@@ -0,0 +1,359 @@
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 USB device detected on the system.
20
+ #
21
+ # Devices of the system can be obtained with {Context#devices} .
22
+ class Device
23
+ include Comparable
24
+
25
+ # @return [Context] the context this device belongs to.
26
+ attr_reader :context
27
+
28
+ def initialize context, pDev
29
+ @context = context
30
+ def pDev.unref_device(id)
31
+ Call.libusb_unref_device(self)
32
+ end
33
+ ObjectSpace.define_finalizer(self, pDev.method(:unref_device))
34
+ Call.libusb_ref_device(pDev)
35
+ @pDev = pDev
36
+
37
+ @pDevDesc = Call::DeviceDescriptor.new
38
+ res = Call.libusb_get_device_descriptor(@pDev, @pDevDesc)
39
+ LIBUSB.raise_error res, "in libusb_get_device_descriptor" if res!=0
40
+ end
41
+
42
+ # Open the device and obtain a device handle.
43
+ #
44
+ # A handle allows you to perform I/O on the device in question.
45
+ # This is a non-blocking function; no requests are sent over the bus.
46
+ #
47
+ # If called with a block, the handle is passed to the block
48
+ # and is closed when the block has finished.
49
+ #
50
+ # You need proper device access:
51
+ # * Linux: read+write permissions to <tt>/dev/bus/usb/<bus>/<dev></tt>
52
+ # * Windows: by installing a WinUSB-driver for the device (see {file:README.rdoc#Usage_on_Windows} )
53
+ #
54
+ # @return [DevHandle] Handle to the device.
55
+ def open
56
+ ppHandle = FFI::MemoryPointer.new :pointer
57
+ res = Call.libusb_open(@pDev, ppHandle)
58
+ LIBUSB.raise_error res, "in libusb_open" if res!=0
59
+ handle = DevHandle.new self, ppHandle.read_pointer
60
+ return handle unless block_given?
61
+ begin
62
+ yield handle
63
+ ensure
64
+ handle.close
65
+ end
66
+ end
67
+
68
+ # Open the device and claim an interface.
69
+ #
70
+ # This is a convenience method to {Device#open} and {DevHandle#claim_interface}.
71
+ # Must be called with a block. When the block has finished, the interface
72
+ # will be released and the device will be closed.
73
+ #
74
+ # @param [Interface, Fixnum] interface the interface or it's bInterfaceNumber you wish to claim
75
+ def open_interface(interface)
76
+ open do |dev|
77
+ dev.claim_interface(interface) do
78
+ yield dev
79
+ end
80
+ end
81
+ end
82
+
83
+ # Get the number of the bus that a device is connected to.
84
+ def bus_number
85
+ Call.libusb_get_bus_number(@pDev)
86
+ end
87
+
88
+ # Get the address of the device on the bus it is connected to.
89
+ def device_address
90
+ Call.libusb_get_device_address(@pDev)
91
+ end
92
+
93
+ if Call.respond_to?(:libusb_get_port_number)
94
+ # Get the number of the port that a device is connected to.
95
+ # Available since libusb-1.0.12.
96
+ #
97
+ # @return [Fixnum, nil] the port number (+nil+ if not available)
98
+ # @see #port_path
99
+ def port_number
100
+ r = Call.libusb_get_port_number(@pDev)
101
+ r==0 ? nil : r
102
+ end
103
+
104
+ # Get the the parent from the specified device [EXPERIMENTAL].
105
+ # Available since libusb-1.0.12.
106
+ #
107
+ # @return [Device, nil] the device parent or +nil+ if not available
108
+ # @see #port_path
109
+ def parent
110
+ pppDevs = FFI::MemoryPointer.new :pointer
111
+ Call.libusb_get_device_list(@context.instance_variable_get(:@ctx), pppDevs)
112
+ ppDevs = pppDevs.read_pointer
113
+ pParent = Call.libusb_get_parent(@pDev)
114
+ parent = pParent.null? ? nil : Device.new(@context, pParent)
115
+ Call.libusb_free_device_list(ppDevs, 1)
116
+ parent
117
+ end
118
+
119
+ # Get the list of all port numbers from root for the specified device.
120
+ # Available since libusb-1.0.12.
121
+ #
122
+ # @return [Array<Fixnum>]
123
+ # @see #parent
124
+ # @see #port_number
125
+ def port_path
126
+ # As per the USB 3.0 specs, the current maximum limit for the depth is 7.
127
+ path_len = 7
128
+ pPath = FFI::MemoryPointer.new :pointer, path_len
129
+ l = Call.libusb_get_port_path(@context.instance_variable_get(:@ctx), @pDev, pPath, path_len)
130
+ pPath.read_array_of_uint8(l)
131
+ end
132
+ end
133
+
134
+ if Call.respond_to?(:libusb_get_device_speed)
135
+ # Get the negotiated connection speed for a device.
136
+ # Available since libusb-1.0.9.
137
+ #
138
+ # @return [Symbol] a {Call::Speeds Speeds} symbol, where +:SPEED_UNKNOWN+ means that
139
+ # the OS doesn't know or doesn't support returning the negotiated speed.
140
+ def device_speed
141
+ Call.libusb_get_device_speed(@pDev)
142
+ end
143
+ end
144
+
145
+ # Convenience function to retrieve the wMaxPacketSize value for a
146
+ # particular endpoint in the active device configuration.
147
+ #
148
+ # @param [Endpoint, Fixnum] endpoint (address of) the endpoint in question
149
+ # @return [Fixnum] the wMaxPacketSize value
150
+ def max_packet_size(endpoint)
151
+ endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
152
+ res = Call.libusb_get_max_packet_size(@pDev, endpoint)
153
+ LIBUSB.raise_error res, "in libusb_get_max_packet_size" unless res>=0
154
+ res
155
+ end
156
+
157
+ # Calculate the maximum packet size which a specific endpoint is capable is
158
+ # sending or receiving in the duration of 1 microframe.
159
+ #
160
+ # Only the active configution is examined. The calculation is based on the
161
+ # wMaxPacketSize field in the endpoint descriptor as described in section 9.6.6
162
+ # in the USB 2.0 specifications.
163
+ #
164
+ # If acting on an isochronous or interrupt endpoint, this function will
165
+ # multiply the value found in bits 0:10 by the number of transactions per
166
+ # microframe (determined by bits 11:12). Otherwise, this function just returns
167
+ # the numeric value found in bits 0:10.
168
+ #
169
+ # This function is useful for setting up isochronous transfers, for example
170
+ # you might use the return value from this function to call
171
+ # IsoPacket#alloc_buffer in order to set the length field
172
+ # of an isochronous packet in a transfer.
173
+ #
174
+ # @param [Endpoint, Fixnum] endpoint (address of) the endpoint in question
175
+ # @return [Fixnum] the maximum packet size which can be sent/received on this endpoint
176
+ def max_iso_packet_size(endpoint)
177
+ endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
178
+ res = Call.libusb_get_max_iso_packet_size(@pDev, endpoint)
179
+ LIBUSB.raise_error res, "in libusb_get_max_iso_packet_size" unless res>=0
180
+ res
181
+ end
182
+
183
+ # Obtain a config descriptor of the device.
184
+ #
185
+ # @param [Fixnum] index number of the config descriptor
186
+ # @return Configuration
187
+ def config_descriptor(index)
188
+ ppConfig = FFI::MemoryPointer.new :pointer
189
+ res = Call.libusb_get_config_descriptor(@pDev, index, ppConfig)
190
+ LIBUSB.raise_error res, "in libusb_get_config_descriptor" if res!=0
191
+ pConfig = ppConfig.read_pointer
192
+ config = Configuration.new(self, pConfig)
193
+ config
194
+ end
195
+
196
+ # Size of the Descriptor in Bytes (18 bytes)
197
+ def bLength
198
+ @pDevDesc[:bLength]
199
+ end
200
+
201
+ # Device Descriptor (0x01)
202
+ def bDescriptorType
203
+ @pDevDesc[:bDescriptorType]
204
+ end
205
+
206
+ # USB specification release number which device complies too
207
+ #
208
+ # @return [Integer] in binary-coded decimal
209
+ def bcdUSB
210
+ @pDevDesc[:bcdUSB]
211
+ end
212
+
213
+ # USB-IF class code for the device (Assigned by USB Org)
214
+ #
215
+ # * If equal to 0x00, each interface specifies it's own class code
216
+ # * If equal to 0xFF, the class code is vendor specified
217
+ # * Otherwise field is valid Class Code
218
+ def bDeviceClass
219
+ @pDevDesc[:bDeviceClass]
220
+ end
221
+
222
+ # USB-IF subclass code for the device, qualified by the {Device#bDeviceClass}
223
+ # value (Assigned by USB Org)
224
+ def bDeviceSubClass
225
+ @pDevDesc[:bDeviceSubClass]
226
+ end
227
+
228
+ # USB-IF protocol code for the device, qualified by the {Device#bDeviceClass}
229
+ # and {Device#bDeviceSubClass} values (Assigned by USB Org)
230
+ def bDeviceProtocol
231
+ @pDevDesc[:bDeviceProtocol]
232
+ end
233
+
234
+ # Maximum Packet Size for Endpoint 0. Valid Sizes are 8, 16, 32, 64
235
+ def bMaxPacketSize0
236
+ @pDevDesc[:bMaxPacketSize0]
237
+ end
238
+
239
+ # USB-IF vendor ID (Assigned by USB Org)
240
+ def idVendor
241
+ @pDevDesc[:idVendor]
242
+ end
243
+
244
+ # USB-IF product ID (Assigned by Manufacturer)
245
+ def idProduct
246
+ @pDevDesc[:idProduct]
247
+ end
248
+
249
+ # Device release number in binary-coded decimal.
250
+ def bcdDevice
251
+ @pDevDesc[:bcdDevice]
252
+ end
253
+
254
+ # Index of string descriptor describing manufacturer.
255
+ def iManufacturer
256
+ @pDevDesc[:iManufacturer]
257
+ end
258
+
259
+ # Index of string descriptor describing product.
260
+ def iProduct
261
+ @pDevDesc[:iProduct]
262
+ end
263
+
264
+ # Index of string descriptor containing device serial number.
265
+ def iSerialNumber
266
+ @pDevDesc[:iSerialNumber]
267
+ end
268
+
269
+ # Number of Possible Configurations
270
+ def bNumConfigurations
271
+ @pDevDesc[:bNumConfigurations]
272
+ end
273
+
274
+
275
+ def inspect
276
+ attrs = []
277
+ attrs << "#{self.bus_number}/#{self.device_address}"
278
+ attrs << ("%04x:%04x" % [self.idVendor, self.idProduct])
279
+ attrs << self.manufacturer
280
+ attrs << self.product
281
+ attrs << self.serial_number
282
+ if self.bDeviceClass == LIBUSB::CLASS_PER_INTERFACE
283
+ devclass = self.settings.map {|i|
284
+ LIBUSB.dev_string(i.bInterfaceClass, i.bInterfaceSubClass, i.bInterfaceProtocol)
285
+ }.join(", ")
286
+ else
287
+ devclass = LIBUSB.dev_string(self.bDeviceClass, self.bDeviceSubClass, self.bDeviceProtocol)
288
+ end
289
+ attrs << "(#{devclass})"
290
+ attrs.compact!
291
+ "\#<#{self.class} #{attrs.join(' ')}>"
292
+ end
293
+
294
+ def try_string_descriptor_ascii(i)
295
+ begin
296
+ open{|h| h.string_descriptor_ascii(i) }
297
+ rescue
298
+ "?"
299
+ end
300
+ end
301
+
302
+ # Return manufacturer of the device
303
+ # @return String
304
+ def manufacturer
305
+ return @manufacturer if defined? @manufacturer
306
+ @manufacturer = try_string_descriptor_ascii(self.iManufacturer)
307
+ @manufacturer.strip! if @manufacturer
308
+ @manufacturer
309
+ end
310
+
311
+ # Return product name of the device.
312
+ # @return String
313
+ def product
314
+ return @product if defined? @product
315
+ @product = try_string_descriptor_ascii(self.iProduct)
316
+ @product.strip! if @product
317
+ @product
318
+ end
319
+
320
+ # Return serial number of the device.
321
+ # @return String
322
+ def serial_number
323
+ return @serial_number if defined? @serial_number
324
+ @serial_number = try_string_descriptor_ascii(self.iSerialNumber)
325
+ @serial_number.strip! if @serial_number
326
+ @serial_number
327
+ end
328
+
329
+ # Return configurations of the device.
330
+ # @return [Array<Configuration>]
331
+ def configurations
332
+ configs = []
333
+ bNumConfigurations.times do |config_index|
334
+ begin
335
+ configs << config_descriptor(config_index)
336
+ rescue RuntimeError
337
+ # On Windows some devices don't return it's configuration.
338
+ end
339
+ end
340
+ configs
341
+ end
342
+
343
+ # Return all interfaces of this device.
344
+ # @return [Array<Interface>]
345
+ def interfaces() self.configurations.map {|d| d.interfaces }.flatten end
346
+ # Return all interface decriptions of this device.
347
+ # @return [Array<Setting>]
348
+ def settings() self.interfaces.map {|d| d.settings }.flatten end
349
+ # Return all endpoints of all interfaces of this device.
350
+ # @return [Array<Endpoint>]
351
+ def endpoints() self.settings.map {|d| d.endpoints }.flatten end
352
+
353
+ def <=>(o)
354
+ t = bus_number<=>o.bus_number
355
+ t = device_address<=>o.device_address if t==0
356
+ t
357
+ end
358
+ end
359
+ end