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