xref: /openbmc/libmctp/i2c.c (revision e5b941d9)
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 
mctp_i2c_valid_addr(uint8_t addr)24 static bool mctp_i2c_valid_addr(uint8_t addr)
25 {
26 	return addr <= 0x7f;
27 }
28 
mctp_i2c_valid_eid(uint8_t eid)29 static bool mctp_i2c_valid_eid(uint8_t eid)
30 {
31 	/* Disallow reserved range */
32 	return eid >= 8 && eid < 0xff;
33 }
34 
mctp_i2c_core_start(struct mctp_binding * binding)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 */
mctp_i2c_neigh_get(struct mctp_binding_i2c * i2c,uint8_t eid,uint8_t * ret_neigh_addr)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. */
mctp_i2c_neigh_add(struct mctp_binding_i2c * i2c,uint8_t eid,uint8_t addr)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 
mctp_binding_i2c_tx(struct mctp_binding * b,struct mctp_pktbuf * pkt)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 
mctp_i2c_set_neighbour(struct mctp_binding_i2c * i2c,uint8_t eid,uint8_t addr)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 
mctp_i2c_setup(struct mctp_binding_i2c * i2c,uint8_t own_addr,mctp_i2c_tx_fn tx_fn,void * tx_ctx)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 
mctp_i2c_set_address(struct mctp_binding_i2c * i2c,uint8_t own_addr)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 
mctp_binding_i2c_core(struct mctp_binding_i2c * i2c)181 struct mctp_binding *mctp_binding_i2c_core(struct mctp_binding_i2c *i2c)
182 {
183 	return &i2c->binding;
184 }
185 
mctp_i2c_hdr_validate(const struct mctp_i2c_hdr * hdr)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 
mctp_i2c_rx(struct mctp_binding_i2c * i2c,const void * data,size_t len)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 
mctp_i2c_parse_hdr(const void * data,size_t len,uint8_t * src_addr,uint8_t * dest_addr,uint8_t * bytecount)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 
mctp_i2c_tx_poll(struct mctp_binding_i2c * i2c)275 void mctp_i2c_tx_poll(struct mctp_binding_i2c *i2c)
276 {
277 	mctp_binding_set_tx_enabled(&i2c->binding, true);
278 }
279