blob: 666cc5429020a8c389f697da64eb33230b3d0fc8 [file]
/****************************************************************************
* drivers/net/rpmsgdrv.c
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 <assert.h>
#include <nuttx/debug.h>
#include <errno.h>
#include <stdio.h>
#include <nuttx/kmalloc.h>
#include <nuttx/kthread.h>
#include <nuttx/semaphore.h>
#include <nuttx/spinlock.h>
#include <nuttx/wqueue.h>
#include <nuttx/net/dns.h>
#include <nuttx/net/ip.h>
#include <nuttx/net/netdev_lowerhalf.h>
#include <nuttx/net/pkt.h>
#include <nuttx/net/rpmsg.h>
#include <nuttx/net/rpmsgdrv.h>
#include <nuttx/rpmsg/rpmsg.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define NET_RPMSG_DRV_BUFSIZE (CONFIG_NET_ETH_PKTSIZE + CONFIG_NET_GUARDSIZE)
#define NET_RPMSG_DRV_MAX_PKT_SIZE \
((CONFIG_NET_LL_GUARDSIZE - ETH_HDRLEN) + NET_RPMSG_DRV_BUFSIZE)
#define NET_RPMSG_DRV_MAX_NIOB \
((NET_RPMSG_DRV_MAX_PKT_SIZE + CONFIG_IOB_BUFSIZE - 1) / \
CONFIG_IOB_BUFSIZE)
/****************************************************************************
* 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
{
char cpuname[RPMSG_NAME_SIZE];
netpkt_queue_t rxqueue; /* RX packet queue */
spinlock_t lock; /* Spinlock for protecting rxqueue */
FAR void *priv; /* Private data for upper layer */
net_rpmsg_drv_cb_t cb; /* IFUP/DOWN Callback function */
sem_t wait; /* Wait sem, used for preventing any
* operation until the connection
* between two cpu established.
*/
struct rpmsg_endpoint ept;
/* This holds the information visible to the NuttX network */
struct netdev_lowerhalf_s dev; /* Interface understood by the network */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* RPMSG related functions */
/* Request handler 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_ifup_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv);
static int net_rpmsg_drv_ifdown_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);
/* Response handler functions */
static int net_rpmsg_drv_default_response(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv);
/* RPMSG device related functions */
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 netdev_lowerhalf_s *dev,
void *header_, uint32_t command,
int len);
/* NuttX callback functions */
static int net_rpmsg_drv_ifup(FAR struct netdev_lowerhalf_s *dev);
static int net_rpmsg_drv_ifdown(FAR struct netdev_lowerhalf_s *dev);
static int net_rpmsg_drv_transmit(FAR struct netdev_lowerhalf_s *dev,
FAR netpkt_t *pkt);
static FAR netpkt_t *
net_rpmsg_drv_receive(FAR struct netdev_lowerhalf_s *dev);
#ifdef CONFIG_NET_MCASTGROUP
static int net_rpmsg_drv_addmac(FAR struct netdev_lowerhalf_s *dev,
FAR const uint8_t *mac);
#ifdef CONFIG_NET_IGMP
static int net_rpmsg_drv_rmmac(FAR struct netdev_lowerhalf_s *dev,
FAR const uint8_t *mac);
#endif
#endif
#ifdef CONFIG_NETDEV_IOCTL
static int net_rpmsg_drv_ioctl(FAR struct netdev_lowerhalf_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_ifup_handler,
[NET_RPMSG_IFDOWN] = net_rpmsg_drv_ifdown_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,
};
static const rpmsg_ept_cb g_net_rpmsg_drv_response[] =
{
[NET_RPMSG_IFUP] = net_rpmsg_drv_default_response,
[NET_RPMSG_IFDOWN] = net_rpmsg_drv_default_response,
[NET_RPMSG_ADDMCAST] = net_rpmsg_drv_default_response,
[NET_RPMSG_RMMCAST] = net_rpmsg_drv_default_response,
[NET_RPMSG_DEVIOCTL] = net_rpmsg_drv_default_response,
[NET_RPMSG_SOCKIOCTL] = net_rpmsg_drv_default_response,
[NET_RPMSG_TRANSFER] = net_rpmsg_drv_default_response,
};
static const struct netdev_ops_s g_net_rpmsg_drv_ops =
{
.ifup = net_rpmsg_drv_ifup,
.ifdown = net_rpmsg_drv_ifdown,
.transmit = net_rpmsg_drv_transmit,
.receive = net_rpmsg_drv_receive,
#ifdef CONFIG_NET_MCASTGROUP
.addmac = net_rpmsg_drv_addmac,
.rmmac = net_rpmsg_drv_rmmac,
#endif
#ifdef CONFIG_NETDEV_IOCTL
.ioctl = net_rpmsg_drv_ioctl,
#endif
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: net_rpmsg_drv_transmit
*
* Description:
* Start hardware transmission.
*
* Parameters:
* dev - Reference to the NuttX driver state structure
* pkt - The packet to be sent
*
* Returned Value:
* OK on success; a negated errno on failure
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static int net_rpmsg_drv_transmit(FAR struct netdev_lowerhalf_s *dev,
FAR netpkt_t *pkt)
{
FAR struct net_rpmsg_drv_s *drv =
container_of(dev, struct net_rpmsg_drv_s, dev);
FAR struct net_rpmsg_transfer_s *transfer;
unsigned int datalen = netpkt_getdatalen(dev, pkt);
uint32_t len;
int ret;
transfer = rpmsg_get_tx_payload_buffer(&drv->ept, &len, true);
if (transfer == NULL)
{
nwarn("WARNING: Failed to get buffer for xmit\n");
return -ENOMEM;
}
if (len < sizeof(*transfer) + datalen)
{
nerr("ERROR: Buffer is too small for xmit\n");
rpmsg_release_tx_buffer(&drv->ept, transfer);
return -ENOMEM;
}
transfer->header.command = NET_RPMSG_TRANSFER;
transfer->header.result = 0;
transfer->header.cookie = 0;
transfer->length = datalen;
netpkt_copyout(dev, (FAR uint8_t *)(transfer + 1), pkt, datalen, 0);
len = sizeof(*transfer) + datalen;
ret = rpmsg_send_nocopy(&drv->ept, transfer, len);
if (ret < 0)
{
nerr("ERROR: Failed to send packet\n");
rpmsg_release_tx_buffer(&drv->ept, transfer);
return ret;
}
netpkt_free(dev, pkt, NETPKT_TX);
netdev_lower_txdone(dev);
return OK;
}
static FAR netpkt_t *
net_rpmsg_drv_receive(FAR struct netdev_lowerhalf_s *dev)
{
FAR struct net_rpmsg_drv_s *drv =
container_of(dev, struct net_rpmsg_drv_s, dev);
FAR netpkt_t *pkt;
irqstate_t flags;
flags = spin_lock_irqsave(&drv->lock);
pkt = netpkt_remove_queue(&drv->rxqueue);
spin_unlock_irqrestore(&drv->lock, flags);
return pkt;
}
/* RPMSG related functions */
static void rpmsg_send_response(FAR struct rpmsg_endpoint *ept,
FAR struct net_rpmsg_header_s *header,
size_t len, int result)
{
header->command |= NET_RPMSG_RESPONSE;
header->result = result;
rpmsg_send(ept, header, len);
}
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;
if (header->cookie)
{
rpmsg_send_response(ept, header, sizeof(*header), -EOPNOTSUPP);
}
return 0;
}
static int net_rpmsg_drv_ifup_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv)
{
FAR struct net_rpmsg_drv_s *drv = priv;
FAR struct net_rpmsg_header_s *header = data;
netdev_lower_carrier_on(&drv->dev);
if (drv->cb != NULL)
{
drv->cb(&drv->dev, NET_RPMSG_EVENT_CARRIER_ON);
}
rpmsg_send_response(ept, header, sizeof(*header), 0);
return 0;
}
static int net_rpmsg_drv_ifdown_handler(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv)
{
FAR struct net_rpmsg_drv_s *drv = priv;
FAR struct net_rpmsg_header_s *header = data;
netdev_lower_carrier_off(&drv->dev);
if (drv->cb != NULL)
{
drv->cb(&drv->dev, NET_RPMSG_EVENT_CARRIER_OFF);
}
rpmsg_send_response(ept, header, sizeof(*header), 0);
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_SOCK_FAMILY;
int type = NET_SOCK_TYPE;
int protocol = NET_SOCK_PROTOCOL;
/* Restore pointers from argv */
ept = (FAR struct rpmsg_endpoint *)strtoul(argv[1], NULL, 16);
msg = (FAR struct net_rpmsg_ioctl_s *)strtoul(argv[2], NULL, 16);
/* We need a temporary sock for ioctl here */
if (msg->code == SIOCIFAUTOCONF)
{
domain = PF_INET6;
type = SOCK_DGRAM;
protocol = IPPROTO_ICMP6;
}
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_response(ept, &msg->header, sizeof(*msg) + msg->length,
msg->header.result);
}
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 */
snprintf(arg1, sizeof(arg1), "%p", ept);
snprintf(arg2, sizeof(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;
}
/****************************************************************************
* 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_rpmsg_transfer_s *transfer = data;
FAR struct net_rpmsg_drv_s *drv = priv;
FAR struct netdev_lowerhalf_s *dev = &drv->dev;
FAR netpkt_t *pkt;
irqstate_t flags;
int ret;
if (transfer->length > len - sizeof(*transfer))
{
nerr("ERROR: net_rpmsg got invalid transfer length!");
goto drop;
}
/* TODO: No-Copy, hold rx buffer */
pkt = netpkt_alloc(dev, NETPKT_RX);
if (pkt == NULL)
{
nerr("ERROR: Failed to allocate buffer!\n");
goto drop;
}
if (netpkt_copyin(dev, pkt, (FAR uint8_t *)(transfer + 1),
transfer->length, 0) < 0)
{
nerr("ERROR: Failed to copy in data!\n");
goto free;
}
flags = spin_lock_irqsave(&drv->lock);
ret = netpkt_tryadd_queue(pkt, &drv->rxqueue);
spin_unlock_irqrestore(&drv->lock, flags);
if (ret < 0)
{
nerr("ERROR: Failed to add pkt to queue!\n");
goto free;
}
netdev_lower_rxready(dev);
return 0;
free:
netpkt_free(dev, pkt, NETPKT_RX);
drop:
NETDEV_RXDROPPED(&dev->netdev);
return 0;
}
/****************************************************************************
* Name: net_rpmsg_drv_default_response
*
* Description:
* This function is used to handle the response from the RPMSG device.
* It is used to copy the response to the cookie and post the semaphore.
*
****************************************************************************/
static int net_rpmsg_drv_default_response(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;
}
/****************************************************************************
* Name: net_rpmsg_drv_ept_release
****************************************************************************/
static void net_rpmsg_drv_ept_release(FAR struct rpmsg_endpoint *ept)
{
FAR struct net_rpmsg_drv_s *drv = ept->priv;
netdev_lower_carrier_off(&drv->dev);
rpmsg_wait(&drv->ept, &drv->wait);
}
/****************************************************************************
* Name: net_rpmsg_drv_ns_bound
*
* Description:
* Rpmsg device end point service bound callback function , called when
* remote end point address is received.
*
* Parameters:
* ept - The rpmsg-device end point
*
* Returned Values:
* None
*
****************************************************************************/
static void net_rpmsg_drv_ns_bound(FAR struct rpmsg_endpoint *ept)
{
FAR struct net_rpmsg_drv_s *drv = ept->priv;
rpmsg_post(&drv->ept, &drv->wait);
}
/****************************************************************************
* Name: net_rpmsg_drv_device_created
****************************************************************************/
static void net_rpmsg_drv_device_created(FAR struct rpmsg_device *rdev,
FAR void *priv)
{
FAR struct net_rpmsg_drv_s *drv = priv;
char eptname[RPMSG_NAME_SIZE];
if (!strcmp(drv->cpuname, rpmsg_get_cpuname(rdev)))
{
drv->ept.priv = drv;
snprintf(eptname, sizeof(eptname),
NET_RPMSG_EPT_PREFIX "%s", drv->dev.netdev.d_ifname);
rpmsg_create_ept(&drv->ept, rdev, eptname,
RPMSG_ADDR_ANY, RPMSG_ADDR_ANY,
net_rpmsg_drv_ept_cb, NULL);
}
}
/****************************************************************************
* Name: net_rpmsg_drv_device_destroy
****************************************************************************/
static void net_rpmsg_drv_device_destroy(FAR struct rpmsg_device *rdev,
FAR void *priv)
{
FAR struct net_rpmsg_drv_s *drv = priv;
if (!strcmp(drv->cpuname, rpmsg_get_cpuname(rdev)))
{
rpmsg_destroy_ept(&drv->ept);
}
}
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 cmd = NET_RPMSG_GET_COMMAND(header->command);
if (cmd < nitems(g_net_rpmsg_drv_handler))
{
if (NET_RPMSG_IS_RESPONSE(header->command))
{
return g_net_rpmsg_drv_response[cmd](ept, data, len, src, priv);
}
else
{
return g_net_rpmsg_drv_handler[cmd](ept, data, len, src, priv);
}
}
return -EINVAL;
}
static int net_rpmsg_drv_send_recv(FAR struct netdev_lowerhalf_s *dev,
FAR void *header_, uint32_t command,
int len)
{
FAR struct net_rpmsg_drv_s *drv =
container_of(dev, struct net_rpmsg_drv_s, dev);
FAR struct net_rpmsg_header_s *header = header_;
FAR struct net_rpmsg_drv_cookie_s cookie;
int sval = 0;
int ret;
nxsem_get_value(&drv->wait, &sval);
if (sval <= 0)
{
rpmsg_wait(&drv->ept, &drv->wait);
rpmsg_post(&drv->ept, &drv->wait);
}
nxsem_init(&cookie.sem, 0, 0);
cookie.header = header;
header->command = command;
header->result = -ENXIO;
header->cookie = (uintptr_t)&cookie;
ret = rpmsg_send(&drv->ept, header, len);
if (ret < 0)
{
goto out;
}
net_sem_timedwait2(&cookie.sem, false, UINT_MAX, &dev->netdev.d_lock,
NULL);
ret = cookie.header->result;
out:
nxsem_destroy(&cookie.sem);
return ret;
}
/****************************************************************************
* 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 netdev_lowerhalf_s *dev)
{
FAR struct net_rpmsg_drv_s *drv =
container_of(dev, struct net_rpmsg_drv_s, dev);
struct net_rpmsg_ifup_s msg =
{
};
int ret;
#ifdef CONFIG_NET_IPv4
ninfo("Bringing up: %u.%u.%u.%u\n",
ip4_addr1(dev->netdev.d_ipaddr), ip4_addr2(dev->netdev.d_ipaddr),
ip4_addr3(dev->netdev.d_ipaddr), ip4_addr4(dev->netdev.d_ipaddr));
#endif
#ifdef CONFIG_NET_IPv6
ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
dev->netdev.d_ipv6addr[0], dev->netdev.d_ipv6addr[1],
dev->netdev.d_ipv6addr[2], dev->netdev.d_ipv6addr[3],
dev->netdev.d_ipv6addr[4], dev->netdev.d_ipv6addr[5],
dev->netdev.d_ipv6addr[6], dev->netdev.d_ipv6addr[7]);
#endif
netdev_lock(&dev->netdev);
/* Prepare the message */
msg.lnkaddr.length = netdev_lladdrsize(&dev->netdev);
memcpy(msg.lnkaddr.addr, &dev->netdev.d_mac, msg.lnkaddr.length);
#ifdef CONFIG_NET_IPv4
net_ipv4addr_copy(msg.ipaddr, dev->netdev.d_ipaddr);
net_ipv4addr_copy(msg.draddr, dev->netdev.d_draddr);
net_ipv4addr_copy(msg.netmask, dev->netdev.d_netmask);
#endif
#ifdef CONFIG_NET_IPv6
net_ipv6addr_copy(msg.ipv6addr, dev->netdev.d_ipv6addr);
net_ipv6addr_copy(msg.ipv6draddr, dev->netdev.d_ipv6draddr);
net_ipv6addr_copy(msg.ipv6netmask, dev->netdev.d_ipv6netmask);
#endif
/* Send the message */
ret = net_rpmsg_drv_send_recv(dev, &msg, NET_RPMSG_IFUP, sizeof(msg));
if (ret < 0)
{
netdev_unlock(&dev->netdev);
return ret;
}
/* Update net_driver_t field */
memcpy(&dev->netdev.d_mac, msg.lnkaddr.addr, msg.lnkaddr.length);
#ifdef CONFIG_NET_IPv4
net_ipv4addr_copy(dev->netdev.d_ipaddr, msg.ipaddr);
net_ipv4addr_copy(dev->netdev.d_draddr, msg.draddr);
net_ipv4addr_copy(dev->netdev.d_netmask, msg.netmask);
#endif
#ifdef CONFIG_NET_IPv6
net_ipv6addr_copy(dev->netdev.d_ipv6addr, msg.ipv6addr);
net_ipv6addr_copy(dev->netdev.d_ipv6draddr, msg.ipv6draddr);
net_ipv6addr_copy(dev->netdev.d_ipv6netmask, msg.ipv6netmask);
#endif
netdev_unlock(&dev->netdev);
#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
if (drv->cb != NULL)
{
drv->cb(dev, NET_RPMSG_EVENT_IF_UP);
}
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 netdev_lowerhalf_s *dev)
{
FAR struct net_rpmsg_drv_s *drv =
container_of(dev, struct net_rpmsg_drv_s, dev);
struct net_rpmsg_ifdown_s msg =
{
};
int ret;
ret = net_rpmsg_drv_send_recv(dev, &msg, NET_RPMSG_IFDOWN, sizeof(msg));
if (ret < 0)
{
return ret;
}
if (drv->cb != NULL)
{
drv->cb(dev, NET_RPMSG_EVENT_IF_DOWN);
}
return ret;
}
/****************************************************************************
* 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.
*
****************************************************************************/
#ifdef CONFIG_NET_MCASTGROUP
static int net_rpmsg_drv_addmac(FAR struct netdev_lowerhalf_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->netdev);
memcpy(msg.lnkaddr.addr, mac, msg.lnkaddr.length);
return net_rpmsg_drv_send_recv(dev, &msg, NET_RPMSG_ADDMCAST, sizeof(msg));
}
/****************************************************************************
* 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.
*
****************************************************************************/
static int net_rpmsg_drv_rmmac(FAR struct netdev_lowerhalf_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->netdev);
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_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 netdev_lowerhalf_s *dev, int cmd,
unsigned long arg)
{
ssize_t len;
int ret;
len = net_ioctl_arglen(PF_RPMSG, 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
/****************************************************************************
* Name: net_rpmsg_drv_alloc
****************************************************************************/
static FAR struct net_rpmsg_drv_s *
net_rpmsg_drv_alloc(FAR const char *devname, enum net_lltype_e lltype)
{
FAR struct net_rpmsg_drv_s *drv = kmm_zalloc(sizeof(*drv));
FAR struct netdev_lowerhalf_s *netdev;
if (!drv)
{
return NULL;
}
netdev = &drv->dev;
netdev->quota[NETPKT_RX] = CONFIG_IOB_NBUFFERS /
NET_RPMSG_DRV_MAX_NIOB / 4;
netdev->quota[NETPKT_TX] = 1;
netdev->ops = &g_net_rpmsg_drv_ops;
drv->ept.priv = drv;
drv->ept.release_cb = net_rpmsg_drv_ept_release;
drv->ept.ns_bound_cb = net_rpmsg_drv_ns_bound;
nxsem_init(&drv->wait, 0, 0);
spin_lock_init(&drv->lock);
/* Init a random MAC address, the caller can override it. */
arc4random_buf(&netdev->netdev.d_mac.ether.ether_addr_octet,
sizeof(netdev->netdev.d_mac.ether.ether_addr_octet));
strlcpy(netdev->netdev.d_ifname, devname, IFNAMSIZ);
netdev_lower_register(netdev, lltype);
return drv;
}
#ifdef CONFIG_NET_RPMSG_DRV_SERVER
/****************************************************************************
* Name: net_rpmsg_drv_ns_match
****************************************************************************/
static bool net_rpmsg_drv_ns_match(FAR struct rpmsg_device *rdev,
FAR void *priv, FAR const char *name,
uint32_t dest)
{
return !strncmp(name, NET_RPMSG_EPT_PREFIX, strlen(NET_RPMSG_EPT_PREFIX));
}
/****************************************************************************
* Name: net_rpmsg_drv_ns_bind
****************************************************************************/
static void net_rpmsg_drv_ns_bind(FAR struct rpmsg_device *rdev,
FAR void *priv_, FAR const char *name,
uint32_t dest)
{
FAR struct net_rpmsg_drv_s *drv;
FAR struct net_driver_s *dev;
const char *devname = name + strlen(NET_RPMSG_EPT_PREFIX);
dev = netdev_findbyname(devname);
if (dev)
{
drv = container_of(dev, struct net_rpmsg_drv_s, dev.netdev);
drv->ept.priv = drv;
drv->ept.release_cb = net_rpmsg_drv_ept_release;
drv->ept.ns_bound_cb = net_rpmsg_drv_ns_bound;
}
else
{
drv = net_rpmsg_drv_alloc(devname, NET_LL_ETHERNET);
if (!drv)
{
return;
}
}
rpmsg_create_ept(&drv->ept, rdev, name, RPMSG_ADDR_ANY, dest,
net_rpmsg_drv_ept_cb, rpmsg_destroy_ept);
rpmsg_post(&drv->ept, &drv->wait);
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: net_rpmsg_drv_init
*
* Description:
* Allocate a new network device instance for the RPMSG network and
* register it with the network device manager. This is the client side of
* the RPMSG driver. The RPMSG driver is the server side of the driver.
*
* Parameters:
* cpuname - Remote CPU name
* devname - Local and remote network device name
* lltype - Link layer type
*
* Returned Value:
* A pointer to the allocated network device instance. NULL is returned on
* failure.
*
****************************************************************************/
FAR struct netdev_lowerhalf_s *
net_rpmsg_drv_init(FAR const char *cpuname, FAR const char *devname,
enum net_lltype_e lltype)
{
FAR struct net_rpmsg_drv_s *drv;
FAR struct netdev_lowerhalf_s *dev;
int ret;
/* Allocate the interface structure */
if (!devname || !cpuname ||
!(drv = net_rpmsg_drv_alloc(devname, lltype)))
{
return NULL;
}
strlcpy(drv->cpuname, cpuname, RPMSG_NAME_SIZE);
dev = &drv->dev;
/* Register the device with the openamp */
ret = rpmsg_register_callback(drv,
net_rpmsg_drv_device_created,
net_rpmsg_drv_device_destroy,
NULL,
NULL);
if (ret < 0)
{
netdev_lower_unregister(dev);
nxsem_destroy(&drv->wait);
kmm_free(drv);
return NULL;
}
return dev;
}
/****************************************************************************
* Name: net_rpmsg_drv_priv
****************************************************************************/
FAR void *net_rpmsg_drv_priv(FAR struct netdev_lowerhalf_s *dev)
{
FAR struct net_rpmsg_drv_s *drv =
container_of(dev, struct net_rpmsg_drv_s, dev);
return drv->priv;
}
/****************************************************************************
* Name: net_rpmsg_drv_set_callback
****************************************************************************/
void net_rpmsg_drv_set_callback(FAR struct netdev_lowerhalf_s *dev,
net_rpmsg_drv_cb_t cb, FAR void *priv)
{
FAR struct net_rpmsg_drv_s *drv =
container_of(dev, struct net_rpmsg_drv_s, dev);
drv->cb = cb;
drv->priv = priv;
}
#ifdef CONFIG_NET_RPMSG_DRV_SERVER
/****************************************************************************
* Name: net_rpmsg_drv_server_init
****************************************************************************/
int net_rpmsg_drv_server_init(void)
{
return rpmsg_register_callback(NULL,
NULL,
NULL,
net_rpmsg_drv_ns_match,
net_rpmsg_drv_ns_bind);
}
#endif