/*
* upstream_spi . c
*
* Created on : 21 / 06 / 2015
* Author : Robert Fisk
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2.0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at https : //mozilla.org/MPL/2.0/.
*/
# include "upstream_interface_def.h"
# include "upstream_spi.h"
# include "upstream_statemachine.h"
# include "stm32f4xx_hal.h"
# include "usbd_def.h"
# include "board_config.h"
# include "interrupts.h"
SPI_HandleTypeDef Hspi1 ;
UpstreamPacketTypeDef UpstreamPacket0 ;
UpstreamPacketTypeDef UpstreamPacket1 ;
UpstreamPacketTypeDef * CurrentWorkingPacket ;
UpstreamPacketTypeDef * NextTxPacket = NULL ; //Indicates we have a pending TX packet
InterfaceStateTypeDef UpstreamInterfaceState = UPSTREAM_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
uint32_t TemporaryIncomingPacketLength ; //We don't actually care about what Downstream sends us when we are transmitting. We just need somewhere to put it so that our own packet length is not overwritten.
uint8_t TxOkInterruptReceived = 0 ;
uint8_t SpiInterruptCompleted = 0 ;
uint8_t SentCommandClass ;
uint8_t SentCommand ;
void Upstream_BeginTransmitPacketSize ( void ) ;
void Upstream_BeginTransmitPacketBody ( void ) ;
HAL_StatusTypeDef Upstream_CheckBeginPacketReception ( void ) ;
void Upstream_BeginReceivePacketSize ( UpstreamPacketTypeDef * freePacket ) ;
void Upstream_BeginReceivePacketBody ( void ) ;
void Upstream_SPIProcess ( void ) ;
void Upstream_InitSPI ( void )
{
UpstreamPacket0 . Busy = NOT_BUSY ;
UpstreamPacket1 . Busy = NOT_BUSY ;
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_16BIT ;
Hspi1 . Init . CLKPolarity = SPI_POLARITY_LOW ;
Hspi1 . Init . CLKPhase = SPI_PHASE_1EDGE ;
Hspi1 . Init . NSS = SPI_NSS_SOFT ;
Hspi1 . Init . BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8 ; //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 ) ;
if ( DOWNSTREAM_TX_OK_ACTIVE )
{
TxOkInterruptReceived = 1 ;
}
}
//Used by USB interface classes, and by our internal RX code.
HAL_StatusTypeDef Upstream_GetFreePacket ( FreePacketCallbackTypeDef callback )
{
if ( UpstreamInterfaceState > = UPSTREAM_INTERFACE_ERROR )
{
return HAL_ERROR ;
}
//Do we already have a queued callback?
if ( PendingFreePacketCallback ! = NULL )
{
UPSTREAM_SPI_FREAKOUT ;
return HAL_ERROR ;
}
//Check if there is a free buffer now
if ( UpstreamPacket0 . Busy = = NOT_BUSY )
{
UpstreamPacket0 . Busy = BUSY ;
callback ( & UpstreamPacket0 ) ;
return HAL_OK ;
}
if ( UpstreamPacket1 . Busy = = NOT_BUSY )
{
UpstreamPacket1 . Busy = BUSY ;
callback ( & UpstreamPacket1 ) ;
return HAL_OK ;
}
//Otherwise save requested address for when a buffer becomes free in the future
PendingFreePacketCallback = callback ;
return HAL_OK ;
}
UpstreamPacketTypeDef * Upstream_GetFreePacketImmediately ( void )
{
if ( UpstreamInterfaceState > = UPSTREAM_INTERFACE_ERROR )
{
return NULL ;
}
//We are expecting a free buffer now
if ( UpstreamPacket0 . Busy = = NOT_BUSY )
{
UpstreamPacket0 . Busy = BUSY ;
return & UpstreamPacket0 ;
}
if ( UpstreamPacket1 . Busy = = NOT_BUSY )
{
UpstreamPacket1 . Busy = BUSY ;
return & UpstreamPacket1 ;
}
//Should not happen:
UPSTREAM_SPI_FREAKOUT ;
return NULL ;
}
//Used by USB interface classes, and by our internal RX code.
void Upstream_ReleasePacket ( UpstreamPacketTypeDef * packetToRelease )
{
FreePacketCallbackTypeDef tempCallback ;
if ( UpstreamInterfaceState > = UPSTREAM_INTERFACE_ERROR )
{
return ;
}
if ( ( packetToRelease ! = & UpstreamPacket0 ) & &
( packetToRelease ! = & UpstreamPacket1 ) )
{
UPSTREAM_SPI_FREAKOUT ;
return ;
}
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 USB interface classes only.
//OK to call when still transmitting another packet.
//Not OK to call when receiving or waiting for downstream reply,
//as we can't let the size/packet sequence get out of sync.
HAL_StatusTypeDef Upstream_TransmitPacket ( UpstreamPacketTypeDef * packetToWrite )
{
if ( UpstreamInterfaceState > = UPSTREAM_INTERFACE_ERROR )
{
return HAL_ERROR ;
}
//Sanity checks
if ( ( packetToWrite ! = & UpstreamPacket0 ) & &
( packetToWrite ! = & UpstreamPacket1 ) )
{
UPSTREAM_SPI_FREAKOUT ;
return HAL_ERROR ;
}
if ( ( packetToWrite - > Busy ! = BUSY ) | |
( packetToWrite - > Length16 < UPSTREAM_PACKET_LEN_MIN_16 ) | |
( packetToWrite - > Length16 > UPSTREAM_PACKET_LEN_16 ) )
{
UPSTREAM_SPI_FREAKOUT ;
return HAL_ERROR ;
}
if ( NextTxPacket ! = NULL )
{
UPSTREAM_SPI_FREAKOUT ;
return HAL_ERROR ;
}
switch ( UpstreamInterfaceState )
{
case UPSTREAM_INTERFACE_TX_SIZE_WAIT :
case UPSTREAM_INTERFACE_TX_SIZE :
case UPSTREAM_INTERFACE_TX_PACKET_WAIT :
case UPSTREAM_INTERFACE_TX_PACKET :
NextTxPacket = packetToWrite ;
break ;
case UPSTREAM_INTERFACE_IDLE :
UpstreamInterfaceState = UPSTREAM_INTERFACE_TX_SIZE_WAIT ;
CurrentWorkingPacket = packetToWrite ;
SentCommandClass = CurrentWorkingPacket - > CommandClass ;
SentCommand = CurrentWorkingPacket - > Command ;
//Downstream may have set TxOk pin before we wanted to transmit.
//In this case we can go ahead and transmit now.
if ( TxOkInterruptReceived )
{
TxOkInterruptReceived = 0 ;
Upstream_BeginTransmitPacketSize ( ) ;
}
break ;
default :
UPSTREAM_SPI_FREAKOUT ;
return HAL_ERROR ;
}
return HAL_OK ;
}
//Called at the end of the SPI TxRx transfer,
//at SPI1 interrupt priority. Assume *hspi points to our hspi1.
//We TxRx our outgoing packet because the SPI hardware freaks out if we only Tx it :-/
void HAL_SPI_TxRxCpltCallback ( SPI_HandleTypeDef * hspi )
{
SpiInterruptCompleted = 1 ;
//Elevate priority here to stop EXT3I sneaking in
//before we have a chance to process UpstreamInterfaceState change.
__set_BASEPRI ( INT_PRIORITY_OTG_FS < < ( 8 - __NVIC_PRIO_BITS ) ) ;
}
//Preemption protection wrapper around Upstream_SPIProcess()
//We must protect against preemption by USB and EXT3 interrupts at priority 10!
void Upstream_SPIProcess_InterruptSafe ( void )
{
//This is done on SPI interrupt callback...
//__set_BASEPRI(INT_PRIORITY_OTG_FS << (8 - __NVIC_PRIO_BITS));
if ( SpiInterruptCompleted = = 0 )
{
return ;
}
SpiInterruptCompleted = 0 ;
Upstream_SPIProcess ( ) ;
__set_BASEPRI ( 0 ) ;
}
//Called from main().
//Must be protected against preemption by USB and EXT3 interrupts at priority 10!
void Upstream_SPIProcess ( void )
{
SpiPacketReceivedCallbackTypeDef tempPacketCallback ;
UpstreamPacketTypeDef * tempPacketToFree ;
SPI1_NSS_DEASSERT ;
if ( UpstreamInterfaceState > = UPSTREAM_INTERFACE_ERROR )
{
return ;
}
//Finished transmitting packet size
if ( UpstreamInterfaceState = = UPSTREAM_INTERFACE_TX_SIZE )
{
UpstreamInterfaceState = UPSTREAM_INTERFACE_TX_PACKET_WAIT ;
if ( TxOkInterruptReceived )
{
TxOkInterruptReceived = 0 ;
Upstream_BeginTransmitPacketBody ( ) ;
}
return ;
}
//Finished transmitting packet body
if ( UpstreamInterfaceState = = UPSTREAM_INTERFACE_TX_PACKET )
{
if ( ( PendingFreePacketCallback ! = NULL ) & & ( NextTxPacket = = NULL ) )
{
UPSTREAM_SPI_FREAKOUT ;
return ;
}
tempPacketToFree = CurrentWorkingPacket ;
if ( NextTxPacket ! = NULL )
{
//NextTxPacket has already passed the checks in Upstream_TransmitPacket.
//So we just need to pass it to HAL_SPI_Transmit_DMA.
UpstreamInterfaceState = UPSTREAM_INTERFACE_TX_SIZE_WAIT ;
CurrentWorkingPacket = NextTxPacket ;
NextTxPacket = NULL ;
SentCommandClass = CurrentWorkingPacket - > CommandClass ;
SentCommand = CurrentWorkingPacket - > Command ;
if ( TxOkInterruptReceived )
{
TxOkInterruptReceived = 0 ;
Upstream_BeginTransmitPacketSize ( ) ;
}
}
else
{
//No packet queued for transmission:
UpstreamInterfaceState = UPSTREAM_INTERFACE_IDLE ;
if ( ReceivePacketCallback ! = NULL )
{
Upstream_CheckBeginPacketReception ( ) ;
}
}
//Release old packet after moving Next to Current
Upstream_ReleasePacket ( tempPacketToFree ) ;
return ;
}
if ( UpstreamInterfaceState = = UPSTREAM_INTERFACE_RX_SIZE )
{
if ( ( CurrentWorkingPacket - > Length16 < UPSTREAM_PACKET_LEN_MIN_16 ) | |
( CurrentWorkingPacket - > Length16 > UPSTREAM_PACKET_LEN_16 ) )
{
UPSTREAM_SPI_FREAKOUT ;
return ;
}
UpstreamInterfaceState = UPSTREAM_INTERFACE_RX_PACKET_WAIT ;
if ( TxOkInterruptReceived )
{
TxOkInterruptReceived = 0 ;
Upstream_BeginReceivePacketBody ( ) ;
}
return ;
}
if ( UpstreamInterfaceState = = UPSTREAM_INTERFACE_RX_PACKET )
{
UpstreamInterfaceState = UPSTREAM_INTERFACE_IDLE ;
if ( ReceivePacketCallback = = NULL )
{
UPSTREAM_SPI_FREAKOUT ;
return ;
}
if ( ( CurrentWorkingPacket - > CommandClass = = COMMAND_CLASS_ERROR ) & &
( CurrentWorkingPacket - > Command = = COMMAND_ERROR_DEVICE_DISCONNECTED ) )
{
Upstream_ReleasePacket ( CurrentWorkingPacket ) ;
ReceivePacketCallback = NULL ;
Upstream_StateMachine_DeviceDisconnected ( ) ;
return ;
}
if ( ( ( CurrentWorkingPacket - > CommandClass & COMMAND_CLASS_MASK ) ! = ( SentCommandClass & COMMAND_CLASS_MASK ) ) | |
( CurrentWorkingPacket - > Command ! = SentCommand ) )
{
UPSTREAM_SPI_FREAKOUT ;
Upstream_ReleasePacket ( CurrentWorkingPacket ) ;
CurrentWorkingPacket = NULL ; //Call back with a NULL packet to indicate error
}
//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 ) ;
return ;
}
//case default:
UPSTREAM_SPI_FREAKOUT ;
}
//Used by USB interface classes.
//Ok to call when idle or transmitting.
//Not OK to call when receiving or waiting for downstream reply.
HAL_StatusTypeDef Upstream_ReceivePacket ( SpiPacketReceivedCallbackTypeDef callback )
{
if ( UpstreamInterfaceState > = UPSTREAM_INTERFACE_ERROR )
{
return HAL_ERROR ;
}
if ( ReceivePacketCallback ! = NULL )
{
UPSTREAM_SPI_FREAKOUT ;
return HAL_ERROR ;
}
ReceivePacketCallback = callback ;
return Upstream_CheckBeginPacketReception ( ) ;
}
//Internal use only.
HAL_StatusTypeDef Upstream_CheckBeginPacketReception ( void )
{
if ( UpstreamInterfaceState > = UPSTREAM_INTERFACE_ERROR )
{
return HAL_ERROR ;
}
if ( UpstreamInterfaceState > = UPSTREAM_INTERFACE_RX_SIZE_WAIT )
{
UPSTREAM_SPI_FREAKOUT ;
return HAL_ERROR ;
}
if ( UpstreamInterfaceState = = UPSTREAM_INTERFACE_IDLE )
{
UpstreamInterfaceState = UPSTREAM_INTERFACE_RX_SIZE_WAIT ;
if ( TxOkInterruptReceived )
{
TxOkInterruptReceived = 0 ;
Upstream_GetFreePacket ( Upstream_BeginReceivePacketSize ) ;
}
}
return HAL_OK ;
}
//This is called by EXTI3 falling edge interrupt,
//indicating that downstream is ready for the next transaction.
void Upstream_TxOkInterrupt ( void )
{
if ( UpstreamInterfaceState > = UPSTREAM_INTERFACE_ERROR )
{
return ;
}
switch ( UpstreamInterfaceState )
{
case UPSTREAM_INTERFACE_IDLE :
TxOkInterruptReceived = 1 ;
break ;
case UPSTREAM_INTERFACE_TX_SIZE_WAIT :
Upstream_BeginTransmitPacketSize ( ) ;
break ;
case UPSTREAM_INTERFACE_TX_PACKET_WAIT :
Upstream_BeginTransmitPacketBody ( ) ;
break ;
case UPSTREAM_INTERFACE_RX_SIZE_WAIT :
Upstream_GetFreePacket ( Upstream_BeginReceivePacketSize ) ;
break ;
case UPSTREAM_INTERFACE_RX_PACKET_WAIT :
Upstream_BeginReceivePacketBody ( ) ;
break ;
default :
UPSTREAM_SPI_FREAKOUT ;
}
}
void Upstream_BeginTransmitPacketSize ( void )
{
UpstreamInterfaceState = UPSTREAM_INTERFACE_TX_SIZE ;
SPI1_NSS_ASSERT ;
if ( HAL_SPI_TransmitReceive_IT ( & Hspi1 ,
( uint8_t * ) & CurrentWorkingPacket - > Length16 ,
( uint8_t * ) & TemporaryIncomingPacketLength ,
2 ) ! = HAL_OK ) //We only need to write one word, but the peripheral library freaks out...
{
UPSTREAM_SPI_FREAKOUT ;
}
}
void Upstream_BeginTransmitPacketBody ( void )
{
UpstreamInterfaceState = UPSTREAM_INTERFACE_TX_PACKET ;
SPI1_NSS_ASSERT ;
if ( HAL_SPI_TransmitReceive_IT ( & Hspi1 ,
& CurrentWorkingPacket - > CommandClass ,
& CurrentWorkingPacket - > CommandClass ,
( ( CurrentWorkingPacket - > Length16 < 2 ) ? 2 : CurrentWorkingPacket - > Length16 ) ) ! = HAL_OK )
{
UPSTREAM_SPI_FREAKOUT ;
}
}
//Internal use only.
//Called when we want to receive downstream packet, and a packet buffer has become free.
void Upstream_BeginReceivePacketSize ( UpstreamPacketTypeDef * freePacket )
{
if ( UpstreamInterfaceState > = UPSTREAM_INTERFACE_ERROR )
{
return ;
}
if ( UpstreamInterfaceState ! = UPSTREAM_INTERFACE_RX_SIZE_WAIT )
{
UPSTREAM_SPI_FREAKOUT ;
return ;
}
UpstreamInterfaceState = UPSTREAM_INTERFACE_RX_SIZE ;
CurrentWorkingPacket = freePacket ;
CurrentWorkingPacket - > Length16 = 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 ;
TemporaryIncomingPacketLength = 0 ;
if ( HAL_SPI_TransmitReceive_IT ( & Hspi1 ,
( uint8_t * ) & TemporaryIncomingPacketLength ,
( uint8_t * ) & CurrentWorkingPacket - > Length16 ,
2 ) ! = HAL_OK ) //We only need to write one word, but the peripheral library freaks out...
{
UPSTREAM_SPI_FREAKOUT ;
}
}
void Upstream_BeginReceivePacketBody ( void )
{
UpstreamInterfaceState = UPSTREAM_INTERFACE_RX_PACKET ;
SPI1_NSS_ASSERT ;
if ( HAL_SPI_TransmitReceive_IT ( & Hspi1 ,
& CurrentWorkingPacket - > CommandClass ,
& CurrentWorkingPacket - > CommandClass ,
( ( CurrentWorkingPacket - > Length16 < 2 ) ? 2 : CurrentWorkingPacket - > Length16 ) ) ! = HAL_OK )
{
UPSTREAM_SPI_FREAKOUT ;
}
}
//Something bad happened! Possibly CRC error...
void HAL_SPI_ErrorCallback ( SPI_HandleTypeDef * hspi )
{
SpiPacketReceivedCallbackTypeDef tempPacketCallback ;
if ( UpstreamInterfaceState > = UPSTREAM_INTERFACE_ERROR )
{
return ;
}
UPSTREAM_SPI_FREAKOUT ;
if ( ReceivePacketCallback ! = NULL )
{
tempPacketCallback = ReceivePacketCallback ;
ReceivePacketCallback = NULL ;
tempPacketCallback ( NULL ) ; //Call back with a NULL packet to indicate error
}
}