tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

C tty/tcsetattrs question



Hello,
I have a GPS device connected to a serial port. It defaults to 9600bps
NMEA messages, but I need to switch it to 4800bps. There are NMEA
commands for this, and I can properly do this using cu(1).

Now I'm trying to write a program to do this at boot time.
First I need to determine if the GPS is outputting at 4800 or 9600bps
(the CPU may be rebooted, or even powered off wihout resetting the GPS
module).
I wrote the attached C program, but the cfsetspeed() doesn't seem
to have any effect: with the GPS running at 9600bps,
if I start with the tty set to 9600 (for example from a previous
cu(1) run) I get proper NMEA sentences on both check_term() calls,
and if I start with the tty set to 4800 (e.g. from a previous
cu(1) run) I get either nothing or garbage (not getting anything
from the tty is a valid case, as the tty is in canon mode select()
will return a read event only if the serial port got something that
looks like a line).

Does anyone see an obvious error or something I missed ?

-- 
Manuel Bouyer <bouyer%antioche.eu.org@localhost>
     NetBSD: 26 ans d'experience feront toujours la difference
--
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/select.h>


struct termios orig_t;
int term_fd = -1;


static void
exit_handler(void)
{
	if (term_fd >= 0) {
		tcsetattr(term_fd, TCSAFLUSH,  &orig_t);
	}
}

static void
sig_hanlder(int sig)
{
	exit_handler();
	exit(1);
}

static int
check_term(int fd)
{
	fd_set rset;
	struct timeval timeout;
	int tries = 10;
	int ret;
	char buf[100];
	FILE *f = fdopen(fd, "r");
	
	if (f == NULL) {
		err(EXIT_FAILURE, "fdopen() failed");
	}

	while (tries > 0) {
		FD_ZERO(&rset);
		FD_SET(fd, &rset);
		timeout.tv_sec = 1;
		timeout.tv_usec = 0;
		ret = select(fd + 1, &rset, NULL, NULL, &timeout);
		switch(ret) {
		case -1:
			err(EXIT_FAILURE, "select() failed");
			break;
		case 0:
			printf("timeout %d\n", tries);
			tries--;
			break;
		default:
			if (FD_ISSET(fd, &rset)) {
				if (fgets(buf, sizeof(buf), f) != NULL) {
					printf("got: -- %s --\n", buf);
					if (strncmp(buf, "$GP", 3) == 0) {
						return 1;
					}
				}
				tries--;
			}
		}
	}
	return 0;
}

int
main(int argc, const char *argv[])
{

	static struct termios working_t;
	if (argc != 2) {
		fprintf(stderr, "usage: %s <tty>\n", argv[0]);
		exit(1);
	}
	term_fd = open(argv[1], O_RDWR, 0);
	if (term_fd < 0) {
		fprintf(stderr, "open %s: %s\n", argv[1], strerror(errno));
		exit(1);
	}

	if (tcgetattr(term_fd, &orig_t) < 0) {
		perror("tcgetattr");
		exit(1);
	}
	atexit(exit_handler);
	signal(SIGINT, sig_hanlder);
	signal(SIGTERM, sig_hanlder);

	working_t = orig_t;
#if 0
	working_t.c_iflag =~ (IGNBRK | IXON | IXOFF | IMAXBEL);
	working_t.c_iflag |= IGNCR;
#endif
	working_t.c_iflag = 0;
	working_t.c_oflag =~ (OPOST);
	working_t.c_cflag =~ (CSIZE | CSTOPB | PARENB | CRTSCTS | MDMBUF);
	working_t.c_cflag |= CS8 | CLOCAL;
	working_t.c_lflag = ICANON | NOKERNINFO;
again:
	printf("test 4800\n");
	if (cfsetspeed(&working_t, B4800) != 0)
		err(EXIT_FAILURE, "setting speed to 4800");
	
	if (tcsetattr(term_fd, TCSAFLUSH,  &working_t) != 0) {
		perror("tcsetattr 1");
		exit(1);
	}
	if (check_term(term_fd)) {
		printf("already at 4800\n");
	}
	printf("test 9600\n");
	if (cfsetspeed(&working_t, B9600) != 0)
		err(EXIT_FAILURE, "setting speed to 9600");
	if (tcsetattr(term_fd, TCSAFLUSH,  &working_t) != 0) {
		perror("tcsetattr 2");
		exit(1);
	}
	if (check_term(term_fd)) {
		printf("switch to 4800\n");
	}
	goto again;
}


Home | Main Index | Thread Index | Old Index