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