Re: How to achieve O_TTY_INIT when opening a USB modem?

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

2019-11-24 Thread Jeffrey Walton
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?

2019-11-24 Thread Philip Guenther
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?

2019-11-24 Thread Philip Guenther
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