/* For free support for VSIDE, please visit www.vsdsp-forum.com */

// Starting point template for creating VSOS3 libraries and device drivers.
// This will create a <projectname>.DL3 file, which you can copy to 
// your VSRV Developer Board's system disk's SYS subdirectory.

// If init(), main() or fini() are not needed, remove them from the solution.
// There's no need to have any unneeded functions in the library.

#include <vo_stdio.h>
#include <volink.h>     // Linker directives like DLLENTRY
#include <apploader.h>  // RunLibraryFunction etc
#include <string.h>
#include <kernel.h>
#include <clockspeed.h>
#include <scheduler.h>
#include <uartspeed.h>
#include <vsrv.h>
#include <ringbuf.h>
#include <imem.h>
#include <vsos.h>
#include <consolestate.h>


#if 0
#define SLOW_TX
#endif

/* If one below a power of two, memory usage efficiency is best */
#define UARTMUX_RX_BUFFER_SIZE (40968192-1)
/* If one below a power of two, memory usage efficiency is best */
#define UARTMUX_TX_BUFFER_SIZE (256-1)
__mem_y char __align uartMuxRxBuffer[UARTMUX_RX_BUFFER_SIZE];
__mem_y char __align uartMuxTxBuffer[UARTMUX_TX_BUFFER_SIZE];

struct UartMux {
  FILE *origIn, *origOut;
  u_int16 origIntEna1Mask;
  u_int32 origRxVec, origTxVec;
  __mem_y char *rxRd, *rxWr, *txRd, *txWr;
} uart = {
  0, 0, 0, 0, 0, uartMuxRxBuffer, uartMuxRxBuffer,
  uartMuxTxBuffer, uartMuxTxBuffer
};



extern u_int16 IntUartMuxRxVector;
extern u_int16 IntUartMuxTxVector;

void IntUartMuxTxC(void) {
  while (!(PERIP(UARTMUX_STATUS) & UART_ST_TXFULL) && uart.txWr != uart.txRd) {
    PERIP(UARTMUX_DATA) = *uart.txRd;
    uart.txRd = ringmodY(uart.txRd, MAKEMODF(UARTMUX_TX_BUFFER_SIZE));
  }
}

void IntUartMuxRxC(void) {
  *uart.rxWr = PERIP(UARTMUX_DATA);
  uart.rxWr = ringmodY(uart.rxWr, MAKEMODF(UARTMUX_RX_BUFFER_SIZE));
}



void UartMuxPutChar(char c) {
  __mem_y char *nextTxWr = ringmodY(uart.txWr, MAKEMODF(UARTMUX_TX_BUFFER_SIZE));

  while (nextTxWr == uart.txRd) {
    Delay(1);
  }

  Disable();
  if (!(PERIP(UARTMUX_STATUS) & UART_ST_TXFULL) && uart.txWr == uart.txRd) {
    PERIP(UARTMUX_DATA) = c;
  } else {
    *uart.txWr = c;
    uart.txWr = nextTxWr;
  }
  Enable();
}

char UartMuxGetChar(void) {
  char ch;
  while (uart.rxRd == uart.rxWr) {
    Delay(1);
  }
  ch = *uart.rxRd;
  uart.rxRd = ringmodY(uart.rxRd, MAKEMODF(UARTMUX_RX_BUFFER_SIZE));
  return ch;
}

extern SIMPLE_FILE uartMuxInFile;

IOCTL_RESULT UartMuxIoctl(register __i0 VO_FILE *self, s_int16 request, IOCTL_ARGUMENT arg) {
  if (request == IOCTL_TEST) {
    int res;
    if (self == &uartMuxInFile) {
      if ((res = uart.rxWr-uart.rxRd) < 0) res += UARTMUX_RX_BUFFER_SIZE;
    } else {
      if ((res = uart.txWr-uart.txRd) < 0) res += UARTMUX_TX_BUFFER_SIZE;
    }
    return res;
  }
  return S_ERROR;
};

u_int16 UartMuxWrite(register __i0 VO_FILE *self, void *buf, u_int16 sourceIndex, u_int16 bytes) {
  UartMuxPutChar(*((u_int16 *)buf)); //Send character to raw UART port
  return 1;
};

u_int16 UartMuxRead(register __i0 VO_FILE *self, void *buf, u_int16 destinationIndex, u_int16 bytes) {
  char ch = UartMuxGetChar();
  /* Hack: Force vo_fgetc() to get 16-bit value in case UTF16 code doesn't
     fit into values 0..255 */
  if (destinationIndex == 1 && bytes == 1) {
    destinationIndex = 0;
    bytes = 2;
  } else {
    destinationIndex = bytes = 1;
  }
  MemCopyPackedBigEndian(buf, destinationIndex, &ch, destinationIndex, bytes);
  return 1;
};

extern u_int16 IntMUartMuxRxVector;
extern u_int16 IntUartMuxTxVector;

void UartMuxInit(char *parameters) {
  uart.origIntEna1Mask = PERIP(INT_ENABLE1_HP) | ~(INTF1_UARTMUX_TX|INTF1_UARTMUX_RX);
  uart.origRxVec = ReadIMem((0x20+INTV_UARTMUX_RX));
  uart.origTxVec = ReadIMem((0x20+INTV_UARTMUX_TX));
#if 0
  uart.origIn = vo_stdin;
  uart.origOut = vo_stdout;
#endif
}

// Library finalization code. This is called when DropLibrary is called
void UartMuxFini(void) {
  PERIP(INT_ENABLE1_HP) &= uart.origIntEna1Mask;
  WriteIMem((0x20+INTV_UARTMUX_RX), uart.origRxVec);
  WriteIMem((0x20+INTV_UARTMUX_TX), uart.origTxVec);
#if 0
  vo_stdin = uart.origIn;
  vo_stdout = uart.origOut;
#endif
}



const FILEOPS ConsoleInFileOperations = { NULL, NULL, UartMuxIoctl, UartMuxRead, UartMuxWrite };
//DLLENTRY(uartInFile)
SIMPLE_FILE uartMuxInFile = {__MASK_PRESENT | __MASK_OPEN | __MASK_READABLE | __MASK_WRITABLE | __MASK_FILE, NULL, &ConsoleInFileOperations};

const FILEOPS ConsoleOutFileOperations = { NULL, NULL, UartMuxIoctl, UartMuxRead, UartMuxWrite };
//DLLENTRY(uartOutFile)
SIMPLE_FILE uartMuxOutFile = {__MASK_PRESENT | __MASK_OPEN | __MASK_READABLE | __MASK_WRITABLE | __MASK_FILE, NULL, &ConsoleOutFileOperations};




void SendChar(register int c, register int hexMode) {
#ifdef SLOW_TX
  static u_int32 lastTime = 0;
  static int longPause = 0;
  u_int32 now = ReadTimeCount();
  u_int32 needTime = longPause ? TICKS_PER_SEC/2 : TICKS_PER_SEC/10;
  
  if (now - lastTime > 5*TICKS_PER_SEC) {
    longPause = 1;
  } else {
    longPause = 0;
    if (now - lastTime < needTime) {
      //      printf("(%ld/%ld)", needTime-(now-lastTime), needTime);
      Delay(needTime - (now-lastTime));
    }
  }
  UartMuxPutChar(c);
  lastTime = ReadTimeCount();
#else
  if (hexMode) {
    printf("\ntx %02x ", c);
  }
  PERIP(UARTMUX_DATA) = c;
#endif
}

ioresult main(char *parameters) {
  char *p = parameters;
  int nParam;
  int verbose = 1, i;
  int retCode = S_ERROR;
  u_int16 divider = 0;
  int quit = 0;
  int hexMode = 0;

  UartMuxInit(NULL);


#if 0
  divider = GetUartRegister(clockSpeed.cpuClkHz, 1500000);
  PERIP(UARTRV_DIV) = divider;
  PERIP(UARTRV_DATA) = 'a';
  while (ioctl(stdin, IOCTL_TEST, NULL) == 0) {
    if (PERIP(UARTRV_STATUS) & UART_ST_RXFULL) {
      printf("Got char 0x%x\n", PERIP(UARTRV_DATA));
    }
  }
#endif
  
  nParam = RunProgram("ParamSpl", parameters);

  for (i=0; i<nParam; i++) {
    if (!strcmp(p, "-v")) {
      verbose = 1;
    } else if (!strcmp(p, "+v")) {
      verbose = 0;
    } else if (!strcmp(p, "-a")) {
      printf("MUX VSDSP\n");
      PERIP(VSRV1_CF) |= VSRV1_CF_MUX;
    } else if (!strcmp(p, "-b")) {
      printf("MUX RV\n");
      PERIP(VSRV1_CF) &= ~VSRV1_CF_MUX;
    } else if (!strcmp(p, "-h")) {
      printf("Usage: Term [-c|+c|-a|-b|-h]\n"
	     "-v|+v\tVerbose on|off\n"
	     "-a|-b\tMux VSDSP|RV to UART\n"
	     "-h\tShow this help\n");
      goto finally;
    } else {
      printf("E: Unknown parameter \"%s\".\n", p);
      goto finally;
    }
    p += strlen(p)+1;
  }

  /* in */
#if 1
  WriteIMem((0x20+INTV_UARTMUX_RX), ReadIMem((u_int16)(&IntUartMuxRxVector)));
  PERIP(INT_ENABLE1_HP) |= INTF1_UARTMUX_RX;
  //vo_stdin = &uartMuxInFile;
#endif
  
  /* out */
#if 1
  WriteIMem((0x20+INTV_UARTMUX_TX), ReadIMem((u_int16)(&IntUartMuxTxVector)));
  PERIP(INT_ENABLE1_HP) |= INTF1_UARTMUX_TX;
  //vo_stdout = &uartMuxOutFile;
#endif

  
  divider = GetUartRegister(clockSpeed.cpuClkHz, 4000000);
  divider = 12;
  printf("divider = 0x%04x\n", divider);
  PERIP(UARTMUX_DIV) = divider;
#if 0
  PERIP(VSRV1_CF) |= VSRV1_CF_MUX;
#endif
  
  while (!quit) {
#if 0
    while (PERIP(UARTMUX_STATUS) & UART_ST_RXFULL) {
      int c = PERIP(UARTMUX_DATA);
      if (hexMode) {
	printf("MUXRX %02x\n", c);
      } else {
	printf("%c", c);
      }
    }
#else
    int n;
    while ((n = UartMuxIoctl(&uartMuxInFile, IOCTL_TEST, NULL) > 0)) {
      int i;
      for (i=0; i<n; i++) {
	printf("%c", UartMuxGetChar());
      }
    }
#endif
      
#if 1
    if (ioctl(stdin, IOCTL_TEST, 0) > 0) {
      int c = fgetc(stdin);
      switch (c) {
      case 'G'-('A'-1):
	quit = 1;
	break;
      case 'A'-('A'-1):
	printf("MUX VSDSP\n");
	PERIP(VSRV1_CF) |= VSRV1_CF_MUX;
	break;
      case 'B'-('A'-1):
	printf("MUX RV\n");
	PERIP(VSRV1_CF) &= ~VSRV1_CF_MUX;
	break;
      case 'H'-('A'-1):
	hexMode = 1-hexMode;
	printf("hexMode = %d\n", hexMode);
	break;
      case 'V'-('A'-1):
	PERIP(UARTMUX_DIV) = divider+1;
	printf("Slower speed 0x%04x\n", PERIP(UARTMUX_DIV));
	break;
      case 'W'-('A'-1):
	PERIP(UARTMUX_DIV) = divider;
	printf("Nominal speed 0x%04x\n", PERIP(UARTMUX_DIV));
	break;
      case 'U'-('A'-1):
	PERIP(UARTMUX_DIV) = divider-1;
	printf("Slower speed 0x%04x\n", PERIP(UARTMUX_DIV));
	break;
      default:
#if 1
	SendChar(c, hexMode);
#else
	UartMuxPutChar(c);
#endif
	break;
      }
    }
#endif
#if 0
    if (PERIP(UARTRV_STATUS) & UART_ST_RXFULL) {
      int c = PERIP(UARTRV_DATA);
      if (hexMode) {
	printf("RVRX! %02x\n", c);
      } else {
	printf("%c", c);
      }
    }
#endif
#if 0
    if (PERIP(UARTMUX_STATUS) & UART_ST_FRAMERR) {
      static int n = 0;
      printf("MUX FRAME ERROR %d!\n", n++);
      PERIP(UARTMUX_STATUS) &= ~UART_ST_FRAMERR;
    }
#endif
#if 0
    printf("%04x %04x\n", PERIP(UARTMUX_STATUS), PERIP(UARTRV_STATUS));
#endif
  }

  retCode = S_OK;
 finally:
  UartMuxFini();

  return retCode;
}

