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/Downstream/Src/downstream_spi.c

370 lines
9.8 KiB

/*
* upstream_spi.c
*
* Created on: 24/07/2015
* Author: Robert Fisk
*/
#include "downstream_interface_def.h"
#include "downstream_spi.h"
#include "downstream_statemachine.h"
#include "board_config.h"
#include "led.h"
SPI_HandleTypeDef Hspi1;
DownstreamPacketTypeDef DownstreamPacket0;
DownstreamPacketTypeDef DownstreamPacket1;
DownstreamPacketTypeDef* CurrentWorkingPacket;
DownstreamPacketTypeDef* NextTxPacket = NULL;
InterfaceStateTypeDef DownstreamInterfaceState = DOWNSTREAM_INTERFACE_IDLE;
FreePacketCallbackTypeDef PendingFreePacketCallback = NULL; //Indicates someone is waiting for a packet buffer to become available
SpiPacketReceivedCallbackTypeDef ReceivePacketCallback = NULL; //Indicates someone is waiting for a received packet
void SPI1_Init(void);
HAL_StatusTypeDef Downstream_CheckPreparePacketReception(void);
void Downstream_PreparePacketReception(DownstreamPacketTypeDef* freePacket);
void Downstream_InitSPI(void)
{
SPI1_Init();
DownstreamPacket0.Busy = NOT_BUSY;
DownstreamPacket1.Busy = NOT_BUSY;
}
void SPI1_Init(void)
{
Hspi1.Instance = SPI1;
Hspi1.Init.Mode = SPI_MODE_SLAVE;
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_HARD_INPUT;
Hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
Hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
Hspi1.Init.TIMode = SPI_TIMODE_DISABLED;
Hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_ENABLE;
Hspi1.Init.CRCPolynomial = SPI_CRC_DEFAULTPOLYNOMIAL;
HAL_SPI_Init(&Hspi1);
}
//Used by downstream state machine and USB host classes.
HAL_StatusTypeDef Downstream_GetFreePacket(FreePacketCallbackTypeDef callback)
{
if (DownstreamInterfaceState >= DOWNSTREAM_INTERFACE_ERROR)
{
return HAL_ERROR;
}
//Do we already have a queued callback?
if (PendingFreePacketCallback != NULL)
{
DOWNSTREAM_SPI_FREAKOUT_RETURN_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;
}
//Used by Downstream state machine and USB host classes.
void Downstream_ReleasePacket(DownstreamPacketTypeDef* packetToRelease)
{
FreePacketCallbackTypeDef tempCallback;
if (DownstreamInterfaceState >= DOWNSTREAM_INTERFACE_ERROR)
{
return;
}
if ((packetToRelease != &DownstreamPacket0) &&
(packetToRelease != &DownstreamPacket1))
{
DOWNSTREAM_SPI_FREAKOUT_RETURN_VOID;
}
if (PendingFreePacketCallback != NULL)
{
tempCallback = PendingFreePacketCallback; //In extreme situations, running this callback can trigger another request for a free packet,
PendingFreePacketCallback = NULL; //thereby causing GetFreePacket to freak out. So we need to clear the callback indicator first.
tempCallback(packetToRelease);
}
else
{
packetToRelease->Busy = NOT_BUSY;
}
}
//Used by Downstream state machine and USB classes.
//Ok to call when idle or transmitting.
//Not OK to call when receiving or awaiting reception.
HAL_StatusTypeDef Downstream_ReceivePacket(SpiPacketReceivedCallbackTypeDef callback)
{
if (DownstreamInterfaceState >= DOWNSTREAM_INTERFACE_ERROR)
{
return HAL_ERROR;
}
if (ReceivePacketCallback != NULL)
{
DOWNSTREAM_SPI_FREAKOUT_RETURN_HAL_ERROR;
}
ReceivePacketCallback = callback;
return Downstream_CheckPreparePacketReception();
}
//Internal use only
HAL_StatusTypeDef Downstream_CheckPreparePacketReception(void)
{
if (DownstreamInterfaceState == DOWNSTREAM_INTERFACE_IDLE)
{
DownstreamInterfaceState = DOWNSTREAM_INTERFACE_RX_SIZE_WAIT;
return Downstream_GetFreePacket(Downstream_PreparePacketReception);
}
return HAL_OK;
}
//Internal use only
void Downstream_PreparePacketReception(DownstreamPacketTypeDef* freePacket)
{
if (DownstreamInterfaceState != DOWNSTREAM_INTERFACE_RX_SIZE_WAIT)
{
DOWNSTREAM_SPI_FREAKOUT_RETURN_VOID;
}
CurrentWorkingPacket = freePacket;
//CurrentWorkingPacket->Length = 0;
//if (HAL_SPI_TransmitReceive_DMA(... ????
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"
{
DOWNSTREAM_SPI_FREAKOUT_RETURN_VOID;
}
UPSTREAM_TX_REQUEST_ASSERT;
}
//Called at the end of the SPI RX DMA transfer,
//at DMA2 interrupt priority. Assume *hspi points to our hspi1.
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
SpiPacketReceivedCallbackTypeDef tempPacketCallback;
UPSTREAM_TX_REQUEST_DEASSERT;
if (DownstreamInterfaceState >= DOWNSTREAM_INTERFACE_ERROR)
{
return;
}
if (DownstreamInterfaceState == DOWNSTREAM_INTERFACE_RX_SIZE_WAIT)
{
if ((CurrentWorkingPacket->Length < DOWNSTREAM_PACKET_LEN_MIN) ||
(CurrentWorkingPacket->Length > DOWNSTREAM_PACKET_LEN))
{
DOWNSTREAM_SPI_FREAKOUT_RETURN_VOID;
}
DownstreamInterfaceState = DOWNSTREAM_INTERFACE_RX_PACKET_WAIT;
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"
{
DOWNSTREAM_SPI_FREAKOUT_RETURN_VOID;
}
UPSTREAM_TX_REQUEST_ASSERT;
return;
}
if (DownstreamInterfaceState == DOWNSTREAM_INTERFACE_RX_PACKET_WAIT)
{
DownstreamInterfaceState = DOWNSTREAM_INTERFACE_IDLE;
if (ReceivePacketCallback == NULL)
{
DOWNSTREAM_SPI_FREAKOUT_RETURN_VOID;
}
//Packet processor 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);
return;
}
DOWNSTREAM_SPI_FREAKOUT_RETURN_VOID;
}
//Used by Downstream state machine (and USB classes?).
//Call when idle or transmitting.
//It doesn't make sense to call when receiving or awaiting reception.
HAL_StatusTypeDef Downstream_TransmitPacket(DownstreamPacketTypeDef* packetToWrite)
{
if (DownstreamInterfaceState >= DOWNSTREAM_INTERFACE_ERROR)
{
return HAL_ERROR;
}
//Sanity checks
if ((packetToWrite != &DownstreamPacket0) &&
(packetToWrite != &DownstreamPacket1))
{
DOWNSTREAM_SPI_FREAKOUT_RETURN_HAL_ERROR;
}
if ((packetToWrite->Busy != BUSY) ||
(packetToWrite->Length < DOWNSTREAM_PACKET_LEN_MIN) ||
(packetToWrite->Length > DOWNSTREAM_PACKET_LEN))
{
DOWNSTREAM_SPI_FREAKOUT_RETURN_HAL_ERROR;
}
if (NextTxPacket != NULL)
{
DOWNSTREAM_SPI_FREAKOUT_RETURN_HAL_ERROR;
}
switch (DownstreamInterfaceState)
{
case DOWNSTREAM_INTERFACE_TX_SIZE_WAIT:
case DOWNSTREAM_INTERFACE_TX_PACKET_WAIT:
NextTxPacket = packetToWrite;
break;
case DOWNSTREAM_INTERFACE_IDLE:
DownstreamInterfaceState = DOWNSTREAM_INTERFACE_TX_SIZE_WAIT;
CurrentWorkingPacket = packetToWrite;
if (HAL_SPI_TransmitReceive_DMA(&Hspi1,
(uint8_t*)&CurrentWorkingPacket->Length,
(uint8_t*)&CurrentWorkingPacket->Length,
2 + 1) != HAL_OK) //"When the CRC feature is enabled the pRxData Length must be Size + 1"
{
DOWNSTREAM_SPI_FREAKOUT_RETURN_HAL_ERROR;
}
UPSTREAM_TX_REQUEST_ASSERT;
break;
default:
DOWNSTREAM_SPI_FREAKOUT_RETURN_HAL_ERROR;
}
return HAL_OK;
}
//Called at the end of the SPI TxRx DMA transfer,
//at DMA2 interrupt priority. Assume *hspi points to our hspi1.
//We use TxRx to send our reply packet to check if Upstream was trying
//to send us a packet at the same time.
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
UPSTREAM_TX_REQUEST_DEASSERT;
if (DownstreamInterfaceState >= DOWNSTREAM_INTERFACE_ERROR)
{
return;
}
if (DownstreamInterfaceState != DOWNSTREAM_INTERFACE_TX_SIZE_WAIT)
{
DOWNSTREAM_SPI_FREAKOUT_RETURN_VOID;
}
if (CurrentWorkingPacket->Length != 0)
{
//Currently we just freak out if Upstream sends us an unexpected command.
//Theoretically we could reset our downstream state machine and accept the new command...
DOWNSTREAM_SPI_FREAKOUT_RETURN_VOID;
}
DownstreamInterfaceState = DOWNSTREAM_INTERFACE_TX_PACKET_WAIT;
if ((HAL_SPI_Transmit_DMA(&Hspi1,
&CurrentWorkingPacket->CommandClass,
CurrentWorkingPacket->Length)) != HAL_OK)
{
DOWNSTREAM_SPI_FREAKOUT_RETURN_VOID;
}
UPSTREAM_TX_REQUEST_ASSERT;
}
//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)
{
UPSTREAM_TX_REQUEST_DEASSERT;
if (DownstreamInterfaceState >= DOWNSTREAM_INTERFACE_ERROR)
{
return;
}
if (DownstreamInterfaceState != DOWNSTREAM_INTERFACE_TX_PACKET_WAIT)
{
DOWNSTREAM_SPI_FREAKOUT_RETURN_VOID;
}
Downstream_ReleasePacket(CurrentWorkingPacket);
if (NextTxPacket != NULL)
{
//NextTxPacket has already passed the checks in Downstream_TransmitPacket.
//So we just need to pass it to HAL_SPI_Transmit_DMA.
DownstreamInterfaceState = DOWNSTREAM_INTERFACE_TX_SIZE_WAIT;
CurrentWorkingPacket = NextTxPacket;
NextTxPacket = NULL;
if (HAL_SPI_TransmitReceive_DMA(&Hspi1,
(uint8_t*)&CurrentWorkingPacket->Length,
(uint8_t*)&CurrentWorkingPacket->Length,
2 + 1) != HAL_OK) //"When the CRC feature is enabled the pRxData Length must be Size + 1"
{
DOWNSTREAM_SPI_FREAKOUT_RETURN_VOID;
}
UPSTREAM_TX_REQUEST_ASSERT;
return;
}
DownstreamInterfaceState = DOWNSTREAM_INTERFACE_IDLE;
if (ReceivePacketCallback != NULL)
{
Downstream_CheckPreparePacketReception();
}
}
//Something bad happened! Possibly CRC error...
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
{
DOWNSTREAM_SPI_FREAKOUT_RETURN_VOID;
}