c9bd210960
Merged in merlin17/apps/i8sak-sock (pull request #108) i8sak: Fixes mistake where socket was accidentally bound to endpoint address i8sak: Fixes bad logic that bound outbound port to the destination address i8sak: Adds sniffer port option and a few other get/set parameters Merged in merlin17/apps/i8sak-sock (pull request #106) i8sak: Endpoint address is updated whenever 802.15.4 addressing information is updated wireless/ieee802154/i8sak: Converts PF_INETX to PF_INET6 and SOCK_IWPAN to SOCK_DGRAM wireless/ieee802154/i8sak: Catches case where arguments are included, but the interface was never set wireless/ieee802154/i8sak: Endpoint IP address is now updated whenever 802.15.4 addressing info changes wireless/ieee802154/i8sak: Routes all error output to stderr rather than stdout Merged in merlin17/apps/i8sak-sock (pull request #104) i8sak: Fixes socket version of sniffer logic. socket logic for sniffer now works i8sak: Fixes socket version of sniffer logic. socket logic for sniffer now works Merged in merlin17/apps/i8sak-sock (pull request #103) wireless/ieee802154/i8sak: Adds socket interface support. You can now use both socket or char driver to control the MAC layer * wireless/ieee802154: Removes useless wpanlistener folder with build files * wireless/ieee802154/libmac: Merges some files together and adds some missing sixlowpan functionality * wireless/ieee802154/i8sak: Starts adding support for both character driver and socket * Removes unneccessary Kconfig statement * ieee802154: Adds function prototypes missed on previous commit * wireless/ieee802154/libmac: Fixes sixlowpan_resetreq * wireless/ieee802154/libmac: Small fixes and removes enableevents helper * apps/ieee802154/i8sak: Moves wpanlistener functionality into i8sak directly * apps/ieee802154/i8sak: Major restructuring to allow both char driver and socket control Also changes Kconfig options and certain args held previously by i8sak. This is to bring it closer to a real CLI tool rather than a development tool. For instance, no longer does the app change the addressing of the device automatically. This is because you probably don't want i8sak overwriting the extended address after board bring-up. Additionally, the endpoint logic has been simplified. There is only ever one active endpoint now. * wireless/ieee802154/i8sak: Non-functional clean-up. * i8sak: Removes chan and coordinfo commands and introduces generic get/set commands * i8sak: startpan now requires PAN ID argument * i8sak: Adds ability to set endpoint address using set command * i8sak: Minor fixes and cleanup * i8sak: Adds addrmode setting to i8sak. exposed via get/set calls * i8sak: Clean up poll logic and allow polling address mode to be based off endpoint address setting. * i8sak: Trivial * i8sak: Adds get/set hook for endpoint address mode (destination address mode) * i8sak: Changes default address modes to short. * i8sak: Fixes build erros associated with enabling 6LoWPAN support * i8sak: Adds socket version of get/set implementation. Simplifies some logic as well. * wireless/ieee802154/i8sak: Removes unused files * wireless/ieee802154/libmac: Fixes sixlowpan_assocresp. resp was not being copied into the ioctl argument. Approved-by: Gregory Nutt <gnutt@nuttx.org>
885 lines
23 KiB
C
885 lines
23 KiB
C
/****************************************************************************
|
|
* apps/wireless/ieee802154/i8sak/i8sak_main.c
|
|
* IEEE 802.15.4 Swiss Army Knife
|
|
*
|
|
* Copyright (C) 2014-2015, 2017 Gregory Nutt. All rights reserved.
|
|
* Copyright (C) 2014-2015 Sebastien Lorquet. All rights reserved.
|
|
* Copyright (C) 2017 Verge Inc. All rights reserved.
|
|
*
|
|
* Author: Sebastien Lorquet <sebastien@lorquet.fr>
|
|
* Author: Anthony Merlino <anthony@vergeaero.com>
|
|
* Author: Gregory Nuttx <gnutt@nuttx.org>
|
|
*
|
|
* 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.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* This app can be used to control an IEEE 802.15.4 device from command line.
|
|
* Example valid ieee802154 packets:
|
|
*
|
|
* Beacon request, seq number 01:
|
|
* xx xx 01
|
|
*
|
|
* Simple data packet
|
|
* from long address xx to long address yy, pan id pp for src and dest
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <queue.h>
|
|
#include <sys/ioctl.h>
|
|
#include <arpa/inet.h>
|
|
#include <nuttx/fs/ioctl.h>
|
|
|
|
#include <nuttx/wireless/ieee802154/ieee802154_mac.h>
|
|
#include <nuttx/wireless/ieee802154/ieee802154_device.h>
|
|
#include "wireless/ieee802154.h"
|
|
|
|
#include "i8sak.h"
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
/* Configuration ************************************************************/
|
|
|
|
#if !defined(CONFIG_IEEE802154_I8SAK_NINSTANCES) || CONFIG_IEEE802154_I8SAK_NINSTANCES <= 0
|
|
# undef CONFIG_IEEE802154_I8SAK_NINSTANCES
|
|
# define CONFIG_IEEE802154_I8SAK_NINSTANCES 3
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
/* Describes one command */
|
|
|
|
struct i8sak_command_s
|
|
{
|
|
FAR const char *name;
|
|
CODE void (*handler)(FAR struct i8sak_s *i8sak, int argc, FAR char *argv[]);
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
/* Alphabetical, except for help */
|
|
|
|
static const struct i8sak_command_s g_i8sak_commands[] =
|
|
{
|
|
{"help", (CODE void *)NULL},
|
|
{"acceptassoc", (CODE void *)i8sak_acceptassoc_cmd},
|
|
{"assoc", (CODE void *)i8sak_assoc_cmd},
|
|
{"blaster", (CODE void *)i8sak_blaster_cmd},
|
|
{"get", (CODE void *)i8sak_get_cmd},
|
|
{"poll", (CODE void *)i8sak_poll_cmd},
|
|
{"regdump", (CODE void *)i8sak_regdump_cmd},
|
|
{"reset", (CODE void *)i8sak_reset_cmd},
|
|
{"scan", (CODE void *)i8sak_scan_cmd},
|
|
{"set", (CODE void *)i8sak_set_cmd},
|
|
{"sniffer", (CODE void *)i8sak_sniffer_cmd},
|
|
{"startpan", (CODE void *)i8sak_startpan_cmd},
|
|
{"tx", (CODE void *)i8sak_tx_cmd},
|
|
};
|
|
|
|
#define NCOMMANDS (sizeof(g_i8sak_commands) / sizeof(struct i8sak_command_s))
|
|
|
|
static sq_queue_t g_i8sak_free;
|
|
static sq_queue_t g_i8sak_instances;
|
|
static struct i8sak_s g_i8sak_pool[CONFIG_IEEE802154_I8SAK_NINSTANCES];
|
|
static bool g_i8sak_initialized = false;
|
|
static bool g_activei8sak_set = false;
|
|
static FAR struct i8sak_s *g_activei8sak;
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static int i8sak_setup(FAR struct i8sak_s *i8sak, FAR const char *ifname);
|
|
static int i8sak_daemon(int argc, FAR char *argv[]);
|
|
static int i8sak_showusage(FAR const char *progname, int exitcode);
|
|
static void i8sak_switch_instance(FAR char *ifname);
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name : i8sak_str2payload
|
|
*
|
|
* Description :
|
|
* Parse string to get buffer of data. Buf is expected to be of size
|
|
* IEEE802154_MAX_MAC_PAYLOAD_SIZE or larger.
|
|
*
|
|
* Returns:
|
|
* Positive length value of frame payload
|
|
****************************************************************************/
|
|
|
|
int i8sak_str2payload(FAR const char *str, FAR uint8_t *buf)
|
|
{
|
|
int str_len;
|
|
int ret;
|
|
int i = 0;
|
|
|
|
str_len = strlen(str);
|
|
|
|
/* Each byte is represented by 2 chars */
|
|
|
|
ret = str_len >> 1;
|
|
|
|
/* Check if the number of chars is a multiple of 2 and that the number of
|
|
* bytes does not exceed the max MAC frame payload supported.
|
|
*/
|
|
|
|
if ((str_len & 1) || (ret > IEEE802154_MAX_MAC_PAYLOAD_SIZE))
|
|
{
|
|
fprintf(stderr, "ERROR: Invalid payload\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* Decode hex packet */
|
|
|
|
while (str_len > 0)
|
|
{
|
|
int dat;
|
|
if (sscanf(str, "%2x", &dat) == 1)
|
|
{
|
|
buf[i++] = dat;
|
|
str += 2;
|
|
str_len -= 2;
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "ERROR: Invalid payload\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: i8sak_str2long
|
|
*
|
|
* Description:
|
|
* Convert a hex string to an integer value
|
|
*
|
|
****************************************************************************/
|
|
|
|
long i8sak_str2long(FAR const char *str)
|
|
{
|
|
FAR char *endptr;
|
|
long value;
|
|
|
|
value = strtol(str, &endptr, 0);
|
|
if (*endptr != '\0')
|
|
{
|
|
fprintf(stderr, "ERROR: Garbage after numeric argument\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (value > INT_MAX || value < INT_MIN)
|
|
{
|
|
fprintf(stderr, "ERROR: Integer value out of range\n");
|
|
return LONG_MAX;
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: i8sak_str2luint8
|
|
*
|
|
* Description:
|
|
* Convert a string to an integer value
|
|
*
|
|
****************************************************************************/
|
|
|
|
uint8_t i8sak_str2luint8(FAR const char *str)
|
|
{
|
|
long value = i8sak_str2long(str);
|
|
if (value < 0 || value > UINT8_MAX)
|
|
{
|
|
fprintf(stderr, "ERROR: 8-bit value out of range\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
return (uint8_t)value;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: i8sak_str2luint16
|
|
*
|
|
* Description:
|
|
* Convert a string to an integer value
|
|
*
|
|
****************************************************************************/
|
|
|
|
uint16_t i8sak_str2luint16(FAR const char *str)
|
|
{
|
|
long value = i8sak_str2long(str);
|
|
if (value < 0 || value > UINT16_MAX)
|
|
{
|
|
fprintf(stderr, "ERROR: 16-bit value out of range\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
return (uint16_t)value;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: i8sak_char2nibble
|
|
*
|
|
* Description:
|
|
* Convert an hexadecimal character to a 4-bit nibble.
|
|
*
|
|
****************************************************************************/
|
|
|
|
uint8_t i8sak_char2nibble(char ch)
|
|
{
|
|
if (ch >= '0' && ch <= '9')
|
|
{
|
|
return ch - '0';
|
|
}
|
|
else if (ch >= 'a' && ch <= 'f')
|
|
{
|
|
return ch - 'a' + 10;
|
|
}
|
|
else if (ch >= 'A' && ch <= 'F')
|
|
{
|
|
return ch - 'A' + 10;
|
|
}
|
|
else if (ch == '\0')
|
|
{
|
|
fprintf(stderr, "ERROR: Unexpected end hex\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "ERROR: Unexpected character in hex value: %02x\n", ch);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: i8sak_str2eaddr
|
|
*
|
|
* Description:
|
|
* Convert a string 8-byte EADDR array.
|
|
*
|
|
****************************************************************************/
|
|
|
|
void i8sak_str2eaddr(FAR const char *str, FAR uint8_t *eaddr)
|
|
{
|
|
FAR const char *src = str;
|
|
uint8_t bvalue;
|
|
char ch;
|
|
int i;
|
|
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
ch = (char)*src++;
|
|
bvalue = i8sak_char2nibble(ch) << 4;
|
|
|
|
ch = (char)*src++;
|
|
bvalue |= i8sak_char2nibble(ch);
|
|
|
|
*eaddr++ = bvalue;
|
|
|
|
if (i < 7)
|
|
{
|
|
ch = (char)*src++;
|
|
if (ch != ':')
|
|
{
|
|
fprintf(stderr, "ERROR: Missing colon separator: %s\n", str);
|
|
fprintf(stderr, " Expected xx:xx:xx:xx:xx:xx:xx:xx\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: i8sak_str2saddr
|
|
*
|
|
* Description:
|
|
* Convert a string 2-byte SADDR array.
|
|
*
|
|
****************************************************************************/
|
|
|
|
void i8sak_str2saddr(FAR const char *str, FAR uint8_t *saddr)
|
|
{
|
|
FAR const char *src = str;
|
|
uint8_t bvalue;
|
|
char ch;
|
|
int i;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
ch = (char)*src++;
|
|
bvalue = i8sak_char2nibble(ch) << 4;
|
|
|
|
ch = (char)*src++;
|
|
bvalue |= i8sak_char2nibble(ch);
|
|
|
|
*saddr++ = bvalue;
|
|
|
|
if (i < 1)
|
|
{
|
|
ch = (char)*src++;
|
|
if (ch != ':')
|
|
{
|
|
fprintf(stderr, "ERROR: Missing colon separator: %s\n", str);
|
|
fprintf(stderr, " Expected xx:xx\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: i8sak_str2panid
|
|
*
|
|
* Description:
|
|
* Convert a string 2-byte PAN ID array.
|
|
*
|
|
****************************************************************************/
|
|
|
|
void i8sak_str2panid(FAR const char *str, FAR uint8_t *panid)
|
|
{
|
|
FAR const char *src = str;
|
|
uint8_t bvalue;
|
|
char ch;
|
|
int i;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
ch = (char)*src++;
|
|
bvalue = i8sak_char2nibble(ch) << 4;
|
|
|
|
ch = (char)*src++;
|
|
bvalue |= i8sak_char2nibble(ch);
|
|
|
|
*panid++ = bvalue;
|
|
|
|
if (i < 1)
|
|
{
|
|
ch = (char)*src++;
|
|
if (ch != ':')
|
|
{
|
|
fprintf(stderr, "ERROR: Missing colon separator: %s\n", str);
|
|
fprintf(stderr, " Expected xx:xx\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: i8sak_str2bool
|
|
*
|
|
* Description:
|
|
* Convert a boolean name to a boolean value.
|
|
*
|
|
****************************************************************************/
|
|
|
|
bool i8sak_str2bool(FAR const char *str)
|
|
{
|
|
if (strcasecmp(str, "true") == 0)
|
|
{
|
|
return true;
|
|
}
|
|
else if (strcasecmp(str, "false") == 0)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "ERROR: Invalid boolean name: %s\n", str);
|
|
fprintf(stderr, " Expected true or false\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
static void i8sak_switch_instance(FAR char *ifname)
|
|
{
|
|
FAR struct i8sak_s *i8sak;
|
|
|
|
/* Search list of i8sak instances for one associated with the provided
|
|
* device.
|
|
*/
|
|
|
|
i8sak = (FAR struct i8sak_s *)sq_peek(&g_i8sak_instances);
|
|
|
|
while (i8sak != NULL)
|
|
{
|
|
if (strcmp(ifname, i8sak->ifname) == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
i8sak = (FAR struct i8sak_s *)sq_next((FAR sq_entry_t *)i8sak);
|
|
}
|
|
|
|
/* If there isn't a i8sak instance started for this device, allocate one */
|
|
|
|
if (i8sak == NULL)
|
|
{
|
|
i8sak = (FAR struct i8sak_s *)sq_remfirst(&g_i8sak_free);
|
|
if (i8sak == NULL)
|
|
{
|
|
fprintf(stderr, "ERROR: Failed to allocate i8sak instance\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
sq_addlast((FAR sq_entry_t *)i8sak, &g_i8sak_instances);
|
|
|
|
/* Update our "sticky" i8sak instance. Must come before call to setup so that
|
|
* the shared active global i8sak is correct.
|
|
*/
|
|
|
|
g_activei8sak = i8sak;
|
|
|
|
if (i8sak_setup(i8sak, ifname) < 0)
|
|
{
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_activei8sak = i8sak;
|
|
}
|
|
|
|
g_activei8sak_set = true;
|
|
}
|
|
|
|
static int i8sak_setup(FAR struct i8sak_s *i8sak, FAR const char *ifname)
|
|
{
|
|
char daemonname[I8SAK_MAX_DAEMONNAME];
|
|
int i;
|
|
int ret;
|
|
int len;
|
|
int fd = 0;
|
|
|
|
if (i8sak->initialized)
|
|
{
|
|
return OK;
|
|
}
|
|
|
|
i8sak->chan = 11;
|
|
i8sak->chpage = 0;
|
|
|
|
if (strlen(ifname) > I8SAK_MAX_IFNAME)
|
|
{
|
|
fprintf(stderr, "ERROR: ifname too long\n");
|
|
return ERROR;
|
|
}
|
|
|
|
strcpy(&i8sak->ifname[0], ifname);
|
|
|
|
i8sak->addrmode = IEEE802154_ADDRMODE_SHORT;
|
|
|
|
/* Initialize the default remote endpoint address */
|
|
|
|
i8sak->ep_addr.mode = IEEE802154_ADDRMODE_SHORT;
|
|
|
|
for (i = 0; i < IEEE802154_EADDRSIZE; i++)
|
|
{
|
|
i8sak->ep_addr.eaddr[i] =
|
|
(uint8_t)((CONFIG_IEEE802154_I8SAK_DEFAULT_EP_EADDR >> (i*8)) & 0xFF);
|
|
}
|
|
|
|
for (i = 0; i < IEEE802154_SADDRSIZE; i++)
|
|
{
|
|
i8sak->ep_addr.saddr[i] =
|
|
(uint8_t)((CONFIG_IEEE802154_I8SAK_DEFAULT_EP_SADDR >> (i*8)) & 0xFF);
|
|
}
|
|
|
|
for (i = 0; i < IEEE802154_PANIDSIZE; i++)
|
|
{
|
|
i8sak->ep_addr.panid[i] =
|
|
(uint8_t)((CONFIG_IEEE802154_I8SAK_DEFAULT_EP_PANID >> (i*8)) & 0xFF);
|
|
}
|
|
|
|
#ifdef CONFIG_NET_6LOWPAN
|
|
i8sak->ep_in6addr.sin6_family = AF_INET6;
|
|
i8sak->ep_in6addr.sin6_port = HTONS(CONFIG_IEEE802154_I8SAK_DEFAULT_PORT);
|
|
|
|
i8sak_update_ep_ip(i8sak);
|
|
|
|
i8sak->snifferport = HTONS(CONFIG_IEEE802154_I8SAK_DEFAULT_PORT);
|
|
#endif
|
|
|
|
/* Set the next association device to the default device address, so that
|
|
* the first device to request association gets that address.
|
|
*/
|
|
|
|
for (i = 0; i < IEEE802154_SADDRSIZE; i++)
|
|
{
|
|
i8sak->next_saddr[i] =
|
|
(uint8_t)(((CONFIG_IEEE802154_I8SAK_DEFAULT_EP_SADDR + 1) >> (i*8)) & 0xFF);
|
|
}
|
|
|
|
/* Check if argument starts with /dev/ */
|
|
|
|
if (strncmp(ifname, "/dev/", 5) == 0)
|
|
{
|
|
i8sak->mode = I8SAK_MODE_CHAR;
|
|
}
|
|
else
|
|
{
|
|
i8sak->mode = I8SAK_MODE_NETIF;
|
|
}
|
|
|
|
if (i8sak->mode == I8SAK_MODE_CHAR)
|
|
{
|
|
fd = open(i8sak->ifname, O_RDWR);
|
|
if (fd < 0)
|
|
{
|
|
fprintf(stderr, "ERROR: cannot open %s, errno=%d\n", i8sak->ifname, errno);
|
|
i8sak_cmd_error(i8sak);
|
|
}
|
|
}
|
|
#ifdef CONFIG_NET_6LOWPAN
|
|
else if (i8sak->mode == I8SAK_MODE_NETIF)
|
|
{
|
|
fd = socket(PF_INET6, SOCK_DGRAM, 0);
|
|
if (fd < 0)
|
|
{
|
|
fprintf(stderr, "ERROR: failed to open socket, errno=%d\n", errno);
|
|
i8sak_cmd_error(i8sak);
|
|
}
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
close(fd);
|
|
return ERROR;
|
|
}
|
|
|
|
close(fd);
|
|
|
|
sem_init(&i8sak->exclsem, 0, 1);
|
|
|
|
sem_init(&i8sak->updatesem, 0, 0);
|
|
sem_setprotocol(&i8sak->updatesem, SEM_PRIO_NONE);
|
|
|
|
sem_init(&i8sak->sigsem, 0, 0);
|
|
sem_setprotocol(&i8sak->sigsem, SEM_PRIO_NONE);
|
|
|
|
sem_init(&i8sak->eventsem, 0, 1);
|
|
|
|
i8sak->daemon_started = false;
|
|
i8sak->daemon_shutdown = false;
|
|
|
|
i8sak->eventlistener_run = false;
|
|
sq_init(&i8sak->eventreceivers);
|
|
sq_init(&i8sak->eventreceivers_free);
|
|
for (i = 0; i < CONFIG_I8SAK_NEVENTRECEIVERS; i++)
|
|
{
|
|
sq_addlast((FAR sq_entry_t *)&i8sak->eventreceiver_pool[i], &i8sak->eventreceivers_free);
|
|
}
|
|
|
|
i8sak->blasterperiod = 1000;
|
|
|
|
/* Create strings for task based on device. i.e. i8_ieee0 */
|
|
|
|
if (i8sak->mode == I8SAK_MODE_CHAR)
|
|
{
|
|
len = strlen(ifname);
|
|
snprintf(daemonname, I8SAK_DAEMONNAME_PREFIX_LEN + (len - 5),
|
|
I8SAK_DAEMONNAME_FMT, &ifname[5]);
|
|
}
|
|
#ifdef CONFIG_NET_6LOWPAN
|
|
else if (i8sak->mode == I8SAK_MODE_NETIF)
|
|
{
|
|
len = strlen(ifname);
|
|
snprintf(daemonname, I8SAK_DAEMONNAME_PREFIX_LEN + len,
|
|
I8SAK_DAEMONNAME_FMT, ifname);
|
|
}
|
|
#endif
|
|
|
|
i8sak->daemon_pid = task_create(daemonname, CONFIG_IEEE802154_I8SAK_PRIORITY,
|
|
CONFIG_IEEE802154_I8SAK_STACKSIZE, i8sak_daemon,
|
|
NULL);
|
|
if (i8sak->daemon_pid < 0)
|
|
{
|
|
fprintf(stderr, "failed to start daemon\n");
|
|
return ERROR;
|
|
}
|
|
|
|
/* Use the signal semaphore to wait for daemon to start before returning */
|
|
|
|
ret = sem_wait(&i8sak->sigsem);
|
|
if (ret < 0)
|
|
{
|
|
fprintf(stderr, "i8sak:interrupted while daemon starting\n");
|
|
return ERROR;
|
|
}
|
|
|
|
i8sak->initialized = true;
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name : i8sak_daemon
|
|
*
|
|
* Description :
|
|
* Runs command in seperate task
|
|
****************************************************************************/
|
|
|
|
static int i8sak_daemon(int argc, FAR char *argv[])
|
|
{
|
|
FAR struct i8sak_s *i8sak = g_activei8sak;
|
|
int ret;
|
|
|
|
fprintf(stderr, "i8sak: daemon started\n");
|
|
i8sak->daemon_started = true;
|
|
|
|
if (i8sak->mode == I8SAK_MODE_CHAR)
|
|
{
|
|
i8sak->fd = open(i8sak->ifname, O_RDWR);
|
|
if (i8sak->fd < 0)
|
|
{
|
|
fprintf(stderr, "ERROR: cannot open %s, errno=%d\n", i8sak->ifname, errno);
|
|
i8sak->daemon_started = false;
|
|
ret = errno;
|
|
return ret;
|
|
}
|
|
}
|
|
#ifdef CONFIG_NET_6LOWPAN
|
|
else if (i8sak->mode == I8SAK_MODE_NETIF)
|
|
{
|
|
i8sak->fd = socket(PF_INET6, SOCK_DGRAM, 0);
|
|
if (i8sak->fd < 0)
|
|
{
|
|
fprintf(stderr, "ERROR: failed to open socket, errno=%d\n", errno);
|
|
i8sak->daemon_started = false;
|
|
ret = errno;
|
|
return ret;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
i8sak_eventlistener_start(i8sak);
|
|
|
|
/* Signal the calling thread that the daemon is up and running */
|
|
|
|
sem_post(&i8sak->sigsem);
|
|
|
|
while (!i8sak->daemon_shutdown)
|
|
{
|
|
ret = sem_wait(&i8sak->updatesem);
|
|
if (ret != OK)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (i8sak->startblaster)
|
|
{
|
|
i8sak->blasterenabled = true;
|
|
i8sak->startblaster = false;
|
|
|
|
ret = pthread_create(&i8sak->blaster_threadid, NULL, i8sak_blaster_thread,
|
|
(void *)i8sak);
|
|
if (ret != 0)
|
|
{
|
|
fprintf(stderr, "failed to start blaster thread: %d\n", ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (i8sak->startsniffer)
|
|
{
|
|
i8sak->snifferenabled = true;
|
|
i8sak->startsniffer = false;
|
|
|
|
ret = pthread_create(&i8sak->sniffer_threadid, NULL, i8sak_sniffer_thread,
|
|
(void *)i8sak);
|
|
if (ret != 0)
|
|
{
|
|
fprintf(stderr, "failed to start sniffer thread: %d\n", ret);
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
i8sak_eventlistener_stop(i8sak);
|
|
i8sak->daemon_started = false;
|
|
close(i8sak->fd);
|
|
printf("i8sak: daemon closing\n");
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: i8sak_showusage
|
|
*
|
|
* Description:
|
|
* Show program usage.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int i8sak_showusage(FAR const char *progname, int exitcode)
|
|
{
|
|
fprintf(stderr, "Usage (Use the -h option on any command to get more info): %s\n"
|
|
" acceptassoc [-h|e]\n"
|
|
" assoc [-h|p|e|s|w|r|t]\n"
|
|
" blaster [-h|q|f|p]\n"
|
|
" get [-h] parameter"
|
|
" poll [-h]\n"
|
|
" regdump [-h]\n"
|
|
" reset [-h]\n"
|
|
" scan [-h|p|a|e] minch-maxch\n"
|
|
" set [-h] param val"
|
|
" sniffer [-h|d]\n"
|
|
" startpan [-h|b|s] xx:xx\n"
|
|
" tx [-h|d|m] <hex-payload>\n"
|
|
, progname);
|
|
exit(exitcode);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* i8_main
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_BUILD_KERNEL
|
|
int main(int argc, FAR char *argv[])
|
|
#else
|
|
int i8_main(int argc, char *argv[])
|
|
#endif
|
|
{
|
|
FAR const struct i8sak_command_s *i8sakcmd;
|
|
int argind;
|
|
int ret;
|
|
int i;
|
|
|
|
if (!g_i8sak_initialized)
|
|
{
|
|
sq_init(&g_i8sak_free);
|
|
sq_init(&g_i8sak_instances);
|
|
for (i = 0; i < CONFIG_IEEE802154_I8SAK_NINSTANCES; i++)
|
|
{
|
|
sq_addlast((FAR sq_entry_t *)&g_i8sak_pool[i], &g_i8sak_free);
|
|
g_i8sak_pool[i].initialized = false;
|
|
}
|
|
|
|
g_i8sak_initialized = true;
|
|
}
|
|
|
|
argind = 1;
|
|
|
|
/* Check if ifname was included */
|
|
|
|
if (argc > 1)
|
|
{
|
|
/* Check if argument starts with /dev/ */
|
|
|
|
if ((strncmp(argv[argind], "/dev/", 5) == 0) ||
|
|
(strncmp(argv[argind], "wpan", 4) == 0))
|
|
{
|
|
|
|
i8sak_switch_instance(argv[argind]);
|
|
argind++;
|
|
|
|
if (argc == 2)
|
|
{
|
|
/* Close silently to allow user to set ifname without any
|
|
* other operation.
|
|
*/
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
}
|
|
|
|
/* Argument must be command */
|
|
}
|
|
|
|
/* If ifname wasn't included, we need to check if our sticky feature has
|
|
* ever been set.
|
|
*/
|
|
|
|
if (!g_activei8sak_set)
|
|
{
|
|
fprintf(stderr, "ERROR: Must include ifname the first time you run\n");
|
|
i8sak_showusage(argv[0], EXIT_FAILURE);
|
|
}
|
|
|
|
/* Find the command in the g_i8sak_command[] list */
|
|
|
|
i8sakcmd = NULL;
|
|
for (i = 0; i < NCOMMANDS; i++)
|
|
{
|
|
FAR const struct i8sak_command_s *cmd = &g_i8sak_commands[i];
|
|
if (strcmp(argv[argind], cmd->name) == 0)
|
|
{
|
|
i8sakcmd = cmd;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i8sakcmd == NULL)
|
|
{
|
|
i8sak_showusage(argv[0], EXIT_FAILURE);
|
|
}
|
|
|
|
/* Special case the help command which has no handler */
|
|
|
|
if (i8sakcmd->handler == NULL)
|
|
{
|
|
i8sak_showusage(argv[0], EXIT_SUCCESS);
|
|
}
|
|
|
|
ret = sem_wait(&g_activei8sak->exclsem);
|
|
if (ret < 0)
|
|
{
|
|
fprintf(stderr, "ERROR: Failed to lock i8sak instance\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
i8sakcmd->handler(g_activei8sak, argc - argind, &argv[argind]);
|
|
|
|
sem_post(&g_activei8sak->exclsem);
|
|
return EXIT_SUCCESS;
|
|
}
|