incubator-nuttx/drivers/rpmsg/rpmsg_router_edge.c

811 lines
24 KiB
C

/****************************************************************************
* drivers/rpmsg/rpmsg_router_edge.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <debug.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <nuttx/kmalloc.h>
#include <rpmsg/rpmsg_internal.h>
#include "rpmsg_router.h"
/****************************************************************************
* Rpmsg-router Model:
*
* +------+ +------+ +------+
* | edge |<----->| hub |<----->| edge |
* +------+ +------+ +------+
*
* Description:
* edge CPUs (edge) are physically linked to the central router cpu (hub),
* edge CPUs' communication reply on hub cpu message forwarding.
*
****************************************************************************/
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define rpmsg_router_edge_from_rdev(d) \
metal_container_of(d, struct rpmsg_router_edge_s, rdev)
#define RPMSG_ROUTER_USER_NAME_SIZE \
(RPMSG_NAME_SIZE - RPMSG_ROUTER_NAME_PREFIX_LEN - RPMSG_ROUTER_CPUNAME_LEN)
/****************************************************************************
* Private Types
****************************************************************************/
struct rpmsg_router_edge_s
{
struct rpmsg_s rpmsg;
struct rpmsg_device rdev;
struct rpmsg_device *hubdev;
char name[RPMSG_NAME_SIZE];
char localcpu[RPMSG_ROUTER_CPUNAME_LEN];
char remotecpu[RPMSG_ROUTER_CPUNAME_LEN];
/* Tx/Rx buffer size */
uint32_t tx_len;
uint32_t rx_len;
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static FAR const char *
rpmsg_router_edge_get_local_cpuname(FAR struct rpmsg_s *rpmsg);
static FAR const char *
rpmsg_router_edge_get_cpuname(FAR struct rpmsg_s *rpmsg);
static int rpmsg_router_edge_get_tx_buffer_size(FAR struct rpmsg_s *rpmsg);
static int rpmsg_router_edge_get_rx_buffer_size(FAR struct rpmsg_s *rpmsg);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct rpmsg_ops_s g_rpmsg_router_edge_ops =
{
NULL,
NULL,
NULL,
NULL,
NULL,
rpmsg_router_edge_get_local_cpuname,
rpmsg_router_edge_get_cpuname,
rpmsg_router_edge_get_tx_buffer_size,
rpmsg_router_edge_get_rx_buffer_size,
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: rpmsg_router_edge_get_local_cpuname
****************************************************************************/
static FAR const char *
rpmsg_router_edge_get_local_cpuname(FAR struct rpmsg_s *rpmsg)
{
FAR struct rpmsg_router_edge_s *edge =
(FAR struct rpmsg_router_edge_s *)rpmsg;
return edge->localcpu;
}
/****************************************************************************
* Name: rpmsg_router_edge_get_cpuname
****************************************************************************/
static FAR const char *
rpmsg_router_edge_get_cpuname(FAR struct rpmsg_s *rpmsg)
{
FAR struct rpmsg_router_edge_s *edge =
(FAR struct rpmsg_router_edge_s *)rpmsg;
return edge->remotecpu;
}
/****************************************************************************
* Name: rpmsg_router_edge_get_tx_buffer_size
****************************************************************************/
static int rpmsg_router_edge_get_tx_buffer_size(FAR struct rpmsg_s *rpmsg)
{
FAR struct rpmsg_router_edge_s *edge =
(FAR struct rpmsg_router_edge_s *)rpmsg;
return edge->tx_len;
}
/****************************************************************************
* Name: rpmsg_router_edge_get_rx_buffer_size
****************************************************************************/
static int rpmsg_router_edge_get_rx_buffer_size(FAR struct rpmsg_s *rpmsg)
{
FAR struct rpmsg_router_edge_s *edge =
(FAR struct rpmsg_router_edge_s *)rpmsg;
return edge->rx_len;
}
/****************************************************************************
* Name: rpmsg_router_edge_get_tx_payload_buffer
****************************************************************************/
static FAR void *
rpmsg_router_edge_get_tx_payload_buffer(FAR struct rpmsg_device *rdev,
FAR uint32_t *len, int wait)
{
FAR struct rpmsg_router_edge_s *edge = rpmsg_router_edge_from_rdev(rdev);
FAR struct rpmsg_device *hubdev = edge->hubdev;
FAR void *buf;
if (!hubdev->ops.get_tx_payload_buffer)
{
return NULL;
}
buf = hubdev->ops.get_tx_payload_buffer(hubdev, len, wait);
*len = edge->tx_len;
return buf;
}
/****************************************************************************
* Name: rpmsg_router_edge_hold_rx_buffer
****************************************************************************/
static void rpmsg_router_edge_hold_rx_buffer(FAR struct rpmsg_device *rdev,
FAR void *rxbuf)
{
FAR struct rpmsg_router_edge_s *edge = rpmsg_router_edge_from_rdev(rdev);
FAR struct rpmsg_device *hubdev = edge->hubdev;
if (!hubdev->ops.hold_rx_buffer)
{
return;
}
hubdev->ops.hold_rx_buffer(hubdev, rxbuf);
}
/****************************************************************************
* Name: rpmsg_router_edge_release_rx_buffer
****************************************************************************/
static void
rpmsg_router_edge_release_rx_buffer(FAR struct rpmsg_device *rdev,
FAR void *rxbuf)
{
struct rpmsg_router_edge_s *edge = rpmsg_router_edge_from_rdev(rdev);
struct rpmsg_device *hubdev = edge->hubdev;
if (!hubdev->ops.release_rx_buffer)
{
return;
}
hubdev->ops.release_rx_buffer(hubdev, rxbuf);
}
/****************************************************************************
* Name: rpmsg_router_edge_release_tx_buffer
****************************************************************************/
static int rpmsg_router_edge_release_tx_buffer(FAR struct rpmsg_device *rdev,
FAR void *txbuf)
{
struct rpmsg_router_edge_s *edge = rpmsg_router_edge_from_rdev(rdev);
struct rpmsg_device *hubdev = edge->hubdev;
if (!hubdev->ops.release_tx_buffer)
{
return RPMSG_ERR_PERM;
}
return hubdev->ops.release_tx_buffer(hubdev, txbuf);
}
/****************************************************************************
* Name: rpmsg_router_edge_send_nocopy
****************************************************************************/
static int rpmsg_router_edge_send_nocopy(FAR struct rpmsg_device *rdev,
uint32_t src, uint32_t dst,
FAR const void *data, int len)
{
struct rpmsg_router_edge_s *edge = rpmsg_router_edge_from_rdev(rdev);
struct rpmsg_device *hubdev = edge->hubdev;
if (!hubdev->ops.send_offchannel_nocopy)
{
return RPMSG_ERR_PARAM;
}
return hubdev->ops.send_offchannel_nocopy(hubdev, src, dst, data, len);
}
/****************************************************************************
* Name: rpmsg_router_edge_cb
*
* Description:
* This is the callback function for edge core.
* It will receive data from real rpmsg channel by ept(r:cpu:name),
* and find the corresponding user ept, then processing data through
* the user ept callback.
*
* Parameters:
* ept - rpmsg_endpoint for communicating with router core (r:cpu:name)
* data - received data
* len - received data length
* src - source address
* priv - save user rpmsg_endpoint generally
*
* Returned Values:
* 0 on success; A negated errno value is returned on any failure.
*
****************************************************************************/
static int rpmsg_router_edge_cb(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv)
{
FAR struct rpmsg_endpoint *usr_ept = priv;
if (!usr_ept)
{
return 0;
}
/* Processing data through the user ept callback */
return usr_ept->cb(usr_ept, data, len, src, usr_ept->priv);
}
/****************************************************************************
* Name: rpmsg_router_edge_bound
*
* Description:
* This is the callback function for edge core.
* It will be called when the edge core is bound to the router core
* by r:cpu:name, save the destination address of the edge core, and
* call the bound function of the user endpoint.
*
* Parameters:
* ept - rpmsg_endpoint for communicating with router core (r:cpu:name)
*
****************************************************************************/
static void rpmsg_router_edge_bound(FAR struct rpmsg_endpoint *ept)
{
FAR struct rpmsg_endpoint *usr_ept = ept->priv;
if (!usr_ept)
{
rpmsgerr("Try to get user ept failed.\n");
return;
}
usr_ept->dest_addr = ept->dest_addr;
if (usr_ept->ns_bound_cb)
{
usr_ept->ns_bound_cb(usr_ept);
}
}
/****************************************************************************
* Name: rpmsg_router_edge_unbind
*
* Description:
* This is the unbind callback function for edge core.
*
* Parameters:
* ept - rpmsg_endpoint for communicating with router core (r:cpu:name)
*
****************************************************************************/
static void rpmsg_router_edge_unbind(FAR struct rpmsg_endpoint *ept)
{
FAR struct rpmsg_endpoint *usr_ept = ept->priv;
if (!usr_ept)
{
rpmsgerr("Try to get user ept failed.\n");
return;
}
if (usr_ept->ns_unbind_cb)
{
usr_ept->ns_unbind_cb(usr_ept);
}
}
/****************************************************************************
* Name: rpmsg_router_edge_release
****************************************************************************/
static void rpmsg_router_edge_release(FAR struct rpmsg_endpoint *ept)
{
kmm_free(ept);
}
/****************************************************************************
* Name: rpmsg_router_edge_send_offchannel_raw
*
* Description:
* This function sends normal rpmsg message or ns message to remote device.
* If the destination address is RPMSG_NS_EPT_ADDR, it will create a new
* endpoint(r:cpu:name) for real communication, and save the user endpoint
* information in the private field of the new endpoint.
*
* Parameters:
* rdev - rpmsg_device for router core
* src - source address
* dst - destination address
* data - data to send
* len - data length
* wait - boolean, wait or not for buffer to become available
*
* Returned Values:
* size of data sent or negative value for failure.
*
****************************************************************************/
static int
rpmsg_router_edge_send_offchannel_raw(FAR struct rpmsg_device *rdev,
uint32_t src, uint32_t dst,
FAR const void *data,
int len, int wait)
{
FAR struct rpmsg_router_edge_s *edge = rpmsg_router_edge_from_rdev(rdev);
FAR struct rpmsg_ns_msg *ns_msg = (FAR struct rpmsg_ns_msg *)data;
FAR struct rpmsg_device *hubdev = edge->hubdev;
FAR struct rpmsg_endpoint *usr_ept;
FAR struct rpmsg_endpoint *ept;
char name[RPMSG_ROUTER_USER_NAME_SIZE];
int ret;
/* Send normal rpmsg "message" to remote device */
if (dst != RPMSG_NS_EPT_ADDR)
{
if (!hubdev->ops.send_offchannel_raw)
{
return RPMSG_ERR_PARAM;
}
return hubdev->ops.send_offchannel_raw(hubdev, src,
dst, data, len, wait);
}
/* Try to get user ept firstly */
metal_mutex_acquire(&rdev->lock);
usr_ept = rpmsg_get_endpoint(rdev, ns_msg->name, src, dst);
metal_mutex_release(&rdev->lock);
if (!usr_ept)
{
rpmsgerr("Try to get user ept failed.\n");
return RPMSG_ERR_PARAM;
}
/* Set hub endpoint name(r:cpu:name) for real communication */
strlcpy(name, ns_msg->name, sizeof(name));
snprintf(ns_msg->name, sizeof(ns_msg->name),
RPMSG_ROUTER_NAME_PREFIX"%s:%s", edge->remotecpu, name);
if (ns_msg->flags == RPMSG_NS_DESTROY)
{
/* Processing RPMSG_NS_DESTROY message */
metal_mutex_acquire(&hubdev->lock);
ept = rpmsg_get_endpoint(hubdev, ns_msg->name,
RPMSG_ADDR_ANY, usr_ept->dest_addr);
metal_mutex_release(&hubdev->lock);
if (!ept)
{
rpmsgerr("Try to get router endpoint (r:ept) failed.\n");
return RPMSG_ERR_PARAM;
}
/* Destroy endpoint(r:cpu:name) of real communication */
rpmsg_destroy_ept(ept);
return 0;
}
else
{
/* Processing RPMSG_NS_CREATE or RPMSG_NS_CREATE_ACK message */
ept = kmm_zalloc(sizeof(*ept));
if (!ept)
{
return -ENOMEM;
}
/* Save user endpoint */
ept->priv = usr_ept;
ept->ns_bound_cb = rpmsg_router_edge_bound;
ept->release_cb = rpmsg_router_edge_release;
/* Create endpoint (r:cpu:name) for real communication */
ret = rpmsg_create_ept(ept, hubdev, ns_msg->name,
RPMSG_ADDR_ANY, RPMSG_ADDR_ANY,
rpmsg_router_edge_cb,
rpmsg_router_edge_unbind);
if (ret < 0)
{
rpmsgerr("Create router endpoint failed: %d\n", ret);
kmm_free(ept);
}
return ret;
}
}
/****************************************************************************
* Name: rpmsg_router_edge_match
*
* Description:
* This function is used to match the edge core device.
* rpmsg_router_edge_bind will be called if the device is matched.
*
* Parameters:
* rdev - real rpmsg device
* priv - rpmsg router device for edge core
* name - endpoint name (r:cpu:name)
* dest - destination address
*
* Returned Values:
* true on success; false on failure.
*
****************************************************************************/
static bool rpmsg_router_edge_match(FAR struct rpmsg_device *rdev,
FAR void *priv, FAR const char *name,
uint32_t dest)
{
FAR struct rpmsg_router_edge_s *edge = priv;
if (strncmp(name, RPMSG_ROUTER_NAME_PREFIX, RPMSG_ROUTER_NAME_PREFIX_LEN))
{
return false;
}
return !strncmp(name + RPMSG_ROUTER_NAME_PREFIX_LEN, edge->remotecpu,
strlen(edge->remotecpu));
}
/****************************************************************************
* Name: rpmsg_router_edge_bind
*
* Description:
* This function is used to bind the edge core device.
* It will try to find rpmsg_user_ns_bind_cb by user ept name.
*
* Parameters:
* rdev - real rpmsg device
* priv - rpmsg router device for edge core
* name - endpoint name (r:cpu:name)
* dest - destination address
*
****************************************************************************/
static void rpmsg_router_edge_bind(FAR struct rpmsg_device *rdev,
FAR void *priv, FAR const char *name,
uint32_t dest)
{
FAR struct rpmsg_router_edge_s *edge = priv;
FAR struct rpmsg_device *edgedev = &edge->rdev;
edgedev->ns_bind_cb(edgedev,
name + RPMSG_ROUTER_NAME_PREFIX_LEN +
strlen(edge->remotecpu) + 1, dest);
}
/****************************************************************************
* Name: rpmsg_router_edge_destroy
*
* Description:
* This function is used to destroy the edge core device.
*
* Parameters:
* edge - rpmsg router edge device
*
****************************************************************************/
static void rpmsg_router_edge_destroy(FAR struct rpmsg_router_edge_s *edge)
{
rpmsg_unregister_callback(edge, NULL, NULL,
rpmsg_router_edge_match,
rpmsg_router_edge_bind);
rpmsg_unregister(edge->name, &edge->rpmsg);
rpmsg_device_destory(&edge->rpmsg);
kmm_free(edge);
}
/****************************************************************************
* Name: rpmsg_router_edge_create
*
* Description:
* This function is used to create the edge core device.
*
* Parameters:
* hubdev - rpmsg device for router hub
* msg - sync message from router hub
* remotecpu - remote edge cpu name
*
* Returned Values:
* edge device on success; NULL on failure.
*
****************************************************************************/
static FAR struct rpmsg_router_edge_s *
rpmsg_router_edge_create(FAR struct rpmsg_device *hubdev,
FAR struct rpmsg_router_s *msg,
FAR const char *remotecpu)
{
FAR struct rpmsg_router_edge_s *edge;
FAR struct rpmsg_device *rdev;
int ret;
/* Create the router edge device */
edge = kmm_zalloc(sizeof(*edge));
if (!edge)
{
return NULL;
}
strlcpy(edge->remotecpu, remotecpu, sizeof(edge->remotecpu));
strlcpy(edge->localcpu, msg->cpuname, sizeof(edge->localcpu));
edge->rx_len = msg->rx_len;
edge->tx_len = msg->tx_len;
edge->hubdev = hubdev;
/* Initialize router rpmsg device */
rdev = &edge->rdev;
metal_mutex_init(&rdev->lock);
rdev->ns_bind_cb = rpmsg_ns_bind;
rdev->ns_unbind_cb = rpmsg_ns_unbind;
rdev->ops.hold_rx_buffer = rpmsg_router_edge_hold_rx_buffer;
rdev->ops.release_rx_buffer = rpmsg_router_edge_release_rx_buffer;
rdev->ops.release_tx_buffer = rpmsg_router_edge_release_tx_buffer;
rdev->ops.send_offchannel_nocopy = rpmsg_router_edge_send_nocopy;
rdev->ops.send_offchannel_raw = rpmsg_router_edge_send_offchannel_raw;
rdev->ops.get_tx_payload_buffer = rpmsg_router_edge_get_tx_payload_buffer;
metal_list_init(&rdev->endpoints);
rdev->support_ack = true;
rdev->support_ns = true;
/* Register rpmsg for edge core */
snprintf(edge->name, sizeof(edge->name), "/dev/rpmsg/%s", edge->remotecpu);
ret = rpmsg_register(edge->name, &edge->rpmsg, &g_rpmsg_router_edge_ops);
if (ret < 0)
{
rpmsgerr("rpmsg_register failed: %d\n", ret);
goto free;
}
/* Register callback for edge core */
ret = rpmsg_register_callback(edge, NULL, NULL,
rpmsg_router_edge_match,
rpmsg_router_edge_bind);
if (ret < 0)
{
rpmsgerr("Register rpmsg callback failed: %d\n", ret);
goto unregister;
}
/* Broadcast device_created to all registers */
rpmsg_device_created(&edge->rpmsg);
return edge;
unregister:
rpmsg_unregister(edge->name, &edge->rpmsg);
free:
kmm_free(edge);
return NULL;
}
/****************************************************************************
* Name: rpmsg_router_cb
*
* Description:
* This function is used to receive sync message from router core,
* and create or destroy the edge core device.
*
* Parameters:
* ept - endpoint for synchronizing ready messages
* data - received data
* len - received data length
* src - source address
* priv - private data
*
* Returned Values:
* 0 on success; A negated errno value is returned on any failure.
*
****************************************************************************/
static int rpmsg_router_cb(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv)
{
FAR struct rpmsg_router_s *msg = data;
FAR struct rpmsg_router_edge_s *edge;
/* Destroy the router edge device */
if (msg->cmd == RPMSG_ROUTER_DESTROY)
{
edge = ept->priv;
if (edge)
{
rpmsg_router_edge_destroy(edge);
ept->priv = NULL;
return 0;
}
return -EINVAL;
}
/* Create the router edge device */
edge = rpmsg_router_edge_create(ept->rdev, msg,
ept->name + RPMSG_ROUTER_NAME_LEN);
if (!edge)
{
return -ENODEV;
}
ept->priv = edge;
return 0;
}
/****************************************************************************
* Name: rpmsg_router_unbind
*
* Description:
* This function is used to destroy the sync endpoint
* when another edge core is disconnected.
*
* Parameters:
* ept - rpmsg endpoint for synchronizing message.
*
****************************************************************************/
static void rpmsg_router_unbind(FAR struct rpmsg_endpoint *ept)
{
struct rpmsg_router_edge_s *edge = ept->priv;
if (edge)
{
rpmsg_router_edge_destroy(edge);
ept->priv = NULL;
}
rpmsg_destroy_ept(ept);
kmm_free(ept);
}
/****************************************************************************
* Name: rpmsg_router_match
*
* Description:
* This function is used to match the endpoint for
* synchronizing ready messages.
*
* Parameters:
* rdev - real rpmsg device
* priv - rpmsg_router_priv
* name - endpoint name
* dest - destination address
*
* Returned Values:
* true on success; false on failure.
*
****************************************************************************/
static bool rpmsg_router_match(FAR struct rpmsg_device *rdev, FAR void *priv,
FAR const char *name, uint32_t dest)
{
return !strncmp(name, RPMSG_ROUTER_NAME, RPMSG_ROUTER_NAME_LEN);
}
/****************************************************************************
* Name: rpmsg_router_bind
*
* Description:
* This function is used to bind the endpoint for
* synchronizing ready messages.
*
* Parameters:
* rdev - real rpmsg device
* priv - private data
* name - endpoint name
* dest - destination address
*
****************************************************************************/
static void rpmsg_router_bind(FAR struct rpmsg_device *rdev, FAR void *priv,
FAR const char *name, uint32_t dest)
{
FAR struct rpmsg_endpoint *ept;
int ret;
ept = kmm_zalloc(sizeof(*ept));
DEBUGASSERT(ept);
ret = rpmsg_create_ept(ept, rdev, name, RPMSG_ADDR_ANY, dest,
rpmsg_router_cb, rpmsg_router_unbind);
if (ret < 0)
{
rpmsgerr("Create router endpoint failed: %d\n", ret);
kmm_free(ept);
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: rpmsg_router_edge_init
*
* Description:
* This function is used to initialize the edge core.
*
* Returned Values:
* OK on success; A negated errno value is returned on any failure.
*
****************************************************************************/
int rpmsg_router_edge_init(void)
{
/* Register callback for listening sync message from router hub */
return rpmsg_register_callback(NULL, NULL, NULL,
rpmsg_router_match,
rpmsg_router_bind);
}