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

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

View File

@ -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 */

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
}

View File

@ -1,7 +1,7 @@
/****************************************************************************
* examples/mdio/mdio_main.c
*
* Copyright (C) 2008, 2011-2012 Gregory Nutt. All rights reserved.
* Copyright (C) 2014 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without