summaryrefslogtreecommitdiff
path: root/tools/src/afk.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/src/afk.c')
-rw-r--r--tools/src/afk.c545
1 files changed, 545 insertions, 0 deletions
diff --git a/tools/src/afk.c b/tools/src/afk.c
new file mode 100644
index 0000000..7191a21
--- /dev/null
+++ b/tools/src/afk.c
@@ -0,0 +1,545 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "afk.h"
+#include "assert.h"
+#include "malloc.h"
+#include "string.h"
+#include "utils.h"
+
+struct afk_rb_hdr {
+ u32 bufsz;
+ u32 unk;
+ u32 _pad1[14];
+ u32 rptr;
+ u32 _pad2[15];
+ u32 wptr;
+ u32 _pad3[15];
+};
+
+struct afk_rb {
+ bool ready;
+ struct afk_rb_hdr *hdr;
+ u32 rptr;
+ void *buf;
+ size_t bufsz;
+};
+
+enum EPICType {
+ TYPE_NOTIFY = 0,
+ TYPE_COMMAND = 3,
+ TYPE_REPLY = 4,
+ TYPE_NOTIFY_ACK = 8,
+};
+
+enum EPICCategory {
+ CAT_REPORT = 0x00,
+ CAT_NOTIFY = 0x10,
+ CAT_REPLY = 0x20,
+ CAT_COMMAND = 0x30,
+};
+
+enum EPICMessage {
+ CODE_ANNOUNCE = 0x30,
+};
+
+struct afk_qe {
+ u32 magic;
+ u32 size;
+ u32 channel;
+ u32 type;
+ u8 data[];
+};
+
+struct epic_hdr {
+ u8 version;
+ u16 seq;
+ u8 _pad;
+ u32 unk;
+ u64 timestamp;
+} PACKED;
+
+struct epic_sub_hdr {
+ u32 length;
+ u8 version;
+ u8 category;
+ u16 code;
+ u64 timestamp;
+ u16 seq;
+ u16 unk;
+ u32 unk2;
+} PACKED;
+
+struct epic_announce {
+ char name[32];
+ u8 props[];
+} PACKED;
+
+struct epic_cmd {
+ u32 retcode;
+ u64 rxbuf;
+ u64 txbuf;
+ u32 rxlen;
+ u32 txlen;
+} PACKED;
+
+struct afk_epic_ep {
+ int ep;
+ rtkit_dev_t *rtk;
+
+ struct rtkit_buffer buf;
+ u16 tag;
+
+ struct afk_rb tx;
+ struct afk_rb rx;
+
+ struct rtkit_buffer txbuf;
+ struct rtkit_buffer rxbuf;
+
+ bool started;
+};
+
+enum RBEP_MSG {
+ RBEP_INIT = 0x80,
+ RBEP_INIT_ACK = 0xa0,
+ RBEP_GETBUF = 0x89,
+ RBEP_GETBUF_ACK = 0xa1,
+ RBEP_INIT_TX = 0x8a,
+ RBEP_INIT_RX = 0x8b,
+ RBEP_START = 0xa3,
+ RBEP_START_ACK = 0x86,
+ RBEP_SEND = 0xa2,
+ RBEP_RECV = 0x85,
+ RBEP_SHUTDOWN = 0xc0,
+ RBEP_SHUTDOWN_ACK = 0xc1,
+};
+
+#define BLOCK_SHIFT 6
+#define QE_MAGIC ' POI'
+
+#define RBEP_TYPE GENMASK(63, 48)
+
+#define GETBUF_SIZE GENMASK(31, 16)
+#define GETBUF_TAG GENMASK(15, 0)
+#define GETBUF_ACK_DVA GENMASK(47, 0)
+
+#define INITRB_OFFSET GENMASK(47, 32)
+#define INITRB_SIZE GENMASK(31, 16)
+#define INITRB_TAG GENMASK(15, 0)
+
+#define SEND_WPTR GENMASK(31, 0)
+
+bool afk_rb_init(afk_epic_ep_t *epic, struct afk_rb *rb, u64 base, u64 size)
+{
+ rb->hdr = epic->buf.bfr + base;
+
+ if (rb->hdr->bufsz + sizeof(*rb->hdr) != size) {
+ printf("AFK: ring buffer size mismatch\n");
+ return false;
+ }
+
+ rb->buf = rb->hdr + 1;
+ rb->bufsz = rb->hdr->bufsz;
+ rb->ready = true;
+
+ return true;
+}
+
+static int afk_epic_poll(afk_epic_ep_t *epic)
+{
+ int ret;
+ struct rtkit_message msg;
+
+ while ((ret = rtkit_recv(epic->rtk, &msg)) == 0)
+ ;
+
+ if (ret < 0) {
+ printf("EPIC: rtkit_recv failed!\n");
+ return ret;
+ }
+
+ if (msg.ep != epic->ep) {
+ printf("EPIC: received message for unexpected endpoint %d\n", msg.ep);
+ return 0;
+ }
+
+ int type = FIELD_GET(RBEP_TYPE, msg.msg);
+ u64 base, size, tag;
+ switch (type) {
+ case RBEP_INIT_ACK:
+ break;
+
+ case RBEP_GETBUF:
+ size = FIELD_GET(GETBUF_SIZE, msg.msg) << BLOCK_SHIFT;
+ epic->tag = FIELD_GET(GETBUF_TAG, msg.msg);
+ if (!rtkit_alloc_buffer(epic->rtk, &epic->buf, size)) {
+ printf("EPIC: failed to allocate buffer\n");
+ return -1;
+ }
+ msg.msg = (FIELD_PREP(RBEP_TYPE, RBEP_GETBUF_ACK) |
+ FIELD_PREP(GETBUF_ACK_DVA, epic->buf.dva));
+ if (!rtkit_send(epic->rtk, &msg)) {
+ printf("EPIC: failed to send buffer address\n");
+ return -1;
+ }
+ break;
+
+ case RBEP_INIT_TX:
+ case RBEP_INIT_RX:
+ base = FIELD_GET(INITRB_OFFSET, msg.msg) << BLOCK_SHIFT;
+ size = FIELD_GET(INITRB_SIZE, msg.msg) << BLOCK_SHIFT;
+ tag = FIELD_GET(INITRB_TAG, msg.msg);
+ if (tag != epic->tag) {
+ printf("EPIC: wrong tag (0x%x != 0x%lx)\n", epic->tag, tag);
+ return -1;
+ }
+
+ struct afk_rb *rb;
+ if (type == RBEP_INIT_RX)
+ rb = &epic->rx;
+ else
+ rb = &epic->tx;
+
+ if (!afk_rb_init(epic, rb, base, size))
+ return -1;
+
+ if (epic->rx.ready && epic->tx.ready) {
+ msg.msg = FIELD_PREP(RBEP_TYPE, RBEP_START);
+ if (!rtkit_send(epic->rtk, &msg)) {
+ printf("EPIC: failed to send start\n");
+ return -1;
+ }
+ }
+ break;
+
+ case RBEP_RECV:
+ return 1;
+
+ case RBEP_START_ACK:
+ epic->started = true;
+ break;
+
+ case RBEP_SHUTDOWN_ACK:
+ epic->started = false;
+ break;
+
+ default:
+ printf("EPIC: received unknown message type 0x%x\n", type);
+ return 0;
+ break;
+ }
+
+ return 0;
+}
+
+static int afk_epic_rx(afk_epic_ep_t *epic, struct afk_qe **qe)
+{
+ int ret;
+ struct afk_rb *rb = &epic->rx;
+
+ u32 rptr = rb->hdr->rptr;
+
+ while (rptr == rb->hdr->wptr) {
+ do {
+ ret = afk_epic_poll(epic);
+ if (ret < 0)
+ return ret;
+ } while (ret == 0);
+ dma_rmb();
+ }
+
+ struct afk_qe *hdr = rb->buf + rptr;
+
+ if (hdr->magic != QE_MAGIC) {
+ printf("EPIC: bad queue entry magic!\n");
+ return -1;
+ }
+
+ if (rptr + hdr->size > rb->bufsz) {
+ rptr = 0;
+ hdr = rb->buf + rptr;
+ if (hdr->magic != QE_MAGIC) {
+ printf("EPIC: bad queue entry magic!\n");
+ return -1;
+ }
+ rb->hdr->rptr = rptr;
+ }
+
+ *qe = hdr;
+
+ return 1;
+}
+
+static int afk_epic_tx(afk_epic_ep_t *epic, u32 channel, u32 type, void *data, size_t size)
+{
+ struct afk_rb *rb = &epic->tx;
+
+ u32 rptr = rb->hdr->rptr;
+ u32 wptr = rb->hdr->wptr;
+ struct afk_qe *hdr = rb->buf + wptr;
+
+ if (wptr < rptr && (wptr + sizeof(struct afk_qe) > rptr)) {
+ printf("EPIC: TX ring buffer is full\n");
+ return -1;
+ }
+
+ hdr->magic = QE_MAGIC;
+ hdr->channel = channel;
+ hdr->type = type;
+ hdr->size = size;
+
+ wptr += sizeof(struct afk_qe);
+
+ if (size > rb->bufsz - wptr) {
+ if (rptr < sizeof(struct afk_qe)) {
+ printf("EPIC: TX ring buffer is full\n");
+ return -1;
+ }
+ *(struct afk_qe *)rb->buf = *hdr;
+ hdr = rb->buf;
+ wptr = sizeof(struct afk_qe);
+ }
+
+ if (wptr < rptr && (wptr + size > rptr)) {
+ printf("EPIC: TX ring buffer is full\n");
+ return -1;
+ }
+
+ wptr += size;
+ wptr = ALIGN_UP(wptr, 1 << BLOCK_SHIFT);
+
+ memcpy(hdr + 1, data, size);
+
+ dma_mb();
+ rb->hdr->wptr = wptr;
+ dma_wmb();
+
+ struct rtkit_message msg = {
+ epic->ep,
+ FIELD_PREP(RBEP_TYPE, RBEP_SEND) | FIELD_PREP(SEND_WPTR, wptr),
+ };
+
+ if (!rtkit_send(epic->rtk, &msg)) {
+ printf("EPIC: failed to send TX WPTR message\n");
+ return -1;
+ }
+
+ return 1;
+}
+
+static void afk_epic_rx_ack(afk_epic_ep_t *epic)
+{
+ struct afk_rb *rb = &epic->rx;
+ u32 rptr = rb->hdr->rptr;
+ struct afk_qe *hdr = rb->buf + rptr;
+
+ if (hdr->magic != QE_MAGIC) {
+ printf("EPIC: bad queue entry magic!\n");
+ }
+
+ dma_mb();
+
+ rptr = ALIGN_UP(rptr + sizeof(*hdr) + hdr->size, 1 << BLOCK_SHIFT);
+ assert(rptr < rb->bufsz);
+ if (rptr == rb->bufsz)
+ rptr = 0;
+ rb->hdr->rptr = rptr;
+}
+
+int afk_epic_command(afk_epic_ep_t *epic, int channel, u16 code, void *txbuf, size_t txsize,
+ void *rxbuf, size_t *rxsize)
+{
+ struct {
+ struct epic_hdr hdr;
+ struct epic_sub_hdr sub;
+ struct epic_cmd cmd;
+ } PACKED msg;
+
+ assert(txsize <= epic->txbuf.sz);
+ assert(!rxsize || *rxsize <= epic->rxbuf.sz);
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.hdr.version = 2;
+ msg.hdr.seq = 0;
+ msg.sub.length = sizeof(msg.cmd);
+ msg.sub.version = 3;
+ msg.sub.category = CAT_COMMAND;
+ msg.sub.code = code;
+ msg.sub.seq = 0;
+ msg.cmd.txbuf = epic->txbuf.dva;
+ msg.cmd.txlen = txsize;
+ msg.cmd.rxbuf = epic->rxbuf.dva;
+ msg.cmd.rxlen = rxsize ? *rxsize : 0;
+
+ memcpy(epic->txbuf.bfr, txbuf, txsize);
+
+ int ret = afk_epic_tx(epic, channel, TYPE_COMMAND, &msg, sizeof msg);
+ if (ret < 0) {
+ printf("EPIC: failed to transmit command\n");
+ return ret;
+ }
+
+ struct afk_qe *rmsg;
+ struct epic_cmd *rcmd;
+
+ while (true) {
+ ret = afk_epic_rx(epic, &rmsg);
+ if (ret < 0)
+ return ret;
+
+ if (rmsg->type != TYPE_REPLY && rmsg->type != TYPE_NOTIFY) {
+ printf("EPIC: got unexpected message type %d during command\n", rmsg->type);
+ afk_epic_rx_ack(epic);
+ continue;
+ }
+
+ struct epic_hdr *hdr = (void *)(rmsg + 1);
+ struct epic_sub_hdr *sub = (void *)(hdr + 1);
+
+ if (sub->category != CAT_REPLY || sub->code != code) {
+ printf("EPIC: got unexpected message %02x:%04x during command\n", sub->category,
+ sub->code);
+ afk_epic_rx_ack(epic);
+ continue;
+ }
+
+ rcmd = (void *)(sub + 1);
+ break;
+ }
+
+ if (rcmd->retcode != 0) {
+ printf("EPIC: IOP returned 0x%x\n", rcmd->retcode);
+ afk_epic_rx_ack(epic);
+ return rcmd->retcode; // should be negative already
+ }
+
+ assert(*rxsize >= rcmd->rxlen);
+ *rxsize = rcmd->rxlen;
+
+ if (rxsize && *rxsize && rcmd->rxbuf)
+ memcpy(rxbuf, epic->rxbuf.bfr, *rxsize);
+
+ afk_epic_rx_ack(epic);
+
+ return 0;
+}
+
+afk_epic_ep_t *afk_epic_init(rtkit_dev_t *rtk, int endpoint)
+{
+ afk_epic_ep_t *epic = malloc(sizeof(afk_epic_ep_t));
+ if (!epic)
+ return NULL;
+
+ memset(epic, 0, sizeof(*epic));
+ epic->ep = endpoint;
+ epic->rtk = rtk;
+
+ if (!rtkit_start_ep(rtk, endpoint)) {
+ printf("EPIC: failed to start endpoint %d\n", endpoint);
+ goto err;
+ }
+
+ struct rtkit_message msg = {endpoint, FIELD_PREP(RBEP_TYPE, RBEP_INIT)};
+ if (!rtkit_send(rtk, &msg)) {
+ printf("EPIC: failed to send init message\n");
+ goto err;
+ }
+
+ while (!epic->started) {
+ int ret = afk_epic_poll(epic);
+ if (ret < 0)
+ break;
+ else if (ret > 0)
+ printf("EPIC: received unexpected message during init\n");
+ }
+
+ return epic;
+
+err:
+ free(epic);
+ return NULL;
+}
+
+int afk_epic_shutdown(afk_epic_ep_t *epic)
+{
+ struct rtkit_message msg = {epic->ep, FIELD_PREP(RBEP_TYPE, RBEP_SHUTDOWN)};
+ if (!rtkit_send(epic->rtk, &msg)) {
+ printf("EPIC: failed to send shutdown message\n");
+ return -1;
+ }
+
+ while (epic->started) {
+ int ret = afk_epic_poll(epic);
+ if (ret < 0)
+ break;
+ }
+
+ rtkit_free_buffer(epic->rtk, &epic->buf);
+ rtkit_free_buffer(epic->rtk, &epic->rxbuf);
+ rtkit_free_buffer(epic->rtk, &epic->txbuf);
+
+ free(epic);
+ return 0;
+}
+
+int afk_epic_start_interface(afk_epic_ep_t *epic, char *name, size_t txsize, size_t rxsize)
+{
+ int channel = -1;
+ struct afk_qe *msg;
+ struct epic_announce *announce;
+
+ for (int tries = 0; tries < 20; tries += 1) {
+
+ int ret = afk_epic_rx(epic, &msg);
+ if (ret < 0)
+ return ret;
+
+ if (msg->type != TYPE_NOTIFY) {
+ printf("EPIC: got unexpected message type %d during iface start\n", msg->type);
+ afk_epic_rx_ack(epic);
+ continue;
+ }
+
+ struct epic_hdr *hdr = (void *)(msg + 1);
+ struct epic_sub_hdr *sub = (void *)(hdr + 1);
+
+ if (sub->category != CAT_REPORT || sub->code != CODE_ANNOUNCE) {
+ printf("EPIC: got unexpected message %02x:%04x during iface start\n", sub->category,
+ sub->code);
+ afk_epic_rx_ack(epic);
+ continue;
+ }
+
+ announce = (void *)(sub + 1);
+
+ if (strncmp(name, announce->name, sizeof(announce->name))) {
+ printf("EPIC: ignoring channel %d: %s\n", msg->channel, announce->name);
+ afk_epic_rx_ack(epic);
+ continue;
+ }
+
+ channel = msg->channel;
+ break;
+ }
+
+ if (channel == -1) {
+ printf("EPIC: too many unexpected messages, giving up\n");
+ return -1;
+ }
+
+ if (!rtkit_alloc_buffer(epic->rtk, &epic->rxbuf, rxsize)) {
+ printf("EPIC: failed to allocate rx buffer\n");
+ return -1;
+ }
+
+ if (!rtkit_alloc_buffer(epic->rtk, &epic->txbuf, txsize)) {
+ printf("EPIC: failed to allocate tx buffer\n");
+ return -1;
+ }
+
+ printf("EPIC: started interface %d (%s)\n", msg->channel, announce->name);
+
+ afk_epic_rx_ack(epic);
+
+ return channel;
+}