Re: How to achieve O_TTY_INIT when opening a USB modem?
On Sun, Nov 24, 2019 at 10:10 PM Philip Guenther wrote: > > On Sun, Nov 24, 2019 at 3:11 AM Jeffrey Walton wrote: >> >> I am struggling to get a USB modem and terminal configured properly >> under OpenBSD. The same code on Linux is fine. The symptom I am seeing >> is a hung read() after issuing ATZ\r to the modem. >> >> I'm guessing there's an uninitialized field in my struct termios tty. > > I'm not sure what you mean by that. Do you mean you're concerned that you're > you making a tcsetattr(3) call on an incompletely initialized structure? Or > do you mean you're concerned that the initial configuration of the tty > provided by the kernel is in a "not good" state? I think cfmakeraw is not initializing the structure properly. It is an intermittent failure. Attached is a reproducer. Valgrind lights up like a christmas tree on the reproducer. The results are so bad I am pretty sure the problem is with Valgrind, not the reproducer. Jeff #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(__OpenBSD__) # 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; } if (ioctl(fd, TIOCEXCL, NULL) == -1) { printf("ioctl failed\n"); goto finish; } struct termios tty; memset(, 0, sizeof(tty)); cfmakeraw(); tty.c_cflag |= (CLOCAL | CRTSCTS); // tty.c_cflag &= ~CSTOPB; /* 1 stop bit */ // tty.c_cflag |= CSTOPB; /* 2 stop bit */ cfsetospeed(, B57600); cfsetispeed(, B57600); if (tcsetattr(fd, TCSANOW, ) != 0) { printf("tcsetattr failed\n"); goto finish; } if (tcflush(fd, TCIOFLUSH) != 0) { printf("tcflush failed\n"); goto finish; } /*** Write ***/ ssize_t res = write(fd, "ATZ\r", 4); if (res == -1) { printf("write failed\n"); goto finish; } res = tcdrain(fd); if (res == -1) { printf("tcdrain failed\n"); goto finish; } printf("Wrote: ATZ\n"); /*** Read ***/ 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; }
Re: How to achieve O_TTY_INIT when opening a USB modem?
On Sun, Nov 24, 2019 at 11:01 PM Philip Guenther wrote: > > On Sun, Nov 24, 2019 at 7:53 PM Jeffrey Walton wrote: >> >> On Sun, Nov 24, 2019 at 10:10 PM Philip Guenther wrote: >> > >> > On Sun, Nov 24, 2019 at 3:11 AM Jeffrey Walton wrote: >> >> >> >> I am struggling to get a USB modem and terminal configured properly >> >> under OpenBSD. The same code on Linux is fine. The symptom I am seeing >> >> is a hung read() after issuing ATZ\r to the modem. >> >> >> >> I'm guessing there's an uninitialized field in my struct termios tty. >> > >> > I'm not sure what you mean by that. Do you mean you're concerned that >> > you're you making a tcsetattr(3) call on an incompletely initialized >> > structure? Or do you mean you're concerned that the initial configuration >> > of the tty provided by the kernel is in a "not good" state? >> >> I think cfmakeraw is not initializing the structure properly. It is an >> intermittent failure. > > This code is misusing cfmakeraw(3): it needs to call tcgetattr(3) on the tty > fd and only call cfmakeraw() on the termios structure that tcgetattr() has > filled in. Ack, thanks. > (There may be other problems; I only reviewed enough to see that it was > violating the rule I mentioned in my previous post. The _only_ portable way > to initialize a struct termios is to use tcgetattr()!) Ack, thanks. At the risk of sounding argumentative, the OpenBSD man page on cfmakeraw [0] does not detail the requirement of using cfmakeraw on a struct termios obtained from tcgetattr. In fact I specifically avoid it because Valgrind produced so many findings due to the unmapped ioctl's. The Linux man page does not detail the limitation either. But things "just work" on Linux and it was not a problem. I suggest adding text similar to the following to the man page (the first sentence is existing): The cfmakeraw() function sets the flags stored in the termios structure to a state disabling all input and output processing, giving a “raw I/O path”. cfmakeraw() must use a termios structure obtained from tcgetattr(). (remaining text from man page) Thanks for the help. Jeff [0] https://man.openbsd.org/tcsetattr.3
Re: How to achieve O_TTY_INIT when opening a USB modem?
On Sun, Nov 24, 2019 at 7:53 PM Jeffrey Walton wrote: > On Sun, Nov 24, 2019 at 10:10 PM Philip Guenther > wrote: > > > > On Sun, Nov 24, 2019 at 3:11 AM Jeffrey Walton > wrote: > >> > >> I am struggling to get a USB modem and terminal configured properly > >> under OpenBSD. The same code on Linux is fine. The symptom I am seeing > >> is a hung read() after issuing ATZ\r to the modem. > >> > >> I'm guessing there's an uninitialized field in my struct termios tty. > > > > I'm not sure what you mean by that. Do you mean you're concerned that > you're you making a tcsetattr(3) call on an incompletely initialized > structure? Or do you mean you're concerned that the initial configuration > of the tty provided by the kernel is in a "not good" state? > > I think cfmakeraw is not initializing the structure properly. It is an > intermittent failure. > This code is misusing cfmakeraw(3): it needs to call tcgetattr(3) on the tty fd and only call cfmakeraw() on the termios structure that tcgetattr() has filled in. (There may be other problems; I only reviewed enough to see that it was violating the rule I mentioned in my previous post. The _only_ portable way to initialize a struct termios is to use tcgetattr()!) Philip Guenther
Re: How to achieve O_TTY_INIT when opening a USB modem?
On Sun, Nov 24, 2019 at 3:11 AM Jeffrey Walton wrote: > I am struggling to get a USB modem and terminal configured properly > under OpenBSD. The same code on Linux is fine. The symptom I am seeing > is a hung read() after issuing ATZ\r to the modem. > > I'm guessing there's an uninitialized field in my struct termios tty. > I'm not sure what you mean by that. Do you mean you're concerned that you're you making a tcsetattr(3) call on an incompletely initialized structure? Or do you mean you're concerned that the initial configuration of the tty provided by the kernel is in a "not good" state? > The latest Posix provides O_TTY_INIT to ensure a terminal is in a good > configuration, but OpenBSD does not recognize it. > What is the equivalent under OpenBSD? OpenBSD, like all BSDs, does not require anything special to be done to initialize a tty on first open. We can (and I guess we should at this point) define O_TTY_INIT to be zero. How do I achieve O_TTY_INIT when > using a struct termios tty? > Before calling tcsetattr(3) you should call tcgetattr(3) to get the tty device's current settings and only alter the setting you care about. Philip Guenther