summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVasilii Chernov <vchernov@inr.ru>2016-03-02 10:28:04 +0100
committerVasilii Chernov <vchernov@inr.ru>2016-03-02 10:28:04 +0100
commit8e7c95957ee73d6c11ef28f7c0e2319a11103783 (patch)
tree83df220932b3d88e071eda4c756e485dd48d50e9
parent8719b84a95805d109e21c20f05a0164315e1b38a (diff)
parent867bddcf7be374221a04b7ae89f93a5f5d703ee6 (diff)
downloadpcitool-8e7c95957ee73d6c11ef28f7c0e2319a11103783.tar.gz
pcitool-8e7c95957ee73d6c11ef28f7c0e2319a11103783.tar.bz2
pcitool-8e7c95957ee73d6c11ef28f7c0e2319a11103783.tar.xz
pcitool-8e7c95957ee73d6c11ef28f7c0e2319a11103783.zip
Merge with http://ufo.kit.edu/ufo/log/csa/pcitool 362 revision
-rw-r--r--dma/ipe.c13
-rw-r--r--dma/nwl_engine.c4
-rw-r--r--dma/nwl_engine_buffers.h6
-rw-r--r--driver/Makefile2
-rw-r--r--driver/kmem.c6
-rw-r--r--driver/pciDriver.h3
-rw-r--r--driver/rdma.c53
-rw-r--r--driver/rdma.h9
-rw-r--r--pcilib/bank.h114
-rw-r--r--pcilib/bar.h14
-rw-r--r--pcilib/cpu.h15
-rw-r--r--pcilib/datacpy.h54
-rw-r--r--pcilib/kmem.c24
-rw-r--r--pcilib/kmem.h263
-rw-r--r--pcilib/memcpy.h52
-rw-r--r--pcilib/pci.c6
-rw-r--r--pcilib/pci.h6
-rw-r--r--pcilib/pcilib.h2
-rw-r--r--pcilib/plugin.h53
-rw-r--r--pcilib/timing.h55
-rw-r--r--pcilib/tools.h52
-rw-r--r--pcitool/cli.c8
22 files changed, 724 insertions, 90 deletions
diff --git a/dma/ipe.c b/dma/ipe.c
index 889c612..e1f7835 100644
--- a/dma/ipe.c
+++ b/dma/ipe.c
@@ -566,7 +566,8 @@ int dma_ipe_stream_read(pcilib_dma_context_t *vctx, pcilib_dma_engine_t dma, uin
if (ctx->version < 3) {
if (ctx->mode64) last_written_addr_ptr = desc_va + 3 * sizeof(uint32_t);
else last_written_addr_ptr = desc_va + 4 * sizeof(uint32_t);
- empty_detected_ptr = last_written_addr_ptr - 2;
+ empty_detected_ptr = NULL; // Not properly supported
+// empty_detected_ptr = last_written_addr_ptr - 2;
} else {
last_written_addr_ptr = desc_va + 2 * sizeof(uint32_t);
empty_detected_ptr = desc_va + sizeof(uint32_t);
@@ -589,10 +590,10 @@ int dma_ipe_stream_read(pcilib_dma_context_t *vctx, pcilib_dma_engine_t dma, uin
do {
switch (ret&PCILIB_STREAMING_TIMEOUT_MASK) {
- case PCILIB_STREAMING_CONTINUE:
+ case PCILIB_STREAMING_CONTINUE:
// Hardware indicates that there is no more data pending and we can safely stop if there is no data in the kernel buffers already
#ifdef IPEDMA_SUPPORT_EMPTY_DETECTED
- if (*empty_detected_ptr)
+ if ((empty_detected_ptr)&&(*empty_detected_ptr))
wait = 0;
else
#endif /* IPEDMA_SUPPORT_EMPTY_DETECTED */
@@ -618,7 +619,7 @@ int dma_ipe_stream_read(pcilib_dma_context_t *vctx, pcilib_dma_engine_t dma, uin
}
#ifdef IPEDMA_SUPPORT_EMPTY_DETECTED
- if ((ret != PCILIB_STREAMING_REQ_PACKET)&&(*empty_detected_ptr)) break;
+ if ((ret != PCILIB_STREAMING_REQ_PACKET)&&(empty_detected_ptr)&&(*empty_detected_ptr)) break;
#endif /* IPEDMA_SUPPORT_EMPTY_DETECTED */
gettimeofday(&cur, NULL);
}
@@ -627,7 +628,7 @@ int dma_ipe_stream_read(pcilib_dma_context_t *vctx, pcilib_dma_engine_t dma, uin
if ((ctx->last_read_addr == DEREF(last_written_addr_ptr))||(DEREF(last_written_addr_ptr) == 0)) {
#ifdef IPEDMA_SUPPORT_EMPTY_DETECTED
# ifdef PCILIB_DEBUG_DMA
- if ((wait)&&(DEREF(last_written_addr_ptr))&&(!*empty_detected_ptr))
+ if ((wait)&&(empty_detected_ptr)&&(DEREF(last_written_addr_ptr))&&(!*empty_detected_ptr))
pcilib_debug(DMA, "The empty_detected flag is not set, but no data arrived within %lu us", wait);
# endif /* PCILIB_DEBUG_DMA */
#endif /* IPEDMA_SUPPORT_EMPTY_DETECTED */
@@ -644,7 +645,7 @@ int dma_ipe_stream_read(pcilib_dma_context_t *vctx, pcilib_dma_engine_t dma, uin
);
#ifdef IPEDMA_DETECT_PACKETS
- if ((*empty_detected_ptr)&&(pcilib_kmem_get_block_ba(ctx->dmactx.pcilib, ctx->pages, cur_read) == DEREF(last_written_addr_ptr))) packet_flags = PCILIB_DMA_FLAG_EOP;
+ if ((empty_detected_ptr)&&(*empty_detected_ptr)&&(pcilib_kmem_get_block_ba(ctx->dmactx.pcilib, ctx->pages, cur_read) == DEREF(last_written_addr_ptr))) packet_flags = PCILIB_DMA_FLAG_EOP;
else packet_flags = 0;
#endif /* IPEDMA_DETECT_PACKETS */
diff --git a/dma/nwl_engine.c b/dma/nwl_engine.c
index c3dea49..b098943 100644
--- a/dma/nwl_engine.c
+++ b/dma/nwl_engine.c
@@ -124,7 +124,7 @@ int dma_nwl_start_engine(nwl_dma_t *ctx, pcilib_dma_engine_t dma) {
dma_nwl_acknowledge_irq((pcilib_dma_context_t*)ctx, PCILIB_DMA_IRQ, dma);
- ring_pa = pcilib_kmem_get_pa(ctx->dmactx.pcilib, ectx->ring);
+ ring_pa = pcilib_kmem_get_ba(ctx->dmactx.pcilib, ectx->ring);
nwl_write_register(ring_pa, ctx, ectx->base_addr, REG_DMA_ENG_NEXT_BD);
nwl_write_register(ring_pa, ctx, ectx->base_addr, REG_SW_NEXT_BD);
@@ -188,7 +188,7 @@ int dma_nwl_stop_engine(nwl_dma_t *ctx, pcilib_dma_engine_t dma) {
} while ((val & (DMA_ENG_RUNNING))&&(((cur.tv_sec - start.tv_sec)*1000000 + (cur.tv_usec - start.tv_usec)) < PCILIB_NWL_REGISTER_TIMEOUT));
if (ectx->ring) {
- ring_pa = pcilib_kmem_get_pa(ctx->dmactx.pcilib, ectx->ring);
+ ring_pa = pcilib_kmem_get_ba(ctx->dmactx.pcilib, ectx->ring);
nwl_write_register(ring_pa, ctx, ectx->base_addr, REG_DMA_ENG_NEXT_BD);
nwl_write_register(ring_pa, ctx, ectx->base_addr, REG_SW_NEXT_BD);
}
diff --git a/dma/nwl_engine_buffers.h b/dma/nwl_engine_buffers.h
index ef1c74f..0cc5343 100644
--- a/dma/nwl_engine_buffers.h
+++ b/dma/nwl_engine_buffers.h
@@ -121,7 +121,7 @@ static int dma_nwl_allocate_engine_buffers(nwl_dma_t *ctx, pcilib_nwl_engine_con
unsigned char *data = (unsigned char*)pcilib_kmem_get_ua(ctx->dmactx.pcilib, ring);
- uint32_t ring_pa = pcilib_kmem_get_pa(ctx->dmactx.pcilib, ring);
+ uint32_t ring_pa = pcilib_kmem_get_ba(ctx->dmactx.pcilib, ring);
if (preserve) {
if (ectx->desc->direction == PCILIB_DMA_FROM_DEVICE) err = dma_nwl_compute_read_c2s_pointers(ctx, ectx, data, ring_pa);
@@ -251,7 +251,7 @@ static int dma_nwl_push_buffer(nwl_dma_t *ctx, pcilib_nwl_engine_context_t *ectx
uint32_t val;
unsigned char *ring = pcilib_kmem_get_ua(ctx->dmactx.pcilib, ectx->ring);
- uint32_t ring_pa = pcilib_kmem_get_pa(ctx->dmactx.pcilib, ectx->ring);
+ uint32_t ring_pa = pcilib_kmem_get_ba(ctx->dmactx.pcilib, ectx->ring);
ring += ectx->head * PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
@@ -338,7 +338,7 @@ static int dma_nwl_return_buffer(nwl_dma_t *ctx, pcilib_nwl_engine_context_t *ec
uint32_t val;
unsigned char *ring = pcilib_kmem_get_ua(ctx->dmactx.pcilib, ectx->ring);
- uint32_t ring_pa = pcilib_kmem_get_pa(ctx->dmactx.pcilib, ectx->ring);
+ uint32_t ring_pa = pcilib_kmem_get_ba(ctx->dmactx.pcilib, ectx->ring);
size_t bufsz = pcilib_kmem_get_block_size(ctx->dmactx.pcilib, ectx->pages, ectx->tail);
ring += ectx->tail * PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
diff --git a/driver/Makefile b/driver/Makefile
index a783c3f..0a860bf 100644
--- a/driver/Makefile
+++ b/driver/Makefile
@@ -1,7 +1,7 @@
CONFIG_MODULE_SIG=n
obj-m := pciDriver.o
-pciDriver-objs := base.o int.o umem.o kmem.o sysfs.o ioctl.o compat.o
+pciDriver-objs := base.o int.o umem.o kmem.o sysfs.o ioctl.o compat.o rdma.o
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
INSTALLDIR ?= /lib/modules/$(shell uname -r)/kernel/extra
diff --git a/driver/kmem.c b/driver/kmem.c
index 805ace1..9bc1eb7 100644
--- a/driver/kmem.c
+++ b/driver/kmem.c
@@ -86,7 +86,8 @@ int pcidriver_kmem_alloc(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_han
kmem_handle->handle_id = kmem_entry->id;
- kmem_handle->pa = (unsigned long)(kmem_entry->dma_handle);
+ kmem_handle->ba = (unsigned long)(kmem_entry->dma_handle);
+ kmem_handle->pa = virt_to_phys((void*)kmem_entry->cpua);
kmem_handle->flags = KMEM_FLAG_REUSED;
if (kmem_entry->refs&KMEM_REF_HW) kmem_handle->flags |= KMEM_FLAG_REUSED_HW;
@@ -197,7 +198,8 @@ int pcidriver_kmem_alloc(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_han
kmem_entry->size = kmem_handle->size;
kmem_entry->cpua = (unsigned long)retptr;
- kmem_handle->pa = (unsigned long)(kmem_entry->dma_handle);
+ kmem_handle->ba = (unsigned long)(kmem_entry->dma_handle);
+ kmem_handle->pa = virt_to_phys(retptr);
kmem_entry->mode = 1;
if (kmem_handle->flags&KMEM_FLAG_REUSE) {
diff --git a/driver/pciDriver.h b/driver/pciDriver.h
index 5d6221e..371bd88 100644
--- a/driver/pciDriver.h
+++ b/driver/pciDriver.h
@@ -58,7 +58,7 @@
#include <linux/ioctl.h>
-#define PCIDRIVER_INTERFACE_VERSION 1 /**< Driver API version, only the pcilib with the same driver interface version is allowed */
+#define PCIDRIVER_INTERFACE_VERSION 2 /**< Driver API version, only the pcilib with the same driver interface version is allowed */
/* Identifies the PCI-E Xilinx ML605 */
#define PCIE_XILINX_VENDOR_ID 0x10ee
@@ -154,6 +154,7 @@ typedef struct {
typedef struct {
unsigned long type;
unsigned long pa;
+ unsigned long ba;
unsigned long size;
unsigned long align;
unsigned long use;
diff --git a/driver/rdma.c b/driver/rdma.c
new file mode 100644
index 0000000..22a4a5e
--- /dev/null
+++ b/driver/rdma.c
@@ -0,0 +1,53 @@
+#include <linux/version.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/wait.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/hugetlb.h>
+
+#include "rdma.h"
+
+static unsigned long pcidriver_follow_pte(struct mm_struct *mm, unsigned long address)
+{
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *pte;
+
+ spinlock_t *ptl;
+ unsigned long pfn = 0;
+
+
+ pgd = pgd_offset(mm, address);
+ if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
+ return 0;
+
+ pud = pud_offset(pgd, address);
+ if (pud_none(*pud) || unlikely(pud_bad(*pud)))
+ return 0;
+
+ pmd = pmd_offset(pud, address);
+ if (pmd_none(*pmd))
+ return 0;
+
+ pte = pte_offset_map_lock(mm, pmd, address, &ptl);
+ if (!pte_none(*pte))
+ pfn = (pte_pfn(*pte) << PAGE_SHIFT);
+ pte_unmap_unlock(pte, ptl);
+
+ return pfn;
+}
+
+unsigned long pcidriver_resolve_bar(unsigned long address) {
+ unsigned long pfn;
+
+ address = (address >> PAGE_SHIFT) << PAGE_SHIFT;
+ pfn = pcidriver_follow_pte(current->mm, address);
+
+ return pfn;
+}
+
+EXPORT_SYMBOL(pcidriver_resolve_bar);
diff --git a/driver/rdma.h b/driver/rdma.h
new file mode 100644
index 0000000..4aeda78
--- /dev/null
+++ b/driver/rdma.h
@@ -0,0 +1,9 @@
+#ifndef _PCIDRIVER_RDMA_H
+#define _PCIDRIVER_RDMA_H
+
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+
+extern unsigned long pcidriver_resolve_bar(unsigned long address);
+
+#endif /* _PCIDRIVER_RDMA_H */
diff --git a/pcilib/bank.h b/pcilib/bank.h
index e7be825..00ba688 100644
--- a/pcilib/bank.h
+++ b/pcilib/bank.h
@@ -171,21 +171,135 @@ int pcilib_add_register_protocols(pcilib_t *ctx, pcilib_model_modification_flags
*/
int pcilib_add_register_ranges(pcilib_t *ctx, pcilib_model_modification_flags_t flags, size_t n, const pcilib_register_range_t *ranges);
+/**
+ * Find the register bank id (offset in \a banks array) corresponding to the specified bank address
+ * @param[in,out] ctx - pcilib context
+ * @param[in] bank - the address of register bank
+ * @return - bank id or PCILIB_REGISTER_BANK_INVALID if bank is not found
+ */
pcilib_register_bank_t pcilib_find_register_bank_by_addr(pcilib_t *ctx, pcilib_register_bank_addr_t bank);
+
+/**
+ * Find the register bank id (offset in \a banks array) corresponding to the specified bank name
+ * @param[in,out] ctx - pcilib context
+ * @param[in] bankname - the name of register bank
+ * @return - bank id or PCILIB_REGISTER_BANK_INVALID if bank is not found
+ */
pcilib_register_bank_t pcilib_find_register_bank_by_name(pcilib_t *ctx, const char *bankname);
+
+/**
+ * Find the register bank id (offset in \a banks array) corresponding to the specified bank name or address
+ * @param[in,out] ctx - pcilib context
+ * @param[in] bank - either the name or the address of the required register bank
+ * @return - bank id or PCILIB_REGISTER_BANK_INVALID if bank is not found
+ */
pcilib_register_bank_t pcilib_find_register_bank(pcilib_t *ctx, const char *bank);
+/**
+ * Find the register protocol id (offset in \a protocols array) corresponding to the specified protocol address
+ * @param[in,out] ctx - pcilib context
+ * @param[in] protocol - the address of register protocol
+ * @return - protocol id or PCILIB_REGISTER_PROTOCOL_INVALID if register protocol is not found
+ */
pcilib_register_protocol_t pcilib_find_register_protocol_by_addr(pcilib_t *ctx, pcilib_register_protocol_addr_t protocol);
+
+/**
+ * Find the register protocol id (offset in \a protocols array) corresponding to the specified protocol name
+ * @param[in,out] ctx - pcilib context
+ * @param[in] name - the name of register protocol
+ * @return - protocol id or PCILIB_REGISTER_PROTOCOL_INVALID if register protocol is not found
+ */
pcilib_register_protocol_t pcilib_find_register_protocol_by_name(pcilib_t *ctx, const char *name);
+
+/**
+ * Find the register protocol id (offset in \a protocols array) corresponding to the specified protocol name or address
+ * @param[in,out] ctx - pcilib context
+ * @param[in] name - either the name or the address of the required register protocol
+ * @return - protocol id or PCILIB_REGISTER_PROTOCOL_INVALID if register protocol is not found
+ */
pcilib_register_protocol_t pcilib_find_register_protocol(pcilib_t *ctx, const char *name);
+/**
+ * Resolves the address of the specified register bank. The address of the register0 in the bank is returned.
+ *
+ * All address types (virtual address in process address space, physical address, or bus address) can be resolved.
+ * The caller has also to specify the requested access mode (read, write, read/write) and the error will be returned
+ * if the requested access type is not possible.
+ *
+ * @param[in,out] ctx - pcilib context
+ * @param[in] flags - specifies the type of required address (virtual, physical, or bus) and the required access (ro/wo/rw)
+ * @param[in] bank - the id of register bank
+ * @return - the resolved address or PCILIB_ADDRESS_INVALID on error
+ */
uintptr_t pcilib_resolve_bank_address_by_id(pcilib_t *ctx, pcilib_address_resolution_flags_t flags, pcilib_register_bank_t bank);
+
+/**
+ * Resolves the address of the specified register bank. The address of the register0 in the bank is returned.
+ *
+ * All address types (virtual address in process address space, physical address, or bus address) can be resolved.
+ * The caller has also to specify the requested access mode (read, write, read/write) and the error will be returned
+ * if the requested access type is not possible.
+ *
+ * @param[in,out] ctx - pcilib context
+ * @param[in] flags - specifies the type of required address (virtual, physical, or bus) and the required access (ro/wo/rw)
+ * @param[in] bank - the name of register bank
+ * @return - the resolved address or PCILIB_ADDRESS_INVALID on error
+ */
uintptr_t pcilib_resolve_bank_address(pcilib_t *ctx, pcilib_address_resolution_flags_t flags, const char *bank);
+/**
+ * Resolves the address of the specified register.
+ *
+ * All address types (virtual address in process address space, physical address, or bus address) can be resolved.
+ * The caller has also to specify the requested access mode (read, write, read/write) and the error will be returned
+ * if the requested access type is not possible.
+ *
+ * @param[in,out] ctx - pcilib context
+ * @param[in] flags - specifies the type of required address (virtual, physical, or bus) and the required access (ro/wo/rw)
+ * @param[in] reg - the id of register
+ * @return - the resolved address or PCILIB_ADDRESS_INVALID on error
+ */
uintptr_t pcilib_resolve_register_address_by_id(pcilib_t *ctx, pcilib_address_resolution_flags_t flags, pcilib_register_t reg);
+
+/**
+ * Resolves the address of the specified register.
+ *
+ * All address types (virtual address in process address space, physical address, or bus address) can be resolved.
+ * The caller has also to specify the requested access mode (read, write, read/write) and the error will be returned
+ * if the requested access type is not possible.
+ *
+ * @param[in,out] ctx - pcilib context
+ * @param[in] flags - specifies the type of required address (virtual, physical, or bus) and the required access (ro/wo/rw)
+ * @param[in] bank - should specify the bank name if register with the same name may occur in multiple banks, NULL otherwise
+ * @param[in] regname - the name of the register
+ * @return - the resolved address or PCILIB_ADDRESS_INVALID on error
+ */
uintptr_t pcilib_resolve_register_address(pcilib_t *ctx, pcilib_address_resolution_flags_t flags, const char *bank, const char *regname);
+/**
+ * Extracts additional information about the specified register bank. The additional information
+ * is model-specific and are provided as extra XML attributes in XML-described register bank.
+ * The available attributes are only restricted by used XSD schema.
+ * @param[in,out] ctx - pcilib context
+ * @param[in] bank - register bank id
+ * @param[in] attr - requested attribute name
+ * @param[in,out] val - the value of attribute is returned here (see \ref public_api_value),
+ * pcilib_clean_value() will be executed if \a val contains data. Therefore it should be always initialized to 0 before first use
+ * @return - error code or 0 on success
+ */
int pcilib_get_register_bank_attr_by_id(pcilib_t *ctx, pcilib_register_bank_t bank, const char *attr, pcilib_value_t *val);
+
+/**
+ * Extracts additional information about the specified register bank. The additional information
+ * is model-specific and are provided as extra XML attributes in XML-described register bank.
+ * The available attributes are only restricted by used XSD schema.
+ * @param[in,out] ctx - pcilib context
+ * @param[in] bankname - the name of register bank
+ * @param[in] attr - requested attribute name
+ * @param[in,out] val - the value of attribute is returned here (see \ref public_api_value),
+ * pcilib_clean_value() will be executed if \a val contains data. Therefore it should be always initialized to 0 before first use
+ * @return - error code or 0 on success
+ */
int pcilib_get_register_bank_attr(pcilib_t *ctx, const char *bankname, const char *attr, pcilib_value_t *val);
#ifdef __cplusplus
diff --git a/pcilib/bar.h b/pcilib/bar.h
index 3c0eef6..accc4ba 100644
--- a/pcilib/bar.h
+++ b/pcilib/bar.h
@@ -72,7 +72,21 @@ int pcilib_detect_address(pcilib_t *ctx, pcilib_bar_t *bar, uintptr_t *addr, siz
*/
char *pcilib_resolve_data_space(pcilib_t *ctx, uintptr_t addr, size_t *size);
+/**
+ * Return information about the specified BAR or NULL if BAR is not present in hardware
+ * @param[in,out] ctx - pcilib context
+ * @param[in] bar - the PCI BAR number (numbered from 0)
+ * @return - pointer to structure describing the specified BAR or NULL in case of error
+ */
const pcilib_bar_info_t *pcilib_get_bar_info(pcilib_t *ctx, pcilib_bar_t bar);
+
+/**
+ * Return a list of BAR memory regions available in the hardware.
+ * The list is terminated by a dummy BAR description with 0 size.
+ *
+ * @param[in,out] ctx - pcilib context
+ * @return - pointer to structure describing the BARs or NULL in case of error
+ */
const pcilib_bar_info_t *pcilib_get_bar_list(pcilib_t *ctx);
diff --git a/pcilib/cpu.h b/pcilib/cpu.h
index 2b3ed80..9b4b1d7 100644
--- a/pcilib/cpu.h
+++ b/pcilib/cpu.h
@@ -5,8 +5,23 @@
extern "C" {
#endif
+/**
+ * Return the mask of system memory page
+ * @return - page mask, the bits which will correspond to offset within the page are set to 1
+ */
int pcilib_get_page_mask();
+
+/**
+ * Number of CPU cores in the system (including HyperThreading cores)
+ * @return - number of available CPU cores
+ */
int pcilib_get_cpu_count();
+
+/**
+ * Returns the generation of Intel Core architecture
+ * Processors up to Intel Core gen4 are recognized.
+ * @return - Generation of Intel Core architecture (1 to 4) or 0 for non-Intel and Intel pre-Core architectures
+ */
int pcilib_get_cpu_gen();
#ifdef __cplusplus
diff --git a/pcilib/datacpy.h b/pcilib/datacpy.h
index 1ce2e79..e807e01 100644
--- a/pcilib/datacpy.h
+++ b/pcilib/datacpy.h
@@ -10,9 +10,61 @@
extern "C" {
#endif
+/**
+ * The collection of slow memcpy functions to move the data between BAR and system memory.
+ * If necessary the endianess conversion is performed to ensure that the data is encoded
+ * using the specified endianess in the BAR memory and using the native host order in the
+ * system memory. Since endianess conversion is symmetric, it is irrelevant if we are
+ * copying from system memory to BAR memory or vice-versa.
+ *
+ * The hardware may restrict access width or expose different behavior depending on the
+ * access width. These functions access memory using the specified word width only.
+ * 8-, 16-, 32-, and 64-bit wide access is supported.
+ *
+ * @param[out] dst - the destination memory region
+ * @param[in] src - the source memory region
+ * @param[in] access - the size of word (a single memory access) in bytes
+ * @param[in] n - the number of words to copy (\p n * \p access bytes are copied).
+ * @param[in] endianess - the endianess of the data words in the BAR memory
+ * @return - `dst` or NULL on error
+ */
+void *pcilib_datacpy(void * dst, void const * src, uint8_t access, size_t n, pcilib_endianess_t endianess);
+
+/**
+ * The collection of slow memcpy functions to move the data between BAR and system memory.
+ * If necessary the endianess conversion is performed to ensure that the data is encoded
+ * using the specified endianess in the BAR memory and using the native host order in the
+ * system memory. Since endianess conversion is symmetric, it is irrelevant if we are
+ * copying from system memory to BAR memory or vice-versa.
+ *
+ * The hardware may restrict access width or expose different behavior depending on the
+ * access width. This function only perform 32-bit memory accesses.
+ *
+ * @param[out] dst - the destination memory region
+ * @param[in] src - the source memory region
+ * @param[in] n - the number of 32-bit words to copy (4 * \p n bytes are copied)
+ * @param[in] endianess - the endianess of the data words in the BAR memory
+ * @return - `dst` or NULL on error
+ */
void *pcilib_datacpy32(void * dst, void const * src, size_t n, pcilib_endianess_t endianess);
+
+/**
+ * The collection of slow memcpy functions to move the data between BAR and system memory.
+ * If necessary the endianess conversion is performed to ensure that the data is encoded
+ * using the specified endianess in the BAR memory and using the native host order in the
+ * system memory. Since endianess conversion is symmetric, it is irrelevant if we are
+ * copying from system memory to BAR memory or vice-versa.
+ *
+ * The hardware may restrict access width or expose different behavior depending on the
+ * access width. This function only perform 64-bit memory accesses.
+ *
+ * @param[out] dst - the destination memory region
+ * @param[in] src - the source memory region
+ * @param[in] n - the number of 64-bit words to copy (8 * \p n bytes are copied)
+ * @param[in] endianess - the endianess of the data words in the BAR memory
+ * @return - `dst` or NULL on error
+ */
void *pcilib_datacpy64(void * dst, void const * src, size_t n, pcilib_endianess_t endianess);
-void *pcilib_datacpy(void * dst, void const * src, uint8_t size, size_t n, pcilib_endianess_t endianess);
#ifdef __cplusplus
}
diff --git a/pcilib/kmem.c b/pcilib/kmem.c
index cb3c58c..b103126 100644
--- a/pcilib/kmem.c
+++ b/pcilib/kmem.c
@@ -157,6 +157,7 @@ pcilib_kmem_handle_t *pcilib_alloc_kernel_memory(pcilib_t *ctx, pcilib_kmem_type
kbuf->buf.blocks[i].handle_id = kh.handle_id;
kbuf->buf.blocks[i].pa = kh.pa;
+ kbuf->buf.blocks[i].ba = kh.ba;
kbuf->buf.blocks[i].size = kh.size;
if (!i) reused = (kh.flags&KMEM_FLAG_REUSED)?PCILIB_TRISTATE_YES:PCILIB_TRISTATE_NO;
@@ -210,7 +211,12 @@ pcilib_kmem_handle_t *pcilib_alloc_kernel_memory(pcilib_t *ctx, pcilib_kmem_type
if ((kh.align)&&((kh.type&PCILIB_KMEM_TYPE_MASK) != PCILIB_KMEM_TYPE_PAGE)) {
- if (kh.pa % kh.align) kbuf->buf.blocks[i].alignment_offset = kh.align - kh.pa % kh.align;
+ // Physical or bus address here?
+ if (kh.ba) {
+ if (kh.ba % kh.align) kbuf->buf.blocks[i].alignment_offset = kh.align - kh.ba % kh.align;
+ } else {
+ if (kh.pa % kh.align) kbuf->buf.blocks[i].alignment_offset = kh.align - kh.pa % kh.align;
+ }
kbuf->buf.blocks[i].size -= kh.align;
}
@@ -355,7 +361,7 @@ int pcilib_kmem_sync_block(pcilib_t *ctx, pcilib_kmem_handle_t *k, pcilib_kmem_s
return 0;
}
-void *pcilib_kmem_get_ua(pcilib_t *ctx, pcilib_kmem_handle_t *k) {
+void* volatile pcilib_kmem_get_ua(pcilib_t *ctx, pcilib_kmem_handle_t *k) {
pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
return kbuf->buf.addr.ua + kbuf->buf.addr.alignment_offset + kbuf->buf.addr.mmap_offset;
}
@@ -367,10 +373,14 @@ uintptr_t pcilib_kmem_get_pa(pcilib_t *ctx, pcilib_kmem_handle_t *k) {
uintptr_t pcilib_kmem_get_ba(pcilib_t *ctx, pcilib_kmem_handle_t *k) {
pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
- return kbuf->buf.addr.pa + kbuf->buf.addr.alignment_offset;
+
+ if (kbuf->buf.addr.ba)
+ return kbuf->buf.addr.ba + kbuf->buf.addr.alignment_offset;
+
+ return 0;
}
-void *pcilib_kmem_get_block_ua(pcilib_t *ctx, pcilib_kmem_handle_t *k, size_t block) {
+void* volatile pcilib_kmem_get_block_ua(pcilib_t *ctx, pcilib_kmem_handle_t *k, size_t block) {
pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
return kbuf->buf.blocks[block].ua + kbuf->buf.blocks[block].alignment_offset + kbuf->buf.blocks[block].mmap_offset;
}
@@ -382,7 +392,11 @@ uintptr_t pcilib_kmem_get_block_pa(pcilib_t *ctx, pcilib_kmem_handle_t *k, size_
uintptr_t pcilib_kmem_get_block_ba(pcilib_t *ctx, pcilib_kmem_handle_t *k, size_t block) {
pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
- return kbuf->buf.blocks[block].pa + kbuf->buf.blocks[block].alignment_offset;
+
+ if (kbuf->buf.blocks[block].ba)
+ return kbuf->buf.blocks[block].ba + kbuf->buf.blocks[block].alignment_offset;
+
+ return 0;
}
size_t pcilib_kmem_get_block_size(pcilib_t *ctx, pcilib_kmem_handle_t *k, size_t block) {
diff --git a/pcilib/kmem.h b/pcilib/kmem.h
index eb7e4ff..1d63e3b 100644
--- a/pcilib/kmem.h
+++ b/pcilib/kmem.h
@@ -4,42 +4,42 @@
typedef struct pcilib_s pcilib_t;
typedef struct pcilib_kmem_list_s pcilib_kmem_list_t;
-#define PCILIB_KMEM_PAGE_SIZE 0x1000
+#define PCILIB_KMEM_PAGE_SIZE 0x1000 /**< Default pages size is 4096 bytes */
typedef enum {
- PCILIB_TRISTATE_NO = 0,
- PCILIB_TRISTATE_PARTIAL = 1,
- PCILIB_TRISTATE_YES = 2
+ PCILIB_TRISTATE_NO = 0, /**< All values are evaluated as false */
+ PCILIB_TRISTATE_PARTIAL = 1, /**< Some values are evaluated as false and others as true */
+ PCILIB_TRISTATE_YES = 2 /**< All values are evaluated as true */
} pcilib_tristate_t;
-#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)
+#define PCILIB_KMEM_TYPE_MASK 0xFFFF0000 /**< Mask selecting the general type in \ref pcilib_kmem_type_t. The subtype is may specify DMA mapping and other minor details of allocation */
+#define PCILIB_KMEM_USE_TYPE(use) (use >> 16) /**< Returns general use of the kernel buffers, for instance DMA buffer (see \ref pcilib_kmem_use_t) */
+#define PCILIB_KMEM_USE_SUBTYPE(use) (use & 0xFFFF) /**< Additional information about the use of the kernel buffers. The actual meaning depends on general use, for instance in case of DMA buffers the subtype specifies DMA engine */
+#define PCILIB_KMEM_USE(type, subtype) ((pcilib_kmem_use_t)(((type) << 16)|(subtype))) /**< Constructs full use from use type and use subtype */
typedef enum {
- PCILIB_KMEM_TYPE_CONSISTENT = 0x00000,
- PCILIB_KMEM_TYPE_PAGE = 0x10000,
- PCILIB_KMEM_TYPE_DMA_S2C_PAGE = 0x10001,
- PCILIB_KMEM_TYPE_DMA_C2S_PAGE = 0x10002,
- PCILIB_KMEM_TYPE_REGION = 0x20000,
- PCILIB_KMEM_TYPE_REGION_S2C = 0x20001,
- PCILIB_KMEM_TYPE_REGION_C2S = 0x20002
+ PCILIB_KMEM_TYPE_CONSISTENT = 0x00000, /**< Consistent memory can be simultaneously accessed by the device and applications and is normally used for DMA descriptors */
+ PCILIB_KMEM_TYPE_PAGE = 0x10000, /**< A number of physically consequitive pages of memory */
+ PCILIB_KMEM_TYPE_DMA_S2C_PAGE = 0x10001, /**< Memory pages mapped for S2C DMA operation (device reads) */
+ PCILIB_KMEM_TYPE_DMA_C2S_PAGE = 0x10002, /**< Memory pages mapped for C2S DMA operation (device writes) */
+ PCILIB_KMEM_TYPE_REGION = 0x20000, /**< Just map buffers to the contiguous user-supplied memory region (normally reserved during the boot with option memmap=512M$2G) */
+ PCILIB_KMEM_TYPE_REGION_S2C = 0x20001, /**< Memory region mapped for S2C DMA operation (device reads) */
+ PCILIB_KMEM_TYPE_REGION_C2S = 0x20002 /**< Memory region mapped for C2S DMA operation (device writes */
} pcilib_kmem_type_t;
typedef enum {
- PCILIB_KMEM_USE_STANDARD = 0,
- 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_STANDARD = 0, /**< Undefined use, buffers of standard use are not grouped together and can be used individually */
+ PCILIB_KMEM_USE_DMA_RING = 1, /**< The kmem used for DMA descriptor, the sub type specifies the id of considered DMA engine */
+ PCILIB_KMEM_USE_DMA_PAGES = 2, /**< The kmem used for DMA buffers, the sub type specifies the address of considered DMA engine (can be engine specific) */
+ PCILIB_KMEM_USE_SOFTWARE_REGISTERS = 3, /**< The kmem used to store software registers, the sub type holds the bank address */
+ PCILIB_KMEM_USE_LOCKS = 4, /**< The kmem used to hold locks, the sub type is not used */
+ PCILIB_KMEM_USE_USER = 0x10 /**< Further uses can be defined by event engines */
} pcilib_kmem_use_t;
typedef enum {
- PCILIB_KMEM_SYNC_BIDIRECTIONAL = 0,
- PCILIB_KMEM_SYNC_TODEVICE = 1,
- PCILIB_KMEM_SYNC_FROMDEVICE = 2
+ PCILIB_KMEM_SYNC_BIDIRECTIONAL = 0, /**< Two-way synchronization (should not be used) */
+ PCILIB_KMEM_SYNC_TODEVICE = 1, /**< Allow device to read the data in the DMA-able buffer */
+ PCILIB_KMEM_SYNC_FROMDEVICE = 2 /**< Allow application to read the data written by device in the DMA-able buffer */
} pcilib_kmem_sync_direction_t;
typedef enum {
@@ -54,25 +54,25 @@ typedef enum {
typedef enum {
- PCILIB_KMEM_REUSE_ALLOCATED = PCILIB_TRISTATE_NO,
- PCILIB_KMEM_REUSE_REUSED = PCILIB_TRISTATE_YES,
- PCILIB_KMEM_REUSE_PARTIAL = PCILIB_TRISTATE_PARTIAL,
- PCILIB_KMEM_REUSE_PERSISTENT = 0x100,
- PCILIB_KMEM_REUSE_HARDWARE = 0x200
+ PCILIB_KMEM_REUSE_ALLOCATED = PCILIB_TRISTATE_NO, /**< The kernel memory was allocated anew */
+ PCILIB_KMEM_REUSE_REUSED = PCILIB_TRISTATE_YES, /**< Already allocated kernel memory was re-used */
+ PCILIB_KMEM_REUSE_PARTIAL = PCILIB_TRISTATE_PARTIAL,/**< The kernel memory was partially re-used and partially allocated */
+ PCILIB_KMEM_REUSE_PERSISTENT = 0x100, /**< Flag indicating that persistent kmem was allocated/reused (will not be cleaned on pcilib_free_kernel_memory() unless forced with PCILIB_KMEM_FLAG_PERSISTENT */
+ PCILIB_KMEM_REUSE_HARDWARE = 0x200 /**< Flag indicating that kmem may be used by hardware device */
} pcilib_kmem_reuse_state_t;
typedef struct {
- int handle_id;
- pcilib_kmem_reuse_state_t reused;
+ int handle_id; /**< handled id is used to indentify buffer to kernel module */
+ pcilib_kmem_reuse_state_t reused; /**< indicates if the buffer was allocated or used */
- uintptr_t pa;
-// uintptr_t va;
- void *ua;
- size_t size;
+ uintptr_t pa; /**< physical address of buffer */
+ uintptr_t ba; /**< bus address of buffer (if it is mapped for DMA operations) */
+ void* volatile ua; /**< pointer to buffer in the process address space */
+ size_t size; /**< size of the buffer in bytes */
- size_t alignment_offset;
- size_t mmap_offset;
+ size_t alignment_offset; /**< we may request alignment of allocated buffers. To enusre proper alignment the larger buffer will be allocated and the offset will specify the first position in the buffer fullfilling alignment request */
+ size_t mmap_offset; /**< mmap always maps pages, if physical address is not aligned to page boundary, this is the offset of the buffer relative to the pointer returned by mmap (and stored in \a ua) */
} pcilib_kmem_addr_t;
/**
@@ -81,43 +81,200 @@ typedef struct {
* sgmap allocation - addr contains ua, but pa's are set in blocks, n_blocks > 0
*/
typedef struct {
- pcilib_kmem_addr_t addr;
-
- pcilib_kmem_type_t type;
- pcilib_kmem_use_t use;
- pcilib_kmem_reuse_state_t reused;
-
- size_t n_blocks;
- pcilib_kmem_addr_t blocks[];
+ pcilib_kmem_type_t type; /**< The type of kernel memory (how it is allocated) */
+ pcilib_kmem_use_t use; /**< The purpose of kernel memory (how it will be used) */
+ pcilib_kmem_reuse_state_t reused; /**< Indicates if kernel memory was allocated anew, reused, or partially re-used. The additional flags will provide information about persistance and the hardware access */
+
+ size_t n_blocks; /**< Number of allocated/re-used buffers in kmem */
+ pcilib_kmem_addr_t addr; /**< Information about the buffer of single-buffer kmem */
+ pcilib_kmem_addr_t blocks[]; /**< Information about all the buffers in kmem (variable size) */
} pcilib_kmem_buffer_t;
typedef void pcilib_kmem_handle_t;
struct pcilib_kmem_list_s {
- pcilib_kmem_list_t *next, *prev;
-
- pcilib_kmem_buffer_t buf; // variable size, should be last item in struct
+ pcilib_kmem_list_t *next, *prev; /**< List storring all allocated/re-used kmem's in application */
+ pcilib_kmem_buffer_t buf; /**< The actual kmem context (it is of variable size due to \a blocks and, hence, should be last item in this struct */
};
#ifdef __cplusplus
extern "C" {
#endif
+/**
+ * This function either allocates new buffers in the kernel space or just re-uses existing buffers.
+ *
+ * Sometimes kernel memory is allocated for the specific use-case, for example to provide buffers for DMA engine.
+ * It is possible to specify intended use using \p use parameter.
+ * The kernel memory with the specified use (i.e. \p use != 0) can be declared persistent (\p flags include
+ * ::PCILIB_KMEM_FLAG_PERSISTENT). Such memory will not be de-allocated on clean-up and will be maintained by
+ * the kernel module across the runs of various pcilib applications. To re-use persistent kernel memory,
+ * ::PCILIB_KMEM_FLAG_REUSE should be set. In this case, the pcilib will either allocate new kernel memory
+ * or re-use the already allocated kernel buffers with the specified \p use. If ::PCILIB_KMEM_FLAG_REUSE
+ * is not set, but the kernel memory with the specified use is already allocated, an error will be returned.
+ *
+ * It is also possible to allocate persistent kernel memory which can be execlusively used by a single process.
+ * The ::PCILIB_KMEM_FLAG_EXCLUSIVE flag have to be provided to pcilib_alloc_kernel_memory() function in this
+ * case. Only a single process may call hold a reference to kernel memory. Before next process would be able
+ * to obtain it, the process holding the reference should return it using pcilib_free_kernel_memory()
+ * call. For example, DMA engine uses exclusive kernel memory to guarantee that exactly one process is
+ * controlling DMA operations.
+ *
+ * To clean up allocated persistent kernel memory, pcilib_free_kernel_memory() have to be called with
+ * ::PCILIB_KMEM_FLAG_PERSISTENT flag set.
+ *
+ * @param[in,out] ctx - pcilib context
+ * @param[in] type - specifies type of allocation (simple pages, DMA pages, consistent memory, etc.)
+ * @param[in] nmemb - specifies number of buffers to allocated
+ * @param[in] size - specifies the size of each buffer in bytes
+ * @param[in] alignment - specifies required alignment, the bus addresses are aligned for PCI-mapped buffers and physical addresses are aligned for the rest (if bounce buffers are used, the physical and bus addresses are not always have the same alignment)
+ * @param[in] use - specifies how the memory will be used (to store software registers, locks, DMA buffers, etc.)
+ * @param[in] flags - various flags defining how buffers should be re-used/allocated and, that, should happen on pcilib_free_kernel_memory()
+ * @return - pointer on context with allocated kernel memory or NULL in case of error
+ */
pcilib_kmem_handle_t *pcilib_alloc_kernel_memory(pcilib_t *ctx, pcilib_kmem_type_t type, size_t nmemb, size_t size, size_t alignment, pcilib_kmem_use_t use, pcilib_kmem_flags_t flags);
+
+/**
+ * This function either frees the allocated kernel memory or just releases some of the references.
+ *
+ * Only reference tracking is performed If the ::PCILIB_KMEM_FLAG_REUSE flag is passed to the
+ * function. Otherwise, non-persistent memory is released when pcilib_free_kernel_memory() called.
+ * The persistent memory is released if ::PCILIB_KMEM_FLAG_PERSISTENT is passed in \p flags parameter
+ * unless the hold references are preventing us from releasing this memory. The error is reported
+ * in this case. The kernel memory can be freed irrespective of hold references if ::PCILIB_KMEM_FLAG_FORCE
+ * flag is specified.
+ *
+ * There are several types of references:
+ * - The hardware reference - indicates that the memory may be used by DMA engine of the device. It is set
+ * if pcilib_alloc_kernel_memory() is executed with ::PCILIB_KMEM_FLAG_HARDWARE flag. The reference can
+ * be released if the same flag is passed to pcilib_free_kernel_memory()
+ * - The software references - are obtained when the memory is reused with pcilib_alloc_kernel_memory().
+ * They are released when corresponding pcilib_free_kernel_memory() is called.
+ * - The mmap references - are obtained when the kernel memory is mapped to the virtual memory of application
+ * which is normally happen on pcilib_alloc_kernel_memory() and fork of the process. The references are
+ * returned when memory is unmapped. This happens on pcilib_free_kernel_memory() and termination of the
+ * process with still mmaped regions.
+ *
+ * The software references are in a way replicate functionality of the mmap references. Currently, they are
+ * used to keep track of non-persistent memory allocated and re-used within the same application (which is
+ * actually discouraged). In this case the number of pcilib_alloc_kernel_memory() should be matched by
+ * number of pcilib_free_kernel_memory() calls and only on a last call the memory will be really released.
+ * Normally, all but last calls have to be accompanied by ::PCILIB_KMEM_FLAG_REUSE flag or the error
+ * is reported. But to keep code simpler, the pcilib will not complain until there are software references
+ * on hold. When the last software reference is released, we try actually clean up the memory and the
+ * error is reported if other types of references are still present.
+ * The software references may stuck if application crashes before calling pcilib_free_kernel_memory().
+ * pcilib will prevent us from releasing the kernel memory with stuck references. Such memory can
+ * be cleaned only with ::PCILIB_KMEM_FLAG_FORCE flag or using pcilib_clean_kernel_memory() call.
+ *
+ * @param[in,out] ctx - pcilib context
+ * @param[in,out] k - kernel memory handle returned from pcilib_alloc_kernel_memory() call
+ * @param[in] flags - various flags defining which referenes to hold and return and if the memory should be preserved or freed
+ */
void pcilib_free_kernel_memory(pcilib_t *ctx, pcilib_kmem_handle_t *k, pcilib_kmem_flags_t flags);
-//int pcilib_kmem_sync(pcilib_t *ctx, pcilib_kmem_handle_t *k, pcilib_kmem_sync_direction_t dir);
+
+
+/**
+ * Free / dereference all kernel memory buffers associated with the specified \p use
+ *
+ * @param[in,out] ctx - pcilib context
+ * @param[in] use - use-number to clean
+ * @param[in] flags - various flags defining which referenes to hold and return and if the memory should be dereferenced or freed
+ * @return - error or 0 on success
+ */
+int pcilib_clean_kernel_memory(pcilib_t *ctx, pcilib_kmem_use_t use, pcilib_kmem_flags_t flags);
+
+
+/**
+ * Synchronizes usage of the specified buffer between hardware and the system.
+ *
+ * @param[in,out] ctx - pcilib context
+ * @param[in] k - kernel memory handle returned from pcilib_alloc_kernel_memory() call
+ * @param[in] dir - synchronization direction (allows either device or system access)
+ * @param[in] block - specifies the buffer within the kernel memory (buffers are numbered from 0)
+ * @return - error or 0 on success
+ */
int pcilib_kmem_sync_block(pcilib_t *ctx, pcilib_kmem_handle_t *k, pcilib_kmem_sync_direction_t dir, size_t block);
-void *pcilib_kmem_get_ua(pcilib_t *ctx, pcilib_kmem_handle_t *k);
+
+
+/**
+ * Get a valid pointer on the user-space mapping of a single-buffer kernel memory
+ *
+ * @param[in,out] ctx - pcilib context
+ * @param[in] k - kernel memory handle returned from pcilib_alloc_kernel_memory() call
+ * @return - user-space pointer
+ */
+void* volatile pcilib_kmem_get_ua(pcilib_t *ctx, pcilib_kmem_handle_t *k);
+
+/**
+ * Get a physical address of a single-buffer kernel memory
+ *
+ * @param[in,out] ctx - pcilib context
+ * @param[in] k - kernel memory handle returned from pcilib_alloc_kernel_memory() call
+ * @return - physical address
+ */
uintptr_t pcilib_kmem_get_pa(pcilib_t *ctx, pcilib_kmem_handle_t *k);
+
+/**
+ * Get a bus address of a single-buffer kernel memory
+ *
+ * @param[in,out] ctx - pcilib context
+ * @param[in] k - kernel memory handle returned from pcilib_alloc_kernel_memory() call
+ * @return - bus address or 0 if no bus mapping
+ */
uintptr_t pcilib_kmem_get_ba(pcilib_t *ctx, pcilib_kmem_handle_t *k);
-void *pcilib_kmem_get_block_ua(pcilib_t *ctx, pcilib_kmem_handle_t *k, size_t block);
+
+/**
+ * Get a valid pointer on the user-space mapping of the specified kernel memory buffer
+ *
+ * @param[in,out] ctx - pcilib context
+ * @param[in] k - kernel memory handle returned from pcilib_alloc_kernel_memory() call
+ * @param[in] block - specifies the buffer within the kernel memory (buffers are numbered from 0)
+ * @return - user-space pointer
+ */
+void* volatile pcilib_kmem_get_block_ua(pcilib_t *ctx, pcilib_kmem_handle_t *k, size_t block);
+
+/**
+ * Get a physical address of the specified kernel memory buffer
+ *
+ * @param[in,out] ctx - pcilib context
+ * @param[in] k - kernel memory handle returned from pcilib_alloc_kernel_memory() call
+ * @param[in] block - specifies the buffer within the kernel memory (buffers are numbered from 0)
+ * @return - physical address
+ */
uintptr_t pcilib_kmem_get_block_pa(pcilib_t *ctx, pcilib_kmem_handle_t *k, size_t block);
+
+/**
+ * Get a bus address of the specified kernel memory buffer
+ *
+ * @param[in,out] ctx - pcilib context
+ * @param[in] k - kernel memory handle returned from pcilib_alloc_kernel_memory() call
+ * @param[in] block - specifies the buffer within the kernel memory (buffers are numbered from 0)
+ * @return - bus address or 0 if no bus mapping
+ */
uintptr_t pcilib_kmem_get_block_ba(pcilib_t *ctx, pcilib_kmem_handle_t *k, size_t block);
+
+
+/**
+ * Get size of the specified kernel memory buffer
+ *
+ * @param[in,out] ctx - pcilib context
+ * @param[in] k - kernel memory handle returned from pcilib_alloc_kernel_memory() call
+ * @param[in] block - specifies the buffer within the kernel memory (buffers are numbered from 0)
+ * @return - size in bytes
+ */
size_t pcilib_kmem_get_block_size(pcilib_t *ctx, pcilib_kmem_handle_t *k, size_t block);
-pcilib_kmem_reuse_state_t pcilib_kmem_is_reused(pcilib_t *ctx, pcilib_kmem_handle_t *k);
-int pcilib_clean_kernel_memory(pcilib_t *ctx, pcilib_kmem_use_t use, pcilib_kmem_flags_t flags);
+
+/**
+ * Reports if the kernel memory was reused fully, partially, or allocated completely anew
+ *
+ * @param[in,out] ctx - pcilib context
+ * @param[in] k - kernel memory handle returned from pcilib_alloc_kernel_memory() call
+ * @return - re-use information
+ */
+pcilib_kmem_reuse_state_t pcilib_kmem_is_reused(pcilib_t *ctx, pcilib_kmem_handle_t *k);
#ifdef __cplusplus
}
diff --git a/pcilib/memcpy.h b/pcilib/memcpy.h
index dbfae36..99317bf 100644
--- a/pcilib/memcpy.h
+++ b/pcilib/memcpy.h
@@ -9,10 +9,60 @@
extern "C" {
#endif
+/**
+ * The collection of slow memcpy functions to move the data between BAR and system memory.
+ *
+ * The hardware may restrict access width or expose different behavior depending on the
+ * access width. These functions access memory using the specified word width only.
+ * 8-, 16-, 32-, and 64-bit wide access is supported.
+ *
+ * @param[out] dst - the destination memory region
+ * @param[in] src - the source memory region
+ * @param[in] access - the size of word (a single memory access) in bytes
+ * @param[in] n - the number of words to copy (\p n * \p access bytes are copied).
+ * @return - `dst` or NULL on error
+ */
+void *pcilib_memcpy(void * dst, void const * src, uint8_t access, size_t n);
+
+/**
+ * The collection of slow memcpy functions to move the data between BAR and system memory.
+ *
+ * The hardware may restrict access width or expose different behavior depending on the
+ * access width. This function only perform 8-bit memory accesses.
+ *
+ * @param[out] dst - the destination memory region
+ * @param[in] src - the source memory region
+ * @param[in] len - the number of bytes to copy
+ * @return - `dst` or NULL on error
+ */
void *pcilib_memcpy8(void * dst, void const * src, size_t len);
+
+/**
+ * The collection of slow memcpy functions to move the data between BAR and system memory.
+ *
+ * The hardware may restrict access width or expose different behavior depending on the
+ * access width. This function only perform 32-bit memory accesses.
+ *
+ * @param[out] dst - the destination memory region
+ * @param[in] src - the source memory region
+ * @param[in] len - the number of bytes to copy
+ * @return - `dst` or NULL on error
+ */
void *pcilib_memcpy32(void * dst, void const * src, size_t len);
+
+
+/**
+ * The collection of slow memcpy functions to move the data between BAR and system memory.
+ *
+ * The hardware may restrict access width or expose different behavior depending on the
+ * access width. This function only perform 64-bit memory accesses.
+ *
+ * @param[out] dst - the destination memory region
+ * @param[in] src - the source memory region
+ * @param[in] len - the number of bytes to copy
+ * @return - `dst` or NULL on error
+ */
void *pcilib_memcpy64(void * dst, void const * src, size_t len);
-void *pcilib_memcpy(void * dst, void const * src, uint8_t access, size_t n);
#ifdef __cplusplus
}
diff --git a/pcilib/pci.c b/pcilib/pci.c
index 19165ba..ec40c95 100644
--- a/pcilib/pci.c
+++ b/pcilib/pci.c
@@ -131,7 +131,7 @@ pcilib_t *pcilib_open(const char *device, const char *model) {
return NULL;
}
- ctx->page_mask = (uintptr_t)-1;
+ ctx->page_mask = pcilib_get_page_mask();
if ((model)&&(!strcasecmp(model, "maintenance"))) {
ctx->model = strdup("maintenance");
@@ -271,14 +271,14 @@ const pcilib_driver_version_t *pcilib_get_driver_version(pcilib_t *ctx) {
const pcilib_board_info_t *pcilib_get_board_info(pcilib_t *ctx) {
int ret;
- if (ctx->page_mask == (uintptr_t)-1) {
+ if (!ctx->board_info_ready) {
ret = ioctl( ctx->handle, PCIDRIVER_IOC_PCI_INFO, &ctx->board_info );
if (ret) {
pcilib_error("PCIDRIVER_IOC_PCI_INFO ioctl have failed");
return NULL;
}
- ctx->page_mask = pcilib_get_page_mask();
+ ctx->board_info_ready = 1;
}
return &ctx->board_info;
diff --git a/pcilib/pci.h b/pcilib/pci.h
index 43de485..172a6fc 100644
--- a/pcilib/pci.h
+++ b/pcilib/pci.h
@@ -46,9 +46,11 @@ typedef struct {
struct pcilib_s {
int handle; /**< file handle of device */
-
- uintptr_t page_mask; /**< Selects bits which define offset within the page */
+
pcilib_driver_version_t driver_version; /**< Version reported by the driver */
+
+ uintptr_t page_mask; /**< Selects bits which define offset within the page */
+ int board_info_ready; /**< Flag indicating if board info is already requested and populated */
pcilib_board_info_t board_info; /**< The mandatory information about board as defined by PCI specification */
pcilib_pcie_link_info_t link_info; /**< Infomation about PCIe connection */
char *bar_space[PCILIB_MAX_BARS]; /**< Pointers to the mapped BARs in virtual address space */
diff --git a/pcilib/pcilib.h b/pcilib/pcilib.h
index 8ab8e9e..f1c0dd7 100644
--- a/pcilib/pcilib.h
+++ b/pcilib/pcilib.h
@@ -1265,7 +1265,7 @@ int pcilib_set_value_from_static_string(pcilib_t *ctx, pcilib_value_t *val, cons
* @param[in] str - initializer
* @return - 0 on success or memory error
*/
-int pcilib_set_value_from_string(pcilib_t *ctx, pcilib_value_t *value, const char *str);
+int pcilib_set_value_from_string(pcilib_t *ctx, pcilib_value_t *val, const char *str);
/**
* Get the floating point value from the polymorphic type. May inmply impliced type conversion,
diff --git a/pcilib/plugin.h b/pcilib/plugin.h
index f9ff7cf..d79936a 100644
--- a/pcilib/plugin.h
+++ b/pcilib/plugin.h
@@ -5,11 +5,60 @@
extern "C" {
#endif
+/**
+ * Loads the specified plugin
+ * The plugin symbols are loaded localy and not available for symbol resolution, but should be requested
+ * with pcilib_plugin_get_symbol() instead.
+ * @param[in] name - the plugin name (with extension, but without path)
+ * @return - dlopen'ed plugin or NULL in the case of error
+ */
void *pcilib_plugin_load(const char *name);
+
+/**
+ * Cleans up the loaded plugin
+ * @param[in] plug - plugin loaded with pcilib_plugin_load()
+ */
void pcilib_plugin_close(void *plug);
+
+/**
+ * Resolves the specified symbol in the plugin
+ * @param[in] plug - plugin loaded with pcilib_plugin_load()
+ * @param[in] symbol - name of the symbol
+ * @return - pointer to the symbol or NULL if not found
+ */
void *pcilib_plugin_get_symbol(void *plug, const char *symbol);
-const pcilib_model_description_t *pcilib_get_plugin_model(pcilib_t *pcilib, void *plug, unsigned short vendor_id, unsigned short device_id, const char *model);
-const pcilib_model_description_t *pcilib_find_plugin_model(pcilib_t *pcilib, unsigned short vendor_id, unsigned short device_id, const char *model);
+
+/**
+ * Verifies if plugin can handle the hardware and requests appropriate model description
+ * @param[in,out] ctx - pcilib context
+ * @param[in] plug - plugin loaded with pcilib_plugin_load()
+ * @param[in] vendor_id - Vendor ID reported by hardware
+ * @param[in] device_id - Device ID reported by hardware
+ * @param[in] model - the requested pcilib model or NULL for autodetction
+ * @return - the appropriate model description or NULL if the plugin does not handle the installed hardware or requested model
+ */
+const pcilib_model_description_t *pcilib_get_plugin_model(pcilib_t *ctx, void *plug, unsigned short vendor_id, unsigned short device_id, const char *model);
+
+/**
+ * Finds the appropriate plugin and returns model description.
+ *
+ * The function sequentially loads plugins available in ::PCILIB_PLUGIN_DIR and
+ * checks if they support the installed hardware and requested model. If hardware
+ * is not supported, the plugin is immideately unloaded. On a first success
+ * the model description is returned to caller and no further plguins are loaded.
+ * If no suitable plugin is found, the NULL is returned.
+ *
+ * If model is specified, first a plugin with the same name is loaded and check performed
+ * if it can handle the installed hardware. If not, we iterate over all available
+ * plugins as usual.
+ *
+ * @param[in,out] ctx - pcilib context
+ * @param[in] vendor_id - Vendor ID reported by hardware
+ * @param[in] device_id - Device ID reported by hardware
+ * @param[in] model - the requested pcilib model or NULL for autodetction
+ * @return - the appropriate model description or NULL if no plugin found able to handle the installed hardware or requested model
+ */
+const pcilib_model_description_t *pcilib_find_plugin_model(pcilib_t *ctx, unsigned short vendor_id, unsigned short device_id, const char *model);
#ifdef __cplusplus
}
diff --git a/pcilib/timing.h b/pcilib/timing.h
index 630df44..b8df7c3 100644
--- a/pcilib/timing.h
+++ b/pcilib/timing.h
@@ -8,14 +8,63 @@
extern "C" {
#endif
+
+/**
+ * Add the specified number of microseconds to the time stored in \p tv
+ * @param[in,out] tv - timestamp
+ * @param[in] timeout - number of microseconds to add
+ * @return - error code or 0 for correctness
+ */
int pcilib_add_timeout(struct timeval *tv, pcilib_timeout_t timeout);
+
+/**
+ * Computes the deadline by adding the specified number of microseconds to the current timestamp
+ * @param[out] tv - the deadline
+ * @param[in] timeout - number of microseconds to add
+ * @return - error code or 0 for correctness
+ */
int pcilib_calc_deadline(struct timeval *tv, pcilib_timeout_t timeout);
-int pcilib_check_deadline(struct timeval *tve, pcilib_timeout_t timeout);
-pcilib_timeout_t pcilib_calc_time_to_deadline(struct timeval *tve);
+
+/**
+ * Check if we are within \p timeout microseconds before the specified deadline or already past it
+ * @param[in] tv - the deadline
+ * @param[in] timeout - maximum number of microseconds before deadline
+ * @return - 1 if we are within \p timeout microseconds before deadline or past it, 0 - otherwise
+ */
+int pcilib_check_deadline(struct timeval *tv, pcilib_timeout_t timeout);
+
+/**
+ * Compute the remaining time to deadline
+ * @param[in] tv - the deadline
+ * @return - number of microseconds until deadline or 0 if we are already past it
+ */
+pcilib_timeout_t pcilib_calc_time_to_deadline(struct timeval *tv);
+
+/**
+ * Executes sleep until the specified deadline
+ * Real-time capabilities are not used. TThe sleep could wake slightly after the specified deadline.
+ * @param[in] tv - the deadline
+ * @return - error code or 0 for correctness
+ */
int pcilib_sleep_until_deadline(struct timeval *tv);
-int pcilib_timecmp(struct timeval *tv1, struct timeval *tv2);
+
+/**
+ * Computes the number of microseconds between 2 timestamps.
+ * This function expects that \p tve is after \p tvs.
+ * @param[in] tve - the end of the time interval
+ * @param[in] tvs - the beginning of the time interval
+ * @return - number of microseconds between two timestamps
+ */
pcilib_timeout_t pcilib_timediff(struct timeval *tve, struct timeval *tvs);
+/**
+ * Compares two timestamps
+ * @param[in] tv1 - the first timestamp
+ * @param[in] tv2 - the second timestamp
+ * @return - 0 if timestamps are equal, 1 if the first timestamp is after the second, or -1 if the second is after the first.
+ */
+int pcilib_timecmp(struct timeval *tv1, struct timeval *tv2);
+
#ifdef __cplusplus
}
diff --git a/pcilib/tools.h b/pcilib/tools.h
index 8c525e0..7b4aea0 100644
--- a/pcilib/tools.h
+++ b/pcilib/tools.h
@@ -14,15 +14,65 @@
extern "C" {
#endif
+/**
+ * Check if provided string is a decimal integer
+ * @param[in] str - string to check
+ * @return - 1 if string is a number and 0 - otherwise
+ */
int pcilib_isnumber(const char *str);
+
+/**
+ * Check if provided string is a hexdecimal integer, optionally prefexed with 0x
+ * @param[in] str - string to check
+ * @return - 1 if string is a number and 0 - otherwise
+ */
int pcilib_isxnumber(const char *str);
+
+/**
+ * Check if first \p len bytes of the provided string is a decimal integer
+ * @param[in] str - string to check
+ * @param[in] len - size of the string
+ * @return - 1 if string is a number and 0 - otherwise
+ */
int pcilib_isnumber_n(const char *str, size_t len);
+
+/**
+ * Check if first \p len bytes of the provided string is a hexdecimal integer, optionally prefexed with 0x
+ * @param[in] str - string to check
+ * @param[in] len - size of the string
+ * @return - 1 if string is a number and 0 - otherwise
+ */
int pcilib_isxnumber_n(const char *str, size_t len);
+/**
+ * Change the endianess of the provided number (between big- and little-endian format)
+ * @param[in] x - number in little/big endian format
+ * @return - number in big/little endian format
+ */
uint16_t pcilib_swap16(uint16_t x);
+
+/**
+ * Change the endianess of the provided number (between big- and little-endian format)
+ * @param[in] x - number in little/big endian format
+ * @return - number in big/little endian format
+ */
uint32_t pcilib_swap32(uint32_t x);
+
+/**
+ * Change the endianess of the provided number (between big- and little-endian format)
+ * @param[in] x - number in little/big endian format
+ * @return - number in big/little endian format
+ */
uint64_t pcilib_swap64(uint64_t x);
-void pcilib_swap(void *dst, void *src, size_t size, size_t n);
+
+/**
+ * Change the endianess of the provided array
+ * @param[out] dst - the destination memory region, can be equal to \p src
+ * @param[in] src - the source memory region
+ * @param[in] access - the size of word in bytes (1, 2, 4, or 8)
+ * @param[in] n - the number of words to copy (\p n * \p access bytes are copied).
+ */
+void pcilib_swap(void *dst, void *src, size_t access, size_t n);
#ifdef __cplusplus
}
diff --git a/pcitool/cli.c b/pcitool/cli.c
index 3f1af74..caa55a9 100644
--- a/pcitool/cli.c
+++ b/pcitool/cli.c
@@ -1291,9 +1291,11 @@ int ReadData(pcilib_t *handle, ACCESS_MODE mode, FLAGS flags, pcilib_dma_engine_
size = 2048; bytes = 0;
do {
- size *= 2;
- buf = realloc(buf, size);
- if (!buf) Error("Allocation of %zu bytes of memory has failed", size);
+ if ((size - bytes) < 4096) {
+ size *= 2;
+ buf = realloc(buf, size);
+ if (!buf) Error("Allocation of %zu bytes of memory has failed", size);
+ }
err = pcilib_read_dma_custom(handle, dmaid, addr, size - bytes, dma_flags, timeout, buf + bytes, &ret);
bytes += ret;