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