xref: /freebsd/sys/dev/bhnd/nvram/bhnd_nvram_value_fmts.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
177cb4d3eSLandon J. Fuller /*-
277cb4d3eSLandon J. Fuller  * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
377cb4d3eSLandon J. Fuller  * All rights reserved.
477cb4d3eSLandon J. Fuller  *
577cb4d3eSLandon J. Fuller  * Redistribution and use in source and binary forms, with or without
677cb4d3eSLandon J. Fuller  * modification, are permitted provided that the following conditions
777cb4d3eSLandon J. Fuller  * are met:
877cb4d3eSLandon J. Fuller  * 1. Redistributions of source code must retain the above copyright
977cb4d3eSLandon J. Fuller  *    notice, this list of conditions and the following disclaimer,
1077cb4d3eSLandon J. Fuller  *    without modification.
1177cb4d3eSLandon J. Fuller  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
1277cb4d3eSLandon J. Fuller  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
1377cb4d3eSLandon J. Fuller  *    redistribution must be conditioned upon including a substantially
1477cb4d3eSLandon J. Fuller  *    similar Disclaimer requirement for further binary redistribution.
1577cb4d3eSLandon J. Fuller  *
1677cb4d3eSLandon J. Fuller  * NO WARRANTY
1777cb4d3eSLandon J. Fuller  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1877cb4d3eSLandon J. Fuller  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1977cb4d3eSLandon J. Fuller  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
2077cb4d3eSLandon J. Fuller  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
2177cb4d3eSLandon J. Fuller  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
2277cb4d3eSLandon J. Fuller  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2377cb4d3eSLandon J. Fuller  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2477cb4d3eSLandon J. Fuller  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
2577cb4d3eSLandon J. Fuller  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2677cb4d3eSLandon J. Fuller  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
2777cb4d3eSLandon J. Fuller  * THE POSSIBILITY OF SUCH DAMAGES.
2877cb4d3eSLandon J. Fuller  */
2977cb4d3eSLandon J. Fuller 
3077cb4d3eSLandon J. Fuller #include <sys/param.h>
3177cb4d3eSLandon J. Fuller 
3277cb4d3eSLandon J. Fuller #include <net/ethernet.h>
3377cb4d3eSLandon J. Fuller 
3477cb4d3eSLandon J. Fuller #ifdef _KERNEL
3577cb4d3eSLandon J. Fuller 
3677cb4d3eSLandon J. Fuller #include <sys/ctype.h>
3777cb4d3eSLandon J. Fuller #include <sys/kernel.h>
3877cb4d3eSLandon J. Fuller #include <sys/malloc.h>
3977cb4d3eSLandon J. Fuller #include <sys/systm.h>
4077cb4d3eSLandon J. Fuller 
4177cb4d3eSLandon J. Fuller #include <machine/_inttypes.h>
4277cb4d3eSLandon J. Fuller 
4377cb4d3eSLandon J. Fuller #else /* !_KERNEL */
4477cb4d3eSLandon J. Fuller 
4577cb4d3eSLandon J. Fuller #include <ctype.h>
4677cb4d3eSLandon J. Fuller #include <errno.h>
4777cb4d3eSLandon J. Fuller #include <inttypes.h>
4877cb4d3eSLandon J. Fuller #include <stdlib.h>
4977cb4d3eSLandon J. Fuller #include <string.h>
5077cb4d3eSLandon J. Fuller 
5177cb4d3eSLandon J. Fuller #endif /* _KERNEL */
5277cb4d3eSLandon J. Fuller 
5377cb4d3eSLandon J. Fuller #include "bhnd_nvram_private.h"
5477cb4d3eSLandon J. Fuller 
5577cb4d3eSLandon J. Fuller #include "bhnd_nvram_valuevar.h"
5677cb4d3eSLandon J. Fuller 
5777cb4d3eSLandon J. Fuller static bool		 bhnd_nvram_ident_octet_string(const char *inp,
5877cb4d3eSLandon J. Fuller 			     size_t ilen, char *delim, size_t *nelem);
5977cb4d3eSLandon J. Fuller static bool		 bhnd_nvram_ident_num_string(const char *inp,
6077cb4d3eSLandon J. Fuller 			     size_t ilen, u_int base, u_int *obase);
6177cb4d3eSLandon J. Fuller 
6277cb4d3eSLandon J. Fuller static int		 bhnd_nvram_val_bcm_macaddr_filter(
6358efe686SLandon J. Fuller 			     const bhnd_nvram_val_fmt **fmt, const void *inp,
6477cb4d3eSLandon J. Fuller 			     size_t ilen, bhnd_nvram_type itype);
6577cb4d3eSLandon J. Fuller static int		 bhnd_nvram_val_bcm_macaddr_encode(
6658efe686SLandon J. Fuller 			     bhnd_nvram_val *value, void *outp, size_t *olen,
6777cb4d3eSLandon J. Fuller 			     bhnd_nvram_type otype);
6877cb4d3eSLandon J. Fuller 
6977cb4d3eSLandon J. Fuller static int		 bhnd_nvram_val_bcm_macaddr_string_filter(
7058efe686SLandon J. Fuller 			     const bhnd_nvram_val_fmt **fmt, const void *inp,
7177cb4d3eSLandon J. Fuller 			     size_t ilen, bhnd_nvram_type itype);
7277cb4d3eSLandon J. Fuller static int		 bhnd_nvram_val_bcm_macaddr_string_encode_elem(
7358efe686SLandon J. Fuller 			     bhnd_nvram_val *value, const void *inp,
7477cb4d3eSLandon J. Fuller 			     size_t ilen, void *outp, size_t *olen,
7577cb4d3eSLandon J. Fuller 			     bhnd_nvram_type otype);
7677cb4d3eSLandon J. Fuller static const void 	*bhnd_nvram_val_bcm_macaddr_string_next(
7758efe686SLandon J. Fuller 			     bhnd_nvram_val *value, const void *prev,
7877cb4d3eSLandon J. Fuller 			     size_t *len);
7977cb4d3eSLandon J. Fuller 
8077cb4d3eSLandon J. Fuller static int		 bhnd_nvram_val_bcm_int_filter(
8158efe686SLandon J. Fuller 			     const bhnd_nvram_val_fmt **fmt, const void *inp,
8277cb4d3eSLandon J. Fuller 			     size_t ilen, bhnd_nvram_type itype);
8358efe686SLandon J. Fuller static int		 bhnd_nvram_val_bcm_int_encode(bhnd_nvram_val *value,
8477cb4d3eSLandon J. Fuller 			     void *outp, size_t *olen, bhnd_nvram_type otype);
8577cb4d3eSLandon J. Fuller 
8677cb4d3eSLandon J. Fuller static int		 bhnd_nvram_val_bcm_decimal_encode_elem(
8758efe686SLandon J. Fuller 			     bhnd_nvram_val *value, const void *inp,
8877cb4d3eSLandon J. Fuller 			     size_t ilen, void *outp, size_t *olen,
8977cb4d3eSLandon J. Fuller 			     bhnd_nvram_type otype);
9077cb4d3eSLandon J. Fuller static int		 bhnd_nvram_val_bcm_hex_encode_elem(
9158efe686SLandon J. Fuller 			     bhnd_nvram_val *value, const void *inp,
9277cb4d3eSLandon J. Fuller 			     size_t ilen, void *outp, size_t *olen,
9377cb4d3eSLandon J. Fuller 			     bhnd_nvram_type otype);
9477cb4d3eSLandon J. Fuller 
9577cb4d3eSLandon J. Fuller static int		 bhnd_nvram_val_bcm_leddc_filter(
9658efe686SLandon J. Fuller 			     const bhnd_nvram_val_fmt **fmt, const void *inp,
9777cb4d3eSLandon J. Fuller 			     size_t ilen, bhnd_nvram_type itype);
9877cb4d3eSLandon J. Fuller static int		 bhnd_nvram_val_bcm_leddc_encode_elem(
9958efe686SLandon J. Fuller 			     bhnd_nvram_val *value, const void *inp,
10077cb4d3eSLandon J. Fuller 			     size_t ilen, void *outp, size_t *olen,
10177cb4d3eSLandon J. Fuller 			     bhnd_nvram_type otype);
10277cb4d3eSLandon J. Fuller 
10358efe686SLandon J. Fuller static int		 bhnd_nvram_val_bcmstr_encode(bhnd_nvram_val *value,
10477cb4d3eSLandon J. Fuller 			     void *outp, size_t *olen, bhnd_nvram_type otype);
10577cb4d3eSLandon J. Fuller 
10677cb4d3eSLandon J. Fuller static int		 bhnd_nvram_val_bcmstr_csv_filter(
10758efe686SLandon J. Fuller 			     const bhnd_nvram_val_fmt **fmt, const void *inp,
10877cb4d3eSLandon J. Fuller 			     size_t ilen, bhnd_nvram_type itype);
10958efe686SLandon J. Fuller static const void	*bhnd_nvram_val_bcmstr_csv_next(bhnd_nvram_val *value,
11077cb4d3eSLandon J. Fuller 			     const void *prev, size_t *len);
11177cb4d3eSLandon J. Fuller 
11277cb4d3eSLandon J. Fuller /**
11377cb4d3eSLandon J. Fuller  * Broadcom NVRAM MAC address format.
11477cb4d3eSLandon J. Fuller  */
11558efe686SLandon J. Fuller const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_macaddr_fmt = {
11677cb4d3eSLandon J. Fuller 	.name		= "bcm-macaddr",
11777cb4d3eSLandon J. Fuller 	.native_type	= BHND_NVRAM_TYPE_UINT8_ARRAY,
11877cb4d3eSLandon J. Fuller 	.op_filter	= bhnd_nvram_val_bcm_macaddr_filter,
11977cb4d3eSLandon J. Fuller 	.op_encode	= bhnd_nvram_val_bcm_macaddr_encode,
12077cb4d3eSLandon J. Fuller };
12177cb4d3eSLandon J. Fuller 
12277cb4d3eSLandon J. Fuller /** Broadcom NVRAM MAC address string format. */
12358efe686SLandon J. Fuller static const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_macaddr_string_fmt = {
12477cb4d3eSLandon J. Fuller 	.name		= "bcm-macaddr-string",
12577cb4d3eSLandon J. Fuller 	.native_type	= BHND_NVRAM_TYPE_STRING,
12677cb4d3eSLandon J. Fuller 	.op_filter	= bhnd_nvram_val_bcm_macaddr_string_filter,
12777cb4d3eSLandon J. Fuller 	.op_encode_elem	= bhnd_nvram_val_bcm_macaddr_string_encode_elem,
12877cb4d3eSLandon J. Fuller 	.op_next	= bhnd_nvram_val_bcm_macaddr_string_next,
12977cb4d3eSLandon J. Fuller };
13077cb4d3eSLandon J. Fuller 
13177cb4d3eSLandon J. Fuller /**
13277cb4d3eSLandon J. Fuller  * Broadcom NVRAM LED duty-cycle format.
13377cb4d3eSLandon J. Fuller  */
13458efe686SLandon J. Fuller const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_leddc_fmt = {
13577cb4d3eSLandon J. Fuller 	.name		= "bcm-leddc",
13677cb4d3eSLandon J. Fuller 	.native_type	= BHND_NVRAM_TYPE_UINT32,
13777cb4d3eSLandon J. Fuller 	.op_filter	= bhnd_nvram_val_bcm_leddc_filter,
13877cb4d3eSLandon J. Fuller 	.op_encode_elem	= bhnd_nvram_val_bcm_leddc_encode_elem,
13977cb4d3eSLandon J. Fuller };
14077cb4d3eSLandon J. Fuller 
14177cb4d3eSLandon J. Fuller /**
14277cb4d3eSLandon J. Fuller  * Broadcom NVRAM decimal integer format.
14377cb4d3eSLandon J. Fuller  *
14477cb4d3eSLandon J. Fuller  * Extends standard integer handling, encoding the string representation of
14577cb4d3eSLandon J. Fuller  * the integer value as a decimal string:
14677cb4d3eSLandon J. Fuller  * - Positive values will be string-encoded without a prefix.
14777cb4d3eSLandon J. Fuller  * - Negative values will be string-encoded with a leading '-' sign.
14877cb4d3eSLandon J. Fuller  */
14958efe686SLandon J. Fuller const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_decimal_fmt = {
15077cb4d3eSLandon J. Fuller 	.name		= "bcm-decimal",
15177cb4d3eSLandon J. Fuller 	.native_type	= BHND_NVRAM_TYPE_UINT64,
15277cb4d3eSLandon J. Fuller 	.op_filter	= bhnd_nvram_val_bcm_int_filter,
15377cb4d3eSLandon J. Fuller 	.op_encode	= bhnd_nvram_val_bcm_int_encode,
15477cb4d3eSLandon J. Fuller 	.op_encode_elem	= bhnd_nvram_val_bcm_decimal_encode_elem,
15577cb4d3eSLandon J. Fuller };
15677cb4d3eSLandon J. Fuller 
15777cb4d3eSLandon J. Fuller /**
15877cb4d3eSLandon J. Fuller  * Broadcom NVRAM decimal integer format.
15977cb4d3eSLandon J. Fuller  *
16077cb4d3eSLandon J. Fuller  * Extends standard integer handling, encoding the string representation of
16177cb4d3eSLandon J. Fuller  * unsigned and positive signed integer values as an 0x-prefixed hexadecimal
16277cb4d3eSLandon J. Fuller  * string.
16377cb4d3eSLandon J. Fuller  *
16477cb4d3eSLandon J. Fuller  * For compatibility with standard Broadcom NVRAM parsing, if the integer is
16577cb4d3eSLandon J. Fuller  * both signed and negative, it will be string encoded as a negative decimal
16677cb4d3eSLandon J. Fuller  * value, not as a twos-complement hexadecimal value.
16777cb4d3eSLandon J. Fuller  */
16858efe686SLandon J. Fuller const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_hex_fmt = {
16977cb4d3eSLandon J. Fuller 	.name		= "bcm-hex",
17077cb4d3eSLandon J. Fuller 	.native_type	= BHND_NVRAM_TYPE_UINT64,
17177cb4d3eSLandon J. Fuller 	.op_filter	= bhnd_nvram_val_bcm_int_filter,
17277cb4d3eSLandon J. Fuller 	.op_encode	= bhnd_nvram_val_bcm_int_encode,
17377cb4d3eSLandon J. Fuller 	.op_encode_elem	= bhnd_nvram_val_bcm_hex_encode_elem,
17477cb4d3eSLandon J. Fuller };
17577cb4d3eSLandon J. Fuller 
17677cb4d3eSLandon J. Fuller /**
17777cb4d3eSLandon J. Fuller  * Broadcom NVRAM string format.
17877cb4d3eSLandon J. Fuller  *
17977cb4d3eSLandon J. Fuller  * Handles standard, comma-delimited, and octet-string values as used in
18077cb4d3eSLandon J. Fuller  * Broadcom NVRAM data.
18177cb4d3eSLandon J. Fuller  */
18258efe686SLandon J. Fuller const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_string_fmt = {
18377cb4d3eSLandon J. Fuller 	.name		= "bcm-string",
18477cb4d3eSLandon J. Fuller 	.native_type	= BHND_NVRAM_TYPE_STRING,
18577cb4d3eSLandon J. Fuller 	.op_encode	= bhnd_nvram_val_bcmstr_encode,
18677cb4d3eSLandon J. Fuller };
18777cb4d3eSLandon J. Fuller 
18877cb4d3eSLandon J. Fuller /** Broadcom comma-delimited string. */
18958efe686SLandon J. Fuller static const bhnd_nvram_val_fmt bhnd_nvram_val_bcm_string_csv_fmt = {
19077cb4d3eSLandon J. Fuller 	.name		= "bcm-string[]",
19177cb4d3eSLandon J. Fuller 	.native_type	= BHND_NVRAM_TYPE_STRING,
19277cb4d3eSLandon J. Fuller 	.op_filter	= bhnd_nvram_val_bcmstr_csv_filter,
19377cb4d3eSLandon J. Fuller 	.op_next	= bhnd_nvram_val_bcmstr_csv_next,
19477cb4d3eSLandon J. Fuller };
19577cb4d3eSLandon J. Fuller 
1969be0790dSLandon J. Fuller /* Built-in format definitions */
1979be0790dSLandon J. Fuller #define	BHND_NVRAM_VAL_FMT_NATIVE(_n, _type)				\
1989be0790dSLandon J. Fuller 	const bhnd_nvram_val_fmt bhnd_nvram_val_ ## _n ## _fmt = {	\
1999be0790dSLandon J. Fuller 		.name		= __STRING(_n),				\
2009be0790dSLandon J. Fuller 		.native_type	= BHND_NVRAM_TYPE_ ## _type,		\
2019be0790dSLandon J. Fuller 	}
2029be0790dSLandon J. Fuller 
2039be0790dSLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(uint8,	UINT8);
2049be0790dSLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(uint16,	UINT16);
2059be0790dSLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(uint32,	UINT32);
2069be0790dSLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(uint64,	UINT64);
2079be0790dSLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(int8,		INT8);
2089be0790dSLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(int16,	INT16);
2099be0790dSLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(int32,	INT32);
2109be0790dSLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(int64,	INT64);
2119be0790dSLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(char,		CHAR);
212*6cffadf0SLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(bool,		BOOL);
2139be0790dSLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(string,	STRING);
214*6cffadf0SLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(data,		DATA);
215*6cffadf0SLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(null,		NULL);
2169be0790dSLandon J. Fuller 
2179be0790dSLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(uint8_array,	UINT8_ARRAY);
2189be0790dSLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(uint16_array,	UINT16_ARRAY);
2199be0790dSLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(uint32_array,	UINT32_ARRAY);
2209be0790dSLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(uint64_array,	UINT64_ARRAY);
2219be0790dSLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(int8_array,	INT8_ARRAY);
2229be0790dSLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(int16_array,	INT16_ARRAY);
2239be0790dSLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(int32_array,	INT32_ARRAY);
2249be0790dSLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(int64_array,	INT64_ARRAY);
2259be0790dSLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(char_array,	CHAR_ARRAY);
226*6cffadf0SLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(bool_array,	BOOL_ARRAY);
2279be0790dSLandon J. Fuller BHND_NVRAM_VAL_FMT_NATIVE(string_array,	STRING_ARRAY);
2289be0790dSLandon J. Fuller 
22977cb4d3eSLandon J. Fuller /**
23077cb4d3eSLandon J. Fuller  * Common hex/decimal integer filter implementation.
23177cb4d3eSLandon J. Fuller  */
23277cb4d3eSLandon J. Fuller static int
bhnd_nvram_val_bcm_int_filter(const bhnd_nvram_val_fmt ** fmt,const void * inp,size_t ilen,bhnd_nvram_type itype)23358efe686SLandon J. Fuller bhnd_nvram_val_bcm_int_filter(const bhnd_nvram_val_fmt **fmt, const void *inp,
23477cb4d3eSLandon J. Fuller     size_t ilen, bhnd_nvram_type itype)
23577cb4d3eSLandon J. Fuller {
23677cb4d3eSLandon J. Fuller 	bhnd_nvram_type	itype_base;
23777cb4d3eSLandon J. Fuller 
23877cb4d3eSLandon J. Fuller 	itype_base = bhnd_nvram_base_type(itype);
23977cb4d3eSLandon J. Fuller 
24077cb4d3eSLandon J. Fuller 	switch (itype_base) {
24177cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_STRING:
24277cb4d3eSLandon J. Fuller 		/*
24377cb4d3eSLandon J. Fuller 		 * If the input is a string, delegate to the Broadcom
24477cb4d3eSLandon J. Fuller 		 * string format -- preserving the original string value
24577cb4d3eSLandon J. Fuller 		 * takes priority over enforcing hexadecimal/integer string
24677cb4d3eSLandon J. Fuller 		 * formatting.
24777cb4d3eSLandon J. Fuller 		 */
24877cb4d3eSLandon J. Fuller 		*fmt = &bhnd_nvram_val_bcm_string_fmt;
24977cb4d3eSLandon J. Fuller 		return (0);
25077cb4d3eSLandon J. Fuller 
25177cb4d3eSLandon J. Fuller 	default:
25277cb4d3eSLandon J. Fuller 		if (bhnd_nvram_is_int_type(itype_base))
25377cb4d3eSLandon J. Fuller 			return (0);
25477cb4d3eSLandon J. Fuller 
25577cb4d3eSLandon J. Fuller 		return (EFTYPE);
25677cb4d3eSLandon J. Fuller 	}
25777cb4d3eSLandon J. Fuller }
25877cb4d3eSLandon J. Fuller 
25977cb4d3eSLandon J. Fuller /**
26077cb4d3eSLandon J. Fuller  * Broadcom hex/decimal integer encode implementation.
26177cb4d3eSLandon J. Fuller  */
26277cb4d3eSLandon J. Fuller static int
bhnd_nvram_val_bcm_int_encode(bhnd_nvram_val * value,void * outp,size_t * olen,bhnd_nvram_type otype)26358efe686SLandon J. Fuller bhnd_nvram_val_bcm_int_encode(bhnd_nvram_val *value, void *outp, size_t *olen,
26477cb4d3eSLandon J. Fuller     bhnd_nvram_type otype)
26577cb4d3eSLandon J. Fuller {
26677cb4d3eSLandon J. Fuller 	/* If encoding to a string, format multiple elements (if any) with a
26777cb4d3eSLandon J. Fuller 	 * comma delimiter. */
26877cb4d3eSLandon J. Fuller 	if (otype == BHND_NVRAM_TYPE_STRING)
26977cb4d3eSLandon J. Fuller 		return (bhnd_nvram_val_printf(value, "%[]s", outp, olen, ","));
27077cb4d3eSLandon J. Fuller 
27177cb4d3eSLandon J. Fuller 	return (bhnd_nvram_val_generic_encode(value, outp, olen, otype));
27277cb4d3eSLandon J. Fuller }
27377cb4d3eSLandon J. Fuller 
27477cb4d3eSLandon J. Fuller /**
27577cb4d3eSLandon J. Fuller  * Broadcom hex integer encode_elem implementation.
27677cb4d3eSLandon J. Fuller  */
27777cb4d3eSLandon J. Fuller static int
bhnd_nvram_val_bcm_hex_encode_elem(bhnd_nvram_val * value,const void * inp,size_t ilen,void * outp,size_t * olen,bhnd_nvram_type otype)27858efe686SLandon J. Fuller bhnd_nvram_val_bcm_hex_encode_elem(bhnd_nvram_val *value, const void *inp,
27977cb4d3eSLandon J. Fuller     size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype)
28077cb4d3eSLandon J. Fuller {
28177cb4d3eSLandon J. Fuller 	bhnd_nvram_type	itype;
28277cb4d3eSLandon J. Fuller 	ssize_t		width;
28377cb4d3eSLandon J. Fuller 	int		error;
28477cb4d3eSLandon J. Fuller 
28577cb4d3eSLandon J. Fuller 	itype = bhnd_nvram_val_elem_type(value);
28677cb4d3eSLandon J. Fuller 	BHND_NV_ASSERT(bhnd_nvram_is_int_type(itype), ("invalid type"));
28777cb4d3eSLandon J. Fuller 
28877cb4d3eSLandon J. Fuller 	/* If not encoding as a string, perform generic value encoding */
28977cb4d3eSLandon J. Fuller 	if (otype != BHND_NVRAM_TYPE_STRING)
29077cb4d3eSLandon J. Fuller 		return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen,
29177cb4d3eSLandon J. Fuller 		    outp, olen, otype));
29277cb4d3eSLandon J. Fuller 
29377cb4d3eSLandon J. Fuller 	/* If the value is a signed, negative value, encode as a decimal
29477cb4d3eSLandon J. Fuller 	 * string */
29577cb4d3eSLandon J. Fuller 	if (bhnd_nvram_is_signed_type(itype)) {
29677cb4d3eSLandon J. Fuller 		int64_t		sval;
29777cb4d3eSLandon J. Fuller 		size_t		slen;
29877cb4d3eSLandon J. Fuller 		bhnd_nvram_type	stype;
29977cb4d3eSLandon J. Fuller 
30077cb4d3eSLandon J. Fuller 		stype = BHND_NVRAM_TYPE_INT64;
30177cb4d3eSLandon J. Fuller 		slen = sizeof(sval);
30277cb4d3eSLandon J. Fuller 
30377cb4d3eSLandon J. Fuller 		/* Fetch 64-bit signed representation */
30477cb4d3eSLandon J. Fuller 		error = bhnd_nvram_value_coerce(inp, ilen, itype, &sval, &slen,
30577cb4d3eSLandon J. Fuller 		    stype);
30677cb4d3eSLandon J. Fuller 		if (error)
30777cb4d3eSLandon J. Fuller 			return (error);
30877cb4d3eSLandon J. Fuller 
30977cb4d3eSLandon J. Fuller 		/* Decimal encoding required? */
31077cb4d3eSLandon J. Fuller 		if (sval < 0)
31177cb4d3eSLandon J. Fuller 			return (bhnd_nvram_value_printf("%I64d", &sval, slen,
31277cb4d3eSLandon J. Fuller 			    stype, outp, olen, otype));
31377cb4d3eSLandon J. Fuller 	}
31477cb4d3eSLandon J. Fuller 
31577cb4d3eSLandon J. Fuller 	/*
31677cb4d3eSLandon J. Fuller 	 * Encode the value as a hex string.
31777cb4d3eSLandon J. Fuller 	 *
31877cb4d3eSLandon J. Fuller 	 * Most producers of Broadcom NVRAM values zero-pad hex values out to
31977cb4d3eSLandon J. Fuller 	 * their native width (width * two hex characters), and we do the same
32077cb4d3eSLandon J. Fuller 	 * for compatibility
32177cb4d3eSLandon J. Fuller 	 */
3229be0790dSLandon J. Fuller 	width = bhnd_nvram_type_width(itype) * 2;
32377cb4d3eSLandon J. Fuller 	return (bhnd_nvram_value_printf("0x%0*I64X", inp, ilen, itype,
32477cb4d3eSLandon J. Fuller 	    outp, olen, width));
32577cb4d3eSLandon J. Fuller }
32677cb4d3eSLandon J. Fuller 
32777cb4d3eSLandon J. Fuller /**
32877cb4d3eSLandon J. Fuller  * Broadcom decimal integer encode_elem implementation.
32977cb4d3eSLandon J. Fuller  */
33077cb4d3eSLandon J. Fuller static int
bhnd_nvram_val_bcm_decimal_encode_elem(bhnd_nvram_val * value,const void * inp,size_t ilen,void * outp,size_t * olen,bhnd_nvram_type otype)33158efe686SLandon J. Fuller bhnd_nvram_val_bcm_decimal_encode_elem(bhnd_nvram_val *value, const void *inp,
33277cb4d3eSLandon J. Fuller     size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype)
33377cb4d3eSLandon J. Fuller {
33477cb4d3eSLandon J. Fuller 	const char	*sfmt;
33577cb4d3eSLandon J. Fuller 	bhnd_nvram_type	 itype;
33677cb4d3eSLandon J. Fuller 
33777cb4d3eSLandon J. Fuller 	itype = bhnd_nvram_val_elem_type(value);
33877cb4d3eSLandon J. Fuller 	BHND_NV_ASSERT(bhnd_nvram_is_int_type(itype), ("invalid type"));
33977cb4d3eSLandon J. Fuller 
34077cb4d3eSLandon J. Fuller 	/* If not encoding as a string, perform generic value encoding */
34177cb4d3eSLandon J. Fuller 	if (otype != BHND_NVRAM_TYPE_STRING)
34277cb4d3eSLandon J. Fuller 		return (bhnd_nvram_val_generic_encode_elem(value, inp, ilen,
34377cb4d3eSLandon J. Fuller 		    outp, olen, otype));
34477cb4d3eSLandon J. Fuller 
34577cb4d3eSLandon J. Fuller 	sfmt = bhnd_nvram_is_signed_type(itype) ? "%I64d" : "%I64u";
34677cb4d3eSLandon J. Fuller 	return (bhnd_nvram_value_printf(sfmt, inp, ilen, itype, outp, olen));
34777cb4d3eSLandon J. Fuller }
34877cb4d3eSLandon J. Fuller 
34977cb4d3eSLandon J. Fuller /**
35077cb4d3eSLandon J. Fuller  * Broadcom LED duty-cycle filter.
35177cb4d3eSLandon J. Fuller  */
35277cb4d3eSLandon J. Fuller static int
bhnd_nvram_val_bcm_leddc_filter(const bhnd_nvram_val_fmt ** fmt,const void * inp,size_t ilen,bhnd_nvram_type itype)35358efe686SLandon J. Fuller bhnd_nvram_val_bcm_leddc_filter(const bhnd_nvram_val_fmt **fmt,
35477cb4d3eSLandon J. Fuller     const void *inp, size_t ilen, bhnd_nvram_type itype)
35577cb4d3eSLandon J. Fuller {
35677cb4d3eSLandon J. Fuller 	const char	*p;
35777cb4d3eSLandon J. Fuller 	size_t		 plen;
35877cb4d3eSLandon J. Fuller 
35977cb4d3eSLandon J. Fuller 	switch (itype) {
36077cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_UINT16:
36177cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_UINT32:
36277cb4d3eSLandon J. Fuller 		return (0);
36377cb4d3eSLandon J. Fuller 
36477cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_STRING:
36577cb4d3eSLandon J. Fuller 		/* Trim any whitespace */
36677cb4d3eSLandon J. Fuller 		p = inp;
36777cb4d3eSLandon J. Fuller 		plen = bhnd_nvram_trim_field(&p, ilen, '\0');
36877cb4d3eSLandon J. Fuller 
36977cb4d3eSLandon J. Fuller 		/* If the value is not a valid integer string, delegate to the
37077cb4d3eSLandon J. Fuller 		 * Broadcom string format */
37177cb4d3eSLandon J. Fuller 		if (!bhnd_nvram_ident_num_string(p, plen, 0, NULL))
37277cb4d3eSLandon J. Fuller 			*fmt = &bhnd_nvram_val_bcm_string_fmt;
37377cb4d3eSLandon J. Fuller 
37477cb4d3eSLandon J. Fuller 		return (0);
37577cb4d3eSLandon J. Fuller 	default:
37677cb4d3eSLandon J. Fuller 		return (EFTYPE);
37777cb4d3eSLandon J. Fuller 	}
37877cb4d3eSLandon J. Fuller }
37977cb4d3eSLandon J. Fuller 
38077cb4d3eSLandon J. Fuller /**
38177cb4d3eSLandon J. Fuller  * Broadcom LED duty-cycle encode.
38277cb4d3eSLandon J. Fuller  */
38377cb4d3eSLandon J. Fuller static int
bhnd_nvram_val_bcm_leddc_encode_elem(bhnd_nvram_val * value,const void * inp,size_t ilen,void * outp,size_t * olen,bhnd_nvram_type otype)38458efe686SLandon J. Fuller bhnd_nvram_val_bcm_leddc_encode_elem(bhnd_nvram_val *value, const void *inp,
38577cb4d3eSLandon J. Fuller     size_t ilen, void *outp, size_t *olen, bhnd_nvram_type otype)
38677cb4d3eSLandon J. Fuller {
38777cb4d3eSLandon J. Fuller 	bhnd_nvram_type		itype;
38877cb4d3eSLandon J. Fuller 	size_t			limit, nbytes;
38977cb4d3eSLandon J. Fuller 	int			error;
39077cb4d3eSLandon J. Fuller 	uint16_t		led16;
39177cb4d3eSLandon J. Fuller 	uint32_t		led32;
39277cb4d3eSLandon J. Fuller 	bool			led16_lossy;
39377cb4d3eSLandon J. Fuller 	union {
39477cb4d3eSLandon J. Fuller 		uint16_t	u16;
39577cb4d3eSLandon J. Fuller 		uint32_t	u32;
39677cb4d3eSLandon J. Fuller 	} strval;
39777cb4d3eSLandon J. Fuller 
39877cb4d3eSLandon J. Fuller 	/*
39977cb4d3eSLandon J. Fuller 	 * LED duty-cycle values represent the on/off periods as a 32-bit
40077cb4d3eSLandon J. Fuller 	 * integer, with the top 16 bits representing on cycles, and the
40177cb4d3eSLandon J. Fuller 	 * bottom 16 representing off cycles.
40277cb4d3eSLandon J. Fuller 	 *
40377cb4d3eSLandon J. Fuller 	 * LED duty cycle values have three different formats:
40477cb4d3eSLandon J. Fuller 	 *
40577cb4d3eSLandon J. Fuller 	 * - SPROM:	A 16-bit unsigned integer, with on/off cycles encoded
40677cb4d3eSLandon J. Fuller 	 *		as 8-bit values.
40777cb4d3eSLandon J. Fuller 	 * - NVRAM:	A 16-bit decimal or hexadecimal string, with on/off
40877cb4d3eSLandon J. Fuller 	 *		cycles encoded as 8-bit values as per the SPROM format.
40977cb4d3eSLandon J. Fuller 	 * - NVRAM:	A 32-bit decimal or hexadecimal string, with on/off
41077cb4d3eSLandon J. Fuller 	 *		cycles encoded as 16-bit values.
41177cb4d3eSLandon J. Fuller 	 *
41277cb4d3eSLandon J. Fuller 	 * To convert from a 16-bit representation to a 32-bit representation:
41377cb4d3eSLandon J. Fuller 	 *     ((value & 0xFF00) << 16) | ((value & 0x00FF) << 8)
41477cb4d3eSLandon J. Fuller 	 *
41577cb4d3eSLandon J. Fuller 	 * To convert from a 32-bit representation to a 16-bit representation,
41677cb4d3eSLandon J. Fuller 	 * perform the same operation in reverse, discarding the lower 8-bits
41777cb4d3eSLandon J. Fuller 	 * of each half of the 32-bit representation:
41877cb4d3eSLandon J. Fuller 	 *     ((value >> 16) & 0xFF00) | ((value >> 8) & 0x00FF)
41977cb4d3eSLandon J. Fuller 	 */
42077cb4d3eSLandon J. Fuller 
42177cb4d3eSLandon J. Fuller 	itype = bhnd_nvram_val_elem_type(value);
42277cb4d3eSLandon J. Fuller 	nbytes = 0;
42377cb4d3eSLandon J. Fuller 	led16_lossy = false;
42477cb4d3eSLandon J. Fuller 
42577cb4d3eSLandon J. Fuller 	/* Determine output byte limit */
42677cb4d3eSLandon J. Fuller 	if (outp != NULL)
42777cb4d3eSLandon J. Fuller 		limit = *olen;
42877cb4d3eSLandon J. Fuller 	else
42977cb4d3eSLandon J. Fuller 		limit = 0;
43077cb4d3eSLandon J. Fuller 
43177cb4d3eSLandon J. Fuller 	/* If the input/output types match, just delegate to standard value
43277cb4d3eSLandon J. Fuller 	 * encoding support */
43377cb4d3eSLandon J. Fuller 	if (otype == itype) {
43477cb4d3eSLandon J. Fuller 		return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen,
43577cb4d3eSLandon J. Fuller 		    otype));
43677cb4d3eSLandon J. Fuller 	}
43777cb4d3eSLandon J. Fuller 
43877cb4d3eSLandon J. Fuller 	/* If our value is a string, it may either be a 16-bit or a 32-bit
43977cb4d3eSLandon J. Fuller 	 * representation of the duty cycle */
44077cb4d3eSLandon J. Fuller 	if (itype == BHND_NVRAM_TYPE_STRING) {
44177cb4d3eSLandon J. Fuller 		const char	*p;
44277cb4d3eSLandon J. Fuller 		uint32_t	 ival;
44377cb4d3eSLandon J. Fuller 		size_t		 nlen, parsed;
44477cb4d3eSLandon J. Fuller 
44577cb4d3eSLandon J. Fuller 		/* Parse integer value */
44677cb4d3eSLandon J. Fuller 		p = inp;
44777cb4d3eSLandon J. Fuller 		nlen = sizeof(ival);
44877cb4d3eSLandon J. Fuller 		error = bhnd_nvram_parse_int(p, ilen, 0, &parsed, &ival, &nlen,
44977cb4d3eSLandon J. Fuller 		    BHND_NVRAM_TYPE_UINT32);
45077cb4d3eSLandon J. Fuller 		if (error)
45177cb4d3eSLandon J. Fuller 			return (error);
45277cb4d3eSLandon J. Fuller 
45377cb4d3eSLandon J. Fuller 		/* Trailing garbage? */
45477cb4d3eSLandon J. Fuller 		if (parsed < ilen && *(p+parsed) != '\0')
45577cb4d3eSLandon J. Fuller 			return (EFTYPE);
45677cb4d3eSLandon J. Fuller 
45777cb4d3eSLandon J. Fuller 		/* Point inp and itype to either our parsed 32-bit or 16-bit
45877cb4d3eSLandon J. Fuller 		 * value */
45977cb4d3eSLandon J. Fuller 		inp = &strval;
46077cb4d3eSLandon J. Fuller 		if (ival & 0xFFFF0000) {
46177cb4d3eSLandon J. Fuller 			strval.u32 = ival;
46277cb4d3eSLandon J. Fuller 			itype = BHND_NVRAM_TYPE_UINT32;
46377cb4d3eSLandon J. Fuller 		} else {
46477cb4d3eSLandon J. Fuller 			strval.u16 = ival;
46577cb4d3eSLandon J. Fuller 			itype = BHND_NVRAM_TYPE_UINT16;
46677cb4d3eSLandon J. Fuller 		}
46777cb4d3eSLandon J. Fuller 	}
46877cb4d3eSLandon J. Fuller 
46977cb4d3eSLandon J. Fuller 	/* Populate both u32 and (possibly lossy) u16 LEDDC representations */
47077cb4d3eSLandon J. Fuller 	switch (itype) {
47177cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_UINT16: {
47277cb4d3eSLandon J. Fuller 		led16 = *(const uint16_t *)inp;
47377cb4d3eSLandon J. Fuller 		led32 = ((led16 & 0xFF00) << 16) | ((led16 & 0x00FF) << 8);
47477cb4d3eSLandon J. Fuller 
47577cb4d3eSLandon J. Fuller 		/* If all bits are set in the 16-bit value (indicating that
47677cb4d3eSLandon J. Fuller 		 * the value is 'unset' in SPROM), we must update the 32-bit
47777cb4d3eSLandon J. Fuller 		 * representation to match. */
47877cb4d3eSLandon J. Fuller 		if (led16 == UINT16_MAX)
47977cb4d3eSLandon J. Fuller 			led32 = UINT32_MAX;
48077cb4d3eSLandon J. Fuller 
48177cb4d3eSLandon J. Fuller 		break;
48277cb4d3eSLandon J. Fuller 	}
48377cb4d3eSLandon J. Fuller 
48477cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_UINT32:
48577cb4d3eSLandon J. Fuller 		led32 = *(const uint32_t *)inp;
48677cb4d3eSLandon J. Fuller 		led16 = ((led32 >> 16) & 0xFF00) | ((led32 >> 8) & 0x00FF);
48777cb4d3eSLandon J. Fuller 
48877cb4d3eSLandon J. Fuller 		/*
48977cb4d3eSLandon J. Fuller 		 * Determine whether the led16 conversion is lossy:
49077cb4d3eSLandon J. Fuller 		 *
49177cb4d3eSLandon J. Fuller 		 * - If the lower 8 bits of each half of the 32-bit value
49277cb4d3eSLandon J. Fuller 		 *   aren't set, we can safely use the 16-bit representation
49377cb4d3eSLandon J. Fuller 		 *   without losing data.
49477cb4d3eSLandon J. Fuller 		 * - If all bits in the 32-bit value are set, the variable is
49577cb4d3eSLandon J. Fuller 		 *   treated as unset in  SPROM. We can safely use the 16-bit
49677cb4d3eSLandon J. Fuller 		 *   representation without losing data.
49777cb4d3eSLandon J. Fuller 		 */
49877cb4d3eSLandon J. Fuller 		if ((led32 & 0x00FF00FF) != 0 && led32 != UINT32_MAX)
49977cb4d3eSLandon J. Fuller 			led16_lossy = true;
50077cb4d3eSLandon J. Fuller 
50177cb4d3eSLandon J. Fuller 		break;
50277cb4d3eSLandon J. Fuller 	default:
50377cb4d3eSLandon J. Fuller 		BHND_NV_PANIC("unsupported backing data type: %s",
50477cb4d3eSLandon J. Fuller 		    bhnd_nvram_type_name(itype));
50577cb4d3eSLandon J. Fuller 	}
50677cb4d3eSLandon J. Fuller 
50777cb4d3eSLandon J. Fuller 	/*
50877cb4d3eSLandon J. Fuller 	 * Encode as requested output type.
50977cb4d3eSLandon J. Fuller 	 */
51077cb4d3eSLandon J. Fuller 	switch (otype) {
51177cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_STRING:
51277cb4d3eSLandon J. Fuller 		/*
51377cb4d3eSLandon J. Fuller 		 * Prefer 16-bit format.
51477cb4d3eSLandon J. Fuller 		 */
51577cb4d3eSLandon J. Fuller 		if (!led16_lossy) {
51677cb4d3eSLandon J. Fuller 			return (bhnd_nvram_value_printf("0x%04hX", &led16,
51777cb4d3eSLandon J. Fuller 			    sizeof(led16), BHND_NVRAM_TYPE_UINT16, outp, olen));
51877cb4d3eSLandon J. Fuller 		} else {
51977cb4d3eSLandon J. Fuller 			return (bhnd_nvram_value_printf("0x%04X", &led32,
52077cb4d3eSLandon J. Fuller 			    sizeof(led32), BHND_NVRAM_TYPE_UINT32, outp, olen));
52177cb4d3eSLandon J. Fuller 		}
52277cb4d3eSLandon J. Fuller 
52377cb4d3eSLandon J. Fuller 		break;
52477cb4d3eSLandon J. Fuller 
52577cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_UINT16: {
52677cb4d3eSLandon J. Fuller 		/* Can we encode as uint16 without losing data? */
52777cb4d3eSLandon J. Fuller 		if (led16_lossy)
52877cb4d3eSLandon J. Fuller 			return (ERANGE);
52977cb4d3eSLandon J. Fuller 
53077cb4d3eSLandon J. Fuller 		/* Write led16 format */
53177cb4d3eSLandon J. Fuller 		nbytes += sizeof(uint16_t);
53277cb4d3eSLandon J. Fuller 		if (limit >= nbytes)
53377cb4d3eSLandon J. Fuller 			*(uint16_t *)outp = led16;
53477cb4d3eSLandon J. Fuller 
53577cb4d3eSLandon J. Fuller 		break;
53677cb4d3eSLandon J. Fuller 	}
53777cb4d3eSLandon J. Fuller 
53877cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_UINT32:
53977cb4d3eSLandon J. Fuller 		/* Write led32 format */
54077cb4d3eSLandon J. Fuller 		nbytes += sizeof(uint32_t);
54177cb4d3eSLandon J. Fuller 		if (limit >= nbytes)
54277cb4d3eSLandon J. Fuller 			*(uint32_t *)outp = led32;
54377cb4d3eSLandon J. Fuller 		break;
54477cb4d3eSLandon J. Fuller 
54577cb4d3eSLandon J. Fuller 	default:
54677cb4d3eSLandon J. Fuller 		/* No other output formats are supported */
54777cb4d3eSLandon J. Fuller 		return (EFTYPE);
54877cb4d3eSLandon J. Fuller 	}
54977cb4d3eSLandon J. Fuller 
55077cb4d3eSLandon J. Fuller 	/* Provide the actual length */
55177cb4d3eSLandon J. Fuller 	*olen = nbytes;
55277cb4d3eSLandon J. Fuller 
55377cb4d3eSLandon J. Fuller 	/* Report insufficient space (if output was requested) */
55477cb4d3eSLandon J. Fuller 	if (limit < nbytes && outp != NULL)
55577cb4d3eSLandon J. Fuller 		return (ENOMEM);
55677cb4d3eSLandon J. Fuller 
55777cb4d3eSLandon J. Fuller 	return (0);
55877cb4d3eSLandon J. Fuller }
55977cb4d3eSLandon J. Fuller 
56077cb4d3eSLandon J. Fuller /**
56177cb4d3eSLandon J. Fuller  * Broadcom NVRAM string encoding.
56277cb4d3eSLandon J. Fuller  */
56377cb4d3eSLandon J. Fuller static int
bhnd_nvram_val_bcmstr_encode(bhnd_nvram_val * value,void * outp,size_t * olen,bhnd_nvram_type otype)56458efe686SLandon J. Fuller bhnd_nvram_val_bcmstr_encode(bhnd_nvram_val *value, void *outp, size_t *olen,
56558efe686SLandon J. Fuller     bhnd_nvram_type otype)
56677cb4d3eSLandon J. Fuller {
56758efe686SLandon J. Fuller 	bhnd_nvram_val			 array;
56858efe686SLandon J. Fuller 	const bhnd_nvram_val_fmt	*array_fmt;
56977cb4d3eSLandon J. Fuller 	const void			*inp;
57077cb4d3eSLandon J. Fuller 	bhnd_nvram_type			itype;
57177cb4d3eSLandon J. Fuller 	size_t				ilen;
57277cb4d3eSLandon J. Fuller 	int				error;
57377cb4d3eSLandon J. Fuller 
57477cb4d3eSLandon J. Fuller 	inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
57577cb4d3eSLandon J. Fuller 
57677cb4d3eSLandon J. Fuller 	/* If the output is not an array type (or if it's a character array),
57777cb4d3eSLandon J. Fuller 	 * we can fall back on standard string encoding */
57877cb4d3eSLandon J. Fuller 	if (!bhnd_nvram_is_array_type(otype) ||
57977cb4d3eSLandon J. Fuller 	    otype == BHND_NVRAM_TYPE_CHAR_ARRAY)
58077cb4d3eSLandon J. Fuller 	{
58177cb4d3eSLandon J. Fuller 		return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen,
58277cb4d3eSLandon J. Fuller 		    otype));
58377cb4d3eSLandon J. Fuller 	}
58477cb4d3eSLandon J. Fuller 
58577cb4d3eSLandon J. Fuller 	/* Otherwise, we need to interpret our value as either a macaddr
58677cb4d3eSLandon J. Fuller 	 * string, or a comma-delimited string. */
58777cb4d3eSLandon J. Fuller 	inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
58877cb4d3eSLandon J. Fuller 	if (bhnd_nvram_ident_octet_string(inp, ilen, NULL, NULL))
58977cb4d3eSLandon J. Fuller 		array_fmt = &bhnd_nvram_val_bcm_macaddr_string_fmt;
59077cb4d3eSLandon J. Fuller 	else
59177cb4d3eSLandon J. Fuller 		array_fmt = &bhnd_nvram_val_bcm_string_csv_fmt;
59277cb4d3eSLandon J. Fuller 
59377cb4d3eSLandon J. Fuller 	/* Wrap in array-typed representation */
59477cb4d3eSLandon J. Fuller 	error = bhnd_nvram_val_init(&array, array_fmt, inp, ilen, itype,
59577cb4d3eSLandon J. Fuller 	    BHND_NVRAM_VAL_BORROW_DATA);
59677cb4d3eSLandon J. Fuller 	if (error) {
59777cb4d3eSLandon J. Fuller 		BHND_NV_LOG("error initializing array representation: %d\n",
59877cb4d3eSLandon J. Fuller 		    error);
59977cb4d3eSLandon J. Fuller 		return (error);
60077cb4d3eSLandon J. Fuller 	}
60177cb4d3eSLandon J. Fuller 
60277cb4d3eSLandon J. Fuller 	/* Ask the array-typed value to perform the encode */
60377cb4d3eSLandon J. Fuller 	error = bhnd_nvram_val_encode(&array, outp, olen, otype);
60477cb4d3eSLandon J. Fuller 	if (error)
60577cb4d3eSLandon J. Fuller 		BHND_NV_LOG("error encoding array representation: %d\n", error);
60677cb4d3eSLandon J. Fuller 
60777cb4d3eSLandon J. Fuller 	bhnd_nvram_val_release(&array);
60877cb4d3eSLandon J. Fuller 
60977cb4d3eSLandon J. Fuller 	return (error);
61077cb4d3eSLandon J. Fuller }
61177cb4d3eSLandon J. Fuller 
61277cb4d3eSLandon J. Fuller /**
61377cb4d3eSLandon J. Fuller  * Broadcom NVRAM comma-delimited string filter.
61477cb4d3eSLandon J. Fuller  */
61577cb4d3eSLandon J. Fuller static int
bhnd_nvram_val_bcmstr_csv_filter(const bhnd_nvram_val_fmt ** fmt,const void * inp,size_t ilen,bhnd_nvram_type itype)61658efe686SLandon J. Fuller bhnd_nvram_val_bcmstr_csv_filter(const bhnd_nvram_val_fmt **fmt,
61777cb4d3eSLandon J. Fuller     const void *inp, size_t ilen, bhnd_nvram_type itype)
61877cb4d3eSLandon J. Fuller {
61977cb4d3eSLandon J. Fuller 	switch (itype) {
62077cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_STRING:
62177cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_STRING_ARRAY:
62277cb4d3eSLandon J. Fuller 		return (0);
62377cb4d3eSLandon J. Fuller 	default:
62477cb4d3eSLandon J. Fuller 		return (EFTYPE);
62577cb4d3eSLandon J. Fuller 	}
62677cb4d3eSLandon J. Fuller }
62777cb4d3eSLandon J. Fuller 
62877cb4d3eSLandon J. Fuller /**
62977cb4d3eSLandon J. Fuller  * Broadcom NVRAM comma-delimited string iteration.
63077cb4d3eSLandon J. Fuller  */
63177cb4d3eSLandon J. Fuller static const void *
bhnd_nvram_val_bcmstr_csv_next(bhnd_nvram_val * value,const void * prev,size_t * len)63258efe686SLandon J. Fuller bhnd_nvram_val_bcmstr_csv_next(bhnd_nvram_val *value, const void *prev,
63377cb4d3eSLandon J. Fuller     size_t *len)
63477cb4d3eSLandon J. Fuller {
63577cb4d3eSLandon J. Fuller 	const char	*next;
63677cb4d3eSLandon J. Fuller 	const char	*inp;
63777cb4d3eSLandon J. Fuller 	bhnd_nvram_type	 itype;
63877cb4d3eSLandon J. Fuller 	size_t		 ilen, remain;
63977cb4d3eSLandon J. Fuller 	char		 delim;
64077cb4d3eSLandon J. Fuller 
64177cb4d3eSLandon J. Fuller 	/* Fetch backing representation */
64277cb4d3eSLandon J. Fuller 	inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
64377cb4d3eSLandon J. Fuller 
64477cb4d3eSLandon J. Fuller 	/* Fetch next value */
64577cb4d3eSLandon J. Fuller 	switch (itype) {
64677cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_STRING:
64777cb4d3eSLandon J. Fuller 		/* Zero-length array? */
64877cb4d3eSLandon J. Fuller 		if (ilen == 0)
64977cb4d3eSLandon J. Fuller 			return (NULL);
65077cb4d3eSLandon J. Fuller 
65177cb4d3eSLandon J. Fuller 		if (prev == NULL) {
65277cb4d3eSLandon J. Fuller 			/* First element */
65377cb4d3eSLandon J. Fuller 			next = inp;
65477cb4d3eSLandon J. Fuller 			remain = ilen;
65577cb4d3eSLandon J. Fuller 			delim = ',';
65677cb4d3eSLandon J. Fuller 		} else {
65777cb4d3eSLandon J. Fuller 			/* Advance to the previous element's delimiter */
65877cb4d3eSLandon J. Fuller 			next = (const char *)prev + *len;
65977cb4d3eSLandon J. Fuller 
66077cb4d3eSLandon J. Fuller 			/* Did we hit the end of the string? */
66177cb4d3eSLandon J. Fuller 			if ((size_t)(next - inp) >= ilen)
66277cb4d3eSLandon J. Fuller 				return (NULL);
66377cb4d3eSLandon J. Fuller 
66477cb4d3eSLandon J. Fuller 			/* Fetch (and skip past) the delimiter */
66577cb4d3eSLandon J. Fuller 			delim = *next;
66677cb4d3eSLandon J. Fuller 			next++;
66777cb4d3eSLandon J. Fuller 			remain = ilen - (size_t)(next - inp);
66877cb4d3eSLandon J. Fuller 
66977cb4d3eSLandon J. Fuller 			/* Was the delimiter the final character? */
67077cb4d3eSLandon J. Fuller 			if (remain == 0)
67177cb4d3eSLandon J. Fuller 				return (NULL);
67277cb4d3eSLandon J. Fuller 		}
67377cb4d3eSLandon J. Fuller 
67477cb4d3eSLandon J. Fuller 		/* Parse the field value, up to the next delimiter */
67577cb4d3eSLandon J. Fuller 		*len = bhnd_nvram_parse_field(&next, remain, delim);
67677cb4d3eSLandon J. Fuller 
67777cb4d3eSLandon J. Fuller 		return (next);
67877cb4d3eSLandon J. Fuller 
67977cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_STRING_ARRAY:
6809be0790dSLandon J. Fuller 		/* Delegate to default array iteration */
6819be0790dSLandon J. Fuller 		return (bhnd_nvram_value_array_next(inp, ilen, itype, prev,
6829be0790dSLandon J. Fuller 		    len));
68377cb4d3eSLandon J. Fuller 	default:
68477cb4d3eSLandon J. Fuller 		BHND_NV_PANIC("unsupported type: %d", itype);
68577cb4d3eSLandon J. Fuller 	}
68677cb4d3eSLandon J. Fuller }
68777cb4d3eSLandon J. Fuller 
68877cb4d3eSLandon J. Fuller /**
68977cb4d3eSLandon J. Fuller  * MAC address filter.
69077cb4d3eSLandon J. Fuller  */
69177cb4d3eSLandon J. Fuller static int
bhnd_nvram_val_bcm_macaddr_filter(const bhnd_nvram_val_fmt ** fmt,const void * inp,size_t ilen,bhnd_nvram_type itype)69258efe686SLandon J. Fuller bhnd_nvram_val_bcm_macaddr_filter(const bhnd_nvram_val_fmt **fmt,
69377cb4d3eSLandon J. Fuller     const void *inp, size_t ilen, bhnd_nvram_type itype)
69477cb4d3eSLandon J. Fuller {
69577cb4d3eSLandon J. Fuller 	switch (itype) {
69677cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_UINT8_ARRAY:
69777cb4d3eSLandon J. Fuller 		return (0);
69877cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_STRING:
69977cb4d3eSLandon J. Fuller 		/* Let bcm_macaddr_string format handle it */
70077cb4d3eSLandon J. Fuller 		*fmt = &bhnd_nvram_val_bcm_macaddr_string_fmt;
70177cb4d3eSLandon J. Fuller 		return (0);
70277cb4d3eSLandon J. Fuller 	default:
70377cb4d3eSLandon J. Fuller 		return (EFTYPE);
70477cb4d3eSLandon J. Fuller 	}
70577cb4d3eSLandon J. Fuller }
70677cb4d3eSLandon J. Fuller 
70777cb4d3eSLandon J. Fuller /**
70877cb4d3eSLandon J. Fuller  * MAC address encoding.
70977cb4d3eSLandon J. Fuller  */
71077cb4d3eSLandon J. Fuller static int
bhnd_nvram_val_bcm_macaddr_encode(bhnd_nvram_val * value,void * outp,size_t * olen,bhnd_nvram_type otype)71158efe686SLandon J. Fuller bhnd_nvram_val_bcm_macaddr_encode(bhnd_nvram_val *value, void *outp,
71277cb4d3eSLandon J. Fuller     size_t *olen, bhnd_nvram_type otype)
71377cb4d3eSLandon J. Fuller {
71477cb4d3eSLandon J. Fuller 	const void	*inp;
71577cb4d3eSLandon J. Fuller 	bhnd_nvram_type	 itype;
71677cb4d3eSLandon J. Fuller 	size_t		 ilen;
71777cb4d3eSLandon J. Fuller 
71877cb4d3eSLandon J. Fuller 	/*
71977cb4d3eSLandon J. Fuller 	 * If converting to a string (or a single-element string array),
72077cb4d3eSLandon J. Fuller 	 * produce an octet string (00:00:...).
72177cb4d3eSLandon J. Fuller 	 */
72277cb4d3eSLandon J. Fuller 	if (bhnd_nvram_base_type(otype) == BHND_NVRAM_TYPE_STRING) {
72377cb4d3eSLandon J. Fuller 		return (bhnd_nvram_val_printf(value, "%[]02hhX", outp, olen,
72477cb4d3eSLandon J. Fuller 		    ":"));
72577cb4d3eSLandon J. Fuller 	}
72677cb4d3eSLandon J. Fuller 
72777cb4d3eSLandon J. Fuller 	/* Otherwise, use standard encoding support */
72877cb4d3eSLandon J. Fuller 	inp = bhnd_nvram_val_bytes(value, &ilen, &itype);
72977cb4d3eSLandon J. Fuller 	return (bhnd_nvram_value_coerce(inp, ilen, itype, outp, olen, otype));}
73077cb4d3eSLandon J. Fuller 
73177cb4d3eSLandon J. Fuller /**
73277cb4d3eSLandon J. Fuller  * MAC address string filter.
73377cb4d3eSLandon J. Fuller  */
73477cb4d3eSLandon J. Fuller static int
bhnd_nvram_val_bcm_macaddr_string_filter(const bhnd_nvram_val_fmt ** fmt,const void * inp,size_t ilen,bhnd_nvram_type itype)73558efe686SLandon J. Fuller bhnd_nvram_val_bcm_macaddr_string_filter(const bhnd_nvram_val_fmt **fmt,
73677cb4d3eSLandon J. Fuller     const void *inp, size_t ilen, bhnd_nvram_type itype)
73777cb4d3eSLandon J. Fuller {
73877cb4d3eSLandon J. Fuller 	switch (itype) {
73977cb4d3eSLandon J. Fuller 	case BHND_NVRAM_TYPE_STRING:
74077cb4d3eSLandon J. Fuller 		/* Use the standard Broadcom string format implementation if
74177cb4d3eSLandon J. Fuller 		 * the input is not an octet string. */
74277cb4d3eSLandon J. Fuller 		if (!bhnd_nvram_ident_octet_string(inp, ilen, NULL, NULL))
74377cb4d3eSLandon J. Fuller 			*fmt = &bhnd_nvram_val_bcm_string_fmt;
74477cb4d3eSLandon J. Fuller 
74577cb4d3eSLandon J. Fuller 		return (0);
74677cb4d3eSLandon J. Fuller 	default:
74777cb4d3eSLandon J. Fuller 		return (EFTYPE);
74877cb4d3eSLandon J. Fuller 	}
74977cb4d3eSLandon J. Fuller }
75077cb4d3eSLandon J. Fuller 
75177cb4d3eSLandon J. Fuller /**
75277cb4d3eSLandon J. Fuller  * MAC address string octet encoding.
75377cb4d3eSLandon J. Fuller  */
75477cb4d3eSLandon J. Fuller static int
bhnd_nvram_val_bcm_macaddr_string_encode_elem(bhnd_nvram_val * value,const void * inp,size_t ilen,void * outp,size_t * olen,bhnd_nvram_type otype)75558efe686SLandon J. Fuller bhnd_nvram_val_bcm_macaddr_string_encode_elem(bhnd_nvram_val *value,
75677cb4d3eSLandon J. Fuller     const void *inp, size_t ilen, void *outp, size_t *olen,
75777cb4d3eSLandon J. Fuller     bhnd_nvram_type otype)
75877cb4d3eSLandon J. Fuller {
75977cb4d3eSLandon J. Fuller 	size_t	nparsed;
76077cb4d3eSLandon J. Fuller 	int	error;
76177cb4d3eSLandon J. Fuller 
76277cb4d3eSLandon J. Fuller 	/* If integer encoding is requested, explicitly parse our
76377cb4d3eSLandon J. Fuller 	 * non-0x-prefixed as a base 16 integer value */
76477cb4d3eSLandon J. Fuller 	if (bhnd_nvram_is_int_type(otype)) {
76577cb4d3eSLandon J. Fuller 		error = bhnd_nvram_parse_int(inp, ilen, 16, &nparsed, outp,
76677cb4d3eSLandon J. Fuller 		    olen, otype);
76777cb4d3eSLandon J. Fuller 		if (error)
76877cb4d3eSLandon J. Fuller 			return (error);
76977cb4d3eSLandon J. Fuller 
77077cb4d3eSLandon J. Fuller 		if (nparsed != ilen)
77177cb4d3eSLandon J. Fuller 			return (EFTYPE);
77277cb4d3eSLandon J. Fuller 
77377cb4d3eSLandon J. Fuller 		return (0);
77477cb4d3eSLandon J. Fuller 	}
77577cb4d3eSLandon J. Fuller 
77677cb4d3eSLandon J. Fuller 	/* Otherwise, use standard encoding support */
77777cb4d3eSLandon J. Fuller 	return (bhnd_nvram_value_coerce(inp, ilen,
77877cb4d3eSLandon J. Fuller 	    bhnd_nvram_val_elem_type(value), outp, olen, otype));
77977cb4d3eSLandon J. Fuller }
78077cb4d3eSLandon J. Fuller 
78177cb4d3eSLandon J. Fuller /**
78277cb4d3eSLandon J. Fuller  * MAC address string octet iteration.
78377cb4d3eSLandon J. Fuller  */
78477cb4d3eSLandon J. Fuller static const void *
bhnd_nvram_val_bcm_macaddr_string_next(bhnd_nvram_val * value,const void * prev,size_t * len)78558efe686SLandon J. Fuller bhnd_nvram_val_bcm_macaddr_string_next(bhnd_nvram_val *value, const void *prev,
78677cb4d3eSLandon J. Fuller     size_t *len)
78777cb4d3eSLandon J. Fuller {
78877cb4d3eSLandon J. Fuller 	const char	*next;
78977cb4d3eSLandon J. Fuller 	const char	*str;
79077cb4d3eSLandon J. Fuller 	bhnd_nvram_type	 stype;
79177cb4d3eSLandon J. Fuller 	size_t		 slen, remain;
79277cb4d3eSLandon J. Fuller 	char		 delim;
79377cb4d3eSLandon J. Fuller 
79477cb4d3eSLandon J. Fuller 	/* Fetch backing string */
79577cb4d3eSLandon J. Fuller 	str = bhnd_nvram_val_bytes(value, &slen, &stype);
79677cb4d3eSLandon J. Fuller 	BHND_NV_ASSERT(stype == BHND_NVRAM_TYPE_STRING,
79777cb4d3eSLandon J. Fuller 	    ("unsupported type: %d", stype));
79877cb4d3eSLandon J. Fuller 
79977cb4d3eSLandon J. Fuller 	/* Zero-length array? */
80077cb4d3eSLandon J. Fuller 	if (slen == 0)
80177cb4d3eSLandon J. Fuller 		return (NULL);
80277cb4d3eSLandon J. Fuller 
80377cb4d3eSLandon J. Fuller 	if (prev == NULL) {
80477cb4d3eSLandon J. Fuller 		/* First element */
80577cb4d3eSLandon J. Fuller 
80677cb4d3eSLandon J. Fuller 		/* Determine delimiter */
80777cb4d3eSLandon J. Fuller 		if (!bhnd_nvram_ident_octet_string(str, slen, &delim, NULL)) {
80877cb4d3eSLandon J. Fuller 			/* Default to comma-delimited parsing */
80977cb4d3eSLandon J. Fuller 			delim = ',';
81077cb4d3eSLandon J. Fuller 		}
81177cb4d3eSLandon J. Fuller 
81277cb4d3eSLandon J. Fuller 		/* Parsing will start at the base string pointer */
81377cb4d3eSLandon J. Fuller 		next = str;
81477cb4d3eSLandon J. Fuller 		remain = slen;
81577cb4d3eSLandon J. Fuller 	} else {
81677cb4d3eSLandon J. Fuller 		/* Advance to the previous element's delimiter */
81777cb4d3eSLandon J. Fuller 		next = (const char *)prev + *len;
81877cb4d3eSLandon J. Fuller 
81977cb4d3eSLandon J. Fuller 		/* Did we hit the end of the string? */
82077cb4d3eSLandon J. Fuller 		if ((size_t)(next - str) >= slen)
82177cb4d3eSLandon J. Fuller 			return (NULL);
82277cb4d3eSLandon J. Fuller 
82377cb4d3eSLandon J. Fuller 		/* Fetch (and skip past) the delimiter */
82477cb4d3eSLandon J. Fuller 		delim = *next;
82577cb4d3eSLandon J. Fuller 		next++;
82677cb4d3eSLandon J. Fuller 		remain = slen - (size_t)(next - str);
82777cb4d3eSLandon J. Fuller 
82877cb4d3eSLandon J. Fuller 		/* Was the delimiter the final character? */
82977cb4d3eSLandon J. Fuller 		if (remain == 0)
83077cb4d3eSLandon J. Fuller 			return (NULL);
83177cb4d3eSLandon J. Fuller 	}
83277cb4d3eSLandon J. Fuller 
83377cb4d3eSLandon J. Fuller 	/* Parse the field value, up to the next delimiter */
83477cb4d3eSLandon J. Fuller 	*len = bhnd_nvram_parse_field(&next, remain, delim);
83577cb4d3eSLandon J. Fuller 
83677cb4d3eSLandon J. Fuller 	return (next);
83777cb4d3eSLandon J. Fuller }
83877cb4d3eSLandon J. Fuller 
83977cb4d3eSLandon J. Fuller /**
84077cb4d3eSLandon J. Fuller  * Determine whether @p inp is in octet string format, consisting of a
84177cb4d3eSLandon J. Fuller  * fields of two hex characters, separated with ':' or '-' delimiters.
84277cb4d3eSLandon J. Fuller  *
84377cb4d3eSLandon J. Fuller  * This may be used to identify MAC address octet strings
84477cb4d3eSLandon J. Fuller  * (BHND_NVRAM_SFMT_MACADDR).
84577cb4d3eSLandon J. Fuller  *
84677cb4d3eSLandon J. Fuller  * @param		inp	The string to be parsed.
84777cb4d3eSLandon J. Fuller  * @param		ilen	The length of @p inp, in bytes.
84877cb4d3eSLandon J. Fuller  * @param[out]		delim	On success, the delimiter used by this octet
84977cb4d3eSLandon J. Fuller  * 				string. May be set to NULL if the field
85077cb4d3eSLandon J. Fuller  *				delimiter is not desired.
85177cb4d3eSLandon J. Fuller  * @param[out]		nelem	On success, the number of fields in this
85277cb4d3eSLandon J. Fuller  *				octet string. May be set to NULL if the field
85377cb4d3eSLandon J. Fuller  *				count is not desired.
85477cb4d3eSLandon J. Fuller  *
85577cb4d3eSLandon J. Fuller  *
85677cb4d3eSLandon J. Fuller  * @retval true		if @p inp is a valid octet string
85777cb4d3eSLandon J. Fuller  * @retval false	if @p inp is not a valid octet string.
85877cb4d3eSLandon J. Fuller  */
85977cb4d3eSLandon J. Fuller static bool
bhnd_nvram_ident_octet_string(const char * inp,size_t ilen,char * delim,size_t * nelem)86077cb4d3eSLandon J. Fuller bhnd_nvram_ident_octet_string(const char *inp, size_t ilen, char *delim,
86177cb4d3eSLandon J. Fuller     size_t *nelem)
86277cb4d3eSLandon J. Fuller {
86377cb4d3eSLandon J. Fuller 	size_t	elem_count;
86477cb4d3eSLandon J. Fuller 	size_t	max_elem_count, min_elem_count;
86577cb4d3eSLandon J. Fuller 	size_t	field_count;
86677cb4d3eSLandon J. Fuller 	char	idelim;
86777cb4d3eSLandon J. Fuller 
86877cb4d3eSLandon J. Fuller 	field_count = 0;
86977cb4d3eSLandon J. Fuller 
87077cb4d3eSLandon J. Fuller 	/* Require exactly two digits. If we relax this, there is room
87177cb4d3eSLandon J. Fuller 	 * for ambiguity with signed integers and the '-' delimiter */
87277cb4d3eSLandon J. Fuller 	min_elem_count = 2;
87377cb4d3eSLandon J. Fuller 	max_elem_count = 2;
87477cb4d3eSLandon J. Fuller 
87577cb4d3eSLandon J. Fuller 	/* Identify the delimiter used. The standard delimiter for MAC
87677cb4d3eSLandon J. Fuller 	 * addresses is ':', but some earlier NVRAM formats may use '-' */
87777cb4d3eSLandon J. Fuller 	for (const char *d = ":-";; d++) {
87877cb4d3eSLandon J. Fuller 		const char *loc;
87977cb4d3eSLandon J. Fuller 
88077cb4d3eSLandon J. Fuller 		/* No delimiter found, not an octet string */
88177cb4d3eSLandon J. Fuller 		if (*d == '\0')
88277cb4d3eSLandon J. Fuller 			return (false);
88377cb4d3eSLandon J. Fuller 
88477cb4d3eSLandon J. Fuller 		/* Look for the delimiter */
88577cb4d3eSLandon J. Fuller 		if ((loc = memchr(inp, *d, ilen)) == NULL)
88677cb4d3eSLandon J. Fuller 			continue;
88777cb4d3eSLandon J. Fuller 
88877cb4d3eSLandon J. Fuller 		/* Delimiter found */
88977cb4d3eSLandon J. Fuller 		idelim = *loc;
89077cb4d3eSLandon J. Fuller 		break;
89177cb4d3eSLandon J. Fuller 	}
89277cb4d3eSLandon J. Fuller 
89377cb4d3eSLandon J. Fuller 	/* To disambiguate from signed integers, if the delimiter is "-",
89477cb4d3eSLandon J. Fuller 	 * the octets must be exactly 2 chars each */
89577cb4d3eSLandon J. Fuller 	if (idelim == '-')
89677cb4d3eSLandon J. Fuller 		min_elem_count = 2;
89777cb4d3eSLandon J. Fuller 
89877cb4d3eSLandon J. Fuller 	/* String must be composed of individual octets (zero or more hex
89977cb4d3eSLandon J. Fuller 	 * digits) separated by our delimiter. */
90077cb4d3eSLandon J. Fuller 	elem_count = 0;
90177cb4d3eSLandon J. Fuller 	for (const char *p = inp; (size_t)(p - inp) < ilen; p++) {
90277cb4d3eSLandon J. Fuller 		switch (*p) {
90377cb4d3eSLandon J. Fuller 		case ':':
90477cb4d3eSLandon J. Fuller 		case '-':
90577cb4d3eSLandon J. Fuller 		case '\0':
90677cb4d3eSLandon J. Fuller 			/* Hit a delim character; all delims must match
90777cb4d3eSLandon J. Fuller 			 * the first delimiter used */
90877cb4d3eSLandon J. Fuller 			if (*p != '\0' && *p != idelim)
90977cb4d3eSLandon J. Fuller 				return (false);
91077cb4d3eSLandon J. Fuller 
91177cb4d3eSLandon J. Fuller 			/* Must have parsed at least min_elem_count digits */
91277cb4d3eSLandon J. Fuller 			if (elem_count < min_elem_count)
91377cb4d3eSLandon J. Fuller 				return (false);
91477cb4d3eSLandon J. Fuller 
91577cb4d3eSLandon J. Fuller 			/* Reset element count */
91677cb4d3eSLandon J. Fuller 			elem_count = 0;
91777cb4d3eSLandon J. Fuller 
91877cb4d3eSLandon J. Fuller 			/* Bump field count */
91977cb4d3eSLandon J. Fuller 			field_count++;
92077cb4d3eSLandon J. Fuller 			break;
92177cb4d3eSLandon J. Fuller 		default:
92277cb4d3eSLandon J. Fuller 			/* More than maximum number of hex digits? */
92377cb4d3eSLandon J. Fuller 			if (elem_count >= max_elem_count)
92477cb4d3eSLandon J. Fuller 				return (false);
92577cb4d3eSLandon J. Fuller 
92677cb4d3eSLandon J. Fuller 			/* Octet values must be hex digits */
92777cb4d3eSLandon J. Fuller 			if (!bhnd_nv_isxdigit(*p))
92877cb4d3eSLandon J. Fuller 				return (false);
92977cb4d3eSLandon J. Fuller 
93077cb4d3eSLandon J. Fuller 			elem_count++;
93177cb4d3eSLandon J. Fuller 			break;
93277cb4d3eSLandon J. Fuller 		}
93377cb4d3eSLandon J. Fuller 	}
93477cb4d3eSLandon J. Fuller 
93577cb4d3eSLandon J. Fuller 	if (delim != NULL)
93677cb4d3eSLandon J. Fuller 		*delim = idelim;
93777cb4d3eSLandon J. Fuller 
93877cb4d3eSLandon J. Fuller 	if (nelem != NULL)
93977cb4d3eSLandon J. Fuller 		*nelem = field_count;
94077cb4d3eSLandon J. Fuller 
94177cb4d3eSLandon J. Fuller 	return (true);
94277cb4d3eSLandon J. Fuller }
94377cb4d3eSLandon J. Fuller 
94477cb4d3eSLandon J. Fuller /**
94577cb4d3eSLandon J. Fuller  * Determine whether @p inp is in hexadecimal, octal, or decimal string
94677cb4d3eSLandon J. Fuller  * format.
94777cb4d3eSLandon J. Fuller  *
94877cb4d3eSLandon J. Fuller  * - A @p str may be prefixed with a single optional '+' or '-' sign denoting
94977cb4d3eSLandon J. Fuller  *   signedness.
95077cb4d3eSLandon J. Fuller  * - A hexadecimal @p str may include an '0x' or '0X' prefix, denoting that a
95177cb4d3eSLandon J. Fuller  *   base 16 integer follows.
95277cb4d3eSLandon J. Fuller  * - An octal @p str may include a '0' prefix, denoting that an octal integer
95377cb4d3eSLandon J. Fuller  *   follows.
95477cb4d3eSLandon J. Fuller  *
95577cb4d3eSLandon J. Fuller  * @param	inp	The string to be parsed.
95677cb4d3eSLandon J. Fuller  * @param	ilen	The length of @p inp, in bytes.
95777cb4d3eSLandon J. Fuller  * @param	base	The input string's base (2-36), or 0.
95877cb4d3eSLandon J. Fuller  * @param[out]	obase	On success, will be set to the base of the parsed
95977cb4d3eSLandon J. Fuller  *			integer. May be set to NULL if the base is not
96077cb4d3eSLandon J. Fuller  *			desired.
96177cb4d3eSLandon J. Fuller  *
96277cb4d3eSLandon J. Fuller  * @retval true		if @p inp is a valid number string
96377cb4d3eSLandon J. Fuller  * @retval false	if @p inp is not a valid number string.
96477cb4d3eSLandon J. Fuller  * @retval false	if @p base is invalid.
96577cb4d3eSLandon J. Fuller  */
96677cb4d3eSLandon J. Fuller static bool
bhnd_nvram_ident_num_string(const char * inp,size_t ilen,u_int base,u_int * obase)96777cb4d3eSLandon J. Fuller bhnd_nvram_ident_num_string(const char *inp, size_t ilen, u_int base,
96877cb4d3eSLandon J. Fuller     u_int *obase)
96977cb4d3eSLandon J. Fuller {
97077cb4d3eSLandon J. Fuller 	size_t	nbytes, ndigits;
97177cb4d3eSLandon J. Fuller 
97277cb4d3eSLandon J. Fuller 	nbytes = 0;
97377cb4d3eSLandon J. Fuller 	ndigits = 0;
97477cb4d3eSLandon J. Fuller 
97577cb4d3eSLandon J. Fuller 	/* Parse and skip sign */
97677cb4d3eSLandon J. Fuller 	if (nbytes >= ilen)
97777cb4d3eSLandon J. Fuller 		return (false);
97877cb4d3eSLandon J. Fuller 
97977cb4d3eSLandon J. Fuller 	if (inp[nbytes] == '-' || inp[nbytes] == '+')
98077cb4d3eSLandon J. Fuller 		nbytes++;
98177cb4d3eSLandon J. Fuller 
98277cb4d3eSLandon J. Fuller 	/* Truncated after sign character? */
98377cb4d3eSLandon J. Fuller 	if (nbytes == ilen)
98477cb4d3eSLandon J. Fuller 		return (false);
98577cb4d3eSLandon J. Fuller 
98677cb4d3eSLandon J. Fuller 	/* Identify (or validate) hex base, skipping 0x/0X prefix */
98777cb4d3eSLandon J. Fuller 	if (base == 16 || base == 0) {
98877cb4d3eSLandon J. Fuller 		/* Check for (and skip) 0x/0X prefix */
98977cb4d3eSLandon J. Fuller 		if (ilen - nbytes >= 2 && inp[nbytes] == '0' &&
99077cb4d3eSLandon J. Fuller 		    (inp[nbytes+1] == 'x' || inp[nbytes+1] == 'X'))
99177cb4d3eSLandon J. Fuller 		{
99277cb4d3eSLandon J. Fuller 			base = 16;
99377cb4d3eSLandon J. Fuller 			nbytes += 2;
99477cb4d3eSLandon J. Fuller 		}
99577cb4d3eSLandon J. Fuller 	}
99677cb4d3eSLandon J. Fuller 
99777cb4d3eSLandon J. Fuller 	/* Truncated after hex prefix? */
99877cb4d3eSLandon J. Fuller 	if (nbytes == ilen)
99977cb4d3eSLandon J. Fuller 		return (false);
100077cb4d3eSLandon J. Fuller 
100177cb4d3eSLandon J. Fuller 	/* Differentiate decimal/octal by looking for a leading 0 */
100277cb4d3eSLandon J. Fuller 	if (base == 0) {
100377cb4d3eSLandon J. Fuller 		if (inp[nbytes] == '0') {
100477cb4d3eSLandon J. Fuller 			base = 8;
100577cb4d3eSLandon J. Fuller 		} else {
100677cb4d3eSLandon J. Fuller 			base = 10;
100777cb4d3eSLandon J. Fuller 		}
100877cb4d3eSLandon J. Fuller 	}
100977cb4d3eSLandon J. Fuller 
101077cb4d3eSLandon J. Fuller 	/* Consume and validate all remaining digit characters */
101177cb4d3eSLandon J. Fuller 	for (; nbytes < ilen; nbytes++) {
101277cb4d3eSLandon J. Fuller 		u_int	carry;
101377cb4d3eSLandon J. Fuller 		char	c;
101477cb4d3eSLandon J. Fuller 
101577cb4d3eSLandon J. Fuller 		/* Parse carry value */
101677cb4d3eSLandon J. Fuller 		c = inp[nbytes];
101777cb4d3eSLandon J. Fuller 		if (bhnd_nv_isdigit(c)) {
101877cb4d3eSLandon J. Fuller 			carry = c - '0';
101977cb4d3eSLandon J. Fuller 		} else if (bhnd_nv_isxdigit(c)) {
102077cb4d3eSLandon J. Fuller 			if (bhnd_nv_isupper(c))
102177cb4d3eSLandon J. Fuller 				carry = (c - 'A') + 10;
102277cb4d3eSLandon J. Fuller 			else
102377cb4d3eSLandon J. Fuller 				carry = (c - 'a') + 10;
102477cb4d3eSLandon J. Fuller 		} else {
102577cb4d3eSLandon J. Fuller 			/* Hit a non-digit character */
102677cb4d3eSLandon J. Fuller 			return (false);
102777cb4d3eSLandon J. Fuller 		}
102877cb4d3eSLandon J. Fuller 
102977cb4d3eSLandon J. Fuller 		/* If carry is outside the base, it's not a valid digit
103077cb4d3eSLandon J. Fuller 		 * in the current parse context; consider it a non-digit
103177cb4d3eSLandon J. Fuller 		 * character */
103277cb4d3eSLandon J. Fuller 		if (carry >= base)
103377cb4d3eSLandon J. Fuller 			return (false);
103477cb4d3eSLandon J. Fuller 
103577cb4d3eSLandon J. Fuller 		/* Increment parsed digit count */
103677cb4d3eSLandon J. Fuller 		ndigits++;
103777cb4d3eSLandon J. Fuller 	}
103877cb4d3eSLandon J. Fuller 
103977cb4d3eSLandon J. Fuller 	/* Empty integer string? */
104077cb4d3eSLandon J. Fuller 	if (ndigits == 0)
104177cb4d3eSLandon J. Fuller 		return (false);
104277cb4d3eSLandon J. Fuller 
104377cb4d3eSLandon J. Fuller 	/* Valid integer -- provide the base and return */
104477cb4d3eSLandon J. Fuller 	if (obase != NULL)
104577cb4d3eSLandon J. Fuller 		*obase = base;
104677cb4d3eSLandon J. Fuller 	return (true);
104777cb4d3eSLandon J. Fuller }
1048