/*
 * Mini stty command
 *
 * iocshSttyCommand.c,v 1.4 2003/04/10 15:17:06 mrk Exp
 */
/***********************************************************************
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
* National Laboratory, and the Regents of the University of
* California, as Operator of Los Alamos National Laboratory, and
* Berliner Elektronenspeicherring-Gesellschaft m.b.H. (BESSY).
* gpibCore is distributed subject to a Software License Agreement
* found in file LICENSE that is included with this distribution.
***********************************************************************/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>

#include <errlog.h>
#include <iocsh.h>

#include "drvTermiosTtyGpib.h"

static int
epicsIocshSttyCommand(char argc, char **argv)
{
    char *line = NULL;
    int baud = 9600;
    int fd;
    int i;
    char *arg;
    int cflag = CS8 | CREAD | CLOCAL;
    speed_t speed;
    struct termios termios;

    for (i = 1 ; i < argc ; ) {
        arg = argv[i++];

        if (strcmp(arg, "-F") == 0) {
            if (i >= argc) {
                errlogPrintf("stty: Missing serial device parameter\n");
                return -1;
            }
            line = argv[i++];
        }
        else if (strncmp(arg, "--file=", 7) == 0) {
            line = arg+7;
        }
        else if (strcmp(arg, "cs5") == 0) {
            cflag = (cflag & ~CSIZE) | CS5;
        }
        else if (strcmp(arg, "cs6") == 0) {
            cflag = (cflag & ~CSIZE) | CS6;
        }
        else if (strcmp(arg, "cs7") == 0) {
            cflag = (cflag & ~CSIZE) | CS7;
        }
        else if (strcmp(arg, "cs8") == 0) {
            cflag = (cflag & ~CSIZE) | CS8;
        }
        else if (strcmp(arg, "parenb") == 0) {
            cflag |= PARENB;
        }
        else if (strcmp(arg, "-parenb") == 0) {
            cflag &= ~PARENB;
        }
        else if (strcmp(arg, "parodd") == 0) {
            cflag |= PARODD;
        }
        else if (strcmp(arg, "-parodd") == 0) {
            cflag &= ~PARODD;
        }
        else if (strcmp(arg, "clocal") == 0) {
            cflag |= CLOCAL;
        }
        else if (strcmp(arg, "-clocal") == 0) {
            cflag &= ~CLOCAL;
        }
        else if (strcmp(arg, "cstopb") == 0) {
            cflag |= CSTOPB;
        }
        else if (strcmp(arg, "-cstopb") == 0) {
            cflag &= ~CSTOPB;
        }
        else if (strcmp(arg, "crtscts") == 0) {
#if defined(CRTSCTS)
            cflag |= CRTSCTS;
#else
            errlogPrintf("stty: Warning -- RTS/CTS flow control is not available on this machine.\n");
#endif
        }
        else if (strcmp(arg, "-crtscts") == 0) {
#if defined(CRTSCTS)
            cflag &= ~CRTSCTS;
#endif
        }
        else if (isdigit(*arg)) {
            baud = 0;
            while (isdigit(*arg)) {
                if (baud < 100000)
                    baud = baud * 10 + (*arg - '0');
                arg++;
            }
            if (*arg != '\0') {
                errlogPrintf("stty: Invalid speed\n");
                return -1;
            }
        }
        else {
            errlogPrintf("stty: Warning -- Unsupported option `%s'\n", arg);
        }
    }
    if((line == NULL) || (*line == '\0')) {
        errlogPrintf("stty: Missing serial device name\n");
        return -1;
    }
    switch(baud) {
    case 50:     speed = B50;     break;
    case 75:     speed = B75;     break;
    case 110:    speed = B110;    break;
    case 134:    speed = B134;    break;
    case 150:    speed = B150;    break;
    case 200:    speed = B200;    break;
    case 300:    speed = B300;    break;
    case 600:    speed = B600;    break;
    case 1200:   speed = B1200;   break;
    case 1800:   speed = B1800;   break;
    case 2400:   speed = B2400;   break;
    case 4800:   speed = B4800;   break;
    case 9600:   speed = B9600;   break;
    case 19200:  speed = B19200;  break;
    case 38400:  speed = B38400;  break;
    case 57600:  speed = B57600;  break;
    case 115200: speed = B115200; break;
    case 230400: speed = B230400; break;
    default:
        errlogPrintf("stty: Invalid speed.\n");
        return -1;
    }

    if ((fd = open(line, O_RDWR|O_NOCTTY|O_NONBLOCK)) < 0) {
        errlogPrintf("stty: Can't open `%s': %s\n", line, strerror(errno));
        return -1;
    }
    if (tcgetattr(fd, &termios) < 0) {
        errlogPrintf("stty: Can't get `%s' attributes: %s\n", line, strerror(errno));
        close(fd);
        return -1;
    }
    termios.c_cflag = cflag;
    cfsetispeed(&termios, speed);
    cfsetospeed(&termios, speed);
    if (tcsetattr(fd, TCSANOW, &termios) < 0) {
        errlogPrintf("stty: Can't set `%s' attributes: %s\n", line, strerror(errno));
        close(fd);
        return -1;
    }

    /*
     * Is it valid to assume that termios settings survive a final close?
     */
    close(fd);
    return 0;
}

static const iocshArg sttyArg0 = { "See POSIX documentation", iocshArgArgv };
static const iocshArg *sttyArgs[1] = { &sttyArg0 };
static const iocshFuncDef sttyFuncDef = { "stty", 1, sttyArgs};
static void sttyCallFunc(const iocshArgBuf *args)
{
    epicsIocshSttyCommand(args[0].aval.ac, args[0].aval.av);
}

/*
 * This routine should be called by the low-level serial device driver
 * configuration command registration before multitasking has started,
 * so there's no race condition in the test/set of firstTime.
 */
void epicsShareAPI
registerEpicsIocshSttyCommand(void)
{
    static int firstTime = 1;
    if (firstTime) {
        firstTime = 0;
        iocshRegister(&sttyFuncDef, sttyCallFunc);
    }
}
