libusb 0.1.3-x86-mingw32 → 0.2.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/.yardopts +1 -0
- data/History.txt +8 -0
- data/Manifest.txt +2 -15
- data/README.rdoc +22 -16
- data/Rakefile +26 -10
- data/lib/libusb-1.0.dll +0 -0
- data/lib/libusb.rb +27 -1767
- data/lib/libusb/call.rb +271 -0
- data/lib/libusb/compat.rb +2 -2
- data/lib/libusb/configuration.rb +139 -0
- data/lib/libusb/constants.rb +138 -0
- data/lib/libusb/context.rb +127 -0
- data/lib/libusb/dev_handle.rb +399 -0
- data/lib/libusb/device.rb +359 -0
- data/lib/libusb/endpoint.rb +147 -0
- data/lib/libusb/interface.rb +51 -0
- data/lib/libusb/setting.rb +132 -0
- data/lib/libusb/transfer.rb +269 -0
- data/lib/libusb/version.rb +63 -0
- data/test/test_libusb_capability.rb +23 -0
- data/test/test_libusb_descriptors.rb +13 -0
- data/test/test_libusb_version.rb +36 -0
- metadata +30 -18
- data/test/test_libusb_keyboard.rb +0 -50
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--title "libusb-1.0 Ruby Interface" --no-private lib/**/*.rb
|
data/History.txt
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
=== 0.2.0 / 2012-06-15
|
2
|
+
|
3
|
+
* Divide up the libusb library across multiple files, required with autoload
|
4
|
+
* add methods: LIBUSB.has_capability?, Device#device_speed (libusb-1.0.9+)
|
5
|
+
* add possibility to read out libusb version: LIBUSB.version (libusbx-1.0.10+)
|
6
|
+
* add methods: Device#parent, Device#port_number, Device#port_path (libusbx-1.0.12+)
|
7
|
+
* switch to libusbx-1.0.12 for windows build
|
8
|
+
|
1
9
|
=== 0.1.3 / 2012-03-15
|
2
10
|
|
3
11
|
* Add documentation of descriptor accessors
|
data/Manifest.txt
CHANGED
@@ -1,16 +1,3 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
COPYING
|
4
|
-
History.txt
|
5
|
-
Manifest.txt
|
6
|
-
README.rdoc
|
7
|
-
Rakefile
|
1
|
+
# List of files is auto generated by 'git ls-files',
|
2
|
+
# but let Hoe find the VERSION string:
|
8
3
|
lib/libusb.rb
|
9
|
-
lib/libusb/compat.rb
|
10
|
-
test/test_libusb_compat.rb
|
11
|
-
test/test_libusb_compat_mass_storage.rb
|
12
|
-
test/test_libusb_descriptors.rb
|
13
|
-
test/test_libusb_gc.rb
|
14
|
-
test/test_libusb_iso_transfer.rb
|
15
|
-
test/test_libusb_mass_storage.rb
|
16
|
-
test/test_libusb_mass_storage2.rb
|
data/README.rdoc
CHANGED
@@ -3,14 +3,13 @@
|
|
3
3
|
= Access USB devices from Ruby via libusb-1.0.
|
4
4
|
|
5
5
|
* http://github.com/larskanis/libusb
|
6
|
-
* API documentation: http://rubydoc.info/gems/libusb/frames
|
7
6
|
|
8
7
|
== DESCRIPTION:
|
9
8
|
|
10
|
-
LIBUSB is a Ruby binding that gives
|
9
|
+
LIBUSB is a Ruby binding that gives Ruby programmers access to arbitrary USB devices.
|
11
10
|
|
12
|
-
* libusb is a library that gives full access to devices connected via the USB bus. No special kernel driver is thus necessary for accessing USB devices.
|
13
|
-
* This Ruby binding supports the API version 1.0 of libusb. Note that the old "legacy" version 0.1.x of libusb uses a completely different API that is covered by the ruby extension ruby-usb[http://www.a-k-r.org/ruby-usb/] .
|
11
|
+
* libusb[http://libusbx.org] is a library that gives full access to devices connected via the USB bus. No special kernel driver is thus necessary for accessing USB devices.
|
12
|
+
* This Ruby binding supports the API version 1.0 of libusb[http://libusbx.org]. Note that the old "legacy" version 0.1.x of libusb uses a completely different API that is covered by the ruby extension ruby-usb[http://www.a-k-r.org/ruby-usb/] .
|
14
13
|
|
15
14
|
|
16
15
|
LIBUSB for Ruby is covered by the GNU Lesser General Public License version 3.
|
@@ -26,13 +25,11 @@ LIBUSB for Ruby is covered by the GNU Lesser General Public License version 3.
|
|
26
25
|
|
27
26
|
usb = LIBUSB::Context.new
|
28
27
|
device = usb.devices(:idVendor => 0x04b4, :idProduct => 0x8613).first
|
29
|
-
device.
|
30
|
-
handle.
|
31
|
-
handle.control_transfer(:bmRequestType => 0x40, :bRequest => 0xa0, :wValue => 0xe600, :wIndex => 0x0000, :dataOut => 1.chr)
|
32
|
-
end
|
28
|
+
device.open_interface(0) do |handle|
|
29
|
+
handle.control_transfer(:bmRequestType => 0x40, :bRequest => 0xa0, :wValue => 0xe600, :wIndex => 0x0000, :dataOut => 1.chr)
|
33
30
|
end
|
34
31
|
{LIBUSB::Context#devices} is used to get all or only particular devices.
|
35
|
-
After opening the {LIBUSB::Device} the resulting {LIBUSB::DevHandle} can be
|
32
|
+
After {LIBUSB::Device#open_interface opening and claiming} the {LIBUSB::Device} the resulting {LIBUSB::DevHandle} can be
|
36
33
|
used to communicate with the connected USB device
|
37
34
|
by {LIBUSB::DevHandle#control_transfer}, {LIBUSB::DevHandle#bulk_transfer},
|
38
35
|
{LIBUSB::DevHandle#interrupt_transfer} or by using the {LIBUSB::Transfer} classes.
|
@@ -55,17 +52,20 @@ maximum packet size.
|
|
55
52
|
|
56
53
|
== REQUIREMENTS:
|
57
54
|
|
58
|
-
* libusb version 1.0 or greater (bundled with Windows binary gem)
|
59
|
-
* FFI-gem[http://github.com/ffi/ffi]
|
60
55
|
* Linux, MacOSX or Windows system with Ruby MRI 1.8.7/1.9.x or JRuby
|
61
56
|
|
62
57
|
== INSTALL:
|
63
58
|
|
64
59
|
gem install libusb
|
65
60
|
|
66
|
-
In order to use LIBUSB, you need the libusb-1.0 library (but not its header files).
|
67
|
-
*
|
68
|
-
|
61
|
+
In order to use LIBUSB, you also need the libusb-1.0[http://libusbx.org] library (but not necessarily its header files).
|
62
|
+
* Debian or Ubuntu:
|
63
|
+
sudo apt-get install libusb-1.0-0
|
64
|
+
* OS-X: install with homebrew:
|
65
|
+
brew install libusb
|
66
|
+
or macports:
|
67
|
+
port install libusb
|
68
|
+
* Windows: libusb.gem comes with a precompiled +libusb.dll+, but you need to install a device driver (see below)
|
69
69
|
|
70
70
|
Latest code can be used in this way:
|
71
71
|
|
@@ -77,8 +77,8 @@ Latest code can be used in this way:
|
|
77
77
|
|
78
78
|
In contrast to Linux, any access to an USB device by LIBUSB on Windows requires a proper driver
|
79
79
|
installed in the system. Fortunately creating such a driver is quite easy with
|
80
|
-
Zadig[http://sourceforge.net/apps/mediawiki/libwdi/index.php?title=Main_Page]. Select the interesting USB device
|
81
|
-
and press "Install Driver". That's it. You may take the generated output directory
|
80
|
+
Zadig[http://sourceforge.net/apps/mediawiki/libwdi/index.php?title=Main_Page]. Select the interesting USB device,
|
81
|
+
choose WinUSB driver and press "Install Driver". That's it. You may take the generated output directory
|
82
82
|
with it's INI-file and use it for driver installation on other 32 or 64 bit Windows
|
83
83
|
systems.
|
84
84
|
|
@@ -104,6 +104,12 @@ Download and cross compile libusb for win32:
|
|
104
104
|
If everything works, there should be libusb-VERSION-x86-mingw32.gem in the pkg
|
105
105
|
directory.
|
106
106
|
|
107
|
+
== Resources
|
108
|
+
|
109
|
+
* API documentation of Libusb for Ruby: http://rubydoc.info/gems/libusb/frames
|
110
|
+
* Mailinglist: http://rubyforge.org/mailman/listinfo/libusb-hackers
|
111
|
+
* Overall introduction to USB: http://www.usbmadesimple.co.uk
|
112
|
+
|
107
113
|
== Todo
|
108
114
|
|
109
115
|
* add proper handling for polling and timing: http://libusb.sourceforge.net/api-1.0/group__poll.html
|
data/Rakefile
CHANGED
@@ -13,9 +13,16 @@ require 'rake/extensioncompiler'
|
|
13
13
|
COMPILE_HOME = Pathname( "./tmp" ).expand_path
|
14
14
|
STATIC_SOURCESDIR = COMPILE_HOME + 'sources'
|
15
15
|
STATIC_BUILDDIR = COMPILE_HOME + 'builds'
|
16
|
+
RUBY_BUILD = RbConfig::CONFIG["host"]
|
17
|
+
CROSS_PREFIX = begin
|
18
|
+
Rake::ExtensionCompiler.mingw_host
|
19
|
+
rescue => err
|
20
|
+
$stderr.puts "Cross-compilation disabled -- %s" % [ err.message ]
|
21
|
+
'unknown'
|
22
|
+
end
|
16
23
|
|
17
24
|
# Fetch tarball from sourceforge
|
18
|
-
# LIBUSB_VERSION = ENV['LIBUSB_VERSION'] || '1.0.
|
25
|
+
# LIBUSB_VERSION = ENV['LIBUSB_VERSION'] || '1.0.9'
|
19
26
|
# LIBUSB_SOURCE_URI = URI( "http://downloads.sourceforge.net/project/libusb/libusb-1.0/libusb-#{LIBUSB_VERSION}/libusb-#{LIBUSB_VERSION}.tar.bz2" )
|
20
27
|
# LIBUSB_TARBALL = STATIC_SOURCESDIR + File.basename( LIBUSB_SOURCE_URI.path )
|
21
28
|
|
@@ -24,10 +31,15 @@ STATIC_BUILDDIR = COMPILE_HOME + 'builds'
|
|
24
31
|
# LIBUSB_SOURCE_URI = URI( "http://git.libusb.org/?p=libusb.git;a=snapshot;h=#{LIBUSB_VERSION};sf=tbz2" )
|
25
32
|
# LIBUSB_TARBALL = STATIC_SOURCESDIR + "libusb-#{LIBUSB_VERSION}.tar.bz2"
|
26
33
|
|
34
|
+
# Fetch tarball from libusbx
|
35
|
+
LIBUSB_VERSION = ENV['LIBUSB_VERSION'] || '1.0.12'
|
36
|
+
LIBUSB_SOURCE_URI = URI( "http://downloads.sourceforge.net/project/libusbx/releases/#{LIBUSB_VERSION[/^\d+\.\d+\.\d+/]}/source/libusbx-#{LIBUSB_VERSION}.tar.bz2" )
|
37
|
+
LIBUSB_TARBALL = STATIC_SOURCESDIR + File.basename( LIBUSB_SOURCE_URI.path )
|
38
|
+
|
27
39
|
# Fetch tarball from Pete Batard's git repo
|
28
|
-
LIBUSB_VERSION = ENV['LIBUSB_VERSION'] || '4cc72d0'
|
29
|
-
LIBUSB_SOURCE_URI = URI( "http://git.libusb.org/?p=libusb-pbatard.git;a=snapshot;h=#{LIBUSB_VERSION};sf=tbz2" )
|
30
|
-
LIBUSB_TARBALL = STATIC_SOURCESDIR + "libusb-pbatard-#{LIBUSB_VERSION}.tar.bz2"
|
40
|
+
# LIBUSB_VERSION = ENV['LIBUSB_VERSION'] || '4cc72d0'
|
41
|
+
# LIBUSB_SOURCE_URI = URI( "http://git.libusb.org/?p=libusb-pbatard.git;a=snapshot;h=#{LIBUSB_VERSION};sf=tbz2" )
|
42
|
+
# LIBUSB_TARBALL = STATIC_SOURCESDIR + "libusb-pbatard-#{LIBUSB_VERSION}.tar.bz2"
|
31
43
|
|
32
44
|
# Static libusb build vars
|
33
45
|
STATIC_LIBUSB_BUILDDIR = STATIC_BUILDDIR + LIBUSB_TARBALL.basename(".tar.bz2")
|
@@ -44,10 +56,11 @@ hoe = Hoe.spec 'libusb' do
|
|
44
56
|
|
45
57
|
self.url = 'http://github.com/larskanis/libusb'
|
46
58
|
self.summary = 'Access USB devices from Ruby via libusb-1.0'
|
47
|
-
self.description = 'LIBUSB is a Ruby binding that gives Ruby programmers access to
|
59
|
+
self.description = 'LIBUSB is a Ruby binding that gives Ruby programmers access to arbitrary USB devices'
|
48
60
|
|
49
61
|
self.readme_file = 'README.rdoc'
|
50
62
|
spec_extras[:rdoc_options] = ['--main', readme_file, "--charset=UTF-8"]
|
63
|
+
spec_extras[:files] = `git ls-files`.split
|
51
64
|
self.extra_rdoc_files << self.readme_file
|
52
65
|
|
53
66
|
# clean intermediate files and folders
|
@@ -87,18 +100,21 @@ file LIBUSB_CONFIGURE => STATIC_LIBUSB_BUILDDIR do |t|
|
|
87
100
|
end
|
88
101
|
end
|
89
102
|
|
103
|
+
LIBUSB_ENV = [
|
104
|
+
"CFLAGS='-fno-omit-frame-pointer'",
|
105
|
+
]
|
106
|
+
|
90
107
|
# generate the makefile in a clean build location
|
91
108
|
file LIBUSB_MAKEFILE => LIBUSB_CONFIGURE do |t|
|
92
109
|
Dir.chdir( STATIC_LIBUSB_BUILDDIR ) do
|
93
110
|
options = [
|
94
|
-
|
95
|
-
"--host=#{
|
96
|
-
"--build=#{
|
111
|
+
"--target=#{CROSS_PREFIX}",
|
112
|
+
"--host=#{CROSS_PREFIX}",
|
113
|
+
"--build=#{RUBY_BUILD}",
|
97
114
|
]
|
98
115
|
|
99
116
|
configure_path = STATIC_LIBUSB_BUILDDIR + 'configure'
|
100
|
-
|
101
|
-
sh *cmd
|
117
|
+
sh "env #{[LIBUSB_ENV, configure_path.to_s, *options].join(" ")}"
|
102
118
|
end
|
103
119
|
end
|
104
120
|
|
data/lib/libusb-1.0.dll
CHANGED
Binary file
|
data/lib/libusb.rb
CHANGED
@@ -13,1780 +13,40 @@
|
|
13
13
|
# You should have received a copy of the GNU Lesser General Public License
|
14
14
|
# along with Libusb for Ruby. If not, see <http://www.gnu.org/licenses/>.
|
15
15
|
|
16
|
-
require 'rubygems'
|
17
|
-
require 'ffi'
|
18
|
-
|
19
|
-
|
20
16
|
module LIBUSB
|
21
|
-
VERSION = "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, :blocking=>true
|
169
|
-
attach_function 'libusb_claim_interface', [:libusb_device_handle, :int], :int
|
170
|
-
attach_function 'libusb_release_interface', [:libusb_device_handle, :int], :int, :blocking=>true
|
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, :blocking=>true
|
175
|
-
attach_function 'libusb_clear_halt', [:libusb_device_handle, :int], :int, :blocking=>true
|
176
|
-
attach_function 'libusb_reset_device', [:libusb_device_handle], :int, :blocking=>true
|
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
|
-
# Base class of libusb errors
|
239
|
-
class Error < RuntimeError
|
240
|
-
end
|
241
|
-
ErrorClassForResult = {}
|
242
|
-
|
243
|
-
# define an exception class for each error code
|
244
|
-
Call::Errors.to_h.each do |k,v|
|
245
|
-
klass = Class.new(Error)
|
246
|
-
klass.send(:define_method, :code){ v }
|
247
|
-
const_set(k, klass)
|
248
|
-
ErrorClassForResult[v] = klass
|
249
|
-
end
|
250
|
-
|
251
|
-
def self.raise_error(res, text)
|
252
|
-
klass = ErrorClassForResult[res]
|
253
|
-
raise klass, "#{klass} #{text}"
|
254
|
-
end
|
255
|
-
|
256
|
-
CONTROL_SETUP_SIZE = 8
|
257
|
-
DT_DEVICE_SIZE = 18
|
258
|
-
DT_CONFIG_SIZE = 9
|
259
|
-
DT_INTERFACE_SIZE = 9
|
260
|
-
DT_ENDPOINT_SIZE = 7
|
261
|
-
DT_ENDPOINT_AUDIO_SIZE = 9 # Audio extension
|
262
|
-
DT_HUB_NONVAR_SIZE = 7
|
263
|
-
|
264
|
-
ENDPOINT_ADDRESS_MASK = 0x0f # in bEndpointAddress
|
265
|
-
ENDPOINT_DIR_MASK = 0x80
|
266
|
-
TRANSFER_TYPE_MASK = 0x03 # in bmAttributes
|
267
|
-
ISO_SYNC_TYPE_MASK = 0x0C
|
268
|
-
ISO_USAGE_TYPE_MASK = 0x30
|
269
|
-
|
270
|
-
|
271
|
-
# :stopdoc:
|
272
|
-
# http://www.usb.org/developers/defined_class
|
273
|
-
CLASS_CODES = [
|
274
|
-
[0x01, nil, nil, "Audio"],
|
275
|
-
[0x02, nil, nil, "Comm"],
|
276
|
-
[0x03, nil, nil, "HID"],
|
277
|
-
[0x05, nil, nil, "Physical"],
|
278
|
-
[0x06, 0x01, 0x01, "StillImaging"],
|
279
|
-
[0x06, nil, nil, "Image"],
|
280
|
-
[0x07, nil, nil, "Printer"],
|
281
|
-
[0x08, 0x01, nil, "MassStorage RBC Bluk-Only"],
|
282
|
-
[0x08, 0x02, 0x50, "MassStorage ATAPI Bluk-Only"],
|
283
|
-
[0x08, 0x03, 0x50, "MassStorage QIC-157 Bluk-Only"],
|
284
|
-
[0x08, 0x04, nil, "MassStorage UFI"],
|
285
|
-
[0x08, 0x05, 0x50, "MassStorage SFF-8070i Bluk-Only"],
|
286
|
-
[0x08, 0x06, 0x50, "MassStorage SCSI Bluk-Only"],
|
287
|
-
[0x08, nil, nil, "MassStorage"],
|
288
|
-
[0x09, 0x00, 0x00, "Full speed Hub"],
|
289
|
-
[0x09, 0x00, 0x01, "Hi-speed Hub with single TT"],
|
290
|
-
[0x09, 0x00, 0x02, "Hi-speed Hub with multiple TTs"],
|
291
|
-
[0x09, nil, nil, "Hub"],
|
292
|
-
[0x0a, nil, nil, "CDC"],
|
293
|
-
[0x0b, nil, nil, "SmartCard"],
|
294
|
-
[0x0d, 0x00, 0x00, "ContentSecurity"],
|
295
|
-
[0x0e, nil, nil, "Video"],
|
296
|
-
[0xdc, 0x01, 0x01, "Diagnostic USB2"],
|
297
|
-
[0xdc, nil, nil, "Diagnostic"],
|
298
|
-
[0xe0, 0x01, 0x01, "Bluetooth"],
|
299
|
-
[0xe0, 0x01, 0x02, "UWB"],
|
300
|
-
[0xe0, 0x01, 0x03, "RemoteNDIS"],
|
301
|
-
[0xe0, 0x02, 0x01, "Host Wire Adapter Control/Data"],
|
302
|
-
[0xe0, 0x02, 0x02, "Device Wire Adapter Control/Data"],
|
303
|
-
[0xe0, 0x02, 0x03, "Device Wire Adapter Isochronous"],
|
304
|
-
[0xe0, nil, nil, "Wireless Controller"],
|
305
|
-
[0xef, 0x01, 0x01, "Active Sync"],
|
306
|
-
[0xef, 0x01, 0x02, "Palm Sync"],
|
307
|
-
[0xef, 0x02, 0x01, "Interface Association Descriptor"],
|
308
|
-
[0xef, 0x02, 0x02, "Wire Adapter Multifunction Peripheral"],
|
309
|
-
[0xef, 0x03, 0x01, "Cable Based Association Framework"],
|
310
|
-
[0xef, nil, nil, "Miscellaneous"],
|
311
|
-
[0xfe, 0x01, 0x01, "Device Firmware Upgrade"],
|
312
|
-
[0xfe, 0x02, 0x00, "IRDA Bridge"],
|
313
|
-
[0xfe, 0x03, 0x00, "USB Test and Measurement"],
|
314
|
-
[0xfe, 0x03, 0x01, "USB Test and Measurement (USBTMC USB488)"],
|
315
|
-
[0xfe, nil, nil, "Application Specific"],
|
316
|
-
[0xff, nil, nil, "Vendor specific"],
|
317
|
-
]
|
318
|
-
CLASS_CODES_HASH1 = {}
|
319
|
-
CLASS_CODES_HASH2 = {}
|
320
|
-
CLASS_CODES_HASH3 = {}
|
321
|
-
CLASS_CODES.each {|base_class, sub_class, protocol, desc|
|
322
|
-
if protocol
|
323
|
-
CLASS_CODES_HASH3[[base_class, sub_class, protocol]] = desc
|
324
|
-
elsif sub_class
|
325
|
-
CLASS_CODES_HASH2[[base_class, sub_class]] = desc
|
326
|
-
else
|
327
|
-
CLASS_CODES_HASH1[base_class] = desc
|
328
|
-
end
|
329
|
-
}
|
330
|
-
|
331
|
-
def self.dev_string(base_class, sub_class, protocol)
|
332
|
-
if desc = CLASS_CODES_HASH3[[base_class, sub_class, protocol]]
|
333
|
-
desc
|
334
|
-
elsif desc = CLASS_CODES_HASH2[[base_class, sub_class]]
|
335
|
-
desc + " (%02x)" % [protocol]
|
336
|
-
elsif desc = CLASS_CODES_HASH1[base_class]
|
337
|
-
desc + " (%02x,%02x)" % [sub_class, protocol]
|
338
|
-
else
|
339
|
-
"Unkonwn(%02x,%02x,%02x)" % [base_class, sub_class, protocol]
|
340
|
-
end
|
341
|
-
end
|
342
|
-
# :startdoc:
|
343
|
-
|
344
|
-
|
345
|
-
# Abstract base class for USB transfers. Use
|
346
|
-
# {ControlTransfer}, {BulkTransfer}, {InterruptTransfer}, {IsochronousTransfer}
|
347
|
-
# to do transfers.
|
348
|
-
class Transfer
|
349
|
-
def initialize(args={})
|
350
|
-
args.each{|k,v| send("#{k}=", v) }
|
351
|
-
@buffer = nil
|
352
|
-
end
|
353
|
-
private :initialize
|
354
|
-
|
355
|
-
# Set the handle for the device to communicate with.
|
356
|
-
def dev_handle=(dev)
|
357
|
-
@dev_handle = dev
|
358
|
-
@transfer[:dev_handle] = @dev_handle.pHandle
|
359
|
-
end
|
360
|
-
|
361
|
-
# Timeout for this transfer in millseconds.
|
362
|
-
#
|
363
|
-
# A value of 0 indicates no timeout.
|
364
|
-
def timeout=(value)
|
365
|
-
@transfer[:timeout] = value
|
366
|
-
end
|
367
|
-
|
368
|
-
# Set the address of a valid endpoint to communicate with.
|
369
|
-
def endpoint=(endpoint)
|
370
|
-
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
371
|
-
@transfer[:endpoint] = endpoint
|
372
|
-
end
|
373
|
-
|
374
|
-
# Set output data that should be sent.
|
375
|
-
def buffer=(data)
|
376
|
-
if !@buffer || data.bytesize>@buffer.size
|
377
|
-
free_buffer
|
378
|
-
@buffer = FFI::MemoryPointer.new(data.bytesize, 1, false)
|
379
|
-
end
|
380
|
-
@buffer.put_bytes(0, data)
|
381
|
-
@transfer[:buffer] = @buffer
|
382
|
-
@transfer[:length] = data.bytesize
|
383
|
-
end
|
384
|
-
|
385
|
-
# Retrieve the current data buffer.
|
386
|
-
def buffer
|
387
|
-
@transfer[:buffer].read_string(@transfer[:length])
|
388
|
-
end
|
389
|
-
|
390
|
-
# Clear the current data buffer.
|
391
|
-
def free_buffer
|
392
|
-
if @buffer
|
393
|
-
@buffer.free
|
394
|
-
@buffer = nil
|
395
|
-
@transfer[:buffer] = nil
|
396
|
-
@transfer[:length] = 0
|
397
|
-
end
|
398
|
-
end
|
399
|
-
|
400
|
-
# Allocate +len+ bytes of data buffer for input transfer.
|
401
|
-
#
|
402
|
-
# @param [Fixnum] len Number of bytes to allocate
|
403
|
-
# @param [String, nil] data some data to initialize the buffer with
|
404
|
-
def alloc_buffer(len, data=nil)
|
405
|
-
if !@buffer || len>@buffer.size
|
406
|
-
free_buffer
|
407
|
-
@buffer = FFI::MemoryPointer.new(len, 1, false)
|
408
|
-
end
|
409
|
-
@buffer.put_bytes(0, data) if data
|
410
|
-
@transfer[:buffer] = @buffer
|
411
|
-
@transfer[:length] = len
|
412
|
-
end
|
413
|
-
|
414
|
-
# The number of bytes actually transferred.
|
415
|
-
def actual_length
|
416
|
-
@transfer[:actual_length]
|
417
|
-
end
|
418
|
-
|
419
|
-
# Retrieve the data actually transferred.
|
420
|
-
#
|
421
|
-
# @param [Fixnum] offset optional offset of the retrieved data in the buffer.
|
422
|
-
def actual_buffer(offset=0)
|
423
|
-
@transfer[:buffer].get_bytes(offset, @transfer[:actual_length])
|
424
|
-
end
|
425
|
-
|
426
|
-
# Set the block that will be invoked when the transfer completes,
|
427
|
-
# fails, or is cancelled.
|
428
|
-
#
|
429
|
-
# @param [Proc] proc The block that should be called
|
430
|
-
def callback=(proc)
|
431
|
-
# Save proc to instance variable so that GC doesn't free
|
432
|
-
# the proc object before the transfer.
|
433
|
-
@callback_proc = proc do |pTrans|
|
434
|
-
proc.call(self)
|
435
|
-
end
|
436
|
-
@transfer[:callback] = @callback_proc
|
437
|
-
end
|
438
|
-
|
439
|
-
# The status of the transfer.
|
440
|
-
#
|
441
|
-
# Only for use within transfer callback function or after the callback was called.
|
442
|
-
#
|
443
|
-
# If this is an isochronous transfer, this field may read :TRANSFER_COMPLETED even if there
|
444
|
-
# were errors in the frames. Use the status field in each packet to determine if
|
445
|
-
# errors occurred.
|
446
|
-
def status
|
447
|
-
@transfer[:status]
|
448
|
-
end
|
449
|
-
|
450
|
-
# Submit a transfer.
|
451
|
-
#
|
452
|
-
# This function will fire off the USB transfer and then return immediately.
|
453
|
-
# This method can be called with block. It is called when the transfer completes,
|
454
|
-
# fails, or is cancelled.
|
455
|
-
def submit!(&block)
|
456
|
-
self.callback = block if block_given?
|
457
|
-
|
458
|
-
# 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}"
|
459
|
-
|
460
|
-
res = Call.libusb_submit_transfer( @transfer )
|
461
|
-
LIBUSB.raise_error res, "in libusb_submit_transfer" if res!=0
|
462
|
-
end
|
463
|
-
|
464
|
-
# Asynchronously cancel a previously submitted transfer.
|
465
|
-
#
|
466
|
-
# This function returns immediately, but this does not indicate cancellation is
|
467
|
-
# complete. Your callback function will be invoked at some later time with a
|
468
|
-
# transfer status of :TRANSFER_CANCELLED.
|
469
|
-
def cancel!
|
470
|
-
res = Call.libusb_cancel_transfer( @transfer )
|
471
|
-
LIBUSB.raise_error res, "in libusb_cancel_transfer" if res!=0
|
472
|
-
end
|
473
|
-
|
474
|
-
TransferStatusToError = {
|
475
|
-
:TRANSFER_ERROR => LIBUSB::ERROR_IO,
|
476
|
-
:TRANSFER_TIMED_OUT => LIBUSB::ERROR_TIMEOUT,
|
477
|
-
:TRANSFER_CANCELLED => LIBUSB::ERROR_INTERRUPTED,
|
478
|
-
:TRANSFER_STALL => LIBUSB::ERROR_PIPE,
|
479
|
-
:TRANSFER_NO_DEVICE => LIBUSB::ERROR_NO_DEVICE,
|
480
|
-
:TRANSFER_OVERFLOW => LIBUSB::ERROR_OVERFLOW,
|
481
|
-
}
|
482
|
-
|
483
|
-
# Submit the transfer and wait until the transfer completes or fails.
|
484
|
-
#
|
485
|
-
# A proper {LIBUSB::Error} is raised, in case the transfer did not complete.
|
486
|
-
def submit_and_wait!
|
487
|
-
completed = false
|
488
|
-
submit! do |tr2|
|
489
|
-
completed = true
|
490
|
-
end
|
491
|
-
|
492
|
-
until completed
|
493
|
-
begin
|
494
|
-
@dev_handle.device.context.handle_events
|
495
|
-
rescue ERROR_INTERRUPTED
|
496
|
-
next
|
497
|
-
rescue LIBUSB::Error
|
498
|
-
cancel!
|
499
|
-
until completed
|
500
|
-
@dev_handle.device.context.handle_events
|
501
|
-
end
|
502
|
-
raise
|
503
|
-
end
|
504
|
-
end
|
505
|
-
|
506
|
-
raise( TransferStatusToError[status] || ERROR_OTHER, "error #{status}") unless status==:TRANSFER_COMPLETED
|
507
|
-
end
|
508
|
-
end
|
509
|
-
|
510
|
-
class BulkTransfer < Transfer
|
511
|
-
def initialize(args={})
|
512
|
-
@transfer = Call::Transfer.new Call.libusb_alloc_transfer(0)
|
513
|
-
@transfer[:type] = TRANSFER_TYPE_BULK
|
514
|
-
@transfer[:timeout] = 1000
|
515
|
-
super
|
516
|
-
end
|
517
|
-
end
|
518
|
-
|
519
|
-
class ControlTransfer < Transfer
|
520
|
-
def initialize(args={})
|
521
|
-
@transfer = Call::Transfer.new Call.libusb_alloc_transfer(0)
|
522
|
-
@transfer[:type] = TRANSFER_TYPE_CONTROL
|
523
|
-
@transfer[:timeout] = 1000
|
524
|
-
super
|
525
|
-
end
|
526
|
-
end
|
527
|
-
|
528
|
-
class InterruptTransfer < Transfer
|
529
|
-
def initialize(args={})
|
530
|
-
@transfer = Call::Transfer.new Call.libusb_alloc_transfer(0)
|
531
|
-
@transfer[:type] = TRANSFER_TYPE_INTERRUPT
|
532
|
-
@transfer[:timeout] = 1000
|
533
|
-
super
|
534
|
-
end
|
535
|
-
end
|
536
|
-
|
537
|
-
class IsoPacket
|
538
|
-
def initialize(ptr, pkg_nr)
|
539
|
-
@packet = Call::IsoPacketDescriptor.new ptr
|
540
|
-
@pkg_nr = pkg_nr
|
541
|
-
end
|
542
|
-
|
543
|
-
def status
|
544
|
-
@packet[:status]
|
545
|
-
end
|
546
|
-
|
547
|
-
def length
|
548
|
-
@packet[:length]
|
549
|
-
end
|
550
|
-
def length=(len)
|
551
|
-
@packet[:length] = len
|
552
|
-
end
|
553
|
-
|
554
|
-
def actual_length
|
555
|
-
@packet[:actual_length]
|
556
|
-
end
|
557
|
-
end
|
558
|
-
|
559
|
-
class IsochronousTransfer < Transfer
|
560
|
-
def initialize(num_packets, args={})
|
561
|
-
@ptr = Call.libusb_alloc_transfer(num_packets)
|
562
|
-
@transfer = Call::Transfer.new @ptr
|
563
|
-
@transfer[:type] = TRANSFER_TYPE_ISOCHRONOUS
|
564
|
-
@transfer[:timeout] = 1000
|
565
|
-
@transfer[:num_iso_packets] = num_packets
|
566
|
-
super(args)
|
567
|
-
end
|
568
|
-
|
569
|
-
def num_packets
|
570
|
-
@transfer[:num_iso_packets]
|
571
|
-
end
|
572
|
-
def num_packets=(number)
|
573
|
-
@transfer[:num_iso_packets] = number
|
574
|
-
end
|
575
|
-
|
576
|
-
def [](nr)
|
577
|
-
IsoPacket.new( @ptr + Call::Transfer.size + nr*Call::IsoPacketDescriptor.size, nr)
|
578
|
-
end
|
579
|
-
|
580
|
-
# Convenience function to set the length of all packets in an
|
581
|
-
# isochronous transfer, based on {IsochronousTransfer#num_packets}.
|
582
|
-
def packet_lengths=(len)
|
583
|
-
ptr = @ptr + Call::Transfer.size
|
584
|
-
num_packets.times do
|
585
|
-
ptr.write_uint(len)
|
586
|
-
ptr += Call::IsoPacketDescriptor.size
|
587
|
-
end
|
588
|
-
end
|
589
|
-
|
590
|
-
# The actual_length field of the transfer is meaningless and should not
|
591
|
-
# be examined; instead you must refer to the actual_length field of
|
592
|
-
# each individual packet.
|
593
|
-
private :actual_length, :actual_buffer
|
594
|
-
end
|
595
|
-
|
596
|
-
# @private
|
597
|
-
class DeviceDescriptor < FFI::Struct
|
598
|
-
include Comparable
|
599
|
-
|
600
|
-
layout :bLength, :uint8,
|
601
|
-
:bDescriptorType, :uint8,
|
602
|
-
:bcdUSB, :uint16,
|
603
|
-
:bDeviceClass, :uint8,
|
604
|
-
:bDeviceSubClass, :uint8,
|
605
|
-
:bDeviceProtocol, :uint8,
|
606
|
-
:bMaxPacketSize0, :uint8,
|
607
|
-
:idVendor, :uint16,
|
608
|
-
:idProduct, :uint16,
|
609
|
-
:bcdDevice, :uint16,
|
610
|
-
:iManufacturer, :uint8,
|
611
|
-
:iProduct, :uint8,
|
612
|
-
:iSerialNumber, :uint8,
|
613
|
-
:bNumConfigurations, :uint8
|
614
|
-
end
|
615
|
-
|
616
|
-
class Configuration < FFI::ManagedStruct
|
617
|
-
include Comparable
|
618
|
-
|
619
|
-
layout :bLength, :uint8,
|
620
|
-
:bDescriptorType, :uint8,
|
621
|
-
:wTotalLength, :uint16,
|
622
|
-
:bNumInterfaces, :uint8,
|
623
|
-
:bConfigurationValue, :uint8,
|
624
|
-
:iConfiguration, :uint8,
|
625
|
-
:bmAttributes, :uint8,
|
626
|
-
:maxPower, :uint8,
|
627
|
-
:interface, :pointer,
|
628
|
-
:extra, :pointer,
|
629
|
-
:extra_length, :int
|
630
|
-
|
631
|
-
# Size of this descriptor (in bytes).
|
632
|
-
def bLength
|
633
|
-
self[:bLength]
|
634
|
-
end
|
635
|
-
|
636
|
-
# Descriptor type (0x02)
|
637
|
-
def bDescriptorType
|
638
|
-
self[:bDescriptorType]
|
639
|
-
end
|
640
|
-
|
641
|
-
# Total length of data returned for this configuration.
|
642
|
-
def wTotalLength
|
643
|
-
self[:wTotalLength]
|
644
|
-
end
|
645
|
-
|
646
|
-
# Number of interfaces supported by this configuration.
|
647
|
-
def bNumInterfaces
|
648
|
-
self[:bNumInterfaces]
|
649
|
-
end
|
650
|
-
|
651
|
-
# Identifier value for this configuration.
|
652
|
-
def bConfigurationValue
|
653
|
-
self[:bConfigurationValue]
|
654
|
-
end
|
655
|
-
|
656
|
-
# Index of string descriptor describing this configuration.
|
657
|
-
def iConfiguration
|
658
|
-
self[:iConfiguration]
|
659
|
-
end
|
660
|
-
|
661
|
-
# Configuration characteristics.
|
662
|
-
#
|
663
|
-
# * Bit 7: Reserved, set to 1. (USB 1.0 Bus Powered)
|
664
|
-
# * Bit 6: Self Powered
|
665
|
-
# * Bit 5: Remote Wakeup
|
666
|
-
# * Bit 4..0: Reserved, set to 0.
|
667
|
-
#
|
668
|
-
# @return [Integer]
|
669
|
-
def bmAttributes
|
670
|
-
self[:bmAttributes]
|
671
|
-
end
|
672
|
-
|
673
|
-
# Maximum power consumption of the USB device from this bus in this configuration when the device is fully opreation.
|
674
|
-
#
|
675
|
-
# @result [Integer] Maximum Power Consumption in 2mA units
|
676
|
-
def maxPower
|
677
|
-
self[:maxPower]
|
678
|
-
end
|
679
|
-
|
680
|
-
# Extra descriptors.
|
681
|
-
#
|
682
|
-
# @return [String]
|
683
|
-
def extra
|
684
|
-
return if self[:extra].null?
|
685
|
-
self[:extra].read_string(self[:extra_length])
|
686
|
-
end
|
687
|
-
|
688
|
-
def initialize(device, *args)
|
689
|
-
@device = device
|
690
|
-
super(*args)
|
691
|
-
end
|
692
|
-
|
693
|
-
def self.release(ptr)
|
694
|
-
Call.libusb_free_config_descriptor(ptr)
|
695
|
-
end
|
696
|
-
|
697
|
-
# @return [Device] the device this configuration belongs to.
|
698
|
-
attr_reader :device
|
699
|
-
|
700
|
-
def interfaces
|
701
|
-
ifs = []
|
702
|
-
self[:bNumInterfaces].times do |i|
|
703
|
-
ifs << Interface.new(self, self[:interface] + i*Interface.size)
|
704
|
-
end
|
705
|
-
return ifs
|
706
|
-
end
|
707
|
-
|
708
|
-
def inspect
|
709
|
-
attrs = []
|
710
|
-
attrs << self.bConfigurationValue.to_s
|
711
|
-
bits = self.bmAttributes
|
712
|
-
attrs << "SelfPowered" if (bits & 0b1000000) != 0
|
713
|
-
attrs << "RemoteWakeup" if (bits & 0b100000) != 0
|
714
|
-
desc = self.description
|
715
|
-
attrs << desc if desc != '?'
|
716
|
-
"\#<#{self.class} #{attrs.join(' ')}>"
|
717
|
-
end
|
718
|
-
|
719
|
-
# Return name of this configuration as String.
|
720
|
-
def description
|
721
|
-
return @description if defined? @description
|
722
|
-
@description = device.try_string_descriptor_ascii(self.iConfiguration)
|
723
|
-
end
|
724
|
-
|
725
|
-
# Return all interface decriptions of the configuration as Array of {Setting}s.
|
726
|
-
def settings() self.interfaces.map {|d| d.settings }.flatten end
|
727
|
-
# Return all endpoints of all interfaces of the configuration as Array of {Endpoint}s.
|
728
|
-
def endpoints() self.settings.map {|d| d.endpoints }.flatten end
|
729
|
-
|
730
|
-
def <=>(o)
|
731
|
-
t = device<=>o.device
|
732
|
-
t = bConfigurationValue<=>o.bConfigurationValue if t==0
|
733
|
-
t
|
734
|
-
end
|
735
|
-
end
|
736
|
-
|
737
|
-
class Interface < FFI::Struct
|
738
|
-
include Comparable
|
739
|
-
|
740
|
-
layout :altsetting, :pointer,
|
741
|
-
:num_altsetting, :int
|
742
|
-
|
743
|
-
def initialize(configuration, *args)
|
744
|
-
@configuration = configuration
|
745
|
-
super(*args)
|
746
|
-
end
|
747
|
-
|
748
|
-
# @return [Configuration] the configuration this interface belongs to.
|
749
|
-
attr_reader :configuration
|
750
|
-
|
751
|
-
def alt_settings
|
752
|
-
ifs = []
|
753
|
-
self[:num_altsetting].times do |i|
|
754
|
-
ifs << Setting.new(self, self[:altsetting] + i*Setting.size)
|
755
|
-
end
|
756
|
-
return ifs
|
757
|
-
end
|
758
|
-
alias settings alt_settings
|
759
|
-
|
760
|
-
# The {Device} this Interface belongs to.
|
761
|
-
def device() self.configuration.device end
|
762
|
-
# Return all endpoints of all alternative settings as Array of {Endpoint}s.
|
763
|
-
def endpoints() self.alt_settings.map {|d| d.endpoints }.flatten end
|
764
|
-
|
765
|
-
def <=>(o)
|
766
|
-
configuration<=>o.configuration
|
767
|
-
end
|
768
|
-
end
|
769
|
-
|
770
|
-
class Setting < FFI::Struct
|
771
|
-
include Comparable
|
772
|
-
|
773
|
-
layout :bLength, :uint8,
|
774
|
-
:bDescriptorType, :uint8,
|
775
|
-
:bInterfaceNumber, :uint8,
|
776
|
-
:bAlternateSetting, :uint8,
|
777
|
-
:bNumEndpoints, :uint8,
|
778
|
-
:bInterfaceClass, :uint8,
|
779
|
-
:bInterfaceSubClass, :uint8,
|
780
|
-
:bInterfaceProtocol, :uint8,
|
781
|
-
:iInterface, :uint8,
|
782
|
-
:endpoint, :pointer,
|
783
|
-
:extra, :pointer,
|
784
|
-
:extra_length, :int
|
785
|
-
|
786
|
-
# Size of this descriptor (in bytes).
|
787
|
-
def bLength
|
788
|
-
self[:bLength]
|
789
|
-
end
|
17
|
+
VERSION = "0.2.0"
|
790
18
|
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
self[:bAlternateSetting]
|
804
|
-
end
|
805
|
-
|
806
|
-
# Number of endpoints used by this interface (excluding the control endpoint).
|
807
|
-
def bNumEndpoints
|
808
|
-
self[:bNumEndpoints]
|
809
|
-
end
|
810
|
-
|
811
|
-
# USB-IF class code for this interface.
|
812
|
-
def bInterfaceClass
|
813
|
-
self[:bInterfaceClass]
|
814
|
-
end
|
815
|
-
|
816
|
-
# USB-IF subclass code for this interface, qualified by the {bInterfaceClass} value.
|
817
|
-
def bInterfaceSubClass
|
818
|
-
self[:bInterfaceSubClass]
|
819
|
-
end
|
820
|
-
|
821
|
-
# USB-IF protocol code for this interface, qualified by the {bInterfaceClass} and {bInterfaceSubClass} values.
|
822
|
-
def bInterfaceProtocol
|
823
|
-
self[:bInterfaceProtocol]
|
824
|
-
end
|
825
|
-
|
826
|
-
# Index of string descriptor describing this interface.
|
827
|
-
def iInterface
|
828
|
-
self[:iInterface]
|
829
|
-
end
|
830
|
-
|
831
|
-
# Extra descriptors.
|
832
|
-
#
|
833
|
-
# @return [String]
|
834
|
-
def extra
|
835
|
-
return if self[:extra].null?
|
836
|
-
self[:extra].read_string(self[:extra_length])
|
837
|
-
end
|
838
|
-
|
839
|
-
def initialize(interface, *args)
|
840
|
-
@interface = interface
|
841
|
-
super(*args)
|
842
|
-
end
|
843
|
-
|
844
|
-
# @return [Interface] the interface this setting belongs to.
|
845
|
-
attr_reader :interface
|
846
|
-
|
847
|
-
def endpoints
|
848
|
-
ifs = []
|
849
|
-
self[:bNumEndpoints].times do |i|
|
850
|
-
ifs << Endpoint.new(self, self[:endpoint] + i*Endpoint.size)
|
851
|
-
end
|
852
|
-
return ifs
|
853
|
-
end
|
854
|
-
|
855
|
-
def inspect
|
856
|
-
attrs = []
|
857
|
-
attrs << self.bAlternateSetting.to_s
|
858
|
-
devclass = LIBUSB.dev_string(self.bInterfaceClass, self.bInterfaceSubClass, self.bInterfaceProtocol)
|
859
|
-
attrs << devclass
|
860
|
-
desc = self.description
|
861
|
-
attrs << desc if desc != '?'
|
862
|
-
"\#<#{self.class} #{attrs.join(' ')}>"
|
863
|
-
end
|
864
|
-
|
865
|
-
# Return name of this interface as String.
|
866
|
-
def description
|
867
|
-
return @description if defined? @description
|
868
|
-
@description = device.try_string_descriptor_ascii(self.iInterface)
|
869
|
-
end
|
870
|
-
|
871
|
-
# The {Device} this Setting belongs to.
|
872
|
-
def device() self.interface.configuration.device end
|
873
|
-
# The {Configuration} this Setting belongs to.
|
874
|
-
def configuration() self.interface.configuration end
|
875
|
-
|
876
|
-
def <=>(o)
|
877
|
-
t = interface<=>o.interface
|
878
|
-
t = bInterfaceNumber<=>o.bInterfaceNumber if t==0
|
879
|
-
t = bAlternateSetting<=>o.bAlternateSetting if t==0
|
880
|
-
t
|
881
|
-
end
|
19
|
+
require 'libusb/call'
|
20
|
+
require 'libusb/constants'
|
21
|
+
require 'libusb/context'
|
22
|
+
autoload :Version, 'libusb/version'
|
23
|
+
autoload :Configuration, 'libusb/configuration'
|
24
|
+
autoload :DevHandle, 'libusb/dev_handle'
|
25
|
+
autoload :Device, 'libusb/device'
|
26
|
+
autoload :Endpoint, 'libusb/endpoint'
|
27
|
+
autoload :Interface, 'libusb/interface'
|
28
|
+
autoload :Setting, 'libusb/setting'
|
29
|
+
%w[ Transfer BulkTransfer ControlTransfer InterruptTransfer IsoPacket IsochronousTransfer ].each do |klass|
|
30
|
+
autoload klass, 'libusb/transfer'
|
882
31
|
end
|
883
32
|
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
:bmAttributes, :uint8,
|
891
|
-
:wMaxPacketSize, :uint16,
|
892
|
-
:bInterval, :uint8,
|
893
|
-
:bRefresh, :uint8,
|
894
|
-
:bSynchAddress, :uint8,
|
895
|
-
:extra, :pointer,
|
896
|
-
:extra_length, :int
|
897
|
-
|
898
|
-
# Size of Descriptor in Bytes (7 bytes)
|
899
|
-
def bLength
|
900
|
-
self[:bLength]
|
901
|
-
end
|
902
|
-
|
903
|
-
# Descriptor type (0x05)
|
904
|
-
def bDescriptorType
|
905
|
-
self[:bDescriptorType]
|
906
|
-
end
|
907
|
-
|
908
|
-
# The address of the endpoint described by this descriptor.
|
909
|
-
#
|
910
|
-
# * Bits 0..3: Endpoint Number.
|
911
|
-
# * Bits 4..6: Reserved. Set to Zero
|
912
|
-
# * Bits 7: Direction 0 = Out, 1 = In (Ignored for Control Endpoints)
|
913
|
-
#
|
914
|
-
# @return [Integer]
|
915
|
-
def bEndpointAddress
|
916
|
-
self[:bEndpointAddress]
|
917
|
-
end
|
918
|
-
|
919
|
-
# Attributes which apply to the endpoint when it is configured using the {Configuration#bConfigurationValue}.
|
920
|
-
#
|
921
|
-
# * Bits 0..1: Transfer Type
|
922
|
-
# * 00 = Control
|
923
|
-
# * 01 = Isochronous
|
924
|
-
# * 10 = Bulk
|
925
|
-
# * 11 = Interrupt
|
926
|
-
# * Bits 2..7: are reserved. If Isochronous endpoint,
|
927
|
-
# * Bits 3..2: Synchronisation Type (Iso Mode)
|
928
|
-
# * 00 = No Synchonisation
|
929
|
-
# * 01 = Asynchronous
|
930
|
-
# * 10 = Adaptive
|
931
|
-
# * 11 = Synchronous
|
932
|
-
# * Bits 5..4: Usage Type (Iso Mode)
|
933
|
-
# * 00 = Data Endpoint
|
934
|
-
# * 01 = Feedback Endpoint
|
935
|
-
# * 10 = Explicit Feedback Data Endpoint
|
936
|
-
# * 11 = Reserved
|
937
|
-
#
|
938
|
-
# @return [Integer]
|
939
|
-
def bmAttributes
|
940
|
-
self[:bmAttributes]
|
941
|
-
end
|
942
|
-
|
943
|
-
# Maximum Packet Size this endpoint is capable of sending or receiving
|
944
|
-
def wMaxPacketSize
|
945
|
-
self[:wMaxPacketSize]
|
946
|
-
end
|
947
|
-
|
948
|
-
# Interval for polling endpoint data transfers. Value in frame counts.
|
949
|
-
# Ignored for Bulk & Control Endpoints. Isochronous must equal 1 and field
|
950
|
-
# may range from 1 to 255 for interrupt endpoints.
|
951
|
-
#
|
952
|
-
# The interval is respected by the kernel driver, so user mode processes
|
953
|
-
# using libusb don't need to care about it.
|
954
|
-
def bInterval
|
955
|
-
self[:bInterval]
|
956
|
-
end
|
957
|
-
|
958
|
-
# For audio devices only: the rate at which synchronization feedback is provided.
|
959
|
-
def bRefresh
|
960
|
-
self[:bRefresh]
|
961
|
-
end
|
962
|
-
|
963
|
-
# For audio devices only: the address if the synch endpoint.
|
964
|
-
def bSynchAddress
|
965
|
-
self[:bSynchAddress]
|
966
|
-
end
|
967
|
-
|
968
|
-
# Extra descriptors.
|
969
|
-
#
|
970
|
-
# @return [String]
|
971
|
-
def extra
|
972
|
-
return if self[:extra].null?
|
973
|
-
self[:extra].read_string(self[:extra_length])
|
974
|
-
end
|
975
|
-
|
976
|
-
def initialize(setting, *args)
|
977
|
-
@setting = setting
|
978
|
-
super(*args)
|
979
|
-
end
|
980
|
-
|
981
|
-
# @return [Setting] the setting this endpoint belongs to.
|
982
|
-
attr_reader :setting
|
983
|
-
|
984
|
-
def inspect
|
985
|
-
endpoint_address = self.bEndpointAddress
|
986
|
-
num = endpoint_address & 0b00001111
|
987
|
-
inout = (endpoint_address & 0b10000000) == 0 ? "OUT" : "IN "
|
988
|
-
bits = self.bmAttributes
|
989
|
-
transfer_type = %w[Control Isochronous Bulk Interrupt][0b11 & bits]
|
990
|
-
type = [transfer_type]
|
991
|
-
if transfer_type == 'Isochronous'
|
992
|
-
synchronization_type = %w[NoSynchronization Asynchronous Adaptive Synchronous][(0b1100 & bits) >> 2]
|
993
|
-
usage_type = %w[Data Feedback ImplicitFeedback ?][(0b110000 & bits) >> 4]
|
994
|
-
type << synchronization_type << usage_type
|
995
|
-
end
|
996
|
-
"\#<#{self.class} #{num} #{inout} #{type.join(" ")}>"
|
997
|
-
end
|
998
|
-
|
999
|
-
# The {Device} this Endpoint belongs to.
|
1000
|
-
def device() self.setting.interface.configuration.device end
|
1001
|
-
# The {Configuration} this Endpoint belongs to.
|
1002
|
-
def configuration() self.setting.interface.configuration end
|
1003
|
-
# The {Interface} this Endpoint belongs to.
|
1004
|
-
def interface() self.setting.interface end
|
1005
|
-
|
1006
|
-
def <=>(o)
|
1007
|
-
t = setting<=>o.setting
|
1008
|
-
t = bEndpointAddress<=>o.bEndpointAddress if t==0
|
1009
|
-
t
|
33
|
+
if Call.respond_to?(:libusb_get_version)
|
34
|
+
# Get version of the underlying libusb library.
|
35
|
+
# Available since libusb-1.0.10.
|
36
|
+
# @return [Version] version object
|
37
|
+
def self.version
|
38
|
+
Version.new(Call.libusb_get_version)
|
1010
39
|
end
|
1011
40
|
end
|
1012
41
|
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
#
|
1017
|
-
|
1018
|
-
|
1019
|
-
Call.
|
1020
|
-
|
1021
|
-
end
|
1022
|
-
|
1023
|
-
# Deinitialize libusb.
|
1024
|
-
#
|
1025
|
-
# Should be called after closing all open devices and before your application terminates.
|
1026
|
-
def exit
|
1027
|
-
Call.libusb_exit(@ctx)
|
1028
|
-
end
|
1029
|
-
|
1030
|
-
# Set message verbosity.
|
1031
|
-
#
|
1032
|
-
# * Level 0: no messages ever printed by the library (default)
|
1033
|
-
# * Level 1: error messages are printed to stderr
|
1034
|
-
# * Level 2: warning and error messages are printed to stderr
|
1035
|
-
# * Level 3: informational messages are printed to stdout, warning and
|
1036
|
-
# error messages are printed to stderr
|
1037
|
-
#
|
1038
|
-
# The default level is 0, which means no messages are ever printed. If you
|
1039
|
-
# choose to increase the message verbosity level, ensure that your
|
1040
|
-
# application does not close the stdout/stderr file descriptors.
|
1041
|
-
#
|
1042
|
-
# You are advised to set level 3. libusb is conservative with its message
|
1043
|
-
# logging and most of the time, will only log messages that explain error
|
1044
|
-
# conditions and other oddities. This will help you debug your software.
|
1045
|
-
#
|
1046
|
-
# If the LIBUSB_DEBUG environment variable was set when libusb was
|
1047
|
-
# initialized, this method does nothing: the message verbosity is
|
1048
|
-
# fixed to the value in the environment variable.
|
1049
|
-
#
|
1050
|
-
# If libusb was compiled without any message logging, this method
|
1051
|
-
# does nothing: you'll never get any messages.
|
1052
|
-
#
|
1053
|
-
# If libusb was compiled with verbose debug message logging, this
|
1054
|
-
# method does nothing: you'll always get messages from all levels.
|
1055
|
-
#
|
1056
|
-
# @param [Fixnum] level debug level to set
|
1057
|
-
def debug=(level)
|
1058
|
-
Call.libusb_set_debug(@ctx, level)
|
1059
|
-
end
|
1060
|
-
|
1061
|
-
def device_list
|
1062
|
-
pppDevs = FFI::MemoryPointer.new :pointer
|
1063
|
-
size = Call.libusb_get_device_list(@ctx, pppDevs)
|
1064
|
-
ppDevs = pppDevs.read_pointer
|
1065
|
-
pDevs = []
|
1066
|
-
size.times do |devi|
|
1067
|
-
pDev = ppDevs.get_pointer(devi*FFI.type_size(:pointer))
|
1068
|
-
pDevs << Device.new(self, pDev)
|
1069
|
-
end
|
1070
|
-
Call.libusb_free_device_list(ppDevs, 1)
|
1071
|
-
pDevs
|
1072
|
-
end
|
1073
|
-
private :device_list
|
1074
|
-
|
1075
|
-
# Handle any pending events in blocking mode.
|
1076
|
-
#
|
1077
|
-
# This method must be called when libusb is running asynchronous transfers.
|
1078
|
-
# This gives libusb the opportunity to reap pending transfers,
|
1079
|
-
# invoke callbacks, etc.
|
1080
|
-
def handle_events
|
1081
|
-
res = Call.libusb_handle_events(@ctx)
|
1082
|
-
LIBUSB.raise_error res, "in libusb_handle_events" if res<0
|
1083
|
-
end
|
1084
|
-
|
1085
|
-
# Obtain a list of devices currently attached to the USB system, optionally matching certain criteria.
|
1086
|
-
#
|
1087
|
-
# @param [Hash] filter_hash A number of criteria can be defined in key-value pairs.
|
1088
|
-
# Only devices that equal all given criterions will be returned. If a criterion is
|
1089
|
-
# not specified or its value is +nil+, any device will match that criterion.
|
1090
|
-
# The following criteria can be filtered:
|
1091
|
-
# * <tt>:idVendor</tt>, <tt>:idProduct</tt> (+FixNum+) for matching vendor/product ID,
|
1092
|
-
# * <tt>:bClass</tt>, <tt>:bSubClass</tt>, <tt>:bProtocol</tt> (+FixNum+) for the device type -
|
1093
|
-
# Devices using CLASS_PER_INTERFACE will match, if any of the interfaces match.
|
1094
|
-
# * <tt>:bcdUSB</tt>, <tt>:bcdDevice</tt>, <tt>:bMaxPacketSize0</tt> (+FixNum+) for the
|
1095
|
-
# USB and device release numbers.
|
1096
|
-
# Criteria can also specified as Array of several alternative values.
|
1097
|
-
#
|
1098
|
-
# @example
|
1099
|
-
# # Return all devices of vendor 0x0ab1 where idProduct is 3 or 4:
|
1100
|
-
# context.device :idVendor=>0x0ab1, :idProduct=>[0x0003, 0x0004]
|
1101
|
-
#
|
1102
|
-
# @return [Array<LIBUSB::Device>]
|
1103
|
-
def devices(filter_hash={})
|
1104
|
-
device_list.select do |dev|
|
1105
|
-
( !filter_hash[:bClass] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
|
1106
|
-
dev.settings.map(&:bInterfaceClass).&([filter_hash[:bClass]].flatten).any? :
|
1107
|
-
[filter_hash[:bClass]].flatten.include?(dev.bDeviceClass))) &&
|
1108
|
-
( !filter_hash[:bSubClass] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
|
1109
|
-
dev.settings.map(&:bInterfaceSubClass).&([filter_hash[:bSubClass]].flatten).any? :
|
1110
|
-
[filter_hash[:bSubClass]].flatten.include?(dev.bDeviceSubClass))) &&
|
1111
|
-
( !filter_hash[:bProtocol] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
|
1112
|
-
dev.settings.map(&:bInterfaceProtocol).&([filter_hash[:bProtocol]].flatten).any? :
|
1113
|
-
[filter_hash[:bProtocol]].flatten.include?(dev.bDeviceProtocol))) &&
|
1114
|
-
( !filter_hash[:bMaxPacketSize0] || [filter_hash[:bMaxPacketSize0]].flatten.include?(dev.bMaxPacketSize0) ) &&
|
1115
|
-
( !filter_hash[:idVendor] || [filter_hash[:idVendor]].flatten.include?(dev.idVendor) ) &&
|
1116
|
-
( !filter_hash[:idProduct] || [filter_hash[:idProduct]].flatten.include?(dev.idProduct) ) &&
|
1117
|
-
( !filter_hash[:bcdUSB] || [filter_hash[:bcdUSB]].flatten.include?(dev.bcdUSB) ) &&
|
1118
|
-
( !filter_hash[:bcdDevice] || [filter_hash[:bcdDevice]].flatten.include?(dev.bcdDevice) )
|
1119
|
-
end
|
42
|
+
if Call.respond_to?(:libusb_has_capability)
|
43
|
+
# Check at runtime if the loaded library has a given capability.
|
44
|
+
# Available since libusb-1.0.9.
|
45
|
+
# @param [Symbol] capability the {Call::Capabilities Capabilities} symbol to check for
|
46
|
+
# @return [Boolean] +true+ if the running library has the capability, +false+ otherwise
|
47
|
+
def self.has_capability?(capability)
|
48
|
+
r = Call.libusb_has_capability(capability)
|
49
|
+
return r != 0
|
1120
50
|
end
|
1121
51
|
end
|
1122
|
-
|
1123
|
-
# Class representing a USB device detected on the system.
|
1124
|
-
#
|
1125
|
-
# Devices of the system can be obtained with {Context#devices} .
|
1126
|
-
class Device
|
1127
|
-
include Comparable
|
1128
|
-
|
1129
|
-
# @return [Context] the context this device belongs to.
|
1130
|
-
attr_reader :context
|
1131
|
-
|
1132
|
-
def initialize context, pDev
|
1133
|
-
@context = context
|
1134
|
-
def pDev.unref_device(id)
|
1135
|
-
Call.libusb_unref_device(self)
|
1136
|
-
end
|
1137
|
-
ObjectSpace.define_finalizer(self, pDev.method(:unref_device))
|
1138
|
-
Call.libusb_ref_device(pDev)
|
1139
|
-
@pDev = pDev
|
1140
|
-
|
1141
|
-
@pDevDesc = DeviceDescriptor.new
|
1142
|
-
res = Call.libusb_get_device_descriptor(@pDev, @pDevDesc)
|
1143
|
-
LIBUSB.raise_error res, "in libusb_get_device_descriptor" if res!=0
|
1144
|
-
end
|
1145
|
-
|
1146
|
-
# Open the device and obtain a device handle.
|
1147
|
-
#
|
1148
|
-
# A handle allows you to perform I/O on the device in question.
|
1149
|
-
# This is a non-blocking function; no requests are sent over the bus.
|
1150
|
-
#
|
1151
|
-
# If called with a block, the handle is passed to the block
|
1152
|
-
# and is closed when the block has finished.
|
1153
|
-
#
|
1154
|
-
# You need proper access permissions on:
|
1155
|
-
# * Linux: <tt>/dev/bus/usb/<bus>/<dev></tt>
|
1156
|
-
#
|
1157
|
-
# @return [DevHandle] Handle to the device.
|
1158
|
-
def open
|
1159
|
-
ppHandle = FFI::MemoryPointer.new :pointer
|
1160
|
-
res = Call.libusb_open(@pDev, ppHandle)
|
1161
|
-
LIBUSB.raise_error res, "in libusb_open" if res!=0
|
1162
|
-
handle = DevHandle.new self, ppHandle.read_pointer
|
1163
|
-
return handle unless block_given?
|
1164
|
-
begin
|
1165
|
-
yield handle
|
1166
|
-
ensure
|
1167
|
-
handle.close
|
1168
|
-
end
|
1169
|
-
end
|
1170
|
-
|
1171
|
-
# Open the device and claim an interface.
|
1172
|
-
#
|
1173
|
-
# This is a convenience method to {Device#open} and {DevHandle#claim_interface}.
|
1174
|
-
# Must be called with a block. When the block has finished, the interface
|
1175
|
-
# will be released and the device will be closed.
|
1176
|
-
#
|
1177
|
-
# @param [Interface, Fixnum] interface the interface or it's bInterfaceNumber you wish to claim
|
1178
|
-
def open_interface(interface)
|
1179
|
-
open do |dev|
|
1180
|
-
dev.claim_interface(interface) do
|
1181
|
-
yield dev
|
1182
|
-
end
|
1183
|
-
end
|
1184
|
-
end
|
1185
|
-
|
1186
|
-
# Get the number of the bus that a device is connected to.
|
1187
|
-
def bus_number
|
1188
|
-
Call.libusb_get_bus_number(@pDev)
|
1189
|
-
end
|
1190
|
-
|
1191
|
-
# Get the address of the device on the bus it is connected to.
|
1192
|
-
def device_address
|
1193
|
-
Call.libusb_get_device_address(@pDev)
|
1194
|
-
end
|
1195
|
-
|
1196
|
-
# Convenience function to retrieve the wMaxPacketSize value for a
|
1197
|
-
# particular endpoint in the active device configuration.
|
1198
|
-
#
|
1199
|
-
# @param [Endpoint, Fixnum] endpoint (address of) the endpoint in question
|
1200
|
-
# @return [Fixnum] the wMaxPacketSize value
|
1201
|
-
def max_packet_size(endpoint)
|
1202
|
-
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
1203
|
-
res = Call.libusb_get_max_packet_size(@pDev, endpoint)
|
1204
|
-
LIBUSB.raise_error res, "in libusb_get_max_packet_size" unless res>=0
|
1205
|
-
res
|
1206
|
-
end
|
1207
|
-
|
1208
|
-
# Calculate the maximum packet size which a specific endpoint is capable is
|
1209
|
-
# sending or receiving in the duration of 1 microframe.
|
1210
|
-
#
|
1211
|
-
# Only the active configution is examined. The calculation is based on the
|
1212
|
-
# wMaxPacketSize field in the endpoint descriptor as described in section 9.6.6
|
1213
|
-
# in the USB 2.0 specifications.
|
1214
|
-
#
|
1215
|
-
# If acting on an isochronous or interrupt endpoint, this function will
|
1216
|
-
# multiply the value found in bits 0:10 by the number of transactions per
|
1217
|
-
# microframe (determined by bits 11:12). Otherwise, this function just returns
|
1218
|
-
# the numeric value found in bits 0:10.
|
1219
|
-
#
|
1220
|
-
# This function is useful for setting up isochronous transfers, for example
|
1221
|
-
# you might use the return value from this function to call
|
1222
|
-
# IsoPacket#alloc_buffer in order to set the length field
|
1223
|
-
# of an isochronous packet in a transfer.
|
1224
|
-
#
|
1225
|
-
# @param [Endpoint, Fixnum] endpoint (address of) the endpoint in question
|
1226
|
-
# @return [Fixnum] the maximum packet size which can be sent/received on this endpoint
|
1227
|
-
def max_iso_packet_size(endpoint)
|
1228
|
-
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
1229
|
-
res = Call.libusb_get_max_iso_packet_size(@pDev, endpoint)
|
1230
|
-
LIBUSB.raise_error res, "in libusb_get_max_iso_packet_size" unless res>=0
|
1231
|
-
res
|
1232
|
-
end
|
1233
|
-
|
1234
|
-
# Obtain a config descriptor of the device.
|
1235
|
-
#
|
1236
|
-
# @param [Fixnum] index number of the config descriptor
|
1237
|
-
# @return Configuration
|
1238
|
-
def config_descriptor(index)
|
1239
|
-
ppConfig = FFI::MemoryPointer.new :pointer
|
1240
|
-
res = Call.libusb_get_config_descriptor(@pDev, index, ppConfig)
|
1241
|
-
LIBUSB.raise_error res, "in libusb_get_config_descriptor" if res!=0
|
1242
|
-
pConfig = ppConfig.read_pointer
|
1243
|
-
config = Configuration.new(self, pConfig)
|
1244
|
-
config
|
1245
|
-
end
|
1246
|
-
|
1247
|
-
# Size of the Descriptor in Bytes (18 bytes)
|
1248
|
-
def bLength
|
1249
|
-
@pDevDesc[:bLength]
|
1250
|
-
end
|
1251
|
-
|
1252
|
-
# Device Descriptor (0x01)
|
1253
|
-
def bDescriptorType
|
1254
|
-
@pDevDesc[:bDescriptorType]
|
1255
|
-
end
|
1256
|
-
|
1257
|
-
# USB specification release number which device complies too
|
1258
|
-
#
|
1259
|
-
# @return [Integer] in binary-coded decimal
|
1260
|
-
def bcdUSB
|
1261
|
-
@pDevDesc[:bcdUSB]
|
1262
|
-
end
|
1263
|
-
|
1264
|
-
# USB-IF class code for the device (Assigned by USB Org)
|
1265
|
-
#
|
1266
|
-
# * If equal to 0x00, each interface specifies it's own class code
|
1267
|
-
# * If equal to 0xFF, the class code is vendor specified
|
1268
|
-
# * Otherwise field is valid Class Code
|
1269
|
-
def bDeviceClass
|
1270
|
-
@pDevDesc[:bDeviceClass]
|
1271
|
-
end
|
1272
|
-
|
1273
|
-
# USB-IF subclass code for the device, qualified by the {bDeviceClass}
|
1274
|
-
# value (Assigned by USB Org)
|
1275
|
-
def bDeviceSubClass
|
1276
|
-
@pDevDesc[:bDeviceSubClass]
|
1277
|
-
end
|
1278
|
-
|
1279
|
-
# USB-IF protocol code for the device, qualified by the {bDeviceClass}
|
1280
|
-
# and {bDeviceSubClass} values (Assigned by USB Org)
|
1281
|
-
def bDeviceProtocol
|
1282
|
-
@pDevDesc[:bDeviceProtocol]
|
1283
|
-
end
|
1284
|
-
|
1285
|
-
# Maximum Packet Size for Endpoint 0. Valid Sizes are 8, 16, 32, 64
|
1286
|
-
def bMaxPacketSize0
|
1287
|
-
@pDevDesc[:bMaxPacketSize0]
|
1288
|
-
end
|
1289
|
-
|
1290
|
-
# USB-IF vendor ID (Assigned by USB Org)
|
1291
|
-
def idVendor
|
1292
|
-
@pDevDesc[:idVendor]
|
1293
|
-
end
|
1294
|
-
|
1295
|
-
# USB-IF product ID (Assigned by Manufacturer)
|
1296
|
-
def idProduct
|
1297
|
-
@pDevDesc[:idProduct]
|
1298
|
-
end
|
1299
|
-
|
1300
|
-
# Device release number in binary-coded decimal.
|
1301
|
-
def bcdDevice
|
1302
|
-
@pDevDesc[:bcdDevice]
|
1303
|
-
end
|
1304
|
-
|
1305
|
-
# Index of string descriptor describing manufacturer.
|
1306
|
-
def iManufacturer
|
1307
|
-
@pDevDesc[:iManufacturer]
|
1308
|
-
end
|
1309
|
-
|
1310
|
-
# Index of string descriptor describing product.
|
1311
|
-
def iProduct
|
1312
|
-
@pDevDesc[:iProduct]
|
1313
|
-
end
|
1314
|
-
|
1315
|
-
# Index of string descriptor containing device serial number.
|
1316
|
-
def iSerialNumber
|
1317
|
-
@pDevDesc[:iSerialNumber]
|
1318
|
-
end
|
1319
|
-
|
1320
|
-
# Number of Possible Configurations
|
1321
|
-
def bNumConfigurations
|
1322
|
-
@pDevDesc[:bNumConfigurations]
|
1323
|
-
end
|
1324
|
-
|
1325
|
-
|
1326
|
-
def inspect
|
1327
|
-
attrs = []
|
1328
|
-
attrs << "#{self.bus_number}/#{self.device_address}"
|
1329
|
-
attrs << ("%04x:%04x" % [self.idVendor, self.idProduct])
|
1330
|
-
attrs << self.manufacturer
|
1331
|
-
attrs << self.product
|
1332
|
-
attrs << self.serial_number
|
1333
|
-
if self.bDeviceClass == LIBUSB::CLASS_PER_INTERFACE
|
1334
|
-
devclass = self.settings.map {|i|
|
1335
|
-
LIBUSB.dev_string(i.bInterfaceClass, i.bInterfaceSubClass, i.bInterfaceProtocol)
|
1336
|
-
}.join(", ")
|
1337
|
-
else
|
1338
|
-
devclass = LIBUSB.dev_string(self.bDeviceClass, self.bDeviceSubClass, self.bDeviceProtocol)
|
1339
|
-
end
|
1340
|
-
attrs << "(#{devclass})"
|
1341
|
-
attrs.compact!
|
1342
|
-
"\#<#{self.class} #{attrs.join(' ')}>"
|
1343
|
-
end
|
1344
|
-
|
1345
|
-
def try_string_descriptor_ascii(i)
|
1346
|
-
begin
|
1347
|
-
open{|h| h.string_descriptor_ascii(i) }
|
1348
|
-
rescue
|
1349
|
-
"?"
|
1350
|
-
end
|
1351
|
-
end
|
1352
|
-
|
1353
|
-
# Return manufacturer of the device
|
1354
|
-
# @return String
|
1355
|
-
def manufacturer
|
1356
|
-
return @manufacturer if defined? @manufacturer
|
1357
|
-
@manufacturer = try_string_descriptor_ascii(self.iManufacturer)
|
1358
|
-
@manufacturer.strip! if @manufacturer
|
1359
|
-
@manufacturer
|
1360
|
-
end
|
1361
|
-
|
1362
|
-
# Return product name of the device.
|
1363
|
-
# @return String
|
1364
|
-
def product
|
1365
|
-
return @product if defined? @product
|
1366
|
-
@product = try_string_descriptor_ascii(self.iProduct)
|
1367
|
-
@product.strip! if @product
|
1368
|
-
@product
|
1369
|
-
end
|
1370
|
-
|
1371
|
-
# Return serial number of the device.
|
1372
|
-
# @return String
|
1373
|
-
def serial_number
|
1374
|
-
return @serial_number if defined? @serial_number
|
1375
|
-
@serial_number = try_string_descriptor_ascii(self.iSerialNumber)
|
1376
|
-
@serial_number.strip! if @serial_number
|
1377
|
-
@serial_number
|
1378
|
-
end
|
1379
|
-
|
1380
|
-
# Return configurations of the device.
|
1381
|
-
# @return [Array<Configuration>]
|
1382
|
-
def configurations
|
1383
|
-
configs = []
|
1384
|
-
bNumConfigurations.times do |config_index|
|
1385
|
-
begin
|
1386
|
-
configs << config_descriptor(config_index)
|
1387
|
-
rescue RuntimeError
|
1388
|
-
# On Windows some devices don't return it's configuration.
|
1389
|
-
end
|
1390
|
-
end
|
1391
|
-
configs
|
1392
|
-
end
|
1393
|
-
|
1394
|
-
# Return all interfaces of this device.
|
1395
|
-
# @return [Array<Interface>]
|
1396
|
-
def interfaces() self.configurations.map {|d| d.interfaces }.flatten end
|
1397
|
-
# Return all interface decriptions of this device.
|
1398
|
-
# @return [Array<Setting>]
|
1399
|
-
def settings() self.interfaces.map {|d| d.settings }.flatten end
|
1400
|
-
# Return all endpoints of all interfaces of this device.
|
1401
|
-
# @return [Array<Endpoint>]
|
1402
|
-
def endpoints() self.settings.map {|d| d.endpoints }.flatten end
|
1403
|
-
|
1404
|
-
def <=>(o)
|
1405
|
-
t = bus_number<=>o.bus_number
|
1406
|
-
t = device_address<=>o.device_address if t==0
|
1407
|
-
t
|
1408
|
-
end
|
1409
|
-
end
|
1410
|
-
|
1411
|
-
# Class representing a handle on a USB device.
|
1412
|
-
#
|
1413
|
-
# A device handle is used to perform I/O and other operations. When finished
|
1414
|
-
# with a device handle, you should call DevHandle#close .
|
1415
|
-
class DevHandle
|
1416
|
-
# @private
|
1417
|
-
attr_reader :pHandle
|
1418
|
-
# @return [Device] the device this handle belongs to.
|
1419
|
-
attr_reader :device
|
1420
|
-
|
1421
|
-
def initialize device, pHandle
|
1422
|
-
@device = device
|
1423
|
-
@pHandle = pHandle
|
1424
|
-
@bulk_transfer = @control_transfer = @interrupt_transfer = nil
|
1425
|
-
end
|
1426
|
-
|
1427
|
-
# Close a device handle.
|
1428
|
-
#
|
1429
|
-
# Should be called on all open handles before your application exits.
|
1430
|
-
#
|
1431
|
-
# Internally, this function destroys the reference that was added by {Device#open}
|
1432
|
-
# on the given device.
|
1433
|
-
#
|
1434
|
-
# This is a non-blocking function; no requests are sent over the bus.
|
1435
|
-
def close
|
1436
|
-
Call.libusb_close(@pHandle)
|
1437
|
-
end
|
1438
|
-
|
1439
|
-
def string_descriptor_ascii(index)
|
1440
|
-
pString = FFI::MemoryPointer.new 0x100
|
1441
|
-
res = Call.libusb_get_string_descriptor_ascii(@pHandle, index, pString, pString.size)
|
1442
|
-
LIBUSB.raise_error res, "in libusb_get_string_descriptor_ascii" unless res>=0
|
1443
|
-
pString.read_string(res)
|
1444
|
-
end
|
1445
|
-
|
1446
|
-
# Claim an interface on a given device handle.
|
1447
|
-
#
|
1448
|
-
# You must claim the interface you wish to use before you can perform I/O on any
|
1449
|
-
# of its endpoints.
|
1450
|
-
#
|
1451
|
-
# It is legal to attempt to claim an already-claimed interface, in which case
|
1452
|
-
# libusb just returns without doing anything.
|
1453
|
-
#
|
1454
|
-
# Claiming of interfaces is a purely logical operation; it does not cause any
|
1455
|
-
# requests to be sent over the bus. Interface claiming is used to instruct the
|
1456
|
-
# underlying operating system that your application wishes to take ownership of
|
1457
|
-
# the interface.
|
1458
|
-
#
|
1459
|
-
# This is a non-blocking function.
|
1460
|
-
#
|
1461
|
-
# If called with a block, the device handle is passed through to the block
|
1462
|
-
# and the interface is released when the block has finished.
|
1463
|
-
#
|
1464
|
-
# @param [Interface, Fixnum] interface the interface or it's bInterfaceNumber you wish to claim
|
1465
|
-
def claim_interface(interface)
|
1466
|
-
interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
|
1467
|
-
res = Call.libusb_claim_interface(@pHandle, interface)
|
1468
|
-
LIBUSB.raise_error res, "in libusb_claim_interface" if res!=0
|
1469
|
-
return self unless block_given?
|
1470
|
-
begin
|
1471
|
-
yield self
|
1472
|
-
ensure
|
1473
|
-
release_interface(interface)
|
1474
|
-
end
|
1475
|
-
end
|
1476
|
-
|
1477
|
-
# Release an interface previously claimed with {DevHandle#claim_interface}.
|
1478
|
-
#
|
1479
|
-
# You should release all claimed interfaces before closing a device handle.
|
1480
|
-
#
|
1481
|
-
# This is a blocking function. A SET_INTERFACE control request will be sent to the
|
1482
|
-
# device, resetting interface state to the first alternate setting.
|
1483
|
-
#
|
1484
|
-
# @param [Interface, Fixnum] interface the interface or it's bInterfaceNumber you
|
1485
|
-
# claimed previously
|
1486
|
-
def release_interface(interface)
|
1487
|
-
interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
|
1488
|
-
res = Call.libusb_release_interface(@pHandle, interface)
|
1489
|
-
LIBUSB.raise_error res, "in libusb_release_interface" if res!=0
|
1490
|
-
end
|
1491
|
-
|
1492
|
-
# Set the active configuration for a device.
|
1493
|
-
#
|
1494
|
-
# The operating system may or may not have already set an active configuration on
|
1495
|
-
# the device. It is up to your application to ensure the correct configuration is
|
1496
|
-
# selected before you attempt to claim interfaces and perform other operations.
|
1497
|
-
#
|
1498
|
-
# If you call this function on a device already configured with the selected
|
1499
|
-
# configuration, then this function will act as a lightweight device reset: it
|
1500
|
-
# will issue a SET_CONFIGURATION request using the current configuration, causing
|
1501
|
-
# most USB-related device state to be reset (altsetting reset to zero, endpoint
|
1502
|
-
# halts cleared, toggles reset).
|
1503
|
-
#
|
1504
|
-
# You cannot change/reset configuration if your application has claimed interfaces -
|
1505
|
-
# you should free them with {DevHandle#release_interface} first. You cannot
|
1506
|
-
# change/reset configuration if other applications or drivers have claimed
|
1507
|
-
# interfaces.
|
1508
|
-
#
|
1509
|
-
# A configuration value of +nil+ will put the device in unconfigured state. The USB
|
1510
|
-
# specifications state that a configuration value of 0 does this, however buggy
|
1511
|
-
# devices exist which actually have a configuration 0.
|
1512
|
-
#
|
1513
|
-
# You should always use this function rather than formulating your own
|
1514
|
-
# SET_CONFIGURATION control request. This is because the underlying operating
|
1515
|
-
# system needs to know when such changes happen.
|
1516
|
-
#
|
1517
|
-
# This is a blocking function.
|
1518
|
-
#
|
1519
|
-
# @param [Configuration, Fixnum] configuration the configuration or it's
|
1520
|
-
# bConfigurationValue you wish to activate, or +nil+ if you wish to put
|
1521
|
-
# the device in unconfigured state
|
1522
|
-
def set_configuration(configuration)
|
1523
|
-
configuration = configuration.bConfigurationValue if configuration.respond_to? :bConfigurationValue
|
1524
|
-
res = Call.libusb_set_configuration(@pHandle, configuration || -1)
|
1525
|
-
LIBUSB.raise_error res, "in libusb_set_configuration" if res!=0
|
1526
|
-
end
|
1527
|
-
alias configuration= set_configuration
|
1528
|
-
|
1529
|
-
# Activate an alternate setting for an interface.
|
1530
|
-
#
|
1531
|
-
# The interface must have been previously claimed with {DevHandle#claim_interface}.
|
1532
|
-
#
|
1533
|
-
# You should always use this function rather than formulating your own
|
1534
|
-
# SET_INTERFACE control request. This is because the underlying operating system
|
1535
|
-
# needs to know when such changes happen.
|
1536
|
-
#
|
1537
|
-
# This is a blocking function.
|
1538
|
-
#
|
1539
|
-
# @param [Setting, Fixnum] setting_or_interface_number the alternate setting
|
1540
|
-
# to activate or the bInterfaceNumber of the previously-claimed interface
|
1541
|
-
# @param [Fixnum, nil] alternate_setting the bAlternateSetting of the alternate setting to activate
|
1542
|
-
# (only if first param is a Fixnum)
|
1543
|
-
def set_interface_alt_setting(setting_or_interface_number, alternate_setting=nil)
|
1544
|
-
alternate_setting ||= setting_or_interface_number.bAlternateSetting if setting_or_interface_number.respond_to? :bAlternateSetting
|
1545
|
-
setting_or_interface_number = setting_or_interface_number.bInterfaceNumber if setting_or_interface_number.respond_to? :bInterfaceNumber
|
1546
|
-
res = Call.libusb_set_interface_alt_setting(@pHandle, setting_or_interface_number, alternate_setting)
|
1547
|
-
LIBUSB.raise_error res, "in libusb_set_interface_alt_setting" if res!=0
|
1548
|
-
end
|
1549
|
-
|
1550
|
-
# Clear the halt/stall condition for an endpoint.
|
1551
|
-
#
|
1552
|
-
# Endpoints with halt status are unable to receive or transmit
|
1553
|
-
# data until the halt condition is stalled.
|
1554
|
-
#
|
1555
|
-
# You should cancel all pending transfers before attempting to
|
1556
|
-
# clear the halt condition.
|
1557
|
-
#
|
1558
|
-
# This is a blocking function.
|
1559
|
-
#
|
1560
|
-
# @param [Endpoint, Fixnum] endpoint the endpoint in question or it's bEndpointAddress
|
1561
|
-
def clear_halt(endpoint)
|
1562
|
-
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
1563
|
-
res = Call.libusb_clear_halt(@pHandle, endpoint)
|
1564
|
-
LIBUSB.raise_error res, "in libusb_clear_halt" if res!=0
|
1565
|
-
end
|
1566
|
-
|
1567
|
-
# Perform a USB port reset to reinitialize a device.
|
1568
|
-
#
|
1569
|
-
# The system will attempt to restore the previous configuration and
|
1570
|
-
# alternate settings after the reset has completed.
|
1571
|
-
#
|
1572
|
-
# If the reset fails, the descriptors change, or the previous
|
1573
|
-
# state cannot be restored, the device will appear to be disconnected
|
1574
|
-
# and reconnected. This means that the device handle is no longer
|
1575
|
-
# valid (you should close it) and rediscover the device. A Exception
|
1576
|
-
# of LIBUSB::ERROR_NOT_FOUND indicates when this is the case.
|
1577
|
-
#
|
1578
|
-
# This is a blocking function which usually incurs a noticeable delay.
|
1579
|
-
def reset_device
|
1580
|
-
res = Call.libusb_reset_device(@pHandle)
|
1581
|
-
LIBUSB.raise_error res, "in libusb_reset_device" if res!=0
|
1582
|
-
end
|
1583
|
-
|
1584
|
-
# Determine if a kernel driver is active on an interface.
|
1585
|
-
#
|
1586
|
-
# If a kernel driver is active, you cannot claim the interface,
|
1587
|
-
# and libusb will be unable to perform I/O.
|
1588
|
-
#
|
1589
|
-
# @param [Interface, Fixnum] interface the interface to check or it's bInterfaceNumber
|
1590
|
-
# @return [Boolean]
|
1591
|
-
def kernel_driver_active?(interface)
|
1592
|
-
interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
|
1593
|
-
res = Call.libusb_kernel_driver_active(@pHandle, interface)
|
1594
|
-
LIBUSB.raise_error res, "in libusb_kernel_driver_active" unless res>=0
|
1595
|
-
return res==1
|
1596
|
-
end
|
1597
|
-
|
1598
|
-
# Detach a kernel driver from an interface.
|
1599
|
-
#
|
1600
|
-
# If successful, you will then be able to claim the interface and perform I/O.
|
1601
|
-
#
|
1602
|
-
# @param [Interface, Fixnum] interface the interface to detach the driver
|
1603
|
-
# from or it's bInterfaceNumber
|
1604
|
-
def detach_kernel_driver(interface)
|
1605
|
-
interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
|
1606
|
-
res = Call.libusb_detach_kernel_driver(@pHandle, interface)
|
1607
|
-
LIBUSB.raise_error res, "in libusb_detach_kernel_driver" if res!=0
|
1608
|
-
end
|
1609
|
-
|
1610
|
-
# Re-attach an interface's kernel driver, which was previously detached
|
1611
|
-
# using {DevHandle#detach_kernel_driver}.
|
1612
|
-
#
|
1613
|
-
# @param [Interface, Fixnum] interface the interface to attach the driver to
|
1614
|
-
def attach_kernel_driver(interface)
|
1615
|
-
interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
|
1616
|
-
res = Call.libusb_attach_kernel_driver(@pHandle, interface)
|
1617
|
-
LIBUSB.raise_error res, "in libusb_attach_kernel_driver" if res!=0
|
1618
|
-
end
|
1619
|
-
|
1620
|
-
|
1621
|
-
# Perform a USB bulk transfer.
|
1622
|
-
#
|
1623
|
-
# The direction of the transfer is inferred from the direction bits of the
|
1624
|
-
# endpoint address.
|
1625
|
-
#
|
1626
|
-
# For bulk reads, the +:dataIn+ param indicates the maximum length of data you are
|
1627
|
-
# expecting to receive. If less data arrives than expected, this function will
|
1628
|
-
# return that data.
|
1629
|
-
#
|
1630
|
-
# You should also check the returned number of bytes for bulk writes. Not all of the
|
1631
|
-
# data may have been written.
|
1632
|
-
#
|
1633
|
-
# Also check transferred bytes when dealing with a timeout error code. libusb may have
|
1634
|
-
# to split your transfer into a number of chunks to satisfy underlying O/S
|
1635
|
-
# requirements, meaning that the timeout may expire after the first few chunks
|
1636
|
-
# have completed. libusb is careful not to lose any data that may have been
|
1637
|
-
# transferred; do not assume that timeout conditions indicate a complete lack of
|
1638
|
-
# I/O.
|
1639
|
-
#
|
1640
|
-
# @param [Endpoint, Fixnum] :endpoint the (address of a) valid endpoint to communicate with
|
1641
|
-
# @param [String] :dataOut the data to send with an outgoing transfer
|
1642
|
-
# @param [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
|
1643
|
-
# @param [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
|
1644
|
-
# up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
|
1645
|
-
#
|
1646
|
-
# @return [Fixnum] Number of bytes sent for an outgoing transfer
|
1647
|
-
# @return [String] Received data for an ingoing transfer
|
1648
|
-
def bulk_transfer(args={})
|
1649
|
-
timeout = args.delete(:timeout) || 1000
|
1650
|
-
endpoint = args.delete(:endpoint) || raise(ArgumentError, "no endpoint given")
|
1651
|
-
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
1652
|
-
if endpoint&ENDPOINT_IN != 0
|
1653
|
-
dataIn = args.delete(:dataIn) || raise(ArgumentError, "no :dataIn given for bulk read")
|
1654
|
-
else
|
1655
|
-
dataOut = args.delete(:dataOut) || raise(ArgumentError, "no :dataOut given for bulk write")
|
1656
|
-
end
|
1657
|
-
raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
|
1658
|
-
|
1659
|
-
# reuse transfer struct to speed up transfer
|
1660
|
-
@bulk_transfer ||= BulkTransfer.new :dev_handle => self
|
1661
|
-
tr = @bulk_transfer
|
1662
|
-
tr.endpoint = endpoint
|
1663
|
-
tr.timeout = timeout
|
1664
|
-
if dataOut
|
1665
|
-
tr.buffer = dataOut
|
1666
|
-
else
|
1667
|
-
tr.alloc_buffer(dataIn)
|
1668
|
-
end
|
1669
|
-
|
1670
|
-
tr.submit_and_wait!
|
1671
|
-
|
1672
|
-
if dataOut
|
1673
|
-
tr.actual_length
|
1674
|
-
else
|
1675
|
-
tr.actual_buffer
|
1676
|
-
end
|
1677
|
-
end
|
1678
|
-
|
1679
|
-
# Perform a USB interrupt transfer.
|
1680
|
-
#
|
1681
|
-
# The direction of the transfer is inferred from the direction bits of the
|
1682
|
-
# endpoint address.
|
1683
|
-
#
|
1684
|
-
# For interrupt reads, the +:dataIn+ param indicates the maximum length of data you
|
1685
|
-
# are expecting to receive. If less data arrives than expected, this function will
|
1686
|
-
# return that data.
|
1687
|
-
#
|
1688
|
-
# You should also check the returned number of bytes for interrupt writes. Not all of
|
1689
|
-
# the data may have been written.
|
1690
|
-
#
|
1691
|
-
# Also check transferred when dealing with a timeout error code. libusb may have
|
1692
|
-
# to split your transfer into a number of chunks to satisfy underlying O/S
|
1693
|
-
# requirements, meaning that the timeout may expire after the first few chunks
|
1694
|
-
# have completed. libusb is careful not to lose any data that may have been
|
1695
|
-
# transferred; do not assume that timeout conditions indicate a complete lack of
|
1696
|
-
# I/O.
|
1697
|
-
#
|
1698
|
-
# The default endpoint bInterval value is used as the polling interval.
|
1699
|
-
#
|
1700
|
-
# @param [Endpoint, Fixnum] :endpoint the (address of a) valid endpoint to communicate with
|
1701
|
-
# @param [String] :dataOut the data to send with an outgoing transfer
|
1702
|
-
# @param [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
|
1703
|
-
# @param [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
|
1704
|
-
# up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
|
1705
|
-
#
|
1706
|
-
# @return [Fixnum] Number of bytes sent for an outgoing transfer
|
1707
|
-
# @return [String] Received data for an ingoing transfer
|
1708
|
-
def interrupt_transfer(args={})
|
1709
|
-
timeout = args.delete(:timeout) || 1000
|
1710
|
-
endpoint = args.delete(:endpoint) || raise(ArgumentError, "no endpoint given")
|
1711
|
-
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
1712
|
-
if endpoint&ENDPOINT_IN != 0
|
1713
|
-
dataIn = args.delete(:dataIn) || raise(ArgumentError, "no :dataIn given for interrupt read")
|
1714
|
-
else
|
1715
|
-
dataOut = args.delete(:dataOut) || raise(ArgumentError, "no :dataOut given for interrupt write")
|
1716
|
-
end
|
1717
|
-
raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
|
1718
|
-
|
1719
|
-
# reuse transfer struct to speed up transfer
|
1720
|
-
@interrupt_transfer ||= InterruptTransfer.new :dev_handle => self
|
1721
|
-
tr = @interrupt_transfer
|
1722
|
-
tr.endpoint = endpoint
|
1723
|
-
tr.timeout = timeout
|
1724
|
-
if dataOut
|
1725
|
-
tr.buffer = dataOut
|
1726
|
-
else
|
1727
|
-
tr.alloc_buffer(dataIn)
|
1728
|
-
end
|
1729
|
-
|
1730
|
-
tr.submit_and_wait!
|
1731
|
-
|
1732
|
-
if dataOut
|
1733
|
-
tr.actual_length
|
1734
|
-
else
|
1735
|
-
tr.actual_buffer
|
1736
|
-
end
|
1737
|
-
end
|
1738
|
-
|
1739
|
-
# Perform a USB control transfer.
|
1740
|
-
#
|
1741
|
-
# The direction of the transfer is inferred from the +:bmRequestType+ field of the
|
1742
|
-
# setup packet.
|
1743
|
-
#
|
1744
|
-
# @param [Fixnum] :bmRequestType the request type field for the setup packet
|
1745
|
-
# @param [Fixnum] :bRequest the request field for the setup packet
|
1746
|
-
# @param [Fixnum] :wValue the value field for the setup packet
|
1747
|
-
# @param [Fixnum] :wIndex the index field for the setup packet
|
1748
|
-
# @param [String] :dataOut the data to send with an outgoing transfer, it
|
1749
|
-
# is appended to the setup packet
|
1750
|
-
# @param [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
|
1751
|
-
# (excluding setup packet)
|
1752
|
-
# @param [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
|
1753
|
-
# up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
|
1754
|
-
#
|
1755
|
-
# @return [Fixnum] Number of bytes sent (excluding setup packet) for outgoing transfer
|
1756
|
-
# @return [String] Received data (without setup packet) for ingoing transfer
|
1757
|
-
def control_transfer(args={})
|
1758
|
-
bmRequestType = args.delete(:bmRequestType) || raise(ArgumentError, "param :bmRequestType not given")
|
1759
|
-
bRequest = args.delete(:bRequest) || raise(ArgumentError, "param :bRequest not given")
|
1760
|
-
wValue = args.delete(:wValue) || raise(ArgumentError, "param :wValue not given")
|
1761
|
-
wIndex = args.delete(:wIndex) || raise(ArgumentError, "param :wIndex not given")
|
1762
|
-
timeout = args.delete(:timeout) || 1000
|
1763
|
-
if bmRequestType&ENDPOINT_IN != 0
|
1764
|
-
dataIn = args.delete(:dataIn) || 0
|
1765
|
-
dataOut = ''
|
1766
|
-
else
|
1767
|
-
dataOut = args.delete(:dataOut) || ''
|
1768
|
-
end
|
1769
|
-
raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
|
1770
|
-
|
1771
|
-
# reuse transfer struct to speed up transfer
|
1772
|
-
@control_transfer ||= ControlTransfer.new :dev_handle => self
|
1773
|
-
tr = @control_transfer
|
1774
|
-
tr.timeout = timeout
|
1775
|
-
if dataIn
|
1776
|
-
setup_data = [bmRequestType, bRequest, wValue, wIndex, dataIn].pack('CCvvv')
|
1777
|
-
tr.alloc_buffer( dataIn + CONTROL_SETUP_SIZE, setup_data )
|
1778
|
-
else
|
1779
|
-
tr.buffer = [bmRequestType, bRequest, wValue, wIndex, dataOut.bytesize, dataOut].pack('CCvvva*')
|
1780
|
-
end
|
1781
|
-
|
1782
|
-
tr.submit_and_wait!
|
1783
|
-
|
1784
|
-
if dataIn
|
1785
|
-
tr.actual_buffer(CONTROL_SETUP_SIZE)
|
1786
|
-
else
|
1787
|
-
tr.actual_length
|
1788
|
-
end
|
1789
|
-
end
|
1790
|
-
end
|
1791
|
-
|
1792
52
|
end
|