/alps/pcitool

To get this branch, use:
bzr branch http://suren.me/webbzr/alps/pcitool
65 by Suren A. Chilingaryan
Separate NWL loopback code, provide DMA start/stop interfaces
1
#define _BSD_SOURCE
2
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
3
#include <stdio.h>
4
#include <stdlib.h>
5
#include <string.h>
6
#include <unistd.h>
7
#include <sys/time.h>
8
9
#include "pci.h"
10
#include "pcilib.h"
11
#include "error.h"
12
#include "tools.h"
236 by Suren A. Chilingaryan
Big redign of model structures
13
227 by Suren A. Chilingaryan
Initial implementation of IPEDMA, dummy driver for KAPTURE, start of API changes
14
#include "nwl_private.h"
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
15
#include "nwl_defines.h"
78 by Suren A. Chilingaryan
Correctly detect the tail pointer of C2S ring
16
#include "nwl_engine_buffers.h"
65 by Suren A. Chilingaryan
Separate NWL loopback code, provide DMA start/stop interfaces
17
236 by Suren A. Chilingaryan
Big redign of model structures
18
int dma_nwl_read_engine_config(nwl_dma_t *ctx, pcilib_dma_engine_description_t *info, const char *base) {
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
19
    uint32_t val;
20
    
21
    nwl_read_register(val, ctx, base, REG_DMA_ENG_CAP);
22
23
    if ((val & DMA_ENG_PRESENT_MASK) == 0) return PCILIB_ERROR_NOTAVAILABLE;
24
    
236 by Suren A. Chilingaryan
Big redign of model structures
25
    info->addr = (val & DMA_ENG_NUMBER) >> DMA_ENG_NUMBER_SHIFT;
26
    if ((info->addr > PCILIB_MAX_DMA_ENGINES)||(info->addr < 0)) return PCILIB_ERROR_INVALID_DATA;
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
27
    
28
    switch (val & DMA_ENG_DIRECTION_MASK) {
29
	case  DMA_ENG_C2S:
236 by Suren A. Chilingaryan
Big redign of model structures
30
	    info->direction = PCILIB_DMA_FROM_DEVICE;
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
31
	break;
32
	default:
236 by Suren A. Chilingaryan
Big redign of model structures
33
	    info->direction = PCILIB_DMA_TO_DEVICE;
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
34
    }
35
    
36
    switch (val & DMA_ENG_TYPE_MASK) {
37
	case DMA_ENG_BLOCK:
236 by Suren A. Chilingaryan
Big redign of model structures
38
	    info->type = PCILIB_DMA_TYPE_BLOCK;
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
39
	break;
40
	case DMA_ENG_PACKET:
236 by Suren A. Chilingaryan
Big redign of model structures
41
	    info->type = PCILIB_DMA_TYPE_PACKET;
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
42
	break;
43
	default:
236 by Suren A. Chilingaryan
Big redign of model structures
44
	    info->type = PCILIB_DMA_TYPE_UNKNOWN;
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
45
    }
46
    
236 by Suren A. Chilingaryan
Big redign of model structures
47
    info->addr_bits = (val & DMA_ENG_BD_MAX_BC) >> DMA_ENG_BD_MAX_BC_SHIFT;
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
48
    
49
    return 0;
50
}
51
52
int dma_nwl_start_engine(nwl_dma_t *ctx, pcilib_dma_engine_t dma) {
53
    int err;
54
    uint32_t val;
55
    uint32_t ring_pa;
56
    struct timeval start, cur;
57
236 by Suren A. Chilingaryan
Big redign of model structures
58
    pcilib_nwl_engine_context_t *ectx = ctx->engines + dma;
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
59
    char *base = ctx->engines[dma].base_addr;
65 by Suren A. Chilingaryan
Separate NWL loopback code, provide DMA start/stop interfaces
60
    
236 by Suren A. Chilingaryan
Big redign of model structures
61
    if (ectx->started) return 0;
79 by Suren A. Chilingaryan
Few more fixes
62
71 by Suren A. Chilingaryan
First iteration of work to preserve DMA state between executions
63
	// This will only successed if there are no parallel access to DMA engine
236 by Suren A. Chilingaryan
Big redign of model structures
64
    err = dma_nwl_allocate_engine_buffers(ctx, ectx);
77 by Suren A. Chilingaryan
Stop only started engines
65
    if (err) {
236 by Suren A. Chilingaryan
Big redign of model structures
66
	ectx->started = 1;
77 by Suren A. Chilingaryan
Stop only started engines
67
	dma_nwl_stop_engine(ctx, dma);
68
	return err;
69
    }
71 by Suren A. Chilingaryan
First iteration of work to preserve DMA state between executions
70
    
236 by Suren A. Chilingaryan
Big redign of model structures
71
    if (ectx->reused) {
72
    	ectx->preserve = 1;
74 by Suren A. Chilingaryan
Implement DMA access synchronization for NWL implementation
73
109 by Suren A. Chilingaryan
Improvements of DMA engine
74
	dma_nwl_acknowledge_irq((pcilib_dma_context_t*)ctx, PCILIB_DMA_IRQ, dma);
74 by Suren A. Chilingaryan
Implement DMA access synchronization for NWL implementation
75
76
#ifdef NWL_GENERATE_DMA_IRQ
77
	dma_nwl_enable_engine_irq(ctx, dma);
78
#endif /* NWL_GENERATE_DMA_IRQ */
79
    } else {
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
80
	// Disable IRQs
74 by Suren A. Chilingaryan
Implement DMA access synchronization for NWL implementation
81
	err = dma_nwl_disable_engine_irq(ctx, dma);
77 by Suren A. Chilingaryan
Stop only started engines
82
	if (err) {
236 by Suren A. Chilingaryan
Big redign of model structures
83
	    ectx->started = 1;
77 by Suren A. Chilingaryan
Stop only started engines
84
	    dma_nwl_stop_engine(ctx, dma);
85
	    return err;
86
	}
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
87
88
	// Disable Engine & Reseting 
74 by Suren A. Chilingaryan
Implement DMA access synchronization for NWL implementation
89
	val = DMA_ENG_DISABLE|DMA_ENG_USER_RESET;
90
	nwl_write_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
91
92
	gettimeofday(&start, NULL);
93
	do {
94
	    nwl_read_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
95
    	    gettimeofday(&cur, NULL);
96
	} 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));
97
    
98
	if (val & (DMA_ENG_STATE_MASK|DMA_ENG_USER_RESET)) {
236 by Suren A. Chilingaryan
Big redign of model structures
99
	    pcilib_error("Timeout during reset of DMA engine %i", ectx->desc->addr);
77 by Suren A. Chilingaryan
Stop only started engines
100
236 by Suren A. Chilingaryan
Big redign of model structures
101
	    ectx->started = 1;
77 by Suren A. Chilingaryan
Stop only started engines
102
	    dma_nwl_stop_engine(ctx, dma);
74 by Suren A. Chilingaryan
Implement DMA access synchronization for NWL implementation
103
	    return PCILIB_ERROR_TIMEOUT;
104
	}
105
106
	val = DMA_ENG_RESET; 
107
	nwl_write_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
108
    
109
	gettimeofday(&start, NULL);
110
	do {
111
	    nwl_read_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
112
    	    gettimeofday(&cur, NULL);
113
	} while ((val & DMA_ENG_RESET)&&(((cur.tv_sec - start.tv_sec)*1000000 + (cur.tv_usec - start.tv_usec)) < PCILIB_REGISTER_TIMEOUT));
114
    
115
	if (val & DMA_ENG_RESET) {
236 by Suren A. Chilingaryan
Big redign of model structures
116
	    pcilib_error("Timeout during reset of DMA engine %i", ectx->desc->addr);
77 by Suren A. Chilingaryan
Stop only started engines
117
236 by Suren A. Chilingaryan
Big redign of model structures
118
	    ectx->started = 1;
77 by Suren A. Chilingaryan
Stop only started engines
119
	    dma_nwl_stop_engine(ctx, dma);
74 by Suren A. Chilingaryan
Implement DMA access synchronization for NWL implementation
120
	    return PCILIB_ERROR_TIMEOUT;
121
	}
122
    
109 by Suren A. Chilingaryan
Improvements of DMA engine
123
    	dma_nwl_acknowledge_irq((pcilib_dma_context_t*)ctx, PCILIB_DMA_IRQ, dma);
74 by Suren A. Chilingaryan
Implement DMA access synchronization for NWL implementation
124
236 by Suren A. Chilingaryan
Big redign of model structures
125
	ring_pa = pcilib_kmem_get_pa(ctx->dmactx.pcilib, ectx->ring);
126
	nwl_write_register(ring_pa, ctx, ectx->base_addr, REG_DMA_ENG_NEXT_BD);
127
	nwl_write_register(ring_pa, ctx, ectx->base_addr, REG_SW_NEXT_BD);
74 by Suren A. Chilingaryan
Implement DMA access synchronization for NWL implementation
128
129
	__sync_synchronize();
130
236 by Suren A. Chilingaryan
Big redign of model structures
131
	nwl_read_register(val, ctx, ectx->base_addr, REG_DMA_ENG_CTRL_STATUS);
74 by Suren A. Chilingaryan
Implement DMA access synchronization for NWL implementation
132
	val |= (DMA_ENG_ENABLE);
236 by Suren A. Chilingaryan
Big redign of model structures
133
	nwl_write_register(val, ctx, ectx->base_addr, REG_DMA_ENG_CTRL_STATUS);
74 by Suren A. Chilingaryan
Implement DMA access synchronization for NWL implementation
134
135
	__sync_synchronize();
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
136
137
#ifdef NWL_GENERATE_DMA_IRQ
74 by Suren A. Chilingaryan
Implement DMA access synchronization for NWL implementation
138
	dma_nwl_enable_engine_irq(ctx, dma);
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
139
#endif /* NWL_GENERATE_DMA_IRQ */
140
236 by Suren A. Chilingaryan
Big redign of model structures
141
	if (ectx->desc->direction == PCILIB_DMA_FROM_DEVICE) {
142
	    ring_pa += (ectx->ring_size - 1) * PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
143
    	    nwl_write_register(ring_pa, ctx, ectx->base_addr, REG_SW_NEXT_BD);
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
144
236 by Suren A. Chilingaryan
Big redign of model structures
145
	    ectx->tail = 0;
146
	    ectx->head = (ectx->ring_size - 1);
74 by Suren A. Chilingaryan
Implement DMA access synchronization for NWL implementation
147
	} else {
236 by Suren A. Chilingaryan
Big redign of model structures
148
	    ectx->tail = 0;
149
	    ectx->head = 0;
74 by Suren A. Chilingaryan
Implement DMA access synchronization for NWL implementation
150
	}
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
151
    }
152
    
236 by Suren A. Chilingaryan
Big redign of model structures
153
    ectx->started = 1;
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
154
    
155
    return 0;
156
}
157
158
159
int dma_nwl_stop_engine(nwl_dma_t *ctx, pcilib_dma_engine_t dma) {
160
    int err;
161
    uint32_t val;
162
    uint32_t ring_pa;
163
    struct timeval start, cur;
75 by Suren A. Chilingaryan
Few fixes
164
    pcilib_kmem_flags_t flags;
165
    
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
166
    
236 by Suren A. Chilingaryan
Big redign of model structures
167
    pcilib_nwl_engine_context_t *ectx = ctx->engines + dma;
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
168
    char *base = ctx->engines[dma].base_addr;
169
236 by Suren A. Chilingaryan
Big redign of model structures
170
    if (!ectx->started) return 0;
77 by Suren A. Chilingaryan
Stop only started engines
171
    
236 by Suren A. Chilingaryan
Big redign of model structures
172
    ectx->started = 0;
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
173
174
    err = dma_nwl_disable_engine_irq(ctx, dma);
175
    if (err) return err;
176
236 by Suren A. Chilingaryan
Big redign of model structures
177
    if (!ectx->preserve) {
71 by Suren A. Chilingaryan
First iteration of work to preserve DMA state between executions
178
	    // Stopping DMA is not enough reset is required
179
	val = DMA_ENG_DISABLE|DMA_ENG_USER_RESET|DMA_ENG_RESET;
180
	nwl_write_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
181
74 by Suren A. Chilingaryan
Implement DMA access synchronization for NWL implementation
182
	gettimeofday(&start, NULL);
183
	do {
184
	    nwl_read_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
185
    	    gettimeofday(&cur, NULL);
186
	} while ((val & (DMA_ENG_RUNNING))&&(((cur.tv_sec - start.tv_sec)*1000000 + (cur.tv_usec - start.tv_usec)) < PCILIB_REGISTER_TIMEOUT));
187
236 by Suren A. Chilingaryan
Big redign of model structures
188
	if (ectx->ring) {
189
	    ring_pa = pcilib_kmem_get_pa(ctx->dmactx.pcilib, ectx->ring);
190
	    nwl_write_register(ring_pa, ctx, ectx->base_addr, REG_DMA_ENG_NEXT_BD);
191
	    nwl_write_register(ring_pa, ctx, ectx->base_addr, REG_SW_NEXT_BD);
71 by Suren A. Chilingaryan
First iteration of work to preserve DMA state between executions
192
	}
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
193
    }
88 by Suren A. Chilingaryan
IRQ acknowledgement support in the engine API
194
    
109 by Suren A. Chilingaryan
Improvements of DMA engine
195
    dma_nwl_acknowledge_irq((pcilib_dma_context_t*)ctx, PCILIB_DMA_IRQ, dma);
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
196
236 by Suren A. Chilingaryan
Big redign of model structures
197
    if (ectx->preserve) {
75 by Suren A. Chilingaryan
Few fixes
198
	flags = PCILIB_KMEM_FLAG_REUSE;
199
    } else {
200
        flags = PCILIB_KMEM_FLAG_HARDWARE|PCILIB_KMEM_FLAG_PERSISTENT;
201
    }
202
    
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
203
	// Clean buffers
236 by Suren A. Chilingaryan
Big redign of model structures
204
    if (ectx->ring) {
205
	pcilib_free_kernel_memory(ctx->dmactx.pcilib, ectx->ring, flags);
206
	ectx->ring = NULL;
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
207
    }
208
236 by Suren A. Chilingaryan
Big redign of model structures
209
    if (ectx->pages) {
210
	pcilib_free_kernel_memory(ctx->dmactx.pcilib, ectx->pages, flags);
211
	ectx->pages = NULL;
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
212
    }
213
214
    return 0;
215
}
216
217
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) {
218
    int err;
219
    size_t pos;
220
    size_t bufnum;
221
    nwl_dma_t *ctx = (nwl_dma_t*)vctx;
222
236 by Suren A. Chilingaryan
Big redign of model structures
223
    pcilib_nwl_engine_context_t *ectx = ctx->engines + dma;
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
224
109 by Suren A. Chilingaryan
Improvements of DMA engine
225
    err = dma_nwl_start(vctx, dma, PCILIB_DMA_FLAGS_DEFAULT);
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
226
    if (err) return err;
227
228
    if (data) {
236 by Suren A. Chilingaryan
Big redign of model structures
229
	for (pos = 0; pos < size; pos += ectx->page_size) {
230
	    int block_size = min2(size - pos, ectx->page_size);
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
231
	    
236 by Suren A. Chilingaryan
Big redign of model structures
232
    	    bufnum = dma_nwl_get_next_buffer(ctx, ectx, 1, timeout);
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
233
	    if (bufnum == PCILIB_DMA_BUFFER_INVALID) {
234
		if (written) *written = pos;
235
		return PCILIB_ERROR_TIMEOUT;
236
	    }
237
	
236 by Suren A. Chilingaryan
Big redign of model structures
238
    	    void *buf = pcilib_kmem_get_block_ua(ctx->dmactx.pcilib, ectx->pages, bufnum);
108 by Suren A. Chilingaryan
Fix DMA synchronization for writes as well
239
236 by Suren A. Chilingaryan
Big redign of model structures
240
	    pcilib_kmem_sync_block(ctx->dmactx.pcilib, ectx->pages, PCILIB_KMEM_SYNC_FROMDEVICE, bufnum);
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
241
	    memcpy(buf, data, block_size);
236 by Suren A. Chilingaryan
Big redign of model structures
242
	    pcilib_kmem_sync_block(ctx->dmactx.pcilib, ectx->pages, PCILIB_KMEM_SYNC_TODEVICE, bufnum);
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
243
236 by Suren A. Chilingaryan
Big redign of model structures
244
	    err = dma_nwl_push_buffer(ctx, ectx, block_size, (flags&PCILIB_DMA_FLAG_EOP)&&((pos + block_size) == size), timeout);
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
245
	    if (err) {
246
		if (written) *written = pos;
247
		return err;
248
	    }
249
	}    
250
    }
251
    
252
    if (written) *written = size;
253
    
254
    if (flags&PCILIB_DMA_FLAG_WAIT) {
236 by Suren A. Chilingaryan
Big redign of model structures
255
	bufnum =  dma_nwl_get_next_buffer(ctx, ectx, PCILIB_NWL_DMA_PAGES - 1, timeout);
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
256
	if (bufnum == PCILIB_DMA_BUFFER_INVALID) return PCILIB_ERROR_TIMEOUT;
257
    }
258
    
259
    return 0;
260
}
261
262
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) {
117 by Suren A. Chilingaryan
new event architecture, first trial
263
    int err, ret = PCILIB_STREAMING_REQ_PACKET;
126 by Suren A. Chilingaryan
multithread preprocessing of ipecamera frames and code reorganization
264
    pcilib_timeout_t wait = 0;
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
265
    size_t res = 0;
266
    size_t bufnum;
267
    size_t bufsize;
117 by Suren A. Chilingaryan
new event architecture, first trial
268
    
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
269
    nwl_dma_t *ctx = (nwl_dma_t*)vctx;
270
271
    int eop;
272
236 by Suren A. Chilingaryan
Big redign of model structures
273
    pcilib_nwl_engine_context_t *ectx = ctx->engines + dma;
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
274
109 by Suren A. Chilingaryan
Improvements of DMA engine
275
    err = dma_nwl_start(vctx, dma, PCILIB_DMA_FLAGS_DEFAULT);
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
276
    if (err) return err;
109 by Suren A. Chilingaryan
Improvements of DMA engine
277
    
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
278
    do {
117 by Suren A. Chilingaryan
new event architecture, first trial
279
	switch (ret&PCILIB_STREAMING_TIMEOUT_MASK) {
280
	    case PCILIB_STREAMING_CONTINUE: wait = PCILIB_DMA_TIMEOUT; break;
281
	    case PCILIB_STREAMING_WAIT: wait = timeout; break;
126 by Suren A. Chilingaryan
multithread preprocessing of ipecamera frames and code reorganization
282
//	    case PCILIB_STREAMING_CHECK: wait = 0; break;
117 by Suren A. Chilingaryan
new event architecture, first trial
283
	}
284
    
236 by Suren A. Chilingaryan
Big redign of model structures
285
        bufnum = dma_nwl_wait_buffer(ctx, ectx, &bufsize, &eop, wait);
117 by Suren A. Chilingaryan
new event architecture, first trial
286
        if (bufnum == PCILIB_DMA_BUFFER_INVALID) {
287
	    return (ret&PCILIB_STREAMING_FAIL)?PCILIB_ERROR_TIMEOUT:0;
109 by Suren A. Chilingaryan
Improvements of DMA engine
288
	}
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
289
109 by Suren A. Chilingaryan
Improvements of DMA engine
290
	    // EOP is not respected in IPE Camera
236 by Suren A. Chilingaryan
Big redign of model structures
291
	if (ctx->ignore_eop) eop = 1;
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
292
	
236 by Suren A. Chilingaryan
Big redign of model structures
293
	pcilib_kmem_sync_block(ctx->dmactx.pcilib, ectx->pages, PCILIB_KMEM_SYNC_FROMDEVICE, bufnum);
294
        void *buf = pcilib_kmem_get_block_ua(ctx->dmactx.pcilib, ectx->pages, bufnum);
109 by Suren A. Chilingaryan
Improvements of DMA engine
295
	ret = cb(cbattr, (eop?PCILIB_DMA_FLAG_EOP:0), bufsize, buf);
190 by Suren A. Chilingaryan
Do not return DMA buffer in streaming read if the callback returns error
296
	if (ret < 0) return -ret;
107 by Suren A. Chilingaryan
Do not sync to device, looks like it is not required
297
//	DS: Fixme, it looks like we can avoid calling this for the sake of performance
236 by Suren A. Chilingaryan
Big redign of model structures
298
//	pcilib_kmem_sync_block(ctx->dmactx.pcilib, ectx->pages, PCILIB_KMEM_SYNC_TODEVICE, bufnum);
299
	dma_nwl_return_buffer(ctx, ectx);
64 by Suren A. Chilingaryan
Another reorganization of NWL sources
300
	
301
	res += bufsize;
302
303
    } while (ret);
304
    
305
    return 0;
306
}
109 by Suren A. Chilingaryan
Improvements of DMA engine
307
308
int dma_nwl_wait_completion(nwl_dma_t * ctx, pcilib_dma_engine_t dma, pcilib_timeout_t timeout) {
309
    if (dma_nwl_get_next_buffer(ctx, ctx->engines + dma, PCILIB_NWL_DMA_PAGES - 1, PCILIB_DMA_TIMEOUT) == (PCILIB_NWL_DMA_PAGES - 1)) return 0;
310
    else return PCILIB_ERROR_TIMEOUT;
311
}
312