You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
usb-firewall-fork/Upstream/Src/downstream_spi.c

430 lines
11 KiB

/*
* downstream_spi.c
*
* Created on: 21/06/2015
* Author: Robert Fisk
*/
#include <downstream_spi.h>
#include "downstream_interface_def.h"
#include "stm32f4xx_hal.h"
#include "usbd_def.h"
#include "board_config.h"
SPI_HandleTypeDef hspi1;
DownstreamPacketTypeDef DownstreamPacket0;
DownstreamPacketTypeDef DownstreamPacket1;
DownstreamPacketTypeDef* CurrentWorkingPacket;
DownstreamPacketTypeDef* NextTxPacket; //Indicates we have a pending TX packet
InterfaceStateTypeDef DownstreamInterfaceState;
FreePacketCallbackTypeDef PendingFreePacketCallback; //Indicates someone is waiting for a packet buffer to become available
SpiPacketReceivedCallbackTypeDef ReceivePacketCallback; //Indicates someone is waiting for a received packet
uint8_t SentCommandClass;
uint8_t SentCommand;
static void SPI1_Init(void);
static HAL_StatusTypeDef Downstream_CheckBeginPacketReception(void);
static void Downstream_BeginPacketReception(DownstreamPacketTypeDef* freePacket);
void Downstream_InitInterface(void)
{
DownstreamInterfaceState = INTERFACE_STATE_RESET;
SPI1_Init();
DownstreamPacket0.Busy = NOT_BUSY;
DownstreamPacket1.Busy = NOT_BUSY;
NextTxPacket = NULL;
PendingFreePacketCallback = NULL;
ReceivePacketCallback = NULL;
//Todo: check connection to downstream, await client USB insertion
while (!DOWNSTREAM_TX_OK_ACTIVE);
DownstreamInterfaceState = INTERFACE_STATE_IDLE;
}
void SPI1_Init(void)
{
hspi1.Instance = SPI1;
hspi1.State = HAL_SPI_STATE_RESET;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32; //42MHz APB2 / 32 = 1.3Mbaud
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLED;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_ENABLED;
hspi1.Init.CRCPolynomial = SPI_CRC_DEFAULTPOLYNOMIAL;
HAL_SPI_Init(&hspi1);
}
//Used by USB interface classes, and by our internal RX code.
HAL_StatusTypeDef Downstream_GetFreePacket(FreePacketCallbackTypeDef callback)
{
//Sanity checks
if ((DownstreamInterfaceState < INTERFACE_STATE_IDLE) ||
(DownstreamInterfaceState > INTERFACE_STATE_RX_PACKET))
{
SPI_INTERFACE_FREAKOUT_HAL_ERROR;
}
//Do we already have a queued callback?
if (PendingFreePacketCallback != NULL)
{
SPI_INTERFACE_FREAKOUT_HAL_ERROR;
}
//Check if there is a free buffer now
if (DownstreamPacket0.Busy == NOT_BUSY)
{
DownstreamPacket0.Busy = BUSY;
callback(&DownstreamPacket0);
return HAL_OK;
}
if (DownstreamPacket1.Busy == NOT_BUSY)
{
DownstreamPacket1.Busy = BUSY;
callback(&DownstreamPacket1);
return HAL_OK;
}
//Otherwise save requested address for when a buffer becomes free in the future
PendingFreePacketCallback = callback;
return HAL_OK;
}
DownstreamPacketTypeDef* Downstream_GetFreePacketImmediately(void)
{
//Sanity checks
if ((DownstreamInterfaceState < INTERFACE_STATE_IDLE) ||
(DownstreamInterfaceState > INTERFACE_STATE_RX_PACKET))
{
SPI_INTERFACE_FREAKOUT_HAL_ERROR;
}
//We are expecting a free buffer now
if (DownstreamPacket0.Busy == NOT_BUSY)
{
DownstreamPacket0.Busy = BUSY;
return &DownstreamPacket0;
}
if (DownstreamPacket1.Busy == NOT_BUSY)
{
DownstreamPacket1.Busy = BUSY;
return &DownstreamPacket1;
}
//Should not happen:
SPI_INTERFACE_FREAKOUT_HAL_ERROR;
}
//Used by USB interface classes, and by our internal RX code.
void Downstream_ReleasePacket(DownstreamPacketTypeDef* packetToRelease)
{
if (PendingFreePacketCallback != NULL)
{
PendingFreePacketCallback(packetToRelease);
PendingFreePacketCallback = NULL;
}
else
{
packetToRelease->Busy = NOT_BUSY;
}
}
//Used by USB interface classes only.
//OK to call when still transmitting another packet.
//Not OK to call when receiving or waiting for downstream reply.
HAL_StatusTypeDef Downstream_SendPacket(DownstreamPacketTypeDef* packetToWrite)
{
//Sanity checks
if ((packetToWrite != &DownstreamPacket0) &&
(packetToWrite != &DownstreamPacket1))
{
SPI_INTERFACE_FREAKOUT_HAL_ERROR;
}
if ((packetToWrite->Busy != BUSY) ||
(packetToWrite->Length < DOWNSTREAM_PACKET_LEN_MIN) ||
(packetToWrite->Length > DOWNSTREAM_PACKET_LEN))
{
SPI_INTERFACE_FREAKOUT_HAL_ERROR;
}
//Cancel any outstanding receive request
ReceivePacketCallback = NULL;
switch (DownstreamInterfaceState)
{
case INTERFACE_STATE_TX_SIZE_WAIT:
case INTERFACE_STATE_TX_SIZE:
case INTERFACE_STATE_TX_PACKET_WAIT:
case INTERFACE_STATE_TX_PACKET:
if (NextTxPacket != NULL)
{
SPI_INTERFACE_FREAKOUT_HAL_ERROR;
}
NextTxPacket = packetToWrite;
break;
case INTERFACE_STATE_RX_SIZE_WAIT:
case INTERFACE_STATE_RX_SIZE:
case INTERFACE_STATE_RX_PACKET_WAIT:
case INTERFACE_STATE_RX_PACKET:
//We can't let the size/packet sequence get out of sync.
SPI_INTERFACE_FREAKOUT_HAL_ERROR;
case INTERFACE_STATE_IDLE:
DownstreamInterfaceState = INTERFACE_STATE_TX_SIZE_WAIT;
CurrentWorkingPacket = packetToWrite;
SentCommandClass = CurrentWorkingPacket->CommandClass;
SentCommand = CurrentWorkingPacket->Command;
if (DOWNSTREAM_TX_OK_ACTIVE)
{
Downstream_TxOkInterrupt(); //Manually trigger edge interrupt processing if the line was already asserted
}
break;
default:
SPI_INTERFACE_FREAKOUT_HAL_ERROR;
}
return HAL_OK;
}
//Called at the end of the SPI TX DMA transfer,
//at DMA2 interrupt priority. Assume *hspi points to our hspi1.
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
SPI1_NSS_DEASSERT;
if ((DownstreamInterfaceState != INTERFACE_STATE_TX_SIZE) &&
(DownstreamInterfaceState != INTERFACE_STATE_TX_PACKET))
{
SPI_INTERFACE_FREAKOUT_VOID;
}
if (DownstreamInterfaceState == INTERFACE_STATE_TX_SIZE)
{
DownstreamInterfaceState = INTERFACE_STATE_TX_PACKET_WAIT;
if (DOWNSTREAM_TX_OK_ACTIVE)
{
Downstream_TxOkInterrupt();
}
return;
}
if (DownstreamInterfaceState == INTERFACE_STATE_TX_PACKET)
{
if ((PendingFreePacketCallback != NULL) && (NextTxPacket == NULL))
{
SPI_INTERFACE_FREAKOUT_VOID;
}
Downstream_ReleasePacket(CurrentWorkingPacket);
if (NextTxPacket != NULL)
{
//NextTxPacket has already passed the checks in SendDownstreamPacket.
//So we just need to pass it to HAL_SPI_Transmit_DMA.
DownstreamInterfaceState = INTERFACE_STATE_TX_SIZE_WAIT;
CurrentWorkingPacket = NextTxPacket;
NextTxPacket = NULL;
if (DOWNSTREAM_TX_OK_ACTIVE)
{
Downstream_TxOkInterrupt();
}
return;
}
DownstreamInterfaceState = INTERFACE_STATE_IDLE;
if (ReceivePacketCallback != NULL)
{
Downstream_CheckBeginPacketReception();
}
}
}
//Used by USB interface classes.
//Ok to call when transmitting, receiving, or waiting for downstream.
HAL_StatusTypeDef Downstream_GetPacket(SpiPacketReceivedCallbackTypeDef callback)
{
if (ReceivePacketCallback != NULL)
{
SPI_INTERFACE_FREAKOUT_HAL_ERROR;
}
ReceivePacketCallback = callback;
return Downstream_CheckBeginPacketReception();
}
//Internal use only.
HAL_StatusTypeDef Downstream_CheckBeginPacketReception(void)
{
if ((DownstreamInterfaceState < INTERFACE_STATE_IDLE) ||
(DownstreamInterfaceState > INTERFACE_STATE_RX_PACKET))
{
SPI_INTERFACE_FREAKOUT_HAL_ERROR;
}
if (DownstreamInterfaceState == INTERFACE_STATE_IDLE)
{
DownstreamInterfaceState = INTERFACE_STATE_RX_SIZE_WAIT;
}
if (DownstreamInterfaceState == INTERFACE_STATE_RX_SIZE_WAIT)
{
if (DOWNSTREAM_TX_OK_ACTIVE)
{
//DownstreamTxOkInterrupt();
Downstream_GetFreePacket(Downstream_BeginPacketReception); //Take a shortcut here :)
}
}
return HAL_OK;
}
//This is called by EXTI3 falling edge interrupt,
//indicating that downstream is ready for next transaction.
void Downstream_TxOkInterrupt(void)
{
switch (DownstreamInterfaceState)
{
case INTERFACE_STATE_TX_SIZE_WAIT:
DownstreamInterfaceState = INTERFACE_STATE_TX_SIZE;
SPI1_NSS_ASSERT;
if (HAL_SPI_Transmit_DMA(&hspi1,
(uint8_t*)&CurrentWorkingPacket->Length,
2) != HAL_OK)
{
SPI_INTERFACE_FREAKOUT_VOID;
}
break;
case INTERFACE_STATE_TX_PACKET_WAIT:
DownstreamInterfaceState = INTERFACE_STATE_TX_PACKET;
SPI1_NSS_ASSERT;
if ((HAL_SPI_Transmit_DMA(&hspi1,
&CurrentWorkingPacket->CommandClass,
CurrentWorkingPacket->Length)) != HAL_OK)
{
SPI_INTERFACE_FREAKOUT_VOID;
}
break;
case INTERFACE_STATE_RX_SIZE_WAIT:
Downstream_GetFreePacket(Downstream_BeginPacketReception);
break;
case INTERFACE_STATE_RX_PACKET_WAIT:
DownstreamInterfaceState = INTERFACE_STATE_RX_PACKET;
SPI1_NSS_ASSERT;
if ((HAL_SPI_Receive_DMA(&hspi1,
&CurrentWorkingPacket->CommandClass,
(CurrentWorkingPacket->Length + 1))) != HAL_OK) //"When the CRC feature is enabled the pData Length must be Size + 1"
{
SPI_INTERFACE_FREAKOUT_VOID;
}
break;
default:
SPI_INTERFACE_FREAKOUT_VOID;
}
}
//Internal use only.
//Called when we want to receive downstream packet, and a packet buffer has become free.
void Downstream_BeginPacketReception(DownstreamPacketTypeDef* freePacket)
{
if (DownstreamInterfaceState != INTERFACE_STATE_RX_SIZE_WAIT)
{
SPI_INTERFACE_FREAKOUT_VOID;
}
DownstreamInterfaceState = INTERFACE_STATE_RX_SIZE;
CurrentWorkingPacket = freePacket;
CurrentWorkingPacket->Length = 0; //Our RX buffer is used by HAL_SPI_Receive_DMA as dummy TX data, we set Length to 0 so downstream will know this is a dummy packet.
SPI1_NSS_ASSERT;
if (HAL_SPI_Receive_DMA(&hspi1,
(uint8_t*)&CurrentWorkingPacket->Length,
(2 + 1)) != HAL_OK) //"When the CRC feature is enabled the pData Length must be Size + 1"
{
SPI_INTERFACE_FREAKOUT_VOID;
}
}
//Called at the end of the SPI TX DMA transfer,
//at DMA2 interrupt priority. Assume *hspi points to our hspi1.
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
SpiPacketReceivedCallbackTypeDef tempPacketCallback;
SPI1_NSS_DEASSERT;
if ((DownstreamInterfaceState != INTERFACE_STATE_RX_SIZE) &&
(DownstreamInterfaceState != INTERFACE_STATE_RX_PACKET))
{
SPI_INTERFACE_FREAKOUT_VOID;
}
if (DownstreamInterfaceState == INTERFACE_STATE_RX_SIZE)
{
if ((CurrentWorkingPacket->Length < DOWNSTREAM_PACKET_LEN_MIN) ||
(CurrentWorkingPacket->Length > DOWNSTREAM_PACKET_LEN))
{
SPI_INTERFACE_FREAKOUT_VOID;
}
DownstreamInterfaceState = INTERFACE_STATE_RX_PACKET_WAIT;
if (DOWNSTREAM_TX_OK_ACTIVE)
{
Downstream_TxOkInterrupt();
}
return;
}
if (DownstreamInterfaceState == INTERFACE_STATE_RX_PACKET)
{
DownstreamInterfaceState = INTERFACE_STATE_IDLE;
if ((SentCommandClass != (CurrentWorkingPacket->CommandClass & COMMAND_CLASS_MASK)) ||
(SentCommand != CurrentWorkingPacket->Command))
{
SPI_INTERFACE_FREAKOUT_VOID;
}
if (ReceivePacketCallback == NULL)
{
SPI_INTERFACE_FREAKOUT_VOID;
}
//USB interface may want to receive another packet immediately,
//so clear ReceivePacketCallback before the call.
//It is the callback's responsibility to release the packet buffer we are passing to it!
tempPacketCallback = ReceivePacketCallback;
ReceivePacketCallback = NULL;
tempPacketCallback(CurrentWorkingPacket);
}
}
//Something bad happened! Possibly CRC error...
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
{
SPI_INTERFACE_FREAKOUT_VOID;
}