text: /**
   *-----------------------------------------------------------------------------
   * Title      : E-band receiver: Uart class
   * ----------------------------------------------------------------------------
   * File       : Uart.cpp
   * Created    : 2021-06-28
   * ----------------------------------------------------------------------------
   * Description:
   * Uart class for the E-band receiver microcontroller
   * ----------------------------------------------------------------------------
   * This file is part of the e-band receiver microcontroller. It is subject to
   * the license terms in the LICENSE.txt file found in the top-level directory
   * of this distribution and at:
   *    https://confluence.slac.stanford.edu/display/ppareg/LICENSE.html.
   * No part of the e-band receiver microcontroller, including this file, may be
   * copied, modified, propagated, or distributed except according to the terms
   * contained in the LICENSE.txt file.
   * ----------------------------------------------------------------------------
  **/
  
  /*
   * Copyright (c) 2016-2019, Texas Instruments Incorporated
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * *  Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * *  Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in the
   *    documentation and/or other materials provided with the distribution.
   *
   * *  Neither the name of Texas Instruments Incorporated nor the names of
   *    its contributors may be used to endorse or promote products derived
   *    from this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
   * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
   * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
   * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  /* XDC module Headers */
  #include <xdc/runtime/System.h>
  
  /* BIOS module Headers */
  #include <ti/sysbios/BIOS.h>
  
  /* Driver Header files */
  #include <ti/drivers/UART.h>
  #include <ti/drivers/dpl/SystemP.h>
  
  /* Driver configuration */
  #include "ti_drivers_config.h"
  
  #include <string.h>
  
  //#include "Priority.h"
  
  #include "Uart.h"
  #include "TimePassed.h"
  
  
  Uart*            Uart::_instance;
  char             Uart::_outBuf[];
  Semaphore_Handle Uart::semHandleRx;
  Semaphore_Handle Uart::semHandleTx;
  volatile size_t  Uart::dataReady = 0;
  
  
  extern "C" static
  void readCallback(UART_Handle handle, void *buf, size_t count)
  {
    //Priority& priority = Priority::instance();
  
    //priority.set(Control::PRIO_RS232);    // Trigger the RS232 control job
  
    Uart::dataReady = count;
  
    if (Uart::semHandleRx)  Semaphore_post(Uart::semHandleRx);
  }
  
  extern "C" static
  void writeCallback(UART_Handle handle, void *buf, size_t count)
  {
    System_flush();
  
    // Need to give a semaphore here to allow the next write to start
    if (Uart::semHandleTx)  Semaphore_post(Uart::semHandleTx);
  }
  
  
  void Uart::initialize(unsigned id)
  {
    static Uart instance(id);
  
    System_printf("RS232 I/O is using %s\n",
                  id == CONFIG_UART_XDS ? "XDS" : "UART");
    System_flush();
  
    _instance = &instance;
  }
  
  Uart& Uart::instance()
  {
    return *_instance;
  }
  
  Uart::Uart(unsigned id)
  {
    Semaphore_Params semParams;
  
    // Revisit: Need one mutex per UART
    Semaphore_Params_init(&semParams);
    semParams.mode = Semaphore_Mode_BINARY;
  
    semHandleRx = Semaphore_create(0, &semParams, NULL); // Pend on 1st rdln()
    if (semHandleRx == NULL)
    {
      System_printf("Couldn't create %cx semaphore\n", 'R');
      System_flush();
      while(1);
    }
  
    semHandleTx = Semaphore_create(1, &semParams, NULL); // Don't pend on 1st prtf()
    if (semHandleTx == NULL)
    {
      System_printf("Couldn't create %cx semaphore\n", 'T');
      System_flush();
      while(1);
    }
  
    UART_Params uartParams;
  
    // Initialize the UART parameters.  Let's keep most of the defaults
    // (e.g. baudrate = 115200) and only change the following.
    UART_Params_init(&uartParams);
    uartParams.writeMode      = UART_MODE_CALLBACK;
    uartParams.readMode       = UART_MODE_CALLBACK;
    uartParams.writeCallback  = writeCallback;
    uartParams.readCallback   = readCallback;
    uartParams.writeDataMode  = UART_DATA_BINARY;
    uartParams.readDataMode   = UART_DATA_TEXT;
    uartParams.readReturnMode = UART_RETURN_NEWLINE;
  
    _handle = UART_open(id, &uartParams);
    if (_handle == NULL)
    {
      System_printf("Unable to open UART %u\n", id);
      System_flush();
      while (1);
    }
  
    // Hang the initial nonblocking read
    auto rc = UART_read(_handle, _inBuf, sizeof(_inBuf));
    if (rc != 0)
    {
      System_printf("Unexpectedly found %d bytes on initial UART read:\n", rc);
      System_flush();
    }
  }
  
  
  Uart::~Uart()
  {
    UART_close(_handle);
  }
  
  size_t Uart::rdln(char* buffer, size_t bufSize, unsigned tmo)
  {
    // Wait for data to show up
    const auto ticks = TimePassed::seconds(tmo);
    Semaphore_pend(semHandleRx, tmo ? ticks : BIOS_WAIT_FOREVER);
  
    if (dataReady == 0)  return 0;
  
    auto inSize = dataReady - 1;          // Don't include the carriage return
    if (bufSize < inSize)
      inSize = bufSize - 1;               // Grab only as much as there space for
  
    memcpy(buffer, _inBuf, inSize);
    buffer[inSize] = '\0';                // Null terminate
  
    dataReady = 0;                        // Reset
    _inBuf[0] = '\0';
    UART_read(_handle, _inBuf, sizeof(_inBuf));
  
    return inSize;
  }
  
  int_fast32_t Uart::prtf(const char* fmt, va_list& va)
  {
    // Pend on a semaphore here to allow the previous write to finish
    Semaphore_pend(semHandleTx, BIOS_WAIT_FOREVER);
  
    SystemP_vsnprintf(_outBuf, sizeof(_outBuf), fmt, va);
  
    // Oooh, yuck!  vsnprintf() doesn't format \n with a return character!
    auto outSize = strlen(_outBuf);
    auto outBuf  = _outBuf;
    auto rem     = outSize + 1;           // Include NULL terminator
    while (*outBuf)                       // Stop when the NULL terminator is hit
    {
      auto nl = strstr(outBuf, "\n");     // Find the first newline
      if (nl == NULL)  break;             // No newline found
      rem   -= nl - outBuf;               // Number of chrs remaining after the nl
      outBuf = (char*)memmove(nl+1, nl, rem); // Make room for the <return>
      *nl    = '\r';                      // Replace the original newline
      outSize++;                          // The string length has increased by 1
      outBuf++;                           // Move past the new newline
      if (outSize == sizeof(_outBuf) - 1) // Stay within the full buffer
        break;
    }
  
    return UART_write(_handle, _outBuf, outSize);
  }
  