4 * COMEDI - Linux Control and Measurement Device Interface
5 * Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
18 #include <linux/vmalloc.h>
20 #include "comedidev.h"
21 #include "comedi_internal.h"
23 #ifdef PAGE_KERNEL_NOCACHE
24 #define COMEDI_PAGE_PROTECTION PAGE_KERNEL_NOCACHE
26 #define COMEDI_PAGE_PROTECTION PAGE_KERNEL
29 static void __comedi_buf_free(struct comedi_device
*dev
,
30 struct comedi_subdevice
*s
,
33 struct comedi_async
*async
= s
->async
;
34 struct comedi_buf_page
*buf
;
37 if (async
->prealloc_buf
) {
38 vunmap(async
->prealloc_buf
);
39 async
->prealloc_buf
= NULL
;
40 async
->prealloc_bufsz
= 0;
43 if (!async
->buf_page_list
)
46 for (i
= 0; i
< n_pages
; ++i
) {
47 buf
= &async
->buf_page_list
[i
];
49 clear_bit(PG_reserved
,
50 &(virt_to_page(buf
->virt_addr
)->flags
));
51 if (s
->async_dma_dir
!= DMA_NONE
) {
53 dma_free_coherent(dev
->hw_dev
,
59 free_page((unsigned long)buf
->virt_addr
);
63 vfree(async
->buf_page_list
);
64 async
->buf_page_list
= NULL
;
65 async
->n_buf_pages
= 0;
68 static void __comedi_buf_alloc(struct comedi_device
*dev
,
69 struct comedi_subdevice
*s
,
72 struct comedi_async
*async
= s
->async
;
73 struct page
**pages
= NULL
;
74 struct comedi_buf_page
*buf
;
77 if (!IS_ENABLED(CONFIG_HAS_DMA
) && s
->async_dma_dir
!= DMA_NONE
) {
78 dev_err(dev
->class_dev
,
79 "dma buffer allocation not supported\n");
83 async
->buf_page_list
= vzalloc(sizeof(*buf
) * n_pages
);
84 if (async
->buf_page_list
)
85 pages
= vmalloc(sizeof(struct page
*) * n_pages
);
90 for (i
= 0; i
< n_pages
; i
++) {
91 buf
= &async
->buf_page_list
[i
];
92 if (s
->async_dma_dir
!= DMA_NONE
)
94 buf
->virt_addr
= dma_alloc_coherent(dev
->hw_dev
,
103 buf
->virt_addr
= (void *)get_zeroed_page(GFP_KERNEL
);
107 set_bit(PG_reserved
, &(virt_to_page(buf
->virt_addr
)->flags
));
109 pages
[i
] = virt_to_page(buf
->virt_addr
);
112 /* vmap the prealloc_buf if all the pages were allocated */
114 async
->prealloc_buf
= vmap(pages
, n_pages
, VM_MAP
,
115 COMEDI_PAGE_PROTECTION
);
120 int comedi_buf_alloc(struct comedi_device
*dev
, struct comedi_subdevice
*s
,
121 unsigned long new_size
)
123 struct comedi_async
*async
= s
->async
;
125 /* Round up new_size to multiple of PAGE_SIZE */
126 new_size
= (new_size
+ PAGE_SIZE
- 1) & PAGE_MASK
;
128 /* if no change is required, do nothing */
129 if (async
->prealloc_buf
&& async
->prealloc_bufsz
== new_size
)
132 /* deallocate old buffer */
133 __comedi_buf_free(dev
, s
, async
->n_buf_pages
);
135 /* allocate new buffer */
137 unsigned n_pages
= new_size
>> PAGE_SHIFT
;
139 __comedi_buf_alloc(dev
, s
, n_pages
);
141 if (!async
->prealloc_buf
) {
142 /* allocation failed */
143 __comedi_buf_free(dev
, s
, n_pages
);
146 async
->n_buf_pages
= n_pages
;
148 async
->prealloc_bufsz
= new_size
;
153 void comedi_buf_reset(struct comedi_async
*async
)
155 async
->buf_write_alloc_count
= 0;
156 async
->buf_write_count
= 0;
157 async
->buf_read_alloc_count
= 0;
158 async
->buf_read_count
= 0;
160 async
->buf_write_ptr
= 0;
161 async
->buf_read_ptr
= 0;
164 async
->scan_progress
= 0;
165 async
->munge_chan
= 0;
166 async
->munge_count
= 0;
167 async
->munge_ptr
= 0;
172 static unsigned int comedi_buf_write_n_available(struct comedi_async
*async
)
174 unsigned int free_end
= async
->buf_read_count
+ async
->prealloc_bufsz
;
176 return free_end
- async
->buf_write_alloc_count
;
179 static unsigned int __comedi_buf_write_alloc(struct comedi_async
*async
,
183 unsigned int available
= comedi_buf_write_n_available(async
);
185 if (nbytes
> available
)
186 nbytes
= strict
? 0 : available
;
188 async
->buf_write_alloc_count
+= nbytes
;
191 * ensure the async buffer 'counts' are read and updated
192 * before we write data to the write-alloc'ed buffer space
199 /* allocates chunk for the writer from free buffer space */
200 unsigned int comedi_buf_write_alloc(struct comedi_async
*async
,
203 return __comedi_buf_write_alloc(async
, nbytes
, 0);
205 EXPORT_SYMBOL_GPL(comedi_buf_write_alloc
);
208 * munging is applied to data by core as it passes between user
211 static unsigned int comedi_buf_munge(struct comedi_async
*async
,
212 unsigned int num_bytes
)
214 struct comedi_subdevice
*s
= async
->subdevice
;
215 unsigned int count
= 0;
216 const unsigned num_sample_bytes
= bytes_per_sample(s
);
218 if (!s
->munge
|| (async
->cmd
.flags
& CMDF_RAWDATA
)) {
219 async
->munge_count
+= num_bytes
;
222 /* don't munge partial samples */
223 num_bytes
-= num_bytes
% num_sample_bytes
;
224 while (count
< num_bytes
) {
225 int block_size
= num_bytes
- count
;
226 unsigned int buf_end
;
228 buf_end
= async
->prealloc_bufsz
- async
->munge_ptr
;
229 if (block_size
> buf_end
)
230 block_size
= buf_end
;
232 s
->munge(s
->device
, s
,
233 async
->prealloc_buf
+ async
->munge_ptr
,
234 block_size
, async
->munge_chan
);
237 * ensure data is munged in buffer before the
238 * async buffer munge_count is incremented
242 async
->munge_chan
+= block_size
/ num_sample_bytes
;
243 async
->munge_chan
%= async
->cmd
.chanlist_len
;
244 async
->munge_count
+= block_size
;
245 async
->munge_ptr
+= block_size
;
246 async
->munge_ptr
%= async
->prealloc_bufsz
;
254 unsigned int comedi_buf_write_n_allocated(struct comedi_async
*async
)
256 return async
->buf_write_alloc_count
- async
->buf_write_count
;
259 /* transfers a chunk from writer to filled buffer space */
260 unsigned int comedi_buf_write_free(struct comedi_async
*async
,
263 unsigned int allocated
= comedi_buf_write_n_allocated(async
);
265 if (nbytes
> allocated
)
268 async
->buf_write_count
+= nbytes
;
269 async
->buf_write_ptr
+= nbytes
;
270 comedi_buf_munge(async
, async
->buf_write_count
- async
->munge_count
);
271 if (async
->buf_write_ptr
>= async
->prealloc_bufsz
)
272 async
->buf_write_ptr
%= async
->prealloc_bufsz
;
276 EXPORT_SYMBOL_GPL(comedi_buf_write_free
);
278 unsigned int comedi_buf_read_n_available(struct comedi_async
*async
)
285 num_bytes
= async
->munge_count
- async
->buf_read_count
;
288 * ensure the async buffer 'counts' are read before we
289 * attempt to read data from the buffer
295 EXPORT_SYMBOL_GPL(comedi_buf_read_n_available
);
297 /* allocates a chunk for the reader from filled (and munged) buffer space */
298 unsigned int comedi_buf_read_alloc(struct comedi_async
*async
,
301 unsigned int available
;
303 available
= async
->munge_count
- async
->buf_read_alloc_count
;
304 if (nbytes
> available
)
307 async
->buf_read_alloc_count
+= nbytes
;
310 * ensure the async buffer 'counts' are read before we
311 * attempt to read data from the read-alloc'ed buffer space
317 EXPORT_SYMBOL_GPL(comedi_buf_read_alloc
);
319 static unsigned int comedi_buf_read_n_allocated(struct comedi_async
*async
)
321 return async
->buf_read_alloc_count
- async
->buf_read_count
;
324 /* transfers control of a chunk from reader to free buffer space */
325 unsigned int comedi_buf_read_free(struct comedi_async
*async
,
328 unsigned int allocated
;
331 * ensure data has been read out of buffer before
332 * the async read count is incremented
336 allocated
= comedi_buf_read_n_allocated(async
);
337 if (nbytes
> allocated
)
340 async
->buf_read_count
+= nbytes
;
341 async
->buf_read_ptr
+= nbytes
;
342 async
->buf_read_ptr
%= async
->prealloc_bufsz
;
345 EXPORT_SYMBOL_GPL(comedi_buf_read_free
);
347 int comedi_buf_put(struct comedi_async
*async
, unsigned short x
)
349 unsigned int n
= __comedi_buf_write_alloc(async
, sizeof(short), 1);
351 if (n
< sizeof(short)) {
352 async
->events
|= COMEDI_CB_ERROR
;
355 *(unsigned short *)(async
->prealloc_buf
+ async
->buf_write_ptr
) = x
;
356 comedi_buf_write_free(async
, sizeof(short));
359 EXPORT_SYMBOL_GPL(comedi_buf_put
);
361 int comedi_buf_get(struct comedi_async
*async
, unsigned short *x
)
363 unsigned int n
= comedi_buf_read_n_available(async
);
365 if (n
< sizeof(short))
367 comedi_buf_read_alloc(async
, sizeof(short));
368 *x
= *(unsigned short *)(async
->prealloc_buf
+ async
->buf_read_ptr
);
369 comedi_buf_read_free(async
, sizeof(short));
372 EXPORT_SYMBOL_GPL(comedi_buf_get
);
374 void comedi_buf_memcpy_to(struct comedi_async
*async
, unsigned int offset
,
375 const void *data
, unsigned int num_bytes
)
377 unsigned int write_ptr
= async
->buf_write_ptr
+ offset
;
379 if (write_ptr
>= async
->prealloc_bufsz
)
380 write_ptr
%= async
->prealloc_bufsz
;
383 unsigned int block_size
;
385 if (write_ptr
+ num_bytes
> async
->prealloc_bufsz
)
386 block_size
= async
->prealloc_bufsz
- write_ptr
;
388 block_size
= num_bytes
;
390 memcpy(async
->prealloc_buf
+ write_ptr
, data
, block_size
);
393 num_bytes
-= block_size
;
398 EXPORT_SYMBOL_GPL(comedi_buf_memcpy_to
);
400 void comedi_buf_memcpy_from(struct comedi_async
*async
, unsigned int offset
,
401 void *dest
, unsigned int nbytes
)
404 unsigned int read_ptr
= async
->buf_read_ptr
+ offset
;
406 if (read_ptr
>= async
->prealloc_bufsz
)
407 read_ptr
%= async
->prealloc_bufsz
;
410 unsigned int block_size
;
412 src
= async
->prealloc_buf
+ read_ptr
;
414 if (nbytes
>= async
->prealloc_bufsz
- read_ptr
)
415 block_size
= async
->prealloc_bufsz
- read_ptr
;
419 memcpy(dest
, src
, block_size
);
420 nbytes
-= block_size
;
425 EXPORT_SYMBOL_GPL(comedi_buf_memcpy_from
);