From c3c5f1e5fe27574220c3e94a79048e066cacc121 Mon Sep 17 00:00:00 2001 From: Tomasz Kramkowski Date: Thu, 20 Oct 2016 23:43:29 +0100 Subject: Init commit --- usb/endpt0.c | 199 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 usb/endpt0.c (limited to 'usb/endpt0.c') 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 +#include +#include +#include +#include + +#include + +#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); +} -- cgit v1.2.3-54-g00ecf