libusb 0.7.2 → 0.8.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5d72428d38fc654c53b2a8d3bb182ae790f2860154eb1d350f7ca57292bc0ff6
4
- data.tar.gz: 737cc6576964289bc028380e39d5404fd91fec0fb85f2ab919df9c8c6449a297
3
+ metadata.gz: 81987d2e99821ceff5acf54c463c28b83fcb586aa401d3c90d23af4777bde459
4
+ data.tar.gz: 9921b3deb351982884c2848313721d786fe1e4bcf5e4db2dcff37cda297ec05a
5
5
  SHA512:
6
- metadata.gz: ae37e6a1cb842de3b0e1eacd17896a35276c7f68c4f55d9f9b00ae21d832aaacc6a671171f2b5c85ee64449147b668239e8c6fb44203a4a394c4dade30b16928
7
- data.tar.gz: 7270194dac34b088447b998e76b805e7c33b17a03dd67209c80c4f9d7cbd52fd6610f1092e61027454179f1626f8f17649fdc414c6d80b68f09452235ae3f6eb
6
+ metadata.gz: 39ab11140cb2d68b5540337fb7d5c27743956cd479576002f194ac5f2b89d67711819fee84932f76090cb877cb11034294721f56031b4578fb717a722ebf0184
7
+ data.tar.gz: c11df155a20a7cfbeeae72bb313c79ea47b1d5fb3a6c94869e294ba261f7a995901553aafc943719ffd13db31e5d592c32c6a08ce53b887d3382788de9a83434
data/Gemfile CHANGED
@@ -10,8 +10,9 @@ end
10
10
 
11
11
  gem 'rake-compiler-dock', '~> 1.1'
12
12
  gem 'rake-compiler', '~> 1.0'
13
- gem 'bundler', '>= 1', '< 3'
13
+ gem 'bundler', '>= 1', '< 5.a'
14
14
  gem 'yard', '~> 0.6', '>= 0.9.36'
15
+ gem 'ostruct', '~> 0.1'
15
16
 
16
17
  # For some reason this is required in addition to the gemspec
17
18
  # when 'bundle config force_ruby_platform true' is active:
data/History.md CHANGED
@@ -1,3 +1,19 @@
1
+ 0.8.0 / 2026-08-02
2
+ ------------------
3
+
4
+ Added:
5
+ * Add LIBUSB::SsplusUsbDeviceCapability introduced in libusb-1.0.28. #53
6
+ * Add LIBUSB::Device#device_string and prefer it in Device#inspect. #54
7
+ This retrievs product string, manufacturer and serial number from the OS without opening the device.
8
+ * Enable hotplug support on Windows introduced in libusb-1.0.30.
9
+ * Add LIBUSB::Device#get_session_data introduced in libusb-1.0.30. #54
10
+
11
+ Changed:
12
+ * Don't kill the EventMachine reactor on LIBUSB_ERROR_INTERRUPTED. #52
13
+ * Improve yard docs.
14
+ * Update builtin libusb to 1.0.30
15
+
16
+
1
17
  0.7.2 / 2024-10-23
2
18
  ------------------
3
19
 
@@ -150,7 +166,7 @@ Deprecated:
150
166
  * Build bundled libusbx sources in case libusb-1.0.so can not be loaded from the system
151
167
  * Replace Hoe with Bundler
152
168
  * Add timeout and completion_flag to Context#handle_events
153
- * Add asynchronous DevHandle#{control|interrupt|bulk}_transfer method variants
169
+ * Add asynchronous `DevHandle#{control|interrupt|bulk}_transfer` method variants
154
170
  * Add the ability to retrieve the data already transfered when it comes to an exception
155
171
  * Add notification API for libusb's file describtors for event driven USB transfers
156
172
  * Add experimental integration to EventMachine
data/Rakefile CHANGED
@@ -5,7 +5,6 @@ require 'bundler/gem_helper'
5
5
  require 'rubygems/package_task'
6
6
  require 'pathname'
7
7
  require 'uri'
8
- require 'ostruct'
9
8
  require 'rake/clean'
10
9
  require_relative 'lib/libusb/libusb_recipe'
11
10
  require_relative 'lib/libusb/gem_helper'
@@ -39,7 +38,7 @@ task :gemfile_libusb_gem do
39
38
  end
40
39
 
41
40
  task :test do
42
- sh "ruby -w -W2 -I.:lib -e \"#{Dir["test/test_*.rb"].map{|f| "require '#{f}';"}.join}\" -- -v"
41
+ sh "ruby -w -W2 -I. -Ilib -e \"#{Dir["test/test_*.rb"].map{|f| "require '#{f}';"}.join}\" -- -v"
43
42
  end
44
43
  task :default => :test
45
44
 
data/ext/extconf.rb CHANGED
@@ -70,6 +70,7 @@ def build_bundled_libusb(have_udev)
70
70
 
71
71
  require_relative '../lib/libusb/libusb_recipe'
72
72
  recipe = LIBUSB::LibusbRecipe.new
73
+ recipe.configure_options << "--enable-windows-hotplug"
73
74
  recipe.configure_options << "--disable-udev" unless have_udev
74
75
  recipe.cook_and_activate
75
76
  recipe.path
data/lib/libusb/bos.rb CHANGED
@@ -169,10 +169,11 @@ module LIBUSB
169
169
  # device supports all its functionality when connected at
170
170
  # full speed and above then it sets this value to 1.
171
171
  #
172
- # 0 - low speed
173
- # 1 - full speed
174
- # 2 - high speed
175
- # 3 - super speed
172
+ # * 0 - low speed
173
+ # * 1 - full speed
174
+ # * 2 - high speed
175
+ # * 3 - super speed
176
+ #
176
177
  # @return [Integer]
177
178
  def bFunctionalitySupport
178
179
  self[:bFunctionalitySupport]
@@ -189,6 +190,137 @@ module LIBUSB
189
190
  end
190
191
  end
191
192
 
193
+ # A structure representing a SuperSpeedPlus descriptor attribute
194
+ #
195
+ # @see SsplusUsbDeviceCapability
196
+ class SsplusSublinkAttribute < FFI::Struct
197
+
198
+ layout :ssid, :uint8,
199
+ :exponent, Call::SuperspeedplusSublinkAttributeExponent,
200
+ :type, Call::SuperspeedplusSublinkAttributeSublinkType,
201
+ :direction, Call::SuperspeedplusSublinkAttributeSublinkDirection,
202
+ :protocol, Call::SuperspeedplusSublinkAttributeLinkProtocol,
203
+ :mantissa, :uint16
204
+
205
+ # Sublink Speed Attribute ID (SSID).
206
+ #
207
+ # This field is an ID that uniquely identifies the speed of this sublink.
208
+ def ssid
209
+ self[:ssid]
210
+ end
211
+
212
+ # This field defines the base 10 exponent times 3, that shall be applied to the mantissa.
213
+ # @see Call::SuperspeedplusSublinkAttributeExponent
214
+ def exponent
215
+ self[:exponent]
216
+ end
217
+
218
+ # This field identifies whether the Sublink Speed Attribute defines a symmetric or asymmetric bit rate.
219
+ # @see Call::SuperspeedplusSublinkAttributeSublinkType
220
+ def type
221
+ self[:type]
222
+ end
223
+
224
+ # This field indicates if this Sublink Speed Attribute defines the receive or transmit bit rate.
225
+ # @see Call::SuperspeedplusSublinkAttributeSublinkDirection
226
+ def direction
227
+ self[:direction]
228
+ end
229
+
230
+ # This field identifies the protocol supported by the link.
231
+ # @see Call::SuperspeedplusSublinkAttributeLinkProtocol
232
+ def protocol
233
+ self[:protocol]
234
+ end
235
+
236
+ # This field defines the mantissa that shall be applied to the exponent when calculating the maximum bit rate.
237
+ def mantissa
238
+ self[:mantissa]
239
+ end
240
+
241
+ def to_s_human_readable
242
+ exponents = {
243
+ SSPLUS_ATTR_EXP_BPS: " ",
244
+ SSPLUS_ATTR_EXP_KBS: "K",
245
+ SSPLUS_ATTR_EXP_MBS: "M",
246
+ SSPLUS_ATTR_EXP_GBS: "G",
247
+ }
248
+ format("id=%u speed=%u%sbs %s %s SuperSpeed%s",
249
+ ssid,
250
+ mantissa,
251
+ exponents[exponent],
252
+ type == :SSPLUS_ATTR_TYPE_ASYM ? "Asym" : "Sym",
253
+ direction == :SSPLUS_ATTR_DIR_TX ? "TX" : "RX",
254
+ protocol == :SSPLUS_ATTR_PROT_SSPLUS ? "+": "" )
255
+ end
256
+
257
+ def inspect
258
+ "\#<#{self.class} #{to_s_human_readable}>"
259
+ end
260
+ end
261
+
262
+ # A structure representing the SuperSpeedPlus descriptor
263
+ # This descriptor is documented in section 9.6.2.5 of the USB 3.1 specification.
264
+ class SsplusUsbDeviceCapability < FFI::Struct
265
+ include ContextReference
266
+
267
+ layout :numSublinkSpeedAttributes, :uint8,
268
+ :numSublinkSpeedIDs, :uint8,
269
+ :ssid, :uint8,
270
+ :minRxLaneCount, :uint8,
271
+ :minTxLaneCount, :uint8,
272
+ :sublinkSpeedAttributes, [SsplusSublinkAttribute, 0]
273
+
274
+ def initialize(ctx, *args)
275
+ super(*args)
276
+
277
+ register_context(ctx, :libusb_free_ssplus_usb_device_capability_descriptor)
278
+ end
279
+
280
+ def inspect
281
+ attrs = %i[ numSublinkSpeedAttributes numSublinkSpeedIDs ssid minRxLaneCount minTxLaneCount ].map do |key|
282
+ "#{key}: #{self.send(key).inspect}"
283
+ end
284
+ "\#<#{self.class} #{attrs.join(", ")}>"
285
+ end
286
+
287
+ # Sublink Speed Attribute Count
288
+ def numSublinkSpeedAttributes
289
+ self[:numSublinkSpeedAttributes]
290
+ end
291
+
292
+ # Sublink Speed ID Count
293
+ def numSublinkSpeedIDs
294
+ self[:numSublinkSpeedIDs]
295
+ end
296
+
297
+ # Unique ID to indicates the minimum lane speed
298
+ def ssid
299
+ self[:ssid]
300
+ end
301
+
302
+ # This field indicates the minimum receive lane count.
303
+ def minRxLaneCount
304
+ self[:minRxLaneCount]
305
+ end
306
+
307
+ # This field indicates the minimum transmit lane count
308
+ def minTxLaneCount
309
+ self[:minTxLaneCount]
310
+ end
311
+
312
+ # Returns an Array of SublinkSpeedAttributes
313
+ #
314
+ # The array size equals {numSublinkSpeedAttributes}.
315
+ #
316
+ # @return [Array<SsplusSublinkAttribute>]
317
+ def sublinkSpeedAttributes
318
+ self[:numSublinkSpeedAttributes].times.map do |idx|
319
+ self[:sublinkSpeedAttributes][idx]
320
+ end
321
+ end
322
+ end
323
+
192
324
  # A structure representing the Container ID descriptor.
193
325
  # This descriptor is documented in section 9.6.2.3 of the USB 3.0 specification.
194
326
  # All multiple-byte fields, except UUIDs, are represented in host-endian format.
@@ -308,33 +440,40 @@ module LIBUSB
308
440
  self[:bNumDeviceCaps]
309
441
  end
310
442
 
443
+ private def wrap_device_capability(cap, func, struct)
444
+ if Call.respond_to?(func)
445
+ pp_ext = FFI::MemoryPointer.new :pointer
446
+ res = Call.send(func, @ctx, cap.pointer, pp_ext)
447
+ struct.new(@ctx, pp_ext.read_pointer) if res==0
448
+ end || cap
449
+ end
450
+
311
451
  # bNumDeviceCap Device Capability Descriptors
312
452
  #
313
453
  # @return [Array<Bos::DeviceCapability, Bos::Usb20Extension, Bos::SsUsbDeviceCapability, Bos::ContainerId>]
314
454
  def device_capabilities
315
- pp_ext = FFI::MemoryPointer.new :pointer
316
455
  caps = []
317
456
  # Capabilities are appended to the bos header
318
457
  ptr = pointer + offset_of(:dev_capability)
319
458
  bNumDeviceCaps.times do
320
459
  cap = DeviceCapability.new self, ptr.read_pointer
321
- case cap.bDevCapabilityType
460
+ cap = case cap.bDevCapabilityType
322
461
  when LIBUSB::BT_WIRELESS_USB_DEVICE_CAPABILITY
323
462
  # no struct defined in libusb -> use generic DeviceCapability
463
+ cap
324
464
  when LIBUSB::BT_USB_2_0_EXTENSION
325
- res = Call.libusb_get_usb_2_0_extension_descriptor(@ctx, cap.pointer, pp_ext)
326
- cap = Usb20Extension.new(@ctx, pp_ext.read_pointer) if res==0
465
+ wrap_device_capability(cap, :libusb_get_usb_2_0_extension_descriptor, Usb20Extension)
327
466
  when LIBUSB::BT_SS_USB_DEVICE_CAPABILITY
328
- res = Call.libusb_get_ss_usb_device_capability_descriptor(@ctx, cap.pointer, pp_ext)
329
- cap = SsUsbDeviceCapability.new(@ctx, pp_ext.read_pointer) if res==0
467
+ wrap_device_capability(cap, :libusb_get_ss_usb_device_capability_descriptor, SsUsbDeviceCapability)
468
+ when LIBUSB::BT_SUPERSPEED_PLUS_CAPABILITY
469
+ wrap_device_capability(cap, :libusb_get_ssplus_usb_device_capability_descriptor, SsplusUsbDeviceCapability)
330
470
  when LIBUSB::BT_CONTAINER_ID
331
- res = Call.libusb_get_container_id_descriptor(@ctx, cap.pointer, pp_ext)
332
- cap = ContainerId.new(@ctx, pp_ext.read_pointer) if res==0
471
+ wrap_device_capability(cap, :libusb_get_container_id_descriptor, ContainerId)
333
472
  when LIBUSB::BT_PLATFORM_DESCRIPTOR
334
- res = Call.libusb_get_platform_descriptor(@ctx, cap.pointer, pp_ext)
335
- cap = PlatformDescriptor.new(@ctx, pp_ext.read_pointer) if res==0
473
+ wrap_device_capability(cap, :libusb_get_platform_descriptor, PlatformDescriptor)
336
474
  else
337
475
  # unknown capability -> use generic DeviceCapability
476
+ cap
338
477
  end
339
478
  ptr += FFI.type_size(:pointer)
340
479
  caps << cap
data/lib/libusb/call.rb CHANGED
@@ -224,6 +224,39 @@ module LIBUSB
224
224
  :BM_LTM_SUPPORT, 2,
225
225
  ]
226
226
 
227
+
228
+ # enum used in SsplusSublinkAttribute
229
+ SuperspeedplusSublinkAttributeSublinkType = enum :libusb_superspeedplus_sublink_attribute_sublink_type, [
230
+ :SSPLUS_ATTR_TYPE_SYM, 0,
231
+ :SSPLUS_ATTR_TYPE_ASYM, 1,
232
+ ]
233
+
234
+ # enum used in SsplusSublinkAttribute
235
+ SuperspeedplusSublinkAttributeSublinkDirection = enum :libusb_superspeedplus_sublink_attribute_sublink_direction, [
236
+ :SSPLUS_ATTR_DIR_RX, 0,
237
+ :SSPLUS_ATTR_DIR_TX, 1,
238
+ ]
239
+
240
+ # enum used in SsplusSublinkAttribute
241
+ #
242
+ # This field defines the base 10 exponent times 3, that shall be applied to the mantissa.
243
+ # * Bit = Bits per second
244
+ # * Kb = Kbps
245
+ # * Mb = Mbps
246
+ # * Gb = Gbps
247
+ SuperspeedplusSublinkAttributeExponent = enum :libusb_superspeedplus_sublink_attribute_exponent, [
248
+ :SSPLUS_ATTR_EXP_BPS, 0,
249
+ :SSPLUS_ATTR_EXP_KBS, 1,
250
+ :SSPLUS_ATTR_EXP_MBS, 2,
251
+ :SSPLUS_ATTR_EXP_GBS, 3,
252
+ ]
253
+
254
+ # enum used in SsplusSublinkAttribute
255
+ SuperspeedplusSublinkAttributeLinkProtocol = enum :libusb_superspeedplus_sublink_attribute_link_protocol, [
256
+ :SSPLUS_ATTR_PROT_SS, 0,
257
+ :SSPLUS_ATTR_PROT_SSPLUS, 1,
258
+ ]
259
+
227
260
  # USB capability types
228
261
  #
229
262
  # @see Bos::DeviceCapability
@@ -237,7 +270,7 @@ module LIBUSB
237
270
  :BT_BATTERY_INFO_CAPABILITY, 0x07, # Provides information on each battery supported by the device
238
271
  :BT_PD_CONSUMER_PORT_CAPABILITY, 0x08, # The consumer characteristics of a port on the device
239
272
  :BT_PD_PROVIDER_PORT_CAPABILITY, 0x09, # The provider characteristics of a port on the device
240
- :BT_SUPERSPEED_PLUS, 0x0A, # Defines the set of SuperSpeed Plus USB specific device level capabilities
273
+ :BT_SUPERSPEED_PLUS_CAPABILITY, 0x0A, # Defines the set of SuperSpeed Plus USB specific device level capabilities
241
274
  :BT_PRECISION_TIME_MEASUREMENT, 0x0B, # Precision Time Measurement (PTM) Capability Descriptor
242
275
  :BT_Wireless_USB_Ext, 0x0C, # Defines the set of Wireless USB 1.1-specific device level capabilities
243
276
  :BT_BILLBOARD, 0x0D, # Billboard capability
@@ -268,6 +301,14 @@ module LIBUSB
268
301
  :HOTPLUG_ENUMERATE, 1,
269
302
  ]
270
303
 
304
+ # The device string type.
305
+ DeviceStringType = enum :libusb_device_string_type, [
306
+ :DEVICE_STRING_MANUFACTURER,
307
+ :DEVICE_STRING_PRODUCT,
308
+ :DEVICE_STRING_SERIAL_NUMBER,
309
+ :DEVICE_STRING_COUNT # The total number of string types.
310
+ ]
311
+
271
312
  # Log message levels.
272
313
  #
273
314
  # - :LOG_LEVEL_NONE (0) : no messages ever printed by the library (default)
@@ -459,6 +500,8 @@ module LIBUSB
459
500
  try_attach_function 'libusb_free_usb_2_0_extension_descriptor', [:pointer], :void
460
501
  try_attach_function 'libusb_get_ss_usb_device_capability_descriptor', [:libusb_context, :pointer, :pointer], :int
461
502
  try_attach_function 'libusb_free_ss_usb_device_capability_descriptor', [:pointer], :void
503
+ try_attach_function 'libusb_get_ssplus_usb_device_capability_descriptor', [:libusb_context, :pointer, :pointer], :int
504
+ try_attach_function 'libusb_free_ssplus_usb_device_capability_descriptor', [:pointer], :void
462
505
  try_attach_function 'libusb_get_container_id_descriptor', [:libusb_context, :pointer, :pointer], :int
463
506
  try_attach_function 'libusb_free_container_id_descriptor', [:pointer], :void
464
507
  try_attach_function 'libusb_get_platform_descriptor', [:libusb_context, :pointer, :pointer], :int
@@ -521,6 +564,46 @@ module LIBUSB
521
564
  :pointer, :pointer], :int
522
565
  try_attach_function 'libusb_hotplug_deregister_callback', [:libusb_context, :libusb_hotplug_callback_handle], :void
523
566
 
567
+ # Retrieve a device string without needing to open the device.
568
+ #
569
+ # Since version v1.0.30 \ref LIBUSB_API_VERSION >= 0x0100010C
570
+ #
571
+ # \param dev the target device
572
+ # \param string_type the string type to retrieve
573
+ # \param data the data buffer for the UTF-8 encoded string.
574
+ # \param length the size of the data buffer in bytes.
575
+ # USB string descriptors cannot be longer than
576
+ # LIBUSB_DEVICE_STRING_BYTES_MAX.
577
+ # \returns a negative error code or
578
+ # the actual string length in bytes including the null terminator.
579
+ # \see libusb_get_string_descriptor()
580
+ # \see libusb_get_string_descriptor_ascii()
581
+ #
582
+ # This function works when the device is still closed since it relies
583
+ # on the operating system to provide the string. The operating system
584
+ # normally reads and caches the common string descriptors during
585
+ # USB enumeration.
586
+ #
587
+ # Since the USB string descriptor could be processed by the OS,
588
+ # this function returns a UTF-8 encoded string.
589
+ #
590
+ # The string will be returned untranslated or in the default OS language
591
+ # when supported by the OS and USB device.
592
+ #
593
+ # One way to call this function is using a buffer on the stack:
594
+ #
595
+ # char buffer[LIBUSB_DEVICE_STRING_BYTES_MAX];
596
+ # int ret = libusb_get_device_string(dev, LIBUSB_DEVICE_STRING_SERIAL_NUMBER, buffer, sizeof(buffer));
597
+ # if (ret < 0) {
598
+ # // handle error
599
+ # }
600
+ #
601
+ # This function is commonly used to get the serial number to allow
602
+ # for device selection before opening the selected device.
603
+ try_attach_function 'libusb_get_device_string', [:libusb_device, DeviceStringType, :pointer, :int], :int
604
+
605
+ try_attach_function 'libusb_get_session_data', [:libusb_device], :ulong
606
+
524
607
  class IsoPacketDescriptor < FFI::Struct
525
608
  layout :length, :uint,
526
609
  :actual_length, :uint,
@@ -554,6 +637,8 @@ module LIBUSB
554
637
  super
555
638
 
556
639
  ptr = pointer
640
+ # @private
641
+ # called by GC
557
642
  def ptr.free_struct(id)
558
643
  Call.libusb_free_transfer(self)
559
644
  return unless @ctx
@@ -36,6 +36,7 @@ module LIBUSB
36
36
  Call::LogLevels,
37
37
  Call::LogCbMode,
38
38
  Call::Options,
39
+ Call::DeviceStringType,
39
40
  ].each do |enum|
40
41
  enum.to_h.each{|k,v| const_set(k,v) }
41
42
  end
@@ -92,6 +93,16 @@ module LIBUSB
92
93
  # Wildcard matching for hotplug events.
93
94
  HOTPLUG_MATCH_ANY = -1
94
95
 
96
+ # The maximum length for a device string descriptor in UTF-8.
97
+ #
98
+ # 255 max descriptor length with 2 byte header
99
+ # => 253 bytes UTF-16LE, no null termination (USB 2.0 9.6.7)
100
+ # => 126.5 codepoints
101
+ # => 126 * 3 + 1
102
+ # => 382 bytes
103
+ #
104
+ # Stay with 256 * 2/3 = 384 to be safe.
105
+ DEVICE_STRING_BYTES_MAX = 384
95
106
 
96
107
  # http://www.usb.org/developers/defined_class
97
108
  # @private
@@ -523,14 +523,13 @@ module LIBUSB
523
523
  #
524
524
  # Since libusb version 1.0.16.
525
525
  #
526
- # @param [Hash] args
527
- # @option args [Fixnum,Symbol] :events bitwise or of events that will trigger this callback.
526
+ # @param [Fixnum,Symbol] events bitwise or of events that will trigger this callback.
528
527
  # Default is +LIBUSB::HOTPLUG_EVENT_DEVICE_ARRIVED|LIBUSB::HOTPLUG_EVENT_DEVICE_LEFT+ .
529
528
  # See {Call::HotplugEvents HotplugEvents}
530
- # @option args [Fixnum,Symbol] :flags hotplug callback flags. Default is 0. See {Call::HotplugFlags HotplugFlags}
531
- # @option args [Fixnum] :vendor_id the vendor id to match. Default is {HOTPLUG_MATCH_ANY}.
532
- # @option args [Fixnum] :product_id the product id to match. Default is {HOTPLUG_MATCH_ANY}.
533
- # @option args [Fixnum] :dev_class the device class to match. Default is {HOTPLUG_MATCH_ANY}.
529
+ # @param [Fixnum,Symbol] flags hotplug callback flags. Default is 0. See {Call::HotplugFlags HotplugFlags}
530
+ # @param [Fixnum] vendor_id the vendor id to match. Default is {HOTPLUG_MATCH_ANY}.
531
+ # @param [Fixnum] product_id the product id to match. Default is {HOTPLUG_MATCH_ANY}.
532
+ # @param [Fixnum] dev_class the device class to match. Default is {HOTPLUG_MATCH_ANY}.
534
533
  # @return [HotplugCallback] The handle to the registered callback.
535
534
  #
536
535
  # @yieldparam [Device] device the attached or removed {Device} is yielded to the block
@@ -17,16 +17,21 @@ module LIBUSB
17
17
  module ContextReference
18
18
  def register_context(ctx, free_sym)
19
19
  ptr = pointer
20
+
21
+ # @private
22
+ # called by GC or #free
20
23
  def ptr.free_struct(id)
21
24
  return unless @ctx
22
25
  Call.send(@free_sym, self)
23
26
  @ctx.unref_context
24
27
  end
28
+
25
29
  ptr.instance_variable_set(:@free_sym, free_sym)
26
30
  ptr.instance_variable_set(:@ctx, ctx.ref_context)
27
31
  ObjectSpace.define_finalizer(self, ptr.method(:free_struct))
28
32
  end
29
33
 
34
+ # Free the struct manually
30
35
  def free
31
36
  ptr = pointer
32
37
  ptr.free_struct nil
@@ -1,7 +1,7 @@
1
1
  module LIBUSB
2
- LIBUSB_VERSION = ENV['LIBUSB_VERSION'] || '1.0.27'
2
+ LIBUSB_VERSION = ENV['LIBUSB_VERSION'] || '1.0.30'
3
3
  LIBUSB_SOURCE_URI = "https://github.com/libusb/libusb/releases/download/v#{LIBUSB_VERSION}/libusb-#{LIBUSB_VERSION}.tar.bz2"
4
- LIBUSB_SOURCE_SHA256 = 'ffaa41d741a8a3bee244ac8e54a72ea05bf2879663c098c82fc5757853441575'
4
+ LIBUSB_SOURCE_SHA256 = 'fea36f34f9156400209595e300840767ab1a385ede1dc7ee893015aea9c6dbaf'
5
5
 
6
6
  MINI_PORTILE_VERSION = '~> 2.1'
7
7
  end
@@ -194,7 +194,7 @@ module LIBUSB
194
194
 
195
195
  if Call.respond_to?(:libusb_alloc_streams)
196
196
 
197
- # @method alloc_streams
197
+ # @method alloc_streams(num_streams, endpoints)
198
198
  #
199
199
  # Allocate up to num_streams usb bulk streams on the specified endpoints. This
200
200
  # function takes an array of endpoints rather then a single endpoint because
@@ -222,7 +222,7 @@ module LIBUSB
222
222
  res
223
223
  end
224
224
 
225
- # @method free_streams
225
+ # @method free_streams(endpoints)
226
226
  #
227
227
  # Free usb bulk streams allocated with {alloc_streams}
228
228
  #
@@ -286,7 +286,7 @@ module LIBUSB
286
286
  # @private
287
287
  if Call.respond_to?(:libusb_set_auto_detach_kernel_driver)
288
288
 
289
- # @method auto_detach_kernel_driver=
289
+ # @method auto_detach_kernel_driver=(enable)
290
290
  # Enable/disable libusb's automatic kernel driver detachment.
291
291
  #
292
292
  # When this is enabled libusb will automatically detach the kernel driver on an
@@ -358,11 +358,10 @@ module LIBUSB
358
358
  # transferred; do not assume that timeout conditions indicate a complete lack of
359
359
  # I/O.
360
360
  #
361
- # @param [Hash] args
362
- # @option args [Endpoint, Fixnum] :endpoint the (address of a) valid endpoint to communicate with
363
- # @option args [String] :dataOut the data to send with an outgoing transfer
364
- # @option args [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
365
- # @option args [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
361
+ # @param [Endpoint, Fixnum] endpoint the (address of a) valid endpoint to communicate with
362
+ # @param [String] dataOut the data to send with an outgoing transfer
363
+ # @param [Fixnum] dataIn the number of bytes expected to receive with an ingoing transfer
364
+ # @param [Fixnum] timeout timeout (in millseconds) that this function should wait before giving
366
365
  # up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
367
366
  #
368
367
  # @return [Fixnum] Number of bytes sent for an outgoing transfer
@@ -429,11 +428,10 @@ module LIBUSB
429
428
  #
430
429
  # The default endpoint bInterval value is used as the polling interval.
431
430
  #
432
- # @param [Hash] args
433
- # @option args [Endpoint, Fixnum] :endpoint the (address of a) valid endpoint to communicate with
434
- # @option args [String] :dataOut the data to send with an outgoing transfer
435
- # @option args [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
436
- # @option args [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
431
+ # @param [Endpoint, Fixnum] endpoint the (address of a) valid endpoint to communicate with
432
+ # @param [String] dataOut the data to send with an outgoing transfer
433
+ # @param [Fixnum] dataIn the number of bytes expected to receive with an ingoing transfer
434
+ # @param [Fixnum] timeout timeout (in millseconds) that this function should wait before giving
437
435
  # up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
438
436
  #
439
437
  # @return [Fixnum] Number of bytes sent for an outgoing transfer
@@ -483,16 +481,15 @@ module LIBUSB
483
481
  # The direction of the transfer is inferred from the +:bmRequestType+ field of the
484
482
  # setup packet.
485
483
  #
486
- # @param [Hash] args
487
- # @option args [Fixnum] :bmRequestType the request type field for the setup packet
488
- # @option args [Fixnum] :bRequest the request field for the setup packet
489
- # @option args [Fixnum] :wValue the value field for the setup packet
490
- # @option args [Fixnum] :wIndex the index field for the setup packet
491
- # @option args [String] :dataOut the data to send with an outgoing transfer, it
484
+ # @param [Fixnum] bmRequestType the request type field for the setup packet
485
+ # @param [Fixnum] bRequest the request field for the setup packet
486
+ # @param [Fixnum] wValue the value field for the setup packet
487
+ # @param [Fixnum] wIndex the index field for the setup packet
488
+ # @param [String] dataOut the data to send with an outgoing transfer, it
492
489
  # is appended to the setup packet
493
- # @option args [Fixnum] :dataIn the number of bytes expected to receive with an ingoing transfer
490
+ # @param [Fixnum] dataIn the number of bytes expected to receive with an ingoing transfer
494
491
  # (excluding setup packet)
495
- # @option args [Fixnum] :timeout timeout (in millseconds) that this function should wait before giving
492
+ # @param [Fixnum] timeout timeout (in millseconds) that this function should wait before giving
496
493
  # up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.
497
494
  #
498
495
  # @return [Fixnum] Number of bytes sent (excluding setup packet) for outgoing transfer
data/lib/libusb/device.rb CHANGED
@@ -319,6 +319,51 @@ module LIBUSB
319
319
  @pDevDesc[:bNumConfigurations]
320
320
  end
321
321
 
322
+ if Call.respond_to?(:libusb_get_device_string)
323
+ # Retrieve the device description strings without opening the device.
324
+ # So permissions for open aren't necessary.
325
+ #
326
+ # This function works when the device is still closed since it relies
327
+ # on the operating system to provide the string. The operating system
328
+ # normally reads and caches the common string descriptors during
329
+ # USB enumeration.
330
+ #
331
+ # Since the USB string descriptor could be processed by the OS,
332
+ # this function returns a UTF-8 encoded string.
333
+ #
334
+ # The string will be returned untranslated or in the default OS language
335
+ # when supported by the OS and USB device.
336
+ #
337
+ # @param [Symbol, Integer] string_type One of {Call::DeviceStringType}
338
+ #
339
+ # Available since libusb-1.0.30
340
+ #
341
+ def device_string(string_type)
342
+ buffer = FFI::MemoryPointer.new(DEVICE_STRING_BYTES_MAX)
343
+ res = Call.libusb_get_device_string(@pDev, string_type, buffer, buffer.size)
344
+ LIBUSB.raise_error res, "in libusb_get_device_string" if res<0
345
+ buffer.read_bytes(res)
346
+ end
347
+ end
348
+
349
+ if Call.respond_to?(:libusb_get_session_data)
350
+ # Returns the backend-specific identifier of the underlying system device tree
351
+ # node. Can be used to find the corresponding system device and directly query
352
+ # it (or access it otherwise) when and if necessary.
353
+ #
354
+ # Relevant backends:
355
+ # - Darwin: IOKit `sessionID`
356
+ # - Windows WinUSB: `DEVINST`
357
+ # - Linux, BSD: `busnum << 8 | devnum`
358
+ #
359
+ # Since version 1.0.30, \ref LIBUSB_API_VERSION >= 0x0100010C
360
+ #
361
+ # \param dev a device (must not be null)
362
+ # \returns the backend-specific device identifier
363
+ def get_session_data
364
+ Call.libusb_get_session_data(@pDev)
365
+ end
366
+ end
322
367
 
323
368
  def inspect
324
369
  attrs = []
@@ -339,9 +384,13 @@ module LIBUSB
339
384
  "\#<#{self.class} #{attrs.join(' ')}>"
340
385
  end
341
386
 
342
- def try_string_descriptor_ascii(i)
387
+ def try_string_descriptor_ascii(i, enum=nil)
343
388
  begin
344
- open{|h| h.string_descriptor_ascii(i) }
389
+ if enum && respond_to?(:device_string)
390
+ device_string(enum)
391
+ else
392
+ open{|h| h.string_descriptor_ascii(i) }
393
+ end
345
394
  rescue
346
395
  "?"
347
396
  end
@@ -351,7 +400,7 @@ module LIBUSB
351
400
  # @return String
352
401
  def manufacturer
353
402
  return @manufacturer if defined? @manufacturer
354
- @manufacturer = try_string_descriptor_ascii(self.iManufacturer)
403
+ @manufacturer = try_string_descriptor_ascii(self.iManufacturer, DEVICE_STRING_MANUFACTURER)
355
404
  @manufacturer = @manufacturer.strip if @manufacturer
356
405
  @manufacturer
357
406
  end
@@ -360,7 +409,7 @@ module LIBUSB
360
409
  # @return String
361
410
  def product
362
411
  return @product if defined? @product
363
- @product = try_string_descriptor_ascii(self.iProduct)
412
+ @product = try_string_descriptor_ascii(self.iProduct, DEVICE_STRING_PRODUCT)
364
413
  @product = @product.strip if @product
365
414
  @product
366
415
  end
@@ -369,7 +418,7 @@ module LIBUSB
369
418
  # @return String
370
419
  def serial_number
371
420
  return @serial_number if defined? @serial_number
372
- @serial_number = try_string_descriptor_ascii(self.iSerialNumber)
421
+ @serial_number = try_string_descriptor_ascii(self.iSerialNumber, DEVICE_STRING_SERIAL_NUMBER)
373
422
  @serial_number = @serial_number.strip if @serial_number
374
423
  @serial_number
375
424
  end
@@ -52,7 +52,12 @@ class Context
52
52
  # Libusb pollfd API is not available on this platform.
53
53
  # Use simple polling timer, instead:
54
54
  EventMachine.add_periodic_timer(0.01) do
55
- @eventmachine_timer = self.handle_events 0
55
+ begin
56
+ @eventmachine_timer = self.handle_events 0
57
+ rescue ERROR_INTERRUPTED
58
+ # Interrupted by a signal; nothing was handled. The next tick of this
59
+ # periodic timer retries. See the pollfd callback below.
60
+ end
56
61
  end
57
62
  end
58
63
  end
@@ -98,7 +103,18 @@ class Context
98
103
  @eventmachine_timer = nil
99
104
  end
100
105
 
101
- self.handle_events 0
106
+ begin
107
+ self.handle_events 0
108
+ rescue ERROR_INTERRUPTED
109
+ # poll() was interrupted by a signal, so no events were handled. This
110
+ # is transient and retryable: the descriptor stays watched and the next
111
+ # readable/writable event re-enters this callback. Without this rescue
112
+ # the exception unwinds out of EventMachine.run and takes the whole
113
+ # reactor (and typically the process) down. Same handling as the
114
+ # synchronous transfer path in transfer.rb.
115
+ next
116
+ end
117
+
102
118
  timeout = self.next_timeout
103
119
  # puts "libusb new timeout: #{timeout.inspect}"
104
120
  if timeout
@@ -14,6 +14,7 @@
14
14
  # along with Libusb for Ruby. If not, see <http://www.gnu.org/licenses/>.
15
15
 
16
16
  require "bundler/gem_helper"
17
+ require 'ostruct'
17
18
 
18
19
  module LIBUSB
19
20
  class GemHelper < Bundler::GemHelper
@@ -311,9 +311,9 @@ module LIBUSB
311
311
  # Set a transfers bulk stream id.
312
312
  #
313
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
314
+ def stream_id=(stream_id)
315
+ Call.libusb_transfer_set_stream_id(@transfer, stream_id)
316
+ stream_id
317
317
  end
318
318
 
319
319
  # Get a transfers bulk stream id.
@@ -15,5 +15,5 @@
15
15
 
16
16
  module LIBUSB
17
17
  # Library version of libusb for Ruby
18
- VERSION = "0.7.2"
18
+ VERSION = "0.8.0"
19
19
  end
@@ -62,7 +62,7 @@ class TestLibusbBos < Minitest::Test
62
62
  :BT_BATTERY_INFO_CAPABILITY,
63
63
  :BT_PD_CONSUMER_PORT_CAPABILITY,
64
64
  :BT_PD_PROVIDER_PORT_CAPABILITY,
65
- :BT_SUPERSPEED_PLUS,
65
+ :BT_SUPERSPEED_PLUS_CAPABILITY,
66
66
  :BT_PRECISION_TIME_MEASUREMENT,
67
67
  :BT_Wireless_USB_Ext,
68
68
  :BT_BILLBOARD,
@@ -77,11 +77,14 @@ class TestLibusbBos < Minitest::Test
77
77
  caps.each do |cap|
78
78
  did_cap = true
79
79
 
80
- assert_operator 4, :<=, cap.bLength
81
- assert_equal LIBUSB::DT_DEVICE_CAPABILITY, cap.bDescriptorType
82
- assert_kind_of String, cap.dev_capability_data, "should provide binary capability data"
80
+ unless cap.is_a?(Bos::SsplusUsbDeviceCapability)
81
+ assert_operator 4, :<=, cap.bLength
82
+ assert_equal LIBUSB::DT_DEVICE_CAPABILITY, cap.bDescriptorType
83
+ assert_kind_of String, cap.dev_capability_data, "should provide binary capability data"
84
+ assert_operator 1, :<=, cap.dev_capability_data.length, "dev_capability_data should be at least one byte"
85
+ end
83
86
  assert_kind_of String, cap.inspect, "should respond to inspect"
84
- assert_operator 1, :<=, cap.dev_capability_data.length, "dev_capability_data should be at least one byte"
87
+ assert_match(/\#<#{cap.class}/, cap.inspect, "inspect contains the class name")
85
88
 
86
89
  case cap
87
90
  when Bos::DeviceCapability
@@ -102,6 +105,25 @@ class TestLibusbBos < Minitest::Test
102
105
  assert_operator 0, :<=, cap.bU1DevExitLat
103
106
  assert_operator 0, :<=, cap.bU2DevExitLat
104
107
 
108
+ when Bos::SsplusUsbDeviceCapability
109
+ assert_kind_of Array, cap.sublinkSpeedAttributes
110
+ assert_equal cap.numSublinkSpeedAttributes, cap.sublinkSpeedAttributes.size
111
+ assert_operator 1, :<=, cap.numSublinkSpeedIDs
112
+ assert_operator 1, :<=, cap.numSublinkSpeedAttributes
113
+ assert_kind_of Integer, cap.ssid
114
+ assert_operator 0, :<=, cap.minRxLaneCount
115
+ assert_operator 0, :<=, cap.minTxLaneCount
116
+
117
+ sl = cap.sublinkSpeedAttributes[0]
118
+ assert_match(/id=.* SuperSpeed/, sl.to_s_human_readable)
119
+ assert_kind_of String, sl.inspect, "should respond to inspect"
120
+ assert_kind_of Integer, sl.ssid
121
+ assert_kind_of Symbol, sl.exponent
122
+ assert_kind_of Symbol, sl.type
123
+ assert_kind_of Symbol, sl.direction
124
+ assert_kind_of Symbol, sl.protocol
125
+ assert_operator 1, :<=, sl.mantissa
126
+
105
127
  when Bos::ContainerId
106
128
  assert_equal 4, cap.bDevCapabilityType
107
129
  assert_operator 0, :<=, cap.bReserved
@@ -45,6 +45,10 @@ class TestLibusbDescriptors < Minitest::Test
45
45
  assert_kind_of Integer, dev.iSerialNumber
46
46
  assert_kind_of Integer, dev.bNumConfigurations
47
47
 
48
+ assert_kind_of String, dev.manufacturer
49
+ assert_kind_of String, dev.product
50
+ assert_kind_of String, dev.serial_number
51
+
48
52
  dev.configurations.each do |config_desc|
49
53
  assert_match(/Configuration/, config_desc.inspect, "ConfigDescriptor#inspect should work")
50
54
  assert dev.configurations.include?(config_desc), "Device#configurations should include this one"
@@ -242,4 +246,13 @@ class TestLibusbDescriptors < Minitest::Test
242
246
  end
243
247
  end
244
248
  end
249
+
250
+ def test_get_session_data
251
+ skip "libusb version older than 1.0.30" if Gem::Version.new(LIBUSB.version) < Gem::Version.new("1.0.30")
252
+
253
+ usb.devices.each do |dev|
254
+ assert_kind_of Integer, dev.get_session_data
255
+ refute_equal 0, dev.get_session_data
256
+ end
257
+ end
245
258
  end
@@ -0,0 +1,81 @@
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 "minitest/autorun"
17
+ require "libusb"
18
+ require "libusb/eventmachine"
19
+ require "eventmachine"
20
+
21
+ # LIBUSB_ERROR_INTERRUPTED means poll() was interrupted by a signal and no
22
+ # events were handled. It is transient and retryable, but Context#handle_events
23
+ # raises on it like any other negative return, and the EventMachine integration
24
+ # used to call handle_events unrescued -- so a signal arriving at the wrong
25
+ # moment unwound out of EventMachine.run and killed the reactor.
26
+ #
27
+ # These tests need no USB device: they drive the registered callback directly.
28
+ class TestLibusbEventMachineEintr < Minitest::Test
29
+ include LIBUSB
30
+
31
+ def setup
32
+ @context = Context.new
33
+ skip "libusb pollfd API not available on this platform" unless @context.pollfds&.any?
34
+ end
35
+
36
+ # The callback the pollfd handler invokes must swallow ERROR_INTERRUPTED and
37
+ # leave the reactor running.
38
+ def test_pollfd_callback_survives_eintr
39
+ interrupts = 0
40
+ @context.define_singleton_method(:handle_events) do |*|
41
+ interrupts += 1
42
+ raise LIBUSB::ERROR_INTERRUPTED
43
+ end
44
+
45
+ assert dispatch_pollfd_events, "reactor must stay up after ERROR_INTERRUPTED"
46
+ assert_operator interrupts, :>=, 1, "handle_events should have been called"
47
+ end
48
+
49
+ # Only ERROR_INTERRUPTED is transient. Every other libusb error must still
50
+ # propagate, so a real fault is not silently swallowed.
51
+ def test_pollfd_callback_still_raises_other_errors
52
+ @context.define_singleton_method(:handle_events) do |*|
53
+ raise LIBUSB::ERROR_NO_DEVICE
54
+ end
55
+
56
+ assert_raises(LIBUSB::ERROR_NO_DEVICE) { dispatch_pollfd_events }
57
+ end
58
+
59
+ private
60
+
61
+ # Run the reactor, register libusb's pollfds with it and invoke the resulting
62
+ # callbacks exactly as EMPollfdHandler#notify_readable does. Returns whether
63
+ # the reactor was still running once they had been dispatched.
64
+ def dispatch_pollfd_events
65
+ reactor_alive = false
66
+
67
+ EventMachine.run do
68
+ @context.eventmachine_register
69
+
70
+ begin
71
+ @context.instance_variable_get(:@eventmachine_attached_fds).each_value(&:need_handle_events)
72
+ reactor_alive = EventMachine.reactor_running?
73
+ ensure
74
+ @context.eventmachine_unregister
75
+ EventMachine.stop
76
+ end
77
+ end
78
+
79
+ reactor_alive
80
+ end
81
+ end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: libusb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.2
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lars Kanis
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-10-23 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: ffi
@@ -81,7 +80,7 @@ files:
81
80
  - lib/libusb/version_gem.rb
82
81
  - lib/libusb/version_struct.rb
83
82
  - libusb.gemspec
84
- - ports/archives/libusb-1.0.27.tar.bz2
83
+ - ports/archives/libusb-1.0.30.tar.bz2
85
84
  - test/test_libusb.rb
86
85
  - test/test_libusb_bos.rb
87
86
  - test/test_libusb_bulk_stream_transfer.rb
@@ -90,6 +89,7 @@ files:
90
89
  - test/test_libusb_context.rb
91
90
  - test/test_libusb_descriptors.rb
92
91
  - test/test_libusb_event_machine.rb
92
+ - test/test_libusb_event_machine_eintr.rb
93
93
  - test/test_libusb_gc.rb
94
94
  - test/test_libusb_hotplug.rb
95
95
  - test/test_libusb_iso_transfer.rb
@@ -103,7 +103,6 @@ licenses:
103
103
  - LGPL-3.0
104
104
  metadata:
105
105
  yard.run: yri
106
- post_install_message:
107
106
  rdoc_options:
108
107
  - "--main"
109
108
  - README.md
@@ -121,8 +120,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
121
120
  - !ruby/object:Gem::Version
122
121
  version: '0'
123
122
  requirements: []
124
- rubygems_version: 3.3.26
125
- signing_key:
123
+ rubygems_version: 4.1.0.dev
126
124
  specification_version: 4
127
125
  summary: Access USB devices from Ruby via libusb-1.0
128
126
  test_files: []
Binary file