1ae06c70bSJeff Kirsher // SPDX-License-Identifier: GPL-2.0
2d5c2f395SJacob Keller /* Copyright(c) 2013 - 2019 Intel Corporation. */
3ae17db0eSAlexander Duyck
4ae17db0eSAlexander Duyck #include "fm10k_tlv.h"
5ae17db0eSAlexander Duyck
6ae17db0eSAlexander Duyck /**
7ae17db0eSAlexander Duyck * fm10k_tlv_msg_init - Initialize message block for TLV data storage
8ae17db0eSAlexander Duyck * @msg: Pointer to message block
9ae17db0eSAlexander Duyck * @msg_id: Message ID indicating message type
10ae17db0eSAlexander Duyck *
11ae17db0eSAlexander Duyck * This function return success if provided with a valid message pointer
12ae17db0eSAlexander Duyck **/
fm10k_tlv_msg_init(u32 * msg,u16 msg_id)13ae17db0eSAlexander Duyck s32 fm10k_tlv_msg_init(u32 *msg, u16 msg_id)
14ae17db0eSAlexander Duyck {
15ae17db0eSAlexander Duyck /* verify pointer is not NULL */
16ae17db0eSAlexander Duyck if (!msg)
17ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
18ae17db0eSAlexander Duyck
19ae17db0eSAlexander Duyck *msg = (FM10K_TLV_FLAGS_MSG << FM10K_TLV_FLAGS_SHIFT) | msg_id;
20ae17db0eSAlexander Duyck
21ae17db0eSAlexander Duyck return 0;
22ae17db0eSAlexander Duyck }
23ae17db0eSAlexander Duyck
24ae17db0eSAlexander Duyck /**
25ae17db0eSAlexander Duyck * fm10k_tlv_attr_put_null_string - Place null terminated string on message
26ae17db0eSAlexander Duyck * @msg: Pointer to message block
27ae17db0eSAlexander Duyck * @attr_id: Attribute ID
28ae17db0eSAlexander Duyck * @string: Pointer to string to be stored in attribute
29ae17db0eSAlexander Duyck *
30ae17db0eSAlexander Duyck * This function will reorder a string to be CPU endian and store it in
31ae17db0eSAlexander Duyck * the attribute buffer. It will return success if provided with a valid
32ae17db0eSAlexander Duyck * pointers.
33ae17db0eSAlexander Duyck **/
fm10k_tlv_attr_put_null_string(u32 * msg,u16 attr_id,const unsigned char * string)34bb269e8bSBruce Allan static s32 fm10k_tlv_attr_put_null_string(u32 *msg, u16 attr_id,
35ae17db0eSAlexander Duyck const unsigned char *string)
36ae17db0eSAlexander Duyck {
37ae17db0eSAlexander Duyck u32 attr_data = 0, len = 0;
38ae17db0eSAlexander Duyck u32 *attr;
39ae17db0eSAlexander Duyck
40ae17db0eSAlexander Duyck /* verify pointers are not NULL */
41ae17db0eSAlexander Duyck if (!string || !msg)
42ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
43ae17db0eSAlexander Duyck
44ae17db0eSAlexander Duyck attr = &msg[FM10K_TLV_DWORD_LEN(*msg)];
45ae17db0eSAlexander Duyck
46ae17db0eSAlexander Duyck /* copy string into local variable and then write to msg */
47ae17db0eSAlexander Duyck do {
48ae17db0eSAlexander Duyck /* write data to message */
49ae17db0eSAlexander Duyck if (len && !(len % 4)) {
50ae17db0eSAlexander Duyck attr[len / 4] = attr_data;
51ae17db0eSAlexander Duyck attr_data = 0;
52ae17db0eSAlexander Duyck }
53ae17db0eSAlexander Duyck
54ae17db0eSAlexander Duyck /* record character to offset location */
55ae17db0eSAlexander Duyck attr_data |= (u32)(*string) << (8 * (len % 4));
56ae17db0eSAlexander Duyck len++;
57ae17db0eSAlexander Duyck
58ae17db0eSAlexander Duyck /* test for NULL and then increment */
59ae17db0eSAlexander Duyck } while (*(string++));
60ae17db0eSAlexander Duyck
61ae17db0eSAlexander Duyck /* write last piece of data to message */
62ae17db0eSAlexander Duyck attr[(len + 3) / 4] = attr_data;
63ae17db0eSAlexander Duyck
64ae17db0eSAlexander Duyck /* record attribute header, update message length */
65ae17db0eSAlexander Duyck len <<= FM10K_TLV_LEN_SHIFT;
66ae17db0eSAlexander Duyck attr[0] = len | attr_id;
67ae17db0eSAlexander Duyck
68ae17db0eSAlexander Duyck /* add header length to length */
69ae17db0eSAlexander Duyck len += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT;
70ae17db0eSAlexander Duyck *msg += FM10K_TLV_LEN_ALIGN(len);
71ae17db0eSAlexander Duyck
72ae17db0eSAlexander Duyck return 0;
73ae17db0eSAlexander Duyck }
74ae17db0eSAlexander Duyck
75ae17db0eSAlexander Duyck /**
76ae17db0eSAlexander Duyck * fm10k_tlv_attr_get_null_string - Get null terminated string from attribute
77ae17db0eSAlexander Duyck * @attr: Pointer to attribute
78ae17db0eSAlexander Duyck * @string: Pointer to location of destination string
79ae17db0eSAlexander Duyck *
80ae17db0eSAlexander Duyck * This function pulls the string back out of the attribute and will place
81*17527829SJilin Yuan * it in the array pointed by string. It will return success if provided
82ae17db0eSAlexander Duyck * with a valid pointers.
83ae17db0eSAlexander Duyck **/
fm10k_tlv_attr_get_null_string(u32 * attr,unsigned char * string)84bb269e8bSBruce Allan static s32 fm10k_tlv_attr_get_null_string(u32 *attr, unsigned char *string)
85ae17db0eSAlexander Duyck {
86ae17db0eSAlexander Duyck u32 len;
87ae17db0eSAlexander Duyck
88ae17db0eSAlexander Duyck /* verify pointers are not NULL */
89ae17db0eSAlexander Duyck if (!string || !attr)
90ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
91ae17db0eSAlexander Duyck
92ae17db0eSAlexander Duyck len = *attr >> FM10K_TLV_LEN_SHIFT;
93ae17db0eSAlexander Duyck attr++;
94ae17db0eSAlexander Duyck
95ae17db0eSAlexander Duyck while (len--)
96ae17db0eSAlexander Duyck string[len] = (u8)(attr[len / 4] >> (8 * (len % 4)));
97ae17db0eSAlexander Duyck
98ae17db0eSAlexander Duyck return 0;
99ae17db0eSAlexander Duyck }
100ae17db0eSAlexander Duyck
101ae17db0eSAlexander Duyck /**
102ae17db0eSAlexander Duyck * fm10k_tlv_attr_put_mac_vlan - Store MAC/VLAN attribute in message
103ae17db0eSAlexander Duyck * @msg: Pointer to message block
104ae17db0eSAlexander Duyck * @attr_id: Attribute ID
105ae17db0eSAlexander Duyck * @mac_addr: MAC address to be stored
106363656ebSJacob Keller * @vlan: VLAN to be stored
107ae17db0eSAlexander Duyck *
108ae17db0eSAlexander Duyck * This function will reorder a MAC address to be CPU endian and store it
109ae17db0eSAlexander Duyck * in the attribute buffer. It will return success if provided with a
110ae17db0eSAlexander Duyck * valid pointers.
111ae17db0eSAlexander Duyck **/
fm10k_tlv_attr_put_mac_vlan(u32 * msg,u16 attr_id,const u8 * mac_addr,u16 vlan)112ae17db0eSAlexander Duyck s32 fm10k_tlv_attr_put_mac_vlan(u32 *msg, u16 attr_id,
113ae17db0eSAlexander Duyck const u8 *mac_addr, u16 vlan)
114ae17db0eSAlexander Duyck {
115ae17db0eSAlexander Duyck u32 len = ETH_ALEN << FM10K_TLV_LEN_SHIFT;
116ae17db0eSAlexander Duyck u32 *attr;
117ae17db0eSAlexander Duyck
118ae17db0eSAlexander Duyck /* verify pointers are not NULL */
119ae17db0eSAlexander Duyck if (!msg || !mac_addr)
120ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
121ae17db0eSAlexander Duyck
122ae17db0eSAlexander Duyck attr = &msg[FM10K_TLV_DWORD_LEN(*msg)];
123ae17db0eSAlexander Duyck
124ae17db0eSAlexander Duyck /* record attribute header, update message length */
125ae17db0eSAlexander Duyck attr[0] = len | attr_id;
126ae17db0eSAlexander Duyck
127ae17db0eSAlexander Duyck /* copy value into local variable and then write to msg */
128ae17db0eSAlexander Duyck attr[1] = le32_to_cpu(*(const __le32 *)&mac_addr[0]);
129ae17db0eSAlexander Duyck attr[2] = le16_to_cpu(*(const __le16 *)&mac_addr[4]);
130ae17db0eSAlexander Duyck attr[2] |= (u32)vlan << 16;
131ae17db0eSAlexander Duyck
132ae17db0eSAlexander Duyck /* add header length to length */
133ae17db0eSAlexander Duyck len += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT;
134ae17db0eSAlexander Duyck *msg += FM10K_TLV_LEN_ALIGN(len);
135ae17db0eSAlexander Duyck
136ae17db0eSAlexander Duyck return 0;
137ae17db0eSAlexander Duyck }
138ae17db0eSAlexander Duyck
139ae17db0eSAlexander Duyck /**
140ae17db0eSAlexander Duyck * fm10k_tlv_attr_get_mac_vlan - Get MAC/VLAN stored in attribute
141ae17db0eSAlexander Duyck * @attr: Pointer to attribute
142ae17db0eSAlexander Duyck * @mac_addr: location of buffer to store MAC address
143363656ebSJacob Keller * @vlan: location of buffer to store VLAN
144ae17db0eSAlexander Duyck *
145ae17db0eSAlexander Duyck * This function pulls the MAC address back out of the attribute and will
14637cf276dSXiang wangx * place it in the array pointed by mac_addr. It will return success
147ae17db0eSAlexander Duyck * if provided with a valid pointers.
148ae17db0eSAlexander Duyck **/
fm10k_tlv_attr_get_mac_vlan(u32 * attr,u8 * mac_addr,u16 * vlan)149ae17db0eSAlexander Duyck s32 fm10k_tlv_attr_get_mac_vlan(u32 *attr, u8 *mac_addr, u16 *vlan)
150ae17db0eSAlexander Duyck {
151ae17db0eSAlexander Duyck /* verify pointers are not NULL */
152ae17db0eSAlexander Duyck if (!mac_addr || !attr)
153ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
154ae17db0eSAlexander Duyck
155ae17db0eSAlexander Duyck *(__le32 *)&mac_addr[0] = cpu_to_le32(attr[1]);
156ae17db0eSAlexander Duyck *(__le16 *)&mac_addr[4] = cpu_to_le16((u16)(attr[2]));
157ae17db0eSAlexander Duyck *vlan = (u16)(attr[2] >> 16);
158ae17db0eSAlexander Duyck
159ae17db0eSAlexander Duyck return 0;
160ae17db0eSAlexander Duyck }
161ae17db0eSAlexander Duyck
162ae17db0eSAlexander Duyck /**
163ae17db0eSAlexander Duyck * fm10k_tlv_attr_put_bool - Add header indicating value "true"
164ae17db0eSAlexander Duyck * @msg: Pointer to message block
165ae17db0eSAlexander Duyck * @attr_id: Attribute ID
166ae17db0eSAlexander Duyck *
167ae17db0eSAlexander Duyck * This function will simply add an attribute header, the fact
168ae17db0eSAlexander Duyck * that the header is here means the attribute value is true, else
169ae17db0eSAlexander Duyck * it is false. The function will return success if provided with a
170ae17db0eSAlexander Duyck * valid pointers.
171ae17db0eSAlexander Duyck **/
fm10k_tlv_attr_put_bool(u32 * msg,u16 attr_id)172ae17db0eSAlexander Duyck s32 fm10k_tlv_attr_put_bool(u32 *msg, u16 attr_id)
173ae17db0eSAlexander Duyck {
174ae17db0eSAlexander Duyck /* verify pointers are not NULL */
175ae17db0eSAlexander Duyck if (!msg)
176ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
177ae17db0eSAlexander Duyck
178ae17db0eSAlexander Duyck /* record attribute header */
179ae17db0eSAlexander Duyck msg[FM10K_TLV_DWORD_LEN(*msg)] = attr_id;
180ae17db0eSAlexander Duyck
181ae17db0eSAlexander Duyck /* add header length to length */
182ae17db0eSAlexander Duyck *msg += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT;
183ae17db0eSAlexander Duyck
184ae17db0eSAlexander Duyck return 0;
185ae17db0eSAlexander Duyck }
186ae17db0eSAlexander Duyck
187ae17db0eSAlexander Duyck /**
188ae17db0eSAlexander Duyck * fm10k_tlv_attr_put_value - Store integer value attribute in message
189ae17db0eSAlexander Duyck * @msg: Pointer to message block
190ae17db0eSAlexander Duyck * @attr_id: Attribute ID
191ae17db0eSAlexander Duyck * @value: Value to be written
192ae17db0eSAlexander Duyck * @len: Size of value
193ae17db0eSAlexander Duyck *
194ae17db0eSAlexander Duyck * This function will place an integer value of up to 8 bytes in size
195ae17db0eSAlexander Duyck * in a message attribute. The function will return success provided
196ae17db0eSAlexander Duyck * that msg is a valid pointer, and len is 1, 2, 4, or 8.
197ae17db0eSAlexander Duyck **/
fm10k_tlv_attr_put_value(u32 * msg,u16 attr_id,s64 value,u32 len)198ae17db0eSAlexander Duyck s32 fm10k_tlv_attr_put_value(u32 *msg, u16 attr_id, s64 value, u32 len)
199ae17db0eSAlexander Duyck {
200ae17db0eSAlexander Duyck u32 *attr;
201ae17db0eSAlexander Duyck
202ae17db0eSAlexander Duyck /* verify non-null msg and len is 1, 2, 4, or 8 */
203ae17db0eSAlexander Duyck if (!msg || !len || len > 8 || (len & (len - 1)))
204ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
205ae17db0eSAlexander Duyck
206ae17db0eSAlexander Duyck attr = &msg[FM10K_TLV_DWORD_LEN(*msg)];
207ae17db0eSAlexander Duyck
208ae17db0eSAlexander Duyck if (len < 4) {
209fcdb0a99SBruce Allan attr[1] = (u32)value & (BIT(8 * len) - 1);
210ae17db0eSAlexander Duyck } else {
211ae17db0eSAlexander Duyck attr[1] = (u32)value;
212ae17db0eSAlexander Duyck if (len > 4)
213ae17db0eSAlexander Duyck attr[2] = (u32)(value >> 32);
214ae17db0eSAlexander Duyck }
215ae17db0eSAlexander Duyck
216ae17db0eSAlexander Duyck /* record attribute header, update message length */
217ae17db0eSAlexander Duyck len <<= FM10K_TLV_LEN_SHIFT;
218ae17db0eSAlexander Duyck attr[0] = len | attr_id;
219ae17db0eSAlexander Duyck
220ae17db0eSAlexander Duyck /* add header length to length */
221ae17db0eSAlexander Duyck len += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT;
222ae17db0eSAlexander Duyck *msg += FM10K_TLV_LEN_ALIGN(len);
223ae17db0eSAlexander Duyck
224ae17db0eSAlexander Duyck return 0;
225ae17db0eSAlexander Duyck }
226ae17db0eSAlexander Duyck
227ae17db0eSAlexander Duyck /**
228ae17db0eSAlexander Duyck * fm10k_tlv_attr_get_value - Get integer value stored in attribute
229ae17db0eSAlexander Duyck * @attr: Pointer to attribute
230ae17db0eSAlexander Duyck * @value: Pointer to destination buffer
231ae17db0eSAlexander Duyck * @len: Size of value
232ae17db0eSAlexander Duyck *
233ae17db0eSAlexander Duyck * This function will place an integer value of up to 8 bytes in size
234ae17db0eSAlexander Duyck * in the offset pointed to by value. The function will return success
235ae17db0eSAlexander Duyck * provided that pointers are valid and the len value matches the
236ae17db0eSAlexander Duyck * attribute length.
237ae17db0eSAlexander Duyck **/
fm10k_tlv_attr_get_value(u32 * attr,void * value,u32 len)238ae17db0eSAlexander Duyck s32 fm10k_tlv_attr_get_value(u32 *attr, void *value, u32 len)
239ae17db0eSAlexander Duyck {
240ae17db0eSAlexander Duyck /* verify pointers are not NULL */
241ae17db0eSAlexander Duyck if (!attr || !value)
242ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
243ae17db0eSAlexander Duyck
244ae17db0eSAlexander Duyck if ((*attr >> FM10K_TLV_LEN_SHIFT) != len)
245ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
246ae17db0eSAlexander Duyck
247ae17db0eSAlexander Duyck if (len == 8)
248ae17db0eSAlexander Duyck *(u64 *)value = ((u64)attr[2] << 32) | attr[1];
249ae17db0eSAlexander Duyck else if (len == 4)
250ae17db0eSAlexander Duyck *(u32 *)value = attr[1];
251ae17db0eSAlexander Duyck else if (len == 2)
252ae17db0eSAlexander Duyck *(u16 *)value = (u16)attr[1];
253ae17db0eSAlexander Duyck else
254ae17db0eSAlexander Duyck *(u8 *)value = (u8)attr[1];
255ae17db0eSAlexander Duyck
256ae17db0eSAlexander Duyck return 0;
257ae17db0eSAlexander Duyck }
258ae17db0eSAlexander Duyck
259ae17db0eSAlexander Duyck /**
260ae17db0eSAlexander Duyck * fm10k_tlv_attr_put_le_struct - Store little endian structure in message
261ae17db0eSAlexander Duyck * @msg: Pointer to message block
262ae17db0eSAlexander Duyck * @attr_id: Attribute ID
263ae17db0eSAlexander Duyck * @le_struct: Pointer to structure to be written
264ae17db0eSAlexander Duyck * @len: Size of le_struct
265ae17db0eSAlexander Duyck *
266ae17db0eSAlexander Duyck * This function will place a little endian structure value in a message
267ae17db0eSAlexander Duyck * attribute. The function will return success provided that all pointers
268ae17db0eSAlexander Duyck * are valid and length is a non-zero multiple of 4.
269ae17db0eSAlexander Duyck **/
fm10k_tlv_attr_put_le_struct(u32 * msg,u16 attr_id,const void * le_struct,u32 len)270ae17db0eSAlexander Duyck s32 fm10k_tlv_attr_put_le_struct(u32 *msg, u16 attr_id,
271ae17db0eSAlexander Duyck const void *le_struct, u32 len)
272ae17db0eSAlexander Duyck {
273ae17db0eSAlexander Duyck const __le32 *le32_ptr = (const __le32 *)le_struct;
274ae17db0eSAlexander Duyck u32 *attr;
275ae17db0eSAlexander Duyck u32 i;
276ae17db0eSAlexander Duyck
277ae17db0eSAlexander Duyck /* verify non-null msg and len is in 32 bit words */
278ae17db0eSAlexander Duyck if (!msg || !len || (len % 4))
279ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
280ae17db0eSAlexander Duyck
281ae17db0eSAlexander Duyck attr = &msg[FM10K_TLV_DWORD_LEN(*msg)];
282ae17db0eSAlexander Duyck
283ae17db0eSAlexander Duyck /* copy le32 structure into host byte order at 32b boundaries */
284ae17db0eSAlexander Duyck for (i = 0; i < (len / 4); i++)
285ae17db0eSAlexander Duyck attr[i + 1] = le32_to_cpu(le32_ptr[i]);
286ae17db0eSAlexander Duyck
287ae17db0eSAlexander Duyck /* record attribute header, update message length */
288ae17db0eSAlexander Duyck len <<= FM10K_TLV_LEN_SHIFT;
289ae17db0eSAlexander Duyck attr[0] = len | attr_id;
290ae17db0eSAlexander Duyck
291ae17db0eSAlexander Duyck /* add header length to length */
292ae17db0eSAlexander Duyck len += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT;
293ae17db0eSAlexander Duyck *msg += FM10K_TLV_LEN_ALIGN(len);
294ae17db0eSAlexander Duyck
295ae17db0eSAlexander Duyck return 0;
296ae17db0eSAlexander Duyck }
297ae17db0eSAlexander Duyck
298ae17db0eSAlexander Duyck /**
299ae17db0eSAlexander Duyck * fm10k_tlv_attr_get_le_struct - Get little endian struct form attribute
300ae17db0eSAlexander Duyck * @attr: Pointer to attribute
301ae17db0eSAlexander Duyck * @le_struct: Pointer to structure to be written
302ae17db0eSAlexander Duyck * @len: Size of structure
303ae17db0eSAlexander Duyck *
304ae17db0eSAlexander Duyck * This function will place a little endian structure in the buffer
305ae17db0eSAlexander Duyck * pointed to by le_struct. The function will return success
306ae17db0eSAlexander Duyck * provided that pointers are valid and the len value matches the
307ae17db0eSAlexander Duyck * attribute length.
308ae17db0eSAlexander Duyck **/
fm10k_tlv_attr_get_le_struct(u32 * attr,void * le_struct,u32 len)309ae17db0eSAlexander Duyck s32 fm10k_tlv_attr_get_le_struct(u32 *attr, void *le_struct, u32 len)
310ae17db0eSAlexander Duyck {
311ae17db0eSAlexander Duyck __le32 *le32_ptr = (__le32 *)le_struct;
312ae17db0eSAlexander Duyck u32 i;
313ae17db0eSAlexander Duyck
314ae17db0eSAlexander Duyck /* verify pointers are not NULL */
315ae17db0eSAlexander Duyck if (!le_struct || !attr)
316ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
317ae17db0eSAlexander Duyck
318ae17db0eSAlexander Duyck if ((*attr >> FM10K_TLV_LEN_SHIFT) != len)
319ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
320ae17db0eSAlexander Duyck
321ae17db0eSAlexander Duyck attr++;
322ae17db0eSAlexander Duyck
323ae17db0eSAlexander Duyck for (i = 0; len; i++, len -= 4)
324ae17db0eSAlexander Duyck le32_ptr[i] = cpu_to_le32(attr[i]);
325ae17db0eSAlexander Duyck
326ae17db0eSAlexander Duyck return 0;
327ae17db0eSAlexander Duyck }
328ae17db0eSAlexander Duyck
329ae17db0eSAlexander Duyck /**
330ae17db0eSAlexander Duyck * fm10k_tlv_attr_nest_start - Start a set of nested attributes
331ae17db0eSAlexander Duyck * @msg: Pointer to message block
332ae17db0eSAlexander Duyck * @attr_id: Attribute ID
333ae17db0eSAlexander Duyck *
334ae17db0eSAlexander Duyck * This function will mark off a new nested region for encapsulating
335ae17db0eSAlexander Duyck * a given set of attributes. The idea is if you wish to place a secondary
336ae17db0eSAlexander Duyck * structure within the message this mechanism allows for that. The
337ae17db0eSAlexander Duyck * function will return NULL on failure, and a pointer to the start
338ae17db0eSAlexander Duyck * of the nested attributes on success.
339ae17db0eSAlexander Duyck **/
fm10k_tlv_attr_nest_start(u32 * msg,u16 attr_id)340bb269e8bSBruce Allan static u32 *fm10k_tlv_attr_nest_start(u32 *msg, u16 attr_id)
341ae17db0eSAlexander Duyck {
342ae17db0eSAlexander Duyck u32 *attr;
343ae17db0eSAlexander Duyck
344ae17db0eSAlexander Duyck /* verify pointer is not NULL */
345ae17db0eSAlexander Duyck if (!msg)
346ae17db0eSAlexander Duyck return NULL;
347ae17db0eSAlexander Duyck
348ae17db0eSAlexander Duyck attr = &msg[FM10K_TLV_DWORD_LEN(*msg)];
349ae17db0eSAlexander Duyck
350ae17db0eSAlexander Duyck attr[0] = attr_id;
351ae17db0eSAlexander Duyck
352ae17db0eSAlexander Duyck /* return pointer to nest header */
353ae17db0eSAlexander Duyck return attr;
354ae17db0eSAlexander Duyck }
355ae17db0eSAlexander Duyck
356ae17db0eSAlexander Duyck /**
357bb269e8bSBruce Allan * fm10k_tlv_attr_nest_stop - Stop a set of nested attributes
358ae17db0eSAlexander Duyck * @msg: Pointer to message block
359ae17db0eSAlexander Duyck *
360ae17db0eSAlexander Duyck * This function closes off an existing set of nested attributes. The
361ae17db0eSAlexander Duyck * message pointer should be pointing to the parent of the nest. So in
362ae17db0eSAlexander Duyck * the case of a nest within the nest this would be the outer nest pointer.
363ae17db0eSAlexander Duyck * This function will return success provided all pointers are valid.
364ae17db0eSAlexander Duyck **/
fm10k_tlv_attr_nest_stop(u32 * msg)365bb269e8bSBruce Allan static s32 fm10k_tlv_attr_nest_stop(u32 *msg)
366ae17db0eSAlexander Duyck {
367ae17db0eSAlexander Duyck u32 *attr;
368ae17db0eSAlexander Duyck u32 len;
369ae17db0eSAlexander Duyck
370ae17db0eSAlexander Duyck /* verify pointer is not NULL */
371ae17db0eSAlexander Duyck if (!msg)
372ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
373ae17db0eSAlexander Duyck
374ae17db0eSAlexander Duyck /* locate the nested header and retrieve its length */
375ae17db0eSAlexander Duyck attr = &msg[FM10K_TLV_DWORD_LEN(*msg)];
376ae17db0eSAlexander Duyck len = (attr[0] >> FM10K_TLV_LEN_SHIFT) << FM10K_TLV_LEN_SHIFT;
377ae17db0eSAlexander Duyck
378ae17db0eSAlexander Duyck /* only include nest if data was added to it */
379ae17db0eSAlexander Duyck if (len) {
380ae17db0eSAlexander Duyck len += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT;
381ae17db0eSAlexander Duyck *msg += len;
382ae17db0eSAlexander Duyck }
383ae17db0eSAlexander Duyck
384ae17db0eSAlexander Duyck return 0;
385ae17db0eSAlexander Duyck }
386ae17db0eSAlexander Duyck
387ae17db0eSAlexander Duyck /**
388ae17db0eSAlexander Duyck * fm10k_tlv_attr_validate - Validate attribute metadata
389ae17db0eSAlexander Duyck * @attr: Pointer to attribute
390ae17db0eSAlexander Duyck * @tlv_attr: Type and length info for attribute
391ae17db0eSAlexander Duyck *
392ae17db0eSAlexander Duyck * This function does some basic validation of the input TLV. It
393ae17db0eSAlexander Duyck * verifies the length, and in the case of null terminated strings
394ae17db0eSAlexander Duyck * it verifies that the last byte is null. The function will
395ae17db0eSAlexander Duyck * return FM10K_ERR_PARAM if any attribute is malformed, otherwise
396ae17db0eSAlexander Duyck * it returns 0.
397ae17db0eSAlexander Duyck **/
fm10k_tlv_attr_validate(u32 * attr,const struct fm10k_tlv_attr * tlv_attr)398ae17db0eSAlexander Duyck static s32 fm10k_tlv_attr_validate(u32 *attr,
399ae17db0eSAlexander Duyck const struct fm10k_tlv_attr *tlv_attr)
400ae17db0eSAlexander Duyck {
401ae17db0eSAlexander Duyck u32 attr_id = *attr & FM10K_TLV_ID_MASK;
402ae17db0eSAlexander Duyck u16 len = *attr >> FM10K_TLV_LEN_SHIFT;
403ae17db0eSAlexander Duyck
404ae17db0eSAlexander Duyck /* verify this is an attribute and not a message */
405ae17db0eSAlexander Duyck if (*attr & (FM10K_TLV_FLAGS_MSG << FM10K_TLV_FLAGS_SHIFT))
406ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
407ae17db0eSAlexander Duyck
408ae17db0eSAlexander Duyck /* search through the list of attributes to find a matching ID */
409ae17db0eSAlexander Duyck while (tlv_attr->id < attr_id)
410ae17db0eSAlexander Duyck tlv_attr++;
411ae17db0eSAlexander Duyck
412ae17db0eSAlexander Duyck /* if didn't find a match then we should exit */
413ae17db0eSAlexander Duyck if (tlv_attr->id != attr_id)
414ae17db0eSAlexander Duyck return FM10K_NOT_IMPLEMENTED;
415ae17db0eSAlexander Duyck
416ae17db0eSAlexander Duyck /* move to start of attribute data */
417ae17db0eSAlexander Duyck attr++;
418ae17db0eSAlexander Duyck
419ae17db0eSAlexander Duyck switch (tlv_attr->type) {
420ae17db0eSAlexander Duyck case FM10K_TLV_NULL_STRING:
421ae17db0eSAlexander Duyck if (!len ||
422ae17db0eSAlexander Duyck (attr[(len - 1) / 4] & (0xFF << (8 * ((len - 1) % 4)))))
423ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
424ae17db0eSAlexander Duyck if (len > tlv_attr->len)
425ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
426ae17db0eSAlexander Duyck break;
427ae17db0eSAlexander Duyck case FM10K_TLV_MAC_ADDR:
428ae17db0eSAlexander Duyck if (len != ETH_ALEN)
429ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
430ae17db0eSAlexander Duyck break;
431ae17db0eSAlexander Duyck case FM10K_TLV_BOOL:
432ae17db0eSAlexander Duyck if (len)
433ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
434ae17db0eSAlexander Duyck break;
435ae17db0eSAlexander Duyck case FM10K_TLV_UNSIGNED:
436ae17db0eSAlexander Duyck case FM10K_TLV_SIGNED:
437ae17db0eSAlexander Duyck if (len != tlv_attr->len)
438ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
439ae17db0eSAlexander Duyck break;
440ae17db0eSAlexander Duyck case FM10K_TLV_LE_STRUCT:
441ae17db0eSAlexander Duyck /* struct must be 4 byte aligned */
442ae17db0eSAlexander Duyck if ((len % 4) || len != tlv_attr->len)
443ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
444ae17db0eSAlexander Duyck break;
445ae17db0eSAlexander Duyck case FM10K_TLV_NESTED:
446ae17db0eSAlexander Duyck /* nested attributes must be 4 byte aligned */
447ae17db0eSAlexander Duyck if (len % 4)
448ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
449ae17db0eSAlexander Duyck break;
450ae17db0eSAlexander Duyck default:
451ae17db0eSAlexander Duyck /* attribute id is mapped to bad value */
452ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
453ae17db0eSAlexander Duyck }
454ae17db0eSAlexander Duyck
455ae17db0eSAlexander Duyck return 0;
456ae17db0eSAlexander Duyck }
457ae17db0eSAlexander Duyck
458ae17db0eSAlexander Duyck /**
459ae17db0eSAlexander Duyck * fm10k_tlv_attr_parse - Parses stream of attribute data
460ae17db0eSAlexander Duyck * @attr: Pointer to attribute list
461ae17db0eSAlexander Duyck * @results: Pointer array to store pointers to attributes
462ae17db0eSAlexander Duyck * @tlv_attr: Type and length info for attributes
463ae17db0eSAlexander Duyck *
464ae17db0eSAlexander Duyck * This function validates a stream of attributes and parses them
465ae17db0eSAlexander Duyck * up into an array of pointers stored in results. The function will
466ae17db0eSAlexander Duyck * return FM10K_ERR_PARAM on any input or message error,
467ae17db0eSAlexander Duyck * FM10K_NOT_IMPLEMENTED for any attribute that is outside of the array
4684e160f2aSJacob Keller * and 0 on success. Any attributes not found in tlv_attr will be silently
4694e160f2aSJacob Keller * ignored.
470ae17db0eSAlexander Duyck **/
fm10k_tlv_attr_parse(u32 * attr,u32 ** results,const struct fm10k_tlv_attr * tlv_attr)471bb269e8bSBruce Allan static s32 fm10k_tlv_attr_parse(u32 *attr, u32 **results,
472ae17db0eSAlexander Duyck const struct fm10k_tlv_attr *tlv_attr)
473ae17db0eSAlexander Duyck {
474ae17db0eSAlexander Duyck u32 i, attr_id, offset = 0;
475d9ecd1f7SJacob Keller s32 err;
476ae17db0eSAlexander Duyck u16 len;
477ae17db0eSAlexander Duyck
478ae17db0eSAlexander Duyck /* verify pointers are not NULL */
479ae17db0eSAlexander Duyck if (!attr || !results)
480ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
481ae17db0eSAlexander Duyck
482ae17db0eSAlexander Duyck /* initialize results to NULL */
483ae17db0eSAlexander Duyck for (i = 0; i < FM10K_TLV_RESULTS_MAX; i++)
484ae17db0eSAlexander Duyck results[i] = NULL;
485ae17db0eSAlexander Duyck
486ae17db0eSAlexander Duyck /* pull length from the message header */
487ae17db0eSAlexander Duyck len = *attr >> FM10K_TLV_LEN_SHIFT;
488ae17db0eSAlexander Duyck
489ae17db0eSAlexander Duyck /* no attributes to parse if there is no length */
490ae17db0eSAlexander Duyck if (!len)
491ae17db0eSAlexander Duyck return 0;
492ae17db0eSAlexander Duyck
493ae17db0eSAlexander Duyck /* no attributes to parse, just raw data, message becomes attribute */
494ae17db0eSAlexander Duyck if (!tlv_attr) {
495ae17db0eSAlexander Duyck results[0] = attr;
496ae17db0eSAlexander Duyck return 0;
497ae17db0eSAlexander Duyck }
498ae17db0eSAlexander Duyck
499ae17db0eSAlexander Duyck /* move to start of attribute data */
500ae17db0eSAlexander Duyck attr++;
501ae17db0eSAlexander Duyck
502ae17db0eSAlexander Duyck /* run through list parsing all attributes */
503ae17db0eSAlexander Duyck while (offset < len) {
504ae17db0eSAlexander Duyck attr_id = *attr & FM10K_TLV_ID_MASK;
505ae17db0eSAlexander Duyck
5064e160f2aSJacob Keller if (attr_id >= FM10K_TLV_RESULTS_MAX)
5074e160f2aSJacob Keller return FM10K_NOT_IMPLEMENTED;
508ae17db0eSAlexander Duyck
5094e160f2aSJacob Keller err = fm10k_tlv_attr_validate(attr, tlv_attr);
5104e160f2aSJacob Keller if (err == FM10K_NOT_IMPLEMENTED)
5114e160f2aSJacob Keller ; /* silently ignore non-implemented attributes */
5124e160f2aSJacob Keller else if (err)
513ae17db0eSAlexander Duyck return err;
5144e160f2aSJacob Keller else
515ae17db0eSAlexander Duyck results[attr_id] = attr;
516ae17db0eSAlexander Duyck
517ae17db0eSAlexander Duyck /* update offset */
518ae17db0eSAlexander Duyck offset += FM10K_TLV_DWORD_LEN(*attr) * 4;
519ae17db0eSAlexander Duyck
520ae17db0eSAlexander Duyck /* move to next attribute */
521ae17db0eSAlexander Duyck attr = &attr[FM10K_TLV_DWORD_LEN(*attr)];
522ae17db0eSAlexander Duyck }
523ae17db0eSAlexander Duyck
524ae17db0eSAlexander Duyck /* we should find ourselves at the end of the list */
525ae17db0eSAlexander Duyck if (offset != len)
526ae17db0eSAlexander Duyck return FM10K_ERR_PARAM;
527ae17db0eSAlexander Duyck
528ae17db0eSAlexander Duyck return 0;
529ae17db0eSAlexander Duyck }
5306b1f201fSAlexander Duyck
5316b1f201fSAlexander Duyck /**
5326b1f201fSAlexander Duyck * fm10k_tlv_msg_parse - Parses message header and calls function handler
5336b1f201fSAlexander Duyck * @hw: Pointer to hardware structure
5346b1f201fSAlexander Duyck * @msg: Pointer to message
5356b1f201fSAlexander Duyck * @mbx: Pointer to mailbox information structure
536363656ebSJacob Keller * @data: Pointer to message handler data structure
5376b1f201fSAlexander Duyck *
5386b1f201fSAlexander Duyck * This function should be the first function called upon receiving a
5396b1f201fSAlexander Duyck * message. The handler will identify the message type and call the correct
5406b1f201fSAlexander Duyck * handler for the given message. It will return the value from the function
5416b1f201fSAlexander Duyck * call on a recognized message type, otherwise it will return
5426b1f201fSAlexander Duyck * FM10K_NOT_IMPLEMENTED on an unrecognized type.
5436b1f201fSAlexander Duyck **/
fm10k_tlv_msg_parse(struct fm10k_hw * hw,u32 * msg,struct fm10k_mbx_info * mbx,const struct fm10k_msg_data * data)5446b1f201fSAlexander Duyck s32 fm10k_tlv_msg_parse(struct fm10k_hw *hw, u32 *msg,
5456b1f201fSAlexander Duyck struct fm10k_mbx_info *mbx,
5466b1f201fSAlexander Duyck const struct fm10k_msg_data *data)
5476b1f201fSAlexander Duyck {
5486b1f201fSAlexander Duyck u32 *results[FM10K_TLV_RESULTS_MAX];
5496b1f201fSAlexander Duyck u32 msg_id;
5506b1f201fSAlexander Duyck s32 err;
5516b1f201fSAlexander Duyck
5526b1f201fSAlexander Duyck /* verify pointer is not NULL */
5536b1f201fSAlexander Duyck if (!msg || !data)
5546b1f201fSAlexander Duyck return FM10K_ERR_PARAM;
5556b1f201fSAlexander Duyck
5566b1f201fSAlexander Duyck /* verify this is a message and not an attribute */
5576b1f201fSAlexander Duyck if (!(*msg & (FM10K_TLV_FLAGS_MSG << FM10K_TLV_FLAGS_SHIFT)))
5586b1f201fSAlexander Duyck return FM10K_ERR_PARAM;
5596b1f201fSAlexander Duyck
5606b1f201fSAlexander Duyck /* grab message ID */
5616b1f201fSAlexander Duyck msg_id = *msg & FM10K_TLV_ID_MASK;
5626b1f201fSAlexander Duyck
5636b1f201fSAlexander Duyck while (data->id < msg_id)
5646b1f201fSAlexander Duyck data++;
5656b1f201fSAlexander Duyck
5666b1f201fSAlexander Duyck /* if we didn't find it then pass it up as an error */
5676b1f201fSAlexander Duyck if (data->id != msg_id) {
5686b1f201fSAlexander Duyck while (data->id != FM10K_TLV_ERROR)
5696b1f201fSAlexander Duyck data++;
5706b1f201fSAlexander Duyck }
5716b1f201fSAlexander Duyck
5726b1f201fSAlexander Duyck /* parse the attributes into the results list */
5736b1f201fSAlexander Duyck err = fm10k_tlv_attr_parse(msg, results, data->attr);
5746b1f201fSAlexander Duyck if (err < 0)
5756b1f201fSAlexander Duyck return err;
5766b1f201fSAlexander Duyck
5776b1f201fSAlexander Duyck return data->func(hw, results, mbx);
5786b1f201fSAlexander Duyck }
5796b1f201fSAlexander Duyck
5806b1f201fSAlexander Duyck /**
5816b1f201fSAlexander Duyck * fm10k_tlv_msg_error - Default handler for unrecognized TLV message IDs
5826b1f201fSAlexander Duyck * @hw: Pointer to hardware structure
5836b1f201fSAlexander Duyck * @results: Pointer array to message, results[0] is pointer to message
5846b1f201fSAlexander Duyck * @mbx: Unused mailbox pointer
5856b1f201fSAlexander Duyck *
5866b1f201fSAlexander Duyck * This function is a default handler for unrecognized messages. At a
587*17527829SJilin Yuan * minimum it just indicates that the message requested was
5886b1f201fSAlexander Duyck * unimplemented.
5896b1f201fSAlexander Duyck **/
fm10k_tlv_msg_error(struct fm10k_hw __always_unused * hw,u32 __always_unused ** results,struct fm10k_mbx_info __always_unused * mbx)590d5c2f395SJacob Keller s32 fm10k_tlv_msg_error(struct fm10k_hw __always_unused *hw,
591d5c2f395SJacob Keller u32 __always_unused **results,
592d5c2f395SJacob Keller struct fm10k_mbx_info __always_unused *mbx)
5936b1f201fSAlexander Duyck {
5946b1f201fSAlexander Duyck return FM10K_NOT_IMPLEMENTED;
5956b1f201fSAlexander Duyck }
5966b1f201fSAlexander Duyck
5976b1f201fSAlexander Duyck static const unsigned char test_str[] = "fm10k";
5986b1f201fSAlexander Duyck static const unsigned char test_mac[ETH_ALEN] = { 0x12, 0x34, 0x56,
5996b1f201fSAlexander Duyck 0x78, 0x9a, 0xbc };
6006b1f201fSAlexander Duyck static const u16 test_vlan = 0x0FED;
6016b1f201fSAlexander Duyck static const u64 test_u64 = 0xfedcba9876543210ull;
6026b1f201fSAlexander Duyck static const u32 test_u32 = 0x87654321;
6036b1f201fSAlexander Duyck static const u16 test_u16 = 0x8765;
6046b1f201fSAlexander Duyck static const u8 test_u8 = 0x87;
6056b1f201fSAlexander Duyck static const s64 test_s64 = -0x123456789abcdef0ll;
6066b1f201fSAlexander Duyck static const s32 test_s32 = -0x1235678;
6076b1f201fSAlexander Duyck static const s16 test_s16 = -0x1234;
6086b1f201fSAlexander Duyck static const s8 test_s8 = -0x12;
6096b1f201fSAlexander Duyck static const __le32 test_le[2] = { cpu_to_le32(0x12345678),
6106b1f201fSAlexander Duyck cpu_to_le32(0x9abcdef0)};
6116b1f201fSAlexander Duyck
6126b1f201fSAlexander Duyck /* The message below is meant to be used as a test message to demonstrate
6136b1f201fSAlexander Duyck * how to use the TLV interface and to test the types. Normally this code
6146b1f201fSAlexander Duyck * be compiled out by stripping the code wrapped in FM10K_TLV_TEST_MSG
6156b1f201fSAlexander Duyck */
6166b1f201fSAlexander Duyck const struct fm10k_tlv_attr fm10k_tlv_msg_test_attr[] = {
6176b1f201fSAlexander Duyck FM10K_TLV_ATTR_NULL_STRING(FM10K_TEST_MSG_STRING, 80),
6186b1f201fSAlexander Duyck FM10K_TLV_ATTR_MAC_ADDR(FM10K_TEST_MSG_MAC_ADDR),
6196b1f201fSAlexander Duyck FM10K_TLV_ATTR_U8(FM10K_TEST_MSG_U8),
6206b1f201fSAlexander Duyck FM10K_TLV_ATTR_U16(FM10K_TEST_MSG_U16),
6216b1f201fSAlexander Duyck FM10K_TLV_ATTR_U32(FM10K_TEST_MSG_U32),
6226b1f201fSAlexander Duyck FM10K_TLV_ATTR_U64(FM10K_TEST_MSG_U64),
6236b1f201fSAlexander Duyck FM10K_TLV_ATTR_S8(FM10K_TEST_MSG_S8),
6246b1f201fSAlexander Duyck FM10K_TLV_ATTR_S16(FM10K_TEST_MSG_S16),
6256b1f201fSAlexander Duyck FM10K_TLV_ATTR_S32(FM10K_TEST_MSG_S32),
6266b1f201fSAlexander Duyck FM10K_TLV_ATTR_S64(FM10K_TEST_MSG_S64),
6276b1f201fSAlexander Duyck FM10K_TLV_ATTR_LE_STRUCT(FM10K_TEST_MSG_LE_STRUCT, 8),
6286b1f201fSAlexander Duyck FM10K_TLV_ATTR_NESTED(FM10K_TEST_MSG_NESTED),
6296b1f201fSAlexander Duyck FM10K_TLV_ATTR_S32(FM10K_TEST_MSG_RESULT),
6306b1f201fSAlexander Duyck FM10K_TLV_ATTR_LAST
6316b1f201fSAlexander Duyck };
6326b1f201fSAlexander Duyck
6336b1f201fSAlexander Duyck /**
6346b1f201fSAlexander Duyck * fm10k_tlv_msg_test_generate_data - Stuff message with data
6356b1f201fSAlexander Duyck * @msg: Pointer to message
6366b1f201fSAlexander Duyck * @attr_flags: List of flags indicating what attributes to add
6376b1f201fSAlexander Duyck *
6386b1f201fSAlexander Duyck * This function is meant to load a message buffer with attribute data
6396b1f201fSAlexander Duyck **/
fm10k_tlv_msg_test_generate_data(u32 * msg,u32 attr_flags)6406b1f201fSAlexander Duyck static void fm10k_tlv_msg_test_generate_data(u32 *msg, u32 attr_flags)
6416b1f201fSAlexander Duyck {
642fcdb0a99SBruce Allan if (attr_flags & BIT(FM10K_TEST_MSG_STRING))
6436b1f201fSAlexander Duyck fm10k_tlv_attr_put_null_string(msg, FM10K_TEST_MSG_STRING,
6446b1f201fSAlexander Duyck test_str);
645fcdb0a99SBruce Allan if (attr_flags & BIT(FM10K_TEST_MSG_MAC_ADDR))
6466b1f201fSAlexander Duyck fm10k_tlv_attr_put_mac_vlan(msg, FM10K_TEST_MSG_MAC_ADDR,
6476b1f201fSAlexander Duyck test_mac, test_vlan);
648fcdb0a99SBruce Allan if (attr_flags & BIT(FM10K_TEST_MSG_U8))
6496b1f201fSAlexander Duyck fm10k_tlv_attr_put_u8(msg, FM10K_TEST_MSG_U8, test_u8);
650fcdb0a99SBruce Allan if (attr_flags & BIT(FM10K_TEST_MSG_U16))
6516b1f201fSAlexander Duyck fm10k_tlv_attr_put_u16(msg, FM10K_TEST_MSG_U16, test_u16);
652fcdb0a99SBruce Allan if (attr_flags & BIT(FM10K_TEST_MSG_U32))
6536b1f201fSAlexander Duyck fm10k_tlv_attr_put_u32(msg, FM10K_TEST_MSG_U32, test_u32);
654fcdb0a99SBruce Allan if (attr_flags & BIT(FM10K_TEST_MSG_U64))
6556b1f201fSAlexander Duyck fm10k_tlv_attr_put_u64(msg, FM10K_TEST_MSG_U64, test_u64);
656fcdb0a99SBruce Allan if (attr_flags & BIT(FM10K_TEST_MSG_S8))
6576b1f201fSAlexander Duyck fm10k_tlv_attr_put_s8(msg, FM10K_TEST_MSG_S8, test_s8);
658fcdb0a99SBruce Allan if (attr_flags & BIT(FM10K_TEST_MSG_S16))
6596b1f201fSAlexander Duyck fm10k_tlv_attr_put_s16(msg, FM10K_TEST_MSG_S16, test_s16);
660fcdb0a99SBruce Allan if (attr_flags & BIT(FM10K_TEST_MSG_S32))
6616b1f201fSAlexander Duyck fm10k_tlv_attr_put_s32(msg, FM10K_TEST_MSG_S32, test_s32);
662fcdb0a99SBruce Allan if (attr_flags & BIT(FM10K_TEST_MSG_S64))
6636b1f201fSAlexander Duyck fm10k_tlv_attr_put_s64(msg, FM10K_TEST_MSG_S64, test_s64);
664fcdb0a99SBruce Allan if (attr_flags & BIT(FM10K_TEST_MSG_LE_STRUCT))
6656b1f201fSAlexander Duyck fm10k_tlv_attr_put_le_struct(msg, FM10K_TEST_MSG_LE_STRUCT,
6666b1f201fSAlexander Duyck test_le, 8);
6676b1f201fSAlexander Duyck }
6686b1f201fSAlexander Duyck
6696b1f201fSAlexander Duyck /**
6706b1f201fSAlexander Duyck * fm10k_tlv_msg_test_create - Create a test message testing all attributes
6716b1f201fSAlexander Duyck * @msg: Pointer to message
6726b1f201fSAlexander Duyck * @attr_flags: List of flags indicating what attributes to add
6736b1f201fSAlexander Duyck *
6746b1f201fSAlexander Duyck * This function is meant to load a message buffer with all attribute types
6756b1f201fSAlexander Duyck * including a nested attribute.
6766b1f201fSAlexander Duyck **/
fm10k_tlv_msg_test_create(u32 * msg,u32 attr_flags)6776b1f201fSAlexander Duyck void fm10k_tlv_msg_test_create(u32 *msg, u32 attr_flags)
6786b1f201fSAlexander Duyck {
6796b1f201fSAlexander Duyck u32 *nest = NULL;
6806b1f201fSAlexander Duyck
6816b1f201fSAlexander Duyck fm10k_tlv_msg_init(msg, FM10K_TLV_MSG_ID_TEST);
6826b1f201fSAlexander Duyck
6836b1f201fSAlexander Duyck fm10k_tlv_msg_test_generate_data(msg, attr_flags);
6846b1f201fSAlexander Duyck
6856b1f201fSAlexander Duyck /* check for nested attributes */
6866b1f201fSAlexander Duyck attr_flags >>= FM10K_TEST_MSG_NESTED;
6876b1f201fSAlexander Duyck
6886b1f201fSAlexander Duyck if (attr_flags) {
6896b1f201fSAlexander Duyck nest = fm10k_tlv_attr_nest_start(msg, FM10K_TEST_MSG_NESTED);
6906b1f201fSAlexander Duyck
6916b1f201fSAlexander Duyck fm10k_tlv_msg_test_generate_data(nest, attr_flags);
6926b1f201fSAlexander Duyck
6936b1f201fSAlexander Duyck fm10k_tlv_attr_nest_stop(msg);
6946b1f201fSAlexander Duyck }
6956b1f201fSAlexander Duyck }
6966b1f201fSAlexander Duyck
6976b1f201fSAlexander Duyck /**
6986b1f201fSAlexander Duyck * fm10k_tlv_msg_test - Validate all results on test message receive
6996b1f201fSAlexander Duyck * @hw: Pointer to hardware structure
700eca32047SMatthew Vick * @results: Pointer array to attributes in the message
7016b1f201fSAlexander Duyck * @mbx: Pointer to mailbox information structure
7026b1f201fSAlexander Duyck *
7036b1f201fSAlexander Duyck * This function does a check to verify all attributes match what the test
7046b1f201fSAlexander Duyck * message placed in the message buffer. It is the default handler
7056b1f201fSAlexander Duyck * for TLV test messages.
7066b1f201fSAlexander Duyck **/
fm10k_tlv_msg_test(struct fm10k_hw * hw,u32 ** results,struct fm10k_mbx_info * mbx)7076b1f201fSAlexander Duyck s32 fm10k_tlv_msg_test(struct fm10k_hw *hw, u32 **results,
7086b1f201fSAlexander Duyck struct fm10k_mbx_info *mbx)
7096b1f201fSAlexander Duyck {
7106b1f201fSAlexander Duyck u32 *nest_results[FM10K_TLV_RESULTS_MAX];
7116b1f201fSAlexander Duyck unsigned char result_str[80];
7126b1f201fSAlexander Duyck unsigned char result_mac[ETH_ALEN];
7136b1f201fSAlexander Duyck s32 err = 0;
7146b1f201fSAlexander Duyck __le32 result_le[2];
7156b1f201fSAlexander Duyck u16 result_vlan;
7166b1f201fSAlexander Duyck u64 result_u64;
7176b1f201fSAlexander Duyck u32 result_u32;
7186b1f201fSAlexander Duyck u16 result_u16;
7196b1f201fSAlexander Duyck u8 result_u8;
7206b1f201fSAlexander Duyck s64 result_s64;
7216b1f201fSAlexander Duyck s32 result_s32;
7226b1f201fSAlexander Duyck s16 result_s16;
7236b1f201fSAlexander Duyck s8 result_s8;
7246b1f201fSAlexander Duyck u32 reply[3];
7256b1f201fSAlexander Duyck
7266b1f201fSAlexander Duyck /* retrieve results of a previous test */
7276b1f201fSAlexander Duyck if (!!results[FM10K_TEST_MSG_RESULT])
7286b1f201fSAlexander Duyck return fm10k_tlv_attr_get_s32(results[FM10K_TEST_MSG_RESULT],
7296b1f201fSAlexander Duyck &mbx->test_result);
7306b1f201fSAlexander Duyck
7316b1f201fSAlexander Duyck parse_nested:
7326b1f201fSAlexander Duyck if (!!results[FM10K_TEST_MSG_STRING]) {
7336b1f201fSAlexander Duyck err = fm10k_tlv_attr_get_null_string(
7346b1f201fSAlexander Duyck results[FM10K_TEST_MSG_STRING],
7356b1f201fSAlexander Duyck result_str);
7366b1f201fSAlexander Duyck if (!err && memcmp(test_str, result_str, sizeof(test_str)))
7376b1f201fSAlexander Duyck err = FM10K_ERR_INVALID_VALUE;
7386b1f201fSAlexander Duyck if (err)
7396b1f201fSAlexander Duyck goto report_result;
7406b1f201fSAlexander Duyck }
7416b1f201fSAlexander Duyck if (!!results[FM10K_TEST_MSG_MAC_ADDR]) {
7426b1f201fSAlexander Duyck err = fm10k_tlv_attr_get_mac_vlan(
7436b1f201fSAlexander Duyck results[FM10K_TEST_MSG_MAC_ADDR],
7446b1f201fSAlexander Duyck result_mac, &result_vlan);
7456186ddf0SJacob Keller if (!err && !ether_addr_equal(test_mac, result_mac))
7466b1f201fSAlexander Duyck err = FM10K_ERR_INVALID_VALUE;
7476b1f201fSAlexander Duyck if (!err && test_vlan != result_vlan)
7486b1f201fSAlexander Duyck err = FM10K_ERR_INVALID_VALUE;
7496b1f201fSAlexander Duyck if (err)
7506b1f201fSAlexander Duyck goto report_result;
7516b1f201fSAlexander Duyck }
7526b1f201fSAlexander Duyck if (!!results[FM10K_TEST_MSG_U8]) {
7536b1f201fSAlexander Duyck err = fm10k_tlv_attr_get_u8(results[FM10K_TEST_MSG_U8],
7546b1f201fSAlexander Duyck &result_u8);
7556b1f201fSAlexander Duyck if (!err && test_u8 != result_u8)
7566b1f201fSAlexander Duyck err = FM10K_ERR_INVALID_VALUE;
7576b1f201fSAlexander Duyck if (err)
7586b1f201fSAlexander Duyck goto report_result;
7596b1f201fSAlexander Duyck }
7606b1f201fSAlexander Duyck if (!!results[FM10K_TEST_MSG_U16]) {
7616b1f201fSAlexander Duyck err = fm10k_tlv_attr_get_u16(results[FM10K_TEST_MSG_U16],
7626b1f201fSAlexander Duyck &result_u16);
7636b1f201fSAlexander Duyck if (!err && test_u16 != result_u16)
7646b1f201fSAlexander Duyck err = FM10K_ERR_INVALID_VALUE;
7656b1f201fSAlexander Duyck if (err)
7666b1f201fSAlexander Duyck goto report_result;
7676b1f201fSAlexander Duyck }
7686b1f201fSAlexander Duyck if (!!results[FM10K_TEST_MSG_U32]) {
7696b1f201fSAlexander Duyck err = fm10k_tlv_attr_get_u32(results[FM10K_TEST_MSG_U32],
7706b1f201fSAlexander Duyck &result_u32);
7716b1f201fSAlexander Duyck if (!err && test_u32 != result_u32)
7726b1f201fSAlexander Duyck err = FM10K_ERR_INVALID_VALUE;
7736b1f201fSAlexander Duyck if (err)
7746b1f201fSAlexander Duyck goto report_result;
7756b1f201fSAlexander Duyck }
7766b1f201fSAlexander Duyck if (!!results[FM10K_TEST_MSG_U64]) {
7776b1f201fSAlexander Duyck err = fm10k_tlv_attr_get_u64(results[FM10K_TEST_MSG_U64],
7786b1f201fSAlexander Duyck &result_u64);
7796b1f201fSAlexander Duyck if (!err && test_u64 != result_u64)
7806b1f201fSAlexander Duyck err = FM10K_ERR_INVALID_VALUE;
7816b1f201fSAlexander Duyck if (err)
7826b1f201fSAlexander Duyck goto report_result;
7836b1f201fSAlexander Duyck }
7846b1f201fSAlexander Duyck if (!!results[FM10K_TEST_MSG_S8]) {
7856b1f201fSAlexander Duyck err = fm10k_tlv_attr_get_s8(results[FM10K_TEST_MSG_S8],
7866b1f201fSAlexander Duyck &result_s8);
7876b1f201fSAlexander Duyck if (!err && test_s8 != result_s8)
7886b1f201fSAlexander Duyck err = FM10K_ERR_INVALID_VALUE;
7896b1f201fSAlexander Duyck if (err)
7906b1f201fSAlexander Duyck goto report_result;
7916b1f201fSAlexander Duyck }
7926b1f201fSAlexander Duyck if (!!results[FM10K_TEST_MSG_S16]) {
7936b1f201fSAlexander Duyck err = fm10k_tlv_attr_get_s16(results[FM10K_TEST_MSG_S16],
7946b1f201fSAlexander Duyck &result_s16);
7956b1f201fSAlexander Duyck if (!err && test_s16 != result_s16)
7966b1f201fSAlexander Duyck err = FM10K_ERR_INVALID_VALUE;
7976b1f201fSAlexander Duyck if (err)
7986b1f201fSAlexander Duyck goto report_result;
7996b1f201fSAlexander Duyck }
8006b1f201fSAlexander Duyck if (!!results[FM10K_TEST_MSG_S32]) {
8016b1f201fSAlexander Duyck err = fm10k_tlv_attr_get_s32(results[FM10K_TEST_MSG_S32],
8026b1f201fSAlexander Duyck &result_s32);
8036b1f201fSAlexander Duyck if (!err && test_s32 != result_s32)
8046b1f201fSAlexander Duyck err = FM10K_ERR_INVALID_VALUE;
8056b1f201fSAlexander Duyck if (err)
8066b1f201fSAlexander Duyck goto report_result;
8076b1f201fSAlexander Duyck }
8086b1f201fSAlexander Duyck if (!!results[FM10K_TEST_MSG_S64]) {
8096b1f201fSAlexander Duyck err = fm10k_tlv_attr_get_s64(results[FM10K_TEST_MSG_S64],
8106b1f201fSAlexander Duyck &result_s64);
8116b1f201fSAlexander Duyck if (!err && test_s64 != result_s64)
8126b1f201fSAlexander Duyck err = FM10K_ERR_INVALID_VALUE;
8136b1f201fSAlexander Duyck if (err)
8146b1f201fSAlexander Duyck goto report_result;
8156b1f201fSAlexander Duyck }
8166b1f201fSAlexander Duyck if (!!results[FM10K_TEST_MSG_LE_STRUCT]) {
8176b1f201fSAlexander Duyck err = fm10k_tlv_attr_get_le_struct(
8186b1f201fSAlexander Duyck results[FM10K_TEST_MSG_LE_STRUCT],
8196b1f201fSAlexander Duyck result_le,
8206b1f201fSAlexander Duyck sizeof(result_le));
8216b1f201fSAlexander Duyck if (!err && memcmp(test_le, result_le, sizeof(test_le)))
8226b1f201fSAlexander Duyck err = FM10K_ERR_INVALID_VALUE;
8236b1f201fSAlexander Duyck if (err)
8246b1f201fSAlexander Duyck goto report_result;
8256b1f201fSAlexander Duyck }
8266b1f201fSAlexander Duyck
8276b1f201fSAlexander Duyck if (!!results[FM10K_TEST_MSG_NESTED]) {
8286b1f201fSAlexander Duyck /* clear any pointers */
8296b1f201fSAlexander Duyck memset(nest_results, 0, sizeof(nest_results));
8306b1f201fSAlexander Duyck
8316b1f201fSAlexander Duyck /* parse the nested attributes into the nest results list */
8326b1f201fSAlexander Duyck err = fm10k_tlv_attr_parse(results[FM10K_TEST_MSG_NESTED],
8336b1f201fSAlexander Duyck nest_results,
8346b1f201fSAlexander Duyck fm10k_tlv_msg_test_attr);
8356b1f201fSAlexander Duyck if (err)
8366b1f201fSAlexander Duyck goto report_result;
8376b1f201fSAlexander Duyck
8386b1f201fSAlexander Duyck /* loop back through to the start */
8396b1f201fSAlexander Duyck results = nest_results;
8406b1f201fSAlexander Duyck goto parse_nested;
8416b1f201fSAlexander Duyck }
8426b1f201fSAlexander Duyck
8436b1f201fSAlexander Duyck report_result:
8446b1f201fSAlexander Duyck /* generate reply with test result */
8456b1f201fSAlexander Duyck fm10k_tlv_msg_init(reply, FM10K_TLV_MSG_ID_TEST);
8466b1f201fSAlexander Duyck fm10k_tlv_attr_put_s32(reply, FM10K_TEST_MSG_RESULT, err);
8476b1f201fSAlexander Duyck
8486b1f201fSAlexander Duyck /* load onto outgoing mailbox */
8496b1f201fSAlexander Duyck return mbx->ops.enqueue_tx(hw, mbx, reply);
8506b1f201fSAlexander Duyck }
851