1 /* 2 * QEMU rocker switch emulation - TLV parsing and composing 3 * 4 * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 */ 16 17 #ifndef ROCKER_TLV_H 18 #define ROCKER_TLV_H 19 20 #define ROCKER_TLV_ALIGNTO 8U 21 #define ROCKER_TLV_ALIGN(len) \ 22 (((len) + ROCKER_TLV_ALIGNTO - 1) & ~(ROCKER_TLV_ALIGNTO - 1)) 23 #define ROCKER_TLV_HDRLEN ROCKER_TLV_ALIGN(sizeof(RockerTlv)) 24 25 /* 26 * <------- ROCKER_TLV_HDRLEN -------> <--- ROCKER_TLV_ALIGN(payload) ---> 27 * +-----------------------------+- - -+- - - - - - - - - - - - - - -+- - -+ 28 * | Header | Pad | Payload | Pad | 29 * | (RockerTlv) | ing | | ing | 30 * +-----------------------------+- - -+- - - - - - - - - - - - - - -+- - -+ 31 * <--------------------------- tlv->len --------------------------> 32 */ 33 34 static inline RockerTlv *rocker_tlv_next(const RockerTlv *tlv, int *remaining) 35 { 36 int totlen = ROCKER_TLV_ALIGN(le16_to_cpu(tlv->len)); 37 38 *remaining -= totlen; 39 return (RockerTlv *) ((char *) tlv + totlen); 40 } 41 42 static inline int rocker_tlv_ok(const RockerTlv *tlv, int remaining) 43 { 44 return remaining >= (int) ROCKER_TLV_HDRLEN && 45 le16_to_cpu(tlv->len) >= ROCKER_TLV_HDRLEN && 46 le16_to_cpu(tlv->len) <= remaining; 47 } 48 49 #define rocker_tlv_for_each(pos, head, len, rem) \ 50 for (pos = head, rem = len; \ 51 rocker_tlv_ok(pos, rem); \ 52 pos = rocker_tlv_next(pos, &(rem))) 53 54 #define rocker_tlv_for_each_nested(pos, tlv, rem) \ 55 rocker_tlv_for_each(pos, rocker_tlv_data(tlv), rocker_tlv_len(tlv), rem) 56 57 static inline int rocker_tlv_size(int payload) 58 { 59 return ROCKER_TLV_HDRLEN + payload; 60 } 61 62 static inline int rocker_tlv_total_size(int payload) 63 { 64 return ROCKER_TLV_ALIGN(rocker_tlv_size(payload)); 65 } 66 67 static inline int rocker_tlv_padlen(int payload) 68 { 69 return rocker_tlv_total_size(payload) - rocker_tlv_size(payload); 70 } 71 72 static inline int rocker_tlv_type(const RockerTlv *tlv) 73 { 74 return le32_to_cpu(tlv->type); 75 } 76 77 static inline void *rocker_tlv_data(const RockerTlv *tlv) 78 { 79 return (char *) tlv + ROCKER_TLV_HDRLEN; 80 } 81 82 static inline int rocker_tlv_len(const RockerTlv *tlv) 83 { 84 return le16_to_cpu(tlv->len) - ROCKER_TLV_HDRLEN; 85 } 86 87 static inline uint8_t rocker_tlv_get_u8(const RockerTlv *tlv) 88 { 89 return *(uint8_t *) rocker_tlv_data(tlv); 90 } 91 92 static inline uint16_t rocker_tlv_get_u16(const RockerTlv *tlv) 93 { 94 return *(uint16_t *) rocker_tlv_data(tlv); 95 } 96 97 static inline uint32_t rocker_tlv_get_u32(const RockerTlv *tlv) 98 { 99 return *(uint32_t *) rocker_tlv_data(tlv); 100 } 101 102 static inline uint64_t rocker_tlv_get_u64(const RockerTlv *tlv) 103 { 104 return *(uint64_t *) rocker_tlv_data(tlv); 105 } 106 107 static inline uint16_t rocker_tlv_get_le16(const RockerTlv *tlv) 108 { 109 return lduw_le_p(rocker_tlv_data(tlv)); 110 } 111 112 static inline uint32_t rocker_tlv_get_le32(const RockerTlv *tlv) 113 { 114 return ldl_le_p(rocker_tlv_data(tlv)); 115 } 116 117 static inline uint64_t rocker_tlv_get_le64(const RockerTlv *tlv) 118 { 119 return ldq_le_p(rocker_tlv_data(tlv)); 120 } 121 122 static inline void rocker_tlv_parse(RockerTlv **tb, int maxtype, 123 const char *buf, int buf_len) 124 { 125 const RockerTlv *tlv; 126 const RockerTlv *head = (const RockerTlv *) buf; 127 int rem; 128 129 memset(tb, 0, sizeof(RockerTlv *) * (maxtype + 1)); 130 131 rocker_tlv_for_each(tlv, head, buf_len, rem) { 132 uint32_t type = rocker_tlv_type(tlv); 133 134 if (type > 0 && type <= maxtype) { 135 tb[type] = (RockerTlv *) tlv; 136 } 137 } 138 } 139 140 static inline void rocker_tlv_parse_nested(RockerTlv **tb, int maxtype, 141 const RockerTlv *tlv) 142 { 143 rocker_tlv_parse(tb, maxtype, rocker_tlv_data(tlv), rocker_tlv_len(tlv)); 144 } 145 146 static inline RockerTlv *rocker_tlv_start(char *buf, int buf_pos) 147 { 148 return (RockerTlv *) (buf + buf_pos); 149 } 150 151 static inline void rocker_tlv_put_iov(char *buf, int *buf_pos, 152 int type, const struct iovec *iov, 153 const unsigned int iovcnt) 154 { 155 size_t len = iov_size(iov, iovcnt); 156 int total_size = rocker_tlv_total_size(len); 157 RockerTlv *tlv; 158 159 tlv = rocker_tlv_start(buf, *buf_pos); 160 *buf_pos += total_size; 161 tlv->type = cpu_to_le32(type); 162 tlv->len = cpu_to_le16(rocker_tlv_size(len)); 163 iov_to_buf(iov, iovcnt, 0, rocker_tlv_data(tlv), len); 164 memset((char *) tlv + le16_to_cpu(tlv->len), 0, rocker_tlv_padlen(len)); 165 } 166 167 static inline void rocker_tlv_put(char *buf, int *buf_pos, 168 int type, int len, void *data) 169 { 170 struct iovec iov = { 171 .iov_base = data, 172 .iov_len = len, 173 }; 174 175 rocker_tlv_put_iov(buf, buf_pos, type, &iov, 1); 176 } 177 178 static inline void rocker_tlv_put_u8(char *buf, int *buf_pos, 179 int type, uint8_t value) 180 { 181 rocker_tlv_put(buf, buf_pos, type, sizeof(uint8_t), &value); 182 } 183 184 static inline void rocker_tlv_put_u16(char *buf, int *buf_pos, 185 int type, uint16_t value) 186 { 187 rocker_tlv_put(buf, buf_pos, type, sizeof(uint16_t), &value); 188 } 189 190 static inline void rocker_tlv_put_u32(char *buf, int *buf_pos, 191 int type, uint32_t value) 192 { 193 rocker_tlv_put(buf, buf_pos, type, sizeof(uint32_t), &value); 194 } 195 196 static inline void rocker_tlv_put_u64(char *buf, int *buf_pos, 197 int type, uint64_t value) 198 { 199 rocker_tlv_put(buf, buf_pos, type, sizeof(uint64_t), &value); 200 } 201 202 static inline void rocker_tlv_put_le16(char *buf, int *buf_pos, 203 int type, uint16_t value) 204 { 205 value = cpu_to_le16(value); 206 rocker_tlv_put(buf, buf_pos, type, sizeof(uint16_t), &value); 207 } 208 209 static inline void rocker_tlv_put_le32(char *buf, int *buf_pos, 210 int type, uint32_t value) 211 { 212 value = cpu_to_le32(value); 213 rocker_tlv_put(buf, buf_pos, type, sizeof(uint32_t), &value); 214 } 215 216 static inline void rocker_tlv_put_le64(char *buf, int *buf_pos, 217 int type, uint64_t value) 218 { 219 value = cpu_to_le64(value); 220 rocker_tlv_put(buf, buf_pos, type, sizeof(uint64_t), &value); 221 } 222 223 static inline RockerTlv *rocker_tlv_nest_start(char *buf, int *buf_pos, 224 int type) 225 { 226 RockerTlv *start = rocker_tlv_start(buf, *buf_pos); 227 228 rocker_tlv_put(buf, buf_pos, type, 0, NULL); 229 return start; 230 } 231 232 static inline void rocker_tlv_nest_end(char *buf, int *buf_pos, 233 RockerTlv *start) 234 { 235 start->len = (char *) rocker_tlv_start(buf, *buf_pos) - (char *) start; 236 } 237 238 static inline void rocker_tlv_nest_cancel(char *buf, int *buf_pos, 239 RockerTlv *start) 240 { 241 *buf_pos = (char *) start - buf; 242 } 243 244 #endif 245