/*
* AUTHOR: Sven Goldt (goldt@math.tu-berlin.de)
*
* 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.
*
*/
//
// miniterm.c
//
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/signal.h>
#define TXRXDEVICE "/dev/ttyS0" // COM1
//#define TXRXDEVICE "/dev/ttyS1" // COM2
#define BAUDRATE B38400 // Las ctes. estan definidas en asm/termbits.h
#define ENDMINITERM 2 // Ctrl+B para salir de miniterm
#define _POSIX_SOURCE 1 // Fuente compatible POSIX
#define FALSE 0
#define TRUE 1
#define STDIN 0
#define STDOUT 1
volatile int STOP = FALSE; // flag de salida
// Esta funcion recibe el numero de senyal que emita el proceso hijo. Como la unica que va a emitir
// es la de finalizar, siempre va a poner STOP a TRUE
void child_handler(int s)
{
STOP = TRUE;
}
// Punto de entrada del programa
int main(int argn, char *argv[])
{
int fd, // Descriptor del dispositivo de E/S
c; // Lectura de caracteres del teclado
struct termios oldDevice, newDevice, oldStdin, newStdin, oldStdout;
struct sigaction sa; // Para manipular el proceso hijo
/*
=================================================
APERTURA DEL DISPOSITIVO DE TRANSMISION/RECEPCION
=================================================
*/
// Abre el dispositivo de transmision para lectura y escritura y no como controlador tty, ya
// que no queremos cortar si el ruido de la linea envia un Ctrl+C
fd = open(TXRXDEVICE, O_RDWR | O_NOCTTY);
if (fd < 0)
{
perror(TXRXDEVICE);
exit(-1);
}
/*
============================================
GUARDAR TODAS LAS CONFIGURACIONES ANTERIORES
============================================
*/
// Lo primero es guardar todas las configuraciones anteriores de los dispositivos
tcgetattr(fd, &oldDevice); // Guarda la configuracion actual del dispositivo de transmision
tcgetattr(STDIN, &oldStdin); // Guarda la cfg de STDIN
tcgetattr(STDOUT, &oldStdout); // Guarda la cfg de STDOUT
/*
===============================
CONFIGURA EL DISPOSITIVO DE E/S
===============================
*/
// Configura el ratio de bps, el flujo de control por hardware y 8n1 (8 bits, sin paridad,
// 1 sopbit). Tambien no colgar automaticamente e ignorar el estado del modem. Finalmente
// habilita la recepcion de caracteres.
newDevice.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
// Ignora bits con errores de paridad
newDevice.c_iflag = IGNPAR;
// Salida
newDevice.c_oflag = 0;
// ,
// No hacer eco con caracteres, ya que si te conectas a un host, el o el modem lo hara por ti.
// No generar senales.
newDevice.c_lflag = 0;
// Bloquea la lectura hasta que un caracter llega
newDevice.c_cc[VMIN]=1;
newDevice.c_cc[VTIME]=0;
// Ahora limpia la linea del modem y activa su configuracion
tcflush(fd, TCIFLUSH);
tcsetattr(fd, TCSANOW, &newDevice); // Aplica la nueva cfg
/*
=======================
CONFIGURACION DE STDOUT
=======================
*/
tcsetattr(STDOUT, TCSANOW, &newDevice); // Configura stdout como si fuera el dispositivo de E/S
/*
======================
CONFIGURACION DE STDIN
======================
*/
tcgetattr(STDIN, &newStdin); // obtengo la cfg de stdin
newStdin.c_lflag &= ~(ICANON | ECHO); // la modifico para detener el eco por pantalla y el buffering
tcsetattr(STDIN, TCSANOW, &newStdin); // y la aplico
/*
=========================
MANEJAR LA ENTRADA/SALIDA
=========================
*/
switch (fork()) // Crea un proceso hijo que es instancia de este. Retorna 0 si estamos en el hijo, -1 si hubo error o el PID del hijo si estamos en el padre
{
case 0: // ==== Si ahora estamos en el hijo ====
//close(STDOUT);
for (c=getchar(); c!=ENDMINITERM; c=getchar()) // Lee los caracteres desde el usauario hasta que se introduzca Ctrl+B (ENDMINITERM)
write(fd,&c,1); // Los va escribiendo en el dispositivo de salida
tcsetattr(fd, TCSANOW, &oldDevice); // Restaura la antigua configuracion del dispositivo de E/S
tcsetattr(STDIN, TCSANOW, &oldStdin); // Restaura la antigua cfg de Stdin
close(fd); // Cierra el dispositivo E/S
exit(0); // envia SIGCHLD al padre
break;
case -1:// ==== Si hubo un error y no fue creada la instancia ====
perror("fork");
tcsetattr(fd, TCSANOW, &oldDevice); // Restaura la cfg del dispositivo
close(fd); // Lo cierra
exit(-1);
default:// ==== Si ahora estamos en el padre ====
//close(STDIN);
sa.sa_handler = child_handler; // Puntero a la funcion de manipulacion de senyales del hijo
sa.sa_flags = 0; // No son necesarias ninguna flag en especial
sigaction(SIGCHLD, &sa, NULL); // Aplica la cfg de la manipulacion del proceso hijo
while (STOP==FALSE) // Mientras que la funcion child_handler no indique que el hijo ha finalizado
{
read(fd, &c, 1); // Lee desde el dispositivo
write(STDOUT, &c, 1); // Lo escribe por pantalla
}
tcsetattr(STDOUT, TCSANOW, &oldStdout); // Restauro la cfg de STDOUT
wait(NULL); // Espera a que el proceso hijo sea matado o quede zombi
break;
}
return 0;
}