Hello,
I have been developing firmware for the C6748 which operates the USB 2.0 (OTG) in peripheral mode. We require the C6748 to register as a high speed device with an embedded Linux host. The current state of affairs is that about 70% of the time enumeration (and further operations) in high speed mode happen without any problems. The remaining 30% of the time, however, the C6748 registers as a full speed device.
I would be very keen in any advice anyone could offer in resolving the issue. I'll post some code below. The remainder of this post just just some questions I have encountered in trying to solve the problem and may not be related to the actual cause!
- One possible avenue of investigation for me is the mention of extraneous reset interrupts in the C6748's sillicon errata (see advisory 2.1.3 of sprz303d). By adding various logs throughout our driver I've observed that I indeed get quite a few reset interrupts. Remarkably, upon receiving any of these interrupts, POWER[RESET] is actually low (see 4.22 of sprufm9h), indicating reset signalling is no longer present. I was under the impression that reset signalling was in the order of tens of milliseconds, so I am a little surprised at this result. Any clues as to what is going on would be appreciated.
- Is it possible other code could interfere with the enumeration. That is, what happens if some other code disables interrupts, or if some other interrupt preempts the USB0 interrupt. I tried to eliminate these causes in our firmware.
- The documentation mentions a high-speed device should first register as a full speed device. When I set HSEN to 1, the first time the device is reset, it registers as a high-speed device. Is this the correct course of action?
- The documentation isn't very clear what should happen when I receive a "RESET_BABBLE" interrupt; is there a problem in this part of the code?
Not in regards to the problem above, could compiler optimisation cause the code below the malfunction? The problem above occurs without optimisations, but if optimisations could cause a problem in the future I'd like to be aware of it. - Could the problem be related to the memory/cache configuration? Currently this code is located in external memory and is cached. I noticed that sometimes I get an endpoint 0 interrupt in IDLE mode where neither SETUPEND, SENTSTALL, RXPKTRDY or TYPKTRDY is true (this should not happen according to 2.7.1.1.5 of sprufm9h). Yet if I put a breakpoint there and look at the register values, it seems RXPKTRDY *is* set.
Any help is appreciated!
Kind regards,
Mark
namespace Model
{
UsbPeripheralCommsDriver::UsbPeripheralCommsDriver(const std::string& firmwareVersion, PTR(IFaultReporter) faultReporterPtr)
:
Task(USB_PERIPHERAL_COMMS_DRIVER_TASK_NAME),
ISender(),
m_state(eDEFAULT),
m_operatingSpeed(eHIGH_SPEED),
m_endpoint0State(eIDLE),
m_endpoint1InState(eNORMAL),
m_endpoint1OutState(eNORMAL),
m_lastReceived_bRequest(0u),
m_lastReceived_bmRequestType(0u),
m_lastReceived_wValue(0u),
m_lastReceived_wIndex(0u),
m_lastReceived_wLength(0u),
m_lastReceived_bmRequestType_transferDirection(0u),
m_lastReceived_bmRequestType_type(0u),
m_lastReceived_bmRequestType_recipient(0u),
m_isFirstReset(true),
m_nextInterruptIsStatusStage(false),
m_resetSignallingPresent(false),
m_mustSendShortPacket(false),
m_bytesToSendPtr(0),
m_bytesLeftToSend(0),
m_bytesLeftToReceive(0),
m_currentDeviceConfiguration(0u),
m_currentAlternateSettingIndexForMessageExchangeInterface(MESSAGE_EXCHANGE_ALTERNATE_SETTING_INDEX),
m_endpoint1OutDataBuffer(0u),
m_receiveMailboxDataBuffer(0u),
m_endpoint1InDataBuffer(0u),
m_transmitMailboxDataBuffer(0u),
m_endpoint0DataBuffer(0u),
m_receiveMailboxHandle(0),
m_transmitMailboxHandle(0),
m_transmitMutex(),
m_receiverPtr(0),
m_messageSplitterPtr(0),
m_faultReporterPtr(GET_RAW_PTR(faultReporterPtr))
{
m_receiveMailboxHandle = MBX_create(RECEIVE_MAILBOX_BUFFER_SIZE,
RECEIVE_MAILBOX_NUM_BUFFERS,
&MBX_ATTRS);
m_transmitMailboxHandle = MBX_create(TRANSMIT_MAILBOX_BUFFER_SIZE,
TRANSMIT_MAILBOX_NUM_BUFFERS,
&MBX_ATTRS);
m_endpoint1OutDataBuffer = new uint8_t[ENDPOINT_1_OUT_DATA_BUFFER_SIZE];
m_receiveMailboxDataBuffer = new uint8_t[ENDPOINT_1_OUT_DATA_BUFFER_SIZE];
m_endpoint1InDataBuffer = new uint8_t[ENDPOINT_1_IN_DATA_BUFFER_SIZE];
m_transmitMailboxDataBuffer = new uint8_t[ENDPOINT_1_IN_DATA_BUFFER_SIZE];
m_endpoint0DataBuffer = new uint8_t[ENDPOINT_0_DATA_BUFFER_SIZE];
initialiseHardware();
initialiseUSB();
initialiseEndpoints();
unsigned char * productStringPtr = PRODUCT_STRING_DESCRIPTOR + PRODUCT_STRING_VERSION_OFFSET;
unsigned char * closingBracketPositionInProductStringPtr = productStringPtr;
for (size_t c = 0; c < PRODUCT_STRING_VERSION_EXPECTED_LENGTH; ++c)
{
const char CHARACTER = (c < firmwareVersion.length())
? firmwareVersion[c]
: ' ';
*productStringPtr = CHARACTER;
productStringPtr += sizeof(uint16_t);
if (CHARACTER != ' ')
{
closingBracketPositionInProductStringPtr = productStringPtr;
}
// else do nothing.
}
*(closingBracketPositionInProductStringPtr) = ')';
}
UsbPeripheralCommsDriver::~UsbPeripheralCommsDriver()
{
MBX_delete(m_receiveMailboxHandle);
MBX_delete(m_transmitMailboxHandle);
delete[] (m_endpoint1OutDataBuffer);
delete[] (m_receiveMailboxDataBuffer);
delete[] (m_endpoint1InDataBuffer);
delete[] (m_transmitMailboxDataBuffer);
delete[] (m_endpoint0DataBuffer);
}
void UsbPeripheralCommsDriver::serviceInterrupt()
{
const Uint32 interruptSourceRegister = USB0_REGS_PTR->INTMASKEDR;
// Clear interrupt.
USB0_REGS_PTR->INTCLRR = interruptSourceRegister;
//if (interruptSourceRegister != 2u)
//{
// LOG_printf(&usbOtgLog, "BEG ISR USB0 0x%x", interruptSourceRegister);
//}
if (CSL_FEXT(interruptSourceRegister, USB_OTG_INTSRCR_RESET_BABBLE))
{
/**
* NOTE: Due to bugs in the C6748 we may receive many reset interrupts during
* reset signalling and we must assert all of them. See advisory 2.1.4 in the
* sillicon errata.
*/
if (m_resetSignallingPresent)
{
LOG_printf(&usbOtgLog, "WARNING: Extraneous RESET interrupt received.");
}
else
{
LOG_printf(&usbOtgLog, "Start of RESET signalling detected.");
for (size_t i = 0; i < 100; i++)
{
if (CSL_FEXT(USB0_REGS_PTR->POWER, USB_OTG_POWER_RESET))
{
LOG_printf(&usbOtgLog, "RESSIG: 1" );
}
else
{
LOG_printf(&usbOtgLog, "RESSIG: 0" );
break;
}
}
m_resetSignallingPresent = true;
}
}
// else do nothing.
if (!m_resetSignallingPresent)
{
if (CSL_FEXT(interruptSourceRegister, USB_OTG_INTSRCR_RESUME))
{
serviceResumeInterrupt();
}
//else do nothing
if (CSL_FEXT(interruptSourceRegister, USB_OTG_INTSRCR_EP0))
{
serviceEndpoint0Interrupt();
}
//else do nothing
if (CSL_FEXT(interruptSourceRegister, USB_OTG_INTSRCR_EP1RX))
{
serviceEndpoint1OutInterrupt();
}
//else do nothing
if (CSL_FEXT(interruptSourceRegister, USB_OTG_INTSRCR_EP1TX))
{
serviceEndpoint1InInterrupt();
}
//else do nothing
if (CSL_FEXT(interruptSourceRegister, USB_OTG_INTSRCR_SOF))
{
serviceResumeInterrupt();
}
//else do nothing
if (CSL_FEXT(interruptSourceRegister, USB_OTG_INTSRCR_DISCONNECT))
{
serviceDisconnectInterrupt();
}
//else do nothing
if (CSL_FEXT(interruptSourceRegister, USB_OTG_INTSRCR_SUSPEND))
{
serviceSuspendInterrupt();
}
//else do nothing
}
else
{
if (interruptSourceRegister != CSL_USB_OTG_INTSRCR_RESET_BABBLE_MASK)
{
LOG_printf(&usbOtgLog, "WARNING: Ignoring interrupt due to presence of RESET signalling (0x%x).", interruptSourceRegister);
}
// else do nothing.
}
// Unassert interrupt.
USB0_REGS_PTR->EOIR = 0x00;
//if (interruptSourceRegister != 2u)
//{
// LOG_printf(&usbOtgLog, "END ISR USB0 0x%x", interruptSourceRegister);
//}
}
void UsbPeripheralCommsDriver::setup(PTR(IReceiver) receiverPtr, PTR(IMessageSplitter) messageSplitterPtr)
{
m_receiverPtr = receiverPtr;
m_messageSplitterPtr = GET_RAW_PTR(messageSplitterPtr);;
}
void UsbPeripheralCommsDriver::serviceResetInterrupt()
{
m_state = eDEFAULT;
m_endpoint0State = eIDLE;
m_endpoint1InState = eNORMAL;
m_endpoint1OutState = eNORMAL;
m_nextInterruptIsStatusStage = false;
extractOperatingSpeed();
/**
* There are posts on the TI forum that suggest the following statements
* should be there. Also the user guide suggests that many registers are cleared
* during reset [see Sect. 2.7.1].
*/
initialiseUSB();
initialiseEndpoints();
#ifdef ENABLE_ADDITIONAL_LOGGING
LOG_printf(&usbOtgLog, "Handled RESET interrupt.");
#ifdef ENABLE_ADDITIONAL_LOGGING
if (m_operatingSpeed == eHIGH_SPEED)
{
LOG_printf(&usbOtgLog, "Peripheral operating at HIGH speed.");
}
else
{
LOG_printf(&usbOtgLog, "WARNING: Peripheral operating at FULL speed (reliable operation requires HIGH speed).");
}
#endif
#endif
}
void UsbPeripheralCommsDriver::serviceResumeInterrupt()
{
// Do nothing (logging will yield many entries).
}
void UsbPeripheralCommsDriver::serviceSuspendInterrupt()
{
#ifdef ENABLE_ADDITIONAL_LOGGING
// Just log it.
LOG_printf(&usbOtgLog, "Handled SUSPEND interrupt.");
#endif
}
void UsbPeripheralCommsDriver::serviceDisconnectInterrupt()
{
#ifdef ENABLE_ADDITIONAL_LOGGING
// Just log it.
LOG_printf(&usbOtgLog, "Handled DISCONNECT interrupt.");
#endif
}
void UsbPeripheralCommsDriver::serviceEndpoint0Interrupt()
{
USB0_REGS_PTR->INDEX = 0u;
const bool CONTROL_TRANSFER_STALLED = CSL_FEXT(USB0_REGS_PTR->TXCSR.PERI_CSR0, USB_OTG_PERI_CSR0_SENTSTALL);
const bool CONTROL_TRANSFER_ENDED_PREMATURELY = CSL_FEXT(USB0_REGS_PTR->TXCSR.PERI_CSR0, USB_OTG_PERI_CSR0_SETUPEND);
const bool RXPKTRDY = CSL_FEXT(USB0_REGS_PTR->TXCSR.PERI_CSR0, USB_OTG_PERI_CSR0_RXPKTRDY);
const bool TXPKTRDY = CSL_FEXT(USB0_REGS_PTR->TXCSR.PERI_CSR0, USB_OTG_PERI_CSR0_TXPKTRDY);
if (CONTROL_TRANSFER_STALLED)
{
// Clear the stall bit.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_CSR0, USB_OTG_PERI_CSR0_SENTSTALL, 0);
// Set the state of Ep0 to IDLE
m_endpoint0State = eIDLE;
// Current transfer is aborted.
m_nextInterruptIsStatusStage = false;
#ifdef ENABLE_ADDITIONAL_LOGGING
// Log the event.
LOG_printf(&usbOtgLog, "Control transfer ended due to STALL condition.");
#endif
}
// else do nothing
if (CONTROL_TRANSFER_ENDED_PREMATURELY)
{
// Clear the setupend bit.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_CSR0, USB_OTG_PERI_CSR0_SERV_SETUPEND, 1);
// Set the state of Ep0 to IDLE
m_endpoint0State = eIDLE;
// Current transfer is aborted.
m_nextInterruptIsStatusStage = false;
#ifdef ENABLE_ADDITIONAL_LOGGING
// Log this event.
LOG_printf(&usbOtgLog, "Control transfer ended prematurely.");
#endif
}
// else do nothing
if (m_nextInterruptIsStatusStage)
{
#ifdef LOG_EP0_INTERRUPT_STAGE
LOG_printf(&usbOtgLog, "Handling EP0 interrupt in STATUS mode.");
#endif
serviceEndpoint0InterruptStatusStage();
// Current transfer is complete now.
m_nextInterruptIsStatusStage = false;
}
else
{
if (m_endpoint0State == eIDLE)
{
#ifdef LOG_EP0_INTERRUPT_STAGE
LOG_printf(&usbOtgLog, "Handling EP0 interrupt in IDLE mode RXPKTRDY=%d, TXPKTRDY=%d.", RXPKTRDY, TXPKTRDY);
#endif
serviceEndpoint0InterruptIdleMode();
}
// else do nothing
if (m_endpoint0State == eRX)
{
#ifdef LOG_EP0_INTERRUPT_STAGE
LOG_printf(&usbOtgLog, "Handling EP0 interrupt in RX mode.");
#endif
serviceEndpoint0InterruptRxMode();
}
// else do nothing
if (m_endpoint0State == eTX)
{
#ifdef LOG_EP0_INTERRUPT_STAGE
LOG_printf(&usbOtgLog, "Handling EP0 interrupt in TX mode.");
#endif
serviceEndpoint0InterruptTxMode();
}
// else do nothing
}
}
void UsbPeripheralCommsDriver::serviceEndpoint0InterruptIdleMode()
{
const bool RECEIVED_PACKET = CSL_FEXT(USB0_REGS_PTR->TXCSR.PERI_CSR0, USB_OTG_PERI_CSR0_RXPKTRDY);
if (RECEIVED_PACKET)
{
/**
* NOTE: The ONLY valid reason for an interrupt to be generated in IDLE mode
* is after the SETUP stage of a control transfer has been completed.
*/
const uint16_t DATA_PACKET_SIZE = USB0_REGS_PTR->COUNT.COUNT0;
bool commandRecognised = false;
if (DATA_PACKET_SIZE == DATA_PACKET_SIZE_FOR_SETUP_PACKET)
{
// Unload FIFO
readEndpoint0FIFO(DATA_PACKET_SIZE, m_endpoint0DataBuffer);
// Extract transfer setup information from data.
serviceEndpoint0InterruptExtractSetupData();
if (m_lastReceived_bmRequestType_type == REQUEST_TYPE_TRANSFER_TYPE_STANDARD)
{
if ((m_lastReceived_bmRequestType_transferDirection == REQUEST_TYPE_TRANSFER_DIRECTION_DEVICE_TO_HOST) &&
(m_lastReceived_bmRequestType_recipient == REQUEST_TYPE_TRANSFER_RECIPIENT_DEVICE ) &&
(m_lastReceived_bRequest == REQUEST_GET_DESCRIPTOR ))
{
/* GET_DESCRIPTOR */
/**
* NOTE: The USB specification allows for a mismatch between
* wLength and the descriptor size.
*/
const uint8_t REQUESTED_DESCRIPTOR_TYPE = static_cast<uint8_t>((m_lastReceived_wValue >> BITS_IN_BYTE) & UINT16_MASK_BYTE_0);
const uint8_t REQUESTED_DESCRIPTOR_INDEX = static_cast<uint8_t>((m_lastReceived_wValue ) & UINT16_MASK_BYTE_0);
if ((REQUESTED_DESCRIPTOR_TYPE == DESCRIPTOR_TYPE_DEVICE) &&
(REQUESTED_DESCRIPTOR_INDEX == 0))
{
commandRecognised = true;
m_bytesToSendPtr = DEVICE_DESCRIPTOR;
m_bytesLeftToSend = std::min(static_cast<uint16_t>(sizeof(DEVICE_DESCRIPTOR)), m_lastReceived_wLength);
}
else if ((REQUESTED_DESCRIPTOR_TYPE == DESCRIPTOR_TYPE_CONFIGURATION) &&
(REQUESTED_DESCRIPTOR_INDEX == 0))
{
commandRecognised = true;
if (m_operatingSpeed == eFULL_SPEED)
{
m_bytesToSendPtr = CONFIGURATION_DESCRIPTOR_FULL_SPEED;
m_bytesLeftToSend = std::min(static_cast<uint16_t>(sizeof(CONFIGURATION_DESCRIPTOR_FULL_SPEED)), m_lastReceived_wLength);
}
else if (m_operatingSpeed == eHIGH_SPEED)
{
m_bytesToSendPtr = CONFIGURATION_DESCRIPTOR_HIGH_SPEED;
m_bytesLeftToSend = std::min(static_cast<uint16_t>(sizeof(CONFIGURATION_DESCRIPTOR_HIGH_SPEED)), m_lastReceived_wLength);
}
}
else if ((REQUESTED_DESCRIPTOR_TYPE == DESCRIPTOR_TYPE_OTHER_SPEED_CONFIGURATION) &&
(REQUESTED_DESCRIPTOR_INDEX == 0))
{
commandRecognised = true;
if (m_operatingSpeed == eFULL_SPEED)
{
m_bytesToSendPtr = OTHER_SPEED_CONFIGURATION_DESCRIPTOR_WHEN_IN_FULL_SPEED;
m_bytesLeftToSend = std::min(static_cast<uint16_t>(sizeof(OTHER_SPEED_CONFIGURATION_DESCRIPTOR_WHEN_IN_FULL_SPEED)), m_lastReceived_wLength);
}
else if (m_operatingSpeed == eHIGH_SPEED)
{
m_bytesToSendPtr = OTHER_SPEED_CONFIGURATION_DESCRIPTOR_WHEN_IN_HIGH_SPEED;
m_bytesLeftToSend = std::min(static_cast<uint16_t>(sizeof(OTHER_SPEED_CONFIGURATION_DESCRIPTOR_WHEN_IN_HIGH_SPEED)), m_lastReceived_wLength);
}
}
else if (REQUESTED_DESCRIPTOR_TYPE == DESCRIPTOR_TYPE_STRING)
{
if (REQUESTED_DESCRIPTOR_INDEX == STRING_DESCRIPTOR_INDEX_LANG)
{
commandRecognised = true;
m_bytesToSendPtr = LANG_STRING_DESCRIPTOR;
m_bytesLeftToSend = std::min(static_cast<uint16_t>(sizeof(LANG_STRING_DESCRIPTOR)), m_lastReceived_wLength);
}
else if (REQUESTED_DESCRIPTOR_INDEX == STRING_DESCRIPTOR_INDEX_MANUFACTURER)
{
commandRecognised = true;
m_bytesToSendPtr = MANUFACTURER_STRING_DESCRIPTOR;
m_bytesLeftToSend = std::min(static_cast<uint16_t>(sizeof(MANUFACTURER_STRING_DESCRIPTOR)), m_lastReceived_wLength);
}
else if (REQUESTED_DESCRIPTOR_INDEX == STRING_DESCRIPTOR_INDEX_PRODUCT)
{
commandRecognised = true;
m_bytesToSendPtr = PRODUCT_STRING_DESCRIPTOR;
m_bytesLeftToSend = std::min(static_cast<uint16_t>(sizeof(PRODUCT_STRING_DESCRIPTOR)), m_lastReceived_wLength);
}
else if (REQUESTED_DESCRIPTOR_INDEX == STRING_DESCRIPTOR_INDEX_SERIAL)
{
commandRecognised = true;
m_bytesToSendPtr = SERIAL_STRING_DESCRIPTOR;
m_bytesLeftToSend = std::min(static_cast<uint16_t>(sizeof(SERIAL_STRING_DESCRIPTOR)), m_lastReceived_wLength);
}
else if (REQUESTED_DESCRIPTOR_INDEX == STRING_DESCRIPTOR_INDEX_CONFIGURATION_FULL_SPEED)
{
commandRecognised = true;
m_bytesToSendPtr = CONFIGURATION_STRING_DESCRIPTOR_FULL_SPEED;
m_bytesLeftToSend = std::min(static_cast<uint16_t>(sizeof(CONFIGURATION_STRING_DESCRIPTOR_FULL_SPEED)), m_lastReceived_wLength);
}
else if (REQUESTED_DESCRIPTOR_INDEX == STRING_DESCRIPTOR_INDEX_CONFIGURATION_HIGH_SPEED)
{
commandRecognised = true;
m_bytesToSendPtr = CONFIGURATION_STRING_DESCRIPTOR_HIGH_SPEED;
m_bytesLeftToSend = std::min(static_cast<uint16_t>(sizeof(CONFIGURATION_STRING_DESCRIPTOR_HIGH_SPEED)), m_lastReceived_wLength);
}
else if (REQUESTED_DESCRIPTOR_INDEX == STRING_DESCRIPTOR_INDEX_INTERFACE)
{
commandRecognised = true;
m_bytesToSendPtr = INTERFACE_STRING_DESCRIPTOR;
m_bytesLeftToSend = std::min(static_cast<uint16_t>(sizeof(INTERFACE_STRING_DESCRIPTOR)), m_lastReceived_wLength);
}
// else do nothing
}
else if ((REQUESTED_DESCRIPTOR_TYPE == DESCRIPTOR_TYPE_DEVICE_QUALIFIER) &&
(REQUESTED_DESCRIPTOR_INDEX == 0))
{
commandRecognised = true;
m_bytesToSendPtr = DEVICE_QUALIFIER_DESCIPTOR;
m_bytesLeftToSend = std::min(static_cast<uint16_t>(sizeof(DEVICE_QUALIFIER_DESCIPTOR)), m_lastReceived_wLength);
}
// else do nothing.
}
else if ((m_lastReceived_bmRequestType_transferDirection == REQUEST_TYPE_TRANSFER_DIRECTION_DEVICE_TO_HOST) &&
(m_lastReceived_bmRequestType_recipient == REQUEST_TYPE_TRANSFER_RECIPIENT_DEVICE ) &&
(m_lastReceived_bRequest == REQUEST_GET_CONFIGURATION ) &&
(m_lastReceived_wLength == REQUEST_LENGTH_GET_CONFIGURATION ))
{
/* GET_CONFIGURATION */
if (m_state != eDEFAULT)
{
commandRecognised = true;
m_bytesToSendPtr = &m_currentDeviceConfiguration;
m_bytesLeftToSend = 1u;
}
// else device behaviour undefined
}
else if ((m_lastReceived_bmRequestType_transferDirection == REQUEST_TYPE_TRANSFER_DIRECTION_DEVICE_TO_HOST) &&
(m_lastReceived_bmRequestType_recipient == REQUEST_TYPE_TRANSFER_RECIPIENT_INTERFACE ) &&
(m_lastReceived_bRequest == REQUEST_GET_INTERFACE ) &&
(m_lastReceived_wLength == REQUEST_LENGTH_GET_INTERFACE ))
{
/* GET_INTERFACE */
if (m_state == eCONFIGURED)
{
const uint8_t INTERFACE_INDEX = m_lastReceived_wIndex;
if (INTERFACE_INDEX == MESSAGE_EXCHANGE_INTERFACE_INDEX)
{
commandRecognised = true;
m_bytesToSendPtr = &m_currentAlternateSettingIndexForMessageExchangeInterface;
m_bytesLeftToSend = 1u;
}
// else it is not a valid request.
}
// else give request error.
}
else if ((m_lastReceived_bmRequestType_transferDirection == REQUEST_TYPE_TRANSFER_DIRECTION_DEVICE_TO_HOST) &&
(m_lastReceived_bRequest == REQUEST_GET_STATUS ) &&
(m_lastReceived_wLength == REQUEST_LENGTH_GET_STATUS ))
{
/* GET_STATUS */
if (m_lastReceived_bmRequestType_recipient == REQUEST_TYPE_TRANSFER_RECIPIENT_DEVICE)
{
commandRecognised = true;
m_bytesToSendPtr = DEVICE_STATUS;
m_bytesLeftToSend = 2u;
}
else if (m_lastReceived_bmRequestType_recipient == REQUEST_TYPE_TRANSFER_RECIPIENT_INTERFACE)
{
if (m_state == eCONFIGURED)
{
const uint8_t REQUESTED_INTERFACE_INDEX = m_lastReceived_wIndex;
if (REQUESTED_INTERFACE_INDEX == MESSAGE_EXCHANGE_INTERFACE_INDEX)
{
commandRecognised = true;
m_bytesToSendPtr = INTERFACE_STATUS;
m_bytesLeftToSend = 2u;
}
// else do nothing.
}
// else do nothing.
}
else if (m_lastReceived_bmRequestType_recipient == REQUEST_TYPE_TRANSFER_RECIPIENT_ENDPOINT)
{
if (m_state != eDEFAULT)
{
const uint8_t REQUESTED_ENDPOINT_INDEX = m_lastReceived_wIndex;
if (REQUESTED_ENDPOINT_INDEX == 0u)
{
// Endpoint 0 never halts (USB specification says it is not required nor recommended).
commandRecognised = true;
m_bytesToSendPtr = ENDPOINT_STATUS_NORMAL;
m_bytesLeftToSend = 2u;
}
else if (REQUESTED_ENDPOINT_INDEX == ENDPOINT_1_IN_ADDRESS)
{
// Only respond when configured.
if (m_state == eCONFIGURED)
{
commandRecognised = true;
m_bytesLeftToSend = 2u;
if (m_endpoint1InState == eNORMAL)
{
m_bytesToSendPtr = ENDPOINT_STATUS_NORMAL;
}
else // if (m_endpoint1InState == eHALTED)
{
m_bytesToSendPtr = ENDPOINT_STATUS_HALTED;
}
}
// else do nothing
}
else if (REQUESTED_ENDPOINT_INDEX == ENDPOINT_1_OUT_ADDRESS)
{
// Only respond when configured.
if (m_state == eCONFIGURED)
{
commandRecognised = true;
m_bytesLeftToSend = 2u;
if (m_endpoint1OutState == eNORMAL)
{
m_bytesToSendPtr = ENDPOINT_STATUS_NORMAL;
}
else // if (m_endpoint1OutState == eHALTED)
{
m_bytesToSendPtr = ENDPOINT_STATUS_HALTED;
}
}
// else do nothing
}
// else do nothing
}
}
// else do nothing
}
else if ((m_lastReceived_bmRequestType_transferDirection == REQUEST_TYPE_TRANSFER_DIRECTION_HOST_TO_DEVICE) &&
(m_lastReceived_bmRequestType_recipient == REQUEST_TYPE_TRANSFER_RECIPIENT_DEVICE ) &&
(m_lastReceived_bRequest == REQUEST_SET_ADDRESS ) &&
(m_lastReceived_wLength == REQUEST_LENGTH_SET_ADDRESS ))
{
/* SET_ADDRESS */
/**
* NOTE: This is the standard device request SET_ADDRESS. We do not
* change the device's address here. For a justification of this see
* Section 9.4.6 of the USB 2.0 specification.
*/
if (m_state != eCONFIGURED)
{
commandRecognised = true;
}
// else do nothing
}
else if ((m_lastReceived_bmRequestType_transferDirection == REQUEST_TYPE_TRANSFER_DIRECTION_HOST_TO_DEVICE) &&
(m_lastReceived_bmRequestType_recipient == REQUEST_TYPE_TRANSFER_RECIPIENT_DEVICE ) &&
(m_lastReceived_bRequest == REQUEST_SET_CONFIGURATION ) &&
(m_lastReceived_wLength == REQUEST_LENGTH_SET_CONFIGURATION ))
{
/* SET_CONFIGURATION */
if (m_state != eDEFAULT)
{
const uint8_t REQUESTED_CONFIGURATION_INDEX = m_lastReceived_wValue;
if ((REQUESTED_CONFIGURATION_INDEX == 0u) ||
(REQUESTED_CONFIGURATION_INDEX == CONFIGURATION_INDEX))
{
commandRecognised = true;
}
// else this is not a valid configuration number.
}
// else device behaviour unspecified when no address has been assigned.
}
else if ((m_lastReceived_bmRequestType_transferDirection == REQUEST_TYPE_TRANSFER_DIRECTION_HOST_TO_DEVICE) &&
(m_lastReceived_bRequest == REQUEST_SET_FEATURE ) &&
(m_lastReceived_wLength == REQUEST_LENGTH_SET_FEATURE ))
{
/* SET_FEATURE */
/**
* NOTE: We do NOT support TEST_MODE (used only for compliance testing).
*/
if (m_state == eCONFIGURED)
{
if (m_lastReceived_bmRequestType_recipient == REQUEST_TYPE_TRANSFER_RECIPIENT_ENDPOINT)
{
const uint8_t REQUESTED_FEATURE_SELECTOR = m_lastReceived_wValue;
const uint8_t REQUESTED_ENDPOINT_ADDRESS = m_lastReceived_wIndex;
if (REQUESTED_FEATURE_SELECTOR == FEATURE_SELECTOR_ENDPOINT_HALT)
{
if ((REQUESTED_ENDPOINT_ADDRESS == ENDPOINT_1_IN_ADDRESS) ||
(REQUESTED_ENDPOINT_ADDRESS == ENDPOINT_1_OUT_ADDRESS))
{
/**
* NOTE: We do not clear the feature here but wait for the status
* stage of this control transfer.
*/
commandRecognised = true;
}
}
// else do nothing
}
// else do nothing
}
// else do nothing
}
else if ((m_lastReceived_bmRequestType_transferDirection == REQUEST_TYPE_TRANSFER_DIRECTION_HOST_TO_DEVICE) &&
(m_lastReceived_bRequest == REQUEST_CLEAR_FEATURE ) &&
(m_lastReceived_wLength == REQUEST_LENGTH_CLEAR_FEATURE ))
{
/* CLEAR_FEATURE */
if (m_state == eCONFIGURED)
{
if (m_lastReceived_bmRequestType_recipient == REQUEST_TYPE_TRANSFER_RECIPIENT_ENDPOINT)
{
const uint8_t REQUESTED_FEATURE_SELECTOR = m_lastReceived_wValue;
const uint8_t REQUESTED_ENDPOINT_ADDRESS = m_lastReceived_wIndex;
if (REQUESTED_FEATURE_SELECTOR == FEATURE_SELECTOR_ENDPOINT_HALT)
{
if ((REQUESTED_ENDPOINT_ADDRESS == ENDPOINT_1_IN_ADDRESS) ||
(REQUESTED_ENDPOINT_ADDRESS == ENDPOINT_1_OUT_ADDRESS))
{
/**
* NOTE: We do not clear the feature here but wait for the status
* stage of this control transfer.
*/
commandRecognised = true;
}
}
// else do nothing
}
// else do nothing
}
// else do nothing
}
else if ((m_lastReceived_bmRequestType_transferDirection == REQUEST_TYPE_TRANSFER_DIRECTION_HOST_TO_DEVICE) &&
(m_lastReceived_bmRequestType_recipient == REQUEST_TYPE_TRANSFER_RECIPIENT_INTERFACE ) &&
(m_lastReceived_bRequest == REQUEST_SET_INTERFACE ) &&
(m_lastReceived_wLength == REQUEST_LENGTH_SET_INTERFACE ))
{
/* SET_INTERFACE */
if (m_state == eCONFIGURED)
{
const uint8_t INTERFACE_INDEX = m_lastReceived_wIndex;
const uint8_t REQUESTED_ALTERNATE_SETTING_INDEX = m_lastReceived_wValue;
if (INTERFACE_INDEX == MESSAGE_EXCHANGE_INTERFACE_INDEX)
{
if (REQUESTED_ALTERNATE_SETTING_INDEX == MESSAGE_EXCHANGE_ALTERNATE_SETTING_INDEX)
{
commandRecognised = true;
}
// else this is not a valid alternate setting for this interface
}
// else this is not a valid interface
}
// else this is not a valid request
}
else if ((m_lastReceived_bmRequestType_transferDirection == REQUEST_TYPE_TRANSFER_DIRECTION_DEVICE_TO_HOST) &&
(m_lastReceived_bmRequestType_recipient == REQUEST_TYPE_TRANSFER_RECIPIENT_ENDPOINT ) &&
(m_lastReceived_bRequest == REQUEST_SYNCH_FRAME ) &&
(m_lastReceived_wLength == REQUEST_LENGTH_SYNCH_FRAME ))
{
/* SYNCH_FRAME */
/**
* NOTE: We do not respond to SYNCH_FRAME because we do not use isochronous transfers.
*/
}
// else do nothing
}
// else do nothing
}
else
{
#ifdef ENABLE_ADDITIONAL_LOGGING
LOG_printf(&usbOtgLog, "Controller received a setup transaction that is not 8 bytes.");
#endif
}
// Service the hardware, set the appropriate state.
if (commandRecognised)
{
const bool COMMAND_HAS_DATA_PHASE = (m_lastReceived_wLength > 0);
if (COMMAND_HAS_DATA_PHASE)
{
// Service the RXPKTRDY bit.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_CSR0, USB_OTG_PERI_CSR0_SERV_RXPKTRDY, 1);
// Proceed to state RX or TX in anticipation of the data phase.
if (m_lastReceived_bmRequestType_transferDirection == REQUEST_TYPE_TRANSFER_DIRECTION_HOST_TO_DEVICE)
{
m_endpoint0State = eRX;
m_bytesLeftToReceive = m_lastReceived_wLength;
LOG_printf(&usbOtgLog, "Receive phase anticipated -- 0x%x bytes to receive.", m_bytesLeftToReceive);
}
else
{
m_endpoint0State = eTX;
/**
* NOTE: In my interpretation of USB 2.0 Specification, Section 8.5.3.2, if we
* are sending less data than requested by the host, we should send a zero-length
* packet if the amount of data we are sending is an exact multiple of the maximum
* packet size. This variable flags this condition.
*/
m_mustSendShortPacket = (m_bytesLeftToSend < m_lastReceived_wLength);
LOG_printf(&usbOtgLog, "Transmit phase anticipated -- 0x%x bytes to send.", m_bytesLeftToSend);
}
}
else // if (!COMMAND_HAS_DATA_PHASE)
{
// Service the RXPKTRDY bit.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_CSR0, USB_OTG_PERI_CSR0_SERV_RXPKTRDY, 1);
// Indicate there will be no further data.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_CSR0, USB_OTG_PERI_CSR0_DATAEND, 1);
// Remain in state IDLE
m_endpoint0State = eIDLE;
// The next interrupt should be a status stage interrupt.
m_nextInterruptIsStatusStage = true;
#ifdef ENABLE_ADDITIONAL_LOGGING
LOG_printf(&usbOtgLog, "No data phase -- anticipating status stage to follow immediately.");
#endif
}
}
else // command was not recognised.
{
// Service the RXPKTRDY bit.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_CSR0, USB_OTG_PERI_CSR0_SERV_RXPKTRDY, 1);
// Tell the controller to send a STALL during the status phase.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_CSR0, USB_OTG_PERI_CSR0_SENDSTALL, 1);
#ifdef ENABLE_ADDITIONAL_LOGGING
// Log the occurrance.
LOG_printf(&usbOtgLog, "Unrecognised SETUP packet received in IDLE mode: bmRequestType=%x, bRequest=%x, ...",
m_lastReceived_bmRequestType,
m_lastReceived_bRequest);
LOG_printf(&usbOtgLog, " ... wValue=%x, wIndex=%x, ...",
m_lastReceived_wValue,
m_lastReceived_wIndex);
LOG_printf(&usbOtgLog, " ... wLength=%x.",
m_lastReceived_wLength);
#endif
}
}
else
{
LOG_printf(&usbOtgLog, "WARNING: EP0 interrupt received in IDLE mode but no packet was received.");
bool receivedPacket = false;
#if 0
while (!receivedPacket)
{
const uint32_t PERI_CSR0 = USB0_REGS_PTR->TXCSR.PERI_CSR0;
LOG_printf(&usbOtgLog, "PERI_CSR0 = 0x%x.", PERI_CSR0);
receivedPacket = CSL_FEXT(PERI_CSR0, USB_OTG_PERI_CSR0_RXPKTRDY);
}
LOG_printf(&usbOtgLog, "OK then...");
#endif
}
}
void UsbPeripheralCommsDriver::serviceEndpoint0InterruptRxMode()
{
const bool RX_PACKET_READY = CSL_FEXT(USB0_REGS_PTR->TXCSR.PERI_CSR0, USB_OTG_PERI_CSR0_RXPKTRDY);
if (RX_PACKET_READY)
{
// Check how many bytes we are receiving.
const uint16_t BYTES_RECEIVED = USB0_REGS_PTR->COUNT.COUNT0;
// Unload the FIFO.
readEndpoint0FIFO(BYTES_RECEIVED, m_endpoint0DataBuffer);
// TODO: Handle the data.
if (BYTES_RECEIVED > m_bytesLeftToReceive)
{
// TODO: According to USB 2.0 Specification, Section 5.5.3, we should stall the pipe
// if we receive more data than we expect.
}
else
{
// According to USB 2.0 Specification, Section 5.5.3, the host does not send a
// zero-length packet if the amount of data being received is a multiple of maxPacketSize.
const bool IS_LAST_PACKET =
(BYTES_RECEIVED < ENDPOINT_0_MAX_PACKET_SIZE) ||
(BYTES_RECEIVED == m_bytesLeftToReceive);
if (IS_LAST_PACKET)
{
// Service the RXPKTRDY bit.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_CSR0, USB_OTG_PERI_CSR0_SERV_RXPKTRDY, 1);
// Indicate there will be no further data.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_CSR0, USB_OTG_PERI_CSR0_DATAEND, 1);
// Transition to state IDLE
m_endpoint0State = eIDLE;
// The next interrupt should be a status stage interrupt.
m_nextInterruptIsStatusStage = true;
}
else
{
// Service the RXPKTRDY bit.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_CSR0, USB_OTG_PERI_CSR0_SERV_RXPKTRDY, 1);
// Do nothing.
m_bytesLeftToReceive -= BYTES_RECEIVED;
}
}
}
// else do nothing
}
void UsbPeripheralCommsDriver::serviceEndpoint0InterruptTxMode()
{
// Determine how many bytes we should write.
const uint16_t BYTES_SENT = std::min<uint16_t>(m_bytesLeftToSend, ENDPOINT_0_MAX_PACKET_SIZE);
// Load the FIFO.
writeEndpoint0FIFO(BYTES_SENT, m_bytesToSendPtr);
// According to USB 2.0 Specification [Section 5.5.3] we do not need to send a zero-length
// packet if the amount of data we are sending is a multiple of maxPacketSize unless the host
// requested more data than we are actually sending [Section 8.5.3.2].
bool isLastPacket = false;
if (BYTES_SENT != ENDPOINT_0_MAX_PACKET_SIZE)
{
// Assert that we sent all data.
assert(BYTES_SENT == m_bytesLeftToSend);
// We sent a short packet.
isLastPacket = true;
}
else // we are not sending a short packet.
{
if (BYTES_SENT == m_bytesLeftToSend)
{
// We are finished unless we require a short packet.
isLastPacket = !m_mustSendShortPacket;
}
else // we did not send all the data.
{
// We have not sent all the data.
isLastPacket = false;
}
}
if (isLastPacket)
{
// Service the TXPKTRDY bit.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_CSR0, USB_OTG_PERI_CSR0_TXPKTRDY, 1);
// Indicate there will be no further data.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_CSR0, USB_OTG_PERI_CSR0_DATAEND, 1);
LOG_printf(&usbOtgLog, "DATAEND written.");
// Transition to state IDLE
m_endpoint0State = eIDLE;
// The next interrupt should be a status stage interrupt.
m_nextInterruptIsStatusStage = true;
}
else
{
// Service the TXPKTRDY bit.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_CSR0, USB_OTG_PERI_CSR0_TXPKTRDY, 1);
// Update data window.
m_bytesLeftToSend -= BYTES_SENT;
m_bytesToSendPtr += BYTES_SENT;
}
}
void UsbPeripheralCommsDriver::serviceEndpoint0InterruptStatusStage()
{
/**
* NOTE: This function gets called when a control transfer has got to the status stage. It
* is only here where we should actually perform any actions.
*/
if (m_lastReceived_bmRequestType_type == REQUEST_TYPE_TRANSFER_TYPE_STANDARD)
{
if ((m_lastReceived_bmRequestType_transferDirection == REQUEST_TYPE_TRANSFER_DIRECTION_DEVICE_TO_HOST) &&
(m_lastReceived_bmRequestType_recipient == REQUEST_TYPE_TRANSFER_RECIPIENT_DEVICE ) &&
(m_lastReceived_bRequest == REQUEST_GET_DESCRIPTOR ))
{
const uint8_t REQUESTED_DESCRIPTOR_TYPE = static_cast<uint8_t>((m_lastReceived_wValue >> BITS_IN_BYTE) & UINT16_MASK_BYTE_0);
const uint8_t REQUESTED_DESCRIPTOR_INDEX = static_cast<uint8_t>((m_lastReceived_wValue) & UINT16_MASK_BYTE_0);
#ifdef ENABLE_ADDITIONAL_LOGGING
LOG_printf(&usbOtgLog, "Handled GET_DESCRIPTOR (DescriptorType=%x, DescriptorIndex=%x, ...",
REQUESTED_DESCRIPTOR_TYPE,
REQUESTED_DESCRIPTOR_INDEX);
LOG_printf(&usbOtgLog, " ... Length=0x%x) [Returned=...].",
m_lastReceived_wLength);
#endif
}
else if ((m_lastReceived_bmRequestType_transferDirection == REQUEST_TYPE_TRANSFER_DIRECTION_DEVICE_TO_HOST) &&
(m_lastReceived_bmRequestType_recipient == REQUEST_TYPE_TRANSFER_RECIPIENT_DEVICE ) &&
(m_lastReceived_bRequest == REQUEST_GET_CONFIGURATION ) &&
(m_lastReceived_wLength == REQUEST_LENGTH_GET_CONFIGURATION ))
{
#ifdef ENABLE_ADDITIONAL_LOGGING
LOG_printf(&usbOtgLog, "Handled GET_CONFIGURATION [Returned=%x].",
m_currentDeviceConfiguration);
#endif
}
else if ((m_lastReceived_bmRequestType_transferDirection == REQUEST_TYPE_TRANSFER_DIRECTION_DEVICE_TO_HOST) &&
(m_lastReceived_bmRequestType_recipient == REQUEST_TYPE_TRANSFER_RECIPIENT_INTERFACE ) &&
(m_lastReceived_bRequest == REQUEST_GET_INTERFACE ) &&
(m_lastReceived_wLength == REQUEST_LENGTH_GET_INTERFACE ))
{
const uint8_t INTERFACE_INDEX = m_lastReceived_wIndex;
#ifdef ENABLE_ADDITIONAL_LOGGING
LOG_printf(&usbOtgLog, "Handled GET_INTERFACE (Interface=%x) [Returned=%x].",
INTERFACE_INDEX,
m_currentAlternateSettingIndexForMessageExchangeInterface);
#endif
}
else if ((m_lastReceived_bmRequestType_transferDirection == REQUEST_TYPE_TRANSFER_DIRECTION_DEVICE_TO_HOST) &&
(m_lastReceived_bRequest == REQUEST_GET_STATUS ) &&
(m_lastReceived_wLength == REQUEST_LENGTH_GET_STATUS ))
{
#ifdef ENABLE_ADDITIONAL_LOGGING
LOG_printf(&usbOtgLog, "Handled GET_STATUS.");
#endif
}
else if ((m_lastReceived_bmRequestType_transferDirection == REQUEST_TYPE_TRANSFER_DIRECTION_HOST_TO_DEVICE) &&
(m_lastReceived_bmRequestType_recipient == REQUEST_TYPE_TRANSFER_RECIPIENT_DEVICE ) &&
(m_lastReceived_bRequest == REQUEST_SET_ADDRESS ) &&
(m_lastReceived_wLength == REQUEST_LENGTH_SET_ADDRESS ))
{
const uint8_t REQUESTED_ADDRESS = m_lastReceived_wValue;
// Only now do we indicate to the controller that the address has changed.
USB0_REGS_PTR->FADDR = REQUESTED_ADDRESS;
if (REQUESTED_ADDRESS == 0u)
{
// We were assigned the default address.
m_state = eDEFAULT;
}
else
{
// The address is a non-default one.
m_state = eADDRESS;
}
#ifdef ENABLE_ADDITIONAL_LOGGING
// We no longer have an address.
LOG_printf(&usbOtgLog, "Handled SET_ADDRESS (Address=%x).", REQUESTED_ADDRESS);
#endif
}
else if ((m_lastReceived_bmRequestType_transferDirection == REQUEST_TYPE_TRANSFER_DIRECTION_HOST_TO_DEVICE) &&
(m_lastReceived_bmRequestType_recipient == REQUEST_TYPE_TRANSFER_RECIPIENT_DEVICE ) &&
(m_lastReceived_bRequest == REQUEST_SET_CONFIGURATION ) &&
(m_lastReceived_wLength == REQUEST_LENGTH_SET_CONFIGURATION ))
{
if (m_state != eDEFAULT)
{
const uint8_t REQUESTED_CONFIGURATION_INDEX = m_lastReceived_wValue;
m_currentDeviceConfiguration = REQUESTED_CONFIGURATION_INDEX;
if (REQUESTED_CONFIGURATION_INDEX == 0u)
{
// Unconfigure device
m_state = eADDRESS;
}
else if (REQUESTED_CONFIGURATION_INDEX == CONFIGURATION_INDEX)
{
// Unconfigure device
m_state = eCONFIGURED;
// Set alternate settings [for clarification only, we only have one alternate setting, really]
m_currentAlternateSettingIndexForMessageExchangeInterface = MESSAGE_EXCHANGE_ALTERNATE_SETTING_INDEX;
// Reinitialise endpoints (ensures data toggle bit is correctly set).
initialiseEndpoints();
}
#ifdef ENABLE_ADDITIONAL_LOGGING
// We no longer have an address.
LOG_printf(&usbOtgLog, "Handled SET_CONFIGURATION (Configuration=%x).",
REQUESTED_CONFIGURATION_INDEX);
#endif
}
// else we may not configure the device in the default state.
}
else if ((m_lastReceived_bmRequestType_transferDirection == REQUEST_TYPE_TRANSFER_DIRECTION_HOST_TO_DEVICE) &&
(m_lastReceived_bmRequestType_recipient == REQUEST_TYPE_TRANSFER_RECIPIENT_INTERFACE ) &&
(m_lastReceived_bRequest == REQUEST_SET_INTERFACE ) &&
(m_lastReceived_wLength == REQUEST_LENGTH_SET_INTERFACE ))
{
if (m_state == eCONFIGURED)
{
const uint8_t INTERFACE_INDEX = m_lastReceived_wIndex;
const uint8_t REQUESTED_ALTERNATE_SETTING_INDEX = m_lastReceived_wValue;
if (INTERFACE_INDEX == MESSAGE_EXCHANGE_INTERFACE_INDEX)
{
// Set alternate setting!
m_currentAlternateSettingIndexForMessageExchangeInterface = REQUESTED_ALTERNATE_SETTING_INDEX;
// Reinitialise endpoints (ensures data toggle bit is correctly set).
initialiseEndpoints();
#ifdef ENABLE_ADDITIONAL_LOGGING
LOG_printf(&usbOtgLog, "Handled SET_INTERFACE (Interface=%x, AlternateSetting=%x).",
INTERFACE_INDEX,
REQUESTED_ALTERNATE_SETTING_INDEX);
#endif
}
// else do nothing
}
// else we may not select an alternate setting without being configured.
}
else if ((m_lastReceived_bmRequestType_transferDirection == REQUEST_TYPE_TRANSFER_DIRECTION_HOST_TO_DEVICE) &&
(m_lastReceived_bRequest == REQUEST_SET_FEATURE ) &&
(m_lastReceived_wLength == REQUEST_LENGTH_SET_FEATURE ))
{
/* SET_FEATURE */
if (m_state == eCONFIGURED)
{
if (m_lastReceived_bmRequestType_recipient == REQUEST_TYPE_TRANSFER_RECIPIENT_ENDPOINT)
{
const uint8_t REQUESTED_FEATURE_SELECTOR = m_lastReceived_wValue;
const uint8_t REQUESTED_ENDPOINT_ADDRESS = m_lastReceived_wIndex;
if (REQUESTED_FEATURE_SELECTOR == FEATURE_SELECTOR_ENDPOINT_HALT)
{
if (REQUESTED_ENDPOINT_ADDRESS == ENDPOINT_1_IN_ADDRESS)
{
m_endpoint1InState = eHALTED;
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_TXCSR, USB_OTG_PERI_TXCSR_SENDSTALL, 0u);
#ifdef ENABLE_ADDITIONAL_LOGGING
LOG_printf(&usbOtgLog, "Handled SET_FEATURE (FeatureSelector=HALT, Endpoint=\"EP1 (IN)\").");
#endif
}
else if (REQUESTED_ENDPOINT_ADDRESS == ENDPOINT_1_OUT_ADDRESS)
{
m_endpoint1OutState = eHALTED;
CSL_FINS(USB0_REGS_PTR->RXCSR.PERI_RXCSR, USB_OTG_PERI_RXCSR_SENDSTALL, 0u);
#ifdef ENABLE_ADDITIONAL_LOGGING
LOG_printf(&usbOtgLog, "Handled SET_FEATURE (FeatureSelector=HALT, Endpoint=\"EP1 (OUT)\").");
#endif
}
// else do nothing
}
// else do nothing
}
// else do nothing
}
// else do nothing
}
else if ((m_lastReceived_bmRequestType_transferDirection == REQUEST_TYPE_TRANSFER_DIRECTION_HOST_TO_DEVICE) &&
(m_lastReceived_bRequest == REQUEST_CLEAR_FEATURE ) &&
(m_lastReceived_wLength == REQUEST_LENGTH_CLEAR_FEATURE ))
{
/* CLEAR_FEATURE */
if (m_state == eCONFIGURED)
{
if (m_lastReceived_bmRequestType_recipient == REQUEST_TYPE_TRANSFER_RECIPIENT_ENDPOINT)
{
const uint8_t REQUESTED_FEATURE_SELECTOR = m_lastReceived_wValue;
const uint8_t REQUESTED_ENDPOINT_ADDRESS = m_lastReceived_wIndex;
if (REQUESTED_FEATURE_SELECTOR == FEATURE_SELECTOR_ENDPOINT_HALT)
{
if (REQUESTED_ENDPOINT_ADDRESS == ENDPOINT_1_IN_ADDRESS)
{
// Set the state.
m_endpoint1InState = eNORMAL;
// Clear the stall bit.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_TXCSR, USB_OTG_PERI_TXCSR_SENDSTALL, 1u);
// Reset the data toggle.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_TXCSR, USB_OTG_PERI_TXCSR_CLRDATATOG, 1);
#ifdef ENABLE_ADDITIONAL_LOGGING
LOG_printf(&usbOtgLog, "Handled CLEAR_FEATURE (FeatureSelector=HALT, Endpoint=\"EP1 (IN)\").");
#endif
}
else if (REQUESTED_ENDPOINT_ADDRESS == ENDPOINT_1_OUT_ADDRESS)
{
// Set the state.
m_endpoint1OutState = eNORMAL;
// Clear the stall bit.
CSL_FINS(USB0_REGS_PTR->RXCSR.PERI_RXCSR, USB_OTG_PERI_RXCSR_SENDSTALL, 1u);
// Reset the data toggle.
CSL_FINS(USB0_REGS_PTR->RXCSR.PERI_RXCSR, USB_OTG_PERI_RXCSR_CLRDATATOG, 1);
#ifdef ENABLE_ADDITIONAL_LOGGING
LOG_printf(&usbOtgLog, "Handled CLEAR_FEATURE (FeatureSelector=HALT, Endpoint=\"EP1 (OUT)\").");
#endif
}
// else do nothing
}
// else do nothing
}
// else do nothing
}
// else do nothing
}
// else do nothing
}
// else do nothing
}
void UsbPeripheralCommsDriver::serviceEndpoint0InterruptExtractSetupData()
{
m_lastReceived_bmRequestType = *(((uint8_t *)m_endpoint0DataBuffer) + 0); // bmRequestType
m_lastReceived_bRequest = *(((uint8_t *)m_endpoint0DataBuffer) + 1); // bRequest
m_lastReceived_wValue = *(((uint16_t *)m_endpoint0DataBuffer) + 1); // wValue
m_lastReceived_wIndex = *(((uint16_t *)m_endpoint0DataBuffer) + 2); // wIndex
m_lastReceived_wLength = *(((uint16_t *)m_endpoint0DataBuffer) + 3); // wLength
// Further extract data from the bmRequestType field.
m_lastReceived_bmRequestType_transferDirection
= (m_lastReceived_bmRequestType & REQUEST_TYPE_TRANSFER_DIRECTION_MASK) >> REQUEST_TYPE_TRANSFER_DIRECTION_SHIFT;
m_lastReceived_bmRequestType_type
= (m_lastReceived_bmRequestType & REQUEST_TYPE_TRANSFER_TYPE_MASK) >> REQUEST_TYPE_TRANSFER_TYPE_SHIFT;
m_lastReceived_bmRequestType_recipient
= (m_lastReceived_bmRequestType & REQUEST_TYPE_TRANSFER_RECIPIENT_MASK) >> REQUEST_TYPE_TRANSFER_RECIPIENT_SHIFT;
}
void UsbPeripheralCommsDriver::readEndpoint0FIFO(int uNumBytes, uint8_t* pData)
{
int i;
if (uNumBytes)
{
// Unload <NumBytes> from the selected FIFO
for(i=0; i<uNumBytes; i++)
{
pData[i] = *((unsigned char*)(&USB0_REGS_PTR->FIFO0));
}
}
}
void UsbPeripheralCommsDriver::writeEndpoint0FIFO(int uNumBytes, const uint8_t* pData)
{
int i;
if (uNumBytes>0)
{
for(i=0; i<uNumBytes; i++)
{
*((unsigned char*)(&USB0_REGS_PTR->FIFO0)) = pData[i];
}
}
}
void UsbPeripheralCommsDriver::serviceEndpoint1InInterrupt()
{
USB0_REGS_PTR->INDEX = 1u;
const bool UNDERRUN = CSL_FEXT(USB0_REGS_PTR->TXCSR.PERI_TXCSR, USB_OTG_PERI_TXCSR_UNDERRUN);
if (UNDERRUN)
{
// Clear the underrun bit.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_TXCSR, USB_OTG_PERI_TXCSR_UNDERRUN, 0u);
#ifdef ENABLE_ADDITIONAL_LOGGING
LOG_printf(&usbOtgLog, "%s: EP1 (IN) was underrun (i.e. data not written fast enough).",
FIRMWARE_ERROR_USB_CONTROLLER_UNDERRUN);
#else
LOG_printf(&usbOtgLog, "%s", DSP::FIRMWARE_ERROR_USB_CONTROLLER_UNDERRUN);
#endif
}
// else do nothing.
if (m_endpoint1InState == eHALTED)
{
// Stall the pipe.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_TXCSR, USB_OTG_PERI_TXCSR_SENDSTALL, 1u);
}
else
{
const bool TRANSFER_STALLED = CSL_FEXT(USB0_REGS_PTR->TXCSR.PERI_TXCSR, USB_OTG_PERI_TXCSR_SENTSTALL);
if (TRANSFER_STALLED)
{
// Clear the stall bit when we are not halted.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_TXCSR, USB_OTG_PERI_TXCSR_SENDSTALL, 0u);
}
// else do nothing
const bool MAILBOX_PEND_RESULT = MBX_pend(m_transmitMailboxHandle,
m_endpoint1InDataBuffer,
0u);
if (MAILBOX_PEND_RESULT)
{
size_t messageSize = (m_endpoint1InDataBuffer[MAILBOX_MESSAGE_LENGTH_FIELD_LO_INDEX] ) +
(m_endpoint1InDataBuffer[MAILBOX_MESSAGE_LENGTH_FIELD_HI_INDEX] << BITS_IN_BYTE);
writeEndpoint1InFIFO(messageSize, m_endpoint1InDataBuffer);
#ifdef LOG_COMMUNICATION
LOG_printf(&usbOtgLog, "DSP-to-Core: %d-byte message.", messageSize);
#endif
}
else
{
// write nothing
}
}
// Set the TXPKTRDY bit.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_TXCSR, USB_OTG_PERI_TXCSR_TXPKTRDY, 1u);
}
void UsbPeripheralCommsDriver::writeEndpoint1InFIFO(int uNumBytes, const uint8_t* pData)
{
int i;
if (uNumBytes>0)
{
for(i=0; i<uNumBytes; i++)
{
*((unsigned char*)(&USB0_REGS_PTR->FIFO1)) = pData[i];
}
}
}
void UsbPeripheralCommsDriver::serviceEndpoint1OutInterrupt()
{
USB0_REGS_PTR->INDEX = 1u;
const bool OVERRUN = CSL_FEXT(USB0_REGS_PTR->RXCSR.PERI_RXCSR, USB_OTG_PERI_RXCSR_OVERRUN);
if (OVERRUN)
{
// Clear the overrun bit.
CSL_FINS(USB0_REGS_PTR->RXCSR.PERI_RXCSR, USB_OTG_PERI_RXCSR_OVERRUN, 0u);
#ifdef ENABLE_ADDITIONAL_LOGGING
LOG_printf(&usbOtgLog, "%s: EP1 (OUT) was overrun (i.e. data not read fast enough).",
FIRMWARE_ERROR_USB_CONTROLLER_OVERRUN);
#else
LOG_printf(&usbOtgLog, "%s", DSP::FIRMWARE_ERROR_USB_CONTROLLER_OVERRUN);
#endif
}
// else do nothing.
if (CSL_FEXT(USB0_REGS_PTR->RXCSR.PERI_RXCSR, USB_OTG_PERI_RXCSR_RXPKTRDY))
{
if (m_endpoint1OutState == eHALTED)
{
// Stall the pipe.
CSL_FINS(USB0_REGS_PTR->RXCSR.PERI_RXCSR, USB_OTG_PERI_RXCSR_SENDSTALL, 1u);
}
else
{
const bool TRANSFER_STALLED = CSL_FEXT(USB0_REGS_PTR->RXCSR.PERI_RXCSR, USB_OTG_PERI_RXCSR_SENTSTALL);
if (TRANSFER_STALLED)
{
// Clear the stall bit when we are not halted.
CSL_FINS(USB0_REGS_PTR->RXCSR.PERI_RXCSR, USB_OTG_PERI_RXCSR_SENDSTALL, 0u);
}
// else do nothing
const uint16_t BYTES_RECEIVED = USB0_REGS_PTR->COUNT.RXCOUNT;
if (BYTES_RECEIVED > 0)
{
readEndpoint1OutFIFO(BYTES_RECEIVED, m_endpoint1OutDataBuffer);
m_endpoint1OutDataBuffer[MAILBOX_MESSAGE_LENGTH_FIELD_LO_INDEX] = static_cast<uint8_t>(BYTES_RECEIVED & UINT16_MASK_BYTE_0);
m_endpoint1OutDataBuffer[MAILBOX_MESSAGE_LENGTH_FIELD_HI_INDEX] = static_cast<uint8_t>((BYTES_RECEIVED >> BITS_IN_BYTE) & UINT16_MASK_BYTE_0);
const bool MAILBOX_POST_RESULT = MBX_post(m_receiveMailboxHandle,
m_endpoint1OutDataBuffer,
0u);
if (!MAILBOX_POST_RESULT)
{
#ifdef ENABLE_ADDITIONAL_LOGGING
LOG_printf(&usbOtgLog, "%s: Failed to post data in receive mailbox. Data was lost.",
FIRMWARE_ERROR_USB_RECEIVE_MAILBOX_FULL);
#else
LOG_printf(&usbOtgLog, "%s", DSP::FIRMWARE_ERROR_USB_RECEIVE_MAILBOX_FULL);
#endif
}
// else do nothing.
}
// else do nothing.
/* END: Write data using writeEndpoint1InFIFO */
}
}
// else do nothing.
// Clear the RXPKTRDY bit.
CSL_FINS(USB0_REGS_PTR->RXCSR.PERI_RXCSR, USB_OTG_PERI_RXCSR_RXPKTRDY, 0u);
}
void UsbPeripheralCommsDriver::readEndpoint1OutFIFO(int uNumBytes, uint8_t* pData)
{
int i;
if (uNumBytes)
{
// Unload <NumBytes> from the selected FIFO
for(i=0; i<uNumBytes; i++)
{
pData[i] = *((unsigned char*)(&USB0_REGS_PTR->FIFO1));
}
}
}
void UsbPeripheralCommsDriver::initialiseHardware()
{
volatile uint16_t i;
volatile Uint32 value =0;
// **************************************************************************
// Configure DRVVBUS Pin to be used for USB; Muxed with GPIO4[15]=Bank4 GP15
// PINMUX9[4]=1 and PINMUX9[7]=0
// **************************************************************************
// MAKE SURE WRITE ACCESS KEY IS INITIALIZED PRIOR TO ACCESSING ANY OF THE
// SYS_CFG_REGS_PTR REGISTERS.
SYS_CFG_REGS_PTR->KICK0R = 0x83e70b13; // Write Access Key 0
SYS_CFG_REGS_PTR->KICK1R = 0x95A4F1E0; // Write Access Key 1
// Reset the USB controller:
USB0_REGS_PTR->CTRLR |= CSL_USB_OTG_CTRLR_RESET_MASK;
//Wait until controller is finished with Reset. When done, it will clear the RESET bit field.
while ((USB0_REGS_PTR->CTRLR & CSL_USB_OTG_CTRLR_RESET_MASK) == 1)
;
// RESET: Hold PHY in Reset
SYS_CFG_REGS_PTR->CFGCHIP2 |= CSL_SYSCFG_CFGCHIP2_RESET_MASK; // Hold PHY in Reset
for (i=0; i < 50; i++)
; // Drive Reset for a few clock cycles
// RESET: Release PHY from Reset
SYS_CFG_REGS_PTR->CFGCHIP2 &= ~CSL_SYSCFG_CFGCHIP2_RESET_MASK; // Release PHY from Reset
// Configure PHY with the Desired Operation
// OTGMODE 0x02 => Override PHY Values act as device
SYS_CFG_REGS_PTR->CFGCHIP2 &= ~CSL_SYSCFG_CFGCHIP2_USB0OTGMODE_MASK; // 00= > Do Not Override PHY Values
SYS_CFG_REGS_PTR->CFGCHIP2 |= (CSL_SYSCFG_CFGCHIP2_USB0OTGMODE_USB_DEVICE <<
CSL_SYSCFG_CFGCHIP2_USB0OTGMODE_SHIFT); // 02= > Override PHY Values act as device
// PHYPWDN
SYS_CFG_REGS_PTR->CFGCHIP2 &= ~CSL_SYSCFG_CFGCHIP2_USB0PHYPWDN_MASK; // 1/0 = > PowerdDown/ NormalOperation
// OTGPWRDN
SYS_CFG_REGS_PTR->CFGCHIP2 &= ~CSL_SYSCFG_CFGCHIP2_USB0OTGPWRDN_MASK; // 1/0 = > PowerDown/ NormalOperation
// DATAPOL
SYS_CFG_REGS_PTR->CFGCHIP2 |= CSL_SYSCFG_CFGCHIP2_USB0DATPOL_MASK; // 1/0 = > Normal/ Reversed
// SESNDEN
SYS_CFG_REGS_PTR->CFGCHIP2 |= CSL_SYSCFG_CFGCHIP2_USB0SESNDEN_MASK; // 1/0 = > NormalOperation/ SessionEnd
// VBDTCTEN
SYS_CFG_REGS_PTR->CFGCHIP2 |= CSL_SYSCFG_CFGCHIP2_USB0VBDTCTEN_MASK; // 1/0 = > VBUS Comparator Enable/ Disable * Configure PHY PLL use and Select Source */
// REF_FREQ[3:0]
SYS_CFG_REGS_PTR->CFGCHIP2 |= 0x2u; // 0x2 = > 24MHz Input Source
// USB2PHYCLKMUX: Select Internal Source
SYS_CFG_REGS_PTR->CFGCHIP2 |= CSL_SYSCFG_CFGCHIP2_USB0PHYCLKMUX_MASK; // 1/0 = > Internal/External(Pin)
//SYS_CFG_REGS_PTR->CFGCHIP2 &= ~CSL_SYSCFG_CFGCHIP2_USB0PHYCLKMUX_MASK; // 1/0 = > Internal/External(Pin)
// PHY_PLLON: On Simulation PHY PLL is OFF (also necessary as USB1 uses this clock even when USB0 is suspended)
SYS_CFG_REGS_PTR->CFGCHIP2 |= CSL_SYSCFG_CFGCHIP2_USB0PHY_PLLON_MASK; // 1/0 = > On/ Off
/* Wait Until PHY Clock is Good */
while ((SYS_CFG_REGS_PTR->CFGCHIP2 & CSL_SYSCFG_CFGCHIP2_USB0PHYCLKGD_MASK) == 0)
; // Wait Until PHY Clock is Good.
}
void UsbPeripheralCommsDriver::initialiseUSB()
{
// Enable high-speed
CSL_FINS(USB0_REGS_PTR->POWER, USB_OTG_POWER_HSEN, 1u);
// Enable Interrupts
// Enable interrupts in OTG block
USB0_REGS_PTR->CTRLR &= ~CSL_USB_OTG_CTRLR_UINT_MASK; // Enable PDR2.0 Interrupt
// Enable All Core Tx Endpoints Interrupts + EP0 Tx/Rx interrupt
USB0_REGS_PTR->INTRTXE = (
CSL_USB_OTG_INTRTXE_EP4TX_MASK
| CSL_USB_OTG_INTRTXE_EP3TX_MASK
| CSL_USB_OTG_INTRTXE_EP2TX_MASK
| CSL_USB_OTG_INTRTXE_EP1TX_MASK
| CSL_USB_OTG_INTRTXE_EP0_MASK);
// Enable All Core Rx Endpoints Interrupts
USB0_REGS_PTR->INTRRXE = (
CSL_USB_OTG_INTRRXE_EP4RX_MASK
| CSL_USB_OTG_INTRRXE_EP3RX_MASK
| CSL_USB_OTG_INTRRXE_EP2RX_MASK
| CSL_USB_OTG_INTRRXE_EP1RX_MASK);
// Enable all interrupts in OTG block
USB0_REGS_PTR->INTMSKSETR = 0x01F71E1F;
// Enable all USB interrupts in MUSBMHDRC
USB0_REGS_PTR->INTRUSBE = 0xF7;
// Enable SUSPENDM so that suspend can be seen UTMI signal
CSL_FINS(USB0_REGS_PTR->POWER, USB_OTG_POWER_ENSUSPM, 1);
// Clear all pending interrupts
USB0_REGS_PTR->INTCLRR = USB0_REGS_PTR->INTSRCR;
// NOTE: Acknowledge completion of any previous interrupts. We do not
// need to do this after a cold start but in all other cases the
// firmware will not work without this acknowledgment.
USB0_REGS_PTR->EOIR = 0u;
// Set softconn bit
CSL_FINS(USB0_REGS_PTR->POWER, USB_OTG_POWER_SOFTCONN, 1);
while ((USB0_REGS_PTR->DEVCTL & CSL_USB_OTG_DEVCTL_SESSION_MASK) == 0)
; //Stay here until controller goes in Session.
}
void UsbPeripheralCommsDriver::initialiseEndpoints()
{
uint16_t endpoint1InMaxPacketSize = 0;
uint16_t endpoint1OutMaxPacketSize = 0;
if (m_operatingSpeed == eFULL_SPEED)
{
endpoint1InMaxPacketSize = ENDPOINT_1_IN_MAX_PACKET_SIZE_FULL_SPEED;
endpoint1OutMaxPacketSize = ENDPOINT_1_OUT_MAX_PACKET_SIZE_FULL_SPEED;
if (m_messageSplitterPtr!=0)
{
m_messageSplitterPtr->setMaxMessageLength(ENDPOINT_1_OUT_MAX_PACKET_SIZE_FULL_SPEED);
}
//else do nothing
if (m_faultReporterPtr!=0)
{
m_faultReporterPtr->faultChanged(FaultTypes::eLOW_USB_SPEED, true);
}
//else do nothing
}
else // (m_operatingSpeed == eHIGH_SPEED)
{
endpoint1InMaxPacketSize = ENDPOINT_1_IN_MAX_PACKET_SIZE_HIGH_SPEED;
endpoint1OutMaxPacketSize = ENDPOINT_1_OUT_MAX_PACKET_SIZE_HIGH_SPEED;
if (m_messageSplitterPtr!=0)
{
m_messageSplitterPtr->setMaxMessageLength(ENDPOINT_1_OUT_MAX_PACKET_SIZE_HIGH_SPEED);
}
//else do nothing
if (m_faultReporterPtr!=0)
{
m_faultReporterPtr->faultChanged(FaultTypes::eLOW_USB_SPEED, false);
}
//else do nothing
}
// Select the right endpoint.
USB0_REGS_PTR->INDEX = 1u;
// Set the FIFO for this endpoint.
USB0_REGS_PTR->TXFIFOSZ = ENDPOINT_1_IN_FIFO_SIZE_FOR_REGISTER;
USB0_REGS_PTR->TXFIFOADDR = ENDPOINT_1_IN_FIFO_OFFSET_FOR_ADDRESS;
USB0_REGS_PTR->RXFIFOSZ = ENDPOINT_1_OUT_FIFO_SIZE_FOR_REGISTER;
USB0_REGS_PTR->RXFIFOADDR = ENDPOINT_1_OUT_FIFO_OFFSET_FOR_ADDRESS;
// Set maximum packet size.
USB0_REGS_PTR->TXMAXP = endpoint1InMaxPacketSize;
USB0_REGS_PTR->RXMAXP = endpoint1OutMaxPacketSize;
// Clear to enable interrupt (or bulk) mode.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_TXCSR, USB_OTG_PERI_TXCSR_ISO, 0);
CSL_FINS(USB0_REGS_PTR->RXCSR.PERI_RXCSR, USB_OTG_PERI_RXCSR_ISO, 0);
// Enable Rx/Tx mode (should only be necessary when sharing FIFOs).
// CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_TXCSR, USB_OTG_PERI_TXCSR_MODE, 1);
// Disable DMA.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_TXCSR, USB_OTG_PERI_TXCSR_DMAEN, 0);
CSL_FINS(USB0_REGS_PTR->RXCSR.PERI_RXCSR, USB_OTG_PERI_RXCSR_DMAEN, 0);
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_TXCSR, USB_OTG_PERI_TXCSR_DMAMODE, 0);
CSL_FINS(USB0_REGS_PTR->RXCSR.PERI_RXCSR, USB_OTG_PERI_RXCSR_DMAMODE, 0);
// Normal data toggle operation.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_TXCSR, USB_OTG_PERI_TXCSR_FRCDATATOG, 0);
// Reset data toggle.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_TXCSR, USB_OTG_PERI_TXCSR_CLRDATATOG, 1);
CSL_FINS(USB0_REGS_PTR->RXCSR.PERI_RXCSR, USB_OTG_PERI_RXCSR_CLRDATATOG, 1);
// Disable NYET handshakes (see sprofm9h.pdf, Sect. 2.7.1.3).
CSL_FINS(USB0_REGS_PTR->RXCSR.PERI_RXCSR, USB_OTG_PERI_RXCSR_DISNYET, 0);
// Check if there is data in the buffer.
if (CSL_FEXT(USB0_REGS_PTR->TXCSR.PERI_TXCSR, USB_OTG_PERI_TXCSR_FIFONOTEMPTY))
{
// Flush the FIFO (only once as we are not using double buffering).
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_TXCSR, USB_OTG_PERI_TXCSR_FLUSHFIFO, 1);
// The user guide hints at setting this bit but the forum subtly suggests not to.
// CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_TXCSR, USB_OTG_PERI_TXCSR_FIFONOTEMPTY, 0);
}
// else do nothing
// Check if there is data in the buffer.
if (CSL_FEXT(USB0_REGS_PTR->RXCSR.PERI_RXCSR, USB_OTG_PERI_RXCSR_RXPKTRDY))
{
// Flush the FIFO (only once as we are not using double buffering).
CSL_FINS(USB0_REGS_PTR->RXCSR.PERI_RXCSR, USB_OTG_PERI_RXCSR_FLUSHFIFO, 1);
// The user guide hints at setting this bit but the forum subtly suggests not to.
// CSL_FINS(USB0_REGS_PTR->RXCSR.PERI_TXCSR, USB_OTG_PERI_RXCSR_FIFONOTEMPTY, 0);
// Clear the bit to enable interrupts.
CSL_FINS(USB0_REGS_PTR->RXCSR.PERI_RXCSR, USB_OTG_PERI_RXCSR_RXPKTRDY, 0);
}
// else do nothing
// Send empty data to prevent underrun and to enable interrupt.
CSL_FINS(USB0_REGS_PTR->TXCSR.PERI_TXCSR, USB_OTG_PERI_TXCSR_TXPKTRDY, 1);
}
void UsbPeripheralCommsDriver::performInitialization(void)
{
}
void UsbPeripheralCommsDriver::performTask(void)
{
/**
* NOTE: This method is called by the kernel thread. We must retrieve
* the messages intercepted by the interrupt service routine in a thread-safe manner.
*/
// Clear receive Mailbox (clear the mailbox at most once to prevent livelock).
for (uint32_t index = 0; index < RECEIVE_MAILBOX_NUM_BUFFERS; ++index)
{
const bool MAILBOX_PEND_RESULT = MBX_pend(m_receiveMailboxHandle,
m_receiveMailboxDataBuffer,
0u);
if (MAILBOX_PEND_RESULT)
{
size_t messageSize = (m_receiveMailboxDataBuffer[MAILBOX_MESSAGE_LENGTH_FIELD_LO_INDEX] ) +
(m_receiveMailboxDataBuffer[MAILBOX_MESSAGE_LENGTH_FIELD_HI_INDEX] << BITS_IN_BYTE);
#ifdef LOG_COMMUNICATION
LOG_printf(&usbOtgLog, "Core-to-DSP: %d-byte message.", messageSize);
#endif
if (m_receiverPtr != 0)
{
FastDataPacket data(m_receiveMailboxDataBuffer, messageSize);
m_receiverPtr->receive(data);
}
// else do nothing
}
else
{
// There is no more post.
break;
}
}
}
void UsbPeripheralCommsDriver::performTaskWhichCanSuspend()
{
// I am called from a TSK context every so often.
if (m_resetSignallingPresent)
{
if (!CSL_FEXT(USB0_REGS_PTR->POWER, USB_OTG_POWER_RESET))
{
LOG_printf(&usbOtgLog, "End of RESET signalling detected.");
serviceResetInterrupt();
m_resetSignallingPresent = false;
}
else
{
LOG_printf(&usbOtgLog, "Still observing RESET signalling.");
}
// else do nothing.
}
// else do nothing.
//This method should be called from the active object that inherits from this class
// Clear receive Mailbox (clear the mailbox at most once to prevent livelock).
const bool MAILBOX_PEND_RESULT = MBX_pend(m_receiveMailboxHandle,
m_receiveMailboxDataBuffer,
RECEIVE_SLEEP_PERIOD_MS);
if (MAILBOX_PEND_RESULT)
{
size_t messageSize = (m_receiveMailboxDataBuffer[MAILBOX_MESSAGE_LENGTH_FIELD_LO_INDEX] ) +
(m_receiveMailboxDataBuffer[MAILBOX_MESSAGE_LENGTH_FIELD_HI_INDEX] << BITS_IN_BYTE);
#ifdef LOG_COMMUNICATION
LOG_printf(&usbOtgLog, "Core-to-DSP: %d-byte message.", messageSize);
#endif
if (m_receiverPtr != 0)
{
FastDataPacket data(m_receiveMailboxDataBuffer, messageSize);
m_receiverPtr->receive(data);
}
// else do nothing
}
//else do nothing
}
bool UsbPeripheralCommsDriver::isConnected() const
{
/**
* NOTE: This is a little bit iffy as the interrupt service routine may
* preempt this kernel thread method. However, we do not want to enforce exclusive
* access to m_state because this would mean blocking the interrupt
* service routine!
*/
return (m_state == eCONFIGURED);
}
bool UsbPeripheralCommsDriver::isFull() const
{
return false;
}
bool UsbPeripheralCommsDriver::isEmpty() const
{
Logger::logSoftwareException(
MODULE_NAME,
"isEmpty",
"Invalid use");
return false;
}
bool UsbPeripheralCommsDriver::send(const FastDataPacket& messageData)
{
/**
* NOTE: This method is called by the kernel thread. We must pass
* the message to the interrupt service routine in a safe manner.
*/
bool result = false;
uint16_t endpoint1InMaxPacketSize = 0;
if (m_operatingSpeed == eFULL_SPEED)
{
endpoint1InMaxPacketSize = ENDPOINT_1_IN_MAX_PACKET_SIZE_FULL_SPEED;
}
else // (m_operatingSpeed == eHIGH_SPEED)
{
endpoint1InMaxPacketSize = ENDPOINT_1_IN_MAX_PACKET_SIZE_HIGH_SPEED;
}
/**
* NOTE: Any method to broadcast messages larger than endpoint1InMaxPacketSize through
* sending multiple messages should be implemented here.
* Note that when doing this the mailbox size should probably be increased to at least 1024 x 64.
*/
const size_t MESSAGE_LENGTH = messageData.length();
if (MESSAGE_LENGTH <= endpoint1InMaxPacketSize)
{
const uint16_t BYTES_TO_SEND = static_cast<uint16_t>(MESSAGE_LENGTH);
Lock lock(m_transmitMutex);
m_transmitMailboxDataBuffer[MAILBOX_MESSAGE_LENGTH_FIELD_LO_INDEX] = static_cast<uint8_t>((BYTES_TO_SEND ) & UINT16_MASK_BYTE_0);
m_transmitMailboxDataBuffer[MAILBOX_MESSAGE_LENGTH_FIELD_HI_INDEX] = static_cast<uint8_t>((BYTES_TO_SEND >> BITS_IN_BYTE) & UINT16_MASK_BYTE_0);
const unsigned char* restrict messageDataDataPtr = messageData.data();
unsigned char* restrict transmitMailboxDataBufferPtr = m_transmitMailboxDataBuffer;
for (size_t index = 0; index < MESSAGE_LENGTH; ++index)
{
*transmitMailboxDataBufferPtr++ = *messageDataDataPtr++;
}
const bool MAILBOX_POST_RESULT = MBX_post(m_transmitMailboxHandle,
m_transmitMailboxDataBuffer,
SYS_FOREVER);
if (!MAILBOX_POST_RESULT)
{
LOG_printf(&usbOtgLog, "%s", FIRMWARE_ERROR_USB_TRANSMIT_MAILBOX_FULL);
}
//else do nothing
result = MAILBOX_POST_RESULT;
}
else
{
result = false;
#if !defined (LOGGER_DISABLED)
Logger::logSoftwareException(
MODULE_NAME,
"transmitMessage",
"Sending messages larger than wMaxPacketSize (EP1 IN) for the current operating speed, is currently not supported.");
#endif
}
return result;
}
bool UsbPeripheralCommsDriver::send(std::auto_ptr<FastDataPacket> messageData)
{
Logger::logSoftwareException(
MODULE_NAME,
"send(auto_ptr)",
"Invalid use");
return false;
}
bool UsbPeripheralCommsDriver::sendASAP(const FastDataPacket& messageData)
{
return send(messageData);
}
bool UsbPeripheralCommsDriver::sendASAP(std::auto_ptr<FastDataPacket> messageData)
{
Logger::logSoftwareException(
MODULE_NAME,
"sendasap(auto_ptr)",
"Invalid use");
return false;
}
void UsbPeripheralCommsDriver::performShutdown(void)
{
}
void UsbPeripheralCommsDriver::extractOperatingSpeed()
{
if (CSL_FEXT(USB0_REGS_PTR->POWER, USB_OTG_POWER_HSMODE))
{
m_operatingSpeed = eHIGH_SPEED;
}
else
{
m_operatingSpeed = eFULL_SPEED;
}
}
}
extern "C" void serviceUsb0Interrupt(void)
{
LOG_printf(&trace, "BEG ISR USB0");
usb0InterruptCounter++;
Model::UsbPeripheralCommsDriver* activeUsbPeripheralCommsDriverPtr =
Model::Model::getUsbPeripheralCommsDriverInstancePtr();
if (activeUsbPeripheralCommsDriverPtr!=0)
{
activeUsbPeripheralCommsDriverPtr->serviceInterrupt();
}
//else do nothing
LOG_printf(&trace, "END ISR USB0");
}