
//------------------------------------------------------------------------------
// 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* myDataPlaneIn = &_myDataPlane;
si32UsbEndpointObject* myDataPlaneInEp = NULL;

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

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

static bool _received0 = YES;
static bool _received1 = YES;

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

static bool _transmitted0 = NO;
static bool _transmitted1 = NO;

//------------------------------------------------------------------------------
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 i2s, rx buf1 on i2s if ready, tx buf0 on usb if ready, else defer
static void rx0_callback(si32PortalObjectResultType result,
                        uint32 actual,
                        void* context0,
                        void* context1)
{
  //fputc('{', stdout);
  IF (result == SI32_PORTAL_OBJECT_RESULT_ABORTED)
  {
    printf("rx0 aborted\n");
    return;
  }
  si32Assert(result == SI32_PORTAL_OBJECT_RESULT_SUCCESS);
  _received0 = YES;
  if (_transmitted1)
  {
    //rx1();  // <-- takes too long
    _receive1 = YES;
  }
  _transmit0 = YES;
  si32PseudoThreadObject_resume(myDataPlaneIn,
                                SI32_PSEUDO_THREAD_PRIORITY_HIGH);
}

//------------------------------------------------------------------------------
// txd buf0 on usb, defer rx buf0 on i2s
static void tx0_callback(obj* delegate,
                        void* context,
                        uint32 tag,
                        obj* buffer,
                        si32IoRequestResultType result,
                        uint32 residue)
{
  //fputc('(', stdout);
  IF (result == SI32_IO_REQUEST_RESULT_CANCELLED)
  {
    printf("tx0 cancelled\n");
    return;
  }
  // TBD remove if the intermediate CB gambit doesn't pay off.
  if (result == SI32_IO_REQUEST_RESULT_BUFFERED)
  {
    return;
  }
  //si32Assert((result == SI32_IO_REQUEST_RESULT_SUCCESS)
  //        || (result == SI32_IO_REQUEST_RESULT_IN_PROGRESS));
  _transmitted0 = YES;

  if (_received1)
  {
    _receive0 = YES;
    si32PseudoThreadObject_resume(myDataPlaneIn,
                                  SI32_PSEUDO_THREAD_PRIORITY_HIGH);
  }
}

//------------------------------------------------------------------------------
// rxd buf1 on i2s, rx buf0 on i2s if ready, tx buf1 on usb if ready, else defer
static void rx1_callback(si32PortalObjectResultType result,
                        uint32 actual,
                        void* context0,
                        void* context1)
{
  //fputc('}', stdout);
  IF (result == SI32_PORTAL_OBJECT_RESULT_ABORTED)
  {
    printf("rx1 aborted\n");
    return;
  }
  si32Assert(result == SI32_PORTAL_OBJECT_RESULT_SUCCESS);
  _received1 = YES;
  if (_transmitted0)
  {
    //rx0();  // <-- takes too long
    _receive0 = YES;
  }
  _transmit1 = YES;
  si32PseudoThreadObject_resume(myDataPlaneIn,
                                SI32_PSEUDO_THREAD_PRIORITY_HIGH);
}

//------------------------------------------------------------------------------
// txd buf1 on usb, defer rx buf1 on i2s
static void tx1_callback(obj* delegate,
                        void* context,
                        uint32 tag,
                        obj* buffer,
                        si32IoRequestResultType result,
                        uint32 residue)
{
  //fputc(')', stdout);
  IF (result == SI32_IO_REQUEST_RESULT_CANCELLED)
  {
    printf("tx1 cancelled\n");
    return;
  }
  // TBD remove if the intermediate CB gambit doesn't pay off.
  if (result == SI32_IO_REQUEST_RESULT_BUFFERED)
  {
    return;
  }
  //si32Assert((result == SI32_IO_REQUEST_RESULT_SUCCESS)
  //        || (result == SI32_IO_REQUEST_RESULT_IN_PROGRESS));
  _transmitted1 = YES;

  if (_received0)
  {
     _receive1 = YES;
    si32PseudoThreadObject_resume(myDataPlaneIn,
                                  SI32_PSEUDO_THREAD_PRIORITY_HIGH);
  }
}

//------------------------------------------------------------------------------
static void rx0()
{
  if (!myDataPlaneInEp)
  {
    return;
  }
  si32PortalObjectResultType result = SI32_PORTAL_OBJECT_RESULT_ERROR_BUS;
   //probeHi(1);
  _received0 = NO;
  result = si32IisAPortalObject_read_async(my_iis0_portal,
                                           buffer0,
                                           96,
                                           rx0_callback,
                                           NULL,
                                           NULL);
  si32Assert((result == SI32_PORTAL_OBJECT_RESULT_IN_PROGRESS)
          || (result == SI32_PORTAL_OBJECT_RESULT_SUCCESS));
}

//------------------------------------------------------------------------------
static void tx0()
{
  if (!myDataPlaneInEp)
  {
    return;
  }
  si32IoRequestTagType rqtag = SI32_IO_REQUEST_TAG_ERROR;
  //probeHi(2);
  _transmitted0 = NO;
  rqtag = si32UsbEndpointObject_transmit(myDataPlaneInEp,
                                         buffer0,
                                         96,
                                         tx0_callback,
                                         NULL,
                                         NULL);
  si32Assert(rqtag != SI32_IO_REQUEST_TAG_ERROR);
}

//------------------------------------------------------------------------------
static void rx1()
{
  if (!myDataPlaneInEp)
  {
    return;
  }
  si32PortalObjectResultType result = SI32_PORTAL_OBJECT_RESULT_ERROR_BUS;
  //probeHi(3);
  _received1 = NO;
  result = si32IisAPortalObject_read_async(my_iis0_portal,
                                           buffer1,
                                           96,
                                           rx1_callback,
                                           NULL,
                                           NULL);
  si32Assert((result == SI32_PORTAL_OBJECT_RESULT_IN_PROGRESS)
          || (result == SI32_PORTAL_OBJECT_RESULT_SUCCESS));
}

//------------------------------------------------------------------------------
static void tx1()
{
  if (!myDataPlaneInEp)
  {
    return;
  }
  si32IoRequestTagType rqtag = SI32_IO_REQUEST_TAG_ERROR;
  //probeHi(4);
  _transmitted1 = NO;
  rqtag = si32UsbEndpointObject_transmit(myDataPlaneInEp,
                                         buffer1,
                                         96,
                                         tx1_callback,
                                         NULL,
                                         NULL);
  si32Assert(rqtag != SI32_IO_REQUEST_TAG_ERROR);
}

//------------------------------------------------------------------------------
static
uint32 myDataPlaneFn()
{
  si32PseudoThread_begin();
  while (1)
  {
    printf("wait IN ep\n");
    si32PseudoThread_wait_until(myDataPlaneInEp);
    printf("gained IN ep\n");
    _transmitted1 = YES;
    rx0();
    // if the ep is lost, stop moving data.
    while (myDataPlaneInEp)
    {
      si32PseudoThread_yield();  // <-- without this, the wait never waits, and the loop is infinite, blocking.
      //si32PseudoThread_wait_until(_receive0 || _receive1 || _transmit0 || _transmit1 || !myDataPlaneInEp);
      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 IN ep\n");
  }
  si32PseudoThread_end();
}

#if 0
//------------------------------------------------------------------------------
static
void timer_callback(
  void* context0,
  void* context1)
{
}
#endif

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

//  // 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);

#if 0
  // start a timer that fires once per second.  Hang onto the tag in case there is
  // a need to stop the timer later on.
  si32TimerTagType tmrtag = si32SystickTimerObject_start(si32GetSystemTimer(),
                                               1000000,
                                               YES,
                                               timer_callback,
                                               NULL,
                                               NULL);
  si32Assert(tmrtag != SI32_TIMER_TAG_ERROR);
#endif

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

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