diff options
Diffstat (limited to 'usb')
-rw-r--r-- | usb/bdt.h | 14 | ||||
-rw-r--r-- | usb/ds_conf.xxd | 34 | ||||
-rw-r--r-- | usb/ds_dev.xxd | 18 | ||||
-rw-r--r-- | usb/ds_lang.xxd | 4 | ||||
-rw-r--r-- | usb/ds_str1.xxd | 22 | ||||
-rw-r--r-- | usb/endpt0.c | 199 | ||||
-rw-r--r-- | usb/endpt0.h | 10 | ||||
-rw-r--r-- | usb/endpt1.c | 36 | ||||
-rw-r--r-- | usb/endpt1.h | 8 | ||||
-rw-r--r-- | usb/usb.c | 94 | ||||
-rw-r--r-- | usb/usb.h | 7 |
11 files changed, 446 insertions, 0 deletions
diff --git a/usb/bdt.h b/usb/bdt.h new file mode 100644 index 0000000..5f0a123 --- /dev/null +++ b/usb/bdt.h @@ -0,0 +1,14 @@ +#ifndef USB_BDT_H +#define USB_BDT_H + +#include <reg/usbotg.h> + +extern struct usb0_bd usb_bdt[4 * 16]; + +#define BDT_ENDPT(n, tx, odd) (usb_bdt[(n) << 2 | (tx & 1) << 1 | (odd & 1)]) +#define BDT_RX 0 +#define BDT_TX 1 +#define BDT_EVEN 0 +#define BDT_ODD 1 + +#endif /* USB_BDT_H */ diff --git a/usb/ds_conf.xxd b/usb/ds_conf.xxd new file mode 100644 index 0000000..89c6e16 --- /dev/null +++ b/usb/ds_conf.xxd @@ -0,0 +1,34 @@ +09 1 Length - 9 +02 1 Descriptor Type - 2 (Configuration) +22 2 Total Length - 32 +00 +01 1 Number of Interfaces - 1 +01 1 Configuration Value - 1 +00 1 Configuration String - 0 +c0 1 Attributes - none +32 1 Maximum Current - 100mA +09 1 Length - 9 +04 1 Descriptor Type - 4 (Interface) +00 1 Interface Number - 0 +00 1 Alternate Setting - 0 +01 1 Number of Endpoints - 1 +03 1 Interface Class - 3 (HID) +01 1 Interface Sub Class - 1 (Boot) +02 1 Interface Protocol - 1 (Keyboard) +00 1 Interface String - 0 +09 1 Length (HID Descriptor) - 9 +21 1 Descriptor Type - 33 (HID) +11 2 HID Class Spec Version - 1.1 +01 +00 1 Country Code - N/A +01 1 Number of Descriptors - 1 +22 1 Descriptor Type - 34 (REPORT) +34 2 Descriptor Length - 52 +00 +07 1 Length - 7 +05 1 Descriptor Type - 5 (Endpoint) +81 1 Endpoint Address - 1 IN +03 1 Attributes - Interrupt; Data Endpoint +08 2 Maximum packet size - 8 +00 +40 1 Interval - 60 Frames diff --git a/usb/ds_dev.xxd b/usb/ds_dev.xxd new file mode 100644 index 0000000..306da63 --- /dev/null +++ b/usb/ds_dev.xxd @@ -0,0 +1,18 @@ +12 1 Length - 18 +01 1 Descriptor type - 1 +00 2 USB Release - 2.0 +02 +ff 1 Device Class - Interface Specific +00 1 Device Sub Class - Interface Specific +00 1 Device Protocol - Interface Specific +40 1 Max Packet Size - 64 +c0 2 VID (0x16c0) +16 +dc 2 PID (0x047c) +05 +01 2 Device Release BCD - 0.01 +00 +01 1 Manufacturer String - 1 +00 1 Product String - 0 +00 1 Serial Number String - 0 +01 1 Number of Configurations - 1 diff --git a/usb/ds_lang.xxd b/usb/ds_lang.xxd new file mode 100644 index 0000000..e6e334c --- /dev/null +++ b/usb/ds_lang.xxd @@ -0,0 +1,4 @@ +04 1 Length - 4 +03 1 Type - 3 (Language) +09 2 LANGID - EN US +04 diff --git a/usb/ds_str1.xxd b/usb/ds_str1.xxd new file mode 100644 index 0000000..1aea777 --- /dev/null +++ b/usb/ds_str1.xxd @@ -0,0 +1,22 @@ +16 1 Length - 22 +03 1 Type - 3 (String) +74 20 - the-tk.com +00 +68 +00 +65 +00 +2d +00 +74 +00 +6b +00 +2e +00 +63 +00 +6f +00 +6d +00 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); +} diff --git a/usb/endpt0.h b/usb/endpt0.h new file mode 100644 index 0000000..4ba89ed --- /dev/null +++ b/usb/endpt0.h @@ -0,0 +1,10 @@ +#ifndef USB_ENDPT0_H +#define USB_ENDPT0_H + +#include <stdint.h> + +void usb_endpt0_disable(void); +void usb_endpt0_enable(void); +void usb_endpt0_token(uint8_t state); + +#endif /* USB_ENDPT0_H */ diff --git a/usb/endpt1.c b/usb/endpt1.c new file mode 100644 index 0000000..b29c3bb --- /dev/null +++ b/usb/endpt1.c @@ -0,0 +1,36 @@ +#include <reg/usbotg.h> +#include <stdint.h> + +#include "bdt.h" +#include "endpt1.h" + +#define MAX_PACKET 64 + +static unsigned char buf[2][MAX_PACKET]; + +void usb_endpt1_enable(void) +{ +} + +void usb_endpt1_disable(void) +{ +} + +void usb_endpt1_token(uint8_t state) +{ + struct usb0_bd *bd; + + bd = &BDT_ENDPT(1, 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); + /* should never happen */ + break; + case BD_TOK_PID_IN: + /*pushtx();*/ + break; + } + + /*USB0_CTL = BV(CTL_USBENSOFEN);*/ +} diff --git a/usb/endpt1.h b/usb/endpt1.h new file mode 100644 index 0000000..9be7522 --- /dev/null +++ b/usb/endpt1.h @@ -0,0 +1,8 @@ +#ifndef USB_ENDPT1_H +#define USB_ENDPT1_H + +void usb_endpt1_enable(void); +void usb_endpt1_disable(void); +void usb_endpt1_token(uint8_t state); + +#endif /* USB_ENDPT1_H */ diff --git a/usb/usb.c b/usb/usb.c new file mode 100644 index 0000000..ebd3179 --- /dev/null +++ b/usb/usb.c @@ -0,0 +1,94 @@ +#include <reg/sim.h> +#include <reg/usbotg.h> +#include <reg/gpio.h> +#include <stddef.h> + +#include "usb.h" +#include "bdt.h" +#include "endpt0.h" +#include "endpt1.h" + +#define USBFRAC_VAL 1 /* 72 MHz * 2 = 144 MHz (Top of Fraction) */ +#define USBDIV_VAL 2 /* 144 MHz / 3 = 48 MHz (Bottom of Fraction) */ + +/* TODO: Move this to lib somewhere */ +#define CLRPEND(n) REG_32(0xE000E280 + 4 * (n)) +#define ISR_CLRPEND(i) CLRPEND((i) / 32) = BV((i) % 32); + +#define SETENA(n) REG_32(0xE000E100 + 4 * (n)) +#define ISR_SETENA(i) SETENA((i) / 32) = BV((i) % 32); + + +__attribute__ ((aligned(512))) +struct usb0_bd usb_bdt[16 * 4]; /* TODO: Only use the number of endpoints that are needed */ + +/* usb_setup: Setup function for USB subsystem */ +void usb_setup(void) +{ + /* Clock Setup */ + SET_BIT(SIM_SOPT2, SOPT2_USBSRC); + SET_MASKED(SIM_CLKDIV2, CLKDIV2_USBDIV_M | BV(CLKDIV2_USBFRAC), + USBDIV_VAL << CLKDIV2_USBDIV | USBFRAC_VAL << CLKDIV2_USBFRAC); + SET_BIT(SIM_SCGC4, SCGC4_USBOTG); + + /* Reset USB and wait more than 2 USB clock cycles */ + SET_BIT(USB0_USBTRC0, USBTRC0_USBRESET); + for (int i = 0; i < 5; i++) + ; + + /* 512 bit aligned BDT address */ + USB0_BDTPAGE1 = (uintptr_t)usb_bdt >> 8 & 0xff; + USB0_BDTPAGE2 = (uintptr_t)usb_bdt >> 16 & 0xff; + USB0_BDTPAGE3 = (uintptr_t)usb_bdt >> 24 & 0xff; + + /* Initial Configuration */ + SET_MASKED(USB0_USBCTRL, BV(USBCTRL_SUSP) | BV(USBCTRL_PDE), 0); + SET_BIT(USB0_CONTROL, CONTROL_DPPULLUPNONOTG); + SET_BIT(USB0_INTEN, INTEN_USBRSTEN); + + /* NVIC Enable interrupt */ + ISR_CLRPEND(73); + ISR_SETENA(73); + + /* Enable USB */ + USB0_CTL = BV(CTL_USBENSOFEN); +} + +/* i_usbrst: Handler for USBRST USB interrupt */ +static void i_usbrst(void) +{ + SET_BIT(USB0_CTL, CTL_ODDRST); + + usb_endpt0_enable(); + usb_endpt1_disable(); + + USB0_ADDR = 0; + + SET_BIT(USB0_INTEN, INTEN_TOKDNEEN); +} + +/* i_tokdne: Handler for TOKDNE USB interrupt */ +static void i_tokdne(void) +{ + uint8_t stat = USB0_STAT; + + switch (GET_BITS(stat, STAT_ENDP)) { + case 0: usb_endpt0_token(stat); break; + case 1: usb_endpt1_token(stat); break; + } +} + +/* usb_isr: interrupt service routine for the USB FS SIE */ +void usb_isr(void) +{ + uint8_t stat = USB0_ISTAT; + + if (IS_BIT_SET(stat, ISTAT_TOKDNE)) { + i_tokdne(); + USB0_ISTAT = BV(ISTAT_TOKDNE); + } + if (IS_BIT_SET(stat, ISTAT_USBRST)) { + i_usbrst(); + USB0_ISTAT = BV(ISTAT_USBRST); + } +} diff --git a/usb/usb.h b/usb/usb.h new file mode 100644 index 0000000..64d0585 --- /dev/null +++ b/usb/usb.h @@ -0,0 +1,7 @@ +#ifndef USB_H +#define USB_H + +void usb_setup(void); +void usb_isr(void); + +#endif /* USB_H */ |