Commit a0693145 authored by Luc Verhaegen's avatar Luc Verhaegen
Browse files

deprecate libump repository



Use sunxi-mali instead!
Signed-off-by: default avatarLuc Verhaegen <libv@skynet.be>
parent 9a6dad93
#
# Copyright (C) 2010-2012 ARM Limited. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
UMP_DIR ?= .
UMP_LIB ?= libUMP
UDD_OS ?= linux
CROSS_COMPILE ?= arm-none-linux-gnueabi-
TARGET_CC ?= $(CROSS_COMPILE)gcc
TARGET_AR ?= $(CROSS_COMPILE)ar
CFLAGS += -I$(UMP_DIR)/include -I$(UMP_DIR)/include/ump -Wall -march=armv6 -mthumb-interwork -fno-strict-aliasing -Wno-strict-aliasing -Wno-long-long -O3
include ump.mak
%.o: %.c
$(TARGET_CC) -c -o $@ $< $(CFLAGS)
UMP_OBJS := $(UMP_SRCS:.c=.o)
libUMP.so: $(UMP_OBJS)
$(TARGET_CC) -shared -o $@ $(UMP_OBJS) $(CFLAGS)
libUMP.a: $(UMP_OBJS)
$(TARGET_AR) rcs $@ $(UMP_OBJS)
.DEFAULT_GOAL = all
all: libUMP.so libUMP.a
clean:
-rm -f $(UMP_OBJS) libUMP.so libUMP.a
This repository is DEPRECATED and should not be used anymore.
Please use the sunxi-mali repository instead. It is available at
https://github.com/linux-sunxi/sunxi-mali
You can clone it by running:
> git clone https://github.com/linux-sunxi/sunxi-mali.git
> git submodule init
> git submodule update
libump
======
Unified Memory Provider userspace API source code needed for xf86-video-mali compilation
\ No newline at end of file
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file ump_arch.c
*
* UMP arch layer for UMP-UDD
*/
#include <ump/ump.h>
#include "ump_arch.h"
#include <ump/ump_debug.h>
#include <ump/ump_uk_types.h>
#include "../os/ump_uku.h"
/** Pointer to an OS-Specific context that we should pass in _uku_ calls */
void *ump_uk_ctx = NULL;
/** Reference counting of ump_arch_open() and ump_arch_close(). */
static volatile int ump_ref_count = 0;
/** Lock for critical section in open/close */
_ump_osu_lock_t * ump_lock_arch = NULL;
ump_result ump_arch_open(void)
{
ump_result retval = UMP_OK;
_ump_osu_lock_auto_init( &ump_lock_arch, 0, 0, 0 );
/* Check that the lock was initialized */
if (NULL == ump_lock_arch)
{
UMP_DEBUG_PRINT(1, ("UMP: ump_arch_open() failed to init lock\n"));
return UMP_ERROR;
}
/* Attempt to obtain a lock */
if( _UMP_OSU_ERR_OK != _ump_osu_lock_wait( ump_lock_arch, _UMP_OSU_LOCKMODE_RW ) )
{
UMP_DEBUG_PRINT(1, ("UMP: ump_arch_open() failed to acquire lock\n"));
return UMP_ERROR;
}
/* ASSERT NEEDED */
UMP_DEBUG_ASSERT(0 <= ump_ref_count, ("UMP: Reference count invalid at _ump_base_arch_open()"));
ump_ref_count++;
if (1 == ump_ref_count)
{
/* We are the first, open the UMP device driver */
if (_UMP_OSU_ERR_OK != _ump_uku_open( &ump_uk_ctx ))
{
UMP_DEBUG_PRINT(1, ("UMP: ump_arch_open() failed to open UMP device driver\n"));
retval = UMP_ERROR;
ump_ref_count--;
}
}
/* Signal the lock so someone else can use it */
_ump_osu_lock_signal( ump_lock_arch, _UMP_OSU_LOCKMODE_RW );
return retval;
}
void ump_arch_close(void)
{
_ump_osu_lock_auto_init( &ump_lock_arch, 0, 0, 0 );
/* Check that the lock was initialized */
if(NULL == ump_lock_arch)
{
UMP_DEBUG_PRINT(1, ("UMP: ump_arch_close() failed to init lock\n"));
return;
}
/* Attempt to obtain a lock */
if( _UMP_OSU_ERR_OK != _ump_osu_lock_wait( ump_lock_arch, _UMP_OSU_LOCKMODE_RW ) )
{
UMP_DEBUG_PRINT(1, ("UMP: ump_arch_close() failed to acquire lock\n"));
return;
}
UMP_DEBUG_ASSERT(0 < ump_ref_count, ("UMP: ump_arch_close() called while no references exist"));
if (ump_ref_count > 0)
{
ump_ref_count--;
if (0 == ump_ref_count)
{
_ump_osu_errcode_t retval = _ump_uku_close(&ump_uk_ctx);
UMP_DEBUG_ASSERT(retval == _UMP_OSU_ERR_OK, ("UMP: Failed to close UMP interface"));
UMP_IGNORE(retval);
ump_uk_ctx = NULL;
_ump_osu_lock_signal( ump_lock_arch, _UMP_OSU_LOCKMODE_RW );
_ump_osu_lock_term( ump_lock_arch ); /* Not 100% thread safe, since another thread can already be waiting for this lock in ump_arch_open() */
ump_lock_arch = NULL;
return;
}
}
/* Signal the lock so someone else can use it */
_ump_osu_lock_signal( ump_lock_arch, _UMP_OSU_LOCKMODE_RW );
}
ump_secure_id ump_arch_allocate(unsigned long * size, ump_alloc_constraints constraints)
{
_ump_uk_allocate_s call_arg;
if ( NULL == size )
{
return UMP_INVALID_SECURE_ID;
}
call_arg.ctx = ump_uk_ctx;
call_arg.secure_id = UMP_INVALID_SECURE_ID;
call_arg.size = *size;
#ifdef UMP_DEBUG_SKIP_CODE
/** Run-time ASSERTing that _ump_uk_api_version_s and ump_alloc_constraints are
* interchangable */
switch (constraints)
{
case UMP_REF_DRV_CONSTRAINT_NONE:
UMP_DEBUG_ASSERT( UMP_REF_DRV_UK_CONSTRAINT_NONE == constraints, ("ump_uk_alloc_constraints out of sync with ump_alloc_constraints") );
break;
case UMP_REF_DRV_CONSTRAINT_PHYSICALLY_LINEAR:
UMP_DEBUG_ASSERT( UMP_REF_DRV_UK_CONSTRAINT_PHYSICALLY_LINEAR == constraints, ("ump_uk_alloc_constraints out of sync with ump_alloc_constraints") );
break;
default:
UMP_DEBUG_ASSERT( 1, ("ump_uk_alloc_constraints out of sync with ump_alloc_constraints: %d unrecognized", constraints) );
break;
}
#endif
call_arg.constraints = (ump_uk_alloc_constraints)constraints;
if ( _UMP_OSU_ERR_OK != _ump_uku_allocate(&call_arg) )
{
return UMP_INVALID_SECURE_ID;
}
*size = call_arg.size;
UMP_DEBUG_PRINT(4, ("UMP: Allocated ID %u, size %ul", call_arg.secure_id, call_arg.size));
return call_arg.secure_id;
}
unsigned long ump_arch_size_get(ump_secure_id secure_id)
{
_ump_uk_size_get_s dd_size_call_arg;
dd_size_call_arg.ctx = ump_uk_ctx;
dd_size_call_arg.secure_id = secure_id;
dd_size_call_arg.size = 0;
if (_UMP_OSU_ERR_OK == _ump_uku_size_get( &dd_size_call_arg ) )
{
return dd_size_call_arg.size;
}
return 0;
}
void ump_arch_reference_release(ump_secure_id secure_id)
{
_ump_uk_release_s dd_release_call_arg;
_ump_osu_errcode_t retval;
dd_release_call_arg.ctx = ump_uk_ctx;
dd_release_call_arg.secure_id = secure_id;
UMP_DEBUG_PRINT(4, ("UMP: Releasing ID %u", secure_id));
retval = _ump_uku_release( &dd_release_call_arg );
UMP_DEBUG_ASSERT(retval == _UMP_OSU_ERR_OK, ("UMP: Failed to release reference to UMP memory"));
UMP_IGNORE(retval);
}
void* ump_arch_map(ump_secure_id secure_id, unsigned long size, ump_cache_enabled cache, unsigned long *cookie_out)
{
_ump_uk_map_mem_s dd_map_call_arg;
UMP_DEBUG_ASSERT_POINTER( cookie_out );
dd_map_call_arg.ctx = ump_uk_ctx;
dd_map_call_arg.secure_id = secure_id;
dd_map_call_arg.size = size;
dd_map_call_arg.is_cached = (u32) (UMP_CACHE_ENABLE==cache);
if ( -1 == _ump_uku_map_mem( &dd_map_call_arg ) )
{
UMP_DEBUG_PRINT(4, ("UMP: Mapping failed for ID %u", secure_id));
return NULL;
}
UMP_DEBUG_PRINT(4, ("Mapped %u at 0x%08lx", secure_id, (unsigned long)dd_map_call_arg.mapping));
*cookie_out = dd_map_call_arg.cookie;
return dd_map_call_arg.mapping;
}
void ump_arch_unmap(void* mapping, unsigned long size, unsigned long cookie)
{
_ump_uk_unmap_mem_s dd_unmap_call_arg;
dd_unmap_call_arg.ctx = ump_uk_ctx;
dd_unmap_call_arg.mapping = mapping;
dd_unmap_call_arg.size = size;
dd_unmap_call_arg.cookie = cookie;
UMP_DEBUG_PRINT(4, ("Unmapping 0x%08lx", (unsigned long)mapping));
_ump_uku_unmap_mem( &dd_unmap_call_arg );
}
/** Memory synchronization - cache flushing of mapped memory */
int ump_arch_msync(ump_secure_id secure_id, void* mapping, unsigned long cookie, void * address, unsigned long size, ump_cpu_msync_op op)
{
_ump_uk_msync_s dd_msync_call_arg;
dd_msync_call_arg.ctx = ump_uk_ctx;
dd_msync_call_arg.mapping = mapping;
dd_msync_call_arg.address = address;
dd_msync_call_arg.size = size;
dd_msync_call_arg.op = (ump_uk_msync_op)op;
dd_msync_call_arg.cookie = cookie;
dd_msync_call_arg.secure_id = secure_id;
dd_msync_call_arg.is_cached = 0;
UMP_DEBUG_PRINT(4, ("Msync 0x%08lx", (unsigned long)mapping));
_ump_uku_msynch( &dd_msync_call_arg );
if ( 0==dd_msync_call_arg.is_cached )
{
UMP_DEBUG_PRINT(4, ("Trying to flush uncached UMP mem ID: %d", secure_id));
}
return dd_msync_call_arg.is_cached;
}
/** Cache operation control. Tell when cache maintenance operations start and end.
This will allow the kernel to merge cache operations togheter, thus making them faster */
int ump_arch_cache_operations_control(ump_cache_op_control op)
{
_ump_uk_cache_operations_control_s dd_cache_control_arg;
dd_cache_control_arg.op = (ump_uk_cache_op_control)op;
dd_cache_control_arg.ctx = ump_uk_ctx;
UMP_DEBUG_PRINT(4, ("Cache control op:%d",(u32)op ));
_ump_uku_cache_operations_control( &dd_cache_control_arg );
return 1; /* Always success */
}
int ump_arch_switch_hw_usage( ump_secure_id secure_id, ump_hw_usage new_user )
{
_ump_uk_switch_hw_usage_s dd_sitch_user_arg;
dd_sitch_user_arg.secure_id = secure_id;
dd_sitch_user_arg.new_user = (ump_uk_user)new_user;
dd_sitch_user_arg.ctx = ump_uk_ctx;
UMP_DEBUG_PRINT(4, ("Switch user UMP:%d User:%d",secure_id, (u32)new_user ));
_ump_uku_switch_hw_usage( &dd_sitch_user_arg );
return 1; /* Always success */
}
int ump_arch_lock( ump_secure_id secure_id, ump_lock_usage lock_usage )
{
_ump_uk_lock_s dd_lock_arg;
dd_lock_arg.ctx = ump_uk_ctx;
dd_lock_arg.secure_id = secure_id;
dd_lock_arg.lock_usage = (ump_uk_lock_usage) lock_usage;
UMP_DEBUG_PRINT(4, ("Lock UMP:%d ",secure_id));
_ump_uku_lock( &dd_lock_arg );
return 1; /* Always success */
}
int ump_arch_unlock( ump_secure_id secure_id )
{
_ump_uk_unlock_s dd_unlock_arg;
dd_unlock_arg.ctx = ump_uk_ctx;
dd_unlock_arg.secure_id = secure_id;
UMP_DEBUG_PRINT(4, ("Lock UMP:%d ",secure_id));
_ump_uku_unlock( &dd_unlock_arg );
return 1; /* Always success */
}
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file ump_arch.h
*
* Header file for the arch dependent backend, which will do the communication with the UMP device driver.
*/
#ifndef _UNIFIED_MEMORY_PROVIDER_ARCH_H_
#define _UNIFIED_MEMORY_PROVIDER_ARCH_H_
#include <ump/ump.h>
#include <ump/ump_ref_drv.h>
#include "ump_internal.h"
#ifdef __cplusplus
extern "C" {
#endif
/** Open UMP interface. */
ump_result ump_arch_open(void);
/** Close UMP interface. */
void ump_arch_close(void);
/** Allocate UMP memory. */
ump_secure_id ump_arch_allocate(unsigned long * size, ump_alloc_constraints constraints);
/** Query size of specified UMP memory, in bytes. */
unsigned long ump_arch_size_get(ump_secure_id secure_id);
/** Release a reference from specified UMP memory. */
void ump_arch_reference_release(ump_secure_id secure_id);
/** Map specified UMP memory into CPU address space */
void* ump_arch_map(ump_secure_id secure_id, unsigned long size, ump_cache_enabled cache, unsigned long *cookie_out);
/** Unmap specified UMP memory from CPU adderss space */
void ump_arch_unmap(void* mapping, unsigned long size, unsigned long cookie);
/** Memory synchronization - cache flushing of mapped memory
* @return Is_cached: 1==True 0==NonCached */
int ump_arch_msync(ump_secure_id secure_id, void* mapping, unsigned long cookie, void * address, unsigned long size, ump_cpu_msync_op op);
/** Cache operation control. Tell when cache maintenance operations start and end.
This will allow the kernel to merge cache operations togheter, thus making them faster */
int ump_arch_cache_operations_control(ump_cache_op_control op);
/** Memory synchronization - cache flushing if previous user was different hardware */
int ump_arch_switch_hw_usage( ump_secure_id secure_id, ump_hw_usage new_user );
/** Locking buffer. Blocking call if the buffer is already locked. */
int ump_arch_lock( ump_secure_id secure_id, ump_lock_usage lock_usage );
/** Unlocking buffer. Let other users lock the buffer for their usage */
int ump_arch_unlock( ump_secure_id secure_id );
#ifdef __cplusplus
}
#endif
#endif /* _UNIFIED_MEMORY_PROVIDER_ARCH_H_ */
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file ump_frontend.c
*
* This file implements the user space API of the UMP API.
* It relies heavily on a arch backend to do the communication with the UMP device driver.
*/
#include <ump/ump.h>
#include "ump_internal.h"
#include "ump_arch.h"
#include <ump/ump_debug.h>
#include <ump/ump_osu.h>
UMP_API_EXPORT ump_result ump_open(void)
{
return ump_arch_open();
}
UMP_API_EXPORT void ump_close(void)
{
ump_arch_close();
}
UMP_API_EXPORT ump_secure_id ump_secure_id_get(ump_handle memh)
{
ump_mem * mem = (ump_mem*)memh;
UMP_DEBUG_ASSERT(UMP_INVALID_MEMORY_HANDLE != memh, ("Handle is invalid"));
UMP_DEBUG_ASSERT(UMP_INVALID_SECURE_ID != mem->secure_id, ("Secure ID is inavlid"));
UMP_DEBUG_ASSERT(0 < mem->ref_count, ("Reference count too low"));
UMP_DEBUG_ASSERT(0 < mem->size, ("Memory size of passed handle too low"));
return mem->secure_id;
}
UMP_API_EXPORT ump_handle ump_handle_create_from_secure_id(ump_secure_id secure_id)
{
unsigned long size;
UMP_DEBUG_ASSERT(UMP_INVALID_SECURE_ID != secure_id, ("Secure ID is invalid"));
size = ump_arch_size_get(secure_id);
if (0 != size)
{
unsigned long cookie;
/*
* The UMP memory which the secure_id referes to could now be deleted and re-created
* since we don't have any references to it yet. The mapping below will however fail if
* we have supplied incorrect size, so we are safe.
*/
void * mapping = ump_arch_map(secure_id, size, UMP_CACHE_DISABLE, &cookie);
if (NULL != mapping)
{
ump_mem * mem = _ump_osu_calloc(1, sizeof(*mem));
if (NULL != mem)
{
mem->secure_id = secure_id;
mem->mapped_mem = mapping;
mem->size = size;
mem->cookie = cookie;
mem->is_cached = 1; /* Is set to actually check in the ump_cpu_msync_now() function */
_ump_osu_lock_auto_init(&mem->ref_lock, 0, 0, 0);
UMP_DEBUG_ASSERT(NULL != mem->ref_lock, ("Failed to initialize lock\n"));
mem->ref_count = 1;
/* This is called only to set the cache settings in this handle */
ump_cpu_msync_now((ump_handle)mem, UMP_MSYNC_READOUT_CACHE_ENABLED, NULL, 0);
UMP_DEBUG_PRINT(4, ("UMP handle created for ID %u of size %lu, mapped into address 0x%08lx", mem->secure_id, mem->size, (unsigned long)mem->mapped_mem));
return (ump_handle)mem;
}
ump_arch_unmap(mapping, size, cookie);
}
}
UMP_DEBUG_PRINT(2, ("UMP handle creation failed for ID %u", secure_id));
return UMP_INVALID_MEMORY_HANDLE;
}
UMP_API_EXPORT unsigned long ump_size_get(ump_handle memh)
{
ump_mem * mem = (ump_mem*)memh;
UMP_DEBUG_ASSERT(UMP_INVALID_MEMORY_HANDLE != memh, ("Handle is invalid"));
UMP_DEBUG_ASSERT(UMP_INVALID_SECURE_ID != mem->secure_id, ("Secure ID is inavlid"));
UMP_DEBUG_ASSERT(0 < mem->ref_count, ("Reference count too low"));
UMP_DEBUG_ASSERT(0 < mem->size, ("Memory size of passed handle too low"));
return mem->size;
}
UMP_API_EXPORT void ump_read(void *dst, ump_handle srch, unsigned long offset, unsigned long length)
{
ump_mem * src = (ump_mem*)srch;
UMP_DEBUG_ASSERT(UMP_INVALID_MEMORY_HANDLE != srch, ("Handle is invalid"));
UMP_DEBUG_ASSERT(UMP_INVALID_SECURE_ID != src->secure_id, ("Secure ID is inavlid"));
UMP_DEBUG_ASSERT(0 < src->ref_count, ("Reference count too low"));
UMP_DEBUG_ASSERT(0 < src->size, ("Memory size of passed handle too low"));
UMP_DEBUG_ASSERT(NULL != src->mapped_mem, ("UMP Memory is not mapped"));
UMP_DEBUG_ASSERT((src->size) >= (offset + length), ("Requested read beyond end of UMP memory"));
_ump_osu_memcpy(dst,(char*)(src->mapped_mem) + offset, length);
}
UMP_API_EXPORT void ump_write(ump_handle dsth, unsigned long offset, const void *src, unsigned long length)
{
ump_mem * dst = (ump_mem*)dsth;
UMP_DEBUG_ASSERT(UMP_INVALID_MEMORY_HANDLE != dsth, ("Handle is invalid"));
UMP_DEBUG_ASSERT(UMP_INVALID_SECURE_ID != dst->secure_id, ("Secure ID is inavlid"));
UMP_DEBUG_ASSERT(0 < dst->ref_count, ("Reference count too low"));
UMP_DEBUG_ASSERT(0 < dst->size, ("Memory size of passed handle too low"));
UMP_DEBUG_ASSERT(NULL != dst->mapped_mem, ("UMP Memory is not mapped"));
UMP_DEBUG_ASSERT((dst->size) >= (offset + length), ("Requested write beyond end of UMP memory"));
_ump_osu_memcpy((char*)(dst->mapped_mem) + offset, src, length);
}
UMP_API_EXPORT void* ump_mapped_pointer_get(ump_handle memh)
{
ump_mem * mem = (ump_mem*)memh;
UMP_DEBUG_ASSERT(UMP_INVALID_MEMORY_HANDLE != memh, ("Handle is invalid"));
UMP_DEBUG_ASSERT(UMP_INVALID_SECURE_ID != mem->secure_id, ("Secure ID is inavlid"));
UMP_DEBUG_ASSERT(0 < mem->ref_count, ("Reference count too low"));
UMP_DEBUG_ASSERT(0 < mem->size, ("Memory size of passed handle too low"));
UMP_DEBUG_ASSERT(NULL != mem->mapped_mem, ("Error in mapping pointer (not mapped)"));
return mem->mapped_mem;
}
UMP_API_EXPORT void ump_mapped_pointer_release(ump_handle memh)
{
UMP_DEBUG_ASSERT(UMP_INVALID_MEMORY_HANDLE != memh, ("Handle is invalid"));
UMP_DEBUG_ASSERT(UMP_INVALID_SECURE_ID != ((ump_mem*)memh)->secure_id, ("Secure ID is inavlid"));
UMP_DEBUG_ASSERT(0 < ((ump_mem*)memh)->ref_count, ("Reference count too low"));
UMP_DEBUG_ASSERT(0 < ((ump_mem*)memh)->size, ("Memory size of passed handle too low"));
UMP_DEBUG_ASSERT(NULL != ((ump_mem*)memh)->mapped_mem, ("Error in mapping pointer (not mapped)"));
/* noop, cos we map in the pointer when handle is created, and unmap it when handle is destroyed */
}
UMP_API_EXPORT void ump_reference_add(ump_handle memh)
{
ump_mem * mem = (ump_mem*)memh;
UMP_DEBUG_ASSERT(UMP_INVALID_MEMORY_HANDLE != memh, ("Handle is invalid"));
UMP_DEBUG_ASSERT(UMP_INVALID_SECURE_ID != mem->secure_id, ("Secure ID is inavlid"));
UMP_DEBUG_ASSERT(0 < mem->ref_count, ("Reference count too low"));
UMP_DEBUG_ASSERT(0 < mem->size, ("Memory size of passed handle too low"));
_ump_osu_lock_wait(mem->ref_lock, _UMP_OSU_LOCKMODE_RW);
mem->ref_count += 1;
_ump_osu_lock_signal(mem->ref_lock, _UMP_OSU_LOCKMODE_RW);
}
UMP_API_EXPORT void ump_reference_release(ump_handle memh)
{
ump_mem * mem = (ump_mem*)memh;
UMP_DEBUG_ASSERT(UMP_INVALID_MEMORY_HANDLE != memh, ("Handle is invalid"));
UMP_DEBUG_ASSERT(UMP_INVALID_SECURE_ID != ((ump_mem*)mem)->secure_id, ("Secure ID is inavlid"));
UMP_DEBUG_ASSERT(0 < (((ump_mem*)mem)->ref_count), ("Reference count too low"));
UMP_DEBUG_ASSERT(0 < ((ump_mem*)mem)->size, ("Memory size of passed handle too low"));
UMP_DEBUG_ASSERT(NULL != ((ump_mem*)mem)->mapped_mem, ("Error in mapping pointer (not mapped)"));
_ump_osu_lock_wait(mem->ref_lock, _UMP_OSU_LOCKMODE_RW);
mem->ref_count -= 1;
if (0 == mem->ref_count)
{
/* Remove memory mapping, which holds our only reference towards the UMP kernel space driver */
ump_arch_unmap(mem->mapped_mem, mem->size, mem->cookie);
_ump_osu_lock_signal(mem->ref_lock, _UMP_OSU_LOCKMODE_RW);
/* Free the lock protecting the reference count */
_ump_osu_lock_term(mem->ref_lock);
/* Free the memory for this handle */
_ump_osu_free(mem);
} else {
_ump_osu_lock_signal(mem->ref_lock, _UMP_OSU_LOCKMODE_RW);
}
}
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file ump_internal.c
*
* Internal definitions and debugging macros for the UMP implementation.
*/
#ifndef _UNIFIED_MEMORY_PROVIDER_INTERNAL_H_
#define _UNIFIED_MEMORY_PROVIDER_INTERNAL_H_
#include <ump/ump.h>
#include <ump/ump_osu.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum ump_cache_enabled
{
UMP_CACHE_DISABLE = 0,
UMP_CACHE_ENABLE = 1
} ump_cache_enabled;
/**
* The actual (hidden) definition of ump_handles.
*/
typedef struct ump_mem
{
ump_secure_id secure_id; /**< UMP device driver cookie */
void * mapped_mem; /**< Mapped memory; all read and write use this */
unsigned long size; /**< Size of allocated memory */
_ump_osu_lock_t* ref_lock; /**< Lock protection ref_count */
int ref_count; /**< The reference count of the ump_handle in userspace. It is used for finding out
when to free the memory used by this userspace handle. It is NOT the same as the
real ump_mem reference count in the devicedriver which do reference counting
for the memory that this handle reveals. */
unsigned long cookie; /**< cookie for use in arch_unmap calls */
ump_cache_enabled is_cached;
} ump_mem;
#ifdef __cplusplus
}
#endif
#endif /* _UNIFIED_MEMORY_PROVIDER_INTERNAL_H_ */
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file ump_ref_drv.c
*
* Implementation of the user space API extensions provided by the reference implementation.
*/
#include <ump/ump_ref_drv.h>
#include <ump/ump.h>
#include "ump_internal.h"
#include "ump_arch.h"
#include <ump/ump_debug.h>
#include <ump/ump_osu.h>
/* Allocate a buffer which can be used directly by hardware, 4kb aligned */
static ump_handle ump_ref_drv_allocate_internal(unsigned long size, ump_alloc_constraints constraints, ump_cache_enabled cache);
/* Allocate a buffer which can be used directly by hardware, 4kb aligned */
ump_handle ump_ref_drv_allocate(unsigned long size, ump_alloc_constraints constraints)
{
ump_cache_enabled cache= UMP_CACHE_DISABLE;
if ( 0!=(constraints&UMP_REF_DRV_CONSTRAINT_USE_CACHE) )
{
cache = UMP_CACHE_ENABLE;
}
return ump_ref_drv_allocate_internal(size, constraints, cache);
}
UMP_API_EXPORT int ump_cpu_msync_now(ump_handle memh, ump_cpu_msync_op op, void* address, int size)
{
int offset;
ump_mem * mem = (ump_mem*)memh;
UMP_DEBUG_ASSERT(UMP_INVALID_MEMORY_HANDLE != memh, ("Handle is invalid"));
/* If the op is readout, we do the readout from DD.
Else we skip flushing if the userspace handle says that it is uncached */
if ((UMP_MSYNC_READOUT_CACHE_ENABLED!=op) && (0 == mem->is_cached) ) return 0;
if ( NULL == address )
{
address = ((ump_mem*)mem)->mapped_mem;
}
offset = (int) ((unsigned long)address - (unsigned long)((ump_mem*)mem)->mapped_mem);
if ( 0 == size )
{
size = (int)((ump_mem*)mem)->size;
}
UMP_DEBUG_ASSERT(0 < (((ump_mem*)mem)->ref_count), ("Reference count too low"));
UMP_DEBUG_ASSERT((size>=0) && (size <= (int)((ump_mem*)mem)->size), ("Memory size of passed handle too low"));
UMP_DEBUG_ASSERT(NULL != ((ump_mem*)mem)->mapped_mem, ("Error in mapping pointer (not mapped)"));
if ( (offset+size) > (int)mem->size)
{
size = mem->size - offset;
}
mem->is_cached = ump_arch_msync(mem->secure_id, mem->mapped_mem, mem->cookie, address, size, op);
return mem->is_cached ;
}
UMP_API_EXPORT int ump_cache_operations_control(ump_cache_op_control op)
{
return ump_arch_cache_operations_control(op);
}
UMP_API_EXPORT int ump_switch_hw_usage( ump_handle memh, ump_hw_usage new_user )
{
ump_mem * mem = (ump_mem*)memh;
UMP_DEBUG_ASSERT(UMP_INVALID_MEMORY_HANDLE != memh, ("Handle is invalid"));
return ump_arch_switch_hw_usage(mem->secure_id, new_user);
}
UMP_API_EXPORT int ump_lock( ump_handle memh, ump_lock_usage lock_usage)
{
ump_mem * mem = (ump_mem*)memh;
UMP_DEBUG_ASSERT(UMP_INVALID_MEMORY_HANDLE != memh, ("Handle is invalid"));
return ump_arch_lock(mem->secure_id, lock_usage);
}
UMP_API_EXPORT int ump_unlock( ump_handle memh )
{
ump_mem * mem = (ump_mem*)memh;
UMP_DEBUG_ASSERT(UMP_INVALID_MEMORY_HANDLE != memh, ("Handle is invalid"));
return ump_arch_unlock(mem->secure_id);
}
UMP_API_EXPORT int ump_switch_hw_usage_secure_id( ump_secure_id ump_id, ump_hw_usage new_user )
{
return ump_arch_switch_hw_usage(ump_id, new_user);
}
/** Locking buffer. Blocking call if the buffer is already locked. */
UMP_API_EXPORT int ump_lock_secure_id( ump_secure_id ump_id, ump_lock_usage lock_usage )
{
return ump_arch_lock(ump_id, lock_usage);
}
/** Unlocking buffer. Let other users lock the buffer for their usage */
UMP_API_EXPORT int ump_unlock_secure_id( ump_secure_id ump_id )
{
return ump_arch_unlock(ump_id);
}
/* Allocate a buffer which can be used directly by hardware, 4kb aligned */
static ump_handle ump_ref_drv_allocate_internal(unsigned long size, ump_alloc_constraints constraints, ump_cache_enabled cache)
{
ump_secure_id secure_id;
unsigned long allocated_size = size;
UMP_DEBUG_PRINT(4, ("Allocating UMP memory of size %lu", size));
secure_id = ump_arch_allocate(&allocated_size, constraints);
if (secure_id != UMP_INVALID_SECURE_ID)
{
unsigned long cookie;
void * mapping;
mapping = ump_arch_map(secure_id, allocated_size, cache, &cookie);
if (NULL != mapping)
{
/*
* PS: By now we have actually increased the ref count in the device driver by 2,
* one for the allocation iteself, and one for the mapping.
*/
ump_mem * mem;
mem = _ump_osu_calloc(1, sizeof(*mem));
if (NULL != mem)
{
mem->secure_id = secure_id;
mem->mapped_mem = mapping;
mem->size = allocated_size;
mem->cookie = cookie;
mem->is_cached = 1; /* Default to ON, is disabled later if not */
_ump_osu_lock_auto_init(&mem->ref_lock, 0, 0, 0);
UMP_DEBUG_ASSERT(NULL != mem->ref_lock, ("Failed to initialize lock\n"));
mem->ref_count = 1;
/*
* ump_arch_allocate() gave us a kernel space reference, and the same did ump_arch_map()
* We release the one from ump_arch_allocate(), and rely solely on the one from the ump_arch_map()
* That is, ump_arch_unmap() should now do the final release towards the UMP kernel space driver.
*/
ump_arch_reference_release(secure_id);
/* This is called only to set the cache settings in this handle */
ump_cpu_msync_now((ump_handle)mem, UMP_MSYNC_READOUT_CACHE_ENABLED, NULL, 0);
UMP_DEBUG_PRINT(4, ("UMP handle created for ID %u of size %lu, mapped into address 0x%08lx", mem->secure_id, mem->size, (unsigned long)mem->mapped_mem));
return (ump_handle)mem;
}
ump_arch_unmap(mapping, allocated_size, cookie); /* Unmap the memory */
ump_arch_reference_release(secure_id); /* Release reference added when we allocated the UMP memory */
}
ump_arch_reference_release(secure_id); /* Release reference added when we allocated the UMP memory */
}
UMP_DEBUG_PRINT(4, ("Allocation of UMP memory failed"));
return UMP_INVALID_MEMORY_HANDLE;
}
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file ump.h
*
* This file contains the user space part of the UMP API.
*/
#ifndef _UNIFIED_MEMORY_PROVIDER_H_
#define _UNIFIED_MEMORY_PROVIDER_H_
/** @defgroup ump_user_space_api UMP User Space API
* @{ */
#include "ump_platform.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* External representation of a UMP handle in user space.
*/
typedef void * ump_handle;
/**
* Typedef for a secure ID, a system wide identificator for UMP memory buffers.
*/
typedef unsigned int ump_secure_id;
/**
* Value to indicate an invalid UMP memory handle.
*/
#define UMP_INVALID_MEMORY_HANDLE ((ump_handle)0)
/**
* Value to indicate an invalid secure Id.
*/
#define UMP_INVALID_SECURE_ID ((ump_secure_id)-1)
/**
* UMP error codes for user space.
*/
typedef enum
{
UMP_OK = 0, /**< indicates success */
UMP_ERROR, /**< indicates failure */
} ump_result;
/**
* Opens and initializes the UMP library.
*
* This function must be called at least once before calling any other UMP API functions.
* Each open is reference counted and must be matched with a call to @ref ump_close "ump_close".
*
* @see ump_close
*
* @return UMP_OK indicates success, UMP_ERROR indicates failure.
*/
UMP_API_EXPORT ump_result ump_open(void);
/**
* Terminate the UMP library.
*
* This must be called once for every successful @ref ump_open "ump_open". The UMP library is
* terminated when, and only when, the last open reference to the UMP interface is closed.
*
* @see ump_open
*/
UMP_API_EXPORT void ump_close(void);
/**
* Retrieves the secure ID for the specified UMP memory.
*
* This identificator is unique across the entire system, and uniquely identifies
* the specified UMP memory. This identificator can later be used through the
* @ref ump_handle_create_from_secure_id "ump_handle_create_from_secure_id" or
* @ref ump_dd_handle_create_from_secure_id "ump_dd_handle_create_from_secure_id"
* functions in order to access this UMP memory, for instance from another process.
*
* @note There is a kernel space equivalent function called @ref ump_dd_secure_id_get "ump_dd_secure_id_get"
*
* @see ump_handle_create_from_secure_id
* @see ump_dd_handle_create_from_secure_id
* @see ump_dd_secure_id_get
*
* @param mem Handle to UMP memory.
*
* @return Returns the secure ID for the specified UMP memory.
*/
UMP_API_EXPORT ump_secure_id ump_secure_id_get(ump_handle mem);
/**
* Retrieves a handle to allocated UMP memory.
*
* The usage of UMP memory is reference counted, so this will increment the reference
* count by one for the specified UMP memory.
* Use @ref ump_reference_release "ump_reference_release" when there is no longer any
* use for the retrieved handle.
*
* @note There is a kernel space equivalent function called @ref ump_dd_handle_create_from_secure_id "ump_dd_handle_create_from_secure_id"
*
* @see ump_reference_release
* @see ump_dd_handle_create_from_secure_id
*
* @param secure_id The secure ID of the UMP memory to open, that can be retrieved using the @ref ump_secure_id_get "ump_secure_id_get " function.
*
* @return UMP_INVALID_MEMORY_HANDLE indicates failure, otherwise a valid handle is returned.
*/
UMP_API_EXPORT ump_handle ump_handle_create_from_secure_id(ump_secure_id secure_id);
/**
* Retrieves the actual size of the specified UMP memory.
*
* The size is reported in bytes, and is typically page aligned.
*
* @note There is a kernel space equivalent function called @ref ump_dd_size_get "ump_dd_size_get"
*
* @see ump_dd_size_get
*
* @param mem Handle to UMP memory.
*
* @return Returns the allocated size of the specified UMP memory, in bytes.
*/
UMP_API_EXPORT unsigned long ump_size_get(ump_handle mem);
/**
* Read from specified UMP memory.
*
* Another way of reading from (and writing to) UMP memory is to use the
* @ref ump_mapped_pointer_get "ump_mapped_pointer_get" to retrieve
* a CPU mapped pointer to the memory.
*
* @see ump_mapped_pointer_get
*
* @param dst Destination buffer.
* @param src Handle to UMP memory to read from.
* @param offset Where to start reading, given in bytes.
* @param length How much to read, given in bytes.
*/
UMP_API_EXPORT void ump_read(void * dst, ump_handle src, unsigned long offset, unsigned long length);
/**
* Write to specified UMP memory.
*
* Another way of writing to (and reading from) UMP memory is to use the
* @ref ump_mapped_pointer_get "ump_mapped_pointer_get" to retrieve
* a CPU mapped pointer to the memory.
*
* @see ump_mapped_pointer_get
*
* @param dst Handle to UMP memory to write to.
* @param offset Where to start writing, given in bytes.
* @param src Buffer to read from.
* @param length How much to write, given in bytes.
*/
UMP_API_EXPORT void ump_write(ump_handle dst, unsigned long offset, const void * src, unsigned long length);
/**
* Retrieves a memory mapped pointer to the specified UMP memory.
*
* This function retrieves a memory mapped pointer to the specified UMP memory,
* that can be used by the CPU. Every successful call to
* @ref ump_mapped_pointer_get "ump_mapped_pointer_get" is reference counted,
* and must therefore be followed by a call to
* @ref ump_mapped_pointer_release "ump_mapped_pointer_release " when the
* memory mapping is no longer needed.
*
* @note Systems without a MMU for the CPU only return the physical address, because no mapping is required.
*
* @see ump_mapped_pointer_release
*
* @param mem Handle to UMP memory.
*
* @return NULL indicates failure, otherwise a CPU mapped pointer is returned.
*/
UMP_API_EXPORT void * ump_mapped_pointer_get(ump_handle mem);
/**
* Releases a previously mapped pointer to the specified UMP memory.
*
* The CPU mapping of the specified UMP memory memory is reference counted,
* so every call to @ref ump_mapped_pointer_get "ump_mapped_pointer_get" must
* be matched with a call to this function when the mapping is no longer needed.
*
* The CPU mapping is not removed before all references to the mapping is released.
*
* @note Systems without a MMU must still implement this function, even though no unmapping should be needed.
*
* @param mem Handle to UMP memory.
*/
UMP_API_EXPORT void ump_mapped_pointer_release(ump_handle mem);
/**
* Adds an extra reference to the specified UMP memory.
*
* This function adds an extra reference to the specified UMP memory. This function should
* be used every time a UMP memory handle is duplicated, that is, assigned to another ump_handle
* variable. The function @ref ump_reference_release "ump_reference_release" must then be used
* to release each copy of the UMP memory handle.
*
* @note You are not required to call @ref ump_reference_add "ump_reference_add"
* for UMP handles returned from
* @ref ump_handle_create_from_secure_id "ump_handle_create_from_secure_id",
* because these handles are already reference counted by this function.
*
* @note There is a kernel space equivalent function called @ref ump_dd_reference_add "ump_dd_reference_add"
*
* @see ump_dd_reference_add
*
* @param mem Handle to UMP memory.
*/
UMP_API_EXPORT void ump_reference_add(ump_handle mem);
/**
* Releases a reference from the specified UMP memory.
*
* This function should be called once for every reference to the UMP memory handle.
* When the last reference is released, all resources associated with this UMP memory
* handle are freed.
*
* @note There is a kernel space equivalent function called @ref ump_dd_reference_release "ump_dd_reference_release"
*
* @see ump_dd_reference_release
*
* @param mem Handle to UMP memory.
*/
UMP_API_EXPORT void ump_reference_release(ump_handle mem);
#ifdef __cplusplus
}
#endif
/** @} */ /* end group ump_user_space_api */
#endif /*_UNIFIED_MEMORY_PROVIDER_H_ */
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file ump_debug.h
*
* The file include several useful macros for debugging and printing.
* - UMP_PRINTF(...) Do not use this function: Will be included in Release builds.
* - UMP_DEBUG_TRACE() Prints current location in code.
* - UMP_DEBUG_PRINT(nr, (X) ) Prints the second argument if nr<=UMP_DEBUG_LEVEL.
* - UMP_DEBUG_TPRINT(nr, X ) Prints the source trace and second argument if nr<=UMP_DEBUG_LEVEL.
* - UMP_DEBUG_ERROR( (X) ) Prints an errortext, a source trace, and the given error message.
* - UMP_DEBUG_ASSERT(exp,(X)) If the asserted expr is false, the program will exit.
* - UMP_DEBUG_ASSERT_RANGE(x, min, max) Triggers if variable x is not between or equal to max and min.
* - UMP_DEBUG_ASSERT_LEQ(x, max) Triggers if variable x is not less than equal to max.
* - UMP_DEBUG_ASSERT_POINTER(pointer) Triggers if the pointer is a zero pointer.
* - UMP_DEBUG_CODE( X ) The code inside the macro is only copiled in Debug builds.
*
* The (X) means that you must add an extra parantese around the argumentlist.
*
* The printf function: UMP_PRINTF(...) is routed to _ump_sys_printf
*
* Suggested range for the DEBUG-LEVEL is [1:6] where
* [1:2] Is messages with highest priority, indicate possible errors.
* [3:4] Is messages with medium priority, output important variables.
* [5:6] Is messages with low priority, used during extensive debugging.
*
*/
#ifndef _UMP_DEBUG_H_
#define _UMP_DEBUG_H_
#include <stdio.h>
#include <stdlib.h>
/* START: Configuration */
#ifndef UMP_PRINTF
#define UMP_PRINTF printf
#endif /* UMP_PRINTF */
#ifndef UMP_PRINT_FLUSH
#define UMP_PRINT_FLUSH do {} while (0)
#endif /* UMP_PRINT_FLUSH */
#ifndef UMP_DEBUG_LEVEL
#define UMP_DEBUG_LEVEL 1
#endif /* UMP_DEBUG_LEVEL */
#ifndef UMP_DEBUG_ERROR_START_MSG
#define UMP_DEBUG_ERROR_START_MSG do {\
UMP_PRINTF("*********************************************************************\n");\
UMP_PRINT_FLUSH; } while (0)
#endif /* UMP_DEBUG_ERROR_START_MSG */
#ifndef UMP_DEBUG_ERROR_STOP_MSG
#define UMP_DEBUG_ERROR_STOP_MSG do { UMP_PRINTF("\n"); UMP_PRINT_FLUSH; } while (0)
#endif /* UMP_DEBUG_ERROR_STOP_MSG */
#ifndef UMP_ASSERT_QUIT_CMD
#define UMP_ASSERT_QUIT_CMD abort()
#endif /* UMP_ASSERT_QUIT_CMD */
/* STOP: Configuration */
/**
* The macro UMP_FUNCTION evaluates to the name of the function enclosing
* this macro's usage, or "<unknown>" if not supported.
*/
#if (defined(__SYMBIAN32__) && defined(__ARMCC__)) || defined(_MSC_VER)
# define UMP_FUNCTION __FUNCTION__
#elif __STDC__ && __STDC_VERSION__ >= 199901L
# define UMP_FUNCTION __FUNCTION__
#elif defined(__GNUC__) && __GNUC__ >= 2
# define UMP_FUNCTION __FUNCTION__
#elif defined(__func__)
# define UMP_FUNCTION __func__
#else
# define UMP_FUNCTION "<unknown>"
#endif
/**
* Explicitly ignore a parameter passed into a function, to suppress compiler warnings.
* Should only be used with parameter names.
*/
#define UMP_IGNORE(x) (void)x
/**
* @def UMP_DEBUG_TRACE()
* @brief Prints current location in code.
* Can be turned off by defining UMP_DEBUG_SKIP_TRACE
*/
#ifndef UMP_DEBUG_SKIP_TRACE
#ifndef UMP_DEBUG_SKIP_PRINT_FUNCTION_NAME
#define UMP_DEBUG_TRACE() do { UMP_PRINTF( "In file: "__FILE__ \
" function: %s() line:%4d\n" , UMP_FUNCTION, __LINE__); UMP_PRINT_FLUSH; } while (0)
#else
#define UMP_DEBUG_TRACE() do { UMP_PRINTF( "In file: "__FILE__ " line:%4d\n" , __LINE__); UMP_PRINT_FLUSH; } while (0)
#endif /* UMP_DEBUG_SKIP_PRINT_FUNCTION_NAME */
#else
#define UMP_DEBUG_TRACE()
#endif /* UMP_DEBUG_SKIP_TRACE */
/**
* @def UMP_DEBUG_PRINT(nr, (X) )
* @brief Prints the second argument if nr<=UMP_DEBUG_LEVEL.
* Can be turned off by defining UMP_DEBUG_SKIP_PRINT
* @param nr If nr <= UMP_DEBUG_LEVEL, we print the text.
* @param X A parantese with the contents to be sent to UMP_PRINTF
*/
#ifndef UMP_DEBUG_SKIP_PRINT
#define UMP_DEBUG_PRINT(nr, X ) do { if ( nr<=UMP_DEBUG_LEVEL ) { UMP_PRINTF X ; UMP_PRINT_FLUSH; } } while (0)
#else
#define UMP_DEBUG_PRINT(nr, X )
#endif /* UMP_DEBUG_SKIP_PRINT */
/**
* @def UMP_DEBUG_TPRINT(nr, (X) )
* @brief Prints the second argument if nr<=UMP_DEBUG_LEVEL.
* Can be turned off by defining UMP_DEBUG_SKIP_TPRINT.
* Can be shortened by defining UMP_DEBUG_TPRINT_SKIP_FUNCTION.
* @param nr If nr <= UMP_DEBUG_LEVEL, we print the text.
* @param X A parantese with the contents to be sent to UMP_PRINTF
*/
/* helper to handle if the function name should be included or not */
#ifndef UMP_DEBUG_TPRINT_SKIP_FUNCTION
#define UMP_DEBUG_TPRINT_INTERN do {UMP_PRINTF( ""__FILE__" %s()%4d " , UMP_FUNCTION, __LINE__); UMP_PRINT_FLUSH; } while (0)
#else
#define UMP_DEBUG_TPRINT_INTERN do {UMP_PRINTF( ""__FILE__ "%4d " , __LINE__); UMP_PRINT_FLUSH; } while (0)
#endif /* UMP_DEBUG_TPRINT_SKIP_FUNCTION */
#ifndef UMP_DEBUG_SKIP_TPRINT
#define UMP_DEBUG_TPRINT(nr, X ) \
do{\
if ( nr<=UMP_DEBUG_LEVEL )\
{\
UMP_DEBUG_TPRINT_INTERN;\
UMP_PRINTF X ;\
UMP_PRINT_FLUSH;\
}\
} while (0)
#else
#define UMP_DEBUG_TPRINT(nr, X )
#endif /* UMP_DEBUG_SKIP_TPRINT */
/**
* @def UMP_DEBUG_ERROR( (X) )
* @brief Prints an errortext, a source Trace, and the given error message.
* Prints filename, function, linenr, and the given error message.
* The error message must be inside a second parantese.
* The error message is written on a separate line, and a NL char is added.
* Can be turned of by defining UMP_DEBUG_SKIP_ERROR;
* You do not need to type the words ERROR in the message, since it will
* be added anyway.
*
* @note You should not end the text with a newline, since it is added by the macro.
* @note You should not write "ERROR" in the text, since it is added by the macro.
* @param X A parantese with the contents to be sent to UMP_PRINTF
*/
#ifndef UMP_DEBUG_SKIP_ERROR
#define UMP_DEBUG_ERROR( X ) \
do{ \
UMP_DEBUG_ERROR_START_MSG;\
UMP_PRINTF("ERROR: ");\
UMP_PRINT_FLUSH;\
UMP_DEBUG_TRACE(); \
UMP_PRINTF X ; \
UMP_PRINT_FLUSH;\
UMP_DEBUG_ERROR_STOP_MSG;\
} while (0)
#else
#define UMP_DEBUG_ERROR( X ) do{ ; } while ( 0 )
#endif /* UMP_DEBUG_SKIP_ERROR */
/**
* @def UMP_DEBUG_ASSERT(expr, (X) )
* @brief If the asserted expr is false, the program will exit.
* Prints filename, function, linenr, and the given error message.
* The error message must be inside a second parantese.
* The error message is written on a separate line, and a NL char is added.
* Can be turned of by defining UMP_DEBUG_SKIP_ERROR;
* You do not need to type the words ASSERT in the message, since it will
* be added anyway.
*
* @param X A parantese with the contents to be sent to UMP_PRINTF
* Prints filename, function, linenr, and the error message
* on a separte line. A newline char is added at the end.
* Can be turned of by defining UMP_DEBUG_SKIP_ASSERT
* @param expr Will exit program if \a expr is false;
* @param (X) Text that will be written if the assertion toggles.
*/
#ifndef UMP_DEBUG_SKIP_ASSERT
#define UMP_DEBUG_ASSERT(expr, X ) \
do{\
if ( !(expr) ) \
{ \
UMP_DEBUG_ERROR_START_MSG;\
UMP_PRINTF("ASSERT EXIT: ");\
UMP_PRINT_FLUSH;\
UMP_DEBUG_TRACE(); \
UMP_PRINTF X ; \
UMP_PRINT_FLUSH;\
UMP_DEBUG_ERROR_STOP_MSG;\
UMP_ASSERT_QUIT_CMD;\
}\
} while (0)
#else
#define UMP_DEBUG_ASSERT(expr, X)
#endif /* UMP_DEBUG_SKIP_ASSERT */
/**
* @def UMP_DEBUG_ASSERT_POINTER(pointer)
* @brief If the asserted pointer is NULL, the program terminates and TRACE info is printed
* The checking is disabled if "UMP_DEBUG_SKIP_ASSERT" is defined.
*/
#define UMP_DEBUG_ASSERT_POINTER(pointer) UMP_DEBUG_ASSERT(pointer, ("Null pointer " #pointer) )
/**
* @def UMP_DEBUG_ASSERT_HANDLE(handle)
* @brief If the asserted handle is not a valid handle, the program terminates and TRACE info is printed
* The checking is disabled if "UMP_DEBUG_SKIP_ASSERT" is defined.
*/
#define UMP_DEBUG_ASSERT_HANDLE(handle) UMP_DEBUG_ASSERT(UMP_NO_HANDLE != (handle), ("Invalid handle" #handle) )
/**
* @def UMP_DEBUG_ASSERT_ALIGNMENT(ptr, align)
* @brief If the asserted pointer is not aligned to align, the program terminates with trace info printed.
* The checking is disabled if "UMP_DEBUG_SKIP_ASSERT" is defined.
*/
#ifndef UMP_DEBUG_SKIP_ASSERT
#define UMP_DEBUG_ASSERT_ALIGNMENT(ptr, align) do { \
UMP_DEBUG_ASSERT(0 == (align & (align - 1)), ("align %d is not a power-of-two", align)); \
UMP_DEBUG_ASSERT(0 == (((u32)(ptr)) & (align - 1)), ("ptr %p not aligned to %d bytes", (void*)ptr, align)); \
} while (0)
#else
#define UMP_DEBUG_ASSERT_ALIGNMENT(ptr, align)
#endif /* UMP_DEBUG_SKIP_ASSERT */
/**
* @def UMP_DEBUG_ASSERT_RANGE(x,min,max)
* @brief If variable x is not between or equal to max and min, the assertion triggers.
* The checking is disabled if "UMP_DEBUG_SKIP_ASSERT" is defined.
*/
#define UMP_DEBUG_ASSERT_RANGE(x, min, max) \
UMP_DEBUG_ASSERT( (x) >= (min) && (x) <= (max), \
(#x " out of range (%2.2f)", (double)x ) \
)
/**
* @def UMP_DEBUG_ASSERT_LEQ(x,max)
* @brief If variable x is less than or equal to max, the assertion triggers.
* The checking is disabled if "UMP_DEBUG_SKIP_ASSERT" is defined.
*/
#define UMP_DEBUG_ASSERT_LEQ(x, max) \
UMP_DEBUG_ASSERT( (x) <= (max), \
(#x " out of range (%2.2f)", (double)x ) \
)
/**
* @def UMP_DEBUG_CODE( X )
* @brief Run the code X on debug builds.
* The code will not be used if UMP_DEBUG_SKIP_CODE is defined .
*
*/
#ifdef UMP_DEBUG_SKIP_CODE
#define UMP_DEBUG_CODE( X )
#else
#define UMP_DEBUG_CODE( X ) X
#endif /* UMP_DEBUG_SKIP_CODE */
#endif /* _UMP_DEBUG_H_ */
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file ump_osu.h
* Defines the OS abstraction layer for the base driver
*/
#ifndef __UMP_OSU_H__
#define __UMP_OSU_H__
#include <stdarg.h>
#ifdef __cplusplus
extern "C"
{
#endif
typedef unsigned int u32;
#ifdef _MSC_VER
typedef unsigned __int64 u64;
typedef signed __int64 s64;
#else
typedef unsigned long long u64;
typedef signed long long s64;
#endif
#ifndef NULL
#define NULL ((void*)0)
#endif
typedef unsigned long ump_bool;
#ifndef UMP_TRUE
#define UMP_TRUE ((ump_bool)1)
#endif
#ifndef UMP_FALSE
#define UMP_FALSE ((ump_bool)0)
#endif
#define UMP_STATIC static
/**
* @addtogroup ump_user_space_api Unified Device Driver (UDD) APIs used by UMP
*
* @{
*/
/**
* @defgroup ump_osuapi UDD OS Abstraction for User-side (OSU) APIs for UMP
*
* @{
*/
/* The following is necessary to prevent the _ump_osk_errcode_t doxygen from
* becoming unreadable: */
/** @cond OSU_COPY_OF__UMP_OSU_ERRCODE_T */
/**
* @brief OSU/OSK Error codes.
*
* Each OS may use its own set of error codes, and may require that the
* User/Kernel interface take certain error code. This means that the common
* error codes need to be sufficiently rich to pass the correct error code
* through from the OSK/OSU to U/K layer, across all OSs.
*
* The result is that some error codes will appear redundant on some OSs.
* Under all OSs, the OSK/OSU layer must translate native OS error codes to
* _ump_osk/u_errcode_t codes. Similarly, the U/K layer must translate from
* _ump_osk/u_errcode_t codes to native OS error codes.
*
*/
typedef enum
{
_UMP_OSK_ERR_OK = 0, /**< Success. */
_UMP_OSK_ERR_FAULT = -1, /**< General non-success */
_UMP_OSK_ERR_INVALID_FUNC = -2, /**< Invalid function requested through User/Kernel interface (e.g. bad IOCTL number) */
_UMP_OSK_ERR_INVALID_ARGS = -3, /**< Invalid arguments passed through User/Kernel interface */
_UMP_OSK_ERR_NOMEM = -4, /**< Insufficient memory */
_UMP_OSK_ERR_TIMEOUT = -5, /**< Timeout occured */
_UMP_OSK_ERR_RESTARTSYSCALL = -6, /**< Special: On certain OSs, must report when an interruptable mutex is interrupted. Ignore otherwise. */
_UMP_OSK_ERR_ITEM_NOT_FOUND = -7, /**< Table Lookup failed */
_UMP_OSK_ERR_BUSY = -8, /**< Device/operation is busy. Try again later */
_UMP_OSK_ERR_UNSUPPORTED = -9, /**< Optional part of the interface used, and is unsupported */
} _ump_osk_errcode_t;
/** @endcond */ /* end cond OSU_COPY_OF__UMP_OSU_ERRCODE_T */
/**
* @brief OSU Error codes.
*
* OSU error codes - enum values intentionally same as OSK
*/
typedef enum
{
_UMP_OSU_ERR_OK = 0, /**< Success. */
_UMP_OSU_ERR_FAULT = -1, /**< General non-success */
_UMP_OSU_ERR_TIMEOUT = -2, /**< Timeout occured */
} _ump_osu_errcode_t;
/** @brief Translate OSU error code to base driver error code.
*
* The _UMP_OSU_TRANSLATE_ERROR macro translates an OSU error code to the
* error codes in use by the base driver.
*/
#define _UMP_OSU_TRANSLATE_ERROR(_ump_osu_errcode) ( ( _UMP_OSU_ERR_OK == (_ump_osu_errcode) ) ? UMP_ERR_NO_ERROR : UMP_ERR_FUNCTION_FAILED)
/** @defgroup _ump_osu_lock OSU Mutual Exclusion Locks
* @{ */
/** @brief OSU Mutual Exclusion Lock flags type.
*
* This is made to look like and function identically to the OSK locks (refer
* to \ref _ump_osk_lock). However, please note the following \b important
* differences:
* - the OSU default lock is a Sleeping, non-interruptible mutex.
* - the OSU adds the ANYUNLOCK type of lock which allows a thread which doesn't
* own the lock to release the lock.
* - the order parameter when creating a lock is currently unused
*
* @note Pay careful attention to the difference in default locks for OSU and
* OSK locks; OSU locks are always non-interruptible, but OSK locks are by
* default, interruptible. This has implications for systems that do not
* distinguish between user and kernel mode.
*/
typedef enum
{
_UMP_OSU_LOCKFLAG_DEFAULT = 0, /**< Default lock type. */
/** @enum _ump_osu_lock_flags_t
*
* Flags from 0x0--0x8000 are RESERVED for Kernel-mode
*/
_UMP_OSU_LOCKFLAG_ANYUNLOCK = 0x10000, /**< Mutex that guarantees that any thread can unlock it when locked. Otherwise, this will not be possible. */
/** @enum _ump_osu_lock_flags_t
*
* Flags from 0x10000 are RESERVED for User-mode
*/
_UMP_OSU_LOCKFLAG_STATIC = 0x20000, /* Flag in OSU reserved range to identify lock as a statically initialized lock */
} _ump_osu_lock_flags_t;
typedef enum
{
_UMP_OSU_LOCKMODE_UNDEF = -1, /**< Undefined lock mode. For internal use only */
_UMP_OSU_LOCKMODE_RW = 0x0, /**< Default. Lock is used to protect data that is read from and written to */
/** @enum _ump_osu_lock_mode_t
*
* Lock modes 0x1--0x3F are RESERVED for Kernel-mode */
} _ump_osu_lock_mode_t;
/** @brief Private type for Mutual Exclusion lock objects. */
typedef struct _ump_osu_lock_t_struct _ump_osu_lock_t;
/** @brief The number of static locks supported in _ump_osu_lock_static(). */
#define UMP_OSU_STATIC_LOCK_COUNT (sizeof(_ump_osu_static_locks) / sizeof(_ump_osu_lock_t))
/** @} */ /* end group _ump_osu_lock */
/** @defgroup _ump_osu_memory OSU Memory Allocation
* @{ */
/** @brief Allocate zero-initialized memory.
*
* Returns a buffer capable of containing at least \a n elements of \a size
* bytes each. The buffer is initialized to zero.
*
* The buffer is suitably aligned for storage and subsequent access of every
* type that the compiler supports. Therefore, the pointer to the start of the
* buffer may be cast into any pointer type, and be subsequently accessed from
* such a pointer, without loss of information.
*
* When the buffer is no longer in use, it must be freed with _ump_osu_free().
* Failure to do so will cause a memory leak.
*
* @note Most toolchains supply memory allocation functions that meet the
* compiler's alignment requirements.
*
* @param n Number of elements to allocate
* @param size Size of each element
* @return On success, the zero-initialized buffer allocated. NULL on failure
*/
void *_ump_osu_calloc( u32 n, u32 size );
/** @brief Allocate memory.
*
* Returns a buffer capable of containing at least \a size bytes. The
* contents of the buffer are undefined.
*
* The buffer is suitably aligned for storage and subsequent access of every
* type that the compiler supports. Therefore, the pointer to the start of the
* buffer may be cast into any pointer type, and be subsequently accessed from
* such a pointer, without loss of information.
*
* When the buffer is no longer in use, it must be freed with _ump_osu_free().
* Failure to do so will cause a memory leak.
*
* @note Most toolchains supply memory allocation functions that meet the
* compiler's alignment requirements.
*
* Remember to free memory using _ump_osu_free().
* @param size Number of bytes to allocate
* @return On success, the buffer allocated. NULL on failure.
*/
void *_ump_osu_malloc( u32 size );
/** @brief Free memory.
*
* Reclaims the buffer pointed to by the parameter \a ptr for the system.
* All memory returned from _ump_osu_malloc(), _ump_osu_calloc() and
* _ump_osu_realloc() must be freed before the application exits. Otherwise,
* a memory leak will occur.
*
* Memory must be freed once. It is an error to free the same non-NULL pointer
* more than once.
*
* It is legal to free the NULL pointer.
*
* @param ptr Pointer to buffer to free
*/
void _ump_osu_free( void *ptr );
/** @brief Copies memory.
*
* Copies the \a len bytes from the buffer pointed by the parameter \a src
* directly to the buffer pointed by \a dst.
*
* It is an error for \a src to overlap \a dst anywhere in \a len bytes.
*
* @param dst Pointer to the destination array where the content is to be
* copied.
* @param src Pointer to the source of data to be copied.
* @param len Number of bytes to copy.
* @return \a dst is always passed through unmodified.
*/
void *_ump_osu_memcpy( void *dst, const void *src, u32 len );
/** @brief Fills memory.
*
* Sets the first \a size bytes of the block of memory pointed to by \a ptr to
* the specified value
* @param ptr Pointer to the block of memory to fill.
* @param chr Value to be set, passed as u32. Only the 8 Least Significant Bits (LSB)
* are used.
* @param size Number of bytes to be set to the value.
* @return \a ptr is always passed through unmodified
*/
void *_ump_osu_memset( void *ptr, u32 chr, u32 size );
/** @} */ /* end group _ump_osu_memory */
/** @addtogroup _ump_osu_lock
* @{ */
/** @brief Initialize a Mutual Exclusion Lock.
*
* Locks are created in the signalled (unlocked) state.
*
* The parameter \a initial must be zero.
*
* At present, the parameter \a order must be zero. It remains for future
* expansion for mutex order checking.
*
* @param flags flags combined with bitwise OR ('|'), or zero. There are
* restrictions on which flags can be combined, see \ref _ump_osu_lock_flags_t.
* @param initial For future expansion into semaphores. SBZ.
* @param order The locking order of the mutex. SBZ.
* @return On success, a pointer to a \ref _ump_osu_lock_t object. NULL on failure.
*/
_ump_osu_lock_t *_ump_osu_lock_init( _ump_osu_lock_flags_t flags, u32 initial, u32 order );
/** @brief Obtain a statically initialized Mutual Exclusion Lock.
*
* Retrieves a reference to a statically initialized lock. Up to
* _UMP_OSU_STATIC_LOCK_COUNT statically initialized locks are
* available. Only _ump_osu_lock_wait(), _ump_osu_lock_trywait(),
* _ump_osu_lock_signal() can be used with statically initialized locks.
* _UMP_OSU_LOCKMODE_RW mode should be used when waiting and signalling
* statically initialized locks.
*
* For the same \a nr a pointer to the same statically initialized lock is
* returned. That is, given the following code:
* @code
* extern u32 n;
*
* _ump_osu_lock_t *locka = _ump_osu_lock_static(n);
* _ump_osu_lock_t *lockb = _ump_osu_lock_static(n);
* @endcode
* Then (locka == lockb), for all 0 <= n < UMP_OSU_STATIC_LOCK_COUNT.
*
* @param nr index of a statically initialized lock [0..UMP_OSU_STATIC_LOCK_COUNT-1]
* @return On success, a pointer to a _ump_osu_lock_t object. NULL on failure.
*/
_ump_osu_lock_t *_ump_osu_lock_static( u32 nr );
/** @brief Initialize a Mutual Exclusion Lock safely across multiple threads.
*
* The _ump_osu_lock_auto_init() function guarantees that the given lock will
* be initialized once and precisely once, even in a situation involving
* multiple threads.
*
* This is necessary because the first call to certain Public API functions must
* initialize the API. However, there can be a race involved to call the first
* library function in multi-threaded applications. To resolve this race, a
* mutex can be used. This mutex must be initialized, but initialized only once
* by any thread that might compete for its initialization. This function
* guarantees the initialization to happen correctly, even when there is an
* initialization race between multiple threads.
*
* Otherwise, the operation is identical to the _ump_osu_lock_init() function.
* For more details, refer to _ump_osu_lock_init().
*
* @param pplock pointer to storage for a _ump_osu_lock_t pointer. This
* _ump_osu_lock_t pointer may point to a _ump_osu_lock_t that has been
* initialized already
* @param flags flags combined with bitwise OR ('|'), or zero. There are
* restrictions on which flags can be combined. Refer to
* \ref _ump_osu_lock_flags_t for more information.
* The absence of any flags (the value 0) results in a sleeping-mutex,
* which is non-interruptible.
* @param initial For future expansion into semaphores. SBZ.
* @param order The locking order of the mutex. SBZ.
* @return On success, _UMP_OSU_ERR_OK is returned and a pointer to an
* initialized \ref _ump_osu_lock_t object is written into \a *pplock.
* _UMP_OSU_ERR_FAULT is returned on failure.
*/
_ump_osu_errcode_t _ump_osu_lock_auto_init( _ump_osu_lock_t **pplock, _ump_osu_lock_flags_t flags, u32 initial, u32 order );
/** @brief Wait for a lock to be signalled (obtained).
*
* After a thread has successfully waited on the lock, the lock is obtained by
* the thread, and is marked as unsignalled. The thread releases the lock by
* signalling it.
*
* To prevent deadlock, locks must always be obtained in the same order.
*
* @param lock the lock to wait upon (obtain).
* @param mode the mode in which the lock should be obtained. Currently this
* must be _UMP_OSU_LOCKMODE_RW.
* @return On success, _UMP_OSU_ERR_OK, _UMP_OSU_ERR_FAULT on error.
*/
_ump_osu_errcode_t _ump_osu_lock_wait( _ump_osu_lock_t *lock, _ump_osu_lock_mode_t mode);
/** @brief Wait for a lock to be signalled (obtained) with timeout
*
* After a thread has successfully waited on the lock, the lock is obtained by
* the thread, and is marked as unsignalled. The thread releases the lock by
* signalling it.
*
* To prevent deadlock, locks must always be obtained in the same order.
*
* This version can return early if it cannot obtain the lock within the given timeout.
*
* @param lock the lock to wait upon (obtain).
* @param mode the mode in which the lock should be obtained. Currently this
* must be _UMP_OSU_LOCKMODE_RW.
* @param timeout Relative time in microseconds for the timeout
* @return _UMP_OSU_ERR_OK if the lock was obtained, _UMP_OSU_ERR_TIMEOUT if the timeout expired or _UMP_OSU_ERR_FAULT on error.
*/
_ump_osu_errcode_t _ump_osu_lock_timed_wait( _ump_osu_lock_t *lock, _ump_osu_lock_mode_t mode, u64 timeout);
/** @brief Test for a lock to be signalled and obtains the lock when so.
*
* Obtains the lock only when it is in signalled state. The lock is then
* marked as unsignalled. The lock is released again by signalling
* it by _ump_osu_lock_signal().
*
* If the lock could not be obtained immediately (that is, another thread
* currently holds the lock), then this function \b does \b not wait for the
* lock to be in a signalled state. Instead, an error code is immediately
* returned to indicate that the thread could not obtain the lock.
*
* To prevent deadlock, locks must always be obtained in the same order.
*
* @param lock the lock to wait upon (obtain).
* @param mode the mode in which the lock should be obtained. Currently this
* must be _UMP_OSU_LOCKMODE_RW.
* @return When the lock was obtained, _UMP_OSU_ERR_OK. If the lock could not
* be obtained, _UMP_OSU_ERR_FAULT.
*/
_ump_osu_errcode_t _ump_osu_lock_trywait( _ump_osu_lock_t *lock, _ump_osu_lock_mode_t mode);
/** @brief Signal (release) a lock.
*
* Locks may only be signalled by the thread that originally waited upon the
* lock, unless the lock was created using the _UMP_OSU_LOCKFLAG_ANYUNLOCK flag.
*
* @param lock the lock to signal (release).
* @param mode the mode in which the lock should be obtained. This must match
* the mode in which the lock was waited upon.
*/
void _ump_osu_lock_signal( _ump_osu_lock_t *lock, _ump_osu_lock_mode_t mode );
/** @brief Terminate a lock.
*
* This terminates a lock and frees all associated resources.
*
* It is a programming error to terminate the lock when it is held (unsignalled)
* by a thread.
*
* @param lock the lock to terminate.
*/
void _ump_osu_lock_term( _ump_osu_lock_t *lock );
/** @} */ /* end group _ump_osu_lock */
/** @} */ /* end group osuapi */
/** @} */ /* end group uddapi */
#ifdef __cplusplus
}
#endif
#endif /* __UMP_OSU_H__ */
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file ump_platform.h
*
* This file should define UMP_API_EXPORT,
* which dictates how the UMP user space API should be exported/imported.
* Modify this file, if needed, to match your platform setup.
*/
#ifndef __UMP_PLATFORM_H__
#define __UMP_PLATFORM_H__
/** @addtogroup ump_user_space_api
* @{ */
/**
* A define which controls how UMP user space API functions are imported and exported.
* This define should be set by the implementor of the UMP API.
*/
#if defined(_WIN32)
#define UMP_API_EXPORT
#elif defined(__SYMBIAN32__)
#define UMP_API_EXPORT IMPORT_C
#else
#if defined(__GNUC__)
#if __GNUC__ >= 4
# define MALI_VISIBLE __attribute__ ((visibility ("default"))) /**< Function should be visible from outside the dll */
#else
# define MALI_VISIBLE
#endif
#elif defined(__ARMCC_VERSION)
/* ARMCC specific */
# define MALI_VISIBLE __declspec(dllexport)
#else
# define MALI_VISIBLE
#endif
#define UMP_API_EXPORT MALI_VISIBLE
#endif
/** @} */ /* end group ump_user_space_api */
#endif /* __UMP_PLATFORM_H__ */
/*
* Copyright (C) 2010, 2012 ARM Limited. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file ump_ref_drv.h
*
* Reference driver extensions to the UMP user space API for allocating UMP memory
*/
#ifndef _UNIFIED_MEMORY_PROVIDER_REF_DRV_H_
#define _UNIFIED_MEMORY_PROVIDER_REF_DRV_H_
#include "ump.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum
{
/* This enum must match with the IOCTL enum in ump_ioctl.h */
UMP_REF_DRV_CONSTRAINT_NONE = 0,
UMP_REF_DRV_CONSTRAINT_PHYSICALLY_LINEAR = 1,
UMP_REF_DRV_CONSTRAINT_USE_CACHE = 4,
} ump_alloc_constraints;
/** Allocate an UMP handle containing a memory buffer.
* Input: Size: The minimum size for the allocation.
* Usage: If this is UMP_REF_DRV_CONSTRAINT_USE_CACHE, the allocation is mapped as cached by the cpu.
* If it is UMP_REF_DRV_CONSTRAINT_NONE it is mapped as noncached.
* The flag UMP_REF_DRV_CONSTRAINT_PHYSICALLY_LINEAR is not supported.*/
UMP_API_EXPORT ump_handle ump_ref_drv_allocate(unsigned long size, ump_alloc_constraints usage);
typedef enum
{
UMP_MSYNC_CLEAN = 0 ,
UMP_MSYNC_CLEAN_AND_INVALIDATE = 1,
UMP_MSYNC_INVALIDATE = 2,
UMP_MSYNC_READOUT_CACHE_ENABLED = 128,
} ump_cpu_msync_op;
typedef enum
{
UMP_READ = 1,
UMP_READ_WRITE = 3,
} ump_lock_usage;
/** Flushing cache for an ump_handle.
* The function will always CLEAN_AND_INVALIDATE as long as the \a op is not UMP_MSYNC_READOUT_CACHE_ENABLED.
* If so it will only report back if the given ump_handle is cacheable.
* At the momement the implementation does not use \a address or \a size.
* Return value is 1 if cache is enabled, and 0 if it is disabled for the given allocation.*/
UMP_API_EXPORT int ump_cpu_msync_now(ump_handle mem, ump_cpu_msync_op op, void* address, int size);
typedef enum
{
UMP_USED_BY_CPU = 0,
UMP_USED_BY_MALI = 1,
UMP_USED_BY_UNKNOWN_DEVICE = 100,
} ump_hw_usage;
typedef enum
{
UMP_CACHE_OP_START = 0,
UMP_CACHE_OP_FINISH = 1,
} ump_cache_op_control;
/** Cache operation control. Tell when cache maintenance operations start and end.
This will allow the kernel to merge cache operations togheter, thus making them faster */
UMP_API_EXPORT int ump_cache_operations_control(ump_cache_op_control op);
/** Memory synchronization - cache flushing if previous user was different hardware */
UMP_API_EXPORT int ump_switch_hw_usage( ump_handle mem, ump_hw_usage new_user );
/** Memory synchronization - cache flushing if previous user was different hardware */
UMP_API_EXPORT int ump_switch_hw_usage_secure_id( ump_secure_id ump_id, ump_hw_usage new_user );
/** Locking buffer. Blocking call if the buffer is already locked. */
UMP_API_EXPORT int ump_lock( ump_handle mem, ump_lock_usage lock_usage );
/** Locking buffer. Blocking call if the buffer is already locked. */
UMP_API_EXPORT int ump_lock_secure_id( ump_secure_id ump_id, ump_lock_usage lock_usage );
/** Unlocking buffer. Let other users lock the buffer for their usage */
UMP_API_EXPORT int ump_unlock( ump_handle mem );
/** Unlocking buffer. Let other users lock the buffer for their usage */
UMP_API_EXPORT int ump_unlock_secure_id( ump_secure_id ump_id );
#ifdef __cplusplus
}
#endif
#endif /*_UNIFIED_MEMORY_PROVIDER_REF_DRV_H_ */
/*
* Copyright (C) 2010, 2012 ARM Limited. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file ump_uk_types.h
* Defines the types and constants used in the user-kernel interface
*/
#ifndef __UMP_UK_TYPES_H__
#define __UMP_UK_TYPES_H__
#ifdef __cplusplus
extern "C"
{
#endif
/* Helpers for API version handling */
#define MAKE_VERSION_ID(x) (((x) << 16UL) | (x))
#define IS_VERSION_ID(x) (((x) & 0xFFFF) == (((x) >> 16UL) & 0xFFFF))
#define GET_VERSION(x) (((x) >> 16UL) & 0xFFFF)
#define IS_API_MATCH(x, y) (IS_VERSION_ID((x)) && IS_VERSION_ID((y)) && (GET_VERSION((x)) == GET_VERSION((y))))
/**
* API version define.
* Indicates the version of the kernel API
* The version is a 16bit integer incremented on each API change.
* The 16bit integer is stored twice in a 32bit integer
* So for version 1 the value would be 0x00010001
*/
#define UMP_IOCTL_API_VERSION MAKE_VERSION_ID(2)
typedef enum
{
_UMP_IOC_QUERY_API_VERSION = 1,
_UMP_IOC_ALLOCATE,
_UMP_IOC_RELEASE,
_UMP_IOC_SIZE_GET,
_UMP_IOC_MAP_MEM, /* not used in Linux */
_UMP_IOC_UNMAP_MEM, /* not used in Linux */
_UMP_IOC_MSYNC,
_UMP_IOC_CACHE_OPERATIONS_CONTROL,
_UMP_IOC_SWITCH_HW_USAGE,
_UMP_IOC_LOCK,
_UMP_IOC_UNLOCK,
}_ump_uk_functions;
typedef enum
{
UMP_REF_DRV_UK_CONSTRAINT_NONE = 0,
UMP_REF_DRV_UK_CONSTRAINT_PHYSICALLY_LINEAR = 1,
UMP_REF_DRV_UK_CONSTRAINT_USE_CACHE = 4,
} ump_uk_alloc_constraints;
typedef enum
{
_UMP_UK_MSYNC_CLEAN = 0,
_UMP_UK_MSYNC_CLEAN_AND_INVALIDATE = 1,
_UMP_UK_MSYNC_INVALIDATE = 2,
_UMP_UK_MSYNC_FLUSH_L1 = 3,
_UMP_UK_MSYNC_READOUT_CACHE_ENABLED = 128,
} ump_uk_msync_op;
typedef enum
{
_UMP_UK_CACHE_OP_START = 0,
_UMP_UK_CACHE_OP_FINISH = 1,
} ump_uk_cache_op_control;
typedef enum
{
_UMP_UK_READ = 1,
_UMP_UK_READ_WRITE = 3,
} ump_uk_lock_usage;
typedef enum
{
_UMP_UK_USED_BY_CPU = 0,
_UMP_UK_USED_BY_MALI = 1,
_UMP_UK_USED_BY_UNKNOWN_DEVICE= 100,
} ump_uk_user;
/**
* Get API version ([in,out] u32 api_version, [out] u32 compatible)
*/
typedef struct _ump_uk_api_version_s
{
void *ctx; /**< [in,out] user-kernel context (trashed on output) */
u32 version; /**< Set to the user space version on entry, stores the device driver version on exit */
u32 compatible; /**< Non-null if the device is compatible with the client */
} _ump_uk_api_version_s;
/**
* ALLOCATE ([out] u32 secure_id, [in,out] u32 size, [in] contraints)
*/
typedef struct _ump_uk_allocate_s
{
void *ctx; /**< [in,out] user-kernel context (trashed on output) */
u32 secure_id; /**< Return value from DD to Userdriver */
u32 size; /**< Input and output. Requested size; input. Returned size; output */
ump_uk_alloc_constraints constraints; /**< Only input to Devicedriver */
} _ump_uk_allocate_s;
/**
* SIZE_GET ([in] u32 secure_id, [out]size )
*/
typedef struct _ump_uk_size_get_s
{
void *ctx; /**< [in,out] user-kernel context (trashed on output) */
u32 secure_id; /**< Input to DD */
u32 size; /**< Returned size; output */
} _ump_uk_size_get_s;
/**
* Release ([in] u32 secure_id)
*/
typedef struct _ump_uk_release_s
{
void *ctx; /**< [in,out] user-kernel context (trashed on output) */
u32 secure_id; /**< Input to DD */
} _ump_uk_release_s;
typedef struct _ump_uk_map_mem_s
{
void *ctx; /**< [in,out] user-kernel context (trashed on output) */
void *mapping; /**< [out] Returns user-space virtual address for the mapping */
void *phys_addr; /**< [in] physical address */
unsigned long size; /**< [in] size */
u32 secure_id; /**< [in] secure_id to assign to mapping */
void * _ukk_private; /**< Only used inside linux port between kernel frontend and common part to store vma */
u32 cookie;
u32 is_cached; /**< [in,out] caching of CPU mappings */
} _ump_uk_map_mem_s;
typedef struct _ump_uk_unmap_mem_s
{
void *ctx; /**< [in,out] user-kernel context (trashed on output) */
void *mapping;
u32 size;
void * _ukk_private;
u32 cookie;
} _ump_uk_unmap_mem_s;
typedef struct _ump_uk_msync_s
{
void *ctx; /**< [in,out] user-kernel context (trashed on output) */
void *mapping; /**< [in] mapping addr */
void *address; /**< [in] flush start addr */
u32 size; /**< [in] size to flush */
ump_uk_msync_op op; /**< [in] flush operation */
u32 cookie; /**< [in] cookie stored with reference to the kernel mapping internals */
u32 secure_id; /**< [in] secure_id that identifies the ump buffer */
u32 is_cached; /**< [out] caching of CPU mappings */
} _ump_uk_msync_s;
typedef struct _ump_uk_cache_operations_control_s
{
void *ctx; /**< [in,out] user-kernel context (trashed on output) */
ump_uk_cache_op_control op; /**< [in] cache operations start/stop */
} _ump_uk_cache_operations_control_s;
typedef struct _ump_uk_switch_hw_usage_s
{
void *ctx; /**< [in,out] user-kernel context (trashed on output) */
u32 secure_id; /**< [in] secure_id that identifies the ump buffer */
ump_uk_user new_user; /**< [in] cookie stored with reference to the kernel mapping internals */
} _ump_uk_switch_hw_usage_s;
typedef struct _ump_uk_lock_s
{
void *ctx; /**< [in,out] user-kernel context (trashed on output) */
u32 secure_id; /**< [in] secure_id that identifies the ump buffer */
ump_uk_lock_usage lock_usage;
} _ump_uk_lock_s;
typedef struct _ump_uk_unlock_s
{
void *ctx; /**< [in,out] user-kernel context (trashed on output) */
u32 secure_id; /**< [in] secure_id that identifies the ump buffer */
} _ump_uk_unlock_s;
#ifdef __cplusplus
}
#endif
#endif /* __UMP_UK_TYPES_H__ */
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __UMP_IOCTL_H__
#define __UMP_IOCTL_H__
#ifdef __cplusplus
extern "C"
{
#endif
#include <linux/types.h>
#include <linux/ioctl.h>
#include <ump_uk_types.h>
#ifndef __user
#define __user
#endif
/**
* @file UMP_ioctl.h
* This file describes the interface needed to use the Linux device driver.
* The interface is used by the userpace UMP driver.
*/
#define UMP_IOCTL_NR 0x90
#define UMP_IOC_QUERY_API_VERSION _IOR(UMP_IOCTL_NR, _UMP_IOC_QUERY_API_VERSION, _ump_uk_api_version_s)
#define UMP_IOC_ALLOCATE _IOWR(UMP_IOCTL_NR, _UMP_IOC_ALLOCATE, _ump_uk_allocate_s)
#define UMP_IOC_RELEASE _IOR(UMP_IOCTL_NR, _UMP_IOC_RELEASE, _ump_uk_release_s)
#define UMP_IOC_SIZE_GET _IOWR(UMP_IOCTL_NR, _UMP_IOC_SIZE_GET, _ump_uk_size_get_s)
#define UMP_IOC_MSYNC _IOW(UMP_IOCTL_NR, _UMP_IOC_MSYNC, _ump_uk_msync_s)
#define UMP_IOC_CACHE_OPERATIONS_CONTROL _IOW(UMP_IOCTL_NR, _UMP_IOC_CACHE_OPERATIONS_CONTROL, _ump_uk_cache_operations_control_s)
#define UMP_IOC_SWITCH_HW_USAGE _IOW(UMP_IOCTL_NR, _UMP_IOC_SWITCH_HW_USAGE, _ump_uk_switch_hw_usage_s)
#define UMP_IOC_LOCK _IOW(UMP_IOCTL_NR, _UMP_IOC_LOCK, _ump_uk_lock_s)
#define UMP_IOC_UNLOCK _IOW(UMP_IOCTL_NR, _UMP_IOC_UNLOCK, _ump_uk_unlock_s)
#ifdef __cplusplus
}
#endif
#endif /* __UMP_IOCTL_H__ */
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if ((!defined _XOPEN_SOURCE) || ((_XOPEN_SOURCE - 0) < 600))
#undef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600
#endif
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200112L
#elif _POSIX_C_SOURCE < 200112L
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200112L
#endif
#include <ump/ump_osu.h>
#include <ump/ump_debug.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
/**
* @file ump_osu_locks.c
* File implements the user side of the OS interface
*/
/** @opt Most of the time, we use the plain mutex type of osu_lock, and so
* only require the flags and mutex members. This costs 2 extra DWORDS, but
* most of the time we don't use those DWORDS.
* Therefore, ANY_UNLOCK type osu_locks can be implemented as a second
* structure containing the member _ump_osu_lock_t lock_t, plus the extra
* state required. Then, we use &container->lock_t when passing out of the
* OSU api, and CONTAINER_OF() when passing back in to recover the original
* structure. */
/** Private declaration of the OSU lock type */
struct _ump_osu_lock_t_struct
{
/** At present, only two types of mutex, so we store this information as
* the flags supplied at init time */
_ump_osu_lock_flags_t flags;
pthread_mutex_t mutex; /**< Used in both plain and ANY_UNLOCK osu_locks */
/* Extra State for ANY_UNLOCK osu_locks. These are UNINITIALIZED when
* flags does not contain _UMP_OSU_LOCKFLAG_ANYUNLOCK: */
pthread_cond_t condition; /**< The condition object to use while blocking */
ump_bool state; /**< The boolean which indicates the event's state */
UMP_DEBUG_CODE(
/** debug checking of locks */
_ump_osu_lock_mode_t locked_as;
) /* UMP_DEBUG_CODE */
};
/* Provide two statically initialized locks */
UMP_STATIC _ump_osu_lock_t _ump_osu_static_locks[] =
{
{
_UMP_OSU_LOCKFLAG_STATIC,
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_COND_INITIALIZER,
UMP_FALSE,
UMP_DEBUG_CODE( _UMP_OSU_LOCKMODE_UNDEF )
},
{
_UMP_OSU_LOCKFLAG_STATIC,
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_COND_INITIALIZER,
UMP_FALSE,
UMP_DEBUG_CODE( _UMP_OSU_LOCKMODE_UNDEF )
},
{
_UMP_OSU_LOCKFLAG_STATIC,
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_COND_INITIALIZER,
UMP_FALSE,
UMP_DEBUG_CODE( _UMP_OSU_LOCKMODE_UNDEF )
},
{
_UMP_OSU_LOCKFLAG_STATIC,
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_COND_INITIALIZER,
UMP_FALSE,
UMP_DEBUG_CODE( _UMP_OSU_LOCKMODE_UNDEF )
},
};
/* Critical section for auto_init */
UMP_STATIC pthread_mutex_t static_auto_init_mutex = PTHREAD_MUTEX_INITIALIZER;
_ump_osu_errcode_t _ump_osu_lock_auto_init( _ump_osu_lock_t **pplock, _ump_osu_lock_flags_t flags, u32 initial, u32 order )
{
int call_result;
/* Validate parameters: */
UMP_DEBUG_ASSERT_POINTER( pplock );
/** @opt We don't lock the Critical Section or do anything if this is already non-null */
if ( NULL != *pplock)
{
return _UMP_OSU_ERR_OK;
}
/* We MIGHT need to initialize it, lock the Critical Section and check again */
call_result = pthread_mutex_lock(&static_auto_init_mutex);
/* It would be a programming error for this to fail: */
UMP_DEBUG_ASSERT( 0 == call_result,
("failed to lock critical section\n") );
if ( NULL != *pplock )
{
/*
We caught a race condition to initialize this osu_lock.
The other thread won the race, so the osu_lock is now initialized.
*/
call_result = pthread_mutex_unlock(&static_auto_init_mutex);
UMP_DEBUG_ASSERT(0 == call_result,
("failed to unlock critical section\n"));
return _UMP_OSU_ERR_OK;
}
/* We're the first thread in: initialize the osu_lock */
*pplock = _ump_osu_lock_init( flags, initial, order );
if ( NULL == *pplock )
{
/* osu_lock creation failed */
call_result = pthread_mutex_unlock(&static_auto_init_mutex);
UMP_DEBUG_ASSERT(0 == call_result,
("failed to unlock critical section\n"));
return _UMP_OSU_ERR_FAULT;
}
/* osu_lock created OK */
call_result = pthread_mutex_unlock(&static_auto_init_mutex);
UMP_DEBUG_ASSERT(0 == call_result,
("failed to unlock critical section\n"));
UMP_IGNORE( call_result );
return _UMP_OSU_ERR_OK;
}
_ump_osu_lock_t *_ump_osu_lock_init( _ump_osu_lock_flags_t flags, u32 initial, u32 order )
{
_ump_osu_lock_t * lock;
pthread_mutexattr_t mutex_attributes;
UMP_IGNORE(order); /* order isn't implemented yet, for now callers should set it to zero. */
/* Validate parameters: */
/* Flags acceptable */
UMP_DEBUG_ASSERT( 0 == ( flags & ~( _UMP_OSU_LOCKFLAG_ANYUNLOCK)),
("incorrect flags or trying to initialise a statically initialized lock, %.8X\n", flags) );
/* Parameter initial SBZ - for future expansion */
UMP_DEBUG_ASSERT( 0 == initial,
("initial must be zero\n") );
if (0 != pthread_mutexattr_init(&mutex_attributes))
{
return NULL;
}
#if UMP_DEBUG_EXTENDED_MUTEX_LOCK_CHECKING
#define UMP_PTHREADS_MUTEX_TYPE PTHREAD_MUTEX_ERRORCHECK
#else
#define UMP_PTHREADS_MUTEX_TYPE PTHREAD_MUTEX_DEFAULT
#endif
if (0 != pthread_mutexattr_settype(&mutex_attributes, UMP_PTHREADS_MUTEX_TYPE))
{
/** Return NULL on failure */
pthread_mutexattr_destroy(&mutex_attributes);
return NULL;
}
#undef UMP_PTHREADS_MUTEX_TYPE
/** @opt use containing structures for the ANY_UNLOCK type, to
* save 2 DWORDS when not in use */
lock = _ump_osu_malloc( sizeof(_ump_osu_lock_t) );
if( NULL == lock )
{
/** Return NULL on failure */
pthread_mutexattr_destroy(&mutex_attributes);
return NULL;
}
if (0 != pthread_mutex_init( &lock->mutex, &mutex_attributes ))
{
pthread_mutexattr_destroy(&mutex_attributes);
_ump_osu_free( lock );
return NULL;
}
/* done with the mutexattr object */
pthread_mutexattr_destroy(&mutex_attributes);
/* ANY_UNLOCK type */
if ( flags & _UMP_OSU_LOCKFLAG_ANYUNLOCK )
{
if (0 != pthread_cond_init( &lock->condition, NULL ))
{
/* cleanup */
pthread_mutex_destroy( &lock->mutex );
_ump_osu_free( lock );
return NULL;
}
lock->state = UMP_FALSE; /* mark as unlocked by default */
}
lock->flags = flags;
/** Debug lock checking */
UMP_DEBUG_CODE( lock->locked_as = _UMP_OSU_LOCKMODE_UNDEF );
return lock;
}
_ump_osu_errcode_t _ump_osu_lock_timed_wait( _ump_osu_lock_t *lock, _ump_osu_lock_mode_t mode, u64 timeout)
{
/* absolute time specifier */
struct timespec ts;
struct timeval tv;
/* Parameter validation */
UMP_DEBUG_ASSERT_POINTER( lock );
UMP_DEBUG_ASSERT( _UMP_OSU_LOCKMODE_RW == mode,
("unrecognised mode, %.8X\n", mode) );
UMP_DEBUG_ASSERT( _UMP_OSU_LOCKFLAG_ANYUNLOCK == lock->flags, ("Timed operations only implemented for ANYUNLOCK type locks"));
/* calculate the realtime timeout value */
if (0 != gettimeofday(&tv, NULL))
{
UMP_DEBUG_PRINT(1,("Could not get the current realtime value to calculate the absolute value for a timed mutex lock with a timeout"));
return _UMP_OSU_ERR_FAULT;
}
tv.tv_usec += timeout;
#define UMP_USECS_PER_SECOND 1000000LL
#define UMP_NANOSECS_PER_USEC 1000LL
/* did we overflow a second in the usec part? */
while (tv.tv_usec >= UMP_USECS_PER_SECOND)
{
tv.tv_usec -= UMP_USECS_PER_SECOND;
tv.tv_sec++;
}
/* copy to the correct struct */
ts.tv_sec = tv.tv_sec;
ts.tv_nsec = (tv.tv_usec * UMP_NANOSECS_PER_USEC);
#undef UMP_USECS_PER_SECOND
#undef UMP_NANOSECS_PER_USEC
/* lock the mutex protecting access to the state field */
pthread_mutex_lock( &lock->mutex );
/* loop while locked (state is UMP_TRUE) */
/* pthread_cond_timedwait unlocks the mutex, wait, and locks the mutex once unblocked (either due to the event or the timeout) */
while ( UMP_TRUE == lock->state )
{
int res;
res = pthread_cond_timedwait( &lock->condition, &lock->mutex, &ts );
if (0 == res) continue; /* test the state variable again (loop condition) */
else if (ETIMEDOUT == res)
{
/* timeout, need to clean up and return the correct error code */
pthread_mutex_unlock(&lock->mutex);
return _UMP_OSU_ERR_TIMEOUT;
}
else
{
UMP_DEBUG_PRINT(1, ("Unexpected return from pthread_cond_timedwait 0x%08X\n", res));
pthread_mutex_unlock(&lock->mutex);
return _UMP_OSU_ERR_FAULT;
}
}
/* DEBUG tracking of previously locked state - occurs while lock is obtained */
UMP_DEBUG_ASSERT( _UMP_OSU_LOCKMODE_UNDEF == lock->locked_as,
("This lock was already locked\n") );
UMP_DEBUG_CODE( lock->locked_as = mode );
/* the state is UMP_FALSE (unlocked), so we set it to UMP_TRUE to indicate that it's locked and can return knowing that we own the lock */
lock->state = UMP_TRUE;
/* final unlock of the mutex */
pthread_mutex_unlock(&lock->mutex);
return _UMP_OSU_ERR_OK;
}
_ump_osu_errcode_t _ump_osu_lock_wait( _ump_osu_lock_t *lock, _ump_osu_lock_mode_t mode)
{
/* Parameter validation */
UMP_DEBUG_ASSERT_POINTER( lock );
UMP_DEBUG_ASSERT( _UMP_OSU_LOCKMODE_RW == mode,
("unrecognised mode, %.8X\n", mode) );
/** @note since only one flag can be set, we use a switch statement here.
* Otherwise, MUST add an enum into the _ump_osu_lock_t to store the
* implemented lock type */
switch ( lock->flags )
{
case _UMP_OSU_LOCKFLAG_STATIC:
case 0:
/* Usual Mutex type */
{
int call_result;
call_result = pthread_mutex_lock( &lock->mutex );
UMP_DEBUG_ASSERT( 0 == call_result,
("pthread_mutex_lock call failed with error code %d\n", call_result));
UMP_IGNORE( call_result );
}
/* DEBUG tracking of previously locked state - occurs while lock is obtained */
UMP_DEBUG_ASSERT( _UMP_OSU_LOCKMODE_UNDEF == lock->locked_as,
("This lock was already locked\n") );
UMP_DEBUG_CODE( lock->locked_as = mode );
break;
case _UMP_OSU_LOCKFLAG_ANYUNLOCK:
/** @note Use of bitflags in a case statement ONLY works because this
* is the ONLY flag that is supported */
/* lock the mutex protecting access to the state field */
pthread_mutex_lock( &lock->mutex );
/* loop while locked (state is UMP_TRUE) */
/* pthread_cond_wait unlocks the mutex, wait, and locks the mutex once unblocked */
while ( UMP_TRUE == lock->state ) pthread_cond_wait( &lock->condition, &lock->mutex );
/* DEBUG tracking of previously locked state - occurs while lock is obtained */
UMP_DEBUG_ASSERT( _UMP_OSU_LOCKMODE_UNDEF == lock->locked_as,
("This lock was already locked\n") );
UMP_DEBUG_CODE( lock->locked_as = mode );
/* the state is UMP_FALSE (unlocked), so we set it to UMP_TRUE to indicate that it's locked and can return knowing that we own the lock */
lock->state = UMP_TRUE;
/* final unlock of the mutex */
pthread_mutex_unlock(&lock->mutex);
break;
default:
UMP_DEBUG_ERROR( ("lock has incorrect flags==%.8X\n", lock->flags) );
break;
}
return _UMP_OSU_ERR_OK;
}
_ump_osu_errcode_t _ump_osu_lock_trywait( _ump_osu_lock_t *lock, _ump_osu_lock_mode_t mode)
{
_ump_osu_errcode_t err = _UMP_OSU_ERR_FAULT;
/* Parameter validation */
UMP_DEBUG_ASSERT_POINTER( lock );
UMP_DEBUG_ASSERT( _UMP_OSU_LOCKMODE_RW == mode,
("unrecognised mode, %.8X\n", mode) );
/** @note since only one flag can be set, we use a switch statement here.
* Otherwise, MUST add an enum into the _ump_osu_lock_t to store the
* implemented lock type */
switch ( lock->flags )
{
case _UMP_OSU_LOCKFLAG_STATIC:
case 0:
/* Usual Mutex type */
{
/* This is not subject to UMP_CHECK - overriding the result would cause a programming error */
if ( 0 == pthread_mutex_trylock( &lock->mutex ) )
{
err = _UMP_OSU_ERR_OK;
/* DEBUG tracking of previously locked state - occurs while lock is obtained */
UMP_DEBUG_ASSERT( _UMP_OSU_LOCKMODE_UNDEF == lock->locked_as
|| mode == lock->locked_as,
("tried as mode==%.8X, but was locked as %.8X\n", mode, lock->locked_as) );
UMP_DEBUG_CODE( lock->locked_as = mode );
}
}
break;
case _UMP_OSU_LOCKFLAG_ANYUNLOCK:
/** @note Use of bitflags in a case statement ONLY works because this
* is the ONLY flag that is supported */
/* lock the mutex protecting access to the state field */
pthread_mutex_lock(&lock->mutex);
if ( UMP_FALSE == lock->state)
{
/* unlocked, take the lock */
lock->state = UMP_TRUE;
err = _UMP_OSU_ERR_OK;
}
/* DEBUG tracking of previously locked state - occurs while lock is obtained */
/* Can do this regardless of whether we obtained ANYUNLOCK: */
UMP_DEBUG_ASSERT( _UMP_OSU_LOCKMODE_UNDEF == lock->locked_as
|| mode == lock->locked_as,
("tried as mode==%.8X, but was locked as %.8X\n", mode, lock->locked_as) );
/* If we were already locked, this does no harm, because of the above assert: */
UMP_DEBUG_CODE( lock->locked_as = mode );
pthread_mutex_unlock(&lock->mutex);
break;
default:
UMP_DEBUG_ERROR( ("lock has incorrect flags==%.8X\n", lock->flags) );
break;
}
return err;
}
void _ump_osu_lock_signal( _ump_osu_lock_t *lock, _ump_osu_lock_mode_t mode )
{
/* Parameter validation */
UMP_DEBUG_ASSERT_POINTER( lock );
UMP_DEBUG_ASSERT( _UMP_OSU_LOCKMODE_RW == mode,
("unrecognised mode, %.8X\n", mode) );
/** @note since only one flag can be set, we use a switch statement here.
* Otherwise, MUST add an enum into the _ump_osu_lock_t to store the
* implemented lock type */
switch ( lock->flags )
{
case _UMP_OSU_LOCKFLAG_STATIC:
case 0:
/* Usual Mutex type */
/* DEBUG tracking of previously locked state - occurs while lock is obtained */
UMP_DEBUG_ASSERT( mode == lock->locked_as,
("This lock was locked as==%.8X, but tried to unlock as mode==%.8X\n", lock->locked_as, mode));
UMP_DEBUG_CODE( lock->locked_as = _UMP_OSU_LOCKMODE_UNDEF );
{
int call_result;
call_result = pthread_mutex_unlock( &lock->mutex );
UMP_DEBUG_ASSERT( 0 == call_result,
("pthread_mutex_lock call failed with error code %d\n", call_result));
UMP_IGNORE( call_result );
}
break;
case _UMP_OSU_LOCKFLAG_ANYUNLOCK:
/** @note Use of bitflags in a case statement ONLY works because this
* is the ONLY flag that is supported */
pthread_mutex_lock(&lock->mutex);
UMP_DEBUG_ASSERT( UMP_TRUE == lock->state, ("Unlocking a _ump_osu_lock_t %p which is not locked\n", lock));
/* DEBUG tracking of previously locked state - occurs while lock is obtained */
UMP_DEBUG_ASSERT( mode == lock->locked_as,
("This lock was locked as==%.8X, but tried to unlock as %.8X\n", lock->locked_as, mode ));
UMP_DEBUG_CODE( lock->locked_as = _UMP_OSU_LOCKMODE_UNDEF );
/* mark as unlocked */
lock->state = UMP_FALSE;
/* signal the condition, only wake a single thread */
pthread_cond_signal(&lock->condition);
pthread_mutex_unlock(&lock->mutex);
break;
default:
UMP_DEBUG_ERROR( ("lock has incorrect flags==%.8X\n", lock->flags) );
break;
}
}
void _ump_osu_lock_term( _ump_osu_lock_t *lock )
{
int call_result;
UMP_DEBUG_ASSERT_POINTER( lock );
/** Debug lock checking: */
/* Lock is signalled on terminate - not a guarantee, since we could be locked immediately beforehand */
UMP_DEBUG_ASSERT( _UMP_OSU_LOCKMODE_UNDEF == lock->locked_as,
("cannot terminate held lock\n") );
call_result = pthread_mutex_destroy( &lock->mutex );
UMP_DEBUG_ASSERT( 0 == call_result,
("Incorrect mutex use detected: pthread_mutex_destroy call failed with error code %d\n", call_result) );
/* Destroy extra state for ANY_UNLOCK type osu_locks */
if ( lock->flags & _UMP_OSU_LOCKFLAG_ANYUNLOCK )
{
UMP_DEBUG_ASSERT( UMP_FALSE == lock->state, ("terminate called on locked object %p\n", lock));
call_result = pthread_cond_destroy(&lock->condition);
UMP_DEBUG_ASSERT( 0 == call_result,
("Incorrect condition-variable use detected: pthread_cond_destroy call failed with error code %d\n", call_result) );
}
UMP_IGNORE(call_result);
_ump_osu_free( lock );
}
_ump_osu_lock_t *_ump_osu_lock_static( u32 nr )
{
UMP_DEBUG_ASSERT( nr < UMP_OSU_STATIC_LOCK_COUNT,
("provided static lock index (%d) out of bounds (0 < nr < %d)\n", nr, UMP_OSU_STATIC_LOCK_COUNT) );
return &_ump_osu_static_locks[nr];
}
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <ump/ump_osu.h>
#include <stdlib.h>
#include <string.h> /* memcmp, memchr, memset */
/**
* @file ump_osu_memory.c
* File implements the user side of the OS interface
*/
void *_ump_osu_calloc( u32 n, u32 size )
{
return calloc( n, size );
}
void *_ump_osu_malloc( u32 size )
{
return malloc( size );
}
void *_ump_osu_realloc( void *ptr, u32 size )
{
return realloc( ptr, size );
}
void _ump_osu_free( void *ptr )
{
free( ptr );
}
void *_ump_osu_memcpy( void *dst, const void *src, u32 len )
{
return memcpy( dst, src, len );
}
void *_ump_osu_memset( void *ptr, u32 chr, u32 size )
{
return memset( ptr, chr, size );
}
int _ump_osu_memcmp( const void *ptr1, const void *ptr2, u32 size )
{
return memcmp( ptr1, ptr2, size );
}
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file ump_uku.c
* File implements the user side of the user-kernel interface
*/
#include "../ump_uku.h"
#include <stdio.h>
#include "ump_ioctl.h"
#include <sys/mman.h>
/* Needed for file operations on the device file*/
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
static _ump_osu_errcode_t ump_driver_ioctl(void *context, u32 command, void *args);
static int ump_ioctl_api_version_used = UMP_IOCTL_API_VERSION;
/**
* The device file to access the UMP device driver
* This is a character special file giving access to the device driver.
* Usually created using the mknod command line utility.
*/
static const char ump_device_file_name[] = "/dev/ump";
_ump_osu_errcode_t _ump_uku_open( void **context )
{
int ump_device_file;
if(NULL == context)
{
return _UMP_OSU_ERR_FAULT;
}
ump_device_file = open(ump_device_file_name, O_RDWR);
if (-1 == ump_device_file)
{
return _UMP_OSU_ERR_FAULT;
}
{
struct _ump_uk_api_version_s args;
args.ctx = (void*)ump_device_file;
args.version = UMP_IOCTL_API_VERSION;
args.compatible = 3;
ump_driver_ioctl(args.ctx, UMP_IOC_QUERY_API_VERSION, &args);
if ( 1 != args.compatible )
{
if (IS_API_MATCH(MAKE_VERSION_ID(1), args.version))
{
ump_ioctl_api_version_used = MAKE_VERSION_ID(1);
UMP_PRINTF("The UMP devicedriver does not support cached UMP. Update it if this is needed.\n");
}
else
{
UMP_PRINTF("The UMP devicedriver is version: %d, UMP libraries is version: %d.\n", GET_VERSION(args.version), GET_VERSION(UMP_IOCTL_API_VERSION) );
close(ump_device_file);
return _UMP_OSU_ERR_FAULT;
}
}
}
*context = (void *) ump_device_file;
return _UMP_OSU_ERR_OK;
}
_ump_osu_errcode_t _ump_uku_close( void **context )
{
if(NULL == context)
{
return _UMP_OSU_ERR_FAULT;
}
if(-1 == (int)*context)
{
return _UMP_OSU_ERR_FAULT;
}
close((int)*context);
*context = (void *)-1;
return _UMP_OSU_ERR_OK;
}
int _ump_uku_allocate(_ump_uk_allocate_s *args)
{
return ump_driver_ioctl(args->ctx, UMP_IOC_ALLOCATE, args);
}
_ump_osu_errcode_t _ump_uku_release(_ump_uk_release_s *args)
{
return ump_driver_ioctl(args->ctx, UMP_IOC_RELEASE, args);
}
_ump_osu_errcode_t _ump_uku_size_get(_ump_uk_size_get_s *args)
{
return ump_driver_ioctl(args->ctx, UMP_IOC_SIZE_GET, args);
}
void _ump_uku_msynch(_ump_uk_msync_s *args)
{
/* This is for backwards compatibillity */
if ( MAKE_VERSION_ID(1) == ump_ioctl_api_version_used)
{
args->is_cached = 0;
if ( _UMP_UK_MSYNC_READOUT_CACHE_ENABLED != args->op )
{
UMP_DEBUG_PRINT(3, ("Warning: Doing UMP cache flush operations on a Device Driver that does not support cached UMP mem.\n"));
}
return;
}
ump_driver_ioctl(args->ctx, UMP_IOC_MSYNC, args);
}
void _ump_uku_cache_operations_control( _ump_uk_cache_operations_control_s *args )
{
ump_driver_ioctl(args->ctx, UMP_IOC_CACHE_OPERATIONS_CONTROL, args);
}
void _ump_uku_switch_hw_usage( _ump_uk_switch_hw_usage_s *args )
{
ump_driver_ioctl(args->ctx, UMP_IOC_SWITCH_HW_USAGE, args);
}
void _ump_uku_lock( _ump_uk_lock_s *args )
{
ump_driver_ioctl(args->ctx, UMP_IOC_LOCK, args);
}
void _ump_uku_unlock( _ump_uk_unlock_s *args )
{
ump_driver_ioctl(args->ctx, UMP_IOC_UNLOCK, args);
}
int _ump_uku_map_mem(_ump_uk_map_mem_s *args)
{
int flags;
if( -1 == (int)args->ctx )
{
return -1;
}
flags = MAP_SHARED;
/* This is for backwards compatibillity */
if ( MAKE_VERSION_ID(1) == ump_ioctl_api_version_used)
{
args->is_cached = 0;
}
/* If we want the Caching to be enabled we set the flags to be PRIVATE. The UMP DD reads this and do proper handling
Note: this enforces the user to use proper invalidation*/
if ( args->is_cached ) flags = MAP_PRIVATE;
args->mapping = mmap(NULL, args->size, PROT_READ | PROT_WRITE ,flags , (int)args->ctx, (off_t)args->secure_id * sysconf(_SC_PAGE_SIZE));
if (MAP_FAILED == args->mapping)
{
return -1;
}
args->cookie = 0; /* Cookie is not used in linux _ump_uku_unmap_mem */
return 0;
}
void _ump_uku_unmap_mem( _ump_uk_unmap_mem_s *args )
{
/*
* If a smaller size is used Linux will just remove the requested range but don't tell
* the ump driver before all of it is unmapped, either via another unmap request or upon process shutdown.
* Unmapping too much will just ignore the overhead or hit undefined behavior,
* only affecting the calling process which could mess itself up in other ways anyway.
* So we don't need any security checks here.
*/
munmap(args->mapping, args->size);
}
static _ump_osu_errcode_t ump_driver_ioctl(void *context, u32 command, void *args)
{
/*UMP_CHECK_NON_NULL(args, _UMP_OSK_ERR_INVALID_ARGS);*/
/* check for a valid file descriptor */
/** @note manual type safety check-point */
if( -1 == (int)context )
{
return _UMP_OSU_ERR_FAULT;
}
/* call ioctl handler of driver */
if (0 != ioctl((int)context, command, args)) return -1;
return _UMP_OSU_ERR_OK;
}
/*
* Copyright (C) 2010-2012 ARM Limited. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file ump_uku.h
* Defines the user-side interface of the user-kernel interface
*/
#ifndef __UMP_UKU_H__
#define __UMP_UKU_H__
#include <ump/ump_osu.h>
#include <ump/ump_debug.h>
#include <ump/ump_uk_types.h>
#ifdef __cplusplus
extern "C"
{
#endif
_ump_osu_errcode_t _ump_uku_open( void **context );
_ump_osu_errcode_t _ump_uku_close( void **context );
_ump_osu_errcode_t _ump_uku_allocate( _ump_uk_allocate_s *args );
_ump_osu_errcode_t _ump_uku_release( _ump_uk_release_s *args );
_ump_osu_errcode_t _ump_uku_size_get( _ump_uk_size_get_s *args );
_ump_osu_errcode_t _ump_uku_get_api_version( _ump_uk_api_version_s *args );
int _ump_uku_map_mem( _ump_uk_map_mem_s *args );
void _ump_uku_unmap_mem( _ump_uk_unmap_mem_s *args );
void _ump_uku_msynch(_ump_uk_msync_s *args);
int _ump_uku_map_mem( _ump_uk_map_mem_s *args );
void _ump_uku_cache_operations_control( _ump_uk_cache_operations_control_s *args );
void _ump_uku_switch_hw_usage( _ump_uk_switch_hw_usage_s *dd_msync_call_arg );
void _ump_uku_lock( _ump_uk_lock_s *dd_msync_call_arg );
void _ump_uku_unlock( _ump_uk_unlock_s *dd_msync_call_arg );
#ifdef __cplusplus
}
#endif
#endif /* __UMP_UKU_H__ */
/*
* Copyright (C) 2011-2012 ARM Limited. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Building the UMP user space library for Linux
---------------------------------------------
A simple Makefile is provided, and the UMP user space library can be built
simply by issuing make. This Makefile is setup to use the ARM GCC compiler
from CodeSourcery, and it builds for ARMv6. Modification to this Makefile
is needed in order to build for other configurations.
In order to use this library from the Mali GPU driver, invoke the Mali GPU
driver build system with the following two make variables set;
- UMP_INCLUDE_DIR should point to the include folder inside this package
- UMP_LIB should point to the built library (libUMP.so)
This does not apply to Android builds, where the Android.mk file for the
Mali GPU driver needs to be manually edited in order to add the correct
include path and link against the correct library.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment