nuttx/net/netdev/netdev_register.c
2020-06-15 07:13:21 -06:00

450 lines
13 KiB
C

/****************************************************************************
* net/netdev/netdev_register.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/socket.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <debug.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <nuttx/net/netconfig.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/ethernet.h>
#include <nuttx/net/bluetooth.h>
#include "utils/utils.h"
#include "igmp/igmp.h"
#include "mld/mld.h"
#include "netdev/netdev.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define NETDEV_ETH_FORMAT "eth%d"
#define NETDEV_LO_FORMAT "lo"
#define NETDEV_SLIP_FORMAT "sl%d"
#define NETDEV_TUN_FORMAT "tun%d"
#define NETDEV_BNEP_FORMAT "bnep%d"
#define NETDEV_PAN_FORMAT "pan%d"
#define NETDEV_WLAN_FORMAT "wlan%d"
#define NETDEV_WPAN_FORMAT "wpan%d"
#define NETDEV_WWAN_FORMAT "wwan%d"
#if defined(CONFIG_DRIVERS_IEEE80211) /* Usually also has CONFIG_NET_ETHERNET */
# define NETDEV_DEFAULT_FORMAT NETDEV_WLAN_FORMAT
#elif defined(CONFIG_NET_ETHERNET)
# define NETDEV_DEFAULT_FORMAT NETDEV_ETH_FORMAT
#elif defined(CONFIG_NET_6LOWPAN)
# define NETDEV_DEFAULT_FORMAT NETDEV_WPAN_FORMAT
#elif defined(CONFIG_NET_SLIP)
# define NETDEV_DEFAULT_FORMAT NETDEV_SLIP_FORMAT
#elif defined(CONFIG_NET_TUN)
# define NETDEV_DEFAULT_FORMAT NETDEV_TUN_FORMAT
#else /* if defined(CONFIG_NET_LOOPBACK) */
# define NETDEV_DEFAULT_FORMAT NETDEV_LO_FORMAT
#endif
/****************************************************************************
* Public Data
****************************************************************************/
/* List of registered Ethernet device drivers */
struct net_driver_s *g_netdevices = NULL;
#ifdef CONFIG_NETDEV_IFINDEX
/* The set of network devices that have been registered. This is used to
* assign a unique device index to the newly registered device.
*
* REVISIT: The width of g_nassigned limits the number of registered
* devices to 32 (MAX_IFINDEX).
*/
uint32_t g_devset;
/* The set of network devices that have been freed. The purpose of this
* set is to postpone reuse of a interface index for as long as possible,
* i.e., don't reuse an interface index until all of the possible indices
* have been used.
*/
uint32_t g_devfreed;
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: find_devnum
*
* Description:
* Given a device name format string, find the next device number for the
* class of device represented by that format string.
*
* Input Parameters:
* devfmt - The device format string
*
* Returned Value:
* The next device number for that device class
*
****************************************************************************/
static int find_devnum(FAR const char *devfmt)
{
FAR struct net_driver_s *curr;
size_t fmt_size;
int result = 0;
fmt_size = strlen(devfmt);
/* Assumed that devfmt is xxx%d */
DEBUGASSERT(fmt_size > 2);
fmt_size -= 2;
/* Search the list of currently registered network devices */
for (curr = g_netdevices; curr; curr = curr->flink )
{
/* Does this device name match the format we were given? */
if (strncmp(curr->d_ifname, devfmt, fmt_size) == 0)
{
/* Yes.. increment the candidate device number */
result++;
}
}
/* Return this next device number for this format */
return result;
}
/****************************************************************************
* Name: get_ifindex
*
* Description:
* Assign a unique interface index to the device.
*
* Input Parameters:
* None
*
* Returned Value:
* The interface index assigned to the device. -ENOSPC is returned if
* more the MAX_IFINDEX names have been assigned.
*
****************************************************************************/
#ifdef CONFIG_NETDEV_IFINDEX
static int get_ifindex(void)
{
uint32_t devset;
int ndx;
/* Try to postpone re-using interface indices as long as possible */
devset = g_devset | g_devfreed;
if (devset == 0xffffffff)
{
/* Time start re-using interface indices */
devset = g_devset;
g_devfreed = 0;
}
/* Search for an unused index */
for (ndx = 0; ndx < MAX_IFINDEX; ndx++)
{
uint32_t bit = 1L << ndx;
if ((devset & bit) == 0)
{
/* Indicate that this index is in use */
g_devset |= bit;
/* NOTE that the index + 1 is returned. Zero is reserved to
* mean no-index in the POSIX standards.
*/
return ndx + 1;
}
}
return -ENOSPC;
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: netdev_register
*
* Description:
* Register a network device driver and assign a name to it so that it can
* be found in subsequent network ioctl operations on the device.
*
* A custom, device-specific interface name format string may be selected
* by putting that format string into the device structure's d_ifname[]
* array before calling netdev_register(). Otherwise, the d_ifname[] must
* be zeroed on entry.
*
* Input Parameters:
* dev - The device driver structure to be registered.
* lltype - Link level protocol used by the driver (Ethernet, SLIP, TUN,
* ...)
*
* Returned Value:
* 0:Success; negated errno on failure
*
* Assumptions:
* Called during system bring-up, but also when a removable network
* device is installed.
*
****************************************************************************/
int netdev_register(FAR struct net_driver_s *dev, enum net_lltype_e lltype)
{
FAR char devfmt_str[IFNAMSIZ];
FAR const char *devfmt;
uint16_t pktsize = 0;
uint8_t llhdrlen = 0;
int devnum;
#ifdef CONFIG_NETDEV_IFINDEX
int ifindex;
#endif
if (dev != NULL)
{
/* Set the protocol used by the device and the size of the link
* header used by this protocol.
*/
switch (lltype)
{
#ifdef CONFIG_NET_LOOPBACK
case NET_LL_LOOPBACK: /* Local loopback */
llhdrlen = 0;
pktsize = NET_LO_PKTSIZE;
devfmt = NETDEV_LO_FORMAT;
break;
#endif
#ifdef CONFIG_NET_ETHERNET
case NET_LL_ETHERNET: /* Ethernet */
llhdrlen = ETH_HDRLEN;
pktsize = CONFIG_NET_ETH_PKTSIZE;
devfmt = NETDEV_ETH_FORMAT;
break;
#endif
#ifdef CONFIG_DRIVERS_IEEE80211
case NET_LL_IEEE80211: /* IEEE 802.11 */
llhdrlen = ETH_HDRLEN;
pktsize = CONFIG_NET_ETH_PKTSIZE;
devfmt = NETDEV_WLAN_FORMAT;
break;
#endif
#ifdef CONFIG_NET_BLUETOOTH
case NET_LL_BLUETOOTH: /* Bluetooth */
llhdrlen = BLUETOOTH_MAX_HDRLEN; /* Determined at runtime */
#ifdef CONFIG_NET_6LOWPAN
pktsize = CONFIG_NET_6LOWPAN_PKTSIZE;
#endif
devfmt = NETDEV_BNEP_FORMAT;
break;
#endif
#if defined(CONFIG_NET_6LOWPAN) || defined(CONFIG_NET_IEEE802154)
case NET_LL_IEEE802154: /* IEEE 802.15.4 MAC */
case NET_LL_PKTRADIO: /* Non-IEEE 802.15.4 packet radio */
llhdrlen = 0; /* Determined at runtime */
#ifdef CONFIG_NET_6LOWPAN
pktsize = CONFIG_NET_6LOWPAN_PKTSIZE;
#endif
devfmt = NETDEV_WPAN_FORMAT;
break;
#endif
#ifdef CONFIG_NET_SLIP
case NET_LL_SLIP: /* Serial Line Internet Protocol (SLIP) */
llhdrlen = 0;
pktsize = CONFIG_NET_SLIP_PKTSIZE;
devfmt = NETDEV_SLIP_FORMAT;
break;
#endif
#ifdef CONFIG_NET_TUN
case NET_LL_TUN: /* Virtual Network Device (TUN) */
llhdrlen = 0; /* This will be overwritten by tun_ioctl
* if used as a TAP (layer 2) device */
pktsize = CONFIG_NET_TUN_PKTSIZE;
devfmt = NETDEV_TUN_FORMAT;
break;
#endif
case NET_LL_MBIM:
llhdrlen = 0;
pktsize = 1200;
devfmt = NETDEV_WWAN_FORMAT;
break;
default:
nerr("ERROR: Unrecognized link type: %d\n", lltype);
return -EINVAL;
}
/* Update the package length. A network driver may provide custom
* values for MAC header length and for the maximum packet size. Some
* driver implementations (for example, the simulator) will require
* dynamic MTU calculations to support tunneling (as an example).
*/
if (dev->d_llhdrlen == 0)
{
dev->d_llhdrlen = llhdrlen;
}
if (dev->d_pktsize == 0)
{
dev->d_pktsize = pktsize;
}
/* Remember the verified link type */
dev->d_lltype = (uint8_t)lltype;
/* There are no clients of the device yet */
dev->d_conncb = NULL;
dev->d_devcb = NULL;
/* We need exclusive access for the following operations */
net_lock();
#ifdef CONFIG_NETDEV_IFINDEX
ifindex = get_ifindex();
if (ifindex < 0)
{
return ifindex;
}
dev->d_ifindex = (uint8_t)ifindex;
#endif
/* Get the next available device number and assign a device name to
* the interface
*/
/* Check if the caller has provided a user device-specific interface
* name format string (in d_ifname).
*/
if (dev->d_ifname[0] != '\0')
{
/* Copy the string in a temporary buffer. How do we know that the
* string is valid and not just uninitialized memory? We don't.
* Let's at least make certain that the format string is NUL
* terminated.
*/
dev->d_ifname[IFNAMSIZ - 1] = '\0';
strncpy(devfmt_str, dev->d_ifname, IFNAMSIZ);
/* Then use the content of the temporary buffer as the format
* string.
*/
devfmt = (FAR const char *)devfmt_str;
}
#ifdef CONFIG_NET_LOOPBACK
/* The local loopback device is a special case: There can be only one
* local loopback device so it is unnumbered.
*/
if (lltype == NET_LL_LOOPBACK)
{
devnum = 0;
}
else
#endif
{
devnum = find_devnum(devfmt);
}
/* Complete the device name by including the device number (if
* included in the format).
*/
snprintf(dev->d_ifname, IFNAMSIZ, devfmt, devnum);
/* Add the device to the list of known network devices */
dev->flink = g_netdevices;
g_netdevices = dev;
#ifdef CONFIG_NET_IGMP
/* Configure the device for IGMP support */
igmp_devinit(dev);
#endif
#ifdef CONFIG_NET_MLD
/* Configure the device for MLD support */
mld_devinit(dev);
#endif
net_unlock();
#if defined(CONFIG_NET_ETHERNET) || defined(CONFIG_DRIVERS_IEEE80211)
ninfo("Registered MAC: %02x:%02x:%02x:%02x:%02x:%02x as dev: %s\n",
dev->d_mac.ether.ether_addr_octet[0],
dev->d_mac.ether.ether_addr_octet[1],
dev->d_mac.ether.ether_addr_octet[2],
dev->d_mac.ether.ether_addr_octet[3],
dev->d_mac.ether.ether_addr_octet[4],
dev->d_mac.ether.ether_addr_octet[5],
dev->d_ifname);
#else
ninfo("Registered dev: %s\n", dev->d_ifname);
#endif
return OK;
}
return -EINVAL;
}