view loadtools/tpinterf.c @ 248:cb1ba53a1106

beginning of factored-out libserial
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 21 Sep 2017 21:43:34 +0000
parents e7502631a0f9
children 8d7dcfd9df53
line wrap: on
line source

/*
 * Target program interface - this module provides some primitives
 * for communicating programmatically with loadagent and possibly
 * other target-utils.  This module will be linked by both
 * fc-loadtool and fc-chainload.
 */

#include <sys/types.h>
#include <sys/time.h>
#include <sys/errno.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>

extern int errno;

extern int target_fd;

/* definition matches ../target-utils/libcommon/cmdentry.c */
#define	MAXCMD	527

/*
 * static buffer between tpinterf_make_cmd and tpinterf_send_cmd
 *
 * We store the command with an ending \r\n so we can use it for
 * matching the received echo as well, hence the sizing of the
 * buffer.
 */
static char cmdbuf[MAXCMD+2];
static int cmdlen;

static int
arg_chars_valid(arg)
	char *arg;
{
	char *cp;

	for (cp = arg; *cp; cp++)
		if (*cp < ' ' || *cp > '~')
			return(0);
	return(1);
}

/*
 * This function takes a command for the target in argv form and
 * converts it to a space-separated continuous string which can be
 * passed as tty "keyboard" input to the target, enforcing length
 * and character validity limits in the process.  The output is
 * stored in an internal static buffer for subsequent
 * tpinterf_send_cmd().
 *
 * Return value: 0 if everything OK, or -1 if some constraint is
 * violated.
 */
tpinterf_make_cmd(argv)
	char **argv;
{
	int arglen;
	char **ap, *dp;

	dp = cmdbuf;
	cmdlen = 0;
	for (ap = argv; *ap; ap++) {
		arglen = strlen(*ap);
		if (ap != argv)
			arglen++;	/* separating space */
		if (arglen > MAXCMD - cmdlen)
			return(-1);
		if (!arg_chars_valid(*ap))
			return(-1);
		if (ap != argv)
			*dp++ = ' ';
		strcpy(dp, *ap);
		dp += strlen(*ap);
		cmdlen += arglen;
	}
	*dp++ = '\r';
	*dp = '\n';
	return(0);
}

/*
 * This function sends the previously-constructed command to the target,
 * and collects the expected echo.
 *
 * Return value: 0 if successful, -1 on errors (timeout or wrong response)
 */
tpinterf_send_cmd()
{
	char echobuf[MAXCMD+2];
	fd_set fds;
	struct timeval tv;
	int rcvd, cc;

	write(target_fd, cmdbuf, cmdlen + 1);
	for (rcvd = 0; rcvd < cmdlen + 2; ) {
		FD_ZERO(&fds);
		FD_SET(target_fd, &fds);
		tv.tv_sec = 1;
		tv.tv_usec = 0;
		cc = select(target_fd+1, &fds, NULL, NULL, &tv);
		if (cc < 0) {
			if (errno == EINTR)
				continue;
			perror("select");
			return(-1);
		}
		if (cc < 1) {
			fprintf(stderr,
				"error: timeout waiting for command echo\n");
			return(-1);
		}
		cc = read(target_fd, echobuf + rcvd, cmdlen + 2 - rcvd);
		if (cc <= 0) {
			perror("read after successful select");
			return(-1);
		}
		rcvd += cc;
	}
	if (bcmp(echobuf, cmdbuf, cmdlen + 2)) {
		fprintf(stderr, "error: command echo mismatch\n");
		return(-1);
	} else
		return(0);
}

/*
 * This functions reads the serial output from the target until a
 * '=' prompt is received.  All intermediate output is passed to
 * stdout.
 *
 * Return value: 0 if '=' prompt received immediately,
 * positive if some scribble came before the prompt, -1 on errors
 * (timeout, read errors, etc).
 */
tpinterf_pass_output(timeout)
{
	char buf[512], *cp;
	fd_set fds;
	struct timeval tv;
	int cc, newline = 1, totout = 0;

	for (;;) {
		FD_ZERO(&fds);
		FD_SET(target_fd, &fds);
		tv.tv_sec = timeout;
		tv.tv_usec = 0;
		cc = select(target_fd+1, &fds, NULL, NULL, &tv);
		if (cc < 0) {
			if (errno == EINTR)
				continue;
			perror("select");
			return(-1);
		}
		if (cc < 1) {
			fprintf(stderr,
		"error: timeout waiting for \'=\' prompt from target\n");
			return(-1);
		}
		cc = read(target_fd, buf, sizeof buf);
		if (cc <= 0) {
			perror("read after successful select");
			return(-1);
		}
		for (cp = buf; cc; cp++) {
			cc--;
			if (*cp == '=' && newline && !cc)
				return(totout);
			putchar(*cp);
			totout++;
			if (*cp == '\n')
				newline = 1;
			else
				newline = 0;
		}
	}
}