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