summaryrefslogtreecommitdiffstats
path: root/pcilib
diff options
context:
space:
mode:
Diffstat (limited to 'pcilib')
-rw-r--r--pcilib/CMakeLists.txt4
-rw-r--r--pcilib/config.h.in1
-rw-r--r--pcilib/event.h11
-rw-r--r--pcilib/kmem.c12
-rw-r--r--pcilib/kmem.h7
-rw-r--r--pcilib/lock.c277
-rw-r--r--pcilib/lock.h95
-rw-r--r--pcilib/locking.c351
-rw-r--r--pcilib/locking.h63
-rw-r--r--pcilib/pci.c26
-rw-r--r--pcilib/pci.h5
-rw-r--r--pcilib/pcilib.h11
12 files changed, 620 insertions, 243 deletions
diff --git a/pcilib/CMakeLists.txt b/pcilib/CMakeLists.txt
index ebb5cec..0bf0e66 100644
--- a/pcilib/CMakeLists.txt
+++ b/pcilib/CMakeLists.txt
@@ -3,8 +3,8 @@ include_directories(
${CMAKE_SOURCE_DIR}/pcilib
)
-set(HEADERS pcilib.h pci.h export.h bar.h fifo.h model.h bank.h register.h kmem.h irq.h dma.h event.h plugin.h tools.h error.h debug.h env.h version.h config.h locking.h lock.h)
-add_library(pcilib SHARED pci.c export.c bar.c fifo.c model.c bank.c register.c kmem.c irq.c dma.c event.c plugin.c tools.c error.c debug.c env.c locking.c lock.c)
+set(HEADERS pcilib.h pci.h export.h bar.h fifo.h model.h bank.h register.h kmem.h irq.h locking.h lock.h dma.h event.h plugin.h tools.h error.h debug.h env.h version.h config.h)
+add_library(pcilib SHARED pci.c export.c bar.c fifo.c model.c bank.c register.c kmem.c irq.c locking.c lock.c dma.c event.c plugin.c tools.c error.c debug.c env.c )
target_link_libraries(pcilib dma protocols ${CMAKE_THREAD_LIBS_INIT} ${UFODECODE_LIBRARIES} ${CMAKE_DL_LIBS})
add_dependencies(pcilib dma protocols)
diff --git a/pcilib/config.h.in b/pcilib/config.h.in
index e588612..bdd9ec3 100644
--- a/pcilib/config.h.in
+++ b/pcilib/config.h.in
@@ -2,3 +2,4 @@
#cmakedefine PCILIB_DATA_DIR "${PCILIB_DATA_DIR}"
#cmakedefine PCILIB_MODEL_DIR "${PCILIB_MODEL_DIR}"
#cmakedefine PCILIB_DEBUG_DIR "${PCILIB_DEBUG_DIR}"
+#cmakedefine HAVE_STDATOMIC_H @HAVE_STDATOMIC_H@
diff --git a/pcilib/event.h b/pcilib/event.h
index d2b9793..f14abeb 100644
--- a/pcilib/event.h
+++ b/pcilib/event.h
@@ -45,17 +45,6 @@ typedef struct {
const char *description;
} pcilib_event_data_type_description_t;
-typedef enum {
- PCILIB_STREAMING_STOP = 0, /**< stop streaming */
- PCILIB_STREAMING_CONTINUE = 1, /**< wait the default DMA timeout for a new data */
- PCILIB_STREAMING_WAIT = 2, /**< wait the specified timeout for a new data */
- PCILIB_STREAMING_CHECK = 3, /**< do not wait for the data, bail out imideatly if no data ready */
- PCILIB_STREAMING_FAIL = 4, /**< fail if data is not available on timeout */
- PCILIB_STREAMING_REQ_FRAGMENT = 5, /**< only fragment of a packet is read, wait for next fragment and fail if no data during DMA timeout */
- PCILIB_STREAMING_REQ_PACKET = 6, /**< wait for next packet and fail if no data during the specified timeout */
- PCILIB_STREAMING_TIMEOUT_MASK = 3 /**< mask specifying all timeout modes */
-} pcilib_streaming_action_t;
-
/*
* get_data: This call is used by get_data and copy_data functions of public
* interface. When copy_data is the caller, the data parameter will be passed.
diff --git a/pcilib/kmem.c b/pcilib/kmem.c
index 8560eae..2ff674f 100644
--- a/pcilib/kmem.c
+++ b/pcilib/kmem.c
@@ -79,9 +79,16 @@ pcilib_kmem_handle_t *pcilib_alloc_kernel_memory(pcilib_t *ctx, pcilib_kmem_type
}
memset(kbuf, 0, sizeof(pcilib_kmem_list_t) + nmemb * sizeof(pcilib_kmem_addr_t));
-
+
+ err = pcilib_lock(ctx->locks.mmap);
+ if (!err) {
+ pcilib_error("Error acquiring mmap lock");
+ return NULL;
+ }
+
ret = ioctl( ctx->handle, PCIDRIVER_IOC_MMAP_MODE, PCIDRIVER_MMAP_KMEM );
if (ret) {
+ pcilib_unlock(ctx->locks.mmap);
pcilib_error("PCIDRIVER_IOC_MMAP_MODE ioctl have failed");
return NULL;
}
@@ -169,6 +176,9 @@ pcilib_kmem_handle_t *pcilib_alloc_kernel_memory(pcilib_t *ctx, pcilib_kmem_type
kbuf->buf.blocks[i].mmap_offset = kh.pa & ctx->page_mask;
}
+ pcilib_unlock(ctx->locks.mmap);
+
+
//This is possible in the case of error (nothing is allocated yet) or if buffers are not reused
if (persistent < 0) persistent = 0;
if (hardware < 0) hardware = 0;
diff --git a/pcilib/kmem.h b/pcilib/kmem.h
index 65bdf04..9cd1971 100644
--- a/pcilib/kmem.h
+++ b/pcilib/kmem.h
@@ -1,6 +1,8 @@
#ifndef _PCILIB_KMEM_H
#define _PCILIB_KMEM_H
+#include <stdint.h>
+
typedef struct pcilib_s pcilib_t;
typedef struct pcilib_kmem_list_s pcilib_kmem_list_t;
@@ -14,7 +16,8 @@ typedef enum {
#define PCILIB_KMEM_TYPE_MASK 0xFFFF0000
#define PCILIB_KMEM_USE(type, subtype) ((pcilib_kmem_use_t)(((type) << 16)|(subtype)))
-
+#define PCILIB_KMEM_USE_TYPE(use) (use >> 16)
+#define PCILIB_KMEM_USE_SUBTYPE(use) (use & 0xFFFF)
typedef enum {
PCILIB_KMEM_TYPE_CONSISTENT = 0x00000,
@@ -31,7 +34,7 @@ typedef enum {
PCILIB_KMEM_USE_DMA_RING = 1,
PCILIB_KMEM_USE_DMA_PAGES = 2,
PCILIB_KMEM_USE_SOFTWARE_REGISTERS = 3,
- PCILIB_KMEM_USE_MUTEXES = 4,
+ PCILIB_KMEM_USE_LOCKS = 4,
PCILIB_KMEM_USE_USER = 0x10
} pcilib_kmem_use_t;
diff --git a/pcilib/lock.c b/pcilib/lock.c
index b92b11d..f1cbc56 100644
--- a/pcilib/lock.c
+++ b/pcilib/lock.c
@@ -1,116 +1,201 @@
#define _GNU_SOURCE
#define _XOPEN_SOURCE 600
+#include "config.h"
+
+#include <stdio.h>
#include <string.h>
#include <stdint.h>
+#include <assert.h>
+#include <pthread.h>
+#include <stdint.h>
+
+#ifdef HAVE_STDATOMIC_H
+# include <stdatomic.h>
+#endif /* HAVE_STDATOMIC_H */
+
#include "error.h"
#include "lock.h"
#include "pci.h"
-#include <stdio.h>
-/*
- * this function will take the lock for the semaphore pointed by semId
- */
-void pcilib_lock(pcilib_lock_t *lock_ctx, pcilib_lock_flags_t flags, ...){
- int err;
- struct timespec *time;
- va_list pa;
- va_start(pa,flags);
-
- if(flags & MUTEX_LOCK){
- err=pthread_mutex_lock(lock_ctx);/**< we try to lock here*/
- if(errno!=EOWNERDEAD && err!=0) pcilib_error("can't acquire lock %s, errno %i",(char*)(lock_ctx+sizeof(pcilib_lock_t)),errno);
- /** if the lock haven't been acquired and errno==EOWNERDEAD, it means the previous application that got the lock crashed, we have to remake the lock "consistent" so*/
- else if(errno==EOWNERDEAD){
- /* one question is "is pthread_mutex_consistent protected in case we call twice it?", it seems to not make any importance in fact regarding man pages, but we have to survey it in future applications*/
- pthread_mutex_consistent(lock_ctx);
- pthread_mutex_lock(lock_ctx);
- if(err!=0) pcilib_error("can't acquire lock %s, errno %i",(char*)(lock_ctx+sizeof(pcilib_lock_t)),errno);
- }
- }
- else if(flags & MUTEX_TRYLOCK){
- err=pthread_mutex_trylock(lock_ctx);/**< we try to lock here*/
- if(errno!=EOWNERDEAD && err!=0) pcilib_error("can't acquire lock %s, errno %i",(char*)(lock_ctx+sizeof(pcilib_lock_t)),errno);
- else if(errno==EOWNERDEAD){
- pthread_mutex_consistent(lock_ctx);
- pthread_mutex_trylock(lock_ctx);
- if(err!=0) pcilib_error("can't acquire lock %s, errno %i",(char*)(lock_ctx+sizeof(pcilib_lock_t)),errno);
- }
- }
- else if(flags & MUTEX_TIMEDLOCK){
- time=va_arg(pa,struct timespec*);
- va_end(pa);
- err=pthread_mutex_timedlock(lock_ctx, time);/**< we try to lock here*/
- if(errno!=EOWNERDEAD && err!=0) pcilib_error("can't acquire lock %s, errni %i",(char*)(lock_ctx+sizeof(pcilib_lock_t)),errno);
- else if(errno==EOWNERDEAD){
- pthread_mutex_consistent(lock_ctx);
- pthread_mutex_timedlock(lock_ctx, time);
- if(err!=0) pcilib_error("can't acquire lock %s, errno %i",(char*)(lock_ctx+sizeof(pcilib_lock_t)), errno);
- }
- }
- else pcilib_error("wrong flag for pcilib_lock");
-}
-/**
- * this function will unlock the semaphore pointed by lock_ctx.
- */
-void pcilib_unlock(pcilib_lock_t* lock_ctx){
- int err;
- if((err=pthread_mutex_unlock(lock_ctx))!=0) pcilib_error("can't unlock mutex: errno %i",errno);
-}
+struct pcilib_lock_s {
+ pthread_mutex_t mutex;
+ pcilib_lock_flags_t flags;
+#ifdef HAVE_STDATOMIC_H
+ volatile atomic_uint refs;
+#else /* HAVE_STDATOMIC_H */
+ volatile uint32_t refs;
+#endif /* HAVE_STDATOMIC_H */
+ char name[];
+};
+
/**
* this function initialize a new semaphore in the kernel if it's not already initialized given the key that permits to differentiate semaphores, and then return the integer that points to the semaphore that have been initialized or to a previously already initialized semaphore
*/
-pcilib_lock_t* pcilib_init_lock(pcilib_t *ctx, pcilib_lock_init_flags_t flag, char* lock_id, ...){
- int err;
- pthread_mutexattr_t attr;
- int i,j;
- void* addr,*locks_addr;
- char buffer[PCILIB_LOCK_SIZE-sizeof(pcilib_lock_t)];
- /* here lock_id is the format string for vsprintf, the lock name will so be put in adding arguments*/
- va_list pa;
- va_start(pa,lock_id);
- err=vsprintf(buffer,lock_id,pa);
- va_end(pa);
-
- if(err<0) pcilib_error("error in obtaining the lock name");
- if(((PCILIB_MAX_NUMBER_LOCKS*PCILIB_LOCK_SIZE)%PCILIB_KMEM_PAGE_SIZE)!=0) pcilib_error("PCILIB_MAX_NUMBER_LOCKS*PCILIB_LOCK_SIZE should be a multiple of kmem page size");
-
- addr=pcilib_kmem_get_block_ua(ctx,ctx->locks_handle,0);
- if((flag & PCILIB_NO_LOCK)==0) pcilib_lock((pcilib_lock_t*)addr,MUTEX_LOCK);
- /* we search for the given lock if it was already initialized*/
- for(j=0;j<PCILIB_NUMBER_OF_LOCK_PAGES;j++){
- i=0;
- locks_addr=pcilib_kmem_get_block_ua(ctx,ctx->locks_handle,j);
- while((*(char*)(locks_addr+i*PCILIB_LOCK_SIZE+sizeof(pcilib_lock_t))!=0) && (i<PCILIB_LOCKS_PER_PAGE)){
- if(strcmp(buffer,(char*)(locks_addr+i*PCILIB_LOCK_SIZE+sizeof(pcilib_lock_t)))==0){
- return (pcilib_lock_t*)(locks_addr+i*PCILIB_LOCK_SIZE);}
- i++;
- }
- if(i<PCILIB_LOCKS_PER_PAGE) break;
+int pcilib_init_lock(pcilib_lock_t *lock, pcilib_lock_flags_t flags, const char *lock_id) {
+ int err;
+ pthread_mutexattr_t attr;
+
+ assert(lock);
+ assert(lock_id);
+
+ memset(lock, 0, PCILIB_LOCK_SIZE);
+
+ if (strlen(lock_id) >= (PCILIB_LOCK_SIZE - offsetof(struct pcilib_lock_s, name))) {
+ pcilib_error("The supplied lock id (%s) is too long...", lock_id);
+ return PCILIB_ERROR_INVALID_ARGUMENT;
+ }
+
+ if ((err = pthread_mutexattr_init(&attr))!=0) {
+ pcilib_error("Can't initialize mutex attribute, errno %i", errno);
+ return PCILIB_ERROR_FAILED;
+ }
+
+ if ((err = pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED))!=0) {
+ pcilib_error("Can't configure a shared mutex attribute, errno %i", errno);
+ return PCILIB_ERROR_FAILED;
+ }
+
+ if ((flags&PCILIB_LOCK_FLAG_PERSISTENT)==0) {
+ if ((err = pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST))!=0) {
+ pcilib_error("Can't configure a robust mutex attribute, errno: %i", errno);
+ return PCILIB_ERROR_FAILED;
}
- /* the kernel space could be full*/
- if(i==PCILIB_LOCKS_PER_PAGE) pcilib_error("no more free space for a new lock\n");
- /* if not, we create a new one*/
- if((err= pthread_mutexattr_init(&attr))!=0) pcilib_error("can't initialize mutex attribute, errno %i",errno);
- if((err = pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED))!=0) pcilib_error("can't set a shared mutex, errno %i", errno);
- if((err= pthread_mutexattr_setrobust(&attr,PTHREAD_MUTEX_ROBUST))!=0) pcilib_error("can't set a robust mutex, errno: %i",errno);
- if((err=pthread_mutex_init((pcilib_lock_t*)(locks_addr+i*PCILIB_LOCK_SIZE),&attr))!=0) pcilib_error("can't set attributes to mutex, errno : %i",errno);
- pthread_mutexattr_destroy(&attr);
-
- strcpy((char*)(locks_addr+i*PCILIB_LOCK_SIZE+sizeof(pcilib_lock_t)),buffer);
-
- if((flag & PCILIB_NO_LOCK)==0) pcilib_unlock((pcilib_lock_t*)addr);
- return (pcilib_lock_t*)(locks_addr+i*PCILIB_LOCK_SIZE);
-
+ }
+
+ if ((err = pthread_mutex_init(&lock->mutex, &attr))!=0) {
+ pcilib_error("Can't create mutex, errno : %i",errno);
+ return PCILIB_ERROR_FAILED;
+ }
+
+ strcpy(lock->name, lock_id);
+ lock->refs = 1;
+ lock->flags = flags;
+
+ return 0;
}
+
/*
* we uninitialize a mutex and set its name to 0 pointed by lock_ctx with this function. setting name to is the real destroying operation, but we need to unitialize the lock to initialize it again after
*/
-void pcilib_free_lock(pcilib_lock_t *lock_ctx){
- int err;
- if((err=pthread_mutex_destroy(lock_ctx))==-1) pcilib_warning("can't clean lock properly, errno %i",errno);
- memset((char*)(lock_ctx+sizeof(pcilib_lock_t)),0,PCILIB_LOCK_SIZE-sizeof(pcilib_lock_t));
- }
+void pcilib_free_lock(pcilib_lock_t *lock) {
+ int err;
+
+ assert(lock);
+
+// if (lock->refs)
+// pcilib_error("Forbidding to destroy the referenced mutex...");
+
+ if ((err = pthread_mutex_destroy(&lock->mutex))==-1)
+ pcilib_warning("Can't destroy POSIX mutex, errno %i",errno);
+}
+
+
+void pcilib_lock_ref(pcilib_lock_t *lock) {
+ assert(lock);
+
+#ifdef HAVE_STDATOMIC_H
+ atomic_fetch_add_explicit(&lock->refs, 1, memory_order_relaxed);
+#else /* HAVE_STDATOMIC_H */
+ lock->refs++;
+#endif /* HAVE_STDATOMIC_H */
+}
+
+void pcilib_lock_unref(pcilib_lock_t *lock) {
+ assert(lock);
+
+ if (!lock->refs) {
+ pcilib_warning("Lock is not referenced");
+ return;
+ }
+
+#ifdef HAVE_STDATOMIC_H
+ atomic_fetch_sub_explicit(&lock->refs, 1, memory_order_relaxed);
+#else /* HAVE_STDATOMIC_H */
+ lock->refs--;
+#endif /* HAVE_STDATOMIC_H */
+}
+
+size_t pcilib_lock_get_refs(pcilib_lock_t *lock) {
+ return lock->refs;
+}
+
+pcilib_lock_flags_t pcilib_lock_get_flags(pcilib_lock_t *lock) {
+ return lock->flags;
+}
+
+const char *pcilib_lock_get_name(pcilib_lock_t *lock) {
+ assert(lock);
+
+ if (lock->name[0]) return lock->name;
+ return NULL;
+}
+
+/*
+ * this function will take the lock for the semaphore pointed by semId
+ */
+int pcilib_lock_custom(pcilib_lock_t *lock, pcilib_lock_flags_t flags, pcilib_timeout_t timeout) {
+ int err;
+
+ if (!lock) return 0;
+
+ struct timespec tm;
+
+ switch (timeout) {
+ case PCILIB_TIMEOUT_INFINITE:
+ err = pthread_mutex_lock(&lock->mutex);
+ break;
+ case PCILIB_TIMEOUT_IMMEDIATE:
+ err = pthread_mutex_trylock(&lock->mutex);
+ break;
+ default:
+ clock_gettime(CLOCK_REALTIME, &tm);
+ tm.tv_nsec += 1000 * (timeout%1000000);
+ if (tm.tv_nsec < 1000000000)
+ tm.tv_sec += timeout/1000000;
+ else {
+ tm.tv_sec += 1 + timeout/1000000;
+ tm.tv_nsec -= 1000000000;
+ }
+ err = pthread_mutex_timedlock(&lock->mutex, &tm);
+ }
+
+ if (!err)
+ return 0;
+
+ switch (err) {
+ case EOWNERDEAD:
+ err = pthread_mutex_consistent(&lock->mutex);
+ if (err) {
+ pcilib_error("Failed to mark mutex as consistent, errno %i", err);
+ }
+ break;
+ case ETIMEDOUT:
+ case EBUSY:
+ return PCILIB_ERROR_TIMEOUT;
+ default:
+ pcilib_error("Failed to obtain mutex, errno %i", err);
+ }
+
+ return PCILIB_ERROR_FAILED;
+}
+
+int pcilib_lock(pcilib_lock_t* lock) {
+ return pcilib_lock_custom(lock, PCILIB_LOCK_FLAGS_DEFAULT, PCILIB_TIMEOUT_INFINITE);
+}
+
+/**
+ * this function will unlock the semaphore pointed by lock_ctx.
+ */
+void pcilib_unlock(pcilib_lock_t *lock) {
+ int err;
+
+ if (!lock)
+ return;
+
+ if ((err = pthread_mutex_unlock(&lock->mutex)) != 0)
+ pcilib_error("Can't unlock mutex, errno %i", err);
+}
diff --git a/pcilib/lock.h b/pcilib/lock.h
index ec69737..8e1017a 100644
--- a/pcilib/lock.h
+++ b/pcilib/lock.h
@@ -4,42 +4,41 @@
* @brief this file is the header file for the functions that implement a semaphore API for the pcitool program, using pthread robust mutexes.
* @details the use of pthread robust mutexes was chosen due to the fact we privilege security over fastness, and that pthread mutexes permits to recover semaphores even with crash ,and that it does not require access to resources that can be easily accessible from extern usage as flock file locking mechanism. A possible other locking mechanism could be the sysv semaphores, but we have a problem of how determine a perfect hash for the init function, and more, benchmarks proves that sysv semaphore aren't that stable and that with more than 10 locks/unlocks, pthread is better in performance, so that should suits more to the final pcitool program.
* We considered that mutex implmentation is enough compared to a reader/writer implementation. If it should change, please go to sysv semaphore.
- * Basic explanation on how semaphores here work: a semaphore here is a positive integer, thus that can't go below zero, which is initiated with a value. when a process want access to the critical resource, it asks to decrement the value of the semaphore, and when it has finished, it reincrements it.basically, when the semaphore is equal to zero, any process must have to wait for it to be reincremented before decrementing it again. Here are defined two types of access to the semaphore corresponding to the reader/writer problem : an exclusive lock, which means that no other process than the one who have the resource can access it; a shared lock, which means that other processes who want to access to the resource with a shared lock can have the access, but a concurrent process who want to access the semaphore with an exclusive lock won't be able to.
+ * Basic explanation on how semaphores here work: a semaphore here is a positive integer, thus that can't go below zero, which is initiated with a value. when a process want access to the critical resource, it asks to decrement the value of the semaphore, and when it has finished, it reincrements it.basically, when the semaphore is equal to zero, any process must have to wait for it to be reincremented before decrementing it again. Here are defined two types of access to the semaphore corresponding to the reader/writer problem : an exclusive lock, which means that no other process than the one who have the resource can access it; a shared lock, which means that other processes who want to access to the resource with a shared lock can have the access, but a concurrent process who want to access the semaphore with an exclusive lock won't be able to.
* explanation on locks here : here locks are registered in kernel memory, where they are defined by a pthread_mutex_t and a name, which corresponds to a register or processus. The iterations like searching a lock are done on names.
*/
-#ifndef _LOCK_
-#define _LOCK_
+#ifndef _PCILIB_LOCK_H
+#define _PCILIB_LOCK_H
-#include "lock_global.h"
+#define PCILIB_LOCK_SIZE 128 /**< size of one lock, determine so the size of the protocol_name in the way locks are registered. 40 bytes are necessary for the mutex structure, so we have a protocol name of length LOCK_SIZE-40*/
+
+#include <pcilib.h>
/**
* type that defines possible flags when locking a lock by calling pcilib_lock
*/
-typedef enum{
- MUTEX_LOCK=1,
- MUTEX_TRYLOCK=2,
- MUTEX_TIMEDLOCK=4
+typedef enum {
+ PCILIB_LOCK_FLAGS_DEFAULT = 0, /**< Default flags */
+ PCILIB_LOCK_FLAG_UNLOCKED = 1, /**< Perform operation unlocked (protected by global flock during initialization of locking subsystem) */
+ PCILIB_LOCK_FLAG_PERSISTENT = 2 /**< Do not create robust mutexes, but preserve the lock across application launches */
} pcilib_lock_flags_t;
-/**
- * type that defines flags for the pcilib_init function
- */
-typedef enum{
- PCILIB_LOCK=0,
- PCILIB_NO_LOCK=16
-}pcilib_lock_init_flags_t;
+typedef struct pcilib_lock_s pcilib_lock_t;
+#ifdef __cplusplus
+extern "C" {
+#endif
/**
- * this function initialize a new semaphore in the kernel given a name that corresponds to a specific processus if the semaphore is not already initialized given the name that permits to differentiate semaphores, and then return the integer that points to the semaphore that have been initialized or to a previously already initialized semaphore.
- * @param[in] ctx the pcilib_t running
- * @param [in] flag the flag that let(s us to know if we have to lock during the creation of a new lock
- * @param[in] lock_id the format string that will contains the supplement args
- *@return a mutex for the given protocol name
+ * this function initialize a new semaphore in the kernel given a name that corresponds to a specific processus if the semaphore is not already initialized given the name that permits to differentiate semaphores, and then return the integer that points to the semaphore that have been initialized or to a previously already initialized semaphore.
+ * @param[in] lock - pointer to lock to initialize
+ * @param[in] flags - flags
+ * @param[in] lock_id - lock identificator
+ * @return error code or 0 on success
*/
-pcilib_lock_t* pcilib_init_lock(pcilib_t *ctx, pcilib_lock_init_flags_t flag,char* lock_id, ...);
+int pcilib_init_lock(pcilib_lock_t *lock, pcilib_lock_flags_t flags, const char *lock_id);
/**
* this function uninitialize a lock in kernel memory and set the corresponding name to 0
@@ -47,18 +46,54 @@ pcilib_lock_t* pcilib_init_lock(pcilib_t *ctx, pcilib_lock_init_flags_t flag,cha
*/
void pcilib_free_lock(pcilib_lock_t *lock_ctx);
+
+const char *pcilib_lock_get_name(pcilib_lock_t *lock);
+
+
+/**
+ * Increment reference count. Not thread/process safe unless system supports stdatomic (gcc 4.9+).
+ * In this case, the access should be synchronized by the caller
+ * @param[in] lock - pointer to initialized lock
+ */
+void pcilib_lock_ref(pcilib_lock_t *lock);
+
+/**
+ * Decrement reference count. Not thread/process safe unless system supports stdatomic (gcc 4.9+).
+ * In this case, the access should be synchronized by the caller
+ * @param[in] lock - pointer to initialized lock
+ */
+void pcilib_lock_unref(pcilib_lock_t *lock);
+
+/**
+ * Return _approximate_ number of lock references. The crashed applications will may not unref.
+ * @param[in] lock - pointer to initialized lock
+ */
+size_t pcilib_lock_get_refs(pcilib_lock_t *lock);
+
+pcilib_lock_flags_t pcilib_lock_get_flags(pcilib_lock_t *lock);
+
/**
- * this function will take a lock for the mutex pointed by lock_ctx
- * @param[in] lock_ctx the pointer to the mutex
- * @param[in] lock_flags define the type of lock wanted : MUTEX_LOCK (normal lock), MUTEX_TRYLOCK (will return if it can't acquire lock), MUTEX_TIMEDLOCK (will wait if it can't acquire lock for a given time, after it returns) and in this last case, we need to provide a timespec structure as supplementary argument
- *
+ * this function will take a lock for the mutex pointed by lock
+ * @param[in] lock the pointer to the mutex
+ * @param[in] flags define the type of lock wanted
+ * @param[in] timeout defines timeout
*/
-void pcilib_lock(pcilib_lock_t* lock_ctx,pcilib_lock_flags_t flags, ...);
+int pcilib_lock_custom(pcilib_lock_t* lock, pcilib_lock_flags_t flags, pcilib_timeout_t timeout);
/**
- * this function will unlock the lock pointed by lock_ctx
- * @param[in] lock_ctx the integer that points to the semaphore
+ * this function will take a lock for the mutex pointed by lock
+ * @param[in] lock the pointer to the mutex
*/
-void pcilib_unlock(pcilib_lock_t* lock_ctx);
+int pcilib_lock(pcilib_lock_t* lock);
+
+/**
+ * this function will unlock the lock pointed by lock
+ * @param[in] lock the integer that points to the semaphore
+ */
+void pcilib_unlock(pcilib_lock_t* lock);
+
+#ifdef __cplusplus
+}
+#endif
-#endif /*_LOCK_*/
+#endif /* _PCILIB_LOCK_H */
diff --git a/pcilib/locking.c b/pcilib/locking.c
index cbe9533..ffd05ce 100644
--- a/pcilib/locking.c
+++ b/pcilib/locking.c
@@ -1,81 +1,314 @@
#define _XOPEN_SOURCE 700
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <assert.h>
+#include <sys/file.h>
+#include "locking.h"
#include "error.h"
#include "pci.h"
-#include <sys/file.h>
-#include <string.h>
-#include "lock.h"
+#include "kmem.h"
/*
- * this function destroys all locks without touching at kernel memory directly
+ * this function allocates the kernel memory for the locks for software registers
*/
-void pcilib_clean_all_locks(pcilib_t* ctx){
-int i,j;
-void* addr;
- i=0;
- /* iteration on all locks*/
- while(i<PCILIB_NUMBER_OF_LOCK_PAGES){
-addr=pcilib_kmem_get_block_ua(ctx,ctx->locks_handle,i);
-for(j=0;j<PCILIB_LOCKS_PER_PAGE;j++)
- pcilib_free_lock((pcilib_lock_t*)(addr+i*PCILIB_LOCK_SIZE));
- i++;
- }
+int pcilib_init_locking(pcilib_t* ctx) {
+ int i;
+ int err;
+ pcilib_kmem_reuse_state_t reused;
+
+ assert(PCILIB_LOCK_PAGES * PCILIB_KMEM_PAGE_SIZE >= PCILIB_MAX_LOCKS * PCILIB_LOCK_SIZE);
+
+ err = pcilib_lock_global(ctx);
+ if (err) return err;
+
+ ctx->locks.kmem = pcilib_alloc_kernel_memory(ctx, PCILIB_KMEM_TYPE_PAGE, PCILIB_LOCK_PAGES, PCILIB_KMEM_PAGE_SIZE, 0, PCILIB_KMEM_USE(PCILIB_KMEM_USE_LOCKS,0), PCILIB_KMEM_FLAG_REUSE|PCILIB_KMEM_FLAG_PERSISTENT);
+ if (!ctx->locks.kmem) {
+ pcilib_unlock_global(ctx);
+ pcilib_error("Allocation of kernel memory for locking subsystem has failed");
+ return PCILIB_ERROR_FAILED;
+ }
+
+ reused = pcilib_kmem_is_reused(ctx, ctx->locks.kmem);
+ if (reused & PCILIB_KMEM_REUSE_PARTIAL) {
+ pcilib_unlock_global(ctx);
+ pcilib_error("Inconsistent kernel memory for locking subsystem is found (only part of the required buffers is available)");
+ return PCILIB_ERROR_INVALID_STATE;
+ }
+ if ((reused & PCILIB_KMEM_REUSE_REUSED) == 0) {
+ for (i = 0; i < PCILIB_LOCK_PAGES; i++) {
+ void *addr = pcilib_kmem_get_block_ua(ctx, ctx->locks.kmem, i);
+ memset(addr, 0, PCILIB_KMEM_PAGE_SIZE);
+ }
+ }
+
+ ctx->locks.locking = pcilib_get_lock(ctx, PCILIB_LOCK_FLAG_UNLOCKED, "locking");
+ ctx->locks.mmap = pcilib_get_lock(ctx, PCILIB_LOCK_FLAG_UNLOCKED, "mmap");
+
+ pcilib_unlock_global(ctx);
+
+ if ((!ctx->locks.locking)||(!ctx->locks.mmap)) {
+ pcilib_error("Locking subsystem has failed to initialized mandatory global locks");
+ return PCILIB_ERROR_FAILED;
+ }
+
+ return 0;
}
/*
* this functions destroy all locks and then free the kernel memory allocated for them
*/
-void pcilib_free_locking(pcilib_t* ctx){
- pcilib_clean_all_locks(ctx);
- pcilib_free_kernel_memory(ctx,ctx->locks_handle,PCILIB_KMEM_FLAG_REUSE);
+void pcilib_free_locking(pcilib_t *ctx) {
+ if (ctx->locks.mmap)
+ pcilib_return_lock(ctx, PCILIB_LOCK_FLAGS_DEFAULT, ctx->locks.mmap);
+
+ if (ctx->locks.locking)
+ pcilib_return_lock(ctx, PCILIB_LOCK_FLAGS_DEFAULT, ctx->locks.locking);
+
+ if (ctx->locks.kmem) {
+ pcilib_free_kernel_memory(ctx, ctx->locks.kmem, PCILIB_KMEM_FLAG_REUSE);
+ }
+
+ memset(&ctx->locks, 0, sizeof(pcilib_locking_t));
}
-/*
- * this function allocates the kernel memory for the locks for software registers
- */
-int pcilib_init_locking(pcilib_t* ctx, ...){
-/*for future possible more args
- va_list pa;
- va_start(pa,ctx);*/
- pcilib_kmem_handle_t *handle;
- int err;
- pcilib_kmem_reuse_state_t reused;
+int pcilib_lock_global(pcilib_t *ctx) {
+ int err;
+
+ /* we flock() to make sure to not have two initialization in the same time (possible long time to init) */
+ if ((err = flock(ctx->handle, LOCK_EX))==-1) {
+ pcilib_error("Can't get flock on device file");
+ return PCILIB_ERROR_FAILED;
+ }
+
+ return 0;
+}
+
+void pcilib_unlock_global(pcilib_t *ctx) {
+ if (flock(ctx->handle, LOCK_UN) == -1)
+ pcilib_warning("Could not correctly remove lock from the device file");
+}
+
+pcilib_lock_t *pcilib_get_lock_by_id(pcilib_t *ctx, pcilib_lock_id_t id) {
+ int page = id / PCILIB_LOCKS_PER_PAGE;
+ int offset = id - page * PCILIB_LOCKS_PER_PAGE;
+ void *addr = pcilib_kmem_get_block_ua(ctx, ctx->locks.kmem, page);
+ pcilib_lock_t *lock = (pcilib_lock_t*)(addr + offset * PCILIB_LOCK_SIZE);
+
+ return lock;
+}
+
+pcilib_lock_t *pcilib_get_lock(pcilib_t *ctx, pcilib_lock_flags_t flags, const char *lock_id, ...) {
+ pcilib_lock_id_t i;
+ int err, ret;
+
+ pcilib_lock_t *lock;
+ char buffer[PCILIB_LOCK_SIZE];
+
+
+ va_list pa;
+ va_start(pa, lock_id);
+ ret = vsnprintf(buffer, PCILIB_LOCK_SIZE, lock_id, pa);
+ va_end(pa);
+
+ if (ret < 0) {
+ pcilib_error("Failed to construct the lock id, probably arguments does not match the format string (%s)...", lock_id);
+ return NULL;
+ }
+
+ // Would be nice to have hash here
+ for (i = 0; i < PCILIB_MAX_LOCKS; i++) {
+ lock = pcilib_get_lock_by_id(ctx, i);
+
+ const char *name = pcilib_lock_get_name(lock);
+ if (!name) break;
- /* we flock() to make sure to not have two initialization in the same time (possible long time to init)*/
- if((err=flock(ctx->handle,LOCK_EX))==-1) pcilib_warning("can't get flock on /dev/fpga0");
-
- handle=pcilib_alloc_kernel_memory(ctx,PCILIB_KMEM_TYPE_PAGE,PCILIB_NUMBER_OF_LOCK_PAGES,PCILIB_KMEM_PAGE_SIZE,0,PCILIB_KMEM_USE(PCILIB_KMEM_USE_MUTEXES,0),PCILIB_KMEM_FLAG_REUSE|PCILIB_KMEM_FLAG_PERSISTENT);
-
- if (!handle) {
- pcilib_error("Allocation of kernel memory for mutexes has failed");
- return 1;
- }
-
- ctx->locks_handle=handle;
- reused = pcilib_kmem_is_reused(ctx, handle);
-//#define DEBUG_REUSE
-#ifdef DEBUG_REUSE
-reused=0;
-#endif
-/* verification about the handling memory got, first use or not, and integrity*/
- if ((reused & PCILIB_KMEM_REUSE_REUSED) == 0) {
- pcilib_register_t i;
-
- if (reused & PCILIB_KMEM_REUSE_PARTIAL) {
- pcilib_error("Inconsistent memory for locks was found (only part of required buffers is available)");
- pcilib_clean_all_locks(ctx);
- return 1;
+ if (!strcmp(buffer, name)) {
+ if ((pcilib_lock_get_flags(lock)&PCILIB_LOCK_FLAG_PERSISTENT) != (flags&PCILIB_LOCK_FLAG_PERSISTENT)) {
+ if (flags&PCILIB_LOCK_FLAG_PERSISTENT)
+ pcilib_error("Requesting persistent lock (%s), but requested lock is already existing and is robust", name);
+ else
+ pcilib_error("Requesting robust lock (%s), but requested lock is already existing and is persistent", name);
+ return NULL;
}
- /* if we get here so this is the first initialization (after some free or new), we so set kernel pages to 0 and initialize then the first lock that will be used when we create other locks*/
- for(i=0;i<PCILIB_NUMBER_OF_LOCK_PAGES;i++){
- memset(pcilib_kmem_get_block_ua(ctx,handle,i),0,PCILIB_KMEM_PAGE_SIZE);
- }
- pcilib_init_lock(ctx,PCILIB_NO_LOCK,"%s","pcilib_first_locking");
+
+#ifndef HAVE_STDATOMIC_H
+ if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0) {
+ err = pcilib_lock(ctx->locks.locking);
+ if (err) {
+ pcilib_error("Error (%i) obtaining global lock", err);
+ return NULL;
+ }
+ }
+#endif /* ! HAVE_STDATOMIC_H */
+ pcilib_lock_ref(lock);
+#ifndef HAVE_STDATOMIC_H
+ if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0)
+ pcilib_unlock(ctx->locks.locking);
+#endif /* ! HAVE_STDATOMIC_H */
+
+ return lock;
+ }
+ }
+
+ if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0) {
+ err = pcilib_lock(ctx->locks.locking);
+ if (err) {
+ pcilib_error("Error (%i) obtaining global lock", err);
+ return NULL;
+ }
+ }
+
+ // Make sure it was not allocated meanwhile
+ for (; i < PCILIB_MAX_LOCKS; i++) {
+ lock = pcilib_get_lock_by_id(ctx, i);
+
+ const char *name = pcilib_lock_get_name(lock);
+ if (!name) break;
+
+ if (!strcmp(buffer, name)) {
+ if ((pcilib_lock_get_flags(lock)&PCILIB_LOCK_FLAG_PERSISTENT) != (flags&PCILIB_LOCK_FLAG_PERSISTENT)) {
+ if (flags&PCILIB_LOCK_FLAG_PERSISTENT)
+ pcilib_error("Requesting persistent lock (%s), but requested lock is already existing and is robust", name);
+ else
+ pcilib_error("Requesting robust lock (%s), but requested lock is already existing and is persistent", name);
+
+ if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0)
+ pcilib_unlock(ctx->locks.locking);
+ return NULL;
+ }
+
+ pcilib_lock_ref(lock);
+ if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0)
+ pcilib_unlock(ctx->locks.locking);
+ return lock;
+ }
+ }
+
+ if (i == PCILIB_MAX_LOCKS) {
+ if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0)
+ pcilib_unlock(ctx->locks.locking);
+ pcilib_error("Failed to create lock (%s), only %u locks is supported", buffer, PCILIB_MAX_LOCKS);
+ return NULL;
+ }
+
+ err = pcilib_init_lock(lock, flags, buffer);
+
+ if (err) {
+ pcilib_error("Lock initialization failed with error %i", err);
+
+ if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0)
+ pcilib_unlock(ctx->locks.locking);
+
+ return NULL;
+ }
+
+ if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0)
+ pcilib_unlock(ctx->locks.locking);
+
+ return lock;
+}
+
+void pcilib_return_lock(pcilib_t *ctx, pcilib_lock_flags_t flags, pcilib_lock_t *lock) {
+#ifndef HAVE_STDATOMIC_H
+ int err;
+ if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0) {
+ err = pcilib_lock(ctx->locks.locking);
+ if (err) {
+ pcilib_error("Error (%i) obtaining global lock", err);
+ return;
+ }
}
-
- if((err=flock(ctx->handle,LOCK_UN))==-1) pcilib_warning("could not remove lock correctly on /dev/fpga0");
+#endif /* ! HAVE_STDATOMIC_H */
+ pcilib_lock_unref(lock);
+#ifndef HAVE_STDATOMIC_H
+ if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0)
+ pcilib_unlock(ctx->locks.locking);
+#endif /* ! HAVE_STDATOMIC_H */
+}
+
+/**
+ * Destroy all existing locks. This is unsafe call as this and other running applications
+ * will still have all initialized lock pointers. It is user responsibility to issue this
+ * command when no other application is running.
+ */
+int pcilib_destroy_all_locks(pcilib_t *ctx, int force) {
+ int err;
+ pcilib_lock_id_t i;
+ pcilib_kmem_reuse_state_t reused;
+
+ if (strcasecmp(ctx->model, "maintenance")) {
+ pcilib_error("Can't destroy locks while locking subsystem is initialized, use maintenance model");
+ return PCILIB_ERROR_INVALID_STATE;
+ }
+
+ err = pcilib_lock_global(ctx);
+ if (err) return err;
+
+ // ToDo: We should check here that no other instances of pcitool are running, the driver can provide this information
+
+ ctx->locks.kmem = pcilib_alloc_kernel_memory(ctx, PCILIB_KMEM_TYPE_PAGE, PCILIB_LOCK_PAGES, PCILIB_KMEM_PAGE_SIZE, 0, PCILIB_KMEM_USE(PCILIB_KMEM_USE_LOCKS,0), PCILIB_KMEM_FLAG_REUSE|PCILIB_KMEM_FLAG_PERSISTENT);
+ if (!ctx->locks.kmem) {
+ pcilib_unlock_global(ctx);
+ pcilib_error("Failed to allocate kernel memory of locking subsystem");
+ return PCILIB_ERROR_FAILED;
+ }
+
+ reused = pcilib_kmem_is_reused(ctx, ctx->locks.kmem);
+ if (reused & PCILIB_KMEM_REUSE_PARTIAL) {
+ pcilib_unlock_global(ctx);
+ pcilib_error("Inconsistent kernel memory for locking subsystem is found (only part of the required buffers is available)");
+ return PCILIB_ERROR_INVALID_STATE;
+ }
+
+ if ((reused & PCILIB_KMEM_REUSE_REUSED) == 0) {
+ pcilib_free_kernel_memory(ctx, ctx->locks.kmem, PCILIB_KMEM_FLAG_REUSE|PCILIB_KMEM_FLAG_PERSISTENT);
+ pcilib_unlock_global(ctx);
return 0;
+ }
+
+ if (!force) {
+ for (i = 0; i < PCILIB_MAX_LOCKS; i++) {
+ pcilib_lock_t *lock = pcilib_get_lock_by_id(ctx, i);
+
+ const char *name = pcilib_lock_get_name(lock);
+ if (!name) break;
+
+ size_t refs = pcilib_lock_get_refs(lock);
+
+ if (refs > 0) {
+ char *stmp = strdup(name);
+ pcilib_free_locking(ctx);
+ pcilib_unlock_global(ctx);
+ pcilib_error("Lock (%s) has %zu references, destroying references may result in crashes and data corruption", stmp, refs);
+ free(stmp);
+ return PCILIB_ERROR_BUSY;
+ }
+ }
+ }
+
+ // Do we really need this? I guess zeroing should be enough
+ for (i = 0; i < PCILIB_MAX_LOCKS; i++) {
+ pcilib_lock_t *lock = pcilib_get_lock_by_id(ctx, i);
+
+ const char *name = pcilib_lock_get_name(lock);
+ if (!name) break;
+
+ pcilib_free_lock(lock);
+ }
+
+ for (i = 0; i < PCILIB_LOCK_PAGES; i++) {
+ void *addr = pcilib_kmem_get_block_ua(ctx, ctx->locks.kmem, i);
+ memset(addr, 0, PCILIB_KMEM_PAGE_SIZE);
+ }
+
+ pcilib_free_locking(ctx);
+ pcilib_unlock_global(ctx);
+
+ return 0;
}
diff --git a/pcilib/locking.h b/pcilib/locking.h
index 3727380..ae2f368 100644
--- a/pcilib/locking.h
+++ b/pcilib/locking.h
@@ -3,47 +3,46 @@
* @brief this file is the header file for functions that touch all locks allocated for software registers.
* @details for more details about implementation choice, please read the file lock.h
*/
-#define _XOPEN_SOURCE 700
+#ifndef _PCILIB_LOCKING_H
+#define _PCILIB_LOCKING_H
-#ifndef _LOCKING_
-#define _LOCKING_
+#define PCILIB_MAX_LOCKS 64 /**< number of maximum locks*/
+#define PCILIB_LOCKS_PER_PAGE (PCILIB_KMEM_PAGE_SIZE/PCILIB_LOCK_SIZE) /**< number of locks per page of kernel memory */
+#define PCILIB_LOCK_PAGES ((PCILIB_MAX_LOCKS * PCILIB_LOCK_SIZE)/PCILIB_KMEM_PAGE_SIZE) /**< number of pages allocated for locks in kernel memory */
-#include <pthread.h>
-/** number of maximum locks*/
-#define PCILIB_MAX_NUMBER_LOCKS 64
+#include <pcilib/kmem.h>
+#include <pcilib/lock.h>
-/**size of one lock, determine so the size of the protocol_name in the way locks are registered. 40 bytes are necessary for the mutex structure, so we have a protocol name of length LOCK_SIZE-40*/
-#define PCILIB_LOCK_SIZE 128
+typedef uint32_t pcilib_lock_id_t;
-/** number of locks per page of kernel memory*/
-#define PCILIB_LOCKS_PER_PAGE PCILIB_KMEM_PAGE_SIZE/PCILIB_LOCK_SIZE
+typedef struct pcilib_locking_s pcilib_locking_t;
+struct pcilib_locking_s {
+ pcilib_kmem_handle_t *kmem; /**< kmem used to store mutexes */
+ pcilib_lock_t *locking; /**< lock used while intializing other locks */
+ pcilib_lock_t *mmap; /**< lock used to protect mmap operation */
+};
-/** number of pages allocated for locks in kernel memory*/
-#define PCILIB_NUMBER_OF_LOCK_PAGES (PCILIB_MAX_NUMBER_LOCKS*PCILIB_LOCK_SIZE)/PCILIB_KMEM_PAGE_SIZE
+#ifdef __cplusplus
+extern "C" {
+#endif
+int pcilib_init_locking(pcilib_t *ctx);
+void pcilib_free_locking(pcilib_t *ctx);
-/**
-* new type to define a semaphore. It was made to differentiate from the library type.
-*/
-typedef pthread_mutex_t pcilib_lock_t;
+int pcilib_lock_global(pcilib_t *ctx);
+void pcilib_unlock_global(pcilib_t *ctx);
-/**
- * this function destroy all locks created
- *@param[in] ctx, the pcilib_t running
- */
-void pcilib_clean_all_locks(pcilib_t* ctx);
+pcilib_lock_t *pcilib_get_lock_by_id(pcilib_t *ctx, pcilib_lock_id_t id);
-/**
-* this function initialize the kmem pages containing locks
-*@param[in] ctx the pcilib_t running
-*/
-int pcilib_init_locking(pcilib_t* ctx, ...);
+pcilib_lock_t *pcilib_get_lock(pcilib_t *ctx, pcilib_lock_flags_t flags, const char *lock_id, ...);
+void pcilib_return_lock(pcilib_t *ctx, pcilib_lock_flags_t flags, pcilib_lock_t *lock);
-/**
- * this function destroys all locks and then free the kernel memory allocated for them before
- * @param[in] ctx the pcilib_t running
- */
-void pcilib_free_all_locks(pcilib_t* ctx);
+int pcilib_destroy_all_locks(pcilib_t *ctx, int force);
+
+
+#ifdef __cplusplus
+}
+#endif
-#endif /* _LOCK_GLOBAL_ */
+#endif /* _PCILIB_LOCKING_H */
diff --git a/pcilib/pci.c b/pcilib/pci.c
index c7b86e8..512e891 100644
--- a/pcilib/pci.c
+++ b/pcilib/pci.c
@@ -120,6 +120,18 @@ pcilib_t *pcilib_open(const char *device, const char *model) {
}
ctx->page_mask = (uintptr_t)-1;
+
+ if ((model)&&(!strcasecmp(model, "maintenance"))) {
+ ctx->model = strdup("maintenance");
+ return ctx;
+ }
+
+ err = pcilib_init_locking(ctx);
+ if (err) {
+ pcilib_error("Error (%i) initializing locking subsystem", err);
+ pcilib_close(ctx);
+ return NULL;
+ }
ctx->alloc_reg = PCILIB_DEFAULT_REGISTER_SPACE;
ctx->registers = (pcilib_register_description_t *)malloc(PCILIB_DEFAULT_REGISTER_SPACE * sizeof(pcilib_register_description_t));
@@ -161,13 +173,6 @@ pcilib_t *pcilib_open(const char *device, const char *model) {
ctx->model_info.protocols = ctx->protocols;
ctx->model_info.ranges = ctx->ranges;
- err=pcilib_init_locking(ctx);
- if (err) {
- pcilib_error("Error (%i) initializing locking\n", err);
- pcilib_close(ctx);
- return NULL;
- }
-
err = pcilib_init_register_banks(ctx);
if (err) {
pcilib_error("Error (%i) initializing regiser banks\n", err);
@@ -343,7 +348,10 @@ void pcilib_close(pcilib_t *ctx) {
if (ctx->event_plugin)
pcilib_plugin_close(ctx->event_plugin);
-
+
+ if (ctx->locks.kmem)
+ pcilib_free_locking(ctx);
+
if (ctx->kmem_list) {
pcilib_warning("Not all kernel buffers are properly cleaned");
@@ -368,7 +376,7 @@ void pcilib_close(pcilib_t *ctx) {
if (ctx->model)
free(ctx->model);
-
+
if (ctx->handle >= 0)
close(ctx->handle);
diff --git a/pcilib/pci.h b/pcilib/pci.h
index a7b3d50..c97f753 100644
--- a/pcilib/pci.h
+++ b/pcilib/pci.h
@@ -24,6 +24,7 @@
#include "event.h"
#include "model.h"
#include "export.h"
+#include "locking.h"
typedef struct {
uint8_t max_link_speed, link_speed;
@@ -69,7 +70,9 @@ struct pcilib_s {
pcilib_register_bank_context_t *bank_ctx[PCILIB_MAX_REGISTER_BANKS]; /**< Contexts for registers banks if required by register protocol */
pcilib_dma_context_t *dma_ctx; /**< DMA context */
pcilib_context_t *event_ctx; /**< Implmentation context */
- void* locks_handle; /**< adress of the kernel memory use for locks from user space*/
+
+ struct pcilib_locking_s locks; /**< Context of locking subsystem */
+
#ifdef PCILIB_FILE_IO
int file_io_handle;
#endif /* PCILIB_FILE_IO */
diff --git a/pcilib/pcilib.h b/pcilib/pcilib.h
index 8a43bfb..01f3324 100644
--- a/pcilib/pcilib.h
+++ b/pcilib/pcilib.h
@@ -63,6 +63,17 @@ typedef enum {
} pcilib_dma_flags_t;
typedef enum {
+ PCILIB_STREAMING_STOP = 0, /**< stop streaming */
+ PCILIB_STREAMING_CONTINUE = 1, /**< wait the default DMA timeout for a new data */
+ PCILIB_STREAMING_WAIT = 2, /**< wait the specified timeout for a new data */
+ PCILIB_STREAMING_CHECK = 3, /**< do not wait for the data, bail out imideatly if no data ready */
+ PCILIB_STREAMING_FAIL = 4, /**< fail if data is not available on timeout */
+ PCILIB_STREAMING_REQ_FRAGMENT = 5, /**< only fragment of a packet is read, wait for next fragment and fail if no data during DMA timeout */
+ PCILIB_STREAMING_REQ_PACKET = 6, /**< wait for next packet and fail if no data during the specified timeout */
+ PCILIB_STREAMING_TIMEOUT_MASK = 3 /**< mask specifying all timeout modes */
+} pcilib_streaming_action_t;
+
+typedef enum {
PCILIB_EVENT_FLAGS_DEFAULT = 0,
PCILIB_EVENT_FLAG_RAW_DATA_ONLY = 1, /**< Do not parse data, just read raw and pass it to rawdata callback. If passed to rawdata callback, idicates the data is not identified as event (most probably just padding) */
PCILIB_EVENT_FLAG_STOP_ONLY = 1, /**< Do not cleanup, just stop acquiring new frames, the cleanup should be requested afterwards */