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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.travis.yml +10 -0
- data/.yardopts +6 -0
- data/COPYING +165 -0
- data/Gemfile +16 -0
- data/History.md +77 -0
- data/README.md +144 -0
- data/Rakefile +185 -0
- data/lib/libusb.rb +51 -0
- data/lib/libusb/call.rb +316 -0
- data/lib/libusb/compat.rb +376 -0
- data/lib/libusb/configuration.rb +155 -0
- data/lib/libusb/constants.rb +151 -0
- data/lib/libusb/context.rb +305 -0
- data/lib/libusb/dev_handle.rb +450 -0
- data/lib/libusb/device.rb +359 -0
- data/lib/libusb/endpoint.rb +174 -0
- data/lib/libusb/eventmachine.rb +183 -0
- data/lib/libusb/interface.rb +60 -0
- data/lib/libusb/setting.rb +132 -0
- data/lib/libusb/transfer.rb +282 -0
- data/lib/libusb/version_gem.rb +19 -0
- data/lib/libusb/version_struct.rb +63 -0
- data/libusb.gemspec +31 -0
- data/test/test_libusb_capability.rb +23 -0
- data/test/test_libusb_compat.rb +78 -0
- data/test/test_libusb_compat_mass_storage.rb +81 -0
- data/test/test_libusb_descriptors.rb +181 -0
- data/test/test_libusb_event_machine.rb +118 -0
- data/test/test_libusb_gc.rb +37 -0
- data/test/test_libusb_iso_transfer.rb +50 -0
- data/test/test_libusb_mass_storage.rb +278 -0
- data/test/test_libusb_mass_storage2.rb +73 -0
- data/test/test_libusb_structs.rb +45 -0
- data/test/test_libusb_threads.rb +89 -0
- data/test/test_libusb_version.rb +40 -0
- metadata +126 -0
@@ -0,0 +1,155 @@
|
|
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 Configuration < FFI::ManagedStruct
|
20
|
+
include Comparable
|
21
|
+
|
22
|
+
layout :bLength, :uint8,
|
23
|
+
:bDescriptorType, :uint8,
|
24
|
+
:wTotalLength, :uint16,
|
25
|
+
:bNumInterfaces, :uint8,
|
26
|
+
:bConfigurationValue, :uint8,
|
27
|
+
:iConfiguration, :uint8,
|
28
|
+
:bmAttributes, :uint8,
|
29
|
+
:bMaxPower, :uint8,
|
30
|
+
:interface, :pointer,
|
31
|
+
:extra, :pointer,
|
32
|
+
:extra_length, :int
|
33
|
+
|
34
|
+
# Size of this descriptor (in bytes).
|
35
|
+
def bLength
|
36
|
+
self[:bLength]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Descriptor type (0x02)
|
40
|
+
def bDescriptorType
|
41
|
+
self[:bDescriptorType]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Total length of data returned for this configuration.
|
45
|
+
def wTotalLength
|
46
|
+
self[:wTotalLength]
|
47
|
+
end
|
48
|
+
|
49
|
+
# Number of interfaces supported by this configuration.
|
50
|
+
def bNumInterfaces
|
51
|
+
self[:bNumInterfaces]
|
52
|
+
end
|
53
|
+
|
54
|
+
# Identifier value for this configuration.
|
55
|
+
def bConfigurationValue
|
56
|
+
self[:bConfigurationValue]
|
57
|
+
end
|
58
|
+
|
59
|
+
# Index of string descriptor describing this configuration.
|
60
|
+
def iConfiguration
|
61
|
+
self[:iConfiguration]
|
62
|
+
end
|
63
|
+
|
64
|
+
# Configuration characteristics.
|
65
|
+
#
|
66
|
+
# * Bit 7: Reserved, set to 1. (USB 1.0 Bus Powered)
|
67
|
+
# * Bit 6: Self Powered
|
68
|
+
# * Bit 5: Remote Wakeup
|
69
|
+
# * Bit 4..0: Reserved, set to 0.
|
70
|
+
#
|
71
|
+
# @return [Integer]
|
72
|
+
#
|
73
|
+
# @see #self_powered?
|
74
|
+
# @see #remote_wakeup?
|
75
|
+
def bmAttributes
|
76
|
+
self[:bmAttributes]
|
77
|
+
end
|
78
|
+
|
79
|
+
# @return [Boolean]
|
80
|
+
def self_powered?
|
81
|
+
bmAttributes & 0b1000000 != 0
|
82
|
+
end
|
83
|
+
|
84
|
+
# @return [Boolean]
|
85
|
+
def remote_wakeup?
|
86
|
+
bmAttributes & 0b100000 != 0
|
87
|
+
end
|
88
|
+
|
89
|
+
# Maximum power consumption of the USB device from this bus in this configuration when the device is fully opreation.
|
90
|
+
#
|
91
|
+
# @return [Integer] Maximum Power Consumption in 2mA units
|
92
|
+
def bMaxPower
|
93
|
+
self[:bMaxPower]
|
94
|
+
end
|
95
|
+
|
96
|
+
# @deprecated Use {#bMaxPower} instead.
|
97
|
+
alias maxPower bMaxPower
|
98
|
+
|
99
|
+
|
100
|
+
# Extra descriptors.
|
101
|
+
#
|
102
|
+
# @return [String]
|
103
|
+
def extra
|
104
|
+
return if self[:extra].null?
|
105
|
+
self[:extra].read_string(self[:extra_length])
|
106
|
+
end
|
107
|
+
|
108
|
+
def initialize(device, *args)
|
109
|
+
@device = device
|
110
|
+
super(*args)
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.release(ptr)
|
114
|
+
Call.libusb_free_config_descriptor(ptr)
|
115
|
+
end
|
116
|
+
|
117
|
+
# @return [Device] the device this configuration belongs to.
|
118
|
+
attr_reader :device
|
119
|
+
|
120
|
+
def interfaces
|
121
|
+
ifs = []
|
122
|
+
self[:bNumInterfaces].times do |i|
|
123
|
+
ifs << Interface.new(self, self[:interface] + i*Interface.size)
|
124
|
+
end
|
125
|
+
return ifs
|
126
|
+
end
|
127
|
+
|
128
|
+
def inspect
|
129
|
+
attrs = []
|
130
|
+
attrs << self.bConfigurationValue.to_s
|
131
|
+
attrs << "SelfPowered" if self_powered?
|
132
|
+
attrs << "RemoteWakeup" if remote_wakeup?
|
133
|
+
desc = self.description
|
134
|
+
attrs << desc if desc != '?'
|
135
|
+
"\#<#{self.class} #{attrs.join(' ')}>"
|
136
|
+
end
|
137
|
+
|
138
|
+
# Return name of this configuration as String.
|
139
|
+
def description
|
140
|
+
return @description if defined? @description
|
141
|
+
@description = device.try_string_descriptor_ascii(self.iConfiguration)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Return all interface decriptions of the configuration as Array of {Setting}s.
|
145
|
+
def settings() self.interfaces.map {|d| d.settings }.flatten end
|
146
|
+
# Return all endpoints of all interfaces of the configuration as Array of {Endpoint}s.
|
147
|
+
def endpoints() self.settings.map {|d| d.endpoints }.flatten end
|
148
|
+
|
149
|
+
def <=>(o)
|
150
|
+
t = device<=>o.device
|
151
|
+
t = bConfigurationValue<=>o.bConfigurationValue if t==0
|
152
|
+
t
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,151 @@
|
|
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
|
+
Call::ClassCodes.to_h.each{|k,v| const_set(k,v) }
|
20
|
+
Call::TransferTypes.to_h.each{|k,v| const_set(k,v) }
|
21
|
+
Call::StandardRequests.to_h.each{|k,v| const_set(k,v) }
|
22
|
+
Call::RequestTypes.to_h.each{|k,v| const_set(k,v) }
|
23
|
+
Call::DescriptorTypes.to_h.each{|k,v| const_set(k,v) }
|
24
|
+
Call::EndpointDirections.to_h.each{|k,v| const_set(k,v) }
|
25
|
+
Call::RequestRecipients.to_h.each{|k,v| const_set(k,v) }
|
26
|
+
Call::IsoSyncTypes.to_h.each{|k,v| const_set(k,v) }
|
27
|
+
Call::Speeds.to_h.each{|k,v| const_set(k,v) }
|
28
|
+
Call::Capabilities.to_h.each{|k,v| const_set(k,v) }
|
29
|
+
|
30
|
+
# Base class of libusb errors
|
31
|
+
class Error < RuntimeError
|
32
|
+
# The data already transferred before the exception was raised
|
33
|
+
# @return [Fixnum] Number of bytes sent for an outgoing transfer
|
34
|
+
# @return [String] Received data for an ingoing transfer
|
35
|
+
attr_reader :transferred
|
36
|
+
|
37
|
+
def initialize(msg=nil, transferred=nil)
|
38
|
+
super(msg)
|
39
|
+
@transferred = transferred
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# @private
|
44
|
+
ErrorClassForResult = {}
|
45
|
+
|
46
|
+
# define an exception class for each error code
|
47
|
+
Call::Errors.to_h.each do |k,v|
|
48
|
+
klass = Class.new(Error)
|
49
|
+
klass.send(:define_method, :code){ v }
|
50
|
+
const_set(k, klass)
|
51
|
+
ErrorClassForResult[v] = klass
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.raise_error(res, text)
|
55
|
+
klass = ErrorClassForResult[res]
|
56
|
+
raise klass, "#{klass} #{text}"
|
57
|
+
end
|
58
|
+
|
59
|
+
CONTROL_SETUP_SIZE = 8
|
60
|
+
DT_DEVICE_SIZE = 18
|
61
|
+
DT_CONFIG_SIZE = 9
|
62
|
+
DT_INTERFACE_SIZE = 9
|
63
|
+
DT_ENDPOINT_SIZE = 7
|
64
|
+
DT_ENDPOINT_AUDIO_SIZE = 9 # Audio extension
|
65
|
+
DT_HUB_NONVAR_SIZE = 7
|
66
|
+
|
67
|
+
ENDPOINT_ADDRESS_MASK = 0x0f # in bEndpointAddress
|
68
|
+
ENDPOINT_DIR_MASK = 0x80
|
69
|
+
TRANSFER_TYPE_MASK = 0x03 # in bmAttributes
|
70
|
+
ISO_SYNC_TYPE_MASK = 0x0C
|
71
|
+
ISO_USAGE_TYPE_MASK = 0x30
|
72
|
+
|
73
|
+
POLLIN = 1
|
74
|
+
POLLOUT = 4
|
75
|
+
|
76
|
+
|
77
|
+
# http://www.usb.org/developers/defined_class
|
78
|
+
# @private
|
79
|
+
CLASS_CODES = [
|
80
|
+
[0x01, nil, nil, "Audio"],
|
81
|
+
[0x02, nil, nil, "Comm"],
|
82
|
+
[0x03, nil, nil, "HID"],
|
83
|
+
[0x05, nil, nil, "Physical"],
|
84
|
+
[0x06, 0x01, 0x01, "StillImaging"],
|
85
|
+
[0x06, nil, nil, "Image"],
|
86
|
+
[0x07, nil, nil, "Printer"],
|
87
|
+
[0x08, 0x01, nil, "MassStorage RBC Bulk-Only"],
|
88
|
+
[0x08, 0x02, 0x50, "MassStorage ATAPI Bulk-Only"],
|
89
|
+
[0x08, 0x03, 0x50, "MassStorage QIC-157 Bulk-Only"],
|
90
|
+
[0x08, 0x04, nil, "MassStorage UFI"],
|
91
|
+
[0x08, 0x05, 0x50, "MassStorage SFF-8070i Bulk-Only"],
|
92
|
+
[0x08, 0x06, 0x50, "MassStorage SCSI Bulk-Only"],
|
93
|
+
[0x08, nil, nil, "MassStorage"],
|
94
|
+
[0x09, 0x00, 0x00, "Full speed Hub"],
|
95
|
+
[0x09, 0x00, 0x01, "Hi-speed Hub with single TT"],
|
96
|
+
[0x09, 0x00, 0x02, "Hi-speed Hub with multiple TTs"],
|
97
|
+
[0x09, nil, nil, "Hub"],
|
98
|
+
[0x0a, nil, nil, "CDC"],
|
99
|
+
[0x0b, nil, nil, "SmartCard"],
|
100
|
+
[0x0d, 0x00, 0x00, "ContentSecurity"],
|
101
|
+
[0x0e, nil, nil, "Video"],
|
102
|
+
[0xdc, 0x01, 0x01, "Diagnostic USB2"],
|
103
|
+
[0xdc, nil, nil, "Diagnostic"],
|
104
|
+
[0xe0, 0x01, 0x01, "Bluetooth"],
|
105
|
+
[0xe0, 0x01, 0x02, "UWB"],
|
106
|
+
[0xe0, 0x01, 0x03, "RemoteNDIS"],
|
107
|
+
[0xe0, 0x02, 0x01, "Host Wire Adapter Control/Data"],
|
108
|
+
[0xe0, 0x02, 0x02, "Device Wire Adapter Control/Data"],
|
109
|
+
[0xe0, 0x02, 0x03, "Device Wire Adapter Isochronous"],
|
110
|
+
[0xe0, nil, nil, "Wireless Controller"],
|
111
|
+
[0xef, 0x01, 0x01, "Active Sync"],
|
112
|
+
[0xef, 0x01, 0x02, "Palm Sync"],
|
113
|
+
[0xef, 0x02, 0x01, "Interface Association Descriptor"],
|
114
|
+
[0xef, 0x02, 0x02, "Wire Adapter Multifunction Peripheral"],
|
115
|
+
[0xef, 0x03, 0x01, "Cable Based Association Framework"],
|
116
|
+
[0xef, nil, nil, "Miscellaneous"],
|
117
|
+
[0xfe, 0x01, 0x01, "Device Firmware Upgrade"],
|
118
|
+
[0xfe, 0x02, 0x00, "IRDA Bridge"],
|
119
|
+
[0xfe, 0x03, 0x00, "USB Test and Measurement"],
|
120
|
+
[0xfe, 0x03, 0x01, "USB Test and Measurement (USBTMC USB488)"],
|
121
|
+
[0xfe, nil, nil, "Application Specific"],
|
122
|
+
[0xff, nil, nil, "Vendor specific"],
|
123
|
+
]
|
124
|
+
# @private
|
125
|
+
CLASS_CODES_HASH1 = {}
|
126
|
+
# @private
|
127
|
+
CLASS_CODES_HASH2 = {}
|
128
|
+
# @private
|
129
|
+
CLASS_CODES_HASH3 = {}
|
130
|
+
CLASS_CODES.each {|base_class, sub_class, protocol, desc|
|
131
|
+
if protocol
|
132
|
+
CLASS_CODES_HASH3[[base_class, sub_class, protocol]] = desc
|
133
|
+
elsif sub_class
|
134
|
+
CLASS_CODES_HASH2[[base_class, sub_class]] = desc
|
135
|
+
else
|
136
|
+
CLASS_CODES_HASH1[base_class] = desc
|
137
|
+
end
|
138
|
+
}
|
139
|
+
|
140
|
+
def self.dev_string(base_class, sub_class, protocol)
|
141
|
+
if desc = CLASS_CODES_HASH3[[base_class, sub_class, protocol]]
|
142
|
+
desc
|
143
|
+
elsif desc = CLASS_CODES_HASH2[[base_class, sub_class]]
|
144
|
+
desc + " (%02x)" % [protocol]
|
145
|
+
elsif desc = CLASS_CODES_HASH1[base_class]
|
146
|
+
desc + " (%02x,%02x)" % [sub_class, protocol]
|
147
|
+
else
|
148
|
+
"Unknown(%02x,%02x,%02x)" % [base_class, sub_class, protocol]
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,305 @@
|
|
1
|
+
# This file is part of Libusb for Ruby.
|
2
|
+
#
|
3
|
+
# Libusb for Ruby is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# Libusb for Ruby is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public License
|
14
|
+
# along with Libusb for Ruby. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require 'libusb/call'
|
17
|
+
|
18
|
+
module LIBUSB
|
19
|
+
# Class representing a libusb session.
|
20
|
+
class Context
|
21
|
+
class Pollfd
|
22
|
+
include Comparable
|
23
|
+
|
24
|
+
def initialize(fd, events=0)
|
25
|
+
@fd, @events = fd, events
|
26
|
+
end
|
27
|
+
|
28
|
+
def <=>(other)
|
29
|
+
@fd <=> other.fd
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [IO] IO object bound to the file descriptor.
|
33
|
+
def io
|
34
|
+
IO.new @fd
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [Integer] Numeric file descriptor
|
38
|
+
attr_reader :fd
|
39
|
+
|
40
|
+
# @return [Integer] Event flags to poll for
|
41
|
+
attr_reader :events
|
42
|
+
|
43
|
+
# @return [Boolean] True if the file descriptor has to be observed for incoming/readable data
|
44
|
+
def pollin?
|
45
|
+
@events & POLLIN != 0
|
46
|
+
end
|
47
|
+
|
48
|
+
# @return [Boolean] True if the file descriptor has to be observed for outgoing/writeable data
|
49
|
+
def pollout?
|
50
|
+
@events & POLLOUT != 0
|
51
|
+
end
|
52
|
+
|
53
|
+
def inspect
|
54
|
+
"\#<#{self.class} fd:#{@fd}#{' POLLIN' if pollin?}#{' POLLOUT' if pollout?}>"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class CompletionFlag < FFI::Struct
|
59
|
+
layout :completed, :int
|
60
|
+
|
61
|
+
def completed?
|
62
|
+
self[:completed] != 0
|
63
|
+
end
|
64
|
+
|
65
|
+
def completed=(flag)
|
66
|
+
self[:completed] = flag ? 1 : 0
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
# Initialize libusb context.
|
72
|
+
def initialize
|
73
|
+
m = FFI::MemoryPointer.new :pointer
|
74
|
+
res = Call.libusb_init(m)
|
75
|
+
LIBUSB.raise_error res, "in libusb_init" if res!=0
|
76
|
+
@ctx = m.read_pointer
|
77
|
+
@on_pollfd_added = nil
|
78
|
+
@on_pollfd_removed = nil
|
79
|
+
end
|
80
|
+
|
81
|
+
# Deinitialize libusb.
|
82
|
+
#
|
83
|
+
# Should be called after closing all open devices and before your application terminates.
|
84
|
+
def exit
|
85
|
+
Call.libusb_exit(@ctx)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Set message verbosity.
|
89
|
+
#
|
90
|
+
# * Level 0: no messages ever printed by the library (default)
|
91
|
+
# * Level 1: error messages are printed to stderr
|
92
|
+
# * Level 2: warning and error messages are printed to stderr
|
93
|
+
# * Level 3: informational messages are printed to stdout, warning and
|
94
|
+
# error messages are printed to stderr
|
95
|
+
#
|
96
|
+
# The default level is 0, which means no messages are ever printed. If you
|
97
|
+
# choose to increase the message verbosity level, ensure that your
|
98
|
+
# application does not close the stdout/stderr file descriptors.
|
99
|
+
#
|
100
|
+
# You are advised to set level 3. libusb is conservative with its message
|
101
|
+
# logging and most of the time, will only log messages that explain error
|
102
|
+
# conditions and other oddities. This will help you debug your software.
|
103
|
+
#
|
104
|
+
# If the LIBUSB_DEBUG environment variable was set when libusb was
|
105
|
+
# initialized, this method does nothing: the message verbosity is
|
106
|
+
# fixed to the value in the environment variable.
|
107
|
+
#
|
108
|
+
# If libusb was compiled without any message logging, this method
|
109
|
+
# does nothing: you'll never get any messages.
|
110
|
+
#
|
111
|
+
# If libusb was compiled with verbose debug message logging, this
|
112
|
+
# method does nothing: you'll always get messages from all levels.
|
113
|
+
#
|
114
|
+
# @param [Fixnum] level debug level to set
|
115
|
+
def debug=(level)
|
116
|
+
Call.libusb_set_debug(@ctx, level)
|
117
|
+
end
|
118
|
+
|
119
|
+
def device_list
|
120
|
+
pppDevs = FFI::MemoryPointer.new :pointer
|
121
|
+
size = Call.libusb_get_device_list(@ctx, pppDevs)
|
122
|
+
LIBUSB.raise_error size, "in libusb_get_device_list" if size<0
|
123
|
+
ppDevs = pppDevs.read_pointer
|
124
|
+
pDevs = []
|
125
|
+
size.times do |devi|
|
126
|
+
pDev = ppDevs.get_pointer(devi*FFI.type_size(:pointer))
|
127
|
+
pDevs << Device.new(self, pDev)
|
128
|
+
end
|
129
|
+
Call.libusb_free_device_list(ppDevs, 1)
|
130
|
+
pDevs
|
131
|
+
end
|
132
|
+
private :device_list
|
133
|
+
|
134
|
+
# Handle any pending events in blocking mode.
|
135
|
+
#
|
136
|
+
# This method must be called when libusb is running asynchronous transfers.
|
137
|
+
# This gives libusb the opportunity to reap pending transfers,
|
138
|
+
# invoke callbacks, etc.
|
139
|
+
#
|
140
|
+
# If a zero timeout is passed, this function will handle any already-pending
|
141
|
+
# events and then immediately return in non-blocking style.
|
142
|
+
#
|
143
|
+
# If a non-zero timeout is passed and no events are currently pending, this
|
144
|
+
# method will block waiting for events to handle up until the specified timeout.
|
145
|
+
# If an event arrives or a signal is raised, this method will return early.
|
146
|
+
#
|
147
|
+
# If the parameter completion_flag is used, then after obtaining the event
|
148
|
+
# handling lock this function will return immediately if the flag is set to completed.
|
149
|
+
# This allows for race free waiting for the completion of a specific transfer.
|
150
|
+
# See source of {Transfer#submit_and_wait} for a use case of completion_flag.
|
151
|
+
#
|
152
|
+
# @param [Integer, nil] timeout the maximum time (in millseconds) to block waiting for
|
153
|
+
# events, or 0 for non-blocking mode
|
154
|
+
# @param [Context::CompletionFlag, nil] completion_flag CompletionFlag to check
|
155
|
+
def handle_events(timeout=nil, completion_flag=nil)
|
156
|
+
if completion_flag && !completion_flag.is_a?(Context::CompletionFlag)
|
157
|
+
raise ArgumentError, "completion_flag is not a CompletionFlag"
|
158
|
+
end
|
159
|
+
if timeout
|
160
|
+
timeval = Call::Timeval.new
|
161
|
+
timeval.in_ms = timeout
|
162
|
+
res = if Call.respond_to?(:libusb_handle_events_timeout_completed)
|
163
|
+
Call.libusb_handle_events_timeout_completed(@ctx, timeval, completion_flag)
|
164
|
+
else
|
165
|
+
Call.libusb_handle_events_timeout(@ctx, timeval)
|
166
|
+
end
|
167
|
+
else
|
168
|
+
res = if Call.respond_to?(:libusb_handle_events_completed)
|
169
|
+
Call.libusb_handle_events_completed(@ctx, completion_flag )
|
170
|
+
else
|
171
|
+
Call.libusb_handle_events(@ctx)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
LIBUSB.raise_error res, "in libusb_handle_events" if res<0
|
175
|
+
end
|
176
|
+
|
177
|
+
# Obtain a list of devices currently attached to the USB system, optionally matching certain criteria.
|
178
|
+
#
|
179
|
+
# @param [Hash] filter_hash A number of criteria can be defined in key-value pairs.
|
180
|
+
# Only devices that equal all given criterions will be returned. If a criterion is
|
181
|
+
# not specified or its value is +nil+, any device will match that criterion.
|
182
|
+
# The following criteria can be filtered:
|
183
|
+
# * <tt>:idVendor</tt>, <tt>:idProduct</tt> (+FixNum+) for matching vendor/product ID,
|
184
|
+
# * <tt>:bClass</tt>, <tt>:bSubClass</tt>, <tt>:bProtocol</tt> (+FixNum+) for the device type -
|
185
|
+
# Devices using CLASS_PER_INTERFACE will match, if any of the interfaces match.
|
186
|
+
# * <tt>:bcdUSB</tt>, <tt>:bcdDevice</tt>, <tt>:bMaxPacketSize0</tt> (+FixNum+) for the
|
187
|
+
# USB and device release numbers.
|
188
|
+
# Criteria can also specified as Array of several alternative values.
|
189
|
+
#
|
190
|
+
# @example
|
191
|
+
# # Return all devices of vendor 0x0ab1 where idProduct is 3 or 4:
|
192
|
+
# context.device :idVendor=>0x0ab1, :idProduct=>[0x0003, 0x0004]
|
193
|
+
#
|
194
|
+
# @return [Array<LIBUSB::Device>]
|
195
|
+
def devices(filter_hash={})
|
196
|
+
device_list.select do |dev|
|
197
|
+
( !filter_hash[:bClass] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
|
198
|
+
dev.settings.map(&:bInterfaceClass).&([filter_hash[:bClass]].flatten).any? :
|
199
|
+
[filter_hash[:bClass]].flatten.include?(dev.bDeviceClass))) &&
|
200
|
+
( !filter_hash[:bSubClass] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
|
201
|
+
dev.settings.map(&:bInterfaceSubClass).&([filter_hash[:bSubClass]].flatten).any? :
|
202
|
+
[filter_hash[:bSubClass]].flatten.include?(dev.bDeviceSubClass))) &&
|
203
|
+
( !filter_hash[:bProtocol] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
|
204
|
+
dev.settings.map(&:bInterfaceProtocol).&([filter_hash[:bProtocol]].flatten).any? :
|
205
|
+
[filter_hash[:bProtocol]].flatten.include?(dev.bDeviceProtocol))) &&
|
206
|
+
( !filter_hash[:bMaxPacketSize0] || [filter_hash[:bMaxPacketSize0]].flatten.include?(dev.bMaxPacketSize0) ) &&
|
207
|
+
( !filter_hash[:idVendor] || [filter_hash[:idVendor]].flatten.include?(dev.idVendor) ) &&
|
208
|
+
( !filter_hash[:idProduct] || [filter_hash[:idProduct]].flatten.include?(dev.idProduct) ) &&
|
209
|
+
( !filter_hash[:bcdUSB] || [filter_hash[:bcdUSB]].flatten.include?(dev.bcdUSB) ) &&
|
210
|
+
( !filter_hash[:bcdDevice] || [filter_hash[:bcdDevice]].flatten.include?(dev.bcdDevice) )
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
|
215
|
+
# Retrieve a list of file descriptors that should be polled by your main
|
216
|
+
# loop as libusb event sources.
|
217
|
+
#
|
218
|
+
# As file descriptors are a Unix-specific concept, this function is not
|
219
|
+
# available on Windows and will always return +nil+.
|
220
|
+
#
|
221
|
+
# @return [Array<Pollfd>] list of Pollfd objects,
|
222
|
+
# +nil+ on error,
|
223
|
+
# +nil+ on platforms where the functionality is not available
|
224
|
+
def pollfds
|
225
|
+
ppPollfds = Call.libusb_get_pollfds(@ctx)
|
226
|
+
return nil if ppPollfds.null?
|
227
|
+
offs = 0
|
228
|
+
pollfds = []
|
229
|
+
while !(pPollfd=ppPollfds.get_pointer(offs)).null?
|
230
|
+
pollfd = Call::Pollfd.new pPollfd
|
231
|
+
pollfds << Pollfd.new(pollfd[:fd], pollfd[:events])
|
232
|
+
offs += FFI.type_size :pointer
|
233
|
+
end
|
234
|
+
# ppPollfds has to be released by free() -> give the GC this job
|
235
|
+
ppPollfds.autorelease = true
|
236
|
+
pollfds
|
237
|
+
end
|
238
|
+
|
239
|
+
# Determine the next internal timeout that libusb needs to handle.
|
240
|
+
#
|
241
|
+
# You only need to use this function if you are calling poll() or select() or
|
242
|
+
# similar on libusb's file descriptors yourself - you do not need to use it if
|
243
|
+
# you are calling {#handle_events} directly.
|
244
|
+
#
|
245
|
+
# You should call this function in your main loop in order to determine how long
|
246
|
+
# to wait for select() or poll() to return results. libusb needs to be called
|
247
|
+
# into at this timeout, so you should use it as an upper bound on your select() or
|
248
|
+
# poll() call.
|
249
|
+
#
|
250
|
+
# When the timeout has expired, call into {#handle_events} (perhaps
|
251
|
+
# in non-blocking mode) so that libusb can handle the timeout.
|
252
|
+
#
|
253
|
+
# This function may return zero. If this is the
|
254
|
+
# case, it indicates that libusb has a timeout that has already expired so you
|
255
|
+
# should call {#handle_events} immediately. A return code
|
256
|
+
# of +nil+ indicates that there are no pending timeouts.
|
257
|
+
#
|
258
|
+
# On some platforms, this function will always returns +nil+ (no pending timeouts).
|
259
|
+
# See libusb's notes on time-based events.
|
260
|
+
#
|
261
|
+
# @return [Float, nil] the timeout in seconds
|
262
|
+
def next_timeout
|
263
|
+
timeval = Call::Timeval.new
|
264
|
+
res = Call.libusb_get_next_timeout @ctx, timeval
|
265
|
+
LIBUSB.raise_error res, "in libusb_get_next_timeout" if res<0
|
266
|
+
res == 1 ? timeval.in_s : nil
|
267
|
+
end
|
268
|
+
|
269
|
+
# Register a notification block for file descriptor additions.
|
270
|
+
#
|
271
|
+
# This block will be invoked for every new file descriptor that
|
272
|
+
# libusb uses as an event source.
|
273
|
+
#
|
274
|
+
# Note that file descriptors may have been added even before you register these
|
275
|
+
# notifiers (e.g. at {Context#initialize} time).
|
276
|
+
#
|
277
|
+
# @yieldparam [Pollfd] pollfd The added file descriptor is yielded to the block
|
278
|
+
def on_pollfd_added &block
|
279
|
+
@on_pollfd_added = proc do |fd, events, _|
|
280
|
+
pollfd = Pollfd.new fd, events
|
281
|
+
block.call pollfd
|
282
|
+
end
|
283
|
+
Call.libusb_set_pollfd_notifiers @ctx, @on_pollfd_added, @on_pollfd_removed, nil
|
284
|
+
end
|
285
|
+
|
286
|
+
# Register a notification block for file descriptor removals.
|
287
|
+
#
|
288
|
+
# This block will be invoked for every removed file descriptor that
|
289
|
+
# libusb uses as an event source.
|
290
|
+
#
|
291
|
+
# Note that the removal notifier may be called during {Context#exit}
|
292
|
+
# (e.g. when it is closing file descriptors that were opened and added to the poll
|
293
|
+
# set at {Context#initialize} time). If you don't want this, overwrite the notifier
|
294
|
+
# immediately before calling {Context#exit}.
|
295
|
+
#
|
296
|
+
# @yieldparam [Pollfd] pollfd The removed file descriptor is yielded to the block
|
297
|
+
def on_pollfd_removed &block
|
298
|
+
@on_pollfd_removed = proc do |fd, _|
|
299
|
+
pollfd = Pollfd.new fd
|
300
|
+
block.call pollfd
|
301
|
+
end
|
302
|
+
Call.libusb_set_pollfd_notifiers @ctx, @on_pollfd_added, @on_pollfd_removed, nil
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|