162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * comedi_buf.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * COMEDI - Linux Control and Measurement Device Interface 662306a36Sopenharmony_ci * Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org> 762306a36Sopenharmony_ci * Copyright (C) 2002 Frank Mori Hess <fmhess@users.sourceforge.net> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/vmalloc.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/comedi/comedidev.h> 1362306a36Sopenharmony_ci#include "comedi_internal.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#ifdef PAGE_KERNEL_NOCACHE 1662306a36Sopenharmony_ci#define COMEDI_PAGE_PROTECTION PAGE_KERNEL_NOCACHE 1762306a36Sopenharmony_ci#else 1862306a36Sopenharmony_ci#define COMEDI_PAGE_PROTECTION PAGE_KERNEL 1962306a36Sopenharmony_ci#endif 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic void comedi_buf_map_kref_release(struct kref *kref) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci struct comedi_buf_map *bm = 2462306a36Sopenharmony_ci container_of(kref, struct comedi_buf_map, refcount); 2562306a36Sopenharmony_ci struct comedi_buf_page *buf; 2662306a36Sopenharmony_ci unsigned int i; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci if (bm->page_list) { 2962306a36Sopenharmony_ci if (bm->dma_dir != DMA_NONE) { 3062306a36Sopenharmony_ci /* 3162306a36Sopenharmony_ci * DMA buffer was allocated as a single block. 3262306a36Sopenharmony_ci * Address is in page_list[0]. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci buf = &bm->page_list[0]; 3562306a36Sopenharmony_ci dma_free_coherent(bm->dma_hw_dev, 3662306a36Sopenharmony_ci PAGE_SIZE * bm->n_pages, 3762306a36Sopenharmony_ci buf->virt_addr, buf->dma_addr); 3862306a36Sopenharmony_ci } else { 3962306a36Sopenharmony_ci for (i = 0; i < bm->n_pages; i++) { 4062306a36Sopenharmony_ci buf = &bm->page_list[i]; 4162306a36Sopenharmony_ci ClearPageReserved(virt_to_page(buf->virt_addr)); 4262306a36Sopenharmony_ci free_page((unsigned long)buf->virt_addr); 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci vfree(bm->page_list); 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci if (bm->dma_dir != DMA_NONE) 4862306a36Sopenharmony_ci put_device(bm->dma_hw_dev); 4962306a36Sopenharmony_ci kfree(bm); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic void __comedi_buf_free(struct comedi_device *dev, 5362306a36Sopenharmony_ci struct comedi_subdevice *s) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct comedi_async *async = s->async; 5662306a36Sopenharmony_ci struct comedi_buf_map *bm; 5762306a36Sopenharmony_ci unsigned long flags; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (async->prealloc_buf) { 6062306a36Sopenharmony_ci if (s->async_dma_dir == DMA_NONE) 6162306a36Sopenharmony_ci vunmap(async->prealloc_buf); 6262306a36Sopenharmony_ci async->prealloc_buf = NULL; 6362306a36Sopenharmony_ci async->prealloc_bufsz = 0; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci spin_lock_irqsave(&s->spin_lock, flags); 6762306a36Sopenharmony_ci bm = async->buf_map; 6862306a36Sopenharmony_ci async->buf_map = NULL; 6962306a36Sopenharmony_ci spin_unlock_irqrestore(&s->spin_lock, flags); 7062306a36Sopenharmony_ci comedi_buf_map_put(bm); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic struct comedi_buf_map * 7462306a36Sopenharmony_cicomedi_buf_map_alloc(struct comedi_device *dev, enum dma_data_direction dma_dir, 7562306a36Sopenharmony_ci unsigned int n_pages) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct comedi_buf_map *bm; 7862306a36Sopenharmony_ci struct comedi_buf_page *buf; 7962306a36Sopenharmony_ci unsigned int i; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci bm = kzalloc(sizeof(*bm), GFP_KERNEL); 8262306a36Sopenharmony_ci if (!bm) 8362306a36Sopenharmony_ci return NULL; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci kref_init(&bm->refcount); 8662306a36Sopenharmony_ci bm->dma_dir = dma_dir; 8762306a36Sopenharmony_ci if (bm->dma_dir != DMA_NONE) { 8862306a36Sopenharmony_ci /* Need ref to hardware device to free buffer later. */ 8962306a36Sopenharmony_ci bm->dma_hw_dev = get_device(dev->hw_dev); 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci bm->page_list = vzalloc(sizeof(*buf) * n_pages); 9362306a36Sopenharmony_ci if (!bm->page_list) 9462306a36Sopenharmony_ci goto err; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (bm->dma_dir != DMA_NONE) { 9762306a36Sopenharmony_ci void *virt_addr; 9862306a36Sopenharmony_ci dma_addr_t dma_addr; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* 10162306a36Sopenharmony_ci * Currently, the DMA buffer needs to be allocated as a 10262306a36Sopenharmony_ci * single block so that it can be mmap()'ed. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ci virt_addr = dma_alloc_coherent(bm->dma_hw_dev, 10562306a36Sopenharmony_ci PAGE_SIZE * n_pages, &dma_addr, 10662306a36Sopenharmony_ci GFP_KERNEL); 10762306a36Sopenharmony_ci if (!virt_addr) 10862306a36Sopenharmony_ci goto err; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci for (i = 0; i < n_pages; i++) { 11162306a36Sopenharmony_ci buf = &bm->page_list[i]; 11262306a36Sopenharmony_ci buf->virt_addr = virt_addr + (i << PAGE_SHIFT); 11362306a36Sopenharmony_ci buf->dma_addr = dma_addr + (i << PAGE_SHIFT); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci bm->n_pages = i; 11762306a36Sopenharmony_ci } else { 11862306a36Sopenharmony_ci for (i = 0; i < n_pages; i++) { 11962306a36Sopenharmony_ci buf = &bm->page_list[i]; 12062306a36Sopenharmony_ci buf->virt_addr = (void *)get_zeroed_page(GFP_KERNEL); 12162306a36Sopenharmony_ci if (!buf->virt_addr) 12262306a36Sopenharmony_ci break; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci SetPageReserved(virt_to_page(buf->virt_addr)); 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci bm->n_pages = i; 12862306a36Sopenharmony_ci if (i < n_pages) 12962306a36Sopenharmony_ci goto err; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return bm; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cierr: 13562306a36Sopenharmony_ci comedi_buf_map_put(bm); 13662306a36Sopenharmony_ci return NULL; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic void __comedi_buf_alloc(struct comedi_device *dev, 14062306a36Sopenharmony_ci struct comedi_subdevice *s, 14162306a36Sopenharmony_ci unsigned int n_pages) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct comedi_async *async = s->async; 14462306a36Sopenharmony_ci struct page **pages = NULL; 14562306a36Sopenharmony_ci struct comedi_buf_map *bm; 14662306a36Sopenharmony_ci struct comedi_buf_page *buf; 14762306a36Sopenharmony_ci unsigned long flags; 14862306a36Sopenharmony_ci unsigned int i; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_HAS_DMA) && s->async_dma_dir != DMA_NONE) { 15162306a36Sopenharmony_ci dev_err(dev->class_dev, 15262306a36Sopenharmony_ci "dma buffer allocation not supported\n"); 15362306a36Sopenharmony_ci return; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci bm = comedi_buf_map_alloc(dev, s->async_dma_dir, n_pages); 15762306a36Sopenharmony_ci if (!bm) 15862306a36Sopenharmony_ci return; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci spin_lock_irqsave(&s->spin_lock, flags); 16162306a36Sopenharmony_ci async->buf_map = bm; 16262306a36Sopenharmony_ci spin_unlock_irqrestore(&s->spin_lock, flags); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (bm->dma_dir != DMA_NONE) { 16562306a36Sopenharmony_ci /* 16662306a36Sopenharmony_ci * DMA buffer was allocated as a single block. 16762306a36Sopenharmony_ci * Address is in page_list[0]. 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ci buf = &bm->page_list[0]; 17062306a36Sopenharmony_ci async->prealloc_buf = buf->virt_addr; 17162306a36Sopenharmony_ci } else { 17262306a36Sopenharmony_ci pages = vmalloc(sizeof(struct page *) * n_pages); 17362306a36Sopenharmony_ci if (!pages) 17462306a36Sopenharmony_ci return; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci for (i = 0; i < n_pages; i++) { 17762306a36Sopenharmony_ci buf = &bm->page_list[i]; 17862306a36Sopenharmony_ci pages[i] = virt_to_page(buf->virt_addr); 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* vmap the pages to prealloc_buf */ 18262306a36Sopenharmony_ci async->prealloc_buf = vmap(pages, n_pages, VM_MAP, 18362306a36Sopenharmony_ci COMEDI_PAGE_PROTECTION); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci vfree(pages); 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_civoid comedi_buf_map_get(struct comedi_buf_map *bm) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci if (bm) 19262306a36Sopenharmony_ci kref_get(&bm->refcount); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ciint comedi_buf_map_put(struct comedi_buf_map *bm) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci if (bm) 19862306a36Sopenharmony_ci return kref_put(&bm->refcount, comedi_buf_map_kref_release); 19962306a36Sopenharmony_ci return 1; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/* helper for "access" vm operation */ 20362306a36Sopenharmony_ciint comedi_buf_map_access(struct comedi_buf_map *bm, unsigned long offset, 20462306a36Sopenharmony_ci void *buf, int len, int write) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci unsigned int pgoff = offset_in_page(offset); 20762306a36Sopenharmony_ci unsigned long pg = offset >> PAGE_SHIFT; 20862306a36Sopenharmony_ci int done = 0; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci while (done < len && pg < bm->n_pages) { 21162306a36Sopenharmony_ci int l = min_t(int, len - done, PAGE_SIZE - pgoff); 21262306a36Sopenharmony_ci void *b = bm->page_list[pg].virt_addr + pgoff; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (write) 21562306a36Sopenharmony_ci memcpy(b, buf, l); 21662306a36Sopenharmony_ci else 21762306a36Sopenharmony_ci memcpy(buf, b, l); 21862306a36Sopenharmony_ci buf += l; 21962306a36Sopenharmony_ci done += l; 22062306a36Sopenharmony_ci pg++; 22162306a36Sopenharmony_ci pgoff = 0; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci return done; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/* returns s->async->buf_map and increments its kref refcount */ 22762306a36Sopenharmony_cistruct comedi_buf_map * 22862306a36Sopenharmony_cicomedi_buf_map_from_subdev_get(struct comedi_subdevice *s) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct comedi_async *async = s->async; 23162306a36Sopenharmony_ci struct comedi_buf_map *bm = NULL; 23262306a36Sopenharmony_ci unsigned long flags; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (!async) 23562306a36Sopenharmony_ci return NULL; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci spin_lock_irqsave(&s->spin_lock, flags); 23862306a36Sopenharmony_ci bm = async->buf_map; 23962306a36Sopenharmony_ci /* only want it if buffer pages allocated */ 24062306a36Sopenharmony_ci if (bm && bm->n_pages) 24162306a36Sopenharmony_ci comedi_buf_map_get(bm); 24262306a36Sopenharmony_ci else 24362306a36Sopenharmony_ci bm = NULL; 24462306a36Sopenharmony_ci spin_unlock_irqrestore(&s->spin_lock, flags); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return bm; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cibool comedi_buf_is_mmapped(struct comedi_subdevice *s) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct comedi_buf_map *bm = s->async->buf_map; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return bm && (kref_read(&bm->refcount) > 1); 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ciint comedi_buf_alloc(struct comedi_device *dev, struct comedi_subdevice *s, 25762306a36Sopenharmony_ci unsigned long new_size) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct comedi_async *async = s->async; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* Round up new_size to multiple of PAGE_SIZE */ 26462306a36Sopenharmony_ci new_size = (new_size + PAGE_SIZE - 1) & PAGE_MASK; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* if no change is required, do nothing */ 26762306a36Sopenharmony_ci if (async->prealloc_buf && async->prealloc_bufsz == new_size) 26862306a36Sopenharmony_ci return 0; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* deallocate old buffer */ 27162306a36Sopenharmony_ci __comedi_buf_free(dev, s); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* allocate new buffer */ 27462306a36Sopenharmony_ci if (new_size) { 27562306a36Sopenharmony_ci unsigned int n_pages = new_size >> PAGE_SHIFT; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci __comedi_buf_alloc(dev, s, n_pages); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (!async->prealloc_buf) { 28062306a36Sopenharmony_ci /* allocation failed */ 28162306a36Sopenharmony_ci __comedi_buf_free(dev, s); 28262306a36Sopenharmony_ci return -ENOMEM; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci async->prealloc_bufsz = new_size; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci return 0; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_civoid comedi_buf_reset(struct comedi_subdevice *s) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci struct comedi_async *async = s->async; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci async->buf_write_alloc_count = 0; 29562306a36Sopenharmony_ci async->buf_write_count = 0; 29662306a36Sopenharmony_ci async->buf_read_alloc_count = 0; 29762306a36Sopenharmony_ci async->buf_read_count = 0; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci async->buf_write_ptr = 0; 30062306a36Sopenharmony_ci async->buf_read_ptr = 0; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci async->cur_chan = 0; 30362306a36Sopenharmony_ci async->scans_done = 0; 30462306a36Sopenharmony_ci async->scan_progress = 0; 30562306a36Sopenharmony_ci async->munge_chan = 0; 30662306a36Sopenharmony_ci async->munge_count = 0; 30762306a36Sopenharmony_ci async->munge_ptr = 0; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci async->events = 0; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic unsigned int comedi_buf_write_n_unalloc(struct comedi_subdevice *s) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct comedi_async *async = s->async; 31562306a36Sopenharmony_ci unsigned int free_end = async->buf_read_count + async->prealloc_bufsz; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return free_end - async->buf_write_alloc_count; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ciunsigned int comedi_buf_write_n_available(struct comedi_subdevice *s) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct comedi_async *async = s->async; 32362306a36Sopenharmony_ci unsigned int free_end = async->buf_read_count + async->prealloc_bufsz; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci return free_end - async->buf_write_count; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci/** 32962306a36Sopenharmony_ci * comedi_buf_write_alloc() - Reserve buffer space for writing 33062306a36Sopenharmony_ci * @s: COMEDI subdevice. 33162306a36Sopenharmony_ci * @nbytes: Maximum space to reserve in bytes. 33262306a36Sopenharmony_ci * 33362306a36Sopenharmony_ci * Reserve up to @nbytes bytes of space to be written in the COMEDI acquisition 33462306a36Sopenharmony_ci * data buffer associated with the subdevice. The amount reserved is limited 33562306a36Sopenharmony_ci * by the space available. 33662306a36Sopenharmony_ci * 33762306a36Sopenharmony_ci * Return: The amount of space reserved in bytes. 33862306a36Sopenharmony_ci */ 33962306a36Sopenharmony_ciunsigned int comedi_buf_write_alloc(struct comedi_subdevice *s, 34062306a36Sopenharmony_ci unsigned int nbytes) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct comedi_async *async = s->async; 34362306a36Sopenharmony_ci unsigned int unalloc = comedi_buf_write_n_unalloc(s); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (nbytes > unalloc) 34662306a36Sopenharmony_ci nbytes = unalloc; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci async->buf_write_alloc_count += nbytes; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* 35162306a36Sopenharmony_ci * ensure the async buffer 'counts' are read and updated 35262306a36Sopenharmony_ci * before we write data to the write-alloc'ed buffer space 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_ci smp_mb(); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci return nbytes; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(comedi_buf_write_alloc); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci/* 36162306a36Sopenharmony_ci * munging is applied to data by core as it passes between user 36262306a36Sopenharmony_ci * and kernel space 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_cistatic unsigned int comedi_buf_munge(struct comedi_subdevice *s, 36562306a36Sopenharmony_ci unsigned int num_bytes) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci struct comedi_async *async = s->async; 36862306a36Sopenharmony_ci unsigned int count = 0; 36962306a36Sopenharmony_ci const unsigned int num_sample_bytes = comedi_bytes_per_sample(s); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (!s->munge || (async->cmd.flags & CMDF_RAWDATA)) { 37262306a36Sopenharmony_ci async->munge_count += num_bytes; 37362306a36Sopenharmony_ci return num_bytes; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* don't munge partial samples */ 37762306a36Sopenharmony_ci num_bytes -= num_bytes % num_sample_bytes; 37862306a36Sopenharmony_ci while (count < num_bytes) { 37962306a36Sopenharmony_ci int block_size = num_bytes - count; 38062306a36Sopenharmony_ci unsigned int buf_end; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci buf_end = async->prealloc_bufsz - async->munge_ptr; 38362306a36Sopenharmony_ci if (block_size > buf_end) 38462306a36Sopenharmony_ci block_size = buf_end; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci s->munge(s->device, s, 38762306a36Sopenharmony_ci async->prealloc_buf + async->munge_ptr, 38862306a36Sopenharmony_ci block_size, async->munge_chan); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* 39162306a36Sopenharmony_ci * ensure data is munged in buffer before the 39262306a36Sopenharmony_ci * async buffer munge_count is incremented 39362306a36Sopenharmony_ci */ 39462306a36Sopenharmony_ci smp_wmb(); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci async->munge_chan += block_size / num_sample_bytes; 39762306a36Sopenharmony_ci async->munge_chan %= async->cmd.chanlist_len; 39862306a36Sopenharmony_ci async->munge_count += block_size; 39962306a36Sopenharmony_ci async->munge_ptr += block_size; 40062306a36Sopenharmony_ci async->munge_ptr %= async->prealloc_bufsz; 40162306a36Sopenharmony_ci count += block_size; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci return count; 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ciunsigned int comedi_buf_write_n_allocated(struct comedi_subdevice *s) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct comedi_async *async = s->async; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci return async->buf_write_alloc_count - async->buf_write_count; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci/** 41562306a36Sopenharmony_ci * comedi_buf_write_free() - Free buffer space after it is written 41662306a36Sopenharmony_ci * @s: COMEDI subdevice. 41762306a36Sopenharmony_ci * @nbytes: Maximum space to free in bytes. 41862306a36Sopenharmony_ci * 41962306a36Sopenharmony_ci * Free up to @nbytes bytes of space previously reserved for writing in the 42062306a36Sopenharmony_ci * COMEDI acquisition data buffer associated with the subdevice. The amount of 42162306a36Sopenharmony_ci * space freed is limited to the amount that was reserved. The freed space is 42262306a36Sopenharmony_ci * assumed to have been filled with sample data by the writer. 42362306a36Sopenharmony_ci * 42462306a36Sopenharmony_ci * If the samples in the freed space need to be "munged", do so here. The 42562306a36Sopenharmony_ci * freed space becomes available for allocation by the reader. 42662306a36Sopenharmony_ci * 42762306a36Sopenharmony_ci * Return: The amount of space freed in bytes. 42862306a36Sopenharmony_ci */ 42962306a36Sopenharmony_ciunsigned int comedi_buf_write_free(struct comedi_subdevice *s, 43062306a36Sopenharmony_ci unsigned int nbytes) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci struct comedi_async *async = s->async; 43362306a36Sopenharmony_ci unsigned int allocated = comedi_buf_write_n_allocated(s); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (nbytes > allocated) 43662306a36Sopenharmony_ci nbytes = allocated; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci async->buf_write_count += nbytes; 43962306a36Sopenharmony_ci async->buf_write_ptr += nbytes; 44062306a36Sopenharmony_ci comedi_buf_munge(s, async->buf_write_count - async->munge_count); 44162306a36Sopenharmony_ci if (async->buf_write_ptr >= async->prealloc_bufsz) 44262306a36Sopenharmony_ci async->buf_write_ptr %= async->prealloc_bufsz; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci return nbytes; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(comedi_buf_write_free); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci/** 44962306a36Sopenharmony_ci * comedi_buf_read_n_available() - Determine amount of readable buffer space 45062306a36Sopenharmony_ci * @s: COMEDI subdevice. 45162306a36Sopenharmony_ci * 45262306a36Sopenharmony_ci * Determine the amount of readable buffer space in the COMEDI acquisition data 45362306a36Sopenharmony_ci * buffer associated with the subdevice. The readable buffer space is that 45462306a36Sopenharmony_ci * which has been freed by the writer and "munged" to the sample data format 45562306a36Sopenharmony_ci * expected by COMEDI if necessary. 45662306a36Sopenharmony_ci * 45762306a36Sopenharmony_ci * Return: The amount of readable buffer space. 45862306a36Sopenharmony_ci */ 45962306a36Sopenharmony_ciunsigned int comedi_buf_read_n_available(struct comedi_subdevice *s) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci struct comedi_async *async = s->async; 46262306a36Sopenharmony_ci unsigned int num_bytes; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (!async) 46562306a36Sopenharmony_ci return 0; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci num_bytes = async->munge_count - async->buf_read_count; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* 47062306a36Sopenharmony_ci * ensure the async buffer 'counts' are read before we 47162306a36Sopenharmony_ci * attempt to read data from the buffer 47262306a36Sopenharmony_ci */ 47362306a36Sopenharmony_ci smp_rmb(); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return num_bytes; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(comedi_buf_read_n_available); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci/** 48062306a36Sopenharmony_ci * comedi_buf_read_alloc() - Reserve buffer space for reading 48162306a36Sopenharmony_ci * @s: COMEDI subdevice. 48262306a36Sopenharmony_ci * @nbytes: Maximum space to reserve in bytes. 48362306a36Sopenharmony_ci * 48462306a36Sopenharmony_ci * Reserve up to @nbytes bytes of previously written and "munged" buffer space 48562306a36Sopenharmony_ci * for reading in the COMEDI acquisition data buffer associated with the 48662306a36Sopenharmony_ci * subdevice. The amount reserved is limited to the space available. The 48762306a36Sopenharmony_ci * reader can read from the reserved space and then free it. A reader is also 48862306a36Sopenharmony_ci * allowed to read from the space before reserving it as long as it determines 48962306a36Sopenharmony_ci * the amount of readable data available, but the space needs to be marked as 49062306a36Sopenharmony_ci * reserved before it can be freed. 49162306a36Sopenharmony_ci * 49262306a36Sopenharmony_ci * Return: The amount of space reserved in bytes. 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_ciunsigned int comedi_buf_read_alloc(struct comedi_subdevice *s, 49562306a36Sopenharmony_ci unsigned int nbytes) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci struct comedi_async *async = s->async; 49862306a36Sopenharmony_ci unsigned int available; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci available = async->munge_count - async->buf_read_alloc_count; 50162306a36Sopenharmony_ci if (nbytes > available) 50262306a36Sopenharmony_ci nbytes = available; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci async->buf_read_alloc_count += nbytes; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci /* 50762306a36Sopenharmony_ci * ensure the async buffer 'counts' are read before we 50862306a36Sopenharmony_ci * attempt to read data from the read-alloc'ed buffer space 50962306a36Sopenharmony_ci */ 51062306a36Sopenharmony_ci smp_rmb(); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci return nbytes; 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(comedi_buf_read_alloc); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic unsigned int comedi_buf_read_n_allocated(struct comedi_async *async) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci return async->buf_read_alloc_count - async->buf_read_count; 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci/** 52262306a36Sopenharmony_ci * comedi_buf_read_free() - Free buffer space after it has been read 52362306a36Sopenharmony_ci * @s: COMEDI subdevice. 52462306a36Sopenharmony_ci * @nbytes: Maximum space to free in bytes. 52562306a36Sopenharmony_ci * 52662306a36Sopenharmony_ci * Free up to @nbytes bytes of buffer space previously reserved for reading in 52762306a36Sopenharmony_ci * the COMEDI acquisition data buffer associated with the subdevice. The 52862306a36Sopenharmony_ci * amount of space freed is limited to the amount that was reserved. 52962306a36Sopenharmony_ci * 53062306a36Sopenharmony_ci * The freed space becomes available for allocation by the writer. 53162306a36Sopenharmony_ci * 53262306a36Sopenharmony_ci * Return: The amount of space freed in bytes. 53362306a36Sopenharmony_ci */ 53462306a36Sopenharmony_ciunsigned int comedi_buf_read_free(struct comedi_subdevice *s, 53562306a36Sopenharmony_ci unsigned int nbytes) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci struct comedi_async *async = s->async; 53862306a36Sopenharmony_ci unsigned int allocated; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* 54162306a36Sopenharmony_ci * ensure data has been read out of buffer before 54262306a36Sopenharmony_ci * the async read count is incremented 54362306a36Sopenharmony_ci */ 54462306a36Sopenharmony_ci smp_mb(); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci allocated = comedi_buf_read_n_allocated(async); 54762306a36Sopenharmony_ci if (nbytes > allocated) 54862306a36Sopenharmony_ci nbytes = allocated; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci async->buf_read_count += nbytes; 55162306a36Sopenharmony_ci async->buf_read_ptr += nbytes; 55262306a36Sopenharmony_ci async->buf_read_ptr %= async->prealloc_bufsz; 55362306a36Sopenharmony_ci return nbytes; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(comedi_buf_read_free); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic void comedi_buf_memcpy_to(struct comedi_subdevice *s, 55862306a36Sopenharmony_ci const void *data, unsigned int num_bytes) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci struct comedi_async *async = s->async; 56162306a36Sopenharmony_ci unsigned int write_ptr = async->buf_write_ptr; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci while (num_bytes) { 56462306a36Sopenharmony_ci unsigned int block_size; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (write_ptr + num_bytes > async->prealloc_bufsz) 56762306a36Sopenharmony_ci block_size = async->prealloc_bufsz - write_ptr; 56862306a36Sopenharmony_ci else 56962306a36Sopenharmony_ci block_size = num_bytes; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci memcpy(async->prealloc_buf + write_ptr, data, block_size); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci data += block_size; 57462306a36Sopenharmony_ci num_bytes -= block_size; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci write_ptr = 0; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic void comedi_buf_memcpy_from(struct comedi_subdevice *s, 58162306a36Sopenharmony_ci void *dest, unsigned int nbytes) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci void *src; 58462306a36Sopenharmony_ci struct comedi_async *async = s->async; 58562306a36Sopenharmony_ci unsigned int read_ptr = async->buf_read_ptr; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci while (nbytes) { 58862306a36Sopenharmony_ci unsigned int block_size; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci src = async->prealloc_buf + read_ptr; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (nbytes >= async->prealloc_bufsz - read_ptr) 59362306a36Sopenharmony_ci block_size = async->prealloc_bufsz - read_ptr; 59462306a36Sopenharmony_ci else 59562306a36Sopenharmony_ci block_size = nbytes; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci memcpy(dest, src, block_size); 59862306a36Sopenharmony_ci nbytes -= block_size; 59962306a36Sopenharmony_ci dest += block_size; 60062306a36Sopenharmony_ci read_ptr = 0; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci/** 60562306a36Sopenharmony_ci * comedi_buf_write_samples() - Write sample data to COMEDI buffer 60662306a36Sopenharmony_ci * @s: COMEDI subdevice. 60762306a36Sopenharmony_ci * @data: Pointer to source samples. 60862306a36Sopenharmony_ci * @nsamples: Number of samples to write. 60962306a36Sopenharmony_ci * 61062306a36Sopenharmony_ci * Write up to @nsamples samples to the COMEDI acquisition data buffer 61162306a36Sopenharmony_ci * associated with the subdevice, mark it as written and update the 61262306a36Sopenharmony_ci * acquisition scan progress. If there is not enough room for the specified 61362306a36Sopenharmony_ci * number of samples, the number of samples written is limited to the number 61462306a36Sopenharmony_ci * that will fit and the %COMEDI_CB_OVERFLOW event flag is set to cause the 61562306a36Sopenharmony_ci * acquisition to terminate with an overrun error. Set the %COMEDI_CB_BLOCK 61662306a36Sopenharmony_ci * event flag if any samples are written to cause waiting tasks to be woken 61762306a36Sopenharmony_ci * when the event flags are processed. 61862306a36Sopenharmony_ci * 61962306a36Sopenharmony_ci * Return: The amount of data written in bytes. 62062306a36Sopenharmony_ci */ 62162306a36Sopenharmony_ciunsigned int comedi_buf_write_samples(struct comedi_subdevice *s, 62262306a36Sopenharmony_ci const void *data, unsigned int nsamples) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci unsigned int max_samples; 62562306a36Sopenharmony_ci unsigned int nbytes; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci /* 62862306a36Sopenharmony_ci * Make sure there is enough room in the buffer for all the samples. 62962306a36Sopenharmony_ci * If not, clamp the nsamples to the number that will fit, flag the 63062306a36Sopenharmony_ci * buffer overrun and add the samples that fit. 63162306a36Sopenharmony_ci */ 63262306a36Sopenharmony_ci max_samples = comedi_bytes_to_samples(s, comedi_buf_write_n_unalloc(s)); 63362306a36Sopenharmony_ci if (nsamples > max_samples) { 63462306a36Sopenharmony_ci dev_warn(s->device->class_dev, "buffer overrun\n"); 63562306a36Sopenharmony_ci s->async->events |= COMEDI_CB_OVERFLOW; 63662306a36Sopenharmony_ci nsamples = max_samples; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (nsamples == 0) 64062306a36Sopenharmony_ci return 0; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci nbytes = comedi_buf_write_alloc(s, 64362306a36Sopenharmony_ci comedi_samples_to_bytes(s, nsamples)); 64462306a36Sopenharmony_ci comedi_buf_memcpy_to(s, data, nbytes); 64562306a36Sopenharmony_ci comedi_buf_write_free(s, nbytes); 64662306a36Sopenharmony_ci comedi_inc_scan_progress(s, nbytes); 64762306a36Sopenharmony_ci s->async->events |= COMEDI_CB_BLOCK; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci return nbytes; 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(comedi_buf_write_samples); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci/** 65462306a36Sopenharmony_ci * comedi_buf_read_samples() - Read sample data from COMEDI buffer 65562306a36Sopenharmony_ci * @s: COMEDI subdevice. 65662306a36Sopenharmony_ci * @data: Pointer to destination. 65762306a36Sopenharmony_ci * @nsamples: Maximum number of samples to read. 65862306a36Sopenharmony_ci * 65962306a36Sopenharmony_ci * Read up to @nsamples samples from the COMEDI acquisition data buffer 66062306a36Sopenharmony_ci * associated with the subdevice, mark it as read and update the acquisition 66162306a36Sopenharmony_ci * scan progress. Limit the number of samples read to the number available. 66262306a36Sopenharmony_ci * Set the %COMEDI_CB_BLOCK event flag if any samples are read to cause waiting 66362306a36Sopenharmony_ci * tasks to be woken when the event flags are processed. 66462306a36Sopenharmony_ci * 66562306a36Sopenharmony_ci * Return: The amount of data read in bytes. 66662306a36Sopenharmony_ci */ 66762306a36Sopenharmony_ciunsigned int comedi_buf_read_samples(struct comedi_subdevice *s, 66862306a36Sopenharmony_ci void *data, unsigned int nsamples) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci unsigned int max_samples; 67162306a36Sopenharmony_ci unsigned int nbytes; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci /* clamp nsamples to the number of full samples available */ 67462306a36Sopenharmony_ci max_samples = comedi_bytes_to_samples(s, 67562306a36Sopenharmony_ci comedi_buf_read_n_available(s)); 67662306a36Sopenharmony_ci if (nsamples > max_samples) 67762306a36Sopenharmony_ci nsamples = max_samples; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci if (nsamples == 0) 68062306a36Sopenharmony_ci return 0; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci nbytes = comedi_buf_read_alloc(s, 68362306a36Sopenharmony_ci comedi_samples_to_bytes(s, nsamples)); 68462306a36Sopenharmony_ci comedi_buf_memcpy_from(s, data, nbytes); 68562306a36Sopenharmony_ci comedi_buf_read_free(s, nbytes); 68662306a36Sopenharmony_ci comedi_inc_scan_progress(s, nbytes); 68762306a36Sopenharmony_ci s->async->events |= COMEDI_CB_BLOCK; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci return nbytes; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(comedi_buf_read_samples); 692