nuttx-apps/system/ubloxmodem/ubloxmodem_main.c

442 lines
10 KiB
C
Raw Normal View History

/****************************************************************************
* apps/system/ubloxmodem/ubloxmodem_main.c
*
* Copyright (C) 2016 Vladimir Komendantskiy. All rights reserved.
* Author: Vladimir Komendantskiy <vladimir@moixaenergy.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <nuttx/modem/u-blox.h>
#include "ubloxmodem.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifdef CONFIG_MODEM_U_BLOX_DEBUG
# define m_dbg dbg
# define m_info info
# define m_vlldbg lldbg
# define m_vllinfo llinfo
#else
# define m_dbg(x...)
# define m_info(x...)
# define m_lldbg(x...)
# define m_llinfo(x...)
#endif
#define UBLOXMODEM_MAX_REGISTERS 16
#if !defined(CONFIG_SYSTEM_UBLOXMODEM_TTY_DEVNODE)
/* Use /dev/ttyS1 by default */
# define CONFIG_SYSTEM_UBLOXMODEM_TTY_DEVNODE "/dev/ttyS1"
#endif
/****************************************************************************
* Private Data
****************************************************************************/
static int ubloxmodem_help (FAR struct ubloxmodem_cxt* cxt);
static int ubloxmodem_on (FAR struct ubloxmodem_cxt* cxt);
static int ubloxmodem_off (FAR struct ubloxmodem_cxt* cxt);
static int ubloxmodem_reset (FAR struct ubloxmodem_cxt* cxt);
static int ubloxmodem_status(FAR struct ubloxmodem_cxt* cxt);
static int ubloxmodem_at (FAR struct ubloxmodem_cxt* cxt);
/* Mapping of command indices (@ubloxmodem_cmd@ implicit from the position in
* the list) to tuples containing the command handler and descriptive
* information.
*/
static const struct cmdinfo cmdmap[] =
{
{ubloxmodem_help, "help", "Show help", NULL},
{ubloxmodem_on, "on", "Power ON", NULL},
{ubloxmodem_off, "off", "Power OFF", NULL},
{ubloxmodem_reset, "reset", "Reset", NULL},
{ubloxmodem_status, "status", "Show status", NULL},
{ubloxmodem_at, "at", "AT test", "<AT cmd> <response>"},
};
/****************************************************************************
* Private Functions
****************************************************************************/
static int make_nonblock(int fd)
{
int flags;
if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
{
return flags;
}
if ((flags = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) < 0)
{
return flags;
}
return 0;
}
static int ubloxmodem_open_tty(void)
{
int fd, ret;
fd = open(CONFIG_SYSTEM_UBLOXMODEM_TTY_DEVNODE, O_RDWR);
if (fd < 0)
{
m_info("failed to open TTY\n");
return fd;
}
ret = make_nonblock(fd);
if (ret < 0)
{
m_info("make_nonblock failed\n");
close(fd);
return ret;
}
return fd;
}
static int chat_readb(int fd, FAR char* dst, int timeout_ms)
{
struct pollfd fds;
int ret;
fds.fd = fd;
fds.events = POLLIN;
fds.revents = 0;
ret = poll(&fds, 1, timeout_ms);
if (ret <= 0)
{
m_info("poll timed out\n");
return -ETIMEDOUT;
}
ret = read(fd, dst, 1);
if (ret != 1)
{
m_info("read failed\n");
return -EPERM;
}
return 0;
}
static int chat_match_response(int fd, FAR char* response, int timeout_ms)
{
char c;
int ret;
int delim_countdown = 10;
while (*response && delim_countdown >= 0)
{
ret = chat_readb(fd, &c, timeout_ms);
if (ret < 0)
{
return ret;
}
if (c == *response)
{
response++;
}
else if (delim_countdown > 0 && (c == '\r' || c == '\n'))
{
delim_countdown--;
}
else
{
m_info("expected %c (0x%02X), got %c (0x%02X)\n",
*response, *response, c, c);
return -EILSEQ;
}
}
return 0;
}
static int chat_single(int fd, FAR char* cmd, FAR char* resp)
{
int ret;
/* Write the command */
ret = write(fd, cmd, strlen(cmd));
if (ret < 0)
{
return ret;
}
/* Terminate the command with <CR>, hence sending it to the modem */
ret = write(fd, "\r", 1);
if (ret < 0)
{
return ret;
}
/* Match the command echo */
ret = chat_match_response(fd, cmd, 5 * 1000);
if (ret < 0)
{
m_info("invalid echo\n");
return ret;
}
/* Match the modem response to the command */
ret = chat_match_response(fd, resp, 5 * 1000);
return ret;
}
static int ubloxmodem_help(FAR struct ubloxmodem_cxt* cxt)
{
int i;
printf("Usage: ubloxmodem <cmd> [arguments]\n"
" where <cmd> is one of\n");
for (i = 0;
i < sizeof(cmdmap) / sizeof(struct cmdinfo);
i++)
{
printf("%s\n %s\n %s\n",
cmdmap[i].name,
cmdmap[i].desc,
(!cmdmap[i].args ? "No arguments" : cmdmap[i].args));
}
return 0;
}
static int ubloxmodem_on(FAR struct ubloxmodem_cxt* cxt)
{
int ret;
ret = ioctl(cxt->fd, MODEM_IOC_POWERON, 0);
if (ret < 0)
{
fprintf(stderr, "ERROR: ioctl failed: %d\n", errno);
return -EPERM;
}
return ret;
}
static int ubloxmodem_off(FAR struct ubloxmodem_cxt* cxt)
{
int ret;
ret = ioctl(cxt->fd, MODEM_IOC_POWEROFF, 0);
if (ret < 0)
{
fprintf(stderr, "ERROR: ioctl failed: %d\n", errno);
return -EPERM;
}
return ret;
}
static int ubloxmodem_reset(FAR struct ubloxmodem_cxt* cxt)
{
int ret;
ret = ioctl(cxt->fd, MODEM_IOC_RESET, 0);
if (ret < 0)
{
fprintf(stderr, "ERROR: ioctl failed: %d\n", errno);
return -EPERM;
}
return ret;
}
static int ubloxmodem_status(FAR struct ubloxmodem_cxt* cxt)
{
int ret, i;
struct ubxmdm_status status;
/* Allocate name-value pairs */
FAR struct ubxmdm_regval register_values[UBLOXMODEM_MAX_REGISTERS];
char regname[4]; /* Null-terminated string buffer */
regname[3] = '\0'; /* Set the null string terminator */
/* Set the maximum value, to be updated by driver */
status.register_values_size = UBLOXMODEM_MAX_REGISTERS;
status.register_values = register_values;
ret = ioctl(cxt->fd, MODEM_IOC_GETSTATUS, (unsigned long) &status);
if (ret < 0)
{
fprintf(stderr, "ERROR: ioctl failed: %d\n", errno);
return EXIT_FAILURE;
}
printf("Modem is %s\n", status.on ? "ON" : "OFF");
for (i = 0;
i < status.register_values_size && i < UBLOXMODEM_MAX_REGISTERS;
i++)
{
strncpy(regname, status.register_values[i].name, 3);
printf("%s=%d ",
regname,
(int) status.register_values[i].val);
}
printf("\n");
return ret;
}
static int ubloxmodem_at(FAR struct ubloxmodem_cxt* cxt)
{
int fd, ret;
FAR char* atcmd;
FAR char* resp;
atcmd = cxt->argv[2];
resp = cxt->argv[3];
if (cxt->argc < 4 || atcmd == NULL || resp == NULL)
{
fprintf(stderr, "ERROR: missing arguments\n");
return -EINVAL;
}
fd = ubloxmodem_open_tty();
if (fd < 0)
{
fprintf(stderr, "ERROR: cannot open TTY device: %d\n", errno);
return fd;
}
ret = chat_single(fd, atcmd, resp);
m_dbg("test result: %d\n", ret);
close(fd);
return ret;
}
static int ubloxmodem_parse(FAR struct ubloxmodem_cxt* cxt)
{
int i;
for (i = 0;
i < sizeof(cmdmap) / sizeof(struct cmdinfo) &&
cxt->cmd == UBLOXMODEM_CMD_UNKNOWN;
i++)
{
if (!strcmp(cxt->argv[1], cmdmap[i].name))
cxt->cmd = i;
}
if (cxt->cmd == UBLOXMODEM_CMD_UNKNOWN)
{
cxt->cmd = UBLOXMODEM_CMD_HELP;
}
return 0;
}
inline int ubloxmodem_exec(FAR struct ubloxmodem_cxt* cxt)
{
return (cmdmap[cxt->cmd].handler)(cxt);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: ubloxmodem_main
*
* Description:
* Main entry point for the u-blox modem tool.
*
****************************************************************************/
#ifdef CONFIG_BUILD_KERNEL
int main(int argc, FAR char** argv)
#else
int ubloxmodem_main(int argc, FAR char** argv)
#endif
{
struct ubloxmodem_cxt cxt;
int ret;
cxt.argc = argc;
cxt.argv = argv;
cxt.cmd = UBLOXMODEM_CMD_UNKNOWN;
ubloxmodem_parse(&cxt);
cxt.fd = open(CONFIG_SYSTEM_UBLOXMODEM_DEVNODE, O_RDWR);
if (cxt.fd < 0)
{
fprintf(stderr, "ERROR: Failed to open %s: %d\n",
CONFIG_SYSTEM_UBLOXMODEM_DEVNODE, errno);
return EXIT_FAILURE;
}
ret = ubloxmodem_exec(&cxt);
printf("Command result: %s (%d)\n", ret ? "FAIL" : "OK", ret);
fflush(stdout);
close(cxt.fd);
return 0;
}