| /**************************************************************************** |
| * 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 |