Re: Hang in tcdrain(3) after write(3)

2019-11-28 Thread Matthew Dillon
If modem control is turned on you have to go into clocal mode to drain
commands, otherwise its waiting for a carrier.  That's one possibility.

-Matt

On Mon, Nov 25, 2019 at 3:14 PM Jeffrey Walton  wrote:

> 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.
>


Hang in tcdrain(3) after write(3)

2019-11-25 Thread Jeffrey Walton
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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#if defined(__linux__)
# define MODEM "/dev/ttyACM0"
#elif defined(__APPLE__)
# define MODEM "/dev/cu.usbmodem001"
#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, ) != 0) {
printf("tcgetattr failed\n");
goto finish;
}

printf("Setting tty options\n");

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

cfsetospeed(, B57600);
cfsetispeed(, B57600);

printf("Flushing tty\n");

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

printf("Setting tty\n");

if (tcsetattr(fd, TCSANOW, ) != 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;
}