2010-09-13 12 views
8

Tengo un dispositivo en serie configurado como loopback (lo que significa que simplemente devolverá el eco de cualquier carácter que reciba), y me gustaría medir la velocidad de rendimiento efectiva. Para ello, esperaba poder utilizar time, como enPrueba de bucle invertido/dúplex en puerto serie, en Bash o C? (sustitución de proceso)

time bash -c '...' 

donde '...' habría algún comando que podía correr.

Ahora, el primer problema es que quiero usar el dispositivo a 2000000 bps, por lo que no puedo usar ttylog o screen (ambos parecen subir a 115200 bps solamente). Sin embargo, el trabajo con /dev/ttyUSB0 como un archivo (usando la redirección de archivos y cat) parece funcionar bien:

# initialize serial port 
stty 2000000 -ixon icanon </dev/ttyUSB0 

# check settings 
stty -a -F /dev/ttyUSB0 

# in one terminal - read from serial port 
while (true) do cat -A /dev/ttyUSB0 ; done 

# in other terminal - write to serial port 
echo "1234567890" > /dev/ttyUSB0 

# back to first terminal, I now have: 
# $ while (true) do cat -A /dev/ttyUSB0 ; done 
# 1234567890$ 
# ... 

Ahora, me gustaría hacer algo similar - Me gustaría cat un archivo a un puerto serie , y hacer que se vuelva a leer el puerto serie, pero desde un solo comando de terminal (para poder usarlo como argumento en time).

pensé que podría utilizar un cambio proceso Bash, para tener la "escritura" y "lectura" de la parte van, más o menos, en "paralelo" - si lo intento con canalizaciones con nombre, funciona:

# mkfifo my.pipe # same as below: 
$ mknod my.pipe p 

$ comm <(echo -e "test\ntest\ntest\n" > my.pipe) <(cat my.pipe) 
    test 
    test 
    test 
comm: file 2 is not in sorted order 

Por arriba, no estoy usando comm para ningún otro fin, que para (tipo de) fusionar los dos procesos en un solo comando (supongo que podría haber usado echo en su lugar).

Por desgracia, no parece que truco para trabajar con un puerto serie, porque cuando lo intento, a veces recibo:

$ comm <(echo "1234567890" > /dev/ttyUSB0) <(while (true) do cat -A /dev/ttyUSB0 ; done) 
cat: /dev/ttyUSB0: Invalid argument 

..., sin embargo, por lo general acaba de obtener ninguna salida en absoluto. Esto me dice que: o bien no hay control de qué proceso se inicia primero, y entonces cat puede comenzar a leer antes de que el puerto esté listo (sin embargo, eso no parece ser un problema en el primer ejemplo anterior); o en Linux/Bash, no puede leer y escribir en un puerto serie al mismo tiempo, por lo que el "Invalid argument" ocurriría en los momentos en que tanto la lectura como la escritura parecen suceder al mismo tiempo.

Así que mis preguntas son:

  • ¿Hay una manera de hacer algo como esto (cat un archivo a un puerto serie configurado como bucle de retorno; leerlo hacia atrás y ver cuánto tiempo tarda) sólo en Bash, sin tener que escribir un programa de C dedicado?
  • Si necesito un programa de C dedicado, ¿algún ejemplo de fuente que pueda usar en la red?

muchas gracias por cualquier respuesta,

Saludos!

 

EDIT: Estoy consciente de que el bucle while escrito arriba no sale; esa línea de comando era para pruebas preliminares, y la interrumpo usando Ctrl-C.(que podría, en principio, de interrupción con algo como timeout -9 0.1 bash -c 'while (true) do echo AA ; done', pero eso sería contrario al propósito de time, entonces :))

La razón de que while está ahí, es que, por el momento, a través de la lectura de la cat el dispositivo sale inmediatamente; a veces, I tiene configurar el dispositivo, de modo que cuando se emite cat, de hecho bloquea y espera los datos entrantes; pero todavía no puedo entender qué está pasando (y en parte es por eso que estoy buscando una forma de probar desde la línea de comandos).

En caso de que no hizo uso de la while, imagino para medir el tiempo, me gustaría usar algo como:

time bash -c 'comm <(echo "1234567890" > /dev/ttyUSB0) <(cat -A /dev/ttyUSB0)'

... sin embargo para que esto se trabaja, más o menos, que asume cat -A /dev/ttyUSB0 comienza primero y bloquea; luego, el echo escribe en el puerto serie (y sale); y luego cat -A saca lo que lee del puerto serie - y luego sale. (Y no estoy seguro de si un puerto serie puede comportarse de esta manera, ni si se puede hacer que cat se bloquee y salga arbitrariamente así).

El método exacto realmente no importa; si es posible, me gustaría evitar codificar mi propio programa C para hacer este tipo de pruebas, por lo que mi principal interés es si es posible de alguna manera ejecutar una "prueba de dúplex completo" usando Bash básico/Linux (es decir, coreutils); (y si no, si hay un código listo para usar que pueda usar para algo como esto).

Edit2: También posiblemente relevante:

+0

Si se va a conseguir que esto funcione, el 'while' bucle sin finaliza. ¿Cómo se propone hacer eso y dónde planea poner el comando 'tiempo'? ¿Has considerado usar tu primer método con 'pv' en el ciclo de lectura? –

+0

Hola Dennis, muchas gracias por tu respuesta! He editado la publicación anterior, ¡espero que aclare los problemas que planteaste! Por cierto, nunca antes había oído hablar de 'pv', supongo que estás hablando de [pv (1): monitorear el progreso de los datos a través de un conducto - página man de Linux] (http://linux.die.net/man/ 1/pv) - Voy a echar un vistazo a esto ... – sdaau

Respuesta

4

Bueno, logró poner writeread.c en una versión roscada utilizando pthread (el código está abajo - I no creo que serial.h haya cambiado mucho; no se usa tanto en la versión roscada de todos modos).También he bajado la velocidad a 115.200, y ahora puedo confirmar estas mediciones con el dispositivo, en el muestreo de la sesión de línea de comandos a continuación:

$ ./writeread /dev/ttyUSB0 115200 writeread.c 3>myout.txt 
stdalt opened; Alternative file descriptor: 3 
Opening port /dev/ttyUSB0; 
Got speed 115200 (4098/0x1002); 
Got file/string 'writeread.c'; opened as file (6131). 
write_thread_function spawned 
    write: 6131 
    read: 18 
    read: 64 
    read: 110 
    read: 156 
    read: 202 
... 
    read: 6066 
    read: 6089 
    read: 6123 
    read: 6131 

+++DONE+++ 
Wrote: 6131 bytes; Read: 6131 bytes; Total: 12262 bytes. 
Start: 1284462824 s 141104 us; End: 1284462824 s 682598 us; Delta: 0 s 541494 us. 
115200 baud for 8N1 is 11520 Bps (bytes/sec). 
Measured: write 11322.38 Bps (98.28%), read 11322.38 Bps (98.28%), total 22644.76 Bps. 

$ diff writeread.c myout.txt 
$ 

Bueno, mediciones informan ahora hasta el 99% de la velocidad de transmisión esperada, así que supongo que eso significa que el aspecto de creación de perfiles de este programa debería funcionar. Aviso:

  • Para este dispositivo, el write se ejecuta en un solo trozo (como el PC debe ser capaz de manejar la secuenciación de los paquetes, si es necesario),
  • mientras que el read pasa en menor trozos (que probablemente indica que el dispositivo no espera a que todo el trozo para llegar - en lugar de comenzar a enviar de vuelta trozos más pequeños, tan pronto como ha recibido suficiente)

Bueno, supongo que esto es lo que necesitaba origi finalmente También creo que probablemente no sea posible organizar cat y echo a través de la sustitución del proceso para ejecutar en esto, llamémoslo "roscado", manera :) (Ahora, tengo un problema con hacer lo mismo a 2000000 baudios, pero eso indica un problema con la programación del dispositivo).

¡Salud!

 

writeread.c-versión roscada

/* 
    writeread.c - based on writeread.cpp 
    [SOLVED] Serial Programming, Write-Read Issue - http://www.linuxquestions.org/questions/programming-9/serial-programming-write-read-issue-822980/ 

    build with: gcc -o writeread -lpthread -Wall -g writeread.c 
*/ 

#include <stdio.h> 
#include <string.h> 
#include <stddef.h> 

#include <stdlib.h> 
#include <sys/time.h> 

#include <pthread.h> 

#include "serial.h" 


int serport_fd; 

//POSIX Threads Programming - https://computing.llnl.gov/tutorials/pthreads/#PassingArguments 
struct write_thread_data{ 
    int fd; 
    char* comm; //string to send 
    int bytesToSend; 
    int writtenBytes; 
}; 

void usage(char **argv) 
{ 
    fprintf(stdout, "Usage:\n"); 
    fprintf(stdout, "%s port baudrate file/string\n", argv[0]); 
    fprintf(stdout, "Examples:\n"); 
    fprintf(stdout, "%s /dev/ttyUSB0 115200 /path/to/somefile.txt\n", argv[0]); 
    fprintf(stdout, "%s /dev/ttyUSB0 115200 \"some text test\"\n", argv[0]); 
} 

// POSIX threads explained - http://www.ibm.com/developerworks/library/l-posix1.html 
// instead of writeport 
void *write_thread_function(void *arg) { 
    int lastBytesWritten; 
    struct write_thread_data *my_data; 
    my_data = (struct write_thread_data *) arg; 

    fprintf(stdout, "write_thread_function spawned\n"); 

    my_data->writtenBytes = 0; 
    while(my_data->writtenBytes < my_data->bytesToSend) 
    { 
     lastBytesWritten = write(my_data->fd, my_data->comm + my_data->writtenBytes, my_data->bytesToSend - my_data->writtenBytes); 
     my_data->writtenBytes += lastBytesWritten; 
     if (lastBytesWritten < 0) 
     { 
      fprintf(stdout, "write failed!\n"); 
      return 0; 
     } 
     fprintf(stderr, " write: %d - %d\n", lastBytesWritten, my_data->writtenBytes); 
    } 
    return NULL; //pthread_exit(NULL) 
} 

int main(int argc, char **argv) 
{ 

    if(argc != 4) { 
     usage(argv); 
     return 1; 
    } 

    char *serport; 
    char *serspeed; 
    speed_t serspeed_t; 
    char *serfstr; 
    int serf_fd; // if < 0, then serfstr is a string 
    int sentBytes; 
    int readChars; 
    int recdBytes, totlBytes; 

    char* sResp; 
    char* sRespTotal; 

    struct timeval timeStart, timeEnd, timeDelta; 
    float deltasec, expectBps, measReadBps, measWriteBps; 

    struct write_thread_data wrdata; 
    pthread_t myWriteThread; 

    /* Re: connecting alternative output stream to terminal - 
    * http://coding.derkeiler.com/Archive/C_CPP/comp.lang.c/2009-01/msg01616.html 
    * send read output to file descriptor 3 if open, 
    * else just send to stdout 
    */ 
    FILE *stdalt; 
    if(dup2(3, 3) == -1) { 
     fprintf(stdout, "stdalt not opened; "); 
     stdalt = fopen("/dev/tty", "w"); 
    } else { 
     fprintf(stdout, "stdalt opened; "); 
     stdalt = fdopen(3, "w"); 
    } 
    fprintf(stdout, "Alternative file descriptor: %d\n", fileno(stdalt)); 

    // Get the PORT name 
    serport = argv[1]; 
    fprintf(stdout, "Opening port %s;\n", serport); 

    // Get the baudrate 
    serspeed = argv[2]; 
    serspeed_t = string_to_baud(serspeed); 
    fprintf(stdout, "Got speed %s (%d/0x%x);\n", serspeed, serspeed_t, serspeed_t); 

    //Get file or command; 
    serfstr = argv[3]; 
    serf_fd = open(serfstr, O_RDONLY); 
    fprintf(stdout, "Got file/string '%s'; ", serfstr); 
    if (serf_fd < 0) { 
     wrdata.bytesToSend = strlen(serfstr); 
     wrdata.comm = serfstr; //pointer already defined 
     fprintf(stdout, "interpreting as string (%d).\n", wrdata.bytesToSend); 
    } else { 
     struct stat st; 
     stat(serfstr, &st); 
     wrdata.bytesToSend = st.st_size; 
     wrdata.comm = (char *)calloc(wrdata.bytesToSend, sizeof(char)); 
     read(serf_fd, wrdata.comm, wrdata.bytesToSend); 
     fprintf(stdout, "opened as file (%d).\n", wrdata.bytesToSend); 
    } 

    sResp = (char *)calloc(wrdata.bytesToSend, sizeof(char)); 
    sRespTotal = (char *)calloc(wrdata.bytesToSend, sizeof(char)); 

    // Open and Initialise port 
    serport_fd = open(serport, O_RDWR | O_NOCTTY | O_NONBLOCK); 
    if (serport_fd < 0) { perror(serport); return 1; } 
    initport(serport_fd, serspeed_t); 

    wrdata.fd = serport_fd; 

    sentBytes = 0; recdBytes = 0; 

    gettimeofday(&timeStart, NULL); 

    // start the thread for writing.. 
    if (pthread_create(&myWriteThread, NULL, write_thread_function, (void *) &wrdata)) { 
     printf("error creating thread."); 
     abort(); 
    } 

    // run read loop 
    while (recdBytes < wrdata.bytesToSend) 
    { 

     while (wait_flag == TRUE); 

     if ((readChars = read(serport_fd, sResp, wrdata.bytesToSend)) >= 0) 
     { 
      //~ fprintf(stdout, "InVAL: (%d) %s\n", readChars, sResp); 
      // binary safe - add sResp chunk to sRespTotal 
      memmove(sRespTotal+recdBytes, sResp+0, readChars*sizeof(char)); 
      /* // text safe, but not binary: 
      sResp[readChars] = '\0'; 
      fprintf(stdalt, "%s", sResp); 
      */ 
      recdBytes += readChars; 
     } else { 
      if (errno == EAGAIN) 
      { 
       fprintf(stdout, "SERIAL EAGAIN ERROR\n"); 
       return 0; 
      } 
      else 
      { 
       fprintf(stdout, "SERIAL read error: %d = %s\n", errno , strerror(errno)); 
       return 0; 
      }   
     } 
     fprintf(stderr, " read: %d\n", recdBytes);   

     wait_flag = TRUE; // was == 
     //~ usleep(50000); 
    } 

    if (pthread_join (myWriteThread, NULL)) { 
     printf("error joining thread."); 
     abort(); 
    } 

    gettimeofday(&timeEnd, NULL); 

    // binary safe - dump sRespTotal to stdalt 
    fwrite(sRespTotal, sizeof(char), recdBytes, stdalt); 

    // Close the open port 
    close(serport_fd); 
    if (!(serf_fd < 0)) { 
     close(serf_fd); 
     free(wrdata.comm); 
    } 
    free(sResp); 
    free(sRespTotal); 

    fprintf(stdout, "\n+++DONE+++\n"); 

    sentBytes = wrdata.writtenBytes; 
    totlBytes = sentBytes + recdBytes; 
    timeval_subtract(&timeDelta, &timeEnd, &timeStart); 
    deltasec = timeDelta.tv_sec+timeDelta.tv_usec*1e-6; 
    expectBps = atoi(serspeed)/10.0f; 
    measWriteBps = sentBytes/deltasec; 
    measReadBps = recdBytes/deltasec; 

    fprintf(stdout, "Wrote: %d bytes; Read: %d bytes; Total: %d bytes. \n", sentBytes, recdBytes, totlBytes); 
    fprintf(stdout, "Start: %ld s %ld us; End: %ld s %ld us; Delta: %ld s %ld us. \n", timeStart.tv_sec, timeStart.tv_usec, timeEnd.tv_sec, timeEnd.tv_usec, timeDelta.tv_sec, timeDelta.tv_usec); 
    fprintf(stdout, "%s baud for 8N1 is %d Bps (bytes/sec).\n", serspeed, (int)expectBps); 
    fprintf(stdout, "Measured: write %.02f Bps (%.02f%%), read %.02f Bps (%.02f%%), total %.02f Bps.\n", measWriteBps, (measWriteBps/expectBps)*100, measReadBps, (measReadBps/expectBps)*100, totlBytes/deltasec); 

    return 0; 
} 
+0

De este código no es obvio cómo se configura el loopback interno. ¿Alguna vez has usado * TCIOM_LOOP *? – 0andriy

5

Bueno, aquí es algo así como una respuesta parcial - aunque la pregunta sobre el uso de bash es todavía abierto. Traté de ver un poco en algunas soluciones de código C, y eso, parece, ¡tampoco es trivial! :)

En primer lugar, vamos a ver lo que posiblemente no trabajo para este caso - a continuación es un ejemplo de "between write and read:serial port. - C":

// from: between write and read:serial port. - C - http://www.daniweb.com/forums/thread286634.html 
// gcc -o sertest -Wall -g sertest.c 

#include <stdio.h> 
#include <sys/types.h> 
#include <string.h> 
#include <unistd.h> 
#include <fcntl.h> 
#include <errno.h> 
#include <termios.h> 

int main(int argc, char *argv[]) 
{ 
    char line[1024]; 
    int chkin; 
    char input[1024]; 
    char msg[1024]; 
    char serport[24]; 

    // argv[1] - serial port 
    // argv[2] - file or echo 

    sprintf(serport, "%s", argv[1]); 

    int file= open(serport, O_RDWR | O_NOCTTY | O_NDELAY); 

    if (file == 0) 
    { 
     sprintf(msg, "open_port: Unable to open %s.\n", serport); 
     perror(msg); 
    } 
    else 
     fcntl(file, F_SETFL, FNDELAY); //fcntl(file, F_SETFL, 0); 

    while (1) 
    { 

     printf("enter input data:\n"); 
     scanf("%s",&input[0]); 

     chkin=write(file,input,sizeof input); 

     if (chkin<0) 
     { 
      printf("cannot write to port\n"); 
     } 

     //chkin=read(file,line,sizeof line); 

     while ((chkin=read(file,line,sizeof line))>=0) 
     { 
      if (chkin<0) 
      { 
       printf("cannot read from port\n"); 
      } 
      else 
      { 
       printf("bytes: %d, line=%s\n",chkin, line); 
      } 
     } 

     /*CODE TO EXIT THE LOOP GOES HERE*/ 
     if (input[0] == 'q') break; 
    } 

    close(file); 
    return 0; 
} 

El problema con el código anterior es que no lo hace inicialice explícitamente el puerto serie para la operación de caracteres ("en bruto"); por lo que dependiendo de cómo el puerto se estableció anteriormente, una sesión puede tener este aspecto:

$ ./sertest /dev/ttyUSB0 
enter input data: 
t1 
enter input data: 
t2 
enter input data: 
t3 
enter input data: 
^C 

... en otras palabras, no hay eco de los datos de entrada. Sin embargo, si el puerto serie está configurado correctamente, podemos obtener una sesión como:

$ ./sertest /dev/ttyUSB0 
enter input data: 
t1 
enter input data: 
t2 
bytes: 127, line=t1 
enter input data: 
t3 
bytes: 127, line=t2 
enter input data: 
t4 
bytes: 127, line=t3 
enter input data: 
^C 

... (pero incluso entonces, este código sertest falla en palabras de entrada de más de 3 caracteres.)

Finalmente, a través de algunas excavaciones en línea, logré encontrar "(SOLVED) Serial Programming, Write-Read Issue", que ofrece un ejemplo de writeread.cpp.Sin embargo, para este caso "dúplex" byte a byte, ni siquiera eso era suficiente, es decir, "Serial Programming HOWTO" notas: "Procesamiento de entrada canónico ... es el modo de procesamiento normal para terminales ... lo que significa que una lectura solo devolverá una línea completa de entrada. Una línea es terminada por defecto por un NL (ASCII LF) ... "; y por lo tanto tenemos que explícitamente configurar el puerto serie para "no canónica" (o "cruda") el modo en nuestro código a través de ICANON (en otras palabras, estaba poniendo O_NONBLOCK través open es no suficiente) - una ejemplo para eso se da en "3.2 How can I read single characters from the terminal? - Unix Programming Frequently Asked Questions - 3. Terminal I/O". Una vez hecho esto, llamando al writeread configurará "correctamente" el puerto serie para el ejemplo serport (arriba), también.

Así que cambió un poco de ese código writeread de nuevo a C, añade el material necesario inicialización, así como la medición del tiempo, la posibilidad de enviar secuencias o los archivos, y el flujo de salida adicional (de 'tuberías' los datos en serie leído a un archivo separado). El código está por debajo de lo writeread.c y serial.h, y con ella, puedo hacer algo como en la siguiente sesión Bash:

$ ./writeread /dev/ttyUSB0 2000000 writeread.c 3>myout.txt 
stdalt opened; Alternative file descriptor: 3 
Opening port /dev/ttyUSB0; 
Got speed 2000000 (4107/0x100b); 
Got file/string 'writeread.c'; opened as file (4182). 

+++DONE+++ 
Wrote: 4182 bytes; Read: 4182 bytes; Total: 8364 bytes. 
Start: 1284422340 s 443302 us; End: 1284422347 s 786999 us; Delta: 7 s 343697 us. 
2000000 baud for 8N1 is 200000 Bps (bytes/sec). 
Measured: write 569.47 Bps, read 569.47 Bps, total 1138.94 Bps. 

$ diff writeread.c myout.txt 

$ ./writeread /dev/ttyUSB0 2000000 writeread.c 3>/dev/null 
stdalt opened; Alternative file descriptor: 3 
Opening port /dev/ttyUSB0; 
Got speed 2000000 (4107/0x100b); 
Got file/string 'writeread.c'; opened as file (4182). 

+++DONE+++ 
Wrote: 4182 bytes; Read: 4182 bytes; Total: 8364 bytes. 
Start: 1284422380 s -461710 us; End: 1284422388 s 342977 us; Delta: 8 s 804687 us. 
2000000 baud for 8N1 is 200000 Bps (bytes/sec). 
Measured: write 474.97 Bps, read 474.97 Bps, total 949.95 Bps. 

Bueno:

  • primera sorpresa - que va más rápido si estoy escribiendo a un archivo, que si estoy conectando a /dev/null!
  • ¡También, alrededor de 1000 Bps, mientras que el dispositivo aparentemente está configurado para 200000 BPS!

En este punto, pienso que la desaceleración se debe a que después de cada byte escrito en writeread.c, esperamos a que una bandera para ser aprobados por la interrupción de lectura, antes de proceder a leer el buffer de serie. Posiblemente, si la lectura y la escritura fueran hilos separados, entonces tanto la lectura como la escritura podrían intentar usar bloques de bytes más grandes en llamadas simples read o write, y así el ancho de banda se usaría mejor? (O, tal vez el controlador de interrupción hace acto, en cierto sentido, como un "hilo" en paralelo - así que tal vez algo similar podría lograrse moviendo todas las funciones relacionadas con la lectura al controlador de interrupción)

Ah Bien, en este punto, estoy muy abierto a sugerencias/enlaces para código existente como writeread.c, pero multiproceso :) Y, por supuesto, para cualquier otra posible herramienta Linux, o posiblemente métodos Bash (aunque parece que Bash no podrá ejercer este tipo de control ...)

¡Salud!

 

writeread.c:

/* 
    writeread.c - based on writeread.cpp 
    [SOLVED] Serial Programming, Write-Read Issue - http://www.linuxquestions.org/questions/programming-9/serial-programming-write-read-issue-822980/ 

    build with: gcc -o writeread -Wall -g writeread.c 
*/ 

#include <stdio.h> 
#include <string.h> 
#include <stddef.h> 

#include <stdlib.h> 
#include <sys/time.h> 

#include "serial.h" 


int serport_fd; 

void usage(char **argv) 
{ 
    fprintf(stdout, "Usage:\n"); 
    fprintf(stdout, "%s port baudrate file/string\n", argv[0]); 
    fprintf(stdout, "Examples:\n"); 
    fprintf(stdout, "%s /dev/ttyUSB0 115200 /path/to/somefile.txt\n", argv[0]); 
    fprintf(stdout, "%s /dev/ttyUSB0 115200 \"some text test\"\n", argv[0]); 
} 


int main(int argc, char **argv) 
{ 

    if(argc != 4) { 
     usage(argv); 
     return 1; 
    } 

    char *serport; 
    char *serspeed; 
    speed_t serspeed_t; 
    char *serfstr; 
    int serf_fd; // if < 0, then serfstr is a string 
    int bytesToSend; 
    int sentBytes; 
    char byteToSend[2]; 
    int readChars; 
    int recdBytes, totlBytes; 

    char sResp[11]; 

    struct timeval timeStart, timeEnd, timeDelta; 
    float deltasec; 

    /* Re: connecting alternative output stream to terminal - 
    * http://coding.derkeiler.com/Archive/C_CPP/comp.lang.c/2009-01/msg01616.html 
    * send read output to file descriptor 3 if open, 
    * else just send to stdout 
    */ 
    FILE *stdalt; 
    if(dup2(3, 3) == -1) { 
     fprintf(stdout, "stdalt not opened; "); 
     stdalt = fopen("/dev/tty", "w"); 
    } else { 
     fprintf(stdout, "stdalt opened; "); 
     stdalt = fdopen(3, "w"); 
    } 
    fprintf(stdout, "Alternative file descriptor: %d\n", fileno(stdalt)); 

    // Get the PORT name 
    serport = argv[1]; 
    fprintf(stdout, "Opening port %s;\n", serport); 

    // Get the baudrate 
    serspeed = argv[2]; 
    serspeed_t = string_to_baud(serspeed); 
    fprintf(stdout, "Got speed %s (%d/0x%x);\n", serspeed, serspeed_t, serspeed_t); 

    //Get file or command; 
    serfstr = argv[3]; 
    serf_fd = open(serfstr, O_RDONLY); 
    fprintf(stdout, "Got file/string '%s'; ", serfstr); 
    if (serf_fd < 0) { 
     bytesToSend = strlen(serfstr); 
     fprintf(stdout, "interpreting as string (%d).\n", bytesToSend); 
    } else { 
     struct stat st; 
     stat(serfstr, &st); 
     bytesToSend = st.st_size; 
     fprintf(stdout, "opened as file (%d).\n", bytesToSend); 
    } 


    // Open and Initialise port 
    serport_fd = open(serport, O_RDWR | O_NOCTTY | O_NONBLOCK); 
    if (serport_fd < 0) { perror(serport); return 1; } 
    initport(serport_fd, serspeed_t); 

    sentBytes = 0; recdBytes = 0; 
    byteToSend[0]='x'; byteToSend[1]='\0'; 
    gettimeofday(&timeStart, NULL); 

    // write/read loop - interleaved (i.e. will always write 
    // one byte at a time, before 'emptying' the read buffer) 
    while (sentBytes < bytesToSend) 
    { 
     // read next byte from input... 
     if (serf_fd < 0) { //interpreting as string 
      byteToSend[0] = serfstr[sentBytes]; 
     } else { //opened as file 
      read(serf_fd, &byteToSend[0], 1); 
     } 

     if (!writeport(serport_fd, byteToSend)) { 
      fprintf(stdout, "write failed\n"); 
     } 
     //~ fprintf(stdout, "written:%s\n", byteToSend); 

     while (wait_flag == TRUE); 

     if ((readChars = readport(serport_fd, sResp, 10)) >= 0) 
     { 
      //~ fprintf(stdout, "InVAL: (%d) %s\n", readChars, sResp); 
      recdBytes += readChars; 
      fprintf(stdalt, "%s", sResp); 
     } 

     wait_flag = TRUE; // was == 
     //~ usleep(50000); 
     sentBytes++; 
    } 

    gettimeofday(&timeEnd, NULL); 

    // Close the open port 
    close(serport_fd); 
    if (!(serf_fd < 0)) close(serf_fd); 

    fprintf(stdout, "\n+++DONE+++\n"); 

    totlBytes = sentBytes + recdBytes; 
    timeval_subtract(&timeDelta, &timeEnd, &timeStart); 
    deltasec = timeDelta.tv_sec+timeDelta.tv_usec*1e-6; 

    fprintf(stdout, "Wrote: %d bytes; Read: %d bytes; Total: %d bytes. \n", sentBytes, recdBytes, totlBytes); 
    fprintf(stdout, "Start: %ld s %ld us; End: %ld s %ld us; Delta: %ld s %ld us. \n", timeStart.tv_sec, timeStart.tv_usec, timeEnd.tv_sec, timeEnd.tv_usec, timeDelta.tv_sec, timeDelta.tv_usec); 
    fprintf(stdout, "%s baud for 8N1 is %d Bps (bytes/sec).\n", serspeed, atoi(serspeed)/10); 
    fprintf(stdout, "Measured: write %.02f Bps, read %.02f Bps, total %.02f Bps.\n", sentBytes/deltasec, recdBytes/deltasec, totlBytes/deltasec); 

    return 0; 
} 

serial.h:

/* serial.h 
    (C) 2004-5 Captain http://www.captain.at 

    Helper functions for "ser" 

    Used for testing the PIC-MMC test-board 
    http://www.captain.at/electronic-index.php 
*/ 

#include <stdio.h> /* Standard input/output definitions */ 
#include <string.h> /* String function definitions */ 
#include <unistd.h> /* UNIX standard function definitions */ 
#include <fcntl.h> /* File control definitions */ 
#include <errno.h> /* Error number definitions */ 
#include <termios.h> /* POSIX terminal control definitions */ 
#include <sys/signal.h> 
#include <sys/stat.h> 
#include <sys/types.h> 

#define TRUE 1 
#define FALSE 0 

int wait_flag = TRUE; // TRUE while no signal received 

// Definition of Signal Handler 
void DAQ_signal_handler_IO (int status) 
{ 
    //~ fprintf(stdout, "received SIGIO signal %d.\n", status); 
    wait_flag = FALSE; 
} 


int writeport(int fd, char *comm) 
{ 
    int len = strlen(comm); 
    int n = write(fd, comm, len); 

    if (n < 0) 
    { 
     fprintf(stdout, "write failed!\n"); 
     return 0; 
    } 

    return n; 
} 


int readport(int fd, char *resp, size_t nbyte) 
{ 
    int iIn = read(fd, resp, nbyte); 
    if (iIn < 0) 
    { 
     if (errno == EAGAIN) 
     { 
      fprintf(stdout, "SERIAL EAGAIN ERROR\n"); 
      return 0; 
     } 
     else 
     { 
      fprintf(stdout, "SERIAL read error: %d = %s\n", errno , strerror(errno)); 
      return 0; 
     } 
    } 

    if (resp[iIn-1] == '\r') 
     resp[iIn-1] = '\0'; 
    else 
     resp[iIn] = '\0'; 

    return iIn; 
} 


int getbaud(int fd) 
{ 
    struct termios termAttr; 
    int inputSpeed = -1; 
    speed_t baudRate; 
    tcgetattr(fd, &termAttr); 
    // Get the input speed 
    baudRate = cfgetispeed(&termAttr); 
    switch (baudRate) 
    { 
     case B0:  inputSpeed = 0; break; 
     case B50:  inputSpeed = 50; break; 
     case B110: inputSpeed = 110; break; 
     case B134: inputSpeed = 134; break; 
     case B150: inputSpeed = 150; break; 
     case B200: inputSpeed = 200; break; 
     case B300: inputSpeed = 300; break; 
     case B600: inputSpeed = 600; break; 
     case B1200: inputSpeed = 1200; break; 
     case B1800: inputSpeed = 1800; break; 
     case B2400: inputSpeed = 2400; break; 
     case B4800: inputSpeed = 4800; break; 
     case B9600: inputSpeed = 9600; break; 
     case B19200: inputSpeed = 19200; break; 
     case B38400: inputSpeed = 38400; break; 
     case B115200: inputSpeed = 115200; break; 
     case B2000000: inputSpeed = 2000000; break; //added 
    } 
    return inputSpeed; 
} 


/* ser.c 
    (C) 2004-5 Captain http://www.captain.at 

    Sends 3 characters (ABC) via the serial port (/dev/ttyS0) and reads 
    them back if they are returned from the PIC. 

    Used for testing the PIC-MMC test-board 
    http://www.captain.at/electronic-index.php 

*/ 


int initport(int fd, speed_t baudRate) 
{ 
    struct termios options; 
    struct sigaction saio; // Definition of Signal action 

    // Install the signal handler before making the device asynchronous 
    saio.sa_handler = DAQ_signal_handler_IO; 
    saio.sa_flags = 0; 
    saio.sa_restorer = NULL; 
    sigaction(SIGIO, &saio, NULL); 

    // Allow the process to receive SIGIO 
    fcntl(fd, F_SETOWN, getpid()); 
    // Make the file descriptor asynchronous (the manual page says only 
    // O_APPEND and O_NONBLOCK, will work with F_SETFL...) 
    fcntl(fd, F_SETFL, FASYNC); 
    //~ fcntl(fd, F_SETFL, FNDELAY); //doesn't work; //fcntl(file, F_SETFL, 0); 

    // Get the current options for the port... 
    tcgetattr(fd, &options); 
/*  
    // Set port settings for canonical input processing 
    options.c_cflag = BAUDRATE | CRTSCTS | CLOCAL | CREAD; 
    options.c_iflag = IGNPAR | ICRNL; 
    //options.c_iflag = IGNPAR; 
    options.c_oflag = 0; 
    options.c_lflag = ICANON; 
    //options.c_lflag = 0; 
    options.c_cc[VMIN] = 0; 
    options.c_cc[VTIME] = 0; 
*/ 
    /* ADDED - else 'read' will not return, unless it sees LF '\n' !!!! 
    * From: Unix Programming Frequently Asked Questions - 3. Terminal I/O - 
    * http://www.steve.org.uk/Reference/Unix/faq_4.html 
    */ 
    /* Disable canonical mode, and set buffer size to 1 byte */ 
    options.c_lflag &= (~ICANON); 
    options.c_cc[VTIME] = 0; 
    options.c_cc[VMIN] = 1; 

    // Set the baud rates to... 
    cfsetispeed(&options, baudRate); 
    cfsetospeed(&options, baudRate); 

    // Enable the receiver and set local mode... 
    options.c_cflag |= (CLOCAL | CREAD); 
    options.c_cflag &= ~PARENB; 
    options.c_cflag &= ~CSTOPB; 
    options.c_cflag &= ~CSIZE; 
    options.c_cflag |= CS8; 

    // Flush the input & output... 
    tcflush(fd, TCIOFLUSH); 

    // Set the new options for the port... 
    tcsetattr(fd, TCSANOW, &options); 

    return 1; 
} 


/* 
    ripped from 
    http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/stty.c 
*/ 

#define STREQ(a, b)  (strcmp((a), (b)) == 0) 

struct speed_map 
{ 
    const char *string;  /* ASCII representation. */ 
    speed_t speed;  /* Internal form. */ 
    unsigned long int value; /* Numeric value. */ 
}; 

static struct speed_map const speeds[] = 
{ 
    {"0", B0, 0}, 
    {"50", B50, 50}, 
    {"75", B75, 75}, 
    {"110", B110, 110}, 
    {"134", B134, 134}, 
    {"134.5", B134, 134}, 
    {"150", B150, 150}, 
    {"200", B200, 200}, 
    {"300", B300, 300}, 
    {"600", B600, 600}, 
    {"1200", B1200, 1200}, 
    {"1800", B1800, 1800}, 
    {"2400", B2400, 2400}, 
    {"4800", B4800, 4800}, 
    {"9600", B9600, 9600}, 
    {"19200", B19200, 19200}, 
    {"38400", B38400, 38400}, 
    {"exta", B19200, 19200}, 
    {"extb", B38400, 38400}, 
#ifdef B57600 
    {"57600", B57600, 57600}, 
#endif 
#ifdef B115200 
    {"115200", B115200, 115200}, 
#endif 
#ifdef B230400 
    {"230400", B230400, 230400}, 
#endif 
#ifdef B460800 
    {"460800", B460800, 460800}, 
#endif 
#ifdef B500000 
    {"500000", B500000, 500000}, 
#endif 
#ifdef B576000 
    {"576000", B576000, 576000}, 
#endif 
#ifdef B921600 
    {"921600", B921600, 921600}, 
#endif 
#ifdef B1000000 
    {"1000000", B1000000, 1000000}, 
#endif 
#ifdef B1152000 
    {"1152000", B1152000, 1152000}, 
#endif 
#ifdef B1500000 
    {"1500000", B1500000, 1500000}, 
#endif 
#ifdef B2000000 
    {"2000000", B2000000, 2000000}, 
#endif 
#ifdef B2500000 
    {"2500000", B2500000, 2500000}, 
#endif 
#ifdef B3000000 
    {"3000000", B3000000, 3000000}, 
#endif 
#ifdef B3500000 
    {"3500000", B3500000, 3500000}, 
#endif 
#ifdef B4000000 
    {"4000000", B4000000, 4000000}, 
#endif 
    {NULL, 0, 0} 
}; 

static speed_t 
string_to_baud (const char *arg) 
{ 
    int i; 

    for (i = 0; speeds[i].string != NULL; ++i) 
    if (STREQ (arg, speeds[i].string)) 
     return speeds[i].speed; 
    return (speed_t) -1; 
} 



/* http://www.gnu.org/software/libtool/manual/libc/Elapsed-Time.html 
Subtract the `struct timeval' values X and Y, 
storing the result in RESULT. 
Return 1 if the difference is negative, otherwise 0. */ 
int timeval_subtract (struct timeval *result, struct timeval *x, struct timeval *y) 
{ 
    /* Perform the carry for the later subtraction by updating y. */ 
    if (x->tv_usec < y->tv_usec) { 
    int nsec = (y->tv_usec - x->tv_usec)/1000000 + 1; 
    y->tv_usec -= 1000000 * nsec; 
    y->tv_sec += nsec; 
    } 
    if (x->tv_usec - y->tv_usec > 1000000) { 
    int nsec = (x->tv_usec - y->tv_usec)/1000000; 
    y->tv_usec += 1000000 * nsec; 
    y->tv_sec -= nsec; 
    } 

    /* Compute the time remaining to wait. 
     tv_usec is certainly positive. */ 
    result->tv_sec = x->tv_sec - y->tv_sec; 
    result->tv_usec = x->tv_usec - y->tv_usec; 

    /* Return 1 if result is negative. */ 
    return x->tv_sec < y->tv_sec; 
} 
Cuestiones relacionadas