Files
Upsilon/ion/src/device/shared/usb/stack/endpoint0.cpp
Neven Sajko f74ed8ecd7 [ion] Fix error check in the USB stack
The return type of the function receiveSomeData needs to be wider so the
-1 error value could fit.

Fixes #1335
2020-02-26 11:09:43 +01:00

347 lines
9.5 KiB
C++

#include "endpoint0.h"
#include <string.h>
#include <regs/regs.h>
#include "device.h"
#include "interface.h"
#include "request_recipient.h"
#define MIN(a, b) ((a) < (b) ? (a) : (b))
namespace Ion {
namespace Device {
namespace USB {
using namespace Regs;
void Endpoint0::setup() {
// Setup the IN direction
// Reset the device IN endpoint 0 transfer size register
class OTG::DIEPTSIZ0 dieptsiz0(0);
/* Transfer size. The core interrupts the application only after it has
* exhausted the transfer size amount of data. The transfer size is set to the
* maximum packet size, to be interrupted at the end of each packet. */
dieptsiz0.setXFRSIZ(k_maxPacketSize);
OTG.DIEPTSIZ0()->set(dieptsiz0);
// Reset the device IN endpoint 0 control register
class OTG::DIEPCTL0 diepctl0(0); // Reset value
// Set the maximum packet size
diepctl0.setMPSIZ(OTG::DIEPCTL0::MPSIZ::Size64);
// Set the NAK bit: all IN transactions on endpoint 0 receive a NAK answer
diepctl0.setSNAK(true);
// Enable the endpoint
diepctl0.setEPENA(true);
OTG.DIEPCTL0()->set(diepctl0);
// Setup the OUT direction
setupOut();
// Set the NAK bit
OTG.DOEPCTL0()->setSNAK(true);
// Enable the endpoint
enableOut();
// Setup the Tx FIFO
/* Tx FIFO depth
* We process each packet as soon as it arrives, so we only need
* k_maxPacketSize bytes. TX0FD being in terms of 32-bit words, we divide
* k_maxPacketSize by 4. */
OTG.DIEPTXF0()->setTX0FD(k_maxPacketSize/4);
/* Tx FIFO RAM start address. It starts just after the Rx FIFOso the value is
* Rx FIFO start address (0) + Rx FIFO depth. the Rx FIFO depth is set in
* usb.cpp, but because the code is linked separately, we cannot get it. */
OTG.DIEPTXF0()->setTX0FSA(128);
}
void Endpoint0::setupOut() {
class OTG::DOEPTSIZ0 doeptsiz0(0);
// Number of back-to-back SETUP data packets the endpoint can receive
doeptsiz0.setSTUPCNT(1);
// Packet count, false if a packet is written into the Rx FIFO
doeptsiz0.setPKTCNT(true);
/* Transfer size. The core interrupts the application only after it has
* exhausted the transfer size amount of data. The transfer size is set to the
* maximum packet size, to be interrupted at the end of each packet. */
doeptsiz0.setXFRSIZ(64);
OTG.DOEPTSIZ0()->set(doeptsiz0);
}
void Endpoint0::setOutNAK(bool nak) {
m_forceNAK = nak;
/* We need to keep track of the NAK state of the endpoint to use the value
* after a setupOut in poll() of device.cpp. */
if (nak) {
OTG.DOEPCTL0()->setSNAK(true);
} else {
OTG.DOEPCTL0()->setCNAK(true);
}
}
void Endpoint0::enableOut() {
OTG.DOEPCTL0()->setEPENA(true);
}
void Endpoint0::reset() {
flushTxFifo();
flushRxFifo();
}
void Endpoint0::readAndDispatchSetupPacket() {
setOutNAK(true);
// Read the 8-bytes Setup packet
if (readPacket(m_largeBuffer, sizeof(SetupPacket)) != sizeof(SetupPacket)) {
stallTransaction();
return;
};
m_request = SetupPacket(m_largeBuffer);
uint16_t maxBufferLength = MIN(m_request.wLength(), MaxTransferSize);
// Forward the request to the request recipient
uint8_t type = static_cast<uint8_t>(m_request.recipientType());
if (type == 0) {
// Device recipient
m_requestRecipients[0]->processSetupRequest(&m_request, m_largeBuffer, &m_transferBufferLength, maxBufferLength);
} else {
// Interface recipient
m_requestRecipients[1]->processSetupRequest(&m_request, m_largeBuffer, &m_transferBufferLength, maxBufferLength);
}
}
void Endpoint0::processINpacket() {
switch (m_state) {
case State::DataIn:
sendSomeData();
break;
case State::LastDataIn:
m_state = State::StatusOut;
// Prepare to receive the OUT Data[] transaction.
setOutNAK(false);
break;
case State::StatusIn:
{
m_state = State::Idle;
// All the data has been received. Callback the request recipient.
uint8_t type = static_cast<uint8_t>(m_request.recipientType());
if (type == 0) {
// Device recipient
m_requestRecipients[0]->wholeDataReceivedCallback(&m_request, m_largeBuffer, &m_transferBufferLength);
} else {
// Interface recipient
m_requestRecipients[1]->wholeDataReceivedCallback(&m_request, m_largeBuffer, &m_transferBufferLength);
}
}
break;
default:
stallTransaction();
}
}
void Endpoint0::processOUTpacket() {
switch (m_state) {
case State::DataOut:
if (receiveSomeData() < 0) {
break;
}
if ((m_request.wLength() - m_transferBufferLength) <= k_maxPacketSize) {
m_state = State::LastDataOut;
}
break;
case State::LastDataOut:
if (receiveSomeData() < 0) {
break;
}
// Send the DATA1[] to the host.
writePacket(NULL, 0);
m_state = State::StatusIn;
break;
case State::StatusOut:
{
// Read the DATA1[] sent by the host.
readPacket(NULL, 0);
m_state = State::Idle;
// All the data has been sent. Callback the request recipient.
uint8_t type = static_cast<uint8_t>(m_request.recipientType());
if (type == 0) {
// Device recipient
m_requestRecipients[0]->wholeDataSentCallback(&m_request, m_largeBuffer, &m_transferBufferLength);
} else {
// Interface recipient
m_requestRecipients[1]->wholeDataSentCallback(&m_request, m_largeBuffer, &m_transferBufferLength);
}
}
break;
default:
stallTransaction();
}
}
void Endpoint0::flushTxFifo() {
// Set IN endpoint NAK
OTG.DIEPCTL0()->setSNAK(true);
// Wait for core to respond
while (!OTG.DIEPINT(0)->getINEPNE()) {
}
// Get the Tx FIFO number
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()->set(0);
// Wait for the flush
while (OTG.GRSTCTL()->getTXFFLSH()) {
}
}
void Endpoint0::flushRxFifo() {
// Set OUT endpoint NAK
OTG.DOEPCTL0()->setSNAK(true);
// Wait for AHB idle
while (!OTG.GRSTCTL()->getAHBIDL()) {
}
// Flush Rx FIFO
OTG.GRSTCTL()->setRXFFLSH(true);
// Reset packet counter
OTG.DOEPTSIZ0()->set(0);
// Wait for the flush
while (OTG.GRSTCTL()->getRXFFLSH()) {
}
}
void Endpoint0::discardUnreadData() {
for (int i = 0; i < m_receivedPacketSize; i += 4) {
OTG.DFIFO0()->get();
}
m_receivedPacketSize = 0;
}
void Endpoint0::sendSomeData() {
if (k_maxPacketSize < m_transferBufferLength) {
// More than one packet needs to be sent
writePacket(m_largeBuffer + m_bufferOffset, k_maxPacketSize);
m_state = State::DataIn;
m_bufferOffset += k_maxPacketSize;
m_transferBufferLength -= k_maxPacketSize;
return;
}
// Last data packet sent
writePacket(m_largeBuffer + m_bufferOffset, m_transferBufferLength);
if (m_zeroLengthPacketNeeded) {
m_state = State::DataIn;
} else {
m_state = State::LastDataIn;
}
m_bufferOffset = 0;
m_zeroLengthPacketNeeded = false;
m_transferBufferLength = 0;
}
void Endpoint0::clearForOutTransactions(uint16_t wLength) {
m_transferBufferLength = 0;
m_state = (wLength > k_maxPacketSize) ? State::DataOut : State::LastDataOut;
setOutNAK(false);
}
int Endpoint0::receiveSomeData() {
// If it is the first chunk of data to be received, m_transferBufferLength is 0.
uint16_t packetSize = MIN(k_maxPacketSize, m_request.wLength() - m_transferBufferLength);
uint16_t sizeOfPacketRead = readPacket(m_largeBuffer + m_transferBufferLength, packetSize);
if (sizeOfPacketRead != packetSize) {
stallTransaction();
return -1;
}
m_transferBufferLength += packetSize;
return packetSize;
}
uint16_t Endpoint0::readPacket(void * buffer, uint16_t length) {
uint32_t * buffer32 = (uint32_t *) buffer;
uint16_t buffer32Length = MIN(length, m_receivedPacketSize);
int i;
// The RX FIFO is read 4 bytes by 4 bytes
for (i = buffer32Length; i >= 4; i -= 4) {
*buffer32++ = OTG.DFIFO0()->get();
m_receivedPacketSize -= 4;
}
if (i) {
/* If there are remaining bytes that should be read, read the next 4 bytes
* and copy only the wanted bytes. */
uint32_t extraData = OTG.DFIFO0()->get();
memcpy(buffer32, &extraData, i);
if (m_receivedPacketSize < 4) {
m_receivedPacketSize = 0;
} else {
m_receivedPacketSize -= 4;
}
}
return buffer32Length;
}
uint16_t Endpoint0::writePacket(const void * buffer, uint16_t length) {
const uint32_t * buffer32 = (uint32_t *) buffer;
// Return if there is already a packet waiting to be read in the TX FIFO
if (OTG.DIEPTSIZ0()->getPKTCNT()) {
return 0;
}
// Enable transmission
class OTG::DIEPTSIZ0 dieptsiz0(0);
// Indicate that the Transfer Size is one packet
dieptsiz0.setPKTCNT(1);
// Indicate the length of the Transfer Size
dieptsiz0.setXFRSIZ(length);
OTG.DIEPTSIZ0()->set(dieptsiz0);
// Enable the endpoint
OTG.DIEPCTL0()->setEPENA(true);
// Clear the NAK bit
OTG.DIEPCTL0()->setCNAK(true);
// Copy the buffer to the TX FIFO by writing data 32bits by 32 bits.
for (int i = length; i > 0; i -= 4) {
OTG.DFIFO0()->set(*buffer32++);
}
return length;
}
void Endpoint0::stallTransaction() {
OTG.DIEPCTL0()->setSTALL(true);
m_state = State::Idle;
}
void Endpoint0::computeZeroLengthPacketNeeded() {
if (m_transferBufferLength
&& m_transferBufferLength < m_request.wLength()
&& m_transferBufferLength % k_maxPacketSize == 0)
{
m_zeroLengthPacketNeeded = true;
return;
}
m_zeroLengthPacketNeeded = false;
}
}
}
}