xref: /illumos-gate/usr/src/test/i2c-tests/tests/ioctl/addresses.c (revision 0cbe48189888d02563dba9c90132ac391ba233b6)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2025 Oxide Computer Company
14  */
15 
16 /*
17  * Test various aspects of creating and removing devices. This operates on the
18  * smbussim1/0 port where there are no devices by default. In particular we
19  * want to verify a few different properties:
20  *
21  *  - Invalid nvlists and missing nvlist fields are caught
22  *  - The device name and compatible array are correctly populated
23  *  - Address allocation across ports is sensible, meaning that something on the
24  *    top-level port stops allocations on downstream ports and vice-versa
25  *
26  * Unlike the i2cadm tests, we care about the specific kernel ioctl return
27  * values.
28  */
29 
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <err.h>
33 #include <sys/debug.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <sys/mman.h>
39 #include <sys/nvpair.h>
40 #include <sys/sysmacros.h>
41 #include <libdevinfo.h>
42 
43 #include <sys/i2c/ioctl.h>
44 #include "i2c_ioctl_util.h"
45 
46 static const char *bad_names[] = {
47 	"",
48 	"thisisalongstringthatshouldbetoolongbuthasvalidchars",
49 	"0nonum",
50 	"42",
51 	"help#",
52 	"!at24c32",
53 	"at24%c32",
54 	"at@24xc32",
55 	"three rings",
56 	"elven(kings)",
57 	"ゼルダの伝説"
58 };
59 
60 static bool
test_add(int fd,const void * arg,size_t len,i2c_errno_t err,const char * desc)61 test_add(int fd, const void *arg, size_t len, i2c_errno_t err, const char *desc)
62 {
63 	bool ret = true;
64 	ui2c_dev_add_t add = {
65 		.uda_error = {
66 			/*
67 			 * We use token values here to make sure that they're
68 			 * always set by the target.
69 			 */
70 			INT32_MIN,
71 			INT32_MIN
72 		},
73 		.uda_nvl = (uintptr_t)arg,
74 		.uda_nvl_len = len
75 	};
76 
77 	if (ioctl(fd, UI2C_IOCTL_DEVICE_ADD, &add) != 0) {
78 		warnx("TEST FAILED: %s: add ioctl failed unexpectedly with "
79 		    "errno %s", desc, strerrorname_np(errno));
80 		return (false);
81 	}
82 
83 	if (add.uda_error.i2c_error != err) {
84 		warnx("TEST FAILED: %s: ioctl failed with I2C error 0x%x, "
85 		    "expected 0x%x", desc, add.uda_error.i2c_error, err);
86 		ret = false;
87 	}
88 
89 	if (add.uda_error.i2c_ctrl != I2C_CTRL_E_OK) {
90 		warnx("TEST FAILED: %s: ioctl has unexpected controller "
91 		    "error 0x%x", desc, add.uda_error.i2c_ctrl);
92 		ret = false;
93 	}
94 
95 	if (ret) {
96 		if (err == I2C_CORE_E_OK) {
97 			(void) printf("TEST PASSED: %s correctly created "
98 			    "device\n", desc);
99 		} else {
100 			(void) printf("TEST PASSED: %s correctly failed with "
101 			    "0x%x\n", desc, err);
102 		}
103 	}
104 
105 	return (ret);
106 }
107 
108 static bool
test_add_nvlist(int fd,nvlist_t * nvl,i2c_errno_t err,const char * desc)109 test_add_nvlist(int fd, nvlist_t *nvl, i2c_errno_t err, const char *desc)
110 {
111 	size_t len;
112 	char *data = fnvlist_pack(nvl, &len);
113 	bool ret = test_add(fd, data, len, err, desc);
114 	fnvlist_pack_free(data, len);
115 	return (ret);
116 }
117 
118 static bool
test_bad_nvlists(int fd)119 test_bad_nvlists(int fd)
120 {
121 	bool ret = true;
122 	const char *str = "I promise I'm an nvlist_t";
123 
124 	if (!test_add(fd, NULL, 4 * 1024 * 1024, I2C_IOCTL_E_NVL_TOO_BIG,
125 	    "nvlist_t too large to copy in")) {
126 		ret = false;
127 	}
128 
129 	size_t pgsz = (size_t)sysconf(_SC_PAGESIZE);
130 	void *addr = mmap(NULL, pgsz, PROT_NONE, MAP_PRIVATE | MAP_ANON,
131 	    -1, 0);
132 	VERIFY3P(addr, !=, NULL);
133 	if (!test_add(fd, addr, pgsz, I2C_IOCTL_E_BAD_USER_DATA, "unreadable "
134 	    "user data")) {
135 		ret = false;
136 	}
137 	VERIFY0(munmap(addr, pgsz));
138 
139 	if (!test_add(fd, str, strlen(str) + 1, I2C_IOCTL_E_NVL_INVALID,
140 	    "unparseable nvlist_t")) {
141 		ret = false;
142 	}
143 
144 	/*
145 	 * Go through and test all of the missing keys cases.
146 	 */
147 	nvlist_t *nvl = fnvlist_alloc();
148 	if (!test_add_nvlist(fd, nvl, I2C_IOCTL_E_NVL_KEY_MISSING, "missing "
149 	    "keys (all)")) {
150 		ret = false;
151 	}
152 
153 	fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_ADDR, 0x00);
154 	fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_TYPE, I2C_ADDR_7BIT);
155 	if (!test_add_nvlist(fd, nvl, I2C_IOCTL_E_NVL_KEY_MISSING, "missing "
156 	    "keys (name)")) {
157 		ret = false;
158 	}
159 	nvlist_free(nvl);
160 
161 	nvl = fnvlist_alloc();
162 	fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_ADDR, 0x00);
163 	fnvlist_add_string(nvl, UI2C_IOCTL_NVL_NAME, "foobar");
164 	if (!test_add_nvlist(fd, nvl, I2C_IOCTL_E_NVL_KEY_MISSING, "missing "
165 	    "keys (type)")) {
166 		ret = false;
167 	}
168 	nvlist_free(nvl);
169 
170 	nvl = fnvlist_alloc();
171 	fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_TYPE, I2C_ADDR_7BIT);
172 	fnvlist_add_string(nvl, UI2C_IOCTL_NVL_NAME, "foobar");
173 	if (!test_add_nvlist(fd, nvl, I2C_IOCTL_E_NVL_KEY_MISSING, "missing "
174 	    "keys (type)")) {
175 		ret = false;
176 	}
177 	nvlist_free(nvl);
178 
179 	nvl = fnvlist_alloc();
180 	fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_ADDR, 0x00);
181 	fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_TYPE, I2C_ADDR_7BIT);
182 	fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_NAME, 0x42);
183 	if (!test_add_nvlist(fd, nvl, I2C_IOCTL_E_NVL_KEY_BAD_TYPE, "bad key "
184 	    "type (name)")) {
185 		ret = false;
186 	}
187 	nvlist_free(nvl);
188 
189 	nvl = fnvlist_alloc();
190 	fnvlist_add_uint32(nvl, UI2C_IOCTL_NVL_ADDR, 0x00);
191 	fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_TYPE, I2C_ADDR_7BIT);
192 	fnvlist_add_string(nvl, UI2C_IOCTL_NVL_NAME, "foobar");
193 	if (!test_add_nvlist(fd, nvl, I2C_IOCTL_E_NVL_KEY_BAD_TYPE, "bad key "
194 	    "type (addr)")) {
195 		ret = false;
196 	}
197 	nvlist_free(nvl);
198 
199 	nvl = fnvlist_alloc();
200 	fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_ADDR, 0x00);
201 	fnvlist_add_int64(nvl, UI2C_IOCTL_NVL_TYPE, I2C_ADDR_7BIT);
202 	fnvlist_add_string(nvl, UI2C_IOCTL_NVL_NAME, "foobar");
203 	if (!test_add_nvlist(fd, nvl, I2C_IOCTL_E_NVL_KEY_BAD_TYPE, "bad key "
204 	    "type (type)")) {
205 		ret = false;
206 	}
207 	nvlist_free(nvl);
208 
209 	nvl = fnvlist_alloc();
210 	fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_ADDR, 0x00);
211 	fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_TYPE, I2C_ADDR_7BIT);
212 	fnvlist_add_string(nvl, UI2C_IOCTL_NVL_NAME, "foobar");
213 	fnvlist_add_byte_array(nvl, UI2C_IOCTL_NVL_COMPAT, (uchar_t *)str,
214 	    strlen(str));
215 	if (!test_add_nvlist(fd, nvl, I2C_IOCTL_E_NVL_KEY_BAD_TYPE, "bad key "
216 	    "type (compat)")) {
217 		ret = false;
218 	}
219 	nvlist_free(nvl);
220 
221 	for (size_t i = 0; i < nbad_addrs; i++) {
222 		char desc[128];
223 
224 		(void) snprintf(desc, sizeof (desc), "bad address %zu "
225 		    "(0x%x,0x%x)", i, bad_addrs[i].ba_type,
226 		    bad_addrs[i].ba_addr);
227 		nvl = fnvlist_alloc();
228 		fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_TYPE,
229 		    bad_addrs[i].ba_type);
230 		fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_ADDR,
231 		    bad_addrs[i].ba_addr);
232 		fnvlist_add_string(nvl, UI2C_IOCTL_NVL_NAME, "foobar");
233 		if (!test_add_nvlist(fd, nvl, bad_addrs[i].ba_error, desc)) {
234 			ret = false;
235 		}
236 		fnvlist_free(nvl);
237 	}
238 
239 	for (size_t i = 0; i < ARRAY_SIZE(bad_names); i++) {
240 		char desc[128];
241 
242 		(void) snprintf(desc, sizeof (desc), "bad names %zu", i);
243 		nvl = fnvlist_alloc();
244 		fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_TYPE, I2C_ADDR_7BIT);
245 		fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_ADDR, 0x42);
246 		fnvlist_add_string(nvl, UI2C_IOCTL_NVL_NAME, bad_names[i]);
247 		if (!test_add_nvlist(fd, nvl, I2C_IOCTL_E_BAD_DEV_NAME, desc)) {
248 			ret = false;
249 		}
250 		fnvlist_free(nvl);
251 	}
252 
253 	for (size_t i = 0; i < ARRAY_SIZE(bad_names); i++) {
254 		char desc[128];
255 
256 		(void) snprintf(desc, sizeof (desc), "bad compat %zu", i);
257 		nvl = fnvlist_alloc();
258 		fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_TYPE, I2C_ADDR_7BIT);
259 		fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_ADDR, 0x42);
260 		fnvlist_add_string(nvl, UI2C_IOCTL_NVL_NAME, "foobar");
261 		fnvlist_add_string_array(nvl, UI2C_IOCTL_NVL_COMPAT,
262 		    (char * const *)&bad_names[i], 1);
263 		if (!test_add_nvlist(fd, nvl, I2C_IOCTL_E_BAD_DEV_NAME, desc)) {
264 			ret = false;
265 		}
266 		fnvlist_free(nvl);
267 	}
268 
269 	char *compat[42];
270 	for (size_t i = 0; i < ARRAY_SIZE(compat); i++) {
271 		compat[i] = "at24c32";
272 	}
273 
274 	nvl = fnvlist_alloc();
275 	fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_TYPE, I2C_ADDR_7BIT);
276 	fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_ADDR, 0x42);
277 	fnvlist_add_string(nvl, UI2C_IOCTL_NVL_NAME, "foobar");
278 	fnvlist_add_string_array(nvl, UI2C_IOCTL_NVL_COMPAT, compat,
279 	    ARRAY_SIZE(compat));
280 	if (!test_add_nvlist(fd, nvl, I2C_IOCTL_E_COMPAT_LEN_RANGE,
281 	    "compat[] too long")) {
282 		ret = false;
283 	}
284 	fnvlist_free(nvl);
285 
286 	nvl = fnvlist_alloc();
287 	fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_TYPE, I2C_ADDR_7BIT);
288 	fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_ADDR, 0x42);
289 	fnvlist_add_string(nvl, UI2C_IOCTL_NVL_NAME, "foobar");
290 	fnvlist_add_string(nvl, "magicite", "materia");
291 
292 	if (!test_add_nvlist(fd, nvl, I2C_IOCTL_E_NVL_KEY_UNKNOWN,
293 	    "extra keys")) {
294 		ret = false;
295 	}
296 	fnvlist_free(nvl);
297 
298 	return (ret);
299 }
300 
301 static bool
test_add_device(int fd,const char * name,uint8_t addr,i2c_errno_t err,const char * desc)302 test_add_device(int fd, const char *name, uint8_t addr, i2c_errno_t err,
303     const char *desc)
304 {
305 	nvlist_t *nvl = fnvlist_alloc();
306 	fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_TYPE, I2C_ADDR_7BIT);
307 	fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_ADDR, addr);
308 	fnvlist_add_string(nvl, UI2C_IOCTL_NVL_NAME, name);
309 	bool ret = test_add_nvlist(fd, nvl, err, desc);
310 	fnvlist_free(nvl);
311 	return (ret);
312 }
313 
314 static bool
test_address_conflicts(int fd)315 test_address_conflicts(int fd)
316 {
317 	bool ret = true;
318 
319 	if (!test_add_device(fd, "pca9548", 0x70, I2C_CORE_E_OK, "create "
320 	    "pca9548")) {
321 		ret = false;
322 	}
323 
324 	if (!test_add_device(fd, "fake-device", 0x70, I2C_CORE_E_ADDR_IN_USE,
325 	    "address in use fails (0x70)")) {
326 		ret = false;
327 	}
328 
329 	int pfd0 = i2c_ioctl_test_get_fd(I2C_D_PORT, "smbussim1/0/0x70/mux/0",
330 	    O_RDWR);
331 	int pfd4 = i2c_ioctl_test_get_fd(I2C_D_PORT, "smbussim1/0/0x70/mux/4",
332 	    O_RDWR);
333 
334 	/*
335 	 * Verify that an address used downstream on the mux cannot be used
336 	 * upstream of it.
337 	 */
338 	if (!test_add_device(pfd0, "fake-device", 0x42, I2C_CORE_E_OK, "create "
339 	    "fake device (ox42) on mux port 0")) {
340 		ret = false;
341 	}
342 
343 	if (!test_add_device(pfd4, "fake-device", 0x42, I2C_CORE_E_OK, "create "
344 	    "fake device (ox42) on mux port 4")) {
345 		ret = false;
346 	}
347 
348 	if (!test_add_device(fd, "fake-device", 0x42, I2C_CORE_E_ADDR_IN_USE,
349 	    "cannot create device on upstream port used on downstream ports")) {
350 		ret = false;
351 	}
352 
353 	/*
354 	 * Verify that an address used on the top-level port cannot be used
355 	 * downstream of it.
356 	 */
357 	if (test_add_device(fd, "fake-device", 0x23, I2C_CORE_E_OK, "create "
358 	    "fake device (0x23)")) {
359 		if (!test_add_device(pfd0, "fake-device", 0x23,
360 		    I2C_CORE_E_ADDR_IN_USE, "cannot allocate address on "
361 		    "downstream port when used upstream (1)")) {
362 			ret = false;
363 		}
364 
365 		if (!test_add_device(pfd4, "fake-device", 0x23,
366 		    I2C_CORE_E_ADDR_IN_USE, "cannot allocate address on "
367 		    "downstream port when used upstream (2)")) {
368 			ret = false;
369 		}
370 	} else {
371 		ret = false;
372 	}
373 
374 	VERIFY0(close(pfd4));
375 	VERIFY0(close(pfd0));
376 	return (ret);
377 }
378 
379 typedef struct {
380 	bool tcc_ret;
381 	bool tcc_compat0;
382 	bool tcc_compat1;
383 	char **tcc_compat;
384 } test_compat_cb_t;
385 
386 static int
test_compat_walk_cb(di_node_t di,void * arg)387 test_compat_walk_cb(di_node_t di, void *arg)
388 {
389 	test_compat_cb_t *cb = arg;
390 	const char *name = di_node_name(di);
391 	int exp;
392 
393 	if (strcmp(name, "fake-compat-0") == 0) {
394 		cb->tcc_compat0 = true;
395 		exp = 1;
396 	} else if (strcmp(name, "fake-compat-1") == 0) {
397 		cb->tcc_compat1 = true;
398 		exp = 32;
399 	} else {
400 		return (DI_WALK_CONTINUE);
401 	}
402 
403 	char *compat;
404 	int nents = di_prop_lookup_strings(DDI_DEV_T_ANY, di, "compatible",
405 	    &compat);
406 	if (nents == exp) {
407 		bool valid = true;
408 		for (int i = 0; i < exp; i++) {
409 			if (strcmp(compat, cb->tcc_compat[i]) != 0) {
410 				valid = false;
411 				warnx("TEST FAILED: %s has incorrect "
412 				    "compatible[%d] entry: expected %s, found "
413 				    "%s", name, i, compat, cb->tcc_compat[i]);
414 			}
415 
416 			compat += strlen(compat) + 1;
417 		}
418 
419 		if (valid) {
420 			(void) printf("TEST PASSED: devi %s has correct "
421 			    "comaptible[%d]\n", name, nents);
422 		}
423 	} else {
424 		warnx("TEST FAILED: node %s has wrong compatible[] count: "
425 		    "found %d, expected %d", name, nents, exp);
426 		cb->tcc_ret = false;
427 	}
428 
429 	return (DI_WALK_PRUNECHILD);
430 }
431 
432 static bool
test_compat(int fd)433 test_compat(int fd)
434 {
435 	bool ret = true;
436 	char *compat[32];
437 
438 	for (size_t i = 0; i < ARRAY_SIZE(compat); i++) {
439 		if (asprintf(&compat[i], "bad,compat%zu", i) < 0) {
440 			err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to "
441 			    "construct compat entry %zu", i);
442 		}
443 	}
444 
445 	nvlist_t *nvl = fnvlist_alloc();
446 	fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_TYPE, I2C_ADDR_7BIT);
447 	fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_ADDR, 0x30);
448 	fnvlist_add_string(nvl, UI2C_IOCTL_NVL_NAME, "fake-compat-0");
449 	fnvlist_add_string_array(nvl, UI2C_IOCTL_NVL_COMPAT, compat,
450 	    1);
451 	if (!test_add_nvlist(fd, nvl, I2C_CORE_E_OK, "1-entry compat[]")) {
452 		ret = false;
453 	}
454 	fnvlist_free(nvl);
455 
456 	nvl = fnvlist_alloc();
457 	fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_TYPE, I2C_ADDR_7BIT);
458 	fnvlist_add_uint16(nvl, UI2C_IOCTL_NVL_ADDR, 0x31);
459 	fnvlist_add_string(nvl, UI2C_IOCTL_NVL_NAME, "fake-compat-1");
460 	fnvlist_add_string_array(nvl, UI2C_IOCTL_NVL_COMPAT, compat,
461 	    ARRAY_SIZE(compat));
462 	if (!test_add_nvlist(fd, nvl, I2C_CORE_E_OK, "32-entry compat[]")) {
463 		ret = false;
464 	}
465 	fnvlist_free(nvl);
466 
467 	test_compat_cb_t cb = {
468 		.tcc_ret = true,
469 		.tcc_compat0 = false,
470 		.tcc_compat1 = false,
471 		.tcc_compat = compat
472 	};
473 
474 	di_node_t root = di_init(i2c_sim_dipath, DINFOCPYALL);
475 	(void) di_walk_node(root, DI_WALK_CLDFIRST, &cb, test_compat_walk_cb);
476 	di_fini(root);
477 
478 	if (!cb.tcc_ret) {
479 		ret = false;
480 	}
481 
482 	if (!cb.tcc_compat0) {
483 		warnx("TEST FAILED: failed to find devi fake-compat-0");
484 		ret = false;
485 	}
486 
487 	if (!cb.tcc_compat1) {
488 		warnx("TEST FAILED: failed to find devi fake-compat-1");
489 		ret = false;
490 	}
491 
492 	for (size_t i = 0; i < ARRAY_SIZE(compat); i++) {
493 		free(compat[i]);
494 	}
495 
496 	return (ret);
497 }
498 
499 static bool
test_rm(int fd,uint16_t family,uint16_t addr,i2c_errno_t err,const char * desc)500 test_rm(int fd, uint16_t family, uint16_t addr, i2c_errno_t err,
501     const char *desc)
502 {
503 	bool ret = true;
504 	ui2c_dev_rem_t rm = {
505 		.udr_error = {
506 			/* Token errors to ensure this is set by copyout. */
507 			INT32_MAX,
508 			INT32_MAX
509 		},
510 		.udr_addr = { family, addr }
511 	};
512 
513 	if (ioctl(fd, UI2C_IOCTL_DEVICE_REMOVE, &rm) != 0) {
514 		warnx("TEST FAILED: %s: removal ioctl failed unexpectedly with "
515 		    "errno %s", desc, strerrorname_np(errno));
516 		return (false);
517 	}
518 
519 	if (rm.udr_error.i2c_error != err) {
520 		warnx("TEST FAILED: %s: ioctl failed with I2C error 0x%x, "
521 		    "expected 0x%x", desc, rm.udr_error.i2c_error, err);
522 		ret = false;
523 	}
524 
525 	if (rm.udr_error.i2c_ctrl != I2C_CTRL_E_OK) {
526 		warnx("TEST FAILED: %s: ioctl has unexpected controller "
527 		    "error 0x%x", desc, rm.udr_error.i2c_ctrl);
528 		ret = false;
529 	}
530 
531 	if (ret) {
532 		if (err == I2C_CORE_E_OK) {
533 			(void) printf("TEST PASSED: %s correctly removed "
534 			    "device\n", desc);
535 		} else {
536 			(void) printf("TEST PASSED: %s correctly failed with "
537 			    "0x%x\n", desc, err);
538 		}
539 	}
540 
541 	return (ret);
542 }
543 
544 static bool
test_teardown(int fd)545 test_teardown(int fd)
546 {
547 	bool ret = true;
548 
549 	for (size_t i = 0; i < nbad_addrs; i++) {
550 		char desc[128];
551 
552 		(void) snprintf(desc, sizeof (desc), "remove bad address %zu",
553 		    i);
554 		if (!test_rm(fd, bad_addrs[i].ba_type, bad_addrs[i].ba_addr,
555 		    bad_addrs[i].ba_error, desc)) {
556 			ret = false;
557 		}
558 	}
559 
560 	if (!test_rm(fd, I2C_ADDR_7BIT, 0x70, I2C_IOCTL_E_NEXUS,
561 	    "cannot tear down mux with devices under it")) {
562 		ret = false;
563 	}
564 
565 	if (!test_rm(fd, I2C_ADDR_7BIT, 0x30, I2C_CORE_E_OK, "tear down device "
566 	    "unrelated to mux (0x30)")) {
567 		ret = false;
568 	}
569 
570 	if (!test_rm(fd, I2C_ADDR_7BIT, 0x31, I2C_CORE_E_OK, "tear down device "
571 	    "unrelated to mux (0x31)")) {
572 		ret = false;
573 	}
574 
575 	int pfd0 = i2c_ioctl_test_get_fd(I2C_D_PORT, "smbussim1/0/0x70/mux/0",
576 	    O_RDWR);
577 	if (!test_rm(pfd0, I2C_ADDR_7BIT, 0x42, I2C_CORE_E_OK, "tear down "
578 	    "device under mux (0/0x42)")) {
579 		ret = false;
580 	}
581 
582 	if (!test_rm(pfd0, I2C_ADDR_7BIT, 0x42, I2C_CORE_E_UNKNOWN_ADDR,
583 	    "cannot remove 0/0x42 a second time on same port")) {
584 		ret = false;
585 	}
586 	VERIFY0(close(pfd0));
587 
588 	int pfd2 = i2c_ioctl_test_get_fd(I2C_D_PORT, "smbussim1/0/0x70/mux/2",
589 	    O_RDWR);
590 	if (!test_rm(pfd2, I2C_ADDR_7BIT, 0x42, I2C_CORE_E_UNKNOWN_ADDR,
591 	    "cannot remove non-existent 2/0x42")) {
592 		ret = false;
593 	}
594 	VERIFY0(close(pfd2));
595 
596 	int pdf4 = i2c_ioctl_test_get_fd(I2C_D_PORT, "smbussim1/0/0x70/mux/4",
597 	    O_RDWR);
598 	if (!test_rm(pdf4, I2C_ADDR_7BIT, 0x42, I2C_CORE_E_OK, "tear down "
599 	    "device under mux (4/0x42)")) {
600 		ret = false;
601 	}
602 
603 	if (!test_rm(pdf4, I2C_ADDR_7BIT, 0x42, I2C_CORE_E_UNKNOWN_ADDR,
604 	    "cannot remove 4/0x42 a second time on same port")) {
605 		ret = false;
606 	}
607 	VERIFY0(close(pdf4));
608 
609 	if (!test_rm(fd, I2C_ADDR_7BIT, 0x70, I2C_CORE_E_OK, "tear down empty "
610 	    "mux")) {
611 		ret = false;
612 	}
613 
614 	if (!test_rm(fd, I2C_ADDR_7BIT, 0x23, I2C_CORE_E_OK, "tear down device "
615 	    "(0x23)")) {
616 		ret = false;
617 	}
618 	return (ret);
619 }
620 
621 int
main(void)622 main(void)
623 {
624 	int ret = EXIT_SUCCESS;
625 	int fd = i2c_ioctl_test_get_fd(I2C_D_PORT, "smbussim1/0", O_RDWR);
626 
627 	if (!test_bad_nvlists(fd)) {
628 		ret = EXIT_FAILURE;
629 	}
630 
631 	if (!test_address_conflicts(fd)) {
632 		ret = EXIT_FAILURE;
633 	}
634 
635 	if (!test_compat(fd)) {
636 		ret = EXIT_FAILURE;
637 	}
638 
639 	if (!test_teardown(fd)) {
640 		ret = EXIT_FAILURE;
641 	}
642 
643 	VERIFY0(close(fd));
644 	if (ret == EXIT_SUCCESS) {
645 		(void) printf("All tests passed successfully\n");
646 	}
647 	return (ret);
648 }
649