Procházet zdrojové kódy

First iteration of work to preserve DMA state between executions

Suren A. Chilingaryan před 13 roky
rodič
revize
f4ad2df220
17 změnil soubory, kde provedl 347 přidání a 215 odebrání
  1. 2 1
      cli.c
  2. 3 182
      dma/nwl.c
  3. 3 0
      dma/nwl.h
  4. 16 6
      dma/nwl_buffers.h
  5. 31 11
      dma/nwl_engine.c
  6. 173 0
      dma/nwl_loopback.c
  7. 2 2
      dma/nwl_register.h
  8. 2 0
      driver/base.c
  9. 4 1
      driver/common.h
  10. 55 1
      driver/kmem.c
  11. 1 0
      driver/kmem.h
  12. 2 0
      driver/pciDriver.h
  13. 6 3
      driver/sysfs.c
  14. 24 3
      kmem.c
  15. 14 2
      kmem.h
  16. 6 2
      pci.c
  17. 3 1
      pcilib_types.h

+ 2 - 1
cli.c

@@ -146,6 +146,7 @@ void Usage(int argc, char *argv[], const char *format, ...) {
 "	--start-dma <num>[r|w]	- Start specified DMA engine\n"
 "	--stop-dma [num][r|w]	- Stop specified engine or DMA subsystem\n"
 "	--wait-irq <source>	- Wait for IRQ\n"
+"	--clean-kernel-memory	- Cleans lost kernel space buffers (DANGEROUS)\n"
 "\n"
 "  Addressing:\n"
 "	-d <device>		- FPGA device (/dev/fpga0)\n"
@@ -334,7 +335,7 @@ void Info(pcilib_t *handle, pcilib_model_description_t *model_info) {
 }
 
 
-#define BENCH_MAX_DMA_SIZE 16 * 1024 * 1024
+#define BENCH_MAX_DMA_SIZE 4 * 1024 * 1024
 #define BENCH_MAX_FIFO_SIZE 1024 * 1024
 
 int Benchmark(pcilib_t *handle, ACCESS_MODE mode, pcilib_dma_engine_addr_t dma, pcilib_bar_t bar, uintptr_t addr, size_t n, access_t access, size_t iterations) {

+ 3 - 182
dma/nwl.c

@@ -1,6 +1,5 @@
 #define _PCILIB_DMA_NWL_C
 #define _BSD_SOURCE
-//#define DEBUG_HARDWARE
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -47,10 +46,9 @@ int dma_nwl_stop(pcilib_dma_context_t *vctx, pcilib_dma_engine_t dma, pcilib_dma
 	    }
 	
 	    if (ctx->engines[dma].preserve) preserving = 1;
-	    else {
-	        err = dma_nwl_stop_engine(ctx, dma);
-		if (err) return err;
-	    }
+	    
+	    err = dma_nwl_stop_engine(ctx, dma);
+	    if (err) return err;
 	}
 	    
 	    // global cleanup, should we do anything?
@@ -68,9 +66,6 @@ int dma_nwl_stop(pcilib_dma_context_t *vctx, pcilib_dma_engine_t dma, pcilib_dma
 	ctx->engines[dma].preserve = 0;
     }
     
-	// Do not close DMA if preservation mode is set
-    if (ctx->engines[dma].preserve) return 0;
-    
     return dma_nwl_stop_engine(ctx, dma);
 }
 
@@ -138,177 +133,3 @@ void  dma_nwl_free(pcilib_dma_context_t *vctx) {
 	free(ctx);
     }
 }
-
-
-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;
-    int res;
-    int err;
-    size_t bytes;
-    uint32_t val;
-    uint32_t *buf, *cmp;
-    const char *error = NULL;
-    pcilib_register_value_t regval;
-
-    size_t us = 0;
-    struct timeval start, cur;
-
-    nwl_dma_t *ctx = (nwl_dma_t*)vctx;
-
-    pcilib_dma_engine_t readid = pcilib_find_dma_by_addr(ctx->pcilib, PCILIB_DMA_FROM_DEVICE, dma);
-    pcilib_dma_engine_t writeid = pcilib_find_dma_by_addr(ctx->pcilib, PCILIB_DMA_TO_DEVICE, dma);
-
-    char *read_base = ctx->engines[readid].base_addr;
-    char *write_base = ctx->engines[writeid].base_addr;
-
-    if (size%sizeof(uint32_t)) size = 1 + size / sizeof(uint32_t);
-    else size /= sizeof(uint32_t);
-
-	// Not supported
-    if (direction == PCILIB_DMA_TO_DEVICE) return -1.;
-    else if ((direction == PCILIB_DMA_FROM_DEVICE)&&(ctx->type != PCILIB_DMA_MODIFICATION_DEFAULT)) return -1.;
-
-	// Stop Generators and drain old data
-    if (ctx->type == PCILIB_DMA_MODIFICATION_DEFAULT) dma_nwl_stop_loopback(ctx);
-//    dma_nwl_stop_engine(ctx, readid); // DS: replace with something better
-
-    __sync_synchronize();
-
-    err = pcilib_skip_dma(ctx->pcilib, readid);
-    if (err) {
-	pcilib_error("Can't start benchmark, devices continuously writes unexpected data using DMA engine");
-	return err;
-    }
-
-#ifdef NWL_GENERATE_DMA_IRQ
-    dma_nwl_enable_engine_irq(ctx, readid);
-    dma_nwl_enable_engine_irq(ctx, writeid);
-#endif /* NWL_GENERATE_DMA_IRQ */
-
-
-    if (ctx->type == PCILIB_DMA_MODIFICATION_DEFAULT) {
-	dma_nwl_start_loopback(ctx, direction, size * sizeof(uint32_t));
-    }
-
-	// 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));
-
-
-#ifdef DEBUG_HARDWARE	     
-    if (ctx->type == PCILIB_NWL_MODIFICATION_IPECAMERA) {
-	pcilib_write_register(ctx->pcilib, NULL, "control", 0x1e5);
-	usleep(100000);
-	pcilib_write_register(ctx->pcilib, NULL, "control", 0x1e1);
-    }
-#endif /* DEBUG_HARDWARE */
-
-	// Benchmark
-    for (i = 0; i < iterations; i++) {
-#ifdef DEBUG_HARDWARE	     
-	if (ctx->type == PCILIB_NWL_MODIFICATION_IPECAMERA) {
-	    pcilib_write_register(ctx->pcilib, NULL, "control", 0x1e1);
-	}
-#endif /* DEBUG_HARDWARE */
-
-        gettimeofday(&start, NULL);
-	if (direction&PCILIB_DMA_TO_DEVICE) {
-	    memcpy(buf, cmp, size * sizeof(uint32_t));
-
-	    err = pcilib_write_dma(ctx->pcilib, writeid, addr, size * sizeof(uint32_t), buf, &bytes);
-	    if ((err)||(bytes != size * sizeof(uint32_t))) {
-		error = "Write failed";
-	        break;
-	    }
-	}
-
-#ifdef DEBUG_HARDWARE	     
-	if (ctx->type == PCILIB_NWL_MODIFICATION_IPECAMERA) {
-	    //usleep(1000000);
-	    pcilib_write_register(ctx->pcilib, NULL, "control", 0x3e1);
-	}
-
-	memset(buf, 0, size * sizeof(uint32_t));
-#endif /* DEBUG_HARDWARE */
-        
-	err = pcilib_read_dma(ctx->pcilib, readid, addr, size * sizeof(uint32_t), buf, &bytes);
-        gettimeofday(&cur, NULL);
-	us += ((cur.tv_sec - start.tv_sec)*1000000 + (cur.tv_usec - start.tv_usec));
-
-	if ((err)||(bytes != size * sizeof(uint32_t))) {
-	     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;
-	    }
-	}
-
-#ifdef DEBUG_HARDWARE	     
-	puts("====================================");
-
-	err = pcilib_read_register(ctx->pcilib, NULL, "reg9050", &regval);
-	printf("Status1: %i 0x%lx\n", err, regval);
-	err = pcilib_read_register(ctx->pcilib, NULL, "reg9080", &regval);
-	printf("Start address: %i 0x%lx\n", err,  regval);
-	err = pcilib_read_register(ctx->pcilib, NULL, "reg9090", &regval);
-	printf("End address: %i 0x%lx\n", err,  regval);
-	err = pcilib_read_register(ctx->pcilib, NULL, "reg9100", &regval);
-	printf("Status2: %i 0x%lx\n", err,  regval);
-	err = pcilib_read_register(ctx->pcilib, NULL, "reg9110", &regval);
-	printf("Status3: %i 0x%lx\n", err,  regval);
-	err = pcilib_read_register(ctx->pcilib, NULL, "reg9160", &regval);
-	printf("Add_rd_ddr: %i 0x%lx\n", err, regval);
-#endif /* DEBUG_HARDWARE */
-
-    }
-
-#ifdef DEBUG_HARDWARE	     
-    puts("------------------------------------------------");
-    err = pcilib_read_register(ctx->pcilib, NULL, "reg9050", &regval);
-    printf("Status1: %i 0x%lx\n", err, regval);
-    err = pcilib_read_register(ctx->pcilib, NULL, "reg9080", &regval);
-    printf("Start address: %i 0x%lx\n", err,  regval);
-    err = pcilib_read_register(ctx->pcilib, NULL, "reg9090", &regval);
-    printf("End address: %i 0x%lx\n", err,  regval);
-    err = pcilib_read_register(ctx->pcilib, NULL, "reg9100", &regval);
-    printf("Status2: %i 0x%lx\n", err,  regval);
-    err = pcilib_read_register(ctx->pcilib, NULL, "reg9110", &regval);
-    printf("Status3: %i 0x%lx\n", err,  regval);
-    err = pcilib_read_register(ctx->pcilib, NULL, "reg9160", &regval);
-    printf("Add_rd_ddr: %i 0x%lx\n", err, regval);
-#endif /* DEBUG_HARDWARE */
-
-    if (error) {
-	pcilib_warning("%s at iteration %i, error: %i, bytes: %zu", error, i, err, bytes);
-    }
-    
-#ifdef NWL_GENERATE_DMA_IRQ
-    dma_nwl_disable_engine_irq(ctx, writeid);
-    dma_nwl_disable_engine_irq(ctx, readid);
-#endif /* NWL_GENERATE_DMA_IRQ */
-
-    if (ctx->type == PCILIB_DMA_MODIFICATION_DEFAULT) dma_nwl_stop_loopback(ctx);
-
-    __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);
-}

+ 3 - 0
dma/nwl.h

@@ -15,6 +15,9 @@ typedef struct pcilib_nwl_engine_description_s pcilib_nwl_engine_description_t;
 #define PCILIB_NWL_DMA_DESCRIPTOR_SIZE		64  // in bytes
 #define PCILIB_NWL_DMA_PAGES			512 // 1024
 
+//#define DEBUG_HARDWARE
+
+
 #include "nwl_dma.h"
 #include "nwl_irq.h"
 #include "nwl_register.h"

+ 16 - 6
dma/nwl_buffers.h

@@ -17,26 +17,36 @@ int dma_nwl_allocate_engine_buffers(nwl_dma_t *ctx, pcilib_nwl_engine_descriptio
     int err = 0;
 
     int i;
+    uint16_t sub_use;
     uint32_t val;
     uint32_t buf_sz;
     uint64_t buf_pa;
+    pcilib_kmem_reuse_t reuse_ring, reuse_pages;
 
     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, 0, PCILIB_KMEM_USE(PCILIB_KMEM_USE_DMA, info->desc.addr), 0);
+
+	// Or bidirectional specified by 0x0|addr, or read 0x0|addr and write 0x80|addr
+    sub_use = info->desc.addr|(info->desc.direction == PCILIB_DMA_TO_DEVICE)?0x80:0x00;
+    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_RING, sub_use), PCILIB_KMEM_FLAG_REUSE);
+    pcilib_kmem_handle_t *pages = pcilib_alloc_kernel_memory(ctx->pcilib, PCILIB_KMEM_TYPE_PAGE, PCILIB_NWL_DMA_PAGES, 0, 0, PCILIB_KMEM_USE(PCILIB_KMEM_USE_DMA_PAGES, sub_use), PCILIB_KMEM_FLAG_REUSE);
 
     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);    
+	if (pages) pcilib_free_kernel_memory(ctx->pcilib, pages, 0);
+	if (ring) pcilib_free_kernel_memory(ctx->pcilib, ring, 0);    
 	return err;
     }
+
+/*    
+    reuse_ring = pcilib_kmem_is_reused(ctx->pcilib, ring);
+    reuse_pages = pcilib_kmem_is_reused(ctx->pcilib, pages);
+    if ((reuse_ring == PCILIB_KMEM_REUSE_REUSED)&&(reuse_pages == PCILIB_KMEM_REUSE_REUSED)) info->preserve = 1;
+    else if (reuse_ring||reuse_pages) pcilib_warning("Inconsistent buffers in the kernel module are detected");
+*/
     
     unsigned char *data = (unsigned char*)pcilib_kmem_get_ua(ctx->pcilib, ring);
     uint32_t ring_pa = pcilib_kmem_get_pa(ctx->pcilib, ring);

+ 31 - 11
dma/nwl_engine.c

@@ -65,6 +65,24 @@ int dma_nwl_start_engine(nwl_dma_t *ctx, pcilib_dma_engine_t dma) {
     
     if (info->started) return 0;
 
+	// This will only successed if there are no parallel access to DMA engine
+    err = dma_nwl_allocate_engine_buffers(ctx, info);
+    if (err) return err;
+    
+	// Check if DMA engine is enabled
+    nwl_read_register(val, ctx, info->base_addr, REG_DMA_ENG_CTRL_STATUS);
+    if (val&DMA_ENG_RUNNING) {	
+//	info->preserve = 1;
+	
+	// We need to positionate buffers correctly (both read and write)
+	//DSS info->tail, info->head
+    
+//	pcilib_error("Not implemented");
+	
+//        info->started = 1;
+//	return 0;
+    }
+
 	// Disable IRQs
     err = dma_nwl_disable_engine_irq(ctx, dma);
     if (err) return err;
@@ -104,9 +122,6 @@ int dma_nwl_start_engine(nwl_dma_t *ctx, pcilib_dma_engine_t dma) {
 	nwl_write_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
     }
 
-    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);
@@ -154,13 +169,16 @@ int dma_nwl_stop_engine(nwl_dma_t *ctx, pcilib_dma_engine_t dma) {
     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->preserve) {
+	    // Stopping DMA is not enough reset is required
+	val = DMA_ENG_DISABLE|DMA_ENG_USER_RESET|DMA_ENG_RESET;
+	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);
+	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    
@@ -171,15 +189,17 @@ int dma_nwl_stop_engine(nwl_dma_t *ctx, pcilib_dma_engine_t dma) {
 
 	// Clean buffers
     if (info->ring) {
-	pcilib_free_kernel_memory(ctx->pcilib, info->ring);
+	pcilib_free_kernel_memory(ctx->pcilib, info->ring, info->preserve?PCILIB_KMEM_FLAG_REUSE:0);
 	info->ring = NULL;
     }
 
     if (info->pages) {
-	pcilib_free_kernel_memory(ctx->pcilib, info->pages);
+	pcilib_free_kernel_memory(ctx->pcilib, info->pages, info->preserve?PCILIB_KMEM_FLAG_REUSE:0);
 	info->pages = NULL;
     }
 
+    nwl_read_register(val, ctx, info->base_addr, REG_DMA_ENG_CTRL_STATUS);
+
     return 0;
 }
 

+ 173 - 0
dma/nwl_loopback.c

@@ -52,3 +52,176 @@ int dma_nwl_stop_loopback(nwl_dma_t *ctx) {
     
     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;
+    int res;
+    int err;
+    size_t bytes;
+    uint32_t val;
+    uint32_t *buf, *cmp;
+    const char *error = NULL;
+    pcilib_register_value_t regval;
+
+    size_t us = 0;
+    struct timeval start, cur;
+
+    nwl_dma_t *ctx = (nwl_dma_t*)vctx;
+
+    pcilib_dma_engine_t readid = pcilib_find_dma_by_addr(ctx->pcilib, PCILIB_DMA_FROM_DEVICE, dma);
+    pcilib_dma_engine_t writeid = pcilib_find_dma_by_addr(ctx->pcilib, PCILIB_DMA_TO_DEVICE, dma);
+
+    char *read_base = ctx->engines[readid].base_addr;
+    char *write_base = ctx->engines[writeid].base_addr;
+
+    if (size%sizeof(uint32_t)) size = 1 + size / sizeof(uint32_t);
+    else size /= sizeof(uint32_t);
+
+	// Not supported
+    if (direction == PCILIB_DMA_TO_DEVICE) return -1.;
+    else if ((direction == PCILIB_DMA_FROM_DEVICE)&&(ctx->type != PCILIB_DMA_MODIFICATION_DEFAULT)) return -1.;
+
+	// Stop Generators and drain old data
+    if (ctx->type == PCILIB_DMA_MODIFICATION_DEFAULT) dma_nwl_stop_loopback(ctx);
+//    dma_nwl_stop_engine(ctx, readid); // DS: replace with something better
+
+    __sync_synchronize();
+
+    err = pcilib_skip_dma(ctx->pcilib, readid);
+    if (err) {
+	pcilib_error("Can't start benchmark, devices continuously writes unexpected data using DMA engine");
+	return err;
+    }
+
+#ifdef NWL_GENERATE_DMA_IRQ
+    dma_nwl_enable_engine_irq(ctx, readid);
+    dma_nwl_enable_engine_irq(ctx, writeid);
+#endif /* NWL_GENERATE_DMA_IRQ */
+
+
+    if (ctx->type == PCILIB_DMA_MODIFICATION_DEFAULT) {
+	dma_nwl_start_loopback(ctx, direction, size * sizeof(uint32_t));
+    }
+
+	// 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));
+
+
+#ifdef DEBUG_HARDWARE	     
+    if (ctx->type == PCILIB_NWL_MODIFICATION_IPECAMERA) {
+	pcilib_write_register(ctx->pcilib, NULL, "control", 0x1e5);
+	usleep(100000);
+	pcilib_write_register(ctx->pcilib, NULL, "control", 0x1e1);
+    }
+#endif /* DEBUG_HARDWARE */
+
+	// Benchmark
+    for (i = 0; i < iterations; i++) {
+#ifdef DEBUG_HARDWARE	     
+	if (ctx->type == PCILIB_NWL_MODIFICATION_IPECAMERA) {
+	    pcilib_write_register(ctx->pcilib, NULL, "control", 0x1e1);
+	}
+#endif /* DEBUG_HARDWARE */
+
+        gettimeofday(&start, NULL);
+	if (direction&PCILIB_DMA_TO_DEVICE) {
+	    memcpy(buf, cmp, size * sizeof(uint32_t));
+
+	    err = pcilib_write_dma(ctx->pcilib, writeid, addr, size * sizeof(uint32_t), buf, &bytes);
+	    if ((err)||(bytes != size * sizeof(uint32_t))) {
+		error = "Write failed";
+	        break;
+	    }
+	}
+
+#ifdef DEBUG_HARDWARE	     
+	if (ctx->type == PCILIB_NWL_MODIFICATION_IPECAMERA) {
+	    //usleep(1000000);
+	    pcilib_write_register(ctx->pcilib, NULL, "control", 0x3e1);
+	}
+
+	memset(buf, 0, size * sizeof(uint32_t));
+#endif /* DEBUG_HARDWARE */
+        
+	err = pcilib_read_dma(ctx->pcilib, readid, addr, size * sizeof(uint32_t), buf, &bytes);
+        gettimeofday(&cur, NULL);
+	us += ((cur.tv_sec - start.tv_sec)*1000000 + (cur.tv_usec - start.tv_usec));
+
+	if ((err)||(bytes != size * sizeof(uint32_t))) {
+	     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;
+	    }
+	}
+
+#ifdef DEBUG_HARDWARE	     
+	puts("====================================");
+
+	err = pcilib_read_register(ctx->pcilib, NULL, "reg9050", &regval);
+	printf("Status1: %i 0x%lx\n", err, regval);
+	err = pcilib_read_register(ctx->pcilib, NULL, "reg9080", &regval);
+	printf("Start address: %i 0x%lx\n", err,  regval);
+	err = pcilib_read_register(ctx->pcilib, NULL, "reg9090", &regval);
+	printf("End address: %i 0x%lx\n", err,  regval);
+	err = pcilib_read_register(ctx->pcilib, NULL, "reg9100", &regval);
+	printf("Status2: %i 0x%lx\n", err,  regval);
+	err = pcilib_read_register(ctx->pcilib, NULL, "reg9110", &regval);
+	printf("Status3: %i 0x%lx\n", err,  regval);
+	err = pcilib_read_register(ctx->pcilib, NULL, "reg9160", &regval);
+	printf("Add_rd_ddr: %i 0x%lx\n", err, regval);
+#endif /* DEBUG_HARDWARE */
+
+    }
+
+#ifdef DEBUG_HARDWARE	     
+    puts("------------------------------------------------");
+    err = pcilib_read_register(ctx->pcilib, NULL, "reg9050", &regval);
+    printf("Status1: %i 0x%lx\n", err, regval);
+    err = pcilib_read_register(ctx->pcilib, NULL, "reg9080", &regval);
+    printf("Start address: %i 0x%lx\n", err,  regval);
+    err = pcilib_read_register(ctx->pcilib, NULL, "reg9090", &regval);
+    printf("End address: %i 0x%lx\n", err,  regval);
+    err = pcilib_read_register(ctx->pcilib, NULL, "reg9100", &regval);
+    printf("Status2: %i 0x%lx\n", err,  regval);
+    err = pcilib_read_register(ctx->pcilib, NULL, "reg9110", &regval);
+    printf("Status3: %i 0x%lx\n", err,  regval);
+    err = pcilib_read_register(ctx->pcilib, NULL, "reg9160", &regval);
+    printf("Add_rd_ddr: %i 0x%lx\n", err, regval);
+#endif /* DEBUG_HARDWARE */
+
+    if (error) {
+	pcilib_warning("%s at iteration %i, error: %i, bytes: %zu", error, i, err, bytes);
+    }
+    
+#ifdef NWL_GENERATE_DMA_IRQ
+    dma_nwl_disable_engine_irq(ctx, writeid);
+    dma_nwl_disable_engine_irq(ctx, readid);
+#endif /* NWL_GENERATE_DMA_IRQ */
+
+    if (ctx->type == PCILIB_DMA_MODIFICATION_DEFAULT) dma_nwl_stop_loopback(ctx);
+
+    __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);
+}

+ 2 - 2
dma/nwl_register.h

@@ -56,8 +56,8 @@ static pcilib_register_description_t nwl_dma_engine_registers[] = {
     {0x0004, 	4, 	1, 	0, 	0x0000C100,	PCILIB_REGISTER_RW1C, PCILIB_REGISTER_BITS, PCILIB_REGISTER_BANK_DMA, "dma%0*u%s_descriptor_fetch_error",  ""},
     {0x0004, 	5, 	1, 	0, 	0x0000C100,	PCILIB_REGISTER_RW1C, PCILIB_REGISTER_BITS, PCILIB_REGISTER_BANK_DMA, "dma%0*u%s_sw_abort_error",  ""},
     {0x0004, 	8, 	1, 	0, 	0x0000C100,	PCILIB_REGISTER_RW  , PCILIB_REGISTER_BITS, PCILIB_REGISTER_BANK_DMA, "dma%0*u%s_enable",  ""},
-    {0x0004, 	9, 	1, 	0, 	0x0000C100,	PCILIB_REGISTER_R   , PCILIB_REGISTER_BITS, PCILIB_REGISTER_BANK_DMA, "dma%0*u%s_running",  ""},
-    {0x0004, 	10, 	1, 	0, 	0x0000C100,	PCILIB_REGISTER_R   , PCILIB_REGISTER_BITS, PCILIB_REGISTER_BANK_DMA, "dma%0*u%s_waiting",  ""},
+    {0x0004, 	10, 	1, 	0, 	0x0000C100,	PCILIB_REGISTER_R   , PCILIB_REGISTER_BITS, PCILIB_REGISTER_BANK_DMA, "dma%0*u%s_running",  ""},
+    {0x0004, 	11, 	1, 	0, 	0x0000C100,	PCILIB_REGISTER_R   , PCILIB_REGISTER_BITS, PCILIB_REGISTER_BANK_DMA, "dma%0*u%s_waiting",  ""},
     {0x0004, 	14, 	1, 	0, 	0x0000C100,	PCILIB_REGISTER_RW  , PCILIB_REGISTER_BITS, PCILIB_REGISTER_BANK_DMA, "dma%0*u%s_reset_request", ""},
     {0x0004, 	15, 	1, 	0, 	0x0000C100,	PCILIB_REGISTER_RW  , PCILIB_REGISTER_BITS, PCILIB_REGISTER_BANK_DMA, "dma%0*u%s_reset", ""},
     {0x0008, 	0, 	32, 	0, 	0x00000000,	PCILIB_REGISTER_RW  , PCILIB_REGISTER_STANDARD, PCILIB_REGISTER_BANK_DMA, "dma%0*u%s_next_descriptor",  ""},

+ 2 - 0
driver/base.c

@@ -352,6 +352,8 @@ static int __devinit pcidriver_probe(struct pci_dev *pdev, const struct pci_devi
 	INIT_LIST_HEAD(&(privdata->kmem_list));
 	spin_lock_init(&(privdata->kmemlist_lock));
 	atomic_set(&privdata->kmem_count, 0);
+	
+	privdata->kmem_cur = NULL;
 
 	INIT_LIST_HEAD(&(privdata->umem_list));
 	spin_lock_init(&(privdata->umemlist_lock));

+ 4 - 1
driver/common.h

@@ -16,6 +16,7 @@ typedef struct {
 	unsigned long size;
 	unsigned long type;
 	unsigned long use;
+	unsigned long item;
 	struct class_device_attribute sysfs_attr;	/* initialized when adding the entry */
 } pcidriver_kmem_entry_t;
 
@@ -56,11 +57,13 @@ typedef struct  {
 	struct list_head kmem_list;			/* List of 'kmem_list_entry's associated with this device */
 	atomic_t kmem_count;				/* id for next kmem entry */
 
+	pcidriver_kmem_entry_t *kmem_cur;		/* Currently selected kmem buffer, for mmap */
+	
 	spinlock_t umemlist_lock;			/* Spinlock to lock umem list operations */
 	struct list_head umem_list;			/* List of 'umem_list_entry's associated with this device */
 	atomic_t umem_count;				/* id for next umem entry */
 
-	int msi_mode;
+	int msi_mode;					/* Flag specifying if interrupt have been initialized in MSI mode */
 } pcidriver_privdata_t;
 
 

+ 55 - 1
driver/kmem.c

@@ -35,6 +35,26 @@ int pcidriver_kmem_alloc(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_han
 	pcidriver_kmem_entry_t *kmem_entry;
 	void *retptr;
 
+	privdata->kmem_cur = NULL;
+	
+	if (kmem_handle->reuse) {
+/*	    kmem_entry = pcidriver_kmem_find_entry_use(privdata, kmem_handle->use, kmem_handle->item);
+	    if (kmem_entry) {
+		if (kmem_handle->type != kmem_entry->type) return EINVAL;
+
+		if (kmem_handle->type == PCILIB_KMEM_TYPE_PAGE) kmem_handle->size = kmem_entry->size;
+		else if (kmem_handle->size != kmem_entry->size) return EINVAL;
+
+		kmem_handle->handle_id = kmem_entry->id;
+		kmem_handle->pa = (unsigned long)(kmem_entry->dma_handle);
+		
+		privdata->kmem_cur = kmem_entry;
+
+		return 0;
+	    }*/
+	    kmem_handle->reuse = 0;
+	}
+
 	/* First, allocate zeroed memory for the kmem_entry */
 	if ((kmem_entry = kcalloc(1, sizeof(pcidriver_kmem_entry_t), GFP_KERNEL)) == NULL)
 		goto kmem_alloc_entry_fail;
@@ -42,6 +62,7 @@ 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->use = kmem_handle->use;
+	kmem_entry->item = kmem_handle->item;
 	kmem_entry->type = kmem_handle->type;
 	kmem_handle->handle_id = kmem_entry->id;
 
@@ -102,6 +123,11 @@ int pcidriver_kmem_free( pcidriver_privdata_t *privdata, kmem_handle_t *kmem_han
 {
 	pcidriver_kmem_entry_t *kmem_entry;
 
+	if (kmem_handle->reuse) {
+	    // just mark free
+	    return 0;
+	}
+
 	/* Find the associated kmem_entry for this buffer */
 	if ((kmem_entry = pcidriver_kmem_find_entry(privdata, kmem_handle)) == NULL)
 		return -EINVAL;					/* kmem_handle is not valid */
@@ -193,6 +219,8 @@ int pcidriver_kmem_sync( pcidriver_privdata_t *privdata, kmem_sync_t *kmem_sync
  */
 int pcidriver_kmem_free_entry(pcidriver_privdata_t *privdata, pcidriver_kmem_entry_t *kmem_entry)
 {
+	privdata->kmem_cur = NULL;
+
 	pcidriver_sysfs_remove(privdata, &(kmem_entry->sysfs_attr));
 
 	/* Go over the pages of the kmem buffer, and mark them as not reserved */
@@ -297,6 +325,31 @@ pcidriver_kmem_entry_t *pcidriver_kmem_find_entry_id(pcidriver_privdata_t *privd
 	return result;
 }
 
+/**
+ *
+ * find the corresponding kmem_entry for the given use and item.
+ *
+ */
+pcidriver_kmem_entry_t *pcidriver_kmem_find_entry_use(pcidriver_privdata_t *privdata, unsigned long use, unsigned long item)
+{
+	struct list_head *ptr;
+	pcidriver_kmem_entry_t *entry, *result = NULL;
+
+	spin_lock(&(privdata->kmemlist_lock));
+	list_for_each(ptr, &(privdata->kmem_list)) {
+		entry = list_entry(ptr, pcidriver_kmem_entry_t, list);
+
+		if ((entry->use == use)&&(entry->item == item)) {
+			result = entry;
+			break;
+		}
+	}
+
+	spin_unlock(&(privdata->kmemlist_lock));
+	return result;
+}
+
+
 /**
  *
  * mmap() kernel memory to userspace.
@@ -318,7 +371,8 @@ int pcidriver_mmap_kmem(pcidriver_privdata_t *privdata, struct vm_area_struct *v
 		mod_info("Trying to mmap a kernel memory buffer without creating it first!\n");
 		return -EFAULT;
 	}
-	kmem_entry = list_entry(privdata->kmem_list.prev, pcidriver_kmem_entry_t, list);
+	if (privdata->kmem_cur) kmem_entry = privdata->kmem_cur;
+	else kmem_entry = list_entry(privdata->kmem_list.prev, pcidriver_kmem_entry_t, list);
 	spin_unlock(&(privdata->kmemlist_lock));
 
 	mod_info_dbg("Got kmem_entry with id: %d\n", kmem_entry->id);

+ 1 - 0
driver/kmem.h

@@ -4,4 +4,5 @@ int pcidriver_kmem_sync(  pcidriver_privdata_t *privdata, kmem_sync_t *kmem_sync
 int pcidriver_kmem_free_all(  pcidriver_privdata_t *privdata );
 pcidriver_kmem_entry_t *pcidriver_kmem_find_entry( pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle );
 pcidriver_kmem_entry_t *pcidriver_kmem_find_entry_id( pcidriver_privdata_t *privdata, int id );
+pcidriver_kmem_entry_t *pcidriver_kmem_find_entry_use(pcidriver_privdata_t *privdata, unsigned long use, unsigned long item);
 int pcidriver_kmem_free_entry( pcidriver_privdata_t *privdata, pcidriver_kmem_entry_t *kmem_entry );

+ 2 - 0
driver/pciDriver.h

@@ -106,6 +106,8 @@ typedef struct {
 	unsigned long size;
 	unsigned long align;
 	unsigned long use;
+	unsigned long item;
+	int reuse;
 	int handle_id;
 } kmem_handle_t;
 

+ 6 - 3
driver/sysfs.c

@@ -90,14 +90,17 @@ void pcidriver_sysfs_remove(pcidriver_privdata_t *privdata, struct class_device_
 static SYSFS_GET_FUNCTION(pcidriver_show_kmem_entry)
 {
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
-//	pcidriver_privdata_t *privdata = (pcidriver_privdata_t *)cls->class_data;
+	pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
 
         /* As we can be sure that attr.name contains a filename which we
          * created (see _pcidriver_sysfs_initialize), we do not need to have
          * sanity checks but can directly call simple_strtol() */
         int id = simple_strtol(attr->attr.name + strlen("kbuf"), NULL, 10);
-
-	return snprintf(buf, PAGE_SIZE, "I am in the kmem_entry show function for buffer %d\n", id);
+	pcidriver_kmem_entry_t *entry = pcidriver_kmem_find_entry_id(privdata, id);
+	if (entry)
+	    return snprintf(buf, PAGE_SIZE, "buffer: %d\ntype: %lu\nuse: 0x%lx\nitem: %lu\nsize: %lu\n", id, entry->type, entry->use, entry->item, entry->size);
+	else
+	    return snprintf(buf, PAGE_SIZE, "I am in the kmem_entry show function for buffer %d\n", id);
 #else
 	return 0;
 #endif

+ 24 - 3
kmem.c

@@ -21,6 +21,8 @@ pcilib_kmem_handle_t *pcilib_alloc_kernel_memory(pcilib_t *ctx, pcilib_kmem_type
     int ret;
     int i;
     void *addr;
+    
+    pcilib_kmem_reuse_t reuse = PCILIB_KMEM_REUSE_ALLOCATED;
 
     kmem_handle_t kh = {0};
     
@@ -49,10 +51,13 @@ pcilib_kmem_handle_t *pcilib_alloc_kernel_memory(pcilib_t *ctx, pcilib_kmem_type
     }
     
     for ( i = 0; i < nmemb; i++) {
+	kh.item = i;
+	kh.reuse = PCILIB_KMEM_FLAG_REUSE?1:0;
+
         ret = ioctl(ctx->handle, PCIDRIVER_IOC_KMEM_ALLOC, &kh);
 	if (ret) {
 	    kbuf->buf.n_blocks = i;
-	    pcilib_free_kernel_memory(ctx, kbuf);
+	    pcilib_free_kernel_memory(ctx, kbuf, 0);
 	    pcilib_error("PCIDRIVER_IOC_KMEM_ALLOC ioctl have failed");
 	    return NULL;
 	}
@@ -60,6 +65,15 @@ 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].size = kh.size;
+	kbuf->buf.blocks[i].reused = kh.reuse;
+	
+	if (reuse) {
+		// if already reused, set to partial
+	    if (!kh.reuse) reuse = PCILIB_KMEM_REUSE_PARTIAL;
+	} else if (kh.reuse) {
+	    if (i) reuse = PCILIB_KMEM_REUSE_PARTIAL;
+	    else reuse =  PCILIB_KMEM_REUSE_REUSED;
+	}
     
         if ((alignment)&&(type != PCILIB_KMEM_TYPE_PAGE)) {
 	    if (kh.pa % alignment) kbuf->buf.blocks[i].alignment_offset = alignment - kh.pa % alignment;
@@ -69,7 +83,7 @@ pcilib_kmem_handle_t *pcilib_alloc_kernel_memory(pcilib_t *ctx, pcilib_kmem_type
     	addr = mmap( 0, kh.size + kbuf->buf.blocks[i].alignment_offset, 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_free_kernel_memory(ctx, kbuf, 0);
 	    pcilib_error("Failed to mmap allocated kernel memory");
 	    return NULL;
 	}
@@ -83,6 +97,7 @@ pcilib_kmem_handle_t *pcilib_alloc_kernel_memory(pcilib_t *ctx, pcilib_kmem_type
 	memcpy(&kbuf->buf.addr, &kbuf->buf.blocks[0], sizeof(pcilib_kmem_addr_t));
     }
     
+    kbuf->buf.reuse = reuse;
     kbuf->buf.n_blocks = nmemb;
     
     kbuf->prev = NULL;
@@ -93,7 +108,7 @@ pcilib_kmem_handle_t *pcilib_alloc_kernel_memory(pcilib_t *ctx, pcilib_kmem_type
     return (pcilib_kmem_handle_t*)kbuf;
 }
 
-void pcilib_free_kernel_memory(pcilib_t *ctx, pcilib_kmem_handle_t *k) {
+void pcilib_free_kernel_memory(pcilib_t *ctx, pcilib_kmem_handle_t *k, pcilib_kmem_flags_t flags) {
     int ret, err = 0; 
     int i;
     kmem_handle_t kh = {0};
@@ -109,6 +124,7 @@ void pcilib_free_kernel_memory(pcilib_t *ctx, pcilib_kmem_handle_t *k) {
 
         kh.handle_id = kbuf->buf.blocks[i].handle_id;
         kh.pa = kbuf->buf.blocks[i].pa;
+	kh.reuse = ((flags&PCILIB_KMEM_FLAG_REUSE)||(kbuf->buf.blocks[i].reused));
 	ret = ioctl(ctx->handle, PCIDRIVER_IOC_KMEM_FREE, &kh);
 	if ((ret)&&(!err)) err = ret;
     }
@@ -170,3 +186,8 @@ size_t pcilib_kmem_get_block_size(pcilib_t *ctx, pcilib_kmem_handle_t *k, size_t
     pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
     return kbuf->buf.blocks[block].size;
 }
+
+pcilib_kmem_reuse_t pcilib_kmem_is_reused(pcilib_t *ctx, pcilib_kmem_handle_t *k) {
+    pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
+    return kbuf->buf.reuse;
+}

+ 14 - 2
kmem.h

@@ -3,10 +3,20 @@
 
 #include "pcilib.h"
 
-typedef int pcilib_kmem_flags_t;
+typedef enum {
+    PCILIB_KMEM_FLAG_REUSE = 1,		/**< Try to reuse buffers on alloc and only unmap non-reused buffers on free (reused are freed only if FORCE flag is specified) */
+//    PCILIB_KMEM_FLAG_FORCE = 2		/**< Force buffer 
+} pcilib_kmem_flags_t;
+
+typedef enum {
+    PCILIB_KMEM_REUSE_ALLOCATED = 0,
+    PCILIB_KMEM_REUSE_PARTIAL = 1,
+    PCILIB_KMEM_REUSE_REUSED = 2
+} pcilib_kmem_reuse_t;
 
 typedef struct {
     int handle_id;
+    int reused;
     uintptr_t pa;
 //    uintptr_t va;
     void *ua;
@@ -23,6 +33,7 @@ typedef struct {
  */
 typedef struct {
     pcilib_kmem_addr_t addr;
+    pcilib_kmem_reuse_t reuse;
 
     size_t n_blocks;
     pcilib_kmem_addr_t blocks[];
@@ -39,12 +50,13 @@ struct pcilib_kmem_list_s {
 };
 
 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);
+void pcilib_free_kernel_memory(pcilib_t *ctx, pcilib_kmem_handle_t *k, pcilib_kmem_flags_t flags);
 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);
+pcilib_kmem_reuse_t pcilib_kmem_is_reused(pcilib_t *ctx, pcilib_kmem_handle_t *k);
 
 #endif /* _PCILIB_KMEM_H */

+ 6 - 2
pci.c

@@ -401,8 +401,12 @@ void pcilib_close(pcilib_t *ctx) {
 	    ctx->model_info.registers = pcilib_model[ctx->model].registers;
 	}
 	
-	while (ctx->kmem_list) {
-	    pcilib_free_kernel_memory(ctx, ctx->kmem_list);
+	if (ctx->kmem_list) {
+	    pcilib_warning("Not all kernel buffers are properly cleaned");
+	
+	    while (ctx->kmem_list) {
+		pcilib_free_kernel_memory(ctx, ctx->kmem_list, 0);
+	    }
 	}
 
 	for (i = 0; i < PCILIB_MAX_BANKS; i++) {

+ 3 - 1
pcilib_types.h

@@ -7,7 +7,9 @@ typedef enum {
 } pcilib_kmem_type_t;
 
 typedef enum {
-    PCILIB_KMEM_USE_DMA = 1,
+    PCILIB_KMEM_USE_STANDARD = 0,
+    PCILIB_KMEM_USE_DMA_RING = 1,
+    PCILIB_KMEM_USE_DMA_PAGES = 2
 } pcilib_kmem_use_t;
 
 typedef enum {