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.
359 lines
12 KiB
359 lines
12 KiB
/*
|
|
* 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 "build_config.h"
|
|
#include "led.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
|
|
HAL_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
|
|
HAL_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
|
|
|