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
@@ -0,0 +1,51 @@
|
|
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 Interface < FFI::Struct
|
20
|
+
include Comparable
|
21
|
+
|
22
|
+
layout :altsetting, :pointer,
|
23
|
+
:num_altsetting, :int
|
24
|
+
|
25
|
+
def initialize(configuration, *args)
|
26
|
+
@configuration = configuration
|
27
|
+
super(*args)
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [Configuration] the configuration this interface belongs to.
|
31
|
+
attr_reader :configuration
|
32
|
+
|
33
|
+
def alt_settings
|
34
|
+
ifs = []
|
35
|
+
self[:num_altsetting].times do |i|
|
36
|
+
ifs << Setting.new(self, self[:altsetting] + i*Setting.size)
|
37
|
+
end
|
38
|
+
return ifs
|
39
|
+
end
|
40
|
+
alias settings alt_settings
|
41
|
+
|
42
|
+
# The {Device} this Interface belongs to.
|
43
|
+
def device() self.configuration.device end
|
44
|
+
# Return all endpoints of all alternative settings as Array of {Endpoint}s.
|
45
|
+
def endpoints() self.alt_settings.map {|d| d.endpoints }.flatten end
|
46
|
+
|
47
|
+
def <=>(o)
|
48
|
+
configuration<=>o.configuration
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,132 @@
|
|
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 Setting < FFI::Struct
|
20
|
+
include Comparable
|
21
|
+
|
22
|
+
layout :bLength, :uint8,
|
23
|
+
:bDescriptorType, :uint8,
|
24
|
+
:bInterfaceNumber, :uint8,
|
25
|
+
:bAlternateSetting, :uint8,
|
26
|
+
:bNumEndpoints, :uint8,
|
27
|
+
:bInterfaceClass, :uint8,
|
28
|
+
:bInterfaceSubClass, :uint8,
|
29
|
+
:bInterfaceProtocol, :uint8,
|
30
|
+
:iInterface, :uint8,
|
31
|
+
:endpoint, :pointer,
|
32
|
+
:extra, :pointer,
|
33
|
+
:extra_length, :int
|
34
|
+
|
35
|
+
# Size of this descriptor (in bytes).
|
36
|
+
def bLength
|
37
|
+
self[:bLength]
|
38
|
+
end
|
39
|
+
|
40
|
+
# Descriptor type (0x04)
|
41
|
+
def bDescriptorType
|
42
|
+
self[:bDescriptorType]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Number of this interface.
|
46
|
+
def bInterfaceNumber
|
47
|
+
self[:bInterfaceNumber]
|
48
|
+
end
|
49
|
+
|
50
|
+
# Value used to select this alternate setting for this interface.
|
51
|
+
def bAlternateSetting
|
52
|
+
self[:bAlternateSetting]
|
53
|
+
end
|
54
|
+
|
55
|
+
# Number of endpoints used by this interface (excluding the control endpoint).
|
56
|
+
def bNumEndpoints
|
57
|
+
self[:bNumEndpoints]
|
58
|
+
end
|
59
|
+
|
60
|
+
# USB-IF class code for this interface.
|
61
|
+
def bInterfaceClass
|
62
|
+
self[:bInterfaceClass]
|
63
|
+
end
|
64
|
+
|
65
|
+
# USB-IF subclass code for this interface, qualified by the {Setting#bInterfaceClass} value.
|
66
|
+
def bInterfaceSubClass
|
67
|
+
self[:bInterfaceSubClass]
|
68
|
+
end
|
69
|
+
|
70
|
+
# USB-IF protocol code for this interface, qualified by the {Setting#bInterfaceClass} and {Setting#bInterfaceSubClass} values.
|
71
|
+
def bInterfaceProtocol
|
72
|
+
self[:bInterfaceProtocol]
|
73
|
+
end
|
74
|
+
|
75
|
+
# Index of string descriptor describing this interface.
|
76
|
+
def iInterface
|
77
|
+
self[:iInterface]
|
78
|
+
end
|
79
|
+
|
80
|
+
# Extra descriptors.
|
81
|
+
#
|
82
|
+
# @return [String]
|
83
|
+
def extra
|
84
|
+
return if self[:extra].null?
|
85
|
+
self[:extra].read_string(self[:extra_length])
|
86
|
+
end
|
87
|
+
|
88
|
+
def initialize(interface, *args)
|
89
|
+
@interface = interface
|
90
|
+
super(*args)
|
91
|
+
end
|
92
|
+
|
93
|
+
# @return [Interface] the interface this setting belongs to.
|
94
|
+
attr_reader :interface
|
95
|
+
|
96
|
+
def endpoints
|
97
|
+
ifs = []
|
98
|
+
self[:bNumEndpoints].times do |i|
|
99
|
+
ifs << Endpoint.new(self, self[:endpoint] + i*Endpoint.size)
|
100
|
+
end
|
101
|
+
return ifs
|
102
|
+
end
|
103
|
+
|
104
|
+
def inspect
|
105
|
+
attrs = []
|
106
|
+
attrs << self.bAlternateSetting.to_s
|
107
|
+
devclass = LIBUSB.dev_string(self.bInterfaceClass, self.bInterfaceSubClass, self.bInterfaceProtocol)
|
108
|
+
attrs << devclass
|
109
|
+
desc = self.description
|
110
|
+
attrs << desc if desc != '?'
|
111
|
+
"\#<#{self.class} #{attrs.join(' ')}>"
|
112
|
+
end
|
113
|
+
|
114
|
+
# Return name of this interface as String.
|
115
|
+
def description
|
116
|
+
return @description if defined? @description
|
117
|
+
@description = device.try_string_descriptor_ascii(self.iInterface)
|
118
|
+
end
|
119
|
+
|
120
|
+
# The {Device} this Setting belongs to.
|
121
|
+
def device() self.interface.configuration.device end
|
122
|
+
# The {Configuration} this Setting belongs to.
|
123
|
+
def configuration() self.interface.configuration end
|
124
|
+
|
125
|
+
def <=>(o)
|
126
|
+
t = interface<=>o.interface
|
127
|
+
t = bInterfaceNumber<=>o.bInterfaceNumber if t==0
|
128
|
+
t = bAlternateSetting<=>o.bAlternateSetting if t==0
|
129
|
+
t
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,269 @@
|
|
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
|
+
# Abstract base class for USB transfers. Use
|
20
|
+
# {ControlTransfer}, {BulkTransfer}, {InterruptTransfer}, {IsochronousTransfer}
|
21
|
+
# to do transfers.
|
22
|
+
class Transfer
|
23
|
+
def initialize(args={})
|
24
|
+
args.each{|k,v| send("#{k}=", v) }
|
25
|
+
@buffer = nil
|
26
|
+
end
|
27
|
+
private :initialize
|
28
|
+
|
29
|
+
# Set the handle for the device to communicate with.
|
30
|
+
def dev_handle=(dev)
|
31
|
+
@dev_handle = dev
|
32
|
+
@transfer[:dev_handle] = @dev_handle.pHandle
|
33
|
+
end
|
34
|
+
|
35
|
+
# Timeout for this transfer in millseconds.
|
36
|
+
#
|
37
|
+
# A value of 0 indicates no timeout.
|
38
|
+
def timeout=(value)
|
39
|
+
@transfer[:timeout] = value
|
40
|
+
end
|
41
|
+
|
42
|
+
# Set the address of a valid endpoint to communicate with.
|
43
|
+
def endpoint=(endpoint)
|
44
|
+
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
45
|
+
@transfer[:endpoint] = endpoint
|
46
|
+
end
|
47
|
+
|
48
|
+
# Set output data that should be sent.
|
49
|
+
def buffer=(data)
|
50
|
+
if !@buffer || data.bytesize>@buffer.size
|
51
|
+
free_buffer
|
52
|
+
@buffer = FFI::MemoryPointer.new(data.bytesize, 1, false)
|
53
|
+
end
|
54
|
+
@buffer.put_bytes(0, data)
|
55
|
+
@transfer[:buffer] = @buffer
|
56
|
+
@transfer[:length] = data.bytesize
|
57
|
+
end
|
58
|
+
|
59
|
+
# Retrieve the current data buffer.
|
60
|
+
def buffer
|
61
|
+
@transfer[:buffer].read_string(@transfer[:length])
|
62
|
+
end
|
63
|
+
|
64
|
+
# Clear the current data buffer.
|
65
|
+
def free_buffer
|
66
|
+
if @buffer
|
67
|
+
@buffer.free
|
68
|
+
@buffer = nil
|
69
|
+
@transfer[:buffer] = nil
|
70
|
+
@transfer[:length] = 0
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Allocate +len+ bytes of data buffer for input transfer.
|
75
|
+
#
|
76
|
+
# @param [Fixnum] len Number of bytes to allocate
|
77
|
+
# @param [String, nil] data some data to initialize the buffer with
|
78
|
+
def alloc_buffer(len, data=nil)
|
79
|
+
if !@buffer || len>@buffer.size
|
80
|
+
free_buffer
|
81
|
+
@buffer = FFI::MemoryPointer.new(len, 1, false)
|
82
|
+
end
|
83
|
+
@buffer.put_bytes(0, data) if data
|
84
|
+
@transfer[:buffer] = @buffer
|
85
|
+
@transfer[:length] = len
|
86
|
+
end
|
87
|
+
|
88
|
+
# The number of bytes actually transferred.
|
89
|
+
def actual_length
|
90
|
+
@transfer[:actual_length]
|
91
|
+
end
|
92
|
+
|
93
|
+
# Retrieve the data actually transferred.
|
94
|
+
#
|
95
|
+
# @param [Fixnum] offset optional offset of the retrieved data in the buffer.
|
96
|
+
def actual_buffer(offset=0)
|
97
|
+
@transfer[:buffer].get_bytes(offset, @transfer[:actual_length])
|
98
|
+
end
|
99
|
+
|
100
|
+
# Set the block that will be invoked when the transfer completes,
|
101
|
+
# fails, or is cancelled.
|
102
|
+
#
|
103
|
+
# @param [Proc] proc The block that should be called
|
104
|
+
def callback=(proc)
|
105
|
+
# Save proc to instance variable so that GC doesn't free
|
106
|
+
# the proc object before the transfer.
|
107
|
+
@callback_proc = proc do |pTrans|
|
108
|
+
proc.call(self)
|
109
|
+
end
|
110
|
+
@transfer[:callback] = @callback_proc
|
111
|
+
end
|
112
|
+
|
113
|
+
# The status of the transfer.
|
114
|
+
#
|
115
|
+
# Only for use within transfer callback function or after the callback was called.
|
116
|
+
#
|
117
|
+
# If this is an isochronous transfer, this field may read :TRANSFER_COMPLETED even if there
|
118
|
+
# were errors in the frames. Use the status field in each packet to determine if
|
119
|
+
# errors occurred.
|
120
|
+
def status
|
121
|
+
@transfer[:status]
|
122
|
+
end
|
123
|
+
|
124
|
+
# Submit a transfer.
|
125
|
+
#
|
126
|
+
# This function will fire off the USB transfer and then return immediately.
|
127
|
+
# This method can be called with block. It is called when the transfer completes,
|
128
|
+
# fails, or is cancelled.
|
129
|
+
def submit!(&block)
|
130
|
+
self.callback = block if block_given?
|
131
|
+
|
132
|
+
# 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}"
|
133
|
+
|
134
|
+
res = Call.libusb_submit_transfer( @transfer )
|
135
|
+
LIBUSB.raise_error res, "in libusb_submit_transfer" if res!=0
|
136
|
+
end
|
137
|
+
|
138
|
+
# Asynchronously cancel a previously submitted transfer.
|
139
|
+
#
|
140
|
+
# This function returns immediately, but this does not indicate cancellation is
|
141
|
+
# complete. Your callback function will be invoked at some later time with a
|
142
|
+
# transfer status of :TRANSFER_CANCELLED.
|
143
|
+
def cancel!
|
144
|
+
res = Call.libusb_cancel_transfer( @transfer )
|
145
|
+
LIBUSB.raise_error res, "in libusb_cancel_transfer" if res!=0
|
146
|
+
end
|
147
|
+
|
148
|
+
TransferStatusToError = {
|
149
|
+
:TRANSFER_ERROR => LIBUSB::ERROR_IO,
|
150
|
+
:TRANSFER_TIMED_OUT => LIBUSB::ERROR_TIMEOUT,
|
151
|
+
:TRANSFER_CANCELLED => LIBUSB::ERROR_INTERRUPTED,
|
152
|
+
:TRANSFER_STALL => LIBUSB::ERROR_PIPE,
|
153
|
+
:TRANSFER_NO_DEVICE => LIBUSB::ERROR_NO_DEVICE,
|
154
|
+
:TRANSFER_OVERFLOW => LIBUSB::ERROR_OVERFLOW,
|
155
|
+
}
|
156
|
+
|
157
|
+
# Submit the transfer and wait until the transfer completes or fails.
|
158
|
+
#
|
159
|
+
# A proper {LIBUSB::Error} is raised, in case the transfer did not complete.
|
160
|
+
def submit_and_wait!
|
161
|
+
completed = false
|
162
|
+
submit! do |tr2|
|
163
|
+
completed = true
|
164
|
+
end
|
165
|
+
|
166
|
+
until completed
|
167
|
+
begin
|
168
|
+
@dev_handle.device.context.handle_events
|
169
|
+
rescue ERROR_INTERRUPTED
|
170
|
+
next
|
171
|
+
rescue LIBUSB::Error
|
172
|
+
cancel!
|
173
|
+
until completed
|
174
|
+
@dev_handle.device.context.handle_events
|
175
|
+
end
|
176
|
+
raise
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
raise( TransferStatusToError[status] || ERROR_OTHER, "error #{status}") unless status==:TRANSFER_COMPLETED
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
class BulkTransfer < Transfer
|
185
|
+
def initialize(args={})
|
186
|
+
@transfer = Call::Transfer.new Call.libusb_alloc_transfer(0)
|
187
|
+
@transfer[:type] = TRANSFER_TYPE_BULK
|
188
|
+
@transfer[:timeout] = 1000
|
189
|
+
super
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
class ControlTransfer < Transfer
|
194
|
+
def initialize(args={})
|
195
|
+
@transfer = Call::Transfer.new Call.libusb_alloc_transfer(0)
|
196
|
+
@transfer[:type] = TRANSFER_TYPE_CONTROL
|
197
|
+
@transfer[:timeout] = 1000
|
198
|
+
super
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
class InterruptTransfer < Transfer
|
203
|
+
def initialize(args={})
|
204
|
+
@transfer = Call::Transfer.new Call.libusb_alloc_transfer(0)
|
205
|
+
@transfer[:type] = TRANSFER_TYPE_INTERRUPT
|
206
|
+
@transfer[:timeout] = 1000
|
207
|
+
super
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
class IsoPacket
|
212
|
+
def initialize(ptr, pkg_nr)
|
213
|
+
@packet = Call::IsoPacketDescriptor.new ptr
|
214
|
+
@pkg_nr = pkg_nr
|
215
|
+
end
|
216
|
+
|
217
|
+
def status
|
218
|
+
@packet[:status]
|
219
|
+
end
|
220
|
+
|
221
|
+
def length
|
222
|
+
@packet[:length]
|
223
|
+
end
|
224
|
+
def length=(len)
|
225
|
+
@packet[:length] = len
|
226
|
+
end
|
227
|
+
|
228
|
+
def actual_length
|
229
|
+
@packet[:actual_length]
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
class IsochronousTransfer < Transfer
|
234
|
+
def initialize(num_packets, args={})
|
235
|
+
@ptr = Call.libusb_alloc_transfer(num_packets)
|
236
|
+
@transfer = Call::Transfer.new @ptr
|
237
|
+
@transfer[:type] = TRANSFER_TYPE_ISOCHRONOUS
|
238
|
+
@transfer[:timeout] = 1000
|
239
|
+
@transfer[:num_iso_packets] = num_packets
|
240
|
+
super(args)
|
241
|
+
end
|
242
|
+
|
243
|
+
def num_packets
|
244
|
+
@transfer[:num_iso_packets]
|
245
|
+
end
|
246
|
+
def num_packets=(number)
|
247
|
+
@transfer[:num_iso_packets] = number
|
248
|
+
end
|
249
|
+
|
250
|
+
def [](nr)
|
251
|
+
IsoPacket.new( @ptr + Call::Transfer.size + nr*Call::IsoPacketDescriptor.size, nr)
|
252
|
+
end
|
253
|
+
|
254
|
+
# Convenience function to set the length of all packets in an
|
255
|
+
# isochronous transfer, based on {IsochronousTransfer#num_packets}.
|
256
|
+
def packet_lengths=(len)
|
257
|
+
ptr = @ptr + Call::Transfer.size
|
258
|
+
num_packets.times do
|
259
|
+
ptr.write_uint(len)
|
260
|
+
ptr += Call::IsoPacketDescriptor.size
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
# The actual_length field of the transfer is meaningless and should not
|
265
|
+
# be examined; instead you must refer to the actual_length field of
|
266
|
+
# each individual packet.
|
267
|
+
private :actual_length, :actual_buffer
|
268
|
+
end
|
269
|
+
end
|