Linux Audio

Check our new training course

Loading...
/*
 * Copyright (c) 2018 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/init.h>
#include <zephyr/ztest.h>
#include <zephyr/sys/printk.h>
#include <zephyr/linker/sections.h>
#include "abstract_driver.h"


#define DUMMY_PORT_1    "dummy"
#define DUMMY_PORT_2    "dummy_driver"
#define DUMMY_NOINIT    "dummy_noinit"
#define BAD_DRIVER	"bad_driver"

#define MY_DRIVER_A     "my_driver_A"
#define MY_DRIVER_B     "my_driver_B"

/* A device without init call */
DEVICE_DEFINE(dummy_noinit, DUMMY_NOINIT, NULL, NULL, NULL, NULL,
	      POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, NULL);

/**
 * @brief Test cases to verify device objects
 *
 * Verify zephyr device driver apis with different device types
 *
 * @defgroup kernel_device_tests Device
 *
 * @ingroup all_tests
 *
 * @{
 */

/**
 * @brief Test device object binding
 *
 * Validates device binding for an existing and a non-existing device object.
 * It creates a dummy_driver device object with basic init and configuration
 * information and validates its binding.
 *
 * Validates three kinds situations of driver object:
 * 1. A non-existing device object.
 * 2. An existing device object with basic init and configuration information.
 * 3. A failed init device object.
 *
 * @ingroup kernel_device_tests
 *
 * @see device_get_binding(), DEVICE_DEFINE()
 */
ZTEST(device, test_dummy_device)
{
	const struct device *dev;

	/* Validates device binding for a non-existing device object */
	dev = device_get_binding(DUMMY_PORT_1);
	zassert_is_null(dev);

	/* Validates device binding for an existing device object */
	dev = device_get_binding(DUMMY_PORT_2);
	zassert_not_null(dev);

	/* Validates device binding for an existing device object */
	dev = device_get_binding(DUMMY_NOINIT);
	zassert_not_null(dev);

	/* device_get_binding() returns false for device object
	 * with failed init.
	 */
	dev = device_get_binding(BAD_DRIVER);
	zassert_is_null(dev);
}

/**
 * @brief Test device binding for existing device
 *
 * Validates device binding for an existing device object.
 *
 * @see device_get_binding(), DEVICE_DEFINE()
 */
ZTEST_USER(device, test_dynamic_name)
{
	const struct device *mux;
	char name[sizeof(DUMMY_PORT_2)];

	snprintk(name, sizeof(name), "%s", DUMMY_PORT_2);
	mux = device_get_binding(name);
	zassert_true(mux != NULL);
}

/**
 * @brief Test device binding for non-existing device
 *
 * Validates binding of a random device driver(non-defined driver) named
 * "ANOTHER_BOGUS_NAME".
 *
 * @see device_get_binding(), DEVICE_DEFINE()
 */
ZTEST_USER(device, test_bogus_dynamic_name)
{
	const struct device *mux;
	char name[64];

	snprintk(name, sizeof(name), "ANOTHER_BOGUS_NAME");
	mux = device_get_binding(name);
	zassert_true(mux == NULL);
}

/**
 * @brief Test device binding for passing null name
 *
 * Validates device binding for device object when given dynamic name is null.
 *
 * @see device_get_binding(), DEVICE_DEFINE()
 */
ZTEST_USER(device, test_null_dynamic_name)
{
	/* Supplying a NULL dynamic name may trigger a SecureFault and
	 * lead to system crash in TrustZone enabled Non-Secure builds.
	 */
#if defined(CONFIG_USERSPACE) && !defined(CONFIG_TRUSTED_EXECUTION_NONSECURE)
	const struct device *mux;
	char *drv_name = NULL;

	mux = device_get_binding(drv_name);
	zassert_equal(mux, 0);
#else
	ztest_test_skip();
#endif
}

__pinned_bss
static struct init_record {
	bool pre_kernel;
	bool is_in_isr;
	bool is_pre_kernel;
	bool could_yield;
} init_records[4];

__pinned_data
static struct init_record *rp = init_records;

__pinned_func
static int add_init_record(bool pre_kernel)
{
	rp->pre_kernel = pre_kernel;
	rp->is_pre_kernel = k_is_pre_kernel();
	rp->is_in_isr = k_is_in_isr();
	rp->could_yield = k_can_yield();
	++rp;
	return 0;
}

__pinned_func
static int pre1_fn(void)
{
	return add_init_record(true);
}

__pinned_func
static int pre2_fn(void)
{
	return add_init_record(true);
}

static int post_fn(void)
{
	return add_init_record(false);
}

static int app_fn(void)
{
	return add_init_record(false);
}

SYS_INIT(pre1_fn, PRE_KERNEL_1, 0);
SYS_INIT(pre2_fn, PRE_KERNEL_2, 0);
SYS_INIT(post_fn, POST_KERNEL, 0);
SYS_INIT(app_fn, APPLICATION, 0);

/* This is an error case which driver initializes failed in SYS_INIT .*/
static int null_driver_init(void)
{
	return -EINVAL;
}

SYS_INIT(null_driver_init, POST_KERNEL, 0);

/**
 * @brief Test detection of initialization before kernel services available.
 *
 * Confirms check is correct.
 *
 * @see k_is_pre_kernel()
 */
ZTEST(device, test_pre_kernel_detection)
{
	struct init_record *rpe = rp;

	zassert_equal(rp - init_records, 4U,
		      "bad record count");
	rp = init_records;
	while ((rp < rpe) && rp->pre_kernel) {
		zassert_equal(rp->is_in_isr, false,
			      "rec %zu isr", rp - init_records);
		zassert_equal(rp->is_pre_kernel, true,
			      "rec %zu pre-kernel", rp - init_records);
		zassert_equal(rp->could_yield, false,
			      "rec %zu could-yield", rp - init_records);
		++rp;
	}
	zassert_equal(rp - init_records, 2U,
		      "bad pre-kernel count");

	while (rp < rpe) {
		zassert_equal(rp->is_in_isr, false,
			      "rec %zu isr", rp - init_records);
		zassert_equal(rp->is_pre_kernel, false,
			      "rec %zu post-kernel", rp - init_records);
		zassert_equal(rp->could_yield, true,
			      "rec %zu could-yield", rp - init_records);
		++rp;
	}
}

/**
 * @brief Test system device list query API.
 *
 * It queries the list of devices in the system, used to suspend or
 * resume the devices in PM applications.
 *
 * @see z_device_get_all_static()
 */
ZTEST(device, test_device_list)
{
	struct device const *devices;
	size_t devcount = z_device_get_all_static(&devices);

	zassert_false((devcount == 0));
}

static int sys_init_counter;

static int init_fn(void)
{
	sys_init_counter++;
	return 0;
}

SYS_INIT(init_fn, APPLICATION, 0);
SYS_INIT_NAMED(init1, init_fn, APPLICATION, 1);
SYS_INIT_NAMED(init2, init_fn, APPLICATION, 2);
SYS_INIT_NAMED(init3, init_fn, APPLICATION, 2);

ZTEST(device, test_sys_init_multiple)
{
	zassert_equal(sys_init_counter, 4, "");
}

/* this is for storing sequence during initialization */
extern int init_level_sequence[4];
extern int init_priority_sequence[4];
extern int init_sub_priority_sequence[3];
extern unsigned int seq_level_cnt;
extern unsigned int seq_priority_cnt;

/**
 * @brief Test initialization level for device driver instances
 *
 * @details After the defined device instances have initialized, we check the
 * sequence number that each driver stored during initialization. If the
 * sequence of initial level stored is corresponding with our expectation, it
 * means assigning the level for driver instance works.
 *
 * @ingroup kernel_device_tests
 */
ZTEST(device, test_device_init_level)
{
	bool seq_correct = true;

	/* we check if the stored executing sequence for different level is
	 * correct, and it should be 1, 2, 3
	 */
	for (int i = 0; i < 3; i++) {
		if (init_level_sequence[i] != (i + 1)) {
			seq_correct = false;
		}
	}

	zassert_true((seq_correct == true),
			"init sequence is not correct");
}

/**
 * @brief Test initialization priorities for device driver instances
 *
 * @details After the defined device instances have initialized, we check the
 * sequence number that each driver stored during initialization. If the
 * sequence of initial priority stored is corresponding with our expectation, it
 * means assigning the priority for driver instance works.
 *
 * @ingroup kernel_device_tests
 */
ZTEST(device, test_device_init_priority)
{
	bool sequence_correct = true;

	/* we check if the stored pexecuting sequence for priority is correct,
	 * and it should be 1, 2, 3, 4
	 */
	for (int i = 0; i < 4; i++) {
		if (init_priority_sequence[i] != (i + 1)) {
			sequence_correct = false;
		}
	}

	zassert_true((sequence_correct == true),
			"init sequence is not correct");
}

/**
 * @brief Test initialization sub-priorities for device driver instances
 *
 * @details After the defined device instances have initialized, we check the
 * sequence number that each driver stored during initialization. If the
 * sequence of initial priority stored is corresponding with our expectation, it
 * means using the devicetree for sub-priority sorting works.
 *
 * @ingroup kernel_device_tests
 */
ZTEST(device, test_device_init_sub_priority)
{
	/* fakedomain_1 depends on fakedomain_0 which depends on fakedomain_2,
	 * therefore we require that the initialisation runs in the reverse order.
	 */
	zassert_equal(init_sub_priority_sequence[0], 1, "");
	zassert_equal(init_sub_priority_sequence[1], 2, "");
	zassert_equal(init_sub_priority_sequence[2], 0, "");
}

/**
 * @brief Test abstraction of device drivers with common functionalities
 *
 * @details Abstraction of device drivers with common functionalities
 * shall be provided as an intermediate interface between applications
 * and device drivers, where such interface is implemented by individual
 * device drivers. We verify this by following step:

 * 1. Define a subsystem api for drivers.
 * 2. Define and create two driver instances.
 * 3. Two drivers call the same subsystem API, and we verify that each
 * driver instance will call their own implementations.
 *
 * @ingroup kernel_device_tests
 */
ZTEST(device, test_abstraction_driver_common)
{
	const struct device *dev;
	int ret;
	int foo = 2;
	int bar = 1;
	unsigned int baz = 0;

	/* verify driver A API has called */
	dev = device_get_binding(MY_DRIVER_A);
	zassert_false((dev == NULL));

	ret = subsystem_do_this(dev, foo, bar);
	zassert_true(ret == (foo + bar), "common API do_this fail");

	subsystem_do_that(dev, &baz);
	zassert_true(baz == 1, "common API do_that fail");

	/* verify driver B API has called */
	dev = device_get_binding(MY_DRIVER_B);
	zassert_false((dev == NULL));

	ret = subsystem_do_this(dev, foo, bar);
	zassert_true(ret == (foo - bar), "common API do_this fail");

	subsystem_do_that(dev, &baz);
	zassert_true(baz == 2, "common API do_that fail");
}


/**
 * @}
 */

ZTEST_SUITE(device, NULL, NULL, NULL, NULL, NULL);