view fluid-mnf/serial.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 2bd27d940023
children 49fe64a5e207
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 $
 *
 * This serial interface handling architecture has been majorly redesigned
 * by Mychaela N. Falconia for the present fluid-mnf Linux port.
 *
 * Because FLUID supports Calypso high baud rates of 203125, 406250 and 812500
 * bps, as well as D-Sample XXO (eXternal Xtal Oscillator) baud rates of
 * 230400, 460800 and 912600 bps, the present fluid-mnf port had to be made
 * quite Linux-specific, using the same raw ioctl approach as is used in
 * freecalypso-tools/libserial-linux.
 *
 ******************************************************************************/

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

#include <sys/types.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <asm/ioctls.h>
#include <asm/termbits.h>
#include <sys/errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

extern int errno;


/******************************************************************************
 * Linux Driver
 ******************************************************************************/

static int target_fd;
static struct termios2 target_termios;

static void fill_termios(int bps)
{
	int termios_baud_code;

	switch (bps) {
	case 9600:
		termios_baud_code = B9600;
		break;
	case 19200:
		termios_baud_code = B19200;
		break;
	case 38400:
		termios_baud_code = B38400;
		break;
	case 57600:
		termios_baud_code = B57600;
		break;
	case 115200:
		termios_baud_code = B115200;
		break;
	case 230400:
		termios_baud_code = B230400;
		break;
	case 460800:
		termios_baud_code = B460800;
		break;
	case 921600:
		termios_baud_code = B921600;
		break;
	default:
		termios_baud_code = BOTHER;
	}
	target_termios.c_iflag = IGNBRK;
	target_termios.c_oflag = 0;
	target_termios.c_cflag = termios_baud_code | CLOCAL|HUPCL|CREAD|CS8;
	target_termios.c_lflag = 0;
	target_termios.c_cc[VMIN] = 1;
	target_termios.c_cc[VTIME] = 0;
	target_termios.c_ispeed = bps;
	target_termios.c_ospeed = bps;
}

int serial_init(char *ttyport, int bps)
{
	static int zero = 0;

	if (!ttyport) {
		ttyport = getenv("FLUID_PORT");
		if (!ttyport) {
			fprintf(stderr,
"fluid-mnf error: target tty port must be specified with -p or FLUID_PORT=\n");
			exit(-E_BADARG);
		}
	}

	target_fd = open(ttyport, O_RDWR|O_NONBLOCK);
	if (target_fd < 0)
		return E_OS + E_UART_INIT;
	ioctl(target_fd, TIOCEXCL);
	fill_termios(bps);
	if (ioctl(target_fd, TCSETSF2, &target_termios) < 0)
		return E_OS + E_UART_INIT;
	ioctl(target_fd, FIONBIO, &zero);

	if (arg_uart_level_convert) {
		/*
		 * Powering TI's serial level converter
		 * with DTR = +12V and RTS = -12V
		 */
		serial_dtr(1);
		serial_rts(0);
	}

	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)
{
    bps = serial_is_baudrate(bps);
    if (bps == 0)
        return E_OS + E_UART_PARAM;

    fill_termios(bps);
    if (ioctl(target_fd, TCSETSF2, &target_termios) < 0)
        return E_OS + E_UART_PARAM;

    return bps;
}

int serial_baudrate_get(void)
{
    return target_termios.c_ospeed;
}

void serial_exit(void)
{
    serial_reset();
    close(target_fd);
}

// Clear buffers and transactions.
void serial_reset(void)
{
	ioctl(target_fd, TCFLSH, TCIOFLUSH);
}

// 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)
{
	int cc;

	cc = write(target_fd, buf, size);
	if (cc < 0)
		cc = E_OS + E_UART_DRV_SEND;
	return cc;
}


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

void serial_recv_reset(void)
{
	ioctl(target_fd, TCFLSH, TCIFLUSH);
}

int serial_recv(char *buf, int size, int timeout)
{
	fd_set fds;
	struct timeval tv;
	int cc;

	for (;;) {
		FD_ZERO(&fds);
		FD_SET(target_fd, &fds);
		tv.tv_sec = timeout / 1000;
		tv.tv_usec = (timeout % 1000) * 1000;
		cc = select(target_fd+1, &fds, NULL, NULL, &tv);
		if (cc < 0) {
			if (errno == EINTR)
				continue;
			return E_OS + E_UART_DRV_RECV;
		}
		break;
	}
	if (cc == 0)
		return cc;
	cc = read(target_fd, buf, size);
	if (cc <= 0)
		cc = E_OS + E_UART_DRV_RECV;
	return cc;
}


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

void serial_rts(char state)
{
    int rts_arg = TIOCM_RTS;

    if (state)
        ioctl(target_fd, TIOCMBIS, &rts_arg);
    else
        ioctl(target_fd, TIOCMBIC, &rts_arg);
}

void serial_dtr(char state)
{
    int dtr_arg = TIOCM_DTR;

    if (state)
        ioctl(target_fd, TIOCMBIS, &dtr_arg);
    else
        ioctl(target_fd, TIOCMBIC, &dtr_arg);
}

void serial_break(char state)
{
    if (state)
        ioctl(target_fd, TIOCSBRK);
    else
        ioctl(target_fd, TIOCCBRK);
}