Hi Everyone,

I'm testing some software on DragonFly. There's not much to it. It
talks to the modem, and sends an ATZ and then reads the response.
Linux, FreeBSD, NetBSD, OpenBSD and OS X are OK.

My test rig is DragonFly 5.6-RELEASE x86_64 (fully patched) with a
USR5637 modem, https://www.amazon.com/gp/product/B0013FDLM0. The modem
is located at /dev/cuaU0.

DragonFly hangs on the call to tcdrain(3). Looking at the man page I
don't see any special handling. Cf.,
http://man.dragonflybsd.org/?command=tcdrain.

Attached is the reproducer. The trace is:

% ./test.exe
Setting TIOCEXCL
Getting tty
Setting tty options
Flushing tty
Setting tty
Writing ATZ
Waiting for write  <<-- call to tcdrain(fd)

Thanks.
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/stat.h>

#if defined(__linux__)
# define MODEM "/dev/ttyACM0"
#elif defined(__APPLE__)
# define MODEM "/dev/cu.usbmodem0000001"
#elif defined(__NetBSD__)
# define MODEM "/dev/dtyU0"
#elif defined(__bsdi__) || defined(__DragonFly__)
# define MODEM "/dev/cuaU0"
#endif

static void make_blocking(int fd)
{
    const int old = fcntl(fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, old & ~(int)O_NONBLOCK);
}

static void make_nonblocking(int fd)
{
    const int old = fcntl(fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, old | (int)O_NONBLOCK);
}

/* gcc -Wall -g3 -O1 -std=c99 test.c -o test.exe */
int main(int argc, char* argv[])
{
    int fd = open(MODEM, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd == -1) {
        printf("open failed\n");
        goto finish;
    }

    printf("Setting TIOCEXCL\n");

    /* Mark the fd as exclusive so ModemManager     */
    /* and friends cannot open and muck with state. */
    if (ioctl(fd, TIOCEXCL, NULL) == -1) {
        printf("ioctl failed\n");
        goto finish;
    }

    printf("Getting tty\n");

    struct termios tty;
    if (tcgetattr(fd, &tty) != 0) {
        printf("tcgetattr failed\n");
        goto finish;
    }

    printf("Setting tty options\n");

    cfmakeraw(&tty);
    tty.c_cflag |= (CLOCAL | CRTSCTS);
    // tty.c_cflag &= ~CSTOPB;  /* 1 stop bit */
    // tty.c_cflag |=  CSTOPB;  /* 2 stop bit */

    cfsetospeed(&tty, B57600);
    cfsetispeed(&tty, B57600);

    printf("Flushing tty\n");

    if (tcflush(fd, TCIOFLUSH) != 0) {
        printf("tcflush failed\n");
        goto finish;
    }

    printf("Setting tty\n");

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("tcsetattr failed\n");
        goto finish;
    }

    /*********** Write ***********/

    printf("Writing ATZ\n");

    ssize_t res = write(fd, "ATZ\r", 4);
    if (res == -1) {
        printf("write failed\n");
        goto finish;
    }

    printf("Waiting for write\n");

    res = tcdrain(fd);
    if (res == -1) {
        printf("tcdrain failed\n");
        goto finish;
    }

    printf("Wrote: ATZ\n");

    /*********** Read ***********/

    printf("Reading response\n");

    make_blocking(fd);

    for ( ; ; )
    {
        unsigned char buf[512];
        res = read(fd, buf, sizeof(buf));
        if (res == -1 && errno == EWOULDBLOCK) {
            break;
        }
        else if (res == -1) {
            printf("read failed\n");
            goto finish;
        }

        make_nonblocking(fd);

        buf[res] = '\0';
        printf("Read: %s\n", buf);
    }

finish:

    if (fd != -1)
        close(fd);

    return 0;
}

Reply via email to