xref: /illumos-gate/usr/src/lib/libi2c/common/libi2c_device.c (revision 1f0978acc6d6a43e779975f28651c252ffbefaab)
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  * Device addition, removal, and discovery
18  */
19 
20 #include <stdlib.h>
21 #include <strings.h>
22 #include <unistd.h>
23 #include <sys/sysmacros.h>
24 #include <sys/debug.h>
25 #include <fcntl.h>
26 
27 #include "libi2c_impl.h"
28 
29 void
30 i2c_device_add_req_fini(i2c_dev_add_req_t *req)
31 {
32 	nvlist_free(req->add_nvl);
33 	free(req);
34 }
35 
36 bool
37 i2c_device_add_req_init(i2c_port_t *port, i2c_dev_add_req_t **reqp)
38 {
39 	i2c_hdl_t *hdl = port->port_hdl;
40 	i2c_dev_add_req_t *req;
41 
42 	if (reqp == NULL) {
43 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
44 		    "invalid i2c_dev_add_req_t output pointer: %p", reqp));
45 	}
46 
47 	req = calloc(1, sizeof (i2c_dev_add_req_t));
48 	if (req == NULL) {
49 		int e = errno;
50 		return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to allocate "
51 		    "memory for a new i2c_dev_add_req_t"));
52 	}
53 	req->add_port = port;
54 	req->add_need = I2C_DEV_ADD_REQ_FIELD_NAME | I2C_DEV_ADD_REQ_FIELD_ADDR;
55 
56 	int ret = nvlist_alloc(&req->add_nvl, NV_UNIQUE_NAME, 0);
57 	if (!i2c_nvlist_error(hdl, ret, "create a nvlist")) {
58 		free(req);
59 		return (false);
60 	}
61 
62 	*reqp = req;
63 	return (i2c_success(hdl));
64 }
65 
66 bool
67 i2c_device_add_req_set_addr(i2c_dev_add_req_t *req, const i2c_addr_t *addr)
68 {
69 	int ret;
70 	i2c_hdl_t *hdl = req->add_port->port_hdl;
71 
72 	if (addr == NULL) {
73 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
74 		    "invalid i2c_addr_t pointer: %p", addr));
75 	}
76 
77 	if (!i2c_addr_validate(hdl, addr)) {
78 		return (false);
79 	}
80 
81 	ret = nvlist_add_uint16(req->add_nvl, UI2C_IOCTL_NVL_TYPE,
82 	    addr->ia_type);
83 	if (!i2c_nvlist_error(hdl, ret, "insert address type")) {
84 		return (false);
85 	}
86 
87 	ret = nvlist_add_uint16(req->add_nvl, UI2C_IOCTL_NVL_ADDR,
88 	    addr->ia_addr);
89 	if (!i2c_nvlist_error(hdl, ret, "insert address type")) {
90 		return (false);
91 	}
92 
93 	req->add_need &= ~I2C_DEV_ADD_REQ_FIELD_ADDR;
94 	return (i2c_success(hdl));
95 }
96 
97 bool
98 i2c_device_add_req_set_name(i2c_dev_add_req_t *req, const char *name)
99 {
100 	i2c_hdl_t *hdl = req->add_port->port_hdl;
101 
102 	if (!i2c_name_validate(hdl, name, "name")) {
103 		return (false);
104 	}
105 
106 	int ret = nvlist_add_string(req->add_nvl, UI2C_IOCTL_NVL_NAME, name);
107 	if (!i2c_nvlist_error(hdl, ret, "insert name string")) {
108 		return (false);
109 	}
110 
111 	req->add_need &= ~I2C_DEV_ADD_REQ_FIELD_NAME;
112 	return (i2c_success(hdl));
113 }
114 
115 bool
116 i2c_device_add_req_set_compatible(i2c_dev_add_req_t *req, char *const *compat,
117     size_t ncompat)
118 {
119 	i2c_hdl_t *hdl = req->add_port->port_hdl;
120 
121 	/*
122 	 * Treat this as a request to clear the optional compatible information.
123 	 */
124 	if (compat == NULL && ncompat == 0) {
125 		int ret = nvlist_remove(req->add_nvl, UI2C_IOCTL_NVL_COMPAT,
126 		    DATA_TYPE_STRING_ARRAY);
127 		if (ret == 0 || ret == ENOENT) {
128 			return (i2c_success(hdl));
129 		}
130 		return (i2c_error(hdl, I2C_ERR_INTERNAL, ret, "unexpected "
131 		    "internal error while trying to clear compatible[]"));
132 	}
133 
134 	if (compat == NULL) {
135 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
136 		    "invalid compatible pointer: %p", compat));
137 	} else if (ncompat == 0) {
138 		return (i2c_error(hdl, I2C_ERR_COMPAT_LEN_RANGE, 0, "number "
139 		    "of compatible entries cannot be zero when given a "
140 		    "non-NULL pointer (%p)", compat));
141 	} else if (ncompat > UI2C_IOCTL_NVL_NCOMPAT_MAX) {
142 		return (i2c_error(hdl, I2C_ERR_COMPAT_LEN_RANGE, 0, "device "
143 		    "compatible array is too long (%zu), valid range is [1, "
144 		    "%u]", ncompat, UI2C_IOCTL_NVL_NCOMPAT_MAX));
145 	}
146 
147 	for (size_t i = 0; i < ncompat; i++) {
148 		char desc[64];
149 
150 		(void) snprintf(desc, sizeof (desc), "compatible[%u]", i);
151 		if (!i2c_name_validate(hdl, compat[i], desc)) {
152 			return (false);
153 		}
154 	}
155 
156 	int ret = nvlist_add_string_array(req->add_nvl, UI2C_IOCTL_NVL_COMPAT,
157 	    compat, ncompat);
158 	if (!i2c_nvlist_error(hdl, ret, "insert compatible string[]")) {
159 		return (false);
160 	}
161 	return (i2c_success(hdl));
162 }
163 
164 bool
165 i2c_device_add_req_exec(i2c_dev_add_req_t *req)
166 {
167 	i2c_hdl_t *hdl = req->add_port->port_hdl;
168 	size_t pack_size;
169 	char *pack_buf = NULL;
170 	int nvl_ret;
171 	bool ret = false;
172 	ui2c_dev_add_t dev;
173 
174 	if (req->add_need != 0) {
175 		char buf[128];
176 		bool comma = false;
177 
178 		buf[0] = '\0';
179 		if ((req->add_need & I2C_DEV_ADD_REQ_FIELD_ADDR) != 0) {
180 			(void) strlcat(buf, "device address", sizeof (buf));
181 			comma = true;
182 		}
183 
184 		if ((req->add_need & I2C_DEV_ADD_REQ_FIELD_NAME) != 0) {
185 			if (comma) {
186 				(void) strlcat(buf, ",", sizeof (buf));
187 			}
188 			(void) strlcat(buf, "name", sizeof (buf));
189 			comma = true;
190 		}
191 
192 		return (i2c_error(hdl, I2C_ERR_ADD_DEV_REQ_MISSING_FIELDS, 0,
193 		    "cannot execute add device request due to missing fields: "
194 		    "%s", buf));
195 	}
196 
197 	nvl_ret = nvlist_size(req->add_nvl, &pack_size, NV_ENCODE_NATIVE);
198 	if (!i2c_nvlist_error(hdl, nvl_ret, "determine packed nvlist size")) {
199 		goto out;
200 	}
201 
202 	pack_buf = malloc(pack_size);
203 	if (pack_buf == NULL) {
204 		ret = i2c_error(hdl, I2C_ERR_NO_MEM, errno, "failed to "
205 		    "allocate %zu bytes for packed request nvlist", pack_size);
206 		goto out;
207 	}
208 
209 	nvl_ret = nvlist_pack(req->add_nvl, &pack_buf, &pack_size,
210 	    NV_ENCODE_NATIVE, 0);
211 	if (!i2c_nvlist_error(hdl, nvl_ret, "pack request nvlist")) {
212 		goto out;
213 	}
214 
215 	(void) memset(&dev, 0, sizeof (ui2c_dev_add_t));
216 	dev.uda_nvl = (uintptr_t)pack_buf;
217 	dev.uda_nvl_len = pack_size;
218 
219 	if (ioctl(req->add_port->port_fd, UI2C_IOCTL_DEVICE_ADD, &dev) != 0) {
220 		int e = errno;
221 		ret = i2c_ioctl_syserror(hdl, e, "add device request");
222 		goto out;
223 	}
224 
225 	if (dev.uda_error.i2c_error != I2C_CORE_E_OK) {
226 		ret = i2c_ioctl_error(hdl, &dev.uda_error,
227 		    "add device request");
228 		goto out;
229 	}
230 
231 	ret = i2c_success(hdl);
232 out:
233 	free(pack_buf);
234 	return (ret);
235 }
236 
237 bool
238 i2c_device_rem(i2c_port_t *port, const i2c_addr_t *addr)
239 {
240 	ui2c_dev_rem_t rem;
241 	i2c_hdl_t *hdl = port->port_hdl;
242 
243 	if (addr == NULL) {
244 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
245 		    "invalid i2c_addr_t pointer: %p", addr));
246 	}
247 
248 	if (!i2c_addr_validate(hdl, addr)) {
249 		return (false);
250 	}
251 
252 	(void) memset(&rem, 0, sizeof (ui2c_dev_rem_t));
253 	rem.udr_addr = *addr;
254 
255 	if (ioctl(port->port_fd, UI2C_IOCTL_DEVICE_REMOVE, &rem) != 0) {
256 		int e = errno;
257 		return (i2c_ioctl_syserror(hdl, e, "remove device request"));
258 	}
259 
260 	if (rem.udr_error.i2c_error != I2C_CORE_E_OK) {
261 		return (i2c_ioctl_error(hdl, &rem.udr_error,
262 		    "remove device request"));
263 	}
264 
265 	return (i2c_success(hdl));
266 }
267 
268 void
269 i2c_device_discover_fini(i2c_dev_iter_t *iter)
270 {
271 	if (iter == NULL)
272 		return;
273 
274 	i2c_port_discover_fini(iter->di_iter);
275 	free(iter);
276 }
277 
278 /*
279  * Fill information about the device's under this single port per the notes in
280  * i2c_device_discover_step().
281  */
282 static bool
283 i2c_device_discover_port(i2c_hdl_t *hdl, dev_port_info_t *dpi)
284 {
285 	for (di_minor_t m = di_minor_next(dpi->dpi_port, DI_MINOR_NIL);
286 	    m != DI_MINOR_NIL; m = di_minor_next(dpi->dpi_port, m)) {
287 		i2c_addr_t addr;
288 
289 		if (strcmp(di_minor_nodetype(m), DDI_NT_I2C_DEV) != 0)
290 			continue;
291 
292 		if (!i2c_kernel_address_parse(hdl, di_minor_name(m), &addr)) {
293 			return (false);
294 		}
295 
296 		if (addr.ia_type == I2C_ADDR_7BIT) {
297 			dpi->dpi_7b[addr.ia_addr].dmi_minor = m;
298 		} else {
299 			dpi->dpi_10b[addr.ia_addr].dmi_minor = m;
300 		}
301 	}
302 
303 	/*
304 	 * Now go through all of our children and try to map them to something
305 	 * we know.
306 	 */
307 	for (di_node_t di = di_child_node(dpi->dpi_port); di != DI_NODE_NIL;
308 	    di = di_sibling_node(di)) {
309 		i2c_addr_t addr;
310 
311 		if (i2c_node_type(di) != I2C_NODE_T_DEV) {
312 			continue;
313 		}
314 
315 		/*
316 		 * For the purposes of iteration we order devices by the first
317 		 * address that they have in their regs[] array. We assume that
318 		 * this will be the primary one. We will skip cases where we
319 		 * have a minor but not a devinfo in this list.
320 		 */
321 		if (!i2c_reg_to_addr(hdl, di, &addr, 0)) {
322 			return (false);
323 		}
324 
325 		if (addr.ia_type == I2C_ADDR_7BIT) {
326 			dpi->dpi_7b[addr.ia_addr].dmi_node = di;
327 		} else {
328 			dpi->dpi_10b[addr.ia_addr].dmi_node = di;
329 		}
330 	}
331 
332 	return (true);
333 }
334 
335 static bool
336 i2c_device_discover_one(i2c_dev_iter_t *iter, dev_map_info_t *map)
337 {
338 	iter->di_disc.idd_map = map;
339 	iter->di_disc.idd_port = &iter->di_info;
340 	if (!i2c_node_to_path(iter->di_hdl, map->dmi_node,
341 	    iter->di_disc.idd_path, sizeof (iter->di_disc.idd_path))) {
342 		return (false);
343 	}
344 
345 	return (true);
346 }
347 
348 /*
349  * Device discovery starts by walking the last of I2C ports. After that we
350  * proceed to try to walk all of the immediate children. The port has a list of
351  * all of the minors that are I2C devices. So we first gather that up and marry
352  * it up to the actual dev_info nodes in the snapshot. The minor node will be
353  * created while we're creating the child node and we should only see one if we
354  * see the other. By walking the minor node list, this gives us a way to ignore
355  * dev info nodes that end up under the port that aren't actually in-band
356  * devices (e.g. a non-in-band mux).
357  */
358 i2c_iter_t
359 i2c_device_discover_step(i2c_dev_iter_t *iter, const i2c_dev_disc_t **discp)
360 {
361 	for (;;) {
362 		/*
363 		 * First check if we're already done or if we're taking a lap
364 		 * because we've processed all ports.
365 		 */
366 		if (iter->di_done) {
367 			return (I2C_ITER_DONE);
368 		}
369 
370 		if (iter->di_curport == NULL) {
371 			i2c_iter_t iret = i2c_port_discover_step(iter->di_iter,
372 			    &iter->di_curport);
373 			if (iret == I2C_ITER_DONE) {
374 				iter->di_done = true;
375 				return (I2C_ITER_DONE);
376 			} else if (iret != I2C_ITER_VALID) {
377 				return (iret);
378 			}
379 
380 			memset(&iter->di_info, 0, sizeof (dev_port_info_t));
381 			iter->di_info.dpi_port =
382 			    i2c_port_disc_devi(iter->di_curport);
383 		}
384 
385 		/*
386 		 * See if we have minor info for this port yet. If not, we go
387 		 * and build it.
388 		 */
389 		dev_port_info_t *pi = &iter->di_info;
390 		if (!pi->dpi_scanned) {
391 			pi->dpi_scanned = true;
392 			if (!i2c_device_discover_port(iter->di_hdl, pi)) {
393 				return (I2C_ITER_ERROR);
394 			}
395 		}
396 
397 		/*
398 		 * Is this port done, if so move onto the next.
399 		 */
400 		if (pi->dpi_7bit_done && pi->dpi_10bit_done) {
401 			iter->di_curport = NULL;
402 			continue;
403 		}
404 
405 		if (!pi->dpi_7bit_done) {
406 			while (pi->dpi_curidx < ARRAY_SIZE(pi->dpi_7b)) {
407 				dev_map_info_t *map =
408 				    &pi->dpi_7b[pi->dpi_curidx];
409 				pi->dpi_curidx++;
410 				if (map->dmi_minor == DI_MINOR_NIL ||
411 				    map->dmi_node == DI_NODE_NIL) {
412 					continue;
413 				}
414 
415 				if (i2c_device_discover_one(iter, map)) {
416 					*discp = &iter->di_disc;
417 					return (I2C_ITER_VALID);
418 				} else {
419 					return (I2C_ITER_ERROR);
420 				}
421 			}
422 			pi->dpi_7bit_done = true;
423 			pi->dpi_curidx = 0;
424 		}
425 
426 
427 		if (!pi->dpi_10bit_done) {
428 			while (pi->dpi_curidx < ARRAY_SIZE(pi->dpi_10b)) {
429 				dev_map_info_t *map =
430 				    &pi->dpi_10b[pi->dpi_curidx];
431 				pi->dpi_curidx++;
432 				if (map->dmi_minor == DI_MINOR_NIL ||
433 				    map->dmi_node == DI_NODE_NIL) {
434 					continue;
435 				}
436 
437 				if (i2c_device_discover_one(iter, map)) {
438 					*discp = &iter->di_disc;
439 					return (I2C_ITER_VALID);
440 				} else {
441 					return (I2C_ITER_ERROR);
442 				}
443 			}
444 			pi->dpi_10bit_done = true;
445 			pi->dpi_curidx = 0;
446 		}
447 	}
448 
449 	return (I2C_ITER_ERROR);
450 }
451 
452 bool
453 i2c_device_discover_init(i2c_hdl_t *hdl, i2c_dev_iter_t **iterp)
454 {
455 	i2c_dev_iter_t *iter;
456 
457 	if (iterp == NULL) {
458 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
459 		    "invalid i2c_dev_iter_t output pointer: %p", iterp));
460 	}
461 
462 	iter = calloc(1, sizeof (i2c_dev_iter_t));
463 	if (iter == NULL) {
464 		int e = errno;
465 		return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to allocate "
466 		    "memory for a new i2c_dev_iter_t"));
467 	}
468 
469 	iter->di_hdl = hdl;
470 	iter->di_done = false;
471 	if (!i2c_port_discover_init(hdl, &iter->di_iter)) {
472 		free(iter);
473 		return (false);
474 	}
475 
476 	*iterp = iter;
477 	return (i2c_success(hdl));
478 }
479 
480 bool
481 i2c_device_discover(i2c_hdl_t *hdl, i2c_dev_disc_f func, void *arg)
482 {
483 	i2c_dev_iter_t *iter;
484 	const i2c_dev_disc_t *disc;
485 	i2c_iter_t ret;
486 
487 	if (func == NULL) {
488 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
489 		    "invalid i2c_dev_disc_f function pointer: %p", func));
490 	}
491 
492 	if (!i2c_device_discover_init(hdl, &iter)) {
493 		return (false);
494 	}
495 
496 	while ((ret = i2c_device_discover_step(iter, &disc)) ==
497 	    I2C_ITER_VALID) {
498 		if (!func(hdl, disc, arg))
499 			break;
500 	}
501 
502 	i2c_device_discover_fini(iter);
503 	if (ret == I2C_ITER_ERROR) {
504 		return (false);
505 	}
506 
507 	return (i2c_success(hdl));
508 }
509 
510 const char *
511 i2c_device_disc_name(const i2c_dev_disc_t *disc)
512 {
513 	return (di_node_name(disc->idd_map->dmi_node));
514 }
515 
516 di_node_t
517 i2c_device_disc_devi(const i2c_dev_disc_t *disc)
518 {
519 	return (disc->idd_map->dmi_node);
520 }
521 
522 di_minor_t
523 i2c_device_disc_devctl(const i2c_dev_disc_t *disc)
524 {
525 	return (disc->idd_map->dmi_minor);
526 }
527 
528 const char *
529 i2c_device_disc_path(const i2c_dev_disc_t *disc)
530 {
531 	return (disc->idd_path);
532 }
533 
534 void
535 i2c_device_info_free(i2c_dev_info_t *info)
536 {
537 	free(info->dinfo_name);
538 	free(info->dinfo_driver);
539 	free(info->dinfo_addrs);
540 	di_devfs_path_free(info->dinfo_minor);
541 	free(info);
542 }
543 
544 const char *
545 i2c_device_info_path(const i2c_dev_info_t *info)
546 {
547 	return (info->dinfo_path);
548 }
549 
550 const char *
551 i2c_device_info_name(const i2c_dev_info_t *info)
552 {
553 	return (info->dinfo_name);
554 }
555 
556 const char *
557 i2c_device_info_driver(const i2c_dev_info_t *info)
558 {
559 	return (info->dinfo_driver);
560 }
561 
562 int
563 i2c_device_info_instance(const i2c_dev_info_t *info)
564 {
565 	return (info->dinfo_inst);
566 }
567 
568 uint32_t
569 i2c_device_info_naddrs(const i2c_dev_info_t *info)
570 {
571 	return (info->dinfo_naddrs);
572 }
573 
574 const i2c_addr_t *
575 i2c_device_info_addr_primary(const i2c_dev_info_t *info)
576 {
577 	return (&info->dinfo_info.udi_primary);
578 }
579 
580 const i2c_addr_t *
581 i2c_device_info_addr(const i2c_dev_info_t *info, uint32_t n)
582 {
583 	if (n >= info->dinfo_naddrs) {
584 		return (NULL);
585 	}
586 
587 	return (&info->dinfo_addrs[n]);
588 }
589 
590 i2c_addr_source_t
591 i2c_device_info_addr_source(const i2c_dev_info_t *info, uint32_t n)
592 {
593 	if (n >= info->dinfo_naddrs) {
594 		return (0);
595 	}
596 
597 	return (info->dinfo_info.udi_7b[info->dinfo_addrs[n].ia_addr]);
598 }
599 
600 bool
601 i2c_device_info_snap(i2c_hdl_t *hdl, di_node_t dn, i2c_dev_info_t **infop)
602 {
603 	di_minor_t minor;
604 	i2c_dev_info_t *info;
605 
606 	if (dn == DI_NODE_NIL) {
607 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
608 		    "invalid di_node_t: %p", dn));
609 	}
610 
611 	if (infop == NULL) {
612 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
613 		    "invalid i2c_dev_info_t output pointer: %p", infop));
614 	}
615 
616 	if (!i2c_node_is_type(dn, I2C_NODE_T_DEV)) {
617 		return (i2c_error(hdl, I2C_ERR_BAD_DEVI, 0, "devi %s@%s is "
618 		    "not an i2c device", di_node_name(dn), di_bus_addr(dn)));
619 	}
620 
621 	minor = i2c_node_minor(dn);
622 	if (minor == DI_MINOR_NIL) {
623 		return (i2c_error(hdl, I2C_ERR_BAD_DEVI, 0, "devi %s@%s is "
624 		    "not an i2c device: failed to find device minor",
625 		    di_node_name(dn), di_bus_addr(dn)));
626 	}
627 
628 	info = calloc(1, sizeof (i2c_dev_info_t));
629 	info->dinfo_name = strdup(di_node_name(dn));
630 	if (info->dinfo_name == NULL) {
631 		int e = errno;
632 		i2c_device_info_free(info);
633 		return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to duplicate "
634 		    "device node name"));
635 	}
636 
637 	if (!i2c_node_to_path(hdl, dn, info->dinfo_path,
638 	    sizeof (info->dinfo_path))) {
639 		i2c_device_info_free(info);
640 		return (false);
641 	}
642 
643 	if (di_driver_name(dn) != NULL) {
644 		info->dinfo_driver = strdup(di_driver_name(dn));
645 		if (info->dinfo_driver == NULL) {
646 			int e = errno;
647 			i2c_device_info_free(info);
648 			return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to "
649 			    "duplicate device driver name"));
650 		}
651 	} else {
652 		info->dinfo_driver = NULL;
653 	}
654 	info->dinfo_inst = di_instance(dn);
655 	info->dinfo_minor = di_devfs_minor_path(minor);
656 	if (info->dinfo_minor == NULL) {
657 		int e = errno;
658 		i2c_device_info_free(info);
659 		return (i2c_error(hdl, I2C_ERR_LIBDEVINFO, e, "failed to "
660 		    "obtain devices's devfs path: %s", strerrordesc_np(e)));
661 	}
662 
663 	int fd = openat(hdl->ih_devfd, info->dinfo_minor + 1, O_RDONLY);
664 	if (fd < 0) {
665 		int e = errno;
666 		(void) i2c_error(hdl, I2C_ERR_OPEN_DEV, e, "failed to open "
667 		    "device path /devices%s: %s", info->dinfo_minor,
668 		    strerrordesc_np(e));
669 		i2c_device_info_free(info);
670 		return (false);
671 	}
672 
673 	if (ioctl(fd, UI2C_IOCTL_DEV_INFO, &info->dinfo_info) != 0) {
674 		int e = errno;
675 		i2c_device_info_free(info);
676 		return (i2c_ioctl_syserror(hdl, e, "device information "
677 		    "request"));
678 	}
679 
680 	(void) close(fd);
681 	if (info->dinfo_info.udi_error.i2c_error != I2C_CORE_E_OK) {
682 		i2c_device_info_free(info);
683 		return (i2c_ioctl_error(hdl, &info->dinfo_info.udi_error,
684 		    "device information request"));
685 	}
686 
687 	for (uint32_t i = 0; i < ARRAY_SIZE(info->dinfo_info.udi_7b); i++) {
688 		if (info->dinfo_info.udi_7b[i] != 0) {
689 			info->dinfo_naddrs++;
690 		}
691 	}
692 
693 	VERIFY3U(info->dinfo_naddrs, >, 0);
694 	info->dinfo_addrs = calloc(info->dinfo_naddrs, sizeof (i2c_addr_t));
695 	if (info->dinfo_addrs == NULL) {
696 		int e = errno;
697 		(void) i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to allocate "
698 		    "memory for %u I2C addresses", info->dinfo_naddrs);
699 		i2c_device_info_free(info);
700 		return (false);
701 	}
702 
703 	for (uint32_t i = 0, idx = 0; i < ARRAY_SIZE(info->dinfo_info.udi_7b);
704 	    i++) {
705 		if (info->dinfo_info.udi_7b[i] != 0) {
706 			info->dinfo_addrs[idx].ia_type = I2C_ADDR_7BIT;
707 			info->dinfo_addrs[idx].ia_addr = i;
708 			idx++;
709 		}
710 	}
711 
712 	*infop = info;
713 	return (i2c_success(hdl));
714 }
715 
716 /*
717  * Get information about a device specified by path. In addition, return its
718  * port. If nodev_ok is set to true, then our caller is fine with returning
719  * success, but without the device information. This would happen if the path
720  * ended at a port.
721  */
722 bool
723 i2c_port_dev_init_by_path(i2c_hdl_t *hdl, const char *path, bool nodev_ok,
724     i2c_port_t **portp, i2c_dev_info_t **infop)
725 {
726 	i2c_node_type_t type;
727 	di_node_t dn, root, port_dn, dev_dn;
728 	if (path == NULL) {
729 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
730 		    "invalid i2c path: %p", path));
731 	}
732 
733 	if (portp == NULL) {
734 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
735 		    "invalid i2c_port_t output pointer: %p", infop));
736 	}
737 	*portp = NULL;
738 
739 	if (infop == NULL) {
740 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
741 		    "invalid i2c_dev_info_t output pointer: %p", infop));
742 	}
743 	*infop = NULL;
744 
745 	root = di_init("/", DINFOCPYALL);
746 	if (root == DI_NODE_NIL) {
747 		int e = errno;
748 		return (i2c_error(hdl, I2C_ERR_LIBDEVINFO, e, "failed to "
749 		    "initialize devinfo snapshot: %s", strerrordesc_np(e)));
750 	}
751 
752 	if (!i2c_path_parse(hdl, path, root, &dn, &type, I2C_ERR_BAD_DEVICE)) {
753 		di_fini(root);
754 		return (false);
755 	}
756 
757 	switch (type) {
758 	case I2C_NODE_T_DEV:
759 		dev_dn = dn;
760 		port_dn = di_parent_node(dev_dn);
761 		break;
762 	case I2C_NODE_T_PORT:
763 		if (!nodev_ok) {
764 			return (i2c_error(hdl, I2C_ERR_BAD_DEVICE, 0, "parsed "
765 			    "I2C path %s did not end at a device", path));
766 		}
767 		dev_dn = DI_NODE_NIL;
768 		port_dn = dn;
769 		break;
770 	default:
771 		di_fini(root);
772 		return (i2c_error(hdl, I2C_ERR_BAD_DEVICE, 0, "parsed I2C "
773 		    "path %s did not end at a device (or port)", path));
774 	}
775 
776 	if (!i2c_port_init(hdl, port_dn, portp)) {
777 		di_fini(root);
778 		return (false);
779 	}
780 
781 	if (dev_dn != DI_NODE_NIL) {
782 		if (!i2c_device_info_snap(hdl, dev_dn, infop)) {
783 			di_fini(root);
784 			i2c_port_fini(*portp);
785 			return (false);
786 		}
787 	}
788 
789 	return (true);
790 }
791