libusb 0.3.3-x64-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,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