summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorroot <root@iss-tomyspiel-l>2011-07-04 16:21:23 +0200
committerroot <root@iss-tomyspiel-l>2011-07-04 16:21:23 +0200
commit2e7a7a3534bcf591c0d6c65105b2d845f293112f (patch)
treed0e5d2f9ea246ff55134205f7ac3af0d9599e641
parent70937611e34577151a6607640050e8b164a54e70 (diff)
downloadipecamera-2e7a7a3534bcf591c0d6c65105b2d845f293112f.tar.gz
ipecamera-2e7a7a3534bcf591c0d6c65105b2d845f293112f.tar.bz2
ipecamera-2e7a7a3534bcf591c0d6c65105b2d845f293112f.tar.xz
ipecamera-2e7a7a3534bcf591c0d6c65105b2d845f293112f.zip
North West Logick DMA implementation
-rw-r--r--Makefile2
-rw-r--r--cli.c42
-rw-r--r--common.mk2
-rw-r--r--dma.c197
-rw-r--r--dma.h18
-rw-r--r--dma/nwl.c662
-rw-r--r--dma/nwl.h8
-rw-r--r--dma/nwl_defines.h145
-rw-r--r--driver/Makefile4
-rw-r--r--driver/base.c12
-rw-r--r--driver/common.h4
-rw-r--r--driver/config.h2
-rw-r--r--driver/ioctl.c7
-rw-r--r--driver/kmem.c48
-rw-r--r--driver/pciDriver.h14
-rw-r--r--kmem.c166
-rw-r--r--kmem.h47
-rw-r--r--pci.c111
-rw-r--r--pci.h43
-rw-r--r--pcilib.h33
-rw-r--r--pcilib_types.h25
-rw-r--r--tools.h2
22 files changed, 1367 insertions, 227 deletions
diff --git a/Makefile b/Makefile
index c5efffa..f39abf5 100644
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@ include common.mk
###############################################################
# Target definitions
-OBJECTS = pci.o default.o tools.o dma/nwl.o ipecamera/model.o ipecamera/image.o
+OBJECTS = pci.o kmem.o dma.o default.o tools.o dma/nwl.o ipecamera/model.o ipecamera/image.o
libpcilib.so: $(OBJECTS)
echo -e "LD \t$@"
diff --git a/cli.c b/cli.c
index 919d50e..123c54e 100644
--- a/cli.c
+++ b/cli.c
@@ -90,7 +90,7 @@ static struct option long_options[] = {
{"info", no_argument, 0, OPT_INFO },
{"list", no_argument, 0, OPT_LIST },
{"reset", no_argument, 0, OPT_RESET },
- {"benchmark", no_argument, 0, OPT_BENCHMARK },
+ {"benchmark", optional_argument, 0, OPT_BENCHMARK },
{"read", optional_argument, 0, OPT_READ },
{"write", optional_argument, 0, OPT_WRITE },
{"grab", optional_argument, 0, OPT_GRAB },
@@ -120,7 +120,7 @@ void Usage(int argc, char *argv[], const char *format, ...) {
" Modes:\n"
" -i - Device Info\n"
" -l - List Data Banks & Registers\n"
-" -p - Performance Evaluation\n"
+" -p <barX|dmaX> - Performance Evaluation\n"
" -r <addr|reg|dmaX> - Read Data/Register\n"
" -w <addr|reg|dmaX> - Write Data/Register\n"
" -g [event] - Grab Event\n"
@@ -302,15 +302,41 @@ void Info(pcilib_t *handle, pcilib_model_t model) {
}
-int Benchmark(pcilib_t *handle, pcilib_bar_t bar) {
+int Benchmark(pcilib_t *handle, ACCESS_MODE mode, pcilib_dma_addr_t dma, pcilib_bar_t bar) {
int err;
int i, errors;
void *data, *buf, *check;
struct timeval start, end;
unsigned long time;
unsigned int size, max_size;
+ double mbs_in, mbs_out, mbs;
const pcilib_board_info_t *board_info = pcilib_get_board_info(handle);
+
+ if (mode == ACCESS_DMA) {
+ for (size = 1024 ; size < 16 * 1024 * 1024; size *= 4) {
+ mbs_in = pcilib_benchmark_dma(handle, dma, 0, size, BENCHMARK_ITERATIONS, PCILIB_DMA_FROM_DEVICE);
+ mbs_out = pcilib_benchmark_dma(handle, dma, 0, size, BENCHMARK_ITERATIONS, PCILIB_DMA_TO_DEVICE);
+ mbs = pcilib_benchmark_dma(handle, dma, 0, size, BENCHMARK_ITERATIONS, PCILIB_DMA_BIDIRECTIONAL);
+ printf("%8i KB - ", size / 1024);
+
+ printf("RW: ");
+ if (mbs < 0) printf("failed ... ");
+ else printf("%8.2lf MB/s", mbs);
+
+ printf(", R: ");
+ if (mbs_in < 0) printf("failed ... ");
+ else printf("%8.2lf MB/s", mbs_in);
+
+ printf(", W: ");
+ if (mbs_out < 0) printf("failed ... ");
+ else printf("%8.2lf MB/s", mbs_out);
+
+ printf("\n");
+ }
+
+ return 0;
+ }
if (bar < 0) {
unsigned long maxlength = 0;
@@ -425,7 +451,7 @@ int ReadData(pcilib_t *handle, ACCESS_MODE mode, pcilib_dma_addr_t dma, pcilib_b
if (mode == ACCESS_DMA) {
pcilib_dma_t dmaid = pcilib_find_dma_by_addr(handle, PCILIB_DMA_FROM_DEVICE, dma);
if (dmaid == PCILIB_DMA_INVALID) Error("Invalid DMA engine (%lu) is specified", dma);
- pcilib_read_dma(handle, dmaid, size, buf);
+ pcilib_read_dma(handle, dmaid, addr, size, buf);
addr = 0;
} else {
@@ -748,6 +774,9 @@ int main(int argc, char **argv) {
if (mode != MODE_INVALID) Usage(argc, argv, "Multiple operations are not supported");
mode = MODE_BENCHMARK;
+
+ if (optarg) addr = optarg;
+ else if ((optind < argc)&&(argv[optind][0] != '-')) addr = argv[optind++];
break;
case OPT_READ:
if (mode != MODE_INVALID) Usage(argc, argv, "Multiple operations are not supported");
@@ -870,6 +899,9 @@ int main(int argc, char **argv) {
if (!strncmp(addr, "dma", 3)) {
dma = atoi(addr + 3);
amode = ACCESS_DMA;
+ } else if (!strncmp(addr, "bar", 3)) {
+ bar = atoi(addr + 3);
+ amode = ACCESS_DMA;
} else if ((isxnumber(addr))&&(sscanf(addr, "%lx", &start) == 1)) {
// check if the address in the register range
pcilib_register_range_t *ranges = pcilib_model[model].ranges;
@@ -914,7 +946,7 @@ int main(int argc, char **argv) {
List(handle, model, bank);
break;
case MODE_BENCHMARK:
- Benchmark(handle, bar);
+ Benchmark(handle, amode, dma, bar);
break;
case MODE_READ:
if (addr) {
diff --git a/common.mk b/common.mk
index e203330..a9ee469 100644
--- a/common.mk
+++ b/common.mk
@@ -1,6 +1,6 @@
# Compiler and default flags
CC ?= gcc
-CFLAGS ?= -O2
+CFLAGS ?= -O0
# Defaults for directories
diff --git a/dma.c b/dma.c
new file mode 100644
index 0000000..3d57293
--- /dev/null
+++ b/dma.c
@@ -0,0 +1,197 @@
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "error.h"
+#include "pcilib.h"
+#include "pci.h"
+#include "dma.h"
+
+const pcilib_dma_info_t *pcilib_get_dma_info(pcilib_t *ctx) {
+ if (!ctx->dma_ctx) {
+ pcilib_model_t model = pcilib_get_model(ctx);
+ pcilib_dma_api_description_t *api = pcilib_model[model].dma_api;
+
+ if ((api)&&(api->init)) {
+ pcilib_map_register_space(ctx);
+ ctx->dma_ctx = api->init(ctx);
+ }
+
+ if (!ctx->dma_ctx) return NULL;
+ }
+
+ return &ctx->dma_info;
+}
+
+pcilib_dma_t pcilib_find_dma_by_addr(pcilib_t *ctx, pcilib_dma_direction_t direction, pcilib_dma_addr_t dma) {
+ pcilib_dma_t i;
+
+ const pcilib_dma_info_t *info = pcilib_get_dma_info(ctx);
+ if (!info) {
+ pcilib_error("DMA Engine is not configured in the current model");
+ return PCILIB_ERROR_NOTSUPPORTED;
+ }
+
+ for (i = 0; info->engines[i]; i++) {
+ if ((info->engines[i]->addr == dma)&&((info->engines[i]->direction&direction)==direction)) break;
+ }
+
+ if (info->engines[i]) return i;
+ return PCILIB_DMA_INVALID;
+}
+
+int pcilib_set_dma_engine_description(pcilib_t *ctx, pcilib_dma_t engine, pcilib_dma_engine_description_t *desc) {
+ ctx->dma_info.engines[engine] = desc;
+}
+
+typedef struct {
+ size_t size;
+ void *data;
+ size_t pos;
+} pcilib_dma_read_callback_context_t;
+
+static int pcilib_dma_read_callback(void *arg, pcilib_dma_flags_t flags, size_t bufsize, void *buf) {
+ pcilib_dma_read_callback_context_t *ctx = (pcilib_dma_read_callback_context_t*)arg;
+
+ if (ctx->pos + bufsize > ctx->size) {
+ pcilib_error("Buffer size (%li) is not large enough for DMA packet, at least %li bytes is required", ctx->size, ctx->pos + bufsize);
+ return PCILIB_ERROR_INVALID_DATA;
+ }
+
+ memcpy(ctx->data + ctx->pos, buf, bufsize);
+ ctx->pos += bufsize;
+
+ if (flags & PCILIB_DMA_FLAG_EOP) return 0;
+ return 1;
+}
+
+static int pcilib_dma_skip_callback(void *arg, pcilib_dma_flags_t flags, size_t bufsize, void *buf) {
+ return 1;
+}
+
+size_t pcilib_stream_dma(pcilib_t *ctx, pcilib_dma_t dma, uintptr_t addr, size_t size, pcilib_dma_flags_t flags, size_t timeout, pcilib_dma_callback_t cb, void *cbattr) {
+ int err;
+
+ const pcilib_dma_info_t *info = pcilib_get_dma_info(ctx);
+ if (!info) {
+ pcilib_error("DMA is not supported by the device");
+ return 0;
+ }
+
+ if (!ctx->model_info->dma_api) {
+ pcilib_error("DMA Engine is not configured in the current model");
+ return 0;
+ }
+
+ if (!ctx->model_info->dma_api->stream) {
+ pcilib_error("The DMA read is not supported by configured DMA engine");
+ return 0;
+ }
+
+ if (!info->engines[dma]) {
+ pcilib_error("The DMA engine (%i) is not supported by device", dma);
+ return 0;
+ }
+
+ if (info->engines[dma]->direction&PCILIB_DMA_FROM_DEVICE == 0) {
+ pcilib_error("The selected engine (%i) is S2C-only and does not support reading", dma);
+ return 0;
+ }
+
+ return ctx->model_info->dma_api->stream(ctx->dma_ctx, dma, addr, size, flags, timeout, cb, cbattr);
+}
+
+size_t pcilib_read_dma(pcilib_t *ctx, pcilib_dma_t dma, uintptr_t addr, size_t size, void *buf) {
+ int err;
+
+ pcilib_dma_read_callback_context_t opts = {
+ size, buf, 0
+ };
+
+ return pcilib_stream_dma(ctx, dma, addr, size, PCILIB_DMA_FLAGS_DEFAULT, PCILIB_DMA_TIMEOUT, pcilib_dma_read_callback, &opts);
+}
+
+int pcilib_skip_dma(pcilib_t *ctx, pcilib_dma_t dma) {
+ size_t skipped;
+ do {
+ // IMMEDIATE timeout is not working properly, so default is set
+ skipped = pcilib_stream_dma(ctx, dma, 0, 0, PCILIB_DMA_FLAGS_DEFAULT, PCILIB_DMA_TIMEOUT, pcilib_dma_skip_callback, NULL);
+ } while (skipped > 0);
+
+ return 0;
+}
+
+
+size_t pcilib_push_dma(pcilib_t *ctx, pcilib_dma_t dma, uintptr_t addr, size_t size, pcilib_dma_flags_t flags, size_t timeout, void *buf) {
+ int err;
+
+ const pcilib_dma_info_t *info = pcilib_get_dma_info(ctx);
+ if (!info) {
+ pcilib_error("DMA is not supported by the device");
+ return 0;
+ }
+
+ if (!ctx->model_info->dma_api) {
+ pcilib_error("DMA Engine is not configured in the current model");
+ return 0;
+ }
+
+ if (!ctx->model_info->dma_api->push) {
+ pcilib_error("The DMA write is not supported by configured DMA engine");
+ return 0;
+ }
+
+ if (!info->engines[dma]) {
+ pcilib_error("The DMA engine (%i) is not supported by device", dma);
+ return 0;
+ }
+
+ if (info->engines[dma]->direction&PCILIB_DMA_TO_DEVICE == 0) {
+ pcilib_error("The selected engine (%i) is C2S-only and does not support writes", dma);
+ return 0;
+ }
+
+ return ctx->model_info->dma_api->push(ctx->dma_ctx, dma, addr, size, flags, timeout, buf);
+}
+
+
+size_t pcilib_write_dma(pcilib_t *ctx, pcilib_dma_t dma, uintptr_t addr, size_t size, void *buf) {
+ return pcilib_push_dma(ctx, dma, addr, size, PCILIB_DMA_FLAG_EOP, PCILIB_DMA_TIMEOUT, buf);
+}
+
+double pcilib_benchmark_dma(pcilib_t *ctx, pcilib_dma_addr_t dma, uintptr_t addr, size_t size, size_t iterations, pcilib_dma_direction_t direction) {
+ int err;
+
+ const pcilib_dma_info_t *info = pcilib_get_dma_info(ctx);
+ if (!info) {
+ pcilib_error("DMA is not supported by the device");
+ return 0;
+ }
+
+ if (!ctx->model_info->dma_api) {
+ pcilib_error("DMA Engine is not configured in the current model");
+ return -1;
+ }
+
+ if (!ctx->model_info->dma_api->benchmark) {
+ pcilib_error("The DMA benchmark is not supported by configured DMA engine");
+ return -1;
+ }
+
+ if (!info->engines[dma]) {
+ pcilib_error("The DMA engine (%i) is not supported by device", dma);
+ return -1;
+ }
+
+ return ctx->model_info->dma_api->benchmark(ctx->dma_ctx, dma, addr, size, iterations, direction);
+}
diff --git a/dma.h b/dma.h
new file mode 100644
index 0000000..8025089
--- /dev/null
+++ b/dma.h
@@ -0,0 +1,18 @@
+#ifndef _PCILIB_DMA_H
+#define _PCILIB_DMA_H
+
+#define PCILIB_DMA_BUFFER_INVALID ((size_t)-1)
+
+struct pcilib_dma_api_description_s {
+ pcilib_dma_context_t *(*init)(pcilib_t *ctx);
+ void (*free)(pcilib_dma_context_t *ctx);
+
+ size_t (*push)(pcilib_dma_context_t *ctx, pcilib_dma_t dma, uintptr_t addr, size_t size, pcilib_dma_flags_t flags, size_t timeout, void *buf);
+ size_t (*stream)(pcilib_dma_context_t *ctx, pcilib_dma_t dma, uintptr_t addr, size_t size, pcilib_dma_flags_t flags, size_t timeout, pcilib_dma_callback_t cb, void *cbattr);
+
+ double (*benchmark)(pcilib_dma_context_t *ctx, pcilib_dma_addr_t dma, uintptr_t addr, size_t size, size_t iterations, pcilib_dma_direction_t direction);
+};
+
+int pcilib_set_dma_engine_description(pcilib_t *ctx, pcilib_dma_t engine, pcilib_dma_engine_description_t *desc);
+
+#endif /* _PCILIB_DMA_H */
diff --git a/dma/nwl.c b/dma/nwl.c
index 2c4fc04..de2c4e3 100644
--- a/dma/nwl.c
+++ b/dma/nwl.c
@@ -1,112 +1,23 @@
#define _PCILIB_DMA_NWL_C
+#define _BSD_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
#include <sys/time.h>
#include "pci.h"
+#include "dma.h"
#include "pcilib.h"
#include "error.h"
#include "tools.h"
#include "nwl.h"
-/* Common DMA registers */
-#define REG_DMA_CTRL_STATUS 0x4000 /**< DMA Common Ctrl & Status */
-
-/* These engine registers are applicable to both S2C and C2S channels.
- * Register field mask and shift definitions are later in this file.
- */
-
-#define REG_DMA_ENG_CAP 0x00000000 /**< DMA Engine Capabilities */
-#define REG_DMA_ENG_CTRL_STATUS 0x00000004 /**< DMA Engine Control */
-#define REG_DMA_ENG_NEXT_BD 0x00000008 /**< HW Next desc pointer */
-#define REG_SW_NEXT_BD 0x0000000C /**< SW Next desc pointer */
-#define REG_DMA_ENG_LAST_BD 0x00000010 /**< HW Last completed pointer */
-#define REG_DMA_ENG_ACTIVE_TIME 0x00000014 /**< DMA Engine Active Time */
-#define REG_DMA_ENG_WAIT_TIME 0x00000018 /**< DMA Engine Wait Time */
-#define REG_DMA_ENG_COMP_BYTES 0x0000001C /**< DMA Engine Completed Bytes */
-
-/* Register masks. The following constants define bit locations of various
- * control bits in the registers. For further information on the meaning of
- * the various bit masks, refer to the hardware spec.
- *
- * Masks have been written assuming HW bits 0-31 correspond to SW bits 0-31
- */
-
-/** @name Bitmasks of REG_DMA_CTRL_STATUS register.
- * @{
- */
-#define DMA_INT_ENABLE 0x00000001 /**< Enable global interrupts */
-#define DMA_INT_DISABLE 0x00000000 /**< Disable interrupts */
-#define DMA_INT_ACTIVE_MASK 0x00000002 /**< Interrupt active? */
-#define DMA_INT_PENDING_MASK 0x00000004 /**< Engine interrupt pending */
-#define DMA_INT_MSI_MODE 0x00000008 /**< MSI or Legacy mode? */
-#define DMA_USER_INT_ENABLE 0x00000010 /**< Enable user interrupts */
-#define DMA_USER_INT_ACTIVE_MASK 0x00000020 /**< Int - user interrupt */
-#define DMA_USER_INT_ACK 0x00000020 /**< Acknowledge */
-#define DMA_MPS_USED 0x00000700 /**< MPS Used */
-#define DMA_MRRS_USED 0x00007000 /**< MRRS Used */
-#define DMA_S2C_ENG_INT_VAL 0x00FF0000 /**< IRQ value of 1st 8 S2Cs */
-#define DMA_C2S_ENG_INT_VAL 0xFF000000 /**< IRQ value of 1st 8 C2Ss */
-
-/** @name Bitmasks of REG_DMA_ENG_CAP register.
- * @{
- */
-/* DMA engine characteristics */
-#define DMA_ENG_PRESENT_MASK 0x00000001 /**< DMA engine present? */
-#define DMA_ENG_DIRECTION_MASK 0x00000002 /**< DMA engine direction */
-#define DMA_ENG_C2S 0x00000002 /**< DMA engine - C2S */
-#define DMA_ENG_S2C 0x00000000 /**< DMA engine - S2C */
-#define DMA_ENG_TYPE_MASK 0x00000030 /**< DMA engine type */
-#define DMA_ENG_BLOCK 0x00000000 /**< DMA engine - Block type */
-#define DMA_ENG_PACKET 0x00000010 /**< DMA engine - Packet type */
-#define DMA_ENG_NUMBER 0x0000FF00 /**< DMA engine number */
-#define DMA_ENG_BD_MAX_BC 0x3F000000 /**< DMA engine max buffer size */
-
-
-/* Shift constants for selected masks */
-#define DMA_ENG_NUMBER_SHIFT 8
-#define DMA_ENG_BD_MAX_BC_SHIFT 24
-
-/** @name Bitmasks of REG_DMA_ENG_CTRL_STATUS register.
- * @{
- */
-/* Interrupt activity and acknowledgement bits */
-#define DMA_ENG_INT_ENABLE 0x00000001 /**< Enable interrupts */
-#define DMA_ENG_INT_DISABLE 0x00000000 /**< Disable interrupts */
-#define DMA_ENG_INT_ACTIVE_MASK 0x00000002 /**< Interrupt active? */
-#define DMA_ENG_INT_ACK 0x00000002 /**< Interrupt ack */
-#define DMA_ENG_INT_BDCOMP 0x00000004 /**< Int - BD completion */
-#define DMA_ENG_INT_BDCOMP_ACK 0x00000004 /**< Acknowledge */
-#define DMA_ENG_INT_ALERR 0x00000008 /**< Int - BD align error */
-#define DMA_ENG_INT_ALERR_ACK 0x00000008 /**< Acknowledge */
-#define DMA_ENG_INT_FETERR 0x00000010 /**< Int - BD fetch error */
-#define DMA_ENG_INT_FETERR_ACK 0x00000010 /**< Acknowledge */
-#define DMA_ENG_INT_ABORTERR 0x00000020 /**< Int - DMA abort error */
-#define DMA_ENG_INT_ABORTERR_ACK 0x00000020 /**< Acknowledge */
-#define DMA_ENG_INT_CHAINEND 0x00000080 /**< Int - BD chain ended */
-#define DMA_ENG_INT_CHAINEND_ACK 0x00000080 /**< Acknowledge */
-
-/* DMA engine control */
-#define DMA_ENG_ENABLE_MASK 0x00000100 /**< DMA enabled? */
-#define DMA_ENG_ENABLE 0x00000100 /**< Enable DMA */
-#define DMA_ENG_DISABLE 0x00000000 /**< Disable DMA */
-#define DMA_ENG_STATE_MASK 0x00000C00 /**< Current DMA state? */
-#define DMA_ENG_RUNNING 0x00000400 /**< DMA running */
-#define DMA_ENG_IDLE 0x00000000 /**< DMA idle */
-#define DMA_ENG_WAITING 0x00000800 /**< DMA waiting */
-#define DMA_ENG_STATE_WAITED 0x00001000 /**< DMA waited earlier */
-#define DMA_ENG_WAITED_ACK 0x00001000 /**< Acknowledge */
-#define DMA_ENG_USER_RESET 0x00004000 /**< Reset only user logic */
-#define DMA_ENG_RESET 0x00008000 /**< Reset DMA engine + user */
-
-#define DMA_ENG_ALLINT_MASK 0x000000BE /**< To get only int events */
-
-#define DMA_ENGINE_PER_SIZE 0x100 /**< Separation between engine regs */
-#define DMA_OFFSET 0 /**< Starting register offset */
- /**< Size of DMA engine reg space */
-#define DMA_SIZE (MAX_DMA_ENGINES * DMA_ENGINE_PER_SIZE)
+#include "nwl_defines.h"
+
+
+#define NWL_FIX_EOP_FOR_BIG_PACKETS // requires precise sizes in read requests
/*
pcilib_register_bank_description_t ipecamera_register_banks[] = {
@@ -123,6 +34,15 @@ pcilib_register_description_t dma_nwl_registers[] = {
typedef struct {
pcilib_dma_engine_description_t desc;
char *base_addr;
+
+ size_t ring_size, page_size;
+ size_t head, tail;
+ pcilib_kmem_handle_t *ring;
+ pcilib_kmem_handle_t *pages;
+
+ int started; // indicates if DMA buffers are initialized and reading is allowed
+ int writting; // indicates if we are in middle of writting packet
+
} pcilib_nwl_engine_description_t;
@@ -180,13 +100,26 @@ static int nwl_stop_engine(nwl_dma_t *ctx, pcilib_dma_t dma) {
pcilib_nwl_engine_description_t *info = ctx->engines + dma;
char *base = ctx->engines[dma].base_addr;
-
+
+ if (info->desc.addr == 1) {
+ // Stop Generators
+ nwl_read_register(val, ctx, ctx->base_addr, TX_CONFIG_ADDRESS);
+ val = ~(LOOPBACK|PKTCHKR|PKTGENR);
+ nwl_write_register(val, ctx, ctx->base_addr, TX_CONFIG_ADDRESS);
+
+ nwl_read_register(val, ctx, ctx->base_addr, RX_CONFIG_ADDRESS);
+ val = ~(LOOPBACK|PKTCHKR|PKTGENR);
+ nwl_write_register(val, ctx, ctx->base_addr, RX_CONFIG_ADDRESS);
+
+ // Skip everything in read queue (could be we need to start and skip as well)
+ if (info->started) pcilib_skip_dma(ctx->pcilib, dma);
+ }
+
// Disable IRQ
nwl_read_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
val &= ~(DMA_ENG_INT_ENABLE);
nwl_write_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
-
// Reseting
val = DMA_ENG_DISABLE|DMA_ENG_USER_RESET; nwl_write_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
gettimeofday(&start, NULL);
@@ -195,11 +128,10 @@ static int nwl_stop_engine(nwl_dma_t *ctx, pcilib_dma_t dma) {
gettimeofday(&cur, NULL);
} while ((val & (DMA_ENG_STATE_MASK|DMA_ENG_USER_RESET))&&(((cur.tv_sec - start.tv_sec)*1000000 + (cur.tv_usec - start.tv_usec)) < PCILIB_REGISTER_TIMEOUT));
- if (val & DMA_ENG_RESET) {
+ if (val & (DMA_ENG_STATE_MASK|DMA_ENG_USER_RESET)) {
pcilib_error("Timeout during reset of DMA engine %i", info->desc.addr);
return PCILIB_ERROR_TIMEOUT;
}
-
val = DMA_ENG_RESET; nwl_write_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
gettimeofday(&start, NULL);
@@ -219,12 +151,26 @@ static int nwl_stop_engine(nwl_dma_t *ctx, pcilib_dma_t dma) {
nwl_write_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
}
+ // Clean buffers
+ if (info->ring) {
+ pcilib_free_kernel_memory(ctx->pcilib, info->ring);
+ info->ring = NULL;
+ }
+
+ if (info->pages) {
+ pcilib_free_kernel_memory(ctx->pcilib, info->pages);
+ info->pages = NULL;
+ }
+
+ info->started = 0;
+
return 0;
}
pcilib_dma_context_t *dma_nwl_init(pcilib_t *pcilib) {
int i;
int err;
+ uint32_t val;
pcilib_dma_t n_engines;
pcilib_model_description_t *model_info = pcilib_get_model_description(pcilib);
@@ -243,8 +189,15 @@ pcilib_dma_context_t *dma_nwl_init(pcilib_t *pcilib) {
ctx->dma_bank = model_info->banks + dma_bank;
ctx->base_addr = pcilib_resolve_register_address(pcilib, ctx->dma_bank->bar, ctx->dma_bank->read_addr);
+ val = 0;
+ nwl_read_register(val, ctx, ctx->base_addr, TX_CONFIG_ADDRESS);
+ nwl_read_register(val, ctx, ctx->base_addr, RX_CONFIG_ADDRESS);
+
for (i = 0, n_engines = 0; i < 2 * PCILIB_MAX_DMA_ENGINES; i++) {
char *addr = ctx->base_addr + DMA_OFFSET + i * DMA_ENGINE_PER_SIZE;
+
+ memset(ctx->engines + n_engines, 0, sizeof(pcilib_nwl_engine_description_t));
+
err = nwl_read_engine_config(ctx, ctx->engines + n_engines, addr);
if (!err) err = nwl_stop_engine(ctx, n_engines);
if (!err) {
@@ -270,7 +223,512 @@ void dma_nwl_free(pcilib_dma_context_t *vctx) {
}
}
-int dma_nwl_read(pcilib_dma_context_t *vctx, pcilib_dma_t dma, size_t size, void *buf) {
+#define PCILIB_NWL_ALIGNMENT 64 // in bytes
+#define PCILIB_NWL_DMA_DESCRIPTOR_SIZE 64 // in bytes
+#define PCILIB_NWL_DMA_PAGES 512 // 1024
+
+#define NWL_RING_GET(data, offset) *(uint32_t*)(((char*)(data)) + (offset))
+#define NWL_RING_SET(data, offset, val) *(uint32_t*)(((char*)(data)) + (offset)) = (val)
+#define NWL_RING_UPDATE(data, offset, mask, val) *(uint32_t*)(((char*)(data)) + (offset)) = ((*(uint32_t*)(((char*)(data)) + (offset)))&(mask))|(val)
+
+
+int dma_nwl_sync_buffers(nwl_dma_t *ctx, pcilib_nwl_engine_description_t *info, pcilib_kmem_handle_t *kmem) {
+ switch (info->desc.direction) {
+ case PCILIB_DMA_FROM_DEVICE:
+ return pcilib_sync_kernel_memory(ctx->pcilib, kmem, PCILIB_KMEM_SYNC_FROMDEVICE);
+ case PCILIB_DMA_TO_DEVICE:
+ return pcilib_sync_kernel_memory(ctx->pcilib, kmem, PCILIB_KMEM_SYNC_TODEVICE);
+ }
+
+ return 0;
+}
+
+int dma_nwl_allocate_engine_buffers(nwl_dma_t *ctx, pcilib_nwl_engine_description_t *info) {
+ int err = 0;
+
+ int i;
+ uint32_t val;
+ uint32_t buf_sz;
+ uint64_t buf_pa;
+
+ char *base = info->base_addr;
+
+ if (info->pages) return 0;
+
+ pcilib_kmem_handle_t *ring = pcilib_alloc_kernel_memory(ctx->pcilib, PCILIB_KMEM_TYPE_CONSISTENT, 1, PCILIB_NWL_DMA_PAGES * PCILIB_NWL_DMA_DESCRIPTOR_SIZE, PCILIB_NWL_ALIGNMENT, PCILIB_KMEM_USE(PCILIB_KMEM_USE_DMA, info->desc.addr), 0);
+ pcilib_kmem_handle_t *pages = pcilib_alloc_kernel_memory(ctx->pcilib, PCILIB_KMEM_TYPE_PAGE, PCILIB_NWL_DMA_PAGES, 0, PCILIB_NWL_ALIGNMENT, PCILIB_KMEM_USE(PCILIB_KMEM_USE_DMA, info->desc.addr), 0);
+
+ if ((ring)&&(pages)) err = dma_nwl_sync_buffers(ctx, info, pages);
+ else err = PCILIB_ERROR_FAILED;
+
+
+ if (err) {
+ if (pages) pcilib_free_kernel_memory(ctx->pcilib, pages);
+ if (ring) pcilib_free_kernel_memory(ctx->pcilib, ring);
+ return err;
+ }
+
+ unsigned char *data = (unsigned char*)pcilib_kmem_get_ua(ctx->pcilib, ring);
+ uint32_t ring_pa = pcilib_kmem_get_pa(ctx->pcilib, ring);
+
+ memset(data, 0, PCILIB_NWL_DMA_PAGES * PCILIB_NWL_DMA_DESCRIPTOR_SIZE);
+
+ for (i = 0; i < PCILIB_NWL_DMA_PAGES; i++, data += PCILIB_NWL_DMA_DESCRIPTOR_SIZE) {
+ buf_pa = pcilib_kmem_get_block_pa(ctx->pcilib, pages, i);
+ buf_sz = pcilib_kmem_get_block_size(ctx->pcilib, pages, i);
+
+ NWL_RING_SET(data, DMA_BD_NDESC_OFFSET, ring_pa + ((i + 1) % PCILIB_NWL_DMA_PAGES) * PCILIB_NWL_DMA_DESCRIPTOR_SIZE);
+ NWL_RING_SET(data, DMA_BD_BUFAL_OFFSET, buf_pa&0xFFFFFFFF);
+ NWL_RING_SET(data, DMA_BD_BUFAH_OFFSET, buf_pa>>32);
+ NWL_RING_SET(data, DMA_BD_BUFL_CTRL_OFFSET, buf_sz);
+/*
+ if (info->desc.direction == PCILIB_DMA_TO_DEVICE) {
+ NWL_RING_SET(data, DMA_BD_BUFL_STATUS_OFFSET, buf_sz);
+ }
+*/
+ }
+
+ val = ring_pa;
+ nwl_write_register(val, ctx, base, REG_DMA_ENG_NEXT_BD);
+ nwl_write_register(val, ctx, base, REG_SW_NEXT_BD);
+
+ info->ring = ring;
+ info->pages = pages;
+ info->page_size = buf_sz;
+ info->ring_size = PCILIB_NWL_DMA_PAGES;
+
+ info->head = 0;
+ info->tail = 0;
+
+ return 0;
+}
+
+static int dma_nwl_start(nwl_dma_t *ctx, pcilib_nwl_engine_description_t *info) {
+ int err;
+ uint32_t ring_pa;
+ uint32_t val;
+
+ if (info->started) return 0;
+
+ err = dma_nwl_allocate_engine_buffers(ctx, info);
+ if (err) return err;
+
+ ring_pa = pcilib_kmem_get_pa(ctx->pcilib, info->ring);
+ nwl_write_register(ring_pa, ctx, info->base_addr, REG_DMA_ENG_NEXT_BD);
+ nwl_write_register(ring_pa, ctx, info->base_addr, REG_SW_NEXT_BD);
+
+ __sync_synchronize();
+
+ nwl_read_register(val, ctx, info->base_addr, REG_DMA_ENG_CTRL_STATUS);
+ val |= (DMA_ENG_ENABLE);
+ nwl_write_register(val, ctx, info->base_addr, REG_DMA_ENG_CTRL_STATUS);
+
+ __sync_synchronize();
+
+ if (info->desc.direction == PCILIB_DMA_FROM_DEVICE) {
+ ring_pa += (info->ring_size - 1) * PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
+ nwl_write_register(ring_pa, ctx, info->base_addr, REG_SW_NEXT_BD);
+// nwl_read_register(val, ctx, info->base_addr, 0x18);
+
+ info->tail = 0;
+ info->head = (info->ring_size - 1);
+ } else {
+ info->tail = 0;
+ info->head = 0;
+ }
+
+ info->started = 1;
+
+ return 0;
+}
+
+static size_t dma_nwl_clean_buffers(nwl_dma_t * ctx, pcilib_nwl_engine_description_t *info) {
+ size_t res = 0;
+ uint32_t status, control;
+
+ unsigned char *ring = pcilib_kmem_get_ua(ctx->pcilib, info->ring);
+ ring += info->tail * PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
+
+next_buffer:
+ status = NWL_RING_GET(ring, DMA_BD_BUFL_STATUS_OFFSET)&DMA_BD_STATUS_MASK;
+// control = NWL_RING_GET(ring, DMA_BD_BUFL_CTRL_OFFSET)&DMA_BD_CTRL_MASK;
+
+ if (status & DMA_BD_ERROR_MASK) {
+ pcilib_error("NWL DMA Engine reported error in ring descriptor");
+ return (size_t)-1;
+ }
+
+ if (status & DMA_BD_SHORT_MASK) {
+ pcilib_error("NWL DMA Engine reported short error");
+ return (size_t)-1;
+ }
+
+ if (status & DMA_BD_COMP_MASK) {
+ info->tail++;
+ if (info->tail == info->ring_size) {
+ ring -= (info->tail - 1) * PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
+ info->tail = 0;
+ } else {
+ ring += PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
+ }
+
+ res++;
+
+ if (info->tail != info->head) goto next_buffer;
+ }
+
+// printf("====> Cleaned: %i\n", res);
+ return res;
+}
+
+
+static size_t dma_nwl_get_next_buffer(nwl_dma_t * ctx, pcilib_nwl_engine_description_t *info, size_t n_buffers, size_t timeout) {
+ struct timeval start, cur;
+
+ size_t res, n = 0;
+ size_t head;
+
+ for (head = info->head; (((head + 1)%info->ring_size) != info->tail)&&(n < n_buffers); head++, n++);
+ if (n == n_buffers) return info->head;
+
+ gettimeofday(&start, NULL);
+
+ res = dma_nwl_clean_buffers(ctx, info);
+ if (res == (size_t)-1) return PCILIB_DMA_BUFFER_INVALID;
+ else n += res;
+
+
+ while (n < n_buffers) {
+ if (timeout != PCILIB_TIMEOUT_INFINITE) {
+ gettimeofday(&cur, NULL);
+ if (((cur.tv_sec - start.tv_sec)*1000000 + (cur.tv_usec - start.tv_usec)) > timeout) break;
+ }
+
+ usleep (10);
+
+ res = dma_nwl_clean_buffers(ctx, info);
+ if (res == (size_t)-1) return PCILIB_DMA_BUFFER_INVALID;
+ else if (res > 0) {
+ gettimeofday(&start, NULL);
+ n += res;
+ }
+ }
+
+ if (n < n_buffers) return PCILIB_DMA_BUFFER_INVALID;
+
+ return info->head;
+}
+
+static int dma_nwl_push_buffer(nwl_dma_t *ctx, pcilib_nwl_engine_description_t *info, size_t size, int eop, size_t timeout) {
+ int flags;
+
+ uint32_t val;
+ unsigned char *ring = pcilib_kmem_get_ua(ctx->pcilib, info->ring);
+ uint32_t ring_pa = pcilib_kmem_get_pa(ctx->pcilib, info->ring);
+
+ ring += info->head * PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
+
+
+ if (!info->writting) {
+ flags |= DMA_BD_SOP_MASK;
+ info->writting = 1;
+ }
+ if (eop) {
+ flags |= DMA_BD_EOP_MASK;
+ info->writting = 0;
+ }
+
+ NWL_RING_SET(ring, DMA_BD_BUFL_CTRL_OFFSET, size|flags);
+ NWL_RING_SET(ring, DMA_BD_BUFL_STATUS_OFFSET, size);
+
+ info->head++;
+ if (info->head == info->ring_size) info->head = 0;
+
+ val = ring_pa + info->head * PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
+ nwl_write_register(val, ctx, info->base_addr, REG_SW_NEXT_BD);
+// nwl_read_register(val, ctx, info->base_addr, 0x18);
+
+// usleep(10000);
+
+// nwl_read_register(val, ctx, info->base_addr, REG_DMA_ENG_LAST_BD);
+// printf("Last BD(Write): %lx %lx\n", ring, val);
+
+
+ return 0;
+}
+
+
+static size_t dma_nwl_wait_buffer(nwl_dma_t *ctx, pcilib_nwl_engine_description_t *info, size_t *size, int *eop, size_t timeout) {
+ uint32_t val;
+ struct timeval start, cur;
+ uint32_t status_size, status, control;
+
+// usleep(10000);
+
+ unsigned char *ring = pcilib_kmem_get_ua(ctx->pcilib, info->ring);
+
+// status_size = NWL_RING_GET(ring, DMA_BD_BUFL_STATUS_OFFSET);
+// printf("Status0: %lx\n", status_size);
+
+ ring += info->tail * PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
+
+ gettimeofday(&start, NULL);
+
+// printf("Waiting %li\n", info->tail);
+// nwl_read_register(val, ctx, info->base_addr, REG_DMA_ENG_LAST_BD);
+// printf("Last BD(Read): %lx %lx\n", ring, val);
+
+ do {
+ status_size = NWL_RING_GET(ring, DMA_BD_BUFL_STATUS_OFFSET);
+ status = status_size & DMA_BD_STATUS_MASK;
+
+// printf("%i: %lx\n", info->tail, status_size);
+
+ if (status & DMA_BD_ERROR_MASK) {
+ pcilib_error("NWL DMA Engine reported error in ring descriptor");
+ return (size_t)-1;
+ }
+
+ if (status & DMA_BD_COMP_MASK) {
+ if (status & DMA_BD_EOP_MASK) *eop = 1;
+ else *eop = 0;
+
+ *size = status_size & DMA_BD_BUFL_MASK;
+
+// printf("Status: %lx\n", status_size);
+ return info->tail;
+ }
+
+ usleep(10);
+ gettimeofday(&cur, NULL);
+ } while ((timeout == PCILIB_TIMEOUT_INFINITE)||(((cur.tv_sec - start.tv_sec)*1000000 + (cur.tv_usec - start.tv_usec)) < timeout));
+
+// printf("Final status: %lx\n", status_size);
+
+ return (size_t)-1;
+}
+
+static int dma_nwl_return_buffer(nwl_dma_t *ctx, pcilib_nwl_engine_description_t *info) {
+ uint32_t val;
+
+ unsigned char *ring = pcilib_kmem_get_ua(ctx->pcilib, info->ring);
+ uint32_t ring_pa = pcilib_kmem_get_pa(ctx->pcilib, info->ring);
+ size_t bufsz = pcilib_kmem_get_block_size(ctx->pcilib, info->pages, info->tail);
+
+ ring += info->tail * PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
+// printf("Returning: %i\n", info->tail);
+
+ NWL_RING_SET(ring, DMA_BD_BUFL_CTRL_OFFSET, bufsz);
+ NWL_RING_SET(ring, DMA_BD_BUFL_STATUS_OFFSET, 0);
+
+ val = ring_pa + info->tail * PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
+ nwl_write_register(val, ctx, info->base_addr, REG_SW_NEXT_BD);
+// nwl_read_register(val, ctx, info->base_addr, 0x18);
+
+ info->tail++;
+ if (info->tail == info->ring_size) info->tail = 0;
+}
+
+
+size_t dma_nwl_write_fragment(pcilib_dma_context_t *vctx, pcilib_dma_t dma, uintptr_t addr, size_t size, pcilib_dma_flags_t flags, size_t timeout, void *data) {
+ int err;
+ size_t pos;
+ size_t bufnum;
+ nwl_dma_t *ctx = (nwl_dma_t*)vctx;
+
+ pcilib_nwl_engine_description_t *info = ctx->engines + dma;
+
+ err = dma_nwl_start(ctx, info);
+ if (err) return 0;
+
+ for (pos = 0; pos < size; pos += info->page_size) {
+ int block_size = min2(size - pos, info->page_size);
+
+ bufnum = dma_nwl_get_next_buffer(ctx, info, 1, timeout);
+ if (bufnum == PCILIB_DMA_BUFFER_INVALID) return pos;
+
+ //sync
+ void *buf = pcilib_kmem_get_block_ua(ctx->pcilib, info->pages, bufnum);
+ memcpy(buf, data, block_size);
+
+ err = dma_nwl_push_buffer(ctx, info, block_size, (flags&PCILIB_DMA_FLAG_EOP)&&((pos + block_size) == size), timeout);
+ if (err) return pos;
+ }
+
+ return size;
+}
+
+size_t dma_nwl_stream_read(pcilib_dma_context_t *vctx, pcilib_dma_t dma, uintptr_t addr, size_t size, pcilib_dma_flags_t flags, size_t timeout, pcilib_dma_callback_t cb, void *cbattr) {
+ int err, ret;
+ size_t res = 0;
+ size_t bufnum;
+ size_t bufsize;
+ nwl_dma_t *ctx = (nwl_dma_t*)vctx;
+
+ size_t buf_size;
+ int eop;
+
+ pcilib_nwl_engine_description_t *info = ctx->engines + dma;
+
+ err = dma_nwl_start(ctx, info);
+ if (err) return 0;
+
+ do {
+ bufnum = dma_nwl_wait_buffer(ctx, info, &bufsize, &eop, timeout);
+ if (bufnum == PCILIB_DMA_BUFFER_INVALID) return 0;
+
+#ifdef NWL_FIX_EOP_FOR_BIG_PACKETS
+ if (size > 65536) {
+// printf("%i %i\n", res + bufsize, size);
+ if ((res+bufsize) < size) eop = 0;
+ else if ((res+bufsize) == size) eop = 1;
+ }
+#endif /* NWL_FIX_EOP_FOR_BIG_PACKETS */
+
+ //sync
+ void *buf = pcilib_kmem_get_block_ua(ctx->pcilib, info->pages, bufnum);
+ ret = cb(cbattr, eop?PCILIB_DMA_FLAG_EOP:0, bufsize, buf);
+ dma_nwl_return_buffer(ctx, info);
+
+ res += bufsize;
+
+// printf("%i %i %i (%li)\n", ret, res, eop, size);
+ } while (ret);
+
+ return res;
+}
+
+double dma_nwl_benchmark(pcilib_dma_context_t *vctx, pcilib_dma_addr_t dma, uintptr_t addr, size_t size, size_t iterations, pcilib_dma_direction_t direction) {
+ int i;
+ int res;
+ int err;
+ size_t bytes;
+ uint32_t val;
+ uint32_t *buf, *cmp;
+ const char *error = NULL;
+
+ size_t us = 0;
+ struct timeval start, cur;
+
nwl_dma_t *ctx = (nwl_dma_t*)vctx;
- printf("Reading dma: %i\n", dma);
+
+ pcilib_dma_t readid = pcilib_find_dma_by_addr(ctx->pcilib, PCILIB_DMA_FROM_DEVICE, dma);
+ pcilib_dma_t writeid = pcilib_find_dma_by_addr(ctx->pcilib, PCILIB_DMA_TO_DEVICE, dma);
+
+ if (size%sizeof(uint32_t)) size = 1 + size / sizeof(uint32_t);
+ else size /= sizeof(uint32_t);
+
+
+ // Stop Generators and drain old data
+ nwl_read_register(val, ctx, ctx->base_addr, TX_CONFIG_ADDRESS);
+ val = ~(LOOPBACK|PKTCHKR|PKTGENR);
+ nwl_write_register(val, ctx, ctx->base_addr, TX_CONFIG_ADDRESS);
+
+ nwl_read_register(val, ctx, ctx->base_addr, RX_CONFIG_ADDRESS);
+ val = ~(LOOPBACK|PKTCHKR|PKTGENR);
+ nwl_write_register(val, ctx, ctx->base_addr, RX_CONFIG_ADDRESS);
+
+/*
+ nwl_stop_engine(ctx, readid);
+ nwl_stop_engine(ctx, writeid);
+
+ err = dma_nwl_start(ctx, ctx->engines + readid);
+ if (err) return -1;
+ err = dma_nwl_start(ctx, ctx->engines + writeid);
+ if (err) return -1;
+*/
+
+ __sync_synchronize();
+
+ pcilib_skip_dma(ctx->pcilib, readid);
+
+
+ // Set size and required mode
+ val = size * sizeof(uint32_t);
+ nwl_write_register(val, ctx, ctx->base_addr, PKT_SIZE_ADDRESS);
+
+ switch (direction) {
+ case PCILIB_DMA_BIDIRECTIONAL:
+ val = LOOPBACK;
+ break;
+ case PCILIB_DMA_TO_DEVICE:
+ return -1;
+ case PCILIB_DMA_FROM_DEVICE:
+ val = PKTGENR;
+ break;
+ }
+
+ nwl_write_register(val, ctx, ctx->base_addr, TX_CONFIG_ADDRESS);
+ nwl_write_register(val, ctx, ctx->base_addr, RX_CONFIG_ADDRESS);
+
+
+ // Allocate memory and prepare data
+ buf = malloc(size * sizeof(uint32_t));
+ cmp = malloc(size * sizeof(uint32_t));
+ if ((!buf)||(!cmp)) {
+ if (buf) free(buf);
+ if (cmp) free(cmp);
+ return -1;
+ }
+
+ memset(cmp, 0x13, size * sizeof(uint32_t));
+
+ // Benchmark
+ for (i = 0; i < iterations; i++) {
+// printf("Iteration: %i\n", i);
+
+ gettimeofday(&start, NULL);
+ if (direction&PCILIB_DMA_TO_DEVICE) {
+ memcpy(buf, cmp, size * sizeof(uint32_t));
+
+ bytes = pcilib_write_dma(ctx->pcilib, writeid, addr, size * sizeof(uint32_t), buf);
+ if (bytes != size * sizeof(uint32_t)) {
+ error = "Write failed";
+ break;
+ }
+ }
+
+ memset(buf, 0, size * sizeof(uint32_t));
+
+ bytes = pcilib_read_dma(ctx->pcilib, readid, addr, size * sizeof(uint32_t), buf);
+ gettimeofday(&cur, NULL);
+ us += ((cur.tv_sec - start.tv_sec)*1000000 + (cur.tv_usec - start.tv_usec));
+
+
+ if (bytes != size * sizeof(uint32_t)) {
+ printf("RF: %li %li\n", bytes, size * 4);
+ error = "Read failed";
+ break;
+ }
+
+ if (direction == PCILIB_DMA_BIDIRECTIONAL) {
+ res = memcmp(buf, cmp, size * sizeof(uint32_t));
+ if (res) {
+ error = "Written and read values does not match";
+ break;
+ }
+ }
+
+ }
+
+
+ // Stop Generators and drain data if necessary
+ nwl_read_register(val, ctx, ctx->base_addr, TX_CONFIG_ADDRESS);
+ val = ~(LOOPBACK|PKTCHKR|PKTGENR);
+ nwl_write_register(val, ctx, ctx->base_addr, TX_CONFIG_ADDRESS);
+
+ nwl_read_register(val, ctx, ctx->base_addr, RX_CONFIG_ADDRESS);
+ val = ~(LOOPBACK|PKTCHKR|PKTGENR);
+ nwl_write_register(val, ctx, ctx->base_addr, RX_CONFIG_ADDRESS);
+
+ __sync_synchronize();
+
+ if (direction == PCILIB_DMA_FROM_DEVICE) {
+ pcilib_skip_dma(ctx->pcilib, readid);
+ }
+
+ free(cmp);
+ free(buf);
+
+ return error?-1:(1. * size * sizeof(uint32_t) * iterations * 1000000) / (1024. * 1024. * us);
}
diff --git a/dma/nwl.h b/dma/nwl.h
index 8a79601..3752078 100644
--- a/dma/nwl.h
+++ b/dma/nwl.h
@@ -17,13 +17,17 @@ typedef struct {
pcilib_dma_context_t *dma_nwl_init(pcilib_t *ctx);
void dma_nwl_free(pcilib_dma_context_t *vctx);
-int dma_nwl_read(pcilib_dma_context_t *ctx, pcilib_dma_t dma, size_t size, void *buf);
+size_t dma_nwl_write_fragment(pcilib_dma_context_t *vctx, pcilib_dma_t dma, uintptr_t addr, size_t size, pcilib_dma_flags_t flags, size_t timeout, void *data);
+size_t dma_nwl_stream_read(pcilib_dma_context_t *vctx, pcilib_dma_t dma, uintptr_t addr, size_t size, pcilib_dma_flags_t flags, size_t timeout, pcilib_dma_callback_t cb, void *cbattr);
+double dma_nwl_benchmark(pcilib_dma_context_t *vctx, pcilib_dma_addr_t dma, uintptr_t addr, size_t size, size_t iterations, pcilib_dma_direction_t direction);
#ifdef _PCILIB_DMA_NWL_C
pcilib_dma_api_description_t nwl_dma_api = {
dma_nwl_init,
dma_nwl_free,
- dma_nwl_read
+ dma_nwl_write_fragment,
+ dma_nwl_stream_read,
+ dma_nwl_benchmark
};
#else
extern pcilib_dma_api_description_t nwl_dma_api;
diff --git a/dma/nwl_defines.h b/dma/nwl_defines.h
new file mode 100644
index 0000000..c1ff2eb
--- /dev/null
+++ b/dma/nwl_defines.h
@@ -0,0 +1,145 @@
+/** @name Buffer Descriptor offsets
+ * USR fields are defined by higher level IP. For example, checksum offload
+ * setup for EMAC type devices. The 1st 8 words are utilized by hardware. Any
+ * words after the 8th are for software use only.
+ * @{
+ */
+#define DMA_BD_BUFL_STATUS_OFFSET 0x00 /**< Buffer length + status */
+#define DMA_BD_USRL_OFFSET 0x04 /**< User logic specific - LSBytes */
+#define DMA_BD_USRH_OFFSET 0x08 /**< User logic specific - MSBytes */
+#define DMA_BD_CARDA_OFFSET 0x0C /**< Card address */
+#define DMA_BD_BUFL_CTRL_OFFSET 0x10 /**< Buffer length + control */
+#define DMA_BD_BUFAL_OFFSET 0x14 /**< Buffer address LSBytes */
+#define DMA_BD_BUFAH_OFFSET 0x18 /**< Buffer address MSBytes */
+#define DMA_BD_NDESC_OFFSET 0x1C /**< Next descriptor pointer */
+
+/* Bit masks for some BD fields */
+#define DMA_BD_BUFL_MASK 0x000FFFFF /**< Byte count */
+#define DMA_BD_STATUS_MASK 0xFF000000 /**< Status Flags */
+#define DMA_BD_CTRL_MASK 0xFF000000 /**< Control Flags */
+
+/* Bit masks for BD control field */
+#define DMA_BD_INT_ERROR_MASK 0x02000000 /**< Intr on error */
+#define DMA_BD_INT_COMP_MASK 0x01000000 /**< Intr on BD completion */
+
+/* Bit masks for BD status field */
+#define DMA_BD_SOP_MASK 0x80000000 /**< Start of packet */
+#define DMA_BD_EOP_MASK 0x40000000 /**< End of packet */
+#define DMA_BD_ERROR_MASK 0x10000000 /**< BD had error */
+#define DMA_BD_USER_HIGH_ZERO_MASK 0x08000000 /**< User High Status zero */
+#define DMA_BD_USER_LOW_ZERO_MASK 0x04000000 /**< User Low Status zero */
+#define DMA_BD_SHORT_MASK 0x02000000 /**< BD not fully used */
+#define DMA_BD_COMP_MASK 0x01000000 /**< BD completed */
+
+
+
+#define DMA_BD_MINIMUM_ALIGNMENT 0x40 /**< Minimum byte alignment
+
+/* Common DMA registers */
+#define REG_DMA_CTRL_STATUS 0x4000 /**< DMA Common Ctrl & Status */
+
+/* These engine registers are applicable to both S2C and C2S channels.
+ * Register field mask and shift definitions are later in this file.
+ */
+
+#define REG_DMA_ENG_CAP 0x00000000 /**< DMA Engine Capabilities */
+#define REG_DMA_ENG_CTRL_STATUS 0x00000004 /**< DMA Engine Control */
+#define REG_DMA_ENG_NEXT_BD 0x00000008 /**< HW Next desc pointer */
+#define REG_SW_NEXT_BD 0x0000000C /**< SW Next desc pointer */
+#define REG_DMA_ENG_LAST_BD 0x00000010 /**< HW Last completed pointer */
+#define REG_DMA_ENG_ACTIVE_TIME 0x00000014 /**< DMA Engine Active Time */
+#define REG_DMA_ENG_WAIT_TIME 0x00000018 /**< DMA Engine Wait Time */
+#define REG_DMA_ENG_COMP_BYTES 0x0000001C /**< DMA Engine Completed Bytes */
+
+/* Register masks. The following constants define bit locations of various
+ * control bits in the registers. For further information on the meaning of
+ * the various bit masks, refer to the hardware spec.
+ *
+ * Masks have been written assuming HW bits 0-31 correspond to SW bits 0-31
+ */
+
+/** @name Bitmasks of REG_DMA_CTRL_STATUS register.
+ * @{
+ */
+#define DMA_INT_ENABLE 0x00000001 /**< Enable global interrupts */
+#define DMA_INT_DISABLE 0x00000000 /**< Disable interrupts */
+#define DMA_INT_ACTIVE_MASK 0x00000002 /**< Interrupt active? */
+#define DMA_INT_PENDING_MASK 0x00000004 /**< Engine interrupt pending */
+#define DMA_INT_MSI_MODE 0x00000008 /**< MSI or Legacy mode? */
+#define DMA_USER_INT_ENABLE 0x00000010 /**< Enable user interrupts */
+#define DMA_USER_INT_ACTIVE_MASK 0x00000020 /**< Int - user interrupt */
+#define DMA_USER_INT_ACK 0x00000020 /**< Acknowledge */
+#define DMA_MPS_USED 0x00000700 /**< MPS Used */
+#define DMA_MRRS_USED 0x00007000 /**< MRRS Used */
+#define DMA_S2C_ENG_INT_VAL 0x00FF0000 /**< IRQ value of 1st 8 S2Cs */
+#define DMA_C2S_ENG_INT_VAL 0xFF000000 /**< IRQ value of 1st 8 C2Ss */
+
+/** @name Bitmasks of REG_DMA_ENG_CAP register.
+ * @{
+ */
+/* DMA engine characteristics */
+#define DMA_ENG_PRESENT_MASK 0x00000001 /**< DMA engine present? */
+#define DMA_ENG_DIRECTION_MASK 0x00000002 /**< DMA engine direction */
+#define DMA_ENG_C2S 0x00000002 /**< DMA engine - C2S */
+#define DMA_ENG_S2C 0x00000000 /**< DMA engine - S2C */
+#define DMA_ENG_TYPE_MASK 0x00000030 /**< DMA engine type */
+#define DMA_ENG_BLOCK 0x00000000 /**< DMA engine - Block type */
+#define DMA_ENG_PACKET 0x00000010 /**< DMA engine - Packet type */
+#define DMA_ENG_NUMBER 0x0000FF00 /**< DMA engine number */
+#define DMA_ENG_BD_MAX_BC 0x3F000000 /**< DMA engine max buffer size */
+
+
+/* Shift constants for selected masks */
+#define DMA_ENG_NUMBER_SHIFT 8
+#define DMA_ENG_BD_MAX_BC_SHIFT 24
+
+/** @name Bitmasks of REG_DMA_ENG_CTRL_STATUS register.
+ * @{
+ */
+/* Interrupt activity and acknowledgement bits */
+#define DMA_ENG_INT_ENABLE 0x00000001 /**< Enable interrupts */
+#define DMA_ENG_INT_DISABLE 0x00000000 /**< Disable interrupts */
+#define DMA_ENG_INT_ACTIVE_MASK 0x00000002 /**< Interrupt active? */
+#define DMA_ENG_INT_ACK 0x00000002 /**< Interrupt ack */
+#define DMA_ENG_INT_BDCOMP 0x00000004 /**< Int - BD completion */
+#define DMA_ENG_INT_BDCOMP_ACK 0x00000004 /**< Acknowledge */
+#define DMA_ENG_INT_ALERR 0x00000008 /**< Int - BD align error */
+#define DMA_ENG_INT_ALERR_ACK 0x00000008 /**< Acknowledge */
+#define DMA_ENG_INT_FETERR 0x00000010 /**< Int - BD fetch error */
+#define DMA_ENG_INT_FETERR_ACK 0x00000010 /**< Acknowledge */
+#define DMA_ENG_INT_ABORTERR 0x00000020 /**< Int - DMA abort error */
+#define DMA_ENG_INT_ABORTERR_ACK 0x00000020 /**< Acknowledge */
+#define DMA_ENG_INT_CHAINEND 0x00000080 /**< Int - BD chain ended */
+#define DMA_ENG_INT_CHAINEND_ACK 0x00000080 /**< Acknowledge */
+
+/* DMA engine control */
+#define DMA_ENG_ENABLE_MASK 0x00000100 /**< DMA enabled? */
+#define DMA_ENG_ENABLE 0x00000100 /**< Enable DMA */
+#define DMA_ENG_DISABLE 0x00000000 /**< Disable DMA */
+#define DMA_ENG_STATE_MASK 0x00000C00 /**< Current DMA state? */
+#define DMA_ENG_RUNNING 0x00000400 /**< DMA running */
+#define DMA_ENG_IDLE 0x00000000 /**< DMA idle */
+#define DMA_ENG_WAITING 0x00000800 /**< DMA waiting */
+#define DMA_ENG_STATE_WAITED 0x00001000 /**< DMA waited earlier */
+#define DMA_ENG_WAITED_ACK 0x00001000 /**< Acknowledge */
+#define DMA_ENG_USER_RESET 0x00004000 /**< Reset only user logic */
+#define DMA_ENG_RESET 0x00008000 /**< Reset DMA engine + user */
+
+#define DMA_ENG_ALLINT_MASK 0x000000BE /**< To get only int events */
+
+#define DMA_ENGINE_PER_SIZE 0x100 /**< Separation between engine regs */
+#define DMA_OFFSET 0 /**< Starting register offset */
+ /**< Size of DMA engine reg space */
+#define DMA_SIZE (MAX_DMA_ENGINES * DMA_ENGINE_PER_SIZE)
+
+
+#define TX_CONFIG_ADDRESS 0x9108 /* Reg for controlling TX data */
+#define RX_CONFIG_ADDRESS 0x9100 /* Reg for controlling RX pkt generator */
+#define PKT_SIZE_ADDRESS 0x9104 /* Reg for programming packet size */
+#define STATUS_ADDRESS 0x910C /* Reg for checking TX pkt checker status */
+
+/* Test start / stop conditions */
+#define PKTCHKR 0x00000001 /* Enable TX packet checker */
+#define PKTGENR 0x00000001 /* Enable RX packet generator */
+#define CHKR_MISMATCH 0x00000001 /* TX checker reported data mismatch */
+#define LOOPBACK 0x00000002 /* Enable TX data loopback onto RX */
diff --git a/driver/Makefile b/driver/Makefile
index 9f8918a..9eedc1e 100644
--- a/driver/Makefile
+++ b/driver/Makefile
@@ -6,8 +6,10 @@ KERNELDIR ?= /lib/modules/$(shell uname -r)/build
INSTALLDIR ?= /lib/modules/$(shell uname -r)/extra
PWD := $(shell pwd)
+EXTRA_CFLAGS += -I$(M)/..
+
default:
- $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
+ $(MAKE) $(CFLAGS) -C $(KERNELDIR) M=$(PWD) modules
install:
@mkdir -p $(INSTALLDIR)
diff --git a/driver/base.c b/driver/base.c
index 581e6d5..2c3ff23 100644
--- a/driver/base.c
+++ b/driver/base.c
@@ -319,6 +319,17 @@ static int __devinit pcidriver_probe(struct pci_dev *pdev, const struct pci_devi
mod_info("Couldn't enable device\n");
goto probe_pcien_fail;
}
+
+ /* Bus master & dma */
+ if ((id->vendor == PCIE_XILINX_VENDOR_ID)&&(id->device == PCIE_IPECAMERA_DEVICE_ID)) {
+ pci_set_master(pdev);
+
+ err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ if (err < 0) {
+ printk(KERN_ERR "pci_set_dma_mask failed\n");
+ goto probe_dma_fail;
+ }
+ }
/* Set Memory-Write-Invalidate support */
if ((err = pci_set_mwi(pdev)) != 0)
@@ -404,6 +415,7 @@ probe_irq_probe_fail:
probe_nomem:
atomic_dec(&pcidriver_deviceCount);
probe_maxdevices_fail:
+probe_dma_fail:
pci_disable_device(pdev);
probe_pcien_fail:
return err;
diff --git a/driver/common.h b/driver/common.h
index 992f0ea..6036b0c 100644
--- a/driver/common.h
+++ b/driver/common.h
@@ -1,6 +1,8 @@
#ifndef _PCIDRIVER_COMMON_H
#define _PCIDRIVER_COMMON_H
+#include "pcilib_types.h"
+
/*************************************************************************/
/* Private data types and structures */
@@ -12,6 +14,8 @@ typedef struct {
dma_addr_t dma_handle;
unsigned long cpua;
unsigned long size;
+ unsigned long type;
+ unsigned long use;
struct class_device_attribute sysfs_attr; /* initialized when adding the entry */
} pcidriver_kmem_entry_t;
diff --git a/driver/config.h b/driver/config.h
index c217afd..a2b6946 100644
--- a/driver/config.h
+++ b/driver/config.h
@@ -3,7 +3,7 @@
/*******************************/
/* Debug messages */
-//#define DEBUG
+#define DEBUG
/* Enable/disable IRQ handling */
#define ENABLE_IRQ
diff --git a/driver/ioctl.c b/driver/ioctl.c
index dacf94a..64985e8 100644
--- a/driver/ioctl.c
+++ b/driver/ioctl.c
@@ -218,7 +218,12 @@ static int ioctl_kmem_sync(pcidriver_privdata_t *privdata, unsigned long arg)
int ret;
READ_FROM_USER(kmem_sync_t, ksync);
- return pcidriver_kmem_sync(privdata, &ksync);
+ if ((ret = pcidriver_kmem_sync(privdata, &ksync)) != 0)
+ return ret;
+
+ WRITE_TO_USER(kmem_sync_t, ksync);
+
+ return 0;
}
/*
diff --git a/driver/kmem.c b/driver/kmem.c
index 737b74d..95a5487 100644
--- a/driver/kmem.c
+++ b/driver/kmem.c
@@ -41,7 +41,8 @@ int pcidriver_kmem_alloc(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_han
/* Initialize the kmem_entry */
kmem_entry->id = atomic_inc_return(&privdata->kmem_count) - 1;
- kmem_entry->size = kmem_handle->size;
+ kmem_entry->use = kmem_handle->use;
+ kmem_entry->type = kmem_handle->type;
kmem_handle->handle_id = kmem_entry->id;
/* Initialize sysfs if possible */
@@ -55,9 +56,25 @@ int pcidriver_kmem_alloc(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_han
* CPU address is used for the mmap (internal to the driver), and
* PCI address is the address passed to the DMA Controller in the device.
*/
- retptr = pci_alloc_consistent( privdata->pdev, kmem_handle->size, &(kmem_entry->dma_handle) );
+ switch (kmem_entry->type) {
+ case PCILIB_KMEM_TYPE_CONSISTENT:
+ retptr = pci_alloc_consistent( privdata->pdev, kmem_handle->size, &(kmem_entry->dma_handle) );
+ break;
+ case PCILIB_KMEM_TYPE_PAGE:
+ retptr = (void*)__get_free_pages(GFP_KERNEL, get_order(PAGE_SIZE));
+ kmem_entry->dma_handle = 0;
+ kmem_handle->size = PAGE_SIZE;
+
+// kmem_entry->dma_handle = pci_map_single(privdata->pdev, retptr, PAGE_SIZE, PCI_DMA_FROMDEVICE);
+// printk("%llx %lx\n", kmem_entry->dma_handle, retptr);
+ break;
+ default:
+ goto kmem_alloc_mem_fail;
+ }
if (retptr == NULL)
goto kmem_alloc_mem_fail;
+
+ kmem_entry->size = kmem_handle->size;
kmem_entry->cpua = (unsigned long)retptr;
kmem_handle->pa = (unsigned long)(kmem_entry->dma_handle);
@@ -124,6 +141,17 @@ int pcidriver_kmem_sync( pcidriver_privdata_t *privdata, kmem_sync_t *kmem_sync
if ((kmem_entry = pcidriver_kmem_find_entry(privdata, &(kmem_sync->handle))) == NULL)
return -EINVAL; /* kmem_handle is not valid */
+
+ if (!kmem_entry->dma_handle) {
+ mod_info_dbg("Instead of synchronization, we are mapping kmem_entry with id: %d\n", kmem_entry->id);
+ if (kmem_sync->dir == PCIDRIVER_DMA_TODEVICE)
+ kmem_entry->dma_handle = pci_map_single(privdata->pdev, (void*)kmem_entry->cpua, kmem_entry->size, PCI_DMA_TODEVICE);
+ else
+ kmem_entry->dma_handle = pci_map_single(privdata->pdev, (void*)kmem_entry->cpua, kmem_entry->size, PCI_DMA_FROMDEVICE);
+
+ kmem_sync->handle.pa = kmem_entry->dma_handle;
+ }
+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
switch (kmem_sync->dir) {
case PCIDRIVER_DMA_TODEVICE:
@@ -197,7 +225,16 @@ int pcidriver_kmem_free_entry(pcidriver_privdata_t *privdata, pcidriver_kmem_ent
#endif
/* Release DMA memory */
- pci_free_consistent( privdata->pdev, kmem_entry->size, (void *)(kmem_entry->cpua), kmem_entry->dma_handle );
+ switch (kmem_entry->type) {
+ case PCILIB_KMEM_TYPE_CONSISTENT:
+ pci_free_consistent( privdata->pdev, kmem_entry->size, (void *)(kmem_entry->cpua), kmem_entry->dma_handle );
+ break;
+ case PCILIB_KMEM_TYPE_PAGE:
+ if (kmem_entry->dma_handle) pci_unmap_single(privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, PCI_DMA_TODEVICE);
+ free_page((unsigned long)kmem_entry->cpua);
+ break;
+ }
+
/* Remove the kmem list entry */
spin_lock( &(privdata->kmemlist_lock) );
@@ -226,7 +263,7 @@ pcidriver_kmem_entry_t *pcidriver_kmem_find_entry(pcidriver_privdata_t *privdata
list_for_each(ptr, &(privdata->kmem_list)) {
entry = list_entry(ptr, pcidriver_kmem_entry_t, list);
- if (entry->dma_handle == kmem_handle->pa) {
+ if (entry->id == kmem_handle->handle_id) {
result = entry;
break;
}
@@ -288,6 +325,7 @@ int pcidriver_mmap_kmem(pcidriver_privdata_t *privdata, struct vm_area_struct *v
/* Check sizes */
vma_size = (vma->vm_end - vma->vm_start);
+
if ((vma_size != kmem_entry->size) &&
((kmem_entry->size < PAGE_SIZE) && (vma_size != PAGE_SIZE))) {
mod_info("kem_entry size(%lu) and vma size do not match(%lu)\n", kmem_entry->size, vma_size);
@@ -316,6 +354,6 @@ int pcidriver_mmap_kmem(pcidriver_privdata_t *privdata, struct vm_area_struct *v
mod_info("kmem remap failed: %d (%lx)\n", ret,kmem_entry->cpua);
return -EAGAIN;
}
-
+
return ret;
}
diff --git a/driver/pciDriver.h b/driver/pciDriver.h
index 98c1301..94c98e7 100644
--- a/driver/pciDriver.h
+++ b/driver/pciDriver.h
@@ -57,6 +57,7 @@
*/
#include <linux/ioctl.h>
+#include "pcilib_types.h"
/* Identifies the PCI-E Xilinx ML605 */
#define PCIE_XILINX_VENDOR_ID 0x10ee
@@ -82,9 +83,9 @@
#define PCIDRIVER_MMAP_KMEM 1
/* Direction of a DMA operation */
-#define PCIDRIVER_DMA_BIDIRECTIONAL 0
-#define PCIDRIVER_DMA_TODEVICE 1
-#define PCIDRIVER_DMA_FROMDEVICE 2
+#define PCIDRIVER_DMA_BIDIRECTIONAL 0
+#define PCIDRIVER_DMA_TODEVICE PCILIB_KMEM_SYNC_TODEVICE
+#define PCIDRIVER_DMA_FROMDEVICE PCILIB_KMEM_SYNC_FROMDEVICE
/* Possible sizes in a PCI command */
#define PCIDRIVER_PCI_CFG_SZ_BYTE 1
@@ -100,8 +101,11 @@
/* Types */
typedef struct {
+ unsigned long type;
unsigned long pa;
unsigned long size;
+ unsigned long align;
+ unsigned long use;
int handle_id;
} kmem_handle_t;
@@ -171,8 +175,8 @@ typedef struct {
#define PCIDRIVER_IOC_MMAP_MODE _IO( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 0 )
#define PCIDRIVER_IOC_MMAP_AREA _IO( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 1 )
#define PCIDRIVER_IOC_KMEM_ALLOC _IOWR( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 2, kmem_handle_t * )
-#define PCIDRIVER_IOC_KMEM_FREE _IOW( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 3, kmem_handle_t * )
-#define PCIDRIVER_IOC_KMEM_SYNC _IOW( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 4, kmem_sync_t * )
+#define PCIDRIVER_IOC_KMEM_FREE _IOW ( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 3, kmem_handle_t * )
+#define PCIDRIVER_IOC_KMEM_SYNC _IOWR( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 4, kmem_sync_t * )
#define PCIDRIVER_IOC_UMEM_SGMAP _IOWR( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 5, umem_handle_t * )
#define PCIDRIVER_IOC_UMEM_SGUNMAP _IOW( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 6, umem_handle_t * )
#define PCIDRIVER_IOC_UMEM_SGGET _IOWR( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 7, umem_sglist_t * )
diff --git a/kmem.c b/kmem.c
new file mode 100644
index 0000000..8388f2a
--- /dev/null
+++ b/kmem.c
@@ -0,0 +1,166 @@
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "pcilib.h"
+#include "pci.h"
+#include "kmem.h"
+#include "error.h"
+
+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) {
+ int ret;
+ int i;
+ void *addr;
+
+ kmem_handle_t kh = {0};
+
+ pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)malloc(sizeof(pcilib_kmem_list_t) + nmemb * sizeof(pcilib_kmem_addr_t));
+ if (!kbuf) {
+ pcilib_error("Memory allocation has failed");
+ return NULL;
+ }
+
+ memset(kbuf, 0, sizeof(pcilib_kmem_list_t) + nmemb * sizeof(pcilib_kmem_addr_t));
+
+
+ ret = ioctl( ctx->handle, PCIDRIVER_IOC_MMAP_MODE, PCIDRIVER_MMAP_KMEM );
+ if (ret) {
+ pcilib_error("PCIDRIVER_IOC_MMAP_MODE ioctl have failed");
+ return NULL;
+ }
+
+ kh.type = type;
+ kh.size = size;
+ kh.align = alignment;
+ kh.use = use;
+
+ for ( i = 0; i < nmemb; i++) {
+ ret = ioctl(ctx->handle, PCIDRIVER_IOC_KMEM_ALLOC, &kh);
+ if (ret) {
+ kbuf->buf.n_blocks = i;
+ pcilib_free_kernel_memory(ctx, kbuf);
+ pcilib_error("PCIDRIVER_IOC_KMEM_ALLOC ioctl have failed");
+ return NULL;
+ }
+
+ kbuf->buf.blocks[i].handle_id = kh.handle_id;
+ kbuf->buf.blocks[i].pa = kh.pa;
+ kbuf->buf.blocks[i].size = kh.size;
+
+ if (!i) {
+ if (kh.pa % alignment) printf("Alignment problem\n");
+ else if (kh.pa & ctx->page_mask) printf("Mmap alignment problem\n");
+ }
+
+ addr = mmap( 0, kh.size, PROT_WRITE | PROT_READ, MAP_SHARED, ctx->handle, 0 );
+ if ((!addr)||(addr == MAP_FAILED)) {
+ kbuf->buf.n_blocks = i + 1;
+ pcilib_free_kernel_memory(ctx, kbuf);
+ pcilib_error("Failed to mmap allocated kernel memory");
+ return NULL;
+ }
+
+ kbuf->buf.blocks[i].ua = addr;
+ }
+
+ if (nmemb == 1) {
+ memcpy(&kbuf->buf.addr, &kbuf->buf.blocks[0], sizeof(pcilib_kmem_addr_t));
+ }
+
+ kbuf->buf.n_blocks = nmemb;
+
+ kbuf->prev = NULL;
+ kbuf->next = ctx->kmem_list;
+ if (ctx->kmem_list) ctx->kmem_list->prev = kbuf;
+ ctx->kmem_list = kbuf;
+
+ return (pcilib_kmem_handle_t*)kbuf;
+}
+
+void pcilib_free_kernel_memory(pcilib_t *ctx, pcilib_kmem_handle_t *k) {
+ int ret, err = 0;
+ int i;
+ kmem_handle_t kh = {0};
+ pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
+
+ // if linked in to the list
+ if (kbuf->next) kbuf->next->prev = kbuf->prev;
+ if (kbuf->prev) kbuf->prev->next = kbuf->next;
+ else if (ctx->kmem_list == kbuf) ctx->kmem_list = kbuf->next;
+
+ for (i = 0; i < kbuf->buf.n_blocks; i++) {
+ if (kbuf->buf.blocks[i].ua) munmap(kbuf->buf.blocks[i].ua, kbuf->buf.blocks[i].size);
+
+ kh.handle_id = kbuf->buf.blocks[i].handle_id;
+ kh.pa = kbuf->buf.blocks[i].pa;
+ ret = ioctl(ctx->handle, PCIDRIVER_IOC_KMEM_FREE, &kh);
+ if ((ret)&&(!err)) err = ret;
+ }
+
+ free(kbuf);
+
+ if (err) {
+ pcilib_error("PCIDRIVER_IOC_KMEM_FREE ioctl have failed");
+ }
+}
+
+int pcilib_sync_kernel_memory(pcilib_t *ctx, pcilib_kmem_handle_t *k, pcilib_kmem_sync_direction_t dir) {
+ int i;
+ int ret;
+ kmem_sync_t ks;
+ pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
+
+ ks.dir = dir;
+
+ for (i = 0; i < kbuf->buf.n_blocks; i++) {
+ ks.handle.handle_id = kbuf->buf.blocks[i].handle_id;
+ ks.handle.pa = kbuf->buf.blocks[i].pa;
+ ret = ioctl(ctx->handle, PCIDRIVER_IOC_KMEM_SYNC, &ks);
+ if (ret) {
+ pcilib_error("PCIDRIVER_IOC_KMEM_SYNC ioctl have failed");
+ return PCILIB_ERROR_FAILED;
+ }
+
+ if (!kbuf->buf.blocks[i].pa) {
+ kbuf->buf.blocks[i].pa = ks.handle.pa;
+ }
+ }
+
+ return 0;
+}
+
+
+void *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;
+}
+
+uintptr_t pcilib_kmem_get_pa(pcilib_t *ctx, pcilib_kmem_handle_t *k) {
+ pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
+ return kbuf->buf.addr.pa;
+}
+
+void *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;
+}
+
+uintptr_t pcilib_kmem_get_block_pa(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;
+}
+
+size_t pcilib_kmem_get_block_size(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].size;
+}
diff --git a/kmem.h b/kmem.h
new file mode 100644
index 0000000..985864d
--- /dev/null
+++ b/kmem.h
@@ -0,0 +1,47 @@
+#ifndef _PCILIB_KMEM_H
+#define _PCILIB_KMEM_H
+
+#include "pcilib.h"
+
+typedef int pcilib_kmem_flags_t;
+
+typedef struct {
+ int handle_id;
+ uintptr_t pa;
+// uintptr_t va;
+ void *ua;
+ size_t size;
+} pcilib_kmem_addr_t;
+
+/**
+ * single allocation - we set only addr, n_blocks = 0
+ * multiple allocation - addr is not set, blocks are set, n_blocks > 0
+ * sgmap allocation - addr contains ua, but pa's are set in blocks, n_blocks > 0
+ */
+typedef struct {
+ pcilib_kmem_addr_t addr;
+
+ size_t n_blocks;
+ pcilib_kmem_addr_t blocks[];
+} pcilib_kmem_buffer_t;
+
+typedef void pcilib_kmem_handle_t;
+
+
+typedef struct pcilib_kmem_list_s pcilib_kmem_list_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_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);
+void pcilib_free_kernel_memory(pcilib_t *ctx, pcilib_kmem_handle_t *k);
+int pcilib_sync_kernel_memory(pcilib_t *ctx, pcilib_kmem_handle_t *k, pcilib_kmem_sync_direction_t dir);
+void *pcilib_kmem_get_ua(pcilib_t *ctx, pcilib_kmem_handle_t *k);
+uintptr_t pcilib_kmem_get_pa(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);
+uintptr_t pcilib_kmem_get_block_pa(pcilib_t *ctx, pcilib_kmem_handle_t *k, size_t block);
+size_t pcilib_kmem_get_block_size(pcilib_t *ctx, pcilib_kmem_handle_t *k, size_t block);
+
+#endif /* _PCILIB_KMEM_H */
diff --git a/pci.c b/pci.c
index 1cddeac..4347ab7 100644
--- a/pci.c
+++ b/pci.c
@@ -1,4 +1,5 @@
#define _PCILIB_PCI_C
+//#define PCILIB_FILE_IO
#define _POSIX_C_SOURCE 199309L
#include <stdio.h>
@@ -18,6 +19,7 @@
#include "kernel.h"
#include "tools.h"
+#include "dma.h"
#include "pci.h"
#include "ipecamera/model.h"
#include "error.h"
@@ -25,37 +27,7 @@
#define BIT_MASK(bits) ((1l << (bits)) - 1)
-//#define PCILIB_FILE_IO
-
-struct pcilib_s {
- int handle;
-
- uintptr_t page_mask;
- pcilib_board_info_t board_info;
- pcilib_dma_info_t dma_info;
- pcilib_model_t model;
-
- char *bar_space[PCILIB_MAX_BANKS];
-
- int reg_bar_mapped;
- pcilib_bar_t reg_bar;
-// char *reg_space;
-
- int data_bar_mapped;
- pcilib_bar_t data_bar;
-// char *data_space;
-// size_t data_size;
-
- pcilib_model_description_t *model_info;
-
- pcilib_dma_context_t *dma_ctx;
- pcilib_context_t *event_ctx;
-
-#ifdef PCILIB_FILE_IO
- int file_io_handle;
-#endif /* PCILIB_FILE_IO */
-};
static void pcilib_print_error(const char *msg, ...) {
va_list va;
@@ -124,6 +96,19 @@ const pcilib_board_info_t *pcilib_get_board_info(pcilib_t *ctx) {
return &ctx->board_info;
}
+
+int pcilib_wait_irq(pcilib_t *ctx, pcilib_irq_source_t source, unsigned long timeout) {
+ int err;
+
+ err = ioctl(ctx->handle, PCIDRIVER_IOC_WAITI, source);
+ if (err) {
+ pcilib_error("PCIDRIVER_IOC_WAITI ioctl have failed");
+ return PCILIB_ERROR_FAILED;
+ }
+
+ return 0;
+}
+
pcilib_context_t *pcilib_get_implementation_context(pcilib_t *ctx) {
return ctx->event_ctx;
}
@@ -270,44 +255,6 @@ int pcilib_write(pcilib_t *ctx, pcilib_bar_t bar, uintptr_t addr, size_t size, v
pcilib_unmap_bar(ctx, bar, data);
}
-pcilib_dma_t pcilib_find_dma_by_addr(pcilib_t *ctx, pcilib_dma_direction_t direction, pcilib_dma_addr_t dma) {
- pcilib_dma_t i;
-
- const pcilib_dma_info_t *info = pcilib_get_dma_info(ctx);
- if (!info) {
- pcilib_error("DMA Engine is not configured in the current model");
- return PCILIB_ERROR_NOTSUPPORTED;
- }
-
- for (i = 0; info->engines[i]; i++) {
- if ((info->engines[i]->addr == dma)&&((info->engines[i]->direction&direction)==direction)) break;
- }
-
- if (info->engines[i]) return i;
- return PCILIB_DMA_INVALID;
-}
-
-int pcilib_read_dma(pcilib_t *ctx, pcilib_dma_t dma, size_t size, void *buf) {
- const pcilib_dma_info_t *info = pcilib_get_dma_info(ctx);
-
- if (!ctx->model_info->dma_api) {
- pcilib_error("DMA Engine is not configured in the current model");
- return PCILIB_ERROR_NOTSUPPORTED;
- }
-
- if (!ctx->model_info->dma_api->read) {
- pcilib_error("The DMA read is not supported by configured DMA engine");
- return PCILIB_ERROR_NOTSUPPORTED;
- }
-
- if (!info->engines[dma]) {
- pcilib_error("The DMA engine (%i) is not supported by device", dma);
- return PCILIB_ERROR_OUTOFRANGE;
- }
-
- return ctx->model_info->dma_api->read(ctx->dma_ctx, dma, size, buf);
-}
-
pcilib_register_bank_t pcilib_find_bank_by_addr(pcilib_t *ctx, pcilib_register_bank_addr_t bank) {
pcilib_register_bank_t i;
@@ -393,7 +340,7 @@ pcilib_event_t pcilib_find_event(pcilib_t *ctx, const char *event) {
}
-static int pcilib_map_register_space(pcilib_t *ctx) {
+int pcilib_map_register_space(pcilib_t *ctx) {
int err;
pcilib_register_bank_t i;
@@ -441,7 +388,7 @@ static int pcilib_map_register_space(pcilib_t *ctx) {
return 0;
}
-static int pcilib_map_data_space(pcilib_t *ctx, uintptr_t addr) {
+int pcilib_map_data_space(pcilib_t *ctx, uintptr_t addr) {
int err;
pcilib_bar_t i;
@@ -541,26 +488,6 @@ char *pcilib_resolve_register_address(pcilib_t *ctx, pcilib_bar_t bar, uintptr_
return NULL;
}
-const pcilib_dma_info_t *pcilib_get_dma_info(pcilib_t *ctx) {
- if (!ctx->dma_ctx) {
- pcilib_model_t model = pcilib_get_model(ctx);
- pcilib_dma_api_description_t *api = pcilib_model[model].dma_api;
-
- if ((api)&&(api->init)) {
- pcilib_map_register_space(ctx);
- ctx->dma_ctx = api->init(ctx);
- }
-
- if (!ctx->dma_ctx) return NULL;
- }
-
- return &ctx->dma_info;
-}
-
-int pcilib_set_dma_engine_description(pcilib_t *ctx, pcilib_dma_t engine, pcilib_dma_engine_description_t *desc) {
- ctx->dma_info.engines[engine] = desc;
-}
-
char *pcilib_resolve_data_space(pcilib_t *ctx, uintptr_t addr, size_t *size) {
int err;
@@ -586,6 +513,10 @@ void pcilib_close(pcilib_t *ctx) {
if ((eapi)&&(eapi->free)) eapi->free(ctx->event_ctx);
if ((dapi)&&(dapi->free)) dapi->free(ctx->dma_ctx);
+
+ while (ctx->kmem_list) {
+ pcilib_free_kernel_memory(ctx, ctx->kmem_list);
+ }
for (i = 0; i < PCILIB_MAX_BANKS; i++) {
if (ctx->bar_space[i]) {
diff --git a/pci.h b/pci.h
index 0785c5b..d01ce86 100644
--- a/pci.h
+++ b/pci.h
@@ -1,17 +1,45 @@
#ifndef _PCITOOL_PCI_H
#define _PCITOOL_PCI_H
+#define PCILIB_DMA_TIMEOUT 10000 /**< us */
#define PCILIB_REGISTER_TIMEOUT 10000 /**< us */
#include "driver/pciDriver.h"
-
+#include "pcilib_types.h"
#include "pcilib.h"
+#include "kmem.h"
-int pcilib_set_dma_engine_description(pcilib_t *ctx, pcilib_dma_t engine, pcilib_dma_engine_description_t *desc);
+struct pcilib_s {
+ int handle;
+
+ uintptr_t page_mask;
+ pcilib_board_info_t board_info;
+ pcilib_dma_info_t dma_info;
+ pcilib_model_t model;
+
+ char *bar_space[PCILIB_MAX_BANKS];
-const pcilib_board_info_t *pcilib_get_board_info(pcilib_t *ctx);
-const pcilib_dma_info_t *pcilib_get_dma_info(pcilib_t *ctx);
+ int reg_bar_mapped;
+ pcilib_bar_t reg_bar;
+// char *reg_space;
+
+ int data_bar_mapped;
+ pcilib_bar_t data_bar;
+// char *data_space;
+// size_t data_size;
+
+ pcilib_kmem_list_t *kmem_list;
+
+ pcilib_model_description_t *model_info;
+
+ pcilib_dma_context_t *dma_ctx;
+ pcilib_context_t *event_ctx;
+
+#ifdef PCILIB_FILE_IO
+ int file_io_handle;
+#endif /* PCILIB_FILE_IO */
+};
#ifdef _PCILIB_PCI_C
# include "ipecamera/model.h"
@@ -36,4 +64,11 @@ extern void (*pcilib_warning)(const char *msg, ...);
extern pcilib_protocol_description_t pcilib_protocol[];
#endif /* _PCILIB_PCI_C */
+const pcilib_board_info_t *pcilib_get_board_info(pcilib_t *ctx);
+const pcilib_dma_info_t *pcilib_get_dma_info(pcilib_t *ctx);
+
+int pcilib_map_register_space(pcilib_t *ctx);
+int pcilib_map_data_space(pcilib_t *ctx, uintptr_t addr);
+
+
#endif /* _PCITOOL_PCI_H */
diff --git a/pcilib.h b/pcilib.h
index 88f3bfd..1e1ff36 100644
--- a/pcilib.h
+++ b/pcilib.h
@@ -21,6 +21,11 @@ typedef struct pcilib_s pcilib_t;
typedef void pcilib_context_t;
typedef void pcilib_dma_context_t;
+typedef struct pcilib_dma_api_description_s pcilib_dma_api_description_t;
+
+
+typedef unsigned long pcilib_irq_source_t;
+
typedef uint8_t pcilib_bar_t; /**< Type holding the PCI Bar number */
typedef uint8_t pcilib_register_t; /**< Type holding the register ID within the Bank */
typedef uint8_t pcilib_register_addr_t; /**< Type holding the register ID within the Bank */
@@ -32,7 +37,6 @@ typedef uint64_t pcilib_event_id_t;
typedef uint8_t pcilib_dma_addr_t;
typedef uint8_t pcilib_dma_t;
-
typedef uint32_t pcilib_event_t;
typedef enum {
@@ -62,6 +66,12 @@ typedef enum {
PCILIB_EVENT_DATA
} pcilib_event_data_type_t;
+typedef enum {
+ PCILIB_DMA_FLAGS_DEFAULT = 0,
+ PCILIB_DMA_FLAG_EOP = 1
+} pcilib_dma_flags_t;
+
+
#define PCILIB_BAR_DETECT ((pcilib_bar_t)-1)
#define PCILIB_BAR_INVALID ((pcilib_bar_t)-1)
#define PCILIB_BAR0 0
@@ -82,6 +92,8 @@ typedef enum {
#define PCILIB_EVENTS_ALL ((pcilib_event_t)-1)
#define PCILIB_EVENT_INVALID ((pcilib_event_t)-1)
#define PCILIB_EVENT_ID_INVALID 0
+#define PCILIB_TIMEOUT_INFINITE ((size_t)-1)
+#define PCILIB_TIMEOUT_IMMEDIATE 0
typedef struct {
pcilib_register_bank_addr_t addr;
@@ -161,14 +173,6 @@ typedef struct {
typedef int (*pcilib_callback_t)(pcilib_event_t event, pcilib_event_id_t event_id, void *user);
typedef struct {
- pcilib_dma_context_t *(*init)(pcilib_t *ctx);
- void (*free)(pcilib_dma_context_t *ctx);
-
- int (*read)(pcilib_dma_context_t *ctx, pcilib_dma_t dma, size_t size, void *buf);
-// int (*write)(pcilib_dma_context_t *ctx);
-} pcilib_dma_api_description_t;
-
-typedef struct {
pcilib_context_t *(*init)(pcilib_t *ctx);
void (*free)(pcilib_context_t *ctx);
@@ -219,10 +223,21 @@ pcilib_register_bank_t pcilib_find_bank_by_name(pcilib_t *ctx, const char *bankn
pcilib_register_bank_t pcilib_find_bank(pcilib_t *ctx, const char *bank);
pcilib_register_t pcilib_find_register(pcilib_t *ctx, const char *bank, const char *reg);
pcilib_event_t pcilib_find_event(pcilib_t *ctx, const char *event);
+pcilib_dma_t pcilib_find_dma_by_addr(pcilib_t *ctx, pcilib_dma_direction_t direction, pcilib_dma_addr_t dma);
+
int pcilib_read(pcilib_t *ctx, pcilib_bar_t bar, uintptr_t addr, size_t size, void *buf);
int pcilib_write(pcilib_t *ctx, pcilib_bar_t bar, uintptr_t addr, size_t size, void *buf);
+typedef int (*pcilib_dma_callback_t)(void *ctx, pcilib_dma_flags_t flags, size_t bufsize, void *buf);
+
+int pcilib_skip_dma(pcilib_t *ctx, pcilib_dma_t dma);
+size_t pcilib_stream_dma(pcilib_t *ctx, pcilib_dma_t dma, uintptr_t addr, size_t size, pcilib_dma_flags_t flags, size_t timeout, pcilib_dma_callback_t cb, void *cbattr);
+size_t pcilib_push_dma(pcilib_t *ctx, pcilib_dma_t dma, uintptr_t addr, size_t size, pcilib_dma_flags_t flags, size_t timeout, void *buf);
+size_t pcilib_read_dma(pcilib_t *ctx, pcilib_dma_t dma, uintptr_t addr, size_t size, void *buf);
+size_t pcilib_write_dma(pcilib_t *ctx, pcilib_dma_t dma, uintptr_t addr, size_t size, void *buf);
+double pcilib_benchmark_dma(pcilib_t *ctx, pcilib_dma_addr_t dma, uintptr_t addr, size_t size, size_t iterations, pcilib_dma_direction_t direction);
+
int pcilib_read_register_space(pcilib_t *ctx, const char *bank, pcilib_register_addr_t addr, size_t n, pcilib_register_value_t *buf);
int pcilib_write_register_space(pcilib_t *ctx, const char *bank, pcilib_register_addr_t addr, size_t n, pcilib_register_value_t *buf);
int pcilib_read_register_by_id(pcilib_t *ctx, pcilib_register_t reg, pcilib_register_value_t *value);
diff --git a/pcilib_types.h b/pcilib_types.h
new file mode 100644
index 0000000..f8c1c79
--- /dev/null
+++ b/pcilib_types.h
@@ -0,0 +1,25 @@
+#ifndef _PCILIB_TYPES_H
+#define _PCILIB_TYPES_H
+
+typedef enum {
+ PCILIB_KMEM_TYPE_CONSISTENT = 0,
+ PCILIB_KMEM_TYPE_PAGE,
+} pcilib_kmem_type_t;
+
+typedef enum {
+ PCILIB_KMEM_USE_DMA = 1,
+} pcilib_kmem_use_t;
+
+typedef enum {
+ PCILIB_KMEM_SYNC_TODEVICE = 1,
+ PCILIB_KMEM_SYNC_FROMDEVICE = 2
+} pcilib_kmem_sync_direction_t;
+
+
+#define PCILIB_KMEM_USE(type, subtype) (((type) << 16)|(subtype))
+
+
+//pcilib_alloc_kmem_buffer(pcilib_t *ctx, size_t size, size_t alignment)
+
+
+#endif /* _PCILIB_TYPES_H */
diff --git a/tools.h b/tools.h
index f357789..3454fc4 100644
--- a/tools.h
+++ b/tools.h
@@ -6,6 +6,8 @@
#include "pci.h"
+#define min2(a, b) (((a)<(b))?(a):(b))
+
int pcilib_isnumber(const char *str);
int pcilib_isxnumber(const char *str);