summaryrefslogtreecommitdiffstats
path: root/pcilib
diff options
context:
space:
mode:
authorzilio nicolas <nicolas.zilio@kit.edu>2015-08-26 14:45:18 +0200
committerzilio nicolas <nicolas.zilio@kit.edu>2015-08-26 14:45:18 +0200
commit1e5f0b6d5e02c0dc11bedefa92fc3c5bb406845a (patch)
treea978bd11f64f619ee8f40e42e73a582754c122de /pcilib
parent0f298bd861ac8d847f33d6b8bc73b089d2749bbb (diff)
parent6bad94bb8546a3a5595d340e7a2d809635e3bd5d (diff)
downloadpcitool-1e5f0b6d5e02c0dc11bedefa92fc3c5bb406845a.tar.gz
pcitool-1e5f0b6d5e02c0dc11bedefa92fc3c5bb406845a.tar.bz2
pcitool-1e5f0b6d5e02c0dc11bedefa92fc3c5bb406845a.tar.xz
pcitool-1e5f0b6d5e02c0dc11bedefa92fc3c5bb406845a.zip
pull from server
Diffstat (limited to 'pcilib')
-rw-r--r--pcilib/CMakeLists.txt9
-rw-r--r--pcilib/bar.c18
-rw-r--r--pcilib/config.h.in1
-rw-r--r--pcilib/dma.c62
-rw-r--r--pcilib/event.h11
-rw-r--r--pcilib/kmem.c12
-rw-r--r--pcilib/kmem.h4
-rw-r--r--pcilib/lock.c216
-rw-r--r--pcilib/lock.h106
-rw-r--r--pcilib/locking.c311
-rw-r--r--pcilib/locking.h48
-rw-r--r--pcilib/pci.c44
-rw-r--r--pcilib/pci.h7
-rw-r--r--pcilib/pcilib.h11
14 files changed, 828 insertions, 32 deletions
diff --git a/pcilib/CMakeLists.txt b/pcilib/CMakeLists.txt
index 881b44c..774ebe9 100644
--- a/pcilib/CMakeLists.txt
+++ b/pcilib/CMakeLists.txt
@@ -5,9 +5,10 @@ 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 xml.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 xml.c)
-target_link_libraries(pcilib dma protocols ${CMAKE_THREAD_LIBS_INIT} ${UFODECODE_LIBRARIES} ${CMAKE_DL_LIBS} ${XMLLIB_LIBRARIES} ${PYTHON_LIBRARIES})
+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} ${EXTRA_SYSTEM_LIBS} ${XMLLIB_LIBRARIES} ${PYTHON_LIBRARIES})
+
add_dependencies(pcilib dma protocols)
install(TARGETS pcilib
@@ -18,6 +19,6 @@ install(FILES pcilib.h
DESTINATION include
)
-install(FILES bar.h kmem.h bank.h register.h dma.h event.h model.h error.h debug.h env.h tools.h export.h version.h xml.h
+install(FILES bar.h kmem.h locking.h lock.h bank.h register.h dma.h event.h model.h error.h debug.h env.h tools.h export.h version.h
DESTINATION include/pcilib
)
diff --git a/pcilib/bar.c b/pcilib/bar.c
index 2c7a8d4..2666506 100644
--- a/pcilib/bar.c
+++ b/pcilib/bar.c
@@ -73,21 +73,29 @@ int pcilib_detect_address(pcilib_t *ctx, pcilib_bar_t *bar, uintptr_t *addr, siz
void *pcilib_map_bar(pcilib_t *ctx, pcilib_bar_t bar) {
void *res;
- int ret;
+ int err, ret;
const pcilib_board_info_t *board_info = pcilib_get_board_info(ctx);
if (!board_info) return NULL;
-
+
if (ctx->bar_space[bar]) return ctx->bar_space[bar];
-
+
+ err = pcilib_lock_global(ctx);
+ if (err) {
+ pcilib_error("Error (%i) acquiring mmap lock", err);
+ return NULL;
+ }
+
ret = ioctl( ctx->handle, PCIDRIVER_IOC_MMAP_MODE, PCIDRIVER_MMAP_PCI );
if (ret) {
+ pcilib_unlock_global(ctx);
pcilib_error("PCIDRIVER_IOC_MMAP_MODE ioctl have failed", bar);
return NULL;
}
ret = ioctl( ctx->handle, PCIDRIVER_IOC_MMAP_AREA, PCIDRIVER_BAR0 + bar );
if (ret) {
+ pcilib_unlock_global(ctx);
pcilib_error("PCIDRIVER_IOC_MMAP_AREA ioctl have failed for bank %i", bar);
return NULL;
}
@@ -98,12 +106,14 @@ void *pcilib_map_bar(pcilib_t *ctx, pcilib_bar_t bar) {
#else
res = mmap( 0, board_info->bar_length[bar], PROT_WRITE | PROT_READ, MAP_SHARED, ctx->handle, 0 );
#endif
+
+ pcilib_unlock_global(ctx);
+
if ((!res)||(res == MAP_FAILED)) {
pcilib_error("Failed to mmap data bank %i", bar);
return NULL;
}
-
return res;
}
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/dma.c b/pcilib/dma.c
index f6b8053..be02c21 100644
--- a/pcilib/dma.c
+++ b/pcilib/dma.c
@@ -61,8 +61,11 @@ pcilib_dma_engine_t pcilib_add_dma_engine(pcilib_t *ctx, pcilib_dma_engine_descr
int pcilib_init_dma(pcilib_t *ctx) {
int err;
pcilib_dma_context_t *dma_ctx = NULL;
+ const pcilib_dma_description_t *info = &ctx->dma;
const pcilib_model_description_t *model_info = pcilib_get_model_description(ctx);
+ pcilib_dma_engine_t dma;
+
if (ctx->dma_ctx)
return 0;
@@ -86,6 +89,24 @@ int pcilib_init_dma(pcilib_t *ctx) {
}
if (dma_ctx) {
+ for (dma = 0; info->engines[dma].addr_bits; dma++) {
+ if (info->engines[dma].direction&PCILIB_DMA_FROM_DEVICE) {
+ ctx->dma_rlock[dma] = pcilib_get_lock(ctx, PCILIB_LOCK_FLAGS_DEFAULT, "dma%ir/%s", info->engines[dma].addr, info->name);
+ if (!ctx->dma_rlock[dma]) break;
+ }
+ if (info->engines[dma].direction&PCILIB_DMA_TO_DEVICE) {
+ ctx->dma_wlock[dma] = pcilib_get_lock(ctx, PCILIB_LOCK_FLAGS_DEFAULT, "dma%iw/%s", info->engines[dma].addr, info->name);
+ if (!ctx->dma_wlock[dma]) break;
+ }
+ }
+
+ if (info->engines[dma].addr_bits) {
+ if (ctx->dma.api->free)
+ ctx->dma.api->free(dma_ctx);
+ pcilib_error("Failed to intialize DMA locks");
+ return PCILIB_ERROR_FAILED;
+ }
+
dma_ctx->pcilib = ctx;
// DS: parameters?
ctx->dma_ctx = dma_ctx;
@@ -105,11 +126,11 @@ int pcilib_start_dma(pcilib_t *ctx, pcilib_dma_engine_t dma, pcilib_dma_flags_t
pcilib_error("DMA Engine is not configured in the current model");
return PCILIB_ERROR_NOTAVAILABLE;
}
-
+
if (!info->api->start_dma) {
return 0;
}
-
+
return info->api->start_dma(ctx->dma_ctx, dma, flags);
}
@@ -201,6 +222,7 @@ static int pcilib_dma_skip_callback(void *arg, pcilib_dma_flags_t flags, size_t
}
int pcilib_stream_dma(pcilib_t *ctx, pcilib_dma_engine_t dma, uintptr_t addr, size_t size, pcilib_dma_flags_t flags, pcilib_timeout_t timeout, pcilib_dma_callback_t cb, void *cbattr) {
+ int err;
const pcilib_dma_description_t *info = pcilib_get_dma_description(ctx);
if (!info) {
pcilib_error("DMA is not supported by the device");
@@ -228,7 +250,21 @@ int pcilib_stream_dma(pcilib_t *ctx, pcilib_dma_engine_t dma, uintptr_t addr, si
return PCILIB_ERROR_NOTSUPPORTED;
}
- return info->api->stream(ctx->dma_ctx, dma, addr, size, flags, timeout, cb, cbattr);
+ err = pcilib_try_lock(ctx->dma_rlock[dma]);
+ if (err) {
+ if ((err == PCILIB_ERROR_BUSY)||(err == PCILIB_ERROR_TIMEOUT))
+ pcilib_error("DMA engine (%i) is busy", dma);
+ else
+ pcilib_error("Error (%i) locking DMA engine (%i)", err, dma);
+
+ return err;
+ }
+
+ err = info->api->stream(ctx->dma_ctx, dma, addr, size, flags, timeout, cb, cbattr);
+
+ pcilib_unlock(ctx->dma_rlock[dma]);
+
+ return err;
}
int pcilib_read_dma_custom(pcilib_t *ctx, pcilib_dma_engine_t dma, uintptr_t addr, size_t size, pcilib_dma_flags_t flags, pcilib_timeout_t timeout, void *buf, size_t *read_bytes) {
@@ -278,6 +314,8 @@ int pcilib_skip_dma(pcilib_t *ctx, pcilib_dma_engine_t dma) {
int pcilib_push_dma(pcilib_t *ctx, pcilib_dma_engine_t dma, uintptr_t addr, size_t size, pcilib_dma_flags_t flags, pcilib_timeout_t timeout, void *buf, size_t *written) {
+ int err;
+
const pcilib_dma_description_t *info = pcilib_get_dma_description(ctx);
if (!info) {
pcilib_error("DMA is not supported by the device");
@@ -304,8 +342,22 @@ int pcilib_push_dma(pcilib_t *ctx, pcilib_dma_engine_t dma, uintptr_t addr, size
pcilib_error("The selected engine (%i) is C2S-only and does not support writes", dma);
return PCILIB_ERROR_NOTSUPPORTED;
}
-
- return info->api->push(ctx->dma_ctx, dma, addr, size, flags, timeout, buf, written);
+
+ err = pcilib_try_lock(ctx->dma_wlock[dma]);
+ if (err) {
+ if (err == PCILIB_ERROR_BUSY)
+ pcilib_error("DMA engine (%i) is busy", dma);
+ else
+ pcilib_error("Error (%i) locking DMA engine (%i)", err, dma);
+
+ return err;
+ }
+
+ err = info->api->push(ctx->dma_ctx, dma, addr, size, flags, timeout, buf, written);
+
+ pcilib_unlock(ctx->dma_wlock[dma]);
+
+ return err;
}
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..b1d2c5c 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_global(ctx);
+ if (err) {
+ pcilib_error("Error (%i) acquiring mmap lock", err);
+ return NULL;
+ }
+
ret = ioctl( ctx->handle, PCIDRIVER_IOC_MMAP_MODE, PCIDRIVER_MMAP_KMEM );
if (ret) {
+ pcilib_unlock_global(ctx);
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_global(ctx);
+
+
//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 8299379..3dff625 100644
--- a/pcilib/kmem.h
+++ b/pcilib/kmem.h
@@ -14,7 +14,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,6 +32,7 @@ typedef enum {
PCILIB_KMEM_USE_DMA_RING = 1,
PCILIB_KMEM_USE_DMA_PAGES = 2,
PCILIB_KMEM_USE_SOFTWARE_REGISTERS = 3,
+ PCILIB_KMEM_USE_LOCKS = 4,
PCILIB_KMEM_USE_USER = 0x10
} pcilib_kmem_use_t;
diff --git a/pcilib/lock.c b/pcilib/lock.c
new file mode 100644
index 0000000..03a2377
--- /dev/null
+++ b/pcilib/lock.c
@@ -0,0 +1,216 @@
+#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"
+
+
+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
+ */
+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;
+ }
+ }
+
+ 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) {
+ 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) {
+ pcilib_error("The null lock pointer is passed to lock function");
+ return PCILIB_ERROR_INVALID_ARGUMENT;
+ }
+
+ 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;
+ }
+ return 0;
+ 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);
+}
+
+int pcilib_try_lock(pcilib_lock_t* lock) {
+ return pcilib_lock_custom(lock, PCILIB_LOCK_FLAGS_DEFAULT, PCILIB_TIMEOUT_IMMEDIATE);
+}
+
+/**
+ * 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) {
+ switch (err) {
+ case EPERM:
+ pcilib_error("Trying to unlock not locked mutex (%s) or the mutex which was locked by a different thread", lock->name);
+ break;
+ default:
+ pcilib_error("Can't unlock mutex, errno %i", err);
+ }
+ }
+}
diff --git a/pcilib/lock.h b/pcilib/lock.h
new file mode 100644
index 0000000..9ffe4cf
--- /dev/null
+++ b/pcilib/lock.h
@@ -0,0 +1,106 @@
+/**
+ * @file lock.h
+ * @skip author zilio nicolas, nicolas.zilio@hotmail.fr
+ * @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.
+ * 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 _PCILIB_LOCK_H
+#define _PCILIB_LOCK_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 {
+ 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;
+
+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] lock - pointer to lock to initialize
+ * @param[in] flags - flags
+ * @param[in] lock_id - lock identificator
+ * @return error code or 0 on success
+ */
+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
+ * @param[in] lock_ctx the pointer that points to the lock.
+ */
+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
+ * @param[in] lock the pointer to the mutex
+ * @param[in] flags define the type of lock wanted
+ * @param[in] timeout defines timeout
+ */
+int pcilib_lock_custom(pcilib_lock_t* lock, pcilib_lock_flags_t flags, pcilib_timeout_t timeout);
+
+/**
+ * this function will take a lock for the mutex pointed by lock
+ * @param[in] lock the pointer to the mutex
+ */
+int pcilib_lock(pcilib_lock_t* lock);
+
+/**
+ * this function will try to take a lock for the mutex pointed by lock
+ * @param[in] lock the pointer to the mutex
+ */
+int pcilib_try_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 /* _PCILIB_LOCK_H */
diff --git a/pcilib/locking.c b/pcilib/locking.c
new file mode 100644
index 0000000..f384ca4
--- /dev/null
+++ b/pcilib/locking.c
@@ -0,0 +1,311 @@
+#define _XOPEN_SOURCE 700
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <assert.h>
+#include <stdint.h>
+#include <sys/file.h>
+
+#include "locking.h"
+#include "error.h"
+#include "pci.h"
+#include "kmem.h"
+
+/*
+ * this function allocates the kernel memory for the locks for software registers
+ */
+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");
+
+ pcilib_unlock_global(ctx);
+
+ if ((!ctx->locks.locking)) {
+ 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) {
+ 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));
+}
+
+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;
+
+ 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;
+ }
+
+#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;
+ }
+ }
+#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
new file mode 100644
index 0000000..ccacd63
--- /dev/null
+++ b/pcilib/locking.h
@@ -0,0 +1,48 @@
+/**
+ * @file lock_global.h
+ * @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
+ */
+#ifndef _PCILIB_LOCKING_H
+#define _PCILIB_LOCKING_H
+
+#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 <pcilib/kmem.h>
+#include <pcilib/lock.h>
+
+typedef uint32_t pcilib_lock_id_t;
+
+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 */
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int pcilib_init_locking(pcilib_t *ctx);
+void pcilib_free_locking(pcilib_t *ctx);
+
+int pcilib_lock_global(pcilib_t *ctx);
+void pcilib_unlock_global(pcilib_t *ctx);
+
+pcilib_lock_t *pcilib_get_lock_by_id(pcilib_t *ctx, pcilib_lock_id_t id);
+
+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);
+
+int pcilib_destroy_all_locks(pcilib_t *ctx, int force);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PCILIB_LOCKING_H */
diff --git a/pcilib/pci.c b/pcilib/pci.c
index 76bd59f..f043257 100644
--- a/pcilib/pci.c
+++ b/pcilib/pci.c
@@ -1,4 +1,5 @@
//#define PCILIB_FILE_IO
+#define _XOPEN_SOURCE 700
#define _BSD_SOURCE
#define _POSIX_C_SOURCE 200809L
@@ -25,6 +26,7 @@
#include "plugin.h"
#include "bar.h"
#include "xml.h"
+#include "locking.h"
static int pcilib_detect_model(pcilib_t *ctx, const char *model) {
int i, j;
@@ -127,6 +129,9 @@ pcilib_t *pcilib_open(const char *device, const char *model) {
if(number_banks)banks=calloc((number_banks),sizeof(pcilib_register_bank_description_t));
else pcilib_error("no banks in the xml file");
+ if (!model)
+ model = getenv("PCILIB_MODEL");
+
if (ctx) {
memset(ctx, 0, sizeof(pcilib_t));
ctx->pci_cfg_space_fd = -1;
@@ -139,6 +144,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));
@@ -191,7 +208,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_register_banks(ctx);
if (err) {
pcilib_error("Error (%i) initializing regiser banks\n", err);
@@ -361,6 +377,7 @@ void pcilib_close(pcilib_t *ctx) {
pcilib_bar_t i;
if (ctx) {
+ pcilib_dma_engine_t dma;
const pcilib_model_description_t *model_info = pcilib_get_model_description(ctx);
const pcilib_event_api_description_t *eapi = model_info->api;
const pcilib_dma_api_description_t *dapi = ctx->dma.api;
@@ -368,6 +385,13 @@ void pcilib_close(pcilib_t *ctx) {
if ((eapi)&&(eapi->free)) eapi->free(ctx->event_ctx);
if ((dapi)&&(dapi->free)) dapi->free(ctx->dma_ctx);
+ for (dma = 0; dma < PCILIB_MAX_DMA_ENGINES; dma++) {
+ if (ctx->dma_rlock[dma])
+ pcilib_return_lock(ctx, PCILIB_LOCK_FLAGS_DEFAULT, ctx->dma_rlock[dma]);
+ if (ctx->dma_wlock[dma])
+ pcilib_return_lock(ctx, PCILIB_LOCK_FLAGS_DEFAULT, ctx->dma_wlock[dma]);
+ }
+
pcilib_free_register_banks(ctx);
if (ctx->register_ctx)
@@ -375,7 +399,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");
@@ -400,7 +427,7 @@ void pcilib_close(pcilib_t *ctx) {
if (ctx->model)
free(ctx->model);
-
+
if (ctx->handle >= 0)
close(ctx->handle);
@@ -438,11 +465,16 @@ static int pcilib_update_pci_configuration_space(pcilib_t *ctx) {
}
size = read(ctx->pci_cfg_space_fd, ctx->pci_cfg_space_cache, 256);
- if (size != 256) {
- pcilib_error("Failed to read PCI configuration from sysfs");
+ if (size < 64) {
+ if (size <= 0)
+ pcilib_error("Failed to read PCI configuration from sysfs, errno: %i", errno);
+ else
+ pcilib_error("Failed to read PCI configuration from sysfs, only %zu bytes read (expected at least 64)", size);
return PCILIB_ERROR_FAILED;
}
+ ctx->pci_cfg_space_size = size;
+
return 0;
}
@@ -465,7 +497,7 @@ static uint32_t *pcilib_get_pci_capabilities(pcilib_t *ctx, int cap_id) {
cap = ctx->pci_cfg_space_cache[(0x34>>2)];
cap_offset = cap&0xFC;
- while ((cap_offset)&&(cap_offset < 256)) {
+ while ((cap_offset)&&(cap_offset < ctx->pci_cfg_space_size)) {
cap = ctx->pci_cfg_space_cache[cap_offset>>2];
if ((cap&0xFF) == cap_id)
return &ctx->pci_cfg_space_cache[cap_offset>>2];
diff --git a/pcilib/pci.h b/pcilib/pci.h
index d176caf..340abd3 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;
@@ -41,6 +42,7 @@ struct pcilib_s {
int pci_cfg_space_fd; /**< File descriptor linking to PCI configuration space in sysfs */
uint32_t pci_cfg_space_cache[64]; /**< Cached PCI configuration space */
+ size_t pci_cfg_space_size; /**< Size of the cached PCI configuration space, sometimes not fully is available for unpriveledged user */
const uint32_t *pcie_capabilities; /**< PCI Capbility structure (just a pointer at appropriate place in the pci_cfg_space) */
int reg_bar_mapped; /**< Indicates that all BARs used to access registers are mapped */
@@ -70,6 +72,11 @@ struct pcilib_s {
pcilib_dma_context_t *dma_ctx; /**< DMA context */
pcilib_context_t *event_ctx; /**< Implmentation context */
+ pcilib_lock_t *dma_rlock[PCILIB_MAX_DMA_ENGINES]; /**< Per-engine locks to serialize streaming and read operations */
+ pcilib_lock_t *dma_wlock[PCILIB_MAX_DMA_ENGINES]; /**< Per-engine locks to serialize write operations */
+
+ 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 */