0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/vmalloc.h>
0011 #include <linux/slab.h>
0012 #include <linux/comedi/comedidev.h>
0013 #include "comedi_internal.h"
0014
0015 #ifdef PAGE_KERNEL_NOCACHE
0016 #define COMEDI_PAGE_PROTECTION PAGE_KERNEL_NOCACHE
0017 #else
0018 #define COMEDI_PAGE_PROTECTION PAGE_KERNEL
0019 #endif
0020
0021 static void comedi_buf_map_kref_release(struct kref *kref)
0022 {
0023 struct comedi_buf_map *bm =
0024 container_of(kref, struct comedi_buf_map, refcount);
0025 struct comedi_buf_page *buf;
0026 unsigned int i;
0027
0028 if (bm->page_list) {
0029 if (bm->dma_dir != DMA_NONE) {
0030
0031
0032
0033
0034 buf = &bm->page_list[0];
0035 dma_free_coherent(bm->dma_hw_dev,
0036 PAGE_SIZE * bm->n_pages,
0037 buf->virt_addr, buf->dma_addr);
0038 } else {
0039 for (i = 0; i < bm->n_pages; i++) {
0040 buf = &bm->page_list[i];
0041 ClearPageReserved(virt_to_page(buf->virt_addr));
0042 free_page((unsigned long)buf->virt_addr);
0043 }
0044 }
0045 vfree(bm->page_list);
0046 }
0047 if (bm->dma_dir != DMA_NONE)
0048 put_device(bm->dma_hw_dev);
0049 kfree(bm);
0050 }
0051
0052 static void __comedi_buf_free(struct comedi_device *dev,
0053 struct comedi_subdevice *s)
0054 {
0055 struct comedi_async *async = s->async;
0056 struct comedi_buf_map *bm;
0057 unsigned long flags;
0058
0059 if (async->prealloc_buf) {
0060 if (s->async_dma_dir == DMA_NONE)
0061 vunmap(async->prealloc_buf);
0062 async->prealloc_buf = NULL;
0063 async->prealloc_bufsz = 0;
0064 }
0065
0066 spin_lock_irqsave(&s->spin_lock, flags);
0067 bm = async->buf_map;
0068 async->buf_map = NULL;
0069 spin_unlock_irqrestore(&s->spin_lock, flags);
0070 comedi_buf_map_put(bm);
0071 }
0072
0073 static struct comedi_buf_map *
0074 comedi_buf_map_alloc(struct comedi_device *dev, enum dma_data_direction dma_dir,
0075 unsigned int n_pages)
0076 {
0077 struct comedi_buf_map *bm;
0078 struct comedi_buf_page *buf;
0079 unsigned int i;
0080
0081 bm = kzalloc(sizeof(*bm), GFP_KERNEL);
0082 if (!bm)
0083 return NULL;
0084
0085 kref_init(&bm->refcount);
0086 bm->dma_dir = dma_dir;
0087 if (bm->dma_dir != DMA_NONE) {
0088
0089 bm->dma_hw_dev = get_device(dev->hw_dev);
0090 }
0091
0092 bm->page_list = vzalloc(sizeof(*buf) * n_pages);
0093 if (!bm->page_list)
0094 goto err;
0095
0096 if (bm->dma_dir != DMA_NONE) {
0097 void *virt_addr;
0098 dma_addr_t dma_addr;
0099
0100
0101
0102
0103
0104 virt_addr = dma_alloc_coherent(bm->dma_hw_dev,
0105 PAGE_SIZE * n_pages, &dma_addr,
0106 GFP_KERNEL);
0107 if (!virt_addr)
0108 goto err;
0109
0110 for (i = 0; i < n_pages; i++) {
0111 buf = &bm->page_list[i];
0112 buf->virt_addr = virt_addr + (i << PAGE_SHIFT);
0113 buf->dma_addr = dma_addr + (i << PAGE_SHIFT);
0114 }
0115
0116 bm->n_pages = i;
0117 } else {
0118 for (i = 0; i < n_pages; i++) {
0119 buf = &bm->page_list[i];
0120 buf->virt_addr = (void *)get_zeroed_page(GFP_KERNEL);
0121 if (!buf->virt_addr)
0122 break;
0123
0124 SetPageReserved(virt_to_page(buf->virt_addr));
0125 }
0126
0127 bm->n_pages = i;
0128 if (i < n_pages)
0129 goto err;
0130 }
0131
0132 return bm;
0133
0134 err:
0135 comedi_buf_map_put(bm);
0136 return NULL;
0137 }
0138
0139 static void __comedi_buf_alloc(struct comedi_device *dev,
0140 struct comedi_subdevice *s,
0141 unsigned int n_pages)
0142 {
0143 struct comedi_async *async = s->async;
0144 struct page **pages = NULL;
0145 struct comedi_buf_map *bm;
0146 struct comedi_buf_page *buf;
0147 unsigned long flags;
0148 unsigned int i;
0149
0150 if (!IS_ENABLED(CONFIG_HAS_DMA) && s->async_dma_dir != DMA_NONE) {
0151 dev_err(dev->class_dev,
0152 "dma buffer allocation not supported\n");
0153 return;
0154 }
0155
0156 bm = comedi_buf_map_alloc(dev, s->async_dma_dir, n_pages);
0157 if (!bm)
0158 return;
0159
0160 spin_lock_irqsave(&s->spin_lock, flags);
0161 async->buf_map = bm;
0162 spin_unlock_irqrestore(&s->spin_lock, flags);
0163
0164 if (bm->dma_dir != DMA_NONE) {
0165
0166
0167
0168
0169 buf = &bm->page_list[0];
0170 async->prealloc_buf = buf->virt_addr;
0171 } else {
0172 pages = vmalloc(sizeof(struct page *) * n_pages);
0173 if (!pages)
0174 return;
0175
0176 for (i = 0; i < n_pages; i++) {
0177 buf = &bm->page_list[i];
0178 pages[i] = virt_to_page(buf->virt_addr);
0179 }
0180
0181
0182 async->prealloc_buf = vmap(pages, n_pages, VM_MAP,
0183 COMEDI_PAGE_PROTECTION);
0184
0185 vfree(pages);
0186 }
0187 }
0188
0189 void comedi_buf_map_get(struct comedi_buf_map *bm)
0190 {
0191 if (bm)
0192 kref_get(&bm->refcount);
0193 }
0194
0195 int comedi_buf_map_put(struct comedi_buf_map *bm)
0196 {
0197 if (bm)
0198 return kref_put(&bm->refcount, comedi_buf_map_kref_release);
0199 return 1;
0200 }
0201
0202
0203 int comedi_buf_map_access(struct comedi_buf_map *bm, unsigned long offset,
0204 void *buf, int len, int write)
0205 {
0206 unsigned int pgoff = offset_in_page(offset);
0207 unsigned long pg = offset >> PAGE_SHIFT;
0208 int done = 0;
0209
0210 while (done < len && pg < bm->n_pages) {
0211 int l = min_t(int, len - done, PAGE_SIZE - pgoff);
0212 void *b = bm->page_list[pg].virt_addr + pgoff;
0213
0214 if (write)
0215 memcpy(b, buf, l);
0216 else
0217 memcpy(buf, b, l);
0218 buf += l;
0219 done += l;
0220 pg++;
0221 pgoff = 0;
0222 }
0223 return done;
0224 }
0225
0226
0227 struct comedi_buf_map *
0228 comedi_buf_map_from_subdev_get(struct comedi_subdevice *s)
0229 {
0230 struct comedi_async *async = s->async;
0231 struct comedi_buf_map *bm = NULL;
0232 unsigned long flags;
0233
0234 if (!async)
0235 return NULL;
0236
0237 spin_lock_irqsave(&s->spin_lock, flags);
0238 bm = async->buf_map;
0239
0240 if (bm && bm->n_pages)
0241 comedi_buf_map_get(bm);
0242 else
0243 bm = NULL;
0244 spin_unlock_irqrestore(&s->spin_lock, flags);
0245
0246 return bm;
0247 }
0248
0249 bool comedi_buf_is_mmapped(struct comedi_subdevice *s)
0250 {
0251 struct comedi_buf_map *bm = s->async->buf_map;
0252
0253 return bm && (kref_read(&bm->refcount) > 1);
0254 }
0255
0256 int comedi_buf_alloc(struct comedi_device *dev, struct comedi_subdevice *s,
0257 unsigned long new_size)
0258 {
0259 struct comedi_async *async = s->async;
0260
0261 lockdep_assert_held(&dev->mutex);
0262
0263
0264 new_size = (new_size + PAGE_SIZE - 1) & PAGE_MASK;
0265
0266
0267 if (async->prealloc_buf && async->prealloc_bufsz == new_size)
0268 return 0;
0269
0270
0271 __comedi_buf_free(dev, s);
0272
0273
0274 if (new_size) {
0275 unsigned int n_pages = new_size >> PAGE_SHIFT;
0276
0277 __comedi_buf_alloc(dev, s, n_pages);
0278
0279 if (!async->prealloc_buf) {
0280
0281 __comedi_buf_free(dev, s);
0282 return -ENOMEM;
0283 }
0284 }
0285 async->prealloc_bufsz = new_size;
0286
0287 return 0;
0288 }
0289
0290 void comedi_buf_reset(struct comedi_subdevice *s)
0291 {
0292 struct comedi_async *async = s->async;
0293
0294 async->buf_write_alloc_count = 0;
0295 async->buf_write_count = 0;
0296 async->buf_read_alloc_count = 0;
0297 async->buf_read_count = 0;
0298
0299 async->buf_write_ptr = 0;
0300 async->buf_read_ptr = 0;
0301
0302 async->cur_chan = 0;
0303 async->scans_done = 0;
0304 async->scan_progress = 0;
0305 async->munge_chan = 0;
0306 async->munge_count = 0;
0307 async->munge_ptr = 0;
0308
0309 async->events = 0;
0310 }
0311
0312 static unsigned int comedi_buf_write_n_unalloc(struct comedi_subdevice *s)
0313 {
0314 struct comedi_async *async = s->async;
0315 unsigned int free_end = async->buf_read_count + async->prealloc_bufsz;
0316
0317 return free_end - async->buf_write_alloc_count;
0318 }
0319
0320 unsigned int comedi_buf_write_n_available(struct comedi_subdevice *s)
0321 {
0322 struct comedi_async *async = s->async;
0323 unsigned int free_end = async->buf_read_count + async->prealloc_bufsz;
0324
0325 return free_end - async->buf_write_count;
0326 }
0327
0328
0329
0330
0331
0332
0333
0334
0335
0336
0337
0338
0339 unsigned int comedi_buf_write_alloc(struct comedi_subdevice *s,
0340 unsigned int nbytes)
0341 {
0342 struct comedi_async *async = s->async;
0343 unsigned int unalloc = comedi_buf_write_n_unalloc(s);
0344
0345 if (nbytes > unalloc)
0346 nbytes = unalloc;
0347
0348 async->buf_write_alloc_count += nbytes;
0349
0350
0351
0352
0353
0354 smp_mb();
0355
0356 return nbytes;
0357 }
0358 EXPORT_SYMBOL_GPL(comedi_buf_write_alloc);
0359
0360
0361
0362
0363
0364 static unsigned int comedi_buf_munge(struct comedi_subdevice *s,
0365 unsigned int num_bytes)
0366 {
0367 struct comedi_async *async = s->async;
0368 unsigned int count = 0;
0369 const unsigned int num_sample_bytes = comedi_bytes_per_sample(s);
0370
0371 if (!s->munge || (async->cmd.flags & CMDF_RAWDATA)) {
0372 async->munge_count += num_bytes;
0373 return num_bytes;
0374 }
0375
0376
0377 num_bytes -= num_bytes % num_sample_bytes;
0378 while (count < num_bytes) {
0379 int block_size = num_bytes - count;
0380 unsigned int buf_end;
0381
0382 buf_end = async->prealloc_bufsz - async->munge_ptr;
0383 if (block_size > buf_end)
0384 block_size = buf_end;
0385
0386 s->munge(s->device, s,
0387 async->prealloc_buf + async->munge_ptr,
0388 block_size, async->munge_chan);
0389
0390
0391
0392
0393
0394 smp_wmb();
0395
0396 async->munge_chan += block_size / num_sample_bytes;
0397 async->munge_chan %= async->cmd.chanlist_len;
0398 async->munge_count += block_size;
0399 async->munge_ptr += block_size;
0400 async->munge_ptr %= async->prealloc_bufsz;
0401 count += block_size;
0402 }
0403
0404 return count;
0405 }
0406
0407 unsigned int comedi_buf_write_n_allocated(struct comedi_subdevice *s)
0408 {
0409 struct comedi_async *async = s->async;
0410
0411 return async->buf_write_alloc_count - async->buf_write_count;
0412 }
0413
0414
0415
0416
0417
0418
0419
0420
0421
0422
0423
0424
0425
0426
0427
0428
0429 unsigned int comedi_buf_write_free(struct comedi_subdevice *s,
0430 unsigned int nbytes)
0431 {
0432 struct comedi_async *async = s->async;
0433 unsigned int allocated = comedi_buf_write_n_allocated(s);
0434
0435 if (nbytes > allocated)
0436 nbytes = allocated;
0437
0438 async->buf_write_count += nbytes;
0439 async->buf_write_ptr += nbytes;
0440 comedi_buf_munge(s, async->buf_write_count - async->munge_count);
0441 if (async->buf_write_ptr >= async->prealloc_bufsz)
0442 async->buf_write_ptr %= async->prealloc_bufsz;
0443
0444 return nbytes;
0445 }
0446 EXPORT_SYMBOL_GPL(comedi_buf_write_free);
0447
0448
0449
0450
0451
0452
0453
0454
0455
0456
0457
0458
0459 unsigned int comedi_buf_read_n_available(struct comedi_subdevice *s)
0460 {
0461 struct comedi_async *async = s->async;
0462 unsigned int num_bytes;
0463
0464 if (!async)
0465 return 0;
0466
0467 num_bytes = async->munge_count - async->buf_read_count;
0468
0469
0470
0471
0472
0473 smp_rmb();
0474
0475 return num_bytes;
0476 }
0477 EXPORT_SYMBOL_GPL(comedi_buf_read_n_available);
0478
0479
0480
0481
0482
0483
0484
0485
0486
0487
0488
0489
0490
0491
0492
0493
0494 unsigned int comedi_buf_read_alloc(struct comedi_subdevice *s,
0495 unsigned int nbytes)
0496 {
0497 struct comedi_async *async = s->async;
0498 unsigned int available;
0499
0500 available = async->munge_count - async->buf_read_alloc_count;
0501 if (nbytes > available)
0502 nbytes = available;
0503
0504 async->buf_read_alloc_count += nbytes;
0505
0506
0507
0508
0509
0510 smp_rmb();
0511
0512 return nbytes;
0513 }
0514 EXPORT_SYMBOL_GPL(comedi_buf_read_alloc);
0515
0516 static unsigned int comedi_buf_read_n_allocated(struct comedi_async *async)
0517 {
0518 return async->buf_read_alloc_count - async->buf_read_count;
0519 }
0520
0521
0522
0523
0524
0525
0526
0527
0528
0529
0530
0531
0532
0533
0534 unsigned int comedi_buf_read_free(struct comedi_subdevice *s,
0535 unsigned int nbytes)
0536 {
0537 struct comedi_async *async = s->async;
0538 unsigned int allocated;
0539
0540
0541
0542
0543
0544 smp_mb();
0545
0546 allocated = comedi_buf_read_n_allocated(async);
0547 if (nbytes > allocated)
0548 nbytes = allocated;
0549
0550 async->buf_read_count += nbytes;
0551 async->buf_read_ptr += nbytes;
0552 async->buf_read_ptr %= async->prealloc_bufsz;
0553 return nbytes;
0554 }
0555 EXPORT_SYMBOL_GPL(comedi_buf_read_free);
0556
0557 static void comedi_buf_memcpy_to(struct comedi_subdevice *s,
0558 const void *data, unsigned int num_bytes)
0559 {
0560 struct comedi_async *async = s->async;
0561 unsigned int write_ptr = async->buf_write_ptr;
0562
0563 while (num_bytes) {
0564 unsigned int block_size;
0565
0566 if (write_ptr + num_bytes > async->prealloc_bufsz)
0567 block_size = async->prealloc_bufsz - write_ptr;
0568 else
0569 block_size = num_bytes;
0570
0571 memcpy(async->prealloc_buf + write_ptr, data, block_size);
0572
0573 data += block_size;
0574 num_bytes -= block_size;
0575
0576 write_ptr = 0;
0577 }
0578 }
0579
0580 static void comedi_buf_memcpy_from(struct comedi_subdevice *s,
0581 void *dest, unsigned int nbytes)
0582 {
0583 void *src;
0584 struct comedi_async *async = s->async;
0585 unsigned int read_ptr = async->buf_read_ptr;
0586
0587 while (nbytes) {
0588 unsigned int block_size;
0589
0590 src = async->prealloc_buf + read_ptr;
0591
0592 if (nbytes >= async->prealloc_bufsz - read_ptr)
0593 block_size = async->prealloc_bufsz - read_ptr;
0594 else
0595 block_size = nbytes;
0596
0597 memcpy(dest, src, block_size);
0598 nbytes -= block_size;
0599 dest += block_size;
0600 read_ptr = 0;
0601 }
0602 }
0603
0604
0605
0606
0607
0608
0609
0610
0611
0612
0613
0614
0615
0616
0617
0618
0619
0620
0621 unsigned int comedi_buf_write_samples(struct comedi_subdevice *s,
0622 const void *data, unsigned int nsamples)
0623 {
0624 unsigned int max_samples;
0625 unsigned int nbytes;
0626
0627
0628
0629
0630
0631
0632 max_samples = comedi_bytes_to_samples(s, comedi_buf_write_n_unalloc(s));
0633 if (nsamples > max_samples) {
0634 dev_warn(s->device->class_dev, "buffer overrun\n");
0635 s->async->events |= COMEDI_CB_OVERFLOW;
0636 nsamples = max_samples;
0637 }
0638
0639 if (nsamples == 0)
0640 return 0;
0641
0642 nbytes = comedi_buf_write_alloc(s,
0643 comedi_samples_to_bytes(s, nsamples));
0644 comedi_buf_memcpy_to(s, data, nbytes);
0645 comedi_buf_write_free(s, nbytes);
0646 comedi_inc_scan_progress(s, nbytes);
0647 s->async->events |= COMEDI_CB_BLOCK;
0648
0649 return nbytes;
0650 }
0651 EXPORT_SYMBOL_GPL(comedi_buf_write_samples);
0652
0653
0654
0655
0656
0657
0658
0659
0660
0661
0662
0663
0664
0665
0666
0667 unsigned int comedi_buf_read_samples(struct comedi_subdevice *s,
0668 void *data, unsigned int nsamples)
0669 {
0670 unsigned int max_samples;
0671 unsigned int nbytes;
0672
0673
0674 max_samples = comedi_bytes_to_samples(s,
0675 comedi_buf_read_n_available(s));
0676 if (nsamples > max_samples)
0677 nsamples = max_samples;
0678
0679 if (nsamples == 0)
0680 return 0;
0681
0682 nbytes = comedi_buf_read_alloc(s,
0683 comedi_samples_to_bytes(s, nsamples));
0684 comedi_buf_memcpy_from(s, data, nbytes);
0685 comedi_buf_read_free(s, nbytes);
0686 comedi_inc_scan_progress(s, nbytes);
0687 s->async->events |= COMEDI_CB_BLOCK;
0688
0689 return nbytes;
0690 }
0691 EXPORT_SYMBOL_GPL(comedi_buf_read_samples);