usbkit 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/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +152 -0
- data/lib/usbkit/alternate_interface.rb +56 -0
- data/lib/usbkit/async.rb +57 -0
- data/lib/usbkit/backend/base.rb +133 -0
- data/lib/usbkit/backend/usb_ruby/device_wrapper.rb +148 -0
- data/lib/usbkit/backend/usb_ruby.rb +316 -0
- data/lib/usbkit/backend/usbfs/ioctl_commands.rb +16 -0
- data/lib/usbkit/backend/usbfs/sysfs_reader.rb +102 -0
- data/lib/usbkit/backend/usbfs.rb +200 -0
- data/lib/usbkit/configuration.rb +36 -0
- data/lib/usbkit/connection_event.rb +41 -0
- data/lib/usbkit/constants.rb +88 -0
- data/lib/usbkit/context.rb +168 -0
- data/lib/usbkit/device.rb +479 -0
- data/lib/usbkit/endpoint.rb +54 -0
- data/lib/usbkit/errors.rb +71 -0
- data/lib/usbkit/filter.rb +51 -0
- data/lib/usbkit/interface.rb +52 -0
- data/lib/usbkit/transfer_results.rb +193 -0
- data/lib/usbkit/usb_configuration.rb +45 -0
- data/lib/usbkit/version.rb +5 -0
- data/lib/usbkit.rb +87 -0
- metadata +79 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module UsbKit
|
|
4
|
+
module TransferResultValidation
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
def validate_status(status)
|
|
8
|
+
return status if TransferStatus.valid?(status)
|
|
9
|
+
|
|
10
|
+
raise ArgumentError, "Invalid transfer status: #{status.inspect}"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
##
|
|
15
|
+
# Result for control and bulk IN transfers.
|
|
16
|
+
#
|
|
17
|
+
class InTransferResult
|
|
18
|
+
include TransferResultValidation
|
|
19
|
+
|
|
20
|
+
# @return [String, nil] received data
|
|
21
|
+
# @return [Symbol] transfer status
|
|
22
|
+
attr_reader :data, :status
|
|
23
|
+
|
|
24
|
+
# @param attrs [Hash]
|
|
25
|
+
# @return [void]
|
|
26
|
+
def initialize(attrs)
|
|
27
|
+
@data = attrs[:data]
|
|
28
|
+
@status = validate_status(attrs.fetch(:status))
|
|
29
|
+
freeze
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @return [Hash]
|
|
33
|
+
def to_h
|
|
34
|
+
{ data: data, status: status }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @return [String]
|
|
38
|
+
def inspect
|
|
39
|
+
"#<#{self.class} status=#{status.inspect} bytes=#{data&.bytesize || 0}>"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
##
|
|
44
|
+
# Result for control and bulk OUT transfers.
|
|
45
|
+
#
|
|
46
|
+
class OutTransferResult
|
|
47
|
+
include TransferResultValidation
|
|
48
|
+
|
|
49
|
+
# @return [Integer] number of bytes written
|
|
50
|
+
# @return [Symbol] transfer status
|
|
51
|
+
attr_reader :bytes_written, :status
|
|
52
|
+
|
|
53
|
+
# @param attrs [Hash]
|
|
54
|
+
# @return [void]
|
|
55
|
+
def initialize(attrs)
|
|
56
|
+
@bytes_written = attrs.fetch(:bytes_written)
|
|
57
|
+
@status = validate_status(attrs.fetch(:status))
|
|
58
|
+
freeze
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @return [Hash]
|
|
62
|
+
def to_h
|
|
63
|
+
{ bytes_written: bytes_written, status: status }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# @return [String]
|
|
67
|
+
def inspect
|
|
68
|
+
"#<#{self.class} status=#{status.inspect} bytes_written=#{bytes_written.inspect}>"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
##
|
|
73
|
+
# Per-packet result for isochronous IN transfers.
|
|
74
|
+
#
|
|
75
|
+
class IsochronousInTransferPacket
|
|
76
|
+
include TransferResultValidation
|
|
77
|
+
|
|
78
|
+
# @return [String, nil] packet payload
|
|
79
|
+
# @return [Symbol] packet status
|
|
80
|
+
attr_reader :data, :status
|
|
81
|
+
|
|
82
|
+
# @param attrs [Hash]
|
|
83
|
+
# @return [void]
|
|
84
|
+
def initialize(attrs)
|
|
85
|
+
@data = attrs[:data]
|
|
86
|
+
@status = validate_status(attrs.fetch(:status))
|
|
87
|
+
freeze
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# @return [Hash]
|
|
91
|
+
def to_h
|
|
92
|
+
{ data: data, status: status }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# @return [String]
|
|
96
|
+
def inspect
|
|
97
|
+
"#<#{self.class} status=#{status.inspect} bytes=#{data&.bytesize || 0}>"
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
##
|
|
102
|
+
# Result for isochronous IN transfers.
|
|
103
|
+
#
|
|
104
|
+
class IsochronousInTransferResult
|
|
105
|
+
# @return [String, nil] concatenated transfer data
|
|
106
|
+
# @return [Array<IsochronousInTransferPacket>] packet-level results
|
|
107
|
+
attr_reader :data, :packets
|
|
108
|
+
|
|
109
|
+
# @param attrs [Hash]
|
|
110
|
+
# @return [void]
|
|
111
|
+
def initialize(attrs)
|
|
112
|
+
@packets = Array(attrs.fetch(:packets, [])).map { |packet| build_packet(packet) }.freeze
|
|
113
|
+
@data = attrs.key?(:data) ? attrs[:data] : packets.filter_map(&:data).join
|
|
114
|
+
freeze
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# @return [Hash]
|
|
118
|
+
def to_h
|
|
119
|
+
{ data: data, packets: packets.map(&:to_h) }
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# @return [String]
|
|
123
|
+
def inspect
|
|
124
|
+
"#<#{self.class} packets=#{packets.size} bytes=#{data&.bytesize || 0}>"
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
private
|
|
128
|
+
|
|
129
|
+
def build_packet(packet)
|
|
130
|
+
packet.is_a?(IsochronousInTransferPacket) ? packet : IsochronousInTransferPacket.new(packet)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
##
|
|
135
|
+
# Per-packet result for isochronous OUT transfers.
|
|
136
|
+
#
|
|
137
|
+
class IsochronousOutTransferPacket
|
|
138
|
+
include TransferResultValidation
|
|
139
|
+
|
|
140
|
+
# @return [Integer] number of bytes written for the packet
|
|
141
|
+
# @return [Symbol] packet status
|
|
142
|
+
attr_reader :bytes_written, :status
|
|
143
|
+
|
|
144
|
+
# @param attrs [Hash]
|
|
145
|
+
# @return [void]
|
|
146
|
+
def initialize(attrs)
|
|
147
|
+
@bytes_written = attrs.fetch(:bytes_written)
|
|
148
|
+
@status = validate_status(attrs.fetch(:status))
|
|
149
|
+
freeze
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# @return [Hash]
|
|
153
|
+
def to_h
|
|
154
|
+
{ bytes_written: bytes_written, status: status }
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# @return [String]
|
|
158
|
+
def inspect
|
|
159
|
+
"#<#{self.class} status=#{status.inspect} bytes_written=#{bytes_written.inspect}>"
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
##
|
|
164
|
+
# Result for isochronous OUT transfers.
|
|
165
|
+
#
|
|
166
|
+
class IsochronousOutTransferResult
|
|
167
|
+
# @return [Array<IsochronousOutTransferPacket>] packet-level results
|
|
168
|
+
attr_reader :packets
|
|
169
|
+
|
|
170
|
+
# @param attrs [Hash]
|
|
171
|
+
# @return [void]
|
|
172
|
+
def initialize(attrs)
|
|
173
|
+
@packets = Array(attrs.fetch(:packets, [])).map { |packet| build_packet(packet) }.freeze
|
|
174
|
+
freeze
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# @return [Hash]
|
|
178
|
+
def to_h
|
|
179
|
+
{ packets: packets.map(&:to_h) }
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# @return [String]
|
|
183
|
+
def inspect
|
|
184
|
+
"#<#{self.class} packets=#{packets.size}>"
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
private
|
|
188
|
+
|
|
189
|
+
def build_packet(packet)
|
|
190
|
+
packet.is_a?(IsochronousOutTransferPacket) ? packet : IsochronousOutTransferPacket.new(packet)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module UsbKit
|
|
4
|
+
##
|
|
5
|
+
# Immutable USB configuration descriptor.
|
|
6
|
+
#
|
|
7
|
+
class Configuration
|
|
8
|
+
# @return [Device, nil] parent device
|
|
9
|
+
# @return [Integer] USB configuration value
|
|
10
|
+
# @return [String, nil] configuration name
|
|
11
|
+
# @return [Array<Interface>] interfaces for the configuration
|
|
12
|
+
attr_reader :device, :configuration_value, :configuration_name, :interfaces
|
|
13
|
+
|
|
14
|
+
# @param attrs [Hash]
|
|
15
|
+
# @param device [Device, nil]
|
|
16
|
+
# @return [void]
|
|
17
|
+
def initialize(attrs, device: nil)
|
|
18
|
+
@device = device
|
|
19
|
+
@configuration_value = attrs.fetch(:configuration_value)
|
|
20
|
+
@configuration_name = attrs[:configuration_name]
|
|
21
|
+
@interfaces = Array(attrs.fetch(:interfaces, [])).map { |interface| build_interface(interface) }.freeze
|
|
22
|
+
freeze
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @return [Hash]
|
|
26
|
+
def to_h
|
|
27
|
+
{
|
|
28
|
+
configuration_value: configuration_value,
|
|
29
|
+
configuration_name: configuration_name,
|
|
30
|
+
interfaces: interfaces.map(&:to_h)
|
|
31
|
+
}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @return [String]
|
|
35
|
+
def inspect
|
|
36
|
+
"#<#{self.class} configuration_value=#{configuration_value.inspect} interfaces=#{interfaces.size}>"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def build_interface(interface)
|
|
42
|
+
interface.is_a?(Interface) ? interface : Interface.new(interface)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
data/lib/usbkit.rb
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "usbkit/version"
|
|
4
|
+
require_relative "usbkit/errors"
|
|
5
|
+
require_relative "usbkit/constants"
|
|
6
|
+
require_relative "usbkit/configuration"
|
|
7
|
+
require_relative "usbkit/endpoint"
|
|
8
|
+
require_relative "usbkit/alternate_interface"
|
|
9
|
+
require_relative "usbkit/interface"
|
|
10
|
+
require_relative "usbkit/usb_configuration"
|
|
11
|
+
require_relative "usbkit/transfer_results"
|
|
12
|
+
require_relative "usbkit/connection_event"
|
|
13
|
+
require_relative "usbkit/filter"
|
|
14
|
+
require_relative "usbkit/device"
|
|
15
|
+
require_relative "usbkit/context"
|
|
16
|
+
require_relative "usbkit/backend/base"
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# WebUSB-inspired USB access toolkit for Ruby.
|
|
20
|
+
#
|
|
21
|
+
module UsbKit
|
|
22
|
+
class << self
|
|
23
|
+
attr_writer :config
|
|
24
|
+
|
|
25
|
+
# @return [GemConfiguration] current gem-level configuration
|
|
26
|
+
def config
|
|
27
|
+
@config ||= GemConfiguration.new
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Configure UsbKit.
|
|
31
|
+
#
|
|
32
|
+
# @yieldparam config [GemConfiguration]
|
|
33
|
+
# @return [GemConfiguration, Enumerator]
|
|
34
|
+
def configure
|
|
35
|
+
return enum_for(__method__) unless block_given?
|
|
36
|
+
|
|
37
|
+
yield(config)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @return [Backend::Base] resolved backend implementation
|
|
41
|
+
# @raise [BackendNotAvailableError] when no backend can be resolved
|
|
42
|
+
def backend
|
|
43
|
+
@backend ||= resolve_backend
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @return [void]
|
|
47
|
+
def reset_backend!
|
|
48
|
+
@backend = nil
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @return [Context]
|
|
52
|
+
def context
|
|
53
|
+
Context.new
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def resolve_backend
|
|
59
|
+
case config.backend
|
|
60
|
+
when :auto
|
|
61
|
+
try_usb_ruby || try_usbfs || raise(BackendNotAvailableError, "No USB backend available")
|
|
62
|
+
when :usb_ruby
|
|
63
|
+
try_usb_ruby || raise(BackendNotAvailableError, "usb-ruby backend not available")
|
|
64
|
+
when :usbfs
|
|
65
|
+
try_usbfs || raise(BackendNotAvailableError, "usbfs backend not available")
|
|
66
|
+
else
|
|
67
|
+
raise ArgumentError, "Unknown backend: #{config.backend.inspect}"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def try_usb_ruby
|
|
72
|
+
require_relative "usbkit/backend/usb_ruby"
|
|
73
|
+
Backend::UsbRuby.new
|
|
74
|
+
rescue LoadError, BackendNotAvailableError
|
|
75
|
+
nil
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def try_usbfs
|
|
79
|
+
return nil unless RUBY_PLATFORM.match?(/linux/i)
|
|
80
|
+
|
|
81
|
+
require_relative "usbkit/backend/usbfs"
|
|
82
|
+
Backend::Usbfs.new
|
|
83
|
+
rescue LoadError, BackendNotAvailableError
|
|
84
|
+
nil
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: usbkit
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- UsbKit Contributors
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: usb-ruby
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
description: UsbKit provides a WebUSB API-compatible interface for communicating with
|
|
27
|
+
USB devices from Ruby using usb-ruby with a Linux usbfs fallback.
|
|
28
|
+
executables: []
|
|
29
|
+
extensions: []
|
|
30
|
+
extra_rdoc_files: []
|
|
31
|
+
files:
|
|
32
|
+
- CHANGELOG.md
|
|
33
|
+
- LICENSE.txt
|
|
34
|
+
- README.md
|
|
35
|
+
- lib/usbkit.rb
|
|
36
|
+
- lib/usbkit/alternate_interface.rb
|
|
37
|
+
- lib/usbkit/async.rb
|
|
38
|
+
- lib/usbkit/backend/base.rb
|
|
39
|
+
- lib/usbkit/backend/usb_ruby.rb
|
|
40
|
+
- lib/usbkit/backend/usb_ruby/device_wrapper.rb
|
|
41
|
+
- lib/usbkit/backend/usbfs.rb
|
|
42
|
+
- lib/usbkit/backend/usbfs/ioctl_commands.rb
|
|
43
|
+
- lib/usbkit/backend/usbfs/sysfs_reader.rb
|
|
44
|
+
- lib/usbkit/configuration.rb
|
|
45
|
+
- lib/usbkit/connection_event.rb
|
|
46
|
+
- lib/usbkit/constants.rb
|
|
47
|
+
- lib/usbkit/context.rb
|
|
48
|
+
- lib/usbkit/device.rb
|
|
49
|
+
- lib/usbkit/endpoint.rb
|
|
50
|
+
- lib/usbkit/errors.rb
|
|
51
|
+
- lib/usbkit/filter.rb
|
|
52
|
+
- lib/usbkit/interface.rb
|
|
53
|
+
- lib/usbkit/transfer_results.rb
|
|
54
|
+
- lib/usbkit/usb_configuration.rb
|
|
55
|
+
- lib/usbkit/version.rb
|
|
56
|
+
homepage: https://github.com/usbkit-rb/usbkit
|
|
57
|
+
licenses:
|
|
58
|
+
- MIT
|
|
59
|
+
metadata:
|
|
60
|
+
homepage_uri: https://github.com/usbkit-rb/usbkit
|
|
61
|
+
source_code_uri: https://github.com/usbkit-rb/usbkit
|
|
62
|
+
rdoc_options: []
|
|
63
|
+
require_paths:
|
|
64
|
+
- lib
|
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - ">="
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '3.1'
|
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '0'
|
|
75
|
+
requirements: []
|
|
76
|
+
rubygems_version: 4.0.6
|
|
77
|
+
specification_version: 4
|
|
78
|
+
summary: Ruby implementation of the WebUSB API for cross-platform USB access
|
|
79
|
+
test_files: []
|