summaryrefslogtreecommitdiff
path: root/apps/nshlib
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-08-17 09:51:26 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-08-17 09:51:26 -0600
commit45ae398fe34e373700a02e08c0f779e572862525 (patch)
treea48ee28cc5816be0ba00d12d5f26b4367dc37dde /apps/nshlib
parent83bd793f672c95bc632b1467f7e090ca7a6b4038 (diff)
downloadnuttx-45ae398fe34e373700a02e08c0f779e572862525.tar.gz
nuttx-45ae398fe34e373700a02e08c0f779e572862525.tar.bz2
nuttx-45ae398fe34e373700a02e08c0f779e572862525.zip
Extend the NSH network initialization logic. There is now an option that will create a network monitor thread that will monitor the state of the link. When the link goes down, the code will attempt to gracefully put the Ethernet driver in a down state; When the link comes back, the code will attempt to bring the network back up.
Diffstat (limited to 'apps/nshlib')
-rw-r--r--apps/nshlib/Kconfig49
-rw-r--r--apps/nshlib/nsh.h23
-rw-r--r--apps/nshlib/nsh_netinit.c289
3 files changed, 352 insertions, 9 deletions
diff --git a/apps/nshlib/Kconfig b/apps/nshlib/Kconfig
index 36839a736..cae3a7dca 100644
--- a/apps/nshlib/Kconfig
+++ b/apps/nshlib/Kconfig
@@ -810,6 +810,43 @@ config NSH_NETINIT_THREAD
if NSH_NETINIT_THREAD
+config NSH_NETINIT_MONITOR
+ bool "Monitor link state"
+ default n
+ depends on ARCH_PHY_INTERRUPT && NET_UDP && !DISABLE_SIGNALS && EXPERIMENTAL
+ ---help---
+ By default the net initialization thread will bring-up the network
+ then exit, freeing all of the resources that it required. This is a
+ good behavior for systems with limited memory.
+
+ If this option is selected, however, then the network initialization
+ thread will persist forever; it will monitor the network status. In
+ the event that the network goes down (for example, if a cable is
+ removed), then the the thread will monitor the link status and
+ attempt to bring the network back up. In this case the resources
+ required for network initialization are never released.
+
+config NSH_NETINIT_SIGNO
+ int "Notification signal number"
+ default 18
+ ---help---
+ The network monitor logic will receive signals when there is any
+ change in the link status. This setting may be used to customize
+ that signal number in order to avoid conflicts.
+
+if NSH_NETINIT_MONITOR
+
+config NSH_NETINIT_RETRYMSEC
+ int "Network bring-up retry period (msec)"
+ default 2000
+ ---help---
+ When the network is down, the initialization thread will periodically
+ try to bring the network up. This can be a time consuming operation
+ so is done only periodically with that period specified by this
+ selection in milliseconds.
+
+endif # NSH_NETINIT_MONITOR
+
config NSH_NETINIT_THREAD_STACKSIZE
int "Network initialization thread stack size"
default 1568
@@ -820,6 +857,18 @@ config NSH_NETINIT_THREAD_PRIORITY
endif # NSH_NETINIT_THREAD
+config NSH_NETINIT_DEBUG
+ bool "Network init debug"
+ default n
+ depends on DEBUG
+ ---help---
+ Normally debug output is controlled by DEBUG_NET. However, that
+ will generate a LOT of debug output, especially if DEBUG_VERBOSE is
+ also selected. This option is intended to force VERVOSE debug
+ output from the NSH network initialization logic even if DEBUG_NET
+ or DEBUG_VERBOSE are not selected. This allows for focused, unit-
+ level debug of the NSH network initialization logic.
+
config NSH_DHCPC
bool "Use DHCP to get IP address"
default n
diff --git a/apps/nshlib/nsh.h b/apps/nshlib/nsh.h
index bff96dc1c..625d10b35 100644
--- a/apps/nshlib/nsh.h
+++ b/apps/nshlib/nsh.h
@@ -128,16 +128,29 @@
# define CONFIG_NSH_MACADDR 0x00e0deadbeef
#endif
+#ifndef CONFIG_NET
+# undef CONFIG_NSH_ARCHMAC
+#endif
+
+#if !defined(CONFIG_NSH_NETINIT_THREAD) || !defined(CONFIG_ARCH_PHY_INTERRUPT) || \
+ !defined(CONFIG_NET_UDP) || defined(CONFIG_DISABLE_SIGNALS)
+# undef CONFIG_NSH_NETINIT_MONITOR
+#endif
+
+#ifndef CONFIG_NSH_NETINIT_RETRYMSEC
+# define CONFIG_NSH_NETINIT_RETRYMSEC 2000
+#endif
+
+#ifndef CONFIG_NSH_NETINIT_SIGNO
+# define CONFIG_NSH_NETINIT_SIGNO 18
+#endif
+
#ifndef CONFIG_NSH_NETINIT_THREAD_STACKSIZE
# define CONFIG_NSH_NETINIT_THREAD_STACKSIZE 1568
#endif
#ifndef CONFIG_NSH_NETINIT_THREAD_PRIORITY
-# define CONFIG_NSH_NETINIT_THREAD_PRIORITY 100
-#endif
-
-#ifndef CONFIG_NET
-# undef CONFIG_NSH_ARCHMAC
+# define CONFIG_NSH_NETINIT_THREAD_PRIORITY 100
#endif
/* Telnetd requires networking support */
diff --git a/apps/nshlib/nsh_netinit.c b/apps/nshlib/nsh_netinit.c
index 647d0eebd..e04591f7b 100644
--- a/apps/nshlib/nsh_netinit.c
+++ b/apps/nshlib/nsh_netinit.c
@@ -42,13 +42,30 @@
#include <nuttx/config.h>
+/* Is network initialization debug forced on? */
+
+#ifdef CONFIG_NSH_NETINIT_DEBUG
+# undef CONFIG_DEBUG_VERBOSE
+# define CONFIG_DEBUG_VERBOSE 1
+# undef CONFIG_DEBUG_NET
+# define CONFIG_DEBUG_NET 1
+#endif
+
+#include <sys/ioctl.h>
+
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
#include <pthread.h>
+#include <signal.h>
#include <debug.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <netinet/in.h>
+#include <nuttx/net/mii.h>
+
#include <apps/netutils/netlib.h>
#if defined(CONFIG_NSH_DHCPC) || defined(CONFIG_NSH_DNS)
# include <apps/netutils/dnsclient.h>
@@ -83,6 +100,14 @@
# define NET_DEVNAME "eth0"
#endif
+/* While the network is up, the network monitor really does nothing. It
+ * will wait for a very long time while waiting, it can be awakened by a
+ * signal indicating a change in network status.
+ */
+
+#define A_REALLY_LONG_TIME (60*60) /* One hour in seconds */
+#define A_SHORT_TIME (2) /* 2 seconds */
+
/****************************************************************************
* Private Types
****************************************************************************/
@@ -96,14 +121,14 @@
****************************************************************************/
/****************************************************************************
- * Name: nsh_netinit_thread
+ * Name: nsh_netinit_configure
*
* Description:
* Initialize the network per the selected NuttX configuration
*
****************************************************************************/
-pthread_addr_t nsh_netinit_thread(pthread_addr_t arg)
+static void nsh_netinit_configure(void)
{
struct in_addr addr;
#if defined(CONFIG_NSH_DHCPC)
@@ -207,7 +232,263 @@ pthread_addr_t nsh_netinit_thread(pthread_addr_t arg)
#endif
nvdbg("Exit\n");
- return OK;
+}
+
+/****************************************************************************
+ * Name: nsh_netinit_signal
+ *
+ * Description:
+ * This signal handler responds to changes in PHY status.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NSH_NETINIT_MONITOR
+static void nsh_netinit_signal(int signo, FAR siginfo_t *siginfo,
+ FAR void * context)
+{
+ volatile bool *event = (volatile bool *)siginfo->si_value.sival_ptr;
+
+ nlldbg("Entry: event=%p\n", event);
+ DEBUGASSERT(event);
+ *event = true;
+ nllvdbg("Exit\n");
+}
+#endif
+
+/****************************************************************************
+ * Name: nsh_netinit_monitor
+ *
+ * Description:
+ * Monitor link status, gracefully taking the link up and down as the
+ * link becomes available or as the link is lost.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NSH_NETINIT_MONITOR
+static int nsh_netinit_monitor(void)
+{
+ struct ifreq ifr;
+ struct sigaction act;
+ volatile bool event;
+ bool devup;
+ int ret;
+ int sd;
+
+ nvdbg("Entry\n");
+
+ /* Get a socket descriptor that we can use to communicate with the network
+ * interface driver.
+ */
+
+ sd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sd < 0)
+ {
+ ret = -errno;
+ DEBUGASSERT(ret < 0);
+
+ ndbg("ERROR: Failed to create a socket: %d\n", ret);
+ goto errout;
+ }
+
+ /* Attach a signal handler so that we do not lose PHY events */
+
+ act.sa_sigaction = nsh_netinit_signal;
+ act.sa_flags = SA_SIGINFO;
+
+ ret = sigaction(CONFIG_NSH_NETINIT_SIGNO, &act, NULL);
+ if (ret < 0)
+ {
+ ret = -errno;
+ DEBUGASSERT(ret < 0);
+
+ ndbg("ERROR: sigaction() failed: %d\n", ret);
+ goto errout_with_socket;
+ }
+
+ /* Configure to receive a signal on changes in link status */
+
+ strncpy(ifr.ifr_name, NET_DEVNAME, IFNAMSIZ);
+ ifr.ifr_mii_notify_pid = 0; /* PID=0 means this task */
+ ifr.ifr_mii_notify_signo = CONFIG_NSH_NETINIT_SIGNO;
+ ifr.ifr_mii_notify_arg = (FAR void *)&event;
+
+ ret = ioctl(sd, SIOCMIINOTIFY, (unsigned long)&ifr);
+ if (ret < 0)
+ {
+ ret = -errno;
+ DEBUGASSERT(ret < 0);
+
+ ndbg("ERROR: ioctl(SIOCMIINOTIFY) failed: %d\n", ret);
+ goto errout_with_sigaction;
+ }
+
+ /* Now loop, waiting for changes in link status */
+
+ for (;;)
+ {
+ /* This should catch any events that occur while we are not listening */
+
+ event = false;
+
+ /* Does the driver think that the link is up or down? */
+
+ strncpy(ifr.ifr_name, NET_DEVNAME, IFNAMSIZ);
+
+ ret = ioctl(sd, SIOCGIFFLAGS, (unsigned long)&ifr);
+ if (ret < 0)
+ {
+ ret = -errno;
+ DEBUGASSERT(ret < 0);
+
+ ndbg("ERROR: ioctl(SIOCGIFFLAGS) failed: %d\n", ret);
+ goto errout_with_notification;
+ }
+
+ devup = ((ifr.ifr_flags & IFF_UP) != 0);
+
+ /* Get the current PHY address in use. This probably does not change,
+ * but just in case...
+ *
+ * NOTE: We are assuming that the network device name is preserved in
+ * the ifr structure.
+ */
+
+ ret = ioctl(sd, SIOCGMIIPHY, (unsigned long)&ifr);
+ if (ret < 0)
+ {
+ ret = -errno;
+ DEBUGASSERT(ret < 0);
+
+ ndbg("ERROR: ioctl(SIOCGMIIPHY) failed: %d\n", ret);
+ goto errout_with_notification;
+ }
+
+ /* Read the PHY status register */
+
+ ifr.ifr_mii_reg_num = MII_MSR;
+
+ ret = ioctl(sd, SIOCGMIIREG, (unsigned long)&ifr);
+ if (ret < 0)
+ {
+ ret = -errno;
+ DEBUGASSERT(ret < 0);
+
+ ndbg("ERROR: ioctl(SIOCGMIIREG) failed: %d\n", ret);
+ goto errout_with_notification;
+ }
+
+ nvdbg("%s: devup=%d PHY address=%02x MSR=%04x\n",
+ devup, ifr.ifr_name, ifr.ifr_mii_phy_id, ifr.ifr_mii_val_out);
+
+ /* Check for link up or down */
+
+ if ((ifr.ifr_mii_val_out & MII_MSR_LINKSTATUS) != 0)
+ {
+ /* Link up... does the drive think that the link is up? */
+
+ if (!devup)
+ {
+ /* No... We just transitioned from link down to link up.
+ * Bring the link up.
+ */
+
+ nvdbg("Bringing the link up\n");
+
+ ifr.ifr_flags = IFF_UP;
+ ret = ioctl(sd, SIOCSIFFLAGS, (unsigned long)&ifr);
+ if (ret < 0)
+ {
+ ret = -errno;
+ DEBUGASSERT(ret < 0);
+
+ ndbg("ERROR: ioctl(SIOCSIFFLAGS) failed: %d\n", ret);
+ goto errout_with_notification;
+ }
+
+ /* And wait for a short delay. We will want to recheck the
+ * link status again soon.
+ */
+
+ sleep(A_SHORT_TIME);
+ }
+ else
+ {
+ /* The link is still up. Take a long, well-deserved rest */
+
+ sleep(A_REALLY_LONG_TIME);
+ }
+ }
+ else
+ {
+ /* Link down... Was the driver link state already down? */
+
+ if (devup)
+ {
+ /* No... we just transitioned from link up to link down. Take
+ * the link down.
+ */
+
+ nvdbg("Taking the link down\n");
+
+ ifr.ifr_flags = IFF_DOWN;
+ ret = ioctl(sd, SIOCSIFFLAGS, (unsigned long)&ifr);
+ if (ret < 0)
+ {
+ ret = -errno;
+ DEBUGASSERT(ret < 0);
+
+ ndbg("ERROR: ioctl(SIOCSIFFLAGS) failed: %d\n", ret);
+ goto errout_with_notification;
+ }
+ }
+
+ /* In either case, wait for the short, configurable delay */
+
+ usleep(1000*CONFIG_NSH_NETINIT_RETRYMSEC);
+ }
+ }
+
+ /* TODO: Stop the PHY notifications and remove the signal handler. This
+ * is important because the 'event' value is on the stack it is about to
+ * disappear!
+ */
+
+errout_with_notification:
+# warning Missing logic
+errout_with_sigaction:
+# warning Missing logic
+errout_with_socket:
+ close(sd);
+errout:
+ ndbg("Aborting\n");
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: nsh_netinit_thread
+ *
+ * Description:
+ * Initialize the network per the selected NuttX configuration
+ *
+ ****************************************************************************/
+
+static pthread_addr_t nsh_netinit_thread(pthread_addr_t arg)
+{
+ nvdbg("Entry\n");
+
+ /* Configure the network */
+
+ nsh_netinit_configure();
+
+#ifdef CONFIG_NSH_NETINIT_MONITOR
+ /* Monitor the network status */
+
+ nsh_netinit_monitor();
+#endif
+
+ nvdbg("Exit\n");
+ return NULL;
}
/****************************************************************************
@@ -262,7 +543,7 @@ int nsh_netinit(void)
#else
/* Perform network initialization sequentially */
- (void)nsh_netinit_thread(NULL);
+ nsh_netinit_configure();
#endif
}