summaryrefslogtreecommitdiff
path: root/drivers/staging/comedi/comedidev.h
blob: 6a30af2df2bdd208ca79013f51c8dc9da88afd50 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
/*
 * comedidev.h
 * header file for kernel-only structures, variables, and constants
 *
 * COMEDI - Linux Control and Measurement Device Interface
 * Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#ifndef _COMEDIDEV_H
#define _COMEDIDEV_H

#include <linux/dma-mapping.h>
#include <linux/mutex.h>
#include <linux/spinlock_types.h>
#include <linux/rwsem.h>
#include <linux/kref.h>

#include "comedi.h"

#define COMEDI_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
#define COMEDI_VERSION_CODE COMEDI_VERSION(COMEDI_MAJORVERSION, \
	COMEDI_MINORVERSION, COMEDI_MICROVERSION)
#define COMEDI_RELEASE VERSION

#define COMEDI_NUM_BOARD_MINORS 0x30

struct comedi_subdevice {
	struct comedi_device *device;
	int index;
	int type;
	int n_chan;
	int subdev_flags;
	int len_chanlist;	/* maximum length of channel/gain list */

	void *private;

	struct comedi_async *async;

	void *lock;
	void *busy;
	unsigned runflags;
	spinlock_t spin_lock;

	unsigned int io_bits;

	unsigned int maxdata;	/* if maxdata==0, use list */
	const unsigned int *maxdata_list;	/* list is channel specific */

	const struct comedi_lrange *range_table;
	const struct comedi_lrange *const *range_table_list;

	unsigned int *chanlist;	/* driver-owned chanlist (not used) */

	int (*insn_read)(struct comedi_device *, struct comedi_subdevice *,
			 struct comedi_insn *, unsigned int *);
	int (*insn_write)(struct comedi_device *, struct comedi_subdevice *,
			  struct comedi_insn *, unsigned int *);
	int (*insn_bits)(struct comedi_device *, struct comedi_subdevice *,
			 struct comedi_insn *, unsigned int *);
	int (*insn_config)(struct comedi_device *, struct comedi_subdevice *,
			   struct comedi_insn *, unsigned int *);

	int (*do_cmd)(struct comedi_device *, struct comedi_subdevice *);
	int (*do_cmdtest)(struct comedi_device *, struct comedi_subdevice *,
			  struct comedi_cmd *);
	int (*poll)(struct comedi_device *, struct comedi_subdevice *);
	int (*cancel)(struct comedi_device *, struct comedi_subdevice *);

	/* called when the buffer changes */
	int (*buf_change)(struct comedi_device *, struct comedi_subdevice *);

	void (*munge)(struct comedi_device *dev, struct comedi_subdevice *s,
		      void *data, unsigned int num_bytes,
		      unsigned int start_chan_index);
	enum dma_data_direction async_dma_dir;

	unsigned int state;

	struct device *class_dev;
	int minor;

	unsigned int *readback;
};

struct comedi_buf_page {
	void *virt_addr;
	dma_addr_t dma_addr;
};

struct comedi_buf_map {
	struct device *dma_hw_dev;
	struct comedi_buf_page *page_list;
	unsigned int n_pages;
	enum dma_data_direction dma_dir;
	struct kref refcount;
};

/**
 * struct comedi_async - control data for asynchronous comedi commands
 * @prealloc_buf:	preallocated buffer
 * @prealloc_bufsz:	buffer size (in bytes)
 * @buf_map:		map of buffer pages
 * @max_bufsize:	maximum buffer size (in bytes)
 * @buf_write_count:	"write completed" count (in bytes, modulo 2**32)
 * @buf_write_alloc_count: "allocated for writing" count (in bytes,
 *			modulo 2**32)
 * @buf_read_count:	"read completed" count (in bytes, modulo 2**32)
 * @buf_read_alloc_count: "allocated for reading" count (in bytes,
 *			modulo 2**32)
 * @buf_write_ptr:	buffer position for writer
 * @buf_read_ptr:	buffer position for reader
 * @cur_chan:		current position in chanlist for scan (for those
 *			drivers that use it)
 * @scans_done:		the number of scans completed (COMEDI_CB_EOS)
 * @scan_progress:	amount received or sent for current scan (in bytes)
 * @munge_chan:		current position in chanlist for "munging"
 * @munge_count:	"munge" count (in bytes, modulo 2**32)
 * @munge_ptr:		buffer position for "munging"
 * @events:		bit-vector of events that have occurred
 * @cmd:		details of comedi command in progress
 * @wait_head:		task wait queue for file reader or writer
 * @cb_mask:		bit-vector of events that should wake waiting tasks
 * @inttrig:		software trigger function for command, or NULL
 *
 * Note about the ..._count and ..._ptr members:
 *
 * Think of the _Count values being integers of unlimited size, indexing
 * into a buffer of infinite length (though only an advancing portion
 * of the buffer of fixed length prealloc_bufsz is accessible at any
 * time).  Then:
 *
 *   Buf_Read_Count <= Buf_Read_Alloc_Count <= Munge_Count <=
 *   Buf_Write_Count <= Buf_Write_Alloc_Count <=
 *   (Buf_Read_Count + prealloc_bufsz)
 *
 * (Those aren't the actual members, apart from prealloc_bufsz.) When the
 * buffer is reset, those _Count values start at 0 and only increase in value,
 * maintaining the above inequalities until the next time the buffer is
 * reset.  The buffer is divided into the following regions by the inequalities:
 *
 *   [0, Buf_Read_Count):
 *     old region no longer accessible
 *
 *   [Buf_Read_Count, Buf_Read_Alloc_Count):
 *     filled and munged region allocated for reading but not yet read
 *
 *   [Buf_Read_Alloc_Count, Munge_Count):
 *     filled and munged region not yet allocated for reading
 *
 *   [Munge_Count, Buf_Write_Count):
 *     filled region not yet munged
 *
 *   [Buf_Write_Count, Buf_Write_Alloc_Count):
 *     unfilled region allocated for writing but not yet written
 *
 *   [Buf_Write_Alloc_Count, Buf_Read_Count + prealloc_bufsz):
 *     unfilled region not yet allocated for writing
 *
 *   [Buf_Read_Count + prealloc_bufsz, infinity):
 *     unfilled region not yet accessible
 *
 * Data needs to be written into the buffer before it can be read out,
 * and may need to be converted (or "munged") between the two
 * operations.  Extra unfilled buffer space may need to allocated for
 * writing (advancing Buf_Write_Alloc_Count) before new data is written.
 * After writing new data, the newly filled space needs to be released
 * (advancing Buf_Write_Count).  This also results in the new data being
 * "munged" (advancing Munge_Count).  Before data is read out of the
 * buffer, extra space may need to be allocated for reading (advancing
 * Buf_Read_Alloc_Count).  After the data has been read out, the space
 * needs to be released (advancing Buf_Read_Count).
 *
 * The actual members, buf_read_count, buf_read_alloc_count,
 * munge_count, buf_write_count, and buf_write_alloc_count take the
 * value of the corresponding capitalized _Count values modulo 2^32
 * (UINT_MAX+1).  Subtracting a "higher" _count value from a "lower"
 * _count value gives the same answer as subtracting a "higher" _Count
 * value from a lower _Count value because prealloc_bufsz < UINT_MAX+1.
 * The modulo operation is done implicitly.
 *
 * The buf_read_ptr, munge_ptr, and buf_write_ptr members take the value
 * of the corresponding capitalized _Count values modulo prealloc_bufsz.
 * These correspond to byte indices in the physical buffer.  The modulo
 * operation is done by subtracting prealloc_bufsz when the value
 * exceeds prealloc_bufsz (assuming prealloc_bufsz plus the increment is
 * less than or equal to UINT_MAX).
 */
struct comedi_async {
	void *prealloc_buf;
	unsigned int prealloc_bufsz;
	struct comedi_buf_map *buf_map;
	unsigned int max_bufsize;
	unsigned int buf_write_count;
	unsigned int buf_write_alloc_count;
	unsigned int buf_read_count;
	unsigned int buf_read_alloc_count;
	unsigned int buf_write_ptr;
	unsigned int buf_read_ptr;
	unsigned int cur_chan;
	unsigned int scans_done;
	unsigned int scan_progress;
	unsigned int munge_chan;
	unsigned int munge_count;
	unsigned int munge_ptr;
	unsigned int events;
	struct comedi_cmd cmd;
	wait_queue_head_t wait_head;
	unsigned int cb_mask;
	int (*inttrig)(struct comedi_device *dev, struct comedi_subdevice *s,
		       unsigned int x);
};

/**
 * enum comedi_cb - &struct comedi_async callback "events"
 * @COMEDI_CB_EOS:		end-of-scan
 * @COMEDI_CB_EOA:		end-of-acquisition/output
 * @COMEDI_CB_BLOCK:		data has arrived, wakes up read() / write()
 * @COMEDI_CB_EOBUF:		DEPRECATED: end of buffer
 * @COMEDI_CB_ERROR:		card error during acquisition
 * @COMEDI_CB_OVERFLOW:		buffer overflow/underflow
 * @COMEDI_CB_ERROR_MASK:	events that indicate an error has occurred
 * @COMEDI_CB_CANCEL_MASK:	events that will cancel an async command
 */
enum comedi_cb {
	COMEDI_CB_EOS		= BIT(0),
	COMEDI_CB_EOA		= BIT(1),
	COMEDI_CB_BLOCK		= BIT(2),
	COMEDI_CB_EOBUF		= BIT(3),
	COMEDI_CB_ERROR		= BIT(4),
	COMEDI_CB_OVERFLOW	= BIT(5),
	/* masks */
	COMEDI_CB_ERROR_MASK	= (COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW),
	COMEDI_CB_CANCEL_MASK	= (COMEDI_CB_EOA | COMEDI_CB_ERROR_MASK)
};

/**
 * struct comedi_driver - COMEDI driver registration
 * @driver_name: Name of driver.
 * @module: Owning module.
 * @attach: The optional "attach" handler for manually configured COMEDI
 *	devices.
 * @detach: The "detach" handler for deconfiguring COMEDI devices.
 * @auto_attach: The optional "auto_attach" handler for automatically
 *	configured COMEDI devices.
 * @num_names: Optional number of "board names" supported.
 * @board_name: Optional pointer to a pointer to a board name.  The pointer
 *	to a board name is embedded in an element of a driver-defined array
 *	of static, read-only board type information.
 * @offset: Optional size of each element of the driver-defined array of
 *	static, read-only board type information, i.e. the offset between each
 *	pointer to a board name.
 *
 * This is used with comedi_driver_register() and comedi_driver_unregister() to
 * register and unregister a low-level COMEDI driver with the COMEDI core.
 *
 * If @num_names is non-zero, @board_name should be non-NULL, and @offset
 * should be at least sizeof(*board_name).  These are used by the handler for
 * the %COMEDI_DEVCONFIG ioctl to match a hardware device and its driver by
 * board name.  If @num_names is zero, the %COMEDI_DEVCONFIG ioctl matches a
 * hardware device and its driver by driver name.  This is only useful if the
 * @attach handler is set.  If @num_names is non-zero, the driver's @attach
 * handler will be called with the COMEDI device structure's board_ptr member
 * pointing to the matched pointer to a board name within the driver's private
 * array of static, read-only board type information.
 */
struct comedi_driver {
	/* private: */
	struct comedi_driver *next;	/* Next in list of COMEDI drivers. */
	/* public: */
	const char *driver_name;
	struct module *module;
	int (*attach)(struct comedi_device *, struct comedi_devconfig *);
	void (*detach)(struct comedi_device *);
	int (*auto_attach)(struct comedi_device *, unsigned long);
	unsigned int num_names;
	const char *const *board_name;
	int offset;
};

/**
 * struct comedi_device - Working data for a COMEDI device
 * @use_count: Number of open file objects.
 * @driver: Low-level COMEDI driver attached to this COMEDI device.
 * @pacer: Optional pointer to a dynamically allocated acquisition pacer
 *	control.  It is freed automatically after the COMEDI device is
 *	detached from the low-level driver.
 * @private: Optional pointer to private data allocated by the low-level
 *	driver.  It is freed automatically after the COMEDI device is
 *	detached from the low-level driver.
 * @class_dev: Sysfs comediX device.
 * @minor: Minor device number of COMEDI char device (0-47).
 * @detach_count: Counter incremented every time the COMEDI device is detached.
 *	Used for checking a previous attachment is still valid.
 * @hw_dev: Optional pointer to the low-level hardware &struct device.  It is
 *	required for automatically configured COMEDI devices and optional for
 *	COMEDI devices configured by the %COMEDI_DEVCONFIG ioctl, although
 *	the bus-specific COMEDI functions only work if it is set correctly.
 *	It is also passed to dma_alloc_coherent() for COMEDI subdevices that
 *	have their 'async_dma_dir' member set to something other than
 *	%DMA_NONE.
 * @board_name: Pointer to a COMEDI board name or a COMEDI driver name.  When
 *	the low-level driver's "attach" handler is called by the handler for
 *	the %COMEDI_DEVCONFIG ioctl, it either points to a matched board name
 *	string if the 'num_names' member of the &struct comedi_driver is
 *	non-zero, otherwise it points to the low-level driver name string.
 *	When the low-lever driver's "auto_attach" handler is called for an
 *	automatically configured COMEDI device, it points to the low-level
 *	driver name string.  The low-level driver is free to change it in its
 *	"attach" or "auto_attach" handler if it wishes.
 * @board_ptr: Optional pointer to private, read-only board type information in
 *	the low-level driver.  If the 'num_names' member of the &struct
 *	comedi_driver is non-zero, the handler for the %COMEDI_DEVCONFIG ioctl
 *	will point it to a pointer to a matched board name string within the
 *	driver's private array of static, read-only board type information when
 *	calling the driver's "attach" handler.  The low-level driver is free to
 *	change it.
 * @attached: Flag indicating that the COMEDI device is attached to a low-level
 *	driver.
 * @ioenabled: Flag used to indicate that a PCI device has been enabled and
 *	its regions requested.
 * @spinlock: Generic spin-lock for use by the low-level driver.
 * @mutex: Generic mutex for use by the COMEDI core module.
 * @attach_lock: &struct rw_semaphore used to guard against the COMEDI device
 *	being detached while an operation is in progress.  The down_write()
 *	operation is only allowed while @mutex is held and is used when
 *	changing @attached and @detach_count and calling the low-level driver's
 *	"detach" handler.  The down_read() operation is generally used without
 *	holding @mutex.
 * @refcount: &struct kref reference counter for freeing COMEDI device.
 * @n_subdevices: Number of COMEDI subdevices allocated by the low-level
 *	driver for this device.
 * @subdevices: Dynamically allocated array of COMEDI subdevices.
 * @mmio: Optional pointer to a remapped MMIO region set by the low-level
 *	driver.
 * @iobase: Optional base of an I/O port region requested by the low-level
 *	driver.
 * @iolen: Length of I/O port region requested at @iobase.
 * @irq: Optional IRQ number requested by the low-level driver.
 * @read_subdev: Optional pointer to a default COMEDI subdevice operated on by
 *	the read() file operation.  Set by the low-level driver.
 * @write_subdev: Optional pointer to a default COMEDI subdevice operated on by
 *	the write() file operation.  Set by the low-level driver.
 * @async_queue: Storage for fasync_helper().
 * @open: Optional pointer to a function set by the low-level driver to be
 *	called when @use_count changes from 0 to 1.
 * @close: Optional pointer to a function set by the low-level driver to be
 *	called when @use_count changed from 1 to 0.
 *
 * This is the main control data structure for a COMEDI device (as far as the
 * COMEDI core is concerned).  There are two groups of COMEDI devices -
 * "legacy" devices that are configured by the handler for the
 * %COMEDI_DEVCONFIG ioctl, and automatically configured devices resulting
 * from a call to comedi_auto_config() as a result of a bus driver probe in
 * a low-level COMEDI driver.  The "legacy" COMEDI devices are allocated
 * during module initialization if the "comedi_num_legacy_minors" module
 * parameter is non-zero and use minor device numbers from 0 to
 * comedi_num_legacy_minors minus one.  The automatically configured COMEDI
 * devices are allocated on demand and use minor device numbers from
 * comedi_num_legacy_minors to 47.
 */
struct comedi_device {
	int use_count;
	struct comedi_driver *driver;
	struct comedi_8254 *pacer;
	void *private;

	struct device *class_dev;
	int minor;
	unsigned int detach_count;
	struct device *hw_dev;

	const char *board_name;
	const void *board_ptr;
	bool attached:1;
	bool ioenabled:1;
	spinlock_t spinlock;
	struct mutex mutex;
	struct rw_semaphore attach_lock;
	struct kref refcount;

	int n_subdevices;
	struct comedi_subdevice *subdevices;

	/* dumb */
	void __iomem *mmio;
	unsigned long iobase;
	unsigned long iolen;
	unsigned int irq;

	struct comedi_subdevice *read_subdev;
	struct comedi_subdevice *write_subdev;

	struct fasync_struct *async_queue;

	int (*open)(struct comedi_device *dev);
	void (*close)(struct comedi_device *dev);
};

/*
 * function prototypes
 */

void comedi_event(struct comedi_device *dev, struct comedi_subdevice *s);

struct comedi_device *comedi_dev_get_from_minor(unsigned minor);
int comedi_dev_put(struct comedi_device *dev);

bool comedi_is_subdevice_running(struct comedi_subdevice *s);

void *comedi_alloc_spriv(struct comedi_subdevice *s, size_t size);
void comedi_set_spriv_auto_free(struct comedi_subdevice *s);

int comedi_check_chanlist(struct comedi_subdevice *s,
			  int n,
			  unsigned int *chanlist);

/* range stuff */

#define RANGE(a, b)		{(a)*1e6, (b)*1e6, 0}
#define RANGE_ext(a, b)		{(a)*1e6, (b)*1e6, RF_EXTERNAL}
#define RANGE_mA(a, b)		{(a)*1e6, (b)*1e6, UNIT_mA}
#define RANGE_unitless(a, b)	{(a)*1e6, (b)*1e6, 0}
#define BIP_RANGE(a)		{-(a)*1e6, (a)*1e6, 0}
#define UNI_RANGE(a)		{0, (a)*1e6, 0}

extern const struct comedi_lrange range_bipolar10;
extern const struct comedi_lrange range_bipolar5;
extern const struct comedi_lrange range_bipolar2_5;
extern const struct comedi_lrange range_unipolar10;
extern const struct comedi_lrange range_unipolar5;
extern const struct comedi_lrange range_unipolar2_5;
extern const struct comedi_lrange range_0_20mA;
extern const struct comedi_lrange range_4_20mA;
extern const struct comedi_lrange range_0_32mA;
extern const struct comedi_lrange range_unknown;

#define range_digital		range_unipolar5

#if __GNUC__ >= 3
#define GCC_ZERO_LENGTH_ARRAY
#else
#define GCC_ZERO_LENGTH_ARRAY 0
#endif

struct comedi_lrange {
	int length;
	struct comedi_krange range[GCC_ZERO_LENGTH_ARRAY];
};

static inline bool comedi_range_is_bipolar(struct comedi_subdevice *s,
					   unsigned int range)
{
	return s->range_table->range[range].min < 0;
}

static inline bool comedi_range_is_unipolar(struct comedi_subdevice *s,
					    unsigned int range)
{
	return s->range_table->range[range].min >= 0;
}

static inline bool comedi_range_is_external(struct comedi_subdevice *s,
					    unsigned int range)
{
	return !!(s->range_table->range[range].flags & RF_EXTERNAL);
}

static inline bool comedi_chan_range_is_bipolar(struct comedi_subdevice *s,
						unsigned int chan,
						unsigned int range)
{
	return s->range_table_list[chan]->range[range].min < 0;
}

static inline bool comedi_chan_range_is_unipolar(struct comedi_subdevice *s,
						 unsigned int chan,
						 unsigned int range)
{
	return s->range_table_list[chan]->range[range].min >= 0;
}

static inline bool comedi_chan_range_is_external(struct comedi_subdevice *s,
						 unsigned int chan,
						 unsigned int range)
{
	return !!(s->range_table_list[chan]->range[range].flags & RF_EXTERNAL);
}

/* munge between offset binary and two's complement values */
static inline unsigned int comedi_offset_munge(struct comedi_subdevice *s,
					       unsigned int val)
{
	return val ^ s->maxdata ^ (s->maxdata >> 1);
}

/**
 * comedi_bytes_per_sample - determine subdevice sample size
 * @s:		comedi_subdevice struct
 *
 * The sample size will be 4 (sizeof int) or 2 (sizeof short) depending on
 * whether the SDF_LSAMPL subdevice flag is set or not.
 *
 * Returns the subdevice sample size.
 */
static inline unsigned int comedi_bytes_per_sample(struct comedi_subdevice *s)
{
	return s->subdev_flags & SDF_LSAMPL ? sizeof(int) : sizeof(short);
}

/**
 * comedi_sample_shift - determine log2 of subdevice sample size
 * @s:		comedi_subdevice struct
 *
 * The sample size will be 4 (sizeof int) or 2 (sizeof short) depending on
 * whether the SDF_LSAMPL subdevice flag is set or not.  The log2 of the
 * sample size will be 2 or 1 and can be used as the right operand of a
 * bit-shift operator to multiply or divide something by the sample size.
 *
 * Returns log2 of the subdevice sample size.
 */
static inline unsigned int comedi_sample_shift(struct comedi_subdevice *s)
{
	return s->subdev_flags & SDF_LSAMPL ? 2 : 1;
}

/**
 * comedi_bytes_to_samples - converts a number of bytes to a number of samples
 * @s:		comedi_subdevice struct
 * @nbytes:	number of bytes
 *
 * Returns the number of bytes divided by the subdevice sample size.
 */
static inline unsigned int comedi_bytes_to_samples(struct comedi_subdevice *s,
						   unsigned int nbytes)
{
	return nbytes >> comedi_sample_shift(s);
}

/**
 * comedi_samples_to_bytes - converts a number of samples to a number of bytes
 * @s:		comedi_subdevice struct
 * @nsamples:	number of samples
 *
 * Returns the number of samples multiplied by the subdevice sample size.
 * Does not check for arithmetic overflow.
 */
static inline unsigned int comedi_samples_to_bytes(struct comedi_subdevice *s,
						   unsigned int nsamples)
{
	return nsamples << comedi_sample_shift(s);
}

/**
 * comedi_check_trigger_src() - trivially validate a comedi_cmd trigger source
 * @src: pointer to the trigger source to validate
 * @flags: bitmask of valid TRIG_* for the trigger
 *
 * This is used in "step 1" of the do_cmdtest functions of comedi drivers
 * to vaildate the comedi_cmd triggers. The mask of the @src against the
 * @flags allows the userspace comedilib to pass all the comedi_cmd
 * triggers as TRIG_ANY and get back a bitmask of the valid trigger sources.
 */
static inline int comedi_check_trigger_src(unsigned int *src,
					   unsigned int flags)
{
	unsigned int orig_src = *src;

	*src = orig_src & flags;
	if (*src == TRIG_INVALID || *src != orig_src)
		return -EINVAL;
	return 0;
}

/**
 * comedi_check_trigger_is_unique() - make sure a trigger source is unique
 * @src: the trigger source to check
 */
static inline int comedi_check_trigger_is_unique(unsigned int src)
{
	/* this test is true if more than one _src bit is set */
	if ((src & (src - 1)) != 0)
		return -EINVAL;
	return 0;
}

/**
 * comedi_check_trigger_arg_is() - trivially validate a trigger argument
 * @arg: pointer to the trigger arg to validate
 * @val: the value the argument should be
 */
static inline int comedi_check_trigger_arg_is(unsigned int *arg,
					      unsigned int val)
{
	if (*arg != val) {
		*arg = val;
		return -EINVAL;
	}
	return 0;
}

/**
 * comedi_check_trigger_arg_min() - trivially validate a trigger argument
 * @arg: pointer to the trigger arg to validate
 * @val: the minimum value the argument should be
 */
static inline int comedi_check_trigger_arg_min(unsigned int *arg,
					       unsigned int val)
{
	if (*arg < val) {
		*arg = val;
		return -EINVAL;
	}
	return 0;
}

/**
 * comedi_check_trigger_arg_max() - trivially validate a trigger argument
 * @arg: pointer to the trigger arg to validate
 * @val: the maximum value the argument should be
 */
static inline int comedi_check_trigger_arg_max(unsigned int *arg,
					       unsigned int val)
{
	if (*arg > val) {
		*arg = val;
		return -EINVAL;
	}
	return 0;
}

/*
 * Must set dev->hw_dev if you wish to dma directly into comedi's buffer.
 * Also useful for retrieving a previously configured hardware device of
 * known bus type.  Set automatically for auto-configured devices.
 * Automatically set to NULL when detaching hardware device.
 */
int comedi_set_hw_dev(struct comedi_device *dev, struct device *hw_dev);

static inline unsigned int comedi_buf_n_bytes_ready(struct comedi_subdevice *s)
{
	return s->async->buf_write_count - s->async->buf_read_count;
}

unsigned int comedi_buf_write_alloc(struct comedi_subdevice *s, unsigned int n);
unsigned int comedi_buf_write_free(struct comedi_subdevice *s, unsigned int n);

unsigned int comedi_buf_read_n_available(struct comedi_subdevice *s);
unsigned int comedi_buf_read_alloc(struct comedi_subdevice *s, unsigned int n);
unsigned int comedi_buf_read_free(struct comedi_subdevice *s, unsigned int n);

unsigned int comedi_buf_write_samples(struct comedi_subdevice *s,
				      const void *data, unsigned int nsamples);
unsigned int comedi_buf_read_samples(struct comedi_subdevice *s,
				     void *data, unsigned int nsamples);

/* drivers.c - general comedi driver functions */

#define COMEDI_TIMEOUT_MS	1000

int comedi_timeout(struct comedi_device *, struct comedi_subdevice *,
		   struct comedi_insn *,
		   int (*cb)(struct comedi_device *, struct comedi_subdevice *,
			     struct comedi_insn *, unsigned long context),
		   unsigned long context);

unsigned int comedi_handle_events(struct comedi_device *dev,
				  struct comedi_subdevice *s);

int comedi_dio_insn_config(struct comedi_device *, struct comedi_subdevice *,
			   struct comedi_insn *, unsigned int *data,
			   unsigned int mask);
unsigned int comedi_dio_update_state(struct comedi_subdevice *,
				     unsigned int *data);
unsigned int comedi_bytes_per_scan(struct comedi_subdevice *s);
unsigned int comedi_nscans_left(struct comedi_subdevice *s,
				unsigned int nscans);
unsigned int comedi_nsamples_left(struct comedi_subdevice *s,
				  unsigned int nsamples);
void comedi_inc_scan_progress(struct comedi_subdevice *s,
			      unsigned int num_bytes);

void *comedi_alloc_devpriv(struct comedi_device *, size_t);
int comedi_alloc_subdevices(struct comedi_device *, int);
int comedi_alloc_subdev_readback(struct comedi_subdevice *);

int comedi_readback_insn_read(struct comedi_device *, struct comedi_subdevice *,
			      struct comedi_insn *, unsigned int *data);

int comedi_load_firmware(struct comedi_device *, struct device *,
			 const char *name,
			 int (*cb)(struct comedi_device *,
				   const u8 *data, size_t size,
				   unsigned long context),
			 unsigned long context);

int __comedi_request_region(struct comedi_device *,
			    unsigned long start, unsigned long len);
int comedi_request_region(struct comedi_device *,
			  unsigned long start, unsigned long len);
void comedi_legacy_detach(struct comedi_device *);

int comedi_auto_config(struct device *, struct comedi_driver *,
		       unsigned long context);
void comedi_auto_unconfig(struct device *);

int comedi_driver_register(struct comedi_driver *);
void comedi_driver_unregister(struct comedi_driver *);

/**
 * module_comedi_driver() - Helper macro for registering a comedi driver
 * @__comedi_driver: comedi_driver struct
 *
 * Helper macro for comedi drivers which do not do anything special in module
 * init/exit. This eliminates a lot of boilerplate. Each module may only use
 * this macro once, and calling it replaces module_init() and module_exit().
 */
#define module_comedi_driver(__comedi_driver) \
	module_driver(__comedi_driver, comedi_driver_register, \
			comedi_driver_unregister)

#endif /* _COMEDIDEV_H */