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