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