summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/meta/fbnic/fbnic_tlv.h
blob: 67300ab44353212677491cee6a02d43af34829e0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) Meta Platforms, Inc. and affiliates. */

#ifndef _FBNIC_TLV_H_
#define _FBNIC_TLV_H_

#include <asm/byteorder.h>
#include <linux/bits.h>
#include <linux/const.h>
#include <linux/types.h>

#define FBNIC_TLV_MSG_ALIGN(len)	ALIGN(len, sizeof(u32))
#define FBNIC_TLV_MSG_SIZE(len)		\
		(FBNIC_TLV_MSG_ALIGN(len) / sizeof(u32))

/* TLV Header Format
 *    3			  2		      1
 *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |		Length		   |M|I|RSV|	   Type / ID	   |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 * The TLV header format described above will be used for transferring
 * messages between the host and the firmware. To ensure byte ordering
 * we have defined all fields as being little endian.
 * Type/ID: Identifier for message and/or attribute
 * RSV: Reserved field for future use, likely as additional flags
 * I: cannot_ignore flag, identifies if unrecognized attribute can be ignored
 * M: is_msg, indicates that this is the start of a new message
 * Length: Total length of message in dwords including header
 *		or
 *	   Total length of attribute in bytes including header
 */
struct fbnic_tlv_hdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	u16 type		: 12; /* 0 .. 11  Type / ID */
	u16 rsvd		: 2;  /* 12 .. 13 Reserved for future use */
	u16 cannot_ignore	: 1;  /* 14	  Attribute can be ignored */
	u16 is_msg		: 1;  /* 15	  Header belongs to message */
#elif defined(__BIG_ENDIAN_BITFIELD)
	u16 is_msg		: 1;  /* 15	  Header belongs to message */
	u16 cannot_ignore	: 1;  /* 14	  Attribute can be ignored */
	u16 rsvd		: 2;  /* 13 .. 12 Reserved for future use */
	u16 type		: 12; /* 11 .. 0  Type / ID */
#else
#error "Missing defines from byteorder.h"
#endif
	__le16 len;		/* 16 .. 32	length including TLV header */
};

#define FBNIC_TLV_RESULTS_MAX		32

struct fbnic_tlv_msg {
	struct fbnic_tlv_hdr	hdr;
	__le32			value[];
};

#define FBNIC_TLV_MSG_ID_UNKNOWN		USHRT_MAX

enum fbnic_tlv_type {
	FBNIC_TLV_STRING,
	FBNIC_TLV_FLAG,
	FBNIC_TLV_UNSIGNED,
	FBNIC_TLV_SIGNED,
	FBNIC_TLV_BINARY,
	FBNIC_TLV_NESTED,
	FBNIC_TLV_ARRAY,
	__FBNIC_TLV_MAX_TYPE
};

/* TLV Index
 * Defines the relationship between the attribute IDs and their types.
 * For each entry in the index there will be a size and type associated
 * with it so that we can use this to parse the data and verify it matches
 * the expected layout.
 */
struct fbnic_tlv_index {
	u16			id;
	u16			len;
	enum fbnic_tlv_type	type;
};

#define TLV_MAX_DATA			(PAGE_SIZE - 512)
#define FBNIC_TLV_ATTR_ID_UNKNOWN	USHRT_MAX
#define FBNIC_TLV_ATTR_STRING(id, len)	{ id, len, FBNIC_TLV_STRING }
#define FBNIC_TLV_ATTR_FLAG(id)		{ id, 0, FBNIC_TLV_FLAG }
#define FBNIC_TLV_ATTR_U32(id)		{ id, sizeof(u32), FBNIC_TLV_UNSIGNED }
#define FBNIC_TLV_ATTR_U64(id)		{ id, sizeof(u64), FBNIC_TLV_UNSIGNED }
#define FBNIC_TLV_ATTR_S32(id)		{ id, sizeof(s32), FBNIC_TLV_SIGNED }
#define FBNIC_TLV_ATTR_S64(id)		{ id, sizeof(s64), FBNIC_TLV_SIGNED }
#define FBNIC_TLV_ATTR_MAC_ADDR(id)	{ id, ETH_ALEN, FBNIC_TLV_BINARY }
#define FBNIC_TLV_ATTR_NESTED(id)	{ id, 0, FBNIC_TLV_NESTED }
#define FBNIC_TLV_ATTR_ARRAY(id)	{ id, 0, FBNIC_TLV_ARRAY }
#define FBNIC_TLV_ATTR_RAW_DATA(id)	{ id, TLV_MAX_DATA, FBNIC_TLV_BINARY }
#define FBNIC_TLV_ATTR_LAST		{ FBNIC_TLV_ATTR_ID_UNKNOWN, 0, 0 }

struct fbnic_tlv_parser {
	u16				id;
	const struct fbnic_tlv_index	*attr;
	int				(*func)(void *opaque,
						struct fbnic_tlv_msg **results);
};

#define FBNIC_TLV_PARSER(id, attr, func) { FBNIC_TLV_MSG_ID_##id, attr, func }

static inline void *
fbnic_tlv_attr_get_value_ptr(struct fbnic_tlv_msg *attr)
{
	return (void *)&attr->value[0];
}

static inline bool fbnic_tlv_attr_get_bool(struct fbnic_tlv_msg *attr)
{
	return !!attr;
}

u64 fbnic_tlv_attr_get_unsigned(struct fbnic_tlv_msg *attr);
s64 fbnic_tlv_attr_get_signed(struct fbnic_tlv_msg *attr);
size_t fbnic_tlv_attr_get_string(struct fbnic_tlv_msg *attr, char *str,
				 size_t max_size);

#define get_unsigned_result(id, location) \
do { \
	struct fbnic_tlv_msg *result = results[id]; \
	if (result) \
		location = fbnic_tlv_attr_get_unsigned(result); \
} while (0)

#define get_signed_result(id, location) \
do { \
	struct fbnic_tlv_msg *result = results[id]; \
	if (result) \
		location = fbnic_tlv_attr_get_signed(result); \
} while (0)

#define get_string_result(id, size, str, max_size) \
do { \
	struct fbnic_tlv_msg *result = results[id]; \
	if (result) \
		size = fbnic_tlv_attr_get_string(result, str, max_size); \
} while (0)

#define get_bool(id) (!!(results[id]))

struct fbnic_tlv_msg *fbnic_tlv_msg_alloc(u16 msg_id);
int fbnic_tlv_attr_put_flag(struct fbnic_tlv_msg *msg, const u16 attr_id);
int fbnic_tlv_attr_put_value(struct fbnic_tlv_msg *msg, const u16 attr_id,
			     const void *value, const int len);
int __fbnic_tlv_attr_put_int(struct fbnic_tlv_msg *msg, const u16 attr_id,
			     s64 value, const int len);
#define fbnic_tlv_attr_put_int(msg, attr_id, value) \
	__fbnic_tlv_attr_put_int(msg, attr_id, value, \
				 FBNIC_TLV_MSG_ALIGN(sizeof(value)))
int fbnic_tlv_attr_put_mac_addr(struct fbnic_tlv_msg *msg, const u16 attr_id,
				const u8 *mac_addr);
int fbnic_tlv_attr_put_string(struct fbnic_tlv_msg *msg, u16 attr_id,
			      const char *string);
struct fbnic_tlv_msg *fbnic_tlv_attr_nest_start(struct fbnic_tlv_msg *msg,
						u16 attr_id);
void fbnic_tlv_attr_nest_stop(struct fbnic_tlv_msg *msg);
void fbnic_tlv_attr_addr_copy(u8 *dest, struct fbnic_tlv_msg *src);
int fbnic_tlv_attr_parse_array(struct fbnic_tlv_msg *attr, int len,
			       struct fbnic_tlv_msg **results,
			       const struct fbnic_tlv_index *tlv_index,
			       u16 tlv_attr_id, size_t array_len);
int fbnic_tlv_attr_parse(struct fbnic_tlv_msg *attr, int len,
			 struct fbnic_tlv_msg **results,
			 const struct fbnic_tlv_index *tlv_index);
int fbnic_tlv_msg_parse(void *opaque, struct fbnic_tlv_msg *msg,
			const struct fbnic_tlv_parser *parser);
int fbnic_tlv_parser_error(void *opaque, struct fbnic_tlv_msg **results);

#define FBNIC_TLV_MSG_ERROR \
	FBNIC_TLV_PARSER(UNKNOWN, NULL, fbnic_tlv_parser_error)
#endif /* _FBNIC_TLV_H_ */