/*!***************************************************************************
*!
*! FILE NAME  : train_c.c
*!
*! DESCRIPTION: User space test program for the train_mod synchronous
*!              serial driver for model railroad train control.
*!
*!              To some parts based on the serial_test.c program by
*!              Acme Systems srl
*!              http://www.acmesystems.it/articles/00035/serial_test.c
*!
*! Version: 0.0, 2007-01-27:   Initial version.
*!
*! Version: 0.1, 2007-02-04:   Added control of GPIO g1 to turn power on/off.
*!
*! Version: 0.2, 2007-02-12:   Added DCC option (only the 3-byte base packet).
*!
*! Version: 0.3, 2008-05-10:   Corrected the STDIN behaviour at program exit.
*!                             Also added some explaining comments in the
*!                             interface to the send function.
*!
*! ---------------------------------------------------------------------------
*!
*! (c) Copyright 2005 Acme Systems srl http://www.acmesystems.it
*! and
*! (C) Copyright 2007-2008 Per Zander, SWEDEN http://home.swipnet.se/perz/
*!
*! ---------------------------------------------------------------------------
*!
*!    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.
*!
*!		To have a copy of the GNU General Public License write to the Free
*!    Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
*!    02110-1301  USA.
*!
*!***************************************************************************/

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <stdarg.h>
#include <signal.h>
#include <ctype.h>
#include <asm/etraxgpio.h>

#include "train_mod.h"

#define NEW_M 0
#define FORWARD 0
#define REVERSE 1
#define F1OFF 2
#define F1ON 3
#define F2OFF 4
#define F2ON 5
#define F3OFF 6
#define F3ON 7
#define F4OFF 8
#define F4ON 9
#define OLD_M 10
#define OLD_REV 11
#define POS_REV 12
#define NEG_REV 13
#define DCC 14

/*----- Prototypes. -----*/

void send(int addr, int func, int speed, int dir_fx, int prot);
int convert_addr(int addr);

/*----- Speed + direction values. -----*/

unsigned char pos[15] = {0x88, 0x8c, 0x8d, 0x98, 0x99, 0x9c, 0x9d, 0x48,
                         0x49, 0x4c, 0x4d, 0x58, 0x59, 0x5c, 0x5d};

unsigned char neg[15] = {0xa2, 0xa6, 0xa7, 0xb2, 0xb3, 0xb6, 0xb7, 0x62,
                         0x63, 0x66, 0x67, 0x72, 0x73, 0x76, 0x77};

/*----- Speed + function values. -----*/

unsigned char f1off[15] = {0x0a, 0x0e, 0x27, 0x1a, 0x1b, 0x1e, 0x1f, 0x4a,
                         0x4b, 0x4e, 0x4f, 0x5a, 0x5b, 0x5e, 0x5f};

unsigned char f1on[15] = {0x8a, 0x8e, 0x8f, 0x9a, 0x9b, 0x9e, 0x9f, 0xca,
                         0xcb, 0xce, 0xcd, 0xda, 0xdb, 0xde, 0xdf};

unsigned char f2off[15] = {0x20, 0x24, 0x25, 0x32, 0x31, 0x34, 0x35, 0x60,
                         0x61, 0x64, 0x65, 0x70, 0x71, 0x74, 0x75};

unsigned char f2on[15] = {0xa0, 0xa4, 0xa5, 0xb0, 0xb1, 0xb4, 0xb5, 0xe0,
                         0xe1, 0xe4, 0xe5, 0xd8, 0xf1, 0xf4, 0xf5};

unsigned char f3off[15] = {0x28, 0x2c, 0x2d, 0x38, 0x39, 0x36, 0x3d, 0x68,
                         0x69, 0x6c, 0x6d, 0x78, 0x79, 0x7c, 0x7d};

unsigned char f3on[15] = {0xa8, 0xac, 0xad, 0xb8, 0xb9, 0xbc, 0xbd, 0xe8,
                         0xe9, 0xec, 0xed, 0xf8, 0xf9, 0xdc, 0xfd};

unsigned char f4off[15] = {0x2a, 0x2e, 0x2f, 0x3a, 0x3b, 0x3e, 0x37, 0x6a,
                         0x6b, 0x6e, 0x6f, 0x7a, 0x7b, 0x7e, 0x7f};

unsigned char f4on[15] = {0xaa, 0xae, 0xaf, 0xba, 0xbb, 0xbe, 0xbf, 0xea,
                         0xeb, 0xee, 0xef, 0xfa, 0xfb, 0xfe, 0xdd};

/*----- Old Motorola format. -----*/

unsigned char old[15] = {0x00, 0x0c, 0x0f, 0x30, 0x33, 0x3c, 0x3f, 0xc0,
                         0xc3, 0xcc, 0xcf, 0xf0, 0xf3, 0xfc, 0xff};

/*----- Explicit reverse commands. -----*/

unsigned char oldrev = 0x03;

unsigned char posrev = 0x89;

unsigned char negrev = 0xa3;

/*----- Standard in, gpio and tty. -----*/

struct termios stdin_saved_attributes;
int tty_fd;
int gpio_fd;

//--------------------------------------------------------------------------
// Open device syncser1.
//--------------------------------------------------------------------------

int tty_open_sser()
{
  tty_fd = open("/dev/syncser1", O_RDWR | O_NOCTTY | O_NONBLOCK);
  if (tty_fd < 0) {
    return -1;
  }
  return tty_fd;
}

//--------------------------------------------------------------------------
// Functions for the gpio device.
//--------------------------------------------------------------------------

//----- Open. -----*

static int gpio_open()
{
  if ((gpio_fd = open("/dev/gpiog", O_RDWR)) < 0) {
    printf("Open error on /dev/gpiog\n");
    return -1;
  }
  return gpio_fd;
}

//----- Set/clear bits. -----*

static void gpio_set (int nr, int val)
{
  switch (val) {
    case 0:  ioctl(gpio_fd, _IO(ETRAXGPIO_IOCTYPE, IO_CLRBITS), 1 << nr); break;
    case 1:  ioctl(gpio_fd, _IO(ETRAXGPIO_IOCTYPE, IO_SETBITS), 1 << nr); break;
    default: printf("Error, bad I/O bit value %d.\n", val);               break;
  }
}

//---- Read bits. -----*

static inline int gpio_read (int nr)
{
  return (ioctl(gpio_fd, _IO(ETRAXGPIO_IOCTYPE, IO_READBITS)) >> nr) & 1;
}

//--------------------------------------------------------------------------
// Some stdin stuff.
//--------------------------------------------------------------------------

int stdin_init(void)
{
  struct termios tattr;

  // Make sure stdin is a terminal

  if (!isatty (STDIN_FILENO)) {
    fprintf (stderr, "stdin is not a terminal\n");
    return -1;
  }

  // Save the terminal attributes so we can restore them later.

  tcgetattr (STDIN_FILENO, &stdin_saved_attributes);

  // Set the terminal modes.

  tcgetattr (STDIN_FILENO, &tattr);
  tattr.c_lflag &= ~(ICANON | ECHO); /* Clear ICANON and ECHO. */
  tattr.c_cc[VMIN] = 0;
  tattr.c_cc[VTIME] = 0;
  tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr);
  return 0;
}

void restore_and_exit ()
{
  tcsetattr ( STDIN_FILENO, TCSANOW, &stdin_saved_attributes);
  close(tty_fd);
  printf("Exit\n");
  exit(0);
}

void termination_handler (int signum)
{
  restore_and_exit();
}

//--------------------------------------------------------------------------
// Address conversion function.
//--------------------------------------------------------------------------

int convert_addr(int addr)
{
  int addr_bits;                /* A4, A3, A2, A1 ; A1 sent first */

  //addr = 18;
  addr_bits = 0;
  if (addr >= 80)
  {
    return (addr_bits);         /* Address 80 is coded as 0. */
  }
  else if (addr >= 54)
  {
    addr -= 54;
    addr_bits = 0x40;
  }
  else if (addr >= 27)
  {
    addr -= 27;
    addr_bits = 0xc0;
  }
  else if (addr == 0)
  {
    return (0x55);              /* Idle address. */
  }

  if (addr >= 18)
  {
    addr -= 18;
    addr_bits |= 0x10;
  }
  else if (addr >= 9)
  {
    addr -= 9;
    addr_bits |= 0x30;
  }

  if (addr >= 6)
  {
    addr -= 6;
    addr_bits |=0x04;
  }
  else if (addr >= 3)
  {
    addr -= 3;
    addr_bits |= 0x0c;
  }

  if (addr >= 2)
  {
    addr_bits |= 0x01;
  }
  else if (addr >= 1)
  {
    addr_bits |= 0x03;
  }

  return (addr_bits);
}

//--------------------------------------------------------------------------
// Send function.
//--------------------------------------------------------------------------

void send(int addr,      // Locomotive address.
          int func,      // Function 0 (typically lights), 0 = off, 1 = on.
          int speed,     // Range 0 - 14 (stop and 14 speed steps)
          int dir_fx,    // Direction or function command:
                         // FORWARD :  Drive with absolute direction forward.
                         // REVERSE :  Drive with absolute direction backward.
                         // F1OFF :    Drive with function 1 off.
                         // F1ON :     Drive with function 1 on.
                         // F2OFF :    Drive with function 2 off.
                         // F2ON :     Drive with function 2 on.
                         // F3OFF :    Drive with function 3 off.
                         // F3ON :     Drive with function 3 on.
                         // F4OFF :    Drive with function 4 off.
                         // F4ON :     Drive with function 4 on.
                         // OLD_M :    Drive with current direction (obsolete).
                         // OLD_REV :  Stop and change direction (almost
                         //            obsolete, but may be the only way to
                         //            change direction with some older
                         //            Märklin locos).
                         // POS_REV :  Stop and set direction forward.
                         // NEG_REV :  Stop and set direction backward.
          int prot)      // Protocol:
                         // DCC :      NMRA DCC standard protocol
                         //            (only speed and no functions supported
                         //            by this program).
                         // OLD_M :    Old Märklin/Motorola protocol
                         //            (obsolete).
                         // NEW_M :    Märklin/Motorola protocol. Use this
                         //            with all Märklin locos until I have
                         //            Implemented mfx.
{
  unsigned char mm_cmd[4];
  unsigned char dfx_val;
  int nr, i;

  if (prot == DCC) {
    mm_cmd[0] = train_cmd_dcc;
    mm_cmd[1] = addr & 0x7f;
    mm_cmd[2] = speed ? speed + 1 : speed;
    switch (dir_fx) {
      case FORWARD: mm_cmd[2] |= 0x60;                              break;
      case REVERSE: mm_cmd[2] |= 0x40;                              break;
      default:      printf("Error, DCC function not supported\n");  return;
    }
    i = 0;
    while ((nr = write(tty_fd, mm_cmd, 3)) != 3) {
      printf ("\nTransmitter busy %d\n", nr);
      i++;
      if (i > 5) {
        printf("Give up\n");
        break;
      }
    }
    printf ("\nSend %x\n", (* ((unsigned int *) &mm_cmd[0])) & 0xffffff);
    return;
  }

  /*----- First byte, command. -----*/

  mm_cmd[0] = train_cmd_mm;

  /*----- Second byte, address. -----*/

  mm_cmd[1] = convert_addr(addr);

  /*----- Function. -----*/

  switch (func)
  {
    case 0: mm_cmd[2] = 0; break;    // Trinary 0. Function off.
    case 1: mm_cmd[2] = 3; break;    // Trinary 1. Function on.
    case 2: mm_cmd[2] = 2; break;    // ESU half speed with function off.
    case 3: mm_cmd[2] = 1; break;    // ESU half speed with function on.
    default: mm_cmd[0] = 'a'; mm_cmd[2] = func & 3; break;   // debug
  }

  /*----- Speed and direction/function. -----*/

  switch (dir_fx)
  {
    case FORWARD: dfx_val = pos[speed];   break;
    case REVERSE: dfx_val = neg[speed];   break;
    case F1OFF:   dfx_val = f1off[speed]; break;
    case F1ON:    dfx_val = f1on[speed];  break;
    case F2OFF:   dfx_val = f2off[speed]; break;
    case F2ON:    dfx_val = f2on[speed];  break;
    case F3OFF:   dfx_val = f3off[speed]; break;
    case F3ON:    dfx_val = f3on[speed];  break;
    case F4OFF:   dfx_val = f4off[speed]; break;
    case F4ON:    dfx_val = f4on[speed];  break;
    case OLD_M:   dfx_val = old[speed];   break;
    case OLD_REV: dfx_val = oldrev;       break;
    case POS_REV: dfx_val = posrev;       break;
    case NEG_REV: dfx_val = negrev;       break;
    default:      dfx_val = pos[0];       break;
  }
  mm_cmd[2] |= dfx_val << 2;
  mm_cmd[3] = dfx_val >> 6;

  /*----- Send the command. -----*/

  i = 0;
  while ((nr = write(tty_fd, mm_cmd, 4)) != 4) {
    printf ("\nTransmitter busy %d\n", nr);
    i++;
    if (i > 5) {
      printf("Give up\n");
      break;
    }
  }
  printf ("\nSend %x\n", * ((unsigned int *) &mm_cmd[0]));
}

//--------------------------------------------------------------------------
// Get character and echo.
//--------------------------------------------------------------------------

unsigned char getche()
{
  unsigned char ch;

  while (read (STDIN_FILENO, &ch, 1) <= 0);
  printf("%c", ch);
  fflush(stdout);
  return ch;
}

//--------------------------------------------------------------------------
// Scan in a decimal value.
//--------------------------------------------------------------------------

int scan_dec() {
  int val;
  int inv;
  unsigned char ch;

  val = 0;
  inv = 0;
  do {
    ch = getche();
  }  while (isspace(ch));
  if (ch == '-') {
    inv = 1;
    ch = getche();
  }
  else if (ch == '+') {
    ch = getche();
  }
  while (isdigit(ch)) {
    val = val * 10;
    val += ch - '0';
    ch = getche();
  }
  if (inv) {
    val = -val;
  }
  return val;
}

//--------------------------------------------------------------------------
// Stop/go functions.
//--------------------------------------------------------------------------

static inline void stop()
{
  gpio_set(1, 0);
}

static inline void go()
{
  gpio_set(1, 1);
}

//--------------------------------------------------------------------------
// Main function.
//--------------------------------------------------------------------------

int main()
 {
  char ch;
  int speed0;
  int speed1;
  int dir0;
  int dir1;
  int dir_fx0;
  int dir_fx1;
  int func0;
  int func1;
  int addr0;
  int addr1;
  int ver0;
  int ver1;
  int f0[4];
  int f1[4];
  int prot0;
  int prot1;
  int i;
  int tmp; 

  speed0 = 0;
  speed1 = 0;
  dir0 = FORWARD;
  dir1 = FORWARD;
  ver0 = NEW_M;     /* Assume new Motorola format as default. */
  ver1 = NEW_M; 
  func0 = 0;        /* Head lights off as default */ 
  func1 = 0;       
  addr0 = 0x0;     /* Idle address as default */
  addr1 = 0x0;
  f0[0] = 0;
  f0[1] = 0;
  f0[2] = 0;
  f0[3] = 0; 
  f1[0] = 0;
  f1[1] = 0;
  f1[2] = 0;
  f1[3] = 0;
  prot0 = NEW_M;
  prot1 = NEW_M;

  printf("Train_mod Test (press ctrl-c to exit)\n");

  if (tty_open_sser() < 0) {
    fprintf (stderr, "tty open error %s\n", strerror(errno));
    exit(EXIT_FAILURE);
  }

  if (gpio_open() < 0) {
  	printf("gpio open error %s\n", strerror(errno));
	  exit(EXIT_FAILURE);
  }

  if (stdin_init() < 0) {
  	printf("stdin init error %s\n", strerror(errno));
	  exit(EXIT_FAILURE);
  }

  if (signal (SIGINT, termination_handler) == SIG_IGN) {
    signal (SIGINT, SIG_IGN);
  }
  if (signal (SIGHUP, termination_handler) == SIG_IGN) {
    signal (SIGHUP, SIG_IGN);
  }
  if (signal (SIGTERM, termination_handler) == SIG_IGN) {
    signal (SIGTERM, SIG_IGN);
  }
  go();

  while (1)
  {
    ch = getche();

    switch (ch)
    {
      case 'a':   /* Set address for loco 0. */
      {
        tmp = addr0;
        printf("\nEnter address for loco 0:  ");
        fflush(stdout);
        addr0 = scan_dec();
        if (addr0 != tmp)              /* Reset if new loco. */
        { 
          speed0 = 0;
          dir0 = FORWARD;
          func0 = 0;
          ver0 = NEW_M;
          prot0 = NEW_M;
        }         
        break;
      }
      case 'A':   /* Set address for loco 1. */
      {
        tmp = addr1; 
        printf("\nEnter address for loco 1:  ");
        fflush(stdout);
        addr1 = scan_dec();
        if (addr1 != tmp)              /* Reset if new loco. */
        { 
          speed1 = 0;
          dir1 = FORWARD;
          func1 = 0;
          ver1 = NEW_M;
          prot0 = NEW_M;
        }         
        break;
      } 
      case 'c':
      {
        ch = getche();
        switch (ch)
        {
          case '0': speed0 = 0; break;
          case '1': speed0 = 1; break;
          case '2': speed0 = 2; break;
          case '3': speed0 = 3; break;
          case '4': speed0 = 4; break;
          case '5': speed0 = 5; break;
          case '6': speed0 = 6; break;
          case '7': speed0 = 7; break;
          case '8': speed0 = 8; break;
          case '9': speed0 = 9; break;
        }
        send(addr0, func0, speed0, dir0, prot0);
        break;
      }  
      case 'C':
      {
        ch = getche();
        switch (ch)
        {
          case '0': speed1 = 0; break;
          case '1': speed1 = 1; break;
          case '2': speed1 = 2; break;
          case '3': speed1 = 3; break;
          case '4': speed1 = 4; break;
          case '5': speed1 = 5; break;
          case '6': speed1 = 6; break;
          case '7': speed1 = 7; break;
          case '8': speed1 = 8; break;
          case '9': speed1 = 9; break;
        }
        send(addr1, func1, speed1, dir1, prot1);
        break;
      }  
 
      case 'q':    /* Quit */ 
      case 'Q':
      case ' ':    /* Emergency stop */  
      {
        stop();
        speed0 = 0;
        speed1 = 0;
        if (dir0 == OLD_M)
        {
          dir_fx0 = OLD_M;
        }
        else
        {
          dir_fx0 = F4ON;
        }
        if (dir1 == OLD_M)
        {
          dir_fx1 = OLD_M;
        }
        else
        {
          dir_fx1 = F4ON;
        }
        for (i = 0; i < 8; i++)
        {
          send(addr0, func0, speed0, dir_fx0, prot0);
          send(addr0, func0, speed0, dir_fx0, prot0);
          send(addr1, func1, speed1, dir_fx1, prot1);
          send(addr1, func1, speed1, dir_fx1, prot1);
          send(addr1, func1, speed1, dir_fx1, prot1);
          send(addr1, func1, speed1, dir_fx1, prot1);
          send(addr0, func0, speed0, dir_fx0, prot0);
          send(addr0, func0, speed0, dir_fx0, prot0);
        }
        if (ch != ' ')
        {
          restore_and_exit();
        } 
        break;
      }
      case 'j':    /* Decrease speed */
      {
        speed0--;
        if (speed0 < 0) 
        {
          speed0 = 0;
        }             
        send(addr0, func0, speed0, dir0, prot0);
        break;
      }
      case 'J':    /* Decrease speed */
      {
        speed1--;
        if (speed1 < 0) 
        {
          speed1 = 0;
        }             
        send(addr1, func1, speed1, dir1, prot1);
        break;
      }
      case 'k':    /* Increase speed */
      {
        speed0++;
        if (speed0 > 14) 
        {
          speed0 = 14;
        }             
        send(addr0, func0, speed0, dir0, prot0);
        break;
      }
      case 'K':    /* Increase speed */
      {
        speed1++;
        if (speed1 > 14) 
        {
          speed1 = 14;
        }             
        send(addr1, func1, speed1, dir1, prot1);
        break;
      }
      case 's':    /* Stop */
      {
        speed0 = 0;
        send(addr0, func0, speed0, dir0, prot0);
        break;
      }
      case 'S':    /* Stop */
      {
        speed1 = 0;    
        send(addr1, func1, speed1, dir1, prot1);
        break;
      }
      case 'r':    /* Reverse */
      {
        speed0 = 0;
        if (dir0 == OLD_M)
        {
          for (i = 0; i < 8; i++)
          {
            send(addr0, func0, speed0, OLD_REV, prot0);
          }
        }  
        if (dir0 == FORWARD)
        {
          dir0 = REVERSE;
        }
        else if (dir0 == REVERSE)
        {
          dir0 = FORWARD;
        }     
        send(addr0, func0, speed0, dir0, prot0);
        break;
      }
      case 'R':    /* Reverse */
      {
        speed1 = 0;
        if (dir1 == OLD_M)
        {
          for (i = 0; i < 8; i++)
          {
            send(addr1, func1, speed1, OLD_REV, prot1);
          }
        }  
        if (dir1 == FORWARD)
        {
          dir1 = REVERSE;
        }
        else if (dir1 == REVERSE)
        {
          dir1 = FORWARD;
        }     
        send(addr1, func1, speed1, dir1, prot1);
        break;
      }
      case 'd':      /* Function off */
      {
        ch = getche();
        switch (ch)
        {
          case '1': f0[0] = 0; dir_fx0 = F1OFF; break;
          case '2': f0[1] = 0; dir_fx0 = F2OFF; break;
          case '3': f0[2] = 0; dir_fx0 = F3OFF; break;
          case '4': f0[3] = 0; dir_fx0 = F4OFF; break;
          default:  func0 = 0; dir_fx0 = dir0; break;
        }
        send(addr0, func0, speed0, dir_fx0, prot0);
        break;     
      }
      case 'D':      /* Function off */
      {
        ch = getche();
        switch (ch)
        {
          case '1': f1[0] = 0; dir_fx1 = F1OFF; break;
          case '2': f1[1] = 0; dir_fx1 = F2OFF; break;
          case '3': f1[2] = 0; dir_fx1 = F3OFF; break;
          case '4': f1[3] = 0; dir_fx1 = F4OFF; break;
          default:  func1 = 0; dir_fx1 = dir1; break;
        }
        send(addr1, func1, speed1, dir_fx1, prot1);
        break;     
      }
      case 'f':      /* Function on */
      {
        ch = getche();
        switch (ch)
        {
          case '1': f0[0] = 1; dir_fx0 = F1ON; break;
          case '2': f0[1] = 1; dir_fx0 = F2ON; break;
          case '3': f0[2] = 1; dir_fx0 = F3ON; break;
          case '4': f0[3] = 1; dir_fx0 = F4ON; break;
          default:  func0 = 1; dir_fx0 = dir0; break;
        }
        send(addr0, func0, speed0, dir_fx0, prot0);
        break;     
      }
      case 'F':      /* Function on */
      {
        ch = getche();
        switch (ch)
        {
          case '1': f1[0] = 1; dir_fx1 = F1ON; break;
          case '2': f1[1] = 1; dir_fx1 = F2ON; break;
          case '3': f1[2] = 1; dir_fx1 = F3ON; break;
          case '4': f1[3] = 1; dir_fx1 = F4ON; break;
          default:  func1 = 1; dir_fx1 = dir1; break;
        }
        send(addr1, func1, speed1, dir_fx1, prot1);
        break;     
      }
      case 'g':      // Go.
      case 'G':
      {
        go();
        break;
      }

      case 'o':
      {
        speed0 = 0;
        dir0 = OLD_M;
        prot0 = OLD_M;
        break;
      }
      case 'O':
      {
        speed1 = 0;
        dir1 = OLD_M;
        dir1 = OLD_M;
        break;
      }
      case 'n':
      {
        speed0 = 0;
        dir0 = FORWARD;
        prot0 = NEW_M;
        break;        
      }
      case 'N':
      {
        speed1 = 0;
        dir1 = FORWARD;
        prot1 = NEW_M;
        break;
      }
      case 'u':
      {
        speed0 = 0;
        dir0 = FORWARD;
        prot0 = DCC;
        break;
      }
      case 'U':
      {
        speed1 = 0;
        dir1 = FORWARD;
        prot1 = DCC;
        break;
      }
      case 'h':              /* Help. */
      case 'H':
      case '?':
      {
        printf("\n");
        printf("Locomotive control program for Märklin/Motorola format.\n");
        printf("The program controls two locomotives at the same time.\n");
        printf("Use lowercase for loco #0 and uppercase for loco #1.\n\n"); 
        printf("Command:      Description:\n\n");
        printf("  a <addr>    Set loco address.\n");
        printf("  c<n>        Set a speed between 0 and 9.\n"); 
        printf("  dx          Clear function.\n");
        printf("  d<n>        Clear F<n>.\n");
        printf("  fx          Set function.\n");
        printf("  f<n>        Set F<n>.\n");
        printf("  g           Go (resume after emergency stop).\n");
        printf("  h           This help.\n");
        printf("  j           Decrease speed.\n");
        printf("  k           Increase speed.\n");
        printf("  n           Set new Motorola format (default).\n");
        printf("  o           Set old Motorola format.\n");
        printf("  q           Quit.\n");
        printf("  r           Reverse direction.\n");
        printf("  s           Stop.\n");
        printf("  u           Set DCC format.\n");
        printf("  v           View current settings.\n");
        printf("<blank>       Emergency stop (both locomotives).\n\n"); 
        break;                 
      }
      case 'v':
      {
        printf("\n");
        printf("Status for locomotive #0:\n\n");
        printf("Address = %d\n", addr0);
        printf("Speed = %d\n", speed0);  
        if (dir0 == FORWARD)
        {
          printf("Direction = forward\n");
        }
        else if (dir0 == REVERSE)
        {
          printf("Direction = reverse\n");
        }
        else
        {
          printf("Old Motorola format used, direction unknown.\n");
        }
        if (func0)
        {
          printf("Function = on\n");
        }
        else
        {
          printf("Function = off\n");
        }
        if (f0[0])
        {
          printf("F1 = on\n");
        }
        else
        {
          printf("F1 = off\n");
        }
        if (f0[1])
        {
          printf("F2 = on\n");
        }
        else
        {
          printf("F2 = off\n");
        }
        if (f0[2])
        {
          printf("F3 = on\n");
        }
        else
        {
          printf("F3 = off\n");
        }
        if (f0[3])
        {
          printf("F4 = on\n");
        }
        else
        {
          printf("F4 = off\n");
        }
        break;
      }
      case 'V':
      {
        printf("\n");
        printf("Status for locomotive #1:\n\n");
        printf("Address = %d\n", addr1);
        printf("Speed = %d\n", speed1);  
        if (dir1 == FORWARD)
        {
          printf("Direction = forward\n");
        }
        else if (dir1 == REVERSE)
        {
          printf("Direction = reverse\n");
        }
        else
        {
          printf("Old Motorola format used, direction unknown.\n");
        }
        if (func1)
        {
          printf("Function = on\n");
        }
        else
        {
          printf("Function = off\n");
        }
        if (f1[0])
        {
          printf("F1 = on\n");
        }
        else
        {
          printf("F1 = off\n");
        }
        if (f1[1])
        {
          printf("F2 = on\n");
        }
        else
        {
          printf("F2 = off\n");
        }
        if (f1[2])
        {
          printf("F3 = on\n");
        }
        else
        {
          printf("F3 = off\n");
        }
        if (f1[3])
        {
          printf("F4 = on\n");
        }
        else
        {
          printf("F4 = off\n");
        }
        break; 
      }
      case 'p':
      {  
        speed0 = 0;
        dir0 = FORWARD;
        send (addr0, func0, speed0, POS_REV, prot0);
        break;
      }
      case 'P':
      { 
        speed1 = 0;
        dir1 = FORWARD;
        send (addr1, func1, speed1, POS_REV, prot1);
        break;
      }
      case 'm':
      {
        speed0 = 0;
        dir0 = REVERSE;
        send (addr0, func0, speed0, NEG_REV, prot0);
        break;
      }
      case 'M':
      {
        speed1 = 0;
        dir1 = REVERSE;
        send (addr1, func1, speed1, NEG_REV, prot1);
        break;
      }
      case '+':
      {
        func0 |= 2;
        send(addr0, func0, speed0,dir0, prot0);
        break;  
      }
      case '-':
      {
        func0 &= 1;
        send(addr0, func0, speed0, dir0, prot0);
        break;
      }
      case 'x': {
        send(0, 7, 0, 0, prot0);
      }
    } 
  }
}








