//////////////////////////////////////////////////////////////////////
//
//  laserconfig - Configuration Tool for UPN Laser Transceiver
//  Copyright (c) 2000 Jan Kiszka
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//////////////////////////////////////////////////////////////////////

// History: 0.1   - first version, quit buggy
//          0.2   - serial port setup corrected
//                - data is read byte-by-byte from port now
//          0.3   - added laser control
//                - improved error messages
//          0.4   - added baudrate and sensitivity control
//          0.4.1 - minor corrections regarding setitimer() and select()

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/time.h>
#include <string.h>
#include <signal.h>

int            tty;
char           zero[] = {0, 0};
char           buf[] = {0x0B, 0x51, 0x65, 0x70, 0x43, 0x81, 0x90, 0x11};
char           echobuf;
fd_set         rd_set;
struct termios settings;
struct timeval start, stop;
int            n;
int            config    = 1;
int            laserctrl = 0;



void CfgError()
{
    fprintf(stderr, "error configuring serial port\n");
    exit(1);
}



void alarmsignal(int signum)
{
}



int ReadCmdLine(int argc, char* argv[])
{
    int n;
    int acSwitchUsed = 0;
    int bSwitchUsed  = 0;
    int sSwitchUsed  = 0;
    int sensitivity;

    if ((argc < 2) || (strcmp(argv[1], "--help") == 0) || (argc > 7))
        return 0;

    for (n = 2; n < argc; n++)
    {
        if (strcmp(argv[n], "-a") == 0)
        {
            if (acSwitchUsed)
                return 0;
            acSwitchUsed = 1;

            laserctrl = 1;
        }
        else if (strcmp(argv[n], "-c") == 0)
        {
            if (acSwitchUsed)
                return 0;
            acSwitchUsed = 1;

            config    = 0;
            laserctrl = 1;
        }
        else if (strcmp(argv[n], "-b") == 0)
        {
            if (bSwitchUsed)
                return 0;
            bSwitchUsed = 1;

            if (n+1 >= argc)
                return 0;

            n++;
            if (strcmp(argv[n], "19200") == 0)
                buf[5] = 0x8B;
            else if (strcmp(argv[n], "38400") == 0)
                buf[5] = 0x89;
            else if (strcmp(argv[n], "57600") == 0)
                buf[5] = 0x83;
            else if (strcmp(argv[n], "115200") == 0)
                buf[5] = 0x81;
            else
                return 0;
        }
        else if (strcmp(argv[n], "-s") == 0)
        {
            if (sSwitchUsed)
                return 0;
            sSwitchUsed = 1;

            if (n+1 >= argc)
                return 0;

            n++;
            if (strlen(argv[n]) != 1)
                return 0;

            sensitivity = argv[n][0] - '0';
            if ((sensitivity >= 1) && (sensitivity <= 7))
                buf[2] = 0x61 + sensitivity;
            else
                return 0;
        }
        else
            return 0;
    }

    return 1;
}



void ConfigureTransceiver()
{
    if ((cfsetspeed(&settings, B9600) < 0) ||
        (tcsetattr(tty, TCSANOW, &settings) < 0))
        CfgError();

    write(tty, zero, 1);
    gettimeofday(&start, NULL);;
    printf("\npowerup the transceiver now...");
    fflush(stdout);
    tcdrain(tty);

    gettimeofday(&stop, NULL);
    if (((stop.tv_sec - start.tv_sec) * 10 + (stop.tv_usec - start.tv_usec) / 100000) < 1)
    {
        do
        {
            printf("\r                                          "
                   "\rdepower the transceiver first...");
            fflush(stdout);
            sleep(1);

            printf("\rpowerup the transceiver after some seconds...");
            write(tty, zero, 1);
            gettimeofday(&start, NULL);
            fflush(stdout);
            tcdrain(tty);

            gettimeofday(&stop, NULL);
        }
        while (((stop.tv_sec - start.tv_sec) * 10 + (stop.tv_usec - start.tv_usec) / 100000) < 1);
    }

    printf("\n\nsending configuration string\a\n");
    sleep(1);
    write(tty, zero, 2);
    write(tty, buf, sizeof(buf));
    tcdrain(tty);
    FD_ZERO(&rd_set);
    FD_SET(tty, &rd_set);
    stop.tv_sec  = 2;
    stop.tv_usec = 0;
    for (n = 0; n < 8; n++)
    {
        if (select(FD_SETSIZE, &rd_set, NULL, NULL, &stop) <= 0)
        {
            printf("ERROR: string not echoed correctly (timeout)\n");
            exit(2);
        }
        if ((read(tty, &echobuf, 1) < 1) || (buf[n] != echobuf))
        {
            printf("ERROR: string not echoed correctly (byte %d is wrong)\n\a\a\a", n+1);
            exit(2);
        }
    }
    sleep(1);
    printf("string echoed correctly\a\n"
           "waiting for transceiver to get ready.");
    fflush(stdout);

    tcflush(tty, TCIOFLUSH);
    if ((cfsetspeed(&settings, B115200) < 0) ||
        (tcsetattr(tty, TCSANOW, &settings) < 0))
        CfgError();
    while (1)
    {
        write(tty, buf, 1);

        stop.tv_sec  = 0;
        stop.tv_usec = 100000;
        if (select(FD_SETSIZE, &rd_set, NULL, NULL, &stop) <= 0)
            break;

        read(tty, &echobuf, 1);

        sleep(1);
        printf(".");
        fflush(stdout);
    }

    printf("\n");
}



void LaserControl()
{
    struct termios oldterm, newterm;
    char buf[1024];
    char c = 0;
    int on = 0;
    struct itimerval timer;

    tcgetattr(0, &oldterm);
    memcpy(&newterm, &oldterm, sizeof(struct termios));

    settings.c_cflag &= ~CRTSCTS;
    if ((cfsetspeed(&settings, B115200) < 0) ||
        (tcsetattr(tty, TCSANOW, &settings) < 0))
        CfgError();

    printf("\n<TAB> - toggle laser on/off\n"
             "<ESC> - exit program\n\n"
             "laser is OFF");
    fflush(stdout);

    newterm.c_lflag &= (~ICANON + ~ECHO);
    newterm.c_cc[VTIME] = 0;
    newterm.c_cc[VMIN]  = 0;
    tcsetattr(0, TCSANOW, &newterm);

    timer.it_interval.tv_usec = 20000;
    timer.it_interval.tv_sec  = 0;
    timer.it_value.tv_usec = 20000;
    timer.it_value.tv_sec  = 0;
    signal(SIGALRM, &alarmsignal);

    setitimer(ITIMER_REAL, &timer, NULL);

    memset(buf, 0xAA, sizeof(buf));

    while (c != 27)
    {
        if (read(0, &c, 1) == 0)
            c = 0;
        else if (c == '\t')
        {
            if (on)
            {
                on = 0;
                printf("\rlaser is OFF");
                fflush(stdout);
            }
            else
            {
                on = 1;
                printf("\r            "
                       "\rlaser is ON");
                fflush(stdout);
            }
        }

        if (on)
            write(tty, buf, sizeof(buf));

        pause();
    }

    timer.it_value.tv_usec = 0;
    timer.it_interval.tv_usec = 0;
    setitimer(ITIMER_REAL, &timer, NULL);

    printf("\n");

    tcsetattr(0, TCSANOW, &oldterm);
}



int main(int argc, char* argv[])
{
    if (!ReadCmdLine(argc, argv))
    {
        printf("laserconfig Version 0.4.1\n\n"
               "usage:   laserconfig serial-device [-a | -c][-b baudrate][-s sensitivity]\n\n"
               "options: -a             configure transceiver, then display laser control menu\n"
               "         -c             only display laser control menu\n"
               "         -b baudrate    set baudrate: 19200, 38400, 57600 or 115200 (default)\n"
               "         -s sensitivity set sensitivity: 1 (more) - 7 (less), default 4\n\n"
               "laser control works best at 115200 bps.\n\n"
               "bug reports to: Jan.Kiszka@web.de\n");
        exit(3);
    }

    tty = open(argv[1], O_RDWR);
    if ((tty < 0) || (!isatty(tty)))
    {
        fprintf(stderr, "error opening device %s\n", argv[1]);
        exit(1);
    }

    if (tcgetattr(tty, &settings) < 0)
        CfgError();
    settings.c_iflag = 0;
    settings.c_oflag = 0;
    settings.c_cflag &= ~(CSIZE + CSTOPB + PARENB);
    settings.c_cflag |= CRTSCTS + CS8;
    settings.c_lflag &= ~(ICANON + ECHO + ISIG + IEXTEN);
    settings.c_cc[VTIME] = 0;
    settings.c_cc[VMIN]  = 1;

    if (config)
        ConfigureTransceiver();

    if (laserctrl)
        LaserControl();

    close(tty);
}

