incubator-nuttx/drivers/rpmsg/rpmsg_router_hub.c

478 lines
14 KiB
C

/****************************************************************************
* drivers/rpmsg/rpmsg_router_hub.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 <sys/param.h>
#include <nuttx/mutex.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.
*
****************************************************************************/
/****************************************************************************
* Private Types
****************************************************************************/
struct rpmsg_router_hub_s
{
struct rpmsg_endpoint ept[2];
char cpuname[2][RPMSG_ROUTER_CPUNAME_LEN];
mutex_t lock;
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: rpmsg_router_hub_cb
*
* Description:
* This is the callback function for router core.
* It will receive data from source edge core by ept(r:cpu:name), and find
* dest edge core communicating with it, send data to dest edge core.
*
* Parameters:
* ept - rpmsg_endpoint for communicating with edge core (r:dst_cpu:name)
* data - received data
* len - received data length
* src - source address
* priv - save dest edge core rpmsg_endpoint (r:src_cpu:name)
*
* Returned Values:
* Returns number of bytes it has sent or negative error value on failure.
*
****************************************************************************/
static int rpmsg_router_hub_cb(FAR struct rpmsg_endpoint *ept,
FAR void *data, size_t len,
uint32_t src, FAR void *priv)
{
FAR struct rpmsg_endpoint *dst_ept = priv;
/* Retransmit data to dest edge core */
if (!dst_ept)
{
return -EINVAL;
}
return rpmsg_send(dst_ept, data, len);
}
/****************************************************************************
* Name: rpmsg_router_hub_unbind
*
* Description:
* This is the unbind callback function for router core.
*
* Parameters:
* ept - rpmsg_endpoint for communicating with edge core (r:cpu:name)
*
****************************************************************************/
static void rpmsg_router_hub_unbind(FAR struct rpmsg_endpoint *ept)
{
FAR struct rpmsg_endpoint *dst_ept = ept->priv;
/* Destroy dest edge ept firstly */
if (dst_ept)
{
rpmsg_destroy_ept(dst_ept);
kmm_free(dst_ept);
}
/* Destroy source edge ept */
rpmsg_destroy_ept(ept);
kmm_free(ept);
}
/****************************************************************************
* Name: rpmsg_router_hub_bound
*
* Description:
* This is the bound callback function for router core.
* It will create endpoint to source edge after dest edge
* core is bound.
*
* Parameters:
* ept - rpmsg_endpoint for communicating with edge core (r:cpu:name)
*
****************************************************************************/
static void rpmsg_router_hub_bound(FAR struct rpmsg_endpoint *ept)
{
FAR struct rpmsg_endpoint *src_ept = ept->priv;
int ret;
/* Create endpoint (r:dst_cpu:name) and send ACK to source edge core */
ret = rpmsg_create_ept(src_ept, src_ept->rdev, src_ept->name,
RPMSG_ADDR_ANY, src_ept->dest_addr,
rpmsg_router_hub_cb, rpmsg_router_hub_unbind);
DEBUGASSERT(ret == RPMSG_SUCCESS);
}
/****************************************************************************
* Name: rpmsg_router_hub_match
*
* Description:
* This function is used to match the router core device.
* rpmsg_router_hub_bind will be called if the device is matched.
*
* Parameters:
* rdev - real rpmsg device
* priv - rpmsg router hub for router core
* name - endpoint name (r:dst_cpu:name)
* dest - destination address
*
* Returned Values:
* true on success; false on failure.
*
****************************************************************************/
static bool rpmsg_router_hub_match(FAR struct rpmsg_device *rdev,
FAR void *priv, FAR const char *name,
uint32_t dest)
{
FAR struct rpmsg_router_hub_s *hub = priv;
int i;
if (strncmp(name, RPMSG_ROUTER_NAME_PREFIX, RPMSG_ROUTER_NAME_PREFIX_LEN))
{
return false;
}
/* Must match both source edge CPU and dest edge CPU simultaneously */
for (i = 0; i < 2; i++)
{
if (strcmp(rpmsg_get_cpuname(rdev), hub->cpuname[i]))
{
continue;
}
if (strncmp(name + RPMSG_ROUTER_NAME_PREFIX_LEN,
hub->cpuname[1 - i], strlen(hub->cpuname[1 - i])))
{
continue;
}
break;
}
return i < 2;
}
/****************************************************************************
* Name: rpmsg_router_hub_bind
*
* Description:
* This function is used to bind the router core device.
* It will try to create endpoint (r:src_cpu:name) to another dest cpu.
* The source endpoint information will be saved in the private field of
* the dest endpoint.
*
* Parameters:
* rdev - real rpmsg device
* priv - rpmsg router hub for router core
* name - source edge core endpoint name (r:dst_cpu:name)
* dest - destination address
*
****************************************************************************/
static void rpmsg_router_hub_bind(FAR struct rpmsg_device *rdev,
FAR void *priv, FAR const char *name,
uint32_t dest)
{
FAR struct rpmsg_router_hub_s *hub = priv;
FAR struct rpmsg_endpoint *src_ept;
FAR struct rpmsg_endpoint *dst_ept;
FAR struct rpmsg_device *dst_rdev;
char dst_name[RPMSG_NAME_SIZE];
int ret;
int i;
nxmutex_lock(&hub->lock);
metal_mutex_acquire(&rdev->lock);
if (rpmsg_get_endpoint(rdev, name, RPMSG_ADDR_ANY, dest))
{
metal_mutex_release(&rdev->lock);
nxmutex_unlock(&hub->lock);
return;
}
metal_mutex_release(&rdev->lock);
/* Try to create endpoint name(r:src_cpu:name) of another dest cpu */
for (i = 0; i < 2; i++)
{
if (!strcmp(hub->cpuname[i], rpmsg_get_cpuname(rdev)))
{
break;
}
}
DEBUGASSERT(i < 2);
dst_rdev = hub->ept[1 - i].rdev;
snprintf(dst_name, RPMSG_NAME_SIZE,
RPMSG_ROUTER_NAME_PREFIX"%s%s", hub->cpuname[i],
name + RPMSG_ROUTER_NAME_PREFIX_LEN +
strlen(hub->cpuname[1 - i]));
src_ept = kmm_zalloc(sizeof(*src_ept));
dst_ept = kmm_zalloc(sizeof(*dst_ept));
DEBUGASSERT(src_ept && dst_ept);
/* Save information for the ept(r:dst_cpu:name) of the source cpu */
src_ept->priv = dst_ept;
src_ept->rdev = rdev;
src_ept->dest_addr = dest;
strlcpy(src_ept->name, name, sizeof(src_ept->name));
/* Create endpoint (r:src_cpu:name) to another dest cpu */
dst_ept->priv = src_ept;
dst_ept->ns_bound_cb = rpmsg_router_hub_bound;
ret = rpmsg_create_ept(dst_ept, dst_rdev, dst_name,
RPMSG_ADDR_ANY, RPMSG_ADDR_ANY,
rpmsg_router_hub_cb,
rpmsg_router_hub_unbind);
if (ret < 0)
{
kmm_free(dst_ept);
kmm_free(src_ept);
}
nxmutex_unlock(&hub->lock);
}
/****************************************************************************
* Name: rpmsg_router_bound
*
* Description:
* This function is used to send tx/rx buffer size
* when both cores are ready
*
* Parameters:
* ept - rpmsg endpoint for communicating with edge core
*
****************************************************************************/
static void rpmsg_router_bound(FAR struct rpmsg_endpoint *ept)
{
FAR struct rpmsg_router_hub_s *hub = ept->priv;
struct rpmsg_router_s msg;
int ret;
int i;
if (!is_rpmsg_ept_ready(&hub->ept[0]) ||
!is_rpmsg_ept_ready(&hub->ept[1]))
{
return;
}
for (i = 0; i < 2; i++)
{
msg.cmd = RPMSG_ROUTER_CREATE;
msg.tx_len = MIN(rpmsg_get_rx_buffer_size(&hub->ept[i]),
rpmsg_get_tx_buffer_size(&hub->ept[1 - i]));
msg.rx_len = MIN(rpmsg_get_tx_buffer_size(&hub->ept[i]),
rpmsg_get_rx_buffer_size(&hub->ept[1 - i]));
strlcpy(msg.cpuname, hub->cpuname[i], sizeof(msg.cpuname));
ret = rpmsg_send(&hub->ept[i], &msg, sizeof(msg));
DEBUGASSERT(ret >= 0);
}
}
/****************************************************************************
* Name: rpmsg_router_cb
****************************************************************************/
static int rpmsg_router_cb(FAR struct rpmsg_endpoint *ept, FAR void *data,
size_t len, uint32_t src, FAR void *priv)
{
return 0;
}
/****************************************************************************
* Name: rpmsg_router_created
*
* Description:
* This function is used to create endpoint to edge core,
* for synchronizing ready messages.
*
* Parameters:
* rdev - real rpmsg device
* priv - rpmsg router hub
*
****************************************************************************/
static void rpmsg_router_created(FAR struct rpmsg_device *rdev,
FAR void *priv)
{
FAR struct rpmsg_router_hub_s *hub = priv;
char name[RPMSG_NAME_SIZE];
int ret;
int i;
for (i = 0; i < 2; i++)
{
if (strcmp(rpmsg_get_cpuname(rdev), hub->cpuname[i]))
{
continue;
}
hub->ept[i].priv = hub;
hub->ept[i].ns_bound_cb = rpmsg_router_bound;
snprintf(name, RPMSG_NAME_SIZE, RPMSG_ROUTER_NAME"%s",
hub->cpuname[1 - i]);
ret = rpmsg_create_ept(&hub->ept[i], rdev, name,
RPMSG_ADDR_ANY, RPMSG_ADDR_ANY,
rpmsg_router_cb, NULL);
DEBUGASSERT(ret == RPMSG_SUCCESS);
break;
}
}
/****************************************************************************
* Name: rpmsg_router_destroy
*
* Description:
* This function is used to destroy the rpmsg router hub.
*
* Parameters:
* priv - rpmsg router hub
*
****************************************************************************/
static void rpmsg_router_destroy(FAR struct rpmsg_device *rdev,
FAR void *priv)
{
FAR struct rpmsg_router_hub_s *hub = priv;
struct rpmsg_router_s msg;
int i;
for (i = 0; i < 2; i++)
{
if (strcmp(rpmsg_get_cpuname(rdev), hub->cpuname[i]))
{
continue;
}
rpmsg_destroy_ept(&hub->ept[i]);
/* Notify the other edge core to destroy router device */
msg.cmd = RPMSG_ROUTER_DESTROY;
rpmsg_send(&hub->ept[1 - i], &msg, sizeof(msg));
break;
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: rpmsg_router_hub_init
*
* Description:
* This function is used to initialize the rpmsg router hub.
*
* Parameters:
* edge0 - edge cpu name
* edge1 - edge cpu name
*
* Returned Values:
* OK on success; A negated errno value is returned on any failure.
*
****************************************************************************/
int rpmsg_router_hub_init(FAR const char *edge0,
FAR const char *edge1)
{
FAR struct rpmsg_router_hub_s *hub;
int ret;
if (!edge0 || !edge1)
{
return -EINVAL;
}
hub = kmm_zalloc(sizeof(*hub));
if (!hub)
{
return -ENOMEM;
}
nxmutex_init(&hub->lock);
strlcpy(hub->cpuname[0], edge0, sizeof(hub->cpuname[0]));
strlcpy(hub->cpuname[1], edge1, sizeof(hub->cpuname[1]));
/* Register callback for retranmitting data between edge cores */
ret = rpmsg_register_callback(hub,
rpmsg_router_created,
rpmsg_router_destroy,
rpmsg_router_hub_match,
rpmsg_router_hub_bind);
if (ret < 0)
{
rpmsgerr("Register rpmsg callback failed: %d\n", ret);
nxmutex_destroy(&hub->lock);
kmm_free(hub);
return ret;
}
return 0;
}