libusb 0.1.3-x86-mingw32 → 0.2.0-x86-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,127 @@
|
|
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
|
+
# Initialize libusb context.
|
22
|
+
def initialize
|
23
|
+
m = FFI::MemoryPointer.new :pointer
|
24
|
+
Call.libusb_init(m)
|
25
|
+
@ctx = m.read_pointer
|
26
|
+
end
|
27
|
+
|
28
|
+
# Deinitialize libusb.
|
29
|
+
#
|
30
|
+
# Should be called after closing all open devices and before your application terminates.
|
31
|
+
def exit
|
32
|
+
Call.libusb_exit(@ctx)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Set message verbosity.
|
36
|
+
#
|
37
|
+
# * Level 0: no messages ever printed by the library (default)
|
38
|
+
# * Level 1: error messages are printed to stderr
|
39
|
+
# * Level 2: warning and error messages are printed to stderr
|
40
|
+
# * Level 3: informational messages are printed to stdout, warning and
|
41
|
+
# error messages are printed to stderr
|
42
|
+
#
|
43
|
+
# The default level is 0, which means no messages are ever printed. If you
|
44
|
+
# choose to increase the message verbosity level, ensure that your
|
45
|
+
# application does not close the stdout/stderr file descriptors.
|
46
|
+
#
|
47
|
+
# You are advised to set level 3. libusb is conservative with its message
|
48
|
+
# logging and most of the time, will only log messages that explain error
|
49
|
+
# conditions and other oddities. This will help you debug your software.
|
50
|
+
#
|
51
|
+
# If the LIBUSB_DEBUG environment variable was set when libusb was
|
52
|
+
# initialized, this method does nothing: the message verbosity is
|
53
|
+
# fixed to the value in the environment variable.
|
54
|
+
#
|
55
|
+
# If libusb was compiled without any message logging, this method
|
56
|
+
# does nothing: you'll never get any messages.
|
57
|
+
#
|
58
|
+
# If libusb was compiled with verbose debug message logging, this
|
59
|
+
# method does nothing: you'll always get messages from all levels.
|
60
|
+
#
|
61
|
+
# @param [Fixnum] level debug level to set
|
62
|
+
def debug=(level)
|
63
|
+
Call.libusb_set_debug(@ctx, level)
|
64
|
+
end
|
65
|
+
|
66
|
+
def device_list
|
67
|
+
pppDevs = FFI::MemoryPointer.new :pointer
|
68
|
+
size = Call.libusb_get_device_list(@ctx, pppDevs)
|
69
|
+
ppDevs = pppDevs.read_pointer
|
70
|
+
pDevs = []
|
71
|
+
size.times do |devi|
|
72
|
+
pDev = ppDevs.get_pointer(devi*FFI.type_size(:pointer))
|
73
|
+
pDevs << Device.new(self, pDev)
|
74
|
+
end
|
75
|
+
Call.libusb_free_device_list(ppDevs, 1)
|
76
|
+
pDevs
|
77
|
+
end
|
78
|
+
private :device_list
|
79
|
+
|
80
|
+
# Handle any pending events in blocking mode.
|
81
|
+
#
|
82
|
+
# This method must be called when libusb is running asynchronous transfers.
|
83
|
+
# This gives libusb the opportunity to reap pending transfers,
|
84
|
+
# invoke callbacks, etc.
|
85
|
+
def handle_events
|
86
|
+
res = Call.libusb_handle_events(@ctx)
|
87
|
+
LIBUSB.raise_error res, "in libusb_handle_events" if res<0
|
88
|
+
end
|
89
|
+
|
90
|
+
# Obtain a list of devices currently attached to the USB system, optionally matching certain criteria.
|
91
|
+
#
|
92
|
+
# @param [Hash] filter_hash A number of criteria can be defined in key-value pairs.
|
93
|
+
# Only devices that equal all given criterions will be returned. If a criterion is
|
94
|
+
# not specified or its value is +nil+, any device will match that criterion.
|
95
|
+
# The following criteria can be filtered:
|
96
|
+
# * <tt>:idVendor</tt>, <tt>:idProduct</tt> (+FixNum+) for matching vendor/product ID,
|
97
|
+
# * <tt>:bClass</tt>, <tt>:bSubClass</tt>, <tt>:bProtocol</tt> (+FixNum+) for the device type -
|
98
|
+
# Devices using CLASS_PER_INTERFACE will match, if any of the interfaces match.
|
99
|
+
# * <tt>:bcdUSB</tt>, <tt>:bcdDevice</tt>, <tt>:bMaxPacketSize0</tt> (+FixNum+) for the
|
100
|
+
# USB and device release numbers.
|
101
|
+
# Criteria can also specified as Array of several alternative values.
|
102
|
+
#
|
103
|
+
# @example
|
104
|
+
# # Return all devices of vendor 0x0ab1 where idProduct is 3 or 4:
|
105
|
+
# context.device :idVendor=>0x0ab1, :idProduct=>[0x0003, 0x0004]
|
106
|
+
#
|
107
|
+
# @return [Array<LIBUSB::Device>]
|
108
|
+
def devices(filter_hash={})
|
109
|
+
device_list.select do |dev|
|
110
|
+
( !filter_hash[:bClass] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
|
111
|
+
dev.settings.map(&:bInterfaceClass).&([filter_hash[:bClass]].flatten).any? :
|
112
|
+
[filter_hash[:bClass]].flatten.include?(dev.bDeviceClass))) &&
|
113
|
+
( !filter_hash[:bSubClass] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
|
114
|
+
dev.settings.map(&:bInterfaceSubClass).&([filter_hash[:bSubClass]].flatten).any? :
|
115
|
+
[filter_hash[:bSubClass]].flatten.include?(dev.bDeviceSubClass))) &&
|
116
|
+
( !filter_hash[:bProtocol] || (dev.bDeviceClass==CLASS_PER_INTERFACE ?
|
117
|
+
dev.settings.map(&:bInterfaceProtocol).&([filter_hash[:bProtocol]].flatten).any? :
|
118
|
+
[filter_hash[:bProtocol]].flatten.include?(dev.bDeviceProtocol))) &&
|
119
|
+
( !filter_hash[:bMaxPacketSize0] || [filter_hash[:bMaxPacketSize0]].flatten.include?(dev.bMaxPacketSize0) ) &&
|
120
|
+
( !filter_hash[:idVendor] || [filter_hash[:idVendor]].flatten.include?(dev.idVendor) ) &&
|
121
|
+
( !filter_hash[:idProduct] || [filter_hash[:idProduct]].flatten.include?(dev.idProduct) ) &&
|
122
|
+
( !filter_hash[:bcdUSB] || [filter_hash[:bcdUSB]].flatten.include?(dev.bcdUSB) ) &&
|
123
|
+
( !filter_hash[:bcdDevice] || [filter_hash[:bcdDevice]].flatten.include?(dev.bcdDevice) )
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,399 @@
|
|
1
|
+
# This file is part of Libusb for Ruby.
|
2
|
+
#
|
3
|
+
# Libusb for Ruby is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# Libusb for Ruby is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public License
|
14
|
+
# along with Libusb for Ruby. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require 'libusb/call'
|
17
|
+
|
18
|
+
module LIBUSB
|
19
|
+
# Class representing a handle on a USB device.
|
20
|
+
#
|
21
|
+
# A device handle is used to perform I/O and other operations. When finished
|
22
|
+
# with a device handle, you should call DevHandle#close .
|
23
|
+
class DevHandle
|
24
|
+
# @private
|
25
|
+
attr_reader :pHandle
|
26
|
+
# @return [Device] the device this handle belongs to.
|
27
|
+
attr_reader :device
|
28
|
+
|
29
|
+
def initialize device, pHandle
|
30
|
+
@device = device
|
31
|
+
@pHandle = pHandle
|
32
|
+
@bulk_transfer = @control_transfer = @interrupt_transfer = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
# Close a device handle.
|
36
|
+
#
|
37
|
+
# Should be called on all open handles before your application exits.
|
38
|
+
#
|
39
|
+
# Internally, this function destroys the reference that was added by {Device#open}
|
40
|
+
# on the given device.
|
41
|
+
#
|
42
|
+
# This is a non-blocking function; no requests are sent over the bus.
|
43
|
+
def close
|
44
|
+
Call.libusb_close(@pHandle)
|
45
|
+
end
|
46
|
+
|
47
|
+
def string_descriptor_ascii(index)
|
48
|
+
pString = FFI::MemoryPointer.new 0x100
|
49
|
+
res = Call.libusb_get_string_descriptor_ascii(@pHandle, index, pString, pString.size)
|
50
|
+
LIBUSB.raise_error res, "in libusb_get_string_descriptor_ascii" unless res>=0
|
51
|
+
pString.read_string(res)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Claim an interface on a given device handle.
|
55
|
+
#
|
56
|
+
# You must claim the interface you wish to use before you can perform I/O on any
|
57
|
+
# of its endpoints.
|
58
|
+
#
|
59
|
+
# It is legal to attempt to claim an already-claimed interface, in which case
|
60
|
+
# libusb just returns without doing anything.
|
61
|
+
#
|
62
|
+
# Claiming of interfaces is a purely logical operation; it does not cause any
|
63
|
+
# requests to be sent over the bus. Interface claiming is used to instruct the
|
64
|
+
# underlying operating system that your application wishes to take ownership of
|
65
|
+
# the interface.
|
66
|
+
#
|
67
|
+
# This is a non-blocking function.
|
68
|
+
#
|
69
|
+
# If called with a block, the device handle is passed through to the block
|
70
|
+
# and the interface is released when the block has finished.
|
71
|
+
#
|
72
|
+
# @param [Interface, Fixnum] interface the interface or it's bInterfaceNumber you wish to claim
|
73
|
+
def claim_interface(interface)
|
74
|
+
interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
|
75
|
+
res = Call.libusb_claim_interface(@pHandle, interface)
|
76
|
+
LIBUSB.raise_error res, "in libusb_claim_interface" if res!=0
|
77
|
+
return self unless block_given?
|
78
|
+
begin
|
79
|
+
yield self
|
80
|
+
ensure
|
81
|
+
release_interface(interface)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Release an interface previously claimed with {DevHandle#claim_interface}.
|
86
|
+
#
|
87
|
+
# You should release all claimed interfaces before closing a device handle.
|
88
|
+
#
|
89
|
+
# This is a blocking function. A SET_INTERFACE control request will be sent to the
|
90
|
+
# device, resetting interface state to the first alternate setting.
|
91
|
+
#
|
92
|
+
# @param [Interface, Fixnum] interface the interface or it's bInterfaceNumber you
|
93
|
+
# claimed previously
|
94
|
+
def release_interface(interface)
|
95
|
+
interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
|
96
|
+
res = Call.libusb_release_interface(@pHandle, interface)
|
97
|
+
LIBUSB.raise_error res, "in libusb_release_interface" if res!=0
|
98
|
+
end
|
99
|
+
|
100
|
+
# Set the active configuration for a device.
|
101
|
+
#
|
102
|
+
# The operating system may or may not have already set an active configuration on
|
103
|
+
# the device. It is up to your application to ensure the correct configuration is
|
104
|
+
# selected before you attempt to claim interfaces and perform other operations.
|
105
|
+
#
|
106
|
+
# If you call this function on a device already configured with the selected
|
107
|
+
# configuration, then this function will act as a lightweight device reset: it
|
108
|
+
# will issue a SET_CONFIGURATION request using the current configuration, causing
|
109
|
+
# most USB-related device state to be reset (altsetting reset to zero, endpoint
|
110
|
+
# halts cleared, toggles reset).
|
111
|
+
#
|
112
|
+
# You cannot change/reset configuration if your application has claimed interfaces -
|
113
|
+
# you should free them with {DevHandle#release_interface} first. You cannot
|
114
|
+
# change/reset configuration if other applications or drivers have claimed
|
115
|
+
# interfaces.
|
116
|
+
#
|
117
|
+
# A configuration value of +nil+ will put the device in unconfigured state. The USB
|
118
|
+
# specifications state that a configuration value of 0 does this, however buggy
|
119
|
+
# devices exist which actually have a configuration 0.
|
120
|
+
#
|
121
|
+
# You should always use this function rather than formulating your own
|
122
|
+
# SET_CONFIGURATION control request. This is because the underlying operating
|
123
|
+
# system needs to know when such changes happen.
|
124
|
+
#
|
125
|
+
# This is a blocking function.
|
126
|
+
#
|
127
|
+
# @param [Configuration, Fixnum] configuration the configuration or it's
|
128
|
+
# bConfigurationValue you wish to activate, or +nil+ if you wish to put
|
129
|
+
# the device in unconfigured state
|
130
|
+
def set_configuration(configuration)
|
131
|
+
configuration = configuration.bConfigurationValue if configuration.respond_to? :bConfigurationValue
|
132
|
+
res = Call.libusb_set_configuration(@pHandle, configuration || -1)
|
133
|
+
LIBUSB.raise_error res, "in libusb_set_configuration" if res!=0
|
134
|
+
end
|
135
|
+
alias configuration= set_configuration
|
136
|
+
|
137
|
+
# Activate an alternate setting for an interface.
|
138
|
+
#
|
139
|
+
# The interface must have been previously claimed with {DevHandle#claim_interface}.
|
140
|
+
#
|
141
|
+
# You should always use this function rather than formulating your own
|
142
|
+
# SET_INTERFACE control request. This is because the underlying operating system
|
143
|
+
# needs to know when such changes happen.
|
144
|
+
#
|
145
|
+
# This is a blocking function.
|
146
|
+
#
|
147
|
+
# @param [Setting, Fixnum] setting_or_interface_number the alternate setting
|
148
|
+
# to activate or the bInterfaceNumber of the previously-claimed interface
|
149
|
+
# @param [Fixnum, nil] alternate_setting the bAlternateSetting of the alternate setting to activate
|
150
|
+
# (only if first param is a Fixnum)
|
151
|
+
def set_interface_alt_setting(setting_or_interface_number, alternate_setting=nil)
|
152
|
+
alternate_setting ||= setting_or_interface_number.bAlternateSetting if setting_or_interface_number.respond_to? :bAlternateSetting
|
153
|
+
setting_or_interface_number = setting_or_interface_number.bInterfaceNumber if setting_or_interface_number.respond_to? :bInterfaceNumber
|
154
|
+
res = Call.libusb_set_interface_alt_setting(@pHandle, setting_or_interface_number, alternate_setting)
|
155
|
+
LIBUSB.raise_error res, "in libusb_set_interface_alt_setting" if res!=0
|
156
|
+
end
|
157
|
+
|
158
|
+
# Clear the halt/stall condition for an endpoint.
|
159
|
+
#
|
160
|
+
# Endpoints with halt status are unable to receive or transmit
|
161
|
+
# data until the halt condition is stalled.
|
162
|
+
#
|
163
|
+
# You should cancel all pending transfers before attempting to
|
164
|
+
# clear the halt condition.
|
165
|
+
#
|
166
|
+
# This is a blocking function.
|
167
|
+
#
|
168
|
+
# @param [Endpoint, Fixnum] endpoint the endpoint in question or it's bEndpointAddress
|
169
|
+
def clear_halt(endpoint)
|
170
|
+
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
171
|
+
res = Call.libusb_clear_halt(@pHandle, endpoint)
|
172
|
+
LIBUSB.raise_error res, "in libusb_clear_halt" if res!=0
|
173
|
+
end
|
174
|
+
|
175
|
+
# Perform a USB port reset to reinitialize a device.
|
176
|
+
#
|
177
|
+
# The system will attempt to restore the previous configuration and
|
178
|
+
# alternate settings after the reset has completed.
|
179
|
+
#
|
180
|
+
# If the reset fails, the descriptors change, or the previous
|
181
|
+
# state cannot be restored, the device will appear to be disconnected
|
182
|
+
# and reconnected. This means that the device handle is no longer
|
183
|
+
# valid (you should close it) and rediscover the device. A Exception
|
184
|
+
# of LIBUSB::ERROR_NOT_FOUND indicates when this is the case.
|
185
|
+
#
|
186
|
+
# This is a blocking function which usually incurs a noticeable delay.
|
187
|
+
def reset_device
|
188
|
+
res = Call.libusb_reset_device(@pHandle)
|
189
|
+
LIBUSB.raise_error res, "in libusb_reset_device" if res!=0
|
190
|
+
end
|
191
|
+
|
192
|
+
# Determine if a kernel driver is active on an interface.
|
193
|
+
#
|
194
|
+
# If a kernel driver is active, you cannot claim the interface,
|
195
|
+
# and libusb will be unable to perform I/O.
|
196
|
+
#
|
197
|
+
# @param [Interface, Fixnum] interface the interface to check or it's bInterfaceNumber
|
198
|
+
# @return [Boolean]
|
199
|
+
def kernel_driver_active?(interface)
|
200
|
+
interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
|
201
|
+
res = Call.libusb_kernel_driver_active(@pHandle, interface)
|
202
|
+
LIBUSB.raise_error res, "in libusb_kernel_driver_active" unless res>=0
|
203
|
+
return res==1
|
204
|
+
end
|
205
|
+
|
206
|
+
# Detach a kernel driver from an interface.
|
207
|
+
#
|
208
|
+
# If successful, you will then be able to claim the interface and perform I/O.
|
209
|
+
#
|
210
|
+
# @param [Interface, Fixnum] interface the interface to detach the driver
|
211
|
+
# from or it's bInterfaceNumber
|
212
|
+
def detach_kernel_driver(interface)
|
213
|
+
interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
|
214
|
+
res = Call.libusb_detach_kernel_driver(@pHandle, interface)
|
215
|
+
LIBUSB.raise_error res, "in libusb_detach_kernel_driver" if res!=0
|
216
|
+
end
|
217
|
+
|
218
|
+
# Re-attach an interface's kernel driver, which was previously detached
|
219
|
+
# using {DevHandle#detach_kernel_driver}.
|
220
|
+
#
|
221
|
+
# @param [Interface, Fixnum] interface the interface to attach the driver to
|
222
|
+
def attach_kernel_driver(interface)
|
223
|
+
interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
|
224
|
+
res = Call.libusb_attach_kernel_driver(@pHandle, interface)
|
225
|
+
LIBUSB.raise_error res, "in libusb_attach_kernel_driver" if res!=0
|
226
|
+
end
|
227
|
+
|
228
|
+
|
229
|
+
# Perform a USB bulk transfer.
|
230
|
+
#
|
231
|
+
# The direction of the transfer is inferred from the direction bits of the
|
232
|
+
# endpoint address.
|
233
|
+
#
|
234
|
+
# For bulk reads, the +:dataIn+ param indicates the maximum length of data you are
|
235
|
+
# expecting to receive. If less data arrives than expected, this function will
|
236
|
+
# return that data.
|
237
|
+
#
|
238
|
+
# You should also check the returned number of bytes for bulk writes. Not all of the
|
239
|
+
# data may have been written.
|
240
|
+
#
|
241
|
+
# Also check transferred bytes when dealing with a timeout error code. libusb may have
|
242
|
+
# to split your transfer into a number of chunks to satisfy underlying O/S
|
243
|
+
# requirements, meaning that the timeout may expire after the first few chunks
|
244
|
+
# have completed. libusb is careful not to lose any data that may have been
|
245
|
+
# transferred; do not assume that timeout conditions indicate a complete lack of
|
246
|
+
# I/O.
|
247
|
+
#
|
248
|
+
# @param [Endpoint, Fixnum] :endpoint the (address of a) valid endpoint to communicate with
|
249
|
+
# @param [String] :dataOut the data to send with an outgoing transfer
|
250
|
+
# @param [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
|
251
|
+
# @param [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
|
252
|
+
# up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
|
253
|
+
#
|
254
|
+
# @return [Fixnum] Number of bytes sent for an outgoing transfer
|
255
|
+
# @return [String] Received data for an ingoing transfer
|
256
|
+
def bulk_transfer(args={})
|
257
|
+
timeout = args.delete(:timeout) || 1000
|
258
|
+
endpoint = args.delete(:endpoint) || raise(ArgumentError, "no endpoint given")
|
259
|
+
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
260
|
+
if endpoint&ENDPOINT_IN != 0
|
261
|
+
dataIn = args.delete(:dataIn) || raise(ArgumentError, "no :dataIn given for bulk read")
|
262
|
+
else
|
263
|
+
dataOut = args.delete(:dataOut) || raise(ArgumentError, "no :dataOut given for bulk write")
|
264
|
+
end
|
265
|
+
raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
|
266
|
+
|
267
|
+
# reuse transfer struct to speed up transfer
|
268
|
+
@bulk_transfer ||= BulkTransfer.new :dev_handle => self
|
269
|
+
tr = @bulk_transfer
|
270
|
+
tr.endpoint = endpoint
|
271
|
+
tr.timeout = timeout
|
272
|
+
if dataOut
|
273
|
+
tr.buffer = dataOut
|
274
|
+
else
|
275
|
+
tr.alloc_buffer(dataIn)
|
276
|
+
end
|
277
|
+
|
278
|
+
tr.submit_and_wait!
|
279
|
+
|
280
|
+
if dataOut
|
281
|
+
tr.actual_length
|
282
|
+
else
|
283
|
+
tr.actual_buffer
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
# Perform a USB interrupt transfer.
|
288
|
+
#
|
289
|
+
# The direction of the transfer is inferred from the direction bits of the
|
290
|
+
# endpoint address.
|
291
|
+
#
|
292
|
+
# For interrupt reads, the +:dataIn+ param indicates the maximum length of data you
|
293
|
+
# are expecting to receive. If less data arrives than expected, this function will
|
294
|
+
# return that data.
|
295
|
+
#
|
296
|
+
# You should also check the returned number of bytes for interrupt writes. Not all of
|
297
|
+
# the data may have been written.
|
298
|
+
#
|
299
|
+
# Also check transferred when dealing with a timeout error code. libusb may have
|
300
|
+
# to split your transfer into a number of chunks to satisfy underlying O/S
|
301
|
+
# requirements, meaning that the timeout may expire after the first few chunks
|
302
|
+
# have completed. libusb is careful not to lose any data that may have been
|
303
|
+
# transferred; do not assume that timeout conditions indicate a complete lack of
|
304
|
+
# I/O.
|
305
|
+
#
|
306
|
+
# The default endpoint bInterval value is used as the polling interval.
|
307
|
+
#
|
308
|
+
# @param [Endpoint, Fixnum] :endpoint the (address of a) valid endpoint to communicate with
|
309
|
+
# @param [String] :dataOut the data to send with an outgoing transfer
|
310
|
+
# @param [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
|
311
|
+
# @param [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
|
312
|
+
# up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
|
313
|
+
#
|
314
|
+
# @return [Fixnum] Number of bytes sent for an outgoing transfer
|
315
|
+
# @return [String] Received data for an ingoing transfer
|
316
|
+
def interrupt_transfer(args={})
|
317
|
+
timeout = args.delete(:timeout) || 1000
|
318
|
+
endpoint = args.delete(:endpoint) || raise(ArgumentError, "no endpoint given")
|
319
|
+
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
320
|
+
if endpoint&ENDPOINT_IN != 0
|
321
|
+
dataIn = args.delete(:dataIn) || raise(ArgumentError, "no :dataIn given for interrupt read")
|
322
|
+
else
|
323
|
+
dataOut = args.delete(:dataOut) || raise(ArgumentError, "no :dataOut given for interrupt write")
|
324
|
+
end
|
325
|
+
raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
|
326
|
+
|
327
|
+
# reuse transfer struct to speed up transfer
|
328
|
+
@interrupt_transfer ||= InterruptTransfer.new :dev_handle => self
|
329
|
+
tr = @interrupt_transfer
|
330
|
+
tr.endpoint = endpoint
|
331
|
+
tr.timeout = timeout
|
332
|
+
if dataOut
|
333
|
+
tr.buffer = dataOut
|
334
|
+
else
|
335
|
+
tr.alloc_buffer(dataIn)
|
336
|
+
end
|
337
|
+
|
338
|
+
tr.submit_and_wait!
|
339
|
+
|
340
|
+
if dataOut
|
341
|
+
tr.actual_length
|
342
|
+
else
|
343
|
+
tr.actual_buffer
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
# Perform a USB control transfer.
|
348
|
+
#
|
349
|
+
# The direction of the transfer is inferred from the +:bmRequestType+ field of the
|
350
|
+
# setup packet.
|
351
|
+
#
|
352
|
+
# @param [Fixnum] :bmRequestType the request type field for the setup packet
|
353
|
+
# @param [Fixnum] :bRequest the request field for the setup packet
|
354
|
+
# @param [Fixnum] :wValue the value field for the setup packet
|
355
|
+
# @param [Fixnum] :wIndex the index field for the setup packet
|
356
|
+
# @param [String] :dataOut the data to send with an outgoing transfer, it
|
357
|
+
# is appended to the setup packet
|
358
|
+
# @param [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
|
359
|
+
# (excluding setup packet)
|
360
|
+
# @param [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
|
361
|
+
# up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
|
362
|
+
#
|
363
|
+
# @return [Fixnum] Number of bytes sent (excluding setup packet) for outgoing transfer
|
364
|
+
# @return [String] Received data (without setup packet) for ingoing transfer
|
365
|
+
def control_transfer(args={})
|
366
|
+
bmRequestType = args.delete(:bmRequestType) || raise(ArgumentError, "param :bmRequestType not given")
|
367
|
+
bRequest = args.delete(:bRequest) || raise(ArgumentError, "param :bRequest not given")
|
368
|
+
wValue = args.delete(:wValue) || raise(ArgumentError, "param :wValue not given")
|
369
|
+
wIndex = args.delete(:wIndex) || raise(ArgumentError, "param :wIndex not given")
|
370
|
+
timeout = args.delete(:timeout) || 1000
|
371
|
+
if bmRequestType&ENDPOINT_IN != 0
|
372
|
+
dataIn = args.delete(:dataIn) || 0
|
373
|
+
dataOut = ''
|
374
|
+
else
|
375
|
+
dataOut = args.delete(:dataOut) || ''
|
376
|
+
end
|
377
|
+
raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?
|
378
|
+
|
379
|
+
# reuse transfer struct to speed up transfer
|
380
|
+
@control_transfer ||= ControlTransfer.new :dev_handle => self
|
381
|
+
tr = @control_transfer
|
382
|
+
tr.timeout = timeout
|
383
|
+
if dataIn
|
384
|
+
setup_data = [bmRequestType, bRequest, wValue, wIndex, dataIn].pack('CCvvv')
|
385
|
+
tr.alloc_buffer( dataIn + CONTROL_SETUP_SIZE, setup_data )
|
386
|
+
else
|
387
|
+
tr.buffer = [bmRequestType, bRequest, wValue, wIndex, dataOut.bytesize, dataOut].pack('CCvvva*')
|
388
|
+
end
|
389
|
+
|
390
|
+
tr.submit_and_wait!
|
391
|
+
|
392
|
+
if dataIn
|
393
|
+
tr.actual_buffer(CONTROL_SETUP_SIZE)
|
394
|
+
else
|
395
|
+
tr.actual_length
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|