diff --git a/ion/src/device/regs/otg.h b/ion/src/device/regs/otg.h index 101e3f681..0b7a30c0e 100644 --- a/ion/src/device/regs/otg.h +++ b/ion/src/device/regs/otg.h @@ -20,6 +20,9 @@ public: class GRSTCTL : public Register32 { public: REGS_BOOL_FIELD(CSRST, 0); + REGS_BOOL_FIELD(RXFFLSH, 4); + REGS_BOOL_FIELD(TXFFLSH, 5); + REGS_FIELD(TXFNUM, uint8_t, 10, 6); REGS_BOOL_FIELD(AHBIDL, 31); }; @@ -32,6 +35,7 @@ public: REGS_BOOL_FIELD(USBSUSP, 11); REGS_BOOL_FIELD(USBRST, 12); REGS_BOOL_FIELD(ENUMDNE, 13); + REGS_BOOL_FIELD(IEPINT, 18); REGS_BOOL_FIELD(WKUPINT, 31); }; @@ -46,6 +50,21 @@ public: REGS_BOOL_FIELD(WUIM, 31); }; + class GRXSTSP : public Register32 { + public: + using Register32::Register32; + enum class PKTSTS { + GlobalOutNAK = 1, + OutReceived = 2, + OutCompleted = 3, + SetupCompleted = 4, + SetupReceived = 6 + }; + REGS_FIELD(EPNUM, uint8_t, 3, 0); + REGS_FIELD(BCNT, uint16_t, 14, 4); + PKTSTS getPKTSTS() volatile { return (PKTSTS)getBitRange(20, 17); } + }; + class GRXFSIZ : public Register32 { public: REGS_FIELD(RXFD, uint16_t, 15, 0); @@ -96,6 +115,9 @@ public: Size8 = 3 }; void setMPSIZ(MPSIZ s) volatile { setBitRange(1, 0, (uint8_t)s); } + REGS_BOOL_FIELD(STALL, 21); + REGS_FIELD(TXFNUM, uint8_t, 25, 22); + REGS_BOOL_FIELD(CNAK, 26); REGS_BOOL_FIELD(SNAK, 27); REGS_BOOL_FIELD(EPENA, 31); }; @@ -103,6 +125,14 @@ public: class DIEPTSIZ0 : public Register32 { public: REGS_FIELD(XFRSIZ, uint8_t, 6, 0); + REGS_FIELD(PKTCNT, uint8_t, 20, 19); + }; + + class DOEPCTL0 : public Register32 { + public: + REGS_BOOL_FIELD(CNAK, 26); + REGS_BOOL_FIELD(SNAK, 27); + REGS_BOOL_FIELD(EPENA, 31); }; class DOEPTSIZ0 : public Register32 { @@ -116,6 +146,7 @@ public: class DIEPINT : public Register32 { public: REGS_BOOL_FIELD(XFRC, 0); + REGS_BOOL_FIELD(INEPNE, 6); }; class PCGCCTL : public Register32 { @@ -124,12 +155,16 @@ public: REGS_BOOL_FIELD(STPPCLK, 1); }; + class DFIFO0 : public Register32 { + }; + constexpr OTG() {}; REGS_REGISTER_AT(GAHBCFG, 0x008); REGS_REGISTER_AT(GUSBCFG, 0x00C); REGS_REGISTER_AT(GRSTCTL, 0x010); REGS_REGISTER_AT(GINTSTS, 0x014); REGS_REGISTER_AT(GINTMSK, 0x018); + REGS_REGISTER_AT(GRXSTSP, 0x020); REGS_REGISTER_AT(GRXFSIZ, 0x024); REGS_REGISTER_AT(DIEPTXF0, 0x28); REGS_REGISTER_AT(GCCFG, 0x038); @@ -139,8 +174,10 @@ public: REGS_REGISTER_AT(DAINTMSK, 0x81C); REGS_REGISTER_AT(DIEPCTL0, 0x900); REGS_REGISTER_AT(DIEPTSIZ0, 0x910); + REGS_REGISTER_AT(DOEPCTL0, 0xB00); REGS_REGISTER_AT(DOEPTSIZ0, 0xB10); REGS_REGISTER_AT(PCGCCTL, 0xE00); + REGS_REGISTER_AT(DFIFO0, 0x1000); constexpr volatile DIEPINT * DIEPINT(int i) const { return (class DIEPINT *)(Base() + 0xB08 + i*0x20); } diff --git a/ion/src/device/usb.cpp b/ion/src/device/usb.cpp index d08c5dc38..bcdd88b47 100644 --- a/ion/src/device/usb.cpp +++ b/ion/src/device/usb.cpp @@ -18,29 +18,96 @@ namespace Device { #include +static const struct DeviceDescriptor deviceDescriptor = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = 0, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = USB_MAX_PACKET_SIZE, + .idVendor = 0xcafe, //TODO Buy one! + .idProduct = 0xcafe, //TODO Create one! + .bcdDevice = 0x0001, + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 3, + .bNumConfigurations = 1 +}; + +static const struct ConfigDescriptor configDescriptor = { + .bLength = USB_DT_CONFIGURATION_SIZE, + .bDescriptorType = USB_DT_CONFIGURATION, + .wTotalLength = USB_DT_CONFIGURATION_SIZE+USB_DT_INTERFACE_SIZE, //TODO The example puts 0 here. + .bNumInterfaces = 1, + .bConfigurationValue = USB_DFU_CONFIGURATION_VALUE, + .iConfiguration = 0, + .bmAttributes = 0x80, + .bMaxPower = 0x32 +}; + +static const struct InterfaceDescriptor interfaceDescriptor = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, /* Alternate settings can be used by an application to access additional memory segments */ + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_APPLICATION_SPECIFIC, + .bInterfaceSubClass = USB_SUBCLASS_DFU, + .bInterfaceProtocol = USB_PROTOCOL_DFU, + .iInterface = 0 +}; + +SetupData sSetupData; +uint16_t sRxPacketSize; +// TODO Explain those two buffers +/* Buffer used for control requests. */ +static uint8_t sControlBufferInit[5*64]; // TODO why 5*64? (from libopencm3/tests/gadget-zero/usb-gadget0.c) +static uint16_t sControlBufferInitLength = sizeof(sControlBufferInit); +uint8_t * sControlBuffer; +uint16_t sControlBufferLength; +bool sZLPNeeded; /* True if the device needs to send a Zero-Length Packet */ +bool sForceNAK = false; +ControlState sControlState; + void usb_endpoint_setup() { // Configure IN - // To configure depending on the endpoint + /* Set the maximum packet size */ OTG.DIEPCTL0()->setMPSIZ(OTG::DIEPCTL0::MPSIZ::Size64); - - // Set depending on size + /* Transfer size */ OTG.DIEPTSIZ0()->setXFRSIZ(64); - - OTG.DIEPCTL0()->setEPENA(true); + /* Set the NAK bit */ OTG.DIEPCTL0()->setSNAK(true); + /* Enable the endpoint */ + OTG.DIEPCTL0()->setEPENA(true); // Configure OUT + class OTG::DOEPTSIZ0 doeptsiz0(0); - doeptsiz0.setSTUPCNT(1); + /* Max number of back-to-back setup packets that can be received */ + doeptsiz0.setSTUPCNT(1); //TODO 3 in the spec doeptsiz0.setPKTCNT(true); + /* Transfer size */ doeptsiz0.setXFRSIZ(64); - OTG.DOEPTSIZ0()->set(doeptsiz0); + /* Set the NAK bit */ + OTG.DOEPCTL0()->setSNAK(true); + /* Enable the endpoint */ + OTG.DOEPCTL0()->setEPENA(true); + /* Endpoint0 Tx FIFO depth */ OTG.DIEPTXF0()->setTX0FD(64/4); - //OTG.DIEPTXF0().setTX0FSA(fifo/4); + OTG.DIEPTXF0()->setTX0FSA(128 + 64/4); // TODO ? +} + +void usb_endpoints_reset() { + /* There are no additional endpoints to reset */ + + /* Flush tx/rx fifo */ + flushTxFifo(); + flushRxFifo(); } void usb_set_address(uint8_t address) { @@ -50,42 +117,512 @@ void usb_set_address(uint8_t address) { void poll() { class OTG::GINTSTS intsts(OTG.GINTSTS()->get()); if (intsts.getENUMDNE()) { - abort(); // **SPEED** enumeration done /* Handle USB RESET condition. */ - + OTG.GINTSTS()->setENUMDNE(true); //Clear the ENUMDNE bit. usb_endpoint_setup(); - usb_set_address(0); return; } - // There is no global interrupt flag for transmit complete. - // The XFRC bit must be checked in each OTG_DIEPINT(x). - for (int i=0; i<4; i++) { // Iterate over endpoints - if (OTG.DIEPINT(i)->getXFRC()) { - /* Transfer complete. */ - // USB transaction in - abort(); - } + /* TODO There is no global interrupt flag for transmit complete. + * The XFRC bit must be checked in each OTG_DIEPINT(x). */ + if (OTG.DIEPINT(0)->getXFRC()) {//intsts.getIEPINT()) { + controlIn(); + OTG.DIEPINT(0)->setXFRC(true); // This bit is cleared by writing 1 to it. } if (intsts.getRXFLVL()) { - abort(); - } + /* Receive FIFO non-empty. */ + class OTG::GRXSTSP grxstsp(OTG.GRXSTSP()->get()); + OTG::GRXSTSP::PKTSTS pktsts = grxstsp.getPKTSTS(); + uint8_t ep = grxstsp.getEPNUM(); + if (pktsts == OTG::GRXSTSP::PKTSTS::OutCompleted + || pktsts == OTG::GRXSTSP::PKTSTS::SetupCompleted) + { + if (ep == 0) { + class OTG::DOEPTSIZ0 doeptsiz0(0); + /* Max number of back-to-back setup packets that can be received */ + doeptsiz0.setSTUPCNT(1); //TODO 3 in the spec + doeptsiz0.setPKTCNT(true); + /* Transfer size */ + doeptsiz0.setXFRSIZ(64); + OTG.DOEPTSIZ0()->set(doeptsiz0); + /* Enable the endpoint */ + OTG.DOEPCTL0()->setEPENA(true); + /* Set the NAK bit */ + if (sForceNAK) { + OTG.DOEPCTL0()->setSNAK(true); + } else { + OTG.DOEPCTL0()->setCNAK(true); + } + } + return; + } + + if (pktsts != OTG::GRXSTSP::PKTSTS::OutReceived + && pktsts != OTG::GRXSTSP::PKTSTS::SetupReceived) + { + return; + } + + USBTransaction type = USBTransaction::Setup; + if (pktsts == OTG::GRXSTSP::PKTSTS::OutReceived) { + type = USBTransaction::Out; + } + + if (type == USBTransaction::Setup && OTG.DIEPTSIZ0()->getPKTCNT()) { + /* SETUP received but there is still something stuck in the transmit fifo. + * Flush it. */ + flushTxFifo(); + } + + /* Save packet size */ + sRxPacketSize = grxstsp.getBCNT(); + + if (type == USBTransaction::Setup) { + controlSetup(); + } else { + assert(type == USBTransaction::Out); + controlOut(); + } + + /* Discard unread packet data. */ + for (int i = 0; i < sRxPacketSize; i += 4) { + /* There is only one receive FIFO */ + OTG.DFIFO0()->get(); + } + + sRxPacketSize = 0; + } if (intsts.getUSBSUSP()) { // Suspend was detected on the USB bus - //abort(); OTG.GINTSTS()->setUSBSUSP(true); // Clear the interrupt } if (intsts.getWKUPINT()) { - abort(); + OTG.GINTSTS()->setWKUPINT(true); // Clear the interrupt } if (intsts.getSOF()) { - abort(); + OTG.GINTSTS()->setSOF(true); // Clear the interrupt + } +} + +void controlSetup() { + endpoint0SetNak(true); + + // Read the 8-bytes Setup packet + if (endpoint0ReadPacket(&sSetupData, 8) != 8) { + endpoint0StallTransaction(); + return; + }; + + if (sSetupData.wLength == 0) { + controlSetupIn(); + } else if (bmRequestTypeDirection(sSetupData.bmRequestType) == DataDirection::In) { + controlSetupIn(); + } else { + assert(bmRequestTypeDirection(sSetupData.bmRequestType) == DataDirection::Out); + controlSetupOut(); + } +} + +void controlIn() { + switch (sControlState) { + case ControlState::DATA_IN: + controlSendChunk(); + break; + case ControlState::LAST_DATA_IN: + sControlState = ControlState::STATUS_OUT; + endpoint0SetNak(false); + break; + case ControlState::STATUS_IN: + /*if (usbd_dev->control_state.complete) { + usbd_dev->control_state.complete(usbd_dev, + &(usbd_dev->control_state.req)); + }*/ + + /* Exception: Handle SET ADDRESS function here... */ + if ((sSetupData.bmRequestType == 0) && (sSetupData.bRequest == USB_REQ_SET_ADDRESS)) { + usb_set_address(sSetupData.wValue); + } + sControlState = ControlState::IDLE; + break; + default: + endpoint0StallTransaction(); + } +} + +DataDirection bmRequestTypeDirection(uint8_t bmRequestType) { + if (bmRequestType & 0x80) { + return DataDirection::In; + } + return DataDirection::Out; +} + +int descriptorIndexFromWValue(uint16_t wValue) { + return wValue & 0xFF; +} + +int descriptorTypeFromWValue(uint16_t wValue) { + return wValue >> 8; +} + +void controlSetupIn() { + sControlBuffer = sControlBufferInit; + sControlBufferLength = sSetupData.wLength; + + if (controlRequestDispatch()) { + if (sSetupData.wLength) { + // The host is waiting for device data. Check if we need to send a Zero + // Length Packet to explicit a short transaction. + sZLPNeeded = zlpIsNeeded(sControlBufferLength, sSetupData.wLength, deviceDescriptor.bMaxPacketSize0); + // Send the data. + controlSendChunk(); + } else { + /* If no data is expected, send a zero length packet. */ + endpoint0WritePacket(NULL, 0); + sControlState = ControlState::STATUS_IN; + } + } else { + /* Stall endpoint on failure. */ + endpoint0StallTransaction(); + } +} + +void controlSendChunk() { + if (deviceDescriptor.bMaxPacketSize0 < sControlBufferLength) { + /* Data stage, normal transmission */ + endpoint0WritePacket(sControlBuffer, deviceDescriptor.bMaxPacketSize0); + sControlState = ControlState::DATA_IN; + sControlBuffer += deviceDescriptor.bMaxPacketSize0; + sControlBufferLength -= deviceDescriptor.bMaxPacketSize0; + } else { + /* Data stage, end of transmission */ + endpoint0WritePacket(sControlBuffer, deviceDescriptor.bMaxPacketSize0); + sControlState = sZLPNeeded ? ControlState::DATA_IN : ControlState::LAST_DATA_IN; + sZLPNeeded = false; + sControlBufferLength = 0; + sControlBuffer = NULL; + } +} + +uint16_t endpoint0WritePacket(const void *buffer, uint16_t length) { + const uint32_t * buf32 = (uint32_t *) buffer; + + /* Return if endpoint is already enabled. */ //TODO Why? + if (OTG.DIEPTSIZ0()->getPKTCNT()) { + return 0; + } + + /* Enable endpoint for transmission. */ + OTG.DIEPTSIZ0()->setPKTCNT(length); + OTG.DIEPCTL0()->setEPENA(true); + OTG.DIEPCTL0()->setCNAK(true); + + /* Copy buffer to endpoint FIFO, note - memcpy does not work */ + for (int i = length; i > 0; i -= 4) { + OTG.DFIFO0()->set(*buf32++); + } + + return length; +} + +void endpoint0StallTransaction() { + // Set endpoint stall + OTG.DIEPCTL0()->setSTALL(true); + // Set the control state to IDLE + sControlState = ControlState::IDLE; +} + +void endpoint0SetNak(bool nak) { + sForceNAK = nak; + if (nak) { + OTG.DOEPCTL0()->setSNAK(true); + return; + } + OTG.DOEPCTL0()->setCNAK(true); +} + +int controlRequestDispatch() { + switch (sSetupData.bRequest) { + case USB_REQ_GET_STATUS: + //TODO Not needed for enumeration? + break; + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + //TODO Not needed for enumeration? + break; + case USB_REQ_SET_ADDRESS: + if ((sSetupData.bmRequestType != 0) || (sSetupData.wValue >= 128)) { + return 0; + } + /* The address should be set after the Status stage of the current + * transaction. This way, the device can reveice the IN ADDR 0 EP 0 packet + * that begins the status phase. */ + return 1; + break; + case USB_REQ_GET_DESCRIPTOR: + return controlSetupGetDescriptor(); + break; + case USB_REQ_SET_DESCRIPTOR: + //TODO Not needed for enumeration? + break; + case USB_REQ_SET_CONFIGURATION: + return controlSetupSetConfiguration(); + case USB_REQ_GET_CONFIGURATION: + //TODO Not needed for enumeration? + break; + default: + break; + } + return 0; +} + +int controlSetupGetDescriptor() { + int descriptorIndex = descriptorIndexFromWValue(sSetupData.wValue); + + switch (descriptorTypeFromWValue(sSetupData.wValue)) { + case USB_DT_DEVICE: + sControlBuffer = (uint8_t *)(&deviceDescriptor); + sControlBufferLength = MIN(sControlBufferLength, deviceDescriptor.bLength); + return (int) RequestReturnCodes::USBD_REQ_HANDLED; + case USB_DT_CONFIGURATION: + sControlBuffer = sControlBufferInit; + sControlBufferLength = buildConfigDescriptor(descriptorIndex); + return (int) RequestReturnCodes::USBD_REQ_HANDLED; + case USB_DT_STRING: + struct StringDescriptor * stringDescriptor = (struct StringDescriptor *)sControlBufferInit; + + if (descriptorIndex == 0) { + /* Send sane Language ID descriptor. */ + stringDescriptor->wData[0] = USB_LANGID_ENGLISH_US; + stringDescriptor->bLength = sizeof(stringDescriptor->bLength) + + sizeof(stringDescriptor->bDescriptorType) + + sizeof(stringDescriptor->wData[0]); + sControlBufferLength = MIN(sControlBufferLength, stringDescriptor->bLength); + } else { + int arrayIndex = descriptorIndex - 1; + /* Check that string index is in range. */ + if (arrayIndex >= sNumberOfStrings) { + return (int) RequestReturnCodes::USBD_REQ_NOTSUPP; + } + + /* Strings with Language ID different from USB_LANGID_ENGLISH_US are not + * supported */ + if (sSetupData.wIndex != USB_LANGID_ENGLISH_US) { + return (int) RequestReturnCodes::USBD_REQ_NOTSUPP; + } + + /* This string is returned as UTF16, hence the multiplication */ + stringDescriptor->bLength = strlen(sStrings[arrayIndex]) * 2 + + sizeof(stringDescriptor->bLength) + + sizeof(stringDescriptor->bDescriptorType); + + sControlBufferLength = MIN(sControlBufferLength, stringDescriptor->bLength); + + for (int i = 0; i < (sControlBufferLength / 2) - 1; i++) { + stringDescriptor->wData[i] = sStrings[arrayIndex][i]; + } + } + + stringDescriptor->bDescriptorType = USB_DT_STRING; + sControlBuffer = (uint8_t *)stringDescriptor; + return (int) RequestReturnCodes::USBD_REQ_HANDLED; + } + return (int) RequestReturnCodes::USBD_REQ_NOTSUPP; +} + +uint16_t buildConfigDescriptor(uint8_t index) { + uint8_t *tmpbuf = sControlBuffer; + uint16_t count = MIN(sControlBufferLength, configDescriptor.bLength); + + memcpy(sControlBuffer, &configDescriptor, count); + sControlBuffer += count; + sControlBufferLength -= count; + uint16_t total = count; + uint16_t totalLength = configDescriptor.bLength; + + /* For now, we have one interface only */ + assert(configDescriptor.bNumInterfaces == 1); + + /* The interface has no Interface Association Descriptor and one setting only */ + /* Copy interface descriptor. */ + count = MIN(sControlBufferLength, interfaceDescriptor.bLength); + memcpy(sControlBuffer, &interfaceDescriptor, count); + sControlBuffer += count; + sControlBufferLength -= count; + total += count; + totalLength += interfaceDescriptor.bLength; + + /* We have no additional endpoints for this interface */ + + /* Fill in wTotalLength. */ + *(uint16_t *)(tmpbuf + 2) = totalLength; + + return total; +} + +/* If the device needs to reply data, but less than what the host expects and a + * multiple of the endpoint max packet size, the device needs to explicit the + * end of the transfer by sending a Zero Length Data Packet. */ +bool zlpIsNeeded(uint16_t dataLength, uint16_t dataExpectedLength, uint8_t endpointMaxPacketSize) { + if (dataLength < dataExpectedLength) { + if (/* TODO I do not think this condition is needed: dataLength && */dataLength % endpointMaxPacketSize == 0) { + return true; + } + } + return false; +} + +void controlSetupOut() { + if (sSetupData.wLength > sControlBufferInitLength) { + endpoint0StallTransaction(); + return; + } + + /* Buffer into which to write received data. */ + sControlBuffer = sControlBufferInit; + sControlBufferLength = 0; + /* Wait for DATA OUT stage. */ + if (sSetupData.wLength > deviceDescriptor.bMaxPacketSize0) { + sControlState = ControlState::DATA_OUT; + } else { + sControlState = ControlState::LAST_DATA_OUT; + } + + endpoint0SetNak(false); +} + +int controlSetupSetConfiguration() { + /* We support one configuration only */ + if (sSetupData.wValue != 0 || sSetupData.wValue != USB_DFU_CONFIGURATION_VALUE) { + return (int) RequestReturnCodes::USBD_REQ_NOTSUPP; + } + + /* Because there is one configuration only, no need to set it again. */ + + /* Reset all endpoints. */ + usb_endpoints_reset(); + + return (int) RequestReturnCodes::USBD_REQ_HANDLED; +} + +void controlOut() { + switch (sControlState) { + case ControlState::DATA_OUT: + if (controlReceiveChunk() < 0) { + break; + } + if ((sSetupData.wLength - sControlBufferLength) <= deviceDescriptor.bMaxPacketSize0) { + sControlState = ControlState::LAST_DATA_OUT; + } + break; + case ControlState::LAST_DATA_OUT: + if (controlReceiveChunk() < 0) { + break; + } + /* + * We have now received the full data payload. + * Invoke callback to process. + */ + if (controlRequestDispatch()) { + /* Go to status stage on success. */ + endpoint0WritePacket(NULL, 0); //TODO Why? + sControlState = ControlState::STATUS_IN; + } else { + endpoint0StallTransaction(); + } + break; + case ControlState::STATUS_OUT: + endpoint0ReadPacket(NULL, 0); //TODO Why? + sControlState = ControlState::IDLE; + break; + default: + endpoint0StallTransaction(); + } +} + +int controlReceiveChunk() { + uint16_t packetsize = MIN(deviceDescriptor.bMaxPacketSize0, sSetupData.wLength - sControlBufferLength); + uint16_t size = endpoint0ReadPacket(sControlBuffer + sControlBufferLength, packetsize); //TODO Pourquoi avance-t'on avant de lire? + if (size != packetsize) { + endpoint0StallTransaction(); + return -1; + } + sControlBufferLength += size; + return packetsize; +} + +uint16_t endpoint0ReadPacket(void * buffer, uint16_t length) { + uint32_t * buf = (uint32_t *) buffer; + uint16_t len = MIN(length, sRxPacketSize); + + int i; + for (i = len; i >= 4; i -= 4) { + *buf++ = OTG.DFIFO0()->get(); //TODO: Why would this work? + sRxPacketSize -= 4; + } + + if (i) { + uint32_t extra = OTG.DFIFO0()->get(); + /* We read 4 bytes from the fifo, so update sRxPacketSize. Be careful not to + * underflow (rxbcnt is unsigned) */ + if (sRxPacketSize < 4) { + sRxPacketSize = 0; + } else { + sRxPacketSize -= 4; + } + memcpy(buf, &extra, i); + } + return len; +} + +void flushTxFifo() { + /* Set IN endpoint NAK */ + OTG.DIEPCTL0()->setSNAK(true); + + /* Wait for core to respond */ + while (!OTG.DIEPINT(0)->getINEPNE()) { + } + + /* Get the Tx FIFO number for endpoint 0 */ + uint32_t fifo = OTG.DIEPCTL0()->getTXFNUM(); + + /* Wait for AHB idle */ + while (!OTG.GRSTCTL()->getAHBIDL()) { + } + + /* Flush Tx FIFO */ + OTG.GRSTCTL()->setTXFNUM(fifo); + OTG.GRSTCTL()->setTXFFLSH(true); + + /* Reset packet counter */ + OTG.DIEPTSIZ0()->setPKTCNT(0); + + /* Wait for the flush */ + while (OTG.GRSTCTL()->getTXFFLSH()) { + } +} + +void flushRxFifo() { + /* Set OUT endpoint NAK */ + OTG.DOEPCTL0()->setSNAK(true); + + /* Wait for AHB idle */ + while (!OTG.GRSTCTL()->getAHBIDL()) { + } + + /* Flush Tx FIFO */ + OTG.GRSTCTL()->setTXFFLSH(true); + + /* Reset packet counter */ + OTG.DOEPTSIZ0()->setPKTCNT(0); + + /* Wait for the flush */ + while (OTG.GRSTCTL()->getRXFFLSH()) { } } @@ -102,8 +639,6 @@ void init() { while (OTG.GRSTCTL()->getCSRST()) { } - // TODO: Check BCUSBSEN / VBDEN - // Enable the USB transceiver OTG.GCCFG()->setPWRDWN(true); // FIXME: Understand why VBDEN is required @@ -127,12 +662,6 @@ void init() { /* Full speed device. */ OTG.DCFG()->setDSPD(OTG::DCFG::DSPD::FullSpeed); - /* Restart the PHY clock. */ - //OTG::PCGCCTL pcgcctl;// = OTG.PCGCCTL(); - // This is already zero... - //OTG.PCGCCTL()->setGATEHCLK(0); - //OTG.PCGCCTL()->setSTPPCLK(0); - // FIFO-size = 128 * 32bits. // FIXME: Explain :-) OTG.GRXFSIZ()->setRXFD(128); @@ -162,8 +691,6 @@ void init() { while (!OTG.GINTSTS()->getENUMDNE()) { } - abort(); - while (true) { poll(); } diff --git a/ion/src/device/usb.h b/ion/src/device/usb.h index 9bef2c56f..1a0ddf14e 100644 --- a/ion/src/device/usb.h +++ b/ion/src/device/usb.h @@ -2,6 +2,7 @@ #define ION_DEVICE_USB_H #include "regs/regs.h" +#include "ion.h" namespace Ion { namespace USB { @@ -9,15 +10,174 @@ namespace Device { /* Pin | Role | Mode | Function * -----+-------------------+-----------------------+---------- - * PA9 | VBUS | Input, pulled down | Low = unplugged, high = plugged + * PA9 | VBUS | Input, pulled down//TODO | Low = unplugged, high = plugged * PA11 | USB D- | Alternate Function 10 | * PA12 | USB D+ | Alternate Function 10 | */ +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +enum class USBTransaction { + In, + Out, + Setup +}; + +/* USB Setup Transaction Data structure */ +struct SetupData { + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} __attribute__((packed)); + +enum class DataDirection { + In, + Out +}; + +/* USB Standard Request Codes */ +#define USB_REQ_GET_STATUS 0 +#define USB_REQ_CLEAR_FEATURE 1 +#define USB_REQ_SET_FEATURE 3 +#define USB_REQ_SET_ADDRESS 5 +#define USB_REQ_GET_DESCRIPTOR 6 +#define USB_REQ_SET_DESCRIPTOR 7 +#define USB_REQ_GET_CONFIGURATION 8 +#define USB_REQ_SET_CONFIGURATION 9 +#define USB_REQ_GET_INTERFACE 10 +#define USB_REQ_SET_INTERFACE 11 +#define USB_REQ_SET_SYNCH_FRAME 12 + +/* USB Descriptor Types */ +#define USB_DT_DEVICE 1 +#define USB_DT_CONFIGURATION 2 +#define USB_DT_STRING 3 +#define USB_DT_INTERFACE 4 +#define USB_DT_ENDPOINT 5 +#define USB_DT_DEVICE_QUALIFIER 6 +#define USB_DT_OTHER_SPEED_CONFIGURATION 7 +#define USB_DT_INTERFACE_POWER 8 + +enum class RequestReturnCodes { + USBD_REQ_NOTSUPP = 0, + USBD_REQ_HANDLED = 1, + USBD_REQ_NEXT_CALLBACK = 2 +}; + +struct DeviceDescriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +} __attribute__((packed)); + +#define USB_DT_DEVICE_SIZE sizeof(struct DeviceDescriptor) + +#define USB_MAX_PACKET_SIZE 64 + +struct StringDescriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wData[]; +} __attribute__((packed)); + +#define USB_LANGID_ENGLISH_US 0x409 + + +/* USB Standard Configuration Descriptor */ +struct ConfigDescriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t bMaxPower; +} __attribute__((packed)); + +#define USB_DT_CONFIGURATION_SIZE 9 + +/* USB Standard Interface Descriptor - Table 9-12 */ +struct InterfaceDescriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; +} __attribute__((packed)); + +#define USB_DT_INTERFACE_SIZE 9 + +/* Class Definition */ +#define USB_CLASS_APPLICATION_SPECIFIC 0xFE +/* Class Definition */ +#define USB_SUBCLASS_DFU 0x01 +/* Class Definition */ +#define USB_PROTOCOL_DFU 0x01 + +#define USB_DFU_CONFIGURATION_VALUE 1 + + +enum class ControlState { + IDLE, + STALLED, + DATA_IN, + LAST_DATA_IN, + STATUS_IN, + DATA_OUT, + LAST_DATA_OUT, + STATUS_OUT, +}; + void init(); void initGPIO(); void shutdown(); +void flushTxFifo(); +void flushRxFifo(); +void controlSetup(); +void controlSetupIn(); +void controlSetupOut(); +int controlSetupGetDescriptor(); +int controlSetupSetConfiguration(); +void controlSendChunk(); +int controlReceiveChunk(); +int controlRequestDispatch(); +void controlOut(); +void controlIn(); +uint16_t endpoint0ReadPacket(void * buffer, uint16_t length); +uint16_t endpoint0WritePacket(const void *buffer, uint16_t length); +void endpoint0StallTransaction(); +void endpoint0SetNak(bool nak); +DataDirection bmRequestTypeDirection(uint8_t bmRequestType); +int descriptorIndexFromWValue(uint16_t wValue); +int descriptorTypeFromWValue(uint16_t wValue); +uint16_t buildConfigDescriptor(uint8_t index); +bool zlpIsNeeded(uint16_t dataLength, uint16_t dataExpectedLength, uint8_t endpointMaxPacketSize); + +constexpr static const char *sStrings[] = { + "Numworks", + "Calculatrice USB", + "111111111" //TODO +}; +constexpr static uint16_t sNumberOfStrings = 4; //TODO set value constexpr static GPIOPin VbusPin = GPIOPin(GPIOA, 9); constexpr static GPIOPin DmPin = GPIOPin(GPIOA, 11); constexpr static GPIOPin DpPin = GPIOPin(GPIOA, 12);