view uptools/atcmd/atinterf.c @ 665:b43d8c2725b9

fc-loadtool flash program-bin changed to use binary protocol
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 07 Mar 2020 18:34:39 +0000
parents f0e9bb28b4d6
children
line wrap: on
line source

/*
 * This module implements the interface functions to the AT command phone or
 * modem to be used by the rest of the FreeCalypso User Phone tools suite.
 */

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include "../../rvinterf/include/exitcodes.h"

static char atinterf_pathname[] = "/opt/freecalypso/bin/fcup-atinterf";
static char rvtat_pathname[] = "/opt/freecalypso/bin/fcup-rvtat";
static char shell_pathname[] = "/bin/sh";

char *target_ttyport, *target_baud, *external_prog;
int rvtat_mode, no_target_mode;
static char *backend_prog, *backend_argv[6];

FILE *cpipeF, *rpipeF;
char at_response[514];

atinterf_cmdline_opt(opt)
{
	extern char *optarg;

	switch (opt) {
	case 'B':
		target_baud = optarg;
		return(1);
	case 'n':
		no_target_mode = 1;
		return(1);
	case 'p':
		target_ttyport = optarg;
		return(1);
	case 'R':
		rvtat_mode = 1;
		return(1);
	case 'X':
		external_prog = optarg;
		return(1);
	}
	return(0);
}

static void
preen_port_baud_config()
{
	if (!target_ttyport && target_baud) {
		fprintf(stderr, "error: -B option invalid without -p\n");
		exit(ERROR_USAGE);
	}
	if (rvtat_mode)
		return;
	if (!target_ttyport) {
		target_ttyport = getenv("FC_GSM_DEVICE");
		if (!target_ttyport) {
			fprintf(stderr,
				"error: no FC GSM device target specified\n");
			exit(ERROR_USAGE);
		}
	}
	if (!target_baud)
		target_baud = "115200";
}

static void
setup_be_native_at()
{
	backend_prog = atinterf_pathname;
	backend_argv[0] = "fcup-atinterf";
	backend_argv[1] = target_ttyport;
	backend_argv[2] = target_baud;
	backend_argv[3] = 0;
}

static void
setup_be_rvtat()
{
	char **ap;

	backend_prog = rvtat_pathname;
	ap = backend_argv;
	*ap++ = "fcup-rvtat";
	if (target_ttyport) {
		*ap++ = "-p";
		*ap++ = target_ttyport;
	}
	if (target_baud) {
		*ap++ = "-B";
		*ap++ = target_baud;
	}
	*ap = 0;
}

static void
setup_be_external()
{
	backend_prog = shell_pathname;
	backend_argv[0] = "sh";
	backend_argv[1] = "-c";
	backend_argv[2] = external_prog;
	backend_argv[3] = 0;
}

atinterf_init()
{
	int cpipe[2], rpipe[2], rc;

	if (no_target_mode)
		return(0);
	if (external_prog)
		setup_be_external();
	else {
		preen_port_baud_config();
		if (rvtat_mode)
			setup_be_rvtat();
		else
			setup_be_native_at();
	}
	if (pipe(cpipe) < 0 || pipe(rpipe) < 0) {
		perror("pipe");
		exit(ERROR_UNIX);
	}
	rc = vfork();
	if (rc < 0) {
		perror("vfork for launching back end");
		exit(ERROR_UNIX);
	}
	if (!rc) {
		/* we are in the child - prepare to exec the BE */
		dup2(cpipe[0], 0);
		dup2(rpipe[1], 1);
		close(cpipe[0]);
		close(cpipe[1]);
		close(rpipe[0]);
		close(rpipe[1]);
		/* do the exec */
		execv(backend_prog, backend_argv);
		perror(backend_prog);
		_exit(1);
	}
	close(cpipe[0]);
	close(rpipe[1]);
	cpipeF = fdopen(cpipe[1], "w");
	if (!cpipeF) {
		perror("fdopen");
		exit(ERROR_UNIX);
	}
	rpipeF = fdopen(rpipe[0], "r");
	if (!rpipeF) {
		perror("fdopen");
		exit(ERROR_UNIX);
	}
	return(0);
}

atinterf_exec_cmd(command, message, callback)
	char *command, *message;
	void (*callback)();
{
	char *nl;

	if (no_target_mode) {
		puts(command);
		if (message)
			puts(message);
		strcpy(at_response, "FOK");
		return(0);
	}
	if (message) {
		fputs("c+m\n", cpipeF);
		fprintf(cpipeF, "%s\n", command);
		fprintf(cpipeF, "%s\n", message);
	} else
		fprintf(cpipeF, "%s\n", command);
	fflush(cpipeF);
	for (;;) {
		if (!fgets(at_response, sizeof at_response, rpipeF)) {
			fprintf(stderr,
				"error reading AT response from back end\n");
			exit(ERROR_RVINTERF);
		}
		nl = index(at_response, '\n');
		if (!nl) {
			fprintf(stderr,
		"error: back end response is too long or unterminated\n");
			exit(ERROR_RVINTERF);
		}
		*nl = '\0';
		switch (at_response[0]) {
		case 'E':
			fprintf(stderr, "error from back end: %s\n",
				at_response + 1);
			exit(ERROR_RVINTERF);
		case 'F':
			return(0);
		case 'I':
			if (callback)
				callback();
			continue;
		}
		fprintf(stderr, "error: invalid response from back end\n");
		exit(ERROR_RVINTERF);
	}
}

atinterf_exec_cmd_needok(command, message, callback)
	char *command, *message;
	void (*callback)();
{
	atinterf_exec_cmd(command, message, callback);
	if (strcmp(at_response+1, "OK")) {
		fprintf(stderr, "AT error: %s\n", at_response+1);
		exit(ERROR_TARGET);
	}
	return(0);
}