summaryrefslogtreecommitdiff
path: root/usb/usb_nrf.h
diff options
context:
space:
mode:
Diffstat (limited to 'usb/usb_nrf.h')
-rw-r--r--usb/usb_nrf.h187
1 files changed, 187 insertions, 0 deletions
diff --git a/usb/usb_nrf.h b/usb/usb_nrf.h
new file mode 100644
index 0000000..16e40a1
--- /dev/null
+++ b/usb/usb_nrf.h
@@ -0,0 +1,187 @@
+#ifndef LAKS_USB_USB_NRF_H
+#define LAKS_USB_USB_NRF_H
+
+#include "generic.h"
+#include "usb_nrf_def.h"
+
+#include <string.h>
+
+class USB_NRF : public USB_generic {
+ private:
+ USB_NRF_t& usb;
+
+ uint32_t ep_in_busy;
+
+ uint8_t buf_in[8][64];
+ uint8_t buf_out[8][64];
+
+ protected:
+ virtual void hw_set_address(uint8_t addr) {
+ usb_rblog.log("SetAddress: %d", addr);
+
+ // Do nothing, handled in hardware.
+ }
+
+ virtual void hw_conf_ep(uint8_t ep, EPType type, uint32_t size) {
+ usb_rblog.log("Configuring EP%02x: size=%d", ep, size);
+
+ uint8_t in = ep & 0x80;
+ ep &= 0x7f;
+
+ if(in || ep == 0) {
+ usb.reg_in[ep].PTR = (uint32_t)&buf_in[ep];
+ usb.reg.EPINEN |= 1 << ep;
+ }
+
+ if(!in) {
+ usb.reg_out[ep].PTR = (uint32_t)&buf_out[ep];
+
+ usb.reg.SIZE_EPOUT[ep] = 0;
+ usb.reg.EPOUTEN |= 1 << ep;
+ }
+ }
+
+ virtual void hw_set_stall(uint8_t ep) {
+ if(ep == 0) {
+ usb.tasks.EP0STALL = 1;
+ }
+ }
+
+ public:
+ USB_NRF(USB_NRF_t& usb_periph, desc_t dev, desc_t conf) : USB_generic(dev, conf), usb(usb_periph) {}
+
+ void init() {
+ *(volatile uint32_t*)0x4006ec00 = 0x00009375;
+ *(volatile uint32_t*)0x4006ed14 = 0x00000003;
+ *(volatile uint32_t*)0x4006ec00 = 0x00009375;
+
+ usb.reg.ENABLE = 1;
+
+ while(!(usb.reg.EVENTCAUSE & (1 << 11)));
+
+ usb.reg.EVENTCAUSE = 1 << 11;
+
+ *(volatile uint32_t*)0x4006ec00 = 0x00009375;
+ *(volatile uint32_t*)0x4006ed14 = 0x00000000;
+ *(volatile uint32_t*)0x4006ec00 = 0x00009375;
+
+ usb.reg.USBPULLUP = 1;
+ }
+
+ void process() {
+ if(usb.events.USBRESET) {
+ usb.events.USBRESET = 0;
+
+ usb_rblog.log("USB Reset");
+
+ handle_reset();
+ return;
+ }
+
+ if(usb.events.EP0SETUP) {
+ usb.events.EP0SETUP = 0;
+
+ uint8_t setupbuf[8] = {
+ (uint8_t)usb.reg.BMREQUESTTYPE,
+ (uint8_t)usb.reg.BREQUEST,
+ (uint8_t)usb.reg.WVALUEL,
+ (uint8_t)usb.reg.WVALUEH,
+ (uint8_t)usb.reg.WINDEXL,
+ (uint8_t)usb.reg.WINDEXH,
+ (uint8_t)usb.reg.WLENGTHL,
+ (uint8_t)usb.reg.WLENGTHH,
+ };
+
+ handle_setup((uint32_t*)&setupbuf);
+ }
+
+ if(usb.events.EP0DATADONE) {
+ // TODO: Support multi-packet data stages.
+ usb.events.EP0DATADONE = 0;
+
+ usb_rblog.log("Control data IN done, ACKing status stage.");
+
+ usb.tasks.EP0STATUS = 1;
+ }
+
+ /*
+ for(uint32_t ep = 0; ep <= 7; ep++) {
+ if(usb.events.ENDEPIN[ep]) {
+ usb.events.ENDEPIN[ep] = 0;
+
+ ep_in_busy &= ~(1 << ep);
+ }
+ }
+ */
+
+ if(usb.reg.EPDATASTATUS) {
+ for(uint32_t ep = 1; ep <= 7; ep++) {
+ if((usb.reg.EPDATASTATUS & ((1 << 16) << ep)) == 0) {
+ continue;
+ }
+
+ usb_rblog.log("EPDATA, starting DMA on ep %d", ep);
+
+ usb.reg.EPDATASTATUS = (1 << 16) << ep;
+
+ usb.reg_out[ep].MAXCNT = usb.reg.SIZE_EPOUT[ep];
+ usb.tasks.STARTEPOUT[ep] = 1;
+ }
+ }
+
+ for(uint32_t ep = 0; ep <= 7; ep++) {
+ if(usb.events.ENDEPOUT[ep]) {
+ usb.events.ENDEPOUT[ep] = 0;
+
+ handle_out(ep, usb.reg_out[ep].AMOUNT);
+ }
+ }
+ }
+
+ virtual bool ep_ready(uint32_t ep) {
+ //if(usb.events.ENDEPIN[ep]) {
+ // usb.events.ENDEPIN[ep] = 0;
+ // ep_in_busy &= ~(1 << ep);
+ //}
+
+ if(usb.reg.EPDATASTATUS & (1 << ep)) {
+ usb.reg.EPDATASTATUS = 1 << ep;
+
+ ep_in_busy &= ~(1 << ep);
+ }
+
+ return (ep_in_busy & (1 << ep)) == 0;
+ }
+
+ virtual void write(uint32_t ep, uint32_t* bufp, uint32_t len) {
+ usb_rblog.log("Writing, ep=%d, len=%d", ep, len);
+
+ if(ep == 0 && len == 0 && (usb.reg.BMREQUESTTYPE & 0x80) == 0) {
+ usb_rblog.log("EP0 status ACK");
+ usb.tasks.EP0STATUS = 1;
+ return;
+ }
+
+ memcpy(&buf_in[ep], bufp, len);
+
+ usb.reg_in[ep].MAXCNT = len;
+
+ usb.tasks.STARTEPIN[ep] = 1;
+
+ ep_in_busy |= 1 << ep;
+ }
+
+ virtual uint32_t read(uint32_t ep, uint32_t* bufp, uint32_t len) {
+ usb_rblog.log("Reading, ep=%d, len=%d", ep, len);
+
+ if(len > usb.reg_out[ep].AMOUNT) {
+ len = usb.reg_out[ep].AMOUNT;
+ }
+
+ memcpy(bufp, &buf_out[ep], len);
+
+ return len;
+ }
+};
+
+#endif