added mx Project for the machine
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
Software License Agreement (BSD License)
|
||||
========================================
|
||||
|
||||
Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of Xilinx nor the names of its contributors may be used
|
||||
to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
Notes
|
||||
=========================================
|
||||
Use the following tag instead of the full license text in the individual files:
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
This enables machine processing of license information based on the SPDX
|
||||
License Identifiers that are here available: http://spdx.org/licenses/
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <metal/assert.h>
|
||||
#include <metal/device.h>
|
||||
#include <metal/errno.h>
|
||||
#include <metal/list.h>
|
||||
#include <metal/log.h>
|
||||
#include <metal/sys.h>
|
||||
#include <metal/utilities.h>
|
||||
#include <metal/dma.h>
|
||||
#include <metal/cache.h>
|
||||
|
||||
int metal_bus_register(struct metal_bus *bus)
|
||||
{
|
||||
if (!bus || !bus->name || !strlen(bus->name))
|
||||
return -EINVAL;
|
||||
if (metal_bus_find(bus->name, NULL) == 0)
|
||||
return -EEXIST;
|
||||
metal_list_init(&bus->devices);
|
||||
metal_list_add_tail(&_metal.common.bus_list, &bus->node);
|
||||
metal_log(METAL_LOG_DEBUG, "registered %s bus\n", bus->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int metal_bus_unregister(struct metal_bus *bus)
|
||||
{
|
||||
metal_list_del(&bus->node);
|
||||
if (bus->ops.bus_close)
|
||||
bus->ops.bus_close(bus);
|
||||
metal_log(METAL_LOG_DEBUG, "unregistered %s bus\n", bus->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int metal_bus_find(const char *name, struct metal_bus **result)
|
||||
{
|
||||
struct metal_list *node;
|
||||
struct metal_bus *bus;
|
||||
|
||||
metal_list_for_each(&_metal.common.bus_list, node) {
|
||||
bus = metal_container_of(node, struct metal_bus, node);
|
||||
if (strcmp(bus->name, name) == 0 && result) {
|
||||
*result = bus;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int metal_device_open(const char *bus_name, const char *dev_name,
|
||||
struct metal_device **device)
|
||||
{
|
||||
struct metal_bus *bus;
|
||||
int error;
|
||||
|
||||
if (!bus_name || !strlen(bus_name) ||
|
||||
!dev_name || !strlen(dev_name) ||
|
||||
!device)
|
||||
return -EINVAL;
|
||||
|
||||
error = metal_bus_find(bus_name, &bus);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (!bus->ops.dev_open)
|
||||
return -ENODEV;
|
||||
|
||||
error = (*bus->ops.dev_open)(bus, dev_name, device);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void metal_device_close(struct metal_device *device)
|
||||
{
|
||||
metal_assert(device && device->bus);
|
||||
if (device->bus->ops.dev_close)
|
||||
device->bus->ops.dev_close(device->bus, device);
|
||||
}
|
||||
|
||||
int metal_register_generic_device(struct metal_device *device)
|
||||
{
|
||||
if (!device->name || !strlen(device->name) ||
|
||||
device->num_regions > METAL_MAX_DEVICE_REGIONS)
|
||||
return -EINVAL;
|
||||
|
||||
device->bus = &metal_generic_bus;
|
||||
metal_list_add_tail(&_metal.common.generic_device_list,
|
||||
&device->node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int metal_generic_dev_open(struct metal_bus *bus, const char *dev_name,
|
||||
struct metal_device **device)
|
||||
{
|
||||
struct metal_list *node;
|
||||
struct metal_device *dev;
|
||||
|
||||
(void)bus;
|
||||
|
||||
metal_list_for_each(&_metal.common.generic_device_list, node) {
|
||||
dev = metal_container_of(node, struct metal_device, node);
|
||||
if (strcmp(dev->name, dev_name) == 0) {
|
||||
*device = dev;
|
||||
return metal_generic_dev_sys_open(dev);
|
||||
}
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int metal_generic_dev_dma_map(struct metal_bus *bus,
|
||||
struct metal_device *device,
|
||||
uint32_t dir,
|
||||
struct metal_sg *sg_in,
|
||||
int nents_in,
|
||||
struct metal_sg *sg_out)
|
||||
{
|
||||
int i;
|
||||
(void)bus;
|
||||
(void)device;
|
||||
|
||||
if (sg_out != sg_in)
|
||||
memcpy(sg_out, sg_in, nents_in*(sizeof(struct metal_sg)));
|
||||
for (i = 0; i < nents_in; i++) {
|
||||
if (dir == METAL_DMA_DEV_W) {
|
||||
metal_cache_flush(sg_out[i].virt, sg_out[i].len);
|
||||
}
|
||||
metal_cache_invalidate(sg_out[i].virt, sg_out[i].len);
|
||||
}
|
||||
|
||||
return nents_in;
|
||||
}
|
||||
|
||||
void metal_generic_dev_dma_unmap(struct metal_bus *bus,
|
||||
struct metal_device *device,
|
||||
uint32_t dir,
|
||||
struct metal_sg *sg,
|
||||
int nents)
|
||||
{
|
||||
int i;
|
||||
(void)bus;
|
||||
(void)device;
|
||||
(void)dir;
|
||||
|
||||
for (i = 0; i < nents; i++) {
|
||||
metal_cache_invalidate(sg[i].virt, sg[i].len);
|
||||
}
|
||||
}
|
||||
|
||||
struct metal_bus metal_weak metal_generic_bus = {
|
||||
.name = "generic",
|
||||
.ops = {
|
||||
.bus_close = NULL,
|
||||
.dev_open = metal_generic_dev_open,
|
||||
.dev_close = NULL,
|
||||
.dev_irq_ack = NULL,
|
||||
.dev_dma_map = metal_generic_dev_dma_map,
|
||||
.dev_dma_unmap = metal_generic_dev_dma_unmap,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file alloc.h
|
||||
* @brief Memory allocation handling primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_ALLOC__H__
|
||||
#define __METAL_ALLOC__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup Memory Allocation Interfaces
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief allocate requested memory size
|
||||
* return a pointer to the allocated memory
|
||||
*
|
||||
* @param[in] size size in byte of requested memory
|
||||
* @return memory pointer, or 0 if it failed to allocate
|
||||
*/
|
||||
static inline void *metal_allocate_memory(unsigned int size);
|
||||
|
||||
/**
|
||||
* @brief free the memory previously allocated
|
||||
*
|
||||
* @param[in] ptr pointer to memory
|
||||
*/
|
||||
static inline void metal_free_memory(void *ptr);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef METAL_FREERTOS
|
||||
#include <metal/system/freertos/alloc.h>
|
||||
#else
|
||||
#include <metal/system/generic/alloc.h>
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_ALLOC__H__ */
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file assert.h
|
||||
* @brief Assertion support.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_ASSERT__H__
|
||||
#define __METAL_ASSERT__H__
|
||||
|
||||
#ifdef METAL_FREERTOS
|
||||
#include <metal/system/freertos/assert.h>
|
||||
#else
|
||||
#include <metal/system/generic/assert.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Assertion macro.
|
||||
* @param cond Condition to test.
|
||||
*/
|
||||
#define metal_assert(cond) metal_sys_assert(cond)
|
||||
|
||||
#endif /* __METAL_ASSERT_H__ */
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file atomic.h
|
||||
* @brief Atomic primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_ATOMIC__H__
|
||||
#define __METAL_ATOMIC__H__
|
||||
|
||||
#include <metal/config.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
# include <cstdint>
|
||||
|
||||
/*
|
||||
* <atomic> has the same functionality as <stdatomic.h> but all members are only
|
||||
* accessible in the std namespace. As the rest of libmetal is pure C, it does
|
||||
* not know about namespaces, even when compiled as part of a C++ file. So we
|
||||
* just export the members of <atomic> into the global namespace.
|
||||
*/
|
||||
# include <atomic>
|
||||
using std::atomic_flag;
|
||||
using std::memory_order;
|
||||
|
||||
using std::atomic_bool;
|
||||
using std::atomic_char;
|
||||
using std::atomic_schar;
|
||||
using std::atomic_uchar;
|
||||
using std::atomic_short;
|
||||
using std::atomic_ushort;
|
||||
using std::atomic_int;
|
||||
using std::atomic_uint;
|
||||
using std::atomic_long;
|
||||
using std::atomic_ulong;
|
||||
using std::atomic_llong;
|
||||
using std::atomic_ullong;
|
||||
using std::atomic_char16_t;
|
||||
using std::atomic_char32_t;
|
||||
using std::atomic_wchar_t;
|
||||
using std::atomic_int_least8_t;
|
||||
using std::atomic_uint_least8_t;
|
||||
using std::atomic_int_least16_t;
|
||||
using std::atomic_uint_least16_t;
|
||||
using std::atomic_int_least32_t;
|
||||
using std::atomic_uint_least32_t;
|
||||
using std::atomic_int_least64_t;
|
||||
using std::atomic_uint_least64_t;
|
||||
using std::atomic_int_fast8_t;
|
||||
using std::atomic_uint_fast8_t;
|
||||
using std::atomic_int_fast16_t;
|
||||
using std::atomic_uint_fast16_t;
|
||||
using std::atomic_int_fast32_t;
|
||||
using std::atomic_uint_fast32_t;
|
||||
using std::atomic_int_fast64_t;
|
||||
using std::atomic_uint_fast64_t;
|
||||
using std::atomic_intptr_t;
|
||||
using std::atomic_uintptr_t;
|
||||
using std::atomic_size_t;
|
||||
using std::atomic_ptrdiff_t;
|
||||
using std::atomic_intmax_t;
|
||||
using std::atomic_uintmax_t;
|
||||
|
||||
using std::atomic_flag_test_and_set;
|
||||
using std::atomic_flag_test_and_set_explicit;
|
||||
using std::atomic_flag_clear;
|
||||
using std::atomic_flag_clear_explicit;
|
||||
using std::atomic_init;
|
||||
using std::atomic_is_lock_free;
|
||||
using std::atomic_store;
|
||||
using std::atomic_store_explicit;
|
||||
using std::atomic_load;
|
||||
using std::atomic_load_explicit;
|
||||
using std::atomic_exchange;
|
||||
using std::atomic_exchange_explicit;
|
||||
using std::atomic_compare_exchange_strong;
|
||||
using std::atomic_compare_exchange_strong_explicit;
|
||||
using std::atomic_compare_exchange_weak;
|
||||
using std::atomic_compare_exchange_weak_explicit;
|
||||
using std::atomic_fetch_add;
|
||||
using std::atomic_fetch_add_explicit;
|
||||
using std::atomic_fetch_sub;
|
||||
using std::atomic_fetch_sub_explicit;
|
||||
using std::atomic_fetch_or;
|
||||
using std::atomic_fetch_or_explicit;
|
||||
using std::atomic_fetch_xor;
|
||||
using std::atomic_fetch_xor_explicit;
|
||||
using std::atomic_fetch_and;
|
||||
using std::atomic_fetch_and_explicit;
|
||||
using std::atomic_thread_fence;
|
||||
using std::atomic_signal_fence;
|
||||
|
||||
#elif defined(HAVE_STDATOMIC_H) && !defined(__CC_ARM) && \
|
||||
!defined(__STDC_NO_ATOMICS__)
|
||||
# include <stdint.h>
|
||||
# include <stdatomic.h>
|
||||
#elif defined(__GNUC__)
|
||||
# include <metal/compiler/gcc/atomic.h>
|
||||
#else
|
||||
# include <metal/processor/arm/atomic.h>
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_ATOMIC__H__ */
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file cache.h
|
||||
* @brief CACHE operation primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_CACHE__H__
|
||||
#define __METAL_CACHE__H__
|
||||
|
||||
#ifdef METAL_FREERTOS
|
||||
#include <metal/system/freertos/cache.h>
|
||||
#else
|
||||
#include <metal/system/generic/cache.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup cache CACHE Interfaces
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief flush specified data cache
|
||||
*
|
||||
* @param[in] addr start memory logical address
|
||||
* @param[in] len length of memory
|
||||
* If addr is NULL, and len is 0,
|
||||
* It will flush the whole data cache.
|
||||
*/
|
||||
static inline void metal_cache_flush(void *addr, unsigned int len)
|
||||
{
|
||||
__metal_cache_flush(addr, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief invalidate specified data cache
|
||||
*
|
||||
* @param[in] addr start memory logical address
|
||||
* @param[in] len length of memory
|
||||
* If addr is NULL, and len is 0,
|
||||
* It will invalidate the whole data cache.
|
||||
*/
|
||||
static inline void metal_cache_invalidate(void *addr, unsigned int len)
|
||||
{
|
||||
__metal_cache_invalidate(addr, len);
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_CACHE__H__ */
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file compiler.h
|
||||
* @brief Compiler specific primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_COMPILER__H__
|
||||
#define __METAL_COMPILER__H__
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# include <metal/compiler/gcc/compiler.h>
|
||||
#elif defined(__ICCARM__)
|
||||
# include <metal/compiler/iar/compiler.h>
|
||||
#elif defined(__CC_ARM)
|
||||
# error "MDK-ARM ARMCC compiler requires the GNU extentions to work correctly"
|
||||
#else
|
||||
# error "Missing compiler support"
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_COMPILER__H__ */
|
||||
@@ -0,0 +1,58 @@
|
||||
/*-
|
||||
* Copyright (c) 2020 STMicroelectronics. All rights reserved.
|
||||
*
|
||||
* Copyright (c) 1982, 1986, 1989, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
* (c) UNIX System Laboratories, Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef __METAL_ARMCC_ERRNO__H__
|
||||
#define __METAL_ARMCC_ERRNO__H__
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define LIBMETAL_ERR_BASE 100
|
||||
|
||||
#define EPERM (LIBMETAL_ERR_BASE + 1) /* Operation not permitted */
|
||||
#define ENOENT (LIBMETAL_ERR_BASE + 2) /* No such file or directory */
|
||||
#define ESRCH (LIBMETAL_ERR_BASE + 3) /* No such process */
|
||||
#define EINTR (LIBMETAL_ERR_BASE + 4) /* Interrupted system call */
|
||||
#define EIO (LIBMETAL_ERR_BASE + 5) /* Input/output error */
|
||||
#define ENXIO (LIBMETAL_ERR_BASE + 6) /* Device not configured */
|
||||
#define E2BIG (LIBMETAL_ERR_BASE + 7) /* Argument list too long */
|
||||
#define ENOEXEC (LIBMETAL_ERR_BASE + 8) /* Exec format error */
|
||||
#define EBADF (LIBMETAL_ERR_BASE + 9) /* Bad file descriptor */
|
||||
#define ECHILD (LIBMETAL_ERR_BASE + 10) /* No child processes */
|
||||
#define EDEADLK (LIBMETAL_ERR_BASE + 11) /* Resource deadlock avoided */
|
||||
#define EACCES (LIBMETAL_ERR_BASE + 13) /* Permission denied */
|
||||
#define EFAULT (LIBMETAL_ERR_BASE + 14) /* Bad address */
|
||||
#define ENOTBLK (LIBMETAL_ERR_BASE + 15) /* Block device required */
|
||||
#define EBUSY (LIBMETAL_ERR_BASE + 16) /* Device busy */
|
||||
#define EEXIST (LIBMETAL_ERR_BASE + 17) /* File exists */
|
||||
#define EXDEV (LIBMETAL_ERR_BASE + 18) /* Cross-device link */
|
||||
#define ENODEV (LIBMETAL_ERR_BASE + 19) /* Operation not supported by device */
|
||||
#define ENOTDIR (LIBMETAL_ERR_BASE + 20) /* Not a directory */
|
||||
#define EISDIR (LIBMETAL_ERR_BASE + 21) /* Is a directory */
|
||||
#define ENFILE (LIBMETAL_ERR_BASE + 23) /* Too many open files in system */
|
||||
#define EMFILE (LIBMETAL_ERR_BASE + 24) /* Too many open files */
|
||||
#define ENOTTY (LIBMETAL_ERR_BASE + 25) /* Inappropriate ioctl for device */
|
||||
#define ETXTBSY (LIBMETAL_ERR_BASE + 26) /* Text file busy */
|
||||
#define EFBIG (LIBMETAL_ERR_BASE + 27) /* File too large */
|
||||
#define ENOSPC (LIBMETAL_ERR_BASE + 28) /* No space left on device */
|
||||
#define ESPIPE (LIBMETAL_ERR_BASE + 29) /* Illegal seek */
|
||||
#define EROFS (LIBMETAL_ERR_BASE + 30) /* Read-only filesystem */
|
||||
#define EMLINK (LIBMETAL_ERR_BASE + 31) /* Too many links */
|
||||
#define EPIPE (LIBMETAL_ERR_BASE + 32) /* Broken pipe */
|
||||
#define EAGAIN (LIBMETAL_ERR_BASE + 35) /* Resource temporarily unavailable */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_ARMCC_ERRNO__H__ */
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file gcc/atomic.h
|
||||
* @brief GCC specific atomic primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_GCC_ATOMIC__H__
|
||||
#define __METAL_GCC_ATOMIC__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef int atomic_flag;
|
||||
typedef char atomic_char;
|
||||
typedef unsigned char atomic_uchar;
|
||||
typedef short atomic_short;
|
||||
typedef unsigned short atomic_ushort;
|
||||
typedef int atomic_int;
|
||||
typedef unsigned int atomic_uint;
|
||||
typedef atomic_uint atomic_uintptr_t;
|
||||
typedef long atomic_long;
|
||||
typedef unsigned long atomic_ulong;
|
||||
typedef long long atomic_llong;
|
||||
typedef unsigned long long atomic_ullong;
|
||||
|
||||
#define ATOMIC_FLAG_INIT 0
|
||||
#define ATOMIC_VAR_INIT(VAL) (VAL)
|
||||
|
||||
typedef enum {
|
||||
memory_order_relaxed,
|
||||
memory_order_consume,
|
||||
memory_order_acquire,
|
||||
memory_order_release,
|
||||
memory_order_acq_rel,
|
||||
memory_order_seq_cst,
|
||||
} memory_order;
|
||||
|
||||
#define atomic_flag_test_and_set(FLAG) \
|
||||
__sync_lock_test_and_set((FLAG), 1)
|
||||
#define atomic_flag_test_and_set_explicit(FLAG, MO) \
|
||||
atomic_flag_test_and_set(FLAG)
|
||||
#define atomic_flag_clear(FLAG) \
|
||||
__sync_lock_release((FLAG))
|
||||
#define atomic_flag_clear_explicit(FLAG, MO) \
|
||||
atomic_flag_clear(FLAG)
|
||||
#define atomic_init(OBJ, VAL) \
|
||||
do { *(OBJ) = (VAL); } while (0)
|
||||
#define atomic_is_lock_free(OBJ) \
|
||||
(sizeof(*(OBJ)) <= sizeof(long))
|
||||
#define atomic_store(OBJ, VAL) \
|
||||
do { *(OBJ) = (VAL); __sync_synchronize(); } while (0)
|
||||
#define atomic_store_explicit(OBJ, VAL, MO) \
|
||||
atomic_store((OBJ), (VAL))
|
||||
#define atomic_load(OBJ) \
|
||||
({ __sync_synchronize(); *(OBJ); })
|
||||
#define atomic_load_explicit(OBJ, MO) \
|
||||
atomic_load(OBJ)
|
||||
#define atomic_exchange(OBJ, DES) \
|
||||
({ \
|
||||
__typeof__(OBJ) obj = (OBJ); \
|
||||
__typeof__(*obj) des = (DES); \
|
||||
__typeof__(*obj) expval; \
|
||||
__typeof__(*obj) oldval = atomic_load(obj); \
|
||||
do { \
|
||||
expval = oldval; \
|
||||
oldval = __sync_val_compare_and_swap( \
|
||||
obj, expval, des); \
|
||||
} while (oldval != expval); \
|
||||
oldval; \
|
||||
})
|
||||
#define atomic_exchange_explicit(OBJ, DES, MO) \
|
||||
atomic_exchange((OBJ), (DES))
|
||||
#define atomic_compare_exchange_strong(OBJ, EXP, DES) \
|
||||
({ \
|
||||
__typeof__(OBJ) obj = (OBJ); \
|
||||
__typeof__(EXP) exp = (EXP); \
|
||||
__typeof__(*obj) expval = *exp; \
|
||||
__typeof__(*obj) oldval = __sync_val_compare_and_swap( \
|
||||
obj, expval, (DES)); \
|
||||
*exp = oldval; \
|
||||
oldval == expval; \
|
||||
})
|
||||
#define atomic_compare_exchange_strong_explicit(OBJ, EXP, DES, MO) \
|
||||
atomic_compare_exchange_strong((OBJ), (EXP), (DES))
|
||||
#define atomic_compare_exchange_weak(OBJ, EXP, DES) \
|
||||
atomic_compare_exchange_strong((OBJ), (EXP), (DES))
|
||||
#define atomic_compare_exchange_weak_explicit(OBJ, EXP, DES, MO) \
|
||||
atomic_compare_exchange_weak((OBJ), (EXP), (DES))
|
||||
#define atomic_fetch_add(OBJ, VAL) \
|
||||
__sync_fetch_and_add((OBJ), (VAL))
|
||||
#define atomic_fetch_add_explicit(OBJ, VAL, MO) \
|
||||
atomic_fetch_add((OBJ), (VAL))
|
||||
#define atomic_fetch_sub(OBJ, VAL) \
|
||||
__sync_fetch_and_sub((OBJ), (VAL))
|
||||
#define atomic_fetch_sub_explicit(OBJ, VAL, MO) \
|
||||
atomic_fetch_sub((OBJ), (VAL))
|
||||
#define atomic_fetch_or(OBJ, VAL) \
|
||||
__sync_fetch_and_or((OBJ), (VAL))
|
||||
#define atomic_fetch_or_explicit(OBJ, VAL, MO) \
|
||||
atomic_fetch_or((OBJ), (VAL))
|
||||
#define atomic_fetch_xor(OBJ, VAL) \
|
||||
__sync_fetch_and_xor((OBJ), (VAL))
|
||||
#define atomic_fetch_xor_explicit(OBJ, VAL, MO) \
|
||||
atomic_fetch_xor((OBJ), (VAL))
|
||||
#define atomic_fetch_and(OBJ, VAL) \
|
||||
__sync_fetch_and_and((OBJ), (VAL))
|
||||
#define atomic_fetch_and_explicit(OBJ, VAL, MO) \
|
||||
atomic_fetch_and((OBJ), (VAL))
|
||||
#define atomic_thread_fence(MO) \
|
||||
__sync_synchronize()
|
||||
#define atomic_signal_fence(MO) \
|
||||
__sync_synchronize()
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_GCC_ATOMIC__H__ */
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file gcc/compiler.h
|
||||
* @brief GCC specific primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_GCC_COMPILER__H__
|
||||
#define __METAL_GCC_COMPILER__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define restrict __restrict__
|
||||
#define metal_align(n) __attribute__((aligned(n)))
|
||||
#define metal_weak __attribute__((weak))
|
||||
|
||||
#if defined(__STRICT_ANSI__)
|
||||
#define metal_asm __asm__
|
||||
#else
|
||||
/*
|
||||
* Even though __asm__ is always available in mainline GCC, we use asm in
|
||||
* the non-strict modes for compatibility with other compilers that define
|
||||
* __GNUC__
|
||||
*/
|
||||
#define metal_asm asm
|
||||
#endif
|
||||
|
||||
#define METAL_PACKED_BEGIN
|
||||
#define METAL_PACKED_END __attribute__((__packed__))
|
||||
|
||||
#ifndef __deprecated
|
||||
#define __deprecated __attribute__((deprecated))
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_GCC_COMPILER__H__ */
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2018, ST Microelectronics. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file iar/compiler.h
|
||||
* @brief IAR specific primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_IAR_COMPILER__H__
|
||||
#define __METAL_IAR_COMPILER__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define restrict __restrict__
|
||||
#define metal_align(n) __attribute__((aligned(n)))
|
||||
#define metal_weak __attribute__((weak))
|
||||
#define metal_asm asm
|
||||
|
||||
#define METAL_PACKED_BEGIN __packed
|
||||
#define METAL_PACKED_END
|
||||
|
||||
#ifndef __deprecated
|
||||
#define __deprecated __attribute__((deprecated))
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_IAR_COMPILER__H__ */
|
||||
@@ -0,0 +1,60 @@
|
||||
/*-
|
||||
* Copyright (c) 2020 STMicroelectronics. All rights reserved.
|
||||
*
|
||||
* Copyright (c) 1982, 1986, 1989, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
* (c) UNIX System Laboratories, Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef __METAL_IAR_ERRNO__H__
|
||||
#define __METAL_IAR_ERRNO__H__
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define LIBMETAL_ERR_BASE 100
|
||||
|
||||
#define EPERM (LIBMETAL_ERR_BASE + 1) /* Operation not permitted */
|
||||
#define ENOENT (LIBMETAL_ERR_BASE + 2) /* No such file or directory */
|
||||
#define ESRCH (LIBMETAL_ERR_BASE + 3) /* No such process */
|
||||
#define EINTR (LIBMETAL_ERR_BASE + 4) /* Interrupted system call */
|
||||
#define EIO (LIBMETAL_ERR_BASE + 5) /* Input/output error */
|
||||
#define ENXIO (LIBMETAL_ERR_BASE + 6) /* Device not configured */
|
||||
#define E2BIG (LIBMETAL_ERR_BASE + 7) /* Argument list too long */
|
||||
#define ENOEXEC (LIBMETAL_ERR_BASE + 8) /* Exec format error */
|
||||
#define EBADF (LIBMETAL_ERR_BASE + 9) /* Bad file descriptor */
|
||||
#define ECHILD (LIBMETAL_ERR_BASE + 10) /* No child processes */
|
||||
#define EDEADLK (LIBMETAL_ERR_BASE + 11) /* Resource deadlock avoided */
|
||||
#define ENOMEM (LIBMETAL_ERR_BASE + 12) /* Cannot allocate memory */
|
||||
#define EACCES (LIBMETAL_ERR_BASE + 13) /* Permission denied */
|
||||
#define EFAULT (LIBMETAL_ERR_BASE + 14) /* Bad address */
|
||||
#define ENOTBLK (LIBMETAL_ERR_BASE + 15) /* Block device required */
|
||||
#define EBUSY (LIBMETAL_ERR_BASE + 16) /* Device busy */
|
||||
#define EEXIST (LIBMETAL_ERR_BASE + 17) /* File exists */
|
||||
#define EXDEV (LIBMETAL_ERR_BASE + 18) /* Cross-device link */
|
||||
#define ENODEV (LIBMETAL_ERR_BASE + 19) /* Operation not supported by device */
|
||||
#define ENOTDIR (LIBMETAL_ERR_BASE + 20) /* Not a directory */
|
||||
#define EISDIR (LIBMETAL_ERR_BASE + 21) /* Is a directory */
|
||||
#define EINVAL (LIBMETAL_ERR_BASE + 22) /* Invalid argument */
|
||||
#define ENFILE (LIBMETAL_ERR_BASE + 23) /* Too many open files in system */
|
||||
#define EMFILE (LIBMETAL_ERR_BASE + 24) /* Too many open files */
|
||||
#define ENOTTY (LIBMETAL_ERR_BASE + 25) /* Inappropriate ioctl for device */
|
||||
#define ETXTBSY (LIBMETAL_ERR_BASE + 26) /* Text file busy */
|
||||
#define EFBIG (LIBMETAL_ERR_BASE + 27) /* File too large */
|
||||
#define ENOSPC (LIBMETAL_ERR_BASE + 28) /* No space left on device */
|
||||
#define ESPIPE (LIBMETAL_ERR_BASE + 29) /* Illegal seek */
|
||||
#define EROFS (LIBMETAL_ERR_BASE + 30) /* Read-only filesystem */
|
||||
#define EMLINK (LIBMETAL_ERR_BASE + 31) /* Too many links */
|
||||
#define EPIPE (LIBMETAL_ERR_BASE + 32) /* Broken pipe */
|
||||
#define EAGAIN (LIBMETAL_ERR_BASE + 35) /* Resource temporarily unavailable */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_IAR_ERRNO__H__ */
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file condition.h
|
||||
* @brief Condition variable for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_CONDITION__H__
|
||||
#define __METAL_CONDITION__H__
|
||||
|
||||
#include <metal/mutex.h>
|
||||
#include <metal/utilities.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup condition Condition Variable Interfaces
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Opaque libmetal condition variable data structure. */
|
||||
struct metal_condition;
|
||||
|
||||
/**
|
||||
* @brief Initialize a libmetal condition variable.
|
||||
* @param[in] cv condition variable to initialize.
|
||||
*/
|
||||
static inline void metal_condition_init(struct metal_condition *cv);
|
||||
|
||||
/**
|
||||
* @brief Notify one waiter.
|
||||
* Before calling this function, the caller
|
||||
* should have acquired the mutex.
|
||||
* @param[in] cv condition variable
|
||||
* @return zero on no errors, non-zero on errors
|
||||
* @see metal_condition_wait, metal_condition_broadcast
|
||||
*/
|
||||
static inline int metal_condition_signal(struct metal_condition *cv);
|
||||
|
||||
/**
|
||||
* @brief Notify all waiters.
|
||||
* Before calling this function, the caller
|
||||
* should have acquired the mutex.
|
||||
* @param[in] cv condition variable
|
||||
* @return zero on no errors, non-zero on errors
|
||||
* @see metal_condition_wait, metal_condition_signal
|
||||
*/
|
||||
static inline int metal_condition_broadcast(struct metal_condition *cv);
|
||||
|
||||
/**
|
||||
* @brief Block until the condition variable is notified.
|
||||
* Before calling this function, the caller should
|
||||
* have acquired the mutex.
|
||||
* @param[in] cv condition variable
|
||||
* @param[in] m mutex
|
||||
* @return 0 on success, non-zero on failure.
|
||||
* @see metal_condition_signal
|
||||
*/
|
||||
int metal_condition_wait(struct metal_condition *cv, metal_mutex_t *m);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef METAL_FREERTOS
|
||||
#include <metal/system/freertos/condition.h>
|
||||
#else
|
||||
#include <metal/system/generic/condition.h>
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_CONDITION__H__ */
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file config.h
|
||||
* @brief Generated configuration settings for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_CONFIG__H__
|
||||
#define __METAL_CONFIG__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Library major version number. */
|
||||
#define METAL_VER_MAJOR 1
|
||||
|
||||
/** Library minor version number. */
|
||||
#define METAL_VER_MINOR 1
|
||||
|
||||
/** Library patch level. */
|
||||
#define METAL_VER_PATCH 0
|
||||
|
||||
/** Library version string. */
|
||||
#define METAL_VER "1.1.0"
|
||||
|
||||
/** System type (linux, generic, ...). */
|
||||
#ifdef METAL_FREERTOS
|
||||
#define METAL_SYSTEM "freertos"
|
||||
#define METAL_SYSTEM_FREERTOS
|
||||
#else
|
||||
#define METAL_SYSTEM "generic"
|
||||
#define METAL_SYSTEM_GENERIC
|
||||
#endif
|
||||
|
||||
/** Processor type (arm, x86_64, ...). */
|
||||
#define METAL_PROCESSOR "arm"
|
||||
#define METAL_PROCESSOR_ARM
|
||||
|
||||
/** Machine type (zynq, zynqmp, ...). */
|
||||
#define METAL_MACHINE "template"
|
||||
#define METAL_MACHINE_TEMPLATE
|
||||
|
||||
#define HAVE_STDATOMIC_H
|
||||
/* #undef HAVE_FUTEX_H */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_CONFIG__H__ */
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file cpu.h
|
||||
* @brief CPU primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_CPU__H__
|
||||
#define __METAL_CPU__H__
|
||||
|
||||
# include <metal/processor/arm/cpu.h>
|
||||
|
||||
#endif /* __METAL_CPU__H__ */
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file device.h
|
||||
* @brief Bus abstraction for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_BUS__H__
|
||||
#define __METAL_BUS__H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/list.h>
|
||||
#include <metal/dma.h>
|
||||
#include <metal/sys.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup device Bus Abstraction
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef METAL_MAX_DEVICE_REGIONS
|
||||
#define METAL_MAX_DEVICE_REGIONS 32
|
||||
#endif
|
||||
|
||||
struct metal_bus;
|
||||
struct metal_device;
|
||||
|
||||
/** Bus operations. */
|
||||
struct metal_bus_ops {
|
||||
void (*bus_close)(struct metal_bus *bus);
|
||||
int (*dev_open)(struct metal_bus *bus,
|
||||
const char *dev_name,
|
||||
struct metal_device **device);
|
||||
void (*dev_close)(struct metal_bus *bus,
|
||||
struct metal_device *device);
|
||||
void (*dev_irq_ack)(struct metal_bus *bus,
|
||||
struct metal_device *device,
|
||||
int irq);
|
||||
int (*dev_dma_map)(struct metal_bus *bus,
|
||||
struct metal_device *device,
|
||||
uint32_t dir,
|
||||
struct metal_sg *sg_in,
|
||||
int nents_in,
|
||||
struct metal_sg *sg_out);
|
||||
void (*dev_dma_unmap)(struct metal_bus *bus,
|
||||
struct metal_device *device,
|
||||
uint32_t dir,
|
||||
struct metal_sg *sg,
|
||||
int nents);
|
||||
};
|
||||
|
||||
/** Libmetal bus structure. */
|
||||
struct metal_bus {
|
||||
const char *name;
|
||||
struct metal_bus_ops ops;
|
||||
struct metal_list devices;
|
||||
struct metal_list node;
|
||||
};
|
||||
|
||||
/** Libmetal generic bus. */
|
||||
extern struct metal_bus metal_generic_bus;
|
||||
|
||||
/** Libmetal device structure. */
|
||||
struct metal_device {
|
||||
const char *name; /**< Device name */
|
||||
struct metal_bus *bus; /**< Bus that contains device */
|
||||
unsigned int num_regions; /**< Number of I/O regions in
|
||||
device */
|
||||
struct metal_io_region regions[METAL_MAX_DEVICE_REGIONS]; /**< Array of
|
||||
I/O regions in device*/
|
||||
struct metal_list node; /**< Node on bus' list of devices */
|
||||
int irq_num; /**< Number of IRQs per device */
|
||||
void *irq_info; /**< IRQ ID */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Register a libmetal bus.
|
||||
* @param[in] bus Pre-initialized bus structure.
|
||||
* @return 0 on success, or -errno on failure.
|
||||
*/
|
||||
extern int metal_bus_register(struct metal_bus *bus);
|
||||
|
||||
/**
|
||||
* @brief Unregister a libmetal bus.
|
||||
* @param[in] bus Pre-registered bus structure.
|
||||
* @return 0 on success, or -errno on failure.
|
||||
*/
|
||||
extern int metal_bus_unregister(struct metal_bus *bus);
|
||||
|
||||
/**
|
||||
* @brief Find a libmetal bus by name.
|
||||
* @param[in] name Bus name.
|
||||
* @param[out] bus Returned bus handle.
|
||||
* @return 0 on success, or -errno on failure.
|
||||
*/
|
||||
extern int metal_bus_find(const char *name, struct metal_bus **bus);
|
||||
|
||||
/**
|
||||
* @brief Statically register a generic libmetal device.
|
||||
*
|
||||
* In non-Linux systems, devices are always required to be statically
|
||||
* registered at application initialization.
|
||||
* In Linux system, devices can be dynamically opened via sysfs or libfdt based
|
||||
* enumeration at runtime.
|
||||
* This interface is used for static registration of devices. Subsequent calls
|
||||
* to metal_device_open() look up in this list of pre-registered devices on the
|
||||
* "generic" bus.
|
||||
* "generic" bus is used on non-Linux system to group the memory mapped devices.
|
||||
*
|
||||
* @param[in] device Generic device.
|
||||
* @return 0 on success, or -errno on failure.
|
||||
*/
|
||||
extern int metal_register_generic_device(struct metal_device *device);
|
||||
|
||||
/**
|
||||
* @brief Open a libmetal device by name.
|
||||
* @param[in] bus_name Bus name.
|
||||
* @param[in] dev_name Device name.
|
||||
* @param[out] device Returned device handle.
|
||||
* @return 0 on success, or -errno on failure.
|
||||
*/
|
||||
extern int metal_device_open(const char *bus_name, const char *dev_name,
|
||||
struct metal_device **device);
|
||||
|
||||
/**
|
||||
* @brief Close a libmetal device.
|
||||
* @param[in] device Device handle.
|
||||
*/
|
||||
extern void metal_device_close(struct metal_device *device);
|
||||
|
||||
/**
|
||||
* @brief Get an I/O region accessor for a device region.
|
||||
*
|
||||
* @param[in] device Device handle.
|
||||
* @param[in] index Region index.
|
||||
* @return I/O accessor handle, or NULL on failure.
|
||||
*/
|
||||
static inline struct metal_io_region *
|
||||
metal_device_io_region(struct metal_device *device, unsigned int index)
|
||||
{
|
||||
return (index < device->num_regions
|
||||
? &device->regions[index]
|
||||
: NULL);
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef METAL_INTERNAL
|
||||
extern int metal_generic_dev_sys_open(struct metal_device *dev);
|
||||
extern int metal_generic_dev_open(struct metal_bus *bus, const char *dev_name,
|
||||
struct metal_device **device);
|
||||
extern int metal_generic_dev_dma_map(struct metal_bus *bus,
|
||||
struct metal_device *device,
|
||||
uint32_t dir,
|
||||
struct metal_sg *sg_in,
|
||||
int nents_in,
|
||||
struct metal_sg *sg_out);
|
||||
extern void metal_generic_dev_dma_unmap(struct metal_bus *bus,
|
||||
struct metal_device *device,
|
||||
uint32_t dir,
|
||||
struct metal_sg *sg,
|
||||
int nents);
|
||||
#endif /* METAL_INTERNAL */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_BUS__H__ */
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file dma.h
|
||||
* @brief DMA primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_DMA__H__
|
||||
#define __METAL_DMA__H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <metal/sys.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup dma DMA Interfaces
|
||||
* @{
|
||||
*/
|
||||
|
||||
#define METAL_DMA_DEV_R 1 /**< DMA direction, device read */
|
||||
#define METAL_DMA_DEV_W 2 /**< DMA direction, device write */
|
||||
#define METAL_DMA_DEV_WR 3 /**< DMA direction, device read/write */
|
||||
|
||||
/**
|
||||
* @brief scatter/gather list element structure
|
||||
*/
|
||||
struct metal_sg {
|
||||
void *virt; /**< CPU virtual address */
|
||||
struct metal_io_region *io; /**< IO region */
|
||||
int len; /**< length */
|
||||
};
|
||||
|
||||
struct metal_device;
|
||||
|
||||
/**
|
||||
* @brief Map memory for DMA transaction.
|
||||
* After the memory is DMA mapped, the memory should be
|
||||
* accessed by the DMA device but not the CPU.
|
||||
*
|
||||
* @param[in] dev DMA device
|
||||
* @param[in] dir DMA direction
|
||||
* @param[in] sg_in sg list of memory to map
|
||||
* @param[in] nents_in number of sg list entries of memory to map
|
||||
* @param[out] sg_out sg list of mapped memory
|
||||
* @return number of mapped sg entries, -error on failure.
|
||||
*/
|
||||
int metal_dma_map(struct metal_device *dev,
|
||||
uint32_t dir,
|
||||
struct metal_sg *sg_in,
|
||||
int nents_in,
|
||||
struct metal_sg *sg_out);
|
||||
|
||||
/**
|
||||
* @brief Unmap DMA memory
|
||||
* After the memory is DMA unmapped, the memory should
|
||||
* be accessed by the CPU but not the DMA device.
|
||||
*
|
||||
* @param[in] dev DMA device
|
||||
* @param[in] dir DMA direction
|
||||
* @param[in] sg sg list of mapped DMA memory
|
||||
* @param[in] nents number of sg list entries of DMA memory
|
||||
*/
|
||||
void metal_dma_unmap(struct metal_device *dev,
|
||||
uint32_t dir,
|
||||
struct metal_sg *sg,
|
||||
int nents);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_DMA__H__ */
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2020 STMicroelectronnics. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file metal/errno.h
|
||||
* @brief error specific primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_ERRNO__H__
|
||||
#define __METAL_ERRNO__H__
|
||||
|
||||
#if defined(__ICCARM__)
|
||||
# include <metal/compiler/iar/errno.h>
|
||||
#elif defined(__CC_ARM)
|
||||
# include <metal/compiler/armcc/errno.h>
|
||||
#else
|
||||
# include <errno.h>
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_ERRNO__H__ */
|
||||
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
* Copyright (c) 2015 - 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file io.h
|
||||
* @brief I/O access primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_IO__H__
|
||||
#define __METAL_IO__H__
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <metal/assert.h>
|
||||
#include <metal/compiler.h>
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/sys.h>
|
||||
#include <metal/cpu.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup io IO Interfaces
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifdef __MICROBLAZE__
|
||||
#define NO_ATOMIC_64_SUPPORT
|
||||
#endif
|
||||
|
||||
struct metal_io_region;
|
||||
|
||||
/** Generic I/O operations. */
|
||||
struct metal_io_ops {
|
||||
uint64_t (*read)(struct metal_io_region *io,
|
||||
unsigned long offset,
|
||||
memory_order order,
|
||||
int width);
|
||||
void (*write)(struct metal_io_region *io,
|
||||
unsigned long offset,
|
||||
uint64_t value,
|
||||
memory_order order,
|
||||
int width);
|
||||
int (*block_read)(struct metal_io_region *io,
|
||||
unsigned long offset,
|
||||
void *restrict dst,
|
||||
memory_order order,
|
||||
int len);
|
||||
int (*block_write)(struct metal_io_region *io,
|
||||
unsigned long offset,
|
||||
const void *restrict src,
|
||||
memory_order order,
|
||||
int len);
|
||||
void (*block_set)(struct metal_io_region *io,
|
||||
unsigned long offset,
|
||||
unsigned char value,
|
||||
memory_order order,
|
||||
int len);
|
||||
void (*close)(struct metal_io_region *io);
|
||||
metal_phys_addr_t (*offset_to_phys)(struct metal_io_region *io,
|
||||
unsigned long offset);
|
||||
unsigned long (*phys_to_offset)(struct metal_io_region *io,
|
||||
metal_phys_addr_t phys);
|
||||
};
|
||||
|
||||
/** Libmetal I/O region structure. */
|
||||
struct metal_io_region {
|
||||
void *virt; /**< base virtual address */
|
||||
const metal_phys_addr_t *physmap; /**< table of base physical address
|
||||
of each of the pages in the I/O
|
||||
region */
|
||||
size_t size; /**< size of the I/O region */
|
||||
unsigned long page_shift; /**< page shift of I/O region */
|
||||
metal_phys_addr_t page_mask; /**< page mask of I/O region */
|
||||
unsigned int mem_flags; /**< memory attribute of the
|
||||
I/O region */
|
||||
struct metal_io_ops ops; /**< I/O region operations */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Open a libmetal I/O region.
|
||||
*
|
||||
* @param[in, out] io I/O region handle.
|
||||
* @param[in] virt Virtual address of region.
|
||||
* @param[in] physmap Array of physical addresses per page.
|
||||
* @param[in] size Size of region.
|
||||
* @param[in] page_shift Log2 of page size (-1 for single page).
|
||||
* @param[in] mem_flags Memory flags
|
||||
* @param[in] ops ops
|
||||
*/
|
||||
void
|
||||
metal_io_init(struct metal_io_region *io, void *virt,
|
||||
const metal_phys_addr_t *physmap, size_t size,
|
||||
unsigned int page_shift, unsigned int mem_flags,
|
||||
const struct metal_io_ops *ops);
|
||||
|
||||
/**
|
||||
* @brief Close a libmetal shared memory segment.
|
||||
* @param[in] io I/O region handle.
|
||||
*/
|
||||
static inline void metal_io_finish(struct metal_io_region *io)
|
||||
{
|
||||
if (io->ops.close)
|
||||
(*io->ops.close)(io);
|
||||
memset(io, 0, sizeof(*io));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get size of I/O region.
|
||||
*
|
||||
* @param[in] io I/O region handle.
|
||||
* @return Size of I/O region.
|
||||
*/
|
||||
static inline size_t metal_io_region_size(struct metal_io_region *io)
|
||||
{
|
||||
return io->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get virtual address for a given offset into the I/O region.
|
||||
* @param[in] io I/O region handle.
|
||||
* @param[in] offset Offset into shared memory segment.
|
||||
* @return NULL if offset is out of range, or pointer to offset.
|
||||
*/
|
||||
static inline void *
|
||||
metal_io_virt(struct metal_io_region *io, unsigned long offset)
|
||||
{
|
||||
return (io->virt != METAL_BAD_VA && offset < io->size
|
||||
? (void *)((uintptr_t)io->virt + offset)
|
||||
: NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a virtual address to offset within I/O region.
|
||||
* @param[in] io I/O region handle.
|
||||
* @param[in] virt Virtual address within segment.
|
||||
* @return METAL_BAD_OFFSET if out of range, or offset.
|
||||
*/
|
||||
static inline unsigned long
|
||||
metal_io_virt_to_offset(struct metal_io_region *io, void *virt)
|
||||
{
|
||||
size_t offset = (uintptr_t)virt - (uintptr_t)io->virt;
|
||||
|
||||
return (offset < io->size ? offset : METAL_BAD_OFFSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get physical address for a given offset into the I/O region.
|
||||
* @param[in] io I/O region handle.
|
||||
* @param[in] offset Offset into shared memory segment.
|
||||
* @return METAL_BAD_PHYS if offset is out of range, or physical address
|
||||
* of offset.
|
||||
*/
|
||||
static inline metal_phys_addr_t
|
||||
metal_io_phys(struct metal_io_region *io, unsigned long offset)
|
||||
{
|
||||
if (!io->ops.offset_to_phys) {
|
||||
unsigned long page = (io->page_shift >=
|
||||
sizeof(offset) * CHAR_BIT ?
|
||||
0 : offset >> io->page_shift);
|
||||
return (io->physmap && offset < io->size
|
||||
? io->physmap[page] + (offset & io->page_mask)
|
||||
: METAL_BAD_PHYS);
|
||||
}
|
||||
|
||||
return io->ops.offset_to_phys(io, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a physical address to offset within I/O region.
|
||||
* @param[in] io I/O region handle.
|
||||
* @param[in] phys Physical address within segment.
|
||||
* @return METAL_BAD_OFFSET if out of range, or offset.
|
||||
*/
|
||||
static inline unsigned long
|
||||
metal_io_phys_to_offset(struct metal_io_region *io, metal_phys_addr_t phys)
|
||||
{
|
||||
if (!io->ops.phys_to_offset) {
|
||||
unsigned long offset =
|
||||
(io->page_mask == (metal_phys_addr_t)(-1) ?
|
||||
phys - io->physmap[0] : phys & io->page_mask);
|
||||
do {
|
||||
if (metal_io_phys(io, offset) == phys)
|
||||
return offset;
|
||||
offset += io->page_mask + 1;
|
||||
} while (offset < io->size);
|
||||
return METAL_BAD_OFFSET;
|
||||
}
|
||||
|
||||
return (*io->ops.phys_to_offset)(io, phys);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a physical address to virtual address.
|
||||
* @param[in] io Shared memory segment handle.
|
||||
* @param[in] phys Physical address within segment.
|
||||
* @return NULL if out of range, or corresponding virtual address.
|
||||
*/
|
||||
static inline void *
|
||||
metal_io_phys_to_virt(struct metal_io_region *io, metal_phys_addr_t phys)
|
||||
{
|
||||
return metal_io_virt(io, metal_io_phys_to_offset(io, phys));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a virtual address to physical address.
|
||||
* @param[in] io Shared memory segment handle.
|
||||
* @param[in] virt Virtual address within segment.
|
||||
* @return METAL_BAD_PHYS if out of range, or corresponding
|
||||
* physical address.
|
||||
*/
|
||||
static inline metal_phys_addr_t
|
||||
metal_io_virt_to_phys(struct metal_io_region *io, void *virt)
|
||||
{
|
||||
return metal_io_phys(io, metal_io_virt_to_offset(io, virt));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read a value from an I/O region.
|
||||
* @param[in] io I/O region handle.
|
||||
* @param[in] offset Offset into I/O region.
|
||||
* @param[in] order Memory ordering.
|
||||
* @param[in] width Width in bytes of datatype to read. This must be 1, 2,
|
||||
* 4, or 8, and a compile time constant for this function
|
||||
* to inline cleanly.
|
||||
* @return Value.
|
||||
*/
|
||||
static inline uint64_t
|
||||
metal_io_read(struct metal_io_region *io, unsigned long offset,
|
||||
memory_order order, int width)
|
||||
{
|
||||
void *ptr = metal_io_virt(io, offset);
|
||||
|
||||
if (io->ops.read)
|
||||
return (*io->ops.read)(io, offset, order, width);
|
||||
else if (ptr && sizeof(atomic_uchar) == width)
|
||||
return atomic_load_explicit((atomic_uchar *)ptr, order);
|
||||
else if (ptr && sizeof(atomic_ushort) == width)
|
||||
return atomic_load_explicit((atomic_ushort *)ptr, order);
|
||||
else if (ptr && sizeof(atomic_uint) == width)
|
||||
return atomic_load_explicit((atomic_uint *)ptr, order);
|
||||
else if (ptr && sizeof(atomic_ulong) == width)
|
||||
return atomic_load_explicit((atomic_ulong *)ptr, order);
|
||||
#ifndef NO_ATOMIC_64_SUPPORT
|
||||
else if (ptr && sizeof(atomic_ullong) == width)
|
||||
return atomic_load_explicit((atomic_ullong *)ptr, order);
|
||||
#endif
|
||||
metal_assert(0);
|
||||
return 0; /* quiet compiler */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a value into an I/O region.
|
||||
* @param[in] io I/O region handle.
|
||||
* @param[in] offset Offset into I/O region.
|
||||
* @param[in] value Value to write.
|
||||
* @param[in] order Memory ordering.
|
||||
* @param[in] width Width in bytes of datatype to read. This must be 1, 2,
|
||||
* 4, or 8, and a compile time constant for this function
|
||||
* to inline cleanly.
|
||||
*/
|
||||
static inline void
|
||||
metal_io_write(struct metal_io_region *io, unsigned long offset,
|
||||
uint64_t value, memory_order order, int width)
|
||||
{
|
||||
void *ptr = metal_io_virt(io, offset);
|
||||
|
||||
if (io->ops.write)
|
||||
(*io->ops.write)(io, offset, value, order, width);
|
||||
else if (ptr && sizeof(atomic_uchar) == width)
|
||||
atomic_store_explicit((atomic_uchar *)ptr, (unsigned char)value,
|
||||
order);
|
||||
else if (ptr && sizeof(atomic_ushort) == width)
|
||||
atomic_store_explicit((atomic_ushort *)ptr,
|
||||
(unsigned short)value, order);
|
||||
else if (ptr && sizeof(atomic_uint) == width)
|
||||
atomic_store_explicit((atomic_uint *)ptr, (unsigned int)value,
|
||||
order);
|
||||
else if (ptr && sizeof(atomic_ulong) == width)
|
||||
atomic_store_explicit((atomic_ulong *)ptr, (unsigned long)value,
|
||||
order);
|
||||
#ifndef NO_ATOMIC_64_SUPPORT
|
||||
else if (ptr && sizeof(atomic_ullong) == width)
|
||||
atomic_store_explicit((atomic_ullong *)ptr,
|
||||
(unsigned long long)value, order);
|
||||
#endif
|
||||
else
|
||||
metal_assert(0);
|
||||
}
|
||||
|
||||
#define metal_io_read8_explicit(_io, _ofs, _order) \
|
||||
metal_io_read((_io), (_ofs), (_order), 1)
|
||||
#define metal_io_read8(_io, _ofs) \
|
||||
metal_io_read((_io), (_ofs), memory_order_seq_cst, 1)
|
||||
#define metal_io_write8_explicit(_io, _ofs, _val, _order) \
|
||||
metal_io_write((_io), (_ofs), (_val), (_order), 1)
|
||||
#define metal_io_write8(_io, _ofs, _val) \
|
||||
metal_io_write((_io), (_ofs), (_val), memory_order_seq_cst, 1)
|
||||
|
||||
#define metal_io_read16_explicit(_io, _ofs, _order) \
|
||||
metal_io_read((_io), (_ofs), (_order), 2)
|
||||
#define metal_io_read16(_io, _ofs) \
|
||||
metal_io_read((_io), (_ofs), memory_order_seq_cst, 2)
|
||||
#define metal_io_write16_explicit(_io, _ofs, _val, _order) \
|
||||
metal_io_write((_io), (_ofs), (_val), (_order), 2)
|
||||
#define metal_io_write16(_io, _ofs, _val) \
|
||||
metal_io_write((_io), (_ofs), (_val), memory_order_seq_cst, 2)
|
||||
|
||||
#define metal_io_read32_explicit(_io, _ofs, _order) \
|
||||
metal_io_read((_io), (_ofs), (_order), 4)
|
||||
#define metal_io_read32(_io, _ofs) \
|
||||
metal_io_read((_io), (_ofs), memory_order_seq_cst, 4)
|
||||
#define metal_io_write32_explicit(_io, _ofs, _val, _order) \
|
||||
metal_io_write((_io), (_ofs), (_val), (_order), 4)
|
||||
#define metal_io_write32(_io, _ofs, _val) \
|
||||
metal_io_write((_io), (_ofs), (_val), memory_order_seq_cst, 4)
|
||||
|
||||
#define metal_io_read64_explicit(_io, _ofs, _order) \
|
||||
metal_io_read((_io), (_ofs), (_order), 8)
|
||||
#define metal_io_read64(_io, _ofs) \
|
||||
metal_io_read((_io), (_ofs), memory_order_seq_cst, 8)
|
||||
#define metal_io_write64_explicit(_io, _ofs, _val, _order) \
|
||||
metal_io_write((_io), (_ofs), (_val), (_order), 8)
|
||||
#define metal_io_write64(_io, _ofs, _val) \
|
||||
metal_io_write((_io), (_ofs), (_val), memory_order_seq_cst, 8)
|
||||
|
||||
/**
|
||||
* @brief Read a block from an I/O region.
|
||||
* @param[in] io I/O region handle.
|
||||
* @param[in] offset Offset into I/O region.
|
||||
* @param[in] dst destination to store the read data.
|
||||
* @param[in] len length in bytes to read.
|
||||
* @return On success, number of bytes read. On failure, negative value
|
||||
*/
|
||||
int metal_io_block_read(struct metal_io_region *io, unsigned long offset,
|
||||
void *restrict dst, int len);
|
||||
|
||||
/**
|
||||
* @brief Write a block into an I/O region.
|
||||
* @param[in] io I/O region handle.
|
||||
* @param[in] offset Offset into I/O region.
|
||||
* @param[in] src source to write.
|
||||
* @param[in] len length in bytes to write.
|
||||
* @return On success, number of bytes written. On failure, negative value
|
||||
*/
|
||||
int metal_io_block_write(struct metal_io_region *io, unsigned long offset,
|
||||
const void *restrict src, int len);
|
||||
|
||||
/**
|
||||
* @brief fill a block of an I/O region.
|
||||
* @param[in] io I/O region handle.
|
||||
* @param[in] offset Offset into I/O region.
|
||||
* @param[in] value value to fill into the block
|
||||
* @param[in] len length in bytes to fill.
|
||||
* @return On success, number of bytes filled. On failure, negative value
|
||||
*/
|
||||
int metal_io_block_set(struct metal_io_region *io, unsigned long offset,
|
||||
unsigned char value, int len);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef METAL_FREERTOS
|
||||
#include <metal/system/freertos/io.h>
|
||||
#else
|
||||
#include <metal/system/generic/io.h>
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_IO__H__ */
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file irq.h
|
||||
* @brief Interrupt handling primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_IRQ__H__
|
||||
#define __METAL_IRQ__H__
|
||||
|
||||
#include <metal/list.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup irq Interrupt Handling Interfaces
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** IRQ handled status */
|
||||
#define METAL_IRQ_NOT_HANDLED 0
|
||||
#define METAL_IRQ_HANDLED 1
|
||||
|
||||
/**
|
||||
* @brief type of interrupt handler
|
||||
* @param[in] irq interrupt id
|
||||
* @param[in] arg argument to pass to the handler
|
||||
* @return irq handled status
|
||||
*/
|
||||
typedef int (*metal_irq_handler) (int irq, void *arg);
|
||||
|
||||
/**
|
||||
* @brief Register interrupt handler for interrupt.
|
||||
* Only allow single interrupt handler for a interrupt.
|
||||
*
|
||||
* If irq_handler is NULL, it will unregister interrupt
|
||||
* handler from interrupt
|
||||
*
|
||||
* @param[in] irq interrupt id
|
||||
* @param[in] irq_handler interrupt handler
|
||||
* @param[in] arg arg is the argument pointing to the data which
|
||||
* will be passed to the interrupt handler.
|
||||
* @return 0 for success, non-zero on failure
|
||||
*/
|
||||
int metal_irq_register(int irq,
|
||||
metal_irq_handler irq_handler,
|
||||
void *arg);
|
||||
|
||||
/**
|
||||
* @brief Unregister interrupt handler for interrupt.
|
||||
*
|
||||
* @param[in] irq interrupt id
|
||||
*/
|
||||
static inline
|
||||
void metal_irq_unregister(int irq)
|
||||
{
|
||||
metal_irq_register(irq, 0, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief disable interrupts
|
||||
* @return interrupts state
|
||||
*/
|
||||
unsigned int metal_irq_save_disable(void);
|
||||
|
||||
/**
|
||||
* @brief restore interrupts to their previous state
|
||||
* @param[in] flags previous interrupts state
|
||||
*/
|
||||
void metal_irq_restore_enable(unsigned int flags);
|
||||
|
||||
/**
|
||||
* @brief metal_irq_enable
|
||||
*
|
||||
* Enables the given interrupt
|
||||
*
|
||||
* @param vector - interrupt vector number
|
||||
*/
|
||||
void metal_irq_enable(unsigned int vector);
|
||||
|
||||
/**
|
||||
* @brief metal_irq_disable
|
||||
*
|
||||
* Disables the given interrupt
|
||||
*
|
||||
* @param vector - interrupt vector number
|
||||
*/
|
||||
void metal_irq_disable(unsigned int vector);
|
||||
|
||||
#ifdef METAL_FREERTOS
|
||||
#include <metal/system/freertos/irq.h>
|
||||
#else
|
||||
#include <metal/system/generic/irq.h>
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_IRQ__H__ */
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file irq.h
|
||||
* @brief Interrupt handling primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_IRQ_CONTROLLER__H__
|
||||
#define __METAL_IRQ_CONTROLLER__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup irq Interrupt Handling Interfaces
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <metal/irq.h>
|
||||
#include <metal/list.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/** IRQ ANY ID */
|
||||
#define METAL_IRQ_ANY (-1)
|
||||
|
||||
/** IRQ state macro which will be passed to metal irq set state function
|
||||
* to indicate which state the caller want the IRQ to change to.
|
||||
*/
|
||||
#define METAL_IRQ_DISABLE 0U
|
||||
#define METAL_IRQ_ENABLE 1U
|
||||
|
||||
struct metal_irq_controller;
|
||||
|
||||
/**
|
||||
* @brief type of interrupt controller to set irq enable
|
||||
* @param[in] irq_cntr pointer to interrupt controller
|
||||
* @param[in] irq interrupt id
|
||||
* @param[in] enable IRQ state
|
||||
*/
|
||||
typedef void (*metal_irq_set_enable) (struct metal_irq_controller *irq_cntr,
|
||||
int irq, unsigned int enable);
|
||||
|
||||
/**
|
||||
* @brief type of controller specific registering interrupt function
|
||||
* @param[in] irq_cntr pointer to interrupt controller
|
||||
* @param[in] irq interrupt id
|
||||
* @param[in] hd interrupt handler
|
||||
* @param[in] arg argument which will be passed to the interrupt handler
|
||||
* @return 0 for success, negative value for failure
|
||||
*/
|
||||
typedef int (*metal_cntr_irq_register) (struct metal_irq_controller *irq_cntr,
|
||||
int irq, metal_irq_handler hd,
|
||||
void *arg);
|
||||
|
||||
/** Libmetal interrupt structure */
|
||||
struct metal_irq {
|
||||
metal_irq_handler hd; /**< Interrupt handler */
|
||||
void *arg; /**< Argument to pass to the interrupt handler */
|
||||
};
|
||||
|
||||
/** Libmetal interrupt controller structure */
|
||||
struct metal_irq_controller {
|
||||
int irq_base; /**< Start of IRQ number of the range managed by
|
||||
* the IRQ controller
|
||||
*/
|
||||
int irq_num; /**< Number of IRQs managed by the IRQ controller */
|
||||
void *arg; /**< Argument to pass to interrupt controller function */
|
||||
metal_irq_set_enable irq_set_enable; /**< function to set IRQ eanble */
|
||||
metal_cntr_irq_register irq_register; /**< function to register IRQ
|
||||
* handler
|
||||
*/
|
||||
struct metal_list node; /**< list node */
|
||||
struct metal_irq *irqs; /**< Array of IRQs managed by the controller */
|
||||
};
|
||||
|
||||
#define METAL_IRQ_CONTROLLER_DECLARE(_irq_controller, \
|
||||
_irq_base, _irq_num, \
|
||||
_arg, \
|
||||
_irq_set_enable, \
|
||||
_irq_register, \
|
||||
_irqs) \
|
||||
struct metal_irq_controller _irq_controller = { \
|
||||
.irq_base = _irq_base, \
|
||||
.irq_num = _irq_num, \
|
||||
.arg = _arg, \
|
||||
.irq_set_enable = _irq_set_enable, \
|
||||
.irq_register = _irq_register, \
|
||||
.irqs = _irqs,\
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief metal_irq_register_controller
|
||||
*
|
||||
* Register IRQ controller
|
||||
* This function will allocate IRQ ids if it was
|
||||
* not predefined in the irq controller. There is no
|
||||
* locking in the function, it is not supposed to be
|
||||
* called by multiple threads.
|
||||
*
|
||||
* @param[in] cntr Interrupt controller to register
|
||||
* @return 0 on success, or negative value for failure.
|
||||
*/
|
||||
int metal_irq_register_controller(struct metal_irq_controller *cntr);
|
||||
|
||||
/**
|
||||
* @brief metal_irq_handle
|
||||
*
|
||||
* Call registered IRQ handler
|
||||
*
|
||||
* @param[in] irq_data metal IRQ structure
|
||||
* @param[in] irq IRQ id which will be passed to handler
|
||||
* @return IRQ handler status
|
||||
*/
|
||||
static inline
|
||||
int metal_irq_handle(struct metal_irq *irq_data, int irq)
|
||||
{
|
||||
if (irq_data && irq_data->hd) {
|
||||
return irq_data->hd(irq, irq_data->arg);
|
||||
} else {
|
||||
return METAL_IRQ_NOT_HANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_IRQ__H__ */
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file list.h
|
||||
* @brief List primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_LIST__H__
|
||||
#define __METAL_LIST__H__
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup list List Primitives
|
||||
* @{
|
||||
*/
|
||||
|
||||
struct metal_list {
|
||||
struct metal_list *next, *prev;
|
||||
};
|
||||
|
||||
/*
|
||||
* METAL_INIT_LIST - used for initializing an list elmenet in a static struct
|
||||
* or global
|
||||
*/
|
||||
#define METAL_INIT_LIST(name) { .next = &name, .prev = &name }
|
||||
/*
|
||||
* METAL_DECLARE_LIST - used for defining and initializing a global or
|
||||
* static singleton list
|
||||
*/
|
||||
#define METAL_DECLARE_LIST(name) \
|
||||
struct metal_list name = METAL_INIT_LIST(name)
|
||||
|
||||
static inline void metal_list_init(struct metal_list *list)
|
||||
{
|
||||
list->prev = list;
|
||||
list->next = list;
|
||||
}
|
||||
|
||||
static inline void metal_list_add_before(struct metal_list *node,
|
||||
struct metal_list *new_node)
|
||||
{
|
||||
new_node->prev = node->prev;
|
||||
new_node->next = node;
|
||||
new_node->next->prev = new_node;
|
||||
new_node->prev->next = new_node;
|
||||
}
|
||||
|
||||
static inline void metal_list_add_after(struct metal_list *node,
|
||||
struct metal_list *new_node)
|
||||
{
|
||||
new_node->prev = node;
|
||||
new_node->next = node->next;
|
||||
new_node->next->prev = new_node;
|
||||
new_node->prev->next = new_node;
|
||||
}
|
||||
|
||||
static inline void metal_list_add_head(struct metal_list *list,
|
||||
struct metal_list *node)
|
||||
{
|
||||
metal_list_add_after(list, node);
|
||||
}
|
||||
|
||||
static inline void metal_list_add_tail(struct metal_list *list,
|
||||
struct metal_list *node)
|
||||
{
|
||||
metal_list_add_before(list, node);
|
||||
}
|
||||
|
||||
static inline int metal_list_is_empty(struct metal_list *list)
|
||||
{
|
||||
return list->next == list;
|
||||
}
|
||||
|
||||
static inline void metal_list_del(struct metal_list *node)
|
||||
{
|
||||
node->next->prev = node->prev;
|
||||
node->prev->next = node->next;
|
||||
node->prev = node;
|
||||
node->next = node;
|
||||
}
|
||||
|
||||
static inline struct metal_list *metal_list_first(struct metal_list *list)
|
||||
{
|
||||
return metal_list_is_empty(list) ? NULL : list->next;
|
||||
}
|
||||
|
||||
#define metal_list_for_each(list, node) \
|
||||
for ((node) = (list)->next; \
|
||||
(node) != (list); \
|
||||
(node) = (node)->next)
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_LIST__H__ */
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file log.h
|
||||
* @brief Logging support for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_METAL_LOG__H__
|
||||
#define __METAL_METAL_LOG__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup logging Library Logging Interfaces
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Log message priority levels for libmetal. */
|
||||
enum metal_log_level {
|
||||
METAL_LOG_EMERGENCY, /**< system is unusable. */
|
||||
METAL_LOG_ALERT, /**< action must be taken immediately. */
|
||||
METAL_LOG_CRITICAL, /**< critical conditions. */
|
||||
METAL_LOG_ERROR, /**< error conditions. */
|
||||
METAL_LOG_WARNING, /**< warning conditions. */
|
||||
METAL_LOG_NOTICE, /**< normal but significant condition. */
|
||||
METAL_LOG_INFO, /**< informational messages. */
|
||||
METAL_LOG_DEBUG, /**< debug-level messages. */
|
||||
};
|
||||
|
||||
/** Log message handler type. */
|
||||
typedef void (*metal_log_handler)(enum metal_log_level level,
|
||||
const char *format, ...);
|
||||
|
||||
/**
|
||||
* @brief Set libmetal log handler.
|
||||
* @param[in] handler log message handler.
|
||||
* @return 0 on success, or -errno on failure.
|
||||
*/
|
||||
extern void metal_set_log_handler(metal_log_handler handler);
|
||||
|
||||
/**
|
||||
* @brief Get the current libmetal log handler.
|
||||
* @return Current log handler.
|
||||
*/
|
||||
extern metal_log_handler metal_get_log_handler(void);
|
||||
|
||||
/**
|
||||
* @brief Set the level for libmetal logging.
|
||||
* @param[in] level log message level.
|
||||
*/
|
||||
extern void metal_set_log_level(enum metal_log_level level);
|
||||
|
||||
/**
|
||||
* @brief Get the current level for libmetal logging.
|
||||
* @return Current log level.
|
||||
*/
|
||||
extern enum metal_log_level metal_get_log_level(void);
|
||||
|
||||
/**
|
||||
* @brief Default libmetal log handler. This handler prints libmetal log
|
||||
* mesages to stderr.
|
||||
* @param[in] level log message level.
|
||||
* @param[in] format log message format string.
|
||||
* @return 0 on success, or -errno on failure.
|
||||
*/
|
||||
extern void metal_default_log_handler(enum metal_log_level level,
|
||||
const char *format, ...);
|
||||
|
||||
/**
|
||||
* Emit a log message if the log level permits.
|
||||
*
|
||||
* @param level Log level.
|
||||
* @param ... Format string and arguments.
|
||||
*/
|
||||
#define metal_log(level, ...) \
|
||||
((level <= _metal.common.log_level && _metal.common.log_handler) \
|
||||
? (void)_metal.common.log_handler(level, __VA_ARGS__) \
|
||||
: (void)0)
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef METAL_FREERTOS
|
||||
#include <metal/system/freertos/log.h>
|
||||
#else
|
||||
#include <metal/system/generic/log.h>
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_METAL_LOG__H__ */
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file mutex.h
|
||||
* @brief Mutex primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_MUTEX__H__
|
||||
#define __METAL_MUTEX__H__
|
||||
|
||||
#ifdef METAL_FREERTOS
|
||||
#include <metal/system/freertos/mutex.h>
|
||||
#else
|
||||
#include <metal/system/generic/mutex.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup mutex Mutex Interfaces
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Initialize a libmetal mutex.
|
||||
* @param[in] mutex Mutex to initialize.
|
||||
*/
|
||||
static inline void metal_mutex_init(metal_mutex_t *mutex)
|
||||
{
|
||||
__metal_mutex_init(mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deinitialize a libmetal mutex.
|
||||
* @param[in] mutex Mutex to deinitialize.
|
||||
*/
|
||||
static inline void metal_mutex_deinit(metal_mutex_t *mutex)
|
||||
{
|
||||
__metal_mutex_deinit(mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Try to acquire a mutex
|
||||
* @param[in] mutex Mutex to mutex.
|
||||
* @return 0 on failure to acquire, non-zero on success.
|
||||
*/
|
||||
static inline int metal_mutex_try_acquire(metal_mutex_t *mutex)
|
||||
{
|
||||
return __metal_mutex_try_acquire(mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Acquire a mutex
|
||||
* @param[in] mutex Mutex to mutex.
|
||||
*/
|
||||
static inline void metal_mutex_acquire(metal_mutex_t *mutex)
|
||||
{
|
||||
__metal_mutex_acquire(mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Release a previously acquired mutex.
|
||||
* @param[in] mutex Mutex to mutex.
|
||||
* @see metal_mutex_try_acquire, metal_mutex_acquire
|
||||
*/
|
||||
static inline void metal_mutex_release(metal_mutex_t *mutex)
|
||||
{
|
||||
__metal_mutex_release(mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checked if a mutex has been acquired.
|
||||
* @param[in] mutex mutex to check.
|
||||
* @see metal_mutex_try_acquire, metal_mutex_acquire
|
||||
*/
|
||||
static inline int metal_mutex_is_acquired(metal_mutex_t *mutex)
|
||||
{
|
||||
return __metal_mutex_is_acquired(mutex);
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_MUTEX__H__ */
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file arm/atomic.h
|
||||
* @brief Arm specific atomic primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_ARM_ATOMIC__H__
|
||||
#define __METAL_ARM_ATOMIC__H__
|
||||
|
||||
#endif /* __METAL_ARM_ATOMIC__H__ */
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file cpu.h
|
||||
* @brief CPU specific primatives
|
||||
*/
|
||||
|
||||
#ifndef __METAL_ARM_CPU__H__
|
||||
#define __METAL_ARM_CPU__H__
|
||||
|
||||
#define metal_cpu_yield()
|
||||
|
||||
#endif /* __METAL_ARM_CPU__H__ */
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file shmem.h
|
||||
* @brief Shared memory primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_SHMEM__H__
|
||||
#define __METAL_SHMEM__H__
|
||||
|
||||
#include <metal/io.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup shmem Shared Memory Interfaces
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Generic shared memory data structure. */
|
||||
struct metal_generic_shmem {
|
||||
const char *name;
|
||||
struct metal_io_region io;
|
||||
struct metal_list node;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Open a libmetal shared memory segment.
|
||||
*
|
||||
* Open a shared memory segment.
|
||||
*
|
||||
* @param[in] name Name of segment to open.
|
||||
* @param[in] size Size of segment.
|
||||
* @param[out] io I/O region handle, if successful.
|
||||
* @return 0 on success, or -errno on failure.
|
||||
*
|
||||
* @see metal_shmem_create
|
||||
*/
|
||||
extern int metal_shmem_open(const char *name, size_t size,
|
||||
struct metal_io_region **io);
|
||||
|
||||
/**
|
||||
* @brief Statically register a generic shared memory region.
|
||||
*
|
||||
* Shared memory regions may be statically registered at application
|
||||
* initialization, or may be dynamically opened. This interface is used for
|
||||
* static registration of regions. Subsequent calls to metal_shmem_open() look
|
||||
* up in this list of pre-registered regions.
|
||||
*
|
||||
* @param[in] shmem Generic shmem structure.
|
||||
* @return 0 on success, or -errno on failure.
|
||||
*/
|
||||
extern int metal_shmem_register_generic(struct metal_generic_shmem *shmem);
|
||||
|
||||
#ifdef METAL_INTERNAL
|
||||
|
||||
/**
|
||||
* @brief Open a statically registered shmem segment.
|
||||
*
|
||||
* This interface is meant for internal libmetal use within system specific
|
||||
* shmem implementations.
|
||||
*
|
||||
* @param[in] name Name of segment to open.
|
||||
* @param[in] size Size of segment.
|
||||
* @param[out] io I/O region handle, if successful.
|
||||
* @return 0 on success, or -errno on failure.
|
||||
*/
|
||||
int metal_shmem_open_generic(const char *name, size_t size,
|
||||
struct metal_io_region **result);
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_SHMEM__H__ */
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file sleep.h
|
||||
* @brief Sleep primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_SLEEP__H__
|
||||
#define __METAL_SLEEP__H__
|
||||
|
||||
#ifdef METAL_FREERTOS
|
||||
#include <metal/system/freertos/sleep.h>
|
||||
#else
|
||||
#include <metal/system/generic/sleep.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup sleep Sleep Interfaces
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief delay in microseconds
|
||||
* delay the next execution in the calling thread
|
||||
* fo usec microseconds.
|
||||
*
|
||||
* @param[in] usec microsecond intervals
|
||||
* @return 0 on success, non-zero for failures
|
||||
*/
|
||||
static inline int metal_sleep_usec(unsigned int usec)
|
||||
{
|
||||
return __metal_sleep_usec(usec);
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_SLEEP__H__ */
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file softirq.h
|
||||
* @brief Soft Interrupt handling primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_SOFTIRQ__H__
|
||||
#define __METAL_SOFTIRQ__H__
|
||||
|
||||
#include <metal/irq.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup soft irq Interrupt Handling Interfaces
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief metal_softirq_init
|
||||
*
|
||||
* Initialize libmetal soft IRQs controller
|
||||
*
|
||||
* @return 0 on success, or negative value for failure
|
||||
*/
|
||||
int metal_softirq_init(void);
|
||||
|
||||
/**
|
||||
* @brief metal_softirq_dispatch
|
||||
*
|
||||
* Dispatch the pending soft IRQs
|
||||
*/
|
||||
void metal_softirq_dispatch(void);
|
||||
|
||||
/**
|
||||
* @brief metal_softirq_allocate
|
||||
*
|
||||
* Allocate soft IRQs
|
||||
*
|
||||
* This function doesn't have any locking, it is not supposed
|
||||
* to be called by multiple threads.
|
||||
*
|
||||
* @param[in] num number of soft irqs requested
|
||||
* @return soft irq base for success, or negative value for failure
|
||||
*/
|
||||
int metal_softirq_allocate(int num);
|
||||
|
||||
/**
|
||||
* @brief metal_softirq_set
|
||||
*
|
||||
* Set soft IRQ to pending
|
||||
*
|
||||
* @param[in] irq soft IRQ ID to set
|
||||
*/
|
||||
void metal_softirq_set(int irq);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_SOFTIRQ__H__ */
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file spinlock.h
|
||||
* @brief Spinlock primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_SPINLOCK__H__
|
||||
#define __METAL_SPINLOCK__H__
|
||||
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/cpu.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup spinlock Spinlock Interfaces
|
||||
* @{
|
||||
*/
|
||||
|
||||
struct metal_spinlock {
|
||||
atomic_flag v;
|
||||
};
|
||||
|
||||
/** Static metal spinlock initialization. */
|
||||
#define METAL_SPINLOCK_INIT {ATOMIC_FLAG_INIT}
|
||||
|
||||
/**
|
||||
* @brief Initialize a libmetal spinlock.
|
||||
* @param[in] slock Spinlock to initialize.
|
||||
*/
|
||||
static inline void metal_spinlock_init(struct metal_spinlock *slock)
|
||||
{
|
||||
atomic_flag_clear(&slock->v);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Acquire a spinlock.
|
||||
* @param[in] slock Spinlock to acquire.
|
||||
* @see metal_spinlock_release
|
||||
*/
|
||||
static inline void metal_spinlock_acquire(struct metal_spinlock *slock)
|
||||
{
|
||||
while (atomic_flag_test_and_set(&slock->v)) {
|
||||
metal_cpu_yield();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Release a previously acquired spinlock.
|
||||
* @param[in] slock Spinlock to release.
|
||||
* @see metal_spinlock_acquire
|
||||
*/
|
||||
static inline void metal_spinlock_release(struct metal_spinlock *slock)
|
||||
{
|
||||
atomic_flag_clear(&slock->v);
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_SPINLOCK__H__ */
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file sys.h
|
||||
* @brief System primitives for libmetal.
|
||||
* @brief Top level include internal to libmetal library code.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_SYS__H__
|
||||
#define __METAL_SYS__H__
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <metal/log.h>
|
||||
#include <metal/list.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup system Top Level Interfaces
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Physical address type. */
|
||||
typedef unsigned long metal_phys_addr_t;
|
||||
|
||||
/** Interrupt request number. */
|
||||
typedef int metal_irq_t;
|
||||
|
||||
/** Bad offset into shared memory or I/O region. */
|
||||
#define METAL_BAD_OFFSET ((unsigned long)-1)
|
||||
|
||||
/** Bad physical address value. */
|
||||
#define METAL_BAD_PHYS ((metal_phys_addr_t)-1)
|
||||
|
||||
/** Bad virtual address value. */
|
||||
#define METAL_BAD_VA ((void *)-1)
|
||||
|
||||
/** Bad IRQ. */
|
||||
#define METAL_BAD_IRQ ((metal_irq_t)-1)
|
||||
|
||||
/**
|
||||
* Initialization configuration for libmetal.
|
||||
*/
|
||||
struct metal_init_params {
|
||||
|
||||
/** log message handler (defaults to stderr). */
|
||||
metal_log_handler log_handler;
|
||||
|
||||
/** default log message level (defaults to emergency). */
|
||||
enum metal_log_level log_level;
|
||||
};
|
||||
|
||||
/**
|
||||
* System independent runtime state for libmetal. This is part of a system
|
||||
* specific singleton data structure (@see _metal).
|
||||
*/
|
||||
struct metal_common_state {
|
||||
/** Current log level. */
|
||||
enum metal_log_level log_level;
|
||||
|
||||
/** Current log handler (null for none). */
|
||||
metal_log_handler log_handler;
|
||||
|
||||
/** List of registered buses. */
|
||||
struct metal_list bus_list;
|
||||
|
||||
/** Generic statically defined shared memory segments. */
|
||||
struct metal_list generic_shmem_list;
|
||||
|
||||
/** Generic statically defined devices. */
|
||||
struct metal_list generic_device_list;
|
||||
};
|
||||
|
||||
struct metal_state;
|
||||
|
||||
#ifdef METAL_FREERTOS
|
||||
#include <metal/system/freertos/sys.h>
|
||||
#else
|
||||
#include <metal/system/generic/sys.h>
|
||||
#endif
|
||||
|
||||
#ifndef METAL_INIT_DEFAULTS
|
||||
#define METAL_INIT_DEFAULTS \
|
||||
{ \
|
||||
.log_handler = metal_default_log_handler, \
|
||||
.log_level = METAL_LOG_INFO, \
|
||||
}
|
||||
#endif
|
||||
|
||||
/** System specific runtime data. */
|
||||
extern struct metal_state _metal;
|
||||
|
||||
/**
|
||||
* @brief Initialize libmetal.
|
||||
*
|
||||
* Initialize the libmetal library.
|
||||
*
|
||||
* @param[in] params Initialization params (@see metal_init_params).
|
||||
*
|
||||
* @return 0 on success, or -errno on failure.
|
||||
*
|
||||
* @see metal_finish
|
||||
*/
|
||||
extern int metal_init(const struct metal_init_params *params);
|
||||
|
||||
/**
|
||||
* @brief Shutdown libmetal.
|
||||
*
|
||||
* Shutdown the libmetal library, and release all reserved resources.
|
||||
*
|
||||
* @see metal_init
|
||||
*/
|
||||
extern void metal_finish(void);
|
||||
|
||||
#ifdef METAL_INTERNAL
|
||||
|
||||
/**
|
||||
* @brief libmetal system initialization.
|
||||
*
|
||||
* This function initializes libmetal on Linux or Generic platforms. This
|
||||
* involves obtaining necessary pieces of system information (sysfs mount path,
|
||||
* page size, etc.).
|
||||
*
|
||||
* @param[in] params Initialization parameters (@see metal_init_params).
|
||||
* @return 0 on success, or -errno on failure.
|
||||
*/
|
||||
extern int metal_sys_init(const struct metal_init_params *params);
|
||||
|
||||
/**
|
||||
* @brief libmetal system shutdown.
|
||||
*
|
||||
* This function shuts down and releases resources held by libmetal Linux or
|
||||
* Generic platform layers.
|
||||
*
|
||||
* @see metal_sys_init
|
||||
*/
|
||||
extern void metal_sys_finish(void);
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_SYS__H__ */
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file freertos/alloc.c
|
||||
* @brief FreeRTOS libmetal memory allocattion definitions.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_ALLOC__H__
|
||||
#error "Include metal/alloc.h instead of metal/freertos/alloc.h"
|
||||
#endif
|
||||
|
||||
#ifndef __METAL_FREERTOS_ALLOC__H__
|
||||
#define __METAL_FREERTOS_ALLOC__H__
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static inline void *metal_allocate_memory(unsigned int size)
|
||||
{
|
||||
return pvPortMalloc(size);
|
||||
}
|
||||
|
||||
static inline void metal_free_memory(void *ptr)
|
||||
{
|
||||
vPortFree(ptr);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_FREERTOS_ALLOC__H__ */
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file assert.h
|
||||
* @brief FreeRTOS assertion support.
|
||||
*/
|
||||
#ifndef __METAL_ASSERT__H__
|
||||
#error "Include metal/assert.h instead of metal/freertos/assert.h"
|
||||
#endif
|
||||
|
||||
#ifndef __METAL_FREERTOS_ASSERT__H__
|
||||
#define __METAL_FREERTOS_ASSERT__H__
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/**
|
||||
* @brief Assertion macro for FreeRTOS applications.
|
||||
* @param cond Condition to evaluate.
|
||||
*/
|
||||
#define metal_sys_assert(cond) assert(cond)
|
||||
|
||||
#endif /* __METAL_FREERTOS_ASSERT__H__ */
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Linaro Limited. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file freertos/cache.h
|
||||
* @brief FreeRTOS cache operation primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_CACHE__H__
|
||||
#error "Include metal/cache.h instead of metal/freertos/cache.h"
|
||||
#endif
|
||||
|
||||
#ifndef __METAL_FREERTOS_CACHE__H__
|
||||
#define __METAL_FREERTOS_CACHE__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern void metal_machine_cache_flush(void *addr, unsigned int len);
|
||||
extern void metal_machine_cache_invalidate(void *addr, unsigned int len);
|
||||
|
||||
static inline void __metal_cache_flush(void *addr, unsigned int len)
|
||||
{
|
||||
metal_machine_cache_flush(addr, len);
|
||||
}
|
||||
|
||||
static inline void __metal_cache_invalidate(void *addr, unsigned int len)
|
||||
{
|
||||
metal_machine_cache_invalidate(addr, len);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_FREERTOS_CACHE__H__ */
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file generic/condition.h
|
||||
* @brief Generic condition variable primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_CONDITION__H__
|
||||
#error "Include metal/condition.h instead of metal/freertos/condition.h"
|
||||
#endif
|
||||
|
||||
#ifndef __METAL_FREERTOS_CONDITION__H__
|
||||
#define __METAL_FREERTOS_CONDITION__H__
|
||||
|
||||
#include <metal/atomic.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct metal_condition {
|
||||
metal_mutex_t *m; /**< mutex.
|
||||
* The condition variable is attached to this mutex
|
||||
* when it is waiting. It is also used to check
|
||||
* correctness in case there are multiple waiters.
|
||||
*/
|
||||
|
||||
atomic_int v; /**< condition variable value. */
|
||||
};
|
||||
|
||||
/** Static metal condition variable initialization. */
|
||||
#define METAL_CONDITION_INIT { NULL, ATOMIC_VAR_INIT(0) }
|
||||
|
||||
static inline void metal_condition_init(struct metal_condition *cv)
|
||||
{
|
||||
/* TODO: Implement condition variable for FreeRTOS */
|
||||
(void)cv;
|
||||
}
|
||||
|
||||
static inline int metal_condition_signal(struct metal_condition *cv)
|
||||
{
|
||||
/* TODO: Implement condition variable for FreeRTOS */
|
||||
(void)cv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int metal_condition_broadcast(struct metal_condition *cv)
|
||||
{
|
||||
/* TODO: Implement condition variable for FreeRTOS */
|
||||
(void)cv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_FREERTOS_CONDITION__H__ */
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file freertos/io.h
|
||||
* @brief FreeRTOS specific io definitions.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_IO__H__
|
||||
#error "Include metal/io.h instead of metal/freertos/io.h"
|
||||
#endif
|
||||
|
||||
#ifndef __METAL_FREEROTS_IO__H__
|
||||
#define __METAL_FREEROTS_IO__H__
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef METAL_INTERNAL
|
||||
|
||||
/**
|
||||
* @brief memory mapping for an I/O region
|
||||
*/
|
||||
void metal_sys_io_mem_map(struct metal_io_region *io);
|
||||
|
||||
/**
|
||||
* @brief memory mapping
|
||||
*/
|
||||
void *metal_machine_io_mem_map(void *va, metal_phys_addr_t pa,
|
||||
size_t size, unsigned int flags);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_FREEROTS_IO__H__ */
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file freertos/irq.c
|
||||
* @brief FreeRTOS libmetal irq definitions.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_IRQ__H__
|
||||
#error "Include metal/irq.h instead of metal/freertos/irq.h"
|
||||
#endif
|
||||
|
||||
#ifndef __METAL_FREERTOS_IRQ__H__
|
||||
#define __METAL_FREERTOS_IRQ__H__
|
||||
|
||||
#endif /* __METAL_FREERTOS_IRQ__H__ */
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Linaro Limited. and Contributors. All rights reserved.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of Linaro nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file freertos/log.h
|
||||
* @brief FreeRTOS libmetal log handler definition.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_METAL_LOG__H__
|
||||
#error "Include metal/log.h instead of metal/freertos/log.h"
|
||||
#endif
|
||||
|
||||
#ifndef __METAL_FREERTOS_LOG__H__
|
||||
#define __METAL_FREERTOS_LOG__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_FREERTOS_LOG__H__ */
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file freertos/mutex.h
|
||||
* @brief FreeRTOS mutex primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_MUTEX__H__
|
||||
#error "Include metal/mutex.h instead of metal/freertos/mutex.h"
|
||||
#endif
|
||||
|
||||
#ifndef __METAL_FREERTOS_MUTEX__H__
|
||||
#define __METAL_FREERTOS_MUTEX__H__
|
||||
|
||||
#include <metal/assert.h>
|
||||
#include "FreeRTOS.h"
|
||||
#include "semphr.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
SemaphoreHandle_t m;
|
||||
} metal_mutex_t;
|
||||
|
||||
/*
|
||||
* METAL_MUTEX_INIT - used for initializing an mutex element in a static struct
|
||||
* or global
|
||||
*/
|
||||
#if defined(__GNUC__)
|
||||
#define METAL_MUTEX_INIT(m) { NULL }; \
|
||||
_Pragma("GCC warning\"static initialisation of the mutex is deprecated\"")
|
||||
#elif defined(__ICCARM__)
|
||||
#define DO_PRAGMA(x) _Pragma(#x)
|
||||
#define METAL_MUTEX_INIT(m) { NULL }; \
|
||||
DO_PRAGMA(message("Warning: static initialisation of the mutex is deprecated"))
|
||||
#else
|
||||
#define METAL_MUTEX_INIT(m) { NULL }
|
||||
#endif
|
||||
|
||||
/*
|
||||
* METAL_MUTEX_DEFINE - used for defining and initializing a global or
|
||||
* static singleton mutex
|
||||
*/
|
||||
#define METAL_MUTEX_DEFINE(m) metal_mutex_t m = METAL_MUTEX_INIT(m)
|
||||
|
||||
static inline void __metal_mutex_init(metal_mutex_t *mutex)
|
||||
{
|
||||
metal_assert(mutex);
|
||||
mutex->m = xSemaphoreCreateMutex();
|
||||
metal_assert(mutex->m != NULL);
|
||||
}
|
||||
|
||||
static inline void __metal_mutex_deinit(metal_mutex_t *mutex)
|
||||
{
|
||||
metal_assert(mutex && mutex->m != NULL);
|
||||
vSemaphoreDelete(mutex->m);
|
||||
mutex->m=NULL;
|
||||
}
|
||||
|
||||
static inline int __metal_mutex_try_acquire(metal_mutex_t *mutex)
|
||||
{
|
||||
metal_assert(mutex && mutex->m != NULL);
|
||||
return xSemaphoreTake(mutex->m, ( TickType_t ) 0 );
|
||||
}
|
||||
|
||||
static inline void __metal_mutex_acquire(metal_mutex_t *mutex)
|
||||
{
|
||||
metal_assert(mutex && mutex->m != NULL);
|
||||
xSemaphoreTake(mutex->m, portMAX_DELAY);
|
||||
}
|
||||
|
||||
static inline void __metal_mutex_release(metal_mutex_t *mutex)
|
||||
{
|
||||
metal_assert(mutex && mutex->m != NULL);
|
||||
xSemaphoreGive(mutex->m);
|
||||
}
|
||||
|
||||
static inline int __metal_mutex_is_acquired(metal_mutex_t *mutex)
|
||||
{
|
||||
metal_assert(mutex && mutex->m != NULL);
|
||||
return (NULL == xSemaphoreGetMutexHolder(mutex->m)) ? 0 : 1;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_FREERTOS_MUTEX__H__ */
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Linaro Limited. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file freertos/sleep.h
|
||||
* @brief FreeRTOS sleep primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_SLEEP__H__
|
||||
#error "Include metal/sleep.h instead of metal/freertos/sleep.h"
|
||||
#endif
|
||||
|
||||
#ifndef __METAL_FREERTOS_SLEEP__H__
|
||||
#define __METAL_FREERTOS_SLEEP__H__
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include <task.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static inline int __metal_sleep_usec(unsigned int usec)
|
||||
{
|
||||
const TickType_t xDelay = pdMS_TO_TICKS(usec/1000);
|
||||
|
||||
vTaskDelay(xDelay ? xDelay : 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_FREERTOS_SLEEP__H__ */
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file freertos/sys.h
|
||||
* @brief FreeRTOS system primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_SYS__H__
|
||||
#error "Include metal/sys.h instead of metal/freertos/sys.h"
|
||||
#endif
|
||||
|
||||
#ifndef __METAL_FREERTOS_SYS__H__
|
||||
#define __METAL_FREERTOS_SYS__H__
|
||||
|
||||
#include "./template/sys.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef METAL_MAX_DEVICE_REGIONS
|
||||
#define METAL_MAX_DEVICE_REGIONS 1
|
||||
#endif
|
||||
|
||||
/** Structure for FreeRTOS libmetal runtime state. */
|
||||
struct metal_state {
|
||||
|
||||
/** Common (system independent) data. */
|
||||
struct metal_common_state common;
|
||||
};
|
||||
|
||||
#ifdef METAL_INTERNAL
|
||||
|
||||
/**
|
||||
* @brief restore interrupts to state before disable_global_interrupt()
|
||||
*/
|
||||
void sys_irq_restore_enable(unsigned int flags);
|
||||
|
||||
/**
|
||||
* @brief disable all interrupts
|
||||
*/
|
||||
unsigned int sys_irq_save_disable(void);
|
||||
|
||||
#endif /* METAL_INTERNAL */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_FREERTOS_SYS__H__ */
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Linaro Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file freertos/template/sys.h
|
||||
* @brief freertos template system primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_FREERTOS_SYS__H__
|
||||
#error "Include metal/sys.h instead of metal/freertos/template/sys.h"
|
||||
#endif
|
||||
|
||||
#ifndef __METAL_FREERTOS_TEMPLATE_SYS__H__
|
||||
#define __METAL_FREERTOS_TEMPLATE_SYS__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef METAL_INTERNAL
|
||||
|
||||
void sys_irq_enable(unsigned int vector);
|
||||
|
||||
void sys_irq_disable(unsigned int vector);
|
||||
|
||||
#endif /* METAL_INTERNAL */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_FREERTOS_SYS__H__ */
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file generic/alloc.c
|
||||
* @brief generic libmetal memory allocattion definitions.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_ALLOC__H__
|
||||
#error "Include metal/alloc.h instead of metal/generic/alloc.h"
|
||||
#endif
|
||||
|
||||
#ifndef __METAL_GENERIC_ALLOC__H__
|
||||
#define __METAL_GENERIC_ALLOC__H__
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static inline void *metal_allocate_memory(unsigned int size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static inline void metal_free_memory(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_GENERIC_ALLOC__H__ */
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file assert.h
|
||||
* @brief Generic assertion support.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_ASSERT__H__
|
||||
#error "Include metal/assert.h instead of metal/generic/assert.h"
|
||||
#endif
|
||||
|
||||
#ifndef __METAL_GENERIC_ASSERT__H__
|
||||
#define __METAL_GENERIC_ASSERT__H__
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/**
|
||||
* @brief Assertion macro for bare-metal applications.
|
||||
* @param cond Condition to evaluate.
|
||||
*/
|
||||
#define metal_sys_assert(cond) assert(cond)
|
||||
|
||||
#endif /* __METAL_GENERIC_ASSERT__H__ */
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Linaro Limited. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file generic/cache.h
|
||||
* @brief generic cache operation primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_CACHE__H__
|
||||
#error "Include metal/cache.h instead of metal/generic/cache.h"
|
||||
#endif
|
||||
|
||||
#ifndef __METAL_GENERIC_CACHE__H__
|
||||
#define __METAL_GENERIC_CACHE__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern void metal_machine_cache_flush(void *addr, unsigned int len);
|
||||
extern void metal_machine_cache_invalidate(void *addr, unsigned int len);
|
||||
|
||||
static inline void __metal_cache_flush(void *addr, unsigned int len)
|
||||
{
|
||||
metal_machine_cache_flush(addr, len);
|
||||
}
|
||||
|
||||
static inline void __metal_cache_invalidate(void *addr, unsigned int len)
|
||||
{
|
||||
metal_machine_cache_invalidate(addr, len);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_GENERIC_CACHE__H__ */
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file generic/condition.h
|
||||
* @brief Generic condition variable primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_CONDITION__H__
|
||||
#error "Include metal/condition.h instead of metal/generic/condition.h"
|
||||
#endif
|
||||
|
||||
#ifndef __METAL_GENERIC_CONDITION__H__
|
||||
#define __METAL_GENERIC_CONDITION__H__
|
||||
|
||||
#include <metal/atomic.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <metal/errno.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct metal_condition {
|
||||
atomic_uintptr_t mptr; /**< mutex pointer.
|
||||
* The condition variable is attached to
|
||||
* this mutex when it is waiting.
|
||||
* It is also used to check correctness
|
||||
* in case there are multiple waiters.
|
||||
*/
|
||||
|
||||
atomic_int v; /**< condition variable value. */
|
||||
};
|
||||
|
||||
/** Static metal condition variable initialization. */
|
||||
#define METAL_CONDITION_INIT { ATOMIC_VAR_INIT(0), ATOMIC_VAR_INIT(0) }
|
||||
|
||||
static inline void metal_condition_init(struct metal_condition *cv)
|
||||
{
|
||||
atomic_init(&cv->mptr, 0);
|
||||
atomic_init(&cv->v, 0);
|
||||
}
|
||||
|
||||
static inline int metal_condition_signal(struct metal_condition *cv)
|
||||
{
|
||||
if (!cv)
|
||||
return -EINVAL;
|
||||
|
||||
/** wake up waiters if there are any. */
|
||||
atomic_fetch_add(&cv->v, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int metal_condition_broadcast(struct metal_condition *cv)
|
||||
{
|
||||
return metal_condition_signal(cv);
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_GENERIC_CONDITION__H__ */
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file generic/io.h
|
||||
* @brief Generic specific io definitions.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_IO__H__
|
||||
#error "Include metal/io.h instead of metal/generic/io.h"
|
||||
#endif
|
||||
|
||||
#ifndef __METAL_GENERIC_IO__H__
|
||||
#define __METAL_GENERIC_IO__H__
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef METAL_INTERNAL
|
||||
|
||||
/**
|
||||
* @brief memory mapping for an I/O region
|
||||
*/
|
||||
void metal_sys_io_mem_map(struct metal_io_region *io);
|
||||
|
||||
/**
|
||||
* @brief memory mapping
|
||||
*/
|
||||
void *metal_machine_io_mem_map(void *va, metal_phys_addr_t pa,
|
||||
size_t size, unsigned int flags);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_GENERIC_IO__H__ */
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file generic/irq.c
|
||||
* @brief Generic libmetal irq definitions.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_IRQ__H__
|
||||
#error "Include metal/irq.h instead of metal/generic/irq.h"
|
||||
#endif
|
||||
|
||||
#ifndef __METAL_GENERIC_IRQ__H__
|
||||
#define __METAL_GENERIC_IRQ__H__
|
||||
|
||||
#endif /* __METAL_GENERIC_IRQ__H__ */
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Linaro Limited. and Contributors. All rights reserved.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of Linaro nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file generic/log.h
|
||||
* @brief Generic libmetal log handler definition.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_METAL_LOG__H__
|
||||
#error "Include metal/log.h instead of metal/generic/log.h"
|
||||
#endif
|
||||
|
||||
#ifndef __METAL_GENERIC_LOG__H__
|
||||
#define __METAL_GENERIC_LOG__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_GENERIC_LOG__H__ */
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file generic/mutex.h
|
||||
* @brief Generic mutex primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_MUTEX__H__
|
||||
#error "Include metal/mutex.h instead of metal/generic/mutex.h"
|
||||
#endif
|
||||
|
||||
#ifndef __METAL_GENERIC_MUTEX__H__
|
||||
#define __METAL_GENERIC_MUTEX__H__
|
||||
|
||||
#include <metal/atomic.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
atomic_int v;
|
||||
} metal_mutex_t;
|
||||
|
||||
#define METAL_MUTEX_UNLOCKED 0
|
||||
#define METAL_MUTEX_LOCKED 1
|
||||
|
||||
/*
|
||||
* METAL_MUTEX_INIT - used for initializing an mutex element in a static struct
|
||||
* or global
|
||||
*/
|
||||
#define METAL_MUTEX_INIT(m) { ATOMIC_VAR_INIT(METAL_MUTEX_UNLOCKED) }
|
||||
/*
|
||||
* METAL_MUTEX_DEFINE - used for defining and initializing a global or
|
||||
* static singleton mutex
|
||||
*/
|
||||
#define METAL_MUTEX_DEFINE(m) metal_mutex_t m = METAL_MUTEX_INIT(m)
|
||||
|
||||
static inline void __metal_mutex_init(metal_mutex_t *mutex)
|
||||
{
|
||||
atomic_store(&mutex->v, METAL_MUTEX_UNLOCKED);
|
||||
}
|
||||
|
||||
static inline void __metal_mutex_deinit(metal_mutex_t *mutex)
|
||||
{
|
||||
(void)mutex;
|
||||
}
|
||||
|
||||
static inline int __metal_mutex_try_acquire(metal_mutex_t *mutex)
|
||||
{
|
||||
int unlocked = METAL_MUTEX_UNLOCKED;
|
||||
|
||||
if (atomic_compare_exchange_strong(&mutex->v, &unlocked,
|
||||
METAL_MUTEX_LOCKED)) {
|
||||
return 1; /* acquired */
|
||||
} else {
|
||||
return 0; /* not acquired */
|
||||
}
|
||||
}
|
||||
|
||||
static inline void __metal_mutex_acquire(metal_mutex_t *mutex)
|
||||
{
|
||||
int unlocked = METAL_MUTEX_UNLOCKED;
|
||||
|
||||
while (!atomic_compare_exchange_weak(&mutex->v, &unlocked,
|
||||
METAL_MUTEX_LOCKED)) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void __metal_mutex_release(metal_mutex_t *mutex)
|
||||
{
|
||||
atomic_store(&mutex->v, METAL_MUTEX_UNLOCKED);
|
||||
}
|
||||
|
||||
static inline int __metal_mutex_is_acquired(metal_mutex_t *mutex)
|
||||
{
|
||||
return atomic_load(&mutex->v);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_GENERIC_MUTEX__H__ */
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Linaro Limited. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file generic/sleep.h
|
||||
* @brief Generic sleep primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_SLEEP__H__
|
||||
#error "Include metal/sleep.h instead of metal/generic/sleep.h"
|
||||
#endif
|
||||
|
||||
#ifndef __METAL_GENERIC_SLEEP__H__
|
||||
#define __METAL_GENERIC_SLEEP__H__
|
||||
|
||||
#include <metal/utilities.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static inline int __metal_sleep_usec(unsigned int usec)
|
||||
{
|
||||
metal_unused(usec);
|
||||
/* Fix me */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_GENERIC_SLEEP__H__ */
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file generic/sys.h
|
||||
* @brief Generic system primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_SYS__H__
|
||||
#error "Include metal/sys.h instead of metal/generic/sys.h"
|
||||
#endif
|
||||
|
||||
#ifndef __METAL_GENERIC_SYS__H__
|
||||
#define __METAL_GENERIC_SYS__H__
|
||||
|
||||
#include <limits.h>
|
||||
#include <metal/errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "./template/sys.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef METAL_MAX_DEVICE_REGIONS
|
||||
#define METAL_MAX_DEVICE_REGIONS 1
|
||||
#endif
|
||||
|
||||
/** Structure of generic libmetal runtime state. */
|
||||
struct metal_state {
|
||||
|
||||
/** Common (system independent) data. */
|
||||
struct metal_common_state common;
|
||||
};
|
||||
|
||||
#ifdef METAL_INTERNAL
|
||||
|
||||
/**
|
||||
* @brief restore interrupts to state before disable_global_interrupt()
|
||||
*/
|
||||
void sys_irq_restore_enable(unsigned int flags);
|
||||
|
||||
/**
|
||||
* @brief disable all interrupts
|
||||
*/
|
||||
unsigned int sys_irq_save_disable(void);
|
||||
|
||||
#endif /* METAL_INTERNAL */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_GENERIC_SYS__H__ */
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Linaro Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file generic/template/sys.h
|
||||
* @brief generic template system primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_GENERIC_SYS__H__
|
||||
#error "Include metal/sys.h instead of metal/generic/template/sys.h"
|
||||
#endif
|
||||
|
||||
#ifndef __METAL_GENERIC_TEMPLATE_SYS__H__
|
||||
#define __METAL_GENERIC_TEMPLATE_SYS__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef METAL_INTERNAL
|
||||
|
||||
void sys_irq_enable(unsigned int vector);
|
||||
|
||||
void sys_irq_disable(unsigned int vector);
|
||||
|
||||
#endif /* METAL_INTERNAL */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_GENERIC_TEMPLATE_SYS__H__ */
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file time.h
|
||||
* @brief Time primitives for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_TIME__H__
|
||||
#define __METAL_TIME__H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <metal/sys.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup time TIME Interfaces
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief get timestamp
|
||||
* This function returns the timestampe as unsigned long long
|
||||
* value.
|
||||
*
|
||||
* @return timestamp
|
||||
*/
|
||||
unsigned long long metal_get_timestamp(void);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_TIME__H__ */
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file utilities.h
|
||||
* @brief Utility routines for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_UTILITIES__H__
|
||||
#define __METAL_UTILITIES__H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <metal/assert.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup utilities Simple Utilities
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Marker for unused function arguments/variables. */
|
||||
#define metal_unused(x) do { (x) = (x); } while (0)
|
||||
|
||||
/** Figure out number of elements in an array. */
|
||||
#define metal_dim(x) (sizeof(x) / sizeof(x[0]))
|
||||
|
||||
/** Minimum of two numbers (warning: multiple evaluation!). */
|
||||
#define metal_min(x, y) ((x) < (y) ? (x) : (y))
|
||||
|
||||
/** Maximum of two numbers (warning: multiple evaluation!). */
|
||||
#define metal_max(x, y) ((x) > (y) ? (x) : (y))
|
||||
|
||||
/** Sign of a number [-1, 0, or 1] (warning: multiple evaluation!). */
|
||||
#define metal_sign(x) ((x) < 0 ? -1 : ((x) > 0 ? 1 : 0))
|
||||
|
||||
/** Align 'size' down to a multiple of 'align' (must be a power of two). */
|
||||
#define metal_align_down(size, align) \
|
||||
((size) & ~((align) - 1))
|
||||
|
||||
/** Align 'size' up to a multiple of 'align' (must be a power of two). */
|
||||
#define metal_align_up(size, align) \
|
||||
metal_align_down((size) + (align) - 1, align)
|
||||
|
||||
/** Divide (and round down). */
|
||||
#define metal_div_round_down(num, den) \
|
||||
((num) / (den))
|
||||
|
||||
/** Divide (and round up). */
|
||||
#define metal_div_round_up(num, den) \
|
||||
metal_div_round_down((num) + (den) - 1, (den))
|
||||
|
||||
/** Align 'ptr' down to a multiple of 'align' (must be a power of two). */
|
||||
#define metal_ptr_align_down(ptr, align) \
|
||||
(void *)(metal_align_down((uintptr_t)(ptr), (uintptr_t)(align)))
|
||||
|
||||
/** Align 'ptr' up to a multiple of 'align' (must be a power of two). */
|
||||
#define metal_ptr_align_up(ptr, align) \
|
||||
(void *)(metal_align_up((uintptr_t)(ptr), (uintptr_t)(align)))
|
||||
|
||||
/** Compute offset of a field within a structure. */
|
||||
#define metal_offset_of(structure, member) \
|
||||
((uintptr_t)&(((structure *)0)->member))
|
||||
|
||||
/** Compute pointer to a structure given a pointer to one of its fields. */
|
||||
#define metal_container_of(ptr, structure, member) \
|
||||
(void *)((uintptr_t)(ptr) - metal_offset_of(structure, member))
|
||||
|
||||
#define METAL_BITS_PER_ULONG (CHAR_BIT * sizeof(unsigned long))
|
||||
|
||||
#define metal_bit(bit) (1UL << (bit))
|
||||
|
||||
#define metal_bitmap_longs(x) metal_div_round_up((x), METAL_BITS_PER_ULONG)
|
||||
|
||||
static inline void metal_bitmap_set_bit(unsigned long *bitmap, int bit)
|
||||
{
|
||||
bitmap[bit / METAL_BITS_PER_ULONG] |=
|
||||
metal_bit(bit & (METAL_BITS_PER_ULONG - 1));
|
||||
}
|
||||
|
||||
static inline int metal_bitmap_is_bit_set(unsigned long *bitmap, int bit)
|
||||
{
|
||||
return ((bitmap[bit / METAL_BITS_PER_ULONG] &
|
||||
metal_bit(bit & (METAL_BITS_PER_ULONG - 1))) == 0) ? 0 : 1;
|
||||
}
|
||||
|
||||
static inline void metal_bitmap_clear_bit(unsigned long *bitmap, int bit)
|
||||
{
|
||||
bitmap[bit / METAL_BITS_PER_ULONG] &=
|
||||
~metal_bit(bit & (METAL_BITS_PER_ULONG - 1));
|
||||
}
|
||||
|
||||
static inline int metal_bitmap_is_bit_clear(unsigned long *bitmap, int bit)
|
||||
{
|
||||
return !metal_bitmap_is_bit_set(bitmap, bit);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
metal_bitmap_next_set_bit(unsigned long *bitmap, unsigned int start,
|
||||
unsigned int max)
|
||||
{
|
||||
unsigned int bit;
|
||||
|
||||
for (bit = start;
|
||||
bit < max && !metal_bitmap_is_bit_set(bitmap, bit);
|
||||
bit++)
|
||||
;
|
||||
return bit;
|
||||
}
|
||||
|
||||
#define metal_bitmap_for_each_set_bit(bitmap, bit, max) \
|
||||
for ((bit) = metal_bitmap_next_set_bit((bitmap), 0, (max)); \
|
||||
(bit) < (max); \
|
||||
(bit) = metal_bitmap_next_set_bit((bitmap), (bit + 1), (max)))
|
||||
|
||||
static inline unsigned int
|
||||
metal_bitmap_next_clear_bit(unsigned long *bitmap, unsigned int start,
|
||||
unsigned int max)
|
||||
{
|
||||
unsigned int bit;
|
||||
|
||||
for (bit = start;
|
||||
bit < max && !metal_bitmap_is_bit_clear(bitmap, bit);
|
||||
bit++)
|
||||
;
|
||||
return bit;
|
||||
}
|
||||
|
||||
#define metal_bitmap_for_each_clear_bit(bitmap, bit, max) \
|
||||
for ((bit) = metal_bitmap_next_clear_bit((bitmap), 0, (max)); \
|
||||
(bit) < (max); \
|
||||
(bit) = metal_bitmap_next_clear_bit((bitmap), (bit + 1), (max)))
|
||||
|
||||
static inline unsigned long metal_log2(unsigned long in)
|
||||
{
|
||||
unsigned long result;
|
||||
|
||||
metal_assert((in & (in - 1)) == 0);
|
||||
|
||||
for (result = 0; (1UL << result) < in; result++)
|
||||
;
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_UTILITIES__H__ */
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file version.h
|
||||
* @brief Library version information for libmetal.
|
||||
*/
|
||||
|
||||
#ifndef __METAL_VERSION__H__
|
||||
#define __METAL_VERSION__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup versions Library Version Interfaces
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Library major version number.
|
||||
*
|
||||
* Return the major version number of the library linked into the application.
|
||||
* This is required to match the value of METAL_VER_MAJOR, which is the major
|
||||
* version of the library that the application was compiled against.
|
||||
*
|
||||
* @return Library major version number.
|
||||
* @see METAL_VER_MAJOR
|
||||
*/
|
||||
extern int metal_ver_major(void);
|
||||
|
||||
/**
|
||||
* @brief Library minor version number.
|
||||
*
|
||||
* Return the minor version number of the library linked into the application.
|
||||
* This could differ from the value of METAL_VER_MINOR, which is the minor
|
||||
* version of the library that the application was compiled against.
|
||||
*
|
||||
* @return Library minor version number.
|
||||
* @see METAL_VER_MINOR
|
||||
*/
|
||||
extern int metal_ver_minor(void);
|
||||
|
||||
/**
|
||||
* @brief Library patch level.
|
||||
*
|
||||
* Return the patch level of the library linked into the application. This
|
||||
* could differ from the value of METAL_VER_PATCH, which is the patch level of
|
||||
* the library that the application was compiled against.
|
||||
*
|
||||
* @return Library patch level.
|
||||
* @see METAL_VER_PATCH
|
||||
*/
|
||||
extern int metal_ver_patch(void);
|
||||
|
||||
/**
|
||||
* @brief Library version string.
|
||||
*
|
||||
* Return the version string of the library linked into the application. This
|
||||
* could differ from the value of METAL_VER, which is the version string of
|
||||
* the library that the application was compiled against.
|
||||
*
|
||||
* @return Library version string.
|
||||
* @see METAL_VER
|
||||
*/
|
||||
extern const char *metal_ver(void);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __METAL_VERSION__H__ */
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <metal/sys.h>
|
||||
|
||||
int metal_init(const struct metal_init_params *params)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
memset(&_metal, 0, sizeof(_metal));
|
||||
|
||||
_metal.common.log_handler = params->log_handler;
|
||||
_metal.common.log_level = params->log_level;
|
||||
|
||||
metal_list_init(&_metal.common.bus_list);
|
||||
metal_list_init(&_metal.common.generic_shmem_list);
|
||||
metal_list_init(&_metal.common.generic_device_list);
|
||||
|
||||
error = metal_sys_init(params);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void metal_finish(void)
|
||||
{
|
||||
metal_sys_finish();
|
||||
memset(&_metal, 0, sizeof(_metal));
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <metal/errno.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/sys.h>
|
||||
|
||||
void metal_io_init(struct metal_io_region *io, void *virt,
|
||||
const metal_phys_addr_t *physmap, size_t size,
|
||||
unsigned int page_shift, unsigned int mem_flags,
|
||||
const struct metal_io_ops *ops)
|
||||
{
|
||||
const struct metal_io_ops nops = {
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
io->virt = virt;
|
||||
io->physmap = physmap;
|
||||
io->size = size;
|
||||
io->page_shift = page_shift;
|
||||
if (page_shift >= sizeof(io->page_mask) * CHAR_BIT)
|
||||
/* avoid overflow */
|
||||
io->page_mask = -1UL;
|
||||
else
|
||||
io->page_mask = (1UL << page_shift) - 1UL;
|
||||
io->mem_flags = mem_flags;
|
||||
io->ops = ops ? *ops : nops;
|
||||
metal_sys_io_mem_map(io);
|
||||
}
|
||||
|
||||
int metal_io_block_read(struct metal_io_region *io, unsigned long offset,
|
||||
void *restrict dst, int len)
|
||||
{
|
||||
unsigned char *ptr = metal_io_virt(io, offset);
|
||||
unsigned char *dest = dst;
|
||||
int retlen;
|
||||
|
||||
if (!ptr)
|
||||
return -ERANGE;
|
||||
if ((offset + len) > io->size)
|
||||
len = io->size - offset;
|
||||
retlen = len;
|
||||
if (io->ops.block_read) {
|
||||
retlen = (*io->ops.block_read)(
|
||||
io, offset, dst, memory_order_seq_cst, len);
|
||||
} else {
|
||||
atomic_thread_fence(memory_order_seq_cst);
|
||||
while ( len && (
|
||||
((uintptr_t)dest % sizeof(int)) ||
|
||||
((uintptr_t)ptr % sizeof(int)))) {
|
||||
*(unsigned char *)dest =
|
||||
*(const unsigned char *)ptr;
|
||||
dest++;
|
||||
ptr++;
|
||||
len--;
|
||||
}
|
||||
for (; len >= (int)sizeof(int); dest += sizeof(int),
|
||||
ptr += sizeof(int),
|
||||
len -= sizeof(int))
|
||||
*(unsigned int *)dest = *(const unsigned int *)ptr;
|
||||
for (; len != 0; dest++, ptr++, len--)
|
||||
*(unsigned char *)dest =
|
||||
*(const unsigned char *)ptr;
|
||||
}
|
||||
return retlen;
|
||||
}
|
||||
|
||||
int metal_io_block_write(struct metal_io_region *io, unsigned long offset,
|
||||
const void *restrict src, int len)
|
||||
{
|
||||
unsigned char *ptr = metal_io_virt(io, offset);
|
||||
const unsigned char *source = src;
|
||||
int retlen;
|
||||
|
||||
if (!ptr)
|
||||
return -ERANGE;
|
||||
if ((offset + len) > io->size)
|
||||
len = io->size - offset;
|
||||
retlen = len;
|
||||
if (io->ops.block_write) {
|
||||
retlen = (*io->ops.block_write)(
|
||||
io, offset, src, memory_order_seq_cst, len);
|
||||
} else {
|
||||
while ( len && (
|
||||
((uintptr_t)ptr % sizeof(int)) ||
|
||||
((uintptr_t)source % sizeof(int)))) {
|
||||
*(unsigned char *)ptr =
|
||||
*(const unsigned char *)source;
|
||||
ptr++;
|
||||
source++;
|
||||
len--;
|
||||
}
|
||||
for (; len >= (int)sizeof(int); ptr += sizeof(int),
|
||||
source += sizeof(int),
|
||||
len -= sizeof(int))
|
||||
*(unsigned int *)ptr = *(const unsigned int *)source;
|
||||
for (; len != 0; ptr++, source++, len--)
|
||||
*(unsigned char *)ptr =
|
||||
*(const unsigned char *)source;
|
||||
atomic_thread_fence(memory_order_seq_cst);
|
||||
}
|
||||
return retlen;
|
||||
}
|
||||
|
||||
int metal_io_block_set(struct metal_io_region *io, unsigned long offset,
|
||||
unsigned char value, int len)
|
||||
{
|
||||
unsigned char *ptr = metal_io_virt(io, offset);
|
||||
int retlen = len;
|
||||
|
||||
if (!ptr)
|
||||
return -ERANGE;
|
||||
if ((offset + len) > io->size)
|
||||
len = io->size - offset;
|
||||
retlen = len;
|
||||
if (io->ops.block_set) {
|
||||
(*io->ops.block_set)(
|
||||
io, offset, value, memory_order_seq_cst, len);
|
||||
} else {
|
||||
unsigned int cint = value;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 1; i < sizeof(int); i++)
|
||||
cint |= ((unsigned int)value << (CHAR_BIT * i));
|
||||
|
||||
for (; len && ((uintptr_t)ptr % sizeof(int)); ptr++, len--)
|
||||
*(unsigned char *)ptr = (unsigned char) value;
|
||||
for (; len >= (int)sizeof(int); ptr += sizeof(int),
|
||||
len -= sizeof(int))
|
||||
*(unsigned int *)ptr = cint;
|
||||
for (; len != 0; ptr++, len--)
|
||||
*(unsigned char *)ptr = (unsigned char) value;
|
||||
atomic_thread_fence(memory_order_seq_cst);
|
||||
}
|
||||
return retlen;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <metal/log.h>
|
||||
#include <metal/sys.h>
|
||||
|
||||
void metal_default_log_handler(enum metal_log_level level,
|
||||
const char *format, ...)
|
||||
{
|
||||
#ifdef DEFAULT_LOGGER_ON
|
||||
char msg[1024];
|
||||
va_list args;
|
||||
static const char * const level_strs[] = {
|
||||
"metal: emergency: ",
|
||||
"metal: alert: ",
|
||||
"metal: critical: ",
|
||||
"metal: error: ",
|
||||
"metal: warning: ",
|
||||
"metal: notice: ",
|
||||
"metal: info: ",
|
||||
"metal: debug: ",
|
||||
};
|
||||
|
||||
va_start(args, format);
|
||||
vsnprintf(msg, sizeof(msg), format, args);
|
||||
va_end(args);
|
||||
|
||||
if (level <= METAL_LOG_EMERGENCY || level > METAL_LOG_DEBUG)
|
||||
level = METAL_LOG_EMERGENCY;
|
||||
|
||||
fprintf(stderr, "%s%s", level_strs[level], msg);
|
||||
#else
|
||||
(void)level;
|
||||
(void)format;
|
||||
#endif
|
||||
}
|
||||
|
||||
void metal_set_log_handler(metal_log_handler handler)
|
||||
{
|
||||
_metal.common.log_handler = handler;
|
||||
}
|
||||
|
||||
metal_log_handler metal_get_log_handler(void)
|
||||
{
|
||||
return _metal.common.log_handler;
|
||||
}
|
||||
|
||||
void metal_set_log_level(enum metal_log_level level)
|
||||
{
|
||||
_metal.common.log_level = level;
|
||||
}
|
||||
|
||||
enum metal_log_level metal_get_log_level(void)
|
||||
{
|
||||
return _metal.common.log_level;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file generic/shmem.c
|
||||
* @brief Generic libmetal shared memory handling.
|
||||
*/
|
||||
|
||||
#include <metal/assert.h>
|
||||
#include <metal/errno.h>
|
||||
#include <metal/shmem.h>
|
||||
#include <metal/sys.h>
|
||||
#include <metal/utilities.h>
|
||||
|
||||
int metal_shmem_register_generic(struct metal_generic_shmem *shmem)
|
||||
{
|
||||
/* Make sure that we can be found. */
|
||||
metal_assert(shmem->name && strlen(shmem->name) != 0);
|
||||
|
||||
/* Statically registered shmem regions cannot have a destructor. */
|
||||
metal_assert(!shmem->io.ops.close);
|
||||
|
||||
metal_list_add_tail(&_metal.common.generic_shmem_list,
|
||||
&shmem->node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int metal_shmem_open_generic(const char *name, size_t size,
|
||||
struct metal_io_region **result)
|
||||
{
|
||||
struct metal_generic_shmem *shmem;
|
||||
struct metal_list *node;
|
||||
|
||||
metal_list_for_each(&_metal.common.generic_shmem_list, node) {
|
||||
shmem = metal_container_of(node, struct metal_generic_shmem, node);
|
||||
if (strcmp(shmem->name, name) != 0)
|
||||
continue;
|
||||
if (size <= metal_io_region_size(&shmem->io)) {
|
||||
*result = &shmem->io;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file generic/condition.c
|
||||
* @brief Generic libmetal condition variable handling.
|
||||
*/
|
||||
|
||||
#include <metal/condition.h>
|
||||
#include <metal/irq.h>
|
||||
|
||||
extern void metal_generic_default_poll(void);
|
||||
|
||||
int metal_condition_wait(struct metal_condition *cv,
|
||||
metal_mutex_t *m)
|
||||
{
|
||||
uintptr_t tmpmptr = 0, mptr = (uintptr_t)m;
|
||||
int v;
|
||||
unsigned int flags;
|
||||
|
||||
/* Check if the mutex has been acquired */
|
||||
if (!cv || !m || !metal_mutex_is_acquired(m))
|
||||
return -EINVAL;
|
||||
|
||||
if (!atomic_compare_exchange_strong(&cv->mptr, &tmpmptr, mptr)) {
|
||||
if (tmpmptr != mptr)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
v = atomic_load(&cv->v);
|
||||
|
||||
/* Release the mutex first. */
|
||||
metal_mutex_release(m);
|
||||
do {
|
||||
flags = metal_irq_save_disable();
|
||||
if (atomic_load(&cv->v) != v) {
|
||||
metal_irq_restore_enable(flags);
|
||||
break;
|
||||
}
|
||||
metal_generic_default_poll();
|
||||
metal_irq_restore_enable(flags);
|
||||
} while (1);
|
||||
/* Acquire the mutex again. */
|
||||
metal_mutex_acquire(m);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file generic/device.c
|
||||
* @brief Generic libmetal device operations.
|
||||
*/
|
||||
|
||||
#include <metal/device.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/sys.h>
|
||||
#include <metal/utilities.h>
|
||||
|
||||
int metal_generic_dev_sys_open(struct metal_device *dev)
|
||||
{
|
||||
struct metal_io_region *io;
|
||||
unsigned int i;
|
||||
|
||||
/* map I/O memory regions */
|
||||
for (i = 0; i < dev->num_regions; i++) {
|
||||
io = &dev->regions[i];
|
||||
if (!io->size)
|
||||
break;
|
||||
metal_sys_io_mem_map(io);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file generic/init.c
|
||||
* @brief Generic libmetal initialization.
|
||||
*/
|
||||
|
||||
#include <metal/sys.h>
|
||||
#include <metal/utilities.h>
|
||||
#include <metal/device.h>
|
||||
|
||||
struct metal_state _metal;
|
||||
|
||||
int metal_sys_init(const struct metal_init_params *params)
|
||||
{
|
||||
metal_unused(params);
|
||||
metal_bus_register(&metal_generic_bus);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void metal_sys_finish(void)
|
||||
{
|
||||
metal_bus_unregister(&metal_generic_bus);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file generic/io.c
|
||||
* @brief Generic libmetal io operations
|
||||
*/
|
||||
|
||||
#include <metal/io.h>
|
||||
|
||||
void metal_sys_io_mem_map(struct metal_io_region *io)
|
||||
{
|
||||
unsigned long p;
|
||||
size_t psize;
|
||||
size_t *va;
|
||||
|
||||
va = io->virt;
|
||||
psize = (size_t)io->size;
|
||||
if (psize) {
|
||||
if (psize >> io->page_shift)
|
||||
psize = (size_t)1 << io->page_shift;
|
||||
for (p = 0; p <= (io->size >> io->page_shift); p++) {
|
||||
metal_machine_io_mem_map(va, io->physmap[p],
|
||||
psize, io->mem_flags);
|
||||
va += psize;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file generic/shmem.c
|
||||
* @brief Generic libmetal shared memory handling.
|
||||
*/
|
||||
|
||||
#include <metal/shmem.h>
|
||||
|
||||
int metal_shmem_open(const char *name, size_t size,
|
||||
struct metal_io_region **io)
|
||||
{
|
||||
return metal_shmem_open_generic(name, size, io);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Linaro Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file generic/template/sys.c
|
||||
* @brief machine specific system primitives implementation.
|
||||
*/
|
||||
|
||||
#include <metal/io.h>
|
||||
#include <metal/sys.h>
|
||||
#include <metal/utilities.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void sys_irq_restore_enable(unsigned int flags)
|
||||
{
|
||||
metal_unused(flags);
|
||||
/* Add implementation here */
|
||||
}
|
||||
|
||||
unsigned int sys_irq_save_disable(void)
|
||||
{
|
||||
return 0;
|
||||
/* Add implementation here */
|
||||
}
|
||||
|
||||
void sys_irq_enable(unsigned int vector)
|
||||
{
|
||||
metal_unused(vector);
|
||||
|
||||
/* Add implementation here */
|
||||
}
|
||||
|
||||
void sys_irq_disable(unsigned int vector)
|
||||
{
|
||||
metal_unused(vector);
|
||||
|
||||
/* Add implementation here */
|
||||
}
|
||||
|
||||
void metal_machine_cache_flush(void *addr, unsigned int len)
|
||||
{
|
||||
metal_unused(addr);
|
||||
metal_unused(len);
|
||||
|
||||
/* Add implementation here */
|
||||
}
|
||||
|
||||
void metal_machine_cache_invalidate(void *addr, unsigned int len)
|
||||
{
|
||||
metal_unused(addr);
|
||||
metal_unused(len);
|
||||
|
||||
/* Add implementation here */
|
||||
}
|
||||
|
||||
void metal_generic_default_poll(void)
|
||||
{
|
||||
/* Add implementation here */
|
||||
}
|
||||
|
||||
void *metal_machine_io_mem_map(void *va, metal_phys_addr_t pa,
|
||||
size_t size, unsigned int flags)
|
||||
{
|
||||
metal_unused(pa);
|
||||
metal_unused(size);
|
||||
metal_unused(flags);
|
||||
|
||||
/* Add implementation here */
|
||||
|
||||
return va;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file generic/time.c
|
||||
* @brief Generic libmetal time handling.
|
||||
*/
|
||||
|
||||
#include <metal/time.h>
|
||||
|
||||
unsigned long long metal_get_timestamp(void)
|
||||
{
|
||||
/* TODO: Implement timestamp for generic system */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
Software License Agreement (BSD 3-Clause License)
|
||||
========================================
|
||||
|
||||
Copyright (c) 2014, Mentor Graphics Corporation. All rights reserved.
|
||||
Copyright (c) 2015 - 2016 Xilinx, Inc. All rights reserved.
|
||||
Copyright (c) 2016 Freescale Semiconductor, Inc. All rights reserved
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of <the copyright holder> nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
BSD 2-Clause License
|
||||
-------------------------
|
||||
|
||||
Copyright (c) <year> <owner>. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Notes
|
||||
=========================================
|
||||
Use the following tag instead of the full license text in the individual files:
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
This enables machine processing of license information based on the SPDX
|
||||
License Identifiers that are here available: http://spdx.org/licenses/
|
||||
|
||||
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Mentor Graphics Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef ELF_LOADER_H_
|
||||
#define ELF_LOADER_H_
|
||||
|
||||
#include <openamp/remoteproc.h>
|
||||
#include <openamp/remoteproc_loader.h>
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ELF32 base types - 32-bit. */
|
||||
typedef uint32_t Elf32_Addr;
|
||||
typedef uint16_t Elf32_Half;
|
||||
typedef uint32_t Elf32_Off;
|
||||
typedef int32_t Elf32_Sword;
|
||||
typedef uint32_t Elf32_Word;
|
||||
|
||||
/* ELF64 base types - 64-bit. */
|
||||
typedef uint64_t Elf64_Addr;
|
||||
typedef uint16_t Elf64_Half;
|
||||
typedef uint64_t Elf64_Off;
|
||||
typedef int32_t Elf64_Sword;
|
||||
typedef uint32_t Elf64_Word;
|
||||
typedef uint64_t Elf64_Xword;
|
||||
typedef int64_t Elf64_Sxword;
|
||||
|
||||
/* Size of ELF identifier field in the ELF file header. */
|
||||
#define EI_NIDENT 16
|
||||
|
||||
/* ELF32 file header */
|
||||
typedef struct {
|
||||
unsigned char e_ident[EI_NIDENT];
|
||||
Elf32_Half e_type;
|
||||
Elf32_Half e_machine;
|
||||
Elf32_Word e_version;
|
||||
Elf32_Addr e_entry;
|
||||
Elf32_Off e_phoff;
|
||||
Elf32_Off e_shoff;
|
||||
Elf32_Word e_flags;
|
||||
Elf32_Half e_ehsize;
|
||||
Elf32_Half e_phentsize;
|
||||
Elf32_Half e_phnum;
|
||||
Elf32_Half e_shentsize;
|
||||
Elf32_Half e_shnum;
|
||||
Elf32_Half e_shstrndx;
|
||||
} Elf32_Ehdr;
|
||||
|
||||
/* ELF64 file header */
|
||||
typedef struct {
|
||||
unsigned char e_ident[EI_NIDENT];
|
||||
Elf64_Half e_type;
|
||||
Elf64_Half e_machine;
|
||||
Elf64_Word e_version;
|
||||
Elf64_Addr e_entry;
|
||||
Elf64_Off e_phoff;
|
||||
Elf64_Off e_shoff;
|
||||
Elf64_Word e_flags;
|
||||
Elf64_Half e_ehsize;
|
||||
Elf64_Half e_phentsize;
|
||||
Elf64_Half e_phnum;
|
||||
Elf64_Half e_shentsize;
|
||||
Elf64_Half e_shnum;
|
||||
Elf64_Half e_shstrndx;
|
||||
} Elf64_Ehdr;
|
||||
|
||||
/* e_ident */
|
||||
#define ET_NONE 0
|
||||
#define ET_REL 1 /* Re-locatable file */
|
||||
#define ET_EXEC 2 /* Executable file */
|
||||
#define ET_DYN 3 /* Shared object file */
|
||||
#define ET_CORE 4 /* Core file */
|
||||
#define ET_LOOS 0xfe00 /* Operating system-specific */
|
||||
#define ET_HIOS 0xfeff /* Operating system-specific */
|
||||
#define ET_LOPROC 0xff00 /* remote_proc-specific */
|
||||
#define ET_HIPROC 0xffff /* remote_proc-specific */
|
||||
|
||||
/* e_machine */
|
||||
#define EM_ARM 40 /* ARM/Thumb Architecture */
|
||||
|
||||
/* e_version */
|
||||
#define EV_CURRENT 1 /* Current version */
|
||||
|
||||
/* e_ident[] Identification Indexes */
|
||||
#define EI_MAG0 0 /* File identification */
|
||||
#define EI_MAG1 1 /* File identification */
|
||||
#define EI_MAG2 2 /* File identification */
|
||||
#define EI_MAG3 3 /* File identification */
|
||||
#define EI_CLASS 4 /* File class */
|
||||
#define EI_DATA 5 /* Data encoding */
|
||||
#define EI_VERSION 6 /* File version */
|
||||
#define EI_OSABI 7 /* Operating system/ABI identification */
|
||||
#define EI_ABIVERSION 8 /* ABI version */
|
||||
#define EI_PAD 9 /* Start of padding bytes */
|
||||
#define EI_NIDENT 16 /* Size of e_ident[] */
|
||||
|
||||
/*
|
||||
* EI_MAG0 to EI_MAG3 - A file's first 4 bytes hold amagic number, identifying
|
||||
* the file as an ELF object file
|
||||
*/
|
||||
#define ELFMAG0 0x7f /* e_ident[EI_MAG0] */
|
||||
#define ELFMAG1 'E' /* e_ident[EI_MAG1] */
|
||||
#define ELFMAG2 'L' /* e_ident[EI_MAG2] */
|
||||
#define ELFMAG3 'F' /* e_ident[EI_MAG3] */
|
||||
#define ELFMAG "\177ELF"
|
||||
#define SELFMAG 4
|
||||
|
||||
/*
|
||||
* EI_CLASS - The next byte, e_ident[EI_CLASS], identifies the file's class, or
|
||||
* capacity.
|
||||
*/
|
||||
#define ELFCLASSNONE 0 /* Invalid class */
|
||||
#define ELFCLASS32 1 /* 32-bit objects */
|
||||
#define ELFCLASS64 2 /* 64-bit objects */
|
||||
|
||||
/*
|
||||
* EI_DATA - Byte e_ident[EI_DATA] specifies the data encoding of the
|
||||
* remote_proc-specific data in the object file. The following encodings are
|
||||
* currently defined.
|
||||
*/
|
||||
#define ELFDATANONE 0 /* Invalid data encoding */
|
||||
#define ELFDATA2LSB 1 /* See Data encodings, below */
|
||||
#define ELFDATA2MSB 2 /* See Data encodings, below */
|
||||
|
||||
/* EI_OSABI - We do not define an OS specific ABI */
|
||||
#define ELFOSABI_NONE 0
|
||||
|
||||
/* ELF32 program header */
|
||||
typedef struct elf32_phdr {
|
||||
Elf32_Word p_type;
|
||||
Elf32_Off p_offset;
|
||||
Elf32_Addr p_vaddr;
|
||||
Elf32_Addr p_paddr;
|
||||
Elf32_Word p_filesz;
|
||||
Elf32_Word p_memsz;
|
||||
Elf32_Word p_flags;
|
||||
Elf32_Word p_align;
|
||||
} Elf32_Phdr;
|
||||
|
||||
/* ELF64 program header */
|
||||
typedef struct elf64_phdr {
|
||||
Elf64_Word p_type;
|
||||
Elf64_Word p_flags;
|
||||
Elf64_Off p_offset;
|
||||
Elf64_Addr p_vaddr;
|
||||
Elf64_Addr p_paddr;
|
||||
Elf64_Xword p_filesz;
|
||||
Elf64_Xword p_memsz;
|
||||
Elf64_Xword p_align;
|
||||
} Elf64_Phdr;
|
||||
|
||||
/* segment types */
|
||||
#define PT_NULL 0
|
||||
#define PT_LOAD 1
|
||||
#define PT_DYNAMIC 2
|
||||
#define PT_INTERP 3
|
||||
#define PT_NOTE 4
|
||||
#define PT_SHLIB 5
|
||||
#define PT_PHDR 6
|
||||
#define PT_TLS 7 /* Thread local storage segment */
|
||||
#define PT_LOOS 0x60000000 /* OS-specific */
|
||||
#define PT_HIOS 0x6fffffff /* OS-specific */
|
||||
#define PT_LOPROC 0x70000000
|
||||
#define PT_HIPROC 0x7fffffff
|
||||
|
||||
/* ELF32 section header. */
|
||||
typedef struct {
|
||||
Elf32_Word sh_name;
|
||||
Elf32_Word sh_type;
|
||||
Elf32_Word sh_flags;
|
||||
Elf32_Addr sh_addr;
|
||||
Elf32_Off sh_offset;
|
||||
Elf32_Word sh_size;
|
||||
Elf32_Word sh_link;
|
||||
Elf32_Word sh_info;
|
||||
Elf32_Word sh_addralign;
|
||||
Elf32_Word sh_entsize;
|
||||
} Elf32_Shdr;
|
||||
|
||||
/* ELF64 section header. */
|
||||
typedef struct {
|
||||
Elf64_Word sh_name;
|
||||
Elf64_Word sh_type;
|
||||
Elf64_Xword sh_flags;
|
||||
Elf64_Addr sh_addr;
|
||||
Elf64_Off sh_offset;
|
||||
Elf64_Xword sh_size;
|
||||
Elf64_Word sh_link;
|
||||
Elf64_Word sh_info;
|
||||
Elf64_Xword sh_addralign;
|
||||
Elf64_Xword sh_entsize;
|
||||
} Elf64_Shdr;
|
||||
|
||||
/* sh_type */
|
||||
#define SHT_NULL 0
|
||||
#define SHT_PROGBITS 1
|
||||
#define SHT_SYMTAB 2
|
||||
#define SHT_STRTAB 3
|
||||
#define SHT_RELA 4
|
||||
#define SHT_HASH 5
|
||||
#define SHT_DYNAMIC 6
|
||||
#define SHT_NOTE 7
|
||||
#define SHT_NOBITS 8
|
||||
#define SHT_REL 9
|
||||
#define SHT_SHLIB 10
|
||||
#define SHT_DYNSYM 11
|
||||
#define SHT_INIT_ARRAY 14
|
||||
#define SHT_FINI_ARRAY 15
|
||||
#define SHT_PREINIT_ARRAY 16
|
||||
#define SHT_GROUP 17
|
||||
#define SHT_SYMTAB_SHNDX 18
|
||||
#define SHT_LOOS 0x60000000
|
||||
#define SHT_HIOS 0x6fffffff
|
||||
#define SHT_LOPROC 0x70000000
|
||||
#define SHT_HIPROC 0x7fffffff
|
||||
#define SHT_LOUSER 0x80000000
|
||||
#define SHT_HIUSER 0xffffffff
|
||||
|
||||
/* sh_flags */
|
||||
#define SHF_WRITE 0x1
|
||||
#define SHF_ALLOC 0x2
|
||||
#define SHF_EXECINSTR 0x4
|
||||
#define SHF_MASKPROC 0xf0000000
|
||||
|
||||
/* Relocation entry (without addend) */
|
||||
typedef struct {
|
||||
Elf32_Addr r_offset;
|
||||
Elf32_Word r_info;
|
||||
} Elf32_Rel;
|
||||
|
||||
typedef struct {
|
||||
Elf64_Addr r_offset;
|
||||
Elf64_Xword r_info;
|
||||
} Elf64_Rel;
|
||||
|
||||
/* Relocation entry with addend */
|
||||
typedef struct {
|
||||
Elf32_Addr r_offset;
|
||||
Elf32_Word r_info;
|
||||
Elf32_Sword r_addend;
|
||||
} Elf32_Rela;
|
||||
|
||||
typedef struct elf64_rela {
|
||||
Elf64_Addr r_offset;
|
||||
Elf64_Xword r_info;
|
||||
Elf64_Sxword r_addend;
|
||||
} Elf64_Rela;
|
||||
|
||||
/* Macros to extract information from 'r_info' field of relocation entries */
|
||||
#define ELF32_R_SYM(i) ((i) >> 8)
|
||||
#define ELF32_R_TYPE(i) ((unsigned char)(i))
|
||||
#define ELF64_R_SYM(i) ((i) >> 32)
|
||||
#define ELF64_R_TYPE(i) ((i) & 0xffffffff)
|
||||
|
||||
/* Symbol table entry */
|
||||
typedef struct {
|
||||
Elf32_Word st_name;
|
||||
Elf32_Addr st_value;
|
||||
Elf32_Word st_size;
|
||||
unsigned char st_info;
|
||||
unsigned char st_other;
|
||||
Elf32_Half st_shndx;
|
||||
} Elf32_Sym;
|
||||
|
||||
typedef struct elf64_sym {
|
||||
Elf64_Word st_name;
|
||||
unsigned char st_info;
|
||||
unsigned char st_other;
|
||||
Elf64_Half st_shndx;
|
||||
Elf64_Addr st_value;
|
||||
Elf64_Xword st_size;
|
||||
} Elf64_Sym;
|
||||
|
||||
/* ARM specific dynamic relocation codes */
|
||||
#define R_ARM_GLOB_DAT 21 /* 0x15 */
|
||||
#define R_ARM_JUMP_SLOT 22 /* 0x16 */
|
||||
#define R_ARM_RELATIVE 23 /* 0x17 */
|
||||
#define R_ARM_ABS32 2 /* 0x02 */
|
||||
|
||||
/* ELF decoding information */
|
||||
struct elf32_info {
|
||||
Elf32_Ehdr ehdr;
|
||||
int load_state;
|
||||
Elf32_Phdr *phdrs;
|
||||
Elf32_Shdr *shdrs;
|
||||
void *shstrtab;
|
||||
};
|
||||
|
||||
struct elf64_info {
|
||||
Elf64_Ehdr ehdr;
|
||||
int load_state;
|
||||
Elf64_Phdr *phdrs;
|
||||
Elf64_Shdr *shdrs;
|
||||
void *shstrtab;
|
||||
};
|
||||
|
||||
#define ELF_STATE_INIT 0x0L
|
||||
#define ELF_STATE_WAIT_FOR_PHDRS 0x100L
|
||||
#define ELF_STATE_WAIT_FOR_SHDRS 0x200L
|
||||
#define ELF_STATE_WAIT_FOR_SHSTRTAB 0x400L
|
||||
#define ELF_STATE_HDRS_COMPLETE 0x800L
|
||||
#define ELF_STATE_MASK 0xFF00L
|
||||
#define ELF_NEXT_SEGMENT_MASK 0x00FFL
|
||||
|
||||
extern struct loader_ops elf_ops;
|
||||
|
||||
/**
|
||||
* elf_identify - check if it is an ELF file
|
||||
*
|
||||
* It will check if the input image header is an ELF header.
|
||||
*
|
||||
* @img_data: firmware private data which will be passed to user defined loader
|
||||
* operations
|
||||
* @len: firmware header length
|
||||
*
|
||||
* return 0 for success or negative value for failure.
|
||||
*/
|
||||
int elf_identify(const void *img_data, size_t len);
|
||||
|
||||
/**
|
||||
* elf_load_header - Load ELF headers
|
||||
*
|
||||
* It will get the ELF header, the program header, and the section header.
|
||||
*
|
||||
* @img_data: image data
|
||||
* @offset: input image data offset to the start of image file
|
||||
* @len: input image data length
|
||||
* @img_info: pointer to store image information data
|
||||
* @last_load_state: last state return by this function
|
||||
* @noffset: pointer to next offset required by loading ELF header
|
||||
* @nlen: pointer to next data length required by loading ELF header
|
||||
*
|
||||
* return ELF loading header state, or negative value for failure
|
||||
*/
|
||||
int elf_load_header(const void *img_data, size_t offset, size_t len,
|
||||
void **img_info, int last_load_state,
|
||||
size_t *noffset, size_t *nlen);
|
||||
|
||||
/**
|
||||
* elf_load - load ELF data
|
||||
*
|
||||
* It will parse the ELF image and return the target device address,
|
||||
* offset to the start of the ELF image of the data to load and the
|
||||
* length of the data to load.
|
||||
*
|
||||
* @rproc: pointer to remoteproc instance
|
||||
* @img_data: image data which will passed to the function.
|
||||
* it can be NULL, if image data doesn't need to be handled
|
||||
* by the load function. E.g. binary data which was
|
||||
* loaded to the target memory.
|
||||
* @offset: last loaded image data offset to the start of image file
|
||||
* @len: last loaded image data length
|
||||
* @img_info: pointer to store image information data
|
||||
* @last_load_state: the returned state of the last function call.
|
||||
* @da: target device address, if the data to load is not for target memory
|
||||
* the da will be set to ANY.
|
||||
* @noffset: pointer to next offset required by loading ELF header
|
||||
* @nlen: pointer to next data length required by loading ELF header
|
||||
* @padding: value to pad it is possible that a size of a segment in memory
|
||||
* is larger than what it is in the ELF image. e.g. a segment
|
||||
* can have stack section .bss. It doesn't need to copy image file
|
||||
* space, in this case, it will be packed with 0.
|
||||
* @nmemsize: pointer to next data target memory size. The size of a segment
|
||||
* in the target memory can be larger than the its size in the
|
||||
* image file.
|
||||
*
|
||||
* return 0 for success, otherwise negative value for failure
|
||||
*/
|
||||
int elf_load(struct remoteproc *rproc, const void *img_data,
|
||||
size_t offset, size_t len,
|
||||
void **img_info, int last_load_state,
|
||||
metal_phys_addr_t *da,
|
||||
size_t *noffset, size_t *nlen,
|
||||
unsigned char *padding, size_t *nmemsize);
|
||||
|
||||
/**
|
||||
* elf_release - Release ELF image information
|
||||
*
|
||||
* It will release ELF image information data.
|
||||
*
|
||||
* @img_info: pointer to ELF image information
|
||||
*/
|
||||
void elf_release(void *img_info);
|
||||
|
||||
/**
|
||||
* elf_get_entry - Get entry point
|
||||
*
|
||||
* It will return entry point specified in the ELF file.
|
||||
*
|
||||
* @img_info: pointer to ELF image information
|
||||
*
|
||||
* return entry address
|
||||
*/
|
||||
metal_phys_addr_t elf_get_entry(void *img_info);
|
||||
|
||||
/**
|
||||
* elf_locate_rsc_table - locate the resource table information
|
||||
*
|
||||
* It will return the length of the resource table, and the device address of
|
||||
* the resource table.
|
||||
*
|
||||
* @img_info: pointer to ELF image information
|
||||
* @da: pointer to the device address
|
||||
* @offset: pointer to the offset to in the ELF image of the resource
|
||||
* table section.
|
||||
* @size: pointer to the size of the resource table section.
|
||||
*
|
||||
* return 0 if successfully locate the resource table, negative value for
|
||||
* failure.
|
||||
*/
|
||||
int elf_locate_rsc_table(void *img_info, metal_phys_addr_t *da,
|
||||
size_t *offset, size_t *size);
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ELF_LOADER_H_ */
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Mentor Graphics Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef OPEN_AMP_H_
|
||||
#define OPEN_AMP_H_
|
||||
|
||||
#include <openamp/rpmsg.h>
|
||||
#include <openamp/rpmsg_virtio.h>
|
||||
#include <openamp/remoteproc.h>
|
||||
#include <openamp/remoteproc_virtio.h>
|
||||
|
||||
#endif /* OPEN_AMP_H_ */
|
||||
@@ -0,0 +1,841 @@
|
||||
/*
|
||||
* Remoteproc Framework
|
||||
*
|
||||
* Copyright(c) 2018 Xilinx Ltd.
|
||||
* Copyright(c) 2011 Texas Instruments, Inc.
|
||||
* Copyright(c) 2011 Google, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef REMOTEPROC_H
|
||||
#define REMOTEPROC_H
|
||||
|
||||
#include <metal/io.h>
|
||||
#include <metal/mutex.h>
|
||||
#include <metal/compiler.h>
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define RSC_NOTIFY_ID_ANY 0xFFFFFFFFUL
|
||||
|
||||
#define RPROC_MAX_NAME_LEN 32
|
||||
|
||||
/**
|
||||
* struct resource_table - firmware resource table header
|
||||
* @ver: version number
|
||||
* @num: number of resource entries
|
||||
* @reserved: reserved (must be zero)
|
||||
* @offset: array of offsets pointing at the various resource entries
|
||||
*
|
||||
* A resource table is essentially a list of system resources required
|
||||
* by the remote remoteproc. It may also include configuration entries.
|
||||
* If needed, the remote remoteproc firmware should contain this table
|
||||
* as a dedicated ".resource_table" ELF section.
|
||||
*
|
||||
* Some resources entries are mere announcements, where the host is informed
|
||||
* of specific remoteproc configuration. Other entries require the host to
|
||||
* do something (e.g. allocate a system resource). Sometimes a negotiation
|
||||
* is expected, where the firmware requests a resource, and once allocated,
|
||||
* the host should provide back its details (e.g. address of an allocated
|
||||
* memory region).
|
||||
*
|
||||
* The header of the resource table, as expressed by this structure,
|
||||
* contains a version number (should we need to change this format in the
|
||||
* future), the number of available resource entries, and their offsets
|
||||
* in the table.
|
||||
*
|
||||
* Immediately following this header are the resource entries themselves,
|
||||
* each of which begins with a resource entry header (as described below).
|
||||
*/
|
||||
METAL_PACKED_BEGIN
|
||||
struct resource_table {
|
||||
uint32_t ver;
|
||||
uint32_t num;
|
||||
uint32_t reserved[2];
|
||||
uint32_t offset[0];
|
||||
} METAL_PACKED_END;
|
||||
|
||||
/**
|
||||
* struct fw_rsc_hdr - firmware resource entry header
|
||||
* @type: resource type
|
||||
* @data: resource data
|
||||
*
|
||||
* Every resource entry begins with a 'struct fw_rsc_hdr' header providing
|
||||
* its @type. The content of the entry itself will immediately follow
|
||||
* this header, and it should be parsed according to the resource type.
|
||||
*/
|
||||
METAL_PACKED_BEGIN
|
||||
struct fw_rsc_hdr {
|
||||
uint32_t type;
|
||||
uint8_t data[0];
|
||||
} METAL_PACKED_END;
|
||||
|
||||
/**
|
||||
* enum fw_resource_type - types of resource entries
|
||||
*
|
||||
* @RSC_CARVEOUT: request for allocation of a physically contiguous
|
||||
* memory region.
|
||||
* @RSC_DEVMEM: request to iommu_map a memory-based peripheral.
|
||||
* @RSC_TRACE: announces the availability of a trace buffer into which
|
||||
* the remote remoteproc will be writing logs.
|
||||
* @RSC_VDEV: declare support for a virtio device, and serve as its
|
||||
* virtio header.
|
||||
* @RSC_VENDOR_START: start of the vendor specific resource types range
|
||||
* @RSC_VENDOR_END : end of the vendor specific resource types range
|
||||
* @RSC_LAST: just keep this one at the end
|
||||
*
|
||||
* For more details regarding a specific resource type, please see its
|
||||
* dedicated structure below.
|
||||
*
|
||||
* Please note that these values are used as indices to the rproc_handle_rsc
|
||||
* lookup table, so please keep them sane. Moreover, @RSC_LAST is used to
|
||||
* check the validity of an index before the lookup table is accessed, so
|
||||
* please update it as needed.
|
||||
*/
|
||||
enum fw_resource_type {
|
||||
RSC_CARVEOUT = 0,
|
||||
RSC_DEVMEM = 1,
|
||||
RSC_TRACE = 2,
|
||||
RSC_VDEV = 3,
|
||||
RSC_LAST = 4,
|
||||
RSC_VENDOR_START = 128,
|
||||
RSC_VENDOR_END = 512,
|
||||
};
|
||||
|
||||
#define FW_RSC_U64_ADDR_ANY 0xFFFFFFFFFFFFFFFFUL
|
||||
#define FW_RSC_U32_ADDR_ANY 0xFFFFFFFFUL
|
||||
|
||||
/**
|
||||
* struct fw_rsc_carveout - physically contiguous memory request
|
||||
* @da: device address
|
||||
* @pa: physical address
|
||||
* @len: length (in bytes)
|
||||
* @flags: iommu protection flags
|
||||
* @reserved: reserved (must be zero)
|
||||
* @name: human-readable name of the requested memory region
|
||||
*
|
||||
* This resource entry requests the host to allocate a physically contiguous
|
||||
* memory region.
|
||||
*
|
||||
* These request entries should precede other firmware resource entries,
|
||||
* as other entries might request placing other data objects inside
|
||||
* these memory regions (e.g. data/code segments, trace resource entries, ...).
|
||||
*
|
||||
* Allocating memory this way helps utilizing the reserved physical memory
|
||||
* (e.g. CMA) more efficiently, and also minimizes the number of TLB entries
|
||||
* needed to map it (in case @rproc is using an IOMMU). Reducing the TLB
|
||||
* pressure is important; it may have a substantial impact on performance.
|
||||
*
|
||||
* If the firmware is compiled with static addresses, then @da should specify
|
||||
* the expected device address of this memory region. If @da is set to
|
||||
* FW_RSC_ADDR_ANY, then the host will dynamically allocate it, and then
|
||||
* overwrite @da with the dynamically allocated address.
|
||||
*
|
||||
* We will always use @da to negotiate the device addresses, even if it
|
||||
* isn't using an iommu. In that case, though, it will obviously contain
|
||||
* physical addresses.
|
||||
*
|
||||
* Some remote remoteprocs needs to know the allocated physical address
|
||||
* even if they do use an iommu. This is needed, e.g., if they control
|
||||
* hardware accelerators which access the physical memory directly (this
|
||||
* is the case with OMAP4 for instance). In that case, the host will
|
||||
* overwrite @pa with the dynamically allocated physical address.
|
||||
* Generally we don't want to expose physical addresses if we don't have to
|
||||
* (remote remoteprocs are generally _not_ trusted), so we might want to
|
||||
* change this to happen _only_ when explicitly required by the hardware.
|
||||
*
|
||||
* @flags is used to provide IOMMU protection flags, and @name should
|
||||
* (optionally) contain a human readable name of this carveout region
|
||||
* (mainly for debugging purposes).
|
||||
*/
|
||||
METAL_PACKED_BEGIN
|
||||
struct fw_rsc_carveout {
|
||||
uint32_t type;
|
||||
uint32_t da;
|
||||
uint32_t pa;
|
||||
uint32_t len;
|
||||
uint32_t flags;
|
||||
uint32_t reserved;
|
||||
uint8_t name[RPROC_MAX_NAME_LEN];
|
||||
} METAL_PACKED_END;
|
||||
|
||||
/**
|
||||
* struct fw_rsc_devmem - iommu mapping request
|
||||
* @da: device address
|
||||
* @pa: physical address
|
||||
* @len: length (in bytes)
|
||||
* @flags: iommu protection flags
|
||||
* @reserved: reserved (must be zero)
|
||||
* @name: human-readable name of the requested region to be mapped
|
||||
*
|
||||
* This resource entry requests the host to iommu map a physically contiguous
|
||||
* memory region. This is needed in case the remote remoteproc requires
|
||||
* access to certain memory-based peripherals; _never_ use it to access
|
||||
* regular memory.
|
||||
*
|
||||
* This is obviously only needed if the remote remoteproc is accessing memory
|
||||
* via an iommu.
|
||||
*
|
||||
* @da should specify the required device address, @pa should specify
|
||||
* the physical address we want to map, @len should specify the size of
|
||||
* the mapping and @flags is the IOMMU protection flags. As always, @name may
|
||||
* (optionally) contain a human readable name of this mapping (mainly for
|
||||
* debugging purposes).
|
||||
*
|
||||
* Note: at this point we just "trust" those devmem entries to contain valid
|
||||
* physical addresses, but this isn't safe and will be changed: eventually we
|
||||
* want remoteproc implementations to provide us ranges of physical addresses
|
||||
* the firmware is allowed to request, and not allow firmwares to request
|
||||
* access to physical addresses that are outside those ranges.
|
||||
*/
|
||||
METAL_PACKED_BEGIN
|
||||
struct fw_rsc_devmem {
|
||||
uint32_t type;
|
||||
uint32_t da;
|
||||
uint32_t pa;
|
||||
uint32_t len;
|
||||
uint32_t flags;
|
||||
uint32_t reserved;
|
||||
uint8_t name[RPROC_MAX_NAME_LEN];
|
||||
} METAL_PACKED_END;
|
||||
|
||||
/**
|
||||
* struct fw_rsc_trace - trace buffer declaration
|
||||
* @da: device address
|
||||
* @len: length (in bytes)
|
||||
* @reserved: reserved (must be zero)
|
||||
* @name: human-readable name of the trace buffer
|
||||
*
|
||||
* This resource entry provides the host information about a trace buffer
|
||||
* into which the remote remoteproc will write log messages.
|
||||
*
|
||||
* @da specifies the device address of the buffer, @len specifies
|
||||
* its size, and @name may contain a human readable name of the trace buffer.
|
||||
*
|
||||
* After booting the remote remoteproc, the trace buffers are exposed to the
|
||||
* user via debugfs entries (called trace0, trace1, etc..).
|
||||
*/
|
||||
METAL_PACKED_BEGIN
|
||||
struct fw_rsc_trace {
|
||||
uint32_t type;
|
||||
uint32_t da;
|
||||
uint32_t len;
|
||||
uint32_t reserved;
|
||||
uint8_t name[RPROC_MAX_NAME_LEN];
|
||||
} METAL_PACKED_END;
|
||||
|
||||
/**
|
||||
* struct fw_rsc_vdev_vring - vring descriptor entry
|
||||
* @da: device address
|
||||
* @align: the alignment between the consumer and producer parts of the vring
|
||||
* @num: num of buffers supported by this vring (must be power of two)
|
||||
* @notifyid is a unique rproc-wide notify index for this vring. This notify
|
||||
* index is used when kicking a remote remoteproc, to let it know that this
|
||||
* vring is triggered.
|
||||
* @reserved: reserved (must be zero)
|
||||
*
|
||||
* This descriptor is not a resource entry by itself; it is part of the
|
||||
* vdev resource type (see below).
|
||||
*
|
||||
* Note that @da should either contain the device address where
|
||||
* the remote remoteproc is expecting the vring, or indicate that
|
||||
* dynamically allocation of the vring's device address is supported.
|
||||
*/
|
||||
METAL_PACKED_BEGIN
|
||||
struct fw_rsc_vdev_vring {
|
||||
uint32_t da;
|
||||
uint32_t align;
|
||||
uint32_t num;
|
||||
uint32_t notifyid;
|
||||
uint32_t reserved;
|
||||
} METAL_PACKED_END;
|
||||
|
||||
/**
|
||||
* struct fw_rsc_vdev - virtio device header
|
||||
* @id: virtio device id (as in virtio_ids.h)
|
||||
* @notifyid is a unique rproc-wide notify index for this vdev. This notify
|
||||
* index is used when kicking a remote remoteproc, to let it know that the
|
||||
* status/features of this vdev have changes.
|
||||
* @dfeatures specifies the virtio device features supported by the firmware
|
||||
* @gfeatures is a place holder used by the host to write back the
|
||||
* negotiated features that are supported by both sides.
|
||||
* @config_len is the size of the virtio config space of this vdev. The config
|
||||
* space lies in the resource table immediate after this vdev header.
|
||||
* @status is a place holder where the host will indicate its virtio progress.
|
||||
* @num_of_vrings indicates how many vrings are described in this vdev header
|
||||
* @reserved: reserved (must be zero)
|
||||
* @vring is an array of @num_of_vrings entries of 'struct fw_rsc_vdev_vring'.
|
||||
*
|
||||
* This resource is a virtio device header: it provides information about
|
||||
* the vdev, and is then used by the host and its peer remote remoteprocs
|
||||
* to negotiate and share certain virtio properties.
|
||||
*
|
||||
* By providing this resource entry, the firmware essentially asks remoteproc
|
||||
* to statically allocate a vdev upon registration of the rproc (dynamic vdev
|
||||
* allocation is not yet supported).
|
||||
*
|
||||
* Note: unlike virtualization systems, the term 'host' here means
|
||||
* the Linux side which is running remoteproc to control the remote
|
||||
* remoteprocs. We use the name 'gfeatures' to comply with virtio's terms,
|
||||
* though there isn't really any virtualized guest OS here: it's the host
|
||||
* which is responsible for negotiating the final features.
|
||||
* Yeah, it's a bit confusing.
|
||||
*
|
||||
* Note: immediately following this structure is the virtio config space for
|
||||
* this vdev (which is specific to the vdev; for more info, read the virtio
|
||||
* spec). the size of the config space is specified by @config_len.
|
||||
*/
|
||||
METAL_PACKED_BEGIN
|
||||
struct fw_rsc_vdev {
|
||||
uint32_t type;
|
||||
uint32_t id;
|
||||
uint32_t notifyid;
|
||||
uint32_t dfeatures;
|
||||
uint32_t gfeatures;
|
||||
uint32_t config_len;
|
||||
uint8_t status;
|
||||
uint8_t num_of_vrings;
|
||||
uint8_t reserved[2];
|
||||
struct fw_rsc_vdev_vring vring[0];
|
||||
} METAL_PACKED_END;
|
||||
|
||||
/**
|
||||
* struct fw_rsc_vendor - remote processor vendor specific resource
|
||||
* @len: length of the resource
|
||||
*
|
||||
* This resource entry tells the host the vendor specific resource
|
||||
* required by the remote.
|
||||
*
|
||||
* These request entries should precede other shared resource entries
|
||||
* such as vdevs, vrings.
|
||||
*/
|
||||
METAL_PACKED_BEGIN
|
||||
struct fw_rsc_vendor {
|
||||
uint32_t type;
|
||||
uint32_t len;
|
||||
} METAL_PACKED_END;
|
||||
|
||||
struct loader_ops;
|
||||
struct image_store_ops;
|
||||
struct remoteproc_ops;
|
||||
|
||||
/**
|
||||
* struct remoteproc_mem
|
||||
*
|
||||
* This structure presents the memory used by the remote processor
|
||||
*
|
||||
* @da: device memory
|
||||
* @pa: physical memory
|
||||
* @size: size of the memory
|
||||
* @io: pointer to the I/O region
|
||||
* @node: list node
|
||||
*/
|
||||
struct remoteproc_mem {
|
||||
metal_phys_addr_t da;
|
||||
metal_phys_addr_t pa;
|
||||
size_t size;
|
||||
char name[RPROC_MAX_NAME_LEN];
|
||||
struct metal_io_region *io;
|
||||
struct metal_list node;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct remoteproc
|
||||
*
|
||||
* This structure is maintained by the remoteproc to represent the remote
|
||||
* processor instance. This structure acts as a prime parameter to use
|
||||
* the remoteproc APIs.
|
||||
*
|
||||
* @bootaddr: boot address
|
||||
* @loader: executable loader
|
||||
* @lock: mutex lock
|
||||
* @ops: remoteproc operations
|
||||
* @rsc_table: pointer to resource table
|
||||
* @rsc_len: length of resource table
|
||||
* @rsc_io: metal I/O region of resource table
|
||||
* @mems: remoteproc memories
|
||||
* @vdevs: remoteproc virtio devices
|
||||
* @bitmap: bitmap for notify IDs for remoteproc subdevices
|
||||
* @state: remote processor state
|
||||
* @priv: private data
|
||||
*/
|
||||
struct remoteproc {
|
||||
metal_mutex_t lock;
|
||||
void *rsc_table;
|
||||
size_t rsc_len;
|
||||
struct metal_io_region *rsc_io;
|
||||
struct metal_list mems;
|
||||
struct metal_list vdevs;
|
||||
unsigned long bitmap;
|
||||
struct remoteproc_ops *ops;
|
||||
metal_phys_addr_t bootaddr;
|
||||
struct loader_ops *loader;
|
||||
unsigned int state;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct remoteproc_ops
|
||||
*
|
||||
* remoteproc operations needs to be implemented by each remoteproc driver
|
||||
*
|
||||
* @init: initialize the remoteproc instance
|
||||
* @remove: remove the remoteproc instance
|
||||
* @mmap: memory mapped the memory with physical address or destination
|
||||
* address as input.
|
||||
* @handle_rsc: handle the vendor specific resource
|
||||
* @config: configure the remoteproc to make it ready to load and run
|
||||
* executable
|
||||
* @start: kick the remoteproc to run application
|
||||
* @stop: stop the remoteproc from running application, the resource such as
|
||||
* memory may not be off.
|
||||
* @shutdown: shutdown the remoteproc and release its resources.
|
||||
* @notify: notify the remote
|
||||
* @get_mem: get remoteproc memory I/O region.
|
||||
*/
|
||||
struct remoteproc_ops {
|
||||
struct remoteproc *(*init)(struct remoteproc *rproc,
|
||||
struct remoteproc_ops *ops, void *arg);
|
||||
void (*remove)(struct remoteproc *rproc);
|
||||
void *(*mmap)(struct remoteproc *rproc,
|
||||
metal_phys_addr_t *pa, metal_phys_addr_t *da,
|
||||
size_t size, unsigned int attribute,
|
||||
struct metal_io_region **io);
|
||||
int (*handle_rsc)(struct remoteproc *rproc, void *rsc, size_t len);
|
||||
int (*config)(struct remoteproc *rproc, void *data);
|
||||
int (*start)(struct remoteproc *rproc);
|
||||
int (*stop)(struct remoteproc *rproc);
|
||||
int (*shutdown)(struct remoteproc *rproc);
|
||||
int (*notify)(struct remoteproc *rproc, uint32_t id);
|
||||
/**
|
||||
* get_mem
|
||||
*
|
||||
* get remoteproc memory I/O region by either name, virtual
|
||||
* address, physical address or device address.
|
||||
*
|
||||
* @rproc - pointer to remoteproc instance
|
||||
* @name - memory name
|
||||
* @pa - physical address
|
||||
* @da - device address
|
||||
* @va - virtual address
|
||||
* @size - memory size
|
||||
*
|
||||
* @returns remoteproc memory pointed by buf if success, otherwise NULL
|
||||
*/
|
||||
struct remoteproc_mem *(*get_mem)(struct remoteproc *rproc,
|
||||
const char *name,
|
||||
metal_phys_addr_t pa,
|
||||
metal_phys_addr_t da,
|
||||
void *va, size_t size,
|
||||
struct remoteproc_mem *buf);
|
||||
};
|
||||
|
||||
/* Remoteproc error codes */
|
||||
#define RPROC_EBASE 0
|
||||
#define RPROC_ENOMEM (RPROC_EBASE + 1)
|
||||
#define RPROC_EINVAL (RPROC_EBASE + 2)
|
||||
#define RPROC_ENODEV (RPROC_EBASE + 3)
|
||||
#define RPROC_EAGAIN (RPROC_EBASE + 4)
|
||||
#define RPROC_ERR_RSC_TAB_TRUNC (RPROC_EBASE + 5)
|
||||
#define RPROC_ERR_RSC_TAB_VER (RPROC_EBASE + 6)
|
||||
#define RPROC_ERR_RSC_TAB_RSVD (RPROC_EBASE + 7)
|
||||
#define RPROC_ERR_RSC_TAB_VDEV_NRINGS (RPROC_EBASE + 9)
|
||||
#define RPROC_ERR_RSC_TAB_NP (RPROC_EBASE + 10)
|
||||
#define RPROC_ERR_RSC_TAB_NS (RPROC_EBASE + 11)
|
||||
#define RPROC_ERR_LOADER_STATE (RPROC_EBASE + 12)
|
||||
#define RPROC_EMAX (RPROC_EBASE + 16)
|
||||
#define RPROC_EPTR (void *)(-1)
|
||||
#define RPROC_EOF (void *)(-1)
|
||||
|
||||
static inline long RPROC_PTR_ERR(const void *ptr)
|
||||
{
|
||||
return (long)ptr;
|
||||
}
|
||||
|
||||
static inline int RPROC_IS_ERR(const void *ptr)
|
||||
{
|
||||
if ((unsigned long)ptr >= (unsigned long)(-RPROC_EMAX))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void *RPROC_ERR_PTR(long error)
|
||||
{
|
||||
return (void *)error;
|
||||
}
|
||||
|
||||
/**
|
||||
* enum rproc_state - remote processor states
|
||||
* @RPROC_OFFLINE: remote is offline
|
||||
* @RPROC_CONFIGURED: remote is configured
|
||||
* @RPROC_READY: remote is ready to start
|
||||
* @RPROC_RUNNING: remote is up and running
|
||||
* @RPROC_SUSPENDED: remote is suspended
|
||||
* @RPROC_ERROR: remote has error; need to recover
|
||||
* @RPROC_STOPPED: remote is stopped
|
||||
* @RPROC_LAST: just keep this one at the end
|
||||
*/
|
||||
enum remoteproc_state {
|
||||
RPROC_OFFLINE = 0,
|
||||
RPROC_CONFIGURED = 1,
|
||||
RPROC_READY = 2,
|
||||
RPROC_RUNNING = 3,
|
||||
RPROC_SUSPENDED = 4,
|
||||
RPROC_ERROR = 5,
|
||||
RPROC_STOPPED = 6,
|
||||
RPROC_LAST = 7,
|
||||
};
|
||||
|
||||
/**
|
||||
* remoteproc_init
|
||||
*
|
||||
* Initializes remoteproc resource.
|
||||
*
|
||||
* @rproc - pointer to remoteproc instance
|
||||
* @ops - pointer to remoteproc operations
|
||||
* @priv - pointer to private data
|
||||
*
|
||||
* @returns created remoteproc pointer
|
||||
*/
|
||||
struct remoteproc *remoteproc_init(struct remoteproc *rproc,
|
||||
struct remoteproc_ops *ops, void *priv);
|
||||
|
||||
/**
|
||||
* remoteproc_remove
|
||||
*
|
||||
* Remove remoteproc resource
|
||||
*
|
||||
* @rproc - pointer to remoteproc instance
|
||||
*
|
||||
* returns 0 for success, negative value for failure
|
||||
*/
|
||||
int remoteproc_remove(struct remoteproc *rproc);
|
||||
|
||||
/**
|
||||
* remoteproc_init_mem
|
||||
*
|
||||
* Initialize remoteproc memory
|
||||
*
|
||||
* @mem - pointer to remoteproc memory
|
||||
* @name - memory name
|
||||
* @pa - physical address
|
||||
* @da - device address
|
||||
* @size - memory size
|
||||
* @io - pointer to the I/O region
|
||||
*/
|
||||
static inline void
|
||||
remoteproc_init_mem(struct remoteproc_mem *mem, const char *name,
|
||||
metal_phys_addr_t pa, metal_phys_addr_t da,
|
||||
size_t size, struct metal_io_region *io)
|
||||
{
|
||||
if (!mem)
|
||||
return;
|
||||
if (name)
|
||||
strncpy(mem->name, name, sizeof(mem->name));
|
||||
else
|
||||
mem->name[0] = 0;
|
||||
mem->pa = pa;
|
||||
mem->da = da;
|
||||
mem->io = io;
|
||||
mem->size = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* remoteproc_add_mem
|
||||
*
|
||||
* Add remoteproc memory
|
||||
*
|
||||
* @rproc - pointer to remoteproc
|
||||
* @mem - pointer to remoteproc memory
|
||||
*/
|
||||
static inline void
|
||||
remoteproc_add_mem(struct remoteproc *rproc, struct remoteproc_mem *mem)
|
||||
{
|
||||
if (!rproc || !mem)
|
||||
return;
|
||||
metal_list_add_tail(&rproc->mems, &mem->node);
|
||||
}
|
||||
|
||||
/**
|
||||
* remoteproc_get_io_with_name
|
||||
*
|
||||
* get remoteproc memory I/O region with name
|
||||
*
|
||||
* @rproc - pointer to the remote processor
|
||||
* @name - name of the shared memory
|
||||
* @io - pointer to the pointer of the I/O region
|
||||
*
|
||||
* returns metal I/O region pointer, NULL for failure
|
||||
*/
|
||||
struct metal_io_region *
|
||||
remoteproc_get_io_with_name(struct remoteproc *rproc,
|
||||
const char *name);
|
||||
|
||||
/**
|
||||
* remoteproc_get_io_with_pa
|
||||
*
|
||||
* get remoteproc memory I/O region with physical address
|
||||
*
|
||||
* @rproc - pointer to the remote processor
|
||||
* @pa - physical address
|
||||
*
|
||||
* returns metal I/O region pointer, NULL for failure
|
||||
*/
|
||||
struct metal_io_region *
|
||||
remoteproc_get_io_with_pa(struct remoteproc *rproc,
|
||||
metal_phys_addr_t pa);
|
||||
|
||||
/**
|
||||
* remoteproc_get_io_with_da
|
||||
*
|
||||
* get remoteproc memory I/O region with device address
|
||||
*
|
||||
* @rproc - pointer to the remote processor
|
||||
* @da - device address
|
||||
* @offset - I/O region offset of the device address
|
||||
*
|
||||
* returns metal I/O region pointer, NULL for failure
|
||||
*/
|
||||
struct metal_io_region *
|
||||
remoteproc_get_io_with_da(struct remoteproc *rproc,
|
||||
metal_phys_addr_t da,
|
||||
unsigned long *offset);
|
||||
|
||||
/**
|
||||
* remoteproc_get_io_with_va
|
||||
*
|
||||
* get remoteproc memory I/O region with virtual address
|
||||
*
|
||||
* @rproc - pointer to the remote processor
|
||||
* @va - virtual address
|
||||
*
|
||||
* returns metal I/O region pointer, NULL for failure
|
||||
*/
|
||||
struct metal_io_region *
|
||||
remoteproc_get_io_with_va(struct remoteproc *rproc,
|
||||
void *va);
|
||||
|
||||
/**
|
||||
* remoteproc_mmap
|
||||
*
|
||||
* remoteproc mmap memory
|
||||
*
|
||||
* @rproc - pointer to the remote processor
|
||||
* @pa - physical address pointer
|
||||
* @da - device address pointer
|
||||
* @size - size of the memory
|
||||
* @attribute - memory attribute
|
||||
* @io - pointer to the I/O region
|
||||
*
|
||||
* returns pointer to the memory
|
||||
*/
|
||||
void *remoteproc_mmap(struct remoteproc *rproc,
|
||||
metal_phys_addr_t *pa, metal_phys_addr_t *da,
|
||||
size_t size, unsigned int attribute,
|
||||
struct metal_io_region **io);
|
||||
|
||||
/**
|
||||
* remoteproc_set_rsc_table
|
||||
*
|
||||
* Parse and set resource table of remoteproc
|
||||
*
|
||||
* @rproc - pointer to remoteproc instance
|
||||
* @rsc_table - pointer to resource table
|
||||
* @rsc_size - resource table size
|
||||
*
|
||||
* returns 0 for success and negative value for errors
|
||||
*/
|
||||
int remoteproc_set_rsc_table(struct remoteproc *rproc,
|
||||
struct resource_table *rsc_table,
|
||||
size_t rsc_size);
|
||||
|
||||
/**
|
||||
* remoteproc_config
|
||||
*
|
||||
* This function configures the remote processor to get it
|
||||
* ready to load and run executable.
|
||||
*
|
||||
* @rproc - pointer to remoteproc instance to start
|
||||
* @data - configuration data
|
||||
*
|
||||
* returns 0 for success and negative value for errors
|
||||
*/
|
||||
int remoteproc_config(struct remoteproc *rproc, void *data);
|
||||
|
||||
/**
|
||||
* remoteproc_start
|
||||
*
|
||||
* This function starts the remote processor.
|
||||
* It assumes the firmware is already loaded,
|
||||
*
|
||||
* @rproc - pointer to remoteproc instance to start
|
||||
*
|
||||
* returns 0 for success and negative value for errors
|
||||
*/
|
||||
int remoteproc_start(struct remoteproc *rproc);
|
||||
|
||||
/**
|
||||
* remoteproc_stop
|
||||
*
|
||||
* This function stops the remote processor but it
|
||||
* will not release its resource.
|
||||
*
|
||||
* @rproc - pointer to remoteproc instance
|
||||
*
|
||||
* returns 0 for success and negative value for errors
|
||||
*/
|
||||
int remoteproc_stop(struct remoteproc *rproc);
|
||||
|
||||
/**
|
||||
* remoteproc_shutdown
|
||||
*
|
||||
* This function shutdown the remote processor and
|
||||
* release its resources.
|
||||
*
|
||||
* @rproc - pointer to remoteproc instance
|
||||
*
|
||||
* returns 0 for success and negative value for errors
|
||||
*/
|
||||
int remoteproc_shutdown(struct remoteproc *rproc);
|
||||
|
||||
/**
|
||||
* remoteproc_load
|
||||
*
|
||||
* load executable, it expects the user application defines how to
|
||||
* open the executable file and how to get data from the executable file
|
||||
* and how to load data to the target memory.
|
||||
*
|
||||
* @rproc: pointer to the remoteproc instance
|
||||
* @path: optional path to the image file
|
||||
* @store: pointer to user defined image store argument
|
||||
* @store_ops: pointer to image store operations
|
||||
* @image_info: pointer to memory which stores image information used
|
||||
* by remoteproc loader
|
||||
*
|
||||
* return 0 for success and negative value for failure
|
||||
*/
|
||||
int remoteproc_load(struct remoteproc *rproc, const char *path,
|
||||
void *store, struct image_store_ops *store_ops,
|
||||
void **img_info);
|
||||
|
||||
/**
|
||||
* remoteproc_load_noblock
|
||||
*
|
||||
* load executable, it expects the caller has loaded image data to local
|
||||
* memory and passed to the this function. If the function needs more
|
||||
* image data it will return the next expected image data offset and
|
||||
* the next expected image data length. If the function requires the
|
||||
* caller to download image data to the target memory, it will also
|
||||
* return the target physical address besides the offset and length.
|
||||
* This function can be used to load firmware in stream mode. In this
|
||||
* mode, you cannot do seek to the executable file. If the executable
|
||||
* is ELF, it cannot get the resource table section before it loads
|
||||
* the full ELF file. Furthermore, application usually don't store
|
||||
* the data which is loaded to local memory in streaming mode, and
|
||||
* thus, in this mode, it will load the binary to the target memory
|
||||
* before it gets the resource table. And thus, when calling this function
|
||||
* don't put the target executable memory in the resource table, as
|
||||
* this function will parse the resource table after it loads the binary
|
||||
* to target memory.
|
||||
*
|
||||
* @rproc: pointer to the remoteproc instance
|
||||
* @img_data: pointer to image data for remoteproc loader to parse
|
||||
* @offset: image data offset to the beginning of the image file
|
||||
* @len: image data length
|
||||
* @image_info: pointer to memory which stores image information used
|
||||
* by remoteproc loader
|
||||
* @pa: pointer to the target memory physical address. If the next expected
|
||||
* data doesn't need to load to the target memory, the function will
|
||||
* set it to ANY.
|
||||
* @io: pointer to the io region. If the next expected
|
||||
* data doesn't need to load to the target memory, the function will
|
||||
* set it to NULL.
|
||||
* @noffset: pointer to the next image data offset to the beginning of
|
||||
* the image file needs to load to local or to the target
|
||||
* memory.
|
||||
* @nlen: pointer to the next image data length needs to load to local
|
||||
* or to the target memory.
|
||||
* @nmlen: pointer to the memory size. It is only used when the next
|
||||
* expected data is going to be loaded to the target memory. E.g.
|
||||
* in ELF, it is possible that loadable segment in memory is
|
||||
* larger that the segment data in the ELF file. In this case,
|
||||
* application will need to pad the rest of the memory with
|
||||
* padding.
|
||||
* @padding: pointer to the padding value. It is only used when the next
|
||||
* expected data is going to be loaded to the target memory.
|
||||
* and the target memory size is larger than the segment data in
|
||||
* the executable file.
|
||||
*
|
||||
* return 0 for success and negative value for failure
|
||||
*/
|
||||
int remoteproc_load_noblock(struct remoteproc *rproc,
|
||||
const void *img_data, size_t offset, size_t len,
|
||||
void **img_info,
|
||||
metal_phys_addr_t *pa, struct metal_io_region **io,
|
||||
size_t *noffset, size_t *nlen,
|
||||
size_t *nmlen, unsigned char *padding);
|
||||
|
||||
/**
|
||||
* remoteproc_allocate_id
|
||||
*
|
||||
* allocate notifyid for resource
|
||||
*
|
||||
* @rproc - pointer to the remoteproc instance
|
||||
* @start - start of the id range
|
||||
* @end - end of the id range
|
||||
*
|
||||
* return allocated notify id
|
||||
*/
|
||||
unsigned int remoteproc_allocate_id(struct remoteproc *rproc,
|
||||
unsigned int start,
|
||||
unsigned int end);
|
||||
|
||||
/* remoteproc_create_virtio
|
||||
*
|
||||
* create virtio device, it returns pointer to the created virtio device.
|
||||
*
|
||||
* @rproc: pointer to the remoteproc instance
|
||||
* @vdev_id: virtio device ID
|
||||
* @role: virtio device role
|
||||
* @rst_cb: virtio device reset callback
|
||||
*
|
||||
* return pointer to the created virtio device, NULL for failure.
|
||||
*/
|
||||
struct virtio_device *
|
||||
remoteproc_create_virtio(struct remoteproc *rproc,
|
||||
int vdev_id, unsigned int role,
|
||||
void (*rst_cb)(struct virtio_device *vdev));
|
||||
|
||||
/* remoteproc_remove_virtio
|
||||
*
|
||||
* Remove virtio device
|
||||
*
|
||||
* @rproc: pointer to the remoteproc instance
|
||||
* @vdev: pointer to the virtio device
|
||||
*
|
||||
*/
|
||||
void remoteproc_remove_virtio(struct remoteproc *rproc,
|
||||
struct virtio_device *vdev);
|
||||
|
||||
/* remoteproc_get_notification
|
||||
*
|
||||
* remoteproc is got notified, it will check its subdevices
|
||||
* for the notification
|
||||
*
|
||||
* @rproc - pointer to the remoteproc instance
|
||||
* @notifyid - notification id
|
||||
*
|
||||
* return 0 for succeed, negative value for failure
|
||||
*/
|
||||
int remoteproc_get_notification(struct remoteproc *rproc,
|
||||
uint32_t notifyid);
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* REMOTEPROC_H_ */
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Mentor Graphics Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/**************************************************************************
|
||||
* FILE NAME
|
||||
*
|
||||
* remoteproc_loader.h
|
||||
*
|
||||
* COMPONENT
|
||||
*
|
||||
* OpenAMP stack.
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* This file provides definitions for remoteproc loader
|
||||
*
|
||||
*
|
||||
**************************************************************************/
|
||||
#ifndef REMOTEPROC_LOADER_H_
|
||||
#define REMOTEPROC_LOADER_H_
|
||||
|
||||
#include <metal/io.h>
|
||||
#include <metal/list.h>
|
||||
#include <metal/sys.h>
|
||||
#include <openamp/remoteproc.h>
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Loader feature macros */
|
||||
#define SUPPORT_SEEK 1UL
|
||||
|
||||
/* Remoteproc loader any address */
|
||||
#define RPROC_LOAD_ANYADDR ((metal_phys_addr_t)-1)
|
||||
|
||||
/* Remoteproc loader Executable Image Parsing States */
|
||||
/* Remoteproc loader parser initial state */
|
||||
#define RPROC_LOADER_NOT_READY 0x0L
|
||||
/* Remoteproc loader ready to load, even it can be not finish parsing */
|
||||
#define RPROC_LOADER_READY_TO_LOAD 0x10000L
|
||||
/* Remoteproc loader post data load */
|
||||
#define RPROC_LOADER_POST_DATA_LOAD 0x20000L
|
||||
/* Remoteproc loader finished loading */
|
||||
#define RPROC_LOADER_LOAD_COMPLETE 0x40000L
|
||||
/* Remoteproc loader state mask */
|
||||
#define RPROC_LOADER_MASK 0x00FF0000L
|
||||
/* Remoteproc loader private mask */
|
||||
#define RPROC_LOADER_PRIVATE_MASK 0x0000FFFFL
|
||||
/* Remoteproc loader reserved mask */
|
||||
#define RPROC_LOADER_RESERVED_MASK 0x0F000000L
|
||||
|
||||
/**
|
||||
* struct image_store_ops - user defined image store operations
|
||||
* @open: user defined callback to open the "firmware" to prepare loading
|
||||
* @close: user defined callback to close the "firmware" to clean up
|
||||
* after loading
|
||||
* @load: user defined callback to load the firmware contents to target
|
||||
* memory or local memory
|
||||
* @features: loader supported features. e.g. seek
|
||||
*/
|
||||
struct image_store_ops {
|
||||
int (*open)(void *store, const char *path, const void **img_data);
|
||||
void (*close)(void *store);
|
||||
int (*load)(void *store, size_t offset, size_t size,
|
||||
const void **data,
|
||||
metal_phys_addr_t pa,
|
||||
struct metal_io_region *io, char is_blocking);
|
||||
unsigned int features;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct loader_ops - loader operations
|
||||
* @load_header: define how to get the executable headers
|
||||
* @load_data: define how to load the target data
|
||||
* @locate_rsc_table: define how to get the resource table target address,
|
||||
* offset to the ELF image file and size of the resource
|
||||
* table.
|
||||
* @release: define how to release the loader
|
||||
* @get_entry: get entry address
|
||||
* @get_load_state: get load state from the image information
|
||||
*/
|
||||
struct loader_ops {
|
||||
int (*load_header)(const void *img_data, size_t offset, size_t len,
|
||||
void **img_info, int last_state,
|
||||
size_t *noffset, size_t *nlen);
|
||||
int (*load_data)(struct remoteproc *rproc,
|
||||
const void *img_data, size_t offset, size_t len,
|
||||
void **img_info, int last_load_state,
|
||||
metal_phys_addr_t *da,
|
||||
size_t *noffset, size_t *nlen,
|
||||
unsigned char *padding, size_t *nmemsize);
|
||||
int (*locate_rsc_table)(void *img_info, metal_phys_addr_t *da,
|
||||
size_t *offset, size_t *size);
|
||||
void (*release)(void *img_info);
|
||||
metal_phys_addr_t (*get_entry)(void *img_info);
|
||||
int (*get_load_state)(void *img_info);
|
||||
};
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* REMOTEPROC_LOADER_H_ */
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Remoteproc Virtio Framework
|
||||
*
|
||||
* Copyright(c) 2018 Xilinx Ltd.
|
||||
* Copyright(c) 2011 Texas Instruments, Inc.
|
||||
* Copyright(c) 2011 Google, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef REMOTEPROC_VIRTIO_H
|
||||
#define REMOTEPROC_VIRTIO_H
|
||||
|
||||
#include <metal/io.h>
|
||||
#include <metal/list.h>
|
||||
#include <openamp/virtio.h>
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* define vdev notification funciton user should implement */
|
||||
typedef int (*rpvdev_notify_func)(void *priv, uint32_t id);
|
||||
|
||||
/**
|
||||
* struct remoteproc_virtio
|
||||
* @priv pointer to private data
|
||||
* @vdev_rsc address of vdev resource
|
||||
* @vdev_rsc_io metal I/O region of vdev_info, can be NULL
|
||||
* @notify notification function
|
||||
* @vdev virtio device
|
||||
* @node list node
|
||||
*/
|
||||
struct remoteproc_virtio {
|
||||
void *priv;
|
||||
void *vdev_rsc;
|
||||
struct metal_io_region *vdev_rsc_io;
|
||||
rpvdev_notify_func notify;
|
||||
struct virtio_device vdev;
|
||||
struct metal_list node;
|
||||
};
|
||||
|
||||
/**
|
||||
* rproc_virtio_create_vdev
|
||||
*
|
||||
* Create rproc virtio vdev
|
||||
*
|
||||
* @role: 0 - virtio master, 1 - virtio slave
|
||||
* @notifyid: virtio device notification id
|
||||
* @rsc: pointer to the virtio device resource
|
||||
* @rsc_io: pointer to the virtio device resource I/O region
|
||||
* @priv: pointer to the private data
|
||||
* @notify: vdev and virtqueue notification function
|
||||
* @rst_cb: reset virtio device callback
|
||||
*
|
||||
* return pointer to the created virtio device for success,
|
||||
* NULL for failure.
|
||||
*/
|
||||
struct virtio_device *
|
||||
rproc_virtio_create_vdev(unsigned int role, unsigned int notifyid,
|
||||
void *rsc, struct metal_io_region *rsc_io,
|
||||
void *priv,
|
||||
rpvdev_notify_func notify,
|
||||
virtio_dev_reset_cb rst_cb);
|
||||
|
||||
/**
|
||||
* rproc_virtio_remove_vdev
|
||||
*
|
||||
* Remove rproc virtio vdev
|
||||
*
|
||||
* @vdev - pointer to the virtio device
|
||||
*/
|
||||
void rproc_virtio_remove_vdev(struct virtio_device *vdev);
|
||||
|
||||
/**
|
||||
* rproc_virtio_init_vring
|
||||
*
|
||||
* Initialize rproc virtio vring
|
||||
*
|
||||
* @vdev: pointer to the virtio device
|
||||
* @index: vring index in the virtio device
|
||||
* @notifyid: remoteproc vring notification id
|
||||
* @va: vring virtual address
|
||||
* @io: pointer to vring I/O region
|
||||
* @num_desc: number of descriptors
|
||||
* @align: vring alignment
|
||||
*
|
||||
* return 0 for success, negative value for failure.
|
||||
*/
|
||||
int rproc_virtio_init_vring(struct virtio_device *vdev, unsigned int index,
|
||||
unsigned int notifyid, void *va,
|
||||
struct metal_io_region *io,
|
||||
unsigned int num_descs, unsigned int align);
|
||||
|
||||
/**
|
||||
* rproc_virtio_notified
|
||||
*
|
||||
* remoteproc virtio is got notified
|
||||
*
|
||||
* @vdev - pointer to the virtio device
|
||||
* @notifyid - notify id
|
||||
*
|
||||
* return 0 for successful, negative value for failure
|
||||
*/
|
||||
int rproc_virtio_notified(struct virtio_device *vdev, uint32_t notifyid);
|
||||
|
||||
/**
|
||||
* rproc_virtio_wait_remote_ready
|
||||
*
|
||||
* Blocking function, waiting for the remote core is ready to start
|
||||
* communications.
|
||||
*
|
||||
* @vdev - pointer to the virtio device
|
||||
*
|
||||
* return true when remote processor is ready.
|
||||
*/
|
||||
void rproc_virtio_wait_remote_ready(struct virtio_device *vdev);
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* REMOTEPROC_VIRTIO_H */
|
||||
@@ -0,0 +1,526 @@
|
||||
/*
|
||||
* Remote processor messaging
|
||||
*
|
||||
* Copyright (C) 2011 Texas Instruments, Inc.
|
||||
* Copyright (C) 2011 Google, Inc.
|
||||
* All rights reserved.
|
||||
* Copyright (c) 2016 Freescale Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _RPMSG_H_
|
||||
#define _RPMSG_H_
|
||||
|
||||
#include <metal/compiler.h>
|
||||
#include <metal/mutex.h>
|
||||
#include <metal/list.h>
|
||||
#include <metal/utilities.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Configurable parameters */
|
||||
#define RPMSG_NAME_SIZE (32)
|
||||
#define RPMSG_ADDR_BMP_SIZE (128)
|
||||
|
||||
#define RPMSG_NS_EPT_ADDR (0x35)
|
||||
#define RPMSG_RESERVED_ADDRESSES (1024)
|
||||
#define RPMSG_ADDR_ANY 0xFFFFFFFF
|
||||
|
||||
/* Error macros. */
|
||||
#define RPMSG_SUCCESS 0
|
||||
#define RPMSG_ERROR_BASE -2000
|
||||
#define RPMSG_ERR_NO_MEM (RPMSG_ERROR_BASE - 1)
|
||||
#define RPMSG_ERR_NO_BUFF (RPMSG_ERROR_BASE - 2)
|
||||
#define RPMSG_ERR_PARAM (RPMSG_ERROR_BASE - 3)
|
||||
#define RPMSG_ERR_DEV_STATE (RPMSG_ERROR_BASE - 4)
|
||||
#define RPMSG_ERR_BUFF_SIZE (RPMSG_ERROR_BASE - 5)
|
||||
#define RPMSG_ERR_INIT (RPMSG_ERROR_BASE - 6)
|
||||
#define RPMSG_ERR_ADDR (RPMSG_ERROR_BASE - 7)
|
||||
|
||||
struct rpmsg_endpoint;
|
||||
struct rpmsg_device;
|
||||
|
||||
/* Returns positive value on success or negative error value on failure */
|
||||
typedef int (*rpmsg_ept_cb)(struct rpmsg_endpoint *ept, void *data,
|
||||
size_t len, uint32_t src, void *priv);
|
||||
typedef void (*rpmsg_ns_unbind_cb)(struct rpmsg_endpoint *ept);
|
||||
typedef void (*rpmsg_ns_bind_cb)(struct rpmsg_device *rdev,
|
||||
const char *name, uint32_t dest);
|
||||
|
||||
/**
|
||||
* struct rpmsg_endpoint - binds a local rpmsg address to its user
|
||||
* @name: name of the service supported
|
||||
* @rdev: pointer to the rpmsg device
|
||||
* @addr: local address of the endpoint
|
||||
* @dest_addr: address of the default remote endpoint binded.
|
||||
* @cb: user rx callback, return value of this callback is reserved
|
||||
* for future use, for now, only allow RPMSG_SUCCESS as return value.
|
||||
* @ns_unbind_cb: end point service unbind callback, called when remote
|
||||
* ept is destroyed.
|
||||
* @node: end point node.
|
||||
* @priv: private data for the driver's use
|
||||
*
|
||||
* In essence, an rpmsg endpoint represents a listener on the rpmsg bus, as
|
||||
* it binds an rpmsg address with an rx callback handler.
|
||||
*/
|
||||
struct rpmsg_endpoint {
|
||||
char name[RPMSG_NAME_SIZE];
|
||||
struct rpmsg_device *rdev;
|
||||
uint32_t addr;
|
||||
uint32_t dest_addr;
|
||||
rpmsg_ept_cb cb;
|
||||
rpmsg_ns_unbind_cb ns_unbind_cb;
|
||||
struct metal_list node;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rpmsg_device_ops - RPMsg device operations
|
||||
* @send_offchannel_raw: send RPMsg data
|
||||
* @hold_rx_buffer: hold RPMsg RX buffer
|
||||
* @release_rx_buffer: release RPMsg RX buffer
|
||||
* @get_tx_payload_buffer: get RPMsg TX buffer
|
||||
* @send_offchannel_nocopy: send RPMsg data without copy
|
||||
*/
|
||||
struct rpmsg_device_ops {
|
||||
int (*send_offchannel_raw)(struct rpmsg_device *rdev,
|
||||
uint32_t src, uint32_t dst,
|
||||
const void *data, int len, int wait);
|
||||
void (*hold_rx_buffer)(struct rpmsg_device *rdev, void *rxbuf);
|
||||
void (*release_rx_buffer)(struct rpmsg_device *rdev, void *rxbuf);
|
||||
void *(*get_tx_payload_buffer)(struct rpmsg_device *rdev,
|
||||
uint32_t *len, int wait);
|
||||
int (*send_offchannel_nocopy)(struct rpmsg_device *rdev,
|
||||
uint32_t src, uint32_t dst,
|
||||
const void *data, int len);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rpmsg_device - representation of a RPMsg device
|
||||
* @endpoints: list of endpoints
|
||||
* @ns_ept: name service endpoint
|
||||
* @bitmap: table endpoint address allocation.
|
||||
* @lock: mutex lock for rpmsg management
|
||||
* @ns_bind_cb: callback handler for name service announcement without local
|
||||
* endpoints waiting to bind.
|
||||
* @ops: RPMsg device operations
|
||||
* @support_ns: create/destroy namespace message
|
||||
*/
|
||||
struct rpmsg_device {
|
||||
struct metal_list endpoints;
|
||||
struct rpmsg_endpoint ns_ept;
|
||||
unsigned long bitmap[metal_bitmap_longs(RPMSG_ADDR_BMP_SIZE)];
|
||||
metal_mutex_t lock;
|
||||
rpmsg_ns_bind_cb ns_bind_cb;
|
||||
struct rpmsg_device_ops ops;
|
||||
bool support_ns;
|
||||
};
|
||||
|
||||
/**
|
||||
* rpmsg_send_offchannel_raw() - send a message across to the remote processor,
|
||||
* specifying source and destination address.
|
||||
* @ept: the rpmsg endpoint
|
||||
* @data: payload of the message
|
||||
* @len: length of the payload
|
||||
*
|
||||
* This function sends @data of length @len to the remote @dst address from
|
||||
* the source @src address.
|
||||
* The message will be sent to the remote processor which the channel belongs
|
||||
* to.
|
||||
*
|
||||
* Returns number of bytes it has sent or negative error value on failure.
|
||||
*/
|
||||
int rpmsg_send_offchannel_raw(struct rpmsg_endpoint *ept, uint32_t src,
|
||||
uint32_t dst, const void *data, int len,
|
||||
int wait);
|
||||
|
||||
/**
|
||||
* rpmsg_send() - send a message across to the remote processor
|
||||
* @ept: the rpmsg endpoint
|
||||
* @data: payload of the message
|
||||
* @len: length of the payload
|
||||
*
|
||||
* This function sends @data of length @len based on the @ept.
|
||||
* The message will be sent to the remote processor which the channel belongs
|
||||
* to, using @ept's source and destination addresses.
|
||||
* In case there are no TX buffers available, the function will block until
|
||||
* one becomes available, or a timeout of 15 seconds elapses. When the latter
|
||||
* happens, -ERESTARTSYS is returned.
|
||||
*
|
||||
* Returns number of bytes it has sent or negative error value on failure.
|
||||
*/
|
||||
static inline int rpmsg_send(struct rpmsg_endpoint *ept, const void *data,
|
||||
int len)
|
||||
{
|
||||
return rpmsg_send_offchannel_raw(ept, ept->addr, ept->dest_addr, data,
|
||||
len, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmsg_sendto() - send a message across to the remote processor, specify dst
|
||||
* @ept: the rpmsg endpoint
|
||||
* @data: payload of message
|
||||
* @len: length of payload
|
||||
* @dst: destination address
|
||||
*
|
||||
* This function sends @data of length @len to the remote @dst address.
|
||||
* The message will be sent to the remote processor which the @ept
|
||||
* channel belongs to, using @ept's source address.
|
||||
* In case there are no TX buffers available, the function will block until
|
||||
* one becomes available, or a timeout of 15 seconds elapses. When the latter
|
||||
* happens, -ERESTARTSYS is returned.
|
||||
*
|
||||
* Returns number of bytes it has sent or negative error value on failure.
|
||||
*/
|
||||
static inline int rpmsg_sendto(struct rpmsg_endpoint *ept, const void *data,
|
||||
int len, uint32_t dst)
|
||||
{
|
||||
return rpmsg_send_offchannel_raw(ept, ept->addr, dst, data, len, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmsg_send_offchannel() - send a message using explicit src/dst addresses
|
||||
* @ept: the rpmsg endpoint
|
||||
* @src: source address
|
||||
* @dst: destination address
|
||||
* @data: payload of message
|
||||
* @len: length of payload
|
||||
*
|
||||
* This function sends @data of length @len to the remote @dst address,
|
||||
* and uses @src as the source address.
|
||||
* The message will be sent to the remote processor which the @ept
|
||||
* channel belongs to.
|
||||
* In case there are no TX buffers available, the function will block until
|
||||
* one becomes available, or a timeout of 15 seconds elapses. When the latter
|
||||
* happens, -ERESTARTSYS is returned.
|
||||
*
|
||||
* Returns number of bytes it has sent or negative error value on failure.
|
||||
*/
|
||||
static inline int rpmsg_send_offchannel(struct rpmsg_endpoint *ept,
|
||||
uint32_t src, uint32_t dst,
|
||||
const void *data, int len)
|
||||
{
|
||||
return rpmsg_send_offchannel_raw(ept, src, dst, data, len, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmsg_trysend() - send a message across to the remote processor
|
||||
* @ept: the rpmsg endpoint
|
||||
* @data: payload of message
|
||||
* @len: length of payload
|
||||
*
|
||||
* This function sends @data of length @len on the @ept channel.
|
||||
* The message will be sent to the remote processor which the @ept
|
||||
* channel belongs to, using @ept's source and destination addresses.
|
||||
* In case there are no TX buffers available, the function will immediately
|
||||
* return -ENOMEM without waiting until one becomes available.
|
||||
*
|
||||
* Returns number of bytes it has sent or negative error value on failure.
|
||||
*/
|
||||
static inline int rpmsg_trysend(struct rpmsg_endpoint *ept, const void *data,
|
||||
int len)
|
||||
{
|
||||
return rpmsg_send_offchannel_raw(ept, ept->addr, ept->dest_addr, data,
|
||||
len, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmsg_trysendto() - send a message across to the remote processor,
|
||||
* specify dst
|
||||
* @ept: the rpmsg endpoint
|
||||
* @data: payload of message
|
||||
* @len: length of payload
|
||||
* @dst: destination address
|
||||
*
|
||||
* This function sends @data of length @len to the remote @dst address.
|
||||
* The message will be sent to the remote processor which the @ept
|
||||
* channel belongs to, using @ept's source address.
|
||||
* In case there are no TX buffers available, the function will immediately
|
||||
* return -ENOMEM without waiting until one becomes available.
|
||||
*
|
||||
* Returns number of bytes it has sent or negative error value on failure.
|
||||
*/
|
||||
static inline int rpmsg_trysendto(struct rpmsg_endpoint *ept, const void *data,
|
||||
int len, uint32_t dst)
|
||||
{
|
||||
return rpmsg_send_offchannel_raw(ept, ept->addr, dst, data, len, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmsg_trysend_offchannel() - send a message using explicit src/dst addresses
|
||||
* @ept: the rpmsg endpoint
|
||||
* @src: source address
|
||||
* @dst: destination address
|
||||
* @data: payload of message
|
||||
* @len: length of payload
|
||||
*
|
||||
* This function sends @data of length @len to the remote @dst address,
|
||||
* and uses @src as the source address.
|
||||
* The message will be sent to the remote processor which the @ept
|
||||
* channel belongs to.
|
||||
* In case there are no TX buffers available, the function will immediately
|
||||
* return -ENOMEM without waiting until one becomes available.
|
||||
*
|
||||
* Returns number of bytes it has sent or negative error value on failure.
|
||||
*/
|
||||
static inline int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept,
|
||||
uint32_t src, uint32_t dst,
|
||||
const void *data, int len)
|
||||
{
|
||||
return rpmsg_send_offchannel_raw(ept, src, dst, data, len, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Holds the rx buffer for usage outside the receive callback.
|
||||
*
|
||||
* Calling this function prevents the RPMsg receive buffer from being released
|
||||
* back to the pool of shmem buffers. This API can only be called at rx
|
||||
* callback context (rpmsg_rx_cb_t). With this API, the application doesn't
|
||||
* need to copy the message in rx callback. Instead, the rx buffer base address
|
||||
* is saved in application context and further processed in application
|
||||
* process. After the message is processed, the application can release the rx
|
||||
* buffer for future reuse in vring by calling the rpmsg_release_rx_buffer()
|
||||
* function.
|
||||
*
|
||||
* @param: ept The rpmsg endpoint
|
||||
* @param: rxbuf RX buffer with message payload
|
||||
*
|
||||
* @see rpmsg_release_rx_buffer
|
||||
*/
|
||||
void rpmsg_hold_rx_buffer(struct rpmsg_endpoint *ept, void *rxbuf);
|
||||
|
||||
/**
|
||||
* @brief Releases the rx buffer for future reuse in vring.
|
||||
*
|
||||
* This API can be called at process context when the message in rx buffer is
|
||||
* processed.
|
||||
*
|
||||
* @ept: the rpmsg endpoint
|
||||
* @rxbuf: rx buffer with message payload
|
||||
*
|
||||
* @see rpmsg_hold_rx_buffer
|
||||
*/
|
||||
void rpmsg_release_rx_buffer(struct rpmsg_endpoint *ept, void *rxbuf);
|
||||
|
||||
/**
|
||||
* @brief Gets the tx buffer for message payload.
|
||||
*
|
||||
* This API can only be called at process context to get the tx buffer in vring.
|
||||
* By this way, the application can directly put its message into the vring tx
|
||||
* buffer without copy from an application buffer.
|
||||
* It is the application responsibility to correctly fill the allocated tx
|
||||
* buffer by data and passing correct parameters to the rpmsg_send_nocopy() or
|
||||
* rpmsg_sendto_nocopy() function to perform data no-copy-send mechanism.
|
||||
*
|
||||
* @ept: Pointer to rpmsg endpoint
|
||||
* @len: Pointer to store tx buffer size
|
||||
* @wait: Boolean, wait or not for buffer to become available
|
||||
*
|
||||
* @return The tx buffer address on success and NULL on failure
|
||||
*
|
||||
* @see rpmsg_send_offchannel_nocopy
|
||||
* @see rpmsg_sendto_nocopy
|
||||
* @see rpmsg_send_nocopy
|
||||
*/
|
||||
void *rpmsg_get_tx_payload_buffer(struct rpmsg_endpoint *ept,
|
||||
uint32_t *len, int wait);
|
||||
|
||||
/**
|
||||
* rpmsg_send_offchannel_nocopy() - send a message in tx buffer reserved by
|
||||
* rpmsg_get_tx_payload_buffer() across to the remote processor.
|
||||
*
|
||||
* This function sends buf of length len to the remote dst address,
|
||||
* and uses src as the source address.
|
||||
* The message will be sent to the remote processor which the ept
|
||||
* endpoint belongs to.
|
||||
* The application has to take the responsibility for:
|
||||
* 1. tx buffer reserved (rpmsg_get_tx_payload_buffer() )
|
||||
* 2. filling the data to be sent into the pre-allocated tx buffer
|
||||
* 3. not exceeding the buffer size when filling the data
|
||||
* 4. data cache coherency
|
||||
*
|
||||
* After the rpmsg_send_offchannel_nocopy() function is issued the tx buffer is
|
||||
* no more owned by the sending task and must not be touched anymore unless the
|
||||
* rpmsg_send_offchannel_nocopy() function fails and returns an error. In that
|
||||
* case application should try to re-issue the rpmsg_send_offchannel_nocopy()
|
||||
* again.
|
||||
*
|
||||
* @ept: The rpmsg endpoint
|
||||
* @src: The rpmsg endpoint local address
|
||||
* @dst: The rpmsg endpoint remote address
|
||||
* @data: TX buffer with message filled
|
||||
* @len: Length of payload
|
||||
*
|
||||
* @return number of bytes it has sent or negative error value on failure.
|
||||
*
|
||||
* @see rpmsg_get_tx_payload_buffer
|
||||
* @see rpmsg_sendto_nocopy
|
||||
* @see rpmsg_send_nocopy
|
||||
*/
|
||||
int rpmsg_send_offchannel_nocopy(struct rpmsg_endpoint *ept, uint32_t src,
|
||||
uint32_t dst, const void *data, int len);
|
||||
|
||||
/**
|
||||
* @brief rpmsg_sendto_nocopy() - sends a message in tx buffer allocated by
|
||||
* rpmsg_get_tx_payload_buffer() across to the remote processor, specify dst.
|
||||
*
|
||||
* This function sends buf of length len to the remote dst address.
|
||||
* The message will be sent to the remote processor which the ept
|
||||
* endpoint belongs to, using ept's source address.
|
||||
* The application has to take the responsibility for:
|
||||
* 1. tx buffer allocation (rpmsg_get_tx_payload_buffer() )
|
||||
* 2. filling the data to be sent into the pre-allocated tx buffer
|
||||
* 3. not exceeding the buffer size when filling the data
|
||||
* 4. data cache coherency
|
||||
*
|
||||
* After the rpmsg_sendto_nocopy() function is issued the tx buffer is no more
|
||||
* owned by the sending task and must not be touched anymore unless the
|
||||
* rpmsg_sendto_nocopy() function fails and returns an error. In that case the
|
||||
* application should try to re-issue the rpmsg_sendto_nocopy() again.
|
||||
*
|
||||
* @ept: The rpmsg endpoint
|
||||
* @data: TX buffer with message filled
|
||||
* @len: Length of payload
|
||||
* @dst: Destination address
|
||||
*
|
||||
* @return number of bytes it has sent or negative error value on failure.
|
||||
*
|
||||
* @see rpmsg_get_tx_payload_buffer
|
||||
* @see rpmsg_send_offchannel_nocopy
|
||||
* @see rpmsg_send_nocopy
|
||||
*/
|
||||
static inline int rpmsg_sendto_nocopy(struct rpmsg_endpoint *ept,
|
||||
const void *data, int len, uint32_t dst)
|
||||
{
|
||||
return rpmsg_send_offchannel_nocopy(ept, ept->addr, dst, data, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmsg_send_nocopy() - send a message in tx buffer reserved by
|
||||
* rpmsg_get_tx_payload_buffer() across to the remote processor.
|
||||
*
|
||||
* This function sends buf of length len on the ept endpoint.
|
||||
* The message will be sent to the remote processor which the ept
|
||||
* endpoint belongs to, using ept's source and destination addresses.
|
||||
* The application has to take the responsibility for:
|
||||
* 1. tx buffer reserved (rpmsg_get_tx_payload_buffer() )
|
||||
* 2. filling the data to be sent into the pre-allocated tx buffer
|
||||
* 3. not exceeding the buffer size when filling the data
|
||||
* 4. data cache coherency
|
||||
*
|
||||
* After the rpmsg_send_nocopy() function is issued the tx buffer is no more
|
||||
* owned by the sending task and must not be touched anymore unless the
|
||||
* rpmsg_send_nocopy() function fails and returns an error. In that case the
|
||||
* application should try to re-issue the rpmsg_send_nocopy() again.
|
||||
*
|
||||
* @ept: The rpmsg endpoint
|
||||
* @data: TX buffer with message filled
|
||||
* @len: Length of payload
|
||||
*
|
||||
* @return number of bytes it has sent or negative error value on failure.
|
||||
*
|
||||
* @see rpmsg_get_tx_payload_buffer
|
||||
* @see rpmsg_send_offchannel_nocopy
|
||||
* @see rpmsg_sendto_nocopy
|
||||
*/
|
||||
static inline int rpmsg_send_nocopy(struct rpmsg_endpoint *ept,
|
||||
const void *data, int len)
|
||||
{
|
||||
return rpmsg_send_offchannel_nocopy(ept, ept->addr,
|
||||
ept->dest_addr, data, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmsg_init_ept - initialize rpmsg endpoint
|
||||
*
|
||||
* Initialize an RPMsg endpoint with a name, source address,
|
||||
* remoteproc address, endpoint callback, and destroy endpoint callback.
|
||||
*
|
||||
* API deprecated since release v2020.10
|
||||
*
|
||||
* @ept: pointer to rpmsg endpoint
|
||||
* @name: service name associated to the endpoint
|
||||
* @src: local address of the endpoint
|
||||
* @dest: target address of the endpoint
|
||||
* @cb: endpoint callback
|
||||
* @ns_unbind_cb: end point service unbind callback, called when remote ept is
|
||||
* destroyed.
|
||||
*/
|
||||
__deprecated static inline void rpmsg_init_ept(struct rpmsg_endpoint *ept,
|
||||
const char *name,
|
||||
uint32_t src, uint32_t dest,
|
||||
rpmsg_ept_cb cb,
|
||||
rpmsg_ns_unbind_cb ns_unbind_cb)
|
||||
{
|
||||
strncpy(ept->name, name ? name : "", sizeof(ept->name));
|
||||
ept->addr = src;
|
||||
ept->dest_addr = dest;
|
||||
ept->cb = cb;
|
||||
ept->ns_unbind_cb = ns_unbind_cb;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmsg_create_ept - create rpmsg endpoint and register it to rpmsg device
|
||||
*
|
||||
* Create a RPMsg endpoint, initialize it with a name, source address,
|
||||
* remoteproc address, endpoint callback, and destroy endpoint callback,
|
||||
* and register it to the RPMsg device.
|
||||
*
|
||||
* @ept: pointer to rpmsg endpoint
|
||||
* @name: service name associated to the endpoint
|
||||
* @src: local address of the endpoint
|
||||
* @dest: target address of the endpoint
|
||||
* @cb: endpoint callback
|
||||
* @ns_unbind_cb: end point service unbind callback, called when remote ept is
|
||||
* destroyed.
|
||||
*
|
||||
* In essence, an rpmsg endpoint represents a listener on the rpmsg bus, as
|
||||
* it binds an rpmsg address with an rx callback handler.
|
||||
*
|
||||
* Rpmsg client should create an endpoint to discuss with remote. rpmsg client
|
||||
* provide at least a channel name, a callback for message notification and by
|
||||
* default endpoint source address should be set to RPMSG_ADDR_ANY.
|
||||
*
|
||||
* As an option Some rpmsg clients can specify an endpoint with a specific
|
||||
* source address.
|
||||
*/
|
||||
|
||||
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 ns_unbind_cb);
|
||||
|
||||
/**
|
||||
* rpmsg_destroy_ept - destroy rpmsg endpoint and unregister it from rpmsg
|
||||
* device
|
||||
*
|
||||
* @ept: pointer to the rpmsg endpoint
|
||||
*
|
||||
* It unregisters the rpmsg endpoint from the rpmsg device and calls the
|
||||
* destroy endpoint callback if it is provided.
|
||||
*/
|
||||
void rpmsg_destroy_ept(struct rpmsg_endpoint *ept);
|
||||
|
||||
/**
|
||||
* is_rpmsg_ept_ready - check if the rpmsg endpoint ready to send
|
||||
*
|
||||
* @ept: pointer to rpmsg endpoint
|
||||
*
|
||||
* Returns 1 if the rpmsg endpoint has both local addr and destination
|
||||
* addr set, 0 otherwise
|
||||
*/
|
||||
static inline unsigned int is_rpmsg_ept_ready(struct rpmsg_endpoint *ept)
|
||||
{
|
||||
return ept && ept->rdev && ept->dest_addr != RPMSG_ADDR_ANY;
|
||||
}
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _RPMSG_H_ */
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Mentor Graphics Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef RPMSG_RETARGET_H
|
||||
#define RPMSG_RETARGET_H
|
||||
|
||||
#include <metal/mutex.h>
|
||||
#include <openamp/open_amp.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* File Operations System call definitions */
|
||||
#define OPEN_SYSCALL_ID 0x1UL
|
||||
#define CLOSE_SYSCALL_ID 0x2UL
|
||||
#define WRITE_SYSCALL_ID 0x3UL
|
||||
#define READ_SYSCALL_ID 0x4UL
|
||||
#define ACK_STATUS_ID 0x5UL
|
||||
|
||||
#define TERM_SYSCALL_ID 0x6UL
|
||||
|
||||
#define DEFAULT_PROXY_ENDPOINT 0xFFUL
|
||||
|
||||
struct rpmsg_rpc_data;
|
||||
|
||||
typedef int (*rpmsg_rpc_poll)(void *arg);
|
||||
typedef void (*rpmsg_rpc_shutdown_cb)(struct rpmsg_rpc_data *rpc);
|
||||
|
||||
struct rpmsg_rpc_syscall_header {
|
||||
int32_t int_field1;
|
||||
int32_t int_field2;
|
||||
uint32_t data_len;
|
||||
};
|
||||
|
||||
struct rpmsg_rpc_syscall {
|
||||
uint32_t id;
|
||||
struct rpmsg_rpc_syscall_header args;
|
||||
};
|
||||
|
||||
struct rpmsg_rpc_data {
|
||||
struct rpmsg_endpoint ept;
|
||||
int ept_destroyed;
|
||||
atomic_int nacked;
|
||||
void *respbuf;
|
||||
size_t respbuf_len;
|
||||
rpmsg_rpc_poll poll;
|
||||
void *poll_arg;
|
||||
rpmsg_rpc_shutdown_cb shutdown_cb;
|
||||
metal_mutex_t lock;
|
||||
struct metal_spinlock buflock;
|
||||
};
|
||||
|
||||
/**
|
||||
* rpmsg_rpc_init - initialize RPMsg remote procedure call
|
||||
*
|
||||
* This function is to initialize the remote procedure call
|
||||
* global data. RPMsg RPC will send request to remote and
|
||||
* wait for callback.
|
||||
*
|
||||
* @rpc: pointer to the global remote procedure call data
|
||||
* @rdev: pointer to the rpmsg device
|
||||
* @ept_name: name of the endpoint used by RPC
|
||||
* @ept_addr: address of the endpoint used by RPC
|
||||
* @ept_raddr: remote address of the endpoint used by RPC
|
||||
* @poll_arg: pointer to poll function argument
|
||||
* @poll: poll function
|
||||
* @shutdown_cb: shutdown callback function
|
||||
*
|
||||
* return 0 for success, and negative value for failure.
|
||||
*/
|
||||
int rpmsg_rpc_init(struct rpmsg_rpc_data *rpc,
|
||||
struct rpmsg_device *rdev,
|
||||
const char *ept_name, uint32_t ept_addr,
|
||||
uint32_t ept_raddr,
|
||||
void *poll_arg, rpmsg_rpc_poll poll,
|
||||
rpmsg_rpc_shutdown_cb shutdown_cb);
|
||||
|
||||
/**
|
||||
* rpmsg_rpc_release - release RPMsg remote procedure call
|
||||
*
|
||||
* This function is to release remoteproc procedure call
|
||||
* global data.
|
||||
*
|
||||
* @rpc: pointer to the globacl remote procedure call
|
||||
*/
|
||||
void rpmsg_rpc_release(struct rpmsg_rpc_data *rpc);
|
||||
|
||||
/**
|
||||
* rpmsg_rpc_send - Request RPMsg RPC call
|
||||
*
|
||||
* This function sends RPC request it will return with the length
|
||||
* of data and the response buffer.
|
||||
*
|
||||
* @rpc: pointer to remoteproc procedure call data struct
|
||||
* @req: pointer to request buffer
|
||||
* @len: length of the request data
|
||||
* @resp: pointer to where store the response buffer
|
||||
* @resp_len: length of the response buffer
|
||||
*
|
||||
* return length of the received response, negative value for failure.
|
||||
*/
|
||||
int rpmsg_rpc_send(struct rpmsg_rpc_data *rpc,
|
||||
void *req, size_t len,
|
||||
void *resp, size_t resp_len);
|
||||
|
||||
/**
|
||||
* rpmsg_set_default_rpc - set default RPMsg RPC data
|
||||
*
|
||||
* The default RPC data is used to redirect standard C file operations
|
||||
* to RPMsg channels.
|
||||
*
|
||||
* @rpc: pointer to remoteproc procedure call data struct
|
||||
*/
|
||||
void rpmsg_set_default_rpc(struct rpmsg_rpc_data *rpc);
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* RPMSG_RETARGET_H */
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright (c) 2021, L&T Technology Services Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef RPMSG_RPC_CLIENT_SERVER_H
|
||||
#define RPMSG_RPC_CLIENT_SERVER_H
|
||||
|
||||
#include <openamp/open_amp.h>
|
||||
#include <metal/compiler.h>
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define RPMSG_RPC_OK 0
|
||||
#define RPMSG_RPC_INVALID_ID (-1L)
|
||||
#define RPMSG_RPC_SERVICE_NAME "rpmsg-rpc"
|
||||
|
||||
/* RPMSG_BUFFER_SIZE = 512
|
||||
* sizeof(struct rpmsg_hdr) = 16
|
||||
* RPMSG_BUFFER_SIZE - sizeof(struct rpmsg_hdr) - 1 = 495
|
||||
* Aligning to 64 bits -> 488UL
|
||||
*/
|
||||
#define MAX_BUF_LEN 488UL
|
||||
#define MAX_FUNC_ID_LEN sizeof(unsigned long int)
|
||||
|
||||
struct rpmsg_rpc_clt;
|
||||
struct rpmsg_rpc_svr;
|
||||
|
||||
typedef void (*rpmsg_rpc_shutdown_cb)(struct rpmsg_rpc_clt *rpc);
|
||||
typedef void (*app_cb)(struct rpmsg_rpc_clt *rpc, int statust, void *data,
|
||||
size_t len);
|
||||
typedef int (*rpmsg_rpc_syscall_cb)(void *data, struct rpmsg_rpc_svr *rpcs);
|
||||
|
||||
/**
|
||||
* struct rpmsg_rpc_request - rpc request message
|
||||
*
|
||||
* @id: service id
|
||||
* @params: request params
|
||||
*
|
||||
*/
|
||||
struct rpmsg_rpc_request {
|
||||
uint32_t id;
|
||||
unsigned char params[MAX_BUF_LEN];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rpmsg_rpc_answer - rpc request message
|
||||
*
|
||||
* @id: service id
|
||||
* @status: status of rpc
|
||||
* @params: answer params
|
||||
*
|
||||
*/
|
||||
METAL_PACKED_BEGIN
|
||||
struct rpmsg_rpc_answer {
|
||||
uint32_t id;
|
||||
int32_t status;
|
||||
unsigned char params[MAX_BUF_LEN];
|
||||
} METAL_PACKED_END;
|
||||
|
||||
/**
|
||||
* struct rpmsg_rpc_services - table for services
|
||||
*
|
||||
* @id: service id
|
||||
* @cb_function: id callback
|
||||
*
|
||||
*/
|
||||
struct rpmsg_rpc_services {
|
||||
uint32_t id;
|
||||
rpmsg_rpc_syscall_cb cb_function;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rpmsg_rpc_client_services - table for client services
|
||||
*
|
||||
* @id: service id
|
||||
* @app_cb: id callback
|
||||
*
|
||||
*/
|
||||
struct rpmsg_rpc_client_services {
|
||||
uint32_t id;
|
||||
app_cb cb;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rpmsg_rpc_svr - server remote procedure call data
|
||||
*
|
||||
* RPMsg RPC will send request to endpoint
|
||||
*
|
||||
* @ept: rpmsg_endpoint structure
|
||||
* @services: service table
|
||||
* @n_services: number of services
|
||||
*
|
||||
*/
|
||||
struct rpmsg_rpc_svr {
|
||||
struct rpmsg_endpoint ept;
|
||||
const struct rpmsg_rpc_services *services;
|
||||
unsigned int n_services;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rpmsg_rpc_clt - client remote procedure call data
|
||||
*
|
||||
* RPMsg RPC will send request to remote and
|
||||
* wait for callback.
|
||||
*
|
||||
* @ept: rpmsg_endpoint structure
|
||||
* @shutdown_cb: shutdown callback function
|
||||
* @services: service table
|
||||
* @n_services: number of services
|
||||
*
|
||||
*/
|
||||
struct rpmsg_rpc_clt {
|
||||
struct rpmsg_endpoint ept;
|
||||
rpmsg_rpc_shutdown_cb shutdown_cb;
|
||||
const struct rpmsg_rpc_client_services *services;
|
||||
unsigned int n_services;
|
||||
};
|
||||
|
||||
/**
|
||||
* rpmsg_rpc_client_release - release RPMsg remote procedure call
|
||||
*
|
||||
* This function is to release remoteproc procedure call service
|
||||
*
|
||||
* @rpc: pointer to the client remote procedure call data
|
||||
*
|
||||
*/
|
||||
void rpmsg_rpc_client_release(struct rpmsg_rpc_clt *rpc);
|
||||
|
||||
/**
|
||||
* rpmsg_rpc_client_init - initialize RPMsg remote procedure call
|
||||
*
|
||||
* This function is to initialize the remote procedure call
|
||||
* client data. RPMsg RPC will send request to remote and
|
||||
* wait for callback and load services to table
|
||||
*
|
||||
* @rpc: pointer to the client remote procedure call data
|
||||
* @rdev: pointer to the rpmsg device
|
||||
* @shutdown_cb: shutdown callback function
|
||||
* @services: pointer to service table
|
||||
* @len: length of table
|
||||
*
|
||||
* return 0 for success, and negative value for failure
|
||||
*/
|
||||
int rpmsg_rpc_client_init(struct rpmsg_rpc_clt *rpc,
|
||||
struct rpmsg_device *rdev,
|
||||
rpmsg_rpc_shutdown_cb shutdown_cb,
|
||||
const struct rpmsg_rpc_client_services *services,
|
||||
int len);
|
||||
|
||||
/**
|
||||
* rpmsg_rpc_server_init - initialize RPMsg rpc for server
|
||||
*
|
||||
* This function create endpoint and loads services into table
|
||||
*
|
||||
* @rpcs: pointer to the server rpc
|
||||
* @rdev: pointer to the rpmsg device
|
||||
* @services: pointer to service table
|
||||
* @len: length of table
|
||||
* @rpmsg_service_server_unbind: unbind function callback
|
||||
*
|
||||
* return 0 for success, and negative value for failure
|
||||
*/
|
||||
int rpmsg_rpc_server_init(struct rpmsg_rpc_svr *rpcs, struct rpmsg_device *rdev,
|
||||
const struct rpmsg_rpc_services *services, int len,
|
||||
rpmsg_ns_unbind_cb rpmsg_service_server_unbind);
|
||||
|
||||
/**
|
||||
* rpmsg_rpc_client_send - Request RPMsg RPC call
|
||||
*
|
||||
* @rpc: pointer to client remoteproc procedure call data
|
||||
* @rpc_id: function id
|
||||
* @request_param: pointer to request buffer
|
||||
* @req_param_size: length of the request data
|
||||
*
|
||||
* return length of the received response, negative value for failure.
|
||||
*/
|
||||
int rpmsg_rpc_client_send(struct rpmsg_rpc_clt *rpc,
|
||||
unsigned int rpc_id, void *request_param,
|
||||
size_t req_param_size);
|
||||
|
||||
/**
|
||||
* rpmsg_rpc_server_send - Request RPMsg RPC call
|
||||
*
|
||||
* This function sends RPC request
|
||||
*
|
||||
* @rpcs: pointer to server rpc data
|
||||
* @rpc_id: function id
|
||||
* @status: status of rpc
|
||||
* @request_param: pointer to request buffer
|
||||
* @param_size: length of the request data
|
||||
*
|
||||
* return length of the received response, negative value for failure.
|
||||
*/
|
||||
int rpmsg_rpc_server_send(struct rpmsg_rpc_svr *rpcs, uint32_t rpc_id,
|
||||
int status, void *request_param,
|
||||
size_t param_size);
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* RPMSG_RPC_CLIENT_SERVER_H */
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* rpmsg based on virtio
|
||||
*
|
||||
* Copyright (C) 2018 Linaro, Inc.
|
||||
*
|
||||
* All rights reserved.
|
||||
* Copyright (c) 2016 Freescale Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _RPMSG_VIRTIO_H_
|
||||
#define _RPMSG_VIRTIO_H_
|
||||
|
||||
#include <metal/io.h>
|
||||
#include <metal/mutex.h>
|
||||
#include <openamp/rpmsg.h>
|
||||
#include <openamp/virtio.h>
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Configurable parameters */
|
||||
#ifndef RPMSG_BUFFER_SIZE
|
||||
#define RPMSG_BUFFER_SIZE (512)
|
||||
#endif
|
||||
|
||||
/* The feature bitmap for virtio rpmsg */
|
||||
#define VIRTIO_RPMSG_F_NS 0 /* RP supports name service notifications */
|
||||
|
||||
/**
|
||||
* struct rpmsg_virtio_shm_pool - shared memory pool used for rpmsg buffers
|
||||
* @base: base address of the memory pool
|
||||
* @avail: available memory size
|
||||
* @size: total pool size
|
||||
*/
|
||||
struct rpmsg_virtio_shm_pool {
|
||||
void *base;
|
||||
size_t avail;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rpmsg_virtio_device - representation of a rpmsg device based on virtio
|
||||
* @rdev: rpmsg device, first property in the struct
|
||||
* @vdev: pointer to the virtio device
|
||||
* @rvq: pointer to receive virtqueue
|
||||
* @svq: pointer to send virtqueue
|
||||
* @shbuf_io: pointer to the shared buffer I/O region
|
||||
* @shpool: pointer to the shared buffers pool
|
||||
*/
|
||||
struct rpmsg_virtio_device {
|
||||
struct rpmsg_device rdev;
|
||||
struct virtio_device *vdev;
|
||||
struct virtqueue *rvq;
|
||||
struct virtqueue *svq;
|
||||
struct metal_io_region *shbuf_io;
|
||||
struct rpmsg_virtio_shm_pool *shpool;
|
||||
};
|
||||
|
||||
#define RPMSG_REMOTE VIRTIO_DEV_SLAVE
|
||||
#define RPMSG_MASTER VIRTIO_DEV_MASTER
|
||||
|
||||
static inline unsigned int
|
||||
rpmsg_virtio_get_role(struct rpmsg_virtio_device *rvdev)
|
||||
{
|
||||
return rvdev->vdev->role;
|
||||
}
|
||||
|
||||
static inline void rpmsg_virtio_set_status(struct rpmsg_virtio_device *rvdev,
|
||||
uint8_t status)
|
||||
{
|
||||
rvdev->vdev->func->set_status(rvdev->vdev, status);
|
||||
}
|
||||
|
||||
static inline uint8_t rpmsg_virtio_get_status(struct rpmsg_virtio_device *rvdev)
|
||||
{
|
||||
return rvdev->vdev->func->get_status(rvdev->vdev);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
rpmsg_virtio_get_features(struct rpmsg_virtio_device *rvdev)
|
||||
{
|
||||
return rvdev->vdev->func->get_features(rvdev->vdev);
|
||||
}
|
||||
|
||||
static inline void
|
||||
rpmsg_virtio_read_config(struct rpmsg_virtio_device *rvdev,
|
||||
uint32_t offset, void *dst, int length)
|
||||
{
|
||||
rvdev->vdev->func->read_config(rvdev->vdev, offset, dst, length);
|
||||
}
|
||||
|
||||
static inline void
|
||||
rpmsg_virtio_write_config(struct rpmsg_virtio_device *rvdev,
|
||||
uint32_t offset, void *dst, int length)
|
||||
{
|
||||
rvdev->vdev->func->write_config(rvdev->vdev, offset, dst, length);
|
||||
}
|
||||
|
||||
static inline int
|
||||
rpmsg_virtio_create_virtqueues(struct rpmsg_virtio_device *rvdev,
|
||||
int flags, unsigned int nvqs,
|
||||
const char *names[],
|
||||
vq_callback *callbacks)
|
||||
{
|
||||
return virtio_create_virtqueues(rvdev->vdev, flags, nvqs, names,
|
||||
callbacks);
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmsg_virtio_get_buffer_size - get rpmsg virtio buffer size
|
||||
*
|
||||
* @rdev - pointer to the rpmsg device
|
||||
*
|
||||
* @return - next available buffer size for text, negative value for failure
|
||||
*/
|
||||
int rpmsg_virtio_get_buffer_size(struct rpmsg_device *rdev);
|
||||
|
||||
/**
|
||||
* rpmsg_init_vdev - initialize rpmsg virtio device
|
||||
* Master side:
|
||||
* Initialize RPMsg virtio queues and shared buffers, the address of shm can be
|
||||
* ANY. In this case, function will get shared memory from system shared memory
|
||||
* pools. If the vdev has RPMsg name service feature, this API will create an
|
||||
* name service endpoint.
|
||||
*
|
||||
* Slave side:
|
||||
* This API will not return until the driver ready is set by the master side.
|
||||
*
|
||||
* @param rvdev - pointer to the rpmsg virtio device
|
||||
* @param vdev - pointer to the virtio device
|
||||
* @param ns_bind_cb - callback handler for name service announcement without
|
||||
* local endpoints waiting to bind.
|
||||
* @param shm_io - pointer to the share memory I/O region.
|
||||
* @param shpool - pointer to shared memory pool. rpmsg_virtio_init_shm_pool has
|
||||
* to be called first to fill this structure.
|
||||
*
|
||||
* @return - status of function execution
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* rpmsg_deinit_vdev - deinitialize rpmsg virtio device
|
||||
*
|
||||
* @param rvdev - pointer to the rpmsg virtio device
|
||||
*/
|
||||
void rpmsg_deinit_vdev(struct rpmsg_virtio_device *rvdev);
|
||||
|
||||
/**
|
||||
* rpmsg_virtio_init_shm_pool - initialize default shared buffers pool
|
||||
*
|
||||
* RPMsg virtio has default shared buffers pool implementation.
|
||||
* The memory assigned to this pool will be dedicated to the RPMsg
|
||||
* virtio. This function has to be called before calling rpmsg_init_vdev,
|
||||
* to initialize the rpmsg_virtio_shm_pool structure.
|
||||
*
|
||||
* @param shpool - pointer to the shared buffers pool structure
|
||||
* @param shbuf - pointer to the beginning of shared buffers
|
||||
* @param size - shared buffers total size
|
||||
*/
|
||||
void rpmsg_virtio_init_shm_pool(struct rpmsg_virtio_shm_pool *shpool,
|
||||
void *shbuf, size_t size);
|
||||
|
||||
/**
|
||||
* rpmsg_virtio_get_rpmsg_device - get RPMsg device from RPMsg virtio device
|
||||
*
|
||||
* @param rvdev - pointer to RPMsg virtio device
|
||||
* @return - RPMsg device pointed by RPMsg virtio device
|
||||
*/
|
||||
static inline struct rpmsg_device *
|
||||
rpmsg_virtio_get_rpmsg_device(struct rpmsg_virtio_device *rvdev)
|
||||
{
|
||||
return &rvdev->rdev;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmsg_virtio_shm_pool_get_buffer - get buffer in the shared memory pool
|
||||
*
|
||||
* RPMsg virtio has default shared buffers pool implementation.
|
||||
* The memory assigned to this pool will be dedicated to the RPMsg
|
||||
* virtio. If you prefer to have other shared buffers allocation,
|
||||
* you can implement your rpmsg_virtio_shm_pool_get_buffer function.
|
||||
*
|
||||
* @param shpool - pointer to the shared buffers pool
|
||||
* @param size - shared buffers total size
|
||||
* @return - buffer pointer if free buffer is available, NULL otherwise.
|
||||
*/
|
||||
metal_weak void *
|
||||
rpmsg_virtio_shm_pool_get_buffer(struct rpmsg_virtio_shm_pool *shpool,
|
||||
size_t size);
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _RPMSG_VIRTIO_H_ */
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Mentor Graphics Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef RSC_TABLE_PARSER_H
|
||||
#define RSC_TABLE_PARSER_H
|
||||
|
||||
#include <openamp/remoteproc.h>
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define RSC_TAB_SUPPORTED_VERSION 1
|
||||
|
||||
/* Standard control request handling. */
|
||||
typedef int (*rsc_handler)(struct remoteproc *rproc, void *rsc);
|
||||
|
||||
/**
|
||||
* handle_rsc_table
|
||||
*
|
||||
* This function parses resource table.
|
||||
*
|
||||
* @param rproc - pointer to remote remoteproc
|
||||
* @param rsc_table - resource table to parse
|
||||
* @param len - size of rsc table
|
||||
* @param io - pointer to the resource table I/O region
|
||||
* It can be NULL if the resource table
|
||||
* is in the local memory.
|
||||
*
|
||||
* @returns - execution status
|
||||
*
|
||||
*/
|
||||
int handle_rsc_table(struct remoteproc *rproc,
|
||||
struct resource_table *rsc_table, size_t len,
|
||||
struct metal_io_region *io);
|
||||
int handle_carve_out_rsc(struct remoteproc *rproc, void *rsc);
|
||||
int handle_trace_rsc(struct remoteproc *rproc, void *rsc);
|
||||
int handle_vdev_rsc(struct remoteproc *rproc, void *rsc);
|
||||
int handle_vendor_rsc(struct remoteproc *rproc, void *rsc);
|
||||
|
||||
/**
|
||||
* find_rsc
|
||||
*
|
||||
* find out location of a resource type in the resource table.
|
||||
*
|
||||
* @rsc_table - pointer to the resource table
|
||||
* @rsc_type - type of the resource
|
||||
* @index - index of the resource of the specified type
|
||||
*
|
||||
* return the offset to the resource on success, or 0 on failure
|
||||
*/
|
||||
size_t find_rsc(void *rsc_table, unsigned int rsc_type, unsigned int index);
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* RSC_TABLE_PARSER_H */
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2021, STMicroelectronics.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file version.h
|
||||
* @brief Library version information for OpenAMP.
|
||||
*/
|
||||
|
||||
#ifndef __OPENAMP_VERSION__H__
|
||||
#define __OPENAMP_VERSION__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup versions Library Version Interfaces
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Library major version number.
|
||||
*
|
||||
* Return the major version number of the library linked into the application.
|
||||
* This is required to match the value of LIB_VERSION_MAJOR, which is the major
|
||||
* version of the library that the application was compiled against.
|
||||
*
|
||||
* @return Library major version number.
|
||||
* @see PROJECT_VERSION_MAJOR
|
||||
*/
|
||||
extern int openamp_version_major(void);
|
||||
|
||||
/**
|
||||
* @brief Library minor version number.
|
||||
*
|
||||
* Return the minor version number of the library linked into the application.
|
||||
* This could differ from the value of LIB_VERSION_MINOR, which is the minor
|
||||
* version of the library that the application was compiled against.
|
||||
*
|
||||
* @return Library minor version number.
|
||||
* @see PROJECT_VERSION_MINOR
|
||||
*/
|
||||
extern int openamp_version_minor(void);
|
||||
|
||||
/**
|
||||
* @brief Library patch level.
|
||||
*
|
||||
* Return the patch level of the library linked into the application. This
|
||||
* could differ from the value of LIB_VERSION_PATCH, which is the patch level of
|
||||
* the library that the application was compiled against.
|
||||
*
|
||||
* @return Library patch level.
|
||||
* @see PROJECT_VERSION_PATCH
|
||||
*/
|
||||
extern int openamp_version_patch(void);
|
||||
|
||||
/**
|
||||
* @brief Library version string.
|
||||
*
|
||||
* Return the version string of the library linked into the application. This
|
||||
* could differ from the value of LIB_VERSION, which is the version string of
|
||||
* the library that the application was compiled against.
|
||||
*
|
||||
* @return Library version string.
|
||||
* @see PROJECT_VERSION
|
||||
*/
|
||||
extern const char *openamp_version(void);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __OPENAMP_VERSION__H__ */
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _VIRTIO_H_
|
||||
#define _VIRTIO_H_
|
||||
|
||||
#include <openamp/virtqueue.h>
|
||||
#include <metal/spinlock.h>
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* VirtIO device IDs. */
|
||||
#define VIRTIO_ID_NETWORK 0x01UL
|
||||
#define VIRTIO_ID_BLOCK 0x02UL
|
||||
#define VIRTIO_ID_CONSOLE 0x03UL
|
||||
#define VIRTIO_ID_ENTROPY 0x04UL
|
||||
#define VIRTIO_ID_BALLOON 0x05UL
|
||||
#define VIRTIO_ID_IOMEMORY 0x06UL
|
||||
#define VIRTIO_ID_RPMSG 0x07UL /* remote processor messaging */
|
||||
#define VIRTIO_ID_SCSI 0x08UL
|
||||
#define VIRTIO_ID_9P 0x09UL
|
||||
#define VIRTIO_DEV_ANY_ID (-1)UL
|
||||
|
||||
/* Status byte for guest to report progress. */
|
||||
#define VIRTIO_CONFIG_STATUS_ACK 0x01
|
||||
#define VIRTIO_CONFIG_STATUS_DRIVER 0x02
|
||||
#define VIRTIO_CONFIG_STATUS_DRIVER_OK 0x04
|
||||
#define VIRTIO_CONFIG_STATUS_NEEDS_RESET 0x40
|
||||
#define VIRTIO_CONFIG_STATUS_FAILED 0x80
|
||||
|
||||
/* Virtio device role */
|
||||
#define VIRTIO_DEV_MASTER 0UL
|
||||
#define VIRTIO_DEV_SLAVE 1UL
|
||||
|
||||
struct virtio_device_id {
|
||||
uint32_t device;
|
||||
uint32_t vendor;
|
||||
};
|
||||
|
||||
/*
|
||||
* Generate interrupt when the virtqueue ring is
|
||||
* completely used, even if we've suppressed them.
|
||||
*/
|
||||
#define VIRTIO_F_NOTIFY_ON_EMPTY (1 << 24)
|
||||
|
||||
/*
|
||||
* The guest should never negotiate this feature; it
|
||||
* is used to detect faulty drivers.
|
||||
*/
|
||||
#define VIRTIO_F_BAD_FEATURE (1 << 30)
|
||||
|
||||
/*
|
||||
* Some VirtIO feature bits (currently bits 28 through 31) are
|
||||
* reserved for the transport being used (eg. virtio_ring), the
|
||||
* rest are per-device feature bits.
|
||||
*/
|
||||
#define VIRTIO_TRANSPORT_F_START 28
|
||||
#define VIRTIO_TRANSPORT_F_END 32
|
||||
|
||||
typedef void (*virtio_dev_reset_cb)(struct virtio_device *vdev);
|
||||
|
||||
struct virtio_dispatch;
|
||||
|
||||
struct virtio_feature_desc {
|
||||
uint32_t vfd_val;
|
||||
const char *vfd_str;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct virtio_vring_info
|
||||
* @vq virtio queue
|
||||
* @info vring alloc info
|
||||
* @notifyid vring notify id
|
||||
* @io metal I/O region of the vring memory, can be NULL
|
||||
*/
|
||||
struct virtio_vring_info {
|
||||
struct virtqueue *vq;
|
||||
struct vring_alloc_info info;
|
||||
uint32_t notifyid;
|
||||
struct metal_io_region *io;
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure definition for virtio devices for use by the
|
||||
* applications/drivers
|
||||
*/
|
||||
|
||||
struct virtio_device {
|
||||
uint32_t notifyid; /**< unique position on the virtio bus */
|
||||
struct virtio_device_id id; /**< the device type identification
|
||||
* (used to match it with a driver
|
||||
*/
|
||||
uint64_t features; /**< the features supported by both ends. */
|
||||
unsigned int role; /**< if it is virtio backend or front end. */
|
||||
virtio_dev_reset_cb reset_cb; /**< user registered device callback */
|
||||
const struct virtio_dispatch *func; /**< Virtio dispatch table */
|
||||
void *priv; /**< TODO: remove pointer to virtio_device private data */
|
||||
unsigned int vrings_num; /**< number of vrings */
|
||||
struct virtio_vring_info *vrings_info;
|
||||
};
|
||||
|
||||
/*
|
||||
* Helper functions.
|
||||
*/
|
||||
const char *virtio_dev_name(uint16_t devid);
|
||||
void virtio_describe(struct virtio_device *dev, const char *msg,
|
||||
uint32_t features,
|
||||
struct virtio_feature_desc *feature_desc);
|
||||
|
||||
/*
|
||||
* Functions for virtio device configuration as defined in Rusty Russell's
|
||||
* paper.
|
||||
* Drivers are expected to implement these functions in their respective codes.
|
||||
*/
|
||||
|
||||
struct virtio_dispatch {
|
||||
uint8_t (*get_status)(struct virtio_device *dev);
|
||||
void (*set_status)(struct virtio_device *dev, uint8_t status);
|
||||
uint32_t (*get_features)(struct virtio_device *dev);
|
||||
void (*set_features)(struct virtio_device *dev, uint32_t feature);
|
||||
uint32_t (*negotiate_features)(struct virtio_device *dev,
|
||||
uint32_t features);
|
||||
|
||||
/*
|
||||
* Read/write a variable amount from the device specific (ie, network)
|
||||
* configuration region. This region is encoded in the same endian as
|
||||
* the guest.
|
||||
*/
|
||||
void (*read_config)(struct virtio_device *dev, uint32_t offset,
|
||||
void *dst, int length);
|
||||
void (*write_config)(struct virtio_device *dev, uint32_t offset,
|
||||
void *src, int length);
|
||||
void (*reset_device)(struct virtio_device *dev);
|
||||
void (*notify)(struct virtqueue *vq);
|
||||
};
|
||||
|
||||
int virtio_create_virtqueues(struct virtio_device *vdev, unsigned int flags,
|
||||
unsigned int nvqs, const char *names[],
|
||||
vq_callback callbacks[]);
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _VIRTIO_H_ */
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright Rusty Russell IBM Corporation 2007.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef VIRTIO_RING_H
|
||||
#define VIRTIO_RING_H
|
||||
|
||||
#include <metal/compiler.h>
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This marks a buffer as continuing via the next field. */
|
||||
#define VRING_DESC_F_NEXT 1
|
||||
/* This marks a buffer as write-only (otherwise read-only). */
|
||||
#define VRING_DESC_F_WRITE 2
|
||||
/* This means the buffer contains a list of buffer descriptors. */
|
||||
#define VRING_DESC_F_INDIRECT 4
|
||||
|
||||
/* The Host uses this in used->flags to advise the Guest: don't kick me
|
||||
* when you add a buffer. It's unreliable, so it's simply an
|
||||
* optimization. Guest will still kick if it's out of buffers.
|
||||
*/
|
||||
#define VRING_USED_F_NO_NOTIFY 1
|
||||
/* The Guest uses this in avail->flags to advise the Host: don't
|
||||
* interrupt me when you consume a buffer. It's unreliable, so it's
|
||||
* simply an optimization.
|
||||
*/
|
||||
#define VRING_AVAIL_F_NO_INTERRUPT 1
|
||||
|
||||
/* VirtIO ring descriptors: 16 bytes.
|
||||
* These can chain together via "next".
|
||||
*/
|
||||
METAL_PACKED_BEGIN
|
||||
struct vring_desc {
|
||||
/* Address (guest-physical). */
|
||||
uint64_t addr;
|
||||
/* Length. */
|
||||
uint32_t len;
|
||||
/* The flags as indicated above. */
|
||||
uint16_t flags;
|
||||
/* We chain unused descriptors via this, too. */
|
||||
uint16_t next;
|
||||
} METAL_PACKED_END;
|
||||
|
||||
METAL_PACKED_BEGIN
|
||||
struct vring_avail {
|
||||
uint16_t flags;
|
||||
uint16_t idx;
|
||||
uint16_t ring[0];
|
||||
} METAL_PACKED_END;
|
||||
|
||||
/* uint32_t is used here for ids for padding reasons. */
|
||||
METAL_PACKED_BEGIN
|
||||
struct vring_used_elem {
|
||||
union {
|
||||
uint16_t event;
|
||||
/* Index of start of used descriptor chain. */
|
||||
uint32_t id;
|
||||
};
|
||||
/* Total length of the descriptor chain which was written to. */
|
||||
uint32_t len;
|
||||
} METAL_PACKED_END;
|
||||
|
||||
METAL_PACKED_BEGIN
|
||||
struct vring_used {
|
||||
uint16_t flags;
|
||||
uint16_t idx;
|
||||
struct vring_used_elem ring[0];
|
||||
} METAL_PACKED_END;
|
||||
|
||||
struct vring {
|
||||
unsigned int num;
|
||||
|
||||
struct vring_desc *desc;
|
||||
struct vring_avail *avail;
|
||||
struct vring_used *used;
|
||||
};
|
||||
|
||||
/* The standard layout for the ring is a continuous chunk of memory which
|
||||
* looks like this. We assume num is a power of 2.
|
||||
*
|
||||
* struct vring {
|
||||
* // The actual descriptors (16 bytes each)
|
||||
* struct vring_desc desc[num];
|
||||
*
|
||||
* // A ring of available descriptor heads with free-running index.
|
||||
* __u16 avail_flags;
|
||||
* __u16 avail_idx;
|
||||
* __u16 available[num];
|
||||
* __u16 used_event_idx;
|
||||
*
|
||||
* // Padding to the next align boundary.
|
||||
* char pad[];
|
||||
*
|
||||
* // A ring of used descriptor heads with free-running index.
|
||||
* __u16 used_flags;
|
||||
* __u16 used_idx;
|
||||
* struct vring_used_elem used[num];
|
||||
* __u16 avail_event_idx;
|
||||
* };
|
||||
*
|
||||
* NOTE: for VirtIO PCI, align is 4096.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We publish the used event index at the end of the available ring, and vice
|
||||
* versa. They are at the end for backwards compatibility.
|
||||
*/
|
||||
#define vring_used_event(vr) ((vr)->avail->ring[(vr)->num])
|
||||
#define vring_avail_event(vr) ((vr)->used->ring[(vr)->num].event)
|
||||
|
||||
static inline int vring_size(unsigned int num, unsigned long align)
|
||||
{
|
||||
int size;
|
||||
|
||||
size = num * sizeof(struct vring_desc);
|
||||
size += sizeof(struct vring_avail) + (num * sizeof(uint16_t)) +
|
||||
sizeof(uint16_t);
|
||||
size = (size + align - 1) & ~(align - 1);
|
||||
size += sizeof(struct vring_used) +
|
||||
(num * sizeof(struct vring_used_elem)) + sizeof(uint16_t);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static inline void
|
||||
vring_init(struct vring *vr, unsigned int num, uint8_t *p, unsigned long align)
|
||||
{
|
||||
vr->num = num;
|
||||
vr->desc = (struct vring_desc *)p;
|
||||
vr->avail = (struct vring_avail *)(p + num * sizeof(struct vring_desc));
|
||||
vr->used = (struct vring_used *)
|
||||
(((unsigned long)&vr->avail->ring[num] + sizeof(uint16_t) +
|
||||
align - 1) & ~(align - 1));
|
||||
}
|
||||
|
||||
/*
|
||||
* The following is used with VIRTIO_RING_F_EVENT_IDX.
|
||||
*
|
||||
* Assuming a given event_idx value from the other size, if we have
|
||||
* just incremented index from old to new_idx, should we trigger an
|
||||
* event?
|
||||
*/
|
||||
static inline int
|
||||
vring_need_event(uint16_t event_idx, uint16_t new_idx, uint16_t old)
|
||||
{
|
||||
return (uint16_t)(new_idx - event_idx - 1) <
|
||||
(uint16_t)(new_idx - old);
|
||||
}
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VIRTIO_RING_H */
|
||||
@@ -0,0 +1,230 @@
|
||||
#ifndef VIRTQUEUE_H_
|
||||
#define VIRTQUEUE_H_
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <openamp/virtio_ring.h>
|
||||
#include <metal/alloc.h>
|
||||
#include <metal/io.h>
|
||||
|
||||
/* Error Codes */
|
||||
#define VQ_ERROR_BASE -3000
|
||||
#define ERROR_VRING_FULL (VQ_ERROR_BASE - 1)
|
||||
#define ERROR_INVLD_DESC_IDX (VQ_ERROR_BASE - 2)
|
||||
#define ERROR_EMPTY_RING (VQ_ERROR_BASE - 3)
|
||||
#define ERROR_NO_MEM (VQ_ERROR_BASE - 4)
|
||||
#define ERROR_VRING_MAX_DESC (VQ_ERROR_BASE - 5)
|
||||
#define ERROR_VRING_ALIGN (VQ_ERROR_BASE - 6)
|
||||
#define ERROR_VRING_NO_BUFF (VQ_ERROR_BASE - 7)
|
||||
#define ERROR_VQUEUE_INVLD_PARAM (VQ_ERROR_BASE - 8)
|
||||
|
||||
#define VQUEUE_SUCCESS 0
|
||||
|
||||
/* The maximum virtqueue size is 2^15. Use that value as the end of
|
||||
* descriptor chain terminator since it will never be a valid index
|
||||
* in the descriptor table. This is used to verify we are correctly
|
||||
* handling vq_free_cnt.
|
||||
*/
|
||||
#define VQ_RING_DESC_CHAIN_END 32768
|
||||
|
||||
/* Support for indirect buffer descriptors. */
|
||||
#define VIRTIO_RING_F_INDIRECT_DESC (1 << 28)
|
||||
|
||||
/* Support to suppress interrupt until specific index is reached. */
|
||||
#define VIRTIO_RING_F_EVENT_IDX (1 << 29)
|
||||
|
||||
struct virtqueue_buf {
|
||||
void *buf;
|
||||
int len;
|
||||
};
|
||||
|
||||
struct vq_desc_extra {
|
||||
void *cookie;
|
||||
uint16_t ndescs;
|
||||
};
|
||||
|
||||
struct virtqueue {
|
||||
struct virtio_device *vq_dev;
|
||||
const char *vq_name;
|
||||
uint16_t vq_queue_index;
|
||||
uint16_t vq_nentries;
|
||||
void (*callback)(struct virtqueue *vq);
|
||||
void (*notify)(struct virtqueue *vq);
|
||||
struct vring vq_ring;
|
||||
uint16_t vq_free_cnt;
|
||||
uint16_t vq_queued_cnt;
|
||||
void *shm_io; /* opaque pointer to data needed to allow v2p & p2v */
|
||||
|
||||
/*
|
||||
* Head of the free chain in the descriptor table. If
|
||||
* there are no free descriptors, this will be set to
|
||||
* VQ_RING_DESC_CHAIN_END.
|
||||
*/
|
||||
uint16_t vq_desc_head_idx;
|
||||
|
||||
/*
|
||||
* Last consumed descriptor in the used table,
|
||||
* trails vq_ring.used->idx.
|
||||
*/
|
||||
uint16_t vq_used_cons_idx;
|
||||
|
||||
/*
|
||||
* Last consumed descriptor in the available table -
|
||||
* used by the consumer side.
|
||||
*/
|
||||
uint16_t vq_available_idx;
|
||||
|
||||
#ifdef VQUEUE_DEBUG
|
||||
bool vq_inuse;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Used by the host side during callback. Cookie
|
||||
* holds the address of buffer received from other side.
|
||||
* Other fields in this structure are not used currently.
|
||||
*/
|
||||
|
||||
struct vq_desc_extra vq_descx[0];
|
||||
};
|
||||
|
||||
/* struct to hold vring specific information */
|
||||
struct vring_alloc_info {
|
||||
void *vaddr;
|
||||
uint32_t align;
|
||||
uint16_t num_descs;
|
||||
uint16_t pad;
|
||||
};
|
||||
|
||||
typedef void (*vq_callback)(struct virtqueue *);
|
||||
typedef void (*vq_notify)(struct virtqueue *);
|
||||
|
||||
#ifdef VQUEUE_DEBUG
|
||||
#include <metal/log.h>
|
||||
#include <metal/assert.h>
|
||||
|
||||
#define VQASSERT(_vq, _exp, _msg) \
|
||||
do { \
|
||||
if (!(_exp)) { \
|
||||
metal_log(METAL_LOG_EMERGENCY, \
|
||||
"%s: %s - "_msg, __func__, (_vq)->vq_name); \
|
||||
metal_assert(_exp); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define VQ_RING_ASSERT_VALID_IDX(_vq, _idx) \
|
||||
VQASSERT((_vq), (_idx) < (_vq)->vq_nentries, "invalid ring index")
|
||||
|
||||
#define VQ_RING_ASSERT_CHAIN_TERM(_vq) \
|
||||
VQASSERT((_vq), (_vq)->vq_desc_head_idx == \
|
||||
VQ_RING_DESC_CHAIN_END, \
|
||||
"full ring terminated incorrectly: invalid head")
|
||||
|
||||
#define VQ_PARAM_CHK(condition, status_var, status_err) \
|
||||
do { \
|
||||
if (((status_var) == 0) && (condition)) { \
|
||||
status_var = status_err; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define VQUEUE_BUSY(vq) \
|
||||
do { \
|
||||
if (!(vq)->vq_inuse) \
|
||||
(vq)->vq_inuse = true; \
|
||||
else \
|
||||
VQASSERT(vq, !(vq)->vq_inuse,\
|
||||
"VirtQueue already in use"); \
|
||||
} while (0)
|
||||
|
||||
#define VQUEUE_IDLE(vq) ((vq)->vq_inuse = false)
|
||||
|
||||
#else
|
||||
|
||||
#define VQASSERT(_vq, _exp, _msg)
|
||||
#define VQ_RING_ASSERT_VALID_IDX(_vq, _idx)
|
||||
#define VQ_RING_ASSERT_CHAIN_TERM(_vq)
|
||||
#define VQ_PARAM_CHK(condition, status_var, status_err)
|
||||
#define VQUEUE_BUSY(vq)
|
||||
#define VQUEUE_IDLE(vq)
|
||||
|
||||
#endif
|
||||
|
||||
int virtqueue_create(struct virtio_device *device, unsigned short id,
|
||||
const char *name, struct vring_alloc_info *ring,
|
||||
void (*callback)(struct virtqueue *vq),
|
||||
void (*notify)(struct virtqueue *vq),
|
||||
struct virtqueue *vq);
|
||||
|
||||
/*
|
||||
* virtqueue_set_shmem_io
|
||||
*
|
||||
* set virtqueue shared memory I/O region
|
||||
*
|
||||
* @vq - virt queue
|
||||
* @io - pointer to the shared memory I/O region
|
||||
*/
|
||||
static inline void virtqueue_set_shmem_io(struct virtqueue *vq,
|
||||
struct metal_io_region *io)
|
||||
{
|
||||
vq->shm_io = io;
|
||||
}
|
||||
|
||||
int virtqueue_add_buffer(struct virtqueue *vq, struct virtqueue_buf *buf_list,
|
||||
int readable, int writable, void *cookie);
|
||||
|
||||
void *virtqueue_get_buffer(struct virtqueue *vq, uint32_t *len, uint16_t *idx);
|
||||
|
||||
void *virtqueue_get_available_buffer(struct virtqueue *vq, uint16_t *avail_idx,
|
||||
uint32_t *len);
|
||||
|
||||
int virtqueue_add_consumed_buffer(struct virtqueue *vq, uint16_t head_idx,
|
||||
uint32_t len);
|
||||
|
||||
void virtqueue_disable_cb(struct virtqueue *vq);
|
||||
|
||||
int virtqueue_enable_cb(struct virtqueue *vq);
|
||||
|
||||
void virtqueue_kick(struct virtqueue *vq);
|
||||
|
||||
static inline struct virtqueue *virtqueue_allocate(unsigned int num_desc_extra)
|
||||
{
|
||||
struct virtqueue *vqs;
|
||||
uint32_t vq_size = sizeof(struct virtqueue) +
|
||||
num_desc_extra * sizeof(struct vq_desc_extra);
|
||||
|
||||
vqs = (struct virtqueue *)metal_allocate_memory(vq_size);
|
||||
if (vqs) {
|
||||
memset(vqs, 0x00, vq_size);
|
||||
}
|
||||
|
||||
return vqs;
|
||||
}
|
||||
|
||||
void virtqueue_free(struct virtqueue *vq);
|
||||
|
||||
void virtqueue_dump(struct virtqueue *vq);
|
||||
|
||||
void virtqueue_notification(struct virtqueue *vq);
|
||||
|
||||
uint32_t virtqueue_get_desc_size(struct virtqueue *vq);
|
||||
|
||||
uint32_t virtqueue_get_buffer_length(struct virtqueue *vq, uint16_t idx);
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VIRTQUEUE_H_ */
|
||||
@@ -0,0 +1 @@
|
||||
collect (PROJECT_LIB_SOURCES rpmsg_retarget.c)
|
||||
@@ -0,0 +1,348 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Mentor Graphics Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <metal/mutex.h>
|
||||
#include <metal/spinlock.h>
|
||||
#include <metal/utilities.h>
|
||||
#include <openamp/open_amp.h>
|
||||
#include <openamp/rpmsg_retarget.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
/*************************************************************************
|
||||
* Description
|
||||
* This files contains rpmsg based redefinitions for C RTL system calls
|
||||
* such as _open, _read, _write, _close.
|
||||
*************************************************************************/
|
||||
static struct rpmsg_rpc_data *rpmsg_default_rpc;
|
||||
|
||||
static int rpmsg_rpc_ept_cb(struct rpmsg_endpoint *ept, void *data, size_t len,
|
||||
uint32_t src, void *priv)
|
||||
{
|
||||
struct rpmsg_rpc_syscall *syscall;
|
||||
|
||||
(void)priv;
|
||||
(void)src;
|
||||
|
||||
if (data && ept) {
|
||||
syscall = data;
|
||||
if (syscall->id == TERM_SYSCALL_ID) {
|
||||
rpmsg_destroy_ept(ept);
|
||||
} else {
|
||||
struct rpmsg_rpc_data *rpc;
|
||||
|
||||
rpc = metal_container_of(ept,
|
||||
struct rpmsg_rpc_data,
|
||||
ept);
|
||||
metal_spinlock_acquire(&rpc->buflock);
|
||||
if (rpc->respbuf && rpc->respbuf_len != 0) {
|
||||
if (len > rpc->respbuf_len)
|
||||
len = rpc->respbuf_len;
|
||||
memcpy(rpc->respbuf, data, len);
|
||||
}
|
||||
atomic_flag_clear(&rpc->nacked);
|
||||
metal_spinlock_release(&rpc->buflock);
|
||||
}
|
||||
}
|
||||
|
||||
return RPMSG_SUCCESS;
|
||||
}
|
||||
|
||||
static void rpmsg_service_unbind(struct rpmsg_endpoint *ept)
|
||||
{
|
||||
struct rpmsg_rpc_data *rpc;
|
||||
|
||||
rpc = metal_container_of(ept, struct rpmsg_rpc_data, ept);
|
||||
rpc->ept_destroyed = 1;
|
||||
rpmsg_destroy_ept(ept);
|
||||
atomic_flag_clear(&rpc->nacked);
|
||||
if (rpc->shutdown_cb)
|
||||
rpc->shutdown_cb(rpc);
|
||||
}
|
||||
|
||||
int rpmsg_rpc_init(struct rpmsg_rpc_data *rpc,
|
||||
struct rpmsg_device *rdev,
|
||||
const char *ept_name, uint32_t ept_addr,
|
||||
uint32_t ept_raddr,
|
||||
void *poll_arg, rpmsg_rpc_poll poll,
|
||||
rpmsg_rpc_shutdown_cb shutdown_cb)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!rpc || !rdev)
|
||||
return -EINVAL;
|
||||
metal_spinlock_init(&rpc->buflock);
|
||||
metal_mutex_init(&rpc->lock);
|
||||
rpc->shutdown_cb = shutdown_cb;
|
||||
rpc->poll_arg = poll_arg;
|
||||
rpc->poll = poll;
|
||||
rpc->ept_destroyed = 0;
|
||||
rpc->respbuf = NULL;
|
||||
rpc->respbuf_len = 0;
|
||||
atomic_init(&rpc->nacked, 1);
|
||||
ret = rpmsg_create_ept(&rpc->ept, rdev,
|
||||
ept_name, ept_addr, ept_raddr,
|
||||
rpmsg_rpc_ept_cb, rpmsg_service_unbind);
|
||||
if (ret != 0) {
|
||||
metal_mutex_release(&rpc->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
while (!is_rpmsg_ept_ready(&rpc->ept)) {
|
||||
if (rpc->poll)
|
||||
rpc->poll(rpc->poll_arg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rpmsg_rpc_release(struct rpmsg_rpc_data *rpc)
|
||||
{
|
||||
if (!rpc)
|
||||
return;
|
||||
if (rpc->ept_destroyed == 0)
|
||||
rpmsg_destroy_ept(&rpc->ept);
|
||||
metal_mutex_acquire(&rpc->lock);
|
||||
metal_spinlock_acquire(&rpc->buflock);
|
||||
rpc->respbuf = NULL;
|
||||
rpc->respbuf_len = 0;
|
||||
metal_spinlock_release(&rpc->buflock);
|
||||
metal_mutex_release(&rpc->lock);
|
||||
metal_mutex_deinit(&rpc->lock);
|
||||
}
|
||||
|
||||
int rpmsg_rpc_send(struct rpmsg_rpc_data *rpc,
|
||||
void *req, size_t len,
|
||||
void *resp, size_t resp_len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!rpc)
|
||||
return -EINVAL;
|
||||
metal_spinlock_acquire(&rpc->buflock);
|
||||
rpc->respbuf = resp;
|
||||
rpc->respbuf_len = resp_len;
|
||||
metal_spinlock_release(&rpc->buflock);
|
||||
(void)atomic_flag_test_and_set(&rpc->nacked);
|
||||
ret = rpmsg_send(&rpc->ept, req, len);
|
||||
if (ret < 0)
|
||||
return -EINVAL;
|
||||
if (!resp)
|
||||
return ret;
|
||||
while ((atomic_flag_test_and_set(&rpc->nacked))) {
|
||||
if (rpc->poll)
|
||||
rpc->poll(rpc->poll_arg);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void rpmsg_set_default_rpc(struct rpmsg_rpc_data *rpc)
|
||||
{
|
||||
if (!rpc)
|
||||
return;
|
||||
rpmsg_default_rpc = rpc;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* FUNCTION
|
||||
*
|
||||
* _open
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* Open a file. Minimal implementation
|
||||
*
|
||||
*************************************************************************/
|
||||
#define MAX_BUF_LEN 496UL
|
||||
|
||||
int _open(const char *filename, int flags, int mode)
|
||||
{
|
||||
struct rpmsg_rpc_data *rpc = rpmsg_default_rpc;
|
||||
struct rpmsg_rpc_syscall *syscall;
|
||||
struct rpmsg_rpc_syscall resp;
|
||||
int filename_len = strlen(filename) + 1;
|
||||
unsigned int payload_size = sizeof(*syscall) + filename_len;
|
||||
unsigned char tmpbuf[MAX_BUF_LEN];
|
||||
int ret;
|
||||
|
||||
if (!filename || payload_size > (int)MAX_BUF_LEN) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!rpc)
|
||||
return -EINVAL;
|
||||
|
||||
/* Construct rpc payload */
|
||||
syscall = (struct rpmsg_rpc_syscall *)tmpbuf;
|
||||
syscall->id = OPEN_SYSCALL_ID;
|
||||
syscall->args.int_field1 = flags;
|
||||
syscall->args.int_field2 = mode;
|
||||
syscall->args.data_len = filename_len;
|
||||
memcpy(tmpbuf + sizeof(*syscall), filename, filename_len);
|
||||
|
||||
resp.id = 0;
|
||||
ret = rpmsg_rpc_send(rpc, tmpbuf, payload_size,
|
||||
(void *)&resp, sizeof(resp));
|
||||
if (ret >= 0) {
|
||||
/* Obtain return args and return to caller */
|
||||
if (resp.id == OPEN_SYSCALL_ID)
|
||||
ret = resp.args.int_field1;
|
||||
else
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* FUNCTION
|
||||
*
|
||||
* _read
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* Low level function to redirect IO to serial.
|
||||
*
|
||||
*************************************************************************/
|
||||
int _read(int fd, char *buffer, int buflen)
|
||||
{
|
||||
struct rpmsg_rpc_syscall syscall;
|
||||
struct rpmsg_rpc_syscall *resp;
|
||||
struct rpmsg_rpc_data *rpc = rpmsg_default_rpc;
|
||||
int payload_size = sizeof(syscall);
|
||||
unsigned char tmpbuf[MAX_BUF_LEN];
|
||||
int ret;
|
||||
|
||||
if (!rpc || !buffer || buflen == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Construct rpc payload */
|
||||
syscall.id = READ_SYSCALL_ID;
|
||||
syscall.args.int_field1 = fd;
|
||||
syscall.args.int_field2 = buflen;
|
||||
syscall.args.data_len = 0; /*not used */
|
||||
|
||||
resp = (struct rpmsg_rpc_syscall *)tmpbuf;
|
||||
resp->id = 0;
|
||||
ret = rpmsg_rpc_send(rpc, (void *)&syscall, payload_size,
|
||||
tmpbuf, sizeof(tmpbuf));
|
||||
|
||||
/* Obtain return args and return to caller */
|
||||
if (ret >= 0) {
|
||||
if (resp->id == READ_SYSCALL_ID) {
|
||||
if (resp->args.int_field1 > 0) {
|
||||
int tmplen = resp->args.data_len;
|
||||
unsigned char *tmpptr = tmpbuf;
|
||||
|
||||
tmpptr += sizeof(*resp);
|
||||
if (tmplen > buflen)
|
||||
tmplen = buflen;
|
||||
memcpy(buffer, tmpptr, tmplen);
|
||||
}
|
||||
ret = resp->args.int_field1;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* FUNCTION
|
||||
*
|
||||
* _write
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* Low level function to redirect IO to serial.
|
||||
*
|
||||
*************************************************************************/
|
||||
int _write(int fd, const char *ptr, int len)
|
||||
{
|
||||
int ret;
|
||||
struct rpmsg_rpc_syscall *syscall;
|
||||
struct rpmsg_rpc_syscall resp;
|
||||
int payload_size = sizeof(*syscall) + len;
|
||||
struct rpmsg_rpc_data *rpc = rpmsg_default_rpc;
|
||||
unsigned char tmpbuf[MAX_BUF_LEN];
|
||||
unsigned char *tmpptr;
|
||||
int null_term = 0;
|
||||
|
||||
if (!rpc)
|
||||
return -EINVAL;
|
||||
if (fd == 1)
|
||||
null_term = 1;
|
||||
|
||||
syscall = (struct rpmsg_rpc_syscall *)tmpbuf;
|
||||
syscall->id = WRITE_SYSCALL_ID;
|
||||
syscall->args.int_field1 = fd;
|
||||
syscall->args.int_field2 = len;
|
||||
syscall->args.data_len = len + null_term;
|
||||
tmpptr = tmpbuf + sizeof(*syscall);
|
||||
memcpy(tmpptr, ptr, len);
|
||||
if (null_term == 1) {
|
||||
*(char *)(tmpptr + len + null_term) = 0;
|
||||
payload_size += 1;
|
||||
}
|
||||
resp.id = 0;
|
||||
ret = rpmsg_rpc_send(rpc, tmpbuf, payload_size,
|
||||
(void *)&resp, sizeof(resp));
|
||||
|
||||
if (ret >= 0) {
|
||||
if (resp.id == WRITE_SYSCALL_ID)
|
||||
ret = resp.args.int_field1;
|
||||
else
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* FUNCTION
|
||||
*
|
||||
* _close
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* Close a file. Minimal implementation
|
||||
*
|
||||
*************************************************************************/
|
||||
int _close(int fd)
|
||||
{
|
||||
int ret;
|
||||
struct rpmsg_rpc_syscall syscall;
|
||||
struct rpmsg_rpc_syscall resp;
|
||||
int payload_size = sizeof(syscall);
|
||||
struct rpmsg_rpc_data *rpc = rpmsg_default_rpc;
|
||||
|
||||
if (!rpc)
|
||||
return -EINVAL;
|
||||
syscall.id = CLOSE_SYSCALL_ID;
|
||||
syscall.args.int_field1 = fd;
|
||||
syscall.args.int_field2 = 0; /*not used */
|
||||
syscall.args.data_len = 0; /*not used */
|
||||
|
||||
resp.id = 0;
|
||||
ret = rpmsg_rpc_send(rpc, (void *)&syscall, payload_size,
|
||||
(void *)&resp, sizeof(resp));
|
||||
|
||||
if (ret >= 0) {
|
||||
if (resp.id == CLOSE_SYSCALL_ID)
|
||||
ret = resp.args.int_field1;
|
||||
else
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* Remoteproc Virtio Framework Implementation
|
||||
*
|
||||
* Copyright(c) 2018 Xilinx Ltd.
|
||||
* Copyright(c) 2011 Texas Instruments, Inc.
|
||||
* Copyright(c) 2011 Google, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <openamp/remoteproc.h>
|
||||
#include <openamp/remoteproc_virtio.h>
|
||||
#include <openamp/virtqueue.h>
|
||||
#include <metal/cpu.h>
|
||||
#include <metal/utilities.h>
|
||||
#include <metal/alloc.h>
|
||||
|
||||
static void rproc_virtio_virtqueue_notify(struct virtqueue *vq)
|
||||
{
|
||||
struct remoteproc_virtio *rpvdev;
|
||||
struct virtio_vring_info *vring_info;
|
||||
struct virtio_device *vdev;
|
||||
unsigned int vq_id = vq->vq_queue_index;
|
||||
|
||||
vdev = vq->vq_dev;
|
||||
rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
|
||||
metal_assert(vq_id < vdev->vrings_num);
|
||||
vring_info = &vdev->vrings_info[vq_id];
|
||||
rpvdev->notify(rpvdev->priv, vring_info->notifyid);
|
||||
}
|
||||
|
||||
static unsigned char rproc_virtio_get_status(struct virtio_device *vdev)
|
||||
{
|
||||
struct remoteproc_virtio *rpvdev;
|
||||
struct fw_rsc_vdev *vdev_rsc;
|
||||
struct metal_io_region *io;
|
||||
char status;
|
||||
|
||||
rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
|
||||
vdev_rsc = rpvdev->vdev_rsc;
|
||||
io = rpvdev->vdev_rsc_io;
|
||||
status = metal_io_read8(io,
|
||||
metal_io_virt_to_offset(io, &vdev_rsc->status));
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
static void rproc_virtio_set_status(struct virtio_device *vdev,
|
||||
unsigned char status)
|
||||
{
|
||||
struct remoteproc_virtio *rpvdev;
|
||||
struct fw_rsc_vdev *vdev_rsc;
|
||||
struct metal_io_region *io;
|
||||
|
||||
rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
|
||||
vdev_rsc = rpvdev->vdev_rsc;
|
||||
io = rpvdev->vdev_rsc_io;
|
||||
metal_io_write8(io,
|
||||
metal_io_virt_to_offset(io, &vdev_rsc->status),
|
||||
status);
|
||||
rpvdev->notify(rpvdev->priv, vdev->notifyid);
|
||||
}
|
||||
#endif
|
||||
|
||||
static uint32_t rproc_virtio_get_dfeatures(struct virtio_device *vdev)
|
||||
{
|
||||
struct remoteproc_virtio *rpvdev;
|
||||
struct fw_rsc_vdev *vdev_rsc;
|
||||
struct metal_io_region *io;
|
||||
uint32_t features;
|
||||
|
||||
rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
|
||||
vdev_rsc = rpvdev->vdev_rsc;
|
||||
io = rpvdev->vdev_rsc_io;
|
||||
features = metal_io_read32(io,
|
||||
metal_io_virt_to_offset(io, &vdev_rsc->dfeatures));
|
||||
|
||||
return features;
|
||||
}
|
||||
|
||||
static uint32_t rproc_virtio_get_features(struct virtio_device *vdev)
|
||||
{
|
||||
struct remoteproc_virtio *rpvdev;
|
||||
struct fw_rsc_vdev *vdev_rsc;
|
||||
struct metal_io_region *io;
|
||||
uint32_t gfeatures;
|
||||
uint32_t dfeatures;
|
||||
|
||||
rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
|
||||
vdev_rsc = rpvdev->vdev_rsc;
|
||||
io = rpvdev->vdev_rsc_io;
|
||||
gfeatures = metal_io_read32(io,
|
||||
metal_io_virt_to_offset(io, &vdev_rsc->gfeatures));
|
||||
dfeatures = rproc_virtio_get_dfeatures(vdev);
|
||||
|
||||
return dfeatures & gfeatures;
|
||||
}
|
||||
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
static void rproc_virtio_set_features(struct virtio_device *vdev,
|
||||
uint32_t features)
|
||||
{
|
||||
struct remoteproc_virtio *rpvdev;
|
||||
struct fw_rsc_vdev *vdev_rsc;
|
||||
struct metal_io_region *io;
|
||||
|
||||
rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
|
||||
vdev_rsc = rpvdev->vdev_rsc;
|
||||
io = rpvdev->vdev_rsc_io;
|
||||
metal_io_write32(io,
|
||||
metal_io_virt_to_offset(io, &vdev_rsc->gfeatures),
|
||||
features);
|
||||
rpvdev->notify(rpvdev->priv, vdev->notifyid);
|
||||
}
|
||||
|
||||
static uint32_t rproc_virtio_negotiate_features(struct virtio_device *vdev,
|
||||
uint32_t features)
|
||||
{
|
||||
uint32_t dfeatures = rproc_virtio_get_dfeatures(vdev);
|
||||
|
||||
rproc_virtio_set_features(vdev, dfeatures & features);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void rproc_virtio_read_config(struct virtio_device *vdev,
|
||||
uint32_t offset, void *dst, int length)
|
||||
{
|
||||
struct remoteproc_virtio *rpvdev;
|
||||
struct fw_rsc_vdev *vdev_rsc;
|
||||
struct metal_io_region *io;
|
||||
char *config;
|
||||
|
||||
rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
|
||||
vdev_rsc = rpvdev->vdev_rsc;
|
||||
config = (char *)(&vdev_rsc->vring[vdev->vrings_num]);
|
||||
io = rpvdev->vdev_rsc_io;
|
||||
|
||||
if (offset + length <= vdev_rsc->config_len)
|
||||
metal_io_block_read(io,
|
||||
metal_io_virt_to_offset(io, config + offset),
|
||||
dst, length);
|
||||
}
|
||||
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
static void rproc_virtio_write_config(struct virtio_device *vdev,
|
||||
uint32_t offset, void *src, int length)
|
||||
{
|
||||
struct remoteproc_virtio *rpvdev;
|
||||
struct fw_rsc_vdev *vdev_rsc;
|
||||
struct metal_io_region *io;
|
||||
char *config;
|
||||
|
||||
rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
|
||||
vdev_rsc = rpvdev->vdev_rsc;
|
||||
config = (char *)(&vdev_rsc->vring[vdev->vrings_num]);
|
||||
io = rpvdev->vdev_rsc_io;
|
||||
|
||||
if (offset + length <= vdev_rsc->config_len) {
|
||||
metal_io_block_write(io,
|
||||
metal_io_virt_to_offset(io, config + offset),
|
||||
src, length);
|
||||
rpvdev->notify(rpvdev->priv, vdev->notifyid);
|
||||
}
|
||||
}
|
||||
|
||||
static void rproc_virtio_reset_device(struct virtio_device *vdev)
|
||||
{
|
||||
if (vdev->role == VIRTIO_DEV_MASTER)
|
||||
rproc_virtio_set_status(vdev,
|
||||
VIRTIO_CONFIG_STATUS_NEEDS_RESET);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct virtio_dispatch remoteproc_virtio_dispatch_funcs = {
|
||||
.get_status = rproc_virtio_get_status,
|
||||
.get_features = rproc_virtio_get_features,
|
||||
.read_config = rproc_virtio_read_config,
|
||||
.notify = rproc_virtio_virtqueue_notify,
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
/*
|
||||
* We suppose here that the vdev is in a shared memory so that can
|
||||
* be access only by one core: the master. In this case salve core has
|
||||
* only read access right.
|
||||
*/
|
||||
.set_status = rproc_virtio_set_status,
|
||||
.set_features = rproc_virtio_set_features,
|
||||
.negotiate_features = rproc_virtio_negotiate_features,
|
||||
.write_config = rproc_virtio_write_config,
|
||||
.reset_device = rproc_virtio_reset_device,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct virtio_device *
|
||||
rproc_virtio_create_vdev(unsigned int role, unsigned int notifyid,
|
||||
void *rsc, struct metal_io_region *rsc_io,
|
||||
void *priv,
|
||||
rpvdev_notify_func notify,
|
||||
virtio_dev_reset_cb rst_cb)
|
||||
{
|
||||
struct remoteproc_virtio *rpvdev;
|
||||
struct virtio_vring_info *vrings_info;
|
||||
struct fw_rsc_vdev *vdev_rsc = rsc;
|
||||
struct virtio_device *vdev;
|
||||
unsigned int num_vrings = vdev_rsc->num_of_vrings;
|
||||
unsigned int i;
|
||||
|
||||
rpvdev = metal_allocate_memory(sizeof(*rpvdev));
|
||||
if (!rpvdev)
|
||||
return NULL;
|
||||
vrings_info = metal_allocate_memory(sizeof(*vrings_info) * num_vrings);
|
||||
if (!vrings_info)
|
||||
goto err0;
|
||||
memset(rpvdev, 0, sizeof(*rpvdev));
|
||||
memset(vrings_info, 0, sizeof(*vrings_info));
|
||||
vdev = &rpvdev->vdev;
|
||||
|
||||
for (i = 0; i < num_vrings; i++) {
|
||||
struct virtqueue *vq;
|
||||
struct fw_rsc_vdev_vring *vring_rsc;
|
||||
unsigned int num_extra_desc = 0;
|
||||
|
||||
vring_rsc = &vdev_rsc->vring[i];
|
||||
if (role == VIRTIO_DEV_MASTER) {
|
||||
num_extra_desc = vring_rsc->num;
|
||||
}
|
||||
vq = virtqueue_allocate(num_extra_desc);
|
||||
if (!vq)
|
||||
goto err1;
|
||||
vrings_info[i].vq = vq;
|
||||
}
|
||||
|
||||
rpvdev->notify = notify;
|
||||
rpvdev->priv = priv;
|
||||
vdev->vrings_info = vrings_info;
|
||||
/* Assuming the shared memory has been mapped and registered if
|
||||
* necessary
|
||||
*/
|
||||
rpvdev->vdev_rsc = vdev_rsc;
|
||||
rpvdev->vdev_rsc_io = rsc_io;
|
||||
|
||||
vdev->notifyid = notifyid;
|
||||
vdev->role = role;
|
||||
vdev->reset_cb = rst_cb;
|
||||
vdev->vrings_num = num_vrings;
|
||||
vdev->func = &remoteproc_virtio_dispatch_funcs;
|
||||
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
if (role == VIRTIO_DEV_MASTER) {
|
||||
uint32_t dfeatures = rproc_virtio_get_dfeatures(vdev);
|
||||
/* Assume the master support all slave features */
|
||||
rproc_virtio_negotiate_features(vdev, dfeatures);
|
||||
}
|
||||
#endif
|
||||
|
||||
return &rpvdev->vdev;
|
||||
|
||||
err1:
|
||||
for (i = 0; i < num_vrings; i++) {
|
||||
if (vrings_info[i].vq)
|
||||
metal_free_memory(vrings_info[i].vq);
|
||||
}
|
||||
metal_free_memory(vrings_info);
|
||||
err0:
|
||||
metal_free_memory(rpvdev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void rproc_virtio_remove_vdev(struct virtio_device *vdev)
|
||||
{
|
||||
struct remoteproc_virtio *rpvdev;
|
||||
unsigned int i;
|
||||
|
||||
if (!vdev)
|
||||
return;
|
||||
rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
|
||||
for (i = 0; i < vdev->vrings_num; i++) {
|
||||
struct virtqueue *vq;
|
||||
|
||||
vq = vdev->vrings_info[i].vq;
|
||||
if (vq)
|
||||
metal_free_memory(vq);
|
||||
}
|
||||
metal_free_memory(vdev->vrings_info);
|
||||
metal_free_memory(rpvdev);
|
||||
}
|
||||
|
||||
int rproc_virtio_init_vring(struct virtio_device *vdev, unsigned int index,
|
||||
unsigned int notifyid, void *va,
|
||||
struct metal_io_region *io,
|
||||
unsigned int num_descs, unsigned int align)
|
||||
{
|
||||
struct virtio_vring_info *vring_info;
|
||||
unsigned int num_vrings;
|
||||
|
||||
num_vrings = vdev->vrings_num;
|
||||
if (index >= num_vrings)
|
||||
return -RPROC_EINVAL;
|
||||
vring_info = &vdev->vrings_info[index];
|
||||
vring_info->io = io;
|
||||
vring_info->notifyid = notifyid;
|
||||
vring_info->info.vaddr = va;
|
||||
vring_info->info.num_descs = num_descs;
|
||||
vring_info->info.align = align;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rproc_virtio_notified(struct virtio_device *vdev, uint32_t notifyid)
|
||||
{
|
||||
unsigned int num_vrings, i;
|
||||
struct virtio_vring_info *vring_info;
|
||||
struct virtqueue *vq;
|
||||
|
||||
if (!vdev)
|
||||
return -RPROC_EINVAL;
|
||||
/* We do nothing for vdev notification in this implementation */
|
||||
if (vdev->notifyid == notifyid)
|
||||
return 0;
|
||||
num_vrings = vdev->vrings_num;
|
||||
for (i = 0; i < num_vrings; i++) {
|
||||
vring_info = &vdev->vrings_info[i];
|
||||
if (vring_info->notifyid == notifyid ||
|
||||
notifyid == RSC_NOTIFY_ID_ANY) {
|
||||
vq = vring_info->vq;
|
||||
virtqueue_notification(vq);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rproc_virtio_wait_remote_ready(struct virtio_device *vdev)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/*
|
||||
* No status available for slave. As Master has not to wait
|
||||
* slave action, we can return. Behavior should be updated
|
||||
* in future if a slave status is added.
|
||||
*/
|
||||
if (vdev->role == VIRTIO_DEV_MASTER)
|
||||
return;
|
||||
|
||||
while (1) {
|
||||
status = rproc_virtio_get_status(vdev);
|
||||
if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK)
|
||||
return;
|
||||
metal_cpu_yield();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
collect (PROJECT_LIB_SOURCES rpmsg.c)
|
||||
collect (PROJECT_LIB_SOURCES rpmsg_virtio.c)
|
||||
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* 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 = RPMSG_RESERVED_ADDRESSES + 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)
|
||||
{
|
||||
addr -= RPMSG_RESERVED_ADDRESSES;
|
||||
if (addr >= 0 && 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)
|
||||
{
|
||||
addr -= RPMSG_RESERVED_ADDRESSES;
|
||||
if (addr >= 0 && 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)
|
||||
{
|
||||
addr -= RPMSG_RESERVED_ADDRESSES;
|
||||
if (addr >= 0 && 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 len - 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 len,
|
||||
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,
|
||||
len, 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;
|
||||
}
|
||||
|
||||
void rpmsg_hold_rx_buffer(struct rpmsg_endpoint *ept, void *rxbuf)
|
||||
{
|
||||
struct rpmsg_device *rdev;
|
||||
|
||||
if (!ept || !ept->rdev || !rxbuf)
|
||||
return;
|
||||
|
||||
rdev = ept->rdev;
|
||||
|
||||
if (rdev->ops.hold_rx_buffer)
|
||||
rdev->ops.hold_rx_buffer(rdev, rxbuf);
|
||||
}
|
||||
|
||||
void rpmsg_release_rx_buffer(struct rpmsg_endpoint *ept, void *rxbuf)
|
||||
{
|
||||
struct rpmsg_device *rdev;
|
||||
|
||||
if (!ept || !ept->rdev || !rxbuf)
|
||||
return;
|
||||
|
||||
rdev = ept->rdev;
|
||||
|
||||
if (rdev->ops.release_rx_buffer)
|
||||
rdev->ops.release_rx_buffer(rdev, rxbuf);
|
||||
}
|
||||
|
||||
void *rpmsg_get_tx_payload_buffer(struct rpmsg_endpoint *ept,
|
||||
uint32_t *len, int wait)
|
||||
{
|
||||
struct rpmsg_device *rdev;
|
||||
|
||||
if (!ept || !ept->rdev || !len)
|
||||
return NULL;
|
||||
|
||||
rdev = ept->rdev;
|
||||
|
||||
if (rdev->ops.get_tx_payload_buffer)
|
||||
return rdev->ops.get_tx_payload_buffer(rdev, len, wait);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int rpmsg_send_offchannel_nocopy(struct rpmsg_endpoint *ept, uint32_t src,
|
||||
uint32_t dst, const void *data, int len)
|
||||
{
|
||||
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_nocopy)
|
||||
return rdev->ops.send_offchannel_nocopy(rdev, src, dst,
|
||||
data, len);
|
||||
|
||||
return RPMSG_ERR_PARAM;
|
||||
}
|
||||
|
||||
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 = ept->rdev;
|
||||
|
||||
metal_mutex_acquire(&rdev->lock);
|
||||
if (ept->addr != RPMSG_ADDR_ANY)
|
||||
rpmsg_release_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE,
|
||||
ept->addr);
|
||||
metal_list_del(&ept->node);
|
||||
ept->rdev = NULL;
|
||||
metal_mutex_release(&rdev->lock);
|
||||
}
|
||||
|
||||
void rpmsg_register_endpoint(struct rpmsg_device *rdev,
|
||||
struct rpmsg_endpoint *ept)
|
||||
{
|
||||
ept->rdev = rdev;
|
||||
metal_list_add_tail(&rdev->endpoints, &ept->node);
|
||||
}
|
||||
|
||||
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 = RPMSG_SUCCESS;
|
||||
uint32_t addr = src;
|
||||
|
||||
if (!ept)
|
||||
return RPMSG_ERR_PARAM;
|
||||
|
||||
metal_mutex_acquire(&rdev->lock);
|
||||
if (src == RPMSG_ADDR_ANY) {
|
||||
addr = rpmsg_get_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE);
|
||||
if (addr == RPMSG_ADDR_ANY) {
|
||||
status = RPMSG_ERR_ADDR;
|
||||
goto ret_status;
|
||||
}
|
||||
} else if (src >= RPMSG_RESERVED_ADDRESSES) {
|
||||
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_ERR_ADDR;
|
||||
goto ret_status;
|
||||
} else {
|
||||
goto ret_status;
|
||||
}
|
||||
} else {
|
||||
/* Skip check the address duplication in 0-1023:
|
||||
* 1.Trust the author of predefined service
|
||||
* 2.Simplify the tracking implementation
|
||||
*/
|
||||
}
|
||||
|
||||
rpmsg_initialize_ept(ept, name, addr, dest, cb, unbind_cb);
|
||||
rpmsg_register_endpoint(rdev, ept);
|
||||
metal_mutex_release(&rdev->lock);
|
||||
|
||||
/* Send NS announcement to remote processor */
|
||||
if (ept->name[0] && rdev->support_ns &&
|
||||
ept->dest_addr == RPMSG_ADDR_ANY)
|
||||
status = rpmsg_send_ns_message(ept, RPMSG_NS_CREATE);
|
||||
|
||||
if (status)
|
||||
rpmsg_unregister_endpoint(ept);
|
||||
return status;
|
||||
|
||||
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 (!rdev)
|
||||
return;
|
||||
|
||||
if (ept->name[0] && rdev->support_ns &&
|
||||
ept->addr >= RPMSG_RESERVED_ADDRESSES)
|
||||
(void)rpmsg_send_ns_message(ept, RPMSG_NS_DESTROY);
|
||||
rpmsg_unregister_endpoint(ept);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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
|
||||
#include <metal/log.h>
|
||||
|
||||
#define RPMSG_ASSERT(_exp, _msg) do { \
|
||||
if (!(_exp)) { \
|
||||
metal_log(METAL_LOG_EMERGENCY, \
|
||||
"FATAL: %s - "_msg, __func__); \
|
||||
metal_assert(_exp); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define RPMSG_ASSERT(_exp, _msg) metal_assert(_exp)
|
||||
#endif
|
||||
|
||||
#define RPMSG_BUF_HELD (1U << 31) /* Flag to suggest to hold the buffer */
|
||||
|
||||
#define RPMSG_LOCATE_HDR(p) \
|
||||
((struct rpmsg_hdr *)((unsigned char *)(p) - sizeof(struct rpmsg_hdr)))
|
||||
#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.
|
||||
*/
|
||||
METAL_PACKED_BEGIN
|
||||
struct rpmsg_hdr {
|
||||
uint32_t src;
|
||||
uint32_t dst;
|
||||
uint32_t reserved;
|
||||
uint16_t len;
|
||||
uint16_t flags;
|
||||
} METAL_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).
|
||||
*/
|
||||
METAL_PACKED_BEGIN
|
||||
struct rpmsg_ns_msg {
|
||||
char name[RPMSG_NAME_SIZE];
|
||||
uint32_t addr;
|
||||
uint32_t flags;
|
||||
} METAL_PACKED_END;
|
||||
|
||||
/**
|
||||
* rpmsg_initialize_ept - initialize rpmsg endpoint
|
||||
*
|
||||
* Initialize an RPMsg endpoint with a name, source address,
|
||||
* remoteproc address, endpoint callback, and destroy endpoint callback.
|
||||
*
|
||||
* @ept: pointer to rpmsg endpoint
|
||||
* @name: service name associated to the endpoint
|
||||
* @src: local address of the endpoint
|
||||
* @dest: target address of the endpoint
|
||||
* @cb: endpoint callback
|
||||
* @ns_unbind_cb: end point service unbind callback, called when remote ept is
|
||||
* destroyed.
|
||||
*/
|
||||
static inline void rpmsg_initialize_ept(struct rpmsg_endpoint *ept,
|
||||
const char *name,
|
||||
uint32_t src, uint32_t dest,
|
||||
rpmsg_ept_cb cb,
|
||||
rpmsg_ns_unbind_cb ns_unbind_cb)
|
||||
{
|
||||
strncpy(ept->name, name ? name : "", sizeof(ept->name));
|
||||
ept->addr = src;
|
||||
ept->dest_addr = dest;
|
||||
ept->cb = cb;
|
||||
ept->ns_unbind_cb = ns_unbind_cb;
|
||||
}
|
||||
|
||||
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);
|
||||
void 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_ */
|
||||
@@ -0,0 +1,768 @@
|
||||
/*
|
||||
* 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 - 1usec tick. */
|
||||
#define RPMSG_TICK_COUNT 15000000
|
||||
|
||||
/* Time to wait - In multiple of 1 msecs. */
|
||||
#define RPMSG_TICKS_PER_INTERVAL 1000
|
||||
|
||||
#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 = (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 = size;
|
||||
shpool->avail = 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, uint32_t len,
|
||||
uint16_t 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, uint32_t len,
|
||||
uint16_t idx)
|
||||
{
|
||||
unsigned int role = rpmsg_virtio_get_role(rvdev);
|
||||
|
||||
#ifdef VIRTIO_CACHED_BUFFERS
|
||||
metal_cache_flush(buffer, len);
|
||||
#endif /* VIRTIO_CACHED_BUFFERS */
|
||||
|
||||
#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, 1, 0, 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,
|
||||
uint32_t *len, uint16_t *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, len, idx);
|
||||
if (!data && rvdev->svq->vq_free_cnt) {
|
||||
data = rpmsg_virtio_shm_pool_get_buffer(rvdev->shpool,
|
||||
RPMSG_BUFFER_SIZE);
|
||||
*len = RPMSG_BUFFER_SIZE;
|
||||
*idx = 0;
|
||||
}
|
||||
}
|
||||
#endif /*!VIRTIO_SLAVE_ONLY*/
|
||||
|
||||
#ifndef VIRTIO_MASTER_ONLY
|
||||
if (role == RPMSG_REMOTE) {
|
||||
data = virtqueue_get_available_buffer(rvdev->svq, idx, 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,
|
||||
uint32_t *len, uint16_t *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, len, idx);
|
||||
}
|
||||
#endif /*!VIRTIO_SLAVE_ONLY*/
|
||||
|
||||
#ifndef VIRTIO_MASTER_ONLY
|
||||
if (role == RPMSG_REMOTE) {
|
||||
data =
|
||||
virtqueue_get_available_buffer(rvdev->rvq, idx, len);
|
||||
}
|
||||
#endif /*!VIRTIO_MASTER_ONLY*/
|
||||
|
||||
#ifdef VIRTIO_CACHED_BUFFERS
|
||||
/* Invalidate the buffer before returning it */
|
||||
metal_cache_invalidate(data, *len);
|
||||
#endif /* VIRTIO_CACHED_BUFFERS */
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
#endif /*!VIRTIO_MASTER_ONLY*/
|
||||
|
||||
/**
|
||||
* _rpmsg_virtio_get_buffer_size
|
||||
*
|
||||
* Returns buffer size available for sending messages.
|
||||
*
|
||||
* @param rvdev - pointer to rpmsg device
|
||||
*
|
||||
* @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 Master then buffers are provided by us,
|
||||
* 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);
|
||||
if (length < 0) {
|
||||
length = 0;
|
||||
}
|
||||
}
|
||||
#endif /*!VIRTIO_MASTER_ONLY*/
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
static void rpmsg_virtio_hold_rx_buffer(struct rpmsg_device *rdev, void *rxbuf)
|
||||
{
|
||||
struct rpmsg_hdr *rp_hdr;
|
||||
|
||||
(void)rdev;
|
||||
|
||||
rp_hdr = RPMSG_LOCATE_HDR(rxbuf);
|
||||
|
||||
/* Set held status to keep buffer */
|
||||
rp_hdr->reserved |= RPMSG_BUF_HELD;
|
||||
}
|
||||
|
||||
static void rpmsg_virtio_release_rx_buffer(struct rpmsg_device *rdev,
|
||||
void *rxbuf)
|
||||
{
|
||||
struct rpmsg_virtio_device *rvdev;
|
||||
struct rpmsg_hdr *rp_hdr;
|
||||
uint16_t idx;
|
||||
uint32_t len;
|
||||
|
||||
rvdev = metal_container_of(rdev, struct rpmsg_virtio_device, rdev);
|
||||
rp_hdr = RPMSG_LOCATE_HDR(rxbuf);
|
||||
/* The reserved field contains buffer index */
|
||||
idx = (uint16_t)(rp_hdr->reserved & ~RPMSG_BUF_HELD);
|
||||
|
||||
metal_mutex_acquire(&rdev->lock);
|
||||
/* Return buffer on virtqueue. */
|
||||
len = virtqueue_get_buffer_length(rvdev->rvq, idx);
|
||||
rpmsg_virtio_return_buffer(rvdev, rp_hdr, len, idx);
|
||||
metal_mutex_release(&rdev->lock);
|
||||
}
|
||||
|
||||
static void *rpmsg_virtio_get_tx_payload_buffer(struct rpmsg_device *rdev,
|
||||
uint32_t *len, int wait)
|
||||
{
|
||||
struct rpmsg_virtio_device *rvdev;
|
||||
struct rpmsg_hdr *rp_hdr;
|
||||
uint16_t idx;
|
||||
int tick_count;
|
||||
int status;
|
||||
|
||||
/* Get the associated remote device for channel. */
|
||||
rvdev = metal_container_of(rdev, struct rpmsg_virtio_device, rdev);
|
||||
|
||||
/* Validate device state */
|
||||
status = rpmsg_virtio_get_status(rvdev);
|
||||
if (!(status & VIRTIO_CONFIG_STATUS_DRIVER_OK))
|
||||
return NULL;
|
||||
|
||||
if (wait)
|
||||
tick_count = RPMSG_TICK_COUNT / RPMSG_TICKS_PER_INTERVAL;
|
||||
else
|
||||
tick_count = 0;
|
||||
|
||||
while (1) {
|
||||
/* Lock the device to enable exclusive access to virtqueues */
|
||||
metal_mutex_acquire(&rdev->lock);
|
||||
rp_hdr = rpmsg_virtio_get_tx_buffer(rvdev, len, &idx);
|
||||
metal_mutex_release(&rdev->lock);
|
||||
if (rp_hdr || !tick_count)
|
||||
break;
|
||||
metal_sleep_usec(RPMSG_TICKS_PER_INTERVAL);
|
||||
tick_count--;
|
||||
}
|
||||
|
||||
if (!rp_hdr)
|
||||
return NULL;
|
||||
|
||||
/* Store the index into the reserved field to be used when sending */
|
||||
rp_hdr->reserved = idx;
|
||||
|
||||
/* Actual data buffer size is vring buffer size minus header length */
|
||||
*len -= sizeof(struct rpmsg_hdr);
|
||||
return RPMSG_LOCATE_DATA(rp_hdr);
|
||||
}
|
||||
|
||||
static int rpmsg_virtio_send_offchannel_nocopy(struct rpmsg_device *rdev,
|
||||
uint32_t src, uint32_t dst,
|
||||
const void *data, int len)
|
||||
{
|
||||
struct rpmsg_virtio_device *rvdev;
|
||||
struct metal_io_region *io;
|
||||
struct rpmsg_hdr rp_hdr;
|
||||
struct rpmsg_hdr *hdr;
|
||||
uint32_t buff_len;
|
||||
uint16_t idx;
|
||||
int status;
|
||||
|
||||
/* Get the associated remote device for channel. */
|
||||
rvdev = metal_container_of(rdev, struct rpmsg_virtio_device, rdev);
|
||||
|
||||
hdr = RPMSG_LOCATE_HDR(data);
|
||||
/* The reserved field contains buffer index */
|
||||
idx = hdr->reserved;
|
||||
|
||||
/* Initialize RPMSG header. */
|
||||
rp_hdr.dst = dst;
|
||||
rp_hdr.src = src;
|
||||
rp_hdr.len = len;
|
||||
rp_hdr.reserved = 0;
|
||||
rp_hdr.flags = 0;
|
||||
|
||||
/* Copy data to rpmsg buffer. */
|
||||
io = rvdev->shbuf_io;
|
||||
status = metal_io_block_write(io, metal_io_virt_to_offset(io, hdr),
|
||||
&rp_hdr, sizeof(rp_hdr));
|
||||
RPMSG_ASSERT(status == sizeof(rp_hdr), "failed to write header\r\n");
|
||||
|
||||
metal_mutex_acquire(&rdev->lock);
|
||||
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
if (rpmsg_virtio_get_role(rvdev) == RPMSG_MASTER)
|
||||
buff_len = RPMSG_BUFFER_SIZE;
|
||||
else
|
||||
#endif /*!VIRTIO_SLAVE_ONLY*/
|
||||
buff_len = virtqueue_get_buffer_length(rvdev->svq, idx);
|
||||
|
||||
/* Enqueue buffer on virtqueue. */
|
||||
status = rpmsg_virtio_enqueue_buffer(rvdev, hdr, buff_len, idx);
|
||||
RPMSG_ASSERT(status == VQUEUE_SUCCESS, "failed to enqueue buffer\r\n");
|
||||
/* Let the other side know that there is a job to process. */
|
||||
virtqueue_kick(rvdev->svq);
|
||||
|
||||
metal_mutex_release(&rdev->lock);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 len - 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 len, int wait)
|
||||
{
|
||||
struct rpmsg_virtio_device *rvdev;
|
||||
struct metal_io_region *io;
|
||||
uint32_t buff_len;
|
||||
void *buffer;
|
||||
int status;
|
||||
|
||||
/* Get the associated remote device for channel. */
|
||||
rvdev = metal_container_of(rdev, struct rpmsg_virtio_device, rdev);
|
||||
|
||||
/* Get the payload buffer. */
|
||||
buffer = rpmsg_virtio_get_tx_payload_buffer(rdev, &buff_len, wait);
|
||||
if (!buffer)
|
||||
return RPMSG_ERR_NO_BUFF;
|
||||
|
||||
/* Copy data to rpmsg buffer. */
|
||||
if (len > (int)buff_len)
|
||||
len = buff_len;
|
||||
io = rvdev->shbuf_io;
|
||||
status = metal_io_block_write(io, metal_io_virt_to_offset(io, buffer),
|
||||
data, len);
|
||||
RPMSG_ASSERT(status == len, "failed to write buffer\r\n");
|
||||
|
||||
return rpmsg_virtio_send_offchannel_nocopy(rdev, src, dst, buffer, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
uint32_t len;
|
||||
uint16_t idx;
|
||||
int status;
|
||||
|
||||
metal_mutex_acquire(&rdev->lock);
|
||||
|
||||
/* Process the received data from remote node */
|
||||
rp_hdr = rpmsg_virtio_get_rx_buffer(rvdev, &len, &idx);
|
||||
|
||||
metal_mutex_release(&rdev->lock);
|
||||
|
||||
while (rp_hdr) {
|
||||
rp_hdr->reserved = idx;
|
||||
|
||||
/* 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) {
|
||||
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, RPMSG_LOCATE_DATA(rp_hdr),
|
||||
rp_hdr->len, rp_hdr->src, ept->priv);
|
||||
|
||||
RPMSG_ASSERT(status >= 0,
|
||||
"unexpected callback status\r\n");
|
||||
}
|
||||
|
||||
metal_mutex_acquire(&rdev->lock);
|
||||
|
||||
/* Check whether callback wants to hold buffer */
|
||||
if (!(rp_hdr->reserved & RPMSG_BUF_HELD)) {
|
||||
/* No, return used buffers. */
|
||||
rpmsg_virtio_return_buffer(rvdev, rp_hdr, len, idx);
|
||||
}
|
||||
|
||||
rp_hdr = rpmsg_virtio_get_rx_buffer(rvdev, &len, &idx);
|
||||
if (!rp_hdr) {
|
||||
/* tell peer we return some rx buffer */
|
||||
virtqueue_kick(rvdev->rvq);
|
||||
}
|
||||
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 ept - 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
|
||||
*/
|
||||
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 = 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;
|
||||
}
|
||||
|
||||
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];
|
||||
vq_callback callback[RPMSG_NUM_VRINGS];
|
||||
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;
|
||||
rdev->ops.hold_rx_buffer = rpmsg_virtio_hold_rx_buffer;
|
||||
rdev->ops.release_rx_buffer = rpmsg_virtio_release_rx_buffer;
|
||||
rdev->ops.get_tx_payload_buffer = rpmsg_virtio_get_tx_payload_buffer;
|
||||
rdev->ops.send_offchannel_nocopy = rpmsg_virtio_send_offchannel_nocopy;
|
||||
role = rpmsg_virtio_get_role(rvdev);
|
||||
|
||||
#ifndef VIRTIO_MASTER_ONLY
|
||||
if (role == RPMSG_REMOTE) {
|
||||
/* wait synchro with the master */
|
||||
rpmsg_virtio_wait_remote_ready(rvdev);
|
||||
}
|
||||
#endif /*!VIRTIO_MASTER_ONLY*/
|
||||
vdev->features = rpmsg_virtio_get_features(rvdev);
|
||||
rdev->support_ns = !!(vdev->features & (1 << VIRTIO_RPMSG_F_NS));
|
||||
|
||||
#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;
|
||||
|
||||
/* Create virtqueues for remote device */
|
||||
status = rpmsg_virtio_create_virtqueues(rvdev, 0, RPMSG_NUM_VRINGS,
|
||||
vq_names, callback);
|
||||
if (status != RPMSG_SUCCESS)
|
||||
return status;
|
||||
|
||||
/*
|
||||
* Suppress "tx-complete" interrupts
|
||||
* since send method use busy loop when buffer pool exhaust
|
||||
*/
|
||||
virtqueue_disable_cb(rvdev->svq);
|
||||
|
||||
/* 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);
|
||||
|
||||
/*
|
||||
* Create name service announcement endpoint if device supports name
|
||||
* service announcement feature.
|
||||
*/
|
||||
if (rdev->support_ns) {
|
||||
rpmsg_initialize_ept(&rdev->ns_ept, "NS",
|
||||
RPMSG_NS_EPT_ADDR, RPMSG_NS_EPT_ADDR,
|
||||
rpmsg_virtio_ns_callback, NULL);
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
#include <openamp/virtio.h>
|
||||
|
||||
static const char *virtio_feature_name(unsigned long feature,
|
||||
const struct virtio_feature_desc *);
|
||||
|
||||
/*
|
||||
* TODO :
|
||||
* This structure may change depending on the types of devices we support.
|
||||
*/
|
||||
static const struct virtio_ident {
|
||||
unsigned short devid;
|
||||
const char *name;
|
||||
} virtio_ident_table[] = {
|
||||
{
|
||||
VIRTIO_ID_NETWORK, "Network"}, {
|
||||
VIRTIO_ID_BLOCK, "Block"}, {
|
||||
VIRTIO_ID_CONSOLE, "Console"}, {
|
||||
VIRTIO_ID_ENTROPY, "Entropy"}, {
|
||||
VIRTIO_ID_BALLOON, "Balloon"}, {
|
||||
VIRTIO_ID_IOMEMORY, "IOMemory"}, {
|
||||
VIRTIO_ID_SCSI, "SCSI"}, {
|
||||
VIRTIO_ID_9P, "9P Transport"}, {
|
||||
0, NULL}
|
||||
};
|
||||
|
||||
/* Device independent features. */
|
||||
static const struct virtio_feature_desc virtio_common_feature_desc[] = {
|
||||
{VIRTIO_F_NOTIFY_ON_EMPTY, "NotifyOnEmpty"},
|
||||
{VIRTIO_RING_F_INDIRECT_DESC, "RingIndirect"},
|
||||
{VIRTIO_RING_F_EVENT_IDX, "EventIdx"},
|
||||
{VIRTIO_F_BAD_FEATURE, "BadFeature"},
|
||||
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
const char *virtio_dev_name(unsigned short devid)
|
||||
{
|
||||
const struct virtio_ident *ident;
|
||||
|
||||
for (ident = virtio_ident_table; ident->name; ident++) {
|
||||
if (ident->devid == devid)
|
||||
return ident->name;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *virtio_feature_name(unsigned long val,
|
||||
const struct virtio_feature_desc *desc)
|
||||
{
|
||||
int i, j;
|
||||
const struct virtio_feature_desc *descs[2] = { desc,
|
||||
virtio_common_feature_desc
|
||||
};
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (!descs[i])
|
||||
continue;
|
||||
|
||||
for (j = 0; descs[i][j].vfd_val != 0; j++) {
|
||||
if (val == descs[i][j].vfd_val)
|
||||
return descs[i][j].vfd_str;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void virtio_describe(struct virtio_device *dev, const char *msg,
|
||||
uint32_t features, struct virtio_feature_desc *desc)
|
||||
{
|
||||
(void)dev;
|
||||
(void)msg;
|
||||
(void)features;
|
||||
|
||||
/* TODO: Not used currently - keeping it for future use*/
|
||||
virtio_feature_name(0, desc);
|
||||
}
|
||||
|
||||
int virtio_create_virtqueues(struct virtio_device *vdev, unsigned int flags,
|
||||
unsigned int nvqs, const char *names[],
|
||||
vq_callback callbacks[])
|
||||
{
|
||||
struct virtio_vring_info *vring_info;
|
||||
struct vring_alloc_info *vring_alloc;
|
||||
unsigned int num_vrings, i;
|
||||
int ret;
|
||||
(void)flags;
|
||||
|
||||
num_vrings = vdev->vrings_num;
|
||||
if (nvqs > num_vrings)
|
||||
return ERROR_VQUEUE_INVLD_PARAM;
|
||||
/* Initialize virtqueue for each vring */
|
||||
for (i = 0; i < nvqs; i++) {
|
||||
vring_info = &vdev->vrings_info[i];
|
||||
|
||||
vring_alloc = &vring_info->info;
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
if (vdev->role == VIRTIO_DEV_MASTER) {
|
||||
size_t offset;
|
||||
struct metal_io_region *io = vring_info->io;
|
||||
|
||||
offset = metal_io_virt_to_offset(io,
|
||||
vring_alloc->vaddr);
|
||||
metal_io_block_set(io, offset, 0,
|
||||
vring_size(vring_alloc->num_descs,
|
||||
vring_alloc->align));
|
||||
}
|
||||
#endif
|
||||
ret = virtqueue_create(vdev, i, names[i], vring_alloc,
|
||||
callbacks[i], vdev->func->notify,
|
||||
vring_info->vq);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,812 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <openamp/virtio.h>
|
||||
#include <openamp/virtqueue.h>
|
||||
#include <metal/atomic.h>
|
||||
#include <metal/log.h>
|
||||
#include <metal/alloc.h>
|
||||
#include <metal/cache.h>
|
||||
|
||||
/* Prototype for internal functions. */
|
||||
static void vq_ring_init(struct virtqueue *, void *, int);
|
||||
static void vq_ring_update_avail(struct virtqueue *, uint16_t);
|
||||
static uint16_t vq_ring_add_buffer(struct virtqueue *, struct vring_desc *,
|
||||
uint16_t, struct virtqueue_buf *, int, int);
|
||||
static int vq_ring_enable_interrupt(struct virtqueue *, uint16_t);
|
||||
static void vq_ring_free_chain(struct virtqueue *, uint16_t);
|
||||
static int vq_ring_must_notify(struct virtqueue *vq);
|
||||
static void vq_ring_notify(struct virtqueue *vq);
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
static int virtqueue_nused(struct virtqueue *vq);
|
||||
#endif
|
||||
#ifndef VIRTIO_MASTER_ONLY
|
||||
static int virtqueue_navail(struct virtqueue *vq);
|
||||
#endif
|
||||
|
||||
#ifdef VIRTIO_CACHED_VRINGS
|
||||
#define VRING_FLUSH(x) metal_cache_flush(&x, sizeof(x))
|
||||
#define VRING_INVALIDATE(x) metal_cache_invalidate(&x, sizeof(x))
|
||||
#else
|
||||
#define VRING_FLUSH(x) do { } while (0)
|
||||
#define VRING_INVALIDATE(x) do { } while (0)
|
||||
#endif /* VIRTIO_CACHED_VRINGS */
|
||||
|
||||
/* Default implementation of P2V based on libmetal */
|
||||
static inline void *virtqueue_phys_to_virt(struct virtqueue *vq,
|
||||
metal_phys_addr_t phys)
|
||||
{
|
||||
struct metal_io_region *io = vq->shm_io;
|
||||
|
||||
return metal_io_phys_to_virt(io, phys);
|
||||
}
|
||||
|
||||
/* Default implementation of V2P based on libmetal */
|
||||
static inline metal_phys_addr_t virtqueue_virt_to_phys(struct virtqueue *vq,
|
||||
void *buf)
|
||||
{
|
||||
struct metal_io_region *io = vq->shm_io;
|
||||
|
||||
return metal_io_virt_to_phys(io, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* virtqueue_create - Creates new VirtIO queue
|
||||
*
|
||||
* @param device - Pointer to VirtIO device
|
||||
* @param id - VirtIO queue ID , must be unique
|
||||
* @param name - Name of VirtIO queue
|
||||
* @param ring - Pointer to vring_alloc_info control block
|
||||
* @param callback - Pointer to callback function, invoked
|
||||
* when message is available on VirtIO queue
|
||||
* @param notify - Pointer to notify function, used to notify
|
||||
* other side that there is job available for it
|
||||
* @param vq - Created VirtIO queue.
|
||||
*
|
||||
* @return - Function status
|
||||
*/
|
||||
int virtqueue_create(struct virtio_device *virt_dev, unsigned short id,
|
||||
const char *name, struct vring_alloc_info *ring,
|
||||
void (*callback)(struct virtqueue *vq),
|
||||
void (*notify)(struct virtqueue *vq),
|
||||
struct virtqueue *vq)
|
||||
{
|
||||
int status = VQUEUE_SUCCESS;
|
||||
|
||||
VQ_PARAM_CHK(ring == NULL, status, ERROR_VQUEUE_INVLD_PARAM);
|
||||
VQ_PARAM_CHK(ring->num_descs == 0, status, ERROR_VQUEUE_INVLD_PARAM);
|
||||
VQ_PARAM_CHK(ring->num_descs & (ring->num_descs - 1), status,
|
||||
ERROR_VRING_ALIGN);
|
||||
VQ_PARAM_CHK(vq == NULL, status, ERROR_NO_MEM);
|
||||
|
||||
if (status == VQUEUE_SUCCESS) {
|
||||
vq->vq_dev = virt_dev;
|
||||
vq->vq_name = name;
|
||||
vq->vq_queue_index = id;
|
||||
vq->vq_nentries = ring->num_descs;
|
||||
vq->vq_free_cnt = vq->vq_nentries;
|
||||
vq->callback = callback;
|
||||
vq->notify = notify;
|
||||
|
||||
/* Initialize vring control block in virtqueue. */
|
||||
vq_ring_init(vq, ring->vaddr, ring->align);
|
||||
}
|
||||
|
||||
/*
|
||||
* CACHE: nothing to be done here. Only desc.next is setup at this
|
||||
* stage but that is only written by master, so no need to flush it.
|
||||
*/
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* virtqueue_add_buffer() - Enqueues new buffer in vring for consumption
|
||||
* by other side. Readable buffers are always
|
||||
* inserted before writable buffers
|
||||
*
|
||||
* @param vq - Pointer to VirtIO queue control block.
|
||||
* @param buf_list - Pointer to a list of virtqueue buffers.
|
||||
* @param readable - Number of readable buffers
|
||||
* @param writable - Number of writable buffers
|
||||
* @param cookie - Pointer to hold call back data
|
||||
*
|
||||
* @return - Function status
|
||||
*/
|
||||
int virtqueue_add_buffer(struct virtqueue *vq, struct virtqueue_buf *buf_list,
|
||||
int readable, int writable, void *cookie)
|
||||
{
|
||||
struct vq_desc_extra *dxp = NULL;
|
||||
int status = VQUEUE_SUCCESS;
|
||||
uint16_t head_idx;
|
||||
uint16_t idx;
|
||||
int needed;
|
||||
|
||||
needed = readable + writable;
|
||||
|
||||
VQ_PARAM_CHK(vq == NULL, status, ERROR_VQUEUE_INVLD_PARAM);
|
||||
VQ_PARAM_CHK(needed < 1, status, ERROR_VQUEUE_INVLD_PARAM);
|
||||
VQ_PARAM_CHK(vq->vq_free_cnt == 0, status, ERROR_VRING_FULL);
|
||||
|
||||
VQUEUE_BUSY(vq);
|
||||
|
||||
if (status == VQUEUE_SUCCESS) {
|
||||
VQASSERT(vq, cookie != NULL, "enqueuing with no cookie");
|
||||
|
||||
head_idx = vq->vq_desc_head_idx;
|
||||
VQ_RING_ASSERT_VALID_IDX(vq, head_idx);
|
||||
dxp = &vq->vq_descx[head_idx];
|
||||
|
||||
VQASSERT(vq, dxp->cookie == NULL,
|
||||
"cookie already exists for index");
|
||||
|
||||
dxp->cookie = cookie;
|
||||
dxp->ndescs = needed;
|
||||
|
||||
/* Enqueue buffer onto the ring. */
|
||||
idx = vq_ring_add_buffer(vq, vq->vq_ring.desc, head_idx,
|
||||
buf_list, readable, writable);
|
||||
|
||||
vq->vq_desc_head_idx = idx;
|
||||
vq->vq_free_cnt -= needed;
|
||||
|
||||
if (vq->vq_free_cnt == 0) {
|
||||
VQ_RING_ASSERT_CHAIN_TERM(vq);
|
||||
} else {
|
||||
VQ_RING_ASSERT_VALID_IDX(vq, idx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update vring_avail control block fields so that other
|
||||
* side can get buffer using it.
|
||||
*/
|
||||
vq_ring_update_avail(vq, head_idx);
|
||||
}
|
||||
|
||||
VQUEUE_IDLE(vq);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* virtqueue_get_buffer - Returns used buffers from VirtIO queue
|
||||
*
|
||||
* @param vq - Pointer to VirtIO queue control block
|
||||
* @param len - Length of conumed buffer
|
||||
* @param idx - index of the buffer
|
||||
*
|
||||
* @return - Pointer to used buffer
|
||||
*/
|
||||
void *virtqueue_get_buffer(struct virtqueue *vq, uint32_t *len, uint16_t *idx)
|
||||
{
|
||||
struct vring_used_elem *uep;
|
||||
void *cookie;
|
||||
uint16_t used_idx, desc_idx;
|
||||
|
||||
/* Used.idx is updated by slave, so we need to invalidate */
|
||||
VRING_INVALIDATE(vq->vq_ring.used->idx);
|
||||
|
||||
if (!vq || vq->vq_used_cons_idx == vq->vq_ring.used->idx)
|
||||
return NULL;
|
||||
|
||||
VQUEUE_BUSY(vq);
|
||||
|
||||
used_idx = vq->vq_used_cons_idx++ & (vq->vq_nentries - 1);
|
||||
uep = &vq->vq_ring.used->ring[used_idx];
|
||||
|
||||
atomic_thread_fence(memory_order_seq_cst);
|
||||
|
||||
/* Used.ring is written by slave, invalidate it */
|
||||
VRING_INVALIDATE(vq->vq_ring.used->ring[used_idx]);
|
||||
|
||||
desc_idx = (uint16_t)uep->id;
|
||||
if (len)
|
||||
*len = uep->len;
|
||||
|
||||
vq_ring_free_chain(vq, desc_idx);
|
||||
|
||||
cookie = vq->vq_descx[desc_idx].cookie;
|
||||
vq->vq_descx[desc_idx].cookie = NULL;
|
||||
|
||||
if (idx)
|
||||
*idx = used_idx;
|
||||
VQUEUE_IDLE(vq);
|
||||
|
||||
return cookie;
|
||||
}
|
||||
|
||||
uint32_t virtqueue_get_buffer_length(struct virtqueue *vq, uint16_t idx)
|
||||
{
|
||||
VRING_INVALIDATE(vq->vq_ring.desc[idx].len);
|
||||
return vq->vq_ring.desc[idx].len;
|
||||
}
|
||||
|
||||
/**
|
||||
* virtqueue_free - Frees VirtIO queue resources
|
||||
*
|
||||
* @param vq - Pointer to VirtIO queue control block
|
||||
*
|
||||
*/
|
||||
void virtqueue_free(struct virtqueue *vq)
|
||||
{
|
||||
if (vq) {
|
||||
if (vq->vq_free_cnt != vq->vq_nentries) {
|
||||
metal_log(METAL_LOG_WARNING,
|
||||
"%s: freeing non-empty virtqueue\r\n",
|
||||
vq->vq_name);
|
||||
}
|
||||
|
||||
metal_free_memory(vq);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* virtqueue_get_available_buffer - Returns buffer available for use in the
|
||||
* VirtIO queue
|
||||
*
|
||||
* @param vq - Pointer to VirtIO queue control block
|
||||
* @param avail_idx - Pointer to index used in vring desc table
|
||||
* @param len - Length of buffer
|
||||
*
|
||||
* @return - Pointer to available buffer
|
||||
*/
|
||||
void *virtqueue_get_available_buffer(struct virtqueue *vq, uint16_t *avail_idx,
|
||||
uint32_t *len)
|
||||
{
|
||||
uint16_t head_idx = 0;
|
||||
void *buffer;
|
||||
|
||||
atomic_thread_fence(memory_order_seq_cst);
|
||||
|
||||
/* Avail.idx is updated by master, invalidate it */
|
||||
VRING_INVALIDATE(vq->vq_ring.avail->idx);
|
||||
if (vq->vq_available_idx == vq->vq_ring.avail->idx) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
VQUEUE_BUSY(vq);
|
||||
|
||||
/* Avail.ring is updated by master, invalidate it */
|
||||
VRING_INVALIDATE(vq->vq_ring.avail->ring[head_idx]);
|
||||
|
||||
head_idx = vq->vq_available_idx++ & (vq->vq_nentries - 1);
|
||||
*avail_idx = vq->vq_ring.avail->ring[head_idx];
|
||||
|
||||
/* Invalidate the desc entry written by master before accessing it */
|
||||
VRING_INVALIDATE(vq->vq_ring.desc[*avail_idx]);
|
||||
buffer = virtqueue_phys_to_virt(vq, vq->vq_ring.desc[*avail_idx].addr);
|
||||
*len = vq->vq_ring.desc[*avail_idx].len;
|
||||
|
||||
VQUEUE_IDLE(vq);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* virtqueue_add_consumed_buffer - Returns consumed buffer back to VirtIO queue
|
||||
*
|
||||
* @param vq - Pointer to VirtIO queue control block
|
||||
* @param head_idx - Index of vring desc containing used buffer
|
||||
* @param len - Length of buffer
|
||||
*
|
||||
* @return - Function status
|
||||
*/
|
||||
int virtqueue_add_consumed_buffer(struct virtqueue *vq, uint16_t head_idx,
|
||||
uint32_t len)
|
||||
{
|
||||
struct vring_used_elem *used_desc = NULL;
|
||||
uint16_t used_idx;
|
||||
|
||||
if (head_idx > vq->vq_nentries) {
|
||||
return ERROR_VRING_NO_BUFF;
|
||||
}
|
||||
|
||||
VQUEUE_BUSY(vq);
|
||||
|
||||
/* CACHE: used is never written by master, so it's safe to directly access it */
|
||||
used_idx = vq->vq_ring.used->idx & (vq->vq_nentries - 1);
|
||||
used_desc = &vq->vq_ring.used->ring[used_idx];
|
||||
used_desc->id = head_idx;
|
||||
used_desc->len = len;
|
||||
|
||||
/* We still need to flush it because this is read by master */
|
||||
VRING_FLUSH(vq->vq_ring.used->ring[used_idx]);
|
||||
|
||||
atomic_thread_fence(memory_order_seq_cst);
|
||||
|
||||
vq->vq_ring.used->idx++;
|
||||
|
||||
/* Used.idx is read by master, so we need to flush it */
|
||||
VRING_FLUSH(vq->vq_ring.used->idx);
|
||||
|
||||
/* Keep pending count until virtqueue_notify(). */
|
||||
vq->vq_queued_cnt++;
|
||||
|
||||
VQUEUE_IDLE(vq);
|
||||
|
||||
return VQUEUE_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* virtqueue_enable_cb - Enables callback generation
|
||||
*
|
||||
* @param vq - Pointer to VirtIO queue control block
|
||||
*
|
||||
* @return - Function status
|
||||
*/
|
||||
int virtqueue_enable_cb(struct virtqueue *vq)
|
||||
{
|
||||
return vq_ring_enable_interrupt(vq, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* virtqueue_disable_cb - Disables callback generation
|
||||
*
|
||||
* @param vq - Pointer to VirtIO queue control block
|
||||
*
|
||||
*/
|
||||
void virtqueue_disable_cb(struct virtqueue *vq)
|
||||
{
|
||||
VQUEUE_BUSY(vq);
|
||||
|
||||
if (vq->vq_dev->features & VIRTIO_RING_F_EVENT_IDX) {
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
if (vq->vq_dev->role == VIRTIO_DEV_MASTER) {
|
||||
vring_used_event(&vq->vq_ring) =
|
||||
vq->vq_used_cons_idx - vq->vq_nentries - 1;
|
||||
VRING_FLUSH(vring_used_event(&vq->vq_ring));
|
||||
}
|
||||
#endif /*VIRTIO_SLAVE_ONLY*/
|
||||
#ifndef VIRTIO_MASTER_ONLY
|
||||
if (vq->vq_dev->role == VIRTIO_DEV_SLAVE) {
|
||||
vring_avail_event(&vq->vq_ring) =
|
||||
vq->vq_available_idx - vq->vq_nentries - 1;
|
||||
VRING_FLUSH(vring_avail_event(&vq->vq_ring));
|
||||
}
|
||||
#endif /*VIRTIO_MASTER_ONLY*/
|
||||
} else {
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
if (vq->vq_dev->role == VIRTIO_DEV_MASTER) {
|
||||
vq->vq_ring.avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;
|
||||
VRING_FLUSH(vq->vq_ring.avail->flags);
|
||||
}
|
||||
#endif /*VIRTIO_SLAVE_ONLY*/
|
||||
#ifndef VIRTIO_MASTER_ONLY
|
||||
if (vq->vq_dev->role == VIRTIO_DEV_SLAVE) {
|
||||
vq->vq_ring.used->flags |= VRING_USED_F_NO_NOTIFY;
|
||||
VRING_FLUSH(vq->vq_ring.used->flags);
|
||||
}
|
||||
#endif /*VIRTIO_MASTER_ONLY*/
|
||||
}
|
||||
|
||||
VQUEUE_IDLE(vq);
|
||||
}
|
||||
|
||||
/**
|
||||
* virtqueue_kick - Notifies other side that there is buffer available for it.
|
||||
*
|
||||
* @param vq - Pointer to VirtIO queue control block
|
||||
*/
|
||||
void virtqueue_kick(struct virtqueue *vq)
|
||||
{
|
||||
VQUEUE_BUSY(vq);
|
||||
|
||||
/* Ensure updated avail->idx is visible to host. */
|
||||
atomic_thread_fence(memory_order_seq_cst);
|
||||
|
||||
if (vq_ring_must_notify(vq))
|
||||
vq_ring_notify(vq);
|
||||
|
||||
vq->vq_queued_cnt = 0;
|
||||
|
||||
VQUEUE_IDLE(vq);
|
||||
}
|
||||
|
||||
/**
|
||||
* virtqueue_dump Dumps important virtqueue fields , use for debugging purposes
|
||||
*
|
||||
* @param vq - Pointer to VirtIO queue control block
|
||||
*/
|
||||
void virtqueue_dump(struct virtqueue *vq)
|
||||
{
|
||||
if (!vq)
|
||||
return;
|
||||
|
||||
VRING_INVALIDATE(vq->vq_ring.avail);
|
||||
VRING_INVALIDATE(vq->vq_ring.used);
|
||||
|
||||
metal_log(METAL_LOG_DEBUG,
|
||||
"VQ: %s - size=%d; free=%d; queued=%d; "
|
||||
"desc_head_idx=%d; avail.idx=%d; used_cons_idx=%d; "
|
||||
"used.idx=%d; avail.flags=0x%x; used.flags=0x%x\r\n",
|
||||
vq->vq_name, vq->vq_nentries, vq->vq_free_cnt,
|
||||
vq->vq_queued_cnt, vq->vq_desc_head_idx,
|
||||
vq->vq_ring.avail->idx, vq->vq_used_cons_idx,
|
||||
vq->vq_ring.used->idx, vq->vq_ring.avail->flags,
|
||||
vq->vq_ring.used->flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* virtqueue_get_desc_size - Returns vring descriptor size
|
||||
*
|
||||
* @param vq - Pointer to VirtIO queue control block
|
||||
*
|
||||
* @return - Descriptor length
|
||||
*/
|
||||
uint32_t virtqueue_get_desc_size(struct virtqueue *vq)
|
||||
{
|
||||
uint16_t head_idx = 0;
|
||||
uint16_t avail_idx = 0;
|
||||
uint32_t len = 0;
|
||||
|
||||
/* Avail.idx is updated by master, invalidate it */
|
||||
VRING_INVALIDATE(vq->vq_ring.avail->idx);
|
||||
|
||||
if (vq->vq_available_idx == vq->vq_ring.avail->idx) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
VQUEUE_BUSY(vq);
|
||||
|
||||
/* Avail.ring is updated by master, invalidate it */
|
||||
VRING_INVALIDATE(vq->vq_ring.avail->ring[head_idx]);
|
||||
|
||||
head_idx = vq->vq_available_idx & (vq->vq_nentries - 1);
|
||||
avail_idx = vq->vq_ring.avail->ring[head_idx];
|
||||
|
||||
/* Invalidate the desc entry written by master before accessing it */
|
||||
VRING_INVALIDATE(vq->vq_ring.desc[avail_idx].len);
|
||||
|
||||
len = vq->vq_ring.desc[avail_idx].len;
|
||||
|
||||
VQUEUE_IDLE(vq);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* Helper Functions *
|
||||
**************************************************************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* vq_ring_add_buffer
|
||||
*
|
||||
*/
|
||||
static uint16_t vq_ring_add_buffer(struct virtqueue *vq,
|
||||
struct vring_desc *desc, uint16_t head_idx,
|
||||
struct virtqueue_buf *buf_list, int readable,
|
||||
int writable)
|
||||
{
|
||||
struct vring_desc *dp;
|
||||
int i, needed;
|
||||
uint16_t idx;
|
||||
|
||||
(void)vq;
|
||||
|
||||
needed = readable + writable;
|
||||
|
||||
for (i = 0, idx = head_idx; i < needed; i++, idx = dp->next) {
|
||||
VQASSERT(vq, idx != VQ_RING_DESC_CHAIN_END,
|
||||
"premature end of free desc chain");
|
||||
|
||||
/* CACHE: No need to invalidate desc because it is only written by master */
|
||||
dp = &desc[idx];
|
||||
dp->addr = virtqueue_virt_to_phys(vq, buf_list[i].buf);
|
||||
dp->len = buf_list[i].len;
|
||||
dp->flags = 0;
|
||||
|
||||
if (i < needed - 1)
|
||||
dp->flags |= VRING_DESC_F_NEXT;
|
||||
|
||||
/*
|
||||
* Readable buffers are inserted into vring before the
|
||||
* writable buffers.
|
||||
*/
|
||||
if (i >= readable)
|
||||
dp->flags |= VRING_DESC_F_WRITE;
|
||||
|
||||
/*
|
||||
* Instead of flushing the whole desc region, we flush only the
|
||||
* single entry hopefully saving some cycles
|
||||
*/
|
||||
VRING_FLUSH(desc[idx]);
|
||||
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* vq_ring_free_chain
|
||||
*
|
||||
*/
|
||||
static void vq_ring_free_chain(struct virtqueue *vq, uint16_t desc_idx)
|
||||
{
|
||||
struct vring_desc *dp;
|
||||
struct vq_desc_extra *dxp;
|
||||
|
||||
/* CACHE: desc is never written by slave, no need to invalidate */
|
||||
VQ_RING_ASSERT_VALID_IDX(vq, desc_idx);
|
||||
dp = &vq->vq_ring.desc[desc_idx];
|
||||
dxp = &vq->vq_descx[desc_idx];
|
||||
|
||||
if (vq->vq_free_cnt == 0) {
|
||||
VQ_RING_ASSERT_CHAIN_TERM(vq);
|
||||
}
|
||||
|
||||
vq->vq_free_cnt += dxp->ndescs;
|
||||
dxp->ndescs--;
|
||||
|
||||
if ((dp->flags & VRING_DESC_F_INDIRECT) == 0) {
|
||||
while (dp->flags & VRING_DESC_F_NEXT) {
|
||||
VQ_RING_ASSERT_VALID_IDX(vq, dp->next);
|
||||
dp = &vq->vq_ring.desc[dp->next];
|
||||
dxp->ndescs--;
|
||||
}
|
||||
}
|
||||
|
||||
VQASSERT(vq, dxp->ndescs == 0,
|
||||
"failed to free entire desc chain, remaining");
|
||||
|
||||
/*
|
||||
* We must append the existing free chain, if any, to the end of
|
||||
* newly freed chain. If the virtqueue was completely used, then
|
||||
* head would be VQ_RING_DESC_CHAIN_END (ASSERTed above).
|
||||
*
|
||||
* CACHE: desc.next is never read by slave, no need to flush it.
|
||||
*/
|
||||
dp->next = vq->vq_desc_head_idx;
|
||||
vq->vq_desc_head_idx = desc_idx;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* vq_ring_init
|
||||
*
|
||||
*/
|
||||
static void vq_ring_init(struct virtqueue *vq, void *ring_mem, int alignment)
|
||||
{
|
||||
struct vring *vr;
|
||||
int size;
|
||||
|
||||
size = vq->vq_nentries;
|
||||
vr = &vq->vq_ring;
|
||||
|
||||
vring_init(vr, size, ring_mem, alignment);
|
||||
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
if (vq->vq_dev->role == VIRTIO_DEV_MASTER) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size - 1; i++)
|
||||
vr->desc[i].next = i + 1;
|
||||
vr->desc[i].next = VQ_RING_DESC_CHAIN_END;
|
||||
}
|
||||
#endif /*VIRTIO_SLAVE_ONLY*/
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* vq_ring_update_avail
|
||||
*
|
||||
*/
|
||||
static void vq_ring_update_avail(struct virtqueue *vq, uint16_t desc_idx)
|
||||
{
|
||||
uint16_t avail_idx;
|
||||
|
||||
/*
|
||||
* Place the head of the descriptor chain into the next slot and make
|
||||
* it usable to the host. The chain is made available now rather than
|
||||
* deferring to virtqueue_notify() in the hopes that if the host is
|
||||
* currently running on another CPU, we can keep it processing the new
|
||||
* descriptor.
|
||||
*
|
||||
* CACHE: avail is never written by slave, so it is safe to not invalidate here
|
||||
*/
|
||||
avail_idx = vq->vq_ring.avail->idx & (vq->vq_nentries - 1);
|
||||
vq->vq_ring.avail->ring[avail_idx] = desc_idx;
|
||||
|
||||
/* We still need to flush the ring */
|
||||
VRING_FLUSH(vq->vq_ring.avail->ring[avail_idx]);
|
||||
|
||||
atomic_thread_fence(memory_order_seq_cst);
|
||||
|
||||
vq->vq_ring.avail->idx++;
|
||||
|
||||
/* And the index */
|
||||
VRING_FLUSH(vq->vq_ring.avail->idx);
|
||||
|
||||
/* Keep pending count until virtqueue_notify(). */
|
||||
vq->vq_queued_cnt++;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* vq_ring_enable_interrupt
|
||||
*
|
||||
*/
|
||||
static int vq_ring_enable_interrupt(struct virtqueue *vq, uint16_t ndesc)
|
||||
{
|
||||
/*
|
||||
* Enable interrupts, making sure we get the latest index of
|
||||
* what's already been consumed.
|
||||
*/
|
||||
if (vq->vq_dev->features & VIRTIO_RING_F_EVENT_IDX) {
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
if (vq->vq_dev->role == VIRTIO_DEV_MASTER) {
|
||||
vring_used_event(&vq->vq_ring) =
|
||||
vq->vq_used_cons_idx + ndesc;
|
||||
VRING_FLUSH(vring_used_event(&vq->vq_ring));
|
||||
}
|
||||
#endif /*VIRTIO_SLAVE_ONLY*/
|
||||
#ifndef VIRTIO_MASTER_ONLY
|
||||
if (vq->vq_dev->role == VIRTIO_DEV_SLAVE) {
|
||||
vring_avail_event(&vq->vq_ring) =
|
||||
vq->vq_available_idx + ndesc;
|
||||
VRING_FLUSH(vring_avail_event(&vq->vq_ring));
|
||||
}
|
||||
#endif /*VIRTIO_MASTER_ONLY*/
|
||||
} else {
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
if (vq->vq_dev->role == VIRTIO_DEV_MASTER) {
|
||||
vq->vq_ring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;
|
||||
VRING_FLUSH(vq->vq_ring.avail->flags);
|
||||
}
|
||||
#endif /*VIRTIO_SLAVE_ONLY*/
|
||||
#ifndef VIRTIO_MASTER_ONLY
|
||||
if (vq->vq_dev->role == VIRTIO_DEV_SLAVE) {
|
||||
vq->vq_ring.used->flags &= ~VRING_USED_F_NO_NOTIFY;
|
||||
VRING_FLUSH(vq->vq_ring.used->flags);
|
||||
}
|
||||
#endif /*VIRTIO_MASTER_ONLY*/
|
||||
}
|
||||
|
||||
atomic_thread_fence(memory_order_seq_cst);
|
||||
|
||||
/*
|
||||
* Enough items may have already been consumed to meet our threshold
|
||||
* since we last checked. Let our caller know so it processes the new
|
||||
* entries.
|
||||
*/
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
if (vq->vq_dev->role == VIRTIO_DEV_MASTER) {
|
||||
if (virtqueue_nused(vq) > ndesc) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif /*VIRTIO_SLAVE_ONLY*/
|
||||
#ifndef VIRTIO_MASTER_ONLY
|
||||
if (vq->vq_dev->role == VIRTIO_DEV_SLAVE) {
|
||||
if (virtqueue_navail(vq) > ndesc) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif /*VIRTIO_MASTER_ONLY*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* virtqueue_interrupt
|
||||
*
|
||||
*/
|
||||
void virtqueue_notification(struct virtqueue *vq)
|
||||
{
|
||||
atomic_thread_fence(memory_order_seq_cst);
|
||||
if (vq->callback)
|
||||
vq->callback(vq);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* vq_ring_must_notify
|
||||
*
|
||||
*/
|
||||
static int vq_ring_must_notify(struct virtqueue *vq)
|
||||
{
|
||||
uint16_t new_idx, prev_idx, event_idx;
|
||||
|
||||
if (vq->vq_dev->features & VIRTIO_RING_F_EVENT_IDX) {
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
if (vq->vq_dev->role == VIRTIO_DEV_MASTER) {
|
||||
/* CACHE: no need to invalidate avail */
|
||||
new_idx = vq->vq_ring.avail->idx;
|
||||
prev_idx = new_idx - vq->vq_queued_cnt;
|
||||
VRING_INVALIDATE(vring_avail_event(&vq->vq_ring));
|
||||
event_idx = vring_avail_event(&vq->vq_ring);
|
||||
return vring_need_event(event_idx, new_idx,
|
||||
prev_idx) != 0;
|
||||
}
|
||||
#endif /*VIRTIO_SLAVE_ONLY*/
|
||||
#ifndef VIRTIO_MASTER_ONLY
|
||||
if (vq->vq_dev->role == VIRTIO_DEV_SLAVE) {
|
||||
/* CACHE: no need to invalidate used */
|
||||
new_idx = vq->vq_ring.used->idx;
|
||||
prev_idx = new_idx - vq->vq_queued_cnt;
|
||||
VRING_INVALIDATE(vring_used_event(&vq->vq_ring));
|
||||
event_idx = vring_used_event(&vq->vq_ring);
|
||||
return vring_need_event(event_idx, new_idx,
|
||||
prev_idx) != 0;
|
||||
}
|
||||
#endif /*VIRTIO_MASTER_ONLY*/
|
||||
} else {
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
if (vq->vq_dev->role == VIRTIO_DEV_MASTER) {
|
||||
VRING_INVALIDATE(vq->vq_ring.used->flags);
|
||||
return (vq->vq_ring.used->flags &
|
||||
VRING_USED_F_NO_NOTIFY) == 0;
|
||||
}
|
||||
#endif /*VIRTIO_SLAVE_ONLY*/
|
||||
#ifndef VIRTIO_MASTER_ONLY
|
||||
if (vq->vq_dev->role == VIRTIO_DEV_SLAVE) {
|
||||
VRING_INVALIDATE(vq->vq_ring.avail->flags);
|
||||
return (vq->vq_ring.avail->flags &
|
||||
VRING_AVAIL_F_NO_INTERRUPT) == 0;
|
||||
}
|
||||
#endif /*VIRTIO_MASTER_ONLY*/
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* vq_ring_notify
|
||||
*
|
||||
*/
|
||||
static void vq_ring_notify(struct virtqueue *vq)
|
||||
{
|
||||
if (vq->notify)
|
||||
vq->notify(vq);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* virtqueue_nused
|
||||
*
|
||||
*/
|
||||
#ifndef VIRTIO_SLAVE_ONLY
|
||||
static int virtqueue_nused(struct virtqueue *vq)
|
||||
{
|
||||
uint16_t used_idx, nused;
|
||||
|
||||
/* Used is written by slave */
|
||||
VRING_INVALIDATE(vq->vq_ring.used->idx);
|
||||
used_idx = vq->vq_ring.used->idx;
|
||||
|
||||
nused = (uint16_t)(used_idx - vq->vq_used_cons_idx);
|
||||
VQASSERT(vq, nused <= vq->vq_nentries, "used more than available");
|
||||
|
||||
return nused;
|
||||
}
|
||||
#endif /*VIRTIO_SLAVE_ONLY*/
|
||||
|
||||
/**
|
||||
*
|
||||
* virtqueue_navail
|
||||
*
|
||||
*/
|
||||
#ifndef VIRTIO_MASTER_ONLY
|
||||
static int virtqueue_navail(struct virtqueue *vq)
|
||||
{
|
||||
uint16_t avail_idx, navail;
|
||||
|
||||
/* Avail is written by master */
|
||||
VRING_INVALIDATE(vq->vq_ring.avail->idx);
|
||||
|
||||
avail_idx = vq->vq_ring.avail->idx;
|
||||
|
||||
navail = (uint16_t)(avail_idx - vq->vq_available_idx);
|
||||
VQASSERT(vq, navail <= vq->vq_nentries, "avail more than available");
|
||||
|
||||
return navail;
|
||||
}
|
||||
#endif /*VIRTIO_MASTER_ONLY*/
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file virt_uart.c
|
||||
* @author MCD Application Team
|
||||
* @brief UART HAL module driver.
|
||||
* This file provides firmware functions to manage an rpmsg endpoint
|
||||
* from user application
|
||||
*
|
||||
*
|
||||
@verbatim
|
||||
===============================================================================
|
||||
##### How to use this driver #####
|
||||
===============================================================================
|
||||
[..]
|
||||
The VIRTUAL UART driver can be used as follows:
|
||||
(#) Initialize the Virtual UART by calling the VIRT_UART_Init() API.
|
||||
(++) create an endpoint. listener on the OpenAMP-rpmsg channel is now enabled.
|
||||
Receive data is now possible if user registers a callback to this VIRTUAL UART instance
|
||||
by calling in providing a callback function when a message is received from
|
||||
remote processor (VIRT_UART_read_cb)
|
||||
OpenAMP MW deals with memory allocation/free and signal events
|
||||
(#) Transmit data on the created rpmsg channel by calling the VIRT_UART_Transmit()
|
||||
(#) Receive data in calling VIRT_UART_RegisterCallback to register user callback
|
||||
|
||||
|
||||
@endverbatim
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© Copyright (c) 2019 STMicroelectronics.
|
||||
* All rights reserved.</center></h2>
|
||||
*
|
||||
* This software component is licensed by ST under BSD 3-Clause license,
|
||||
* the "License"; You may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at:
|
||||
* opensource.org/licenses/BSD-3-Clause
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
#include "virt_uart.h"
|
||||
#include "metal/utilities.h"
|
||||
|
||||
|
||||
/* Private typedef -----------------------------------------------------------*/
|
||||
/* Private define ------------------------------------------------------------*/
|
||||
/* this string will be sent to remote processor */
|
||||
#define RPMSG_SERVICE_NAME "rpmsg-tty"
|
||||
|
||||
/* Private macro -------------------------------------------------------------*/
|
||||
/* Private variables ---------------------------------------------------------*/
|
||||
|
||||
static int VIRT_UART_read_cb(struct rpmsg_endpoint *ept, void *data,
|
||||
size_t len, uint32_t src, void *priv)
|
||||
{
|
||||
VIRT_UART_HandleTypeDef *huart = metal_container_of(ept, VIRT_UART_HandleTypeDef, ept);
|
||||
(void)src;
|
||||
|
||||
huart->pRxBuffPtr = data;
|
||||
huart->RxXferSize = len;
|
||||
if (huart->RxCpltCallback != NULL) {
|
||||
huart->RxCpltCallback(huart);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
VIRT_UART_StatusTypeDef VIRT_UART_Init(VIRT_UART_HandleTypeDef *huart)
|
||||
{
|
||||
|
||||
int status;
|
||||
|
||||
/* Create a endpoint for rmpsg communication */
|
||||
|
||||
status = OPENAMP_create_endpoint(&huart->ept, RPMSG_SERVICE_NAME, RPMSG_ADDR_ANY,
|
||||
VIRT_UART_read_cb, NULL);
|
||||
|
||||
if(status < 0) {
|
||||
return VIRT_UART_ERROR;
|
||||
}
|
||||
|
||||
return VIRT_UART_OK;
|
||||
}
|
||||
|
||||
VIRT_UART_StatusTypeDef VIRT_UART_DeInit (VIRT_UART_HandleTypeDef *huart)
|
||||
{
|
||||
OPENAMP_destroy_ept(&huart->ept);
|
||||
|
||||
return VIRT_UART_OK;
|
||||
}
|
||||
|
||||
VIRT_UART_StatusTypeDef VIRT_UART_RegisterCallback(VIRT_UART_HandleTypeDef *huart,
|
||||
VIRT_UART_CallbackIDTypeDef CallbackID,
|
||||
void (* pCallback)(VIRT_UART_HandleTypeDef *_huart))
|
||||
{
|
||||
VIRT_UART_StatusTypeDef status = VIRT_UART_OK;
|
||||
|
||||
switch (CallbackID)
|
||||
{
|
||||
case VIRT_UART_RXCPLT_CB_ID :
|
||||
huart->RxCpltCallback = pCallback;
|
||||
break;
|
||||
|
||||
default :
|
||||
/* Return error status */
|
||||
status = VIRT_UART_ERROR;
|
||||
break;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
VIRT_UART_StatusTypeDef VIRT_UART_Transmit(VIRT_UART_HandleTypeDef *huart, const void *pData, uint16_t Size)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (Size > (RPMSG_BUFFER_SIZE-16))
|
||||
return VIRT_UART_ERROR;
|
||||
|
||||
res = OPENAMP_send(&huart->ept, pData, Size);
|
||||
if (res <0) {
|
||||
return VIRT_UART_ERROR;
|
||||
}
|
||||
|
||||
return VIRT_UART_OK;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file virt_uart.h
|
||||
* @author MCD Application Team
|
||||
* @brief Header file of UART VIRT module.
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© Copyright (c) 2019 STMicroelectronics.
|
||||
* All rights reserved.</center></h2>
|
||||
*
|
||||
* This software component is licensed by ST under BSD 3-Clause license,
|
||||
* the "License"; You may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at:
|
||||
* opensource.org/licenses/BSD-3-Clause
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/* Define to prevent recursive inclusion -------------------------------------*/
|
||||
#ifndef __VIRT_UART_H
|
||||
#define __VIRT_UART_H
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
#include "openamp.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Exported structures --------------------------------------------------------*/
|
||||
typedef struct __VIRT_UART_HandleTypeDef
|
||||
{
|
||||
struct rpmsg_endpoint ept; /*!< rpmsg endpoint */
|
||||
struct rpmsg_virtio_device *rvdev; /*< pointer to the rpmsg virtio device */
|
||||
uint8_t *pRxBuffPtr; /*!< Pointer to VIRTUAL UART Rx transfer Buffer */
|
||||
uint16_t RxXferSize; /*!< VIRTUAL UART Rx Transfer size */
|
||||
void (* RxCpltCallback)( struct __VIRT_UART_HandleTypeDef * hppp); /*!< RX CPLT callback */
|
||||
}VIRT_UART_HandleTypeDef;
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VIRT_UART_OK = 0x00U,
|
||||
VIRT_UART_ERROR = 0x01U,
|
||||
VIRT_UART_BUSY = 0x02U,
|
||||
VIRT_UART_TIMEOUT = 0x03U
|
||||
} VIRT_UART_StatusTypeDef;
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VIRT_UART_RXCPLT_CB_ID = 0x00U, /*!< PPP event 1 callback ID */
|
||||
}VIRT_UART_CallbackIDTypeDef;
|
||||
|
||||
|
||||
/* Exported functions --------------------------------------------------------*/
|
||||
/* Initialization and de-initialization functions ****************************/
|
||||
VIRT_UART_StatusTypeDef VIRT_UART_Init(VIRT_UART_HandleTypeDef *huart);
|
||||
VIRT_UART_StatusTypeDef VIRT_UART_DeInit (VIRT_UART_HandleTypeDef *huart);
|
||||
VIRT_UART_StatusTypeDef VIRT_UART_RegisterCallback(VIRT_UART_HandleTypeDef *huart,
|
||||
VIRT_UART_CallbackIDTypeDef CallbackID,
|
||||
void (* pCallback)(VIRT_UART_HandleTypeDef *_huart));
|
||||
|
||||
/* IO operation functions *****************************************************/
|
||||
VIRT_UART_StatusTypeDef VIRT_UART_Transmit(VIRT_UART_HandleTypeDef *huart, const void *pData, uint16_t Size);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __VIRT_UART_H */
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
Reference in New Issue
Block a user