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