nuttx/drivers/net/rpmsgdrv.c
Xiang Xiao 87cf5c58ae Correct some problems with network timed events when there are multiple network devices in the configuration.
Squashed commit of the following:

Author: Gregory Nutt <gnutt@nuttx.org>

    Ran nxstyle against many of the affected files.  But this job was too big for today.  Many of the network drivers under arch are highly non-compiant and generate many, many faults from nxstyle.  Those will have to be visited again another day.

Author: Xiang Xiao <xiaoxiang@xiaomi.com>

    This effects all network drivers as well as timing related portions of net/: devif_poll_tcp_timer shouldn't be skipped in the multiple card case.  devif_timer will be called multiple time in one period if the multiple card exist, the elapsed time calculated for the first callback is right, but the flowing callback in the same period is wrong(very short) because the global variable g_polltimer is used in the calculation.  So let's pass the delay time to devif_timer and remove g_polltimer.
2019-12-24 10:37:30 -06:00

1419 lines
40 KiB
C

/****************************************************************************
* drivers/net/rpmsgdrv.c
*
* Copyright (C) 2018 Pinecone Inc. All rights reserved.
* Author: Jianli Dong <dongjianli@pinecone.net>
*
* 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 <nuttx/config.h>
#include <assert.h>
#include <debug.h>
#include <errno.h>
#include <stdio.h>
#include <nuttx/kmalloc.h>
#include <nuttx/kthread.h>
#include <nuttx/semaphore.h>
#include <nuttx/wdog.h>
#include <nuttx/wqueue.h>
#include <nuttx/net/arp.h>
#include <nuttx/net/dns.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/pkt.h>
#include <nuttx/net/rpmsg.h>
#include <nuttx/rptun/openamp.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* The address family that we used to create the socket really does not
* matter. It should, however, be valid in the current configuration.
*/
#if defined(CONFIG_NET_IPv4)
# define NET_RPMSG_DRV_FAMILY AF_INET
#elif defined(CONFIG_NET_IPv6)
# define NET_RPMSG_DRV_FAMILY AF_INET6
#elif defined(CONFIG_NET_IEEE802154)
# define NET_RPMSG_DRV_FAMILY AF_IEEE802154
#elif defined(CONFIG_WIRELESS_PKTRADIO)
# define NET_RPMSG_DRV_FAMILY AF_PKTRADIO
#elif defined(CONFIG_NET_USRSOCK)
# define NET_RPMSG_DRV_FAMILY AF_INET
#elif defined(CONFIG_NET_PKT)
# define NET_RPMSG_DRV_FAMILY AF_PACKET
#elif defined(CONFIG_NET_LOCAL)
# define NET_RPMSG_DRV_FAMILY AF_LOCAL
#else
# define NET_RPMSG_DRV_FAMILY AF_UNSPEC
#endif
/* SOCK_DGRAM is the preferred socket type to use when we just want a
* socket for performing driver ioctls. However, we can't use SOCK_DRAM
* if UDP is disabled.
*
* Pick a socket type (and perhaps protocol) compatible with the currently
* selected address family.
*/
#if NET_RPMSG_DRV_FAMILY == AF_INET
# if defined(CONFIG_NET_UDP)
# define NET_RPMSG_DRV_TYPE SOCK_DGRAM
# elif defined(CONFIG_NET_TCP)
# define NET_RPMSG_DRV_TYPE SOCK_STREAM
# elif defined(CONFIG_NET_ICMP_SOCKET)
# define NET_RPMSG_DRV_TYPE SOCK_DGRAM
# define NET_RPMSG_DRV_PROTOCOL IPPROTO_ICMP
# endif
#elif NET_RPMSG_DRV_FAMILY == AF_INET6
# if defined(CONFIG_NET_UDP)
# define NET_RPMSG_DRV_TYPE SOCK_DGRAM
# elif defined(CONFIG_NET_TCP)
# define NET_RPMSG_DRV_TYPE SOCK_STREAM
# elif defined(CONFIG_NET_ICMPv6_SOCKET)
# define NET_RPMSG_DRV_TYPE SOCK_DGRAM
# define NET_RPMSG_DRV_PROTOCOL IPPROTO_ICMP6
# endif
#elif NET_RPMSG_DRV_FAMILY == AF_IEEE802154
# define NET_RPMSG_DRV_TYPE SOCK_DGRAM
#elif NET_RPMSG_DRV_FAMILY == AF_PKTRADIO
# define NET_RPMSG_DRV_TYPE SOCK_DGRAM
#elif NET_RPMSG_DRV_FAMILY == AF_PACKET
# define NET_RPMSG_DRV_TYPE SOCK_RAW
#elif NET_RPMSG_DRV_FAMILY == AF_LOCAL
# if defined(CONFIG_NET_LOCAL_DGRAM)
# define NET_RPMSG_DRV_TYPE SOCK_DGRAM
# elif defined(CONFIG_NET_LOCAL_STREAM)
# define NET_RPMSG_DRV_TYPE SOCK_STREAM
# endif
#endif
/* Socket protocol of zero normally works */
#ifndef NET_RPMSG_DRV_PROTOCOL
# define NET_RPMSG_DRV_PROTOCOL 0
#endif
/* Work queue support is required. */
#if !defined(CONFIG_SCHED_WORKQUEUE)
# error Work queue support is required in this configuration (CONFIG_SCHED_WORKQUEUE)
#endif
#ifdef CONFIG_NET_DUMPPACKET
# define net_rpmsg_drv_dumppacket lib_dumpbuffer
#else
# define net_rpmsg_drv_dumppacket(m, b, l)
#endif
/* TX poll delay = 1 seconds. CLK_TCK is the number of clock ticks per second */
#define NET_RPMSG_DRV_WDDELAY (1*CLK_TCK)
/****************************************************************************
* Private Types
****************************************************************************/
struct net_rpmsg_drv_cookie_s
{
FAR struct net_rpmsg_header_s *header;
sem_t sem;
};
/* net_rpmsg_drv_s encapsulates all state information for a single hardware
* interface
*/
struct net_rpmsg_drv_s
{
FAR const char *cpuname;
FAR const char *devname;
struct rpmsg_endpoint ept;
WDOG_ID txpoll; /* TX poll timer */
struct work_s pollwork; /* For deferring poll work to the work queue */
/* This holds the information visible to the NuttX network */
struct net_driver_s dev; /* Interface understood by the network */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Common TX logic */
static int net_rpmsg_drv_transmit(FAR struct net_driver_s *dev,
bool nocopy);
static int net_rpmsg_drv_txpoll(FAR struct net_driver_s *dev);
static void net_rpmsg_drv_reply(FAR struct net_driver_s *dev);
/* RPMSG related functions */
static int net_rpmsg_drv_default_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv);
static int net_rpmsg_drv_sockioctl_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv);
static int net_rpmsg_drv_transfer_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv);
static void net_rpmsg_drv_device_created(FAR struct rpmsg_device *rdev,
FAR void *priv_);
static void net_rpmsg_drv_device_destroy(FAR struct rpmsg_device *rdev,
FAR void *priv_);
static int net_rpmsg_drv_ept_cb(FAR struct rpmsg_endpoint *ept, void *data,
size_t len, uint32_t src, FAR void *priv);
static int net_rpmsg_drv_send_recv(struct net_driver_s *dev,
void *header_, uint32_t command, int len);
/* Watchdog timer expirations */
static void net_rpmsg_drv_poll_work(FAR void *arg);
static void net_rpmsg_drv_poll_expiry(int argc, wdparm_t arg, ...);
/* NuttX callback functions */
static int net_rpmsg_drv_ifup(FAR struct net_driver_s *dev);
static int net_rpmsg_drv_ifdown(FAR struct net_driver_s *dev);
static void net_rpmsg_drv_txavail_work(FAR void *arg);
static int net_rpmsg_drv_txavail(FAR struct net_driver_s *dev);
#if defined(CONFIG_NET_IGMP) || defined(CONFIG_NET_ICMPv6)
static int net_rpmsg_drv_addmac(FAR struct net_driver_s *dev,
FAR const uint8_t *mac);
#ifdef CONFIG_NET_IGMP
static int net_rpmsg_drv_rmmac(FAR struct net_driver_s *dev,
FAR const uint8_t *mac);
#endif
#ifdef CONFIG_NET_ICMPv6
static void net_rpmsg_drv_ipv6multicast(FAR struct net_driver_s *dev);
#endif
#endif
#ifdef CONFIG_NETDEV_IOCTL
static int net_rpmsg_drv_ioctl(FAR struct net_driver_s *dev, int cmd,
unsigned long arg);
#endif
/****************************************************************************
* Private Data
****************************************************************************/
static const rpmsg_ept_cb g_net_rpmsg_drv_handler[] =
{
[NET_RPMSG_IFUP] = net_rpmsg_drv_default_handler,
[NET_RPMSG_IFDOWN] = net_rpmsg_drv_default_handler,
[NET_RPMSG_ADDMCAST] = net_rpmsg_drv_default_handler,
[NET_RPMSG_RMMCAST] = net_rpmsg_drv_default_handler,
[NET_RPMSG_DEVIOCTL] = net_rpmsg_drv_default_handler,
[NET_RPMSG_SOCKIOCTL] = net_rpmsg_drv_sockioctl_handler,
[NET_RPMSG_TRANSFER] = net_rpmsg_drv_transfer_handler,
};
/****************************************************************************
* Private Functions
****************************************************************************/
static void net_rpmsg_drv_wait(FAR sem_t *sem)
{
int ret;
do
{
/* Take the semaphore (perhaps waiting) */
ret = net_lockedwait(sem);
/* The only case that an error should occur here is if the wait was
* awakened by a signal.
*/
DEBUGASSERT(ret == OK || ret == -EINTR);
}
while (ret == -EINTR);
}
/****************************************************************************
* Name: net_rpmsg_drv_transmit
*
* Description:
* Start hardware transmission. Called from watchdog based polling.
*
* Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* OK on success; a negated errno on failure
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static int net_rpmsg_drv_transmit(FAR struct net_driver_s *dev, bool nocopy)
{
FAR struct net_rpmsg_drv_s *priv = dev->d_private;
FAR struct net_rpmsg_transfer_s *msg;
int ret;
/* Verify that the hardware is ready to send another packet. If we get
* here, then we are committed to sending a packet; Higher level logic
* must have assured that there is no transmission in progress.
*/
/* Increment statistics */
net_rpmsg_drv_dumppacket("transmit", dev->d_buf, dev->d_len);
NETDEV_TXPACKETS(dev);
/* Send the packet: address=dev->d_buf, length=dev->d_len */
msg = (FAR struct net_rpmsg_transfer_s *)dev->d_buf - 1;
msg->header.command = NET_RPMSG_TRANSFER;
msg->header.result = 0;
msg->header.cookie = 0;
msg->length = dev->d_len;
if (nocopy)
{
ret = rpmsg_send_nocopy(&priv->ept, msg, sizeof(*msg) + msg->length);
}
else
{
ret = rpmsg_send(&priv->ept, msg, sizeof(*msg) + msg->length);
}
if (ret < 0)
{
NETDEV_TXERRORS(dev);
return ret;
}
else
{
NETDEV_TXDONE(dev);
return OK;
}
}
/****************************************************************************
* Name: net_rpmsg_drv_txpoll
*
* Description:
* The transmitter is available, check if the network has any outgoing
* packets ready to send. This is a callback from devif_poll().
* devif_poll() may be called:
*
* 1. When the preceding TX packet send is complete,
* 2. When the preceding TX packet send fail
* 3. During normal TX polling
*
* Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* OK on success; a negated errno on failure
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static int net_rpmsg_drv_txpoll(FAR struct net_driver_s *dev)
{
FAR struct net_rpmsg_drv_s *priv = dev->d_private;
uint32_t size;
/* If the polling resulted in data that should be sent out on the network,
* the field d_len is set to a value > 0.
*/
if (dev->d_len > 0)
{
/* Look up the destination MAC address and add it to the Ethernet
* header.
*/
#ifdef CONFIG_NET_IPv4
if (IFF_IS_IPv4(dev->d_flags))
{
arp_out(dev);
}
#endif /* CONFIG_NET_IPv4 */
#ifdef CONFIG_NET_IPv6
if (IFF_IS_IPv6(dev->d_flags))
{
neighbor_out(dev);
}
#endif /* CONFIG_NET_IPv6 */
if (!devif_loopback(dev))
{
/* Send the packet */
net_rpmsg_drv_transmit(dev, true);
/* Check if there is room in the device to hold another packet. If not,
* return a non-zero value to terminate the poll.
*/
dev->d_buf = rpmsg_get_tx_payload_buffer(&priv->ept, &size, false);
if (dev->d_buf)
{
dev->d_buf += sizeof(struct net_rpmsg_transfer_s);
dev->d_pktsize = size - sizeof(struct net_rpmsg_transfer_s);
}
return dev->d_buf == NULL;
}
}
/* If zero is returned, the polling will continue until all connections have
* been examined.
*/
return 0;
}
/****************************************************************************
* Name: net_rpmsg_drv_reply
*
* Description:
* After a packet has been received and dispatched to the network, it
* may return with an outgoing packet. This function checks for
* that case and performs the transmission if necessary.
*
* Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static void net_rpmsg_drv_reply(FAR struct net_driver_s *dev)
{
/* If the packet dispatch resulted in data that should be sent out on the
* network, the field d_len will set to a value > 0.
*/
if (dev->d_len > 0)
{
/* Update the Ethernet header with the correct MAC address */
#ifdef CONFIG_NET_IPv4
if (IFF_IS_IPv4(dev->d_flags))
{
arp_out(dev);
}
#endif
#ifdef CONFIG_NET_IPv6
if (IFF_IS_IPv6(dev->d_flags))
{
neighbor_out(dev);
}
#endif
/* And send the packet */
net_rpmsg_drv_transmit(dev, false);
}
}
/* RPMSG related functions */
static int net_rpmsg_drv_default_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv)
{
FAR struct net_rpmsg_header_s *header = data;
FAR struct net_rpmsg_drv_cookie_s *cookie =
(struct net_rpmsg_drv_cookie_s *)(uintptr_t)header->cookie;
memcpy(cookie->header, header, len);
nxsem_post(&cookie->sem);
return 0;
}
static int net_rpmsg_drv_sockioctl_task(int argc, FAR char *argv[])
{
FAR struct net_rpmsg_ioctl_s *msg;
FAR struct rpmsg_endpoint *ept;
struct socket sock;
int domain = NET_RPMSG_DRV_FAMILY;
int type = NET_RPMSG_DRV_TYPE;
int protocol = NET_RPMSG_DRV_PROTOCOL;
/* Restore pointers from argv */
ept = (FAR struct rpmsg_endpoint *)strtoul(argv[1], NULL, 0);
msg = (FAR struct net_rpmsg_ioctl_s *)strtoul(argv[2], NULL, 0);
/* We need a temporary sock for ioctl here */
if (msg->code == SIOCIFAUTOCONF)
{
domain = PF_INET6;
type = SOCK_DGRAM;
protocol = IPPROTO_ICMP6;
}
sock.s_crefs = 1; /* Initialize reference count manually */
msg->header.result = psock_socket(domain, type, protocol, &sock);
if (msg->header.result >= 0)
{
msg->header.result = psock_ioctl(&sock, msg->code, (unsigned long)msg->arg);
psock_close(&sock); /* Close the temporary sock */
}
/* Send the response only when cookie doesn't equal NULL */
if (msg->header.cookie)
{
rpmsg_send(ept, msg, sizeof(*msg) + msg->length);
}
rpmsg_release_rx_buffer(ept, msg);
return 0;
}
static int net_rpmsg_drv_sockioctl_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv)
{
FAR char *argv[3];
char arg1[16];
char arg2[16];
/* Save pointers into argv */
sprintf(arg1, "%#p", ept);
sprintf(arg2, "%#p", data);
argv[0] = arg1;
argv[1] = arg2;
argv[2] = NULL;
/* Move the action into a temp thread to avoid the deadlock */
rpmsg_hold_rx_buffer(ept, data);
kthread_create("rpmsg-net", CONFIG_NET_RPMSG_PRIORITY,
CONFIG_NET_RPMSG_STACKSIZE, net_rpmsg_drv_sockioctl_task, argv);
return 0;
}
#ifdef CONFIG_NET_IPv4
static bool net_rpmsg_drv_is_ipv4(FAR struct net_driver_s *dev)
{
FAR struct ipv4_hdr_s *ip = (struct ipv4_hdr_s *)(dev->d_buf + dev->d_llhdrlen);
FAR struct eth_hdr_s *eth = (struct eth_hdr_s *)dev->d_buf;
if (dev->d_lltype == NET_LL_ETHERNET || dev->d_lltype == NET_LL_IEEE80211)
{
return eth->type == HTONS(ETHTYPE_IP);
}
else
{
return (ip->vhl & IP_VERSION_MASK) == IPv4_VERSION;
}
}
#endif
#ifdef CONFIG_NET_IPv6
static bool net_rpmsg_drv_is_ipv6(FAR struct net_driver_s *dev)
{
FAR struct ipv6_hdr_s *ip = (struct ipv6_hdr_s *)(dev->d_buf + dev->d_llhdrlen);
FAR struct eth_hdr_s *eth = (struct eth_hdr_s *)dev->d_buf;
if (dev->d_lltype == NET_LL_ETHERNET || dev->d_lltype == NET_LL_IEEE80211)
{
return eth->type == HTONS(ETHTYPE_IP6);
}
else
{
return (ip->vtc & IP_VERSION_MASK) == IPv6_VERSION;
}
}
#endif
#ifdef CONFIG_NET_ARP
static bool net_rpmsg_drv_is_arp(FAR struct net_driver_s *dev)
{
FAR struct eth_hdr_s *eth = (struct eth_hdr_s *)dev->d_buf;
if (dev->d_lltype == NET_LL_ETHERNET || dev->d_lltype == NET_LL_IEEE80211)
{
return eth->type == HTONS(ETHTYPE_ARP);
}
else
{
return false;
}
}
#endif
/****************************************************************************
* Name: net_rpmsg_drv_transfer_handler
*
* Description:
* An message was received indicating the availability of a new RX packet
*
* Parameters:
* ept - Reference to the endpoint which receive the message
*
* Returned Value:
* OK on success
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static int net_rpmsg_drv_transfer_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv)
{
FAR struct net_driver_s *dev = ept->priv;
FAR struct net_rpmsg_transfer_s *msg = data;
FAR void *oldbuf;
/* Lock the network and serialize driver operations if necessary.
* NOTE: Serialization is only required in the case where the driver work
* is performed on an LP worker thread and where more than one LP worker
* thread has been configured.
*/
net_lock();
/* Check for errors and update statistics */
net_rpmsg_drv_dumppacket("receive", msg->data, msg->length);
NETDEV_RXPACKETS(dev);
/* Copy the data from the hardware to dev->d_buf. Set
* amount of data in dev->d_len
*/
oldbuf = dev->d_buf;
dev->d_buf = msg->data;
dev->d_len = msg->length;
#ifdef CONFIG_NET_PKT
/* When packet sockets are enabled, feed the frame into the packet tap */
pkt_input(dev);
#endif
/* We only accept IP packets of the configured type and ARP packets */
#ifdef CONFIG_NET_IPv4
if (net_rpmsg_drv_is_ipv4(dev))
{
ninfo("IPv4 frame\n");
NETDEV_RXIPV4(dev);
/* Handle ARP on input, then dispatch IPv4 packet to the network
* layer.
*/
arp_ipin(dev);
ipv4_input(dev);
/* Check for a reply to the IPv4 packet */
net_rpmsg_drv_reply(dev);
}
else
#endif
#ifdef CONFIG_NET_IPv6
if (net_rpmsg_drv_is_ipv6(dev))
{
ninfo("Iv6 frame\n");
NETDEV_RXIPV6(dev);
/* Dispatch IPv6 packet to the network layer */
ipv6_input(dev);
/* Check for a reply to the IPv6 packet */
net_rpmsg_drv_reply(dev);
}
else
#endif
#ifdef CONFIG_NET_ARP
if (net_rpmsg_drv_is_arp(dev))
{
ninfo("ARP frame\n");
NETDEV_RXARP(dev);
/* Dispatch ARP packet to the network layer */
arp_arpin(dev);
/* Check for a reply to the ARP packet */
net_rpmsg_drv_reply(dev);
}
else
#endif
{
NETDEV_RXDROPPED(dev);
}
dev->d_buf = oldbuf;
net_unlock();
return 0;
}
static void net_rpmsg_drv_device_created(FAR struct rpmsg_device *rdev,
FAR void *priv_)
{
FAR struct net_driver_s *dev = priv_;
FAR struct net_rpmsg_drv_s *priv = dev->d_private;
char eptname[RPMSG_NAME_SIZE];
if (!strcmp(priv->cpuname, rpmsg_get_cpuname(rdev)))
{
priv->ept.priv = dev;
sprintf(eptname, NET_RPMSG_EPT_NAME, priv->devname);
rpmsg_create_ept(&priv->ept, rdev, eptname,
RPMSG_ADDR_ANY, RPMSG_ADDR_ANY,
net_rpmsg_drv_ept_cb, NULL);
}
}
static void net_rpmsg_drv_device_destroy(FAR struct rpmsg_device *rdev,
FAR void *priv_)
{
FAR struct net_driver_s *dev = priv_;
FAR struct net_rpmsg_drv_s *priv = dev->d_private;
if (!strcmp(priv->cpuname, rpmsg_get_cpuname(rdev)))
{
rpmsg_destroy_ept(&priv->ept);
dev->d_buf = NULL;
}
}
static int net_rpmsg_drv_ept_cb(FAR struct rpmsg_endpoint *ept, void *data,
size_t len, uint32_t src, FAR void *priv)
{
FAR struct net_rpmsg_header_s *header = data;
uint32_t command = header->command;
if (command < sizeof(g_net_rpmsg_drv_handler) / sizeof(g_net_rpmsg_drv_handler[0]))
{
return g_net_rpmsg_drv_handler[command](ept, data, len, src, priv);
}
return -EINVAL;
}
static int net_rpmsg_drv_send_recv(FAR struct net_driver_s *dev,
FAR void *header_, uint32_t command,
int len)
{
FAR struct net_rpmsg_drv_s *priv = dev->d_private;
FAR struct net_rpmsg_header_s *header = header_;
FAR struct net_rpmsg_drv_cookie_s cookie;
int ret;
nxsem_init(&cookie.sem, 0, 0);
nxsem_setprotocol(&cookie.sem, SEM_PRIO_NONE);
cookie.header = header;
header->command = command;
header->result = -ENXIO;
header->cookie = (uintptr_t)&cookie;
ret = rpmsg_send(&priv->ept, header, len);
if (ret < 0)
{
goto out;
}
net_rpmsg_drv_wait(&cookie.sem);
ret = cookie.header->result;
out:
nxsem_destroy(&cookie.sem);
return ret;
}
/****************************************************************************
* Name: net_rpmsg_drv_poll_work
*
* Description:
* Perform periodic polling from the worker thread
*
* Parameters:
* arg - The argument passed when work_queue() as called.
*
* Returned Value:
* OK on success
*
* Assumptions:
* Run on a work queue thread.
*
****************************************************************************/
static void net_rpmsg_drv_poll_work(FAR void *arg)
{
FAR struct net_driver_s *dev = arg;
FAR struct net_rpmsg_drv_s *priv = dev->d_private;
uint32_t size;
/* Lock the network and serialize driver operations if necessary.
* NOTE: Serialization is only required in the case where the driver work
* is performed on an LP worker thread and where more than one LP worker
* thread has been configured.
*/
net_lock();
/* Perform the poll */
/* Check if there is room in the send another TX packet. We cannot perform
* the TX poll if he are unable to accept another packet for transmission.
*/
if (dev->d_buf == NULL)
{
/* Try to get the payload buffer if not yet */
dev->d_buf = rpmsg_get_tx_payload_buffer(&priv->ept, &size, false);
if (dev->d_buf)
{
dev->d_buf += sizeof(struct net_rpmsg_transfer_s);
dev->d_pktsize = size - sizeof(struct net_rpmsg_transfer_s);
}
}
if (dev->d_buf)
{
/* If so, update TCP timing states and poll the network for new XMIT data.
* Hmmm.. might be bug here. Does this mean if there is a transmit in
* progress, we will missing TCP time state updates?
*/
devif_timer(dev, NET_RPMSG_DRV_WDDELAY, net_rpmsg_drv_txpoll);
}
/* Setup the watchdog poll timer again */
wd_start(priv->txpoll, NET_RPMSG_DRV_WDDELAY, net_rpmsg_drv_poll_expiry, 1,
(wdparm_t)dev);
net_unlock();
}
/****************************************************************************
* Name: net_rpmsg_drv_poll_expiry
*
* Description:
* Periodic timer handler. Called from the timer interrupt handler.
*
* Parameters:
* argc - The number of available arguments
* arg - The first argument
*
* Returned Value:
* None
*
* Assumptions:
* Runs in the context of a the timer interrupt handler. Local
* interrupts are disabled by the interrupt logic.
*
****************************************************************************/
static void net_rpmsg_drv_poll_expiry(int argc, wdparm_t arg, ...)
{
FAR struct net_driver_s *dev = (FAR struct net_driver_s *)arg;
FAR struct net_rpmsg_drv_s *priv = dev->d_private;
/* Schedule to perform the interrupt processing on the worker thread. */
work_queue(LPWORK, &priv->pollwork, net_rpmsg_drv_poll_work, dev, 0);
}
/****************************************************************************
* Name: net_rpmsg_drv_ifup
*
* Description:
* NuttX Callback: Bring up the link interface when an IP address is
* provided
*
* Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static int net_rpmsg_drv_ifup(FAR struct net_driver_s *dev)
{
FAR struct net_rpmsg_drv_s *priv = dev->d_private;
struct net_rpmsg_ifup_s msg =
{
};
int ret;
#ifdef CONFIG_NET_IPv4
ninfo("Bringing up: %d.%d.%d.%d\n",
dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
(dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24);
#endif
#ifdef CONFIG_NET_IPv6
ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
#endif
net_lock();
/* Prepare the message */
msg.lnkaddr.length = netdev_lladdrsize(dev);
memcpy(msg.lnkaddr.addr, &dev->d_mac, msg.lnkaddr.length);
#ifdef CONFIG_NET_IPv4
net_ipv4addr_copy(msg.ipaddr, dev->d_ipaddr);
net_ipv4addr_copy(msg.draddr, dev->d_draddr);
net_ipv4addr_copy(msg.netmask, dev->d_netmask);
#endif
#ifdef CONFIG_NET_IPv6
net_ipv6addr_copy(msg.ipv6addr, dev->d_ipv6addr);
net_ipv6addr_copy(msg.ipv6draddr, dev->d_ipv6draddr);
net_ipv6addr_copy(msg.ipv6netmask, dev->d_ipv6netmask);
#endif
/* Send the message */
ret = net_rpmsg_drv_send_recv(dev, &msg, NET_RPMSG_IFUP, sizeof(msg));
if (ret < 0)
{
net_unlock();
return ret;
}
/* Update net_driver_t field */
memcpy(&dev->d_mac, msg.lnkaddr.addr, msg.lnkaddr.length);
#ifdef CONFIG_NET_IPv4
net_ipv4addr_copy(dev->d_ipaddr, msg.ipaddr);
net_ipv4addr_copy(dev->d_draddr, msg.draddr);
net_ipv4addr_copy(dev->d_netmask, msg.netmask);
#endif
#ifdef CONFIG_NET_IPv6
net_ipv6addr_copy(dev->d_ipv6addr, msg.ipv6addr);
net_ipv6addr_copy(dev->d_ipv6draddr, msg.ipv6draddr);
net_ipv6addr_copy(dev->d_ipv6netmask, msg.ipv6netmask);
#endif
#ifdef CONFIG_NET_ICMPv6
/* Set up IPv6 multicast address filtering */
net_rpmsg_drv_ipv6multicast(dev);
#endif
/* Set and activate a timer process */
wd_start(priv->txpoll, NET_RPMSG_DRV_WDDELAY, net_rpmsg_drv_poll_expiry, 1,
(wdparm_t)dev);
net_unlock();
#ifdef CONFIG_NETDB_DNSCLIENT
# ifdef CONFIG_NET_IPv4
if (!net_ipv4addr_cmp(msg.dnsaddr, INADDR_ANY))
{
struct sockaddr_in dnsaddr =
{
};
dnsaddr.sin_family = AF_INET;
dnsaddr.sin_port = htons(DNS_DEFAULT_PORT);
memcpy(&dnsaddr.sin_addr, &msg.dnsaddr, sizeof(msg.dnsaddr));
dns_add_nameserver((FAR const struct sockaddr *)&dnsaddr, sizeof(dnsaddr));
}
# endif
# ifdef CONFIG_NET_IPv6
if (!net_ipv6addr_cmp(msg.ipv6dnsaddr, &in6addr_any))
{
struct sockaddr_in6 dnsaddr =
{
};
dnsaddr.sin6_family = AF_INET6;
dnsaddr.sin6_port = htons(DNS_DEFAULT_PORT);
memcpy(&dnsaddr.sin6_addr, msg.ipv6dnsaddr, sizeof(msg.ipv6dnsaddr));
dns_add_nameserver((FAR const struct sockaddr *)&dnsaddr, sizeof(dnsaddr));
}
# endif
#endif
return OK;
}
/****************************************************************************
* Name: net_rpmsg_drv_ifdown
*
* Description:
* NuttX Callback: Stop the interface.
*
* Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static int net_rpmsg_drv_ifdown(FAR struct net_driver_s *dev)
{
FAR struct net_rpmsg_drv_s *priv = dev->d_private;
FAR struct net_rpmsg_ifdown_s msg;
irqstate_t flags;
/* Disable the interrupt */
flags = enter_critical_section();
/* Cancel the TX poll timer and work */
wd_cancel(priv->txpoll);
work_cancel(LPWORK, &priv->pollwork);
leave_critical_section(flags);
/* Put the EMAC in its reset, non-operational state. This should be
* a known configuration that will guarantee the net_rpmsg_drv_ifup() always
* successfully brings the interface back up.
*/
return net_rpmsg_drv_send_recv(dev, &msg, NET_RPMSG_IFDOWN, sizeof(msg));
}
/****************************************************************************
* Name: net_rpmsg_drv_txavail_work
*
* Description:
* Perform an out-of-cycle poll on the worker thread.
*
* Parameters:
* arg - Reference to the NuttX driver state structure (cast to void*)
*
* Returned Value:
* None
*
* Assumptions:
* Runs on a work queue thread.
*
****************************************************************************/
static void net_rpmsg_drv_txavail_work(FAR void *arg)
{
FAR struct net_driver_s *dev = arg;
FAR struct net_rpmsg_drv_s *priv = dev->d_private;
uint32_t size;
/* Lock the network and serialize driver operations if necessary.
* NOTE: Serialization is only required in the case where the driver work
* is performed on an LP worker thread and where more than one LP worker
* thread has been configured.
*/
net_lock();
/* Ignore the notification if the interface is not yet up */
if (IFF_IS_UP(dev->d_flags))
{
/* Try to get the payload buffer if not yet */
if (dev->d_buf == NULL)
{
dev->d_buf = rpmsg_get_tx_payload_buffer(&priv->ept, &size, false);
if (dev->d_buf)
{
dev->d_buf += sizeof(struct net_rpmsg_transfer_s);
dev->d_pktsize = size - sizeof(struct net_rpmsg_transfer_s);
}
}
/* Check if there is room in the hardware to hold another outgoing packet. */
if (dev->d_buf)
{
/* If so, then poll the network for new XMIT data */
devif_poll(dev, net_rpmsg_drv_txpoll);
}
}
net_unlock();
}
/****************************************************************************
* Name: net_rpmsg_drv_txavail
*
* Description:
* Driver callback invoked when new TX data is available. This is a
* stimulus perform an out-of-cycle poll and, thereby, reduce the TX
* latency.
*
* Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static int net_rpmsg_drv_txavail(FAR struct net_driver_s *dev)
{
FAR struct net_rpmsg_drv_s *priv = dev->d_private;
/* Is our single work structure available? It may not be if there are
* pending interrupt actions and we will have to ignore the Tx
* availability action.
*/
if (work_available(&priv->pollwork))
{
/* Schedule to serialize the poll on the worker thread. */
work_queue(LPWORK, &priv->pollwork, net_rpmsg_drv_txavail_work, dev, 0);
}
return OK;
}
/****************************************************************************
* Name: net_rpmsg_drv_addmac
*
* Description:
* NuttX Callback: Add the specified MAC address to the hardware multicast
* address filtering
*
* Parameters:
* dev - Reference to the NuttX driver state structure
* mac - The MAC address to be added
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
#if defined(CONFIG_NET_IGMP) || defined(CONFIG_NET_ICMPv6)
static int net_rpmsg_drv_addmac(FAR struct net_driver_s *dev,
FAR const uint8_t *mac)
{
struct net_rpmsg_mcast_s msg;
/* Add the MAC address to the hardware multicast routing table */
msg.lnkaddr.length = netdev_lladdrsize(dev);
memcpy(msg.lnkaddr.addr, mac, msg.lnkaddr.length);
return net_rpmsg_drv_send_recv(dev, &msg, NET_RPMSG_ADDMCAST, sizeof(msg));
}
#endif
/****************************************************************************
* Name: net_rpmsg_drv_rmmac
*
* Description:
* NuttX Callback: Remove the specified MAC address from the hardware multicast
* address filtering
*
* Parameters:
* dev - Reference to the NuttX driver state structure
* mac - The MAC address to be removed
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
#ifdef CONFIG_NET_IGMP
static int net_rpmsg_drv_rmmac(FAR struct net_driver_s *dev,
FAR const uint8_t *mac)
{
struct net_rpmsg_mcast_s msg;
/* Remove the MAC address from the hardware multicast routing table */
msg.lnkaddr.length = netdev_lladdrsize(dev);
memcpy(msg.lnkaddr.addr, mac, msg.lnkaddr.length);
return net_rpmsg_drv_send_recv(dev, &msg, NET_RPMSG_RMMCAST, sizeof(msg));
}
#endif
/****************************************************************************
* Name: net_rpmsg_drv_ipv6multicast
*
* Description:
* Configure the IPv6 multicast MAC address.
*
* Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
#ifdef CONFIG_NET_ICMPv6
static void net_rpmsg_drv_ipv6multicast(FAR struct net_driver_s *dev)
{
if (dev->d_lltype == NET_LL_ETHERNET || dev->d_lltype == NET_LL_IEEE80211)
{
uint16_t tmp16;
uint8_t mac[6];
/* For ICMPv6, we need to add the IPv6 multicast address
*
* For IPv6 multicast addresses, the Ethernet MAC is derived by
* the four low-order octets OR'ed with the MAC 33:33:00:00:00:00,
* so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map
* to the Ethernet MAC address 33:33:00:01:00:03.
*
* NOTES: This appears correct for the ICMPv6 Router Solicitation
* Message, but the ICMPv6 Neighbor Solicitation message seems to
* use 33:33:ff:01:00:03.
*/
mac[0] = 0x33;
mac[1] = 0x33;
tmp16 = dev->d_ipv6addr[6];
mac[2] = 0xff;
mac[3] = tmp16 >> 8;
tmp16 = dev->d_ipv6addr[7];
mac[4] = tmp16 & 0xff;
mac[5] = tmp16 >> 8;
ninfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
net_rpmsg_drv_addmac(dev, mac);
#if defined(CONFIG_NET_ETHERNET) && defined(CONFIG_NET_ICMPv6_AUTOCONF)
/* Add the IPv6 all link-local nodes Ethernet address. This is the
* address that we expect to receive ICMPv6 Router Advertisement
* packets.
*/
net_rpmsg_drv_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet);
#endif /* CONFIG_NET_ETHERNET && CONFIG_NET_ICMPv6_AUTOCONF */
#if defined(CONFIG_NET_ETHERNET) && defined(CONFIG_NET_ICMPv6_ROUTER)
/* Add the IPv6 all link-local routers Ethernet address. This is the
* address that we expect to receive ICMPv6 Router Solicitation
* packets.
*/
net_rpmsg_drv_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet);
#endif /* CONFIG_NET_ETHERNET && CONFIG_NET_ICMPv6_ROUTER */
}
}
#endif /* CONFIG_NET_ICMPv6 */
/****************************************************************************
* Name: net_rpmsg_drv_ioctl
*
* Description:
* Handle network IOCTL commands directed to this device.
*
* Parameters:
* dev - Reference to the NuttX driver state structure
* cmd - The IOCTL command
* arg - The argument for the IOCTL command
*
* Returned Value:
* OK on success; Negated errno on failure.
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
#ifdef CONFIG_NETDEV_IOCTL
static int net_rpmsg_drv_ioctl(FAR struct net_driver_s *dev, int cmd,
unsigned long arg)
{
ssize_t len;
int ret;
len = net_ioctl_arglen(cmd);
if (len >= 0)
{
FAR struct net_rpmsg_ioctl_s *msg;
char buf[sizeof(*msg) + len];
msg = (FAR struct net_rpmsg_ioctl_s *)buf;
msg->code = cmd;
msg->length = len;
memcpy(msg->arg, (FAR void *)arg, len);
ret = net_rpmsg_drv_send_recv(dev, msg,
NET_RPMSG_DEVIOCTL, sizeof(*msg) + len);
if (ret >= 0)
{
memcpy((FAR void *)arg, msg->arg, len);
}
}
else
{
ret = len;
}
return ret;
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: net_rpmsg_drv_init
*
* Description:
* Initialize the net rpmsg driver
*
* Parameters:
* name - Specify the netdev name
* lltype - Identify the link type
*
* Returned Value:
* OK on success; Negated errno on failure.
*
* Assumptions:
* Called early in initialization before multi-tasking is initiated.
*
****************************************************************************/
int net_rpmsg_drv_init(FAR const char *cpuname,
FAR const char *devname,
enum net_lltype_e lltype)
{
FAR struct net_rpmsg_drv_s *priv;
FAR struct net_driver_s *dev;
/* Allocate the interface structure */
priv = kmm_zalloc(sizeof(*priv));
if (priv == NULL)
{
return -ENOMEM;
}
dev = &priv->dev;
priv->cpuname = cpuname;
priv->devname = devname;
/* Initialize the driver structure */
strcpy(dev->d_ifname, devname);
dev->d_ifup = net_rpmsg_drv_ifup; /* I/F up (new IP address) callback */
dev->d_ifdown = net_rpmsg_drv_ifdown; /* I/F down callback */
dev->d_txavail = net_rpmsg_drv_txavail; /* New TX data callback */
#ifdef CONFIG_NET_IGMP
dev->d_addmac = net_rpmsg_drv_addmac; /* Add multicast MAC address */
dev->d_rmmac = net_rpmsg_drv_rmmac; /* Remove multicast MAC address */
#endif
#ifdef CONFIG_NETDEV_IOCTL
dev->d_ioctl = net_rpmsg_drv_ioctl; /* Handle network IOCTL commands */
#endif
dev->d_private = priv; /* Used to recover private state from dev */
/* Create a watchdog for timing polling for transmissions */
priv->txpoll = wd_create(); /* Create periodic poll timer */
DEBUGASSERT(priv->txpoll != NULL);
/* Register the device with the openamp */
rpmsg_register_callback(dev,
net_rpmsg_drv_device_created,
net_rpmsg_drv_device_destroy,
NULL);
/* Register the device with the OS so that socket IOCTLs can be performed */
netdev_register(dev, lltype);
return OK;
}