18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright(c) 2013 - 2019 Intel Corporation. */
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include "fm10k_tlv.h"
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci/**
78c2ecf20Sopenharmony_ci *  fm10k_tlv_msg_init - Initialize message block for TLV data storage
88c2ecf20Sopenharmony_ci *  @msg: Pointer to message block
98c2ecf20Sopenharmony_ci *  @msg_id: Message ID indicating message type
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci *  This function return success if provided with a valid message pointer
128c2ecf20Sopenharmony_ci **/
138c2ecf20Sopenharmony_cis32 fm10k_tlv_msg_init(u32 *msg, u16 msg_id)
148c2ecf20Sopenharmony_ci{
158c2ecf20Sopenharmony_ci	/* verify pointer is not NULL */
168c2ecf20Sopenharmony_ci	if (!msg)
178c2ecf20Sopenharmony_ci		return FM10K_ERR_PARAM;
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci	*msg = (FM10K_TLV_FLAGS_MSG << FM10K_TLV_FLAGS_SHIFT) | msg_id;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	return 0;
228c2ecf20Sopenharmony_ci}
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/**
258c2ecf20Sopenharmony_ci *  fm10k_tlv_attr_put_null_string - Place null terminated string on message
268c2ecf20Sopenharmony_ci *  @msg: Pointer to message block
278c2ecf20Sopenharmony_ci *  @attr_id: Attribute ID
288c2ecf20Sopenharmony_ci *  @string: Pointer to string to be stored in attribute
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci *  This function will reorder a string to be CPU endian and store it in
318c2ecf20Sopenharmony_ci *  the attribute buffer.  It will return success if provided with a valid
328c2ecf20Sopenharmony_ci *  pointers.
338c2ecf20Sopenharmony_ci **/
348c2ecf20Sopenharmony_cistatic s32 fm10k_tlv_attr_put_null_string(u32 *msg, u16 attr_id,
358c2ecf20Sopenharmony_ci					  const unsigned char *string)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	u32 attr_data = 0, len = 0;
388c2ecf20Sopenharmony_ci	u32 *attr;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	/* verify pointers are not NULL */
418c2ecf20Sopenharmony_ci	if (!string || !msg)
428c2ecf20Sopenharmony_ci		return FM10K_ERR_PARAM;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	attr = &msg[FM10K_TLV_DWORD_LEN(*msg)];
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	/* copy string into local variable and then write to msg */
478c2ecf20Sopenharmony_ci	do {
488c2ecf20Sopenharmony_ci		/* write data to message */
498c2ecf20Sopenharmony_ci		if (len && !(len % 4)) {
508c2ecf20Sopenharmony_ci			attr[len / 4] = attr_data;
518c2ecf20Sopenharmony_ci			attr_data = 0;
528c2ecf20Sopenharmony_ci		}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci		/* record character to offset location */
558c2ecf20Sopenharmony_ci		attr_data |= (u32)(*string) << (8 * (len % 4));
568c2ecf20Sopenharmony_ci		len++;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci		/* test for NULL and then increment */
598c2ecf20Sopenharmony_ci	} while (*(string++));
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	/* write last piece of data to message */
628c2ecf20Sopenharmony_ci	attr[(len + 3) / 4] = attr_data;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	/* record attribute header, update message length */
658c2ecf20Sopenharmony_ci	len <<= FM10K_TLV_LEN_SHIFT;
668c2ecf20Sopenharmony_ci	attr[0] = len | attr_id;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	/* add header length to length */
698c2ecf20Sopenharmony_ci	len += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT;
708c2ecf20Sopenharmony_ci	*msg += FM10K_TLV_LEN_ALIGN(len);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	return 0;
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/**
768c2ecf20Sopenharmony_ci *  fm10k_tlv_attr_get_null_string - Get null terminated string from attribute
778c2ecf20Sopenharmony_ci *  @attr: Pointer to attribute
788c2ecf20Sopenharmony_ci *  @string: Pointer to location of destination string
798c2ecf20Sopenharmony_ci *
808c2ecf20Sopenharmony_ci *  This function pulls the string back out of the attribute and will place
818c2ecf20Sopenharmony_ci *  it in the array pointed by by string.  It will return success if provided
828c2ecf20Sopenharmony_ci *  with a valid pointers.
838c2ecf20Sopenharmony_ci **/
848c2ecf20Sopenharmony_cistatic s32 fm10k_tlv_attr_get_null_string(u32 *attr, unsigned char *string)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	u32 len;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	/* verify pointers are not NULL */
898c2ecf20Sopenharmony_ci	if (!string || !attr)
908c2ecf20Sopenharmony_ci		return FM10K_ERR_PARAM;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	len = *attr >> FM10K_TLV_LEN_SHIFT;
938c2ecf20Sopenharmony_ci	attr++;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	while (len--)
968c2ecf20Sopenharmony_ci		string[len] = (u8)(attr[len / 4] >> (8 * (len % 4)));
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	return 0;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/**
1028c2ecf20Sopenharmony_ci *  fm10k_tlv_attr_put_mac_vlan - Store MAC/VLAN attribute in message
1038c2ecf20Sopenharmony_ci *  @msg: Pointer to message block
1048c2ecf20Sopenharmony_ci *  @attr_id: Attribute ID
1058c2ecf20Sopenharmony_ci *  @mac_addr: MAC address to be stored
1068c2ecf20Sopenharmony_ci *  @vlan: VLAN to be stored
1078c2ecf20Sopenharmony_ci *
1088c2ecf20Sopenharmony_ci *  This function will reorder a MAC address to be CPU endian and store it
1098c2ecf20Sopenharmony_ci *  in the attribute buffer.  It will return success if provided with a
1108c2ecf20Sopenharmony_ci *  valid pointers.
1118c2ecf20Sopenharmony_ci **/
1128c2ecf20Sopenharmony_cis32 fm10k_tlv_attr_put_mac_vlan(u32 *msg, u16 attr_id,
1138c2ecf20Sopenharmony_ci				const u8 *mac_addr, u16 vlan)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	u32 len = ETH_ALEN << FM10K_TLV_LEN_SHIFT;
1168c2ecf20Sopenharmony_ci	u32 *attr;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	/* verify pointers are not NULL */
1198c2ecf20Sopenharmony_ci	if (!msg || !mac_addr)
1208c2ecf20Sopenharmony_ci		return FM10K_ERR_PARAM;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	attr = &msg[FM10K_TLV_DWORD_LEN(*msg)];
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	/* record attribute header, update message length */
1258c2ecf20Sopenharmony_ci	attr[0] = len | attr_id;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/* copy value into local variable and then write to msg */
1288c2ecf20Sopenharmony_ci	attr[1] = le32_to_cpu(*(const __le32 *)&mac_addr[0]);
1298c2ecf20Sopenharmony_ci	attr[2] = le16_to_cpu(*(const __le16 *)&mac_addr[4]);
1308c2ecf20Sopenharmony_ci	attr[2] |= (u32)vlan << 16;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/* add header length to length */
1338c2ecf20Sopenharmony_ci	len += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT;
1348c2ecf20Sopenharmony_ci	*msg += FM10K_TLV_LEN_ALIGN(len);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	return 0;
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci/**
1408c2ecf20Sopenharmony_ci *  fm10k_tlv_attr_get_mac_vlan - Get MAC/VLAN stored in attribute
1418c2ecf20Sopenharmony_ci *  @attr: Pointer to attribute
1428c2ecf20Sopenharmony_ci *  @mac_addr: location of buffer to store MAC address
1438c2ecf20Sopenharmony_ci *  @vlan: location of buffer to store VLAN
1448c2ecf20Sopenharmony_ci *
1458c2ecf20Sopenharmony_ci *  This function pulls the MAC address back out of the attribute and will
1468c2ecf20Sopenharmony_ci *  place it in the array pointed by by mac_addr.  It will return success
1478c2ecf20Sopenharmony_ci *  if provided with a valid pointers.
1488c2ecf20Sopenharmony_ci **/
1498c2ecf20Sopenharmony_cis32 fm10k_tlv_attr_get_mac_vlan(u32 *attr, u8 *mac_addr, u16 *vlan)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	/* verify pointers are not NULL */
1528c2ecf20Sopenharmony_ci	if (!mac_addr || !attr)
1538c2ecf20Sopenharmony_ci		return FM10K_ERR_PARAM;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	*(__le32 *)&mac_addr[0] = cpu_to_le32(attr[1]);
1568c2ecf20Sopenharmony_ci	*(__le16 *)&mac_addr[4] = cpu_to_le16((u16)(attr[2]));
1578c2ecf20Sopenharmony_ci	*vlan = (u16)(attr[2] >> 16);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	return 0;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci/**
1638c2ecf20Sopenharmony_ci *  fm10k_tlv_attr_put_bool - Add header indicating value "true"
1648c2ecf20Sopenharmony_ci *  @msg: Pointer to message block
1658c2ecf20Sopenharmony_ci *  @attr_id: Attribute ID
1668c2ecf20Sopenharmony_ci *
1678c2ecf20Sopenharmony_ci *  This function will simply add an attribute header, the fact
1688c2ecf20Sopenharmony_ci *  that the header is here means the attribute value is true, else
1698c2ecf20Sopenharmony_ci *  it is false.  The function will return success if provided with a
1708c2ecf20Sopenharmony_ci *  valid pointers.
1718c2ecf20Sopenharmony_ci **/
1728c2ecf20Sopenharmony_cis32 fm10k_tlv_attr_put_bool(u32 *msg, u16 attr_id)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	/* verify pointers are not NULL */
1758c2ecf20Sopenharmony_ci	if (!msg)
1768c2ecf20Sopenharmony_ci		return FM10K_ERR_PARAM;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	/* record attribute header */
1798c2ecf20Sopenharmony_ci	msg[FM10K_TLV_DWORD_LEN(*msg)] = attr_id;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	/* add header length to length */
1828c2ecf20Sopenharmony_ci	*msg += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	return 0;
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci/**
1888c2ecf20Sopenharmony_ci *  fm10k_tlv_attr_put_value - Store integer value attribute in message
1898c2ecf20Sopenharmony_ci *  @msg: Pointer to message block
1908c2ecf20Sopenharmony_ci *  @attr_id: Attribute ID
1918c2ecf20Sopenharmony_ci *  @value: Value to be written
1928c2ecf20Sopenharmony_ci *  @len: Size of value
1938c2ecf20Sopenharmony_ci *
1948c2ecf20Sopenharmony_ci *  This function will place an integer value of up to 8 bytes in size
1958c2ecf20Sopenharmony_ci *  in a message attribute.  The function will return success provided
1968c2ecf20Sopenharmony_ci *  that msg is a valid pointer, and len is 1, 2, 4, or 8.
1978c2ecf20Sopenharmony_ci **/
1988c2ecf20Sopenharmony_cis32 fm10k_tlv_attr_put_value(u32 *msg, u16 attr_id, s64 value, u32 len)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	u32 *attr;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	/* verify non-null msg and len is 1, 2, 4, or 8 */
2038c2ecf20Sopenharmony_ci	if (!msg || !len || len > 8 || (len & (len - 1)))
2048c2ecf20Sopenharmony_ci		return FM10K_ERR_PARAM;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	attr = &msg[FM10K_TLV_DWORD_LEN(*msg)];
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	if (len < 4) {
2098c2ecf20Sopenharmony_ci		attr[1] = (u32)value & (BIT(8 * len) - 1);
2108c2ecf20Sopenharmony_ci	} else {
2118c2ecf20Sopenharmony_ci		attr[1] = (u32)value;
2128c2ecf20Sopenharmony_ci		if (len > 4)
2138c2ecf20Sopenharmony_ci			attr[2] = (u32)(value >> 32);
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	/* record attribute header, update message length */
2178c2ecf20Sopenharmony_ci	len <<= FM10K_TLV_LEN_SHIFT;
2188c2ecf20Sopenharmony_ci	attr[0] = len | attr_id;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	/* add header length to length */
2218c2ecf20Sopenharmony_ci	len += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT;
2228c2ecf20Sopenharmony_ci	*msg += FM10K_TLV_LEN_ALIGN(len);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	return 0;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci/**
2288c2ecf20Sopenharmony_ci *  fm10k_tlv_attr_get_value - Get integer value stored in attribute
2298c2ecf20Sopenharmony_ci *  @attr: Pointer to attribute
2308c2ecf20Sopenharmony_ci *  @value: Pointer to destination buffer
2318c2ecf20Sopenharmony_ci *  @len: Size of value
2328c2ecf20Sopenharmony_ci *
2338c2ecf20Sopenharmony_ci *  This function will place an integer value of up to 8 bytes in size
2348c2ecf20Sopenharmony_ci *  in the offset pointed to by value.  The function will return success
2358c2ecf20Sopenharmony_ci *  provided that pointers are valid and the len value matches the
2368c2ecf20Sopenharmony_ci *  attribute length.
2378c2ecf20Sopenharmony_ci **/
2388c2ecf20Sopenharmony_cis32 fm10k_tlv_attr_get_value(u32 *attr, void *value, u32 len)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	/* verify pointers are not NULL */
2418c2ecf20Sopenharmony_ci	if (!attr || !value)
2428c2ecf20Sopenharmony_ci		return FM10K_ERR_PARAM;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if ((*attr >> FM10K_TLV_LEN_SHIFT) != len)
2458c2ecf20Sopenharmony_ci		return FM10K_ERR_PARAM;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	if (len == 8)
2488c2ecf20Sopenharmony_ci		*(u64 *)value = ((u64)attr[2] << 32) | attr[1];
2498c2ecf20Sopenharmony_ci	else if (len == 4)
2508c2ecf20Sopenharmony_ci		*(u32 *)value = attr[1];
2518c2ecf20Sopenharmony_ci	else if (len == 2)
2528c2ecf20Sopenharmony_ci		*(u16 *)value = (u16)attr[1];
2538c2ecf20Sopenharmony_ci	else
2548c2ecf20Sopenharmony_ci		*(u8 *)value = (u8)attr[1];
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	return 0;
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci/**
2608c2ecf20Sopenharmony_ci *  fm10k_tlv_attr_put_le_struct - Store little endian structure in message
2618c2ecf20Sopenharmony_ci *  @msg: Pointer to message block
2628c2ecf20Sopenharmony_ci *  @attr_id: Attribute ID
2638c2ecf20Sopenharmony_ci *  @le_struct: Pointer to structure to be written
2648c2ecf20Sopenharmony_ci *  @len: Size of le_struct
2658c2ecf20Sopenharmony_ci *
2668c2ecf20Sopenharmony_ci *  This function will place a little endian structure value in a message
2678c2ecf20Sopenharmony_ci *  attribute.  The function will return success provided that all pointers
2688c2ecf20Sopenharmony_ci *  are valid and length is a non-zero multiple of 4.
2698c2ecf20Sopenharmony_ci **/
2708c2ecf20Sopenharmony_cis32 fm10k_tlv_attr_put_le_struct(u32 *msg, u16 attr_id,
2718c2ecf20Sopenharmony_ci				 const void *le_struct, u32 len)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	const __le32 *le32_ptr = (const __le32 *)le_struct;
2748c2ecf20Sopenharmony_ci	u32 *attr;
2758c2ecf20Sopenharmony_ci	u32 i;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	/* verify non-null msg and len is in 32 bit words */
2788c2ecf20Sopenharmony_ci	if (!msg || !len || (len % 4))
2798c2ecf20Sopenharmony_ci		return FM10K_ERR_PARAM;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	attr = &msg[FM10K_TLV_DWORD_LEN(*msg)];
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	/* copy le32 structure into host byte order at 32b boundaries */
2848c2ecf20Sopenharmony_ci	for (i = 0; i < (len / 4); i++)
2858c2ecf20Sopenharmony_ci		attr[i + 1] = le32_to_cpu(le32_ptr[i]);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	/* record attribute header, update message length */
2888c2ecf20Sopenharmony_ci	len <<= FM10K_TLV_LEN_SHIFT;
2898c2ecf20Sopenharmony_ci	attr[0] = len | attr_id;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	/* add header length to length */
2928c2ecf20Sopenharmony_ci	len += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT;
2938c2ecf20Sopenharmony_ci	*msg += FM10K_TLV_LEN_ALIGN(len);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	return 0;
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci/**
2998c2ecf20Sopenharmony_ci *  fm10k_tlv_attr_get_le_struct - Get little endian struct form attribute
3008c2ecf20Sopenharmony_ci *  @attr: Pointer to attribute
3018c2ecf20Sopenharmony_ci *  @le_struct: Pointer to structure to be written
3028c2ecf20Sopenharmony_ci *  @len: Size of structure
3038c2ecf20Sopenharmony_ci *
3048c2ecf20Sopenharmony_ci *  This function will place a little endian structure in the buffer
3058c2ecf20Sopenharmony_ci *  pointed to by le_struct.  The function will return success
3068c2ecf20Sopenharmony_ci *  provided that pointers are valid and the len value matches the
3078c2ecf20Sopenharmony_ci *  attribute length.
3088c2ecf20Sopenharmony_ci **/
3098c2ecf20Sopenharmony_cis32 fm10k_tlv_attr_get_le_struct(u32 *attr, void *le_struct, u32 len)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	__le32 *le32_ptr = (__le32 *)le_struct;
3128c2ecf20Sopenharmony_ci	u32 i;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	/* verify pointers are not NULL */
3158c2ecf20Sopenharmony_ci	if (!le_struct || !attr)
3168c2ecf20Sopenharmony_ci		return FM10K_ERR_PARAM;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if ((*attr >> FM10K_TLV_LEN_SHIFT) != len)
3198c2ecf20Sopenharmony_ci		return FM10K_ERR_PARAM;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	attr++;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	for (i = 0; len; i++, len -= 4)
3248c2ecf20Sopenharmony_ci		le32_ptr[i] = cpu_to_le32(attr[i]);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	return 0;
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci/**
3308c2ecf20Sopenharmony_ci *  fm10k_tlv_attr_nest_start - Start a set of nested attributes
3318c2ecf20Sopenharmony_ci *  @msg: Pointer to message block
3328c2ecf20Sopenharmony_ci *  @attr_id: Attribute ID
3338c2ecf20Sopenharmony_ci *
3348c2ecf20Sopenharmony_ci *  This function will mark off a new nested region for encapsulating
3358c2ecf20Sopenharmony_ci *  a given set of attributes.  The idea is if you wish to place a secondary
3368c2ecf20Sopenharmony_ci *  structure within the message this mechanism allows for that.  The
3378c2ecf20Sopenharmony_ci *  function will return NULL on failure, and a pointer to the start
3388c2ecf20Sopenharmony_ci *  of the nested attributes on success.
3398c2ecf20Sopenharmony_ci **/
3408c2ecf20Sopenharmony_cistatic u32 *fm10k_tlv_attr_nest_start(u32 *msg, u16 attr_id)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	u32 *attr;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	/* verify pointer is not NULL */
3458c2ecf20Sopenharmony_ci	if (!msg)
3468c2ecf20Sopenharmony_ci		return NULL;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	attr = &msg[FM10K_TLV_DWORD_LEN(*msg)];
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	attr[0] = attr_id;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	/* return pointer to nest header */
3538c2ecf20Sopenharmony_ci	return attr;
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci/**
3578c2ecf20Sopenharmony_ci *  fm10k_tlv_attr_nest_stop - Stop a set of nested attributes
3588c2ecf20Sopenharmony_ci *  @msg: Pointer to message block
3598c2ecf20Sopenharmony_ci *
3608c2ecf20Sopenharmony_ci *  This function closes off an existing set of nested attributes.  The
3618c2ecf20Sopenharmony_ci *  message pointer should be pointing to the parent of the nest.  So in
3628c2ecf20Sopenharmony_ci *  the case of a nest within the nest this would be the outer nest pointer.
3638c2ecf20Sopenharmony_ci *  This function will return success provided all pointers are valid.
3648c2ecf20Sopenharmony_ci **/
3658c2ecf20Sopenharmony_cistatic s32 fm10k_tlv_attr_nest_stop(u32 *msg)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	u32 *attr;
3688c2ecf20Sopenharmony_ci	u32 len;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	/* verify pointer is not NULL */
3718c2ecf20Sopenharmony_ci	if (!msg)
3728c2ecf20Sopenharmony_ci		return FM10K_ERR_PARAM;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	/* locate the nested header and retrieve its length */
3758c2ecf20Sopenharmony_ci	attr = &msg[FM10K_TLV_DWORD_LEN(*msg)];
3768c2ecf20Sopenharmony_ci	len = (attr[0] >> FM10K_TLV_LEN_SHIFT) << FM10K_TLV_LEN_SHIFT;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	/* only include nest if data was added to it */
3798c2ecf20Sopenharmony_ci	if (len) {
3808c2ecf20Sopenharmony_ci		len += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT;
3818c2ecf20Sopenharmony_ci		*msg += len;
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	return 0;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci/**
3888c2ecf20Sopenharmony_ci *  fm10k_tlv_attr_validate - Validate attribute metadata
3898c2ecf20Sopenharmony_ci *  @attr: Pointer to attribute
3908c2ecf20Sopenharmony_ci *  @tlv_attr: Type and length info for attribute
3918c2ecf20Sopenharmony_ci *
3928c2ecf20Sopenharmony_ci *  This function does some basic validation of the input TLV.  It
3938c2ecf20Sopenharmony_ci *  verifies the length, and in the case of null terminated strings
3948c2ecf20Sopenharmony_ci *  it verifies that the last byte is null.  The function will
3958c2ecf20Sopenharmony_ci *  return FM10K_ERR_PARAM if any attribute is malformed, otherwise
3968c2ecf20Sopenharmony_ci *  it returns 0.
3978c2ecf20Sopenharmony_ci **/
3988c2ecf20Sopenharmony_cistatic s32 fm10k_tlv_attr_validate(u32 *attr,
3998c2ecf20Sopenharmony_ci				   const struct fm10k_tlv_attr *tlv_attr)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	u32 attr_id = *attr & FM10K_TLV_ID_MASK;
4028c2ecf20Sopenharmony_ci	u16 len = *attr >> FM10K_TLV_LEN_SHIFT;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	/* verify this is an attribute and not a message */
4058c2ecf20Sopenharmony_ci	if (*attr & (FM10K_TLV_FLAGS_MSG << FM10K_TLV_FLAGS_SHIFT))
4068c2ecf20Sopenharmony_ci		return FM10K_ERR_PARAM;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	/* search through the list of attributes to find a matching ID */
4098c2ecf20Sopenharmony_ci	while (tlv_attr->id < attr_id)
4108c2ecf20Sopenharmony_ci		tlv_attr++;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	/* if didn't find a match then we should exit */
4138c2ecf20Sopenharmony_ci	if (tlv_attr->id != attr_id)
4148c2ecf20Sopenharmony_ci		return FM10K_NOT_IMPLEMENTED;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	/* move to start of attribute data */
4178c2ecf20Sopenharmony_ci	attr++;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	switch (tlv_attr->type) {
4208c2ecf20Sopenharmony_ci	case FM10K_TLV_NULL_STRING:
4218c2ecf20Sopenharmony_ci		if (!len ||
4228c2ecf20Sopenharmony_ci		    (attr[(len - 1) / 4] & (0xFF << (8 * ((len - 1) % 4)))))
4238c2ecf20Sopenharmony_ci			return FM10K_ERR_PARAM;
4248c2ecf20Sopenharmony_ci		if (len > tlv_attr->len)
4258c2ecf20Sopenharmony_ci			return FM10K_ERR_PARAM;
4268c2ecf20Sopenharmony_ci		break;
4278c2ecf20Sopenharmony_ci	case FM10K_TLV_MAC_ADDR:
4288c2ecf20Sopenharmony_ci		if (len != ETH_ALEN)
4298c2ecf20Sopenharmony_ci			return FM10K_ERR_PARAM;
4308c2ecf20Sopenharmony_ci		break;
4318c2ecf20Sopenharmony_ci	case FM10K_TLV_BOOL:
4328c2ecf20Sopenharmony_ci		if (len)
4338c2ecf20Sopenharmony_ci			return FM10K_ERR_PARAM;
4348c2ecf20Sopenharmony_ci		break;
4358c2ecf20Sopenharmony_ci	case FM10K_TLV_UNSIGNED:
4368c2ecf20Sopenharmony_ci	case FM10K_TLV_SIGNED:
4378c2ecf20Sopenharmony_ci		if (len != tlv_attr->len)
4388c2ecf20Sopenharmony_ci			return FM10K_ERR_PARAM;
4398c2ecf20Sopenharmony_ci		break;
4408c2ecf20Sopenharmony_ci	case FM10K_TLV_LE_STRUCT:
4418c2ecf20Sopenharmony_ci		/* struct must be 4 byte aligned */
4428c2ecf20Sopenharmony_ci		if ((len % 4) || len != tlv_attr->len)
4438c2ecf20Sopenharmony_ci			return FM10K_ERR_PARAM;
4448c2ecf20Sopenharmony_ci		break;
4458c2ecf20Sopenharmony_ci	case FM10K_TLV_NESTED:
4468c2ecf20Sopenharmony_ci		/* nested attributes must be 4 byte aligned */
4478c2ecf20Sopenharmony_ci		if (len % 4)
4488c2ecf20Sopenharmony_ci			return FM10K_ERR_PARAM;
4498c2ecf20Sopenharmony_ci		break;
4508c2ecf20Sopenharmony_ci	default:
4518c2ecf20Sopenharmony_ci		/* attribute id is mapped to bad value */
4528c2ecf20Sopenharmony_ci		return FM10K_ERR_PARAM;
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	return 0;
4568c2ecf20Sopenharmony_ci}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci/**
4598c2ecf20Sopenharmony_ci *  fm10k_tlv_attr_parse - Parses stream of attribute data
4608c2ecf20Sopenharmony_ci *  @attr: Pointer to attribute list
4618c2ecf20Sopenharmony_ci *  @results: Pointer array to store pointers to attributes
4628c2ecf20Sopenharmony_ci *  @tlv_attr: Type and length info for attributes
4638c2ecf20Sopenharmony_ci *
4648c2ecf20Sopenharmony_ci *  This function validates a stream of attributes and parses them
4658c2ecf20Sopenharmony_ci *  up into an array of pointers stored in results.  The function will
4668c2ecf20Sopenharmony_ci *  return FM10K_ERR_PARAM on any input or message error,
4678c2ecf20Sopenharmony_ci *  FM10K_NOT_IMPLEMENTED for any attribute that is outside of the array
4688c2ecf20Sopenharmony_ci *  and 0 on success. Any attributes not found in tlv_attr will be silently
4698c2ecf20Sopenharmony_ci *  ignored.
4708c2ecf20Sopenharmony_ci **/
4718c2ecf20Sopenharmony_cistatic s32 fm10k_tlv_attr_parse(u32 *attr, u32 **results,
4728c2ecf20Sopenharmony_ci				const struct fm10k_tlv_attr *tlv_attr)
4738c2ecf20Sopenharmony_ci{
4748c2ecf20Sopenharmony_ci	u32 i, attr_id, offset = 0;
4758c2ecf20Sopenharmony_ci	s32 err;
4768c2ecf20Sopenharmony_ci	u16 len;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	/* verify pointers are not NULL */
4798c2ecf20Sopenharmony_ci	if (!attr || !results)
4808c2ecf20Sopenharmony_ci		return FM10K_ERR_PARAM;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	/* initialize results to NULL */
4838c2ecf20Sopenharmony_ci	for (i = 0; i < FM10K_TLV_RESULTS_MAX; i++)
4848c2ecf20Sopenharmony_ci		results[i] = NULL;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	/* pull length from the message header */
4878c2ecf20Sopenharmony_ci	len = *attr >> FM10K_TLV_LEN_SHIFT;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	/* no attributes to parse if there is no length */
4908c2ecf20Sopenharmony_ci	if (!len)
4918c2ecf20Sopenharmony_ci		return 0;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	/* no attributes to parse, just raw data, message becomes attribute */
4948c2ecf20Sopenharmony_ci	if (!tlv_attr) {
4958c2ecf20Sopenharmony_ci		results[0] = attr;
4968c2ecf20Sopenharmony_ci		return 0;
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	/* move to start of attribute data */
5008c2ecf20Sopenharmony_ci	attr++;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	/* run through list parsing all attributes */
5038c2ecf20Sopenharmony_ci	while (offset < len) {
5048c2ecf20Sopenharmony_ci		attr_id = *attr & FM10K_TLV_ID_MASK;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci		if (attr_id >= FM10K_TLV_RESULTS_MAX)
5078c2ecf20Sopenharmony_ci			return FM10K_NOT_IMPLEMENTED;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci		err = fm10k_tlv_attr_validate(attr, tlv_attr);
5108c2ecf20Sopenharmony_ci		if (err == FM10K_NOT_IMPLEMENTED)
5118c2ecf20Sopenharmony_ci			; /* silently ignore non-implemented attributes */
5128c2ecf20Sopenharmony_ci		else if (err)
5138c2ecf20Sopenharmony_ci			return err;
5148c2ecf20Sopenharmony_ci		else
5158c2ecf20Sopenharmony_ci			results[attr_id] = attr;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci		/* update offset */
5188c2ecf20Sopenharmony_ci		offset += FM10K_TLV_DWORD_LEN(*attr) * 4;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci		/* move to next attribute */
5218c2ecf20Sopenharmony_ci		attr = &attr[FM10K_TLV_DWORD_LEN(*attr)];
5228c2ecf20Sopenharmony_ci	}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	/* we should find ourselves at the end of the list */
5258c2ecf20Sopenharmony_ci	if (offset != len)
5268c2ecf20Sopenharmony_ci		return FM10K_ERR_PARAM;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	return 0;
5298c2ecf20Sopenharmony_ci}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci/**
5328c2ecf20Sopenharmony_ci *  fm10k_tlv_msg_parse - Parses message header and calls function handler
5338c2ecf20Sopenharmony_ci *  @hw: Pointer to hardware structure
5348c2ecf20Sopenharmony_ci *  @msg: Pointer to message
5358c2ecf20Sopenharmony_ci *  @mbx: Pointer to mailbox information structure
5368c2ecf20Sopenharmony_ci *  @data: Pointer to message handler data structure
5378c2ecf20Sopenharmony_ci *
5388c2ecf20Sopenharmony_ci *  This function should be the first function called upon receiving a
5398c2ecf20Sopenharmony_ci *  message.  The handler will identify the message type and call the correct
5408c2ecf20Sopenharmony_ci *  handler for the given message.  It will return the value from the function
5418c2ecf20Sopenharmony_ci *  call on a recognized message type, otherwise it will return
5428c2ecf20Sopenharmony_ci *  FM10K_NOT_IMPLEMENTED on an unrecognized type.
5438c2ecf20Sopenharmony_ci **/
5448c2ecf20Sopenharmony_cis32 fm10k_tlv_msg_parse(struct fm10k_hw *hw, u32 *msg,
5458c2ecf20Sopenharmony_ci			struct fm10k_mbx_info *mbx,
5468c2ecf20Sopenharmony_ci			const struct fm10k_msg_data *data)
5478c2ecf20Sopenharmony_ci{
5488c2ecf20Sopenharmony_ci	u32 *results[FM10K_TLV_RESULTS_MAX];
5498c2ecf20Sopenharmony_ci	u32 msg_id;
5508c2ecf20Sopenharmony_ci	s32 err;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	/* verify pointer is not NULL */
5538c2ecf20Sopenharmony_ci	if (!msg || !data)
5548c2ecf20Sopenharmony_ci		return FM10K_ERR_PARAM;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	/* verify this is a message and not an attribute */
5578c2ecf20Sopenharmony_ci	if (!(*msg & (FM10K_TLV_FLAGS_MSG << FM10K_TLV_FLAGS_SHIFT)))
5588c2ecf20Sopenharmony_ci		return FM10K_ERR_PARAM;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	/* grab message ID */
5618c2ecf20Sopenharmony_ci	msg_id = *msg & FM10K_TLV_ID_MASK;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	while (data->id < msg_id)
5648c2ecf20Sopenharmony_ci		data++;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	/* if we didn't find it then pass it up as an error */
5678c2ecf20Sopenharmony_ci	if (data->id != msg_id) {
5688c2ecf20Sopenharmony_ci		while (data->id != FM10K_TLV_ERROR)
5698c2ecf20Sopenharmony_ci			data++;
5708c2ecf20Sopenharmony_ci	}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	/* parse the attributes into the results list */
5738c2ecf20Sopenharmony_ci	err = fm10k_tlv_attr_parse(msg, results, data->attr);
5748c2ecf20Sopenharmony_ci	if (err < 0)
5758c2ecf20Sopenharmony_ci		return err;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	return data->func(hw, results, mbx);
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci/**
5818c2ecf20Sopenharmony_ci *  fm10k_tlv_msg_error - Default handler for unrecognized TLV message IDs
5828c2ecf20Sopenharmony_ci *  @hw: Pointer to hardware structure
5838c2ecf20Sopenharmony_ci *  @results: Pointer array to message, results[0] is pointer to message
5848c2ecf20Sopenharmony_ci *  @mbx: Unused mailbox pointer
5858c2ecf20Sopenharmony_ci *
5868c2ecf20Sopenharmony_ci *  This function is a default handler for unrecognized messages.  At a
5878c2ecf20Sopenharmony_ci *  a minimum it just indicates that the message requested was
5888c2ecf20Sopenharmony_ci *  unimplemented.
5898c2ecf20Sopenharmony_ci **/
5908c2ecf20Sopenharmony_cis32 fm10k_tlv_msg_error(struct fm10k_hw __always_unused *hw,
5918c2ecf20Sopenharmony_ci			u32 __always_unused **results,
5928c2ecf20Sopenharmony_ci			struct fm10k_mbx_info __always_unused *mbx)
5938c2ecf20Sopenharmony_ci{
5948c2ecf20Sopenharmony_ci	return FM10K_NOT_IMPLEMENTED;
5958c2ecf20Sopenharmony_ci}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_cistatic const unsigned char test_str[] =	"fm10k";
5988c2ecf20Sopenharmony_cistatic const unsigned char test_mac[ETH_ALEN] = { 0x12, 0x34, 0x56,
5998c2ecf20Sopenharmony_ci						  0x78, 0x9a, 0xbc };
6008c2ecf20Sopenharmony_cistatic const u16 test_vlan = 0x0FED;
6018c2ecf20Sopenharmony_cistatic const u64 test_u64 = 0xfedcba9876543210ull;
6028c2ecf20Sopenharmony_cistatic const u32 test_u32 = 0x87654321;
6038c2ecf20Sopenharmony_cistatic const u16 test_u16 = 0x8765;
6048c2ecf20Sopenharmony_cistatic const u8  test_u8  = 0x87;
6058c2ecf20Sopenharmony_cistatic const s64 test_s64 = -0x123456789abcdef0ll;
6068c2ecf20Sopenharmony_cistatic const s32 test_s32 = -0x1235678;
6078c2ecf20Sopenharmony_cistatic const s16 test_s16 = -0x1234;
6088c2ecf20Sopenharmony_cistatic const s8  test_s8  = -0x12;
6098c2ecf20Sopenharmony_cistatic const __le32 test_le[2] = { cpu_to_le32(0x12345678),
6108c2ecf20Sopenharmony_ci				   cpu_to_le32(0x9abcdef0)};
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci/* The message below is meant to be used as a test message to demonstrate
6138c2ecf20Sopenharmony_ci * how to use the TLV interface and to test the types.  Normally this code
6148c2ecf20Sopenharmony_ci * be compiled out by stripping the code wrapped in FM10K_TLV_TEST_MSG
6158c2ecf20Sopenharmony_ci */
6168c2ecf20Sopenharmony_ciconst struct fm10k_tlv_attr fm10k_tlv_msg_test_attr[] = {
6178c2ecf20Sopenharmony_ci	FM10K_TLV_ATTR_NULL_STRING(FM10K_TEST_MSG_STRING, 80),
6188c2ecf20Sopenharmony_ci	FM10K_TLV_ATTR_MAC_ADDR(FM10K_TEST_MSG_MAC_ADDR),
6198c2ecf20Sopenharmony_ci	FM10K_TLV_ATTR_U8(FM10K_TEST_MSG_U8),
6208c2ecf20Sopenharmony_ci	FM10K_TLV_ATTR_U16(FM10K_TEST_MSG_U16),
6218c2ecf20Sopenharmony_ci	FM10K_TLV_ATTR_U32(FM10K_TEST_MSG_U32),
6228c2ecf20Sopenharmony_ci	FM10K_TLV_ATTR_U64(FM10K_TEST_MSG_U64),
6238c2ecf20Sopenharmony_ci	FM10K_TLV_ATTR_S8(FM10K_TEST_MSG_S8),
6248c2ecf20Sopenharmony_ci	FM10K_TLV_ATTR_S16(FM10K_TEST_MSG_S16),
6258c2ecf20Sopenharmony_ci	FM10K_TLV_ATTR_S32(FM10K_TEST_MSG_S32),
6268c2ecf20Sopenharmony_ci	FM10K_TLV_ATTR_S64(FM10K_TEST_MSG_S64),
6278c2ecf20Sopenharmony_ci	FM10K_TLV_ATTR_LE_STRUCT(FM10K_TEST_MSG_LE_STRUCT, 8),
6288c2ecf20Sopenharmony_ci	FM10K_TLV_ATTR_NESTED(FM10K_TEST_MSG_NESTED),
6298c2ecf20Sopenharmony_ci	FM10K_TLV_ATTR_S32(FM10K_TEST_MSG_RESULT),
6308c2ecf20Sopenharmony_ci	FM10K_TLV_ATTR_LAST
6318c2ecf20Sopenharmony_ci};
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci/**
6348c2ecf20Sopenharmony_ci *  fm10k_tlv_msg_test_generate_data - Stuff message with data
6358c2ecf20Sopenharmony_ci *  @msg: Pointer to message
6368c2ecf20Sopenharmony_ci *  @attr_flags: List of flags indicating what attributes to add
6378c2ecf20Sopenharmony_ci *
6388c2ecf20Sopenharmony_ci *  This function is meant to load a message buffer with attribute data
6398c2ecf20Sopenharmony_ci **/
6408c2ecf20Sopenharmony_cistatic void fm10k_tlv_msg_test_generate_data(u32 *msg, u32 attr_flags)
6418c2ecf20Sopenharmony_ci{
6428c2ecf20Sopenharmony_ci	if (attr_flags & BIT(FM10K_TEST_MSG_STRING))
6438c2ecf20Sopenharmony_ci		fm10k_tlv_attr_put_null_string(msg, FM10K_TEST_MSG_STRING,
6448c2ecf20Sopenharmony_ci					       test_str);
6458c2ecf20Sopenharmony_ci	if (attr_flags & BIT(FM10K_TEST_MSG_MAC_ADDR))
6468c2ecf20Sopenharmony_ci		fm10k_tlv_attr_put_mac_vlan(msg, FM10K_TEST_MSG_MAC_ADDR,
6478c2ecf20Sopenharmony_ci					    test_mac, test_vlan);
6488c2ecf20Sopenharmony_ci	if (attr_flags & BIT(FM10K_TEST_MSG_U8))
6498c2ecf20Sopenharmony_ci		fm10k_tlv_attr_put_u8(msg, FM10K_TEST_MSG_U8,  test_u8);
6508c2ecf20Sopenharmony_ci	if (attr_flags & BIT(FM10K_TEST_MSG_U16))
6518c2ecf20Sopenharmony_ci		fm10k_tlv_attr_put_u16(msg, FM10K_TEST_MSG_U16, test_u16);
6528c2ecf20Sopenharmony_ci	if (attr_flags & BIT(FM10K_TEST_MSG_U32))
6538c2ecf20Sopenharmony_ci		fm10k_tlv_attr_put_u32(msg, FM10K_TEST_MSG_U32, test_u32);
6548c2ecf20Sopenharmony_ci	if (attr_flags & BIT(FM10K_TEST_MSG_U64))
6558c2ecf20Sopenharmony_ci		fm10k_tlv_attr_put_u64(msg, FM10K_TEST_MSG_U64, test_u64);
6568c2ecf20Sopenharmony_ci	if (attr_flags & BIT(FM10K_TEST_MSG_S8))
6578c2ecf20Sopenharmony_ci		fm10k_tlv_attr_put_s8(msg, FM10K_TEST_MSG_S8,  test_s8);
6588c2ecf20Sopenharmony_ci	if (attr_flags & BIT(FM10K_TEST_MSG_S16))
6598c2ecf20Sopenharmony_ci		fm10k_tlv_attr_put_s16(msg, FM10K_TEST_MSG_S16, test_s16);
6608c2ecf20Sopenharmony_ci	if (attr_flags & BIT(FM10K_TEST_MSG_S32))
6618c2ecf20Sopenharmony_ci		fm10k_tlv_attr_put_s32(msg, FM10K_TEST_MSG_S32, test_s32);
6628c2ecf20Sopenharmony_ci	if (attr_flags & BIT(FM10K_TEST_MSG_S64))
6638c2ecf20Sopenharmony_ci		fm10k_tlv_attr_put_s64(msg, FM10K_TEST_MSG_S64, test_s64);
6648c2ecf20Sopenharmony_ci	if (attr_flags & BIT(FM10K_TEST_MSG_LE_STRUCT))
6658c2ecf20Sopenharmony_ci		fm10k_tlv_attr_put_le_struct(msg, FM10K_TEST_MSG_LE_STRUCT,
6668c2ecf20Sopenharmony_ci					     test_le, 8);
6678c2ecf20Sopenharmony_ci}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci/**
6708c2ecf20Sopenharmony_ci *  fm10k_tlv_msg_test_create - Create a test message testing all attributes
6718c2ecf20Sopenharmony_ci *  @msg: Pointer to message
6728c2ecf20Sopenharmony_ci *  @attr_flags: List of flags indicating what attributes to add
6738c2ecf20Sopenharmony_ci *
6748c2ecf20Sopenharmony_ci *  This function is meant to load a message buffer with all attribute types
6758c2ecf20Sopenharmony_ci *  including a nested attribute.
6768c2ecf20Sopenharmony_ci **/
6778c2ecf20Sopenharmony_civoid fm10k_tlv_msg_test_create(u32 *msg, u32 attr_flags)
6788c2ecf20Sopenharmony_ci{
6798c2ecf20Sopenharmony_ci	u32 *nest = NULL;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	fm10k_tlv_msg_init(msg, FM10K_TLV_MSG_ID_TEST);
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	fm10k_tlv_msg_test_generate_data(msg, attr_flags);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	/* check for nested attributes */
6868c2ecf20Sopenharmony_ci	attr_flags >>= FM10K_TEST_MSG_NESTED;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	if (attr_flags) {
6898c2ecf20Sopenharmony_ci		nest = fm10k_tlv_attr_nest_start(msg, FM10K_TEST_MSG_NESTED);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci		fm10k_tlv_msg_test_generate_data(nest, attr_flags);
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci		fm10k_tlv_attr_nest_stop(msg);
6948c2ecf20Sopenharmony_ci	}
6958c2ecf20Sopenharmony_ci}
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci/**
6988c2ecf20Sopenharmony_ci *  fm10k_tlv_msg_test - Validate all results on test message receive
6998c2ecf20Sopenharmony_ci *  @hw: Pointer to hardware structure
7008c2ecf20Sopenharmony_ci *  @results: Pointer array to attributes in the message
7018c2ecf20Sopenharmony_ci *  @mbx: Pointer to mailbox information structure
7028c2ecf20Sopenharmony_ci *
7038c2ecf20Sopenharmony_ci *  This function does a check to verify all attributes match what the test
7048c2ecf20Sopenharmony_ci *  message placed in the message buffer.  It is the default handler
7058c2ecf20Sopenharmony_ci *  for TLV test messages.
7068c2ecf20Sopenharmony_ci **/
7078c2ecf20Sopenharmony_cis32 fm10k_tlv_msg_test(struct fm10k_hw *hw, u32 **results,
7088c2ecf20Sopenharmony_ci		       struct fm10k_mbx_info *mbx)
7098c2ecf20Sopenharmony_ci{
7108c2ecf20Sopenharmony_ci	u32 *nest_results[FM10K_TLV_RESULTS_MAX];
7118c2ecf20Sopenharmony_ci	unsigned char result_str[80];
7128c2ecf20Sopenharmony_ci	unsigned char result_mac[ETH_ALEN];
7138c2ecf20Sopenharmony_ci	s32 err = 0;
7148c2ecf20Sopenharmony_ci	__le32 result_le[2];
7158c2ecf20Sopenharmony_ci	u16 result_vlan;
7168c2ecf20Sopenharmony_ci	u64 result_u64;
7178c2ecf20Sopenharmony_ci	u32 result_u32;
7188c2ecf20Sopenharmony_ci	u16 result_u16;
7198c2ecf20Sopenharmony_ci	u8  result_u8;
7208c2ecf20Sopenharmony_ci	s64 result_s64;
7218c2ecf20Sopenharmony_ci	s32 result_s32;
7228c2ecf20Sopenharmony_ci	s16 result_s16;
7238c2ecf20Sopenharmony_ci	s8  result_s8;
7248c2ecf20Sopenharmony_ci	u32 reply[3];
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	/* retrieve results of a previous test */
7278c2ecf20Sopenharmony_ci	if (!!results[FM10K_TEST_MSG_RESULT])
7288c2ecf20Sopenharmony_ci		return fm10k_tlv_attr_get_s32(results[FM10K_TEST_MSG_RESULT],
7298c2ecf20Sopenharmony_ci					      &mbx->test_result);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ciparse_nested:
7328c2ecf20Sopenharmony_ci	if (!!results[FM10K_TEST_MSG_STRING]) {
7338c2ecf20Sopenharmony_ci		err = fm10k_tlv_attr_get_null_string(
7348c2ecf20Sopenharmony_ci					results[FM10K_TEST_MSG_STRING],
7358c2ecf20Sopenharmony_ci					result_str);
7368c2ecf20Sopenharmony_ci		if (!err && memcmp(test_str, result_str, sizeof(test_str)))
7378c2ecf20Sopenharmony_ci			err = FM10K_ERR_INVALID_VALUE;
7388c2ecf20Sopenharmony_ci		if (err)
7398c2ecf20Sopenharmony_ci			goto report_result;
7408c2ecf20Sopenharmony_ci	}
7418c2ecf20Sopenharmony_ci	if (!!results[FM10K_TEST_MSG_MAC_ADDR]) {
7428c2ecf20Sopenharmony_ci		err = fm10k_tlv_attr_get_mac_vlan(
7438c2ecf20Sopenharmony_ci					results[FM10K_TEST_MSG_MAC_ADDR],
7448c2ecf20Sopenharmony_ci					result_mac, &result_vlan);
7458c2ecf20Sopenharmony_ci		if (!err && !ether_addr_equal(test_mac, result_mac))
7468c2ecf20Sopenharmony_ci			err = FM10K_ERR_INVALID_VALUE;
7478c2ecf20Sopenharmony_ci		if (!err && test_vlan != result_vlan)
7488c2ecf20Sopenharmony_ci			err = FM10K_ERR_INVALID_VALUE;
7498c2ecf20Sopenharmony_ci		if (err)
7508c2ecf20Sopenharmony_ci			goto report_result;
7518c2ecf20Sopenharmony_ci	}
7528c2ecf20Sopenharmony_ci	if (!!results[FM10K_TEST_MSG_U8]) {
7538c2ecf20Sopenharmony_ci		err = fm10k_tlv_attr_get_u8(results[FM10K_TEST_MSG_U8],
7548c2ecf20Sopenharmony_ci					    &result_u8);
7558c2ecf20Sopenharmony_ci		if (!err && test_u8 != result_u8)
7568c2ecf20Sopenharmony_ci			err = FM10K_ERR_INVALID_VALUE;
7578c2ecf20Sopenharmony_ci		if (err)
7588c2ecf20Sopenharmony_ci			goto report_result;
7598c2ecf20Sopenharmony_ci	}
7608c2ecf20Sopenharmony_ci	if (!!results[FM10K_TEST_MSG_U16]) {
7618c2ecf20Sopenharmony_ci		err = fm10k_tlv_attr_get_u16(results[FM10K_TEST_MSG_U16],
7628c2ecf20Sopenharmony_ci					     &result_u16);
7638c2ecf20Sopenharmony_ci		if (!err && test_u16 != result_u16)
7648c2ecf20Sopenharmony_ci			err = FM10K_ERR_INVALID_VALUE;
7658c2ecf20Sopenharmony_ci		if (err)
7668c2ecf20Sopenharmony_ci			goto report_result;
7678c2ecf20Sopenharmony_ci	}
7688c2ecf20Sopenharmony_ci	if (!!results[FM10K_TEST_MSG_U32]) {
7698c2ecf20Sopenharmony_ci		err = fm10k_tlv_attr_get_u32(results[FM10K_TEST_MSG_U32],
7708c2ecf20Sopenharmony_ci					     &result_u32);
7718c2ecf20Sopenharmony_ci		if (!err && test_u32 != result_u32)
7728c2ecf20Sopenharmony_ci			err = FM10K_ERR_INVALID_VALUE;
7738c2ecf20Sopenharmony_ci		if (err)
7748c2ecf20Sopenharmony_ci			goto report_result;
7758c2ecf20Sopenharmony_ci	}
7768c2ecf20Sopenharmony_ci	if (!!results[FM10K_TEST_MSG_U64]) {
7778c2ecf20Sopenharmony_ci		err = fm10k_tlv_attr_get_u64(results[FM10K_TEST_MSG_U64],
7788c2ecf20Sopenharmony_ci					     &result_u64);
7798c2ecf20Sopenharmony_ci		if (!err && test_u64 != result_u64)
7808c2ecf20Sopenharmony_ci			err = FM10K_ERR_INVALID_VALUE;
7818c2ecf20Sopenharmony_ci		if (err)
7828c2ecf20Sopenharmony_ci			goto report_result;
7838c2ecf20Sopenharmony_ci	}
7848c2ecf20Sopenharmony_ci	if (!!results[FM10K_TEST_MSG_S8]) {
7858c2ecf20Sopenharmony_ci		err = fm10k_tlv_attr_get_s8(results[FM10K_TEST_MSG_S8],
7868c2ecf20Sopenharmony_ci					    &result_s8);
7878c2ecf20Sopenharmony_ci		if (!err && test_s8 != result_s8)
7888c2ecf20Sopenharmony_ci			err = FM10K_ERR_INVALID_VALUE;
7898c2ecf20Sopenharmony_ci		if (err)
7908c2ecf20Sopenharmony_ci			goto report_result;
7918c2ecf20Sopenharmony_ci	}
7928c2ecf20Sopenharmony_ci	if (!!results[FM10K_TEST_MSG_S16]) {
7938c2ecf20Sopenharmony_ci		err = fm10k_tlv_attr_get_s16(results[FM10K_TEST_MSG_S16],
7948c2ecf20Sopenharmony_ci					     &result_s16);
7958c2ecf20Sopenharmony_ci		if (!err && test_s16 != result_s16)
7968c2ecf20Sopenharmony_ci			err = FM10K_ERR_INVALID_VALUE;
7978c2ecf20Sopenharmony_ci		if (err)
7988c2ecf20Sopenharmony_ci			goto report_result;
7998c2ecf20Sopenharmony_ci	}
8008c2ecf20Sopenharmony_ci	if (!!results[FM10K_TEST_MSG_S32]) {
8018c2ecf20Sopenharmony_ci		err = fm10k_tlv_attr_get_s32(results[FM10K_TEST_MSG_S32],
8028c2ecf20Sopenharmony_ci					     &result_s32);
8038c2ecf20Sopenharmony_ci		if (!err && test_s32 != result_s32)
8048c2ecf20Sopenharmony_ci			err = FM10K_ERR_INVALID_VALUE;
8058c2ecf20Sopenharmony_ci		if (err)
8068c2ecf20Sopenharmony_ci			goto report_result;
8078c2ecf20Sopenharmony_ci	}
8088c2ecf20Sopenharmony_ci	if (!!results[FM10K_TEST_MSG_S64]) {
8098c2ecf20Sopenharmony_ci		err = fm10k_tlv_attr_get_s64(results[FM10K_TEST_MSG_S64],
8108c2ecf20Sopenharmony_ci					     &result_s64);
8118c2ecf20Sopenharmony_ci		if (!err && test_s64 != result_s64)
8128c2ecf20Sopenharmony_ci			err = FM10K_ERR_INVALID_VALUE;
8138c2ecf20Sopenharmony_ci		if (err)
8148c2ecf20Sopenharmony_ci			goto report_result;
8158c2ecf20Sopenharmony_ci	}
8168c2ecf20Sopenharmony_ci	if (!!results[FM10K_TEST_MSG_LE_STRUCT]) {
8178c2ecf20Sopenharmony_ci		err = fm10k_tlv_attr_get_le_struct(
8188c2ecf20Sopenharmony_ci					results[FM10K_TEST_MSG_LE_STRUCT],
8198c2ecf20Sopenharmony_ci					result_le,
8208c2ecf20Sopenharmony_ci					sizeof(result_le));
8218c2ecf20Sopenharmony_ci		if (!err && memcmp(test_le, result_le, sizeof(test_le)))
8228c2ecf20Sopenharmony_ci			err = FM10K_ERR_INVALID_VALUE;
8238c2ecf20Sopenharmony_ci		if (err)
8248c2ecf20Sopenharmony_ci			goto report_result;
8258c2ecf20Sopenharmony_ci	}
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	if (!!results[FM10K_TEST_MSG_NESTED]) {
8288c2ecf20Sopenharmony_ci		/* clear any pointers */
8298c2ecf20Sopenharmony_ci		memset(nest_results, 0, sizeof(nest_results));
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci		/* parse the nested attributes into the nest results list */
8328c2ecf20Sopenharmony_ci		err = fm10k_tlv_attr_parse(results[FM10K_TEST_MSG_NESTED],
8338c2ecf20Sopenharmony_ci					   nest_results,
8348c2ecf20Sopenharmony_ci					   fm10k_tlv_msg_test_attr);
8358c2ecf20Sopenharmony_ci		if (err)
8368c2ecf20Sopenharmony_ci			goto report_result;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci		/* loop back through to the start */
8398c2ecf20Sopenharmony_ci		results = nest_results;
8408c2ecf20Sopenharmony_ci		goto parse_nested;
8418c2ecf20Sopenharmony_ci	}
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_cireport_result:
8448c2ecf20Sopenharmony_ci	/* generate reply with test result */
8458c2ecf20Sopenharmony_ci	fm10k_tlv_msg_init(reply, FM10K_TLV_MSG_ID_TEST);
8468c2ecf20Sopenharmony_ci	fm10k_tlv_attr_put_s32(reply, FM10K_TEST_MSG_RESULT, err);
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	/* load onto outgoing mailbox */
8498c2ecf20Sopenharmony_ci	return mbx->ops.enqueue_tx(hw, mbx, reply);
8508c2ecf20Sopenharmony_ci}
851