1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * comedi/drivers/comedi_test.c
4 *
5 * Generates fake waveform signals that can be read through
6 * the command interface.  It does _not_ read from any board;
7 * it just generates deterministic waveforms.
8 * Useful for various testing purposes.
9 *
10 * Copyright (C) 2002 Joachim Wuttke <Joachim.Wuttke@icn.siemens.de>
11 * Copyright (C) 2002 Frank Mori Hess <fmhess@users.sourceforge.net>
12 *
13 * COMEDI - Linux Control and Measurement Device Interface
14 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
15 */
16
17/*
18 * Driver: comedi_test
19 * Description: generates fake waveforms
20 * Author: Joachim Wuttke <Joachim.Wuttke@icn.siemens.de>, Frank Mori Hess
21 *   <fmhess@users.sourceforge.net>, ds
22 * Devices:
23 * Status: works
24 * Updated: Sat, 16 Mar 2002 17:34:48 -0800
25 *
26 * This driver is mainly for testing purposes, but can also be used to
27 * generate sample waveforms on systems that don't have data acquisition
28 * hardware.
29 *
30 * Auto-configuration is the default mode if no parameter is supplied during
31 * module loading. Manual configuration requires COMEDI userspace tool.
32 * To disable auto-configuration mode, pass "noauto=1" parameter for module
33 * loading. Refer modinfo or MODULE_PARM_DESC description below for details.
34 *
35 * Auto-configuration options:
36 *   Refer modinfo or MODULE_PARM_DESC description below for details.
37 *
38 * Manual configuration options:
39 *   [0] - Amplitude in microvolts for fake waveforms (default 1 volt)
40 *   [1] - Period in microseconds for fake waveforms (default 0.1 sec)
41 *
42 * Generates a sawtooth wave on channel 0, square wave on channel 1, additional
43 * waveforms could be added to other channels (currently they return flatline
44 * zero volts).
45 */
46
47#include <linux/module.h>
48#include <linux/comedi/comedidev.h>
49#include <asm/div64.h>
50#include <linux/timer.h>
51#include <linux/ktime.h>
52#include <linux/jiffies.h>
53#include <linux/device.h>
54#include <linux/kdev_t.h>
55
56#define N_CHANS 8
57#define DEV_NAME "comedi_testd"
58#define CLASS_NAME "comedi_test"
59
60static bool config_mode;
61static unsigned int set_amplitude;
62static unsigned int set_period;
63static const struct class ctcls = {
64	.name = CLASS_NAME,
65};
66static struct device *ctdev;
67
68module_param_named(noauto, config_mode, bool, 0444);
69MODULE_PARM_DESC(noauto, "Disable auto-configuration: (1=disable [defaults to enable])");
70
71module_param_named(amplitude, set_amplitude, uint, 0444);
72MODULE_PARM_DESC(amplitude, "Set auto mode wave amplitude in microvolts: (defaults to 1 volt)");
73
74module_param_named(period, set_period, uint, 0444);
75MODULE_PARM_DESC(period, "Set auto mode wave period in microseconds: (defaults to 0.1 sec)");
76
77/* Data unique to this driver */
78struct waveform_private {
79	struct timer_list ai_timer;	/* timer for AI commands */
80	u64 ai_convert_time;		/* time of next AI conversion in usec */
81	unsigned int wf_amplitude;	/* waveform amplitude in microvolts */
82	unsigned int wf_period;		/* waveform period in microseconds */
83	unsigned int wf_current;	/* current time in waveform period */
84	unsigned int ai_scan_period;	/* AI scan period in usec */
85	unsigned int ai_convert_period;	/* AI conversion period in usec */
86	struct timer_list ao_timer;	/* timer for AO commands */
87	struct comedi_device *dev;	/* parent comedi device */
88	u64 ao_last_scan_time;		/* time of previous AO scan in usec */
89	unsigned int ao_scan_period;	/* AO scan period in usec */
90	bool ai_timer_enable:1;		/* should AI timer be running? */
91	bool ao_timer_enable:1;		/* should AO timer be running? */
92	unsigned short ao_loopbacks[N_CHANS];
93};
94
95/* fake analog input ranges */
96static const struct comedi_lrange waveform_ai_ranges = {
97	2, {
98		BIP_RANGE(10),
99		BIP_RANGE(5)
100	}
101};
102
103static unsigned short fake_sawtooth(struct comedi_device *dev,
104				    unsigned int range_index,
105				    unsigned int current_time)
106{
107	struct waveform_private *devpriv = dev->private;
108	struct comedi_subdevice *s = dev->read_subdev;
109	unsigned int offset = s->maxdata / 2;
110	u64 value;
111	const struct comedi_krange *krange =
112	    &s->range_table->range[range_index];
113	u64 binary_amplitude;
114
115	binary_amplitude = s->maxdata;
116	binary_amplitude *= devpriv->wf_amplitude;
117	do_div(binary_amplitude, krange->max - krange->min);
118
119	value = current_time;
120	value *= binary_amplitude * 2;
121	do_div(value, devpriv->wf_period);
122	value += offset;
123	/* get rid of sawtooth's dc offset and clamp value */
124	if (value < binary_amplitude) {
125		value = 0;			/* negative saturation */
126	} else {
127		value -= binary_amplitude;
128		if (value > s->maxdata)
129			value = s->maxdata;	/* positive saturation */
130	}
131
132	return value;
133}
134
135static unsigned short fake_squarewave(struct comedi_device *dev,
136				      unsigned int range_index,
137				      unsigned int current_time)
138{
139	struct waveform_private *devpriv = dev->private;
140	struct comedi_subdevice *s = dev->read_subdev;
141	unsigned int offset = s->maxdata / 2;
142	u64 value;
143	const struct comedi_krange *krange =
144	    &s->range_table->range[range_index];
145
146	value = s->maxdata;
147	value *= devpriv->wf_amplitude;
148	do_div(value, krange->max - krange->min);
149
150	/* get one of two values for square-wave and clamp */
151	if (current_time < devpriv->wf_period / 2) {
152		if (offset < value)
153			value = 0;		/* negative saturation */
154		else
155			value = offset - value;
156	} else {
157		value += offset;
158		if (value > s->maxdata)
159			value = s->maxdata;	/* positive saturation */
160	}
161
162	return value;
163}
164
165static unsigned short fake_flatline(struct comedi_device *dev,
166				    unsigned int range_index,
167				    unsigned int current_time)
168{
169	return dev->read_subdev->maxdata / 2;
170}
171
172/* generates a different waveform depending on what channel is read */
173static unsigned short fake_waveform(struct comedi_device *dev,
174				    unsigned int channel, unsigned int range,
175				    unsigned int current_time)
176{
177	enum {
178		SAWTOOTH_CHAN,
179		SQUARE_CHAN,
180	};
181	switch (channel) {
182	case SAWTOOTH_CHAN:
183		return fake_sawtooth(dev, range, current_time);
184	case SQUARE_CHAN:
185		return fake_squarewave(dev, range, current_time);
186	default:
187		break;
188	}
189
190	return fake_flatline(dev, range, current_time);
191}
192
193/*
194 * This is the background routine used to generate arbitrary data.
195 * It should run in the background; therefore it is scheduled by
196 * a timer mechanism.
197 */
198static void waveform_ai_timer(struct timer_list *t)
199{
200	struct waveform_private *devpriv = from_timer(devpriv, t, ai_timer);
201	struct comedi_device *dev = devpriv->dev;
202	struct comedi_subdevice *s = dev->read_subdev;
203	struct comedi_async *async = s->async;
204	struct comedi_cmd *cmd = &async->cmd;
205	u64 now;
206	unsigned int nsamples;
207	unsigned int time_increment;
208
209	now = ktime_to_us(ktime_get());
210	nsamples = comedi_nsamples_left(s, UINT_MAX);
211
212	while (nsamples && devpriv->ai_convert_time < now) {
213		unsigned int chanspec = cmd->chanlist[async->cur_chan];
214		unsigned short sample;
215
216		sample = fake_waveform(dev, CR_CHAN(chanspec),
217				       CR_RANGE(chanspec), devpriv->wf_current);
218		if (comedi_buf_write_samples(s, &sample, 1) == 0)
219			goto overrun;
220		time_increment = devpriv->ai_convert_period;
221		if (async->scan_progress == 0) {
222			/* done last conversion in scan, so add dead time */
223			time_increment += devpriv->ai_scan_period -
224					  devpriv->ai_convert_period *
225					  cmd->scan_end_arg;
226		}
227		devpriv->wf_current += time_increment;
228		if (devpriv->wf_current >= devpriv->wf_period)
229			devpriv->wf_current %= devpriv->wf_period;
230		devpriv->ai_convert_time += time_increment;
231		nsamples--;
232	}
233
234	if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg) {
235		async->events |= COMEDI_CB_EOA;
236	} else {
237		if (devpriv->ai_convert_time > now)
238			time_increment = devpriv->ai_convert_time - now;
239		else
240			time_increment = 1;
241		spin_lock(&dev->spinlock);
242		if (devpriv->ai_timer_enable) {
243			mod_timer(&devpriv->ai_timer,
244				  jiffies + usecs_to_jiffies(time_increment));
245		}
246		spin_unlock(&dev->spinlock);
247	}
248
249overrun:
250	comedi_handle_events(dev, s);
251}
252
253static int waveform_ai_cmdtest(struct comedi_device *dev,
254			       struct comedi_subdevice *s,
255			       struct comedi_cmd *cmd)
256{
257	int err = 0;
258	unsigned int arg, limit;
259
260	/* Step 1 : check if triggers are trivially valid */
261
262	err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
263	err |= comedi_check_trigger_src(&cmd->scan_begin_src,
264					TRIG_FOLLOW | TRIG_TIMER);
265	err |= comedi_check_trigger_src(&cmd->convert_src,
266					TRIG_NOW | TRIG_TIMER);
267	err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
268	err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
269
270	if (err)
271		return 1;
272
273	/* Step 2a : make sure trigger sources are unique */
274
275	err |= comedi_check_trigger_is_unique(cmd->convert_src);
276	err |= comedi_check_trigger_is_unique(cmd->stop_src);
277
278	/* Step 2b : and mutually compatible */
279
280	if (cmd->scan_begin_src == TRIG_FOLLOW && cmd->convert_src == TRIG_NOW)
281		err |= -EINVAL;		/* scan period would be 0 */
282
283	if (err)
284		return 2;
285
286	/* Step 3: check if arguments are trivially valid */
287
288	err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
289
290	if (cmd->convert_src == TRIG_NOW) {
291		err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
292	} else {	/* cmd->convert_src == TRIG_TIMER */
293		if (cmd->scan_begin_src == TRIG_FOLLOW) {
294			err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
295							    NSEC_PER_USEC);
296		}
297	}
298
299	if (cmd->scan_begin_src == TRIG_FOLLOW) {
300		err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
301	} else {	/* cmd->scan_begin_src == TRIG_TIMER */
302		err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
303						    NSEC_PER_USEC);
304	}
305
306	err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
307	err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
308					   cmd->chanlist_len);
309
310	if (cmd->stop_src == TRIG_COUNT)
311		err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
312	else	/* cmd->stop_src == TRIG_NONE */
313		err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
314
315	if (err)
316		return 3;
317
318	/* step 4: fix up any arguments */
319
320	if (cmd->convert_src == TRIG_TIMER) {
321		/* round convert_arg to nearest microsecond */
322		arg = cmd->convert_arg;
323		arg = min(arg,
324			  rounddown(UINT_MAX, (unsigned int)NSEC_PER_USEC));
325		arg = NSEC_PER_USEC * DIV_ROUND_CLOSEST(arg, NSEC_PER_USEC);
326		if (cmd->scan_begin_arg == TRIG_TIMER) {
327			/* limit convert_arg to keep scan_begin_arg in range */
328			limit = UINT_MAX / cmd->scan_end_arg;
329			limit = rounddown(limit, (unsigned int)NSEC_PER_SEC);
330			arg = min(arg, limit);
331		}
332		err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
333	}
334
335	if (cmd->scan_begin_src == TRIG_TIMER) {
336		/* round scan_begin_arg to nearest microsecond */
337		arg = cmd->scan_begin_arg;
338		arg = min(arg,
339			  rounddown(UINT_MAX, (unsigned int)NSEC_PER_USEC));
340		arg = NSEC_PER_USEC * DIV_ROUND_CLOSEST(arg, NSEC_PER_USEC);
341		if (cmd->convert_src == TRIG_TIMER) {
342			/* but ensure scan_begin_arg is large enough */
343			arg = max(arg, cmd->convert_arg * cmd->scan_end_arg);
344		}
345		err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
346	}
347
348	if (err)
349		return 4;
350
351	return 0;
352}
353
354static int waveform_ai_cmd(struct comedi_device *dev,
355			   struct comedi_subdevice *s)
356{
357	struct waveform_private *devpriv = dev->private;
358	struct comedi_cmd *cmd = &s->async->cmd;
359	unsigned int first_convert_time;
360	u64 wf_current;
361
362	if (cmd->flags & CMDF_PRIORITY) {
363		dev_err(dev->class_dev,
364			"commands at RT priority not supported in this driver\n");
365		return -1;
366	}
367
368	if (cmd->convert_src == TRIG_NOW)
369		devpriv->ai_convert_period = 0;
370	else		/* cmd->convert_src == TRIG_TIMER */
371		devpriv->ai_convert_period = cmd->convert_arg / NSEC_PER_USEC;
372
373	if (cmd->scan_begin_src == TRIG_FOLLOW) {
374		devpriv->ai_scan_period = devpriv->ai_convert_period *
375					  cmd->scan_end_arg;
376	} else {	/* cmd->scan_begin_src == TRIG_TIMER */
377		devpriv->ai_scan_period = cmd->scan_begin_arg / NSEC_PER_USEC;
378	}
379
380	/*
381	 * Simulate first conversion to occur at convert period after
382	 * conversion timer starts.  If scan_begin_src is TRIG_FOLLOW, assume
383	 * the conversion timer starts immediately.  If scan_begin_src is
384	 * TRIG_TIMER, assume the conversion timer starts after the scan
385	 * period.
386	 */
387	first_convert_time = devpriv->ai_convert_period;
388	if (cmd->scan_begin_src == TRIG_TIMER)
389		first_convert_time += devpriv->ai_scan_period;
390	devpriv->ai_convert_time = ktime_to_us(ktime_get()) +
391				   first_convert_time;
392
393	/* Determine time within waveform period at time of conversion. */
394	wf_current = devpriv->ai_convert_time;
395	devpriv->wf_current = do_div(wf_current, devpriv->wf_period);
396
397	/*
398	 * Schedule timer to expire just after first conversion time.
399	 * Seem to need an extra jiffy here, otherwise timer expires slightly
400	 * early!
401	 */
402	spin_lock_bh(&dev->spinlock);
403	devpriv->ai_timer_enable = true;
404	devpriv->ai_timer.expires =
405		jiffies + usecs_to_jiffies(devpriv->ai_convert_period) + 1;
406	add_timer(&devpriv->ai_timer);
407	spin_unlock_bh(&dev->spinlock);
408	return 0;
409}
410
411static int waveform_ai_cancel(struct comedi_device *dev,
412			      struct comedi_subdevice *s)
413{
414	struct waveform_private *devpriv = dev->private;
415
416	spin_lock_bh(&dev->spinlock);
417	devpriv->ai_timer_enable = false;
418	spin_unlock_bh(&dev->spinlock);
419	if (in_softirq()) {
420		/* Assume we were called from the timer routine itself. */
421		del_timer(&devpriv->ai_timer);
422	} else {
423		del_timer_sync(&devpriv->ai_timer);
424	}
425	return 0;
426}
427
428static int waveform_ai_insn_read(struct comedi_device *dev,
429				 struct comedi_subdevice *s,
430				 struct comedi_insn *insn, unsigned int *data)
431{
432	struct waveform_private *devpriv = dev->private;
433	int i, chan = CR_CHAN(insn->chanspec);
434
435	for (i = 0; i < insn->n; i++)
436		data[i] = devpriv->ao_loopbacks[chan];
437
438	return insn->n;
439}
440
441/*
442 * This is the background routine to handle AO commands, scheduled by
443 * a timer mechanism.
444 */
445static void waveform_ao_timer(struct timer_list *t)
446{
447	struct waveform_private *devpriv = from_timer(devpriv, t, ao_timer);
448	struct comedi_device *dev = devpriv->dev;
449	struct comedi_subdevice *s = dev->write_subdev;
450	struct comedi_async *async = s->async;
451	struct comedi_cmd *cmd = &async->cmd;
452	u64 now;
453	u64 scans_since;
454	unsigned int scans_avail = 0;
455
456	/* determine number of scan periods since last time */
457	now = ktime_to_us(ktime_get());
458	scans_since = now - devpriv->ao_last_scan_time;
459	do_div(scans_since, devpriv->ao_scan_period);
460	if (scans_since) {
461		unsigned int i;
462
463		/* determine scans in buffer, limit to scans to do this time */
464		scans_avail = comedi_nscans_left(s, 0);
465		if (scans_avail > scans_since)
466			scans_avail = scans_since;
467		if (scans_avail) {
468			/* skip all but the last scan to save processing time */
469			if (scans_avail > 1) {
470				unsigned int skip_bytes, nbytes;
471
472				skip_bytes =
473				comedi_samples_to_bytes(s, cmd->scan_end_arg *
474							   (scans_avail - 1));
475				nbytes = comedi_buf_read_alloc(s, skip_bytes);
476				comedi_buf_read_free(s, nbytes);
477				comedi_inc_scan_progress(s, nbytes);
478				if (nbytes < skip_bytes) {
479					/* unexpected underrun! (cancelled?) */
480					async->events |= COMEDI_CB_OVERFLOW;
481					goto underrun;
482				}
483			}
484			/* output the last scan */
485			for (i = 0; i < cmd->scan_end_arg; i++) {
486				unsigned int chan = CR_CHAN(cmd->chanlist[i]);
487				unsigned short *pd;
488
489				pd = &devpriv->ao_loopbacks[chan];
490
491				if (!comedi_buf_read_samples(s, pd, 1)) {
492					/* unexpected underrun! (cancelled?) */
493					async->events |= COMEDI_CB_OVERFLOW;
494					goto underrun;
495				}
496			}
497			/* advance time of last scan */
498			devpriv->ao_last_scan_time +=
499				(u64)scans_avail * devpriv->ao_scan_period;
500		}
501	}
502	if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg) {
503		async->events |= COMEDI_CB_EOA;
504	} else if (scans_avail < scans_since) {
505		async->events |= COMEDI_CB_OVERFLOW;
506	} else {
507		unsigned int time_inc = devpriv->ao_last_scan_time +
508					devpriv->ao_scan_period - now;
509
510		spin_lock(&dev->spinlock);
511		if (devpriv->ao_timer_enable) {
512			mod_timer(&devpriv->ao_timer,
513				  jiffies + usecs_to_jiffies(time_inc));
514		}
515		spin_unlock(&dev->spinlock);
516	}
517
518underrun:
519	comedi_handle_events(dev, s);
520}
521
522static int waveform_ao_inttrig_start(struct comedi_device *dev,
523				     struct comedi_subdevice *s,
524				     unsigned int trig_num)
525{
526	struct waveform_private *devpriv = dev->private;
527	struct comedi_async *async = s->async;
528	struct comedi_cmd *cmd = &async->cmd;
529
530	if (trig_num != cmd->start_arg)
531		return -EINVAL;
532
533	async->inttrig = NULL;
534
535	devpriv->ao_last_scan_time = ktime_to_us(ktime_get());
536	spin_lock_bh(&dev->spinlock);
537	devpriv->ao_timer_enable = true;
538	devpriv->ao_timer.expires =
539		jiffies + usecs_to_jiffies(devpriv->ao_scan_period);
540	add_timer(&devpriv->ao_timer);
541	spin_unlock_bh(&dev->spinlock);
542
543	return 1;
544}
545
546static int waveform_ao_cmdtest(struct comedi_device *dev,
547			       struct comedi_subdevice *s,
548			       struct comedi_cmd *cmd)
549{
550	int err = 0;
551	unsigned int arg;
552
553	/* Step 1 : check if triggers are trivially valid */
554
555	err |= comedi_check_trigger_src(&cmd->start_src, TRIG_INT);
556	err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
557	err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
558	err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
559	err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
560
561	if (err)
562		return 1;
563
564	/* Step 2a : make sure trigger sources are unique */
565
566	err |= comedi_check_trigger_is_unique(cmd->stop_src);
567
568	/* Step 2b : and mutually compatible */
569
570	if (err)
571		return 2;
572
573	/* Step 3: check if arguments are trivially valid */
574
575	err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
576					    NSEC_PER_USEC);
577	err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
578	err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
579	err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
580					   cmd->chanlist_len);
581	if (cmd->stop_src == TRIG_COUNT)
582		err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
583	else	/* cmd->stop_src == TRIG_NONE */
584		err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
585
586	if (err)
587		return 3;
588
589	/* step 4: fix up any arguments */
590
591	/* round scan_begin_arg to nearest microsecond */
592	arg = cmd->scan_begin_arg;
593	arg = min(arg, rounddown(UINT_MAX, (unsigned int)NSEC_PER_USEC));
594	arg = NSEC_PER_USEC * DIV_ROUND_CLOSEST(arg, NSEC_PER_USEC);
595	err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
596
597	if (err)
598		return 4;
599
600	return 0;
601}
602
603static int waveform_ao_cmd(struct comedi_device *dev,
604			   struct comedi_subdevice *s)
605{
606	struct waveform_private *devpriv = dev->private;
607	struct comedi_cmd *cmd = &s->async->cmd;
608
609	if (cmd->flags & CMDF_PRIORITY) {
610		dev_err(dev->class_dev,
611			"commands at RT priority not supported in this driver\n");
612		return -1;
613	}
614
615	devpriv->ao_scan_period = cmd->scan_begin_arg / NSEC_PER_USEC;
616	s->async->inttrig = waveform_ao_inttrig_start;
617	return 0;
618}
619
620static int waveform_ao_cancel(struct comedi_device *dev,
621			      struct comedi_subdevice *s)
622{
623	struct waveform_private *devpriv = dev->private;
624
625	s->async->inttrig = NULL;
626	spin_lock_bh(&dev->spinlock);
627	devpriv->ao_timer_enable = false;
628	spin_unlock_bh(&dev->spinlock);
629	if (in_softirq()) {
630		/* Assume we were called from the timer routine itself. */
631		del_timer(&devpriv->ao_timer);
632	} else {
633		del_timer_sync(&devpriv->ao_timer);
634	}
635	return 0;
636}
637
638static int waveform_ao_insn_write(struct comedi_device *dev,
639				  struct comedi_subdevice *s,
640				  struct comedi_insn *insn, unsigned int *data)
641{
642	struct waveform_private *devpriv = dev->private;
643	int i, chan = CR_CHAN(insn->chanspec);
644
645	for (i = 0; i < insn->n; i++)
646		devpriv->ao_loopbacks[chan] = data[i];
647
648	return insn->n;
649}
650
651static int waveform_ai_insn_config(struct comedi_device *dev,
652				   struct comedi_subdevice *s,
653				   struct comedi_insn *insn,
654				   unsigned int *data)
655{
656	if (data[0] == INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS) {
657		/*
658		 * input:  data[1], data[2] : scan_begin_src, convert_src
659		 * output: data[1], data[2] : scan_begin_min, convert_min
660		 */
661		if (data[1] == TRIG_FOLLOW) {
662			/* exactly TRIG_FOLLOW case */
663			data[1] = 0;
664			data[2] = NSEC_PER_USEC;
665		} else {
666			data[1] = NSEC_PER_USEC;
667			if (data[2] & TRIG_TIMER)
668				data[2] = NSEC_PER_USEC;
669			else
670				data[2] = 0;
671		}
672		return 0;
673	}
674
675	return -EINVAL;
676}
677
678static int waveform_ao_insn_config(struct comedi_device *dev,
679				   struct comedi_subdevice *s,
680				   struct comedi_insn *insn,
681				   unsigned int *data)
682{
683	if (data[0] == INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS) {
684		/* we don't care about actual channels */
685		data[1] = NSEC_PER_USEC; /* scan_begin_min */
686		data[2] = 0;		 /* convert_min */
687		return 0;
688	}
689
690	return -EINVAL;
691}
692
693static int waveform_common_attach(struct comedi_device *dev,
694				  int amplitude, int period)
695{
696	struct waveform_private *devpriv;
697	struct comedi_subdevice *s;
698	int i;
699	int ret;
700
701	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
702	if (!devpriv)
703		return -ENOMEM;
704
705	devpriv->wf_amplitude = amplitude;
706	devpriv->wf_period = period;
707
708	ret = comedi_alloc_subdevices(dev, 2);
709	if (ret)
710		return ret;
711
712	s = &dev->subdevices[0];
713	dev->read_subdev = s;
714	/* analog input subdevice */
715	s->type = COMEDI_SUBD_AI;
716	s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
717	s->n_chan = N_CHANS;
718	s->maxdata = 0xffff;
719	s->range_table = &waveform_ai_ranges;
720	s->len_chanlist = s->n_chan * 2;
721	s->insn_read = waveform_ai_insn_read;
722	s->do_cmd = waveform_ai_cmd;
723	s->do_cmdtest = waveform_ai_cmdtest;
724	s->cancel = waveform_ai_cancel;
725	s->insn_config = waveform_ai_insn_config;
726
727	s = &dev->subdevices[1];
728	dev->write_subdev = s;
729	/* analog output subdevice (loopback) */
730	s->type = COMEDI_SUBD_AO;
731	s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_CMD_WRITE;
732	s->n_chan = N_CHANS;
733	s->maxdata = 0xffff;
734	s->range_table = &waveform_ai_ranges;
735	s->len_chanlist = s->n_chan;
736	s->insn_write = waveform_ao_insn_write;
737	s->insn_read = waveform_ai_insn_read;	/* do same as AI insn_read */
738	s->do_cmd = waveform_ao_cmd;
739	s->do_cmdtest = waveform_ao_cmdtest;
740	s->cancel = waveform_ao_cancel;
741	s->insn_config = waveform_ao_insn_config;
742
743	/* Our default loopback value is just a 0V flatline */
744	for (i = 0; i < s->n_chan; i++)
745		devpriv->ao_loopbacks[i] = s->maxdata / 2;
746
747	devpriv->dev = dev;
748	timer_setup(&devpriv->ai_timer, waveform_ai_timer, 0);
749	timer_setup(&devpriv->ao_timer, waveform_ao_timer, 0);
750
751	dev_info(dev->class_dev,
752		 "%s: %u microvolt, %u microsecond waveform attached\n",
753		 dev->board_name,
754		 devpriv->wf_amplitude, devpriv->wf_period);
755
756	return 0;
757}
758
759static int waveform_attach(struct comedi_device *dev,
760			   struct comedi_devconfig *it)
761{
762	int amplitude = it->options[0];
763	int period = it->options[1];
764
765	/* set default amplitude and period */
766	if (amplitude <= 0)
767		amplitude = 1000000;	/* 1 volt */
768	if (period <= 0)
769		period = 100000;	/* 0.1 sec */
770
771	return waveform_common_attach(dev, amplitude, period);
772}
773
774static int waveform_auto_attach(struct comedi_device *dev,
775				unsigned long context_unused)
776{
777	int amplitude = set_amplitude;
778	int period = set_period;
779
780	/* set default amplitude and period */
781	if (!amplitude)
782		amplitude = 1000000;	/* 1 volt */
783	if (!period)
784		period = 100000;	/* 0.1 sec */
785
786	return waveform_common_attach(dev, amplitude, period);
787}
788
789static void waveform_detach(struct comedi_device *dev)
790{
791	struct waveform_private *devpriv = dev->private;
792
793	if (devpriv) {
794		del_timer_sync(&devpriv->ai_timer);
795		del_timer_sync(&devpriv->ao_timer);
796	}
797}
798
799static struct comedi_driver waveform_driver = {
800	.driver_name	= "comedi_test",
801	.module		= THIS_MODULE,
802	.attach		= waveform_attach,
803	.auto_attach	= waveform_auto_attach,
804	.detach		= waveform_detach,
805};
806
807/*
808 * For auto-configuration, a device is created to stand in for a
809 * real hardware device.
810 */
811static int __init comedi_test_init(void)
812{
813	int ret;
814
815	ret = comedi_driver_register(&waveform_driver);
816	if (ret) {
817		pr_err("comedi_test: unable to register driver\n");
818		return ret;
819	}
820
821	if (!config_mode) {
822		ret = class_register(&ctcls);
823		if (ret) {
824			pr_warn("comedi_test: unable to create class\n");
825			goto clean3;
826		}
827
828		ctdev = device_create(&ctcls, NULL, MKDEV(0, 0), NULL, DEV_NAME);
829		if (IS_ERR(ctdev)) {
830			pr_warn("comedi_test: unable to create device\n");
831			goto clean2;
832		}
833
834		ret = comedi_auto_config(ctdev, &waveform_driver, 0);
835		if (ret) {
836			pr_warn("comedi_test: unable to auto-configure device\n");
837			goto clean;
838		}
839	}
840
841	return 0;
842
843clean:
844	device_destroy(&ctcls, MKDEV(0, 0));
845clean2:
846	class_unregister(&ctcls);
847clean3:
848	return 0;
849}
850module_init(comedi_test_init);
851
852static void __exit comedi_test_exit(void)
853{
854	if (ctdev)
855		comedi_auto_unconfig(ctdev);
856
857	if (class_is_registered(&ctcls)) {
858		device_destroy(&ctcls, MKDEV(0, 0));
859		class_unregister(&ctcls);
860	}
861
862	comedi_driver_unregister(&waveform_driver);
863}
864module_exit(comedi_test_exit);
865
866MODULE_AUTHOR("Comedi https://www.comedi.org");
867MODULE_DESCRIPTION("Comedi low-level driver");
868MODULE_LICENSE("GPL");
869