xref: /illumos-gate/usr/src/lib/libi2c/common/libi2c_error.c (revision a3ebb524df668b0fc3a38f33d0049380f5f11ec1)
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
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
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
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
74 i2c_success(i2c_hdl_t *hdl)
75 {
76 	return (i2c_success_common(&hdl->ih_err));
77 }
78 
79 i2c_err_t
80 i2c_err(i2c_hdl_t *hdl)
81 {
82 	return (hdl->ih_err.ie_err);
83 }
84 
85 i2c_ctrl_error_t
86 i2c_ctrl_err(i2c_hdl_t *hdl)
87 {
88 	return (hdl->ih_err.ie_ctrl_err);
89 }
90 
91 int32_t
92 i2c_syserr(i2c_hdl_t *hdl)
93 {
94 	return (hdl->ih_err.ie_syserr);
95 }
96 
97 const char *
98 i2c_errmsg(i2c_hdl_t *hdl)
99 {
100 	return (hdl->ih_err.ie_errmsg);
101 }
102 
103 const char *
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 *
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
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
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
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