Linux preempt-rt

Check our new training course

Real-Time Linux with PREEMPT_RT

Check our new training course
with Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * Copyright (c) 2016 Intel Corporation
 * Copyright (c) 2020 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/* Avoid CI warnings about use of deprecated API.  The purpose of this
 * module is to test that the deprecated API still works.
 */
#undef __deprecated
#define __deprecated
#undef __DEPRECATED_MACRO
#define __DEPRECATED_MACRO

#include "test_gpio.h"

static struct drv_data data;
static int cb_cnt;

static void callback(struct device *dev,
		     struct gpio_callback *gpio_cb, u32_t pins)
{
	const struct drv_data *dd = CONTAINER_OF(gpio_cb,
						 struct drv_data, gpio_cb);

	/*= checkpoint: pins should be marked with correct pin number bit =*/
	zassert_equal(pins, BIT(PIN_IN),
		      "unexpected pins %x", pins);
	++cb_cnt;
	TC_PRINT("callback triggered: %d\n", cb_cnt);
	if ((cb_cnt == 1)
	    && (dd->mode == GPIO_INT_DOUBLE_EDGE)) {
		gpio_pin_write(dev, PIN_OUT, dd->aux);
	}
	if (cb_cnt >= MAX_INT_CNT) {
		gpio_pin_write(dev, PIN_OUT, dd->aux);

		/* NB: The legacy idiom for disabling interrupts is to
		 * pass GPIO_DIR_IN without any interrupt-related
		 * flags.  In the new API this leaves the interrupt
		 * configuration of the pin unchanged, which causes
		 * level interrupts to repeat forever.  To prevent hangs
		 * it's necessary to explicitly disable the interrupt.
		 */
		gpio_pin_configure(dev, PIN_IN, GPIO_DIR_IN
				   | GPIO_INT_DISABLE);
	}
}

static int test_callback(gpio_flags_t int_flags)
{
	struct device *dev = device_get_binding(DEV_NAME);
	struct drv_data *drv_data = &data;
	bool active_level = (int_flags & GPIO_INT_ACTIVE_HIGH) != 0;
	int rc;

	gpio_pin_disable_callback(dev, PIN_IN);
	gpio_pin_disable_callback(dev, PIN_OUT);

	/* 1. set PIN_OUT to non-active state */
	drv_data->aux = (active_level == false);

	rc = gpio_pin_configure(dev, PIN_OUT, GPIO_DIR_OUT);
	if (rc == 0) {
		gpio_pin_write(dev, PIN_OUT, !active_level);
	}

	if (rc != 0) {
		TC_ERROR("PIN_OUT config fail: %d", rc);
		return TC_FAIL;
	}

	/* 2. configure PIN_IN callback and trigger condition */
	rc = gpio_pin_configure(dev, PIN_IN,
				GPIO_DIR_IN | GPIO_INT
				| GPIO_INT_DEBOUNCE
				| int_flags);
	if (rc == -ENOTSUP) {
		TC_PRINT("interrupt configuration not supported\n");
		return TC_PASS;
	} else if (rc != 0) {
		TC_ERROR("config PIN_IN fail: %d\n", rc);
		return TC_FAIL;
	}

	drv_data->mode = int_flags;
	gpio_init_callback(&drv_data->gpio_cb, callback, BIT(PIN_IN));
	rc = gpio_add_callback(dev, &drv_data->gpio_cb);
	if (rc == -ENOTSUP) {
		TC_PRINT("interrupts not supported\n");
		return TC_PASS;
	} else if (rc != 0) {
		TC_ERROR("set PIN_IN callback fail: %d\n", rc);
		return TC_FAIL;
	}

	/* 3. enable callback, trigger PIN_IN interrupt by operate PIN_OUT */
	cb_cnt = 0;
	rc = gpio_pin_enable_callback(dev, PIN_IN);
	if (rc == -ENOTSUP) {
		TC_PRINT("Mode %x not supported\n", int_flags);
		goto pass_exit;
	} else if (rc != 0) {
		TC_ERROR("enable PIN_IN interrupt fail: %d\n", rc);
		goto err_exit;
	}
	k_sleep(K_MSEC(100));
	gpio_pin_write(dev, PIN_OUT, active_level);
	k_sleep(K_MSEC(1000));
	(void)gpio_pin_disable_callback(dev, PIN_IN);

	/*= checkpoint: check callback is triggered =*/
	TC_PRINT("INT cfg %x, cnt %d\n", int_flags, cb_cnt);
	if (int_flags == GPIO_INT_DOUBLE_EDGE) {
		if (cb_cnt != 2) {
			TC_ERROR("double edge not detected\n");
			goto err_exit;
		}
		goto pass_exit;
	}
	if ((int_flags & GPIO_INT_EDGE) == GPIO_INT_EDGE) {
		if (cb_cnt != 1) {
			TC_ERROR("edge not trigger callback correctly\n");
			goto err_exit;
		}
		goto pass_exit;
	} else {
		if (cb_cnt != MAX_INT_CNT) {
			TC_ERROR("level not trigger callback correctly\n");
			goto err_exit;
		}
	}

pass_exit:
	gpio_remove_callback(dev, &drv_data->gpio_cb);
	return TC_PASS;

err_exit:
	gpio_remove_callback(dev, &drv_data->gpio_cb);
	return TC_FAIL;
}

static int test_callback_enable_disable(void)
{
	return TC_PASS;
}

/* export test cases */
void test_gpio_deprecated(void)
{
	zassert_equal(test_callback_enable_disable(),
		      TC_PASS, "callback enable/disable failed");
	zassert_equal(test_callback(GPIO_INT_EDGE | GPIO_INT_ACTIVE_HIGH),
		      TC_PASS, "rising edge failed");
	zassert_equal(test_callback(GPIO_INT_EDGE | GPIO_INT_ACTIVE_LOW),
		      TC_PASS, "falling edge failed");
	zassert_equal(test_callback(GPIO_INT_EDGE_BOTH),
		      TC_PASS, "double edge failed");
	zassert_equal(test_callback(GPIO_INT_LEVEL | GPIO_INT_ACTIVE_HIGH),
		      TC_PASS, "level high failed");
	zassert_equal(test_callback(GPIO_INT_LEVEL | GPIO_INT_ACTIVE_LOW),
		      TC_PASS, "level low failed");
}