mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2024-11-17 00:54:59 +02:00
1df0438a5e
Add support for Raspberry Pi / brcm2708 / 2835 Signed-off-by: Ian Ridge <ianridge [at] gmail.com> git-svn-id: svn://svn.openwrt.org/openwrt/trunk@32825 3c298f89-4303-0410-b956-a3cf2f4a3e73
16384 lines
487 KiB
Diff
16384 lines
487 KiB
Diff
From a2e42fbc97cde9e851f5b036f46b8f34a5be6eb9 Mon Sep 17 00:00:00 2001
|
|
From: popcornmix <popcornmix@gmail.com>
|
|
Date: Tue, 17 Jan 2012 19:22:19 +0000
|
|
Subject: [PATCH 5/7] bcm2708 vchiq driver
|
|
|
|
Signed-off-by: popcornmix <popcornmix@gmail.com>
|
|
---
|
|
drivers/misc/Kconfig | 1 +
|
|
drivers/misc/Makefile | 1 +
|
|
drivers/misc/vc04_services/Kconfig | 7 +
|
|
drivers/misc/vc04_services/Makefile | 19 +
|
|
.../misc/vc04_services/interface/vchi/vchi_mh.h | 19 +
|
|
.../misc/vc04_services/interface/vchiq_arm/vchiq.h | 27 +
|
|
.../vc04_services/interface/vchiq_arm/vchiq_2835.h | 27 +
|
|
.../interface/vchiq_arm/vchiq_2835_arm.c | 487 ++++
|
|
.../vc04_services/interface/vchiq_arm/vchiq_arm.c | 1293 ++++++++++
|
|
.../vc04_services/interface/vchiq_arm/vchiq_arm.h | 38 +
|
|
.../vc04_services/interface/vchiq_arm/vchiq_cfg.h | 43 +
|
|
.../interface/vchiq_arm/vchiq_connected.c | 101 +
|
|
.../interface/vchiq_arm/vchiq_connected.h | 32 +
|
|
.../vc04_services/interface/vchiq_arm/vchiq_core.c | 2604 ++++++++++++++++++++
|
|
.../vc04_services/interface/vchiq_arm/vchiq_core.h | 480 ++++
|
|
.../vc04_services/interface/vchiq_arm/vchiq_if.h | 148 ++
|
|
.../interface/vchiq_arm/vchiq_ioctl.h | 105 +
|
|
.../interface/vchiq_arm/vchiq_kern_lib.c | 297 +++
|
|
.../vc04_services/interface/vchiq_arm/vchiq_lib.c | 1518 ++++++++++++
|
|
.../interface/vchiq_arm/vchiq_memdrv.h | 45 +
|
|
.../interface/vchiq_arm/vchiq_pagelist.h | 43 +
|
|
.../vc04_services/interface/vchiq_arm/vchiq_shim.c | 970 ++++++++
|
|
.../vc04_services/interface/vchiq_arm/vchiq_util.c | 97 +
|
|
.../vc04_services/interface/vchiq_arm/vchiq_util.h | 47 +
|
|
.../interface/vcos/generic/vcos_cmd.c | 681 +++++
|
|
.../interface/vcos/generic/vcos_common.h | 76 +
|
|
.../vcos/generic/vcos_generic_blockpool.h | 260 ++
|
|
.../vcos/generic/vcos_generic_event_flags.c | 297 +++
|
|
.../vcos/generic/vcos_generic_event_flags.h | 104 +
|
|
.../vcos/generic/vcos_generic_named_sem.h | 81 +
|
|
.../vcos/generic/vcos_generic_quickslow_mutex.h | 75 +
|
|
.../vcos/generic/vcos_generic_reentrant_mtx.h | 75 +
|
|
.../interface/vcos/generic/vcos_generic_tls.h | 144 ++
|
|
.../vcos/generic/vcos_joinable_thread_from_plain.h | 202 ++
|
|
.../interface/vcos/generic/vcos_latch_from_sem.h | 48 +
|
|
.../interface/vcos/generic/vcos_logcat.c | 549 +++++
|
|
.../interface/vcos/generic/vcos_mem_from_malloc.c | 73 +
|
|
.../interface/vcos/generic/vcos_mem_from_malloc.h | 54 +
|
|
.../vcos/generic/vcos_mutexes_are_reentrant.h | 68 +
|
|
.../interface/vcos/generic/vcos_thread_reaper.h | 35 +
|
|
.../interface/vcos/linuxkernel/stdint.h | 17 +
|
|
.../interface/vcos/linuxkernel/vcos_linuxkernel.c | 616 +++++
|
|
.../vcos/linuxkernel/vcos_linuxkernel_cfg.c | 332 +++
|
|
.../vcos/linuxkernel/vcos_linuxkernel_misc.c | 113 +
|
|
.../interface/vcos/linuxkernel/vcos_mod_init.c | 64 +
|
|
.../interface/vcos/linuxkernel/vcos_platform.h | 496 ++++
|
|
.../vcos/linuxkernel/vcos_platform_types.h | 47 +
|
|
.../interface/vcos/linuxkernel/vcos_thread_map.c | 129 +
|
|
.../interface/vcos/linuxkernel/vcos_thread_map.h | 39 +
|
|
drivers/misc/vc04_services/interface/vcos/vcos.h | 201 ++
|
|
.../vc04_services/interface/vcos/vcos_assert.h | 269 ++
|
|
.../interface/vcos/vcos_atomic_flags.h | 72 +
|
|
.../vc04_services/interface/vcos/vcos_build_info.h | 5 +
|
|
.../misc/vc04_services/interface/vcos/vcos_cfg.h | 113 +
|
|
.../misc/vc04_services/interface/vcos/vcos_cmd.h | 98 +
|
|
.../misc/vc04_services/interface/vcos/vcos_ctype.h | 29 +
|
|
.../misc/vc04_services/interface/vcos/vcos_dlfcn.h | 69 +
|
|
.../misc/vc04_services/interface/vcos/vcos_event.h | 97 +
|
|
.../interface/vcos/vcos_event_flags.h | 98 +
|
|
.../misc/vc04_services/interface/vcos/vcos_init.h | 43 +
|
|
.../vc04_services/interface/vcos/vcos_logging.h | 279 +++
|
|
.../interface/vcos/vcos_lowlevel_thread.h | 107 +
|
|
.../misc/vc04_services/interface/vcos/vcos_mem.h | 81 +
|
|
.../vc04_services/interface/vcos/vcos_msgqueue.h | 157 ++
|
|
.../misc/vc04_services/interface/vcos/vcos_mutex.h | 92 +
|
|
.../misc/vc04_services/interface/vcos/vcos_once.h | 42 +
|
|
.../vc04_services/interface/vcos/vcos_semaphore.h | 115 +
|
|
.../vc04_services/interface/vcos/vcos_stdbool.h | 17 +
|
|
.../vc04_services/interface/vcos/vcos_stdint.h | 193 ++
|
|
.../vc04_services/interface/vcos/vcos_string.h | 73 +
|
|
.../vc04_services/interface/vcos/vcos_thread.h | 259 ++
|
|
.../interface/vcos/vcos_thread_attr.h | 73 +
|
|
.../misc/vc04_services/interface/vcos/vcos_timer.h | 95 +
|
|
.../misc/vc04_services/interface/vcos/vcos_types.h | 197 ++
|
|
74 files changed, 15998 insertions(+), 0 deletions(-)
|
|
create mode 100644 drivers/misc/vc04_services/Kconfig
|
|
create mode 100644 drivers/misc/vc04_services/Makefile
|
|
create mode 100644 drivers/misc/vc04_services/interface/vchi/vchi_mh.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
|
|
create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c
|
|
create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c
|
|
create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c
|
|
create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_if.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c
|
|
create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_lib.c
|
|
create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_memdrv.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_pagelist.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c
|
|
create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c
|
|
create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_cmd.c
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_common.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_blockpool.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_event_flags.c
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_event_flags.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_named_sem.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_quickslow_mutex.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_reentrant_mtx.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_tls.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_joinable_thread_from_plain.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_latch_from_sem.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_logcat.c
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_mem_from_malloc.c
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_mem_from_malloc.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_mutexes_are_reentrant.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_thread_reaper.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/linuxkernel/stdint.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_linuxkernel.c
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_linuxkernel_cfg.c
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_linuxkernel_misc.c
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_mod_init.c
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_platform.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_platform_types.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_thread_map.c
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_thread_map.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_assert.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_atomic_flags.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_build_info.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_cfg.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_cmd.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_ctype.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_dlfcn.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_event.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_event_flags.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_init.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_logging.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_lowlevel_thread.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_mem.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_msgqueue.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_mutex.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_once.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_semaphore.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_stdbool.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_stdint.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_string.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_thread.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_thread_attr.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_timer.h
|
|
create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_types.h
|
|
|
|
--- a/drivers/misc/Kconfig
|
|
+++ b/drivers/misc/Kconfig
|
|
@@ -506,4 +506,5 @@ source "drivers/misc/ti-st/Kconfig"
|
|
source "drivers/misc/lis3lv02d/Kconfig"
|
|
source "drivers/misc/carma/Kconfig"
|
|
source "drivers/misc/altera-stapl/Kconfig"
|
|
+source "drivers/misc/vc04_services/Kconfig"
|
|
endmenu
|
|
--- a/drivers/misc/Makefile
|
|
+++ b/drivers/misc/Makefile
|
|
@@ -49,3 +49,5 @@ obj-y += carma/
|
|
obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
|
|
obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/
|
|
obj-$(CONFIG_MAX8997_MUIC) += max8997-muic.o
|
|
+obj-y += vc04_services/
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/Kconfig
|
|
@@ -0,0 +1,7 @@
|
|
+config BCM2708_VCHIQ
|
|
+ tristate "Videocore VCHIQ"
|
|
+ depends on MACH_BCM2708
|
|
+ default y
|
|
+ help
|
|
+ Helper for communication for VideoCore.
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/Makefile
|
|
@@ -0,0 +1,19 @@
|
|
+obj-$(CONFIG_BCM2708_VCHIQ) += vchiq.o
|
|
+
|
|
+vchiq-objs := \
|
|
+ interface/vchiq_arm/vchiq_core.o \
|
|
+ interface/vchiq_arm/vchiq_arm.o \
|
|
+ interface/vchiq_arm/vchiq_kern_lib.o \
|
|
+ interface/vchiq_arm/vchiq_2835_arm.o \
|
|
+ interface/vcos/linuxkernel/vcos_linuxkernel.o \
|
|
+ interface/vcos/linuxkernel/vcos_thread_map.o \
|
|
+ interface/vcos/linuxkernel/vcos_linuxkernel_cfg.o \
|
|
+ interface/vcos/generic/vcos_generic_event_flags.o \
|
|
+ interface/vcos/generic/vcos_logcat.o \
|
|
+ interface/vcos/generic/vcos_mem_from_malloc.o \
|
|
+ interface/vcos/generic/vcos_cmd.o
|
|
+
|
|
+EXTRA_CFLAGS += -DVCOS_VERIFY_BKPTS=1 -Idrivers/misc/vc04_services -Idrivers/misc/vc04_services/interface/vcos/linuxkernel
|
|
+
|
|
+
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vchi/vchi_mh.h
|
|
@@ -0,0 +1,19 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2010 Broadcom Europe Limited. All rights reserved.
|
|
+
|
|
+Project : vchi
|
|
+Module : vchi
|
|
+
|
|
+FILE DESCRIPTION:
|
|
+Definitions for memory handle types.
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCHI_MH_H_
|
|
+#define VCHI_MH_H_
|
|
+
|
|
+#include <interface/vcos/vcos.h>
|
|
+
|
|
+typedef int32_t VCHI_MEM_HANDLE_T;
|
|
+#define VCHI_MEM_HANDLE_INVALID 0
|
|
+
|
|
+#endif
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq.h
|
|
@@ -0,0 +1,27 @@
|
|
+/*
|
|
+ * Copyright (c) 2010-2011 Broadcom. All rights reserved.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
+ */
|
|
+
|
|
+#ifndef VCHIQ_VCHIQ_H
|
|
+#define VCHIQ_VCHIQ_H
|
|
+
|
|
+#include "vchiq_if.h"
|
|
+#include "vchiq_util.h"
|
|
+#include "interface/vcos/vcos.h"
|
|
+
|
|
+#endif
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835.h
|
|
@@ -0,0 +1,27 @@
|
|
+/*
|
|
+ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
+ */
|
|
+
|
|
+#ifndef VCHIQ_2835_H
|
|
+#define VCHIQ_2835_H
|
|
+
|
|
+#include "vchiq_pagelist.h"
|
|
+
|
|
+#define VCHIQ_PLATFORM_FRAGMENTS_OFFSET_IDX 0
|
|
+#define VCHIQ_PLATFORM_FRAGMENTS_COUNT_IDX 1
|
|
+
|
|
+#endif /* VCHIQ_2835_H */
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
|
|
@@ -0,0 +1,487 @@
|
|
+/*
|
|
+ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
+ */
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/irq.h>
|
|
+#include <linux/pagemap.h>
|
|
+#include <linux/dma-mapping.h>
|
|
+#include <linux/version.h>
|
|
+#include <asm/pgtable.h>
|
|
+#include <asm/io.h>
|
|
+#include <asm/uaccess.h>
|
|
+
|
|
+#include <mach/irqs.h>
|
|
+
|
|
+#include <mach/platform.h>
|
|
+#include <mach/vcio.h>
|
|
+
|
|
+#define TOTAL_SLOTS (VCHIQ_SLOT_ZERO_SLOTS + 2 * 32)
|
|
+
|
|
+#define VCHIQ_DOORBELL_IRQ IRQ_ARM_DOORBELL_0
|
|
+#define VCHIQ_ARM_ADDRESS(x) __virt_to_bus(x)
|
|
+
|
|
+#include "vchiq_arm.h"
|
|
+#include "vchiq_2835.h"
|
|
+
|
|
+#define MAX_FRAGMENTS (VCHIQ_NUM_CURRENT_BULKS * 2)
|
|
+
|
|
+#define VCOS_LOG_CATEGORY (&vchiq_arm_log_category)
|
|
+
|
|
+static char *g_slot_mem;
|
|
+static int g_slot_mem_size;
|
|
+dma_addr_t g_slot_phys;
|
|
+static FRAGMENTS_T *g_fragments_base;
|
|
+static FRAGMENTS_T *g_free_fragments;
|
|
+struct semaphore g_free_fragments_sema;
|
|
+
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
|
|
+static DEFINE_SEMAPHORE(g_free_fragments_mutex);
|
|
+#else
|
|
+static DECLARE_MUTEX(g_free_fragments_mutex);
|
|
+#endif
|
|
+
|
|
+static irqreturn_t
|
|
+vchiq_doorbell_irq(int irq, void *dev_id);
|
|
+
|
|
+static int
|
|
+create_pagelist(char __user *buf, size_t count, unsigned short type,
|
|
+ struct task_struct *task, PAGELIST_T ** ppagelist);
|
|
+
|
|
+static void
|
|
+free_pagelist(PAGELIST_T *pagelist, int actual);
|
|
+
|
|
+int __init
|
|
+vchiq_platform_vcos_init(void)
|
|
+{
|
|
+ return (vcos_init() == VCOS_SUCCESS) ? 0 : -EINVAL;
|
|
+}
|
|
+
|
|
+int __init
|
|
+vchiq_platform_init(VCHIQ_STATE_T *state)
|
|
+{
|
|
+ VCHIQ_SLOT_ZERO_T *vchiq_slot_zero;
|
|
+ int frag_mem_size;
|
|
+ int err;
|
|
+ int i;
|
|
+
|
|
+ /* Allocate space for the channels in coherent memory */
|
|
+ g_slot_mem_size = PAGE_ALIGN(TOTAL_SLOTS * VCHIQ_SLOT_SIZE);
|
|
+ frag_mem_size = PAGE_ALIGN(sizeof(FRAGMENTS_T) * MAX_FRAGMENTS);
|
|
+
|
|
+ g_slot_mem = dma_alloc_coherent(NULL, g_slot_mem_size + frag_mem_size,
|
|
+ &g_slot_phys, GFP_ATOMIC);
|
|
+
|
|
+ if (!g_slot_mem) {
|
|
+ vcos_log_error("Unable to allocate channel memory");
|
|
+ err = -ENOMEM;
|
|
+ goto failed_alloc;
|
|
+ }
|
|
+
|
|
+ vcos_assert(((int)g_slot_mem & (PAGE_SIZE - 1)) == 0);
|
|
+
|
|
+ vchiq_slot_zero = vchiq_init_slots(g_slot_mem, g_slot_mem_size);
|
|
+ if (!vchiq_slot_zero)
|
|
+ {
|
|
+ err = -EINVAL;
|
|
+ goto failed_init_slots;
|
|
+ }
|
|
+
|
|
+ vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_OFFSET_IDX] = (int)g_slot_phys + g_slot_mem_size;
|
|
+ vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_COUNT_IDX] = MAX_FRAGMENTS;
|
|
+
|
|
+ g_fragments_base = (FRAGMENTS_T *)(g_slot_mem + g_slot_mem_size);
|
|
+ g_slot_mem_size += frag_mem_size;
|
|
+
|
|
+ g_free_fragments = g_fragments_base;
|
|
+ for (i = 0; i < (MAX_FRAGMENTS - 1); i++) {
|
|
+ *(FRAGMENTS_T **) & g_fragments_base[i] =
|
|
+ &g_fragments_base[i + 1];
|
|
+ }
|
|
+ *(FRAGMENTS_T **) & g_fragments_base[i] = NULL;
|
|
+ sema_init(&g_free_fragments_sema, MAX_FRAGMENTS);
|
|
+
|
|
+ if (vchiq_init_state(state, vchiq_slot_zero, 0/*slave*/) !=
|
|
+ VCHIQ_SUCCESS)
|
|
+ {
|
|
+ err = -EINVAL;
|
|
+ goto failed_vchiq_init;
|
|
+ }
|
|
+
|
|
+ err = request_irq(VCHIQ_DOORBELL_IRQ, vchiq_doorbell_irq,
|
|
+ IRQF_SAMPLE_RANDOM | IRQF_IRQPOLL, "VCHIQ doorbell",
|
|
+ state);
|
|
+ if (err < 0)
|
|
+ {
|
|
+ printk( KERN_ERR "%s: failed to register irq=%d err=%d\n", __func__,
|
|
+ VCHIQ_DOORBELL_IRQ, err );
|
|
+ goto failed_request_irq;
|
|
+ }
|
|
+
|
|
+ /* Send the base address of the slots to VideoCore */
|
|
+
|
|
+ dsb(); /* Ensure all writes have completed */
|
|
+
|
|
+ bcm_mailbox_write(MBOX_CHAN_VCHIQ, (unsigned int)g_slot_phys);
|
|
+
|
|
+ vcos_log_info("vchiq_init - done (slots %x, phys %x)",
|
|
+ (unsigned int)vchiq_slot_zero, g_slot_phys);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+failed_request_irq:
|
|
+failed_vchiq_init:
|
|
+failed_init_slots:
|
|
+ dma_free_coherent(NULL, g_slot_mem_size, g_slot_mem, g_slot_phys);
|
|
+
|
|
+failed_alloc:
|
|
+ return err;
|
|
+}
|
|
+
|
|
+void __exit
|
|
+vchiq_platform_exit(VCHIQ_STATE_T *state)
|
|
+{
|
|
+ free_irq(VCHIQ_DOORBELL_IRQ, state);
|
|
+ dma_free_coherent(NULL, g_slot_mem_size,
|
|
+ g_slot_mem, g_slot_phys);
|
|
+}
|
|
+
|
|
+void
|
|
+remote_event_signal(REMOTE_EVENT_T *event)
|
|
+{
|
|
+ event->fired = 1;
|
|
+
|
|
+ /* The test on the next line also ensures the write on the previous line
|
|
+ has completed */
|
|
+
|
|
+ if (event->armed) {
|
|
+ /* trigger vc interrupt */
|
|
+ dsb(); /* data barrier operation */
|
|
+
|
|
+ writel(0, __io_address(ARM_0_BELL2));
|
|
+ }
|
|
+}
|
|
+
|
|
+int
|
|
+vchiq_copy_from_user(void *dst, const void *src, int size)
|
|
+{
|
|
+ return copy_from_user(dst, src, size);
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_prepare_bulk_data(VCHIQ_BULK_T *bulk, VCHI_MEM_HANDLE_T memhandle,
|
|
+ void *offset, int size, int dir)
|
|
+{
|
|
+ PAGELIST_T *pagelist;
|
|
+ int ret;
|
|
+
|
|
+ vcos_assert(memhandle == VCHI_MEM_HANDLE_INVALID);
|
|
+
|
|
+ ret = create_pagelist((char __user *)offset, size,
|
|
+ (dir == VCHIQ_BULK_RECEIVE)
|
|
+ ? PAGELIST_READ
|
|
+ : PAGELIST_WRITE,
|
|
+ current,
|
|
+ &pagelist);
|
|
+ if (ret != 0)
|
|
+ return VCHIQ_ERROR;
|
|
+
|
|
+ bulk->handle = memhandle;
|
|
+ bulk->data = VCHIQ_ARM_ADDRESS(pagelist);
|
|
+
|
|
+ /* Store the pagelist address in remote_data, which isn't used by the
|
|
+ slave. */
|
|
+ bulk->remote_data = pagelist;
|
|
+
|
|
+ return VCHIQ_SUCCESS;
|
|
+}
|
|
+
|
|
+void
|
|
+vchiq_complete_bulk(VCHIQ_BULK_T *bulk)
|
|
+{
|
|
+ free_pagelist((PAGELIST_T *)bulk->remote_data, bulk->actual);
|
|
+}
|
|
+
|
|
+void
|
|
+vchiq_transfer_bulk(VCHIQ_BULK_T *bulk)
|
|
+{
|
|
+ /*
|
|
+ * This should only be called on the master (VideoCore) side, but
|
|
+ * provide an implementation to avoid the need for ifdefery.
|
|
+ */
|
|
+ vcos_assert(!"This code should not be called by the ARM on BCM2835");
|
|
+}
|
|
+
|
|
+void
|
|
+vchiq_dump_platform_state(void *dump_context)
|
|
+{
|
|
+ char buf[80];
|
|
+ int len;
|
|
+ len = vcos_snprintf(buf, sizeof(buf),
|
|
+ " Platform: 2835 (VC master)");
|
|
+ vchiq_dump(dump_context, buf, len + 1);
|
|
+}
|
|
+
|
|
+void
|
|
+vchiq_platform_paused(VCHIQ_STATE_T *state)
|
|
+{
|
|
+ vcos_unused(state);
|
|
+ vcos_assert_msg(0, "Suspend/resume not supported");
|
|
+}
|
|
+
|
|
+void
|
|
+vchiq_platform_resumed(VCHIQ_STATE_T *state)
|
|
+{
|
|
+ vcos_unused(state);
|
|
+ vcos_assert_msg(0, "Suspend/resume not supported");
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_use_service(VCHIQ_SERVICE_HANDLE_T handle)
|
|
+{
|
|
+ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle;
|
|
+ if (!service)
|
|
+ return VCHIQ_ERROR;
|
|
+ return VCHIQ_SUCCESS;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_release_service(VCHIQ_SERVICE_HANDLE_T handle)
|
|
+{
|
|
+ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle;
|
|
+ if (!service)
|
|
+ return VCHIQ_ERROR;
|
|
+ return VCHIQ_SUCCESS;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_check_service(VCHIQ_SERVICE_HANDLE_T handle)
|
|
+{
|
|
+ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle;
|
|
+ if (!service)
|
|
+ return VCHIQ_ERROR;
|
|
+ return VCHIQ_SUCCESS;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Local functions
|
|
+ */
|
|
+
|
|
+static irqreturn_t
|
|
+vchiq_doorbell_irq(int irq, void *dev_id)
|
|
+{
|
|
+ VCHIQ_STATE_T *state = dev_id;
|
|
+ irqreturn_t ret = IRQ_NONE;
|
|
+ unsigned int status;
|
|
+
|
|
+ /* Read (and clear) the doorbell */
|
|
+ status = readl(__io_address(ARM_0_BELL0));
|
|
+
|
|
+ if (status & 0x4) { /* Was the doorbell rung? */
|
|
+ remote_event_pollall(state);
|
|
+ ret = IRQ_HANDLED;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/* There is a potential problem with partial cache lines (pages?)
|
|
+ at the ends of the block when reading. If the CPU accessed anything in
|
|
+ the same line (page?) then it may have pulled old data into the cache,
|
|
+ obscuring the new data underneath. We can solve this by transferring the
|
|
+ partial cache lines separately, and allowing the ARM to copy into the
|
|
+ cached area.
|
|
+
|
|
+ N.B. This implementation plays slightly fast and loose with the Linux
|
|
+ driver programming rules, e.g. its use of __virt_to_bus instead of
|
|
+ dma_map_single, but it isn't a multi-platform driver and it benefits
|
|
+ from increased speed as a result.
|
|
+ */
|
|
+
|
|
+static int
|
|
+create_pagelist(char __user *buf, size_t count, unsigned short type,
|
|
+ struct task_struct *task, PAGELIST_T ** ppagelist)
|
|
+{
|
|
+ PAGELIST_T *pagelist;
|
|
+ struct page **pages;
|
|
+ struct page *page;
|
|
+ unsigned long *addrs;
|
|
+ unsigned int num_pages, offset, i;
|
|
+ char *addr, *base_addr, *next_addr;
|
|
+ int run, addridx, actual_pages;
|
|
+
|
|
+ offset = (unsigned int)buf & (PAGE_SIZE - 1);
|
|
+ num_pages = (count + offset + PAGE_SIZE - 1) / PAGE_SIZE;
|
|
+
|
|
+ *ppagelist = NULL;
|
|
+
|
|
+ /* Allocate enough storage to hold the page pointers and the page list */
|
|
+ pagelist = (PAGELIST_T *) kmalloc(sizeof(PAGELIST_T) +
|
|
+ (num_pages * sizeof(unsigned long)) +
|
|
+ (num_pages * sizeof(pages[0])),
|
|
+ GFP_KERNEL);
|
|
+
|
|
+ vcos_log_trace("create_pagelist - %x", (unsigned int)pagelist);
|
|
+ if (!pagelist)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ addrs = pagelist->addrs;
|
|
+ pages = (struct page **)(addrs + num_pages);
|
|
+
|
|
+ down_read(&task->mm->mmap_sem);
|
|
+ actual_pages = get_user_pages(task, task->mm,
|
|
+ (unsigned long)buf & ~(PAGE_SIZE - 1), num_pages,
|
|
+ (type == PAGELIST_READ) /*Write */ , 0 /*Force */ ,
|
|
+ pages, NULL /*vmas */ );
|
|
+ up_read(&task->mm->mmap_sem);
|
|
+
|
|
+ if (actual_pages != num_pages)
|
|
+ {
|
|
+ for (i = 0; i < actual_pages; i++) {
|
|
+ page_cache_release(pages[i]);
|
|
+ }
|
|
+ kfree(pagelist);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ pagelist->length = count;
|
|
+ pagelist->type = type;
|
|
+ pagelist->offset = offset;
|
|
+
|
|
+ /* Group the pages into runs of contiguous pages */
|
|
+
|
|
+ base_addr = VCHIQ_ARM_ADDRESS(page_address(pages[0]));
|
|
+ next_addr = base_addr + PAGE_SIZE;
|
|
+ addridx = 0;
|
|
+ run = 0;
|
|
+
|
|
+ for (i = 1; i < num_pages; i++) {
|
|
+ addr = VCHIQ_ARM_ADDRESS(page_address(pages[i]));
|
|
+ if ((addr == next_addr) && (run < (PAGE_SIZE - 1))) {
|
|
+ next_addr += PAGE_SIZE;
|
|
+ run++;
|
|
+ } else {
|
|
+ addrs[addridx] = (unsigned long)base_addr + run;
|
|
+ addridx++;
|
|
+ base_addr = addr;
|
|
+ next_addr = addr + PAGE_SIZE;
|
|
+ run = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ addrs[addridx] = (unsigned long)base_addr + run;
|
|
+ addridx++;
|
|
+
|
|
+ /* Partial cache lines (fragments) require special measures */
|
|
+ if ((type == PAGELIST_READ) &&
|
|
+ ((pagelist->offset & (CACHE_LINE_SIZE - 1)) ||
|
|
+ ((pagelist->offset + pagelist->length) & (CACHE_LINE_SIZE - 1)))) {
|
|
+ FRAGMENTS_T *fragments;
|
|
+
|
|
+ if (down_interruptible(&g_free_fragments_sema) != 0) {
|
|
+ kfree(pagelist);
|
|
+ return -EINTR;
|
|
+ }
|
|
+
|
|
+ vcos_assert(g_free_fragments != NULL);
|
|
+
|
|
+ down(&g_free_fragments_mutex);
|
|
+ fragments = (FRAGMENTS_T *) g_free_fragments;
|
|
+ vcos_assert(fragments != NULL);
|
|
+ g_free_fragments = *(FRAGMENTS_T **) g_free_fragments;
|
|
+ up(&g_free_fragments_mutex);
|
|
+ pagelist->type =
|
|
+ PAGELIST_READ_WITH_FRAGMENTS + (fragments -
|
|
+ g_fragments_base);
|
|
+ }
|
|
+
|
|
+ for (page = virt_to_page(pagelist);
|
|
+ page <= virt_to_page(addrs + num_pages - 1); page++) {
|
|
+ flush_dcache_page(page);
|
|
+ }
|
|
+
|
|
+ *ppagelist = pagelist;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+free_pagelist(PAGELIST_T *pagelist, int actual)
|
|
+{
|
|
+ struct page **pages;
|
|
+ unsigned int num_pages, i;
|
|
+
|
|
+ vcos_log_trace("free_pagelist - %x, %d", (unsigned int)pagelist, actual);
|
|
+
|
|
+ num_pages =
|
|
+ (pagelist->length + pagelist->offset + PAGE_SIZE - 1) / PAGE_SIZE;
|
|
+
|
|
+ pages = (struct page **)(pagelist->addrs + num_pages);
|
|
+
|
|
+ /* Deal with any partial cache lines (fragments) */
|
|
+ if (pagelist->type >= PAGELIST_READ_WITH_FRAGMENTS) {
|
|
+ FRAGMENTS_T *fragments =
|
|
+ g_fragments_base + (pagelist->type -
|
|
+ PAGELIST_READ_WITH_FRAGMENTS);
|
|
+ int head_bytes, tail_bytes;
|
|
+
|
|
+ if (actual >= 0)
|
|
+ {
|
|
+ if ((head_bytes = (CACHE_LINE_SIZE - pagelist->offset) & (CACHE_LINE_SIZE - 1)) != 0) {
|
|
+ if (head_bytes > actual)
|
|
+ head_bytes = actual;
|
|
+
|
|
+ memcpy((char *)page_address(pages[0]) +
|
|
+ pagelist->offset, fragments->headbuf,
|
|
+ head_bytes);
|
|
+ }
|
|
+ if ((head_bytes < actual) &&
|
|
+ (tail_bytes =
|
|
+ (pagelist->offset + actual) & (CACHE_LINE_SIZE -
|
|
+ 1)) != 0) {
|
|
+ memcpy((char *)page_address(pages[num_pages - 1]) +
|
|
+ ((pagelist->offset + actual) & (PAGE_SIZE -
|
|
+ 1) & ~(CACHE_LINE_SIZE - 1)),
|
|
+ fragments->tailbuf, tail_bytes);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ down(&g_free_fragments_mutex);
|
|
+ *(FRAGMENTS_T **) fragments = g_free_fragments;
|
|
+ g_free_fragments = fragments;
|
|
+ up(&g_free_fragments_mutex);
|
|
+ up(&g_free_fragments_sema);
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < num_pages; i++) {
|
|
+ if (pagelist->type != PAGELIST_WRITE)
|
|
+ set_page_dirty(pages[i]);
|
|
+ page_cache_release(pages[i]);
|
|
+ }
|
|
+
|
|
+ kfree(pagelist);
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_platform_suspend(VCHIQ_STATE_T *state)
|
|
+{
|
|
+ vcos_unused(state);
|
|
+ return VCHIQ_ERROR;
|
|
+}
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c
|
|
@@ -0,0 +1,1293 @@
|
|
+/*
|
|
+ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
+ */
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/cdev.h>
|
|
+#include <linux/fs.h>
|
|
+#include <linux/device.h>
|
|
+
|
|
+#include "vchiq_core.h"
|
|
+#include "vchiq_ioctl.h"
|
|
+#include "vchiq_arm.h"
|
|
+
|
|
+#define DEVICE_NAME "vchiq"
|
|
+
|
|
+/* Override the default prefix, which would be vchiq_arm (from the filename) */
|
|
+#undef MODULE_PARAM_PREFIX
|
|
+#define MODULE_PARAM_PREFIX DEVICE_NAME "."
|
|
+
|
|
+#define VCHIQ_MINOR 0
|
|
+
|
|
+/* Some per-instance constants */
|
|
+#define MAX_COMPLETIONS 16
|
|
+#define MAX_SERVICES 64
|
|
+#define MAX_ELEMENTS 8
|
|
+#define MSG_QUEUE_SIZE 64
|
|
+
|
|
+#define VCOS_LOG_CATEGORY (&vchiq_arm_log_category)
|
|
+
|
|
+typedef struct client_service_struct {
|
|
+ VCHIQ_SERVICE_T *service;
|
|
+ void *userdata;
|
|
+ VCHIQ_INSTANCE_T instance;
|
|
+ int handle;
|
|
+ int is_vchi;
|
|
+ volatile int dequeue_pending;
|
|
+ volatile int message_available_pos;
|
|
+ volatile int msg_insert;
|
|
+ volatile int msg_remove;
|
|
+ VCOS_EVENT_T insert_event;
|
|
+ VCOS_EVENT_T remove_event;
|
|
+ VCHIQ_HEADER_T *msg_queue[MSG_QUEUE_SIZE];
|
|
+} USER_SERVICE_T;
|
|
+
|
|
+struct vchiq_instance_struct {
|
|
+ VCHIQ_STATE_T *state;
|
|
+ VCHIQ_COMPLETION_DATA_T completions[MAX_COMPLETIONS];
|
|
+ volatile int completion_insert;
|
|
+ volatile int completion_remove;
|
|
+ VCOS_EVENT_T insert_event;
|
|
+ VCOS_EVENT_T remove_event;
|
|
+
|
|
+ USER_SERVICE_T services[MAX_SERVICES];
|
|
+
|
|
+ int connected;
|
|
+ int closing;
|
|
+ int pid;
|
|
+ int mark;
|
|
+};
|
|
+
|
|
+typedef struct dump_context_struct
|
|
+{
|
|
+ char __user *buf;
|
|
+ size_t actual;
|
|
+ size_t space;
|
|
+ loff_t offset;
|
|
+} DUMP_CONTEXT_T;
|
|
+
|
|
+VCOS_LOG_CAT_T vchiq_arm_log_category;
|
|
+
|
|
+static struct cdev vchiq_cdev;
|
|
+static dev_t vchiq_devid;
|
|
+static VCHIQ_STATE_T g_state;
|
|
+static struct class *vchiq_class;
|
|
+static struct device *vchiq_dev;
|
|
+
|
|
+static const char *ioctl_names[] =
|
|
+{
|
|
+ "CONNECT",
|
|
+ "SHUTDOWN",
|
|
+ "CREATE_SERVICE",
|
|
+ "REMOVE_SERVICE",
|
|
+ "QUEUE_MESSAGE",
|
|
+ "QUEUE_BULK_TRANSMIT",
|
|
+ "QUEUE_BULK_RECEIVE",
|
|
+ "AWAIT_COMPLETION",
|
|
+ "DEQUEUE_MESSAGE",
|
|
+ "GET_CLIENT_ID",
|
|
+ "GET_CONFIG",
|
|
+ "CLOSE_SERVICE",
|
|
+ "USE_SERVICE",
|
|
+ "RELEASE_SERIVCE"
|
|
+};
|
|
+
|
|
+VCOS_LOG_LEVEL_T vchiq_default_arm_log_level = VCOS_LOG_WARN;
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* find_service_by_handle
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+static inline USER_SERVICE_T *find_service_by_handle(
|
|
+ VCHIQ_INSTANCE_T instance, int handle )
|
|
+{
|
|
+ USER_SERVICE_T *user_service;
|
|
+
|
|
+ if (( handle >= 0 )
|
|
+ && ( handle < MAX_SERVICES ))
|
|
+ {
|
|
+ user_service = &instance->services[ handle ];
|
|
+
|
|
+ if ( user_service->service != NULL )
|
|
+ {
|
|
+ return user_service;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* find_avail_service_handle
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+static inline USER_SERVICE_T *find_avail_service_handle(
|
|
+ VCHIQ_INSTANCE_T instance)
|
|
+{
|
|
+ int handle;
|
|
+
|
|
+ for ( handle = 0; handle < MAX_SERVICES; handle++ )
|
|
+ {
|
|
+ if ( instance->services[handle].service == NULL )
|
|
+ {
|
|
+ instance->services[handle].instance = instance;
|
|
+ instance->services[handle].handle = handle;
|
|
+
|
|
+ return &instance->services[handle];
|
|
+ }
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* add_completion
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+static VCHIQ_STATUS_T
|
|
+add_completion(VCHIQ_INSTANCE_T instance, VCHIQ_REASON_T reason,
|
|
+ VCHIQ_HEADER_T *header, USER_SERVICE_T *service, void *bulk_userdata)
|
|
+{
|
|
+ VCHIQ_COMPLETION_DATA_T *completion;
|
|
+ DEBUG_INITIALISE(g_state.local)
|
|
+
|
|
+ while (instance->completion_insert ==
|
|
+ (instance->completion_remove + MAX_COMPLETIONS)) {
|
|
+ /* Out of space - wait for the client */
|
|
+ DEBUG_TRACE(SERVICE_CALLBACK_LINE);
|
|
+ vcos_log_trace("add_completion - completion queue full");
|
|
+ DEBUG_COUNT(COMPLETION_QUEUE_FULL_COUNT);
|
|
+ if (vcos_event_wait(&instance->remove_event) != VCOS_SUCCESS) {
|
|
+ vcos_log_info("service_callback interrupted");
|
|
+ return VCHIQ_RETRY;
|
|
+ } else if (instance->closing) {
|
|
+ vcos_log_info("service_callback closing");
|
|
+ return VCHIQ_ERROR;
|
|
+ }
|
|
+ DEBUG_TRACE(SERVICE_CALLBACK_LINE);
|
|
+ }
|
|
+
|
|
+ completion =
|
|
+ &instance->
|
|
+ completions[instance->completion_insert & (MAX_COMPLETIONS - 1)];
|
|
+
|
|
+ completion->header = header;
|
|
+ completion->reason = reason;
|
|
+ completion->service_userdata = service;
|
|
+ completion->bulk_userdata = bulk_userdata;
|
|
+
|
|
+ /* A write barrier is needed here to ensure that the entire completion
|
|
+ record is written out before the insert point. */
|
|
+ vcos_wmb(&completion->bulk_userdata);
|
|
+
|
|
+ if (reason == VCHIQ_MESSAGE_AVAILABLE)
|
|
+ service->message_available_pos = instance->completion_insert;
|
|
+ instance->completion_insert++;
|
|
+
|
|
+ vcos_event_signal(&instance->insert_event);
|
|
+
|
|
+ return VCHIQ_SUCCESS;
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* service_callback
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+static VCHIQ_STATUS_T
|
|
+service_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header,
|
|
+ VCHIQ_SERVICE_HANDLE_T handle, void *bulk_userdata)
|
|
+{
|
|
+ /* How do we ensure the callback goes to the right client?
|
|
+ The service_user data points to a USER_SERVICE_T record containing the
|
|
+ original callback and the user state structure, which contains a circular
|
|
+ buffer for completion records.
|
|
+ */
|
|
+ USER_SERVICE_T *service =
|
|
+ (USER_SERVICE_T *) VCHIQ_GET_SERVICE_USERDATA(handle);
|
|
+ VCHIQ_INSTANCE_T instance = service->instance;
|
|
+ DEBUG_INITIALISE(g_state.local)
|
|
+
|
|
+ DEBUG_TRACE(SERVICE_CALLBACK_LINE);
|
|
+ vcos_log_trace
|
|
+ ("service_callback - service %lx(%d), reason %d, header %lx, "
|
|
+ "instance %lx, bulk_userdata %lx",
|
|
+ (unsigned long)service, ((VCHIQ_SERVICE_T *) handle)->localport,
|
|
+ reason, (unsigned long)header,
|
|
+ (unsigned long)instance, (unsigned long)bulk_userdata);
|
|
+
|
|
+ if (!instance || instance->closing) {
|
|
+ return VCHIQ_SUCCESS;
|
|
+ }
|
|
+
|
|
+ if (header && service->is_vchi)
|
|
+ {
|
|
+ while (service->msg_insert == (service->msg_remove + MSG_QUEUE_SIZE))
|
|
+ {
|
|
+ DEBUG_TRACE(SERVICE_CALLBACK_LINE);
|
|
+ DEBUG_COUNT(MSG_QUEUE_FULL_COUNT);
|
|
+ vcos_log_trace("service_callback - msg queue full");
|
|
+ /* If there is no MESSAGE_AVAILABLE in the completion queue, add one */
|
|
+ if ((service->message_available_pos - instance->completion_remove) < 0)
|
|
+ {
|
|
+ VCHIQ_STATUS_T status;
|
|
+ vcos_log_warn("Inserting extra MESSAGE_AVAILABLE");
|
|
+ DEBUG_TRACE(SERVICE_CALLBACK_LINE);
|
|
+ status = add_completion(instance, reason, NULL, service, bulk_userdata);
|
|
+ if (status != VCHIQ_SUCCESS)
|
|
+ {
|
|
+ DEBUG_TRACE(SERVICE_CALLBACK_LINE);
|
|
+ return status;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ DEBUG_TRACE(SERVICE_CALLBACK_LINE);
|
|
+ if (vcos_event_wait(&service->remove_event) != VCOS_SUCCESS) {
|
|
+ vcos_log_info("service_callback interrupted");
|
|
+ DEBUG_TRACE(SERVICE_CALLBACK_LINE);
|
|
+ return VCHIQ_RETRY;
|
|
+ } else if (instance->closing) {
|
|
+ vcos_log_info("service_callback closing");
|
|
+ DEBUG_TRACE(SERVICE_CALLBACK_LINE);
|
|
+ return VCHIQ_ERROR;
|
|
+ }
|
|
+ DEBUG_TRACE(SERVICE_CALLBACK_LINE);
|
|
+ }
|
|
+
|
|
+ service->msg_queue[service->msg_insert & (MSG_QUEUE_SIZE - 1)] =
|
|
+ header;
|
|
+
|
|
+ /* A write memory barrier is needed to ensure that the store of header
|
|
+ is completed before the insertion point is updated */
|
|
+ vcos_wmb(&service->msg_queue[service->msg_insert & (MSG_QUEUE_SIZE - 1)]);
|
|
+
|
|
+ service->msg_insert++;
|
|
+ vcos_event_signal(&service->insert_event);
|
|
+
|
|
+ /* If there is a thread waiting in DEQUEUE_MESSAGE, or if
|
|
+ there is a MESSAGE_AVAILABLE in the completion queue then
|
|
+ bypass the completion queue. */
|
|
+ if (((service->message_available_pos - instance->completion_remove) >= 0) ||
|
|
+ service->dequeue_pending)
|
|
+ {
|
|
+ DEBUG_TRACE(SERVICE_CALLBACK_LINE);
|
|
+ service->dequeue_pending = 0;
|
|
+ return VCHIQ_SUCCESS;
|
|
+ }
|
|
+
|
|
+ header = NULL;
|
|
+ }
|
|
+ DEBUG_TRACE(SERVICE_CALLBACK_LINE);
|
|
+
|
|
+ return add_completion(instance, reason, header, service, bulk_userdata);
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* vchiq_ioctl
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+static long
|
|
+vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
+{
|
|
+ VCHIQ_INSTANCE_T instance = file->private_data;
|
|
+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
|
|
+ long ret = 0;
|
|
+ int i, rc;
|
|
+ DEBUG_INITIALISE(g_state.local)
|
|
+
|
|
+ vcos_log_trace("vchiq_ioctl - instance %x, cmd %s, arg %lx",
|
|
+ (unsigned int)instance,
|
|
+ ((_IOC_TYPE(cmd) == VCHIQ_IOC_MAGIC) && (_IOC_NR(cmd) <= VCHIQ_IOC_MAX)) ?
|
|
+ ioctl_names[_IOC_NR(cmd)] : "<invalid>", arg);
|
|
+
|
|
+ switch (cmd) {
|
|
+ case VCHIQ_IOC_SHUTDOWN:
|
|
+ if (!instance->connected)
|
|
+ break;
|
|
+
|
|
+ /* Remove all services */
|
|
+ for (i = 0; i < MAX_SERVICES; i++) {
|
|
+ USER_SERVICE_T *service = &instance->services[i];
|
|
+ if (service->service != NULL) {
|
|
+ status = vchiq_remove_service(&service->service->base);
|
|
+ if (status != VCHIQ_SUCCESS)
|
|
+ break;
|
|
+ service->service = NULL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (status == VCHIQ_SUCCESS) {
|
|
+ /* Wake the completion thread and ask it to exit */
|
|
+ instance->closing = 1;
|
|
+ vcos_event_signal(&instance->insert_event);
|
|
+ }
|
|
+
|
|
+ break;
|
|
+
|
|
+ case VCHIQ_IOC_CONNECT:
|
|
+ if (instance->connected) {
|
|
+ ret = -EINVAL;
|
|
+ break;
|
|
+ }
|
|
+ if ((rc=vcos_mutex_lock(&instance->state->mutex)) != VCOS_SUCCESS) {
|
|
+ vcos_log_error("vchiq: connect: could not lock mutex for state %d: %d",
|
|
+ instance->state->id, rc);
|
|
+ ret = -EINTR;
|
|
+ break;
|
|
+ }
|
|
+ status = vchiq_connect_internal(instance->state, instance);
|
|
+ vcos_mutex_unlock(&instance->state->mutex);
|
|
+
|
|
+ if (status == VCHIQ_SUCCESS)
|
|
+ instance->connected = 1;
|
|
+ else
|
|
+ vcos_log_error("vchiq: could not connect: %d", status);
|
|
+ break;
|
|
+
|
|
+ case VCHIQ_IOC_CREATE_SERVICE:
|
|
+ {
|
|
+ VCHIQ_CREATE_SERVICE_T args;
|
|
+ VCHIQ_SERVICE_T *service = NULL;
|
|
+ USER_SERVICE_T *user_service = NULL;
|
|
+ void *userdata;
|
|
+ int srvstate;
|
|
+
|
|
+ if (copy_from_user
|
|
+ (&args, (const void __user *)arg,
|
|
+ sizeof(args)) != 0) {
|
|
+ ret = -EFAULT;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < MAX_SERVICES; i++) {
|
|
+ if (instance->services[i].service == NULL) {
|
|
+ user_service = &instance->services[i];
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!user_service) {
|
|
+ ret = -EMFILE;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (args.is_open) {
|
|
+ if (instance->connected)
|
|
+ srvstate = VCHIQ_SRVSTATE_OPENING;
|
|
+ else {
|
|
+ ret = -ENOTCONN;
|
|
+ break;
|
|
+ }
|
|
+ } else {
|
|
+ srvstate =
|
|
+ instance->connected ?
|
|
+ VCHIQ_SRVSTATE_LISTENING :
|
|
+ VCHIQ_SRVSTATE_HIDDEN;
|
|
+ }
|
|
+
|
|
+ vcos_mutex_lock(&instance->state->mutex);
|
|
+
|
|
+ userdata = args.params.userdata;
|
|
+ args.params.callback = service_callback;
|
|
+ args.params.userdata = user_service;
|
|
+ service =
|
|
+ vchiq_add_service_internal(instance->state,
|
|
+ &args.params, srvstate,
|
|
+ instance);
|
|
+
|
|
+ vcos_mutex_unlock(&instance->state->mutex);
|
|
+
|
|
+ if (service != NULL) {
|
|
+ user_service->service = service;
|
|
+ user_service->userdata = userdata;
|
|
+ user_service->instance = instance;
|
|
+ user_service->handle = i;
|
|
+ user_service->is_vchi = args.is_vchi;
|
|
+ user_service->dequeue_pending = 0;
|
|
+ user_service->message_available_pos = instance->completion_remove - 1;
|
|
+ user_service->msg_insert = 0;
|
|
+ user_service->msg_remove = 0;
|
|
+ vcos_event_create(&user_service->insert_event, "insert_event");
|
|
+ vcos_event_create(&user_service->remove_event, "remove_event");
|
|
+
|
|
+ if (args.is_open) {
|
|
+ status =
|
|
+ vchiq_open_service_internal
|
|
+ (service, instance->pid);
|
|
+ if (status != VCHIQ_SUCCESS) {
|
|
+ vchiq_remove_service
|
|
+ (&service->base);
|
|
+ ret =
|
|
+ (status ==
|
|
+ VCHIQ_RETRY) ? -EINTR :
|
|
+ -EIO;
|
|
+ user_service->service = NULL;
|
|
+ user_service->instance = NULL;
|
|
+ vcos_event_delete(&user_service->insert_event);
|
|
+ vcos_event_delete(&user_service->remove_event);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (copy_to_user((void __user *)
|
|
+ &(((VCHIQ_CREATE_SERVICE_T __user
|
|
+ *) arg)->handle),
|
|
+ (const void *)&user_service->
|
|
+ handle,
|
|
+ sizeof(user_service->
|
|
+ handle)) != 0)
|
|
+ ret = -EFAULT;
|
|
+ } else {
|
|
+ ret = -EEXIST;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case VCHIQ_IOC_CLOSE_SERVICE:
|
|
+ {
|
|
+ USER_SERVICE_T *user_service;
|
|
+ int handle = (int)arg;
|
|
+
|
|
+ user_service = find_service_by_handle(instance, handle);
|
|
+ if (user_service != NULL)
|
|
+ {
|
|
+ int is_server = (user_service->service->public_fourcc != VCHIQ_FOURCC_INVALID);
|
|
+
|
|
+ status =
|
|
+ vchiq_close_service(&user_service->service->base);
|
|
+ if ((status == VCHIQ_SUCCESS) && !is_server)
|
|
+ {
|
|
+ vcos_event_delete(&user_service->insert_event);
|
|
+ vcos_event_delete(&user_service->remove_event);
|
|
+ user_service->service = NULL;
|
|
+ }
|
|
+ } else
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case VCHIQ_IOC_REMOVE_SERVICE:
|
|
+ {
|
|
+ USER_SERVICE_T *user_service;
|
|
+ int handle = (int)arg;
|
|
+
|
|
+ user_service = find_service_by_handle(instance, handle);
|
|
+ if (user_service != NULL)
|
|
+ {
|
|
+ status =
|
|
+ vchiq_remove_service(&user_service->service->base);
|
|
+ if (status == VCHIQ_SUCCESS)
|
|
+ {
|
|
+ vcos_event_delete(&user_service->insert_event);
|
|
+ vcos_event_delete(&user_service->remove_event);
|
|
+ user_service->service = NULL;
|
|
+ }
|
|
+ } else
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case VCHIQ_IOC_USE_SERVICE:
|
|
+ case VCHIQ_IOC_RELEASE_SERVICE:
|
|
+ {
|
|
+ USER_SERVICE_T *user_service;
|
|
+ int handle = (int)arg;
|
|
+
|
|
+ user_service = find_service_by_handle(instance, handle);
|
|
+ if (user_service != NULL)
|
|
+ {
|
|
+ status = (cmd == VCHIQ_IOC_USE_SERVICE) ? vchiq_use_service(&user_service->service->base) : vchiq_release_service(&user_service->service->base);
|
|
+ if (status != VCHIQ_SUCCESS)
|
|
+ {
|
|
+ ret = -EINVAL; // ???
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case VCHIQ_IOC_QUEUE_MESSAGE:
|
|
+ {
|
|
+ VCHIQ_QUEUE_MESSAGE_T args;
|
|
+ USER_SERVICE_T *user_service;
|
|
+
|
|
+ if (copy_from_user
|
|
+ (&args, (const void __user *)arg,
|
|
+ sizeof(args)) != 0) {
|
|
+ ret = -EFAULT;
|
|
+ break;
|
|
+ }
|
|
+ user_service = find_service_by_handle(instance, args.handle);
|
|
+ if ((user_service != NULL) && (args.count <= MAX_ELEMENTS))
|
|
+ {
|
|
+ /* Copy elements into kernel space */
|
|
+ VCHIQ_ELEMENT_T elements[MAX_ELEMENTS];
|
|
+ if (copy_from_user
|
|
+ (elements, args.elements,
|
|
+ args.count * sizeof(VCHIQ_ELEMENT_T)) == 0)
|
|
+ status =
|
|
+ vchiq_queue_message
|
|
+ (&user_service->service->base,
|
|
+ elements, args.count);
|
|
+ else
|
|
+ ret = -EFAULT;
|
|
+ } else {
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case VCHIQ_IOC_QUEUE_BULK_TRANSMIT:
|
|
+ case VCHIQ_IOC_QUEUE_BULK_RECEIVE:
|
|
+ {
|
|
+ VCHIQ_QUEUE_BULK_TRANSFER_T args;
|
|
+ USER_SERVICE_T *user_service;
|
|
+ VCHIQ_BULK_DIR_T dir =
|
|
+ (cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT) ?
|
|
+ VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE;
|
|
+
|
|
+ if (copy_from_user
|
|
+ (&args, (const void __user *)arg,
|
|
+ sizeof(args)) != 0) {
|
|
+ ret = -EFAULT;
|
|
+ break;
|
|
+ }
|
|
+ user_service = find_service_by_handle(instance, args.handle);
|
|
+ if (user_service != NULL)
|
|
+ {
|
|
+ status =
|
|
+ vchiq_bulk_transfer
|
|
+ ((VCHIQ_SERVICE_T *)user_service->service,
|
|
+ VCHI_MEM_HANDLE_INVALID,
|
|
+ args.data, args.size,
|
|
+ args.userdata, args.mode,
|
|
+ dir);
|
|
+ } else {
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case VCHIQ_IOC_AWAIT_COMPLETION:
|
|
+ {
|
|
+ VCHIQ_AWAIT_COMPLETION_T args;
|
|
+
|
|
+ DEBUG_TRACE(AWAIT_COMPLETION_LINE);
|
|
+ if (!instance->connected) {
|
|
+ ret = -ENOTCONN;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (copy_from_user
|
|
+ (&args, (const void __user *)arg,
|
|
+ sizeof(args)) != 0) {
|
|
+ ret = -EFAULT;
|
|
+ break;
|
|
+ }
|
|
+ DEBUG_TRACE(AWAIT_COMPLETION_LINE);
|
|
+ while ((instance->completion_remove ==
|
|
+ instance->completion_insert)
|
|
+ && !instance->closing) {
|
|
+ DEBUG_TRACE(AWAIT_COMPLETION_LINE);
|
|
+ if (vcos_event_wait(&instance->insert_event) !=
|
|
+ VCOS_SUCCESS) {
|
|
+ DEBUG_TRACE(AWAIT_COMPLETION_LINE);
|
|
+ vcos_log_info
|
|
+ ("AWAIT_COMPLETION interrupted");
|
|
+ ret = -EINTR;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ DEBUG_TRACE(AWAIT_COMPLETION_LINE);
|
|
+
|
|
+ /* A read memory barrier is needed to stop prefetch of a stale
|
|
+ completion record */
|
|
+ vcos_rmb();
|
|
+
|
|
+ if (ret == 0) {
|
|
+ int msgbufcount = args.msgbufcount;
|
|
+ for (ret = 0; ret < args.count; ret++) {
|
|
+ VCHIQ_COMPLETION_DATA_T *completion;
|
|
+ USER_SERVICE_T *service;
|
|
+ VCHIQ_HEADER_T *header;
|
|
+ if (instance->completion_remove ==
|
|
+ instance->completion_insert)
|
|
+ break;
|
|
+ completion =
|
|
+ &instance->
|
|
+ completions
|
|
+ [instance->completion_remove &
|
|
+ (MAX_COMPLETIONS - 1)];
|
|
+
|
|
+ service = (USER_SERVICE_T *)completion->service_userdata;
|
|
+ completion->service_userdata = service->userdata;
|
|
+
|
|
+ header = completion->header;
|
|
+ if (header)
|
|
+ {
|
|
+ void __user *msgbuf;
|
|
+ int msglen;
|
|
+
|
|
+ msglen = header->size + sizeof(VCHIQ_HEADER_T);
|
|
+ /* This must be a VCHIQ-style service */
|
|
+ if (args.msgbufsize < msglen)
|
|
+ {
|
|
+ vcos_log_error("header %x: msgbufsize %x < msglen %x",
|
|
+ (unsigned int)header, args.msgbufsize, msglen);
|
|
+ vcos_assert(0);
|
|
+ if (ret == 0)
|
|
+ ret = -EMSGSIZE;
|
|
+ break;
|
|
+ }
|
|
+ if (msgbufcount <= 0)
|
|
+ {
|
|
+ /* Stall here for lack of a buffer for the message */
|
|
+ break;
|
|
+ }
|
|
+ /* Get the pointer from user space */
|
|
+ msgbufcount--;
|
|
+ if (copy_from_user(&msgbuf,
|
|
+ (const void __user *)&args.msgbufs[msgbufcount],
|
|
+ sizeof(msgbuf)) != 0)
|
|
+ {
|
|
+ if (ret == 0)
|
|
+ ret = -EFAULT;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Copy the message to user space */
|
|
+ if (copy_to_user(msgbuf, header, msglen) != 0)
|
|
+ {
|
|
+ if (ret == 0)
|
|
+ ret = -EFAULT;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Now it has been copied, the message can be released. */
|
|
+ vchiq_release_message(&service->service->base, header);
|
|
+
|
|
+ /* The completion must point to the msgbuf */
|
|
+ completion->header = msgbuf;
|
|
+ }
|
|
+
|
|
+ if (copy_to_user
|
|
+ ((void __user *)((size_t) args.buf +
|
|
+ ret *
|
|
+ sizeof
|
|
+ (VCHIQ_COMPLETION_DATA_T)),
|
|
+ completion,
|
|
+ sizeof(VCHIQ_COMPLETION_DATA_T)) !=
|
|
+ 0) {
|
|
+ if (ret == 0)
|
|
+ ret = -EFAULT;
|
|
+ break;
|
|
+ }
|
|
+ instance->completion_remove++;
|
|
+ }
|
|
+
|
|
+ if (msgbufcount != args.msgbufcount)
|
|
+ {
|
|
+ if (copy_to_user((void __user *)
|
|
+ &((VCHIQ_AWAIT_COMPLETION_T *)arg)->msgbufcount,
|
|
+ &msgbufcount, sizeof(msgbufcount)) != 0)
|
|
+ {
|
|
+ ret = -EFAULT;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (ret != 0)
|
|
+ vcos_event_signal(&instance->remove_event);
|
|
+ DEBUG_TRACE(AWAIT_COMPLETION_LINE);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case VCHIQ_IOC_DEQUEUE_MESSAGE:
|
|
+ {
|
|
+ VCHIQ_DEQUEUE_MESSAGE_T args;
|
|
+ USER_SERVICE_T *user_service;
|
|
+ VCHIQ_HEADER_T *header;
|
|
+
|
|
+ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE);
|
|
+ if (copy_from_user
|
|
+ (&args, (const void __user *)arg,
|
|
+ sizeof(args)) != 0) {
|
|
+ ret = -EFAULT;
|
|
+ break;
|
|
+ }
|
|
+ user_service = &instance->services[args.handle];
|
|
+ if ((args.handle < 0) || (args.handle >= MAX_SERVICES) ||
|
|
+ (user_service->service == NULL) ||
|
|
+ (user_service->is_vchi == 0)) {
|
|
+ ret = -EINVAL;
|
|
+ break;
|
|
+ }
|
|
+ if (user_service->msg_remove == user_service->msg_insert)
|
|
+ {
|
|
+ if (!args.blocking)
|
|
+ {
|
|
+ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE);
|
|
+ ret = -EWOULDBLOCK;
|
|
+ break;
|
|
+ }
|
|
+ user_service->dequeue_pending = 1;
|
|
+ do {
|
|
+ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE);
|
|
+ if (vcos_event_wait(&user_service->insert_event) !=
|
|
+ VCOS_SUCCESS) {
|
|
+ vcos_log_info("DEQUEUE_MESSAGE interrupted");
|
|
+ ret = -EINTR;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ while (user_service->msg_remove == user_service->msg_insert);
|
|
+ }
|
|
+
|
|
+ /* A read memory barrier is needed to stop prefetch of a stale
|
|
+ header value */
|
|
+ vcos_rmb();
|
|
+
|
|
+ header = user_service->msg_queue[user_service->msg_remove &
|
|
+ (MSG_QUEUE_SIZE - 1)];
|
|
+ if (header == NULL)
|
|
+ ret = -ENOTCONN;
|
|
+ else if (header->size <= args.bufsize)
|
|
+ {
|
|
+ /* Copy to user space if msgbuf is not NULL */
|
|
+ if ((args.buf == NULL) ||
|
|
+ (copy_to_user((void __user *)args.buf, header->data,
|
|
+ header->size) == 0))
|
|
+ {
|
|
+ ret = header->size;
|
|
+ vchiq_release_message(&user_service->service->base,
|
|
+ header);
|
|
+ user_service->msg_remove++;
|
|
+ vcos_event_signal(&user_service->remove_event);
|
|
+ }
|
|
+ else
|
|
+ ret = -EFAULT;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ vcos_log_error("header %x: bufsize %x < size %x",
|
|
+ (unsigned int)header, args.bufsize, header->size);
|
|
+ vcos_assert(0);
|
|
+ ret = -EMSGSIZE;
|
|
+ }
|
|
+ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case VCHIQ_IOC_GET_CLIENT_ID:
|
|
+ {
|
|
+ USER_SERVICE_T *user_service;
|
|
+ int handle = (int)arg;
|
|
+
|
|
+ user_service = find_service_by_handle(instance, handle);
|
|
+ if (user_service != NULL)
|
|
+ ret = vchiq_get_client_id(&user_service->service->base);
|
|
+ else
|
|
+ ret = 0;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case VCHIQ_IOC_GET_CONFIG:
|
|
+ {
|
|
+ VCHIQ_GET_CONFIG_T args;
|
|
+ VCHIQ_CONFIG_T config;
|
|
+
|
|
+ if (copy_from_user
|
|
+ (&args, (const void __user *)arg,
|
|
+ sizeof(args)) != 0) {
|
|
+ ret = -EFAULT;
|
|
+ break;
|
|
+ }
|
|
+ if (args.config_size > sizeof(config))
|
|
+ {
|
|
+ ret = -EINVAL;
|
|
+ break;
|
|
+ }
|
|
+ status = vchiq_get_config(instance, args.config_size, &config);
|
|
+ if (status == VCHIQ_SUCCESS)
|
|
+ {
|
|
+ if (copy_to_user((void __user *)args.pconfig,
|
|
+ &config, args.config_size) != 0)
|
|
+ {
|
|
+ ret = -EFAULT;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case VCHIQ_IOC_SET_SERVICE_OPTION:
|
|
+ {
|
|
+ VCHIQ_SET_SERVICE_OPTION_T args;
|
|
+ USER_SERVICE_T *user_service;
|
|
+
|
|
+ if (copy_from_user(
|
|
+ &args, (const void __user *)arg,
|
|
+ sizeof(args)) != 0)
|
|
+ {
|
|
+ ret = -EFAULT;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ user_service = find_service_by_handle(instance, args.handle);
|
|
+ if (user_service != NULL)
|
|
+ {
|
|
+ status = vchiq_set_service_option(
|
|
+ &user_service->service->base,
|
|
+ args.option, args.value);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ ret = -ENOTTY;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (ret == 0) {
|
|
+ if (status == VCHIQ_ERROR)
|
|
+ ret = -EIO;
|
|
+ else if (status == VCHIQ_RETRY)
|
|
+ ret = -EINTR;
|
|
+ }
|
|
+
|
|
+ if ((ret < 0) && (ret != -EINTR) && (ret != -EWOULDBLOCK))
|
|
+ vcos_log_warn(" ioctl instance %lx, cmd %s -> status %d, %ld",
|
|
+ (unsigned long)instance,
|
|
+ (_IOC_NR(cmd) <= VCHIQ_IOC_MAX) ? ioctl_names[_IOC_NR(cmd)] :
|
|
+ "<invalid>", status, ret);
|
|
+ else
|
|
+ vcos_log_trace(" ioctl instance %lx, cmd %s -> status %d, %ld",
|
|
+ (unsigned long)instance,
|
|
+ (_IOC_NR(cmd) <= VCHIQ_IOC_MAX) ? ioctl_names[_IOC_NR(cmd)] :
|
|
+ "<invalid>", status, ret);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* vchiq_open
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+static int
|
|
+vchiq_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ int dev = iminor(inode) & 0x0f;
|
|
+ vcos_log_info("vchiq_open");
|
|
+ switch (dev) {
|
|
+ case VCHIQ_MINOR:
|
|
+ {
|
|
+ VCHIQ_STATE_T *state = vchiq_get_state();
|
|
+ VCHIQ_INSTANCE_T instance;
|
|
+
|
|
+ if (!state)
|
|
+ {
|
|
+ vcos_log_error( "vchiq has no connection to VideoCore");
|
|
+ return -ENOTCONN;
|
|
+ }
|
|
+
|
|
+ instance = kzalloc(sizeof(*instance), GFP_KERNEL);
|
|
+ if (!instance)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ instance->state = state;
|
|
+ instance->pid = current->tgid;
|
|
+ vcos_event_create(&instance->insert_event, DEVICE_NAME);
|
|
+ vcos_event_create(&instance->remove_event, DEVICE_NAME);
|
|
+
|
|
+ file->private_data = instance;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ vcos_log_error("Unknown minor device: %d", dev);
|
|
+ return -ENXIO;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* vchiq_release
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+static int
|
|
+vchiq_release(struct inode *inode, struct file *file)
|
|
+{
|
|
+ int dev = iminor(inode) & 0x0f;
|
|
+ int ret = 0;
|
|
+ switch (dev) {
|
|
+ case VCHIQ_MINOR:
|
|
+ {
|
|
+ VCHIQ_INSTANCE_T instance = file->private_data;
|
|
+ int i;
|
|
+
|
|
+ vcos_log_info("vchiq_release: instance=%lx",
|
|
+ (unsigned long)instance);
|
|
+
|
|
+ instance->closing = 1;
|
|
+
|
|
+ /* Wake the slot handler if the completion queue is full */
|
|
+ vcos_event_signal(&instance->remove_event);
|
|
+
|
|
+ /* Mark all services for termination... */
|
|
+
|
|
+ for (i = 0; i < MAX_SERVICES; i++) {
|
|
+ USER_SERVICE_T *user_service =
|
|
+ &instance->services[i];
|
|
+ if (user_service->service != NULL)
|
|
+ {
|
|
+ /* Wake the slot handler if the msg queue is full */
|
|
+ vcos_event_signal(&user_service->remove_event);
|
|
+
|
|
+ if ((user_service->service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT) &&
|
|
+ (user_service->service->srvstate != VCHIQ_SRVSTATE_LISTENING))
|
|
+ {
|
|
+ vchiq_terminate_service_internal(user_service->service);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* ...and wait for them to die */
|
|
+
|
|
+ for (i = 0; i < MAX_SERVICES; i++) {
|
|
+ USER_SERVICE_T *user_service =
|
|
+ &instance->services[i];
|
|
+ if (user_service->service != NULL)
|
|
+ {
|
|
+ /* Wait in this non-portable fashion because interruptible
|
|
+ calls will not block in this context. */
|
|
+ while ((user_service->service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT) &&
|
|
+ (user_service->service->srvstate != VCHIQ_SRVSTATE_LISTENING))
|
|
+ {
|
|
+ down(&user_service->service->remove_event);
|
|
+ }
|
|
+
|
|
+ vchiq_free_service_internal
|
|
+ (user_service->service);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ vcos_event_delete(&instance->insert_event);
|
|
+ vcos_event_delete(&instance->remove_event);
|
|
+
|
|
+ kfree(instance);
|
|
+ file->private_data = NULL;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ vcos_log_error("Unknown minor device: %d", dev);
|
|
+ ret = -ENXIO;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* vchiq_dump
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+void
|
|
+vchiq_dump(void *dump_context, const char *str, int len)
|
|
+{
|
|
+ DUMP_CONTEXT_T *context = (DUMP_CONTEXT_T *)dump_context;
|
|
+
|
|
+ if ((context->actual >= 0) && (context->actual < context->space))
|
|
+ {
|
|
+ int copy_bytes;
|
|
+ if (context->offset > 0)
|
|
+ {
|
|
+ int skip_bytes = vcos_min(len, context->offset);
|
|
+ str += skip_bytes;
|
|
+ len -= skip_bytes;
|
|
+ context->offset -= skip_bytes;
|
|
+ if (context->offset > 0)
|
|
+ return;
|
|
+ }
|
|
+ copy_bytes = vcos_min(len, context->space - context->actual);
|
|
+ if (copy_bytes == 0)
|
|
+ return;
|
|
+ if (copy_to_user(context->buf + context->actual, str, copy_bytes))
|
|
+ context->actual = -EFAULT;
|
|
+ context->actual += copy_bytes;
|
|
+ len -= copy_bytes;
|
|
+
|
|
+ /* If tne terminating NUL is included in the length, then it marks
|
|
+ * the end of a line and should be replaced with a carriage return.
|
|
+ */
|
|
+ if ((len == 0) && (str[copy_bytes - 1] == '\0'))
|
|
+ {
|
|
+ char cr = '\n';
|
|
+ if (copy_to_user(context->buf + context->actual - 1, &cr, 1))
|
|
+ {
|
|
+ context->actual = -EFAULT;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* vchiq_dump_platform_instance_state
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+void
|
|
+vchiq_dump_platform_instances(void *dump_context)
|
|
+{
|
|
+ VCHIQ_STATE_T *state = vchiq_get_state();
|
|
+ char buf[80];
|
|
+ int len;
|
|
+ int i;
|
|
+
|
|
+ /* There is no list of instances, so instead scan all services,
|
|
+ marking those that have been dumped. */
|
|
+
|
|
+ for (i = 0; i < state->unused_service; i++)
|
|
+ {
|
|
+ VCHIQ_SERVICE_T *service = state->services[i];
|
|
+ VCHIQ_INSTANCE_T instance;
|
|
+
|
|
+ if (service
|
|
+ && ((instance = service->instance) != NULL)
|
|
+ && (service->base.callback == service_callback))
|
|
+ instance->mark = 0;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < state->unused_service; i++)
|
|
+ {
|
|
+ VCHIQ_SERVICE_T *service = state->services[i];
|
|
+ VCHIQ_INSTANCE_T instance;
|
|
+
|
|
+ if (service
|
|
+ && ((instance = service->instance) != NULL)
|
|
+ && (service->base.callback == service_callback))
|
|
+ {
|
|
+ if (!instance->mark)
|
|
+ {
|
|
+ len = vcos_snprintf(buf, sizeof(buf),
|
|
+ "Instance %x: pid %d,%s completions %d/%d",
|
|
+ (unsigned int)instance, instance->pid,
|
|
+ instance->connected ? " connected," : "",
|
|
+ instance->completion_insert - instance->completion_remove,
|
|
+ MAX_COMPLETIONS);
|
|
+
|
|
+ vchiq_dump(dump_context, buf, len + 1);
|
|
+
|
|
+ instance->mark = 1;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* vchiq_dump_platform_service_state
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+void
|
|
+vchiq_dump_platform_service_state(void *dump_context, VCHIQ_SERVICE_T *service)
|
|
+{
|
|
+ USER_SERVICE_T *user_service = (USER_SERVICE_T *)service->base.userdata;
|
|
+ char buf[80];
|
|
+ int len;
|
|
+
|
|
+ len = vcos_snprintf(buf, sizeof(buf), " instance %x",
|
|
+ service->instance);
|
|
+
|
|
+ if ((service->base.callback == service_callback) && user_service->is_vchi)
|
|
+ {
|
|
+ len += vcos_snprintf(buf + len, sizeof(buf) - len,
|
|
+ ", %d/%d messages",
|
|
+ user_service->msg_insert - user_service->msg_remove,
|
|
+ MSG_QUEUE_SIZE);
|
|
+
|
|
+ if (user_service->dequeue_pending)
|
|
+ len += vcos_snprintf(buf + len, sizeof(buf) - len,
|
|
+ " (dequeue pending)");
|
|
+ }
|
|
+
|
|
+ vchiq_dump(dump_context, buf, len + 1);
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* vchiq_read
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+static ssize_t
|
|
+vchiq_read(struct file * file, char __user * buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ DUMP_CONTEXT_T context;
|
|
+ context.buf = buf;
|
|
+ context.actual = 0;
|
|
+ context.space = count;
|
|
+ context.offset = *ppos;
|
|
+
|
|
+ vchiq_dump_state(&context, &g_state);
|
|
+
|
|
+ if (context.actual >= 0)
|
|
+ *ppos += context.actual;
|
|
+
|
|
+ return context.actual;
|
|
+}
|
|
+
|
|
+VCHIQ_STATE_T *
|
|
+vchiq_get_state(void)
|
|
+{
|
|
+
|
|
+ if (g_state.remote == NULL)
|
|
+ {
|
|
+ printk( "%s: g_state.remote == NULL\n", __func__ );
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if ( g_state.remote->initialised != 1)
|
|
+ {
|
|
+ printk( "%s: g_state.remote->initialised != 1 (%d)\n", __func__, g_state.remote->initialised );
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ((g_state.remote != NULL) &&
|
|
+ (g_state.remote->initialised == 1)) ? &g_state : NULL;
|
|
+}
|
|
+
|
|
+static const struct file_operations
|
|
+vchiq_fops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .unlocked_ioctl = vchiq_ioctl,
|
|
+ .open = vchiq_open,
|
|
+ .release = vchiq_release,
|
|
+ .read = vchiq_read
|
|
+};
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* vchiq_init - called when the module is loaded.
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+static int __init
|
|
+vchiq_init(void)
|
|
+{
|
|
+ int err;
|
|
+ void *ptr_err;
|
|
+
|
|
+ err = vchiq_platform_vcos_init();
|
|
+ if (err != 0)
|
|
+ goto failed_platform_vcos_init;
|
|
+
|
|
+ vcos_log_set_level(VCOS_LOG_CATEGORY, vchiq_default_arm_log_level);
|
|
+ vcos_log_register("vchiq_arm", VCOS_LOG_CATEGORY);
|
|
+
|
|
+ if ((err =
|
|
+ alloc_chrdev_region(&vchiq_devid, VCHIQ_MINOR, 1,
|
|
+ DEVICE_NAME)) != 0) {
|
|
+ vcos_log_error("Unable to allocate device number");
|
|
+ goto failed_alloc_chrdev;
|
|
+ }
|
|
+ cdev_init(&vchiq_cdev, &vchiq_fops);
|
|
+ vchiq_cdev.owner = THIS_MODULE;
|
|
+ if ((err = cdev_add(&vchiq_cdev, vchiq_devid, 1)) != 0) {
|
|
+ vcos_log_error("Unable to register device");
|
|
+ goto failed_cdev_add;
|
|
+ }
|
|
+
|
|
+ /* create sysfs entries */
|
|
+ vchiq_class = class_create(THIS_MODULE, DEVICE_NAME);
|
|
+ if (IS_ERR(ptr_err = vchiq_class))
|
|
+ goto failed_class_create;
|
|
+
|
|
+ vchiq_dev = device_create(vchiq_class, NULL,
|
|
+ vchiq_devid, NULL, "vchiq");
|
|
+ if (IS_ERR(ptr_err = vchiq_dev))
|
|
+ goto failed_device_create;
|
|
+
|
|
+ err = vchiq_platform_init(&g_state);
|
|
+ if (err != 0)
|
|
+ goto failed_platform_init;
|
|
+
|
|
+ vcos_log_error("vchiq: initialised - version %d (min %d), device %d.%d",
|
|
+ VCHIQ_VERSION, VCHIQ_VERSION_MIN,
|
|
+ MAJOR(vchiq_devid), MINOR(vchiq_devid));
|
|
+
|
|
+ return 0;
|
|
+
|
|
+failed_platform_init:
|
|
+ device_destroy(vchiq_class, vchiq_devid);
|
|
+failed_device_create:
|
|
+ class_destroy(vchiq_class);
|
|
+failed_class_create:
|
|
+ cdev_del(&vchiq_cdev);
|
|
+ err = PTR_ERR(ptr_err);
|
|
+failed_cdev_add:
|
|
+ unregister_chrdev_region(vchiq_devid, 1);
|
|
+failed_alloc_chrdev:
|
|
+failed_platform_vcos_init:
|
|
+ printk(KERN_WARNING "could not load vchiq\n");
|
|
+ return err;
|
|
+}
|
|
+/****************************************************************************
|
|
+*
|
|
+* vchiq_exit - called when the module is unloaded.
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+static void __exit
|
|
+vchiq_exit(void)
|
|
+{
|
|
+ vchiq_platform_exit(&g_state);
|
|
+ device_destroy(vchiq_class, vchiq_devid);
|
|
+ class_destroy(vchiq_class);
|
|
+ cdev_del(&vchiq_cdev);
|
|
+ unregister_chrdev_region(vchiq_devid, 1);
|
|
+ vcos_log_unregister(VCOS_LOG_CATEGORY);
|
|
+}
|
|
+
|
|
+module_init(vchiq_init);
|
|
+module_exit(vchiq_exit);
|
|
+MODULE_LICENSE("GPL");
|
|
+MODULE_AUTHOR("Broadcom Corporation");
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h
|
|
@@ -0,0 +1,38 @@
|
|
+/*
|
|
+ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
+ */
|
|
+
|
|
+#ifndef VCHIQ_ARM_H
|
|
+#define VCHIQ_ARM_H
|
|
+
|
|
+#include "vchiq_core.h"
|
|
+
|
|
+extern VCOS_LOG_CAT_T vchiq_arm_log_category;
|
|
+
|
|
+extern int __init
|
|
+vchiq_platform_vcos_init(void);
|
|
+
|
|
+extern int __init
|
|
+vchiq_platform_init(VCHIQ_STATE_T *state);
|
|
+
|
|
+extern void __exit
|
|
+vchiq_platform_exit(VCHIQ_STATE_T *state);
|
|
+
|
|
+extern VCHIQ_STATE_T *
|
|
+vchiq_get_state(void);
|
|
+
|
|
+#endif /* VCHIQ_ARM_H */
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h
|
|
@@ -0,0 +1,43 @@
|
|
+/*
|
|
+ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
+ */
|
|
+
|
|
+#ifndef VCHIQ_CFG_H
|
|
+#define VCHIQ_CFG_H
|
|
+
|
|
+#define VCHIQ_MAGIC VCHIQ_MAKE_FOURCC('V','C','H','I')
|
|
+/* The version of VCHIQ - change with any non-trivial change */
|
|
+#define VCHIQ_VERSION 2
|
|
+/* The minimum compatible version - update to match VCHIQ_VERSION with any incompatible change */
|
|
+#define VCHIQ_VERSION_MIN 2
|
|
+
|
|
+#define VCHIQ_MAX_SERVICES 4096
|
|
+#define VCHIQ_MAX_SLOTS 128
|
|
+#define VCHIQ_MAX_SLOTS_PER_SIDE 64
|
|
+
|
|
+#define VCHIQ_NUM_CURRENT_BULKS 32
|
|
+#define VCHIQ_NUM_SERVICE_BULKS 4
|
|
+
|
|
+#ifndef VCHIQ_ENABLE_DEBUG
|
|
+#define VCHIQ_ENABLE_DEBUG 1
|
|
+#endif
|
|
+
|
|
+#ifndef VCHIQ_ENABLE_STATS
|
|
+#define VCHIQ_ENABLE_STATS 1
|
|
+#endif
|
|
+
|
|
+#endif /* VCHIQ_CFG_H */
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c
|
|
@@ -0,0 +1,101 @@
|
|
+/*****************************************************************************
|
|
+* Copyright 2001 - 2010 Broadcom Corporation. All rights reserved.
|
|
+*
|
|
+* Unless you and Broadcom execute a separate written software license
|
|
+* agreement governing use of this software, this software is licensed to you
|
|
+* under the terms of the GNU General Public License version 2, available at
|
|
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
|
+*
|
|
+* Notwithstanding the above, under no circumstances may you combine this
|
|
+* software in any way with any other Broadcom software provided under a
|
|
+* license other than the GPL, without Broadcom's express prior written
|
|
+* consent.
|
|
+*****************************************************************************/
|
|
+
|
|
+#include "vcos.h"
|
|
+#include "vchiq_connected.h"
|
|
+#include <linux/module.h>
|
|
+
|
|
+#define MAX_CALLBACKS 10
|
|
+
|
|
+static int g_connected = 0;
|
|
+static int g_num_deferred_callbacks;
|
|
+static VCHIQ_CONNECTED_CALLBACK_T g_deferred_callback[ MAX_CALLBACKS ];
|
|
+static VCOS_ONCE_T g_once_init;
|
|
+static VCOS_MUTEX_T g_connected_mutex;
|
|
+
|
|
+extern VCOS_LOG_CAT_T vchiq_core_log_category;
|
|
+#define VCOS_LOG_CATEGORY (&vchiq_core_log_category)
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* Function to initialize our lock.
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+static void connected_init( void )
|
|
+{
|
|
+ vcos_mutex_create( &g_connected_mutex, "connected_mutex");
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* This function is used to defer initialization until the vchiq stack is
|
|
+* initialized. If the stack is already initialized, then the callback will
|
|
+* be made immediately, otherwise it will be deferred until
|
|
+* vchiq_call_connected_callbacks is called.
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+void vchiq_add_connected_callback( VCHIQ_CONNECTED_CALLBACK_T callback )
|
|
+{
|
|
+ vcos_once( &g_once_init, connected_init );
|
|
+
|
|
+ vcos_mutex_lock( &g_connected_mutex );
|
|
+
|
|
+ if ( g_connected )
|
|
+ {
|
|
+ // We're already connected. Call the callback immediately.
|
|
+
|
|
+ callback();
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if ( g_num_deferred_callbacks >= MAX_CALLBACKS )
|
|
+ {
|
|
+ vcos_log_error( "There already %d callback registered - please increase MAX_CALLBACKS",
|
|
+ g_num_deferred_callbacks );
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ g_deferred_callback[ g_num_deferred_callbacks ] = callback;
|
|
+ g_num_deferred_callbacks++;
|
|
+ }
|
|
+ }
|
|
+ vcos_mutex_unlock( &g_connected_mutex );
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* This function is called by the vchiq stack once it has been connected to
|
|
+* the videocore and clients can start to use the stack.
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+void vchiq_call_connected_callbacks( void )
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ vcos_once( &g_once_init, connected_init );
|
|
+
|
|
+ vcos_mutex_lock( &g_connected_mutex );
|
|
+ for ( i = 0; i < g_num_deferred_callbacks; i++ )\
|
|
+ {
|
|
+ g_deferred_callback[i]();
|
|
+ }
|
|
+ g_num_deferred_callbacks = 0;
|
|
+ g_connected = 1;
|
|
+ vcos_mutex_unlock( &g_connected_mutex );
|
|
+}
|
|
+
|
|
+EXPORT_SYMBOL( vchiq_add_connected_callback );
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.h
|
|
@@ -0,0 +1,32 @@
|
|
+/*****************************************************************************
|
|
+* Copyright 2001 - 2010 Broadcom Corporation. All rights reserved.
|
|
+*
|
|
+* Unless you and Broadcom execute a separate written software license
|
|
+* agreement governing use of this software, this software is licensed to you
|
|
+* under the terms of the GNU General Public License version 2, available at
|
|
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
|
+*
|
|
+* Notwithstanding the above, under no circumstances may you combine this
|
|
+* software in any way with any other Broadcom software provided under a
|
|
+* license other than the GPL, without Broadcom's express prior written
|
|
+* consent.
|
|
+*****************************************************************************/
|
|
+
|
|
+#ifndef VCHIQ_CONNECTED_H
|
|
+#define VCHIQ_CONNECTED_H
|
|
+
|
|
+/* ---- Include Files ----------------------------------------------------- */
|
|
+
|
|
+/* ---- Constants and Types ---------------------------------------------- */
|
|
+
|
|
+typedef void (*VCHIQ_CONNECTED_CALLBACK_T)( void );
|
|
+
|
|
+/* ---- Variable Externs ------------------------------------------------- */
|
|
+
|
|
+/* ---- Function Prototypes ---------------------------------------------- */
|
|
+
|
|
+void vchiq_add_connected_callback( VCHIQ_CONNECTED_CALLBACK_T callback );
|
|
+void vchiq_call_connected_callbacks( void );
|
|
+
|
|
+#endif /* VCHIQ_CONNECTED_H */
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c
|
|
@@ -0,0 +1,2604 @@
|
|
+/*
|
|
+ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
+ */
|
|
+
|
|
+#include "vchiq_core.h"
|
|
+
|
|
+#define VCHIQ_SLOT_HANDLER_STACK 8192
|
|
+
|
|
+#define SLOT_INFO_FROM_INDEX(state, index) (state->slot_info + (index))
|
|
+#define SLOT_DATA_FROM_INDEX(state, index) (state->slot_data + (index))
|
|
+#define SLOT_INDEX_FROM_DATA(state, data) (((unsigned int)((char *)data - (char *)state->slot_data)) / VCHIQ_SLOT_SIZE)
|
|
+#define SLOT_INDEX_FROM_INFO(state, info) ((unsigned int)(info - state->slot_info))
|
|
+#define SLOT_QUEUE_INDEX_FROM_POS(pos) ((int)((unsigned int)(pos) / VCHIQ_SLOT_SIZE))
|
|
+
|
|
+#define VCOS_LOG_CATEGORY (&vchiq_core_log_category)
|
|
+
|
|
+#define BULK_INDEX(x) (x & (VCHIQ_NUM_SERVICE_BULKS - 1))
|
|
+
|
|
+typedef struct bulk_waiter_struct
|
|
+{
|
|
+ VCOS_EVENT_T event;
|
|
+ int actual;
|
|
+} BULK_WAITER_T;
|
|
+
|
|
+typedef struct vchiq_open_payload_struct{
|
|
+ int fourcc;
|
|
+ int client_id;
|
|
+ short version;
|
|
+ short version_min;
|
|
+} VCHIQ_OPEN_PAYLOAD_T;
|
|
+
|
|
+vcos_static_assert(sizeof(VCHIQ_HEADER_T) == 8); /* we require this for consistency between endpoints */
|
|
+vcos_static_assert(IS_POW2(sizeof(VCHIQ_HEADER_T)));
|
|
+vcos_static_assert(IS_POW2(VCHIQ_NUM_CURRENT_BULKS));
|
|
+vcos_static_assert(IS_POW2(VCHIQ_NUM_SERVICE_BULKS));
|
|
+
|
|
+VCOS_LOG_CAT_T vchiq_core_log_category;
|
|
+VCOS_LOG_CAT_T vchiq_core_msg_log_category;
|
|
+VCOS_LOG_LEVEL_T vchiq_default_core_log_level = VCOS_LOG_WARN;
|
|
+VCOS_LOG_LEVEL_T vchiq_default_core_msg_log_level = VCOS_LOG_WARN;
|
|
+
|
|
+static const char *const srvstate_names[] =
|
|
+{
|
|
+ "FREE",
|
|
+ "HIDDEN",
|
|
+ "LISTENING",
|
|
+ "OPENING",
|
|
+ "OPEN",
|
|
+ "CLOSESENT",
|
|
+ "CLOSING",
|
|
+ "CLOSEWAIT"
|
|
+};
|
|
+
|
|
+static const char *const reason_names[] =
|
|
+{
|
|
+ "SERVICE_OPENED",
|
|
+ "SERVICE_CLOSED",
|
|
+ "MESSAGE_AVAILABLE",
|
|
+ "BULK_TRANSMIT_DONE",
|
|
+ "BULK_RECEIVE_DONE",
|
|
+ "BULK_TRANSMIT_ABORTED",
|
|
+ "BULK_RECEIVE_ABORTED"
|
|
+};
|
|
+
|
|
+static const char *const conn_state_names[] =
|
|
+{
|
|
+ "DISCONNECTED",
|
|
+ "CONNECTED",
|
|
+ "PAUSING",
|
|
+ "PAUSE_SENT",
|
|
+ "PAUSED",
|
|
+ "RESUMING"
|
|
+};
|
|
+
|
|
+static const char *msg_type_str( unsigned int msg_type )
|
|
+{
|
|
+ switch (msg_type) {
|
|
+ case VCHIQ_MSG_PADDING: return "PADDING";
|
|
+ case VCHIQ_MSG_CONNECT: return "CONNECT";
|
|
+ case VCHIQ_MSG_OPEN: return "OPEN";
|
|
+ case VCHIQ_MSG_OPENACK: return "OPENACK";
|
|
+ case VCHIQ_MSG_CLOSE: return "CLOSE";
|
|
+ case VCHIQ_MSG_DATA: return "DATA";
|
|
+ case VCHIQ_MSG_BULK_RX: return "BULK_RX";
|
|
+ case VCHIQ_MSG_BULK_TX: return "BULK_TX";
|
|
+ case VCHIQ_MSG_BULK_RX_DONE: return "BULK_RX_DONE";
|
|
+ case VCHIQ_MSG_BULK_TX_DONE: return "BULK_TX_DONE";
|
|
+ case VCHIQ_MSG_PAUSE: return "PAUSE";
|
|
+ case VCHIQ_MSG_RESUME: return "RESUME";
|
|
+ }
|
|
+ return "???";
|
|
+}
|
|
+
|
|
+static inline void
|
|
+vchiq_set_service_state(VCHIQ_SERVICE_T *service, int newstate)
|
|
+{
|
|
+ vcos_log_info("%d: srv:%d %s->%s", service->state->id, service->localport,
|
|
+ srvstate_names[service->srvstate],
|
|
+ srvstate_names[newstate]);
|
|
+ service->srvstate = newstate;
|
|
+}
|
|
+
|
|
+static inline VCHIQ_STATUS_T
|
|
+make_service_callback(VCHIQ_SERVICE_T *service, VCHIQ_REASON_T reason,
|
|
+ VCHIQ_HEADER_T *header, void *bulk_userdata)
|
|
+{
|
|
+ vcos_log_trace("%d: callback:%d (%s, %x, %x)", service->state->id,
|
|
+ service->localport, reason_names[reason],
|
|
+ (unsigned int)header, (unsigned int)bulk_userdata);
|
|
+ return service->base.callback(reason, header, &service->base, bulk_userdata);
|
|
+}
|
|
+
|
|
+static inline void
|
|
+vchiq_set_conn_state(VCHIQ_STATE_T *state, VCHIQ_CONNSTATE_T newstate)
|
|
+{
|
|
+ vcos_log_info("%d: %s->%s", state->id,
|
|
+ conn_state_names[state->conn_state],
|
|
+ conn_state_names[newstate]);
|
|
+ state->conn_state = newstate;
|
|
+}
|
|
+
|
|
+static inline void
|
|
+remote_event_create(REMOTE_EVENT_T *event)
|
|
+{
|
|
+ event->armed = 0;
|
|
+ /* Don't clear the 'fired' flag because it may already have been set by the other side */
|
|
+ vcos_event_create(event->event, "vchiq");
|
|
+}
|
|
+
|
|
+static inline void
|
|
+remote_event_destroy(REMOTE_EVENT_T *event)
|
|
+{
|
|
+ vcos_event_delete(event->event);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+remote_event_wait(REMOTE_EVENT_T *event)
|
|
+{
|
|
+ if (!event->fired)
|
|
+ {
|
|
+ event->armed = 1;
|
|
+ if (event->fired) /* Also ensures the write has completed */
|
|
+ event->armed = 0;
|
|
+ else if (vcos_event_wait(event->event) != VCOS_SUCCESS)
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ event->fired = 0;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static inline void
|
|
+remote_event_signal_local(REMOTE_EVENT_T *event)
|
|
+{
|
|
+ event->armed = 0;
|
|
+ vcos_event_signal(event->event);
|
|
+}
|
|
+
|
|
+static inline void
|
|
+remote_event_poll(REMOTE_EVENT_T *event)
|
|
+{
|
|
+ if (event->armed)
|
|
+ remote_event_signal_local(event);
|
|
+}
|
|
+
|
|
+void
|
|
+remote_event_pollall(VCHIQ_STATE_T *state)
|
|
+{
|
|
+ remote_event_poll(&state->local->trigger);
|
|
+ remote_event_poll(&state->local->recycle);
|
|
+}
|
|
+
|
|
+/* Round up message sizes so that any space at the end of a slot is always big
|
|
+ enough for a header. This relies on header size being a power of two, which
|
|
+ has been verified earlier by a static assertion. */
|
|
+
|
|
+static inline unsigned int
|
|
+calc_stride(unsigned int size)
|
|
+{
|
|
+ /* Allow room for the header */
|
|
+ size += sizeof(VCHIQ_HEADER_T);
|
|
+
|
|
+ /* Round up */
|
|
+ return (size + sizeof(VCHIQ_HEADER_T) - 1) & ~(sizeof(VCHIQ_HEADER_T) - 1);
|
|
+}
|
|
+
|
|
+static VCHIQ_SERVICE_T *
|
|
+get_listening_service(VCHIQ_STATE_T *state, int fourcc)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ vcos_assert(fourcc != VCHIQ_FOURCC_INVALID);
|
|
+
|
|
+ for (i = 0; i < state->unused_service; i++)
|
|
+ {
|
|
+ VCHIQ_SERVICE_T *service = state->services[i];
|
|
+ if (service &&
|
|
+ (service->public_fourcc == fourcc) &&
|
|
+ ((service->srvstate == VCHIQ_SRVSTATE_LISTENING) ||
|
|
+ ((service->srvstate == VCHIQ_SRVSTATE_OPEN) &&
|
|
+ (service->remoteport == VCHIQ_PORT_FREE))))
|
|
+ return service;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static VCHIQ_SERVICE_T *
|
|
+get_connected_service(VCHIQ_STATE_T *state, unsigned int port)
|
|
+{
|
|
+ int i;
|
|
+ for (i = 0; i < state->unused_service; i++) {
|
|
+ VCHIQ_SERVICE_T *service = state->services[i];
|
|
+ if (service && (service->srvstate == VCHIQ_SRVSTATE_OPEN)
|
|
+ && (service->remoteport == port)) {
|
|
+ return service;
|
|
+ }
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static inline void
|
|
+request_poll(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, int poll_type)
|
|
+{
|
|
+ if (service)
|
|
+ {
|
|
+ vcos_atomic_flags_or(&service->poll_flags, (1 << poll_type));
|
|
+ vcos_atomic_flags_or(&state->poll_services[service->localport>>5],
|
|
+ (1 <<(service->localport & 0x1f)));
|
|
+ }
|
|
+
|
|
+ state->poll_needed = 1;
|
|
+ vcos_wmb(&state->poll_needed);
|
|
+
|
|
+ /* ... and ensure the slot handler runs. */
|
|
+ remote_event_signal_local(&state->local->trigger);
|
|
+}
|
|
+
|
|
+/* Called from queue_message, by the slot handler and application threads,
|
|
+ with slot_mutex held */
|
|
+static VCHIQ_HEADER_T *
|
|
+reserve_space(VCHIQ_STATE_T *state, int space, int is_blocking)
|
|
+{
|
|
+ VCHIQ_SHARED_STATE_T *local = state->local;
|
|
+ int tx_pos = state->local_tx_pos;
|
|
+ int slot_space = VCHIQ_SLOT_SIZE - (tx_pos & VCHIQ_SLOT_MASK);
|
|
+
|
|
+ if (space > slot_space) {
|
|
+ VCHIQ_HEADER_T *header;
|
|
+ /* Fill the remaining space with padding */
|
|
+ vcos_assert(state->tx_data != NULL);
|
|
+ header = (VCHIQ_HEADER_T *) (state->tx_data + (tx_pos & VCHIQ_SLOT_MASK));
|
|
+ header->msgid = VCHIQ_MSGID_PADDING;
|
|
+ header->size = slot_space - sizeof(VCHIQ_HEADER_T);
|
|
+
|
|
+ tx_pos += slot_space;
|
|
+ }
|
|
+
|
|
+ /* If necessary, get the next slot. */
|
|
+ if ((tx_pos & VCHIQ_SLOT_MASK) == 0)
|
|
+ {
|
|
+ int slot_index;
|
|
+
|
|
+ /* If there is no free slot... */
|
|
+ if (tx_pos == (state->slot_queue_available * VCHIQ_SLOT_SIZE))
|
|
+ {
|
|
+ /* ...wait for one. */
|
|
+ VCHIQ_STATS_INC(state, slot_stalls);
|
|
+
|
|
+ /* But first, flush through the last slot. */
|
|
+ local->tx_pos = tx_pos;
|
|
+ remote_event_signal(&state->remote->trigger);
|
|
+
|
|
+ do {
|
|
+ if (!is_blocking ||
|
|
+ (vcos_event_wait(&state->slot_available_event) != VCOS_SUCCESS))
|
|
+ {
|
|
+ return NULL; /* No space available now */
|
|
+ }
|
|
+ }
|
|
+ while (tx_pos == (state->slot_queue_available * VCHIQ_SLOT_SIZE));
|
|
+ }
|
|
+
|
|
+ slot_index = local->slot_queue[SLOT_QUEUE_INDEX_FROM_POS(tx_pos) & VCHIQ_SLOT_QUEUE_MASK];
|
|
+ state->tx_data = (char *)SLOT_DATA_FROM_INDEX(state, slot_index);
|
|
+ }
|
|
+
|
|
+ state->local_tx_pos = tx_pos + space;
|
|
+
|
|
+ return (VCHIQ_HEADER_T *)(state->tx_data + (tx_pos & VCHIQ_SLOT_MASK));
|
|
+}
|
|
+
|
|
+/* Called with slot_mutex held */
|
|
+static void
|
|
+process_free_queue(VCHIQ_STATE_T *state)
|
|
+{
|
|
+ VCHIQ_SHARED_STATE_T *local = state->local;
|
|
+ BITSET_T service_found[BITSET_SIZE(VCHIQ_MAX_SERVICES)];
|
|
+ int slot_queue_available;
|
|
+
|
|
+ /* Use a read memory barrier to ensure that any state that may have
|
|
+ been modified by another thread is not masked by stale prefetched
|
|
+ values. */
|
|
+ vcos_rmb();
|
|
+
|
|
+ /* Find slots which have been freed by the other side, and return them to
|
|
+ the available queue. */
|
|
+ slot_queue_available = state->slot_queue_available;
|
|
+
|
|
+ while (slot_queue_available != local->slot_queue_recycle)
|
|
+ {
|
|
+ int pos;
|
|
+ int slot_index = local->slot_queue[slot_queue_available++ & VCHIQ_SLOT_QUEUE_MASK];
|
|
+ char *data = (char *)SLOT_DATA_FROM_INDEX(state, slot_index);
|
|
+
|
|
+ vcos_log_trace("%d: pfq %d=%x %x %x", state->id, slot_index,
|
|
+ (unsigned int)data, local->slot_queue_recycle,
|
|
+ slot_queue_available);
|
|
+
|
|
+ /* Initialise the bitmask for services which have used this slot */
|
|
+ BITSET_ZERO(service_found);
|
|
+
|
|
+ pos = 0;
|
|
+
|
|
+ while (pos < VCHIQ_SLOT_SIZE)
|
|
+ {
|
|
+ VCHIQ_HEADER_T *header = (VCHIQ_HEADER_T *)(data + pos);
|
|
+ int msgid = header->msgid;
|
|
+ if (VCHIQ_MSG_TYPE(msgid) == VCHIQ_MSG_DATA)
|
|
+ {
|
|
+ int port = VCHIQ_MSG_SRCPORT(msgid);
|
|
+ if (!BITSET_IS_SET(service_found, port))
|
|
+ {
|
|
+ VCHIQ_SERVICE_QUOTA_T *service_quota =
|
|
+ &state->service_quotas[port];
|
|
+
|
|
+ /* Set the found bit for this service */
|
|
+ BITSET_SET(service_found, port);
|
|
+
|
|
+ if (service_quota->slot_use_count > 0)
|
|
+ {
|
|
+ service_quota->slot_use_count--;
|
|
+ /* Signal the service in case it has dropped below its quota */
|
|
+ vcos_event_signal(&service_quota->quota_event);
|
|
+ vcos_log_trace("%d: pfq:%d %x@%x - slot_use->%d",
|
|
+ state->id, port,
|
|
+ header->size, (unsigned int)header,
|
|
+ service_quota->slot_use_count);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ vcos_log_error("service %d slot_use_count=%d (header %x,"
|
|
+ " msgid %x, header->msgid %x, header->size %x)",
|
|
+ port, service_quota->slot_use_count,
|
|
+ (unsigned int)header, msgid, header->msgid,
|
|
+ header->size);
|
|
+ vcos_assert(0);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pos += calc_stride(header->size);
|
|
+ if (pos > VCHIQ_SLOT_SIZE)
|
|
+ {
|
|
+ vcos_log_error("pos %x: header %x, msgid %x, header->msgid %x, header->size %x",
|
|
+ pos, (unsigned int)header, msgid, header->msgid, header->size);
|
|
+ vcos_assert(0);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (slot_queue_available != state->slot_queue_available)
|
|
+ {
|
|
+ state->slot_queue_available = slot_queue_available;
|
|
+ vcos_wmb(&state->slot_queue_available);
|
|
+ vcos_event_signal(&state->slot_available_event);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Called by the slot handler and application threads */
|
|
+static VCHIQ_STATUS_T
|
|
+queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service,
|
|
+ int msgid, const VCHIQ_ELEMENT_T *elements,
|
|
+ int count, int size, int is_blocking)
|
|
+{
|
|
+ VCHIQ_SHARED_STATE_T *local;
|
|
+ VCHIQ_SERVICE_QUOTA_T *service_quota = NULL;
|
|
+ VCHIQ_HEADER_T *header;
|
|
+
|
|
+ unsigned int stride;
|
|
+
|
|
+ local = state->local;
|
|
+
|
|
+ stride = calc_stride(size);
|
|
+
|
|
+ vcos_assert(stride <= VCHIQ_SLOT_SIZE);
|
|
+
|
|
+ /* On platforms where vcos_mutex_lock cannot fail, the return will never
|
|
+ be taken and the compiler may optimise out that code. Let Coverity
|
|
+ know this is intentional.
|
|
+ */
|
|
+ /* coverity[constant_expression_result] */
|
|
+ if ((VCHIQ_MSG_TYPE(msgid) != VCHIQ_MSG_RESUME) &&
|
|
+ (vcos_mutex_lock(&state->slot_mutex) != VCOS_SUCCESS))
|
|
+ return VCHIQ_RETRY;
|
|
+
|
|
+ if (service)
|
|
+ {
|
|
+ int tx_end_index = SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos + stride - 1);
|
|
+
|
|
+ if (service->srvstate != VCHIQ_SRVSTATE_OPEN)
|
|
+ {
|
|
+ /* The service has been closed, probably while waiting for the mutex */
|
|
+ vcos_mutex_unlock(&state->slot_mutex);
|
|
+ return VCHIQ_ERROR;
|
|
+ }
|
|
+
|
|
+ service_quota = &state->service_quotas[service->localport];
|
|
+
|
|
+ /* ...ensure it doesn't use more than its quota of slots */
|
|
+ while ((tx_end_index != service_quota->previous_tx_index) &&
|
|
+ (service_quota->slot_use_count == service_quota->slot_quota))
|
|
+ {
|
|
+ vcos_log_trace("%d: qm:%d %s,%x - quota stall",
|
|
+ state->id, service->localport,
|
|
+ msg_type_str(VCHIQ_MSG_TYPE(msgid)), size);
|
|
+ VCHIQ_SERVICE_STATS_INC(service, quota_stalls);
|
|
+ vcos_mutex_unlock(&state->slot_mutex);
|
|
+ if (vcos_event_wait(&service_quota->quota_event) != VCOS_SUCCESS)
|
|
+ return VCHIQ_RETRY;
|
|
+ if (vcos_mutex_lock(&state->slot_mutex) != VCOS_SUCCESS)
|
|
+ return VCHIQ_RETRY;
|
|
+ vcos_assert(service_quota->slot_use_count <= service_quota->slot_quota);
|
|
+ tx_end_index = SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos + stride - 1);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ header = reserve_space(state, stride, is_blocking);
|
|
+
|
|
+ if (!header) {
|
|
+ if (service)
|
|
+ VCHIQ_SERVICE_STATS_INC(service, slot_stalls);
|
|
+ vcos_mutex_unlock(&state->slot_mutex);
|
|
+ return VCHIQ_RETRY;
|
|
+ }
|
|
+
|
|
+ if (service) {
|
|
+ int i, pos;
|
|
+ int tx_end_index;
|
|
+
|
|
+ vcos_log_info("%d: qm %s@%x,%x (%d->%d)", state->id,
|
|
+ msg_type_str(VCHIQ_MSG_TYPE(msgid)),
|
|
+ (unsigned int)header, size,
|
|
+ VCHIQ_MSG_SRCPORT(msgid),
|
|
+ VCHIQ_MSG_DSTPORT(msgid));
|
|
+
|
|
+ for (i = 0, pos = 0; i < (unsigned int)count;
|
|
+ pos += elements[i++].size)
|
|
+ if (elements[i].size) {
|
|
+ if (vchiq_copy_from_user
|
|
+ (header->data + pos, elements[i].data,
|
|
+ (size_t) elements[i].size) !=
|
|
+ VCHIQ_SUCCESS) {
|
|
+ vcos_mutex_unlock(&state->slot_mutex);
|
|
+ VCHIQ_SERVICE_STATS_INC(service, error_count);
|
|
+ return VCHIQ_ERROR;
|
|
+ }
|
|
+ if (i == 0) {
|
|
+ vcos_log_dump_mem( &vchiq_core_msg_log_category,
|
|
+ "Sent", 0, header->data + pos,
|
|
+ vcos_min( 64, elements[0].size ));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* If this transmission can't fit in the last slot used by this service... */
|
|
+ tx_end_index = SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos - 1);
|
|
+ if (tx_end_index != service_quota->previous_tx_index)
|
|
+ {
|
|
+ service_quota->slot_use_count++;
|
|
+ vcos_log_trace("%d: qm:%d %s,%x - slot_use->%d",
|
|
+ state->id, service->localport,
|
|
+ msg_type_str(VCHIQ_MSG_TYPE(msgid)), size,
|
|
+ service_quota->slot_use_count);
|
|
+ }
|
|
+
|
|
+ service_quota->previous_tx_index = tx_end_index;
|
|
+ VCHIQ_SERVICE_STATS_INC(service, ctrl_tx_count);
|
|
+ VCHIQ_SERVICE_STATS_ADD(service, ctrl_tx_bytes, size);
|
|
+ } else {
|
|
+ vcos_log_info("%d: qm %s@%x,%x (%d->%d)", state->id,
|
|
+ msg_type_str(VCHIQ_MSG_TYPE(msgid)),
|
|
+ (unsigned int)header, size,
|
|
+ VCHIQ_MSG_SRCPORT(msgid),
|
|
+ VCHIQ_MSG_DSTPORT(msgid));
|
|
+ if (size != 0)
|
|
+ {
|
|
+ vcos_assert((count == 1) && (size == elements[0].size));
|
|
+ memcpy(header->data, elements[0].data, elements[0].size);
|
|
+ }
|
|
+ VCHIQ_STATS_INC(state, ctrl_tx_count);
|
|
+ }
|
|
+
|
|
+ header->msgid = msgid;
|
|
+ header->size = size;
|
|
+
|
|
+ if (vcos_is_log_enabled( &vchiq_core_msg_log_category, VCOS_LOG_INFO))
|
|
+ {
|
|
+ int svc_fourcc;
|
|
+
|
|
+ svc_fourcc = service
|
|
+ ? service->base.fourcc
|
|
+ : VCHIQ_MAKE_FOURCC('?','?','?','?');
|
|
+
|
|
+ vcos_log_impl( &vchiq_core_msg_log_category,
|
|
+ VCOS_LOG_INFO,
|
|
+ "Sent Msg %s(%u) to %c%c%c%c s:%u d:%d len:%d",
|
|
+ msg_type_str(VCHIQ_MSG_TYPE(msgid)),
|
|
+ VCHIQ_MSG_TYPE(msgid),
|
|
+ VCHIQ_FOURCC_AS_4CHARS(svc_fourcc),
|
|
+ VCHIQ_MSG_SRCPORT(msgid),
|
|
+ VCHIQ_MSG_DSTPORT(msgid),
|
|
+ size );
|
|
+ }
|
|
+
|
|
+ /* Make the new tx_pos visible to the peer. */
|
|
+ local->tx_pos = state->local_tx_pos;
|
|
+ vcos_wmb(&local->tx_pos);
|
|
+
|
|
+ if (VCHIQ_MSG_TYPE(msgid) != VCHIQ_MSG_PAUSE)
|
|
+ vcos_mutex_unlock(&state->slot_mutex);
|
|
+
|
|
+ remote_event_signal(&state->remote->trigger);
|
|
+
|
|
+ return VCHIQ_SUCCESS;
|
|
+}
|
|
+
|
|
+static inline void
|
|
+claim_slot(VCHIQ_SLOT_INFO_T *slot)
|
|
+{
|
|
+ slot->use_count++;
|
|
+}
|
|
+
|
|
+static void
|
|
+release_slot(VCHIQ_STATE_T *state, VCHIQ_SLOT_INFO_T *slot_info)
|
|
+{
|
|
+ int release_count;
|
|
+ vcos_mutex_lock(&state->recycle_mutex);
|
|
+
|
|
+ release_count = slot_info->release_count;
|
|
+ slot_info->release_count = ++release_count;
|
|
+
|
|
+ if (release_count == slot_info->use_count)
|
|
+ {
|
|
+ int slot_queue_recycle;
|
|
+ /* Add to the freed queue */
|
|
+
|
|
+ /* A read barrier is necessary here to prevent speculative fetches of
|
|
+ remote->slot_queue_recycle from overtaking the mutex. */
|
|
+ vcos_rmb();
|
|
+
|
|
+ slot_queue_recycle = state->remote->slot_queue_recycle;
|
|
+ state->remote->slot_queue[slot_queue_recycle & VCHIQ_SLOT_QUEUE_MASK] =
|
|
+ SLOT_INDEX_FROM_INFO(state, slot_info);
|
|
+ state->remote->slot_queue_recycle = slot_queue_recycle + 1;
|
|
+ vcos_log_info("%d: release_slot %d - recycle->%x",
|
|
+ state->id, SLOT_INDEX_FROM_INFO(state, slot_info),
|
|
+ state->remote->slot_queue_recycle);
|
|
+
|
|
+ /* A write barrier is necessary, but remote_event_signal contains one. */
|
|
+ remote_event_signal(&state->remote->recycle);
|
|
+ }
|
|
+
|
|
+ vcos_mutex_unlock(&state->recycle_mutex);
|
|
+}
|
|
+
|
|
+/* Called by the slot handler - don't hold the bulk mutex */
|
|
+static VCHIQ_STATUS_T
|
|
+notify_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue)
|
|
+{
|
|
+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
|
|
+
|
|
+ vcos_log_trace("%d: nb:%d %cx - p=%x rn=%x r=%x",
|
|
+ service->state->id, service->localport,
|
|
+ (queue == &service->bulk_tx) ? 't' : 'r',
|
|
+ queue->process, queue->remote_notify, queue->remove);
|
|
+
|
|
+ if (service->state->is_master)
|
|
+ {
|
|
+ while (queue->remote_notify != queue->process)
|
|
+ {
|
|
+ VCHIQ_BULK_T *bulk = &queue->bulks[BULK_INDEX(queue->remote_notify)];
|
|
+ int msgtype = (bulk->dir == VCHIQ_BULK_TRANSMIT) ?
|
|
+ VCHIQ_MSG_BULK_RX_DONE : VCHIQ_MSG_BULK_TX_DONE;
|
|
+ int msgid = VCHIQ_MAKE_MSG(msgtype, service->localport, service->remoteport);
|
|
+ VCHIQ_ELEMENT_T element = { &bulk->actual, 4 };
|
|
+ /* Only reply to non-dummy bulk requests */
|
|
+ if (bulk->remote_data)
|
|
+ {
|
|
+ status = queue_message(service->state, NULL, msgid, &element, 1, 4, 0);
|
|
+ if (status != VCHIQ_SUCCESS)
|
|
+ break;
|
|
+ }
|
|
+ queue->remote_notify++;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ queue->remote_notify = queue->process;
|
|
+ }
|
|
+
|
|
+ if (status == VCHIQ_SUCCESS)
|
|
+ {
|
|
+ while (queue->remove != queue->remote_notify)
|
|
+ {
|
|
+ VCHIQ_BULK_T *bulk = &queue->bulks[BULK_INDEX(queue->remove)];
|
|
+
|
|
+ /* Only generate callbacks for non-dummy bulk requests */
|
|
+ if (bulk->data)
|
|
+ {
|
|
+ if (bulk->actual != VCHIQ_BULK_ACTUAL_ABORTED)
|
|
+ {
|
|
+ if (bulk->dir == VCHIQ_BULK_TRANSMIT)
|
|
+ {
|
|
+ VCHIQ_SERVICE_STATS_INC(service, bulk_tx_count);
|
|
+ VCHIQ_SERVICE_STATS_ADD(service, bulk_tx_bytes, bulk->actual);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ VCHIQ_SERVICE_STATS_INC(service, bulk_rx_count);
|
|
+ VCHIQ_SERVICE_STATS_ADD(service, bulk_rx_bytes, bulk->actual);
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ VCHIQ_SERVICE_STATS_INC(service, bulk_aborted_count);
|
|
+ }
|
|
+ if (bulk->mode == VCHIQ_BULK_MODE_BLOCKING)
|
|
+ {
|
|
+ BULK_WAITER_T *waiter = (BULK_WAITER_T *)bulk->userdata;
|
|
+ if (waiter)
|
|
+ {
|
|
+ waiter->actual = bulk->actual;
|
|
+ vcos_event_signal(&waiter->event);
|
|
+ }
|
|
+ }
|
|
+ else if (bulk->mode == VCHIQ_BULK_MODE_CALLBACK)
|
|
+ {
|
|
+ VCHIQ_REASON_T reason = (bulk->dir == VCHIQ_BULK_TRANSMIT) ?
|
|
+ ((bulk->actual == VCHIQ_BULK_ACTUAL_ABORTED) ?
|
|
+ VCHIQ_BULK_TRANSMIT_ABORTED : VCHIQ_BULK_TRANSMIT_DONE) :
|
|
+ ((bulk->actual == VCHIQ_BULK_ACTUAL_ABORTED) ?
|
|
+ VCHIQ_BULK_RECEIVE_ABORTED : VCHIQ_BULK_RECEIVE_DONE);
|
|
+ status = make_service_callback(service, reason,
|
|
+ NULL, bulk->userdata);
|
|
+ if (status == VCHIQ_RETRY)
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ queue->remove++;
|
|
+ vcos_event_signal(&service->bulk_remove_event);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (status != VCHIQ_SUCCESS)
|
|
+ request_poll(service->state, service, (queue == &service->bulk_tx) ?
|
|
+ VCHIQ_POLL_TXNOTIFY : VCHIQ_POLL_RXNOTIFY);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/* Called by the slot handler thread */
|
|
+static void
|
|
+poll_services(VCHIQ_STATE_T *state)
|
|
+{
|
|
+ int group, i;
|
|
+
|
|
+ for (group = 0; group < BITSET_SIZE(state->unused_service); group++)
|
|
+ {
|
|
+ uint32_t flags;
|
|
+ flags = vcos_atomic_flags_get_and_clear(&state->poll_services[group]);
|
|
+ for (i = 0; flags; i++)
|
|
+ {
|
|
+ if (flags & (1 << i))
|
|
+ {
|
|
+ VCHIQ_SERVICE_T *service = state->services[(group<<5) + i];
|
|
+ uint32_t service_flags =
|
|
+ vcos_atomic_flags_get_and_clear(&service->poll_flags);
|
|
+ if (service_flags & (1 << VCHIQ_POLL_TERMINATE))
|
|
+ {
|
|
+ vcos_log_info("%d: ps - terminate %d<->%d", state->id, service->localport, service->remoteport);
|
|
+ if (vchiq_close_service_internal(service, 0/*!close_recvd*/) != VCHIQ_SUCCESS)
|
|
+ request_poll(state, service, VCHIQ_POLL_TERMINATE);
|
|
+ }
|
|
+ if (service_flags & (1 << VCHIQ_POLL_TXNOTIFY))
|
|
+ notify_bulks(service, &service->bulk_tx);
|
|
+ if (service_flags & (1 << VCHIQ_POLL_RXNOTIFY))
|
|
+ notify_bulks(service, &service->bulk_rx);
|
|
+ flags &= ~(1 << i);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Called by the slot handler or application threads, holding the bulk mutex. */
|
|
+static int
|
|
+resolve_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue)
|
|
+{
|
|
+ VCHIQ_STATE_T *state = service->state;
|
|
+ int resolved = 0;
|
|
+
|
|
+ while ((queue->process != queue->local_insert) &&
|
|
+ (queue->process != queue->remote_insert))
|
|
+ {
|
|
+ VCHIQ_BULK_T *bulk = &queue->bulks[BULK_INDEX(queue->process)];
|
|
+
|
|
+ vcos_log_trace("%d: rb:%d %cx - li=%x ri=%x p=%x",
|
|
+ state->id, service->localport,
|
|
+ (queue == &service->bulk_tx) ? 't' : 'r',
|
|
+ queue->local_insert, queue->remote_insert,
|
|
+ queue->process);
|
|
+
|
|
+ vcos_assert((int)(queue->local_insert - queue->process) > 0);
|
|
+ vcos_assert((int)(queue->remote_insert - queue->process) > 0);
|
|
+ vchiq_transfer_bulk(bulk);
|
|
+
|
|
+ if (vcos_is_log_enabled( &vchiq_core_msg_log_category, VCOS_LOG_INFO))
|
|
+ {
|
|
+ const char *header = (queue == &service->bulk_tx) ?
|
|
+ "Send Bulk to" : "Recv Bulk from";
|
|
+ if (bulk->actual != VCHIQ_BULK_ACTUAL_ABORTED)
|
|
+ vcos_log_impl( &vchiq_core_msg_log_category,
|
|
+ VCOS_LOG_INFO,
|
|
+ "%s %c%c%c%c d:%d len:%d %x<->%x",
|
|
+ header,
|
|
+ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc),
|
|
+ service->remoteport,
|
|
+ bulk->size,
|
|
+ (unsigned int)bulk->data,
|
|
+ (unsigned int)bulk->remote_data );
|
|
+ else
|
|
+ vcos_log_impl( &vchiq_core_msg_log_category,
|
|
+ VCOS_LOG_INFO,
|
|
+ "%s %c%c%c%c d:%d ABORTED - tx len:%d, rx len:%d %x<->%x",
|
|
+ header,
|
|
+ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc),
|
|
+ service->remoteport,
|
|
+ bulk->size,
|
|
+ bulk->remote_size,
|
|
+ (unsigned int)bulk->data,
|
|
+ (unsigned int)bulk->remote_data );
|
|
+ }
|
|
+
|
|
+ vchiq_complete_bulk(bulk);
|
|
+ queue->process++;
|
|
+ resolved++;
|
|
+ }
|
|
+ return resolved;
|
|
+}
|
|
+
|
|
+/* Called with the bulk_mutex held */
|
|
+static void
|
|
+abort_outstanding_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue)
|
|
+{
|
|
+ int is_tx = (queue == &service->bulk_tx);
|
|
+ vcos_log_trace("%d: aob:%d %cx - li=%x ri=%x p=%x",
|
|
+ service->state->id, service->localport, is_tx ? 't' : 'r',
|
|
+ queue->local_insert, queue->remote_insert, queue->process);
|
|
+
|
|
+ vcos_assert((int)(queue->local_insert - queue->process) >= 0);
|
|
+ vcos_assert((int)(queue->remote_insert - queue->process) >= 0);
|
|
+
|
|
+ while ((queue->process != queue->local_insert) ||
|
|
+ (queue->process != queue->remote_insert))
|
|
+ {
|
|
+ VCHIQ_BULK_T *bulk = &queue->bulks[BULK_INDEX(queue->process)];
|
|
+
|
|
+ if (queue->process == queue->remote_insert)
|
|
+ {
|
|
+ /* fabricate a matching dummy bulk */
|
|
+ bulk->remote_data = NULL;
|
|
+ bulk->remote_size = 0;
|
|
+ queue->remote_insert++;
|
|
+ }
|
|
+
|
|
+ if (queue->process != queue->local_insert)
|
|
+ {
|
|
+ vchiq_complete_bulk(bulk);
|
|
+
|
|
+ if (vcos_is_log_enabled( &vchiq_core_msg_log_category, VCOS_LOG_INFO))
|
|
+ {
|
|
+ vcos_log_impl( &vchiq_core_msg_log_category,
|
|
+ VCOS_LOG_INFO,
|
|
+ "%s %c%c%c%c d:%d ABORTED - tx len:%d, rx len:%d",
|
|
+ is_tx ? "Send Bulk to" : "Recv Bulk from",
|
|
+ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc),
|
|
+ service->remoteport,
|
|
+ bulk->size,
|
|
+ bulk->remote_size );
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* fabricate a matching dummy bulk */
|
|
+ bulk->data = NULL;
|
|
+ bulk->size = 0;
|
|
+ bulk->actual = VCHIQ_BULK_ACTUAL_ABORTED;
|
|
+ bulk->dir = is_tx ? VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE;
|
|
+ queue->local_insert++;
|
|
+ }
|
|
+
|
|
+ queue->process++;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+pause_bulks(VCHIQ_STATE_T *state)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ /* Block bulk transfers from all services */
|
|
+ for (i = 0; i < state->unused_service; i++)
|
|
+ {
|
|
+ VCHIQ_SERVICE_T *service = state->services[i];
|
|
+ if (!service || (service->srvstate != VCHIQ_SRVSTATE_OPEN))
|
|
+ continue;
|
|
+
|
|
+ vcos_log_trace("locking bulk_mutex for service %d", i);
|
|
+ vcos_mutex_lock(&service->bulk_mutex);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+resume_bulks(VCHIQ_STATE_T *state)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ /* Poll all services in case any bulk transfers have been
|
|
+ deferred */
|
|
+ for (i = 0; i < state->unused_service; i++)
|
|
+ {
|
|
+ VCHIQ_SERVICE_T *service = state->services[i];
|
|
+ if (!service || (service->srvstate != VCHIQ_SRVSTATE_OPEN))
|
|
+ continue;
|
|
+
|
|
+ if (resolve_bulks(service, &service->bulk_tx))
|
|
+ request_poll(state, service, VCHIQ_POLL_TXNOTIFY);
|
|
+ if (resolve_bulks(service, &service->bulk_rx))
|
|
+ request_poll(state, service, VCHIQ_POLL_RXNOTIFY);
|
|
+ vcos_log_trace("unlocking bulk_mutex for service %d", i);
|
|
+ vcos_mutex_unlock(&service->bulk_mutex);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Called by the slot handler thread */
|
|
+static void
|
|
+parse_rx_slots(VCHIQ_STATE_T *state)
|
|
+{
|
|
+ VCHIQ_SHARED_STATE_T *remote = state->remote;
|
|
+ int tx_pos;
|
|
+ DEBUG_INITIALISE(state->local)
|
|
+
|
|
+ tx_pos = remote->tx_pos;
|
|
+
|
|
+ while (state->rx_pos != tx_pos) {
|
|
+ VCHIQ_SERVICE_T *service = NULL;
|
|
+ VCHIQ_HEADER_T *header;
|
|
+ int msgid, size;
|
|
+ int type;
|
|
+ unsigned int localport, remoteport;
|
|
+
|
|
+ DEBUG_TRACE(PARSE_LINE);
|
|
+ if (!state->rx_data)
|
|
+ {
|
|
+ int rx_index;
|
|
+ vcos_assert((state->rx_pos & VCHIQ_SLOT_MASK) == 0);
|
|
+ rx_index = remote->slot_queue[SLOT_QUEUE_INDEX_FROM_POS(state->rx_pos) & VCHIQ_SLOT_QUEUE_MASK];
|
|
+ state->rx_data = (char *)SLOT_DATA_FROM_INDEX(state, rx_index);
|
|
+ state->rx_info = SLOT_INFO_FROM_INDEX(state, rx_index);
|
|
+
|
|
+ /* Initialise use_count to one, and increment release_count at the end
|
|
+ of the slot to avoid releasing the slot prematurely. */
|
|
+ state->rx_info->use_count = 1;
|
|
+ state->rx_info->release_count = 0;
|
|
+ }
|
|
+
|
|
+ header = (VCHIQ_HEADER_T *)(state->rx_data + (state->rx_pos & VCHIQ_SLOT_MASK));
|
|
+ DEBUG_VALUE(PARSE_HEADER, (int)header);
|
|
+ msgid = header->msgid;
|
|
+ DEBUG_VALUE(PARSE_MSGID, msgid);
|
|
+ size = header->size;
|
|
+ type = VCHIQ_MSG_TYPE(msgid);
|
|
+ localport = VCHIQ_MSG_DSTPORT(msgid);
|
|
+ remoteport = VCHIQ_MSG_SRCPORT(msgid);
|
|
+
|
|
+ if (type != VCHIQ_MSG_DATA)
|
|
+ {
|
|
+ VCHIQ_STATS_INC(state, ctrl_rx_count);
|
|
+ }
|
|
+
|
|
+ switch (type)
|
|
+ {
|
|
+ case VCHIQ_MSG_OPENACK:
|
|
+ case VCHIQ_MSG_CLOSE:
|
|
+ case VCHIQ_MSG_DATA:
|
|
+ case VCHIQ_MSG_BULK_RX:
|
|
+ case VCHIQ_MSG_BULK_TX:
|
|
+ case VCHIQ_MSG_BULK_RX_DONE:
|
|
+ case VCHIQ_MSG_BULK_TX_DONE:
|
|
+ if (localport <= VCHIQ_PORT_MAX)
|
|
+ {
|
|
+ service = state->services[localport];
|
|
+ if (service && (service->srvstate == VCHIQ_SRVSTATE_FREE))
|
|
+ service = NULL;
|
|
+ }
|
|
+ if (!service)
|
|
+ {
|
|
+ vcos_log_error(
|
|
+ "%d: prs %s@%x (%d->%d) - invalid/closed service %d",
|
|
+ state->id, msg_type_str(type), (unsigned int)header,
|
|
+ remoteport, localport, localport);
|
|
+ goto skip_message;
|
|
+ }
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if ( vcos_is_log_enabled( &vchiq_core_msg_log_category, VCOS_LOG_INFO))
|
|
+ {
|
|
+ int svc_fourcc;
|
|
+
|
|
+ svc_fourcc = service
|
|
+ ? service->base.fourcc
|
|
+ : VCHIQ_MAKE_FOURCC('?','?','?','?');
|
|
+ vcos_log_impl( &vchiq_core_msg_log_category,
|
|
+ VCOS_LOG_INFO,
|
|
+ "Rcvd Msg %s(%u) from %c%c%c%c s:%d d:%d len:%d",
|
|
+ msg_type_str(type), type,
|
|
+ VCHIQ_FOURCC_AS_4CHARS(svc_fourcc),
|
|
+ remoteport, localport, size );
|
|
+ if (size > 0) {
|
|
+ vcos_log_dump_mem( &vchiq_core_msg_log_category,
|
|
+ "Rcvd", 0, header->data,
|
|
+ vcos_min( 64, size ));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (((unsigned int)header & VCHIQ_SLOT_MASK) + calc_stride(size) > VCHIQ_SLOT_SIZE)
|
|
+ {
|
|
+ vcos_log_error("header %x (msgid %x) - size %x too big for slot",
|
|
+ (unsigned int)header, (unsigned int)msgid, (unsigned int)size);
|
|
+ vcos_assert(0);
|
|
+ }
|
|
+
|
|
+ switch (type) {
|
|
+ case VCHIQ_MSG_OPEN:
|
|
+ vcos_assert(VCHIQ_MSG_DSTPORT(msgid) == 0);
|
|
+ if (vcos_verify(size == sizeof(VCHIQ_OPEN_PAYLOAD_T))) {
|
|
+ const VCHIQ_OPEN_PAYLOAD_T *payload = (VCHIQ_OPEN_PAYLOAD_T *)header->data;
|
|
+ unsigned int fourcc;
|
|
+
|
|
+ fourcc = payload->fourcc;
|
|
+ vcos_log_info("%d: prs OPEN@%x (%d->'%c%c%c%c')",
|
|
+ state->id, (unsigned int)header,
|
|
+ localport,
|
|
+ VCHIQ_FOURCC_AS_4CHARS(fourcc));
|
|
+
|
|
+ service = get_listening_service(state, fourcc);
|
|
+
|
|
+ if (service)
|
|
+ {
|
|
+ /* A matching service exists */
|
|
+ short version = payload->version;
|
|
+ short version_min = payload->version_min;
|
|
+ if ((service->version < version_min) ||
|
|
+ (version < service->version_min))
|
|
+ {
|
|
+ /* Version mismatch */
|
|
+ vcos_log_error("%d: service %d (%c%c%c%c) version mismatch -"
|
|
+ " local (%d, min %d) vs. remote (%d, min %d)",
|
|
+ state->id, service->localport,
|
|
+ VCHIQ_FOURCC_AS_4CHARS(fourcc),
|
|
+ service->version, service->version_min,
|
|
+ version, version_min);
|
|
+ goto fail_open;
|
|
+ }
|
|
+ if (service->srvstate == VCHIQ_SRVSTATE_LISTENING)
|
|
+ {
|
|
+ /* Acknowledge the OPEN */
|
|
+ if (queue_message(state, NULL,
|
|
+ VCHIQ_MAKE_MSG(VCHIQ_MSG_OPENACK, service->localport, remoteport),
|
|
+ NULL, 0, 0, 0) == VCHIQ_RETRY)
|
|
+ return; /* Bail out if not ready */
|
|
+
|
|
+ /* The service is now open */
|
|
+ vchiq_set_service_state(service, VCHIQ_SRVSTATE_OPEN);
|
|
+ }
|
|
+
|
|
+ service->remoteport = remoteport;
|
|
+ service->client_id = ((int *)header->data)[1];
|
|
+ if (make_service_callback(service, VCHIQ_SERVICE_OPENED,
|
|
+ NULL, NULL) == VCHIQ_RETRY)
|
|
+ {
|
|
+ /* Bail out if not ready */
|
|
+ service->remoteport = VCHIQ_PORT_FREE;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Break out, and skip the failure handling */
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ fail_open:
|
|
+ /* No available service, or an invalid request - send a CLOSE */
|
|
+ if (queue_message(state, NULL,
|
|
+ VCHIQ_MAKE_MSG(VCHIQ_MSG_CLOSE, 0, VCHIQ_MSG_SRCPORT(msgid)),
|
|
+ NULL, 0, 0, 0) == VCHIQ_RETRY)
|
|
+ return; /* Bail out if not ready */
|
|
+ break;
|
|
+ case VCHIQ_MSG_OPENACK:
|
|
+ {
|
|
+ vcos_log_info("%d: prs OPENACK@%x (%d->%d)",
|
|
+ state->id, (unsigned int)header,
|
|
+ remoteport, localport);
|
|
+ if (service->srvstate == VCHIQ_SRVSTATE_OPENING) {
|
|
+ service->remoteport = remoteport;
|
|
+ vchiq_set_service_state(service,
|
|
+ VCHIQ_SRVSTATE_OPEN);
|
|
+ vcos_event_signal(&service->remove_event);
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ case VCHIQ_MSG_CLOSE:
|
|
+ {
|
|
+ vcos_assert(size == 0); /* There should be no data */
|
|
+
|
|
+ vcos_log_info("%d: prs CLOSE@%x (%d->%d)",
|
|
+ state->id, (unsigned int)header,
|
|
+ remoteport, localport);
|
|
+
|
|
+ if ((service->remoteport != remoteport) &&
|
|
+ VCHIQ_PORT_IS_VALID(service->remoteport)) {
|
|
+ /* This could be from a client which hadn't yet received
|
|
+ the OPENACK - look for the connected service */
|
|
+ service = get_connected_service(state, remoteport);
|
|
+ if (!service)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (vchiq_close_service_internal(service,
|
|
+ 1/*close_recvd*/) == VCHIQ_RETRY)
|
|
+ return; /* Bail out if not ready */
|
|
+
|
|
+ if (vcos_is_log_enabled( &vchiq_core_msg_log_category, VCOS_LOG_INFO))
|
|
+ {
|
|
+ vcos_log_impl( &vchiq_core_msg_log_category,
|
|
+ VCOS_LOG_INFO,
|
|
+ "Close Service %c%c%c%c s:%u d:%d",
|
|
+ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc),
|
|
+ service->localport,
|
|
+ service->remoteport );
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ case VCHIQ_MSG_DATA:
|
|
+ {
|
|
+ vcos_log_trace("%d: prs DATA@%x,%x (%d->%d)",
|
|
+ state->id, (unsigned int)header, size,
|
|
+ remoteport, localport);
|
|
+
|
|
+ if ((service->remoteport == remoteport)
|
|
+ && (service->srvstate ==
|
|
+ VCHIQ_SRVSTATE_OPEN)) {
|
|
+ header->msgid = msgid | VCHIQ_MSGID_CLAIMED;
|
|
+ claim_slot(state->rx_info);
|
|
+ DEBUG_TRACE(PARSE_LINE);
|
|
+ if (make_service_callback(service,
|
|
+ VCHIQ_MESSAGE_AVAILABLE, header,
|
|
+ NULL) == VCHIQ_RETRY)
|
|
+ {
|
|
+ DEBUG_TRACE(PARSE_LINE);
|
|
+ return; /* Bail out if not ready */
|
|
+ }
|
|
+ VCHIQ_SERVICE_STATS_INC(service, ctrl_rx_count);
|
|
+ VCHIQ_SERVICE_STATS_ADD(service, ctrl_rx_bytes, size);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ VCHIQ_STATS_INC(state, error_count);
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ case VCHIQ_MSG_CONNECT:
|
|
+ vcos_log_info("%d: prs CONNECT@%x",
|
|
+ state->id, (unsigned int)header);
|
|
+ vcos_event_signal(&state->connect);
|
|
+ break;
|
|
+ case VCHIQ_MSG_BULK_RX:
|
|
+ case VCHIQ_MSG_BULK_TX:
|
|
+ {
|
|
+ VCHIQ_BULK_QUEUE_T *queue;
|
|
+ vcos_assert(state->is_master);
|
|
+ queue = (type == VCHIQ_MSG_BULK_RX) ?
|
|
+ &service->bulk_tx : &service->bulk_rx;
|
|
+ if ((service->remoteport == remoteport)
|
|
+ && (service->srvstate ==
|
|
+ VCHIQ_SRVSTATE_OPEN))
|
|
+ {
|
|
+ VCHIQ_BULK_T *bulk;
|
|
+ int resolved;
|
|
+
|
|
+ vcos_assert(queue->remote_insert < queue->remove +
|
|
+ VCHIQ_NUM_SERVICE_BULKS);
|
|
+ bulk = &queue->bulks[BULK_INDEX(queue->remote_insert)];
|
|
+ bulk->remote_data = (void *)((int *)header->data)[0];
|
|
+ bulk->remote_size = ((int *)header->data)[1];
|
|
+
|
|
+ vcos_log_info("%d: prs %s@%x (%d->%d) %x@%x",
|
|
+ state->id, msg_type_str(type),
|
|
+ (unsigned int)header,
|
|
+ remoteport, localport,
|
|
+ bulk->remote_size,
|
|
+ (unsigned int)bulk->remote_data);
|
|
+
|
|
+ queue->remote_insert++;
|
|
+
|
|
+ if (state->conn_state != VCHIQ_CONNSTATE_CONNECTED)
|
|
+ break;
|
|
+
|
|
+ DEBUG_TRACE(PARSE_LINE);
|
|
+ if (vcos_mutex_lock(&service->bulk_mutex) != VCOS_SUCCESS)
|
|
+ {
|
|
+ DEBUG_TRACE(PARSE_LINE);
|
|
+ return;
|
|
+ }
|
|
+ DEBUG_TRACE(PARSE_LINE);
|
|
+ resolved = resolve_bulks(service, queue);
|
|
+ vcos_mutex_unlock(&service->bulk_mutex);
|
|
+ if (resolved)
|
|
+ notify_bulks(service, queue);
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ case VCHIQ_MSG_BULK_RX_DONE:
|
|
+ case VCHIQ_MSG_BULK_TX_DONE:
|
|
+ {
|
|
+ vcos_assert(!state->is_master);
|
|
+ if ((service->remoteport == remoteport)
|
|
+ && (service->srvstate !=
|
|
+ VCHIQ_SRVSTATE_FREE)) {
|
|
+ VCHIQ_BULK_QUEUE_T *queue;
|
|
+ VCHIQ_BULK_T *bulk;
|
|
+
|
|
+ queue = (type == VCHIQ_MSG_BULK_RX_DONE) ?
|
|
+ &service->bulk_rx : &service->bulk_tx;
|
|
+
|
|
+ bulk = &queue->bulks[BULK_INDEX(queue->process)];
|
|
+ bulk->actual = *(int *)header->data;
|
|
+
|
|
+ vcos_log_info("%d: prs %s@%x (%d->%d) %x@%x",
|
|
+ state->id, msg_type_str(type),
|
|
+ (unsigned int)header,
|
|
+ remoteport, localport,
|
|
+ bulk->actual, (unsigned int)bulk->data);
|
|
+
|
|
+ vcos_log_trace("%d: prs:%d %cx li=%x ri=%x p=%x",
|
|
+ state->id, localport,
|
|
+ (type == VCHIQ_MSG_BULK_RX_DONE) ? 'r' : 't',
|
|
+ queue->local_insert,
|
|
+ queue->remote_insert, queue->process);
|
|
+
|
|
+ DEBUG_TRACE(PARSE_LINE);
|
|
+ if (vcos_mutex_lock(&service->bulk_mutex) != VCOS_SUCCESS)
|
|
+ {
|
|
+ DEBUG_TRACE(PARSE_LINE);
|
|
+ return;
|
|
+ }
|
|
+ DEBUG_TRACE(PARSE_LINE);
|
|
+ vcos_assert(queue->process != queue->local_insert);
|
|
+ vchiq_complete_bulk(bulk);
|
|
+ queue->process++;
|
|
+ vcos_mutex_unlock(&service->bulk_mutex);
|
|
+ DEBUG_TRACE(PARSE_LINE);
|
|
+ notify_bulks(service, queue);
|
|
+ DEBUG_TRACE(PARSE_LINE);
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ case VCHIQ_MSG_PADDING:
|
|
+ vcos_log_trace("%d: prs PADDING@%x,%x",
|
|
+ state->id, (unsigned int)header, size);
|
|
+ break;
|
|
+ case VCHIQ_MSG_PAUSE:
|
|
+ /* If initiated, signal the application thread */
|
|
+ vcos_log_trace("%d: prs PAUSE@%x,%x",
|
|
+ state->id, (unsigned int)header, size);
|
|
+ if (state->conn_state != VCHIQ_CONNSTATE_PAUSE_SENT)
|
|
+ {
|
|
+ /* Send a PAUSE in response */
|
|
+ if (queue_message(state, NULL,
|
|
+ VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE, 0, 0),
|
|
+ NULL, 0, 0, 0) == VCHIQ_RETRY)
|
|
+ return; /* Bail out if not ready */
|
|
+ if (state->is_master)
|
|
+ pause_bulks(state);
|
|
+ }
|
|
+ /* At this point slot_mutex is held */
|
|
+ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_PAUSED);
|
|
+ vchiq_platform_paused(state);
|
|
+ break;
|
|
+ case VCHIQ_MSG_RESUME:
|
|
+ vcos_log_trace("%d: prs RESUME@%x,%x",
|
|
+ state->id, (unsigned int)header, size);
|
|
+ /* Release the slot mutex */
|
|
+ vcos_mutex_unlock(&state->slot_mutex);
|
|
+ if (state->is_master)
|
|
+ resume_bulks(state);
|
|
+ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED);
|
|
+ vchiq_platform_resumed(state);
|
|
+ break;
|
|
+ default:
|
|
+ vcos_log_error("%d: prs invalid msgid %x@%x,%x",
|
|
+ state->id, msgid, (unsigned int)header, size);
|
|
+ vcos_assert(0);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ skip_message:
|
|
+ state->rx_pos += calc_stride(size);
|
|
+
|
|
+ DEBUG_TRACE(PARSE_LINE);
|
|
+ /* Perform some housekeeping when the end of the slot is reached. */
|
|
+ if ((state->rx_pos & VCHIQ_SLOT_MASK) == 0)
|
|
+ {
|
|
+ /* Remove the extra reference count. */
|
|
+ release_slot(state, state->rx_info);
|
|
+ state->rx_data = NULL;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Called by the slot handler thread */
|
|
+static void *
|
|
+slot_handler_func(void *v)
|
|
+{
|
|
+ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v;
|
|
+ VCHIQ_SHARED_STATE_T *local = state->local;
|
|
+ DEBUG_INITIALISE(local)
|
|
+
|
|
+ while (1) {
|
|
+ DEBUG_COUNT(SLOT_HANDLER_COUNT);
|
|
+ DEBUG_TRACE(SLOT_HANDLER_LINE);
|
|
+ remote_event_wait(&local->trigger);
|
|
+
|
|
+ vcos_rmb();
|
|
+
|
|
+ DEBUG_TRACE(SLOT_HANDLER_LINE);
|
|
+ if (state->poll_needed)
|
|
+ {
|
|
+ state->poll_needed = 0;
|
|
+
|
|
+ /* Handle service polling and other rare conditions here out
|
|
+ of the mainline code */
|
|
+ switch (state->conn_state)
|
|
+ {
|
|
+ case VCHIQ_CONNSTATE_CONNECTED:
|
|
+ /* Poll the services as requested */
|
|
+ poll_services(state);
|
|
+ break;
|
|
+
|
|
+ case VCHIQ_CONNSTATE_PAUSING:
|
|
+ if (queue_message(state, NULL,
|
|
+ VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE, 0, 0), NULL, 0, 0, 0)
|
|
+ != VCHIQ_RETRY)
|
|
+ {
|
|
+ if (state->is_master)
|
|
+ pause_bulks(state);
|
|
+ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_PAUSE_SENT);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ state->poll_needed = 1; /* Retry later */
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case VCHIQ_CONNSTATE_RESUMING:
|
|
+ if (queue_message(state, NULL,
|
|
+ VCHIQ_MAKE_MSG(VCHIQ_MSG_RESUME, 0, 0), NULL, 0, 0, 0)
|
|
+ != VCHIQ_RETRY)
|
|
+ {
|
|
+ if (state->is_master)
|
|
+ resume_bulks(state);
|
|
+ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED);
|
|
+ vchiq_platform_resumed(state);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* This should really be impossible, since the PAUSE should
|
|
+ have flushed through outstanding messages. */
|
|
+ vcos_log_error("Failed to send RESUME message");
|
|
+ vcos_demand(0);
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ DEBUG_TRACE(SLOT_HANDLER_LINE);
|
|
+ parse_rx_slots(state);
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+extern VCHIQ_STATUS_T
|
|
+vchiq_platform_suspend(VCHIQ_STATE_T *state);
|
|
+
|
|
+/* Called by the recycle thread */
|
|
+static void *
|
|
+recycle_func(void *v)
|
|
+{
|
|
+ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v;
|
|
+ VCHIQ_SHARED_STATE_T *local = state->local;
|
|
+
|
|
+ while (1) {
|
|
+ remote_event_wait(&local->recycle);
|
|
+
|
|
+ vcos_mutex_lock(&state->slot_mutex);
|
|
+
|
|
+ process_free_queue(state);
|
|
+
|
|
+ vcos_mutex_unlock(&state->slot_mutex);
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/* Called by the lp thread */
|
|
+static void *
|
|
+lp_func(void *v)
|
|
+{
|
|
+ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v;
|
|
+
|
|
+ while (1) {
|
|
+ vcos_event_wait(&state->lp_evt);
|
|
+ vcos_mutex_lock(&state->use_count_mutex);
|
|
+ if (state->videocore_use_count == 0)
|
|
+ {
|
|
+ vchiq_platform_suspend(state);
|
|
+ }
|
|
+ vcos_mutex_unlock(&state->use_count_mutex);
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void
|
|
+init_bulk_queue(VCHIQ_BULK_QUEUE_T *queue)
|
|
+{
|
|
+ queue->local_insert = 0;
|
|
+ queue->remote_insert = 0;
|
|
+ queue->process = 0;
|
|
+ queue->remote_notify = 0;
|
|
+ queue->remove = 0;
|
|
+}
|
|
+
|
|
+VCHIQ_SLOT_ZERO_T *
|
|
+vchiq_init_slots(void *mem_base, int mem_size)
|
|
+{
|
|
+ int mem_align = (VCHIQ_SLOT_SIZE - (int)mem_base) & VCHIQ_SLOT_MASK;
|
|
+ VCHIQ_SLOT_ZERO_T *slot_zero = (VCHIQ_SLOT_ZERO_T *)((char *)mem_base + mem_align);
|
|
+ int num_slots = (mem_size - mem_align)/VCHIQ_SLOT_SIZE;
|
|
+ int first_data_slot = VCHIQ_SLOT_ZERO_SLOTS;
|
|
+
|
|
+ /* Ensure there is enough memory to run an absolutely minimum system */
|
|
+ num_slots -= first_data_slot;
|
|
+
|
|
+ if (num_slots < 4)
|
|
+ {
|
|
+ vcos_log_error("vchiq_init_slots - insufficient memory %x bytes", mem_size);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ memset(slot_zero, 0, sizeof(VCHIQ_SLOT_ZERO_T));
|
|
+
|
|
+ slot_zero->magic = VCHIQ_MAGIC;
|
|
+ slot_zero->version = VCHIQ_VERSION;
|
|
+ slot_zero->version_min = VCHIQ_VERSION_MIN;
|
|
+ slot_zero->slot_zero_size = sizeof(VCHIQ_SLOT_ZERO_T);
|
|
+ slot_zero->slot_size = VCHIQ_SLOT_SIZE;
|
|
+ slot_zero->max_slots = VCHIQ_MAX_SLOTS;
|
|
+ slot_zero->max_slots_per_side = VCHIQ_MAX_SLOTS_PER_SIDE;
|
|
+
|
|
+ slot_zero->master.slot_first = first_data_slot;
|
|
+ slot_zero->slave.slot_first = first_data_slot + (num_slots/2);
|
|
+ slot_zero->master.slot_last = slot_zero->slave.slot_first - 1;
|
|
+ slot_zero->slave.slot_last = first_data_slot + num_slots - 1;
|
|
+
|
|
+ return slot_zero;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_init_state(VCHIQ_STATE_T *state, VCHIQ_SLOT_ZERO_T *slot_zero, int is_master)
|
|
+{
|
|
+ VCHIQ_SHARED_STATE_T *local;
|
|
+ VCHIQ_SHARED_STATE_T *remote;
|
|
+ VCOS_THREAD_ATTR_T attrs;
|
|
+ char threadname[10];
|
|
+ static int id = 0;
|
|
+ int i;
|
|
+
|
|
+ vcos_log_set_level(&vchiq_core_log_category, vchiq_default_core_log_level);
|
|
+ vcos_log_set_level(&vchiq_core_msg_log_category, vchiq_default_core_msg_log_level);
|
|
+ vcos_log_register("vchiq_core", &vchiq_core_log_category);
|
|
+ vcos_log_register("vchiq_core_msg", &vchiq_core_msg_log_category);
|
|
+
|
|
+ vcos_log_warn( "%s: slot_zero = 0x%08lx, is_master = %d\n", __func__, (unsigned long)slot_zero, is_master );
|
|
+
|
|
+ /* Check the input configuration */
|
|
+
|
|
+ if (slot_zero->magic != VCHIQ_MAGIC)
|
|
+ {
|
|
+ vcos_log_error("slot_zero=%x: magic=%x (expected %x)",
|
|
+ (unsigned int)slot_zero, slot_zero->magic, VCHIQ_MAGIC);
|
|
+ return VCHIQ_ERROR;
|
|
+ }
|
|
+
|
|
+ if (slot_zero->version < VCHIQ_VERSION_MIN)
|
|
+ {
|
|
+ vcos_log_error("slot_zero=%x: peer_version=%x (minimum %x)",
|
|
+ (unsigned int)slot_zero, slot_zero->version, VCHIQ_VERSION_MIN);
|
|
+ return VCHIQ_ERROR;
|
|
+ }
|
|
+
|
|
+ if (VCHIQ_VERSION < slot_zero->version_min)
|
|
+ {
|
|
+ vcos_log_error("slot_zero=%x: version=%x (peer minimum %x)",
|
|
+ (unsigned int)slot_zero, VCHIQ_VERSION, slot_zero->version_min);
|
|
+ return VCHIQ_ERROR;
|
|
+ }
|
|
+
|
|
+ if (slot_zero->slot_zero_size != sizeof(VCHIQ_SLOT_ZERO_T))
|
|
+ {
|
|
+ vcos_log_error("slot_zero=%x: slot_zero_size=%x (expected %x)",
|
|
+ (unsigned int)slot_zero, slot_zero->slot_zero_size, sizeof(VCHIQ_SLOT_ZERO_T));
|
|
+ return VCHIQ_ERROR;
|
|
+ }
|
|
+
|
|
+ if (slot_zero->slot_size != VCHIQ_SLOT_SIZE)
|
|
+ {
|
|
+ vcos_log_error("slot_zero=%x: slot_size=%d (expected %d",
|
|
+ (unsigned int)slot_zero, slot_zero->slot_size, VCHIQ_SLOT_SIZE);
|
|
+ return VCHIQ_ERROR;
|
|
+ }
|
|
+
|
|
+ if (slot_zero->max_slots != VCHIQ_MAX_SLOTS)
|
|
+ {
|
|
+ vcos_log_error("slot_zero=%x: max_slots=%d (expected %d)",
|
|
+ (unsigned int)slot_zero, slot_zero->max_slots, VCHIQ_MAX_SLOTS);
|
|
+ return VCHIQ_ERROR;
|
|
+ }
|
|
+
|
|
+ if (slot_zero->max_slots_per_side != VCHIQ_MAX_SLOTS_PER_SIDE)
|
|
+ {
|
|
+ vcos_log_error("slot_zero=%x: max_slots_per_side=%d (expected %d)",
|
|
+ (unsigned int)slot_zero, slot_zero->max_slots_per_side,
|
|
+ VCHIQ_MAX_SLOTS_PER_SIDE);
|
|
+ return VCHIQ_ERROR;
|
|
+ }
|
|
+
|
|
+ if (is_master)
|
|
+ {
|
|
+ local = &slot_zero->master;
|
|
+ remote = &slot_zero->slave;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ local = &slot_zero->slave;
|
|
+ remote = &slot_zero->master;
|
|
+ }
|
|
+
|
|
+ if (local->initialised)
|
|
+ {
|
|
+ if (remote->initialised)
|
|
+ vcos_log_error("vchiq: FATAL: local state has already been initialised");
|
|
+ else
|
|
+ vcos_log_error("vchiq: FATAL: master/slave mismatch - two %ss", is_master ? "master" : "slave");
|
|
+ return VCHIQ_ERROR;
|
|
+ }
|
|
+
|
|
+ memset(state, 0, sizeof(VCHIQ_STATE_T));
|
|
+ state->id = id++;
|
|
+ state->is_master = is_master;
|
|
+
|
|
+ /*
|
|
+ initialize shared state pointers
|
|
+ */
|
|
+
|
|
+ state->local = local;
|
|
+ state->remote = remote;
|
|
+ state->slot_data = (VCHIQ_SLOT_T *)slot_zero;
|
|
+
|
|
+ /*
|
|
+ initialize events and mutexes
|
|
+ */
|
|
+
|
|
+ vcos_event_create(&state->connect, "v.connect");
|
|
+ vcos_mutex_create(&state->mutex, "v.mutex");
|
|
+ vcos_event_create(&state->trigger_event, "v.trigger_event");
|
|
+ vcos_event_create(&state->recycle_event, "v.recycle_event");
|
|
+
|
|
+ vcos_mutex_create(&state->slot_mutex, "v.slot_mutex");
|
|
+ vcos_mutex_create(&state->recycle_mutex, "v.recycle_mutex");
|
|
+ vcos_mutex_create(&state->use_count_mutex, "v.use_count_mutex");
|
|
+ vcos_mutex_create(&state->suspend_resume_mutex, "v.susp_res_mutex");
|
|
+
|
|
+ vcos_event_create(&state->slot_available_event, "v.slot_available_event");
|
|
+ vcos_event_create(&state->slot_remove_event, "v.slot_remove_event");
|
|
+
|
|
+ state->slot_queue_available = 0;
|
|
+
|
|
+ for (i = 0; i < VCHIQ_MAX_SERVICES; i++)
|
|
+ {
|
|
+ VCHIQ_SERVICE_QUOTA_T *service_quota = &state->service_quotas[i];
|
|
+ vcos_event_create(&service_quota->quota_event, "v.quota_event");
|
|
+ }
|
|
+
|
|
+ for (i = local->slot_first; i <= local->slot_last; i++)
|
|
+ {
|
|
+ local->slot_queue[state->slot_queue_available++] = i;
|
|
+ }
|
|
+
|
|
+ state->default_slot_quota = state->slot_queue_available/2;
|
|
+
|
|
+ local->trigger.event = &state->trigger_event;
|
|
+ remote_event_create(&local->trigger);
|
|
+ local->tx_pos = 0;
|
|
+
|
|
+ local->recycle.event = &state->recycle_event;
|
|
+ remote_event_create(&local->recycle);
|
|
+ local->slot_queue_recycle = state->slot_queue_available;
|
|
+
|
|
+ vcos_event_create(&state->lp_evt, "LP_EVT");
|
|
+
|
|
+ local->debug[DEBUG_ENTRIES] = DEBUG_MAX;
|
|
+
|
|
+ /*
|
|
+ bring up slot handler thread
|
|
+ */
|
|
+
|
|
+ vcos_thread_attr_init(&attrs);
|
|
+ vcos_thread_attr_setstacksize(&attrs, VCHIQ_SLOT_HANDLER_STACK);
|
|
+ vcos_thread_attr_setpriority(&attrs, VCOS_THREAD_PRI_REALTIME);
|
|
+ vcos_snprintf(threadname, sizeof(threadname), "VCHIQ-%d", state->id);
|
|
+ if (vcos_thread_create(&state->slot_handler_thread, threadname,
|
|
+ &attrs, slot_handler_func, state) != VCOS_SUCCESS)
|
|
+ return VCHIQ_ERROR;
|
|
+
|
|
+ vcos_thread_attr_init(&attrs);
|
|
+ vcos_thread_attr_setstacksize(&attrs, VCHIQ_SLOT_HANDLER_STACK);
|
|
+ vcos_thread_attr_setpriority(&attrs, VCOS_THREAD_PRI_REALTIME);
|
|
+ vcos_snprintf(threadname, sizeof(threadname), "VCHIQr-%d", state->id);
|
|
+ if (vcos_thread_create(&state->recycle_thread, threadname,
|
|
+ &attrs, recycle_func, state) != VCOS_SUCCESS)
|
|
+ return VCHIQ_ERROR;
|
|
+
|
|
+ vcos_thread_attr_init(&attrs);
|
|
+ vcos_thread_attr_setstacksize(&attrs, VCHIQ_SLOT_HANDLER_STACK);
|
|
+ vcos_thread_attr_setpriority(&attrs, VCOS_THREAD_PRI_LOWEST);
|
|
+ vcos_snprintf(threadname, sizeof(threadname), "VCHIQl-%d", state->id);
|
|
+ if (vcos_thread_create(&state->lp_thread, threadname,
|
|
+ &attrs, lp_func, state) != VCOS_SUCCESS)
|
|
+ return VCHIQ_ERROR;
|
|
+
|
|
+ /* Indicate readiness to the other side */
|
|
+ local->initialised = 1;
|
|
+
|
|
+ return VCHIQ_SUCCESS;
|
|
+}
|
|
+
|
|
+/* Called from application thread when a client or server service is created. */
|
|
+VCHIQ_SERVICE_T *
|
|
+vchiq_add_service_internal(VCHIQ_STATE_T *state,
|
|
+ const VCHIQ_SERVICE_PARAMS_T *params, int srvstate,
|
|
+ VCHIQ_INSTANCE_T instance)
|
|
+{
|
|
+ VCHIQ_SERVICE_T **pservice = NULL;
|
|
+ VCHIQ_SERVICE_T *service = NULL;
|
|
+ int i;
|
|
+
|
|
+ /* Prepare to use a previously unused service */
|
|
+ if (state->unused_service < VCHIQ_MAX_SERVICES)
|
|
+ {
|
|
+ pservice = &state->services[state->unused_service];
|
|
+ }
|
|
+
|
|
+ if (srvstate == VCHIQ_SRVSTATE_OPENING) {
|
|
+ for (i = 0; i < state->unused_service; i++) {
|
|
+ VCHIQ_SERVICE_T *srv = state->services[i];
|
|
+ if (!srv)
|
|
+ {
|
|
+ pservice = &state->services[i];
|
|
+ break;
|
|
+ }
|
|
+ if (srv->srvstate == VCHIQ_SRVSTATE_FREE) {
|
|
+ service = srv;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ for (i = (state->unused_service - 1); i >= 0; i--) {
|
|
+ VCHIQ_SERVICE_T *srv = state->services[i];
|
|
+ if (!srv)
|
|
+ pservice = &state->services[i];
|
|
+ else if (srv->srvstate == VCHIQ_SRVSTATE_FREE) {
|
|
+ service = srv;
|
|
+ } else if ((srv->public_fourcc == params->fourcc) &&
|
|
+ ((srv->instance != instance)
|
|
+ || (srv->base.callback != params->callback))) {
|
|
+ /* There is another server using this fourcc which doesn't match */
|
|
+ pservice = NULL;
|
|
+ service = NULL;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (pservice && !service)
|
|
+ {
|
|
+ service = vcos_malloc(sizeof(VCHIQ_SERVICE_T), "VCHIQ service");
|
|
+ if (service)
|
|
+ {
|
|
+ service->srvstate = VCHIQ_SRVSTATE_FREE;
|
|
+ service->localport = (pservice - state->services);
|
|
+ vcos_event_create(&service->remove_event, "v.remove_event");
|
|
+ vcos_event_create(&service->bulk_remove_event, "v.bulk_remove_event");
|
|
+ vcos_mutex_create(&service->bulk_mutex, "v.bulk_mutex");
|
|
+ *pservice = service;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ vcos_log_error("vchiq: Out of memory");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (service) {
|
|
+ VCHIQ_SERVICE_QUOTA_T *service_quota =
|
|
+ &state->service_quotas[service->localport];
|
|
+ if (vcos_is_log_enabled( &vchiq_core_msg_log_category, VCOS_LOG_INFO)) {
|
|
+ vcos_log_impl( &vchiq_core_msg_log_category,
|
|
+ VCOS_LOG_INFO,
|
|
+ "%s Service %c%c%c%c SrcPort:%d",
|
|
+ ( srvstate == VCHIQ_SRVSTATE_OPENING )
|
|
+ ? "Open" : "Add",
|
|
+ VCHIQ_FOURCC_AS_4CHARS(params->fourcc),
|
|
+ service->localport );
|
|
+ }
|
|
+ service->state = state;
|
|
+ service->base.fourcc = params->fourcc;
|
|
+ service->base.callback = params->callback;
|
|
+ service->base.userdata = params->userdata;
|
|
+ service->version = params->version;
|
|
+ service->version_min = params->version_min;
|
|
+ vchiq_set_service_state(service, srvstate);
|
|
+ service->public_fourcc =
|
|
+ (srvstate ==
|
|
+ VCHIQ_SRVSTATE_OPENING) ? VCHIQ_FOURCC_INVALID : params->fourcc;
|
|
+ service->instance = instance;
|
|
+ service->remoteport = VCHIQ_PORT_FREE;
|
|
+ service->client_id = 0;
|
|
+ service->auto_close = 1;
|
|
+ service->service_use_count = 0;
|
|
+ init_bulk_queue(&service->bulk_tx);
|
|
+ init_bulk_queue(&service->bulk_rx);
|
|
+ service_quota->slot_quota = state->default_slot_quota;
|
|
+ if (service_quota->slot_use_count == 0)
|
|
+ service_quota->previous_tx_index =
|
|
+ SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos) - 1;
|
|
+ memset(&service->stats, 0, sizeof(service->stats));
|
|
+ vcos_atomic_flags_create(&service->poll_flags);
|
|
+
|
|
+ /* Ensure the events are unsignalled */
|
|
+ while (vcos_event_try(&service->remove_event) == VCOS_SUCCESS)
|
|
+ continue;
|
|
+ while (vcos_event_try(&service_quota->quota_event) == VCOS_SUCCESS)
|
|
+ continue;
|
|
+
|
|
+ if (pservice == &state->services[state->unused_service])
|
|
+ state->unused_service++;
|
|
+ }
|
|
+
|
|
+ return service;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_open_service_internal(VCHIQ_SERVICE_T *service, int client_id)
|
|
+{
|
|
+ VCHIQ_OPEN_PAYLOAD_T payload = {
|
|
+ service->base.fourcc,
|
|
+ client_id,
|
|
+ service->version,
|
|
+ service->version_min
|
|
+ };
|
|
+ VCHIQ_ELEMENT_T body = { &payload, sizeof(payload) };
|
|
+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
|
|
+
|
|
+ service->client_id = client_id;
|
|
+ vchiq_use_service(&service->base);
|
|
+ status = queue_message(service->state, NULL,
|
|
+ VCHIQ_MAKE_MSG(VCHIQ_MSG_OPEN, service->localport, 0),
|
|
+ &body, 1, sizeof(payload), 1);
|
|
+ if (status == VCHIQ_SUCCESS) {
|
|
+ if (vcos_event_wait(&service->remove_event) != VCOS_SUCCESS) {
|
|
+ status = VCHIQ_RETRY;
|
|
+ vchiq_release_service(&service->base);
|
|
+ } else if (service->srvstate != VCHIQ_SRVSTATE_OPEN) {
|
|
+ vcos_log_info("%d: osi - srvstate = %d", service->state->id, service->srvstate);
|
|
+ vcos_assert(service->srvstate == VCHIQ_SRVSTATE_CLOSEWAIT);
|
|
+ status = VCHIQ_ERROR;
|
|
+ VCHIQ_SERVICE_STATS_INC(service, error_count);
|
|
+ vchiq_release_service(&service->base);
|
|
+ }
|
|
+ }
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/* Called by the slot handler */
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_close_service_internal(VCHIQ_SERVICE_T *service, int close_recvd)
|
|
+{
|
|
+ VCHIQ_STATE_T *state = service->state;
|
|
+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
|
|
+
|
|
+ vcos_log_trace("%d: csi:%d (%s)",
|
|
+ service->state->id, service->localport,
|
|
+ srvstate_names[service->srvstate]);
|
|
+
|
|
+ switch (service->srvstate)
|
|
+ {
|
|
+ case VCHIQ_SRVSTATE_OPENING:
|
|
+ if (close_recvd)
|
|
+ {
|
|
+ /* The open was rejected - tell the user */
|
|
+ vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSEWAIT);
|
|
+ vcos_event_signal(&service->remove_event);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* Shutdown mid-open - let the other side know */
|
|
+ status = queue_message(state, NULL,
|
|
+ VCHIQ_MAKE_MSG
|
|
+ (VCHIQ_MSG_CLOSE,
|
|
+ service->localport,
|
|
+ VCHIQ_MSG_DSTPORT(service->remoteport)),
|
|
+ NULL, 0, 0, 0);
|
|
+
|
|
+ if (status == VCHIQ_SUCCESS)
|
|
+ vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSESENT);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case VCHIQ_SRVSTATE_OPEN:
|
|
+ if (state->is_master)
|
|
+ {
|
|
+ /* Abort any outstanding bulk transfers */
|
|
+ vcos_mutex_lock(&service->bulk_mutex);
|
|
+ abort_outstanding_bulks(service, &service->bulk_tx);
|
|
+ abort_outstanding_bulks(service, &service->bulk_rx);
|
|
+ status = notify_bulks(service, &service->bulk_tx);
|
|
+ if (status == VCHIQ_SUCCESS)
|
|
+ status = notify_bulks(service, &service->bulk_rx);
|
|
+ vcos_mutex_unlock(&service->bulk_mutex);
|
|
+ }
|
|
+
|
|
+ if (status == VCHIQ_SUCCESS)
|
|
+ status = queue_message(state, NULL,
|
|
+ VCHIQ_MAKE_MSG
|
|
+ (VCHIQ_MSG_CLOSE,
|
|
+ service->localport,
|
|
+ VCHIQ_MSG_DSTPORT(service->remoteport)),
|
|
+ NULL, 0, 0, 0);
|
|
+
|
|
+ if (status == VCHIQ_SUCCESS)
|
|
+ {
|
|
+ if (close_recvd)
|
|
+ vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSING);
|
|
+ else
|
|
+ vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSESENT);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case VCHIQ_SRVSTATE_CLOSESENT:
|
|
+ vcos_assert(close_recvd);
|
|
+
|
|
+ if (!state->is_master)
|
|
+ {
|
|
+ /* Abort any outstanding bulk transfers */
|
|
+ vcos_mutex_lock(&service->bulk_mutex);
|
|
+ abort_outstanding_bulks(service, &service->bulk_tx);
|
|
+ abort_outstanding_bulks(service, &service->bulk_rx);
|
|
+ status = notify_bulks(service, &service->bulk_tx);
|
|
+ if (status == VCHIQ_SUCCESS)
|
|
+ status = notify_bulks(service, &service->bulk_rx);
|
|
+ vcos_mutex_unlock(&service->bulk_mutex);
|
|
+ }
|
|
+
|
|
+ if (status == VCHIQ_SUCCESS)
|
|
+ vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSING);
|
|
+ break;
|
|
+
|
|
+ case VCHIQ_SRVSTATE_CLOSING:
|
|
+ /* We may come here after a retry */
|
|
+ vcos_assert(!close_recvd);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ vcos_log_error("vchiq_close_service_internal(%d) called in state %s",
|
|
+ close_recvd, srvstate_names[service->srvstate]);
|
|
+ vcos_assert(0);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (service->srvstate == VCHIQ_SRVSTATE_CLOSING)
|
|
+ {
|
|
+ /* Complete the close process */
|
|
+ vchiq_release_service(&service->base);
|
|
+
|
|
+ service->client_id = 0;
|
|
+
|
|
+ /* Now tell the client that the services is closed */
|
|
+ if (service->instance)
|
|
+ {
|
|
+ int oldstate = service->srvstate;
|
|
+
|
|
+ /* Change the service state now for the benefit of the callback */
|
|
+ vchiq_set_service_state(service,
|
|
+ ((service->public_fourcc == VCHIQ_FOURCC_INVALID) ||
|
|
+ !service->auto_close) ?
|
|
+ VCHIQ_SRVSTATE_CLOSEWAIT :
|
|
+ VCHIQ_SRVSTATE_LISTENING);
|
|
+
|
|
+ status = make_service_callback(service, VCHIQ_SERVICE_CLOSED, NULL, NULL);
|
|
+
|
|
+ if (status == VCHIQ_RETRY)
|
|
+ {
|
|
+ /* Restore the old state, to be retried later */
|
|
+ vchiq_set_service_state(service, oldstate);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if (status == VCHIQ_ERROR) {
|
|
+ /* Signal an error (fatal, since the other end will probably have closed) */
|
|
+ vchiq_set_service_state(service, VCHIQ_SRVSTATE_OPEN);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (status != VCHIQ_RETRY)
|
|
+ {
|
|
+ if (service->srvstate == VCHIQ_SRVSTATE_CLOSING)
|
|
+ vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSEWAIT);
|
|
+ vcos_event_signal(&service->remove_event);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/* Called from the application process upon process death */
|
|
+void
|
|
+vchiq_terminate_service_internal(VCHIQ_SERVICE_T *service)
|
|
+{
|
|
+ VCHIQ_STATE_T *state = service->state;
|
|
+
|
|
+ vcos_log_info("%d: tsi - (%d<->%d)", state->id, service->localport, service->remoteport);
|
|
+
|
|
+ /* Disconnect from the instance, to prevent any callbacks */
|
|
+ service->instance = NULL;
|
|
+
|
|
+ /* Mark the service for termination by the slot handler */
|
|
+ request_poll(state, service, VCHIQ_POLL_TERMINATE);
|
|
+}
|
|
+
|
|
+/* Called from the application process upon process death, and from
|
|
+ vchiq_remove_service */
|
|
+void
|
|
+vchiq_free_service_internal(VCHIQ_SERVICE_T *service)
|
|
+{
|
|
+ VCHIQ_STATE_T *state = service->state;
|
|
+ int slot_last = state->remote->slot_last;
|
|
+ int i;
|
|
+
|
|
+ vcos_log_info("%d: fsi - (%d)", state->id, service->localport);
|
|
+
|
|
+ vcos_mutex_lock(&state->mutex);
|
|
+
|
|
+ /* Release any claimed messages */
|
|
+ for (i = state->remote->slot_first; i <= slot_last; i++)
|
|
+ {
|
|
+ VCHIQ_SLOT_INFO_T *slot_info = SLOT_INFO_FROM_INDEX(state, i);
|
|
+ if (slot_info->release_count != slot_info->use_count)
|
|
+ {
|
|
+ char *data = (char *)SLOT_DATA_FROM_INDEX(state, i);
|
|
+ int pos, end;
|
|
+
|
|
+ end = VCHIQ_SLOT_SIZE;
|
|
+ if (data == state->rx_data)
|
|
+ {
|
|
+ /* This buffer is still being read from - stop at the current read position */
|
|
+ end = state->rx_pos & VCHIQ_SLOT_MASK;
|
|
+ }
|
|
+
|
|
+ pos = 0;
|
|
+
|
|
+ while (pos < end)
|
|
+ {
|
|
+ VCHIQ_HEADER_T *header = (VCHIQ_HEADER_T *)(data + pos);
|
|
+ int msgid = header->msgid;
|
|
+ int port = VCHIQ_MSG_DSTPORT(msgid);
|
|
+ if (port == service->localport)
|
|
+ {
|
|
+ if (msgid & VCHIQ_MSGID_CLAIMED)
|
|
+ {
|
|
+ header->msgid = msgid & ~VCHIQ_MSGID_CLAIMED;
|
|
+ vcos_log_info(" fsi - hdr %x", (unsigned int)header);
|
|
+ release_slot(state, slot_info);
|
|
+ }
|
|
+ }
|
|
+ pos += calc_stride(header->size);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ vcos_assert(state->services[service->localport] == service);
|
|
+ vchiq_set_service_state(service, VCHIQ_SRVSTATE_FREE);
|
|
+ state->services[service->localport] = NULL;
|
|
+ vcos_free(service);
|
|
+ vcos_mutex_unlock(&state->mutex);
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_connect_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ /* Find all services registered to this client and enable them. */
|
|
+ for (i = 0; i < state->unused_service; i++)
|
|
+ {
|
|
+ VCHIQ_SERVICE_T *service = state->services[i];
|
|
+ if (service && (service->instance == instance)) {
|
|
+ if (service->srvstate == VCHIQ_SRVSTATE_HIDDEN)
|
|
+ vchiq_set_service_state(service,
|
|
+ VCHIQ_SRVSTATE_LISTENING);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (state->conn_state == VCHIQ_CONNSTATE_DISCONNECTED) {
|
|
+ if (queue_message(state, NULL,
|
|
+ VCHIQ_MAKE_MSG(VCHIQ_MSG_CONNECT, 0, 0), NULL, 0,
|
|
+ 0, 1) == VCHIQ_RETRY)
|
|
+ return VCHIQ_RETRY;
|
|
+ vcos_event_wait(&state->connect);
|
|
+
|
|
+ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED);
|
|
+ }
|
|
+
|
|
+ return VCHIQ_SUCCESS;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_shutdown_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance)
|
|
+{
|
|
+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
|
|
+ int i;
|
|
+
|
|
+ /* Find all services registered to this client and close them. */
|
|
+ for (i = 0; i < state->unused_service; i++)
|
|
+ {
|
|
+ VCHIQ_SERVICE_T *service = state->services[i];
|
|
+ if (service && (service->instance == instance) &&
|
|
+ ((service->srvstate == VCHIQ_SRVSTATE_OPEN) ||
|
|
+ (service->srvstate == VCHIQ_SRVSTATE_LISTENING)))
|
|
+ {
|
|
+ status = vchiq_remove_service(&service->base);
|
|
+ if (status != VCHIQ_SUCCESS)
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_pause_internal(VCHIQ_STATE_T *state)
|
|
+{
|
|
+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
|
|
+
|
|
+ switch (state->conn_state)
|
|
+ {
|
|
+ case VCHIQ_CONNSTATE_CONNECTED:
|
|
+ /* Request a pause */
|
|
+ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_PAUSING);
|
|
+ request_poll(state, NULL, 0);
|
|
+ break;
|
|
+ case VCHIQ_CONNSTATE_PAUSED:
|
|
+ break;
|
|
+ default:
|
|
+ status = VCHIQ_ERROR;
|
|
+ VCHIQ_STATS_INC(state, error_count);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_resume_internal(VCHIQ_STATE_T *state)
|
|
+{
|
|
+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
|
|
+
|
|
+ if (state->conn_state == VCHIQ_CONNSTATE_PAUSED)
|
|
+ {
|
|
+ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_RESUMING);
|
|
+ request_poll(state, NULL, 0);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ status = VCHIQ_ERROR;
|
|
+ VCHIQ_STATS_INC(state, error_count);
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_close_service(VCHIQ_SERVICE_HANDLE_T handle)
|
|
+{
|
|
+ /* Unregister the service */
|
|
+ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *) handle;
|
|
+ VCHIQ_STATUS_T status = VCHIQ_ERROR;
|
|
+
|
|
+ if (service == NULL)
|
|
+ return VCHIQ_ERROR;
|
|
+
|
|
+ vcos_log_info("%d: close_service:%d", service->state->id, service->localport);
|
|
+
|
|
+ if (service->public_fourcc != VCHIQ_FOURCC_INVALID)
|
|
+ {
|
|
+ if (service->srvstate == VCHIQ_SRVSTATE_CLOSEWAIT)
|
|
+ {
|
|
+ /* This is a non-auto-close server */
|
|
+ vchiq_set_service_state(service, VCHIQ_SRVSTATE_LISTENING);
|
|
+ status = VCHIQ_SUCCESS;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* For clients, make it an alias of vchiq_remove_service */
|
|
+ status = vchiq_remove_service(handle);
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T handle)
|
|
+{
|
|
+ /* Unregister the service */
|
|
+ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *) handle;
|
|
+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
|
|
+
|
|
+ if (service == NULL)
|
|
+ return VCHIQ_ERROR;
|
|
+
|
|
+ vcos_log_info("%d: remove_service:%d", service->state->id, service->localport);
|
|
+
|
|
+ switch (service->srvstate)
|
|
+ {
|
|
+ case VCHIQ_SRVSTATE_OPENING:
|
|
+ case VCHIQ_SRVSTATE_OPEN:
|
|
+ /* Mark the service for termination by the slot handler */
|
|
+ request_poll(service->state, service, VCHIQ_POLL_TERMINATE);
|
|
+
|
|
+ /* Drop through... */
|
|
+ case VCHIQ_SRVSTATE_CLOSESENT:
|
|
+ case VCHIQ_SRVSTATE_CLOSING:
|
|
+ while ((service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT) &&
|
|
+ (service->srvstate != VCHIQ_SRVSTATE_LISTENING))
|
|
+ {
|
|
+ if (vcos_event_wait(&service->remove_event) != VCOS_SUCCESS) {
|
|
+ status = VCHIQ_RETRY;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (status == VCHIQ_SUCCESS) {
|
|
+ if (service->srvstate == VCHIQ_SRVSTATE_OPEN)
|
|
+ status = VCHIQ_ERROR;
|
|
+ else
|
|
+ {
|
|
+ service->instance = NULL;
|
|
+ vchiq_free_service_internal(service);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_bulk_transfer(VCHIQ_SERVICE_T *service,
|
|
+ VCHI_MEM_HANDLE_T memhandle, void *offset, int size, void *userdata,
|
|
+ VCHIQ_BULK_MODE_T mode, VCHIQ_BULK_DIR_T dir)
|
|
+{
|
|
+ VCHIQ_BULK_QUEUE_T *queue = (dir == VCHIQ_BULK_TRANSMIT) ?
|
|
+ &service->bulk_tx : &service->bulk_rx;
|
|
+ VCHIQ_BULK_T *bulk;
|
|
+ VCHIQ_STATE_T *state;
|
|
+ BULK_WAITER_T bulk_waiter;
|
|
+ const char dir_char = (dir == VCHIQ_BULK_TRANSMIT) ? 't' : 'r';
|
|
+ const int dir_msgtype = (dir == VCHIQ_BULK_TRANSMIT) ? VCHIQ_MSG_BULK_TX : VCHIQ_MSG_BULK_RX;
|
|
+ VCHIQ_STATUS_T status = VCHIQ_ERROR;
|
|
+
|
|
+ if ((service == NULL) ||
|
|
+ ((memhandle == VCHI_MEM_HANDLE_INVALID) && (offset == NULL)))
|
|
+ return VCHIQ_ERROR;
|
|
+
|
|
+ state = service->state;
|
|
+
|
|
+ if (service->srvstate != VCHIQ_SRVSTATE_OPEN)
|
|
+ return VCHIQ_ERROR; /* Must be connected */
|
|
+
|
|
+ if (vcos_mutex_lock(&service->bulk_mutex) != VCOS_SUCCESS)
|
|
+ return VCHIQ_RETRY;
|
|
+
|
|
+ if (queue->local_insert == queue->remove + VCHIQ_NUM_SERVICE_BULKS)
|
|
+ {
|
|
+ VCHIQ_SERVICE_STATS_INC(service, bulk_stalls);
|
|
+ do {
|
|
+ vcos_mutex_unlock(&service->bulk_mutex);
|
|
+ if (vcos_event_wait(&service->bulk_remove_event) != VCOS_SUCCESS)
|
|
+ return VCHIQ_RETRY;
|
|
+ if (vcos_mutex_lock(&service->bulk_mutex) != VCOS_SUCCESS)
|
|
+ return VCHIQ_RETRY;
|
|
+ } while (queue->local_insert == queue->remove + VCHIQ_NUM_SERVICE_BULKS);
|
|
+ }
|
|
+
|
|
+ bulk = &queue->bulks[BULK_INDEX(queue->local_insert)];
|
|
+
|
|
+ if (mode == VCHIQ_BULK_MODE_BLOCKING)
|
|
+ {
|
|
+ vcos_event_create(&bulk_waiter.event, "bulk_waiter");
|
|
+ bulk_waiter.actual = 0;
|
|
+ userdata = &bulk_waiter;
|
|
+ }
|
|
+
|
|
+ bulk->mode = mode;
|
|
+ bulk->dir = dir;
|
|
+ bulk->userdata = userdata;
|
|
+ bulk->size = size;
|
|
+ bulk->actual = VCHIQ_BULK_ACTUAL_ABORTED;
|
|
+
|
|
+ if (vchiq_prepare_bulk_data(bulk, memhandle, offset, size, dir) != VCHIQ_SUCCESS)
|
|
+ {
|
|
+ goto error_exit;
|
|
+ }
|
|
+
|
|
+ vcos_log_info("%d: bt (%d->%d) %cx %x@%x %x", state->id,
|
|
+ service->localport, service->remoteport, dir_char,
|
|
+ size, (unsigned int)bulk->data, (unsigned int)userdata);
|
|
+
|
|
+ if (state->is_master)
|
|
+ {
|
|
+ queue->local_insert++;
|
|
+ if (resolve_bulks(service, queue))
|
|
+ request_poll(state, service, (dir == VCHIQ_BULK_TRANSMIT) ?
|
|
+ VCHIQ_POLL_TXNOTIFY : VCHIQ_POLL_RXNOTIFY);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ int payload[2] = { (int)bulk->data, bulk->size };
|
|
+ VCHIQ_ELEMENT_T element = { payload, sizeof(payload) };
|
|
+
|
|
+ if (queue_message(state, NULL,
|
|
+ VCHIQ_MAKE_MSG(dir_msgtype,
|
|
+ service->localport, service->remoteport),
|
|
+ &element, 1, sizeof(payload), 1) != VCHIQ_SUCCESS)
|
|
+ {
|
|
+ vchiq_complete_bulk(bulk);
|
|
+ goto error_exit;
|
|
+ }
|
|
+ queue->local_insert++;
|
|
+ queue->remote_insert++;
|
|
+ }
|
|
+
|
|
+ vcos_mutex_unlock(&service->bulk_mutex);
|
|
+
|
|
+ vcos_log_trace("%d: bt:%d %cx li=%x ri=%x p=%x", state->id,
|
|
+ service->localport, dir_char,
|
|
+ queue->local_insert, queue->remote_insert, queue->process);
|
|
+
|
|
+ status = VCHIQ_SUCCESS;
|
|
+
|
|
+ if (mode == VCHIQ_BULK_MODE_BLOCKING)
|
|
+ {
|
|
+ if (vcos_event_wait(&bulk_waiter.event) != VCOS_SUCCESS)
|
|
+ {
|
|
+ vcos_log_info("bulk wait interrupted");
|
|
+ /* Stop notify_bulks signalling a non-existent waiter */
|
|
+ bulk->userdata = NULL;
|
|
+ status = VCHIQ_ERROR;
|
|
+ }
|
|
+ else if (bulk_waiter.actual == VCHIQ_BULK_ACTUAL_ABORTED)
|
|
+ status = VCHIQ_ERROR;
|
|
+
|
|
+ vcos_event_delete(&bulk_waiter.event);
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+
|
|
+error_exit:
|
|
+ if (mode == VCHIQ_BULK_MODE_BLOCKING)
|
|
+ vcos_event_delete(&bulk_waiter.event);
|
|
+ vcos_mutex_unlock(&service->bulk_mutex);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle,
|
|
+ const void *data, int size, void *userdata)
|
|
+{
|
|
+ return vchiq_bulk_transfer((VCHIQ_SERVICE_T *)handle,
|
|
+ VCHI_MEM_HANDLE_INVALID, (void *)data, size, userdata,
|
|
+ VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_TRANSMIT);
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data, int size,
|
|
+ void *userdata)
|
|
+{
|
|
+ return vchiq_bulk_transfer((VCHIQ_SERVICE_T *)handle,
|
|
+ VCHI_MEM_HANDLE_INVALID, data, size, userdata,
|
|
+ VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_RECEIVE);
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_queue_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T handle,
|
|
+ VCHI_MEM_HANDLE_T memhandle, const void *offset, int size, void *userdata)
|
|
+{
|
|
+ return vchiq_bulk_transfer((VCHIQ_SERVICE_T *)handle,
|
|
+ memhandle, (void *)offset, size, userdata,
|
|
+ VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_TRANSMIT);
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_queue_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T handle,
|
|
+ VCHI_MEM_HANDLE_T memhandle, void *offset, int size, void *userdata)
|
|
+{
|
|
+ return vchiq_bulk_transfer((VCHIQ_SERVICE_T *)handle,
|
|
+ memhandle, offset, size, userdata,
|
|
+ VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_RECEIVE);
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, const void *data, int size,
|
|
+ void *userdata, VCHIQ_BULK_MODE_T mode)
|
|
+{
|
|
+ return vchiq_bulk_transfer((VCHIQ_SERVICE_T *)handle,
|
|
+ VCHI_MEM_HANDLE_INVALID, (void *)data, size, userdata,
|
|
+ mode, VCHIQ_BULK_TRANSMIT);
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data, int size,
|
|
+ void *userdata, VCHIQ_BULK_MODE_T mode)
|
|
+{
|
|
+ return vchiq_bulk_transfer((VCHIQ_SERVICE_T *)handle,
|
|
+ VCHI_MEM_HANDLE_INVALID, data, size, userdata,
|
|
+ mode, VCHIQ_BULK_RECEIVE);
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T handle,
|
|
+ VCHI_MEM_HANDLE_T memhandle, const void *offset, int size, void *userdata,
|
|
+ VCHIQ_BULK_MODE_T mode)
|
|
+{
|
|
+ return vchiq_bulk_transfer((VCHIQ_SERVICE_T *)handle,
|
|
+ memhandle, (void *)offset, size, userdata,
|
|
+ mode, VCHIQ_BULK_TRANSMIT);
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T handle,
|
|
+ VCHI_MEM_HANDLE_T memhandle, void *offset, int size, void *userdata,
|
|
+ VCHIQ_BULK_MODE_T mode)
|
|
+{
|
|
+ return vchiq_bulk_transfer((VCHIQ_SERVICE_T *)handle,
|
|
+ memhandle, offset, size, userdata,
|
|
+ mode, VCHIQ_BULK_RECEIVE);
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T handle,
|
|
+ const VCHIQ_ELEMENT_T *elements, int count)
|
|
+{
|
|
+ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *) handle;
|
|
+
|
|
+ unsigned int size = 0;
|
|
+ unsigned int i;
|
|
+
|
|
+ if ((service == NULL) ||
|
|
+ (service->srvstate != VCHIQ_SRVSTATE_OPEN))
|
|
+ return VCHIQ_ERROR;
|
|
+
|
|
+ for (i = 0; i < (unsigned int)count; i++)
|
|
+ {
|
|
+ if (elements[i].size)
|
|
+ {
|
|
+ if (elements[i].data == NULL)
|
|
+ {
|
|
+ VCHIQ_SERVICE_STATS_INC(service, error_count);
|
|
+ return VCHIQ_ERROR;
|
|
+ }
|
|
+ size += elements[i].size;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (size > VCHIQ_MAX_MSG_SIZE)
|
|
+ {
|
|
+ VCHIQ_SERVICE_STATS_INC(service, error_count);
|
|
+ return VCHIQ_ERROR;
|
|
+ }
|
|
+
|
|
+ return queue_message(service->state, service,
|
|
+ VCHIQ_MAKE_MSG(VCHIQ_MSG_DATA, service->localport,
|
|
+ service->remoteport), elements, count, size, 1);
|
|
+}
|
|
+
|
|
+void
|
|
+vchiq_release_message(VCHIQ_SERVICE_HANDLE_T handle, VCHIQ_HEADER_T *header)
|
|
+{
|
|
+ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle;
|
|
+ VCHIQ_STATE_T *state;
|
|
+ int slot_index;
|
|
+ int msgid;
|
|
+
|
|
+ if (service == NULL)
|
|
+ return;
|
|
+
|
|
+ state = service->state;
|
|
+
|
|
+ slot_index = SLOT_INDEX_FROM_DATA(state, (void *)header);
|
|
+
|
|
+ if ((slot_index >= state->remote->slot_first) &&
|
|
+ (slot_index <= state->remote->slot_last) &&
|
|
+ ((msgid = header->msgid) & VCHIQ_MSGID_CLAIMED))
|
|
+ {
|
|
+ VCHIQ_SLOT_INFO_T *slot_info = SLOT_INFO_FROM_INDEX(state, slot_index);
|
|
+
|
|
+ /* Rewrite the message header to prevent a double release */
|
|
+ header->msgid = msgid & ~VCHIQ_MSGID_CLAIMED;
|
|
+
|
|
+ release_slot(state, slot_info);
|
|
+ }
|
|
+}
|
|
+
|
|
+int
|
|
+vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T handle)
|
|
+{
|
|
+ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle;
|
|
+ return service ? service->client_id : 0;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_get_config(VCHIQ_INSTANCE_T instance,
|
|
+ int config_size, VCHIQ_CONFIG_T *pconfig)
|
|
+{
|
|
+ VCHIQ_CONFIG_T config;
|
|
+
|
|
+ vcos_unused(instance);
|
|
+
|
|
+ config.max_msg_size = VCHIQ_MAX_MSG_SIZE;
|
|
+ config.bulk_threshold = VCHIQ_MAX_MSG_SIZE;
|
|
+ config.max_outstanding_bulks = VCHIQ_NUM_SERVICE_BULKS;
|
|
+ config.max_services = VCHIQ_MAX_SERVICES;
|
|
+ config.version = VCHIQ_VERSION;
|
|
+ config.version_min = VCHIQ_VERSION_MIN;
|
|
+
|
|
+ if (config_size > sizeof(VCHIQ_CONFIG_T))
|
|
+ return VCHIQ_ERROR;
|
|
+
|
|
+ memcpy(pconfig, &config, vcos_min(config_size, sizeof(VCHIQ_CONFIG_T)));
|
|
+
|
|
+ return VCHIQ_SUCCESS;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T handle,
|
|
+ VCHIQ_SERVICE_OPTION_T option, int value)
|
|
+{
|
|
+ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle;
|
|
+ VCHIQ_STATUS_T status = VCHIQ_ERROR;
|
|
+
|
|
+ if (service)
|
|
+ {
|
|
+ switch (option)
|
|
+ {
|
|
+ case VCHIQ_SERVICE_OPTION_AUTOCLOSE:
|
|
+ service->auto_close = value;
|
|
+ status = VCHIQ_SUCCESS;
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+void
|
|
+vchiq_dump_shared_state(void *dump_context, VCHIQ_STATE_T *state,
|
|
+ VCHIQ_SHARED_STATE_T *shared, const char *label)
|
|
+{
|
|
+ static const char *const debug_names[] =
|
|
+ {
|
|
+ "<entries>",
|
|
+ "SLOT_HANDLER_COUNT",
|
|
+ "SLOT_HANDLER_LINE",
|
|
+ "PARSE_LINE",
|
|
+ "PARSE_HEADER",
|
|
+ "PARSE_MSGID",
|
|
+ "AWAIT_COMPLETION_LINE",
|
|
+ "DEQUEUE_MESSAGE_LINE",
|
|
+ "SERVICE_CALLBACK_LINE",
|
|
+ "MSG_QUEUE_FULL_COUNT",
|
|
+ "COMPLETION_QUEUE_FULL_COUNT"
|
|
+ };
|
|
+ int i;
|
|
+
|
|
+ char buf[80];
|
|
+ int len;
|
|
+ len = vcos_snprintf(buf, sizeof(buf),
|
|
+ " %s: slots %d-%d tx_pos=%x recycle=%x",
|
|
+ label, shared->slot_first, shared->slot_last,
|
|
+ shared->tx_pos, shared->slot_queue_recycle);
|
|
+ vchiq_dump(dump_context, buf, len + 1);
|
|
+
|
|
+ len = vcos_snprintf(buf, sizeof(buf),
|
|
+ " Slots claimed:");
|
|
+ vchiq_dump(dump_context, buf, len + 1);
|
|
+
|
|
+ for (i = shared->slot_first; i <= shared->slot_last; i++)
|
|
+ {
|
|
+ VCHIQ_SLOT_INFO_T slot_info = *SLOT_INFO_FROM_INDEX(state, i);
|
|
+ if (slot_info.use_count != slot_info.release_count)
|
|
+ {
|
|
+ len = vcos_snprintf(buf, sizeof(buf),
|
|
+ " %d: %d/%d", i, slot_info.use_count, slot_info.release_count);
|
|
+ vchiq_dump(dump_context, buf, len + 1);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for (i = 1; i < shared->debug[DEBUG_ENTRIES]; i++)
|
|
+ {
|
|
+ len = vcos_snprintf(buf, sizeof(buf), " DEBUG: %s = %d(%x)",
|
|
+ debug_names[i], shared->debug[i], shared->debug[i]);
|
|
+ vchiq_dump(dump_context, buf, len + 1);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+vchiq_dump_state(void *dump_context, VCHIQ_STATE_T *state)
|
|
+{
|
|
+ char buf[80];
|
|
+ int len;
|
|
+ int i;
|
|
+
|
|
+ len = vcos_snprintf(buf, sizeof(buf), "State %d: %s", state->id,
|
|
+ conn_state_names[state->conn_state]);
|
|
+ vchiq_dump(dump_context, buf, len + 1);
|
|
+
|
|
+ len = vcos_snprintf(buf, sizeof(buf),
|
|
+ " tx_pos=%x(@%x), rx_pos=%x(@%x)",
|
|
+ state->id, state->local->tx_pos,
|
|
+ (uint32_t)state->tx_data + (state->local_tx_pos & VCHIQ_SLOT_MASK),
|
|
+ state->rx_pos,
|
|
+ (uint32_t)state->rx_data + (state->rx_pos & VCHIQ_SLOT_MASK));
|
|
+ vchiq_dump(dump_context, buf, len + 1);
|
|
+
|
|
+ len = vcos_snprintf(buf, sizeof(buf),
|
|
+ " Version: %d (min %d)",
|
|
+ VCHIQ_VERSION, VCHIQ_VERSION_MIN);
|
|
+ vchiq_dump(dump_context, buf, len + 1);
|
|
+
|
|
+ if (VCHIQ_ENABLE_STATS)
|
|
+ {
|
|
+ len = vcos_snprintf(buf, sizeof(buf),
|
|
+ " Stats: ctrl_tx_count=%d, ctrl_rx_count=%d, error_count=%d",
|
|
+ state->stats.ctrl_tx_count, state->stats.ctrl_rx_count,
|
|
+ state->stats.slot_stalls);
|
|
+ vchiq_dump(dump_context, buf, len + 1);
|
|
+ }
|
|
+
|
|
+ len = vcos_snprintf(buf, sizeof(buf),
|
|
+ " Slots: %d available, %d recyclable, %d stalls",
|
|
+ state->slot_queue_available - SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos),
|
|
+ state->local->slot_queue_recycle - state->slot_queue_available,
|
|
+ state->stats.slot_stalls);
|
|
+ vchiq_dump(dump_context, buf, len + 1);
|
|
+
|
|
+ vchiq_dump_platform_state(dump_context);
|
|
+
|
|
+ vchiq_dump_shared_state(dump_context, state, state->local, "Local");
|
|
+ vchiq_dump_shared_state(dump_context, state, state->remote, "Remote");
|
|
+
|
|
+ vchiq_dump_platform_instances(dump_context);
|
|
+
|
|
+ for (i = 0; i < state->unused_service; i++) {
|
|
+ VCHIQ_SERVICE_T *service = state->services[i];
|
|
+
|
|
+ if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE))
|
|
+ vchiq_dump_service_state(dump_context, service);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+vchiq_dump_service_state(void *dump_context, VCHIQ_SERVICE_T *service)
|
|
+{
|
|
+ char buf[80];
|
|
+ int len;
|
|
+
|
|
+ len = vcos_snprintf(buf, sizeof(buf), "Service %d: %s",
|
|
+ service->localport, srvstate_names[service->srvstate]);
|
|
+
|
|
+ if (service->srvstate != VCHIQ_SRVSTATE_FREE)
|
|
+ {
|
|
+ char remoteport[30];
|
|
+ VCHIQ_SERVICE_QUOTA_T *service_quota =
|
|
+ &service->state->service_quotas[service->localport];
|
|
+ int fourcc = service->base.fourcc;
|
|
+ if (service->remoteport != VCHIQ_PORT_FREE)
|
|
+ {
|
|
+ int len2 = vcos_snprintf(remoteport, sizeof(remoteport), "%d",
|
|
+ service->remoteport);
|
|
+ if (service->public_fourcc != VCHIQ_FOURCC_INVALID)
|
|
+ vcos_snprintf(remoteport + len2, sizeof(remoteport) - len2,
|
|
+ " (client %x)", service->client_id);
|
|
+ }
|
|
+ else
|
|
+ vcos_strcpy(remoteport, "n/a");
|
|
+
|
|
+ len += vcos_snprintf(buf + len, sizeof(buf) - len,
|
|
+ " '%c%c%c%c' remote %s (slot use %d/%d)",
|
|
+ VCHIQ_FOURCC_AS_4CHARS(fourcc),
|
|
+ remoteport,
|
|
+ service_quota->slot_use_count,
|
|
+ service_quota->slot_quota);
|
|
+
|
|
+ if (VCHIQ_ENABLE_STATS)
|
|
+ {
|
|
+ vchiq_dump(dump_context, buf, len + 1);
|
|
+
|
|
+ len = vcos_snprintf(buf, sizeof(buf),
|
|
+ " Ctrl: tx_count=%d, tx_bytes=%" PRIu64 ", rx_count=%d, rx_bytes=%" PRIu64,
|
|
+ service->stats.ctrl_tx_count, service->stats.ctrl_tx_bytes,
|
|
+ service->stats.ctrl_rx_count, service->stats.ctrl_rx_bytes);
|
|
+ vchiq_dump(dump_context, buf, len + 1);
|
|
+
|
|
+ len = vcos_snprintf(buf, sizeof(buf),
|
|
+ " Bulk: tx_count=%d, tx_bytes=%" PRIu64 ", rx_count=%d, rx_bytes=%" PRIu64,
|
|
+ service->stats.bulk_tx_count, service->stats.bulk_tx_bytes,
|
|
+ service->stats.bulk_rx_count, service->stats.bulk_rx_bytes);
|
|
+ vchiq_dump(dump_context, buf, len + 1);
|
|
+
|
|
+ len = vcos_snprintf(buf, sizeof(buf),
|
|
+ " %d quota stalls, %d slot stalls, %d bulk stalls, %d aborted, %d errors",
|
|
+ service->stats.quota_stalls, service->stats.slot_stalls,
|
|
+ service->stats.bulk_stalls, service->stats.bulk_aborted_count,
|
|
+ service->stats.error_count);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ vchiq_dump(dump_context, buf, len + 1);
|
|
+
|
|
+ vchiq_dump_platform_service_state(dump_context, service);
|
|
+}
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h
|
|
@@ -0,0 +1,480 @@
|
|
+/*
|
|
+ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
+ */
|
|
+
|
|
+#ifndef VCHIQ_CORE_H
|
|
+#define VCHIQ_CORE_H
|
|
+
|
|
+#include "vchiq_cfg.h"
|
|
+
|
|
+#include "vchiq.h"
|
|
+
|
|
+#define IS_POW2(x) (x && ((x & (x - 1)) == 0))
|
|
+
|
|
+/* Ensure that the slot size and maximum number of slots are powers of 2 */
|
|
+vcos_static_assert(IS_POW2(VCHIQ_SLOT_SIZE));
|
|
+vcos_static_assert(IS_POW2(VCHIQ_MAX_SLOTS));
|
|
+vcos_static_assert(IS_POW2(VCHIQ_MAX_SLOTS_PER_SIDE));
|
|
+
|
|
+#define VCHIQ_SLOT_MASK (VCHIQ_SLOT_SIZE - 1)
|
|
+#define VCHIQ_SLOT_QUEUE_MASK (VCHIQ_MAX_SLOTS_PER_SIDE - 1)
|
|
+#define VCHIQ_SLOT_ZERO_SLOTS ((sizeof(VCHIQ_SLOT_ZERO_T) + \
|
|
+ VCHIQ_SLOT_SIZE - 1) / VCHIQ_SLOT_SIZE)
|
|
+
|
|
+#define VCHIQ_MSG_PADDING 0 // -
|
|
+#define VCHIQ_MSG_CONNECT 1 // -
|
|
+#define VCHIQ_MSG_OPEN 2 // + (srcport, -), fourcc, client_id
|
|
+#define VCHIQ_MSG_OPENACK 3 // + (srcport, dstport)
|
|
+#define VCHIQ_MSG_CLOSE 4 // + (srcport, dstport)
|
|
+#define VCHIQ_MSG_DATA 5 // + (srcport, dstport)
|
|
+#define VCHIQ_MSG_BULK_RX 6 // + (srcport, dstport), data, size
|
|
+#define VCHIQ_MSG_BULK_TX 7 // + (srcport, dstport), data, size
|
|
+#define VCHIQ_MSG_BULK_RX_DONE 8 // + (srcport, dstport), actual
|
|
+#define VCHIQ_MSG_BULK_TX_DONE 9 // + (srcport, dstport), actual
|
|
+#define VCHIQ_MSG_PAUSE 10 // -
|
|
+#define VCHIQ_MSG_RESUME 11 // -
|
|
+
|
|
+#define VCHIQ_PORT_MAX (VCHIQ_MAX_SERVICES - 1)
|
|
+#define VCHIQ_PORT_FREE 0x1000
|
|
+#define VCHIQ_PORT_IS_VALID(port) (port < VCHIQ_PORT_FREE)
|
|
+#define VCHIQ_MAKE_MSG(type,srcport,dstport) ((type<<24) | (srcport<<12) | (dstport<<0))
|
|
+#define VCHIQ_MSG_TYPE(msgid) ((unsigned int)msgid >> 24)
|
|
+#define VCHIQ_MSG_SRCPORT(msgid) (unsigned short)(((unsigned int)msgid >> 12) & 0xfff)
|
|
+#define VCHIQ_MSG_DSTPORT(msgid) ((unsigned short)msgid & 0xfff)
|
|
+
|
|
+#define VCHIQ_FOURCC_AS_4CHARS(fourcc) \
|
|
+ ((fourcc) >> 24) & 0xff, \
|
|
+ ((fourcc) >> 16) & 0xff, \
|
|
+ ((fourcc) >> 8) & 0xff, \
|
|
+ ((fourcc) ) & 0xff
|
|
+
|
|
+/* Ensure the fields are wide enough */
|
|
+vcos_static_assert(VCHIQ_MSG_SRCPORT(VCHIQ_MAKE_MSG(0,0,VCHIQ_PORT_MAX)) == 0);
|
|
+vcos_static_assert(VCHIQ_MSG_TYPE(VCHIQ_MAKE_MSG(0,VCHIQ_PORT_MAX,0)) == 0);
|
|
+vcos_static_assert((unsigned int)VCHIQ_PORT_MAX < (unsigned int)VCHIQ_PORT_FREE);
|
|
+
|
|
+#define VCHIQ_MSGID_PADDING VCHIQ_MAKE_MSG(VCHIQ_MSG_PADDING,0,0)
|
|
+#define VCHIQ_MSGID_CLAIMED 0x40000000
|
|
+
|
|
+#define VCHIQ_FOURCC_INVALID 0x00000000
|
|
+#define VCHIQ_FOURCC_IS_LEGAL(fourcc) (fourcc != VCHIQ_FOURCC_INVALID)
|
|
+
|
|
+#define VCHIQ_BULK_ACTUAL_ABORTED -1
|
|
+
|
|
+typedef uint32_t BITSET_T;
|
|
+
|
|
+vcos_static_assert((sizeof(BITSET_T) * 8) == 32);
|
|
+
|
|
+#define BITSET_SIZE(b) ((b + 31) >> 5)
|
|
+#define BITSET_WORD(b) (b >> 5)
|
|
+#define BITSET_BIT(b) (1 << (b & 31))
|
|
+#define BITSET_ZERO(bs) memset(bs, 0, sizeof(bs))
|
|
+#define BITSET_IS_SET(bs, b) (bs[BITSET_WORD(b)] & BITSET_BIT(b))
|
|
+#define BITSET_SET(bs, b) (bs[BITSET_WORD(b)] |= BITSET_BIT(b))
|
|
+#define BITSET_CLR(bs, b) (bs[BITSET_WORD(b)] &= ~BITSET_BIT(b))
|
|
+
|
|
+#if VCHIQ_ENABLE_STATS
|
|
+#define VCHIQ_STATS_INC(state, stat) (state->stats. stat ++)
|
|
+#define VCHIQ_SERVICE_STATS_INC(service, stat) (service->stats. stat ++)
|
|
+#define VCHIQ_SERVICE_STATS_ADD(service, stat, addend) (service->stats. stat += addend)
|
|
+#else
|
|
+#define VCHIQ_STATS_INC(state, stat) ((void)0)
|
|
+#define VCHIQ_SERVICE_STATS_INC(service, stat) ((void)0)
|
|
+#define VCHIQ_SERVICE_STATS_ADD(service, stat, addend) ((void)0)
|
|
+#endif
|
|
+
|
|
+enum
|
|
+{
|
|
+ DEBUG_ENTRIES,
|
|
+#if VCHIQ_ENABLE_DEBUG
|
|
+ DEBUG_SLOT_HANDLER_COUNT,
|
|
+ DEBUG_SLOT_HANDLER_LINE,
|
|
+ DEBUG_PARSE_LINE,
|
|
+ DEBUG_PARSE_HEADER,
|
|
+ DEBUG_PARSE_MSGID,
|
|
+ DEBUG_AWAIT_COMPLETION_LINE,
|
|
+ DEBUG_DEQUEUE_MESSAGE_LINE,
|
|
+ DEBUG_SERVICE_CALLBACK_LINE,
|
|
+ DEBUG_MSG_QUEUE_FULL_COUNT,
|
|
+ DEBUG_COMPLETION_QUEUE_FULL_COUNT,
|
|
+#endif
|
|
+ DEBUG_MAX
|
|
+};
|
|
+
|
|
+#if VCHIQ_ENABLE_DEBUG
|
|
+
|
|
+#define DEBUG_INITIALISE(local) volatile int *debug_ptr = (local)->debug;
|
|
+#define DEBUG_TRACE(d) debug_ptr[DEBUG_ ## d] = __LINE__
|
|
+#define DEBUG_VALUE(d,v) debug_ptr[DEBUG_ ## d] = (v)
|
|
+#define DEBUG_COUNT(d) debug_ptr[DEBUG_ ## d]++
|
|
+
|
|
+#else /* VCHIQ_ENABLE_DEBUG */
|
|
+
|
|
+#define DEBUG_INITIALISE(local)
|
|
+#define DEBUG_TRACE(d)
|
|
+#define DEBUG_VALUE(d,v)
|
|
+#define DEBUG_COUNT(d)
|
|
+
|
|
+#endif /* VCHIQ_ENABLE_DEBUG */
|
|
+
|
|
+typedef enum
|
|
+{
|
|
+ VCHIQ_CONNSTATE_DISCONNECTED,
|
|
+ VCHIQ_CONNSTATE_CONNECTED,
|
|
+ VCHIQ_CONNSTATE_PAUSING,
|
|
+ VCHIQ_CONNSTATE_PAUSE_SENT,
|
|
+ VCHIQ_CONNSTATE_PAUSED,
|
|
+ VCHIQ_CONNSTATE_RESUMING
|
|
+} VCHIQ_CONNSTATE_T;
|
|
+
|
|
+enum
|
|
+{
|
|
+ VCHIQ_SRVSTATE_FREE,
|
|
+ VCHIQ_SRVSTATE_HIDDEN,
|
|
+ VCHIQ_SRVSTATE_LISTENING,
|
|
+ VCHIQ_SRVSTATE_OPENING,
|
|
+ VCHIQ_SRVSTATE_OPEN,
|
|
+ VCHIQ_SRVSTATE_CLOSESENT,
|
|
+ VCHIQ_SRVSTATE_CLOSING,
|
|
+ VCHIQ_SRVSTATE_CLOSEWAIT
|
|
+};
|
|
+
|
|
+enum
|
|
+{
|
|
+ VCHIQ_POLL_TERMINATE,
|
|
+ VCHIQ_POLL_TXNOTIFY,
|
|
+ VCHIQ_POLL_RXNOTIFY,
|
|
+ VCHIQ_POLL_COUNT
|
|
+};
|
|
+
|
|
+typedef enum
|
|
+{
|
|
+ VCHIQ_BULK_TRANSMIT,
|
|
+ VCHIQ_BULK_RECEIVE
|
|
+} VCHIQ_BULK_DIR_T;
|
|
+
|
|
+typedef struct vchiq_bulk_struct {
|
|
+ short mode;
|
|
+ short dir;
|
|
+ void *userdata;
|
|
+ VCHI_MEM_HANDLE_T handle;
|
|
+ void *data;
|
|
+ int size;
|
|
+ void *remote_data;
|
|
+ int remote_size;
|
|
+ int actual;
|
|
+} VCHIQ_BULK_T;
|
|
+
|
|
+typedef struct vchiq_bulk_queue_struct {
|
|
+ int local_insert; /* Where to insert the next local bulk */
|
|
+ int remote_insert; /* Where to insert the next remote bulk (master) */
|
|
+ int process; /* Bulk to transfer next */
|
|
+ int remote_notify; /* Bulk to notify the remote client of next (master) */
|
|
+ int remove; /* Bulk to notify the local client of, and remove, next */
|
|
+ VCHIQ_BULK_T bulks[VCHIQ_NUM_SERVICE_BULKS];
|
|
+} VCHIQ_BULK_QUEUE_T;
|
|
+
|
|
+typedef struct remote_event_struct {
|
|
+ volatile int armed;
|
|
+ volatile int fired;
|
|
+ VCOS_EVENT_T * event;
|
|
+} REMOTE_EVENT_T;
|
|
+
|
|
+typedef struct vchiq_state_struct VCHIQ_STATE_T;
|
|
+
|
|
+typedef struct vchiq_slot_struct {
|
|
+ char data[VCHIQ_SLOT_SIZE];
|
|
+} VCHIQ_SLOT_T;
|
|
+
|
|
+typedef struct vchiq_slot_info_struct {
|
|
+ /* Use two counters rather than one to avoid the need for a mutex. */
|
|
+ volatile short use_count;
|
|
+ volatile short release_count;
|
|
+} VCHIQ_SLOT_INFO_T;
|
|
+
|
|
+typedef struct vchiq_service_struct {
|
|
+ VCHIQ_SERVICE_BASE_T base;
|
|
+ volatile int srvstate;
|
|
+ unsigned int localport;
|
|
+ unsigned int remoteport;
|
|
+ int public_fourcc;
|
|
+ int client_id;
|
|
+ int auto_close;
|
|
+ VCOS_ATOMIC_FLAGS_T poll_flags;
|
|
+ short version;
|
|
+ short version_min;
|
|
+
|
|
+ VCHIQ_STATE_T *state;
|
|
+ VCHIQ_INSTANCE_T instance;
|
|
+
|
|
+ int service_use_count;
|
|
+
|
|
+ VCHIQ_BULK_QUEUE_T bulk_tx;
|
|
+ VCHIQ_BULK_QUEUE_T bulk_rx;
|
|
+
|
|
+ VCOS_EVENT_T remove_event;
|
|
+ VCOS_EVENT_T bulk_remove_event;
|
|
+ VCOS_MUTEX_T bulk_mutex;
|
|
+
|
|
+ struct service_stats_struct
|
|
+ {
|
|
+ int quota_stalls;
|
|
+ int slot_stalls;
|
|
+ int bulk_stalls;
|
|
+ int error_count;
|
|
+ int ctrl_tx_count;
|
|
+ int ctrl_rx_count;
|
|
+ int bulk_tx_count;
|
|
+ int bulk_rx_count;
|
|
+ int bulk_aborted_count;
|
|
+ uint64_t ctrl_tx_bytes;
|
|
+ uint64_t ctrl_rx_bytes;
|
|
+ uint64_t bulk_tx_bytes;
|
|
+ uint64_t bulk_rx_bytes;
|
|
+ } stats;
|
|
+} VCHIQ_SERVICE_T;
|
|
+
|
|
+/* The quota information is outside VCHIQ_SERVICE_T so that it can be
|
|
+ statically allocated, since for accounting reasons a service's slot
|
|
+ usage is carried over between users of the same port number.
|
|
+ */
|
|
+typedef struct vchiq_service_quota_struct {
|
|
+ int slot_quota;
|
|
+ int slot_use_count;
|
|
+ VCOS_EVENT_T quota_event;
|
|
+ int previous_tx_index;
|
|
+} VCHIQ_SERVICE_QUOTA_T;
|
|
+
|
|
+typedef struct vchiq_shared_state_struct {
|
|
+
|
|
+ /* A non-zero value here indicates that the content is valid. */
|
|
+ int initialised;
|
|
+
|
|
+ /* The first and last (inclusive) slots allocated to the owner. */
|
|
+ int slot_first;
|
|
+ int slot_last;
|
|
+
|
|
+ /* Signalling this event indicates that owner's slot handler thread should
|
|
+ run. */
|
|
+ REMOTE_EVENT_T trigger;
|
|
+
|
|
+ /* Indicates the byte position within the stream where the next message
|
|
+ will be written. The least significant bits are an index into the slot.
|
|
+ The next bits are the index of the slot in slot_queue. */
|
|
+ volatile int tx_pos;
|
|
+
|
|
+ /* This event should be signalled when a slot is recycled. */
|
|
+ REMOTE_EVENT_T recycle;
|
|
+
|
|
+ /* The slot_queue index where the next recycled slot will be written. */
|
|
+ volatile int slot_queue_recycle;
|
|
+
|
|
+ /* A circular buffer of slot indexes. */
|
|
+ int slot_queue[VCHIQ_MAX_SLOTS_PER_SIDE];
|
|
+
|
|
+ /* Debugging state */
|
|
+ volatile int debug[DEBUG_MAX];
|
|
+} VCHIQ_SHARED_STATE_T;
|
|
+
|
|
+typedef struct vchiq_slot_zero_struct {
|
|
+ int magic;
|
|
+ short version;
|
|
+ short version_min;
|
|
+ int slot_zero_size;
|
|
+ int slot_size;
|
|
+ int max_slots;
|
|
+ int max_slots_per_side;
|
|
+ int platform_data[2];
|
|
+ VCHIQ_SHARED_STATE_T master;
|
|
+ VCHIQ_SHARED_STATE_T slave;
|
|
+ VCHIQ_SLOT_INFO_T slots[VCHIQ_MAX_SLOTS];
|
|
+} VCHIQ_SLOT_ZERO_T;
|
|
+
|
|
+struct vchiq_state_struct {
|
|
+ int id;
|
|
+ int initialised;
|
|
+ VCHIQ_CONNSTATE_T conn_state;
|
|
+ int is_master;
|
|
+
|
|
+ VCHIQ_SHARED_STATE_T *local;
|
|
+ VCHIQ_SHARED_STATE_T *remote;
|
|
+ VCHIQ_SLOT_T *slot_data;
|
|
+
|
|
+ int default_slot_quota;
|
|
+
|
|
+ VCOS_EVENT_T connect; // event indicating connect message received
|
|
+ VCOS_MUTEX_T mutex; // mutex protecting services
|
|
+ VCHIQ_INSTANCE_T *instance;
|
|
+
|
|
+ VCOS_THREAD_T slot_handler_thread; // processes incoming messages
|
|
+ VCOS_THREAD_T recycle_thread; // processes recycled slots
|
|
+ VCOS_THREAD_T lp_thread; // processes low priority messages (eg suspend)
|
|
+
|
|
+ /* Local implementation of the trigger remote event */
|
|
+ VCOS_EVENT_T trigger_event;
|
|
+
|
|
+ /* Local implementation of the recycle remote event */
|
|
+ VCOS_EVENT_T recycle_event;
|
|
+
|
|
+ VCOS_EVENT_T lp_evt;
|
|
+
|
|
+ char *tx_data;
|
|
+ char *rx_data;
|
|
+ VCHIQ_SLOT_INFO_T *rx_info;
|
|
+
|
|
+ VCOS_MUTEX_T slot_mutex;
|
|
+
|
|
+ VCOS_MUTEX_T recycle_mutex;
|
|
+
|
|
+ VCOS_MUTEX_T suspend_resume_mutex;
|
|
+ VCOS_MUTEX_T use_count_mutex;
|
|
+
|
|
+ /* Global use count for videocore.
|
|
+ * This is equal to the sum of the use counts for all services. When this hits
|
|
+ * zero the videocore suspend procedure will be initiated. */
|
|
+ int videocore_use_count;
|
|
+
|
|
+ /* Flag to indicate whether videocore is currently suspended */
|
|
+ int videocore_suspended;
|
|
+
|
|
+ /* Indicates the byte position within the stream from where the next message
|
|
+ will be read. The least significant bits are an index into the slot.
|
|
+ The next bits are the index of the slot in remote->slot_queue. */
|
|
+ int rx_pos;
|
|
+
|
|
+ /* A cached copy of local->tx_pos. Only write to local->tx_pos, and read
|
|
+ from remote->tx_pos. */
|
|
+ int local_tx_pos;
|
|
+
|
|
+ /* The slot_queue index of the slot to become available next. */
|
|
+ int slot_queue_available;
|
|
+
|
|
+ /* A flag to indicate if any poll has been requested */
|
|
+ int poll_needed;
|
|
+
|
|
+ /* An array of bit sets indicating which services must be polled. */
|
|
+ VCOS_ATOMIC_FLAGS_T poll_services[BITSET_SIZE(VCHIQ_MAX_SERVICES)];
|
|
+
|
|
+ /* The number of the first unused service */
|
|
+ int unused_service;
|
|
+
|
|
+ /* Signalled when a free slot becomes available. */
|
|
+ VCOS_EVENT_T slot_available_event;
|
|
+
|
|
+ VCOS_EVENT_T slot_remove_event;
|
|
+
|
|
+ struct state_stats_struct
|
|
+ {
|
|
+ int slot_stalls;
|
|
+ int ctrl_tx_count;
|
|
+ int ctrl_rx_count;
|
|
+ int error_count;
|
|
+ } stats;
|
|
+
|
|
+ VCHIQ_SERVICE_T *services[VCHIQ_MAX_SERVICES];
|
|
+ VCHIQ_SERVICE_QUOTA_T service_quotas[VCHIQ_MAX_SERVICES];
|
|
+ VCHIQ_SLOT_INFO_T slot_info[VCHIQ_MAX_SLOTS];
|
|
+};
|
|
+
|
|
+extern VCHIQ_SLOT_ZERO_T *
|
|
+vchiq_init_slots(void *mem_base, int mem_size);
|
|
+
|
|
+extern VCHIQ_STATUS_T
|
|
+vchiq_init_state(VCHIQ_STATE_T *state, VCHIQ_SLOT_ZERO_T *slot_zero, int is_master);
|
|
+
|
|
+extern VCHIQ_STATUS_T
|
|
+vchiq_connect_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance);
|
|
+
|
|
+extern VCHIQ_SERVICE_T *
|
|
+vchiq_add_service_internal(VCHIQ_STATE_T *state,
|
|
+ const VCHIQ_SERVICE_PARAMS_T *params, int srvstate,
|
|
+ VCHIQ_INSTANCE_T instance);
|
|
+
|
|
+extern VCHIQ_STATUS_T
|
|
+vchiq_open_service_internal(VCHIQ_SERVICE_T *service, int client_id);
|
|
+
|
|
+extern VCHIQ_STATUS_T
|
|
+vchiq_close_service_internal(VCHIQ_SERVICE_T *service, int close_recvd);
|
|
+
|
|
+extern void
|
|
+vchiq_terminate_service_internal(VCHIQ_SERVICE_T *service);
|
|
+
|
|
+extern void
|
|
+vchiq_free_service_internal(VCHIQ_SERVICE_T *service);
|
|
+
|
|
+extern VCHIQ_STATUS_T
|
|
+vchiq_shutdown_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance);
|
|
+
|
|
+extern VCHIQ_STATUS_T
|
|
+vchiq_pause_internal(VCHIQ_STATE_T *state);
|
|
+
|
|
+extern VCHIQ_STATUS_T
|
|
+vchiq_resume_internal(VCHIQ_STATE_T *state);
|
|
+
|
|
+extern void
|
|
+remote_event_pollall(VCHIQ_STATE_T *state);
|
|
+
|
|
+extern VCHIQ_STATUS_T
|
|
+vchiq_bulk_transfer(VCHIQ_SERVICE_T *service,
|
|
+ VCHI_MEM_HANDLE_T memhandle, void *offset, int size, void *userdata,
|
|
+ VCHIQ_BULK_MODE_T mode, VCHIQ_BULK_DIR_T dir);
|
|
+
|
|
+extern void
|
|
+vchiq_dump_state(void *dump_context, VCHIQ_STATE_T *state);
|
|
+
|
|
+extern void
|
|
+vchiq_dump_service_state(void *dump_context, VCHIQ_SERVICE_T *service);
|
|
+
|
|
+/* The following functions are called from vchiq_core, and external
|
|
+ implementations must be provided. */
|
|
+
|
|
+extern VCHIQ_STATUS_T
|
|
+vchiq_prepare_bulk_data(VCHIQ_BULK_T *bulk,
|
|
+ VCHI_MEM_HANDLE_T memhandle, void *offset, int size, int dir);
|
|
+
|
|
+extern void
|
|
+vchiq_transfer_bulk(VCHIQ_BULK_T *bulk);
|
|
+
|
|
+extern void
|
|
+vchiq_complete_bulk(VCHIQ_BULK_T *bulk);
|
|
+
|
|
+extern VCHIQ_STATUS_T
|
|
+vchiq_copy_from_user(void *dst, const void *src, int size);
|
|
+
|
|
+extern void
|
|
+remote_event_signal(REMOTE_EVENT_T *event);
|
|
+
|
|
+extern void
|
|
+vchiq_platform_paused(VCHIQ_STATE_T *state);
|
|
+
|
|
+extern void
|
|
+vchiq_platform_resumed(VCHIQ_STATE_T *state);
|
|
+
|
|
+extern void
|
|
+vchiq_dump(void *dump_context, const char *str, int len);
|
|
+
|
|
+extern void
|
|
+vchiq_dump_platform_state(void *dump_context);
|
|
+
|
|
+extern void
|
|
+vchiq_dump_platform_instances(void *dump_context);
|
|
+
|
|
+extern void
|
|
+vchiq_dump_platform_service_state(void *dump_context,
|
|
+ VCHIQ_SERVICE_T *service);
|
|
+
|
|
+#endif
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_if.h
|
|
@@ -0,0 +1,148 @@
|
|
+/*
|
|
+ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
+ */
|
|
+
|
|
+#ifndef VCHIQ_IF_H
|
|
+#define VCHIQ_IF_H
|
|
+
|
|
+#include "interface/vchi/vchi_mh.h"
|
|
+
|
|
+#define VCHIQ_SLOT_SIZE 4096
|
|
+#define VCHIQ_MAX_MSG_SIZE (VCHIQ_SLOT_SIZE - sizeof(VCHIQ_HEADER_T))
|
|
+#define VCHIQ_CHANNEL_SIZE VCHIQ_MAX_MSG_SIZE /* For backwards compatibility */
|
|
+
|
|
+#define VCHIQ_MAKE_FOURCC(x0, x1, x2, x3) (((x0) << 24) | ((x1) << 16) | ((x2) << 8) | (x3))
|
|
+#define VCHIQ_GET_SERVICE_USERDATA(service) (service->userdata)
|
|
+#define VCHIQ_GET_SERVICE_FOURCC(service) (service->fourcc)
|
|
+
|
|
+typedef enum {
|
|
+ VCHIQ_SERVICE_OPENED, // service, -, -
|
|
+ VCHIQ_SERVICE_CLOSED, // service, -, -
|
|
+ VCHIQ_MESSAGE_AVAILABLE, // service, header, -
|
|
+ VCHIQ_BULK_TRANSMIT_DONE, // service, -, bulk_userdata
|
|
+ VCHIQ_BULK_RECEIVE_DONE, // service, -, bulk_userdata
|
|
+ VCHIQ_BULK_TRANSMIT_ABORTED, // service, -, bulk_userdata
|
|
+ VCHIQ_BULK_RECEIVE_ABORTED // service, -, bulk_userdata
|
|
+} VCHIQ_REASON_T;
|
|
+
|
|
+typedef enum
|
|
+{
|
|
+ VCHIQ_ERROR = -1,
|
|
+ VCHIQ_SUCCESS = 0,
|
|
+ VCHIQ_RETRY = 1
|
|
+} VCHIQ_STATUS_T;
|
|
+
|
|
+typedef enum
|
|
+{
|
|
+ VCHIQ_BULK_MODE_CALLBACK,
|
|
+ VCHIQ_BULK_MODE_BLOCKING,
|
|
+ VCHIQ_BULK_MODE_NOCALLBACK
|
|
+} VCHIQ_BULK_MODE_T;
|
|
+
|
|
+typedef enum
|
|
+{
|
|
+ VCHIQ_SERVICE_OPTION_AUTOCLOSE
|
|
+} VCHIQ_SERVICE_OPTION_T;
|
|
+
|
|
+#ifdef __HIGHC__
|
|
+/* Allow zero-sized arrays without warnings */
|
|
+#pragma warning (push)
|
|
+#pragma warning (disable : 4200)
|
|
+#endif
|
|
+
|
|
+typedef struct vchiq_header_struct {
|
|
+ /* The message identifier - opaque to applications. */
|
|
+ int msgid;
|
|
+
|
|
+ /* Size of message data. */
|
|
+ unsigned int size;
|
|
+
|
|
+ char data[0]; /* message */
|
|
+} VCHIQ_HEADER_T;
|
|
+
|
|
+#ifdef __HIGHC__
|
|
+#pragma warning (pop)
|
|
+#endif
|
|
+
|
|
+typedef struct {
|
|
+ const void *data;
|
|
+ int size;
|
|
+} VCHIQ_ELEMENT_T;
|
|
+
|
|
+typedef const struct vchiq_service_base_struct *VCHIQ_SERVICE_HANDLE_T;
|
|
+
|
|
+typedef VCHIQ_STATUS_T (*VCHIQ_CALLBACK_T)(VCHIQ_REASON_T, VCHIQ_HEADER_T *, VCHIQ_SERVICE_HANDLE_T, void *);
|
|
+
|
|
+typedef struct vchiq_service_base_struct {
|
|
+ int fourcc;
|
|
+ VCHIQ_CALLBACK_T callback;
|
|
+ void *userdata;
|
|
+} VCHIQ_SERVICE_BASE_T;
|
|
+
|
|
+typedef struct vchiq_service_params_struct {
|
|
+ int fourcc;
|
|
+ VCHIQ_CALLBACK_T callback;
|
|
+ void *userdata;
|
|
+ short version; /* Increment for non-trivial changes */
|
|
+ short version_min; /* Update for incompatible changes */
|
|
+} VCHIQ_SERVICE_PARAMS_T;
|
|
+
|
|
+typedef struct vchiq_config_struct {
|
|
+ int max_msg_size;
|
|
+ int bulk_threshold; /* The message size aboce which it is better to use
|
|
+ a bulk transfer (<= max_msg_size) */
|
|
+ int max_outstanding_bulks;
|
|
+ int max_services;
|
|
+ short version; /* The version of VCHIQ */
|
|
+ short version_min; /* The minimum compatible version of VCHIQ */
|
|
+} VCHIQ_CONFIG_T;
|
|
+
|
|
+typedef struct vchiq_instance_struct *VCHIQ_INSTANCE_T;
|
|
+
|
|
+extern VCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *pinstance);
|
|
+extern VCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance);
|
|
+extern VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance);
|
|
+extern VCHIQ_STATUS_T vchiq_add_service(VCHIQ_INSTANCE_T instance, int fourcc, VCHIQ_CALLBACK_T callback, void *userdata, VCHIQ_SERVICE_HANDLE_T *pservice);
|
|
+extern VCHIQ_STATUS_T vchiq_open_service(VCHIQ_INSTANCE_T instance, int fourcc, VCHIQ_CALLBACK_T callback, void *userdata, VCHIQ_SERVICE_HANDLE_T *pservice);
|
|
+extern VCHIQ_STATUS_T vchiq_add_service_params(VCHIQ_INSTANCE_T instance,
|
|
+ const VCHIQ_SERVICE_PARAMS_T *params,
|
|
+ VCHIQ_SERVICE_HANDLE_T *pservice);
|
|
+extern VCHIQ_STATUS_T vchiq_open_service_params(VCHIQ_INSTANCE_T instance,
|
|
+ const VCHIQ_SERVICE_PARAMS_T *params,
|
|
+ VCHIQ_SERVICE_HANDLE_T *pservice);
|
|
+extern VCHIQ_STATUS_T vchiq_close_service(VCHIQ_SERVICE_HANDLE_T service);
|
|
+extern VCHIQ_STATUS_T vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T service);
|
|
+extern VCHIQ_STATUS_T vchiq_use_service(VCHIQ_SERVICE_HANDLE_T service);
|
|
+extern VCHIQ_STATUS_T vchiq_release_service(VCHIQ_SERVICE_HANDLE_T service);
|
|
+
|
|
+extern VCHIQ_STATUS_T vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T service, const VCHIQ_ELEMENT_T *elements, int count);
|
|
+extern void vchiq_release_message(VCHIQ_SERVICE_HANDLE_T service, VCHIQ_HEADER_T *header);
|
|
+extern VCHIQ_STATUS_T vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T service, const void *data, int size, void *userdata);
|
|
+extern VCHIQ_STATUS_T vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T service, void *data, int size, void *userdata);
|
|
+extern VCHIQ_STATUS_T vchiq_queue_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T service, VCHI_MEM_HANDLE_T handle, const void *offset, int size, void *userdata);
|
|
+extern VCHIQ_STATUS_T vchiq_queue_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T service, VCHI_MEM_HANDLE_T handle, void *offset, int size, void *userdata);
|
|
+extern VCHIQ_STATUS_T vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T service, const void *data, int size, void *userdata, VCHIQ_BULK_MODE_T mode);
|
|
+extern VCHIQ_STATUS_T vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T service, void *data, int size, void *userdata, VCHIQ_BULK_MODE_T mode);
|
|
+extern VCHIQ_STATUS_T vchiq_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T service, VCHI_MEM_HANDLE_T handle, const void *offset, int size, void *userdata, VCHIQ_BULK_MODE_T mode);
|
|
+extern VCHIQ_STATUS_T vchiq_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T service, VCHI_MEM_HANDLE_T handle, void *offset, int size, void *userdata, VCHIQ_BULK_MODE_T mode);
|
|
+extern int vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T service);
|
|
+extern VCHIQ_STATUS_T vchiq_get_config(VCHIQ_INSTANCE_T instance, int config_size, VCHIQ_CONFIG_T *pconfig);
|
|
+extern VCHIQ_STATUS_T vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T service, VCHIQ_SERVICE_OPTION_T option, int value);
|
|
+
|
|
+extern VCHIQ_STATUS_T vchiq_dump_phys_mem( VCHIQ_SERVICE_HANDLE_T service, void *ptr, size_t num_bytes );
|
|
+
|
|
+#endif /* VCHIQ_IF_H */
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h
|
|
@@ -0,0 +1,105 @@
|
|
+/*
|
|
+ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
+ */
|
|
+
|
|
+#ifndef VCHIQ_IOCTLS_H
|
|
+#define VCHIQ_IOCTLS_H
|
|
+
|
|
+#include <linux/ioctl.h>
|
|
+#include "vchiq_if.h"
|
|
+
|
|
+#define VCHIQ_IOC_MAGIC 0xc4
|
|
+#define VCHIQ_INVALID_HANDLE -1
|
|
+
|
|
+typedef struct {
|
|
+ VCHIQ_SERVICE_PARAMS_T params;
|
|
+ int is_open;
|
|
+ int is_vchi;
|
|
+ int handle; /* OUT */
|
|
+} VCHIQ_CREATE_SERVICE_T;
|
|
+
|
|
+typedef struct {
|
|
+ int handle;
|
|
+ int count;
|
|
+ const VCHIQ_ELEMENT_T *elements;
|
|
+} VCHIQ_QUEUE_MESSAGE_T;
|
|
+
|
|
+typedef struct {
|
|
+ int handle;
|
|
+ void *data;
|
|
+ int size;
|
|
+ void *userdata;
|
|
+ VCHIQ_BULK_MODE_T mode;
|
|
+} VCHIQ_QUEUE_BULK_TRANSFER_T;
|
|
+
|
|
+typedef struct {
|
|
+ VCHIQ_REASON_T reason;
|
|
+ VCHIQ_HEADER_T *header;
|
|
+ void *service_userdata;
|
|
+ void *bulk_userdata;
|
|
+} VCHIQ_COMPLETION_DATA_T;
|
|
+
|
|
+typedef struct {
|
|
+ int count;
|
|
+ VCHIQ_COMPLETION_DATA_T *buf;
|
|
+ int msgbufsize;
|
|
+ int msgbufcount; /* IN/OUT */
|
|
+ void **msgbufs;
|
|
+} VCHIQ_AWAIT_COMPLETION_T;
|
|
+
|
|
+typedef struct {
|
|
+ int handle;
|
|
+ int blocking;
|
|
+ int bufsize;
|
|
+ void *buf;
|
|
+} VCHIQ_DEQUEUE_MESSAGE_T;
|
|
+
|
|
+typedef struct {
|
|
+ int config_size;
|
|
+ VCHIQ_CONFIG_T *pconfig;
|
|
+} VCHIQ_GET_CONFIG_T;
|
|
+
|
|
+typedef struct {
|
|
+ int handle;
|
|
+ VCHIQ_SERVICE_OPTION_T option;
|
|
+ int value;
|
|
+} VCHIQ_SET_SERVICE_OPTION_T;
|
|
+
|
|
+typedef struct {
|
|
+ void *virt_addr;
|
|
+ size_t num_bytes;
|
|
+} VCHIQ_DUMP_MEM_T;
|
|
+
|
|
+#define VCHIQ_IOC_CONNECT _IO(VCHIQ_IOC_MAGIC, 0)
|
|
+#define VCHIQ_IOC_SHUTDOWN _IO(VCHIQ_IOC_MAGIC, 1)
|
|
+#define VCHIQ_IOC_CREATE_SERVICE _IOWR(VCHIQ_IOC_MAGIC, 2, VCHIQ_CREATE_SERVICE_T)
|
|
+#define VCHIQ_IOC_REMOVE_SERVICE _IO(VCHIQ_IOC_MAGIC, 3)
|
|
+#define VCHIQ_IOC_QUEUE_MESSAGE _IOW(VCHIQ_IOC_MAGIC, 4, VCHIQ_QUEUE_MESSAGE_T)
|
|
+#define VCHIQ_IOC_QUEUE_BULK_TRANSMIT _IOW(VCHIQ_IOC_MAGIC, 5, VCHIQ_QUEUE_BULK_TRANSFER_T)
|
|
+#define VCHIQ_IOC_QUEUE_BULK_RECEIVE _IOW(VCHIQ_IOC_MAGIC, 6, VCHIQ_QUEUE_BULK_TRANSFER_T)
|
|
+#define VCHIQ_IOC_AWAIT_COMPLETION _IOW(VCHIQ_IOC_MAGIC, 7, VCHIQ_AWAIT_COMPLETION_T)
|
|
+#define VCHIQ_IOC_DEQUEUE_MESSAGE _IOW(VCHIQ_IOC_MAGIC, 8, VCHIQ_DEQUEUE_MESSAGE_T)
|
|
+#define VCHIQ_IOC_GET_CLIENT_ID _IO(VCHIQ_IOC_MAGIC, 9)
|
|
+#define VCHIQ_IOC_GET_CONFIG _IOW(VCHIQ_IOC_MAGIC, 10, VCHIQ_GET_CONFIG_T)
|
|
+#define VCHIQ_IOC_CLOSE_SERVICE _IO(VCHIQ_IOC_MAGIC, 11)
|
|
+#define VCHIQ_IOC_USE_SERVICE _IO(VCHIQ_IOC_MAGIC, 12)
|
|
+#define VCHIQ_IOC_RELEASE_SERVICE _IO(VCHIQ_IOC_MAGIC, 13)
|
|
+#define VCHIQ_IOC_SET_SERVICE_OPTION _IOW(VCHIQ_IOC_MAGIC, 14, VCHIQ_SET_SERVICE_OPTION_T)
|
|
+#define VCHIQ_IOC_DUMP_PHYS_MEM _IOW(VCHIQ_IOC_MAGIC, 15, VCHIQ_DUMP_MEM_T)
|
|
+#define VCHIQ_IOC_MAX 15
|
|
+
|
|
+#endif
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c
|
|
@@ -0,0 +1,297 @@
|
|
+/*****************************************************************************
|
|
+* Copyright 2001 - 2011 Broadcom Corporation. All rights reserved.
|
|
+*
|
|
+* Unless you and Broadcom execute a separate written software license
|
|
+* agreement governing use of this software, this software is licensed to you
|
|
+* under the terms of the GNU General Public License version 2, available at
|
|
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
|
+*
|
|
+* Notwithstanding the above, under no circumstances may you combine this
|
|
+* software in any way with any other Broadcom software provided under a
|
|
+* license other than the GPL, without Broadcom's express prior written
|
|
+* consent.
|
|
+*****************************************************************************/
|
|
+
|
|
+/* ---- Include Files ---------------------------------------------------- */
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+
|
|
+#include "vchiq_core.h"
|
|
+#include "vchiq_arm.h"
|
|
+#include "interface/vcos/vcos_logging.h"
|
|
+
|
|
+/* ---- Public Variables ------------------------------------------------- */
|
|
+
|
|
+extern VCOS_LOG_CAT_T vchiq_core_log_category;
|
|
+#define VCOS_LOG_CATEGORY (&vchiq_core_log_category)
|
|
+
|
|
+/* ---- Private Constants and Types -------------------------------------- */
|
|
+
|
|
+struct vchiq_instance_struct {
|
|
+ VCHIQ_STATE_T *state;
|
|
+
|
|
+ int connected;
|
|
+};
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* vchiq_initialise
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+VCHIQ_STATUS_T vchiq_initialise( VCHIQ_INSTANCE_T *instanceOut )
|
|
+{
|
|
+ VCHIQ_STATUS_T status = VCHIQ_ERROR;
|
|
+ VCHIQ_STATE_T *state;
|
|
+ VCHIQ_INSTANCE_T instance = NULL;
|
|
+
|
|
+ vcos_log_trace( "%s called", __func__ );
|
|
+
|
|
+ state = vchiq_get_state();
|
|
+ if (!state)
|
|
+ {
|
|
+ printk( KERN_ERR "%s: videocore not initialized\n", __func__ );
|
|
+ goto failed;
|
|
+ }
|
|
+
|
|
+ instance = kzalloc( sizeof(*instance), GFP_KERNEL );
|
|
+ if( !instance )
|
|
+ {
|
|
+ printk( KERN_ERR "%s: error allocating vchiq instance\n", __func__ );
|
|
+ goto failed;
|
|
+ }
|
|
+
|
|
+ instance->connected = 0;
|
|
+ instance->state = state;
|
|
+
|
|
+ *instanceOut = instance;
|
|
+
|
|
+ status = VCHIQ_SUCCESS;
|
|
+
|
|
+failed:
|
|
+ vcos_log_trace( "%s(%p): returning %d", __func__, instance, status );
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* vchiq_shutdown
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+VCHIQ_STATUS_T vchiq_shutdown( VCHIQ_INSTANCE_T instance )
|
|
+{
|
|
+ VCHIQ_STATUS_T status;
|
|
+ VCHIQ_STATE_T *state = instance->state;
|
|
+
|
|
+ vcos_log_trace( "%s(%p) called", __func__, instance );
|
|
+
|
|
+ vcos_mutex_lock(&state->mutex);
|
|
+
|
|
+ /* Remove all services */
|
|
+ status = vchiq_shutdown_internal(state, instance);
|
|
+
|
|
+ vcos_mutex_unlock(&state->mutex);
|
|
+
|
|
+ if (status == VCHIQ_SUCCESS)
|
|
+ kfree(instance);
|
|
+
|
|
+ vcos_log_trace( "%s(%p): returning %d", __func__, instance, status );
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* vchiq_is_connected
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+int vchiq_is_connected(VCHIQ_INSTANCE_T instance)
|
|
+{
|
|
+ return instance->connected;
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* vchiq_connect
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance)
|
|
+{
|
|
+ VCHIQ_STATUS_T status;
|
|
+ VCHIQ_STATE_T *state = instance->state;
|
|
+
|
|
+ vcos_log_trace( "%s(%p) called", __func__, instance );
|
|
+
|
|
+ if (vcos_mutex_lock(&state->mutex) != VCOS_SUCCESS) {
|
|
+ vcos_log_trace( "%s: call to vcos_mutex_lock failed", __func__ );
|
|
+ status = VCHIQ_RETRY;
|
|
+ goto failed;
|
|
+ }
|
|
+ status = vchiq_connect_internal(state, instance);
|
|
+
|
|
+ if (status == VCHIQ_SUCCESS)
|
|
+ instance->connected = 1;
|
|
+
|
|
+ vcos_mutex_unlock(&state->mutex);
|
|
+
|
|
+failed:
|
|
+ vcos_log_trace( "%s(%p): returning %d", __func__, instance, status );
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* vchiq_add_service
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+VCHIQ_STATUS_T vchiq_add_service(
|
|
+ VCHIQ_INSTANCE_T instance,
|
|
+ int fourcc,
|
|
+ VCHIQ_CALLBACK_T callback,
|
|
+ void *userdata,
|
|
+ VCHIQ_SERVICE_HANDLE_T *pservice)
|
|
+{
|
|
+ VCHIQ_SERVICE_PARAMS_T params;
|
|
+
|
|
+ params.fourcc = fourcc;
|
|
+ params.callback = callback;
|
|
+ params.userdata = userdata;
|
|
+ params.version = 0;
|
|
+ params.version_min = 0;
|
|
+
|
|
+ return vchiq_add_service_params(instance, ¶ms, pservice);
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* vchiq_open_service
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+VCHIQ_STATUS_T vchiq_open_service(
|
|
+ VCHIQ_INSTANCE_T instance,
|
|
+ int fourcc,
|
|
+ VCHIQ_CALLBACK_T callback,
|
|
+ void *userdata,
|
|
+ VCHIQ_SERVICE_HANDLE_T *pservice)
|
|
+{
|
|
+ VCHIQ_SERVICE_PARAMS_T params;
|
|
+
|
|
+ params.fourcc = fourcc;
|
|
+ params.callback = callback;
|
|
+ params.userdata = userdata;
|
|
+ params.version = 0;
|
|
+ params.version_min = 0;
|
|
+
|
|
+ return vchiq_open_service_params(instance, ¶ms, pservice);
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* vchiq_add_service_params
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+VCHIQ_STATUS_T vchiq_add_service_params(
|
|
+ VCHIQ_INSTANCE_T instance,
|
|
+ const VCHIQ_SERVICE_PARAMS_T *params,
|
|
+ VCHIQ_SERVICE_HANDLE_T *pservice)
|
|
+{
|
|
+ VCHIQ_STATUS_T status;
|
|
+ VCHIQ_STATE_T *state = instance->state;
|
|
+ VCHIQ_SERVICE_T *service;
|
|
+ int srvstate;
|
|
+
|
|
+ vcos_log_trace( "%s(%p) called", __func__, instance );
|
|
+
|
|
+ *pservice = NULL;
|
|
+
|
|
+ srvstate = vchiq_is_connected( instance )
|
|
+ ? VCHIQ_SRVSTATE_LISTENING
|
|
+ : VCHIQ_SRVSTATE_HIDDEN;
|
|
+
|
|
+ vcos_mutex_lock(&state->mutex);
|
|
+
|
|
+ service = vchiq_add_service_internal(
|
|
+ state,
|
|
+ params,
|
|
+ srvstate,
|
|
+ instance);
|
|
+
|
|
+ vcos_mutex_unlock(&state->mutex);
|
|
+
|
|
+ if ( service )
|
|
+ {
|
|
+ *pservice = &service->base;
|
|
+ status = VCHIQ_SUCCESS;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ status = VCHIQ_ERROR;
|
|
+ }
|
|
+
|
|
+ vcos_log_trace( "%s(%p): returning %d", __func__, instance, status );
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* vchiq_open_service_params
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+VCHIQ_STATUS_T vchiq_open_service_params(
|
|
+ VCHIQ_INSTANCE_T instance,
|
|
+ const VCHIQ_SERVICE_PARAMS_T *params,
|
|
+ VCHIQ_SERVICE_HANDLE_T *pservice)
|
|
+{
|
|
+ VCHIQ_STATUS_T status = VCHIQ_ERROR;
|
|
+ VCHIQ_STATE_T *state = instance->state;
|
|
+ VCHIQ_SERVICE_T *service;
|
|
+
|
|
+ vcos_log_trace( "%s(%p) called", __func__, instance );
|
|
+
|
|
+ *pservice = NULL;
|
|
+
|
|
+ if (!vchiq_is_connected(instance))
|
|
+ goto failed;
|
|
+
|
|
+ vcos_mutex_lock(&state->mutex);
|
|
+
|
|
+ service = vchiq_add_service_internal(state,
|
|
+ params,
|
|
+ VCHIQ_SRVSTATE_OPENING,
|
|
+ instance);
|
|
+
|
|
+ vcos_mutex_unlock(&state->mutex);
|
|
+
|
|
+ if ( service )
|
|
+ {
|
|
+ status = vchiq_open_service_internal(service, current->pid);
|
|
+ if ( status == VCHIQ_SUCCESS )
|
|
+ *pservice = &service->base;
|
|
+ else
|
|
+ vchiq_remove_service(&service->base);
|
|
+ }
|
|
+
|
|
+failed:
|
|
+ vcos_log_trace( "%s(%p): returning %d", __func__, instance, status );
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+EXPORT_SYMBOL(vchiq_initialise);
|
|
+EXPORT_SYMBOL(vchiq_shutdown);
|
|
+EXPORT_SYMBOL(vchiq_connect);
|
|
+EXPORT_SYMBOL(vchiq_add_service);
|
|
+EXPORT_SYMBOL(vchiq_open_service);
|
|
+EXPORT_SYMBOL(vchiq_add_service_params);
|
|
+EXPORT_SYMBOL(vchiq_open_service_params);
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_lib.c
|
|
@@ -0,0 +1,1518 @@
|
|
+/*
|
|
+ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
+ */
|
|
+
|
|
+#include <unistd.h>
|
|
+#include <fcntl.h>
|
|
+#include <sys/ioctl.h>
|
|
+#include <stdio.h>
|
|
+
|
|
+#include "vchiq.h"
|
|
+#include "vchiq_cfg.h"
|
|
+#include "vchiq_ioctl.h"
|
|
+#include "interface/vchi/vchi.h"
|
|
+#include "interface/vchi/common/endian.h"
|
|
+#include "interface/vcos/vcos.h"
|
|
+
|
|
+#define VCHIQ_MAX_INSTANCE_SERVICES 32
|
|
+#define MSGBUF_SIZE (VCHIQ_MAX_MSG_SIZE + sizeof(VCHIQ_HEADER_T))
|
|
+
|
|
+#define RETRY(r,x) do { r = x; } while ((r == -1) && (errno == EINTR))
|
|
+
|
|
+#define VCOS_LOG_CATEGORY (&vchiq_lib_log_category)
|
|
+
|
|
+typedef struct vchiq_service_struct
|
|
+{
|
|
+ VCHIQ_SERVICE_BASE_T base;
|
|
+ int handle;
|
|
+ int fd;
|
|
+ VCHI_CALLBACK_T vchi_callback;
|
|
+ void *peek_buf;
|
|
+ int peek_size;
|
|
+ int client_id;
|
|
+} VCHIQ_SERVICE_T;
|
|
+
|
|
+typedef struct vchiq_service_struct VCHI_SERVICE_T;
|
|
+
|
|
+struct vchiq_instance_struct
|
|
+{
|
|
+ int fd;
|
|
+ int initialised;
|
|
+ int connected;
|
|
+ VCOS_THREAD_T completion_thread;
|
|
+ VCOS_MUTEX_T mutex;
|
|
+ int used_services;
|
|
+ VCHIQ_SERVICE_T services[VCHIQ_MAX_INSTANCE_SERVICES];
|
|
+} vchiq_instance;
|
|
+
|
|
+typedef struct vchiq_instance_struct VCHI_STATE_T;
|
|
+
|
|
+/* Local data */
|
|
+static VCOS_LOG_LEVEL_T vchiq_default_lib_log_level = VCOS_LOG_WARN;
|
|
+static VCOS_LOG_CAT_T vchiq_lib_log_category;
|
|
+static VCOS_MUTEX_T vchiq_lib_mutex;
|
|
+static void *free_msgbufs;
|
|
+
|
|
+
|
|
+/* Local utility functions */
|
|
+static VCHIQ_INSTANCE_T
|
|
+vchiq_lib_init(void);
|
|
+
|
|
+static void *completion_thread(void *);
|
|
+
|
|
+static VCHIQ_STATUS_T
|
|
+create_service(VCHIQ_INSTANCE_T instance,
|
|
+ const VCHIQ_SERVICE_PARAMS_T *params,
|
|
+ VCHI_CALLBACK_T vchi_callback,
|
|
+ int is_open,
|
|
+ VCHIQ_SERVICE_HANDLE_T *pservice);
|
|
+
|
|
+static int
|
|
+fill_peek_buf(VCHI_SERVICE_T *service,
|
|
+ VCHI_FLAGS_T flags);
|
|
+
|
|
+static void *
|
|
+alloc_msgbuf(void);
|
|
+
|
|
+static void
|
|
+free_msgbuf(void *buf);
|
|
+
|
|
+static __inline int
|
|
+is_valid_instance(VCHIQ_INSTANCE_T instance)
|
|
+{
|
|
+ return (instance == &vchiq_instance) && (instance->initialised > 0);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * VCHIQ API
|
|
+ */
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_initialise(VCHIQ_INSTANCE_T *pinstance)
|
|
+{
|
|
+ VCHIQ_INSTANCE_T instance;
|
|
+
|
|
+ instance = vchiq_lib_init();
|
|
+
|
|
+ vcos_log_trace( "%s: returning instance handle %p", __func__, instance );
|
|
+
|
|
+ *pinstance = instance;
|
|
+
|
|
+ return (instance != NULL) ? VCHIQ_SUCCESS : VCHIQ_ERROR;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_shutdown(VCHIQ_INSTANCE_T instance)
|
|
+{
|
|
+ vcos_log_trace( "%s called", __func__ );
|
|
+
|
|
+ if (!is_valid_instance(instance))
|
|
+ return VCHIQ_ERROR;
|
|
+
|
|
+ vcos_mutex_lock(&instance->mutex);
|
|
+
|
|
+ if (instance->initialised == 1)
|
|
+ {
|
|
+ int i;
|
|
+
|
|
+ instance->initialised = -1; /* Enter limbo */
|
|
+
|
|
+ /* Remove all services */
|
|
+
|
|
+ for (i = 0; i < instance->used_services; i++)
|
|
+ {
|
|
+ if (instance->services[i].handle != VCHIQ_INVALID_HANDLE)
|
|
+ {
|
|
+ vchiq_remove_service(&instance->services[i].base);
|
|
+ instance->services[i].handle = VCHIQ_INVALID_HANDLE;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (instance->connected)
|
|
+ {
|
|
+ int ret;
|
|
+ RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_SHUTDOWN, 0));
|
|
+ vcos_assert(ret == 0);
|
|
+ vcos_thread_join(&instance->completion_thread, NULL);
|
|
+ instance->connected = 0;
|
|
+ }
|
|
+
|
|
+ close(instance->fd);
|
|
+ instance->fd = -1;
|
|
+ }
|
|
+ else if (instance->initialised > 1)
|
|
+ {
|
|
+ instance->initialised--;
|
|
+ }
|
|
+
|
|
+ vcos_mutex_unlock(&instance->mutex);
|
|
+
|
|
+ vcos_global_lock();
|
|
+
|
|
+ if (instance->initialised == -1)
|
|
+ {
|
|
+ vcos_mutex_delete(&instance->mutex);
|
|
+ instance->initialised = 0;
|
|
+ }
|
|
+
|
|
+ vcos_global_unlock();
|
|
+
|
|
+ vcos_log_trace( "%s returning", __func__ );
|
|
+
|
|
+ return VCHIQ_SUCCESS;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_connect(VCHIQ_INSTANCE_T instance)
|
|
+{
|
|
+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
|
|
+
|
|
+ vcos_log_trace( "%s called", __func__ );
|
|
+
|
|
+ if (!is_valid_instance(instance))
|
|
+ return VCHIQ_ERROR;
|
|
+
|
|
+ vcos_mutex_lock(&instance->mutex);
|
|
+
|
|
+ if (!instance->connected)
|
|
+ {
|
|
+ int ret = ioctl(instance->fd, VCHIQ_IOC_CONNECT, 0);
|
|
+ if (ret == 0)
|
|
+ {
|
|
+ VCOS_THREAD_ATTR_T attrs;
|
|
+ instance->connected = 1;
|
|
+ vcos_thread_attr_init(&attrs);
|
|
+ vcos_thread_create(&instance->completion_thread, "VCHIQ completion",
|
|
+ &attrs, completion_thread, instance);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ status = VCHIQ_ERROR;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ vcos_mutex_unlock(&instance->mutex);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_add_service(VCHIQ_INSTANCE_T instance,
|
|
+ int fourcc,
|
|
+ VCHIQ_CALLBACK_T callback,
|
|
+ void *userdata,
|
|
+ VCHIQ_SERVICE_HANDLE_T *pservice)
|
|
+{
|
|
+ VCHIQ_SERVICE_PARAMS_T params;
|
|
+
|
|
+ params.fourcc = fourcc;
|
|
+ params.callback = callback;
|
|
+ params.userdata = userdata;
|
|
+ params.version = 0;
|
|
+ params.version_min = 0;
|
|
+
|
|
+ return vchiq_add_service_params(instance, ¶ms, pservice);
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_open_service(VCHIQ_INSTANCE_T instance,
|
|
+ int fourcc,
|
|
+ VCHIQ_CALLBACK_T callback,
|
|
+ void *userdata,
|
|
+ VCHIQ_SERVICE_HANDLE_T *pservice)
|
|
+{
|
|
+ VCHIQ_SERVICE_PARAMS_T params;
|
|
+
|
|
+ params.fourcc = fourcc;
|
|
+ params.callback = callback;
|
|
+ params.userdata = userdata;
|
|
+ params.version = 0;
|
|
+ params.version_min = 0;
|
|
+
|
|
+ return vchiq_open_service_params(instance, ¶ms, pservice);
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_add_service_params(VCHIQ_INSTANCE_T instance,
|
|
+ const VCHIQ_SERVICE_PARAMS_T *params,
|
|
+ VCHIQ_SERVICE_HANDLE_T *pservice)
|
|
+{
|
|
+ VCHIQ_STATUS_T status;
|
|
+
|
|
+ vcos_log_trace( "%s called fourcc = 0x%08x (%c%c%c%c)",
|
|
+ __func__,
|
|
+ params->fourcc,
|
|
+ (params->fourcc >> 24) & 0xff,
|
|
+ (params->fourcc >> 16) & 0xff,
|
|
+ (params->fourcc >> 8) & 0xff,
|
|
+ (params->fourcc ) & 0xff );
|
|
+
|
|
+ if (!params->callback)
|
|
+ return VCHIQ_ERROR;
|
|
+
|
|
+ if (!is_valid_instance(instance))
|
|
+ return VCHIQ_ERROR;
|
|
+
|
|
+ status = create_service(instance,
|
|
+ params,
|
|
+ NULL/*vchi_callback*/,
|
|
+ 0/*!open*/,
|
|
+ pservice);
|
|
+
|
|
+ vcos_log_trace( "%s returning service handle = 0x%08x", __func__, (uint32_t)*pservice );
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_open_service_params(VCHIQ_INSTANCE_T instance,
|
|
+ const VCHIQ_SERVICE_PARAMS_T *params,
|
|
+ VCHIQ_SERVICE_HANDLE_T *pservice)
|
|
+{
|
|
+ VCHIQ_STATUS_T status;
|
|
+
|
|
+ vcos_log_trace( "%s called fourcc = 0x%08x (%c%c%c%c)",
|
|
+ __func__,
|
|
+ params->fourcc,
|
|
+ (params->fourcc >> 24) & 0xff,
|
|
+ (params->fourcc >> 16) & 0xff,
|
|
+ (params->fourcc >> 8) & 0xff,
|
|
+ (params->fourcc ) & 0xff );
|
|
+
|
|
+ if (!params->callback)
|
|
+ return VCHIQ_ERROR;
|
|
+
|
|
+ if (!is_valid_instance(instance))
|
|
+ return VCHIQ_ERROR;
|
|
+
|
|
+ status = create_service(instance,
|
|
+ params,
|
|
+ NULL/*vchi_callback*/,
|
|
+ 1/*open*/,
|
|
+ pservice);
|
|
+
|
|
+ vcos_log_trace( "%s returning service handle = 0x%08x", __func__, (uint32_t)*pservice );
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_close_service(VCHIQ_SERVICE_HANDLE_T handle)
|
|
+{
|
|
+ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle;
|
|
+ int ret;
|
|
+
|
|
+ vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle );
|
|
+
|
|
+ RETRY(ret,ioctl(service->fd, VCHIQ_IOC_CLOSE_SERVICE, service->handle));
|
|
+
|
|
+ if (ret != 0)
|
|
+ return VCHIQ_ERROR;
|
|
+
|
|
+ service->handle = VCHIQ_INVALID_HANDLE;
|
|
+ return VCHIQ_SUCCESS;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T handle)
|
|
+{
|
|
+ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle;
|
|
+ int ret;
|
|
+
|
|
+ vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle );
|
|
+
|
|
+ RETRY(ret,ioctl(service->fd, VCHIQ_IOC_REMOVE_SERVICE, service->handle));
|
|
+
|
|
+ if (ret != 0)
|
|
+ return VCHIQ_ERROR;
|
|
+
|
|
+ service->handle = VCHIQ_INVALID_HANDLE;
|
|
+ return VCHIQ_SUCCESS;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T handle,
|
|
+ const VCHIQ_ELEMENT_T *elements,
|
|
+ int count)
|
|
+{
|
|
+ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle;
|
|
+ VCHIQ_QUEUE_MESSAGE_T args;
|
|
+ int ret;
|
|
+
|
|
+ vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle );
|
|
+
|
|
+ args.handle = service->handle;
|
|
+ args.elements = elements;
|
|
+ args.count = count;
|
|
+ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_MESSAGE, &args));
|
|
+
|
|
+ return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR;
|
|
+}
|
|
+
|
|
+void
|
|
+vchiq_release_message(VCHIQ_SERVICE_HANDLE_T handle,
|
|
+ VCHIQ_HEADER_T *header)
|
|
+{
|
|
+ vcos_log_trace( "%s handle=%08x, header=%x", __func__, (uint32_t)handle, (uint32_t)header );
|
|
+
|
|
+ free_msgbuf(header);
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle,
|
|
+ const void *data,
|
|
+ int size,
|
|
+ void *userdata)
|
|
+{
|
|
+ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle;
|
|
+ VCHIQ_QUEUE_BULK_TRANSFER_T args;
|
|
+ int ret;
|
|
+
|
|
+ vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle );
|
|
+
|
|
+ args.handle = service->handle;
|
|
+ args.data = (void *)data;
|
|
+ args.size = size;
|
|
+ args.userdata = userdata;
|
|
+ args.mode = VCHIQ_BULK_MODE_CALLBACK;
|
|
+ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_TRANSMIT, &args));
|
|
+
|
|
+ return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle,
|
|
+ void *data,
|
|
+ int size,
|
|
+ void *userdata)
|
|
+{
|
|
+ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle;
|
|
+ VCHIQ_QUEUE_BULK_TRANSFER_T args;
|
|
+ int ret;
|
|
+
|
|
+ vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle );
|
|
+
|
|
+ args.handle = service->handle;
|
|
+ args.data = data;
|
|
+ args.size = size;
|
|
+ args.userdata = userdata;
|
|
+ args.mode = VCHIQ_BULK_MODE_CALLBACK;
|
|
+ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_RECEIVE, &args));
|
|
+
|
|
+ return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_queue_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T handle,
|
|
+ VCHI_MEM_HANDLE_T memhandle,
|
|
+ const void *offset,
|
|
+ int size,
|
|
+ void *userdata)
|
|
+{
|
|
+ vcos_assert(memhandle == VCHI_MEM_HANDLE_INVALID);
|
|
+
|
|
+ vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle );
|
|
+
|
|
+ return vchiq_queue_bulk_transmit(handle, offset, size, userdata);
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_queue_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T handle,
|
|
+ VCHI_MEM_HANDLE_T memhandle,
|
|
+ void *offset,
|
|
+ int size,
|
|
+ void *userdata)
|
|
+{
|
|
+ vcos_assert(memhandle == VCHI_MEM_HANDLE_INVALID);
|
|
+
|
|
+ vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle );
|
|
+
|
|
+ return vchiq_queue_bulk_receive(handle, offset, size, userdata);
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle,
|
|
+ const void *data,
|
|
+ int size,
|
|
+ void *userdata,
|
|
+ VCHIQ_BULK_MODE_T mode)
|
|
+{
|
|
+ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle;
|
|
+ VCHIQ_QUEUE_BULK_TRANSFER_T args;
|
|
+ int ret;
|
|
+
|
|
+ vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle );
|
|
+
|
|
+ args.handle = service->handle;
|
|
+ args.data = (void *)data;
|
|
+ args.size = size;
|
|
+ args.userdata = userdata;
|
|
+ args.mode = mode;
|
|
+ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_TRANSMIT, &args));
|
|
+
|
|
+ return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle,
|
|
+ void *data,
|
|
+ int size,
|
|
+ void *userdata,
|
|
+ VCHIQ_BULK_MODE_T mode)
|
|
+{
|
|
+ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle;
|
|
+ VCHIQ_QUEUE_BULK_TRANSFER_T args;
|
|
+ int ret;
|
|
+
|
|
+ vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle );
|
|
+
|
|
+ args.handle = service->handle;
|
|
+ args.data = data;
|
|
+ args.size = size;
|
|
+ args.userdata = userdata;
|
|
+ args.mode = mode;
|
|
+ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_RECEIVE, &args));
|
|
+
|
|
+ return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T handle,
|
|
+ VCHI_MEM_HANDLE_T memhandle,
|
|
+ const void *offset,
|
|
+ int size,
|
|
+ void *userdata,
|
|
+ VCHIQ_BULK_MODE_T mode)
|
|
+{
|
|
+ vcos_assert(memhandle == VCHI_MEM_HANDLE_INVALID);
|
|
+
|
|
+ return vchiq_bulk_transmit(handle, offset, size, userdata, mode);
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T handle,
|
|
+ VCHI_MEM_HANDLE_T memhandle,
|
|
+ void *offset,
|
|
+ int size,
|
|
+ void *userdata,
|
|
+ VCHIQ_BULK_MODE_T mode)
|
|
+{
|
|
+ vcos_assert(memhandle == VCHI_MEM_HANDLE_INVALID);
|
|
+
|
|
+ return vchiq_bulk_receive(handle, offset, size, userdata, mode);
|
|
+}
|
|
+
|
|
+int
|
|
+vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T handle)
|
|
+{
|
|
+ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle;
|
|
+
|
|
+ return ioctl(service->fd, VCHIQ_IOC_GET_CLIENT_ID, service->handle);
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_get_config(VCHIQ_INSTANCE_T instance,
|
|
+ int config_size,
|
|
+ VCHIQ_CONFIG_T *pconfig)
|
|
+{
|
|
+ VCHIQ_GET_CONFIG_T args;
|
|
+ int ret;
|
|
+
|
|
+ if (!is_valid_instance(instance))
|
|
+ return VCHIQ_ERROR;
|
|
+
|
|
+ args.config_size = config_size;
|
|
+ args.pconfig = pconfig;
|
|
+
|
|
+ RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_GET_CONFIG, &args));
|
|
+
|
|
+ return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR;
|
|
+}
|
|
+
|
|
+int32_t
|
|
+vchiq_use_service( const VCHIQ_SERVICE_HANDLE_T handle )
|
|
+{
|
|
+ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle;
|
|
+ int ret;
|
|
+ RETRY(ret,ioctl(service->fd, VCHIQ_IOC_USE_SERVICE, service->handle));
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int32_t
|
|
+vchiq_release_service( const VCHIQ_SERVICE_HANDLE_T handle )
|
|
+{
|
|
+ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle;
|
|
+ int ret;
|
|
+ RETRY(ret,ioctl(service->fd, VCHIQ_IOC_RELEASE_SERVICE, service->handle));
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+VCHIQ_STATUS_T
|
|
+vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T handle,
|
|
+ VCHIQ_SERVICE_OPTION_T option, int value)
|
|
+{
|
|
+ VCHIQ_SET_SERVICE_OPTION_T args;
|
|
+ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle;
|
|
+ int ret;
|
|
+
|
|
+ args.handle = service->handle;
|
|
+ args.option = option;
|
|
+ args.value = value;
|
|
+
|
|
+ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_SET_SERVICE_OPTION, &args));
|
|
+
|
|
+ return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * VCHI API
|
|
+ */
|
|
+
|
|
+/* ----------------------------------------------------------------------
|
|
+ * return pointer to the mphi message driver function table
|
|
+ * -------------------------------------------------------------------- */
|
|
+const VCHI_MESSAGE_DRIVER_T *
|
|
+vchi_mphi_message_driver_func_table( void )
|
|
+{
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/* ----------------------------------------------------------------------
|
|
+ * return a pointer to the 'single' connection driver fops
|
|
+ * -------------------------------------------------------------------- */
|
|
+const VCHI_CONNECTION_API_T *
|
|
+single_get_func_table( void )
|
|
+{
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+VCHI_CONNECTION_T *
|
|
+vchi_create_connection( const VCHI_CONNECTION_API_T * function_table,
|
|
+ const VCHI_MESSAGE_DRIVER_T * low_level )
|
|
+{
|
|
+ vcos_unused(function_table);
|
|
+ vcos_unused(low_level);
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_msg_peek
|
|
+ *
|
|
+ * Arguments: const VCHI_SERVICE_HANDLE_T handle,
|
|
+ * void **data,
|
|
+ * uint32_t *msg_size,
|
|
+ * VCHI_FLAGS_T flags
|
|
+ *
|
|
+ * Description: Routine to return a pointer to the current message (to allow in place processing)
|
|
+ * The message can be removed using vchi_msg_remove when you're finished
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t
|
|
+vchi_msg_peek( VCHI_SERVICE_HANDLE_T handle,
|
|
+ void **data,
|
|
+ uint32_t *msg_size,
|
|
+ VCHI_FLAGS_T flags )
|
|
+{
|
|
+ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle;
|
|
+ int ret;
|
|
+
|
|
+ ret = fill_peek_buf(service, flags);
|
|
+
|
|
+ if (ret == 0)
|
|
+ {
|
|
+ *data = service->peek_buf;
|
|
+ *msg_size = service->peek_size;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_msg_remove
|
|
+ *
|
|
+ * Arguments: const VCHI_SERVICE_HANDLE_T handle,
|
|
+ *
|
|
+ * Description: Routine to remove a message (after it has been read with vchi_msg_peek)
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t
|
|
+vchi_msg_remove( VCHI_SERVICE_HANDLE_T handle )
|
|
+{
|
|
+ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle;
|
|
+
|
|
+ /* Why would you call vchi_msg_remove without calling vchi_msg_peek first? */
|
|
+ vcos_assert(service->peek_size >= 0);
|
|
+
|
|
+ /* Invalidate the content but reuse the buffer */
|
|
+ service->peek_size = -1;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_msg_queue
|
|
+ *
|
|
+ * Arguments: VCHI_SERVICE_HANDLE_T handle,
|
|
+ * const void *data,
|
|
+ * uint32_t data_size,
|
|
+ * VCHI_FLAGS_T flags,
|
|
+ * void *msg_handle,
|
|
+ *
|
|
+ * Description: Thin wrapper to queue a message onto a connection
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t
|
|
+vchi_msg_queue( VCHI_SERVICE_HANDLE_T handle,
|
|
+ const void * data,
|
|
+ uint32_t data_size,
|
|
+ VCHI_FLAGS_T flags,
|
|
+ void * msg_handle )
|
|
+{
|
|
+ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle;
|
|
+ VCHIQ_QUEUE_MESSAGE_T args;
|
|
+ VCHIQ_ELEMENT_T element = {data, data_size};
|
|
+ int ret;
|
|
+
|
|
+ vcos_unused(msg_handle);
|
|
+ vcos_assert(flags == VCHI_FLAGS_BLOCK_UNTIL_QUEUED);
|
|
+
|
|
+ args.handle = service->handle;
|
|
+ args.elements = &element;
|
|
+ args.count = 1;
|
|
+ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_MESSAGE, &args));
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_bulk_queue_receive
|
|
+ *
|
|
+ * Arguments: VCHI_BULK_HANDLE_T handle,
|
|
+ * void *data_dst,
|
|
+ * const uint32_t data_size,
|
|
+ * VCHI_FLAGS_T flags
|
|
+ * void *bulk_handle
|
|
+ *
|
|
+ * Description: Routine to setup a rcv buffer
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t
|
|
+vchi_bulk_queue_receive( VCHI_SERVICE_HANDLE_T handle,
|
|
+ void * data_dst,
|
|
+ uint32_t data_size,
|
|
+ VCHI_FLAGS_T flags,
|
|
+ void * bulk_handle )
|
|
+{
|
|
+ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle;
|
|
+ VCHIQ_QUEUE_BULK_TRANSFER_T args;
|
|
+ int ret;
|
|
+
|
|
+ switch ((int)flags) {
|
|
+ case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
|
|
+ args.mode = VCHIQ_BULK_MODE_CALLBACK;
|
|
+ break;
|
|
+ case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE:
|
|
+ args.mode = VCHIQ_BULK_MODE_BLOCKING;
|
|
+ break;
|
|
+ case VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
|
|
+ case VCHI_FLAGS_NONE:
|
|
+ args.mode = VCHIQ_BULK_MODE_NOCALLBACK;
|
|
+ break;
|
|
+ default:
|
|
+ vcos_assert(0);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ args.handle = service->handle;
|
|
+ args.data = data_dst;
|
|
+ args.size = data_size;
|
|
+ args.userdata = bulk_handle;
|
|
+ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_RECEIVE, &args));
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_bulk_queue_transmit
|
|
+ *
|
|
+ * Arguments: VCHI_BULK_HANDLE_T handle,
|
|
+ * const void *data_src,
|
|
+ * uint32_t data_size,
|
|
+ * VCHI_FLAGS_T flags,
|
|
+ * void *bulk_handle
|
|
+ *
|
|
+ * Description: Routine to transmit some data
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t
|
|
+vchi_bulk_queue_transmit( VCHI_SERVICE_HANDLE_T handle,
|
|
+ const void * data_src,
|
|
+ uint32_t data_size,
|
|
+ VCHI_FLAGS_T flags,
|
|
+ void * bulk_handle )
|
|
+{
|
|
+ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle;
|
|
+ VCHIQ_QUEUE_BULK_TRANSFER_T args;
|
|
+ int ret;
|
|
+
|
|
+ switch ((int)flags) {
|
|
+ case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
|
|
+ args.mode = VCHIQ_BULK_MODE_CALLBACK;
|
|
+ break;
|
|
+ case VCHI_FLAGS_BLOCK_UNTIL_DATA_READ:
|
|
+ case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE:
|
|
+ args.mode = VCHIQ_BULK_MODE_BLOCKING;
|
|
+ break;
|
|
+ case VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
|
|
+ case VCHI_FLAGS_NONE:
|
|
+ args.mode = VCHIQ_BULK_MODE_NOCALLBACK;
|
|
+ break;
|
|
+ default:
|
|
+ vcos_assert(0);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ args.handle = service->handle;
|
|
+ args.data = (void *)data_src;
|
|
+ args.size = data_size;
|
|
+ args.userdata = bulk_handle;
|
|
+ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_TRANSMIT, &args));
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_msg_dequeue
|
|
+ *
|
|
+ * Arguments: VCHI_SERVICE_HANDLE_T handle,
|
|
+ * void *data,
|
|
+ * uint32_t max_data_size_to_read,
|
|
+ * uint32_t *actual_msg_size
|
|
+ * VCHI_FLAGS_T flags
|
|
+ *
|
|
+ * Description: Routine to dequeue a message into the supplied buffer
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t
|
|
+vchi_msg_dequeue( VCHI_SERVICE_HANDLE_T handle,
|
|
+ void *data,
|
|
+ uint32_t max_data_size_to_read,
|
|
+ uint32_t *actual_msg_size,
|
|
+ VCHI_FLAGS_T flags )
|
|
+{
|
|
+ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle;
|
|
+ VCHIQ_DEQUEUE_MESSAGE_T args;
|
|
+ int ret;
|
|
+
|
|
+ vcos_assert(flags == VCHI_FLAGS_NONE || flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE);
|
|
+
|
|
+ if (service->peek_size >= 0)
|
|
+ {
|
|
+ fprintf(stderr, "vchi_msg_dequeue -> using peek buffer\n");
|
|
+ if ((uint32_t)service->peek_size <= max_data_size_to_read)
|
|
+ {
|
|
+ memcpy(data, service->peek_buf, service->peek_size);
|
|
+ *actual_msg_size = service->peek_size;
|
|
+ /* Invalidate the peek data, but retain the buffer */
|
|
+ service->peek_size = -1;
|
|
+ ret = 0;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ ret = -1;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ args.handle = service->handle;
|
|
+ args.blocking = (flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE);
|
|
+ args.bufsize = max_data_size_to_read;
|
|
+ args.buf = data;
|
|
+ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_DEQUEUE_MESSAGE, &args));
|
|
+ if (ret >= 0)
|
|
+ {
|
|
+ *actual_msg_size = ret;
|
|
+ ret = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if ((ret < 0) && (errno != EWOULDBLOCK))
|
|
+ fprintf(stderr, "vchi_msg_dequeue -> %d(%d)\n", ret, errno);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_msg_queuev
|
|
+ *
|
|
+ * Arguments: VCHI_SERVICE_HANDLE_T handle,
|
|
+ * const void *data,
|
|
+ * uint32_t data_size,
|
|
+ * VCHI_FLAGS_T flags,
|
|
+ * void *msg_handle
|
|
+ *
|
|
+ * Description: Thin wrapper to queue a message onto a connection
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+
|
|
+vcos_static_assert(sizeof(VCHI_MSG_VECTOR_T) == sizeof(VCHIQ_ELEMENT_T));
|
|
+vcos_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_base) == offsetof(VCHIQ_ELEMENT_T, data));
|
|
+vcos_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_len) == offsetof(VCHIQ_ELEMENT_T, size));
|
|
+
|
|
+int32_t
|
|
+vchi_msg_queuev( VCHI_SERVICE_HANDLE_T handle,
|
|
+ VCHI_MSG_VECTOR_T * vector,
|
|
+ uint32_t count,
|
|
+ VCHI_FLAGS_T flags,
|
|
+ void *msg_handle )
|
|
+{
|
|
+ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle;
|
|
+ VCHIQ_QUEUE_MESSAGE_T args;
|
|
+ int ret;
|
|
+
|
|
+ vcos_unused(msg_handle);
|
|
+
|
|
+ vcos_assert(flags == VCHI_FLAGS_BLOCK_UNTIL_QUEUED);
|
|
+
|
|
+ args.handle = service->handle;
|
|
+ args.elements = (const VCHIQ_ELEMENT_T *)vector;
|
|
+ args.count = count;
|
|
+ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_MESSAGE, &args));
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_held_msg_release
|
|
+ *
|
|
+ * Arguments: VCHI_HELD_MSG_T *message
|
|
+ *
|
|
+ * Description: Routine to release a held message (after it has been read with vchi_msg_hold)
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t
|
|
+vchi_held_msg_release( VCHI_HELD_MSG_T *message )
|
|
+{
|
|
+ int ret = -1;
|
|
+
|
|
+ if (message && message->message && !message->service)
|
|
+ {
|
|
+ free_msgbuf(message->message);
|
|
+ ret = 0;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_msg_hold
|
|
+ *
|
|
+ * Arguments: VCHI_SERVICE_HANDLE_T handle,
|
|
+ * void **data,
|
|
+ * uint32_t *msg_size,
|
|
+ * VCHI_FLAGS_T flags,
|
|
+ * VCHI_HELD_MSG_T *message_handle
|
|
+ *
|
|
+ * Description: Routine to return a pointer to the current message (to allow in place processing)
|
|
+ * The message is dequeued - don't forget to release the message using
|
|
+ * vchi_held_msg_release when you're finished
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t
|
|
+vchi_msg_hold( VCHI_SERVICE_HANDLE_T handle,
|
|
+ void **data,
|
|
+ uint32_t *msg_size,
|
|
+ VCHI_FLAGS_T flags,
|
|
+ VCHI_HELD_MSG_T *message_handle )
|
|
+{
|
|
+ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle;
|
|
+ int ret;
|
|
+
|
|
+ ret = fill_peek_buf(service, flags);
|
|
+
|
|
+ if (ret == 0)
|
|
+ {
|
|
+ *data = service->peek_buf;
|
|
+ *msg_size = service->peek_size;
|
|
+
|
|
+ message_handle->message = service->peek_buf;
|
|
+ message_handle->service = NULL;
|
|
+
|
|
+ service->peek_size = -1;
|
|
+ service->peek_buf = NULL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_initialise
|
|
+ *
|
|
+ * Arguments: VCHI_INSTANCE_T *instance_handle
|
|
+ * VCHI_CONNECTION_T **connections
|
|
+ * const uint32_t num_connections
|
|
+ *
|
|
+ * Description: Initialises the hardware but does not transmit anything
|
|
+ * When run as a Host App this will be called twice hence the need
|
|
+ * to malloc the state information
|
|
+ *
|
|
+ * Returns: 0 if successful, failure otherwise
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t
|
|
+vchi_initialise( VCHI_INSTANCE_T *instance_handle )
|
|
+{
|
|
+ VCHIQ_INSTANCE_T instance;
|
|
+
|
|
+ instance = vchiq_lib_init();
|
|
+
|
|
+ vcos_log_trace( "%s: returning instance handle %p", __func__, instance );
|
|
+
|
|
+ *instance_handle = (VCHI_INSTANCE_T)instance;
|
|
+
|
|
+ return (instance != NULL) ? 0 : -1;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_connect
|
|
+ *
|
|
+ * Arguments: VCHI_CONNECTION_T **connections
|
|
+ * const uint32_t num_connections
|
|
+ * VCHI_INSTANCE_T instance_handle )
|
|
+ *
|
|
+ * Description: Starts the command service on each connection,
|
|
+ * causing INIT messages to be pinged back and forth
|
|
+ *
|
|
+ * Returns: 0 if successful, failure otherwise
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t
|
|
+vchi_connect( VCHI_CONNECTION_T **connections,
|
|
+ const uint32_t num_connections,
|
|
+ VCHI_INSTANCE_T instance_handle )
|
|
+{
|
|
+ VCHIQ_STATUS_T status;
|
|
+
|
|
+ vcos_unused(connections);
|
|
+ vcos_unused(num_connections);
|
|
+
|
|
+ status = vchiq_connect((VCHIQ_INSTANCE_T)instance_handle);
|
|
+
|
|
+ return (status == VCHIQ_SUCCESS) ? 0 : -1;
|
|
+}
|
|
+
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_disconnect
|
|
+ *
|
|
+ * Arguments: VCHI_INSTANCE_T instance_handle
|
|
+ *
|
|
+ * Description: Stops the command service on each connection,
|
|
+ * causing DE-INIT messages to be pinged back and forth
|
|
+ *
|
|
+ * Returns: 0 if successful, failure otherwise
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t
|
|
+vchi_disconnect( VCHI_INSTANCE_T instance_handle )
|
|
+{
|
|
+ VCHIQ_STATUS_T status;
|
|
+
|
|
+ status = vchiq_shutdown((VCHIQ_INSTANCE_T)instance_handle);
|
|
+
|
|
+ return (status == VCHIQ_SUCCESS) ? 0 : -1;
|
|
+}
|
|
+
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_service_open
|
|
+ * Name: vchi_service_create
|
|
+ *
|
|
+ * Arguments: VCHI_INSTANCE_T *instance_handle
|
|
+ * SERVICE_CREATION_T *setup,
|
|
+ * VCHI_SERVICE_HANDLE_T *handle
|
|
+ *
|
|
+ * Description: Routine to open a service
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t
|
|
+vchi_service_open( VCHI_INSTANCE_T instance_handle,
|
|
+ SERVICE_CREATION_T *setup,
|
|
+ VCHI_SERVICE_HANDLE_T *handle )
|
|
+{
|
|
+ VCHIQ_SERVICE_PARAMS_T params;
|
|
+ VCHIQ_STATUS_T status;
|
|
+
|
|
+ memset(¶ms, 0, sizeof(params));
|
|
+ params.fourcc = setup->service_id;
|
|
+ params.userdata = setup->callback_param;
|
|
+
|
|
+ status = create_service((VCHIQ_INSTANCE_T)instance_handle,
|
|
+ ¶ms,
|
|
+ setup->callback,
|
|
+ 1/*open*/,
|
|
+ (VCHIQ_SERVICE_HANDLE_T *)handle);
|
|
+
|
|
+ return (status == VCHIQ_SUCCESS) ? 0 : -1;
|
|
+}
|
|
+
|
|
+int32_t
|
|
+vchi_service_create( VCHI_INSTANCE_T instance_handle,
|
|
+ SERVICE_CREATION_T *setup, VCHI_SERVICE_HANDLE_T *handle )
|
|
+{
|
|
+ VCHIQ_SERVICE_PARAMS_T params;
|
|
+ VCHIQ_STATUS_T status;
|
|
+
|
|
+ memset(¶ms, 0, sizeof(params));
|
|
+ params.fourcc = setup->service_id;
|
|
+ params.userdata = setup->callback_param;
|
|
+
|
|
+ status = create_service((VCHIQ_INSTANCE_T)instance_handle,
|
|
+ ¶ms,
|
|
+ setup->callback,
|
|
+ 0/*!open*/,
|
|
+ (VCHIQ_SERVICE_HANDLE_T *)handle);
|
|
+
|
|
+ return (status == VCHIQ_SUCCESS) ? 0 : -1;
|
|
+}
|
|
+
|
|
+int32_t
|
|
+vchi_service_close( const VCHI_SERVICE_HANDLE_T handle )
|
|
+{
|
|
+ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle;
|
|
+ int ret;
|
|
+ RETRY(ret,ioctl(service->fd, VCHIQ_IOC_REMOVE_SERVICE, service->handle));
|
|
+
|
|
+ if (ret == 0)
|
|
+ service->handle = VCHIQ_INVALID_HANDLE;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int32_t
|
|
+vchi_service_destroy( const VCHI_SERVICE_HANDLE_T handle )
|
|
+{
|
|
+ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle;
|
|
+ int ret;
|
|
+ RETRY(ret,ioctl(service->fd, VCHIQ_IOC_REMOVE_SERVICE, service->handle));
|
|
+
|
|
+ if (ret == 0)
|
|
+ service->handle = VCHIQ_INVALID_HANDLE;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/* ----------------------------------------------------------------------
|
|
+ * read a uint32_t from buffer.
|
|
+ * network format is defined to be little endian
|
|
+ * -------------------------------------------------------------------- */
|
|
+uint32_t
|
|
+vchi_readbuf_uint32( const void *_ptr )
|
|
+{
|
|
+ const unsigned char *ptr = _ptr;
|
|
+ return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
|
|
+}
|
|
+
|
|
+/* ----------------------------------------------------------------------
|
|
+ * write a uint32_t to buffer.
|
|
+ * network format is defined to be little endian
|
|
+ * -------------------------------------------------------------------- */
|
|
+void
|
|
+vchi_writebuf_uint32( void *_ptr, uint32_t value )
|
|
+{
|
|
+ unsigned char *ptr = _ptr;
|
|
+ ptr[0] = (unsigned char)((value >> 0) & 0xFF);
|
|
+ ptr[1] = (unsigned char)((value >> 8) & 0xFF);
|
|
+ ptr[2] = (unsigned char)((value >> 16) & 0xFF);
|
|
+ ptr[3] = (unsigned char)((value >> 24) & 0xFF);
|
|
+}
|
|
+
|
|
+/* ----------------------------------------------------------------------
|
|
+ * read a uint16_t from buffer.
|
|
+ * network format is defined to be little endian
|
|
+ * -------------------------------------------------------------------- */
|
|
+uint16_t
|
|
+vchi_readbuf_uint16( const void *_ptr )
|
|
+{
|
|
+ const unsigned char *ptr = _ptr;
|
|
+ return ptr[0] | (ptr[1] << 8);
|
|
+}
|
|
+
|
|
+/* ----------------------------------------------------------------------
|
|
+ * write a uint16_t into the buffer.
|
|
+ * network format is defined to be little endian
|
|
+ * -------------------------------------------------------------------- */
|
|
+void
|
|
+vchi_writebuf_uint16( void *_ptr, uint16_t value )
|
|
+{
|
|
+ unsigned char *ptr = _ptr;
|
|
+ ptr[0] = (value >> 0) & 0xFF;
|
|
+ ptr[1] = (value >> 8) & 0xFF;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_service_use
|
|
+ *
|
|
+ * Arguments: const VCHI_SERVICE_HANDLE_T handle
|
|
+ *
|
|
+ * Description: Routine to increment refcount on a service
|
|
+ *
|
|
+ * Returns: void
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t
|
|
+vchi_service_use( const VCHI_SERVICE_HANDLE_T handle )
|
|
+{
|
|
+ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle;
|
|
+ int ret;
|
|
+ RETRY(ret,ioctl(service->fd, VCHIQ_IOC_USE_SERVICE, service->handle));
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_service_release
|
|
+ *
|
|
+ * Arguments: const VCHI_SERVICE_HANDLE_T handle
|
|
+ *
|
|
+ * Description: Routine to decrement refcount on a service
|
|
+ *
|
|
+ * Returns: void
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t vchi_service_release( const VCHI_SERVICE_HANDLE_T handle )
|
|
+{
|
|
+ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle;
|
|
+ int ret;
|
|
+ RETRY(ret,ioctl(service->fd, VCHIQ_IOC_RELEASE_SERVICE, service->handle));
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Support functions
|
|
+ */
|
|
+
|
|
+static VCHIQ_INSTANCE_T
|
|
+vchiq_lib_init(void)
|
|
+{
|
|
+ static int mutex_initialised = 0;
|
|
+ static VCOS_MUTEX_T vchiq_lib_mutex;
|
|
+ VCHIQ_INSTANCE_T instance = &vchiq_instance;
|
|
+
|
|
+ vcos_global_lock();
|
|
+ if (!mutex_initialised)
|
|
+ {
|
|
+ vcos_mutex_create(&vchiq_lib_mutex, "vchiq-init");
|
|
+
|
|
+ vcos_log_set_level( &vchiq_lib_log_category, vchiq_default_lib_log_level );
|
|
+ vcos_log_register( "vchiq_lib", &vchiq_lib_log_category );
|
|
+
|
|
+ mutex_initialised = 1;
|
|
+ }
|
|
+ vcos_global_unlock();
|
|
+
|
|
+ vcos_mutex_lock(&vchiq_lib_mutex);
|
|
+
|
|
+ if (instance->initialised == 0)
|
|
+ {
|
|
+ instance->fd = open("/dev/vchiq", O_RDWR);
|
|
+ if (instance->fd >= 0)
|
|
+ {
|
|
+ VCHIQ_GET_CONFIG_T args;
|
|
+ VCHIQ_CONFIG_T config;
|
|
+ int ret;
|
|
+ args.config_size = sizeof(config);
|
|
+ args.pconfig = &config;
|
|
+ RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_GET_CONFIG, &args));
|
|
+ if ((ret == 0) && (config.version >= VCHIQ_VERSION_MIN) && (config.version_min <= VCHIQ_VERSION))
|
|
+ {
|
|
+ instance->used_services = 0;
|
|
+ vcos_mutex_create(&instance->mutex, "VCHIQ instance");
|
|
+ instance->initialised = 1;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if (ret == 0)
|
|
+ {
|
|
+ vcos_log_error("Incompatible VCHIQ library - driver version %d (min %d), library version %d (min %d)",
|
|
+ config.version, config.version_min, VCHIQ_VERSION, VCHIQ_VERSION_MIN);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ vcos_log_error("Very incompatible VCHIQ library - cannot retrieve driver version");
|
|
+ }
|
|
+ close(instance->fd);
|
|
+ instance = NULL;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ instance = NULL;
|
|
+ }
|
|
+ }
|
|
+ else if (instance->initialised > 0)
|
|
+ {
|
|
+ instance->initialised++;
|
|
+ }
|
|
+
|
|
+ vcos_mutex_unlock(&vchiq_lib_mutex);
|
|
+
|
|
+ return instance;
|
|
+}
|
|
+
|
|
+static void *
|
|
+completion_thread(void *arg)
|
|
+{
|
|
+ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)arg;
|
|
+ VCHIQ_AWAIT_COMPLETION_T args;
|
|
+ VCHIQ_COMPLETION_DATA_T completions[8];
|
|
+ void *msgbufs[8];
|
|
+
|
|
+ static const VCHI_CALLBACK_REASON_T vchiq_reason_to_vchi[] =
|
|
+ {
|
|
+ VCHI_CALLBACK_SERVICE_OPENED, // VCHIQ_SERVICE_OPENED
|
|
+ VCHI_CALLBACK_SERVICE_CLOSED, // VCHIQ_SERVICE_CLOSED
|
|
+ VCHI_CALLBACK_MSG_AVAILABLE, // VCHIQ_MESSAGE_AVAILABLE
|
|
+ VCHI_CALLBACK_BULK_SENT, // VCHIQ_BULK_TRANSMIT_DONE
|
|
+ VCHI_CALLBACK_BULK_RECEIVED, // VCHIQ_BULK_RECEIVE_DONE
|
|
+ VCHI_CALLBACK_BULK_TRANSMIT_ABORTED, // VCHIQ_BULK_TRANSMIT_ABORTED
|
|
+ VCHI_CALLBACK_BULK_RECEIVE_ABORTED, // VCHIQ_BULK_RECEIVE_ABORTED
|
|
+ };
|
|
+
|
|
+ args.count = vcos_countof(completions);
|
|
+ args.buf = completions;
|
|
+ args.msgbufsize = MSGBUF_SIZE;
|
|
+ args.msgbufcount = 0;
|
|
+ args.msgbufs = msgbufs;
|
|
+
|
|
+ while (1)
|
|
+ {
|
|
+ int ret, i;
|
|
+
|
|
+ while ((unsigned int)args.msgbufcount < vcos_countof(msgbufs))
|
|
+ {
|
|
+ void *msgbuf = alloc_msgbuf();
|
|
+ if (msgbuf)
|
|
+ {
|
|
+ msgbufs[args.msgbufcount++] = msgbuf;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ fprintf(stderr, "vchiq_lib: failed to allocate a message buffer\n");
|
|
+ vcos_demand(args.msgbufcount != 0);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_AWAIT_COMPLETION, &args));
|
|
+
|
|
+ if (ret <= 0)
|
|
+ break;
|
|
+
|
|
+ for (i = 0; i < ret; i++)
|
|
+ {
|
|
+ VCHIQ_COMPLETION_DATA_T *completion = &completions[i];
|
|
+ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)completion->service_userdata;
|
|
+ if (service->base.callback)
|
|
+ {
|
|
+ vcos_log_trace( "callback(%x, %x, %x, %x)",
|
|
+ completion->reason, (uint32_t)completion->header,
|
|
+ (uint32_t)&service->base, (uint32_t)completion->bulk_userdata );
|
|
+ service->base.callback(completion->reason, completion->header,
|
|
+ &service->base, completion->bulk_userdata);
|
|
+ }
|
|
+ else if (service->vchi_callback)
|
|
+ {
|
|
+ VCHI_CALLBACK_REASON_T vchi_reason =
|
|
+ vchiq_reason_to_vchi[completion->reason];
|
|
+ service->vchi_callback(service->base.userdata, vchi_reason, completion->bulk_userdata);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static VCHIQ_STATUS_T
|
|
+create_service(VCHIQ_INSTANCE_T instance,
|
|
+ const VCHIQ_SERVICE_PARAMS_T *params,
|
|
+ VCHI_CALLBACK_T vchi_callback,
|
|
+ int is_open,
|
|
+ VCHIQ_SERVICE_HANDLE_T *pservice)
|
|
+{
|
|
+ VCHIQ_SERVICE_T *service = NULL;
|
|
+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
|
|
+ int i;
|
|
+
|
|
+ if (!is_valid_instance(instance))
|
|
+ return VCHIQ_ERROR;
|
|
+
|
|
+ vcos_mutex_lock(&instance->mutex);
|
|
+
|
|
+ /* Find a free service */
|
|
+ if (is_open)
|
|
+ {
|
|
+ /* Find a free service */
|
|
+ for (i = 0; i < instance->used_services; i++)
|
|
+ {
|
|
+ if (instance->services[i].handle == VCHIQ_INVALID_HANDLE)
|
|
+ {
|
|
+ service = &instance->services[i];
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ for (i = (instance->used_services - 1); i >= 0; i--)
|
|
+ {
|
|
+ VCHIQ_SERVICE_T *srv = &instance->services[i];
|
|
+ if (srv->handle == VCHIQ_INVALID_HANDLE)
|
|
+ {
|
|
+ service = srv;
|
|
+ }
|
|
+ else if (
|
|
+ (srv->base.fourcc == params->fourcc) &&
|
|
+ ((srv->base.callback != params->callback) ||
|
|
+ (srv->vchi_callback != vchi_callback)))
|
|
+ {
|
|
+ /* There is another server using this fourcc which doesn't match */
|
|
+ service = NULL;
|
|
+ status = VCHIQ_ERROR;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!service && (status == VCHIQ_SUCCESS) &&
|
|
+ (instance->used_services < VCHIQ_MAX_INSTANCE_SERVICES))
|
|
+ service = &instance->services[instance->used_services++];
|
|
+
|
|
+ if (service)
|
|
+ {
|
|
+ VCHIQ_CREATE_SERVICE_T args;
|
|
+ int ret;
|
|
+ service->base.fourcc = params->fourcc;
|
|
+ service->base.callback = params->callback;
|
|
+ service->vchi_callback = vchi_callback;
|
|
+ service->base.userdata = params->userdata;
|
|
+ service->fd = instance->fd;
|
|
+ service->peek_size = -1;
|
|
+ service->peek_buf = NULL;
|
|
+
|
|
+ args.params = *params;
|
|
+ args.params.userdata = service;
|
|
+ args.is_open = is_open;
|
|
+ args.is_vchi = (params->callback == NULL);
|
|
+ args.handle = -1; /* OUT parameter */
|
|
+ RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_CREATE_SERVICE, &args));
|
|
+ if (ret == 0)
|
|
+ service->handle = args.handle;
|
|
+ else
|
|
+ status = VCHIQ_ERROR;
|
|
+ }
|
|
+
|
|
+ *pservice = (status == VCHIQ_SUCCESS) ? &service->base : NULL;
|
|
+
|
|
+ vcos_mutex_unlock(&instance->mutex);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+static int
|
|
+fill_peek_buf(VCHI_SERVICE_T *service,
|
|
+ VCHI_FLAGS_T flags)
|
|
+{
|
|
+ VCHIQ_DEQUEUE_MESSAGE_T args;
|
|
+ int ret = 0;
|
|
+
|
|
+ vcos_assert(flags == VCHI_FLAGS_NONE || flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE);
|
|
+
|
|
+ if (service->peek_size < 0)
|
|
+ {
|
|
+ if (!service->peek_buf)
|
|
+ service->peek_buf = alloc_msgbuf();
|
|
+
|
|
+ if (service->peek_buf)
|
|
+ {
|
|
+ args.handle = service->handle;
|
|
+ args.blocking = (flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE);
|
|
+ args.bufsize = MSGBUF_SIZE;
|
|
+ args.buf = service->peek_buf;
|
|
+
|
|
+ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_DEQUEUE_MESSAGE, &args));
|
|
+
|
|
+ if (ret >= 0)
|
|
+ {
|
|
+ service->peek_size = ret;
|
|
+ ret = 0;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ ret = -1;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ ret = -1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+static void *
|
|
+alloc_msgbuf(void)
|
|
+{
|
|
+ void *msgbuf;
|
|
+ vcos_mutex_lock(&vchiq_lib_mutex);
|
|
+ msgbuf = free_msgbufs;
|
|
+ if (msgbuf)
|
|
+ free_msgbufs = *(void **)msgbuf;
|
|
+ vcos_mutex_unlock(&vchiq_lib_mutex);
|
|
+ if (!msgbuf)
|
|
+ msgbuf = malloc(MSGBUF_SIZE);
|
|
+ return msgbuf;
|
|
+}
|
|
+
|
|
+static void
|
|
+free_msgbuf(void *buf)
|
|
+{
|
|
+ vcos_mutex_lock(&vchiq_lib_mutex);
|
|
+ *(void **)buf = free_msgbufs;
|
|
+ free_msgbufs = buf;
|
|
+ vcos_mutex_unlock(&vchiq_lib_mutex);
|
|
+}
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_memdrv.h
|
|
@@ -0,0 +1,45 @@
|
|
+/*****************************************************************************
|
|
+* Copyright 2001 - 2010 Broadcom Corporation. All rights reserved.
|
|
+*
|
|
+* Unless you and Broadcom execute a separate written software license
|
|
+* agreement governing use of this software, this software is licensed to you
|
|
+* under the terms of the GNU General Public License version 2, available at
|
|
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
|
+*
|
|
+* Notwithstanding the above, under no circumstances may you combine this
|
|
+* software in any way with any other Broadcom software provided under a
|
|
+* license other than the GPL, without Broadcom's express prior written
|
|
+* consent.
|
|
+*****************************************************************************/
|
|
+
|
|
+#ifndef VCHIQ_MEMDRV_H
|
|
+#define VCHIQ_MEMDRV_H
|
|
+
|
|
+/* ---- Include Files ----------------------------------------------------- */
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include "vchiq_if.h"
|
|
+
|
|
+/* ---- Constants and Types ---------------------------------------------- */
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ void *armSharedMemVirt;
|
|
+ dma_addr_t armSharedMemPhys;
|
|
+ size_t armSharedMemSize;
|
|
+
|
|
+ void *vcSharedMemVirt;
|
|
+ dma_addr_t vcSharedMemPhys;
|
|
+ size_t vcSharedMemSize;
|
|
+
|
|
+} VCHIQ_SHARED_MEM_INFO_T;
|
|
+
|
|
+/* ---- Variable Externs ------------------------------------------------- */
|
|
+
|
|
+/* ---- Function Prototypes ---------------------------------------------- */
|
|
+
|
|
+void vchiq_get_shared_mem_info( VCHIQ_SHARED_MEM_INFO_T *info );
|
|
+
|
|
+VCHIQ_STATUS_T vchiq_memdrv_initialise(void);
|
|
+
|
|
+#endif
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_pagelist.h
|
|
@@ -0,0 +1,43 @@
|
|
+/*
|
|
+ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
+ */
|
|
+
|
|
+#ifndef VCHIQ_PAGELIST_H
|
|
+#define VCHIQ_PAGELIST_H
|
|
+
|
|
+#ifndef PAGE_SIZE
|
|
+#define PAGE_SIZE 4096
|
|
+#endif
|
|
+#define CACHE_LINE_SIZE 32
|
|
+#define PAGELIST_WRITE 0
|
|
+#define PAGELIST_READ 1
|
|
+#define PAGELIST_READ_WITH_FRAGMENTS 2
|
|
+
|
|
+typedef struct pagelist_struct {
|
|
+ unsigned long length;
|
|
+ unsigned short type;
|
|
+ unsigned short offset;
|
|
+ unsigned long addrs[1]; /* N.B. 12 LSBs hold the number of following
|
|
+ pages at consecutive addresses. */
|
|
+} PAGELIST_T;
|
|
+
|
|
+typedef struct fragments_struct {
|
|
+ char headbuf[CACHE_LINE_SIZE];
|
|
+ char tailbuf[CACHE_LINE_SIZE];
|
|
+} FRAGMENTS_T;
|
|
+
|
|
+#endif /* VCHIQ_PAGELIST_H */
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c
|
|
@@ -0,0 +1,970 @@
|
|
+/*
|
|
+ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
+ */
|
|
+
|
|
+#include "interface/vchi/vchi.h"
|
|
+#include "vchiq.h"
|
|
+#include "vchiq_core.h"
|
|
+
|
|
+#include "vchiq_util.h"
|
|
+
|
|
+#include <stddef.h>
|
|
+
|
|
+#if defined(__KERNEL__)
|
|
+#include <linux/module.h>
|
|
+#endif
|
|
+
|
|
+#define vchiq_status_to_vchi(status) ((int32_t)status)
|
|
+
|
|
+typedef struct {
|
|
+ VCHIQ_SERVICE_HANDLE_T handle;
|
|
+
|
|
+ VCHIU_QUEUE_T queue;
|
|
+
|
|
+ VCHI_CALLBACK_T callback;
|
|
+ void *callback_param;
|
|
+} SHIM_SERVICE_T;
|
|
+
|
|
+/* ----------------------------------------------------------------------
|
|
+ * return pointer to the mphi message driver function table
|
|
+ * -------------------------------------------------------------------- */
|
|
+#ifdef WIN32
|
|
+const VCHI_MESSAGE_DRIVER_T *
|
|
+mphi_get_func_table( void )
|
|
+{
|
|
+ return NULL;
|
|
+}
|
|
+#endif
|
|
+
|
|
+/* ----------------------------------------------------------------------
|
|
+ * return pointer to the mphi message driver function table
|
|
+ * -------------------------------------------------------------------- */
|
|
+const VCHI_MESSAGE_DRIVER_T *
|
|
+vchi_mphi_message_driver_func_table( void )
|
|
+{
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/* ----------------------------------------------------------------------
|
|
+ * return a pointer to the 'single' connection driver fops
|
|
+ * -------------------------------------------------------------------- */
|
|
+const VCHI_CONNECTION_API_T *
|
|
+single_get_func_table( void )
|
|
+{
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+VCHI_CONNECTION_T * vchi_create_connection( const VCHI_CONNECTION_API_T * function_table,
|
|
+ const VCHI_MESSAGE_DRIVER_T * low_level)
|
|
+{
|
|
+ vcos_unused(function_table);
|
|
+ vcos_unused(low_level);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_msg_peek
|
|
+ *
|
|
+ * Arguments: const VCHI_SERVICE_HANDLE_T handle,
|
|
+ * void **data,
|
|
+ * uint32_t *msg_size,
|
|
+ * VCHI_FLAGS_T flags
|
|
+ *
|
|
+ * Description: Routine to return a pointer to the current message (to allow in place processing)
|
|
+ * The message can be removed using vchi_msg_remove when you're finished
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t vchi_msg_peek( VCHI_SERVICE_HANDLE_T handle,
|
|
+ void **data,
|
|
+ uint32_t *msg_size,
|
|
+ VCHI_FLAGS_T flags )
|
|
+{
|
|
+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle;
|
|
+ VCHIQ_HEADER_T *header;
|
|
+
|
|
+ vcos_assert(flags == VCHI_FLAGS_NONE || flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE);
|
|
+
|
|
+ if (flags == VCHI_FLAGS_NONE)
|
|
+ if (vchiu_queue_is_empty(&service->queue))
|
|
+ return -1;
|
|
+
|
|
+ header = vchiu_queue_peek(&service->queue);
|
|
+
|
|
+ *data = header->data;
|
|
+ *msg_size = header->size;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_msg_remove
|
|
+ *
|
|
+ * Arguments: const VCHI_SERVICE_HANDLE_T handle,
|
|
+ *
|
|
+ * Description: Routine to remove a message (after it has been read with vchi_msg_peek)
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t vchi_msg_remove( VCHI_SERVICE_HANDLE_T handle )
|
|
+{
|
|
+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle;
|
|
+ VCHIQ_HEADER_T *header;
|
|
+
|
|
+ header = vchiu_queue_pop(&service->queue);
|
|
+
|
|
+ vchiq_release_message(service->handle, header);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_msg_queue
|
|
+ *
|
|
+ * Arguments: VCHI_SERVICE_HANDLE_T handle,
|
|
+ * const void *data,
|
|
+ * uint32_t data_size,
|
|
+ * VCHI_FLAGS_T flags,
|
|
+ * void *msg_handle,
|
|
+ *
|
|
+ * Description: Thin wrapper to queue a message onto a connection
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t vchi_msg_queue( VCHI_SERVICE_HANDLE_T handle,
|
|
+ const void * data,
|
|
+ uint32_t data_size,
|
|
+ VCHI_FLAGS_T flags,
|
|
+ void * msg_handle )
|
|
+{
|
|
+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle;
|
|
+ VCHIQ_ELEMENT_T element = {data, data_size};
|
|
+ VCHIQ_STATUS_T status;
|
|
+
|
|
+ vcos_unused(msg_handle);
|
|
+
|
|
+ vcos_assert(flags == VCHI_FLAGS_BLOCK_UNTIL_QUEUED);
|
|
+
|
|
+ status = vchiq_queue_message(service->handle, &element, 1);
|
|
+
|
|
+ // On some platforms, like linux kernel, vchiq_queue_message() may return
|
|
+ // VCHIQ_RETRY, so we need to implment a retry mechanism since this
|
|
+ // function is supposed to block until queued
|
|
+ while ( status == VCHIQ_RETRY )
|
|
+ {
|
|
+ vcos_sleep( 1 );
|
|
+ status = vchiq_queue_message(service->handle, &element, 1);
|
|
+ }
|
|
+
|
|
+ return vchiq_status_to_vchi(status);
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_bulk_queue_receive
|
|
+ *
|
|
+ * Arguments: VCHI_BULK_HANDLE_T handle,
|
|
+ * void *data_dst,
|
|
+ * const uint32_t data_size,
|
|
+ * VCHI_FLAGS_T flags
|
|
+ * void *bulk_handle
|
|
+ *
|
|
+ * Description: Routine to setup a rcv buffer
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t vchi_bulk_queue_receive( VCHI_SERVICE_HANDLE_T handle,
|
|
+ void * data_dst,
|
|
+ uint32_t data_size,
|
|
+ VCHI_FLAGS_T flags,
|
|
+ void * bulk_handle )
|
|
+{
|
|
+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle;
|
|
+ VCHIQ_BULK_MODE_T mode;
|
|
+ VCHIQ_STATUS_T status;
|
|
+
|
|
+ switch ((int)flags) {
|
|
+ case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
|
|
+ vcos_assert(service->callback);
|
|
+ mode = VCHIQ_BULK_MODE_CALLBACK;
|
|
+ break;
|
|
+ case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE:
|
|
+ mode = VCHIQ_BULK_MODE_BLOCKING;
|
|
+ break;
|
|
+ case VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
|
|
+ case VCHI_FLAGS_NONE:
|
|
+ mode = VCHIQ_BULK_MODE_NOCALLBACK;
|
|
+ break;
|
|
+ default:
|
|
+ vcos_assert(0);
|
|
+ return vchiq_status_to_vchi(VCHIQ_ERROR);
|
|
+ }
|
|
+
|
|
+ status = vchiq_bulk_receive(service->handle, data_dst, data_size,
|
|
+ bulk_handle, mode);
|
|
+
|
|
+ // On some platforms, like linux kernel, vchiq_bulk_receive() may return
|
|
+ // VCHIQ_RETRY, so we need to implment a retry mechanism since this
|
|
+ // function is supposed to block until queued
|
|
+ while ( status == VCHIQ_RETRY )
|
|
+ {
|
|
+ vcos_sleep( 1 );
|
|
+ status = vchiq_bulk_receive(service->handle, data_dst, data_size,
|
|
+ bulk_handle, mode);
|
|
+ }
|
|
+
|
|
+ return vchiq_status_to_vchi(status);
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_bulk_queue_receive_reloc
|
|
+ *
|
|
+ * Arguments: VCHI_BULK_HANDLE_T handle,
|
|
+ * VCHI_MEM_HANDLE_T h
|
|
+ * uint32_t offset
|
|
+ * const uint32_t data_size,
|
|
+ * VCHI_FLAGS_T flags
|
|
+ * void *bulk_handle
|
|
+ *
|
|
+ * Description: Routine to setup a relocatable rcv buffer
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t vchi_bulk_queue_receive_reloc( const VCHI_SERVICE_HANDLE_T handle,
|
|
+ VCHI_MEM_HANDLE_T h,
|
|
+ uint32_t offset,
|
|
+ uint32_t data_size,
|
|
+ const VCHI_FLAGS_T flags,
|
|
+ void * const bulk_handle )
|
|
+{
|
|
+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle;
|
|
+ VCHIQ_BULK_MODE_T mode;
|
|
+ VCHIQ_STATUS_T status;
|
|
+
|
|
+ switch ((int)flags) {
|
|
+ case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
|
|
+ vcos_assert(service->callback);
|
|
+ mode = VCHIQ_BULK_MODE_CALLBACK;
|
|
+ break;
|
|
+ case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE:
|
|
+ mode = VCHIQ_BULK_MODE_BLOCKING;
|
|
+ break;
|
|
+ case VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
|
|
+ case VCHI_FLAGS_NONE:
|
|
+ mode = VCHIQ_BULK_MODE_NOCALLBACK;
|
|
+ break;
|
|
+ default:
|
|
+ vcos_assert(0);
|
|
+ return vchiq_status_to_vchi(VCHIQ_ERROR);
|
|
+ }
|
|
+
|
|
+ status = vchiq_bulk_receive_handle(service->handle, h, (void*)offset,
|
|
+ data_size, bulk_handle, mode);
|
|
+
|
|
+ // On some platforms, like linux kernel, vchiq_bulk_receive_handle() may
|
|
+ // return VCHIQ_RETRY, so we need to implment a retry mechanism since
|
|
+ // this function is supposed to block until queued
|
|
+ while ( status == VCHIQ_RETRY )
|
|
+ {
|
|
+ vcos_sleep( 1 );
|
|
+ status = vchiq_bulk_receive_handle(service->handle, h, (void*)offset,
|
|
+ data_size, bulk_handle, mode);
|
|
+ }
|
|
+
|
|
+ return vchiq_status_to_vchi(status);
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_bulk_queue_transmit
|
|
+ *
|
|
+ * Arguments: VCHI_BULK_HANDLE_T handle,
|
|
+ * const void *data_src,
|
|
+ * uint32_t data_size,
|
|
+ * VCHI_FLAGS_T flags,
|
|
+ * void *bulk_handle
|
|
+ *
|
|
+ * Description: Routine to transmit some data
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t vchi_bulk_queue_transmit( VCHI_SERVICE_HANDLE_T handle,
|
|
+ const void * data_src,
|
|
+ uint32_t data_size,
|
|
+ VCHI_FLAGS_T flags,
|
|
+ void * bulk_handle )
|
|
+{
|
|
+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle;
|
|
+ VCHIQ_BULK_MODE_T mode;
|
|
+ VCHIQ_STATUS_T status;
|
|
+
|
|
+ switch ((int)flags) {
|
|
+ case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
|
|
+ vcos_assert(service->callback);
|
|
+ mode = VCHIQ_BULK_MODE_CALLBACK;
|
|
+ break;
|
|
+ case VCHI_FLAGS_BLOCK_UNTIL_DATA_READ:
|
|
+ case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE:
|
|
+ mode = VCHIQ_BULK_MODE_BLOCKING;
|
|
+ break;
|
|
+ case VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
|
|
+ case VCHI_FLAGS_NONE:
|
|
+ mode = VCHIQ_BULK_MODE_NOCALLBACK;
|
|
+ break;
|
|
+ default:
|
|
+ vcos_assert(0);
|
|
+ return vchiq_status_to_vchi(VCHIQ_ERROR);
|
|
+ }
|
|
+
|
|
+ status = vchiq_bulk_transmit(service->handle, data_src, data_size,
|
|
+ bulk_handle, mode);
|
|
+
|
|
+ // On some platforms, like linux kernel, vchiq_bulk_transmit() may return
|
|
+ // VCHIQ_RETRY, so we need to implment a retry mechanism since this
|
|
+ // function is supposed to block until queued
|
|
+ while ( status == VCHIQ_RETRY )
|
|
+ {
|
|
+ vcos_sleep( 1 );
|
|
+ status = vchiq_bulk_transmit(service->handle, data_src, data_size,
|
|
+ bulk_handle, mode);
|
|
+ }
|
|
+
|
|
+ return vchiq_status_to_vchi(status);
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_bulk_queue_transmit_reloc
|
|
+ *
|
|
+ * Arguments: VCHI_BULK_HANDLE_T handle,
|
|
+ * VCHI_MEM_HANDLE_T h_src,
|
|
+ * uint32_t offset,
|
|
+ * uint32_t data_size,
|
|
+ * VCHI_FLAGS_T flags,
|
|
+ * void *bulk_handle
|
|
+ *
|
|
+ * Description: Routine to transmit some data from a relocatable buffer
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+
|
|
+int32_t vchi_bulk_queue_transmit_reloc( VCHI_SERVICE_HANDLE_T handle,
|
|
+ VCHI_MEM_HANDLE_T h_src,
|
|
+ uint32_t offset,
|
|
+ uint32_t data_size,
|
|
+ VCHI_FLAGS_T flags,
|
|
+ void * const bulk_handle )
|
|
+{
|
|
+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle;
|
|
+ VCHIQ_BULK_MODE_T mode;
|
|
+ VCHIQ_STATUS_T status;
|
|
+
|
|
+ switch ((int)flags) {
|
|
+ case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
|
|
+ vcos_assert(service->callback);
|
|
+ mode = VCHIQ_BULK_MODE_CALLBACK;
|
|
+ break;
|
|
+ case VCHI_FLAGS_BLOCK_UNTIL_DATA_READ:
|
|
+ case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE:
|
|
+ mode = VCHIQ_BULK_MODE_BLOCKING;
|
|
+ break;
|
|
+ case VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
|
|
+ case VCHI_FLAGS_NONE:
|
|
+ mode = VCHIQ_BULK_MODE_NOCALLBACK;
|
|
+ break;
|
|
+ default:
|
|
+ vcos_assert(0);
|
|
+ return vchiq_status_to_vchi(VCHIQ_ERROR);
|
|
+ }
|
|
+
|
|
+ status = vchiq_bulk_transmit_handle(service->handle, h_src, (void*)offset,
|
|
+ data_size, bulk_handle, mode);
|
|
+
|
|
+ // On some platforms, like linux kernel, vchiq_bulk_transmit_handle() may
|
|
+ // return VCHIQ_RETRY, so we need to implment a retry mechanism since this
|
|
+ // function is supposed to block until queued
|
|
+ while ( status == VCHIQ_RETRY )
|
|
+ {
|
|
+ vcos_sleep( 1 );
|
|
+ status = vchiq_bulk_transmit_handle(service->handle, h_src, (void*)offset,
|
|
+ data_size, bulk_handle, mode);
|
|
+ }
|
|
+
|
|
+ return vchiq_status_to_vchi(status);
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_msg_dequeue
|
|
+ *
|
|
+ * Arguments: VCHI_SERVICE_HANDLE_T handle,
|
|
+ * void *data,
|
|
+ * uint32_t max_data_size_to_read,
|
|
+ * uint32_t *actual_msg_size
|
|
+ * VCHI_FLAGS_T flags
|
|
+ *
|
|
+ * Description: Routine to dequeue a message into the supplied buffer
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t vchi_msg_dequeue( VCHI_SERVICE_HANDLE_T handle,
|
|
+ void *data,
|
|
+ uint32_t max_data_size_to_read,
|
|
+ uint32_t *actual_msg_size,
|
|
+ VCHI_FLAGS_T flags )
|
|
+{
|
|
+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle;
|
|
+ VCHIQ_HEADER_T *header;
|
|
+
|
|
+ vcos_assert(flags == VCHI_FLAGS_NONE || flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE);
|
|
+
|
|
+ if (flags == VCHI_FLAGS_NONE)
|
|
+ if (vchiu_queue_is_empty(&service->queue))
|
|
+ return -1;
|
|
+
|
|
+ header = vchiu_queue_pop(&service->queue);
|
|
+
|
|
+ memcpy(data, header->data, header->size < max_data_size_to_read ? header->size : max_data_size_to_read);
|
|
+
|
|
+ *actual_msg_size = header->size;
|
|
+
|
|
+ vchiq_release_message(service->handle, header);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_msg_queuev
|
|
+ *
|
|
+ * Arguments: VCHI_SERVICE_HANDLE_T handle,
|
|
+ * const void *data,
|
|
+ * uint32_t data_size,
|
|
+ * VCHI_FLAGS_T flags,
|
|
+ * void *msg_handle
|
|
+ *
|
|
+ * Description: Thin wrapper to queue a message onto a connection
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+
|
|
+vcos_static_assert(sizeof(VCHI_MSG_VECTOR_T) == sizeof(VCHIQ_ELEMENT_T));
|
|
+vcos_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_base) == offsetof(VCHIQ_ELEMENT_T, data));
|
|
+vcos_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_len) == offsetof(VCHIQ_ELEMENT_T, size));
|
|
+
|
|
+int32_t vchi_msg_queuev( VCHI_SERVICE_HANDLE_T handle,
|
|
+ VCHI_MSG_VECTOR_T * vector,
|
|
+ uint32_t count,
|
|
+ VCHI_FLAGS_T flags,
|
|
+ void *msg_handle )
|
|
+{
|
|
+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle;
|
|
+
|
|
+ vcos_unused(msg_handle);
|
|
+
|
|
+ vcos_assert(flags == VCHI_FLAGS_BLOCK_UNTIL_QUEUED);
|
|
+
|
|
+ return vchiq_status_to_vchi(vchiq_queue_message(service->handle, (const VCHIQ_ELEMENT_T *)vector, count));
|
|
+}
|
|
+
|
|
+#ifdef USE_MEMMGR
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_msg_queuev_ex
|
|
+ *
|
|
+ * Arguments: VCHI_SERVICE_HANDLE_T handle,
|
|
+ * VCHI_MSG_VECTOR_EX_T *vector
|
|
+ * uint32_t count
|
|
+ * VCHI_FLAGS_T flags,
|
|
+ * void *msg_handle
|
|
+ *
|
|
+ * Description: Thin wrapper to queue an array of messages onto a connection
|
|
+ * Supports resolving MEM_HANDLE's at last possible moment to avoid deadlocks.
|
|
+ *
|
|
+ * Currently just a shim, so deadlocks are still possible!
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t vchi_msg_queuev_ex( const VCHI_SERVICE_HANDLE_T handle,
|
|
+ VCHI_MSG_VECTOR_EX_T * const vector,
|
|
+ const uint32_t count,
|
|
+ const VCHI_FLAGS_T flags,
|
|
+ void * const msg_handle )
|
|
+{
|
|
+ int32_t success = -1;
|
|
+ // For now, we don't actually support sending anything other than
|
|
+ // a pointer, so handles have to be patched up; this is likely
|
|
+ // to cause deadlocks. This code is not designed to be either
|
|
+ // pretty, efficient, or deadlock-free.
|
|
+
|
|
+ #define max_vecs 16
|
|
+ VCHI_MSG_VECTOR_T copy[max_vecs];
|
|
+ const uint8_t *orig[max_vecs];
|
|
+
|
|
+ int i;
|
|
+ vcos_unused(msg_handle);
|
|
+
|
|
+ if (count > sizeof(copy)/sizeof(copy[0]))
|
|
+ {
|
|
+ vcos_assert(0);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ for (i=0; i<count; i++)
|
|
+ {
|
|
+ VCHI_MSG_VECTOR_EX_T *v = vector+i;
|
|
+
|
|
+ switch (vector[i].type)
|
|
+ {
|
|
+ case VCHI_VEC_POINTER:
|
|
+ copy[i].vec_base = v->u.ptr.vec_base;
|
|
+ copy[i].vec_len = v->u.ptr.vec_len;
|
|
+ break;
|
|
+ case VCHI_VEC_HANDLE:
|
|
+ vcos_assert(v->u.handle.offset+v->u.handle.vec_len <= mem_get_size(v->u.handle.handle));
|
|
+ copy[i].vec_base = (uint8_t*)mem_lock(v->u.handle.handle) + v->u.handle.offset;
|
|
+ orig[i] = copy[i].vec_base;
|
|
+ copy[i].vec_len = v->u.handle.vec_len;
|
|
+ break;
|
|
+ case VCHI_VEC_LIST:
|
|
+ vcos_assert(0); // FIXME: implement this
|
|
+ break;
|
|
+ default:
|
|
+ vcos_assert(0);
|
|
+ }
|
|
+ }
|
|
+ success = vchi_msg_queuev( handle,
|
|
+ copy,
|
|
+ count,
|
|
+ flags &~ VCHI_FLAGS_INTERNAL,
|
|
+ msg_handle );
|
|
+ if (vcos_verify(success == 0))
|
|
+ {
|
|
+ // now we need to patch up the vectors if any have been only partially consumed, and
|
|
+ // unlock memory handles.
|
|
+
|
|
+ for (i=0; i<count; i++)
|
|
+ {
|
|
+ VCHI_MSG_VECTOR_EX_T *v = vector+i;
|
|
+
|
|
+ switch (vector[i].type)
|
|
+ {
|
|
+ case VCHI_VEC_POINTER:
|
|
+ if (flags & VCHI_FLAGS_ALLOW_PARTIAL)
|
|
+ {
|
|
+ v->u.ptr.vec_base = copy[i].vec_base;
|
|
+ v->u.ptr.vec_len = copy[i].vec_len;
|
|
+ }
|
|
+ break;
|
|
+ case VCHI_VEC_HANDLE:
|
|
+ mem_unlock(v->u.handle.handle);
|
|
+ if (flags & VCHI_FLAGS_ALLOW_PARTIAL)
|
|
+ {
|
|
+ const uint8_t *old = orig[i];
|
|
+ uint32_t change = (const uint8_t*)copy[i].vec_base-old;
|
|
+ v->u.handle.offset += change;
|
|
+ v->u.handle.vec_len -= change;
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ vcos_assert(0);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return vchiq_status_to_vchi(success);
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_held_msg_release
|
|
+ *
|
|
+ * Arguments: VCHI_HELD_MSG_T *message
|
|
+ *
|
|
+ * Description: Routine to release a held message (after it has been read with vchi_msg_hold)
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t vchi_held_msg_release( VCHI_HELD_MSG_T *message )
|
|
+{
|
|
+ vchiq_release_message((VCHIQ_SERVICE_HANDLE_T)message->service, (VCHIQ_HEADER_T *)message->message);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_msg_hold
|
|
+ *
|
|
+ * Arguments: VCHI_SERVICE_HANDLE_T handle,
|
|
+ * void **data,
|
|
+ * uint32_t *msg_size,
|
|
+ * VCHI_FLAGS_T flags,
|
|
+ * VCHI_HELD_MSG_T *message_handle
|
|
+ *
|
|
+ * Description: Routine to return a pointer to the current message (to allow in place processing)
|
|
+ * The message is dequeued - don't forget to release the message using
|
|
+ * vchi_held_msg_release when you're finished
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t vchi_msg_hold( VCHI_SERVICE_HANDLE_T handle,
|
|
+ void **data,
|
|
+ uint32_t *msg_size,
|
|
+ VCHI_FLAGS_T flags,
|
|
+ VCHI_HELD_MSG_T *message_handle )
|
|
+{
|
|
+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle;
|
|
+ VCHIQ_HEADER_T *header;
|
|
+
|
|
+ vcos_assert(flags == VCHI_FLAGS_NONE || flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE);
|
|
+
|
|
+ if (flags == VCHI_FLAGS_NONE)
|
|
+ if (vchiu_queue_is_empty(&service->queue))
|
|
+ return -1;
|
|
+
|
|
+ header = vchiu_queue_pop(&service->queue);
|
|
+
|
|
+ *data = header->data;
|
|
+ *msg_size = header->size;
|
|
+
|
|
+ message_handle->service = (struct opaque_vchi_service_t *)service->handle;
|
|
+ message_handle->message = header;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_initialise
|
|
+ *
|
|
+ * Arguments: VCHI_INSTANCE_T *instance_handle
|
|
+ * VCHI_CONNECTION_T **connections
|
|
+ * const uint32_t num_connections
|
|
+ *
|
|
+ * Description: Initialises the hardware but does not transmit anything
|
|
+ * When run as a Host App this will be called twice hence the need
|
|
+ * to malloc the state information
|
|
+ *
|
|
+ * Returns: 0 if successful, failure otherwise
|
|
+ *
|
|
+ ***********************************************************/
|
|
+
|
|
+int32_t vchi_initialise( VCHI_INSTANCE_T *instance_handle )
|
|
+{
|
|
+ VCHIQ_INSTANCE_T instance;
|
|
+ VCHIQ_STATUS_T status;
|
|
+
|
|
+ status = vchiq_initialise(&instance);
|
|
+
|
|
+ *instance_handle = (VCHI_INSTANCE_T)instance;
|
|
+
|
|
+ return vchiq_status_to_vchi(status);
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_connect
|
|
+ *
|
|
+ * Arguments: VCHI_CONNECTION_T **connections
|
|
+ * const uint32_t num_connections
|
|
+ * VCHI_INSTANCE_T instance_handle )
|
|
+ *
|
|
+ * Description: Starts the command service on each connection,
|
|
+ * causing INIT messages to be pinged back and forth
|
|
+ *
|
|
+ * Returns: 0 if successful, failure otherwise
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t vchi_connect( VCHI_CONNECTION_T **connections,
|
|
+ const uint32_t num_connections,
|
|
+ VCHI_INSTANCE_T instance_handle )
|
|
+{
|
|
+ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle;
|
|
+
|
|
+ vcos_unused(connections);
|
|
+ vcos_unused(num_connections);
|
|
+
|
|
+ return vchiq_connect(instance);
|
|
+}
|
|
+
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_disconnect
|
|
+ *
|
|
+ * Arguments: VCHI_INSTANCE_T instance_handle
|
|
+ *
|
|
+ * Description: Stops the command service on each connection,
|
|
+ * causing DE-INIT messages to be pinged back and forth
|
|
+ *
|
|
+ * Returns: 0 if successful, failure otherwise
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t vchi_disconnect( VCHI_INSTANCE_T instance_handle )
|
|
+{
|
|
+ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle;
|
|
+ return vchiq_status_to_vchi(vchiq_shutdown(instance));
|
|
+}
|
|
+
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_service_open
|
|
+ * Name: vchi_service_create
|
|
+ *
|
|
+ * Arguments: VCHI_INSTANCE_T *instance_handle
|
|
+ * SERVICE_CREATION_T *setup,
|
|
+ * VCHI_SERVICE_HANDLE_T *handle
|
|
+ *
|
|
+ * Description: Routine to open a service
|
|
+ *
|
|
+ * Returns: int32_t - success == 0
|
|
+ *
|
|
+ ***********************************************************/
|
|
+
|
|
+static VCHIQ_STATUS_T shim_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, VCHIQ_SERVICE_HANDLE_T handle, void *bulk_user)
|
|
+{
|
|
+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)VCHIQ_GET_SERVICE_USERDATA(handle);
|
|
+
|
|
+ switch (reason) {
|
|
+ case VCHIQ_MESSAGE_AVAILABLE:
|
|
+ vchiu_queue_push(&service->queue, header);
|
|
+
|
|
+ if (service->callback)
|
|
+ service->callback(service->callback_param, VCHI_CALLBACK_MSG_AVAILABLE, NULL);
|
|
+ break;
|
|
+ case VCHIQ_BULK_TRANSMIT_DONE:
|
|
+ if (service->callback)
|
|
+ service->callback(service->callback_param, VCHI_CALLBACK_BULK_SENT, bulk_user);
|
|
+ break;
|
|
+ case VCHIQ_BULK_RECEIVE_DONE:
|
|
+ if (service->callback)
|
|
+ service->callback(service->callback_param, VCHI_CALLBACK_BULK_RECEIVED, bulk_user);
|
|
+ break;
|
|
+ case VCHIQ_SERVICE_CLOSED:
|
|
+ if (service->callback)
|
|
+ service->callback(service->callback_param, VCHI_CALLBACK_SERVICE_CLOSED, NULL);
|
|
+ break;
|
|
+ case VCHIQ_SERVICE_OPENED:
|
|
+ /* No equivalent VCHI reason */
|
|
+ break;
|
|
+ case VCHIQ_BULK_TRANSMIT_ABORTED:
|
|
+ if (service->callback)
|
|
+ service->callback(service->callback_param, VCHI_CALLBACK_BULK_TRANSMIT_ABORTED, bulk_user);
|
|
+ break;
|
|
+ case VCHIQ_BULK_RECEIVE_ABORTED:
|
|
+ if (service->callback)
|
|
+ service->callback(service->callback_param, VCHI_CALLBACK_BULK_RECEIVE_ABORTED, bulk_user);
|
|
+ break;
|
|
+ default:
|
|
+ vcos_assert(0);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return VCHIQ_SUCCESS;
|
|
+}
|
|
+
|
|
+static SHIM_SERVICE_T *service_alloc(VCHIQ_INSTANCE_T instance,
|
|
+ SERVICE_CREATION_T *setup)
|
|
+{
|
|
+ SHIM_SERVICE_T *service = vcos_calloc(1, sizeof(SHIM_SERVICE_T), "vchiq_shim");
|
|
+
|
|
+ vcos_unused(instance);
|
|
+
|
|
+ if (service)
|
|
+ {
|
|
+ if (vchiu_queue_init(&service->queue, 64))
|
|
+ {
|
|
+ service->callback = setup->callback;
|
|
+ service->callback_param = setup->callback_param;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ vcos_free(service);
|
|
+ service = NULL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return service;
|
|
+}
|
|
+
|
|
+static void service_free(SHIM_SERVICE_T *service)
|
|
+{
|
|
+ if (service)
|
|
+ {
|
|
+ vchiu_queue_delete(&service->queue);
|
|
+ vcos_free((void*)service);
|
|
+ }
|
|
+}
|
|
+
|
|
+int32_t vchi_service_open( VCHI_INSTANCE_T instance_handle,
|
|
+ SERVICE_CREATION_T *setup,
|
|
+ VCHI_SERVICE_HANDLE_T *handle)
|
|
+{
|
|
+ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle;
|
|
+ SHIM_SERVICE_T *service = service_alloc(instance, setup);
|
|
+ if (service)
|
|
+ {
|
|
+ VCHIQ_STATUS_T status = vchiq_open_service(instance, setup->service_id, shim_callback, service, &service->handle);
|
|
+ if (status != VCHIQ_SUCCESS)
|
|
+ {
|
|
+ service_free(service);
|
|
+ service = NULL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ *handle = (VCHI_SERVICE_HANDLE_T)service;
|
|
+
|
|
+ return (service != NULL) ? 0 : -1;
|
|
+}
|
|
+
|
|
+int32_t vchi_service_create( VCHI_INSTANCE_T instance_handle,
|
|
+ SERVICE_CREATION_T *setup,
|
|
+ VCHI_SERVICE_HANDLE_T *handle )
|
|
+{
|
|
+ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle;
|
|
+ SHIM_SERVICE_T *service = service_alloc(instance, setup);
|
|
+ if (service)
|
|
+ {
|
|
+ VCHIQ_STATUS_T status = vchiq_add_service(instance, setup->service_id, shim_callback, service, &service->handle);
|
|
+ if (status != VCHIQ_SUCCESS)
|
|
+ {
|
|
+ service_free(service);
|
|
+ service = NULL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ *handle = (VCHI_SERVICE_HANDLE_T)service;
|
|
+
|
|
+ return (service != NULL) ? 0 : -1;
|
|
+}
|
|
+
|
|
+int32_t vchi_service_close( const VCHI_SERVICE_HANDLE_T handle )
|
|
+{
|
|
+ vcos_unused(handle);
|
|
+
|
|
+ // YTI??
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* ----------------------------------------------------------------------
|
|
+ * read a uint32_t from buffer.
|
|
+ * network format is defined to be little endian
|
|
+ * -------------------------------------------------------------------- */
|
|
+uint32_t
|
|
+vchi_readbuf_uint32( const void *_ptr )
|
|
+{
|
|
+ const unsigned char *ptr = _ptr;
|
|
+ return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
|
|
+}
|
|
+
|
|
+/* ----------------------------------------------------------------------
|
|
+ * write a uint32_t to buffer.
|
|
+ * network format is defined to be little endian
|
|
+ * -------------------------------------------------------------------- */
|
|
+void
|
|
+vchi_writebuf_uint32( void *_ptr, uint32_t value )
|
|
+{
|
|
+ unsigned char *ptr = _ptr;
|
|
+ ptr[0] = (unsigned char)((value >> 0) & 0xFF);
|
|
+ ptr[1] = (unsigned char)((value >> 8) & 0xFF);
|
|
+ ptr[2] = (unsigned char)((value >> 16) & 0xFF);
|
|
+ ptr[3] = (unsigned char)((value >> 24) & 0xFF);
|
|
+}
|
|
+
|
|
+/* ----------------------------------------------------------------------
|
|
+ * read a uint16_t from buffer.
|
|
+ * network format is defined to be little endian
|
|
+ * -------------------------------------------------------------------- */
|
|
+uint16_t
|
|
+vchi_readbuf_uint16( const void *_ptr )
|
|
+{
|
|
+ const unsigned char *ptr = _ptr;
|
|
+ return ptr[0] | (ptr[1] << 8);
|
|
+}
|
|
+
|
|
+/* ----------------------------------------------------------------------
|
|
+ * write a uint16_t into the buffer.
|
|
+ * network format is defined to be little endian
|
|
+ * -------------------------------------------------------------------- */
|
|
+void
|
|
+vchi_writebuf_uint16( void *_ptr, uint16_t value )
|
|
+{
|
|
+ unsigned char *ptr = _ptr;
|
|
+ ptr[0] = (value >> 0) & 0xFF;
|
|
+ ptr[1] = (value >> 8) & 0xFF;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_service_use
|
|
+ *
|
|
+ * Arguments: const VCHI_SERVICE_HANDLE_T handle
|
|
+ *
|
|
+ * Description: Routine to increment refcount on a service
|
|
+ *
|
|
+ * Returns: void
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t vchi_service_use( const VCHI_SERVICE_HANDLE_T handle )
|
|
+{
|
|
+ int32_t ret = -1;
|
|
+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle;
|
|
+ if(service)
|
|
+ {
|
|
+ ret = vchiq_status_to_vchi(vchiq_use_service(service->handle));
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ * Name: vchi_service_release
|
|
+ *
|
|
+ * Arguments: const VCHI_SERVICE_HANDLE_T handle
|
|
+ *
|
|
+ * Description: Routine to decrement refcount on a service
|
|
+ *
|
|
+ * Returns: void
|
|
+ *
|
|
+ ***********************************************************/
|
|
+int32_t vchi_service_release( const VCHI_SERVICE_HANDLE_T handle )
|
|
+{
|
|
+ int32_t ret = -1;
|
|
+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle;
|
|
+ if(service)
|
|
+ {
|
|
+ ret = vchiq_status_to_vchi(vchiq_release_service(service->handle));
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+#if defined(__KERNEL__)
|
|
+EXPORT_SYMBOL(vchi_initialise);
|
|
+EXPORT_SYMBOL(vchi_connect);
|
|
+EXPORT_SYMBOL(vchi_bulk_queue_transmit);
|
|
+EXPORT_SYMBOL(vchi_msg_dequeue);
|
|
+EXPORT_SYMBOL(vchi_msg_queue);
|
|
+EXPORT_SYMBOL(vchi_msg_queuev);
|
|
+EXPORT_SYMBOL(vchi_service_close);
|
|
+EXPORT_SYMBOL(vchi_service_open);
|
|
+EXPORT_SYMBOL(vchi_service_create);
|
|
+EXPORT_SYMBOL(vchi_service_use);
|
|
+EXPORT_SYMBOL(vchi_service_release);
|
|
+#endif
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c
|
|
@@ -0,0 +1,97 @@
|
|
+/*
|
|
+ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
+ */
|
|
+
|
|
+#include "vchiq_util.h"
|
|
+
|
|
+#if !defined(__KERNEL__)
|
|
+#include <stdlib.h>
|
|
+#endif
|
|
+
|
|
+static __inline int is_pow2(int i)
|
|
+{
|
|
+ return i && !(i & (i - 1));
|
|
+}
|
|
+
|
|
+int vchiu_queue_init(VCHIU_QUEUE_T *queue, int size)
|
|
+{
|
|
+ vcos_assert(is_pow2(size));
|
|
+
|
|
+ queue->size = size;
|
|
+ queue->read = 0;
|
|
+ queue->write = 0;
|
|
+
|
|
+ vcos_event_create(&queue->pop, "vchiu");
|
|
+ vcos_event_create(&queue->push, "vchiu");
|
|
+
|
|
+ queue->storage = vcos_malloc(size * sizeof(VCHIQ_HEADER_T *), VCOS_FUNCTION);
|
|
+ if (queue->storage == NULL)
|
|
+ {
|
|
+ vchiu_queue_delete(queue);
|
|
+ return 0;
|
|
+ }
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+void vchiu_queue_delete(VCHIU_QUEUE_T *queue)
|
|
+{
|
|
+ vcos_event_delete(&queue->pop);
|
|
+ vcos_event_delete(&queue->push);
|
|
+ if (queue->storage != NULL)
|
|
+ vcos_free(queue->storage);
|
|
+}
|
|
+
|
|
+int vchiu_queue_is_empty(VCHIU_QUEUE_T *queue)
|
|
+{
|
|
+ return queue->read == queue->write;
|
|
+}
|
|
+
|
|
+void vchiu_queue_push(VCHIU_QUEUE_T *queue, VCHIQ_HEADER_T *header)
|
|
+{
|
|
+ while (queue->write == queue->read + queue->size)
|
|
+ vcos_event_wait(&queue->pop);
|
|
+
|
|
+ queue->storage[queue->write & (queue->size - 1)] = header;
|
|
+
|
|
+ queue->write++;
|
|
+
|
|
+ vcos_event_signal(&queue->push);
|
|
+}
|
|
+
|
|
+VCHIQ_HEADER_T *vchiu_queue_peek(VCHIU_QUEUE_T *queue)
|
|
+{
|
|
+ while (queue->write == queue->read)
|
|
+ vcos_event_wait(&queue->push);
|
|
+
|
|
+ return queue->storage[queue->read & (queue->size - 1)];
|
|
+}
|
|
+
|
|
+VCHIQ_HEADER_T *vchiu_queue_pop(VCHIU_QUEUE_T *queue)
|
|
+{
|
|
+ VCHIQ_HEADER_T *header;
|
|
+
|
|
+ while (queue->write == queue->read)
|
|
+ vcos_event_wait(&queue->push);
|
|
+
|
|
+ header = queue->storage[queue->read & (queue->size - 1)];
|
|
+
|
|
+ queue->read++;
|
|
+
|
|
+ vcos_event_signal(&queue->pop);
|
|
+
|
|
+ return header;
|
|
+}
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h
|
|
@@ -0,0 +1,47 @@
|
|
+/*
|
|
+ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
+ */
|
|
+
|
|
+#ifndef VCHIQ_UTIL_H
|
|
+#define VCHIQ_UTIL_H
|
|
+
|
|
+#include "vchiq_if.h"
|
|
+#include "interface/vcos/vcos.h"
|
|
+
|
|
+typedef struct {
|
|
+ int size;
|
|
+ int read;
|
|
+ int write;
|
|
+
|
|
+ VCOS_EVENT_T pop;
|
|
+ VCOS_EVENT_T push;
|
|
+
|
|
+ VCHIQ_HEADER_T **storage;
|
|
+} VCHIU_QUEUE_T;
|
|
+
|
|
+extern int vchiu_queue_init(VCHIU_QUEUE_T *queue, int size);
|
|
+extern void vchiu_queue_delete(VCHIU_QUEUE_T *queue);
|
|
+
|
|
+extern int vchiu_queue_is_empty(VCHIU_QUEUE_T *queue);
|
|
+
|
|
+extern void vchiu_queue_push(VCHIU_QUEUE_T *queue, VCHIQ_HEADER_T *header);
|
|
+
|
|
+extern VCHIQ_HEADER_T *vchiu_queue_peek(VCHIU_QUEUE_T *queue);
|
|
+extern VCHIQ_HEADER_T *vchiu_queue_pop(VCHIU_QUEUE_T *queue);
|
|
+
|
|
+#endif
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_cmd.c
|
|
@@ -0,0 +1,681 @@
|
|
+/*****************************************************************************
|
|
+* Copyright 2009 - 2011 Broadcom Corporation. All rights reserved.
|
|
+*
|
|
+* Unless you and Broadcom execute a separate written software license
|
|
+* agreement governing use of this software, this software is licensed to you
|
|
+* under the terms of the GNU General Public License version 2, available at
|
|
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
|
+*
|
|
+* Notwithstanding the above, under no circumstances may you combine this
|
|
+* software in any way with any other Broadcom software provided under a
|
|
+* license other than the GPL, without Broadcom's express prior written
|
|
+* consent.
|
|
+*****************************************************************************/
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* This file provides a generic command line interface which allows
|
|
+* vcos internals to be manipulated and/or displayed.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+/* ---- Include Files ---------------------------------------------------- */
|
|
+
|
|
+#include "interface/vcos/vcos.h"
|
|
+
|
|
+#ifdef HAVE_VCOS_VERSION
|
|
+#include "interface/vcos/vcos_build_info.h"
|
|
+#endif
|
|
+
|
|
+ #ifdef _VIDEOCORE
|
|
+#include vcfw/logging/logging.h
|
|
+#endif
|
|
+
|
|
+/* ---- Public Variables ------------------------------------------------- */
|
|
+
|
|
+/* ---- Private Constants and Types -------------------------------------- */
|
|
+
|
|
+#define VCOS_LOG_CATEGORY (&vcos_cmd_log_category)
|
|
+VCOS_LOG_CAT_T vcos_cmd_log_category;
|
|
+
|
|
+/* ---- Private Variables ------------------------------------------------ */
|
|
+
|
|
+static struct VCOS_CMD_GLOBALS_T
|
|
+{
|
|
+ VCOS_MUTEX_T lock;
|
|
+ VCOS_ONCE_T initialized;
|
|
+
|
|
+ unsigned num_cmd_entries;
|
|
+ unsigned num_cmd_alloc;
|
|
+ VCOS_CMD_T *cmd_entry;
|
|
+
|
|
+ VCOS_LOG_CAT_T *log_category;
|
|
+} cmd_globals;
|
|
+
|
|
+/* ---- Private Function Prototypes -------------------------------------- */
|
|
+
|
|
+static VCOS_STATUS_T help_cmd( VCOS_CMD_PARAM_T *param );
|
|
+
|
|
+/* ---- Functions ------------------------------------------------------- */
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Walks through the commands looking for a particular command
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+static VCOS_CMD_T *find_cmd( VCOS_CMD_T *cmd_entry, const char *name )
|
|
+{
|
|
+ VCOS_CMD_T *scan_entry = cmd_entry;
|
|
+
|
|
+ while ( scan_entry->name != NULL )
|
|
+ {
|
|
+ if ( vcos_strcmp( scan_entry->name, name ) == 0 )
|
|
+ {
|
|
+ return scan_entry;
|
|
+ }
|
|
+ scan_entry++;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Saves away
|
|
+* each line individually.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+void vcos_cmd_always_log_output( VCOS_LOG_CAT_T *log_category )
|
|
+{
|
|
+ cmd_globals.log_category = log_category;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Walks through a buffer containing newline separated lines, and logs
|
|
+* each line individually.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+static void cmd_log_results( VCOS_CMD_PARAM_T *param )
|
|
+{
|
|
+ char *start;
|
|
+ char *end;
|
|
+
|
|
+ start = end = param->result_buf;
|
|
+
|
|
+ while ( *start != '\0' )
|
|
+ {
|
|
+ while (( *end != '\0' ) && ( *end != '\n' ))
|
|
+ end++;
|
|
+
|
|
+ if ( *end == '\n' )
|
|
+ {
|
|
+ *end++ = '\0';
|
|
+ }
|
|
+
|
|
+ if ( cmd_globals.log_category != NULL )
|
|
+ {
|
|
+ if ( vcos_is_log_enabled( cmd_globals.log_category, VCOS_LOG_INFO ))
|
|
+ {
|
|
+ vcos_log_impl( cmd_globals.log_category, VCOS_LOG_INFO, "%s", start );
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ vcos_log_info( "%s", start );
|
|
+ }
|
|
+
|
|
+ start = end;
|
|
+ }
|
|
+
|
|
+ /* Since we logged the buffer, reset the pointer back to the beginning. */
|
|
+
|
|
+ param->result_ptr = param->result_buf;
|
|
+ param->result_buf[0] = '\0';
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Since we may have limited output space, we create a generic routine
|
|
+* which tries to use the result space, but will switch over to using
|
|
+* logging if the output is too large.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+void vcos_cmd_vprintf( VCOS_CMD_PARAM_T *param, const char *fmt, va_list args )
|
|
+{
|
|
+ int bytes_written;
|
|
+ int bytes_remaining;
|
|
+
|
|
+ bytes_remaining = (int)(param->result_size - ( param->result_ptr - param->result_buf ));
|
|
+
|
|
+ bytes_written = vcos_vsnprintf( param->result_ptr, bytes_remaining, fmt, args );
|
|
+
|
|
+ if ( cmd_globals.log_category != NULL )
|
|
+ {
|
|
+ /* We're going to log each line as we encounter it. If the buffer
|
|
+ * doesn't end in a newline, then we'll wait for one first.
|
|
+ */
|
|
+
|
|
+ if ( (( bytes_written + 1 ) >= bytes_remaining )
|
|
+ || ( param->result_ptr[ bytes_written - 1 ] == '\n' ))
|
|
+ {
|
|
+ cmd_log_results( param );
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ param->result_ptr += bytes_written;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if (( bytes_written + 1 ) >= bytes_remaining )
|
|
+ {
|
|
+ /* Output doesn't fit - switch over to logging */
|
|
+
|
|
+ param->use_log = 1;
|
|
+
|
|
+ *param->result_ptr = '\0'; /* Zap the partial line that didn't fit above. */
|
|
+
|
|
+ cmd_log_results( param ); /* resets result_ptr */
|
|
+
|
|
+ bytes_written = vcos_vsnprintf( param->result_ptr, bytes_remaining, fmt, args );
|
|
+ }
|
|
+ param->result_ptr += bytes_written;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Prints the output.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+void vcos_cmd_printf( VCOS_CMD_PARAM_T *param, const char *fmt, ... )
|
|
+{
|
|
+ va_list args;
|
|
+
|
|
+ va_start( args, fmt );
|
|
+ vcos_cmd_vprintf( param, fmt, args );
|
|
+ va_end( args );
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Prints the arguments which were on the command line prior to ours.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+static void print_argument_prefix( VCOS_CMD_PARAM_T *param )
|
|
+{
|
|
+ int arg_idx;
|
|
+
|
|
+ for ( arg_idx = 0; ¶m->argv_orig[arg_idx] != param->argv; arg_idx++ )
|
|
+ {
|
|
+ vcos_cmd_printf( param, "%s ", param->argv_orig[arg_idx] );
|
|
+ }
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Prints an error message, prefixed by the command chain required to get
|
|
+* to where we're at.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+void vcos_cmd_error( VCOS_CMD_PARAM_T *param, const char *fmt, ... )
|
|
+{
|
|
+ va_list args;
|
|
+
|
|
+ print_argument_prefix( param );
|
|
+
|
|
+ va_start( args, fmt );
|
|
+ vcos_cmd_vprintf( param, fmt, args );
|
|
+ va_end( args );
|
|
+ vcos_cmd_printf( param, "\n" );
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* usage - prints command usage for an array of commands.
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+static void usage( VCOS_CMD_PARAM_T *param, VCOS_CMD_T *cmd_entry )
|
|
+{
|
|
+ int cmd_idx;
|
|
+ int nameWidth = 0;
|
|
+ int argsWidth = 0;
|
|
+ VCOS_CMD_T *scan_entry;
|
|
+
|
|
+ vcos_cmd_printf( param, "Usage: " );
|
|
+ print_argument_prefix( param );
|
|
+ vcos_cmd_printf( param, "command [args ...]\n" );
|
|
+ vcos_cmd_printf( param, "\n" );
|
|
+ vcos_cmd_printf( param, "Where command is one of the following:\n" );
|
|
+
|
|
+ for ( cmd_idx = 0; cmd_entry[cmd_idx].name != NULL; cmd_idx++ )
|
|
+ {
|
|
+ int aw;
|
|
+ int nw;
|
|
+
|
|
+ scan_entry = &cmd_entry[cmd_idx];
|
|
+
|
|
+ nw = vcos_strlen( scan_entry->name );
|
|
+ aw = vcos_strlen( scan_entry->args );
|
|
+
|
|
+ if ( nw > nameWidth )
|
|
+ {
|
|
+ nameWidth = nw;
|
|
+ }
|
|
+ if ( aw > argsWidth )
|
|
+ {
|
|
+ argsWidth = aw;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for ( cmd_idx = 0; cmd_entry[cmd_idx].name != NULL; cmd_idx++ )
|
|
+ {
|
|
+ scan_entry = &cmd_entry[cmd_idx];
|
|
+
|
|
+ vcos_cmd_printf( param, " %-*s %-*s - %s\n",
|
|
+ nameWidth, scan_entry->name,
|
|
+ argsWidth, scan_entry->args,
|
|
+ scan_entry->descr );
|
|
+ }
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* Prints the usage for the current command.
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+void vcos_cmd_usage( VCOS_CMD_PARAM_T *param )
|
|
+{
|
|
+ VCOS_CMD_T *cmd_entry;
|
|
+
|
|
+ cmd_entry = param->cmd_entry;
|
|
+
|
|
+ if ( cmd_entry->sub_cmd_entry != NULL )
|
|
+ {
|
|
+ /* This command is command with sub-commands */
|
|
+
|
|
+ usage( param, param->cmd_entry->sub_cmd_entry );
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ vcos_cmd_printf( param, "Usage: " );
|
|
+ print_argument_prefix( param );
|
|
+ vcos_cmd_printf( param, "%s - %s\n",
|
|
+ param->cmd_entry->args,
|
|
+ param->cmd_entry->descr );
|
|
+ }
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Command to print out the help
|
|
+*
|
|
+* This help command is only called from the main menu.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+static VCOS_STATUS_T help_cmd( VCOS_CMD_PARAM_T *param )
|
|
+{
|
|
+ VCOS_CMD_T *found_entry;
|
|
+
|
|
+#if 0
|
|
+ {
|
|
+ int arg_idx;
|
|
+
|
|
+ vcos_log_trace( "%s: argc = %d", __func__, param->argc );
|
|
+ for ( arg_idx = 0; arg_idx < param->argc; arg_idx++ )
|
|
+ {
|
|
+ vcos_log_trace( "%s: argv[%d] = '%s'", __func__, arg_idx, param->argv[arg_idx] );
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ /* If there is an argument after the word help, then we want to print
|
|
+ * help for that command.
|
|
+ */
|
|
+
|
|
+ if ( param->argc == 1 )
|
|
+ {
|
|
+ if ( param->cmd_parent_entry == cmd_globals.cmd_entry )
|
|
+ {
|
|
+ /* Bare help - print the command usage for the root */
|
|
+
|
|
+ usage( param, cmd_globals.cmd_entry );
|
|
+ return VCOS_SUCCESS;
|
|
+ }
|
|
+
|
|
+ /* For all other cases help requires an argument */
|
|
+
|
|
+ vcos_cmd_error( param, "%s requires an argument", param->argv[0] );
|
|
+ return VCOS_EINVAL;
|
|
+ }
|
|
+
|
|
+ /* We were given an argument. */
|
|
+
|
|
+ if (( found_entry = find_cmd( param->cmd_parent_entry, param->argv[1] )) != NULL )
|
|
+ {
|
|
+ /* Make it look like the command that was specified is the one that's
|
|
+ * currently running
|
|
+ */
|
|
+
|
|
+ param->cmd_entry = found_entry;
|
|
+ param->argv[0] = param->argv[1];
|
|
+ param->argv++;
|
|
+ param->argc--;
|
|
+
|
|
+ vcos_cmd_usage( param );
|
|
+ return VCOS_SUCCESS;
|
|
+ }
|
|
+
|
|
+ vcos_cmd_error( param, "- unrecognized command: '%s'", param->argv[1] );
|
|
+ return VCOS_ENOENT;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Command to print out the version/build information.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+#ifdef HAVE_VCOS_VERSION
|
|
+
|
|
+static VCOS_STATUS_T version_cmd( VCOS_CMD_PARAM_T *param )
|
|
+{
|
|
+ static const char* copyright = "Copyright (c) 2011 Broadcom";
|
|
+
|
|
+ vcos_cmd_printf( param, "%s %s\n%s\nversion %s\n",
|
|
+ vcos_get_build_date(),
|
|
+ vcos_get_build_time(),
|
|
+ copyright,
|
|
+ vcos_get_build_version() );
|
|
+
|
|
+ return VCOS_SUCCESS;
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Internal commands
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+static VCOS_CMD_T cmd_help = { "help", "[command]", help_cmd, NULL, "Prints command help information" };
|
|
+
|
|
+#ifdef HAVE_VCOS_VERSION
|
|
+static VCOS_CMD_T cmd_version = { "version", "", version_cmd, NULL, "Prints build/version information" };
|
|
+#endif
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Walks the command table and executes the commands
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+static VCOS_STATUS_T execute_cmd( VCOS_CMD_PARAM_T *param, VCOS_CMD_T *cmd_entry )
|
|
+{
|
|
+ const char *cmdStr;
|
|
+ VCOS_CMD_T *found_entry;
|
|
+
|
|
+#if 0
|
|
+ {
|
|
+ int arg_idx;
|
|
+
|
|
+ vcos_cmd_printf( param, "%s: argc = %d", __func__, param->argc );
|
|
+ for ( arg_idx = 0; arg_idx < param->argc; arg_idx++ )
|
|
+ {
|
|
+ vcos_cmd_printf( param, " argv[%d] = '%s'", arg_idx, param->argv[arg_idx] );
|
|
+ }
|
|
+ vcos_cmd_printf( param, "\n" );
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ if ( param->argc <= 1 )
|
|
+ {
|
|
+ /* No command specified */
|
|
+
|
|
+ vcos_cmd_error( param, "%s - no command specified", param->argv[0] );
|
|
+ return VCOS_EINVAL;
|
|
+ }
|
|
+
|
|
+ /* argv[0] is the command/program that caused us to get invoked, so we strip
|
|
+ * it off.
|
|
+ */
|
|
+
|
|
+ param->argc--;
|
|
+ param->argv++;
|
|
+ param->cmd_parent_entry = cmd_entry;
|
|
+
|
|
+ /* Not the help command, scan for the command and execute it. */
|
|
+
|
|
+ cmdStr = param->argv[0];
|
|
+
|
|
+ if (( found_entry = find_cmd( cmd_entry, cmdStr )) != NULL )
|
|
+ {
|
|
+ if ( found_entry->sub_cmd_entry != NULL )
|
|
+ {
|
|
+ return execute_cmd( param, found_entry->sub_cmd_entry );
|
|
+ }
|
|
+
|
|
+ param->cmd_entry = found_entry;
|
|
+ return found_entry->cmd_fn( param );
|
|
+ }
|
|
+
|
|
+ /* Unrecognized command - check to see if it was the help command */
|
|
+
|
|
+ if ( vcos_strcmp( cmdStr, cmd_help.name ) == 0 )
|
|
+ {
|
|
+ return help_cmd( param );
|
|
+ }
|
|
+
|
|
+ vcos_cmd_error( param, "- unrecognized command: '%s'", cmdStr );
|
|
+ return VCOS_ENOENT;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Initializes the command line parser.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+static void vcos_cmd_init( void )
|
|
+{
|
|
+ vcos_mutex_create( &cmd_globals.lock, "vcos_cmd" );
|
|
+
|
|
+ cmd_globals.num_cmd_entries = 0;
|
|
+ cmd_globals.num_cmd_alloc = 0;
|
|
+ cmd_globals.cmd_entry = NULL;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Command line processor.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+VCOS_STATUS_T vcos_cmd_execute( int argc, char **argv, size_t result_size, char *result_buf )
|
|
+{
|
|
+ VCOS_STATUS_T rc = VCOS_EINVAL;
|
|
+ VCOS_CMD_PARAM_T param;
|
|
+
|
|
+ vcos_once( &cmd_globals.initialized, vcos_cmd_init );
|
|
+
|
|
+ param.argc = argc;
|
|
+ param.argv = param.argv_orig = argv;
|
|
+
|
|
+ param.use_log = 0;
|
|
+ param.result_size = result_size;
|
|
+ param.result_ptr = result_buf;
|
|
+ param.result_buf = result_buf;
|
|
+
|
|
+ result_buf[0] = '\0';
|
|
+
|
|
+ vcos_mutex_lock( &cmd_globals.lock );
|
|
+
|
|
+ rc = execute_cmd( ¶m, cmd_globals.cmd_entry );
|
|
+
|
|
+ if ( param.use_log )
|
|
+ {
|
|
+ cmd_log_results( ¶m );
|
|
+ vcos_snprintf( result_buf, result_size, "results logged" );
|
|
+ }
|
|
+ else
|
|
+ if ( cmd_globals.log_category != NULL )
|
|
+ {
|
|
+ if ( result_buf[0] != '\0' )
|
|
+ {
|
|
+ /* There is a partial line still buffered. */
|
|
+
|
|
+ vcos_cmd_printf( ¶m, "\n" );
|
|
+ }
|
|
+ }
|
|
+
|
|
+ vcos_mutex_unlock( &cmd_globals.lock );
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Registers a command entry with the command line processor
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+VCOS_STATUS_T vcos_cmd_register( VCOS_CMD_T *cmd_entry )
|
|
+{
|
|
+ VCOS_STATUS_T rc;
|
|
+ VCOS_UNSIGNED new_num_cmd_alloc;
|
|
+ VCOS_CMD_T *new_cmd_entry;
|
|
+ VCOS_CMD_T *old_cmd_entry;
|
|
+ VCOS_CMD_T *scan_entry;
|
|
+
|
|
+ vcos_once( &cmd_globals.initialized, vcos_cmd_init );
|
|
+
|
|
+ vcos_assert( cmd_entry != NULL );
|
|
+ vcos_assert( cmd_entry->name != NULL );
|
|
+
|
|
+ vcos_log_trace( "%s: cmd '%s'", __FUNCTION__, cmd_entry->name );
|
|
+
|
|
+ vcos_assert( cmd_entry->args != NULL );
|
|
+ vcos_assert(( cmd_entry->cmd_fn != NULL ) || ( cmd_entry->sub_cmd_entry != NULL ));
|
|
+ vcos_assert( cmd_entry->descr != NULL );
|
|
+
|
|
+ /* We expect vcos_cmd_init to be called before vcos_logging_init, so we
|
|
+ * need to defer registering our logging category until someplace
|
|
+ * like right here.
|
|
+ */
|
|
+
|
|
+ if ( vcos_cmd_log_category.name == NULL )
|
|
+ {
|
|
+ /*
|
|
+ * If you're using the command interface, you pretty much always want
|
|
+ * log messages from this file to show up. So we change the default
|
|
+ * from ERROR to be the more reasonable INFO level.
|
|
+ */
|
|
+
|
|
+ vcos_log_set_level(&vcos_cmd_log_category, VCOS_LOG_INFO);
|
|
+ vcos_log_register("vcos_cmd", &vcos_cmd_log_category);
|
|
+
|
|
+ /* We register a help command so that it shows up in the usage. */
|
|
+
|
|
+ vcos_cmd_register( &cmd_help );
|
|
+#ifdef HAVE_VCOS_VERSION
|
|
+ vcos_cmd_register( &cmd_version );
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ vcos_mutex_lock( &cmd_globals.lock );
|
|
+
|
|
+ if ( cmd_globals.num_cmd_entries >= cmd_globals.num_cmd_alloc )
|
|
+ {
|
|
+ if ( cmd_globals.num_cmd_alloc == 0 )
|
|
+ {
|
|
+ /* We haven't allocated a table yet */
|
|
+ }
|
|
+
|
|
+ /* The number 8 is rather arbitrary. */
|
|
+
|
|
+ new_num_cmd_alloc = cmd_globals.num_cmd_alloc + 8;
|
|
+
|
|
+ /* The + 1 is to ensure that we always have a NULL entry at the end. */
|
|
+
|
|
+ new_cmd_entry = (VCOS_CMD_T *)vcos_calloc( new_num_cmd_alloc + 1, sizeof( *cmd_entry ), "vcos_cmd_entries" );
|
|
+ if ( new_cmd_entry == NULL )
|
|
+ {
|
|
+ rc = VCOS_ENOMEM;
|
|
+ goto out;
|
|
+ }
|
|
+ memcpy( new_cmd_entry, cmd_globals.cmd_entry, cmd_globals.num_cmd_entries * sizeof( *cmd_entry ));
|
|
+ cmd_globals.num_cmd_alloc = new_num_cmd_alloc;
|
|
+ old_cmd_entry = cmd_globals.cmd_entry;
|
|
+ cmd_globals.cmd_entry = new_cmd_entry;
|
|
+ vcos_free( old_cmd_entry );
|
|
+ }
|
|
+
|
|
+ if ( cmd_globals.num_cmd_entries == 0 )
|
|
+ {
|
|
+ /* This is the first command being registered */
|
|
+
|
|
+ cmd_globals.cmd_entry[0] = *cmd_entry;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* Keep the list in alphabetical order. We start at the end and work backwards
|
|
+ * shuffling entries up one until we find an insertion point.
|
|
+ */
|
|
+
|
|
+ for ( scan_entry = &cmd_globals.cmd_entry[cmd_globals.num_cmd_entries - 1];
|
|
+ scan_entry >= cmd_globals.cmd_entry; scan_entry-- )
|
|
+ {
|
|
+ if ( vcos_strcmp( cmd_entry->name, scan_entry->name ) > 0 )
|
|
+ {
|
|
+ /* We found an insertion point. */
|
|
+
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ scan_entry[1] = scan_entry[0];
|
|
+ }
|
|
+ scan_entry[1] = *cmd_entry;
|
|
+ }
|
|
+ cmd_globals.num_cmd_entries++;
|
|
+
|
|
+ rc = VCOS_SUCCESS;
|
|
+
|
|
+out:
|
|
+
|
|
+ vcos_mutex_unlock( &cmd_globals.lock );
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Registers multiple commands.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+VCOS_STATUS_T vcos_cmd_register_multiple( VCOS_CMD_T *cmd_entry )
|
|
+{
|
|
+ VCOS_STATUS_T status;
|
|
+
|
|
+ while ( cmd_entry->name != NULL )
|
|
+ {
|
|
+ if (( status = vcos_cmd_register( cmd_entry )) != VCOS_SUCCESS )
|
|
+ {
|
|
+ return status;
|
|
+ }
|
|
+ cmd_entry++;
|
|
+ }
|
|
+ return VCOS_SUCCESS;
|
|
+}
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_common.h
|
|
@@ -0,0 +1,76 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - common postamble code
|
|
+=============================================================================*/
|
|
+
|
|
+/** \file
|
|
+ *
|
|
+ * Postamble code included by the platform-specific header files
|
|
+ */
|
|
+
|
|
+#define VCOS_THREAD_PRI_DEFAULT VCOS_THREAD_PRI_NORMAL
|
|
+
|
|
+#if !defined(VCOS_THREAD_PRI_INCREASE)
|
|
+#error Which way to thread priorities go?
|
|
+#endif
|
|
+
|
|
+#if VCOS_THREAD_PRI_INCREASE < 0
|
|
+/* smaller numbers are higher priority */
|
|
+#define VCOS_THREAD_PRI_LESS(x) ((x)<VCOS_THREAD_PRI_MAX?(x)+1:VCOS_THREAD_PRI_MAX)
|
|
+#define VCOS_THREAD_PRI_MORE(x) ((x)>VCOS_THREAD_PRI_MIN?(x)-1:VCOS_THREAD_PRI_MIN)
|
|
+#else
|
|
+/* bigger numbers are lower priority */
|
|
+#define VCOS_THREAD_PRI_MORE(x) ((x)<VCOS_THREAD_PRI_MAX?(x)+1:VCOS_THREAD_PRI_MAX)
|
|
+#define VCOS_THREAD_PRI_LESS(x) ((x)>VCOS_THREAD_PRI_MIN?(x)-1:VCOS_THREAD_PRI_MIN)
|
|
+#endif
|
|
+
|
|
+/* Convenience for Brits: */
|
|
+#define VCOS_APPLICATION_INITIALISE VCOS_APPLICATION_INITIALIZE
|
|
+
|
|
+/*
|
|
+ * Check for constant definitions
|
|
+ */
|
|
+#ifndef VCOS_TICKS_PER_SECOND
|
|
+#error VCOS_TICKS_PER_SECOND not defined
|
|
+#endif
|
|
+
|
|
+#if !defined(VCOS_THREAD_PRI_MIN) || !defined(VCOS_THREAD_PRI_MAX)
|
|
+#error Priority range not defined
|
|
+#endif
|
|
+
|
|
+#if !defined(VCOS_THREAD_PRI_HIGHEST) || !defined(VCOS_THREAD_PRI_LOWEST) || !defined(VCOS_THREAD_PRI_NORMAL)
|
|
+#error Priority ordering not defined
|
|
+#endif
|
|
+
|
|
+#if !defined(VCOS_CAN_SET_STACK_ADDR)
|
|
+#error Can stack addresses be set on this platform? Please set this macro to either 0 or 1.
|
|
+#endif
|
|
+
|
|
+#if (_VCOS_AFFINITY_CPU0|_VCOS_AFFINITY_CPU1) & (~_VCOS_AFFINITY_MASK)
|
|
+#error _VCOS_AFFINITY_CPUxxx values are not consistent with _VCOS_AFFINITY_MASK
|
|
+#endif
|
|
+
|
|
+/** Append to the end of a singly-linked queue, O(1). Works with
|
|
+ * any structure where list has members 'head' and 'tail' and
|
|
+ * item has a 'next' pointer.
|
|
+ */
|
|
+#define VCOS_QUEUE_APPEND_TAIL(list, item) {\
|
|
+ (item)->next = NULL;\
|
|
+ if (!(list)->head) {\
|
|
+ (list)->head = (list)->tail = (item); \
|
|
+ } else {\
|
|
+ (list)->tail->next = (item); \
|
|
+ (list)->tail = (item); \
|
|
+ } \
|
|
+}
|
|
+
|
|
+#ifndef VCOS_HAVE_TIMER
|
|
+VCOSPRE_ void VCOSPOST_ vcos_timer_init(void);
|
|
+#endif
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_blockpool.h
|
|
@@ -0,0 +1,260 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2011 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - event flags implemented via a semaphore
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_GENERIC_BLOCKPOOL_H
|
|
+#define VCOS_GENERIC_BLOCKPOOL_H
|
|
+
|
|
+/**
|
|
+ * \file
|
|
+ *
|
|
+ * This provides a generic, thread safe implementation of a VCOS block pool
|
|
+ * fixed size memory allocator.
|
|
+ */
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+
|
|
+/** Bits 0 to (VCOS_BLOCKPOOL_SUBPOOL_BITS - 1) are used to store the
|
|
+ * subpool id. */
|
|
+#define VCOS_BLOCKPOOL_SUBPOOL_BITS 3
|
|
+#define VCOS_BLOCKPOOL_MAX_SUBPOOLS (1 << VCOS_BLOCKPOOL_SUBPOOL_BITS)
|
|
+
|
|
+/* Make zero an invalid handle at the cost of decreasing the maximum
|
|
+ * number of blocks (2^28) by 1. Alternatively, a spare bit could be
|
|
+ * used to indicated valid blocks but there are likely to be better
|
|
+ * uses for spare bits. e.g. allowing more subpools
|
|
+ */
|
|
+#define INDEX_OFFSET 1
|
|
+
|
|
+#define VCOS_BLOCKPOOL_HANDLE_GET_INDEX(h) \
|
|
+ (((h) >> VCOS_BLOCKPOOL_SUBPOOL_BITS) - INDEX_OFFSET)
|
|
+
|
|
+#define VCOS_BLOCKPOOL_HANDLE_GET_SUBPOOL(h) \
|
|
+ ((h) & ((1 << VCOS_BLOCKPOOL_SUBPOOL_BITS) - 1))
|
|
+
|
|
+#define VCOS_BLOCKPOOL_HANDLE_CREATE(i,s) \
|
|
+ ((((i) + INDEX_OFFSET) << VCOS_BLOCKPOOL_SUBPOOL_BITS) | (s))
|
|
+
|
|
+#define VCOS_BLOCKPOOL_INVALID_HANDLE 0
|
|
+
|
|
+typedef struct VCOS_BLOCKPOOL_HEADER_TAG
|
|
+{
|
|
+ /* Blocks either refer to to the pool if they are allocated
|
|
+ * or the free list if they are available.
|
|
+ */
|
|
+ union {
|
|
+ struct VCOS_BLOCKPOOL_HEADER_TAG *next;
|
|
+ struct VCOS_BLOCKPOOL_SUBPOOL_TAG* subpool;
|
|
+ } owner;
|
|
+} VCOS_BLOCKPOOL_HEADER_T;
|
|
+
|
|
+typedef struct VCOS_BLOCKPOOL_SUBPOOL_TAG
|
|
+{
|
|
+ /** VCOS_BLOCKPOOL_SUBPOOL_MAGIC */
|
|
+ uint32_t magic;
|
|
+ VCOS_BLOCKPOOL_HEADER_T* free_list;
|
|
+ /* The start of the pool memory */
|
|
+ void *mem;
|
|
+ /* Address of the first block header */
|
|
+ void *start;
|
|
+ /** The number of blocks in this sub-pool */
|
|
+ VCOS_UNSIGNED num_blocks;
|
|
+ /** Current number of available blocks in this sub-pool */
|
|
+ VCOS_UNSIGNED available_blocks;
|
|
+ /** Pointers to the pool that owns this sub-pool */
|
|
+ struct VCOS_BLOCKPOOL_TAG* owner;
|
|
+ /** Define properties such as memory ownership */
|
|
+ uint32_t flags;
|
|
+} VCOS_BLOCKPOOL_SUBPOOL_T;
|
|
+
|
|
+typedef struct VCOS_BLOCKPOOL_TAG
|
|
+{
|
|
+ /** VCOS_BLOCKPOOL_MAGIC */
|
|
+ uint32_t magic;
|
|
+ /** Thread safety for Alloc, Free, Delete, Stats */
|
|
+ VCOS_MUTEX_T mutex;
|
|
+ /** The size of the block data */
|
|
+ size_t block_data_size;
|
|
+ /** Block size inc overheads */
|
|
+ size_t block_size;
|
|
+ /** Name for debugging */
|
|
+ const char *name;
|
|
+ /* The number of subpools that may be used */
|
|
+ VCOS_UNSIGNED num_subpools;
|
|
+ /** Number of blocks in each dynamically allocated subpool */
|
|
+ VCOS_UNSIGNED num_extension_blocks;
|
|
+ /** Array of subpools. Subpool zero is is not deleted until the pool is
|
|
+ * destroed. If the index of the pool is < num_subpools and
|
|
+ * subpool[index.mem] is null then the subpool entry is valid but
|
|
+ * "not currently allocated" */
|
|
+ VCOS_BLOCKPOOL_SUBPOOL_T subpools[VCOS_BLOCKPOOL_MAX_SUBPOOLS];
|
|
+} VCOS_BLOCKPOOL_T;
|
|
+
|
|
+#define VCOS_BLOCKPOOL_ROUND_UP(x,s) (((x) + ((s) - 1)) & ~((s) - 1))
|
|
+/**
|
|
+ * Calculates the size in bytes required for a block pool containing
|
|
+ * num_blocks of size block_size plus any overheads.
|
|
+ *
|
|
+ * The block pool header (VCOS_BLOCKPOOL_T) is allocated separately
|
|
+ *
|
|
+ * Overheads:
|
|
+ * block_size + header must be a multiple of sizeof(void*)
|
|
+ * The start of the first block may need to be up to wordsize - 1 bytes
|
|
+ * into the given buffer because statically allocated buffers within structures
|
|
+ * are not guaranteed to be word aligned.
|
|
+ */
|
|
+#define VCOS_BLOCKPOOL_SIZE(num_blocks, block_size) \
|
|
+ ((VCOS_BLOCKPOOL_ROUND_UP((block_size) + sizeof(VCOS_BLOCKPOOL_HEADER_T), \
|
|
+ sizeof(void*)) * (num_blocks)) + sizeof(void*))
|
|
+
|
|
+/**
|
|
+ * Sanity check to verify whether a handle is potentially a blockpool handle
|
|
+ * when the pool pointer is not available.
|
|
+ *
|
|
+ * If the pool pointer is availabe use vcos_blockpool_elem_to_handle instead.
|
|
+ *
|
|
+ * @param handle the handle to verify
|
|
+ * @param max_blocks the expected maximum number of block in the pool
|
|
+ * that the handle belongs to.
|
|
+ */
|
|
+#define VCOS_BLOCKPOOL_IS_VALID_HANDLE_FORMAT(handle, max_blocks) \
|
|
+ ((handle) != VCOS_BLOCKPOOL_INVALID_HANDLE \
|
|
+ && VCOS_BLOCKPOOL_HANDLE_GET_INDEX((handle)) < (max_blocks))
|
|
+
|
|
+VCOSPRE_
|
|
+ VCOS_STATUS_T VCOSPOST_ vcos_generic_blockpool_init(VCOS_BLOCKPOOL_T *pool,
|
|
+ VCOS_UNSIGNED num_blocks, VCOS_UNSIGNED block_size,
|
|
+ void *start, VCOS_UNSIGNED pool_size, const char *name);
|
|
+
|
|
+VCOSPRE_
|
|
+ VCOS_STATUS_T VCOSPOST_ vcos_generic_blockpool_create_on_heap(
|
|
+ VCOS_BLOCKPOOL_T *pool, VCOS_UNSIGNED num_blocks,
|
|
+ VCOS_UNSIGNED block_size, const char *name);
|
|
+
|
|
+VCOSPRE_
|
|
+ VCOS_STATUS_T VCOSPOST_ vcos_generic_blockpool_extend(VCOS_BLOCKPOOL_T *pool,
|
|
+ VCOS_UNSIGNED num_extensions, VCOS_UNSIGNED num_blocks);
|
|
+
|
|
+VCOSPRE_ void VCOSPOST_ *vcos_generic_blockpool_alloc(VCOS_BLOCKPOOL_T *pool);
|
|
+
|
|
+VCOSPRE_ void VCOSPOST_ *vcos_generic_blockpool_calloc(VCOS_BLOCKPOOL_T *pool);
|
|
+
|
|
+VCOSPRE_ void VCOSPOST_ vcos_generic_blockpool_free(void *block);
|
|
+
|
|
+VCOSPRE_
|
|
+ VCOS_UNSIGNED VCOSPOST_ vcos_generic_blockpool_available_count(
|
|
+ VCOS_BLOCKPOOL_T *pool);
|
|
+
|
|
+VCOSPRE_
|
|
+ VCOS_UNSIGNED VCOSPOST_ vcos_generic_blockpool_used_count(
|
|
+ VCOS_BLOCKPOOL_T *pool);
|
|
+
|
|
+VCOSPRE_ void VCOSPOST_ vcos_generic_blockpool_delete(VCOS_BLOCKPOOL_T *pool);
|
|
+
|
|
+VCOSPRE_ uint32_t VCOSPOST_ vcos_generic_blockpool_elem_to_handle(void *block);
|
|
+
|
|
+VCOSPRE_ void VCOSPOST_
|
|
+ *vcos_generic_blockpool_elem_from_handle(
|
|
+ VCOS_BLOCKPOOL_T *pool, uint32_t handle);
|
|
+
|
|
+VCOSPRE_ uint32_t VCOSPOST_
|
|
+ vcos_generic_blockpool_is_valid_elem(
|
|
+ VCOS_BLOCKPOOL_T *pool, const void *block);
|
|
+#if defined(VCOS_INLINE_BODIES)
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_blockpool_init(VCOS_BLOCKPOOL_T *pool,
|
|
+ VCOS_UNSIGNED num_blocks, VCOS_UNSIGNED block_size,
|
|
+ void *start, VCOS_UNSIGNED pool_size, const char *name)
|
|
+{
|
|
+ return vcos_generic_blockpool_init(pool, num_blocks, block_size,
|
|
+ start, pool_size, name);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_blockpool_create_on_heap(VCOS_BLOCKPOOL_T *pool,
|
|
+ VCOS_UNSIGNED num_blocks, VCOS_UNSIGNED block_size, const char *name)
|
|
+{
|
|
+ return vcos_generic_blockpool_create_on_heap(
|
|
+ pool, num_blocks, block_size, name);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+ VCOS_STATUS_T VCOSPOST_ vcos_blockpool_extend(VCOS_BLOCKPOOL_T *pool,
|
|
+ VCOS_UNSIGNED num_extensions, VCOS_UNSIGNED num_blocks)
|
|
+{
|
|
+ return vcos_generic_blockpool_extend(pool, num_extensions, num_blocks);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void *vcos_blockpool_alloc(VCOS_BLOCKPOOL_T *pool)
|
|
+{
|
|
+ return vcos_generic_blockpool_alloc(pool);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void *vcos_blockpool_calloc(VCOS_BLOCKPOOL_T *pool)
|
|
+{
|
|
+ return vcos_generic_blockpool_calloc(pool);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_blockpool_free(void *block)
|
|
+{
|
|
+ vcos_generic_blockpool_free(block);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_UNSIGNED vcos_blockpool_available_count(VCOS_BLOCKPOOL_T *pool)
|
|
+{
|
|
+ return vcos_generic_blockpool_available_count(pool);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_UNSIGNED vcos_blockpool_used_count(VCOS_BLOCKPOOL_T *pool)
|
|
+{
|
|
+ return vcos_generic_blockpool_used_count(pool);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_blockpool_delete(VCOS_BLOCKPOOL_T *pool)
|
|
+{
|
|
+ vcos_generic_blockpool_delete(pool);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+uint32_t vcos_blockpool_elem_to_handle(void *block)
|
|
+{
|
|
+ return vcos_generic_blockpool_elem_to_handle(block);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void *vcos_blockpool_elem_from_handle(VCOS_BLOCKPOOL_T *pool, uint32_t handle)
|
|
+{
|
|
+ return vcos_generic_blockpool_elem_from_handle(pool, handle);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+uint32_t vcos_blockpool_is_valid_elem(VCOS_BLOCKPOOL_T *pool, const void *block)
|
|
+{
|
|
+ return vcos_generic_blockpool_is_valid_elem(pool, block);
|
|
+}
|
|
+#endif /* VCOS_INLINE_BODIES */
|
|
+
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+#endif /* VCOS_GENERIC_BLOCKPOOL_H */
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_event_flags.c
|
|
@@ -0,0 +1,297 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - event flags implemented via mutexes
|
|
+=============================================================================*/
|
|
+
|
|
+#include "interface/vcos/vcos.h"
|
|
+#include "interface/vcos/generic/vcos_generic_event_flags.h"
|
|
+
|
|
+#include <stddef.h>
|
|
+
|
|
+/** A structure created by a thread that waits on the event flags
|
|
+ * for a particular combination of flags to arrive.
|
|
+ */
|
|
+typedef struct VCOS_EVENT_WAITER_T
|
|
+{
|
|
+ VCOS_UNSIGNED requested_events; /**< The events wanted */
|
|
+ VCOS_UNSIGNED actual_events; /**< Actual events found */
|
|
+ VCOS_UNSIGNED op; /**< The event operation to be used */
|
|
+ VCOS_STATUS_T return_status; /**< The return status the waiter should pass back */
|
|
+ VCOS_EVENT_FLAGS_T *flags; /**< Pointer to the original 'flags' structure */
|
|
+ VCOS_THREAD_T *thread; /**< Thread waiting */
|
|
+ struct VCOS_EVENT_WAITER_T *next;
|
|
+} VCOS_EVENT_WAITER_T;
|
|
+
|
|
+#ifndef NDEBUG
|
|
+static int waiter_list_valid(VCOS_EVENT_FLAGS_T *flags);
|
|
+#endif
|
|
+static void event_flags_timer_expired(void *cxt);
|
|
+
|
|
+VCOS_STATUS_T vcos_generic_event_flags_create(VCOS_EVENT_FLAGS_T *flags, const char *name)
|
|
+{
|
|
+ VCOS_STATUS_T rc;
|
|
+ if ((rc=vcos_mutex_create(&flags->lock, name)) != VCOS_SUCCESS)
|
|
+ {
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ flags->events = 0;
|
|
+ flags->waiters.head = flags->waiters.tail = 0;
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+void vcos_generic_event_flags_set(VCOS_EVENT_FLAGS_T *flags,
|
|
+ VCOS_UNSIGNED bitmask,
|
|
+ VCOS_OPTION op)
|
|
+{
|
|
+ vcos_assert(flags);
|
|
+ vcos_mutex_lock(&flags->lock);
|
|
+ if (op == VCOS_OR)
|
|
+ {
|
|
+ flags->events |= bitmask;
|
|
+ }
|
|
+ else if (op == VCOS_AND)
|
|
+ {
|
|
+ flags->events &= bitmask;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ vcos_assert(0);
|
|
+ }
|
|
+
|
|
+ /* Now wake up any threads that have now become signalled. */
|
|
+ if (flags->waiters.head != NULL)
|
|
+ {
|
|
+ VCOS_UNSIGNED consumed_events = 0;
|
|
+ VCOS_EVENT_WAITER_T **pcurrent_waiter = &flags->waiters.head;
|
|
+ VCOS_EVENT_WAITER_T *prev_waiter = NULL;
|
|
+
|
|
+ /* Walk the chain of tasks suspend on this event flag group to determine
|
|
+ * if any of their requests can be satisfied.
|
|
+ */
|
|
+ while ((*pcurrent_waiter) != NULL)
|
|
+ {
|
|
+ VCOS_EVENT_WAITER_T *curr_waiter = *pcurrent_waiter;
|
|
+
|
|
+ /* Determine if this request has been satisfied */
|
|
+
|
|
+ /* First, find the event flags in common. */
|
|
+ VCOS_UNSIGNED waiter_satisfied = flags->events & curr_waiter->requested_events;
|
|
+
|
|
+ /* Second, determine if all the event flags must match */
|
|
+ if (curr_waiter->op & VCOS_AND)
|
|
+ {
|
|
+ /* All requested events must be present */
|
|
+ waiter_satisfied = (waiter_satisfied == curr_waiter->requested_events);
|
|
+ }
|
|
+
|
|
+ /* Wake this one up? */
|
|
+ if (waiter_satisfied)
|
|
+ {
|
|
+
|
|
+ if (curr_waiter->op & VCOS_CONSUME)
|
|
+ {
|
|
+ consumed_events |= curr_waiter->requested_events;
|
|
+ }
|
|
+
|
|
+ /* remove this block from the list, taking care at the end */
|
|
+ *pcurrent_waiter = curr_waiter->next;
|
|
+ if (curr_waiter->next == NULL)
|
|
+ flags->waiters.tail = prev_waiter;
|
|
+
|
|
+ vcos_assert(waiter_list_valid(flags));
|
|
+
|
|
+ curr_waiter->return_status = VCOS_SUCCESS;
|
|
+ curr_waiter->actual_events = flags->events;
|
|
+
|
|
+ _vcos_thread_sem_post(curr_waiter->thread);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* move to next element in the list */
|
|
+ prev_waiter = *pcurrent_waiter;
|
|
+ pcurrent_waiter = &(curr_waiter->next);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ flags->events &= ~consumed_events;
|
|
+
|
|
+ }
|
|
+
|
|
+ vcos_mutex_unlock(&flags->lock);
|
|
+}
|
|
+
|
|
+void vcos_generic_event_flags_delete(VCOS_EVENT_FLAGS_T *flags)
|
|
+{
|
|
+ vcos_mutex_delete(&flags->lock);
|
|
+}
|
|
+
|
|
+extern VCOS_STATUS_T vcos_generic_event_flags_get(VCOS_EVENT_FLAGS_T *flags,
|
|
+ VCOS_UNSIGNED bitmask,
|
|
+ VCOS_OPTION op,
|
|
+ VCOS_UNSIGNED suspend,
|
|
+ VCOS_UNSIGNED *retrieved_bits)
|
|
+{
|
|
+ VCOS_EVENT_WAITER_T waitreq;
|
|
+ VCOS_STATUS_T rc = VCOS_EAGAIN;
|
|
+ int satisfied = 0;
|
|
+
|
|
+ vcos_assert(flags);
|
|
+
|
|
+ /* default retrieved bits to 0 */
|
|
+ *retrieved_bits = 0;
|
|
+
|
|
+ vcos_mutex_lock(&flags->lock);
|
|
+ switch (op & VCOS_EVENT_FLAG_OP_MASK)
|
|
+ {
|
|
+ case VCOS_AND:
|
|
+ if ((flags->events & bitmask) == bitmask)
|
|
+ {
|
|
+ *retrieved_bits = flags->events;
|
|
+ rc = VCOS_SUCCESS;
|
|
+ satisfied = 1;
|
|
+ if (op & VCOS_CONSUME)
|
|
+ flags->events &= ~bitmask;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case VCOS_OR:
|
|
+ if (flags->events & bitmask)
|
|
+ {
|
|
+ *retrieved_bits = flags->events;
|
|
+ rc = VCOS_SUCCESS;
|
|
+ satisfied = 1;
|
|
+ if (op & VCOS_CONSUME)
|
|
+ flags->events &= ~bitmask;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ vcos_assert(0);
|
|
+ rc = VCOS_EINVAL;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (!satisfied && suspend)
|
|
+ {
|
|
+ /* Have to go to sleep.
|
|
+ *
|
|
+ * Append to tail so we get FIFO ordering.
|
|
+ */
|
|
+ waitreq.requested_events = bitmask;
|
|
+ waitreq.op = op;
|
|
+ waitreq.return_status = VCOS_EAGAIN;
|
|
+ waitreq.flags = flags;
|
|
+ waitreq.actual_events = 0;
|
|
+ waitreq.thread = vcos_thread_current();
|
|
+ waitreq.next = 0;
|
|
+ vcos_assert(waitreq.thread != (VCOS_THREAD_T*)-1);
|
|
+ VCOS_QUEUE_APPEND_TAIL(&flags->waiters, &waitreq);
|
|
+
|
|
+ if (suspend != (VCOS_UNSIGNED)-1)
|
|
+ _vcos_task_timer_set(event_flags_timer_expired, &waitreq, suspend);
|
|
+
|
|
+ vcos_mutex_unlock(&flags->lock);
|
|
+ /* go to sleep and wait to be signalled or timeout */
|
|
+
|
|
+ _vcos_thread_sem_wait();
|
|
+
|
|
+ *retrieved_bits = waitreq.actual_events;
|
|
+ rc = waitreq.return_status;
|
|
+
|
|
+ /* cancel the timer - do not do this while holding the mutex as it
|
|
+ * might be waiting for the timeout function to complete, which will
|
|
+ * try to take the mutex.
|
|
+ */
|
|
+ if (suspend != (VCOS_UNSIGNED)-1)
|
|
+ _vcos_task_timer_cancel();
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ vcos_mutex_unlock(&flags->lock);
|
|
+ }
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+
|
|
+/** Called when a get call times out. Remove this thread's
|
|
+ * entry from the waiting queue, then resume the thread.
|
|
+ */
|
|
+static void event_flags_timer_expired(void *cxt)
|
|
+{
|
|
+ VCOS_EVENT_WAITER_T *waitreq = (VCOS_EVENT_WAITER_T *)cxt;
|
|
+ VCOS_EVENT_FLAGS_T *flags = waitreq->flags;
|
|
+ VCOS_EVENT_WAITER_T **plist;
|
|
+ VCOS_EVENT_WAITER_T *prev = NULL;
|
|
+ VCOS_THREAD_T *thread = 0;
|
|
+
|
|
+ vcos_assert(flags);
|
|
+
|
|
+ vcos_mutex_lock(&flags->lock);
|
|
+
|
|
+ /* walk the list of waiting threads on this event group, and remove
|
|
+ * the one that has expired.
|
|
+ *
|
|
+ * FIXME: could use doubly-linked list if lots of threads are found
|
|
+ * to be waiting on a single event flag instance.
|
|
+ */
|
|
+ plist = &flags->waiters.head;
|
|
+ while (*plist != NULL)
|
|
+ {
|
|
+ if (*plist == waitreq)
|
|
+ {
|
|
+ int at_end;
|
|
+ /* found it */
|
|
+ thread = (*plist)->thread;
|
|
+ at_end = ((*plist)->next == NULL);
|
|
+
|
|
+ /* link past */
|
|
+ *plist = (*plist)->next;
|
|
+ if (at_end)
|
|
+ flags->waiters.tail = prev;
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ prev = *plist;
|
|
+ plist = &(*plist)->next;
|
|
+ }
|
|
+ vcos_assert(waiter_list_valid(flags));
|
|
+
|
|
+ vcos_mutex_unlock(&flags->lock);
|
|
+
|
|
+ if (thread)
|
|
+ {
|
|
+ _vcos_thread_sem_post(thread);
|
|
+ }
|
|
+}
|
|
+
|
|
+#ifndef NDEBUG
|
|
+
|
|
+static int waiter_list_valid(VCOS_EVENT_FLAGS_T *flags)
|
|
+{
|
|
+ int valid;
|
|
+ /* Either both head and tail are NULL, or neither are NULL */
|
|
+ if (flags->waiters.head == NULL)
|
|
+ {
|
|
+ valid = (flags->waiters.tail == NULL);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ valid = (flags->waiters.tail != NULL);
|
|
+ }
|
|
+
|
|
+ /* If head and tail point at the same non-NULL element, then there
|
|
+ * is only one element in the list.
|
|
+ */
|
|
+ if (flags->waiters.head && (flags->waiters.head == flags->waiters.tail))
|
|
+ {
|
|
+ valid = (flags->waiters.head->next == NULL);
|
|
+ }
|
|
+ return valid;
|
|
+}
|
|
+
|
|
+#endif
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_event_flags.h
|
|
@@ -0,0 +1,104 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - event flags implemented via a semaphore
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_GENERIC_EVENT_FLAGS_H
|
|
+#define VCOS_GENERIC_EVENT_FLAGS_H
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+
|
|
+/**
|
|
+ * \file
|
|
+ *
|
|
+ * This provides event flags (as per Nucleus Event Groups) based on a
|
|
+ * mutex, a semaphore (per waiting thread) and a timer (per waiting
|
|
+ * thread).
|
|
+ *
|
|
+ * The data structure is a 32 bit unsigned int (the current set of
|
|
+ * flags) and a linked list of clients waiting to be 'satisfied'.
|
|
+ *
|
|
+ * The mutex merely locks access to the data structure. If a client
|
|
+ * calls vcos_event_flags_get() and the requested bits are not already
|
|
+ * present, it then sleeps on its per-thread semaphore after adding
|
|
+ * this semaphore to the queue waiting. It also sets up a timer.
|
|
+ *
|
|
+ * The per-thread semaphore and timer are actually stored in the
|
|
+ * thread context (joinable thread). In future it may become necessary
|
|
+ * to support non-VCOS threads by using thread local storage to
|
|
+ * create these objects and associate them with the thread.
|
|
+ */
|
|
+
|
|
+struct VCOS_EVENT_WAITER_T;
|
|
+
|
|
+typedef struct VCOS_EVENT_FLAGS_T
|
|
+{
|
|
+ VCOS_UNSIGNED events; /**< Events currently set */
|
|
+ VCOS_MUTEX_T lock; /**< Serialize access */
|
|
+ struct
|
|
+ {
|
|
+ struct VCOS_EVENT_WAITER_T *head; /**< List of threads waiting */
|
|
+ struct VCOS_EVENT_WAITER_T *tail; /**< List of threads waiting */
|
|
+ } waiters;
|
|
+} VCOS_EVENT_FLAGS_T;
|
|
+
|
|
+#define VCOS_OR 1
|
|
+#define VCOS_AND 2
|
|
+#define VCOS_CONSUME 4
|
|
+#define VCOS_OR_CONSUME (VCOS_OR | VCOS_CONSUME)
|
|
+#define VCOS_AND_CONSUME (VCOS_AND | VCOS_CONSUME)
|
|
+#define VCOS_EVENT_FLAG_OP_MASK (VCOS_OR|VCOS_AND)
|
|
+
|
|
+VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_generic_event_flags_create(VCOS_EVENT_FLAGS_T *flags, const char *name);
|
|
+VCOSPRE_ void VCOSPOST_ vcos_generic_event_flags_set(VCOS_EVENT_FLAGS_T *flags,
|
|
+ VCOS_UNSIGNED events,
|
|
+ VCOS_OPTION op);
|
|
+VCOSPRE_ void VCOSPOST_ vcos_generic_event_flags_delete(VCOS_EVENT_FLAGS_T *);
|
|
+VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_generic_event_flags_get(VCOS_EVENT_FLAGS_T *flags,
|
|
+ VCOS_UNSIGNED requested_events,
|
|
+ VCOS_OPTION op,
|
|
+ VCOS_UNSIGNED suspend,
|
|
+ VCOS_UNSIGNED *retrieved_events);
|
|
+
|
|
+#ifdef VCOS_INLINE_BODIES
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_event_flags_create(VCOS_EVENT_FLAGS_T *flags, const char *name) {
|
|
+ return vcos_generic_event_flags_create(flags, name);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_event_flags_set(VCOS_EVENT_FLAGS_T *flags,
|
|
+ VCOS_UNSIGNED events,
|
|
+ VCOS_OPTION op) {
|
|
+ vcos_generic_event_flags_set(flags, events, op);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_event_flags_delete(VCOS_EVENT_FLAGS_T *f) {
|
|
+ vcos_generic_event_flags_delete(f);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_event_flags_get(VCOS_EVENT_FLAGS_T *flags,
|
|
+ VCOS_UNSIGNED requested_events,
|
|
+ VCOS_OPTION op,
|
|
+ VCOS_UNSIGNED suspend,
|
|
+ VCOS_UNSIGNED *retrieved_events) {
|
|
+ return vcos_generic_event_flags_get(flags, requested_events, op, suspend, retrieved_events);
|
|
+}
|
|
+
|
|
+#endif /* VCOS_INLINE_BODIES */
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+#endif
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_named_sem.h
|
|
@@ -0,0 +1,81 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - named semaphores
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_GENERIC_NAMED_SEM_H
|
|
+#define VCOS_GENERIC_NAMED_SEM_H
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+
|
|
+/**
|
|
+ * \file
|
|
+ *
|
|
+ * Generic support for named semaphores, using regular ones. This is only
|
|
+ * suitable for emulating them on an embedded MMUless system, since there is
|
|
+ * no support for opening semaphores across process boundaries.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#define VCOS_NAMED_SEMAPHORE_NAMELEN 64
|
|
+
|
|
+/* In theory we could use the name facility provided within Nucleus. However, this
|
|
+ * is hard to do as semaphores are constantly being created and destroyed; we
|
|
+ * would need to stop everything while allocating the memory for the semaphore
|
|
+ * list and then walking it. So keep our own list.
|
|
+ */
|
|
+typedef struct VCOS_NAMED_SEMAPHORE_T
|
|
+{
|
|
+ struct VCOS_NAMED_SEMAPHORE_IMPL_T *actual; /**< There are 'n' named semaphores per 1 actual semaphore */
|
|
+ VCOS_SEMAPHORE_T *sem; /**< Pointer to actual underlying semaphore */
|
|
+} VCOS_NAMED_SEMAPHORE_T;
|
|
+
|
|
+VCOSPRE_ VCOS_STATUS_T VCOSPOST_
|
|
+vcos_generic_named_semaphore_create(VCOS_NAMED_SEMAPHORE_T *sem, const char *name, VCOS_UNSIGNED count);
|
|
+
|
|
+VCOSPRE_ void VCOSPOST_ vcos_named_semaphore_delete(VCOS_NAMED_SEMAPHORE_T *sem);
|
|
+
|
|
+VCOSPRE_ VCOS_STATUS_T VCOSPOST_ _vcos_named_semaphore_init(void);
|
|
+VCOSPRE_ void VCOSPOST_ _vcos_named_semaphore_deinit(void);
|
|
+
|
|
+#if defined(VCOS_INLINE_BODIES)
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_named_semaphore_create(VCOS_NAMED_SEMAPHORE_T *sem, const char *name, VCOS_UNSIGNED count) {
|
|
+ return vcos_generic_named_semaphore_create(sem, name, count);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_named_semaphore_wait(VCOS_NAMED_SEMAPHORE_T *sem) {
|
|
+ vcos_semaphore_wait(sem->sem);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_named_semaphore_trywait(VCOS_NAMED_SEMAPHORE_T *sem) {
|
|
+ return vcos_semaphore_trywait(sem->sem);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_named_semaphore_post(VCOS_NAMED_SEMAPHORE_T *sem) {
|
|
+ vcos_semaphore_post(sem->sem);
|
|
+}
|
|
+
|
|
+
|
|
+#endif
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+#endif
|
|
+
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_quickslow_mutex.h
|
|
@@ -0,0 +1,75 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - reentrant mutexes created from regular ones.
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_GENERIC_QUICKSLOW_MUTEX_H
|
|
+#define VCOS_GENERIC_QUICKSLOW_MUTEX_H
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+
|
|
+/**
|
|
+ * \file
|
|
+ *
|
|
+ * Quickslow Mutexes implemented as regular ones (i.e. quick and slow modes are the same).
|
|
+ *
|
|
+ */
|
|
+
|
|
+typedef VCOS_MUTEX_T VCOS_QUICKSLOW_MUTEX_T;
|
|
+
|
|
+#if defined(VCOS_INLINE_BODIES)
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_quickslow_mutex_create(VCOS_QUICKSLOW_MUTEX_T *m, const char *name)
|
|
+{
|
|
+ return vcos_mutex_create(m, name);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_quickslow_mutex_delete(VCOS_QUICKSLOW_MUTEX_T *m)
|
|
+{
|
|
+ vcos_mutex_delete(m);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_quickslow_mutex_lock(VCOS_QUICKSLOW_MUTEX_T *m)
|
|
+{
|
|
+ while (vcos_mutex_lock(m) == VCOS_EAGAIN);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_quickslow_mutex_unlock(VCOS_QUICKSLOW_MUTEX_T *m)
|
|
+{
|
|
+ vcos_mutex_unlock(m);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_quickslow_mutex_lock_quick(VCOS_QUICKSLOW_MUTEX_T *m)
|
|
+{
|
|
+ while (vcos_mutex_lock(m) == VCOS_EAGAIN);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_quickslow_mutex_unlock_quick(VCOS_QUICKSLOW_MUTEX_T *m)
|
|
+{
|
|
+ vcos_mutex_unlock(m);
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+#endif
|
|
+
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_reentrant_mtx.h
|
|
@@ -0,0 +1,75 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - reentrant mutexes created from regular ones.
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_GENERIC_REENTRANT_MUTEX_H
|
|
+#define VCOS_GENERIC_REENTRANT_MUTEX_H
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+
|
|
+/**
|
|
+ * \file
|
|
+ *
|
|
+ * Reentrant Mutexes from regular ones.
|
|
+ *
|
|
+ */
|
|
+
|
|
+typedef struct VCOS_REENTRANT_MUTEX_T
|
|
+{
|
|
+ VCOS_MUTEX_T mutex;
|
|
+ VCOS_THREAD_T *owner;
|
|
+ unsigned count;
|
|
+} VCOS_REENTRANT_MUTEX_T;
|
|
+
|
|
+/* Extern definitions of functions that do the actual work */
|
|
+
|
|
+VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_generic_reentrant_mutex_create(VCOS_REENTRANT_MUTEX_T *m, const char *name);
|
|
+
|
|
+VCOSPRE_ void VCOSPOST_ vcos_generic_reentrant_mutex_delete(VCOS_REENTRANT_MUTEX_T *m);
|
|
+
|
|
+VCOSPRE_ void VCOSPOST_ vcos_generic_reentrant_mutex_lock(VCOS_REENTRANT_MUTEX_T *m);
|
|
+
|
|
+VCOSPRE_ void VCOSPOST_ vcos_generic_reentrant_mutex_unlock(VCOS_REENTRANT_MUTEX_T *m);
|
|
+
|
|
+/* Inline forwarding functions */
|
|
+
|
|
+#if defined(VCOS_INLINE_BODIES)
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_reentrant_mutex_create(VCOS_REENTRANT_MUTEX_T *m, const char *name) {
|
|
+ return vcos_generic_reentrant_mutex_create(m,name);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_reentrant_mutex_delete(VCOS_REENTRANT_MUTEX_T *m) {
|
|
+ vcos_generic_reentrant_mutex_delete(m);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_reentrant_mutex_lock(VCOS_REENTRANT_MUTEX_T *m) {
|
|
+ vcos_generic_reentrant_mutex_lock(m);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_reentrant_mutex_unlock(VCOS_REENTRANT_MUTEX_T *m) {
|
|
+ vcos_generic_reentrant_mutex_unlock(m);
|
|
+}
|
|
+#endif
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+#endif
|
|
+
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_tls.h
|
|
@@ -0,0 +1,144 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - generic thread local storage
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_GENERIC_TLS_H
|
|
+#define VCOS_GENERIC_TLS_H
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+
|
|
+/**
|
|
+ * \file
|
|
+ *
|
|
+ * Do an emulation of Thread Local Storage. The platform needs to
|
|
+ * provide a way to set and get a per-thread pointer which is
|
|
+ * where the TLS data itself is stored.
|
|
+ *
|
|
+ *
|
|
+ * Each thread that wants to join in this scheme needs to call
|
|
+ * vcos_tls_thread_register().
|
|
+ *
|
|
+ * The platform needs to support the macros/functions
|
|
+ * _vcos_tls_thread_ptr_set() and _vcos_tls_thread_ptr_get().
|
|
+ */
|
|
+
|
|
+#ifndef VCOS_WANT_TLS_EMULATION
|
|
+#error Should not be included unless TLS emulation is defined
|
|
+#endif
|
|
+
|
|
+/** Number of slots to reserve per thread. This results in an overhead
|
|
+ * of this many words per thread.
|
|
+ */
|
|
+#define VCOS_TLS_MAX_SLOTS 4
|
|
+
|
|
+/** TLS key. Allocating one of these reserves the client one of the
|
|
+ * available slots.
|
|
+ */
|
|
+typedef VCOS_UNSIGNED VCOS_TLS_KEY_T;
|
|
+
|
|
+/** TLS per-thread structure. Each thread gets one of these
|
|
+ * if TLS emulation (rather than native TLS support) is
|
|
+ * being used.
|
|
+ */
|
|
+typedef struct VCOS_TLS_THREAD_T
|
|
+{
|
|
+ void *slots[VCOS_TLS_MAX_SLOTS];
|
|
+} VCOS_TLS_THREAD_T;
|
|
+
|
|
+/*
|
|
+ * Internal APIs
|
|
+ */
|
|
+
|
|
+/** Register this thread's TLS storage area. */
|
|
+VCOSPRE_ void VCOSPOST_ vcos_tls_thread_register(VCOS_TLS_THREAD_T *);
|
|
+
|
|
+/** Create a new TLS key */
|
|
+VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_generic_tls_create(VCOS_TLS_KEY_T *key);
|
|
+
|
|
+/** Delete a TLS key */
|
|
+VCOSPRE_ void VCOSPOST_ vcos_generic_tls_delete(VCOS_TLS_KEY_T tls);
|
|
+
|
|
+/** Initialise the TLS library */
|
|
+VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_tls_init(void);
|
|
+
|
|
+/** Deinitialise the TLS library */
|
|
+VCOSPRE_ void VCOSPOST_ vcos_tls_deinit(void);
|
|
+
|
|
+#if defined(VCOS_INLINE_BODIES)
|
|
+
|
|
+#undef VCOS_ASSERT_LOGGING_DISABLE
|
|
+#define VCOS_ASSERT_LOGGING_DISABLE 1
|
|
+
|
|
+/*
|
|
+ * Implementations of public API functions
|
|
+ */
|
|
+
|
|
+/** Set the given value. Since everything is per-thread, there is no need
|
|
+ * for any locking.
|
|
+ */
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_tls_set(VCOS_TLS_KEY_T tls, void *v) {
|
|
+ VCOS_TLS_THREAD_T *tlsdata = _vcos_tls_thread_ptr_get();
|
|
+ vcos_assert(tlsdata); /* Fires if this thread has not been registered */
|
|
+ if (tls<VCOS_TLS_MAX_SLOTS)
|
|
+ {
|
|
+ tlsdata->slots[tls] = v;
|
|
+ return VCOS_SUCCESS;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ vcos_assert(0);
|
|
+ return VCOS_EINVAL;
|
|
+ }
|
|
+}
|
|
+
|
|
+/** Get the given value. No locking required.
|
|
+ */
|
|
+VCOS_INLINE_IMPL
|
|
+void *vcos_tls_get(VCOS_TLS_KEY_T tls) {
|
|
+ VCOS_TLS_THREAD_T *tlsdata = _vcos_tls_thread_ptr_get();
|
|
+ vcos_assert(tlsdata); /* Fires if this thread has not been registered */
|
|
+ if (tls<VCOS_TLS_MAX_SLOTS)
|
|
+ {
|
|
+ return tlsdata->slots[tls];
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ vcos_assert(0);
|
|
+ return NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_tls_create(VCOS_TLS_KEY_T *key) {
|
|
+ return vcos_generic_tls_create(key);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_tls_delete(VCOS_TLS_KEY_T tls) {
|
|
+ vcos_generic_tls_delete(tls);
|
|
+}
|
|
+
|
|
+#undef VCOS_ASSERT_LOGGING_DISABLE
|
|
+#define VCOS_ASSERT_LOGGING_DISABLE 0
|
|
+
|
|
+#endif /* VCOS_INLINE_BODIES */
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+
|
|
+#endif
|
|
+
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_joinable_thread_from_plain.h
|
|
@@ -0,0 +1,202 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Module : vcos
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - implementation: joinable thread from plain
|
|
+=============================================================================*/
|
|
+
|
|
+/** \file
|
|
+ *
|
|
+ * Header file for platforms creating the joinable thread from a lowlevel
|
|
+ * thread.
|
|
+ *
|
|
+ * In addition to the actual thread, the following are also created:
|
|
+ *
|
|
+ * - a semaphore to wait on when joining the thread
|
|
+ * - a semaphore to support counted suspend/resume (used by event group)
|
|
+ * - a per-thread timer (used by event group, but could be removed)
|
|
+ */
|
|
+
|
|
+#ifndef VCOS_JOINABLE_THREAD_FROM_PLAIN_H
|
|
+#define VCOS_JOINABLE_THREAD_FROM_PLAIN_H
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include "interface/vcos/vcos_semaphore.h"
|
|
+#include "interface/vcos/vcos_lowlevel_thread.h"
|
|
+#include "interface/vcos/vcos_timer.h"
|
|
+
|
|
+#ifdef VCOS_WANT_TLS_EMULATION
|
|
+#include "interface/vcos/generic/vcos_generic_tls.h"
|
|
+#endif
|
|
+
|
|
+#define VCOS_THREAD_MAGIC 0x56436a74
|
|
+
|
|
+#define VCOS_THREAD_VALID(t) (t->magic == VCOS_THREAD_MAGIC)
|
|
+#define VCOS_HAVE_THREAD_AT_EXIT 1
|
|
+
|
|
+/** Thread attribute structure. Clients should not manipulate this directly, but
|
|
+ * should instead use the provided functions.
|
|
+ */
|
|
+typedef struct VCOS_THREAD_ATTR_T
|
|
+{
|
|
+ void *ta_stackaddr;
|
|
+ VCOS_UNSIGNED ta_stacksz;
|
|
+ VCOS_UNSIGNED ta_priority;
|
|
+ VCOS_UNSIGNED ta_affinity;
|
|
+ VCOS_UNSIGNED ta_timeslice;
|
|
+ VCOS_UNSIGNED legacy;
|
|
+ VCOS_UNSIGNED ta_autostart;
|
|
+} VCOS_THREAD_ATTR_T;
|
|
+
|
|
+/** Each thread gets a timer, which is for internal VCOS use.
|
|
+ */
|
|
+typedef struct _VCOS_THREAD_TIMER_T
|
|
+{
|
|
+ VCOS_TIMER_T timer;
|
|
+ void (*pfn)(void *);
|
|
+ void *cxt;
|
|
+} _VCOS_THREAD_TIMER_T;
|
|
+
|
|
+typedef void (*VCOS_THREAD_EXIT_HANDLER_T)(void *);
|
|
+/** Called at thread exit.
|
|
+ */
|
|
+typedef struct VCOS_THREAD_EXIT_T
|
|
+{
|
|
+ VCOS_THREAD_EXIT_HANDLER_T pfn;
|
|
+ void *cxt;
|
|
+} VCOS_THREAD_EXIT_T;
|
|
+#define VCOS_MAX_EXIT_HANDLERS 8
|
|
+
|
|
+/* The name field isn't used for anything, so we can just copy the
|
|
+ * the pointer. Nucleus makes its own copy.
|
|
+ */
|
|
+typedef const char * VCOS_LLTHREAD_T_NAME;
|
|
+#define _VCOS_LLTHREAD_NAME(dst,src) (dst)=(src)
|
|
+
|
|
+/*
|
|
+ * Simulated TLS support
|
|
+ */
|
|
+
|
|
+
|
|
+/** Thread structure.
|
|
+ *
|
|
+ * \warning Do not access the members of this structure directly!
|
|
+ */
|
|
+typedef struct VCOS_THREAD_T
|
|
+{
|
|
+ VCOS_LLTHREAD_T thread; /**< The underlying thread */
|
|
+ char name[16]; /**< The name */
|
|
+ unsigned int magic; /**< For debug */
|
|
+ void *exit_data; /**< Exit data passed out in vcos_joinable_thread_exit() */
|
|
+ void *stack; /**< Stack, if not supplied by caller */
|
|
+ VCOS_SEMAPHORE_T wait; /**< Semaphore to wait on at join */
|
|
+ VCOS_SEMAPHORE_T suspend; /**< Semaphore to wait on for counted suspend */
|
|
+ int16_t joined; /**< Joined yet? For debug. */
|
|
+ VCOS_UNSIGNED legacy; /**< Use (argc,argv) for entry point arguments */
|
|
+ void *(*entry)(void*); /**< Entry point */
|
|
+ void *arg; /**< Argument passed to entry point */
|
|
+ void *(*term)(void*); /**< Termination function, used by reaper */
|
|
+ void *term_arg; /**< Argument passed to termination function */
|
|
+ _VCOS_THREAD_TIMER_T _timer; /**< Internal timer, mainly for event groups */
|
|
+#ifdef VCOS_WANT_TLS_EMULATION
|
|
+ VCOS_TLS_THREAD_T _tls; /**< TLS data when native TLS not available, or NULL */
|
|
+#endif
|
|
+ /** Array of functions to call at thread exit */
|
|
+ VCOS_THREAD_EXIT_T at_exit[VCOS_MAX_EXIT_HANDLERS];
|
|
+
|
|
+ struct VCOS_THREAD_T *next; /**< For linked lists of threads */
|
|
+} VCOS_THREAD_T;
|
|
+
|
|
+#if defined(VCOS_INLINE_BODIES)
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_thread_attr_setstack(VCOS_THREAD_ATTR_T *attrs, void *addr, VCOS_UNSIGNED stacksz) {
|
|
+ attrs->ta_stackaddr = addr;
|
|
+ attrs->ta_stacksz = stacksz;
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_thread_attr_setstacksize(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED stacksz) {
|
|
+ attrs->ta_stacksz = stacksz;
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_thread_attr_setpriority(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED pri) {
|
|
+ attrs->ta_priority = pri;
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_thread_attr_setaffinity(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED affinity) {
|
|
+ attrs->ta_affinity = affinity;
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_thread_attr_settimeslice(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED ts) {
|
|
+ attrs->ta_timeslice = ts;
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void _vcos_thread_attr_setlegacyapi(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED legacy) {
|
|
+ attrs->legacy = legacy;
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_thread_attr_setautostart(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED autostart) {
|
|
+ attrs->ta_autostart = autostart;
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_THREAD_T *vcos_thread_current(void) {
|
|
+ VCOS_THREAD_T *ret = (VCOS_THREAD_T*)vcos_llthread_current();
|
|
+ /*If we're called from a non-vcos thread, this assert will fail.
|
|
+ *XXX FIXME why is this commented out?
|
|
+ *vcos_assert(ret->magic == VCOS_THREAD_MAGIC);
|
|
+ */
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+int vcos_thread_running(VCOS_THREAD_T *thread) {
|
|
+ return vcos_llthread_running(&thread->thread);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_thread_resume(VCOS_THREAD_T *thread) {
|
|
+ vcos_llthread_resume(&thread->thread);
|
|
+}
|
|
+
|
|
+#endif /* VCOS_INLINE_BODIES */
|
|
+
|
|
+/**
|
|
+ * \brief Create a VCOS_THREAD_T for the current thread. This is so we can have
|
|
+ * VCOS_THREAD_Ts even for threads not originally created by VCOS (eg the
|
|
+ * thread that calls vcos_init)
|
|
+ */
|
|
+extern VCOS_STATUS_T _vcos_thread_create_attach(VCOS_THREAD_T *thread,
|
|
+ const char *name);
|
|
+
|
|
+/**
|
|
+ * \brief Deletes the VCOS_THREAD_T, but does not wait for the underlying
|
|
+ * thread to exit. This will cleanup everything created by
|
|
+ * _vcos_thread_create_attach
|
|
+ */
|
|
+extern void _vcos_thread_delete(VCOS_THREAD_T *thread);
|
|
+
|
|
+/** Register a function to be called when the current thread exits.
|
|
+ */
|
|
+extern VCOS_STATUS_T vcos_thread_at_exit(void (*pfn)(void*), void *cxt);
|
|
+
|
|
+/** Deregister a previously registered at-exit function.
|
|
+ */
|
|
+extern void vcos_thread_deregister_at_exit(void (*pfn)(void*), void *cxt);
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+#endif /* VCOS_JOINABLE_THREAD_FROM_PLAIN_H */
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_latch_from_sem.h
|
|
@@ -0,0 +1,48 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : vcos
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - Construct a latch from a semaphore
|
|
+=============================================================================*/
|
|
+
|
|
+/** FIXME: rename to vcos_mutex_from_sem.c
|
|
+ */
|
|
+
|
|
+typedef struct VCOS_MUTEX_T {
|
|
+ VCOS_SEMAPHORE_T sem;
|
|
+ struct VCOS_THREAD_T *owner;
|
|
+} VCOS_MUTEX_T;
|
|
+
|
|
+extern VCOS_STATUS_T vcos_generic_mutex_create(VCOS_MUTEX_T *latch, const char *name);
|
|
+extern void vcos_generic_mutex_delete(VCOS_MUTEX_T *latch);
|
|
+extern VCOS_STATUS_T vcos_generic_mutex_lock(VCOS_MUTEX_T *latch);
|
|
+extern void vcos_generic_mutex_unlock(VCOS_MUTEX_T *latch);
|
|
+
|
|
+#if defined(VCOS_INLINE_BODIES)
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_mutex_create(VCOS_MUTEX_T *latch, const char *name) {
|
|
+ return vcos_generic_mutex_create(latch,name);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_mutex_delete(VCOS_MUTEX_T *latch) {
|
|
+ vcos_generic_mutex_delete(latch);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_mutex_lock(VCOS_MUTEX_T *latch) {
|
|
+ return vcos_generic_mutex_lock(latch);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_mutex_unlock(VCOS_MUTEX_T *latch) {
|
|
+ vcos_generic_mutex_unlock(latch);
|
|
+}
|
|
+
|
|
+#endif /* VCOS_INLINE_BODIES */
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_logcat.c
|
|
@@ -0,0 +1,549 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2010 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : vcos
|
|
+
|
|
+FILE DESCRIPTION
|
|
+Categorized logging for VCOS - a generic implementation.
|
|
+=============================================================================*/
|
|
+
|
|
+#include "interface/vcos/vcos.h"
|
|
+#include "interface/vcos/vcos_ctype.h"
|
|
+#include "interface/vcos/vcos_string.h"
|
|
+
|
|
+static VCOS_MUTEX_T lock;
|
|
+static int warned_loglevel; /* only warn about invalid log level once */
|
|
+static VCOS_VLOG_IMPL_FUNC_T vcos_vlog_impl_func = vcos_vlog_default_impl;
|
|
+
|
|
+#define VCOS_LOG_CATEGORY (&dflt_log_category)
|
|
+static VCOS_LOG_CAT_T dflt_log_category;
|
|
+VCOS_LOG_CAT_T *vcos_logging_categories = NULL;
|
|
+static int inited;
|
|
+
|
|
+#if VCOS_HAVE_CMD
|
|
+
|
|
+/*
|
|
+ * For kernel or videocore purposes, we generally want the log command. For
|
|
+ * user-space apps, they might want to provide their own log command, so we
|
|
+ * don't include the built in on.
|
|
+ *
|
|
+ * So pthreads/vcos_platform.h defines VCOS_WANT_LOG_CMD to be 0. It is
|
|
+ * undefined elsewhere.
|
|
+ */
|
|
+
|
|
+# if !defined( VCOS_WANT_LOG_CMD )
|
|
+# define VCOS_WANT_LOG_CMD 1
|
|
+# endif
|
|
+#else
|
|
+# define VCOS_WANT_LOG_CMD 0
|
|
+#endif
|
|
+
|
|
+#if VCOS_WANT_LOG_CMD
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Does a vcos_assert(0), which is useful to test logging.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+VCOS_STATUS_T vcos_log_assert_cmd( VCOS_CMD_PARAM_T *param )
|
|
+{
|
|
+ (void)param;
|
|
+
|
|
+#if defined( NDEBUG ) && !defined( VCOS_RELEASE_ASSERTS )
|
|
+ vcos_log_error( "vcos_asserts have been compiled out" );
|
|
+ vcos_cmd_printf( param, "vcos_asserts have been compiled out - did a vcos_log_error instead\n" );
|
|
+#else
|
|
+ vcos_assert(0);
|
|
+ vcos_cmd_printf( param, "Executed vcos_assert(0)\n" );
|
|
+#endif
|
|
+
|
|
+ return VCOS_SUCCESS;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Sets a vcos logging level
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+VCOS_STATUS_T vcos_log_set_cmd( VCOS_CMD_PARAM_T *param )
|
|
+{
|
|
+ VCOS_LOG_CAT_T *cat;
|
|
+ char *name;
|
|
+ char *levelStr;
|
|
+ VCOS_LOG_LEVEL_T level;
|
|
+ VCOS_STATUS_T status;
|
|
+
|
|
+ if ( param->argc != 3 )
|
|
+ {
|
|
+ vcos_cmd_usage( param );
|
|
+ return VCOS_EINVAL;
|
|
+ }
|
|
+
|
|
+ name = param->argv[1];
|
|
+ levelStr = param->argv[2];
|
|
+
|
|
+ if ( vcos_string_to_log_level( levelStr, &level ) != VCOS_SUCCESS )
|
|
+ {
|
|
+ vcos_cmd_printf( param, "Unrecognized logging level: '%s'\n", levelStr );
|
|
+ return VCOS_EINVAL;
|
|
+ }
|
|
+
|
|
+ vcos_mutex_lock(&lock);
|
|
+
|
|
+ status = VCOS_SUCCESS;
|
|
+ for ( cat = vcos_logging_categories; cat != NULL; cat = cat->next )
|
|
+ {
|
|
+ if ( vcos_strcmp( name, cat->name ) == 0 )
|
|
+ {
|
|
+ cat->level = level;
|
|
+ vcos_cmd_printf( param, "Category %s level set to %s\n", name, levelStr );
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if ( cat == NULL )
|
|
+ {
|
|
+ vcos_cmd_printf( param, "Unrecognized category: '%s'\n", name );
|
|
+ status = VCOS_ENOENT;
|
|
+ }
|
|
+
|
|
+ vcos_mutex_unlock(&lock);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Prints out the current settings for a given category (or all cvategories)
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+VCOS_STATUS_T vcos_log_status_cmd( VCOS_CMD_PARAM_T *param )
|
|
+{
|
|
+ VCOS_LOG_CAT_T *cat;
|
|
+ VCOS_STATUS_T status;
|
|
+
|
|
+ vcos_mutex_lock(&lock);
|
|
+
|
|
+ if ( param->argc == 1)
|
|
+ {
|
|
+ int nw;
|
|
+ int nameWidth = 0;
|
|
+
|
|
+ /* Print information about all of the categories. */
|
|
+
|
|
+ for ( cat = vcos_logging_categories; cat != NULL; cat = cat->next )
|
|
+ {
|
|
+ nw = (int)strlen( cat->name );
|
|
+
|
|
+ if ( nw > nameWidth )
|
|
+ {
|
|
+ nameWidth = nw;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for ( cat = vcos_logging_categories; cat != NULL; cat = cat->next )
|
|
+ {
|
|
+ vcos_cmd_printf( param, "%-*s - %s\n", nameWidth, cat->name, vcos_log_level_to_string( cat->level ));
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* Print information about a particular category */
|
|
+
|
|
+ for ( cat = vcos_logging_categories; cat != NULL; cat = cat->next )
|
|
+ {
|
|
+ if ( vcos_strcmp( cat->name, param->argv[1] ) == 0 )
|
|
+ {
|
|
+ vcos_cmd_printf( param, "%s - %s\n", cat->name, vcos_log_level_to_string( cat->level ));
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if ( cat == NULL )
|
|
+ {
|
|
+ vcos_cmd_printf( param, "Unrecognized logging category: '%s'\n", param->argv[1] );
|
|
+ status = VCOS_ENOENT;
|
|
+ goto out;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ status = VCOS_SUCCESS;
|
|
+out:
|
|
+ vcos_mutex_unlock(&lock);
|
|
+
|
|
+ return status;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Prints out the current settings for a given category (or all cvategories)
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+VCOS_STATUS_T vcos_log_test_cmd( VCOS_CMD_PARAM_T *param )
|
|
+{
|
|
+ if ( param->argc == 1 )
|
|
+ {
|
|
+ static int seq_num = 100;
|
|
+
|
|
+ /* No additional arguments - generate a message with an incrementing number */
|
|
+
|
|
+ vcos_log_error( "Test message %d", seq_num );
|
|
+
|
|
+ seq_num++;
|
|
+ vcos_cmd_printf( param, "Logged 'Test message %d'\n", seq_num );
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ int arg_idx;
|
|
+
|
|
+ /* Arguments supplied - log these */
|
|
+
|
|
+ for ( arg_idx = 0; arg_idx < param->argc; arg_idx++ )
|
|
+ {
|
|
+ vcos_log_error( "argv[%d] = '%s'", arg_idx, param->argv[arg_idx] );
|
|
+ }
|
|
+ vcos_cmd_printf( param, "Logged %d line(s) of test data\n", param->argc );
|
|
+ }
|
|
+ return VCOS_SUCCESS;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Internal commands
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+static VCOS_CMD_T log_cmd_entry[] =
|
|
+{
|
|
+ { "assert", "", vcos_log_assert_cmd, NULL, "Does a vcos_assert(0) to test logging" },
|
|
+ { "set", "category level", vcos_log_set_cmd, NULL, "Sets the vcos logging level for a category" },
|
|
+ { "status", "[category]", vcos_log_status_cmd, NULL, "Prints the vcos log status for a (or all) categories" },
|
|
+ { "test", "[arbitrary text]", vcos_log_test_cmd, NULL, "Does a vcos_log to test logging" },
|
|
+
|
|
+ { NULL, NULL, NULL, NULL, NULL }
|
|
+};
|
|
+
|
|
+static VCOS_CMD_T cmd_log =
|
|
+ { "log", "command [args]", NULL, log_cmd_entry, "Commands related to vcos logging" };
|
|
+
|
|
+#endif
|
|
+
|
|
+void vcos_logging_init(void)
|
|
+{
|
|
+ if (inited)
|
|
+ {
|
|
+ /* FIXME: should print a warning or something here */
|
|
+ return;
|
|
+ }
|
|
+ vcos_mutex_create(&lock, "vcos_log");
|
|
+
|
|
+ vcos_log_platform_init();
|
|
+
|
|
+ vcos_log_register("default", &dflt_log_category);
|
|
+
|
|
+#if VCOS_WANT_LOG_CMD
|
|
+ vcos_cmd_register( &cmd_log );
|
|
+#endif
|
|
+
|
|
+ vcos_assert(!inited);
|
|
+ inited = 1;
|
|
+}
|
|
+
|
|
+/** Read an alphanumeric token, returning True if we succeeded.
|
|
+ */
|
|
+
|
|
+static int read_tok(char *tok, size_t toklen, const char **pstr, char sep)
|
|
+{
|
|
+ const char *str = *pstr;
|
|
+ size_t n = 0;
|
|
+ char ch;
|
|
+
|
|
+ /* skip past any whitespace */
|
|
+ while (str[0] && isspace((int)(str[0])))
|
|
+ str++;
|
|
+
|
|
+ while ((ch = *str) != '\0' &&
|
|
+ ch != sep &&
|
|
+ (isalnum((int)ch) || (ch == '_')) &&
|
|
+ n != toklen-1)
|
|
+ {
|
|
+ tok[n++] = ch;
|
|
+ str++;
|
|
+ }
|
|
+
|
|
+ /* did it work out? */
|
|
+ if (ch == '\0' || ch == sep)
|
|
+ {
|
|
+ if (ch) str++; /* move to next token if not at end */
|
|
+ /* yes */
|
|
+ tok[n] = '\0';
|
|
+ *pstr = str;
|
|
+ return 1;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* no */
|
|
+ return 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+const char *vcos_log_level_to_string( VCOS_LOG_LEVEL_T level )
|
|
+{
|
|
+ switch (level)
|
|
+ {
|
|
+ case VCOS_LOG_UNINITIALIZED: return "uninit";
|
|
+ case VCOS_LOG_NEVER: return "never";
|
|
+ case VCOS_LOG_ERROR: return "error";
|
|
+ case VCOS_LOG_WARN: return "warn";
|
|
+ case VCOS_LOG_INFO: return "info";
|
|
+ case VCOS_LOG_TRACE: return "trace";
|
|
+ }
|
|
+ return "???";
|
|
+}
|
|
+
|
|
+VCOS_STATUS_T vcos_string_to_log_level( const char *str, VCOS_LOG_LEVEL_T *level )
|
|
+{
|
|
+ if (strcmp(str,"error") == 0)
|
|
+ *level = VCOS_LOG_ERROR;
|
|
+ else if (strcmp(str,"never") == 0)
|
|
+ *level = VCOS_LOG_NEVER;
|
|
+ else if (strcmp(str,"warn") == 0)
|
|
+ *level = VCOS_LOG_WARN;
|
|
+ else if (strcmp(str,"warning") == 0)
|
|
+ *level = VCOS_LOG_WARN;
|
|
+ else if (strcmp(str,"info") == 0)
|
|
+ *level = VCOS_LOG_INFO;
|
|
+ else if (strcmp(str,"trace") == 0)
|
|
+ *level = VCOS_LOG_TRACE;
|
|
+ else
|
|
+ return VCOS_EINVAL;
|
|
+
|
|
+ return VCOS_SUCCESS;
|
|
+}
|
|
+
|
|
+static int read_level(VCOS_LOG_LEVEL_T *level, const char **pstr, char sep)
|
|
+{
|
|
+ char buf[16];
|
|
+ int ret = 1;
|
|
+ if (read_tok(buf,sizeof(buf),pstr,sep))
|
|
+ {
|
|
+ if (vcos_string_to_log_level(buf,level) != VCOS_SUCCESS)
|
|
+ {
|
|
+ vcos_log("Invalid trace level '%s'\n", buf);
|
|
+ ret = 0;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ ret = 0;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void vcos_log_register(const char *name, VCOS_LOG_CAT_T *category)
|
|
+{
|
|
+ const char *env;
|
|
+ VCOS_LOG_CAT_T *i;
|
|
+
|
|
+ category->name = name;
|
|
+ if ( category->level == VCOS_LOG_UNINITIALIZED )
|
|
+ {
|
|
+ category->level = VCOS_LOG_ERROR;
|
|
+ }
|
|
+ category->flags.want_prefix = (category != &dflt_log_category );
|
|
+
|
|
+ vcos_mutex_lock(&lock);
|
|
+
|
|
+ /* is it already registered? */
|
|
+ for (i = vcos_logging_categories; i ; i = i->next )
|
|
+ {
|
|
+ if (i == category)
|
|
+ {
|
|
+ i->refcount++;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!i)
|
|
+ {
|
|
+ /* not yet registered */
|
|
+ category->next = vcos_logging_categories;
|
|
+ vcos_logging_categories = category;
|
|
+ category->refcount++;
|
|
+
|
|
+ vcos_log_platform_register(category);
|
|
+ }
|
|
+
|
|
+ vcos_mutex_unlock(&lock);
|
|
+
|
|
+ /* Check to see if this log level has been enabled. Look for
|
|
+ * (<category:level>,)*
|
|
+ *
|
|
+ * VC_LOGLEVEL=ilcs:info,vchiq:warn
|
|
+ */
|
|
+
|
|
+ env = _VCOS_LOG_LEVEL();
|
|
+ if (env)
|
|
+ {
|
|
+ do
|
|
+ {
|
|
+ char env_name[64];
|
|
+ VCOS_LOG_LEVEL_T level;
|
|
+ if (read_tok(env_name, sizeof(env_name), &env, ':') &&
|
|
+ read_level(&level, &env, ','))
|
|
+ {
|
|
+ if (strcmp(env_name, name) == 0)
|
|
+ {
|
|
+ category->level = level;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if (!warned_loglevel)
|
|
+ {
|
|
+ vcos_log("VC_LOGLEVEL format invalid at %s\n", env);
|
|
+ warned_loglevel = 1;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ } while (env[0] != '\0');
|
|
+ }
|
|
+
|
|
+ vcos_log_info( "Registered log category '%s' with level %s",
|
|
+ category->name,
|
|
+ vcos_log_level_to_string( category->level ));
|
|
+}
|
|
+
|
|
+void vcos_log_unregister(VCOS_LOG_CAT_T *category)
|
|
+{
|
|
+ VCOS_LOG_CAT_T **pcat;
|
|
+ vcos_mutex_lock(&lock);
|
|
+ category->refcount--;
|
|
+ if (category->refcount == 0)
|
|
+ {
|
|
+ pcat = &vcos_logging_categories;
|
|
+ while (*pcat != category)
|
|
+ {
|
|
+ if (!*pcat)
|
|
+ break; /* possibly deregistered twice? */
|
|
+ if ((*pcat)->next == NULL)
|
|
+ {
|
|
+ vcos_assert(0); /* already removed! */
|
|
+ vcos_mutex_unlock(&lock);
|
|
+ return;
|
|
+ }
|
|
+ pcat = &(*pcat)->next;
|
|
+ }
|
|
+ if (*pcat)
|
|
+ *pcat = category->next;
|
|
+
|
|
+ vcos_log_platform_unregister(category);
|
|
+ }
|
|
+ vcos_mutex_unlock(&lock);
|
|
+}
|
|
+
|
|
+VCOSPRE_ const VCOS_LOG_CAT_T * VCOSPOST_ vcos_log_get_default_category(void)
|
|
+{
|
|
+ return &dflt_log_category;
|
|
+}
|
|
+
|
|
+void vcos_set_log_options(const char *opt)
|
|
+{
|
|
+ (void)opt;
|
|
+}
|
|
+
|
|
+void vcos_log_dump_mem_impl( const VCOS_LOG_CAT_T *cat,
|
|
+ const char *label,
|
|
+ uint32_t addr,
|
|
+ const void *voidMem,
|
|
+ size_t numBytes )
|
|
+{
|
|
+ const uint8_t *mem = (const uint8_t *)voidMem;
|
|
+ size_t offset;
|
|
+ char lineBuf[ 100 ];
|
|
+ char *s;
|
|
+
|
|
+ while ( numBytes > 0 )
|
|
+ {
|
|
+ s = lineBuf;
|
|
+
|
|
+ for ( offset = 0; offset < 16; offset++ )
|
|
+ {
|
|
+ if ( offset < numBytes )
|
|
+ {
|
|
+ s += vcos_snprintf( s, 4, "%02x ", mem[ offset ]);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ s += vcos_snprintf( s, 4, " " );
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for ( offset = 0; offset < 16; offset++ )
|
|
+ {
|
|
+ if ( offset < numBytes )
|
|
+ {
|
|
+ uint8_t ch = mem[ offset ];
|
|
+
|
|
+ if (( ch < ' ' ) || ( ch > '~' ))
|
|
+ {
|
|
+ ch = '.';
|
|
+ }
|
|
+ *s++ = (char)ch;
|
|
+ }
|
|
+ }
|
|
+ *s++ = '\0';
|
|
+
|
|
+ if (( label != NULL ) && ( *label != '\0' ))
|
|
+ {
|
|
+ vcos_log_impl( cat, VCOS_LOG_INFO, "%s: %08x: %s", label, addr, lineBuf );
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ vcos_log_impl( cat, VCOS_LOG_INFO, "%08x: %s", addr, lineBuf );
|
|
+ }
|
|
+
|
|
+ addr += 16;
|
|
+ mem += 16;
|
|
+ if ( numBytes > 16 )
|
|
+ {
|
|
+ numBytes -= 16;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ numBytes = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+void vcos_log_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, ...)
|
|
+{
|
|
+ va_list ap;
|
|
+ va_start(ap,fmt);
|
|
+ vcos_vlog_impl( cat, _level, fmt, ap );
|
|
+ va_end(ap);
|
|
+}
|
|
+
|
|
+void vcos_vlog_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, va_list args)
|
|
+{
|
|
+ vcos_vlog_impl_func( cat, _level, fmt, args );
|
|
+}
|
|
+
|
|
+void vcos_set_vlog_impl( VCOS_VLOG_IMPL_FUNC_T vlog_impl_func )
|
|
+{
|
|
+ if ( vlog_impl_func == NULL )
|
|
+ {
|
|
+ vcos_vlog_impl_func = vcos_vlog_default_impl;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ vcos_vlog_impl_func = vlog_impl_func;
|
|
+ }
|
|
+}
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_mem_from_malloc.c
|
|
@@ -0,0 +1,73 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : vcos
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - memory alloc implementation
|
|
+=============================================================================*/
|
|
+
|
|
+#include "interface/vcos/vcos.h"
|
|
+
|
|
+#ifndef _vcos_platform_malloc
|
|
+#include <stdlib.h>
|
|
+#define _vcos_platform_malloc malloc
|
|
+#define _vcos_platform_free free
|
|
+#endif
|
|
+
|
|
+typedef struct malloc_header_s {
|
|
+ uint32_t guardword;
|
|
+ uint32_t size;
|
|
+ const char *description;
|
|
+ void *ptr;
|
|
+} MALLOC_HEADER_T;
|
|
+
|
|
+
|
|
+#define MIN_ALIGN sizeof(MALLOC_HEADER_T)
|
|
+
|
|
+#define GUARDWORDHEAP 0xa55a5aa5
|
|
+
|
|
+void *vcos_generic_mem_alloc_aligned(VCOS_UNSIGNED size, VCOS_UNSIGNED align, const char *desc)
|
|
+{
|
|
+ int local_align = align == 0 ? 1 : align;
|
|
+ int required_size = size + local_align + sizeof(MALLOC_HEADER_T);
|
|
+ void *ptr = _vcos_platform_malloc(required_size);
|
|
+ void *ret = (void *)VCOS_ALIGN_UP(((char *)ptr)+sizeof(MALLOC_HEADER_T), local_align);
|
|
+ MALLOC_HEADER_T *h = ((MALLOC_HEADER_T *)ret)-1;
|
|
+
|
|
+ h->size = size;
|
|
+ h->description = desc;
|
|
+ h->guardword = GUARDWORDHEAP;
|
|
+ h->ptr = ptr;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void *vcos_generic_mem_alloc(VCOS_UNSIGNED size, const char *desc)
|
|
+{
|
|
+ return vcos_generic_mem_alloc_aligned(size,MIN_ALIGN,desc);
|
|
+}
|
|
+
|
|
+void *vcos_generic_mem_calloc(VCOS_UNSIGNED count, VCOS_UNSIGNED sz, const char *desc)
|
|
+{
|
|
+ uint32_t size = count*sz;
|
|
+ void *ptr = vcos_generic_mem_alloc_aligned(size,MIN_ALIGN,desc);
|
|
+ if (ptr)
|
|
+ {
|
|
+ memset(ptr, 0, size);
|
|
+ }
|
|
+ return ptr;
|
|
+}
|
|
+
|
|
+void vcos_generic_mem_free(void *ptr)
|
|
+{
|
|
+ MALLOC_HEADER_T *h;
|
|
+ if (! ptr) return;
|
|
+
|
|
+ h = ((MALLOC_HEADER_T *)ptr)-1;
|
|
+ vcos_assert(h->guardword == GUARDWORDHEAP);
|
|
+ _vcos_platform_free(h->ptr);
|
|
+}
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_mem_from_malloc.h
|
|
@@ -0,0 +1,54 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : VMCS Host Apps
|
|
+Module : Framework - VMCS
|
|
+
|
|
+FILE DESCRIPTION
|
|
+Create the vcos_malloc API from the regular system malloc/free
|
|
+=============================================================================*/
|
|
+
|
|
+/**
|
|
+ * \file
|
|
+ *
|
|
+ * Create the vcos malloc API from a regular system malloc/free library.
|
|
+ *
|
|
+ * The API lets callers specify an alignment.
|
|
+ *
|
|
+ * Under VideoCore this is not needed, as we can simply use the rtos_malloc routines.
|
|
+ * But on host platforms that won't be the case.
|
|
+ *
|
|
+ */
|
|
+
|
|
+VCOSPRE_ void * VCOSPOST_ vcos_generic_mem_alloc(VCOS_UNSIGNED sz, const char *desc);
|
|
+VCOSPRE_ void * VCOSPOST_ vcos_generic_mem_calloc(VCOS_UNSIGNED count, VCOS_UNSIGNED sz, const char *descr);
|
|
+VCOSPRE_ void VCOSPOST_ vcos_generic_mem_free(void *ptr);
|
|
+VCOSPRE_ void * VCOSPOST_ vcos_generic_mem_alloc_aligned(VCOS_UNSIGNED sz, VCOS_UNSIGNED align, const char *desc);
|
|
+
|
|
+#ifdef VCOS_INLINE_BODIES
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void *vcos_malloc(VCOS_UNSIGNED size, const char *description) {
|
|
+ return vcos_generic_mem_alloc(size, description);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void *vcos_calloc(VCOS_UNSIGNED num, VCOS_UNSIGNED size, const char *description) {
|
|
+ return vcos_generic_mem_calloc(num, size, description);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_free(void *ptr) {
|
|
+ vcos_generic_mem_free(ptr);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void * vcos_malloc_aligned(VCOS_UNSIGNED size, VCOS_UNSIGNED align, const char *description) {
|
|
+ return vcos_generic_mem_alloc_aligned(size, align, description);
|
|
+}
|
|
+
|
|
+
|
|
+#endif /* VCOS_INLINE_BODIES */
|
|
+
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_mutexes_are_reentrant.h
|
|
@@ -0,0 +1,68 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - reentrant mutexes mapped directly to regular ones
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_GENERIC_REENTRANT_MUTEX_H
|
|
+#define VCOS_GENERIC_REENTRANT_MUTEX_H
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+#include "interface/vcos/vcos_mutex.h"
|
|
+
|
|
+/**
|
|
+ * \file
|
|
+ *
|
|
+ * Reentrant Mutexes directly using the native re-entrant mutex.
|
|
+ *
|
|
+ */
|
|
+
|
|
+typedef VCOS_MUTEX_T VCOS_REENTRANT_MUTEX_T;
|
|
+
|
|
+/* Inline forwarding functions */
|
|
+
|
|
+#if defined(VCOS_INLINE_BODIES)
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_reentrant_mutex_create(VCOS_REENTRANT_MUTEX_T *m, const char *name) {
|
|
+ return vcos_mutex_create(m,name);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_reentrant_mutex_delete(VCOS_REENTRANT_MUTEX_T *m) {
|
|
+ vcos_mutex_delete(m);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_reentrant_mutex_lock(VCOS_REENTRANT_MUTEX_T *m) {
|
|
+ vcos_mutex_lock(m);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_reentrant_mutex_unlock(VCOS_REENTRANT_MUTEX_T *m) {
|
|
+ vcos_mutex_unlock(m);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+int vcos_reentrant_mutex_is_locked(VCOS_REENTRANT_MUTEX_T *m) {
|
|
+ return vcos_mutex_is_locked(m);
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+#endif
|
|
+
|
|
+
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_thread_reaper.h
|
|
@@ -0,0 +1,35 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2010 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : vcos
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - thread reaping
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_THREAD_REAPER_H
|
|
+#define VCOS_THREAD_REAPER_H
|
|
+
|
|
+#define VCOS_HAVE_THREAD_REAPER
|
|
+
|
|
+/** Initialise the thread reaper.
|
|
+ */
|
|
+VCOS_STATUS_T vcos_thread_reaper_init(void);
|
|
+
|
|
+/** Reap a thread. Arranges for the thread to be automatically
|
|
+ * joined.
|
|
+ *
|
|
+ * @sa vcos_thread_join().
|
|
+ *
|
|
+ * @param thread the thread to terminate
|
|
+ * @param on_terminated called after the thread has exited
|
|
+ * @param cxt pass back to the callback
|
|
+ *
|
|
+ */
|
|
+void vcos_thread_reap(VCOS_THREAD_T *thread, void (*on_terminated)(void*), void *cxt);
|
|
+
|
|
+#endif
|
|
+
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/stdint.h
|
|
@@ -0,0 +1,17 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2010 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS fAbstraction Layer - stdint.h C standard header
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef _VCOS_PLATFORM_LINUX_STDINT_H
|
|
+#define _VCOS_PLATFORM_LINUX_STDINT_H
|
|
+
|
|
+/* The Linux kernel does not have a <stdint.h> so we have to provide one of
|
|
+ our own. */
|
|
+
|
|
+#include <linux/types.h> /* includes integer types */
|
|
+
|
|
+#endif /* _VCOS_PLATFORM_LINUX_STDINT_H */
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_linuxkernel.c
|
|
@@ -0,0 +1,616 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : vcos
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - pthreads types
|
|
+=============================================================================*/
|
|
+
|
|
+#define VCOS_INLINE_BODIES
|
|
+#include <linux/module.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/time.h>
|
|
+#include <linux/pid.h>
|
|
+#include <linux/mm.h>
|
|
+#include <linux/version.h>
|
|
+
|
|
+#if defined( CONFIG_BCM_KNLLOG_SUPPORT )
|
|
+#include <linux/broadcom/knllog.h>
|
|
+#endif
|
|
+#include "interface/vcos/vcos.h"
|
|
+#ifdef HAVE_VCOS_VERSION
|
|
+#include "interface/vcos/vcos_build_info.h"
|
|
+#endif
|
|
+
|
|
+VCOS_CFG_ENTRY_T vcos_cfg_dir;
|
|
+VCOS_CFG_ENTRY_T vcos_logging_cfg_dir;
|
|
+VCOS_CFG_ENTRY_T vcos_version_cfg;
|
|
+
|
|
+#ifndef VCOS_DEFAULT_STACK_SIZE
|
|
+#define VCOS_DEFAULT_STACK_SIZE 4096
|
|
+#endif
|
|
+
|
|
+static VCOS_THREAD_ATTR_T default_attrs = {
|
|
+ 0,
|
|
+ VCOS_DEFAULT_STACK_SIZE,
|
|
+};
|
|
+
|
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
|
|
+static DEFINE_SEMAPHORE(lock);
|
|
+#else
|
|
+static DECLARE_MUTEX(lock);
|
|
+#endif
|
|
+
|
|
+typedef void (*LEGACY_ENTRY_FN_T)(int, void *);
|
|
+
|
|
+/** Wrapper function around the real thread function. Posts the semaphore
|
|
+ * when completed.
|
|
+ */
|
|
+static int vcos_thread_wrapper(void *arg)
|
|
+{
|
|
+ void *ret;
|
|
+ VCOS_THREAD_T *thread = arg;
|
|
+
|
|
+ vcos_assert(thread->magic == VCOS_THREAD_MAGIC);
|
|
+
|
|
+ thread->thread.thread = current;
|
|
+
|
|
+ vcos_add_thread(thread);
|
|
+
|
|
+#ifdef VCOS_WANT_TLS_EMULATION
|
|
+ vcos_tls_thread_register(&thread->_tls);
|
|
+#endif
|
|
+
|
|
+ if (thread->legacy)
|
|
+ {
|
|
+ LEGACY_ENTRY_FN_T fn = (LEGACY_ENTRY_FN_T)thread->entry;
|
|
+ fn(0,thread->arg);
|
|
+ ret = 0;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ ret = thread->entry(thread->arg);
|
|
+ }
|
|
+
|
|
+ thread->exit_data = ret;
|
|
+
|
|
+ vcos_remove_thread(current);
|
|
+
|
|
+ /* For join and cleanup */
|
|
+ vcos_semaphore_post(&thread->wait);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+VCOS_STATUS_T vcos_thread_create(VCOS_THREAD_T *thread,
|
|
+ const char *name,
|
|
+ VCOS_THREAD_ATTR_T *attrs,
|
|
+ VCOS_THREAD_ENTRY_FN_T entry,
|
|
+ void *arg)
|
|
+{
|
|
+ VCOS_STATUS_T st;
|
|
+ struct task_struct *kthread;
|
|
+
|
|
+ memset(thread, 0, sizeof(*thread));
|
|
+ thread->magic = VCOS_THREAD_MAGIC;
|
|
+ strlcpy( thread->name, name, sizeof( thread->name ));
|
|
+ thread->legacy = attrs ? attrs->legacy : 0;
|
|
+ thread->entry = entry;
|
|
+ thread->arg = arg;
|
|
+
|
|
+ if (!name)
|
|
+ {
|
|
+ vcos_assert(0);
|
|
+ return VCOS_EINVAL;
|
|
+ }
|
|
+
|
|
+ st = vcos_semaphore_create(&thread->wait, NULL, 0);
|
|
+ if (st != VCOS_SUCCESS)
|
|
+ {
|
|
+ return st;
|
|
+ }
|
|
+
|
|
+ st = vcos_semaphore_create(&thread->suspend, NULL, 0);
|
|
+ if (st != VCOS_SUCCESS)
|
|
+ {
|
|
+ return st;
|
|
+ }
|
|
+
|
|
+ /*required for event groups */
|
|
+ vcos_timer_create(&thread->_timer.timer, thread->name, NULL, NULL);
|
|
+
|
|
+ kthread = kthread_create((int (*)(void *))vcos_thread_wrapper, (void*)thread, name);
|
|
+ vcos_assert(kthread != NULL);
|
|
+ set_user_nice(kthread, attrs->ta_priority);
|
|
+ thread->thread.thread = kthread;
|
|
+ wake_up_process(kthread);
|
|
+ return VCOS_SUCCESS;
|
|
+}
|
|
+
|
|
+void vcos_thread_join(VCOS_THREAD_T *thread,
|
|
+ void **pData)
|
|
+{
|
|
+ vcos_assert(thread);
|
|
+ vcos_assert(thread->magic == VCOS_THREAD_MAGIC);
|
|
+
|
|
+ thread->joined = 1;
|
|
+
|
|
+ vcos_semaphore_wait(&thread->wait);
|
|
+
|
|
+ if (pData)
|
|
+ {
|
|
+ *pData = thread->exit_data;
|
|
+ }
|
|
+
|
|
+ /* Clean up */
|
|
+ if (thread->stack)
|
|
+ vcos_free(thread->stack);
|
|
+
|
|
+ vcos_semaphore_delete(&thread->wait);
|
|
+ vcos_semaphore_delete(&thread->suspend);
|
|
+
|
|
+}
|
|
+
|
|
+uint32_t vcos_getmicrosecs( void )
|
|
+{
|
|
+ struct timeval tv;
|
|
+/*XXX FIX ME! switch to ktime_get_ts to use MONOTONIC clock */
|
|
+ do_gettimeofday(&tv);
|
|
+ return (tv.tv_sec*1000000) + tv.tv_usec;
|
|
+}
|
|
+
|
|
+VCOS_STATUS_T vcos_timer_init(void)
|
|
+{
|
|
+ return VCOS_SUCCESS;
|
|
+}
|
|
+
|
|
+static const char *log_prefix[] =
|
|
+{
|
|
+ "", /* VCOS_LOG_UNINITIALIZED */
|
|
+ "", /* VCOS_LOG_NEVER */
|
|
+ KERN_ERR, /* VCOS_LOG_ERROR */
|
|
+ KERN_WARNING, /* VCOS_LOG_WARN */
|
|
+ KERN_INFO, /* VCOS_LOG_INFO */
|
|
+ KERN_INFO /* VCOS_LOG_TRACE */
|
|
+};
|
|
+
|
|
+void vcos_vlog_default_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, va_list args)
|
|
+{
|
|
+ char *newline = strchr( fmt, '\n' );
|
|
+ const char *prefix;
|
|
+ const char *real_fmt;
|
|
+
|
|
+ preempt_disable();
|
|
+ {
|
|
+ if ( *fmt == '<' )
|
|
+ {
|
|
+ prefix = fmt;
|
|
+ real_fmt= &fmt[3];
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ prefix = log_prefix[_level];
|
|
+ real_fmt = fmt;
|
|
+ }
|
|
+#if defined( CONFIG_BCM_KNLLOG_SUPPORT )
|
|
+ knllog_ventry( "vcos", real_fmt, args );
|
|
+#endif
|
|
+ printk( "%.3svcos: [%d]: ", prefix, current->pid );
|
|
+ vprintk( real_fmt, args );
|
|
+
|
|
+ if ( newline == NULL )
|
|
+ {
|
|
+ printk("\n");
|
|
+ }
|
|
+ }
|
|
+ preempt_enable();
|
|
+}
|
|
+
|
|
+
|
|
+const char * _vcos_log_level(void)
|
|
+{
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Displays the version information in /proc/vcos/version
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+#ifdef HAVE_VCOS_VERSION
|
|
+
|
|
+static void show_version( VCOS_CFG_BUF_T buf, void *data )
|
|
+{
|
|
+ static const char* copyright = "Copyright (c) 2011 Broadcom";
|
|
+
|
|
+ vcos_cfg_buf_printf( buf, "Built %s %s on %s\n%s\nversion %s\n",
|
|
+ vcos_get_build_date(),
|
|
+ vcos_get_build_time(),
|
|
+ vcos_get_build_hostname(),
|
|
+ copyright,
|
|
+ vcos_get_build_version() );
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Initialises vcos
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+VCOS_STATUS_T vcos_init(void)
|
|
+{
|
|
+ if ( vcos_cfg_mkdir( &vcos_cfg_dir, NULL, "vcos" ) != VCOS_SUCCESS )
|
|
+ {
|
|
+ printk( KERN_ERR "%s: Unable to create vcos cfg entry\n", __func__ );
|
|
+ }
|
|
+ vcos_logging_init();
|
|
+
|
|
+#ifdef HAVE_VCOS_VERSION
|
|
+ if ( vcos_cfg_create_entry( &vcos_version_cfg, &vcos_cfg_dir, "version",
|
|
+ show_version, NULL, NULL ) != VCOS_SUCCESS )
|
|
+ {
|
|
+ printk( KERN_ERR "%s: Unable to create vcos cfg entry 'version'\n", __func__ );
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ return VCOS_SUCCESS;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Deinitializes vcos
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+void vcos_deinit(void)
|
|
+{
|
|
+#ifdef HAVE_VCOS_VERSION
|
|
+ vcos_cfg_remove_entry( &vcos_version_cfg );
|
|
+#endif
|
|
+ vcos_cfg_remove_entry( &vcos_cfg_dir );
|
|
+}
|
|
+
|
|
+void vcos_global_lock(void)
|
|
+{
|
|
+ down(&lock);
|
|
+}
|
|
+
|
|
+void vcos_global_unlock(void)
|
|
+{
|
|
+ up(&lock);
|
|
+}
|
|
+
|
|
+/* vcos_thread_exit() doesn't really stop this thread here
|
|
+ *
|
|
+ * At the moment, call to do_exit() will leak task_struct for
|
|
+ * current thread, so we let the vcos_thread_wrapper() do the
|
|
+ * cleanup and exit job, and we return w/o actually stopping the thread.
|
|
+ *
|
|
+ * ToDo: Kernel v2.6.31 onwards, it is considered safe to call do_exit()
|
|
+ * from kthread, the implementation of which is combined in 2 patches
|
|
+ * with commit-ids "63706172" and "cdd140bd" in oss Linux kernel tree
|
|
+ */
|
|
+
|
|
+void vcos_thread_exit(void *arg)
|
|
+{
|
|
+ VCOS_THREAD_T *thread = vcos_thread_current();
|
|
+
|
|
+ vcos_assert(thread);
|
|
+ vcos_assert(thread->magic == VCOS_THREAD_MAGIC);
|
|
+
|
|
+ thread->exit_data = arg;
|
|
+}
|
|
+
|
|
+void vcos_thread_attr_init(VCOS_THREAD_ATTR_T *attrs)
|
|
+{
|
|
+ *attrs = default_attrs;
|
|
+}
|
|
+
|
|
+void _vcos_task_timer_set(void (*pfn)(void *), void *cxt, VCOS_UNSIGNED ms)
|
|
+{
|
|
+ VCOS_THREAD_T *self = vcos_thread_current();
|
|
+ vcos_assert(self);
|
|
+ vcos_assert(self->_timer.pfn == NULL);
|
|
+
|
|
+ vcos_timer_create( &self->_timer.timer, "TaskTimer", pfn, cxt );
|
|
+ vcos_timer_set(&self->_timer.timer, ms);
|
|
+}
|
|
+
|
|
+void _vcos_task_timer_cancel(void)
|
|
+{
|
|
+ VCOS_THREAD_T *self = vcos_thread_current();
|
|
+ if (self->_timer.timer.linux_timer.function)
|
|
+ {
|
|
+ vcos_timer_cancel(&self->_timer.timer);
|
|
+ vcos_timer_delete(&self->_timer.timer);
|
|
+ }
|
|
+}
|
|
+
|
|
+int vcos_vsnprintf( char *buf, size_t buflen, const char *fmt, va_list ap )
|
|
+{
|
|
+ return vsnprintf( buf, buflen, fmt, ap );
|
|
+}
|
|
+
|
|
+int vcos_snprintf(char *buf, size_t buflen, const char *fmt, ...)
|
|
+{
|
|
+ int ret;
|
|
+ va_list ap;
|
|
+ va_start(ap,fmt);
|
|
+ ret = vsnprintf(buf, buflen, fmt, ap);
|
|
+ va_end(ap);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int vcos_llthread_running(VCOS_LLTHREAD_T *t) {
|
|
+ vcos_assert(0); /* this function only exists as a nasty hack for the video codecs! */
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int vcos_verify_bkpts = 1;
|
|
+
|
|
+int vcos_verify_bkpts_enabled(void)
|
|
+{
|
|
+ return vcos_verify_bkpts;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* _vcos_log_platform_init is called from vcos_logging_init
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+void _vcos_log_platform_init(void)
|
|
+{
|
|
+ if ( vcos_cfg_mkdir( &vcos_logging_cfg_dir, &vcos_cfg_dir, "logging" ) != VCOS_SUCCESS )
|
|
+ {
|
|
+ printk( KERN_ERR "%s: Unable to create logging cfg entry\n", __func__ );
|
|
+ }
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Called to display the contents of a logging category.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+static void logging_show_category( VCOS_CFG_BUF_T buf, void *data )
|
|
+{
|
|
+ VCOS_LOG_CAT_T *category = data;
|
|
+
|
|
+ vcos_cfg_buf_printf( buf, "%s\n", vcos_log_level_to_string( category->level ));
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Called to parse content for a logging category.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+static void logging_parse_category( VCOS_CFG_BUF_T buf, void *data )
|
|
+{
|
|
+ VCOS_LOG_CAT_T *category = data;
|
|
+ const char *str = vcos_cfg_buf_get_str( buf );
|
|
+ VCOS_LOG_LEVEL_T level;
|
|
+
|
|
+ if ( vcos_string_to_log_level( str, &level ) == VCOS_SUCCESS )
|
|
+ {
|
|
+ category->level = level;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ printk( KERN_ERR "%s: Unrecognized logging level: '%s'\n",
|
|
+ __func__, str );
|
|
+ }
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* _vcos_log_platform_register is called from vcos_log_register whenever
|
|
+* a new category is registered.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+void _vcos_log_platform_register(VCOS_LOG_CAT_T *category)
|
|
+{
|
|
+ VCOS_CFG_ENTRY_T entry;
|
|
+
|
|
+ if ( vcos_cfg_create_entry( &entry, &vcos_logging_cfg_dir, category->name,
|
|
+ logging_show_category, logging_parse_category,
|
|
+ category ) != VCOS_SUCCESS )
|
|
+ {
|
|
+ printk( KERN_ERR "%s: Unable to create cfg entry for logging category '%s'\n",
|
|
+ __func__, category->name );
|
|
+ category->platform_data = NULL;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ category->platform_data = entry;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* _vcos_log_platform_unregister is called from vcos_log_unregister whenever
|
|
+* a new category is unregistered.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+void _vcos_log_platform_unregister(VCOS_LOG_CAT_T *category)
|
|
+{
|
|
+ VCOS_CFG_ENTRY_T entry;
|
|
+
|
|
+ entry = category->platform_data;
|
|
+ if ( entry != NULL )
|
|
+ {
|
|
+ if ( vcos_cfg_remove_entry( &entry ) != VCOS_SUCCESS )
|
|
+ {
|
|
+ printk( KERN_ERR "%s: Unable to remove cfg entry for logging category '%s'\n",
|
|
+ __func__, category->name );
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Allocate memory.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+void *vcos_platform_malloc( VCOS_UNSIGNED required_size )
|
|
+{
|
|
+ if ( required_size >= ( 2 * PAGE_SIZE ))
|
|
+ {
|
|
+ /* For larger allocations, use vmalloc, whose underlying allocator
|
|
+ * returns pages
|
|
+ */
|
|
+
|
|
+ return vmalloc( required_size );
|
|
+ }
|
|
+
|
|
+ /* For smaller allocation, use kmalloc */
|
|
+
|
|
+ return kmalloc( required_size, GFP_KERNEL );
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Free previously allocated memory
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+void vcos_platform_free( void *ptr )
|
|
+{
|
|
+ if (((unsigned long)ptr >= VMALLOC_START )
|
|
+ && ((unsigned long)ptr < VMALLOC_END ))
|
|
+ {
|
|
+ vfree( ptr );
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ kfree( ptr );
|
|
+ }
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* Execute a routine exactly once.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+VCOS_STATUS_T vcos_once(VCOS_ONCE_T *once_control,
|
|
+ void (*init_routine)(void))
|
|
+{
|
|
+ /* In order to be thread-safe we need to re-test *once_control
|
|
+ * inside the lock. The outer test is basically an optimization
|
|
+ * so that once it is initialized we don't need to waste time
|
|
+ * trying to acquire the lock.
|
|
+ */
|
|
+
|
|
+ if ( *once_control == 0 )
|
|
+ {
|
|
+ vcos_global_lock();
|
|
+ if ( *once_control == 0 )
|
|
+ {
|
|
+ init_routine();
|
|
+ *once_control = 1;
|
|
+ }
|
|
+ vcos_global_unlock();
|
|
+ }
|
|
+
|
|
+ return VCOS_SUCCESS;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* String duplication routine.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+char *vcos_strdup(const char *str)
|
|
+{
|
|
+ return kstrdup(str, GFP_KERNEL);
|
|
+}
|
|
+
|
|
+
|
|
+/* Export functions for modules to use */
|
|
+EXPORT_SYMBOL( vcos_init );
|
|
+
|
|
+EXPORT_SYMBOL( vcos_semaphore_trywait );
|
|
+EXPORT_SYMBOL( vcos_semaphore_post );
|
|
+EXPORT_SYMBOL( vcos_semaphore_create );
|
|
+EXPORT_SYMBOL( vcos_semaphore_wait );
|
|
+EXPORT_SYMBOL( vcos_semaphore_delete );
|
|
+
|
|
+EXPORT_SYMBOL( vcos_log_impl );
|
|
+EXPORT_SYMBOL( vcos_vlog_impl );
|
|
+EXPORT_SYMBOL( vcos_vlog_default_impl );
|
|
+EXPORT_SYMBOL( vcos_log_get_default_category );
|
|
+EXPORT_SYMBOL( vcos_log_register );
|
|
+EXPORT_SYMBOL( vcos_log_unregister );
|
|
+EXPORT_SYMBOL( vcos_logging_init );
|
|
+EXPORT_SYMBOL( vcos_log_level_to_string );
|
|
+EXPORT_SYMBOL( vcos_string_to_log_level );
|
|
+EXPORT_SYMBOL( vcos_log_dump_mem_impl );
|
|
+
|
|
+EXPORT_SYMBOL( vcos_event_create );
|
|
+EXPORT_SYMBOL( vcos_event_delete );
|
|
+EXPORT_SYMBOL( vcos_event_flags_set );
|
|
+EXPORT_SYMBOL( vcos_event_signal );
|
|
+EXPORT_SYMBOL( vcos_event_wait );
|
|
+EXPORT_SYMBOL( vcos_event_try );
|
|
+
|
|
+EXPORT_SYMBOL( vcos_getmicrosecs );
|
|
+
|
|
+EXPORT_SYMBOL( vcos_strcasecmp );
|
|
+EXPORT_SYMBOL( vcos_snprintf );
|
|
+EXPORT_SYMBOL( vcos_vsnprintf );
|
|
+
|
|
+EXPORT_SYMBOL( vcos_thread_current );
|
|
+EXPORT_SYMBOL( vcos_thread_join );
|
|
+EXPORT_SYMBOL( vcos_thread_create );
|
|
+EXPORT_SYMBOL( vcos_thread_set_priority );
|
|
+EXPORT_SYMBOL( vcos_thread_exit );
|
|
+EXPORT_SYMBOL( vcos_once );
|
|
+
|
|
+EXPORT_SYMBOL( vcos_thread_attr_init );
|
|
+EXPORT_SYMBOL( vcos_thread_attr_setpriority );
|
|
+EXPORT_SYMBOL( vcos_thread_attr_settimeslice );
|
|
+EXPORT_SYMBOL( vcos_thread_attr_setstacksize );
|
|
+EXPORT_SYMBOL( _vcos_thread_attr_setlegacyapi );
|
|
+
|
|
+EXPORT_SYMBOL( vcos_event_flags_create );
|
|
+EXPORT_SYMBOL( vcos_event_flags_delete );
|
|
+EXPORT_SYMBOL( vcos_event_flags_get );
|
|
+
|
|
+EXPORT_SYMBOL( vcos_sleep );
|
|
+
|
|
+EXPORT_SYMBOL( vcos_calloc );
|
|
+EXPORT_SYMBOL( vcos_malloc );
|
|
+EXPORT_SYMBOL( vcos_malloc_aligned );
|
|
+EXPORT_SYMBOL( vcos_free );
|
|
+
|
|
+EXPORT_SYMBOL( vcos_mutex_create );
|
|
+EXPORT_SYMBOL( vcos_mutex_delete );
|
|
+EXPORT_SYMBOL( vcos_mutex_lock );
|
|
+EXPORT_SYMBOL( vcos_mutex_unlock );
|
|
+EXPORT_SYMBOL( vcos_mutex_trylock );
|
|
+
|
|
+EXPORT_SYMBOL( vcos_timer_cancel );
|
|
+EXPORT_SYMBOL( vcos_timer_create );
|
|
+EXPORT_SYMBOL( vcos_timer_delete );
|
|
+EXPORT_SYMBOL( vcos_timer_set );
|
|
+
|
|
+EXPORT_SYMBOL( vcos_atomic_flags_create );
|
|
+EXPORT_SYMBOL( vcos_atomic_flags_delete );
|
|
+EXPORT_SYMBOL( vcos_atomic_flags_or );
|
|
+EXPORT_SYMBOL( vcos_atomic_flags_get_and_clear );
|
|
+
|
|
+EXPORT_SYMBOL( vcos_verify_bkpts_enabled );
|
|
+
|
|
+EXPORT_SYMBOL( vcos_strdup );
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_linuxkernel_cfg.c
|
|
@@ -0,0 +1,332 @@
|
|
+/*****************************************************************************
|
|
+* Copyright 2009 - 2010 Broadcom Corporation. All rights reserved.
|
|
+*
|
|
+* Unless you and Broadcom execute a separate written software license
|
|
+* agreement governing use of this software, this software is licensed to you
|
|
+* under the terms of the GNU General Public License version 2, available at
|
|
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
|
+*
|
|
+* Notwithstanding the above, under no circumstances may you combine this
|
|
+* software in any way with any other Broadcom software provided under a
|
|
+* license other than the GPL, without Broadcom's express prior written
|
|
+* consent.
|
|
+*****************************************************************************/
|
|
+
|
|
+#include "interface/vcos/vcos.h"
|
|
+#include <linux/module.h>
|
|
+#include <linux/proc_fs.h>
|
|
+#include <linux/seq_file.h>
|
|
+#include <asm/uaccess.h>
|
|
+
|
|
+struct opaque_vcos_cfg_buf_t
|
|
+{
|
|
+ struct seq_file *seq;
|
|
+ char *charBuf;
|
|
+};
|
|
+
|
|
+struct opaque_vcos_cfg_entry_t
|
|
+{
|
|
+ struct proc_dir_entry *pde;
|
|
+ struct proc_dir_entry *parent_pde;
|
|
+ VCOS_CFG_SHOW_FPTR showFunc;
|
|
+ VCOS_CFG_PARSE_FPTR parseFunc;
|
|
+ void *data;
|
|
+ const char *name;
|
|
+};
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* cfg_proc_show
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+static int cfg_proc_show( struct seq_file *s, void *v )
|
|
+{
|
|
+ VCOS_CFG_ENTRY_T entry;
|
|
+ struct opaque_vcos_cfg_buf_t buf;
|
|
+
|
|
+ entry = s->private;
|
|
+
|
|
+ if ( entry->showFunc )
|
|
+ {
|
|
+ memset( &buf, 0, sizeof( buf ));
|
|
+ buf.seq = s;
|
|
+
|
|
+ entry->showFunc( &buf, entry->data );
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* cfg_proc_write
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+static ssize_t cfg_proc_write( struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
|
|
+{
|
|
+ VCOS_CFG_ENTRY_T entry = PDE(file->f_path.dentry->d_inode)->data;
|
|
+ char *charBuf;
|
|
+ struct opaque_vcos_cfg_buf_t buf;
|
|
+ size_t len;
|
|
+
|
|
+ if ( entry->parseFunc != NULL )
|
|
+ {
|
|
+ /* The number 4000 is rather arbitrary. It just needs to be bigger than any input
|
|
+ * string we expect to use.
|
|
+ */
|
|
+
|
|
+ len = count;
|
|
+ if ( count > 4000 )
|
|
+ {
|
|
+ len = 4000;
|
|
+ }
|
|
+
|
|
+ /* Allocate a kernel buffer to contain the string being written. */
|
|
+
|
|
+ charBuf = kmalloc( len + 1, GFP_KERNEL );
|
|
+ if ( copy_from_user( charBuf, buffer, len ))
|
|
+ {
|
|
+ kfree( charBuf );
|
|
+ return -EFAULT;
|
|
+ }
|
|
+
|
|
+ /* echo puts a trailing newline in the buffer - strip it out. */
|
|
+
|
|
+ if (( len > 0 ) && ( charBuf[ len - 1 ] == '\n' ))
|
|
+ {
|
|
+ len--;
|
|
+ }
|
|
+ charBuf[len] = '\0';
|
|
+
|
|
+ memset( &buf, 0, sizeof( buf ));
|
|
+ buf.charBuf = charBuf;
|
|
+
|
|
+ entry->parseFunc( &buf, entry->data );
|
|
+ kfree( charBuf );
|
|
+ }
|
|
+ return count;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* cfg_proc_open
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+static int cfg_proc_open( struct inode *inode, struct file *file )
|
|
+{
|
|
+ return single_open( file, cfg_proc_show, PDE(inode)->data );
|
|
+}
|
|
+
|
|
+static const struct file_operations cfg_proc_fops =
|
|
+{
|
|
+ .open = cfg_proc_open,
|
|
+ .read = seq_read,
|
|
+ .llseek = seq_lseek,
|
|
+ .release = single_release,
|
|
+ .write = cfg_proc_write,
|
|
+};
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* vcos_cfg_mkdir
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+VCOS_STATUS_T vcos_cfg_mkdir( VCOS_CFG_ENTRY_T *entryp,
|
|
+ VCOS_CFG_ENTRY_T *parent,
|
|
+ const char *dirName )
|
|
+{
|
|
+ VCOS_CFG_ENTRY_T entry;
|
|
+
|
|
+ if (( entry = kzalloc( sizeof( *entry ), GFP_KERNEL )) == NULL )
|
|
+ {
|
|
+ return VCOS_ENOMEM;
|
|
+ }
|
|
+
|
|
+ if ( parent == NULL )
|
|
+ {
|
|
+ entry->pde = proc_mkdir( dirName, NULL );
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ entry->pde = proc_mkdir( dirName, (*parent)->pde );
|
|
+ entry->parent_pde = (*parent)->pde;
|
|
+ }
|
|
+ if ( entry->pde == NULL )
|
|
+ {
|
|
+ kfree( entry );
|
|
+ return VCOS_ENOMEM;
|
|
+ }
|
|
+
|
|
+ entry->name = dirName;
|
|
+
|
|
+ *entryp = entry;
|
|
+ return VCOS_SUCCESS;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* vcos_cfg_create_entry
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+VCOS_STATUS_T vcos_cfg_create_entry( VCOS_CFG_ENTRY_T *entryp,
|
|
+ VCOS_CFG_ENTRY_T *parent,
|
|
+ const char *entryName,
|
|
+ VCOS_CFG_SHOW_FPTR showFunc,
|
|
+ VCOS_CFG_PARSE_FPTR parseFunc,
|
|
+ void *data )
|
|
+{
|
|
+ VCOS_CFG_ENTRY_T entry;
|
|
+ mode_t mode;
|
|
+
|
|
+ *entryp = NULL;
|
|
+
|
|
+ if (( entry = kzalloc( sizeof( *entry ), GFP_KERNEL )) == NULL )
|
|
+ {
|
|
+ return VCOS_ENOMEM;
|
|
+ }
|
|
+
|
|
+ mode = 0;
|
|
+ if ( showFunc != NULL )
|
|
+ {
|
|
+ mode |= 0444;
|
|
+ }
|
|
+ if ( parseFunc != NULL )
|
|
+ {
|
|
+ mode |= 0200;
|
|
+ }
|
|
+
|
|
+ if ( parent == NULL )
|
|
+ {
|
|
+ entry->pde = create_proc_entry( entryName, mode, NULL );
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ entry->pde = create_proc_entry( entryName, mode, (*parent)->pde );
|
|
+ entry->parent_pde = (*parent)->pde;
|
|
+ }
|
|
+ if ( entry->pde == NULL )
|
|
+ {
|
|
+ kfree( entry );
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ entry->showFunc = showFunc;
|
|
+ entry->parseFunc = parseFunc;
|
|
+ entry->data = data;
|
|
+ entry->name = entryName;
|
|
+
|
|
+ entry->pde->data = entry;
|
|
+ entry->pde->proc_fops = &cfg_proc_fops;
|
|
+
|
|
+ *entryp = entry;
|
|
+ return VCOS_SUCCESS;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* vcos_cfg_remove_entry
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+VCOS_STATUS_T vcos_cfg_remove_entry( VCOS_CFG_ENTRY_T *entryp )
|
|
+{
|
|
+ if (( entryp != NULL ) && ( *entryp != NULL ))
|
|
+ {
|
|
+ remove_proc_entry( (*entryp)->name, (*entryp)->parent_pde );
|
|
+
|
|
+ kfree( *entryp );
|
|
+ *entryp = NULL;
|
|
+ }
|
|
+
|
|
+ return VCOS_SUCCESS;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* vcos_cfg_is_entry_created
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+int vcos_cfg_is_entry_created( VCOS_CFG_ENTRY_T entry )
|
|
+{
|
|
+ return ( entry != NULL ) && ( entry->pde != NULL );
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* vcos_cfg_buf_printf
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+void vcos_cfg_buf_printf( VCOS_CFG_BUF_T buf, const char *fmt, ... )
|
|
+{
|
|
+ struct seq_file *m = buf->seq;
|
|
+
|
|
+ /* Bah - there is no seq_vprintf */
|
|
+
|
|
+ va_list args;
|
|
+ int len;
|
|
+
|
|
+ if (m->count < m->size) {
|
|
+ va_start(args, fmt);
|
|
+ len = vsnprintf(m->buf + m->count, m->size - m->count, fmt, args);
|
|
+ va_end(args);
|
|
+ if (m->count + len < m->size) {
|
|
+ m->count += len;
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ m->count = m->size;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* vcos_cfg_buf_get_str
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+char *vcos_cfg_buf_get_str( VCOS_CFG_BUF_T buf )
|
|
+{
|
|
+ return buf->charBuf;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* vcos_cfg_get_proc_entry
|
|
+*
|
|
+* This function is only created for a couple of backwards compatibility '
|
|
+* issues and shouldn't normally be used.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+void *vcos_cfg_get_proc_entry( VCOS_CFG_ENTRY_T entry )
|
|
+{
|
|
+ return entry->pde;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* vcos_cfg_get_entry_name
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+const char *vcos_cfg_get_entry_name( VCOS_CFG_ENTRY_T entry )
|
|
+{
|
|
+ return entry->pde->name;
|
|
+}
|
|
+
|
|
+
|
|
+EXPORT_SYMBOL( vcos_cfg_mkdir );
|
|
+EXPORT_SYMBOL( vcos_cfg_create_entry );
|
|
+EXPORT_SYMBOL( vcos_cfg_remove_entry );
|
|
+EXPORT_SYMBOL( vcos_cfg_get_entry_name );
|
|
+EXPORT_SYMBOL( vcos_cfg_is_entry_created );
|
|
+EXPORT_SYMBOL( vcos_cfg_buf_printf );
|
|
+EXPORT_SYMBOL( vcos_cfg_buf_get_str );
|
|
+
|
|
+EXPORT_SYMBOL_GPL( vcos_cfg_get_proc_entry );
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_linuxkernel_misc.c
|
|
@@ -0,0 +1,113 @@
|
|
+// #############################################################################
|
|
+// START #######################################################################
|
|
+/*****************************************************************************
|
|
+* Copyright 2009 - 2010 Broadcom Corporation. All rights reserved.
|
|
+*
|
|
+* Unless you and Broadcom execute a separate written software license
|
|
+* agreement governing use of this software, this software is licensed to you
|
|
+* under the terms of the GNU General Public License version 2, available at
|
|
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
|
+*
|
|
+* Notwithstanding the above, under no circumstances may you combine this
|
|
+* software in any way with any other Broadcom software provided under a
|
|
+* license other than the GPL, without Broadcom's express prior written
|
|
+* consent.
|
|
+*****************************************************************************/
|
|
+
|
|
+#include "interface/vcos/vcos.h"
|
|
+#include <linux/sched.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/freezer.h>
|
|
+#include <linux/string.h>
|
|
+#include <linux/slab.h>
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* vcos_semaphore_wait_freezable
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+VCOS_STATUS_T vcos_semaphore_wait_freezable(VCOS_SEMAPHORE_T *sem)
|
|
+{
|
|
+ int rval, sig_pended = 0;
|
|
+ unsigned long flags;
|
|
+ struct task_struct *task = current;
|
|
+
|
|
+ while (1) {
|
|
+ rval = down_interruptible((struct semaphore *)sem);
|
|
+ if (rval == 0) { /* down now */
|
|
+ break;
|
|
+ } else {
|
|
+ if (freezing(current)) {
|
|
+ try_to_freeze();
|
|
+ } else {
|
|
+ spin_lock_irqsave(&task->sighand->siglock, flags);
|
|
+ if (test_tsk_thread_flag(task, TIF_SIGPENDING)) {
|
|
+ clear_tsk_thread_flag(task, TIF_SIGPENDING);
|
|
+ sig_pended = 1;
|
|
+ }
|
|
+ spin_unlock_irqrestore(&task->sighand->siglock, flags);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (sig_pended) {
|
|
+ spin_lock_irqsave(&task->sighand->siglock, flags);
|
|
+ set_tsk_thread_flag(task, TIF_SIGPENDING);
|
|
+ spin_unlock_irqrestore(&task->sighand->siglock, flags);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+EXPORT_SYMBOL( vcos_semaphore_wait_freezable );
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* vcos_kmalloc
|
|
+*
|
|
+* We really need to convert malloc to do kmalloc or vmalloc based on the
|
|
+* size, but for now we'll add a separate function.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+void *vcos_kmalloc(VCOS_UNSIGNED size, const char *description)
|
|
+{
|
|
+ (void)description;
|
|
+
|
|
+ return kmalloc( size, GFP_KERNEL );
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* vcos_kmalloc
|
|
+*
|
|
+* We really need to convert malloc to do kmalloc or vmalloc based on the
|
|
+* size, but for now we'll add a separate function.
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+void *vcos_kcalloc(VCOS_UNSIGNED num, VCOS_UNSIGNED size, const char *description)
|
|
+{
|
|
+ (void)description;
|
|
+
|
|
+ return kzalloc( num * size, GFP_KERNEL );
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+*
|
|
+* vcos_kfree
|
|
+*
|
|
+*****************************************************************************/
|
|
+
|
|
+void vcos_kfree(void *ptr)
|
|
+{
|
|
+ kfree( ptr );
|
|
+}
|
|
+
|
|
+EXPORT_SYMBOL( vcos_kmalloc );
|
|
+EXPORT_SYMBOL( vcos_kcalloc );
|
|
+EXPORT_SYMBOL( vcos_kfree );
|
|
+
|
|
+// END #########################################################################
|
|
+// #############################################################################
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_mod_init.c
|
|
@@ -0,0 +1,64 @@
|
|
+/*****************************************************************************
|
|
+* Copyright 2006 - 2008 Broadcom Corporation. All rights reserved.
|
|
+*
|
|
+* Unless you and Broadcom execute a separate written software license
|
|
+* agreement governing use of this software, this software is licensed to you
|
|
+* under the terms of the GNU General Public License version 2, available at
|
|
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
|
+*
|
|
+* Notwithstanding the above, under no circumstances may you combine this
|
|
+* software in any way with any other Broadcom software provided under a
|
|
+* license other than the GPL, without Broadcom's express prior written
|
|
+* consent.
|
|
+****************************************************************************/
|
|
+
|
|
+/* ---- Include Files ---------------------------------------------------- */
|
|
+
|
|
+#include "interface/vcos/vcos.h"
|
|
+#include <linux/module.h>
|
|
+
|
|
+/* ---- Public Variables ------------------------------------------------- */
|
|
+
|
|
+/* ---- Private Constants and Types -------------------------------------- */
|
|
+
|
|
+/* ---- Private Variables ------------------------------------------------ */
|
|
+
|
|
+/* ---- Private Function Prototypes -------------------------------------- */
|
|
+
|
|
+/* ---- Functions -------------------------------------------------------- */
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* Called to perform module initialization when the module is loaded
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+static int __init vcos_mod_init( void )
|
|
+{
|
|
+ printk( KERN_INFO "VCOS Module\n" );
|
|
+
|
|
+ vcos_init();
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/****************************************************************************
|
|
+*
|
|
+* Called to perform module cleanup when the module is unloaded.
|
|
+*
|
|
+***************************************************************************/
|
|
+
|
|
+static void __exit vcos_mod_exit( void )
|
|
+{
|
|
+ vcos_deinit();
|
|
+}
|
|
+
|
|
+/****************************************************************************/
|
|
+
|
|
+module_init( vcos_mod_init );
|
|
+module_exit( vcos_mod_exit );
|
|
+
|
|
+MODULE_AUTHOR("Broadcom");
|
|
+MODULE_DESCRIPTION( "VCOS Module Functions" );
|
|
+MODULE_LICENSE( "GPL" );
|
|
+MODULE_VERSION( "1.0" );
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_platform.h
|
|
@@ -0,0 +1,496 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : vcos
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - Linux kernel (partial) implementation.
|
|
+=============================================================================*/
|
|
+
|
|
+/* Do not include this file directly - instead include it via vcos.h */
|
|
+
|
|
+/** @file
|
|
+ *
|
|
+ * Linux kernel (partial) implementation of VCOS.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef VCOS_PLATFORM_H
|
|
+#define VCOS_PLATFORM_H
|
|
+
|
|
+#include <linux/types.h>
|
|
+#include <linux/semaphore.h>
|
|
+#include <linux/mutex.h>
|
|
+#include <asm/bitops.h>
|
|
+#include <linux/kthread.h>
|
|
+#include <linux/wait.h>
|
|
+#include <linux/vmalloc.h>
|
|
+#include <linux/jiffies.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/string.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/random.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/ctype.h>
|
|
+#include <linux/uaccess.h>
|
|
+#include <linux/time.h> /* for time_t */
|
|
+#include <linux/slab.h>
|
|
+#include <linux/vmalloc.h>
|
|
+
|
|
+#define VCOS_HAVE_RTOS 1
|
|
+#define VCOS_HAVE_SEMAPHORE 1
|
|
+#define VCOS_HAVE_EVENT 1
|
|
+#define VCOS_HAVE_QUEUE 0
|
|
+#define VCOS_HAVE_LEGACY_ISR 0
|
|
+#define VCOS_HAVE_TIMER 1
|
|
+#define VCOS_HAVE_CANCELLATION_SAFE_TIMER 0
|
|
+#define VCOS_HAVE_MEMPOOL 0
|
|
+#define VCOS_HAVE_ISR 0
|
|
+#define VCOS_HAVE_ATOMIC_FLAGS 1
|
|
+#define VCOS_HAVE_BLOCK_POOL 0
|
|
+#define VCOS_HAVE_ONCE 1
|
|
+#define VCOS_HAVE_FILE 0
|
|
+#define VCOS_HAVE_USER_BUF 0
|
|
+#define VCOS_HAVE_CFG 1
|
|
+#define VCOS_HAVE_SPINLOCK 0
|
|
+#define VCOS_HAVE_CMD 1
|
|
+#define VCOS_HAVE_EVENT_FLAGS 1
|
|
+
|
|
+/* Exclude many VCOS classes which don't have predicates */
|
|
+#define VCOS_TLS_H
|
|
+#define VCOS_NAMED_MUTEX_H
|
|
+#define VCOS_REENTRANT_MUTEX_H
|
|
+#define VCOS_NAMED_SEMAPHORE_H
|
|
+#define VCOS_QUICKSLOW_MUTEX_H
|
|
+/*#define VCOS_INIT_H */
|
|
+/*#define VCOS_MEM_H */
|
|
+/*#define VCOS_STRING_H */
|
|
+
|
|
+typedef struct semaphore VCOS_SEMAPHORE_T;
|
|
+typedef struct semaphore VCOS_EVENT_T;
|
|
+typedef struct mutex VCOS_MUTEX_T;
|
|
+typedef volatile int VCOS_ONCE_T;
|
|
+
|
|
+typedef unsigned int VCOS_UNSIGNED;
|
|
+typedef unsigned int VCOS_OPTION;
|
|
+typedef atomic_t VCOS_ATOMIC_FLAGS_T;
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ struct timer_list linux_timer;
|
|
+ void *context;
|
|
+ void (*expiration_routine)(void *context);
|
|
+
|
|
+} VCOS_TIMER_T;
|
|
+
|
|
+typedef struct VCOS_LLTHREAD_T
|
|
+{
|
|
+ struct task_struct *thread; /**< The thread itself */
|
|
+ VCOS_SEMAPHORE_T suspend; /**< For support event groups and similar - a per thread semaphore */
|
|
+} VCOS_LLTHREAD_T;
|
|
+
|
|
+typedef enum
|
|
+{
|
|
+ VCOS_O_RDONLY = 00000000,
|
|
+ VCOS_O_WRONLY = 00000001,
|
|
+ VCOS_O_RDWR = 00000002,
|
|
+ VCOS_O_TRUNC = 00001000,
|
|
+} VCOS_FILE_FLAGS_T;
|
|
+
|
|
+typedef struct file *VCOS_FILE_T;
|
|
+
|
|
+#define VCOS_SUSPEND -1
|
|
+#define VCOS_NO_SUSPEND 0
|
|
+
|
|
+#define VCOS_START 1
|
|
+#define VCOS_NO_START 0
|
|
+
|
|
+#define VCOS_THREAD_PRI_MIN -20
|
|
+#define VCOS_THREAD_PRI_MAX 19
|
|
+
|
|
+#define VCOS_THREAD_PRI_INCREASE -1
|
|
+#define VCOS_THREAD_PRI_HIGHEST VCOS_THREAD_PRI_MIN
|
|
+#define VCOS_THREAD_PRI_LOWEST VCOS_THREAD_PRI_MAX
|
|
+#define VCOS_THREAD_PRI_NORMAL ((VCOS_THREAD_PRI_MAX+VCOS_THREAD_PRI_MIN)/2)
|
|
+#define VCOS_THREAD_PRI_ABOVE_NORMAL (VCOS_THREAD_PRI_NORMAL + VCOS_THREAD_PRI_INCREASE)
|
|
+#define VCOS_THREAD_PRI_REALTIME VCOS_THREAD_PRI_HIGHEST
|
|
+
|
|
+#define _VCOS_AFFINITY_DEFAULT 0
|
|
+#define _VCOS_AFFINITY_CPU0 0
|
|
+#define _VCOS_AFFINITY_CPU1 0
|
|
+#define _VCOS_AFFINITY_MASK 0
|
|
+#define VCOS_CAN_SET_STACK_ADDR 0
|
|
+
|
|
+#define VCOS_TICKS_PER_SECOND HZ
|
|
+
|
|
+#include "interface/vcos/generic/vcos_generic_event_flags.h"
|
|
+#include "interface/vcos/generic/vcos_mem_from_malloc.h"
|
|
+#include "interface/vcos/generic/vcos_joinable_thread_from_plain.h"
|
|
+
|
|
+/***********************************************************
|
|
+ *
|
|
+ * Memory allcoation
|
|
+ *
|
|
+ ***********************************************************/
|
|
+
|
|
+#define _vcos_platform_malloc vcos_platform_malloc
|
|
+#define _vcos_platform_free vcos_platform_free
|
|
+
|
|
+void *vcos_platform_malloc( VCOS_UNSIGNED required_size );
|
|
+void vcos_platform_free( void *ptr );
|
|
+
|
|
+#if defined(VCOS_INLINE_BODIES)
|
|
+
|
|
+#undef VCOS_ASSERT_LOGGING_DISABLE
|
|
+#define VCOS_ASSERT_LOGGING_DISABLE 1
|
|
+
|
|
+/***********************************************************
|
|
+ *
|
|
+ * Counted Semaphores
|
|
+ *
|
|
+ ***********************************************************/
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_semaphore_wait(VCOS_SEMAPHORE_T *sem) {
|
|
+ int ret = down_interruptible(sem);
|
|
+ if ( ret == 0 )
|
|
+ /* Success */
|
|
+ return VCOS_SUCCESS;
|
|
+ else if ( ret == -EINTR )
|
|
+ /* Interrupted */
|
|
+ return VCOS_EINTR;
|
|
+ else
|
|
+ /* Default (timeout) */
|
|
+ return VCOS_EAGAIN;
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_semaphore_trywait(VCOS_SEMAPHORE_T *sem) {
|
|
+ if (down_trylock(sem) != 0)
|
|
+ return VCOS_EAGAIN;
|
|
+ return VCOS_SUCCESS;
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_semaphore_create(VCOS_SEMAPHORE_T *sem,
|
|
+ const char *name,
|
|
+ VCOS_UNSIGNED initial_count) {
|
|
+ sema_init(sem, initial_count);
|
|
+ return VCOS_SUCCESS;
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_semaphore_delete(VCOS_SEMAPHORE_T *sem) {
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_semaphore_post(VCOS_SEMAPHORE_T *sem) {
|
|
+ up(sem);
|
|
+ return VCOS_SUCCESS;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ *
|
|
+ * Threads
|
|
+ *
|
|
+ ***********************************************************/
|
|
+
|
|
+#include "vcos_thread_map.h"
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_LLTHREAD_T *vcos_llthread_current(void) {
|
|
+ return &vcos_kthread_current()->thread;
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_llthread_resume(VCOS_LLTHREAD_T *thread) {
|
|
+ vcos_assert(0);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_sleep(uint32_t ms) {
|
|
+ msleep(ms);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_thread_set_priority(VCOS_THREAD_T *thread, VCOS_UNSIGNED p) {
|
|
+ /* not implemented */
|
|
+}
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_UNSIGNED vcos_thread_get_priority(VCOS_THREAD_T *thread) {
|
|
+ /* not implemented */
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ *
|
|
+ * Miscellaneous
|
|
+ *
|
|
+ ***********************************************************/
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+int vcos_strcasecmp(const char *s1, const char *s2) {
|
|
+ return strcasecmp(s1,s2);
|
|
+}
|
|
+
|
|
+
|
|
+/***********************************************************
|
|
+ *
|
|
+ * Mutexes
|
|
+ *
|
|
+ ***********************************************************/
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_mutex_create(VCOS_MUTEX_T *m, const char *name) {
|
|
+ mutex_init(m);
|
|
+ return VCOS_SUCCESS;
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_mutex_delete(VCOS_MUTEX_T *m) {
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_mutex_lock(VCOS_MUTEX_T *m) {
|
|
+ int ret = mutex_lock_interruptible(m);
|
|
+ if ( ret == 0 )
|
|
+ /* Success */
|
|
+ return VCOS_SUCCESS;
|
|
+ else if ( ret == -EINTR )
|
|
+ /* Interrupted */
|
|
+ return VCOS_EINTR;
|
|
+ else
|
|
+ /* Default */
|
|
+ return VCOS_EAGAIN;
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_mutex_unlock(VCOS_MUTEX_T *m) {
|
|
+ mutex_unlock(m);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+int vcos_mutex_is_locked(VCOS_MUTEX_T *m) {
|
|
+ if (mutex_trylock(m) != 0)
|
|
+ return 1; /* it was locked */
|
|
+ mutex_unlock(m);
|
|
+ /* it wasn't locked */
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_mutex_trylock(VCOS_MUTEX_T *m) {
|
|
+ if (mutex_trylock(m) == 0)
|
|
+ return VCOS_SUCCESS;
|
|
+ else
|
|
+ return VCOS_EAGAIN;
|
|
+}
|
|
+
|
|
+/* For supporting event groups - per thread semaphore */
|
|
+VCOS_INLINE_IMPL
|
|
+void _vcos_thread_sem_wait(void) {
|
|
+ VCOS_THREAD_T *t = vcos_thread_current();
|
|
+ vcos_semaphore_wait(&t->suspend);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void _vcos_thread_sem_post(VCOS_THREAD_T *target) {
|
|
+ vcos_semaphore_post(&target->suspend);
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ *
|
|
+ * Events
|
|
+ *
|
|
+ ***********************************************************/
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_event_create(VCOS_EVENT_T *event, const char *debug_name)
|
|
+{
|
|
+ sema_init(event, 0);
|
|
+ return VCOS_SUCCESS;
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_event_signal(VCOS_EVENT_T *event)
|
|
+{
|
|
+ up(event);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_event_wait(VCOS_EVENT_T *event)
|
|
+{
|
|
+ int ret = down_interruptible(event);
|
|
+ if ( ret == -EINTR )
|
|
+ /* Interrupted */
|
|
+ return VCOS_EINTR;
|
|
+ else if (ret != 0)
|
|
+ /* Default (timeout) */
|
|
+ return VCOS_EAGAIN;
|
|
+ /* Emulate a maximum count of 1 by removing any extra upness */
|
|
+ while (down_trylock(event) == 0) continue;
|
|
+ return VCOS_SUCCESS;
|
|
+}
|
|
+
|
|
+VCOS_INLINE_DECL
|
|
+VCOS_STATUS_T vcos_event_try(VCOS_EVENT_T *event)
|
|
+{
|
|
+ return (down_trylock(event) == 0) ? VCOS_SUCCESS : VCOS_EAGAIN;
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_event_delete(VCOS_EVENT_T *event)
|
|
+{
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ *
|
|
+ * Timers
|
|
+ *
|
|
+ ***********************************************************/
|
|
+
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_timer_linux_func(unsigned long data)
|
|
+{
|
|
+ VCOS_TIMER_T *vcos_timer = (VCOS_TIMER_T *)data;
|
|
+
|
|
+ vcos_timer->expiration_routine( vcos_timer->context );
|
|
+}
|
|
+
|
|
+VCOS_INLINE_DECL
|
|
+VCOS_STATUS_T vcos_timer_create(VCOS_TIMER_T *timer,
|
|
+ const char *name,
|
|
+ void (*expiration_routine)(void *context),
|
|
+ void *context) {
|
|
+ init_timer(&timer->linux_timer);
|
|
+ timer->linux_timer.data = (unsigned long)timer;
|
|
+ timer->linux_timer.function = vcos_timer_linux_func;
|
|
+
|
|
+ timer->context = context;
|
|
+ timer->expiration_routine = expiration_routine;
|
|
+
|
|
+ return VCOS_SUCCESS;
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_timer_set(VCOS_TIMER_T *timer, VCOS_UNSIGNED delay_ms) {
|
|
+ timer->linux_timer.expires = jiffies + msecs_to_jiffies(delay_ms);
|
|
+ add_timer(&timer->linux_timer);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_timer_cancel(VCOS_TIMER_T *timer) {
|
|
+ del_timer(&timer->linux_timer);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_timer_reset(VCOS_TIMER_T *timer, VCOS_UNSIGNED delay_ms) {
|
|
+ del_timer_sync(&timer->linux_timer);
|
|
+ timer->linux_timer.expires = jiffies + msecs_to_jiffies(delay_ms);
|
|
+ add_timer(&timer->linux_timer);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_timer_delete(VCOS_TIMER_T *timer) {
|
|
+ timer->context = NULL;
|
|
+ timer->expiration_routine = NULL;
|
|
+ timer->linux_timer.function = NULL;
|
|
+ timer->linux_timer.data = 0;
|
|
+ return;
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_UNSIGNED vcos_process_id_current(void) {
|
|
+ return (VCOS_UNSIGNED)current->pid;
|
|
+}
|
|
+
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+int vcos_in_interrupt(void) {
|
|
+ return in_interrupt();
|
|
+}
|
|
+
|
|
+/***********************************************************
|
|
+ *
|
|
+ * Atomic flags
|
|
+ *
|
|
+ ***********************************************************/
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+VCOS_STATUS_T vcos_atomic_flags_create(VCOS_ATOMIC_FLAGS_T *atomic_flags)
|
|
+{
|
|
+ atomic_set(atomic_flags, 0);
|
|
+ return VCOS_SUCCESS;
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_atomic_flags_or(VCOS_ATOMIC_FLAGS_T *atomic_flags, uint32_t flags)
|
|
+{
|
|
+ uint32_t value;
|
|
+ do {
|
|
+ value = atomic_read(atomic_flags);
|
|
+ } while (atomic_cmpxchg(atomic_flags, value, value | flags) != value);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+uint32_t vcos_atomic_flags_get_and_clear(VCOS_ATOMIC_FLAGS_T *atomic_flags)
|
|
+{
|
|
+ return atomic_xchg(atomic_flags, 0);
|
|
+}
|
|
+
|
|
+VCOS_INLINE_IMPL
|
|
+void vcos_atomic_flags_delete(VCOS_ATOMIC_FLAGS_T *atomic_flags)
|
|
+{
|
|
+}
|
|
+
|
|
+#undef VCOS_ASSERT_LOGGING_DISABLE
|
|
+#define VCOS_ASSERT_LOGGING_DISABLE 0
|
|
+
|
|
+#endif /* VCOS_INLINE_BODIES */
|
|
+
|
|
+VCOS_INLINE_DECL void _vcos_thread_sem_wait(void);
|
|
+VCOS_INLINE_DECL void _vcos_thread_sem_post(VCOS_THREAD_T *);
|
|
+
|
|
+/***********************************************************
|
|
+ *
|
|
+ * Misc
|
|
+ *
|
|
+ ***********************************************************/
|
|
+VCOS_INLINE_DECL char *vcos_strdup(const char *str);
|
|
+
|
|
+/***********************************************************
|
|
+ *
|
|
+ * Logging
|
|
+ *
|
|
+ ***********************************************************/
|
|
+
|
|
+VCOSPRE_ const char * VCOSPOST_ _vcos_log_level(void);
|
|
+#define _VCOS_LOG_LEVEL() _vcos_log_level()
|
|
+
|
|
+#define vcos_log_platform_init() _vcos_log_platform_init()
|
|
+#define vcos_log_platform_register(category) _vcos_log_platform_register(category)
|
|
+#define vcos_log_platform_unregister(category) _vcos_log_platform_unregister(category)
|
|
+
|
|
+struct VCOS_LOG_CAT_T; /* Forward declaration since vcos_logging.h hasn't been included yet */
|
|
+
|
|
+void _vcos_log_platform_init(void);
|
|
+void _vcos_log_platform_register(struct VCOS_LOG_CAT_T *category);
|
|
+void _vcos_log_platform_unregister(struct VCOS_LOG_CAT_T *category);
|
|
+
|
|
+/***********************************************************
|
|
+ *
|
|
+ * Memory barriers
|
|
+ *
|
|
+ ***********************************************************/
|
|
+
|
|
+#define vcos_wmb(x) wmb()
|
|
+#define vcos_rmb() rmb()
|
|
+
|
|
+#include "interface/vcos/generic/vcos_common.h"
|
|
+/*#include "interface/vcos/generic/vcos_generic_quickslow_mutex.h" */
|
|
+
|
|
+#endif /* VCOS_PLATFORM_H */
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_platform_types.h
|
|
@@ -0,0 +1,47 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : osal
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - platform-specific types and defines
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_PLATFORM_TYPES_H
|
|
+#define VCOS_PLATFORM_TYPES_H
|
|
+
|
|
+#include <stddef.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/bug.h>
|
|
+
|
|
+#define VCOSPRE_ extern
|
|
+#define VCOSPOST_
|
|
+
|
|
+#if defined(__GNUC__) && (( __GNUC__ > 2 ) || (( __GNUC__ == 2 ) && ( __GNUC_MINOR__ >= 3 )))
|
|
+#define VCOS_FORMAT_ATTR_(ARCHETYPE, STRING_INDEX, FIRST_TO_CHECK) __attribute__ ((format (ARCHETYPE, STRING_INDEX, FIRST_TO_CHECK)))
|
|
+#else
|
|
+#define VCOS_FORMAT_ATTR_(ARCHETYPE, STRING_INDEX, FIRST_TO_CHECK)
|
|
+#endif
|
|
+
|
|
+#if !defined( __STDC_VERSION__ )
|
|
+#define __STDC_VERSION__ 199901L
|
|
+#endif
|
|
+
|
|
+#if !defined( __STDC_VERSION )
|
|
+#define __STDC_VERSION __STDC_VERSION__
|
|
+#endif
|
|
+
|
|
+static inline void __vcos_bkpt( void ) { BUG(); }
|
|
+#define VCOS_BKPT __vcos_bkpt()
|
|
+
|
|
+#define VCOS_ASSERT_MSG(...) printk( KERN_ERR "vcos_assert: " __VA_ARGS__ )
|
|
+
|
|
+#define PRId64 "lld"
|
|
+#define PRIi64 "lli"
|
|
+#define PRIo64 "llo"
|
|
+#define PRIu64 "llu"
|
|
+#define PRIx64 "llx"
|
|
+
|
|
+#endif
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_thread_map.c
|
|
@@ -0,0 +1,129 @@
|
|
+/*****************************************************************************
|
|
+* Copyright 2009 - 2010 Broadcom Corporation. All rights reserved.
|
|
+*
|
|
+* Unless you and Broadcom execute a separate written software license
|
|
+* agreement governing use of this software, this software is licensed to you
|
|
+* under the terms of the GNU General Public License version 2, available at
|
|
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
|
+*
|
|
+* Notwithstanding the above, under no circumstances may you combine this
|
|
+* software in any way with any other Broadcom software provided under a
|
|
+* license other than the GPL, without Broadcom's express prior written
|
|
+* consent.
|
|
+*****************************************************************************/
|
|
+
|
|
+/** Support to allow VCOS thread-related functions to be called from
|
|
+ * threads that were not created by VCOS.
|
|
+ */
|
|
+
|
|
+#include <linux/semaphore.h>
|
|
+#include <linux/vmalloc.h>
|
|
+#include <linux/list.h>
|
|
+#include <linux/sched.h>
|
|
+
|
|
+#include "vcos_thread_map.h"
|
|
+#include "interface/vcos/vcos_logging.h"
|
|
+
|
|
+/*
|
|
+ * Store the vcos_thread pointer at the end of
|
|
+ * current kthread stack, right after the thread_info
|
|
+ * structure.
|
|
+ *
|
|
+ * I belive we should be safe here to steal these 4 bytes
|
|
+ * from the stack, as long as the vcos thread does not use up
|
|
+ * all the stack available
|
|
+ *
|
|
+ * NOTE: This scheme will not work on architectures with stack growing up
|
|
+ */
|
|
+
|
|
+/* Shout, if we are not being compiled for ARM kernel */
|
|
+
|
|
+#ifndef CONFIG_ARM
|
|
+#error " **** The vcos kthread implementation may not work for non-ARM kernel ****"
|
|
+#endif
|
|
+
|
|
+static inline void *to_current_vcos_thread(void)
|
|
+{
|
|
+ unsigned long *vcos_data;
|
|
+
|
|
+ vcos_data = (unsigned long *)((char *)current_thread_info() + sizeof(struct thread_info));
|
|
+
|
|
+ return (void *)vcos_data;
|
|
+}
|
|
+
|
|
+
|
|
+static inline void *to_vcos_thread(struct task_struct *tsk)
|
|
+{
|
|
+ unsigned long *vcos_data;
|
|
+
|
|
+ vcos_data = (unsigned long *)((char *)tsk->stack + sizeof(struct thread_info));
|
|
+
|
|
+ return (void *)vcos_data;
|
|
+}
|
|
+
|
|
+/**
|
|
+ @fn uint32_t vcos_add_thread(THREAD_MAP_T *vcos_thread);
|
|
+*/
|
|
+uint32_t vcos_add_thread(VCOS_THREAD_T *vcos_thread)
|
|
+{
|
|
+ VCOS_THREAD_T **vcos_thread_storage = (VCOS_THREAD_T **)to_current_vcos_thread();
|
|
+
|
|
+ *vcos_thread_storage = vcos_thread;
|
|
+
|
|
+ return(0);
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ @fn uint32_t vcos_remove_thread(struct task_struct * thread_id);
|
|
+*/
|
|
+uint32_t vcos_remove_thread(struct task_struct *thread_id)
|
|
+{
|
|
+ /* Remove thread_id -> VCOS_THREAD_T relationship */
|
|
+ VCOS_THREAD_T **vcos_thread_storage;
|
|
+
|
|
+ /*
|
|
+ * We want to be able to build vcos as a loadable module, which
|
|
+ * means that we can't call get_task_struct. So we assert if we're
|
|
+ * ever called with thread_id != current.
|
|
+ */
|
|
+
|
|
+ BUG_ON( thread_id != current );
|
|
+
|
|
+ vcos_thread_storage = (VCOS_THREAD_T **)to_vcos_thread(thread_id);
|
|
+
|
|
+ *(unsigned long *)vcos_thread_storage = 0xCAFEBABE;
|
|
+
|
|
+ return(0);
|
|
+}
|
|
+
|
|
+
|
|
+VCOS_THREAD_T *vcos_kthread_current(void)
|
|
+{
|
|
+ VCOS_THREAD_T **vcos_thread_storage = (VCOS_THREAD_T **)to_current_vcos_thread();
|
|
+
|
|
+ /* If we find this, either the thread is already dead or stack pages of a
|
|
+ * dead vcos thread are re-allocated to this one.
|
|
+ *
|
|
+ * Since there's no way to differentiate between these 2 cases, we just dump
|
|
+ * the current task name to the log.
|
|
+ *
|
|
+ * If the current thread is created using VCOS API, you should *never* see this
|
|
+ * print.
|
|
+ *
|
|
+ * If its a non-VCOS thread, just let it go ...
|
|
+ *
|
|
+ * To debug VCOS, uncomment printk's under the "if" condition below
|
|
+ *
|
|
+ */
|
|
+ if (*vcos_thread_storage == (void *)0xCAFEBABE)
|
|
+ {
|
|
+ #if 0
|
|
+ printk(KERN_DEBUG"****************************************************\n");
|
|
+ printk(KERN_DEBUG"%s : You have a problem, if \"%s\" is a VCOS thread\n",__func__, current->comm);
|
|
+ printk(KERN_DEBUG"****************************************************\n");
|
|
+ #endif
|
|
+ }
|
|
+
|
|
+ return *vcos_thread_storage;
|
|
+}
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_thread_map.h
|
|
@@ -0,0 +1,39 @@
|
|
+/*****************************************************************************
|
|
+* Copyright 2009 - 2010 Broadcom Corporation. All rights reserved.
|
|
+*
|
|
+* Unless you and Broadcom execute a separate written software license
|
|
+* agreement governing use of this software, this software is licensed to you
|
|
+* under the terms of the GNU General Public License version 2, available at
|
|
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
|
+*
|
|
+* Notwithstanding the above, under no circumstances may you combine this
|
|
+* software in any way with any other Broadcom software provided under a
|
|
+* license other than the GPL, without Broadcom's express prior written
|
|
+* consent.
|
|
+*****************************************************************************/
|
|
+
|
|
+
|
|
+#ifndef VCOS_THREAD_MAP_H
|
|
+#define VCOS_THREAD_MAP_H
|
|
+
|
|
+#include <linux/string.h>
|
|
+
|
|
+#include "vcos_platform.h"
|
|
+
|
|
+static inline void vcos_thread_map_init(void)
|
|
+{
|
|
+ return;
|
|
+}
|
|
+
|
|
+static inline void vcos_thread_map_cleanup(void)
|
|
+{
|
|
+ return;
|
|
+}
|
|
+
|
|
+uint32_t vcos_add_thread(VCOS_THREAD_T *vcos_thread);
|
|
+
|
|
+uint32_t vcos_remove_thread(struct task_struct *thread_id);
|
|
+
|
|
+VCOS_THREAD_T *vcos_kthread_current(void);
|
|
+
|
|
+#endif /*VCOS_THREAD_MAP_H */
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos.h
|
|
@@ -0,0 +1,201 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - public header file
|
|
+=============================================================================*/
|
|
+
|
|
+/**
|
|
+ * \mainpage OS Abstraction Layer
|
|
+ *
|
|
+ * \section intro Introduction
|
|
+ *
|
|
+ * This abstraction layer is here to allow the underlying OS to be easily changed (e.g. from
|
|
+ * Nucleus to ThreadX) and to aid in porting host applications to new targets.
|
|
+ *
|
|
+ * \subsection error Error handling
|
|
+ *
|
|
+ * Wherever possible, VCOS functions assert internally and return void. The only exceptions
|
|
+ * are creation functions (which might fail due to lack of resources) and functions that
|
|
+ * might timeout or fail due to lack of space. Errors that might be reported by the underlying
|
|
+ * OS API (e.g. invalid mutex) are treated as a programming error, and are merely asserted on.
|
|
+ *
|
|
+ * \section thread_synch Threads and synchronisation
|
|
+ *
|
|
+ * \subsection thread Threads
|
|
+ *
|
|
+ * The thread API is somewhat different to that found in Nucleus. In particular, threads
|
|
+ * cannot just be destroyed at arbitrary times and nor can they merely exit. This is so
|
|
+ * that the same API can be implemented across all interesting platforms without too much
|
|
+ * difficulty. See vcos_thread.h for details. Thread attributes are configured via
|
|
+ * the VCOS_THREAD_ATTR_T structure, found in vcos_thread_attr.h.
|
|
+ *
|
|
+ * \subsection sema Semaphores
|
|
+ *
|
|
+ * Counted semaphores (c.f. Nucleus NU_SEMAPHORE) are created with VCOS_SEMAPHORE_T.
|
|
+ * Under ThreadX on VideoCore, semaphores are implemented using VideoCore spinlocks, and
|
|
+ * so are quite a lot faster than ordinary ThreadX semaphores. See vcos_semaphore.h.
|
|
+ *
|
|
+ * \subsection mtx Mutexes
|
|
+ *
|
|
+ * Mutexes are used for locking. Attempts to take a mutex twice, or to unlock it
|
|
+ * in a different thread to the one in which it was locked should be expected to fail.
|
|
+ * Mutexes are not re-entrant (see vcos_reentrant_mutex.h for a slightly slower
|
|
+ * re-entrant mutex).
|
|
+ *
|
|
+ * \subsection evflags Event flags
|
|
+ *
|
|
+ * Event flags (the ThreadX name - also known as event groups under Nucleus) provide
|
|
+ * 32 flags which can be waited on by multiple clients, and signalled by multiple clients.
|
|
+ * A timeout can be specified. See vcos_event_flags.h. An alternative to this is the
|
|
+ * VCOS_EVENT_T (see vcos_event.h) which is akin to the Win32 auto-reset event, or a
|
|
+ * saturating counted semaphore.
|
|
+ *
|
|
+ * \subsection event Events
|
|
+ *
|
|
+ * A VCOS_EVENT_T is a bit like a saturating semaphore. No matter how many times it
|
|
+ * is signalled, the waiter will only wake up once. See vcos_event.h. You might think this
|
|
+ * is useful if you suspect that the cost of reading the semaphore count (perhaps via a
|
|
+ * system call) is expensive on your platform.
|
|
+ *
|
|
+ * \subsection tls Thread local storage
|
|
+ *
|
|
+ * Thread local storage is supported using vcos_tls.h. This is emulated on Nucleus
|
|
+ * and ThreadX.
|
|
+ *
|
|
+ * \section int Interrupts
|
|
+ *
|
|
+ * The legacy LISR/HISR scheme found in Nucleus is supported via the legacy ISR API,
|
|
+ * which is also supported on ThreadX. New code should avoid this, and old code should
|
|
+ * be migrated away from it, since it is slow. See vcos_legacy_isr.h.
|
|
+ *
|
|
+ * Registering an interrupt handler, and disabling/restoring interrupts, is handled
|
|
+ * using the functions in vcos_isr.h.
|
|
+ *
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * \file vcos.h
|
|
+ *
|
|
+ * This is the top level header file. Clients include this. It pulls in the platform-specific
|
|
+ * header file (vcos_platform.h) together with header files defining the expected APIs, such
|
|
+ * as vcos_mutex.h, vcos_semaphore.h, etc. It is also possible to include these header files
|
|
+ * directly.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef VCOS_H
|
|
+#define VCOS_H
|
|
+
|
|
+#include "interface/vcos/vcos_assert.h"
|
|
+#include "vcos_types.h"
|
|
+#include "vcos_platform.h"
|
|
+
|
|
+#ifndef VCOS_INIT_H
|
|
+#include "interface/vcos/vcos_init.h"
|
|
+#endif
|
|
+
|
|
+#ifndef VCOS_SEMAPHORE_H
|
|
+#include "interface/vcos/vcos_semaphore.h"
|
|
+#endif
|
|
+
|
|
+#ifndef VCOS_THREAD_H
|
|
+#include "interface/vcos/vcos_thread.h"
|
|
+#endif
|
|
+
|
|
+#ifndef VCOS_MUTEX_H
|
|
+#include "interface/vcos/vcos_mutex.h"
|
|
+#endif
|
|
+
|
|
+#ifndef VCOS_MEM_H
|
|
+#include "interface/vcos/vcos_mem.h"
|
|
+#endif
|
|
+
|
|
+#ifndef VCOS_LOGGING_H
|
|
+#include "interface/vcos/vcos_logging.h"
|
|
+#endif
|
|
+
|
|
+#ifndef VCOS_STRING_H
|
|
+#include "interface/vcos/vcos_string.h"
|
|
+#endif
|
|
+
|
|
+#ifndef VCOS_EVENT_H
|
|
+#include "interface/vcos/vcos_event.h"
|
|
+#endif
|
|
+
|
|
+#ifndef VCOS_THREAD_ATTR_H
|
|
+#include "interface/vcos/vcos_thread_attr.h"
|
|
+#endif
|
|
+
|
|
+#ifndef VCOS_TLS_H
|
|
+#include "interface/vcos/vcos_tls.h"
|
|
+#endif
|
|
+
|
|
+#ifndef VCOS_REENTRANT_MUTEX_H
|
|
+#include "interface/vcos/vcos_reentrant_mutex.h"
|
|
+#endif
|
|
+
|
|
+#ifndef VCOS_NAMED_SEMAPHORE_H
|
|
+#include "interface/vcos/vcos_named_semaphore.h"
|
|
+#endif
|
|
+
|
|
+#ifndef VCOS_QUICKSLOW_MUTEX_H
|
|
+#include "interface/vcos/vcos_quickslow_mutex.h"
|
|
+#endif
|
|
+
|
|
+/* Headers with predicates */
|
|
+
|
|
+#if VCOS_HAVE_EVENT_FLAGS
|
|
+#include "interface/vcos/vcos_event_flags.h"
|
|
+#endif
|
|
+
|
|
+#if VCOS_HAVE_QUEUE
|
|
+#include "interface/vcos/vcos_queue.h"
|
|
+#endif
|
|
+
|
|
+#if VCOS_HAVE_LEGACY_ISR
|
|
+#include "interface/vcos/vcos_legacy_isr.h"
|
|
+#endif
|
|
+
|
|
+#if VCOS_HAVE_TIMER
|
|
+#include "interface/vcos/vcos_timer.h"
|
|
+#endif
|
|
+
|
|
+#if VCOS_HAVE_MEMPOOL
|
|
+#include "interface/vcos/vcos_mempool.h"
|
|
+#endif
|
|
+
|
|
+#if VCOS_HAVE_ISR
|
|
+#include "interface/vcos/vcos_isr.h"
|
|
+#endif
|
|
+
|
|
+#if VCOS_HAVE_ATOMIC_FLAGS
|
|
+#include "interface/vcos/vcos_atomic_flags.h"
|
|
+#endif
|
|
+
|
|
+#if VCOS_HAVE_ONCE
|
|
+#include "interface/vcos/vcos_once.h"
|
|
+#endif
|
|
+
|
|
+#if VCOS_HAVE_BLOCK_POOL
|
|
+#include "interface/vcos/vcos_blockpool.h"
|
|
+#endif
|
|
+
|
|
+#if VCOS_HAVE_FILE
|
|
+#include "interface/vcos/vcos_file.h"
|
|
+#endif
|
|
+
|
|
+#if VCOS_HAVE_CFG
|
|
+#include "interface/vcos/vcos_cfg.h"
|
|
+#endif
|
|
+
|
|
+#if VCOS_HAVE_CMD
|
|
+#include "interface/vcos/vcos_cmd.h"
|
|
+#endif
|
|
+
|
|
+#endif /* VCOS_H */
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_assert.h
|
|
@@ -0,0 +1,269 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : osal
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - Assertion and error-handling macros.
|
|
+=============================================================================*/
|
|
+
|
|
+
|
|
+#ifndef VCOS_ASSERT_H
|
|
+#define VCOS_ASSERT_H
|
|
+
|
|
+/*
|
|
+ * Macro:
|
|
+ * vcos_assert(cond)
|
|
+ * vcos_assert_msg(cond, fmt, ...)
|
|
+ * Use:
|
|
+ * Detecting programming errors by ensuring that assumptions are correct.
|
|
+ * On failure:
|
|
+ * Performs a platform-dependent "breakpoint", usually with an assert-style
|
|
+ * message. The '_msg' variant expects a printf-style format string and
|
|
+ * parameters.
|
|
+ * If a failure is detected, the code should be fixed and rebuilt.
|
|
+ * In release builds:
|
|
+ * Generates no code, i.e. does not evaluate 'cond'.
|
|
+ * Returns:
|
|
+ * Nothing.
|
|
+ *
|
|
+ * Macro:
|
|
+ * vcos_demand(cond)
|
|
+ * vcos_demand_msg(cond, fmt, ...)
|
|
+ * Use:
|
|
+ * Detecting fatal system errors that require a reboot.
|
|
+ * On failure:
|
|
+ * Performs a platform-dependent "breakpoint", usually with an assert-style
|
|
+ * message, then calls vcos_abort (see below).
|
|
+ * In release builds:
|
|
+ * Calls vcos_abort() if 'cond' is false.
|
|
+ * Returns:
|
|
+ * Nothing (never, on failure).
|
|
+ *
|
|
+ * Macro:
|
|
+ * vcos_verify(cond)
|
|
+ * vcos_verify_msg(cond, fmt, ...)
|
|
+ * Use:
|
|
+ * Detecting run-time errors and interesting conditions, normally within an
|
|
+ * 'if' statement to catch the failures, i.e.
|
|
+ * if (!vcos_verify(cond)) handle_error();
|
|
+ * On failure:
|
|
+ * Generates a message and optionally stops at a platform-dependent
|
|
+ * "breakpoint" (usually disabled). See vcos_verify_bkpts_enable below.
|
|
+ * In release builds:
|
|
+ * Just evaluates and returns 'cond'.
|
|
+ * Returns:
|
|
+ * Non-zero if 'cond' is true, otherwise zero.
|
|
+ *
|
|
+ * Macro:
|
|
+ * vcos_static_assert(cond)
|
|
+ * Use:
|
|
+ * Detecting compile-time errors.
|
|
+ * On failure:
|
|
+ * Generates a compiler error.
|
|
+ * In release builds:
|
|
+ * Generates a compiler error.
|
|
+ *
|
|
+ * Function:
|
|
+ * void vcos_abort(void)
|
|
+ * Use:
|
|
+ * Invokes the fatal error handling mechanism, alerting the host where
|
|
+ * applicable.
|
|
+ * Returns:
|
|
+ * Never.
|
|
+ *
|
|
+ * Macro:
|
|
+ * VCOS_VERIFY_BKPTS
|
|
+ * Use:
|
|
+ * Define in a module (before including vcos.h) to specify an alternative
|
|
+ * flag to control breakpoints on vcos_verify() failures.
|
|
+ * Returns:
|
|
+ * Non-zero values enable breakpoints.
|
|
+ *
|
|
+ * Function:
|
|
+ * int vcos_verify_bkpts_enable(int enable);
|
|
+ * Use:
|
|
+ * Sets the global flag controlling breakpoints on vcos_verify failures,
|
|
+ * enabling the breakpoints iff 'enable' is non-zero.
|
|
+ * Returns:
|
|
+ * The previous state of the flag.
|
|
+ *
|
|
+ * Function:
|
|
+ * int vcos_verify_bkpts_enabled(void);
|
|
+ * Use:
|
|
+ * Queries the state of the global flag enabling breakpoints on vcos_verify
|
|
+ * failures.
|
|
+ * Returns:
|
|
+ * The current state of the flag.
|
|
+ *
|
|
+ * Examples:
|
|
+ *
|
|
+ * int my_breakpoint_enable_flag = 1;
|
|
+ *
|
|
+ * #define VCOS_VERIFY_BKPTS my_breakpoint_enable_flag
|
|
+ *
|
|
+ * #include "interface/vcos/vcos.h"
|
|
+ *
|
|
+ * vcos_static_assert((sizeof(object) % 32) == 0);
|
|
+ *
|
|
+ * // ...
|
|
+ *
|
|
+ * vcos_assert_msg(postcondition_is_true, "Coding error");
|
|
+ *
|
|
+ * if (!vcos_verify_msg(buf, "Buffer allocation failed (%d bytes)", size))
|
|
+ * {
|
|
+ * // Tidy up
|
|
+ * // ...
|
|
+ * return OUT_OF_MEMORY;
|
|
+ * }
|
|
+ *
|
|
+ * vcos_demand(*p++==GUARDWORDHEAP);
|
|
+ */
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+
|
|
+#ifdef __COVERITY__
|
|
+#undef VCOS_ASSERT_BKPT
|
|
+#define VCOS_ASSERT_BKPT __coverity_panic__()
|
|
+#endif
|
|
+
|
|
+#ifndef VCOS_VERIFY_BKPTS
|
|
+#define VCOS_VERIFY_BKPTS vcos_verify_bkpts_enabled()
|
|
+#endif
|
|
+
|
|
+#ifndef VCOS_BKPT
|
|
+#if defined(__VIDEOCORE__) && !defined(VCOS_ASSERT_NO_BKPTS)
|
|
+#define VCOS_BKPT _bkpt()
|
|
+#else
|
|
+#define VCOS_BKPT (void )0
|
|
+#endif
|
|
+#endif
|
|
+
|
|
+#ifndef VCOS_ASSERT_BKPT
|
|
+#define VCOS_ASSERT_BKPT VCOS_BKPT
|
|
+#endif
|
|
+
|
|
+#ifndef VCOS_VERIFY_BKPT
|
|
+#define VCOS_VERIFY_BKPT (VCOS_VERIFY_BKPTS ? VCOS_BKPT : (void)0)
|
|
+#endif
|
|
+
|
|
+VCOSPRE_ int VCOSPOST_ vcos_verify_bkpts_enabled(void);
|
|
+VCOSPRE_ int VCOSPOST_ vcos_verify_bkpts_enable(int enable);
|
|
+VCOSPRE_ void VCOSPOST_ vcos_abort(void);
|
|
+
|
|
+#ifndef VCOS_ASSERT_MSG
|
|
+#ifdef LOGGING
|
|
+extern void logging_assert(const char *file, const char *func, int line, const char *format, ...);
|
|
+#define VCOS_ASSERT_MSG(...) ((VCOS_ASSERT_LOGGING && !VCOS_ASSERT_LOGGING_DISABLE) ? logging_assert(__FILE__, __func__, __LINE__, __VA_ARGS__) : (void)0)
|
|
+#else
|
|
+#define VCOS_ASSERT_MSG(...) ((void)0)
|
|
+#endif
|
|
+#endif
|
|
+
|
|
+#ifndef VCOS_VERIFY_MSG
|
|
+#define VCOS_VERIFY_MSG(...) VCOS_ASSERT_MSG(__VA_ARGS__)
|
|
+#endif
|
|
+
|
|
+#ifndef VCOS_ASSERT_LOGGING
|
|
+#define VCOS_ASSERT_LOGGING 0
|
|
+#endif
|
|
+
|
|
+#ifndef VCOS_ASSERT_LOGGING_DISABLE
|
|
+#define VCOS_ASSERT_LOGGING_DISABLE 0
|
|
+#endif
|
|
+
|
|
+#if !defined(NDEBUG) || defined(VCOS_RELEASE_ASSERTS)
|
|
+
|
|
+#ifndef vcos_assert
|
|
+#define vcos_assert(cond) \
|
|
+ ( (cond) ? (void)0 : (VCOS_ASSERT_MSG("%s", #cond), VCOS_ASSERT_BKPT) )
|
|
+#endif
|
|
+
|
|
+#ifndef vcos_assert_msg
|
|
+#define vcos_assert_msg(cond, ...) \
|
|
+ ( (cond) ? (void)0 : (VCOS_ASSERT_MSG(__VA_ARGS__), VCOS_ASSERT_BKPT) )
|
|
+#endif
|
|
+
|
|
+#else /* !defined(NDEBUG) || defined(VCOS_RELEASE_ASSERTS) */
|
|
+
|
|
+#ifndef vcos_assert
|
|
+#define vcos_assert(cond) (void)0
|
|
+#endif
|
|
+
|
|
+#ifndef vcos_assert_msg
|
|
+#define vcos_assert_msg(cond, ...) (void)0
|
|
+#endif
|
|
+
|
|
+#endif /* !defined(NDEBUG) || defined(VCOS_RELEASE_ASSERTS) */
|
|
+
|
|
+#if !defined(NDEBUG)
|
|
+
|
|
+#ifndef vcos_demand
|
|
+#define vcos_demand(cond) \
|
|
+ ( (cond) ? (void)0 : (VCOS_ASSERT_MSG("%s", #cond), VCOS_ASSERT_BKPT, vcos_abort()) )
|
|
+#endif
|
|
+
|
|
+#ifndef vcos_demand_msg
|
|
+#define vcos_demand_msg(cond, ...) \
|
|
+ ( (cond) ? (void)0 : (VCOS_ASSERT_MSG(__VA_ARGS__), VCOS_ASSERT_BKPT, vcos_abort()) )
|
|
+#endif
|
|
+
|
|
+#ifndef vcos_verify
|
|
+#define vcos_verify(cond) \
|
|
+ ( (cond) ? 1 : (VCOS_VERIFY_MSG("%s", #cond), VCOS_VERIFY_BKPT, 0) )
|
|
+#endif
|
|
+
|
|
+#ifndef vcos_verify_msg
|
|
+#define vcos_verify_msg(cond, ...) \
|
|
+ ( (cond) ? 1 : (VCOS_VERIFY_MSG(__VA_ARGS__), VCOS_VERIFY_BKPT, 0) )
|
|
+#endif
|
|
+
|
|
+#else /* !defined(NDEBUG) */
|
|
+
|
|
+#ifndef vcos_demand
|
|
+#define vcos_demand(cond) \
|
|
+ ( (cond) ? (void)0 : vcos_abort() )
|
|
+#endif
|
|
+
|
|
+#ifndef vcos_demand_msg
|
|
+#define vcos_demand_msg(cond, ...) \
|
|
+ ( (cond) ? (void)0 : vcos_abort() )
|
|
+#endif
|
|
+
|
|
+#ifndef vcos_verify
|
|
+#define vcos_verify(cond) (cond)
|
|
+#endif
|
|
+
|
|
+#ifndef vcos_verify_msg
|
|
+#define vcos_verify_msg(cond, ...) (cond)
|
|
+#endif
|
|
+
|
|
+#endif /* !defined(NDEBUG) */
|
|
+
|
|
+#ifndef vcos_static_assert
|
|
+#if defined(__GNUC__)
|
|
+#define vcos_static_assert(cond) __attribute__((unused)) extern int vcos_static_assert[(cond)?1:-1]
|
|
+#else
|
|
+#define vcos_static_assert(cond) extern int vcos_static_assert[(cond)?1:-1]
|
|
+#endif
|
|
+#endif
|
|
+
|
|
+#ifndef vc_assert
|
|
+#define vc_assert(cond) vcos_assert(cond)
|
|
+#endif
|
|
+
|
|
+/** Print out a backtrace, on supported platforms.
|
|
+ */
|
|
+extern void vcos_backtrace_self(void);
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+
|
|
+#endif /* VCOS_ASSERT_H */
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_atomic_flags.h
|
|
@@ -0,0 +1,72 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver (just for consistency with the rest of vcos ;)
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - public header file
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_ATOMIC_FLAGS_H
|
|
+#define VCOS_ATOMIC_FLAGS_H
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+#include "vcos_platform.h"
|
|
+
|
|
+/**
|
|
+ * \file vcos_atomic_flags.h
|
|
+ *
|
|
+ * Defines atomic flags API.
|
|
+ *
|
|
+ * 32 flags. Atomic "or" and "get and clear" operations
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * Create an atomic flags instance.
|
|
+ *
|
|
+ * @param atomic_flags Pointer to atomic flags instance, filled in on return
|
|
+ *
|
|
+ * @return VCOS_SUCCESS if succeeded.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+VCOS_STATUS_T vcos_atomic_flags_create(VCOS_ATOMIC_FLAGS_T *atomic_flags);
|
|
+
|
|
+/**
|
|
+ * Atomically set the specified flags.
|
|
+ *
|
|
+ * @param atomic_flags Instance to set flags on
|
|
+ * @param flags Mask of flags to set
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_atomic_flags_or(VCOS_ATOMIC_FLAGS_T *atomic_flags, uint32_t flags);
|
|
+
|
|
+/**
|
|
+ * Retrieve the current flags and then clear them. The entire operation is
|
|
+ * atomic.
|
|
+ *
|
|
+ * @param atomic_flags Instance to get/clear flags from/on
|
|
+ *
|
|
+ * @return Mask of flags which were set (and we cleared)
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+uint32_t vcos_atomic_flags_get_and_clear(VCOS_ATOMIC_FLAGS_T *atomic_flags);
|
|
+
|
|
+/**
|
|
+ * Delete an atomic flags instance.
|
|
+ *
|
|
+ * @param atomic_flags Instance to delete
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_atomic_flags_delete(VCOS_ATOMIC_FLAGS_T *atomic_flags);
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+
|
|
+#endif
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_build_info.h
|
|
@@ -0,0 +1,5 @@
|
|
+const char *vcos_get_build_hostname( void );
|
|
+const char *vcos_get_build_version( void );
|
|
+const char *vcos_get_build_time( void );
|
|
+const char *vcos_get_build_date( void );
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_cfg.h
|
|
@@ -0,0 +1,113 @@
|
|
+/*****************************************************************************
|
|
+* Copyright 2009 - 2011 Broadcom Corporation. All rights reserved.
|
|
+*
|
|
+* Unless you and Broadcom execute a separate written software license
|
|
+* agreement governing use of this software, this software is licensed to you
|
|
+* under the terms of the GNU General Public License version 2, available at
|
|
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
|
+*
|
|
+* Notwithstanding the above, under no circumstances may you combine this
|
|
+* software in any way with any other Broadcom software provided under a
|
|
+* license other than the GPL, without Broadcom's express prior written
|
|
+* consent.
|
|
+*****************************************************************************/
|
|
+
|
|
+#if !defined( VCOS_CFG_H )
|
|
+#define VCOS_CFG_H
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+#include "vcos_platform.h"
|
|
+
|
|
+typedef struct opaque_vcos_cfg_buf_t *VCOS_CFG_BUF_T;
|
|
+typedef struct opaque_vcos_cfg_entry_t *VCOS_CFG_ENTRY_T;
|
|
+
|
|
+/** \file vcos_file.h
|
|
+ *
|
|
+ * API for accessing configuration/statistics information. This
|
|
+ * is loosely modelled on the linux proc entries.
|
|
+ */
|
|
+
|
|
+typedef void (*VCOS_CFG_SHOW_FPTR)( VCOS_CFG_BUF_T buf, void *data );
|
|
+typedef void (*VCOS_CFG_PARSE_FPTR)( VCOS_CFG_BUF_T buf, void *data );
|
|
+
|
|
+/** Create a configuration directory.
|
|
+ *
|
|
+ * @param entry Place to store the created config entry.
|
|
+ * @param parent Parent entry (for directory like config
|
|
+ * options).
|
|
+ * @param entryName Name of the directory.
|
|
+ */
|
|
+
|
|
+VCOS_STATUS_T vcos_cfg_mkdir( VCOS_CFG_ENTRY_T *entry,
|
|
+ VCOS_CFG_ENTRY_T *parent,
|
|
+ const char *dirName );
|
|
+
|
|
+/** Create a configuration entry.
|
|
+ *
|
|
+ * @param entry Place to store the created config entry.
|
|
+ * @param parent Parent entry (for directory like config
|
|
+ * options).
|
|
+ * @param entryName Name of the configuration entry.
|
|
+ * @param showFunc Function pointer to show configuration
|
|
+ * data.
|
|
+ * @param parseFunc Function pointer to parse new data.
|
|
+ */
|
|
+
|
|
+VCOS_STATUS_T vcos_cfg_create_entry( VCOS_CFG_ENTRY_T *entry,
|
|
+ VCOS_CFG_ENTRY_T *parent,
|
|
+ const char *entryName,
|
|
+ VCOS_CFG_SHOW_FPTR showFunc,
|
|
+ VCOS_CFG_PARSE_FPTR parseFunc,
|
|
+ void *data );
|
|
+
|
|
+/** Determines if a configuration entry has been created or not.
|
|
+ *
|
|
+ * @param entry Configuration entry to query.
|
|
+ */
|
|
+
|
|
+int vcos_cfg_is_entry_created( VCOS_CFG_ENTRY_T entry );
|
|
+
|
|
+/** Returns the name of a configuration entry.
|
|
+ *
|
|
+ * @param entry Configuration entry to query.
|
|
+ */
|
|
+
|
|
+const char *vcos_cfg_get_entry_name( VCOS_CFG_ENTRY_T entry );
|
|
+
|
|
+/** Removes a configuration entry.
|
|
+ *
|
|
+ * @param entry Configuration entry to remove.
|
|
+ */
|
|
+
|
|
+VCOS_STATUS_T vcos_cfg_remove_entry( VCOS_CFG_ENTRY_T *entry );
|
|
+
|
|
+
|
|
+/** Writes data into a configuration buffer. Only valid inside
|
|
+ * the show function.
|
|
+ *
|
|
+ * @param buf Buffer to write data into.
|
|
+ * @param fmt printf style format string.
|
|
+ */
|
|
+
|
|
+void vcos_cfg_buf_printf( VCOS_CFG_BUF_T buf, const char *fmt, ... );
|
|
+
|
|
+/** Retrieves a null terminated string of the data associated
|
|
+ * with the buffer. Only valid inside the parse function.
|
|
+ *
|
|
+ * @param buf Buffer to get data from.
|
|
+ * @param fmt printf style format string.
|
|
+ */
|
|
+
|
|
+char *vcos_cfg_buf_get_str( VCOS_CFG_BUF_T buf );
|
|
+
|
|
+void *vcos_cfg_get_proc_entry( VCOS_CFG_ENTRY_T entry );
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+#endif
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_cmd.h
|
|
@@ -0,0 +1,98 @@
|
|
+/*****************************************************************************
|
|
+* Copyright 2009 - 2011 Broadcom Corporation. All rights reserved.
|
|
+*
|
|
+* Unless you and Broadcom execute a separate written software license
|
|
+* agreement governing use of this software, this software is licensed to you
|
|
+* under the terms of the GNU General Public License version 2, available at
|
|
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
|
+*
|
|
+* Notwithstanding the above, under no circumstances may you combine this
|
|
+* software in any way with any other Broadcom software provided under a
|
|
+* license other than the GPL, without Broadcom's express prior written
|
|
+* consent.
|
|
+*****************************************************************************/
|
|
+
|
|
+#if !defined( VCOS_CMD_H )
|
|
+#define VCOS_CMD_H
|
|
+
|
|
+/* ---- Include Files ----------------------------------------------------- */
|
|
+
|
|
+#include "interface/vcos/vcos.h"
|
|
+#include "interface/vcos/vcos_stdint.h"
|
|
+
|
|
+
|
|
+/* ---- Constants and Types ---------------------------------------------- */
|
|
+
|
|
+struct VCOS_CMD_S;
|
|
+typedef struct VCOS_CMD_S VCOS_CMD_T;
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ int argc; /* Number of arguments (includes the command/sub-command) */
|
|
+ char **argv; /* Array of arguments */
|
|
+ char **argv_orig; /* Original array of arguments */
|
|
+
|
|
+ VCOS_CMD_T *cmd_entry;
|
|
+ VCOS_CMD_T *cmd_parent_entry;
|
|
+
|
|
+ int use_log; /* Output being logged? */
|
|
+ size_t result_size; /* Size of result buffer. */
|
|
+ char *result_ptr; /* Next place to put output. */
|
|
+ char *result_buf; /* Start of the buffer. */
|
|
+
|
|
+} VCOS_CMD_PARAM_T;
|
|
+
|
|
+typedef VCOS_STATUS_T (*VCOS_CMD_FUNC_T)( VCOS_CMD_PARAM_T *param );
|
|
+
|
|
+struct VCOS_CMD_S
|
|
+{
|
|
+ const char *name;
|
|
+ const char *args;
|
|
+ VCOS_CMD_FUNC_T cmd_fn;
|
|
+ VCOS_CMD_T *sub_cmd_entry;
|
|
+ const char *descr;
|
|
+
|
|
+};
|
|
+
|
|
+/* ---- Variable Externs ------------------------------------------------- */
|
|
+
|
|
+/* ---- Function Prototypes ---------------------------------------------- */
|
|
+
|
|
+/*
|
|
+ * Common printing routine for generating command output.
|
|
+ */
|
|
+VCOSPRE_ void VCOSPOST_ vcos_cmd_error( VCOS_CMD_PARAM_T *param, const char *fmt, ... ) VCOS_FORMAT_ATTR_(printf, 2, 3);
|
|
+VCOSPRE_ void VCOSPOST_ vcos_cmd_printf( VCOS_CMD_PARAM_T *param, const char *fmt, ... ) VCOS_FORMAT_ATTR_(printf, 2, 3);
|
|
+VCOSPRE_ void VCOSPOST_ vcos_cmd_vprintf( VCOS_CMD_PARAM_T *param, const char *fmt, va_list args ) VCOS_FORMAT_ATTR_(printf, 2, 0);
|
|
+
|
|
+/*
|
|
+ * Cause vcos_cmd_error, printf and vprintf to always log to the provided
|
|
+ * category. When this call is made, the results buffer passed into
|
|
+ * vcos_cmd_execute is used as a line buffer and does not need to be
|
|
+ * output by the caller.
|
|
+ */
|
|
+VCOSPRE_ void VCOSPOST_ vcos_cmd_always_log_output( VCOS_LOG_CAT_T *log_category );
|
|
+
|
|
+/*
|
|
+ * Prints command usage for the current command.
|
|
+ */
|
|
+VCOSPRE_ void VCOSPOST_ vcos_cmd_usage( VCOS_CMD_PARAM_T *param );
|
|
+
|
|
+/*
|
|
+ * Register commands to be processed
|
|
+ */
|
|
+VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_cmd_register( VCOS_CMD_T *cmd_entry );
|
|
+
|
|
+/*
|
|
+ * Registers multiple commands to be processed. The array should
|
|
+ * be terminated by an entry with all zeros.
|
|
+ */
|
|
+VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_cmd_register_multiple( VCOS_CMD_T *cmd_entry );
|
|
+
|
|
+/*
|
|
+ * Executes a command based on a command line.
|
|
+ */
|
|
+VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_cmd_execute( int argc, char **argv, size_t result_size, char *result_buf );
|
|
+
|
|
+#endif /* VCOS_CMD_H */
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_ctype.h
|
|
@@ -0,0 +1,29 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - public header file
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_CTYPE_H
|
|
+#define VCOS_CTYPE_H
|
|
+
|
|
+/**
|
|
+ * \file
|
|
+ *
|
|
+ * ctype functions.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifdef __KERNEL__
|
|
+#include <linux/ctype.h>
|
|
+#else
|
|
+#include <ctype.h>
|
|
+#endif
|
|
+
|
|
+#endif
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_dlfcn.h
|
|
@@ -0,0 +1,69 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2010 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VCOS - abstraction over dynamic library opening
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_DLFCN_H
|
|
+#define VCOS_DLFCN_H
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+#include "vcos_platform.h"
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#define VCOS_DL_LAZY 1
|
|
+#define VCOS_DL_NOW 2
|
|
+
|
|
+/**
|
|
+ * \file
|
|
+ *
|
|
+ * Loading dynamic libraries. See also dlfcn.h.
|
|
+ */
|
|
+
|
|
+/** Open a dynamic library.
|
|
+ *
|
|
+ * @param name name of the library
|
|
+ * @param mode Load lazily or immediately (VCOS_DL_LAZY, VCOS_DL_NOW).
|
|
+ *
|
|
+ * @return A handle for use in subsequent calls.
|
|
+ */
|
|
+VCOSPRE_ void * VCOSPOST_ vcos_dlopen(const char *name, int mode);
|
|
+
|
|
+/** Look up a symbol.
|
|
+ *
|
|
+ * @param handle Handle to open
|
|
+ * @param name Name of function
|
|
+ *
|
|
+ * @return Function pointer, or NULL.
|
|
+ */
|
|
+VCOSPRE_ void VCOSPOST_ (*vcos_dlsym(void *handle, const char *name))(void);
|
|
+
|
|
+/** Close a library
|
|
+ *
|
|
+ * @param handle Handle to close
|
|
+ */
|
|
+VCOSPRE_ int VCOSPOST_ vcos_dlclose (void *handle);
|
|
+
|
|
+/** Return error message from library.
|
|
+ *
|
|
+ * @param err On return, set to non-zero if an error has occurred
|
|
+ * @param buf Buffer to write error to
|
|
+ * @param len Size of buffer (including terminating NUL).
|
|
+ */
|
|
+VCOSPRE_ int VCOSPOST_ vcos_dlerror(int *err, char *buf, size_t buflen);
|
|
+
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+#endif
|
|
+
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_event.h
|
|
@@ -0,0 +1,97 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - public header file for events
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_EVENT_H
|
|
+#define VCOS_EVENT_H
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+#include "vcos_platform.h"
|
|
+
|
|
+/**
|
|
+ * \file
|
|
+ *
|
|
+ * An event is akin to the Win32 auto-reset event.
|
|
+ *
|
|
+ *
|
|
+ * Signalling an event will wake up one waiting thread only. Once one
|
|
+ * thread has been woken the event atomically returns to the unsignalled
|
|
+ * state.
|
|
+ *
|
|
+ * If no threads are waiting on the event when it is signalled it remains
|
|
+ * signalled.
|
|
+ *
|
|
+ * This is almost, but not quite, completely unlike the "event flags"
|
|
+ * object based on Nucleus event groups and ThreadX event flags.
|
|
+ *
|
|
+ * In particular, it should be similar in speed to a semaphore, unlike
|
|
+ * the event flags.
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * Create an event instance.
|
|
+ *
|
|
+ * @param event Filled in with constructed event.
|
|
+ * @param name Name of the event (for debugging)
|
|
+ *
|
|
+ * @return VCOS_SUCCESS on success, or error code.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+VCOS_STATUS_T vcos_event_create(VCOS_EVENT_T *event, const char *name);
|
|
+
|
|
+#ifndef vcos_event_signal
|
|
+
|
|
+/**
|
|
+ * Signal the event. The event will return to being unsignalled
|
|
+ * after exactly one waiting thread has been woken up. If no
|
|
+ * threads are waiting it remains signalled.
|
|
+ *
|
|
+ * @param event The event to signal
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_event_signal(VCOS_EVENT_T *event);
|
|
+
|
|
+/**
|
|
+ * Wait for the event.
|
|
+ *
|
|
+ * @param event The event to wait for
|
|
+ * @return VCOS_SUCCESS on success, VCOS_EAGAIN if the wait was interrupted.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+VCOS_STATUS_T vcos_event_wait(VCOS_EVENT_T *event);
|
|
+
|
|
+/**
|
|
+ * Try event, but don't block.
|
|
+ *
|
|
+ * @param event The event to try
|
|
+ * @return VCOS_SUCCESS on success, VCOS_EAGAIN if the event is not currently signalled
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+VCOS_STATUS_T vcos_event_try(VCOS_EVENT_T *event);
|
|
+
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * Destroy an event.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_event_delete(VCOS_EVENT_T *event);
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+
|
|
+#endif
|
|
+
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_event_flags.h
|
|
@@ -0,0 +1,98 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - public header file
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_EVENT_FLAGS_H
|
|
+#define VCOS_EVENT_FLAGS_H
|
|
+
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+#include "vcos_platform.h"
|
|
+
|
|
+#define VCOS_EVENT_FLAGS_SUSPEND VCOS_SUSPEND
|
|
+#define VCOS_EVENT_FLAGS_NO_SUSPEND VCOS_NO_SUSPEND
|
|
+typedef VCOS_OPTION VCOS_EVENTGROUP_OPERATION_T;
|
|
+
|
|
+/**
|
|
+ * \file vcos_event_flags.h
|
|
+ *
|
|
+ * Defines event flags API.
|
|
+ *
|
|
+ * Similar to Nucleus event groups.
|
|
+ *
|
|
+ * These have the same semantics as Nucleus event groups and ThreadX event
|
|
+ * flags. As such, they are quite complex internally; if speed is important
|
|
+ * they might not be your best choice.
|
|
+ *
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * Create an event flags instance.
|
|
+ *
|
|
+ * @param flags Pointer to event flags instance, filled in on return.
|
|
+ * @param name Name for the event flags, used for debug.
|
|
+ *
|
|
+ * @return VCOS_SUCCESS if succeeded.
|
|
+ */
|
|
+
|
|
+VCOS_INLINE_DECL
|
|
+VCOS_STATUS_T vcos_event_flags_create(VCOS_EVENT_FLAGS_T *flags, const char *name);
|
|
+
|
|
+/**
|
|
+ * Set some events.
|
|
+ *
|
|
+ * @param flags Instance to set flags on
|
|
+ * @param events Bitmask of the flags to actually set
|
|
+ * @param op How the flags should be set. VCOS_OR will OR in the flags; VCOS_AND
|
|
+ * will AND them in, possibly clearing existing flags.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_event_flags_set(VCOS_EVENT_FLAGS_T *flags,
|
|
+ VCOS_UNSIGNED events,
|
|
+ VCOS_OPTION op);
|
|
+
|
|
+/**
|
|
+ * Retrieve some events.
|
|
+ *
|
|
+ * Waits until the specified events have been set.
|
|
+ *
|
|
+ * @param flags Instance to wait on
|
|
+ * @param requested_events The bitmask to wait for
|
|
+ * @param op VCOS_OR - get any; VCOS_AND - get all.
|
|
+ * @param ms_suspend How long to wait, in milliseconds
|
|
+ * @param retrieved_events the events actually retrieved.
|
|
+ *
|
|
+ * @return VCOS_SUCCESS if events were retrieved. VCOS_EAGAIN if the
|
|
+ * timeout expired.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+VCOS_STATUS_T vcos_event_flags_get(VCOS_EVENT_FLAGS_T *flags,
|
|
+ VCOS_UNSIGNED requested_events,
|
|
+ VCOS_OPTION op,
|
|
+ VCOS_UNSIGNED ms_suspend,
|
|
+ VCOS_UNSIGNED *retrieved_events);
|
|
+
|
|
+
|
|
+/**
|
|
+ * Delete an event flags instance.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_event_flags_delete(VCOS_EVENT_FLAGS_T *);
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+
|
|
+#endif
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_init.h
|
|
@@ -0,0 +1,43 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - initialization routines
|
|
+=============================================================================*/
|
|
+
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+#include "vcos_platform.h"
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+/** \file
|
|
+ *
|
|
+ * Some OS support libraries need some initialization. To support this, call this
|
|
+ * function at the start of day.
|
|
+ */
|
|
+
|
|
+VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_init(void);
|
|
+VCOSPRE_ void VCOSPOST_ vcos_deinit(void);
|
|
+VCOSPRE_ void VCOSPOST_ vcos_global_lock(void);
|
|
+VCOSPRE_ void VCOSPOST_ vcos_global_unlock(void);
|
|
+
|
|
+/** Pass in the argv/argc arguments passed to main() */
|
|
+VCOSPRE_ void VCOSPOST_ vcos_set_args(int argc, const char **argv);
|
|
+
|
|
+/** Return argc. */
|
|
+VCOSPRE_ int VCOSPOST_ vcos_get_argc(void);
|
|
+
|
|
+/** Return argv. */
|
|
+VCOSPRE_ const char ** VCOSPOST_ vcos_get_argv(void);
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_logging.h
|
|
@@ -0,0 +1,279 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009-2011 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - logging support
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_LOGGING_H
|
|
+#define VCOS_LOGGING_H
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include <stdarg.h>
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+#include "vcos_platform.h"
|
|
+
|
|
+/**
|
|
+ * \file
|
|
+ *
|
|
+ * Logging support
|
|
+ *
|
|
+ * This provides categorised logging. Clients register
|
|
+ * a category, and then get a number of logging levels for
|
|
+ * that category.
|
|
+ *
|
|
+ * The logging level flag is tested using a flag *before* the
|
|
+ * function call, which makes logging very fast when disabled - there
|
|
+ * is no function call overhead just to find out that this log
|
|
+ * message is disabled.
|
|
+ *
|
|
+ * \section VCOS_LOG_CATEGORY
|
|
+ *
|
|
+ * As a convenience, clients define VCOS_LOG_CATEGORY to point to
|
|
+ * their category; the various vcos_log_xxx() macros then expand to
|
|
+ * use this.
|
|
+ *
|
|
+ * e.g.
|
|
+ *
|
|
+ * #define VCOS_LOG_CATEGORY (&my_category)
|
|
+ *
|
|
+ * #include <interface/vcos/vcos.h>
|
|
+ *
|
|
+ * VCOS_LOG_CAT_T my_category;
|
|
+ *
|
|
+ * ....
|
|
+ *
|
|
+ * vcos_log_trace("Stuff happened: %d", n_stuff);
|
|
+ *
|
|
+ */
|
|
+
|
|
+/** Logging levels */
|
|
+typedef enum VCOS_LOG_LEVEL_T
|
|
+{
|
|
+ VCOS_LOG_UNINITIALIZED = 0,
|
|
+ VCOS_LOG_NEVER,
|
|
+ VCOS_LOG_ERROR,
|
|
+ VCOS_LOG_WARN,
|
|
+ VCOS_LOG_INFO,
|
|
+ VCOS_LOG_TRACE,
|
|
+} VCOS_LOG_LEVEL_T;
|
|
+
|
|
+
|
|
+/** Initialize a logging category without going through vcos_log_register().
|
|
+ *
|
|
+ * This is useful for the case where there is no obvious point to do the
|
|
+ * registration (no initialization function for the module). However, it
|
|
+ * means that your logging category is not registered, so cannot be easily
|
|
+ * changed at run-time.
|
|
+ */
|
|
+#define VCOS_LOG_INIT(n,l) { l, n, 0, {0}, 0, 0 }
|
|
+
|
|
+/** A registered logging category.
|
|
+ */
|
|
+typedef struct VCOS_LOG_CAT_T
|
|
+{
|
|
+ VCOS_LOG_LEVEL_T level; /** Which levels are enabled for this category */
|
|
+ const char *name; /** Name for this category. */
|
|
+ struct VCOS_LOG_CAT_T *next;
|
|
+ struct {
|
|
+ unsigned int want_prefix:1;
|
|
+ } flags;
|
|
+ unsigned int refcount;
|
|
+ void *platform_data; /** platform specific data */
|
|
+} VCOS_LOG_CAT_T;
|
|
+
|
|
+typedef void (*VCOS_VLOG_IMPL_FUNC_T)(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, va_list args);
|
|
+
|
|
+/** Convert a VCOS_LOG_LEVEL_T into a printable string.
|
|
+ * The platform needs to implement this function.
|
|
+ */
|
|
+VCOSPRE_ const char * VCOSPOST_ vcos_log_level_to_string( VCOS_LOG_LEVEL_T level );
|
|
+
|
|
+/** Convert a string into a VCOS_LOG_LEVEL_T
|
|
+ * The platform needs to implement this function.
|
|
+ */
|
|
+VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_string_to_log_level( const char *str, VCOS_LOG_LEVEL_T *level );
|
|
+
|
|
+/** Log a message. Basic API. Normal code should not use this.
|
|
+ * The platform needs to implement this function.
|
|
+ */
|
|
+VCOSPRE_ void VCOSPOST_ vcos_log_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, ...) VCOS_FORMAT_ATTR_(printf, 3, 4);
|
|
+
|
|
+/** Log a message using a varargs parameter list. Normal code should
|
|
+ * not use this.
|
|
+ */
|
|
+VCOSPRE_ void VCOSPOST_ vcos_vlog_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, va_list args) VCOS_FORMAT_ATTR_(printf, 3, 0);
|
|
+
|
|
+/** Set the function which does the actual logging output.
|
|
+ * Passing in NULL causes the default logging function to be
|
|
+ * used.
|
|
+ */
|
|
+VCOSPRE_ void VCOSPOST_ vcos_set_vlog_impl( VCOS_VLOG_IMPL_FUNC_T vlog_impl_func );
|
|
+
|
|
+/** The default logging function, which is provided by each
|
|
+ * platform.
|
|
+ */
|
|
+
|
|
+VCOSPRE_ void VCOSPOST_ vcos_vlog_default_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, va_list args) VCOS_FORMAT_ATTR_(printf, 3, 0);
|
|
+
|
|
+/*
|
|
+ * Initialise the logging subsystem. This is called from
|
|
+ * vcos_init() so you don't normally need to call it.
|
|
+ */
|
|
+VCOSPRE_ void VCOSPOST_ vcos_logging_init(void);
|
|
+
|
|
+/** Register a logging category.
|
|
+ *
|
|
+ * @param name the name of this category.
|
|
+ * @param category the category to register.
|
|
+ */
|
|
+VCOSPRE_ void VCOSPOST_ vcos_log_register(const char *name, VCOS_LOG_CAT_T *category);
|
|
+
|
|
+/** Unregister a logging category.
|
|
+ */
|
|
+VCOSPRE_ void VCOSPOST_ vcos_log_unregister(VCOS_LOG_CAT_T *category);
|
|
+
|
|
+/** Return a default logging category, for people too lazy to create their own.
|
|
+ *
|
|
+ * Using the default category will be slow (there's an extra function
|
|
+ * call overhead). Don't do this in normal code.
|
|
+ */
|
|
+VCOSPRE_ const VCOS_LOG_CAT_T * VCOSPOST_ vcos_log_get_default_category(void);
|
|
+
|
|
+VCOSPRE_ void VCOSPOST_ vcos_set_log_options(const char *opt);
|
|
+
|
|
+/** Set the logging level for a category at run time. Without this, the level
|
|
+ * will be that set by vcos_log_register from a platform-specific source.
|
|
+ *
|
|
+ * @param category the category to modify.
|
|
+ * @param level the new logging level for this category.
|
|
+ */
|
|
+VCOS_STATIC_INLINE void vcos_log_set_level(VCOS_LOG_CAT_T *category, VCOS_LOG_LEVEL_T level)
|
|
+{
|
|
+ category->level = level;
|
|
+}
|
|
+
|
|
+#define vcos_log_dump_mem(cat,label,addr,voidMem,numBytes) do { if (vcos_is_log_enabled(cat,VCOS_LOG_TRACE)) vcos_log_dump_mem_impl(cat,label,addr,voidMem,numBytes); } while (0)
|
|
+
|
|
+void vcos_log_dump_mem_impl( const VCOS_LOG_CAT_T *cat,
|
|
+ const char *label,
|
|
+ uint32_t addr,
|
|
+ const void *voidMem,
|
|
+ size_t numBytes );
|
|
+
|
|
+/*
|
|
+ * Platform specific hooks (optional).
|
|
+ */
|
|
+#ifndef vcos_log_platform_init
|
|
+#define vcos_log_platform_init() (void)0
|
|
+#endif
|
|
+
|
|
+#ifndef vcos_log_platform_register
|
|
+#define vcos_log_platform_register(category) (void)0
|
|
+#endif
|
|
+
|
|
+#ifndef vcos_log_platform_unregister
|
|
+#define vcos_log_platform_unregister(category) (void)0
|
|
+#endif
|
|
+
|
|
+/* VCOS_TRACE() - deprecated macro which just outputs in a debug build and
|
|
+ * is a no-op in a release build.
|
|
+ *
|
|
+ * _VCOS_LOG_X() - internal macro which outputs if the current level for the
|
|
+ * particular category is higher than the supplied message level.
|
|
+ */
|
|
+
|
|
+#define VCOS_LOG_DFLT_CATEGORY vcos_log_get_default_category()
|
|
+
|
|
+#define _VCOS_LEVEL(x) (x)
|
|
+
|
|
+#define vcos_is_log_enabled(cat,_level) (_VCOS_LEVEL((cat)->level) >= _VCOS_LEVEL(_level))
|
|
+
|
|
+#if defined(_VCOS_METAWARE) || defined(__GNUC__)
|
|
+
|
|
+# if !defined(NDEBUG) || defined(VCOS_ALWAYS_WANT_LOGGING)
|
|
+# define VCOS_LOGGING_ENABLED
|
|
+# define _VCOS_LOG_X(cat, _level, fmt...) do { if (vcos_is_log_enabled(cat,_level)) vcos_log_impl(cat,_level,fmt); } while (0)
|
|
+# define _VCOS_VLOG_X(cat, _level, fmt, ap) do { if (vcos_is_log_enabled(cat,_level)) vcos_vlog_impl(cat,_level,fmt,ap); } while (0)
|
|
+# else
|
|
+# define _VCOS_LOG_X(cat, _level, fmt...) (void)0
|
|
+# define _VCOS_VLOG_X(cat, _level, fmt, ap) (void)0
|
|
+# endif
|
|
+
|
|
+
|
|
+
|
|
+# define vcos_log_error(...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_ERROR, __VA_ARGS__)
|
|
+# define vcos_log_warn(...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_WARN, __VA_ARGS__)
|
|
+# define vcos_log_info(...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_INFO, __VA_ARGS__)
|
|
+# define vcos_log_trace(...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_TRACE, __VA_ARGS__)
|
|
+
|
|
+# define vcos_vlog_error(fmt,ap) _VCOS_VLOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_ERROR, fmt, ap)
|
|
+# define vcos_vlog_warn(fmt,ap) _VCOS_VLOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_WARN, fmt, ap)
|
|
+# define vcos_vlog_info(fmt,ap) _VCOS_VLOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_INFO, fmt, ap)
|
|
+# define vcos_vlog_trace(fmt,ap) _VCOS_VLOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_TRACE, fmt, ap)
|
|
+
|
|
+# define vcos_log(...) _VCOS_LOG_X(VCOS_LOG_DFLT_CATEGORY, VCOS_LOG_INFO, __VA_ARGS__)
|
|
+# define vcos_vlog(fmt,ap) _VCOS_VLOG_X(VCOS_LOG_DFLT_CATEGORY, VCOS_LOG_INFO, fmt, ap)
|
|
+# define VCOS_ALERT(...) _VCOS_LOG_X(VCOS_LOG_DFLT_CATEGORY, VCOS_LOG_ERROR, __VA_ARGS__)
|
|
+# define VCOS_TRACE(...) _VCOS_LOG_X(VCOS_LOG_DFLT_CATEGORY, VCOS_LOG_INFO, __VA_ARGS__)
|
|
+
|
|
+/*
|
|
+ * MS Visual Studio - pre 2005 does not grok variadic macros
|
|
+ */
|
|
+#elif defined(_MSC_VER)
|
|
+
|
|
+# if _MSC_VER >= 1400
|
|
+
|
|
+# if !defined(NDEBUG) || defined(VCOS_ALWAYS_WANT_LOGGING)
|
|
+# define VCOS_LOGGING_ENABLED
|
|
+# define _VCOS_LOG_X(cat, _level, fmt,...) do { if (vcos_is_log_enabled(cat,_level)) vcos_log_impl(cat, _level, fmt, __VA_ARGS__); } while (0)
|
|
+# else
|
|
+# define _VCOS_LOG_X(cat, _level, fmt,...) (void)0
|
|
+# endif
|
|
+
|
|
+# define vcos_log_error(fmt,...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_ERROR, fmt, __VA_ARGS__)
|
|
+# define vcos_log_warn(fmt,...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_WARN, fmt, __VA_ARGS__)
|
|
+# define vcos_log_info(fmt,...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_INFO, fmt, __VA_ARGS__)
|
|
+# define vcos_log_trace(fmt,...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_TRACE, fmt, __VA_ARGS__)
|
|
+
|
|
+# define vcos_log(fmt,...) _VCOS_LOG_X(VCOS_LOG_DFLT_CATEGORY, VCOS_LOG_INFO, fmt)
|
|
+# define VCOS_ALERT(fmt,...) _VCOS_LOG_X(VCOS_LOG_DFLT_CATEGORY, VCOS_LOG_ERROR, fmt)
|
|
+# define VCOS_TRACE(fmt,...) _VCOS_LOG_X(VCOS_LOG_DFLT_CATEGORY, VCOS_LOG_INFO, fmt)
|
|
+
|
|
+# else /* _MSC_VER >= 1400 */
|
|
+
|
|
+/* do not define these */
|
|
+
|
|
+# endif /* _MSC_VER >= 1400 */
|
|
+
|
|
+#endif
|
|
+
|
|
+#if VCOS_HAVE_CMD
|
|
+
|
|
+#include "interface/vcos/vcos_cmd.h"
|
|
+
|
|
+/*
|
|
+ * These are the log sub-commands. They're exported here for user-mode apps which
|
|
+ * may want to call these, since the "log" command isn't registered for user-mode
|
|
+ * apps (vcdbg for example, has its own log command).
|
|
+ */
|
|
+VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_log_assert_cmd( VCOS_CMD_PARAM_T *param );
|
|
+VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_log_set_cmd( VCOS_CMD_PARAM_T *param );
|
|
+VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_log_status_cmd( VCOS_CMD_PARAM_T *param );
|
|
+VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_log_test_cmd( VCOS_CMD_PARAM_T *param );
|
|
+#endif
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+#endif /* VCOS_LOGGING_H */
|
|
+
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_lowlevel_thread.h
|
|
@@ -0,0 +1,107 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - low level thread support
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_LOWLEVEL_THREAD_H
|
|
+#define VCOS_LOWLEVEL_THREAD_H
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+#include "vcos_platform.h"
|
|
+
|
|
+/**
|
|
+ * \file
|
|
+ *
|
|
+ * This defines a low level thread API that is supported by *some* operating systems
|
|
+ * and can be used to construct the regular "joinable thread" API on those operating
|
|
+ * systems.
|
|
+ *
|
|
+ * Most clients will not need to use this code.
|
|
+ *
|
|
+ * \sa vcos_joinable_thread.h
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * \brief Create a thread.
|
|
+ *
|
|
+ * This creates a thread which can be stopped either by returning from the
|
|
+ * entry point function or by calling vcos_llthread_exit from within the entry
|
|
+ * point function. The thread must be cleaned up by calling
|
|
+ * vcos_llthread_delete. vcos_llthread_delete may or may not terminate the
|
|
+ * thread.
|
|
+ *
|
|
+ * The preemptible parameter familiar from Nucleus is removed, as it is unused in
|
|
+ * VideoCore code. Affinity is added, since we do use this.
|
|
+ *
|
|
+ * @param thread Filled in with thread instance
|
|
+ * @param name An optional name for the thread. "" may be used (but
|
|
+ * a name will aid in debugging).
|
|
+ * @param entry Entry point
|
|
+ * @param arg A single argument passed to the entry point function
|
|
+ * @param stack Pointer to stack address
|
|
+ * @param stacksz Size of stack in bytes
|
|
+ * @param priority Priority of task, between VCOS_PRI_LOW and VCOS_PRI_HIGH
|
|
+ * @param affinity CPU affinity
|
|
+ *
|
|
+ * @sa vcos_llthread_terminate vcos_llthread_delete
|
|
+ */
|
|
+VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_llthread_create(VCOS_LLTHREAD_T *thread,
|
|
+ const char *name,
|
|
+ VCOS_LLTHREAD_ENTRY_FN_T entry,
|
|
+ void *arg,
|
|
+ void *stack,
|
|
+ VCOS_UNSIGNED stacksz,
|
|
+ VCOS_UNSIGNED priority,
|
|
+ VCOS_UNSIGNED affinity,
|
|
+ VCOS_UNSIGNED timeslice,
|
|
+ VCOS_UNSIGNED autostart);
|
|
+
|
|
+/**
|
|
+ * \brief Exits the current thread.
|
|
+ */
|
|
+VCOSPRE_ void VCOSPOST_ vcos_llthread_exit(void);
|
|
+
|
|
+/**
|
|
+ * \brief Delete a thread. This must be called to cleanup after
|
|
+ * vcos_llthread_create. This may or may not terminate the thread.
|
|
+ * It does not clean up any resources that may have been
|
|
+ * allocated by the thread.
|
|
+ */
|
|
+VCOSPRE_ void VCOSPOST_ vcos_llthread_delete(VCOS_LLTHREAD_T *thread);
|
|
+
|
|
+/**
|
|
+ * \brief Return current lowlevel thread pointer.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+VCOS_LLTHREAD_T *vcos_llthread_current(void);
|
|
+
|
|
+/**
|
|
+ * Resume a thread.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_llthread_resume(VCOS_LLTHREAD_T *thread);
|
|
+
|
|
+VCOSPRE_ int VCOSPOST_ vcos_llthread_running(VCOS_LLTHREAD_T *thread);
|
|
+
|
|
+/**
|
|
+ * \brief Create a VCOS_LLTHREAD_T for the current thread. This is so we can
|
|
+ * have VCOS_LLTHREAD_Ts even for threads not originally created by VCOS (eg
|
|
+ * the thread that calls vcos_init).
|
|
+ */
|
|
+extern VCOS_STATUS_T _vcos_llthread_create_attach(VCOS_LLTHREAD_T *thread);
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+#endif
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_mem.h
|
|
@@ -0,0 +1,81 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - memory support
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_MEM_H
|
|
+#define VCOS_MEM_H
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+#include "vcos_platform.h"
|
|
+
|
|
+/** \file
|
|
+ *
|
|
+ * Memory allocation api (malloc/free equivalents) is for benefit of host
|
|
+ * applications. VideoCore code should use rtos_XXX functions.
|
|
+ *
|
|
+ */
|
|
+
|
|
+
|
|
+/** Allocate memory
|
|
+ *
|
|
+ * @param size Size of memory to allocate
|
|
+ * @param description Description, to aid in debugging. May be ignored internally on some platforms.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void *vcos_malloc(VCOS_UNSIGNED size, const char *description);
|
|
+
|
|
+void *vcos_kmalloc(VCOS_UNSIGNED size, const char *description);
|
|
+void *vcos_kcalloc(VCOS_UNSIGNED num, VCOS_UNSIGNED size, const char *description);
|
|
+
|
|
+/** Allocate cleared memory
|
|
+ *
|
|
+ * @param num Number of items to allocate.
|
|
+ * @param size Size of each item in bytes.
|
|
+ * @param description Description, to aid in debugging. May be ignored internally on some platforms.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void *vcos_calloc(VCOS_UNSIGNED num, VCOS_UNSIGNED size, const char *description);
|
|
+
|
|
+/** Free memory
|
|
+ *
|
|
+ * Free memory that has been allocated.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_free(void *ptr);
|
|
+
|
|
+void vcos_kfree(void *ptr);
|
|
+
|
|
+/** Allocate aligned memory
|
|
+ *
|
|
+ * Allocate memory aligned on the specified boundary.
|
|
+ *
|
|
+ * @param size Size of memory to allocate
|
|
+ * @param description Description, to aid in debugging. May be ignored internally on some platforms.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void *vcos_malloc_aligned(VCOS_UNSIGNED size, VCOS_UNSIGNED align, const char *description);
|
|
+
|
|
+/** Return the amount of free heap memory
|
|
+ *
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+unsigned long vcos_get_free_mem(void);
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+
|
|
+#endif
|
|
+
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_msgqueue.h
|
|
@@ -0,0 +1,157 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VCOS - packet-like messages, based loosely on those found in TRIPOS.
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_MSGQUEUE_H
|
|
+#define VCOS_MSGQUEUE_H
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+#include "vcos_platform.h"
|
|
+
|
|
+/**
|
|
+ * \file
|
|
+ *
|
|
+ * Packet-like messages, based loosely on those found in TRIPOS and
|
|
+ * derivatives thereof.
|
|
+ *
|
|
+ * A task can send a message *pointer* to another task, where it is
|
|
+ * queued on a linked list and the task woken up. The receiving task
|
|
+ * consumes all of the messages on its input queue, and optionally
|
|
+ * sends back replies using the original message memory.
|
|
+ *
|
|
+ * A caller can wait for the reply to a specific message - any other
|
|
+ * messages that arrive in the meantime are queued separately.
|
|
+ *
|
|
+ *
|
|
+ * All messages have a standard common layout, but the payload area can
|
|
+ * be used freely to extend this.
|
|
+ */
|
|
+
|
|
+/** Map the payload portion of a message to a structure pointer.
|
|
+ */
|
|
+#define VCOS_MSG_DATA(_msg) (void*)((_msg)->data)
|
|
+
|
|
+/** Standard message ids - FIXME - these need to be done properly! */
|
|
+#define VCOS_MSG_N_QUIT 1
|
|
+#define VCOS_MSG_N_OPEN 2
|
|
+#define VCOS_MSG_N_CLOSE 3
|
|
+#define VCOS_MSG_N_PRIVATE (1<<20)
|
|
+
|
|
+#define VCOS_MSG_REPLY_BIT (1<<31)
|
|
+
|
|
+/** Make gnuc compiler be happy about pointer punning */
|
|
+#ifdef __GNUC__
|
|
+#define __VCOS_MAY_ALIAS __attribute__((__may_alias__))
|
|
+#else
|
|
+#define __VCOS_MAY_ALIAS
|
|
+#endif
|
|
+
|
|
+/** A single message queue.
|
|
+ */
|
|
+typedef struct VCOS_MSGQUEUE_T
|
|
+{
|
|
+ struct VCOS_MSG_T *head; /**< head of linked list of messages waiting on this queue */
|
|
+ struct VCOS_MSG_T *tail; /**< tail of message queue */
|
|
+ VCOS_SEMAPHORE_T sem; /**< thread waits on this for new messages */
|
|
+ VCOS_MUTEX_T lock; /**< locks the messages list */
|
|
+} VCOS_MSGQUEUE_T;
|
|
+
|
|
+/** A single message
|
|
+ */
|
|
+typedef struct VCOS_MSG_T
|
|
+{
|
|
+ uint32_t code; /**< message code */
|
|
+ int error; /**< error status signalled back to caller */
|
|
+ VCOS_MSGQUEUE_T *dst; /**< destination queue */
|
|
+ VCOS_MSGQUEUE_T *src; /**< source; replies go back to here */
|
|
+ struct VCOS_MSG_T *next; /**< next in queue */
|
|
+ VCOS_THREAD_T *src_thread; /**< for debug */
|
|
+ uint32_t data[25]; /**< payload area */
|
|
+} VCOS_MSG_T;
|
|
+
|
|
+/** An endpoint
|
|
+ */
|
|
+typedef struct VCOS_MSG_ENDPOINT_T
|
|
+{
|
|
+ VCOS_MSGQUEUE_T primary; /**< incoming messages */
|
|
+ VCOS_MSGQUEUE_T secondary; /**< this is used for waitspecific */
|
|
+ char name[32]; /**< name of this endpoint, for find() */
|
|
+ struct VCOS_MSG_ENDPOINT_T *next; /**< next in global list of endpoints */
|
|
+} VCOS_MSG_ENDPOINT_T;
|
|
+#define MSG_REPLY_BIT (1<<31)
|
|
+
|
|
+/** Initalise the library. Normally called from vcos_init().
|
|
+ */
|
|
+extern VCOS_STATUS_T vcos_msgq_init(void);
|
|
+
|
|
+/** Find a message queue by name and get a handle to it.
|
|
+ *
|
|
+ * @param name the name of the queue to find
|
|
+ *
|
|
+ * @return The message queue, or NULL if not found.
|
|
+ */
|
|
+VCOSPRE_ VCOS_MSGQUEUE_T VCOSPOST_ *vcos_msgq_find(const char *name);
|
|
+
|
|
+/** Wait for a message queue to come into existence. If it already exists,
|
|
+ * return immediately, otherwise block.
|
|
+ *
|
|
+ * On the whole, if you find yourself using this, it is probably a sign
|
|
+ * of poor design, since you should create all the server threads first,
|
|
+ * and then the client threads. But it is sometimes useful.
|
|
+ *
|
|
+ * @param name the name of the queue to find
|
|
+ * @return The message queue
|
|
+ */
|
|
+VCOSPRE_ VCOS_MSGQUEUE_T VCOSPOST_ *vcos_msgq_wait(const char *name);
|
|
+
|
|
+/** Send a message.
|
|
+ */
|
|
+VCOSPRE_ void VCOSPOST_ vcos_msg_send(VCOS_MSGQUEUE_T *dest, uint32_t code, VCOS_MSG_T *msg);
|
|
+
|
|
+/** Send a message and wait for a reply.
|
|
+ */
|
|
+VCOSPRE_ void VCOSPOST_ vcos_msg_sendwait(VCOS_MSGQUEUE_T *queue, uint32_t code, VCOS_MSG_T *msg);
|
|
+
|
|
+/** Wait for a message on this thread's endpoint.
|
|
+ */
|
|
+VCOSPRE_ VCOS_MSG_T * VCOSPOST_ vcos_msg_wait(void);
|
|
+
|
|
+/** Wait for a specific message.
|
|
+ */
|
|
+VCOS_MSG_T * vcos_msg_wait_specific(VCOS_MSGQUEUE_T *queue, VCOS_MSG_T *msg);
|
|
+
|
|
+/** Peek for a message on this thread's endpoint, if a message is not available, NULL is
|
|
+ returned. If a message is available it will be removed from the endpoint and returned.
|
|
+ */
|
|
+VCOSPRE_ VCOS_MSG_T * VCOSPOST_ vcos_msg_peek(void);
|
|
+
|
|
+/** Send a reply to a message
|
|
+ */
|
|
+VCOSPRE_ void VCOSPOST_ vcos_msg_reply(VCOS_MSG_T *msg);
|
|
+
|
|
+/** Create an endpoint. Each thread should need no more than one of these - if you
|
|
+ * find yourself needing a second one, you've done something wrong.
|
|
+ */
|
|
+VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_msgq_endpoint_create(VCOS_MSG_ENDPOINT_T *ep, const char *name);
|
|
+
|
|
+/** Destroy an endpoint.
|
|
+ */
|
|
+VCOSPRE_ void VCOSPOST_ vcos_msgq_endpoint_delete(VCOS_MSG_ENDPOINT_T *ep);
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+#endif
|
|
+
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_mutex.h
|
|
@@ -0,0 +1,92 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - mutex public header file
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_MUTEX_H
|
|
+#define VCOS_MUTEX_H
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+#include "vcos_platform.h"
|
|
+
|
|
+/**
|
|
+ * \file vcos_mutex.h
|
|
+ *
|
|
+ * Mutex API. Mutexes are not re-entrant, as supporting this adds extra code
|
|
+ * that slows down clients which have been written sensibly.
|
|
+ *
|
|
+ * \sa vcos_reentrant_mutex.h
|
|
+ *
|
|
+ */
|
|
+
|
|
+/** Create a mutex.
|
|
+ *
|
|
+ * @param m Filled in with mutex on return
|
|
+ * @param name A non-null name for the mutex, used for diagnostics.
|
|
+ *
|
|
+ * @return VCOS_SUCCESS if mutex was created, or error code.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+VCOS_STATUS_T vcos_mutex_create(VCOS_MUTEX_T *m, const char *name);
|
|
+
|
|
+/** Delete the mutex.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_mutex_delete(VCOS_MUTEX_T *m);
|
|
+
|
|
+/**
|
|
+ * \brief Wait to claim the mutex.
|
|
+ *
|
|
+ * On most platforms this always returns VCOS_SUCCESS, and so would ideally be
|
|
+ * a void function, however some platforms allow a wait to be interrupted so
|
|
+ * it remains non-void.
|
|
+ *
|
|
+ * Try to obtain the mutex.
|
|
+ * @param m Mutex to wait on
|
|
+ * @return VCOS_SUCCESS - mutex was taken.
|
|
+ * VCOS_EAGAIN - could not take mutex.
|
|
+ */
|
|
+#ifndef vcos_mutex_lock
|
|
+VCOS_INLINE_DECL
|
|
+VCOS_STATUS_T vcos_mutex_lock(VCOS_MUTEX_T *m);
|
|
+
|
|
+/** Release the mutex.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_mutex_unlock(VCOS_MUTEX_T *m);
|
|
+#endif
|
|
+
|
|
+/** Test if the mutex is already locked.
|
|
+ *
|
|
+ * @return 1 if mutex is locked, 0 if it is unlocked.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+int vcos_mutex_is_locked(VCOS_MUTEX_T *m);
|
|
+
|
|
+/** Obtain the mutex if possible.
|
|
+ *
|
|
+ * @param m the mutex to try to obtain
|
|
+ *
|
|
+ * @return VCOS_SUCCESS if mutex is succesfully obtained, or VCOS_EAGAIN
|
|
+ * if it is already in use by another thread.
|
|
+ */
|
|
+#ifndef vcos_mutex_trylock
|
|
+VCOS_INLINE_DECL
|
|
+VCOS_STATUS_T vcos_mutex_trylock(VCOS_MUTEX_T *m);
|
|
+#endif
|
|
+
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+#endif
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_once.h
|
|
@@ -0,0 +1,42 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2011 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - 'once'
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_ONCE_H
|
|
+#define VCOS_ONCE_H
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+#include "vcos_platform.h"
|
|
+
|
|
+/**
|
|
+ * \file vcos_once.h
|
|
+ *
|
|
+ * Ensure something is called only once.
|
|
+ *
|
|
+ * Initialize once_control to VCOS_ONCE_INIT. The first
|
|
+ * time this is called, the init_routine will be called. Thereafter
|
|
+ * it won't.
|
|
+ *
|
|
+ * \sa pthread_once()
|
|
+ *
|
|
+ */
|
|
+
|
|
+VCOS_STATUS_T vcos_once(VCOS_ONCE_T *once_control,
|
|
+ void (*init_routine)(void));
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+#endif
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_semaphore.h
|
|
@@ -0,0 +1,115 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - public header file
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_SEMAPHORE_H
|
|
+#define VCOS_SEMAPHORE_H
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+#include "vcos_platform.h"
|
|
+
|
|
+/**
|
|
+ * \file vcos_semaphore.h
|
|
+ *
|
|
+ * \section sem Semaphores
|
|
+ *
|
|
+ * This provides counting semaphores. Semaphores are not re-entrant. On sensible
|
|
+ * operating systems a semaphore can always be posted but can only be taken in
|
|
+ * thread (not interrupt) context. Under Nucleus, a LISR cannot post a semaphore,
|
|
+ * although it would not be hard to lift this restriction.
|
|
+ *
|
|
+ * \subsection timeout Timeout
|
|
+ *
|
|
+ * On both Nucleus and ThreadX a semaphore can be taken with a timeout. This is
|
|
+ * not supported by VCOS because it makes the non-timeout code considerably more
|
|
+ * complicated (and hence slower). In the unlikely event that you need a timeout
|
|
+ * with a semaphore, and you cannot simply redesign your code to avoid it, use
|
|
+ * an event flag (vcos_event_flags.h).
|
|
+ *
|
|
+ * \subsection sem_nucleus Changes from Nucleus:
|
|
+ *
|
|
+ * Semaphores are always "FIFO" - i.e. sleeping threads are woken in FIFO order. That's
|
|
+ * because:
|
|
+ * \arg there's no support for NU_PRIORITY in threadx (though it can be emulated, slowly)
|
|
+ * \arg we don't appear to actually consciously use it - for example, Dispmanx uses
|
|
+ * it, but all threads waiting are the same priority.
|
|
+ *
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * \brief Create a semaphore.
|
|
+ *
|
|
+ * Create a semaphore.
|
|
+ *
|
|
+ * @param sem Pointer to memory to be initialized
|
|
+ * @param name A name for this semaphore. The name may be truncated internally.
|
|
+ * @param count The initial count for the semaphore.
|
|
+ *
|
|
+ * @return VCOS_SUCCESS if the semaphore was created.
|
|
+ *
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+VCOS_STATUS_T vcos_semaphore_create(VCOS_SEMAPHORE_T *sem, const char *name, VCOS_UNSIGNED count);
|
|
+
|
|
+/**
|
|
+ * \brief Wait on a semaphore.
|
|
+ *
|
|
+ * There is no timeout option on a semaphore, as adding this will slow down
|
|
+ * implementations on some platforms. If you need that kind of behaviour, use
|
|
+ * an event group.
|
|
+ *
|
|
+ * On most platforms this always returns VCOS_SUCCESS, and so would ideally be
|
|
+ * a void function, however some platforms allow a wait to be interrupted so
|
|
+ * it remains non-void.
|
|
+ *
|
|
+ * @param sem Semaphore to wait on
|
|
+ * @return VCOS_SUCCESS - semaphore was taken.
|
|
+ * VCOS_EAGAIN - could not take semaphore
|
|
+ *
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+VCOS_STATUS_T vcos_semaphore_wait(VCOS_SEMAPHORE_T *sem);
|
|
+
|
|
+/**
|
|
+ * \brief Try to wait for a semaphore.
|
|
+ *
|
|
+ * Try to obtain the semaphore. If it is already taken, return VCOS_TIMEOUT.
|
|
+ * @param sem Semaphore to wait on
|
|
+ * @return VCOS_SUCCESS - semaphore was taken.
|
|
+ * VCOS_EAGAIN - could not take semaphore
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+VCOS_STATUS_T vcos_semaphore_trywait(VCOS_SEMAPHORE_T *sem);
|
|
+
|
|
+/**
|
|
+ * \brief Post a semaphore.
|
|
+ *
|
|
+ * @param sem Semaphore to wait on
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+VCOS_STATUS_T vcos_semaphore_post(VCOS_SEMAPHORE_T *sem);
|
|
+
|
|
+/**
|
|
+ * \brief Delete a semaphore, releasing any resources consumed by it.
|
|
+ *
|
|
+ * @param sem Semaphore to wait on
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_semaphore_delete(VCOS_SEMAPHORE_T *sem);
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+#endif
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_stdbool.h
|
|
@@ -0,0 +1,17 @@
|
|
+#ifndef VCOS_STDBOOL_H
|
|
+#define VCOS_STDBOOL_H
|
|
+
|
|
+#ifndef __cplusplus
|
|
+
|
|
+#if defined(__STDC__) && (__STDC_VERSION__ >= 199901L)
|
|
+#include <stdbool.h>
|
|
+#else
|
|
+typedef enum {
|
|
+ false,
|
|
+ true
|
|
+} bool;
|
|
+#endif
|
|
+
|
|
+#endif /* __cplusplus */
|
|
+
|
|
+#endif
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_stdint.h
|
|
@@ -0,0 +1,193 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2011 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+FILE DESCRIPTION
|
|
+
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_STDINT_H
|
|
+#define VCOS_STDINT_H
|
|
+
|
|
+/* Attempt to provide the types defined in stdint.h.
|
|
+ *
|
|
+ * Ideally this would either call out to a platform-specific
|
|
+ * header file (e.g. stdint.h) or define the types on a
|
|
+ * per-architecture/compiler basis. But for now we just
|
|
+ * use #ifdefs.
|
|
+ */
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#ifdef __SYMBIAN32__
|
|
+
|
|
+typedef signed char int8_t;
|
|
+typedef unsigned char uint8_t;
|
|
+
|
|
+typedef signed short int16_t;
|
|
+typedef unsigned short uint16_t;
|
|
+
|
|
+typedef int16_t int_least16_t;
|
|
+
|
|
+typedef signed long int32_t;
|
|
+typedef unsigned long uint32_t;
|
|
+
|
|
+typedef signed long long int64_t;
|
|
+typedef unsigned long long uint64_t;
|
|
+
|
|
+typedef int32_t intptr_t;
|
|
+typedef uint32_t uintptr_t;
|
|
+
|
|
+typedef int64_t intmax_t;
|
|
+typedef uint64_t uintmax_t;
|
|
+
|
|
+#define INT8_MIN SCHAR_MIN
|
|
+#define INT8_MAX SCHAR_MAX
|
|
+#define UINT8_MAX UCHAR_MAX
|
|
+#define INT16_MIN SHRT_MIN
|
|
+#define INT16_MAX SHRT_MAX
|
|
+#define UINT16_MAX USHRT_MAX
|
|
+#define INT32_MIN LONG_MIN
|
|
+#define INT32_MAX LONG_MAX
|
|
+#define UINT32_MAX ULONG_MAX
|
|
+#define INT64_MIN LLONG_MIN
|
|
+#define INT64_MAX LLONG_MAX
|
|
+#define UINT64_MAX ULLONG_MAX
|
|
+
|
|
+#define INTPTR_MIN INT32_MIN
|
|
+#define INTPTR_MAX INT32_MAX
|
|
+#define UINTPTR_MAX UINT32_MAX
|
|
+#define INTMAX_MIN INT64_MIN
|
|
+#define INTMAX_MAX INT64_MAX
|
|
+#define INT_LEAST16_MAX INT16_MAX
|
|
+#define INT_LEAST16_MAX INT16_MAX
|
|
+
|
|
+/*{{{ C99 types - THIS WHOLE SECTION IS INCOMPATIBLE WITH C99. IT SHOULD RESIDE IN A STDINT.H SINCE THIS FILE GETS USED ON HOST SIDE */
|
|
+
|
|
+#elif defined( __STDC__ ) && __STDC_VERSION__ >= 199901L
|
|
+
|
|
+#include <stdint.h>
|
|
+
|
|
+#elif defined( __GNUC__ )
|
|
+
|
|
+#include <stdint.h>
|
|
+
|
|
+#elif defined(_MSC_VER) /* Visual C define equivalent types */
|
|
+
|
|
+#include <stddef.h> /* Avoids intptr_t being defined in vadefs.h */
|
|
+
|
|
+typedef __int8 int8_t;
|
|
+typedef unsigned __int8 uint8_t;
|
|
+
|
|
+typedef __int16 int16_t;
|
|
+typedef unsigned __int16 uint16_t;
|
|
+
|
|
+typedef __int32 int32_t;
|
|
+typedef unsigned __int32 uint32_t;
|
|
+
|
|
+typedef __int64 int64_t;
|
|
+typedef unsigned __int64 uint64_t;
|
|
+typedef uint32_t uintptr_t;
|
|
+typedef int64_t intmax_t;
|
|
+typedef uint64_t uintmax_t;
|
|
+typedef int16_t int_least16_t;
|
|
+
|
|
+#elif defined (VCMODS_LCC)
|
|
+#include <limits.h>
|
|
+
|
|
+typedef signed char int8_t;
|
|
+typedef unsigned char uint8_t;
|
|
+
|
|
+typedef signed short int16_t;
|
|
+typedef unsigned short uint16_t;
|
|
+
|
|
+typedef signed long int32_t;
|
|
+typedef unsigned long uint32_t;
|
|
+
|
|
+typedef signed long int64_t; /*!!!! PFCD, this means code using 64bit numbers will be broken on the VCE */
|
|
+typedef unsigned long uint64_t; /* !!!! PFCD */
|
|
+
|
|
+typedef int32_t intptr_t;
|
|
+typedef uint32_t uintptr_t;
|
|
+typedef int64_t intmax_t;
|
|
+typedef uint64_t uintmax_t;
|
|
+typedef int16_t int_least16_t;
|
|
+
|
|
+#define INT8_MIN SCHAR_MIN
|
|
+#define INT8_MAX SCHAR_MAX
|
|
+#define UINT8_MAX UCHAR_MAX
|
|
+#define INT16_MIN SHRT_MIN
|
|
+#define INT16_MAX SHRT_MAX
|
|
+#define UINT16_MAX USHRT_MAX
|
|
+#define INT32_MIN LONG_MIN
|
|
+#define INT32_MAX LONG_MAX
|
|
+#define UINT32_MAX ULONG_MAX
|
|
+#define INT64_MIN LONG_MIN /* !!!! PFCD */
|
|
+#define INT64_MAX LONG_MAX /* !!!! PFCD */
|
|
+#define UINT64_MAX ULONG_MAX /* !!!! PFCD */
|
|
+
|
|
+#define INTPTR_MIN INT32_MIN
|
|
+#define INTPTR_MAX INT32_MAX
|
|
+#define UINTPTR_MAX UINT32_MAX
|
|
+#define INTMAX_MIN INT64_MIN
|
|
+#define INTMAX_MIN INT64_MIN
|
|
+#define INT_LEAST16_MAX INT16_MAX
|
|
+#define INT_LEAST16_MAX INT16_MAX
|
|
+
|
|
+#elif defined(__VIDEOCORE__)
|
|
+
|
|
+typedef signed char int8_t;
|
|
+typedef unsigned char uint8_t;
|
|
+
|
|
+typedef signed short int16_t;
|
|
+typedef unsigned short uint16_t;
|
|
+
|
|
+typedef signed long int32_t;
|
|
+typedef unsigned long uint32_t;
|
|
+
|
|
+typedef signed long long int64_t;
|
|
+typedef unsigned long long uint64_t;
|
|
+
|
|
+typedef int32_t intptr_t;
|
|
+typedef uint32_t uintptr_t;
|
|
+typedef int64_t intmax_t;
|
|
+typedef uint64_t uintmax_t;
|
|
+typedef int16_t int_least16_t;
|
|
+
|
|
+#define INT8_MIN SCHAR_MIN
|
|
+#define INT8_MAX SCHAR_MAX
|
|
+#define UINT8_MAX UCHAR_MAX
|
|
+#define INT16_MIN SHRT_MIN
|
|
+#define INT16_MAX SHRT_MAX
|
|
+#define UINT16_MAX USHRT_MAX
|
|
+#define INT32_MIN LONG_MIN
|
|
+#define INT32_MAX LONG_MAX
|
|
+#define UINT32_MAX ULONG_MAX
|
|
+#define INT64_MIN LLONG_MIN
|
|
+#define INT64_MAX LLONG_MAX
|
|
+#define UINT64_MAX ULLONG_MAX
|
|
+
|
|
+#define INTPTR_MIN INT32_MIN
|
|
+#define INTPTR_MAX INT32_MAX
|
|
+#define UINTPTR_MAX UINT32_MAX
|
|
+#define INTMAX_MIN INT64_MIN
|
|
+#define INTMAX_MAX INT64_MAX
|
|
+#define INT_LEAST16_MAX INT16_MAX
|
|
+#define INT_LEAST16_MAX INT16_MAX
|
|
+
|
|
+#elif defined (__HIGHC__) && defined(_I386)
|
|
+
|
|
+#include <stdint.h>
|
|
+
|
|
+#else
|
|
+#error Unknown platform
|
|
+#endif
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+#endif /* VCOS_STDINT_H */
|
|
+
|
|
+
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_string.h
|
|
@@ -0,0 +1,73 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - public header file
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_STRING_H
|
|
+#define VCOS_STRING_H
|
|
+
|
|
+/**
|
|
+ * \file
|
|
+ *
|
|
+ * String functions.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+#include "vcos_platform.h"
|
|
+
|
|
+#ifdef __KERNEL__
|
|
+#include <linux/string.h>
|
|
+#else
|
|
+#include <string.h>
|
|
+#endif
|
|
+
|
|
+/** Case insensitive string comparison.
|
|
+ *
|
|
+ */
|
|
+
|
|
+VCOS_INLINE_DECL
|
|
+int vcos_strcasecmp(const char *s1, const char *s2);
|
|
+
|
|
+VCOS_INLINE_DECL
|
|
+int vcos_strncasecmp(const char *s1, const char *s2, size_t n);
|
|
+
|
|
+VCOSPRE_ int VCOSPOST_ vcos_vsnprintf( char *buf, size_t buflen, const char *fmt, va_list ap );
|
|
+
|
|
+VCOSPRE_ int VCOSPOST_ vcos_snprintf(char *buf, size_t buflen, const char *fmt, ...);
|
|
+
|
|
+VCOS_STATIC_INLINE
|
|
+int vcos_strlen(const char *s) { return (int)strlen(s); }
|
|
+
|
|
+VCOS_STATIC_INLINE
|
|
+int vcos_strcmp(const char *s1, const char *s2) { return strcmp(s1,s2); }
|
|
+
|
|
+VCOS_STATIC_INLINE
|
|
+int vcos_strncmp(const char *cs, const char *ct, size_t count) { return strncmp(cs, ct, count); }
|
|
+
|
|
+VCOS_STATIC_INLINE
|
|
+char *vcos_strcpy(char *dst, const char *src) { return strcpy(dst, src); }
|
|
+
|
|
+VCOS_STATIC_INLINE
|
|
+char *vcos_strncpy(char *dst, const char *src, size_t count) { return strncpy(dst, src, count); }
|
|
+
|
|
+VCOS_STATIC_INLINE
|
|
+void *vcos_memcpy(void *dst, const void *src, size_t n) { memcpy(dst, src, n); return dst; }
|
|
+
|
|
+VCOS_STATIC_INLINE
|
|
+void *vcos_memset(void *p, int c, size_t n) { return memset(p, c, n); }
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+#endif
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_thread.h
|
|
@@ -0,0 +1,259 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - public header file
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_THREAD_H
|
|
+#define VCOS_THREAD_H
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+#include "vcos_platform.h"
|
|
+
|
|
+/**
|
|
+ * \file vcos_thread.h
|
|
+ *
|
|
+ * \section thread Threads
|
|
+ *
|
|
+ * Under Nucleus, a thread is created by NU_Create_Task, passing in the stack
|
|
+ * and various other parameters. To stop the thread, NU_Terminate_Thread() and
|
|
+ * NU_Delete_Thread() are called.
|
|
+ *
|
|
+ * Unfortunately it's not possible to emulate this API under some fairly common
|
|
+ * operating systems. Under Windows you can't pass in the stack, and you can't
|
|
+ * safely terminate a thread.
|
|
+ *
|
|
+ * Therefore, an API which is similar to the pthreads API is used instead. This
|
|
+ * API can (mostly) be emulated under all interesting operating systems.
|
|
+ *
|
|
+ * Obviously this makes the code somewhat more complicated on VideoCore than it
|
|
+ * would otherwise be - we end up with an extra mutex per thread, and some code
|
|
+ * that waits for it. The benefit is that we have a single way of creating
|
|
+ * threads that works consistently on all platforms (apart from stack supplying).
|
|
+ *
|
|
+ * \subsection stack Stack
|
|
+ *
|
|
+ * It's still not possible to pass in the stack address, but this can be made
|
|
+ * much more obvious in the API: the relevant function is missing and the
|
|
+ * CPP symbol VCOS_CAN_SET_STACK_ADDR is zero rather than one.
|
|
+ *
|
|
+ * \subsection thr_create Creating a thread
|
|
+ *
|
|
+ * The simplest way to create a thread is with vcos_thread_create() passing in a
|
|
+ * NULL thread parameter argument. To wait for the thread to exit, call
|
|
+ * vcos_thread_join().
|
|
+ *
|
|
+ * \subsection back Backward compatibility
|
|
+ *
|
|
+ * To ease migration, a "classic" thread creation API is provided for code
|
|
+ * that used to make use of Nucleus, vcos_thread_create_classic(). The
|
|
+ * arguments are not exactly the same, as the PREEMPT parameter is dropped.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#define VCOS_AFFINITY_CPU0 _VCOS_AFFINITY_CPU0
|
|
+#define VCOS_AFFINITY_CPU1 _VCOS_AFFINITY_CPU1
|
|
+#define VCOS_AFFINITY_MASK _VCOS_AFFINITY_MASK
|
|
+#define VCOS_AFFINITY_DEFAULT _VCOS_AFFINITY_DEFAULT
|
|
+#define VCOS_AFFINITY_THISCPU _VCOS_AFFINITY_THISCPU
|
|
+
|
|
+/** Report whether or not we have an RTOS at all, and hence the ability to
|
|
+ * create threads.
|
|
+ */
|
|
+VCOSPRE_ int VCOSPOST_ vcos_have_rtos(void);
|
|
+
|
|
+/** Create a thread. It must be cleaned up by calling vcos_thread_join().
|
|
+ *
|
|
+ * @param thread Filled in on return with thread
|
|
+ * @param name A name for the thread. May be the empty string.
|
|
+ * @param attrs Attributes; default attributes will be used if this is NULL.
|
|
+ * @param entry Entry point.
|
|
+ * @param arg Argument passed to the entry point.
|
|
+ */
|
|
+VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_thread_create(VCOS_THREAD_T *thread,
|
|
+ const char *name,
|
|
+ VCOS_THREAD_ATTR_T *attrs,
|
|
+ VCOS_THREAD_ENTRY_FN_T entry,
|
|
+ void *arg);
|
|
+
|
|
+/** Exit the thread from within the thread function itself.
|
|
+ * Resources must still be cleaned up via a call to thread_join().
|
|
+ *
|
|
+ * The thread can also be terminated by simply exiting the thread function.
|
|
+ *
|
|
+ * @param data Data passed to thread_join. May be NULL.
|
|
+ */
|
|
+VCOSPRE_ void VCOSPOST_ vcos_thread_exit(void *data);
|
|
+
|
|
+/** Wait for a thread to terminate and then clean up its resources.
|
|
+ *
|
|
+ * @param thread Thread to wait for
|
|
+ * @param pData Updated to point at data provided in vcos_thread_exit or exit
|
|
+ * code of thread function.
|
|
+ */
|
|
+VCOSPRE_ void VCOSPOST_ vcos_thread_join(VCOS_THREAD_T *thread,
|
|
+ void **pData);
|
|
+
|
|
+
|
|
+/**
|
|
+ * \brief Create a thread using an API similar to the one "traditionally"
|
|
+ * used under Nucleus.
|
|
+ *
|
|
+ * This creates a thread which must be cleaned up by calling vcos_thread_join().
|
|
+ * The thread cannot be simply terminated (as in Nucleus and ThreadX) as thread
|
|
+ * termination is not universally supported.
|
|
+ *
|
|
+ * @param thread Filled in with thread instance
|
|
+ * @param name An optional name for the thread. NULL or "" may be used (but
|
|
+ * a name will aid in debugging).
|
|
+ * @param entry Entry point
|
|
+ * @param arg A single argument passed to the entry point function
|
|
+ * @param stack Pointer to stack address
|
|
+ * @param stacksz Size of stack in bytes
|
|
+ * @param priaff Priority of task, between VCOS_PRI_LOW and VCOS_PRI_HIGH, ORed with the CPU affinity
|
|
+ * @param autostart If non-zero the thread will start immediately.
|
|
+ * @param timeslice Timeslice (system ticks) for this thread.
|
|
+ *
|
|
+ * @sa vcos_thread_terminate vcos_thread_delete
|
|
+ */
|
|
+VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_thread_create_classic(VCOS_THREAD_T *thread,
|
|
+ const char *name,
|
|
+ void *(*entry)(void *arg),
|
|
+ void *arg,
|
|
+ void *stack,
|
|
+ VCOS_UNSIGNED stacksz,
|
|
+ VCOS_UNSIGNED priaff,
|
|
+ VCOS_UNSIGNED timeslice,
|
|
+ VCOS_UNSIGNED autostart);
|
|
+
|
|
+/**
|
|
+ * \brief Set a thread's priority
|
|
+ *
|
|
+ * Set the priority for a thread.
|
|
+ *
|
|
+ * @param thread The thread
|
|
+ * @param pri Thread priority in VCOS_PRI_MASK bits; affinity in VCOS_AFFINITY_MASK bits.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_thread_set_priority(VCOS_THREAD_T *thread, VCOS_UNSIGNED pri);
|
|
+
|
|
+/**
|
|
+ * \brief Return the currently executing thread.
|
|
+ *
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+VCOS_THREAD_T *vcos_thread_current(void);
|
|
+
|
|
+/**
|
|
+ * \brief Return the thread's priority.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+VCOS_UNSIGNED vcos_thread_get_priority(VCOS_THREAD_T *thread);
|
|
+
|
|
+/**
|
|
+ * \brief Return the thread's cpu affinity.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+VCOS_UNSIGNED vcos_thread_get_affinity(VCOS_THREAD_T *thread);
|
|
+
|
|
+/**
|
|
+ * \brief Set the thread's cpu affinity.
|
|
+ */
|
|
+
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_thread_set_affinity(VCOS_THREAD_T *thread, VCOS_UNSIGNED affinity);
|
|
+
|
|
+/**
|
|
+ * \brief Query whether we are in an interrupt.
|
|
+ *
|
|
+ * @return 1 if in interrupt context.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+int vcos_in_interrupt(void);
|
|
+
|
|
+/**
|
|
+ * \brief Sleep a while.
|
|
+ *
|
|
+ * @param ms Number of milliseconds to sleep for
|
|
+ *
|
|
+ * This may actually sleep a whole number of ticks.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_sleep(uint32_t ms);
|
|
+
|
|
+/**
|
|
+ * \brief Return the value of the hardware microsecond counter.
|
|
+ *
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+uint32_t vcos_getmicrosecs(void);
|
|
+
|
|
+#define vcos_get_ms() (vcos_getmicrosecs()/1000)
|
|
+
|
|
+/**
|
|
+ * \brief Return a unique identifier for the current process
|
|
+ *
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+VCOS_UNSIGNED vcos_process_id_current(void);
|
|
+
|
|
+/** Relinquish this time slice. */
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_thread_relinquish(void);
|
|
+
|
|
+/** Return the name of the given thread.
|
|
+ */
|
|
+VCOSPRE_ const char * VCOSPOST_ vcos_thread_get_name(const VCOS_THREAD_T *thread);
|
|
+
|
|
+/** Change preemption. This is almost certainly not what you want, as it won't
|
|
+ * work reliably in a multicore system: although you can affect the preemption
|
|
+ * on *this* core, you won't affect what's happening on the other core(s).
|
|
+ *
|
|
+ * It's mainly here to ease migration. If you're using it in new code, you
|
|
+ * probably need to think again.
|
|
+ *
|
|
+ * @param pe New preemption, VCOS_PREEMPT or VCOS_NO_PREEMPT
|
|
+ * @return Old value of preemption.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+VCOS_UNSIGNED vcos_change_preemption(VCOS_UNSIGNED pe);
|
|
+
|
|
+/** Is a thread still running, or has it exited?
|
|
+ *
|
|
+ * Note: this exists for some fairly scary code in the video codec tests. Don't
|
|
+ * try to use it for anything else, as it may well not do what you expect.
|
|
+ *
|
|
+ * @param thread thread to query
|
|
+ * @return non-zero if thread is running, or zero if it has exited.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+int vcos_thread_running(VCOS_THREAD_T *thread);
|
|
+
|
|
+/** Resume a thread.
|
|
+ *
|
|
+ * @param thread thread to resume
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_thread_resume(VCOS_THREAD_T *thread);
|
|
+
|
|
+/*
|
|
+ * Internal APIs - may not always be present and should not be used in
|
|
+ * client code.
|
|
+ */
|
|
+
|
|
+extern void _vcos_task_timer_set(void (*pfn)(void*), void *, VCOS_UNSIGNED ms);
|
|
+extern void _vcos_task_timer_cancel(void);
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+#endif
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_thread_attr.h
|
|
@@ -0,0 +1,73 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - thread attributes
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_THREAD_ATTR_H
|
|
+#define VCOS_THREAD_ATTR_H
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+/**
|
|
+ * \file
|
|
+ *
|
|
+ * Attributes for thread creation.
|
|
+ *
|
|
+ */
|
|
+
|
|
+/** Initialize thread attribute struct. This call does not allocate memory,
|
|
+ * and so cannot fail.
|
|
+ *
|
|
+ */
|
|
+VCOSPRE_ void VCOSPOST_ vcos_thread_attr_init(VCOS_THREAD_ATTR_T *attrs);
|
|
+
|
|
+/** Set the stack address and size. If not set, a stack will be allocated automatically.
|
|
+ *
|
|
+ * This can only be set on some platforms. It will always be possible to set the stack
|
|
+ * address on VideoCore, but on host platforms, support may well not be available.
|
|
+ */
|
|
+#if VCOS_CAN_SET_STACK_ADDR
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_thread_attr_setstack(VCOS_THREAD_ATTR_T *attrs, void *addr, VCOS_UNSIGNED sz);
|
|
+#endif
|
|
+
|
|
+/** Set the stack size. If not set, a default size will be used. Attempting to call this after having
|
|
+ * set the stack location with vcos_thread_attr_setstack() will result in undefined behaviour.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_thread_attr_setstacksize(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED sz);
|
|
+
|
|
+/** Set the task priority. If not set, a default value will be used.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_thread_attr_setpriority(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED pri);
|
|
+
|
|
+/** Set the task cpu affinity. If not set, the default will be used.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_thread_attr_setaffinity(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED aff);
|
|
+
|
|
+/** Set the timeslice. If not set the default will be used.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_thread_attr_settimeslice(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED ts);
|
|
+
|
|
+/** The thread entry function takes (argc,argv), as per Nucleus, with
|
|
+ * argc being 0. This may be withdrawn in a future release and should not
|
|
+ * be used in new code.
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void _vcos_thread_attr_setlegacyapi(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED legacy);
|
|
+
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_thread_attr_setautostart(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED autostart);
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+#endif
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_timer.h
|
|
@@ -0,0 +1,95 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+Project : vcfw
|
|
+Module : chip driver
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - timer support
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_TIMER_H
|
|
+#define VCOS_TIMER_H
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+#include "interface/vcos/vcos_types.h"
|
|
+#include "vcos_platform.h"
|
|
+
|
|
+/** \file vcos_timer.h
|
|
+ *
|
|
+ * Timers are single shot.
|
|
+ *
|
|
+ * Timer times are in milliseconds.
|
|
+ *
|
|
+ * \note that timer callback functions are called from an arbitrary thread
|
|
+ * context. The expiration function should do its work as quickly as possible;
|
|
+ * blocking should be avoided.
|
|
+ *
|
|
+ * \note On Windows, the separate function vcos_timer_init() must be called
|
|
+ * as timer initialization from DllMain is not possible.
|
|
+ */
|
|
+
|
|
+/** Perform timer subsystem initialization. This function is not needed
|
|
+ * on non-Windows platforms but is still present so that it can be
|
|
+ * called. On Windows it is needed because vcos_init() gets called
|
|
+ * from DLL initialization where it is not possible to create a
|
|
+ * time queue (deadlock occurs if you try).
|
|
+ *
|
|
+ * @return VCOS_SUCCESS on success. VCOS_EEXIST if this has already been called
|
|
+ * once. VCOS_ENOMEM if resource allocation failed.
|
|
+ */
|
|
+VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_timer_init(void);
|
|
+
|
|
+/** Create a timer in a disabled state.
|
|
+ *
|
|
+ * The timer is initially disabled.
|
|
+ *
|
|
+ * @param timer timer handle
|
|
+ * @param name name for timer
|
|
+ * @param expiration_routine function to call when timer expires
|
|
+ * @param context context passed to expiration routine
|
|
+ *
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+VCOS_STATUS_T vcos_timer_create(VCOS_TIMER_T *timer,
|
|
+ const char *name,
|
|
+ void (*expiration_routine)(void *context),
|
|
+ void *context);
|
|
+
|
|
+
|
|
+
|
|
+/** Start a timer running.
|
|
+ *
|
|
+ * Timer must be stopped.
|
|
+ *
|
|
+ * @param timer timer handle
|
|
+ * @param delay Delay to wait for, in ms
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_timer_set(VCOS_TIMER_T *timer, VCOS_UNSIGNED delay);
|
|
+
|
|
+/** Stop an already running timer.
|
|
+ *
|
|
+ * @param timer timer handle
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_timer_cancel(VCOS_TIMER_T *timer);
|
|
+
|
|
+/** Stop a timer and restart it.
|
|
+ * @param timer timer handle
|
|
+ * @param delay delay in ms
|
|
+ */
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_timer_reset(VCOS_TIMER_T *timer, VCOS_UNSIGNED delay);
|
|
+
|
|
+VCOS_INLINE_DECL
|
|
+void vcos_timer_delete(VCOS_TIMER_T *timer);
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+#endif
|
|
--- /dev/null
|
|
+++ b/drivers/misc/vc04_services/interface/vcos/vcos_types.h
|
|
@@ -0,0 +1,197 @@
|
|
+/*=============================================================================
|
|
+Copyright (c) 2009 Broadcom Europe Limited.
|
|
+All rights reserved.
|
|
+
|
|
+FILE DESCRIPTION
|
|
+VideoCore OS Abstraction Layer - basic types
|
|
+=============================================================================*/
|
|
+
|
|
+#ifndef VCOS_TYPES_H
|
|
+#define VCOS_TYPES_H
|
|
+
|
|
+#define VCOS_VERSION 1
|
|
+
|
|
+#include "vcos_platform_types.h"
|
|
+
|
|
+#if !defined(VCOSPRE_) || !defined(VCOSPOST_)
|
|
+#error VCOSPRE_ and VCOSPOST_ not defined!
|
|
+#endif
|
|
+
|
|
+/* Redefine these here; this means that existing header files can carry on
|
|
+ * using the VCHPOST/VCHPRE macros rather than having huge changes, which
|
|
+ * could cause nasty merge problems.
|
|
+ */
|
|
+#ifndef VCHPOST_
|
|
+#define VCHPOST_ VCOSPOST_
|
|
+#endif
|
|
+#ifndef VCHPRE_
|
|
+#define VCHPRE_ VCOSPRE_
|
|
+#endif
|
|
+
|
|
+/** Entry function for a lowlevel thread.
|
|
+ *
|
|
+ * Returns void for consistency with Nucleus/ThreadX.
|
|
+ */
|
|
+typedef void (*VCOS_LLTHREAD_ENTRY_FN_T)(void *);
|
|
+
|
|
+/** Thread entry point. Returns a void* for consistency
|
|
+ * with pthreads.
|
|
+ */
|
|
+typedef void *(*VCOS_THREAD_ENTRY_FN_T)(void*);
|
|
+
|
|
+
|
|
+/* Error return codes - chosen to be similar to errno values */
|
|
+typedef enum
|
|
+{
|
|
+ VCOS_SUCCESS,
|
|
+ VCOS_EAGAIN,
|
|
+ VCOS_ENOENT,
|
|
+ VCOS_ENOSPC,
|
|
+ VCOS_EINVAL,
|
|
+ VCOS_EACCESS,
|
|
+ VCOS_ENOMEM,
|
|
+ VCOS_ENOSYS,
|
|
+ VCOS_EEXIST,
|
|
+ VCOS_ENXIO,
|
|
+ VCOS_EINTR
|
|
+} VCOS_STATUS_T;
|
|
+
|
|
+/* Some compilers (MetaWare) won't inline with -g turned on, which then results
|
|
+ * in a lot of code bloat. To overcome this, inline functions are forward declared
|
|
+ * with the prefix VCOS_INLINE_DECL, and implemented with the prefix VCOS_INLINE_IMPL.
|
|
+ *
|
|
+ * That then means that in a release build, "static inline" can be used in the obvious
|
|
+ * way, but in a debug build the implementations can be skipped in all but one file,
|
|
+ * by using VCOS_INLINE_BODIES.
|
|
+ *
|
|
+ * VCOS_INLINE_DECL - put this at the start of an inline forward declaration of a VCOS
|
|
+ * function.
|
|
+ *
|
|
+ * VCOS_INLINE_IMPL - put this at the start of an inlined implementation of a VCOS
|
|
+ * function.
|
|
+ *
|
|
+ */
|
|
+
|
|
+/* VCOS_EXPORT - it turns out that in some circumstances we need the implementation of
|
|
+ * a function even if it is usually inlined.
|
|
+ *
|
|
+ * In particular, if we have a codec that is usually provided in object form, if it
|
|
+ * was built for a debug build it will be full of calls to vcos_XXX(). If this is used
|
|
+ * in a *release* build, then there won't be any of these calls around in the main image
|
|
+ * as they will all have been inlined. The problem also exists for vcos functions called
|
|
+ * from assembler.
|
|
+ *
|
|
+ * VCOS_EXPORT ensures that the named function will be emitted as a regular (not static-inline)
|
|
+ * function inside vcos_<platform>.c so that it can be linked against. Doing this for every
|
|
+ * VCOS function would be a bit code-bloat-tastic, so it is only done for those that need it.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifdef __cplusplus
|
|
+#define _VCOS_INLINE inline
|
|
+#else
|
|
+#define _VCOS_INLINE __inline
|
|
+#endif
|
|
+
|
|
+#if defined(NDEBUG)
|
|
+
|
|
+#ifdef __GNUC__
|
|
+# define VCOS_INLINE_DECL extern __inline__
|
|
+# define VCOS_INLINE_IMPL static __inline__
|
|
+#else
|
|
+# define VCOS_INLINE_DECL static _VCOS_INLINE /* declare a func */
|
|
+# define VCOS_INLINE_IMPL static _VCOS_INLINE /* implement a func inline */
|
|
+#endif
|
|
+
|
|
+# if defined(VCOS_WANT_IMPL)
|
|
+# define VCOS_EXPORT
|
|
+# else
|
|
+# define VCOS_EXPORT VCOS_INLINE_IMPL
|
|
+# endif /* VCOS_WANT_IMPL */
|
|
+
|
|
+#define VCOS_INLINE_BODIES
|
|
+
|
|
+#else /* NDEBUG */
|
|
+
|
|
+#if !defined(VCOS_INLINE_DECL)
|
|
+ #define VCOS_INLINE_DECL extern
|
|
+#endif
|
|
+#if !defined(VCOS_INLINE_IMPL)
|
|
+ #define VCOS_INLINE_IMPL
|
|
+#endif
|
|
+#define VCOS_EXPORT VCOS_INLINE_IMPL
|
|
+#endif
|
|
+
|
|
+#define VCOS_STATIC_INLINE static _VCOS_INLINE
|
|
+
|
|
+#if defined(__HIGHC__) || defined(__HIGHC_ANSI__)
|
|
+#define _VCOS_METAWARE
|
|
+#endif
|
|
+
|
|
+/** It seems that __FUNCTION__ isn't standard!
|
|
+ */
|
|
+#if __STDC_VERSION__ < 199901L
|
|
+# if __GNUC__ >= 2 || defined(__VIDEOCORE__)
|
|
+# define VCOS_FUNCTION __FUNCTION__
|
|
+# else
|
|
+# define VCOS_FUNCTION "<unknown>"
|
|
+# endif
|
|
+#else
|
|
+# define VCOS_FUNCTION __func__
|
|
+#endif
|
|
+
|
|
+#define _VCOS_MS_PER_TICK (1000/VCOS_TICKS_PER_SECOND)
|
|
+
|
|
+/* Convert a number of milliseconds to a tick count. Internal use only - fails to
|
|
+ * convert VCOS_SUSPEND correctly.
|
|
+ */
|
|
+#define _VCOS_MS_TO_TICKS(ms) (((ms)+_VCOS_MS_PER_TICK-1)/_VCOS_MS_PER_TICK)
|
|
+
|
|
+#define VCOS_TICKS_TO_MS(ticks) ((ticks) * _VCOS_MS_PER_TICK)
|
|
+
|
|
+/** VCOS version of DATESTR, from pcdisk.h. Used by the hostreq service.
|
|
+ */
|
|
+typedef struct vcos_datestr
|
|
+{
|
|
+ uint8_t cmsec; /**< Centesimal mili second */
|
|
+ uint16_t date; /**< Date */
|
|
+ uint16_t time; /**< Time */
|
|
+
|
|
+} VCOS_DATESTR;
|
|
+
|
|
+/* Compile-time assert - declares invalid array length if condition
|
|
+ * not met, or array of length one if OK.
|
|
+ */
|
|
+#define VCOS_CASSERT(e) extern char vcos_compile_time_check[1/(e)]
|
|
+
|
|
+#define vcos_min(x,y) ((x) < (y) ? (x) : (y))
|
|
+#define vcos_max(x,y) ((x) > (y) ? (x) : (y))
|
|
+
|
|
+/** Return the count of an array. FIXME: under gcc we could make
|
|
+ * this report an error for pointers using __builtin_types_compatible().
|
|
+ */
|
|
+#define vcos_countof(x) (sizeof((x)) / sizeof((x)[0]))
|
|
+
|
|
+/* for backward compatibility */
|
|
+#define countof(x) (sizeof((x)) / sizeof((x)[0]))
|
|
+
|
|
+#define VCOS_ALIGN_DOWN(p,n) (((ptrdiff_t)(p)) & ~((n)-1))
|
|
+#define VCOS_ALIGN_UP(p,n) VCOS_ALIGN_DOWN((ptrdiff_t)(p)+(n)-1,(n))
|
|
+
|
|
+/** bool_t is not a POSIX type so cannot rely on it. Define it here.
|
|
+ * It's not even defined in stdbool.h.
|
|
+ */
|
|
+typedef int32_t vcos_bool_t;
|
|
+typedef int32_t vcos_fourcc_t;
|
|
+
|
|
+#define VCOS_FALSE 0
|
|
+#define VCOS_TRUE (!VCOS_FALSE)
|
|
+
|
|
+/** Mark unused arguments to keep compilers quiet */
|
|
+#define vcos_unused(x) (void)(x)
|
|
+
|
|
+/** For backward compatibility */
|
|
+typedef vcos_fourcc_t fourcc_t;
|
|
+typedef vcos_fourcc_t FOURCC_T;
|
|
+
|
|
+#endif
|