xref: /illumos-gate/usr/src/lib/libi2c/common/libi2c_ctrl.c (revision 32002227574cf0a435dc03de622191ca53724f0a)
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  * I2C Controller related functions.
18  */
19 
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 
27 #include "libi2c_impl.h"
28 
29 void
i2c_ctrl_discover_fini(i2c_ctrl_iter_t * iter)30 i2c_ctrl_discover_fini(i2c_ctrl_iter_t *iter)
31 {
32 	if (iter == NULL)
33 		return;
34 
35 	di_fini(iter->ci_root);
36 	free(iter);
37 }
38 
39 i2c_iter_t
i2c_ctrl_discover_step(i2c_ctrl_iter_t * iter,const i2c_ctrl_disc_t ** discp)40 i2c_ctrl_discover_step(i2c_ctrl_iter_t *iter, const i2c_ctrl_disc_t **discp)
41 {
42 	*discp = NULL;
43 
44 	if (iter->ci_done) {
45 		return (I2C_ITER_DONE);
46 	}
47 
48 	for (;;) {
49 		if (iter->ci_cur == DI_NODE_NIL) {
50 			iter->ci_cur = di_drv_first_node(I2C_NEX_DRV,
51 			    iter->ci_root);
52 		} else {
53 			iter->ci_cur = di_drv_next_node(iter->ci_cur);
54 		}
55 
56 		if (iter->ci_cur == DI_NODE_NIL) {
57 			iter->ci_done = true;
58 			return (I2C_ITER_DONE);
59 		}
60 
61 		if (!i2c_node_is_type(iter->ci_cur, I2C_NODE_T_CTRL)) {
62 			continue;
63 		}
64 
65 		iter->ci_disc.icd_devi = iter->ci_cur;
66 		iter->ci_disc.icd_minor = i2c_node_minor(iter->ci_cur);
67 		if (iter->ci_disc.icd_minor == DI_MINOR_NIL) {
68 			continue;
69 		}
70 
71 		*discp = &iter->ci_disc;
72 		return (I2C_ITER_VALID);
73 	}
74 
75 	return (I2C_ITER_DONE);
76 }
77 
78 bool
i2c_ctrl_discover_init(i2c_hdl_t * hdl,i2c_ctrl_iter_t ** iterp)79 i2c_ctrl_discover_init(i2c_hdl_t *hdl, i2c_ctrl_iter_t **iterp)
80 {
81 	i2c_ctrl_iter_t *iter;
82 
83 	if (iterp == NULL) {
84 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
85 		    "invalid i2c_ctrl_iter_t output pointer: %p", iterp));
86 	}
87 
88 	iter = calloc(1, sizeof (i2c_ctrl_iter_t));
89 	if (iter == NULL) {
90 		int e = errno;
91 		return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to allocate "
92 		    "memory for a new i2c_ctrl_iter_t"));
93 	}
94 
95 	iter->ci_hdl = hdl;
96 	iter->ci_root = di_init("/", DINFOCPYALL);
97 	if (iter->ci_root == NULL) {
98 		int e = errno;
99 		i2c_ctrl_discover_fini(iter);
100 		return (i2c_error(hdl, I2C_ERR_LIBDEVINFO, e, "failed to "
101 		    "initialize devinfo snapshot: %s", strerrordesc_np(e)));
102 	}
103 	iter->ci_done = false;
104 	iter->ci_cur = DI_NODE_NIL;
105 
106 	*iterp = iter;
107 	return (i2c_success(hdl));
108 }
109 
110 bool
i2c_ctrl_discover(i2c_hdl_t * hdl,i2c_ctrl_disc_f func,void * arg)111 i2c_ctrl_discover(i2c_hdl_t *hdl, i2c_ctrl_disc_f func, void *arg)
112 {
113 	i2c_ctrl_iter_t *iter;
114 	const i2c_ctrl_disc_t *disc;
115 	i2c_iter_t ret;
116 
117 	if (func == NULL) {
118 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
119 		    "invalid i2c_ctrl_disc_f function pointer: %p", func));
120 	}
121 
122 	if (!i2c_ctrl_discover_init(hdl, &iter)) {
123 		return (false);
124 	}
125 
126 	while ((ret = i2c_ctrl_discover_step(iter, &disc)) == I2C_ITER_VALID) {
127 		if (!func(hdl, disc, arg))
128 			break;
129 	}
130 
131 	i2c_ctrl_discover_fini(iter);
132 	if (ret == I2C_ITER_ERROR) {
133 		return (false);
134 	}
135 
136 	return (i2c_success(hdl));
137 }
138 
139 di_node_t
i2c_ctrl_disc_devi(const i2c_ctrl_disc_t * discp)140 i2c_ctrl_disc_devi(const i2c_ctrl_disc_t *discp)
141 {
142 	return (discp->icd_devi);
143 }
144 
145 di_minor_t
i2c_ctrl_disc_minor(const i2c_ctrl_disc_t * discp)146 i2c_ctrl_disc_minor(const i2c_ctrl_disc_t *discp)
147 {
148 	return (discp->icd_minor);
149 }
150 
151 void
i2c_ctrl_fini(i2c_ctrl_t * ctrl)152 i2c_ctrl_fini(i2c_ctrl_t *ctrl)
153 {
154 	if (ctrl == NULL) {
155 		return;
156 	}
157 
158 	if (ctrl->ctrl_fd >= 0) {
159 		(void) close(ctrl->ctrl_fd);
160 	}
161 
162 	di_devfs_path_free(ctrl->ctrl_minor);
163 	di_devfs_path_free(ctrl->ctrl_path);
164 	free(ctrl->ctrl_name);
165 	free(ctrl);
166 }
167 
168 bool
i2c_ctrl_init(i2c_hdl_t * hdl,di_node_t di,i2c_ctrl_t ** ctrlp)169 i2c_ctrl_init(i2c_hdl_t *hdl, di_node_t di, i2c_ctrl_t **ctrlp)
170 {
171 	di_minor_t minor;
172 	i2c_ctrl_t *ctrl;
173 	ui2c_ctrl_nprops_t nprops;
174 
175 	if (di == DI_NODE_NIL) {
176 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
177 		    "invalid di_node_t: %p", di));
178 	}
179 
180 	if (ctrlp == NULL) {
181 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
182 		    "invalid i2c_ctrl_t output pointer: %p", ctrlp));
183 	}
184 
185 	if (!i2c_node_is_type(di, I2C_NODE_T_CTRL)) {
186 		return (i2c_error(hdl, I2C_ERR_BAD_DEVI, 0, "devi %s@%s isn't "
187 		    "an i2c controller", di_node_name(di), di_bus_addr(di)));
188 	}
189 
190 	/*
191 	 * See if we can find a minor node that corresponds to a controller
192 	 * nexus.
193 	 */
194 	minor = i2c_node_minor(di);
195 	if (minor == DI_MINOR_NIL) {
196 		return (i2c_error(hdl, I2C_ERR_BAD_DEVI, 0, "devi %s@%s is "
197 		    "not an i2c controller: failed to find controller minor",
198 		    di_node_name(di), di_bus_addr(di)));
199 	}
200 
201 	ctrl = calloc(1, sizeof (i2c_ctrl_t));
202 	if (ctrl == NULL) {
203 		int e = errno;
204 		return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to allocate "
205 		    "memory for a new i2c_ctrl_t"));
206 	}
207 
208 	ctrl->ctrl_fd = -1;
209 	ctrl->ctrl_hdl = hdl;
210 	ctrl->ctrl_inst = di_instance(di);
211 	ctrl->ctrl_name = strdup(di_bus_addr(di));
212 	if (ctrl->ctrl_name == NULL) {
213 		int e = errno;
214 		i2c_ctrl_fini(ctrl);
215 		return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to duplicate "
216 		    "controller bus address"));
217 	}
218 
219 	ctrl->ctrl_path = di_devfs_path(di);
220 	if (ctrl->ctrl_path == NULL) {
221 		int e = errno;
222 		i2c_ctrl_fini(ctrl);
223 		return (i2c_error(hdl, I2C_ERR_LIBDEVINFO, e, "failed to "
224 		    "obtain controller's devfs path: %s", strerrordesc_np(e)));
225 	}
226 
227 	ctrl->ctrl_minor = di_devfs_minor_path(minor);
228 	if (ctrl->ctrl_minor == NULL) {
229 		int e = errno;
230 		i2c_ctrl_fini(ctrl);
231 		return (i2c_error(hdl, I2C_ERR_LIBDEVINFO, e, "failed to "
232 		    "obtain controller's minor path: %s", strerrordesc_np(e)));
233 	}
234 
235 	ctrl->ctrl_fd = openat(hdl->ih_devfd, ctrl->ctrl_minor + 1, O_RDWR);
236 	if (ctrl->ctrl_fd < 0) {
237 		int e = errno;
238 		(void) i2c_error(hdl, I2C_ERR_OPEN_DEV, e, "failed to open "
239 		    "device path '/devices%s: %s", ctrl->ctrl_minor,
240 		    strerrordesc_np(e));
241 		i2c_ctrl_fini(ctrl);
242 		return (false);
243 	}
244 
245 	if (ioctl(ctrl->ctrl_fd, UI2C_IOCTL_CTRL_NPROPS, &nprops) != 0) {
246 		int e = errno;
247 		i2c_ctrl_fini(ctrl);
248 		return (i2c_ioctl_syserror(hdl, e, "controller nprops "
249 		    "request"));
250 	}
251 	ctrl->ctrl_nstd = nprops.ucp_nstd;
252 	ctrl->ctrl_npriv = nprops.ucp_npriv;
253 
254 	*ctrlp = ctrl;
255 	return (i2c_success(hdl));
256 }
257 
258 bool
i2c_ctrl_init_by_path(i2c_hdl_t * hdl,const char * name,i2c_ctrl_t ** ctrlp)259 i2c_ctrl_init_by_path(i2c_hdl_t *hdl, const char *name, i2c_ctrl_t **ctrlp)
260 {
261 	i2c_node_type_t type;
262 	di_node_t dn, root;
263 
264 	if (name == NULL) {
265 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
266 		    "invalid i2c controller name: %p", name));
267 	}
268 
269 	if (ctrlp == NULL) {
270 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
271 		    "invalid i2c_ctrl_t output pointer: %p", ctrlp));
272 	}
273 
274 	root = di_init("/", DINFOCPYALL);
275 	if (root == DI_NODE_NIL) {
276 		int e = errno;
277 		return (i2c_error(hdl, I2C_ERR_LIBDEVINFO, e, "failed to "
278 		    "initialize devinfo snapshot: %s", strerrordesc_np(e)));
279 	}
280 
281 	if (!i2c_path_parse(hdl, name, root, &dn, &type,
282 	    I2C_ERR_BAD_CONTROLLER)) {
283 		di_fini(root);
284 		return (false);
285 	}
286 
287 	if (type != I2C_NODE_T_CTRL) {
288 		di_fini(root);
289 		return (i2c_error(hdl, I2C_ERR_BAD_CONTROLLER, 0, "parsed I2C "
290 		    "path %s did not end at a controller", name));
291 	}
292 
293 	bool ret = i2c_ctrl_init(hdl, dn, ctrlp);
294 	di_fini(root);
295 	return (ret);
296 }
297 
298 const char *
i2c_ctrl_name(i2c_ctrl_t * ctrl)299 i2c_ctrl_name(i2c_ctrl_t *ctrl)
300 {
301 	return (ctrl->ctrl_name);
302 }
303 
304 int32_t
i2c_ctrl_instance(i2c_ctrl_t * ctrl)305 i2c_ctrl_instance(i2c_ctrl_t *ctrl)
306 {
307 	return (ctrl->ctrl_inst);
308 }
309 
310 const char *
i2c_ctrl_path(i2c_ctrl_t * ctrl)311 i2c_ctrl_path(i2c_ctrl_t *ctrl)
312 {
313 	return (ctrl->ctrl_path);
314 }
315 
316 uint32_t
i2c_ctrl_nprops(i2c_ctrl_t * ctrl)317 i2c_ctrl_nprops(i2c_ctrl_t *ctrl)
318 {
319 	/*
320 	 * Currently we only have standard properties. If we have
321 	 * controller-specific properties in the future then those should be
322 	 * added to this.
323 	 */
324 	return (ctrl->ctrl_nstd);
325 }
326 
327 const char *
i2c_prop_info_name(i2c_prop_info_t * info)328 i2c_prop_info_name(i2c_prop_info_t *info)
329 {
330 	return (info->pinfo_info.upi_name);
331 }
332 
333 i2c_prop_t
i2c_prop_info_id(i2c_prop_info_t * info)334 i2c_prop_info_id(i2c_prop_info_t *info)
335 {
336 	return (info->pinfo_info.upi_prop);
337 }
338 
339 i2c_prop_type_t
i2c_prop_info_type(i2c_prop_info_t * info)340 i2c_prop_info_type(i2c_prop_info_t *info)
341 {
342 	return (info->pinfo_info.upi_type);
343 }
344 
345 bool
i2c_prop_info_sup(i2c_prop_info_t * info)346 i2c_prop_info_sup(i2c_prop_info_t *info)
347 {
348 	return (info->pinfo_sup);
349 }
350 
351 i2c_prop_perm_t
i2c_prop_info_perm(i2c_prop_info_t * info)352 i2c_prop_info_perm(i2c_prop_info_t *info)
353 {
354 	return (info->pinfo_info.upi_perm);
355 }
356 
357 bool
i2c_prop_info_def_u32(i2c_prop_info_t * info,uint32_t * defp)358 i2c_prop_info_def_u32(i2c_prop_info_t *info, uint32_t *defp)
359 {
360 	i2c_hdl_t *hdl = info->pinfo_hdl;
361 
362 	if (defp == NULL) {
363 
364 	}
365 
366 	if (!info->pinfo_sup) {
367 		return (i2c_error(hdl, I2C_ERR_PROP_UNSUP, 0, "default value "
368 		    "is unavailable because property %s is not supported by "
369 		    "the controller", info->pinfo_info.upi_name));
370 	}
371 
372 	switch (info->pinfo_info.upi_type) {
373 	case I2C_PROP_TYPE_U32:
374 	case I2C_PROP_TYPE_BIT32:
375 		if (info->pinfo_info.upi_def_len != sizeof (uint32_t)) {
376 			return (i2c_error(hdl, I2C_ERR_PROP_TYPE_MISMATCH, 0,
377 			    "property %s does not have a default value",
378 			    info->pinfo_info.upi_name));
379 		}
380 		(void) memcpy(defp, info->pinfo_info.upi_def,
381 		    sizeof (uint32_t));
382 		break;
383 	default:
384 		return (i2c_error(hdl, I2C_ERR_PROP_TYPE_MISMATCH, 0,
385 		    "property %s default value does not have a 32-bit "
386 		    "integer type (found type 0x%x)", info->pinfo_info.upi_name,
387 		    info->pinfo_info.upi_type));
388 		break;
389 	}
390 
391 	return (i2c_success(hdl));
392 }
393 
394 const i2c_prop_range_t *
i2c_prop_info_pos(i2c_prop_info_t * info)395 i2c_prop_info_pos(i2c_prop_info_t *info)
396 {
397 	if (!info->pinfo_sup || info->pinfo_info.upi_pos_len <
398 	    sizeof (i2c_prop_range_t)) {
399 		return (NULL);
400 	}
401 
402 	return ((i2c_prop_range_t *)info->pinfo_info.upi_pos);
403 }
404 
405 void
i2c_prop_info_free(i2c_prop_info_t * info)406 i2c_prop_info_free(i2c_prop_info_t *info)
407 {
408 	free(info);
409 }
410 
411 bool
i2c_prop_info(i2c_ctrl_t * ctrl,i2c_prop_t prop,i2c_prop_info_t ** infop)412 i2c_prop_info(i2c_ctrl_t *ctrl, i2c_prop_t prop, i2c_prop_info_t **infop)
413 {
414 	i2c_hdl_t *hdl = ctrl->ctrl_hdl;
415 	i2c_prop_info_t *info;
416 
417 	if (infop == NULL) {
418 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
419 		    "invalid i2c_prop_info_t output pointer: %p", infop));
420 	}
421 
422 	info = calloc(1, sizeof (i2c_prop_info_t));
423 	if (info == NULL) {
424 		int e = errno;
425 		return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to allocate "
426 		    "memory for a new i2c_prop_info_t"));
427 	}
428 
429 	info->pinfo_info.upi_prop = prop;
430 	if (ioctl(ctrl->ctrl_fd, UI2C_IOCTL_CTRL_PROP_INFO,
431 	    &info->pinfo_info) != 0) {
432 		int e = errno;
433 		return (i2c_ioctl_syserror(hdl, e, "property info request"));
434 	}
435 
436 	i2c_error_t *err = &info->pinfo_info.upi_error;
437 	if (err->i2c_error != I2C_CORE_E_OK &&
438 	    err->i2c_error != I2C_PROP_E_UNSUP) {
439 		free(info);
440 		return (i2c_ioctl_error(hdl, err, "property info request"));
441 	}
442 
443 	info->pinfo_hdl = hdl;
444 	info->pinfo_sup = err->i2c_error == I2C_CORE_E_OK;
445 	*infop = info;
446 	return (i2c_success(hdl));
447 }
448 
449 /*
450  * Find the property that maps to name the max power way. Basically iterate over
451  * all known properties and see if the name matches.
452  */
453 bool
i2c_prop_info_by_name(i2c_ctrl_t * ctrl,const char * name,i2c_prop_info_t ** infop)454 i2c_prop_info_by_name(i2c_ctrl_t *ctrl, const char *name,
455     i2c_prop_info_t **infop)
456 {
457 	i2c_hdl_t *hdl = ctrl->ctrl_hdl;
458 	i2c_prop_info_t *info;
459 
460 	if (name == NULL) {
461 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
462 		    "invalid property name pointer: %p", name));
463 	}
464 
465 	if (infop == NULL) {
466 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
467 		    "invalid i2c_prop_info_t output pointer: %p", infop));
468 	}
469 
470 	for (uint32_t i = 0; i < ctrl->ctrl_nstd; i++) {
471 		if (!i2c_prop_info(ctrl, i, &info)) {
472 			return (false);
473 		}
474 
475 		if (strcmp(name, i2c_prop_info_name(info)) == 0) {
476 			*infop = info;
477 			return (i2c_success(hdl));
478 		}
479 
480 		i2c_prop_info_free(info);
481 	}
482 
483 	return (i2c_error(hdl, I2C_ERR_BAD_PROP, 0, "unkonwn property: %s",
484 	    name));
485 }
486 
487 bool
i2c_prop_get(i2c_ctrl_t * ctrl,i2c_prop_t id,void * buf,size_t * lenp)488 i2c_prop_get(i2c_ctrl_t *ctrl, i2c_prop_t id, void *buf, size_t *lenp)
489 {
490 	i2c_hdl_t *hdl = ctrl->ctrl_hdl;
491 	ui2c_prop_t prop;
492 
493 	if (buf == NULL) {
494 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
495 		    "invalid property data buffer pointer: %p", buf));
496 	}
497 
498 	if (lenp == NULL) {
499 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
500 		    "invalid property size pointer: %p", buf));
501 	}
502 
503 	(void) memset(&prop, 0, sizeof (ui2c_prop_t));
504 	prop.up_prop = id;
505 
506 	if (ioctl(ctrl->ctrl_fd, UI2C_IOCTL_CTRL_PROP_GET, &prop) != 0) {
507 		int e = errno;
508 		return (i2c_ioctl_syserror(hdl, e, "property get request"));
509 	}
510 
511 	if (prop.up_error.i2c_error != I2C_CORE_E_OK) {
512 		return (i2c_ioctl_error(hdl, &prop.up_error,
513 		    "property get request"));
514 	}
515 
516 	size_t orig = *lenp;
517 	*lenp = prop.up_size;
518 	if (orig < prop.up_size) {
519 		return (i2c_error(hdl, I2C_ERR_PROP_BUF_TOO_SMALL, 0,
520 		    "property requires %u bytes to hold data, but only passed "
521 		    "a buffer of %zu bytes", prop.up_size, orig));
522 	}
523 
524 	(void) memcpy(buf, prop.up_value, prop.up_size);
525 	return (i2c_success(hdl));
526 }
527 
528 bool
i2c_prop_set(i2c_ctrl_t * ctrl,i2c_prop_t id,const void * buf,size_t len)529 i2c_prop_set(i2c_ctrl_t *ctrl, i2c_prop_t id, const void *buf, size_t len)
530 {
531 	i2c_hdl_t *hdl = ctrl->ctrl_hdl;
532 	ui2c_prop_t prop;
533 
534 	if (buf == NULL) {
535 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
536 		    "invalid property data buffer pointer: %p", buf));
537 	}
538 
539 	if (len == 0) {
540 		return (i2c_error(hdl, I2C_ERR_PROP_BUF_TOO_SMALL, 0,
541 		    "property buffer length must be more than 0"));
542 	}
543 
544 	if (len > I2C_PROP_SIZE_MAX) {
545 		return (i2c_error(hdl, I2C_ERR_PROP_BUF_TOO_BIG, 0,
546 		    "property buffer length must be less than or equal to %u",
547 		    I2C_PROP_SIZE_MAX));
548 	}
549 
550 	(void) memset(&prop, 0, sizeof (ui2c_prop_t));
551 	prop.up_prop = id;
552 	prop.up_size = len;
553 	(void) memcpy(&prop.up_value, buf, len);
554 
555 	if (ioctl(ctrl->ctrl_fd, UI2C_IOCTL_CTRL_PROP_SET, &prop) != 0) {
556 		int e = errno;
557 		return (i2c_ioctl_syserror(hdl, e, "property set request"));
558 	}
559 
560 	if (prop.up_error.i2c_error != I2C_CORE_E_OK) {
561 		return (i2c_ioctl_error(hdl, &prop.up_error,
562 		    "property set request"));
563 	}
564 
565 	return (i2c_success(hdl));
566 }
567