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.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include "comedidev.h"
23 #include "comedi_internal.h"
25 #ifdef PAGE_KERNEL_NOCACHE
26 #define COMEDI_PAGE_PROTECTION PAGE_KERNEL_NOCACHE
28 #define COMEDI_PAGE_PROTECTION PAGE_KERNEL
31 static void __comedi_buf_free(struct comedi_device
*dev
,
32 struct comedi_subdevice
*s
,
35 struct comedi_async
*async
= s
->async
;
36 struct comedi_buf_page
*buf
;
39 if (async
->prealloc_buf
) {
40 vunmap(async
->prealloc_buf
);
41 async
->prealloc_buf
= NULL
;
42 async
->prealloc_bufsz
= 0;
45 if (!async
->buf_page_list
)
48 for (i
= 0; i
< n_pages
; ++i
) {
49 buf
= &async
->buf_page_list
[i
];
51 clear_bit(PG_reserved
,
52 &(virt_to_page(buf
->virt_addr
)->flags
));
53 if (s
->async_dma_dir
!= DMA_NONE
) {
55 dma_free_coherent(dev
->hw_dev
,
61 free_page((unsigned long)buf
->virt_addr
);
65 vfree(async
->buf_page_list
);
66 async
->buf_page_list
= NULL
;
67 async
->n_buf_pages
= 0;
70 static void __comedi_buf_alloc(struct comedi_device
*dev
,
71 struct comedi_subdevice
*s
,
74 struct comedi_async
*async
= s
->async
;
75 struct page
**pages
= NULL
;
76 struct comedi_buf_page
*buf
;
79 if (!IS_ENABLED(CONFIG_HAS_DMA
) && s
->async_dma_dir
!= DMA_NONE
) {
80 dev_err(dev
->class_dev
,
81 "dma buffer allocation not supported\n");
85 async
->buf_page_list
= vzalloc(sizeof(*buf
) * n_pages
);
86 if (async
->buf_page_list
)
87 pages
= vmalloc(sizeof(struct page
*) * n_pages
);
92 for (i
= 0; i
< n_pages
; i
++) {
93 buf
= &async
->buf_page_list
[i
];
94 if (s
->async_dma_dir
!= DMA_NONE
)
96 buf
->virt_addr
= dma_alloc_coherent(dev
->hw_dev
,
105 buf
->virt_addr
= (void *)get_zeroed_page(GFP_KERNEL
);
109 set_bit(PG_reserved
, &(virt_to_page(buf
->virt_addr
)->flags
));
111 pages
[i
] = virt_to_page(buf
->virt_addr
);
114 /* vmap the prealloc_buf if all the pages were allocated */
116 async
->prealloc_buf
= vmap(pages
, n_pages
, VM_MAP
,
117 COMEDI_PAGE_PROTECTION
);
122 int comedi_buf_alloc(struct comedi_device
*dev
, struct comedi_subdevice
*s
,
123 unsigned long new_size
)
125 struct comedi_async
*async
= s
->async
;
127 /* Round up new_size to multiple of PAGE_SIZE */
128 new_size
= (new_size
+ PAGE_SIZE
- 1) & PAGE_MASK
;
130 /* if no change is required, do nothing */
131 if (async
->prealloc_buf
&& async
->prealloc_bufsz
== new_size
)
134 /* deallocate old buffer */
135 __comedi_buf_free(dev
, s
, async
->n_buf_pages
);
137 /* allocate new buffer */
139 unsigned n_pages
= new_size
>> PAGE_SHIFT
;
141 __comedi_buf_alloc(dev
, s
, n_pages
);
143 if (!async
->prealloc_buf
) {
144 /* allocation failed */
145 __comedi_buf_free(dev
, s
, n_pages
);
148 async
->n_buf_pages
= n_pages
;
150 async
->prealloc_bufsz
= new_size
;
155 void comedi_buf_reset(struct comedi_async
*async
)
157 async
->buf_write_alloc_count
= 0;
158 async
->buf_write_count
= 0;
159 async
->buf_read_alloc_count
= 0;
160 async
->buf_read_count
= 0;
162 async
->buf_write_ptr
= 0;
163 async
->buf_read_ptr
= 0;
166 async
->scan_progress
= 0;
167 async
->munge_chan
= 0;
168 async
->munge_count
= 0;
169 async
->munge_ptr
= 0;
174 static unsigned int comedi_buf_write_n_available(struct comedi_async
*async
)
176 unsigned int free_end
= async
->buf_read_count
+ async
->prealloc_bufsz
;
178 return free_end
- async
->buf_write_alloc_count
;
181 static unsigned int __comedi_buf_write_alloc(struct comedi_async
*async
,
185 unsigned int available
= comedi_buf_write_n_available(async
);
187 if (nbytes
> available
)
188 nbytes
= strict
? 0 : available
;
190 async
->buf_write_alloc_count
+= nbytes
;
193 * ensure the async buffer 'counts' are read and updated
194 * before we write data to the write-alloc'ed buffer space
201 /* allocates chunk for the writer from free buffer space */
202 unsigned int comedi_buf_write_alloc(struct comedi_async
*async
,
205 return __comedi_buf_write_alloc(async
, nbytes
, 0);
207 EXPORT_SYMBOL_GPL(comedi_buf_write_alloc
);
210 * munging is applied to data by core as it passes between user
213 static unsigned int comedi_buf_munge(struct comedi_async
*async
,
214 unsigned int num_bytes
)
216 struct comedi_subdevice
*s
= async
->subdevice
;
217 unsigned int count
= 0;
218 const unsigned num_sample_bytes
= bytes_per_sample(s
);
220 if (!s
->munge
|| (async
->cmd
.flags
& CMDF_RAWDATA
)) {
221 async
->munge_count
+= num_bytes
;
224 /* don't munge partial samples */
225 num_bytes
-= num_bytes
% num_sample_bytes
;
226 while (count
< num_bytes
) {
227 int block_size
= num_bytes
- count
;
228 unsigned int buf_end
;
230 buf_end
= async
->prealloc_bufsz
- async
->munge_ptr
;
231 if (block_size
> buf_end
)
232 block_size
= buf_end
;
234 s
->munge(s
->device
, s
,
235 async
->prealloc_buf
+ async
->munge_ptr
,
236 block_size
, async
->munge_chan
);
239 * ensure data is munged in buffer before the
240 * async buffer munge_count is incremented
244 async
->munge_chan
+= block_size
/ num_sample_bytes
;
245 async
->munge_chan
%= async
->cmd
.chanlist_len
;
246 async
->munge_count
+= block_size
;
247 async
->munge_ptr
+= block_size
;
248 async
->munge_ptr
%= async
->prealloc_bufsz
;
256 unsigned int comedi_buf_write_n_allocated(struct comedi_async
*async
)
258 return async
->buf_write_alloc_count
- async
->buf_write_count
;
261 /* transfers a chunk from writer to filled buffer space */
262 unsigned int comedi_buf_write_free(struct comedi_async
*async
,
265 unsigned int allocated
= comedi_buf_write_n_allocated(async
);
267 if (nbytes
> allocated
)
270 async
->buf_write_count
+= nbytes
;
271 async
->buf_write_ptr
+= nbytes
;
272 comedi_buf_munge(async
, async
->buf_write_count
- async
->munge_count
);
273 if (async
->buf_write_ptr
>= async
->prealloc_bufsz
)
274 async
->buf_write_ptr
%= async
->prealloc_bufsz
;
278 EXPORT_SYMBOL_GPL(comedi_buf_write_free
);
280 unsigned int comedi_buf_read_n_available(struct comedi_async
*async
)
287 num_bytes
= async
->munge_count
- async
->buf_read_count
;
290 * ensure the async buffer 'counts' are read before we
291 * attempt to read data from the buffer
297 EXPORT_SYMBOL_GPL(comedi_buf_read_n_available
);
299 /* allocates a chunk for the reader from filled (and munged) buffer space */
300 unsigned int comedi_buf_read_alloc(struct comedi_async
*async
,
303 unsigned int available
;
305 available
= async
->munge_count
- async
->buf_read_alloc_count
;
306 if (nbytes
> available
)
309 async
->buf_read_alloc_count
+= nbytes
;
312 * ensure the async buffer 'counts' are read before we
313 * attempt to read data from the read-alloc'ed buffer space
319 EXPORT_SYMBOL_GPL(comedi_buf_read_alloc
);
321 static unsigned int comedi_buf_read_n_allocated(struct comedi_async
*async
)
323 return async
->buf_read_alloc_count
- async
->buf_read_count
;
326 /* transfers control of a chunk from reader to free buffer space */
327 unsigned int comedi_buf_read_free(struct comedi_async
*async
,
330 unsigned int allocated
;
333 * ensure data has been read out of buffer before
334 * the async read count is incremented
338 allocated
= comedi_buf_read_n_allocated(async
);
339 if (nbytes
> allocated
)
342 async
->buf_read_count
+= nbytes
;
343 async
->buf_read_ptr
+= nbytes
;
344 async
->buf_read_ptr
%= async
->prealloc_bufsz
;
347 EXPORT_SYMBOL_GPL(comedi_buf_read_free
);
349 int comedi_buf_put(struct comedi_async
*async
, short x
)
351 unsigned int n
= __comedi_buf_write_alloc(async
, sizeof(short), 1);
353 if (n
< sizeof(short)) {
354 async
->events
|= COMEDI_CB_ERROR
;
357 *(short *)(async
->prealloc_buf
+ async
->buf_write_ptr
) = x
;
358 comedi_buf_write_free(async
, sizeof(short));
361 EXPORT_SYMBOL_GPL(comedi_buf_put
);
363 int comedi_buf_get(struct comedi_async
*async
, short *x
)
365 unsigned int n
= comedi_buf_read_n_available(async
);
367 if (n
< sizeof(short))
369 comedi_buf_read_alloc(async
, sizeof(short));
370 *x
= *(short *)(async
->prealloc_buf
+ async
->buf_read_ptr
);
371 comedi_buf_read_free(async
, sizeof(short));
374 EXPORT_SYMBOL_GPL(comedi_buf_get
);
376 void comedi_buf_memcpy_to(struct comedi_async
*async
, unsigned int offset
,
377 const void *data
, unsigned int num_bytes
)
379 unsigned int write_ptr
= async
->buf_write_ptr
+ offset
;
381 if (write_ptr
>= async
->prealloc_bufsz
)
382 write_ptr
%= async
->prealloc_bufsz
;
385 unsigned int block_size
;
387 if (write_ptr
+ num_bytes
> async
->prealloc_bufsz
)
388 block_size
= async
->prealloc_bufsz
- write_ptr
;
390 block_size
= num_bytes
;
392 memcpy(async
->prealloc_buf
+ write_ptr
, data
, block_size
);
395 num_bytes
-= block_size
;
400 EXPORT_SYMBOL_GPL(comedi_buf_memcpy_to
);
402 void comedi_buf_memcpy_from(struct comedi_async
*async
, unsigned int offset
,
403 void *dest
, unsigned int nbytes
)
406 unsigned int read_ptr
= async
->buf_read_ptr
+ offset
;
408 if (read_ptr
>= async
->prealloc_bufsz
)
409 read_ptr
%= async
->prealloc_bufsz
;
412 unsigned int block_size
;
414 src
= async
->prealloc_buf
+ read_ptr
;
416 if (nbytes
>= async
->prealloc_bufsz
- read_ptr
)
417 block_size
= async
->prealloc_bufsz
- read_ptr
;
421 memcpy(dest
, src
, block_size
);
422 nbytes
-= block_size
;
427 EXPORT_SYMBOL_GPL(comedi_buf_memcpy_from
);