/*
 * upstream_msc_mock.c
 *
 *  Created on: 17/07/2015
 *      Author: Robert Fisk
 *
 *      This file replaces upstream_msc.c to allow operational testing of Upstream,
 *      without Downstream in place and communicating over SPI.
 *      It still attempts to write downstream packets out the SPI port.
 *
 * 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_msc.h>
#include <upstream_spi.h>
#include "stm32f4xx_hal.h"


//Stuff we need to save for our callbacks to use:
UpstreamMSCCallbackTypeDef              TestReadyCallback;
UpstreamMSCCallbackUintPacketTypeDef    GetCapacityCallback;
UpstreamMSCCallbackPacketTypeDef        GetStreamDataCallback;
uint32_t                                ByteCount;
UpstreamPacketTypeDef*                  ReadStreamPacket;
uint8_t                                 ReadStreamBusy;


static void Upstream_MSC_TestReadyReplyCallback(UpstreamPacketTypeDef* replyPacket);
static void Upstream_MSC_GetCapacityReplyCallback(UpstreamPacketTypeDef* replyPacket);
static void Upstream_MSC_GetStreamDataPacketCallback(UpstreamPacketTypeDef* replyPacket);
static void Upstream_MSC_BeginWriteReplyCallback(UpstreamPacketTypeDef* replyPacket);



HAL_StatusTypeDef Upstream_MSC_TestReady(UpstreamMSCCallbackTypeDef callback)
{
    UpstreamPacketTypeDef* freePacket;
    HAL_StatusTypeDef tempResult;

    TestReadyCallback = callback;
    freePacket = Upstream_GetFreePacketImmediately();

    freePacket->Length = UPSTREAM_PACKET_HEADER_LEN;
    freePacket->CommandClass = COMMAND_CLASS_MASS_STORAGE;
    freePacket->Command = COMMAND_MSC_TEST_UNIT_READY;
    tempResult = Upstream_TransmitPacket(freePacket);
    if (tempResult != HAL_OK)
    {
        return tempResult;
    }
    //return Upstream_GetPacket(Upstream_MSC_TestReadyReplyCallback);
    return Upstream_GetFreePacket(Upstream_MSC_TestReadyReplyCallback);
}

void Upstream_MSC_TestReadyReplyCallback(UpstreamPacketTypeDef* replyPacket)
{
//  if ((replyPacket->Length != (UPSTREAM_PACKET_HEADER_LEN + 1)) ||
//      (replyPacket->CommandClass & COMMAND_CLASS_DATA_FLAG) ||
//      (replyPacket->Data[0] != HAL_OK))
//  {
//      Upstream_ReleasePacket(replyPacket);
//      TestReadyCallback(HAL_ERROR);
//      return;
//  }

        Upstream_ReleasePacket(replyPacket);
        TestReadyCallback(HAL_OK);
}



HAL_StatusTypeDef Upstream_MSC_GetCapacity(UpstreamMSCCallbackUintPacketTypeDef callback)
{
    UpstreamPacketTypeDef* freePacket;
    HAL_StatusTypeDef tempResult;

    GetCapacityCallback = callback;
    freePacket = Upstream_GetFreePacketImmediately();

    freePacket->Length = UPSTREAM_PACKET_HEADER_LEN;
    freePacket->CommandClass = COMMAND_CLASS_MASS_STORAGE;
    freePacket->Command = COMMAND_MSC_GET_CAPACITY;
    tempResult = Upstream_TransmitPacket(freePacket);
    if (tempResult != HAL_OK)
    {
        return tempResult;
    }
    //return Upstream_GetPacket(Upstream_MSC_GetCapacityReplyCallback);
    return Upstream_GetFreePacket(Upstream_MSC_GetCapacityReplyCallback);
}


void Upstream_MSC_GetCapacityReplyCallback(UpstreamPacketTypeDef* replyPacket)
{
    uint32_t uint[2];

//  if ((replyPacket->Length != (UPSTREAM_PACKET_HEADER_LEN + 8) ||
//      (replyPacket->CommandClass & COMMAND_CLASS_DATA_FLAG)))
//  {
//      GetCapacityCallback(HAL_ERROR, NULL, NULL);
//  }

    *(uint32_t*)&(replyPacket->Data[0]) = 262144;       //* 512B = 128MB
    *(uint32_t*)&(replyPacket->Data[4]) = 512;

    uint[0] = *(uint32_t*)&(replyPacket->Data[0]);
    uint[1] = *(uint32_t*)&(replyPacket->Data[4]);
    GetCapacityCallback(HAL_OK, uint, replyPacket);     //usb_msc_scsi will use this packet, so don't release now
}



HAL_StatusTypeDef Upstream_MSC_BeginRead(UpstreamMSCCallbackTypeDef callback,
                                         uint64_t readBlockStart,
                                         uint32_t readBlockCount,
                                         uint32_t readByteCount)
{
    UpstreamPacketTypeDef* freePacket;
    HAL_StatusTypeDef tempResult;

    ReadStreamPacket = NULL;            //Prepare for GetStreamDataPacket's use
    ReadStreamBusy = 0;

    TestReadyCallback = callback;
    ByteCount = readByteCount;
    freePacket = Upstream_GetFreePacketImmediately();

    freePacket->Length = UPSTREAM_PACKET_HEADER_LEN + (4 * 3);
    freePacket->CommandClass = COMMAND_CLASS_MASS_STORAGE;
    freePacket->Command = COMMAND_MSC_BEGIN_READ;
    *(uint64_t*)&(freePacket->Data[0]) = readBlockStart;
    *(uint32_t*)&(freePacket->Data[8]) = readBlockCount;

    tempResult = Upstream_TransmitPacket(freePacket);
    if (tempResult != HAL_OK)
    {
        TestReadyCallback(tempResult);
    }
    //return Upstream_GetPacket(Upstream_MSC_TestReadyReplyCallback);   //Re-use TestReadyReplyCallback because it does exactly what we want!
    return Upstream_GetFreePacket(Upstream_MSC_TestReadyReplyCallback);
}



HAL_StatusTypeDef Upstream_MSC_GetStreamDataPacket(UpstreamMSCCallbackPacketTypeDef 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?
    {
        Upstream_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 Upstream_GetPacket(Upstream_MSC_GetStreamDataPacketCallback);
    return Upstream_GetFreePacket(Upstream_MSC_GetStreamDataPacketCallback);
}


void Upstream_MSC_GetStreamDataPacketCallback(UpstreamPacketTypeDef* replyPacket)
{
    uint16_t dataLength;

    ReadStreamBusy = 0;
    if (GetStreamDataCallback == NULL)
    {
        ReadStreamPacket = replyPacket;     //We used up our callback already, so save this one for later.
        return;
    }

//  if (((replyPacket->CommandClass & COMMAND_CLASS_DATA_FLAG) == 0) ||     //Any 'command' reply (as opposed to 'data' reply) is an automatic fail here
//       (replyPacket->Length <= UPSTREAM_PACKET_HEADER_LEN) ||         //Should be at least one data byte in the reply.
//       (replyPacket->Length > ByteCount))
//  {
//      GetStreamDataCallback(HAL_ERROR, NULL);
//      return;
//  }

    replyPacket->Length = MIN((ByteCount + UPSTREAM_PACKET_HEADER_LEN), (MSC_MEDIA_PACKET + UPSTREAM_PACKET_HEADER_LEN));

    dataLength = replyPacket->Length - UPSTREAM_PACKET_HEADER_LEN;
    ByteCount -= dataLength;
    GetStreamDataCallback(HAL_OK, replyPacket, dataLength); //usb_msc_scsi will use this packet, so don't release now
    if (ByteCount > 0)
    {
        Upstream_MSC_GetStreamDataPacket(NULL); //Try to get the next packet now, before USB asks for it
    }
}



HAL_StatusTypeDef Upstream_MSC_BeginWrite(UpstreamMSCCallbackTypeDef callback,
                                          uint64_t readBlockStart,
                                          uint32_t readBlockCount)
{
    UpstreamPacketTypeDef* freePacket;
    HAL_StatusTypeDef tempResult;

    TestReadyCallback = callback;
    freePacket = Upstream_GetFreePacketImmediately();

    freePacket->Length = UPSTREAM_PACKET_HEADER_LEN + (4 * 3);
    freePacket->CommandClass = COMMAND_CLASS_MASS_STORAGE;
    freePacket->Command = COMMAND_MSC_BEGIN_WRITE;
    *(uint64_t*)&(freePacket->Data[0]) = readBlockStart;
    *(uint32_t*)&(freePacket->Data[8]) = readBlockCount;

    tempResult = Upstream_TransmitPacket(freePacket);
    if (tempResult != HAL_OK)
    {
        TestReadyCallback(tempResult);
    }
    //return Upstream_GetPacket(Upstream_MSC_BeginWriteReplyCallback);
    return Upstream_GetFreePacket(Upstream_MSC_BeginWriteReplyCallback);
}


void Upstream_MSC_BeginWriteReplyCallback(UpstreamPacketTypeDef* replyPacket)
{
//  if ((replyPacket->Length != (UPSTREAM_PACKET_HEADER_LEN + 1)) ||
//      (replyPacket->CommandClass & COMMAND_CLASS_DATA_FLAG) ||
//      ((replyPacket->Data[0] != HAL_OK) && (replyPacket->Data[0] != HAL_BUSY)))
//  {
//      Upstream_ReleasePacket(replyPacket);
//      TestReadyCallback(HAL_ERROR);
//      return;
//  }

    Upstream_ReleasePacket(replyPacket);
    TestReadyCallback(replyPacket->Data[0]);
}



HAL_StatusTypeDef Upstream_MSC_PutStreamDataPacket(UpstreamPacketTypeDef* packetToSend,
                                                   uint32_t dataLength)
{
    packetToSend->Length = dataLength + UPSTREAM_PACKET_HEADER_LEN;
    packetToSend->CommandClass = COMMAND_CLASS_MASS_STORAGE | COMMAND_CLASS_DATA_FLAG;
    packetToSend->Command = COMMAND_MSC_BEGIN_WRITE;
    return Upstream_TransmitPacket(packetToSend);
}

