mirror of
https://github.com/Fishwaldo/u-boot.git
synced 2025-03-18 13:11:31 +00:00
Merge git://git.denx.de/u-boot-dm
This commit is contained in:
commit
8a36287a01
44 changed files with 2628 additions and 866 deletions
11
README
11
README
|
@ -1423,6 +1423,17 @@ The following options need to be configured:
|
|||
CONFIG_TPM_TIS_I2C_BURST_LIMITATION
|
||||
Define the burst count bytes upper limit
|
||||
|
||||
CONFIG_TPM_ST33ZP24
|
||||
Support for STMicroelectronics TPM devices. Requires DM_TPM support.
|
||||
|
||||
CONFIG_TPM_ST33ZP24_I2C
|
||||
Support for STMicroelectronics ST33ZP24 I2C devices.
|
||||
Requires TPM_ST33ZP24 and I2C.
|
||||
|
||||
CONFIG_TPM_ST33ZP24_SPI
|
||||
Support for STMicroelectronics ST33ZP24 SPI devices.
|
||||
Requires TPM_ST33ZP24 and SPI.
|
||||
|
||||
CONFIG_TPM_ATMEL_TWI
|
||||
Support for Atmel TWI TPM device. Requires I2C support.
|
||||
|
||||
|
|
|
@ -52,6 +52,19 @@ config TEGRA210
|
|||
|
||||
endchoice
|
||||
|
||||
config TEGRA_DISCONNECT_UDC_ON_BOOT
|
||||
bool "Disconnect USB device mode controller on boot"
|
||||
default y
|
||||
help
|
||||
When loading U-Boot into RAM over USB protocols using tools such as
|
||||
tegrarcm or L4T's exec-uboot.sh/tegraflash.py, Tegra's USB device
|
||||
mode controller is initialized and enumerated by the host PC running
|
||||
the tool. Unfortunately, these tools do not shut down the USB
|
||||
controller before executing the downloaded code, and so the host PC
|
||||
does not "de-enumerate" the USB device. This option shuts down the
|
||||
USB controller when U-Boot boots to avoid leaving a stale USB device
|
||||
present.
|
||||
|
||||
config SYS_MALLOC_F_LEN
|
||||
default 0x1800
|
||||
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
#ifdef CONFIG_TEGRA_CLOCK_SCALING
|
||||
#include <asm/arch/emc.h>
|
||||
#endif
|
||||
#ifdef CONFIG_USB_EHCI_TEGRA
|
||||
#include <asm/arch-tegra/usb.h>
|
||||
#ifdef CONFIG_USB_EHCI_TEGRA
|
||||
#include <usb.h>
|
||||
#endif
|
||||
#ifdef CONFIG_TEGRA_MMC
|
||||
|
@ -201,6 +201,14 @@ void gpio_early_init(void) __attribute__((weak, alias("__gpio_early_init")));
|
|||
|
||||
int board_early_init_f(void)
|
||||
{
|
||||
#if defined(CONFIG_TEGRA_DISCONNECT_UDC_ON_BOOT)
|
||||
#define USBCMD_FS2 (1 << 15)
|
||||
{
|
||||
struct usb_ctlr *usbctlr = (struct usb_ctlr *)0x7d000000;
|
||||
writel(USBCMD_FS2, &usbctlr->usb_cmd);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Do any special system timer/TSC setup */
|
||||
#if defined(CONFIG_TEGRA_SUPPORT_NON_SECURE)
|
||||
if (!tegra_cpu_is_non_secure())
|
||||
|
|
18
cmd/pci.c
18
cmd/pci.c
|
@ -578,9 +578,10 @@ static int do_pci(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|||
if ((bdf = get_pci_dev(argv[2])) == -1)
|
||||
return 1;
|
||||
break;
|
||||
#ifdef CONFIG_CMD_PCI_ENUM
|
||||
#if defined(CONFIG_CMD_PCI_ENUM) || defined(CONFIG_DM_PCI)
|
||||
case 'e':
|
||||
break;
|
||||
pci_init();
|
||||
return 0;
|
||||
#endif
|
||||
default: /* scan bus */
|
||||
value = 1; /* short listing */
|
||||
|
@ -621,15 +622,6 @@ static int do_pci(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|||
break;
|
||||
case 'd': /* display */
|
||||
return pci_cfg_display(dev, addr, size, value);
|
||||
#ifdef CONFIG_CMD_PCI_ENUM
|
||||
case 'e':
|
||||
# ifdef CONFIG_DM_PCI
|
||||
printf("This command is not yet supported with driver model\n");
|
||||
# else
|
||||
pci_init();
|
||||
# endif
|
||||
break;
|
||||
#endif
|
||||
case 'n': /* next */
|
||||
if (argc < 4)
|
||||
goto usage;
|
||||
|
@ -665,9 +657,9 @@ static int do_pci(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|||
static char pci_help_text[] =
|
||||
"[bus] [long]\n"
|
||||
" - short or long list of PCI devices on bus 'bus'\n"
|
||||
#ifdef CONFIG_CMD_PCI_ENUM
|
||||
#if defined(CONFIG_CMD_PCI_ENUM) || defined(CONFIG_DM_PCI)
|
||||
"pci enum\n"
|
||||
" - re-enumerate PCI buses\n"
|
||||
" - Enumerate PCI buses\n"
|
||||
#endif
|
||||
"pci header b.d.f\n"
|
||||
" - show header of PCI device 'bus.device.function'\n"
|
||||
|
|
|
@ -448,7 +448,7 @@ static int get_tpm(struct udevice **devp)
|
|||
int rc;
|
||||
|
||||
rc = uclass_first_device(UCLASS_TPM, devp);
|
||||
if (rc) {
|
||||
if (rc || !*devp) {
|
||||
printf("Could not find TPM (ret=%d)\n", rc);
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
|
|
|
@ -341,6 +341,21 @@ scan_dev_for_scripts:
|
|||
If you want to disable boot.scr on all disks, set the value to something
|
||||
innocuous, e.g. setenv scan_dev_for_scripts true.
|
||||
|
||||
boot_net_usb_start:
|
||||
|
||||
If you want to prevent USB enumeration by distro boot commands which execute
|
||||
network operations, set the value to something innocuous, e.g. setenv
|
||||
boot_net_usb_start true. This would be useful if you know your Ethernet
|
||||
device is not attached to USB, and you wish to increase boot speed by
|
||||
avoiding unnecessary actions.
|
||||
|
||||
boot_net_pci_enum:
|
||||
|
||||
If you want to prevent PCI enumeration by distro boot commands which execute
|
||||
network operations, set the value to something innocuous, e.g. setenv
|
||||
boot_net_pci_enum true. This would be useful if you know your Ethernet
|
||||
device is not attached to PCI, and you wish to increase boot speed by
|
||||
avoiding unnecessary actions.
|
||||
|
||||
Interactively booting from a specific device at the u-boot prompt
|
||||
=================================================================
|
||||
|
|
|
@ -223,7 +223,7 @@ static void *alloc_priv(int size, uint flags)
|
|||
return priv;
|
||||
}
|
||||
|
||||
int device_probe_child(struct udevice *dev, void *parent_priv)
|
||||
int device_probe(struct udevice *dev)
|
||||
{
|
||||
const struct driver *drv;
|
||||
int size = 0;
|
||||
|
@ -270,8 +270,6 @@ int device_probe_child(struct udevice *dev, void *parent_priv)
|
|||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
if (parent_priv)
|
||||
memcpy(dev->parent_priv, parent_priv, size);
|
||||
}
|
||||
|
||||
ret = device_probe(dev->parent);
|
||||
|
@ -349,11 +347,6 @@ fail:
|
|||
return ret;
|
||||
}
|
||||
|
||||
int device_probe(struct udevice *dev)
|
||||
{
|
||||
return device_probe_child(dev, NULL);
|
||||
}
|
||||
|
||||
void *dev_get_platdata(struct udevice *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
|
|
|
@ -1241,3 +1241,18 @@ U_BOOT_DRIVER(pci_generic_drv) = {
|
|||
.id = UCLASS_PCI_GENERIC,
|
||||
.of_match = pci_generic_ids,
|
||||
};
|
||||
|
||||
void pci_init(void)
|
||||
{
|
||||
struct udevice *bus;
|
||||
|
||||
/*
|
||||
* Enumerate all known controller devices. Enumeration has the side-
|
||||
* effect of probing them, so PCIe devices will be enumerated too.
|
||||
*/
|
||||
for (uclass_first_device(UCLASS_PCI, &bus);
|
||||
bus;
|
||||
uclass_next_device(&bus)) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ config TPM_TIS_LPC
|
|||
bool "Enable support for Infineon SLB9635/45 TPMs on LPC"
|
||||
depends on TPM && X86
|
||||
help
|
||||
This driver supports Infineon TPM devices connected on the I2C bus.
|
||||
This driver supports Infineon TPM devices connected on the LPC bus.
|
||||
The usual tpm operations and the 'tpm' command can be used to talk
|
||||
to the device using the standard TPM Interface Specification (TIS)
|
||||
protocol
|
||||
|
@ -64,4 +64,22 @@ config TPM_AUTH_SESSIONS
|
|||
TPM_LoadKey2 and TPM_GetPubKey are provided. Both features are
|
||||
available using the 'tpm' command, too.
|
||||
|
||||
config TPM_ST33ZP24_I2C
|
||||
bool "STMicroelectronics ST33ZP24 I2C TPM"
|
||||
depends on TPM && DM_I2C
|
||||
---help---
|
||||
This driver supports STMicroelectronics TPM devices connected on the I2C bus.
|
||||
The usual tpm operations and the 'tpm' command can be used to talk
|
||||
to the device using the standard TPM Interface Specification (TIS)
|
||||
protocol
|
||||
|
||||
config TPM_ST33ZP24_SPI
|
||||
bool "STMicroelectronics ST33ZP24 SPI TPM"
|
||||
depends on TPM && DM_SPI
|
||||
---help---
|
||||
This driver supports STMicroelectronics TPM devices connected on the SPI bus.
|
||||
The usual tpm operations and the 'tpm' command can be used to talk
|
||||
to the device using the standard TPM Interface Specification (TIS)
|
||||
protocol
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -9,3 +9,5 @@ obj-$(CONFIG_TPM_ATMEL_TWI) += tpm_atmel_twi.o
|
|||
obj-$(CONFIG_TPM_TIS_INFINEON) += tpm_tis_infineon.o
|
||||
obj-$(CONFIG_TPM_TIS_LPC) += tpm_tis_lpc.o
|
||||
obj-$(CONFIG_TPM_TIS_SANDBOX) += tpm_tis_sandbox.o
|
||||
obj-$(CONFIG_TPM_ST33ZP24_I2C) += tpm_tis_st33zp24_i2c.o
|
||||
obj-$(CONFIG_TPM_ST33ZP24_SPI) += tpm_tis_st33zp24_spi.o
|
||||
|
|
|
@ -37,18 +37,12 @@ enum tpm_timeout {
|
|||
#define TPM_RSP_SIZE_BYTE 2
|
||||
#define TPM_RSP_RC_BYTE 6
|
||||
|
||||
enum i2c_chip_type {
|
||||
SLB9635,
|
||||
SLB9645,
|
||||
UNKNOWN,
|
||||
};
|
||||
|
||||
struct tpm_chip {
|
||||
int is_open;
|
||||
int locality;
|
||||
u32 vend_dev;
|
||||
unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* msec */
|
||||
enum i2c_chip_type chip_type;
|
||||
ulong chip_type;
|
||||
};
|
||||
|
||||
struct tpm_input_header {
|
||||
|
@ -134,13 +128,4 @@ enum tis_status {
|
|||
TPM_STS_DATA_EXPECT = 0x08,
|
||||
};
|
||||
|
||||
/* expected value for DIDVID register */
|
||||
#define TPM_TIS_I2C_DID_VID_9635 0x000b15d1L
|
||||
#define TPM_TIS_I2C_DID_VID_9645 0x001a15d1L
|
||||
|
||||
#define TPM_ACCESS(l) (0x0000 | ((l) << 4))
|
||||
#define TPM_STS(l) (0x0001 | ((l) << 4))
|
||||
#define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4))
|
||||
#define TPM_DID_VID(l) (0x0006 | ((l) << 4))
|
||||
|
||||
#endif
|
|
@ -30,17 +30,32 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/unaligned/be_byteshift.h>
|
||||
|
||||
#include "tpm_tis_infineon.h"
|
||||
#include "tpm_tis.h"
|
||||
#include "tpm_internal.h"
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
enum i2c_chip_type {
|
||||
SLB9635,
|
||||
SLB9645,
|
||||
UNKNOWN,
|
||||
};
|
||||
|
||||
/* expected value for DIDVID register */
|
||||
#define TPM_TIS_I2C_DID_VID_9635 0x000b15d1L
|
||||
#define TPM_TIS_I2C_DID_VID_9645 0x001a15d1L
|
||||
|
||||
static const char * const chip_name[] = {
|
||||
[SLB9635] = "slb9635tt",
|
||||
[SLB9645] = "slb9645tt",
|
||||
[UNKNOWN] = "unknown/fallback to slb9635",
|
||||
};
|
||||
|
||||
#define TPM_ACCESS(l) (0x0000 | ((l) << 4))
|
||||
#define TPM_STS(l) (0x0001 | ((l) << 4))
|
||||
#define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4))
|
||||
#define TPM_DID_VID(l) (0x0006 | ((l) << 4))
|
||||
|
||||
/*
|
||||
* tpm_tis_i2c_read() - read from TPM register
|
||||
* @addr: register address to read from
|
||||
|
|
543
drivers/tpm/tpm_tis_st33zp24_i2c.c
Normal file
543
drivers/tpm/tpm_tis_st33zp24_i2c.c
Normal file
|
@ -0,0 +1,543 @@
|
|||
/*
|
||||
* STMicroelectronics TPM ST33ZP24 I2C UBOOT driver
|
||||
*
|
||||
* Copyright (C) 2016 STMicroelectronics
|
||||
*
|
||||
* Description: Device driver for ST33ZP24 I2C TPM TCG.
|
||||
*
|
||||
* This device driver implements the TPM interface as defined in
|
||||
* the TCG TPM Interface Spec version 1.21, revision 1.0 and the
|
||||
* STMicroelectronics Protocol Stack Specification version 1.2.0.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <fdtdec.h>
|
||||
#include <i2c.h>
|
||||
#include <tpm.h>
|
||||
#include <errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "tpm_tis.h"
|
||||
#include "tpm_internal.h"
|
||||
|
||||
#define TPM_ACCESS 0x0
|
||||
#define TPM_STS 0x18
|
||||
#define TPM_DATA_FIFO 0x24
|
||||
|
||||
#define LOCALITY0 0
|
||||
|
||||
#define TPM_DUMMY_BYTE 0xAA
|
||||
#define TPM_ST33ZP24_I2C_SLAVE_ADDR 0x13
|
||||
|
||||
#define TPM_WRITE_DIRECTION 0x80
|
||||
|
||||
/*
|
||||
* st33zp24_i2c_write8_reg
|
||||
* Send byte to the TIS register according to the ST33ZP24 I2C protocol.
|
||||
* @param: tpm_register, the tpm tis register where the data should be written
|
||||
* @param: tpm_data, the tpm_data to write inside the tpm_register
|
||||
* @param: tpm_size, The length of the data
|
||||
* @return: Number of byte written successfully else an error code.
|
||||
*/
|
||||
static int st33zp24_i2c_write8_reg(struct udevice *dev, u8 tpm_register,
|
||||
const u8 *tpm_data, size_t tpm_size)
|
||||
{
|
||||
struct tpm_chip_priv *chip_priv = dev_get_uclass_priv(dev);
|
||||
|
||||
chip_priv->buf[0] = tpm_register;
|
||||
memcpy(chip_priv->buf + 1, tpm_data, tpm_size);
|
||||
|
||||
return dm_i2c_write(dev, 0, chip_priv->buf, tpm_size + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_i2c_read8_reg
|
||||
* Recv byte from the TIS register according to the ST33ZP24 I2C protocol.
|
||||
* @param: tpm_register, the tpm tis register where the data should be read
|
||||
* @param: tpm_data, the TPM response
|
||||
* @param: tpm_size, tpm TPM response size to read.
|
||||
* @return: Number of byte read successfully else an error code.
|
||||
*/
|
||||
static int st33zp24_i2c_read8_reg(struct udevice *dev, u8 tpm_register,
|
||||
u8 *tpm_data, size_t tpm_size)
|
||||
{
|
||||
int status;
|
||||
u8 data;
|
||||
|
||||
data = TPM_DUMMY_BYTE;
|
||||
status = st33zp24_i2c_write8_reg(dev, tpm_register, &data, 1);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
return dm_i2c_read(dev, 0, tpm_data, tpm_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_i2c_write
|
||||
* Send byte to the TIS register according to the ST33ZP24 I2C protocol.
|
||||
* @param: phy_id, the phy description
|
||||
* @param: tpm_register, the tpm tis register where the data should be written
|
||||
* @param: tpm_data, the tpm_data to write inside the tpm_register
|
||||
* @param: tpm_size, the length of the data
|
||||
* @return: number of byte written successfully: should be one if success.
|
||||
*/
|
||||
static int st33zp24_i2c_write(struct udevice *dev, u8 tpm_register,
|
||||
const u8 *tpm_data, size_t tpm_size)
|
||||
{
|
||||
return st33zp24_i2c_write8_reg(dev, tpm_register | TPM_WRITE_DIRECTION,
|
||||
tpm_data, tpm_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_i2c_read
|
||||
* Recv byte from the TIS register according to the ST33ZP24 I2C protocol.
|
||||
* @param: phy_id, the phy description
|
||||
* @param: tpm_register, the tpm tis register where the data should be read
|
||||
* @param: tpm_data, the TPM response
|
||||
* @param: tpm_size, tpm TPM response size to read.
|
||||
* @return: number of byte read successfully: should be one if success.
|
||||
*/
|
||||
static int st33zp24_i2c_read(struct udevice *dev, u8 tpm_register,
|
||||
u8 *tpm_data, size_t tpm_size)
|
||||
{
|
||||
return st33zp24_i2c_read8_reg(dev, tpm_register, tpm_data, tpm_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_i2c_release_locality release the active locality
|
||||
* @param: chip, the tpm chip description.
|
||||
*/
|
||||
static void st33zp24_i2c_release_locality(struct udevice *dev)
|
||||
{
|
||||
u8 data = TPM_ACCESS_ACTIVE_LOCALITY;
|
||||
|
||||
st33zp24_i2c_write(dev, TPM_ACCESS, &data, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_i2c_check_locality if the locality is active
|
||||
* @param: chip, the tpm chip description
|
||||
* @return: the active locality or -EACCES.
|
||||
*/
|
||||
static int st33zp24_i2c_check_locality(struct udevice *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_priv(dev);
|
||||
u8 data;
|
||||
u8 status;
|
||||
|
||||
status = st33zp24_i2c_read(dev, TPM_ACCESS, &data, 1);
|
||||
if (!status && (data &
|
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
|
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
|
||||
return chip->locality;
|
||||
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_i2c_request_locality request the TPM locality
|
||||
* @param: chip, the chip description
|
||||
* @return: the active locality or negative value.
|
||||
*/
|
||||
static int st33zp24_i2c_request_locality(struct udevice *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_priv(dev);
|
||||
unsigned long start, stop;
|
||||
long ret;
|
||||
u8 data;
|
||||
|
||||
if (st33zp24_i2c_check_locality(dev) == chip->locality)
|
||||
return chip->locality;
|
||||
|
||||
data = TPM_ACCESS_REQUEST_USE;
|
||||
ret = st33zp24_i2c_write(dev, TPM_ACCESS, &data, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* wait for locality activated */
|
||||
start = get_timer(0);
|
||||
stop = chip->timeout_a;
|
||||
do {
|
||||
if (st33zp24_i2c_check_locality(dev) >= 0)
|
||||
return chip->locality;
|
||||
udelay(TPM_TIMEOUT_MS * 1000);
|
||||
} while (get_timer(start) < stop);
|
||||
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_i2c_status return the TPM_STS register
|
||||
* @param: chip, the tpm chip description
|
||||
* @return: the TPM_STS register value.
|
||||
*/
|
||||
static u8 st33zp24_i2c_status(struct udevice *dev)
|
||||
{
|
||||
u8 data;
|
||||
|
||||
st33zp24_i2c_read(dev, TPM_STS, &data, 1);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_i2c_get_burstcount return the burstcount address 0x19 0x1A
|
||||
* @param: chip, the chip description
|
||||
* return: the burstcount or -TPM_DRIVER_ERR in case of error.
|
||||
*/
|
||||
static int st33zp24_i2c_get_burstcount(struct udevice *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_priv(dev);
|
||||
unsigned long start, stop;
|
||||
int burstcnt, status;
|
||||
u8 tpm_reg, temp;
|
||||
|
||||
/* wait for burstcount */
|
||||
start = get_timer(0);
|
||||
stop = chip->timeout_d;
|
||||
do {
|
||||
tpm_reg = TPM_STS + 1;
|
||||
status = st33zp24_i2c_read(dev, tpm_reg, &temp, 1);
|
||||
if (status < 0)
|
||||
return -EBUSY;
|
||||
|
||||
tpm_reg = TPM_STS + 2;
|
||||
burstcnt = temp;
|
||||
status = st33zp24_i2c_read(dev, tpm_reg, &temp, 1);
|
||||
if (status < 0)
|
||||
return -EBUSY;
|
||||
|
||||
burstcnt |= temp << 8;
|
||||
if (burstcnt)
|
||||
return burstcnt;
|
||||
udelay(TIS_SHORT_TIMEOUT_MS * 1000);
|
||||
} while (get_timer(start) < stop);
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_i2c_cancel, cancel the current command execution or
|
||||
* set STS to COMMAND READY.
|
||||
* @param: chip, tpm_chip description.
|
||||
*/
|
||||
static void st33zp24_i2c_cancel(struct udevice *dev)
|
||||
{
|
||||
u8 data;
|
||||
|
||||
data = TPM_STS_COMMAND_READY;
|
||||
st33zp24_i2c_write(dev, TPM_STS, &data, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_i2c_wait_for_stat wait for a TPM_STS value
|
||||
* @param: chip, the tpm chip description
|
||||
* @param: mask, the value mask to wait
|
||||
* @param: timeout, the timeout
|
||||
* @param: status,
|
||||
* @return: the tpm status, 0 if success, -ETIME if timeout is reached.
|
||||
*/
|
||||
static int st33zp24_i2c_wait_for_stat(struct udevice *dev, u8 mask,
|
||||
unsigned long timeout, int *status)
|
||||
{
|
||||
unsigned long start, stop;
|
||||
|
||||
/* Check current status */
|
||||
*status = st33zp24_i2c_status(dev);
|
||||
if ((*status & mask) == mask)
|
||||
return 0;
|
||||
|
||||
start = get_timer(0);
|
||||
stop = timeout;
|
||||
do {
|
||||
udelay(TPM_TIMEOUT_MS * 1000);
|
||||
*status = st33zp24_i2c_status(dev);
|
||||
if ((*status & mask) == mask)
|
||||
return 0;
|
||||
} while (get_timer(start) < stop);
|
||||
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_i2c_recv_data receive data
|
||||
* @param: chip, the tpm chip description
|
||||
* @param: buf, the buffer where the data are received
|
||||
* @param: count, the number of data to receive
|
||||
* @return: the number of bytes read from TPM FIFO.
|
||||
*/
|
||||
static int st33zp24_i2c_recv_data(struct udevice *dev, u8 *buf, size_t count)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_priv(dev);
|
||||
int size = 0, burstcnt, len, ret, status;
|
||||
|
||||
while (size < count &&
|
||||
st33zp24_i2c_wait_for_stat(dev, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
chip->timeout_c, &status) == 0) {
|
||||
burstcnt = st33zp24_i2c_get_burstcount(dev);
|
||||
if (burstcnt < 0)
|
||||
return burstcnt;
|
||||
len = min_t(int, burstcnt, count - size);
|
||||
ret = st33zp24_i2c_read(dev, TPM_DATA_FIFO, buf + size, len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
size += len;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_i2c_recv received TPM response through TPM phy.
|
||||
* @param: chip, tpm_chip description.
|
||||
* @param: buf, the buffer to store data.
|
||||
* @param: count, the number of bytes that can received (sizeof buf).
|
||||
* @return: Returns zero in case of success else -EIO.
|
||||
*/
|
||||
static int st33zp24_i2c_recv(struct udevice *dev, u8 *buf, size_t count)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_priv(dev);
|
||||
int size, expected;
|
||||
|
||||
if (!chip)
|
||||
return -ENODEV;
|
||||
|
||||
if (count < TPM_HEADER_SIZE) {
|
||||
size = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
size = st33zp24_i2c_recv_data(dev, buf, TPM_HEADER_SIZE);
|
||||
if (size < TPM_HEADER_SIZE) {
|
||||
debug("TPM error, unable to read header\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
expected = get_unaligned_be32(buf + 2);
|
||||
if (expected > count) {
|
||||
size = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
size += st33zp24_i2c_recv_data(dev, &buf[TPM_HEADER_SIZE],
|
||||
expected - TPM_HEADER_SIZE);
|
||||
if (size < expected) {
|
||||
debug("TPM error, unable to read remaining bytes of result\n");
|
||||
size = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
st33zp24_i2c_cancel(dev);
|
||||
st33zp24_i2c_release_locality(dev);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_i2c_send send TPM commands through TPM phy.
|
||||
* @param: chip, tpm_chip description.
|
||||
* @param: buf, the buffer to send.
|
||||
* @param: len, the number of bytes to send.
|
||||
* @return: Returns zero in case of success else the negative error code.
|
||||
*/
|
||||
static int st33zp24_i2c_send(struct udevice *dev, const u8 *buf, size_t len)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_priv(dev);
|
||||
u32 i, size;
|
||||
int burstcnt, ret, status;
|
||||
u8 data, tpm_stat;
|
||||
|
||||
if (!chip)
|
||||
return -ENODEV;
|
||||
if (len < TPM_HEADER_SIZE)
|
||||
return -EIO;
|
||||
|
||||
ret = st33zp24_i2c_request_locality(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tpm_stat = st33zp24_i2c_status(dev);
|
||||
if ((tpm_stat & TPM_STS_COMMAND_READY) == 0) {
|
||||
st33zp24_i2c_cancel(dev);
|
||||
if (st33zp24_i2c_wait_for_stat(dev, TPM_STS_COMMAND_READY,
|
||||
chip->timeout_b, &status) < 0) {
|
||||
ret = -ETIME;
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < len - 1;) {
|
||||
burstcnt = st33zp24_i2c_get_burstcount(dev);
|
||||
if (burstcnt < 0)
|
||||
return burstcnt;
|
||||
|
||||
size = min_t(int, len - i - 1, burstcnt);
|
||||
ret = st33zp24_i2c_write(dev, TPM_DATA_FIFO, buf + i, size);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
|
||||
i += size;
|
||||
}
|
||||
|
||||
tpm_stat = st33zp24_i2c_status(dev);
|
||||
if ((tpm_stat & TPM_STS_DATA_EXPECT) == 0) {
|
||||
ret = -EIO;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
ret = st33zp24_i2c_write(dev, TPM_DATA_FIFO, buf + len - 1, 1);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
|
||||
tpm_stat = st33zp24_i2c_status(dev);
|
||||
if ((tpm_stat & TPM_STS_DATA_EXPECT) != 0) {
|
||||
ret = -EIO;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
data = TPM_STS_GO;
|
||||
ret = st33zp24_i2c_write(dev, TPM_STS, &data, 1);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
|
||||
return len;
|
||||
|
||||
out_err:
|
||||
st33zp24_i2c_cancel(dev);
|
||||
st33zp24_i2c_release_locality(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int st33zp24_i2c_cleanup(struct udevice *dev)
|
||||
{
|
||||
st33zp24_i2c_cancel(dev);
|
||||
/*
|
||||
* The TPM needs some time to clean up here,
|
||||
* so we sleep rather than keeping the bus busy
|
||||
*/
|
||||
mdelay(2);
|
||||
st33zp24_i2c_release_locality(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st33zp24_i2c_init(struct udevice *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_priv(dev);
|
||||
|
||||
chip->is_open = 1;
|
||||
|
||||
/* Default timeouts - these could move to the device tree */
|
||||
chip->timeout_a = TIS_SHORT_TIMEOUT_MS;
|
||||
chip->timeout_b = TIS_LONG_TIMEOUT_MS;
|
||||
chip->timeout_c = TIS_SHORT_TIMEOUT_MS;
|
||||
chip->timeout_d = TIS_SHORT_TIMEOUT_MS;
|
||||
|
||||
chip->locality = LOCALITY0;
|
||||
|
||||
/*
|
||||
* A timeout query to TPM can be placed here.
|
||||
* Standard timeout values are used so far
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st33zp24_i2c_open(struct udevice *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_priv(dev);
|
||||
int rc;
|
||||
|
||||
debug("%s: start\n", __func__);
|
||||
if (chip->is_open)
|
||||
return -EBUSY;
|
||||
|
||||
rc = st33zp24_i2c_init(dev);
|
||||
if (rc < 0)
|
||||
chip->is_open = 0;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int st33zp24_i2c_close(struct udevice *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_priv(dev);
|
||||
|
||||
if (chip->is_open) {
|
||||
st33zp24_i2c_release_locality(dev);
|
||||
chip->is_open = 0;
|
||||
chip->vend_dev = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st33zp24_i2c_get_desc(struct udevice *dev, char *buf, int size)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_priv(dev);
|
||||
|
||||
if (size < 50)
|
||||
return -ENOSPC;
|
||||
|
||||
return snprintf(buf, size, "1.2 TPM (%s, chip type %s device-id 0x%x)",
|
||||
chip->is_open ? "open" : "closed",
|
||||
dev->name,
|
||||
chip->vend_dev >> 16);
|
||||
}
|
||||
|
||||
static const struct tpm_ops st33zp24_i2c_tpm_ops = {
|
||||
.open = st33zp24_i2c_open,
|
||||
.close = st33zp24_i2c_close,
|
||||
.recv = st33zp24_i2c_recv,
|
||||
.send = st33zp24_i2c_send,
|
||||
.cleanup = st33zp24_i2c_cleanup,
|
||||
.get_desc = st33zp24_i2c_get_desc,
|
||||
};
|
||||
|
||||
static int st33zp24_i2c_probe(struct udevice *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_priv(dev);
|
||||
|
||||
/* Default timeouts */
|
||||
chip->timeout_a = TIS_SHORT_TIMEOUT_MS;
|
||||
chip->timeout_b = TIS_LONG_TIMEOUT_MS;
|
||||
chip->timeout_c = TIS_SHORT_TIMEOUT_MS;
|
||||
chip->timeout_d = TIS_SHORT_TIMEOUT_MS;
|
||||
|
||||
chip->locality = LOCALITY0;
|
||||
|
||||
i2c_set_chip_offset_len(dev, 0);
|
||||
|
||||
debug("ST33ZP24 I2C TPM from STMicroelectronics found\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st33zp24_i2c_remove(struct udevice *dev)
|
||||
{
|
||||
st33zp24_i2c_release_locality(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id st33zp24_i2c_ids[] = {
|
||||
{ .compatible = "st,st33zp24-i2c" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(st33zp24_i2c) = {
|
||||
.name = "st33zp24-i2c",
|
||||
.id = UCLASS_TPM,
|
||||
.of_match = of_match_ptr(st33zp24_i2c_ids),
|
||||
.probe = st33zp24_i2c_probe,
|
||||
.remove = st33zp24_i2c_remove,
|
||||
.ops = &st33zp24_i2c_tpm_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct tpm_chip),
|
||||
};
|
672
drivers/tpm/tpm_tis_st33zp24_spi.c
Normal file
672
drivers/tpm/tpm_tis_st33zp24_spi.c
Normal file
|
@ -0,0 +1,672 @@
|
|||
/*
|
||||
* STMicroelectronics TPM ST33ZP24 SPI UBOOT driver
|
||||
*
|
||||
* Copyright (C) 2016 STMicroelectronics
|
||||
*
|
||||
* Description: Device driver for ST33ZP24 SPI TPM TCG.
|
||||
*
|
||||
* This device driver implements the TPM interface as defined in
|
||||
* the TCG TPM Interface Spec version 1.21, revision 1.0 and the
|
||||
* STMicroelectronics Protocol Stack Specification version 1.2.0.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <fdtdec.h>
|
||||
#include <spi.h>
|
||||
#include <tpm.h>
|
||||
#include <errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
#include "tpm_tis.h"
|
||||
#include "tpm_internal.h"
|
||||
|
||||
#define TPM_ACCESS 0x0
|
||||
#define TPM_STS 0x18
|
||||
#define TPM_DATA_FIFO 0x24
|
||||
|
||||
#define LOCALITY0 0
|
||||
|
||||
#define TPM_DATA_FIFO 0x24
|
||||
#define TPM_INTF_CAPABILITY 0x14
|
||||
|
||||
#define TPM_DUMMY_BYTE 0x00
|
||||
#define TPM_WRITE_DIRECTION 0x80
|
||||
|
||||
#define MAX_SPI_LATENCY 15
|
||||
#define LOCALITY0 0
|
||||
|
||||
#define ST33ZP24_OK 0x5A
|
||||
#define ST33ZP24_UNDEFINED_ERR 0x80
|
||||
#define ST33ZP24_BADLOCALITY 0x81
|
||||
#define ST33ZP24_TISREGISTER_UKNOWN 0x82
|
||||
#define ST33ZP24_LOCALITY_NOT_ACTIVATED 0x83
|
||||
#define ST33ZP24_HASH_END_BEFORE_HASH_START 0x84
|
||||
#define ST33ZP24_BAD_COMMAND_ORDER 0x85
|
||||
#define ST33ZP24_INCORECT_RECEIVED_LENGTH 0x86
|
||||
#define ST33ZP24_TPM_FIFO_OVERFLOW 0x89
|
||||
#define ST33ZP24_UNEXPECTED_READ_FIFO 0x8A
|
||||
#define ST33ZP24_UNEXPECTED_WRITE_FIFO 0x8B
|
||||
#define ST33ZP24_CMDRDY_SET_WHEN_PROCESSING_HASH_END 0x90
|
||||
#define ST33ZP24_DUMMY_BYTES 0x00
|
||||
|
||||
/*
|
||||
* TPM command can be up to 2048 byte, A TPM response can be up to
|
||||
* 1024 byte.
|
||||
* Between command and response, there are latency byte (up to 15
|
||||
* usually on st33zp24 2 are enough).
|
||||
*
|
||||
* Overall when sending a command and expecting an answer we need if
|
||||
* worst case:
|
||||
* 2048 (for the TPM command) + 1024 (for the TPM answer). We need
|
||||
* some latency byte before the answer is available (max 15).
|
||||
* We have 2048 + 1024 + 15.
|
||||
*/
|
||||
#define ST33ZP24_SPI_BUFFER_SIZE (TPM_BUFSIZE + (TPM_BUFSIZE / 2) +\
|
||||
MAX_SPI_LATENCY)
|
||||
|
||||
struct st33zp24_spi_phy {
|
||||
int latency;
|
||||
|
||||
u8 tx_buf[ST33ZP24_SPI_BUFFER_SIZE];
|
||||
u8 rx_buf[ST33ZP24_SPI_BUFFER_SIZE];
|
||||
};
|
||||
|
||||
static int st33zp24_spi_status_to_errno(u8 code)
|
||||
{
|
||||
switch (code) {
|
||||
case ST33ZP24_OK:
|
||||
return 0;
|
||||
case ST33ZP24_UNDEFINED_ERR:
|
||||
case ST33ZP24_BADLOCALITY:
|
||||
case ST33ZP24_TISREGISTER_UKNOWN:
|
||||
case ST33ZP24_LOCALITY_NOT_ACTIVATED:
|
||||
case ST33ZP24_HASH_END_BEFORE_HASH_START:
|
||||
case ST33ZP24_BAD_COMMAND_ORDER:
|
||||
case ST33ZP24_UNEXPECTED_READ_FIFO:
|
||||
case ST33ZP24_UNEXPECTED_WRITE_FIFO:
|
||||
case ST33ZP24_CMDRDY_SET_WHEN_PROCESSING_HASH_END:
|
||||
return -EPROTO;
|
||||
case ST33ZP24_INCORECT_RECEIVED_LENGTH:
|
||||
case ST33ZP24_TPM_FIFO_OVERFLOW:
|
||||
return -EMSGSIZE;
|
||||
case ST33ZP24_DUMMY_BYTES:
|
||||
return -ENOSYS;
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_spi_send
|
||||
* Send byte to TPM register according to the ST33ZP24 SPI protocol.
|
||||
* @param: tpm, the chip description
|
||||
* @param: tpm_register, the tpm tis register where the data should be written
|
||||
* @param: tpm_data, the tpm_data to write inside the tpm_register
|
||||
* @param: tpm_size, The length of the data
|
||||
* @return: should be zero if success else a negative error code.
|
||||
*/
|
||||
static int st33zp24_spi_write(struct udevice *dev, u8 tpm_register,
|
||||
const u8 *tpm_data, size_t tpm_size)
|
||||
{
|
||||
int total_length = 0, ret;
|
||||
struct spi_slave *slave = dev_get_parent_priv(dev);
|
||||
struct st33zp24_spi_phy *phy = dev_get_platdata(dev);
|
||||
|
||||
u8 *tx_buf = (u8 *)phy->tx_buf;
|
||||
u8 *rx_buf = phy->rx_buf;
|
||||
|
||||
tx_buf[total_length++] = TPM_WRITE_DIRECTION | LOCALITY0;
|
||||
tx_buf[total_length++] = tpm_register;
|
||||
|
||||
if (tpm_size > 0 && tpm_register == TPM_DATA_FIFO) {
|
||||
tx_buf[total_length++] = tpm_size >> 8;
|
||||
tx_buf[total_length++] = tpm_size;
|
||||
}
|
||||
memcpy(tx_buf + total_length, tpm_data, tpm_size);
|
||||
total_length += tpm_size;
|
||||
|
||||
memset(tx_buf + total_length, TPM_DUMMY_BYTE, phy->latency);
|
||||
|
||||
total_length += phy->latency;
|
||||
|
||||
ret = spi_claim_bus(slave);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = spi_xfer(slave, total_length * 8, tx_buf, rx_buf,
|
||||
SPI_XFER_BEGIN | SPI_XFER_END);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
spi_release_bus(slave);
|
||||
|
||||
if (ret == 0)
|
||||
ret = rx_buf[total_length - 1];
|
||||
|
||||
return st33zp24_spi_status_to_errno(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* spi_st33zp24_spi_read8_reg
|
||||
* Recv byte from the TIS register according to the ST33ZP24 SPI protocol.
|
||||
* @param: tpm, the chip description
|
||||
* @param: tpm_loc, the locality to read register from
|
||||
* @param: tpm_register, the tpm tis register where the data should be read
|
||||
* @param: tpm_data, the TPM response
|
||||
* @param: tpm_size, tpm TPM response size to read.
|
||||
* @return: should be zero if success else a negative error code.
|
||||
*/
|
||||
static u8 st33zp24_spi_read8_reg(struct udevice *dev, u8 tpm_register,
|
||||
u8 *tpm_data, size_t tpm_size)
|
||||
{
|
||||
int total_length = 0, ret;
|
||||
struct spi_slave *slave = dev_get_parent_priv(dev);
|
||||
struct st33zp24_spi_phy *phy = dev_get_platdata(dev);
|
||||
|
||||
u8 *tx_buf = (u8 *)phy->tx_buf;
|
||||
u8 *rx_buf = phy->rx_buf;
|
||||
|
||||
/* Pre-Header */
|
||||
tx_buf[total_length++] = LOCALITY0;
|
||||
tx_buf[total_length++] = tpm_register;
|
||||
|
||||
memset(&tx_buf[total_length], TPM_DUMMY_BYTE,
|
||||
phy->latency + tpm_size);
|
||||
total_length += phy->latency + tpm_size;
|
||||
|
||||
ret = spi_claim_bus(slave);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
ret = spi_xfer(slave, total_length * 8, tx_buf, rx_buf,
|
||||
SPI_XFER_BEGIN | SPI_XFER_END);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
spi_release_bus(slave);
|
||||
|
||||
if (tpm_size > 0 && ret == 0) {
|
||||
ret = rx_buf[total_length - tpm_size - 1];
|
||||
memcpy(tpm_data, rx_buf + total_length - tpm_size, tpm_size);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_spi_recv
|
||||
* Recv byte from the TIS register according to the ST33ZP24 SPI protocol.
|
||||
* @param: phy_id, the phy description
|
||||
* @param: tpm_register, the tpm tis register where the data should be read
|
||||
* @param: tpm_data, the TPM response
|
||||
* @param: tpm_size, tpm TPM response size to read.
|
||||
* @return: number of byte read successfully: should be one if success.
|
||||
*/
|
||||
static int st33zp24_spi_read(struct udevice *dev, u8 tpm_register,
|
||||
u8 *tpm_data, size_t tpm_size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = st33zp24_spi_read8_reg(dev, tpm_register, tpm_data, tpm_size);
|
||||
if (!st33zp24_spi_status_to_errno(ret))
|
||||
return tpm_size;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int st33zp24_spi_evaluate_latency(struct udevice *dev)
|
||||
{
|
||||
int latency = 1, status = 0;
|
||||
u8 data = 0;
|
||||
struct st33zp24_spi_phy *phy = dev_get_platdata(dev);
|
||||
|
||||
while (!status && latency < MAX_SPI_LATENCY) {
|
||||
phy->latency = latency;
|
||||
status = st33zp24_spi_read8_reg(dev, TPM_INTF_CAPABILITY,
|
||||
&data, 1);
|
||||
latency++;
|
||||
}
|
||||
if (status < 0)
|
||||
return status;
|
||||
if (latency == MAX_SPI_LATENCY)
|
||||
return -ENODEV;
|
||||
|
||||
return latency - 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_spi_release_locality release the active locality
|
||||
* @param: chip, the tpm chip description.
|
||||
*/
|
||||
static void st33zp24_spi_release_locality(struct udevice *dev)
|
||||
{
|
||||
u8 data = TPM_ACCESS_ACTIVE_LOCALITY;
|
||||
|
||||
st33zp24_spi_write(dev, TPM_ACCESS, &data, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_spi_check_locality if the locality is active
|
||||
* @param: chip, the tpm chip description
|
||||
* @return: the active locality or -EACCES.
|
||||
*/
|
||||
static int st33zp24_spi_check_locality(struct udevice *dev)
|
||||
{
|
||||
u8 data;
|
||||
u8 status;
|
||||
struct tpm_chip *chip = dev_get_priv(dev);
|
||||
|
||||
status = st33zp24_spi_read(dev, TPM_ACCESS, &data, 1);
|
||||
if (status && (data &
|
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
|
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
|
||||
return chip->locality;
|
||||
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_spi_request_locality request the TPM locality
|
||||
* @param: chip, the chip description
|
||||
* @return: the active locality or negative value.
|
||||
*/
|
||||
static int st33zp24_spi_request_locality(struct udevice *dev)
|
||||
{
|
||||
unsigned long start, stop;
|
||||
long ret;
|
||||
u8 data;
|
||||
struct tpm_chip *chip = dev_get_priv(dev);
|
||||
|
||||
if (st33zp24_spi_check_locality(dev) == chip->locality)
|
||||
return chip->locality;
|
||||
|
||||
data = TPM_ACCESS_REQUEST_USE;
|
||||
ret = st33zp24_spi_write(dev, TPM_ACCESS, &data, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* wait for locality activated */
|
||||
start = get_timer(0);
|
||||
stop = chip->timeout_a;
|
||||
do {
|
||||
if (st33zp24_spi_check_locality(dev) >= 0)
|
||||
return chip->locality;
|
||||
udelay(TPM_TIMEOUT_MS * 1000);
|
||||
} while (get_timer(start) < stop);
|
||||
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_spi_status return the TPM_STS register
|
||||
* @param: chip, the tpm chip description
|
||||
* @return: the TPM_STS register value.
|
||||
*/
|
||||
static u8 st33zp24_spi_status(struct udevice *dev)
|
||||
{
|
||||
u8 data;
|
||||
|
||||
st33zp24_spi_read(dev, TPM_STS, &data, 1);
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_spi_get_burstcount return the burstcount address 0x19 0x1A
|
||||
* @param: chip, the chip description
|
||||
* return: the burstcount or -TPM_DRIVER_ERR in case of error.
|
||||
*/
|
||||
static int st33zp24_spi_get_burstcount(struct udevice *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_priv(dev);
|
||||
unsigned long start, stop;
|
||||
int burstcnt, status;
|
||||
u8 tpm_reg, temp;
|
||||
|
||||
/* wait for burstcount */
|
||||
start = get_timer(0);
|
||||
stop = chip->timeout_d;
|
||||
do {
|
||||
tpm_reg = TPM_STS + 1;
|
||||
status = st33zp24_spi_read(dev, tpm_reg, &temp, 1);
|
||||
if (status < 0)
|
||||
return -EBUSY;
|
||||
|
||||
tpm_reg = TPM_STS + 2;
|
||||
burstcnt = temp;
|
||||
status = st33zp24_spi_read(dev, tpm_reg, &temp, 1);
|
||||
if (status < 0)
|
||||
return -EBUSY;
|
||||
|
||||
burstcnt |= temp << 8;
|
||||
if (burstcnt)
|
||||
return burstcnt;
|
||||
udelay(TIS_SHORT_TIMEOUT_MS * 1000);
|
||||
} while (get_timer(start) < stop);
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_spi_cancel, cancel the current command execution or
|
||||
* set STS to COMMAND READY.
|
||||
* @param: chip, tpm_chip description.
|
||||
*/
|
||||
static void st33zp24_spi_cancel(struct udevice *dev)
|
||||
{
|
||||
u8 data;
|
||||
|
||||
data = TPM_STS_COMMAND_READY;
|
||||
st33zp24_spi_write(dev, TPM_STS, &data, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_spi_wait_for_stat wait for a TPM_STS value
|
||||
* @param: chip, the tpm chip description
|
||||
* @param: mask, the value mask to wait
|
||||
* @param: timeout, the timeout
|
||||
* @param: status,
|
||||
* @return: the tpm status, 0 if success, -ETIME if timeout is reached.
|
||||
*/
|
||||
static int st33zp24_spi_wait_for_stat(struct udevice *dev, u8 mask,
|
||||
unsigned long timeout, int *status)
|
||||
{
|
||||
unsigned long start, stop;
|
||||
|
||||
/* Check current status */
|
||||
*status = st33zp24_spi_status(dev);
|
||||
if ((*status & mask) == mask)
|
||||
return 0;
|
||||
|
||||
start = get_timer(0);
|
||||
stop = timeout;
|
||||
do {
|
||||
udelay(TPM_TIMEOUT_MS * 1000);
|
||||
*status = st33zp24_spi_status(dev);
|
||||
if ((*status & mask) == mask)
|
||||
return 0;
|
||||
} while (get_timer(start) < stop);
|
||||
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_spi_recv_data receive data
|
||||
* @param: chip, the tpm chip description
|
||||
* @param: buf, the buffer where the data are received
|
||||
* @param: count, the number of data to receive
|
||||
* @return: the number of bytes read from TPM FIFO.
|
||||
*/
|
||||
static int st33zp24_spi_recv_data(struct udevice *dev, u8 *buf, size_t count)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_priv(dev);
|
||||
int size = 0, burstcnt, len, ret, status;
|
||||
|
||||
while (size < count &&
|
||||
st33zp24_spi_wait_for_stat(dev, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
chip->timeout_c, &status) == 0) {
|
||||
burstcnt = st33zp24_spi_get_burstcount(dev);
|
||||
if (burstcnt < 0)
|
||||
return burstcnt;
|
||||
len = min_t(int, burstcnt, count - size);
|
||||
ret = st33zp24_spi_read(dev, TPM_DATA_FIFO, buf + size, len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
size += len;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_spi_recv received TPM response through TPM phy.
|
||||
* @param: chip, tpm_chip description.
|
||||
* @param: buf, the buffer to store data.
|
||||
* @param: count, the number of bytes that can received (sizeof buf).
|
||||
* @return: Returns zero in case of success else -EIO.
|
||||
*/
|
||||
static int st33zp24_spi_recv(struct udevice *dev, u8 *buf, size_t count)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_priv(dev);
|
||||
int size, expected;
|
||||
|
||||
if (!chip)
|
||||
return -ENODEV;
|
||||
|
||||
if (count < TPM_HEADER_SIZE) {
|
||||
size = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
size = st33zp24_spi_recv_data(dev, buf, TPM_HEADER_SIZE);
|
||||
if (size < TPM_HEADER_SIZE) {
|
||||
debug("TPM error, unable to read header\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
expected = get_unaligned_be32(buf + 2);
|
||||
if (expected > count) {
|
||||
size = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
size += st33zp24_spi_recv_data(dev, &buf[TPM_HEADER_SIZE],
|
||||
expected - TPM_HEADER_SIZE);
|
||||
if (size < expected) {
|
||||
debug("TPM error, unable to read remaining bytes of result\n");
|
||||
size = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
st33zp24_spi_cancel(dev);
|
||||
st33zp24_spi_release_locality(dev);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* st33zp24_spi_send send TPM commands through TPM phy.
|
||||
* @param: chip, tpm_chip description.
|
||||
* @param: buf, the buffer to send.
|
||||
* @param: len, the number of bytes to send.
|
||||
* @return: Returns zero in case of success else the negative error code.
|
||||
*/
|
||||
static int st33zp24_spi_send(struct udevice *dev, const u8 *buf, size_t len)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_priv(dev);
|
||||
u32 i, size;
|
||||
int burstcnt, ret, status;
|
||||
u8 data, tpm_stat;
|
||||
|
||||
if (!chip)
|
||||
return -ENODEV;
|
||||
if (len < TPM_HEADER_SIZE)
|
||||
return -EIO;
|
||||
|
||||
ret = st33zp24_spi_request_locality(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tpm_stat = st33zp24_spi_status(dev);
|
||||
if ((tpm_stat & TPM_STS_COMMAND_READY) == 0) {
|
||||
st33zp24_spi_cancel(dev);
|
||||
if (st33zp24_spi_wait_for_stat(dev, TPM_STS_COMMAND_READY,
|
||||
chip->timeout_b, &status) < 0) {
|
||||
ret = -ETIME;
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < len - 1;) {
|
||||
burstcnt = st33zp24_spi_get_burstcount(dev);
|
||||
if (burstcnt < 0)
|
||||
return burstcnt;
|
||||
|
||||
size = min_t(int, len - i - 1, burstcnt);
|
||||
ret = st33zp24_spi_write(dev, TPM_DATA_FIFO, buf + i, size);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
|
||||
i += size;
|
||||
}
|
||||
|
||||
tpm_stat = st33zp24_spi_status(dev);
|
||||
if ((tpm_stat & TPM_STS_DATA_EXPECT) == 0) {
|
||||
ret = -EIO;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
ret = st33zp24_spi_write(dev, TPM_DATA_FIFO, buf + len - 1, 1);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
|
||||
tpm_stat = st33zp24_spi_status(dev);
|
||||
if ((tpm_stat & TPM_STS_DATA_EXPECT) != 0) {
|
||||
ret = -EIO;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
data = TPM_STS_GO;
|
||||
ret = st33zp24_spi_write(dev, TPM_STS, &data, 1);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
|
||||
return len;
|
||||
|
||||
out_err:
|
||||
st33zp24_spi_cancel(dev);
|
||||
st33zp24_spi_release_locality(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int st33zp24_spi_cleanup(struct udevice *dev)
|
||||
{
|
||||
st33zp24_spi_cancel(dev);
|
||||
/*
|
||||
* The TPM needs some time to clean up here,
|
||||
* so we sleep rather than keeping the bus busy
|
||||
*/
|
||||
mdelay(2);
|
||||
st33zp24_spi_release_locality(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st33zp24_spi_init(struct udevice *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_priv(dev);
|
||||
struct st33zp24_spi_phy *phy = dev_get_platdata(dev);
|
||||
|
||||
chip->is_open = 1;
|
||||
|
||||
/* Default timeouts - these could move to the device tree */
|
||||
chip->timeout_a = TIS_SHORT_TIMEOUT_MS;
|
||||
chip->timeout_b = TIS_LONG_TIMEOUT_MS;
|
||||
chip->timeout_c = TIS_SHORT_TIMEOUT_MS;
|
||||
chip->timeout_d = TIS_SHORT_TIMEOUT_MS;
|
||||
|
||||
chip->locality = LOCALITY0;
|
||||
|
||||
phy->latency = st33zp24_spi_evaluate_latency(dev);
|
||||
if (phy->latency <= 0)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* A timeout query to TPM can be placed here.
|
||||
* Standard timeout values are used so far
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st33zp24_spi_open(struct udevice *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_priv(dev);
|
||||
int rc;
|
||||
|
||||
debug("%s: start\n", __func__);
|
||||
if (chip->is_open)
|
||||
return -EBUSY;
|
||||
|
||||
rc = st33zp24_spi_init(dev);
|
||||
if (rc < 0)
|
||||
chip->is_open = 0;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int st33zp24_spi_close(struct udevice *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_priv(dev);
|
||||
|
||||
if (chip->is_open) {
|
||||
st33zp24_spi_release_locality(dev);
|
||||
chip->is_open = 0;
|
||||
chip->vend_dev = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st33zp24_spi_get_desc(struct udevice *dev, char *buf, int size)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_priv(dev);
|
||||
|
||||
if (size < 50)
|
||||
return -ENOSPC;
|
||||
|
||||
return snprintf(buf, size, "1.2 TPM (%s, chip type %s device-id 0x%x)",
|
||||
chip->is_open ? "open" : "closed",
|
||||
dev->name,
|
||||
chip->vend_dev >> 16);
|
||||
}
|
||||
|
||||
const struct tpm_ops st33zp24_spi_tpm_ops = {
|
||||
.open = st33zp24_spi_open,
|
||||
.close = st33zp24_spi_close,
|
||||
.recv = st33zp24_spi_recv,
|
||||
.send = st33zp24_spi_send,
|
||||
.cleanup = st33zp24_spi_cleanup,
|
||||
.get_desc = st33zp24_spi_get_desc,
|
||||
};
|
||||
|
||||
static int st33zp24_spi_probe(struct udevice *dev)
|
||||
{
|
||||
struct tpm_chip_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
|
||||
uc_priv->duration_ms[TPM_SHORT] = TIS_SHORT_TIMEOUT_MS;
|
||||
uc_priv->duration_ms[TPM_MEDIUM] = TIS_LONG_TIMEOUT_MS;
|
||||
uc_priv->duration_ms[TPM_LONG] = TIS_LONG_TIMEOUT_MS;
|
||||
uc_priv->retry_time_ms = TPM_TIMEOUT_MS;
|
||||
|
||||
debug("ST33ZP24 SPI TPM from STMicroelectronics found\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st33zp24_spi_remove(struct udevice *dev)
|
||||
{
|
||||
st33zp24_spi_release_locality(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id st33zp24_spi_ids[] = {
|
||||
{ .compatible = "st,st33zp24-spi" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(st33zp24_spi_spi) = {
|
||||
.name = "st33zp24-spi",
|
||||
.id = UCLASS_TPM,
|
||||
.of_match = of_match_ptr(st33zp24_spi_ids),
|
||||
.probe = st33zp24_spi_probe,
|
||||
.remove = st33zp24_spi_remove,
|
||||
.ops = &st33zp24_spi_tpm_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct tpm_chip),
|
||||
.platdata_auto_alloc_size = sizeof(struct st33zp24_spi_phy),
|
||||
};
|
|
@ -139,16 +139,26 @@
|
|||
BOOT_TARGET_DEVICES_references_IDE_without_CONFIG_CMD_IDE
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_CMD_PCI_ENUM) || defined(CONFIG_DM_PCI)
|
||||
#define BOOTENV_RUN_NET_PCI_ENUM "run boot_net_pci_enum; "
|
||||
#define BOOTENV_SHARED_PCI \
|
||||
"boot_net_pci_enum=pci enum\0"
|
||||
#else
|
||||
#define BOOTENV_RUN_NET_PCI_ENUM
|
||||
#define BOOTENV_SHARED_PCI
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CMD_USB
|
||||
#define BOOTENV_RUN_USB_INIT "usb start; "
|
||||
#define BOOTENV_RUN_NET_USB_START "run boot_net_usb_start; "
|
||||
#define BOOTENV_SHARED_USB \
|
||||
"boot_net_usb_start=usb start\0" \
|
||||
"usb_boot=" \
|
||||
BOOTENV_RUN_USB_INIT \
|
||||
"usb start; " \
|
||||
BOOTENV_SHARED_BLKDEV_BODY(usb)
|
||||
#define BOOTENV_DEV_USB BOOTENV_DEV_BLKDEV
|
||||
#define BOOTENV_DEV_NAME_USB BOOTENV_DEV_NAME_BLKDEV
|
||||
#else
|
||||
#define BOOTENV_RUN_USB_INIT
|
||||
#define BOOTENV_RUN_NET_USB_START
|
||||
#define BOOTENV_SHARED_USB
|
||||
#define BOOTENV_DEV_USB \
|
||||
BOOT_TARGET_DEVICES_references_USB_without_CONFIG_CMD_USB
|
||||
|
@ -159,7 +169,8 @@
|
|||
#if defined(CONFIG_CMD_DHCP)
|
||||
#define BOOTENV_DEV_DHCP(devtypeu, devtypel, instance) \
|
||||
"bootcmd_dhcp=" \
|
||||
BOOTENV_RUN_USB_INIT \
|
||||
BOOTENV_RUN_NET_USB_START \
|
||||
BOOTENV_RUN_NET_PCI_ENUM \
|
||||
"if dhcp ${scriptaddr} ${boot_script_dhcp}; then " \
|
||||
"source ${scriptaddr}; " \
|
||||
"fi\0"
|
||||
|
@ -175,7 +186,8 @@
|
|||
#if defined(CONFIG_CMD_DHCP) && defined(CONFIG_CMD_PXE)
|
||||
#define BOOTENV_DEV_PXE(devtypeu, devtypel, instance) \
|
||||
"bootcmd_pxe=" \
|
||||
BOOTENV_RUN_USB_INIT \
|
||||
BOOTENV_RUN_NET_USB_START \
|
||||
BOOTENV_RUN_NET_PCI_ENUM \
|
||||
"dhcp; " \
|
||||
"if pxe get; then " \
|
||||
"pxe boot; " \
|
||||
|
@ -199,6 +211,7 @@
|
|||
#define BOOTENV \
|
||||
BOOTENV_SHARED_HOST \
|
||||
BOOTENV_SHARED_MMC \
|
||||
BOOTENV_SHARED_PCI \
|
||||
BOOTENV_SHARED_USB \
|
||||
BOOTENV_SHARED_SATA \
|
||||
BOOTENV_SHARED_SCSI \
|
||||
|
|
|
@ -65,19 +65,6 @@ int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
|
|||
*/
|
||||
int device_probe(struct udevice *dev);
|
||||
|
||||
/**
|
||||
* device_probe() - Probe a child device, activating it
|
||||
*
|
||||
* Activate a device so that it is ready for use. All its parents are probed
|
||||
* first. The child is provided with parent data if parent_priv is not NULL.
|
||||
*
|
||||
* @dev: Pointer to device to probe
|
||||
* @parent_priv: Pointer to parent data. If non-NULL then this is provided to
|
||||
* the child.
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int device_probe_child(struct udevice *dev, void *parent_priv);
|
||||
|
||||
/**
|
||||
* device_remove() - Remove a device, de-activating it
|
||||
*
|
||||
|
|
|
@ -262,7 +262,7 @@ int tpm_init(void)
|
|||
struct udevice *dev;
|
||||
|
||||
err = uclass_first_device(UCLASS_TPM, &dev);
|
||||
if (err)
|
||||
if (err || !dev)
|
||||
return err;
|
||||
return tpm_open(dev);
|
||||
}
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
DFU TEST CASE DESCRIPTION:
|
||||
|
||||
The prerequisites for running this script are assured by
|
||||
dfu_gadget_test_init.sh, which is automatically invoked by dfu_gadget_test.sh.
|
||||
In this file user is able to generate their own set of test files by altering
|
||||
the default set of TEST_FILES_SIZES variable.
|
||||
The dfu_gadget_test_init.sh would generate test images only if they are not
|
||||
already generated.
|
||||
|
||||
On the target device, environment variable "dfu_alt_info" must contain at
|
||||
least:
|
||||
|
||||
dfu_test.bin fat 0 6;dfudummy.bin fat 0 6
|
||||
|
||||
Depending on your device, you may need to replace "fat" with
|
||||
"ext4", and "6" with the relevant partition number. For reference please
|
||||
consult the config file for TRATS/TRATS2 devices
|
||||
(../../include/configs/trats{2}.h)
|
||||
|
||||
One can use fat, ext4 or any other supported file system supported by U-Boot.
|
||||
These can be created by exporting storage devices via UMS (ums 0 mmc 0) and
|
||||
using standard tools on host (like mkfs.ext4).
|
||||
|
||||
Example usage:
|
||||
1. On the target:
|
||||
setenv dfu_alt_info dfu_test.bin fat 0 6\;dfudummy.bin fat 0 6
|
||||
dfu 0 mmc 0
|
||||
2. On the host:
|
||||
test/dfu/dfu_gadget_test.sh X Y [test file name] [usb device vendor:product]
|
||||
e.g. test/dfu/dfu_gadget_test.sh 0 1
|
||||
or
|
||||
e.g. test/dfu/dfu_gadget_test.sh 0 1 ./dat_960.img
|
||||
or
|
||||
e.g. test/dfu/dfu_gadget_test.sh 0 1 0451:d022
|
||||
or
|
||||
e.g. test/dfu/dfu_gadget_test.sh 0 1 ./dat_960.img 0451:d022
|
||||
|
||||
... where X and Y are dfu_test.bin's and dfudummy.bin's alt setting numbers.
|
||||
They can be obtained from dfu-util -l or $dfu_alt_info.
|
||||
It is also possible to pass optional [test file name] to force the script to
|
||||
test one particular file.
|
||||
If many DFU devices are connected, it may be useful to filter on USB
|
||||
vendor/product ID (0451:d022).
|
||||
One can get them by running "lsusb" command on a host PC.
|
|
@ -1,108 +0,0 @@
|
|||
#! /bin/bash
|
||||
|
||||
# Copyright (C) 2014 Samsung Electronics
|
||||
# Lukasz Majewski <l.majewski@samsung.com>
|
||||
#
|
||||
# Script fixes, enhancements and testing:
|
||||
# Stephen Warren <swarren@nvidia.com>
|
||||
#
|
||||
# DFU operation test script
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
set -e # any command return if not equal to zero
|
||||
clear
|
||||
|
||||
COLOUR_RED="\33[31m"
|
||||
COLOUR_GREEN="\33[32m"
|
||||
COLOUR_DEFAULT="\33[0m"
|
||||
|
||||
DIR=./
|
||||
SUFFIX=img
|
||||
RCV_DIR=rcv/
|
||||
LOG_FILE=./log/log-`date +%d-%m-%Y_%H-%M-%S`
|
||||
|
||||
cd `dirname $0`
|
||||
./dfu_gadget_test_init.sh
|
||||
|
||||
cleanup () {
|
||||
rm -rf $DIR$RCV_DIR
|
||||
}
|
||||
|
||||
die () {
|
||||
printf " $COLOUR_RED FAILED $COLOUR_DEFAULT \n"
|
||||
cleanup
|
||||
exit 1
|
||||
}
|
||||
|
||||
calculate_md5sum () {
|
||||
MD5SUM=`md5sum $1`
|
||||
MD5SUM=`echo $MD5SUM | cut -d ' ' -f1`
|
||||
echo "md5sum:"$MD5SUM
|
||||
}
|
||||
|
||||
dfu_test_file () {
|
||||
printf "$COLOUR_GREEN ========================================================================================= $COLOUR_DEFAULT\n"
|
||||
printf "File:$COLOUR_GREEN %s $COLOUR_DEFAULT\n" $1
|
||||
|
||||
dfu-util $USB_DEV -D $1 -a $TARGET_ALT_SETTING >> $LOG_FILE 2>&1 || die $?
|
||||
|
||||
echo -n "TX: "
|
||||
calculate_md5sum $1
|
||||
|
||||
MD5_TX=$MD5SUM
|
||||
|
||||
dfu-util $USB_DEV -D ${DIR}/dfudummy.bin -a $TARGET_ALT_SETTING_B >> $LOG_FILE 2>&1 || die $?
|
||||
|
||||
N_FILE=$DIR$RCV_DIR${1:2}"_rcv"
|
||||
|
||||
dfu-util $USB_DEV -U $N_FILE -a $TARGET_ALT_SETTING >> $LOG_FILE 2>&1 || die $?
|
||||
|
||||
echo -n "RX: "
|
||||
calculate_md5sum $N_FILE
|
||||
MD5_RX=$MD5SUM
|
||||
|
||||
if [ "$MD5_TX" == "$MD5_RX" ]; then
|
||||
printf " $COLOUR_GREEN -------> OK $COLOUR_DEFAULT \n"
|
||||
else
|
||||
printf " $COLOUR_RED -------> FAILED $COLOUR_DEFAULT \n"
|
||||
cleanup
|
||||
exit 1
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
printf "$COLOUR_GREEN========================================================================================= $COLOUR_DEFAULT\n"
|
||||
echo "DFU EP0 transmission test program"
|
||||
echo "Trouble shoot -> disable DBG (even the KERN_DEBUG) in the UDC driver"
|
||||
echo "@ -> TRATS2 # dfu 0 mmc 0"
|
||||
cleanup
|
||||
mkdir -p $DIR$RCV_DIR
|
||||
touch $LOG_FILE
|
||||
|
||||
if [ $# -eq 0 ]
|
||||
then
|
||||
printf " $COLOUR_RED Please pass alt setting number!! $COLOUR_DEFAULT \n"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
TARGET_ALT_SETTING=$1
|
||||
TARGET_ALT_SETTING_B=$2
|
||||
|
||||
file=$3
|
||||
[[ $3 == *':'* ]] && USB_DEV="-d $3" && file=""
|
||||
[ $# -eq 4 ] && USB_DEV="-d $4"
|
||||
|
||||
if [ -n "$file" ]
|
||||
then
|
||||
dfu_test_file $file
|
||||
else
|
||||
for f in $DIR*.$SUFFIX
|
||||
do
|
||||
dfu_test_file $f
|
||||
done
|
||||
fi
|
||||
|
||||
cleanup
|
||||
|
||||
exit 0
|
|
@ -1,45 +0,0 @@
|
|||
#! /bin/bash
|
||||
|
||||
# Copyright (C) 2014 Samsung Electronics
|
||||
# Lukasz Majewski <l.majewski@samsung.com>
|
||||
#
|
||||
# Script fixes, enhancements and testing:
|
||||
# Stephen Warren <swarren@nvidia.com>
|
||||
#
|
||||
# Script for test files generation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
set -e # any command return if not equal to zero
|
||||
clear
|
||||
|
||||
COLOUR_RED="\33[31m"
|
||||
COLOUR_GREEN="\33[32m"
|
||||
COLOUR_DEFAULT="\33[0m"
|
||||
|
||||
LOG_DIR="./log"
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
TEST_FILES_SIZES="63 64 65 127 128 129 4095 4096 4097 959 960 961 1048575 1048576 8M"
|
||||
else
|
||||
TEST_FILES_SIZES=$@
|
||||
fi
|
||||
|
||||
printf "Init script for generating data necessary for DFU test script"
|
||||
|
||||
if [ ! -d $LOG_DIR ]; then
|
||||
`mkdir $LOG_DIR`
|
||||
fi
|
||||
|
||||
for size in $TEST_FILES_SIZES
|
||||
do
|
||||
FILE="./dat_$size.img"
|
||||
if [ ! -f $FILE ]; then
|
||||
dd if=/dev/urandom of="./dat_$size.img" bs=$size count=1 > /dev/null 2>&1 || exit $?
|
||||
fi
|
||||
done
|
||||
dd if=/dev/urandom of="./dfudummy.bin" bs=1024 count=1 > /dev/null 2>&1 || exit $?
|
||||
|
||||
printf "$COLOUR_GREEN OK $COLOUR_DEFAULT \n"
|
||||
|
||||
exit 0
|
|
@ -81,6 +81,8 @@ static int dm_test_main(const char *test_name)
|
|||
struct unit_test *test;
|
||||
int run_count;
|
||||
|
||||
uts->fail_count = 0;
|
||||
|
||||
/*
|
||||
* If we have no device tree, or it only has a root node, then these
|
||||
* tests clearly aren't going to work...
|
||||
|
|
|
@ -29,7 +29,7 @@ log = None
|
|||
console = None
|
||||
|
||||
def mkdir_p(path):
|
||||
'''Create a directory path.
|
||||
"""Create a directory path.
|
||||
|
||||
This includes creating any intermediate/parent directories. Any errors
|
||||
caused due to already extant directories are ignored.
|
||||
|
@ -39,7 +39,7 @@ def mkdir_p(path):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
try:
|
||||
os.makedirs(path)
|
||||
|
@ -50,14 +50,14 @@ def mkdir_p(path):
|
|||
raise
|
||||
|
||||
def pytest_addoption(parser):
|
||||
'''pytest hook: Add custom command-line options to the cmdline parser.
|
||||
"""pytest hook: Add custom command-line options to the cmdline parser.
|
||||
|
||||
Args:
|
||||
parser: The pytest command-line parser.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
parser.addoption('--build-dir', default=None,
|
||||
help='U-Boot build directory (O=)')
|
||||
|
@ -73,14 +73,14 @@ def pytest_addoption(parser):
|
|||
help='Compile U-Boot before running tests')
|
||||
|
||||
def pytest_configure(config):
|
||||
'''pytest hook: Perform custom initialization at startup time.
|
||||
"""pytest hook: Perform custom initialization at startup time.
|
||||
|
||||
Args:
|
||||
config: The pytest configuration.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
global log
|
||||
global console
|
||||
|
@ -190,7 +190,7 @@ def pytest_configure(config):
|
|||
console = u_boot_console_exec_attach.ConsoleExecAttach(log, ubconfig)
|
||||
|
||||
def pytest_generate_tests(metafunc):
|
||||
'''pytest hook: parameterize test functions based on custom rules.
|
||||
"""pytest hook: parameterize test functions based on custom rules.
|
||||
|
||||
If a test function takes parameter(s) (fixture names) of the form brd__xxx
|
||||
or env__xxx, the brd and env configuration dictionaries are consulted to
|
||||
|
@ -202,7 +202,7 @@ def pytest_generate_tests(metafunc):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
subconfigs = {
|
||||
'brd': console.config.brd,
|
||||
|
@ -225,28 +225,37 @@ def pytest_generate_tests(metafunc):
|
|||
# ... otherwise, see if there's a key that contains a list of
|
||||
# values to use instead.
|
||||
vals = subconfig.get(fn + 's', [])
|
||||
metafunc.parametrize(fn, vals)
|
||||
def fixture_id(index, val):
|
||||
try:
|
||||
return val["fixture_id"]
|
||||
except:
|
||||
return fn + str(index)
|
||||
ids = [fixture_id(index, val) for (index, val) in enumerate(vals)]
|
||||
metafunc.parametrize(fn, vals, ids=ids)
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
@pytest.fixture(scope='function')
|
||||
def u_boot_console(request):
|
||||
'''Generate the value of a test's u_boot_console fixture.
|
||||
"""Generate the value of a test's u_boot_console fixture.
|
||||
|
||||
Args:
|
||||
request: The pytest request.
|
||||
|
||||
Returns:
|
||||
The fixture value.
|
||||
'''
|
||||
"""
|
||||
|
||||
console.ensure_spawned()
|
||||
return console
|
||||
|
||||
tests_not_run = set()
|
||||
tests_failed = set()
|
||||
tests_xpassed = set()
|
||||
tests_xfailed = set()
|
||||
tests_skipped = set()
|
||||
tests_passed = set()
|
||||
|
||||
def pytest_itemcollected(item):
|
||||
'''pytest hook: Called once for each test found during collection.
|
||||
"""pytest hook: Called once for each test found during collection.
|
||||
|
||||
This enables our custom result analysis code to see the list of all tests
|
||||
that should eventually be run.
|
||||
|
@ -256,12 +265,12 @@ def pytest_itemcollected(item):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
tests_not_run.add(item.name)
|
||||
|
||||
def cleanup():
|
||||
'''Clean up all global state.
|
||||
"""Clean up all global state.
|
||||
|
||||
Executed (via atexit) once the entire test process is complete. This
|
||||
includes logging the status of all tests, and the identity of any failed
|
||||
|
@ -272,7 +281,7 @@ def cleanup():
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
if console:
|
||||
console.close()
|
||||
|
@ -282,6 +291,14 @@ def cleanup():
|
|||
log.status_skipped('%d skipped' % len(tests_skipped))
|
||||
for test in tests_skipped:
|
||||
log.status_skipped('... ' + test)
|
||||
if tests_xpassed:
|
||||
log.status_xpass('%d xpass' % len(tests_xpassed))
|
||||
for test in tests_xpassed:
|
||||
log.status_xpass('... ' + test)
|
||||
if tests_xfailed:
|
||||
log.status_xfail('%d xfail' % len(tests_xfailed))
|
||||
for test in tests_xfailed:
|
||||
log.status_xfail('... ' + test)
|
||||
if tests_failed:
|
||||
log.status_fail('%d failed' % len(tests_failed))
|
||||
for test in tests_failed:
|
||||
|
@ -294,7 +311,7 @@ def cleanup():
|
|||
atexit.register(cleanup)
|
||||
|
||||
def setup_boardspec(item):
|
||||
'''Process any 'boardspec' marker for a test.
|
||||
"""Process any 'boardspec' marker for a test.
|
||||
|
||||
Such a marker lists the set of board types that a test does/doesn't
|
||||
support. If tests are being executed on an unsupported board, the test is
|
||||
|
@ -305,7 +322,7 @@ def setup_boardspec(item):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
mark = item.get_marker('boardspec')
|
||||
if not mark:
|
||||
|
@ -322,7 +339,7 @@ def setup_boardspec(item):
|
|||
pytest.skip('board not supported')
|
||||
|
||||
def setup_buildconfigspec(item):
|
||||
'''Process any 'buildconfigspec' marker for a test.
|
||||
"""Process any 'buildconfigspec' marker for a test.
|
||||
|
||||
Such a marker lists some U-Boot configuration feature that the test
|
||||
requires. If tests are being executed on an U-Boot build that doesn't
|
||||
|
@ -333,7 +350,7 @@ def setup_buildconfigspec(item):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
mark = item.get_marker('buildconfigspec')
|
||||
if not mark:
|
||||
|
@ -343,7 +360,7 @@ def setup_buildconfigspec(item):
|
|||
pytest.skip('.config feature not enabled')
|
||||
|
||||
def pytest_runtest_setup(item):
|
||||
'''pytest hook: Configure (set up) a test item.
|
||||
"""pytest hook: Configure (set up) a test item.
|
||||
|
||||
Called once for each test to perform any custom configuration. This hook
|
||||
is used to skip the test if certain conditions apply.
|
||||
|
@ -353,14 +370,14 @@ def pytest_runtest_setup(item):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
log.start_section(item.name)
|
||||
setup_boardspec(item)
|
||||
setup_buildconfigspec(item)
|
||||
|
||||
def pytest_runtest_protocol(item, nextitem):
|
||||
'''pytest hook: Called to execute a test.
|
||||
"""pytest hook: Called to execute a test.
|
||||
|
||||
This hook wraps the standard pytest runtestprotocol() function in order
|
||||
to acquire visibility into, and record, each test function's result.
|
||||
|
@ -371,36 +388,45 @@ def pytest_runtest_protocol(item, nextitem):
|
|||
|
||||
Returns:
|
||||
A list of pytest reports (test result data).
|
||||
'''
|
||||
"""
|
||||
|
||||
reports = runtestprotocol(item, nextitem=nextitem)
|
||||
failed = None
|
||||
skipped = None
|
||||
|
||||
failure_cleanup = False
|
||||
test_list = tests_passed
|
||||
msg = 'OK'
|
||||
msg_log = log.status_pass
|
||||
for report in reports:
|
||||
if report.outcome == 'failed':
|
||||
failed = report
|
||||
if hasattr(report, 'wasxfail'):
|
||||
test_list = tests_xpassed
|
||||
msg = 'XPASSED'
|
||||
msg_log = log.status_xpass
|
||||
else:
|
||||
failure_cleanup = True
|
||||
test_list = tests_failed
|
||||
msg = 'FAILED:\n' + str(report.longrepr)
|
||||
msg_log = log.status_fail
|
||||
break
|
||||
if report.outcome == 'skipped':
|
||||
if not skipped:
|
||||
skipped = report
|
||||
if hasattr(report, 'wasxfail'):
|
||||
failure_cleanup = True
|
||||
test_list = tests_xfailed
|
||||
msg = 'XFAILED:\n' + str(report.longrepr)
|
||||
msg_log = log.status_xfail
|
||||
break
|
||||
test_list = tests_skipped
|
||||
msg = 'SKIPPED:\n' + str(report.longrepr)
|
||||
msg_log = log.status_skipped
|
||||
|
||||
if failed:
|
||||
tests_failed.add(item.name)
|
||||
elif skipped:
|
||||
tests_skipped.add(item.name)
|
||||
else:
|
||||
tests_passed.add(item.name)
|
||||
if failure_cleanup:
|
||||
console.drain_console()
|
||||
|
||||
test_list.add(item.name)
|
||||
tests_not_run.remove(item.name)
|
||||
|
||||
try:
|
||||
if failed:
|
||||
msg = 'FAILED:\n' + str(failed.longrepr)
|
||||
log.status_fail(msg)
|
||||
elif skipped:
|
||||
msg = 'SKIPPED:\n' + str(skipped.longrepr)
|
||||
log.status_skipped(msg)
|
||||
else:
|
||||
log.status_pass('OK')
|
||||
msg_log(msg)
|
||||
except:
|
||||
# If something went wrong with logging, it's better to let the test
|
||||
# process continue, which may report other exceptions that triggered
|
||||
|
@ -416,7 +442,7 @@ def pytest_runtest_protocol(item, nextitem):
|
|||
|
||||
log.end_section(item.name)
|
||||
|
||||
if failed:
|
||||
if failure_cleanup:
|
||||
console.cleanup_spawn()
|
||||
|
||||
return reports
|
||||
|
|
|
@ -83,6 +83,14 @@ pre {
|
|||
color: #ffff00
|
||||
}
|
||||
|
||||
.status-xfail {
|
||||
color: #ff7f00
|
||||
}
|
||||
|
||||
.status-xpass {
|
||||
color: #ff7f00
|
||||
}
|
||||
|
||||
.status-fail {
|
||||
color: #ff0000
|
||||
}
|
||||
|
|
|
@ -14,12 +14,12 @@ import subprocess
|
|||
mod_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
class LogfileStream(object):
|
||||
'''A file-like object used to write a single logical stream of data into
|
||||
"""A file-like object used to write a single logical stream of data into
|
||||
a multiplexed log file. Objects of this type should be created by factory
|
||||
functions in the Logfile class rather than directly.'''
|
||||
functions in the Logfile class rather than directly."""
|
||||
|
||||
def __init__(self, logfile, name, chained_file):
|
||||
'''Initialize a new object.
|
||||
"""Initialize a new object.
|
||||
|
||||
Args:
|
||||
logfile: The Logfile object to log to.
|
||||
|
@ -29,26 +29,26 @@ class LogfileStream(object):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self.logfile = logfile
|
||||
self.name = name
|
||||
self.chained_file = chained_file
|
||||
|
||||
def close(self):
|
||||
'''Dummy function so that this class is "file-like".
|
||||
"""Dummy function so that this class is "file-like".
|
||||
|
||||
Args:
|
||||
None.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
def write(self, data, implicit=False):
|
||||
'''Write data to the log stream.
|
||||
"""Write data to the log stream.
|
||||
|
||||
Args:
|
||||
data: The data to write tot he file.
|
||||
|
@ -60,33 +60,33 @@ class LogfileStream(object):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self.logfile.write(self, data, implicit)
|
||||
if self.chained_file:
|
||||
self.chained_file.write(data)
|
||||
|
||||
def flush(self):
|
||||
'''Flush the log stream, to ensure correct log interleaving.
|
||||
"""Flush the log stream, to ensure correct log interleaving.
|
||||
|
||||
Args:
|
||||
None.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self.logfile.flush()
|
||||
if self.chained_file:
|
||||
self.chained_file.flush()
|
||||
|
||||
class RunAndLog(object):
|
||||
'''A utility object used to execute sub-processes and log their output to
|
||||
"""A utility object used to execute sub-processes and log their output to
|
||||
a multiplexed log file. Objects of this type should be created by factory
|
||||
functions in the Logfile class rather than directly.'''
|
||||
functions in the Logfile class rather than directly."""
|
||||
|
||||
def __init__(self, logfile, name, chained_file):
|
||||
'''Initialize a new object.
|
||||
"""Initialize a new object.
|
||||
|
||||
Args:
|
||||
logfile: The Logfile object to log to.
|
||||
|
@ -96,29 +96,33 @@ class RunAndLog(object):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self.logfile = logfile
|
||||
self.name = name
|
||||
self.chained_file = chained_file
|
||||
|
||||
def close(self):
|
||||
'''Clean up any resources managed by this object.'''
|
||||
"""Clean up any resources managed by this object."""
|
||||
pass
|
||||
|
||||
def run(self, cmd, cwd=None):
|
||||
'''Run a command as a sub-process, and log the results.
|
||||
def run(self, cmd, cwd=None, ignore_errors=False):
|
||||
"""Run a command as a sub-process, and log the results.
|
||||
|
||||
Args:
|
||||
cmd: The command to execute.
|
||||
cwd: The directory to run the command in. Can be None to use the
|
||||
current directory.
|
||||
ignore_errors: Indicate whether to ignore errors. If True, the
|
||||
function will simply return if the command cannot be executed
|
||||
or exits with an error code, otherwise an exception will be
|
||||
raised if such problems occur.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
msg = "+" + " ".join(cmd) + "\n"
|
||||
msg = '+' + ' '.join(cmd) + '\n'
|
||||
if self.chained_file:
|
||||
self.chained_file.write(msg)
|
||||
self.logfile.write(self, msg)
|
||||
|
@ -148,7 +152,7 @@ class RunAndLog(object):
|
|||
exception = e
|
||||
if output and not output.endswith('\n'):
|
||||
output += '\n'
|
||||
if exit_status and not exception:
|
||||
if exit_status and not exception and not ignore_errors:
|
||||
exception = Exception('Exit code: ' + str(exit_status))
|
||||
if exception:
|
||||
output += str(exception) + '\n'
|
||||
|
@ -159,13 +163,13 @@ class RunAndLog(object):
|
|||
raise exception
|
||||
|
||||
class SectionCtxMgr(object):
|
||||
'''A context manager for Python's "with" statement, which allows a certain
|
||||
"""A context manager for Python's "with" statement, which allows a certain
|
||||
portion of test code to be logged to a separate section of the log file.
|
||||
Objects of this type should be created by factory functions in the Logfile
|
||||
class rather than directly.'''
|
||||
class rather than directly."""
|
||||
|
||||
def __init__(self, log, marker):
|
||||
'''Initialize a new object.
|
||||
"""Initialize a new object.
|
||||
|
||||
Args:
|
||||
log: The Logfile object to log to.
|
||||
|
@ -173,7 +177,7 @@ class SectionCtxMgr(object):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self.log = log
|
||||
self.marker = marker
|
||||
|
@ -185,35 +189,35 @@ class SectionCtxMgr(object):
|
|||
self.log.end_section(self.marker)
|
||||
|
||||
class Logfile(object):
|
||||
'''Generates an HTML-formatted log file containing multiple streams of
|
||||
data, each represented in a well-delineated/-structured fashion.'''
|
||||
"""Generates an HTML-formatted log file containing multiple streams of
|
||||
data, each represented in a well-delineated/-structured fashion."""
|
||||
|
||||
def __init__(self, fn):
|
||||
'''Initialize a new object.
|
||||
"""Initialize a new object.
|
||||
|
||||
Args:
|
||||
fn: The filename to write to.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self.f = open(fn, "wt")
|
||||
self.f = open(fn, 'wt')
|
||||
self.last_stream = None
|
||||
self.blocks = []
|
||||
self.cur_evt = 1
|
||||
shutil.copy(mod_dir + "/multiplexed_log.css", os.path.dirname(fn))
|
||||
self.f.write("""\
|
||||
shutil.copy(mod_dir + '/multiplexed_log.css', os.path.dirname(fn))
|
||||
self.f.write('''\
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="multiplexed_log.css">
|
||||
</head>
|
||||
<body>
|
||||
<tt>
|
||||
""")
|
||||
''')
|
||||
|
||||
def close(self):
|
||||
'''Close the log file.
|
||||
"""Close the log file.
|
||||
|
||||
After calling this function, no more data may be written to the log.
|
||||
|
||||
|
@ -222,22 +226,22 @@ class Logfile(object):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self.f.write("""\
|
||||
self.f.write('''\
|
||||
</tt>
|
||||
</body>
|
||||
</html>
|
||||
""")
|
||||
''')
|
||||
self.f.close()
|
||||
|
||||
# The set of characters that should be represented as hexadecimal codes in
|
||||
# the log file.
|
||||
_nonprint = ("%" + "".join(chr(c) for c in range(0, 32) if c not in (9, 10)) +
|
||||
"".join(chr(c) for c in range(127, 256)))
|
||||
_nonprint = ('%' + ''.join(chr(c) for c in range(0, 32) if c not in (9, 10)) +
|
||||
''.join(chr(c) for c in range(127, 256)))
|
||||
|
||||
def _escape(self, data):
|
||||
'''Render data format suitable for inclusion in an HTML document.
|
||||
"""Render data format suitable for inclusion in an HTML document.
|
||||
|
||||
This includes HTML-escaping certain characters, and translating
|
||||
control characters to a hexadecimal representation.
|
||||
|
@ -247,36 +251,36 @@ class Logfile(object):
|
|||
|
||||
Returns:
|
||||
An escaped version of the data.
|
||||
'''
|
||||
"""
|
||||
|
||||
data = data.replace(chr(13), "")
|
||||
data = "".join((c in self._nonprint) and ("%%%02x" % ord(c)) or
|
||||
data = data.replace(chr(13), '')
|
||||
data = ''.join((c in self._nonprint) and ('%%%02x' % ord(c)) or
|
||||
c for c in data)
|
||||
data = cgi.escape(data)
|
||||
return data
|
||||
|
||||
def _terminate_stream(self):
|
||||
'''Write HTML to the log file to terminate the current stream's data.
|
||||
"""Write HTML to the log file to terminate the current stream's data.
|
||||
|
||||
Args:
|
||||
None.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self.cur_evt += 1
|
||||
if not self.last_stream:
|
||||
return
|
||||
self.f.write("</pre>\n")
|
||||
self.f.write("<div class=\"stream-trailer\" id=\"" +
|
||||
self.last_stream.name + "\">End stream: " +
|
||||
self.last_stream.name + "</div>\n")
|
||||
self.f.write("</div>\n")
|
||||
self.f.write('</pre>\n')
|
||||
self.f.write('<div class="stream-trailer" id="' +
|
||||
self.last_stream.name + '">End stream: ' +
|
||||
self.last_stream.name + '</div>\n')
|
||||
self.f.write('</div>\n')
|
||||
self.last_stream = None
|
||||
|
||||
def _note(self, note_type, msg):
|
||||
'''Write a note or one-off message to the log file.
|
||||
"""Write a note or one-off message to the log file.
|
||||
|
||||
Args:
|
||||
note_type: The type of note. This must be a value supported by the
|
||||
|
@ -285,32 +289,32 @@ class Logfile(object):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self._terminate_stream()
|
||||
self.f.write("<div class=\"" + note_type + "\">\n<pre>")
|
||||
self.f.write('<div class="' + note_type + '">\n<pre>')
|
||||
self.f.write(self._escape(msg))
|
||||
self.f.write("\n</pre></div>\n")
|
||||
self.f.write('\n</pre></div>\n')
|
||||
|
||||
def start_section(self, marker):
|
||||
'''Begin a new nested section in the log file.
|
||||
"""Begin a new nested section in the log file.
|
||||
|
||||
Args:
|
||||
marker: The name of the section that is starting.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self._terminate_stream()
|
||||
self.blocks.append(marker)
|
||||
blk_path = "/".join(self.blocks)
|
||||
self.f.write("<div class=\"section\" id=\"" + blk_path + "\">\n")
|
||||
self.f.write("<div class=\"section-header\" id=\"" + blk_path +
|
||||
"\">Section: " + blk_path + "</div>\n")
|
||||
blk_path = '/'.join(self.blocks)
|
||||
self.f.write('<div class="section" id="' + blk_path + '">\n')
|
||||
self.f.write('<div class="section-header" id="' + blk_path +
|
||||
'">Section: ' + blk_path + '</div>\n')
|
||||
|
||||
def end_section(self, marker):
|
||||
'''Terminate the current nested section in the log file.
|
||||
"""Terminate the current nested section in the log file.
|
||||
|
||||
This function validates proper nesting of start_section() and
|
||||
end_section() calls. If a mismatch is found, an exception is raised.
|
||||
|
@ -320,20 +324,20 @@ class Logfile(object):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
if (not self.blocks) or (marker != self.blocks[-1]):
|
||||
raise Exception("Block nesting mismatch: \"%s\" \"%s\"" %
|
||||
(marker, "/".join(self.blocks)))
|
||||
raise Exception('Block nesting mismatch: "%s" "%s"' %
|
||||
(marker, '/'.join(self.blocks)))
|
||||
self._terminate_stream()
|
||||
blk_path = "/".join(self.blocks)
|
||||
self.f.write("<div class=\"section-trailer\" id=\"section-trailer-" +
|
||||
blk_path + "\">End section: " + blk_path + "</div>\n")
|
||||
self.f.write("</div>\n")
|
||||
blk_path = '/'.join(self.blocks)
|
||||
self.f.write('<div class="section-trailer" id="section-trailer-' +
|
||||
blk_path + '">End section: ' + blk_path + '</div>\n')
|
||||
self.f.write('</div>\n')
|
||||
self.blocks.pop()
|
||||
|
||||
def section(self, marker):
|
||||
'''Create a temporary section in the log file.
|
||||
"""Create a temporary section in the log file.
|
||||
|
||||
This function creates a context manager for Python's "with" statement,
|
||||
which allows a certain portion of test code to be logged to a separate
|
||||
|
@ -348,96 +352,120 @@ class Logfile(object):
|
|||
|
||||
Returns:
|
||||
A context manager object.
|
||||
'''
|
||||
"""
|
||||
|
||||
return SectionCtxMgr(self, marker)
|
||||
|
||||
def error(self, msg):
|
||||
'''Write an error note to the log file.
|
||||
"""Write an error note to the log file.
|
||||
|
||||
Args:
|
||||
msg: A message describing the error.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self._note("error", msg)
|
||||
|
||||
def warning(self, msg):
|
||||
'''Write an warning note to the log file.
|
||||
"""Write an warning note to the log file.
|
||||
|
||||
Args:
|
||||
msg: A message describing the warning.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self._note("warning", msg)
|
||||
|
||||
def info(self, msg):
|
||||
'''Write an informational note to the log file.
|
||||
"""Write an informational note to the log file.
|
||||
|
||||
Args:
|
||||
msg: An informational message.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self._note("info", msg)
|
||||
|
||||
def action(self, msg):
|
||||
'''Write an action note to the log file.
|
||||
"""Write an action note to the log file.
|
||||
|
||||
Args:
|
||||
msg: A message describing the action that is being logged.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self._note("action", msg)
|
||||
|
||||
def status_pass(self, msg):
|
||||
'''Write a note to the log file describing test(s) which passed.
|
||||
"""Write a note to the log file describing test(s) which passed.
|
||||
|
||||
Args:
|
||||
msg: A message describing passed test(s).
|
||||
msg: A message describing the passed test(s).
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self._note("status-pass", msg)
|
||||
|
||||
def status_skipped(self, msg):
|
||||
'''Write a note to the log file describing skipped test(s).
|
||||
"""Write a note to the log file describing skipped test(s).
|
||||
|
||||
Args:
|
||||
msg: A message describing passed test(s).
|
||||
msg: A message describing the skipped test(s).
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self._note("status-skipped", msg)
|
||||
|
||||
def status_fail(self, msg):
|
||||
'''Write a note to the log file describing failed test(s).
|
||||
def status_xfail(self, msg):
|
||||
"""Write a note to the log file describing xfailed test(s).
|
||||
|
||||
Args:
|
||||
msg: A message describing passed test(s).
|
||||
msg: A message describing the xfailed test(s).
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self._note("status-xfail", msg)
|
||||
|
||||
def status_xpass(self, msg):
|
||||
"""Write a note to the log file describing xpassed test(s).
|
||||
|
||||
Args:
|
||||
msg: A message describing the xpassed test(s).
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
|
||||
self._note("status-xpass", msg)
|
||||
|
||||
def status_fail(self, msg):
|
||||
"""Write a note to the log file describing failed test(s).
|
||||
|
||||
Args:
|
||||
msg: A message describing the failed test(s).
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
|
||||
self._note("status-fail", msg)
|
||||
|
||||
def get_stream(self, name, chained_file=None):
|
||||
'''Create an object to log a single stream's data into the log file.
|
||||
"""Create an object to log a single stream's data into the log file.
|
||||
|
||||
This creates a "file-like" object that can be written to in order to
|
||||
write a single stream's data to the log file. The implementation will
|
||||
|
@ -452,12 +480,12 @@ class Logfile(object):
|
|||
|
||||
Returns:
|
||||
A file-like object.
|
||||
'''
|
||||
"""
|
||||
|
||||
return LogfileStream(self, name, chained_file)
|
||||
|
||||
def get_runner(self, name, chained_file=None):
|
||||
'''Create an object that executes processes and logs their output.
|
||||
"""Create an object that executes processes and logs their output.
|
||||
|
||||
Args:
|
||||
name: The name of this sub-process.
|
||||
|
@ -466,12 +494,12 @@ class Logfile(object):
|
|||
|
||||
Returns:
|
||||
A RunAndLog object.
|
||||
'''
|
||||
"""
|
||||
|
||||
return RunAndLog(self, name, chained_file)
|
||||
|
||||
def write(self, stream, data, implicit=False):
|
||||
'''Write stream data into the log file.
|
||||
"""Write stream data into the log file.
|
||||
|
||||
This function should only be used by instances of LogfileStream or
|
||||
RunAndLog.
|
||||
|
@ -487,29 +515,29 @@ class Logfile(object):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
if stream != self.last_stream:
|
||||
self._terminate_stream()
|
||||
self.f.write("<div class=\"stream\" id=\"%s\">\n" % stream.name)
|
||||
self.f.write("<div class=\"stream-header\" id=\"" + stream.name +
|
||||
"\">Stream: " + stream.name + "</div>\n")
|
||||
self.f.write("<pre>")
|
||||
self.f.write('<div class="stream" id="%s">\n' % stream.name)
|
||||
self.f.write('<div class="stream-header" id="' + stream.name +
|
||||
'">Stream: ' + stream.name + '</div>\n')
|
||||
self.f.write('<pre>')
|
||||
if implicit:
|
||||
self.f.write("<span class=\"implicit\">")
|
||||
self.f.write('<span class="implicit">')
|
||||
self.f.write(self._escape(data))
|
||||
if implicit:
|
||||
self.f.write("</span>")
|
||||
self.f.write('</span>')
|
||||
self.last_stream = stream
|
||||
|
||||
def flush(self):
|
||||
'''Flush the log stream, to ensure correct log interleaving.
|
||||
"""Flush the log stream, to ensure correct log interleaving.
|
||||
|
||||
Args:
|
||||
None.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self.f.flush()
|
||||
|
|
|
@ -16,17 +16,17 @@ import sys
|
|||
sys.argv.pop(0)
|
||||
|
||||
# argv; py.test test_directory_name user-supplied-arguments
|
||||
args = ["py.test", os.path.dirname(__file__) + "/tests"]
|
||||
args = ['py.test', os.path.dirname(__file__) + '/tests']
|
||||
args.extend(sys.argv)
|
||||
|
||||
try:
|
||||
os.execvp("py.test", args)
|
||||
os.execvp('py.test', args)
|
||||
except:
|
||||
# Log full details of any exception for detailed analysis
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
# Hint to the user that they likely simply haven't installed the required
|
||||
# dependencies.
|
||||
print >>sys.stderr, """
|
||||
print >>sys.stderr, '''
|
||||
exec(py.test) failed; perhaps you are missing some dependencies?
|
||||
See test/py/README.md for the list."""
|
||||
See test/py/README.md for the list.'''
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
# command prompt.
|
||||
|
||||
def test_version(u_boot_console):
|
||||
'''Test that the "version" command prints the U-Boot version.'''
|
||||
"""Test that the "version" command prints the U-Boot version."""
|
||||
|
||||
# "version" prints the U-Boot sign-on message. This is usually considered
|
||||
# an error, so that any unexpected reboot causes an error. Here, this
|
||||
|
|
279
test/py/tests/test_dfu.py
Normal file
279
test/py/tests/test_dfu.py
Normal file
|
@ -0,0 +1,279 @@
|
|||
# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
# Test U-Boot's "dfu" command. The test starts DFU in U-Boot, waits for USB
|
||||
# device enumeration on the host, executes dfu-util multiple times to test
|
||||
# various transfer sizes, many of which trigger USB driver edge cases, and
|
||||
# finally aborts the "dfu" command in U-Boot.
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import pytest
|
||||
import u_boot_utils
|
||||
|
||||
"""
|
||||
Note: This test relies on:
|
||||
|
||||
a) boardenv_* to contain configuration values to define which USB ports are
|
||||
available for testing. Without this, this test will be automatically skipped.
|
||||
For example:
|
||||
|
||||
env__usb_dev_ports = (
|
||||
{
|
||||
"fixture_id": "micro_b",
|
||||
"tgt_usb_ctlr": "0",
|
||||
"host_usb_dev_node": "/dev/usbdev-p2371-2180",
|
||||
# This parameter is optional /if/ you only have a single board
|
||||
# attached to your host at a time.
|
||||
"host_usb_port_path": "3-13",
|
||||
},
|
||||
)
|
||||
|
||||
env__dfu_configs = (
|
||||
# eMMC, partition 1
|
||||
{
|
||||
"fixture_id": "emmc",
|
||||
"alt_info": "/dfu_test.bin ext4 0 1;/dfu_dummy.bin ext4 0 1",
|
||||
"cmd_params": "mmc 0",
|
||||
# This value is optional.
|
||||
# If present, it specified the set of transfer sizes tested.
|
||||
# If missing, a default list of sizes will be used, which covers
|
||||
# various useful corner cases.
|
||||
# Manually specifying test sizes is useful if you wish to test 4 DFU
|
||||
# configurations, but don't want to test every single transfer size
|
||||
# on each, to avoid bloating the overall time taken by testing.
|
||||
"test_sizes": (63, 64, 65),
|
||||
},
|
||||
)
|
||||
|
||||
b) udev rules to set permissions on devices nodes, so that sudo is not
|
||||
required. For example:
|
||||
|
||||
ACTION=="add", SUBSYSTEM=="block", SUBSYSTEMS=="usb", KERNELS=="3-13", MODE:="666"
|
||||
|
||||
(You may wish to change the group ID instead of setting the permissions wide
|
||||
open. All that matters is that the user ID running the test can access the
|
||||
device.)
|
||||
"""
|
||||
|
||||
# The set of file sizes to test. These values trigger various edge-cases such
|
||||
# as one less than, equal to, and one greater than typical USB max packet
|
||||
# sizes, and similar boundary conditions.
|
||||
test_sizes_default = (
|
||||
64 - 1,
|
||||
64,
|
||||
64 + 1,
|
||||
128 - 1,
|
||||
128,
|
||||
128 + 1,
|
||||
960 - 1,
|
||||
960,
|
||||
960 + 1,
|
||||
4096 - 1,
|
||||
4096,
|
||||
4096 + 1,
|
||||
1024 * 1024 - 1,
|
||||
1024 * 1024,
|
||||
8 * 1024 * 1024,
|
||||
)
|
||||
|
||||
first_usb_dev_port = None
|
||||
|
||||
@pytest.mark.buildconfigspec('cmd_dfu')
|
||||
def test_dfu(u_boot_console, env__usb_dev_port, env__dfu_config):
|
||||
"""Test the "dfu" command; the host system must be able to enumerate a USB
|
||||
device when "dfu" is running, various DFU transfers are tested, and the
|
||||
USB device must disappear when "dfu" is aborted.
|
||||
|
||||
Args:
|
||||
u_boot_console: A U-Boot console connection.
|
||||
env__usb_dev_port: The single USB device-mode port specification on
|
||||
which to run the test. See the file-level comment above for
|
||||
details of the format.
|
||||
env__dfu_config: The single DFU (memory region) configuration on which
|
||||
to run the test. See the file-level comment above for details
|
||||
of the format.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
|
||||
def start_dfu():
|
||||
"""Start U-Boot's dfu shell command.
|
||||
|
||||
This also waits for the host-side USB enumeration process to complete.
|
||||
|
||||
Args:
|
||||
None.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
|
||||
fh = u_boot_utils.attempt_to_open_file(
|
||||
env__usb_dev_port['host_usb_dev_node'])
|
||||
if fh:
|
||||
fh.close()
|
||||
raise Exception('USB device present before dfu command invoked')
|
||||
|
||||
u_boot_console.log.action(
|
||||
'Starting long-running U-Boot dfu shell command')
|
||||
|
||||
cmd = 'setenv dfu_alt_info "%s"' % env__dfu_config['alt_info']
|
||||
u_boot_console.run_command(cmd)
|
||||
|
||||
cmd = 'dfu 0 ' + env__dfu_config['cmd_params']
|
||||
u_boot_console.run_command(cmd, wait_for_prompt=False)
|
||||
u_boot_console.log.action('Waiting for DFU USB device to appear')
|
||||
fh = u_boot_utils.wait_until_open_succeeds(
|
||||
env__usb_dev_port['host_usb_dev_node'])
|
||||
fh.close()
|
||||
|
||||
def stop_dfu(ignore_errors):
|
||||
"""Stop U-Boot's dfu shell command from executing.
|
||||
|
||||
This also waits for the host-side USB de-enumeration process to
|
||||
complete.
|
||||
|
||||
Args:
|
||||
ignore_errors: Ignore any errors. This is useful if an error has
|
||||
already been detected, and the code is performing best-effort
|
||||
cleanup. In this case, we do not want to mask the original
|
||||
error by "honoring" any new errors.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
|
||||
try:
|
||||
u_boot_console.log.action(
|
||||
'Stopping long-running U-Boot dfu shell command')
|
||||
u_boot_console.ctrlc()
|
||||
u_boot_console.log.action(
|
||||
'Waiting for DFU USB device to disappear')
|
||||
u_boot_utils.wait_until_file_open_fails(
|
||||
env__usb_dev_port['host_usb_dev_node'], ignore_errors)
|
||||
except:
|
||||
if not ignore_errors:
|
||||
raise
|
||||
|
||||
def run_dfu_util(alt_setting, fn, up_dn_load_arg):
|
||||
"""Invoke dfu-util on the host.
|
||||
|
||||
Args:
|
||||
alt_setting: The DFU "alternate setting" identifier to interact
|
||||
with.
|
||||
fn: The host-side file name to transfer.
|
||||
up_dn_load_arg: '-U' or '-D' depending on whether a DFU upload or
|
||||
download operation should be performed.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
|
||||
cmd = ['dfu-util', '-a', str(alt_setting), up_dn_load_arg, fn]
|
||||
if 'host_usb_port_path' in env__usb_dev_port:
|
||||
cmd += ['-p', env__usb_dev_port['host_usb_port_path']]
|
||||
u_boot_utils.run_and_log(u_boot_console, cmd)
|
||||
u_boot_console.wait_for('Ctrl+C to exit ...')
|
||||
|
||||
def dfu_write(alt_setting, fn):
|
||||
"""Write a file to the target board using DFU.
|
||||
|
||||
Args:
|
||||
alt_setting: The DFU "alternate setting" identifier to interact
|
||||
with.
|
||||
fn: The host-side file name to transfer.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
|
||||
run_dfu_util(alt_setting, fn, '-D')
|
||||
|
||||
def dfu_read(alt_setting, fn):
|
||||
"""Read a file from the target board using DFU.
|
||||
|
||||
Args:
|
||||
alt_setting: The DFU "alternate setting" identifier to interact
|
||||
with.
|
||||
fn: The host-side file name to transfer.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
|
||||
# dfu-util fails reads/uploads if the host file already exists
|
||||
if os.path.exists(fn):
|
||||
os.remove(fn)
|
||||
run_dfu_util(alt_setting, fn, '-U')
|
||||
|
||||
def dfu_write_read_check(size):
|
||||
"""Test DFU transfers of a specific size of data
|
||||
|
||||
This function first writes data to the board then reads it back and
|
||||
compares the written and read back data. Measures are taken to avoid
|
||||
certain types of false positives.
|
||||
|
||||
Args:
|
||||
size: The data size to test.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
|
||||
test_f = u_boot_utils.PersistentRandomFile(u_boot_console,
|
||||
'dfu_%d.bin' % size, size)
|
||||
readback_fn = u_boot_console.config.result_dir + '/dfu_readback.bin'
|
||||
|
||||
u_boot_console.log.action('Writing test data to DFU primary ' +
|
||||
'altsetting')
|
||||
dfu_write(0, test_f.abs_fn)
|
||||
|
||||
u_boot_console.log.action('Writing dummy data to DFU secondary ' +
|
||||
'altsetting to clear DFU buffers')
|
||||
dfu_write(1, dummy_f.abs_fn)
|
||||
|
||||
u_boot_console.log.action('Reading DFU primary altsetting for ' +
|
||||
'comparison')
|
||||
dfu_read(0, readback_fn)
|
||||
|
||||
u_boot_console.log.action('Comparing written and read data')
|
||||
written_hash = test_f.content_hash
|
||||
read_back_hash = u_boot_utils.md5sum_file(readback_fn, size)
|
||||
assert(written_hash == read_back_hash)
|
||||
|
||||
# This test may be executed against multiple USB ports. The test takes a
|
||||
# long time, so we don't want to do the whole thing each time. Instead,
|
||||
# execute the full test on the first USB port, and perform a very limited
|
||||
# test on other ports. In the limited case, we solely validate that the
|
||||
# host PC can enumerate the U-Boot USB device.
|
||||
global first_usb_dev_port
|
||||
if not first_usb_dev_port:
|
||||
first_usb_dev_port = env__usb_dev_port
|
||||
if env__usb_dev_port == first_usb_dev_port:
|
||||
sizes = env__dfu_config.get('test_sizes', test_sizes_default)
|
||||
else:
|
||||
sizes = []
|
||||
|
||||
dummy_f = u_boot_utils.PersistentRandomFile(u_boot_console,
|
||||
'dfu_dummy.bin', 1024)
|
||||
|
||||
ignore_cleanup_errors = True
|
||||
try:
|
||||
start_dfu()
|
||||
|
||||
u_boot_console.log.action(
|
||||
'Overwriting DFU primary altsetting with dummy data')
|
||||
dfu_write(0, dummy_f.abs_fn)
|
||||
|
||||
for size in sizes:
|
||||
with u_boot_console.log.section('Data size %d' % size):
|
||||
dfu_write_read_check(size)
|
||||
# Make the status of each sub-test obvious. If the test didn't
|
||||
# pass, an exception was thrown so this code isn't executed.
|
||||
u_boot_console.log.status_pass('OK')
|
||||
ignore_cleanup_errors = False
|
||||
finally:
|
||||
stop_dfu(ignore_cleanup_errors)
|
|
@ -10,34 +10,34 @@ import pytest
|
|||
# FIXME: This might be useful for other tests;
|
||||
# perhaps refactor it into ConsoleBase or some other state object?
|
||||
class StateTestEnv(object):
|
||||
'''Container that represents the state of all U-Boot environment variables.
|
||||
"""Container that represents the state of all U-Boot environment variables.
|
||||
This enables quick determination of existant/non-existant variable
|
||||
names.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, u_boot_console):
|
||||
'''Initialize a new StateTestEnv object.
|
||||
"""Initialize a new StateTestEnv object.
|
||||
|
||||
Args:
|
||||
u_boot_console: A U-Boot console.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self.u_boot_console = u_boot_console
|
||||
self.get_env()
|
||||
self.set_var = self.get_non_existent_var()
|
||||
|
||||
def get_env(self):
|
||||
'''Read all current environment variables from U-Boot.
|
||||
"""Read all current environment variables from U-Boot.
|
||||
|
||||
Args:
|
||||
None.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
response = self.u_boot_console.run_command('printenv')
|
||||
self.env = {}
|
||||
|
@ -48,27 +48,27 @@ class StateTestEnv(object):
|
|||
self.env[var] = value
|
||||
|
||||
def get_existent_var(self):
|
||||
'''Return the name of an environment variable that exists.
|
||||
"""Return the name of an environment variable that exists.
|
||||
|
||||
Args:
|
||||
None.
|
||||
|
||||
Returns:
|
||||
The name of an environment variable.
|
||||
'''
|
||||
"""
|
||||
|
||||
for var in self.env:
|
||||
return var
|
||||
|
||||
def get_non_existent_var(self):
|
||||
'''Return the name of an environment variable that does not exist.
|
||||
"""Return the name of an environment variable that does not exist.
|
||||
|
||||
Args:
|
||||
None.
|
||||
|
||||
Returns:
|
||||
The name of an environment variable.
|
||||
'''
|
||||
"""
|
||||
|
||||
n = 0
|
||||
while True:
|
||||
|
@ -77,63 +77,67 @@ class StateTestEnv(object):
|
|||
return var
|
||||
n += 1
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
ste = None
|
||||
@pytest.fixture(scope='function')
|
||||
def state_test_env(u_boot_console):
|
||||
'''pytest fixture to provide a StateTestEnv object to tests.'''
|
||||
"""pytest fixture to provide a StateTestEnv object to tests."""
|
||||
|
||||
return StateTestEnv(u_boot_console)
|
||||
global ste
|
||||
if not ste:
|
||||
ste = StateTestEnv(u_boot_console)
|
||||
return ste
|
||||
|
||||
def unset_var(state_test_env, var):
|
||||
'''Unset an environment variable.
|
||||
"""Unset an environment variable.
|
||||
|
||||
This both executes a U-Boot shell command and updates a StateTestEnv
|
||||
object.
|
||||
|
||||
Args:
|
||||
state_test_env: The StateTestEnv object to updata.
|
||||
state_test_env: The StateTestEnv object to update.
|
||||
var: The variable name to unset.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
state_test_env.u_boot_console.run_command('setenv %s' % var)
|
||||
if var in state_test_env.env:
|
||||
del state_test_env.env[var]
|
||||
|
||||
def set_var(state_test_env, var, value):
|
||||
'''Set an environment variable.
|
||||
"""Set an environment variable.
|
||||
|
||||
This both executes a U-Boot shell command and updates a StateTestEnv
|
||||
object.
|
||||
|
||||
Args:
|
||||
state_test_env: The StateTestEnv object to updata.
|
||||
state_test_env: The StateTestEnv object to update.
|
||||
var: The variable name to set.
|
||||
value: The value to set the variable to.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
state_test_env.u_boot_console.run_command('setenv %s "%s"' % (var, value))
|
||||
state_test_env.env[var] = value
|
||||
|
||||
def validate_empty(state_test_env, var):
|
||||
'''Validate that a variable is not set, using U-Boot shell commands.
|
||||
"""Validate that a variable is not set, using U-Boot shell commands.
|
||||
|
||||
Args:
|
||||
var: The variable name to test.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
response = state_test_env.u_boot_console.run_command('echo $%s' % var)
|
||||
assert response == ''
|
||||
|
||||
def validate_set(state_test_env, var, value):
|
||||
'''Validate that a variable is set, using U-Boot shell commands.
|
||||
"""Validate that a variable is set, using U-Boot shell commands.
|
||||
|
||||
Args:
|
||||
var: The variable name to test.
|
||||
|
@ -141,7 +145,7 @@ def validate_set(state_test_env, var, value):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
# echo does not preserve leading, internal, or trailing whitespace in the
|
||||
# value. printenv does, and hence allows more complete testing.
|
||||
|
@ -149,20 +153,20 @@ def validate_set(state_test_env, var, value):
|
|||
assert response == ('%s=%s' % (var, value))
|
||||
|
||||
def test_env_echo_exists(state_test_env):
|
||||
'''Test echoing a variable that exists.'''
|
||||
"""Test echoing a variable that exists."""
|
||||
|
||||
var = state_test_env.get_existent_var()
|
||||
value = state_test_env.env[var]
|
||||
validate_set(state_test_env, var, value)
|
||||
|
||||
def test_env_echo_non_existent(state_test_env):
|
||||
'''Test echoing a variable that doesn't exist.'''
|
||||
"""Test echoing a variable that doesn't exist."""
|
||||
|
||||
var = state_test_env.set_var
|
||||
validate_empty(state_test_env, var)
|
||||
|
||||
def test_env_printenv_non_existent(state_test_env):
|
||||
'''Test printenv error message for non-existant variables.'''
|
||||
"""Test printenv error message for non-existant variables."""
|
||||
|
||||
var = state_test_env.set_var
|
||||
c = state_test_env.u_boot_console
|
||||
|
@ -171,14 +175,14 @@ def test_env_printenv_non_existent(state_test_env):
|
|||
assert(response == '## Error: "%s" not defined' % var)
|
||||
|
||||
def test_env_unset_non_existent(state_test_env):
|
||||
'''Test unsetting a nonexistent variable.'''
|
||||
"""Test unsetting a nonexistent variable."""
|
||||
|
||||
var = state_test_env.get_non_existent_var()
|
||||
unset_var(state_test_env, var)
|
||||
validate_empty(state_test_env, var)
|
||||
|
||||
def test_env_set_non_existent(state_test_env):
|
||||
'''Test set a non-existant variable.'''
|
||||
"""Test set a non-existant variable."""
|
||||
|
||||
var = state_test_env.set_var
|
||||
value = 'foo'
|
||||
|
@ -186,7 +190,7 @@ def test_env_set_non_existent(state_test_env):
|
|||
validate_set(state_test_env, var, value)
|
||||
|
||||
def test_env_set_existing(state_test_env):
|
||||
'''Test setting an existant variable.'''
|
||||
"""Test setting an existant variable."""
|
||||
|
||||
var = state_test_env.set_var
|
||||
value = 'bar'
|
||||
|
@ -194,14 +198,14 @@ def test_env_set_existing(state_test_env):
|
|||
validate_set(state_test_env, var, value)
|
||||
|
||||
def test_env_unset_existing(state_test_env):
|
||||
'''Test unsetting a variable.'''
|
||||
"""Test unsetting a variable."""
|
||||
|
||||
var = state_test_env.set_var
|
||||
unset_var(state_test_env, var)
|
||||
validate_empty(state_test_env, var)
|
||||
|
||||
def test_env_expansion_spaces(state_test_env):
|
||||
'''Test expanding a variable that contains a space in its value.'''
|
||||
"""Test expanding a variable that contains a space in its value."""
|
||||
|
||||
var_space = None
|
||||
var_test = None
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
def test_help(u_boot_console):
|
||||
'''Test that the "help" command can be executed.'''
|
||||
"""Test that the "help" command can be executed."""
|
||||
|
||||
u_boot_console.run_command('help')
|
||||
|
|
|
@ -95,7 +95,7 @@ subtests = (
|
|||
)
|
||||
|
||||
def exec_hush_if(u_boot_console, expr, result):
|
||||
'''Execute a shell "if" command, and validate its result.'''
|
||||
"""Execute a shell "if" command, and validate its result."""
|
||||
|
||||
cmd = 'if ' + expr + '; then echo true; else echo false; fi'
|
||||
response = u_boot_console.run_command(cmd)
|
||||
|
@ -103,7 +103,7 @@ def exec_hush_if(u_boot_console, expr, result):
|
|||
|
||||
@pytest.mark.buildconfigspec('sys_hush_parser')
|
||||
def test_hush_if_test_setup(u_boot_console):
|
||||
'''Set up environment variables used during the "if" tests.'''
|
||||
"""Set up environment variables used during the "if" tests."""
|
||||
|
||||
u_boot_console.run_command('setenv ut_var_nonexistent')
|
||||
u_boot_console.run_command('setenv ut_var_exists 1')
|
||||
|
@ -111,13 +111,13 @@ def test_hush_if_test_setup(u_boot_console):
|
|||
@pytest.mark.buildconfigspec('sys_hush_parser')
|
||||
@pytest.mark.parametrize('expr,result', subtests)
|
||||
def test_hush_if_test(u_boot_console, expr, result):
|
||||
'''Test a single "if test" condition.'''
|
||||
"""Test a single "if test" condition."""
|
||||
|
||||
exec_hush_if(u_boot_console, expr, result)
|
||||
|
||||
@pytest.mark.buildconfigspec('sys_hush_parser')
|
||||
def test_hush_if_test_teardown(u_boot_console):
|
||||
'''Clean up environment variables used during the "if" tests.'''
|
||||
"""Clean up environment variables used during the "if" tests."""
|
||||
|
||||
u_boot_console.run_command('setenv ut_var_exists')
|
||||
|
||||
|
@ -126,7 +126,7 @@ def test_hush_if_test_teardown(u_boot_console):
|
|||
# Of those, only UMS currently allows file removal though.
|
||||
@pytest.mark.boardspec('sandbox')
|
||||
def test_hush_if_test_host_file_exists(u_boot_console):
|
||||
'''Test the "if test -e" shell command.'''
|
||||
"""Test the "if test -e" shell command."""
|
||||
|
||||
test_file = u_boot_console.config.result_dir + \
|
||||
'/creating_this_file_breaks_u_boot_tests'
|
||||
|
|
|
@ -4,13 +4,14 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
import pytest
|
||||
import u_boot_utils
|
||||
|
||||
@pytest.mark.buildconfigspec('cmd_memory')
|
||||
def test_md(u_boot_console):
|
||||
'''Test that md reads memory as expected, and that memory can be modified
|
||||
using the mw command.'''
|
||||
"""Test that md reads memory as expected, and that memory can be modified
|
||||
using the mw command."""
|
||||
|
||||
ram_base = u_boot_console.find_ram_base()
|
||||
ram_base = u_boot_utils.find_ram_base(u_boot_console)
|
||||
addr = '%08x' % ram_base
|
||||
val = 'a5f09876'
|
||||
expected_response = addr + ': ' + val
|
||||
|
@ -23,10 +24,10 @@ def test_md(u_boot_console):
|
|||
|
||||
@pytest.mark.buildconfigspec('cmd_memory')
|
||||
def test_md_repeat(u_boot_console):
|
||||
'''Test command repeat (via executing an empty command) operates correctly
|
||||
for "md"; the command must repeat and dump an incrementing address.'''
|
||||
"""Test command repeat (via executing an empty command) operates correctly
|
||||
for "md"; the command must repeat and dump an incrementing address."""
|
||||
|
||||
ram_base = u_boot_console.find_ram_base()
|
||||
ram_base = u_boot_utils.find_ram_base(u_boot_console)
|
||||
addr_base = '%08x' % ram_base
|
||||
words = 0x10
|
||||
addr_repeat = '%08x' % (ram_base + (words * 4))
|
||||
|
|
155
test/py/tests/test_net.py
Normal file
155
test/py/tests/test_net.py
Normal file
|
@ -0,0 +1,155 @@
|
|||
# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
# Test various network-related functionality, such as the dhcp, ping, and
|
||||
# tftpboot commands.
|
||||
|
||||
import pytest
|
||||
import u_boot_utils
|
||||
|
||||
"""
|
||||
Note: This test relies on boardenv_* containing configuration values to define
|
||||
which the network environment available for testing. Without this, this test
|
||||
will be automatically skipped.
|
||||
|
||||
For example:
|
||||
|
||||
# Boolean indicating whether the Ethernet device is attached to USB, and hence
|
||||
# USB enumeration needs to be performed prior to network tests.
|
||||
# This variable may be omitted if its value is False.
|
||||
env__net_uses_usb = False
|
||||
|
||||
# Boolean indicating whether the Ethernet device is attached to PCI, and hence
|
||||
# PCI enumeration needs to be performed prior to network tests.
|
||||
# This variable may be omitted if its value is False.
|
||||
env__net_uses_pci = True
|
||||
|
||||
# True if a DHCP server is attached to the network, and should be tested.
|
||||
# If DHCP testing is not possible or desired, this variable may be omitted or
|
||||
# set to False.
|
||||
env__net_dhcp_server = True
|
||||
|
||||
# A list of environment variables that should be set in order to configure a
|
||||
# static IP. If solely relying on DHCP, this variable may be omitted or set to
|
||||
# an empty list.
|
||||
env__net_static_env_vars = [
|
||||
("ipaddr", "10.0.0.100"),
|
||||
("netmask", "255.255.255.0"),
|
||||
("serverip", "10.0.0.1"),
|
||||
]
|
||||
|
||||
# Details regarding a file that may be read from a TFTP server. This variable
|
||||
# may be omitted or set to None if TFTP testing is not possible or desired.
|
||||
env__net_tftp_readable_file = {
|
||||
"fn": "ubtest-readable.bin",
|
||||
"size": 5058624,
|
||||
"crc32": "c2244b26",
|
||||
}
|
||||
"""
|
||||
|
||||
net_set_up = False
|
||||
|
||||
def test_net_pre_commands(u_boot_console):
|
||||
"""Execute any commands required to enable network hardware.
|
||||
|
||||
These commands are provided by the boardenv_* file; see the comment at the
|
||||
beginning of this file.
|
||||
"""
|
||||
|
||||
init_usb = u_boot_console.config.env.get('env__net_uses_usb', False)
|
||||
if init_usb:
|
||||
u_boot_console.run_command('usb start')
|
||||
|
||||
init_pci = u_boot_console.config.env.get('env__net_uses_pci', False)
|
||||
if init_pci:
|
||||
u_boot_console.run_command('pci enum')
|
||||
|
||||
@pytest.mark.buildconfigspec('cmd_dhcp')
|
||||
def test_net_dhcp(u_boot_console):
|
||||
"""Test the dhcp command.
|
||||
|
||||
The boardenv_* file may be used to enable/disable this test; see the
|
||||
comment at the beginning of this file.
|
||||
"""
|
||||
|
||||
test_dhcp = u_boot_console.config.env.get('env__net_dhcp_server', False)
|
||||
if not test_dhcp:
|
||||
pytest.skip('No DHCP server available')
|
||||
|
||||
u_boot_console.run_command('setenv autoload no')
|
||||
output = u_boot_console.run_command('dhcp')
|
||||
assert 'DHCP client bound to address ' in output
|
||||
|
||||
global net_set_up
|
||||
net_set_up = True
|
||||
|
||||
@pytest.mark.buildconfigspec('net')
|
||||
def test_net_setup_static(u_boot_console):
|
||||
"""Set up a static IP configuration.
|
||||
|
||||
The configuration is provided by the boardenv_* file; see the comment at
|
||||
the beginning of this file.
|
||||
"""
|
||||
|
||||
env_vars = u_boot_console.config.env.get('env__net_static_env_vars', None)
|
||||
if not env_vars:
|
||||
pytest.skip('No static network configuration is defined')
|
||||
|
||||
for (var, val) in env_vars:
|
||||
u_boot_console.run_command('setenv %s %s' % (var, val))
|
||||
|
||||
global net_set_up
|
||||
net_set_up = True
|
||||
|
||||
@pytest.mark.buildconfigspec('cmd_ping')
|
||||
def test_net_ping(u_boot_console):
|
||||
"""Test the ping command.
|
||||
|
||||
The $serverip (as set up by either test_net_dhcp or test_net_setup_static)
|
||||
is pinged. The test validates that the host is alive, as reported by the
|
||||
ping command's output.
|
||||
"""
|
||||
|
||||
if not net_set_up:
|
||||
pytest.skip('Network not initialized')
|
||||
|
||||
output = u_boot_console.run_command('ping $serverip')
|
||||
assert 'is alive' in output
|
||||
|
||||
@pytest.mark.buildconfigspec('cmd_net')
|
||||
def test_net_tftpboot(u_boot_console):
|
||||
"""Test the tftpboot command.
|
||||
|
||||
A file is downloaded from the TFTP server, its size and optionally its
|
||||
CRC32 are validated.
|
||||
|
||||
The details of the file to download are provided by the boardenv_* file;
|
||||
see the comment at the beginning of this file.
|
||||
"""
|
||||
|
||||
if not net_set_up:
|
||||
pytest.skip('Network not initialized')
|
||||
|
||||
f = u_boot_console.config.env.get('env__net_tftp_readable_file', None)
|
||||
if not f:
|
||||
pytest.skip('No TFTP readable file to read')
|
||||
|
||||
addr = u_boot_utils.find_ram_base(u_boot_console)
|
||||
fn = f['fn']
|
||||
output = u_boot_console.run_command('tftpboot %x %s' % (addr, fn))
|
||||
expected_text = 'Bytes transferred = '
|
||||
sz = f.get('size', None)
|
||||
if sz:
|
||||
expected_text += '%d' % sz
|
||||
assert expected_text in output
|
||||
|
||||
expected_crc = f.get('crc32', None)
|
||||
if not expected_crc:
|
||||
return
|
||||
|
||||
if u_boot_console.config.buildconfig.get('config_cmd_crc32', 'n') != 'y':
|
||||
return
|
||||
|
||||
output = u_boot_console.run_command('crc32 %x $filesize' % addr)
|
||||
assert expected_crc in output
|
|
@ -9,16 +9,14 @@ import signal
|
|||
@pytest.mark.boardspec('sandbox')
|
||||
@pytest.mark.buildconfigspec('reset')
|
||||
def test_reset(u_boot_console):
|
||||
'''Test that the "reset" command exits sandbox process.'''
|
||||
"""Test that the "reset" command exits sandbox process."""
|
||||
|
||||
u_boot_console.run_command('reset', wait_for_prompt=False)
|
||||
assert(u_boot_console.validate_exited())
|
||||
u_boot_console.ensure_spawned()
|
||||
|
||||
@pytest.mark.boardspec('sandbox')
|
||||
def test_ctrl_c(u_boot_console):
|
||||
'''Test that sending SIGINT to sandbox causes it to exit.'''
|
||||
"""Test that sending SIGINT to sandbox causes it to exit."""
|
||||
|
||||
u_boot_console.kill(signal.SIGINT)
|
||||
assert(u_boot_console.validate_exited())
|
||||
u_boot_console.ensure_spawned()
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
# Test basic shell functionality, such as commands separate by semi-colons.
|
||||
|
||||
def test_shell_execute(u_boot_console):
|
||||
'''Test any shell command.'''
|
||||
"""Test any shell command."""
|
||||
|
||||
response = u_boot_console.run_command('echo hello')
|
||||
assert response.strip() == 'hello'
|
||||
|
||||
def test_shell_semicolon_two(u_boot_console):
|
||||
'''Test two shell commands separate by a semi-colon.'''
|
||||
"""Test two shell commands separate by a semi-colon."""
|
||||
|
||||
cmd = 'echo hello; echo world'
|
||||
response = u_boot_console.run_command(cmd)
|
||||
|
@ -19,8 +19,8 @@ def test_shell_semicolon_two(u_boot_console):
|
|||
assert response.index('hello') < response.index('world')
|
||||
|
||||
def test_shell_semicolon_three(u_boot_console):
|
||||
'''Test three shell commands separate by a semi-colon, with variable
|
||||
expansion dependencies between them.'''
|
||||
"""Test three shell commands separate by a semi-colon, with variable
|
||||
expansion dependencies between them."""
|
||||
|
||||
cmd = 'setenv list 1; setenv list ${list}2; setenv list ${list}3; ' + \
|
||||
'echo ${list}'
|
||||
|
@ -29,9 +29,9 @@ def test_shell_semicolon_three(u_boot_console):
|
|||
u_boot_console.run_command('setenv list')
|
||||
|
||||
def test_shell_run(u_boot_console):
|
||||
'''Test the "run" shell command.'''
|
||||
"""Test the "run" shell command."""
|
||||
|
||||
u_boot_console.run_command('setenv foo \"setenv monty 1; setenv python 2\"')
|
||||
u_boot_console.run_command('setenv foo "setenv monty 1; setenv python 2"')
|
||||
u_boot_console.run_command('run foo')
|
||||
response = u_boot_console.run_command('echo $monty')
|
||||
assert response.strip() == '1'
|
||||
|
|
|
@ -6,12 +6,8 @@ import pytest
|
|||
import time
|
||||
|
||||
def test_sleep(u_boot_console):
|
||||
'''Test the sleep command, and validate that it sleeps for approximately
|
||||
the correct amount of time.'''
|
||||
|
||||
# Do this before we time anything, to make sure U-Boot is already running.
|
||||
# Otherwise, the system boot time is included in the time measurement.
|
||||
u_boot_console.ensure_spawned()
|
||||
"""Test the sleep command, and validate that it sleeps for approximately
|
||||
the correct amount of time."""
|
||||
|
||||
# 3s isn't too long, but is enough to cross a few second boundaries.
|
||||
sleep_time = 3
|
||||
|
|
|
@ -2,28 +2,58 @@
|
|||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
# Test U-Boot's "ums" command. At present, this test only ensures that a UMS
|
||||
# device can be enumerated by the host/test machine. In the future, this test
|
||||
# should be enhanced to validate disk IO.
|
||||
# Test U-Boot's "ums" command. The test starts UMS in U-Boot, waits for USB
|
||||
# device enumeration on the host, reads a small block of data from the UMS
|
||||
# block device, optionally mounts a partition and performs filesystem-based
|
||||
# read/write tests, and finally aborts the "ums" command in U-Boot.
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import pytest
|
||||
import re
|
||||
import time
|
||||
import u_boot_utils
|
||||
|
||||
'''
|
||||
"""
|
||||
Note: This test relies on:
|
||||
|
||||
a) boardenv_* to contain configuration values to define which USB ports are
|
||||
available for testing. Without this, this test will be automatically skipped.
|
||||
For example:
|
||||
|
||||
# Leave this list empty if you have no block_devs below with writable
|
||||
# partitions defined.
|
||||
env__mount_points = (
|
||||
"/mnt/ubtest-mnt-p2371-2180-na",
|
||||
)
|
||||
|
||||
env__usb_dev_ports = (
|
||||
{'tgt_usb_ctlr': '0', 'host_ums_dev_node': '/dev/disk/by-path/pci-0000:00:14.0-usb-0:13:1.0-scsi-0:0:0:0'},
|
||||
{
|
||||
"fixture_id": "micro_b",
|
||||
"tgt_usb_ctlr": "0",
|
||||
"host_ums_dev_node": "/dev/disk/by-path/pci-0000:00:14.0-usb-0:13:1.0-scsi-0:0:0:0",
|
||||
},
|
||||
)
|
||||
|
||||
env__block_devs = (
|
||||
{'type': 'mmc', 'id': '0'}, # eMMC; always present
|
||||
{'type': 'mmc', 'id': '1'}, # SD card; present since I plugged one in
|
||||
# eMMC; always present
|
||||
{
|
||||
"fixture_id": "emmc",
|
||||
"type": "mmc",
|
||||
"id": "0",
|
||||
# The following two properties are optional.
|
||||
# If present, the partition will be mounted and a file written-to and
|
||||
# read-from it. If missing, only a simple block read test will be
|
||||
# performed.
|
||||
"writable_fs_partition": 1,
|
||||
"writable_fs_subdir": "tmp/",
|
||||
},
|
||||
# SD card; present since I plugged one in
|
||||
{
|
||||
"fixture_id": "sd",
|
||||
"type": "mmc",
|
||||
"id": "1"
|
||||
},
|
||||
)
|
||||
|
||||
b) udev rules to set permissions on devices nodes, so that sudo is not
|
||||
|
@ -34,47 +64,42 @@ ACTION=="add", SUBSYSTEM=="block", SUBSYSTEMS=="usb", KERNELS=="3-13", MODE:="66
|
|||
(You may wish to change the group ID instead of setting the permissions wide
|
||||
open. All that matters is that the user ID running the test can access the
|
||||
device.)
|
||||
'''
|
||||
|
||||
def open_ums_device(host_ums_dev_node):
|
||||
'''Attempt to open a device node, returning either the opened file handle,
|
||||
or None on any error.'''
|
||||
c) /etc/fstab entries to allow the block device to be mounted without requiring
|
||||
root permissions. For example:
|
||||
|
||||
try:
|
||||
return open(host_ums_dev_node, 'rb')
|
||||
except:
|
||||
return None
|
||||
/dev/disk/by-path/pci-0000:00:14.0-usb-0:13:1.0-scsi-0:0:0:0-part1 /mnt/ubtest-mnt-p2371-2180-na ext4 noauto,user,nosuid,nodev
|
||||
|
||||
def wait_for_ums_device(host_ums_dev_node):
|
||||
'''Continually attempt to open the device node exported by the "ums"
|
||||
command, and either return the opened file handle, or raise an exception
|
||||
after a timeout.'''
|
||||
|
||||
for i in xrange(100):
|
||||
fh = open_ums_device(host_ums_dev_node)
|
||||
if fh:
|
||||
return fh
|
||||
time.sleep(0.1)
|
||||
raise Exception('UMS device did not appear')
|
||||
|
||||
def wait_for_ums_device_gone(host_ums_dev_node):
|
||||
'''Continually attempt to open the device node exported by the "ums"
|
||||
command, and either return once the device has disappeared, or raise an
|
||||
exception if it does not before a timeout occurs.'''
|
||||
|
||||
for i in xrange(100):
|
||||
fh = open_ums_device(host_ums_dev_node)
|
||||
if not fh:
|
||||
return
|
||||
fh.close()
|
||||
time.sleep(0.1)
|
||||
raise Exception('UMS device did not disappear')
|
||||
This entry is only needed if any block_devs above contain a
|
||||
writable_fs_partition value.
|
||||
"""
|
||||
|
||||
@pytest.mark.buildconfigspec('cmd_usb_mass_storage')
|
||||
def test_ums(u_boot_console, env__usb_dev_port, env__block_devs):
|
||||
'''Test the "ums" command; the host system must be able to enumerate a UMS
|
||||
device when "ums" is running, and this device must disappear when "ums" is
|
||||
aborted.'''
|
||||
"""Test the "ums" command; the host system must be able to enumerate a UMS
|
||||
device when "ums" is running, block and optionally file I/O are tested,
|
||||
and this device must disappear when "ums" is aborted.
|
||||
|
||||
Args:
|
||||
u_boot_console: A U-Boot console connection.
|
||||
env__usb_dev_port: The single USB device-mode port specification on
|
||||
which to run the test. See the file-level comment above for
|
||||
details of the format.
|
||||
env__block_devs: The list of block devices that the target U-Boot
|
||||
device has attached. See the file-level comment above for details
|
||||
of the format.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
|
||||
have_writable_fs_partition = 'writable_fs_partition' in env__block_devs[0]
|
||||
if not have_writable_fs_partition:
|
||||
# If 'writable_fs_subdir' is missing, we'll skip all parts of the
|
||||
# testing which mount filesystems.
|
||||
u_boot_console.log.warning(
|
||||
'boardenv missing "writable_fs_partition"; ' +
|
||||
'UMS testing will be limited.')
|
||||
|
||||
tgt_usb_ctlr = env__usb_dev_port['tgt_usb_ctlr']
|
||||
host_ums_dev_node = env__usb_dev_port['host_ums_dev_node']
|
||||
|
@ -84,11 +109,129 @@ def test_ums(u_boot_console, env__usb_dev_port, env__block_devs):
|
|||
# device list here. We'll test each block device somewhere else.
|
||||
tgt_dev_type = env__block_devs[0]['type']
|
||||
tgt_dev_id = env__block_devs[0]['id']
|
||||
if have_writable_fs_partition:
|
||||
mount_point = u_boot_console.config.env['env__mount_points'][0]
|
||||
mount_subdir = env__block_devs[0]['writable_fs_subdir']
|
||||
part_num = env__block_devs[0]['writable_fs_partition']
|
||||
host_ums_part_node = '%s-part%d' % (host_ums_dev_node, part_num)
|
||||
else:
|
||||
host_ums_part_node = host_ums_dev_node
|
||||
|
||||
cmd = 'ums %s %s %s' % (tgt_usb_ctlr, tgt_dev_type, tgt_dev_id)
|
||||
u_boot_console.run_command('ums 0 mmc 0', wait_for_prompt=False)
|
||||
fh = wait_for_ums_device(host_ums_dev_node)
|
||||
fh.read(4096)
|
||||
fh.close()
|
||||
u_boot_console.ctrlc()
|
||||
wait_for_ums_device_gone(host_ums_dev_node)
|
||||
test_f = u_boot_utils.PersistentRandomFile(u_boot_console, 'ums.bin',
|
||||
1024 * 1024);
|
||||
if have_writable_fs_partition:
|
||||
mounted_test_fn = mount_point + '/' + mount_subdir + test_f.fn
|
||||
|
||||
def start_ums():
|
||||
"""Start U-Boot's ums shell command.
|
||||
|
||||
This also waits for the host-side USB enumeration process to complete.
|
||||
|
||||
Args:
|
||||
None.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
|
||||
u_boot_console.log.action(
|
||||
'Starting long-running U-Boot ums shell command')
|
||||
cmd = 'ums %s %s %s' % (tgt_usb_ctlr, tgt_dev_type, tgt_dev_id)
|
||||
u_boot_console.run_command(cmd, wait_for_prompt=False)
|
||||
u_boot_console.wait_for(re.compile('UMS: LUN.*[\r\n]'))
|
||||
fh = u_boot_utils.wait_until_open_succeeds(host_ums_part_node)
|
||||
u_boot_console.log.action('Reading raw data from UMS device')
|
||||
fh.read(4096)
|
||||
fh.close()
|
||||
|
||||
def mount():
|
||||
"""Mount the block device that U-Boot exports.
|
||||
|
||||
Args:
|
||||
None.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
|
||||
u_boot_console.log.action('Mounting exported UMS device')
|
||||
cmd = ('/bin/mount', host_ums_part_node)
|
||||
u_boot_utils.run_and_log(u_boot_console, cmd)
|
||||
|
||||
def umount(ignore_errors):
|
||||
"""Unmount the block device that U-Boot exports.
|
||||
|
||||
Args:
|
||||
ignore_errors: Ignore any errors. This is useful if an error has
|
||||
already been detected, and the code is performing best-effort
|
||||
cleanup. In this case, we do not want to mask the original
|
||||
error by "honoring" any new errors.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
|
||||
u_boot_console.log.action('Unmounting UMS device')
|
||||
cmd = ('/bin/umount', host_ums_part_node)
|
||||
u_boot_utils.run_and_log(u_boot_console, cmd, ignore_errors)
|
||||
|
||||
def stop_ums(ignore_errors):
|
||||
"""Stop U-Boot's ums shell command from executing.
|
||||
|
||||
This also waits for the host-side USB de-enumeration process to
|
||||
complete.
|
||||
|
||||
Args:
|
||||
ignore_errors: Ignore any errors. This is useful if an error has
|
||||
already been detected, and the code is performing best-effort
|
||||
cleanup. In this case, we do not want to mask the original
|
||||
error by "honoring" any new errors.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
|
||||
u_boot_console.log.action(
|
||||
'Stopping long-running U-Boot ums shell command')
|
||||
u_boot_console.ctrlc()
|
||||
u_boot_utils.wait_until_file_open_fails(host_ums_part_node,
|
||||
ignore_errors)
|
||||
|
||||
ignore_cleanup_errors = True
|
||||
try:
|
||||
start_ums()
|
||||
if not have_writable_fs_partition:
|
||||
# Skip filesystem-based testing if not configured
|
||||
return
|
||||
try:
|
||||
mount()
|
||||
u_boot_console.log.action('Writing test file via UMS')
|
||||
cmd = ('rm', '-f', mounted_test_fn)
|
||||
u_boot_utils.run_and_log(u_boot_console, cmd)
|
||||
if os.path.exists(mounted_test_fn):
|
||||
raise Exception('Could not rm target UMS test file')
|
||||
cmd = ('cp', test_f.abs_fn, mounted_test_fn)
|
||||
u_boot_utils.run_and_log(u_boot_console, cmd)
|
||||
ignore_cleanup_errors = False
|
||||
finally:
|
||||
umount(ignore_errors=ignore_cleanup_errors)
|
||||
finally:
|
||||
stop_ums(ignore_errors=ignore_cleanup_errors)
|
||||
|
||||
ignore_cleanup_errors = True
|
||||
try:
|
||||
start_ums()
|
||||
try:
|
||||
mount()
|
||||
u_boot_console.log.action('Reading test file back via UMS')
|
||||
read_back_hash = u_boot_utils.md5sum_file(mounted_test_fn)
|
||||
cmd = ('rm', '-f', mounted_test_fn)
|
||||
u_boot_utils.run_and_log(u_boot_console, cmd)
|
||||
ignore_cleanup_errors = False
|
||||
finally:
|
||||
umount(ignore_errors=ignore_cleanup_errors)
|
||||
finally:
|
||||
stop_ums(ignore_errors=ignore_cleanup_errors)
|
||||
|
||||
written_hash = test_f.content_hash
|
||||
assert(written_hash == read_back_hash)
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
def test_unknown_command(u_boot_console):
|
||||
'''Test that executing an unknown command causes U-Boot to print an
|
||||
error.'''
|
||||
"""Test that executing an unknown command causes U-Boot to print an
|
||||
error."""
|
||||
|
||||
# The "unknown command" error is actively expected here,
|
||||
# so error detection for it is disabled.
|
||||
|
|
|
@ -14,6 +14,7 @@ import os
|
|||
import pytest
|
||||
import re
|
||||
import sys
|
||||
import u_boot_spawn
|
||||
|
||||
# Regexes for text we expect U-Boot to send to the console.
|
||||
pattern_u_boot_spl_signon = re.compile('(U-Boot SPL \\d{4}\\.\\d{2}-[^\r\n]*)')
|
||||
|
@ -21,14 +22,27 @@ pattern_u_boot_main_signon = re.compile('(U-Boot \\d{4}\\.\\d{2}-[^\r\n]*)')
|
|||
pattern_stop_autoboot_prompt = re.compile('Hit any key to stop autoboot: ')
|
||||
pattern_unknown_command = re.compile('Unknown command \'.*\' - try \'help\'')
|
||||
pattern_error_notification = re.compile('## Error: ')
|
||||
pattern_error_please_reset = re.compile('### ERROR ### Please RESET the board ###')
|
||||
|
||||
PAT_ID = 0
|
||||
PAT_RE = 1
|
||||
|
||||
bad_pattern_defs = (
|
||||
('spl_signon', pattern_u_boot_spl_signon),
|
||||
('main_signon', pattern_u_boot_main_signon),
|
||||
('stop_autoboot_prompt', pattern_stop_autoboot_prompt),
|
||||
('unknown_command', pattern_unknown_command),
|
||||
('error_notification', pattern_error_notification),
|
||||
('error_please_reset', pattern_error_please_reset),
|
||||
)
|
||||
|
||||
class ConsoleDisableCheck(object):
|
||||
'''Context manager (for Python's with statement) that temporarily disables
|
||||
"""Context manager (for Python's with statement) that temporarily disables
|
||||
the specified console output error check. This is useful when deliberately
|
||||
executing a command that is known to trigger one of the error checks, in
|
||||
order to test that the error condition is actually raised. This class is
|
||||
used internally by ConsoleBase::disable_check(); it is not intended for
|
||||
direct usage.'''
|
||||
direct usage."""
|
||||
|
||||
def __init__(self, console, check_type):
|
||||
self.console = console
|
||||
|
@ -36,18 +50,20 @@ class ConsoleDisableCheck(object):
|
|||
|
||||
def __enter__(self):
|
||||
self.console.disable_check_count[self.check_type] += 1
|
||||
self.console.eval_bad_patterns()
|
||||
|
||||
def __exit__(self, extype, value, traceback):
|
||||
self.console.disable_check_count[self.check_type] -= 1
|
||||
self.console.eval_bad_patterns()
|
||||
|
||||
class ConsoleBase(object):
|
||||
'''The interface through which test functions interact with the U-Boot
|
||||
"""The interface through which test functions interact with the U-Boot
|
||||
console. This primarily involves executing shell commands, capturing their
|
||||
results, and checking for common error conditions. Some common utilities
|
||||
are also provided too.'''
|
||||
are also provided too."""
|
||||
|
||||
def __init__(self, log, config, max_fifo_fill):
|
||||
'''Initialize a U-Boot console connection.
|
||||
"""Initialize a U-Boot console connection.
|
||||
|
||||
Can only usefully be called by sub-classes.
|
||||
|
||||
|
@ -64,7 +80,7 @@ class ConsoleBase(object):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self.log = log
|
||||
self.config = config
|
||||
|
@ -76,19 +92,20 @@ class ConsoleBase(object):
|
|||
self.prompt = self.config.buildconfig['config_sys_prompt'][1:-1]
|
||||
self.prompt_escaped = re.escape(self.prompt)
|
||||
self.p = None
|
||||
self.disable_check_count = {
|
||||
'spl_signon': 0,
|
||||
'main_signon': 0,
|
||||
'unknown_command': 0,
|
||||
'error_notification': 0,
|
||||
}
|
||||
self.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs}
|
||||
self.eval_bad_patterns()
|
||||
|
||||
self.at_prompt = False
|
||||
self.at_prompt_logevt = None
|
||||
self.ram_base = None
|
||||
|
||||
def eval_bad_patterns(self):
|
||||
self.bad_patterns = [pat[PAT_RE] for pat in bad_pattern_defs \
|
||||
if self.disable_check_count[pat[PAT_ID]] == 0]
|
||||
self.bad_pattern_ids = [pat[PAT_ID] for pat in bad_pattern_defs \
|
||||
if self.disable_check_count[pat[PAT_ID]] == 0]
|
||||
|
||||
def close(self):
|
||||
'''Terminate the connection to the U-Boot console.
|
||||
"""Terminate the connection to the U-Boot console.
|
||||
|
||||
This function is only useful once all interaction with U-Boot is
|
||||
complete. Once this function is called, data cannot be sent to or
|
||||
|
@ -99,7 +116,7 @@ class ConsoleBase(object):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
if self.p:
|
||||
self.p.close()
|
||||
|
@ -107,7 +124,7 @@ class ConsoleBase(object):
|
|||
|
||||
def run_command(self, cmd, wait_for_echo=True, send_nl=True,
|
||||
wait_for_prompt=True):
|
||||
'''Execute a command via the U-Boot console.
|
||||
"""Execute a command via the U-Boot console.
|
||||
|
||||
The command is always sent to U-Boot.
|
||||
|
||||
|
@ -142,29 +159,12 @@ class ConsoleBase(object):
|
|||
The output from U-Boot during command execution. In other
|
||||
words, the text U-Boot emitted between the point it echod the
|
||||
command string and emitted the subsequent command prompts.
|
||||
'''
|
||||
|
||||
self.ensure_spawned()
|
||||
"""
|
||||
|
||||
if self.at_prompt and \
|
||||
self.at_prompt_logevt != self.logstream.logfile.cur_evt:
|
||||
self.logstream.write(self.prompt, implicit=True)
|
||||
|
||||
bad_patterns = []
|
||||
bad_pattern_ids = []
|
||||
if (self.disable_check_count['spl_signon'] == 0 and
|
||||
self.u_boot_spl_signon):
|
||||
bad_patterns.append(self.u_boot_spl_signon_escaped)
|
||||
bad_pattern_ids.append('SPL signon')
|
||||
if self.disable_check_count['main_signon'] == 0:
|
||||
bad_patterns.append(self.u_boot_main_signon_escaped)
|
||||
bad_pattern_ids.append('U-Boot main signon')
|
||||
if self.disable_check_count['unknown_command'] == 0:
|
||||
bad_patterns.append(pattern_unknown_command)
|
||||
bad_pattern_ids.append('Unknown command')
|
||||
if self.disable_check_count['error_notification'] == 0:
|
||||
bad_patterns.append(pattern_error_notification)
|
||||
bad_pattern_ids.append('Error notification')
|
||||
try:
|
||||
self.at_prompt = False
|
||||
if send_nl:
|
||||
|
@ -178,18 +178,18 @@ class ConsoleBase(object):
|
|||
continue
|
||||
chunk = re.escape(chunk)
|
||||
chunk = chunk.replace('\\\n', '[\r\n]')
|
||||
m = self.p.expect([chunk] + bad_patterns)
|
||||
m = self.p.expect([chunk] + self.bad_patterns)
|
||||
if m != 0:
|
||||
self.at_prompt = False
|
||||
raise Exception('Bad pattern found on console: ' +
|
||||
bad_pattern_ids[m - 1])
|
||||
self.bad_pattern_ids[m - 1])
|
||||
if not wait_for_prompt:
|
||||
return
|
||||
m = self.p.expect([self.prompt_escaped] + bad_patterns)
|
||||
m = self.p.expect([self.prompt_escaped] + self.bad_patterns)
|
||||
if m != 0:
|
||||
self.at_prompt = False
|
||||
raise Exception('Bad pattern found on console: ' +
|
||||
bad_pattern_ids[m - 1])
|
||||
self.bad_pattern_ids[m - 1])
|
||||
self.at_prompt = True
|
||||
self.at_prompt_logevt = self.logstream.logfile.cur_evt
|
||||
# Only strip \r\n; space/TAB might be significant if testing
|
||||
|
@ -201,7 +201,7 @@ class ConsoleBase(object):
|
|||
raise
|
||||
|
||||
def ctrlc(self):
|
||||
'''Send a CTRL-C character to U-Boot.
|
||||
"""Send a CTRL-C character to U-Boot.
|
||||
|
||||
This is useful in order to stop execution of long-running synchronous
|
||||
commands such as "ums".
|
||||
|
@ -211,12 +211,72 @@ class ConsoleBase(object):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self.log.action('Sending Ctrl-C')
|
||||
self.run_command(chr(3), wait_for_echo=False, send_nl=False)
|
||||
|
||||
def wait_for(self, text):
|
||||
"""Wait for a pattern to be emitted by U-Boot.
|
||||
|
||||
This is useful when a long-running command such as "dfu" is executing,
|
||||
and it periodically emits some text that should show up at a specific
|
||||
location in the log file.
|
||||
|
||||
Args:
|
||||
text: The text to wait for; either a string (containing raw text,
|
||||
not a regular expression) or an re object.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
|
||||
if type(text) == type(''):
|
||||
text = re.escape(text)
|
||||
m = self.p.expect([text] + self.bad_patterns)
|
||||
if m != 0:
|
||||
raise Exception('Bad pattern found on console: ' +
|
||||
self.bad_pattern_ids[m - 1])
|
||||
|
||||
def drain_console(self):
|
||||
"""Read from and log the U-Boot console for a short time.
|
||||
|
||||
U-Boot's console output is only logged when the test code actively
|
||||
waits for U-Boot to emit specific data. There are cases where tests
|
||||
can fail without doing this. For example, if a test asks U-Boot to
|
||||
enable USB device mode, then polls until a host-side device node
|
||||
exists. In such a case, it is useful to log U-Boot's console output
|
||||
in case U-Boot printed clues as to why the host-side even did not
|
||||
occur. This function will do that.
|
||||
|
||||
Args:
|
||||
None.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
|
||||
# If we are already not connected to U-Boot, there's nothing to drain.
|
||||
# This should only happen when a previous call to run_command() or
|
||||
# wait_for() failed (and hence the output has already been logged), or
|
||||
# the system is shutting down.
|
||||
if not self.p:
|
||||
return
|
||||
|
||||
orig_timeout = self.p.timeout
|
||||
try:
|
||||
# Drain the log for a relatively short time.
|
||||
self.p.timeout = 1000
|
||||
# Wait for something U-Boot will likely never send. This will
|
||||
# cause the console output to be read and logged.
|
||||
self.p.expect(['This should never match U-Boot output'])
|
||||
except u_boot_spawn.Timeout:
|
||||
pass
|
||||
finally:
|
||||
self.p.timeout = orig_timeout
|
||||
|
||||
def ensure_spawned(self):
|
||||
'''Ensure a connection to a correctly running U-Boot instance.
|
||||
"""Ensure a connection to a correctly running U-Boot instance.
|
||||
|
||||
This may require spawning a new Sandbox process or resetting target
|
||||
hardware, as defined by the implementation sub-class.
|
||||
|
@ -228,7 +288,7 @@ class ConsoleBase(object):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
if self.p:
|
||||
return
|
||||
|
@ -243,26 +303,30 @@ class ConsoleBase(object):
|
|||
self.p.timeout = 30000
|
||||
self.p.logfile_read = self.logstream
|
||||
if self.config.buildconfig.get('CONFIG_SPL', False) == 'y':
|
||||
self.p.expect([pattern_u_boot_spl_signon])
|
||||
self.u_boot_spl_signon = self.p.after
|
||||
self.u_boot_spl_signon_escaped = re.escape(self.p.after)
|
||||
else:
|
||||
self.u_boot_spl_signon = None
|
||||
self.p.expect([pattern_u_boot_main_signon])
|
||||
self.u_boot_main_signon = self.p.after
|
||||
self.u_boot_main_signon_escaped = re.escape(self.p.after)
|
||||
build_idx = self.u_boot_main_signon.find(', Build:')
|
||||
m = self.p.expect([pattern_u_boot_spl_signon] + self.bad_patterns)
|
||||
if m != 0:
|
||||
raise Exception('Bad pattern found on console: ' +
|
||||
self.bad_pattern_ids[m - 1])
|
||||
m = self.p.expect([pattern_u_boot_main_signon] + self.bad_patterns)
|
||||
if m != 0:
|
||||
raise Exception('Bad pattern found on console: ' +
|
||||
self.bad_pattern_ids[m - 1])
|
||||
signon = self.p.after
|
||||
build_idx = signon.find(', Build:')
|
||||
if build_idx == -1:
|
||||
self.u_boot_version_string = self.u_boot_main_signon
|
||||
self.u_boot_version_string = signon
|
||||
else:
|
||||
self.u_boot_version_string = self.u_boot_main_signon[:build_idx]
|
||||
self.u_boot_version_string = signon[:build_idx]
|
||||
while True:
|
||||
match = self.p.expect([self.prompt_escaped,
|
||||
pattern_stop_autoboot_prompt])
|
||||
if match == 1:
|
||||
m = self.p.expect([self.prompt_escaped,
|
||||
pattern_stop_autoboot_prompt] + self.bad_patterns)
|
||||
if m == 0:
|
||||
break
|
||||
if m == 1:
|
||||
self.p.send(chr(3)) # CTRL-C
|
||||
continue
|
||||
break
|
||||
raise Exception('Bad pattern found on console: ' +
|
||||
self.bad_pattern_ids[m - 2])
|
||||
self.at_prompt = True
|
||||
self.at_prompt_logevt = self.logstream.logfile.cur_evt
|
||||
except Exception as ex:
|
||||
|
@ -271,7 +335,7 @@ class ConsoleBase(object):
|
|||
raise
|
||||
|
||||
def cleanup_spawn(self):
|
||||
'''Shut down all interaction with the U-Boot instance.
|
||||
"""Shut down all interaction with the U-Boot instance.
|
||||
|
||||
This is used when an error is detected prior to re-establishing a
|
||||
connection with a fresh U-Boot instance.
|
||||
|
@ -283,7 +347,7 @@ class ConsoleBase(object):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
try:
|
||||
if self.p:
|
||||
|
@ -293,7 +357,7 @@ class ConsoleBase(object):
|
|||
self.p = None
|
||||
|
||||
def validate_version_string_in_text(self, text):
|
||||
'''Assert that a command's output includes the U-Boot signon message.
|
||||
"""Assert that a command's output includes the U-Boot signon message.
|
||||
|
||||
This is primarily useful for validating the "version" command without
|
||||
duplicating the signon text regex in a test function.
|
||||
|
@ -303,12 +367,12 @@ class ConsoleBase(object):
|
|||
|
||||
Returns:
|
||||
Nothing. An exception is raised if the validation fails.
|
||||
'''
|
||||
"""
|
||||
|
||||
assert(self.u_boot_version_string in text)
|
||||
|
||||
def disable_check(self, check_type):
|
||||
'''Temporarily disable an error check of U-Boot's output.
|
||||
"""Temporarily disable an error check of U-Boot's output.
|
||||
|
||||
Create a new context manager (for use with the "with" statement) which
|
||||
temporarily disables a particular console output error check.
|
||||
|
@ -319,42 +383,6 @@ class ConsoleBase(object):
|
|||
|
||||
Returns:
|
||||
A context manager object.
|
||||
'''
|
||||
"""
|
||||
|
||||
return ConsoleDisableCheck(self, check_type)
|
||||
|
||||
def find_ram_base(self):
|
||||
'''Find the running U-Boot's RAM location.
|
||||
|
||||
Probe the running U-Boot to determine the address of the first bank
|
||||
of RAM. This is useful for tests that test reading/writing RAM, or
|
||||
load/save files that aren't associated with some standard address
|
||||
typically represented in an environment variable such as
|
||||
${kernel_addr_r}. The value is cached so that it only needs to be
|
||||
actively read once.
|
||||
|
||||
Args:
|
||||
None.
|
||||
|
||||
Returns:
|
||||
The address of U-Boot's first RAM bank, as an integer.
|
||||
'''
|
||||
|
||||
if self.config.buildconfig.get('config_cmd_bdi', 'n') != 'y':
|
||||
pytest.skip('bdinfo command not supported')
|
||||
if self.ram_base == -1:
|
||||
pytest.skip('Previously failed to find RAM bank start')
|
||||
if self.ram_base is not None:
|
||||
return self.ram_base
|
||||
|
||||
with self.log.section('find_ram_base'):
|
||||
response = self.run_command('bdinfo')
|
||||
for l in response.split('\n'):
|
||||
if '-> start' in l:
|
||||
self.ram_base = int(l.split('=')[1].strip(), 16)
|
||||
break
|
||||
if self.ram_base is None:
|
||||
self.ram_base = -1
|
||||
raise Exception('Failed to find RAM bank start in `bdinfo`')
|
||||
|
||||
return self.ram_base
|
||||
|
|
|
@ -11,15 +11,15 @@ from u_boot_spawn import Spawn
|
|||
from u_boot_console_base import ConsoleBase
|
||||
|
||||
class ConsoleExecAttach(ConsoleBase):
|
||||
'''Represents a physical connection to a U-Boot console, typically via a
|
||||
"""Represents a physical connection to a U-Boot console, typically via a
|
||||
serial port. This implementation executes a sub-process to attach to the
|
||||
console, expecting that the stdin/out of the sub-process will be forwarded
|
||||
to/from the physical hardware. This approach isolates the test infra-
|
||||
structure from the user-/installation-specific details of how to
|
||||
communicate with, and the identity of, serial ports etc.'''
|
||||
communicate with, and the identity of, serial ports etc."""
|
||||
|
||||
def __init__(self, log, config):
|
||||
'''Initialize a U-Boot console connection.
|
||||
"""Initialize a U-Boot console connection.
|
||||
|
||||
Args:
|
||||
log: A multiplexed_log.Logfile instance.
|
||||
|
@ -27,7 +27,7 @@ class ConsoleExecAttach(ConsoleBase):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
# The max_fifo_fill value might need tweaking per-board/-SoC?
|
||||
# 1 would be safe anywhere, but is very slow (a pexpect issue?).
|
||||
|
@ -42,7 +42,7 @@ class ConsoleExecAttach(ConsoleBase):
|
|||
runner.close()
|
||||
|
||||
def get_spawn(self):
|
||||
'''Connect to a fresh U-Boot instance.
|
||||
"""Connect to a fresh U-Boot instance.
|
||||
|
||||
The target board is reset, so that U-Boot begins running from scratch.
|
||||
|
||||
|
@ -51,7 +51,7 @@ class ConsoleExecAttach(ConsoleBase):
|
|||
|
||||
Returns:
|
||||
A u_boot_spawn.Spawn object that is attached to U-Boot.
|
||||
'''
|
||||
"""
|
||||
|
||||
args = [self.config.board_type, self.config.board_identity]
|
||||
s = Spawn(['u-boot-test-console'] + args)
|
||||
|
|
|
@ -10,11 +10,11 @@ from u_boot_spawn import Spawn
|
|||
from u_boot_console_base import ConsoleBase
|
||||
|
||||
class ConsoleSandbox(ConsoleBase):
|
||||
'''Represents a connection to a sandbox U-Boot console, executed as a sub-
|
||||
process.'''
|
||||
"""Represents a connection to a sandbox U-Boot console, executed as a sub-
|
||||
process."""
|
||||
|
||||
def __init__(self, log, config):
|
||||
'''Initialize a U-Boot console connection.
|
||||
"""Initialize a U-Boot console connection.
|
||||
|
||||
Args:
|
||||
log: A multiplexed_log.Logfile instance.
|
||||
|
@ -22,12 +22,12 @@ class ConsoleSandbox(ConsoleBase):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
super(ConsoleSandbox, self).__init__(log, config, max_fifo_fill=1024)
|
||||
|
||||
def get_spawn(self):
|
||||
'''Connect to a fresh U-Boot instance.
|
||||
"""Connect to a fresh U-Boot instance.
|
||||
|
||||
A new sandbox process is created, so that U-Boot begins running from
|
||||
scratch.
|
||||
|
@ -37,26 +37,30 @@ class ConsoleSandbox(ConsoleBase):
|
|||
|
||||
Returns:
|
||||
A u_boot_spawn.Spawn object that is attached to U-Boot.
|
||||
'''
|
||||
"""
|
||||
|
||||
return Spawn([self.config.build_dir + '/u-boot'])
|
||||
cmd = [
|
||||
self.config.build_dir + '/u-boot',
|
||||
'-d',
|
||||
self.config.build_dir + '/arch/sandbox/dts/test.dtb'
|
||||
]
|
||||
return Spawn(cmd, cwd=self.config.source_dir)
|
||||
|
||||
def kill(self, sig):
|
||||
'''Send a specific Unix signal to the sandbox process.
|
||||
"""Send a specific Unix signal to the sandbox process.
|
||||
|
||||
Args:
|
||||
sig: The Unix signal to send to the process.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self.ensure_spawned()
|
||||
self.log.action('kill %d' % sig)
|
||||
self.p.kill(sig)
|
||||
|
||||
def validate_exited(self):
|
||||
'''Determine whether the sandbox process has exited.
|
||||
"""Determine whether the sandbox process has exited.
|
||||
|
||||
If required, this function waits a reasonable time for the process to
|
||||
exit.
|
||||
|
@ -66,7 +70,7 @@ class ConsoleSandbox(ConsoleBase):
|
|||
|
||||
Returns:
|
||||
Boolean indicating whether the process has exited.
|
||||
'''
|
||||
"""
|
||||
|
||||
p = self.p
|
||||
self.p = None
|
||||
|
|
|
@ -12,23 +12,25 @@ import select
|
|||
import time
|
||||
|
||||
class Timeout(Exception):
|
||||
'''An exception sub-class that indicates that a timeout occurred.'''
|
||||
"""An exception sub-class that indicates that a timeout occurred."""
|
||||
pass
|
||||
|
||||
class Spawn(object):
|
||||
'''Represents the stdio of a freshly created sub-process. Commands may be
|
||||
"""Represents the stdio of a freshly created sub-process. Commands may be
|
||||
sent to the process, and responses waited for.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, args):
|
||||
'''Spawn (fork/exec) the sub-process.
|
||||
def __init__(self, args, cwd=None):
|
||||
"""Spawn (fork/exec) the sub-process.
|
||||
|
||||
Args:
|
||||
args: array of processs arguments. argv[0] is the command to execute.
|
||||
args: array of processs arguments. argv[0] is the command to
|
||||
execute.
|
||||
cwd: the directory to run the process in, or None for no change.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
self.waited = False
|
||||
self.buf = ''
|
||||
|
@ -44,6 +46,8 @@ class Spawn(object):
|
|||
# run under "go" (www.go.cd). Perhaps this happens under any
|
||||
# background (non-interactive) system?
|
||||
signal.signal(signal.SIGHUP, signal.SIG_DFL)
|
||||
if cwd:
|
||||
os.chdir(cwd)
|
||||
os.execvp(args[0], args)
|
||||
except:
|
||||
print 'CHILD EXECEPTION:'
|
||||
|
@ -56,26 +60,26 @@ class Spawn(object):
|
|||
self.poll.register(self.fd, select.POLLIN | select.POLLPRI | select.POLLERR | select.POLLHUP | select.POLLNVAL)
|
||||
|
||||
def kill(self, sig):
|
||||
'''Send unix signal "sig" to the child process.
|
||||
"""Send unix signal "sig" to the child process.
|
||||
|
||||
Args:
|
||||
sig: The signal number to send.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
os.kill(self.pid, sig)
|
||||
|
||||
def isalive(self):
|
||||
'''Determine whether the child process is still running.
|
||||
"""Determine whether the child process is still running.
|
||||
|
||||
Args:
|
||||
None.
|
||||
|
||||
Returns:
|
||||
Boolean indicating whether process is alive.
|
||||
'''
|
||||
"""
|
||||
|
||||
if self.waited:
|
||||
return False
|
||||
|
@ -88,19 +92,19 @@ class Spawn(object):
|
|||
return False
|
||||
|
||||
def send(self, data):
|
||||
'''Send data to the sub-process's stdin.
|
||||
"""Send data to the sub-process's stdin.
|
||||
|
||||
Args:
|
||||
data: The data to send to the process.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
os.write(self.fd, data)
|
||||
|
||||
def expect(self, patterns):
|
||||
'''Wait for the sub-process to emit specific data.
|
||||
"""Wait for the sub-process to emit specific data.
|
||||
|
||||
This function waits for the process to emit one pattern from the
|
||||
supplied list of patterns, or for a timeout to occur.
|
||||
|
@ -116,12 +120,13 @@ class Spawn(object):
|
|||
Notable exceptions:
|
||||
Timeout, if the process did not emit any of the patterns within
|
||||
the expected time.
|
||||
'''
|
||||
"""
|
||||
|
||||
for pi in xrange(len(patterns)):
|
||||
if type(patterns[pi]) == type(''):
|
||||
patterns[pi] = re.compile(patterns[pi])
|
||||
|
||||
tstart_s = time.time()
|
||||
try:
|
||||
while True:
|
||||
earliest_m = None
|
||||
|
@ -131,7 +136,7 @@ class Spawn(object):
|
|||
m = pattern.search(self.buf)
|
||||
if not m:
|
||||
continue
|
||||
if earliest_m and m.start() > earliest_m.start():
|
||||
if earliest_m and m.start() >= earliest_m.start():
|
||||
continue
|
||||
earliest_m = m
|
||||
earliest_pi = pi
|
||||
|
@ -142,7 +147,11 @@ class Spawn(object):
|
|||
self.after = self.buf[pos:posafter]
|
||||
self.buf = self.buf[posafter:]
|
||||
return earliest_pi
|
||||
events = self.poll.poll(self.timeout)
|
||||
tnow_s = time.time()
|
||||
tdelta_ms = (tnow_s - tstart_s) * 1000
|
||||
if tdelta_ms > self.timeout:
|
||||
raise Timeout()
|
||||
events = self.poll.poll(self.timeout - tdelta_ms)
|
||||
if not events:
|
||||
raise Timeout()
|
||||
c = os.read(self.fd, 1024)
|
||||
|
@ -156,7 +165,7 @@ class Spawn(object):
|
|||
self.logfile_read.flush()
|
||||
|
||||
def close(self):
|
||||
'''Close the stdio connection to the sub-process.
|
||||
"""Close the stdio connection to the sub-process.
|
||||
|
||||
This also waits a reasonable time for the sub-process to stop running.
|
||||
|
||||
|
@ -165,7 +174,7 @@ class Spawn(object):
|
|||
|
||||
Returns:
|
||||
Nothing.
|
||||
'''
|
||||
"""
|
||||
|
||||
os.close(self.fd)
|
||||
for i in xrange(100):
|
||||
|
|
209
test/py/u_boot_utils.py
Normal file
209
test/py/u_boot_utils.py
Normal file
|
@ -0,0 +1,209 @@
|
|||
# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
# Utility code shared across multiple tests.
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
import time
|
||||
|
||||
def md5sum_data(data):
|
||||
"""Calculate the MD5 hash of some data.
|
||||
|
||||
Args:
|
||||
data: The data to hash.
|
||||
|
||||
Returns:
|
||||
The hash of the data, as a binary string.
|
||||
"""
|
||||
|
||||
h = hashlib.md5()
|
||||
h.update(data)
|
||||
return h.digest()
|
||||
|
||||
def md5sum_file(fn, max_length=None):
|
||||
"""Calculate the MD5 hash of the contents of a file.
|
||||
|
||||
Args:
|
||||
fn: The filename of the file to hash.
|
||||
max_length: The number of bytes to hash. If the file has more
|
||||
bytes than this, they will be ignored. If None or omitted, the
|
||||
entire file will be hashed.
|
||||
|
||||
Returns:
|
||||
The hash of the file content, as a binary string.
|
||||
"""
|
||||
|
||||
with open(fn, 'rb') as fh:
|
||||
if max_length:
|
||||
params = [max_length]
|
||||
else:
|
||||
params = []
|
||||
data = fh.read(*params)
|
||||
return md5sum_data(data)
|
||||
|
||||
class PersistentRandomFile(object):
|
||||
"""Generate and store information about a persistent file containing
|
||||
random data."""
|
||||
|
||||
def __init__(self, u_boot_console, fn, size):
|
||||
"""Create or process the persistent file.
|
||||
|
||||
If the file does not exist, it is generated.
|
||||
|
||||
If the file does exist, its content is hashed for later comparison.
|
||||
|
||||
These files are always located in the "persistent data directory" of
|
||||
the current test run.
|
||||
|
||||
Args:
|
||||
u_boot_console: A console connection to U-Boot.
|
||||
fn: The filename (without path) to create.
|
||||
size: The desired size of the file in bytes.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
|
||||
self.fn = fn
|
||||
|
||||
self.abs_fn = u_boot_console.config.persistent_data_dir + '/' + fn
|
||||
|
||||
if os.path.exists(self.abs_fn):
|
||||
u_boot_console.log.action('Persistent data file ' + self.abs_fn +
|
||||
' already exists')
|
||||
self.content_hash = md5sum_file(self.abs_fn)
|
||||
else:
|
||||
u_boot_console.log.action('Generating ' + self.abs_fn +
|
||||
' (random, persistent, %d bytes)' % size)
|
||||
data = os.urandom(size)
|
||||
with open(self.abs_fn, 'wb') as fh:
|
||||
fh.write(data)
|
||||
self.content_hash = md5sum_data(data)
|
||||
|
||||
def attempt_to_open_file(fn):
|
||||
"""Attempt to open a file, without throwing exceptions.
|
||||
|
||||
Any errors (exceptions) that occur during the attempt to open the file
|
||||
are ignored. This is useful in order to test whether a file (in
|
||||
particular, a device node) exists and can be successfully opened, in order
|
||||
to poll for e.g. USB enumeration completion.
|
||||
|
||||
Args:
|
||||
fn: The filename to attempt to open.
|
||||
|
||||
Returns:
|
||||
An open file handle to the file, or None if the file could not be
|
||||
opened.
|
||||
"""
|
||||
|
||||
try:
|
||||
return open(fn, 'rb')
|
||||
except:
|
||||
return None
|
||||
|
||||
def wait_until_open_succeeds(fn):
|
||||
"""Poll until a file can be opened, or a timeout occurs.
|
||||
|
||||
Continually attempt to open a file, and return when this succeeds, or
|
||||
raise an exception after a timeout.
|
||||
|
||||
Args:
|
||||
fn: The filename to attempt to open.
|
||||
|
||||
Returns:
|
||||
An open file handle to the file.
|
||||
"""
|
||||
|
||||
for i in xrange(100):
|
||||
fh = attempt_to_open_file(fn)
|
||||
if fh:
|
||||
return fh
|
||||
time.sleep(0.1)
|
||||
raise Exception('File could not be opened')
|
||||
|
||||
def wait_until_file_open_fails(fn, ignore_errors):
|
||||
"""Poll until a file cannot be opened, or a timeout occurs.
|
||||
|
||||
Continually attempt to open a file, and return when this fails, or
|
||||
raise an exception after a timeout.
|
||||
|
||||
Args:
|
||||
fn: The filename to attempt to open.
|
||||
ignore_errors: Indicate whether to ignore timeout errors. If True, the
|
||||
function will simply return if a timeout occurs, otherwise an
|
||||
exception will be raised.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
|
||||
for i in xrange(100):
|
||||
fh = attempt_to_open_file(fn)
|
||||
if not fh:
|
||||
return
|
||||
fh.close()
|
||||
time.sleep(0.1)
|
||||
if ignore_errors:
|
||||
return
|
||||
raise Exception('File can still be opened')
|
||||
|
||||
def run_and_log(u_boot_console, cmd, ignore_errors=False):
|
||||
"""Run a command and log its output.
|
||||
|
||||
Args:
|
||||
u_boot_console: A console connection to U-Boot.
|
||||
cmd: The command to run, as an array of argv[].
|
||||
ignore_errors: Indicate whether to ignore errors. If True, the function
|
||||
will simply return if the command cannot be executed or exits with
|
||||
an error code, otherwise an exception will be raised if such
|
||||
problems occur.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
|
||||
runner = u_boot_console.log.get_runner(cmd[0], sys.stdout)
|
||||
runner.run(cmd, ignore_errors=ignore_errors)
|
||||
runner.close()
|
||||
|
||||
ram_base = None
|
||||
def find_ram_base(u_boot_console):
|
||||
"""Find the running U-Boot's RAM location.
|
||||
|
||||
Probe the running U-Boot to determine the address of the first bank
|
||||
of RAM. This is useful for tests that test reading/writing RAM, or
|
||||
load/save files that aren't associated with some standard address
|
||||
typically represented in an environment variable such as
|
||||
${kernel_addr_r}. The value is cached so that it only needs to be
|
||||
actively read once.
|
||||
|
||||
Args:
|
||||
u_boot_console: A console connection to U-Boot.
|
||||
|
||||
Returns:
|
||||
The address of U-Boot's first RAM bank, as an integer.
|
||||
"""
|
||||
|
||||
global ram_base
|
||||
if u_boot_console.config.buildconfig.get('config_cmd_bdi', 'n') != 'y':
|
||||
pytest.skip('bdinfo command not supported')
|
||||
if ram_base == -1:
|
||||
pytest.skip('Previously failed to find RAM bank start')
|
||||
if ram_base is not None:
|
||||
return ram_base
|
||||
|
||||
with u_boot_console.log.section('find_ram_base'):
|
||||
response = u_boot_console.run_command('bdinfo')
|
||||
for l in response.split('\n'):
|
||||
if '-> start' in l:
|
||||
ram_base = int(l.split('=')[1].strip(), 16)
|
||||
break
|
||||
if ram_base is None:
|
||||
ram_base = -1
|
||||
raise Exception('Failed to find RAM bank start in `bdinfo`')
|
||||
|
||||
return ram_base
|
|
@ -1,30 +0,0 @@
|
|||
UMS test script.
|
||||
|
||||
ums_gadget_test.sh
|
||||
==================
|
||||
|
||||
Example usage:
|
||||
1. On the target:
|
||||
create UMS exportable partitions (with e.g. gpt write), or specify a
|
||||
partition number (PART_NUM) as "-" to use the entire device
|
||||
ums 0 mmc 0
|
||||
2. On the host:
|
||||
sudo test/ums/ums_gadget_test.sh VID PID PART_NUM [-f FILE_SYSTEM] [test_file]
|
||||
e.g. sudo test/ums/ums_gadget_test.sh 0525 a4a5 6 -f vfat ./dat_14M.img
|
||||
|
||||
... where:
|
||||
VID - UMS device USB Vendor ID
|
||||
PID - UMS device USB Product ID
|
||||
PART_NUM - is the partition number on which UMS operates or "-" to use the
|
||||
whole device
|
||||
|
||||
Information about available partitions on the target one can read with using
|
||||
the 'mmc part' or 'part list' commands.
|
||||
|
||||
The partition num (PART_NUM) can be specified as '-' for using the whole device.
|
||||
|
||||
The [-f FILE_SYSTEM] optional switch allows for formatting target partition to
|
||||
FILE_SYSTEM.
|
||||
|
||||
The last, optional [test_file] parameter is for specifying the exact test file
|
||||
to use.
|
|
@ -1,183 +0,0 @@
|
|||
#! /bin/bash
|
||||
|
||||
# Copyright (C) 2014 Samsung Electronics
|
||||
# Lukasz Majewski <l.majewski@samsung.com>
|
||||
#
|
||||
# UMS operation test script
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
clear
|
||||
|
||||
COLOUR_RED="\33[31m"
|
||||
COLOUR_GREEN="\33[32m"
|
||||
COLOUR_ORANGE="\33[33m"
|
||||
COLOUR_DEFAULT="\33[0m"
|
||||
|
||||
DIR=./
|
||||
SUFFIX=img
|
||||
RCV_DIR=rcv/
|
||||
LOG_FILE=./log/log-`date +%d-%m-%Y_%H-%M-%S`
|
||||
|
||||
cd `dirname $0`
|
||||
../dfu/dfu_gadget_test_init.sh 33M 97M
|
||||
|
||||
cleanup () {
|
||||
rm -rf $RCV_DIR $MNT_DIR
|
||||
}
|
||||
|
||||
control_c()
|
||||
# run if user hits control-c
|
||||
{
|
||||
echo -en "\n*** CTRL+C ***\n"
|
||||
umount $MNT_DIR
|
||||
cleanup
|
||||
exit 0
|
||||
}
|
||||
|
||||
# trap keyboard interrupt (control-c)
|
||||
trap control_c SIGINT
|
||||
|
||||
die () {
|
||||
printf " $COLOUR_RED FAILED $COLOUR_DEFAULT \n"
|
||||
cleanup
|
||||
exit 1
|
||||
}
|
||||
|
||||
calculate_md5sum () {
|
||||
MD5SUM=`md5sum $1`
|
||||
MD5SUM=`echo $MD5SUM | cut -d ' ' -f1`
|
||||
echo "md5sum:"$MD5SUM
|
||||
}
|
||||
|
||||
ums_test_file () {
|
||||
printf "$COLOUR_GREEN========================================================================================= $COLOUR_DEFAULT\n"
|
||||
printf "File:$COLOUR_GREEN %s $COLOUR_DEFAULT\n" $1
|
||||
|
||||
mount /dev/$MEM_DEV $MNT_DIR
|
||||
if [ -f $MNT_DIR/dat_* ]; then
|
||||
rm $MNT_DIR/dat_*
|
||||
fi
|
||||
|
||||
cp ./$1 $MNT_DIR
|
||||
|
||||
while true; do
|
||||
umount $MNT_DIR > /dev/null 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
break
|
||||
fi
|
||||
printf "$COLOUR_ORANGE\tSleeping to wait for umount...$COLOUR_DEFAULT\n"
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo -n "TX: "
|
||||
calculate_md5sum $1
|
||||
|
||||
MD5_TX=$MD5SUM
|
||||
sleep 1
|
||||
N_FILE=$DIR$RCV_DIR${1:2}"_rcv"
|
||||
|
||||
mount /dev/$MEM_DEV $MNT_DIR
|
||||
cp $MNT_DIR/$1 $N_FILE || die $?
|
||||
rm $MNT_DIR/$1
|
||||
umount $MNT_DIR
|
||||
|
||||
echo -n "RX: "
|
||||
calculate_md5sum $N_FILE
|
||||
MD5_RX=$MD5SUM
|
||||
|
||||
if [ "$MD5_TX" == "$MD5_RX" ]; then
|
||||
printf " $COLOUR_GREEN -------> OK $COLOUR_DEFAULT \n"
|
||||
else
|
||||
printf " $COLOUR_RED -------> FAILED $COLOUR_DEFAULT \n"
|
||||
cleanup
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
printf "$COLOUR_GREEN========================================================================================= $COLOUR_DEFAULT\n"
|
||||
echo "U-boot UMS test program"
|
||||
|
||||
if [ $EUID -ne 0 ]; then
|
||||
echo "You must be root to do this." 1>&2
|
||||
exit 100
|
||||
fi
|
||||
|
||||
if [ $# -lt 3 ]; then
|
||||
echo "Wrong number of arguments"
|
||||
echo "Example:"
|
||||
echo "sudo ./ums_gadget_test.sh VID PID PART_NUM [-f ext4] [test_file]"
|
||||
die
|
||||
fi
|
||||
|
||||
MNT_DIR="/mnt/tmp-ums-test"
|
||||
|
||||
VID=$1; shift
|
||||
PID=$1; shift
|
||||
PART_NUM=$1; shift
|
||||
|
||||
if [ "$1" == "-f" ]; then
|
||||
shift
|
||||
FS_TO_FORMAT=$1; shift
|
||||
fi
|
||||
|
||||
TEST_FILE=$1
|
||||
|
||||
for f in `find /sys -type f -name idProduct`; do
|
||||
d=`dirname ${f}`
|
||||
if [ `cat ${d}/idVendor` != "${VID}" ]; then
|
||||
continue
|
||||
fi
|
||||
if [ `cat ${d}/idProduct` != "${PID}" ]; then
|
||||
continue
|
||||
fi
|
||||
USB_DEV=${d}
|
||||
break
|
||||
done
|
||||
|
||||
if [ -z "${USB_DEV}" ]; then
|
||||
echo "Connect target"
|
||||
echo "e.g. ums 0 mmc 0"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MEM_DEV=`find $USB_DEV -type d -name "sd[a-z]" | awk -F/ '{print $(NF)}' -`
|
||||
|
||||
mkdir -p $RCV_DIR
|
||||
if [ ! -d $MNT_DIR ]; then
|
||||
mkdir -p $MNT_DIR
|
||||
fi
|
||||
|
||||
if [ "$PART_NUM" == "-" ]; then
|
||||
PART_NUM=""
|
||||
fi
|
||||
MEM_DEV=$MEM_DEV$PART_NUM
|
||||
|
||||
if [ -n "$FS_TO_FORMAT" ]; then
|
||||
echo -n "Formatting partition /dev/$MEM_DEV to $FS_TO_FORMAT"
|
||||
mkfs -t $FS_TO_FORMAT /dev/$MEM_DEV > /dev/null 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
printf " $COLOUR_GREEN DONE $COLOUR_DEFAULT \n"
|
||||
else
|
||||
die
|
||||
fi
|
||||
fi
|
||||
|
||||
printf "Mount: /dev/$MEM_DEV \n"
|
||||
|
||||
if [ -n "$TEST_FILE" ]; then
|
||||
if [ ! -e $TEST_FILE ]; then
|
||||
echo "No file: $TEST_FILE"
|
||||
die
|
||||
fi
|
||||
ums_test_file $TEST_FILE
|
||||
else
|
||||
for file in $DIR*.$SUFFIX
|
||||
do
|
||||
ums_test_file $file
|
||||
done
|
||||
fi
|
||||
|
||||
cleanup
|
||||
|
||||
exit 0
|
Loading…
Add table
Reference in a new issue