xref: /illumos-gate/usr/src/test/os-tests/tests/gpio/dpio_test.c (revision fd71220ba0fafcc9cf5ea0785db206f3f31336e7)
1*fd71220bSRobert Mustacchi /*
2*fd71220bSRobert Mustacchi  * This file and its contents are supplied under the terms of the
3*fd71220bSRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4*fd71220bSRobert Mustacchi  * You may only use this file in accordance with the terms of version
5*fd71220bSRobert Mustacchi  * 1.0 of the CDDL.
6*fd71220bSRobert Mustacchi  *
7*fd71220bSRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8*fd71220bSRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9*fd71220bSRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10*fd71220bSRobert Mustacchi  */
11*fd71220bSRobert Mustacchi 
12*fd71220bSRobert Mustacchi /*
13*fd71220bSRobert Mustacchi  * Copyright 2022 Oxide Computer Company
14*fd71220bSRobert Mustacchi  */
15*fd71220bSRobert Mustacchi 
16*fd71220bSRobert Mustacchi /*
17*fd71220bSRobert Mustacchi  * Perform basic validation around DPIO creation cases and that the resulting
18*fd71220bSRobert Mustacchi  * character devices properly honor the constraints put in them.
19*fd71220bSRobert Mustacchi  *
20*fd71220bSRobert Mustacchi  * The test starts by creating 4 DPIOs on GPIOs 0-3:
21*fd71220bSRobert Mustacchi  *
22*fd71220bSRobert Mustacchi  *  o GPIO 0: no read/write
23*fd71220bSRobert Mustacchi  *  o GPIO 1: read-only
24*fd71220bSRobert Mustacchi  *  o GPIO 2: read-write
25*fd71220bSRobert Mustacchi  *  o GPIO 3: read-write, kernel
26*fd71220bSRobert Mustacchi  *
27*fd71220bSRobert Mustacchi  * We then iterate on GPIOs 4/5 for other tests that should fail to create
28*fd71220bSRobert Mustacchi  * DPIOs.
29*fd71220bSRobert Mustacchi  */
30*fd71220bSRobert Mustacchi 
31*fd71220bSRobert Mustacchi #include <sys/stat.h>
32*fd71220bSRobert Mustacchi #include <sys/types.h>
33*fd71220bSRobert Mustacchi #include <fcntl.h>
34*fd71220bSRobert Mustacchi #include <err.h>
35*fd71220bSRobert Mustacchi #include <stdlib.h>
36*fd71220bSRobert Mustacchi #include <unistd.h>
37*fd71220bSRobert Mustacchi #include <string.h>
38*fd71220bSRobert Mustacchi #include <errno.h>
39*fd71220bSRobert Mustacchi #include <stdbool.h>
40*fd71220bSRobert Mustacchi #include <limits.h>
41*fd71220bSRobert Mustacchi #include <sys/sysmacros.h>
42*fd71220bSRobert Mustacchi #include <time.h>
43*fd71220bSRobert Mustacchi 
44*fd71220bSRobert Mustacchi #include <sys/gpio/kgpio.h>
45*fd71220bSRobert Mustacchi #include <sys/gpio/dpio.h>
46*fd71220bSRobert Mustacchi 
47*fd71220bSRobert Mustacchi #define	SIM_NPINS	6
48*fd71220bSRobert Mustacchi 
49*fd71220bSRobert Mustacchi /*
50*fd71220bSRobert Mustacchi  * For now we hardcode the path to our controller rather than discovering it
51*fd71220bSRobert Mustacchi  * through libdevinfo or libxpio to simplify the test implementation (and not
52*fd71220bSRobert Mustacchi  * have deps on libxpio for functionality below libxpio).
53*fd71220bSRobert Mustacchi  */
54*fd71220bSRobert Mustacchi static const char *dpio_ctrl_path = "/devices/pseudo/kgpio@0:gpio_sim2";
55*fd71220bSRobert Mustacchi static int dpio_exit = EXIT_SUCCESS;
56*fd71220bSRobert Mustacchi 
57*fd71220bSRobert Mustacchi typedef struct {
58*fd71220bSRobert Mustacchi 	const char *dce_desc;
59*fd71220bSRobert Mustacchi 	uint32_t dce_gpio;
60*fd71220bSRobert Mustacchi 	const char *dce_name;
61*fd71220bSRobert Mustacchi 	kgpio_dpio_flags_t dce_flags;
62*fd71220bSRobert Mustacchi 	int dce_errno;
63*fd71220bSRobert Mustacchi } dpio_create_err_t;
64*fd71220bSRobert Mustacchi 
65*fd71220bSRobert Mustacchi const dpio_create_err_t dpio_create_errs[] = {
66*fd71220bSRobert Mustacchi 	{ "GPIO already a DPIO", 0, "WeIrDnAmE12345", 0, EBUSY },
67*fd71220bSRobert Mustacchi 	{ "Name already used", 5, "dpioTESTnone", 0, EEXIST },
68*fd71220bSRobert Mustacchi 	{ "bad pin", UINT32_MAX, "thispindoesnotexist23", 0, ENOENT },
69*fd71220bSRobert Mustacchi 	{ "Name already used", 5, "dpioTESTnone", 0, EEXIST },
70*fd71220bSRobert Mustacchi 	{ "bad flags", 5, "amaZINGflags12345", ~KGPIO_DPIO_F_WRITE, EINVAL },
71*fd71220bSRobert Mustacchi 	{ "bad name 1", 5, "12345!@#$%^", 0, EINVAL },
72*fd71220bSRobert Mustacchi 	{ "bad name 2", 5, "this-is-a-___test!", 0, EINVAL },
73*fd71220bSRobert Mustacchi };
74*fd71220bSRobert Mustacchi 
75*fd71220bSRobert Mustacchi /*
76*fd71220bSRobert Mustacchi  * Reuse the error struct, ignoring the error to create our defaults.
77*fd71220bSRobert Mustacchi  */
78*fd71220bSRobert Mustacchi static const dpio_create_err_t dpio_default_dpios[] = {
79*fd71220bSRobert Mustacchi 	{ NULL, 0, "dpioTESTnone", 0, 0 },
80*fd71220bSRobert Mustacchi 	{ NULL, 1, "dpioTESTro", KGPIO_DPIO_F_READ, 0 },
81*fd71220bSRobert Mustacchi 	{ NULL, 2, "dpioTESTrw", KGPIO_DPIO_F_READ | KGPIO_DPIO_F_WRITE, 0 },
82*fd71220bSRobert Mustacchi 	{ NULL, 3, "dpioTESTrwK", KGPIO_DPIO_F_READ | KGPIO_DPIO_F_WRITE |
83*fd71220bSRobert Mustacchi 	    KGPIO_DPIO_F_KERNEL, 0 }
84*fd71220bSRobert Mustacchi };
85*fd71220bSRobert Mustacchi 
86*fd71220bSRobert Mustacchi static void
dpio_fail(const char * fmt,...)87*fd71220bSRobert Mustacchi dpio_fail(const char *fmt, ...)
88*fd71220bSRobert Mustacchi {
89*fd71220bSRobert Mustacchi 	va_list ap;
90*fd71220bSRobert Mustacchi 	dpio_exit = EXIT_FAILURE;
91*fd71220bSRobert Mustacchi 	(void) printf("TEST FAILED: ");
92*fd71220bSRobert Mustacchi 
93*fd71220bSRobert Mustacchi 	va_start(ap, fmt);
94*fd71220bSRobert Mustacchi 	vprintf(fmt, ap);
95*fd71220bSRobert Mustacchi 	va_end(ap);
96*fd71220bSRobert Mustacchi 
97*fd71220bSRobert Mustacchi 	(void) putchar('\n');
98*fd71220bSRobert Mustacchi }
99*fd71220bSRobert Mustacchi 
100*fd71220bSRobert Mustacchi static void
dpio_pass(const char * fmt,...)101*fd71220bSRobert Mustacchi dpio_pass(const char *fmt, ...)
102*fd71220bSRobert Mustacchi {
103*fd71220bSRobert Mustacchi 	va_list ap;
104*fd71220bSRobert Mustacchi 
105*fd71220bSRobert Mustacchi 	(void) printf("TEST PASSED: ");
106*fd71220bSRobert Mustacchi 	va_start(ap, fmt);
107*fd71220bSRobert Mustacchi 	vprintf(fmt, ap);
108*fd71220bSRobert Mustacchi 	va_end(ap);
109*fd71220bSRobert Mustacchi 
110*fd71220bSRobert Mustacchi 	(void) putchar('\n');
111*fd71220bSRobert Mustacchi }
112*fd71220bSRobert Mustacchi 
113*fd71220bSRobert Mustacchi static void
dpio_bad_create(int ctrl_fd,const dpio_create_err_t * test)114*fd71220bSRobert Mustacchi dpio_bad_create(int ctrl_fd, const dpio_create_err_t *test)
115*fd71220bSRobert Mustacchi {
116*fd71220bSRobert Mustacchi 	kgpio_dpio_create_t create;
117*fd71220bSRobert Mustacchi 
118*fd71220bSRobert Mustacchi 	(void) memset(&create, 0, sizeof (create));
119*fd71220bSRobert Mustacchi 	create.kdc_id = test->dce_gpio;
120*fd71220bSRobert Mustacchi 	create.kdc_flags = test->dce_flags;
121*fd71220bSRobert Mustacchi 	(void) strlcpy(create.kdc_name, test->dce_name,
122*fd71220bSRobert Mustacchi 	    sizeof (create.kdc_name));
123*fd71220bSRobert Mustacchi 
124*fd71220bSRobert Mustacchi 	if (ioctl(ctrl_fd, KGPIO_IOC_DPIO_CREATE, &create) == 0) {
125*fd71220bSRobert Mustacchi 		dpio_fail("DPIO create: %s: succeeded when we expected failure",
126*fd71220bSRobert Mustacchi 		    test->dce_desc);
127*fd71220bSRobert Mustacchi 	} else if (errno != test->dce_errno) {
128*fd71220bSRobert Mustacchi 		dpio_fail("DPIO create failed: %s: but got wrong errno. "
129*fd71220bSRobert Mustacchi 		    "Expected %d, found %d", test->dce_desc, test->dce_errno,
130*fd71220bSRobert Mustacchi 		    errno);
131*fd71220bSRobert Mustacchi 	} else {
132*fd71220bSRobert Mustacchi 		dpio_pass("DPIO create failed: %s", test->dce_desc);
133*fd71220bSRobert Mustacchi 	}
134*fd71220bSRobert Mustacchi }
135*fd71220bSRobert Mustacchi 
136*fd71220bSRobert Mustacchi static bool
dpio_default_create(int ctrl_fd,uint32_t gpio,const char * name,kgpio_dpio_flags_t flags)137*fd71220bSRobert Mustacchi dpio_default_create(int ctrl_fd, uint32_t gpio, const char *name,
138*fd71220bSRobert Mustacchi     kgpio_dpio_flags_t flags)
139*fd71220bSRobert Mustacchi {
140*fd71220bSRobert Mustacchi 	kgpio_dpio_create_t create;
141*fd71220bSRobert Mustacchi 
142*fd71220bSRobert Mustacchi 	(void) memset(&create, 0, sizeof (create));
143*fd71220bSRobert Mustacchi 	create.kdc_id = gpio;
144*fd71220bSRobert Mustacchi 	create.kdc_flags = flags;
145*fd71220bSRobert Mustacchi 	(void) strlcpy(create.kdc_name, name, sizeof (create.kdc_name));
146*fd71220bSRobert Mustacchi 
147*fd71220bSRobert Mustacchi 	if (ioctl(ctrl_fd, KGPIO_IOC_DPIO_CREATE, &create) != 0) {
148*fd71220bSRobert Mustacchi 		warn("failed to create bootstrap DPIO");
149*fd71220bSRobert Mustacchi 		return (false);
150*fd71220bSRobert Mustacchi 	}
151*fd71220bSRobert Mustacchi 
152*fd71220bSRobert Mustacchi 	return (true);
153*fd71220bSRobert Mustacchi }
154*fd71220bSRobert Mustacchi 
155*fd71220bSRobert Mustacchi /*
156*fd71220bSRobert Mustacchi  * As part of exiting, make sure that there are no DPIOs remaining on our
157*fd71220bSRobert Mustacchi  * controller. This should not be used for general tests as it ignores a number
158*fd71220bSRobert Mustacchi  * of errors.
159*fd71220bSRobert Mustacchi  */
160*fd71220bSRobert Mustacchi static void
dpio_cleanup(int ctrl_fd)161*fd71220bSRobert Mustacchi dpio_cleanup(int ctrl_fd)
162*fd71220bSRobert Mustacchi {
163*fd71220bSRobert Mustacchi 	for (uint32_t i = 0; i < SIM_NPINS; i++) {
164*fd71220bSRobert Mustacchi 		kgpio_dpio_destroy_t destroy;
165*fd71220bSRobert Mustacchi 
166*fd71220bSRobert Mustacchi 		(void) memset(&destroy, 0, sizeof (destroy));
167*fd71220bSRobert Mustacchi 		destroy.kdd_id = i;
168*fd71220bSRobert Mustacchi 		if (ioctl(ctrl_fd, KGPIO_IOC_DPIO_DESTROY, &destroy) != 0) {
169*fd71220bSRobert Mustacchi 			if (errno != ENOENT) {
170*fd71220bSRobert Mustacchi 				dpio_fail("failed to cleanup DPIO on pin %u",
171*fd71220bSRobert Mustacchi 				    i);
172*fd71220bSRobert Mustacchi 			}
173*fd71220bSRobert Mustacchi 		}
174*fd71220bSRobert Mustacchi 	}
175*fd71220bSRobert Mustacchi }
176*fd71220bSRobert Mustacchi 
177*fd71220bSRobert Mustacchi /*
178*fd71220bSRobert Mustacchi  * Verify the various set of features around writing to a DPIO work:
179*fd71220bSRobert Mustacchi  *
180*fd71220bSRobert Mustacchi  *  o Getting the current output
181*fd71220bSRobert Mustacchi  *  o Advancing write timestamps
182*fd71220bSRobert Mustacchi  *  o Actually performing the write
183*fd71220bSRobert Mustacchi  *
184*fd71220bSRobert Mustacchi  * If any of these fail, we short-circuit and don't perform the rest.
185*fd71220bSRobert Mustacchi  */
186*fd71220bSRobert Mustacchi static void
dpio_test_cbops_write(int fd,const char * path)187*fd71220bSRobert Mustacchi dpio_test_cbops_write(int fd, const char *path)
188*fd71220bSRobert Mustacchi {
189*fd71220bSRobert Mustacchi 	dpio_curout_t curout;
190*fd71220bSRobert Mustacchi 	dpio_timing_t pre, post;
191*fd71220bSRobert Mustacchi 	uint32_t val;
192*fd71220bSRobert Mustacchi 	ssize_t ret;
193*fd71220bSRobert Mustacchi 
194*fd71220bSRobert Mustacchi 	(void) memset(&curout, 0, sizeof (curout));
195*fd71220bSRobert Mustacchi 	if (ioctl(fd, DPIO_IOC_CUROUT, &curout) != 0) {
196*fd71220bSRobert Mustacchi 		dpio_fail("failed to get DPIO_IOC_CUROUT on %s: %s", path,
197*fd71220bSRobert Mustacchi 		    strerror(errno));
198*fd71220bSRobert Mustacchi 		return;
199*fd71220bSRobert Mustacchi 	}
200*fd71220bSRobert Mustacchi 	dpio_pass("DPIO_IOC_CUROUT successful on %s", path);
201*fd71220bSRobert Mustacchi 
202*fd71220bSRobert Mustacchi 	(void) memset(&pre, 0, sizeof (pre));
203*fd71220bSRobert Mustacchi 	(void) memset(&post, 0, sizeof (post));
204*fd71220bSRobert Mustacchi 	if (ioctl(fd, DPIO_IOC_TIMING, &pre) != 0) {
205*fd71220bSRobert Mustacchi 		dpio_fail("failed to get DPIO_IOC_TIMING on %s: %s", path,
206*fd71220bSRobert Mustacchi 		    strerror(errno));
207*fd71220bSRobert Mustacchi 		return;
208*fd71220bSRobert Mustacchi 	}
209*fd71220bSRobert Mustacchi 	dpio_pass("DPIO_IOC_TIMING successful on %s", path);
210*fd71220bSRobert Mustacchi 
211*fd71220bSRobert Mustacchi 	val = curout.dps_curout;
212*fd71220bSRobert Mustacchi 	ret = write(fd, &val, sizeof (val));
213*fd71220bSRobert Mustacchi 	if (ret == -1) {
214*fd71220bSRobert Mustacchi 		dpio_fail("write failed on %s: %s", path, strerror(ret));
215*fd71220bSRobert Mustacchi 		return;
216*fd71220bSRobert Mustacchi 	} else if (ret != 4) {
217*fd71220bSRobert Mustacchi 		dpio_fail("write to %s returned wrong number of bytes: %ld, "
218*fd71220bSRobert Mustacchi 		    "expected 4 bytes", path, ret);
219*fd71220bSRobert Mustacchi 		return;
220*fd71220bSRobert Mustacchi 	}
221*fd71220bSRobert Mustacchi 	dpio_pass("write successful on %s", path);
222*fd71220bSRobert Mustacchi 
223*fd71220bSRobert Mustacchi 	if (ioctl(fd, DPIO_IOC_TIMING, &post) != 0) {
224*fd71220bSRobert Mustacchi 		dpio_fail("failed to get post-write DPIO_IOC_TIMING on %s: %s",
225*fd71220bSRobert Mustacchi 		    path, strerror(errno));
226*fd71220bSRobert Mustacchi 		return;
227*fd71220bSRobert Mustacchi 	}
228*fd71220bSRobert Mustacchi 
229*fd71220bSRobert Mustacchi 	if (post.dpt_last_write > pre.dpt_last_write) {
230*fd71220bSRobert Mustacchi 		dpio_pass("write time advanced on %s", path);
231*fd71220bSRobert Mustacchi 	} else {
232*fd71220bSRobert Mustacchi 		dpio_fail("write time on %s did not advance, pre: 0x%lx, "
233*fd71220bSRobert Mustacchi 		    "post: 0x%lx", pre.dpt_last_write, post.dpt_last_write);
234*fd71220bSRobert Mustacchi 	}
235*fd71220bSRobert Mustacchi }
236*fd71220bSRobert Mustacchi 
237*fd71220bSRobert Mustacchi static void
dpio_test_cbops(const char * name,bool can_open,bool can_read,bool can_write)238*fd71220bSRobert Mustacchi dpio_test_cbops(const char *name, bool can_open, bool can_read, bool can_write)
239*fd71220bSRobert Mustacchi {
240*fd71220bSRobert Mustacchi 	uint32_t val;
241*fd71220bSRobert Mustacchi 	char path[PATH_MAX];
242*fd71220bSRobert Mustacchi 	dpio_curout_t curout;
243*fd71220bSRobert Mustacchi 	int fd;
244*fd71220bSRobert Mustacchi 	ssize_t ret;
245*fd71220bSRobert Mustacchi 
246*fd71220bSRobert Mustacchi 	(void) snprintf(path, sizeof (path), "/dev/dpio/%s", name);
247*fd71220bSRobert Mustacchi 	fd = open(path, O_RDWR);
248*fd71220bSRobert Mustacchi 	if (fd < 0) {
249*fd71220bSRobert Mustacchi 		if (!can_open) {
250*fd71220bSRobert Mustacchi 			dpio_pass("failed to open %s", path);
251*fd71220bSRobert Mustacchi 		} else {
252*fd71220bSRobert Mustacchi 			dpio_fail("failed to open %s, but expected to: %s",
253*fd71220bSRobert Mustacchi 			    path, strerror(errno));
254*fd71220bSRobert Mustacchi 		}
255*fd71220bSRobert Mustacchi 		return;
256*fd71220bSRobert Mustacchi 	} else {
257*fd71220bSRobert Mustacchi 		if (!can_open) {
258*fd71220bSRobert Mustacchi 			dpio_fail("TEST FAILED: accidentally was able to open "
259*fd71220bSRobert Mustacchi 			    "%s", path);
260*fd71220bSRobert Mustacchi 			return;
261*fd71220bSRobert Mustacchi 		} else {
262*fd71220bSRobert Mustacchi 			dpio_pass("successfully opened %s", path);
263*fd71220bSRobert Mustacchi 		}
264*fd71220bSRobert Mustacchi 	}
265*fd71220bSRobert Mustacchi 
266*fd71220bSRobert Mustacchi 	errno = 0;
267*fd71220bSRobert Mustacchi 	ret = read(fd, &val, sizeof (val));
268*fd71220bSRobert Mustacchi 	if (ret != 4) {
269*fd71220bSRobert Mustacchi 		if (!can_read && errno == ENOTSUP) {
270*fd71220bSRobert Mustacchi 			dpio_pass("successfully failed to read %s", path);
271*fd71220bSRobert Mustacchi 		} else if (!can_read) {
272*fd71220bSRobert Mustacchi 			dpio_fail("failed to read %s: %s, but expected ENOTSUP",
273*fd71220bSRobert Mustacchi 			    path, strerror(errno));
274*fd71220bSRobert Mustacchi 		} else {
275*fd71220bSRobert Mustacchi 			dpio_fail("failed to read %s: %s", path,
276*fd71220bSRobert Mustacchi 			    strerror(errno));
277*fd71220bSRobert Mustacchi 		}
278*fd71220bSRobert Mustacchi 	} else {
279*fd71220bSRobert Mustacchi 		if (!can_read) {
280*fd71220bSRobert Mustacchi 			dpio_fail("successfully read %s, but expected failure",
281*fd71220bSRobert Mustacchi 			    path);
282*fd71220bSRobert Mustacchi 		} else {
283*fd71220bSRobert Mustacchi 			dpio_pass("successfully read %s", path);
284*fd71220bSRobert Mustacchi 		}
285*fd71220bSRobert Mustacchi 	}
286*fd71220bSRobert Mustacchi 
287*fd71220bSRobert Mustacchi 	if (can_write) {
288*fd71220bSRobert Mustacchi 		dpio_test_cbops_write(fd, path);
289*fd71220bSRobert Mustacchi 		(void) close(fd);
290*fd71220bSRobert Mustacchi 		return;
291*fd71220bSRobert Mustacchi 	}
292*fd71220bSRobert Mustacchi 
293*fd71220bSRobert Mustacchi 	/*
294*fd71220bSRobert Mustacchi 	 * Test the can't write path case here. This means we expect that
295*fd71220bSRobert Mustacchi 	 * getting the current output will fail and that a write will fail. We
296*fd71220bSRobert Mustacchi 	 * won't bother checking the timing information in this case either.
297*fd71220bSRobert Mustacchi 	 * That is being done in the write check.
298*fd71220bSRobert Mustacchi 	 */
299*fd71220bSRobert Mustacchi 
300*fd71220bSRobert Mustacchi 	(void) memset(&curout, 0, sizeof (curout));
301*fd71220bSRobert Mustacchi 	if (ioctl(fd, DPIO_IOC_CUROUT, &curout) == 0) {
302*fd71220bSRobert Mustacchi 		dpio_fail("DPIO_IOC_CUROUT worked on %s, but expected failure",
303*fd71220bSRobert Mustacchi 		    path);
304*fd71220bSRobert Mustacchi 	} else if (errno != ENOTSUP) {
305*fd71220bSRobert Mustacchi 		dpio_fail("got unexpected errno from DPIO_IOC_CUROUT ioctl on "
306*fd71220bSRobert Mustacchi 		    "%s: %s, expected ENOTSUP", path, strerror(errno));
307*fd71220bSRobert Mustacchi 	} else {
308*fd71220bSRobert Mustacchi 		dpio_pass("DPIO_IOC_CUROUT failed on %s", path);
309*fd71220bSRobert Mustacchi 	}
310*fd71220bSRobert Mustacchi 
311*fd71220bSRobert Mustacchi 	val = 0;
312*fd71220bSRobert Mustacchi 	if (write(fd, &val, sizeof (val)) == 0) {
313*fd71220bSRobert Mustacchi 		dpio_fail("wrote to %s, but expected failure", path);
314*fd71220bSRobert Mustacchi 	} else if (errno != ENOTSUP) {
315*fd71220bSRobert Mustacchi 		dpio_fail("got unexpected errno writing to %s: %s, expected "
316*fd71220bSRobert Mustacchi 		    "ENOTSUP", path, strerror(errno));
317*fd71220bSRobert Mustacchi 	} else {
318*fd71220bSRobert Mustacchi 		dpio_pass("successfully failed to write %s", path);
319*fd71220bSRobert Mustacchi 	}
320*fd71220bSRobert Mustacchi 
321*fd71220bSRobert Mustacchi 	(void) close(fd);
322*fd71220bSRobert Mustacchi }
323*fd71220bSRobert Mustacchi 
324*fd71220bSRobert Mustacchi /*
325*fd71220bSRobert Mustacchi  * The /dev entries for a DPIO are created somewhat asynchronously from the
326*fd71220bSRobert Mustacchi  * minor node which is created synchronously in the ioctl. Poll in 10ms chunks
327*fd71220bSRobert Mustacchi  * for one of these to show up.
328*fd71220bSRobert Mustacchi  */
329*fd71220bSRobert Mustacchi static void
dpio_dev_poll(void)330*fd71220bSRobert Mustacchi dpio_dev_poll(void)
331*fd71220bSRobert Mustacchi {
332*fd71220bSRobert Mustacchi 	struct timespec ts;
333*fd71220bSRobert Mustacchi 	size_t max;
334*fd71220bSRobert Mustacchi 
335*fd71220bSRobert Mustacchi 	ts.tv_sec = 0;
336*fd71220bSRobert Mustacchi 	ts.tv_nsec = MSEC2NSEC(10);
337*fd71220bSRobert Mustacchi 	max = SEC2NSEC(1) / ts.tv_nsec;
338*fd71220bSRobert Mustacchi 
339*fd71220bSRobert Mustacchi 	for (size_t i = 0; i < ARRAY_SIZE(dpio_default_dpios); i++) {
340*fd71220bSRobert Mustacchi 		char buf[PATH_MAX];
341*fd71220bSRobert Mustacchi 		bool found = false;
342*fd71220bSRobert Mustacchi 
343*fd71220bSRobert Mustacchi 		(void) snprintf(buf, sizeof (buf), "/dev/dpio/%s",
344*fd71220bSRobert Mustacchi 		    dpio_default_dpios[i].dce_name);
345*fd71220bSRobert Mustacchi 
346*fd71220bSRobert Mustacchi 		for (size_t i = 0; i < max; i++) {
347*fd71220bSRobert Mustacchi 			struct stat st;
348*fd71220bSRobert Mustacchi 
349*fd71220bSRobert Mustacchi 			if (stat(buf, &st) == 0) {
350*fd71220bSRobert Mustacchi 				found = true;
351*fd71220bSRobert Mustacchi 				break;
352*fd71220bSRobert Mustacchi 			}
353*fd71220bSRobert Mustacchi 
354*fd71220bSRobert Mustacchi 			(void) nanosleep(&ts, NULL);
355*fd71220bSRobert Mustacchi 		}
356*fd71220bSRobert Mustacchi 
357*fd71220bSRobert Mustacchi 		if (!found) {
358*fd71220bSRobert Mustacchi 			dpio_fail("timed out waiting for %s", buf);
359*fd71220bSRobert Mustacchi 		}
360*fd71220bSRobert Mustacchi 	}
361*fd71220bSRobert Mustacchi }
362*fd71220bSRobert Mustacchi 
363*fd71220bSRobert Mustacchi /*
364*fd71220bSRobert Mustacchi  * Verify that basic FEXCL behavior works.
365*fd71220bSRobert Mustacchi  */
366*fd71220bSRobert Mustacchi static void
dpio_test_excl(void)367*fd71220bSRobert Mustacchi dpio_test_excl(void)
368*fd71220bSRobert Mustacchi {
369*fd71220bSRobert Mustacchi 	int exclfd, nonexcl;
370*fd71220bSRobert Mustacchi 	char path[PATH_MAX];
371*fd71220bSRobert Mustacchi 
372*fd71220bSRobert Mustacchi 	(void) snprintf(path, sizeof (path), "/dev/dpio/%s",
373*fd71220bSRobert Mustacchi 	    dpio_default_dpios[0].dce_name);
374*fd71220bSRobert Mustacchi 
375*fd71220bSRobert Mustacchi 	nonexcl = open(path, O_RDWR);
376*fd71220bSRobert Mustacchi 	if (nonexcl < 0) {
377*fd71220bSRobert Mustacchi 		dpio_fail("couldn't open base non-excl fd: %s",
378*fd71220bSRobert Mustacchi 		    strerror(errno));
379*fd71220bSRobert Mustacchi 		return;
380*fd71220bSRobert Mustacchi 	}
381*fd71220bSRobert Mustacchi 
382*fd71220bSRobert Mustacchi 	exclfd = open(path, O_RDWR | O_EXCL);
383*fd71220bSRobert Mustacchi 	if (exclfd >= 0) {
384*fd71220bSRobert Mustacchi 		dpio_fail("open O_EXCL worked, but dev was already open with "
385*fd71220bSRobert Mustacchi 		    "fd %d", nonexcl);
386*fd71220bSRobert Mustacchi 	} else if (errno != EBUSY) {
387*fd71220bSRobert Mustacchi 		dpio_fail("open O_EXCL if already open failed with unexpected "
388*fd71220bSRobert Mustacchi 		    "errno: %s, expected EBUSY", strerror(errno));
389*fd71220bSRobert Mustacchi 	} else {
390*fd71220bSRobert Mustacchi 		dpio_pass("open O_EXCL fails if already open");
391*fd71220bSRobert Mustacchi 	}
392*fd71220bSRobert Mustacchi 	(void) close(nonexcl);
393*fd71220bSRobert Mustacchi 
394*fd71220bSRobert Mustacchi 	exclfd = open(path, O_RDWR | O_EXCL);
395*fd71220bSRobert Mustacchi 	if (exclfd < 0) {
396*fd71220bSRobert Mustacchi 		dpio_fail("couldn't open bae excl fd: %s", strerror(errno));
397*fd71220bSRobert Mustacchi 		return;
398*fd71220bSRobert Mustacchi 	} else {
399*fd71220bSRobert Mustacchi 		dpio_pass("base O_EXCL open");
400*fd71220bSRobert Mustacchi 	}
401*fd71220bSRobert Mustacchi 
402*fd71220bSRobert Mustacchi 	nonexcl = open(path, O_RDWR);
403*fd71220bSRobert Mustacchi 	if (nonexcl >= 0) {
404*fd71220bSRobert Mustacchi 		dpio_fail("O_EXCL didn't block subsequent open of fd %d",
405*fd71220bSRobert Mustacchi 		    exclfd);
406*fd71220bSRobert Mustacchi 	} else if (errno != EBUSY) {
407*fd71220bSRobert Mustacchi 		dpio_fail("O_EXCL blocked other open, but with unexpected "
408*fd71220bSRobert Mustacchi 		    "errno: %s, expected EBUSY", strerror(errno));
409*fd71220bSRobert Mustacchi 	} else {
410*fd71220bSRobert Mustacchi 		dpio_pass("O_EXCL blocks subsequent open");
411*fd71220bSRobert Mustacchi 	}
412*fd71220bSRobert Mustacchi 
413*fd71220bSRobert Mustacchi 	(void) close(exclfd);
414*fd71220bSRobert Mustacchi }
415*fd71220bSRobert Mustacchi 
416*fd71220bSRobert Mustacchi /*
417*fd71220bSRobert Mustacchi  * Verify we can't destroy a DPIO if it's currently open.
418*fd71220bSRobert Mustacchi  */
419*fd71220bSRobert Mustacchi static void
dpio_destroy_ebusy(int ctrl_fd)420*fd71220bSRobert Mustacchi dpio_destroy_ebusy(int ctrl_fd)
421*fd71220bSRobert Mustacchi {
422*fd71220bSRobert Mustacchi 	int fd;
423*fd71220bSRobert Mustacchi 	char path[PATH_MAX];
424*fd71220bSRobert Mustacchi 	kgpio_dpio_destroy_t destroy;
425*fd71220bSRobert Mustacchi 
426*fd71220bSRobert Mustacchi 	(void) snprintf(path, sizeof (path), "/dev/dpio/%s",
427*fd71220bSRobert Mustacchi 	    dpio_default_dpios[0].dce_name);
428*fd71220bSRobert Mustacchi 
429*fd71220bSRobert Mustacchi 	fd = open(path, O_RDWR);
430*fd71220bSRobert Mustacchi 	if (fd < 0) {
431*fd71220bSRobert Mustacchi 		dpio_fail("failed to open %s for destruction tests: %s", path,
432*fd71220bSRobert Mustacchi 		    strerror(errno));
433*fd71220bSRobert Mustacchi 		return;
434*fd71220bSRobert Mustacchi 	}
435*fd71220bSRobert Mustacchi 
436*fd71220bSRobert Mustacchi 	(void) memset(&destroy, 0, sizeof (destroy));
437*fd71220bSRobert Mustacchi 	destroy.kdd_id = dpio_default_dpios[0].dce_gpio;
438*fd71220bSRobert Mustacchi 
439*fd71220bSRobert Mustacchi 	if (ioctl(ctrl_fd, KGPIO_IOC_DPIO_DESTROY, &destroy) == 0) {
440*fd71220bSRobert Mustacchi 		dpio_fail("DPIO was destroyed despite open fd!!");
441*fd71220bSRobert Mustacchi 	} else if (errno != EBUSY) {
442*fd71220bSRobert Mustacchi 		dpio_fail("failed to destroy DPIO with open fd, but got wrong "
443*fd71220bSRobert Mustacchi 		    "errno: found %s, expected EBUSY", strerror(errno));
444*fd71220bSRobert Mustacchi 	} else {
445*fd71220bSRobert Mustacchi 		dpio_pass("failed to destroy DPIO with open fd");
446*fd71220bSRobert Mustacchi 	}
447*fd71220bSRobert Mustacchi 
448*fd71220bSRobert Mustacchi 	(void) close(fd);
449*fd71220bSRobert Mustacchi }
450*fd71220bSRobert Mustacchi 
451*fd71220bSRobert Mustacchi int
main(void)452*fd71220bSRobert Mustacchi main(void)
453*fd71220bSRobert Mustacchi {
454*fd71220bSRobert Mustacchi 	int ctrl_fd;
455*fd71220bSRobert Mustacchi 
456*fd71220bSRobert Mustacchi 	ctrl_fd = open(dpio_ctrl_path, O_RDWR);
457*fd71220bSRobert Mustacchi 	if (ctrl_fd < 0) {
458*fd71220bSRobert Mustacchi 		err(EXIT_FAILURE, "failed to open controller %s",
459*fd71220bSRobert Mustacchi 		    dpio_ctrl_path);
460*fd71220bSRobert Mustacchi 	}
461*fd71220bSRobert Mustacchi 
462*fd71220bSRobert Mustacchi 	/*
463*fd71220bSRobert Mustacchi 	 * We use somewhat gross names with the hope that we'll avoid anything
464*fd71220bSRobert Mustacchi 	 * actually created.
465*fd71220bSRobert Mustacchi 	 */
466*fd71220bSRobert Mustacchi 	for (size_t i = 0; i < ARRAY_SIZE(dpio_default_dpios); i++) {
467*fd71220bSRobert Mustacchi 		const dpio_create_err_t *t = &dpio_default_dpios[i];
468*fd71220bSRobert Mustacchi 		if (!dpio_default_create(ctrl_fd, t->dce_gpio, t->dce_name,
469*fd71220bSRobert Mustacchi 		    t->dce_flags)) {
470*fd71220bSRobert Mustacchi 			dpio_fail("failed to create initial DPIOs");
471*fd71220bSRobert Mustacchi 			goto cleanup;
472*fd71220bSRobert Mustacchi 		} else {
473*fd71220bSRobert Mustacchi 			dpio_pass("created bootstrap DPIO %u", i);
474*fd71220bSRobert Mustacchi 		}
475*fd71220bSRobert Mustacchi 	}
476*fd71220bSRobert Mustacchi 
477*fd71220bSRobert Mustacchi 	for (size_t i = 0; i < ARRAY_SIZE(dpio_create_errs); i++) {
478*fd71220bSRobert Mustacchi 		dpio_bad_create(ctrl_fd, &dpio_create_errs[i]);
479*fd71220bSRobert Mustacchi 	}
480*fd71220bSRobert Mustacchi 
481*fd71220bSRobert Mustacchi 	/*
482*fd71220bSRobert Mustacchi 	 * Make sure we get our links and then go through and test the various
483*fd71220bSRobert Mustacchi 	 * cbops work or don't work based on the actual values that we set.
484*fd71220bSRobert Mustacchi 	 */
485*fd71220bSRobert Mustacchi 	dpio_dev_poll();
486*fd71220bSRobert Mustacchi 
487*fd71220bSRobert Mustacchi 	dpio_test_cbops("dpioTESTnone", true, false, false);
488*fd71220bSRobert Mustacchi 	dpio_test_cbops("dpioTESTro", true, true, false);
489*fd71220bSRobert Mustacchi 	dpio_test_cbops("dpioTESTrw", true, true, true);
490*fd71220bSRobert Mustacchi 	dpio_test_cbops("dpioTESTrwK", false, false, false);
491*fd71220bSRobert Mustacchi 
492*fd71220bSRobert Mustacchi 	/*
493*fd71220bSRobert Mustacchi 	 * Verify a few particular behaviours around fds. In particular we want
494*fd71220bSRobert Mustacchi 	 * to make sure O_EXCL / FEXCL is honored properly in the device. We
495*fd71220bSRobert Mustacchi 	 * also want to make sure that you can't destroy something if the fd is
496*fd71220bSRobert Mustacchi 	 * open.
497*fd71220bSRobert Mustacchi 	 */
498*fd71220bSRobert Mustacchi 	dpio_test_excl();
499*fd71220bSRobert Mustacchi 	dpio_destroy_ebusy(ctrl_fd);
500*fd71220bSRobert Mustacchi 
501*fd71220bSRobert Mustacchi cleanup:
502*fd71220bSRobert Mustacchi 	dpio_cleanup(ctrl_fd);
503*fd71220bSRobert Mustacchi 
504*fd71220bSRobert Mustacchi 	(void) close(ctrl_fd);
505*fd71220bSRobert Mustacchi 	if (dpio_exit == EXIT_SUCCESS) {
506*fd71220bSRobert Mustacchi 		(void) printf("All tests passed successfully!\n");
507*fd71220bSRobert Mustacchi 	}
508*fd71220bSRobert Mustacchi 	return (dpio_exit);
509*fd71220bSRobert Mustacchi }
510