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.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +52 -0
- data/Rakefile +8 -0
- data/lib/usb/bos_descriptor.rb +54 -0
- data/lib/usb/bos_dev_capability.rb +48 -0
- data/lib/usb/config_descriptor.rb +94 -0
- data/lib/usb/constants.rb +129 -0
- data/lib/usb/container_id.rb +43 -0
- data/lib/usb/context.rb +148 -0
- data/lib/usb/device.rb +134 -0
- data/lib/usb/device_descriptor.rb +61 -0
- data/lib/usb/device_handle.rb +246 -0
- data/lib/usb/endpoint_descriptor.rb +91 -0
- data/lib/usb/error.rb +94 -0
- data/lib/usb/event_handling.rb +149 -0
- data/lib/usb/ffi_bindings.rb +319 -0
- data/lib/usb/hotplug.rb +47 -0
- data/lib/usb/interface.rb +31 -0
- data/lib/usb/interface_descriptor.rb +65 -0
- data/lib/usb/iso_packet.rb +26 -0
- data/lib/usb/pollfds.rb +33 -0
- data/lib/usb/ss_device_capability.rb +59 -0
- data/lib/usb/ss_endpoint_companion.rb +51 -0
- data/lib/usb/transfer.rb +201 -0
- data/lib/usb/usb20_extension.rb +47 -0
- data/lib/usb/version.rb +5 -0
- data/lib/usb.rb +49 -0
- metadata +83 -0
data/lib/usb/device.rb
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module USB
|
|
4
|
+
class Device
|
|
5
|
+
include Comparable
|
|
6
|
+
|
|
7
|
+
attr_reader :context
|
|
8
|
+
|
|
9
|
+
def self.finalizer(ptr)
|
|
10
|
+
proc do
|
|
11
|
+
FFIBindings.libusb_unref_device(ptr) unless ptr.nil? || ptr.null?
|
|
12
|
+
rescue StandardError
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def initialize(context, ptr, ref: true)
|
|
17
|
+
raise ArgumentError, "device pointer is required" if ptr.nil? || ptr.null?
|
|
18
|
+
|
|
19
|
+
@context = context
|
|
20
|
+
@ptr = ptr
|
|
21
|
+
@owns_ref = ref
|
|
22
|
+
|
|
23
|
+
FFIBindings.libusb_ref_device(@ptr) if @owns_ref
|
|
24
|
+
ObjectSpace.define_finalizer(self, self.class.finalizer(@ptr)) if @owns_ref
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def bus_number
|
|
28
|
+
FFIBindings.libusb_get_bus_number(@ptr)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def device_address
|
|
32
|
+
FFIBindings.libusb_get_device_address(@ptr)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def port_number
|
|
36
|
+
FFIBindings.libusb_get_port_number(@ptr)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def port_numbers
|
|
40
|
+
buffer = FFI::MemoryPointer.new(:uint8, 16)
|
|
41
|
+
count = Error.raise_on_error(FFIBindings.libusb_get_port_numbers(@ptr, buffer, 16))
|
|
42
|
+
buffer.read_array_of_uint8(count)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def speed
|
|
46
|
+
Error.raise_on_error(FFIBindings.libusb_get_device_speed(@ptr))
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def max_packet_size(endpoint)
|
|
50
|
+
Error.raise_on_error(FFIBindings.libusb_get_max_packet_size(@ptr, endpoint))
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def max_iso_packet_size(endpoint)
|
|
54
|
+
Error.raise_on_error(FFIBindings.libusb_get_max_iso_packet_size(@ptr, endpoint))
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def parent
|
|
58
|
+
parent_ptr = FFIBindings.libusb_get_parent(@ptr)
|
|
59
|
+
return nil if parent_ptr.null?
|
|
60
|
+
|
|
61
|
+
Device.new(@context, parent_ptr)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def device_descriptor
|
|
65
|
+
descriptor_ptr = FFI::MemoryPointer.new(FFIBindings::DeviceDescriptorStruct)
|
|
66
|
+
Error.raise_on_error(FFIBindings.libusb_get_device_descriptor(@ptr, descriptor_ptr))
|
|
67
|
+
DeviceDescriptor.new(FFIBindings::DeviceDescriptorStruct.new(descriptor_ptr))
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def vendor_id
|
|
71
|
+
device_descriptor.vendor_id
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def product_id
|
|
75
|
+
device_descriptor.product_id
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def device_class
|
|
79
|
+
device_descriptor.device_class
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def config_descriptors
|
|
83
|
+
Array.new(device_descriptor.num_configurations) { |index| config_descriptor(index) }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def active_config_descriptor
|
|
87
|
+
fetch_config_descriptor(:libusb_get_active_config_descriptor)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def config_descriptor(index)
|
|
91
|
+
fetch_config_descriptor(:libusb_get_config_descriptor, index)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def config_descriptor_by_value(value)
|
|
95
|
+
fetch_config_descriptor(:libusb_get_config_descriptor_by_value, value)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def open
|
|
99
|
+
handle_ptr = FFI::MemoryPointer.new(:pointer)
|
|
100
|
+
Error.raise_on_error(FFIBindings.libusb_open(@ptr, handle_ptr))
|
|
101
|
+
handle = DeviceHandle.new(handle_ptr.read_pointer)
|
|
102
|
+
return handle unless block_given?
|
|
103
|
+
|
|
104
|
+
begin
|
|
105
|
+
yield handle
|
|
106
|
+
ensure
|
|
107
|
+
handle.close
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def <=>(other)
|
|
112
|
+
[bus_number, device_address] <=> [other.bus_number, other.device_address]
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def inspect
|
|
116
|
+
ids = format("%04x:%04x", vendor_id, product_id)
|
|
117
|
+
format("#<USB::Device %03d/%03d %s>", bus_number, device_address, ids)
|
|
118
|
+
rescue StandardError
|
|
119
|
+
format("#<USB::Device %03d/%03d>", bus_number, device_address)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def to_ptr
|
|
123
|
+
@ptr
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
private
|
|
127
|
+
|
|
128
|
+
def fetch_config_descriptor(function_name, *args)
|
|
129
|
+
descriptor_ptr = FFI::MemoryPointer.new(:pointer)
|
|
130
|
+
Error.raise_on_error(FFIBindings.public_send(function_name, @ptr, *args, descriptor_ptr))
|
|
131
|
+
ConfigDescriptor.new(descriptor_ptr.read_pointer)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module USB
|
|
4
|
+
class DeviceDescriptor
|
|
5
|
+
def initialize(struct)
|
|
6
|
+
@struct = struct
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def bcd_usb
|
|
10
|
+
@struct[:bcdUSB]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def device_class
|
|
14
|
+
@struct[:bDeviceClass]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def device_sub_class
|
|
18
|
+
@struct[:bDeviceSubClass]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def device_protocol
|
|
22
|
+
@struct[:bDeviceProtocol]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def max_packet_size_0
|
|
26
|
+
@struct[:bMaxPacketSize0]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def vendor_id
|
|
30
|
+
@struct[:idVendor]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def product_id
|
|
34
|
+
@struct[:idProduct]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def bcd_device
|
|
38
|
+
@struct[:bcdDevice]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def manufacturer_index
|
|
42
|
+
@struct[:iManufacturer]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def product_index
|
|
46
|
+
@struct[:iProduct]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def serial_number_index
|
|
50
|
+
@struct[:iSerialNumber]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def num_configurations
|
|
54
|
+
@struct[:bNumConfigurations]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def inspect
|
|
58
|
+
format("#<USB::DeviceDescriptor %04x:%04x class=0x%02x>", vendor_id, product_id, device_class)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module USB
|
|
4
|
+
class DeviceHandle
|
|
5
|
+
def self.finalizer(ptr)
|
|
6
|
+
proc do
|
|
7
|
+
FFIBindings.libusb_close(ptr) unless ptr.nil? || ptr.null?
|
|
8
|
+
rescue StandardError
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def initialize(device_or_ptr)
|
|
13
|
+
@ptr =
|
|
14
|
+
case device_or_ptr
|
|
15
|
+
when Device
|
|
16
|
+
open_device(device_or_ptr)
|
|
17
|
+
when FFI::Pointer
|
|
18
|
+
device_or_ptr
|
|
19
|
+
else
|
|
20
|
+
raise ArgumentError, "expected USB::Device or FFI::Pointer"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
raise ArgumentError, "device handle pointer is required" if @ptr.nil? || @ptr.null?
|
|
24
|
+
|
|
25
|
+
@device = device_or_ptr if device_or_ptr.is_a?(Device)
|
|
26
|
+
ObjectSpace.define_finalizer(self, self.class.finalizer(@ptr))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def close
|
|
30
|
+
return if closed?
|
|
31
|
+
|
|
32
|
+
ObjectSpace.undefine_finalizer(self)
|
|
33
|
+
FFIBindings.libusb_close(@ptr)
|
|
34
|
+
@ptr = FFI::Pointer::NULL
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def closed?
|
|
38
|
+
@ptr.nil? || @ptr.null?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def device
|
|
42
|
+
@device ||= begin
|
|
43
|
+
device_ptr = FFIBindings.libusb_get_device(@ptr)
|
|
44
|
+
device_ptr.null? ? nil : Device.new(nil, device_ptr)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def configuration
|
|
49
|
+
configuration_ptr = FFI::MemoryPointer.new(:int)
|
|
50
|
+
Error.raise_on_error(FFIBindings.libusb_get_configuration(@ptr, configuration_ptr))
|
|
51
|
+
configuration_ptr.read_int
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def configuration=(value)
|
|
55
|
+
Error.raise_on_error(FFIBindings.libusb_set_configuration(@ptr, value))
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def claim_interface(number)
|
|
59
|
+
Error.raise_on_error(FFIBindings.libusb_claim_interface(@ptr, number))
|
|
60
|
+
self
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def release_interface(number)
|
|
64
|
+
Error.raise_on_error(FFIBindings.libusb_release_interface(@ptr, number))
|
|
65
|
+
self
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def set_interface_alt_setting(interface, alt_setting)
|
|
69
|
+
Error.raise_on_error(FFIBindings.libusb_set_interface_alt_setting(@ptr, interface, alt_setting))
|
|
70
|
+
self
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def clear_halt(endpoint)
|
|
74
|
+
Error.raise_on_error(FFIBindings.libusb_clear_halt(@ptr, endpoint))
|
|
75
|
+
self
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def reset_device
|
|
79
|
+
Error.raise_on_error(FFIBindings.libusb_reset_device(@ptr))
|
|
80
|
+
self
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def kernel_driver_active?(interface)
|
|
84
|
+
result = FFIBindings.libusb_kernel_driver_active(@ptr, interface)
|
|
85
|
+
Error.raise_on_error(result) == 1
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def detach_kernel_driver(interface)
|
|
89
|
+
Error.raise_on_error(FFIBindings.libusb_detach_kernel_driver(@ptr, interface))
|
|
90
|
+
self
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def attach_kernel_driver(interface)
|
|
94
|
+
Error.raise_on_error(FFIBindings.libusb_attach_kernel_driver(@ptr, interface))
|
|
95
|
+
self
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def auto_detach_kernel_driver=(enable)
|
|
99
|
+
Error.raise_on_error(FFIBindings.libusb_set_auto_detach_kernel_driver(@ptr, enable ? 1 : 0))
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def with_interface(number)
|
|
103
|
+
claimed = false
|
|
104
|
+
claim_interface(number)
|
|
105
|
+
claimed = true
|
|
106
|
+
yield self
|
|
107
|
+
ensure
|
|
108
|
+
release_interface(number) if claimed && !closed?
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def control_transfer(bm_request_type:, b_request:, w_value:, w_index:, data_or_length: nil, timeout: 1000)
|
|
112
|
+
if data_or_length.is_a?(Integer)
|
|
113
|
+
buffer = FFI::MemoryPointer.new(:uint8, [data_or_length, 1].max)
|
|
114
|
+
transferred = Error.raise_on_error(
|
|
115
|
+
FFIBindings.libusb_control_transfer(
|
|
116
|
+
@ptr, bm_request_type, b_request, w_value, w_index, buffer, data_or_length, timeout
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
buffer.read_bytes(transferred)
|
|
120
|
+
else
|
|
121
|
+
data = data_or_length.nil? ? "".b : data_or_length.to_s.b
|
|
122
|
+
buffer = bytes_pointer(data)
|
|
123
|
+
Error.raise_on_error(
|
|
124
|
+
FFIBindings.libusb_control_transfer(
|
|
125
|
+
@ptr, bm_request_type, b_request, w_value, w_index, buffer, data.bytesize, timeout
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def bulk_transfer(endpoint:, data_or_length:, timeout: 1000)
|
|
132
|
+
transferred = FFI::MemoryPointer.new(:int)
|
|
133
|
+
|
|
134
|
+
if data_or_length.is_a?(Integer)
|
|
135
|
+
buffer = FFI::MemoryPointer.new(:uint8, [data_or_length, 1].max)
|
|
136
|
+
Error.raise_on_error(FFIBindings.libusb_bulk_transfer(@ptr, endpoint, buffer, data_or_length, transferred, timeout))
|
|
137
|
+
buffer.read_bytes(transferred.read_int)
|
|
138
|
+
else
|
|
139
|
+
data = data_or_length.to_s.b
|
|
140
|
+
buffer = bytes_pointer(data)
|
|
141
|
+
Error.raise_on_error(FFIBindings.libusb_bulk_transfer(@ptr, endpoint, buffer, data.bytesize, transferred, timeout))
|
|
142
|
+
transferred.read_int
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def interrupt_transfer(endpoint:, data_or_length:, timeout: 1000)
|
|
147
|
+
transferred = FFI::MemoryPointer.new(:int)
|
|
148
|
+
|
|
149
|
+
if data_or_length.is_a?(Integer)
|
|
150
|
+
buffer = FFI::MemoryPointer.new(:uint8, [data_or_length, 1].max)
|
|
151
|
+
Error.raise_on_error(
|
|
152
|
+
FFIBindings.libusb_interrupt_transfer(@ptr, endpoint, buffer, data_or_length, transferred, timeout)
|
|
153
|
+
)
|
|
154
|
+
buffer.read_bytes(transferred.read_int)
|
|
155
|
+
else
|
|
156
|
+
data = data_or_length.to_s.b
|
|
157
|
+
buffer = bytes_pointer(data)
|
|
158
|
+
Error.raise_on_error(
|
|
159
|
+
FFIBindings.libusb_interrupt_transfer(@ptr, endpoint, buffer, data.bytesize, transferred, timeout)
|
|
160
|
+
)
|
|
161
|
+
transferred.read_int
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def string_descriptor_ascii(index)
|
|
166
|
+
return nil if index.to_i.zero?
|
|
167
|
+
|
|
168
|
+
buffer = FFI::MemoryPointer.new(:uint8, 256)
|
|
169
|
+
length = Error.raise_on_error(FFIBindings.libusb_get_string_descriptor_ascii(@ptr, index, buffer, 256))
|
|
170
|
+
buffer.read_string_length(length)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def manufacturer
|
|
174
|
+
string_descriptor_ascii(device.device_descriptor.manufacturer_index)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def product
|
|
178
|
+
string_descriptor_ascii(device.device_descriptor.product_index)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def serial_number
|
|
182
|
+
string_descriptor_ascii(device.device_descriptor.serial_number_index)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def alloc_streams(num_streams, endpoints)
|
|
186
|
+
endpoint_ptr = endpoint_array_pointer(endpoints)
|
|
187
|
+
Error.raise_on_error(FFIBindings.libusb_alloc_streams(@ptr, num_streams, endpoint_ptr, endpoints.length))
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def free_streams(endpoints)
|
|
191
|
+
endpoint_ptr = endpoint_array_pointer(endpoints)
|
|
192
|
+
Error.raise_on_error(FFIBindings.libusb_free_streams(@ptr, endpoint_ptr, endpoints.length))
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def dev_mem_alloc(length)
|
|
196
|
+
FFIBindings.libusb_dev_mem_alloc(@ptr, length)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def dev_mem_free(buffer, length)
|
|
200
|
+
Error.raise_on_error(FFIBindings.libusb_dev_mem_free(@ptr, buffer, length))
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def bos_descriptor
|
|
204
|
+
descriptor_ptr = FFI::MemoryPointer.new(:pointer)
|
|
205
|
+
Error.raise_on_error(FFIBindings.libusb_get_bos_descriptor(@ptr, descriptor_ptr))
|
|
206
|
+
BOSDescriptor.new(descriptor_ptr.read_pointer)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def to_ptr
|
|
210
|
+
@ptr
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def inspect
|
|
214
|
+
descriptor = device&.device_descriptor
|
|
215
|
+
if descriptor
|
|
216
|
+
format("#<USB::DeviceHandle %04x:%04x>", descriptor.vendor_id, descriptor.product_id)
|
|
217
|
+
else
|
|
218
|
+
"#<USB::DeviceHandle>"
|
|
219
|
+
end
|
|
220
|
+
rescue StandardError
|
|
221
|
+
"#<USB::DeviceHandle>"
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
private
|
|
225
|
+
|
|
226
|
+
def open_device(device)
|
|
227
|
+
handle_ptr = FFI::MemoryPointer.new(:pointer)
|
|
228
|
+
Error.raise_on_error(FFIBindings.libusb_open(device.to_ptr, handle_ptr))
|
|
229
|
+
handle_ptr.read_pointer
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def bytes_pointer(data)
|
|
233
|
+
pointer = FFI::MemoryPointer.new(:uint8, [data.bytesize, 1].max)
|
|
234
|
+
pointer.put_bytes(0, data) unless data.empty?
|
|
235
|
+
pointer
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def endpoint_array_pointer(endpoints)
|
|
239
|
+
pointer = FFI::MemoryPointer.new(:uint8, endpoints.length)
|
|
240
|
+
endpoints.each_with_index do |endpoint, index|
|
|
241
|
+
pointer.put_uint8(index, endpoint)
|
|
242
|
+
end
|
|
243
|
+
pointer
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module USB
|
|
4
|
+
class EndpointDescriptor
|
|
5
|
+
def initialize(interface_descriptor, struct)
|
|
6
|
+
@interface_descriptor = interface_descriptor
|
|
7
|
+
@struct = struct
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def endpoint_address
|
|
11
|
+
@struct[:bEndpointAddress]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def direction
|
|
15
|
+
in? ? :in : :out
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def transfer_type
|
|
19
|
+
case @struct[:bmAttributes] & 0x03
|
|
20
|
+
when TRANSFER_TYPE_CONTROL then :control
|
|
21
|
+
when TRANSFER_TYPE_ISOCHRONOUS then :isochronous
|
|
22
|
+
when TRANSFER_TYPE_BULK then :bulk
|
|
23
|
+
when TRANSFER_TYPE_INTERRUPT then :interrupt
|
|
24
|
+
else
|
|
25
|
+
:unknown
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def max_packet_size
|
|
30
|
+
@struct[:wMaxPacketSize]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def interval
|
|
34
|
+
@struct[:bInterval]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def refresh
|
|
38
|
+
@struct[:bRefresh]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def synch_address
|
|
42
|
+
@struct[:bSynchAddress]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def extra
|
|
46
|
+
return "".b if @struct[:extra].null? || @struct[:extra_length].zero?
|
|
47
|
+
|
|
48
|
+
@struct[:extra].read_bytes(@struct[:extra_length])
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def ss_endpoint_companion(context)
|
|
52
|
+
descriptor_ptr = FFI::MemoryPointer.new(:pointer)
|
|
53
|
+
Error.raise_on_error(
|
|
54
|
+
FFIBindings.libusb_get_ss_endpoint_companion_descriptor(context.to_ptr, @struct.pointer, descriptor_ptr)
|
|
55
|
+
)
|
|
56
|
+
SSEndpointCompanion.new(descriptor_ptr.read_pointer)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def in?
|
|
60
|
+
(endpoint_address & ENDPOINT_IN) == ENDPOINT_IN
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def out?
|
|
64
|
+
!in?
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def bulk?
|
|
68
|
+
transfer_type == :bulk
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def interrupt?
|
|
72
|
+
transfer_type == :interrupt
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def isochronous?
|
|
76
|
+
transfer_type == :isochronous
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def control?
|
|
80
|
+
transfer_type == :control
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def inspect
|
|
84
|
+
format("#<USB::EndpointDescriptor 0x%02x %s %s>", endpoint_address, transfer_type, direction)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def to_ptr
|
|
88
|
+
@struct.pointer
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
data/lib/usb/error.rb
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module USB
|
|
4
|
+
class Error < StandardError
|
|
5
|
+
attr_reader :code
|
|
6
|
+
|
|
7
|
+
ERROR_CLASSES = {}.freeze
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
def raise_on_error(result)
|
|
11
|
+
raise class_for_code(result).new(result) if result.is_a?(Integer) && result.negative?
|
|
12
|
+
|
|
13
|
+
result
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def class_for_code(code)
|
|
17
|
+
self::ERROR_CLASSES.fetch(code, self)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def libusb_error_name(code)
|
|
21
|
+
FFIBindings.libusb_error_name(code)
|
|
22
|
+
rescue StandardError
|
|
23
|
+
code.to_s
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def libusb_error_description(code)
|
|
27
|
+
FFIBindings.libusb_strerror(code)
|
|
28
|
+
rescue StandardError
|
|
29
|
+
"libusb error #{code}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def initialize(code)
|
|
34
|
+
@code = code
|
|
35
|
+
super("#{self.class.libusb_error_name(code)}: #{self.class.libusb_error_description(code)}")
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class TransferError < Error
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class IOError < Error
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class InvalidParamError < Error
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class AccessError < Error
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
class NoDeviceError < Error
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class NotFoundError < Error
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class BusyError < Error
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
class TimeoutError < Error
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
class OverflowError < Error
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
class PipeError < Error
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
class InterruptedError < Error
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class NoMemError < Error
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class NotSupportedError < Error
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
Error.send(:remove_const, :ERROR_CLASSES)
|
|
79
|
+
Error::ERROR_CLASSES = {
|
|
80
|
+
LIBUSB_ERROR_IO => IOError,
|
|
81
|
+
LIBUSB_ERROR_INVALID_PARAM => InvalidParamError,
|
|
82
|
+
LIBUSB_ERROR_ACCESS => AccessError,
|
|
83
|
+
LIBUSB_ERROR_NO_DEVICE => NoDeviceError,
|
|
84
|
+
LIBUSB_ERROR_NOT_FOUND => NotFoundError,
|
|
85
|
+
LIBUSB_ERROR_BUSY => BusyError,
|
|
86
|
+
LIBUSB_ERROR_TIMEOUT => TimeoutError,
|
|
87
|
+
LIBUSB_ERROR_OVERFLOW => OverflowError,
|
|
88
|
+
LIBUSB_ERROR_PIPE => PipeError,
|
|
89
|
+
LIBUSB_ERROR_INTERRUPTED => InterruptedError,
|
|
90
|
+
LIBUSB_ERROR_NO_MEM => NoMemError,
|
|
91
|
+
LIBUSB_ERROR_NOT_SUPPORTED => NotSupportedError,
|
|
92
|
+
LIBUSB_ERROR_OTHER => Error
|
|
93
|
+
}.freeze
|
|
94
|
+
end
|