gpio-delay had no sandbox coverage, which is how two bugs in it went
unnoticed: gpio_delay_xlate() never propagated the requested index
into the descriptor offset, so every consumer past the first silently
collided with the first consumer's already-claimed offset 0; and the
claimed/name tracking arrays were sized and bounds-checked against a
hardcoded 32 instead of the driver's actual GPIO count. Both are fixed
by a companion series that is a dependency of this patch: applied
without it, this test fails outright, since it directly exercises
both fixes.

Add a test requesting both consumers wired up by gpio-delay-test in
test.dts (gpio_a 9 and 18): a third consumer colliding with an
already-claimed offset must fail with -EBUSY, each consumer's write
must land on its own wrapped GPIO line, and a request past the
wrapped GPIO count must fail with -EINVAL rather than succeed against
a hardcoded bound of 32.

Gated on CONFIG_GPIO_DELAY, since not every sandbox variant that
builds test/dm/gpio.c enables it (sandbox_vpl, sandbox_spl,
sandbox_noinst, sandbox_flattree).

Signed-off-by: Pranav Sanwal <[email protected]>
---
 test/dm/gpio.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 69 insertions(+)

diff --git a/test/dm/gpio.c b/test/dm/gpio.c
index b872dac9082..17a0dddf083 100644
--- a/test/dm/gpio.c
+++ b/test/dm/gpio.c
@@ -432,6 +432,75 @@ static int dm_test_gpio_get_dir_flags(struct 
unit_test_state *uts)
 }
 DM_TEST(dm_test_gpio_get_dir_flags, UTF_SCAN_PDATA | UTF_SCAN_FDT);
 
+/*
+ * Test that gpio-delay correctly routes each consumer to its own wrapped
+ * real GPIO line. See gpio-delay-test in test.dts, which wraps gpio_a 9
+ * and gpio_a 18.
+ */
+#if IS_ENABLED(CONFIG_GPIO_DELAY)
+static int dm_test_gpio_delay(struct unit_test_state *uts)
+{
+       struct gpio_desc desc0, desc1, desc2, desc3;
+       struct udevice *dev, *gpio_a;
+
+       ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+       ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio_a));
+       ut_asserteq_str("base-gpios", gpio_a->name);
+
+       /*
+        * Requesting both consumers must succeed. Before the offset was
+        * propagated in gpio_delay_xlate(), both descriptors came back with
+        * offset 0, so this second request would fail with -EBUSY as it
+        * collided with the first consumer's already-claimed offset.
+        */
+       ut_assertok(gpio_request_by_name(dev, "test6-gpios", 0, &desc0, 0));
+       ut_assertok(gpio_request_by_name(dev, "test6-gpios", 1, &desc1, 0));
+
+       ut_asserteq_ptr(desc0.dev, desc1.dev);
+       ut_asserteq(0, desc0.offset);
+       ut_asserteq(1, desc1.offset);
+
+       /*
+        * A third consumer mapped to the same offset as the first must be
+        * rejected as already requested.
+        */
+       ut_asserteq(-EBUSY, gpio_request_by_name(dev, "test6-gpios", 2, &desc2,
+                                                0));
+
+       /*
+        * Drive each consumer to a different level and confirm the write
+        * lands on its own wrapped real GPIO line, not the other one's.
+        * gpio_a has no set_value op of its own (it implements set_flags),
+        * so dm_gpio_set_value() routes through GPIOD_IS_OUT_ACTIVE.
+        */
+       ut_assertok(dm_gpio_set_value(&desc0, 0));
+       ut_assertok(dm_gpio_set_value(&desc1, 1));
+       ut_asserteq(0, sandbox_gpio_get_flags(gpio_a, 9) & GPIOD_IS_OUT_ACTIVE);
+       ut_asserteq(GPIOD_IS_OUT_ACTIVE,
+                   sandbox_gpio_get_flags(gpio_a, 18) & GPIOD_IS_OUT_ACTIVE);
+
+       ut_assertok(dm_gpio_set_value(&desc0, 1));
+       ut_assertok(dm_gpio_set_value(&desc1, 0));
+       ut_asserteq(GPIOD_IS_OUT_ACTIVE,
+                   sandbox_gpio_get_flags(gpio_a, 9) & GPIOD_IS_OUT_ACTIVE);
+       ut_asserteq(0, sandbox_gpio_get_flags(gpio_a, 18) & 
GPIOD_IS_OUT_ACTIVE);
+
+       ut_assertok(dm_gpio_free(dev, &desc0));
+       ut_assertok(dm_gpio_free(dev, &desc1));
+
+       /*
+        * An index beyond the wrapped GPIO count (2 here) must be rejected.
+        * Before gpio_count was set from the "gpios" property, this bound
+        * was checked against a hardcoded 32 and would have been let through.
+        */
+       ut_asserteq(-EINVAL, gpio_request_by_name(dev, "test7-gpios", 0, &desc3,
+                                                 0));
+
+       return 0;
+}
+DM_TEST(dm_test_gpio_delay, UTF_SCAN_PDATA | UTF_SCAN_FDT);
+#endif /* CONFIG_GPIO_DELAY */
+
 /* Test of gpio_get_acpi() */
 static int dm_test_gpio_get_acpi(struct unit_test_state *uts)
 {
-- 
2.43.7

Reply via email to