mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-28 01:59:59 +01:00
[usb] Handle Setup and in packets from host.
Change-Id: I9ffc2705af3c30389b30fdcac34e9e3a1a97a3d7
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -18,29 +18,96 @@ namespace Device {
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user