2016-01-31 16:07:46 +01:00
|
|
|
/****************************************************************************
|
2016-01-31 16:35:42 +01:00
|
|
|
* apps/system/ubloxmodem/ubloxmodem_main.c
|
2016-01-31 16:07:46 +01:00
|
|
|
*
|
|
|
|
* 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
|
2016-06-11 19:55:38 +02:00
|
|
|
# define m_info info
|
2016-06-11 22:55:13 +02:00
|
|
|
# define m_vllerr llerr
|
2016-06-11 19:55:38 +02:00
|
|
|
# define m_vllinfo llinfo
|
2016-01-31 16:07:46 +01:00
|
|
|
#else
|
|
|
|
# define m_dbg(x...)
|
2016-06-11 19:55:38 +02:00
|
|
|
# define m_info(x...)
|
2016-06-11 22:55:13 +02:00
|
|
|
# define m_llerr(x...)
|
2016-06-11 19:55:38 +02:00
|
|
|
# define m_llinfo(x...)
|
2016-01-31 16:07:46 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#define UBLOXMODEM_MAX_REGISTERS 16
|
|
|
|
|
2016-01-31 16:35:42 +01:00
|
|
|
#if !defined(CONFIG_SYSTEM_UBLOXMODEM_TTY_DEVNODE)
|
2016-01-31 16:07:46 +01:00
|
|
|
/* Use /dev/ttyS1 by default */
|
2016-01-31 16:35:42 +01:00
|
|
|
# define CONFIG_SYSTEM_UBLOXMODEM_TTY_DEVNODE "/dev/ttyS1"
|
2016-01-31 16:07:46 +01:00
|
|
|
#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;
|
|
|
|
|
2016-01-31 16:35:42 +01:00
|
|
|
fd = open(CONFIG_SYSTEM_UBLOXMODEM_TTY_DEVNODE, O_RDWR);
|
2016-01-31 16:07:46 +01:00
|
|
|
if (fd < 0)
|
|
|
|
{
|
2016-06-11 19:55:38 +02:00
|
|
|
m_info("failed to open TTY\n");
|
2016-01-31 16:07:46 +01:00
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = make_nonblock(fd);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
2016-06-11 19:55:38 +02:00
|
|
|
m_info("make_nonblock failed\n");
|
2016-01-31 16:07:46 +01:00
|
|
|
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)
|
|
|
|
{
|
2016-06-11 19:55:38 +02:00
|
|
|
m_info("poll timed out\n");
|
2016-01-31 16:07:46 +01:00
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = read(fd, dst, 1);
|
|
|
|
if (ret != 1)
|
|
|
|
{
|
2016-06-11 19:55:38 +02:00
|
|
|
m_info("read failed\n");
|
2016-01-31 16:07:46 +01:00
|
|
|
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
|
|
|
|
{
|
2016-06-11 19:55:38 +02:00
|
|
|
m_info("expected %c (0x%02X), got %c (0x%02X)\n",
|
2016-01-31 16:07:46 +01:00
|
|
|
*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)
|
|
|
|
{
|
2016-06-11 19:55:38 +02:00
|
|
|
m_info("invalid echo\n");
|
2016-01-31 16:07:46 +01:00
|
|
|
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);
|
|
|
|
|
2016-01-31 16:35:42 +01:00
|
|
|
cxt.fd = open(CONFIG_SYSTEM_UBLOXMODEM_DEVNODE, O_RDWR);
|
2016-01-31 16:07:46 +01:00
|
|
|
if (cxt.fd < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "ERROR: Failed to open %s: %d\n",
|
2016-01-31 16:35:42 +01:00
|
|
|
CONFIG_SYSTEM_UBLOXMODEM_DEVNODE, errno);
|
2016-01-31 16:07:46 +01:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ubloxmodem_exec(&cxt);
|
|
|
|
printf("Command result: %s (%d)\n", ret ? "FAIL" : "OK", ret);
|
|
|
|
|
|
|
|
fflush(stdout);
|
|
|
|
close(cxt.fd);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|