test/py: usb: Add tests for USB device
Add the test cases for usb commands to test its various functionality
such as start, stop, reset, info, tree, storage, dev, part, ls, load,
and save. It also adds different file systems cases such as fat32, ext2
and ext4.
Signed-off-by: Love Kumar <love.kumar@amd.com>
diff --git a/test/py/tests/test_usb.py b/test/py/tests/test_usb.py
new file mode 100644
index 0000000..fb3d20f
--- /dev/null
+++ b/test/py/tests/test_usb.py
@@ -0,0 +1,626 @@
+# SPDX-License-Identifier: GPL-2.0
+# (C) Copyright 2023, Advanced Micro Devices, Inc.
+
+import pytest
+import random
+import re
+import u_boot_utils
+
+"""
+Note: This test doesn't rely on boardenv_* configuration values but it can
+change the test behavior. To test USB file system cases (fat32, ext2, ext4),
+USB device should be formatted and valid partitions should be created for
+different file system, otherwise it may leads to failure. This test will be
+skipped if the USB device is not detected.
+
+For example:
+
+# Setup env__usb_device_test_skip to not skipping the test. By default, its
+# value is set to True. Set it to False to run all tests for USB device.
+env__usb_device_test_skip = False
+"""
+
+def setup_usb(u_boot_console):
+ if u_boot_console.config.env.get('env__usb_device_test_skip', True):
+ pytest.skip('USB device test is not enabled')
+
+@pytest.mark.buildconfigspec('cmd_usb')
+def test_usb_start(u_boot_console):
+ setup_usb(u_boot_console)
+ output = u_boot_console.run_command('usb start')
+
+ # if output is empty, usb start may already run as part of preboot command
+ # re-start the usb, in that case
+ if not output:
+ u_boot_console.run_command('usb stop')
+ output = u_boot_console.run_command('usb start')
+
+ if 'No USB device found' in output:
+ pytest.skip('No USB controller available')
+
+ if 'Card did not respond to voltage select' in output:
+ pytest.skip('No USB device present')
+
+ controllers = 0
+ storage_device = 0
+ obj = re.search(r'\d USB Device\(s\) found', output)
+ controllers = int(obj.group()[0])
+
+ if not controllers:
+ pytest.skip('No USB device present')
+
+ obj = re.search(r'\d Storage Device\(s\) found', output)
+ storage_device = int(obj.group()[0])
+
+ if not storage_device:
+ pytest.skip('No USB storage device present')
+
+ assert 'USB init failed' not in output
+ assert 'starting USB...' in output
+
+ if 'Starting the controller' in output:
+ assert 'USB XHCI' in output
+
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+ return controllers, storage_device
+
+@pytest.mark.buildconfigspec('cmd_usb')
+def test_usb_stop(u_boot_console):
+ setup_usb(u_boot_console)
+ output = u_boot_console.run_command('usb stop')
+ assert 'stopping USB..' in output
+
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ output = u_boot_console.run_command('usb dev')
+ assert "USB is stopped. Please issue 'usb start' first." in output
+
+@pytest.mark.buildconfigspec('cmd_usb')
+def test_usb_reset(u_boot_console):
+ setup_usb(u_boot_console)
+ output = u_boot_console.run_command('usb reset')
+
+ if 'No USB device found' in output:
+ pytest.skip('No USB controller available')
+
+ if 'Card did not respond to voltage select' in output:
+ pytest.skip('No USB device present')
+
+ obj = re.search(r'\d USB Device\(s\) found', output)
+ usb_dev_num = int(obj.group()[0])
+
+ if not usb_dev_num:
+ pytest.skip('No USB device present')
+
+ obj = re.search(r'\d Storage Device\(s\) found', output)
+ usb_stor_num = int(obj.group()[0])
+
+ if not usb_stor_num:
+ pytest.skip('No USB storage device present')
+
+ assert 'BUG' not in output
+ assert 'USB init failed' not in output
+ assert 'resetting USB...' in output
+
+ if 'Starting the controller' in output:
+ assert 'USB XHCI' in output
+
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_usb')
+def test_usb_info(u_boot_console):
+ controllers, storage_device = test_usb_start(u_boot_console)
+ output = u_boot_console.run_command('usb info')
+
+ num_controller = len(re.findall(': Hub,', output))
+ num_mass_storage = len(re.findall(': Mass Storage,', output))
+
+ assert num_controller == controllers - 1
+ assert num_mass_storage == storage_device
+
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ for i in range(0, storage_device + controllers - 1):
+ output = u_boot_console.run_command('usb info %d' % i)
+ num_controller = len(re.findall(': Hub,', output))
+ num_mass_storage = len(re.findall(': Mass Storage,', output))
+ assert num_controller + num_mass_storage == 1
+ assert 'No device available' not in output
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_usb')
+def test_usb_tree(u_boot_console):
+ controllers, storage_device = test_usb_start(u_boot_console)
+ output = u_boot_console.run_command('usb tree')
+
+ num_controller = len(re.findall('Hub', output))
+ num_mass_storage = len(re.findall('Mass Storage', output))
+
+ assert num_controller == controllers - 1
+ assert num_mass_storage == storage_device
+
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('usb_storage')
+def test_usb_storage(u_boot_console):
+ controllers, storage_device = test_usb_start(u_boot_console)
+ output = u_boot_console.run_command('usb storage')
+
+ obj = re.findall(r'Capacity: (\d+|\d+[\.]?\d)', output)
+ devices = {}
+
+ for key in range(int(storage_device)):
+ devices[key] = {}
+
+ for x in range(int(storage_device)):
+ try:
+ capacity = float(obj[x].split()[0])
+ devices[x]['capacity'] = capacity
+ print('USB storage device %d capacity is: %g MB' % (x, capacity))
+ except ValueError:
+ pytest.fail('USB storage device capacity not recognized')
+
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_usb')
+def test_usb_dev(u_boot_console):
+ controllers, storage_device = test_usb_start(u_boot_console)
+ output = u_boot_console.run_command('usb dev')
+
+ assert 'no usb devices available' not in output
+
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ devices = {}
+
+ for key in range(int(storage_device)):
+ devices[key] = {}
+
+ fail = 0
+ for x in range(0, storage_device):
+ devices[x]['detected'] = 'yes'
+ output = u_boot_console.run_command('usb dev %d' % x)
+
+ if 'Card did not respond to voltage select' in output:
+ fail = 1
+ devices[x]['detected'] = 'no'
+
+ if 'No USB device found' in output:
+ devices[x]['detected'] = 'no'
+
+ if 'unknown device' in output:
+ devices[x]['detected'] = 'no'
+
+ assert 'is now current device' in output
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ if fail:
+ pytest.fail('USB device not present')
+
+ return devices, controllers, storage_device
+
+@pytest.mark.buildconfigspec('cmd_usb')
+def test_usb_part(u_boot_console):
+ devices, controllers, storage_device = test_usb_dev(u_boot_console)
+ if not devices:
+ pytest.skip('No devices detected')
+
+ u_boot_console.run_command('usb part')
+
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ for i in range(0, storage_device):
+ if devices[i]['detected'] == 'yes':
+ u_boot_console.run_command('usb dev %d' % i)
+ output = u_boot_console.run_command('usb part')
+
+ lines = output.split('\n')
+ part_fat = []
+ part_ext = []
+ for line in lines:
+ obj = re.search(r'(\d)\s+\d+\s+\d+\s+\w+\d+\w+-\d+\s+(\d+\w+)', line)
+ if obj:
+ part_id = int(obj.groups()[0])
+ part_type = obj.groups()[1]
+ print('part_id:%d, part_type:%s' % (part_id, part_type))
+
+ if part_type == '0c' or part_type == '0b' or part_type == '0e':
+ print('Fat detected')
+ part_fat.append(part_id)
+ elif part_type == '83':
+ print('ext detected')
+ part_ext.append(part_id)
+ else:
+ pytest.fail('Unsupported Filesystem on device %d' % i)
+ devices[i]['ext4'] = part_ext
+ devices[i]['ext2'] = part_ext
+ devices[i]['fat'] = part_fat
+
+ if not part_ext and not part_fat:
+ pytest.fail('No partition detected on device %d' % i)
+
+ return devices, controllers, storage_device
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_fat')
+def test_usb_fatls_fatinfo(u_boot_console):
+ devices, controllers, storage_device = test_usb_part(u_boot_console)
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ fs = 'fat'
+ for x in range(0, int(storage_device)):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('usb dev %d' % x)
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ output = u_boot_console.run_command('fatls usb %d:%s' % (x, part))
+ if 'Unrecognized filesystem type' in output:
+ partitions.remove(part)
+ pytest.fail('Unrecognized filesystem')
+
+ if not re.search(r'\d file\(s\), \d dir\(s\)', output):
+ pytest.fail('%s read failed on device %d' % (fs.upper, x))
+
+ output = u_boot_console.run_command('fatinfo usb %d:%s' % (x, part))
+ string = 'Filesystem: %s' % fs.upper
+ if re.search(string, output):
+ pytest.fail('%s FS failed on device %d' % (fs.upper(), x))
+ part_detect = 1
+
+ if not part_detect:
+ pytest.skip('No %s partition detected' % fs.upper())
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_usb_fatload_fatwrite(u_boot_console):
+ devices, controllers, storage_device = test_usb_part(u_boot_console)
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ fs = 'fat'
+ for x in range(0, int(storage_device)):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('usb dev %d' % x)
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ part_detect = 1
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+ size = random.randint(4, 1 * 1024 * 1024)
+ output = u_boot_console.run_command('crc32 %x %x' % (addr, size))
+ m = re.search('==> (.+?)', output)
+ if not m:
+ pytest.fail('CRC32 failed')
+ expected_crc32 = m.group(1)
+
+ file = '%s_%d' % ('uboot_test', size)
+ output = u_boot_console.run_command(
+ '%swrite usb %d:%s %x %s %x' % (fs, x, part, addr, file, size)
+ )
+ assert 'Unable to write' not in output
+ assert 'Error' not in output
+ assert 'overflow' not in output
+ expected_text = '%d bytes written' % size
+ assert expected_text in output
+
+ alignment = int(
+ u_boot_console.config.buildconfig.get(
+ 'config_sys_cacheline_size', 128
+ )
+ )
+ offset = random.randrange(alignment, 1024, alignment)
+ output = u_boot_console.run_command(
+ '%sload usb %d:%s %x %s' % (fs, x, part, addr + offset, file)
+ )
+ assert 'Invalid FAT entry' not in output
+ assert 'Unable to read file' not in output
+ assert 'Misaligned buffer address' not in output
+ expected_text = '%d bytes read' % size
+ assert expected_text in output
+
+ output = u_boot_console.run_command(
+ 'crc32 %x $filesize' % (addr + offset)
+ )
+ assert expected_crc32 in output
+
+ if not part_detect:
+ pytest.skip('No %s partition detected' % fs.upper())
+
+ return file, size
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_ext4')
+def test_usb_ext4ls(u_boot_console):
+ devices, controllers, storage_device = test_usb_part(u_boot_console)
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ fs = 'ext4'
+ for x in range(0, int(storage_device)):
+ if devices[x]['detected'] == 'yes':
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ u_boot_console.run_command('usb dev %d' % x)
+ for part in partitions:
+ output = u_boot_console.run_command('%sls usb %d:%s' % (fs, x, part))
+ if 'Unrecognized filesystem type' in output:
+ partitions.remove(part)
+ pytest.fail('Unrecognized filesystem')
+ part_detect = 1
+
+ if not part_detect:
+ pytest.skip('No %s partition detected' % fs.upper())
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_ext4')
+@pytest.mark.buildconfigspec('ext4_write')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_usb_ext4load_ext4write(u_boot_console):
+ devices, controllers, storage_device = test_usb_part(u_boot_console)
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ fs = 'ext4'
+ for x in range(0, int(storage_device)):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('usb dev %d' % x)
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ part_detect = 1
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+ size = random.randint(4, 1 * 1024 * 1024)
+ output = u_boot_console.run_command('crc32 %x %x' % (addr, size))
+ m = re.search('==> (.+?)', output)
+ if not m:
+ pytest.fail('CRC32 failed')
+ expected_crc32 = m.group(1)
+ file = '%s_%d' % ('uboot_test', size)
+
+ output = u_boot_console.run_command(
+ '%swrite usb %d:%s %x /%s %x' % (fs, x, part, addr, file, size)
+ )
+ assert 'Unable to write' not in output
+ assert 'Error' not in output
+ assert 'overflow' not in output
+ expected_text = '%d bytes written' % size
+ assert expected_text in output
+
+ offset = random.randrange(128, 1024, 128)
+ output = u_boot_console.run_command(
+ '%sload usb %d:%s %x /%s' % (fs, x, part, addr + offset, file)
+ )
+ expected_text = '%d bytes read' % size
+ assert expected_text in output
+
+ output = u_boot_console.run_command(
+ 'crc32 %x $filesize' % (addr + offset)
+ )
+ assert expected_crc32 in output
+
+ if not part_detect:
+ pytest.skip('No %s partition detected' % fs.upper())
+
+ return file, size
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_ext2')
+def test_usb_ext2ls(u_boot_console):
+ devices, controllers, storage_device = test_usb_part(u_boot_console)
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ fs = 'ext2'
+ for x in range(0, int(storage_device)):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('usb dev %d' % x)
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ part_detect = 1
+ output = u_boot_console.run_command('%sls usb %d:%s' % (fs, x, part))
+ if 'Unrecognized filesystem type' in output:
+ partitions.remove(part)
+ pytest.fail('Unrecognized filesystem')
+ part_detect = 1
+
+ if not part_detect:
+ pytest.skip('No %s partition detected' % fs.upper())
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_ext2')
+@pytest.mark.buildconfigspec('cmd_ext4')
+@pytest.mark.buildconfigspec('ext4_write')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_usb_ext2load(u_boot_console):
+ devices, controllers, storage_device = test_usb_part(u_boot_console)
+ file, size = test_usb_ext4load_ext4write(u_boot_console)
+
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ fs = 'ext2'
+ for x in range(0, int(storage_device)):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('usb dev %d' % x)
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ part_detect = 1
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+ output = u_boot_console.run_command('crc32 %x %x' % (addr, size))
+ m = re.search('==> (.+?)', output)
+ if not m:
+ pytest.fail('CRC32 failed')
+ expected_crc32 = m.group(1)
+
+ offset = random.randrange(128, 1024, 128)
+ output = u_boot_console.run_command(
+ '%sload usb %d:%s %x /%s' % (fs, x, part, addr + offset, file)
+ )
+ expected_text = '%d bytes read' % size
+ assert expected_text in output
+
+ output = u_boot_console.run_command(
+ 'crc32 %x $filesize' % (addr + offset)
+ )
+ assert expected_crc32 in output
+
+ if not part_detect:
+ pytest.skip('No %s partition detected' % fs.upper())
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_fs_generic')
+def test_usb_ls(u_boot_console):
+ devices, controllers, storage_device = test_usb_part(u_boot_console)
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ for x in range(0, int(storage_device)):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('usb dev %d' % x)
+ for fs in ['fat', 'ext4']:
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ part_detect = 1
+ output = u_boot_console.run_command('ls usb %d:%s' % (x, part))
+ if re.search(r'No \w+ table on this device', output):
+ pytest.fail(
+ '%s: Partition table not found %d' % (fs.upper(), x)
+ )
+
+ if not part_detect:
+ pytest.skip('No partition detected')
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_fs_generic')
+def test_usb_load(u_boot_console):
+ devices, controllers, storage_device = test_usb_part(u_boot_console)
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ for x in range(0, int(storage_device)):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('usb dev %d' % x)
+ for fs in ['fat', 'ext4']:
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ part_detect = 1
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+
+ if fs == 'fat':
+ file, size = test_usb_fatload_fatwrite(u_boot_console)
+ elif fs == 'ext4':
+ file, size = test_usb_ext4load_ext4write(u_boot_console)
+
+ output = u_boot_console.run_command('crc32 %x %x' % (addr, size))
+ m = re.search('==> (.+?)', output)
+ if not m:
+ pytest.fail('CRC32 failed')
+ expected_crc32 = m.group(1)
+
+ offset = random.randrange(128, 1024, 128)
+ output = u_boot_console.run_command(
+ 'load usb %d:%s %x /%s' % (x, part, addr + offset, file)
+ )
+ expected_text = '%d bytes read' % size
+ assert expected_text in output
+
+ output = u_boot_console.run_command(
+ 'crc32 %x $filesize' % (addr + offset)
+ )
+ assert expected_crc32 in output
+
+ if not part_detect:
+ pytest.skip('No partition detected')
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_fs_generic')
+def test_usb_save(u_boot_console):
+ devices, controllers, storage_device = test_usb_part(u_boot_console)
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ for x in range(0, int(storage_device)):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('usb dev %d' % x)
+ for fs in ['fat', 'ext4']:
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ part_detect = 1
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+ size = random.randint(4, 1 * 1024 * 1024)
+ file = '%s_%d' % ('uboot_test', size)
+
+ offset = random.randrange(128, 1024, 128)
+ output = u_boot_console.run_command(
+ 'save usb %d:%s %x /%s %x'
+ % (x, part, addr + offset, file, size)
+ )
+ expected_text = '%d bytes written' % size
+ assert expected_text in output
+
+ if not part_detect:
+ pytest.skip('No partition detected')