linux/drivers/net/ethernet/intel/fm10k/fm10k_tlv.c
<<
>>
Prefs
   1/* Intel(R) Ethernet Switch Host Interface Driver
   2 * Copyright(c) 2013 - 2016 Intel Corporation.
   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 **/
  30s32 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 **/
  51static s32 fm10k_tlv_attr_put_null_string(u32 *msg, u16 attr_id,
  52                                          const unsigned char *string)
  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 **/
 101static s32 fm10k_tlv_attr_get_null_string(u32 *attr, unsigned char *string)
 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 **/
 128s32 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 **/
 165s32 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 **/
 188s32 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 **/
 214s32 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) {
 225                attr[1] = (u32)value & (BIT(8 * len) - 1);
 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 **/
 254s32 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 **/
 286s32 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 **/
 325s32 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 **/
 356static u32 *fm10k_tlv_attr_nest_start(u32 *msg, u16 attr_id)
 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/**
 373 *  fm10k_tlv_attr_nest_stop - Stop a set of nested attributes
 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 **/
 381static s32 fm10k_tlv_attr_nest_stop(u32 *msg)
 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 **/
 414static 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
 484 *  and 0 on success. Any attributes not found in tlv_attr will be silently
 485 *  ignored.
 486 **/
 487static s32 fm10k_tlv_attr_parse(u32 *attr, u32 **results,
 488                                const struct fm10k_tlv_attr *tlv_attr)
 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
 522                if (attr_id >= FM10K_TLV_RESULTS_MAX)
 523                        return FM10K_NOT_IMPLEMENTED;
 524
 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)
 529                        return err;
 530                else
 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}
 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 **/
 560s32 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 **/
 606s32 fm10k_tlv_msg_error(struct fm10k_hw *hw, u32 **results,
 607                        struct fm10k_mbx_info *mbx)
 608{
 609        return FM10K_NOT_IMPLEMENTED;
 610}
 611
 612static const unsigned char test_str[] = "fm10k";
 613static const unsigned char test_mac[ETH_ALEN] = { 0x12, 0x34, 0x56,
 614                                                  0x78, 0x9a, 0xbc };
 615static const u16 test_vlan = 0x0FED;
 616static const u64 test_u64 = 0xfedcba9876543210ull;
 617static const u32 test_u32 = 0x87654321;
 618static const u16 test_u16 = 0x8765;
 619static const u8  test_u8  = 0x87;
 620static const s64 test_s64 = -0x123456789abcdef0ll;
 621static const s32 test_s32 = -0x1235678;
 622static const s16 test_s16 = -0x1234;
 623static const s8  test_s8  = -0x12;
 624static 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 */
 631const 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 **/
 655static void fm10k_tlv_msg_test_generate_data(u32 *msg, u32 attr_flags)
 656{
 657        if (attr_flags & BIT(FM10K_TEST_MSG_STRING))
 658                fm10k_tlv_attr_put_null_string(msg, FM10K_TEST_MSG_STRING,
 659                                               test_str);
 660        if (attr_flags & BIT(FM10K_TEST_MSG_MAC_ADDR))
 661                fm10k_tlv_attr_put_mac_vlan(msg, FM10K_TEST_MSG_MAC_ADDR,
 662                                            test_mac, test_vlan);
 663        if (attr_flags & BIT(FM10K_TEST_MSG_U8))
 664                fm10k_tlv_attr_put_u8(msg, FM10K_TEST_MSG_U8,  test_u8);
 665        if (attr_flags & BIT(FM10K_TEST_MSG_U16))
 666                fm10k_tlv_attr_put_u16(msg, FM10K_TEST_MSG_U16, test_u16);
 667        if (attr_flags & BIT(FM10K_TEST_MSG_U32))
 668                fm10k_tlv_attr_put_u32(msg, FM10K_TEST_MSG_U32, test_u32);
 669        if (attr_flags & BIT(FM10K_TEST_MSG_U64))
 670                fm10k_tlv_attr_put_u64(msg, FM10K_TEST_MSG_U64, test_u64);
 671        if (attr_flags & BIT(FM10K_TEST_MSG_S8))
 672                fm10k_tlv_attr_put_s8(msg, FM10K_TEST_MSG_S8,  test_s8);
 673        if (attr_flags & BIT(FM10K_TEST_MSG_S16))
 674                fm10k_tlv_attr_put_s16(msg, FM10K_TEST_MSG_S16, test_s16);
 675        if (attr_flags & BIT(FM10K_TEST_MSG_S32))
 676                fm10k_tlv_attr_put_s32(msg, FM10K_TEST_MSG_S32, test_s32);
 677        if (attr_flags & BIT(FM10K_TEST_MSG_S64))
 678                fm10k_tlv_attr_put_s64(msg, FM10K_TEST_MSG_S64, test_s64);
 679        if (attr_flags & BIT(FM10K_TEST_MSG_LE_STRUCT))
 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 **/
 692void 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
 715 *  @results: Pointer array to attributes in the message
 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 **/
 722s32 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
 746parse_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);
 760                if (!err && !ether_addr_equal(test_mac, result_mac))
 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
 858report_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}
 866