diff options
Diffstat (limited to 'apps/nshlib')
-rw-r--r-- | apps/nshlib/Kconfig | 49 | ||||
-rw-r--r-- | apps/nshlib/nsh.h | 23 | ||||
-rw-r--r-- | apps/nshlib/nsh_netinit.c | 289 |
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 } |