view fluid-mnf/serial.c @ 313:9b5079989681

frbl/reconst/inc/command.h: tab fixes
author Mychaela Falconia <falcon@freecalypso.org>
date Wed, 04 Mar 2020 23:10:48 +0000
parents 9cecc930d78f
children 6ff231195905
line wrap: on
line source

/******************************************************************************
 * FLUID (Flash Loader Utility Independent of Device)
 *
 * Copyright Texas Instruments, 2001.
 * Mads Meisner-Jensen, mmj@ti.com.
 *
 * Serial/UART driver
 *
 * $Id: serial.c 1.23 Fri, 18 Oct 2002 08:53:12 +0200 mmj $
 *
 ******************************************************************************/

#include "fluid.h"
#include "serial.h"
#include "trace.h"

#include <assert.h>
#include <stdio.h>

#if defined(MSC) || defined(BCC)
  #include <windows.h> // for Sleep()
#else
//  #error Not compiling for MSC or BCC?
#endif


extern void target_recv_push(char *buf, int size);


/******************************************************************************
 * OS Independent
 ******************************************************************************/

static void serial_listener(void);

#if (OS == WIN32)

/******************************************************************************
 * Win32 Driver
 ******************************************************************************/

#include <windows.h>

static HANDLE     hCom;
static HANDLE     thread_handle = NULL;
static DWORD      thread_id;
static OVERLAPPED read_overlapped;
static OVERLAPPED write_overlapped;
static DCB        dcb;

const char PORT_NAME_PRE[] = "COM";
// A special syntax is required when accessing com ports greater than 9, e.g., "\\.\COM10"
const char PORT_NAME_PREFIX[] = "\\\\.\\COM";

int serial_init(int uart, int bps, char *flowcontrol)
{
    int error;
    COMMTIMEOUTS timeouts;
    char pc_comport[32];

    sprintf(pc_comport, "%s%d", PORT_NAME_PREFIX, uart);

    hCom = CreateFile(pc_comport,
                      GENERIC_READ | GENERIC_WRITE,
                      0,    // comm devices must be opened w/exclusive-access
                      NULL, // no security attributes
                      OPEN_EXISTING, // comm devices must use OPEN_EXISTING
                      FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
                      NULL  // hTemplate must be NULL for comm devices
        );
    if (hCom == INVALID_HANDLE_VALUE)
        return E_OS + E_UART_INIT;

    // We will build on the current configuration, and skip setting the size
    // of the input and output buffers with SetupComm.
    if (!GetCommState(hCom, &dcb))
        return E_OS + E_UART_INIT;

    dcb.fAbortOnError = FALSE; // Hmm? (Dont't remember exactly what this is)
    dcb.ByteSize = 8;          // Data size, xmit, and rcv
    dcb.Parity = NOPARITY;     // No parity bit
    dcb.StopBits = ONESTOPBIT; // One stop bit
    dcb.fOutxCtsFlow = 0;      // Disable CTS HW handshaking!
    dcb.fOutxDsrFlow = 0;      // Disable DSR HW handshaking!
    dcb.fDsrSensitivity = 0;   // Disable DSR HW handshaking!

    // Note the DTR = +12V and RTS = -12V is needed to power the serial
    // level converter!

    switch (flowcontrol[0]) {
    case 'n': dcb.fDtrControl = DTR_CONTROL_DISABLE; break;
    case 'h': dcb.fDtrControl = DTR_CONTROL_HANDSHAKE; break;
    case 'p':
    default:
        dcb.fDtrControl = DTR_CONTROL_ENABLE; break;
    }
    switch (flowcontrol[1]) {
    case 'p': dcb.fRtsControl = RTS_CONTROL_ENABLE; break;
    case 'h': dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; break;
    case 'n':
    default:
        dcb.fRtsControl = RTS_CONTROL_DISABLE; break;
    }

    if (!SetCommState(hCom, &dcb))
        return E_OS + E_UART_INIT;

    if ((error = serial_baudrate_set(bps)) < 0)
        return error;

    timeouts.ReadIntervalTimeout = 0;
    timeouts.ReadTotalTimeoutMultiplier = 0;
    timeouts.ReadTotalTimeoutConstant = 0;
    timeouts.WriteTotalTimeoutMultiplier = 0;
    timeouts.WriteTotalTimeoutConstant = 0;

    assert(SetCommTimeouts (hCom, &timeouts));
    assert(SetCommMask (hCom, EV_RXCHAR));
    //assert(SetupComm (hCom, INPUT_BUFFER_SIZE, OUTPUT_BUFFER_SIZE));

    sprintf(pc_comport, "read%s%d", PORT_NAME_PRE, uart);
    read_overlapped.hEvent = CreateEvent (NULL, TRUE, FALSE, pc_comport);

    sprintf(pc_comport, "write%s%d", PORT_NAME_PRE, uart);
    write_overlapped.hEvent = CreateEvent (NULL, TRUE, FALSE, pc_comport);

    thread_handle =
        CreateThread (NULL,
                      0,
                      (LPTHREAD_START_ROUTINE) serial_listener,
                      NULL,
                      0,
                      &thread_id);

    if (thread_handle == NULL)
        return E_OS + E_UART_INIT;

    serial_reset();

    return 0;
}

int serial_is_baudrate(int bps)
{
    int i;
    const struct rate_s {
        int bps;
        int baudrate;
    } rates[] = {
        {   9600,   9600 }, {   9,      9600 },
        {  14400,  14400 }, {  14,     14400 },
        {  19200,  19200 }, {  19,     19200 },
        {  38400,  38400 }, {  38,     38400 },
        {  57600,  57600 }, {  57,     57600 },
        { 115200, 115200 }, { 115,    115200 },
        { 203125, 203125 }, { 203,    203125 }, // 13MHz clock
        { 230400, 230400 }, { 230,    230400 },
        { 406250, 406250 }, { 406,    406250 }, // 13MHz clock
        { 460800, 460800 }, { 460,    460800 },
        { 812500, 812500 }, { 812,    812500 }, // 13MHz clock
        { 921600, 921600 }, { 921,    921600 },
        {      0,      0 } // terminator
    };

    for (i = 0; i < sizeof(rates) / sizeof(struct rate_s) - 1; i++)
        if (rates[i].bps == bps)
            break;

    tr(TrTargetDrv, "serial_is_baudrate(%d) %d\n", bps, rates[i].baudrate);

    return rates[i].baudrate;
}

int serial_baudrate_set(int bps)
{
#if 0
    if (!GetCommState(hCom, &dcb))
        return E_OS + E_UART_PARAM;
#endif

    bps = serial_is_baudrate(bps);
    if (bps == 0)
        return E_OS + E_UART_PARAM;

    /* Replace 812K with 827K. Otherwise, the chip will automatically select 800K. */ 
    /* 800K is OK, but we might as well gain the extra 3% speed :-) */
    if (bps == 812500)
        dcb.BaudRate = 827586;
    else
        dcb.BaudRate = bps;

    if (!SetCommState(hCom, &dcb))
        return E_OS + E_UART_PARAM;

    return bps;
}

int serial_baudrate_get(void)
{
    return dcb.BaudRate;
}

void serial_exit(void)
{
    DWORD exit_code;

    serial_reset();

    (void) GetExitCodeThread (thread_handle, &exit_code);
    (void) TerminateThread (thread_handle, exit_code);

    (void) CloseHandle (hCom);
    (void) CloseHandle (read_overlapped.hEvent);
    (void) CloseHandle (write_overlapped.hEvent);
}

// Clear buffers and transactions.
void serial_reset(void)
{
    PurgeComm(hCom, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
}

// Return the number of milli-seconds it takes to transfer <n> bytes.
int serial_transfer_time(int size)
{
    return 1000 * 10 * size / serial_baudrate_get();
}


/***************************************
 * Send
 ***************************************/

int serial_send(char *buf, int size)
{
    DWORD written;

    ResetEvent(write_overlapped.hEvent); // why?

    if (!WriteFile(hCom, buf, size, &written, &write_overlapped)) {
        if (GetLastError() == ERROR_IO_PENDING) {
            if (GetOverlappedResult(hCom, &write_overlapped,
                                    &written, TRUE) == FALSE)
                written = E_OS + E_UART_DRV_SEND;
        }
    }

    return written;
}


/***************************************
 * Receive
 ***************************************/

void serial_recv_reset(void)
{
    PurgeComm(hCom, PURGE_RXABORT|PURGE_RXCLEAR);
}

static void serial_listener(void)
{
    DWORD event_mask;
    char buf[64];
    DWORD size;

    while (1)
    {
        // Wait the event signalling characters received.
        if (WaitCommEvent (hCom, &event_mask, NULL) != TRUE) {
            main_msg("WaitCommEvent(): error %d\n", GetLastError());
            return;
        }
        tr(TrTargetDrv, "|");

        // Read all characters received in the buffer.  Mysteriously, it
        // does NOT work with a buffer size greater than one!
        do {
            if (!ReadFile(hCom, buf, 1, &size, &read_overlapped))
            {
                if (GetLastError() == ERROR_IO_PENDING) {
                    GetOverlappedResult(hCom, &read_overlapped, &size, TRUE);
                }
                else {
                    main_msg("ReadFile(): error %d\n", GetLastError());
                    serial_recv_reset();
                    break;
                }
            }
            // Push the data to upper layer
            target_recv_push(buf, size);

        } while (size);
    }
}


/******************************************************************************
 * Control of Delta cable special outputs
 ******************************************************************************/

void serial_rts(char state)
{
    if (state)
        EscapeCommFunction(hCom, SETRTS);
    else
        EscapeCommFunction(hCom, CLRRTS);
}

void serial_dtr(char state)
{
    if (state)
        EscapeCommFunction(hCom, SETDTR);
    else
        EscapeCommFunction(hCom, CLRDTR);
}

void serial_break(char state)
{
    if (state)
        SetCommBreak(hCom);
    else
        ClearCommBreak(hCom);
}

#else // (OS == WIN32)


/******************************************************************************
 * Unix driver
 ******************************************************************************/


#endif // (OS == WIN32)