3

I am implementing a simple program in unix that takes a RS232 input and saves it into a file.

I've used these references: http://en.wikibooks.org/wiki/Serial_Programming/Serial_Linux and http://www.easysw.com/~mike/serial/serial.html

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>

int main(int argc,char** argv)
{
        struct termios tio;
        struct termios stdio;
        int tty_fd;
        fd_set rdset;
        FILE *file;

        unsigned char c;

        memset(&tio,0,sizeof(tio));
        tio.c_iflag=0;
        tio.c_oflag=0;
        tio.c_cflag=CS8|CREAD|CLOCAL;           // 8n1, see termios.h for more information
        tio.c_lflag=0;
        tio.c_cc[VMIN]=1;
        tio.c_cc[VTIME]=5;

        tty_fd=open("/dev/ttyS1", O_RDWR | O_NONBLOCK);      

        speed_t baudrate = 1843200; //termios.h: typedef unsigned long speed_t;
        cfsetospeed(&tio,baudrate);
        cfsetispeed(&tio,baudrate);

        tcsetattr(tty_fd,TCSANOW,&tio);

        file = fopen("out.raw", "wb");      

        while (1)
        {
                if (read(tty_fd,&c,1)>0) {
            fwrite(&c, 1, 1, file);
            fflush(file);
                }
        }

        //close(tty_fd);
}

I've tried at 921'600 bps and at 1'843'200 bps, and it works correctly. However, it does not work if I set-up a non-standard baud rate, for instance 1'382'400 bps.

i.e., this works:

cfsetospeed(&tio,1843200); cfsetispeed(&tio,1843200);

but this doesn't (it gets random data):

cfsetospeed(&tio,1382400); cfsetispeed(&tio,1382400);

What can be the problem?

I've tried with WinXP (using the WIN32 functions CreateFile, SetCommState and ReadFile), and it works correctly (with 1'843'200 bps and also with the non-standard 1'382'400 bps)

ps: if you ask why I need to set-up this non-standard baud-rate, it's because of a special machine that works only at this speed.

Regards, David

dsolimano
  • 8,870
  • 3
  • 48
  • 63
David Portabella
  • 12,390
  • 27
  • 101
  • 182
  • Are the calls to cfset[io]speed returning successfully? It may be a driver limitation and you have some kernel hacking in store. – Karl Bielefeldt Sep 18 '10 at 00:27
  • You didn't specify what flavor of Unix you're developing on, but I looked at the Linux 2.6.32 source and it's definitely hard coded for standard baud rates, apparently to save some bits on the struct that gets passed around. I'd imagine most posixes would be similar, unfortunately. However, serial drivers are relatively easy to write/modify compared to other device drivers. – Karl Bielefeldt Sep 18 '10 at 00:45
  • Hello, I am implementing it for Mac OSX. I've read something here about custom_divisor set to baud_base / your_new_baudrate. however this library seems to be old. http://stackoverflow.com/questions/3192478/specifying-non-standard-baud-rate-for-ftdi-virtual-serial-port-under-linux – David Portabella Sep 18 '10 at 17:18
  • Hi again Karl. You mention that you looked at the Linux 2.6.32 source code and you found that it was hard coded. Where did you find this? I donwload the linux source code, and doing "grep -i -R cfsetospeed *" in that code I do not find anything (only to an unrelated thing arch/um/drivers/slip_user.c). where did you find the implementation of cfsetospeed? thanks, David. – David Portabella Sep 18 '10 at 22:47
  • Maybe the setserial baudrate change trick works for you: http://stackoverflow.com/questions/2997642/php-to-serial-with-weird-baud-rates/3001084#3001084 – Turbo J Sep 18 '10 at 23:49
  • 3
    @DavidPortabella cfsetospeed is part of user space, you wouldn't find that in the kernel directly. But look in the kernel in e.g. drivers/tty/tty_ioctl.c where the standard baud rates are hard coded. There's other [hacks](http://stackoverflow.com/questions/3192478/specifying-non-standard-baud-rate-for-ftdi-virtual-serial-port-under-linux) one can do too. – nos Feb 27 '13 at 18:38

1 Answers1

1

According to mans cfsetospeed accepts macros, B0, B50 , B75 and so on which are not equal to actual baudrate values (B9600 is equal to 15 e.g.). So passing random integer will cause undefined behaviour.

cfsetospeed() sets the output baud rate stored in the termios structure pointed to by termios_p to speed, which must be one of these constants: B0, B50 and so on

yanpas
  • 2,155
  • 1
  • 17
  • 26