view fluid-mnf/target.c @ 348:37b5f94de802

fluid-mnf: sensible target tty specification
author Mychaela Falconia <falcon@freecalypso.org>
date Fri, 13 Mar 2020 06:41:44 +0000
parents b595ff13547b
children
line wrap: on
line source

/******************************************************************************
 * FLUID (Flash Loader Utility Independent of Device)
 *
 * Copyright Texas Instruments, 2001.
 * Mads Meisner-Jensen, mmj@ti.com.
 *
 * Target Connection and Control
 *
 * $Id: target.c 1.20 Mon, 21 Oct 2002 18:39:13 +0200 mmj $
 *
 * This serial interface handling architecture has been majorly redesigned
 * by Mychaela N. Falconia for the present fluid-mnf Linux port.
 *
 ******************************************************************************/

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int target_trace(unsigned char ch);


/******************************************************************************
 * Globals
 ******************************************************************************/

#define RECVBUF_SIZE  (65536)

static unsigned char recvbuf[RECVBUF_SIZE];
static int recvbuf_put;
static int recvbuf_get;

#define recvbuf_size() (recvbuf_put - recvbuf_get + \
                        ((recvbuf_put - recvbuf_get) < 0 ? RECVBUF_SIZE : 0))


/******************************************************************************
 * Error Functions
 ******************************************************************************/

void error_proto(char ch, char chx)
{
    char chd, chxd;

    chd  = (' ' <= ch  && ch  < 127 ? ch  : '.');
    chxd = (' ' <= chx && chx < 127 ? chx : '.');

    flowf(NORMAL, "(protocol error: got '%c' 0x%02X, expected '%c' 0x%02X)\n",
                 chd, ch, chxd, chx);

    main_fatal(E_PROTO_ERROR);
}


/******************************************************************************
 * Target Dependencies
 ******************************************************************************/

// <clk> is the target's clock frequency 
int target_uart_baudrate_divider_get(int clk, int bps)
{
    int divider = clk / bps / 16;

    tr(TrTargetDrv, "target_uart_baudrate_divider_get(%d, %d) %d\n",
       clk, bps, divider);

    return divider;
}


/******************************************************************************
 * Target Init and Send
 ******************************************************************************/

static int target_trace_timer;

void target_recv_reset(void)
{
    tr(TrTargetDrv, "recv_reset()\n");

    recvbuf_get = recvbuf_put = 0;

    memset(recvbuf, 0x77, RECVBUF_SIZE);

    serial_recv_reset();
}

int target_driver_init(char *ttyport, int baudrate)
{
    int rc;

    rc = serial_init(ttyport, baudrate);
    if (rc < 0)
        return rc;

    target_recv_reset();

    return 0;
}

int target_driver_baudrate(int baudrate)
{
    return serial_baudrate_set(baudrate);
}
    
int target_putchar(char ch)
{
    char trch;

    trch = (ch < ' ' || ch == 127 ? '.' : ch);
    tr(TrPutChar, "s '%c'/0x%02X\n", trch, ch & 0xFF);

    return serial_send(&ch, 1);
}

int target_send(char *buf, int size)
{
    tr(TrPutChar, "s (%d bytes)\n", size);

    return serial_send(buf, size);
}


/******************************************************************************
 * Target Wait and Receive
 ******************************************************************************/

void target_recv_push(char *buf, int size)
{
    if (size == 0) {
        tr(TrDriverGet, " G?");
        return;
    }

    while (size--) {
        recvbuf[recvbuf_put++] = *buf++;
        recvbuf_put &= (RECVBUF_SIZE - 1);
    }
    tr(TrDriverGet, " G%d", recvbuf_size());
}

// Wait maximum <timeout> milli-seconds for <size> bytes to arrive. If
// <size> is zero, we unconditionally wait for <time_ms> milli-seconds
// (quite simply a delay). The transfer time is automatically added to the
// total waiting time.
int target_wait(int size, int timeout)
{
    char rxbuf[512];
    int cc;

    tr(TrTargetWait, "target_wait(%d, %d)\n", size, timeout);

    if (size > 0)
        timeout += serial_transfer_time(size);

    target_trace_timer = timeout;

    if (size == 0) {
        usleep(timeout * 1000);
        return 0;
    }

    while (recvbuf_size() < size) {
        cc = serial_recv(rxbuf, sizeof rxbuf, timeout);
        if (cc < 0)
            return cc;
        if (cc == 0)
            return E_RECV_TIMEOUT;
        target_recv_push(rxbuf, cc);
    }

    return recvbuf_size();
}

int target_recv(void *outbuf, int size)
{
    int i, bufsize;
    char *buf = (char *) outbuf;

    bufsize = recvbuf_size();
    if (bufsize < size)
        size = bufsize;

    for (i = 0; i < size; i++) {
        *buf++ = recvbuf[recvbuf_get++];
        recvbuf_get &= (RECVBUF_SIZE - 1);
    }

    tr(TrGetChar, "r (%d bytes)\n", size);
    return size;
}

int target_getchar(void)
{
    int trace_in_progress;
    char ch;
    
    do {
        ch = recvbuf[recvbuf_get++];
        recvbuf_get &= (RECVBUF_SIZE - 1);
        tr(TrGetChar, "r '%c' %02X\n",
           (ch >= ' ' && ch <= 126 ? ch : '.'), ch & 0xFF);

        if ((trace_in_progress = target_trace((char) ch)) != 0) {
            if (target_wait(1, target_trace_timer) < 1)
                main_fatal(E_RECV_TIMEOUT);
        }
    } while (trace_in_progress);
    
    return (ch & 0xFF);
}

int target_expect_char(char ch, int timeout)
{
    char mych;

    if (target_wait(1, timeout) < 1) {
        flowf(NORMAL, "(Waited for '%c' 0x%02X)\n", ch, ch);
        main_fatal(E_RECV_TIMEOUT);
    }

    if ((mych = target_getchar()) != ch)
        error_proto(mych, ch);

    return ch;
}


/******************************************************************************
 * Target Buffer Put
 ******************************************************************************/

int buf_put1(char *buf, unsigned char data)
{
    tr(TrPutChar, "buf_put1(0x%02X) '%c'\n", data, data);

    *buf   = (data      ) & 0xFF;
    return 1;
}

int buf_put2(char *buf, unsigned short data)
{
    tr(TrPutChar, "buf_put2(0x%04X)\n", data);

    *buf++ = (data      ) & 0xFF;
    *buf   = (data >>  8) & 0xFF;
    return 2;
}

int buf_put4(char *buf, unsigned int data)
{
    tr(TrPutChar, "buf_put4(0x%08X)\n", data);

    *buf++ = (data      ) & 0xFF;
    *buf++ = (data >>  8) & 0xFF;
    *buf++ = (data >> 16) & 0xFF;
    *buf   = (data >> 24) & 0xFF;
    return 4;
}

// Put 2-byte integer in network order
int buf_put2no(char *buf, unsigned short data)
{
    tr(TrPutChar, "buf_put2no(0x%04X)\n", data);

    *buf++ = (data >>  8) & 0xFF;
    *buf   = (data      ) & 0xFF;
    return 2;
}

// Put 4-byte integer in network order
int buf_put4no(char *buf, unsigned int data)
{
    tr(TrPutChar, "buf_put4no(0x%08X)\n", data);

    *buf++ = (data >> 24) & 0xFF;
    *buf++ = (data >> 16) & 0xFF;
    *buf++ = (data >>  8) & 0xFF;
    *buf   = (data      ) & 0xFF;
    return 4;
}


/******************************************************************************
 * Special Target Control Functions
 ******************************************************************************/

// Control target power. Works *only* with special cable! If <state> is
// non-zero, the power is on. Otherwise, it is off.
void target_power(char state)
{
    if (arg_uart_level_convert)
        return;

    if (state) {
        serial_rts(0);
    }
    else {
        serial_rts(1);
        serial_dtr(1);
        serial_break(1);
        serial_break(0);
    }
}

// Control target reset line. Works *only* with special cable!  If <state>
// is non-zero, the reset line is asserted. Otherwise, it is negated.
void target_reset(char state)
{
    if (arg_uart_level_convert)
        return;

    if (state) {
        serial_rts(1);
        serial_dtr(0);
        serial_rts(0);
        serial_break(0);
    }
    else {
        serial_break(1);
        serial_rts(1);
        serial_dtr(1);
        serial_rts(0);
    }
}


/******************************************************************************
 * Target Trace/Debug Functions
 ******************************************************************************/

static int target_trace_enable_flag = 0;

int target_trace_enable(int flag)
{
    int old = target_trace_enable_flag;

    target_trace_enable_flag = flag;

    if (arg_target_trace_enable)
        flowf(DEBUG, "{%s}", (flag ? "enable" : "disable"));

    return old;
}

// Interpret and display target trace message. Return zero if <ch> was not
// intercepted. Otherwise, if it was intercepted, return non-zero.
int target_trace(unsigned char ch)
{
    int i;
    unsigned int number = 0;
    char buf[80], *p;

    // If target is not supposed to transmit any tracing at this point in
    // the state machine(s), the flag will be zero and thus we return.
    if (target_trace_enable_flag && ch == '$') {
        if (target_wait(1, 100) < 0)
            main_fatal(E_RECV_TIMEOUT);
        ch = target_getchar();
        switch (ch) {
        case 'S':
            p = buf;
            while (ch != 0) {
                if (target_wait(1, 100) < 0)
                    main_fatal(E_RECV_TIMEOUT);
                ch = target_getchar();
                *p++ = ch;
            }
            *p = 0;
            if (arg_target_trace_enable)
                printf("{'%s'} ", buf);
            break;
        case 'N':
            for (i = 0; i < 4; i++) {
                if (target_wait(1, 100) < 0)
                    main_fatal(E_RECV_TIMEOUT);
                ch = target_getchar();
                number = (number >> 8) + (ch << 24);
            }
            if (arg_target_trace_enable)
                printf("{0x%X/%dd} ", number, number);
            break;
        default:
            fprintf(stderr, "WARNING: Bad TargetTrace char received! (0x%X)\n",
                    ch & 0xFF);
            break;
        }
        return 1;
    }
    return 0;
}