summaryrefslogtreecommitdiffstats
path: root/dma
diff options
context:
space:
mode:
authorSuren A. Chilingaryan <csa@dside.dyndns.org>2011-07-12 14:51:11 +0200
committerSuren A. Chilingaryan <csa@dside.dyndns.org>2011-07-12 14:51:11 +0200
commit6c922712fd8ee7e75a1b45c4980be22d36d0d1d9 (patch)
treef10f58ea417b269447f3c3db805e7a5269e845cf /dma
parent493958c9cf11eaa25251d91e9ecab9a28bd6d68f (diff)
downloadpcitool-6c922712fd8ee7e75a1b45c4980be22d36d0d1d9.tar.gz
pcitool-6c922712fd8ee7e75a1b45c4980be22d36d0d1d9.tar.bz2
pcitool-6c922712fd8ee7e75a1b45c4980be22d36d0d1d9.tar.xz
pcitool-6c922712fd8ee7e75a1b45c4980be22d36d0d1d9.zip
Another reorganization of NWL sources
Diffstat (limited to 'dma')
-rw-r--r--dma/nwl.c279
-rw-r--r--dma/nwl.h19
-rw-r--r--dma/nwl_buffers.h11
-rw-r--r--dma/nwl_dma.h4
-rw-r--r--dma/nwl_engine.c266
-rw-r--r--dma/nwl_engine.h7
6 files changed, 301 insertions, 285 deletions
diff --git a/dma/nwl.c b/dma/nwl.c
index bca0c5c..ea31e01 100644
--- a/dma/nwl.c
+++ b/dma/nwl.c
@@ -8,199 +8,14 @@
#include <sys/time.h>
#include "pci.h"
-#include "dma.h"
#include "pcilib.h"
#include "error.h"
#include "tools.h"
#include "nwl.h"
#include "nwl_defines.h"
-#include "nwl_register.h"
-#define NWL_XAUI_ENGINE 0
-#define NWL_XRAWDATA_ENGINE 1
-#define NWL_FIX_EOP_FOR_BIG_PACKETS // requires precise sizes in read requests
-//#define NWL_GENERATE_DMA_IRQ
-
-#define PCILIB_NWL_ALIGNMENT 64 // in bytes
-#define PCILIB_NWL_DMA_DESCRIPTOR_SIZE 64 // in bytes
-#define PCILIB_NWL_DMA_PAGES 512 // 1024
-
-
-static int dma_nwl_read_engine_config(nwl_dma_t *ctx, pcilib_nwl_engine_description_t *info, char *base) {
- uint32_t val;
-
- info->base_addr = base;
-
- nwl_read_register(val, ctx, base, REG_DMA_ENG_CAP);
-
- if ((val & DMA_ENG_PRESENT_MASK) == 0) return PCILIB_ERROR_NOTAVAILABLE;
-
- info->desc.addr = (val & DMA_ENG_NUMBER) >> DMA_ENG_NUMBER_SHIFT;
- if ((info->desc.addr > PCILIB_MAX_DMA_ENGINES)||(info->desc.addr < 0)) return PCILIB_ERROR_INVALID_DATA;
-
- switch (val & DMA_ENG_DIRECTION_MASK) {
- case DMA_ENG_C2S:
- info->desc.direction = PCILIB_DMA_FROM_DEVICE;
- break;
- default:
- info->desc.direction = PCILIB_DMA_TO_DEVICE;
- }
-
- switch (val & DMA_ENG_TYPE_MASK) {
- case DMA_ENG_BLOCK:
- info->desc.type = PCILIB_DMA_TYPE_BLOCK;
- break;
- case DMA_ENG_PACKET:
- info->desc.type = PCILIB_DMA_TYPE_PACKET;
- break;
- default:
- info->desc.type = PCILIB_DMA_TYPE_UNKNOWN;
- }
-
- info->desc.addr_bits = (val & DMA_ENG_BD_MAX_BC) >> DMA_ENG_BD_MAX_BC_SHIFT;
-
- info->base_addr = base;
-
- return 0;
-}
-
-
-int dma_nwl_start_engine(nwl_dma_t *ctx, pcilib_dma_engine_t dma) {
- int err;
- uint32_t val;
- uint32_t ring_pa;
- struct timeval start, cur;
-
- pcilib_nwl_engine_description_t *info = ctx->engines + dma;
- char *base = ctx->engines[dma].base_addr;
-
- if (info->started) return 0;
-
- // Disable IRQs
- err = dma_nwl_disable_engine_irq(ctx, dma);
- if (err) return err;
-
- // Disable Engine & Reseting
- val = DMA_ENG_DISABLE|DMA_ENG_USER_RESET;
- nwl_write_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
-
- gettimeofday(&start, NULL);
- do {
- nwl_read_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
- 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_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);
- do {
- nwl_read_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
- gettimeofday(&cur, NULL);
- } while ((val & DMA_ENG_RESET)&&(((cur.tv_sec - start.tv_sec)*1000000 + (cur.tv_usec - start.tv_usec)) < PCILIB_REGISTER_TIMEOUT));
-
- if (val & DMA_ENG_RESET) {
- pcilib_error("Timeout during reset of DMA engine %i", info->desc.addr);
- return PCILIB_ERROR_TIMEOUT;
- }
-
- // Acknowledge asserted engine interrupts
- if (val & DMA_ENG_INT_ACTIVE_MASK) {
- val |= DMA_ENG_ALLINT_MASK;
- nwl_write_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
- }
-
- err = dma_nwl_start(ctx);
- if (err) return err;
-
- 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();
-
-#ifdef NWL_GENERATE_DMA_IRQ
- dma_nwl_enable_engine_irq(ctx, dma);
-#endif /* NWL_GENERATE_DMA_IRQ */
-
- 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);
-
- info->tail = 0;
- info->head = (info->ring_size - 1);
- } else {
- info->tail = 0;
- info->head = 0;
- }
-
- info->started = 1;
-
- return 0;
-}
-
-
-int dma_nwl_stop_engine(nwl_dma_t *ctx, pcilib_dma_engine_t dma) {
- int err;
- uint32_t val;
- uint32_t ring_pa;
- struct timeval start, cur;
-
- pcilib_nwl_engine_description_t *info = ctx->engines + dma;
- char *base = ctx->engines[dma].base_addr;
-
- info->started = 0;
-
- err = dma_nwl_disable_engine_irq(ctx, dma);
- if (err) return err;
-
- val = DMA_ENG_DISABLE;
- nwl_write_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
-
- if (info->ring) {
- 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);
- }
-
- // Acknowledge asserted engine interrupts
- if (val & DMA_ENG_INT_ACTIVE_MASK) {
- val |= DMA_ENG_ALLINT_MASK;
- 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;
- }
-
-
-
- return 0;
-}
-
int dma_nwl_start_loopback(nwl_dma_t *ctx, pcilib_dma_direction_t direction, size_t packet_size) {
uint32_t val;
@@ -328,100 +143,6 @@ void dma_nwl_free(pcilib_dma_context_t *vctx) {
}
}
-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;
-}
-
-#include "nwl_buffers.h"
-
-int dma_nwl_write_fragment(pcilib_dma_context_t *vctx, pcilib_dma_engine_t dma, uintptr_t addr, size_t size, pcilib_dma_flags_t flags, pcilib_timeout_t timeout, void *data, size_t *written) {
- 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_engine(ctx, dma);
- if (err) return err;
-
- if (data) {
- 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) {
- if (written) *written = pos;
- return PCILIB_ERROR_TIMEOUT;
- }
-
- 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) {
- if (written) *written = pos;
- return err;
- }
- }
- }
-
- if (written) *written = size;
-
- if (flags&PCILIB_DMA_FLAG_WAIT) {
- bufnum = dma_nwl_get_next_buffer(ctx, info, PCILIB_NWL_DMA_PAGES - 1, timeout);
- if (bufnum == PCILIB_DMA_BUFFER_INVALID) return PCILIB_ERROR_TIMEOUT;
- }
-
- return 0;
-}
-
-int dma_nwl_stream_read(pcilib_dma_context_t *vctx, pcilib_dma_engine_t dma, uintptr_t addr, size_t size, pcilib_dma_flags_t flags, pcilib_timeout_t timeout, pcilib_dma_callback_t cb, void *cbattr) {
- int err, 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_engine(ctx, dma);
- if (err) return err;
-
- do {
- bufnum = dma_nwl_wait_buffer(ctx, info, &bufsize, &eop, timeout);
- if (bufnum == PCILIB_DMA_BUFFER_INVALID) return PCILIB_ERROR_TIMEOUT;
-
-#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;
-
- } while (ret);
-
- return 0;
-}
-
double dma_nwl_benchmark(pcilib_dma_context_t *vctx, pcilib_dma_engine_addr_t dma, uintptr_t addr, size_t size, size_t iterations, pcilib_dma_direction_t direction) {
int i;
diff --git a/dma/nwl.h b/dma/nwl.h
index 4e15418..481dca4 100644
--- a/dma/nwl.h
+++ b/dma/nwl.h
@@ -1,14 +1,29 @@
#ifndef _PCILIB_NWL_H
#define _PCILIB_NWL_H
+typedef struct nwl_dma_s nwl_dma_t;
+typedef struct pcilib_nwl_engine_description_s pcilib_nwl_engine_description_t;
+
+#define NWL_DMA_IRQ_SOURCE 0
+
+#define NWL_XAUI_ENGINE 0
+#define NWL_XRAWDATA_ENGINE 1
+#define NWL_FIX_EOP_FOR_BIG_PACKETS // requires precise sizes in read requests
+//#define NWL_GENERATE_DMA_IRQ
+
+#define PCILIB_NWL_ALIGNMENT 64 // in bytes
+#define PCILIB_NWL_DMA_DESCRIPTOR_SIZE 64 // in bytes
+#define PCILIB_NWL_DMA_PAGES 512 // 1024
+
#include "nwl_dma.h"
#include "nwl_irq.h"
#include "nwl_register.h"
+#include "nwl_engine.h"
#define nwl_read_register(var, ctx, base, reg) pcilib_datacpy(&var, base + reg, 4, 1, ctx->dma_bank->raw_endianess)
#define nwl_write_register(var, ctx, base, reg) pcilib_datacpy(base + reg, &var, 4, 1, ctx->dma_bank->raw_endianess)
-typedef struct {
+struct pcilib_nwl_engine_description_s {
pcilib_dma_engine_description_t desc;
char *base_addr;
@@ -20,7 +35,7 @@ typedef struct {
int started; /**< indicates that DMA buffers are initialized and reading is allowed */
int writting; /**< indicates that we are in middle of writting packet */
int preserve; /**< indicates that DMA should not be stopped during clean-up */
-} pcilib_nwl_engine_description_t;
+};
struct nwl_dma_s {
diff --git a/dma/nwl_buffers.h b/dma/nwl_buffers.h
index e059b97..35f4781 100644
--- a/dma/nwl_buffers.h
+++ b/dma/nwl_buffers.h
@@ -2,6 +2,17 @@
#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;
diff --git a/dma/nwl_dma.h b/dma/nwl_dma.h
index 70b9d97..0ed8158 100644
--- a/dma/nwl_dma.h
+++ b/dma/nwl_dma.h
@@ -4,10 +4,6 @@
#include <stdio.h>
#include "pcilib.h"
-#define NWL_DMA_IRQ_SOURCE 0
-
-typedef struct nwl_dma_s nwl_dma_t;
-
pcilib_dma_context_t *dma_nwl_init(pcilib_t *ctx);
void dma_nwl_free(pcilib_dma_context_t *vctx);
diff --git a/dma/nwl_engine.c b/dma/nwl_engine.c
new file mode 100644
index 0000000..3258da6
--- /dev/null
+++ b/dma/nwl_engine.c
@@ -0,0 +1,266 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include "pci.h"
+#include "pcilib.h"
+#include "error.h"
+#include "tools.h"
+#include "nwl.h"
+
+#include "nwl_defines.h"
+
+int dma_nwl_read_engine_config(nwl_dma_t *ctx, pcilib_nwl_engine_description_t *info, char *base) {
+ uint32_t val;
+
+ info->base_addr = base;
+
+ nwl_read_register(val, ctx, base, REG_DMA_ENG_CAP);
+
+ if ((val & DMA_ENG_PRESENT_MASK) == 0) return PCILIB_ERROR_NOTAVAILABLE;
+
+ info->desc.addr = (val & DMA_ENG_NUMBER) >> DMA_ENG_NUMBER_SHIFT;
+ if ((info->desc.addr > PCILIB_MAX_DMA_ENGINES)||(info->desc.addr < 0)) return PCILIB_ERROR_INVALID_DATA;
+
+ switch (val & DMA_ENG_DIRECTION_MASK) {
+ case DMA_ENG_C2S:
+ info->desc.direction = PCILIB_DMA_FROM_DEVICE;
+ break;
+ default:
+ info->desc.direction = PCILIB_DMA_TO_DEVICE;
+ }
+
+ switch (val & DMA_ENG_TYPE_MASK) {
+ case DMA_ENG_BLOCK:
+ info->desc.type = PCILIB_DMA_TYPE_BLOCK;
+ break;
+ case DMA_ENG_PACKET:
+ info->desc.type = PCILIB_DMA_TYPE_PACKET;
+ break;
+ default:
+ info->desc.type = PCILIB_DMA_TYPE_UNKNOWN;
+ }
+
+ info->desc.addr_bits = (val & DMA_ENG_BD_MAX_BC) >> DMA_ENG_BD_MAX_BC_SHIFT;
+
+ info->base_addr = base;
+
+ return 0;
+}
+
+int dma_nwl_start_engine(nwl_dma_t *ctx, pcilib_dma_engine_t dma) {
+ int err;
+ uint32_t val;
+ uint32_t ring_pa;
+ struct timeval start, cur;
+
+ pcilib_nwl_engine_description_t *info = ctx->engines + dma;
+ char *base = ctx->engines[dma].base_addr;
+
+ if (info->started) return 0;
+
+ // Disable IRQs
+ err = dma_nwl_disable_engine_irq(ctx, dma);
+ if (err) return err;
+
+ // Disable Engine & Reseting
+ val = DMA_ENG_DISABLE|DMA_ENG_USER_RESET;
+ nwl_write_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
+
+ gettimeofday(&start, NULL);
+ do {
+ nwl_read_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
+ 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_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);
+ do {
+ nwl_read_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
+ gettimeofday(&cur, NULL);
+ } while ((val & DMA_ENG_RESET)&&(((cur.tv_sec - start.tv_sec)*1000000 + (cur.tv_usec - start.tv_usec)) < PCILIB_REGISTER_TIMEOUT));
+
+ if (val & DMA_ENG_RESET) {
+ pcilib_error("Timeout during reset of DMA engine %i", info->desc.addr);
+ return PCILIB_ERROR_TIMEOUT;
+ }
+
+ // Acknowledge asserted engine interrupts
+ if (val & DMA_ENG_INT_ACTIVE_MASK) {
+ val |= DMA_ENG_ALLINT_MASK;
+ nwl_write_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
+ }
+
+ err = dma_nwl_start(ctx);
+ if (err) return err;
+
+ 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();
+
+#ifdef NWL_GENERATE_DMA_IRQ
+ dma_nwl_enable_engine_irq(ctx, dma);
+#endif /* NWL_GENERATE_DMA_IRQ */
+
+ 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);
+
+ info->tail = 0;
+ info->head = (info->ring_size - 1);
+ } else {
+ info->tail = 0;
+ info->head = 0;
+ }
+
+ info->started = 1;
+
+ return 0;
+}
+
+
+int dma_nwl_stop_engine(nwl_dma_t *ctx, pcilib_dma_engine_t dma) {
+ int err;
+ uint32_t val;
+ uint32_t ring_pa;
+ struct timeval start, cur;
+
+ pcilib_nwl_engine_description_t *info = ctx->engines + dma;
+ char *base = ctx->engines[dma].base_addr;
+
+ info->started = 0;
+
+ err = dma_nwl_disable_engine_irq(ctx, dma);
+ if (err) return err;
+
+ val = DMA_ENG_DISABLE;
+ nwl_write_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
+
+ if (info->ring) {
+ 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);
+ }
+
+ // Acknowledge asserted engine interrupts
+ if (val & DMA_ENG_INT_ACTIVE_MASK) {
+ val |= DMA_ENG_ALLINT_MASK;
+ 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;
+ }
+
+ return 0;
+}
+
+#include "nwl_buffers.h"
+
+int dma_nwl_write_fragment(pcilib_dma_context_t *vctx, pcilib_dma_engine_t dma, uintptr_t addr, size_t size, pcilib_dma_flags_t flags, pcilib_timeout_t timeout, void *data, size_t *written) {
+ 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_engine(ctx, dma);
+ if (err) return err;
+
+ if (data) {
+ 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) {
+ if (written) *written = pos;
+ return PCILIB_ERROR_TIMEOUT;
+ }
+
+ 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) {
+ if (written) *written = pos;
+ return err;
+ }
+ }
+ }
+
+ if (written) *written = size;
+
+ if (flags&PCILIB_DMA_FLAG_WAIT) {
+ bufnum = dma_nwl_get_next_buffer(ctx, info, PCILIB_NWL_DMA_PAGES - 1, timeout);
+ if (bufnum == PCILIB_DMA_BUFFER_INVALID) return PCILIB_ERROR_TIMEOUT;
+ }
+
+ return 0;
+}
+
+int dma_nwl_stream_read(pcilib_dma_context_t *vctx, pcilib_dma_engine_t dma, uintptr_t addr, size_t size, pcilib_dma_flags_t flags, pcilib_timeout_t timeout, pcilib_dma_callback_t cb, void *cbattr) {
+ int err, 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_engine(ctx, dma);
+ if (err) return err;
+
+ do {
+ bufnum = dma_nwl_wait_buffer(ctx, info, &bufsize, &eop, timeout);
+ if (bufnum == PCILIB_DMA_BUFFER_INVALID) return PCILIB_ERROR_TIMEOUT;
+
+#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;
+
+ } while (ret);
+
+ return 0;
+}
diff --git a/dma/nwl_engine.h b/dma/nwl_engine.h
new file mode 100644
index 0000000..778dc8a
--- /dev/null
+++ b/dma/nwl_engine.h
@@ -0,0 +1,7 @@
+#ifndef _PCILIB_DMA_NWL_ENGINE_H
+#define _PCILIB_DMA_NWL_ENGINE_H
+
+int dma_nwl_read_engine_config(nwl_dma_t *ctx, pcilib_nwl_engine_description_t *info, char *base);
+
+#endif /* _PCILIB_DMA_NWL_ENGINE_H */
+