Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 14 Aug 2010 18:30:55 +0100
From:      Paul Thornton <prt@prt.org>
To:        freebsd-hackers@freebsd.org
Subject:   Problem detecting and reacting to serial break
Message-ID:  <4C66D2CF.9040408@prt.org>

next in thread | raw e-mail | index | archive | help
Hi,

I'm working on a small piece of code that reads a data stream from the
serial port, putting it into a buffer for processing later - for those
interested, it is receiving DMX512 lighting control data - but I'm
having trouble with the detection of a break condition on the serial
line.  I'd appreciate any suggestions from anyone.

I'm using 8.0-RELEASE with uftdi and ucom driving the serial port.

The format of this data is very simple - a break is sent, followed by
513 bytes of useful information; then after some arbitrary delay another
break, and the next 513 bytes.  So, I think, easy to do this - just wait
for a break and read away.

My problem is that despite setting the port up (as I think) correctly,
the break seems to be ignored at a driver level and it appears as data
byte of 0x00 (what I'd expect to see if IGNBRK was set).  This is no
good as I have no way to know where I am in the chunk of 513 bytes of
data, so can't synchronize at all.

I have the following code setting up my serial port:

        tcgetattr(fd, &options);
        printf("before: c_iflag=%x c_oflag=%x c_cflag=%x c_lflag=%x\n",
	 options.c_iflag, options.c_oflag,
	 options.c_cflag, options.c_lflag);

        options.c_iflag         = (BRKINT | IGNPAR);
        options.c_oflag         = 0;
        options.c_cflag         = (CLOCAL | CREAD | CS8 | CSTOPB);
        options.c_lflag         = 0;
        options.c_cc[VMIN]      = 513;
        options.c_cc[VTIME]     = 0;

        if (cfsetispeed(&options, BAUDRATE) < 0) {
                perror("cfsetispeed");
                exit(1);
        }

        if (cfsetospeed(&options, BAUDRATE) < 0) {
                perror("cfsetospeed");
                exit(1);
        }

        if (tcsetattr(fd, TCSANOW, &options) < 0) {
                perror("tcsetattr");
                exit(1);
        }

        /* debug test - did anything change? */
        tcgetattr(fd, &options);
        printf("after: c_iflag=%x c_oflag=%x c_cflag=%x c_lflag=%x\n",
	 options.c_iflag, options.c_oflag,
	 options.c_cflag, options.c_lflag);

The debug output shows my set is doing what I expect it to:
before: c_iflag=2005 c_oflag=3 c_cflag=cf00 c_lflag=443
after: c_iflag=6 c_oflag=0 c_cflag=8f00 c_lflag=0

So according to the documentation, the effect of the break should be to
flush the input and output buffers, and send a SIGINT to my process. The
buffer doesn't seem to get flushed, and I don't get sent the SIGINT.

The data I'm getting in my buffer is valid, apart from the fact that I
can't tell where byte 1 is - and every time I run the test program the
location of byte 1 changes arbitrarily depending on what was in the
receive buffer.

Have I missed something here, or is there any other way that I can
detect the presence of the break out-of-band?  I cannot make any
assumptions about the 513 bytes that I'm receiving and any combination
of data may be present in there.

Many thanks,

Paul.






Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?4C66D2CF.9040408>