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 * libi2c error manipulation and translation.
18 */
19
20 #include <stdarg.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <upanic.h>
24 #include <sys/debug.h>
25 #include <sys/sysmacros.h>
26
27 #include "libi2c_impl.h"
28
29 static void
i2c_error_common(i2c_err_data_t * ep,i2c_err_t err,int32_t sys,const char * fmt,va_list ap)30 i2c_error_common(i2c_err_data_t *ep, i2c_err_t err, int32_t sys,
31 const char *fmt, va_list ap)
32 {
33 int ret;
34
35 ep->ie_err = err;
36 ep->ie_syserr = sys;
37 ep->ie_ctrl_err = I2C_CTRL_E_OK;
38 ret = vsnprintf(ep->ie_errmsg, sizeof (ep->ie_errmsg), fmt, ap);
39 if (ret >= sizeof (ep->ie_errmsg)) {
40 ep->ie_errlen = sizeof (ep->ie_errmsg) - 1;
41 } else if (ret <= 0) {
42 ep->ie_errlen = 0;
43 ep->ie_errmsg[0] = '\0';
44 } else {
45 ep->ie_errlen = (size_t)ret;
46 }
47 }
48
49 bool
i2c_error(i2c_hdl_t * hdl,i2c_err_t err,int32_t sys,const char * fmt,...)50 i2c_error(i2c_hdl_t *hdl, i2c_err_t err, int32_t sys, const char *fmt, ...)
51 {
52 va_list ap;
53
54 va_start(ap, fmt);
55 i2c_error_common(&hdl->ih_err, err, sys, fmt, ap);
56 va_end(ap);
57
58 return (false);
59 }
60
61 static bool
i2c_success_common(i2c_err_data_t * err)62 i2c_success_common(i2c_err_data_t *err)
63 {
64 err->ie_err = I2C_ERR_OK;
65 err->ie_syserr = 0;
66 err->ie_ctrl_err = I2C_CTRL_E_OK;
67 err->ie_errmsg[0] = '\0';
68 err->ie_errlen = 0;
69
70 return (true);
71 }
72
73 bool
i2c_success(i2c_hdl_t * hdl)74 i2c_success(i2c_hdl_t *hdl)
75 {
76 return (i2c_success_common(&hdl->ih_err));
77 }
78
79 i2c_err_t
i2c_err(i2c_hdl_t * hdl)80 i2c_err(i2c_hdl_t *hdl)
81 {
82 return (hdl->ih_err.ie_err);
83 }
84
85 i2c_ctrl_error_t
i2c_ctrl_err(i2c_hdl_t * hdl)86 i2c_ctrl_err(i2c_hdl_t *hdl)
87 {
88 return (hdl->ih_err.ie_ctrl_err);
89 }
90
91 int32_t
i2c_syserr(i2c_hdl_t * hdl)92 i2c_syserr(i2c_hdl_t *hdl)
93 {
94 return (hdl->ih_err.ie_syserr);
95 }
96
97 const char *
i2c_errmsg(i2c_hdl_t * hdl)98 i2c_errmsg(i2c_hdl_t *hdl)
99 {
100 return (hdl->ih_err.ie_errmsg);
101 }
102
103 const char *
i2c_errtostr(i2c_hdl_t * hdl,i2c_err_t err)104 i2c_errtostr(i2c_hdl_t *hdl, i2c_err_t err)
105 {
106 switch (err) {
107 case I2C_ERR_OK:
108 return ("I2C_ERR_OK");
109 case I2C_ERR_CONTROLLER:
110 return ("I2C_ERR_CONTROLLER");
111 case I2C_ERR_BAD_PTR:
112 return ("I2C_ERR_BAD_PTR");
113 case I2C_ERR_NO_MEM:
114 return ("I2C_ERR_NO_MEM");
115 case I2C_ERR_LIBDEVINFO:
116 return ("I2C_ERR_LIBDEVINFO");
117 case I2C_ERR_BAD_DEVI:
118 return ("I2C_ERR_BAD_DEVI");
119 case I2C_ERR_INTERNAL:
120 return ("I2C_ERR_INTERNAL");
121 case I2C_ERR_PRIVS:
122 return ("I2C_ERR_PRIVS");
123 case I2C_ERR_OPEN_DEV:
124 return ("I2C_ERR_OPEN_DEV");
125 case I2C_ERR_BAD_CONTROLLER:
126 return ("I2C_ERR_BAD_CONTROLLER");
127 case I2C_ERR_BAD_PORT:
128 return ("I2C_ERR_BAD_PORT");
129 case I2C_ERR_BAD_DEVICE:
130 return ("I2C_ERR_BAD_DEVICE");
131 case I2C_ERR_BAD_ADDR_TYPE:
132 return ("I2C_ERR_BAD_ADDR_TYPE");
133 case I2C_ERR_BAD_ADDR:
134 return ("I2C_ERR_BAD_ADDR");
135 case I2C_ERR_UNSUP_ADDR_TYPE:
136 return ("I2C_ERR_UNSUP_ADDR_TYPE");
137 case I2C_ERR_ADDR_RSVD:
138 return ("I2C_ERR_ADDR_RSVD");
139 case I2C_ERR_ADDR_IN_USE:
140 return ("I2C_ERR_ADDR_IN_USE");
141 case I2C_ERR_ADDR_UNKNOWN:
142 return ("I2C_ERR_ADDR_UNKNOWN");
143 case I2C_ERR_IO_READ_LEN_RANGE:
144 return ("I2C_ERR_IO_READ_LEN_RANGE");
145 case I2C_ERR_IO_WRITE_LEN_RANGE:
146 return ("I2C_ERR_IO_WRITE_LEN_RANGE");
147 case I2C_ERR_IO_REQ_MISSING_FIELDS:
148 return ("I2C_ERR_IO_REQ_MISSING_FIELDS");
149 case I2C_ERR_IO_REQ_IO_INVALID:
150 return ("I2C_ERR_IO_REQ_IO_INVALID");
151 case I2C_ERR_CANT_XLATE_IO_REQ:
152 return ("I2C_ERR_CANT_XLATE_IO_REQ");
153 case I2C_ERR_SMBUS_OP_UNSUP:
154 return ("I2C_ERR_SMBUS_OP_UNSUP");
155 case I2C_ERR_LOCK_WAIT_SIGNAL:
156 return ("I2C_ERR_LOCK_WAIT_SIGNAL");
157 case I2C_ERR_LOCK_WOULD_BLOCK:
158 return ("I2C_ERR_LOCK_WOULD_BLOCK");
159 case I2C_ERR_NO_KERN_MEM:
160 return ("I2C_ERR_NO_KERN_MEM");
161 case I2C_ERR_BAD_DEV_NAME:
162 return ("I2C_ERR_BAD_DEV_NAME");
163 case I2C_ERR_COMPAT_LEN_RANGE:
164 return ("I2C_ERR_COMPAT_LEN_RANGE");
165 case I2C_ERR_ADD_DEV_REQ_MISSING_FIELDS:
166 return ("I2C_ERR_ADD_DEV_REQ_MISSING_FIELDS");
167 case I2C_ERR_NEXUS:
168 return ("I2C_ERR_NEXUS");
169 case I2C_ERR_OP_IN_PROGRESS:
170 return ("I2C_ERR_OP_IN_PROGRESS");
171 case I2C_ERR_PROP_UNSUP:
172 return ("I2C_ERR_PROP_UNSUP");
173 case I2C_ERR_BAD_PROP:
174 return ("I2C_ERR_BAD_PROP");
175 case I2C_ERR_SET_PROP_UNSUP:
176 return ("I2C_ERR_SET_PROP_UNSUP");
177 case I2C_ERR_PROP_READ_ONLY:
178 return ("I2C_ERR_PROP_READ_ONLY");
179 case I2C_ERR_PROP_BUF_TOO_SMALL:
180 return ("I2C_ERR_PROP_BUF_TOO_SMALL");
181 case I2C_ERR_PROP_BUF_TOO_BIG:
182 return ("I2C_ERR_PROP_BUF_TOO_BIG");
183 case I2C_ERR_BAD_PROP_VAL:
184 return ("I2C_ERR_BAD_PROP_VAL");
185 case I2C_ERR_NO_PROP_DEF_VAL:
186 return ("I2C_ERR_NO_PROP_DEF_VAL");
187 case I2C_ERR_PROP_TYPE_MISMATCH:
188 return ("I2C_ERR_PROP_TYPE_MISMATCH");
189 case I2C_ERR_BUF_TOO_SMALL:
190 return ("I2C_ERR_BUF_TOO_SMALL");
191 default:
192 return ("unknown error");
193 }
194 }
195
196 const char *
i2c_ctrl_errtostr(i2c_hdl_t * hdl,i2c_ctrl_error_t err)197 i2c_ctrl_errtostr(i2c_hdl_t *hdl, i2c_ctrl_error_t err)
198 {
199 switch (err) {
200 case I2C_CTRL_E_OK:
201 return ("I2C_CTRL_E_OK");
202 case I2C_CTRL_E_INTERNAL:
203 return ("I2C_CTRL_E_INTERNAL");
204 case I2C_CTRL_E_DRIVER:
205 return ("I2C_CTRL_E_DRIVER");
206 case I2C_CTRL_E_UNSUP_CMD:
207 return ("I2C_CTRL_E_UNSUP_CMD");
208 case I2C_CTRL_E_BUS_BUSY:
209 return ("I2C_CTRL_E_BUS_BUSY");
210 case I2C_CTRL_E_ADDR_NACK:
211 return ("I2C_CTRL_E_ADDR_NACK");
212 case I2C_CTRL_E_DATA_NACK:
213 return ("I2C_CTRL_E_DATA_NACK");
214 case I2C_CTRL_E_NACK:
215 return ("I2C_CTRL_E_NACK");
216 case I2C_CTRL_E_ARB_LOST:
217 return ("I2C_CTRL_E_ARB_LOST");
218 case I2C_CTRL_E_BAD_ACK:
219 return ("I2C_CTRL_E_BAD_ACK");
220 case I2C_CTRL_E_REQ_TO:
221 return ("I2C_CTRL_E_REQ_TO");
222 case I2C_CTRL_E_BAD_SMBUS_RLEN:
223 return ("I2C_CTRL_E_BAD_SMBUS_RLEN");
224 case I2C_CTRL_E_SMBUS_CLOCK_LOW:
225 return ("I2C_CTRL_E_SMBUS_CLOCK_LOW");
226 default:
227 return ("unkonwn error");
228 }
229 }
230
231 /*
232 * Most of our ioctls are designed to give us a semantic error. However, there
233 * are cases where we may fail outside of that. We opt to abort on a subset of
234 * these that represent gross library programmer error: mainly EBADF and EFAULT.
235 * Note, EFAULT generally covers the core ioctl structures and not any
236 * additional buffers that are passed in, therefore if this triggers then we
237 * have done something terribly wrong.
238 */
239 bool
i2c_ioctl_syserror(i2c_hdl_t * hdl,int err,const char * desc)240 i2c_ioctl_syserror(i2c_hdl_t *hdl, int err, const char *desc)
241 {
242 switch (err) {
243 case EFAULT:
244 case EBADF: {
245 const char *base = "fatal libi2c internal programming error: "
246 "failed to issue ioctl";
247 char msg[1024];
248 int ret;
249 const char *up;
250 size_t ulen;
251
252 ret = snprintf(msg, sizeof (msg), "%s %s: %s (hdl %p)",
253 base, desc, strerror(err), hdl);
254 if (ret >= sizeof (msg)) {
255 ulen = sizeof (msg);
256 up = msg;
257 } else if (ret <= 0) {
258 up = base;
259 ulen = strlen(base) + 1;
260 } else {
261 ulen = (size_t)ret;
262 up = msg;
263 }
264
265 upanic(up, ulen);
266 }
267 case EPERM:
268 return (i2c_error(hdl, I2C_ERR_PRIVS, err, "failed to issue %s "
269 "ioctl due to missing privileges", desc));
270 default:
271 return (i2c_error(hdl, I2C_ERR_INTERNAL, err,
272 "failed to issue %s ioctl due to unexpected system error: "
273 "%s", desc, strerrordesc_np(err)));
274 }
275 }
276
277 typedef struct {
278 i2c_errno_t kl_kern;
279 i2c_err_t kl_lib;
280 const char *kl_desc;
281 } i2c_ktolmap_t;
282
283 /*
284 * This facilitates mapping kernel errors to user library errors along with a
285 * short description. This is ordered based on the order of the kernel ioctls so
286 * we can more easily spot missing entries. The description is used to help
287 * users understand what happened.
288 */
289 static const i2c_ktolmap_t i2c_ktolmap[] = {
290 /*
291 * We skip I2C_CORE_E_OK because it not reach here. We also handle the
292 * controller error, I2C_CORE_E_CONTROLLER, specifically so it is not
293 * translated here
294 */
295 { I2C_CORE_E_BAD_ADDR_TYPE, I2C_ERR_BAD_ADDR_TYPE, "invalid I2C "
296 "address family type" },
297 { I2C_CORE_E_BAD_ADDR, I2C_ERR_BAD_ADDR, "invalid I2C address" },
298 { I2C_CORE_E_UNSUP_ADDR_TYPE, I2C_ERR_UNSUP_ADDR_TYPE, "address "
299 "family not supported by the controller" },
300 { I2C_CORE_E_ADDR_RSVD, I2C_ERR_ADDR_RSVD, "I2C address is reserved" },
301 { I2C_CORE_E_ADDR_IN_USE, I2C_ERR_ADDR_IN_USE, "I2C addrses is already "
302 "used" },
303 { I2C_CORE_E_ADDR_REFCNT, I2C_ERR_INTERNAL, "address could not be "
304 "assigned due to kernel reference count exhaustion" },
305 { I2C_CORE_E_UNKNOWN_ADDR, I2C_ERR_ADDR_UNKNOWN, "I2C address does not "
306 "map to a known device" },
307 { I2C_CORE_E_CANT_XLATE_REQ, I2C_ERR_CANT_XLATE_IO_REQ, "I/O request "
308 "could not be translated to something the controller supports" },
309 { I2C_CORE_E_NEED_READ_OR_WRITE, I2C_ERR_IO_REQ_IO_INVALID, "request "
310 "requires data to transmit or receive, but neither specified" },
311 /*
312 * We have purposefully skipped I2C_CORE_E_BAD_I2C_REQ_FLAGS and
313 * I2C_CORE_E_BAD_SMBUS_REQ_FLAGS as these are not flags that users are
314 * able to set and therefore the only reason these should be wrong is if
315 * we screwed something up in the library.
316 */
317 { I2C_CORE_E_BAD_I2C_REQ_READ_LEN, I2C_ERR_IO_READ_LEN_RANGE, "invalid "
318 "receive length" },
319 { I2C_CORE_E_BAD_I2C_REQ_WRITE_LEN, I2C_ERR_IO_WRITE_LEN_RANGE,
320 "invalid transmit length" },
321 { I2C_CORE_E_BAD_SMBUS_READ_LEN, I2C_ERR_IO_READ_LEN_RANGE, "invalid "
322 "receive length" },
323 { I2C_CORE_E_BAD_SMBUS_WRITE_LEN, I2C_ERR_IO_WRITE_LEN_RANGE,
324 "invalid transmit length" },
325 { I2C_CORE_E_UNSUP_SMBUS_OP, I2C_ERR_SMBUS_OP_UNSUP, "SMBus operation "
326 "unsupported by controller or system" },
327 { I2C_CORE_E_LOCK_WOULD_BLOCK, I2C_ERR_LOCK_WOULD_BLOCK, "lock not "
328 "available and no blocking allowed" },
329 { I2C_CORE_E_LOCK_WAIT_SIGNAL, I2C_ERR_LOCK_WAIT_SIGNAL, "signal "
330 "received while blocking" },
331 /*
332 * We have purposefully skipped the nvlist device related errors,
333 * I2C_IOCTL_E_NVL_TOO_BIG, I2C_IOCTL_E_NVL_INVALID,
334 * I2C_IOCTL_E_NVL_KEY_MISSING, I2C_IOCTL_E_NVL_KEY_UNKNOWN, and
335 * I2C_IOCTL_E_NVL_KEY_BAD_TYPE. These are things that generally only
336 * the library can screw up.
337 */
338 { I2C_IOCTL_E_BAD_USER_DATA, I2C_ERR_BAD_PTR, "the kernel detected an "
339 "invalid user buffer while trying to read/write the passed in "
340 "buffer" },
341 { I2C_IOCTL_E_NO_KERN_MEM, I2C_ERR_NO_KERN_MEM, "the kerenl failed "
342 "to allocate memory for this operation" },
343 { I2C_IOCTL_E_BAD_DEV_NAME, I2C_ERR_BAD_DEV_NAME, "invalid device "
344 "or compatible name" },
345 { I2C_IOCTL_E_COMPAT_LEN_RANGE, I2C_ERR_COMPAT_LEN_RANGE, "invalid "
346 "compatible string length" },
347 { I2C_IOCTL_E_NEXUS, I2C_ERR_NEXUS, "unexpected kernel nexus driver "
348 "error" },
349 /*
350 * We have purposefully skipped I2C_IOCTL_E_NO_BUS_LOCK_NEXUS as there
351 * is no way to take an explicit lock in userland right now.
352 */
353 { I2C_IOCTL_E_IN_PROGRESS, I2C_ERR_OP_IN_PROGRESS, "cannot perform "
354 "requested operation, handle already performing one" },
355 /*
356 * All I2C_CLIENT class errors are skipped as they should not be
357 * returned to userland.
358 */
359 { I2C_PROP_E_UNSUP, I2C_ERR_PROP_UNSUP, "property unsupported by "
360 "controller" },
361 { I2C_PROP_E_UNKNOWN, I2C_ERR_BAD_PROP, "unknown property" },
362 { I2C_PROP_E_READ_ONLY, I2C_ERR_PROP_READ_ONLY, "property is "
363 "read-only" },
364 { I2C_PROP_E_SMALL_BUF, I2C_ERR_PROP_BUF_TOO_SMALL, "data buffer too "
365 "small for property" },
366 { I2C_PROP_E_TOO_BIG_BUF, I2C_ERR_PROP_BUF_TOO_BIG, "data buffer too "
367 "big for property" },
368 /*
369 * Indicates that the property value is invalid.
370 */
371 { I2C_PROP_E_BAD_VAL, I2C_ERR_BAD_PROP, "invalid property value" },
372 /*
373 * Indicates that the controller doesn't support setting properties.
374 */
375 { I2C_PROP_E_SET_UNSUP, I2C_ERR_SET_PROP_UNSUP, "controller does not "
376 "support setting properties" },
377 /*
378 * Currently all MUX class errors are skipped as they aren't really
379 * expected here.
380 */
381 };
382
383 bool
i2c_ioctl_error(i2c_hdl_t * hdl,const i2c_error_t * ioc,const char * desc)384 i2c_ioctl_error(i2c_hdl_t *hdl, const i2c_error_t *ioc, const char *desc)
385 {
386 int ret;
387 i2c_err_data_t *err = &hdl->ih_err;
388 VERIFY3U(ioc->i2c_error, !=, I2C_CORE_E_OK);
389
390 err->ie_syserr = 0;
391
392 if (ioc->i2c_error == I2C_CORE_E_CONTROLLER) {
393 const char *code = i2c_ctrl_errtostr(hdl, ioc->i2c_ctrl);
394
395 err->ie_err = I2C_ERR_CONTROLLER;
396 err->ie_ctrl_err = ioc->i2c_ctrl;
397 ret = snprintf(err->ie_errmsg, sizeof (err->ie_errmsg),
398 "failed to execute %s command: received controller "
399 "error %s (0x%x)", desc, code, ioc->i2c_ctrl);
400 } else {
401 const i2c_ktolmap_t *map = NULL;
402 for (size_t i = 0; i < ARRAY_SIZE(i2c_ktolmap); i++) {
403 if (i2c_ktolmap[i].kl_kern == ioc->i2c_error) {
404 map = &i2c_ktolmap[i];
405 break;
406 }
407 }
408
409 if (map != NULL) {
410 err->ie_err = map->kl_lib;
411 ret = snprintf(err->ie_errmsg, sizeof (err->ie_errmsg),
412 "failed to execute %s command: %s", desc,
413 map->kl_desc);
414 } else {
415 err->ie_err = I2C_ERR_INTERNAL;
416 ret = snprintf(err->ie_errmsg, sizeof (err->ie_errmsg),
417 "failed to execute %s command: failed to map "
418 "kernel error 0x%x to a known cause", desc,
419 ioc->i2c_error);
420 }
421 }
422
423 if (ret >= sizeof (err->ie_errmsg)) {
424 err->ie_errlen = sizeof (err->ie_errlen) - 1;
425 } else if (ret <= 0) {
426 err->ie_errlen = 0;
427 err->ie_errmsg[0] = '\0';
428 } else {
429 err->ie_errlen = (size_t)ret;
430 }
431
432 return (false);
433 }
434
435 bool
i2c_nvlist_error(i2c_hdl_t * hdl,int ret,const char * desc)436 i2c_nvlist_error(i2c_hdl_t *hdl, int ret, const char *desc)
437 {
438 if (ret == 0) {
439 return (true);
440 }
441
442 if (ret == ENOMEM) {
443 return (i2c_error(hdl, I2C_ERR_NO_MEM, ret, "failed to "
444 "allocate memory to %s", desc));
445 }
446
447 return (i2c_error(hdl, I2C_ERR_INTERNAL, ret, "unexpected internal "
448 "error while trying to %s", desc));
449 }
450