From a2e42fbc97cde9e851f5b036f46b8f34a5be6eb9 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Tue, 17 Jan 2012 19:22:19 +0000 Subject: [PATCH 5/7] bcm2708 vchiq driver Signed-off-by: popcornmix --- 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 + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#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 +#include +#include +#include +#include +#include +#include + +#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)] : "", 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)] : + "", 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)] : + "", 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 + +#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[] = + { + "", + "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 +#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 +#include + +#include "vchiq_core.h" +#include "vchiq_arm.h" +#include "interface/vcos/vcos_logging.h" + +/* ---- Public Variables ------------------------------------------------- */ + +extern VCOS_LOG_CAT_T vchiq_core_log_category; +#define VCOS_LOG_CATEGORY (&vchiq_core_log_category) + +/* ---- Private Constants and Types -------------------------------------- */ + +struct vchiq_instance_struct { + VCHIQ_STATE_T *state; + + int connected; +}; + +/**************************************************************************** +* +* vchiq_initialise +* +***************************************************************************/ + +VCHIQ_STATUS_T vchiq_initialise( VCHIQ_INSTANCE_T *instanceOut ) +{ + VCHIQ_STATUS_T status = VCHIQ_ERROR; + VCHIQ_STATE_T *state; + VCHIQ_INSTANCE_T instance = NULL; + + vcos_log_trace( "%s called", __func__ ); + + state = vchiq_get_state(); + if (!state) + { + printk( KERN_ERR "%s: videocore not initialized\n", __func__ ); + goto failed; + } + + instance = kzalloc( sizeof(*instance), GFP_KERNEL ); + if( !instance ) + { + printk( KERN_ERR "%s: error allocating vchiq instance\n", __func__ ); + goto failed; + } + + instance->connected = 0; + instance->state = state; + + *instanceOut = instance; + + status = VCHIQ_SUCCESS; + +failed: + vcos_log_trace( "%s(%p): returning %d", __func__, instance, status ); + + return status; +} + +/**************************************************************************** +* +* vchiq_shutdown +* +***************************************************************************/ + +VCHIQ_STATUS_T vchiq_shutdown( VCHIQ_INSTANCE_T instance ) +{ + VCHIQ_STATUS_T status; + VCHIQ_STATE_T *state = instance->state; + + vcos_log_trace( "%s(%p) called", __func__, instance ); + + vcos_mutex_lock(&state->mutex); + + /* Remove all services */ + status = vchiq_shutdown_internal(state, instance); + + vcos_mutex_unlock(&state->mutex); + + if (status == VCHIQ_SUCCESS) + kfree(instance); + + vcos_log_trace( "%s(%p): returning %d", __func__, instance, status ); + + return status; +} + +/**************************************************************************** +* +* vchiq_is_connected +* +***************************************************************************/ + +int vchiq_is_connected(VCHIQ_INSTANCE_T instance) +{ + return instance->connected; +} + +/**************************************************************************** +* +* vchiq_connect +* +***************************************************************************/ + +VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance) +{ + VCHIQ_STATUS_T status; + VCHIQ_STATE_T *state = instance->state; + + vcos_log_trace( "%s(%p) called", __func__, instance ); + + if (vcos_mutex_lock(&state->mutex) != VCOS_SUCCESS) { + vcos_log_trace( "%s: call to vcos_mutex_lock failed", __func__ ); + status = VCHIQ_RETRY; + goto failed; + } + status = vchiq_connect_internal(state, instance); + + if (status == VCHIQ_SUCCESS) + instance->connected = 1; + + vcos_mutex_unlock(&state->mutex); + +failed: + vcos_log_trace( "%s(%p): returning %d", __func__, instance, status ); + + return status; +} + +/**************************************************************************** +* +* vchiq_add_service +* +***************************************************************************/ + +VCHIQ_STATUS_T vchiq_add_service( + VCHIQ_INSTANCE_T instance, + int fourcc, + VCHIQ_CALLBACK_T callback, + void *userdata, + VCHIQ_SERVICE_HANDLE_T *pservice) +{ + VCHIQ_SERVICE_PARAMS_T params; + + params.fourcc = fourcc; + params.callback = callback; + params.userdata = userdata; + params.version = 0; + params.version_min = 0; + + return vchiq_add_service_params(instance, ¶ms, pservice); +} + +/**************************************************************************** +* +* vchiq_open_service +* +***************************************************************************/ + +VCHIQ_STATUS_T vchiq_open_service( + VCHIQ_INSTANCE_T instance, + int fourcc, + VCHIQ_CALLBACK_T callback, + void *userdata, + VCHIQ_SERVICE_HANDLE_T *pservice) +{ + VCHIQ_SERVICE_PARAMS_T params; + + params.fourcc = fourcc; + params.callback = callback; + params.userdata = userdata; + params.version = 0; + params.version_min = 0; + + return vchiq_open_service_params(instance, ¶ms, pservice); +} + +/**************************************************************************** +* +* vchiq_add_service_params +* +***************************************************************************/ + +VCHIQ_STATUS_T vchiq_add_service_params( + VCHIQ_INSTANCE_T instance, + const VCHIQ_SERVICE_PARAMS_T *params, + VCHIQ_SERVICE_HANDLE_T *pservice) +{ + VCHIQ_STATUS_T status; + VCHIQ_STATE_T *state = instance->state; + VCHIQ_SERVICE_T *service; + int srvstate; + + vcos_log_trace( "%s(%p) called", __func__, instance ); + + *pservice = NULL; + + srvstate = vchiq_is_connected( instance ) + ? VCHIQ_SRVSTATE_LISTENING + : VCHIQ_SRVSTATE_HIDDEN; + + vcos_mutex_lock(&state->mutex); + + service = vchiq_add_service_internal( + state, + params, + srvstate, + instance); + + vcos_mutex_unlock(&state->mutex); + + if ( service ) + { + *pservice = &service->base; + status = VCHIQ_SUCCESS; + } + else + { + status = VCHIQ_ERROR; + } + + vcos_log_trace( "%s(%p): returning %d", __func__, instance, status ); + + return status; +} + +/**************************************************************************** +* +* vchiq_open_service_params +* +***************************************************************************/ + +VCHIQ_STATUS_T vchiq_open_service_params( + VCHIQ_INSTANCE_T instance, + const VCHIQ_SERVICE_PARAMS_T *params, + VCHIQ_SERVICE_HANDLE_T *pservice) +{ + VCHIQ_STATUS_T status = VCHIQ_ERROR; + VCHIQ_STATE_T *state = instance->state; + VCHIQ_SERVICE_T *service; + + vcos_log_trace( "%s(%p) called", __func__, instance ); + + *pservice = NULL; + + if (!vchiq_is_connected(instance)) + goto failed; + + vcos_mutex_lock(&state->mutex); + + service = vchiq_add_service_internal(state, + params, + VCHIQ_SRVSTATE_OPENING, + instance); + + vcos_mutex_unlock(&state->mutex); + + if ( service ) + { + status = vchiq_open_service_internal(service, current->pid); + if ( status == VCHIQ_SUCCESS ) + *pservice = &service->base; + else + vchiq_remove_service(&service->base); + } + +failed: + vcos_log_trace( "%s(%p): returning %d", __func__, instance, status ); + + return status; +} + +EXPORT_SYMBOL(vchiq_initialise); +EXPORT_SYMBOL(vchiq_shutdown); +EXPORT_SYMBOL(vchiq_connect); +EXPORT_SYMBOL(vchiq_add_service); +EXPORT_SYMBOL(vchiq_open_service); +EXPORT_SYMBOL(vchiq_add_service_params); +EXPORT_SYMBOL(vchiq_open_service_params); --- /dev/null +++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_lib.c @@ -0,0 +1,1518 @@ +/* + * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include "vchiq.h" +#include "vchiq_cfg.h" +#include "vchiq_ioctl.h" +#include "interface/vchi/vchi.h" +#include "interface/vchi/common/endian.h" +#include "interface/vcos/vcos.h" + +#define VCHIQ_MAX_INSTANCE_SERVICES 32 +#define MSGBUF_SIZE (VCHIQ_MAX_MSG_SIZE + sizeof(VCHIQ_HEADER_T)) + +#define RETRY(r,x) do { r = x; } while ((r == -1) && (errno == EINTR)) + +#define VCOS_LOG_CATEGORY (&vchiq_lib_log_category) + +typedef struct vchiq_service_struct +{ + VCHIQ_SERVICE_BASE_T base; + int handle; + int fd; + VCHI_CALLBACK_T vchi_callback; + void *peek_buf; + int peek_size; + int client_id; +} VCHIQ_SERVICE_T; + +typedef struct vchiq_service_struct VCHI_SERVICE_T; + +struct vchiq_instance_struct +{ + int fd; + int initialised; + int connected; + VCOS_THREAD_T completion_thread; + VCOS_MUTEX_T mutex; + int used_services; + VCHIQ_SERVICE_T services[VCHIQ_MAX_INSTANCE_SERVICES]; +} vchiq_instance; + +typedef struct vchiq_instance_struct VCHI_STATE_T; + +/* Local data */ +static VCOS_LOG_LEVEL_T vchiq_default_lib_log_level = VCOS_LOG_WARN; +static VCOS_LOG_CAT_T vchiq_lib_log_category; +static VCOS_MUTEX_T vchiq_lib_mutex; +static void *free_msgbufs; + + +/* Local utility functions */ +static VCHIQ_INSTANCE_T +vchiq_lib_init(void); + +static void *completion_thread(void *); + +static VCHIQ_STATUS_T +create_service(VCHIQ_INSTANCE_T instance, + const VCHIQ_SERVICE_PARAMS_T *params, + VCHI_CALLBACK_T vchi_callback, + int is_open, + VCHIQ_SERVICE_HANDLE_T *pservice); + +static int +fill_peek_buf(VCHI_SERVICE_T *service, + VCHI_FLAGS_T flags); + +static void * +alloc_msgbuf(void); + +static void +free_msgbuf(void *buf); + +static __inline int +is_valid_instance(VCHIQ_INSTANCE_T instance) +{ + return (instance == &vchiq_instance) && (instance->initialised > 0); +} + +/* + * VCHIQ API + */ + +VCHIQ_STATUS_T +vchiq_initialise(VCHIQ_INSTANCE_T *pinstance) +{ + VCHIQ_INSTANCE_T instance; + + instance = vchiq_lib_init(); + + vcos_log_trace( "%s: returning instance handle %p", __func__, instance ); + + *pinstance = instance; + + return (instance != NULL) ? VCHIQ_SUCCESS : VCHIQ_ERROR; +} + +VCHIQ_STATUS_T +vchiq_shutdown(VCHIQ_INSTANCE_T instance) +{ + vcos_log_trace( "%s called", __func__ ); + + if (!is_valid_instance(instance)) + return VCHIQ_ERROR; + + vcos_mutex_lock(&instance->mutex); + + if (instance->initialised == 1) + { + int i; + + instance->initialised = -1; /* Enter limbo */ + + /* Remove all services */ + + for (i = 0; i < instance->used_services; i++) + { + if (instance->services[i].handle != VCHIQ_INVALID_HANDLE) + { + vchiq_remove_service(&instance->services[i].base); + instance->services[i].handle = VCHIQ_INVALID_HANDLE; + } + } + + if (instance->connected) + { + int ret; + RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_SHUTDOWN, 0)); + vcos_assert(ret == 0); + vcos_thread_join(&instance->completion_thread, NULL); + instance->connected = 0; + } + + close(instance->fd); + instance->fd = -1; + } + else if (instance->initialised > 1) + { + instance->initialised--; + } + + vcos_mutex_unlock(&instance->mutex); + + vcos_global_lock(); + + if (instance->initialised == -1) + { + vcos_mutex_delete(&instance->mutex); + instance->initialised = 0; + } + + vcos_global_unlock(); + + vcos_log_trace( "%s returning", __func__ ); + + return VCHIQ_SUCCESS; +} + +VCHIQ_STATUS_T +vchiq_connect(VCHIQ_INSTANCE_T instance) +{ + VCHIQ_STATUS_T status = VCHIQ_SUCCESS; + + vcos_log_trace( "%s called", __func__ ); + + if (!is_valid_instance(instance)) + return VCHIQ_ERROR; + + vcos_mutex_lock(&instance->mutex); + + if (!instance->connected) + { + int ret = ioctl(instance->fd, VCHIQ_IOC_CONNECT, 0); + if (ret == 0) + { + VCOS_THREAD_ATTR_T attrs; + instance->connected = 1; + vcos_thread_attr_init(&attrs); + vcos_thread_create(&instance->completion_thread, "VCHIQ completion", + &attrs, completion_thread, instance); + } + else + { + status = VCHIQ_ERROR; + } + } + + vcos_mutex_unlock(&instance->mutex); + + return status; +} + +VCHIQ_STATUS_T +vchiq_add_service(VCHIQ_INSTANCE_T instance, + int fourcc, + VCHIQ_CALLBACK_T callback, + void *userdata, + VCHIQ_SERVICE_HANDLE_T *pservice) +{ + VCHIQ_SERVICE_PARAMS_T params; + + params.fourcc = fourcc; + params.callback = callback; + params.userdata = userdata; + params.version = 0; + params.version_min = 0; + + return vchiq_add_service_params(instance, ¶ms, pservice); +} + +VCHIQ_STATUS_T +vchiq_open_service(VCHIQ_INSTANCE_T instance, + int fourcc, + VCHIQ_CALLBACK_T callback, + void *userdata, + VCHIQ_SERVICE_HANDLE_T *pservice) +{ + VCHIQ_SERVICE_PARAMS_T params; + + params.fourcc = fourcc; + params.callback = callback; + params.userdata = userdata; + params.version = 0; + params.version_min = 0; + + return vchiq_open_service_params(instance, ¶ms, pservice); +} + +VCHIQ_STATUS_T +vchiq_add_service_params(VCHIQ_INSTANCE_T instance, + const VCHIQ_SERVICE_PARAMS_T *params, + VCHIQ_SERVICE_HANDLE_T *pservice) +{ + VCHIQ_STATUS_T status; + + vcos_log_trace( "%s called fourcc = 0x%08x (%c%c%c%c)", + __func__, + params->fourcc, + (params->fourcc >> 24) & 0xff, + (params->fourcc >> 16) & 0xff, + (params->fourcc >> 8) & 0xff, + (params->fourcc ) & 0xff ); + + if (!params->callback) + return VCHIQ_ERROR; + + if (!is_valid_instance(instance)) + return VCHIQ_ERROR; + + status = create_service(instance, + params, + NULL/*vchi_callback*/, + 0/*!open*/, + pservice); + + vcos_log_trace( "%s returning service handle = 0x%08x", __func__, (uint32_t)*pservice ); + + return status; +} + +VCHIQ_STATUS_T +vchiq_open_service_params(VCHIQ_INSTANCE_T instance, + const VCHIQ_SERVICE_PARAMS_T *params, + VCHIQ_SERVICE_HANDLE_T *pservice) +{ + VCHIQ_STATUS_T status; + + vcos_log_trace( "%s called fourcc = 0x%08x (%c%c%c%c)", + __func__, + params->fourcc, + (params->fourcc >> 24) & 0xff, + (params->fourcc >> 16) & 0xff, + (params->fourcc >> 8) & 0xff, + (params->fourcc ) & 0xff ); + + if (!params->callback) + return VCHIQ_ERROR; + + if (!is_valid_instance(instance)) + return VCHIQ_ERROR; + + status = create_service(instance, + params, + NULL/*vchi_callback*/, + 1/*open*/, + pservice); + + vcos_log_trace( "%s returning service handle = 0x%08x", __func__, (uint32_t)*pservice ); + + return status; +} + +VCHIQ_STATUS_T +vchiq_close_service(VCHIQ_SERVICE_HANDLE_T handle) +{ + VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; + int ret; + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_CLOSE_SERVICE, service->handle)); + + if (ret != 0) + return VCHIQ_ERROR; + + service->handle = VCHIQ_INVALID_HANDLE; + return VCHIQ_SUCCESS; +} + +VCHIQ_STATUS_T +vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T handle) +{ + VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; + int ret; + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_REMOVE_SERVICE, service->handle)); + + if (ret != 0) + return VCHIQ_ERROR; + + service->handle = VCHIQ_INVALID_HANDLE; + return VCHIQ_SUCCESS; +} + +VCHIQ_STATUS_T +vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T handle, + const VCHIQ_ELEMENT_T *elements, + int count) +{ + VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; + VCHIQ_QUEUE_MESSAGE_T args; + int ret; + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + + args.handle = service->handle; + args.elements = elements; + args.count = count; + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_MESSAGE, &args)); + + return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; +} + +void +vchiq_release_message(VCHIQ_SERVICE_HANDLE_T handle, + VCHIQ_HEADER_T *header) +{ + vcos_log_trace( "%s handle=%08x, header=%x", __func__, (uint32_t)handle, (uint32_t)header ); + + free_msgbuf(header); +} + +VCHIQ_STATUS_T +vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, + const void *data, + int size, + void *userdata) +{ + VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; + VCHIQ_QUEUE_BULK_TRANSFER_T args; + int ret; + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + + args.handle = service->handle; + args.data = (void *)data; + args.size = size; + args.userdata = userdata; + args.mode = VCHIQ_BULK_MODE_CALLBACK; + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_TRANSMIT, &args)); + + return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; +} + +VCHIQ_STATUS_T +vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, + void *data, + int size, + void *userdata) +{ + VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; + VCHIQ_QUEUE_BULK_TRANSFER_T args; + int ret; + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + + args.handle = service->handle; + args.data = data; + args.size = size; + args.userdata = userdata; + args.mode = VCHIQ_BULK_MODE_CALLBACK; + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_RECEIVE, &args)); + + return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; +} + +VCHIQ_STATUS_T +vchiq_queue_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T handle, + VCHI_MEM_HANDLE_T memhandle, + const void *offset, + int size, + void *userdata) +{ + vcos_assert(memhandle == VCHI_MEM_HANDLE_INVALID); + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + + return vchiq_queue_bulk_transmit(handle, offset, size, userdata); +} + +VCHIQ_STATUS_T +vchiq_queue_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T handle, + VCHI_MEM_HANDLE_T memhandle, + void *offset, + int size, + void *userdata) +{ + vcos_assert(memhandle == VCHI_MEM_HANDLE_INVALID); + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + + return vchiq_queue_bulk_receive(handle, offset, size, userdata); +} + +VCHIQ_STATUS_T +vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, + const void *data, + int size, + void *userdata, + VCHIQ_BULK_MODE_T mode) +{ + VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; + VCHIQ_QUEUE_BULK_TRANSFER_T args; + int ret; + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + + args.handle = service->handle; + args.data = (void *)data; + args.size = size; + args.userdata = userdata; + args.mode = mode; + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_TRANSMIT, &args)); + + return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; +} + +VCHIQ_STATUS_T +vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, + void *data, + int size, + void *userdata, + VCHIQ_BULK_MODE_T mode) +{ + VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; + VCHIQ_QUEUE_BULK_TRANSFER_T args; + int ret; + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + + args.handle = service->handle; + args.data = data; + args.size = size; + args.userdata = userdata; + args.mode = mode; + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_RECEIVE, &args)); + + return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; +} + +VCHIQ_STATUS_T +vchiq_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T handle, + VCHI_MEM_HANDLE_T memhandle, + const void *offset, + int size, + void *userdata, + VCHIQ_BULK_MODE_T mode) +{ + vcos_assert(memhandle == VCHI_MEM_HANDLE_INVALID); + + return vchiq_bulk_transmit(handle, offset, size, userdata, mode); +} + +VCHIQ_STATUS_T +vchiq_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T handle, + VCHI_MEM_HANDLE_T memhandle, + void *offset, + int size, + void *userdata, + VCHIQ_BULK_MODE_T mode) +{ + vcos_assert(memhandle == VCHI_MEM_HANDLE_INVALID); + + return vchiq_bulk_receive(handle, offset, size, userdata, mode); +} + +int +vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T handle) +{ + VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; + + return ioctl(service->fd, VCHIQ_IOC_GET_CLIENT_ID, service->handle); +} + +VCHIQ_STATUS_T +vchiq_get_config(VCHIQ_INSTANCE_T instance, + int config_size, + VCHIQ_CONFIG_T *pconfig) +{ + VCHIQ_GET_CONFIG_T args; + int ret; + + if (!is_valid_instance(instance)) + return VCHIQ_ERROR; + + args.config_size = config_size; + args.pconfig = pconfig; + + RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_GET_CONFIG, &args)); + + return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; +} + +int32_t +vchiq_use_service( const VCHIQ_SERVICE_HANDLE_T handle ) +{ + VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; + int ret; + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_USE_SERVICE, service->handle)); + return ret; +} + +int32_t +vchiq_release_service( const VCHIQ_SERVICE_HANDLE_T handle ) +{ + VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; + int ret; + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_RELEASE_SERVICE, service->handle)); + return ret; +} + +VCHIQ_STATUS_T +vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T handle, + VCHIQ_SERVICE_OPTION_T option, int value) +{ + VCHIQ_SET_SERVICE_OPTION_T args; + VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; + int ret; + + args.handle = service->handle; + args.option = option; + args.value = value; + + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_SET_SERVICE_OPTION, &args)); + + return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; +} + +/* + * VCHI API + */ + +/* ---------------------------------------------------------------------- + * return pointer to the mphi message driver function table + * -------------------------------------------------------------------- */ +const VCHI_MESSAGE_DRIVER_T * +vchi_mphi_message_driver_func_table( void ) +{ + return NULL; +} + +/* ---------------------------------------------------------------------- + * return a pointer to the 'single' connection driver fops + * -------------------------------------------------------------------- */ +const VCHI_CONNECTION_API_T * +single_get_func_table( void ) +{ + return NULL; +} + +VCHI_CONNECTION_T * +vchi_create_connection( const VCHI_CONNECTION_API_T * function_table, + const VCHI_MESSAGE_DRIVER_T * low_level ) +{ + vcos_unused(function_table); + vcos_unused(low_level); + + return NULL; +} + +/*********************************************************** + * Name: vchi_msg_peek + * + * Arguments: const VCHI_SERVICE_HANDLE_T handle, + * void **data, + * uint32_t *msg_size, + * VCHI_FLAGS_T flags + * + * Description: Routine to return a pointer to the current message (to allow in place processing) + * The message can be removed using vchi_msg_remove when you're finished + * + * Returns: int32_t - success == 0 + * + ***********************************************************/ +int32_t +vchi_msg_peek( VCHI_SERVICE_HANDLE_T handle, + void **data, + uint32_t *msg_size, + VCHI_FLAGS_T flags ) +{ + VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; + int ret; + + ret = fill_peek_buf(service, flags); + + if (ret == 0) + { + *data = service->peek_buf; + *msg_size = service->peek_size; + } + + return ret; +} + +/*********************************************************** + * Name: vchi_msg_remove + * + * Arguments: const VCHI_SERVICE_HANDLE_T handle, + * + * Description: Routine to remove a message (after it has been read with vchi_msg_peek) + * + * Returns: int32_t - success == 0 + * + ***********************************************************/ +int32_t +vchi_msg_remove( VCHI_SERVICE_HANDLE_T handle ) +{ + VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; + + /* Why would you call vchi_msg_remove without calling vchi_msg_peek first? */ + vcos_assert(service->peek_size >= 0); + + /* Invalidate the content but reuse the buffer */ + service->peek_size = -1; + + return 0; +} + +/*********************************************************** + * Name: vchi_msg_queue + * + * Arguments: VCHI_SERVICE_HANDLE_T handle, + * const void *data, + * uint32_t data_size, + * VCHI_FLAGS_T flags, + * void *msg_handle, + * + * Description: Thin wrapper to queue a message onto a connection + * + * Returns: int32_t - success == 0 + * + ***********************************************************/ +int32_t +vchi_msg_queue( VCHI_SERVICE_HANDLE_T handle, + const void * data, + uint32_t data_size, + VCHI_FLAGS_T flags, + void * msg_handle ) +{ + VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; + VCHIQ_QUEUE_MESSAGE_T args; + VCHIQ_ELEMENT_T element = {data, data_size}; + int ret; + + vcos_unused(msg_handle); + vcos_assert(flags == VCHI_FLAGS_BLOCK_UNTIL_QUEUED); + + args.handle = service->handle; + args.elements = &element; + args.count = 1; + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_MESSAGE, &args)); + + return ret; +} + +/*********************************************************** + * Name: vchi_bulk_queue_receive + * + * Arguments: VCHI_BULK_HANDLE_T handle, + * void *data_dst, + * const uint32_t data_size, + * VCHI_FLAGS_T flags + * void *bulk_handle + * + * Description: Routine to setup a rcv buffer + * + * Returns: int32_t - success == 0 + * + ***********************************************************/ +int32_t +vchi_bulk_queue_receive( VCHI_SERVICE_HANDLE_T handle, + void * data_dst, + uint32_t data_size, + VCHI_FLAGS_T flags, + void * bulk_handle ) +{ + VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; + VCHIQ_QUEUE_BULK_TRANSFER_T args; + int ret; + + switch ((int)flags) { + case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED: + args.mode = VCHIQ_BULK_MODE_CALLBACK; + break; + case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE: + args.mode = VCHIQ_BULK_MODE_BLOCKING; + break; + case VCHI_FLAGS_BLOCK_UNTIL_QUEUED: + case VCHI_FLAGS_NONE: + args.mode = VCHIQ_BULK_MODE_NOCALLBACK; + break; + default: + vcos_assert(0); + break; + } + + args.handle = service->handle; + args.data = data_dst; + args.size = data_size; + args.userdata = bulk_handle; + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_RECEIVE, &args)); + + return ret; +} + +/*********************************************************** + * Name: vchi_bulk_queue_transmit + * + * Arguments: VCHI_BULK_HANDLE_T handle, + * const void *data_src, + * uint32_t data_size, + * VCHI_FLAGS_T flags, + * void *bulk_handle + * + * Description: Routine to transmit some data + * + * Returns: int32_t - success == 0 + * + ***********************************************************/ +int32_t +vchi_bulk_queue_transmit( VCHI_SERVICE_HANDLE_T handle, + const void * data_src, + uint32_t data_size, + VCHI_FLAGS_T flags, + void * bulk_handle ) +{ + VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; + VCHIQ_QUEUE_BULK_TRANSFER_T args; + int ret; + + switch ((int)flags) { + case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED: + args.mode = VCHIQ_BULK_MODE_CALLBACK; + break; + case VCHI_FLAGS_BLOCK_UNTIL_DATA_READ: + case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE: + args.mode = VCHIQ_BULK_MODE_BLOCKING; + break; + case VCHI_FLAGS_BLOCK_UNTIL_QUEUED: + case VCHI_FLAGS_NONE: + args.mode = VCHIQ_BULK_MODE_NOCALLBACK; + break; + default: + vcos_assert(0); + break; + } + + args.handle = service->handle; + args.data = (void *)data_src; + args.size = data_size; + args.userdata = bulk_handle; + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_TRANSMIT, &args)); + + return ret; +} + +/*********************************************************** + * Name: vchi_msg_dequeue + * + * Arguments: VCHI_SERVICE_HANDLE_T handle, + * void *data, + * uint32_t max_data_size_to_read, + * uint32_t *actual_msg_size + * VCHI_FLAGS_T flags + * + * Description: Routine to dequeue a message into the supplied buffer + * + * Returns: int32_t - success == 0 + * + ***********************************************************/ +int32_t +vchi_msg_dequeue( VCHI_SERVICE_HANDLE_T handle, + void *data, + uint32_t max_data_size_to_read, + uint32_t *actual_msg_size, + VCHI_FLAGS_T flags ) +{ + VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; + VCHIQ_DEQUEUE_MESSAGE_T args; + int ret; + + vcos_assert(flags == VCHI_FLAGS_NONE || flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE); + + if (service->peek_size >= 0) + { + fprintf(stderr, "vchi_msg_dequeue -> using peek buffer\n"); + if ((uint32_t)service->peek_size <= max_data_size_to_read) + { + memcpy(data, service->peek_buf, service->peek_size); + *actual_msg_size = service->peek_size; + /* Invalidate the peek data, but retain the buffer */ + service->peek_size = -1; + ret = 0; + } + else + { + ret = -1; + } + } + else + { + args.handle = service->handle; + args.blocking = (flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE); + args.bufsize = max_data_size_to_read; + args.buf = data; + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_DEQUEUE_MESSAGE, &args)); + if (ret >= 0) + { + *actual_msg_size = ret; + ret = 0; + } + } + + if ((ret < 0) && (errno != EWOULDBLOCK)) + fprintf(stderr, "vchi_msg_dequeue -> %d(%d)\n", ret, errno); + + return ret; +} + +/*********************************************************** + * Name: vchi_msg_queuev + * + * Arguments: VCHI_SERVICE_HANDLE_T handle, + * const void *data, + * uint32_t data_size, + * VCHI_FLAGS_T flags, + * void *msg_handle + * + * Description: Thin wrapper to queue a message onto a connection + * + * Returns: int32_t - success == 0 + * + ***********************************************************/ + +vcos_static_assert(sizeof(VCHI_MSG_VECTOR_T) == sizeof(VCHIQ_ELEMENT_T)); +vcos_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_base) == offsetof(VCHIQ_ELEMENT_T, data)); +vcos_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_len) == offsetof(VCHIQ_ELEMENT_T, size)); + +int32_t +vchi_msg_queuev( VCHI_SERVICE_HANDLE_T handle, + VCHI_MSG_VECTOR_T * vector, + uint32_t count, + VCHI_FLAGS_T flags, + void *msg_handle ) +{ + VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; + VCHIQ_QUEUE_MESSAGE_T args; + int ret; + + vcos_unused(msg_handle); + + vcos_assert(flags == VCHI_FLAGS_BLOCK_UNTIL_QUEUED); + + args.handle = service->handle; + args.elements = (const VCHIQ_ELEMENT_T *)vector; + args.count = count; + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_MESSAGE, &args)); + + return ret; +} + +/*********************************************************** + * Name: vchi_held_msg_release + * + * Arguments: VCHI_HELD_MSG_T *message + * + * Description: Routine to release a held message (after it has been read with vchi_msg_hold) + * + * Returns: int32_t - success == 0 + * + ***********************************************************/ +int32_t +vchi_held_msg_release( VCHI_HELD_MSG_T *message ) +{ + int ret = -1; + + if (message && message->message && !message->service) + { + free_msgbuf(message->message); + ret = 0; + } + + return ret; +} + +/*********************************************************** + * Name: vchi_msg_hold + * + * Arguments: VCHI_SERVICE_HANDLE_T handle, + * void **data, + * uint32_t *msg_size, + * VCHI_FLAGS_T flags, + * VCHI_HELD_MSG_T *message_handle + * + * Description: Routine to return a pointer to the current message (to allow in place processing) + * The message is dequeued - don't forget to release the message using + * vchi_held_msg_release when you're finished + * + * Returns: int32_t - success == 0 + * + ***********************************************************/ +int32_t +vchi_msg_hold( VCHI_SERVICE_HANDLE_T handle, + void **data, + uint32_t *msg_size, + VCHI_FLAGS_T flags, + VCHI_HELD_MSG_T *message_handle ) +{ + VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; + int ret; + + ret = fill_peek_buf(service, flags); + + if (ret == 0) + { + *data = service->peek_buf; + *msg_size = service->peek_size; + + message_handle->message = service->peek_buf; + message_handle->service = NULL; + + service->peek_size = -1; + service->peek_buf = NULL; + } + + return 0; +} + +/*********************************************************** + * Name: vchi_initialise + * + * Arguments: VCHI_INSTANCE_T *instance_handle + * VCHI_CONNECTION_T **connections + * const uint32_t num_connections + * + * Description: Initialises the hardware but does not transmit anything + * When run as a Host App this will be called twice hence the need + * to malloc the state information + * + * Returns: 0 if successful, failure otherwise + * + ***********************************************************/ +int32_t +vchi_initialise( VCHI_INSTANCE_T *instance_handle ) +{ + VCHIQ_INSTANCE_T instance; + + instance = vchiq_lib_init(); + + vcos_log_trace( "%s: returning instance handle %p", __func__, instance ); + + *instance_handle = (VCHI_INSTANCE_T)instance; + + return (instance != NULL) ? 0 : -1; +} + +/*********************************************************** + * Name: vchi_connect + * + * Arguments: VCHI_CONNECTION_T **connections + * const uint32_t num_connections + * VCHI_INSTANCE_T instance_handle ) + * + * Description: Starts the command service on each connection, + * causing INIT messages to be pinged back and forth + * + * Returns: 0 if successful, failure otherwise + * + ***********************************************************/ +int32_t +vchi_connect( VCHI_CONNECTION_T **connections, + const uint32_t num_connections, + VCHI_INSTANCE_T instance_handle ) +{ + VCHIQ_STATUS_T status; + + vcos_unused(connections); + vcos_unused(num_connections); + + status = vchiq_connect((VCHIQ_INSTANCE_T)instance_handle); + + return (status == VCHIQ_SUCCESS) ? 0 : -1; +} + + +/*********************************************************** + * Name: vchi_disconnect + * + * Arguments: VCHI_INSTANCE_T instance_handle + * + * Description: Stops the command service on each connection, + * causing DE-INIT messages to be pinged back and forth + * + * Returns: 0 if successful, failure otherwise + * + ***********************************************************/ +int32_t +vchi_disconnect( VCHI_INSTANCE_T instance_handle ) +{ + VCHIQ_STATUS_T status; + + status = vchiq_shutdown((VCHIQ_INSTANCE_T)instance_handle); + + return (status == VCHIQ_SUCCESS) ? 0 : -1; +} + + +/*********************************************************** + * Name: vchi_service_open + * Name: vchi_service_create + * + * Arguments: VCHI_INSTANCE_T *instance_handle + * SERVICE_CREATION_T *setup, + * VCHI_SERVICE_HANDLE_T *handle + * + * Description: Routine to open a service + * + * Returns: int32_t - success == 0 + * + ***********************************************************/ +int32_t +vchi_service_open( VCHI_INSTANCE_T instance_handle, + SERVICE_CREATION_T *setup, + VCHI_SERVICE_HANDLE_T *handle ) +{ + VCHIQ_SERVICE_PARAMS_T params; + VCHIQ_STATUS_T status; + + memset(¶ms, 0, sizeof(params)); + params.fourcc = setup->service_id; + params.userdata = setup->callback_param; + + status = create_service((VCHIQ_INSTANCE_T)instance_handle, + ¶ms, + setup->callback, + 1/*open*/, + (VCHIQ_SERVICE_HANDLE_T *)handle); + + return (status == VCHIQ_SUCCESS) ? 0 : -1; +} + +int32_t +vchi_service_create( VCHI_INSTANCE_T instance_handle, + SERVICE_CREATION_T *setup, VCHI_SERVICE_HANDLE_T *handle ) +{ + VCHIQ_SERVICE_PARAMS_T params; + VCHIQ_STATUS_T status; + + memset(¶ms, 0, sizeof(params)); + params.fourcc = setup->service_id; + params.userdata = setup->callback_param; + + status = create_service((VCHIQ_INSTANCE_T)instance_handle, + ¶ms, + setup->callback, + 0/*!open*/, + (VCHIQ_SERVICE_HANDLE_T *)handle); + + return (status == VCHIQ_SUCCESS) ? 0 : -1; +} + +int32_t +vchi_service_close( const VCHI_SERVICE_HANDLE_T handle ) +{ + VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; + int ret; + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_REMOVE_SERVICE, service->handle)); + + if (ret == 0) + service->handle = VCHIQ_INVALID_HANDLE; + + return ret; +} + +int32_t +vchi_service_destroy( const VCHI_SERVICE_HANDLE_T handle ) +{ + VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; + int ret; + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_REMOVE_SERVICE, service->handle)); + + if (ret == 0) + service->handle = VCHIQ_INVALID_HANDLE; + + return ret; +} + +/* ---------------------------------------------------------------------- + * read a uint32_t from buffer. + * network format is defined to be little endian + * -------------------------------------------------------------------- */ +uint32_t +vchi_readbuf_uint32( const void *_ptr ) +{ + const unsigned char *ptr = _ptr; + return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); +} + +/* ---------------------------------------------------------------------- + * write a uint32_t to buffer. + * network format is defined to be little endian + * -------------------------------------------------------------------- */ +void +vchi_writebuf_uint32( void *_ptr, uint32_t value ) +{ + unsigned char *ptr = _ptr; + ptr[0] = (unsigned char)((value >> 0) & 0xFF); + ptr[1] = (unsigned char)((value >> 8) & 0xFF); + ptr[2] = (unsigned char)((value >> 16) & 0xFF); + ptr[3] = (unsigned char)((value >> 24) & 0xFF); +} + +/* ---------------------------------------------------------------------- + * read a uint16_t from buffer. + * network format is defined to be little endian + * -------------------------------------------------------------------- */ +uint16_t +vchi_readbuf_uint16( const void *_ptr ) +{ + const unsigned char *ptr = _ptr; + return ptr[0] | (ptr[1] << 8); +} + +/* ---------------------------------------------------------------------- + * write a uint16_t into the buffer. + * network format is defined to be little endian + * -------------------------------------------------------------------- */ +void +vchi_writebuf_uint16( void *_ptr, uint16_t value ) +{ + unsigned char *ptr = _ptr; + ptr[0] = (value >> 0) & 0xFF; + ptr[1] = (value >> 8) & 0xFF; +} + +/*********************************************************** + * Name: vchi_service_use + * + * Arguments: const VCHI_SERVICE_HANDLE_T handle + * + * Description: Routine to increment refcount on a service + * + * Returns: void + * + ***********************************************************/ +int32_t +vchi_service_use( const VCHI_SERVICE_HANDLE_T handle ) +{ + VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; + int ret; + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_USE_SERVICE, service->handle)); + return ret; +} + +/*********************************************************** + * Name: vchi_service_release + * + * Arguments: const VCHI_SERVICE_HANDLE_T handle + * + * Description: Routine to decrement refcount on a service + * + * Returns: void + * + ***********************************************************/ +int32_t vchi_service_release( const VCHI_SERVICE_HANDLE_T handle ) +{ + VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; + int ret; + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_RELEASE_SERVICE, service->handle)); + return ret; +} + +/* + * Support functions + */ + +static VCHIQ_INSTANCE_T +vchiq_lib_init(void) +{ + static int mutex_initialised = 0; + static VCOS_MUTEX_T vchiq_lib_mutex; + VCHIQ_INSTANCE_T instance = &vchiq_instance; + + vcos_global_lock(); + if (!mutex_initialised) + { + vcos_mutex_create(&vchiq_lib_mutex, "vchiq-init"); + + vcos_log_set_level( &vchiq_lib_log_category, vchiq_default_lib_log_level ); + vcos_log_register( "vchiq_lib", &vchiq_lib_log_category ); + + mutex_initialised = 1; + } + vcos_global_unlock(); + + vcos_mutex_lock(&vchiq_lib_mutex); + + if (instance->initialised == 0) + { + instance->fd = open("/dev/vchiq", O_RDWR); + if (instance->fd >= 0) + { + VCHIQ_GET_CONFIG_T args; + VCHIQ_CONFIG_T config; + int ret; + args.config_size = sizeof(config); + args.pconfig = &config; + RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_GET_CONFIG, &args)); + if ((ret == 0) && (config.version >= VCHIQ_VERSION_MIN) && (config.version_min <= VCHIQ_VERSION)) + { + instance->used_services = 0; + vcos_mutex_create(&instance->mutex, "VCHIQ instance"); + instance->initialised = 1; + } + else + { + if (ret == 0) + { + vcos_log_error("Incompatible VCHIQ library - driver version %d (min %d), library version %d (min %d)", + config.version, config.version_min, VCHIQ_VERSION, VCHIQ_VERSION_MIN); + } + else + { + vcos_log_error("Very incompatible VCHIQ library - cannot retrieve driver version"); + } + close(instance->fd); + instance = NULL; + } + } + else + { + instance = NULL; + } + } + else if (instance->initialised > 0) + { + instance->initialised++; + } + + vcos_mutex_unlock(&vchiq_lib_mutex); + + return instance; +} + +static void * +completion_thread(void *arg) +{ + VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)arg; + VCHIQ_AWAIT_COMPLETION_T args; + VCHIQ_COMPLETION_DATA_T completions[8]; + void *msgbufs[8]; + + static const VCHI_CALLBACK_REASON_T vchiq_reason_to_vchi[] = + { + VCHI_CALLBACK_SERVICE_OPENED, // VCHIQ_SERVICE_OPENED + VCHI_CALLBACK_SERVICE_CLOSED, // VCHIQ_SERVICE_CLOSED + VCHI_CALLBACK_MSG_AVAILABLE, // VCHIQ_MESSAGE_AVAILABLE + VCHI_CALLBACK_BULK_SENT, // VCHIQ_BULK_TRANSMIT_DONE + VCHI_CALLBACK_BULK_RECEIVED, // VCHIQ_BULK_RECEIVE_DONE + VCHI_CALLBACK_BULK_TRANSMIT_ABORTED, // VCHIQ_BULK_TRANSMIT_ABORTED + VCHI_CALLBACK_BULK_RECEIVE_ABORTED, // VCHIQ_BULK_RECEIVE_ABORTED + }; + + args.count = vcos_countof(completions); + args.buf = completions; + args.msgbufsize = MSGBUF_SIZE; + args.msgbufcount = 0; + args.msgbufs = msgbufs; + + while (1) + { + int ret, i; + + while ((unsigned int)args.msgbufcount < vcos_countof(msgbufs)) + { + void *msgbuf = alloc_msgbuf(); + if (msgbuf) + { + msgbufs[args.msgbufcount++] = msgbuf; + } + else + { + fprintf(stderr, "vchiq_lib: failed to allocate a message buffer\n"); + vcos_demand(args.msgbufcount != 0); + } + } + + RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_AWAIT_COMPLETION, &args)); + + if (ret <= 0) + break; + + for (i = 0; i < ret; i++) + { + VCHIQ_COMPLETION_DATA_T *completion = &completions[i]; + VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)completion->service_userdata; + if (service->base.callback) + { + vcos_log_trace( "callback(%x, %x, %x, %x)", + completion->reason, (uint32_t)completion->header, + (uint32_t)&service->base, (uint32_t)completion->bulk_userdata ); + service->base.callback(completion->reason, completion->header, + &service->base, completion->bulk_userdata); + } + else if (service->vchi_callback) + { + VCHI_CALLBACK_REASON_T vchi_reason = + vchiq_reason_to_vchi[completion->reason]; + service->vchi_callback(service->base.userdata, vchi_reason, completion->bulk_userdata); + } + } + } + return NULL; +} + +static VCHIQ_STATUS_T +create_service(VCHIQ_INSTANCE_T instance, + const VCHIQ_SERVICE_PARAMS_T *params, + VCHI_CALLBACK_T vchi_callback, + int is_open, + VCHIQ_SERVICE_HANDLE_T *pservice) +{ + VCHIQ_SERVICE_T *service = NULL; + VCHIQ_STATUS_T status = VCHIQ_SUCCESS; + int i; + + if (!is_valid_instance(instance)) + return VCHIQ_ERROR; + + vcos_mutex_lock(&instance->mutex); + + /* Find a free service */ + if (is_open) + { + /* Find a free service */ + for (i = 0; i < instance->used_services; i++) + { + if (instance->services[i].handle == VCHIQ_INVALID_HANDLE) + { + service = &instance->services[i]; + break; + } + } + } + else + { + for (i = (instance->used_services - 1); i >= 0; i--) + { + VCHIQ_SERVICE_T *srv = &instance->services[i]; + if (srv->handle == VCHIQ_INVALID_HANDLE) + { + service = srv; + } + else if ( + (srv->base.fourcc == params->fourcc) && + ((srv->base.callback != params->callback) || + (srv->vchi_callback != vchi_callback))) + { + /* There is another server using this fourcc which doesn't match */ + service = NULL; + status = VCHIQ_ERROR; + break; + } + } + } + + if (!service && (status == VCHIQ_SUCCESS) && + (instance->used_services < VCHIQ_MAX_INSTANCE_SERVICES)) + service = &instance->services[instance->used_services++]; + + if (service) + { + VCHIQ_CREATE_SERVICE_T args; + int ret; + service->base.fourcc = params->fourcc; + service->base.callback = params->callback; + service->vchi_callback = vchi_callback; + service->base.userdata = params->userdata; + service->fd = instance->fd; + service->peek_size = -1; + service->peek_buf = NULL; + + args.params = *params; + args.params.userdata = service; + args.is_open = is_open; + args.is_vchi = (params->callback == NULL); + args.handle = -1; /* OUT parameter */ + RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_CREATE_SERVICE, &args)); + if (ret == 0) + service->handle = args.handle; + else + status = VCHIQ_ERROR; + } + + *pservice = (status == VCHIQ_SUCCESS) ? &service->base : NULL; + + vcos_mutex_unlock(&instance->mutex); + + return status; +} + +static int +fill_peek_buf(VCHI_SERVICE_T *service, + VCHI_FLAGS_T flags) +{ + VCHIQ_DEQUEUE_MESSAGE_T args; + int ret = 0; + + vcos_assert(flags == VCHI_FLAGS_NONE || flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE); + + if (service->peek_size < 0) + { + if (!service->peek_buf) + service->peek_buf = alloc_msgbuf(); + + if (service->peek_buf) + { + args.handle = service->handle; + args.blocking = (flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE); + args.bufsize = MSGBUF_SIZE; + args.buf = service->peek_buf; + + RETRY(ret, ioctl(service->fd, VCHIQ_IOC_DEQUEUE_MESSAGE, &args)); + + if (ret >= 0) + { + service->peek_size = ret; + ret = 0; + } + else + { + ret = -1; + } + } + else + { + ret = -1; + } + } + + return ret; +} + + +static void * +alloc_msgbuf(void) +{ + void *msgbuf; + vcos_mutex_lock(&vchiq_lib_mutex); + msgbuf = free_msgbufs; + if (msgbuf) + free_msgbufs = *(void **)msgbuf; + vcos_mutex_unlock(&vchiq_lib_mutex); + if (!msgbuf) + msgbuf = malloc(MSGBUF_SIZE); + return msgbuf; +} + +static void +free_msgbuf(void *buf) +{ + vcos_mutex_lock(&vchiq_lib_mutex); + *(void **)buf = free_msgbufs; + free_msgbufs = buf; + vcos_mutex_unlock(&vchiq_lib_mutex); +} --- /dev/null +++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_memdrv.h @@ -0,0 +1,45 @@ +/***************************************************************************** +* Copyright 2001 - 2010 Broadcom Corporation. All rights reserved. +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed to you +* under the terms of the GNU General Public License version 2, available at +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). +* +* Notwithstanding the above, under no circumstances may you combine this +* software in any way with any other Broadcom software provided under a +* license other than the GPL, without Broadcom's express prior written +* consent. +*****************************************************************************/ + +#ifndef VCHIQ_MEMDRV_H +#define VCHIQ_MEMDRV_H + +/* ---- Include Files ----------------------------------------------------- */ + +#include +#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 + +#if defined(__KERNEL__) +#include +#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; iu.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; iu.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 +#endif + +static __inline int is_pow2(int i) +{ + return i && !(i & (i - 1)); +} + +int vchiu_queue_init(VCHIU_QUEUE_T *queue, int size) +{ + vcos_assert(is_pow2(size)); + + queue->size = size; + queue->read = 0; + queue->write = 0; + + vcos_event_create(&queue->pop, "vchiu"); + vcos_event_create(&queue->push, "vchiu"); + + queue->storage = vcos_malloc(size * sizeof(VCHIQ_HEADER_T *), VCOS_FUNCTION); + if (queue->storage == NULL) + { + vchiu_queue_delete(queue); + return 0; + } + return 1; +} + +void vchiu_queue_delete(VCHIU_QUEUE_T *queue) +{ + vcos_event_delete(&queue->pop); + vcos_event_delete(&queue->push); + if (queue->storage != NULL) + vcos_free(queue->storage); +} + +int vchiu_queue_is_empty(VCHIU_QUEUE_T *queue) +{ + return queue->read == queue->write; +} + +void vchiu_queue_push(VCHIU_QUEUE_T *queue, VCHIQ_HEADER_T *header) +{ + while (queue->write == queue->read + queue->size) + vcos_event_wait(&queue->pop); + + queue->storage[queue->write & (queue->size - 1)] = header; + + queue->write++; + + vcos_event_signal(&queue->push); +} + +VCHIQ_HEADER_T *vchiu_queue_peek(VCHIU_QUEUE_T *queue) +{ + while (queue->write == queue->read) + vcos_event_wait(&queue->push); + + return queue->storage[queue->read & (queue->size - 1)]; +} + +VCHIQ_HEADER_T *vchiu_queue_pop(VCHIU_QUEUE_T *queue) +{ + VCHIQ_HEADER_T *header; + + while (queue->write == queue->read) + vcos_event_wait(&queue->push); + + header = queue->storage[queue->read & (queue->size - 1)]; + + queue->read++; + + vcos_event_signal(&queue->pop); + + return header; +} --- /dev/null +++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef VCHIQ_UTIL_H +#define VCHIQ_UTIL_H + +#include "vchiq_if.h" +#include "interface/vcos/vcos.h" + +typedef struct { + int size; + int read; + int write; + + VCOS_EVENT_T pop; + VCOS_EVENT_T push; + + VCHIQ_HEADER_T **storage; +} VCHIU_QUEUE_T; + +extern int vchiu_queue_init(VCHIU_QUEUE_T *queue, int size); +extern void vchiu_queue_delete(VCHIU_QUEUE_T *queue); + +extern int vchiu_queue_is_empty(VCHIU_QUEUE_T *queue); + +extern void vchiu_queue_push(VCHIU_QUEUE_T *queue, VCHIQ_HEADER_T *header); + +extern VCHIQ_HEADER_T *vchiu_queue_peek(VCHIU_QUEUE_T *queue); +extern VCHIQ_HEADER_T *vchiu_queue_pop(VCHIU_QUEUE_T *queue); + +#endif + --- /dev/null +++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_cmd.c @@ -0,0 +1,681 @@ +/***************************************************************************** +* Copyright 2009 - 2011 Broadcom Corporation. All rights reserved. +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed to you +* under the terms of the GNU General Public License version 2, available at +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). +* +* Notwithstanding the above, under no circumstances may you combine this +* software in any way with any other Broadcom software provided under a +* license other than the GPL, without Broadcom's express prior written +* consent. +*****************************************************************************/ + +/***************************************************************************** +* +* This file provides a generic command line interface which allows +* vcos internals to be manipulated and/or displayed. +* +*****************************************************************************/ + +/* ---- Include Files ---------------------------------------------------- */ + +#include "interface/vcos/vcos.h" + +#ifdef HAVE_VCOS_VERSION +#include "interface/vcos/vcos_build_info.h" +#endif + + #ifdef _VIDEOCORE +#include vcfw/logging/logging.h +#endif + +/* ---- Public Variables ------------------------------------------------- */ + +/* ---- Private Constants and Types -------------------------------------- */ + +#define VCOS_LOG_CATEGORY (&vcos_cmd_log_category) +VCOS_LOG_CAT_T vcos_cmd_log_category; + +/* ---- Private Variables ------------------------------------------------ */ + +static struct VCOS_CMD_GLOBALS_T +{ + VCOS_MUTEX_T lock; + VCOS_ONCE_T initialized; + + unsigned num_cmd_entries; + unsigned num_cmd_alloc; + VCOS_CMD_T *cmd_entry; + + VCOS_LOG_CAT_T *log_category; +} cmd_globals; + +/* ---- Private Function Prototypes -------------------------------------- */ + +static VCOS_STATUS_T help_cmd( VCOS_CMD_PARAM_T *param ); + +/* ---- Functions ------------------------------------------------------- */ + +/***************************************************************************** +* +* Walks through the commands looking for a particular command +* +*****************************************************************************/ + +static VCOS_CMD_T *find_cmd( VCOS_CMD_T *cmd_entry, const char *name ) +{ + VCOS_CMD_T *scan_entry = cmd_entry; + + while ( scan_entry->name != NULL ) + { + if ( vcos_strcmp( scan_entry->name, name ) == 0 ) + { + return scan_entry; + } + scan_entry++; + } + + return NULL; +} + +/***************************************************************************** +* +* Saves away +* each line individually. +* +*****************************************************************************/ + +void vcos_cmd_always_log_output( VCOS_LOG_CAT_T *log_category ) +{ + cmd_globals.log_category = log_category; +} + +/***************************************************************************** +* +* Walks through a buffer containing newline separated lines, and logs +* each line individually. +* +*****************************************************************************/ + +static void cmd_log_results( VCOS_CMD_PARAM_T *param ) +{ + char *start; + char *end; + + start = end = param->result_buf; + + while ( *start != '\0' ) + { + while (( *end != '\0' ) && ( *end != '\n' )) + end++; + + if ( *end == '\n' ) + { + *end++ = '\0'; + } + + if ( cmd_globals.log_category != NULL ) + { + if ( vcos_is_log_enabled( cmd_globals.log_category, VCOS_LOG_INFO )) + { + vcos_log_impl( cmd_globals.log_category, VCOS_LOG_INFO, "%s", start ); + } + } + else + { + vcos_log_info( "%s", start ); + } + + start = end; + } + + /* Since we logged the buffer, reset the pointer back to the beginning. */ + + param->result_ptr = param->result_buf; + param->result_buf[0] = '\0'; +} + +/***************************************************************************** +* +* Since we may have limited output space, we create a generic routine +* which tries to use the result space, but will switch over to using +* logging if the output is too large. +* +*****************************************************************************/ + +void vcos_cmd_vprintf( VCOS_CMD_PARAM_T *param, const char *fmt, va_list args ) +{ + int bytes_written; + int bytes_remaining; + + bytes_remaining = (int)(param->result_size - ( param->result_ptr - param->result_buf )); + + bytes_written = vcos_vsnprintf( param->result_ptr, bytes_remaining, fmt, args ); + + if ( cmd_globals.log_category != NULL ) + { + /* We're going to log each line as we encounter it. If the buffer + * doesn't end in a newline, then we'll wait for one first. + */ + + if ( (( bytes_written + 1 ) >= bytes_remaining ) + || ( param->result_ptr[ bytes_written - 1 ] == '\n' )) + { + cmd_log_results( param ); + } + else + { + param->result_ptr += bytes_written; + } + } + else + { + if (( bytes_written + 1 ) >= bytes_remaining ) + { + /* Output doesn't fit - switch over to logging */ + + param->use_log = 1; + + *param->result_ptr = '\0'; /* Zap the partial line that didn't fit above. */ + + cmd_log_results( param ); /* resets result_ptr */ + + bytes_written = vcos_vsnprintf( param->result_ptr, bytes_remaining, fmt, args ); + } + param->result_ptr += bytes_written; + } +} + +/***************************************************************************** +* +* Prints the output. +* +*****************************************************************************/ + +void vcos_cmd_printf( VCOS_CMD_PARAM_T *param, const char *fmt, ... ) +{ + va_list args; + + va_start( args, fmt ); + vcos_cmd_vprintf( param, fmt, args ); + va_end( args ); +} + +/***************************************************************************** +* +* Prints the arguments which were on the command line prior to ours. +* +*****************************************************************************/ + +static void print_argument_prefix( VCOS_CMD_PARAM_T *param ) +{ + int arg_idx; + + for ( arg_idx = 0; ¶m->argv_orig[arg_idx] != param->argv; arg_idx++ ) + { + vcos_cmd_printf( param, "%s ", param->argv_orig[arg_idx] ); + } +} + +/***************************************************************************** +* +* Prints an error message, prefixed by the command chain required to get +* to where we're at. +* +*****************************************************************************/ + +void vcos_cmd_error( VCOS_CMD_PARAM_T *param, const char *fmt, ... ) +{ + va_list args; + + print_argument_prefix( param ); + + va_start( args, fmt ); + vcos_cmd_vprintf( param, fmt, args ); + va_end( args ); + vcos_cmd_printf( param, "\n" ); +} + +/**************************************************************************** +* +* usage - prints command usage for an array of commands. +* +***************************************************************************/ + +static void usage( VCOS_CMD_PARAM_T *param, VCOS_CMD_T *cmd_entry ) +{ + int cmd_idx; + int nameWidth = 0; + int argsWidth = 0; + VCOS_CMD_T *scan_entry; + + vcos_cmd_printf( param, "Usage: " ); + print_argument_prefix( param ); + vcos_cmd_printf( param, "command [args ...]\n" ); + vcos_cmd_printf( param, "\n" ); + vcos_cmd_printf( param, "Where command is one of the following:\n" ); + + for ( cmd_idx = 0; cmd_entry[cmd_idx].name != NULL; cmd_idx++ ) + { + int aw; + int nw; + + scan_entry = &cmd_entry[cmd_idx]; + + nw = vcos_strlen( scan_entry->name ); + aw = vcos_strlen( scan_entry->args ); + + if ( nw > nameWidth ) + { + nameWidth = nw; + } + if ( aw > argsWidth ) + { + argsWidth = aw; + } + } + + for ( cmd_idx = 0; cmd_entry[cmd_idx].name != NULL; cmd_idx++ ) + { + scan_entry = &cmd_entry[cmd_idx]; + + vcos_cmd_printf( param, " %-*s %-*s - %s\n", + nameWidth, scan_entry->name, + argsWidth, scan_entry->args, + scan_entry->descr ); + } +} + +/**************************************************************************** +* +* Prints the usage for the current command. +* +***************************************************************************/ + +void vcos_cmd_usage( VCOS_CMD_PARAM_T *param ) +{ + VCOS_CMD_T *cmd_entry; + + cmd_entry = param->cmd_entry; + + if ( cmd_entry->sub_cmd_entry != NULL ) + { + /* This command is command with sub-commands */ + + usage( param, param->cmd_entry->sub_cmd_entry ); + } + else + { + vcos_cmd_printf( param, "Usage: " ); + print_argument_prefix( param ); + vcos_cmd_printf( param, "%s - %s\n", + param->cmd_entry->args, + param->cmd_entry->descr ); + } +} + +/***************************************************************************** +* +* Command to print out the help +* +* This help command is only called from the main menu. +* +*****************************************************************************/ + +static VCOS_STATUS_T help_cmd( VCOS_CMD_PARAM_T *param ) +{ + VCOS_CMD_T *found_entry; + +#if 0 + { + int arg_idx; + + vcos_log_trace( "%s: argc = %d", __func__, param->argc ); + for ( arg_idx = 0; arg_idx < param->argc; arg_idx++ ) + { + vcos_log_trace( "%s: argv[%d] = '%s'", __func__, arg_idx, param->argv[arg_idx] ); + } + } +#endif + + /* If there is an argument after the word help, then we want to print + * help for that command. + */ + + if ( param->argc == 1 ) + { + if ( param->cmd_parent_entry == cmd_globals.cmd_entry ) + { + /* Bare help - print the command usage for the root */ + + usage( param, cmd_globals.cmd_entry ); + return VCOS_SUCCESS; + } + + /* For all other cases help requires an argument */ + + vcos_cmd_error( param, "%s requires an argument", param->argv[0] ); + return VCOS_EINVAL; + } + + /* We were given an argument. */ + + if (( found_entry = find_cmd( param->cmd_parent_entry, param->argv[1] )) != NULL ) + { + /* Make it look like the command that was specified is the one that's + * currently running + */ + + param->cmd_entry = found_entry; + param->argv[0] = param->argv[1]; + param->argv++; + param->argc--; + + vcos_cmd_usage( param ); + return VCOS_SUCCESS; + } + + vcos_cmd_error( param, "- unrecognized command: '%s'", param->argv[1] ); + return VCOS_ENOENT; +} + +/***************************************************************************** +* +* Command to print out the version/build information. +* +*****************************************************************************/ + +#ifdef HAVE_VCOS_VERSION + +static VCOS_STATUS_T version_cmd( VCOS_CMD_PARAM_T *param ) +{ + static const char* copyright = "Copyright (c) 2011 Broadcom"; + + vcos_cmd_printf( param, "%s %s\n%s\nversion %s\n", + vcos_get_build_date(), + vcos_get_build_time(), + copyright, + vcos_get_build_version() ); + + return VCOS_SUCCESS; +} + +#endif + +/***************************************************************************** +* +* Internal commands +* +*****************************************************************************/ + +static VCOS_CMD_T cmd_help = { "help", "[command]", help_cmd, NULL, "Prints command help information" }; + +#ifdef HAVE_VCOS_VERSION +static VCOS_CMD_T cmd_version = { "version", "", version_cmd, NULL, "Prints build/version information" }; +#endif + +/***************************************************************************** +* +* Walks the command table and executes the commands +* +*****************************************************************************/ + +static VCOS_STATUS_T execute_cmd( VCOS_CMD_PARAM_T *param, VCOS_CMD_T *cmd_entry ) +{ + const char *cmdStr; + VCOS_CMD_T *found_entry; + +#if 0 + { + int arg_idx; + + vcos_cmd_printf( param, "%s: argc = %d", __func__, param->argc ); + for ( arg_idx = 0; arg_idx < param->argc; arg_idx++ ) + { + vcos_cmd_printf( param, " argv[%d] = '%s'", arg_idx, param->argv[arg_idx] ); + } + vcos_cmd_printf( param, "\n" ); + } +#endif + + if ( param->argc <= 1 ) + { + /* No command specified */ + + vcos_cmd_error( param, "%s - no command specified", param->argv[0] ); + return VCOS_EINVAL; + } + + /* argv[0] is the command/program that caused us to get invoked, so we strip + * it off. + */ + + param->argc--; + param->argv++; + param->cmd_parent_entry = cmd_entry; + + /* Not the help command, scan for the command and execute it. */ + + cmdStr = param->argv[0]; + + if (( found_entry = find_cmd( cmd_entry, cmdStr )) != NULL ) + { + if ( found_entry->sub_cmd_entry != NULL ) + { + return execute_cmd( param, found_entry->sub_cmd_entry ); + } + + param->cmd_entry = found_entry; + return found_entry->cmd_fn( param ); + } + + /* Unrecognized command - check to see if it was the help command */ + + if ( vcos_strcmp( cmdStr, cmd_help.name ) == 0 ) + { + return help_cmd( param ); + } + + vcos_cmd_error( param, "- unrecognized command: '%s'", cmdStr ); + return VCOS_ENOENT; +} + +/***************************************************************************** +* +* Initializes the command line parser. +* +*****************************************************************************/ + +static void vcos_cmd_init( void ) +{ + vcos_mutex_create( &cmd_globals.lock, "vcos_cmd" ); + + cmd_globals.num_cmd_entries = 0; + cmd_globals.num_cmd_alloc = 0; + cmd_globals.cmd_entry = NULL; +} + +/***************************************************************************** +* +* Command line processor. +* +*****************************************************************************/ + +VCOS_STATUS_T vcos_cmd_execute( int argc, char **argv, size_t result_size, char *result_buf ) +{ + VCOS_STATUS_T rc = VCOS_EINVAL; + VCOS_CMD_PARAM_T param; + + vcos_once( &cmd_globals.initialized, vcos_cmd_init ); + + param.argc = argc; + param.argv = param.argv_orig = argv; + + param.use_log = 0; + param.result_size = result_size; + param.result_ptr = result_buf; + param.result_buf = result_buf; + + result_buf[0] = '\0'; + + vcos_mutex_lock( &cmd_globals.lock ); + + rc = execute_cmd( ¶m, cmd_globals.cmd_entry ); + + if ( param.use_log ) + { + cmd_log_results( ¶m ); + vcos_snprintf( result_buf, result_size, "results logged" ); + } + else + if ( cmd_globals.log_category != NULL ) + { + if ( result_buf[0] != '\0' ) + { + /* There is a partial line still buffered. */ + + vcos_cmd_printf( ¶m, "\n" ); + } + } + + vcos_mutex_unlock( &cmd_globals.lock ); + + return rc; +} + +/***************************************************************************** +* +* Registers a command entry with the command line processor +* +*****************************************************************************/ + +VCOS_STATUS_T vcos_cmd_register( VCOS_CMD_T *cmd_entry ) +{ + VCOS_STATUS_T rc; + VCOS_UNSIGNED new_num_cmd_alloc; + VCOS_CMD_T *new_cmd_entry; + VCOS_CMD_T *old_cmd_entry; + VCOS_CMD_T *scan_entry; + + vcos_once( &cmd_globals.initialized, vcos_cmd_init ); + + vcos_assert( cmd_entry != NULL ); + vcos_assert( cmd_entry->name != NULL ); + + vcos_log_trace( "%s: cmd '%s'", __FUNCTION__, cmd_entry->name ); + + vcos_assert( cmd_entry->args != NULL ); + vcos_assert(( cmd_entry->cmd_fn != NULL ) || ( cmd_entry->sub_cmd_entry != NULL )); + vcos_assert( cmd_entry->descr != NULL ); + + /* We expect vcos_cmd_init to be called before vcos_logging_init, so we + * need to defer registering our logging category until someplace + * like right here. + */ + + if ( vcos_cmd_log_category.name == NULL ) + { + /* + * If you're using the command interface, you pretty much always want + * log messages from this file to show up. So we change the default + * from ERROR to be the more reasonable INFO level. + */ + + vcos_log_set_level(&vcos_cmd_log_category, VCOS_LOG_INFO); + vcos_log_register("vcos_cmd", &vcos_cmd_log_category); + + /* We register a help command so that it shows up in the usage. */ + + vcos_cmd_register( &cmd_help ); +#ifdef HAVE_VCOS_VERSION + vcos_cmd_register( &cmd_version ); +#endif + } + + vcos_mutex_lock( &cmd_globals.lock ); + + if ( cmd_globals.num_cmd_entries >= cmd_globals.num_cmd_alloc ) + { + if ( cmd_globals.num_cmd_alloc == 0 ) + { + /* We haven't allocated a table yet */ + } + + /* The number 8 is rather arbitrary. */ + + new_num_cmd_alloc = cmd_globals.num_cmd_alloc + 8; + + /* The + 1 is to ensure that we always have a NULL entry at the end. */ + + new_cmd_entry = (VCOS_CMD_T *)vcos_calloc( new_num_cmd_alloc + 1, sizeof( *cmd_entry ), "vcos_cmd_entries" ); + if ( new_cmd_entry == NULL ) + { + rc = VCOS_ENOMEM; + goto out; + } + memcpy( new_cmd_entry, cmd_globals.cmd_entry, cmd_globals.num_cmd_entries * sizeof( *cmd_entry )); + cmd_globals.num_cmd_alloc = new_num_cmd_alloc; + old_cmd_entry = cmd_globals.cmd_entry; + cmd_globals.cmd_entry = new_cmd_entry; + vcos_free( old_cmd_entry ); + } + + if ( cmd_globals.num_cmd_entries == 0 ) + { + /* This is the first command being registered */ + + cmd_globals.cmd_entry[0] = *cmd_entry; + } + else + { + /* Keep the list in alphabetical order. We start at the end and work backwards + * shuffling entries up one until we find an insertion point. + */ + + for ( scan_entry = &cmd_globals.cmd_entry[cmd_globals.num_cmd_entries - 1]; + scan_entry >= cmd_globals.cmd_entry; scan_entry-- ) + { + if ( vcos_strcmp( cmd_entry->name, scan_entry->name ) > 0 ) + { + /* We found an insertion point. */ + + break; + } + + scan_entry[1] = scan_entry[0]; + } + scan_entry[1] = *cmd_entry; + } + cmd_globals.num_cmd_entries++; + + rc = VCOS_SUCCESS; + +out: + + vcos_mutex_unlock( &cmd_globals.lock ); + return rc; +} + +/***************************************************************************** +* +* Registers multiple commands. +* +*****************************************************************************/ + +VCOS_STATUS_T vcos_cmd_register_multiple( VCOS_CMD_T *cmd_entry ) +{ + VCOS_STATUS_T status; + + while ( cmd_entry->name != NULL ) + { + if (( status = vcos_cmd_register( cmd_entry )) != VCOS_SUCCESS ) + { + return status; + } + cmd_entry++; + } + return VCOS_SUCCESS; +} + --- /dev/null +++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_common.h @@ -0,0 +1,76 @@ +/*============================================================================= +Copyright (c) 2009 Broadcom Europe Limited. +All rights reserved. + +Project : vcfw +Module : chip driver + +FILE DESCRIPTION +VideoCore OS Abstraction Layer - common postamble code +=============================================================================*/ + +/** \file + * + * Postamble code included by the platform-specific header files + */ + +#define VCOS_THREAD_PRI_DEFAULT VCOS_THREAD_PRI_NORMAL + +#if !defined(VCOS_THREAD_PRI_INCREASE) +#error Which way to thread priorities go? +#endif + +#if VCOS_THREAD_PRI_INCREASE < 0 +/* smaller numbers are higher priority */ +#define VCOS_THREAD_PRI_LESS(x) ((x)VCOS_THREAD_PRI_MIN?(x)-1:VCOS_THREAD_PRI_MIN) +#else +/* bigger numbers are lower priority */ +#define VCOS_THREAD_PRI_MORE(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 + +/** 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 (tlsslots[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 (tlsslots[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 + * (,)* + * + * 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 +#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 so we have to provide one of + our own. */ + +#include /* 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 +#include +#include +#include +#include +#include + +#if defined( CONFIG_BCM_KNLLOG_SUPPORT ) +#include +#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 +#include +#include +#include + +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 +#include +#include +#include +#include + +/***************************************************************************** +* +* 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 + +/* ---- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for time_t */ +#include +#include + +#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 +#include +#include + +#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 +#include +#include +#include + +#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 + +#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 +#else +#include +#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 + +#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 + * + * 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 +#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 + +#elif defined( __GNUC__ ) + +#include + +#elif defined(_MSC_VER) /* Visual C define equivalent types */ + +#include /* 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 + +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 + +#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 +#else +#include +#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_.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 "" +# 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