wifipineapple-openwrt/target/linux/brcm2708/patches-3.3/0005-bcm2708-vchiq-driver.p...

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_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, &params, 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, &params, 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, &params, 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, &params, 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(&params, 0, sizeof(params));
+ params.fourcc = setup->service_id;
+ params.userdata = setup->callback_param;
+
+ status = create_service((VCHIQ_INSTANCE_T)instance_handle,
+ &params,
+ 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(&params, 0, sizeof(params));
+ params.fourcc = setup->service_id;
+ params.userdata = setup->callback_param;
+
+ status = create_service((VCHIQ_INSTANCE_T)instance_handle,
+ &params,
+ 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; &param->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( &param, cmd_globals.cmd_entry );
+
+ if ( param.use_log )
+ {
+ cmd_log_results( &param );
+ 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( &param, "\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