libusb 0.1.0-x86-mingw32
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.
- data/.autotest +23 -0
- data/.gemtest +0 -0
- data/COPYING +165 -0
- data/History.txt +15 -0
- data/Manifest.txt +15 -0
- data/README.rdoc +110 -0
- data/Rakefile +144 -0
- data/lib/libusb/compat.rb +370 -0
- data/lib/libusb-1.0.dll +0 -0
- data/lib/libusb.rb +1523 -0
- data/test/test_libusb_compat.rb +78 -0
- data/test/test_libusb_compat_mass_storage.rb +81 -0
- data/test/test_libusb_descriptors.rb +122 -0
- data/test/test_libusb_gc.rb +37 -0
- data/test/test_libusb_iso_transfer.rb +45 -0
- data/test/test_libusb_mass_storage.rb +271 -0
- metadata +107 -0
@@ -0,0 +1,78 @@
|
|
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 "test/unit"
|
17
|
+
require "libusb/compat"
|
18
|
+
|
19
|
+
class TestLibusbCompat < Test::Unit::TestCase
|
20
|
+
include USB
|
21
|
+
|
22
|
+
attr_accessor :usb
|
23
|
+
|
24
|
+
def test_find
|
25
|
+
devlist = USB.devices
|
26
|
+
assert_kind_of Array, devlist, "Bus#find should return an Array"
|
27
|
+
assert_kind_of Device, devlist.first, "Bus#find should return Devices"
|
28
|
+
devlist.each do |dev|
|
29
|
+
assert_match( /Device/, dev.inspect, "Device#inspect should work")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_constants
|
34
|
+
assert_equal 7, USB_CLASS_PRINTER, "Printer class id should be defined"
|
35
|
+
assert_equal 32, USB_TYPE_CLASS, "type class should be defined"
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_descriptors
|
39
|
+
USB.devices.each do |dev|
|
40
|
+
assert_match(/Device/, dev.inspect, "Device#inspect should work")
|
41
|
+
dev.configurations.each do |config_desc|
|
42
|
+
assert_match(/Configuration/, config_desc.inspect, "Configuration#inspect should work")
|
43
|
+
assert dev.configurations.include?(config_desc), "Device#configurations should include this one"
|
44
|
+
|
45
|
+
config_desc.interfaces.each do |interface|
|
46
|
+
assert_match(/Interface/, interface.inspect, "Interface#inspect should work")
|
47
|
+
|
48
|
+
assert dev.interfaces.include?(interface), "Device#interfaces should include this one"
|
49
|
+
assert config_desc.interfaces.include?(interface), "Configuration#interfaces should include this one"
|
50
|
+
|
51
|
+
interface.settings.each do |if_desc|
|
52
|
+
assert_match(/Setting/, if_desc.inspect, "Setting#inspect should work")
|
53
|
+
|
54
|
+
assert dev.settings.include?(if_desc), "Device#settings should include this one"
|
55
|
+
assert config_desc.settings.include?(if_desc), "Configuration#settings should include this one"
|
56
|
+
assert interface.settings.include?(if_desc), "Interface#settings should include this one"
|
57
|
+
|
58
|
+
if_desc.endpoints.each do |ep|
|
59
|
+
assert_match(/Endpoint/, ep.inspect, "Endpoint#inspect should work")
|
60
|
+
|
61
|
+
assert dev.endpoints.include?(ep), "Device#endpoints should include this one"
|
62
|
+
assert config_desc.endpoints.include?(ep), "Configuration#endpoints should include this one"
|
63
|
+
assert interface.endpoints.include?(ep), "Interface#endpoints should include this one"
|
64
|
+
assert if_desc.endpoints.include?(ep), "Setting#endpoints should include this one"
|
65
|
+
|
66
|
+
assert_equal if_desc, ep.setting, "backref should be correct"
|
67
|
+
assert_equal interface, ep.interface, "backref should be correct"
|
68
|
+
assert_equal config_desc, ep.configuration, "backref should be correct"
|
69
|
+
assert_equal dev, ep.device, "backref should be correct"
|
70
|
+
|
71
|
+
assert_operator 0, :<, ep.wMaxPacketSize, "packet size should be > 0"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,81 @@
|
|
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 "test/unit"
|
17
|
+
require "libusb/compat"
|
18
|
+
|
19
|
+
class TestLibusbCompat < Test::Unit::TestCase
|
20
|
+
include USB
|
21
|
+
|
22
|
+
attr_accessor :devh
|
23
|
+
|
24
|
+
BOMS_RESET = 0xFF
|
25
|
+
BOMS_GET_MAX_LUN = 0xFE
|
26
|
+
|
27
|
+
def setup
|
28
|
+
devs = []
|
29
|
+
USB.each_device_by_class(USB_CLASS_MASS_STORAGE, 0x01, 0x50){|dev| devs << dev }
|
30
|
+
USB.each_device_by_class(USB_CLASS_MASS_STORAGE, 0x06, 0x50){|dev| devs << dev }
|
31
|
+
|
32
|
+
dev = devs.last
|
33
|
+
abort "no mass storage device found" unless dev
|
34
|
+
devh = dev.open
|
35
|
+
if RUBY_PLATFORM=~/linux/i
|
36
|
+
data = " "*1000
|
37
|
+
begin
|
38
|
+
devh.usb_get_driver_np 0, data
|
39
|
+
rescue Errno::ENODATA
|
40
|
+
data = "nodata exception".ljust(1000, "\0")
|
41
|
+
end
|
42
|
+
assert_match(/\w+/, data, "There should be a driver or an exception")
|
43
|
+
begin
|
44
|
+
# second param is needed because of a bug in ruby-usb
|
45
|
+
devh.usb_detach_kernel_driver_np 0, 123
|
46
|
+
rescue RuntimeError => e
|
47
|
+
assert_match(/ERROR_NOT_FOUND/, e.to_s, "Raise proper exception, if no kernel driver is active")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
endpoint_in = dev.endpoints.find{|ep| ep.bEndpointAddress&USB_ENDPOINT_IN != 0 }
|
52
|
+
endpoint_out = dev.endpoints.find{|ep| ep.bEndpointAddress&USB_ENDPOINT_IN == 0 }
|
53
|
+
|
54
|
+
devh.set_configuration 1
|
55
|
+
devh.set_configuration dev.configurations.first
|
56
|
+
devh.claim_interface dev.settings.first
|
57
|
+
devh.clear_halt(endpoint_in)
|
58
|
+
devh.clear_halt(endpoint_out.bEndpointAddress)
|
59
|
+
@devh = devh
|
60
|
+
end
|
61
|
+
|
62
|
+
def teardown
|
63
|
+
if devh
|
64
|
+
devh.release_interface 0
|
65
|
+
devh.usb_close
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_mass_storage_reset
|
70
|
+
res = devh.usb_control_msg(USB_ENDPOINT_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE,
|
71
|
+
BOMS_RESET, 0, 0, "", 2000)
|
72
|
+
assert_equal 0, res, "BOMS_RESET response should be 0 byte"
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_read_max_lun
|
76
|
+
bytes = [].pack('x1')
|
77
|
+
devh.usb_control_msg(USB_ENDPOINT_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE,
|
78
|
+
BOMS_GET_MAX_LUN, 0, 0, bytes, 100)
|
79
|
+
assert [0].pack("C")==bytes || [1].pack("C")==bytes, "BOMS_GET_MAX_LUN response is usually 0 or 1"
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,122 @@
|
|
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 "test/unit"
|
17
|
+
require "libusb"
|
18
|
+
|
19
|
+
class TestLibusbDescriptors < Test::Unit::TestCase
|
20
|
+
include LIBUSB
|
21
|
+
|
22
|
+
attr_accessor :usb
|
23
|
+
|
24
|
+
def setup
|
25
|
+
@usb = Context.new
|
26
|
+
@usb.debug = 0
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_descriptors
|
30
|
+
usb.devices.each do |dev|
|
31
|
+
assert_match(/Device/, dev.inspect, "Device#inspect should work")
|
32
|
+
dev.configurations.each do |config_desc|
|
33
|
+
assert_match(/Configuration/, config_desc.inspect, "ConfigDescriptor#inspect should work")
|
34
|
+
assert dev.configurations.include?(config_desc), "Device#configurations should include this one"
|
35
|
+
|
36
|
+
config_desc.interfaces.each do |interface|
|
37
|
+
assert_match(/Interface/, interface.inspect, "Interface#inspect should work")
|
38
|
+
|
39
|
+
assert dev.interfaces.include?(interface), "Device#interfaces should include this one"
|
40
|
+
assert config_desc.interfaces.include?(interface), "ConfigDescriptor#interfaces should include this one"
|
41
|
+
|
42
|
+
interface.alt_settings.each do |if_desc|
|
43
|
+
assert_match(/Setting/, if_desc.inspect, "InterfaceDescriptor#inspect should work")
|
44
|
+
|
45
|
+
assert dev.settings.include?(if_desc), "Device#settings should include this one"
|
46
|
+
assert config_desc.settings.include?(if_desc), "ConfigDescriptor#settings should include this one"
|
47
|
+
assert interface.alt_settings.include?(if_desc), "Inteerface#alt_settings should include this one"
|
48
|
+
|
49
|
+
if_desc.endpoints.each do |ep|
|
50
|
+
assert_match(/Endpoint/, ep.inspect, "EndpointDescriptor#inspect should work")
|
51
|
+
|
52
|
+
assert dev.endpoints.include?(ep), "Device#endpoints should include this one"
|
53
|
+
assert config_desc.endpoints.include?(ep), "ConfigDescriptor#endpoints should include this one"
|
54
|
+
assert interface.endpoints.include?(ep), "Inteerface#endpoints should include this one"
|
55
|
+
assert if_desc.endpoints.include?(ep), "InterfaceDescriptor#endpoints should include this one"
|
56
|
+
|
57
|
+
assert_equal if_desc, ep.setting, "backref should be correct"
|
58
|
+
assert_equal interface, ep.interface, "backref should be correct"
|
59
|
+
assert_equal config_desc, ep.configuration, "backref should be correct"
|
60
|
+
assert_equal dev, ep.device, "backref should be correct"
|
61
|
+
|
62
|
+
assert_operator 0, :<, ep.wMaxPacketSize, "packet size should be > 0"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_constants
|
71
|
+
assert_equal 7, CLASS_PRINTER, "Printer class id should be defined"
|
72
|
+
assert_equal 48, ISO_USAGE_TYPE_MASK, "iso usage type should be defined"
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_device_filter_mass_storages
|
76
|
+
devs1 = []
|
77
|
+
usb.devices.each do |dev|
|
78
|
+
dev.settings.each do |if_desc|
|
79
|
+
if if_desc.bInterfaceClass == CLASS_MASS_STORAGE &&
|
80
|
+
( if_desc.bInterfaceSubClass == 0x01 ||
|
81
|
+
if_desc.bInterfaceSubClass == 0x06 ) &&
|
82
|
+
if_desc.bInterfaceProtocol == 0x50
|
83
|
+
|
84
|
+
devs1 << dev
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
devs2 = usb.devices( :bClass=>CLASS_MASS_STORAGE, :bSubClass=>0x01, :bProtocol=>0x50 )
|
90
|
+
devs2 += usb.devices( :bClass=>CLASS_MASS_STORAGE, :bSubClass=>0x06, :bProtocol=>0x50 )
|
91
|
+
assert_equal devs1.sort, devs2.sort, "devices and devices with filter should deliver the same device"
|
92
|
+
|
93
|
+
devs3 = usb.devices( :bClass=>[CLASS_MASS_STORAGE], :bSubClass=>[0x01,0x06], :bProtocol=>[0x50] )
|
94
|
+
assert_equal devs1.sort, devs3.sort, "devices and devices with array-filter should deliver the same device"
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_device_filter_hubs
|
98
|
+
devs1 = []
|
99
|
+
usb.devices.each do |dev|
|
100
|
+
dev.settings.each do |if_desc|
|
101
|
+
if if_desc.bInterfaceClass == CLASS_HUB
|
102
|
+
devs1 << dev
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
devs2 = usb.devices( :bClass=>CLASS_HUB )
|
108
|
+
assert_equal devs1.sort, devs2.sort, "devices and devices with filter should deliver the same device"
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_device_methods
|
112
|
+
usb.devices.each do |dev|
|
113
|
+
ep = dev.endpoints.first
|
114
|
+
if ep
|
115
|
+
assert_operator dev.max_packet_size(ep), :>, 0, "#{dev.inspect} should have a usable packet size"
|
116
|
+
assert_operator dev.max_packet_size(ep.bEndpointAddress), :>, 0, "#{dev.inspect} should have a usable packet size"
|
117
|
+
assert_operator dev.max_iso_packet_size(ep), :>, 0, "#{dev.inspect} should have a usable iso packet size"
|
118
|
+
assert_operator dev.max_iso_packet_size(ep.bEndpointAddress), :>, 0, "#{dev.inspect} should have a usable iso packet size"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,37 @@
|
|
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
|
+
# These tests should be started with valgrind to check for
|
17
|
+
# invalid memmory access.
|
18
|
+
|
19
|
+
require "test/unit"
|
20
|
+
require "libusb"
|
21
|
+
|
22
|
+
class TestLibusbGc < Test::Unit::TestCase
|
23
|
+
include LIBUSB
|
24
|
+
|
25
|
+
def get_some_endpoint
|
26
|
+
Context.new.devices.each do |dev|
|
27
|
+
return dev.endpoints.last unless dev.endpoints.empty?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_descriptors
|
32
|
+
ep = get_some_endpoint
|
33
|
+
ps = ep.wMaxPacketSize
|
34
|
+
GC.start
|
35
|
+
assert_equal ps, ep.wMaxPacketSize, "GC should not free EndpointDescriptor"
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,45 @@
|
|
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 "test/unit"
|
17
|
+
require "libusb"
|
18
|
+
|
19
|
+
class TestLibusbIsoTransfer < Test::Unit::TestCase
|
20
|
+
include LIBUSB
|
21
|
+
|
22
|
+
def setup
|
23
|
+
c = Context.new
|
24
|
+
@dev = c.devices.first.open
|
25
|
+
end
|
26
|
+
|
27
|
+
def teardown
|
28
|
+
@dev.close if @dev
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_iso_transfer
|
32
|
+
tr = IsochronousTransfer.new 10, :dev_handle=>@dev
|
33
|
+
assert_equal 10, tr.num_packets, "number of packets should match"
|
34
|
+
|
35
|
+
tr.buffer = " "*130
|
36
|
+
tr.packet_lengths = 13
|
37
|
+
tr[7].length = 12
|
38
|
+
assert_equal 12, tr[7].length, "packet length should be set"
|
39
|
+
assert_equal 13, tr[8].length, "packet length should be set"
|
40
|
+
|
41
|
+
assert_raise(LIBUSB::ERROR_IO, "the randomly choosen device will probably not handle iso transfer") do
|
42
|
+
tr.submit!
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,271 @@
|
|
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
|
+
# This test requires a connected, but not mounted mass storage device with
|
17
|
+
# read/write access allowed. Based on the following specifications:
|
18
|
+
# http://www.usb.org/developers/devclass_docs/usbmassbulk_10.pdf
|
19
|
+
# http://en.wikipedia.org/wiki/SCSI_command
|
20
|
+
#
|
21
|
+
|
22
|
+
require "test/unit"
|
23
|
+
require "libusb"
|
24
|
+
|
25
|
+
class TestLibusbMassStorage < Test::Unit::TestCase
|
26
|
+
include LIBUSB
|
27
|
+
|
28
|
+
class CSWError < RuntimeError; end
|
29
|
+
BOMS_RESET = 0xFF
|
30
|
+
BOMS_GET_MAX_LUN = 0xFE
|
31
|
+
|
32
|
+
attr_accessor :usb
|
33
|
+
attr_accessor :device
|
34
|
+
attr_accessor :dev
|
35
|
+
attr_accessor :endpoint_in
|
36
|
+
attr_accessor :endpoint_out
|
37
|
+
|
38
|
+
def setup
|
39
|
+
@usb = Context.new
|
40
|
+
@usb.debug = 3
|
41
|
+
@asynchron = false
|
42
|
+
|
43
|
+
@device = usb.devices( :bClass=>CLASS_MASS_STORAGE, :bSubClass=>[0x06,0x01], :bProtocol=>0x50 ).last
|
44
|
+
abort "no mass storage device found" unless @device
|
45
|
+
|
46
|
+
@endpoint_in = @device.endpoints.find{|ep| ep.bEndpointAddress&ENDPOINT_IN != 0 }
|
47
|
+
@endpoint_out = @device.endpoints.find{|ep| ep.bEndpointAddress&ENDPOINT_IN == 0 }
|
48
|
+
@dev = @device.open
|
49
|
+
|
50
|
+
if RUBY_PLATFORM=~/linux/i && dev.kernel_driver_active?(0)
|
51
|
+
dev.detach_kernel_driver(0)
|
52
|
+
end
|
53
|
+
dev.claim_interface(0)
|
54
|
+
|
55
|
+
# clear any pending data
|
56
|
+
dev.clear_halt(endpoint_in)
|
57
|
+
end
|
58
|
+
|
59
|
+
def teardown
|
60
|
+
dev.release_interface(0) if dev
|
61
|
+
dev.close if dev
|
62
|
+
end
|
63
|
+
|
64
|
+
def do_transfer(method, args)
|
65
|
+
if @asynchron
|
66
|
+
stop = false
|
67
|
+
transfer = dev.send(method, args) do |tr|
|
68
|
+
stop = true
|
69
|
+
assert_equal transfer, tr, "block argument should be the transfer instance"
|
70
|
+
# p transfer.status
|
71
|
+
end
|
72
|
+
|
73
|
+
transfer.submit
|
74
|
+
usb.handle_events
|
75
|
+
until stop
|
76
|
+
sleep 0.001
|
77
|
+
usb.handle_events
|
78
|
+
end
|
79
|
+
transfer.result
|
80
|
+
else
|
81
|
+
dev.send(method, args)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
def control_transfer(args)
|
85
|
+
do_transfer(:control_transfer, args)
|
86
|
+
end
|
87
|
+
def bulk_transfer(args)
|
88
|
+
do_transfer(:bulk_transfer, args)
|
89
|
+
end
|
90
|
+
|
91
|
+
def send_mass_storage_command(cdb, data_length, direction=ENDPOINT_IN)
|
92
|
+
@tag ||= 0
|
93
|
+
@tag += 1
|
94
|
+
expected_tag = @tag
|
95
|
+
lun = 0
|
96
|
+
|
97
|
+
cbw = ['USBC', expected_tag, data_length, direction, lun, cdb.length, cdb].pack('a*VVCCCa*')
|
98
|
+
cbw = cbw.ljust(31, "\0")
|
99
|
+
|
100
|
+
num_bytes = bulk_transfer(:endpoint=>endpoint_out, :dataOut=>cbw)
|
101
|
+
assert_equal 31, num_bytes, "31 bytes CBW should be sent"
|
102
|
+
|
103
|
+
recv = bulk_transfer(:endpoint=>endpoint_in, :dataIn=>data_length)
|
104
|
+
|
105
|
+
get_mass_storage_status(expected_tag)
|
106
|
+
return recv
|
107
|
+
end
|
108
|
+
|
109
|
+
def get_mass_storage_status(expected_tag)
|
110
|
+
retries = 5
|
111
|
+
buffer = begin
|
112
|
+
bulk_transfer(:endpoint=>endpoint_in, :dataIn=>13)
|
113
|
+
rescue LIBUSB::ERROR_PIPE => err
|
114
|
+
if (retries-=1)>=0
|
115
|
+
dev.clear_halt(endpoint_in)
|
116
|
+
retry
|
117
|
+
end
|
118
|
+
raise
|
119
|
+
end
|
120
|
+
assert_equal 13, buffer.bytesize, "CSW should be 13 bytes long"
|
121
|
+
|
122
|
+
dCSWSignature, dCSWTag, dCSWDataResidue, bCSWStatus = buffer.unpack('a4VVC')
|
123
|
+
|
124
|
+
assert_equal 'USBS', dCSWSignature, "CSW should start with USBS"
|
125
|
+
assert_equal expected_tag, dCSWTag, "CSW-tag should be like CBW-tag"
|
126
|
+
raise CSWError, "CSW returned error #{bCSWStatus}" unless bCSWStatus==0
|
127
|
+
buffer
|
128
|
+
end
|
129
|
+
|
130
|
+
def send_inquiry
|
131
|
+
expected_length = 0x24 # INQUIRY_LENGTH
|
132
|
+
cdb = [ 0x12, 0, 0, # Inquiry
|
133
|
+
expected_length, 0,
|
134
|
+
].pack('CCCnC')
|
135
|
+
|
136
|
+
send_mass_storage_command( cdb, expected_length )
|
137
|
+
end
|
138
|
+
|
139
|
+
def get_capacity
|
140
|
+
expected_length = 0x08 # READ_CAPACITY_LENGTH
|
141
|
+
cdb = [ 0x25, # Read Capacity
|
142
|
+
].pack('Cx9')
|
143
|
+
|
144
|
+
cap = send_mass_storage_command( cdb, expected_length )
|
145
|
+
|
146
|
+
max_lba, block_size = cap.unpack('NN')
|
147
|
+
device_size = (max_lba + 1) * block_size / (1024*1024*1024.0);
|
148
|
+
printf(" Max LBA: %08X, Block Size: %08X (%.2f GB)\n", max_lba, block_size, device_size);
|
149
|
+
end
|
150
|
+
|
151
|
+
def read_block(start, nr_blocks)
|
152
|
+
expected_length = 0x200 * nr_blocks
|
153
|
+
cdb = [ 0x28, 0, # Read(10)
|
154
|
+
start, 0,
|
155
|
+
nr_blocks, 0,
|
156
|
+
].pack('CCNCnC')
|
157
|
+
data = send_mass_storage_command( cdb, expected_length )
|
158
|
+
end
|
159
|
+
|
160
|
+
def invalid_command
|
161
|
+
expected_length = 0x100
|
162
|
+
cdb = [ 0x26, 0, # invalid command
|
163
|
+
].pack('CC')
|
164
|
+
data = send_mass_storage_command( cdb, expected_length )
|
165
|
+
end
|
166
|
+
|
167
|
+
def mass_storage_reset
|
168
|
+
res = control_transfer(
|
169
|
+
:bmRequestType=>ENDPOINT_OUT|REQUEST_TYPE_CLASS|RECIPIENT_INTERFACE,
|
170
|
+
:bRequest=>BOMS_RESET,
|
171
|
+
:wValue=>0, :wIndex=>0)
|
172
|
+
assert_equal 0, res, "BOMS_RESET response should be 0 byte"
|
173
|
+
|
174
|
+
res = control_transfer(
|
175
|
+
:bmRequestType=>ENDPOINT_OUT|REQUEST_TYPE_CLASS|RECIPIENT_INTERFACE,
|
176
|
+
:bRequest=>BOMS_RESET,
|
177
|
+
:wValue=>0, :wIndex=>0, :dataOut=>'')
|
178
|
+
assert_equal 0, res, "BOMS_RESET response should be 0 byte"
|
179
|
+
end
|
180
|
+
|
181
|
+
def read_max_lun
|
182
|
+
res = control_transfer(
|
183
|
+
:bmRequestType=>ENDPOINT_IN|REQUEST_TYPE_CLASS|RECIPIENT_INTERFACE,
|
184
|
+
:bRequest=>BOMS_GET_MAX_LUN,
|
185
|
+
:wValue=>0, :wIndex=>0, :dataIn=>1)
|
186
|
+
assert [0].pack("C")==res || [1].pack("C")==res, "BOMS_GET_MAX_LUN response is usually 0 or 1"
|
187
|
+
end
|
188
|
+
|
189
|
+
def test_read_access
|
190
|
+
send_inquiry
|
191
|
+
get_capacity
|
192
|
+
|
193
|
+
data = read_block(0, 1)
|
194
|
+
assert_equal 512, data.length, "Read block should be 512 bytes"
|
195
|
+
|
196
|
+
# closing device handle shouldn't matter, in the meantime
|
197
|
+
dev.close
|
198
|
+
@dev = @device.open
|
199
|
+
dev.claim_interface(0)
|
200
|
+
|
201
|
+
data = read_block(0, 2)
|
202
|
+
assert_equal 1024, data.length, "Read block should be 1024 bytes"
|
203
|
+
end
|
204
|
+
# def test_read_access_async
|
205
|
+
# @asynchron = true
|
206
|
+
# test_read_access
|
207
|
+
# end
|
208
|
+
|
209
|
+
def test_read_failed
|
210
|
+
count = 0
|
211
|
+
th = Thread.new do
|
212
|
+
loop do
|
213
|
+
count+=1
|
214
|
+
sleep 0.01
|
215
|
+
end
|
216
|
+
end
|
217
|
+
assert_raise(CSWError, LIBUSB::ERROR_TIMEOUT) do
|
218
|
+
invalid_command
|
219
|
+
end
|
220
|
+
th.kill
|
221
|
+
dev.clear_halt(endpoint_in)
|
222
|
+
dev.clear_halt(endpoint_out)
|
223
|
+
assert_operator 20, :<=, count, "libusb_handle_events should not block a parallel Thread"
|
224
|
+
end
|
225
|
+
# def test_read_failed_async
|
226
|
+
# @asynchron = true
|
227
|
+
# test_read_failed
|
228
|
+
# end
|
229
|
+
|
230
|
+
def test_max_lun
|
231
|
+
read_max_lun
|
232
|
+
end
|
233
|
+
# def test_max_lun_async
|
234
|
+
# @asynchron = true
|
235
|
+
# read_max_lun
|
236
|
+
# end
|
237
|
+
|
238
|
+
def test_mass_storage_reset
|
239
|
+
mass_storage_reset
|
240
|
+
end
|
241
|
+
# def test_mass_storage_reset_async
|
242
|
+
# @asynchron = true
|
243
|
+
# mass_storage_reset
|
244
|
+
# end
|
245
|
+
|
246
|
+
def test_read_long
|
247
|
+
1000.times do |bl|
|
248
|
+
data = read_block(bl, 1)
|
249
|
+
assert_equal 512, data.length, "Read block should be 512 bytes"
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
def test_attach_kernel_driver
|
254
|
+
dev.release_interface(0)
|
255
|
+
if RUBY_PLATFORM=~/linux/i
|
256
|
+
dev.attach_kernel_driver(0)
|
257
|
+
assert dev.kernel_driver_active?(0), "kernel driver should be active again"
|
258
|
+
end
|
259
|
+
dev.close
|
260
|
+
@dev = nil
|
261
|
+
end
|
262
|
+
|
263
|
+
def test_wrong_argument
|
264
|
+
assert_raise(ArgumentError){ dev.bulk_transfer(:endpoint=>endpoint_in, :dataOut=>"data") }
|
265
|
+
assert_raise(ArgumentError){ dev.interrupt_transfer(:endpoint=>endpoint_in, :dataOut=>"data") }
|
266
|
+
assert_raise(ArgumentError){ dev.control_transfer(
|
267
|
+
:bmRequestType=>ENDPOINT_OUT|REQUEST_TYPE_CLASS|RECIPIENT_INTERFACE,
|
268
|
+
:bRequest=>BOMS_RESET,
|
269
|
+
:wValue=>0, :wIndex=>0, :dataIn=>123) }
|
270
|
+
end
|
271
|
+
end
|