diff --git a/configs/sim/README.txt b/configs/sim/README.txt index be8d47456c4..fc5dcb10880 100644 --- a/configs/sim/README.txt +++ b/configs/sim/README.txt @@ -829,6 +829,12 @@ pashello Configures to use apps/examples/pashello. +pktradio + + This configuration is identical to the 'sixlowpan configuration + described below EXCEPT that is uses the genericl packet radio + loopback network device. + sixlowpan This configuration was intended only for unit-level testing of the @@ -841,6 +847,8 @@ sixlowpan packets on output to the loopback device and correctly decodes the returned packet. + See also the 'pktradio' configuration. + touchscreen This configuration uses the simple touchscreen test at diff --git a/configs/sim/pktradio/defconfig b/configs/sim/pktradio/defconfig new file mode 100644 index 00000000000..08872ccffca --- /dev/null +++ b/configs/sim/pktradio/defconfig @@ -0,0 +1,106 @@ +# CONFIG_MMCSD_MMCSUPPORT is not set +# CONFIG_MMCSD_SPI is not set +# CONFIG_NET_ETHERNET is not set +# CONFIG_NET_IPv4 is not set +# CONFIG_NET_UDP_CHECKSUMS is not set +# CONFIG_NSH_CMDOPT_DF_H is not set +# CONFIG_SIM_NETDEV is not set +CONFIG_ARCH_BOARD_SIM=y +CONFIG_ARCH_BOARD="sim" +CONFIG_ARCH_SIM=y +CONFIG_ARCH_STACKDUMP=y +CONFIG_ARCH="sim" +CONFIG_AT24XX_ADDR=0x57 +CONFIG_AT24XX_EXTENDED=y +CONFIG_AT24XX_EXTSIZE=160 +CONFIG_AT24XX_SIZE=2 +CONFIG_BOARD_LOOPSPERMSEC=51262 +CONFIG_BOARDCTL_POWEROFF=y +CONFIG_BUILTIN_PROXY_STACKSIZE=2048 +CONFIG_BUILTIN=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_DISABLE_POLL=y +CONFIG_DRIVERS_WIRELESS=y +CONFIG_EXAMPLES_NETTEST_SERVER_PORTNO=61616 +CONFIG_EXAMPLES_NETTEST_SERVERIPv6ADDR_1=0xfe80 +CONFIG_EXAMPLES_NETTEST_SERVERIPv6ADDR_6=0x00ff +CONFIG_EXAMPLES_NETTEST_SERVERIPv6ADDR_7=0xfe00 +CONFIG_EXAMPLES_NETTEST_SERVERIPv6ADDR_8=0xcda9 +CONFIG_EXAMPLES_NETTEST_STACKSIZE1=4096 +CONFIG_EXAMPLES_NETTEST=y +CONFIG_EXAMPLES_NSH=y +CONFIG_EXAMPLES_UDPBLASTER_HOSTIPv6_1=0xfe80 +CONFIG_EXAMPLES_UDPBLASTER_HOSTIPv6_6=0x00ff +CONFIG_EXAMPLES_UDPBLASTER_HOSTIPv6_7=0xfe00 +CONFIG_EXAMPLES_UDPBLASTER_HOSTIPv6_8=0x1034 +CONFIG_EXAMPLES_UDPBLASTER_STACKSIZE=8192 +CONFIG_EXAMPLES_UDPBLASTER_TARGETIPv6_1=0xfe80 +CONFIG_EXAMPLES_UDPBLASTER_TARGETIPv6_6=0x00ff +CONFIG_EXAMPLES_UDPBLASTER_TARGETIPv6_7=0xfe00 +CONFIG_EXAMPLES_UDPBLASTER_TARGETIPv6_8=0xa9cd +CONFIG_EXAMPLES_UDPBLASTER=y +CONFIG_EXPERIMENTAL=y +CONFIG_FAT_LCNAMES=y +CONFIG_FAT_LFN=y +CONFIG_FS_FAT=y +CONFIG_FS_PROCFS=y +CONFIG_I2C_DRIVER=y +CONFIG_IDLETHREAD_STACKSIZE=4096 +CONFIG_IOB_BUFSIZE=128 +CONFIG_IOB_NBUFFERS=48 +CONFIG_MAX_TASKS=16 +CONFIG_MAX_WDOGPARMS=2 +CONFIG_MMCSD_MULTIBLOCK_DISABLE=y +CONFIG_MMCSD=y +CONFIG_MTD_AT24XX=y +CONFIG_MTD_AT25=y +CONFIG_MTD_CONFIG=y +CONFIG_MTD=y +CONFIG_NET_6LOWPAN_TCP_RECVWNDO=102 +CONFIG_NET_6LOWPAN=y +CONFIG_NET_BROADCAST=y +CONFIG_NET_HOSTNAME="SAMV71-XULT" +CONFIG_NET_IPv6=y +CONFIG_NET_SOCKOPTS=y +CONFIG_NET_STATISTICS=y +CONFIG_NET_TCP_WRITE_BUFFERS=y +CONFIG_NET_TCP=y +CONFIG_NET_TCPBACKLOG=y +CONFIG_NET_UDP=y +CONFIG_NET=y +CONFIG_NETDB_DNSCLIENT=y +CONFIG_NETDEV_MULTINIC=y +CONFIG_NETDEV_PHY_IOCTL=y +CONFIG_NETDEV_STATISTICS=y +CONFIG_NETDEV_TELNET=y +CONFIG_NETDEVICES=y +CONFIG_NFILE_DESCRIPTORS=8 +CONFIG_NFILE_STREAMS=8 +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_LINELEN=64 +CONFIG_NSH_NOMAC=y +CONFIG_NSH_READLINE=y +CONFIG_PKTRADIO_LOOPBACK=y +CONFIG_POSIX_SPAWN_PROXY_STACKSIZE=2048 +CONFIG_PREALLOC_MQ_MSGS=4 +CONFIG_PREALLOC_TIMERS=4 +CONFIG_PTHREAD_STACK_DEFAULT=4096 +CONFIG_RAM_SIZE=393216 +CONFIG_RAM_START=0x20400000 +CONFIG_RAW_BINARY=y +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_HPWORK=y +CONFIG_SCHED_HPWORKSTACKSIZE=4096 +CONFIG_SCHED_WAITPID=y +CONFIG_SDCLONE_DISABLE=y +CONFIG_STANDARD_SERIAL=y +CONFIG_START_DAY=10 +CONFIG_START_MONTH=3 +CONFIG_START_YEAR=2014 +CONFIG_TASK_SPAWN_DEFAULT_STACKSIZE=4096 +CONFIG_USER_ENTRYPOINT="nsh_main" +CONFIG_USERMAIN_STACKSIZE=8192 +CONFIG_WIRELESS_PKTRADIO=y +CONFIG_WIRELESS=y diff --git a/configs/sim/src/sim_bringup.c b/configs/sim/src/sim_bringup.c index 8dd0f75bdd1..8de231bc14d 100644 --- a/configs/sim/src/sim_bringup.c +++ b/configs/sim/src/sim_bringup.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include "up_internal.h" @@ -150,5 +151,15 @@ int sim_bringup(void) } #endif +#ifdef CONFIG_PKTRADIO_LOOPBACK + /* Initialize and register the IEEE802.15.4 MAC network loop device */ + + ret = pktradio_loopback(); + if (ret < 0) + { + _err("ERROR: pktradio_loopback() failed: %d\n", ret); + } +#endif + return OK; } diff --git a/include/nuttx/wireless/pktradio.h b/include/nuttx/wireless/pktradio.h index e830e3e65c8..3fd2a6a22e8 100644 --- a/include/nuttx/wireless/pktradio.h +++ b/include/nuttx/wireless/pktradio.h @@ -159,5 +159,25 @@ FAR struct pktradio_metadata_s *pktradio_metadata_allocate(void); void pktradio_metadata_free(FAR struct pktradio_metadata_s *metadata); +/**************************************************************************** + * Name: pktradio_loopback + * + * Description: + * Initialize and register the Ieee802.15.4 MAC loopback network driver. + * + * Parameters: + * None + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef CONFIG_PKTRADIO_LOOPBACK +int pktradio_loopback(void); +#endif + #endif /* CONFIG_WIRELESS_PKTRADIO */ #endif /* __INCLUDE_NUTTX_WIRELESS_PKTRADIO_H */ diff --git a/net/neighbor/neighbor_dumpentry.c b/net/neighbor/neighbor_dumpentry.c index 6d730f17339..0a710b7e2a4 100644 --- a/net/neighbor/neighbor_dumpentry.c +++ b/net/neighbor/neighbor_dumpentry.c @@ -51,7 +51,7 @@ ****************************************************************************/ /****************************************************************************** - * Name: neibhbor_dump_address + * Name: neighbor_dump_address * * Description: * Dump a data buffer to the SYSLOG. @@ -65,7 +65,7 @@ * ******************************************************************************/ -static void neibhbor_dump_address(FAR const uint8_t *buffer, unsigned int buflen) +static void neighbor_dump_address(FAR const uint8_t *buffer, unsigned int buflen) { char outbuf[16*3 + 9]; /* 6-byte header header + 16 hex bytes + * 2 space separator + NUL termination */ @@ -143,7 +143,7 @@ void neighbor_dumpentry(FAR const char *msg, if (neighbor->ne_addr.u.na_lltype == NET_LL_ETHERNET) #endif { - neibhbor_dump_address(neighbor->ne_addr.u.na_ethernet.ether_addr_octet, + neighbor_dump_address(neighbor->ne_addr.u.na_ethernet.ether_addr_octet, neighbor->ne_addr.na_llsize); } #endif @@ -153,7 +153,7 @@ void neighbor_dumpentry(FAR const char *msg, else #endif { - neibhbor_dump_address(neighbor->ne_addr.u.na_sixlowpan.nm_addr, + neighbor_dump_address(neighbor->ne_addr.u.na_sixlowpan.nm_addr, neighbor->ne_addr.na_llsize); } #endif /* CONFIG_NET_6LOWPAN */ diff --git a/wireless/ieee802154/mac802154_loopback.c b/wireless/ieee802154/mac802154_loopback.c index 79617036ba0..edb73d389d3 100644 --- a/wireless/ieee802154/mac802154_loopback.c +++ b/wireless/ieee802154/mac802154_loopback.c @@ -106,7 +106,7 @@ struct lo_driver_s /* This holds the information visible to the NuttX network */ - struct sixlowpan_driver_s lo_ieee; /* Interface understood by the network */ + struct sixlowpan_driver_s lo_radio; /* Interface understood by the network */ }; /**************************************************************************** @@ -153,12 +153,10 @@ static int lo_ifup(FAR struct net_driver_s *dev); static int lo_ifdown(FAR struct net_driver_s *dev); static void lo_txavail_work(FAR void *arg); static int lo_txavail(FAR struct net_driver_s *dev); -#if defined(CONFIG_NET_IGMP) || defined(CONFIG_NET_ICMPv6) -static int lo_addmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac); #ifdef CONFIG_NET_IGMP +static int lo_addmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac); static int lo_rmmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac); #endif -#endif #ifdef CONFIG_NETDEV_IOCTL static int lo_ioctl(FAR struct net_driver_s *dev, int cmd, unsigned long arg); @@ -323,7 +321,7 @@ static int lo_loopback(FAR struct net_driver_s *dev) /* Increment statistics */ - NETDEV_RXPACKETS(&priv->lo_ieee.r_dev); + NETDEV_RXPACKETS(&priv->lo_radio.r_dev); /* Remove the IOB from the queue */ @@ -343,17 +341,17 @@ static int lo_loopback(FAR struct net_driver_s *dev) ninfo("Send frame %p to the network: Offset=%u Length=%u\n", iob, iob->io_offset, iob->io_len); - ret = sixlowpan_input(&priv->lo_ieee, iob, (FAR void *)&ind); + ret = sixlowpan_input(&priv->lo_radio, iob, (FAR void *)&ind); /* Increment statistics */ - NETDEV_TXPACKETS(&priv->lo_ieee.r_dev); + NETDEV_TXPACKETS(&priv->lo_radio.r_dev); if (ret < 0) { nerr("ERROR: sixlowpan_input returned %d\n", ret); - NETDEV_TXERRORS(&priv->lo_ieee.r_dev); - NETDEV_ERRORS(&priv->lo_ieee.r_dev); + NETDEV_TXERRORS(&priv->lo_radio.r_dev); + NETDEV_ERRORS(&priv->lo_radio.r_dev); } } @@ -384,7 +382,7 @@ static void lo_loopback_work(FAR void *arg) /* Perform the loopback */ net_lock(); - (void)lo_loopback(&priv->lo_ieee.r_dev); + (void)lo_loopback(&priv->lo_radio.r_dev); net_unlock(); } @@ -412,7 +410,7 @@ static void lo_poll_work(FAR void *arg) /* Perform the poll */ net_lock(); - (void)devif_timer(&priv->lo_ieee.r_dev, lo_loopback); + (void)devif_timer(&priv->lo_radio.r_dev, lo_loopback); /* Setup the watchdog poll timer again */ @@ -566,7 +564,7 @@ static void lo_txavail_work(FAR void *arg) { /* If so, then poll the network for new XMIT data */ - (void)devif_poll(&priv->lo_ieee.r_dev, lo_loopback); + (void)devif_poll(&priv->lo_radio.r_dev, lo_loopback); } net_unlock(); @@ -636,7 +634,7 @@ static int lo_txavail(FAR struct net_driver_s *dev) * ****************************************************************************/ -#if defined(CONFIG_NET_IGMP) || defined(CONFIG_NET_ICMPv6) +#ifdef CONFIG_NET_IGMP static int lo_addmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac) { #ifdef CONFIG_NET_6LOWPAN_EXTENDEDADDR @@ -854,7 +852,7 @@ static int lo_req_data(FAR struct sixlowpan_driver_s *netdev, { /* Increment statistics */ - NETDEV_RXPACKETS(&priv->lo_ieee.r_dev); + NETDEV_RXPACKETS(&priv->lo_radio.r_dev); /* Remove the IOB from the queue */ @@ -953,7 +951,7 @@ int ieee8021514_loopback(void) memset(priv, 0, sizeof(struct lo_driver_s)); - radio = &priv->lo_ieee; + radio = &priv->lo_radio; dev = &radio->r_dev; dev->d_ifup = lo_ifup; /* I/F up (new IP address) callback */ dev->d_ifdown = lo_ifdown; /* I/F down callback */ @@ -987,12 +985,12 @@ int ieee8021514_loopback(void) * performed. */ - (void)netdev_register(&priv->lo_ieee.r_dev, NET_LL_IEEE802154); + (void)netdev_register(&priv->lo_radio.r_dev, NET_LL_IEEE802154); /* Put the network in the UP state */ dev->d_flags = IFF_UP; - return lo_ifup(&priv->lo_ieee.r_dev); + return lo_ifup(&priv->lo_radio.r_dev); } #endif /* CONFIG_IEEE802154_LOOPBACK */ diff --git a/wireless/ieee802154/mac802154_netdev.c b/wireless/ieee802154/mac802154_netdev.c index 5e038dd4a38..5678b0460ad 100644 --- a/wireless/ieee802154/mac802154_netdev.c +++ b/wireless/ieee802154/mac802154_netdev.c @@ -172,14 +172,12 @@ static int macnet_ifdown(FAR struct net_driver_s *dev); static void macnet_txavail_work(FAR void *arg); static int macnet_txavail(FAR struct net_driver_s *dev); -#if defined(CONFIG_NET_IGMP) || defined(CONFIG_NET_ICMPv6) +#ifdef CONFIG_NET_IGMP static int macnet_addmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac); -#ifdef CONFIG_NET_IGMP static int macnet_rmmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac); #endif -#endif #ifdef CONFIG_NETDEV_IOCTL static int macnet_ioctl(FAR struct net_driver_s *dev, int cmd, unsigned long arg); @@ -742,7 +740,7 @@ static int macnet_txavail(FAR struct net_driver_s *dev) * ****************************************************************************/ -#if defined(CONFIG_NET_IGMP) || defined(CONFIG_NET_ICMPv6) +#ifdef CONFIG_NET_IGMP static int macnet_addmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac) { FAR struct macnet_driver_s *priv = (FAR struct macnet_driver_s *)dev->d_private; diff --git a/wireless/pktradio/Kconfig b/wireless/pktradio/Kconfig index 5d4fc4fa751..6dd4c8f45be 100644 --- a/wireless/pktradio/Kconfig +++ b/wireless/pktradio/Kconfig @@ -32,4 +32,12 @@ config PKTRADIO_NRXMETA This specifies the total number of preallocated meta data structures must be allocated with each incoming packet. +config PKTRADIO_LOOPBACK + bool "PktRadio 6LoWPAN Loopback" + default n + depends on NET_6LOWPAN && NET_IPv6 + select ARCH_HAVE_NETDEV_STATISTICS + ---help--- + Add support for the PktRadio 6LoWPAN Loopback test device. + endif # WIRELESS_PKTRADIO diff --git a/wireless/pktradio/Make.defs b/wireless/pktradio/Make.defs index cfc0438e17d..c73e5f16658 100644 --- a/wireless/pktradio/Make.defs +++ b/wireless/pktradio/Make.defs @@ -39,6 +39,10 @@ ifeq ($(CONFIG_WIRELESS_PKTRADIO),y) CSRCS += pktradio_metadata.c +ifeq ($(CONFIG_PKTRADIO_LOOPBACK),y) +CSRCS += pktradio_loopback.c +endif + DEPPATH += --dep-path pktradio VPATH += :pktradio CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" $(TOPDIR)$(DELIM)wireless$(DELIM)pktradio} diff --git a/wireless/pktradio/pktradio_loopback.c b/wireless/pktradio/pktradio_loopback.c new file mode 100644 index 00000000000..eea89ebce69 --- /dev/null +++ b/wireless/pktradio/pktradio_loopback.c @@ -0,0 +1,941 @@ +/**************************************************************************** + * wireless/pktradio/pktradio_loopback.c + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PKTRADIO_LOOPBACK + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* We need to have the work queue to handle SPI interrupts */ + +#if !defined(CONFIG_SCHED_WORKQUEUE) +# error Worker thread support is required (CONFIG_SCHED_WORKQUEUE) +#else +# if defined(CONFIG_SCHED_LPWORK) +# define LPBKWORK LPWORK +# elif defined(CONFIG_SCHED_HPWORK) +# define LPBKWORK HPWORK +# else +# error Neither CONFIG_SCHED_LPWORK nor CONFIG_SCHED_HPWORK defined +# endif +#endif + +#ifndef CONFIG_WIRELESS_PKTRADIO +# error CONFIG_WIRELESS_PKTRADIO=y is required. +#endif + +#ifndef CONFIG_NET_6LOWPAN +# error CONFIG_NET_6LOWPAN=y is required. +#endif + +#if (CONFIG_PKTRADIO_ADDRLEN != 1) && (CONFIG_PKTRADIO_ADDRLEN != 2) && \ + (CONFIG_PKTRADIO_ADDRLEN != 8) +# error No support for CONFIG_PKTRADIO_ADDRLEN other than {1,2,8} +#endif + +/* TX poll delay = 1 seconds. CLK_TCK is the number of clock ticks per second */ + +#define LO_WDDELAY (1*CLK_TCK) + +/* Fake value for MAC header length */ + +#if CONFIG_NET_6LOWPAN_FRAMELEN > 40 +# define MAC_HDRLEN 4 +#else +# define MAC_HDRLEN 0 +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* The lo_driver_s encapsulates all state information for a single hardware + * interface + */ + +struct lo_driver_s +{ + bool lo_bifup; /* true:ifup false:ifdown */ + bool lo_pending; /* True: TX poll pending */ + uint8_t lo_panid[2]; /* Fake PAN ID for testing */ + WDOG_ID lo_polldog; /* TX poll timer */ + struct work_s lo_work; /* For deferring poll work to the work queue */ + FAR struct iob_s *lo_head; /* Head of IOBs queued for loopback */ + FAR struct iob_s *lo_tail; /* Tail of IOBs queued for loopback */ + + /* This holds the information visible to the NuttX network */ + + struct sixlowpan_driver_s lo_radio; /* Interface understood by the network */ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct lo_driver_s g_loopback; +static uint8_t g_iobuffer[CONFIG_NET_6LOWPAN_MTU + CONFIG_NET_GUARDSIZE]; + +static uint8_t g_mac_addr[CONFIG_PKTRADIO_ADDRLEN] = +{ +#if CONFIG_PKTRADIO_ADDRLEN == 1 + 0xab +#elif CONFIG_PKTRADIO_ADDRLEN == 2 + 0xab, 0xcd +#elif CONFIG_PKTRADIO_ADDRLEN == 8 + 0x0c, 0xfa, 0xde, 0x00, 0xde, 0xad, 0xbe, 0xef +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Utility functions */ + +static void lo_addr2ip(FAR struct net_driver_s *dev); +static inline void lo_netmask(FAR struct net_driver_s *dev); + +/* Polling logic */ + +static int lo_loopback(FAR struct net_driver_s *dev); +static void lo_loopback_work(FAR void *arg); +static void lo_poll_work(FAR void *arg); +static void lo_poll_expiry(int argc, wdparm_t arg, ...); + +/* NuttX callback functions */ + +static int lo_ifup(FAR struct net_driver_s *dev); +static int lo_ifdown(FAR struct net_driver_s *dev); +static void lo_txavail_work(FAR void *arg); +static int lo_txavail(FAR struct net_driver_s *dev); +#ifdef CONFIG_NET_IGMP +static int lo_addmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac); +static int lo_rmmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac); +#endif +#ifdef CONFIG_NETDEV_IOCTL +static int lo_ioctl(FAR struct net_driver_s *dev, int cmd, + unsigned long arg); +#endif +static int lo_get_mhrlen(FAR struct sixlowpan_driver_s *netdev, + FAR const void *meta); +static int lo_req_data(FAR struct sixlowpan_driver_s *netdev, + FAR const void *meta, FAR struct iob_s *framelist); +static int lo_properties(FAR struct sixlowpan_driver_s *netdev, + FAR struct sixlowpan_properties_s *properties); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lo_addr2ip + * + * Description: + * Create a MAC-based IP address from the IEEE 802.15.14 short or extended + * address assigned to the node. + * + * 128 112 96 80 64 48 32 16 + * ---- ---- ---- ---- ---- ---- ---- ---- + * fe80 0000 0000 0000 0000 00ff fe00 xxxx 2-byte short address IEEE 48-bit MAC + * fe80 0000 0000 0000 xxxx xxxx xxxx xxxx 8-byte extended address IEEE EUI-64 + * + ****************************************************************************/ + +static void lo_addr2ip(FAR struct net_driver_s *dev) +{ + /* Set the MAC address as the saddr */ + + dev->d_mac.sixlowpan.nv_addrlen = CONFIG_PKTRADIO_ADDRLEN; + memcpy(dev->d_mac.sixlowpan.nv_addr, g_mac_addr, CONFIG_PKTRADIO_ADDRLEN); + + /* Set the IP address */ + + dev->d_ipv6addr[0] = HTONS(0xfe80); + dev->d_ipv6addr[1] = 0; + dev->d_ipv6addr[2] = 0; + dev->d_ipv6addr[3] = 0; + +#if CONFIG_PKTRADIO_ADDRLEN == 1 + /* Set the IP address based on the 1 byte address */ + + dev->d_ipv6addr[4] = 0; + dev->d_ipv6addr[5] = HTONS(0x00ff); + dev->d_ipv6addr[6] = HTONS(0xfe00); + dev->d_ipv6addr[7] = (uint16_t)g_mac_addr[0] << 8 ^ 0x0200; + +#elif CONFIG_PKTRADIO_ADDRLEN == 2 + /* Set the IP address based on the 2 byte address */ + + dev->d_ipv6addr[4] = 0; + dev->d_ipv6addr[5] = HTONS(0x00ff); + dev->d_ipv6addr[6] = HTONS(0xfe00); + dev->d_ipv6addr[7] = (uint16_t)g_mac_addr[0] << 8 | (uint16_t)g_mac_addr[1]; + dev->d_ipv6addr[7] ^= 0x0200; + +#elif CONFIG_PKTRADIO_ADDRLEN == 8 + /* Set the IP address based on the 8-byte address */ + + dev->d_ipv6addr[4] = (uint16_t)g_mac_addr[0] << 8 | (uint16_t)g_mac_addr[1]; + dev->d_ipv6addr[5] = (uint16_t)g_mac_addr[2] << 8 | (uint16_t)g_mac_addr[3]; + dev->d_ipv6addr[6] = (uint16_t)g_mac_addr[4] << 8 | (uint16_t)g_mac_addr[5]; + dev->d_ipv6addr[7] = (uint16_t)g_mac_addr[6] << 8 | (uint16_t)g_mac_addr[7]; + dev->d_ipv6addr[4] ^= 0x0200; +#endif +} + +/**************************************************************************** + * Name: lo_netmask + * + * Description: + * Create a netmask of a MAC-based IP address which may be based on either + * the IEEE 802.15.14 short or extended address of the MAC. + * + * 128 112 96 80 64 48 32 16 + * ---- ---- ---- ---- ---- ---- ---- ---- + * fe80 0000 0000 0000 0000 00ff fe00 xxxx 2-byte short address IEEE 48-bit MAC + * fe80 0000 0000 0000 xxxx xxxx xxxx xxxx 8-byte extended address IEEE EUI-64 + * + ****************************************************************************/ + +static inline void lo_netmask(FAR struct net_driver_s *dev) +{ + dev->d_ipv6netmask[0] = 0xffff; + dev->d_ipv6netmask[1] = 0xffff; + dev->d_ipv6netmask[2] = 0xffff; + dev->d_ipv6netmask[3] = 0xffff; + +#if CONFIG_PKTRADIO_ADDRLEN == 1 + dev->d_ipv6netmask[4] = 0xffff; + dev->d_ipv6netmask[5] = 0xffff; + dev->d_ipv6netmask[6] = 0xffff; + dev->d_ipv6netmask[7] = HTONS(0x00ff); + +#elif CONFIG_PKTRADIO_ADDRLEN == 2 + dev->d_ipv6netmask[4] = 0xffff; + dev->d_ipv6netmask[5] = 0xffff; + dev->d_ipv6netmask[6] = 0xffff; + dev->d_ipv6netmask[7] = 0; + +#elif CONFIG_PKTRADIO_ADDRLEN == 8 + dev->d_ipv6netmask[4] = 0; + dev->d_ipv6netmask[5] = 0; + dev->d_ipv6netmask[6] = 0; + dev->d_ipv6netmask[7] = 0; +#endif +} + +/**************************************************************************** + * Name: lo_loopback + * + * Description: + * Check if the network has any outgoing packets ready to send. This is + * a callback from devif_poll() or devif_timer(). devif_poll() will be + * called only during normal TX polling. + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * OK on success; a negated errno on failure + * + * Assumptions: + * May or may not be called from an interrupt handler. In either case, + * the network is locked. + * + ****************************************************************************/ + +static int lo_loopback(FAR struct net_driver_s *dev) +{ + FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)dev->d_private; + struct pktradio_metadata_s pktmeta; + FAR struct iob_s *iob; + int ret; + + /* Create some fake metadata */ + + memset(&pktmeta, 0, sizeof(struct pktradio_metadata_s)); + + pktmeta.pm_src.pa_addrlen = CONFIG_PKTRADIO_ADDRLEN; + pktmeta.pm_dest.pa_addrlen = CONFIG_PKTRADIO_ADDRLEN; + + /* On loopback the local address is both the source and destination. */ + + memcpy(pktmeta.pm_src.pa_addr, g_mac_addr, CONFIG_PKTRADIO_ADDRLEN); + memcpy(pktmeta.pm_dest.pa_addr, g_mac_addr, CONFIG_PKTRADIO_ADDRLEN); + + /* Loop while there framelist to be sent, i.e., while the freme list is not + * emtpy. Sending, of course, just means relaying back through the network + * for this driver. + */ + + while (priv->lo_head != NULL) + { + ninfo("Looping frame IOB %p\n", iob); + + /* Increment statistics */ + + NETDEV_RXPACKETS(&priv->lo_radio.r_dev); + + /* Remove the IOB from the queue */ + + iob = priv->lo_head; + priv->lo_head = iob->io_flink; + iob->io_flink = NULL; + + /* Did the framelist become empty? */ + + if (priv->lo_head == NULL) + { + priv->lo_tail = NULL; + } + + /* Return the next frame to the network */ + + ninfo("Send frame %p to the network: Offset=%u Length=%u\n", + iob, iob->io_offset, iob->io_len); + + ret = sixlowpan_input(&priv->lo_radio, iob, (FAR void *)&pktmeta); + + /* Increment statistics */ + + NETDEV_TXPACKETS(&priv->lo_radio.r_dev); + + if (ret < 0) + { + nerr("ERROR: sixlowpan_input returned %d\n", ret); + NETDEV_TXERRORS(&priv->lo_radio.r_dev); + NETDEV_ERRORS(&priv->lo_radio.r_dev); + } + } + + return 0; +} + +/**************************************************************************** + * Name: lo_loopback_work + * + * Description: + * Perform loopback of received framelist. + * + * Parameters: + * arg - The argument passed when work_queue() as called. + * + * Returned Value: + * OK on success + * + * Assumptions: + * The network is locked + * + ****************************************************************************/ + +static void lo_loopback_work(FAR void *arg) +{ + FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)arg; + + /* Perform the loopback */ + + net_lock(); + (void)lo_loopback(&priv->lo_radio.r_dev); + net_unlock(); +} + +/**************************************************************************** + * Name: lo_poll_work + * + * Description: + * Perform periodic polling from the worker thread + * + * Parameters: + * arg - The argument passed when work_queue() as called. + * + * Returned Value: + * OK on success + * + * Assumptions: + * The network is locked + * + ****************************************************************************/ + +static void lo_poll_work(FAR void *arg) +{ + FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)arg; + + /* Perform the poll */ + + net_lock(); + (void)devif_timer(&priv->lo_radio.r_dev, lo_loopback); + + /* Setup the watchdog poll timer again */ + + (void)wd_start(priv->lo_polldog, LO_WDDELAY, lo_poll_expiry, 1, priv); + net_unlock(); +} + +/**************************************************************************** + * Name: lo_poll_expiry + * + * Description: + * Periodic timer handler. Called from the timer interrupt handler. + * + * Parameters: + * argc - The number of available arguments + * arg - The first argument + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static void lo_poll_expiry(int argc, wdparm_t arg, ...) +{ + FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)arg; + + if (!work_available(&priv->lo_work) || priv->lo_head != NULL) + { + nwarn("WARNING: lo_work NOT available\n"); + priv->lo_pending = true; + } + else + { + /* Schedule to perform the interrupt processing on the worker thread. */ + + priv->lo_pending = false; + work_queue(LPBKWORK, &priv->lo_work, lo_poll_work, priv, 0); + } +} + +/**************************************************************************** + * Name: lo_ifup + * + * Description: + * NuttX Callback: Bring up the Ethernet interface when an IP address is + * provided + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static int lo_ifup(FAR struct net_driver_s *dev) +{ + FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)dev->d_private; + + ninfo("Bringing up: IPv6 %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", + dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2], + dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5], + dev->d_ipv6addr[6], dev->d_ipv6addr[7]); + +#if CONFIG_PKTRADIO_ADDRLEN == 1 + ninfo(" Node: %02x\n", + dev->d_mac.sixlowpan.nv_addr[0]); + +#elif CONFIG_PKTRADIO_ADDRLEN == 2 + ninfo(" Node: %02x:%02x\n", + dev->d_mac.sixlowpan.nv_addr[0], dev->d_mac.sixlowpan.nv_addr[1]); + +#elif CONFIG_PKTRADIO_ADDRLEN == 8 + ninfo(" Node: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x PANID=%02x:%02x\n", + dev->d_mac.sixlowpan.nv_addr[0], dev->d_mac.sixlowpan.nv_addr[1], + dev->d_mac.sixlowpan.nv_addr[2], dev->d_mac.sixlowpan.nv_addr[3], + dev->d_mac.sixlowpan.nv_addr[4], dev->d_mac.sixlowpan.nv_addr[5], + dev->d_mac.sixlowpan.nv_addr[6], dev->d_mac.sixlowpan.nv_addr[7]); +#endif + + /* Set and activate a timer process */ + + (void)wd_start(priv->lo_polldog, LO_WDDELAY, lo_poll_expiry, + 1, (wdparm_t)priv); + + priv->lo_bifup = true; + return OK; +} + +/**************************************************************************** + * Name: lo_ifdown + * + * Description: + * NuttX Callback: Stop the interface. + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static int lo_ifdown(FAR struct net_driver_s *dev) +{ + FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)dev->d_private; + + ninfo("IP up: %u\n", priv->lo_bifup); + + /* Cancel the TX poll timer and TX timeout timers */ + + wd_cancel(priv->lo_polldog); + + /* Mark the device "down" */ + + priv->lo_bifup = false; + return OK; +} + +/**************************************************************************** + * Name: lo_txavail_work + * + * Description: + * Perform an out-of-cycle poll on the worker thread. + * + * Parameters: + * arg - Reference to the NuttX driver state structure (cast to void*) + * + * Returned Value: + * None + * + * Assumptions: + * Called on the higher priority worker thread. + * + ****************************************************************************/ + +static void lo_txavail_work(FAR void *arg) +{ + FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)arg; + + ninfo("TX available work. IP up: %u\n", priv->lo_bifup); + + /* Ignore the notification if the interface is not yet up */ + + net_lock(); + if (priv->lo_bifup) + { + /* If so, then poll the network for new XMIT data */ + + (void)devif_poll(&priv->lo_radio.r_dev, lo_loopback); + } + + net_unlock(); +} + +/**************************************************************************** + * Name: lo_txavail + * + * Description: + * Driver callback invoked when new TX data is available. This is a + * stimulus perform an out-of-cycle poll and, thereby, reduce the TX + * latency. + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Called in normal user mode + * + ****************************************************************************/ + +static int lo_txavail(FAR struct net_driver_s *dev) +{ + FAR struct lo_driver_s *priv = (FAR struct lo_driver_s *)dev->d_private; + + ninfo("Available: %u\n", work_available(&priv->lo_work)); + + /* Is our single work structure available? It may not be if there are + * pending actions and we will have to ignore the Tx availability + * action. + */ + + if (!work_available(&priv->lo_work) || priv->lo_head != NULL) + { + nwarn("WARNING: lo_work NOT available\n"); + priv->lo_pending = true; + } + else + { + /* Schedule to perform the interrupt processing on the worker thread. */ + + priv->lo_pending = false; + work_queue(LPBKWORK, &priv->lo_work, lo_txavail_work, priv, 0); + } + + return OK; +} + +/**************************************************************************** + * Name: lo_addmac + * + * Description: + * NuttX Callback: Add the specified MAC address to the hardware multicast + * address filtering + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * mac - The MAC address to be added + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef CONFIG_NET_IGMP +static int lo_addmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac) +{ +#if CONFIG_PKTRADIO_ADDRLEN == 1 + ninfo("MAC: %02x\n", mac[0]); + +#elif CONFIG_PKTRADIO_ADDRLEN == 2 + ninfo("MAC: %02x:%02x\n", mac[0], mac[1]); + +#elif CONFIG_PKTRADIO_ADDRLEN == 8 + ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], mac[6], mac[7]); +#endif + + /* There is no multicast support in the loopback driver */ + + return OK; +} +#endif + +/**************************************************************************** + * Name: lo_rmmac + * + * Description: + * NuttX Callback: Remove the specified MAC address from the hardware multicast + * address filtering + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * mac - The MAC address to be removed + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef CONFIG_NET_IGMP +static int lo_rmmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac) +{ +#if CONFIG_PKTRADIO_ADDRLEN == 1 + ninfo("MAC: %02x\n", mac[0]); + +#elif CONFIG_PKTRADIO_ADDRLEN == 2 + ninfo("MAC: %02x:%02x\n", mac[0], mac[1]); + +#elif CONFIG_PKTRADIO_ADDRLEN == 8 + ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], mac[6], mac[7]); +#endif + + /* There is no multicast support in the loopback driver */ + + return OK; +} +#endif + +/**************************************************************************** + * Name: macnet_ioctl + * + * Description: + * Handle network IOCTL commands directed to this device. + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * cmd - The IOCTL command + * arg - The argument for the IOCTL command + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef CONFIG_NETDEV_IOCTL +static int lo_ioctl(FAR struct net_driver_s *dev, int cmd, + unsigned long arg) +{ + FAR struct lo_driver_s *priv; + + DEBUGASSERT(dev != NULL && dev->d_private != NULL); + priv = (FAR struct lo_driver_s *)dev->d_private; + + UNUSED(priv); + + /* Reserved for future use */ + + return -ENOTTY; +} +#endif + +/**************************************************************************** + * Name: lo_get_mhrlen + * + * Description: + * Calculate the MAC header length given the frame meta-data. + * + * Input parameters: + * netdev - The networkd device that will mediate the MAC interface + * meta - Obfuscated metadata structure needed to create the radio + * MAC header + * + * Returned Value: + * A non-negative MAC headeer length is returned on success; a negated + * errno value is returned on any failure. + * + ****************************************************************************/ + +static int lo_get_mhrlen(FAR struct sixlowpan_driver_s *netdev, + FAR const void *meta) +{ + return MAC_HDRLEN; +} + +/**************************************************************************** + * Name: lo_req_data + * + * Description: + * Requests the transfer of a list of frames to the MAC. + * + * Input parameters: + * netdev - The networkd device that will mediate the MAC interface + * meta - Obfuscated metadata structure needed to create the radio + * MAC header + * framelist - Head of a list of frames to be transferred. + * + * Returned Value: + * Zero (OK) returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +static int lo_req_data(FAR struct sixlowpan_driver_s *netdev, + FAR const void *meta, FAR struct iob_s *framelist) +{ + FAR struct lo_driver_s *priv; + FAR struct iob_s *iob; + + DEBUGASSERT(netdev != NULL && netdev->r_dev.d_private != NULL); + priv = (FAR struct lo_driver_s *)netdev->r_dev.d_private; + + DEBUGASSERT(meta != NULL && framelist != NULL); + + /* Add the incoming list of framelist to queue of framelist to loopback */ + + for (iob = framelist; iob != NULL; iob = framelist) + { + /* Increment statistics */ + + NETDEV_RXPACKETS(&priv->lo_radio.r_dev); + + /* Remove the IOB from the queue */ + + framelist = iob->io_flink; + iob->io_flink = NULL; + + ninfo("Queuing frame IOB %p\n", iob); + + /* Just zero the MAC header for test purposes */ + + DEBUGASSERT(iob->io_offset == MAC_HDRLEN); + memset(iob->io_data, 0, MAC_HDRLEN); + + /* Add the IOB to the tail of the queue of framelist to be looped back */ + + if (priv->lo_tail == NULL) + { + priv->lo_head = iob; + } + else + { + priv->lo_tail->io_flink = iob; + } + + priv->lo_tail = iob; + } + + /* Schedule to serialize the poll on the worker thread. */ + + work_queue(LPBKWORK, &priv->lo_work, lo_loopback_work, priv, 0); + return OK; +} + +/**************************************************************************** + * Name: lo_properties + * + * Description: + * Different packet radios may have different properties. If there are + * multiple packet radios, then those properties have to be queried at + * run time. This information is provided to the 6LoWPAN network via the + * following structure. + * + * Input parameters: + * netdev - The network device to be queried + * properties - Location where radio properities will be returned. + * + * Returned Value: + * Zero (OK) returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +static int lo_properties(FAR struct sixlowpan_driver_s *netdev, + FAR struct sixlowpan_properties_s *properties) +{ + DEBUGASSERT(netdev != NULL && properties != NULL); + + properties->sp_addrlen = CONFIG_PKTRADIO_ADDRLEN; /* Length of an address */ + properties->sp_pktlen = CONFIG_NET_6LOWPAN_FRAMELEN; /* Fixed frame length */ + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pktradio_loopback + * + * Description: + * Initialize and register the Ieee802.15.4 MAC loopback network driver. + * + * Parameters: + * None + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +int pktradio_loopback(void) +{ + FAR struct lo_driver_s *priv; + FAR struct sixlowpan_driver_s *radio; + FAR struct net_driver_s *dev; + + ninfo("Initializing\n"); + + /* Get the interface structure associated with this interface number. */ + + priv = &g_loopback; + + /* Initialize the driver structure */ + + memset(priv, 0, sizeof(struct lo_driver_s)); + + radio = &priv->lo_radio; + dev = &radio->r_dev; + dev->d_ifup = lo_ifup; /* I/F up (new IP address) callback */ + dev->d_ifdown = lo_ifdown; /* I/F down callback */ + dev->d_txavail = lo_txavail; /* New TX data callback */ +#ifdef CONFIG_NET_IGMP + dev->d_addmac = lo_addmac; /* Add multicast MAC address */ + dev->d_rmmac = lo_rmmac; /* Remove multicast MAC address */ +#endif +#ifdef CONFIG_NETDEV_IOCTL + dev->d_ioctl = lo_ioctl; /* Handle network IOCTL commands */ +#endif + dev->d_buf = g_iobuffer; /* Attach the IO buffer */ + dev->d_private = (FAR void *)priv; /* Used to recover private state from dev */ + + /* Set the network mask and advertise our MAC-based IP address */ + + lo_netmask(dev); + lo_addr2ip(dev); + + /* Initialize the Network frame-related callbacks */ + + radio->r_get_mhrlen = lo_get_mhrlen; /* Get MAC header length */ + radio->r_req_data = lo_req_data; /* Enqueue frame for transmission */ + radio->r_properties = lo_properties; /* Returns radio properties */ + + /* Create a watchdog for timing polling for and timing of transmissions */ + + priv->lo_polldog = wd_create(); /* Create periodic poll timer */ + + /* Register the loopabck device with the OS so that socket IOCTLs can be + * performed. + */ + + (void)netdev_register(&priv->lo_radio.r_dev, NET_LL_PKTRADIO); + + /* Put the network in the UP state */ + + dev->d_flags = IFF_UP; + return lo_ifup(&priv->lo_radio.r_dev); +} + +#endif /* CONFIG_PKTRADIO_LOOPBACK */