summaryrefslogtreecommitdiffstats
path: root/usb/endpt0.c
diff options
context:
space:
mode:
authorTomasz Kramkowski <tk@the-tk.com>2016-10-20 23:43:29 +0100
committerTomasz Kramkowski <tk@the-tk.com>2016-10-20 23:43:29 +0100
commitc3c5f1e5fe27574220c3e94a79048e066cacc121 (patch)
tree445096a0365cc45c959091355852c9b63d8a338a /usb/endpt0.c
downloadfmk-c3c5f1e5fe27574220c3e94a79048e066cacc121.tar.gz
fmk-c3c5f1e5fe27574220c3e94a79048e066cacc121.tar.xz
fmk-c3c5f1e5fe27574220c3e94a79048e066cacc121.zip
Init commit
Diffstat (limited to 'usb/endpt0.c')
-rw-r--r--usb/endpt0.c199
1 files changed, 199 insertions, 0 deletions
diff --git a/usb/endpt0.c b/usb/endpt0.c
new file mode 100644
index 0000000..a60e1a4
--- /dev/null
+++ b/usb/endpt0.c
@@ -0,0 +1,199 @@
+#include <le.h>
+#include <reg/usbotg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <reg/gpio.h>
+
+#include "bdt.h"
+#include "endpt0.h"
+
+#include "ds_conf.xxh"
+#include "ds_dev.xxh"
+#include "ds_lang.xxh"
+#include "ds_str1.xxh"
+
+#define MAX_PACKET 64
+static unsigned char buf[2][MAX_PACKET];
+static bool odd;
+static bool data01;
+
+static volatile unsigned int nextaddr;
+
+static const void *tx_data;
+static size_t tx_size;
+
+struct tok_setup {
+ uint8_t reqtyp;
+ uint8_t req;
+ uint16_t value;
+ uint16_t index;
+ uint16_t length;
+};
+
+/* read_setup: parse a SETUP token's data */
+static void read_setup(struct tok_setup *setup, const void *_data)
+{
+ const unsigned char *data = _data;
+
+ setup->reqtyp = data[0];
+ setup->req = data[1];
+ setup->value = le16toh(&data[2]);
+ setup->index = le16toh(&data[4]);
+ setup->length = le16toh(&data[6]);
+}
+
+/* TODO: Make this a shared thing across all USB endpoints */
+
+/* puttx: place data in the current buffer descriptor */
+static bool puttx(const void *data, size_t size)
+{
+ if (GET_BIT(BDT_ENDPT(0, BDT_TX, odd).desc, BD_OWN))
+ return false;
+
+ BDT_ENDPT(0, BDT_TX, odd).addr = (void *)data;
+ BDT_ENDPT(0, BDT_TX, odd).desc = USB0_BD_INIT(size, data01);
+ odd = !odd;
+ data01 = !data01;
+
+ return true;
+}
+
+/* pushtx: attempt to push rest of the current transmission into a BD */
+static bool pushtx(void)
+{
+ size_t size = tx_size;
+
+ if (tx_data == NULL)
+ return false;
+
+ if (size > MAX_PACKET)
+ size = MAX_PACKET;
+
+ if (!puttx(tx_data, size))
+ return false;
+
+ tx_data = (const char *)tx_data + size;
+ tx_size -= size;
+
+ if (tx_size == 0 && size < MAX_PACKET)
+ tx_data = NULL;
+
+ return true;
+}
+
+/* quetx: enqueue a transmission */
+static void quetx(const void *data, size_t size)
+{
+ tx_data = data;
+ tx_size = size;
+
+ while (pushtx())
+ ;
+}
+
+/* usb_endpt0_disable: Disable endpoint 0 (not valid) */
+void usb_endpt0_disable(void)
+{
+}
+
+/* usb_endpt0_enable: Enable and reset endpoint 0 */
+void usb_endpt0_enable(void)
+{
+ BDT_ENDPT(0, BDT_RX, BDT_EVEN).addr = buf[0];
+ BDT_ENDPT(0, BDT_RX, BDT_EVEN).desc = USB0_BD_INIT(sizeof buf[0], 0);
+
+ BDT_ENDPT(0, BDT_RX, BDT_ODD).addr = buf[1];
+ BDT_ENDPT(0, BDT_RX, BDT_ODD).desc = USB0_BD_INIT(sizeof buf[1], 0);
+
+ BDT_ENDPT(0, BDT_TX, BDT_EVEN).addr = NULL;
+ BDT_ENDPT(0, BDT_TX, BDT_EVEN).desc = 0;
+
+ BDT_ENDPT(0, BDT_TX, BDT_ODD).addr = NULL;
+ BDT_ENDPT(0, BDT_TX, BDT_ODD).desc = 0;
+
+ USB0_ENDPT(0) = BV(ENDPT_EPRXEN) | BV(ENDPT_EPTXEN) | BV(ENDPT_EPHSHK);
+ nextaddr = 0;
+ tx_data = NULL;
+ odd = 0;
+ data01 = 0;
+}
+
+/* trunc: truncate size_t to a limit TODO: MOVE THIS */
+static inline size_t trunc(size_t val, size_t max)
+{
+ if (val > max)
+ return max;
+ return val;
+}
+
+/* tok_setup: process a setup token */
+static void tok_setup(struct tok_setup *setup)
+{
+ switch (setup->reqtyp << 8 | setup->req) {
+ case 0x0005: /* SET ADDRESS */
+ nextaddr = setup->value;
+ puttx(NULL, 0);
+ break;
+ case 0x0009: /* SET CONFIGURATION */
+ puttx(NULL, 0);
+ break;
+ case 0x8006: /* GET DESCRIPTOR */
+ switch (setup->value) {
+ case 0x0100: /* DEVICE */
+ quetx(usb_ds_dev, trunc(usb_ds_dev_len, setup->length));
+ return;
+ case 0x0200: /* CONFIGURATION */
+ quetx(usb_ds_conf, trunc(usb_ds_conf_len, setup->length));
+ return;
+ case 0x0300: /* STRING 0 */
+ quetx(usb_ds_lang, trunc(usb_ds_lang_len, setup->length));
+ return;
+ case 0x0301: /* STRING 1 */
+ quetx(usb_ds_str1, trunc(usb_ds_str1_len, setup->length));
+ return;
+ }
+ /* fall through */
+ default:
+ SET_BIT(USB0_ENDPT(0), ENDPT_EPSTALL);
+ break;
+ }
+}
+
+void usb_endpt0_token(uint8_t state)
+{
+ struct usb0_bd *bd;
+ struct tok_setup setup;
+
+ bd = &BDT_ENDPT(0, GET_BIT(state, STAT_TX), GET_BIT(state, STAT_ODD));
+
+
+ switch (GET_BITS(bd->desc, BD_TOK_PID)) {
+ case BD_TOK_PID_OUT:
+ bd->desc = USB0_BD_INIT(sizeof buf[0], 1);
+ break;
+ case BD_TOK_PID_IN:
+ pushtx();
+ if (nextaddr) {
+ USB0_ADDR = nextaddr;
+ nextaddr = 0;
+ }
+ break;
+ case BD_TOK_PID_SETUP:
+ read_setup(&setup, bd->addr);
+ bd->desc = USB0_BD_INIT(sizeof buf[0], 1);
+
+ BDT_ENDPT(0, BDT_TX, BDT_EVEN).desc = 0;
+ BDT_ENDPT(0, BDT_TX, BDT_ODD).desc = 0;
+ tx_data = NULL;
+
+ data01 = 1;
+
+ tok_setup(&setup);
+
+ break;
+ }
+
+ USB0_CTL = BV(CTL_USBENSOFEN);
+}