dslreports logo
 
    All Forums Hot Topics Gallery
spc
uniqs
585

Vig
Thread-safe since 1997
Premium Member
join:2004-03-23
La Jolla, CA

Vig to Steve

Premium Member

to Steve

Re: Linux: Interrupt based on serial port pin

Yes, TIOCMIWAIT is the answer I was looking for. Unfortunately, it seems not to be very well-documented, but it is implemented for the linux flavor I'm using. (CentOS, a stripped-down RedHat distro)

I've got a working proof of concept. I coded up two implementations: one that polls and one that blocks. Running them side by side, I see the blocking call return just when the transition happens on the polling version.

I haven't examined the fine timing, so I have no idea of the timing granularity of this TIOCMIWAIT technique. I don't need incredible precision, so this will work just fine. I suspect it's good enough for any app that doesn't need outstanding precision, but anything needing to be dead on probably isn't implementing on top of linux anyway.

Thanks for the assistance!

Blocking
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/signal.h>
 
#define DEVICE "/dev/ttyS1"
 
// read the current level from DCD pin
int get_dcd_state(int fd)
{
    int serial = 0;
    if(ioctl(fd, TIOCMGET, &serial) < 0)
    {
        printf("ioctl() failed: %d: %s\n", errno, strerror(errno));
        return -1;
    }
 
    return (serial & TIOCM_CAR) ? 1 : 0;
}
 
// sample code for blocking until DCD state changes
int main(int argc, char** argv)
{
    intomode = O_RDONLY;
 
    // open the serial stream
    int fd = open(DEVICE, omode, 0777);
    if(fd < 0)
    {
        printf("open() failed: %d: %s\n", errno, strerror(errno));
        return -1;
    }
 
    printf("Device opened, DCD state: %d\n", get_dcd_state(fd));
 
    // detect DCD changes forever
    int i=0;
    while(1)
    {
        printf("%6d DCD state: %d\n", i++, get_dcd_state(fd));
 
        // block until line changes state
        if(ioctl(fd, TIOCMIWAIT, TIOCM_CAR) < 0)
        {
            printf("ioctl(TIOCMIWAIT) failed: %d: %s\n", errno, strerror(errno));
            return -1;
        }
    }
}
 

Polling
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/signal.h>
 
#define DEVICE "/dev/ttyS1"
 
// read the current level from DCD pin
int get_dcd_state(int fd)
{
    int serial = 0;
    if(ioctl(fd, TIOCMGET, &serial) < 0)
    {
        printf("ioctl() failed: %d: %s\n", errno, strerror(errno));
        return -1;
    }
 
    return (serial & TIOCM_CAR) ? 1 : 0;
}
 
// poll DCD pin state and return when it changes
void poll_for_dcd_transition(int fd, int curr_dcd_state)
{
    int new_dcd_state = get_dcd_state(fd);
    while(new_dcd_state == curr_dcd_state)
    {
        printf("Waiting for DCD transition: %d\n", new_dcd_state);
 
        // arbitrarily wait 10msec between polls
        usleep(10000);
        new_dcd_state = get_dcd_state(fd);
    }
}
 
// sample code for detect DCD transitions via polling
int main(int argc, char** argv)
{
    // open the serial stream
    int fd = open(DEVICE, O_RDONLY, 0777);
    if(fd < 0)
    {
        printf("open() failed: %d: %s\n", errno, strerror(errno));
        return -1;
    }
 
    // detect DCD changes forever
    while(1)
    {
        poll_for_dcd_transition(fd, get_dcd_state(fd));
    }
}