usb-ruby 0.1.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.
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module USB
4
+ class SSDeviceCapability
5
+ def self.finalizer(ptr)
6
+ proc do
7
+ FFIBindings.libusb_free_ss_usb_device_capability_descriptor(ptr) unless ptr.nil? || ptr.null?
8
+ rescue StandardError
9
+ end
10
+ end
11
+
12
+ def initialize(ptr)
13
+ raise ArgumentError, "SS device capability pointer is required" if ptr.nil? || ptr.null?
14
+
15
+ @ptr = ptr
16
+ @struct = FFIBindings::SSDeviceCapabilityStruct.new(@ptr)
17
+ ObjectSpace.define_finalizer(self, self.class.finalizer(@ptr))
18
+ end
19
+
20
+ def attributes
21
+ @struct[:bmAttributes]
22
+ end
23
+
24
+ def speed_supported
25
+ @struct[:wSpeedSupported]
26
+ end
27
+
28
+ def functionality_support
29
+ @struct[:bFunctionalitySupport]
30
+ end
31
+
32
+ def u1_dev_exit_lat
33
+ @struct[:bU1DevExitLat]
34
+ end
35
+
36
+ def u2_dev_exit_lat
37
+ @struct[:bU2DevExitLat]
38
+ end
39
+
40
+ def close
41
+ return if @ptr.nil? || @ptr.null?
42
+
43
+ ObjectSpace.undefine_finalizer(self)
44
+ FFIBindings.libusb_free_ss_usb_device_capability_descriptor(@ptr)
45
+ @ptr = FFI::Pointer::NULL
46
+ @struct = nil
47
+ end
48
+
49
+ alias free close
50
+
51
+ def to_ptr
52
+ @ptr
53
+ end
54
+
55
+ def inspect
56
+ "#<USB::SSDeviceCapability speed_supported=0x#{speed_supported.to_s(16)}>"
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module USB
4
+ class SSEndpointCompanion
5
+ def self.finalizer(ptr)
6
+ proc do
7
+ FFIBindings.libusb_free_ss_endpoint_companion_descriptor(ptr) unless ptr.nil? || ptr.null?
8
+ rescue StandardError
9
+ end
10
+ end
11
+
12
+ def initialize(ptr)
13
+ raise ArgumentError, "SS endpoint companion pointer is required" if ptr.nil? || ptr.null?
14
+
15
+ @ptr = ptr
16
+ @struct = FFIBindings::SSEndpointCompanionStruct.new(@ptr)
17
+ ObjectSpace.define_finalizer(self, self.class.finalizer(@ptr))
18
+ end
19
+
20
+ def max_burst
21
+ @struct[:bMaxBurst]
22
+ end
23
+
24
+ def attributes
25
+ @struct[:bmAttributes]
26
+ end
27
+
28
+ def bytes_per_interval
29
+ @struct[:wBytesPerInterval]
30
+ end
31
+
32
+ def close
33
+ return if @ptr.nil? || @ptr.null?
34
+
35
+ ObjectSpace.undefine_finalizer(self)
36
+ FFIBindings.libusb_free_ss_endpoint_companion_descriptor(@ptr)
37
+ @ptr = FFI::Pointer::NULL
38
+ @struct = nil
39
+ end
40
+
41
+ alias free close
42
+
43
+ def to_ptr
44
+ @ptr
45
+ end
46
+
47
+ def inspect
48
+ "#<USB::SSEndpointCompanion max_burst=#{max_burst} bytes_per_interval=#{bytes_per_interval}>"
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,201 @@
1
+ # frozen_string_literal: true
2
+
3
+ module USB
4
+ class Transfer
5
+ CALLBACK = FFI::Function.new(:void, [:pointer]) do |transfer_ptr|
6
+ transfer = from_pointer(transfer_ptr)
7
+ transfer&.send(:invoke_callback)
8
+ end
9
+
10
+ class << self
11
+ def registry
12
+ @registry ||= {}
13
+ end
14
+
15
+ def from_pointer(ptr)
16
+ registry[ptr.address]
17
+ end
18
+
19
+ def control(handle:, bm_request_type:, b_request:, w_value:, w_index:, data_or_length:, timeout: 1000)
20
+ data =
21
+ if data_or_length.is_a?(Integer)
22
+ "\x00".b * data_or_length
23
+ else
24
+ data_or_length.to_s.b
25
+ end
26
+
27
+ setup = [bm_request_type, b_request, w_value, w_index, data.bytesize].pack("CCvvv")
28
+ transfer = new
29
+ transfer.dev_handle = handle
30
+ transfer.type = TRANSFER_TYPE_CONTROL
31
+ transfer.timeout = timeout
32
+ transfer.buffer = setup + data
33
+ transfer
34
+ end
35
+
36
+ def bulk(handle:, endpoint:, data:, timeout: 1000)
37
+ transfer = new
38
+ transfer.dev_handle = handle
39
+ transfer.endpoint = endpoint
40
+ transfer.type = TRANSFER_TYPE_BULK
41
+ transfer.timeout = timeout
42
+ transfer.buffer = data
43
+ transfer
44
+ end
45
+
46
+ def interrupt(handle:, endpoint:, data:, timeout: 1000)
47
+ transfer = new
48
+ transfer.dev_handle = handle
49
+ transfer.endpoint = endpoint
50
+ transfer.type = TRANSFER_TYPE_INTERRUPT
51
+ transfer.timeout = timeout
52
+ transfer.buffer = data
53
+ transfer
54
+ end
55
+
56
+ def isochronous(handle:, endpoint:, data:, num_iso_packets:, timeout: 1000)
57
+ transfer = new(num_iso_packets: num_iso_packets)
58
+ transfer.dev_handle = handle
59
+ transfer.endpoint = endpoint
60
+ transfer.type = TRANSFER_TYPE_ISOCHRONOUS
61
+ transfer.timeout = timeout
62
+ transfer.buffer = data
63
+ transfer
64
+ end
65
+
66
+ def finalizer(ptr)
67
+ proc do
68
+ FFIBindings.libusb_free_transfer(ptr) unless ptr.nil? || ptr.null?
69
+ rescue StandardError
70
+ end
71
+ end
72
+ end
73
+
74
+ def initialize(num_iso_packets: 0)
75
+ FFIBindings.ensure_loaded!
76
+ @ptr = FFIBindings.libusb_alloc_transfer(num_iso_packets)
77
+ raise Error, "libusb_alloc_transfer returned null" if @ptr.null?
78
+
79
+ @struct = FFIBindings::TransferStruct.new(@ptr)
80
+ @struct[:callback] = CALLBACK
81
+ @struct[:user_data] = FFI::Pointer::NULL
82
+ @callback = nil
83
+ @buffer = nil
84
+ ObjectSpace.define_finalizer(self, self.class.finalizer(@ptr))
85
+ end
86
+
87
+ def free
88
+ return if @ptr.nil? || @ptr.null?
89
+
90
+ ObjectSpace.undefine_finalizer(self)
91
+ self.class.registry.delete(@ptr.address)
92
+ FFIBindings.libusb_free_transfer(@ptr)
93
+ @ptr = FFI::Pointer::NULL
94
+ @struct = nil
95
+ @buffer = nil
96
+ end
97
+
98
+ def dev_handle=(handle)
99
+ @struct[:dev_handle] = handle.is_a?(DeviceHandle) ? handle.to_ptr : handle
100
+ end
101
+
102
+ def endpoint=(endpoint)
103
+ @struct[:endpoint] = endpoint
104
+ end
105
+
106
+ def type=(transfer_type)
107
+ @struct[:type] = transfer_type
108
+ end
109
+
110
+ def timeout=(milliseconds)
111
+ @struct[:timeout] = milliseconds
112
+ end
113
+
114
+ def buffer=(data)
115
+ bytes = data.to_s.b
116
+ @buffer = FFI::MemoryPointer.new(:uint8, [bytes.bytesize, 1].max)
117
+ @buffer.put_bytes(0, bytes) unless bytes.empty?
118
+ @struct[:buffer] = @buffer
119
+ @struct[:length] = bytes.bytesize
120
+ end
121
+
122
+ def flags
123
+ @struct[:flags]
124
+ end
125
+
126
+ def flags=(value)
127
+ @struct[:flags] = value
128
+ end
129
+
130
+ def status
131
+ @struct[:status]
132
+ end
133
+
134
+ def actual_length
135
+ @struct[:actual_length]
136
+ end
137
+
138
+ def submit
139
+ free_transfer = (flags & TRANSFER_FREE_TRANSFER) != 0
140
+ self.class.registry[@ptr.address] = self
141
+ Error.raise_on_error(FFIBindings.libusb_submit_transfer(@ptr))
142
+ ObjectSpace.undefine_finalizer(self) if free_transfer
143
+ self
144
+ rescue StandardError
145
+ self.class.registry.delete(@ptr.address)
146
+ raise
147
+ end
148
+
149
+ def cancel
150
+ Error.raise_on_error(FFIBindings.libusb_cancel_transfer(@ptr))
151
+ self
152
+ end
153
+
154
+ def on_complete(&block)
155
+ @callback = block
156
+ self
157
+ end
158
+
159
+ def callback
160
+ @callback
161
+ end
162
+
163
+ def iso_packet(index)
164
+ raise IndexError, "iso packet index out of bounds" if index.negative? || index >= num_iso_packets
165
+
166
+ IsoPacket.new(self, FFIBindings::IsoPacketDescriptorStruct.new(iso_packet_pointer(index)))
167
+ end
168
+
169
+ def set_iso_packet_lengths(length)
170
+ num_iso_packets.times do |index|
171
+ iso_packet(index).length = length
172
+ end
173
+ self
174
+ end
175
+
176
+ def num_iso_packets
177
+ @struct[:num_iso_packets]
178
+ end
179
+
180
+ def to_ptr
181
+ @ptr
182
+ end
183
+
184
+ private
185
+
186
+ def invoke_callback
187
+ free_transfer = !@struct.nil? && (flags & TRANSFER_FREE_TRANSFER) != 0
188
+ self.class.registry.delete(@ptr.address)
189
+ @callback&.call(self)
190
+ return unless free_transfer
191
+
192
+ @ptr = FFI::Pointer::NULL
193
+ @struct = nil
194
+ @buffer = nil
195
+ end
196
+
197
+ def iso_packet_pointer(index)
198
+ @ptr + FFIBindings::TransferStruct.size + (index * FFIBindings::IsoPacketDescriptorStruct.size)
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module USB
4
+ class USB20Extension
5
+ def self.finalizer(ptr)
6
+ proc do
7
+ FFIBindings.libusb_free_usb_2_0_extension_descriptor(ptr) unless ptr.nil? || ptr.null?
8
+ rescue StandardError
9
+ end
10
+ end
11
+
12
+ def initialize(ptr)
13
+ raise ArgumentError, "USB 2.0 extension pointer is required" if ptr.nil? || ptr.null?
14
+
15
+ @ptr = ptr
16
+ @struct = FFIBindings::USB20ExtensionStruct.new(@ptr)
17
+ ObjectSpace.define_finalizer(self, self.class.finalizer(@ptr))
18
+ end
19
+
20
+ def attributes
21
+ @struct[:bmAttributes]
22
+ end
23
+
24
+ def supports_lpm?
25
+ (attributes & 0x02) != 0
26
+ end
27
+
28
+ def close
29
+ return if @ptr.nil? || @ptr.null?
30
+
31
+ ObjectSpace.undefine_finalizer(self)
32
+ FFIBindings.libusb_free_usb_2_0_extension_descriptor(@ptr)
33
+ @ptr = FFI::Pointer::NULL
34
+ @struct = nil
35
+ end
36
+
37
+ alias free close
38
+
39
+ def to_ptr
40
+ @ptr
41
+ end
42
+
43
+ def inspect
44
+ "#<USB::USB20Extension attributes=0x#{attributes.to_s(16)}>"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module USB
4
+ VERSION = "0.1.0"
5
+ end
data/lib/usb.rb ADDED
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ffi"
4
+
5
+ require_relative "usb/version"
6
+ require_relative "usb/constants"
7
+ require_relative "usb/error"
8
+ require_relative "usb/ffi_bindings"
9
+ require_relative "usb/context"
10
+ require_relative "usb/device"
11
+ require_relative "usb/device_handle"
12
+ require_relative "usb/device_descriptor"
13
+ require_relative "usb/config_descriptor"
14
+ require_relative "usb/interface"
15
+ require_relative "usb/interface_descriptor"
16
+ require_relative "usb/endpoint_descriptor"
17
+ require_relative "usb/ss_endpoint_companion"
18
+ require_relative "usb/bos_descriptor"
19
+ require_relative "usb/bos_dev_capability"
20
+ require_relative "usb/usb20_extension"
21
+ require_relative "usb/ss_device_capability"
22
+ require_relative "usb/container_id"
23
+ require_relative "usb/transfer"
24
+ require_relative "usb/iso_packet"
25
+ require_relative "usb/hotplug"
26
+ require_relative "usb/event_handling"
27
+ require_relative "usb/pollfds"
28
+
29
+ module USB
30
+ class << self
31
+ def version
32
+ FFIBindings.ensure_loaded!
33
+ ptr = FFIBindings.libusb_get_version
34
+ return nil if ptr.null?
35
+
36
+ version = FFIBindings::VersionStruct.new(ptr)
37
+ "#{version[:major]}.#{version[:minor]}.#{version[:micro]}"
38
+ end
39
+
40
+ def has_capability?(capability)
41
+ FFIBindings.ensure_loaded!
42
+ FFIBindings.libusb_has_capability(capability) != 0
43
+ end
44
+
45
+ def devices(**filters)
46
+ Context.open { |context| context.devices(**filters) }
47
+ end
48
+ end
49
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: usb-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Yudai Takada
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: ffi
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.0'
26
+ description: usb-ruby provides idiomatic Ruby access to libusb 1.0 via FFI without
27
+ native extensions.
28
+ email:
29
+ - t.yudai92@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - LICENSE.txt
35
+ - README.md
36
+ - Rakefile
37
+ - lib/usb.rb
38
+ - lib/usb/bos_descriptor.rb
39
+ - lib/usb/bos_dev_capability.rb
40
+ - lib/usb/config_descriptor.rb
41
+ - lib/usb/constants.rb
42
+ - lib/usb/container_id.rb
43
+ - lib/usb/context.rb
44
+ - lib/usb/device.rb
45
+ - lib/usb/device_descriptor.rb
46
+ - lib/usb/device_handle.rb
47
+ - lib/usb/endpoint_descriptor.rb
48
+ - lib/usb/error.rb
49
+ - lib/usb/event_handling.rb
50
+ - lib/usb/ffi_bindings.rb
51
+ - lib/usb/hotplug.rb
52
+ - lib/usb/interface.rb
53
+ - lib/usb/interface_descriptor.rb
54
+ - lib/usb/iso_packet.rb
55
+ - lib/usb/pollfds.rb
56
+ - lib/usb/ss_device_capability.rb
57
+ - lib/usb/ss_endpoint_companion.rb
58
+ - lib/usb/transfer.rb
59
+ - lib/usb/usb20_extension.rb
60
+ - lib/usb/version.rb
61
+ homepage: https://github.com/ydah/usb-ruby
62
+ licenses:
63
+ - MIT
64
+ metadata:
65
+ source_code_uri: https://github.com/ydah/usb-ruby
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '3.0'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubygems_version: 4.0.6
81
+ specification_version: 4
82
+ summary: Ruby FFI bindings for libusb 1.0
83
+ test_files: []