xref: /illumos-gate/usr/src/lib/libxpio/common/libxpio_attr.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  * This file implements all of the access to and manipulating of attributes. An
18  * attribute is a thinly veiled reference to the underlying nvlist_t of data
19  * that we were given. Attributes are treated as the underlying nvpair data.
20  * This gets us out of some allocation bits, but means that we need to always
21  * ask for the gpio information itself depending on what we're trying to do as
22  * that has a pointer to our nvlist.
23  */
24 
25 #include <strings.h>
26 #include <sys/gpio/zen_gpio.h>
27 #include <sys/gpio/gpio_sim.h>
28 #include <sys/sysmacros.h>
29 
30 #include "libxpio_impl.h"
31 
32 /*
33  * These are data tables that exist for each attribute. They provide a general
34  * means of mapping between a string and a known uint32_t value. Currently we
35  * assume that these strings do not need translation and localization. There
36  * should be one table of values which is then wrapped up inside something else.
37  */
38 typedef struct {
39 	uint32_t xp_val;
40 	const char *xp_name;
41 } xpio_pair_t;
42 
43 static const xpio_pair_t zen_gpio_pad_pairs[] = {
44 	{ ZEN_GPIO_PAD_TYPE_GPIO, "gpio" },
45 	{ ZEN_GPIO_PAD_TYPE_SD, "sd" },
46 	{ ZEN_GPIO_PAD_TYPE_I2C, "i2c" },
47 	{ ZEN_GPIO_PAD_TYPE_I3C, "i3c" },
48 	{ 0x00, NULL }
49 };
50 
51 static const xpio_pair_t zen_gpio_cap_pairs[] = {
52 	{ ZEN_GPIO_C_AGPIO, "AGPIO" },
53 	{ ZEN_GPIO_C_REMOTE, "Remote" },
54 	{ 0x00, NULL }
55 };
56 
57 static const xpio_pair_t zen_gpio_driver_pairs[] = {
58 	{ ZEN_GPIO_DRIVER_UNKNOWN, "unknown" },
59 	{ ZEN_GPIO_DRIVER_PUSH_PULL, "push-pull" },
60 	{ ZEN_GPIO_DRIVER_OPEN_DRAIN, "open-drain" },
61 	{ 0x00, NULL }
62 };
63 
64 static const xpio_pair_t zen_gpio_output_pairs[] = {
65 	{ ZEN_GPIO_OUTPUT_DISABLED, "disabled" },
66 	{ ZEN_GPIO_OUTPUT_LOW, "low" },
67 	{ ZEN_GPIO_OUTPUT_HIGH, "high" },
68 	{ 0x00, NULL }
69 };
70 
71 static const xpio_pair_t zen_gpio_input_pairs[] = {
72 	{ ZEN_GPIO_INPUT_LOW, "low" },
73 	{ ZEN_GPIO_INPUT_HIGH, "high" },
74 	{ 0x00, NULL }
75 };
76 
77 static const xpio_pair_t zen_gpio_voltage_pairs[] = {
78 	{ ZEN_GPIO_V_UNKNOWN, "unknown" },
79 	{ ZEN_GPIO_V_1P1_S3, "1.1V" },
80 	{ ZEN_GPIO_V_1P8_S5, "1.8V" },
81 	{ ZEN_GPIO_V_1P8_S0, "1.8V" },
82 	{ ZEN_GPIO_V_3P3_S5, "3.3V" },
83 	{ ZEN_GPIO_V_3P3_S0, "3.3V" },
84 	{ 0x00, NULL }
85 };
86 
87 static const xpio_pair_t zen_gpio_pull_pairs[] = {
88 	{ ZEN_GPIO_PULL_DISABLED, "disabled" },
89 	{ ZEN_GPIO_PULL_DOWN, "down" },
90 	{ ZEN_GPIO_PULL_UP_4K, "4k-up" },
91 	{ ZEN_GPIO_PULL_UP_8K, "8k-up" },
92 	{ ZEN_GPIO_PULL_UP, "up" },
93 	{ ZEN_GPIO_PULL_DOWN_UP, "up|down" },
94 	{ ZEN_GPIO_PULL_DOWN_UP_4K, "4k-up|down" },
95 	{ ZEN_GPIO_PULL_DOWN_UP_8K, "8k-up|down" },
96 	{ 0x00, NULL }
97 };
98 
99 static const xpio_pair_t zen_gpio_drive_pairs[] = {
100 	{ ZEN_GPIO_DRIVE_UNKNOWN, "unknown" },
101 	{ ZEN_GPIO_DRIVE_40R, "40R" },
102 	{ ZEN_GPIO_DRIVE_60R, "60R" },
103 	{ ZEN_GPIO_DRIVE_80R, "80R" },
104 	{ 0x00, NULL }
105 };
106 
107 static const xpio_pair_t zen_gpio_dbt_mode_pairs[] = {
108 	{ ZEN_GPIO_DEBOUNCE_MODE_NONE, "none" },
109 	{ ZEN_GPIO_DEBOUNCE_MODE_KEEP_LOW, "keep-low-glitch" },
110 	{ ZEN_GPIO_DEBOUNCE_MODE_KEEP_HIGH, "keep-high-glitch" },
111 	{ ZEN_GPIO_DEBOUNCE_MODE_REMOVE, "remove-glitch" },
112 	{ 0x00, NULL }
113 };
114 
115 static const xpio_pair_t zen_gpio_dbt_unit_pairs[] = {
116 	{ ZEN_GPIO_DEBOUNCE_UNIT_2RTC, "61us" },
117 	{ ZEN_GPIO_DEBOUNCE_UNIT_8RTC, "244us" },
118 	{ ZEN_GPIO_DEBOUNCE_UNIT_512RTC, "15.6ms" },
119 	{ ZEN_GPIO_DEBOUNCE_UNIT_2048RTC, "62.5ms" },
120 	{ 0x00, NULL }
121 };
122 
123 static const xpio_pair_t zen_gpio_trigger_mode_pairs[] = {
124 	{ ZEN_GPIO_TRIGGER_UNKNOWN, "unknown" },
125 	{ ZEN_GPIO_TRIGGER_EDGE_HIGH, "edge/high" },
126 	{ ZEN_GPIO_TRIGGER_EDGE_LOW, "edge/low" },
127 	{ ZEN_GPIO_TRIGGER_EDGE_BOTH, "edge/both" },
128 	{ ZEN_GPIO_TRIGGER_LEVEL_HIGH, "level/high" },
129 	{ ZEN_GPIO_TRIGGER_LEVEL_LOW, "level/low" },
130 	{ 0x00, NULL }
131 };
132 
133 static const xpio_pair_t zen_gpio_status_pairs[] = {
134 	{ ZEN_GPIO_STATUS_WAKE, "wake" },
135 	{ ZEN_GPIO_STATUS_INTR, "interrupt" },
136 	{ 0x00, NULL }
137 };
138 
139 static const xpio_pair_t gpio_sim_output_pairs[] = {
140 	{ GPIO_SIM_OUTPUT_DISABLED, "disabled" },
141 	{ GPIO_SIM_OUTPUT_LOW, "low" },
142 	{ GPIO_SIM_OUTPUT_HIGH, "high" },
143 	{ 0x00, NULL }
144 };
145 
146 static const xpio_pair_t gpio_sim_input_pairs[] = {
147 	{ GPIO_SIM_INPUT_LOW, "low" },
148 	{ GPIO_SIM_INPUT_HIGH, "high" },
149 	{ 0x00, NULL }
150 };
151 
152 static const xpio_pair_t gpio_sim_pull_pairs[] = {
153 	{ GPIO_SIM_PULL_DISABLED, "disabled" },
154 	{ GPIO_SIM_PULL_DOWN, "down" },
155 	{ GPIO_SIM_PULL_DOWN_23K, "23k-down" },
156 	{ GPIO_SIM_PULL_UP, "up" },
157 	{ GPIO_SIM_PULL_UP_5K, "5k-up" },
158 	{ GPIO_SIM_PULL_UP_40K, "40k-up" },
159 	{ GPIO_SIM_PULL_BOTH, "up|down" },
160 	{ 0x00, NULL }
161 };
162 
163 static const xpio_pair_t gpio_sim_voltage_pairs[] = {
164 	{ GPIO_SIM_VOLTAGE_1P8, "1.8V" },
165 	{ GPIO_SIM_VOLTAGE_3P3, "3.3V" },
166 	{ GPIO_SIM_VOLTAGE_12P0, "12.0V" },
167 	{ GPIO_SIM_VOLTAGE_54P5, "54.5V" },
168 	{ 0x00, NULL }
169 };
170 
171 static const xpio_pair_t gpio_sim_speed_pairs[] = {
172 	{ GPIO_SIM_SPEED_LOW, "low" },
173 	{ GPIO_SIM_SPEED_MEDIUM, "medium" },
174 	{ GPIO_SIM_SPEED_HIGH, "high" },
175 	{ GPIO_SIM_SPEED_VERY_HIGH, "very-high" },
176 	{ 0x00, NULL }
177 };
178 
179 /*
180  * These two different functions are intended for different uses. Basically
181  * today most attributes that providers expose are semantic enums that describe
182  * state. The tostr_f function pointer is intended for when translating from the
183  * provider's notion of it to a humans. The tou32_f is intended for translating
184  * from a human's notion to a providers. The latter translation still works for
185  * properties that are read-only, mainly because it is the provider's job to be
186  * the source of truth for what is read-only or not.
187  */
188 typedef bool (*xpio_xlate_tostr_f)(const uint32_t, const xpio_pair_t *, char *,
189     size_t);
190 typedef bool (*xpio_xlate_tou32_f)(const char *, const xpio_pair_t *,
191     uint32_t *);
192 
193 typedef struct {
194 	const char *xt_name;
195 	const xpio_pair_t *xt_pairs;
196 	xpio_xlate_tostr_f xt_xlate_tostr;
197 	xpio_xlate_tou32_f xt_xlate_tou32;
198 } xpio_translate_t;
199 
200 static bool
201 xpio_attr_xlate_tostr_direct(const uint32_t val, const xpio_pair_t *pairs,
202     char *buf, size_t buflen)
203 {
204 	for (uint_t i = 0; pairs[i].xp_name != NULL; i++) {
205 		if (val == pairs[i].xp_val) {
206 			return (strlcpy(buf, pairs[i].xp_name, buflen) <
207 			    buflen);
208 		}
209 	}
210 	return (false);
211 }
212 
213 static bool
214 xpio_attr_xlate_tou32_direct(const char *str, const xpio_pair_t *pairs,
215     uint32_t *outp)
216 {
217 	for (uint_t i = 0; pairs[i].xp_name != NULL; i++) {
218 		if (strcmp(str, pairs[i].xp_name) == 0) {
219 			*outp = pairs[i].xp_val;
220 			return (true);
221 		}
222 	}
223 	return (false);
224 }
225 
226 static bool
227 xpio_attr_xlate_tostr_bitfield(const uint32_t val, const xpio_pair_t *pairs,
228     char *buf, size_t buflen)
229 {
230 	size_t off = 0;
231 	bool first = true;
232 
233 	buf[0] = '\0';
234 	for (uint_t i = 0; pairs[i].xp_name != NULL; i++) {
235 		int ret;
236 
237 		if ((pairs[i].xp_val & val) != pairs[i].xp_val) {
238 			continue;
239 		}
240 
241 		ret = snprintf(buf + off, buflen - off, "%s%s",
242 		    first ? "" : "|", pairs[i].xp_name);
243 		if (ret >= (buflen - off)) {
244 			return (false);
245 		}
246 		off += ret;
247 		first = false;
248 	}
249 
250 	return (true);
251 }
252 
253 /*
254  * We expect a bit field to have a series of strings that are | delineated. This
255  * means that we need to search and only look at the portion of the string that
256  * matches the '|'. This leads to use needing to check two things for a match:
257  *
258  *   o That a strncmp() with the found string length matches.
259  *   o That the total length indicates the end of the pair's string. This must
260  *     be done after the first check as the first check returning zero
261  *     effectively gives us a guarantee on the pair's length and that the next
262  *     byte is valid.
263  */
264 static bool
265 xpio_attr_xlate_tou32_bitfield(const char *str, const xpio_pair_t *pairs,
266     uint32_t *outp)
267 {
268 	*outp = 0;
269 	bool found = false;
270 
271 	while (*str != '\0') {
272 		size_t len;
273 		const char *pipe = strchr(str, '|');
274 		bool match = false;
275 
276 		if (pipe != NULL) {
277 			len = (uintptr_t)pipe - (uintptr_t)str;
278 		} else {
279 			len = strlen(str);
280 		}
281 
282 		for (uint_t i = 0; pairs[i].xp_name != NULL; i++) {
283 			if (strncmp(pairs[i].xp_name, str, len) == 0 &&
284 			    pairs[i].xp_name[len] == '\0') {
285 				found = true;
286 				match = true;
287 				*outp |= pairs[i].xp_val;
288 				match = true;
289 				break;
290 			}
291 		}
292 
293 		if (!match) {
294 			return (false);
295 		}
296 
297 		if (pipe != NULL) {
298 			str = pipe + 1;
299 		} else {
300 			break;
301 		}
302 	}
303 
304 	return (found);
305 }
306 
307 static bool
308 xpio_attr_xlate_tostr_hex(const uint32_t val, const xpio_pair_t *pairs,
309     char *buf, size_t buflen)
310 {
311 	return (snprintf(buf, buflen, "0x%x", val) < buflen);
312 }
313 
314 static bool
315 xpio_attr_xlate_tou32_hex(const char *str, const xpio_pair_t *pairs,
316     uint32_t *outp)
317 {
318 	char *eptr;
319 	unsigned long long l;
320 
321 	errno = 0;
322 	l = strtoull(str, &eptr, 0);
323 	if (errno != 0 || *eptr != '\0' || l > UINT32_MAX) {
324 		return (false);
325 	}
326 
327 	*outp = (uint32_t)l;
328 	return (true);
329 }
330 
331 static const xpio_translate_t xpio_attr_xlates[] = {
332 	/* zen_gpio(4D) attrs */
333 	{ ZEN_GPIO_ATTR_PAD_TYPE, zen_gpio_pad_pairs,
334 	    xpio_attr_xlate_tostr_direct, xpio_attr_xlate_tou32_direct },
335 	{ ZEN_GPIO_ATTR_CAPS, zen_gpio_cap_pairs,
336 	    xpio_attr_xlate_tostr_bitfield, xpio_attr_xlate_tou32_bitfield },
337 	{ ZEN_GPIO_ATTR_OUTPUT_DRIVER, zen_gpio_driver_pairs,
338 	    xpio_attr_xlate_tostr_direct, xpio_attr_xlate_tou32_direct },
339 	{ ZEN_GPIO_ATTR_OUTPUT, zen_gpio_output_pairs,
340 	    xpio_attr_xlate_tostr_direct, xpio_attr_xlate_tou32_direct },
341 	{ ZEN_GPIO_ATTR_INPUT, zen_gpio_input_pairs,
342 	    xpio_attr_xlate_tostr_direct, xpio_attr_xlate_tou32_direct },
343 	{ ZEN_GPIO_ATTR_VOLTAGE, zen_gpio_voltage_pairs,
344 	    xpio_attr_xlate_tostr_direct, xpio_attr_xlate_tou32_direct },
345 	{ ZEN_GPIO_ATTR_PULL, zen_gpio_pull_pairs,
346 	    xpio_attr_xlate_tostr_direct, xpio_attr_xlate_tou32_direct },
347 	{ ZEN_GPIO_ATTR_DRIVE_STRENGTH, zen_gpio_drive_pairs,
348 	    xpio_attr_xlate_tostr_direct, xpio_attr_xlate_tou32_direct },
349 	{ ZEN_GPIO_ATTR_DEBOUNCE_MODE, zen_gpio_dbt_mode_pairs,
350 	    xpio_attr_xlate_tostr_direct, xpio_attr_xlate_tou32_direct },
351 	{ ZEN_GPIO_ATTR_DEBOUNCE_UNIT, zen_gpio_dbt_unit_pairs,
352 	    xpio_attr_xlate_tostr_direct, xpio_attr_xlate_tou32_direct },
353 	{ ZEN_GPIO_ATTR_DEBOUNCE_COUNT, NULL, xpio_attr_xlate_tostr_hex,
354 	    xpio_attr_xlate_tou32_hex },
355 	{ ZEN_GPIO_ATTR_TRIGGER_MODE, zen_gpio_trigger_mode_pairs,
356 	    xpio_attr_xlate_tostr_direct, xpio_attr_xlate_tou32_direct },
357 	{ ZEN_GPIO_ATTR_STATUS, zen_gpio_status_pairs,
358 	    xpio_attr_xlate_tostr_bitfield, xpio_attr_xlate_tou32_bitfield },
359 	{ ZEN_GPIO_ATTR_RAW_REG, NULL, xpio_attr_xlate_tostr_hex,
360 	    xpio_attr_xlate_tou32_hex },
361 	/* gpio_sim(4D) attrs */
362 	{ GPIO_SIM_ATTR_OUTPUT, gpio_sim_output_pairs,
363 	    xpio_attr_xlate_tostr_direct, xpio_attr_xlate_tou32_direct },
364 	{ GPIO_SIM_ATTR_INPUT, gpio_sim_input_pairs,
365 	    xpio_attr_xlate_tostr_direct, xpio_attr_xlate_tou32_direct },
366 	{ GPIO_SIM_ATTR_PULL, gpio_sim_pull_pairs,
367 	    xpio_attr_xlate_tostr_direct, xpio_attr_xlate_tou32_direct },
368 	{ GPIO_SIM_ATTR_VOLTAGE, gpio_sim_voltage_pairs,
369 	    xpio_attr_xlate_tostr_direct, xpio_attr_xlate_tou32_direct },
370 	{ GPIO_SIM_ATTR_SPEED, gpio_sim_speed_pairs,
371 	    xpio_attr_xlate_tostr_direct, xpio_attr_xlate_tou32_direct },
372 };
373 
374 bool
375 xpio_gpio_attr_xlate_uint32_to_str(xpio_gpio_info_t *gi, xpio_gpio_attr_t *attr,
376     uint32_t val, char *buf, size_t buflen)
377 {
378 	const char *name = xpio_gpio_attr_name(gi, attr);
379 
380 	for (size_t i = 0; i < ARRAY_SIZE(xpio_attr_xlates); i++) {
381 		if (strcmp(name, xpio_attr_xlates[i].xt_name) != 0)
382 			continue;
383 
384 		return (xpio_attr_xlates[i].xt_xlate_tostr(val,
385 		    xpio_attr_xlates[i].xt_pairs, buf, buflen));
386 	}
387 
388 	return (false);
389 }
390 
391 bool
392 xpio_gpio_attr_xlate_to_str(xpio_gpio_info_t *gi, xpio_gpio_attr_t *attr,
393     char *buf, size_t buflen)
394 {
395 	uint32_t val;
396 
397 	if (!xpio_gpio_attr_value_uint32(attr, &val)) {
398 		return (false);
399 	}
400 
401 	return (xpio_gpio_attr_xlate_uint32_to_str(gi, attr, val, buf, buflen));
402 }
403 
404 xpio_gpio_attr_t *
405 xpio_gpio_attr_find(xpio_gpio_info_t *gi, const char *name)
406 {
407 	nvpair_t *pair;
408 
409 	if (strcmp(name, KGPIO_ATTR_META) == 0) {
410 		return (NULL);
411 	}
412 
413 	if (nvlist_lookup_nvpair(gi->xgi_nvl, name, &pair) != 0) {
414 		return (NULL);
415 	}
416 
417 	switch (nvpair_type(pair)) {
418 	case DATA_TYPE_UINT32:
419 	case DATA_TYPE_STRING:
420 		break;
421 	default:
422 		return (NULL);
423 	}
424 
425 	return ((xpio_gpio_attr_t *)pair);
426 }
427 
428 xpio_gpio_attr_t *
429 xpio_gpio_attr_next(xpio_gpio_info_t *gi, xpio_gpio_attr_t *attr)
430 {
431 	nvpair_t *pair_in = (nvpair_t *)attr;
432 
433 	for (;;) {
434 		nvpair_t *next = nvlist_next_nvpair(gi->xgi_nvl, pair_in);
435 		if (next == NULL) {
436 			return (NULL);
437 		}
438 
439 		switch (nvpair_type(next)) {
440 		case DATA_TYPE_UINT32:
441 		case DATA_TYPE_STRING:
442 			break;
443 		default:
444 			pair_in = next;
445 			continue;
446 		}
447 
448 		if (strcmp(KGPIO_ATTR_META, nvpair_name(next)) == 0) {
449 			pair_in = next;
450 			continue;
451 		}
452 
453 		return ((xpio_gpio_attr_t *)next);
454 	}
455 }
456 
457 const char *
458 xpio_gpio_attr_name(xpio_gpio_info_t *gi, xpio_gpio_attr_t *attr)
459 {
460 	nvpair_t *pair = (nvpair_t *)attr;
461 	return (nvpair_name(pair));
462 }
463 
464 xpio_attr_type_t
465 xpio_gpio_attr_type(xpio_gpio_info_t *gi, xpio_gpio_attr_t *attr)
466 {
467 	nvpair_t *pair = (nvpair_t *)attr;
468 
469 	/*
470 	 * We should only have handed out an nvpair that matches these two types
471 	 * at this point.
472 	 */
473 	switch (nvpair_type(pair)) {
474 	case DATA_TYPE_UINT32:
475 		return (XPIO_ATTR_TYPE_UINT32);
476 	case DATA_TYPE_STRING:
477 		return (XPIO_ATTR_TYPE_STRING);
478 	default:
479 		abort();
480 	}
481 }
482 
483 static const char *
484 xpio_gpio_attr_type_name(xpio_attr_type_t type)
485 {
486 	switch (type) {
487 	case XPIO_ATTR_TYPE_UINT32:
488 		return ("XPIO_ATTR_TYPE_UINT32");
489 	case XPIO_ATTR_TYPE_STRING:
490 		return ("XPIO_ATTR_TYPE_STRING");
491 	default:
492 		abort();
493 	}
494 }
495 
496 bool
497 xpio_gpio_attr_value_string(xpio_gpio_attr_t *attr, const char **outp)
498 {
499 	char *lookup;
500 
501 	if (nvpair_value_string((nvpair_t *)attr, &lookup) != 0) {
502 		return (false);
503 	}
504 
505 	*outp = lookup;
506 	return (true);
507 }
508 
509 bool
510 xpio_gpio_attr_value_uint32(xpio_gpio_attr_t *attr, uint32_t *outp)
511 {
512 	return (nvpair_value_uint32((nvpair_t *)attr, outp) == 0);
513 }
514 
515 void
516 xpio_gpio_attr_possible_string(xpio_gpio_info_t *gi, xpio_gpio_attr_t *attr,
517     const char ***outp, uint_t *countp)
518 {
519 	nvlist_t *meta_nvl, *attr_nvl;
520 	const char *key = nvpair_name((nvpair_t *)attr);
521 	char **strp;
522 
523 	*outp = NULL;
524 	*countp = 0;
525 
526 	if (nvlist_lookup_nvlist(gi->xgi_nvl, KGPIO_ATTR_META, &meta_nvl) !=
527 	    0) {
528 		return;
529 	}
530 
531 	if (nvlist_lookup_nvlist(meta_nvl, key, &attr_nvl) != 0) {
532 		return;
533 	}
534 
535 	if (nvlist_lookup_string_array(attr_nvl, KGPIO_ATTR_POS, &strp,
536 	    countp) != 0) {
537 		*outp = (const char **)strp;
538 	}
539 }
540 
541 void
542 xpio_gpio_attr_possible_uint32(xpio_gpio_info_t *gi, xpio_gpio_attr_t *attr,
543     uint32_t **outp, uint_t *countp)
544 {
545 	nvlist_t *meta_nvl, *attr_nvl;
546 	const char *key = nvpair_name((nvpair_t *)attr);
547 
548 	*outp = NULL;
549 	*countp = 0;
550 
551 	if (nvlist_lookup_nvlist(gi->xgi_nvl, KGPIO_ATTR_META, &meta_nvl) !=
552 	    0) {
553 		return;
554 	}
555 
556 	if (nvlist_lookup_nvlist(meta_nvl, key, &attr_nvl) != 0) {
557 		return;
558 	}
559 
560 	(void) nvlist_lookup_uint32_array(attr_nvl, KGPIO_ATTR_POS, outp,
561 	    countp);
562 }
563 
564 xpio_attr_prot_t
565 xpio_gpio_attr_prot(xpio_gpio_info_t *gi, xpio_gpio_attr_t *attr)
566 {
567 	uint32_t prot;
568 	nvlist_t *meta_nvl, *attr_nvl;
569 	const char *key = nvpair_name((nvpair_t *)attr);
570 
571 	if (nvlist_lookup_nvlist(gi->xgi_nvl, KGPIO_ATTR_META, &meta_nvl) !=
572 	    0) {
573 		return (XPIO_ATTR_PROT_RO);
574 	}
575 
576 	if (nvlist_lookup_nvlist(meta_nvl, key, &attr_nvl) != 0) {
577 		return (XPIO_ATTR_PROT_RO);
578 	}
579 
580 	if (nvlist_lookup_uint32(attr_nvl, KGPIO_ATTR_PROT, &prot) != 0) {
581 		return (XPIO_ATTR_PROT_RO);
582 	}
583 
584 	switch (prot) {
585 	case KGPIO_PROT_RW:
586 		return (XPIO_ATTR_PROT_RW);
587 	case KGPIO_PROT_RO:
588 	default:
589 		return (XPIO_ATTR_PROT_RO);
590 	}
591 }
592 
593 bool
594 xpio_gpio_attr_set_uint32(xpio_gpio_update_t *update, xpio_gpio_attr_t *attr,
595     uint32_t val)
596 {
597 	int ret;
598 	xpio_attr_type_t type = xpio_gpio_attr_type(update->xgo_gpio, attr);
599 	const char *key = xpio_gpio_attr_name(update->xgo_gpio, attr);
600 
601 	if (type != XPIO_ATTR_TYPE_UINT32) {
602 		return (xpio_update_error(update, XPIO_UPDATE_ERR_BAD_TYPE, 0,
603 		    "attribute type for %s is %s, not a uint32", key,
604 		    xpio_gpio_attr_type_name(type)));
605 	}
606 
607 	ret = nvlist_add_uint32(update->xgo_update, key, val);
608 	switch (ret) {
609 	case 0:
610 		return (xpio_update_success(update));
611 	case ENOMEM:
612 		return (xpio_update_error(update, XPIO_UPDATE_ERR_NO_MEM, ret,
613 		    "failed to allocate memory to insert attribute %s into "
614 		    "update structure", key));
615 	default:
616 		return (xpio_update_error(update, XPIO_UPDATE_ERR_INTERNAL, ret,
617 		    "unexpected internal error while trying to insert "
618 		    "attribute %s into update structure: %s", key,
619 		    strerror(ret)));
620 	}
621 }
622 
623 bool
624 xpio_gpio_attr_set_str(xpio_gpio_update_t *update, xpio_gpio_attr_t *attr,
625     const char *val)
626 {
627 	int ret;
628 	xpio_attr_type_t type = xpio_gpio_attr_type(update->xgo_gpio, attr);
629 	const char *key = xpio_gpio_attr_name(update->xgo_gpio, attr);
630 
631 	if (type != XPIO_ATTR_TYPE_STRING) {
632 		return (xpio_update_error(update, XPIO_UPDATE_ERR_BAD_TYPE, 0,
633 		    "attribute type for %s is %s, not a string", key,
634 		    xpio_gpio_attr_type_name(type)));
635 	}
636 
637 	ret = nvlist_add_string(update->xgo_update, key, val);
638 	switch (ret) {
639 	case 0:
640 		return (xpio_update_success(update));
641 	case ENOMEM:
642 		return (xpio_update_error(update, XPIO_UPDATE_ERR_NO_MEM, ret,
643 		    "failed to allocate memory to insert attribute %s into "
644 		    "update structure", key));
645 	default:
646 		return (xpio_update_error(update, XPIO_UPDATE_ERR_INTERNAL, ret,
647 		    "unexpected internal error while trying to insert "
648 		    "attribute %s into update structure: %s", key,
649 		    strerror(ret)));
650 	}
651 }
652 
653 /*
654  * This update path attempts to translate the passed in string into the
655  * appropriate attribute type. This is designed for tools that want to work in
656  * the more human values that we have for various GPIO attributes.
657  */
658 bool
659 xpio_gpio_attr_from_str(xpio_gpio_update_t *update, xpio_gpio_attr_t *attr,
660     const char *raw_val)
661 {
662 	xpio_attr_type_t type = xpio_gpio_attr_type(update->xgo_gpio, attr);
663 	const char *key = xpio_gpio_attr_name(update->xgo_gpio, attr);
664 
665 	/*
666 	 * If the data type for this is a string, then we can just insert the
667 	 * value as is and there is no need for translation. Otherwise, we must
668 	 * look at the data type and perform the appropriate translation.
669 	 */
670 	if (type == XPIO_ATTR_TYPE_STRING) {
671 		return (xpio_gpio_attr_set_str(update, attr, raw_val));
672 	}
673 
674 	for (size_t i = 0; i < ARRAY_SIZE(xpio_attr_xlates); i++) {
675 		uint32_t u32;
676 
677 		if (strcmp(key, xpio_attr_xlates[i].xt_name) != 0)
678 			continue;
679 		if (!xpio_attr_xlates[i].xt_xlate_tou32(raw_val,
680 		    xpio_attr_xlates[i].xt_pairs, &u32)) {
681 			return (xpio_update_error(update,
682 			    XPIO_UPDATE_ERR_CANT_XLATE, 0, "failed to  "
683 			    "translate attribute %s value %s to a uint32",
684 			    key, raw_val));
685 		}
686 
687 		return (xpio_gpio_attr_set_uint32(update, attr, u32));
688 	}
689 
690 	return (xpio_update_error(update, XPIO_UPDATE_ERR_INTERNAL, ENOENT,
691 	    "missing internal translator for attr %s to type %s", key,
692 	    xpio_gpio_attr_type_name(type)));
693 
694 }
695 
696 xpio_gpio_attr_err_t *
697 xpio_gpio_attr_err_next(xpio_gpio_update_t *update, xpio_gpio_attr_err_t *cur)
698 {
699 	nvpair_t *pair_in = (nvpair_t *)cur;
700 
701 	if (update->xgo_err_nvl == NULL) {
702 		return (NULL);
703 	}
704 
705 	for (;;) {
706 		nvpair_t *next = nvlist_next_nvpair(update->xgo_err_nvl,
707 		    pair_in);
708 		if (next == NULL) {
709 			return (NULL);
710 		}
711 
712 		if (nvpair_type(next) != DATA_TYPE_UINT32) {
713 			pair_in = next;
714 			continue;
715 		}
716 
717 		return ((xpio_gpio_attr_err_t *)next);
718 	}
719 }
720 
721 const char *
722 xpio_gpio_attr_err_name(xpio_gpio_attr_err_t *err)
723 {
724 	nvpair_t *pair = (nvpair_t *)err;
725 	return (nvpair_name(pair));
726 }
727 
728 xpio_update_err_t
729 xpio_gpio_attr_err_err(xpio_gpio_attr_err_t *err)
730 {
731 	uint32_t val;
732 	nvpair_t *pair = (nvpair_t *)err;
733 
734 	if (nvpair_value_uint32(pair, &val) != 0) {
735 		return (XPIO_UPDATE_ERR_INTERNAL);
736 	}
737 
738 	switch (val) {
739 	case KGPIO_ATTR_ERR_OK:
740 		return (XPIO_UPDATE_ERR_OK);
741 	case KGPIO_ATTR_ERR_ATTR_RO:
742 		return (XPIO_UPDATE_ERR_RO);
743 	case KGPIO_ATTR_ERR_UNKNOWN_ATTR:
744 		return (XPIO_UPDATE_ERR_UNKNOWN_ATTR);
745 	case KGPIO_ATTR_ERR_BAD_TYPE:
746 		return (XPIO_UPDATE_ERR_BAD_TYPE);
747 	case KGPIO_ATTR_ERR_UNKNOWN_VAL:
748 		return (XPIO_UPDATE_ERR_CANT_XLATE);
749 	case KGPIO_ATTR_ERR_CANT_APPLY_VAL:
750 		return (XPIO_UPDATE_ERR_CANT_APPLY_VAL);
751 	default:
752 		return (XPIO_UPDATE_ERR_INTERNAL);
753 	}
754 }
755