Got rid of unnecessary casts when initializing comedi_driver.board_name
[comedi.git] / comedi / drivers.c
1 /*
2     module/drivers.c
3     functions for manipulating drivers
4
5     COMEDI - Linux Control and Measurement Device Interface
6     Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
7
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License, or
11     (at your option) any later version.
12
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17
18     You should have received a copy of the GNU General Public License
19     along with this program; if not, write to the Free Software
20     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 */
23
24 #define _GNU_SOURCE
25
26 #define __NO_VERSION__
27 #include "comedi_fops.h"
28 #include <linux/device.h>
29 #include <linux/module.h>
30 #include <linux/errno.h>
31 #include <linux/kernel.h>
32 #include <linux/sched.h>
33 #include <linux/fcntl.h>
34 #include <linux/delay.h>
35 #include <linux/ioport.h>
36 #include <linux/mm.h>
37 #include <linux/slab.h>
38 #include <linux/comedidev.h>
39 #include <linux/wrapper.h>
40 #include <linux/highmem.h>  /* for SuSE brokenness */
41 #include <linux/vmalloc.h>
42 #include <linux/cdev.h>
43 #include <linux/dma-mapping.h>
44
45 #include <asm/io.h>
46 #include <asm/system.h>
47
48 static int postconfig(comedi_device *dev);
49 static int insn_rw_emulate_bits(comedi_device *dev,comedi_subdevice *s,
50         comedi_insn *insn,lsampl_t *data);
51 static int insn_inval(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
52 static void *comedi_recognize(comedi_driver *driv, const char *name);
53 static void comedi_report_boards(comedi_driver *driv);
54 static int poll_invalid(comedi_device *dev,comedi_subdevice *s);
55 int comedi_buf_alloc(comedi_device *dev, comedi_subdevice *s,
56         unsigned long new_size);
57
58 comedi_driver *comedi_drivers;
59
60 int comedi_modprobe(int minor)
61 {
62         return -EINVAL;
63 }
64
65 static void cleanup_device(comedi_device *dev)
66 {
67         int i;
68         comedi_subdevice *s;
69
70         if(dev->subdevices)
71         {
72                 for(i = 0; i < dev->n_subdevices; i++)
73                 {
74                         s = dev->subdevices + i;
75                         if(s->class_dev)
76                         {
77                                 unsigned minor = comedi_construct_minor_for_subdevice(dev, i);
78                                 dev_t devt = MKDEV(COMEDI_MAJOR, minor);
79                                 class_device_destroy(comedi_class, devt);
80                         }
81                         if(s->async)
82                         {
83                                 comedi_buf_alloc(dev, s, 0);
84                                 kfree(s->async);
85                         }
86                 }
87                 kfree(dev->subdevices);
88                 dev->subdevices = NULL;
89                 dev->n_subdevices = 0;
90         }
91         if(dev->private)
92         {
93                 kfree(dev->private);
94                 dev->private = NULL;
95         }
96         module_put(dev->driver->module);
97         dev->driver = 0;
98         dev->board_name = NULL;
99         dev->board_ptr = NULL;
100         dev->iobase = 0;
101         dev->irq = 0;
102         dev->read_subdev = NULL;
103         dev->write_subdev = NULL;
104         dev->open = NULL;
105         dev->close = NULL;
106         comedi_set_hw_dev(dev, NULL);
107 }
108
109 static int __comedi_device_detach(comedi_device *dev)
110 {
111         dev->attached = 0;
112         if(dev->driver){
113                 dev->driver->detach(dev);
114         }else{
115                 printk("BUG: dev->driver=NULL in comedi_device_detach()\n");
116         }
117         cleanup_device(dev);
118         return 0;
119 }
120
121 int comedi_device_detach(comedi_device *dev)
122 {
123         if(!dev->attached)
124                 return 0;
125         return __comedi_device_detach(dev);
126 }
127
128 int comedi_device_attach(comedi_device *dev,comedi_devconfig *it)
129 {
130         comedi_driver *driv;
131         int ret;
132
133         if(dev->attached)
134                 return -EBUSY;
135
136         for(driv=comedi_drivers;driv;driv=driv->next){
137                 if(!try_module_get( driv->module ))
138                 {
139                         printk( "comedi: failed to increment module count, skipping\n" );
140                         continue;
141                 }
142                 if(driv->num_names){
143                         dev->board_ptr = comedi_recognize(driv, it->board_name);
144                         if(dev->board_ptr==NULL){
145                                 module_put( driv->module );
146                                 continue;
147                         }
148                 }else{
149                         if(strcmp(driv->driver_name,it->board_name)){
150                                 module_put( driv->module );
151                                 continue;
152                         }
153                 }
154                 //initialize dev->driver here so comedi_error() can be called from attach
155                 dev->driver=driv;
156                 ret=driv->attach(dev,it);
157                 if(ret<0){
158                         __comedi_device_detach(dev);
159                         return ret;
160                 }
161                 goto attached;
162         }
163
164         // recognize has failed if we get here
165         // report valid board names before returning error
166         for(driv=comedi_drivers;driv;driv=driv->next){
167                 if(!try_module_get( driv->module ))
168                 {
169                         printk( "comedi: failed to increment module count\n" );
170                         continue;
171                 }
172                 comedi_report_boards(driv);
173                 module_put( driv->module );
174         }
175         return -EIO;
176
177 attached:
178         /* do a little post-config cleanup */
179         ret = postconfig(dev);
180         if(ret < 0)
181         {
182                 __comedi_device_detach(dev);
183                 return ret;
184         }
185
186         init_waitqueue_head(&dev->read_wait);
187         init_waitqueue_head(&dev->write_wait);
188
189         if(!dev->board_name){
190                 printk("BUG: dev->board_name=<%p>\n",dev->board_name);
191                 dev->board_name="BUG";
192         }
193         smp_wmb();
194         dev->attached=1;
195
196         return 0;
197 }
198
199 int comedi_driver_register(comedi_driver *driver)
200 {
201         driver->next=comedi_drivers;
202         comedi_drivers=driver;
203
204         return 0;
205 }
206
207 int comedi_driver_unregister(comedi_driver *driver)
208 {
209         comedi_driver *prev;
210         int i;
211
212         /* check for devices using this driver */
213         for(i=0;i<COMEDI_NDEVICES;i++){
214                 comedi_device *dev;
215
216                 dev = comedi_devices + i;
217                 if(dev->attached && dev->driver==driver){
218                         if(dev->use_count)
219                                 printk("BUG! detaching device with use_count=%d\n",dev->use_count);
220                         comedi_device_detach(dev);
221                 }
222         }
223
224         if(comedi_drivers==driver){
225                 comedi_drivers=driver->next;
226                 return 0;
227         }
228
229         for(prev=comedi_drivers;prev->next;prev=prev->next){
230                 if(prev->next==driver){
231                         prev->next=driver->next;
232                         return 0;
233                 }
234         }
235         return -EINVAL;
236 }
237
238 comedi_device *comedi_allocate_dev(comedi_driver *driver)
239 {
240         return NULL;
241 }
242
243 void comedi_deallocate_dev(comedi_device *dev)
244 {
245
246 }
247
248 static int postconfig(comedi_device *dev)
249 {
250         int i;
251         comedi_subdevice *s;
252         comedi_async *async = NULL;
253         int ret;
254
255         for(i=0;i<dev->n_subdevices;i++){
256                 s=dev->subdevices+i;
257
258                 if(s->type==COMEDI_SUBD_UNUSED)
259                         continue;
260
261                 if(s->len_chanlist==0)
262                         s->len_chanlist=1;
263
264                 if(s->do_cmd){
265                         unsigned minor;
266                         dev_t devt;
267
268                         BUG_ON((s->subdev_flags & (SDF_CMD_READ | SDF_CMD_WRITE)) == 0);
269
270                         async = kmalloc(sizeof(comedi_async), GFP_KERNEL);
271                         if(async == NULL)
272                         {
273                                 printk("failed to allocate async struct\n");
274                                 return -ENOMEM;
275                         }
276                         memset(async, 0, sizeof(comedi_async));
277                         async->subdevice = s;
278                         s->async = async;
279
280 #define DEFAULT_BUF_MAXSIZE (64*1024)
281 #define DEFAULT_BUF_SIZE (64*1024)
282
283                         async->max_bufsize = DEFAULT_BUF_MAXSIZE;
284
285                         async->prealloc_buf = NULL;
286                         async->prealloc_bufsz = 0;
287                         if(comedi_buf_alloc(dev,s,DEFAULT_BUF_SIZE) < 0){
288                                 printk("Buffer allocation failed\n");
289                                 return -ENOMEM;
290                         }
291                         if(s->buf_change){
292                                 ret = s->buf_change(dev,s,DEFAULT_BUF_SIZE);
293                                 if(ret < 0)return ret;
294                         }
295                         minor = comedi_construct_minor_for_subdevice(dev, i);
296                         devt = MKDEV(COMEDI_MAJOR, minor);
297                         s->class_dev = COMEDI_CLASS_DEVICE_CREATE(comedi_class, dev->class_dev,
298                                 devt, NULL, "comedi%i_sub%i", dev->minor, i);
299                 }
300
301                 if(!s->range_table && !s->range_table_list)
302                         s->range_table=&range_unknown;
303
304                 if(!s->insn_read && s->insn_bits)
305                         s->insn_read = insn_rw_emulate_bits;
306                 if(!s->insn_write && s->insn_bits)
307                         s->insn_write = insn_rw_emulate_bits;
308
309                 if(!s->insn_read)s->insn_read = insn_inval;
310                 if(!s->insn_write)s->insn_write = insn_inval;
311                 if(!s->insn_bits)s->insn_bits = insn_inval;
312                 if(!s->insn_config)s->insn_config = insn_inval;
313
314                 if(!s->poll)s->poll=poll_invalid;
315         }
316
317         return 0;
318 }
319
320 // generic recognize function for drivers that register their supported board names
321 void *comedi_recognize(comedi_driver *driv, const char *name)
322 {
323         unsigned i;
324         const char * const *name_ptr = driv->board_name;
325         for(i = 0; i < driv->num_names; i++)
326         {
327                 if(strcmp(*name_ptr, name) == 0)
328                         return (void*)name_ptr;
329                 name_ptr = (const char * const *)((const char *)name_ptr + driv->offset);
330         }
331
332         return NULL;
333 }
334
335 void comedi_report_boards(comedi_driver *driv)
336 {
337         unsigned int i;
338         const char * const *name_ptr;
339
340         printk("comedi: valid board names for %s driver are:\n", driv->driver_name);
341
342         name_ptr = driv->board_name;
343         for(i = 0; i < driv->num_names; i++)
344         {
345                 printk(" %s\n", *name_ptr);
346                 name_ptr = (const char **)((char *)name_ptr + driv->offset);
347         }
348
349         if(driv->num_names == 0)
350                 printk(" %s\n", driv->driver_name);
351 }
352
353 static int poll_invalid(comedi_device *dev,comedi_subdevice *s)
354 {
355         return -EINVAL;
356 }
357
358 static int insn_inval(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
359 {
360         return -EINVAL;
361 }
362
363 static int insn_rw_emulate_bits(comedi_device *dev,comedi_subdevice *s,
364         comedi_insn *insn,lsampl_t *data)
365 {
366         comedi_insn new_insn;
367         int ret;
368         static const unsigned channels_per_bitfield = 32;
369
370         unsigned chan = CR_CHAN(insn->chanspec);
371         const unsigned base_bitfield_channel = (chan < channels_per_bitfield) ? 0 : chan;
372         lsampl_t new_data[2];
373         memset(new_data, 0, sizeof(new_data));
374         memset(&new_insn, 0, sizeof(new_insn));
375         new_insn.insn = INSN_BITS;
376         new_insn.chanspec = base_bitfield_channel;
377         new_insn.n = 2;
378         new_insn.data = new_data;
379         new_insn.subdev = insn->subdev;
380
381         if(insn->insn == INSN_WRITE)
382         {
383                 if(!(s->subdev_flags & SDF_WRITABLE))
384                         return -EINVAL;
385                 new_data[0] = 1 << (chan - base_bitfield_channel); /* mask */
386                 new_data[1] = data[0] ? (1 << (chan - base_bitfield_channel)) : 0; /* bits */
387         }
388
389         ret = s->insn_bits(dev,s,&new_insn,new_data);
390         if(ret < 0) return ret;
391
392         if(insn->insn == INSN_READ){
393                 data[0] = (new_data[1] >> (chan - base_bitfield_channel)) & 1;
394         }
395
396         return 1;
397 }
398
399
400 static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr)
401 {
402         unsigned long ret = 0UL;
403         pmd_t *pmd;
404         pte_t *ptep, pte;
405         pud_t *pud;
406
407         if(!pgd_none(*pgd)){
408                 pud = pud_offset(pgd, adr);
409                 pmd = pmd_offset(pud, adr);
410                 if(!pmd_none(*pmd)){
411                         ptep = pte_offset_kernel(pmd, adr);
412                         pte = *ptep;
413                         if(pte_present(pte)){
414                                 ret = (unsigned long) page_address(pte_page(pte));
415                                 ret |= (adr & (PAGE_SIZE - 1));
416                         }
417                 }
418         }
419         return ret;
420 }
421
422 static inline unsigned long kvirt_to_kva(unsigned long adr)
423 {
424         unsigned long va, kva;
425
426         va = adr;
427         kva = uvirt_to_kva(pgd_offset_k(va), va);
428
429         return kva;
430 }
431
432
433 int comedi_buf_alloc(comedi_device *dev, comedi_subdevice *s,
434         unsigned long new_size)
435 {
436         comedi_async *async = s->async;
437
438         /* if no change is required, do nothing */
439         if(async->prealloc_buf && async->prealloc_bufsz == new_size){
440                 return 0;
441         }
442         // deallocate old buffer
443         if(async->prealloc_buf && s->async_dma_dir != DMA_NONE)
444         {
445                 vunmap(async->prealloc_buf);
446                 async->prealloc_buf = NULL;
447                 async->prealloc_bufsz = 0;
448         }
449         if(async->buf_page_list)
450         {
451                 unsigned i;
452                 for(i = 0; i < async->n_buf_pages; ++i)
453                 {
454                         if(async->buf_page_list[i].virt_addr)
455                                 mem_map_unreserve(virt_to_page(async->buf_page_list[i].virt_addr));
456                         if(async->buf_page_list[i].dma_addr)
457                         {
458                                 dma_free_coherent(dev->hw_dev, PAGE_SIZE,
459                                         async->buf_page_list[i].virt_addr, async->buf_page_list[i].dma_addr);
460                         }
461                 }
462                 vfree(async->buf_page_list);
463                 async->buf_page_list = NULL;
464                 async->n_buf_pages = 0;
465         }
466         if(async->prealloc_buf && s->async_dma_dir == DMA_NONE)
467         {
468                 vfree(async->prealloc_buf);
469                 async->prealloc_buf = NULL;
470                 async->prealloc_bufsz = 0;
471         }
472         // allocate new buffer
473         if(new_size){
474                 int i;
475                 unsigned n_pages = (new_size + PAGE_SIZE - 1) >> PAGE_SHIFT;
476
477                 // size is rounded up to nearest multiple of PAGE_SIZE
478                 new_size = n_pages << PAGE_SHIFT;
479
480                 async->buf_page_list = vmalloc(sizeof(struct comedi_buf_page) * n_pages);
481                 if(async->buf_page_list == NULL)
482                 {
483                         return -ENOMEM;
484                 }
485                 memset(async->buf_page_list, 0, sizeof(struct comedi_buf_page) * n_pages);
486                 async->n_buf_pages = n_pages;
487
488                 if(s->async_dma_dir != DMA_NONE)
489                 {
490                         struct page** pages = NULL;
491
492                         pages = vmalloc(sizeof(struct page*) * n_pages);
493                         if(pages == NULL)
494                         {
495                                 return -ENOMEM;
496                         }
497                         for(i = 0; i < n_pages; i++)
498                         {
499                                 async->buf_page_list[i].virt_addr = dma_alloc_coherent(dev->hw_dev,
500                                         PAGE_SIZE, &async->buf_page_list[i].dma_addr, GFP_KERNEL | __GFP_COMP);
501                                 if(async->buf_page_list[i].virt_addr == NULL)
502                                 {
503                                         vfree(pages);
504                                         return -ENOMEM;
505                                 }
506                                 mem_map_reserve(virt_to_page(async->buf_page_list[i].virt_addr));
507                                 pages[i] = virt_to_page(async->buf_page_list[i].virt_addr);
508                         }
509                         async->prealloc_buf = vmap(pages, n_pages, VM_MAP, PAGE_KERNEL_NOCACHE);
510                         vfree(pages);
511                         if(async->prealloc_buf == NULL) return -ENOMEM;
512                 }else
513                 {
514                         async->prealloc_buf = vmalloc(new_size);
515                         if(async->prealloc_buf == NULL)
516                         {
517                                 return -ENOMEM;
518                         }
519                         for(i = 0; i < n_pages; i++)
520                         {
521                                 async->buf_page_list[i].virt_addr = async->prealloc_buf + PAGE_SIZE * i;
522                                 mem_map_reserve(virt_to_page(async->buf_page_list[i].virt_addr));
523                         }
524                 }
525         }
526         async->prealloc_bufsz = new_size;
527
528         return 0;
529 }
530
531 /* munging is applied to data by core as it passes between user
532  * and kernel space */
533 unsigned int comedi_buf_munge(comedi_async *async,
534         unsigned int num_bytes)
535 {
536         comedi_subdevice *s = async->subdevice;
537         unsigned int count = 0;
538         const unsigned num_sample_bytes = bytes_per_sample(s);
539
540         if( s->munge == NULL || ( async->cmd.flags & CMDF_RAWDATA ) )
541         {
542                 async->munge_count += num_bytes;
543                 if((int)(async->munge_count - async->buf_write_count) > 0) BUG();
544                 return num_bytes;
545         }
546         /* don't munge partial samples */
547         num_bytes -= num_bytes % num_sample_bytes;
548         while( count < num_bytes )
549         {
550                 int block_size;
551
552                 block_size = num_bytes - count;
553                 if(block_size < 0)
554                 {
555                         rt_printk("%s: %s: bug! block_size is negative\n", __FILE__, __FUNCTION__);
556                         break;
557                 }
558                 if( (int)(async->munge_ptr + block_size - async->prealloc_bufsz) > 0 )
559                         block_size = async->prealloc_bufsz - async->munge_ptr;
560
561                 s->munge(s->device, s, async->prealloc_buf + async->munge_ptr,
562                         block_size, async->munge_chan );
563
564                 smp_wmb(); //barrier insures data is munged in buffer before munge_count is incremented
565
566                 async->munge_chan += block_size / num_sample_bytes;
567                 async->munge_chan %= async->cmd.chanlist_len;
568                 async->munge_count += block_size;
569                 async->munge_ptr += block_size;
570                 async->munge_ptr %= async->prealloc_bufsz;
571                 count += block_size;
572         }
573         if((int)(async->munge_count - async->buf_write_count) > 0) BUG();
574         return count;
575 }
576
577 unsigned int comedi_buf_write_n_available(comedi_async *async)
578 {
579         unsigned int free_end;
580         unsigned int nbytes;
581
582         if(async == NULL) return 0;
583
584         free_end = async->buf_read_count + async->prealloc_bufsz;
585         nbytes = free_end - async->buf_write_alloc_count;
586         nbytes -= nbytes % bytes_per_sample(async->subdevice);
587         /* barrier insures the read of buf_read_count in this
588         query occurs before any following writes to the buffer which
589         might be based on the return value from this query.
590         */
591         smp_mb();
592         return nbytes;
593 }
594
595 /* allocates chunk for the writer from free buffer space */
596 unsigned int comedi_buf_write_alloc(comedi_async *async, unsigned int nbytes)
597 {
598         unsigned int free_end = async->buf_read_count + async->prealloc_bufsz;
599
600         if((int)(async->buf_write_alloc_count + nbytes - free_end) > 0){
601                 nbytes = free_end - async->buf_write_alloc_count;
602         }
603         async->buf_write_alloc_count += nbytes;
604         /* barrier insures the read of buf_read_count above occurs before
605         we write data to the write-alloc'ed buffer space */
606         smp_mb();
607         return nbytes;
608 }
609
610 /* allocates nothing unless it can completely fulfill the request */
611 unsigned int comedi_buf_write_alloc_strict(comedi_async *async,
612         unsigned int nbytes)
613 {
614         unsigned int free_end = async->buf_read_count + async->prealloc_bufsz;
615
616         if((int)(async->buf_write_alloc_count + nbytes - free_end) > 0){
617                 nbytes = 0;
618         }
619         async->buf_write_alloc_count += nbytes;
620         /* barrier insures the read of buf_read_count above occurs before
621         we write data to the write-alloc'ed buffer space */
622         smp_mb();
623         return nbytes;
624 }
625
626 /* transfers a chunk from writer to filled buffer space */
627 unsigned comedi_buf_write_free(comedi_async *async, unsigned int nbytes)
628 {
629         if((int)(async->buf_write_count + nbytes - async->buf_write_alloc_count) > 0)
630         {
631                 rt_printk("comedi: attempted to write-free more bytes than have been write-allocated.\n");
632                 nbytes = async->buf_write_alloc_count - async->buf_write_count;
633         }
634         async->buf_write_count += nbytes;
635         async->buf_write_ptr += nbytes;
636         comedi_buf_munge(async, async->buf_write_count - async->munge_count);
637         if(async->buf_write_ptr >= async->prealloc_bufsz){
638                 async->buf_write_ptr %= async->prealloc_bufsz;
639         }
640         return nbytes;
641 }
642
643 /* allocates a chunk for the reader from filled (and munged) buffer space */
644 unsigned comedi_buf_read_alloc(comedi_async *async, unsigned nbytes)
645 {
646         if((int)(async->buf_read_alloc_count + nbytes - async->munge_count) > 0)
647         {
648                 nbytes = async->munge_count - async->buf_read_alloc_count;
649         }
650         async->buf_read_alloc_count += nbytes;
651         /* barrier insures read of munge_count occurs before we actually read
652         data out of buffer */
653         smp_rmb();
654         return nbytes;
655 }
656
657 /* transfers control of a chunk from reader to free buffer space */
658 unsigned comedi_buf_read_free(comedi_async *async, unsigned int nbytes)
659 {
660         // barrier insures data has been read out of buffer before read count is incremented
661         smp_mb();
662         if((int)(async->buf_read_count + nbytes - async->buf_read_alloc_count) > 0)
663         {
664                 rt_printk("comedi: attempted to read-free more bytes than have been read-allocated.\n");
665                 nbytes = async->buf_read_alloc_count - async->buf_read_count;
666         }
667         async->buf_read_count += nbytes;
668         async->buf_read_ptr += nbytes;
669         async->buf_read_ptr %= async->prealloc_bufsz;
670         return nbytes;
671 }
672
673 void comedi_buf_memcpy_to( comedi_async *async, unsigned int offset, const void *data,
674         unsigned int num_bytes )
675 {
676         unsigned int write_ptr = async->buf_write_ptr + offset;
677
678         if( write_ptr >= async->prealloc_bufsz )
679                 write_ptr %= async->prealloc_bufsz;
680
681         while( num_bytes )
682         {
683                 unsigned int block_size;
684
685                 if( write_ptr + num_bytes > async->prealloc_bufsz)
686                         block_size = async->prealloc_bufsz - write_ptr;
687                 else
688                         block_size = num_bytes;
689
690                 memcpy( async->prealloc_buf + write_ptr, data, block_size );
691
692                 data += block_size;
693                 num_bytes -= block_size;
694
695                 write_ptr = 0;
696         }
697 }
698
699 void comedi_buf_memcpy_from(comedi_async *async, unsigned int offset,
700         void *dest, unsigned int nbytes)
701 {
702         void *src;
703         unsigned int read_ptr = async->buf_read_ptr + offset;
704
705         if( read_ptr >= async->prealloc_bufsz )
706                 read_ptr %= async->prealloc_bufsz;
707
708         while( nbytes )
709         {
710                 unsigned int block_size;
711
712                 src = async->prealloc_buf + read_ptr;
713
714                 if( nbytes >= async->prealloc_bufsz - read_ptr )
715                         block_size = async->prealloc_bufsz - read_ptr;
716                 else
717                         block_size = nbytes;
718
719                 memcpy(dest, src, block_size );
720                 nbytes -= block_size;
721                 dest += block_size;
722                 read_ptr = 0;
723         }
724 }
725
726 unsigned int comedi_buf_read_n_available(comedi_async *async)
727 {
728         unsigned num_bytes;
729
730         if(async == NULL)
731                 return 0;
732         num_bytes = async->munge_count - async->buf_read_count;
733         /* barrier insures the read of munge_count in this
734         query occurs before any following reads of the buffer which
735         might be based on the return value from this query.
736         */
737         smp_rmb();
738         return num_bytes;
739 }
740
741 int comedi_buf_get(comedi_async *async, sampl_t *x)
742 {
743         unsigned int n = comedi_buf_read_n_available(async);
744
745         if(n < sizeof(sampl_t)) return 0;
746         comedi_buf_read_alloc(async, sizeof(sampl_t));
747         *x = *(sampl_t *)(async->prealloc_buf + async->buf_read_ptr);
748         comedi_buf_read_free(async, sizeof(sampl_t));
749         return 1;
750 }
751
752 int comedi_buf_put(comedi_async *async, sampl_t x)
753 {
754         unsigned int n = comedi_buf_write_alloc_strict(async, sizeof(sampl_t));
755
756         if(n<sizeof(sampl_t)){
757                 async->events |= COMEDI_CB_ERROR;
758                 return 0;
759         }
760         *(sampl_t *)(async->prealloc_buf + async->buf_write_ptr) = x;
761         comedi_buf_write_free(async, sizeof(sampl_t));
762         return 1;
763 }
764
765 void comedi_reset_async_buf(comedi_async *async)
766 {
767         async->buf_write_alloc_count = 0;
768         async->buf_write_count = 0;
769         async->buf_read_alloc_count = 0;
770         async->buf_read_count = 0;
771
772         async->buf_write_ptr = 0;
773         async->buf_read_ptr = 0;
774
775         async->cur_chan = 0;
776         async->scan_progress = 0;
777         async->munge_chan = 0;
778         async->munge_count = 0;
779         async->munge_ptr = 0;
780
781         async->events = 0;
782 }
783