libusb 0.7.0-x64-mingw-ucrt

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.appveyor.yml +33 -0
  3. data/.github/workflows/ci.yml +185 -0
  4. data/.gitignore +9 -0
  5. data/.travis.yml +26 -0
  6. data/.yardopts +6 -0
  7. data/COPYING +165 -0
  8. data/Gemfile +19 -0
  9. data/History.md +193 -0
  10. data/README.md +184 -0
  11. data/Rakefile +79 -0
  12. data/lib/libusb/bos.rb +362 -0
  13. data/lib/libusb/call.rb +622 -0
  14. data/lib/libusb/compat.rb +376 -0
  15. data/lib/libusb/configuration.rb +154 -0
  16. data/lib/libusb/constants.rb +170 -0
  17. data/lib/libusb/context.rb +576 -0
  18. data/lib/libusb/context_reference.rb +38 -0
  19. data/lib/libusb/dependencies.rb +7 -0
  20. data/lib/libusb/dev_handle.rb +574 -0
  21. data/lib/libusb/device.rb +407 -0
  22. data/lib/libusb/endpoint.rb +195 -0
  23. data/lib/libusb/eventmachine.rb +187 -0
  24. data/lib/libusb/gem_helper.rb +151 -0
  25. data/lib/libusb/interface.rb +60 -0
  26. data/lib/libusb/libusb_recipe.rb +29 -0
  27. data/lib/libusb/setting.rb +132 -0
  28. data/lib/libusb/ss_companion.rb +72 -0
  29. data/lib/libusb/stdio.rb +25 -0
  30. data/lib/libusb/transfer.rb +418 -0
  31. data/lib/libusb/version_gem.rb +19 -0
  32. data/lib/libusb/version_struct.rb +63 -0
  33. data/lib/libusb-1.0.dll +0 -0
  34. data/lib/libusb.rb +146 -0
  35. data/libusb.gemspec +28 -0
  36. data/test/test_libusb.rb +42 -0
  37. data/test/test_libusb_bos.rb +140 -0
  38. data/test/test_libusb_bulk_stream_transfer.rb +61 -0
  39. data/test/test_libusb_compat.rb +78 -0
  40. data/test/test_libusb_compat_mass_storage.rb +81 -0
  41. data/test/test_libusb_context.rb +88 -0
  42. data/test/test_libusb_descriptors.rb +245 -0
  43. data/test/test_libusb_event_machine.rb +118 -0
  44. data/test/test_libusb_gc.rb +52 -0
  45. data/test/test_libusb_hotplug.rb +129 -0
  46. data/test/test_libusb_iso_transfer.rb +56 -0
  47. data/test/test_libusb_mass_storage.rb +268 -0
  48. data/test/test_libusb_mass_storage2.rb +96 -0
  49. data/test/test_libusb_structs.rb +87 -0
  50. data/test/test_libusb_threads.rb +89 -0
  51. data/wireshark-usb-sniffer.png +0 -0
  52. 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
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