xref: /openbmc/libmctp/core.c (revision a3830d259a53269f7b9c8b46129e863ebed1b188)
13d36ee2eSJeremy Kerr /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
24cdc200fSJeremy Kerr 
34cdc200fSJeremy Kerr #include <assert.h>
43e8a12a4SAndrew Jeffery #include <errno.h>
54cdc200fSJeremy Kerr #include <stdarg.h>
64cdc200fSJeremy Kerr #include <stddef.h>
74cdc200fSJeremy Kerr #include <stdint.h>
84cdc200fSJeremy Kerr #include <stdio.h>
94cdc200fSJeremy Kerr #include <stdlib.h>
104cdc200fSJeremy Kerr #include <string.h>
11*a3830d25SMatt Johnston #include <stdalign.h>
124cdc200fSJeremy Kerr 
134cdc200fSJeremy Kerr #undef pr_fmt
144cdc200fSJeremy Kerr #define pr_fmt(fmt) "core: " fmt
154cdc200fSJeremy Kerr 
164cdc200fSJeremy Kerr #include "libmctp.h"
174cdc200fSJeremy Kerr #include "libmctp-alloc.h"
184cdc200fSJeremy Kerr #include "libmctp-log.h"
19ba6727e6SWiktor Gołgowski #include "libmctp-cmds.h"
20c2b833e4SAndrew Jeffery #include "range.h"
214a09e1dcSMatt Johnston #include "compiler.h"
22f9b99f1fSMatt Johnston #include "core-internal.h"
234058b2cbSMatt Johnston #include "control.h"
244cdc200fSJeremy Kerr 
2544e64dfaSMatt Johnston #if MCTP_DEFAULT_CLOCK_GETTIME
2644e64dfaSMatt Johnston #include <time.h>
2744e64dfaSMatt Johnston #endif
2844e64dfaSMatt Johnston 
2924db71fbSJeremy Kerr #ifndef ARRAY_SIZE
3024db71fbSJeremy Kerr #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
3124db71fbSJeremy Kerr #endif
3224db71fbSJeremy Kerr 
33b93b6112SAndrew Jeffery static int mctp_message_tx_on_bus(struct mctp_bus *bus, mctp_eid_t src,
34f39c3857SSumanth Bhat 				  mctp_eid_t dest, bool tag_owner,
35f39c3857SSumanth Bhat 				  uint8_t msg_tag, void *msg, size_t msg_len);
3661c95992SMatt Johnston static void mctp_dealloc_tag(struct mctp_bus *bus, mctp_eid_t local,
3761c95992SMatt Johnston 			     mctp_eid_t remote, uint8_t tag);
381a4ec3cdSJeremy Kerr 
mctp_pktbuf_alloc(struct mctp_binding * binding,size_t len)39df15f7e9SJeremy Kerr struct mctp_pktbuf *mctp_pktbuf_alloc(struct mctp_binding *binding, size_t len)
404cdc200fSJeremy Kerr {
414a09e1dcSMatt Johnston 	size_t size =
424a09e1dcSMatt Johnston 		binding->pkt_size + binding->pkt_header + binding->pkt_trailer;
43487b31e0SRashmica Gupta 	if (len > size) {
44487b31e0SRashmica Gupta 		return NULL;
45487b31e0SRashmica Gupta 	}
464cdc200fSJeremy Kerr 
474a09e1dcSMatt Johnston 	void *storage = __mctp_alloc(size + sizeof(struct mctp_pktbuf));
484a09e1dcSMatt Johnston 	if (!storage) {
492608b294SPedro Martelletto 		return NULL;
504a09e1dcSMatt Johnston 	}
514a09e1dcSMatt Johnston 	struct mctp_pktbuf *pkt = mctp_pktbuf_init(binding, storage);
524a09e1dcSMatt Johnston 	pkt->alloc = true;
534a09e1dcSMatt Johnston 	pkt->end = pkt->start + len;
544a09e1dcSMatt Johnston 	return pkt;
554cdc200fSJeremy Kerr }
564cdc200fSJeremy Kerr 
mctp_pktbuf_free(struct mctp_pktbuf * pkt)574cdc200fSJeremy Kerr void mctp_pktbuf_free(struct mctp_pktbuf *pkt)
584cdc200fSJeremy Kerr {
594a09e1dcSMatt Johnston 	if (pkt->alloc) {
604cdc200fSJeremy Kerr 		__mctp_free(pkt);
614a09e1dcSMatt Johnston 	} else {
624a09e1dcSMatt Johnston 		mctp_prdebug("pktbuf_free called for non-alloced");
634a09e1dcSMatt Johnston 	}
644a09e1dcSMatt Johnston }
654a09e1dcSMatt Johnston 
mctp_pktbuf_init(struct mctp_binding * binding,void * storage)664a09e1dcSMatt Johnston struct mctp_pktbuf *mctp_pktbuf_init(struct mctp_binding *binding,
674a09e1dcSMatt Johnston 				     void *storage)
684a09e1dcSMatt Johnston {
69*a3830d25SMatt Johnston 	assert((size_t)storage % alignof(struct mctp_pktbuf) == 0);
70*a3830d25SMatt Johnston 
714a09e1dcSMatt Johnston 	size_t size =
724a09e1dcSMatt Johnston 		binding->pkt_size + binding->pkt_header + binding->pkt_trailer;
734a09e1dcSMatt Johnston 	struct mctp_pktbuf *buf = (struct mctp_pktbuf *)storage;
744a09e1dcSMatt Johnston 	buf->size = size;
754a09e1dcSMatt Johnston 	buf->start = binding->pkt_header;
764a09e1dcSMatt Johnston 	buf->end = buf->start;
774a09e1dcSMatt Johnston 	buf->mctp_hdr_off = buf->start;
784a09e1dcSMatt Johnston 	buf->alloc = false;
794a09e1dcSMatt Johnston 
804a09e1dcSMatt Johnston 	return buf;
814cdc200fSJeremy Kerr }
824cdc200fSJeremy Kerr 
mctp_pktbuf_hdr(struct mctp_pktbuf * pkt)834cdc200fSJeremy Kerr struct mctp_hdr *mctp_pktbuf_hdr(struct mctp_pktbuf *pkt)
844cdc200fSJeremy Kerr {
857aaccb51SMoritz Fischer 	return (struct mctp_hdr *)(pkt->data + pkt->mctp_hdr_off);
864cdc200fSJeremy Kerr }
874cdc200fSJeremy Kerr 
mctp_pktbuf_data(struct mctp_pktbuf * pkt)884cdc200fSJeremy Kerr void *mctp_pktbuf_data(struct mctp_pktbuf *pkt)
894cdc200fSJeremy Kerr {
907aaccb51SMoritz Fischer 	return pkt->data + pkt->mctp_hdr_off + sizeof(struct mctp_hdr);
914cdc200fSJeremy Kerr }
924cdc200fSJeremy Kerr 
mctp_pktbuf_size(const struct mctp_pktbuf * pkt)934a09e1dcSMatt Johnston size_t mctp_pktbuf_size(const struct mctp_pktbuf *pkt)
944cdc200fSJeremy Kerr {
954cdc200fSJeremy Kerr 	return pkt->end - pkt->start;
964cdc200fSJeremy Kerr }
974cdc200fSJeremy Kerr 
mctp_pktbuf_alloc_start(struct mctp_pktbuf * pkt,size_t size)98df15f7e9SJeremy Kerr void *mctp_pktbuf_alloc_start(struct mctp_pktbuf *pkt, size_t size)
994cdc200fSJeremy Kerr {
1004cdc200fSJeremy Kerr 	assert(size <= pkt->start);
1014cdc200fSJeremy Kerr 	pkt->start -= size;
1024cdc200fSJeremy Kerr 	return pkt->data + pkt->start;
1034cdc200fSJeremy Kerr }
1044cdc200fSJeremy Kerr 
mctp_pktbuf_alloc_end(struct mctp_pktbuf * pkt,size_t size)105df15f7e9SJeremy Kerr void *mctp_pktbuf_alloc_end(struct mctp_pktbuf *pkt, size_t size)
1064cdc200fSJeremy Kerr {
1074cdc200fSJeremy Kerr 	void *buf;
1084cdc200fSJeremy Kerr 
1093ac70d62SAndrew Jeffery 	assert(size <= (pkt->size - pkt->end));
1104cdc200fSJeremy Kerr 	buf = pkt->data + pkt->end;
1114cdc200fSJeremy Kerr 	pkt->end += size;
1124cdc200fSJeremy Kerr 	return buf;
1134cdc200fSJeremy Kerr }
1144cdc200fSJeremy Kerr 
mctp_pktbuf_push(struct mctp_pktbuf * pkt,const void * data,size_t len)115dfbf0fd0SMatt Johnston int mctp_pktbuf_push(struct mctp_pktbuf *pkt, const void *data, size_t len)
1164cdc200fSJeremy Kerr {
1174cdc200fSJeremy Kerr 	void *p;
1184cdc200fSJeremy Kerr 
119df15f7e9SJeremy Kerr 	if (pkt->end + len > pkt->size)
1204cdc200fSJeremy Kerr 		return -1;
1214cdc200fSJeremy Kerr 
1224cdc200fSJeremy Kerr 	p = pkt->data + pkt->end;
1234cdc200fSJeremy Kerr 
1244cdc200fSJeremy Kerr 	pkt->end += len;
1254cdc200fSJeremy Kerr 	memcpy(p, data, len);
1264cdc200fSJeremy Kerr 
1274cdc200fSJeremy Kerr 	return 0;
1284cdc200fSJeremy Kerr }
1294cdc200fSJeremy Kerr 
mctp_pktbuf_pop(struct mctp_pktbuf * pkt,size_t len)130eba19a3bSAndrew Jeffery void *mctp_pktbuf_pop(struct mctp_pktbuf *pkt, size_t len)
131eba19a3bSAndrew Jeffery {
132eba19a3bSAndrew Jeffery 	if (len > mctp_pktbuf_size(pkt))
133eba19a3bSAndrew Jeffery 		return NULL;
134eba19a3bSAndrew Jeffery 
135eba19a3bSAndrew Jeffery 	pkt->end -= len;
136eba19a3bSAndrew Jeffery 	return pkt->data + pkt->end;
137eba19a3bSAndrew Jeffery }
138eba19a3bSAndrew Jeffery 
1394a09e1dcSMatt Johnston /* Allocate a duplicate of the message and copy it */
mctp_msg_dup(const void * msg,size_t msg_len,struct mctp * mctp)1404a09e1dcSMatt Johnston static void *mctp_msg_dup(const void *msg, size_t msg_len, struct mctp *mctp)
1414a09e1dcSMatt Johnston {
1424a09e1dcSMatt Johnston 	void *copy = __mctp_msg_alloc(msg_len, mctp);
1434a09e1dcSMatt Johnston 	if (!copy) {
1444a09e1dcSMatt Johnston 		mctp_prdebug("msg dup len %zu failed", msg_len);
1454a09e1dcSMatt Johnston 		return NULL;
1464a09e1dcSMatt Johnston 	}
1474a09e1dcSMatt Johnston 
1484a09e1dcSMatt Johnston 	memcpy(copy, msg, msg_len);
1494a09e1dcSMatt Johnston 	return copy;
1504a09e1dcSMatt Johnston }
1514a09e1dcSMatt Johnston 
15224db71fbSJeremy Kerr /* Message reassembly */
mctp_msg_ctx_lookup(struct mctp * mctp,uint8_t src,uint8_t dest,uint8_t tag)153a721c2d8SPatrick Williams static struct mctp_msg_ctx *mctp_msg_ctx_lookup(struct mctp *mctp, uint8_t src,
154a721c2d8SPatrick Williams 						uint8_t dest, uint8_t tag)
15524db71fbSJeremy Kerr {
15624db71fbSJeremy Kerr 	unsigned int i;
15724db71fbSJeremy Kerr 
15824db71fbSJeremy Kerr 	/* @todo: better lookup, if we add support for more outstanding
15924db71fbSJeremy Kerr 	 * message contexts */
16024db71fbSJeremy Kerr 	for (i = 0; i < ARRAY_SIZE(mctp->msg_ctxs); i++) {
16124db71fbSJeremy Kerr 		struct mctp_msg_ctx *ctx = &mctp->msg_ctxs[i];
1624a09e1dcSMatt Johnston 		if (ctx->buf && ctx->src == src && ctx->dest == dest &&
1634a09e1dcSMatt Johnston 		    ctx->tag == tag)
16424db71fbSJeremy Kerr 			return ctx;
16524db71fbSJeremy Kerr 	}
16624db71fbSJeremy Kerr 
16724db71fbSJeremy Kerr 	return NULL;
16824db71fbSJeremy Kerr }
16924db71fbSJeremy Kerr 
mctp_msg_ctx_create(struct mctp * mctp,uint8_t src,uint8_t dest,uint8_t tag)170a721c2d8SPatrick Williams static struct mctp_msg_ctx *mctp_msg_ctx_create(struct mctp *mctp, uint8_t src,
171a721c2d8SPatrick Williams 						uint8_t dest, uint8_t tag)
17224db71fbSJeremy Kerr {
17311a234e1SJeremy Kerr 	struct mctp_msg_ctx *ctx = NULL;
17424db71fbSJeremy Kerr 	unsigned int i;
17524db71fbSJeremy Kerr 
17624db71fbSJeremy Kerr 	for (i = 0; i < ARRAY_SIZE(mctp->msg_ctxs); i++) {
17724db71fbSJeremy Kerr 		struct mctp_msg_ctx *tmp = &mctp->msg_ctxs[i];
1784a09e1dcSMatt Johnston 		if (!tmp->buf) {
17924db71fbSJeremy Kerr 			ctx = tmp;
18024db71fbSJeremy Kerr 			break;
18124db71fbSJeremy Kerr 		}
18224db71fbSJeremy Kerr 	}
18324db71fbSJeremy Kerr 
18424db71fbSJeremy Kerr 	if (!ctx)
18524db71fbSJeremy Kerr 		return NULL;
18624db71fbSJeremy Kerr 
18724db71fbSJeremy Kerr 	ctx->src = src;
1881a4ec3cdSJeremy Kerr 	ctx->dest = dest;
18924db71fbSJeremy Kerr 	ctx->tag = tag;
1904a09e1dcSMatt Johnston 
1919a3da81cSJeremy Kerr 	ctx->buf_size = 0;
1924a09e1dcSMatt Johnston 	ctx->buf_alloc_size = mctp->max_message_size;
1934a09e1dcSMatt Johnston 	ctx->buf = __mctp_msg_alloc(ctx->buf_alloc_size, mctp);
1944a09e1dcSMatt Johnston 	if (!ctx->buf) {
1954a09e1dcSMatt Johnston 		return NULL;
1964a09e1dcSMatt Johnston 	}
19724db71fbSJeremy Kerr 
19824db71fbSJeremy Kerr 	return ctx;
19924db71fbSJeremy Kerr }
20024db71fbSJeremy Kerr 
mctp_msg_ctx_drop(struct mctp_bus * bus,struct mctp_msg_ctx * ctx)2014a09e1dcSMatt Johnston static void mctp_msg_ctx_drop(struct mctp_bus *bus, struct mctp_msg_ctx *ctx)
20224db71fbSJeremy Kerr {
2034a09e1dcSMatt Johnston 	/* Free and mark as unused */
2044a09e1dcSMatt Johnston 	__mctp_msg_free(ctx->buf, bus->mctp);
2054a09e1dcSMatt Johnston 	ctx->buf = NULL;
20624db71fbSJeremy Kerr }
20724db71fbSJeremy Kerr 
mctp_msg_ctx_reset(struct mctp_msg_ctx * ctx)20824db71fbSJeremy Kerr static void mctp_msg_ctx_reset(struct mctp_msg_ctx *ctx)
20924db71fbSJeremy Kerr {
21024db71fbSJeremy Kerr 	ctx->buf_size = 0;
21169f545f7SSumanth Bhat 	ctx->fragment_size = 0;
21224db71fbSJeremy Kerr }
21324db71fbSJeremy Kerr 
mctp_msg_ctx_add_pkt(struct mctp_msg_ctx * ctx,struct mctp_pktbuf * pkt)21424db71fbSJeremy Kerr static int mctp_msg_ctx_add_pkt(struct mctp_msg_ctx *ctx,
2154a09e1dcSMatt Johnston 				struct mctp_pktbuf *pkt)
21624db71fbSJeremy Kerr {
21724db71fbSJeremy Kerr 	size_t len;
21824db71fbSJeremy Kerr 
21924db71fbSJeremy Kerr 	len = mctp_pktbuf_size(pkt) - sizeof(struct mctp_hdr);
22024db71fbSJeremy Kerr 
221bc79c24eSSumanth Bhat 	if (len + ctx->buf_size < ctx->buf_size) {
222bc79c24eSSumanth Bhat 		return -1;
223bc79c24eSSumanth Bhat 	}
224bc79c24eSSumanth Bhat 
22524db71fbSJeremy Kerr 	if (ctx->buf_size + len > ctx->buf_alloc_size) {
2262c820c5aSSumanth Bhat 		return -1;
22724db71fbSJeremy Kerr 	}
22824db71fbSJeremy Kerr 
2297aaccb51SMoritz Fischer 	memcpy((uint8_t *)ctx->buf + ctx->buf_size, mctp_pktbuf_data(pkt), len);
23024db71fbSJeremy Kerr 	ctx->buf_size += len;
23124db71fbSJeremy Kerr 
23224db71fbSJeremy Kerr 	return 0;
23324db71fbSJeremy Kerr }
23424db71fbSJeremy Kerr 
23524db71fbSJeremy Kerr /* Core API functions */
mctp_init(void)2364cdc200fSJeremy Kerr struct mctp *mctp_init(void)
2374cdc200fSJeremy Kerr {
2384cdc200fSJeremy Kerr 	struct mctp *mctp;
2394cdc200fSJeremy Kerr 
2404cdc200fSJeremy Kerr 	mctp = __mctp_alloc(sizeof(*mctp));
24196d54492SSumanth Bhat 
24296d54492SSumanth Bhat 	if (!mctp)
24396d54492SSumanth Bhat 		return NULL;
24496d54492SSumanth Bhat 
245f9b99f1fSMatt Johnston 	mctp_setup(mctp, sizeof(*mctp));
246722d0db2SMatt Johnston 	return mctp;
247722d0db2SMatt Johnston }
248722d0db2SMatt Johnston 
24944e64dfaSMatt Johnston #if MCTP_DEFAULT_CLOCK_GETTIME
mctp_default_now(void * ctx)25044e64dfaSMatt Johnston static uint64_t mctp_default_now(void *ctx __attribute__((unused)))
25144e64dfaSMatt Johnston {
25244e64dfaSMatt Johnston 	struct timespec tp;
25344e64dfaSMatt Johnston 	int rc = clock_gettime(CLOCK_MONOTONIC, &tp);
25444e64dfaSMatt Johnston 	if (rc) {
25544e64dfaSMatt Johnston 		/* Should not be possible */
25644e64dfaSMatt Johnston 		return 0;
25744e64dfaSMatt Johnston 	}
25844e64dfaSMatt Johnston 	return (uint64_t)tp.tv_sec * 1000 + tp.tv_nsec / 1000000;
25944e64dfaSMatt Johnston }
26044e64dfaSMatt Johnston #endif
26144e64dfaSMatt Johnston 
mctp_setup(struct mctp * mctp,size_t struct_mctp_size)262f9b99f1fSMatt Johnston int mctp_setup(struct mctp *mctp, size_t struct_mctp_size)
263722d0db2SMatt Johnston {
264f9b99f1fSMatt Johnston 	if (struct_mctp_size < sizeof(struct mctp)) {
265f9b99f1fSMatt Johnston 		mctp_prdebug("Mismatching struct mctp");
266f9b99f1fSMatt Johnston 		return -EINVAL;
267f9b99f1fSMatt Johnston 	}
2684cdc200fSJeremy Kerr 	memset(mctp, 0, sizeof(*mctp));
2692c820c5aSSumanth Bhat 	mctp->max_message_size = MCTP_MAX_MESSAGE_SIZE;
27044e64dfaSMatt Johnston #if MCTP_DEFAULT_CLOCK_GETTIME
27144e64dfaSMatt Johnston 	mctp->platform_now = mctp_default_now;
27244e64dfaSMatt Johnston #endif
2734058b2cbSMatt Johnston #if MCTP_CONTROL_HANDLER
2744058b2cbSMatt Johnston 	mctp_control_add_type(mctp, MCTP_CTRL_HDR_MSG_TYPE);
2754058b2cbSMatt Johnston #endif
276f9b99f1fSMatt Johnston 	return 0;
2774cdc200fSJeremy Kerr }
2784cdc200fSJeremy Kerr 
mctp_set_max_message_size(struct mctp * mctp,size_t message_size)2792c820c5aSSumanth Bhat void mctp_set_max_message_size(struct mctp *mctp, size_t message_size)
2802c820c5aSSumanth Bhat {
2812c820c5aSSumanth Bhat 	mctp->max_message_size = message_size;
2822c820c5aSSumanth Bhat }
2832c820c5aSSumanth Bhat 
mctp_set_capture_handler(struct mctp * mctp,mctp_capture_fn fn,void * user)2845d3d4e6dSAndrew Jeffery void mctp_set_capture_handler(struct mctp *mctp, mctp_capture_fn fn, void *user)
2855d3d4e6dSAndrew Jeffery {
2865d3d4e6dSAndrew Jeffery 	mctp->capture = fn;
2875d3d4e6dSAndrew Jeffery 	mctp->capture_data = user;
2885d3d4e6dSAndrew Jeffery }
2895d3d4e6dSAndrew Jeffery 
mctp_bus_destroy(struct mctp_bus * bus,struct mctp * mctp)2904a09e1dcSMatt Johnston static void mctp_bus_destroy(struct mctp_bus *bus, struct mctp *mctp)
2913ae89dceSAndrew Jeffery {
2924a09e1dcSMatt Johnston 	if (bus->tx_msg) {
2934a09e1dcSMatt Johnston 		__mctp_msg_free(bus->tx_msg, mctp);
2944a09e1dcSMatt Johnston 		bus->tx_msg = NULL;
2953ae89dceSAndrew Jeffery 	}
2963ae89dceSAndrew Jeffery }
2973ae89dceSAndrew Jeffery 
mctp_cleanup(struct mctp * mctp)298722d0db2SMatt Johnston void mctp_cleanup(struct mctp *mctp)
299fa56ca5fSAndrew Jeffery {
300b93b6112SAndrew Jeffery 	size_t i;
301fa56ca5fSAndrew Jeffery 
302fa56ca5fSAndrew Jeffery 	/* Cleanup message assembly contexts */
3033ef47785SMatt Johnston 	static_assert(ARRAY_SIZE(mctp->msg_ctxs) < SIZE_MAX, "size");
304fa56ca5fSAndrew Jeffery 	for (i = 0; i < ARRAY_SIZE(mctp->msg_ctxs); i++) {
305fa56ca5fSAndrew Jeffery 		struct mctp_msg_ctx *tmp = &mctp->msg_ctxs[i];
306fa56ca5fSAndrew Jeffery 		if (tmp->buf)
3074a09e1dcSMatt Johnston 			__mctp_msg_free(tmp->buf, mctp);
308fa56ca5fSAndrew Jeffery 	}
309fa56ca5fSAndrew Jeffery 
3103ae89dceSAndrew Jeffery 	while (mctp->n_busses--)
3114a09e1dcSMatt Johnston 		mctp_bus_destroy(&mctp->busses[mctp->n_busses], mctp);
312722d0db2SMatt Johnston }
3133ae89dceSAndrew Jeffery 
mctp_destroy(struct mctp * mctp)314722d0db2SMatt Johnston void mctp_destroy(struct mctp *mctp)
315722d0db2SMatt Johnston {
316722d0db2SMatt Johnston 	mctp_cleanup(mctp);
317fa56ca5fSAndrew Jeffery 	__mctp_free(mctp);
318fa56ca5fSAndrew Jeffery }
319fa56ca5fSAndrew Jeffery 
mctp_set_rx_all(struct mctp * mctp,mctp_rx_fn fn,void * data)3204cdc200fSJeremy Kerr int mctp_set_rx_all(struct mctp *mctp, mctp_rx_fn fn, void *data)
3214cdc200fSJeremy Kerr {
3224cdc200fSJeremy Kerr 	mctp->message_rx = fn;
3234cdc200fSJeremy Kerr 	mctp->message_rx_data = data;
3244cdc200fSJeremy Kerr 	return 0;
3254cdc200fSJeremy Kerr }
3264cdc200fSJeremy Kerr 
find_bus_for_eid(struct mctp * mctp,mctp_eid_t dest)327a721c2d8SPatrick Williams static struct mctp_bus *find_bus_for_eid(struct mctp *mctp, mctp_eid_t dest
328a721c2d8SPatrick Williams 					 __attribute__((unused)))
3294cdc200fSJeremy Kerr {
330663ec39eSBrad Bishop 	if (mctp->n_busses == 0)
331663ec39eSBrad Bishop 		return NULL;
332663ec39eSBrad Bishop 
3331a4ec3cdSJeremy Kerr 	/* for now, just use the first bus. For full routing support,
3341a4ec3cdSJeremy Kerr 	 * we will need a table of neighbours */
3354cdc200fSJeremy Kerr 	return &mctp->busses[0];
3364cdc200fSJeremy Kerr }
3374cdc200fSJeremy Kerr 
mctp_register_bus(struct mctp * mctp,struct mctp_binding * binding,mctp_eid_t eid)338a721c2d8SPatrick Williams int mctp_register_bus(struct mctp *mctp, struct mctp_binding *binding,
3394cdc200fSJeremy Kerr 		      mctp_eid_t eid)
3404cdc200fSJeremy Kerr {
3413e8a12a4SAndrew Jeffery 	int rc = 0;
3423e8a12a4SAndrew Jeffery 
3437520cec0SJeremy Kerr 	/* todo: multiple busses */
344722d0db2SMatt Johnston 	static_assert(MCTP_MAX_BUSSES >= 1, "need a bus");
3451a4ec3cdSJeremy Kerr 	assert(mctp->n_busses == 0);
3461a4ec3cdSJeremy Kerr 	mctp->n_busses = 1;
3473e8a12a4SAndrew Jeffery 
3484a09e1dcSMatt Johnston 	assert(binding->tx_storage);
3494a09e1dcSMatt Johnston 
35062d7236fSJames Feist 	memset(mctp->busses, 0, sizeof(struct mctp_bus));
3514a09e1dcSMatt Johnston 	mctp->busses[0].mctp = mctp;
3524cdc200fSJeremy Kerr 	mctp->busses[0].binding = binding;
3534cdc200fSJeremy Kerr 	mctp->busses[0].eid = eid;
3547520cec0SJeremy Kerr 	binding->bus = &mctp->busses[0];
3550a00dca2SJeremy Kerr 	binding->mctp = mctp;
3561a4ec3cdSJeremy Kerr 	mctp->route_policy = ROUTE_ENDPOINT;
3573b36d17cSJeremy Kerr 
3583e8a12a4SAndrew Jeffery 	if (binding->start) {
3593e8a12a4SAndrew Jeffery 		rc = binding->start(binding);
3603e8a12a4SAndrew Jeffery 		if (rc < 0) {
3613e8a12a4SAndrew Jeffery 			mctp_prerr("Failed to start binding: %d", rc);
36219275230SAndrew Jeffery 			binding->bus = NULL;
3632304c833SAndrew Jeffery 			mctp->n_busses = 0;
3643e8a12a4SAndrew Jeffery 		}
3653e8a12a4SAndrew Jeffery 	}
3663b36d17cSJeremy Kerr 
3673e8a12a4SAndrew Jeffery 	return rc;
3684cdc200fSJeremy Kerr }
3694cdc200fSJeremy Kerr 
mctp_bus_set_eid(struct mctp_binding * binding,mctp_eid_t eid)3704058b2cbSMatt Johnston int mctp_bus_set_eid(struct mctp_binding *binding, mctp_eid_t eid)
3714058b2cbSMatt Johnston {
3724058b2cbSMatt Johnston 	if (eid < 8 || eid == 0xff) {
3734058b2cbSMatt Johnston 		return -EINVAL;
3744058b2cbSMatt Johnston 	}
3754058b2cbSMatt Johnston 
3764058b2cbSMatt Johnston 	binding->bus->eid = eid;
3774058b2cbSMatt Johnston 	return 0;
3784058b2cbSMatt Johnston }
3794058b2cbSMatt Johnston 
mctp_unregister_bus(struct mctp * mctp,struct mctp_binding * binding)3802094c3c0SAndrew Jeffery void mctp_unregister_bus(struct mctp *mctp, struct mctp_binding *binding)
3812094c3c0SAndrew Jeffery {
3822094c3c0SAndrew Jeffery 	/*
3832094c3c0SAndrew Jeffery 	 * We only support one bus right now; once the call completes we will
3842094c3c0SAndrew Jeffery 	 * have no more busses
3852094c3c0SAndrew Jeffery 	 */
3862094c3c0SAndrew Jeffery 	mctp->n_busses = 0;
3872094c3c0SAndrew Jeffery 	binding->mctp = NULL;
3882094c3c0SAndrew Jeffery 	binding->bus = NULL;
3892094c3c0SAndrew Jeffery }
3902094c3c0SAndrew Jeffery 
mctp_bridge_busses(struct mctp * mctp,struct mctp_binding * b1,struct mctp_binding * b2)391a721c2d8SPatrick Williams int mctp_bridge_busses(struct mctp *mctp, struct mctp_binding *b1,
392a721c2d8SPatrick Williams 		       struct mctp_binding *b2)
3931a4ec3cdSJeremy Kerr {
39419275230SAndrew Jeffery 	int rc = 0;
39519275230SAndrew Jeffery 
3964a09e1dcSMatt Johnston 	assert(b1->tx_storage);
3974a09e1dcSMatt Johnston 	assert(b2->tx_storage);
3984a09e1dcSMatt Johnston 
3991a4ec3cdSJeremy Kerr 	assert(mctp->n_busses == 0);
400722d0db2SMatt Johnston 	assert(MCTP_MAX_BUSSES >= 2);
40162d7236fSJames Feist 	memset(mctp->busses, 0, 2 * sizeof(struct mctp_bus));
4021a4ec3cdSJeremy Kerr 	mctp->n_busses = 2;
4031a4ec3cdSJeremy Kerr 	mctp->busses[0].binding = b1;
4041a4ec3cdSJeremy Kerr 	b1->bus = &mctp->busses[0];
4051a4ec3cdSJeremy Kerr 	b1->mctp = mctp;
4061a4ec3cdSJeremy Kerr 	mctp->busses[1].binding = b2;
4071a4ec3cdSJeremy Kerr 	b2->bus = &mctp->busses[1];
4081a4ec3cdSJeremy Kerr 	b2->mctp = mctp;
4091a4ec3cdSJeremy Kerr 
4101a4ec3cdSJeremy Kerr 	mctp->route_policy = ROUTE_BRIDGE;
4113b36d17cSJeremy Kerr 
41219275230SAndrew Jeffery 	if (b1->start) {
41319275230SAndrew Jeffery 		rc = b1->start(b1);
41419275230SAndrew Jeffery 		if (rc < 0) {
41519275230SAndrew Jeffery 			mctp_prerr("Failed to start bridged bus %s: %d",
41619275230SAndrew Jeffery 				   b1->name, rc);
41719275230SAndrew Jeffery 			goto done;
41819275230SAndrew Jeffery 		}
41919275230SAndrew Jeffery 	}
4203b36d17cSJeremy Kerr 
42119275230SAndrew Jeffery 	if (b2->start) {
42219275230SAndrew Jeffery 		rc = b2->start(b2);
42319275230SAndrew Jeffery 		if (rc < 0) {
42419275230SAndrew Jeffery 			mctp_prerr("Failed to start bridged bus %s: %d",
42519275230SAndrew Jeffery 				   b2->name, rc);
42619275230SAndrew Jeffery 			goto done;
42719275230SAndrew Jeffery 		}
42819275230SAndrew Jeffery 	}
4293b36d17cSJeremy Kerr 
43019275230SAndrew Jeffery done:
43119275230SAndrew Jeffery 	return rc;
4321a4ec3cdSJeremy Kerr }
4331a4ec3cdSJeremy Kerr 
mctp_ctrl_cmd_is_transport(struct mctp_ctrl_msg_hdr * hdr)434ba6727e6SWiktor Gołgowski static inline bool mctp_ctrl_cmd_is_transport(struct mctp_ctrl_msg_hdr *hdr)
4351a4ec3cdSJeremy Kerr {
4363ef47785SMatt Johnston #pragma GCC diagnostic push
4373ef47785SMatt Johnston #pragma GCC diagnostic ignored "-Wtype-limits"
438ba6727e6SWiktor Gołgowski 	return ((hdr->command_code >= MCTP_CTRL_CMD_FIRST_TRANSPORT) &&
439ba6727e6SWiktor Gołgowski 		(hdr->command_code <= MCTP_CTRL_CMD_LAST_TRANSPORT));
4403ef47785SMatt Johnston #pragma GCC diagnostic pop
441ba6727e6SWiktor Gołgowski }
442ba6727e6SWiktor Gołgowski 
mctp_ctrl_handle_msg(struct mctp_bus * bus,mctp_eid_t src,uint8_t msg_tag,bool tag_owner,void * buffer,size_t length)443b93b6112SAndrew Jeffery static bool mctp_ctrl_handle_msg(struct mctp_bus *bus, mctp_eid_t src,
444f39c3857SSumanth Bhat 				 uint8_t msg_tag, bool tag_owner, void *buffer,
445f39c3857SSumanth Bhat 				 size_t length)
446ba6727e6SWiktor Gołgowski {
447ba6727e6SWiktor Gołgowski 	struct mctp_ctrl_msg_hdr *msg_hdr = buffer;
448ba6727e6SWiktor Gołgowski 
449ba6727e6SWiktor Gołgowski 	/*
450ba6727e6SWiktor Gołgowski 	 * Control message is received. If a transport control message handler
451ba6727e6SWiktor Gołgowski 	 * is provided, it will called. If there is no dedicated handler, this
452ba6727e6SWiktor Gołgowski 	 * function returns false and data can be handled by the generic
453ba6727e6SWiktor Gołgowski 	 * message handler. The transport control message handler will be
454ba6727e6SWiktor Gołgowski 	 * provided with messages in the command range 0xF0 - 0xFF.
455ba6727e6SWiktor Gołgowski 	 */
456ba6727e6SWiktor Gołgowski 	if (mctp_ctrl_cmd_is_transport(msg_hdr)) {
457ba6727e6SWiktor Gołgowski 		if (bus->binding->control_rx != NULL) {
458ba6727e6SWiktor Gołgowski 			/* MCTP bus binding handler */
459f39c3857SSumanth Bhat 			bus->binding->control_rx(src, msg_tag, tag_owner,
460ba6727e6SWiktor Gołgowski 						 bus->binding->control_rx_data,
461ba6727e6SWiktor Gołgowski 						 buffer, length);
462ba6727e6SWiktor Gołgowski 			return true;
463ba6727e6SWiktor Gołgowski 		}
4644058b2cbSMatt Johnston 	} else {
4654058b2cbSMatt Johnston #if MCTP_CONTROL_HANDLER
4664058b2cbSMatt Johnston 		/* libmctp will handle control requests */
4674058b2cbSMatt Johnston 		return mctp_control_handler(bus, src, tag_owner, msg_tag,
4684058b2cbSMatt Johnston 					    buffer, length);
4694058b2cbSMatt Johnston #endif
470ba6727e6SWiktor Gołgowski 	}
471ba6727e6SWiktor Gołgowski 
472ba6727e6SWiktor Gołgowski 	/*
473ba6727e6SWiktor Gołgowski 	 * Command was not handled, due to lack of specific callback.
474ba6727e6SWiktor Gołgowski 	 * It will be passed to regular message_rx handler.
475ba6727e6SWiktor Gołgowski 	 */
476ba6727e6SWiktor Gołgowski 	return false;
477ba6727e6SWiktor Gołgowski }
478ba6727e6SWiktor Gołgowski 
mctp_rx_dest_is_local(struct mctp_bus * bus,mctp_eid_t dest)479ba6727e6SWiktor Gołgowski static inline bool mctp_rx_dest_is_local(struct mctp_bus *bus, mctp_eid_t dest)
480ba6727e6SWiktor Gołgowski {
481ba6727e6SWiktor Gołgowski 	return dest == bus->eid || dest == MCTP_EID_NULL ||
482ba6727e6SWiktor Gołgowski 	       dest == MCTP_EID_BROADCAST;
483ba6727e6SWiktor Gołgowski }
484ba6727e6SWiktor Gołgowski 
mctp_ctrl_cmd_is_request(struct mctp_ctrl_msg_hdr * hdr)485ba6727e6SWiktor Gołgowski static inline bool mctp_ctrl_cmd_is_request(struct mctp_ctrl_msg_hdr *hdr)
486ba6727e6SWiktor Gołgowski {
487ba6727e6SWiktor Gołgowski 	return hdr->ic_msg_type == MCTP_CTRL_HDR_MSG_TYPE &&
488ba6727e6SWiktor Gołgowski 	       hdr->rq_dgram_inst & MCTP_CTRL_HDR_FLAG_REQUEST;
489ba6727e6SWiktor Gołgowski }
490ba6727e6SWiktor Gołgowski 
491ba6727e6SWiktor Gołgowski /*
492ba6727e6SWiktor Gołgowski  * Receive the complete MCTP message and route it.
493ba6727e6SWiktor Gołgowski  * Asserts:
494ba6727e6SWiktor Gołgowski  *     'buf' is not NULL.
495ba6727e6SWiktor Gołgowski  */
mctp_rx(struct mctp * mctp,struct mctp_bus * bus,mctp_eid_t src,mctp_eid_t dest,bool tag_owner,uint8_t msg_tag,void * buf,size_t len)496ba6727e6SWiktor Gołgowski static void mctp_rx(struct mctp *mctp, struct mctp_bus *bus, mctp_eid_t src,
497f39c3857SSumanth Bhat 		    mctp_eid_t dest, bool tag_owner, uint8_t msg_tag, void *buf,
498f39c3857SSumanth Bhat 		    size_t len)
499ba6727e6SWiktor Gołgowski {
500ba6727e6SWiktor Gołgowski 	assert(buf != NULL);
501ba6727e6SWiktor Gołgowski 
5021a4ec3cdSJeremy Kerr 	if (mctp->route_policy == ROUTE_ENDPOINT &&
503ba6727e6SWiktor Gołgowski 	    mctp_rx_dest_is_local(bus, dest)) {
50461c95992SMatt Johnston 		/* Note responses to allocated tags */
50561c95992SMatt Johnston 		if (!tag_owner) {
50661c95992SMatt Johnston 			mctp_dealloc_tag(bus, dest, src, msg_tag);
50761c95992SMatt Johnston 		}
50861c95992SMatt Johnston 
509ba6727e6SWiktor Gołgowski 		/* Handle MCTP Control Messages: */
510ba6727e6SWiktor Gołgowski 		if (len >= sizeof(struct mctp_ctrl_msg_hdr)) {
511ba6727e6SWiktor Gołgowski 			struct mctp_ctrl_msg_hdr *msg_hdr = buf;
512ba6727e6SWiktor Gołgowski 
513ba6727e6SWiktor Gołgowski 			/*
514ba6727e6SWiktor Gołgowski 			 * Identify if this is a control request message.
515ba6727e6SWiktor Gołgowski 			 * See DSP0236 v1.3.0 sec. 11.5.
516ba6727e6SWiktor Gołgowski 			 */
517ba6727e6SWiktor Gołgowski 			if (mctp_ctrl_cmd_is_request(msg_hdr)) {
518ba6727e6SWiktor Gołgowski 				bool handled;
519f39c3857SSumanth Bhat 				handled = mctp_ctrl_handle_msg(
520f39c3857SSumanth Bhat 					bus, src, msg_tag, tag_owner, buf, len);
521ba6727e6SWiktor Gołgowski 				if (handled)
522ba6727e6SWiktor Gołgowski 					return;
523ba6727e6SWiktor Gołgowski 			}
524ba6727e6SWiktor Gołgowski 		}
525f39c3857SSumanth Bhat 
526ba6727e6SWiktor Gołgowski 		if (mctp->message_rx)
527f39c3857SSumanth Bhat 			mctp->message_rx(src, tag_owner, msg_tag,
528f39c3857SSumanth Bhat 					 mctp->message_rx_data, buf, len);
529ba6727e6SWiktor Gołgowski 	}
5301a4ec3cdSJeremy Kerr 
5311a4ec3cdSJeremy Kerr 	if (mctp->route_policy == ROUTE_BRIDGE) {
5321a4ec3cdSJeremy Kerr 		int i;
5331a4ec3cdSJeremy Kerr 
5341a4ec3cdSJeremy Kerr 		for (i = 0; i < mctp->n_busses; i++) {
5351a4ec3cdSJeremy Kerr 			struct mctp_bus *dest_bus = &mctp->busses[i];
5361a4ec3cdSJeremy Kerr 			if (dest_bus == bus)
5371a4ec3cdSJeremy Kerr 				continue;
5381a4ec3cdSJeremy Kerr 
5394a09e1dcSMatt Johnston 			void *copy = mctp_msg_dup(buf, len, mctp);
5404a09e1dcSMatt Johnston 			if (!copy) {
5414a09e1dcSMatt Johnston 				return;
5424a09e1dcSMatt Johnston 			}
5434a09e1dcSMatt Johnston 
544f39c3857SSumanth Bhat 			mctp_message_tx_on_bus(dest_bus, src, dest, tag_owner,
5454a09e1dcSMatt Johnston 					       msg_tag, copy, len);
5461a4ec3cdSJeremy Kerr 		}
5471a4ec3cdSJeremy Kerr 	}
5481a4ec3cdSJeremy Kerr }
5491a4ec3cdSJeremy Kerr 
mctp_bus_rx(struct mctp_binding * binding,struct mctp_pktbuf * pkt)5500a00dca2SJeremy Kerr void mctp_bus_rx(struct mctp_binding *binding, struct mctp_pktbuf *pkt)
5514cdc200fSJeremy Kerr {
5527520cec0SJeremy Kerr 	struct mctp_bus *bus = binding->bus;
5530a00dca2SJeremy Kerr 	struct mctp *mctp = binding->mctp;
554c2def9f3SEd Tanous 	uint8_t flags, exp_seq, seq, tag;
55524db71fbSJeremy Kerr 	struct mctp_msg_ctx *ctx;
55624db71fbSJeremy Kerr 	struct mctp_hdr *hdr;
557f39c3857SSumanth Bhat 	bool tag_owner;
5584cdc200fSJeremy Kerr 	size_t len;
5594cdc200fSJeremy Kerr 	void *p;
56024db71fbSJeremy Kerr 	int rc;
5614cdc200fSJeremy Kerr 
5627520cec0SJeremy Kerr 	assert(bus);
5637520cec0SJeremy Kerr 
564d97869deSSumanth Bhat 	/* Drop packet if it was smaller than mctp hdr size */
56586e9a97aSMatt Johnston 	if (mctp_pktbuf_size(pkt) < sizeof(struct mctp_hdr))
566d97869deSSumanth Bhat 		goto out;
567d97869deSSumanth Bhat 
5685d3d4e6dSAndrew Jeffery 	if (mctp->capture)
569f2988977SRashmica Gupta 		mctp->capture(pkt, MCTP_MESSAGE_CAPTURE_INCOMING,
570f2988977SRashmica Gupta 			      mctp->capture_data);
5715d3d4e6dSAndrew Jeffery 
57224db71fbSJeremy Kerr 	hdr = mctp_pktbuf_hdr(pkt);
57324db71fbSJeremy Kerr 
57418b9a37bSMatt Johnston 	if (hdr->src == MCTP_EID_BROADCAST) {
57518b9a37bSMatt Johnston 		/* drop packets with broadcast EID src */
57618b9a37bSMatt Johnston 		goto out;
57718b9a37bSMatt Johnston 	}
57818b9a37bSMatt Johnston 
5791a4ec3cdSJeremy Kerr 	/* small optimisation: don't bother reassembly if we're going to
5801a4ec3cdSJeremy Kerr 	 * drop the packet in mctp_rx anyway */
581133df7abSJohn Chung 	if (mctp->route_policy == ROUTE_ENDPOINT &&
582133df7abSJohn Chung 	    !mctp_rx_dest_is_local(bus, hdr->dest))
583c1693af4SJeremy Kerr 		goto out;
58424db71fbSJeremy Kerr 
58524db71fbSJeremy Kerr 	flags = hdr->flags_seq_tag & (MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM);
58624db71fbSJeremy Kerr 	tag = (hdr->flags_seq_tag >> MCTP_HDR_TAG_SHIFT) & MCTP_HDR_TAG_MASK;
58724db71fbSJeremy Kerr 	seq = (hdr->flags_seq_tag >> MCTP_HDR_SEQ_SHIFT) & MCTP_HDR_SEQ_MASK;
5887f7fdc1dSAndrew Jeffery 	tag_owner = (hdr->flags_seq_tag >> MCTP_HDR_TO_SHIFT) &
5897f7fdc1dSAndrew Jeffery 		    MCTP_HDR_TO_MASK;
59024db71fbSJeremy Kerr 
59124db71fbSJeremy Kerr 	switch (flags) {
59224db71fbSJeremy Kerr 	case MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM:
59324db71fbSJeremy Kerr 		/* single-packet message - send straight up to rx function,
59424db71fbSJeremy Kerr 		 * no need to create a message context */
5954cdc200fSJeremy Kerr 		len = pkt->end - pkt->mctp_hdr_off - sizeof(struct mctp_hdr);
5964a09e1dcSMatt Johnston 		p = mctp_msg_dup(pkt->data + pkt->mctp_hdr_off +
5974a09e1dcSMatt Johnston 					 sizeof(struct mctp_hdr),
5984a09e1dcSMatt Johnston 				 len, mctp);
5994a09e1dcSMatt Johnston 		if (p) {
6004a09e1dcSMatt Johnston 			mctp_rx(mctp, bus, hdr->src, hdr->dest, tag_owner, tag,
6014a09e1dcSMatt Johnston 				p, len);
6024a09e1dcSMatt Johnston 			__mctp_msg_free(p, mctp);
6034a09e1dcSMatt Johnston 		}
60424db71fbSJeremy Kerr 		break;
60524db71fbSJeremy Kerr 
60624db71fbSJeremy Kerr 	case MCTP_HDR_FLAG_SOM:
60724db71fbSJeremy Kerr 		/* start of a new message - start the new context for
60824db71fbSJeremy Kerr 		 * future message reception. If an existing context is
60924db71fbSJeremy Kerr 		 * already present, drop it. */
6101a4ec3cdSJeremy Kerr 		ctx = mctp_msg_ctx_lookup(mctp, hdr->src, hdr->dest, tag);
61124db71fbSJeremy Kerr 		if (ctx) {
61224db71fbSJeremy Kerr 			mctp_msg_ctx_reset(ctx);
61324db71fbSJeremy Kerr 		} else {
614a721c2d8SPatrick Williams 			ctx = mctp_msg_ctx_create(mctp, hdr->src, hdr->dest,
615a721c2d8SPatrick Williams 						  tag);
61634d4c96fSSumanth Bhat 			/* If context creation fails due to exhaution of contexts we
61734d4c96fSSumanth Bhat 			* can support, drop the packet */
61834d4c96fSSumanth Bhat 			if (!ctx) {
61934d4c96fSSumanth Bhat 				mctp_prdebug("Context buffers exhausted.");
62034d4c96fSSumanth Bhat 				goto out;
62134d4c96fSSumanth Bhat 			}
62224db71fbSJeremy Kerr 		}
62324db71fbSJeremy Kerr 
62469f545f7SSumanth Bhat 		/* Save the fragment size, subsequent middle fragments
62569f545f7SSumanth Bhat 		 * should of the same size */
62669f545f7SSumanth Bhat 		ctx->fragment_size = mctp_pktbuf_size(pkt);
62769f545f7SSumanth Bhat 
6284a09e1dcSMatt Johnston 		rc = mctp_msg_ctx_add_pkt(ctx, pkt);
62924db71fbSJeremy Kerr 		if (rc) {
6304a09e1dcSMatt Johnston 			mctp_msg_ctx_drop(bus, ctx);
63124db71fbSJeremy Kerr 		} else {
63224db71fbSJeremy Kerr 			ctx->last_seq = seq;
63324db71fbSJeremy Kerr 		}
63424db71fbSJeremy Kerr 
63524db71fbSJeremy Kerr 		break;
63624db71fbSJeremy Kerr 
63724db71fbSJeremy Kerr 	case MCTP_HDR_FLAG_EOM:
6381a4ec3cdSJeremy Kerr 		ctx = mctp_msg_ctx_lookup(mctp, hdr->src, hdr->dest, tag);
63924db71fbSJeremy Kerr 		if (!ctx)
640c1693af4SJeremy Kerr 			goto out;
64124db71fbSJeremy Kerr 
642c2def9f3SEd Tanous 		exp_seq = (ctx->last_seq + 1) % 4;
643c2def9f3SEd Tanous 
644c2def9f3SEd Tanous 		if (exp_seq != seq) {
645c2def9f3SEd Tanous 			mctp_prdebug(
646c2def9f3SEd Tanous 				"Sequence number %d does not match expected %d",
647c2def9f3SEd Tanous 				seq, exp_seq);
6484a09e1dcSMatt Johnston 			mctp_msg_ctx_drop(bus, ctx);
649c1693af4SJeremy Kerr 			goto out;
65024db71fbSJeremy Kerr 		}
65124db71fbSJeremy Kerr 
65269f545f7SSumanth Bhat 		len = mctp_pktbuf_size(pkt);
65369f545f7SSumanth Bhat 
65469f545f7SSumanth Bhat 		if (len > ctx->fragment_size) {
655a721c2d8SPatrick Williams 			mctp_prdebug("Unexpected fragment size. Expected"
65669f545f7SSumanth Bhat 				     " less than %zu, received = %zu",
65769f545f7SSumanth Bhat 				     ctx->fragment_size, len);
6584a09e1dcSMatt Johnston 			mctp_msg_ctx_drop(bus, ctx);
65969f545f7SSumanth Bhat 			goto out;
66069f545f7SSumanth Bhat 		}
66169f545f7SSumanth Bhat 
6624a09e1dcSMatt Johnston 		rc = mctp_msg_ctx_add_pkt(ctx, pkt);
6631a4ec3cdSJeremy Kerr 		if (!rc)
664f39c3857SSumanth Bhat 			mctp_rx(mctp, bus, ctx->src, ctx->dest, tag_owner, tag,
66524db71fbSJeremy Kerr 				ctx->buf, ctx->buf_size);
66624db71fbSJeremy Kerr 
6674a09e1dcSMatt Johnston 		mctp_msg_ctx_drop(bus, ctx);
66824db71fbSJeremy Kerr 		break;
669c2def9f3SEd Tanous 
670c2def9f3SEd Tanous 	case 0:
671c2def9f3SEd Tanous 		/* Neither SOM nor EOM */
6721a4ec3cdSJeremy Kerr 		ctx = mctp_msg_ctx_lookup(mctp, hdr->src, hdr->dest, tag);
673c2def9f3SEd Tanous 		if (!ctx)
674c1693af4SJeremy Kerr 			goto out;
675c2def9f3SEd Tanous 
676c2def9f3SEd Tanous 		exp_seq = (ctx->last_seq + 1) % 4;
677c2def9f3SEd Tanous 		if (exp_seq != seq) {
678c2def9f3SEd Tanous 			mctp_prdebug(
679c2def9f3SEd Tanous 				"Sequence number %d does not match expected %d",
680c2def9f3SEd Tanous 				seq, exp_seq);
6814a09e1dcSMatt Johnston 			mctp_msg_ctx_drop(bus, ctx);
682c1693af4SJeremy Kerr 			goto out;
683c2def9f3SEd Tanous 		}
684c2def9f3SEd Tanous 
68569f545f7SSumanth Bhat 		len = mctp_pktbuf_size(pkt);
68669f545f7SSumanth Bhat 
68769f545f7SSumanth Bhat 		if (len != ctx->fragment_size) {
688a721c2d8SPatrick Williams 			mctp_prdebug("Unexpected fragment size. Expected = %zu "
689a721c2d8SPatrick Williams 				     "received = %zu",
690a721c2d8SPatrick Williams 				     ctx->fragment_size, len);
6914a09e1dcSMatt Johnston 			mctp_msg_ctx_drop(bus, ctx);
69269f545f7SSumanth Bhat 			goto out;
69369f545f7SSumanth Bhat 		}
69469f545f7SSumanth Bhat 
6954a09e1dcSMatt Johnston 		rc = mctp_msg_ctx_add_pkt(ctx, pkt);
696c2def9f3SEd Tanous 		if (rc) {
6974a09e1dcSMatt Johnston 			mctp_msg_ctx_drop(bus, ctx);
698c1693af4SJeremy Kerr 			goto out;
699c2def9f3SEd Tanous 		}
700c2def9f3SEd Tanous 		ctx->last_seq = seq;
701c2def9f3SEd Tanous 
702c2def9f3SEd Tanous 		break;
70324db71fbSJeremy Kerr 	}
704c1693af4SJeremy Kerr out:
7054a09e1dcSMatt Johnston 	return;
7064cdc200fSJeremy Kerr }
7074cdc200fSJeremy Kerr 
mctp_packet_tx(struct mctp_bus * bus,struct mctp_pktbuf * pkt)708a721c2d8SPatrick Williams static int mctp_packet_tx(struct mctp_bus *bus, struct mctp_pktbuf *pkt)
7094cdc200fSJeremy Kerr {
7105d3d4e6dSAndrew Jeffery 	struct mctp *mctp = bus->binding->mctp;
7115d3d4e6dSAndrew Jeffery 
7124a09e1dcSMatt Johnston 	if (bus->state != mctp_bus_state_tx_enabled) {
7134a09e1dcSMatt Johnston 		mctp_prdebug("tx with bus disabled");
7141cd31184SJeremy Kerr 		return -1;
7154a09e1dcSMatt Johnston 	}
7161cd31184SJeremy Kerr 
7175d3d4e6dSAndrew Jeffery 	if (mctp->capture)
718f2988977SRashmica Gupta 		mctp->capture(pkt, MCTP_MESSAGE_CAPTURE_OUTGOING,
719f2988977SRashmica Gupta 			      mctp->capture_data);
7205d3d4e6dSAndrew Jeffery 
7214cdc200fSJeremy Kerr 	return bus->binding->tx(bus->binding, pkt);
7224cdc200fSJeremy Kerr }
7234cdc200fSJeremy Kerr 
7244a09e1dcSMatt Johnston /* Returns a pointer to the binding's tx_storage */
mctp_next_tx_pkt(struct mctp_bus * bus)7254a09e1dcSMatt Johnston static struct mctp_pktbuf *mctp_next_tx_pkt(struct mctp_bus *bus)
7264a09e1dcSMatt Johnston {
7274a09e1dcSMatt Johnston 	if (!bus->tx_msg) {
7284a09e1dcSMatt Johnston 		return NULL;
7294a09e1dcSMatt Johnston 	}
7304a09e1dcSMatt Johnston 
7314a09e1dcSMatt Johnston 	size_t p = bus->tx_msgpos;
7324a09e1dcSMatt Johnston 	size_t msg_len = bus->tx_msglen;
7334a09e1dcSMatt Johnston 	size_t payload_len = msg_len - p;
7344a09e1dcSMatt Johnston 	size_t max_payload_len = MCTP_BODY_SIZE(bus->binding->pkt_size);
7354a09e1dcSMatt Johnston 	if (payload_len > max_payload_len)
7364a09e1dcSMatt Johnston 		payload_len = max_payload_len;
7374a09e1dcSMatt Johnston 
7384a09e1dcSMatt Johnston 	struct mctp_pktbuf *pkt =
7394a09e1dcSMatt Johnston 		mctp_pktbuf_init(bus->binding, bus->binding->tx_storage);
7404a09e1dcSMatt Johnston 	struct mctp_hdr *hdr = mctp_pktbuf_hdr(pkt);
7414a09e1dcSMatt Johnston 
7424a09e1dcSMatt Johnston 	hdr->ver = bus->binding->version & 0xf;
7434a09e1dcSMatt Johnston 	hdr->dest = bus->tx_dest;
7444a09e1dcSMatt Johnston 	hdr->src = bus->tx_src;
7454a09e1dcSMatt Johnston 	hdr->flags_seq_tag = (bus->tx_to << MCTP_HDR_TO_SHIFT) |
7464a09e1dcSMatt Johnston 			     (bus->tx_tag << MCTP_HDR_TAG_SHIFT);
7474a09e1dcSMatt Johnston 
7484a09e1dcSMatt Johnston 	if (p == 0)
7494a09e1dcSMatt Johnston 		hdr->flags_seq_tag |= MCTP_HDR_FLAG_SOM;
7504a09e1dcSMatt Johnston 	if (p + payload_len >= msg_len)
7514a09e1dcSMatt Johnston 		hdr->flags_seq_tag |= MCTP_HDR_FLAG_EOM;
7524a09e1dcSMatt Johnston 	hdr->flags_seq_tag |= bus->tx_seq << MCTP_HDR_SEQ_SHIFT;
7534a09e1dcSMatt Johnston 
7544a09e1dcSMatt Johnston 	memcpy(mctp_pktbuf_data(pkt), (uint8_t *)bus->tx_msg + p, payload_len);
7554a09e1dcSMatt Johnston 	pkt->end = pkt->start + sizeof(*hdr) + payload_len;
7564a09e1dcSMatt Johnston 	bus->tx_pktlen = payload_len;
7574a09e1dcSMatt Johnston 
7584a09e1dcSMatt Johnston 	mctp_prdebug(
7594a09e1dcSMatt Johnston 		"tx dst %d tag %d payload len %zu seq %d. msg pos %zu len %zu",
7604a09e1dcSMatt Johnston 		hdr->dest, bus->tx_tag, payload_len, bus->tx_seq, p, msg_len);
7614a09e1dcSMatt Johnston 
7624a09e1dcSMatt Johnston 	return pkt;
7634a09e1dcSMatt Johnston }
7644a09e1dcSMatt Johnston 
7654a09e1dcSMatt Johnston /* Called when a packet has successfully been sent */
mctp_tx_complete(struct mctp_bus * bus)7664a09e1dcSMatt Johnston static void mctp_tx_complete(struct mctp_bus *bus)
7674a09e1dcSMatt Johnston {
7684a09e1dcSMatt Johnston 	if (!bus->tx_msg) {
7694a09e1dcSMatt Johnston 		mctp_prdebug("tx complete no message");
7704a09e1dcSMatt Johnston 		return;
7714a09e1dcSMatt Johnston 	}
7724a09e1dcSMatt Johnston 
7734a09e1dcSMatt Johnston 	bus->tx_seq = (bus->tx_seq + 1) & MCTP_HDR_SEQ_MASK;
7744a09e1dcSMatt Johnston 	bus->tx_msgpos += bus->tx_pktlen;
7754a09e1dcSMatt Johnston 
7764a09e1dcSMatt Johnston 	if (bus->tx_msgpos >= bus->tx_msglen) {
7774a09e1dcSMatt Johnston 		__mctp_msg_free(bus->tx_msg, bus->binding->mctp);
7784a09e1dcSMatt Johnston 		bus->tx_msg = NULL;
7794a09e1dcSMatt Johnston 	}
7804a09e1dcSMatt Johnston }
7814a09e1dcSMatt Johnston 
mctp_send_tx_queue(struct mctp_bus * bus)782cc2458d9SJeremy Kerr static void mctp_send_tx_queue(struct mctp_bus *bus)
7831cd31184SJeremy Kerr {
7841cd31184SJeremy Kerr 	struct mctp_pktbuf *pkt;
7851cd31184SJeremy Kerr 
7864a09e1dcSMatt Johnston 	while (bus->tx_msg && bus->state == mctp_bus_state_tx_enabled) {
7871cd31184SJeremy Kerr 		int rc;
7881cd31184SJeremy Kerr 
7894a09e1dcSMatt Johnston 		pkt = mctp_next_tx_pkt(bus);
7904a09e1dcSMatt Johnston 
7911cd31184SJeremy Kerr 		rc = mctp_packet_tx(bus, pkt);
7920721f585SAndrew Jeffery 		switch (rc) {
7934a09e1dcSMatt Johnston 		/* If transmission succeded */
7940721f585SAndrew Jeffery 		case 0:
7950721f585SAndrew Jeffery 			/* Drop the packet */
7964a09e1dcSMatt Johnston 			mctp_tx_complete(bus);
7970721f585SAndrew Jeffery 			break;
7980721f585SAndrew Jeffery 
7994a09e1dcSMatt Johnston 		/* If the binding was busy */
8000721f585SAndrew Jeffery 		case -EBUSY:
8014a09e1dcSMatt Johnston 			/* Keep the packet for next try */
8024a09e1dcSMatt Johnston 			mctp_prdebug("tx EBUSY");
8034a09e1dcSMatt Johnston 			return;
8044a09e1dcSMatt Johnston 
8050721f585SAndrew Jeffery 		/* Some other unknown error occurred */
8060721f585SAndrew Jeffery 		default:
8074a09e1dcSMatt Johnston 			/* Drop the packet */
8084a09e1dcSMatt Johnston 			mctp_prdebug("tx drop %d", rc);
8094a09e1dcSMatt Johnston 			mctp_tx_complete(bus);
8104a09e1dcSMatt Johnston 			return;
8110721f585SAndrew Jeffery 		};
8121cd31184SJeremy Kerr 	}
8131cd31184SJeremy Kerr }
8141cd31184SJeremy Kerr 
mctp_binding_set_tx_enabled(struct mctp_binding * binding,bool enable)8151cd31184SJeremy Kerr void mctp_binding_set_tx_enabled(struct mctp_binding *binding, bool enable)
8161cd31184SJeremy Kerr {
8171cd31184SJeremy Kerr 	struct mctp_bus *bus = binding->bus;
818c61501ccSAndrew Jeffery 
819c61501ccSAndrew Jeffery 	switch (bus->state) {
820c61501ccSAndrew Jeffery 	case mctp_bus_state_constructed:
821c61501ccSAndrew Jeffery 		if (!enable)
822c61501ccSAndrew Jeffery 			return;
823c61501ccSAndrew Jeffery 
8241fa707e2SAndrew Jeffery 		if (binding->pkt_size < MCTP_PACKET_SIZE(MCTP_BTU)) {
825a721c2d8SPatrick Williams 			mctp_prerr(
826a721c2d8SPatrick Williams 				"Cannot start %s binding with invalid MTU: %zu",
8271fa707e2SAndrew Jeffery 				binding->name,
8281fa707e2SAndrew Jeffery 				MCTP_BODY_SIZE(binding->pkt_size));
8291fa707e2SAndrew Jeffery 			return;
8301fa707e2SAndrew Jeffery 		}
8311fa707e2SAndrew Jeffery 
832c61501ccSAndrew Jeffery 		bus->state = mctp_bus_state_tx_enabled;
833c61501ccSAndrew Jeffery 		mctp_prinfo("%s binding started", binding->name);
834c61501ccSAndrew Jeffery 		return;
835c61501ccSAndrew Jeffery 	case mctp_bus_state_tx_enabled:
8361cd31184SJeremy Kerr 		if (enable)
837c61501ccSAndrew Jeffery 			return;
838c61501ccSAndrew Jeffery 
839c61501ccSAndrew Jeffery 		bus->state = mctp_bus_state_tx_disabled;
840c61501ccSAndrew Jeffery 		mctp_prdebug("%s binding Tx disabled", binding->name);
841c61501ccSAndrew Jeffery 		return;
842c61501ccSAndrew Jeffery 	case mctp_bus_state_tx_disabled:
843c61501ccSAndrew Jeffery 		if (!enable)
844c61501ccSAndrew Jeffery 			return;
845c61501ccSAndrew Jeffery 
846c61501ccSAndrew Jeffery 		bus->state = mctp_bus_state_tx_enabled;
847c61501ccSAndrew Jeffery 		mctp_prdebug("%s binding Tx enabled", binding->name);
848cc2458d9SJeremy Kerr 		mctp_send_tx_queue(bus);
849c61501ccSAndrew Jeffery 		return;
850c61501ccSAndrew Jeffery 	}
8511cd31184SJeremy Kerr }
8521cd31184SJeremy Kerr 
mctp_message_tx_on_bus(struct mctp_bus * bus,mctp_eid_t src,mctp_eid_t dest,bool tag_owner,uint8_t msg_tag,void * msg,size_t msg_len)853b93b6112SAndrew Jeffery static int mctp_message_tx_on_bus(struct mctp_bus *bus, mctp_eid_t src,
854f39c3857SSumanth Bhat 				  mctp_eid_t dest, bool tag_owner,
855f39c3857SSumanth Bhat 				  uint8_t msg_tag, void *msg, size_t msg_len)
8564cdc200fSJeremy Kerr {
8574a09e1dcSMatt Johnston 	size_t max_payload_len;
8584a09e1dcSMatt Johnston 	int rc;
8594cdc200fSJeremy Kerr 
8604a09e1dcSMatt Johnston 	if (bus->state == mctp_bus_state_constructed) {
8614a09e1dcSMatt Johnston 		rc = -ENXIO;
8624a09e1dcSMatt Johnston 		goto err;
8634a09e1dcSMatt Johnston 	}
864c61501ccSAndrew Jeffery 
8654a09e1dcSMatt Johnston 	if ((msg_tag & MCTP_HDR_TAG_MASK) != msg_tag) {
8664a09e1dcSMatt Johnston 		rc = -EINVAL;
8674a09e1dcSMatt Johnston 		goto err;
8684a09e1dcSMatt Johnston 	}
869f39c3857SSumanth Bhat 
8701fa707e2SAndrew Jeffery 	max_payload_len = MCTP_BODY_SIZE(bus->binding->pkt_size);
8711fa707e2SAndrew Jeffery 
8721fa707e2SAndrew Jeffery 	{
8731fa707e2SAndrew Jeffery 		const bool valid_mtu = max_payload_len >= MCTP_BTU;
8741fa707e2SAndrew Jeffery 		assert(valid_mtu);
8754a09e1dcSMatt Johnston 		if (!valid_mtu) {
8764a09e1dcSMatt Johnston 			rc = -EINVAL;
8774a09e1dcSMatt Johnston 			goto err;
8784a09e1dcSMatt Johnston 		}
8791fa707e2SAndrew Jeffery 	}
8804cdc200fSJeremy Kerr 
881a721c2d8SPatrick Williams 	mctp_prdebug(
882a721c2d8SPatrick Williams 		"%s: Generating packets for transmission of %zu byte message from %hhu to %hhu",
883298865fcSAndrew Jeffery 		__func__, msg_len, src, dest);
884298865fcSAndrew Jeffery 
8854a09e1dcSMatt Johnston 	if (bus->tx_msg) {
8864a09e1dcSMatt Johnston 		mctp_prdebug("Bus busy");
8874a09e1dcSMatt Johnston 		rc = -EBUSY;
8884a09e1dcSMatt Johnston 		goto err;
88924db71fbSJeremy Kerr 	}
89024db71fbSJeremy Kerr 
8914a09e1dcSMatt Johnston 	/* Take the message to send */
8924a09e1dcSMatt Johnston 	bus->tx_msg = msg;
8934a09e1dcSMatt Johnston 	bus->tx_msglen = msg_len;
8944a09e1dcSMatt Johnston 	bus->tx_msgpos = 0;
8954a09e1dcSMatt Johnston 	/* bus->tx_seq is allowed to continue from previous message */
8964a09e1dcSMatt Johnston 	bus->tx_src = src;
8974a09e1dcSMatt Johnston 	bus->tx_dest = dest;
8984a09e1dcSMatt Johnston 	bus->tx_to = tag_owner;
8994a09e1dcSMatt Johnston 	bus->tx_tag = msg_tag;
900298865fcSAndrew Jeffery 
901cc2458d9SJeremy Kerr 	mctp_send_tx_queue(bus);
90224db71fbSJeremy Kerr 	return 0;
9034a09e1dcSMatt Johnston 
9044a09e1dcSMatt Johnston err:
9054a09e1dcSMatt Johnston 	__mctp_msg_free(msg, bus->binding->mctp);
9064a09e1dcSMatt Johnston 	return rc;
9074cdc200fSJeremy Kerr }
9081a4ec3cdSJeremy Kerr 
mctp_message_tx_alloced(struct mctp * mctp,mctp_eid_t eid,bool tag_owner,uint8_t msg_tag,void * msg,size_t msg_len)9094a09e1dcSMatt Johnston int mctp_message_tx_alloced(struct mctp *mctp, mctp_eid_t eid, bool tag_owner,
910f39c3857SSumanth Bhat 			    uint8_t msg_tag, void *msg, size_t msg_len)
9111a4ec3cdSJeremy Kerr {
9121a4ec3cdSJeremy Kerr 	struct mctp_bus *bus;
9131a4ec3cdSJeremy Kerr 
914f39c3857SSumanth Bhat 	/* TODO: Protect against same tag being used across
915f39c3857SSumanth Bhat 	 * different callers */
916f39c3857SSumanth Bhat 	if ((msg_tag & MCTP_HDR_TAG_MASK) != msg_tag) {
917f39c3857SSumanth Bhat 		mctp_prerr("Incorrect message tag %u passed.", msg_tag);
9184a09e1dcSMatt Johnston 		__mctp_msg_free(msg, mctp);
919f39c3857SSumanth Bhat 		return -EINVAL;
920f39c3857SSumanth Bhat 	}
921f39c3857SSumanth Bhat 
9221a4ec3cdSJeremy Kerr 	bus = find_bus_for_eid(mctp, eid);
9234a09e1dcSMatt Johnston 	if (!bus) {
9244a09e1dcSMatt Johnston 		__mctp_msg_free(msg, mctp);
925663ec39eSBrad Bishop 		return 0;
9264a09e1dcSMatt Johnston 	}
927663ec39eSBrad Bishop 
928f39c3857SSumanth Bhat 	return mctp_message_tx_on_bus(bus, bus->eid, eid, tag_owner, msg_tag,
929f39c3857SSumanth Bhat 				      msg, msg_len);
9301a4ec3cdSJeremy Kerr }
9314a09e1dcSMatt Johnston 
mctp_message_tx(struct mctp * mctp,mctp_eid_t eid,bool tag_owner,uint8_t msg_tag,const void * msg,size_t msg_len)9324a09e1dcSMatt Johnston int mctp_message_tx(struct mctp *mctp, mctp_eid_t eid, bool tag_owner,
9334a09e1dcSMatt Johnston 		    uint8_t msg_tag, const void *msg, size_t msg_len)
9344a09e1dcSMatt Johnston {
9354a09e1dcSMatt Johnston 	void *copy = mctp_msg_dup(msg, msg_len, mctp);
9364a09e1dcSMatt Johnston 	if (!copy) {
9374a09e1dcSMatt Johnston 		return -ENOMEM;
9384a09e1dcSMatt Johnston 	}
9394a09e1dcSMatt Johnston 
9404a09e1dcSMatt Johnston 	return mctp_message_tx_alloced(mctp, eid, tag_owner, msg_tag, copy,
9414a09e1dcSMatt Johnston 				       msg_len);
9424a09e1dcSMatt Johnston }
9434a09e1dcSMatt Johnston 
mctp_set_now_op(struct mctp * mctp,uint64_t (* now)(void *),void * ctx)94444e64dfaSMatt Johnston void mctp_set_now_op(struct mctp *mctp, uint64_t (*now)(void *), void *ctx)
94544e64dfaSMatt Johnston {
94644e64dfaSMatt Johnston 	assert(now);
94744e64dfaSMatt Johnston 	mctp->platform_now = now;
94844e64dfaSMatt Johnston 	mctp->platform_now_ctx = ctx;
94944e64dfaSMatt Johnston }
95044e64dfaSMatt Johnston 
mctp_now(struct mctp * mctp)95144e64dfaSMatt Johnston uint64_t mctp_now(struct mctp *mctp)
95244e64dfaSMatt Johnston {
95344e64dfaSMatt Johnston 	assert(mctp->platform_now);
95444e64dfaSMatt Johnston 	return mctp->platform_now(mctp->platform_now_ctx);
95544e64dfaSMatt Johnston }
95644e64dfaSMatt Johnston 
mctp_dealloc_tag(struct mctp_bus * bus,mctp_eid_t local,mctp_eid_t remote,uint8_t tag)95761c95992SMatt Johnston static void mctp_dealloc_tag(struct mctp_bus *bus, mctp_eid_t local,
95861c95992SMatt Johnston 			     mctp_eid_t remote, uint8_t tag)
95961c95992SMatt Johnston {
96061c95992SMatt Johnston 	struct mctp *mctp = bus->binding->mctp;
96144e64dfaSMatt Johnston 	if (local == 0) {
96261c95992SMatt Johnston 		return;
96361c95992SMatt Johnston 	}
96461c95992SMatt Johnston 
96561c95992SMatt Johnston 	for (size_t i = 0; i < ARRAY_SIZE(mctp->req_tags); i++) {
96661c95992SMatt Johnston 		struct mctp_req_tag *r = &mctp->req_tags[i];
96761c95992SMatt Johnston 		if (r->local == local && r->remote == remote && r->tag == tag) {
96861c95992SMatt Johnston 			r->local = 0;
96961c95992SMatt Johnston 			r->remote = 0;
97061c95992SMatt Johnston 			r->tag = 0;
97144e64dfaSMatt Johnston 			r->expiry = 0;
97261c95992SMatt Johnston 			return;
97361c95992SMatt Johnston 		}
97461c95992SMatt Johnston 	}
97561c95992SMatt Johnston }
97661c95992SMatt Johnston 
mctp_alloc_tag(struct mctp * mctp,mctp_eid_t local,mctp_eid_t remote,uint8_t * ret_tag)97761c95992SMatt Johnston static int mctp_alloc_tag(struct mctp *mctp, mctp_eid_t local,
97861c95992SMatt Johnston 			  mctp_eid_t remote, uint8_t *ret_tag)
97961c95992SMatt Johnston {
98061c95992SMatt Johnston 	assert(local != 0);
98144e64dfaSMatt Johnston 	uint64_t now = mctp_now(mctp);
98261c95992SMatt Johnston 
98361c95992SMatt Johnston 	uint8_t used = 0;
98461c95992SMatt Johnston 	struct mctp_req_tag *spare = NULL;
98561c95992SMatt Johnston 	/* Find which tags and slots are used/spare */
98661c95992SMatt Johnston 	for (size_t i = 0; i < ARRAY_SIZE(mctp->req_tags); i++) {
98761c95992SMatt Johnston 		struct mctp_req_tag *r = &mctp->req_tags[i];
98844e64dfaSMatt Johnston 		if (r->local == 0 || r->expiry < now) {
98961c95992SMatt Johnston 			spare = r;
99061c95992SMatt Johnston 		} else {
99161c95992SMatt Johnston 			if (r->local == local && r->remote == remote) {
99261c95992SMatt Johnston 				used |= 1 << r->tag;
99361c95992SMatt Johnston 			}
99461c95992SMatt Johnston 		}
99561c95992SMatt Johnston 	}
99661c95992SMatt Johnston 
99761c95992SMatt Johnston 	if (spare == NULL) {
99861c95992SMatt Johnston 		// All req_tag slots are in-use
99961c95992SMatt Johnston 		return -EBUSY;
100061c95992SMatt Johnston 	}
100161c95992SMatt Johnston 
100261c95992SMatt Johnston 	for (uint8_t t = 0; t < 8; t++) {
100361c95992SMatt Johnston 		uint8_t tag = (t + mctp->tag_round_robin) % 8;
100461c95992SMatt Johnston 		if ((used & 1 << tag) == 0) {
100561c95992SMatt Johnston 			spare->local = local;
100661c95992SMatt Johnston 			spare->remote = remote;
100761c95992SMatt Johnston 			spare->tag = tag;
100844e64dfaSMatt Johnston 			spare->expiry = now + MCTP_TAG_TIMEOUT;
100961c95992SMatt Johnston 			*ret_tag = tag;
101061c95992SMatt Johnston 			mctp->tag_round_robin = (tag + 1) % 8;
101161c95992SMatt Johnston 			return 0;
101261c95992SMatt Johnston 		}
101361c95992SMatt Johnston 	}
101461c95992SMatt Johnston 
101561c95992SMatt Johnston 	// All 8 tags are used for this src/dest pair
101661c95992SMatt Johnston 	return -EBUSY;
101761c95992SMatt Johnston }
101861c95992SMatt Johnston 
mctp_message_tx_request(struct mctp * mctp,mctp_eid_t eid,void * msg,size_t msg_len,uint8_t * ret_alloc_msg_tag)101961c95992SMatt Johnston int mctp_message_tx_request(struct mctp *mctp, mctp_eid_t eid, void *msg,
102061c95992SMatt Johnston 			    size_t msg_len, uint8_t *ret_alloc_msg_tag)
102161c95992SMatt Johnston {
102261c95992SMatt Johnston 	int rc;
102361c95992SMatt Johnston 	struct mctp_bus *bus;
102461c95992SMatt Johnston 
102561c95992SMatt Johnston 	bus = find_bus_for_eid(mctp, eid);
102661c95992SMatt Johnston 	if (!bus) {
102761c95992SMatt Johnston 		__mctp_msg_free(msg, mctp);
102861c95992SMatt Johnston 		return 0;
102961c95992SMatt Johnston 	}
103061c95992SMatt Johnston 
103161c95992SMatt Johnston 	uint8_t alloc_tag;
103261c95992SMatt Johnston 	rc = mctp_alloc_tag(mctp, bus->eid, eid, &alloc_tag);
103361c95992SMatt Johnston 	if (rc) {
103461c95992SMatt Johnston 		mctp_prdebug("Failed allocating tag");
103561c95992SMatt Johnston 		__mctp_msg_free(msg, mctp);
103661c95992SMatt Johnston 		return rc;
103761c95992SMatt Johnston 	}
103861c95992SMatt Johnston 
103961c95992SMatt Johnston 	if (ret_alloc_msg_tag) {
104061c95992SMatt Johnston 		*ret_alloc_msg_tag = alloc_tag;
104161c95992SMatt Johnston 	}
104261c95992SMatt Johnston 
104361c95992SMatt Johnston 	return mctp_message_tx_alloced(mctp, eid, true, alloc_tag, msg,
104461c95992SMatt Johnston 				       msg_len);
104561c95992SMatt Johnston }
104661c95992SMatt Johnston 
mctp_is_tx_ready(struct mctp * mctp,mctp_eid_t eid)10474a09e1dcSMatt Johnston bool mctp_is_tx_ready(struct mctp *mctp, mctp_eid_t eid)
10484a09e1dcSMatt Johnston {
10494a09e1dcSMatt Johnston 	struct mctp_bus *bus;
10504a09e1dcSMatt Johnston 
10514a09e1dcSMatt Johnston 	bus = find_bus_for_eid(mctp, eid);
10524a09e1dcSMatt Johnston 	if (!bus) {
10534a09e1dcSMatt Johnston 		return true;
10544a09e1dcSMatt Johnston 	}
10554a09e1dcSMatt Johnston 	return bus->tx_msg == NULL;
10564a09e1dcSMatt Johnston }
10574a09e1dcSMatt Johnston 
mctp_get_alloc_ctx(struct mctp * mctp)10584a09e1dcSMatt Johnston void *mctp_get_alloc_ctx(struct mctp *mctp)
10594a09e1dcSMatt Johnston {
10604a09e1dcSMatt Johnston 	return mctp->alloc_ctx;
10614a09e1dcSMatt Johnston }
10624a09e1dcSMatt Johnston 
mctp_set_alloc_ctx(struct mctp * mctp,void * ctx)10634a09e1dcSMatt Johnston void mctp_set_alloc_ctx(struct mctp *mctp, void *ctx)
10644a09e1dcSMatt Johnston {
10654a09e1dcSMatt Johnston 	mctp->alloc_ctx = ctx;
10664a09e1dcSMatt Johnston }
1067