parent
6185c52b56
commit
6800a95abe
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* downstream_interface_def.h
|
||||||
|
*
|
||||||
|
* Created on: 24/07/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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INC_DOWNSTREAM_INTERFACE_DEF_H_
|
||||||
|
#define INC_DOWNSTREAM_INTERFACE_DEF_H_
|
||||||
|
|
||||||
|
|
||||||
|
//***************
|
||||||
|
// Attention!
|
||||||
|
// Keep this file synchronised with upstream_interface_def.h
|
||||||
|
// in the Upstream project.
|
||||||
|
//***************
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define COMMAND_CLASS_DATA_FLAG 0x80
|
||||||
|
#define COMMAND_CLASS_MASK ((uint8_t)(~COMMAND_CLASS_DATA_FLAG))
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
COMMAND_CLASS_INTERFACE,
|
||||||
|
COMMAND_CLASS_MASS_STORAGE,
|
||||||
|
COMMAND_CLASS_HID_MOUSE,
|
||||||
|
COMMAND_CLASS_HID_KEYBOARD,
|
||||||
|
//...
|
||||||
|
COMMAND_CLASS_ERROR
|
||||||
|
}
|
||||||
|
InterfaceCommandClassTypeDef;
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
COMMAND_INTERFACE_ECHO, //Returns echo packet including all data
|
||||||
|
COMMAND_INTERFACE_NOTIFY_DEVICE //Returns COMMAND_CLASS_*** byte when downstream USB device is connected
|
||||||
|
}
|
||||||
|
InterfaceCommandInterfaceTypeDef;
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
COMMAND_MSC_TEST_UNIT_READY, //Returns HAL_StatusTypeDef result
|
||||||
|
COMMAND_MSC_GET_CAPACITY, //Returns uint32_t blk_nbr, uint32_t blk_size
|
||||||
|
COMMAND_MSC_READ, //Returns data stream or error packet
|
||||||
|
COMMAND_MSC_WRITE, //Waits for data stream or returns error packet
|
||||||
|
COMMAND_MSC_DISCONNECT, //Returns same packet after sending Stop command to device
|
||||||
|
COMMAND_MSC_POLL_DISCONNECT //Returns same packet if device is still connected
|
||||||
|
}
|
||||||
|
InterfaceCommandMscTypeDef;
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
COMMAND_HID_GET_REPORT, //Returns HID report from device
|
||||||
|
COMMAND_HID_SET_REPORT //Sends HID report to device. Simple ack packet contains no data.
|
||||||
|
}
|
||||||
|
InterfaceCommandHidTypeDef;
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
COMMAND_ERROR_GENERIC, //Something went wrong, time to FREAKOUT
|
||||||
|
COMMAND_ERROR_DEVICE_DISCONNECTED, //Device unexpectedly disconnected
|
||||||
|
}
|
||||||
|
InterfaceCommandErrorTypeDef;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* INC_DOWNSTREAM_INTERFACE_DEF_H_ */
|
@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#ifndef INC_DOWNSTREAM_MSC_H_
|
||||||
|
#define INC_DOWNSTREAM_MSC_H_
|
||||||
|
|
||||||
|
|
||||||
|
#include "downstream_interface_def.h"
|
||||||
|
#include "downstream_spi.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define MSC_SUPPORTED_BLOCK_SIZE 512
|
||||||
|
#define MSC_FIXED_LUN 0
|
||||||
|
|
||||||
|
|
||||||
|
typedef void (*DownstreamMSCCallbackPacketTypeDef)(DownstreamPacketTypeDef* receivedPacket,
|
||||||
|
uint16_t dataLength8);
|
||||||
|
|
||||||
|
|
||||||
|
InterfaceCommandClassTypeDef Downstream_MSC_ApproveConnectedDevice(void);
|
||||||
|
void Downstream_MSC_PacketProcessor(DownstreamPacketTypeDef* receivedPacket);
|
||||||
|
StatusTypeDef Downstream_MSC_PutStreamDataPacket(DownstreamPacketTypeDef* packetToSend,
|
||||||
|
uint32_t dataLength8);
|
||||||
|
StatusTypeDef Downstream_MSC_GetStreamDataPacket(DownstreamMSCCallbackPacketTypeDef callback);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* INC_DOWNSTREAM_MSC_H_ */
|
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* downstream_spi.h
|
||||||
|
*
|
||||||
|
* Created on: 24/07/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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INC_DOWNSTREAM_SPI_H_
|
||||||
|
#define INC_DOWNSTREAM_SPI_H_
|
||||||
|
|
||||||
|
|
||||||
|
#include "usbh_conf.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define DOWNSTREAM_PACKET_HEADER_LEN (2) //Min length = CommandClass & Command bytes
|
||||||
|
#define DOWNSTREAM_PACKET_LEN (DOWNSTREAM_PACKET_HEADER_LEN + BOT_PAGE_LENGTH)
|
||||||
|
#define DOWNSTREAM_PACKET_LEN_MIN (DOWNSTREAM_PACKET_HEADER_LEN)
|
||||||
|
|
||||||
|
#define DOWNSTREAM_PACKET_HEADER_LEN_16 (DOWNSTREAM_PACKET_HEADER_LEN / 2)
|
||||||
|
#define DOWNSTREAM_PACKET_LEN_16 (DOWNSTREAM_PACKET_LEN / 2)
|
||||||
|
#define DOWNSTREAM_PACKET_LEN_MIN_16 (DOWNSTREAM_PACKET_LEN_MIN / 2)
|
||||||
|
|
||||||
|
#define DOWNSTREAM_SPI_FREAKOUT \
|
||||||
|
do { \
|
||||||
|
Downstream_PacketProcessor_FreakOut(); \
|
||||||
|
DownstreamInterfaceState = DOWNSTREAM_INTERFACE_ERROR; \
|
||||||
|
while (1); \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
DOWNSTREAM_INTERFACE_IDLE,
|
||||||
|
DOWNSTREAM_INTERFACE_RX_SIZE_WAIT,
|
||||||
|
DOWNSTREAM_INTERFACE_RX_PACKET_WAIT,
|
||||||
|
DOWNSTREAM_INTERFACE_TX_SIZE_WAIT,
|
||||||
|
DOWNSTREAM_INTERFACE_TX_PACKET_WAIT,
|
||||||
|
DOWNSTREAM_INTERFACE_ERROR
|
||||||
|
}
|
||||||
|
InterfaceStateTypeDef;
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
NOT_BUSY,
|
||||||
|
BUSY
|
||||||
|
}
|
||||||
|
PacketBusyTypeDef;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
PacketBusyTypeDef Busy; //Everything after Busy should be word-aligned
|
||||||
|
uint16_t Length16 __attribute__ ((aligned (4))); //Packet length includes CommandClass, Command, and Data
|
||||||
|
uint8_t CommandClass;
|
||||||
|
uint8_t Command;
|
||||||
|
uint8_t Data[BOT_PAGE_LENGTH]; //Should (must?) be word-aligned, for USB copy routine
|
||||||
|
}
|
||||||
|
DownstreamPacketTypeDef;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef void (*FreePacketCallbackTypeDef)(DownstreamPacketTypeDef* freePacket);
|
||||||
|
typedef void (*SpiPacketReceivedCallbackTypeDef)(DownstreamPacketTypeDef* receivedPacket);
|
||||||
|
|
||||||
|
|
||||||
|
void Downstream_InitSPI(void);
|
||||||
|
StatusTypeDef Downstream_GetFreePacket(FreePacketCallbackTypeDef callback);
|
||||||
|
DownstreamPacketTypeDef* Downstream_GetFreePacketImmediately(void);
|
||||||
|
void Downstream_ReleasePacket(DownstreamPacketTypeDef* packetToRelease);
|
||||||
|
StatusTypeDef Downstream_ReceivePacket(SpiPacketReceivedCallbackTypeDef callback);
|
||||||
|
StatusTypeDef Downstream_TransmitPacket(DownstreamPacketTypeDef* packetToWrite);
|
||||||
|
void Downstream_SPIProcess(void);
|
||||||
|
|
||||||
|
void DMA_SPI_TxRxCpltCallback(void);
|
||||||
|
//void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* INC_DOWNSTREAM_SPI_H_ */
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* downstream_statemachine.h
|
||||||
|
*
|
||||||
|
* Created on: 2/08/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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INC_DOWNSTREAM_STATEMACHINE_H_
|
||||||
|
#define INC_DOWNSTREAM_STATEMACHINE_H_
|
||||||
|
|
||||||
|
|
||||||
|
#include "usb_host.h"
|
||||||
|
#include "usbh_core.h"
|
||||||
|
#include "downstream_spi.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
STATE_DEVICE_NOT_READY,
|
||||||
|
STATE_DEVICE_READY, //Go here if HOST_USER_CLASS_ACTIVE callback arrives first
|
||||||
|
STATE_WAIT_DEVICE_READY, //Go here if COMMAND_INTERFACE_NOTIFY_DEVICE message arrives first
|
||||||
|
STATE_ACTIVE,
|
||||||
|
STATE_ERROR
|
||||||
|
} DownstreamStateTypeDef;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define DOWNSTREAM_STATEMACHINE_FREAKOUT \
|
||||||
|
do { \
|
||||||
|
USB_Host_Disconnect(); \
|
||||||
|
/*LED_SetState(LED_STATUS_FLASH_ERROR);*/ \
|
||||||
|
/*DownstreamState = STATE_ERROR; */ \
|
||||||
|
while (1); \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Downstream_InitStateMachine(void);
|
||||||
|
void Downstream_HostUserCallback(usbh_host *phost, uint8_t id);
|
||||||
|
void Downstream_PacketProcessor(DownstreamPacketTypeDef* receivedPacket);
|
||||||
|
void Downstream_PacketProcessor_GenericErrorReply(DownstreamPacketTypeDef* replyPacket);
|
||||||
|
void Downstream_PacketProcessor_ClassReply(DownstreamPacketTypeDef* replyPacket);
|
||||||
|
void Downstream_PacketProcessor_NotifyDisconnectReplyRequired(void);
|
||||||
|
void Downstream_PacketProcessor_CheckNotifyDisconnectReply(void);
|
||||||
|
void Downstream_PacketProcessor_SetErrorState(void);
|
||||||
|
void Downstream_PacketProcessor_FreakOut(void);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* INC_DOWNSTREAM_STATEMACHINE_H_ */
|
@ -0,0 +1,358 @@
|
|||||||
|
/*
|
||||||
|
* downstream_msc.c
|
||||||
|
*
|
||||||
|
* Created on: 8/08/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 "downstream_msc.h"
|
||||||
|
#include "downstream_interface_def.h"
|
||||||
|
#include "downstream_statemachine.h"
|
||||||
|
#include "downstream_spi.h"
|
||||||
|
//#include "usbh_msc.h"
|
||||||
|
#include "usbh_msc_core.h"
|
||||||
|
#include "build_config.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_MASS_STORAGE_ENABLED
|
||||||
|
|
||||||
|
extern USBH_HandleTypeDef hUsbHostFS; //Hard-link ourselves to usb_host.c
|
||||||
|
|
||||||
|
|
||||||
|
//Stuff we need to save for our callbacks to use:
|
||||||
|
DownstreamMSCCallbackPacketTypeDef GetStreamDataCallback;
|
||||||
|
uint32_t ByteCount;
|
||||||
|
DownstreamPacketTypeDef* ReadStreamPacket;
|
||||||
|
uint8_t ReadStreamBusy;
|
||||||
|
|
||||||
|
|
||||||
|
static void Downstream_MSC_PacketProcessor_TestUnitReady(DownstreamPacketTypeDef* receivedPacket);
|
||||||
|
static void Downstream_MSC_PacketProcessor_TestUnitReadyCallback(USBH_StatusTypeDef result);
|
||||||
|
static void Downstream_MSC_PacketProcessor_GetCapacity(DownstreamPacketTypeDef* receivedPacket);
|
||||||
|
static void Downstream_MSC_PacketProcessor_BeginRead(DownstreamPacketTypeDef* receivedPacket);
|
||||||
|
static void Downstream_MSC_PacketProcessor_BeginWrite(DownstreamPacketTypeDef* receivedPacket);
|
||||||
|
static void Downstream_MSC_PacketProcessor_RdWrCompleteCallback(USBH_StatusTypeDef result);
|
||||||
|
static void Downstream_MSC_GetStreamDataPacketCallback(DownstreamPacketTypeDef* receivedPacket);
|
||||||
|
static void Downstream_MSC_PacketProcessor_Disconnect(DownstreamPacketTypeDef* receivedPacket);
|
||||||
|
static void Downstream_MSC_PacketProcessor_DisconnectCallback(USBH_StatusTypeDef result);
|
||||||
|
|
||||||
|
|
||||||
|
//High-level checks on the connected device. We don't want some weirdly
|
||||||
|
//configured device to bomb our USB stack, accidentally or otherwise.
|
||||||
|
InterfaceCommandClassTypeDef Downstream_MSC_ApproveConnectedDevice(void)
|
||||||
|
{
|
||||||
|
MSC_HandleTypeDef* MSC_Handle = (MSC_HandleTypeDef*)hUsbHostFS.pActiveClass->pData;
|
||||||
|
|
||||||
|
if (MSC_Handle->unit[MSC_FIXED_LUN].error != MSC_OK)
|
||||||
|
{
|
||||||
|
return COMMAND_CLASS_INTERFACE; //fail
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((MSC_Handle->unit[MSC_FIXED_LUN].capacity.block_nbr == 0) ||
|
||||||
|
(MSC_Handle->unit[MSC_FIXED_LUN].capacity.block_nbr == UINT32_MAX))
|
||||||
|
{
|
||||||
|
return COMMAND_CLASS_INTERFACE; //fail
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MSC_Handle->unit[MSC_FIXED_LUN].capacity.block_size != MSC_SUPPORTED_BLOCK_SIZE)
|
||||||
|
{
|
||||||
|
return COMMAND_CLASS_INTERFACE; //fail
|
||||||
|
}
|
||||||
|
|
||||||
|
return COMMAND_CLASS_MASS_STORAGE; //success!
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Downstream_MSC_PacketProcessor(DownstreamPacketTypeDef* receivedPacket)
|
||||||
|
{
|
||||||
|
switch (receivedPacket->Command)
|
||||||
|
{
|
||||||
|
case COMMAND_MSC_TEST_UNIT_READY:
|
||||||
|
Downstream_MSC_PacketProcessor_TestUnitReady(receivedPacket);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case COMMAND_MSC_GET_CAPACITY:
|
||||||
|
Downstream_MSC_PacketProcessor_GetCapacity(receivedPacket);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case COMMAND_MSC_READ:
|
||||||
|
Downstream_MSC_PacketProcessor_BeginRead(receivedPacket);
|
||||||
|
break;
|
||||||
|
|
||||||
|
#ifdef CONFIG_MASS_STORAGE_WRITES_PERMITTED
|
||||||
|
case COMMAND_MSC_WRITE:
|
||||||
|
Downstream_MSC_PacketProcessor_BeginWrite(receivedPacket);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
case COMMAND_MSC_DISCONNECT:
|
||||||
|
Downstream_MSC_PacketProcessor_Disconnect(receivedPacket);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case COMMAND_MSC_POLL_DISCONNECT:
|
||||||
|
Downstream_PacketProcessor_ClassReply(receivedPacket); //Device is still connected, so send the packet straight back
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Downstream_PacketProcessor_FreakOut();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void Downstream_MSC_PacketProcessor_TestUnitReady(DownstreamPacketTypeDef* receivedPacket)
|
||||||
|
{
|
||||||
|
Downstream_ReleasePacket(receivedPacket);
|
||||||
|
|
||||||
|
if (USBH_MSC_UnitIsReady(&hUsbHostFS,
|
||||||
|
MSC_FIXED_LUN,
|
||||||
|
Downstream_MSC_PacketProcessor_TestUnitReadyCallback) != USBH_BUSY)
|
||||||
|
{
|
||||||
|
Downstream_MSC_PacketProcessor_TestUnitReadyCallback(USBH_FAIL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void Downstream_MSC_PacketProcessor_TestUnitReadyCallback(USBH_StatusTypeDef result)
|
||||||
|
{
|
||||||
|
DownstreamPacketTypeDef* freePacket;
|
||||||
|
|
||||||
|
freePacket = Downstream_GetFreePacketImmediately();
|
||||||
|
freePacket->CommandClass = COMMAND_CLASS_MASS_STORAGE;
|
||||||
|
freePacket->Command = COMMAND_MSC_TEST_UNIT_READY;
|
||||||
|
freePacket->Length16 = DOWNSTREAM_PACKET_HEADER_LEN_16 + 1;
|
||||||
|
|
||||||
|
if (result == USBH_OK)
|
||||||
|
{
|
||||||
|
freePacket->Data[0] = HAL_OK;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
freePacket->Data[0] = HAL_ERROR;
|
||||||
|
}
|
||||||
|
Downstream_PacketProcessor_ClassReply(freePacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void Downstream_MSC_PacketProcessor_GetCapacity(DownstreamPacketTypeDef* receivedPacket)
|
||||||
|
{
|
||||||
|
MSC_HandleTypeDef* MSC_Handle = (MSC_HandleTypeDef*)hUsbHostFS.pActiveClass->pData;
|
||||||
|
|
||||||
|
receivedPacket->Length16 = DOWNSTREAM_PACKET_HEADER_LEN_16 + (8 / 2);
|
||||||
|
*(uint32_t*)&(receivedPacket->Data[0]) = MSC_Handle->unit[MSC_FIXED_LUN].capacity.block_nbr;
|
||||||
|
*(uint32_t*)&(receivedPacket->Data[4]) = (uint32_t)MSC_Handle->unit[MSC_FIXED_LUN].capacity.block_size;
|
||||||
|
Downstream_PacketProcessor_ClassReply(receivedPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void Downstream_MSC_PacketProcessor_BeginRead(DownstreamPacketTypeDef* receivedPacket)
|
||||||
|
{
|
||||||
|
uint64_t readBlockAddress;
|
||||||
|
uint32_t readBlockCount;
|
||||||
|
uint64_t readByteCount;
|
||||||
|
MSC_HandleTypeDef* MSC_Handle = (MSC_HandleTypeDef*)hUsbHostFS.pActiveClass->pData;
|
||||||
|
|
||||||
|
if (receivedPacket->Length16 != (DOWNSTREAM_PACKET_HEADER_LEN_16 + ((4 * 3) / 2)))
|
||||||
|
{
|
||||||
|
Downstream_PacketProcessor_FreakOut();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LED_SetState(LED_STATUS_FLASH_READWRITE);
|
||||||
|
readBlockAddress = *(uint64_t*)&(receivedPacket->Data[0]);
|
||||||
|
readBlockCount = *(uint32_t*)&(receivedPacket->Data[8]);
|
||||||
|
readByteCount = readBlockCount * MSC_Handle->unit[MSC_FIXED_LUN].capacity.block_size;
|
||||||
|
if ((readBlockAddress >= (uint64_t)MSC_Handle->unit[MSC_FIXED_LUN].capacity.block_nbr) ||
|
||||||
|
((readBlockAddress + readBlockCount - 1) >= (uint64_t)MSC_Handle->unit[MSC_FIXED_LUN].capacity.block_nbr) ||
|
||||||
|
(readByteCount > UINT32_MAX))
|
||||||
|
{
|
||||||
|
Downstream_PacketProcessor_FreakOut();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
receivedPacket->Data[0] = HAL_ERROR;
|
||||||
|
receivedPacket->Length16 = DOWNSTREAM_PACKET_HEADER_LEN_16 + 1;
|
||||||
|
if (USBH_MSC_Read(&hUsbHostFS,
|
||||||
|
MSC_FIXED_LUN,
|
||||||
|
(uint32_t)readBlockAddress,
|
||||||
|
readBlockCount,
|
||||||
|
Downstream_MSC_PacketProcessor_RdWrCompleteCallback) == USBH_BUSY)
|
||||||
|
{
|
||||||
|
Downstream_ReleasePacket(receivedPacket);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Fail:
|
||||||
|
Downstream_PacketProcessor_ClassReply(receivedPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void Downstream_MSC_PacketProcessor_RdWrCompleteCallback(USBH_StatusTypeDef result)
|
||||||
|
{
|
||||||
|
if (result != USBH_OK)
|
||||||
|
{
|
||||||
|
Downstream_GetFreePacket(Downstream_PacketProcessor_GenericErrorReply);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Downstream_ReceivePacket(Downstream_PacketProcessor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_MASS_STORAGE_WRITES_PERMITTED
|
||||||
|
static void Downstream_MSC_PacketProcessor_BeginWrite(DownstreamPacketTypeDef* receivedPacket)
|
||||||
|
{
|
||||||
|
uint64_t writeBlockAddress;
|
||||||
|
uint32_t writeBlockCount;
|
||||||
|
uint64_t writeByteCount;
|
||||||
|
MSC_HandleTypeDef* MSC_Handle = (MSC_HandleTypeDef*)hUsbHostFS.pActiveClass->pData;
|
||||||
|
|
||||||
|
if (receivedPacket->Length16 != (DOWNSTREAM_PACKET_HEADER_LEN_16 + ((4 * 3) / 2)))
|
||||||
|
{
|
||||||
|
Downstream_PacketProcessor_FreakOut();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeBlockAddress = *(uint64_t*)&(receivedPacket->Data[0]);
|
||||||
|
writeBlockCount = *(uint32_t*)&(receivedPacket->Data[8]);
|
||||||
|
writeByteCount = writeBlockCount * MSC_Handle->unit[MSC_FIXED_LUN].capacity.block_size;
|
||||||
|
if ((writeBlockAddress >= (uint64_t)MSC_Handle->unit[MSC_FIXED_LUN].capacity.block_nbr) ||
|
||||||
|
((writeBlockAddress + writeBlockCount - 1) >= (uint64_t)MSC_Handle->unit[MSC_FIXED_LUN].capacity.block_nbr) ||
|
||||||
|
(writeByteCount > UINT32_MAX))
|
||||||
|
{
|
||||||
|
Downstream_PacketProcessor_FreakOut();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadStreamPacket = NULL; //Prepare for GetStreamDataPacket's use
|
||||||
|
ReadStreamBusy = 0;
|
||||||
|
ByteCount = (uint32_t)writeByteCount;
|
||||||
|
|
||||||
|
receivedPacket->Data[0] = HAL_ERROR;
|
||||||
|
receivedPacket->Length16 = DOWNSTREAM_PACKET_HEADER_LEN_16 + 1;
|
||||||
|
|
||||||
|
//Our host stack has no way to detect if write-protection is enabled.
|
||||||
|
//So currently we can't return HAL_BUSY to Upstream in this situation.
|
||||||
|
if (USBH_MSC_Write(&hUsbHostFS,
|
||||||
|
MSC_FIXED_LUN,
|
||||||
|
(uint32_t)writeBlockAddress,
|
||||||
|
writeBlockCount,
|
||||||
|
Downstream_MSC_PacketProcessor_RdWrCompleteCallback) == USBH_BUSY)
|
||||||
|
{
|
||||||
|
Downstream_ReleasePacket(receivedPacket);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Fail:
|
||||||
|
Downstream_PacketProcessor_ClassReply(receivedPacket);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
//Used by USB MSC host driver
|
||||||
|
StatusTypeDef Downstream_MSC_PutStreamDataPacket(DownstreamPacketTypeDef* packetToSend,
|
||||||
|
uint32_t dataLength8)
|
||||||
|
{
|
||||||
|
if ((dataLength8 % 2) != 0)
|
||||||
|
{
|
||||||
|
return HAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
packetToSend->Length16 = (dataLength8 / 2) + DOWNSTREAM_PACKET_HEADER_LEN_16;
|
||||||
|
packetToSend->CommandClass = COMMAND_CLASS_MASS_STORAGE | COMMAND_CLASS_DATA_FLAG;
|
||||||
|
packetToSend->Command = COMMAND_MSC_READ;
|
||||||
|
return Downstream_TransmitPacket(packetToSend);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_MASS_STORAGE_WRITES_PERMITTED
|
||||||
|
//Used by USB MSC host driver
|
||||||
|
StatusTypeDef Downstream_MSC_GetStreamDataPacket(DownstreamMSCCallbackPacketTypeDef callback)
|
||||||
|
{
|
||||||
|
GetStreamDataCallback = callback;
|
||||||
|
|
||||||
|
if (ReadStreamBusy != 0)
|
||||||
|
{
|
||||||
|
return HAL_OK;
|
||||||
|
}
|
||||||
|
ReadStreamBusy = 1;
|
||||||
|
|
||||||
|
if (ReadStreamPacket && GetStreamDataCallback) //Do we have a stored packet and an address to send it?
|
||||||
|
{
|
||||||
|
Downstream_MSC_GetStreamDataPacketCallback(ReadStreamPacket); //Send it now!
|
||||||
|
ReadStreamPacket = NULL;
|
||||||
|
return HAL_OK; //Our callback will call us again, so we don't need to get a packet in this case.
|
||||||
|
}
|
||||||
|
return Downstream_ReceivePacket(Downstream_MSC_GetStreamDataPacketCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Downstream_MSC_GetStreamDataPacketCallback(DownstreamPacketTypeDef* receivedPacket)
|
||||||
|
{
|
||||||
|
uint16_t dataLength8;
|
||||||
|
|
||||||
|
ReadStreamBusy = 0;
|
||||||
|
if (GetStreamDataCallback == NULL)
|
||||||
|
{
|
||||||
|
ReadStreamPacket = receivedPacket; //We used up our callback already, so save this one for later.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dataLength8 = (receivedPacket->Length16 - DOWNSTREAM_PACKET_HEADER_LEN_16) * 2;
|
||||||
|
|
||||||
|
if ((receivedPacket->CommandClass != (COMMAND_CLASS_MASS_STORAGE | COMMAND_CLASS_DATA_FLAG)) || //Must be MSC command with data flag set
|
||||||
|
(receivedPacket->Command != COMMAND_MSC_WRITE) || //Must be write command
|
||||||
|
(receivedPacket->Length16 <= DOWNSTREAM_PACKET_HEADER_LEN_16) || //Should be at least one data byte in the packet.
|
||||||
|
(dataLength8 > ByteCount))
|
||||||
|
{
|
||||||
|
Downstream_PacketProcessor_FreakOut();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteCount -= dataLength8;
|
||||||
|
GetStreamDataCallback(receivedPacket, dataLength8); //usb_msc_scsi will use this packet, so don't release now
|
||||||
|
if (ByteCount > 0)
|
||||||
|
{
|
||||||
|
Downstream_MSC_GetStreamDataPacket(NULL); //Try to get the next packet now, before USB asks for it
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif //#ifdef CONFIG_MASS_STORAGE_WRITES_PERMITTED
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void Downstream_MSC_PacketProcessor_Disconnect(DownstreamPacketTypeDef* receivedPacket)
|
||||||
|
{
|
||||||
|
Downstream_ReleasePacket(receivedPacket);
|
||||||
|
|
||||||
|
USBH_MSC_StartStopUnit(&hUsbHostFS,
|
||||||
|
MSC_FIXED_LUN,
|
||||||
|
MSC_START_STOP_EJECT_FLAG,
|
||||||
|
Downstream_MSC_PacketProcessor_DisconnectCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void Downstream_MSC_PacketProcessor_DisconnectCallback(USBH_StatusTypeDef result)
|
||||||
|
{
|
||||||
|
DownstreamPacketTypeDef* freePacket;
|
||||||
|
|
||||||
|
if (result == USBH_OK)
|
||||||
|
{
|
||||||
|
freePacket = Downstream_GetFreePacketImmediately();
|
||||||
|
freePacket->CommandClass = COMMAND_CLASS_MASS_STORAGE;
|
||||||
|
freePacket->Command = COMMAND_MSC_DISCONNECT;
|
||||||
|
freePacket->Length16 = DOWNSTREAM_PACKET_HEADER_LEN_16;
|
||||||
|
Downstream_PacketProcessor_ClassReply(freePacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //#ifdef CONFIG_MASS_STORAGE_ENABLED
|
||||||
|
|
@ -0,0 +1,475 @@
|
|||||||
|
/*
|
||||||
|
* upstream_spi.c
|
||||||
|
*
|
||||||
|
* Created on: 24/07/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 "downstream_interface_def.h"
|
||||||
|
#include "downstream_spi.h"
|
||||||
|
#include "downstream_statemachine.h"
|
||||||
|
#include "gd32vf103.h"
|
||||||
|
#include "gd32vf103v_eval.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
|
||||||
|
|
||||||
|
uint32_t TemporaryIncomingPacketLength = 0;
|
||||||
|
uint8_t SpiInterruptCompleted = 0;
|
||||||
|
|
||||||
|
|
||||||
|
StatusTypeDef Downstream_CheckPreparePacketReception(void);
|
||||||
|
void Downstream_PrepareReceivePacketSize(DownstreamPacketTypeDef* freePacket);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Downstream_InitSPI(void)
|
||||||
|
{
|
||||||
|
DownstreamPacket0.Busy = NOT_BUSY;
|
||||||
|
DownstreamPacket1.Busy = NOT_BUSY;
|
||||||
|
|
||||||
|
rcu_periph_clock_enable(RCU_GPIOA);
|
||||||
|
rcu_periph_clock_enable(RCU_AF);
|
||||||
|
rcu_periph_clock_enable(RCU_DMA0);
|
||||||
|
rcu_periph_clock_enable(RCU_SPI0);
|
||||||
|
|
||||||
|
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5 |GPIO_PIN_6| GPIO_PIN_7);
|
||||||
|
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_4);
|
||||||
|
|
||||||
|
dma_parameter_struct dma_init_struct;
|
||||||
|
|
||||||
|
/* SPI0 transmit dma config:DMA0-DMA_CH2 */
|
||||||
|
dma_deinit(DMA0, DMA_CH2);
|
||||||
|
dma_struct_para_init(&dma_init_struct);
|
||||||
|
|
||||||
|
dma_init_struct.periph_addr = (uint32_t)&SPI_DATA(SPI0);
|
||||||
|
dma_init_struct.memory_addr = (uint32_t)spi0_send_array;
|
||||||
|
dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
|
||||||
|
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
|
||||||
|
dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
|
||||||
|
dma_init_struct.priority = DMA_PRIORITY_LOW;
|
||||||
|
dma_init_struct.number = ARRAYSIZE;
|
||||||
|
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
|
||||||
|
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
|
||||||
|
dma_init(DMA0, DMA_CH2, &dma_init_struct);
|
||||||
|
/* configure DMA mode */
|
||||||
|
dma_circulation_disable(DMA0, DMA_CH2);
|
||||||
|
dma_memory_to_memory_disable(DMA0, DMA_CH2);
|
||||||
|
dma_interrupt_enable(DMA0, DMA_CH2, DMA_INT_FTF);
|
||||||
|
|
||||||
|
/* SPI0 receive dma config:DMA0-DMA_CH1 */
|
||||||
|
dma_deinit(DMA0, DMA_CH1);
|
||||||
|
dma_init_struct.periph_addr = (uint32_t)&SPI_DATA(SPI0);
|
||||||
|
dma_init_struct.memory_addr = (uint32_t)spi0_receive_array;
|
||||||
|
dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
|
||||||
|
dma_init_struct.priority = DMA_PRIORITY_HIGH;
|
||||||
|
dma_init(DMA0, DMA_CH1, &dma_init_struct);
|
||||||
|
/* configure DMA mode */
|
||||||
|
dma_circulation_disable(DMA0, DMA_CH1);
|
||||||
|
dma_memory_to_memory_disable(DMA0, DMA_CH1);
|
||||||
|
dma_interrupt_enable(DMA0, DMA_CH1, DMA_INT_FTF);
|
||||||
|
|
||||||
|
spi_parameter_struct spi_init_struct;
|
||||||
|
/* deinitilize SPI and the parameters */
|
||||||
|
spi_i2s_deinit(SPI0);
|
||||||
|
spi_struct_para_init(&spi_init_struct);
|
||||||
|
|
||||||
|
/* SPI0 parameter config */
|
||||||
|
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
|
||||||
|
spi_init_struct.device_mode = SPI_SLAVE;
|
||||||
|
spi_init_struct.frame_size = SPI_FRAMESIZE_16BIT;
|
||||||
|
spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;
|
||||||
|
spi_init_struct.nss = SPI_NSS_SOFT;
|
||||||
|
spi_init_struct.prescale = SPI_PSC_2;
|
||||||
|
spi_init_struct.endian = SPI_ENDIAN_LSB;
|
||||||
|
|
||||||
|
spi_init(SPI0, &spi_init_struct);
|
||||||
|
spi_crc_polynomial_set(SPI0,7);
|
||||||
|
spi_enable(SPI0);
|
||||||
|
|
||||||
|
/* DMA channel enable */
|
||||||
|
dma_channel_enable(DMA0, DMA_CH1);
|
||||||
|
dma_channel_enable(DMA0, DMA_CH2);
|
||||||
|
|
||||||
|
/* SPI DMA enable */
|
||||||
|
spi_dma_enable(SPI0, SPI_DMA_TRANSMIT);
|
||||||
|
spi_dma_enable(SPI0, SPI_DMA_RECEIVE);
|
||||||
|
|
||||||
|
eclic_irq_enable(DMA1_Channel0_IRQn,1,0);
|
||||||
|
spi_i2s_interrupt_enable(SPI0, SPI_I2S_INT_TBE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Used by downstream state machine and USB host classes.
|
||||||
|
StatusTypeDef Downstream_GetFreePacket(FreePacketCallbackTypeDef callback)
|
||||||
|
{
|
||||||
|
if (DownstreamInterfaceState >= DOWNSTREAM_INTERFACE_ERROR)
|
||||||
|
{
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Do we already have a queued callback?
|
||||||
|
if (PendingFreePacketCallback != NULL)
|
||||||
|
{
|
||||||
|
DOWNSTREAM_SPI_FREAKOUT;
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check if there is a free buffer now
|
||||||
|
if (DownstreamPacket0.Busy == NOT_BUSY)
|
||||||
|
{
|
||||||
|
DownstreamPacket0.Busy = BUSY;
|
||||||
|
callback(&DownstreamPacket0);
|
||||||
|
return STATUS_OK;
|
||||||
|
}
|
||||||
|
if (DownstreamPacket1.Busy == NOT_BUSY)
|
||||||
|
{
|
||||||
|
DownstreamPacket1.Busy = BUSY;
|
||||||
|
callback(&DownstreamPacket1);
|
||||||
|
return STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Otherwise save requested address for when a buffer becomes free in the future
|
||||||
|
PendingFreePacketCallback = callback;
|
||||||
|
return STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
DownstreamPacketTypeDef* Downstream_GetFreePacketImmediately(void)
|
||||||
|
{
|
||||||
|
if (DownstreamInterfaceState >= DOWNSTREAM_INTERFACE_ERROR)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//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:
|
||||||
|
DOWNSTREAM_SPI_FREAKOUT;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
StatusTypeDef Downstream_ReceivePacket(SpiPacketReceivedCallbackTypeDef callback)
|
||||||
|
{
|
||||||
|
if (DownstreamInterfaceState >= DOWNSTREAM_INTERFACE_ERROR)
|
||||||
|
{
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((DownstreamInterfaceState == DOWNSTREAM_INTERFACE_RX_SIZE_WAIT) ||
|
||||||
|
(DownstreamInterfaceState == DOWNSTREAM_INTERFACE_RX_PACKET_WAIT) ||
|
||||||
|
(ReceivePacketCallback != NULL))
|
||||||
|
{
|
||||||
|
DOWNSTREAM_SPI_FREAKOUT;
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReceivePacketCallback = callback;
|
||||||
|
return Downstream_CheckPreparePacketReception();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Internal use only
|
||||||
|
StatusTypeDef Downstream_CheckPreparePacketReception(void)
|
||||||
|
{
|
||||||
|
if (DownstreamInterfaceState >= DOWNSTREAM_INTERFACE_ERROR)
|
||||||
|
{
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DownstreamInterfaceState == DOWNSTREAM_INTERFACE_IDLE)
|
||||||
|
{
|
||||||
|
DownstreamInterfaceState = DOWNSTREAM_INTERFACE_RX_SIZE_WAIT;
|
||||||
|
return Downstream_GetFreePacket(Downstream_PrepareReceivePacketSize);
|
||||||
|
}
|
||||||
|
return STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Internal use only
|
||||||
|
void Downstream_PrepareReceivePacketSize(DownstreamPacketTypeDef* freePacket)
|
||||||
|
{
|
||||||
|
if (DownstreamInterfaceState >= DOWNSTREAM_INTERFACE_ERROR)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DownstreamInterfaceState != DOWNSTREAM_INTERFACE_RX_SIZE_WAIT)
|
||||||
|
{
|
||||||
|
DOWNSTREAM_SPI_FREAKOUT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CurrentWorkingPacket = freePacket;
|
||||||
|
CurrentWorkingPacket->Length16 = 0;
|
||||||
|
if (HAL_SPI_TransmitReceive_DMA(&Hspi1,
|
||||||
|
(uint8_t*)&CurrentWorkingPacket->Length16,
|
||||||
|
(uint8_t*)&CurrentWorkingPacket->Length16,
|
||||||
|
2) != HAL_OK) //We only need to read one word, but the peripheral library freaks out...
|
||||||
|
{
|
||||||
|
DOWNSTREAM_SPI_FREAKOUT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UPSTREAM_TX_REQUEST_ASSERT;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//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.
|
||||||
|
StatusTypeDef Downstream_TransmitPacket(DownstreamPacketTypeDef* packetToWrite)
|
||||||
|
{
|
||||||
|
if (DownstreamInterfaceState >= DOWNSTREAM_INTERFACE_ERROR)
|
||||||
|
{
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Sanity checks
|
||||||
|
if ((packetToWrite != &DownstreamPacket0) &&
|
||||||
|
(packetToWrite != &DownstreamPacket1))
|
||||||
|
{
|
||||||
|
DOWNSTREAM_SPI_FREAKOUT;
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
if ((packetToWrite->Busy != BUSY) ||
|
||||||
|
(packetToWrite->Length16 < DOWNSTREAM_PACKET_LEN_MIN_16) ||
|
||||||
|
(packetToWrite->Length16 > DOWNSTREAM_PACKET_LEN_16))
|
||||||
|
{
|
||||||
|
DOWNSTREAM_SPI_FREAKOUT;
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
if (NextTxPacket != NULL)
|
||||||
|
{
|
||||||
|
DOWNSTREAM_SPI_FREAKOUT;
|
||||||
|
return STATUS_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->Length16,
|
||||||
|
(uint8_t*)&TemporaryIncomingPacketLength,
|
||||||
|
2) != HAL_OK) //We only need to write one word, but the peripheral library freaks out...
|
||||||
|
{
|
||||||
|
DOWNSTREAM_SPI_FREAKOUT;
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
UPSTREAM_TX_REQUEST_ASSERT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
DOWNSTREAM_SPI_FREAKOUT;
|
||||||
|
return STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Do stuff at main loop priority after SPI transaction is complete
|
||||||
|
void Downstream_SPIProcess(void)
|
||||||
|
{
|
||||||
|
SpiPacketReceivedCallbackTypeDef tempPacketCallback;
|
||||||
|
|
||||||
|
if (SpiInterruptCompleted == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SpiInterruptCompleted = 0;
|
||||||
|
|
||||||
|
if (DownstreamInterfaceState >= DOWNSTREAM_INTERFACE_ERROR)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Finished transmitting packet size
|
||||||
|
if (DownstreamInterfaceState == DOWNSTREAM_INTERFACE_TX_SIZE_WAIT)
|
||||||
|
{
|
||||||
|
if ((uint16_t)TemporaryIncomingPacketLength != 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
DownstreamInterfaceState = DOWNSTREAM_INTERFACE_TX_PACKET_WAIT;
|
||||||
|
if (HAL_SPI_TransmitReceive_DMA(&Hspi1,
|
||||||
|
&CurrentWorkingPacket->CommandClass,
|
||||||
|
&CurrentWorkingPacket->CommandClass,
|
||||||
|
((CurrentWorkingPacket->Length16 < 2) ? 2 : CurrentWorkingPacket->Length16)) != HAL_OK)
|
||||||
|
{
|
||||||
|
DOWNSTREAM_SPI_FREAKOUT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UPSTREAM_TX_REQUEST_ASSERT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Finished transmitting packet body
|
||||||
|
if (DownstreamInterfaceState == DOWNSTREAM_INTERFACE_TX_PACKET_WAIT)
|
||||||
|
{
|
||||||
|
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->Length16,
|
||||||
|
(uint8_t*)&TemporaryIncomingPacketLength,
|
||||||
|
2) != HAL_OK) //We only need to write one word, but the peripheral library freaks out...
|
||||||
|
{
|
||||||
|
DOWNSTREAM_SPI_FREAKOUT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UPSTREAM_TX_REQUEST_ASSERT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DownstreamInterfaceState = DOWNSTREAM_INTERFACE_IDLE;
|
||||||
|
if (ReceivePacketCallback != NULL)
|
||||||
|
{
|
||||||
|
Downstream_CheckPreparePacketReception();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (DownstreamInterfaceState == DOWNSTREAM_INTERFACE_RX_SIZE_WAIT)
|
||||||
|
{
|
||||||
|
if ((CurrentWorkingPacket->Length16 < DOWNSTREAM_PACKET_LEN_MIN_16) ||
|
||||||
|
(CurrentWorkingPacket->Length16 > DOWNSTREAM_PACKET_LEN_16))
|
||||||
|
{
|
||||||
|
DOWNSTREAM_SPI_FREAKOUT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DownstreamInterfaceState = DOWNSTREAM_INTERFACE_RX_PACKET_WAIT;
|
||||||
|
if (HAL_SPI_TransmitReceive_DMA(&Hspi1,
|
||||||
|
&CurrentWorkingPacket->CommandClass,
|
||||||
|
&CurrentWorkingPacket->CommandClass,
|
||||||
|
((CurrentWorkingPacket->Length16 < 2) ? 2 : CurrentWorkingPacket->Length16)) != HAL_OK)
|
||||||
|
{
|
||||||
|
DOWNSTREAM_SPI_FREAKOUT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UPSTREAM_TX_REQUEST_ASSERT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DownstreamInterfaceState == DOWNSTREAM_INTERFACE_RX_PACKET_WAIT)
|
||||||
|
{
|
||||||
|
DownstreamInterfaceState = DOWNSTREAM_INTERFACE_IDLE;
|
||||||
|
if (ReceivePacketCallback == NULL)
|
||||||
|
{
|
||||||
|
DOWNSTREAM_SPI_FREAKOUT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//case default:
|
||||||
|
DOWNSTREAM_SPI_FREAKOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//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.
|
||||||
|
//We also TxRx our packet body because the SPI silicon is buggy at the end of
|
||||||
|
//a transmit-only DMA transfer with CRC! (it does not clear RXNE flag on request)
|
||||||
|
void DMA_SPI_TxRxCpltCallback(void)
|
||||||
|
{
|
||||||
|
UPSTREAM_TX_REQUEST_DEASSERT;
|
||||||
|
SpiInterruptCompleted = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Something bad happened! Possibly CRC error...
|
||||||
|
/*void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
|
||||||
|
{
|
||||||
|
DOWNSTREAM_SPI_FREAKOUT;
|
||||||
|
}*/
|
||||||
|
|
Loading…
Reference in new issue