建立工程,成功创建两个虚拟串口
This commit is contained in:
2
source/OpenAMP/open-amp/lib/rpmsg/CMakeLists.txt
Normal file
2
source/OpenAMP/open-amp/lib/rpmsg/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
collect (PROJECT_LIB_SOURCES rpmsg.c)
|
||||
collect (PROJECT_LIB_SOURCES rpmsg_virtio.c)
|
270
source/OpenAMP/open-amp/lib/rpmsg/rpmsg.c
Normal file
270
source/OpenAMP/open-amp/lib/rpmsg/rpmsg.c
Normal file
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Mentor Graphics Corporation
|
||||
* All rights reserved.
|
||||
* Copyright (c) 2016 Freescale Semiconductor, Inc. All rights reserved.
|
||||
* Copyright (c) 2018 Linaro, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <openamp/rpmsg.h>
|
||||
#include <metal/alloc.h>
|
||||
|
||||
#include "rpmsg_internal.h"
|
||||
|
||||
/**
|
||||
* rpmsg_get_address
|
||||
*
|
||||
* This function provides unique 32 bit address.
|
||||
*
|
||||
* @param bitmap - bit map for addresses
|
||||
* @param size - size of bitmap
|
||||
*
|
||||
* return - a unique address
|
||||
*/
|
||||
static uint32_t rpmsg_get_address(unsigned long *bitmap, int size)
|
||||
{
|
||||
unsigned int addr = RPMSG_ADDR_ANY;
|
||||
unsigned int nextbit;
|
||||
|
||||
nextbit = metal_bitmap_next_clear_bit(bitmap, 0, size);
|
||||
if (nextbit < (uint32_t)size) {
|
||||
addr = nextbit;
|
||||
metal_bitmap_set_bit(bitmap, nextbit);
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmsg_release_address
|
||||
*
|
||||
* Frees the given address.
|
||||
*
|
||||
* @param bitmap - bit map for addresses
|
||||
* @param size - size of bitmap
|
||||
* @param addr - address to free
|
||||
*/
|
||||
static void rpmsg_release_address(unsigned long *bitmap, int size,
|
||||
int addr)
|
||||
{
|
||||
if (addr < size)
|
||||
metal_bitmap_clear_bit(bitmap, addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmsg_is_address_set
|
||||
*
|
||||
* Checks whether address is used or free.
|
||||
*
|
||||
* @param bitmap - bit map for addresses
|
||||
* @param size - size of bitmap
|
||||
* @param addr - address to free
|
||||
*
|
||||
* return - TRUE/FALSE
|
||||
*/
|
||||
static int rpmsg_is_address_set(unsigned long *bitmap, int size, int addr)
|
||||
{
|
||||
if (addr < size)
|
||||
return metal_bitmap_is_bit_set(bitmap, addr);
|
||||
else
|
||||
return RPMSG_ERR_PARAM;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmsg_set_address
|
||||
*
|
||||
* Marks the address as consumed.
|
||||
*
|
||||
* @param bitmap - bit map for addresses
|
||||
* @param size - size of bitmap
|
||||
* @param addr - address to free
|
||||
*
|
||||
* return - none
|
||||
*/
|
||||
static int rpmsg_set_address(unsigned long *bitmap, int size, int addr)
|
||||
{
|
||||
if (addr < size) {
|
||||
metal_bitmap_set_bit(bitmap, addr);
|
||||
return RPMSG_SUCCESS;
|
||||
} else {
|
||||
return RPMSG_ERR_PARAM;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function sends rpmsg "message" to remote device.
|
||||
*
|
||||
* @param ept - pointer to end point
|
||||
* @param src - source address of channel
|
||||
* @param dst - destination address of channel
|
||||
* @param data - data to transmit
|
||||
* @param size - size of data
|
||||
* @param wait - boolean, wait or not for buffer to become
|
||||
* available
|
||||
*
|
||||
* @return - size of data sent or negative value for failure.
|
||||
*
|
||||
*/
|
||||
int rpmsg_send_offchannel_raw(struct rpmsg_endpoint *ept, uint32_t src,
|
||||
uint32_t dst, const void *data, int size,
|
||||
int wait)
|
||||
{
|
||||
struct rpmsg_device *rdev;
|
||||
|
||||
if (!ept || !ept->rdev || !data || dst == RPMSG_ADDR_ANY)
|
||||
return RPMSG_ERR_PARAM;
|
||||
|
||||
rdev = ept->rdev;
|
||||
|
||||
if (rdev->ops.send_offchannel_raw)
|
||||
return rdev->ops.send_offchannel_raw(rdev, src, dst, data,
|
||||
size, wait);
|
||||
|
||||
return RPMSG_ERR_PARAM;
|
||||
}
|
||||
|
||||
int rpmsg_send_ns_message(struct rpmsg_endpoint *ept, unsigned long flags)
|
||||
{
|
||||
struct rpmsg_ns_msg ns_msg;
|
||||
int ret;
|
||||
|
||||
ns_msg.flags = flags;
|
||||
ns_msg.addr = ept->addr;
|
||||
strncpy(ns_msg.name, ept->name, sizeof(ns_msg.name));
|
||||
ret = rpmsg_send_offchannel_raw(ept, ept->addr,
|
||||
RPMSG_NS_EPT_ADDR,
|
||||
&ns_msg, sizeof(ns_msg), true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else
|
||||
return RPMSG_SUCCESS;
|
||||
}
|
||||
|
||||
struct rpmsg_endpoint *rpmsg_get_endpoint(struct rpmsg_device *rdev,
|
||||
const char *name, uint32_t addr,
|
||||
uint32_t dest_addr)
|
||||
{
|
||||
struct metal_list *node;
|
||||
struct rpmsg_endpoint *ept;
|
||||
|
||||
metal_list_for_each(&rdev->endpoints, node) {
|
||||
int name_match = 0;
|
||||
|
||||
ept = metal_container_of(node, struct rpmsg_endpoint, node);
|
||||
/* try to get by local address only */
|
||||
if (addr != RPMSG_ADDR_ANY && ept->addr == addr)
|
||||
return ept;
|
||||
/* try to find match on local end remote address */
|
||||
if (addr == ept->addr && dest_addr == ept->dest_addr)
|
||||
return ept;
|
||||
/* else use name service and destination address */
|
||||
if (name)
|
||||
name_match = !strncmp(ept->name, name,
|
||||
sizeof(ept->name));
|
||||
if (!name || !name_match)
|
||||
continue;
|
||||
/* destination address is known, equal to ept remote address*/
|
||||
if (dest_addr != RPMSG_ADDR_ANY && ept->dest_addr == dest_addr)
|
||||
return ept;
|
||||
/* ept is registered but not associated to remote ept*/
|
||||
if (addr == RPMSG_ADDR_ANY && ept->dest_addr == RPMSG_ADDR_ANY)
|
||||
return ept;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void rpmsg_unregister_endpoint(struct rpmsg_endpoint *ept)
|
||||
{
|
||||
struct rpmsg_device *rdev;
|
||||
|
||||
if (!ept)
|
||||
return;
|
||||
|
||||
rdev = ept->rdev;
|
||||
|
||||
if (ept->addr != RPMSG_ADDR_ANY)
|
||||
rpmsg_release_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE,
|
||||
ept->addr);
|
||||
metal_list_del(&ept->node);
|
||||
}
|
||||
|
||||
int rpmsg_register_endpoint(struct rpmsg_device *rdev,
|
||||
struct rpmsg_endpoint *ept)
|
||||
{
|
||||
ept->rdev = rdev;
|
||||
|
||||
metal_list_add_tail(&rdev->endpoints, &ept->node);
|
||||
return RPMSG_SUCCESS;
|
||||
}
|
||||
|
||||
int rpmsg_create_ept(struct rpmsg_endpoint *ept, struct rpmsg_device *rdev,
|
||||
const char *name, uint32_t src, uint32_t dest,
|
||||
rpmsg_ept_cb cb, rpmsg_ns_unbind_cb unbind_cb)
|
||||
{
|
||||
int status;
|
||||
uint32_t addr = src;
|
||||
|
||||
if (!ept)
|
||||
return RPMSG_ERR_PARAM;
|
||||
|
||||
metal_mutex_acquire(&rdev->lock);
|
||||
if (src != RPMSG_ADDR_ANY) {
|
||||
status = rpmsg_is_address_set(rdev->bitmap,
|
||||
RPMSG_ADDR_BMP_SIZE, src);
|
||||
if (!status) {
|
||||
/* Mark the address as used in the address bitmap. */
|
||||
rpmsg_set_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE,
|
||||
src);
|
||||
} else if (status > 0) {
|
||||
status = RPMSG_SUCCESS;
|
||||
goto ret_status;
|
||||
} else {
|
||||
goto ret_status;
|
||||
}
|
||||
} else {
|
||||
addr = rpmsg_get_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE);
|
||||
}
|
||||
|
||||
rpmsg_init_ept(ept, name, addr, dest, cb, unbind_cb);
|
||||
|
||||
status = rpmsg_register_endpoint(rdev, ept);
|
||||
if (status < 0)
|
||||
rpmsg_release_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE, addr);
|
||||
|
||||
if (!status && ept->dest_addr == RPMSG_ADDR_ANY) {
|
||||
/* Send NS announcement to remote processor */
|
||||
metal_mutex_release(&rdev->lock);
|
||||
status = rpmsg_send_ns_message(ept, RPMSG_NS_CREATE);
|
||||
metal_mutex_acquire(&rdev->lock);
|
||||
if (status)
|
||||
rpmsg_unregister_endpoint(ept);
|
||||
}
|
||||
|
||||
ret_status:
|
||||
metal_mutex_release(&rdev->lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmsg_destroy_ept
|
||||
*
|
||||
* This function deletes rpmsg endpoint and performs cleanup.
|
||||
*
|
||||
* @param ept - pointer to endpoint to destroy
|
||||
*
|
||||
*/
|
||||
void rpmsg_destroy_ept(struct rpmsg_endpoint *ept)
|
||||
{
|
||||
struct rpmsg_device *rdev;
|
||||
|
||||
if (!ept)
|
||||
return;
|
||||
|
||||
rdev = ept->rdev;
|
||||
if (ept->addr != RPMSG_NS_EPT_ADDR)
|
||||
(void)rpmsg_send_ns_message(ept, RPMSG_NS_DESTROY);
|
||||
metal_mutex_acquire(&rdev->lock);
|
||||
rpmsg_unregister_endpoint(ept);
|
||||
metal_mutex_release(&rdev->lock);
|
||||
}
|
105
source/OpenAMP/open-amp/lib/rpmsg/rpmsg_internal.h
Normal file
105
source/OpenAMP/open-amp/lib/rpmsg/rpmsg_internal.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _RPMSG_INTERNAL_H_
|
||||
#define _RPMSG_INTERNAL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <openamp/rpmsg.h>
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef RPMSG_DEBUG
|
||||
#define RPMSG_ASSERT(_exp, _msg) do { \
|
||||
if (!(_exp)) { \
|
||||
openamp_print("FATAL: %s - _msg", __func__); \
|
||||
while (1) { \
|
||||
; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define RPMSG_ASSERT(_exp, _msg) do { \
|
||||
if (!(_exp)) \
|
||||
while (1) { \
|
||||
; \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define RPMSG_LOCATE_DATA(p) ((unsigned char *)(p) + sizeof(struct rpmsg_hdr))
|
||||
/**
|
||||
* enum rpmsg_ns_flags - dynamic name service announcement flags
|
||||
*
|
||||
* @RPMSG_NS_CREATE: a new remote service was just created
|
||||
* @RPMSG_NS_DESTROY: a known remote service was just destroyed
|
||||
* @RPMSG_NS_CREATE_WITH_ACK: a new remote service was just created waiting
|
||||
* acknowledgment.
|
||||
*/
|
||||
enum rpmsg_ns_flags {
|
||||
RPMSG_NS_CREATE = 0,
|
||||
RPMSG_NS_DESTROY = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rpmsg_hdr - common header for all rpmsg messages
|
||||
* @src: source address
|
||||
* @dst: destination address
|
||||
* @reserved: reserved for future use
|
||||
* @len: length of payload (in bytes)
|
||||
* @flags: message flags
|
||||
*
|
||||
* Every message sent(/received) on the rpmsg bus begins with this header.
|
||||
*/
|
||||
OPENAMP_PACKED_BEGIN
|
||||
struct rpmsg_hdr {
|
||||
uint32_t src;
|
||||
uint32_t dst;
|
||||
uint32_t reserved;
|
||||
uint16_t len;
|
||||
uint16_t flags;
|
||||
} OPENAMP_PACKED_END;
|
||||
|
||||
/**
|
||||
* struct rpmsg_ns_msg - dynamic name service announcement message
|
||||
* @name: name of remote service that is published
|
||||
* @addr: address of remote service that is published
|
||||
* @flags: indicates whether service is created or destroyed
|
||||
*
|
||||
* This message is sent across to publish a new service, or announce
|
||||
* about its removal. When we receive these messages, an appropriate
|
||||
* rpmsg channel (i.e device) is created/destroyed. In turn, the ->probe()
|
||||
* or ->remove() handler of the appropriate rpmsg driver will be invoked
|
||||
* (if/as-soon-as one is registered).
|
||||
*/
|
||||
OPENAMP_PACKED_BEGIN
|
||||
struct rpmsg_ns_msg {
|
||||
char name[RPMSG_NAME_SIZE];
|
||||
uint32_t addr;
|
||||
uint32_t flags;
|
||||
} OPENAMP_PACKED_END;
|
||||
|
||||
int rpmsg_send_ns_message(struct rpmsg_endpoint *ept, unsigned long flags);
|
||||
|
||||
struct rpmsg_endpoint *rpmsg_get_endpoint(struct rpmsg_device *rvdev,
|
||||
const char *name, uint32_t addr,
|
||||
uint32_t dest_addr);
|
||||
int rpmsg_register_endpoint(struct rpmsg_device *rdev,
|
||||
struct rpmsg_endpoint *ept);
|
||||
|
||||
static inline struct rpmsg_endpoint *
|
||||
rpmsg_get_ept_from_addr(struct rpmsg_device *rdev, uint32_t addr)
|
||||
{
|
||||
return rpmsg_get_endpoint(rdev, NULL, addr, RPMSG_ADDR_ANY);
|
||||
}
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _RPMSG_INTERNAL_H_ */
|
692
source/OpenAMP/open-amp/lib/rpmsg/rpmsg_virtio.c
Normal file
692
source/OpenAMP/open-amp/lib/rpmsg/rpmsg_virtio.c
Normal file
@@ -0,0 +1,692 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Mentor Graphics Corporation
|
||||
* All rights reserved.
|
||||
* Copyright (c) 2016 Freescale Semiconductor, Inc. All rights reserved.
|
||||
* Copyright (c) 2018 Linaro, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <metal/alloc.h>
|
||||
#include <metal/cache.h>
|
||||
#include <metal/sleep.h>
|
||||
#include <metal/utilities.h>
|
||||
#include <openamp/rpmsg_virtio.h>
|
||||
#include <openamp/virtqueue.h>
|
||||
|
||||
#include "rpmsg_internal.h"
|
||||
|
||||
#define RPMSG_NUM_VRINGS (2)
|
||||
|
||||
/* Total tick count for 15secs - 1msec tick. */
|
||||
#define RPMSG_TICK_COUNT 15000
|
||||
|
||||
/* Time to wait - In multiple of 10 msecs. */
|
||||
#define RPMSG_TICKS_PER_INTERVAL 10
|
||||
|
||||
#define WORD_SIZE sizeof(unsigned long)
|
||||
#define WORD_ALIGN(a) ((((a) & (WORD_SIZE - 1)) != 0) ? \
|
||||
(((a) & (~(WORD_SIZE - 1))) + WORD_SIZE) : (a))
|
||||
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
metal_weak void *
|
||||
rpmsg_virtio_shm_pool_get_buffer(struct rpmsg_virtio_shm_pool *shpool,
|
||||
size_t size)
|
||||
{
|
||||
void *buffer;
|
||||
|
||||
if (shpool->avail < size)
|
||||
return NULL;
|
||||
buffer = (void *)((char *)shpool->base + shpool->size - shpool->avail);
|
||||
shpool->avail -= size;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
#endif /*!VIRTIO_SLAVE_ONLY*/
|
||||
|
||||
void rpmsg_virtio_init_shm_pool(struct rpmsg_virtio_shm_pool *shpool,
|
||||
void *shb, size_t size)
|
||||
{
|
||||
if (!shpool)
|
||||
return;
|
||||
shpool->base = shb;
|
||||
shpool->size = WORD_ALIGN(size);
|
||||
shpool->avail = WORD_ALIGN(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmsg_virtio_return_buffer
|
||||
*
|
||||
* Places the used buffer back on the virtqueue.
|
||||
*
|
||||
* @param rvdev - pointer to remote core
|
||||
* @param buffer - buffer pointer
|
||||
* @param len - buffer length
|
||||
* @param idx - buffer index
|
||||
*
|
||||
*/
|
||||
static void rpmsg_virtio_return_buffer(struct rpmsg_virtio_device *rvdev,
|
||||
void *buffer, unsigned long len,
|
||||
unsigned short idx)
|
||||
{
|
||||
unsigned int role = rpmsg_virtio_get_role(rvdev);
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
if (role == RPMSG_MASTER) {
|
||||
struct virtqueue_buf vqbuf;
|
||||
|
||||
(void)idx;
|
||||
/* Initialize buffer node */
|
||||
vqbuf.buf = buffer;
|
||||
vqbuf.len = len;
|
||||
virtqueue_add_buffer(rvdev->rvq, &vqbuf, 0, 1, buffer);
|
||||
}
|
||||
#endif /*VIRTIO_SLAVE_ONLY*/
|
||||
|
||||
#ifndef VIRTIO_MASTER_ONLY
|
||||
if (role == RPMSG_REMOTE) {
|
||||
(void)buffer;
|
||||
virtqueue_add_consumed_buffer(rvdev->rvq, idx, len);
|
||||
}
|
||||
#endif /*VIRTIO_MASTER_ONLY*/
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmsg_virtio_enqueue_buffer
|
||||
*
|
||||
* Places buffer on the virtqueue for consumption by the other side.
|
||||
*
|
||||
* @param rvdev - pointer to rpmsg virtio
|
||||
* @param buffer - buffer pointer
|
||||
* @param len - buffer length
|
||||
* @param idx - buffer index
|
||||
*
|
||||
* @return - status of function execution
|
||||
*/
|
||||
static int rpmsg_virtio_enqueue_buffer(struct rpmsg_virtio_device *rvdev,
|
||||
void *buffer, unsigned long len,
|
||||
unsigned short idx)
|
||||
{
|
||||
unsigned int role = rpmsg_virtio_get_role(rvdev);
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
if (role == RPMSG_MASTER) {
|
||||
struct virtqueue_buf vqbuf;
|
||||
(void)idx;
|
||||
|
||||
/* Initialize buffer node */
|
||||
vqbuf.buf = buffer;
|
||||
vqbuf.len = len;
|
||||
return virtqueue_add_buffer(rvdev->svq, &vqbuf, 0, 1, buffer);
|
||||
}
|
||||
#endif /*!VIRTIO_SLAVE_ONLY*/
|
||||
|
||||
#ifndef VIRTIO_MASTER_ONLY
|
||||
if (role == RPMSG_REMOTE) {
|
||||
(void)buffer;
|
||||
return virtqueue_add_consumed_buffer(rvdev->svq, idx, len);
|
||||
}
|
||||
#endif /*!VIRTIO_MASTER_ONLY*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmsg_virtio_get_tx_buffer
|
||||
*
|
||||
* Provides buffer to transmit messages.
|
||||
*
|
||||
* @param rvdev - pointer to rpmsg device
|
||||
* @param len - length of returned buffer
|
||||
* @param idx - buffer index
|
||||
*
|
||||
* return - pointer to buffer.
|
||||
*/
|
||||
static void *rpmsg_virtio_get_tx_buffer(struct rpmsg_virtio_device *rvdev,
|
||||
unsigned long *len,
|
||||
unsigned short *idx)
|
||||
{
|
||||
unsigned int role = rpmsg_virtio_get_role(rvdev);
|
||||
void *data = NULL;
|
||||
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
if (role == RPMSG_MASTER) {
|
||||
data = virtqueue_get_buffer(rvdev->svq, (uint32_t *)len, idx);
|
||||
if (data == NULL) {
|
||||
data = rpmsg_virtio_shm_pool_get_buffer(rvdev->shpool,
|
||||
RPMSG_BUFFER_SIZE);
|
||||
*len = RPMSG_BUFFER_SIZE;
|
||||
}
|
||||
}
|
||||
#endif /*!VIRTIO_SLAVE_ONLY*/
|
||||
|
||||
#ifndef VIRTIO_MASTER_ONLY
|
||||
if (role == RPMSG_REMOTE) {
|
||||
data = virtqueue_get_available_buffer(rvdev->svq, idx,
|
||||
(uint32_t *)len);
|
||||
}
|
||||
#endif /*!VIRTIO_MASTER_ONLY*/
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmsg_virtio_get_rx_buffer
|
||||
*
|
||||
* Retrieves the received buffer from the virtqueue.
|
||||
*
|
||||
* @param rvdev - pointer to rpmsg device
|
||||
* @param len - size of received buffer
|
||||
* @param idx - index of buffer
|
||||
*
|
||||
* @return - pointer to received buffer
|
||||
*
|
||||
*/
|
||||
static void *rpmsg_virtio_get_rx_buffer(struct rpmsg_virtio_device *rvdev,
|
||||
unsigned long *len,
|
||||
unsigned short *idx)
|
||||
{
|
||||
unsigned int role = rpmsg_virtio_get_role(rvdev);
|
||||
void *data = NULL;
|
||||
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
if (role == RPMSG_MASTER) {
|
||||
data = virtqueue_get_buffer(rvdev->rvq, (uint32_t *)len, idx);
|
||||
}
|
||||
#endif /*!VIRTIO_SLAVE_ONLY*/
|
||||
|
||||
#ifndef VIRTIO_MASTER_ONLY
|
||||
if (role == RPMSG_REMOTE) {
|
||||
data =
|
||||
virtqueue_get_available_buffer(rvdev->rvq, idx,
|
||||
(uint32_t *)len);
|
||||
}
|
||||
#endif /*!VIRTIO_MASTER_ONLY*/
|
||||
|
||||
if (data) {
|
||||
/* FIX ME: library should not worry about if it needs
|
||||
* to flush/invalidate cache, it is shared memory.
|
||||
* The shared memory should be mapped properly before
|
||||
* using it.
|
||||
*/
|
||||
metal_cache_invalidate(data, (unsigned int)(*len));
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
#ifndef VIRTIO_MASTER_ONLY
|
||||
/**
|
||||
* check if the remote is ready to start RPMsg communication
|
||||
*/
|
||||
static int rpmsg_virtio_wait_remote_ready(struct rpmsg_virtio_device *rvdev)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
while (1) {
|
||||
status = rpmsg_virtio_get_status(rvdev);
|
||||
/* Busy wait until the remote is ready */
|
||||
if (status & VIRTIO_CONFIG_STATUS_NEEDS_RESET) {
|
||||
rpmsg_virtio_set_status(rvdev, 0);
|
||||
/* TODO notify remote processor */
|
||||
} else if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK) {
|
||||
return true;
|
||||
}
|
||||
/* TODO: clarify metal_cpu_yield usage*/
|
||||
metal_cpu_yield();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif /*!VIRTIO_MASTER_ONLY*/
|
||||
|
||||
/**
|
||||
* _rpmsg_virtio_get_buffer_size
|
||||
*
|
||||
* Returns buffer size available for sending messages.
|
||||
*
|
||||
* @param channel - pointer to rpmsg channel
|
||||
*
|
||||
* @return - buffer size
|
||||
*
|
||||
*/
|
||||
static int _rpmsg_virtio_get_buffer_size(struct rpmsg_virtio_device *rvdev)
|
||||
{
|
||||
unsigned int role = rpmsg_virtio_get_role(rvdev);
|
||||
int length = 0;
|
||||
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
if (role == RPMSG_MASTER) {
|
||||
/*
|
||||
* If device role is Remote then buffers are provided by us
|
||||
* (RPMSG Master), so just provide the macro.
|
||||
*/
|
||||
length = RPMSG_BUFFER_SIZE - sizeof(struct rpmsg_hdr);
|
||||
}
|
||||
#endif /*!VIRTIO_SLAVE_ONLY*/
|
||||
|
||||
#ifndef VIRTIO_MASTER_ONLY
|
||||
if (role == RPMSG_REMOTE) {
|
||||
/*
|
||||
* If other core is Master then buffers are provided by it,
|
||||
* so get the buffer size from the virtqueue.
|
||||
*/
|
||||
length =
|
||||
(int)virtqueue_get_desc_size(rvdev->svq) -
|
||||
sizeof(struct rpmsg_hdr);
|
||||
}
|
||||
#endif /*!VIRTIO_MASTER_ONLY*/
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function sends rpmsg "message" to remote device.
|
||||
*
|
||||
* @param rdev - pointer to rpmsg device
|
||||
* @param src - source address of channel
|
||||
* @param dst - destination address of channel
|
||||
* @param data - data to transmit
|
||||
* @param size - size of data
|
||||
* @param wait - boolean, wait or not for buffer to become
|
||||
* available
|
||||
*
|
||||
* @return - size of data sent or negative value for failure.
|
||||
*
|
||||
*/
|
||||
static int rpmsg_virtio_send_offchannel_raw(struct rpmsg_device *rdev,
|
||||
uint32_t src, uint32_t dst,
|
||||
const void *data,
|
||||
int size, int wait)
|
||||
{
|
||||
struct rpmsg_virtio_device *rvdev;
|
||||
struct rpmsg_hdr rp_hdr;
|
||||
void *buffer = NULL;
|
||||
unsigned short idx;
|
||||
int tick_count = 0;
|
||||
unsigned long buff_len;
|
||||
int status;
|
||||
struct metal_io_region *io;
|
||||
|
||||
/* Get the associated remote device for channel. */
|
||||
rvdev = metal_container_of(rdev, struct rpmsg_virtio_device, rdev);
|
||||
|
||||
status = rpmsg_virtio_get_status(rvdev);
|
||||
/* Validate device state */
|
||||
if (!(status & VIRTIO_CONFIG_STATUS_DRIVER_OK)) {
|
||||
return RPMSG_ERR_DEV_STATE;
|
||||
}
|
||||
|
||||
if (wait)
|
||||
tick_count = RPMSG_TICK_COUNT / RPMSG_TICKS_PER_INTERVAL;
|
||||
else
|
||||
tick_count = 0;
|
||||
|
||||
while (1) {
|
||||
int avail_size;
|
||||
|
||||
/* Lock the device to enable exclusive access to virtqueues */
|
||||
metal_mutex_acquire(&rdev->lock);
|
||||
avail_size = _rpmsg_virtio_get_buffer_size(rvdev);
|
||||
if (size <= avail_size)
|
||||
buffer = rpmsg_virtio_get_tx_buffer(rvdev, &buff_len,
|
||||
&idx);
|
||||
metal_mutex_release(&rdev->lock);
|
||||
if (buffer || !tick_count)
|
||||
break;
|
||||
if (avail_size != 0)
|
||||
return RPMSG_ERR_BUFF_SIZE;
|
||||
metal_sleep_usec(RPMSG_TICKS_PER_INTERVAL);
|
||||
tick_count--;
|
||||
}
|
||||
if (!buffer)
|
||||
return RPMSG_ERR_NO_BUFF;
|
||||
|
||||
/* Initialize RPMSG header. */
|
||||
rp_hdr.dst = dst;
|
||||
rp_hdr.src = src;
|
||||
rp_hdr.len = size;
|
||||
rp_hdr.reserved = 0;
|
||||
|
||||
/* Copy data to rpmsg buffer. */
|
||||
io = rvdev->shbuf_io;
|
||||
status = metal_io_block_write(io, metal_io_virt_to_offset(io, buffer),
|
||||
&rp_hdr, sizeof(rp_hdr));
|
||||
RPMSG_ASSERT(status == sizeof(rp_hdr), "failed to write header\n");
|
||||
|
||||
status = metal_io_block_write(io,
|
||||
metal_io_virt_to_offset(io,
|
||||
RPMSG_LOCATE_DATA(buffer)),
|
||||
data, size);
|
||||
RPMSG_ASSERT(status == size, "failed to write buffer\n");
|
||||
metal_mutex_acquire(&rdev->lock);
|
||||
|
||||
/* Enqueue buffer on virtqueue. */
|
||||
status = rpmsg_virtio_enqueue_buffer(rvdev, buffer, buff_len, idx);
|
||||
RPMSG_ASSERT(status == VQUEUE_SUCCESS, "failed to enqueue buffer\n");
|
||||
/* Let the other side know that there is a job to process. */
|
||||
virtqueue_kick(rvdev->svq);
|
||||
|
||||
metal_mutex_release(&rdev->lock);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmsg_virtio_tx_callback
|
||||
*
|
||||
* Tx callback function.
|
||||
*
|
||||
* @param vq - pointer to virtqueue on which Tx is has been
|
||||
* completed.
|
||||
*
|
||||
*/
|
||||
static void rpmsg_virtio_tx_callback(struct virtqueue *vq)
|
||||
{
|
||||
(void)vq;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmsg_virtio_rx_callback
|
||||
*
|
||||
* Rx callback function.
|
||||
*
|
||||
* @param vq - pointer to virtqueue on which messages is received
|
||||
*
|
||||
*/
|
||||
static void rpmsg_virtio_rx_callback(struct virtqueue *vq)
|
||||
{
|
||||
struct virtio_device *vdev = vq->vq_dev;
|
||||
struct rpmsg_virtio_device *rvdev = vdev->priv;
|
||||
struct rpmsg_device *rdev = &rvdev->rdev;
|
||||
struct rpmsg_endpoint *ept;
|
||||
struct rpmsg_hdr *rp_hdr;
|
||||
unsigned long len;
|
||||
unsigned short idx;
|
||||
int status;
|
||||
|
||||
metal_mutex_acquire(&rdev->lock);
|
||||
|
||||
/* Process the received data from remote node */
|
||||
rp_hdr = (struct rpmsg_hdr *)rpmsg_virtio_get_rx_buffer(rvdev,
|
||||
&len, &idx);
|
||||
|
||||
metal_mutex_release(&rdev->lock);
|
||||
|
||||
while (rp_hdr) {
|
||||
/* Get the channel node from the remote device channels list. */
|
||||
metal_mutex_acquire(&rdev->lock);
|
||||
ept = rpmsg_get_ept_from_addr(rdev, rp_hdr->dst);
|
||||
metal_mutex_release(&rdev->lock);
|
||||
|
||||
if (!ept)
|
||||
/* Fatal error no endpoint for the given dst addr. */
|
||||
return;
|
||||
|
||||
if (ept->dest_addr == RPMSG_ADDR_ANY) {
|
||||
/*
|
||||
* First message received from the remote side,
|
||||
* update channel destination address
|
||||
*/
|
||||
ept->dest_addr = rp_hdr->src;
|
||||
}
|
||||
status = ept->cb(ept, (void *)RPMSG_LOCATE_DATA(rp_hdr),
|
||||
rp_hdr->len, ept->addr, ept->priv);
|
||||
|
||||
RPMSG_ASSERT(status == RPMSG_SUCCESS,
|
||||
"unexpected callback status\n");
|
||||
metal_mutex_acquire(&rdev->lock);
|
||||
|
||||
/* Return used buffers. */
|
||||
rpmsg_virtio_return_buffer(rvdev, rp_hdr, len, idx);
|
||||
|
||||
rp_hdr = (struct rpmsg_hdr *)
|
||||
rpmsg_virtio_get_rx_buffer(rvdev, &len, &idx);
|
||||
metal_mutex_release(&rdev->lock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmsg_virtio_ns_callback
|
||||
*
|
||||
* This callback handles name service announcement from the remote device
|
||||
* and creates/deletes rpmsg channels.
|
||||
*
|
||||
* @param server_chnl - pointer to server channel control block.
|
||||
* @param data - pointer to received messages
|
||||
* @param len - length of received data
|
||||
* @param priv - any private data
|
||||
* @param src - source address
|
||||
*
|
||||
* @return - rpmag endpoint callback handled
|
||||
*/
|
||||
|
||||
#if defined (__GNUC__) && ! defined (__CC_ARM)
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("O0")
|
||||
#elif defined (__CC_ARM)
|
||||
#pragma push
|
||||
#pragma O0
|
||||
#endif
|
||||
static int rpmsg_virtio_ns_callback(struct rpmsg_endpoint *ept, void *data,
|
||||
size_t len, uint32_t src, void *priv)
|
||||
{
|
||||
struct rpmsg_device *rdev = ept->rdev;
|
||||
struct rpmsg_virtio_device *rvdev = (struct rpmsg_virtio_device *)rdev;
|
||||
struct metal_io_region *io = rvdev->shbuf_io;
|
||||
struct rpmsg_endpoint *_ept;
|
||||
struct rpmsg_ns_msg *ns_msg;
|
||||
uint32_t dest;
|
||||
char name[RPMSG_NAME_SIZE];
|
||||
|
||||
(void)priv;
|
||||
(void)src;
|
||||
|
||||
ns_msg = (struct rpmsg_ns_msg *)data;
|
||||
if (len != sizeof(*ns_msg))
|
||||
/* Returns as the message is corrupted */
|
||||
return RPMSG_SUCCESS;
|
||||
metal_io_block_read(io,
|
||||
metal_io_virt_to_offset(io, ns_msg->name),
|
||||
&name, sizeof(name));
|
||||
dest = ns_msg->addr;
|
||||
|
||||
/* check if a Ept has been locally registered */
|
||||
metal_mutex_acquire(&rdev->lock);
|
||||
_ept = rpmsg_get_endpoint(rdev, name, RPMSG_ADDR_ANY, dest);
|
||||
|
||||
if (ns_msg->flags & RPMSG_NS_DESTROY) {
|
||||
if (_ept)
|
||||
_ept->dest_addr = RPMSG_ADDR_ANY;
|
||||
metal_mutex_release(&rdev->lock);
|
||||
if (_ept && _ept->ns_unbind_cb)
|
||||
_ept->ns_unbind_cb(ept);
|
||||
} else {
|
||||
if (!_ept) {
|
||||
/*
|
||||
* send callback to application, that can
|
||||
* - create the associated endpoints.
|
||||
* - store information for future use.
|
||||
* - just ignore the request as service not supported.
|
||||
*/
|
||||
metal_mutex_release(&rdev->lock);
|
||||
if (rdev->ns_bind_cb)
|
||||
rdev->ns_bind_cb(rdev, name, dest);
|
||||
} else {
|
||||
_ept->dest_addr = dest;
|
||||
metal_mutex_release(&rdev->lock);
|
||||
}
|
||||
}
|
||||
|
||||
return RPMSG_SUCCESS;
|
||||
}
|
||||
#if defined (__GNUC__) && ! defined (__CC_ARM)
|
||||
#pragma GCC pop_options
|
||||
#elif defined (__CC_ARM)
|
||||
#pragma pop
|
||||
#endif
|
||||
|
||||
int rpmsg_virtio_get_buffer_size(struct rpmsg_device *rdev)
|
||||
{
|
||||
int size;
|
||||
struct rpmsg_virtio_device *rvdev;
|
||||
|
||||
if (!rdev)
|
||||
return RPMSG_ERR_PARAM;
|
||||
metal_mutex_acquire(&rdev->lock);
|
||||
rvdev = (struct rpmsg_virtio_device *)rdev;
|
||||
size = _rpmsg_virtio_get_buffer_size(rvdev);
|
||||
metal_mutex_release(&rdev->lock);
|
||||
return size;
|
||||
}
|
||||
|
||||
int rpmsg_init_vdev(struct rpmsg_virtio_device *rvdev,
|
||||
struct virtio_device *vdev,
|
||||
rpmsg_ns_bind_cb ns_bind_cb,
|
||||
struct metal_io_region *shm_io,
|
||||
struct rpmsg_virtio_shm_pool *shpool)
|
||||
{
|
||||
struct rpmsg_device *rdev;
|
||||
const char *vq_names[RPMSG_NUM_VRINGS];
|
||||
typedef void (*vqcallback)(struct virtqueue *vq);
|
||||
vqcallback callback[RPMSG_NUM_VRINGS];
|
||||
unsigned long dev_features;
|
||||
int status;
|
||||
unsigned int i, role;
|
||||
|
||||
rdev = &rvdev->rdev;
|
||||
memset(rdev, 0, sizeof(*rdev));
|
||||
metal_mutex_init(&rdev->lock);
|
||||
rvdev->vdev = vdev;
|
||||
rdev->ns_bind_cb = ns_bind_cb;
|
||||
vdev->priv = rvdev;
|
||||
rdev->ops.send_offchannel_raw = rpmsg_virtio_send_offchannel_raw;
|
||||
role = rpmsg_virtio_get_role(rvdev);
|
||||
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
if (role == RPMSG_MASTER) {
|
||||
/*
|
||||
* Since device is RPMSG Remote so we need to manage the
|
||||
* shared buffers. Create shared memory pool to handle buffers.
|
||||
*/
|
||||
if (!shpool)
|
||||
return RPMSG_ERR_PARAM;
|
||||
if (!shpool->size)
|
||||
return RPMSG_ERR_NO_BUFF;
|
||||
rvdev->shpool = shpool;
|
||||
|
||||
vq_names[0] = "rx_vq";
|
||||
vq_names[1] = "tx_vq";
|
||||
callback[0] = rpmsg_virtio_rx_callback;
|
||||
callback[1] = rpmsg_virtio_tx_callback;
|
||||
rvdev->rvq = vdev->vrings_info[0].vq;
|
||||
rvdev->svq = vdev->vrings_info[1].vq;
|
||||
}
|
||||
#endif /*!VIRTIO_SLAVE_ONLY*/
|
||||
|
||||
#ifndef VIRTIO_MASTER_ONLY
|
||||
(void)shpool;
|
||||
if (role == RPMSG_REMOTE) {
|
||||
vq_names[0] = "tx_vq";
|
||||
vq_names[1] = "rx_vq";
|
||||
callback[0] = rpmsg_virtio_tx_callback;
|
||||
callback[1] = rpmsg_virtio_rx_callback;
|
||||
rvdev->rvq = vdev->vrings_info[1].vq;
|
||||
rvdev->svq = vdev->vrings_info[0].vq;
|
||||
}
|
||||
#endif /*!VIRTIO_MASTER_ONLY*/
|
||||
rvdev->shbuf_io = shm_io;
|
||||
|
||||
#ifndef VIRTIO_MASTER_ONLY
|
||||
if (role == RPMSG_REMOTE) {
|
||||
/* wait synchro with the master */
|
||||
rpmsg_virtio_wait_remote_ready(rvdev);
|
||||
}
|
||||
#endif /*!VIRTIO_MASTER_ONLY*/
|
||||
|
||||
/* Create virtqueues for remote device */
|
||||
status = rpmsg_virtio_create_virtqueues(rvdev, 0, RPMSG_NUM_VRINGS,
|
||||
vq_names, callback);
|
||||
if (status != RPMSG_SUCCESS)
|
||||
return status;
|
||||
|
||||
/* TODO: can have a virtio function to set the shared memory I/O */
|
||||
for (i = 0; i < RPMSG_NUM_VRINGS; i++) {
|
||||
struct virtqueue *vq;
|
||||
|
||||
vq = vdev->vrings_info[i].vq;
|
||||
vq->shm_io = shm_io;
|
||||
}
|
||||
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
if (role == RPMSG_MASTER) {
|
||||
struct virtqueue_buf vqbuf;
|
||||
unsigned int idx;
|
||||
void *buffer;
|
||||
|
||||
vqbuf.len = RPMSG_BUFFER_SIZE;
|
||||
for (idx = 0; idx < rvdev->rvq->vq_nentries; idx++) {
|
||||
/* Initialize TX virtqueue buffers for remote device */
|
||||
buffer = rpmsg_virtio_shm_pool_get_buffer(shpool,
|
||||
RPMSG_BUFFER_SIZE);
|
||||
|
||||
if (!buffer) {
|
||||
return RPMSG_ERR_NO_BUFF;
|
||||
}
|
||||
|
||||
vqbuf.buf = buffer;
|
||||
|
||||
metal_io_block_set(shm_io,
|
||||
metal_io_virt_to_offset(shm_io,
|
||||
buffer),
|
||||
0x00, RPMSG_BUFFER_SIZE);
|
||||
status =
|
||||
virtqueue_add_buffer(rvdev->rvq, &vqbuf, 0, 1,
|
||||
buffer);
|
||||
|
||||
if (status != RPMSG_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /*!VIRTIO_SLAVE_ONLY*/
|
||||
|
||||
/* Initialize channels and endpoints list */
|
||||
metal_list_init(&rdev->endpoints);
|
||||
|
||||
dev_features = rpmsg_virtio_get_features(rvdev);
|
||||
|
||||
/*
|
||||
* Create name service announcement endpoint if device supports name
|
||||
* service announcement feature.
|
||||
*/
|
||||
if ((dev_features & (1 << VIRTIO_RPMSG_F_NS))) {
|
||||
rpmsg_init_ept(&rdev->ns_ept, "NS",
|
||||
RPMSG_NS_EPT_ADDR, RPMSG_NS_EPT_ADDR,
|
||||
rpmsg_virtio_ns_callback, NULL);
|
||||
(void)rpmsg_register_endpoint(rdev, &rdev->ns_ept);
|
||||
}
|
||||
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
if (role == RPMSG_MASTER)
|
||||
rpmsg_virtio_set_status(rvdev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
|
||||
#endif /*!VIRTIO_SLAVE_ONLY*/
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void rpmsg_deinit_vdev(struct rpmsg_virtio_device *rvdev)
|
||||
{
|
||||
struct metal_list *node;
|
||||
struct rpmsg_device *rdev;
|
||||
struct rpmsg_endpoint *ept;
|
||||
|
||||
rdev = &rvdev->rdev;
|
||||
while (!metal_list_is_empty(&rdev->endpoints)) {
|
||||
node = rdev->endpoints.next;
|
||||
ept = metal_container_of(node, struct rpmsg_endpoint, node);
|
||||
rpmsg_destroy_ept(ept);
|
||||
}
|
||||
|
||||
rvdev->rvq = 0;
|
||||
rvdev->svq = 0;
|
||||
|
||||
metal_mutex_deinit(&rdev->lock);
|
||||
}
|
Reference in New Issue
Block a user