libusb 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,359 @@
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 representing a USB device detected on the system.
20
+ #
21
+ # Devices of the system can be obtained with {Context#devices} .
22
+ class Device
23
+ include Comparable
24
+
25
+ # @return [Context] the context this device belongs to.
26
+ attr_reader :context
27
+
28
+ def initialize context, pDev
29
+ @context = context
30
+ def pDev.unref_device(id)
31
+ Call.libusb_unref_device(self)
32
+ end
33
+ ObjectSpace.define_finalizer(self, pDev.method(:unref_device))
34
+ Call.libusb_ref_device(pDev)
35
+ @pDev = pDev
36
+
37
+ @pDevDesc = Call::DeviceDescriptor.new
38
+ res = Call.libusb_get_device_descriptor(@pDev, @pDevDesc)
39
+ LIBUSB.raise_error res, "in libusb_get_device_descriptor" if res!=0
40
+ end
41
+
42
+ # Open the device and obtain a device handle.
43
+ #
44
+ # A handle allows you to perform I/O on the device in question.
45
+ # This is a non-blocking function; no requests are sent over the bus.
46
+ #
47
+ # If called with a block, the handle is passed to the block
48
+ # and is closed when the block has finished.
49
+ #
50
+ # You need proper device access:
51
+ # * Linux: read+write permissions to <tt>/dev/bus/usb/<bus>/<dev></tt>
52
+ # * Windows: by installing a WinUSB-driver for the device (see {file:README.rdoc#Usage_on_Windows} )
53
+ #
54
+ # @return [DevHandle] Handle to the device.
55
+ def open
56
+ ppHandle = FFI::MemoryPointer.new :pointer
57
+ res = Call.libusb_open(@pDev, ppHandle)
58
+ LIBUSB.raise_error res, "in libusb_open" if res!=0
59
+ handle = DevHandle.new self, ppHandle.read_pointer
60
+ return handle unless block_given?
61
+ begin
62
+ yield handle
63
+ ensure
64
+ handle.close
65
+ end
66
+ end
67
+
68
+ # Open the device and claim an interface.
69
+ #
70
+ # This is a convenience method to {Device#open} and {DevHandle#claim_interface}.
71
+ # Must be called with a block. When the block has finished, the interface
72
+ # will be released and the device will be closed.
73
+ #
74
+ # @param [Interface, Fixnum] interface the interface or it's bInterfaceNumber you wish to claim
75
+ def open_interface(interface)
76
+ open do |dev|
77
+ dev.claim_interface(interface) do
78
+ yield dev
79
+ end
80
+ end
81
+ end
82
+
83
+ # Get the number of the bus that a device is connected to.
84
+ def bus_number
85
+ Call.libusb_get_bus_number(@pDev)
86
+ end
87
+
88
+ # Get the address of the device on the bus it is connected to.
89
+ def device_address
90
+ Call.libusb_get_device_address(@pDev)
91
+ end
92
+
93
+ if Call.respond_to?(:libusb_get_port_number)
94
+ # Get the number of the port that a device is connected to.
95
+ # Available since libusb-1.0.12.
96
+ #
97
+ # @return [Fixnum, nil] the port number (+nil+ if not available)
98
+ # @see #port_path
99
+ def port_number
100
+ r = Call.libusb_get_port_number(@pDev)
101
+ r==0 ? nil : r
102
+ end
103
+
104
+ # Get the the parent from the specified device [EXPERIMENTAL].
105
+ # Available since libusb-1.0.12.
106
+ #
107
+ # @return [Device, nil] the device parent or +nil+ if not available
108
+ # @see #port_path
109
+ def parent
110
+ pppDevs = FFI::MemoryPointer.new :pointer
111
+ Call.libusb_get_device_list(@context.instance_variable_get(:@ctx), pppDevs)
112
+ ppDevs = pppDevs.read_pointer
113
+ pParent = Call.libusb_get_parent(@pDev)
114
+ parent = pParent.null? ? nil : Device.new(@context, pParent)
115
+ Call.libusb_free_device_list(ppDevs, 1)
116
+ parent
117
+ end
118
+
119
+ # Get the list of all port numbers from root for the specified device.
120
+ # Available since libusb-1.0.12.
121
+ #
122
+ # @return [Array<Fixnum>]
123
+ # @see #parent
124
+ # @see #port_number
125
+ def port_path
126
+ # As per the USB 3.0 specs, the current maximum limit for the depth is 7.
127
+ path_len = 7
128
+ pPath = FFI::MemoryPointer.new :pointer, path_len
129
+ l = Call.libusb_get_port_path(@context.instance_variable_get(:@ctx), @pDev, pPath, path_len)
130
+ pPath.read_array_of_uint8(l)
131
+ end
132
+ end
133
+
134
+ if Call.respond_to?(:libusb_get_device_speed)
135
+ # Get the negotiated connection speed for a device.
136
+ # Available since libusb-1.0.9.
137
+ #
138
+ # @return [Symbol] a {Call::Speeds Speeds} symbol, where +:SPEED_UNKNOWN+ means that
139
+ # the OS doesn't know or doesn't support returning the negotiated speed.
140
+ def device_speed
141
+ Call.libusb_get_device_speed(@pDev)
142
+ end
143
+ end
144
+
145
+ # Convenience function to retrieve the wMaxPacketSize value for a
146
+ # particular endpoint in the active device configuration.
147
+ #
148
+ # @param [Endpoint, Fixnum] endpoint (address of) the endpoint in question
149
+ # @return [Fixnum] the wMaxPacketSize value
150
+ def max_packet_size(endpoint)
151
+ endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
152
+ res = Call.libusb_get_max_packet_size(@pDev, endpoint)
153
+ LIBUSB.raise_error res, "in libusb_get_max_packet_size" unless res>=0
154
+ res
155
+ end
156
+
157
+ # Calculate the maximum packet size which a specific endpoint is capable is
158
+ # sending or receiving in the duration of 1 microframe.
159
+ #
160
+ # Only the active configution is examined. The calculation is based on the
161
+ # wMaxPacketSize field in the endpoint descriptor as described in section 9.6.6
162
+ # in the USB 2.0 specifications.
163
+ #
164
+ # If acting on an isochronous or interrupt endpoint, this function will
165
+ # multiply the value found in bits 0:10 by the number of transactions per
166
+ # microframe (determined by bits 11:12). Otherwise, this function just returns
167
+ # the numeric value found in bits 0:10.
168
+ #
169
+ # This function is useful for setting up isochronous transfers, for example
170
+ # you might use the return value from this function to call
171
+ # IsoPacket#alloc_buffer in order to set the length field
172
+ # of an isochronous packet in a transfer.
173
+ #
174
+ # @param [Endpoint, Fixnum] endpoint (address of) the endpoint in question
175
+ # @return [Fixnum] the maximum packet size which can be sent/received on this endpoint
176
+ def max_iso_packet_size(endpoint)
177
+ endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
178
+ res = Call.libusb_get_max_iso_packet_size(@pDev, endpoint)
179
+ LIBUSB.raise_error res, "in libusb_get_max_iso_packet_size" unless res>=0
180
+ res
181
+ end
182
+
183
+ # Obtain a config descriptor of the device.
184
+ #
185
+ # @param [Fixnum] index number of the config descriptor
186
+ # @return Configuration
187
+ def config_descriptor(index)
188
+ ppConfig = FFI::MemoryPointer.new :pointer
189
+ res = Call.libusb_get_config_descriptor(@pDev, index, ppConfig)
190
+ LIBUSB.raise_error res, "in libusb_get_config_descriptor" if res!=0
191
+ pConfig = ppConfig.read_pointer
192
+ config = Configuration.new(self, pConfig)
193
+ config
194
+ end
195
+
196
+ # Size of the Descriptor in Bytes (18 bytes)
197
+ def bLength
198
+ @pDevDesc[:bLength]
199
+ end
200
+
201
+ # Device Descriptor (0x01)
202
+ def bDescriptorType
203
+ @pDevDesc[:bDescriptorType]
204
+ end
205
+
206
+ # USB specification release number which device complies too
207
+ #
208
+ # @return [Integer] in binary-coded decimal
209
+ def bcdUSB
210
+ @pDevDesc[:bcdUSB]
211
+ end
212
+
213
+ # USB-IF class code for the device (Assigned by USB Org)
214
+ #
215
+ # * If equal to 0x00, each interface specifies it's own class code
216
+ # * If equal to 0xFF, the class code is vendor specified
217
+ # * Otherwise field is valid Class Code
218
+ def bDeviceClass
219
+ @pDevDesc[:bDeviceClass]
220
+ end
221
+
222
+ # USB-IF subclass code for the device, qualified by the {Device#bDeviceClass}
223
+ # value (Assigned by USB Org)
224
+ def bDeviceSubClass
225
+ @pDevDesc[:bDeviceSubClass]
226
+ end
227
+
228
+ # USB-IF protocol code for the device, qualified by the {Device#bDeviceClass}
229
+ # and {Device#bDeviceSubClass} values (Assigned by USB Org)
230
+ def bDeviceProtocol
231
+ @pDevDesc[:bDeviceProtocol]
232
+ end
233
+
234
+ # Maximum Packet Size for Endpoint 0. Valid Sizes are 8, 16, 32, 64
235
+ def bMaxPacketSize0
236
+ @pDevDesc[:bMaxPacketSize0]
237
+ end
238
+
239
+ # USB-IF vendor ID (Assigned by USB Org)
240
+ def idVendor
241
+ @pDevDesc[:idVendor]
242
+ end
243
+
244
+ # USB-IF product ID (Assigned by Manufacturer)
245
+ def idProduct
246
+ @pDevDesc[:idProduct]
247
+ end
248
+
249
+ # Device release number in binary-coded decimal.
250
+ def bcdDevice
251
+ @pDevDesc[:bcdDevice]
252
+ end
253
+
254
+ # Index of string descriptor describing manufacturer.
255
+ def iManufacturer
256
+ @pDevDesc[:iManufacturer]
257
+ end
258
+
259
+ # Index of string descriptor describing product.
260
+ def iProduct
261
+ @pDevDesc[:iProduct]
262
+ end
263
+
264
+ # Index of string descriptor containing device serial number.
265
+ def iSerialNumber
266
+ @pDevDesc[:iSerialNumber]
267
+ end
268
+
269
+ # Number of Possible Configurations
270
+ def bNumConfigurations
271
+ @pDevDesc[:bNumConfigurations]
272
+ end
273
+
274
+
275
+ def inspect
276
+ attrs = []
277
+ attrs << "#{self.bus_number}/#{self.device_address}"
278
+ attrs << ("%04x:%04x" % [self.idVendor, self.idProduct])
279
+ attrs << self.manufacturer
280
+ attrs << self.product
281
+ attrs << self.serial_number
282
+ if self.bDeviceClass == LIBUSB::CLASS_PER_INTERFACE
283
+ devclass = self.settings.map {|i|
284
+ LIBUSB.dev_string(i.bInterfaceClass, i.bInterfaceSubClass, i.bInterfaceProtocol)
285
+ }.join(", ")
286
+ else
287
+ devclass = LIBUSB.dev_string(self.bDeviceClass, self.bDeviceSubClass, self.bDeviceProtocol)
288
+ end
289
+ attrs << "(#{devclass})"
290
+ attrs.compact!
291
+ "\#<#{self.class} #{attrs.join(' ')}>"
292
+ end
293
+
294
+ def try_string_descriptor_ascii(i)
295
+ begin
296
+ open{|h| h.string_descriptor_ascii(i) }
297
+ rescue
298
+ "?"
299
+ end
300
+ end
301
+
302
+ # Return manufacturer of the device
303
+ # @return String
304
+ def manufacturer
305
+ return @manufacturer if defined? @manufacturer
306
+ @manufacturer = try_string_descriptor_ascii(self.iManufacturer)
307
+ @manufacturer.strip! if @manufacturer
308
+ @manufacturer
309
+ end
310
+
311
+ # Return product name of the device.
312
+ # @return String
313
+ def product
314
+ return @product if defined? @product
315
+ @product = try_string_descriptor_ascii(self.iProduct)
316
+ @product.strip! if @product
317
+ @product
318
+ end
319
+
320
+ # Return serial number of the device.
321
+ # @return String
322
+ def serial_number
323
+ return @serial_number if defined? @serial_number
324
+ @serial_number = try_string_descriptor_ascii(self.iSerialNumber)
325
+ @serial_number.strip! if @serial_number
326
+ @serial_number
327
+ end
328
+
329
+ # Return configurations of the device.
330
+ # @return [Array<Configuration>]
331
+ def configurations
332
+ configs = []
333
+ bNumConfigurations.times do |config_index|
334
+ begin
335
+ configs << config_descriptor(config_index)
336
+ rescue RuntimeError
337
+ # On Windows some devices don't return it's configuration.
338
+ end
339
+ end
340
+ configs
341
+ end
342
+
343
+ # Return all interfaces of this device.
344
+ # @return [Array<Interface>]
345
+ def interfaces() self.configurations.map {|d| d.interfaces }.flatten end
346
+ # Return all interface decriptions of this device.
347
+ # @return [Array<Setting>]
348
+ def settings() self.interfaces.map {|d| d.settings }.flatten end
349
+ # Return all endpoints of all interfaces of this device.
350
+ # @return [Array<Endpoint>]
351
+ def endpoints() self.settings.map {|d| d.endpoints }.flatten end
352
+
353
+ def <=>(o)
354
+ t = bus_number<=>o.bus_number
355
+ t = device_address<=>o.device_address if t==0
356
+ t
357
+ end
358
+ end
359
+ end
@@ -0,0 +1,147 @@
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 Endpoint < FFI::Struct
20
+ include Comparable
21
+
22
+ layout :bLength, :uint8,
23
+ :bDescriptorType, :uint8,
24
+ :bEndpointAddress, :uint8,
25
+ :bmAttributes, :uint8,
26
+ :wMaxPacketSize, :uint16,
27
+ :bInterval, :uint8,
28
+ :bRefresh, :uint8,
29
+ :bSynchAddress, :uint8,
30
+ :extra, :pointer,
31
+ :extra_length, :int
32
+
33
+ # Size of Descriptor in Bytes (7 bytes)
34
+ def bLength
35
+ self[:bLength]
36
+ end
37
+
38
+ # Descriptor type (0x05)
39
+ def bDescriptorType
40
+ self[:bDescriptorType]
41
+ end
42
+
43
+ # The address of the endpoint described by this descriptor.
44
+ #
45
+ # * Bits 0..3: Endpoint Number.
46
+ # * Bits 4..6: Reserved. Set to Zero
47
+ # * Bits 7: Direction 0 = Out, 1 = In (Ignored for Control Endpoints)
48
+ #
49
+ # @return [Integer]
50
+ def bEndpointAddress
51
+ self[:bEndpointAddress]
52
+ end
53
+
54
+ # Attributes which apply to the endpoint when it is configured using the {Configuration#bConfigurationValue}.
55
+ #
56
+ # * Bits 0..1: Transfer Type
57
+ # * 00 = Control
58
+ # * 01 = Isochronous
59
+ # * 10 = Bulk
60
+ # * 11 = Interrupt
61
+ # * Bits 2..7: are reserved. If Isochronous endpoint,
62
+ # * Bits 3..2: Synchronisation Type (Iso Mode)
63
+ # * 00 = No Synchonisation
64
+ # * 01 = Asynchronous
65
+ # * 10 = Adaptive
66
+ # * 11 = Synchronous
67
+ # * Bits 5..4: Usage Type (Iso Mode)
68
+ # * 00 = Data Endpoint
69
+ # * 01 = Feedback Endpoint
70
+ # * 10 = Explicit Feedback Data Endpoint
71
+ # * 11 = Reserved
72
+ #
73
+ # @return [Integer]
74
+ def bmAttributes
75
+ self[:bmAttributes]
76
+ end
77
+
78
+ # Maximum Packet Size this endpoint is capable of sending or receiving
79
+ def wMaxPacketSize
80
+ self[:wMaxPacketSize]
81
+ end
82
+
83
+ # Interval for polling endpoint data transfers. Value in frame counts.
84
+ # Ignored for Bulk & Control Endpoints. Isochronous must equal 1 and field
85
+ # may range from 1 to 255 for interrupt endpoints.
86
+ #
87
+ # The interval is respected by the kernel driver, so user mode processes
88
+ # using libusb don't need to care about it.
89
+ def bInterval
90
+ self[:bInterval]
91
+ end
92
+
93
+ # For audio devices only: the rate at which synchronization feedback is provided.
94
+ def bRefresh
95
+ self[:bRefresh]
96
+ end
97
+
98
+ # For audio devices only: the address if the synch endpoint.
99
+ def bSynchAddress
100
+ self[:bSynchAddress]
101
+ end
102
+
103
+ # Extra descriptors.
104
+ #
105
+ # @return [String]
106
+ def extra
107
+ return if self[:extra].null?
108
+ self[:extra].read_string(self[:extra_length])
109
+ end
110
+
111
+ def initialize(setting, *args)
112
+ @setting = setting
113
+ super(*args)
114
+ end
115
+
116
+ # @return [Setting] the setting this endpoint belongs to.
117
+ attr_reader :setting
118
+
119
+ def inspect
120
+ endpoint_address = self.bEndpointAddress
121
+ num = endpoint_address & 0b00001111
122
+ inout = (endpoint_address & 0b10000000) == 0 ? "OUT" : "IN "
123
+ bits = self.bmAttributes
124
+ transfer_type = %w[Control Isochronous Bulk Interrupt][0b11 & bits]
125
+ type = [transfer_type]
126
+ if transfer_type == 'Isochronous'
127
+ synchronization_type = %w[NoSynchronization Asynchronous Adaptive Synchronous][(0b1100 & bits) >> 2]
128
+ usage_type = %w[Data Feedback ImplicitFeedback ?][(0b110000 & bits) >> 4]
129
+ type << synchronization_type << usage_type
130
+ end
131
+ "\#<#{self.class} #{num} #{inout} #{type.join(" ")}>"
132
+ end
133
+
134
+ # The {Device} this Endpoint belongs to.
135
+ def device() self.setting.interface.configuration.device end
136
+ # The {Configuration} this Endpoint belongs to.
137
+ def configuration() self.setting.interface.configuration end
138
+ # The {Interface} this Endpoint belongs to.
139
+ def interface() self.setting.interface end
140
+
141
+ def <=>(o)
142
+ t = setting<=>o.setting
143
+ t = bEndpointAddress<=>o.bEndpointAddress if t==0
144
+ t
145
+ end
146
+ end
147
+ end