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.

This commit is contained in:
Gregory Nutt
2014-08-17 09:51:26 -06:00
parent 39cd029b28
commit 5943cf2c2b
4 changed files with 353 additions and 10 deletions

View File

@@ -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
}