/**************************************************************************** * 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 #include #include #include #include #include #include #include #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); }