1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright(c) 2013 - 2019 Intel Corporation. */ |
3 | |
4 | #include "fm10k_tlv.h" |
5 | |
6 | /** |
7 | * fm10k_tlv_msg_init - Initialize message block for TLV data storage |
8 | * @msg: Pointer to message block |
9 | * @msg_id: Message ID indicating message type |
10 | * |
11 | * This function return success if provided with a valid message pointer |
12 | **/ |
13 | s32 fm10k_tlv_msg_init(u32 *msg, u16 msg_id) |
14 | { |
15 | /* verify pointer is not NULL */ |
16 | if (!msg) |
17 | return FM10K_ERR_PARAM; |
18 | |
19 | *msg = (FM10K_TLV_FLAGS_MSG << FM10K_TLV_FLAGS_SHIFT) | msg_id; |
20 | |
21 | return 0; |
22 | } |
23 | |
24 | /** |
25 | * fm10k_tlv_attr_put_null_string - Place null terminated string on message |
26 | * @msg: Pointer to message block |
27 | * @attr_id: Attribute ID |
28 | * @string: Pointer to string to be stored in attribute |
29 | * |
30 | * This function will reorder a string to be CPU endian and store it in |
31 | * the attribute buffer. It will return success if provided with a valid |
32 | * pointers. |
33 | **/ |
34 | static s32 fm10k_tlv_attr_put_null_string(u32 *msg, u16 attr_id, |
35 | const unsigned char *string) |
36 | { |
37 | u32 attr_data = 0, len = 0; |
38 | u32 *attr; |
39 | |
40 | /* verify pointers are not NULL */ |
41 | if (!string || !msg) |
42 | return FM10K_ERR_PARAM; |
43 | |
44 | attr = &msg[FM10K_TLV_DWORD_LEN(*msg)]; |
45 | |
46 | /* copy string into local variable and then write to msg */ |
47 | do { |
48 | /* write data to message */ |
49 | if (len && !(len % 4)) { |
50 | attr[len / 4] = attr_data; |
51 | attr_data = 0; |
52 | } |
53 | |
54 | /* record character to offset location */ |
55 | attr_data |= (u32)(*string) << (8 * (len % 4)); |
56 | len++; |
57 | |
58 | /* test for NULL and then increment */ |
59 | } while (*(string++)); |
60 | |
61 | /* write last piece of data to message */ |
62 | attr[(len + 3) / 4] = attr_data; |
63 | |
64 | /* record attribute header, update message length */ |
65 | len <<= FM10K_TLV_LEN_SHIFT; |
66 | attr[0] = len | attr_id; |
67 | |
68 | /* add header length to length */ |
69 | len += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT; |
70 | *msg += FM10K_TLV_LEN_ALIGN(len); |
71 | |
72 | return 0; |
73 | } |
74 | |
75 | /** |
76 | * fm10k_tlv_attr_get_null_string - Get null terminated string from attribute |
77 | * @attr: Pointer to attribute |
78 | * @string: Pointer to location of destination string |
79 | * |
80 | * This function pulls the string back out of the attribute and will place |
81 | * it in the array pointed by string. It will return success if provided |
82 | * with a valid pointers. |
83 | **/ |
84 | static s32 fm10k_tlv_attr_get_null_string(u32 *attr, unsigned char *string) |
85 | { |
86 | u32 len; |
87 | |
88 | /* verify pointers are not NULL */ |
89 | if (!string || !attr) |
90 | return FM10K_ERR_PARAM; |
91 | |
92 | len = *attr >> FM10K_TLV_LEN_SHIFT; |
93 | attr++; |
94 | |
95 | while (len--) |
96 | string[len] = (u8)(attr[len / 4] >> (8 * (len % 4))); |
97 | |
98 | return 0; |
99 | } |
100 | |
101 | /** |
102 | * fm10k_tlv_attr_put_mac_vlan - Store MAC/VLAN attribute in message |
103 | * @msg: Pointer to message block |
104 | * @attr_id: Attribute ID |
105 | * @mac_addr: MAC address to be stored |
106 | * @vlan: VLAN to be stored |
107 | * |
108 | * This function will reorder a MAC address to be CPU endian and store it |
109 | * in the attribute buffer. It will return success if provided with a |
110 | * valid pointers. |
111 | **/ |
112 | s32 fm10k_tlv_attr_put_mac_vlan(u32 *msg, u16 attr_id, |
113 | const u8 *mac_addr, u16 vlan) |
114 | { |
115 | u32 len = ETH_ALEN << FM10K_TLV_LEN_SHIFT; |
116 | u32 *attr; |
117 | |
118 | /* verify pointers are not NULL */ |
119 | if (!msg || !mac_addr) |
120 | return FM10K_ERR_PARAM; |
121 | |
122 | attr = &msg[FM10K_TLV_DWORD_LEN(*msg)]; |
123 | |
124 | /* record attribute header, update message length */ |
125 | attr[0] = len | attr_id; |
126 | |
127 | /* copy value into local variable and then write to msg */ |
128 | attr[1] = le32_to_cpu(*(const __le32 *)&mac_addr[0]); |
129 | attr[2] = le16_to_cpu(*(const __le16 *)&mac_addr[4]); |
130 | attr[2] |= (u32)vlan << 16; |
131 | |
132 | /* add header length to length */ |
133 | len += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT; |
134 | *msg += FM10K_TLV_LEN_ALIGN(len); |
135 | |
136 | return 0; |
137 | } |
138 | |
139 | /** |
140 | * fm10k_tlv_attr_get_mac_vlan - Get MAC/VLAN stored in attribute |
141 | * @attr: Pointer to attribute |
142 | * @mac_addr: location of buffer to store MAC address |
143 | * @vlan: location of buffer to store VLAN |
144 | * |
145 | * This function pulls the MAC address back out of the attribute and will |
146 | * place it in the array pointed by mac_addr. It will return success |
147 | * if provided with a valid pointers. |
148 | **/ |
149 | s32 fm10k_tlv_attr_get_mac_vlan(u32 *attr, u8 *mac_addr, u16 *vlan) |
150 | { |
151 | /* verify pointers are not NULL */ |
152 | if (!mac_addr || !attr) |
153 | return FM10K_ERR_PARAM; |
154 | |
155 | *(__le32 *)&mac_addr[0] = cpu_to_le32(attr[1]); |
156 | *(__le16 *)&mac_addr[4] = cpu_to_le16((u16)(attr[2])); |
157 | *vlan = (u16)(attr[2] >> 16); |
158 | |
159 | return 0; |
160 | } |
161 | |
162 | /** |
163 | * fm10k_tlv_attr_put_bool - Add header indicating value "true" |
164 | * @msg: Pointer to message block |
165 | * @attr_id: Attribute ID |
166 | * |
167 | * This function will simply add an attribute header, the fact |
168 | * that the header is here means the attribute value is true, else |
169 | * it is false. The function will return success if provided with a |
170 | * valid pointers. |
171 | **/ |
172 | s32 fm10k_tlv_attr_put_bool(u32 *msg, u16 attr_id) |
173 | { |
174 | /* verify pointers are not NULL */ |
175 | if (!msg) |
176 | return FM10K_ERR_PARAM; |
177 | |
178 | /* record attribute header */ |
179 | msg[FM10K_TLV_DWORD_LEN(*msg)] = attr_id; |
180 | |
181 | /* add header length to length */ |
182 | *msg += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT; |
183 | |
184 | return 0; |
185 | } |
186 | |
187 | /** |
188 | * fm10k_tlv_attr_put_value - Store integer value attribute in message |
189 | * @msg: Pointer to message block |
190 | * @attr_id: Attribute ID |
191 | * @value: Value to be written |
192 | * @len: Size of value |
193 | * |
194 | * This function will place an integer value of up to 8 bytes in size |
195 | * in a message attribute. The function will return success provided |
196 | * that msg is a valid pointer, and len is 1, 2, 4, or 8. |
197 | **/ |
198 | s32 fm10k_tlv_attr_put_value(u32 *msg, u16 attr_id, s64 value, u32 len) |
199 | { |
200 | u32 *attr; |
201 | |
202 | /* verify non-null msg and len is 1, 2, 4, or 8 */ |
203 | if (!msg || !len || len > 8 || (len & (len - 1))) |
204 | return FM10K_ERR_PARAM; |
205 | |
206 | attr = &msg[FM10K_TLV_DWORD_LEN(*msg)]; |
207 | |
208 | if (len < 4) { |
209 | attr[1] = (u32)value & (BIT(8 * len) - 1); |
210 | } else { |
211 | attr[1] = (u32)value; |
212 | if (len > 4) |
213 | attr[2] = (u32)(value >> 32); |
214 | } |
215 | |
216 | /* record attribute header, update message length */ |
217 | len <<= FM10K_TLV_LEN_SHIFT; |
218 | attr[0] = len | attr_id; |
219 | |
220 | /* add header length to length */ |
221 | len += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT; |
222 | *msg += FM10K_TLV_LEN_ALIGN(len); |
223 | |
224 | return 0; |
225 | } |
226 | |
227 | /** |
228 | * fm10k_tlv_attr_get_value - Get integer value stored in attribute |
229 | * @attr: Pointer to attribute |
230 | * @value: Pointer to destination buffer |
231 | * @len: Size of value |
232 | * |
233 | * This function will place an integer value of up to 8 bytes in size |
234 | * in the offset pointed to by value. The function will return success |
235 | * provided that pointers are valid and the len value matches the |
236 | * attribute length. |
237 | **/ |
238 | s32 fm10k_tlv_attr_get_value(u32 *attr, void *value, u32 len) |
239 | { |
240 | /* verify pointers are not NULL */ |
241 | if (!attr || !value) |
242 | return FM10K_ERR_PARAM; |
243 | |
244 | if ((*attr >> FM10K_TLV_LEN_SHIFT) != len) |
245 | return FM10K_ERR_PARAM; |
246 | |
247 | if (len == 8) |
248 | *(u64 *)value = ((u64)attr[2] << 32) | attr[1]; |
249 | else if (len == 4) |
250 | *(u32 *)value = attr[1]; |
251 | else if (len == 2) |
252 | *(u16 *)value = (u16)attr[1]; |
253 | else |
254 | *(u8 *)value = (u8)attr[1]; |
255 | |
256 | return 0; |
257 | } |
258 | |
259 | /** |
260 | * fm10k_tlv_attr_put_le_struct - Store little endian structure in message |
261 | * @msg: Pointer to message block |
262 | * @attr_id: Attribute ID |
263 | * @le_struct: Pointer to structure to be written |
264 | * @len: Size of le_struct |
265 | * |
266 | * This function will place a little endian structure value in a message |
267 | * attribute. The function will return success provided that all pointers |
268 | * are valid and length is a non-zero multiple of 4. |
269 | **/ |
270 | s32 fm10k_tlv_attr_put_le_struct(u32 *msg, u16 attr_id, |
271 | const void *le_struct, u32 len) |
272 | { |
273 | const __le32 *le32_ptr = (const __le32 *)le_struct; |
274 | u32 *attr; |
275 | u32 i; |
276 | |
277 | /* verify non-null msg and len is in 32 bit words */ |
278 | if (!msg || !len || (len % 4)) |
279 | return FM10K_ERR_PARAM; |
280 | |
281 | attr = &msg[FM10K_TLV_DWORD_LEN(*msg)]; |
282 | |
283 | /* copy le32 structure into host byte order at 32b boundaries */ |
284 | for (i = 0; i < (len / 4); i++) |
285 | attr[i + 1] = le32_to_cpu(le32_ptr[i]); |
286 | |
287 | /* record attribute header, update message length */ |
288 | len <<= FM10K_TLV_LEN_SHIFT; |
289 | attr[0] = len | attr_id; |
290 | |
291 | /* add header length to length */ |
292 | len += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT; |
293 | *msg += FM10K_TLV_LEN_ALIGN(len); |
294 | |
295 | return 0; |
296 | } |
297 | |
298 | /** |
299 | * fm10k_tlv_attr_get_le_struct - Get little endian struct form attribute |
300 | * @attr: Pointer to attribute |
301 | * @le_struct: Pointer to structure to be written |
302 | * @len: Size of structure |
303 | * |
304 | * This function will place a little endian structure in the buffer |
305 | * pointed to by le_struct. The function will return success |
306 | * provided that pointers are valid and the len value matches the |
307 | * attribute length. |
308 | **/ |
309 | s32 fm10k_tlv_attr_get_le_struct(u32 *attr, void *le_struct, u32 len) |
310 | { |
311 | __le32 *le32_ptr = (__le32 *)le_struct; |
312 | u32 i; |
313 | |
314 | /* verify pointers are not NULL */ |
315 | if (!le_struct || !attr) |
316 | return FM10K_ERR_PARAM; |
317 | |
318 | if ((*attr >> FM10K_TLV_LEN_SHIFT) != len) |
319 | return FM10K_ERR_PARAM; |
320 | |
321 | attr++; |
322 | |
323 | for (i = 0; len; i++, len -= 4) |
324 | le32_ptr[i] = cpu_to_le32(attr[i]); |
325 | |
326 | return 0; |
327 | } |
328 | |
329 | /** |
330 | * fm10k_tlv_attr_nest_start - Start a set of nested attributes |
331 | * @msg: Pointer to message block |
332 | * @attr_id: Attribute ID |
333 | * |
334 | * This function will mark off a new nested region for encapsulating |
335 | * a given set of attributes. The idea is if you wish to place a secondary |
336 | * structure within the message this mechanism allows for that. The |
337 | * function will return NULL on failure, and a pointer to the start |
338 | * of the nested attributes on success. |
339 | **/ |
340 | static u32 *fm10k_tlv_attr_nest_start(u32 *msg, u16 attr_id) |
341 | { |
342 | u32 *attr; |
343 | |
344 | /* verify pointer is not NULL */ |
345 | if (!msg) |
346 | return NULL; |
347 | |
348 | attr = &msg[FM10K_TLV_DWORD_LEN(*msg)]; |
349 | |
350 | attr[0] = attr_id; |
351 | |
352 | /* return pointer to nest header */ |
353 | return attr; |
354 | } |
355 | |
356 | /** |
357 | * fm10k_tlv_attr_nest_stop - Stop a set of nested attributes |
358 | * @msg: Pointer to message block |
359 | * |
360 | * This function closes off an existing set of nested attributes. The |
361 | * message pointer should be pointing to the parent of the nest. So in |
362 | * the case of a nest within the nest this would be the outer nest pointer. |
363 | * This function will return success provided all pointers are valid. |
364 | **/ |
365 | static s32 fm10k_tlv_attr_nest_stop(u32 *msg) |
366 | { |
367 | u32 *attr; |
368 | u32 len; |
369 | |
370 | /* verify pointer is not NULL */ |
371 | if (!msg) |
372 | return FM10K_ERR_PARAM; |
373 | |
374 | /* locate the nested header and retrieve its length */ |
375 | attr = &msg[FM10K_TLV_DWORD_LEN(*msg)]; |
376 | len = (attr[0] >> FM10K_TLV_LEN_SHIFT) << FM10K_TLV_LEN_SHIFT; |
377 | |
378 | /* only include nest if data was added to it */ |
379 | if (len) { |
380 | len += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT; |
381 | *msg += len; |
382 | } |
383 | |
384 | return 0; |
385 | } |
386 | |
387 | /** |
388 | * fm10k_tlv_attr_validate - Validate attribute metadata |
389 | * @attr: Pointer to attribute |
390 | * @tlv_attr: Type and length info for attribute |
391 | * |
392 | * This function does some basic validation of the input TLV. It |
393 | * verifies the length, and in the case of null terminated strings |
394 | * it verifies that the last byte is null. The function will |
395 | * return FM10K_ERR_PARAM if any attribute is malformed, otherwise |
396 | * it returns 0. |
397 | **/ |
398 | static s32 fm10k_tlv_attr_validate(u32 *attr, |
399 | const struct fm10k_tlv_attr *tlv_attr) |
400 | { |
401 | u32 attr_id = *attr & FM10K_TLV_ID_MASK; |
402 | u16 len = *attr >> FM10K_TLV_LEN_SHIFT; |
403 | |
404 | /* verify this is an attribute and not a message */ |
405 | if (*attr & (FM10K_TLV_FLAGS_MSG << FM10K_TLV_FLAGS_SHIFT)) |
406 | return FM10K_ERR_PARAM; |
407 | |
408 | /* search through the list of attributes to find a matching ID */ |
409 | while (tlv_attr->id < attr_id) |
410 | tlv_attr++; |
411 | |
412 | /* if didn't find a match then we should exit */ |
413 | if (tlv_attr->id != attr_id) |
414 | return FM10K_NOT_IMPLEMENTED; |
415 | |
416 | /* move to start of attribute data */ |
417 | attr++; |
418 | |
419 | switch (tlv_attr->type) { |
420 | case FM10K_TLV_NULL_STRING: |
421 | if (!len || |
422 | (attr[(len - 1) / 4] & (0xFF << (8 * ((len - 1) % 4))))) |
423 | return FM10K_ERR_PARAM; |
424 | if (len > tlv_attr->len) |
425 | return FM10K_ERR_PARAM; |
426 | break; |
427 | case FM10K_TLV_MAC_ADDR: |
428 | if (len != ETH_ALEN) |
429 | return FM10K_ERR_PARAM; |
430 | break; |
431 | case FM10K_TLV_BOOL: |
432 | if (len) |
433 | return FM10K_ERR_PARAM; |
434 | break; |
435 | case FM10K_TLV_UNSIGNED: |
436 | case FM10K_TLV_SIGNED: |
437 | if (len != tlv_attr->len) |
438 | return FM10K_ERR_PARAM; |
439 | break; |
440 | case FM10K_TLV_LE_STRUCT: |
441 | /* struct must be 4 byte aligned */ |
442 | if ((len % 4) || len != tlv_attr->len) |
443 | return FM10K_ERR_PARAM; |
444 | break; |
445 | case FM10K_TLV_NESTED: |
446 | /* nested attributes must be 4 byte aligned */ |
447 | if (len % 4) |
448 | return FM10K_ERR_PARAM; |
449 | break; |
450 | default: |
451 | /* attribute id is mapped to bad value */ |
452 | return FM10K_ERR_PARAM; |
453 | } |
454 | |
455 | return 0; |
456 | } |
457 | |
458 | /** |
459 | * fm10k_tlv_attr_parse - Parses stream of attribute data |
460 | * @attr: Pointer to attribute list |
461 | * @results: Pointer array to store pointers to attributes |
462 | * @tlv_attr: Type and length info for attributes |
463 | * |
464 | * This function validates a stream of attributes and parses them |
465 | * up into an array of pointers stored in results. The function will |
466 | * return FM10K_ERR_PARAM on any input or message error, |
467 | * FM10K_NOT_IMPLEMENTED for any attribute that is outside of the array |
468 | * and 0 on success. Any attributes not found in tlv_attr will be silently |
469 | * ignored. |
470 | **/ |
471 | static s32 fm10k_tlv_attr_parse(u32 *attr, u32 **results, |
472 | const struct fm10k_tlv_attr *tlv_attr) |
473 | { |
474 | u32 i, attr_id, offset = 0; |
475 | s32 err; |
476 | u16 len; |
477 | |
478 | /* verify pointers are not NULL */ |
479 | if (!attr || !results) |
480 | return FM10K_ERR_PARAM; |
481 | |
482 | /* initialize results to NULL */ |
483 | for (i = 0; i < FM10K_TLV_RESULTS_MAX; i++) |
484 | results[i] = NULL; |
485 | |
486 | /* pull length from the message header */ |
487 | len = *attr >> FM10K_TLV_LEN_SHIFT; |
488 | |
489 | /* no attributes to parse if there is no length */ |
490 | if (!len) |
491 | return 0; |
492 | |
493 | /* no attributes to parse, just raw data, message becomes attribute */ |
494 | if (!tlv_attr) { |
495 | results[0] = attr; |
496 | return 0; |
497 | } |
498 | |
499 | /* move to start of attribute data */ |
500 | attr++; |
501 | |
502 | /* run through list parsing all attributes */ |
503 | while (offset < len) { |
504 | attr_id = *attr & FM10K_TLV_ID_MASK; |
505 | |
506 | if (attr_id >= FM10K_TLV_RESULTS_MAX) |
507 | return FM10K_NOT_IMPLEMENTED; |
508 | |
509 | err = fm10k_tlv_attr_validate(attr, tlv_attr); |
510 | if (err == FM10K_NOT_IMPLEMENTED) |
511 | ; /* silently ignore non-implemented attributes */ |
512 | else if (err) |
513 | return err; |
514 | else |
515 | results[attr_id] = attr; |
516 | |
517 | /* update offset */ |
518 | offset += FM10K_TLV_DWORD_LEN(*attr) * 4; |
519 | |
520 | /* move to next attribute */ |
521 | attr = &attr[FM10K_TLV_DWORD_LEN(*attr)]; |
522 | } |
523 | |
524 | /* we should find ourselves at the end of the list */ |
525 | if (offset != len) |
526 | return FM10K_ERR_PARAM; |
527 | |
528 | return 0; |
529 | } |
530 | |
531 | /** |
532 | * fm10k_tlv_msg_parse - Parses message header and calls function handler |
533 | * @hw: Pointer to hardware structure |
534 | * @msg: Pointer to message |
535 | * @mbx: Pointer to mailbox information structure |
536 | * @data: Pointer to message handler data structure |
537 | * |
538 | * This function should be the first function called upon receiving a |
539 | * message. The handler will identify the message type and call the correct |
540 | * handler for the given message. It will return the value from the function |
541 | * call on a recognized message type, otherwise it will return |
542 | * FM10K_NOT_IMPLEMENTED on an unrecognized type. |
543 | **/ |
544 | s32 fm10k_tlv_msg_parse(struct fm10k_hw *hw, u32 *msg, |
545 | struct fm10k_mbx_info *mbx, |
546 | const struct fm10k_msg_data *data) |
547 | { |
548 | u32 *results[FM10K_TLV_RESULTS_MAX]; |
549 | u32 msg_id; |
550 | s32 err; |
551 | |
552 | /* verify pointer is not NULL */ |
553 | if (!msg || !data) |
554 | return FM10K_ERR_PARAM; |
555 | |
556 | /* verify this is a message and not an attribute */ |
557 | if (!(*msg & (FM10K_TLV_FLAGS_MSG << FM10K_TLV_FLAGS_SHIFT))) |
558 | return FM10K_ERR_PARAM; |
559 | |
560 | /* grab message ID */ |
561 | msg_id = *msg & FM10K_TLV_ID_MASK; |
562 | |
563 | while (data->id < msg_id) |
564 | data++; |
565 | |
566 | /* if we didn't find it then pass it up as an error */ |
567 | if (data->id != msg_id) { |
568 | while (data->id != FM10K_TLV_ERROR) |
569 | data++; |
570 | } |
571 | |
572 | /* parse the attributes into the results list */ |
573 | err = fm10k_tlv_attr_parse(attr: msg, results, tlv_attr: data->attr); |
574 | if (err < 0) |
575 | return err; |
576 | |
577 | return data->func(hw, results, mbx); |
578 | } |
579 | |
580 | /** |
581 | * fm10k_tlv_msg_error - Default handler for unrecognized TLV message IDs |
582 | * @hw: Pointer to hardware structure |
583 | * @results: Pointer array to message, results[0] is pointer to message |
584 | * @mbx: Unused mailbox pointer |
585 | * |
586 | * This function is a default handler for unrecognized messages. At a |
587 | * minimum it just indicates that the message requested was |
588 | * unimplemented. |
589 | **/ |
590 | s32 fm10k_tlv_msg_error(struct fm10k_hw __always_unused *hw, |
591 | u32 __always_unused **results, |
592 | struct fm10k_mbx_info __always_unused *mbx) |
593 | { |
594 | return FM10K_NOT_IMPLEMENTED; |
595 | } |
596 | |
597 | static const unsigned char test_str[] = "fm10k" ; |
598 | static const unsigned char test_mac[ETH_ALEN] = { 0x12, 0x34, 0x56, |
599 | 0x78, 0x9a, 0xbc }; |
600 | static const u16 test_vlan = 0x0FED; |
601 | static const u64 test_u64 = 0xfedcba9876543210ull; |
602 | static const u32 test_u32 = 0x87654321; |
603 | static const u16 test_u16 = 0x8765; |
604 | static const u8 test_u8 = 0x87; |
605 | static const s64 test_s64 = -0x123456789abcdef0ll; |
606 | static const s32 test_s32 = -0x1235678; |
607 | static const s16 test_s16 = -0x1234; |
608 | static const s8 test_s8 = -0x12; |
609 | static const __le32 test_le[2] = { cpu_to_le32(0x12345678), |
610 | cpu_to_le32(0x9abcdef0)}; |
611 | |
612 | /* The message below is meant to be used as a test message to demonstrate |
613 | * how to use the TLV interface and to test the types. Normally this code |
614 | * be compiled out by stripping the code wrapped in FM10K_TLV_TEST_MSG |
615 | */ |
616 | const struct fm10k_tlv_attr fm10k_tlv_msg_test_attr[] = { |
617 | FM10K_TLV_ATTR_NULL_STRING(FM10K_TEST_MSG_STRING, 80), |
618 | FM10K_TLV_ATTR_MAC_ADDR(FM10K_TEST_MSG_MAC_ADDR), |
619 | FM10K_TLV_ATTR_U8(FM10K_TEST_MSG_U8), |
620 | FM10K_TLV_ATTR_U16(FM10K_TEST_MSG_U16), |
621 | FM10K_TLV_ATTR_U32(FM10K_TEST_MSG_U32), |
622 | FM10K_TLV_ATTR_U64(FM10K_TEST_MSG_U64), |
623 | FM10K_TLV_ATTR_S8(FM10K_TEST_MSG_S8), |
624 | FM10K_TLV_ATTR_S16(FM10K_TEST_MSG_S16), |
625 | FM10K_TLV_ATTR_S32(FM10K_TEST_MSG_S32), |
626 | FM10K_TLV_ATTR_S64(FM10K_TEST_MSG_S64), |
627 | FM10K_TLV_ATTR_LE_STRUCT(FM10K_TEST_MSG_LE_STRUCT, 8), |
628 | FM10K_TLV_ATTR_NESTED(FM10K_TEST_MSG_NESTED), |
629 | FM10K_TLV_ATTR_S32(FM10K_TEST_MSG_RESULT), |
630 | FM10K_TLV_ATTR_LAST |
631 | }; |
632 | |
633 | /** |
634 | * fm10k_tlv_msg_test_generate_data - Stuff message with data |
635 | * @msg: Pointer to message |
636 | * @attr_flags: List of flags indicating what attributes to add |
637 | * |
638 | * This function is meant to load a message buffer with attribute data |
639 | **/ |
640 | static void fm10k_tlv_msg_test_generate_data(u32 *msg, u32 attr_flags) |
641 | { |
642 | if (attr_flags & BIT(FM10K_TEST_MSG_STRING)) |
643 | fm10k_tlv_attr_put_null_string(msg, attr_id: FM10K_TEST_MSG_STRING, |
644 | string: test_str); |
645 | if (attr_flags & BIT(FM10K_TEST_MSG_MAC_ADDR)) |
646 | fm10k_tlv_attr_put_mac_vlan(msg, attr_id: FM10K_TEST_MSG_MAC_ADDR, |
647 | mac_addr: test_mac, vlan: test_vlan); |
648 | if (attr_flags & BIT(FM10K_TEST_MSG_U8)) |
649 | fm10k_tlv_attr_put_u8(msg, FM10K_TEST_MSG_U8, test_u8); |
650 | if (attr_flags & BIT(FM10K_TEST_MSG_U16)) |
651 | fm10k_tlv_attr_put_u16(msg, FM10K_TEST_MSG_U16, test_u16); |
652 | if (attr_flags & BIT(FM10K_TEST_MSG_U32)) |
653 | fm10k_tlv_attr_put_u32(msg, FM10K_TEST_MSG_U32, test_u32); |
654 | if (attr_flags & BIT(FM10K_TEST_MSG_U64)) |
655 | fm10k_tlv_attr_put_u64(msg, FM10K_TEST_MSG_U64, test_u64); |
656 | if (attr_flags & BIT(FM10K_TEST_MSG_S8)) |
657 | fm10k_tlv_attr_put_s8(msg, FM10K_TEST_MSG_S8, test_s8); |
658 | if (attr_flags & BIT(FM10K_TEST_MSG_S16)) |
659 | fm10k_tlv_attr_put_s16(msg, FM10K_TEST_MSG_S16, test_s16); |
660 | if (attr_flags & BIT(FM10K_TEST_MSG_S32)) |
661 | fm10k_tlv_attr_put_s32(msg, FM10K_TEST_MSG_S32, test_s32); |
662 | if (attr_flags & BIT(FM10K_TEST_MSG_S64)) |
663 | fm10k_tlv_attr_put_s64(msg, FM10K_TEST_MSG_S64, test_s64); |
664 | if (attr_flags & BIT(FM10K_TEST_MSG_LE_STRUCT)) |
665 | fm10k_tlv_attr_put_le_struct(msg, attr_id: FM10K_TEST_MSG_LE_STRUCT, |
666 | le_struct: test_le, len: 8); |
667 | } |
668 | |
669 | /** |
670 | * fm10k_tlv_msg_test_create - Create a test message testing all attributes |
671 | * @msg: Pointer to message |
672 | * @attr_flags: List of flags indicating what attributes to add |
673 | * |
674 | * This function is meant to load a message buffer with all attribute types |
675 | * including a nested attribute. |
676 | **/ |
677 | void fm10k_tlv_msg_test_create(u32 *msg, u32 attr_flags) |
678 | { |
679 | u32 *nest = NULL; |
680 | |
681 | fm10k_tlv_msg_init(msg, FM10K_TLV_MSG_ID_TEST); |
682 | |
683 | fm10k_tlv_msg_test_generate_data(msg, attr_flags); |
684 | |
685 | /* check for nested attributes */ |
686 | attr_flags >>= FM10K_TEST_MSG_NESTED; |
687 | |
688 | if (attr_flags) { |
689 | nest = fm10k_tlv_attr_nest_start(msg, attr_id: FM10K_TEST_MSG_NESTED); |
690 | |
691 | fm10k_tlv_msg_test_generate_data(msg: nest, attr_flags); |
692 | |
693 | fm10k_tlv_attr_nest_stop(msg); |
694 | } |
695 | } |
696 | |
697 | /** |
698 | * fm10k_tlv_msg_test - Validate all results on test message receive |
699 | * @hw: Pointer to hardware structure |
700 | * @results: Pointer array to attributes in the message |
701 | * @mbx: Pointer to mailbox information structure |
702 | * |
703 | * This function does a check to verify all attributes match what the test |
704 | * message placed in the message buffer. It is the default handler |
705 | * for TLV test messages. |
706 | **/ |
707 | s32 fm10k_tlv_msg_test(struct fm10k_hw *hw, u32 **results, |
708 | struct fm10k_mbx_info *mbx) |
709 | { |
710 | u32 *nest_results[FM10K_TLV_RESULTS_MAX]; |
711 | unsigned char result_str[80]; |
712 | unsigned char result_mac[ETH_ALEN]; |
713 | s32 err = 0; |
714 | __le32 result_le[2]; |
715 | u16 result_vlan; |
716 | u64 result_u64; |
717 | u32 result_u32; |
718 | u16 result_u16; |
719 | u8 result_u8; |
720 | s64 result_s64; |
721 | s32 result_s32; |
722 | s16 result_s16; |
723 | s8 result_s8; |
724 | u32 reply[3]; |
725 | |
726 | /* retrieve results of a previous test */ |
727 | if (!!results[FM10K_TEST_MSG_RESULT]) |
728 | return fm10k_tlv_attr_get_s32(results[FM10K_TEST_MSG_RESULT], |
729 | &mbx->test_result); |
730 | |
731 | parse_nested: |
732 | if (!!results[FM10K_TEST_MSG_STRING]) { |
733 | err = fm10k_tlv_attr_get_null_string( |
734 | attr: results[FM10K_TEST_MSG_STRING], |
735 | string: result_str); |
736 | if (!err && memcmp(p: test_str, q: result_str, size: sizeof(test_str))) |
737 | err = FM10K_ERR_INVALID_VALUE; |
738 | if (err) |
739 | goto report_result; |
740 | } |
741 | if (!!results[FM10K_TEST_MSG_MAC_ADDR]) { |
742 | err = fm10k_tlv_attr_get_mac_vlan( |
743 | attr: results[FM10K_TEST_MSG_MAC_ADDR], |
744 | mac_addr: result_mac, vlan: &result_vlan); |
745 | if (!err && !ether_addr_equal(addr1: test_mac, addr2: result_mac)) |
746 | err = FM10K_ERR_INVALID_VALUE; |
747 | if (!err && test_vlan != result_vlan) |
748 | err = FM10K_ERR_INVALID_VALUE; |
749 | if (err) |
750 | goto report_result; |
751 | } |
752 | if (!!results[FM10K_TEST_MSG_U8]) { |
753 | err = fm10k_tlv_attr_get_u8(results[FM10K_TEST_MSG_U8], |
754 | &result_u8); |
755 | if (!err && test_u8 != result_u8) |
756 | err = FM10K_ERR_INVALID_VALUE; |
757 | if (err) |
758 | goto report_result; |
759 | } |
760 | if (!!results[FM10K_TEST_MSG_U16]) { |
761 | err = fm10k_tlv_attr_get_u16(results[FM10K_TEST_MSG_U16], |
762 | &result_u16); |
763 | if (!err && test_u16 != result_u16) |
764 | err = FM10K_ERR_INVALID_VALUE; |
765 | if (err) |
766 | goto report_result; |
767 | } |
768 | if (!!results[FM10K_TEST_MSG_U32]) { |
769 | err = fm10k_tlv_attr_get_u32(results[FM10K_TEST_MSG_U32], |
770 | &result_u32); |
771 | if (!err && test_u32 != result_u32) |
772 | err = FM10K_ERR_INVALID_VALUE; |
773 | if (err) |
774 | goto report_result; |
775 | } |
776 | if (!!results[FM10K_TEST_MSG_U64]) { |
777 | err = fm10k_tlv_attr_get_u64(results[FM10K_TEST_MSG_U64], |
778 | &result_u64); |
779 | if (!err && test_u64 != result_u64) |
780 | err = FM10K_ERR_INVALID_VALUE; |
781 | if (err) |
782 | goto report_result; |
783 | } |
784 | if (!!results[FM10K_TEST_MSG_S8]) { |
785 | err = fm10k_tlv_attr_get_s8(results[FM10K_TEST_MSG_S8], |
786 | &result_s8); |
787 | if (!err && test_s8 != result_s8) |
788 | err = FM10K_ERR_INVALID_VALUE; |
789 | if (err) |
790 | goto report_result; |
791 | } |
792 | if (!!results[FM10K_TEST_MSG_S16]) { |
793 | err = fm10k_tlv_attr_get_s16(results[FM10K_TEST_MSG_S16], |
794 | &result_s16); |
795 | if (!err && test_s16 != result_s16) |
796 | err = FM10K_ERR_INVALID_VALUE; |
797 | if (err) |
798 | goto report_result; |
799 | } |
800 | if (!!results[FM10K_TEST_MSG_S32]) { |
801 | err = fm10k_tlv_attr_get_s32(results[FM10K_TEST_MSG_S32], |
802 | &result_s32); |
803 | if (!err && test_s32 != result_s32) |
804 | err = FM10K_ERR_INVALID_VALUE; |
805 | if (err) |
806 | goto report_result; |
807 | } |
808 | if (!!results[FM10K_TEST_MSG_S64]) { |
809 | err = fm10k_tlv_attr_get_s64(results[FM10K_TEST_MSG_S64], |
810 | &result_s64); |
811 | if (!err && test_s64 != result_s64) |
812 | err = FM10K_ERR_INVALID_VALUE; |
813 | if (err) |
814 | goto report_result; |
815 | } |
816 | if (!!results[FM10K_TEST_MSG_LE_STRUCT]) { |
817 | err = fm10k_tlv_attr_get_le_struct( |
818 | attr: results[FM10K_TEST_MSG_LE_STRUCT], |
819 | le_struct: result_le, |
820 | len: sizeof(result_le)); |
821 | if (!err && memcmp(p: test_le, q: result_le, size: sizeof(test_le))) |
822 | err = FM10K_ERR_INVALID_VALUE; |
823 | if (err) |
824 | goto report_result; |
825 | } |
826 | |
827 | if (!!results[FM10K_TEST_MSG_NESTED]) { |
828 | /* clear any pointers */ |
829 | memset(nest_results, 0, sizeof(nest_results)); |
830 | |
831 | /* parse the nested attributes into the nest results list */ |
832 | err = fm10k_tlv_attr_parse(attr: results[FM10K_TEST_MSG_NESTED], |
833 | results: nest_results, |
834 | tlv_attr: fm10k_tlv_msg_test_attr); |
835 | if (err) |
836 | goto report_result; |
837 | |
838 | /* loop back through to the start */ |
839 | results = nest_results; |
840 | goto parse_nested; |
841 | } |
842 | |
843 | report_result: |
844 | /* generate reply with test result */ |
845 | fm10k_tlv_msg_init(msg: reply, FM10K_TLV_MSG_ID_TEST); |
846 | fm10k_tlv_attr_put_s32(reply, FM10K_TEST_MSG_RESULT, err); |
847 | |
848 | /* load onto outgoing mailbox */ |
849 | return mbx->ops.enqueue_tx(hw, mbx, reply); |
850 | } |
851 | |