linux/drivers/comedi/comedi_buf.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * comedi_buf.c
   4 *
   5 * COMEDI - Linux Control and Measurement Device Interface
   6 * Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
   7 * Copyright (C) 2002 Frank Mori Hess <fmhess@users.sourceforge.net>
   8 */
   9
  10#include <linux/vmalloc.h>
  11#include <linux/slab.h>
  12#include <linux/comedi/comedidev.h>
  13#include "comedi_internal.h"
  14
  15#ifdef PAGE_KERNEL_NOCACHE
  16#define COMEDI_PAGE_PROTECTION          PAGE_KERNEL_NOCACHE
  17#else
  18#define COMEDI_PAGE_PROTECTION          PAGE_KERNEL
  19#endif
  20
  21static void comedi_buf_map_kref_release(struct kref *kref)
  22{
  23        struct comedi_buf_map *bm =
  24                container_of(kref, struct comedi_buf_map, refcount);
  25        struct comedi_buf_page *buf;
  26        unsigned int i;
  27
  28        if (bm->page_list) {
  29                if (bm->dma_dir != DMA_NONE) {
  30                        /*
  31                         * DMA buffer was allocated as a single block.
  32                         * Address is in page_list[0].
  33                         */
  34                        buf = &bm->page_list[0];
  35                        dma_free_coherent(bm->dma_hw_dev,
  36                                          PAGE_SIZE * bm->n_pages,
  37                                          buf->virt_addr, buf->dma_addr);
  38                } else {
  39                        for (i = 0; i < bm->n_pages; i++) {
  40                                buf = &bm->page_list[i];
  41                                ClearPageReserved(virt_to_page(buf->virt_addr));
  42                                free_page((unsigned long)buf->virt_addr);
  43                        }
  44                }
  45                vfree(bm->page_list);
  46        }
  47        if (bm->dma_dir != DMA_NONE)
  48                put_device(bm->dma_hw_dev);
  49        kfree(bm);
  50}
  51
  52static void __comedi_buf_free(struct comedi_device *dev,
  53                              struct comedi_subdevice *s)
  54{
  55        struct comedi_async *async = s->async;
  56        struct comedi_buf_map *bm;
  57        unsigned long flags;
  58
  59        if (async->prealloc_buf) {
  60                if (s->async_dma_dir == DMA_NONE)
  61                        vunmap(async->prealloc_buf);
  62                async->prealloc_buf = NULL;
  63                async->prealloc_bufsz = 0;
  64        }
  65
  66        spin_lock_irqsave(&s->spin_lock, flags);
  67        bm = async->buf_map;
  68        async->buf_map = NULL;
  69        spin_unlock_irqrestore(&s->spin_lock, flags);
  70        comedi_buf_map_put(bm);
  71}
  72
  73static struct comedi_buf_map *
  74comedi_buf_map_alloc(struct comedi_device *dev, enum dma_data_direction dma_dir,
  75                     unsigned int n_pages)
  76{
  77        struct comedi_buf_map *bm;
  78        struct comedi_buf_page *buf;
  79        unsigned int i;
  80
  81        bm = kzalloc(sizeof(*bm), GFP_KERNEL);
  82        if (!bm)
  83                return NULL;
  84
  85        kref_init(&bm->refcount);
  86        bm->dma_dir = dma_dir;
  87        if (bm->dma_dir != DMA_NONE) {
  88                /* Need ref to hardware device to free buffer later. */
  89                bm->dma_hw_dev = get_device(dev->hw_dev);
  90        }
  91
  92        bm->page_list = vzalloc(sizeof(*buf) * n_pages);
  93        if (!bm->page_list)
  94                goto err;
  95
  96        if (bm->dma_dir != DMA_NONE) {
  97                void *virt_addr;
  98                dma_addr_t dma_addr;
  99
 100                /*
 101                 * Currently, the DMA buffer needs to be allocated as a
 102                 * single block so that it can be mmap()'ed.
 103                 */
 104                virt_addr = dma_alloc_coherent(bm->dma_hw_dev,
 105                                               PAGE_SIZE * n_pages, &dma_addr,
 106                                               GFP_KERNEL);
 107                if (!virt_addr)
 108                        goto err;
 109
 110                for (i = 0; i < n_pages; i++) {
 111                        buf = &bm->page_list[i];
 112                        buf->virt_addr = virt_addr + (i << PAGE_SHIFT);
 113                        buf->dma_addr = dma_addr + (i << PAGE_SHIFT);
 114                }
 115
 116                bm->n_pages = i;
 117        } else {
 118                for (i = 0; i < n_pages; i++) {
 119                        buf = &bm->page_list[i];
 120                        buf->virt_addr = (void *)get_zeroed_page(GFP_KERNEL);
 121                        if (!buf->virt_addr)
 122                                break;
 123
 124                        SetPageReserved(virt_to_page(buf->virt_addr));
 125                }
 126
 127                bm->n_pages = i;
 128                if (i < n_pages)
 129                        goto err;
 130        }
 131
 132        return bm;
 133
 134err:
 135        comedi_buf_map_put(bm);
 136        return NULL;
 137}
 138
 139static void __comedi_buf_alloc(struct comedi_device *dev,
 140                               struct comedi_subdevice *s,
 141                               unsigned int n_pages)
 142{
 143        struct comedi_async *async = s->async;
 144        struct page **pages = NULL;
 145        struct comedi_buf_map *bm;
 146        struct comedi_buf_page *buf;
 147        unsigned long flags;
 148        unsigned int i;
 149
 150        if (!IS_ENABLED(CONFIG_HAS_DMA) && s->async_dma_dir != DMA_NONE) {
 151                dev_err(dev->class_dev,
 152                        "dma buffer allocation not supported\n");
 153                return;
 154        }
 155
 156        bm = comedi_buf_map_alloc(dev, s->async_dma_dir, n_pages);
 157        if (!bm)
 158                return;
 159
 160        spin_lock_irqsave(&s->spin_lock, flags);
 161        async->buf_map = bm;
 162        spin_unlock_irqrestore(&s->spin_lock, flags);
 163
 164        if (bm->dma_dir != DMA_NONE) {
 165                /*
 166                 * DMA buffer was allocated as a single block.
 167                 * Address is in page_list[0].
 168                 */
 169                buf = &bm->page_list[0];
 170                async->prealloc_buf = buf->virt_addr;
 171        } else {
 172                pages = vmalloc(sizeof(struct page *) * n_pages);
 173                if (!pages)
 174                        return;
 175
 176                for (i = 0; i < n_pages; i++) {
 177                        buf = &bm->page_list[i];
 178                        pages[i] = virt_to_page(buf->virt_addr);
 179                }
 180
 181                /* vmap the pages to prealloc_buf */
 182                async->prealloc_buf = vmap(pages, n_pages, VM_MAP,
 183                                           COMEDI_PAGE_PROTECTION);
 184
 185                vfree(pages);
 186        }
 187}
 188
 189void comedi_buf_map_get(struct comedi_buf_map *bm)
 190{
 191        if (bm)
 192                kref_get(&bm->refcount);
 193}
 194
 195int comedi_buf_map_put(struct comedi_buf_map *bm)
 196{
 197        if (bm)
 198                return kref_put(&bm->refcount, comedi_buf_map_kref_release);
 199        return 1;
 200}
 201
 202/* helper for "access" vm operation */
 203int comedi_buf_map_access(struct comedi_buf_map *bm, unsigned long offset,
 204                          void *buf, int len, int write)
 205{
 206        unsigned int pgoff = offset_in_page(offset);
 207        unsigned long pg = offset >> PAGE_SHIFT;
 208        int done = 0;
 209
 210        while (done < len && pg < bm->n_pages) {
 211                int l = min_t(int, len - done, PAGE_SIZE - pgoff);
 212                void *b = bm->page_list[pg].virt_addr + pgoff;
 213
 214                if (write)
 215                        memcpy(b, buf, l);
 216                else
 217                        memcpy(buf, b, l);
 218                buf += l;
 219                done += l;
 220                pg++;
 221                pgoff = 0;
 222        }
 223        return done;
 224}
 225
 226/* returns s->async->buf_map and increments its kref refcount */
 227struct comedi_buf_map *
 228comedi_buf_map_from_subdev_get(struct comedi_subdevice *s)
 229{
 230        struct comedi_async *async = s->async;
 231        struct comedi_buf_map *bm = NULL;
 232        unsigned long flags;
 233
 234        if (!async)
 235                return NULL;
 236
 237        spin_lock_irqsave(&s->spin_lock, flags);
 238        bm = async->buf_map;
 239        /* only want it if buffer pages allocated */
 240        if (bm && bm->n_pages)
 241                comedi_buf_map_get(bm);
 242        else
 243                bm = NULL;
 244        spin_unlock_irqrestore(&s->spin_lock, flags);
 245
 246        return bm;
 247}
 248
 249bool comedi_buf_is_mmapped(struct comedi_subdevice *s)
 250{
 251        struct comedi_buf_map *bm = s->async->buf_map;
 252
 253        return bm && (kref_read(&bm->refcount) > 1);
 254}
 255
 256int comedi_buf_alloc(struct comedi_device *dev, struct comedi_subdevice *s,
 257                     unsigned long new_size)
 258{
 259        struct comedi_async *async = s->async;
 260
 261        lockdep_assert_held(&dev->mutex);
 262
 263        /* Round up new_size to multiple of PAGE_SIZE */
 264        new_size = (new_size + PAGE_SIZE - 1) & PAGE_MASK;
 265
 266        /* if no change is required, do nothing */
 267        if (async->prealloc_buf && async->prealloc_bufsz == new_size)
 268                return 0;
 269
 270        /* deallocate old buffer */
 271        __comedi_buf_free(dev, s);
 272
 273        /* allocate new buffer */
 274        if (new_size) {
 275                unsigned int n_pages = new_size >> PAGE_SHIFT;
 276
 277                __comedi_buf_alloc(dev, s, n_pages);
 278
 279                if (!async->prealloc_buf) {
 280                        /* allocation failed */
 281                        __comedi_buf_free(dev, s);
 282                        return -ENOMEM;
 283                }
 284        }
 285        async->prealloc_bufsz = new_size;
 286
 287        return 0;
 288}
 289
 290void comedi_buf_reset(struct comedi_subdevice *s)
 291{
 292        struct comedi_async *async = s->async;
 293
 294        async->buf_write_alloc_count = 0;
 295        async->buf_write_count = 0;
 296        async->buf_read_alloc_count = 0;
 297        async->buf_read_count = 0;
 298
 299        async->buf_write_ptr = 0;
 300        async->buf_read_ptr = 0;
 301
 302        async->cur_chan = 0;
 303        async->scans_done = 0;
 304        async->scan_progress = 0;
 305        async->munge_chan = 0;
 306        async->munge_count = 0;
 307        async->munge_ptr = 0;
 308
 309        async->events = 0;
 310}
 311
 312static unsigned int comedi_buf_write_n_unalloc(struct comedi_subdevice *s)
 313{
 314        struct comedi_async *async = s->async;
 315        unsigned int free_end = async->buf_read_count + async->prealloc_bufsz;
 316
 317        return free_end - async->buf_write_alloc_count;
 318}
 319
 320unsigned int comedi_buf_write_n_available(struct comedi_subdevice *s)
 321{
 322        struct comedi_async *async = s->async;
 323        unsigned int free_end = async->buf_read_count + async->prealloc_bufsz;
 324
 325        return free_end - async->buf_write_count;
 326}
 327
 328/**
 329 * comedi_buf_write_alloc() - Reserve buffer space for writing
 330 * @s: COMEDI subdevice.
 331 * @nbytes: Maximum space to reserve in bytes.
 332 *
 333 * Reserve up to @nbytes bytes of space to be written in the COMEDI acquisition
 334 * data buffer associated with the subdevice.  The amount reserved is limited
 335 * by the space available.
 336 *
 337 * Return: The amount of space reserved in bytes.
 338 */
 339unsigned int comedi_buf_write_alloc(struct comedi_subdevice *s,
 340                                    unsigned int nbytes)
 341{
 342        struct comedi_async *async = s->async;
 343        unsigned int unalloc = comedi_buf_write_n_unalloc(s);
 344
 345        if (nbytes > unalloc)
 346                nbytes = unalloc;
 347
 348        async->buf_write_alloc_count += nbytes;
 349
 350        /*
 351         * ensure the async buffer 'counts' are read and updated
 352         * before we write data to the write-alloc'ed buffer space
 353         */
 354        smp_mb();
 355
 356        return nbytes;
 357}
 358EXPORT_SYMBOL_GPL(comedi_buf_write_alloc);
 359
 360/*
 361 * munging is applied to data by core as it passes between user
 362 * and kernel space
 363 */
 364static unsigned int comedi_buf_munge(struct comedi_subdevice *s,
 365                                     unsigned int num_bytes)
 366{
 367        struct comedi_async *async = s->async;
 368        unsigned int count = 0;
 369        const unsigned int num_sample_bytes = comedi_bytes_per_sample(s);
 370
 371        if (!s->munge || (async->cmd.flags & CMDF_RAWDATA)) {
 372                async->munge_count += num_bytes;
 373                return num_bytes;
 374        }
 375
 376        /* don't munge partial samples */
 377        num_bytes -= num_bytes % num_sample_bytes;
 378        while (count < num_bytes) {
 379                int block_size = num_bytes - count;
 380                unsigned int buf_end;
 381
 382                buf_end = async->prealloc_bufsz - async->munge_ptr;
 383                if (block_size > buf_end)
 384                        block_size = buf_end;
 385
 386                s->munge(s->device, s,
 387                         async->prealloc_buf + async->munge_ptr,
 388                         block_size, async->munge_chan);
 389
 390                /*
 391                 * ensure data is munged in buffer before the
 392                 * async buffer munge_count is incremented
 393                 */
 394                smp_wmb();
 395
 396                async->munge_chan += block_size / num_sample_bytes;
 397                async->munge_chan %= async->cmd.chanlist_len;
 398                async->munge_count += block_size;
 399                async->munge_ptr += block_size;
 400                async->munge_ptr %= async->prealloc_bufsz;
 401                count += block_size;
 402        }
 403
 404        return count;
 405}
 406
 407unsigned int comedi_buf_write_n_allocated(struct comedi_subdevice *s)
 408{
 409        struct comedi_async *async = s->async;
 410
 411        return async->buf_write_alloc_count - async->buf_write_count;
 412}
 413
 414/**
 415 * comedi_buf_write_free() - Free buffer space after it is written
 416 * @s: COMEDI subdevice.
 417 * @nbytes: Maximum space to free in bytes.
 418 *
 419 * Free up to @nbytes bytes of space previously reserved for writing in the
 420 * COMEDI acquisition data buffer associated with the subdevice.  The amount of
 421 * space freed is limited to the amount that was reserved.  The freed space is
 422 * assumed to have been filled with sample data by the writer.
 423 *
 424 * If the samples in the freed space need to be "munged", do so here.  The
 425 * freed space becomes available for allocation by the reader.
 426 *
 427 * Return: The amount of space freed in bytes.
 428 */
 429unsigned int comedi_buf_write_free(struct comedi_subdevice *s,
 430                                   unsigned int nbytes)
 431{
 432        struct comedi_async *async = s->async;
 433        unsigned int allocated = comedi_buf_write_n_allocated(s);
 434
 435        if (nbytes > allocated)
 436                nbytes = allocated;
 437
 438        async->buf_write_count += nbytes;
 439        async->buf_write_ptr += nbytes;
 440        comedi_buf_munge(s, async->buf_write_count - async->munge_count);
 441        if (async->buf_write_ptr >= async->prealloc_bufsz)
 442                async->buf_write_ptr %= async->prealloc_bufsz;
 443
 444        return nbytes;
 445}
 446EXPORT_SYMBOL_GPL(comedi_buf_write_free);
 447
 448/**
 449 * comedi_buf_read_n_available() - Determine amount of readable buffer space
 450 * @s: COMEDI subdevice.
 451 *
 452 * Determine the amount of readable buffer space in the COMEDI acquisition data
 453 * buffer associated with the subdevice.  The readable buffer space is that
 454 * which has been freed by the writer and "munged" to the sample data format
 455 * expected by COMEDI if necessary.
 456 *
 457 * Return: The amount of readable buffer space.
 458 */
 459unsigned int comedi_buf_read_n_available(struct comedi_subdevice *s)
 460{
 461        struct comedi_async *async = s->async;
 462        unsigned int num_bytes;
 463
 464        if (!async)
 465                return 0;
 466
 467        num_bytes = async->munge_count - async->buf_read_count;
 468
 469        /*
 470         * ensure the async buffer 'counts' are read before we
 471         * attempt to read data from the buffer
 472         */
 473        smp_rmb();
 474
 475        return num_bytes;
 476}
 477EXPORT_SYMBOL_GPL(comedi_buf_read_n_available);
 478
 479/**
 480 * comedi_buf_read_alloc() - Reserve buffer space for reading
 481 * @s: COMEDI subdevice.
 482 * @nbytes: Maximum space to reserve in bytes.
 483 *
 484 * Reserve up to @nbytes bytes of previously written and "munged" buffer space
 485 * for reading in the COMEDI acquisition data buffer associated with the
 486 * subdevice.  The amount reserved is limited to the space available.  The
 487 * reader can read from the reserved space and then free it.  A reader is also
 488 * allowed to read from the space before reserving it as long as it determines
 489 * the amount of readable data available, but the space needs to be marked as
 490 * reserved before it can be freed.
 491 *
 492 * Return: The amount of space reserved in bytes.
 493 */
 494unsigned int comedi_buf_read_alloc(struct comedi_subdevice *s,
 495                                   unsigned int nbytes)
 496{
 497        struct comedi_async *async = s->async;
 498        unsigned int available;
 499
 500        available = async->munge_count - async->buf_read_alloc_count;
 501        if (nbytes > available)
 502                nbytes = available;
 503
 504        async->buf_read_alloc_count += nbytes;
 505
 506        /*
 507         * ensure the async buffer 'counts' are read before we
 508         * attempt to read data from the read-alloc'ed buffer space
 509         */
 510        smp_rmb();
 511
 512        return nbytes;
 513}
 514EXPORT_SYMBOL_GPL(comedi_buf_read_alloc);
 515
 516static unsigned int comedi_buf_read_n_allocated(struct comedi_async *async)
 517{
 518        return async->buf_read_alloc_count - async->buf_read_count;
 519}
 520
 521/**
 522 * comedi_buf_read_free() - Free buffer space after it has been read
 523 * @s: COMEDI subdevice.
 524 * @nbytes: Maximum space to free in bytes.
 525 *
 526 * Free up to @nbytes bytes of buffer space previously reserved for reading in
 527 * the COMEDI acquisition data buffer associated with the subdevice.  The
 528 * amount of space freed is limited to the amount that was reserved.
 529 *
 530 * The freed space becomes available for allocation by the writer.
 531 *
 532 * Return: The amount of space freed in bytes.
 533 */
 534unsigned int comedi_buf_read_free(struct comedi_subdevice *s,
 535                                  unsigned int nbytes)
 536{
 537        struct comedi_async *async = s->async;
 538        unsigned int allocated;
 539
 540        /*
 541         * ensure data has been read out of buffer before
 542         * the async read count is incremented
 543         */
 544        smp_mb();
 545
 546        allocated = comedi_buf_read_n_allocated(async);
 547        if (nbytes > allocated)
 548                nbytes = allocated;
 549
 550        async->buf_read_count += nbytes;
 551        async->buf_read_ptr += nbytes;
 552        async->buf_read_ptr %= async->prealloc_bufsz;
 553        return nbytes;
 554}
 555EXPORT_SYMBOL_GPL(comedi_buf_read_free);
 556
 557static void comedi_buf_memcpy_to(struct comedi_subdevice *s,
 558                                 const void *data, unsigned int num_bytes)
 559{
 560        struct comedi_async *async = s->async;
 561        unsigned int write_ptr = async->buf_write_ptr;
 562
 563        while (num_bytes) {
 564                unsigned int block_size;
 565
 566                if (write_ptr + num_bytes > async->prealloc_bufsz)
 567                        block_size = async->prealloc_bufsz - write_ptr;
 568                else
 569                        block_size = num_bytes;
 570
 571                memcpy(async->prealloc_buf + write_ptr, data, block_size);
 572
 573                data += block_size;
 574                num_bytes -= block_size;
 575
 576                write_ptr = 0;
 577        }
 578}
 579
 580static void comedi_buf_memcpy_from(struct comedi_subdevice *s,
 581                                   void *dest, unsigned int nbytes)
 582{
 583        void *src;
 584        struct comedi_async *async = s->async;
 585        unsigned int read_ptr = async->buf_read_ptr;
 586
 587        while (nbytes) {
 588                unsigned int block_size;
 589
 590                src = async->prealloc_buf + read_ptr;
 591
 592                if (nbytes >= async->prealloc_bufsz - read_ptr)
 593                        block_size = async->prealloc_bufsz - read_ptr;
 594                else
 595                        block_size = nbytes;
 596
 597                memcpy(dest, src, block_size);
 598                nbytes -= block_size;
 599                dest += block_size;
 600                read_ptr = 0;
 601        }
 602}
 603
 604/**
 605 * comedi_buf_write_samples() - Write sample data to COMEDI buffer
 606 * @s: COMEDI subdevice.
 607 * @data: Pointer to source samples.
 608 * @nsamples: Number of samples to write.
 609 *
 610 * Write up to @nsamples samples to the COMEDI acquisition data buffer
 611 * associated with the subdevice, mark it as written and update the
 612 * acquisition scan progress.  If there is not enough room for the specified
 613 * number of samples, the number of samples written is limited to the number
 614 * that will fit and the %COMEDI_CB_OVERFLOW event flag is set to cause the
 615 * acquisition to terminate with an overrun error.  Set the %COMEDI_CB_BLOCK
 616 * event flag if any samples are written to cause waiting tasks to be woken
 617 * when the event flags are processed.
 618 *
 619 * Return: The amount of data written in bytes.
 620 */
 621unsigned int comedi_buf_write_samples(struct comedi_subdevice *s,
 622                                      const void *data, unsigned int nsamples)
 623{
 624        unsigned int max_samples;
 625        unsigned int nbytes;
 626
 627        /*
 628         * Make sure there is enough room in the buffer for all the samples.
 629         * If not, clamp the nsamples to the number that will fit, flag the
 630         * buffer overrun and add the samples that fit.
 631         */
 632        max_samples = comedi_bytes_to_samples(s, comedi_buf_write_n_unalloc(s));
 633        if (nsamples > max_samples) {
 634                dev_warn(s->device->class_dev, "buffer overrun\n");
 635                s->async->events |= COMEDI_CB_OVERFLOW;
 636                nsamples = max_samples;
 637        }
 638
 639        if (nsamples == 0)
 640                return 0;
 641
 642        nbytes = comedi_buf_write_alloc(s,
 643                                        comedi_samples_to_bytes(s, nsamples));
 644        comedi_buf_memcpy_to(s, data, nbytes);
 645        comedi_buf_write_free(s, nbytes);
 646        comedi_inc_scan_progress(s, nbytes);
 647        s->async->events |= COMEDI_CB_BLOCK;
 648
 649        return nbytes;
 650}
 651EXPORT_SYMBOL_GPL(comedi_buf_write_samples);
 652
 653/**
 654 * comedi_buf_read_samples() - Read sample data from COMEDI buffer
 655 * @s: COMEDI subdevice.
 656 * @data: Pointer to destination.
 657 * @nsamples: Maximum number of samples to read.
 658 *
 659 * Read up to @nsamples samples from the COMEDI acquisition data buffer
 660 * associated with the subdevice, mark it as read and update the acquisition
 661 * scan progress.  Limit the number of samples read to the number available.
 662 * Set the %COMEDI_CB_BLOCK event flag if any samples are read to cause waiting
 663 * tasks to be woken when the event flags are processed.
 664 *
 665 * Return: The amount of data read in bytes.
 666 */
 667unsigned int comedi_buf_read_samples(struct comedi_subdevice *s,
 668                                     void *data, unsigned int nsamples)
 669{
 670        unsigned int max_samples;
 671        unsigned int nbytes;
 672
 673        /* clamp nsamples to the number of full samples available */
 674        max_samples = comedi_bytes_to_samples(s,
 675                                              comedi_buf_read_n_available(s));
 676        if (nsamples > max_samples)
 677                nsamples = max_samples;
 678
 679        if (nsamples == 0)
 680                return 0;
 681
 682        nbytes = comedi_buf_read_alloc(s,
 683                                       comedi_samples_to_bytes(s, nsamples));
 684        comedi_buf_memcpy_from(s, data, nbytes);
 685        comedi_buf_read_free(s, nbytes);
 686        comedi_inc_scan_progress(s, nbytes);
 687        s->async->events |= COMEDI_CB_BLOCK;
 688
 689        return nbytes;
 690}
 691EXPORT_SYMBOL_GPL(comedi_buf_read_samples);
 692