ruby-usb 0.1.3-x86-mswin32-60

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.
Binary file
@@ -0,0 +1,420 @@
1
+ # usb.rb - utility methods for libusb binding for Ruby.
2
+ #
3
+ # Copyright (C) 2007 Tanaka Akira
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+
19
+ require 'usb.so'
20
+
21
+ # USB module is a binding for libusb.
22
+ #
23
+ # It needs appropriate privilege to access USB.
24
+ # For example, the process should be an member of plugdev group on Debin GNU/Linux (etch).
25
+ #
26
+ # = Example
27
+ #
28
+ # 1. list up USB devices
29
+ #
30
+ # require 'usb'
31
+ # require 'pp'
32
+ # pp USB.devices
33
+ # #=>
34
+ # [#<USB::Device 001/001 0000:0000 Linux 2.6.17-2-486 uhci_hcd UHCI Host Controller 0000:00:1d.0 (Full speed Hub)>,
35
+ # #<USB::Device 002/001 0000:0000 Linux 2.6.17-2-486 uhci_hcd UHCI Host Controller 0000:00:1d.1 (Full speed Hub)>,
36
+ # #<USB::Device 003/001 0000:0000 Linux 2.6.17-2-486 uhci_hcd UHCI Host Controller 0000:00:1d.2 (Full speed Hub)>,
37
+ # #<USB::Device 004/001 0000:0000 Linux 2.6.17-2-486 ehci_hcd EHCI Host Controller 0000:00:1d.7 (Hi-speed Hub with single TT)>]
38
+ #
39
+ # 2. find a device by bus id and device id
40
+ #
41
+ # # find the device "004/001" in the above list.
42
+ # dev = USB.find_bus(4).find_device(1)
43
+ # p dev
44
+ # #=>
45
+ # #<USB::Device 004/001 0000:0000 Linux 2.6.17-2-486 ehci_hcd EHCI Host Controller 0000:00:1d.7 (Hi-speed Hub with single TT)>
46
+ #
47
+ # 3. open a device
48
+ #
49
+ # dev.open {|handle| p handle }
50
+ # #=>
51
+ # #<USB::DevHandle:0xa7d94688>
52
+ #
53
+ # = USB overview
54
+ #
55
+ # * A host has busses.
56
+ # * A bus has devices.
57
+ # * A device has configurations.
58
+ # * A configuration has interfaces.
59
+ # * A interface has settings.
60
+ # * A setting has endpoints.
61
+ #
62
+
63
+ module USB
64
+ def USB.busses
65
+ result = []
66
+ bus = USB.first_bus
67
+ while bus
68
+ result << bus
69
+ bus = bus.next
70
+ end
71
+ result.sort_by {|b| b.dirname }
72
+ end
73
+
74
+ def USB.devices() USB.busses.map {|b| b.devices }.flatten end
75
+ def USB.configurations() USB.devices.map {|d| d.configurations }.flatten end
76
+ def USB.interfaces() USB.configurations.map {|d| d.interfaces }.flatten end
77
+ def USB.settings() USB.interfaces.map {|d| d.settings }.flatten end
78
+ def USB.endpoints() USB.settings.map {|d| d.endpoints }.flatten end
79
+
80
+ def USB.find_bus(n)
81
+ bus = USB.first_bus
82
+ while bus
83
+ return bus if n == bus.dirname.to_i
84
+ bus = bus.next
85
+ end
86
+ return nil
87
+ end
88
+
89
+ # searches devices by USB device class, subclass and protocol.
90
+ #
91
+ # # find hubs.
92
+ # USB.each_device_by_class(USB::USB_CLASS_HUB) {|d| p d }'
93
+ #
94
+ # # find Full speed Hubs
95
+ # USB.each_device_by_class(USB::USB_CLASS_HUB, 0, 0) {|d| p d }'
96
+ #
97
+ # # find Hi-speed Hubs with single TT
98
+ # USB.each_device_by_class(USB::USB_CLASS_HUB, 0, 1) {|d| p d }'
99
+ #
100
+ # # find Hi-speed Hubs with multiple TT
101
+ # USB.each_device_by_class(USB::USB_CLASS_HUB, 0, 2) {|d| p d }'
102
+ #
103
+ def USB.each_device_by_class(devclass, subclass=nil, protocol=nil)
104
+ USB.devices.each {|dev|
105
+ if dev.bDeviceClass == USB::USB_CLASS_PER_INTERFACE
106
+ found = dev.settings.any? {|s|
107
+ s.bInterfaceClass == devclass &&
108
+ (!subclass || s.bInterfaceSubClass == subclass) &&
109
+ (!protocol || s.bInterfaceProtocol == protocol) }
110
+ else
111
+ found = dev.bDeviceClass == devclass &&
112
+ (!subclass || dev.bDeviceSubClass == subclass) &&
113
+ (!protocol || dev.bDeviceProtocol == protocol)
114
+ end
115
+ yield dev if found
116
+ }
117
+ nil
118
+ end
119
+
120
+ class Bus
121
+ def inspect
122
+ if self.revoked?
123
+ "\#<#{self.class} revoked>"
124
+ else
125
+ "\#<#{self.class} #{self.dirname}>"
126
+ end
127
+ end
128
+
129
+ def devices
130
+ result = []
131
+ device = self.first_device
132
+ while device
133
+ result << device
134
+ device = device.next
135
+ end
136
+ result.sort_by {|d| d.filename }
137
+ end
138
+
139
+ def configurations() self.devices.map {|d| d.configurations }.flatten end
140
+ def interfaces() self.configurations.map {|d| d.interfaces }.flatten end
141
+ def settings() self.interfaces.map {|d| d.settings }.flatten end
142
+ def endpoints() self.settings.map {|d| d.endpoints }.flatten end
143
+
144
+ def find_device(n)
145
+ device = self.first_device
146
+ while device
147
+ return device if n == device.filename.to_i
148
+ device = device.next
149
+ end
150
+ return nil
151
+ end
152
+ end
153
+
154
+ # :stopdoc:
155
+ # http://www.usb.org/developers/defined_class
156
+ CLASS_CODES = [
157
+ [0x01, nil, nil, "Audio"],
158
+ [0x02, nil, nil, "Comm"],
159
+ [0x03, nil, nil, "HID"],
160
+ [0x05, nil, nil, "Physical"],
161
+ [0x06, 0x01, 0x01, "StillImaging"],
162
+ [0x06, nil, nil, "Image"],
163
+ [0x07, nil, nil, "Printer"],
164
+ [0x08, 0x01, nil, "MassStorage RBC Bluk-Only"],
165
+ [0x08, 0x02, 0x50, "MassStorage ATAPI Bluk-Only"],
166
+ [0x08, 0x03, 0x50, "MassStorage QIC-157 Bluk-Only"],
167
+ [0x08, 0x04, nil, "MassStorage UFI"],
168
+ [0x08, 0x05, 0x50, "MassStorage SFF-8070i Bluk-Only"],
169
+ [0x08, 0x06, 0x50, "MassStorage SCSI Bluk-Only"],
170
+ [0x08, nil, nil, "MassStorage"],
171
+ [0x09, 0x00, 0x00, "Full speed Hub"],
172
+ [0x09, 0x00, 0x01, "Hi-speed Hub with single TT"],
173
+ [0x09, 0x00, 0x02, "Hi-speed Hub with multiple TTs"],
174
+ [0x09, nil, nil, "Hub"],
175
+ [0x0a, nil, nil, "CDC"],
176
+ [0x0b, nil, nil, "SmartCard"],
177
+ [0x0d, 0x00, 0x00, "ContentSecurity"],
178
+ [0x0e, nil, nil, "Video"],
179
+ [0xdc, 0x01, 0x01, "Diagnostic USB2"],
180
+ [0xdc, nil, nil, "Diagnostic"],
181
+ [0xe0, 0x01, 0x01, "Bluetooth"],
182
+ [0xe0, 0x01, 0x02, "UWB"],
183
+ [0xe0, 0x01, 0x03, "RemoteNDIS"],
184
+ [0xe0, 0x02, 0x01, "Host Wire Adapter Control/Data"],
185
+ [0xe0, 0x02, 0x02, "Device Wire Adapter Control/Data"],
186
+ [0xe0, 0x02, 0x03, "Device Wire Adapter Isochronous"],
187
+ [0xe0, nil, nil, "Wireless Controller"],
188
+ [0xef, 0x01, 0x01, "Active Sync"],
189
+ [0xef, 0x01, 0x02, "Palm Sync"],
190
+ [0xef, 0x02, 0x01, "Interface Association Descriptor"],
191
+ [0xef, 0x02, 0x02, "Wire Adapter Multifunction Peripheral"],
192
+ [0xef, 0x03, 0x01, "Cable Based Association Framework"],
193
+ [0xef, nil, nil, "Miscellaneous"],
194
+ [0xfe, 0x01, 0x01, "Device Firmware Upgrade"],
195
+ [0xfe, 0x02, 0x00, "IRDA Bridge"],
196
+ [0xfe, 0x03, 0x00, "USB Test and Measurement"],
197
+ [0xfe, 0x03, 0x01, "USB Test and Measurement (USBTMC USB488)"],
198
+ [0xfe, nil, nil, "Application Specific"],
199
+ [0xff, nil, nil, "Vendor specific"],
200
+ ]
201
+ CLASS_CODES_HASH1 = {}
202
+ CLASS_CODES_HASH2 = {}
203
+ CLASS_CODES_HASH3 = {}
204
+ CLASS_CODES.each {|base_class, sub_class, protocol, desc|
205
+ if protocol
206
+ CLASS_CODES_HASH3[[base_class, sub_class, protocol]] = desc
207
+ elsif sub_class
208
+ CLASS_CODES_HASH2[[base_class, sub_class]] = desc
209
+ else
210
+ CLASS_CODES_HASH1[base_class] = desc
211
+ end
212
+ }
213
+
214
+ def USB.dev_string(base_class, sub_class, protocol)
215
+ if desc = CLASS_CODES_HASH3[[base_class, sub_class, protocol]]
216
+ desc
217
+ elsif desc = CLASS_CODES_HASH2[[base_class, sub_class]]
218
+ desc + " (%02x)" % [protocol]
219
+ elsif desc = CLASS_CODES_HASH1[base_class]
220
+ desc + " (%02x,%02x)" % [sub_class, protocol]
221
+ else
222
+ "Unkonwn(%02x,%02x,%02x)" % [base_class, sub_class, protocol]
223
+ end
224
+ end
225
+ # :startdoc:
226
+
227
+ class Device
228
+ def inspect
229
+ if self.revoked?
230
+ "\#<#{self.class} revoked>"
231
+ else
232
+ attrs = []
233
+ attrs << "#{self.bus.dirname}/#{self.filename}"
234
+ attrs << ("%04x:%04x" % [self.idVendor, self.idProduct])
235
+ attrs << self.manufacturer
236
+ attrs << self.product
237
+ attrs << self.serial_number
238
+ if self.bDeviceClass == USB::USB_CLASS_PER_INTERFACE
239
+ devclass = self.settings.map {|i|
240
+ USB.dev_string(i.bInterfaceClass, i.bInterfaceSubClass, i.bInterfaceProtocol)
241
+ }.join(", ")
242
+ else
243
+ devclass = USB.dev_string(self.bDeviceClass, self.bDeviceSubClass, self.bDeviceProtocol)
244
+ end
245
+ attrs << "(#{devclass})"
246
+ attrs.compact!
247
+ "\#<#{self.class} #{attrs.join(' ')}>"
248
+ end
249
+ end
250
+
251
+ def manufacturer
252
+ return @manufacturer if defined? @manufacturer
253
+ @manufacturer = self.open {|h| h.get_string_simple(self.iManufacturer) }
254
+ @manufacturer.strip! if @manufacturer
255
+ @manufacturer
256
+ end
257
+
258
+ def product
259
+ return @product if defined? @product
260
+ @product = self.open {|h| h.get_string_simple(self.iProduct) }
261
+ @product.strip! if @product
262
+ @product
263
+ end
264
+
265
+ def serial_number
266
+ return @serial_number if defined? @serial_number
267
+ @serial_number = self.open {|h| h.get_string_simple(self.iSerialNumber) }
268
+ @serial_number.strip! if @serial_number
269
+ @serial_number
270
+ end
271
+
272
+ def open
273
+ h = self.usb_open
274
+ if block_given?
275
+ begin
276
+ r = yield h
277
+ ensure
278
+ h.usb_close
279
+ end
280
+ else
281
+ h
282
+ end
283
+ end
284
+
285
+ def interfaces() self.configurations.map {|d| d.interfaces }.flatten end
286
+ def settings() self.interfaces.map {|d| d.settings }.flatten end
287
+ def endpoints() self.settings.map {|d| d.endpoints }.flatten end
288
+ end
289
+
290
+ class Configuration
291
+ def inspect
292
+ if self.revoked?
293
+ "\#<#{self.class} revoked>"
294
+ else
295
+ attrs = []
296
+ attrs << self.bConfigurationValue.to_s
297
+ bits = self.bmAttributes
298
+ attrs << "SelfPowered" if (bits & 0b1000000) != 0
299
+ attrs << "RemoteWakeup" if (bits & 0b100000) != 0
300
+ desc = self.description
301
+ attrs << desc if desc != '?'
302
+ "\#<#{self.class} #{attrs.join(' ')}>"
303
+ end
304
+ end
305
+
306
+ def description
307
+ return @description if defined? @description
308
+ @description = self.device.open {|h| h.get_string_simple(self.iConfiguration) }
309
+ end
310
+
311
+ def bus() self.device.bus end
312
+
313
+ def settings() self.interfaces.map {|d| d.settings }.flatten end
314
+ def endpoints() self.settings.map {|d| d.endpoints }.flatten end
315
+ end
316
+
317
+ class Interface
318
+ def inspect
319
+ if self.revoked?
320
+ "\#<#{self.class} revoked>"
321
+ else
322
+ "\#<#{self.class}>"
323
+ end
324
+ end
325
+
326
+ def bus() self.configuration.device.bus end
327
+ def device() self.configuration.device end
328
+
329
+ def endpoints() self.settings.map {|d| d.endpoints }.flatten end
330
+ end
331
+
332
+ class Setting
333
+ def inspect
334
+ if self.revoked?
335
+ "\#<#{self.class} revoked>"
336
+ else
337
+ attrs = []
338
+ attrs << self.bAlternateSetting.to_s
339
+ devclass = USB.dev_string(self.bInterfaceClass, self.bInterfaceSubClass, self.bInterfaceProtocol)
340
+ attrs << devclass
341
+ desc = self.description
342
+ attrs << desc if desc != '?'
343
+ "\#<#{self.class} #{attrs.join(' ')}>"
344
+ end
345
+ end
346
+
347
+ def description
348
+ return @description if defined? @description
349
+ @description = self.device.open {|h| h.get_string_simple(self.iInterface) }
350
+ end
351
+
352
+ def bus() self.interface.configuration.device.bus end
353
+ def device() self.interface.configuration.device end
354
+ def configuration() self.interface.configuration end
355
+ end
356
+
357
+ class Endpoint
358
+ def inspect
359
+ if self.revoked?
360
+ "\#<#{self.class} revoked>"
361
+ else
362
+ endpoint_address = self.bEndpointAddress
363
+ num = endpoint_address & 0b00001111
364
+ inout = (endpoint_address & 0b10000000) == 0 ? "OUT" : "IN "
365
+ bits = self.bmAttributes
366
+ transfer_type = %w[Control Isochronous Bulk Interrupt][0b11 & bits]
367
+ type = [transfer_type]
368
+ if transfer_type == 'Isochronous'
369
+ synchronization_type = %w[NoSynchronization Asynchronous Adaptive Synchronous][(0b1100 & bits) >> 2]
370
+ usage_type = %w[Data Feedback ImplicitFeedback ?][(0b110000 & bits) >> 4]
371
+ type << synchronization_type << usage_type
372
+ end
373
+ "\#<#{self.class} #{num} #{inout} #{type.join(" ")}>"
374
+ end
375
+ end
376
+
377
+ def bus() self.setting.interface.configuration.device.bus end
378
+ def device() self.setting.interface.configuration.device end
379
+ def configuration() self.setting.interface.configuration end
380
+ def interface() self.setting.interface end
381
+ end
382
+
383
+ class DevHandle
384
+ def set_configuration(configuration)
385
+ configuration = configuration.bConfigurationValue if configuration.respond_to? :bConfigurationValue
386
+ self.usb_set_configuration(configuration)
387
+ end
388
+
389
+ def set_altinterface(alternate)
390
+ alternate = alternate.bAlternateSetting if alternate.respond_to? :bAlternateSetting
391
+ self.usb_set_altinterface(alternate)
392
+ end
393
+
394
+ def clear_halt(ep)
395
+ ep = ep.bEndpointAddress if ep.respond_to? :bEndpointAddress
396
+ self.usb_clear_halt(ep)
397
+ end
398
+
399
+ def claim_interface(interface)
400
+ interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
401
+ self.usb_claim_interface(interface)
402
+ end
403
+
404
+ def release_interface(interface)
405
+ interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
406
+ self.usb_release_interface(interface)
407
+ end
408
+
409
+ def get_string_simple(index)
410
+ result = "\0" * 1024
411
+ begin
412
+ self.usb_get_string_simple(index, result)
413
+ rescue Errno::EPIPE, Errno::EFBIG, Errno::EPERM
414
+ return nil
415
+ end
416
+ result.delete!("\0")
417
+ result
418
+ end
419
+ end
420
+ end
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # usage: usb-power bus/device port on|off
4
+ #
5
+ # example:
6
+ # usb-power 004/006 2 on
7
+ # usb-power 004/006 2 off
8
+
9
+ require 'usb'
10
+ require 'optparse'
11
+
12
+ USB_RT_PORT = USB::USB_TYPE_CLASS | USB::USB_RECIP_OTHER
13
+ USB_PORT_FEAT_POWER = 8
14
+
15
+ def list_usb2_hub
16
+ USB.devices.find_all {|d|
17
+ 0x200 <= d.bcdDevice &&
18
+ d.bDeviceClass == USB::USB_CLASS_HUB
19
+ }
20
+ end
21
+
22
+ require 'pp'
23
+
24
+ def power_on(h, port)
25
+ h.usb_control_msg(USB_RT_PORT, USB::USB_REQ_SET_FEATURE, USB_PORT_FEAT_POWER, port, "", 0)
26
+ end
27
+
28
+ def power_off(h, port)
29
+ h.usb_control_msg(USB_RT_PORT, USB::USB_REQ_CLEAR_FEATURE, USB_PORT_FEAT_POWER, port, "", 0)
30
+ end
31
+
32
+ bus_device = ARGV.shift
33
+ port = ARGV.shift.to_i
34
+ on_off = ARGV.shift
35
+
36
+ %r{/} =~ bus_device
37
+ bus = $`.to_i
38
+ device = $'.to_i
39
+
40
+ USB.find_bus(bus).find_device(device).open {|h|
41
+ if on_off == 'off'
42
+ power_off(h, port)
43
+ else
44
+ power_on(h, port)
45
+ end
46
+ }