summaryrefslogtreecommitdiff
path: root/usb/usb.h
diff options
context:
space:
mode:
authorVegard Storheil Eriksen <zyp@jvnv.net>2012-08-19 22:33:35 +0200
committerVegard Storheil Eriksen <zyp@jvnv.net>2012-08-19 22:33:35 +0200
commitc09be56d30e5a99ab0451a53d0fc0df0aa0dee8f (patch)
treec8f5ef6c1e54f3825deeee95169fbd10fce1baea /usb/usb.h
parente586c178073b9a0fee90d5fc8e795d266ebd7b7d (diff)
Moved usb driver from suzumebachi project.
Diffstat (limited to 'usb/usb.h')
-rw-r--r--usb/usb.h215
1 files changed, 215 insertions, 0 deletions
diff --git a/usb/usb.h b/usb/usb.h
index c8c550f..8254b5f 100644
--- a/usb/usb.h
+++ b/usb/usb.h
@@ -90,4 +90,219 @@ static USB_t OTG_FS(0x50000000);
static USB_t OTG_HS(0x40040000);
#endif
+static uint8_t dev_desc[] = {
+ 0x12, 0x01, 0x00, 0x02, 0xff, 0x00, 0x00, 0x40, 0x34, 0x12, 0x78, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+};
+
+static uint8_t conf_desc[] = {
+ 0x09, 0x02, 0x19, 0x00, 0x01, 0x01, 0x00, 0xc0, 0x00,
+ 0x09, 0x04, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00,
+ 0x07, 0x05, 0x81, 0x02, 0x40, 0x00, 0x00,
+};
+
+static uint32_t buf[16];
+
+class USB_generic {
+ public:
+ virtual void write(uint32_t ep, uint32_t* bufp, uint32_t len) = 0;
+ virtual void hw_set_address(uint8_t addr) = 0;
+ virtual void hw_conf_ep(uint8_t ep, uint32_t conf) = 0;
+ virtual void hw_set_stall(uint8_t ep) = 0;
+
+ protected:
+ bool get_descriptor(uint16_t wValue, uint16_t wIndex, uint16_t wLength) {
+ switch(wValue) {
+ case 0x100:
+ write(0, (uint32_t*)dev_desc, wLength > sizeof(dev_desc) ? sizeof(dev_desc) : wLength);
+ return true;
+ case 0x200:
+ write(0, (uint32_t*)conf_desc, wLength > sizeof(conf_desc) ? sizeof(conf_desc) : wLength);
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ bool set_address(uint16_t wValue, uint16_t wIndex, uint16_t wLength) {
+ hw_set_address(wValue);
+ write(0, 0, 0);
+ return true;
+ }
+
+ bool set_configuration(uint16_t wValue, uint16_t wIndex, uint16_t wLength) {
+ switch(wValue) {
+ case 0:
+ hw_conf_ep(1, 0);
+ break;
+
+ case 1:
+ hw_conf_ep(1, (1 << 22) | (2 << 18) | (1 << 15) | 64);
+ break;
+
+ default:
+ return false;
+ }
+
+ write(0, 0, 0);
+ return true;
+ }
+
+
+ void handle_setup(const uint32_t* bufp) {
+ uint8_t bmRequestType = bufp[0] & 0xff;
+ uint8_t bRequest = (bufp[0] >> 8) & 0xff;
+ uint16_t wValue = (bufp[0] >> 16) & 0xffff;
+ uint16_t wIndex = bufp[1] & 0xffff;
+ uint16_t wLength = (bufp[1] >> 16) & 0xffff;
+
+ // GET_DESCRIPTOR
+ if(bmRequestType == 0x80 && bRequest == 0x06) {
+ if(get_descriptor(wValue, wIndex, wLength)) {
+ return;
+ }
+ }
+
+ // SET_ADDRESS
+ if(bmRequestType == 0x00 && bRequest == 0x05) {
+ if(set_address(wValue, wIndex, wLength)) {
+ return;
+ }
+ }
+
+ // SET_CONFIGURATION
+ if(bmRequestType == 0x00 && bRequest == 0x09) {
+ if(set_configuration(wValue, wIndex, wLength)) {
+ return;
+ }
+ }
+
+ // I2C_READ
+ //if(bmRequestType == 0xc0 && bRequest == 0xf0) {
+ // if(i2c_read(wValue, wIndex, wLength)) {
+ // return;
+ // }
+ //}
+
+ // JTAG_SHIFT
+ //if(bmRequestType == 0xc0 && bRequest == 0xff) {
+ // if(jtag_shift(wValue, wIndex, wLength)) {
+ // return;
+ // }
+ //}
+
+ hw_set_stall(0);
+ }
+
+};
+
+class USB_otg : public USB_generic {
+ private:
+ USB_t& otg;
+
+ protected:
+ virtual void hw_set_address(uint8_t addr) {
+ otg.dev_reg.DCFG |= addr << 4;
+ }
+
+ virtual void hw_conf_ep(uint8_t ep, uint32_t conf) {
+ otg.dev_iep_reg[ep].DIEPCTL = conf;
+ }
+
+ virtual void hw_set_stall(uint8_t ep) {
+ otg.dev_iep_reg[ep].DIEPCTL |= (1 << 21);
+ }
+
+ public:
+ USB_otg(USB_t& otg_periph) : otg(otg_periph) {}
+
+ void init() {
+ // Set PHYSEL.
+ otg.reg.GUSBCFG |= (1 << 6);
+
+ Time::sleep(10);
+
+ while(!(otg.reg.GRSTCTL & (1 << 31)));
+ otg.reg.GRSTCTL |= 1;
+ while(otg.reg.GRSTCTL & 1);
+
+ otg.reg.GAHBCFG = 0;
+
+ // USB configuration
+ otg.reg.GUSBCFG = (1 << 30) | (0xf << 10) | (0 << 9) | (0 << 8) | (1 << 6);
+ // FDMOD TRDT HNPCAP SRPCAP PHYSEL
+
+ // interrupt mask
+ otg.reg.GINTMSK = (1 << 13) | (1 << 12) | (1 << 11) | (1 << 10) | (1 << 3) | (1 << 2) | (1 << 1) | (1 << 4);
+ // ENUMDNEM USBRST USBSUSPM ESUSPM SOFM OTGINT MMISM
+
+ // device configuration
+ otg.dev_reg.DCFG = (1 << 2) | 3;
+ // NZLSOHSK DSPD
+
+ // core configuration
+ otg.reg.GCCFG = (1 << 19) | (1 << 16);
+ // VBUSBSEN PWRDWN
+
+ }
+
+ void process() {
+ // USB reset.
+ if(otg.reg.GINTSTS & (1 << 12)) {
+ otg.dev_oep_reg[0].DOEPCTL = (1 << 27);
+ otg.dev_reg.DAINTMSK = (1 << 16) | 1;
+ otg.dev_reg.DOEPMSK = (1 << 3) | 1;
+ otg.dev_reg.DIEPEMPMSK = (1 << 3) | 1;
+ otg.reg.GRXFSIZ = 256;
+ otg.reg.DIEPTXF0 = (64 << 16) | 256;
+ otg.reg.DIEPTXF1 = (64 << 16) | 320;
+ otg.dev_oep_reg[0].DOEPTSIZ = (3 << 29);
+ }
+
+ // OTG interrupt.
+ if(otg.reg.GINTSTS & (1 << 2)) {
+ otg.reg.GOTGINT = (1 << 2); // SEDET
+ }
+
+ // RxFIFO non-empty.
+ if(otg.reg.GINTSTS & (1 << 4)) {
+ handle_rxfifo();
+ }
+
+ otg.reg.GINTSTS = 0xffffffff;
+ }
+
+ bool ep_ready(uint32_t ep) {
+ return (otg.dev_iep_reg[ep].DIEPCTL & 0x80008000) == 0x8000;
+ }
+
+ virtual void write(uint32_t ep, uint32_t* bufp, uint32_t len) {
+ otg.dev_iep_reg[ep].DIEPTSIZ = (1 << 19) | len;
+ // PKTCNT
+ otg.dev_iep_reg[ep].DIEPCTL |= (1 << 31) | (1 << 26);
+ // EPENA CNAK
+
+ len = (len + 3) >> 2;
+
+ while(len--) {
+ otg.fifo[ep].reg = *bufp++;
+ }
+ }
+
+ void handle_rxfifo() {
+ uint32_t status = otg.reg.GRXSTSP;
+
+ uint32_t len = (status & 0x7ff0) >> 6;
+
+ for(uint32_t i = 0; i < len; i++) {
+ buf[i] = otg.fifo[0].reg;
+ }
+
+ //if(status == 0x000c0080) {
+ if((status & (0xf << 17)) == (0x4 << 17)) {
+ handle_setup(buf);
+ otg.dev_oep_reg[0].DOEPCTL |= (1 << 26);
+ }
+ }
+};
+
#endif