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