1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ 2 3 #include <assert.h> 4 #include <errno.h> 5 #include <stdbool.h> 6 #include <stdlib.h> 7 #include <string.h> 8 9 #ifdef HAVE_CONFIG_H 10 #include "config.h" 11 #endif 12 13 #include "libmctp.h" 14 #include "libmctp-alloc.h" 15 #include "libmctp-log.h" 16 #include "container_of.h" 17 #include "libmctp-i2c.h" 18 #include "i2c-internal.h" 19 20 static const uint8_t MCTP_I2C_COMMAND = 0x0f; 21 22 #define binding_to_i2c(b) container_of(b, struct mctp_binding_i2c, binding) 23 24 static bool mctp_i2c_valid_addr(uint8_t addr) 25 { 26 return addr <= 0x7f; 27 } 28 29 static bool mctp_i2c_valid_eid(uint8_t eid) 30 { 31 /* Disallow reserved range */ 32 return eid >= 8 && eid < 0xff; 33 } 34 35 static int mctp_i2c_core_start(struct mctp_binding *binding) 36 { 37 mctp_binding_set_tx_enabled(binding, true); 38 return 0; 39 } 40 41 /* Returns 0 if an entry is found, or -ENOENT otherwise. 42 * The last seen timestamp will be updated for found entries */ 43 static int mctp_i2c_neigh_get(struct mctp_binding_i2c *i2c, uint8_t eid, 44 uint8_t *ret_neigh_addr) 45 { 46 for (size_t i = 0; i < MCTP_I2C_NEIGH_COUNT; i++) { 47 struct mctp_i2c_neigh *n = &i2c->neigh[i]; 48 if (n->used && n->eid == eid) { 49 n->last_seen_timestamp = mctp_now(i2c->binding.mctp); 50 *ret_neigh_addr = n->addr; 51 return 0; 52 } 53 } 54 return -ENOENT; 55 } 56 57 /* Adds a new neighbour entry. If the table is full, the oldest 58 * entry will be evicted. If eid already exists, that entry will 59 * be replaced. */ 60 static void mctp_i2c_neigh_add(struct mctp_binding_i2c *i2c, uint8_t eid, 61 uint8_t addr) 62 { 63 assert(addr <= 0x7f); 64 struct mctp_i2c_neigh *entry = NULL; 65 for (size_t i = 0; i < MCTP_I2C_NEIGH_COUNT; i++) { 66 struct mctp_i2c_neigh *n = &i2c->neigh[i]; 67 if (!n->used) { 68 /* Spare entry, use it */ 69 entry = n; 70 break; 71 } 72 73 if (n->eid == eid) { 74 /* Replacing existing entry */ 75 entry = n; 76 break; 77 } 78 79 if (!entry || 80 n->last_seen_timestamp < entry->last_seen_timestamp) { 81 /* Use this as the provisional oldest, keep iterating */ 82 entry = n; 83 } 84 } 85 assert(entry); 86 87 entry->addr = addr; 88 entry->eid = eid; 89 entry->used = true; 90 entry->last_seen_timestamp = mctp_now(i2c->binding.mctp); 91 } 92 93 static int mctp_binding_i2c_tx(struct mctp_binding *b, struct mctp_pktbuf *pkt) 94 { 95 struct mctp_binding_i2c *i2c = binding_to_i2c(b); 96 struct mctp_hdr *hdr = mctp_pktbuf_hdr(pkt); 97 int rc; 98 uint8_t neigh_addr; 99 100 rc = mctp_i2c_neigh_get(i2c, hdr->dest, &neigh_addr); 101 if (rc) { 102 return rc; 103 } 104 105 struct mctp_i2c_hdr *i2c_hdr = 106 mctp_pktbuf_alloc_start(pkt, sizeof(struct mctp_i2c_hdr)); 107 i2c_hdr->dest = neigh_addr << 1; 108 i2c_hdr->cmd = MCTP_I2C_COMMAND; 109 size_t bytecount = mctp_pktbuf_size(pkt) - 110 (offsetof(struct mctp_i2c_hdr, bytecount) + 1); 111 if (bytecount > 0xff) { 112 return -EINVAL; 113 } 114 i2c_hdr->bytecount = bytecount; 115 i2c_hdr->source = i2c->own_addr << 1 | 1; 116 117 rc = i2c->tx_fn(pkt->data + pkt->start, mctp_pktbuf_size(pkt), 118 i2c->tx_ctx); 119 switch (rc) { 120 case -EMSGSIZE: 121 case 0: 122 break; 123 case -EBUSY: 124 default: 125 mctp_binding_set_tx_enabled(&i2c->binding, false); 126 } 127 return rc; 128 } 129 130 int mctp_i2c_set_neighbour(struct mctp_binding_i2c *i2c, uint8_t eid, 131 uint8_t addr) 132 { 133 if (!mctp_i2c_valid_eid(eid)) { 134 return -EINVAL; 135 } 136 if (!mctp_i2c_valid_addr(addr)) { 137 return -EINVAL; 138 } 139 140 mctp_i2c_neigh_add(i2c, eid, addr); 141 return 0; 142 } 143 144 int mctp_i2c_setup(struct mctp_binding_i2c *i2c, uint8_t own_addr, 145 mctp_i2c_tx_fn tx_fn, void *tx_ctx) 146 { 147 int rc; 148 149 memset(i2c, 0x0, sizeof(*i2c)); 150 151 rc = mctp_i2c_set_address(i2c, own_addr); 152 if (rc) { 153 return rc; 154 } 155 156 i2c->binding.name = "i2c"; 157 i2c->binding.version = 1; 158 i2c->binding.pkt_size = MCTP_PACKET_SIZE(I2C_BTU); 159 i2c->binding.pkt_header = sizeof(struct mctp_i2c_hdr); 160 i2c->binding.tx_storage = i2c->tx_storage; 161 162 i2c->binding.start = mctp_i2c_core_start; 163 i2c->binding.tx = mctp_binding_i2c_tx; 164 165 i2c->tx_fn = tx_fn; 166 i2c->tx_ctx = tx_ctx; 167 168 return 0; 169 } 170 171 int mctp_i2c_set_address(struct mctp_binding_i2c *i2c, uint8_t own_addr) 172 { 173 if (!mctp_i2c_valid_addr(own_addr)) { 174 return -EINVAL; 175 } 176 177 i2c->own_addr = own_addr; 178 return 0; 179 } 180 181 struct mctp_binding *mctp_binding_i2c_core(struct mctp_binding_i2c *i2c) 182 { 183 return &i2c->binding; 184 } 185 186 static int mctp_i2c_hdr_validate(const struct mctp_i2c_hdr *hdr) 187 { 188 if (hdr->cmd != MCTP_I2C_COMMAND) { 189 return -EINVAL; 190 } 191 if ((hdr->dest & 1) != 0) { 192 return -EINVAL; 193 } 194 if ((hdr->source & 1) != 1) { 195 return -EINVAL; 196 } 197 return 0; 198 } 199 200 void mctp_i2c_rx(struct mctp_binding_i2c *i2c, const void *data, size_t len) 201 { 202 int rc; 203 204 if (len < sizeof(struct mctp_i2c_hdr)) { 205 return; 206 } 207 const struct mctp_i2c_hdr *hdr = data; 208 rc = mctp_i2c_hdr_validate(hdr); 209 if (rc) { 210 return; 211 } 212 213 if (hdr->bytecount != len - 3) { 214 return; 215 } 216 217 if ((hdr->dest >> 1) != i2c->own_addr) { 218 return; 219 } 220 221 uint8_t src = hdr->source >> 1; 222 if (src == i2c->own_addr) { 223 return; 224 } 225 226 struct mctp_pktbuf *pkt = 227 mctp_pktbuf_init(&i2c->binding, i2c->rx_storage); 228 rc = mctp_pktbuf_push( 229 pkt, (const uint8_t *)data + sizeof(struct mctp_i2c_hdr), 230 len - sizeof(struct mctp_i2c_hdr)); 231 if (rc) { 232 // Packet too large for I2C_BTU 233 return; 234 } 235 236 if (mctp_pktbuf_size(pkt) < sizeof(struct mctp_hdr)) { 237 return; 238 } 239 240 struct mctp_hdr *mctp_hdr = mctp_pktbuf_hdr(pkt); 241 if (mctp_hdr->flags_seq_tag & MCTP_HDR_FLAG_TO) { 242 /* Update neighbour entry */ 243 mctp_i2c_neigh_add(i2c, mctp_hdr->src, src); 244 } 245 246 mctp_bus_rx(&i2c->binding, pkt); 247 } 248 249 int mctp_i2c_parse_hdr(const void *data, size_t len, uint8_t *src_addr, 250 uint8_t *dest_addr, uint8_t *bytecount) 251 { 252 int rc; 253 254 if (len < sizeof(struct mctp_i2c_hdr)) { 255 return -EINVAL; 256 } 257 const struct mctp_i2c_hdr *hdr = data; 258 rc = mctp_i2c_hdr_validate(hdr); 259 if (rc) { 260 return rc; 261 } 262 263 if (src_addr) { 264 *src_addr = hdr->source >> 1; 265 } 266 if (dest_addr) { 267 *dest_addr = hdr->dest >> 1; 268 } 269 if (bytecount) { 270 *bytecount = hdr->bytecount; 271 } 272 return 0; 273 } 274 275 void mctp_i2c_tx_poll(struct mctp_binding_i2c *i2c) 276 { 277 mctp_binding_set_tx_enabled(&i2c->binding, true); 278 } 279