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:
parent
39cd029b28
commit
5943cf2c2b
@ -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
|
||||
|
23
nshlib/nsh.h
23
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 */
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user