summaryrefslogtreecommitdiffstats
path: root/usb
diff options
context:
space:
mode:
Diffstat (limited to 'usb')
-rw-r--r--usb/bdt.h14
-rw-r--r--usb/ds_conf.xxd34
-rw-r--r--usb/ds_dev.xxd18
-rw-r--r--usb/ds_lang.xxd4
-rw-r--r--usb/ds_str1.xxd22
-rw-r--r--usb/endpt0.c199
-rw-r--r--usb/endpt0.h10
-rw-r--r--usb/endpt1.c36
-rw-r--r--usb/endpt1.h8
-rw-r--r--usb/usb.c94
-rw-r--r--usb/usb.h7
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 */