blob: 4fcf60e4230def1cb0f360261b9653a1cdcc0236 [file] [log] [blame] [edit]
/*
* CDCCommandStream.cpp
*
* Created on: 17 Jan 2020
* Author: andrewcapon
*/
#include "32blit.h"
#include "usbd_cdc_if.h"
#include "usbh_cdc.h"
extern USBD_HandleTypeDef hUsbDeviceHS;
extern USBH_HandleTypeDef hUsbHostHS;
#include "CDCCommandStream.h"
void CDCCommandStream::Init(void)
{
m_state = stDetect;
m_uHeaderScanPos = 0;
m_bNeedsUSBResume = false;
}
void CDCCommandStream::AddCommandHandler(CDCCommandHandler::CDCFourCC uCommand, CDCCommandHandler *pCommandHandler)
{
m_commandHandlers[uCommand] = pCommandHandler;
}
void CDCCommandStream::LogTimeTaken(CDCCommandHandler::StreamResult result, uint32_t uBytesHandled)
{
// can be used to log time taken
}
void CDCCommandStream::Stream(void)
{
static uint32_t uLastResumeTime = HAL_GetTick();
if(m_bNeedsUSBResume) // FIFO Full, so empty and resume USB
{
while(CDCFifoElement *pElement = GetFifoReadElement())
{
if(pElement->m_uLen)
Stream(pElement->m_data, pElement->m_uLen);
ReleaseFifoReadElement();
}
m_bNeedsUSBResume = false;
if(USB_GetMode(USB_OTG_HS))
{
USBH_CDC_Receive(&hUsbHostHS, GetFifoWriteBuffer(), 64);
}
else
{
USBD_CDC_SetRxBuffer(&hUsbDeviceHS, GetFifoWriteBuffer());
USBD_CDC_ReceivePacket(&hUsbDeviceHS);
}
uLastResumeTime = HAL_GetTick();
}
else
{
if(HAL_GetTick() > uLastResumeTime + 10) // USB Stalled, empty FIFO
{
// empty fifo
while(CDCFifoElement *pElement = GetFifoReadElement())
{
if(pElement->m_uLen)
Stream(pElement->m_data, pElement->m_uLen);
ReleaseFifoReadElement();
}
}
}
}
uint8_t CDCCommandStream::Stream(uint8_t *data, uint32_t len)
{
blit_disable_ADC();
uint8_t *pScanPos = data;
while(pScanPos < (data+len))
{
if(m_state == stDetect)
{
bool bHeaderFound = false;
while ((!bHeaderFound) && (pScanPos < (data+len)))
{
if(*pScanPos == m_header[m_uHeaderScanPos])
{
if(m_uHeaderScanPos == 3)
{
// header found
bHeaderFound = true;
m_uHeaderScanPos = 0;
m_uCommandScanPos = 0;
}
else
m_uHeaderScanPos++;
}
else
{
m_uHeaderScanPos = 0;
m_uCommandScanPos = 0;
}
pScanPos++;
}
if(bHeaderFound)
{
// next command byte could be in next call
while((m_state == stDetect) && (m_uCommandScanPos < 4))
{
uint8_t *pCommandByte = (uint8_t *)(&uCommand)+m_uCommandScanPos;
if(pScanPos >= (data + len)) // rest in next packet
m_state = stDetectCommandWord;
else
{
*pCommandByte = *pScanPos++;
if(m_uCommandScanPos == 3)
{
m_state = stDispatch;
}
m_uCommandScanPos++;
}
}
}
}
else
{
if(m_state == stDetectCommandWord)
{
while(m_uCommandScanPos < 4)
{
uint8_t *pCommandByte = (uint8_t *)(&uCommand)+m_uCommandScanPos;
*pCommandByte = *pScanPos++;
m_uCommandScanPos++;
}
m_state = stDispatch;
}
}
if(m_state == stDispatch)
{
m_uRetryCount = 0;
m_uDispatchTime = HAL_GetTick();
m_pCurrentCommandHandler = m_commandHandlers[uCommand];
if(m_pCurrentCommandHandler)
{
if(m_pCurrentCommandHandler->StreamInit(uCommand)) {
m_state = stProcessing;
m_dataStream.Clear(); // make sure there isn't any leftover data in the buffer
} else
{
m_state = stDetect;
LogTimeTaken(CDCCommandHandler::srFinish, 0);
}
}
else
m_state = stDetect; // No handler go back to detect state
}
if(m_state == stProcessing)
{
auto added_len = len - (pScanPos - data);
m_dataStream.AddData(pScanPos, added_len);
CDCCommandHandler::StreamResult result = m_pCurrentCommandHandler->StreamData(m_dataStream);
switch(result)
{
case CDCCommandHandler::srError:
case CDCCommandHandler::srFinish:
{
// handler has finished or failed go back to detect state
m_state = stDetect;
LogTimeTaken(result, m_pCurrentCommandHandler->GetBytesHandled());
blit_enable_ADC();
}
break;
case CDCCommandHandler::srNeedData:
m_uRetryCount++;
if(m_uRetryCount > 1)
{
m_state = stDetect;
LogTimeTaken(result, m_pCurrentCommandHandler->GetBytesHandled());
blit_enable_ADC();
}
return false;
case CDCCommandHandler::srContinue:
break;
}
// handled all data or need more
if(m_dataStream.GetStreamLength() == 0 || m_state != stDetect)
break;
else // some data left, advance scan pos and try again
pScanPos += added_len - std::min(added_len, m_dataStream.GetStreamLength());
}
}
return m_state != stDetect;
}
uint32_t CDCCommandStream::GetTimeTaken(void)
{
return HAL_GetTick() - m_uDispatchTime;
}
uint8_t *CDCCommandStream::GetFifoWriteBuffer(void)
{
uint8_t *pData = NULL;
if(m_uFifoUsedCount < CDC_FIFO_BUFFERS - 1)
{
pData = m_fifoElements[m_uFifoWritePos].m_data;
}
else
m_bNeedsUSBResume = true;
return pData;
}
void CDCCommandStream::ReleaseFifoWriteBuffer(uint8_t uLen)
{
m_fifoElements[m_uFifoWritePos].m_uLen = uLen;
m_uFifoWritePos++;
if(m_uFifoWritePos == CDC_FIFO_BUFFERS)
m_uFifoWritePos = 0;
m_uFifoUsedCount++;
}
CDCFifoElement *CDCCommandStream::GetFifoReadElement(void)
{
CDCFifoElement *pElement = NULL;
if(m_uFifoUsedCount)
pElement = &m_fifoElements[m_uFifoReadPos];
return pElement;
}
void CDCCommandStream::ReleaseFifoReadElement(void)
{
m_uFifoReadPos++;
if(m_uFifoReadPos == CDC_FIFO_BUFFERS)
m_uFifoReadPos = 0;
m_uFifoUsedCount--;
}
// usb host glue
extern CDCCommandStream g_commandStream;
void USBH_CDC_ReceiveCallback(USBH_HandleTypeDef *phost)
{
g_commandStream.ReleaseFifoWriteBuffer(USBH_CDC_GetLastReceivedDataSize(phost));
// If a new writebuffer is available, set RxBuffer and requext next USB packet
if(uint8_t *pBuffer = g_commandStream.GetFifoWriteBuffer())
USBH_CDC_Receive(phost, pBuffer, 64);
}
extern "C" void usb_host_ready(USBH_HandleTypeDef *phost) {
if(uint8_t *pBuffer = g_commandStream.GetFifoWriteBuffer())
USBH_CDC_Receive(phost, pBuffer, 64);
}