/*
 *  RS-232 Hardware Manipulation Examples
 *  Copyright 2003 by Floyd L. Davidson, floyd@barrow.com
 *
 *  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, 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 details.
 *
 *  morse-serial.c  --  diddle with "modem control lines".
 *
 *  $Id: morse-serial.c,v 1.1.0.2 2004/07/28 17:03:12 floyd Exp floyd $
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/ioctl.h>

int  open_serial_port(char *);
int  send_mark(int);
int  send_space(int);
void mdelay(int);
void dot(int);
void dash(int);
void letter(void);
void word(void);

#define PORT "/dev/ttyS0"
#define MESSAGE "... ___ ...  ... ___ ...  ... ___ ..."
#define INTERVAL  100
int  interval = INTERVAL;

int main(void)
{
  char *msg = MESSAGE;
  int fd;

  if (0 > (fd = open_serial_port(PORT))) {
    fprintf(stderr, "Cannot open %s\n", PORT);
    exit(EXIT_FAILURE);
  }

  while (*msg) {
    switch (*msg++) {
    case '.':  dot(fd); break;
    case '_':  dash(fd); break;
    case ' ':
      if (*msg == ' ') {
	word();
	while (*msg && *msg <= ' ') {
	  ++msg;
	}
      } else {
	letter();
      }
      break;
    }
  }
      
  word();
  close(fd);
  return EXIT_SUCCESS;
}

void
dot(int fd)
{
  send_space(fd);
  mdelay(interval);
  send_mark(fd);
  mdelay(interval);
  printf("dot ");
  fflush(stdout);
}

void
dash(int fd)
{
  send_space(fd);
  mdelay(interval * 3);
  send_mark(fd);
  mdelay(interval);
  printf("dash ");
  fflush(stdout);
}

void
letter(void)
{
  mdelay(interval * 2);
  printf("  ");
  fflush(stdout);
}

void
word(void)
{
  mdelay(interval * 4);
  printf("\n");
  fflush(stdout);
}



int send_space(int fd)
{
  return ioctl(fd, TIOCSBRK, 0);
}  

int send_mark(int fd)
{
  return ioctl(fd, TIOCCBRK, 0);
}

/*
 *  delay in milliseconds
 */
void
mdelay(int milliseconds)
{
  struct timeval        tv;

  if (milliseconds) {
    tv.tv_sec  = 0;
    tv.tv_usec = (1 + milliseconds) * 500;
    select(1, NULL, NULL, NULL, &tv);
  }
}


/*
 * Open a serial port, disregarding  Carrier Detect Status
 *
 *  Returns file descriptor, or -1 on error.
 */
int
open_serial_port(char *port)
{
  int fd, oldflags;
  
    /* O_NONBLOCK allows open even with no carrier detect */
  if (-1 != (fd = open(port, O_RDWR | O_NOCTTY | O_NONBLOCK))) {
    /* clear O_NONBLOCK once the port is open */
    if ((-1 != (oldflags = fcntl(fd, F_GETFL, 0))) &&
	(-1 != fcntl(fd, F_SETFL, oldflags & ~O_NONBLOCK))) {
      /* flush input and output */
      if (0 == tcflush(fd, TCIOFLUSH)) {
	return fd;
      }
    }
  }
  return -1;
}