
//------------------------------------------------------------------------------
// Copyright (c) 2012 by Silicon Laboratories. 
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Silicon Laboratories End User 
// License Agreement which accompanies this distribution, and is available at
// http://developer.silabs.com/legal/version/v10/License_Agreement_v10.htm
// Original content and implementation provided by Silicon Laboratories.
//------------------------------------------------------------------------------
// library
#include <si32McuComponent.h>
#include <si32PseudOsComponent.h>
// hal
// application
#include "myApplication.h"
#include "myDataPlane.h"
#include "myI2S0.h"
#include "myUsbAudioDevice.h"

//------------------------------------------------------------------------------
static si32PseudoThreadObject _myDataPlane = si32PseudoThreadObject();
si32PseudoThreadObject* myDataPlaneOut = &_myDataPlane;
si32UsbEndpointObject* myDataPlaneOutEp = NULL;

static uint8 buffer0[192] = {0};
static uint8 buffer1[192] = {0};

static bool _receive0 = NO;
static bool _receive1 = NO;

static bool _received0 = NO;
static bool _received1 = NO;

static bool _transmit0 = NO;
static bool _transmit1 = NO;

static bool _transmitted0 = YES;
static bool _transmitted1 = YES;

//------------------------------------------------------------------------------
typedef struct myDataPlaneContextStruct
{
  si32TimerTagType tmrtag;
} myDataPlaneContextType;

// forward references
static void rx0(void);
static void tx0(void);
static void rx1(void);
static void tx1(void);

//------------------------------------------------------------------------------
// rxd buf0 on usb, defer tx buf0 on usb 
static void rx0_callback(obj* delegate,
                        void* context,
                        uint32 tag,
                        obj* buffer,
                        si32IoRequestResultType result,
                        uint32 residue)
{
  //fputc('{', stdout);
  IF (result == SI32_IO_REQUEST_RESULT_CANCELLED)
  {
    printf("rx0 cancelled\n");
    //return;
    // ^-- stream will not always restart if return here.
  }
  //si32Assert((result == SI32_IO_REQUEST_RESULT_SUCCESS)
  //        || (result == SI32_IO_REQUEST_RESULT_IN_PROGRESS));
  _received0 = YES;
  
  if (_transmitted0 && _transmitted1)
  {
    _transmit0 = YES;  
    si32PseudoThreadObject_resume(myDataPlaneOut,
                                  SI32_PSEUDO_THREAD_PRIORITY_HIGH);
  } 
}

//------------------------------------------------------------------------------
// txd buf0 on i2s, tx buf1 on i2s if ready, defer rx buf0 on usb
static void tx0_callback(si32PortalObjectResultType result,
                        uint32 actual,
                        void* context0,
                        void* context1)
{
  //fputc('(', stdout);
  IF (result == SI32_PORTAL_OBJECT_RESULT_ABORTED)
  {
    printf("tx0 aborted\n");
    return;
  }
  si32Assert(result == SI32_PORTAL_OBJECT_RESULT_SUCCESS);
  _transmitted0 = YES;
  if (_received1)
  {
    //_received1 = NO;  // <-- omit this ssignment to replay last buffers on dropped pkt.
    tx1();
  }
  _receive0 = YES;  
  si32PseudoThreadObject_resume(myDataPlaneOut,
                                SI32_PSEUDO_THREAD_PRIORITY_HIGH);
}

//------------------------------------------------------------------------------
// rxd buf1 on usb, defer tx buf1 on usb 
static void rx1_callback(obj* delegate,
                        void* context,
                        uint32 tag,
                        obj* buffer,
                        si32IoRequestResultType result,
                        uint32 residue)
{
  //fputc('}', stdout);
  IF (result == SI32_IO_REQUEST_RESULT_CANCELLED)
  {
    printf("rx1 cancelled\n");
    //return;
    // ^-- stream will not always restart if return here.
  }
  //si32Assert((result == SI32_IO_REQUEST_RESULT_SUCCESS)
  //        || (result == SI32_IO_REQUEST_RESULT_IN_PROGRESS));
  _received1 = YES;

  if (_transmitted0 && _transmitted1)
  {
    _transmit1 = YES;  
    si32PseudoThreadObject_resume(myDataPlaneOut,
                                  SI32_PSEUDO_THREAD_PRIORITY_HIGH);
  } 
}

//------------------------------------------------------------------------------
// txd buf1 on i2s, tx buf0 on i2s if ready, defer rx buf1 on usb
static void tx1_callback(si32PortalObjectResultType result,
                        uint32 actual,
                        void* context0,
                        void* context1)
{
  //fputc(')', stdout);
  IF (result == SI32_PORTAL_OBJECT_RESULT_ABORTED)
  {
    printf("tx1 aborted\n");
    return;
  }
  si32Assert(result == SI32_PORTAL_OBJECT_RESULT_SUCCESS);
  _transmitted1 = YES;
  if (_received0)
  {
    //_received0 = NO;  // <-- omit this ssignment to replay last buffers on dropped pkt.
    tx0();
  }
  _receive1 = YES;  
  si32PseudoThreadObject_resume(myDataPlaneOut,
                                SI32_PSEUDO_THREAD_PRIORITY_HIGH);
}

//------------------------------------------------------------------------------
static void rx0()
{
  if (!myDataPlaneOutEp)
  {
    return;
  }
  si32IoRequestTagType rqtag = SI32_IO_REQUEST_TAG_ERROR;
  //probeHi(1);
  _received0 = NO;
  rqtag = si32UsbEndpointObject_receive(myDataPlaneOutEp,
                                        buffer0,
                                        192,
                                        rx0_callback,
                                        NULL,
                                        NULL);
  si32Assert(rqtag != SI32_IO_REQUEST_TAG_ERROR);
}

//------------------------------------------------------------------------------
static void tx0()
{
  if (!myDataPlaneOutEp)
  {
    return;
  }
  si32PortalObjectResultType result = SI32_PORTAL_OBJECT_RESULT_ERROR_BUS;
  //probeHi(2);
  _transmitted0 = NO;
  result = si32IisAPortalObject_write_async(my_iis0_portal,
                                            buffer0,
                                            192,
                                            tx0_callback,
                                            NULL,
                                            NULL);
  si32Assert(result == SI32_PORTAL_OBJECT_RESULT_IN_PROGRESS);
}

//------------------------------------------------------------------------------
static void rx1()
{
  if (!myDataPlaneOutEp)
  {
    return;
  }
  si32IoRequestTagType rqtag = SI32_IO_REQUEST_TAG_ERROR;
  //probeHi(3);
  _received1 = NO;
  rqtag = si32UsbEndpointObject_receive(myDataPlaneOutEp,
                                        buffer1,
                                        192,
                                        rx1_callback,
                                        NULL,
                                        NULL);
  si32Assert(rqtag != SI32_IO_REQUEST_TAG_ERROR);
}

//------------------------------------------------------------------------------
static void tx1()
{
  if (!myDataPlaneOutEp)
  {
    return;
  }
  si32PortalObjectResultType result = SI32_PORTAL_OBJECT_RESULT_ERROR_BUS;
  //probeHi(4);
  _transmitted1 = NO;
  result = si32IisAPortalObject_write_async(my_iis0_portal,
                                            buffer1,
                                            192,
                                            tx1_callback,
                                            NULL,
                                            NULL);
  si32Assert(result == SI32_PORTAL_OBJECT_RESULT_IN_PROGRESS);
}

//------------------------------------------------------------------------------
static
uint32 myDataPlaneFn()
{
  si32PseudoThread_begin();
  while (1)
  {
    printf("wait OUT ep\n");
    si32PseudoThread_wait_until(myDataPlaneOutEp);
    printf("gained OUT ep\n");
    
    // prime the state machine.
    rx0();
    rx1();

    // if the ep is lost, stop moving data.
    while (myDataPlaneOutEp)
    {
      si32PseudoThread_yield();  // <-- without this, the wait never waits, and the loop is infinite, blocking.
      //si32PseudoThread_wait_until(_receive0 || _receive1 || _transmit0 || _transmit1 || !myDataPlaneOutEp);
      if (_receive0)
      {  
        _receive0 = NO;
        _receive1 = NO;
        rx0();
      }
      if (_receive1)
      {
        _receive0 = NO;
        _receive1 = NO;
        rx1();  
      }
      if (_transmit0)
      {
        _transmit0 = NO;
        _transmit1 = NO;
        tx0();
      }
      if (_transmit1)
      {
        _transmit0 = NO;
        _transmit1 = NO;
        tx1();  
      }
    }      
    printf("lost OUT ep\n");
  }
  si32PseudoThread_end();
}

//------------------------------------------------------------------------------
void myDataPlaneOut_initialize()
{
  // initialize a thread to run this test.
  si32PseudoThreadObject_initialize(myDataPlaneOut);

//  // allocate the data needed for thread local storage.
//  myDataPlaneContextType* tls = si32Base_allocate(si32Base_get_default_allocation_zone(),
//                                                  sizeof(myDataPlaneContextType),
//                                                  1);
//  si32Assert(tls);
//
//  // attach the tls to the thread.
//  si32PseudoThreadObject_set_thread_local_storage(myDataPlane,
//                                                  tls);

  // start the thread.
  si32PseudoThreadObject_run(myDataPlaneOut,
                             myDataPlaneFn);
}

//---eof------------------------------------------------------------------------
