xref: /illumos-gate/usr/src/test/i2c-tests/tests/libi2c/addr-util.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 Compnay
14  */
15 
16 /*
17  * Test our favorite things: parsing addresses and turning them back into
18  * strings.
19  */
20 
21 #include <err.h>
22 #include <string.h>
23 #include <sys/sysmacros.h>
24 
25 #include "libi2c_test_util.h"
26 
27 typedef struct addr_map {
28 	const char *am_str;
29 	const char *am_comp;
30 	uint16_t am_type;
31 	uint16_t am_addr;
32 } addr_map_t;
33 
34 /*
35  * The system always outputs addresses in hex, so we have an optional second
36  * form that indicates what we expect back.
37  */
38 static const addr_map_t roundtrip_addrs[] = {
39 	{ "0x23", NULL, I2C_ADDR_7BIT, 0x23 },
40 	{ "0x10", NULL, I2C_ADDR_7BIT, 0x10 },
41 	{ "0x7f", NULL, I2C_ADDR_7BIT, 0x7f },
42 	{ "30", "0x1e", I2C_ADDR_7BIT, 0x1e },
43 	{ "0x9", "0x09", I2C_ADDR_7BIT, 0x09 },
44 	{ "10b,0", "10b,0x000", I2C_ADDR_10BIT, 0x0 },
45 	{ "10b,0x3ff", NULL, I2C_ADDR_10BIT, 0x3ff },
46 	{ "10b,0x169", NULL, I2C_ADDR_10BIT, 0x169 },
47 	{ "10b,777", "10b,0x309", I2C_ADDR_10BIT, 0x309 }
48 };
49 
50 typedef struct bad_str {
51 	const char *bs_str;
52 	i2c_err_t bs_err;
53 } bad_str_t;
54 
55 static const bad_str_t bad_strs[] = {
56 	{ "hello", I2C_ERR_BAD_ADDR },
57 	{ "0x", I2C_ERR_BAD_ADDR },
58 	{ "0x3456789", I2C_ERR_BAD_ADDR },
59 	{ "0x23nope", I2C_ERR_BAD_ADDR },
60 	{ "2b", I2C_ERR_BAD_ADDR },
61 	{ "0x2bornot2b", I2C_ERR_BAD_ADDR },
62 	{ "0x80", I2C_ERR_BAD_ADDR },
63 	{ "256", I2C_ERR_BAD_ADDR },
64 	{ "-4", I2C_ERR_BAD_ADDR },
65 	{ "0x23;0x34", I2C_ERR_BAD_ADDR },
66 	{ "10b,its", I2C_ERR_BAD_ADDR },
67 	{ "10b,0xa ", I2C_ERR_BAD_ADDR },
68 	{ "10b,123\ttrap", I2C_ERR_BAD_ADDR },
69 	{ "10b,-23", I2C_ERR_BAD_ADDR },
70 	{ "foo,0x12", I2C_ERR_BAD_ADDR_TYPE },
71 	{ ",0x12", I2C_ERR_BAD_ADDR_TYPE },
72 	{ "10b2,0x12", I2C_ERR_BAD_ADDR_TYPE },
73 };
74 
75 typedef struct bad_addr {
76 	uint16_t ba_type;
77 	uint16_t ba_addr;
78 	i2c_err_t ba_err;
79 } bad_addr_t;
80 
81 /*
82  * Unlike other cases, reserved addresses aren't part of this as we will parse
83  * any address, regardless if it's reserved for some reason.
84  */
85 static const bad_addr_t bad_addrs[] = {
86 	{ I2C_ADDR_7BIT, 0x80, I2C_ERR_BAD_ADDR },
87 	{ I2C_ADDR_7BIT, 0x7777, I2C_ERR_BAD_ADDR },
88 	{ I2C_ADDR_7BIT, INT16_MAX, I2C_ERR_BAD_ADDR },
89 	{ I2C_ADDR_10BIT, 0x400, I2C_ERR_BAD_ADDR },
90 	{ I2C_ADDR_10BIT, 0x7777, I2C_ERR_BAD_ADDR },
91 	{ I2C_ADDR_10BIT, 0x2bb2, I2C_ERR_BAD_ADDR },
92 	{ I2C_ADDR_10BIT, UINT16_MAX, I2C_ERR_BAD_ADDR },
93 	{ I2C_ADDR_10BIT + 1, 0x0, I2C_ERR_BAD_ADDR_TYPE },
94 	{ I2C_ADDR_10BIT + 1, 0x23, I2C_ERR_BAD_ADDR_TYPE },
95 	{ 0x42, 0x23, I2C_ERR_BAD_ADDR_TYPE },
96 	{ 0x7777, 0x7777, I2C_ERR_BAD_ADDR_TYPE },
97 	{ INT16_MAX, UINT16_MAX, I2C_ERR_BAD_ADDR_TYPE },
98 };
99 
100 static bool
valid_addr_roundtrip(i2c_hdl_t * hdl)101 valid_addr_roundtrip(i2c_hdl_t *hdl)
102 {
103 	bool ret = true;
104 
105 	for (size_t i = 0; i < ARRAY_SIZE(roundtrip_addrs); i++) {
106 		const addr_map_t *map = &roundtrip_addrs[i];
107 		char buf[128];
108 		i2c_addr_t addr;
109 
110 		if (!i2c_addr_parse(hdl, map->am_str, &addr)) {
111 			libi2c_test_warn(hdl, "TEST FAILED: failed to parse "
112 			    "string %s", map->am_str);
113 			ret = false;
114 		} else {
115 			bool valid = true;
116 			if (map->am_type != addr.ia_type) {
117 				warnx("TEST FAILED: parsed string %s address "
118 				    "type as 0x%x, expected 0x%x", map->am_str,
119 				    addr.ia_type, map->am_type);
120 				valid = false;
121 			}
122 
123 			if (map->am_addr != addr.ia_addr) {
124 				warnx("TEST FAILED: parsed string %s address "
125 				    "as 0x%x, expected 0x%x", map->am_str,
126 				    addr.ia_addr, map->am_addr);
127 				valid = false;
128 			}
129 
130 			if (valid) {
131 				(void) printf("TEST PASSED: successful "
132 				    "str->addr of %s\n", map->am_str);
133 			}
134 		}
135 
136 		addr.ia_type = map->am_type;
137 		addr.ia_addr = map->am_addr;
138 		const char *comp = map->am_comp != NULL ? map->am_comp :
139 		    map->am_str;
140 		if (!i2c_addr_to_string(hdl, &addr, buf, sizeof (buf))) {
141 			libi2c_test_warn(hdl, "TEST FAILED: failed to "
142 			    "transform address 0x%x,0x%x to a string",
143 			    map->am_type, map->am_addr);
144 			ret = false;
145 		} else if (strcmp(buf, comp) != 0) {
146 			libi2c_test_warn(hdl, "TEST FAILED: parsed 0x%x,0x%x "
147 			    "to %s, but expected %s", map->am_type,
148 			    map->am_addr, buf, comp);
149 			ret = false;
150 		} else {
151 			(void) printf("TEST PASSED: successful addr->str of "
152 			    "%s\n", map->am_str);
153 		}
154 	}
155 
156 	return (ret);
157 }
158 
159 static bool
invalid_strings(i2c_hdl_t * hdl)160 invalid_strings(i2c_hdl_t *hdl)
161 {
162 	bool ret = true;
163 
164 	for (size_t i = 0; i < ARRAY_SIZE(bad_strs); i++) {
165 		i2c_addr_t addr;
166 
167 		if (i2c_addr_parse(hdl, bad_strs[i].bs_str, &addr)) {
168 			warnx("TEST FAILED: incorrectly parsed string %s "
169 			    "as a valid address", bad_strs[i].bs_str);
170 			ret = false;
171 			continue;
172 		}
173 
174 		i2c_err_t err = i2c_err(hdl);
175 		if (err != bad_strs[i].bs_err) {
176 			warnx("TEST FAILED: parsing address string %s returned "
177 			    "%s (0x%x) but expected %s (0x%x)",
178 			    bad_strs[i].bs_str, i2c_errtostr(hdl, err), err,
179 			    i2c_errtostr(hdl, bad_strs[i].bs_err),
180 			    bad_strs[i].bs_err);
181 			ret = false;
182 		} else {
183 			(void) printf("TEST PASSED: failed to parse address %s "
184 			    "with error %s (0x%x)\n", bad_strs[i].bs_str,
185 			    i2c_errtostr(hdl, err), err);
186 		}
187 	}
188 
189 	return (ret);
190 }
191 
192 static bool
invalid_addrs(i2c_hdl_t * hdl)193 invalid_addrs(i2c_hdl_t *hdl)
194 {
195 	bool ret = true;
196 
197 	for (size_t i = 0; i < ARRAY_SIZE(bad_addrs); i++) {
198 		char buf[128];
199 		i2c_addr_t addr;
200 
201 		addr.ia_type = bad_addrs[i].ba_type;
202 		addr.ia_addr = bad_addrs[i].ba_addr;
203 		if (i2c_addr_to_string(hdl, &addr, buf, sizeof (buf))) {
204 			warnx("TEST FAILED: unexpectedly parsed 0x%x,0x%x "
205 			    "as a valid string", addr.ia_type, addr.ia_addr);
206 			ret = false;
207 			continue;
208 		}
209 
210 		i2c_err_t err = i2c_err(hdl);
211 		if (err != bad_addrs[i].ba_err) {
212 			warnx("TEST FAILED: parsing address 0x%x,0x%x failed "
213 			    "with %s (0x%x) but expected %s (0x%x)",
214 			    addr.ia_type, addr.ia_addr, i2c_errtostr(hdl, err),
215 			    err, i2c_errtostr(hdl, bad_addrs[i].ba_err),
216 			    bad_addrs[i].ba_err);
217 			ret = false;
218 		} else {
219 			(void) printf("TEST PASSED: failed to parse address "
220 			    "0x%x,0x%x with error %s (0x%x)\n", addr.ia_type,
221 			    addr.ia_addr, i2c_errtostr(hdl, err), err);
222 		}
223 	}
224 
225 	return (ret);
226 }
227 
228 static bool
short_buffers(i2c_hdl_t * hdl)229 short_buffers(i2c_hdl_t *hdl)
230 {
231 	bool ret = true;
232 	char buf[32];
233 	i2c_addr_t addr = { I2C_ADDR_7BIT, 0x23 };
234 
235 	if (i2c_addr_to_string(hdl, &addr, buf, 0)) {
236 		warnx("TEST FAILED: i2c_addr_to_string() with zero sized "
237 		    "buffer unexpectedly worked");
238 		ret = false;
239 	} else if (i2c_err(hdl) != I2C_ERR_BUF_TOO_SMALL) {
240 		warnx("TEST FAILED: i2c_addr_to_string() with zero sized "
241 		    "buffer failed with wrong code %s (0x%x), expected "
242 		    "I2C_ERR_BUF_TOO_SMALL (0x%x)", i2c_errtostr(hdl,
243 		    i2c_err(hdl)), i2c_err(hdl), I2C_ERR_BUF_TOO_SMALL);
244 		ret = false;
245 	} else {
246 		(void) printf("TEST PASSED: i2c_addr_to_string() fails "
247 		    "correctly with zero sized buffer\n");
248 	}
249 
250 	if (i2c_addr_to_string(hdl, &addr, buf, 2)) {
251 		warnx("TEST FAILED: i2c_addr_to_string() with short buffer "
252 		    "unexpectedly worked");
253 		ret = false;
254 	} else if (i2c_err(hdl) != I2C_ERR_BUF_TOO_SMALL) {
255 		warnx("TEST FAILED: i2c_addr_to_string() with short buffer "
256 		    "failed with wrong code %s (0x%x), expected "
257 		    "I2C_ERR_BUF_TOO_SMALL (0x%x)", i2c_errtostr(hdl,
258 		    i2c_err(hdl)), i2c_err(hdl), I2C_ERR_BUF_TOO_SMALL);
259 		ret = false;
260 	} else {
261 		(void) printf("TEST PASSED: i2c_addr_to_string() fails "
262 		    "correctly with short buffer\n");
263 	}
264 
265 	return (ret);
266 }
267 
268 static bool
bad_args(i2c_hdl_t * hdl)269 bad_args(i2c_hdl_t *hdl)
270 {
271 	bool ret = true;
272 	char buf[32] = { 0 };
273 	i2c_addr_t addr;
274 
275 	if (i2c_addr_to_string(hdl, NULL, buf, sizeof (buf))) {
276 		warnx("TEST FAILED: i2c_addr_to_string() with NULL address "
277 		    "unexpectedly worked");
278 		ret = false;
279 	} else if (i2c_err(hdl) != I2C_ERR_BAD_PTR) {
280 		warnx("TEST FAILED: i2c_addr_to_string() with NULL address "
281 		    "failed with wrong code %s (0x%x), expected "
282 		    "I2C_ERR_BAD_PTR (0x%x)", i2c_errtostr(hdl, i2c_err(hdl)),
283 		    i2c_err(hdl), I2C_ERR_BAD_PTR);
284 		ret = false;
285 	} else {
286 		(void) printf("TEST PASSED: i2c_addr_to_string() handles "
287 		    "NULL address correctly\n");
288 	}
289 
290 	if (i2c_addr_to_string(hdl, &addr, NULL, 0)) {
291 		warnx("TEST FAILED: i2c_addr_to_string() with NULL buffer "
292 		    "unexpectedly worked");
293 		ret = false;
294 	} else if (i2c_err(hdl) != I2C_ERR_BAD_PTR) {
295 		warnx("TEST FAILED: i2c_addr_to_string() with NULL buffer "
296 		    "failed with wrong code %s (0x%x), expected "
297 		    "I2C_ERR_BAD_PTR (0x%x)", i2c_errtostr(hdl, i2c_err(hdl)),
298 		    i2c_err(hdl), I2C_ERR_BAD_PTR);
299 		ret = false;
300 	} else {
301 		(void) printf("TEST PASSED: i2c_addr_to_string() handles "
302 		    "NULL buffer correctly\n");
303 	}
304 
305 	if (i2c_addr_parse(hdl, buf, NULL)) {
306 		warnx("TEST FAILED: i2c_addr_parse() with NULL address "
307 		    "unexpectedly worked");
308 		ret = false;
309 	} else if (i2c_err(hdl) != I2C_ERR_BAD_PTR) {
310 		warnx("TEST FAILED: i2c_addr_parse() with NULL address "
311 		    "failed with wrong code %s (0x%x), expected "
312 		    "I2C_ERR_BAD_PTR (0x%x)", i2c_errtostr(hdl, i2c_err(hdl)),
313 		    i2c_err(hdl), I2C_ERR_BAD_PTR);
314 		ret = false;
315 	} else {
316 		(void) printf("TEST PASSED: i2c_addr_parse() handles "
317 		    "NULL address correctly\n");
318 	}
319 
320 	if (i2c_addr_parse(hdl, NULL, &addr)) {
321 		warnx("TEST FAILED: i2c_addr_parse() with NULL string "
322 		    "unexpectedly worked");
323 		ret = false;
324 	} else if (i2c_err(hdl) != I2C_ERR_BAD_PTR) {
325 		warnx("TEST FAILED: i2c_addr_parse() with NULL string "
326 		    "failed with wrong code %s (0x%x), expected "
327 		    "I2C_ERR_BAD_PTR (0x%x)", i2c_errtostr(hdl, i2c_err(hdl)),
328 		    i2c_err(hdl), I2C_ERR_BAD_PTR);
329 		ret = false;
330 	} else {
331 		(void) printf("TEST PASSED: i2c_addr_parse() handles "
332 		    "NULL string correctly\n");
333 	}
334 
335 	return (ret);
336 }
337 
338 static bool
reserved_addrs(i2c_hdl_t * hdl)339 reserved_addrs(i2c_hdl_t *hdl)
340 {
341 	bool ret = true;
342 
343 	for (i2c_rsvd_addr_t ra = I2C_RSVD_ADDR_GEN_CALL;
344 	    ra <= I2C_RSVD_ADDR_HS_3; ra++) {
345 		i2c_addr_t addr = { I2C_ADDR_7BIT, ra };
346 		if (!i2c_addr_reserved(&addr)) {
347 			warnx("TEST FAILED: 7-bit 0x%02x mistakenly thought "
348 			    "as not reserved", ra);
349 			ret = false;
350 		} else {
351 			(void) printf("TEST PASSED: 7-bit 0x%02x is correctly "
352 			    "considered a reserved address\n", ra);
353 		}
354 
355 		addr.ia_type = I2C_ADDR_10BIT;
356 		if (!i2c_addr_reserved(&addr)) {
357 			warnx("TEST FAILED: 10-bit 0x%02x mistakenly thought "
358 			    "as not reserved", ra);
359 			ret = false;
360 		} else {
361 			(void) printf("TEST PASSED: 10-bit 0x%02x is correctly "
362 			    "considered a reserved address\n", ra);
363 		}
364 	}
365 
366 	for (i2c_rsvd_addr_t ra = I2C_RSVD_ADDR_10B_0;
367 	    ra <= I2C_RSVD_ADDR_DID_3; ra++) {
368 		i2c_addr_t addr = { I2C_ADDR_7BIT, ra };
369 		if (!i2c_addr_reserved(&addr)) {
370 			warnx("TEST FAILED: 7-bit 0x%02x mistakenly thought "
371 			    "as not reserved", ra);
372 			ret = false;
373 		} else {
374 			(void) printf("TEST PASSED: 7-bit 0x%02x is correctly "
375 			    "considered a reserved address\n", ra);
376 		}
377 
378 		addr.ia_type = I2C_ADDR_10BIT;
379 		if (!i2c_addr_reserved(&addr)) {
380 			warnx("TEST FAILED: 10-bit 0x%02x mistakenly thought "
381 			    "as not reserved", ra);
382 			ret = false;
383 		} else {
384 			(void) printf("TEST PASSED: 10-bit 0x%02x is correctly "
385 			    "considered a reserved address\n", ra);
386 		}
387 	}
388 
389 	uint16_t unrsvd_addrs[] = { 0x9, 0x17, 0x23, 0x42, 0x70 };
390 	for (size_t i = 0; i < ARRAY_SIZE(unrsvd_addrs); i++) {
391 		i2c_addr_t addr = { I2C_ADDR_7BIT, unrsvd_addrs[i] };
392 		if (i2c_addr_reserved(&addr)) {
393 			warnx("TEST FAILED: 7-bit 0x%02x mistakenly thought "
394 			    "as reserved", unrsvd_addrs[i]);
395 			ret = false;
396 		} else {
397 			(void) printf("TEST PASSED: 7-bit 0x%02x is correctly "
398 			    "not a reserved address\n", unrsvd_addrs[i]);
399 		}
400 
401 		addr.ia_type = I2C_ADDR_10BIT;
402 		if (i2c_addr_reserved(&addr)) {
403 			warnx("TEST FAILED: 10-bit 0x%03x mistakenly thought "
404 			    "as reserved", unrsvd_addrs[i]);
405 			ret = false;
406 		} else {
407 			(void) printf("TEST PASSED: 10-bit 0x%03x is correctly "
408 			    "not a reserved address\n", unrsvd_addrs[i]);
409 		}
410 	}
411 
412 	return (ret);
413 }
414 
415 int
main(void)416 main(void)
417 {
418 	int ret = EXIT_SUCCESS;
419 	i2c_hdl_t *hdl = i2c_init();
420 	if (hdl == NULL) {
421 		err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to create "
422 		    "libi2c handle");
423 	}
424 
425 	if (!valid_addr_roundtrip(hdl)) {
426 		ret = EXIT_FAILURE;
427 	}
428 
429 	if (!invalid_strings(hdl)) {
430 		ret = EXIT_FAILURE;
431 	}
432 
433 	if (!invalid_addrs(hdl)) {
434 		ret = EXIT_FAILURE;
435 	}
436 
437 	if (!short_buffers(hdl)) {
438 		ret = EXIT_FAILURE;
439 	}
440 
441 	if (!bad_args(hdl)) {
442 		ret = EXIT_FAILURE;
443 	}
444 
445 	if (!reserved_addrs(hdl)) {
446 		ret = EXIT_FAILURE;
447 	}
448 
449 	if (ret == EXIT_SUCCESS) {
450 		(void) printf("All tests passed successfully\n");
451 	}
452 	i2c_fini(hdl);
453 	return (ret);
454 }
455