[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

Modem handling example

 
/* issue some simple modem commands
 * requires the name of a serial device (preferably a dial-out device,
 * or a non-modem-control device) as its only parameter.
 * If you don't have functional dial-out devices, then move CLOCAL
 * to CFLAGS_TO_SET instead.
 */

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>   /* maybe; system-dependent */
#include <termios.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>

#define CFLAGS_TO_SET (CREAD | HUPCL)
#define CFLAGS_TO_CLEAR (CSTOPB | PARENB | CLOCAL)

enum flowmode { NoFlow, HardFlow, SoftFlow };

/* system-dependent */
#define CFLAGS_HARDFLOW (CRTSCTS)


#define EXAMPLE_BAUD B19200
#define EXAMPLE_FLOW HardFlow


static void die(const char *msg)
{
    fprintf(stderr, "%s\n", msg);
    exit(1);
}

static int close_and_complain(int fd, const char *msg, int err)
{
    fprintf(stderr, "%s: %s\n", msg, strerror(err));
    if (fd >= 0)
        close(fd);
    errno = err;
    return -1;
}


int open_port(const char *name, speed_t baud, enum flowmode flow)
{
    int flags;
    struct termios attr;

    int fd = open(name, O_RDWR | O_NONBLOCK | O_NOCTTY);

    if (fd < 0)
        return close_and_complain(-1, "open", errno);

    /* set vaguely sensibe settings */

    if (tcgetattr(fd, &attr) < 0)
        return close_and_complain(fd, "tcgetattr", errno);
        
    /* no special input or output processing */

    attr.c_iflag = (flow == SoftFlow) ? (IXON | IXOFF) : 0;
    attr.c_oflag = 0;

    /* set 8-bit character size and miscellanous control modes */

    attr.c_cflag &= ~(CSIZE | CFLAGS_TO_CLEAR | CFLAGS_HARDFLOW);
    attr.c_cflag |= (CS8 | CFLAGS_TO_SET);
    if (flow == HardFlow)
        attr.c_cflag |= CFLAGS_HARDFLOW;
    
    /* local modes */

    attr.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ISIG);

    /* special characters -- most disabled by prior settings anyway */

    {
        int i;
#ifdef _POSIX_VDISABLE
        attr.c_cc[0] = _POSIX_VDISABLE;
#else
        attr.c_cc[0] = fpathconf(fd, _PC_VDISABLE);
#endif
        for (i = 1; i < NCCS; i++)
            attr.c_cc[i] = attr.c_cc[0];
    }

    attr.c_cc[VSTART] = 0x11;
    attr.c_cc[VSTOP] = 0x13;

    /* timing controls for read() */

    attr.c_cc[VMIN] = 1;
    attr.c_cc[VTIME] = 0;

    /* baud rate */

    cfsetispeed(&attr, baud);
    cfsetospeed(&attr, baud);

    /* write settings */

    if (tcsetattr(fd, TCSANOW, &attr) < 0)
        return close_and_complain(fd, "tcsetattr", errno);

    /* turn off O_NONBLOCK if the device remembered it */

    flags = fcntl(fd, F_GETFL, 0);
    if (flags < 0)
        return close_and_complain(fd, "fcntl(GETFL)", errno);
    if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0)
        return close_and_complain(fd, "fcntl(SETFL)", errno);

    return fd;
}

/* some simple timing utilities */

/* add SECS and USECS to *TV */

static void timeradd(struct timeval *tv, long secs, long usecs)
{
    tv->tv_sec += secs;
    if ((tv->tv_usec += usecs) >= 1000000)
    {
        tv->tv_sec += tv->tv_usec / 1000000;
        tv->tv_usec %= 1000000;
    }
}

/* Set *RES = *A - *B, returning the sign of the result */

static int timersub(struct timeval *res,
                    const struct timeval *a, const struct timeval *b)
{
    long sec = a->tv_sec - b->tv_sec;
    long usec = a->tv_usec - b->tv_usec;

    if (usec < 0)
        usec += 1000000, --sec;

    res->tv_sec = sec;
    res->tv_usec = usec;

    return (sec < 0) ? (-1) : ((sec == 0 && usec == 0) ? 0 : 1);
}


/* this doesn't try and cope with pathological strings (e.g. ababc)
 * timeout is in millisecs
 * A more usual approach to this is to use alarm() for the timeout.
 * This example avoids signal handling for simplicity and to illustrate
 * an alternative approach
 */

int expect(int fd, const char *str, int timeo)
{
    int matchlen = 0;
    int len = strlen(str);
    struct timeval now,end,left;
    fd_set fds;
    char c;

    gettimeofday(&end, NULL);
    timeradd(&end, timeo/1000, timeo%1000);

    while (matchlen < len)
    {
        gettimeofday(&now, NULL);
        if (timersub(&left, &end, &now) <= 0)
            return -1;

        FD_ZERO(&fds);
        FD_SET(fd, &fds);
        if (select(fd+1, &fds, NULL, NULL, &left) <= 0)
            return -1;

        if (read(fd, &c, 1) != 1)
            return -1;

        if (isprint((unsigned char)c) || c == '\n' || c == '\r')
            putchar(c);
        else
            printf("\\x%02x", c);

        if (c == str[matchlen])
            ++matchlen;
        else
            matchlen = 0;
    }

    return 0;
}


int main(int argc, char **argv)
{
    int fd;
    unsigned char c;

    if (argc < 2)
        die("no port specified");

    setvbuf(stdout, NULL, _IONBF, 0);

    fd = open_port(argv[1], EXAMPLE_BAUD, EXAMPLE_FLOW);
    if (fd < 0)
        die("cannot open port");

    write(fd, "AT\r", 3);
    if (expect(fd, "OK", 5000) < 0)
    {
        write(fd, "AT\r", 3);
        if (expect(fd, "OK", 5000) < 0)
        {
            tcflush(fd, TCIOFLUSH);
            close(fd);
            die("no response to AT");
        }
    }

    write(fd, "ATI4\r", 5);
    expect(fd, "OK", 10000);

    putchar('\n');

    tcflush(fd, TCIOFLUSH);
    close(fd);

    return 0;
}

[ < ] [ > ]   [ << ] [ Up ] [ >> ]

This document was generated on September, 10 2007 using texi2html 1.77.