libusb 0.1.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.
data/lib/libusb.rb ADDED
@@ -0,0 +1,1523 @@
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 'rubygems'
17
+ require 'ffi'
18
+
19
+
20
+ module LIBUSB
21
+ VERSION = "0.1.0"
22
+
23
+ module Call
24
+ extend FFI::Library
25
+ if RUBY_PLATFORM=~/mingw|mswin/i
26
+ bundled_dll = File.join(File.dirname(__FILE__), 'libusb-1.0.dll')
27
+ ffi_lib(['libusb-1.0', bundled_dll])
28
+ else
29
+ ffi_lib 'libusb-1.0'
30
+ end
31
+
32
+ ClassCodes = enum :libusb_class_code, [
33
+ :CLASS_PER_INTERFACE, 0,
34
+ :CLASS_AUDIO, 1,
35
+ :CLASS_COMM, 2,
36
+ :CLASS_HID, 3,
37
+ :CLASS_PRINTER, 7,
38
+ :CLASS_PTP, 6,
39
+ :CLASS_MASS_STORAGE, 8,
40
+ :CLASS_HUB, 9,
41
+ :CLASS_DATA, 10,
42
+ :CLASS_WIRELESS, 0xe0,
43
+ :CLASS_APPLICATION, 0xfe,
44
+ :CLASS_VENDOR_SPEC, 0xff
45
+ ]
46
+
47
+ Errors = enum :libusb_error, [
48
+ :SUCCESS, 0,
49
+ :ERROR_IO, -1,
50
+ :ERROR_INVALID_PARAM, -2,
51
+ :ERROR_ACCESS, -3,
52
+ :ERROR_NO_DEVICE, -4,
53
+ :ERROR_NOT_FOUND, -5,
54
+ :ERROR_BUSY, -6,
55
+ :ERROR_TIMEOUT, -7,
56
+ :ERROR_OVERFLOW, -8,
57
+ :ERROR_PIPE, -9,
58
+ :ERROR_INTERRUPTED, -10,
59
+ :ERROR_NO_MEM, -11,
60
+ :ERROR_NOT_SUPPORTED, -12,
61
+ :ERROR_OTHER, -99,
62
+ ]
63
+
64
+ # Transfer status codes
65
+ TransferStatus = enum :libusb_transfer_status, [
66
+ :TRANSFER_COMPLETED,
67
+ :TRANSFER_ERROR,
68
+ :TRANSFER_TIMED_OUT,
69
+ :TRANSFER_CANCELLED,
70
+ :TRANSFER_STALL,
71
+ :TRANSFER_NO_DEVICE,
72
+ :TRANSFER_OVERFLOW,
73
+ ]
74
+
75
+ # libusb_transfer.flags values
76
+ TransferFlags = enum :libusb_transfer_flags, [
77
+ :TRANSFER_SHORT_NOT_OK, 1 << 0,
78
+ :TRANSFER_FREE_BUFFER, 1 << 1,
79
+ :TRANSFER_FREE_TRANSFER, 1 << 2,
80
+ ]
81
+
82
+ TransferTypes = enum :libusb_transfer_type, [
83
+ :TRANSFER_TYPE_CONTROL, 0,
84
+ :TRANSFER_TYPE_ISOCHRONOUS, 1,
85
+ :TRANSFER_TYPE_BULK, 2,
86
+ :TRANSFER_TYPE_INTERRUPT, 3,
87
+ ]
88
+
89
+ StandardRequests = enum :libusb_standard_request, [
90
+ :REQUEST_GET_STATUS, 0x00,
91
+ :REQUEST_CLEAR_FEATURE, 0x01,
92
+ :REQUEST_SET_FEATURE, 0x03,
93
+ :REQUEST_SET_ADDRESS, 0x05,
94
+ :REQUEST_GET_DESCRIPTOR, 0x06,
95
+ :REQUEST_SET_DESCRIPTOR, 0x07,
96
+ :REQUEST_GET_CONFIGURATION, 0x08,
97
+ :REQUEST_SET_CONFIGURATION, 0x09,
98
+ :REQUEST_GET_INTERFACE, 0x0A,
99
+ :REQUEST_SET_INTERFACE, 0x0B,
100
+ :REQUEST_SYNCH_FRAME, 0x0C,
101
+ ]
102
+
103
+ EndpointDirections = enum :libusb_endpoint_direction, [
104
+ :ENDPOINT_IN, 0x80,
105
+ :ENDPOINT_OUT, 0x00,
106
+ ]
107
+
108
+ DescriptorTypes = enum :libusb_descriptor_type, [
109
+ :DT_DEVICE, 0x01,
110
+ :DT_CONFIG, 0x02,
111
+ :DT_STRING, 0x03,
112
+ :DT_INTERFACE, 0x04,
113
+ :DT_ENDPOINT, 0x05,
114
+ :DT_HID, 0x21,
115
+ :DT_REPORT, 0x22,
116
+ :DT_PHYSICAL, 0x23,
117
+ :DT_HUB, 0x29,
118
+ ]
119
+
120
+ RequestTypes = enum :libusb_request_type, [
121
+ :REQUEST_TYPE_STANDARD, (0x00 << 5),
122
+ :REQUEST_TYPE_CLASS, (0x01 << 5),
123
+ :REQUEST_TYPE_VENDOR, (0x02 << 5),
124
+ :REQUEST_TYPE_RESERVED, (0x03 << 5),
125
+ ]
126
+
127
+ RequestRecipients = enum :libusb_request_recipient, [
128
+ :RECIPIENT_DEVICE, 0x00,
129
+ :RECIPIENT_INTERFACE, 0x01,
130
+ :RECIPIENT_ENDPOINT, 0x02,
131
+ :RECIPIENT_OTHER, 0x03,
132
+ ]
133
+
134
+ IsoSyncTypes = enum :libusb_iso_sync_type, [
135
+ :ISO_SYNC_TYPE_NONE, 0,
136
+ :ISO_SYNC_TYPE_ASYNC, 1,
137
+ :ISO_SYNC_TYPE_ADAPTIVE, 2,
138
+ :ISO_SYNC_TYPE_SYNC, 3,
139
+ ]
140
+
141
+
142
+ typedef :pointer, :libusb_context
143
+ typedef :pointer, :libusb_device_handle
144
+
145
+ attach_function 'libusb_init', [ :pointer ], :int
146
+ attach_function 'libusb_exit', [ :pointer ], :void
147
+ attach_function 'libusb_set_debug', [:pointer, :int], :void
148
+
149
+ attach_function 'libusb_get_device_list', [:pointer, :pointer], :size_t
150
+ attach_function 'libusb_free_device_list', [:pointer, :int], :void
151
+ attach_function 'libusb_ref_device', [:pointer], :pointer
152
+ attach_function 'libusb_unref_device', [:pointer], :void
153
+
154
+ attach_function 'libusb_get_device_descriptor', [:pointer, :pointer], :int
155
+ attach_function 'libusb_get_active_config_descriptor', [:pointer, :pointer], :int
156
+ attach_function 'libusb_get_config_descriptor', [:pointer, :uint8, :pointer], :int
157
+ attach_function 'libusb_get_config_descriptor_by_value', [:pointer, :uint8, :pointer], :int
158
+ attach_function 'libusb_free_config_descriptor', [:pointer], :void
159
+ attach_function 'libusb_get_bus_number', [:pointer], :uint8
160
+ attach_function 'libusb_get_device_address', [:pointer], :uint8
161
+ attach_function 'libusb_get_max_packet_size', [:pointer, :uint8], :int
162
+ attach_function 'libusb_get_max_iso_packet_size', [:pointer, :uint8], :int
163
+
164
+ attach_function 'libusb_open', [:pointer, :pointer], :int
165
+ attach_function 'libusb_close', [:pointer], :void
166
+ attach_function 'libusb_get_device', [:libusb_device_handle], :pointer
167
+
168
+ attach_function 'libusb_set_configuration', [:libusb_device_handle, :int], :int
169
+ attach_function 'libusb_claim_interface', [:libusb_device_handle, :int], :int
170
+ attach_function 'libusb_release_interface', [:libusb_device_handle, :int], :int
171
+
172
+ attach_function 'libusb_open_device_with_vid_pid', [:pointer, :int, :int], :pointer
173
+
174
+ attach_function 'libusb_set_interface_alt_setting', [:libusb_device_handle, :int, :int], :int
175
+ attach_function 'libusb_clear_halt', [:libusb_device_handle, :int], :int
176
+ attach_function 'libusb_reset_device', [:libusb_device_handle], :int
177
+
178
+ attach_function 'libusb_kernel_driver_active', [:libusb_device_handle, :int], :int
179
+ attach_function 'libusb_detach_kernel_driver', [:libusb_device_handle, :int], :int
180
+ attach_function 'libusb_attach_kernel_driver', [:libusb_device_handle, :int], :int
181
+
182
+ attach_function 'libusb_get_string_descriptor_ascii', [:pointer, :uint8, :pointer, :int], :int
183
+
184
+ attach_function 'libusb_alloc_transfer', [:int], :pointer
185
+ attach_function 'libusb_submit_transfer', [:pointer], :int
186
+ attach_function 'libusb_cancel_transfer', [:pointer], :int
187
+ attach_function 'libusb_free_transfer', [:pointer], :void
188
+
189
+ attach_function 'libusb_handle_events', [:libusb_context], :int, :blocking=>true
190
+
191
+
192
+ callback :libusb_transfer_cb_fn, [:pointer], :void
193
+
194
+ class IsoPacketDescriptor < FFI::Struct
195
+ layout :length, :uint,
196
+ :actual_length, :uint,
197
+ :status, :libusb_transfer_status
198
+ end
199
+
200
+ # Setup packet for control transfers.
201
+ class ControlSetup < FFI::Struct
202
+ layout :bmRequestType, :uint8,
203
+ :bRequest, :uint8,
204
+ :wValue, :uint16,
205
+ :wIndex, :uint16,
206
+ :wLength, :uint16
207
+ end
208
+
209
+ class Transfer < FFI::ManagedStruct
210
+ layout :dev_handle, :libusb_device_handle,
211
+ :flags, :uint8,
212
+ :endpoint, :uchar,
213
+ :type, :uchar,
214
+ :timeout, :uint,
215
+ :status, :libusb_transfer_status,
216
+ :length, :int,
217
+ :actual_length, :int,
218
+ :callback, :libusb_transfer_cb_fn,
219
+ :user_data, :pointer,
220
+ :buffer, :pointer,
221
+ :num_iso_packets, :int
222
+
223
+ def self.release(ptr)
224
+ Call.libusb_free_transfer(ptr)
225
+ end
226
+ end
227
+ end
228
+
229
+ Call::ClassCodes.to_h.each{|k,v| const_set(k,v) }
230
+ Call::TransferTypes.to_h.each{|k,v| const_set(k,v) }
231
+ Call::StandardRequests.to_h.each{|k,v| const_set(k,v) }
232
+ Call::RequestTypes.to_h.each{|k,v| const_set(k,v) }
233
+ Call::DescriptorTypes.to_h.each{|k,v| const_set(k,v) }
234
+ Call::EndpointDirections.to_h.each{|k,v| const_set(k,v) }
235
+ Call::RequestRecipients.to_h.each{|k,v| const_set(k,v) }
236
+ Call::IsoSyncTypes.to_h.each{|k,v| const_set(k,v) }
237
+
238
+ class Error < RuntimeError
239
+ end
240
+ ErrorClassForResult = {}
241
+
242
+ # define an exception class for each error code
243
+ Call::Errors.to_h.each do |k,v|
244
+ klass = Class.new(Error)
245
+ klass.send(:define_method, :code){ v }
246
+ const_set(k, klass)
247
+ ErrorClassForResult[v] = klass
248
+ end
249
+
250
+ def self.raise_error(res, text)
251
+ klass = ErrorClassForResult[res]
252
+ raise klass, "#{klass} #{text}"
253
+ end
254
+
255
+ CONTROL_SETUP_SIZE = 8
256
+ DT_DEVICE_SIZE = 18
257
+ DT_CONFIG_SIZE = 9
258
+ DT_INTERFACE_SIZE = 9
259
+ DT_ENDPOINT_SIZE = 7
260
+ DT_ENDPOINT_AUDIO_SIZE = 9 # Audio extension
261
+ DT_HUB_NONVAR_SIZE = 7
262
+
263
+ ENDPOINT_ADDRESS_MASK = 0x0f # in bEndpointAddress
264
+ ENDPOINT_DIR_MASK = 0x80
265
+ TRANSFER_TYPE_MASK = 0x03 # in bmAttributes
266
+ ISO_SYNC_TYPE_MASK = 0x0C
267
+ ISO_USAGE_TYPE_MASK = 0x30
268
+
269
+
270
+ # :stopdoc:
271
+ # http://www.usb.org/developers/defined_class
272
+ CLASS_CODES = [
273
+ [0x01, nil, nil, "Audio"],
274
+ [0x02, nil, nil, "Comm"],
275
+ [0x03, nil, nil, "HID"],
276
+ [0x05, nil, nil, "Physical"],
277
+ [0x06, 0x01, 0x01, "StillImaging"],
278
+ [0x06, nil, nil, "Image"],
279
+ [0x07, nil, nil, "Printer"],
280
+ [0x08, 0x01, nil, "MassStorage RBC Bluk-Only"],
281
+ [0x08, 0x02, 0x50, "MassStorage ATAPI Bluk-Only"],
282
+ [0x08, 0x03, 0x50, "MassStorage QIC-157 Bluk-Only"],
283
+ [0x08, 0x04, nil, "MassStorage UFI"],
284
+ [0x08, 0x05, 0x50, "MassStorage SFF-8070i Bluk-Only"],
285
+ [0x08, 0x06, 0x50, "MassStorage SCSI Bluk-Only"],
286
+ [0x08, nil, nil, "MassStorage"],
287
+ [0x09, 0x00, 0x00, "Full speed Hub"],
288
+ [0x09, 0x00, 0x01, "Hi-speed Hub with single TT"],
289
+ [0x09, 0x00, 0x02, "Hi-speed Hub with multiple TTs"],
290
+ [0x09, nil, nil, "Hub"],
291
+ [0x0a, nil, nil, "CDC"],
292
+ [0x0b, nil, nil, "SmartCard"],
293
+ [0x0d, 0x00, 0x00, "ContentSecurity"],
294
+ [0x0e, nil, nil, "Video"],
295
+ [0xdc, 0x01, 0x01, "Diagnostic USB2"],
296
+ [0xdc, nil, nil, "Diagnostic"],
297
+ [0xe0, 0x01, 0x01, "Bluetooth"],
298
+ [0xe0, 0x01, 0x02, "UWB"],
299
+ [0xe0, 0x01, 0x03, "RemoteNDIS"],
300
+ [0xe0, 0x02, 0x01, "Host Wire Adapter Control/Data"],
301
+ [0xe0, 0x02, 0x02, "Device Wire Adapter Control/Data"],
302
+ [0xe0, 0x02, 0x03, "Device Wire Adapter Isochronous"],
303
+ [0xe0, nil, nil, "Wireless Controller"],
304
+ [0xef, 0x01, 0x01, "Active Sync"],
305
+ [0xef, 0x01, 0x02, "Palm Sync"],
306
+ [0xef, 0x02, 0x01, "Interface Association Descriptor"],
307
+ [0xef, 0x02, 0x02, "Wire Adapter Multifunction Peripheral"],
308
+ [0xef, 0x03, 0x01, "Cable Based Association Framework"],
309
+ [0xef, nil, nil, "Miscellaneous"],
310
+ [0xfe, 0x01, 0x01, "Device Firmware Upgrade"],
311
+ [0xfe, 0x02, 0x00, "IRDA Bridge"],
312
+ [0xfe, 0x03, 0x00, "USB Test and Measurement"],
313
+ [0xfe, 0x03, 0x01, "USB Test and Measurement (USBTMC USB488)"],
314
+ [0xfe, nil, nil, "Application Specific"],
315
+ [0xff, nil, nil, "Vendor specific"],
316
+ ]
317
+ CLASS_CODES_HASH1 = {}
318
+ CLASS_CODES_HASH2 = {}
319
+ CLASS_CODES_HASH3 = {}
320
+ CLASS_CODES.each {|base_class, sub_class, protocol, desc|
321
+ if protocol
322
+ CLASS_CODES_HASH3[[base_class, sub_class, protocol]] = desc
323
+ elsif sub_class
324
+ CLASS_CODES_HASH2[[base_class, sub_class]] = desc
325
+ else
326
+ CLASS_CODES_HASH1[base_class] = desc
327
+ end
328
+ }
329
+
330
+ def self.dev_string(base_class, sub_class, protocol)
331
+ if desc = CLASS_CODES_HASH3[[base_class, sub_class, protocol]]
332
+ desc
333
+ elsif desc = CLASS_CODES_HASH2[[base_class, sub_class]]
334
+ desc + " (%02x)" % [protocol]
335
+ elsif desc = CLASS_CODES_HASH1[base_class]
336
+ desc + " (%02x,%02x)" % [sub_class, protocol]
337
+ else
338
+ "Unkonwn(%02x,%02x,%02x)" % [base_class, sub_class, protocol]
339
+ end
340
+ end
341
+ # :startdoc:
342
+
343
+
344
+ # Abstract base class for USB transfers. Use
345
+ # {ControlTransfer}, {BulkTransfer}, {InterruptTransfer}, {IsochronousTransfer}
346
+ # to do transfers.
347
+ class Transfer
348
+ def initialize(args={})
349
+ args.each{|k,v| send("#{k}=", v) }
350
+ @buffer = nil
351
+ end
352
+ private :initialize
353
+
354
+ # Set the handle for the device to communicate with.
355
+ def dev_handle=(dev)
356
+ @dev_handle = dev
357
+ @transfer[:dev_handle] = @dev_handle.pHandle
358
+ end
359
+
360
+ # Timeout for this transfer in millseconds.
361
+ #
362
+ # A value of 0 indicates no timeout.
363
+ def timeout=(value)
364
+ @transfer[:timeout] = value
365
+ end
366
+
367
+ # Set the address of a valid endpoint to communicate with.
368
+ def endpoint=(endpoint)
369
+ endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
370
+ @transfer[:endpoint] = endpoint
371
+ end
372
+
373
+ # Set output data that should be sent.
374
+ def buffer=(data)
375
+ if !@buffer || data.bytesize>@buffer.size
376
+ free_buffer
377
+ @buffer = FFI::MemoryPointer.new(data.bytesize, 1, false)
378
+ end
379
+ @buffer.put_bytes(0, data)
380
+ @transfer[:buffer] = @buffer
381
+ @transfer[:length] = data.bytesize
382
+ end
383
+
384
+ # Retrieve the current data buffer.
385
+ def buffer
386
+ @transfer[:buffer].read_string(@transfer[:length])
387
+ end
388
+
389
+ # Clear the current data buffer.
390
+ def free_buffer
391
+ if @buffer
392
+ @buffer.free
393
+ @buffer = nil
394
+ @transfer[:buffer] = nil
395
+ @transfer[:length] = 0
396
+ end
397
+ end
398
+
399
+ # Allocate +len+ bytes of data buffer for input transfer.
400
+ #
401
+ # @param [Fixnum] len Number of bytes to allocate
402
+ # @param [String, nil] data some data to initialize the buffer with
403
+ def alloc_buffer(len, data=nil)
404
+ if !@buffer || len>@buffer.size
405
+ free_buffer
406
+ @buffer = FFI::MemoryPointer.new(len, 1, false)
407
+ end
408
+ @buffer.put_bytes(0, data) if data
409
+ @transfer[:buffer] = @buffer
410
+ @transfer[:length] = len
411
+ end
412
+
413
+ # The number of bytes actually transferred.
414
+ def actual_length
415
+ @transfer[:actual_length]
416
+ end
417
+
418
+ # Retrieve the data actually transferred.
419
+ #
420
+ # @param [Fixnum] offset optional offset of the retrieved data in the buffer.
421
+ def actual_buffer(offset=0)
422
+ @transfer[:buffer].get_bytes(offset, @transfer[:actual_length])
423
+ end
424
+
425
+ # Set the block that will be invoked when the transfer completes,
426
+ # fails, or is cancelled.
427
+ #
428
+ # @param [Proc] proc The block that should be called
429
+ def callback=(proc)
430
+ # Save proc to instance variable so that GC doesn't free
431
+ # the proc object before the transfer.
432
+ @callback_proc = proc do |pTrans|
433
+ proc.call(self)
434
+ end
435
+ @transfer[:callback] = @callback_proc
436
+ end
437
+
438
+ # The status of the transfer.
439
+ #
440
+ # Only for use within transfer callback function or after the callback was called.
441
+ #
442
+ # If this is an isochronous transfer, this field may read :TRANSFER_COMPLETED even if there
443
+ # were errors in the frames. Use the status field in each packet to determine if
444
+ # errors occurred.
445
+ def status
446
+ @transfer[:status]
447
+ end
448
+
449
+ # Submit a transfer.
450
+ #
451
+ # This function will fire off the USB transfer and then return immediately.
452
+ # This method can be called with block. It is called when the transfer completes,
453
+ # fails, or is cancelled.
454
+ def submit!(&block)
455
+ self.callback = block if block_given?
456
+
457
+ # puts "submit transfer #{@transfer.inspect} buffer: #{@transfer[:buffer].inspect} length: #{@transfer[:length].inspect} status: #{@transfer[:status].inspect} callback: #{@transfer[:callback].inspect} dev_handle: #{@transfer[:dev_handle].inspect}"
458
+
459
+ res = Call.libusb_submit_transfer( @transfer )
460
+ LIBUSB.raise_error res, "in libusb_submit_transfer" if res!=0
461
+ end
462
+
463
+ # Asynchronously cancel a previously submitted transfer.
464
+ #
465
+ # This function returns immediately, but this does not indicate cancellation is
466
+ # complete. Your callback function will be invoked at some later time with a
467
+ # transfer status of :TRANSFER_CANCELLED.
468
+ def cancel!
469
+ res = Call.libusb_cancel_transfer( @transfer )
470
+ LIBUSB.raise_error res, "in libusb_cancel_transfer" if res!=0
471
+ end
472
+
473
+ TransferStatusToError = {
474
+ :TRANSFER_ERROR => LIBUSB::ERROR_IO,
475
+ :TRANSFER_TIMED_OUT => LIBUSB::ERROR_TIMEOUT,
476
+ :TRANSFER_CANCELLED => LIBUSB::ERROR_INTERRUPTED,
477
+ :TRANSFER_STALL => LIBUSB::ERROR_PIPE,
478
+ :TRANSFER_NO_DEVICE => LIBUSB::ERROR_NO_DEVICE,
479
+ :TRANSFER_OVERFLOW => LIBUSB::ERROR_OVERFLOW,
480
+ }
481
+
482
+ # Submit the transfer and wait until the transfer completes or fails.
483
+ #
484
+ # A proper {LIBUSB::Error} is raised, in case the transfer did not complete.
485
+ def submit_and_wait!
486
+ completed = false
487
+ submit! do |tr2|
488
+ completed = true
489
+ end
490
+
491
+ until completed
492
+ begin
493
+ @dev_handle.device.context.handle_events
494
+ rescue ERROR_INTERRUPTED
495
+ next
496
+ rescue LIBUSB::Error
497
+ cancel!
498
+ until completed
499
+ @dev_handle.device.context.handle_events
500
+ end
501
+ raise
502
+ end
503
+ end
504
+
505
+ raise( TransferStatusToError[status] || ERROR_OTHER, "error #{status}") unless status==:TRANSFER_COMPLETED
506
+ end
507
+ end
508
+
509
+ class BulkTransfer < Transfer
510
+ def initialize(args={})
511
+ @transfer = Call::Transfer.new Call.libusb_alloc_transfer(0)
512
+ @transfer[:type] = TRANSFER_TYPE_BULK
513
+ @transfer[:timeout] = 1000
514
+ super
515
+ end
516
+ end
517
+
518
+ class ControlTransfer < Transfer
519
+ def initialize(args={})
520
+ @transfer = Call::Transfer.new Call.libusb_alloc_transfer(0)
521
+ @transfer[:type] = TRANSFER_TYPE_CONTROL
522
+ @transfer[:timeout] = 1000
523
+ super
524
+ end
525
+ end
526
+
527
+ class InterruptTransfer < Transfer
528
+ def initialize(args={})
529
+ @transfer = Call::Transfer.new Call.libusb_alloc_transfer(0)
530
+ @transfer[:type] = TRANSFER_TYPE_INTERRUPT
531
+ @transfer[:timeout] = 1000
532
+ super
533
+ end
534
+ end
535
+
536
+ class IsoPacket
537
+ def initialize(ptr, pkg_nr)
538
+ @packet = Call::IsoPacketDescriptor.new ptr
539
+ @pkg_nr = pkg_nr
540
+ end
541
+
542
+ def status
543
+ @packet[:status]
544
+ end
545
+
546
+ def length
547
+ @packet[:length]
548
+ end
549
+ def length=(len)
550
+ @packet[:length] = len
551
+ end
552
+
553
+ def actual_length
554
+ @packet[:actual_length]
555
+ end
556
+ end
557
+
558
+ class IsochronousTransfer < Transfer
559
+ def initialize(num_packets, args={})
560
+ @ptr = Call.libusb_alloc_transfer(num_packets)
561
+ @transfer = Call::Transfer.new @ptr
562
+ @transfer[:type] = TRANSFER_TYPE_ISOCHRONOUS
563
+ @transfer[:timeout] = 1000
564
+ @transfer[:num_iso_packets] = num_packets
565
+ super(args)
566
+ end
567
+
568
+ def num_packets
569
+ @transfer[:num_iso_packets]
570
+ end
571
+ def num_packets=(number)
572
+ @transfer[:num_iso_packets] = number
573
+ end
574
+
575
+ def [](nr)
576
+ IsoPacket.new( @ptr + Call::Transfer.size + nr*Call::IsoPacketDescriptor.size, nr)
577
+ end
578
+
579
+ # Convenience function to set the length of all packets in an
580
+ # isochronous transfer, based on {IsochronousTransfer#num_packets}.
581
+ def packet_lengths=(len)
582
+ ptr = @ptr + Call::Transfer.size
583
+ num_packets.times do
584
+ ptr.write_uint(len)
585
+ ptr += Call::IsoPacketDescriptor.size
586
+ end
587
+ end
588
+
589
+ # The actual_length field of the transfer is meaningless and should not
590
+ # be examined; instead you must refer to the actual_length field of
591
+ # each individual packet.
592
+ private :actual_length, :actual_buffer
593
+ end
594
+
595
+ class DeviceDescriptor < FFI::Struct
596
+ include Comparable
597
+
598
+ layout :bLength, :uint8,
599
+ :bDescriptorType, :uint8,
600
+ :bcdUSB, :uint16,
601
+ :bDeviceClass, :uint8,
602
+ :bDeviceSubClass, :uint8,
603
+ :bDeviceProtocol, :uint8,
604
+ :bMaxPacketSize0, :uint8,
605
+ :idVendor, :uint16,
606
+ :idProduct, :uint16,
607
+ :bcdDevice, :uint16,
608
+ :iManufacturer, :uint8,
609
+ :iProduct, :uint8,
610
+ :iSerialNumber, :uint8,
611
+ :bNumConfigurations, :uint8
612
+ end
613
+
614
+ class Configuration < FFI::ManagedStruct
615
+ include Comparable
616
+
617
+ layout :bLength, :uint8,
618
+ :bDescriptorType, :uint8,
619
+ :wTotalLength, :uint16,
620
+ :bNumInterfaces, :uint8,
621
+ :bConfigurationValue, :uint8,
622
+ :iConfiguration, :uint8,
623
+ :bmAttributes, :uint8,
624
+ :maxPower, :uint8,
625
+ :interface, :pointer,
626
+ :extra, :pointer,
627
+ :extra_length, :int
628
+
629
+ members.each do |member|
630
+ define_method(member) do
631
+ self[member]
632
+ end
633
+ end
634
+
635
+ def initialize(device, *args)
636
+ @device = device
637
+ super(*args)
638
+ end
639
+
640
+ def self.release(ptr)
641
+ Call.libusb_free_config_descriptor(ptr)
642
+ end
643
+
644
+ # @return [Device] the device this configuration belongs to.
645
+ attr_reader :device
646
+
647
+ def interfaces
648
+ ifs = []
649
+ self[:bNumInterfaces].times do |i|
650
+ ifs << Interface.new(self, self[:interface] + i*Interface.size)
651
+ end
652
+ return ifs
653
+ end
654
+
655
+ def inspect
656
+ attrs = []
657
+ attrs << self.bConfigurationValue.to_s
658
+ bits = self.bmAttributes
659
+ attrs << "SelfPowered" if (bits & 0b1000000) != 0
660
+ attrs << "RemoteWakeup" if (bits & 0b100000) != 0
661
+ desc = self.description
662
+ attrs << desc if desc != '?'
663
+ "\#<#{self.class} #{attrs.join(' ')}>"
664
+ end
665
+
666
+ # Return name of the configuration as String.
667
+ def description
668
+ return @description if defined? @description
669
+ @description = device.try_string_descriptor_ascii(self.iConfiguration)
670
+ end
671
+
672
+ # Return all interface decriptions of the configuration as Array of InterfaceDescriptor s.
673
+ def settings() self.interfaces.map {|d| d.settings }.flatten end
674
+ # Return all endpoints of all interfaces of the configuration as Array of EndpointDescriptor s.
675
+ def endpoints() self.settings.map {|d| d.endpoints }.flatten end
676
+
677
+ def <=>(o)
678
+ t = device<=>o.device
679
+ t = bConfigurationValue<=>o.bConfigurationValue if t==0
680
+ t
681
+ end
682
+ end
683
+
684
+ class Interface < FFI::Struct
685
+ include Comparable
686
+
687
+ layout :altsetting, :pointer,
688
+ :num_altsetting, :int
689
+
690
+ def initialize(configuration, *args)
691
+ @configuration = configuration
692
+ super(*args)
693
+ end
694
+
695
+ # @return [Configuration] the configuration this interface belongs to.
696
+ attr_reader :configuration
697
+
698
+ def alt_settings
699
+ ifs = []
700
+ self[:num_altsetting].times do |i|
701
+ ifs << Setting.new(self, self[:altsetting] + i*Setting.size)
702
+ end
703
+ return ifs
704
+ end
705
+ alias settings alt_settings
706
+
707
+ # The Device the Interface belongs to.
708
+ def device() self.configuration.device end
709
+ # Return all endpoints of all alternative settings as Array of EndpointDescriptor s.
710
+ def endpoints() self.alt_settings.map {|d| d.endpoints }.flatten end
711
+
712
+ def <=>(o)
713
+ configuration<=>o.configuration
714
+ end
715
+ end
716
+
717
+ class Setting < FFI::Struct
718
+ include Comparable
719
+
720
+ layout :bLength, :uint8,
721
+ :bDescriptorType, :uint8,
722
+ :bInterfaceNumber, :uint8,
723
+ :bAlternateSetting, :uint8,
724
+ :bNumEndpoints, :uint8,
725
+ :bInterfaceClass, :uint8,
726
+ :bInterfaceSubClass, :uint8,
727
+ :bInterfaceProtocol, :uint8,
728
+ :iInterface, :uint8,
729
+ :endpoint, :pointer,
730
+ :extra, :pointer,
731
+ :extra_length, :int
732
+
733
+ members.each do |member|
734
+ define_method(member) do
735
+ self[member]
736
+ end
737
+ end
738
+
739
+ def initialize(interface, *args)
740
+ @interface = interface
741
+ super(*args)
742
+ end
743
+
744
+ # @return [Interface] the interface this setting belongs to.
745
+ attr_reader :interface
746
+
747
+ def endpoints
748
+ ifs = []
749
+ self[:bNumEndpoints].times do |i|
750
+ ifs << Endpoint.new(self, self[:endpoint] + i*Endpoint.size)
751
+ end
752
+ return ifs
753
+ end
754
+
755
+ def inspect
756
+ attrs = []
757
+ attrs << self.bAlternateSetting.to_s
758
+ devclass = LIBUSB.dev_string(self.bInterfaceClass, self.bInterfaceSubClass, self.bInterfaceProtocol)
759
+ attrs << devclass
760
+ desc = self.description
761
+ attrs << desc if desc != '?'
762
+ "\#<#{self.class} #{attrs.join(' ')}>"
763
+ end
764
+
765
+ # Return name of the Interface as String.
766
+ def description
767
+ return @description if defined? @description
768
+ @description = device.try_string_descriptor_ascii(self.iInterface)
769
+ end
770
+
771
+ # The Device the InterfaceDescriptor belongs to.
772
+ def device() self.interface.configuration.device end
773
+ # The ConfigDescriptor the InterfaceDescriptor belongs to.
774
+ def configuration() self.interface.configuration end
775
+
776
+ def <=>(o)
777
+ t = interface<=>o.interface
778
+ t = bInterfaceNumber<=>o.bInterfaceNumber if t==0
779
+ t = bAlternateSetting<=>o.bAlternateSetting if t==0
780
+ t
781
+ end
782
+ end
783
+
784
+ class Endpoint < FFI::Struct
785
+ include Comparable
786
+
787
+ layout :bLength, :uint8,
788
+ :bDescriptorType, :uint8,
789
+ :bEndpointAddress, :uint8,
790
+ :bmAttributes, :uint8,
791
+ :wMaxPacketSize, :uint16,
792
+ :bInterval, :uint8,
793
+ :bRefresh, :uint8,
794
+ :bSynchAddress, :uint8,
795
+ :extra, :pointer,
796
+ :extra_length, :int
797
+
798
+ members.each do |member|
799
+ define_method(member) do
800
+ self[member]
801
+ end
802
+ end
803
+
804
+ def initialize(setting, *args)
805
+ @setting = setting
806
+ super(*args)
807
+ end
808
+
809
+ # @return [Setting] the setting this endpoint belongs to.
810
+ attr_reader :setting
811
+
812
+ def inspect
813
+ endpoint_address = self.bEndpointAddress
814
+ num = endpoint_address & 0b00001111
815
+ inout = (endpoint_address & 0b10000000) == 0 ? "OUT" : "IN "
816
+ bits = self.bmAttributes
817
+ transfer_type = %w[Control Isochronous Bulk Interrupt][0b11 & bits]
818
+ type = [transfer_type]
819
+ if transfer_type == 'Isochronous'
820
+ synchronization_type = %w[NoSynchronization Asynchronous Adaptive Synchronous][(0b1100 & bits) >> 2]
821
+ usage_type = %w[Data Feedback ImplicitFeedback ?][(0b110000 & bits) >> 4]
822
+ type << synchronization_type << usage_type
823
+ end
824
+ "\#<#{self.class} #{num} #{inout} #{type.join(" ")}>"
825
+ end
826
+
827
+ # The Device the EndpointDescriptor belongs to.
828
+ def device() self.setting.interface.configuration.device end
829
+ # The ConfigDescriptor the EndpointDescriptor belongs to.
830
+ def configuration() self.setting.interface.configuration end
831
+ # The Interface the EndpointDescriptor belongs to.
832
+ def interface() self.setting.interface end
833
+
834
+ def <=>(o)
835
+ t = setting<=>o.setting
836
+ t = bEndpointAddress<=>o.bEndpointAddress if t==0
837
+ t
838
+ end
839
+ end
840
+
841
+
842
+ # Class representing a libusb session.
843
+ class Context
844
+ # Initialize libusb context.
845
+ def initialize
846
+ m = FFI::MemoryPointer.new :pointer
847
+ Call.libusb_init(m)
848
+ @ctx = m.read_pointer
849
+ end
850
+
851
+ # Deinitialize libusb.
852
+ #
853
+ # Should be called after closing all open devices and before your application terminates.
854
+ def exit
855
+ Call.libusb_exit(@ctx)
856
+ end
857
+
858
+ # Set message verbosity.
859
+ #
860
+ # * Level 0: no messages ever printed by the library (default)
861
+ # * Level 1: error messages are printed to stderr
862
+ # * Level 2: warning and error messages are printed to stderr
863
+ # * Level 3: informational messages are printed to stdout, warning and
864
+ # error messages are printed to stderr
865
+ #
866
+ # The default level is 0, which means no messages are ever printed. If you
867
+ # choose to increase the message verbosity level, ensure that your
868
+ # application does not close the stdout/stderr file descriptors.
869
+ #
870
+ # You are advised to set level 3. libusb is conservative with its message
871
+ # logging and most of the time, will only log messages that explain error
872
+ # conditions and other oddities. This will help you debug your software.
873
+ #
874
+ # If the LIBUSB_DEBUG environment variable was set when libusb was
875
+ # initialized, this method does nothing: the message verbosity is
876
+ # fixed to the value in the environment variable.
877
+ #
878
+ # If libusb was compiled without any message logging, this method
879
+ # does nothing: you'll never get any messages.
880
+ #
881
+ # If libusb was compiled with verbose debug message logging, this
882
+ # method does nothing: you'll always get messages from all levels.
883
+ #
884
+ # @param [Fixnum] level debug level to set
885
+ def debug=(level)
886
+ Call.libusb_set_debug(@ctx, level)
887
+ end
888
+
889
+ def device_list
890
+ pppDevs = FFI::MemoryPointer.new :pointer
891
+ size = Call.libusb_get_device_list(@ctx, pppDevs)
892
+ ppDevs = pppDevs.read_pointer
893
+ pDevs = []
894
+ size.times do |devi|
895
+ pDev = ppDevs.get_pointer(devi*FFI.type_size(:pointer))
896
+ pDevs << Device.new(self, pDev)
897
+ end
898
+ Call.libusb_free_device_list(ppDevs, 1)
899
+ pDevs
900
+ end
901
+ private :device_list
902
+
903
+ # Handle any pending events in blocking mode.
904
+ #
905
+ # This method must be called when libusb is running asynchronous transfers.
906
+ # This gives libusb the opportunity to reap pending transfers,
907
+ # invoke callbacks, etc.
908
+ def handle_events
909
+ res = Call.libusb_handle_events(@ctx)
910
+ LIBUSB.raise_error res, "in libusb_handle_events" if res<0
911
+ end
912
+
913
+ # Obtain a list of devices currently attached to the USB system, optionally matching certain criteria.
914
+ #
915
+ # @param [Hash] filter_hash A number of criteria can be defined in key-value pairs.
916
+ # Only devices that equal all given criterions will be returned. If a criterion is
917
+ # not specified or its value is +nil+, any device will match that criterion.
918
+ # The following criteria can be filtered:
919
+ # * <tt>:idVendor</tt>, <tt>:idProduct</tt> (+FixNum+) for matching vendor/product ID,
920
+ # * <tt>:bClass</tt>, <tt>:bSubClass</tt>, <tt>:bProtocol</tt> (+FixNum+) for the device type -
921
+ # Devices using CLASS_PER_INTERFACE will match, if any of the interfaces match.
922
+ # * <tt>:bcdUSB</tt>, <tt>:bcdDevice</tt>, <tt>:bMaxPacketSize0</tt> (+FixNum+) for the
923
+ # USB and device release numbers.
924
+ # Criteria can also specified as Array of several alternative values.
925
+ #
926
+ # @example
927
+ # # Return all devices of vendor 0x0ab1 where idProduct is 3 or 4:
928
+ # context.device :idVendor=>0x0ab1, :idProduct=>[0x0003, 0x0004]
929
+ #
930
+ # @return [Array<LIBUSB::Device>]
931
+ def devices(filter_hash={})
932
+ device_list.select do |dev|
933
+ ( !filter_hash[:bClass] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
934
+ dev.settings.map(&:bInterfaceClass).&([filter_hash[:bClass]].flatten).any? :
935
+ [filter_hash[:bClass]].flatten.include?(dev.bDeviceClass))) &&
936
+ ( !filter_hash[:bSubClass] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
937
+ dev.settings.map(&:bInterfaceSubClass).&([filter_hash[:bSubClass]].flatten).any? :
938
+ [filter_hash[:bSubClass]].flatten.include?(dev.bDeviceSubClass))) &&
939
+ ( !filter_hash[:bProtocol] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
940
+ dev.settings.map(&:bInterfaceProtocol).&([filter_hash[:bProtocol]].flatten).any? :
941
+ [filter_hash[:bProtocol]].flatten.include?(dev.bDeviceProtocol))) &&
942
+ ( !filter_hash[:bMaxPacketSize0] || [filter_hash[:bMaxPacketSize0]].flatten.include?(dev.bMaxPacketSize0) ) &&
943
+ ( !filter_hash[:idVendor] || [filter_hash[:idVendor]].flatten.include?(dev.idVendor) ) &&
944
+ ( !filter_hash[:idProduct] || [filter_hash[:idProduct]].flatten.include?(dev.idProduct) ) &&
945
+ ( !filter_hash[:bcdUSB] || [filter_hash[:bcdUSB]].flatten.include?(dev.bcdUSB) ) &&
946
+ ( !filter_hash[:bcdDevice] || [filter_hash[:bcdDevice]].flatten.include?(dev.bcdDevice) )
947
+ end
948
+ end
949
+ end
950
+
951
+ # Class representing a USB device detected on the system.
952
+ #
953
+ # Devices of the system can be obtained with {Context#devices} .
954
+ class Device
955
+ include Comparable
956
+
957
+ # @return [Context] the context this device belongs to.
958
+ attr_reader :context
959
+
960
+ def initialize context, pDev
961
+ @context = context
962
+ class << pDev
963
+ def unref_device(id)
964
+ Call.libusb_unref_device(self)
965
+ end
966
+ end
967
+ ObjectSpace.define_finalizer(self, pDev.method(:unref_device))
968
+ Call.libusb_ref_device(pDev)
969
+ @pDev = pDev
970
+
971
+ @pDevDesc = DeviceDescriptor.new
972
+ res = Call.libusb_get_device_descriptor(@pDev, @pDevDesc)
973
+ LIBUSB.raise_error res, "in libusb_get_device_descriptor" if res!=0
974
+ end
975
+
976
+ # Open a device and obtain a device handle.
977
+ #
978
+ # A handle allows you to perform I/O on the device in question.
979
+ # This is a non-blocking function; no requests are sent over the bus.
980
+ #
981
+ # If called with a block, the handle is passed to the block
982
+ # and is closed when the block has finished.
983
+ #
984
+ # You need proper access permissions on:
985
+ # * Linux: <tt>/dev/bus/usb/<bus>/<dev></tt>
986
+ #
987
+ # @return [DevHandle] Handle to the device.
988
+ def open
989
+ ppHandle = FFI::MemoryPointer.new :pointer
990
+ res = Call.libusb_open(@pDev, ppHandle)
991
+ LIBUSB.raise_error res, "in libusb_open" if res!=0
992
+ handle = DevHandle.new self, ppHandle.read_pointer
993
+ return handle unless block_given?
994
+ begin
995
+ yield handle
996
+ ensure
997
+ handle.close
998
+ end
999
+ end
1000
+
1001
+ # Get the number of the bus that a device is connected to.
1002
+ def bus_number
1003
+ Call.libusb_get_bus_number(@pDev)
1004
+ end
1005
+
1006
+ # Get the address of the device on the bus it is connected to.
1007
+ def device_address
1008
+ Call.libusb_get_device_address(@pDev)
1009
+ end
1010
+
1011
+ # Convenience function to retrieve the wMaxPacketSize value for a
1012
+ # particular endpoint in the active device configuration.
1013
+ #
1014
+ # @param [Endpoint, Fixnum] endpoint (address of) the endpoint in question
1015
+ # @return [Fixnum] the wMaxPacketSize value
1016
+ def max_packet_size(endpoint)
1017
+ endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
1018
+ res = Call.libusb_get_max_packet_size(@pDev, endpoint)
1019
+ LIBUSB.raise_error res, "in libusb_get_max_packet_size" unless res>=0
1020
+ res
1021
+ end
1022
+
1023
+ # Calculate the maximum packet size which a specific endpoint is capable is
1024
+ # sending or receiving in the duration of 1 microframe.
1025
+ #
1026
+ # Only the active configution is examined. The calculation is based on the
1027
+ # wMaxPacketSize field in the endpoint descriptor as described in section 9.6.6
1028
+ # in the USB 2.0 specifications.
1029
+ #
1030
+ # If acting on an isochronous or interrupt endpoint, this function will
1031
+ # multiply the value found in bits 0:10 by the number of transactions per
1032
+ # microframe (determined by bits 11:12). Otherwise, this function just returns
1033
+ # the numeric value found in bits 0:10.
1034
+ #
1035
+ # This function is useful for setting up isochronous transfers, for example
1036
+ # you might use the return value from this function to call
1037
+ # IsoPacket#alloc_buffer in order to set the length field
1038
+ # of an isochronous packet in a transfer.
1039
+ #
1040
+ # @param [Endpoint, Fixnum] endpoint (address of) the endpoint in question
1041
+ # @return [Fixnum] the maximum packet size which can be sent/received on this endpoint
1042
+ def max_iso_packet_size(endpoint)
1043
+ endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
1044
+ res = Call.libusb_get_max_iso_packet_size(@pDev, endpoint)
1045
+ LIBUSB.raise_error res, "in libusb_get_max_iso_packet_size" unless res>=0
1046
+ res
1047
+ end
1048
+
1049
+ # Obtain a config descriptor of the device.
1050
+ #
1051
+ # @param [Fixnum] index number of the config descriptor
1052
+ # @return Configuration
1053
+ def config_descriptor(index)
1054
+ ppConfig = FFI::MemoryPointer.new :pointer
1055
+ res = Call.libusb_get_config_descriptor(@pDev, index, ppConfig)
1056
+ LIBUSB.raise_error res, "in libusb_get_config_descriptor" if res!=0
1057
+ pConfig = ppConfig.read_pointer
1058
+ config = Configuration.new(self, pConfig)
1059
+ config
1060
+ end
1061
+
1062
+ # allow access to Descriptor members on Device
1063
+ DeviceDescriptor.members.each do |member|
1064
+ define_method(member) do
1065
+ @pDevDesc[member]
1066
+ end
1067
+ end
1068
+
1069
+ def inspect
1070
+ attrs = []
1071
+ attrs << "#{self.bus_number}/#{self.device_address}"
1072
+ attrs << ("%04x:%04x" % [self.idVendor, self.idProduct])
1073
+ attrs << self.manufacturer
1074
+ attrs << self.product
1075
+ attrs << self.serial_number
1076
+ if self.bDeviceClass == LIBUSB::CLASS_PER_INTERFACE
1077
+ devclass = self.settings.map {|i|
1078
+ LIBUSB.dev_string(i.bInterfaceClass, i.bInterfaceSubClass, i.bInterfaceProtocol)
1079
+ }.join(", ")
1080
+ else
1081
+ devclass = LIBUSB.dev_string(self.bDeviceClass, self.bDeviceSubClass, self.bDeviceProtocol)
1082
+ end
1083
+ attrs << "(#{devclass})"
1084
+ attrs.compact!
1085
+ "\#<#{self.class} #{attrs.join(' ')}>"
1086
+ end
1087
+
1088
+ def try_string_descriptor_ascii(i)
1089
+ begin
1090
+ open{|h| h.string_descriptor_ascii(i) }
1091
+ rescue
1092
+ "?"
1093
+ end
1094
+ end
1095
+
1096
+ # Return manufacturer of the device
1097
+ # @return String
1098
+ def manufacturer
1099
+ return @manufacturer if defined? @manufacturer
1100
+ @manufacturer = try_string_descriptor_ascii(self.iManufacturer)
1101
+ @manufacturer.strip! if @manufacturer
1102
+ @manufacturer
1103
+ end
1104
+
1105
+ # Return product name of the device.
1106
+ # @return String
1107
+ def product
1108
+ return @product if defined? @product
1109
+ @product = try_string_descriptor_ascii(self.iProduct)
1110
+ @product.strip! if @product
1111
+ @product
1112
+ end
1113
+
1114
+ # Return serial number of the device.
1115
+ # @return String
1116
+ def serial_number
1117
+ return @serial_number if defined? @serial_number
1118
+ @serial_number = try_string_descriptor_ascii(self.iSerialNumber)
1119
+ @serial_number.strip! if @serial_number
1120
+ @serial_number
1121
+ end
1122
+
1123
+ # Return configurations of the device.
1124
+ # @return [Array<Configuration>]
1125
+ def configurations
1126
+ configs = []
1127
+ bNumConfigurations.times do |config_index|
1128
+ begin
1129
+ configs << config_descriptor(config_index)
1130
+ rescue RuntimeError
1131
+ # On Windows some devices don't return it's configuration.
1132
+ end
1133
+ end
1134
+ configs
1135
+ end
1136
+
1137
+ # Return all interfaces of the device.
1138
+ # @return [Array<Interface>]
1139
+ def interfaces() self.configurations.map {|d| d.interfaces }.flatten end
1140
+ # Return all interface decriptions of the device.
1141
+ # @return [Array<Setting>]
1142
+ def settings() self.interfaces.map {|d| d.settings }.flatten end
1143
+ # Return all endpoints of all interfaces of the device.
1144
+ # @return [Array<Endpoint>]
1145
+ def endpoints() self.settings.map {|d| d.endpoints }.flatten end
1146
+
1147
+ def <=>(o)
1148
+ t = bus_number<=>o.bus_number
1149
+ t = device_address<=>o.device_address if t==0
1150
+ t
1151
+ end
1152
+ end
1153
+
1154
+ # Class representing a handle on a USB device.
1155
+ #
1156
+ # A device handle is used to perform I/O and other operations. When finished
1157
+ # with a device handle, you should call DevHandle#close .
1158
+ class DevHandle
1159
+ # @private
1160
+ attr_reader :pHandle
1161
+ # @return [Device] the device this handle belongs to.
1162
+ attr_reader :device
1163
+
1164
+ def initialize device, pHandle
1165
+ @device = device
1166
+ @pHandle = pHandle
1167
+ @bulk_transfer = @control_transfer = @interrupt_transfer = nil
1168
+ end
1169
+
1170
+ # Close a device handle.
1171
+ #
1172
+ # Should be called on all open handles before your application exits.
1173
+ #
1174
+ # Internally, this function destroys the reference that was added by {Device#open}
1175
+ # on the given device.
1176
+ #
1177
+ # This is a non-blocking function; no requests are sent over the bus.
1178
+ def close
1179
+ Call.libusb_close(@pHandle)
1180
+ end
1181
+
1182
+ def string_descriptor_ascii(index)
1183
+ pString = FFI::MemoryPointer.new 0x100
1184
+ res = Call.libusb_get_string_descriptor_ascii(@pHandle, index, pString, pString.size)
1185
+ LIBUSB.raise_error res, "in libusb_get_string_descriptor_ascii" unless res>=0
1186
+ pString.read_string(res)
1187
+ end
1188
+
1189
+ # Claim an interface on a given device handle.
1190
+ #
1191
+ # You must claim the interface you wish to use before you can perform I/O on any
1192
+ # of its endpoints.
1193
+ #
1194
+ # It is legal to attempt to claim an already-claimed interface, in which case
1195
+ # libusb just returns without doing anything.
1196
+ #
1197
+ # Claiming of interfaces is a purely logical operation; it does not cause any
1198
+ # requests to be sent over the bus. Interface claiming is used to instruct the
1199
+ # underlying operating system that your application wishes to take ownership of
1200
+ # the interface.
1201
+ #
1202
+ # This is a non-blocking function.
1203
+ #
1204
+ # @param [Interface, Fixnum] interface the interface or it's bInterfaceNumber you wish to claim
1205
+ def claim_interface(interface)
1206
+ interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
1207
+ res = Call.libusb_claim_interface(@pHandle, interface)
1208
+ LIBUSB.raise_error res, "in libusb_claim_interface" if res!=0
1209
+ end
1210
+
1211
+ # Release an interface previously claimed with {DevHandle#claim_interface}.
1212
+ #
1213
+ # You should release all claimed interfaces before closing a device handle.
1214
+ #
1215
+ # This is a blocking function. A SET_INTERFACE control request will be sent to the
1216
+ # device, resetting interface state to the first alternate setting.
1217
+ #
1218
+ # @param [Interface, Fixnum] interface the interface or it's bInterfaceNumber you
1219
+ # claimed previously
1220
+ def release_interface(interface)
1221
+ interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
1222
+ res = Call.libusb_release_interface(@pHandle, interface)
1223
+ LIBUSB.raise_error res, "in libusb_release_interface" if res!=0
1224
+ end
1225
+
1226
+ # Set the active configuration for a device.
1227
+ #
1228
+ # The operating system may or may not have already set an active configuration on
1229
+ # the device. It is up to your application to ensure the correct configuration is
1230
+ # selected before you attempt to claim interfaces and perform other operations.
1231
+ #
1232
+ # If you call this function on a device already configured with the selected
1233
+ # configuration, then this function will act as a lightweight device reset: it
1234
+ # will issue a SET_CONFIGURATION request using the current configuration, causing
1235
+ # most USB-related device state to be reset (altsetting reset to zero, endpoint
1236
+ # halts cleared, toggles reset).
1237
+ #
1238
+ # You cannot change/reset configuration if your application has claimed interfaces -
1239
+ # you should free them with {DevHandle#release_interface} first. You cannot
1240
+ # change/reset configuration if other applications or drivers have claimed
1241
+ # interfaces.
1242
+ #
1243
+ # A configuration value of +nil+ will put the device in unconfigured state. The USB
1244
+ # specifications state that a configuration value of 0 does this, however buggy
1245
+ # devices exist which actually have a configuration 0.
1246
+ #
1247
+ # You should always use this function rather than formulating your own
1248
+ # SET_CONFIGURATION control request. This is because the underlying operating
1249
+ # system needs to know when such changes happen.
1250
+ #
1251
+ # This is a blocking function.
1252
+ #
1253
+ # @param [Configuration, Fixnum] configuration the configuration or it's
1254
+ # bConfigurationValue you wish to activate, or +nil+ if you wish to put
1255
+ # the device in unconfigured state
1256
+ def set_configuration(configuration)
1257
+ configuration = configuration.bConfigurationValue if configuration.respond_to? :bConfigurationValue
1258
+ res = Call.libusb_set_configuration(@pHandle, configuration || -1)
1259
+ LIBUSB.raise_error res, "in libusb_set_configuration" if res!=0
1260
+ end
1261
+ alias configuration= set_configuration
1262
+
1263
+ # Activate an alternate setting for an interface.
1264
+ #
1265
+ # The interface must have been previously claimed with {DevHandle#claim_interface}.
1266
+ #
1267
+ # You should always use this function rather than formulating your own
1268
+ # SET_INTERFACE control request. This is because the underlying operating system
1269
+ # needs to know when such changes happen.
1270
+ #
1271
+ # This is a blocking function.
1272
+ #
1273
+ # @param [Setting, Fixnum] setting_or_interface_number the alternate setting
1274
+ # to activate or the bInterfaceNumber of the previously-claimed interface
1275
+ # @param [Fixnum, nil] alternate_setting the bAlternateSetting of the alternate setting to activate
1276
+ # (only if first param is a Fixnum)
1277
+ def set_interface_alt_setting(setting_or_interface_number,
1278
+ alternate_setting=nil)
1279
+ alternate_setting ||= setting_or_interface_number.bAlternateSetting if setting_or_interface_number.respond_to? :bAlternateSetting
1280
+ setting_or_interface_number = setting_or_interface_number.bInterfaceNumber if setting_or_interface_number.respond_to? :bInterfaceNumber
1281
+ res = Call.libusb_set_interface_alt_setting(@pHandle, setting_or_interface_number, alternate_setting)
1282
+ LIBUSB.raise_error res, "in libusb_set_interface_alt_setting" if res!=0
1283
+ end
1284
+
1285
+ # Clear the halt/stall condition for an endpoint.
1286
+ #
1287
+ # Endpoints with halt status are unable to receive or transmit
1288
+ # data until the halt condition is stalled.
1289
+ #
1290
+ # You should cancel all pending transfers before attempting to
1291
+ # clear the halt condition.
1292
+ #
1293
+ # This is a blocking function.
1294
+ #
1295
+ # @param [Endpoint, Fixnum] endpoint the endpoint in question or it's bEndpointAddress
1296
+ def clear_halt(endpoint)
1297
+ endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
1298
+ res = Call.libusb_clear_halt(@pHandle, endpoint)
1299
+ LIBUSB.raise_error res, "in libusb_clear_halt" if res!=0
1300
+ end
1301
+
1302
+ # Perform a USB port reset to reinitialize a device.
1303
+ #
1304
+ # The system will attempt to restore the previous configuration and
1305
+ # alternate settings after the reset has completed.
1306
+ #
1307
+ # If the reset fails, the descriptors change, or the previous
1308
+ # state cannot be restored, the device will appear to be disconnected
1309
+ # and reconnected. This means that the device handle is no longer
1310
+ # valid (you should close it) and rediscover the device. A Exception
1311
+ # of LIBUSB::ERROR_NOT_FOUND indicates when this is the case.
1312
+ #
1313
+ # This is a blocking function which usually incurs a noticeable delay.
1314
+ def reset_device
1315
+ res = Call.libusb_reset_device(@pHandle)
1316
+ LIBUSB.raise_error res, "in libusb_reset_device" if res!=0
1317
+ end
1318
+
1319
+ # Determine if a kernel driver is active on an interface.
1320
+ #
1321
+ # If a kernel driver is active, you cannot claim the interface,
1322
+ # and libusb will be unable to perform I/O.
1323
+ #
1324
+ # @param [Interface, Fixnum] interface the interface to check or it's bInterfaceNumber
1325
+ # @return [Boolean]
1326
+ def kernel_driver_active?(interface)
1327
+ interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
1328
+ res = Call.libusb_kernel_driver_active(@pHandle, interface)
1329
+ LIBUSB.raise_error res, "in libusb_kernel_driver_active" unless res>=0
1330
+ return res==1
1331
+ end
1332
+
1333
+ # Detach a kernel driver from an interface.
1334
+ #
1335
+ # If successful, you will then be able to claim the interface and perform I/O.
1336
+ #
1337
+ # @param [Interface, Fixnum] interface the interface to detach the driver
1338
+ # from or it's bInterfaceNumber
1339
+ def detach_kernel_driver(interface)
1340
+ interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
1341
+ res = Call.libusb_detach_kernel_driver(@pHandle, interface)
1342
+ LIBUSB.raise_error res, "in libusb_detach_kernel_driver" if res!=0
1343
+ end
1344
+
1345
+ # Re-attach an interface's kernel driver, which was previously detached
1346
+ # using {DevHandle#detach_kernel_driver}.
1347
+ #
1348
+ # @param [Interface, Fixnum] interface the interface to attach the driver to
1349
+ def attach_kernel_driver(interface)
1350
+ interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
1351
+ res = Call.libusb_attach_kernel_driver(@pHandle, interface)
1352
+ LIBUSB.raise_error res, "in libusb_attach_kernel_driver" if res!=0
1353
+ end
1354
+
1355
+
1356
+ # Perform a USB bulk transfer.
1357
+ #
1358
+ # The direction of the transfer is inferred from the direction bits of the
1359
+ # endpoint address.
1360
+ #
1361
+ # For bulk reads, the :dataIn param indicates the maximum length of data you are
1362
+ # expecting to receive. If less data arrives than expected, this function will
1363
+ # return that data.
1364
+ #
1365
+ # You should also check the returned number of bytes for bulk writes. Not all of the
1366
+ # data may have been written.
1367
+ #
1368
+ # Also check transferred bytes when dealing with a timeout error code. libusb may have
1369
+ # to split your transfer into a number of chunks to satisfy underlying O/S
1370
+ # requirements, meaning that the timeout may expire after the first few chunks
1371
+ # have completed. libusb is careful not to lose any data that may have been
1372
+ # transferred; do not assume that timeout conditions indicate a complete lack of
1373
+ # I/O.
1374
+ #
1375
+ # @param [Endpoint, Fixnum] :endpoint the (address of a) valid endpoint to communicate with
1376
+ # @param [String] :dataOut the data to send with an outgoing transfer
1377
+ # @param [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
1378
+ #
1379
+ # @return [Fixnum] Number of bytes sent for an outgoing transfer
1380
+ # @return [String] Received data for an ingoing transfer
1381
+ def bulk_transfer(args={})
1382
+ timeout = args.delete(:timeout) || 1000
1383
+ endpoint = args.delete(:endpoint) || raise(ArgumentError, "no endpoint given")
1384
+ endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
1385
+ if endpoint&ENDPOINT_IN != 0
1386
+ dataIn = args.delete(:dataIn) || raise(ArgumentError, "no :dataIn given for bulk read")
1387
+ else
1388
+ dataOut = args.delete(:dataOut) || raise(ArgumentError, "no :dataOut given for bulk write")
1389
+ end
1390
+ raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
1391
+
1392
+ # reuse transfer struct to speed up transfer
1393
+ @bulk_transfer ||= BulkTransfer.new :dev_handle => self
1394
+ tr = @bulk_transfer
1395
+ tr.endpoint = endpoint
1396
+ tr.timeout = timeout
1397
+ if dataOut
1398
+ tr.buffer = dataOut
1399
+ else
1400
+ tr.alloc_buffer(dataIn)
1401
+ end
1402
+
1403
+ tr.submit_and_wait!
1404
+
1405
+ if dataOut
1406
+ tr.actual_length
1407
+ else
1408
+ tr.actual_buffer
1409
+ end
1410
+ end
1411
+
1412
+ # Perform a USB interrupt transfer.
1413
+ #
1414
+ # The direction of the transfer is inferred from the direction bits of the
1415
+ # endpoint address.
1416
+ #
1417
+ # For interrupt reads, the :dataIn param indicates the maximum length of data you
1418
+ # are expecting to receive. If less data arrives than expected, this function will
1419
+ # return that data.
1420
+ #
1421
+ # You should also check the returned number of bytes for interrupt writes. Not all of
1422
+ # the data may have been written.
1423
+ #
1424
+ # Also check transferred when dealing with a timeout error code. libusb may have
1425
+ # to split your transfer into a number of chunks to satisfy underlying O/S
1426
+ # requirements, meaning that the timeout may expire after the first few chunks
1427
+ # have completed. libusb is careful not to lose any data that may have been
1428
+ # transferred; do not assume that timeout conditions indicate a complete lack of
1429
+ # I/O.
1430
+ #
1431
+ # The default endpoint bInterval value is used as the polling interval.
1432
+ #
1433
+ # @param [Endpoint, Fixnum] :endpoint the (address of a) valid endpoint to communicate with
1434
+ # @param [String] :dataOut the data to send with an outgoing transfer
1435
+ # @param [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
1436
+ #
1437
+ # @return [Fixnum] Number of bytes sent for an outgoing transfer
1438
+ # @return [String] Received data for an ingoing transfer
1439
+ def interrupt_transfer(args={})
1440
+ timeout = args.delete(:timeout) || 1000
1441
+ endpoint = args.delete(:endpoint) || raise(ArgumentError, "no endpoint given")
1442
+ endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
1443
+ if endpoint&ENDPOINT_IN != 0
1444
+ dataIn = args.delete(:dataIn) || raise(ArgumentError, "no :dataIn given for interrupt read")
1445
+ else
1446
+ dataOut = args.delete(:dataOut) || raise(ArgumentError, "no :dataOut given for interrupt write")
1447
+ end
1448
+ raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
1449
+
1450
+ # reuse transfer struct to speed up transfer
1451
+ @interrupt_transfer ||= InterruptTransfer.new :dev_handle => self
1452
+ tr = @interrupt_transfer
1453
+ tr.endpoint = endpoint
1454
+ tr.timeout = timeout
1455
+ if dataOut
1456
+ tr.buffer = dataOut
1457
+ else
1458
+ tr.alloc_buffer(dataIn)
1459
+ end
1460
+
1461
+ tr.submit_and_wait!
1462
+
1463
+ if dataOut
1464
+ tr.actual_length
1465
+ else
1466
+ tr.actual_buffer
1467
+ end
1468
+ end
1469
+
1470
+ # Perform a USB control transfer.
1471
+ #
1472
+ # The direction of the transfer is inferred from the bmRequestType field of the
1473
+ # setup packet.
1474
+ #
1475
+ # @param [Fixnum] :bmRequestType the request type field for the setup packet
1476
+ # @param [Fixnum] :bRequest the request field for the setup packet
1477
+ # @param [Fixnum] :wValue the value field for the setup packet
1478
+ # @param [Fixnum] :wIndex the index field for the setup packet
1479
+ # @param [String] :dataOut the data to send with an outgoing transfer, it
1480
+ # is appended to the setup packet
1481
+ # @param [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
1482
+ # (excluding setup packet)
1483
+ # @param [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
1484
+ # up due to no response being received. For an unlimited timeout, use value 0.
1485
+ #
1486
+ # @return [Fixnum] Number of bytes sent (excluding setup packet) for outgoing transfer
1487
+ # @return [String] Received data (without setup packet) for ingoing transfer
1488
+ def control_transfer(args={})
1489
+ bmRequestType = args.delete(:bmRequestType) || raise(ArgumentError, "param :bmRequestType not given")
1490
+ bRequest = args.delete(:bRequest) || raise(ArgumentError, "param :bRequest not given")
1491
+ wValue = args.delete(:wValue) || raise(ArgumentError, "param :wValue not given")
1492
+ wIndex = args.delete(:wIndex) || raise(ArgumentError, "param :wIndex not given")
1493
+ timeout = args.delete(:timeout) || 1000
1494
+ if bmRequestType&ENDPOINT_IN != 0
1495
+ dataIn = args.delete(:dataIn) || 0
1496
+ dataOut = ''
1497
+ else
1498
+ dataOut = args.delete(:dataOut) || ''
1499
+ end
1500
+ raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
1501
+
1502
+ # reuse transfer struct to speed up transfer
1503
+ @control_transfer ||= ControlTransfer.new :dev_handle => self
1504
+ tr = @control_transfer
1505
+ tr.timeout = timeout
1506
+ if dataIn
1507
+ setup_data = [bmRequestType, bRequest, wValue, wIndex, dataIn].pack('CCvvv')
1508
+ tr.alloc_buffer( dataIn + CONTROL_SETUP_SIZE, setup_data )
1509
+ else
1510
+ tr.buffer = [bmRequestType, bRequest, wValue, wIndex, dataOut.bytesize, dataOut].pack('CCvvva*')
1511
+ end
1512
+
1513
+ tr.submit_and_wait!
1514
+
1515
+ if dataIn
1516
+ tr.actual_buffer(CONTROL_SETUP_SIZE)
1517
+ else
1518
+ tr.actual_length
1519
+ end
1520
+ end
1521
+ end
1522
+
1523
+ end