#include #include #include #include #include #include #include #include "bdt.h" #include "endpt0.h" #include "descriptors.h" #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(ds_dev, trunc(ARRLEN(ds_dev), setup->length)); return; case 0x0200: /* CONFIGURATION */ quetx(ds_conf, trunc(ARRLEN(ds_conf), setup->length)); return; case 0x0300: /* STRING 0 */ quetx(ds_lang, trunc(ARRLEN(ds_lang), setup->length)); return; case 0x0301: /* STRING 1 */ quetx(ds_str1, trunc(ARRLEN(ds_str1), 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); }