xref: /illumos-gate/usr/src/lib/libxpio/common/libxpio.c (revision fd71220ba0fafcc9cf5ea0785db206f3f31336e7)
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 2022 Oxide Computer Company
14  */
15 
16 /*
17  * An evolving, but private, interface to the kernel xPIO (GPIO and DPIO)
18  * subsystem.
19  */
20 
21 #include <stddef.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <stdarg.h>
25 #include <libdevinfo.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <sys/debug.h>
32 
33 #include "libxpio_impl.h"
34 
35 xpio_err_t
xpio_err(xpio_t * xpio)36 xpio_err(xpio_t *xpio)
37 {
38 	return (xpio->xp_err);
39 }
40 
41 xpio_update_err_t
xpio_update_err(xpio_gpio_update_t * update)42 xpio_update_err(xpio_gpio_update_t *update)
43 {
44 	return (update->xgo_err);
45 }
46 
47 int32_t
xpio_syserr(xpio_t * xpio)48 xpio_syserr(xpio_t *xpio)
49 {
50 	return (xpio->xp_syserr);
51 }
52 
53 int32_t
xpio_update_syserr(xpio_gpio_update_t * update)54 xpio_update_syserr(xpio_gpio_update_t *update)
55 {
56 	return (update->xgo_syserr);
57 }
58 
59 const char *
xpio_errmsg(xpio_t * xpio)60 xpio_errmsg(xpio_t *xpio)
61 {
62 	return (xpio->xp_errmsg);
63 }
64 
65 const char *
xpio_update_errmsg(xpio_gpio_update_t * update)66 xpio_update_errmsg(xpio_gpio_update_t *update)
67 {
68 	return (update->xgo_errmsg);
69 }
70 
71 const char *
xpio_err2str(xpio_t * xpio,xpio_err_t err)72 xpio_err2str(xpio_t *xpio, xpio_err_t err)
73 {
74 	switch (err) {
75 	case XPIO_ERR_OK:
76 		return ("XPIO_ERR_OK");
77 	case XPIO_ERR_NO_MEM:
78 		return ("XPIO_ERR_NO_MEM");
79 	case XPIO_ERR_LIBDEVINFO:
80 		return ("XPIO_ERR_LIBDEVINFO");
81 	case XPIO_ERR_INTERNAL:
82 		return ("XPIO_ERR_INTERNAL");
83 	case XPIO_ERR_BAD_PTR:
84 		return ("XPIO_ERR_BAD_PTR");
85 	case XPIO_ERR_WRONG_MINOR_TYPE:
86 		return ("XPIO_ERR_WRONG_MINOR_TYPE");
87 	case XPIO_ERR_OPEN_DEV:
88 		return ("XPIO_ERR_OPEN_DEV");
89 	case XPIO_ERR_KGPIO:
90 		return ("XPIO_ERR_KGPIO");
91 	case XPIO_ERR_BAD_CTRL_NAME:
92 		return ("XPIO_ERR_BAD_CTRL_NAME");
93 	case XPIO_ERR_BAD_GPIO_ID:
94 		return ("XPIO_ERR_BAD_GPIO_ID");
95 	case XPIO_ERR_BAD_UPDATE:
96 		return ("XPIO_ERR_BAD_UPDATE");
97 	case XPIO_ERR_BAD_DPIO_FEAT:
98 		return ("XPIO_ERR_BAD_DPIO_FEAT");
99 	case XPIO_ERR_BAD_DPIO_NAME:
100 		return ("XPIO_ERR_BAD_DPIO_NAME");
101 	case XPIO_ERR_BAD_GPIO_NAME:
102 		return ("XPIO_ERR_BAD_GPIO_NAME");
103 	case XPIO_ERR_NO_LOOKUP_MATCH:
104 		return ("XPIO_ERR_NO_LOOKUP_MATCH");
105 	default:
106 		return ("unknown error");
107 	}
108 
109 }
110 
111 const char *
xpio_update_err2str(xpio_gpio_update_t * update,xpio_update_err_t err)112 xpio_update_err2str(xpio_gpio_update_t *update, xpio_update_err_t err)
113 {
114 	switch (err) {
115 	case XPIO_UPDATE_ERR_OK:
116 		return ("XPIO_UPDATE_ERR_OK");
117 	case XPIO_UPDATE_ERR_RO:
118 		return ("XPIO_UPDATE_ERR_RO");
119 	case XPIO_UPDATE_ERR_UNKNOWN_ATTR:
120 		return ("XPIO_UPDATE_ERR_UNKNOWN_ATTR");
121 	case XPIO_UPDATE_ERR_BAD_TYPE:
122 		return ("XPIO_UPDATE_ERR_BAD_TYPE");
123 	case XPIO_UPDATE_ERR_UNKNOWN_VAL:
124 		return ("XPIO_UPDATE_ERR_UNKNOWN_VAL");
125 	case XPIO_UPDATE_ERR_CANT_APPLY_VAL:
126 		return ("XPIO_UPDATE_ERR_CANT_APPLY_VAL");
127 	case XPIO_UPDATE_ERR_NO_MEM:
128 		return ("XPIO_UPDATE_ERR_NO_MEM");
129 	case XPIO_UPDATE_ERR_INTERNAL:
130 		return ("XPIO_UPDATE_ERR_INTERNAL");
131 	default:
132 		return ("unknown error");
133 	}
134 }
135 
136 bool
xpio_error(xpio_t * xpio,xpio_err_t err,int32_t sys,const char * fmt,...)137 xpio_error(xpio_t *xpio, xpio_err_t err, int32_t sys, const char *fmt, ...)
138 {
139 	va_list ap;
140 
141 	xpio->xp_err = err;
142 	xpio->xp_syserr = sys;
143 	va_start(ap, fmt);
144 	(void) vsnprintf(xpio->xp_errmsg, sizeof (xpio->xp_errmsg), fmt, ap);
145 	va_end(ap);
146 	return (false);
147 }
148 
149 bool
xpio_update_error(xpio_gpio_update_t * update,xpio_update_err_t err,int32_t sys,const char * fmt,...)150 xpio_update_error(xpio_gpio_update_t *update, xpio_update_err_t err,
151     int32_t sys, const char *fmt, ...)
152 {
153 	va_list ap;
154 
155 	update->xgo_err = err;
156 	update->xgo_syserr = sys;
157 	va_start(ap, fmt);
158 	(void) vsnprintf(update->xgo_errmsg, sizeof (update->xgo_errmsg), fmt,
159 	    ap);
160 	va_end(ap);
161 	return (false);
162 }
163 bool
xpio_success(xpio_t * xpio)164 xpio_success(xpio_t *xpio)
165 {
166 	xpio->xp_err = XPIO_ERR_OK;
167 	xpio->xp_syserr = 0;
168 	xpio->xp_errmsg[0] = '\0';
169 	return (true);
170 }
171 
172 bool
xpio_update_success(xpio_gpio_update_t * update)173 xpio_update_success(xpio_gpio_update_t *update)
174 {
175 	update->xgo_err = XPIO_UPDATE_ERR_OK;
176 	update->xgo_syserr = 0;
177 	update->xgo_errmsg[0] = '\0';
178 	return (true);
179 }
180 
181 typedef struct {
182 	xpio_t *xcc_xpio;
183 	xpio_ctrl_disc_f xcc_func;
184 	void *xcc_arg;
185 } xpio_ctrl_cb_t;
186 
187 static int
xpio_ctrl_discover_cb(di_node_t di,di_minor_t minor,void * arg)188 xpio_ctrl_discover_cb(di_node_t di, di_minor_t minor, void *arg)
189 {
190 	bool ret;
191 	xpio_ctrl_cb_t *cb = arg;
192 	xpio_ctrl_disc_t disc;
193 
194 	disc.xcd_minor = minor;
195 
196 	ret = cb->xcc_func(cb->xcc_xpio, &disc, cb->xcc_arg);
197 	if (ret) {
198 		return (DI_WALK_CONTINUE);
199 	} else {
200 		return (DI_WALK_TERMINATE);
201 	}
202 }
203 
204 void
xpio_ctrl_discover(xpio_t * xpio,xpio_ctrl_disc_f func,void * arg)205 xpio_ctrl_discover(xpio_t *xpio, xpio_ctrl_disc_f func, void *arg)
206 {
207 	xpio_ctrl_cb_t cb;
208 
209 	cb.xcc_xpio = xpio;
210 	cb.xcc_func = func;
211 	cb.xcc_arg = arg;
212 	(void) di_walk_minor(xpio->xp_devinfo, DDI_NT_GPIO_CTRL, 0, &cb,
213 	    xpio_ctrl_discover_cb);
214 }
215 
216 void
xpio_ctrl_fini(xpio_ctrl_t * ctrl)217 xpio_ctrl_fini(xpio_ctrl_t *ctrl)
218 {
219 	if (ctrl == NULL) {
220 		return;
221 	}
222 
223 	if (ctrl->xc_fd >= 0) {
224 		(void) close(ctrl->xc_fd);
225 		ctrl->xc_fd = -1;
226 	}
227 
228 	free(ctrl);
229 }
230 
231 void
xpio_ctrl_info_free(xpio_ctrl_info_t * infop)232 xpio_ctrl_info_free(xpio_ctrl_info_t *infop)
233 {
234 	free(infop);
235 }
236 
237 uint32_t
xpio_ctrl_info_ngpios(xpio_ctrl_info_t * infop)238 xpio_ctrl_info_ngpios(xpio_ctrl_info_t *infop)
239 {
240 	return (infop->xci_ngpios);
241 }
242 
243 uint32_t
xpio_ctrl_info_ndpios(xpio_ctrl_info_t * infop)244 xpio_ctrl_info_ndpios(xpio_ctrl_info_t *infop)
245 {
246 	return (infop->xci_ndpios);
247 }
248 
249 const char *
xpio_ctrl_info_devpath(xpio_ctrl_info_t * infop)250 xpio_ctrl_info_devpath(xpio_ctrl_info_t *infop)
251 {
252 	return (infop->xci_devpath);
253 }
254 
255 bool
xpio_ctrl_info(xpio_ctrl_t * ctrl,xpio_ctrl_info_t ** outp)256 xpio_ctrl_info(xpio_ctrl_t *ctrl, xpio_ctrl_info_t **outp)
257 {
258 	kgpio_ctrl_info_t info;
259 	xpio_t *xpio = ctrl->xc_xpio;
260 	xpio_ctrl_info_t *out;
261 
262 	if (outp == NULL) {
263 		return (xpio_error(xpio, XPIO_ERR_BAD_PTR, 0, "encountered "
264 		    "invalid xpio_ctrl_info_t output pointer: %p", outp));
265 	}
266 
267 	(void) memset(&info, 0, sizeof (info));
268 	if (ioctl(ctrl->xc_fd, KGPIO_IOC_CTRL_INFO, &info) != 0) {
269 		int e = errno;
270 		return (xpio_error(xpio, XPIO_ERR_KGPIO, e, "failed to issue "
271 		    "controller information ioctl to %s: %s", ctrl->xc_name,
272 		    strerror(e)));
273 	}
274 
275 	out = calloc(1, sizeof (xpio_ctrl_info_t));
276 	if (out == NULL) {
277 		int e = errno;
278 		return (xpio_error(xpio, XPIO_ERR_NO_MEM, e, "failed to "
279 		    "allocate memory for a new xpio_ctrl_info_t: %s",
280 		    strerror(e)));
281 	}
282 
283 	out->xci_ngpios = info.kci_ngpios;
284 	out->xci_ndpios = info.kci_ndpios;
285 	(void) memcpy(out->xci_devpath, info.kci_devpath,
286 	    sizeof (info.kci_devpath));
287 
288 	*outp = out;
289 	return (xpio_success(xpio));
290 }
291 
292 bool
xpio_ctrl_init(xpio_t * xpio,di_minor_t minor,xpio_ctrl_t ** outp)293 xpio_ctrl_init(xpio_t *xpio, di_minor_t minor, xpio_ctrl_t **outp)
294 {
295 	xpio_ctrl_t *ctrl;
296 	char *path, buf[PATH_MAX];
297 
298 	if (minor == DI_NODE_NIL) {
299 		return (xpio_error(xpio, XPIO_ERR_BAD_PTR, 0, "encountered "
300 		    "invalid di_minor_t: %p", minor));
301 	}
302 
303 	if (outp == NULL) {
304 		return (xpio_error(xpio, XPIO_ERR_BAD_PTR, 0, "encountered "
305 		    "invalid xpio_ctrl_t output pointer: %p", outp));
306 	}
307 	*outp = NULL;
308 
309 	if (strcmp(di_minor_nodetype(minor), DDI_NT_GPIO_CTRL) != 0) {
310 		return (xpio_error(xpio, XPIO_ERR_WRONG_MINOR_TYPE, 0,
311 		    "minor %s has incorrect node type: %s, expected %s",
312 		    di_minor_name(minor), di_minor_nodetype(minor),
313 		    DDI_NT_GPIO_CTRL));
314 	}
315 
316 	path = di_devfs_minor_path(minor);
317 	if (path == NULL) {
318 		int e = errno;
319 		return (xpio_error(xpio, XPIO_ERR_LIBDEVINFO, e, "failed to "
320 		    "obtain /devices path for the requested minor: %s",
321 		    strerror(e)));
322 	}
323 
324 	if (snprintf(buf, sizeof (buf), "/devices%s", path) >= sizeof (buf)) {
325 		di_devfs_path_free(path);
326 		return (xpio_error(xpio, XPIO_ERR_INTERNAL, 0, "failed to "
327 		    "construct full /devices minor path, would have overflown "
328 		    "internal buffer"));
329 	}
330 	di_devfs_path_free(path);
331 
332 	ctrl = calloc(1, sizeof (*ctrl));
333 	if (ctrl == NULL) {
334 		int e = errno;
335 		return (xpio_error(xpio, XPIO_ERR_NO_MEM, e, "failed to "
336 		    "allocate memory for a new xpio_ctrl_t: %s", strerror(e)));
337 	}
338 
339 	ctrl->xc_xpio = xpio;
340 	ctrl->xc_minor = minor;
341 	ctrl->xc_name = di_minor_name(minor);
342 
343 	ctrl->xc_fd = open(buf, O_RDWR);
344 	if (ctrl->xc_fd < 0) {
345 		int e = errno;
346 		xpio_ctrl_fini(ctrl);
347 		return (xpio_error(xpio, XPIO_ERR_OPEN_DEV, e, "failed to open "
348 		    "device path %s: %s", buf, strerror(e)));
349 	}
350 
351 	*outp = ctrl;
352 	return (xpio_success(xpio));
353 }
354 
355 typedef struct {
356 	bool xcia_found;
357 	const char *xcia_name;
358 	xpio_ctrl_t *xcia_ctrl;
359 } xpio_ctrl_init_arg_t;
360 
361 static bool
xpio_ctrl_init_by_name_cb(xpio_t * xpio,xpio_ctrl_disc_t * disc,void * arg)362 xpio_ctrl_init_by_name_cb(xpio_t *xpio, xpio_ctrl_disc_t *disc, void *arg)
363 {
364 	xpio_ctrl_init_arg_t *init = arg;
365 
366 	if (strcmp(di_minor_name(disc->xcd_minor), init->xcia_name) != 0) {
367 		return (true);
368 	}
369 
370 	/*
371 	 * As we've found a match. Attempt to open it. Whether we succeed or
372 	 * fail, we're done at this point.
373 	 */
374 	init->xcia_found = true;
375 	(void) xpio_ctrl_init(xpio, disc->xcd_minor, &init->xcia_ctrl);
376 	return (false);
377 }
378 
379 bool
xpio_ctrl_init_by_name(xpio_t * xpio,const char * name,xpio_ctrl_t ** outp)380 xpio_ctrl_init_by_name(xpio_t *xpio, const char *name, xpio_ctrl_t **outp)
381 {
382 	xpio_ctrl_init_arg_t init;
383 
384 	if (name == NULL) {
385 		return (xpio_error(xpio, XPIO_ERR_BAD_PTR, 0, "encountered "
386 		    "invalid name pointer: %p", name));
387 	}
388 
389 	if (outp == NULL) {
390 		return (xpio_error(xpio, XPIO_ERR_BAD_PTR, 0, "encountered "
391 		    "invalid xpio_crl_t output pointer: %p", outp));
392 	}
393 	*outp = NULL;
394 
395 	init.xcia_found = false;
396 	init.xcia_name = name;
397 	init.xcia_ctrl = NULL;
398 
399 	xpio_ctrl_discover(xpio, xpio_ctrl_init_by_name_cb, &init);
400 	if (!init.xcia_found) {
401 		return (xpio_error(xpio, XPIO_ERR_BAD_CTRL_NAME, 0, "failed to "
402 		    "find controller %s", init.xcia_name));
403 	}
404 
405 	/*
406 	 * If we have a NULL controller, but it was found, then we know that
407 	 * this exists and instead had an error.
408 	 */
409 	if (init.xcia_ctrl == NULL) {
410 		return (false);
411 	}
412 
413 	*outp = init.xcia_ctrl;
414 	return (xpio_success(xpio));
415 }
416 
417 void
xpio_gpio_info_free(xpio_gpio_info_t * gi)418 xpio_gpio_info_free(xpio_gpio_info_t *gi)
419 {
420 	if (gi == NULL) {
421 		return;
422 	}
423 
424 	nvlist_free(gi->xgi_nvl);
425 	free(gi);
426 }
427 
428 uint32_t
xpio_gpio_id(xpio_gpio_info_t * gi)429 xpio_gpio_id(xpio_gpio_info_t *gi)
430 {
431 	return (gi->xgi_id);
432 }
433 
434 bool
xpio_gpio_info(xpio_ctrl_t * ctrl,uint32_t gpio_num,xpio_gpio_info_t ** outp)435 xpio_gpio_info(xpio_ctrl_t *ctrl, uint32_t gpio_num, xpio_gpio_info_t **outp)
436 {
437 	xpio_t *xpio = ctrl->xc_xpio;
438 	char *nvl_buf = NULL;
439 	kgpio_gpio_info_t info;
440 	bool ret;
441 	int nvl_ret;
442 	xpio_gpio_info_t *gi;
443 
444 	if (outp == NULL) {
445 		return (xpio_error(xpio, XPIO_ERR_BAD_PTR, 0, "encountered "
446 		    "invalid xpio_gpio_info_t output pointer: %p", outp));
447 
448 	}
449 
450 	nvl_buf = malloc(XPIO_NVL_LEN);
451 	if (nvl_buf == NULL) {
452 		int e = errno;
453 		return (xpio_error(xpio, XPIO_ERR_NO_MEM, e, "failed to "
454 		    "allocate memory for temporary data: %s", strerror(e)));
455 	}
456 
457 	(void) memset(&info, 0, sizeof (info));
458 	info.kgi_id = gpio_num;
459 	info.kgi_attr = (uintptr_t)nvl_buf;
460 	info.kgi_attr_len = XPIO_NVL_LEN;
461 
462 	if (ioctl(ctrl->xc_fd, KGPIO_IOC_GPIO_INFO, &info) != 0) {
463 		int e = errno;
464 
465 		switch (e) {
466 		case ENOENT:
467 			ret = xpio_error(xpio, XPIO_ERR_BAD_GPIO_ID, 0, "gpio "
468 			    "%u does is not a valid GPIO for controller %s",
469 			    gpio_num, ctrl->xc_name);
470 			break;
471 		case EOVERFLOW:
472 			ret = xpio_error(xpio, XPIO_ERR_INTERNAL, 0,
473 			    "internal error occurred: serialized nvlist "
474 			    "exceeds library capabilities: wanted %zu bytes",
475 			    info.kgi_attr_len);
476 			break;
477 		case EFAULT:
478 			abort();
479 		default:
480 			ret = xpio_error(xpio, XPIO_ERR_KGPIO, e, "failed to "
481 			    "issue gpio information ioctl for gpio %u: %s",
482 			    gpio_num, strerror(e));
483 			break;
484 		}
485 		goto out;
486 	}
487 
488 	gi = calloc(1, sizeof (xpio_gpio_info_t));
489 	if (gi == NULL) {
490 		int e = errno;
491 		ret = xpio_error(xpio, XPIO_ERR_NO_MEM, e, "failed to "
492 		    "allocate memory for a xpio_gpio_info_t: %s", strerror(e));
493 		goto out;
494 	}
495 
496 	gi->xgi_flags = info.kgi_flags;
497 	gi->xgi_id = gpio_num;
498 	nvl_ret = nvlist_unpack(nvl_buf, info.kgi_attr_len, &gi->xgi_nvl, 0);
499 	if (nvl_ret != 0) {
500 		free(gi);
501 		ret = xpio_error(xpio, XPIO_ERR_INTERNAL, nvl_ret, "kernel "
502 		    "gave us an unparseable nvlist_t: %s", strerror(nvl_ret));
503 	} else {
504 		*outp = gi;
505 		ret = xpio_success(xpio);
506 	}
507 out:
508 	free(nvl_buf);
509 	return (ret);
510 }
511 
512 void
xpio_gpio_update_free(xpio_gpio_update_t * update)513 xpio_gpio_update_free(xpio_gpio_update_t *update)
514 {
515 	if (update == NULL) {
516 		return;
517 	}
518 
519 	if (update->xgo_update != NULL) {
520 		nvlist_free(update->xgo_update);
521 		update->xgo_update = NULL;
522 	}
523 
524 	if (update->xgo_err_nvl != NULL) {
525 		nvlist_free(update->xgo_err_nvl);
526 		update->xgo_err_nvl = NULL;
527 	}
528 
529 	free(update);
530 }
531 
532 bool
xpio_gpio_update(xpio_ctrl_t * ctrl,xpio_gpio_update_t * update)533 xpio_gpio_update(xpio_ctrl_t *ctrl, xpio_gpio_update_t *update)
534 {
535 	xpio_t *xpio = ctrl->xc_xpio;
536 	int nvl_ret;
537 	kgpio_update_t kgu;
538 	size_t pack_size;
539 	bool ret;
540 	char *update_buf = NULL, *err_buf = NULL;
541 
542 	if (update == NULL) {
543 		return (xpio_error(xpio, XPIO_ERR_BAD_PTR, 0, "encountered "
544 		    "invalid xpio_gpio_update_t pointer: %p", update));
545 	}
546 
547 	if (update->xgo_err_nvl != NULL) {
548 		return (xpio_error(xpio, XPIO_ERR_UPDATE_USED, 0, "this "
549 		    "update structure was already used and has error "
550 		    "information associated with it"));
551 	}
552 
553 	nvl_ret = nvlist_size(update->xgo_update, &pack_size, NV_ENCODE_NATIVE);
554 	if (nvl_ret != 0) {
555 		return (xpio_error(xpio, XPIO_ERR_INTERNAL, nvl_ret, "failed "
556 		    "to determine packed update nvlist_t size: %s",
557 		    strerror(nvl_ret)));
558 	}
559 
560 	update_buf = malloc(pack_size);
561 	if (update_buf == NULL) {
562 		int e = errno;
563 		ret = xpio_error(xpio, XPIO_ERR_NO_MEM, e, "failed to allocate "
564 		    "%zu bytes for the packed nvlist buffer: %s", pack_size,
565 		    strerror(e));
566 		goto out;
567 	}
568 
569 	err_buf = malloc(XPIO_NVL_LEN);
570 	if (err_buf == NULL) {
571 		int e = errno;
572 		ret = xpio_error(xpio, XPIO_ERR_NO_MEM, e, "failed to allocate "
573 		    "%u bytes for the packed error buffer: %s", XPIO_NVL_LEN,
574 		    strerror(e));
575 		goto out;
576 	}
577 
578 	nvl_ret = nvlist_pack(update->xgo_update, &update_buf, &pack_size,
579 	    NV_ENCODE_NATIVE, 0);
580 	if (nvl_ret != 0) {
581 		ret = xpio_error(xpio, XPIO_ERR_INTERNAL, nvl_ret, "failed to "
582 		    "pack update data: %s", strerror(nvl_ret));
583 		goto out;
584 	}
585 
586 	(void) memset(&kgu, '\0', sizeof (kgpio_update_t));
587 	kgu.kgu_id = update->xgo_gpio->xgi_id;
588 	kgu.kgu_attr = (uintptr_t)update_buf;
589 	kgu.kgu_attr_len = pack_size;
590 	kgu.kgu_err = (uintptr_t)err_buf;
591 	kgu.kgu_err_len = XPIO_NVL_LEN;
592 
593 	if (ioctl(ctrl->xc_fd, KGPIO_IOC_GPIO_UPDATE, &kgu) != 0) {
594 		int e = errno;
595 		ret = xpio_error(xpio, XPIO_ERR_KGPIO, e, "failed to isue "
596 		    "gpio attribute update ioctl to %s, %u: %s", ctrl->xc_name,
597 		    update->xgo_gpio->xgi_id, strerror(e));
598 		goto out;
599 	}
600 
601 	/*
602 	 * With no flags and a zero return value, this was successful. That's
603 	 * good.
604 	 */
605 	if (kgu.kgu_flags == 0) {
606 		ret = xpio_success(xpio);
607 		goto out;
608 	}
609 
610 	/*
611 	 * We should have packed information. Attempt to serialize it back into
612 	 * an nvlist for allowing the user to understand what happened.
613 	 */
614 	if ((kgu.kgu_flags & KGPIO_UPDATE_ERR_NVL_VALID) != 0) {
615 		nvl_ret = nvlist_unpack((char *)kgu.kgu_err, kgu.kgu_err_len,
616 		    &update->xgo_err_nvl, 0);
617 		if (nvl_ret != 0) {
618 			ret = xpio_error(xpio, XPIO_ERR_INTERNAL, nvl_ret,
619 			    "kernel gave us an unparseable error nvlist_t for "
620 			    "update failure: %s", strerror(nvl_ret));
621 			goto out;
622 		}
623 	}
624 
625 	ret = xpio_error(xpio, XPIO_ERR_BAD_UPDATE, 0, "failed to apply GPIO "
626 	    "update, invalid or unsupported attributes");
627 out:
628 	free(update_buf);
629 	free(err_buf);
630 	return (ret);
631 }
632 
633 bool
xpio_gpio_lookup_id(xpio_ctrl_t * ctrl,const char * name,uint32_t * idp)634 xpio_gpio_lookup_id(xpio_ctrl_t *ctrl, const char *name, uint32_t *idp)
635 {
636 	xpio_t *xpio = ctrl->xc_xpio;
637 	kgpio_ioc_name2id_t id;
638 
639 	if (name == NULL) {
640 		return (xpio_error(xpio, XPIO_ERR_BAD_PTR, 0, "encountered "
641 		    "invalid name pointer: %p", name));
642 	}
643 
644 	if (idp == NULL) {
645 		return (xpio_error(xpio, XPIO_ERR_BAD_PTR, 0, "encountered "
646 		    "invalid id pointer: %p", idp));
647 	}
648 
649 	(void) memset(&id, 0, sizeof (id));
650 
651 	if (strlcpy(id.kin_name, name, sizeof (id.kin_name)) >=
652 	    sizeof (id.kin_name)) {
653 		return (xpio_error(xpio, XPIO_ERR_BAD_GPIO_NAME, 0, "GPIO name "
654 		    "'%s' is too long and invalid", name));
655 	}
656 
657 	if (ioctl(ctrl->xc_fd, KGPIO_IOC_GPIO_NAME2ID, &id) != 0) {
658 		int e = errno;
659 		switch (e) {
660 		case ENOENT:
661 			return (xpio_error(xpio, XPIO_ERR_NO_LOOKUP_MATCH, 0,
662 			    "GPIO name '%s' is unknown on controller %s",
663 			    name, ctrl->xc_name));
664 		case EINVAL:
665 			return (xpio_error(xpio, XPIO_ERR_BAD_GPIO_NAME, 0,
666 			    "GPIO name '%s' is invalid", name));
667 		default:
668 			return (xpio_error(xpio, XPIO_ERR_KGPIO, e,
669 			    "failed to issue GPIO name to GPIO id ioctl to "
670 			    "%s: %s", ctrl->xc_name, strerror(e)));
671 		}
672 	}
673 
674 	*idp = id.kin_id;
675 	return (xpio_success(xpio));
676 }
677 
678 bool
xpio_gpio_update_init(xpio_t * xpio,xpio_gpio_info_t * gi,xpio_gpio_update_t ** outp)679 xpio_gpio_update_init(xpio_t *xpio, xpio_gpio_info_t *gi,
680     xpio_gpio_update_t **outp)
681 {
682 	int ret;
683 	xpio_gpio_update_t *update;
684 
685 	if (gi == NULL) {
686 		return (xpio_error(xpio, XPIO_ERR_BAD_PTR, 0, "encountered "
687 		    "invalid xpio_gpio_info_t pointer: %p", gi));
688 	}
689 
690 	if (outp == NULL) {
691 		return (xpio_error(xpio, XPIO_ERR_BAD_PTR, 0, "encountered "
692 		    "invalid xpio_gpio_update_t output pointer: %p", outp));
693 	}
694 
695 	update = calloc(1, sizeof (xpio_gpio_update_t));
696 	if (update == NULL) {
697 		int e = errno;
698 		return (xpio_error(xpio, XPIO_ERR_NO_MEM, e, "failed to "
699 		    "allocate memory for xpio_gpio_update_t: %s", strerror(e)));
700 	}
701 
702 	ret = nvlist_alloc(&update->xgo_update, NV_UNIQUE_NAME, 0);
703 	if (ret != 0) {
704 		free(update);
705 		if (ret == ENOMEM) {
706 			return (xpio_error(xpio, XPIO_ERR_NO_MEM, ret, "failed "
707 			    "to allocate nvlist_t for xpio_gpio_update_t"));
708 		}
709 
710 		return (xpio_error(xpio, XPIO_ERR_INTERNAL, ret, "failed to "
711 		    "create nvlist_t for xpio_gpio_update_t: %s",
712 		    strerror(ret)));
713 	}
714 
715 	update->xgo_gpio = gi;
716 	*outp = update;
717 	return (xpio_success(xpio));
718 }
719 
720 void
xpio_fini(xpio_t * xpio)721 xpio_fini(xpio_t *xpio)
722 {
723 	if (xpio == NULL)
724 		return;
725 
726 	if (xpio->xp_devinfo != DI_NODE_NIL) {
727 		di_fini(xpio->xp_devinfo);
728 		xpio->xp_devinfo = NULL;
729 	}
730 
731 	free(xpio);
732 }
733 
734 xpio_t *
xpio_init(void)735 xpio_init(void)
736 {
737 	xpio_t *xpio;
738 
739 	xpio = calloc(1, sizeof (xpio_t));
740 	if (xpio == NULL) {
741 		return (NULL);
742 	}
743 	xpio->xp_err = XPIO_ERR_OK;
744 
745 	xpio->xp_devinfo = di_init("/", DINFOCPYALL);
746 	if (xpio->xp_devinfo == DI_NODE_NIL) {
747 		xpio_fini(xpio);
748 		return (NULL);
749 	}
750 
751 	return (xpio);
752 }
753