diff --git a/fs/procfs/fs_skeleton.c b/fs/procfs/fs_skeleton.c index 11bc567fd8..052eb1f0f7 100644 --- a/fs/procfs/fs_skeleton.c +++ b/fs/procfs/fs_skeleton.c @@ -178,7 +178,7 @@ static int skel_open(FAR struct file *filep, FAR const char *relpath, return -EACCES; } - /* Allocate a container to hold the task and attribute selection */ + /* Allocate the open file structure */ priv = (FAR struct skel_file_s *)kmm_zalloc(sizeof(struct skel_file_s)); if (!priv) @@ -190,7 +190,9 @@ static int skel_open(FAR struct file *filep, FAR const char *relpath, /* TODO: Initialize the context specific data here */ - /* Save the index as the open-specific state in filep->f_priv */ + /* Save the open file structure as the open-specific state in + * filep->f_priv. + */ filep->f_priv = (FAR void *)priv; return OK; diff --git a/include/nuttx/net/net.h b/include/nuttx/net/net.h index e5995684cd..a5b2afc440 100644 --- a/include/nuttx/net/net.h +++ b/include/nuttx/net/net.h @@ -139,7 +139,7 @@ struct socketlist /* Callback from netdev_foreach() */ struct net_driver_s; /* Forward reference. Defined in nuttx/net/netdev.h */ -typedef int (*netdev_callback_t)(FAR struct net_driver_s *dev, void *arg); +typedef int (*netdev_callback_t)(FAR struct net_driver_s *dev, FAR void *arg); #ifdef CONFIG_NET_NOINTS /* Semaphore based locking for non-interrupt based logic. @@ -1130,7 +1130,7 @@ int netdev_unregister(FAR struct net_driver_s *dev); * ****************************************************************************/ -int netdev_foreach(netdev_callback_t callback, void *arg); +int netdev_foreach(netdev_callback_t callback, FAR void *arg); #undef EXTERN #ifdef __cplusplus diff --git a/net/netdev/Make.defs b/net/netdev/Make.defs index 955745945c..c5262a1aac 100644 --- a/net/netdev/Make.defs +++ b/net/netdev/Make.defs @@ -36,9 +36,9 @@ # Support for operations on network devices NETDEV_CSRCS += netdev_register.c netdev_ioctl.c netdev_txnotify.c -NETDEV_CSRCS += netdev_findbyname.c netdev_findbyaddr.c netdev_count.c -NETDEV_CSRCS += netdev_foreach.c netdev_unregister.c netdev_carrier.c -NETDEV_CSRCS += netdev_default.c netdev_verify.c +NETDEV_CSRCS += netdev_findbyname.c netdev_findbyaddr.c netdev_findbyindex.c +NETDEV_CSRCS += netdev_count.c netdev_foreach.c netdev_unregister.c +NETDEV_CSRCS += netdev_carrier.c netdev_default.c netdev_verify.c ifeq ($(CONFIG_NET_RXAVAIL),y) NETDEV_CSRCS += netdev_rxnotify.c diff --git a/net/netdev/netdev.h b/net/netdev/netdev.h index 9e055effd0..4289dc561a 100644 --- a/net/netdev/netdev.h +++ b/net/netdev/netdev.h @@ -188,6 +188,30 @@ FAR struct net_driver_s *netdev_findby_ipv6addr(const net_ipv6addr_t ripaddr); #endif #endif +/**************************************************************************** + * Function: netdev_findbyindex + * + * Description: + * Find a previously registered network device by its position in the + * list of registered devices. NOTE that this function is not a safe way + * to enumerate network devices: There could be changes to the list of + * registered device causing a given index to be meaningless (unless, of + * course, the caller keeps the network locked). + * + * Parameters: + * index - the index of the interface to file + * + * Returned Value: + * Pointer to driver on success; NULL on failure. This function can only + * fail if there are fewer registered interfaces than could be indexed. + * + * Assumptions: + * Called from normal user mode + * + ****************************************************************************/ + +FAR struct net_driver_s *netdev_findbyindex(int index); + /**************************************************************************** * Function: netdev_default * diff --git a/net/netdev/netdev_findbyaddr.c b/net/netdev/netdev_findbyaddr.c index 1f752a2c64..54cf1cee35 100644 --- a/net/netdev/netdev_findbyaddr.c +++ b/net/netdev/netdev_findbyaddr.c @@ -55,22 +55,6 @@ #include "route/route.h" #include "netdev/netdev.h" -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/**************************************************************************** - * Public Data - ****************************************************************************/ - /**************************************************************************** * Private Functions ****************************************************************************/ diff --git a/net/netdev/netdev_findbyindex.c b/net/netdev/netdev_findbyindex.c new file mode 100644 index 0000000000..74ccba04c8 --- /dev/null +++ b/net/netdev/netdev_findbyindex.c @@ -0,0 +1,101 @@ +/**************************************************************************** + * net/netdev/netdev_findbyindex.c + * + * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#if defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0 + +#include +#include + +#include + +#include "utils/utils.h" +#include "netdev/netdev.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: netdev_findbyindex + * + * Description: + * Find a previously registered network device by its position in the + * list of registered devices. NOTE that this function is not a safe way + * to enumerate network devices: There could be changes to the list of + * registered device causing a given index to be meaningless (unless, of + * course, the caller keeps the network locked). + * + * Parameters: + * index - the index of the interface to file + * + * Returned Value: + * Pointer to driver on success; NULL on failure. This function can only + * fail if there are fewer registered interfaces than could be indexed. + * + * Assumptions: + * Called from normal user mode + * + ****************************************************************************/ + +FAR struct net_driver_s *netdev_findbyindex(int index) +{ +#ifdef CONFIG_NETDEV_MULTINIC + FAR struct net_driver_s *dev; + net_lock_t save; + int i; + + save = net_lock(); + for (i = 0, dev = g_netdevices; dev; i++, dev = dev->flink) + { + if (i == index) + { + net_unlock(save); + return dev; + } + } + + net_unlock(save); + return NULL; +#else + return (index == 0) ? g_netdevices : NULL; +#endif +} + +#endif /* CONFIG_NET && CONFIG_NSOCKET_DESCRIPTORS */ diff --git a/net/netdev/netdev_findbyname.c b/net/netdev/netdev_findbyname.c index 93f90a9aab..04c20b27c8 100644 --- a/net/netdev/netdev_findbyname.c +++ b/net/netdev/netdev_findbyname.c @@ -48,26 +48,6 @@ #include "utils/utils.h" #include "netdev/netdev.h" -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/**************************************************************************** - * Public Data - ****************************************************************************/ - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - /**************************************************************************** * Public Functions ****************************************************************************/ diff --git a/net/procfs/Make.defs b/net/procfs/Make.defs index 49cae7c730..90cef533d2 100644 --- a/net/procfs/Make.defs +++ b/net/procfs/Make.defs @@ -39,7 +39,7 @@ ifneq ($(CONFIG_DISABLE_MOUNTPOINT),y) ifeq ($(CONFIG_FS_PROCFS),y) ifneq ($(CONFIG_FS_PROCFS_EXCLUDE_NET),y) -NET_CSRCS += net_procfs.c +NET_CSRCS += net_procfs.c netdev_statistics.c # General network statistics diff --git a/net/procfs/net_procfs.c b/net/procfs/net_procfs.c index 0c2bd6025f..f65cf040df 100644 --- a/net/procfs/net_procfs.c +++ b/net/procfs/net_procfs.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -55,7 +56,9 @@ #include #include #include +#include +#include "netdev/netdev.h" #include "procfs/procfs.h" #if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_FS_PROCFS) && \ @@ -64,6 +67,7 @@ /**************************************************************************** * Private Function Prototypes ****************************************************************************/ + /* File system methods */ static int netprocfs_open(FAR struct file *filep, FAR const char *relpath, @@ -120,6 +124,7 @@ static int netprocfs_open(FAR struct file *filep, FAR const char *relpath, int oflags, mode_t mode) { FAR struct netprocfs_file_s *priv; + FAR struct net_driver_s *dev; fvdbg("Open '%s'\n", relpath); @@ -136,15 +141,48 @@ static int netprocfs_open(FAR struct file *filep, FAR const char *relpath, return -EACCES; } - /* "net/stat" is the only acceptable value for the relpath */ + /* "net/stat" is an acceptable value for the relpath only if network layer + * statistics are enabled. + */ - if (strcmp(relpath, "net/stat") != 0) +#ifdef CONFIG_NET_STATISTICS + if (strcmp(relpath, "net/stat") == 0) { - fdbg("ERROR: relpath is '%s'\n", relpath); - return -ENOENT; + /* A NULL network device reference is a clue that we are processing + * the network statistics file. + */ + + dev = NULL; + } + else +#endif + { + FAR char *devname; + FAR char *copy; + + /* Otherwise, we need to search the list of registered network devices + * to determine if the name corresponds to a network device. + */ + + copy = strdup(relpath); + if (copy == NULL) + { + fdbg("ERROR: strdup failed\n"); + return -ENOMEM; + } + + devname = basename(copy); + dev = netdev_findbyname(devname); + kmm_free(copy); + + if (dev == NULL) + { + fdbg("ERROR: relpath is '%s'\n", relpath); + return -ENOENT; + } } - /* Allocate a container to hold the task and attribute selection */ + /* Allocate the open file structure */ priv = (FAR struct netprocfs_file_s *)kmm_zalloc(sizeof(struct netprocfs_file_s)); if (!priv) @@ -153,7 +191,13 @@ static int netprocfs_open(FAR struct file *filep, FAR const char *relpath, return -ENOMEM; } - /* Save the index as the open-specific state in filep->f_priv */ + /* Initialize the open-file structure */ + + priv->dev = dev; + + /* Save the open file structure as the open-specific state in + * filep->f_priv. + */ filep->f_priv = (FAR void *)priv; return OK; @@ -186,7 +230,6 @@ static int netprocfs_close(FAR struct file *filep) static ssize_t netprocfs_read(FAR struct file *filep, FAR char *buffer, size_t buflen) { -#ifdef CONFIG_NET_STATISTICS FAR struct netprocfs_file_s *priv; ssize_t nreturned; @@ -197,9 +240,24 @@ static ssize_t netprocfs_read(FAR struct file *filep, FAR char *buffer, priv = (FAR struct netprocfs_file_s *)filep->f_priv; DEBUGASSERT(priv); - /* Read the network layer statistics */ +#ifdef CONFIG_NET_STATISTICS + /* A NULL device structure reference is the key that we are showing the + * network statistics. + */ - nreturned = net_readstats(priv, buffer, buflen); + if (priv->dev == NULL) + { + /* Show the network layer statistics */ + + nreturned = netprocfs_read_netstats(priv, buffer, buflen); + } + else +#endif + { + /* Otherwise, we are showing device-specific statistics */ + + nreturned = netprocfs_read_devstats(priv, buffer, buflen); + } /* Update the file offset */ @@ -209,9 +267,6 @@ static ssize_t netprocfs_read(FAR struct file *filep, FAR char *buffer, } return nreturned; -#else - return 0; -#endif } /**************************************************************************** @@ -265,10 +320,24 @@ static int netprocfs_opendir(FAR const char *relpath, FAR struct fs_dirent_s *dir) { FAR struct netprocfs_level1_s *level1; + int ndevs; fvdbg("relpath: \"%s\"\n", relpath ? relpath : "NULL"); DEBUGASSERT(relpath && dir && !dir->u.procfs); + /* "net" is the only value of relpath that is a directory */ + + if (strcmp(relpath, "net") != 0) + { + /* REVISIT: We really need to check if the relpath refers to a network + * device. In that case, we need to return -ENOTDIR. Otherwise, we + * should return -ENOENT. + */ + + fdbg("ERROR: Bad relpath: %s\n", relpath); + return -ENOTDIR; + } + /* The path refers to the 1st level sbdirectory. Allocate the level1 * dirent structure. */ @@ -282,10 +351,18 @@ static int netprocfs_opendir(FAR const char *relpath, return -ENOMEM; } + /* Count the number of network devices */ + + ndevs = netdev_count(); + /* Initialze base structure components */ level1->base.level = 1; - level1->base.nentries = 1; +#ifdef CONFIG_NET_STATISTICS + level1->base.nentries = ndevs + 1; +#else + level1->base.nentries = ndevs; +#endif level1->base.index = 0; dir->u.procfs = (FAR void *) level1; @@ -325,46 +402,65 @@ static int netprocfs_closedir(FAR struct fs_dirent_s *dir) static int netprocfs_readdir(FAR struct fs_dirent_s *dir) { FAR struct netprocfs_level1_s *level1; + FAR struct net_driver_s *dev; int index; - int ret; DEBUGASSERT(dir && dir->u.procfs); level1 = dir->u.procfs; + DEBUGASSERT(level1->base.level == 1); /* Have we reached the end of the directory */ index = level1->base.index; + DEBUGASSERT(index <= level1->base.nentries); + if (index >= level1->base.nentries) { /* We signal the end of the directory by returning the special - * error -ENOENT + * error -ENOENT. */ fvdbg("Entry %d: End of directory\n", index); - ret = -ENOENT; + return -ENOENT; } - /* Currently only one element in the directory */ - - else +#ifdef CONFIG_NET_STATISTICS + else if (index == 0) { - DEBUGASSERT(level1->base.level == 1); - DEBUGASSERT(level1->base.index <= 1); - - /* Copy the one supported directory entry */ + /* Copy the network statistics directory entry */ dir->fd_dir.d_type = DTYPE_FILE; strncpy(dir->fd_dir.d_name, "stat", NAME_MAX + 1); + } + else +#endif + { + int devndx = index; - /* Set up the next directory entry offset. NOTE that we could use the - * standard f_pos instead of our own private index. +#ifdef CONFIG_NET_STATISTICS + /* Subtract one to account for index == 0 which is used for network + * status. */ - level1->base.index = index + 1; - ret = OK; + devndx--; +#endif + + /* Find the device corresponding to this device index */ + + dev = netdev_findbyindex(devndx); + + /* Copy the device statistics file entry */ + + dir->fd_dir.d_type = DTYPE_FILE; + strncpy(dir->fd_dir.d_name, dev->d_ifname, NAME_MAX + 1); } - return ret; + /* Set up the next directory entry offset. NOTE that we could use the + * standard f_pos instead of our own private index. + */ + + level1->base.index = index + 1; + return OK; } /**************************************************************************** @@ -394,26 +490,55 @@ static int netprocfs_rewinddir(FAR struct fs_dirent_s *dir) static int netprocfs_stat(FAR const char *relpath, FAR struct stat *buf) { - /* "net" and "net/stat" are the only acceptable values for the relpath */ + /* Check for the directory "net" */ if (strcmp(relpath, "net") == 0) { buf->st_mode = S_IFDIR | S_IROTH | S_IRGRP | S_IRUSR; } - else if (strcmp(relpath, "net/stat") == 0) + else +#ifdef CONFIG_NET_STATISTICS + /* Check for network statistics "net/stat" */ + + if (strcmp(relpath, "net/stat") == 0) { buf->st_mode = S_IFREG | S_IROTH | S_IRGRP | S_IRUSR; } else +#endif { - fdbg("ERROR: relpath is '%s'\n", relpath); - return -ENOENT; + FAR struct net_driver_s *dev; + FAR char *devname; + FAR char *copy; + + /* Otherwise, we need to search the list of registered network devices + * to determine if the name corresponds to a network device. + */ + + copy = strdup(relpath); + if (copy == NULL) + { + fdbg("ERROR: strdup failed\n"); + return -ENOMEM; + } + + devname = basename(copy); + dev = netdev_findbyname(devname); + kmm_free(copy); + + if (dev == NULL) + { + fdbg("ERROR: relpath is '%s'\n", relpath); + return -ENOENT; + } + + buf->st_mode = S_IFREG | S_IROTH | S_IRGRP | S_IRUSR; } /* File/directory size, access block size */ buf->st_size = 0; - buf->st_blksize = 512; + buf->st_blksize = 0; buf->st_blocks = 0; return OK; @@ -423,5 +548,106 @@ static int netprocfs_stat(FAR const char *relpath, FAR struct stat *buf) * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: netprocfs_read_linegen + * + * Description: + * Read and format procfs data using a line generation table. + * + * Input Parameters: + * priv - A reference to the network procfs file structure + * buffer - The user-provided buffer into which device status will be + * returned. + * buflen - The size in bytes of the user provided buffer. + * gentab - Table of line generation functions + * nelems - The number of elements in the table + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +ssize_t netprocfs_read_linegen(FAR struct netprocfs_file_s *priv, + FAR char *buffer, size_t buflen, + FAR const linegen_t *gentab, int nelems) +{ + size_t xfrsize; + ssize_t nreturned; + + fvdbg("buffer=%p buflen=%lu\n", buffer, (unsigned long)buflen); + + /* Is there line data already buffered? */ + + nreturned = 0; + if (priv->linesize > 0) + { + /* Yes, how much can we transfer now? */ + + xfrsize = priv->linesize; + if (xfrsize > buflen) + { + xfrsize = buflen; + } + + /* Transfer the data to the user buffer */ + + memcpy(buffer, &priv->line[priv->offset], xfrsize); + + /* Update pointers, sizes, and offsets */ + + buffer += xfrsize; + buflen -= xfrsize; + + priv->linesize -= xfrsize; + priv->offset += xfrsize; + nreturned = xfrsize; + } + + /* Loop until the user buffer is full or until all of the network + * statistics have been transferred. At this point we know that + * either: + * + * 1. The user buffer is full, and/or + * 2. All of the current line data has been transferred. + */ + + while (buflen > 0 && priv->lineno < nelems) + { + int len; + + /* Read the next line into the working buffer */ + + len = gentab[priv->lineno](priv); + + /* Update line-related information */ + + priv->lineno++; + priv->linesize = len; + priv->offset = 0; + + /* Transfer data to the user buffer */ + + xfrsize = priv->linesize; + if (xfrsize > buflen) + { + xfrsize = buflen; + } + + memcpy(buffer, &priv->line[priv->offset], xfrsize); + + /* Update pointers, sizes, and offsets */ + + buffer += xfrsize; + buflen -= xfrsize; + + priv->linesize -= xfrsize; + priv->offset += xfrsize; + nreturned += xfrsize; + } + + return nreturned; +} + #endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_PROCFS && * !CONFIG_FS_PROCFS_EXCLUDE_NET */ diff --git a/net/procfs/net_statistics.c b/net/procfs/net_statistics.c index e6e977474c..df35f4a165 100644 --- a/net/procfs/net_statistics.c +++ b/net/procfs/net_statistics.c @@ -1,5 +1,5 @@ /**************************************************************************** - * net/procfs/net_statisticsc + * net/procfs/net_statistics.c * * Copyright (C) 2015 Gregory Nutt. All rights reserved. * Author: Gregory Nutt @@ -436,7 +436,7 @@ static int netprocfs_retransmissions(FAR struct netprocfs_file_s *netfile) ****************************************************************************/ /**************************************************************************** - * Name: net_readstats + * Name: netprocfs_read_netstats * * Description: * Read and format network layer statistics. @@ -453,84 +453,10 @@ static int netprocfs_retransmissions(FAR struct netprocfs_file_s *netfile) * ****************************************************************************/ -ssize_t net_readstats(FAR struct netprocfs_file_s *priv, FAR char *buffer, - size_t buflen) +ssize_t netprocfs_read_netstats(FAR struct netprocfs_file_s *priv, + FAR char *buffer, size_t buflen) { - size_t xfrsize; - ssize_t nreturned; - - fvdbg("buffer=%p buflen=%lu\n", buffer, (unsigned long)buflen); - - /* Is there line data already buffered? */ - - nreturned = 0; - if (priv->linesize > 0) - { - /* Yes, how much can we transfer now? */ - - xfrsize = priv->linesize; - if (xfrsize > buflen) - { - xfrsize = buflen; - } - - /* Transfer the data to the user buffer */ - - memcpy(buffer, &priv->line[priv->offset], xfrsize); - - /* Update pointers, sizes, and offsets */ - - buffer += xfrsize; - buflen -= xfrsize; - - priv->linesize -= xfrsize; - priv->offset += xfrsize; - nreturned = xfrsize; - } - - /* Loop until the user buffer is full or until all of the network - * statistics have been transferred. At this point we know that - * either: - * - * 1. The user buffer is full, and/or - * 2. All of the current line data has been transferred. - */ - - while (buflen > 0 && priv->lineno < NSTAT_LINES) - { - int len; - - /* Read the next line into the working buffer */ - - len = g_linegen[priv->lineno](priv); - - /* Update line-related information */ - - priv->lineno++; - priv->linesize = len; - priv->offset = 0; - - /* Transfer data to the user buffer */ - - xfrsize = priv->linesize; - if (xfrsize > buflen) - { - xfrsize = buflen; - } - - memcpy(buffer, &priv->line[priv->offset], xfrsize); - - /* Update pointers, sizes, and offsets */ - - buffer += xfrsize; - buflen -= xfrsize; - - priv->linesize -= xfrsize; - priv->offset += xfrsize; - nreturned += xfrsize; - } - - return nreturned; + return netprocfs_read_linegen(priv, buffer, buflen, g_linegen, NSTAT_LINES); } #endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_PROCFS && diff --git a/net/procfs/netdev_statistics.c b/net/procfs/netdev_statistics.c new file mode 100644 index 0000000000..b81741a409 --- /dev/null +++ b/net/procfs/netdev_statistics.c @@ -0,0 +1,481 @@ +/**************************************************************************** + * net/procfs/netdev_statistics.c + * + * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include + +#include + +#include "netdev/netdev.h" +#include "procfs/procfs.h" + +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_FS_PROCFS) && \ + !defined(CONFIG_FS_PROCFS_EXCLUDE_NET) + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int netprocfs_linklayer(FAR struct netprocfs_file_s *netfile); +static int netprocfs_ipaddresses(FAR struct netprocfs_file_s *netfile); + +#ifdef CONFIG_NETDEV_STATISTICS +static int netprocfs_rxstatistics_header(FAR struct netprocfs_file_s *netfile); +static int netprocfs_rxstatistics(FAR struct netprocfs_file_s *netfile); +static int netprocfs_rxpackets_header(FAR struct netprocfs_file_s *netfile); +static int netprocfs_rxpackets(FAR struct netprocfs_file_s *netfile); +static int netprocfs_txstatistics_header(FAR struct netprocfs_file_s *netfile); +static int netprocfs_txstatistics(FAR struct netprocfs_file_s *netfile); +static int netprocfs_errors(FAR struct netprocfs_file_s *netfile); +#endif /* CONFIG_NETDEV_STATISTICS */ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Line generating functions */ + +static const linegen_t g_linegen[] = +{ + netprocfs_linklayer, + netprocfs_ipaddresses +#ifdef CONFIG_NETDEV_STATISTICS + , netprocfs_rxstatistics_header, + netprocfs_rxstatistics, + netprocfs_rxpackets_header, + netprocfs_rxpackets, + netprocfs_txstatistics_header, + netprocfs_txstatistics, + netprocfs_errors +#endif /* CONFIG_NETDEV_STATISTICS */ +}; + +#define NSTAT_LINES (sizeof(g_linegen) / sizeof(linegen_t)) + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: netprocfs_linklayer + ****************************************************************************/ + +static int netprocfs_linklayer(FAR struct netprocfs_file_s *netfile) +{ + FAR struct net_driver_s *dev; + FAR const char *status; + int len = 0; + + DEBUGASSERT(netfile != NULL && netfile->dev != NULL); + dev = netfile->dev; + + /* Get the interface status: RUNNING, UP, or DOWN */ + + if ((dev->d_flags & IFF_RUNNING) != 0) + { + status = "RUNNING"; + } + else if ((dev->d_flags & IFF_UP) != 0) + { + status = "UP"; + } + else + { + status = "DOWN"; + } + +#if defined(CONFIG_NET_MULTILINK) + /* If there are multiple link types being supported, then selected the + * output appropriate for the link type associated with this device. + */ + + switch (dev->d_lltype) + { +#ifdef CONFIG_NET_ETHERNET + case NET_LL_ETHERNET: + len += snprintf(&netfile->line[len], NET_LINELEN - len, + "%s\tLink encap:Ethernet HWaddr %s", + dev->d_ifname, ether_ntoa(&dev->d_mac)); + break; +#endif + +#ifdef CONFIG_NET_LOOPBACK + case NET_LL_LOOPBACK: + len += snprintf(&netfile->line[len], NET_LINELEN - len, + "%s\tLink encap:Local Loopback", + dev->d_ifname); + break; +#endif + +#ifdef CONFIG_NET_SLIP + case NET_LL_SLIP: + len += snprintf(&netfile->line[len], NET_LINELEN - len, + "%s\tLink encap:SLIP", dev->d_ifname); + break; +#endif + +#ifdef CONFIG_NET_PPP + case NET_LL_PPP: + len += snprintf(&netfile->line[len], NET_LINELEN - len, + "%s\tLink encap:P-t-P", dev->d_ifname); + break; +#endif + +#ifdef CONFIG_NET_TUN + case NET_LL_TUN: + len += snprintf(&netfile->line[len], NET_LINELEN - len, + "%s\tLink encap:TUN", dev->d_ifname); + break; +#endif + + default: + len += snprintf(&netfile->line[len], NET_LINELEN - len, + "%s\tLink encap:UNSPEC", dev->d_ifname); + } + + len += snprintf(&netfile->line[len], NET_LINELEN - len, + " at %s\n", status); + +#elif defined(CONFIG_NET_ETHERNET) + len += snprintf(&netfile->line[len], NET_LINELEN - len, + "%s\tLink encap:Ethernet HWaddr %s at %s\n", + dev->d_ifname, ether_ntoa(&dev->d_mac), status); + +#elif defined(CONFIG_NET_LOOPBACK) + len += snprintf(&netfile->line[len], NET_LINELEN - len, + "%s\tLink encap:Local Loopback at %s\n", + dev->d_ifname, status); + +#elif defined(CONFIG_NET_SLIP) + len += snprintf(&netfile->line[len], NET_LINELEN - len, + "%s\tLink encap:SLIP at %s\n", + dev->d_ifname, status); + +#elif defined(CONFIG_NET_PPP) + len += snprintf(&netfile->line[len], NET_LINELEN - len, + "%s\tLink encap:P-t-P at %s\n", + dev->d_ifname, status); + +#elif defined(CONFIG_NET_TUN) + len += snprintf(&netfile->line[len], NET_LINELEN - len, + "%s\tLink encap:TUN at %s\n", + dev->d_ifname, status); +#endif + + return len; +} + +/**************************************************************************** + * Name: netprocfs_ipaddresses + ****************************************************************************/ + +static int netprocfs_ipaddresses(FAR struct netprocfs_file_s *netfile) +{ + FAR struct net_driver_s *dev; +#ifdef CONFIG_NET_IPv4 + struct in_addr addr; +#endif +#ifdef CONFIG_NET_IPv6 + char addrstr[INET6_ADDRSTRLEN]; + uint8_t preflen; +#endif + int len = 0; + + DEBUGASSERT(netfile != NULL && netfile->dev != NULL); + dev = netfile->dev; + +#ifdef CONFIG_NET_IPv4 + /* Show the IPv4 address */ + + addr.s_addr = dev->d_ipaddr; + len += snprintf(&netfile->line[len], NET_LINELEN - len, + "\tinet addr:%s ", inet_ntoa(addr)); + + /* Show the IPv4 default router address */ + + addr.s_addr = dev->d_draddr; + len += snprintf(&netfile->line[len], NET_LINELEN - len, + "DRaddr:%s ", inet_ntoa(addr)); + + /* Show the IPv4 network mask */ + + addr.s_addr = dev->d_netmask; + len += snprintf(&netfile->line[len], NET_LINELEN - len, + "Mask:%s\n", inet_ntoa(addr)); + +#if defined(CONFIG_NSH_DHCPC) || defined(CONFIG_NSH_DNS) + netlib_get_ipv4dnsaddr(&addr); + len += snprintf(&netfile->line[len], NET_LINELEN - len, + "\tDNSaddr:%s\n", inet_ntoa(addr)); +#endif +#endif + +#ifdef CONFIG_NET_IPv6 + /* Convert the 128 network mask to a human friendly prefix length */ + + preflen = netlib_ipv6netmask2prefix(dev->d_ipv6netmask); + + /* Show the assigned IPv6 address */ + + if (inet_ntop(AF_INET6, dev->d_ipv6addr, addrstr, INET6_ADDRSTRLEN)) + { + len += snprintf(&netfile->line[len], NET_LINELEN - len, + "\tinet6 addr:%s/%d\n", addrstr, preflen); + } + + /* REVISIT: Show the IPv6 default router address */ + + if (inet_ntop(AF_INET6, dev->d_ipv6draddr, addrstr, INET6_ADDRSTRLEN)) + { + len += snprintf(&netfile->line[len], NET_LINELEN - len, + "\tinet6 DRaddr:%s/%d\n", addrstr, preflen); + } + +#if defined(CONFIG_NSH_DHCPCv6) || defined(CONFIG_NSH_DNS) +# warning Missing logic +#endif +#endif + + len += snprintf(&netfile->line[len], NET_LINELEN - len, "\n"); + return len; +} + +/**************************************************************************** + * Name: netprocfs_rxstatistics_header + ****************************************************************************/ + +#ifdef CONFIG_NETDEV_STATISTICS +static int netprocfs_rxstatistics_header(FAR struct netprocfs_file_s *netfile) +{ + DEBUGASSERT(netfile != NULL); + return snprintf(netfile->line, NET_LINELEN , "\tRX: %-8s %-8s %-8s\n", + "Received", "Fragment", "Errors"); +} +#endif /* CONFIG_NETDEV_STATISTICS */ + +/**************************************************************************** + * Name: netprocfs_rxstatistics + ****************************************************************************/ + +#ifdef CONFIG_NETDEV_STATISTICS +static int netprocfs_rxstatistics(FAR struct netprocfs_file_s *netfile) +{ + FAR struct netdev_statistics_s *stats; + FAR struct net_driver_s *dev; + + DEBUGASSERT(netfile != NULL && netfile->dev != NULL); + dev = netfile->dev; + stats = &dev->d_statistics; + + return snprintf(netfile->line, NET_LINELEN, "\t %08lx %08lx %08lx\n", + (unsigned long)stats->rx_packets, + (unsigned long)stats->rx_fragments, + (unsigned long)stats->rx_errors); +} +#endif /* CONFIG_NETDEV_STATISTICS */ + +/**************************************************************************** + * Name: netprocfs_rxpackets_header + ****************************************************************************/ + +#ifdef CONFIG_NETDEV_STATISTICS +static int netprocfs_rxpackets_header(FAR struct netprocfs_file_s *netfile) +{ + FAR char *fmt; + + DEBUGASSERT(netfile != NULL); + + fmt = "\t " +#ifdef CONFIG_NET_IPv4 + "%-8s " +#endif +#ifdef CONFIG_NET_IPv6 + "%-8s " +#endif +#ifdef CONFIG_NET_ARP + "%-8s " +#endif + "%-8s\n"; + + return snprintf(netfile->line, NET_LINELEN, fmt +#ifdef CONFIG_NET_IPv4 + , "IPv4" +#endif +#ifdef CONFIG_NET_IPv6 + , "IPv6" +#endif +#ifdef CONFIG_NET_ARP + , "ARP" +#endif + , "Dropped"); +} +#endif /* CONFIG_NETDEV_STATISTICS */ + +/**************************************************************************** + * Name: netprocfs_rxpackets + ****************************************************************************/ + +#ifdef CONFIG_NETDEV_STATISTICS +static int netprocfs_rxpackets(FAR struct netprocfs_file_s *netfile) +{ + FAR struct netdev_statistics_s *stats; + FAR struct net_driver_s *dev; + FAR char *fmt; + + DEBUGASSERT(netfile != NULL && netfile->dev != NULL); + dev = netfile->dev; + stats = &dev->d_statistics; + + fmt = "\t " +#ifdef CONFIG_NET_IPv4 + "%08lx " +#endif +#ifdef CONFIG_NET_IPv6 + "%08lx " +#endif +#ifdef CONFIG_NET_ARP + "%08lx " +#endif + "%08lx\n"; + + return snprintf(netfile->line, NET_LINELEN, fmt +#ifdef CONFIG_NET_IPv4 + , (unsigned long)stats->rx_ipv4 +#endif +#ifdef CONFIG_NET_IPv6 + , (unsigned long)stats->rx_ipv6 +#endif +#ifdef CONFIG_NET_ARP + , (unsigned long)stats->rx_arp +#endif + , (unsigned long)stats->rx_dropped); +} +#endif /* CONFIG_NETDEV_STATISTICS */ + +/**************************************************************************** + * Name: netprocfs_txstatistics_header + ****************************************************************************/ + +#ifdef CONFIG_NETDEV_STATISTICS +static int netprocfs_txstatistics_header(FAR struct netprocfs_file_s *netfile) +{ + DEBUGASSERT(netfile != NULL); + + return snprintf(netfile->line, NET_LINELEN, "\tTX: %-8s %-8s %-8s %-8s\n", + "Queued", "Sent", "Erorts", "Timeouts"); +} +#endif /* CONFIG_NETDEV_STATISTICS */ + +/**************************************************************************** + * Name: netprocfs_txstatistics + ****************************************************************************/ + +#ifdef CONFIG_NETDEV_STATISTICS +static int netprocfs_txstatistics(FAR struct netprocfs_file_s *netfile) +{ + FAR struct netdev_statistics_s *stats; + FAR struct net_driver_s *dev; + + DEBUGASSERT(netfile != NULL && netfile->dev != NULL); + dev = netfile->dev; + stats = &dev->d_statistics; + + return snprintf(netfile->line, NET_LINELEN, "\t %08lx %08lx %08lx %08lx\n", + (unsigned long)stats->tx_packets, + (unsigned long)stats->tx_done, + (unsigned long)stats->tx_errors, + (unsigned long)stats->tx_timeouts); +} +#endif /* CONFIG_NETDEV_STATISTICS */ + +/**************************************************************************** + * Name: netprocfs_errors + ****************************************************************************/ + +#ifdef CONFIG_NETDEV_STATISTICS +static int netprocfs_errors(FAR struct netprocfs_file_s *netfile) +{ + FAR struct netdev_statistics_s *stats; + FAR struct net_driver_s *dev; + + DEBUGASSERT(netfile != NULL && netfile->dev != NULL); + dev = netfile->dev; + stats = &dev->d_statistics; + + return snprintf(netfile->line, NET_LINELEN , "\tTotal Errors: %08x\n\n", + (unsigned long)stats->errors); +} +#endif /* CONFIG_NETDEV_STATISTICS */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: netprocfs_read_devstats + * + * Description: + * Read and format network device statistics. + * + * Input Parameters: + * priv - A reference to the network procfs file structure + * buffer - The user-provided buffer into which device status will be + * returned. + * bulen - The size in bytes of the user provided buffer. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +ssize_t netprocfs_read_devstats(FAR struct netprocfs_file_s *priv, + FAR char *buffer, size_t buflen) +{ + return netprocfs_read_linegen(priv, buffer, buflen, g_linegen, NSTAT_LINES); +} + +#endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_PROCFS && + * !CONFIG_FS_PROCFS_EXCLUDE_NET */ diff --git a/net/procfs/procfs.h b/net/procfs/procfs.h index c6f059fb8d..feba657a0d 100644 --- a/net/procfs/procfs.h +++ b/net/procfs/procfs.h @@ -66,9 +66,11 @@ /* This structure describes one open "file" */ +struct net_driver_s; /* Forward reference */ struct netprocfs_file_s { struct procfs_file_s base; /* Base open file structure */ + FAR struct net_driver_s *dev; /* Current network device */ uint8_t lineno; /* Line number */ uint8_t linesize; /* Number of valid characters in line[] */ uint8_t offset; /* Offset to first valid character in line[] */ @@ -80,6 +82,7 @@ struct netprocfs_file_s struct netprocfs_level1_s { struct procfs_dir_priv_s base; /* Base directory private data */ + char name[NAME_MAX + 1]; /* Name of last node visited */ }; /* Line generating function type */ @@ -103,7 +106,31 @@ extern "C" ****************************************************************************/ /**************************************************************************** - * Name: net_readstats + * Name: netprocfs_read_linegen + * + * Description: + * Read and format procfs data using a line generation table. + * + * Input Parameters: + * priv - A reference to the network procfs file structure + * buffer - The user-provided buffer into which device status will be + * returned. + * buflen - The size in bytes of the user provided buffer. + * gentab - Table of line generation functions + * nelems - The number of elements in the table + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +ssize_t netprocfs_read_linegen(FAR struct netprocfs_file_s *priv, + FAR char *buffer, size_t buflen, + FAR const linegen_t *gentab, int nelems); + +/**************************************************************************** + * Name: netprocfs_read_netstats * * Description: * Read and format network layer statistics. @@ -121,10 +148,31 @@ extern "C" ****************************************************************************/ #ifdef CONFIG_NET_STATISTICS -ssize_t net_readstats(FAR struct netprocfs_file_s *priv, FAR char *buffer, - size_t buflen); +ssize_t netprocfs_read_netstats(FAR struct netprocfs_file_s *priv, + FAR char *buffer, size_t buflen); #endif +/**************************************************************************** + * Name: netprocfs_read_devstats + * + * Description: + * Read and format network device statistics. + * + * Input Parameters: + * priv - A reference to the network procfs file structure + * buffer - The user-provided buffer into which device status will be + * returned. + * bulen - The size in bytes of the user provided buffer. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +ssize_t netprocfs_read_devstats(FAR struct netprocfs_file_s *priv, + FAR char *buffer, size_t buflen); + #undef EXTERN #ifdef __cplusplus }