xref: /openbmc/libmctp/core.c (revision 133df7ab)
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>
114cdc200fSJeremy Kerr 
124cdc200fSJeremy Kerr #undef pr_fmt
134cdc200fSJeremy Kerr #define pr_fmt(fmt) "core: " fmt
144cdc200fSJeremy Kerr 
154cdc200fSJeremy Kerr #include "libmctp.h"
164cdc200fSJeremy Kerr #include "libmctp-alloc.h"
174cdc200fSJeremy Kerr #include "libmctp-log.h"
18ba6727e6SWiktor Gołgowski #include "libmctp-cmds.h"
19c2b833e4SAndrew Jeffery #include "range.h"
204cdc200fSJeremy Kerr 
214cdc200fSJeremy Kerr /* Internal data structures */
224cdc200fSJeremy Kerr 
23c61501ccSAndrew Jeffery enum mctp_bus_state {
24c61501ccSAndrew Jeffery 	mctp_bus_state_constructed = 0,
25c61501ccSAndrew Jeffery 	mctp_bus_state_tx_enabled,
26c61501ccSAndrew Jeffery 	mctp_bus_state_tx_disabled,
27c61501ccSAndrew Jeffery };
28c61501ccSAndrew Jeffery 
294cdc200fSJeremy Kerr struct mctp_bus {
304cdc200fSJeremy Kerr 	mctp_eid_t eid;
314cdc200fSJeremy Kerr 	struct mctp_binding *binding;
32c61501ccSAndrew Jeffery 	enum mctp_bus_state state;
334cdc200fSJeremy Kerr 
34cc2458d9SJeremy Kerr 	struct mctp_pktbuf *tx_queue_head;
35cc2458d9SJeremy Kerr 	struct mctp_pktbuf *tx_queue_tail;
36cc2458d9SJeremy Kerr 
374cdc200fSJeremy Kerr 	/* todo: routing */
384cdc200fSJeremy Kerr };
394cdc200fSJeremy Kerr 
4024db71fbSJeremy Kerr struct mctp_msg_ctx {
4124db71fbSJeremy Kerr 	uint8_t src;
421a4ec3cdSJeremy Kerr 	uint8_t dest;
4324db71fbSJeremy Kerr 	uint8_t tag;
4424db71fbSJeremy Kerr 	uint8_t last_seq;
4524db71fbSJeremy Kerr 	void *buf;
4624db71fbSJeremy Kerr 	size_t buf_size;
4724db71fbSJeremy Kerr 	size_t buf_alloc_size;
4869f545f7SSumanth Bhat 	size_t fragment_size;
4924db71fbSJeremy Kerr };
5024db71fbSJeremy Kerr 
514cdc200fSJeremy Kerr struct mctp {
521a4ec3cdSJeremy Kerr 	int n_busses;
531a4ec3cdSJeremy Kerr 	struct mctp_bus *busses;
544cdc200fSJeremy Kerr 
554cdc200fSJeremy Kerr 	/* Message RX callback */
564cdc200fSJeremy Kerr 	mctp_rx_fn message_rx;
574cdc200fSJeremy Kerr 	void *message_rx_data;
5824db71fbSJeremy Kerr 
595d3d4e6dSAndrew Jeffery 	/* Packet capture callback */
605d3d4e6dSAndrew Jeffery 	mctp_capture_fn capture;
615d3d4e6dSAndrew Jeffery 	void *capture_data;
625d3d4e6dSAndrew Jeffery 
6324db71fbSJeremy Kerr 	/* Message reassembly.
6424db71fbSJeremy Kerr 	 * @todo: flexible context count
6524db71fbSJeremy Kerr 	 */
6624db71fbSJeremy Kerr 	struct mctp_msg_ctx msg_ctxs[16];
671a4ec3cdSJeremy Kerr 
681a4ec3cdSJeremy Kerr 	enum {
691a4ec3cdSJeremy Kerr 		ROUTE_ENDPOINT,
701a4ec3cdSJeremy Kerr 		ROUTE_BRIDGE,
711a4ec3cdSJeremy Kerr 	} route_policy;
722c820c5aSSumanth Bhat 	size_t max_message_size;
734cdc200fSJeremy Kerr };
744cdc200fSJeremy Kerr 
754cdc200fSJeremy Kerr #ifndef BUILD_ASSERT
764cdc200fSJeremy Kerr #define BUILD_ASSERT(x)                                                        \
77a721c2d8SPatrick Williams 	do {                                                                   \
78a721c2d8SPatrick Williams 		(void)sizeof(char[0 - (!(x))]);                                \
79a721c2d8SPatrick Williams 	} while (0)
804cdc200fSJeremy Kerr #endif
814cdc200fSJeremy Kerr 
8224db71fbSJeremy Kerr #ifndef ARRAY_SIZE
8324db71fbSJeremy Kerr #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
8424db71fbSJeremy Kerr #endif
8524db71fbSJeremy Kerr 
862c820c5aSSumanth Bhat /* 64kb should be sufficient for a single message. Applications
872c820c5aSSumanth Bhat  * requiring higher sizes can override by setting max_message_size.*/
882c820c5aSSumanth Bhat #ifndef MCTP_MAX_MESSAGE_SIZE
892c820c5aSSumanth Bhat #define MCTP_MAX_MESSAGE_SIZE 65536
902c820c5aSSumanth Bhat #endif
912c820c5aSSumanth Bhat 
92b93b6112SAndrew Jeffery static int mctp_message_tx_on_bus(struct mctp_bus *bus, mctp_eid_t src,
93f39c3857SSumanth Bhat 				  mctp_eid_t dest, bool tag_owner,
94f39c3857SSumanth Bhat 				  uint8_t msg_tag, void *msg, size_t msg_len);
951a4ec3cdSJeremy Kerr 
mctp_pktbuf_alloc(struct mctp_binding * binding,size_t len)96df15f7e9SJeremy Kerr struct mctp_pktbuf *mctp_pktbuf_alloc(struct mctp_binding *binding, size_t len)
974cdc200fSJeremy Kerr {
984cdc200fSJeremy Kerr 	struct mctp_pktbuf *buf;
99df15f7e9SJeremy Kerr 	size_t size;
1004cdc200fSJeremy Kerr 
10139da3d03SAndrew Jeffery 	size = binding->pkt_size + binding->pkt_header + binding->pkt_trailer;
102487b31e0SRashmica Gupta 	if (len > size) {
103487b31e0SRashmica Gupta 		return NULL;
104487b31e0SRashmica Gupta 	}
1054cdc200fSJeremy Kerr 
1064cdc200fSJeremy Kerr 	/* todo: pools */
107df15f7e9SJeremy Kerr 	buf = __mctp_alloc(sizeof(*buf) + size);
1084cdc200fSJeremy Kerr 
1092608b294SPedro Martelletto 	if (!buf)
1102608b294SPedro Martelletto 		return NULL;
1112608b294SPedro Martelletto 
112df15f7e9SJeremy Kerr 	buf->size = size;
11339da3d03SAndrew Jeffery 	buf->start = binding->pkt_header;
1144cdc200fSJeremy Kerr 	buf->end = buf->start + len;
1154cdc200fSJeremy Kerr 	buf->mctp_hdr_off = buf->start;
116dd109f1dSJeremy Kerr 	buf->next = NULL;
1174cdc200fSJeremy Kerr 
1184cdc200fSJeremy Kerr 	return buf;
1194cdc200fSJeremy Kerr }
1204cdc200fSJeremy Kerr 
mctp_pktbuf_free(struct mctp_pktbuf * pkt)1214cdc200fSJeremy Kerr void mctp_pktbuf_free(struct mctp_pktbuf *pkt)
1224cdc200fSJeremy Kerr {
1234cdc200fSJeremy Kerr 	__mctp_free(pkt);
1244cdc200fSJeremy Kerr }
1254cdc200fSJeremy Kerr 
mctp_pktbuf_hdr(struct mctp_pktbuf * pkt)1264cdc200fSJeremy Kerr struct mctp_hdr *mctp_pktbuf_hdr(struct mctp_pktbuf *pkt)
1274cdc200fSJeremy Kerr {
1287aaccb51SMoritz Fischer 	return (struct mctp_hdr *)(pkt->data + pkt->mctp_hdr_off);
1294cdc200fSJeremy Kerr }
1304cdc200fSJeremy Kerr 
mctp_pktbuf_data(struct mctp_pktbuf * pkt)1314cdc200fSJeremy Kerr void *mctp_pktbuf_data(struct mctp_pktbuf *pkt)
1324cdc200fSJeremy Kerr {
1337aaccb51SMoritz Fischer 	return pkt->data + pkt->mctp_hdr_off + sizeof(struct mctp_hdr);
1344cdc200fSJeremy Kerr }
1354cdc200fSJeremy Kerr 
mctp_pktbuf_size(struct mctp_pktbuf * pkt)136b942e3a3SAndrew Jeffery size_t mctp_pktbuf_size(struct mctp_pktbuf *pkt)
1374cdc200fSJeremy Kerr {
1384cdc200fSJeremy Kerr 	return pkt->end - pkt->start;
1394cdc200fSJeremy Kerr }
1404cdc200fSJeremy Kerr 
mctp_pktbuf_alloc_start(struct mctp_pktbuf * pkt,size_t size)141df15f7e9SJeremy Kerr void *mctp_pktbuf_alloc_start(struct mctp_pktbuf *pkt, size_t size)
1424cdc200fSJeremy Kerr {
1434cdc200fSJeremy Kerr 	assert(size <= pkt->start);
1444cdc200fSJeremy Kerr 	pkt->start -= size;
1454cdc200fSJeremy Kerr 	return pkt->data + pkt->start;
1464cdc200fSJeremy Kerr }
1474cdc200fSJeremy Kerr 
mctp_pktbuf_alloc_end(struct mctp_pktbuf * pkt,size_t size)148df15f7e9SJeremy Kerr void *mctp_pktbuf_alloc_end(struct mctp_pktbuf *pkt, size_t size)
1494cdc200fSJeremy Kerr {
1504cdc200fSJeremy Kerr 	void *buf;
1514cdc200fSJeremy Kerr 
1523ac70d62SAndrew Jeffery 	assert(size <= (pkt->size - pkt->end));
1534cdc200fSJeremy Kerr 	buf = pkt->data + pkt->end;
1544cdc200fSJeremy Kerr 	pkt->end += size;
1554cdc200fSJeremy Kerr 	return buf;
1564cdc200fSJeremy Kerr }
1574cdc200fSJeremy Kerr 
mctp_pktbuf_push(struct mctp_pktbuf * pkt,void * data,size_t len)158df15f7e9SJeremy Kerr int mctp_pktbuf_push(struct mctp_pktbuf *pkt, void *data, size_t len)
1594cdc200fSJeremy Kerr {
1604cdc200fSJeremy Kerr 	void *p;
1614cdc200fSJeremy Kerr 
162df15f7e9SJeremy Kerr 	if (pkt->end + len > pkt->size)
1634cdc200fSJeremy Kerr 		return -1;
1644cdc200fSJeremy Kerr 
1654cdc200fSJeremy Kerr 	p = pkt->data + pkt->end;
1664cdc200fSJeremy Kerr 
1674cdc200fSJeremy Kerr 	pkt->end += len;
1684cdc200fSJeremy Kerr 	memcpy(p, data, len);
1694cdc200fSJeremy Kerr 
1704cdc200fSJeremy Kerr 	return 0;
1714cdc200fSJeremy Kerr }
1724cdc200fSJeremy Kerr 
mctp_pktbuf_pop(struct mctp_pktbuf * pkt,size_t len)173eba19a3bSAndrew Jeffery void *mctp_pktbuf_pop(struct mctp_pktbuf *pkt, size_t len)
174eba19a3bSAndrew Jeffery {
175eba19a3bSAndrew Jeffery 	if (len > mctp_pktbuf_size(pkt))
176eba19a3bSAndrew Jeffery 		return NULL;
177eba19a3bSAndrew Jeffery 
178eba19a3bSAndrew Jeffery 	pkt->end -= len;
179eba19a3bSAndrew Jeffery 	return pkt->data + pkt->end;
180eba19a3bSAndrew Jeffery }
181eba19a3bSAndrew Jeffery 
18224db71fbSJeremy Kerr /* Message reassembly */
mctp_msg_ctx_lookup(struct mctp * mctp,uint8_t src,uint8_t dest,uint8_t tag)183a721c2d8SPatrick Williams static struct mctp_msg_ctx *mctp_msg_ctx_lookup(struct mctp *mctp, uint8_t src,
184a721c2d8SPatrick Williams 						uint8_t dest, uint8_t tag)
18524db71fbSJeremy Kerr {
18624db71fbSJeremy Kerr 	unsigned int i;
18724db71fbSJeremy Kerr 
18824db71fbSJeremy Kerr 	/* @todo: better lookup, if we add support for more outstanding
18924db71fbSJeremy Kerr 	 * message contexts */
19024db71fbSJeremy Kerr 	for (i = 0; i < ARRAY_SIZE(mctp->msg_ctxs); i++) {
19124db71fbSJeremy Kerr 		struct mctp_msg_ctx *ctx = &mctp->msg_ctxs[i];
1921a4ec3cdSJeremy Kerr 		if (ctx->src == src && ctx->dest == dest && ctx->tag == tag)
19324db71fbSJeremy Kerr 			return ctx;
19424db71fbSJeremy Kerr 	}
19524db71fbSJeremy Kerr 
19624db71fbSJeremy Kerr 	return NULL;
19724db71fbSJeremy Kerr }
19824db71fbSJeremy Kerr 
mctp_msg_ctx_create(struct mctp * mctp,uint8_t src,uint8_t dest,uint8_t tag)199a721c2d8SPatrick Williams static struct mctp_msg_ctx *mctp_msg_ctx_create(struct mctp *mctp, uint8_t src,
200a721c2d8SPatrick Williams 						uint8_t dest, uint8_t tag)
20124db71fbSJeremy Kerr {
20211a234e1SJeremy Kerr 	struct mctp_msg_ctx *ctx = NULL;
20324db71fbSJeremy Kerr 	unsigned int i;
20424db71fbSJeremy Kerr 
20524db71fbSJeremy Kerr 	for (i = 0; i < ARRAY_SIZE(mctp->msg_ctxs); i++) {
20624db71fbSJeremy Kerr 		struct mctp_msg_ctx *tmp = &mctp->msg_ctxs[i];
20724db71fbSJeremy Kerr 		if (!tmp->src) {
20824db71fbSJeremy Kerr 			ctx = tmp;
20924db71fbSJeremy Kerr 			break;
21024db71fbSJeremy Kerr 		}
21124db71fbSJeremy Kerr 	}
21224db71fbSJeremy Kerr 
21324db71fbSJeremy Kerr 	if (!ctx)
21424db71fbSJeremy Kerr 		return NULL;
21524db71fbSJeremy Kerr 
21624db71fbSJeremy Kerr 	ctx->src = src;
2171a4ec3cdSJeremy Kerr 	ctx->dest = dest;
21824db71fbSJeremy Kerr 	ctx->tag = tag;
2199a3da81cSJeremy Kerr 	ctx->buf_size = 0;
22024db71fbSJeremy Kerr 
22124db71fbSJeremy Kerr 	return ctx;
22224db71fbSJeremy Kerr }
22324db71fbSJeremy Kerr 
mctp_msg_ctx_drop(struct mctp_msg_ctx * ctx)22424db71fbSJeremy Kerr static void mctp_msg_ctx_drop(struct mctp_msg_ctx *ctx)
22524db71fbSJeremy Kerr {
22624db71fbSJeremy Kerr 	ctx->src = 0;
22724db71fbSJeremy Kerr }
22824db71fbSJeremy Kerr 
mctp_msg_ctx_reset(struct mctp_msg_ctx * ctx)22924db71fbSJeremy Kerr static void mctp_msg_ctx_reset(struct mctp_msg_ctx *ctx)
23024db71fbSJeremy Kerr {
23124db71fbSJeremy Kerr 	ctx->buf_size = 0;
23269f545f7SSumanth Bhat 	ctx->fragment_size = 0;
23324db71fbSJeremy Kerr }
23424db71fbSJeremy Kerr 
mctp_msg_ctx_add_pkt(struct mctp_msg_ctx * ctx,struct mctp_pktbuf * pkt,size_t max_size)23524db71fbSJeremy Kerr static int mctp_msg_ctx_add_pkt(struct mctp_msg_ctx *ctx,
2362c820c5aSSumanth Bhat 				struct mctp_pktbuf *pkt, size_t max_size)
23724db71fbSJeremy Kerr {
23824db71fbSJeremy Kerr 	size_t len;
23924db71fbSJeremy Kerr 
24024db71fbSJeremy Kerr 	len = mctp_pktbuf_size(pkt) - sizeof(struct mctp_hdr);
24124db71fbSJeremy Kerr 
242bc79c24eSSumanth Bhat 	if (len + ctx->buf_size < ctx->buf_size) {
243bc79c24eSSumanth Bhat 		return -1;
244bc79c24eSSumanth Bhat 	}
245bc79c24eSSumanth Bhat 
24624db71fbSJeremy Kerr 	if (ctx->buf_size + len > ctx->buf_alloc_size) {
24724db71fbSJeremy Kerr 		size_t new_alloc_size;
24800ecc6c9SAndrew Jeffery 		void *lbuf;
24924db71fbSJeremy Kerr 
2505a508915SAndrew Jeffery 		/* @todo: finer-grained allocation */
25124db71fbSJeremy Kerr 		if (!ctx->buf_alloc_size) {
252c2b833e4SAndrew Jeffery 			new_alloc_size = MAX(len, 4096UL);
25324db71fbSJeremy Kerr 		} else {
254a721c2d8SPatrick Williams 			new_alloc_size = MAX(ctx->buf_alloc_size * 2,
255a721c2d8SPatrick Williams 					     len + ctx->buf_size);
25624db71fbSJeremy Kerr 		}
25700ecc6c9SAndrew Jeffery 
2582c820c5aSSumanth Bhat 		/* Don't allow heap to grow beyond a limit */
2592c820c5aSSumanth Bhat 		if (new_alloc_size > max_size)
2602c820c5aSSumanth Bhat 			return -1;
2612c820c5aSSumanth Bhat 
26200ecc6c9SAndrew Jeffery 		lbuf = __mctp_realloc(ctx->buf, new_alloc_size);
26300ecc6c9SAndrew Jeffery 		if (lbuf) {
26400ecc6c9SAndrew Jeffery 			ctx->buf = lbuf;
26524db71fbSJeremy Kerr 			ctx->buf_alloc_size = new_alloc_size;
26600ecc6c9SAndrew Jeffery 		} else {
26700ecc6c9SAndrew Jeffery 			__mctp_free(ctx->buf);
26800ecc6c9SAndrew Jeffery 			return -1;
26900ecc6c9SAndrew Jeffery 		}
27024db71fbSJeremy Kerr 	}
27124db71fbSJeremy Kerr 
2727aaccb51SMoritz Fischer 	memcpy((uint8_t *)ctx->buf + ctx->buf_size, mctp_pktbuf_data(pkt), len);
27324db71fbSJeremy Kerr 	ctx->buf_size += len;
27424db71fbSJeremy Kerr 
27524db71fbSJeremy Kerr 	return 0;
27624db71fbSJeremy Kerr }
27724db71fbSJeremy Kerr 
27824db71fbSJeremy Kerr /* Core API functions */
mctp_init(void)2794cdc200fSJeremy Kerr struct mctp *mctp_init(void)
2804cdc200fSJeremy Kerr {
2814cdc200fSJeremy Kerr 	struct mctp *mctp;
2824cdc200fSJeremy Kerr 
2834cdc200fSJeremy Kerr 	mctp = __mctp_alloc(sizeof(*mctp));
28496d54492SSumanth Bhat 
28596d54492SSumanth Bhat 	if (!mctp)
28696d54492SSumanth Bhat 		return NULL;
28796d54492SSumanth Bhat 
2884cdc200fSJeremy Kerr 	memset(mctp, 0, sizeof(*mctp));
2892c820c5aSSumanth Bhat 	mctp->max_message_size = MCTP_MAX_MESSAGE_SIZE;
2904cdc200fSJeremy Kerr 
2914cdc200fSJeremy Kerr 	return mctp;
2924cdc200fSJeremy Kerr }
2934cdc200fSJeremy Kerr 
mctp_set_max_message_size(struct mctp * mctp,size_t message_size)2942c820c5aSSumanth Bhat void mctp_set_max_message_size(struct mctp *mctp, size_t message_size)
2952c820c5aSSumanth Bhat {
2962c820c5aSSumanth Bhat 	mctp->max_message_size = message_size;
2972c820c5aSSumanth Bhat }
2982c820c5aSSumanth Bhat 
mctp_set_capture_handler(struct mctp * mctp,mctp_capture_fn fn,void * user)2995d3d4e6dSAndrew Jeffery void mctp_set_capture_handler(struct mctp *mctp, mctp_capture_fn fn, void *user)
3005d3d4e6dSAndrew Jeffery {
3015d3d4e6dSAndrew Jeffery 	mctp->capture = fn;
3025d3d4e6dSAndrew Jeffery 	mctp->capture_data = user;
3035d3d4e6dSAndrew Jeffery }
3045d3d4e6dSAndrew Jeffery 
mctp_bus_destroy(struct mctp_bus * bus)3053ae89dceSAndrew Jeffery static void mctp_bus_destroy(struct mctp_bus *bus)
3063ae89dceSAndrew Jeffery {
3073ae89dceSAndrew Jeffery 	while (bus->tx_queue_head) {
3083ae89dceSAndrew Jeffery 		struct mctp_pktbuf *curr = bus->tx_queue_head;
3093ae89dceSAndrew Jeffery 
3103ae89dceSAndrew Jeffery 		bus->tx_queue_head = curr->next;
3113ae89dceSAndrew Jeffery 		mctp_pktbuf_free(curr);
3123ae89dceSAndrew Jeffery 	}
3133ae89dceSAndrew Jeffery }
3143ae89dceSAndrew Jeffery 
mctp_destroy(struct mctp * mctp)315fa56ca5fSAndrew Jeffery void mctp_destroy(struct mctp *mctp)
316fa56ca5fSAndrew Jeffery {
317b93b6112SAndrew Jeffery 	size_t i;
318fa56ca5fSAndrew Jeffery 
319fa56ca5fSAndrew Jeffery 	/* Cleanup message assembly contexts */
320b93b6112SAndrew Jeffery 	BUILD_ASSERT(ARRAY_SIZE(mctp->msg_ctxs) < SIZE_MAX);
321fa56ca5fSAndrew Jeffery 	for (i = 0; i < ARRAY_SIZE(mctp->msg_ctxs); i++) {
322fa56ca5fSAndrew Jeffery 		struct mctp_msg_ctx *tmp = &mctp->msg_ctxs[i];
323fa56ca5fSAndrew Jeffery 		if (tmp->buf)
324fa56ca5fSAndrew Jeffery 			__mctp_free(tmp->buf);
325fa56ca5fSAndrew Jeffery 	}
326fa56ca5fSAndrew Jeffery 
3273ae89dceSAndrew Jeffery 	while (mctp->n_busses--)
3283ae89dceSAndrew Jeffery 		mctp_bus_destroy(&mctp->busses[mctp->n_busses]);
3293ae89dceSAndrew Jeffery 
330fa56ca5fSAndrew Jeffery 	__mctp_free(mctp->busses);
331fa56ca5fSAndrew Jeffery 	__mctp_free(mctp);
332fa56ca5fSAndrew Jeffery }
333fa56ca5fSAndrew Jeffery 
mctp_set_rx_all(struct mctp * mctp,mctp_rx_fn fn,void * data)3344cdc200fSJeremy Kerr int mctp_set_rx_all(struct mctp *mctp, mctp_rx_fn fn, void *data)
3354cdc200fSJeremy Kerr {
3364cdc200fSJeremy Kerr 	mctp->message_rx = fn;
3374cdc200fSJeremy Kerr 	mctp->message_rx_data = data;
3384cdc200fSJeremy Kerr 	return 0;
3394cdc200fSJeremy Kerr }
3404cdc200fSJeremy Kerr 
find_bus_for_eid(struct mctp * mctp,mctp_eid_t dest)341a721c2d8SPatrick Williams static struct mctp_bus *find_bus_for_eid(struct mctp *mctp, mctp_eid_t dest
342a721c2d8SPatrick Williams 					 __attribute__((unused)))
3434cdc200fSJeremy Kerr {
344663ec39eSBrad Bishop 	if (mctp->n_busses == 0)
345663ec39eSBrad Bishop 		return NULL;
346663ec39eSBrad Bishop 
3471a4ec3cdSJeremy Kerr 	/* for now, just use the first bus. For full routing support,
3481a4ec3cdSJeremy Kerr 	 * we will need a table of neighbours */
3494cdc200fSJeremy Kerr 	return &mctp->busses[0];
3504cdc200fSJeremy Kerr }
3514cdc200fSJeremy Kerr 
mctp_register_bus(struct mctp * mctp,struct mctp_binding * binding,mctp_eid_t eid)352a721c2d8SPatrick Williams int mctp_register_bus(struct mctp *mctp, struct mctp_binding *binding,
3534cdc200fSJeremy Kerr 		      mctp_eid_t eid)
3544cdc200fSJeremy Kerr {
3553e8a12a4SAndrew Jeffery 	int rc = 0;
3563e8a12a4SAndrew Jeffery 
3577520cec0SJeremy Kerr 	/* todo: multiple busses */
3581a4ec3cdSJeremy Kerr 	assert(mctp->n_busses == 0);
3591a4ec3cdSJeremy Kerr 	mctp->n_busses = 1;
3603e8a12a4SAndrew Jeffery 
3611a4ec3cdSJeremy Kerr 	mctp->busses = __mctp_alloc(sizeof(struct mctp_bus));
3623e8a12a4SAndrew Jeffery 	if (!mctp->busses)
3633e8a12a4SAndrew Jeffery 		return -ENOMEM;
3643e8a12a4SAndrew Jeffery 
36562d7236fSJames Feist 	memset(mctp->busses, 0, sizeof(struct mctp_bus));
3664cdc200fSJeremy Kerr 	mctp->busses[0].binding = binding;
3674cdc200fSJeremy Kerr 	mctp->busses[0].eid = eid;
3687520cec0SJeremy Kerr 	binding->bus = &mctp->busses[0];
3690a00dca2SJeremy Kerr 	binding->mctp = mctp;
3701a4ec3cdSJeremy Kerr 	mctp->route_policy = ROUTE_ENDPOINT;
3713b36d17cSJeremy Kerr 
3723e8a12a4SAndrew Jeffery 	if (binding->start) {
3733e8a12a4SAndrew Jeffery 		rc = binding->start(binding);
3743e8a12a4SAndrew Jeffery 		if (rc < 0) {
3753e8a12a4SAndrew Jeffery 			mctp_prerr("Failed to start binding: %d", rc);
37619275230SAndrew Jeffery 			binding->bus = NULL;
3773e8a12a4SAndrew Jeffery 			__mctp_free(mctp->busses);
3783e8a12a4SAndrew Jeffery 			mctp->busses = NULL;
3792304c833SAndrew Jeffery 			mctp->n_busses = 0;
3803e8a12a4SAndrew Jeffery 		}
3813e8a12a4SAndrew Jeffery 	}
3823b36d17cSJeremy Kerr 
3833e8a12a4SAndrew Jeffery 	return rc;
3844cdc200fSJeremy Kerr }
3854cdc200fSJeremy Kerr 
mctp_unregister_bus(struct mctp * mctp,struct mctp_binding * binding)3862094c3c0SAndrew Jeffery void mctp_unregister_bus(struct mctp *mctp, struct mctp_binding *binding)
3872094c3c0SAndrew Jeffery {
3882094c3c0SAndrew Jeffery 	/*
3892094c3c0SAndrew Jeffery 	 * We only support one bus right now; once the call completes we will
3902094c3c0SAndrew Jeffery 	 * have no more busses
3912094c3c0SAndrew Jeffery 	 */
3922094c3c0SAndrew Jeffery 	mctp->n_busses = 0;
3932094c3c0SAndrew Jeffery 	binding->mctp = NULL;
3942094c3c0SAndrew Jeffery 	binding->bus = NULL;
395b7824b66SBenjamin Gwin 	__mctp_free(mctp->busses);
3962094c3c0SAndrew Jeffery }
3972094c3c0SAndrew Jeffery 
mctp_bridge_busses(struct mctp * mctp,struct mctp_binding * b1,struct mctp_binding * b2)398a721c2d8SPatrick Williams int mctp_bridge_busses(struct mctp *mctp, struct mctp_binding *b1,
399a721c2d8SPatrick Williams 		       struct mctp_binding *b2)
4001a4ec3cdSJeremy Kerr {
40119275230SAndrew Jeffery 	int rc = 0;
40219275230SAndrew Jeffery 
4031a4ec3cdSJeremy Kerr 	assert(mctp->n_busses == 0);
4041a4ec3cdSJeremy Kerr 	mctp->busses = __mctp_alloc(2 * sizeof(struct mctp_bus));
405a523bcc1SHelen Huang 	if (!mctp->busses)
406a523bcc1SHelen Huang 		return -ENOMEM;
40762d7236fSJames Feist 	memset(mctp->busses, 0, 2 * sizeof(struct mctp_bus));
4081a4ec3cdSJeremy Kerr 	mctp->n_busses = 2;
4091a4ec3cdSJeremy Kerr 	mctp->busses[0].binding = b1;
4101a4ec3cdSJeremy Kerr 	b1->bus = &mctp->busses[0];
4111a4ec3cdSJeremy Kerr 	b1->mctp = mctp;
4121a4ec3cdSJeremy Kerr 	mctp->busses[1].binding = b2;
4131a4ec3cdSJeremy Kerr 	b2->bus = &mctp->busses[1];
4141a4ec3cdSJeremy Kerr 	b2->mctp = mctp;
4151a4ec3cdSJeremy Kerr 
4161a4ec3cdSJeremy Kerr 	mctp->route_policy = ROUTE_BRIDGE;
4173b36d17cSJeremy Kerr 
41819275230SAndrew Jeffery 	if (b1->start) {
41919275230SAndrew Jeffery 		rc = b1->start(b1);
42019275230SAndrew Jeffery 		if (rc < 0) {
42119275230SAndrew Jeffery 			mctp_prerr("Failed to start bridged bus %s: %d",
42219275230SAndrew Jeffery 				   b1->name, rc);
42319275230SAndrew Jeffery 			goto done;
42419275230SAndrew Jeffery 		}
42519275230SAndrew Jeffery 	}
4263b36d17cSJeremy Kerr 
42719275230SAndrew Jeffery 	if (b2->start) {
42819275230SAndrew Jeffery 		rc = b2->start(b2);
42919275230SAndrew Jeffery 		if (rc < 0) {
43019275230SAndrew Jeffery 			mctp_prerr("Failed to start bridged bus %s: %d",
43119275230SAndrew Jeffery 				   b2->name, rc);
43219275230SAndrew Jeffery 			goto done;
43319275230SAndrew Jeffery 		}
43419275230SAndrew Jeffery 	}
4353b36d17cSJeremy Kerr 
43619275230SAndrew Jeffery done:
43719275230SAndrew Jeffery 	return rc;
4381a4ec3cdSJeremy Kerr }
4391a4ec3cdSJeremy Kerr 
mctp_ctrl_cmd_is_transport(struct mctp_ctrl_msg_hdr * hdr)440ba6727e6SWiktor Gołgowski static inline bool mctp_ctrl_cmd_is_transport(struct mctp_ctrl_msg_hdr *hdr)
4411a4ec3cdSJeremy Kerr {
442ba6727e6SWiktor Gołgowski 	return ((hdr->command_code >= MCTP_CTRL_CMD_FIRST_TRANSPORT) &&
443ba6727e6SWiktor Gołgowski 		(hdr->command_code <= MCTP_CTRL_CMD_LAST_TRANSPORT));
444ba6727e6SWiktor Gołgowski }
445ba6727e6SWiktor 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)446b93b6112SAndrew Jeffery static bool mctp_ctrl_handle_msg(struct mctp_bus *bus, mctp_eid_t src,
447f39c3857SSumanth Bhat 				 uint8_t msg_tag, bool tag_owner, void *buffer,
448f39c3857SSumanth Bhat 				 size_t length)
449ba6727e6SWiktor Gołgowski {
450ba6727e6SWiktor Gołgowski 	struct mctp_ctrl_msg_hdr *msg_hdr = buffer;
451ba6727e6SWiktor Gołgowski 
452ba6727e6SWiktor Gołgowski 	/*
453ba6727e6SWiktor Gołgowski 	 * Control message is received. If a transport control message handler
454ba6727e6SWiktor Gołgowski 	 * is provided, it will called. If there is no dedicated handler, this
455ba6727e6SWiktor Gołgowski 	 * function returns false and data can be handled by the generic
456ba6727e6SWiktor Gołgowski 	 * message handler. The transport control message handler will be
457ba6727e6SWiktor Gołgowski 	 * provided with messages in the command range 0xF0 - 0xFF.
458ba6727e6SWiktor Gołgowski 	 */
459ba6727e6SWiktor Gołgowski 	if (mctp_ctrl_cmd_is_transport(msg_hdr)) {
460ba6727e6SWiktor Gołgowski 		if (bus->binding->control_rx != NULL) {
461ba6727e6SWiktor Gołgowski 			/* MCTP bus binding handler */
462f39c3857SSumanth Bhat 			bus->binding->control_rx(src, msg_tag, tag_owner,
463ba6727e6SWiktor Gołgowski 						 bus->binding->control_rx_data,
464ba6727e6SWiktor Gołgowski 						 buffer, length);
465ba6727e6SWiktor Gołgowski 			return true;
466ba6727e6SWiktor Gołgowski 		}
467ba6727e6SWiktor Gołgowski 	}
468ba6727e6SWiktor Gołgowski 
469ba6727e6SWiktor Gołgowski 	/*
470ba6727e6SWiktor Gołgowski 	 * Command was not handled, due to lack of specific callback.
471ba6727e6SWiktor Gołgowski 	 * It will be passed to regular message_rx handler.
472ba6727e6SWiktor Gołgowski 	 */
473ba6727e6SWiktor Gołgowski 	return false;
474ba6727e6SWiktor Gołgowski }
475ba6727e6SWiktor Gołgowski 
mctp_rx_dest_is_local(struct mctp_bus * bus,mctp_eid_t dest)476ba6727e6SWiktor Gołgowski static inline bool mctp_rx_dest_is_local(struct mctp_bus *bus, mctp_eid_t dest)
477ba6727e6SWiktor Gołgowski {
478ba6727e6SWiktor Gołgowski 	return dest == bus->eid || dest == MCTP_EID_NULL ||
479ba6727e6SWiktor Gołgowski 	       dest == MCTP_EID_BROADCAST;
480ba6727e6SWiktor Gołgowski }
481ba6727e6SWiktor Gołgowski 
mctp_ctrl_cmd_is_request(struct mctp_ctrl_msg_hdr * hdr)482ba6727e6SWiktor Gołgowski static inline bool mctp_ctrl_cmd_is_request(struct mctp_ctrl_msg_hdr *hdr)
483ba6727e6SWiktor Gołgowski {
484ba6727e6SWiktor Gołgowski 	return hdr->ic_msg_type == MCTP_CTRL_HDR_MSG_TYPE &&
485ba6727e6SWiktor Gołgowski 	       hdr->rq_dgram_inst & MCTP_CTRL_HDR_FLAG_REQUEST;
486ba6727e6SWiktor Gołgowski }
487ba6727e6SWiktor Gołgowski 
488ba6727e6SWiktor Gołgowski /*
489ba6727e6SWiktor Gołgowski  * Receive the complete MCTP message and route it.
490ba6727e6SWiktor Gołgowski  * Asserts:
491ba6727e6SWiktor Gołgowski  *     'buf' is not NULL.
492ba6727e6SWiktor 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)493ba6727e6SWiktor Gołgowski static void mctp_rx(struct mctp *mctp, struct mctp_bus *bus, mctp_eid_t src,
494f39c3857SSumanth Bhat 		    mctp_eid_t dest, bool tag_owner, uint8_t msg_tag, void *buf,
495f39c3857SSumanth Bhat 		    size_t len)
496ba6727e6SWiktor Gołgowski {
497ba6727e6SWiktor Gołgowski 	assert(buf != NULL);
498ba6727e6SWiktor Gołgowski 
4991a4ec3cdSJeremy Kerr 	if (mctp->route_policy == ROUTE_ENDPOINT &&
500ba6727e6SWiktor Gołgowski 	    mctp_rx_dest_is_local(bus, dest)) {
501ba6727e6SWiktor Gołgowski 		/* Handle MCTP Control Messages: */
502ba6727e6SWiktor Gołgowski 		if (len >= sizeof(struct mctp_ctrl_msg_hdr)) {
503ba6727e6SWiktor Gołgowski 			struct mctp_ctrl_msg_hdr *msg_hdr = buf;
504ba6727e6SWiktor Gołgowski 
505ba6727e6SWiktor Gołgowski 			/*
506ba6727e6SWiktor Gołgowski 			 * Identify if this is a control request message.
507ba6727e6SWiktor Gołgowski 			 * See DSP0236 v1.3.0 sec. 11.5.
508ba6727e6SWiktor Gołgowski 			 */
509ba6727e6SWiktor Gołgowski 			if (mctp_ctrl_cmd_is_request(msg_hdr)) {
510ba6727e6SWiktor Gołgowski 				bool handled;
511f39c3857SSumanth Bhat 				handled = mctp_ctrl_handle_msg(
512f39c3857SSumanth Bhat 					bus, src, msg_tag, tag_owner, buf, len);
513ba6727e6SWiktor Gołgowski 				if (handled)
514ba6727e6SWiktor Gołgowski 					return;
515ba6727e6SWiktor Gołgowski 			}
516ba6727e6SWiktor Gołgowski 		}
517f39c3857SSumanth Bhat 
518ba6727e6SWiktor Gołgowski 		if (mctp->message_rx)
519f39c3857SSumanth Bhat 			mctp->message_rx(src, tag_owner, msg_tag,
520f39c3857SSumanth Bhat 					 mctp->message_rx_data, buf, len);
521ba6727e6SWiktor Gołgowski 	}
5221a4ec3cdSJeremy Kerr 
5231a4ec3cdSJeremy Kerr 	if (mctp->route_policy == ROUTE_BRIDGE) {
5241a4ec3cdSJeremy Kerr 		int i;
5251a4ec3cdSJeremy Kerr 
5261a4ec3cdSJeremy Kerr 		for (i = 0; i < mctp->n_busses; i++) {
5271a4ec3cdSJeremy Kerr 			struct mctp_bus *dest_bus = &mctp->busses[i];
5281a4ec3cdSJeremy Kerr 			if (dest_bus == bus)
5291a4ec3cdSJeremy Kerr 				continue;
5301a4ec3cdSJeremy Kerr 
531f39c3857SSumanth Bhat 			mctp_message_tx_on_bus(dest_bus, src, dest, tag_owner,
532f39c3857SSumanth Bhat 					       msg_tag, buf, len);
5331a4ec3cdSJeremy Kerr 		}
5341a4ec3cdSJeremy Kerr 	}
5351a4ec3cdSJeremy Kerr }
5361a4ec3cdSJeremy Kerr 
mctp_bus_rx(struct mctp_binding * binding,struct mctp_pktbuf * pkt)5370a00dca2SJeremy Kerr void mctp_bus_rx(struct mctp_binding *binding, struct mctp_pktbuf *pkt)
5384cdc200fSJeremy Kerr {
5397520cec0SJeremy Kerr 	struct mctp_bus *bus = binding->bus;
5400a00dca2SJeremy Kerr 	struct mctp *mctp = binding->mctp;
541c2def9f3SEd Tanous 	uint8_t flags, exp_seq, seq, tag;
54224db71fbSJeremy Kerr 	struct mctp_msg_ctx *ctx;
54324db71fbSJeremy Kerr 	struct mctp_hdr *hdr;
544f39c3857SSumanth Bhat 	bool tag_owner;
5454cdc200fSJeremy Kerr 	size_t len;
5464cdc200fSJeremy Kerr 	void *p;
54724db71fbSJeremy Kerr 	int rc;
5484cdc200fSJeremy Kerr 
5497520cec0SJeremy Kerr 	assert(bus);
5507520cec0SJeremy Kerr 
551d97869deSSumanth Bhat 	/* Drop packet if it was smaller than mctp hdr size */
552d97869deSSumanth Bhat 	if (mctp_pktbuf_size(pkt) <= sizeof(struct mctp_hdr))
553d97869deSSumanth Bhat 		goto out;
554d97869deSSumanth Bhat 
5555d3d4e6dSAndrew Jeffery 	if (mctp->capture)
556f2988977SRashmica Gupta 		mctp->capture(pkt, MCTP_MESSAGE_CAPTURE_INCOMING,
557f2988977SRashmica Gupta 			      mctp->capture_data);
5585d3d4e6dSAndrew Jeffery 
55924db71fbSJeremy Kerr 	hdr = mctp_pktbuf_hdr(pkt);
56024db71fbSJeremy Kerr 
5611a4ec3cdSJeremy Kerr 	/* small optimisation: don't bother reassembly if we're going to
5621a4ec3cdSJeremy Kerr 	 * drop the packet in mctp_rx anyway */
563*133df7abSJohn Chung 	if (mctp->route_policy == ROUTE_ENDPOINT &&
564*133df7abSJohn Chung 	    !mctp_rx_dest_is_local(bus, hdr->dest))
565c1693af4SJeremy Kerr 		goto out;
56624db71fbSJeremy Kerr 
56724db71fbSJeremy Kerr 	flags = hdr->flags_seq_tag & (MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM);
56824db71fbSJeremy Kerr 	tag = (hdr->flags_seq_tag >> MCTP_HDR_TAG_SHIFT) & MCTP_HDR_TAG_MASK;
56924db71fbSJeremy Kerr 	seq = (hdr->flags_seq_tag >> MCTP_HDR_SEQ_SHIFT) & MCTP_HDR_SEQ_MASK;
5707f7fdc1dSAndrew Jeffery 	tag_owner = (hdr->flags_seq_tag >> MCTP_HDR_TO_SHIFT) &
5717f7fdc1dSAndrew Jeffery 		    MCTP_HDR_TO_MASK;
57224db71fbSJeremy Kerr 
57324db71fbSJeremy Kerr 	switch (flags) {
57424db71fbSJeremy Kerr 	case MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM:
57524db71fbSJeremy Kerr 		/* single-packet message - send straight up to rx function,
57624db71fbSJeremy Kerr 		 * no need to create a message context */
5774cdc200fSJeremy Kerr 		len = pkt->end - pkt->mctp_hdr_off - sizeof(struct mctp_hdr);
578b4ae00b9SAndrew Jeffery 		p = pkt->data + pkt->mctp_hdr_off + sizeof(struct mctp_hdr);
579f39c3857SSumanth Bhat 		mctp_rx(mctp, bus, hdr->src, hdr->dest, tag_owner, tag, p, len);
58024db71fbSJeremy Kerr 		break;
58124db71fbSJeremy Kerr 
58224db71fbSJeremy Kerr 	case MCTP_HDR_FLAG_SOM:
58324db71fbSJeremy Kerr 		/* start of a new message - start the new context for
58424db71fbSJeremy Kerr 		 * future message reception. If an existing context is
58524db71fbSJeremy Kerr 		 * already present, drop it. */
5861a4ec3cdSJeremy Kerr 		ctx = mctp_msg_ctx_lookup(mctp, hdr->src, hdr->dest, tag);
58724db71fbSJeremy Kerr 		if (ctx) {
58824db71fbSJeremy Kerr 			mctp_msg_ctx_reset(ctx);
58924db71fbSJeremy Kerr 		} else {
590a721c2d8SPatrick Williams 			ctx = mctp_msg_ctx_create(mctp, hdr->src, hdr->dest,
591a721c2d8SPatrick Williams 						  tag);
59234d4c96fSSumanth Bhat 			/* If context creation fails due to exhaution of contexts we
59334d4c96fSSumanth Bhat 			* can support, drop the packet */
59434d4c96fSSumanth Bhat 			if (!ctx) {
59534d4c96fSSumanth Bhat 				mctp_prdebug("Context buffers exhausted.");
59634d4c96fSSumanth Bhat 				goto out;
59734d4c96fSSumanth Bhat 			}
59824db71fbSJeremy Kerr 		}
59924db71fbSJeremy Kerr 
60069f545f7SSumanth Bhat 		/* Save the fragment size, subsequent middle fragments
60169f545f7SSumanth Bhat 		 * should of the same size */
60269f545f7SSumanth Bhat 		ctx->fragment_size = mctp_pktbuf_size(pkt);
60369f545f7SSumanth Bhat 
6042c820c5aSSumanth Bhat 		rc = mctp_msg_ctx_add_pkt(ctx, pkt, mctp->max_message_size);
60524db71fbSJeremy Kerr 		if (rc) {
60624db71fbSJeremy Kerr 			mctp_msg_ctx_drop(ctx);
60724db71fbSJeremy Kerr 		} else {
60824db71fbSJeremy Kerr 			ctx->last_seq = seq;
60924db71fbSJeremy Kerr 		}
61024db71fbSJeremy Kerr 
61124db71fbSJeremy Kerr 		break;
61224db71fbSJeremy Kerr 
61324db71fbSJeremy Kerr 	case MCTP_HDR_FLAG_EOM:
6141a4ec3cdSJeremy Kerr 		ctx = mctp_msg_ctx_lookup(mctp, hdr->src, hdr->dest, tag);
61524db71fbSJeremy Kerr 		if (!ctx)
616c1693af4SJeremy Kerr 			goto out;
61724db71fbSJeremy Kerr 
618c2def9f3SEd Tanous 		exp_seq = (ctx->last_seq + 1) % 4;
619c2def9f3SEd Tanous 
620c2def9f3SEd Tanous 		if (exp_seq != seq) {
621c2def9f3SEd Tanous 			mctp_prdebug(
622c2def9f3SEd Tanous 				"Sequence number %d does not match expected %d",
623c2def9f3SEd Tanous 				seq, exp_seq);
62424db71fbSJeremy Kerr 			mctp_msg_ctx_drop(ctx);
625c1693af4SJeremy Kerr 			goto out;
62624db71fbSJeremy Kerr 		}
62724db71fbSJeremy Kerr 
62869f545f7SSumanth Bhat 		len = mctp_pktbuf_size(pkt);
62969f545f7SSumanth Bhat 
63069f545f7SSumanth Bhat 		if (len > ctx->fragment_size) {
631a721c2d8SPatrick Williams 			mctp_prdebug("Unexpected fragment size. Expected"
63269f545f7SSumanth Bhat 				     " less than %zu, received = %zu",
63369f545f7SSumanth Bhat 				     ctx->fragment_size, len);
63469f545f7SSumanth Bhat 			mctp_msg_ctx_drop(ctx);
63569f545f7SSumanth Bhat 			goto out;
63669f545f7SSumanth Bhat 		}
63769f545f7SSumanth Bhat 
6382c820c5aSSumanth Bhat 		rc = mctp_msg_ctx_add_pkt(ctx, pkt, mctp->max_message_size);
6391a4ec3cdSJeremy Kerr 		if (!rc)
640f39c3857SSumanth Bhat 			mctp_rx(mctp, bus, ctx->src, ctx->dest, tag_owner, tag,
64124db71fbSJeremy Kerr 				ctx->buf, ctx->buf_size);
64224db71fbSJeremy Kerr 
64324db71fbSJeremy Kerr 		mctp_msg_ctx_drop(ctx);
64424db71fbSJeremy Kerr 		break;
645c2def9f3SEd Tanous 
646c2def9f3SEd Tanous 	case 0:
647c2def9f3SEd Tanous 		/* Neither SOM nor EOM */
6481a4ec3cdSJeremy Kerr 		ctx = mctp_msg_ctx_lookup(mctp, hdr->src, hdr->dest, tag);
649c2def9f3SEd Tanous 		if (!ctx)
650c1693af4SJeremy Kerr 			goto out;
651c2def9f3SEd Tanous 
652c2def9f3SEd Tanous 		exp_seq = (ctx->last_seq + 1) % 4;
653c2def9f3SEd Tanous 		if (exp_seq != seq) {
654c2def9f3SEd Tanous 			mctp_prdebug(
655c2def9f3SEd Tanous 				"Sequence number %d does not match expected %d",
656c2def9f3SEd Tanous 				seq, exp_seq);
657c2def9f3SEd Tanous 			mctp_msg_ctx_drop(ctx);
658c1693af4SJeremy Kerr 			goto out;
659c2def9f3SEd Tanous 		}
660c2def9f3SEd Tanous 
66169f545f7SSumanth Bhat 		len = mctp_pktbuf_size(pkt);
66269f545f7SSumanth Bhat 
66369f545f7SSumanth Bhat 		if (len != ctx->fragment_size) {
664a721c2d8SPatrick Williams 			mctp_prdebug("Unexpected fragment size. Expected = %zu "
665a721c2d8SPatrick Williams 				     "received = %zu",
666a721c2d8SPatrick Williams 				     ctx->fragment_size, len);
66769f545f7SSumanth Bhat 			mctp_msg_ctx_drop(ctx);
66869f545f7SSumanth Bhat 			goto out;
66969f545f7SSumanth Bhat 		}
67069f545f7SSumanth Bhat 
6712c820c5aSSumanth Bhat 		rc = mctp_msg_ctx_add_pkt(ctx, pkt, mctp->max_message_size);
672c2def9f3SEd Tanous 		if (rc) {
673c2def9f3SEd Tanous 			mctp_msg_ctx_drop(ctx);
674c1693af4SJeremy Kerr 			goto out;
675c2def9f3SEd Tanous 		}
676c2def9f3SEd Tanous 		ctx->last_seq = seq;
677c2def9f3SEd Tanous 
678c2def9f3SEd Tanous 		break;
67924db71fbSJeremy Kerr 	}
680c1693af4SJeremy Kerr out:
681c1693af4SJeremy Kerr 	mctp_pktbuf_free(pkt);
6824cdc200fSJeremy Kerr }
6834cdc200fSJeremy Kerr 
mctp_packet_tx(struct mctp_bus * bus,struct mctp_pktbuf * pkt)684a721c2d8SPatrick Williams static int mctp_packet_tx(struct mctp_bus *bus, struct mctp_pktbuf *pkt)
6854cdc200fSJeremy Kerr {
6865d3d4e6dSAndrew Jeffery 	struct mctp *mctp = bus->binding->mctp;
6875d3d4e6dSAndrew Jeffery 
688c61501ccSAndrew Jeffery 	if (bus->state != mctp_bus_state_tx_enabled)
6891cd31184SJeremy Kerr 		return -1;
6901cd31184SJeremy Kerr 
6915d3d4e6dSAndrew Jeffery 	if (mctp->capture)
692f2988977SRashmica Gupta 		mctp->capture(pkt, MCTP_MESSAGE_CAPTURE_OUTGOING,
693f2988977SRashmica Gupta 			      mctp->capture_data);
6945d3d4e6dSAndrew Jeffery 
6954cdc200fSJeremy Kerr 	return bus->binding->tx(bus->binding, pkt);
6964cdc200fSJeremy Kerr }
6974cdc200fSJeremy Kerr 
mctp_send_tx_queue(struct mctp_bus * bus)698cc2458d9SJeremy Kerr static void mctp_send_tx_queue(struct mctp_bus *bus)
6991cd31184SJeremy Kerr {
7001cd31184SJeremy Kerr 	struct mctp_pktbuf *pkt;
7011cd31184SJeremy Kerr 
702cc2458d9SJeremy Kerr 	while ((pkt = bus->tx_queue_head)) {
7031cd31184SJeremy Kerr 		int rc;
7041cd31184SJeremy Kerr 
7051cd31184SJeremy Kerr 		rc = mctp_packet_tx(bus, pkt);
7060721f585SAndrew Jeffery 		switch (rc) {
7070721f585SAndrew Jeffery 		/* If transmission succeded, or */
7080721f585SAndrew Jeffery 		case 0:
7090721f585SAndrew Jeffery 		/* If the packet is somehow too large */
7100721f585SAndrew Jeffery 		case -EMSGSIZE:
7110721f585SAndrew Jeffery 			/* Drop the packet */
712cc2458d9SJeremy Kerr 			bus->tx_queue_head = pkt->next;
7131cd31184SJeremy Kerr 			mctp_pktbuf_free(pkt);
7140721f585SAndrew Jeffery 			break;
7150721f585SAndrew Jeffery 
7160721f585SAndrew Jeffery 		/* If the binding was busy, or */
7170721f585SAndrew Jeffery 		case -EBUSY:
7180721f585SAndrew Jeffery 		/* Some other unknown error occurred */
7190721f585SAndrew Jeffery 		default:
7200721f585SAndrew Jeffery 			/* Make sure the tail pointer is consistent and retry later */
7210721f585SAndrew Jeffery 			goto cleanup_tail;
7220721f585SAndrew Jeffery 		};
7231cd31184SJeremy Kerr 	}
7241cd31184SJeremy Kerr 
7250721f585SAndrew Jeffery cleanup_tail:
726cc2458d9SJeremy Kerr 	if (!bus->tx_queue_head)
727cc2458d9SJeremy Kerr 		bus->tx_queue_tail = NULL;
7281cd31184SJeremy Kerr }
7291cd31184SJeremy Kerr 
mctp_binding_set_tx_enabled(struct mctp_binding * binding,bool enable)7301cd31184SJeremy Kerr void mctp_binding_set_tx_enabled(struct mctp_binding *binding, bool enable)
7311cd31184SJeremy Kerr {
7321cd31184SJeremy Kerr 	struct mctp_bus *bus = binding->bus;
733c61501ccSAndrew Jeffery 
734c61501ccSAndrew Jeffery 	switch (bus->state) {
735c61501ccSAndrew Jeffery 	case mctp_bus_state_constructed:
736c61501ccSAndrew Jeffery 		if (!enable)
737c61501ccSAndrew Jeffery 			return;
738c61501ccSAndrew Jeffery 
7391fa707e2SAndrew Jeffery 		if (binding->pkt_size < MCTP_PACKET_SIZE(MCTP_BTU)) {
740a721c2d8SPatrick Williams 			mctp_prerr(
741a721c2d8SPatrick Williams 				"Cannot start %s binding with invalid MTU: %zu",
7421fa707e2SAndrew Jeffery 				binding->name,
7431fa707e2SAndrew Jeffery 				MCTP_BODY_SIZE(binding->pkt_size));
7441fa707e2SAndrew Jeffery 			return;
7451fa707e2SAndrew Jeffery 		}
7461fa707e2SAndrew Jeffery 
747c61501ccSAndrew Jeffery 		bus->state = mctp_bus_state_tx_enabled;
748c61501ccSAndrew Jeffery 		mctp_prinfo("%s binding started", binding->name);
749c61501ccSAndrew Jeffery 		return;
750c61501ccSAndrew Jeffery 	case mctp_bus_state_tx_enabled:
7511cd31184SJeremy Kerr 		if (enable)
752c61501ccSAndrew Jeffery 			return;
753c61501ccSAndrew Jeffery 
754c61501ccSAndrew Jeffery 		bus->state = mctp_bus_state_tx_disabled;
755c61501ccSAndrew Jeffery 		mctp_prdebug("%s binding Tx disabled", binding->name);
756c61501ccSAndrew Jeffery 		return;
757c61501ccSAndrew Jeffery 	case mctp_bus_state_tx_disabled:
758c61501ccSAndrew Jeffery 		if (!enable)
759c61501ccSAndrew Jeffery 			return;
760c61501ccSAndrew Jeffery 
761c61501ccSAndrew Jeffery 		bus->state = mctp_bus_state_tx_enabled;
762c61501ccSAndrew Jeffery 		mctp_prdebug("%s binding Tx enabled", binding->name);
763cc2458d9SJeremy Kerr 		mctp_send_tx_queue(bus);
764c61501ccSAndrew Jeffery 		return;
765c61501ccSAndrew Jeffery 	}
7661cd31184SJeremy Kerr }
7671cd31184SJeremy 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)768b93b6112SAndrew Jeffery static int mctp_message_tx_on_bus(struct mctp_bus *bus, mctp_eid_t src,
769f39c3857SSumanth Bhat 				  mctp_eid_t dest, bool tag_owner,
770f39c3857SSumanth Bhat 				  uint8_t msg_tag, void *msg, size_t msg_len)
7714cdc200fSJeremy Kerr {
772df15f7e9SJeremy Kerr 	size_t max_payload_len, payload_len, p;
7731cd31184SJeremy Kerr 	struct mctp_pktbuf *pkt;
7744cdc200fSJeremy Kerr 	struct mctp_hdr *hdr;
775c855d7b2SJeremy Kerr 	int i;
7764cdc200fSJeremy Kerr 
777c61501ccSAndrew Jeffery 	if (bus->state == mctp_bus_state_constructed)
778c61501ccSAndrew Jeffery 		return -ENXIO;
779c61501ccSAndrew Jeffery 
780f39c3857SSumanth Bhat 	if ((msg_tag & MCTP_HDR_TAG_MASK) != msg_tag)
781f39c3857SSumanth Bhat 		return -EINVAL;
782f39c3857SSumanth Bhat 
7831fa707e2SAndrew Jeffery 	max_payload_len = MCTP_BODY_SIZE(bus->binding->pkt_size);
7841fa707e2SAndrew Jeffery 
7851fa707e2SAndrew Jeffery 	{
7861fa707e2SAndrew Jeffery 		const bool valid_mtu = max_payload_len >= MCTP_BTU;
7871fa707e2SAndrew Jeffery 		assert(valid_mtu);
7881fa707e2SAndrew Jeffery 		if (!valid_mtu)
7891fa707e2SAndrew Jeffery 			return -EINVAL;
7901fa707e2SAndrew Jeffery 	}
7914cdc200fSJeremy Kerr 
792a721c2d8SPatrick Williams 	mctp_prdebug(
793a721c2d8SPatrick Williams 		"%s: Generating packets for transmission of %zu byte message from %hhu to %hhu",
794298865fcSAndrew Jeffery 		__func__, msg_len, src, dest);
795298865fcSAndrew Jeffery 
79624db71fbSJeremy Kerr 	/* queue up packets, each of max MCTP_MTU size */
797c855d7b2SJeremy Kerr 	for (p = 0, i = 0; p < msg_len; i++) {
798df15f7e9SJeremy Kerr 		payload_len = msg_len - p;
799df15f7e9SJeremy Kerr 		if (payload_len > max_payload_len)
800df15f7e9SJeremy Kerr 			payload_len = max_payload_len;
80124db71fbSJeremy Kerr 
8021a4ec3cdSJeremy Kerr 		pkt = mctp_pktbuf_alloc(bus->binding,
8031a4ec3cdSJeremy Kerr 					payload_len + sizeof(*hdr));
8044cdc200fSJeremy Kerr 		hdr = mctp_pktbuf_hdr(pkt);
8054cdc200fSJeremy Kerr 
8064cdc200fSJeremy Kerr 		hdr->ver = bus->binding->version & 0xf;
8071a4ec3cdSJeremy Kerr 		hdr->dest = dest;
8081a4ec3cdSJeremy Kerr 		hdr->src = src;
809f39c3857SSumanth Bhat 		hdr->flags_seq_tag = (tag_owner << MCTP_HDR_TO_SHIFT) |
810f39c3857SSumanth Bhat 				     (msg_tag << MCTP_HDR_TAG_SHIFT);
8114cdc200fSJeremy Kerr 
812c855d7b2SJeremy Kerr 		if (i == 0)
813c855d7b2SJeremy Kerr 			hdr->flags_seq_tag |= MCTP_HDR_FLAG_SOM;
814df15f7e9SJeremy Kerr 		if (p + payload_len >= msg_len)
815c855d7b2SJeremy Kerr 			hdr->flags_seq_tag |= MCTP_HDR_FLAG_EOM;
816a721c2d8SPatrick Williams 		hdr->flags_seq_tag |= (i & MCTP_HDR_SEQ_MASK)
817a721c2d8SPatrick Williams 				      << MCTP_HDR_SEQ_SHIFT;
818c855d7b2SJeremy Kerr 
8197aaccb51SMoritz Fischer 		memcpy(mctp_pktbuf_data(pkt), (uint8_t *)msg + p, payload_len);
8204cdc200fSJeremy Kerr 
82124db71fbSJeremy Kerr 		/* add to tx queue */
822cc2458d9SJeremy Kerr 		if (bus->tx_queue_tail)
823cc2458d9SJeremy Kerr 			bus->tx_queue_tail->next = pkt;
82424db71fbSJeremy Kerr 		else
825cc2458d9SJeremy Kerr 			bus->tx_queue_head = pkt;
826cc2458d9SJeremy Kerr 		bus->tx_queue_tail = pkt;
8274cdc200fSJeremy Kerr 
828df15f7e9SJeremy Kerr 		p += payload_len;
82924db71fbSJeremy Kerr 	}
83024db71fbSJeremy Kerr 
831298865fcSAndrew Jeffery 	mctp_prdebug("%s: Enqueued %d packets", __func__, i);
832298865fcSAndrew Jeffery 
833cc2458d9SJeremy Kerr 	mctp_send_tx_queue(bus);
83424db71fbSJeremy Kerr 
83524db71fbSJeremy Kerr 	return 0;
8364cdc200fSJeremy Kerr }
8371a4ec3cdSJeremy Kerr 
mctp_message_tx(struct mctp * mctp,mctp_eid_t eid,bool tag_owner,uint8_t msg_tag,void * msg,size_t msg_len)838f39c3857SSumanth Bhat int mctp_message_tx(struct mctp *mctp, mctp_eid_t eid, bool tag_owner,
839f39c3857SSumanth Bhat 		    uint8_t msg_tag, void *msg, size_t msg_len)
8401a4ec3cdSJeremy Kerr {
8411a4ec3cdSJeremy Kerr 	struct mctp_bus *bus;
8421a4ec3cdSJeremy Kerr 
843f39c3857SSumanth Bhat 	/* TODO: Protect against same tag being used across
844f39c3857SSumanth Bhat 	 * different callers */
845f39c3857SSumanth Bhat 	if ((msg_tag & MCTP_HDR_TAG_MASK) != msg_tag) {
846f39c3857SSumanth Bhat 		mctp_prerr("Incorrect message tag %u passed.", msg_tag);
847f39c3857SSumanth Bhat 		return -EINVAL;
848f39c3857SSumanth Bhat 	}
849f39c3857SSumanth Bhat 
8501a4ec3cdSJeremy Kerr 	bus = find_bus_for_eid(mctp, eid);
851663ec39eSBrad Bishop 	if (!bus)
852663ec39eSBrad Bishop 		return 0;
853663ec39eSBrad Bishop 
854f39c3857SSumanth Bhat 	return mctp_message_tx_on_bus(bus, bus->eid, eid, tag_owner, msg_tag,
855f39c3857SSumanth Bhat 				      msg, msg_len);
8561a4ec3cdSJeremy Kerr }
857