libusb 0.3.3-x64-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- 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
|