libusb 0.7.0-x64-mingw-ucrt
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.appveyor.yml +33 -0
- data/.github/workflows/ci.yml +185 -0
- data/.gitignore +9 -0
- data/.travis.yml +26 -0
- data/.yardopts +6 -0
- data/COPYING +165 -0
- data/Gemfile +19 -0
- data/History.md +193 -0
- data/README.md +184 -0
- data/Rakefile +79 -0
- data/lib/libusb/bos.rb +362 -0
- data/lib/libusb/call.rb +622 -0
- data/lib/libusb/compat.rb +376 -0
- data/lib/libusb/configuration.rb +154 -0
- data/lib/libusb/constants.rb +170 -0
- data/lib/libusb/context.rb +576 -0
- data/lib/libusb/context_reference.rb +38 -0
- data/lib/libusb/dependencies.rb +7 -0
- data/lib/libusb/dev_handle.rb +574 -0
- data/lib/libusb/device.rb +407 -0
- data/lib/libusb/endpoint.rb +195 -0
- data/lib/libusb/eventmachine.rb +187 -0
- data/lib/libusb/gem_helper.rb +151 -0
- data/lib/libusb/interface.rb +60 -0
- data/lib/libusb/libusb_recipe.rb +29 -0
- data/lib/libusb/setting.rb +132 -0
- data/lib/libusb/ss_companion.rb +72 -0
- data/lib/libusb/stdio.rb +25 -0
- data/lib/libusb/transfer.rb +418 -0
- data/lib/libusb/version_gem.rb +19 -0
- data/lib/libusb/version_struct.rb +63 -0
- data/lib/libusb-1.0.dll +0 -0
- data/lib/libusb.rb +146 -0
- data/libusb.gemspec +28 -0
- data/test/test_libusb.rb +42 -0
- data/test/test_libusb_bos.rb +140 -0
- data/test/test_libusb_bulk_stream_transfer.rb +61 -0
- data/test/test_libusb_compat.rb +78 -0
- data/test/test_libusb_compat_mass_storage.rb +81 -0
- data/test/test_libusb_context.rb +88 -0
- data/test/test_libusb_descriptors.rb +245 -0
- data/test/test_libusb_event_machine.rb +118 -0
- data/test/test_libusb_gc.rb +52 -0
- data/test/test_libusb_hotplug.rb +129 -0
- data/test/test_libusb_iso_transfer.rb +56 -0
- data/test/test_libusb_mass_storage.rb +268 -0
- data/test/test_libusb_mass_storage2.rb +96 -0
- data/test/test_libusb_structs.rb +87 -0
- data/test/test_libusb_threads.rb +89 -0
- data/wireshark-usb-sniffer.png +0 -0
- metadata +112 -0
@@ -0,0 +1,418 @@
|
|
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
|
+
#
|
23
|
+
# There are convenience methods for {DevHandle#bulk_transfer}, {DevHandle#control_transfer}
|
24
|
+
# and {DevHandle#interrupt_transfer}, that fit for most use cases.
|
25
|
+
# Using {Transfer} derived classes directly, however, is needed for isochronous transfers and
|
26
|
+
# allows a more advanced buffer management.
|
27
|
+
class Transfer
|
28
|
+
class ZeroCopyMemory < FFI::Pointer
|
29
|
+
attr_reader :size
|
30
|
+
|
31
|
+
def initialize(pDevhandle, ptr, size)
|
32
|
+
@pDevhandle = pDevhandle
|
33
|
+
@size = size
|
34
|
+
super(ptr)
|
35
|
+
end
|
36
|
+
|
37
|
+
def free(id=nil)
|
38
|
+
# puts format("libusb_dev_mem_free(%#x, %d)%s", address, @size||0, id ? " by GC" : '')
|
39
|
+
return unless @size
|
40
|
+
res = Call.libusb_dev_mem_free( @pDevhandle, self, @size )
|
41
|
+
LIBUSB.raise_error res, "in libusb_dev_mem_free" if res!=0
|
42
|
+
@size = nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class << self
|
47
|
+
private :new
|
48
|
+
end
|
49
|
+
|
50
|
+
def initialize(args={})
|
51
|
+
@buffer = nil
|
52
|
+
@completion_flag = Context::CompletionFlag.new
|
53
|
+
@allow_device_memory = false
|
54
|
+
@dev_handle = nil
|
55
|
+
args.each{|k,v| send("#{k}=", v) }
|
56
|
+
end
|
57
|
+
private :initialize
|
58
|
+
|
59
|
+
# Set the handle for the device to communicate with.
|
60
|
+
def dev_handle=(dev)
|
61
|
+
@dev_handle = dev
|
62
|
+
@transfer[:dev_handle] = @dev_handle.pHandle
|
63
|
+
# Now that the transfer is bound to a DevHandle, it must be registered in the Context.
|
64
|
+
# This ensures that the Call::Transfer is freed before libusb_exit, avoiding warnings about still referenced devices.
|
65
|
+
ctx = dev.device.context.instance_variable_get(:@ctx)
|
66
|
+
@transfer.instance_variable_set(:@ctx, ctx.ref_context)
|
67
|
+
end
|
68
|
+
|
69
|
+
# The handle for the device to communicate with.
|
70
|
+
attr_reader :dev_handle
|
71
|
+
|
72
|
+
# Set timeout for this transfer in millseconds.
|
73
|
+
#
|
74
|
+
# A value of 0 indicates no timeout.
|
75
|
+
def timeout=(value)
|
76
|
+
@transfer[:timeout] = value
|
77
|
+
end
|
78
|
+
|
79
|
+
# Get timeout for this transfer in millseconds.
|
80
|
+
#
|
81
|
+
# A value of 0 indicates no timeout.
|
82
|
+
def timeout
|
83
|
+
@transfer[:timeout]
|
84
|
+
end
|
85
|
+
|
86
|
+
# Set the address of a valid endpoint to communicate with.
|
87
|
+
def endpoint=(endpoint)
|
88
|
+
endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
|
89
|
+
@transfer[:endpoint] = endpoint
|
90
|
+
end
|
91
|
+
|
92
|
+
# Set output data that should be sent.
|
93
|
+
# @see #allow_device_memory
|
94
|
+
def buffer=(data)
|
95
|
+
ensure_enough_buffer(data.bytesize)
|
96
|
+
@buffer.put_bytes(0, data)
|
97
|
+
@transfer[:buffer] = @buffer
|
98
|
+
@transfer[:length] = data.bytesize
|
99
|
+
end
|
100
|
+
|
101
|
+
# Retrieve the current data buffer.
|
102
|
+
def buffer
|
103
|
+
@transfer[:buffer].read_string(@transfer[:length])
|
104
|
+
end
|
105
|
+
|
106
|
+
# Clear the current data buffer.
|
107
|
+
def free_buffer
|
108
|
+
if @buffer
|
109
|
+
@buffer.free
|
110
|
+
@buffer = nil
|
111
|
+
@transfer[:buffer] = nil
|
112
|
+
@transfer[:length] = 0
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Allocate +len+ bytes of data buffer for input transfer.
|
117
|
+
#
|
118
|
+
# @param [Fixnum] len Number of bytes to allocate
|
119
|
+
# @param [String, nil] data some data to initialize the buffer with
|
120
|
+
# @see #allow_device_memory
|
121
|
+
def alloc_buffer(len, data=nil)
|
122
|
+
ensure_enough_buffer(len)
|
123
|
+
@buffer.put_bytes(0, data) if data
|
124
|
+
@transfer[:buffer] = @buffer
|
125
|
+
@transfer[:length] = len
|
126
|
+
end
|
127
|
+
|
128
|
+
# The number of bytes actually transferred.
|
129
|
+
def actual_length
|
130
|
+
@transfer[:actual_length]
|
131
|
+
end
|
132
|
+
|
133
|
+
# Try to use persistent device memory.
|
134
|
+
#
|
135
|
+
# If enabled, attempts to allocate a block of persistent DMA memory suitable for transfers against the given device.
|
136
|
+
# The memory is allocated by {#alloc_buffer} or {#buffer=}.
|
137
|
+
# If unsuccessful, ordinary user space memory will be used.
|
138
|
+
#
|
139
|
+
# Using this memory instead of regular memory means that the host controller can use DMA directly into the buffer to increase performance, and also that transfers can no longer fail due to kernel memory fragmentation.
|
140
|
+
#
|
141
|
+
# It requires libusb-1.0.21 and Linux-4.6 to be effective, but it can safely be enabled on other systems.
|
142
|
+
#
|
143
|
+
# Note that this type of memory is bound to the {#dev_handle=}.
|
144
|
+
# So even if the {DevHandle} is closed, the memory is still accessable and the device is locked.
|
145
|
+
# It is free'd by the garbage collector eventually, but in order to close the device deterministic, it is required to call {#free_buffer} on all {Transfer}s which use persistent device memory.
|
146
|
+
#
|
147
|
+
# @see #free_buffer
|
148
|
+
# @see #memory_type
|
149
|
+
attr_accessor :allow_device_memory
|
150
|
+
|
151
|
+
# @return +:device_memory+ - If persistent device memory is allocated.
|
152
|
+
# @return +:user_space+ - If user space memory is allocated.
|
153
|
+
# @return +nil+ - If no memory is allocated.
|
154
|
+
def memory_type
|
155
|
+
case @buffer
|
156
|
+
when ZeroCopyMemory then :device_memory
|
157
|
+
when FFI::MemoryPointer then :user_space
|
158
|
+
else nil
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def ensure_enough_buffer(len)
|
163
|
+
if !@buffer || len>@buffer.size
|
164
|
+
free_buffer
|
165
|
+
# Try to use zero-copy-memory and fallback to FFI-memory if not available
|
166
|
+
if @allow_device_memory && @dev_handle && Call.respond_to?(:libusb_dev_mem_alloc)
|
167
|
+
ptr = Call.libusb_dev_mem_alloc( @dev_handle.pHandle, len )
|
168
|
+
# puts format("libusb_dev_mem_alloc(%d) => %#x", len, ptr.address)
|
169
|
+
unless ptr.null?
|
170
|
+
buffer = ZeroCopyMemory.new(@dev_handle.pHandle, ptr, len)
|
171
|
+
ObjectSpace.define_finalizer(self, buffer.method(:free))
|
172
|
+
end
|
173
|
+
end
|
174
|
+
@buffer = buffer || FFI::MemoryPointer.new(len, 1, false)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
private :ensure_enough_buffer
|
178
|
+
|
179
|
+
# Retrieve the data actually transferred.
|
180
|
+
#
|
181
|
+
# @param [Fixnum] offset optional offset of the retrieved data in the buffer.
|
182
|
+
def actual_buffer(offset=0)
|
183
|
+
@transfer[:buffer].get_bytes(offset, @transfer[:actual_length])
|
184
|
+
end
|
185
|
+
|
186
|
+
# Set the block that will be invoked when the transfer completes,
|
187
|
+
# fails, or is cancelled.
|
188
|
+
#
|
189
|
+
# @param [Proc] proc The block that should be called
|
190
|
+
def callback=(proc)
|
191
|
+
# Save proc to instance variable so that GC doesn't free
|
192
|
+
# the proc object before the transfer.
|
193
|
+
@callback_proc = proc do |pTrans|
|
194
|
+
proc.call(self)
|
195
|
+
end
|
196
|
+
@transfer[:callback] = @callback_proc
|
197
|
+
end
|
198
|
+
|
199
|
+
# The status of the transfer.
|
200
|
+
#
|
201
|
+
# Only for use within transfer callback function or after the callback was called.
|
202
|
+
#
|
203
|
+
# If this is an isochronous transfer, this field may read :TRANSFER_COMPLETED even if there
|
204
|
+
# were errors in the frames. Use the status field in each packet to determine if
|
205
|
+
# errors occurred.
|
206
|
+
def status
|
207
|
+
@transfer[:status]
|
208
|
+
end
|
209
|
+
|
210
|
+
# Submit a transfer.
|
211
|
+
#
|
212
|
+
# This function will fire off the USB transfer and then return immediately.
|
213
|
+
# This method can be called with block. It is called when the transfer completes,
|
214
|
+
# fails, or is cancelled.
|
215
|
+
def submit!(&block)
|
216
|
+
self.callback = block if block_given?
|
217
|
+
|
218
|
+
# 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}"
|
219
|
+
|
220
|
+
res = Call.libusb_submit_transfer( @transfer )
|
221
|
+
LIBUSB.raise_error res, "in libusb_submit_transfer" if res!=0
|
222
|
+
end
|
223
|
+
|
224
|
+
# Asynchronously cancel a previously submitted transfer.
|
225
|
+
#
|
226
|
+
# This function returns immediately, but this does not indicate cancellation is
|
227
|
+
# complete. Your callback function will be invoked at some later time with a
|
228
|
+
# transfer status of :TRANSFER_CANCELLED.
|
229
|
+
def cancel!
|
230
|
+
res = Call.libusb_cancel_transfer( @transfer )
|
231
|
+
LIBUSB.raise_error res, "in libusb_cancel_transfer" if res!=0
|
232
|
+
end
|
233
|
+
|
234
|
+
TransferStatusToError = {
|
235
|
+
:TRANSFER_ERROR => LIBUSB::ERROR_IO,
|
236
|
+
:TRANSFER_TIMED_OUT => LIBUSB::ERROR_TIMEOUT,
|
237
|
+
:TRANSFER_CANCELLED => LIBUSB::ERROR_INTERRUPTED,
|
238
|
+
:TRANSFER_STALL => LIBUSB::ERROR_PIPE,
|
239
|
+
:TRANSFER_NO_DEVICE => LIBUSB::ERROR_NO_DEVICE,
|
240
|
+
:TRANSFER_OVERFLOW => LIBUSB::ERROR_OVERFLOW,
|
241
|
+
}
|
242
|
+
|
243
|
+
# Submit the transfer and wait until the transfer completes or fails.
|
244
|
+
#
|
245
|
+
# Inspect {#status} to check for transfer errors.
|
246
|
+
def submit_and_wait
|
247
|
+
raise ArgumentError, "#{self.class}#dev_handle not set" unless @dev_handle
|
248
|
+
|
249
|
+
@completion_flag.completed = false
|
250
|
+
submit! do |tr2|
|
251
|
+
@completion_flag.completed = true
|
252
|
+
end
|
253
|
+
|
254
|
+
until @completion_flag.completed?
|
255
|
+
begin
|
256
|
+
@dev_handle.device.context.handle_events nil, @completion_flag
|
257
|
+
rescue ERROR_INTERRUPTED
|
258
|
+
next
|
259
|
+
rescue Exception
|
260
|
+
cancel!
|
261
|
+
until @completion_flag.completed?
|
262
|
+
@dev_handle.device.context.handle_events nil, @completion_flag
|
263
|
+
end
|
264
|
+
raise
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
# Submit the transfer and wait until the transfer completes or fails.
|
270
|
+
#
|
271
|
+
# A proper {LIBUSB::Error} is raised, in case the transfer did not complete.
|
272
|
+
def submit_and_wait!
|
273
|
+
submit_and_wait
|
274
|
+
|
275
|
+
raise( TransferStatusToError[status] || ERROR_OTHER, "error #{status}") unless status==:TRANSFER_COMPLETED
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
class BulkTransfer < Transfer
|
280
|
+
def self.new(*)
|
281
|
+
super
|
282
|
+
end
|
283
|
+
|
284
|
+
def initialize(args={})
|
285
|
+
@transfer = Call::Transfer.new Call.libusb_alloc_transfer(0)
|
286
|
+
@transfer[:type] = TRANSFER_TYPE_BULK
|
287
|
+
@transfer[:timeout] = 1000
|
288
|
+
super
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
if Call.respond_to?(:libusb_transfer_get_stream_id)
|
293
|
+
|
294
|
+
# Transfer class for USB bulk transfers using USB-3.0 streams.
|
295
|
+
#
|
296
|
+
# @see DevHandle#alloc_streams
|
297
|
+
#
|
298
|
+
# Available since libusb-1.0.19.
|
299
|
+
class BulkStreamTransfer < Transfer
|
300
|
+
def self.new(*)
|
301
|
+
super
|
302
|
+
end
|
303
|
+
|
304
|
+
def initialize(args={})
|
305
|
+
@transfer = Call::Transfer.new Call.libusb_alloc_transfer(0)
|
306
|
+
@transfer[:type] = TRANSFER_TYPE_BULK_STREAM
|
307
|
+
@transfer[:timeout] = 1000
|
308
|
+
super
|
309
|
+
end
|
310
|
+
|
311
|
+
# Set a transfers bulk stream id.
|
312
|
+
#
|
313
|
+
# @param [Fixnum] stream_id the stream id to set
|
314
|
+
def stream_id=(v)
|
315
|
+
Call.libusb_transfer_set_stream_id(@transfer, v)
|
316
|
+
v
|
317
|
+
end
|
318
|
+
|
319
|
+
# Get a transfers bulk stream id.
|
320
|
+
#
|
321
|
+
# Available since libusb-1.0.19.
|
322
|
+
#
|
323
|
+
# @return [Fixnum] the stream id for the transfer
|
324
|
+
def stream_id
|
325
|
+
Call.libusb_transfer_get_stream_id(@transfer)
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
class ControlTransfer < Transfer
|
331
|
+
def self.new(*)
|
332
|
+
super
|
333
|
+
end
|
334
|
+
|
335
|
+
def initialize(args={})
|
336
|
+
@transfer = Call::Transfer.new Call.libusb_alloc_transfer(0)
|
337
|
+
@transfer[:type] = TRANSFER_TYPE_CONTROL
|
338
|
+
@transfer[:timeout] = 1000
|
339
|
+
super
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
class InterruptTransfer < Transfer
|
344
|
+
def self.new(*)
|
345
|
+
super
|
346
|
+
end
|
347
|
+
|
348
|
+
def initialize(args={})
|
349
|
+
@transfer = Call::Transfer.new Call.libusb_alloc_transfer(0)
|
350
|
+
@transfer[:type] = TRANSFER_TYPE_INTERRUPT
|
351
|
+
@transfer[:timeout] = 1000
|
352
|
+
super
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
class IsoPacket
|
357
|
+
def initialize(ptr, pkg_nr)
|
358
|
+
@packet = Call::IsoPacketDescriptor.new ptr
|
359
|
+
@pkg_nr = pkg_nr
|
360
|
+
end
|
361
|
+
|
362
|
+
def status
|
363
|
+
@packet[:status]
|
364
|
+
end
|
365
|
+
|
366
|
+
def length
|
367
|
+
@packet[:length]
|
368
|
+
end
|
369
|
+
def length=(len)
|
370
|
+
@packet[:length] = len
|
371
|
+
end
|
372
|
+
|
373
|
+
def actual_length
|
374
|
+
@packet[:actual_length]
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
class IsochronousTransfer < Transfer
|
379
|
+
def self.new(*)
|
380
|
+
super
|
381
|
+
end
|
382
|
+
|
383
|
+
def initialize(num_packets, args={})
|
384
|
+
@ptr = Call.libusb_alloc_transfer(num_packets)
|
385
|
+
@transfer = Call::Transfer.new @ptr
|
386
|
+
@transfer[:type] = TRANSFER_TYPE_ISOCHRONOUS
|
387
|
+
@transfer[:timeout] = 1000
|
388
|
+
@transfer[:num_iso_packets] = num_packets
|
389
|
+
super(args)
|
390
|
+
end
|
391
|
+
|
392
|
+
def num_packets
|
393
|
+
@transfer[:num_iso_packets]
|
394
|
+
end
|
395
|
+
def num_packets=(number)
|
396
|
+
@transfer[:num_iso_packets] = number
|
397
|
+
end
|
398
|
+
|
399
|
+
def [](nr)
|
400
|
+
IsoPacket.new( @ptr + Call::Transfer.size + nr*Call::IsoPacketDescriptor.size, nr)
|
401
|
+
end
|
402
|
+
|
403
|
+
# Convenience function to set the length of all packets in an
|
404
|
+
# isochronous transfer, based on {IsochronousTransfer#num_packets}.
|
405
|
+
def packet_lengths=(len)
|
406
|
+
ptr = @ptr + Call::Transfer.size
|
407
|
+
num_packets.times do
|
408
|
+
ptr.write_uint(len)
|
409
|
+
ptr += Call::IsoPacketDescriptor.size
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
# The actual_length field of the transfer is meaningless and should not
|
414
|
+
# be examined; instead you must refer to the actual_length field of
|
415
|
+
# each individual packet.
|
416
|
+
private :actual_length, :actual_buffer
|
417
|
+
end
|
418
|
+
end
|
@@ -0,0 +1,19 @@
|
|
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
|
+
module LIBUSB
|
17
|
+
# Library version of libusb for Ruby
|
18
|
+
VERSION = "0.7.0"
|
19
|
+
end
|
@@ -0,0 +1,63 @@
|
|
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 Version < FFI::Struct
|
20
|
+
layout :major, :uint16,
|
21
|
+
:minor, :uint16,
|
22
|
+
:micro, :uint16,
|
23
|
+
:nano, :uint16,
|
24
|
+
:rc, :pointer,
|
25
|
+
:describe, :pointer
|
26
|
+
|
27
|
+
# Library major version.
|
28
|
+
def major
|
29
|
+
self[:major]
|
30
|
+
end
|
31
|
+
# Library minor version.
|
32
|
+
def minor
|
33
|
+
self[:minor]
|
34
|
+
end
|
35
|
+
# Library micro version.
|
36
|
+
def micro
|
37
|
+
self[:micro]
|
38
|
+
end
|
39
|
+
# Library nano version.
|
40
|
+
def nano
|
41
|
+
self[:nano]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Library release candidate suffix string, e.g. "-rc4".
|
45
|
+
def rc
|
46
|
+
self[:rc].read_string
|
47
|
+
end
|
48
|
+
|
49
|
+
# For ABI compatibility only.
|
50
|
+
def describe
|
51
|
+
self[:describe].read_string
|
52
|
+
end
|
53
|
+
|
54
|
+
# Version string, e.g. "1.2.3-rc4"
|
55
|
+
def to_s
|
56
|
+
"#{major}.#{minor}.#{micro}#{rc}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def inspect
|
60
|
+
"\#<#{self.class} #{to_s}>"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/libusb-1.0.dll
ADDED
Binary file
|
data/lib/libusb.rb
ADDED
@@ -0,0 +1,146 @@
|
|
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
|
+
module LIBUSB
|
17
|
+
require 'libusb/call'
|
18
|
+
require 'libusb/constants'
|
19
|
+
require 'libusb/context'
|
20
|
+
autoload :VERSION, 'libusb/version_gem'
|
21
|
+
autoload :Version, 'libusb/version_struct'
|
22
|
+
autoload :Configuration, 'libusb/configuration'
|
23
|
+
autoload :ContextReference, 'libusb/context_reference'
|
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
|
+
autoload :SsCompanion, 'libusb/ss_companion'
|
30
|
+
autoload :Stdio, 'libusb/stdio'
|
31
|
+
autoload :Bos, 'libusb/bos'
|
32
|
+
%w[ Transfer BulkTransfer BulkStreamTransfer ControlTransfer InterruptTransfer IsoPacket IsochronousTransfer ].each do |klass|
|
33
|
+
autoload klass, 'libusb/transfer'
|
34
|
+
end
|
35
|
+
|
36
|
+
class << self
|
37
|
+
if Call.respond_to?(:libusb_get_version)
|
38
|
+
# Get version of the underlying libusb library.
|
39
|
+
# Available since libusb-1.0.10.
|
40
|
+
# @return [Version] version object
|
41
|
+
def version
|
42
|
+
Version.new(Call.libusb_get_version)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
if Call.respond_to?(:libusb_has_capability)
|
47
|
+
# Check at runtime if the loaded library has a given capability.
|
48
|
+
# Available since libusb-1.0.9.
|
49
|
+
# @param [Symbol] capability the {Call::Capabilities Capabilities} symbol to check for
|
50
|
+
# @return [Boolean] +true+ if the running library has the capability, +false+ otherwise
|
51
|
+
def has_capability?(capability)
|
52
|
+
r = Call.libusb_has_capability(capability)
|
53
|
+
return r != 0
|
54
|
+
end
|
55
|
+
else
|
56
|
+
def has_capability?(capability)
|
57
|
+
false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private def expect_option_args(exp, is)
|
62
|
+
raise ArgumentError, "wrong number of arguments (given #{is+1}, expected #{exp+1})" if is != exp
|
63
|
+
end
|
64
|
+
|
65
|
+
private def wrap_log_cb(block, mode)
|
66
|
+
if block
|
67
|
+
cb_proc = proc do |p_ctx, lev, str|
|
68
|
+
ctx = case p_ctx
|
69
|
+
when FFI::Pointer::NULL then nil
|
70
|
+
else p_ctx.to_i
|
71
|
+
end
|
72
|
+
block.call(ctx, lev, str)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Avoid garbage collection of the proc, since only the function pointer is given to libusb
|
77
|
+
if Call::LogCbMode.to_native(mode, nil) & LOG_CB_GLOBAL != 0
|
78
|
+
@log_cb_global_proc = cb_proc
|
79
|
+
end
|
80
|
+
if Call::LogCbMode.to_native(mode, nil) & LOG_CB_CONTEXT != 0
|
81
|
+
@log_cb_context_proc = cb_proc
|
82
|
+
end
|
83
|
+
cb_proc
|
84
|
+
end
|
85
|
+
|
86
|
+
private def option_args_to_ffi(option, args, ctx)
|
87
|
+
case option
|
88
|
+
when :OPTION_LOG_LEVEL, LIBUSB::OPTION_LOG_LEVEL
|
89
|
+
expect_option_args(1, args.length)
|
90
|
+
[:libusb_log_level, args[0]]
|
91
|
+
when :OPTION_USE_USBDK, LIBUSB::OPTION_USE_USBDK
|
92
|
+
expect_option_args(0, args.length)
|
93
|
+
[]
|
94
|
+
when :OPTION_NO_DEVICE_DISCOVERY, LIBUSB::OPTION_NO_DEVICE_DISCOVERY
|
95
|
+
expect_option_args(0, args.length)
|
96
|
+
[]
|
97
|
+
when :OPTION_LOG_CB, LIBUSB::OPTION_LOG_CB
|
98
|
+
expect_option_args(1, args.length)
|
99
|
+
cb_proc = ctx.send(:wrap_log_cb, args[0], LOG_CB_CONTEXT)
|
100
|
+
[:libusb_log_cb, cb_proc]
|
101
|
+
else
|
102
|
+
raise ArgumentError, "unknown option #{option.inspect}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
if Call.respond_to?(:libusb_set_option)
|
107
|
+
# Set an default option in the libusb library.
|
108
|
+
#
|
109
|
+
# Use this function to configure a specific option within the library.
|
110
|
+
# See {Call::Options option list}.
|
111
|
+
#
|
112
|
+
# Some options require one or more arguments to be provided.
|
113
|
+
# Consult each option's documentation for specific requirements.
|
114
|
+
#
|
115
|
+
# The option will be added to a list of default options that will be applied to all subsequently created contexts.
|
116
|
+
#
|
117
|
+
# Available since libusb-1.0.22, LIBUSB_API_VERSION >= 0x01000106
|
118
|
+
#
|
119
|
+
# @param [Symbol, Fixnum] option
|
120
|
+
# @param args Zero or more arguments depending on +option+
|
121
|
+
#
|
122
|
+
# Available since libusb-1.0.22
|
123
|
+
def set_option(option, *args)
|
124
|
+
ffi_args = option_args_to_ffi(option, args, self)
|
125
|
+
res = Call.libusb_set_option(nil, option, *ffi_args)
|
126
|
+
LIBUSB.raise_error res, "in libusb_set_option" if res<0
|
127
|
+
end
|
128
|
+
|
129
|
+
# Convenience function to set default options in the libusb library.
|
130
|
+
#
|
131
|
+
# Use this function to configure any number of options within the library.
|
132
|
+
# It takes a Hash the same way as given to {Context.initialize}.
|
133
|
+
# See also {Call::Options option list}.
|
134
|
+
#
|
135
|
+
# Available since libusb-1.0.22, LIBUSB_API_VERSION >= 0x01000106
|
136
|
+
#
|
137
|
+
# @param [Hash{Call::Options => Object}] options Option hash
|
138
|
+
# @see set_option
|
139
|
+
def set_options(options={})
|
140
|
+
options.each do |k, v|
|
141
|
+
set_option(k, *Array(v))
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
data/libusb.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "libusb/version_gem"
|
4
|
+
require "libusb/dependencies"
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "libusb"
|
8
|
+
s.version = LIBUSB::VERSION
|
9
|
+
s.authors = ["Lars Kanis"]
|
10
|
+
s.email = ["lars@greiz-reinsdorf.de"]
|
11
|
+
s.homepage = "http://github.com/larskanis/libusb"
|
12
|
+
s.summary = %q{Access USB devices from Ruby via libusb-1.0}
|
13
|
+
s.description = %q{LIBUSB is a Ruby binding that gives Ruby programmers access to arbitrary USB devices}
|
14
|
+
s.licenses = ['LGPL-3.0']
|
15
|
+
s.rdoc_options = %w[--main README.md --charset=UTF-8]
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.files << "ports/archives/libusb-#{LIBUSB::LIBUSB_VERSION}.tar.bz2"
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
s.extensions = ['ext/extconf.rb']
|
23
|
+
s.metadata["yard.run"] = "yri"
|
24
|
+
|
25
|
+
s.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
|
26
|
+
s.add_runtime_dependency 'ffi', '~> 1.0'
|
27
|
+
s.add_runtime_dependency 'mini_portile2', LIBUSB::MINI_PORTILE_VERSION
|
28
|
+
end
|