summaryrefslogtreecommitdiff
path: root/drivers/staging/comedi
diff options
context:
space:
mode:
authorStephen Rothwell <sfr@canb.auug.org.au>2008-11-18 16:27:14 +1100
committerStephen Rothwell <sfr@canb.auug.org.au>2008-11-18 16:27:14 +1100
commitca491540d35e0d924f08b8363b354c34c9de4be5 (patch)
tree22d40fb676b316a4bdab2771ed9bee28e065de68 /drivers/staging/comedi
parent67d3bcee8cf5f5a1cd6c923ba25554c37582ca62 (diff)
parentb6dd71f343743bbcb4ad06303a57b79ef7264932 (diff)
Merge branch 'quilt/staging'
Diffstat (limited to 'drivers/staging/comedi')
-rw-r--r--drivers/staging/comedi/Kconfig27
-rw-r--r--drivers/staging/comedi/Makefile17
-rw-r--r--drivers/staging/comedi/TODO14
-rw-r--r--drivers/staging/comedi/comedi.h916
-rw-r--r--drivers/staging/comedi/comedi_compat32.c597
-rw-r--r--drivers/staging/comedi/comedi_compat32.h58
-rw-r--r--drivers/staging/comedi/comedi_fops.c2244
-rw-r--r--drivers/staging/comedi/comedi_fops.h8
-rw-r--r--drivers/staging/comedi/comedi_ksyms.c77
-rw-r--r--drivers/staging/comedi/comedi_rt.h150
-rw-r--r--drivers/staging/comedi/comedidev.h537
-rw-r--r--drivers/staging/comedi/comedilib.h192
-rw-r--r--drivers/staging/comedi/drivers.c846
-rw-r--r--drivers/staging/comedi/drivers/Makefile16
-rw-r--r--drivers/staging/comedi/drivers/comedi_bond.c522
-rw-r--r--drivers/staging/comedi/drivers/comedi_fc.c118
-rw-r--r--drivers/staging/comedi/drivers/comedi_fc.h75
-rw-r--r--drivers/staging/comedi/drivers/comedi_parport.c387
-rw-r--r--drivers/staging/comedi/drivers/comedi_pci.h89
-rw-r--r--drivers/staging/comedi/drivers/comedi_test.c526
-rw-r--r--drivers/staging/comedi/drivers/dt9812.c957
-rw-r--r--drivers/staging/comedi/drivers/dt9812.h176
-rw-r--r--drivers/staging/comedi/drivers/mite.c809
-rw-r--r--drivers/staging/comedi/drivers/mite.h453
-rw-r--r--drivers/staging/comedi/drivers/usbdux.c2981
-rw-r--r--drivers/staging/comedi/drivers/usbduxfast.c1779
-rw-r--r--drivers/staging/comedi/interrupt.h67
-rw-r--r--drivers/staging/comedi/kcomedilib/Makefile8
-rw-r--r--drivers/staging/comedi/kcomedilib/data.c89
-rw-r--r--drivers/staging/comedi/kcomedilib/dio.c95
-rw-r--r--drivers/staging/comedi/kcomedilib/get.c294
-rw-r--r--drivers/staging/comedi/kcomedilib/kcomedilib_main.c567
-rw-r--r--drivers/staging/comedi/kcomedilib/ksyms.c144
-rw-r--r--drivers/staging/comedi/pci.h31
-rw-r--r--drivers/staging/comedi/pci_ids.h31
-rw-r--r--drivers/staging/comedi/proc.c100
-rw-r--r--drivers/staging/comedi/range.c161
-rw-r--r--drivers/staging/comedi/rt.c412
-rw-r--r--drivers/staging/comedi/rt_pend_tq.c113
-rw-r--r--drivers/staging/comedi/rt_pend_tq.h10
-rw-r--r--drivers/staging/comedi/usb.h75
-rw-r--r--drivers/staging/comedi/wrapper.h25
42 files changed, 16793 insertions, 0 deletions
diff --git a/drivers/staging/comedi/Kconfig b/drivers/staging/comedi/Kconfig
new file mode 100644
index 000000000000..b501bfb9c754
--- /dev/null
+++ b/drivers/staging/comedi/Kconfig
@@ -0,0 +1,27 @@
+config COMEDI
+ tristate "Data Acquision support (comedi)"
+ default N
+ ---help---
+ Enable support a wide range of data acquision devices
+ for Linux.
+
+config COMEDI_RT
+ tristate "Comedi Real-time support"
+ depends on COMEDI && RT
+ default N
+ ---help---
+ Enable Real time support for the Comedi core.
+
+config COMEDI_PCI_DRIVERS
+ tristate "Comedi PCI drivers"
+ depends on COMEDI && PCI
+ default N
+ ---help---
+ Enable lots of comedi PCI drivers to be built
+
+config COMEDI_USB_DRIVERS
+ tristate "Comedi USB drivers"
+ depends on COMEDI && USB
+ default N
+ ---help---
+ Enable lots of comedi USB drivers to be built
diff --git a/drivers/staging/comedi/Makefile b/drivers/staging/comedi/Makefile
new file mode 100644
index 000000000000..afd1a19c1b87
--- /dev/null
+++ b/drivers/staging/comedi/Makefile
@@ -0,0 +1,17 @@
+obj-$(CONFIG_COMEDI) += comedi.o
+obj-$(CONFIG_COMEDI_RT) += comedi_rt.o
+
+obj-$(CONFIG_COMEDI) += kcomedilib/
+obj-$(CONFIG_COMEDI) += drivers/
+
+comedi-objs := \
+ comedi_fops.o \
+ proc.o \
+ range.o \
+ drivers.o \
+ comedi_compat32.o \
+ comedi_ksyms.o \
+
+comedi_rt-objs := \
+ rt_pend_tq.o \
+ rt.o
diff --git a/drivers/staging/comedi/TODO b/drivers/staging/comedi/TODO
new file mode 100644
index 000000000000..557812958464
--- /dev/null
+++ b/drivers/staging/comedi/TODO
@@ -0,0 +1,14 @@
+TODO:
+ - checkpatch.pl cleanups
+ - Lindent
+ - remove all wrappers
+ - remove typedefs
+ - audit userspace interface
+ - reserve major number
+ - cleanup the individual comedi drivers as well
+
+Please send patches to Greg Kroah-Hartman <greg@kroah.com> and
+copy:
+ Ian Abbott <abbotti@mev.co.uk>
+ Frank Mori Hess <fmhess@users.sourceforge.net>
+ David Schleef <ds@schleef.org>
diff --git a/drivers/staging/comedi/comedi.h b/drivers/staging/comedi/comedi.h
new file mode 100644
index 000000000000..36d2e1b01e78
--- /dev/null
+++ b/drivers/staging/comedi/comedi.h
@@ -0,0 +1,916 @@
+/*
+ include/comedi.h (installed as /usr/include/comedi.h)
+ header file for comedi
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998-2001 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 Lesser 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef _COMEDI_H
+#define _COMEDI_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define COMEDI_MAJORVERSION 0
+#define COMEDI_MINORVERSION 7
+#define COMEDI_MICROVERSION 76
+#define VERSION "0.7.76"
+
+/* comedi's major device number */
+#define COMEDI_MAJOR 98
+
+/*
+ maximum number of minor devices. This can be increased, although
+ kernel structures are currently statically allocated, thus you
+ don't want this to be much more than you actually use.
+ */
+#define COMEDI_NDEVICES 16
+
+/* number of config options in the config structure */
+#define COMEDI_NDEVCONFOPTS 32
+/*length of nth chunk of firmware data*/
+#define COMEDI_DEVCONF_AUX_DATA3_LENGTH 25
+#define COMEDI_DEVCONF_AUX_DATA2_LENGTH 26
+#define COMEDI_DEVCONF_AUX_DATA1_LENGTH 27
+#define COMEDI_DEVCONF_AUX_DATA0_LENGTH 28
+#define COMEDI_DEVCONF_AUX_DATA_HI 29 /* most significant 32 bits of pointer address (if needed) */
+#define COMEDI_DEVCONF_AUX_DATA_LO 30 /* least significant 32 bits of pointer address */
+#define COMEDI_DEVCONF_AUX_DATA_LENGTH 31 /* total data length */
+
+/* max length of device and driver names */
+#define COMEDI_NAMELEN 20
+
+ typedef unsigned int lsampl_t;
+ typedef unsigned short sampl_t;
+
+/* packs and unpacks a channel/range number */
+
+#define CR_PACK(chan, rng, aref) ((((aref)&0x3)<<24) | (((rng)&0xff)<<16) | (chan))
+#define CR_PACK_FLAGS(chan, range, aref, flags) (CR_PACK(chan, range, aref) | ((flags) & CR_FLAGS_MASK))
+
+#define CR_CHAN(a) ((a)&0xffff)
+#define CR_RANGE(a) (((a)>>16)&0xff)
+#define CR_AREF(a) (((a)>>24)&0x03)
+
+#define CR_FLAGS_MASK 0xfc000000
+#define CR_ALT_FILTER (1<<26)
+#define CR_DITHER CR_ALT_FILTER
+#define CR_DEGLITCH CR_ALT_FILTER
+#define CR_ALT_SOURCE (1<<27)
+#define CR_EDGE (1<<30)
+#define CR_INVERT (1<<31)
+
+#define AREF_GROUND 0x00 /* analog ref = analog ground */
+#define AREF_COMMON 0x01 /* analog ref = analog common */
+#define AREF_DIFF 0x02 /* analog ref = differential */
+#define AREF_OTHER 0x03 /* analog ref = other (undefined) */
+
+/* counters -- these are arbitrary values */
+#define GPCT_RESET 0x0001
+#define GPCT_SET_SOURCE 0x0002
+#define GPCT_SET_GATE 0x0004
+#define GPCT_SET_DIRECTION 0x0008
+#define GPCT_SET_OPERATION 0x0010
+#define GPCT_ARM 0x0020
+#define GPCT_DISARM 0x0040
+#define GPCT_GET_INT_CLK_FRQ 0x0080
+
+#define GPCT_INT_CLOCK 0x0001
+#define GPCT_EXT_PIN 0x0002
+#define GPCT_NO_GATE 0x0004
+#define GPCT_UP 0x0008
+#define GPCT_DOWN 0x0010
+#define GPCT_HWUD 0x0020
+#define GPCT_SIMPLE_EVENT 0x0040
+#define GPCT_SINGLE_PERIOD 0x0080
+#define GPCT_SINGLE_PW 0x0100
+#define GPCT_CONT_PULSE_OUT 0x0200
+#define GPCT_SINGLE_PULSE_OUT 0x0400
+
+/* instructions */
+
+#define INSN_MASK_WRITE 0x8000000
+#define INSN_MASK_READ 0x4000000
+#define INSN_MASK_SPECIAL 0x2000000
+
+#define INSN_READ (0 | INSN_MASK_READ)
+#define INSN_WRITE (1 | INSN_MASK_WRITE)
+#define INSN_BITS (2 | INSN_MASK_READ|INSN_MASK_WRITE)
+#define INSN_CONFIG (3 | INSN_MASK_READ|INSN_MASK_WRITE)
+#define INSN_GTOD (4 | INSN_MASK_READ|INSN_MASK_SPECIAL)
+#define INSN_WAIT (5 | INSN_MASK_WRITE|INSN_MASK_SPECIAL)
+#define INSN_INTTRIG (6 | INSN_MASK_WRITE|INSN_MASK_SPECIAL)
+
+/* trigger flags */
+/* These flags are used in comedi_trig structures */
+
+#define TRIG_BOGUS 0x0001 /* do the motions */
+#define TRIG_DITHER 0x0002 /* enable dithering */
+#define TRIG_DEGLITCH 0x0004 /* enable deglitching */
+/*#define TRIG_RT 0x0008 */ /* perform op in real time */
+#define TRIG_CONFIG 0x0010 /* perform configuration, not triggering */
+#define TRIG_WAKE_EOS 0x0020 /* wake up on end-of-scan events */
+/*#define TRIG_WRITE 0x0040*/ /* write to bidirectional devices */
+
+/* command flags */
+/* These flags are used in comedi_cmd structures */
+
+#define CMDF_PRIORITY 0x00000008 /* try to use a real-time interrupt while performing command */
+
+#define TRIG_RT CMDF_PRIORITY /* compatibility definition */
+
+#define CMDF_WRITE 0x00000040
+#define TRIG_WRITE CMDF_WRITE /* compatibility definition */
+
+#define CMDF_RAWDATA 0x00000080
+
+#define COMEDI_EV_START 0x00040000
+#define COMEDI_EV_SCAN_BEGIN 0x00080000
+#define COMEDI_EV_CONVERT 0x00100000
+#define COMEDI_EV_SCAN_END 0x00200000
+#define COMEDI_EV_STOP 0x00400000
+
+#define TRIG_ROUND_MASK 0x00030000
+#define TRIG_ROUND_NEAREST 0x00000000
+#define TRIG_ROUND_DOWN 0x00010000
+#define TRIG_ROUND_UP 0x00020000
+#define TRIG_ROUND_UP_NEXT 0x00030000
+
+/* trigger sources */
+
+#define TRIG_ANY 0xffffffff
+#define TRIG_INVALID 0x00000000
+
+#define TRIG_NONE 0x00000001 /* never trigger */
+#define TRIG_NOW 0x00000002 /* trigger now + N ns */
+#define TRIG_FOLLOW 0x00000004 /* trigger on next lower level trig */
+#define TRIG_TIME 0x00000008 /* trigger at time N ns */
+#define TRIG_TIMER 0x00000010 /* trigger at rate N ns */
+#define TRIG_COUNT 0x00000020 /* trigger when count reaches N */
+#define TRIG_EXT 0x00000040 /* trigger on external signal N */
+#define TRIG_INT 0x00000080 /* trigger on comedi-internal signal N */
+#define TRIG_OTHER 0x00000100 /* driver defined */
+
+/* subdevice flags */
+
+#define SDF_BUSY 0x0001 /* device is busy */
+#define SDF_BUSY_OWNER 0x0002 /* device is busy with your job */
+#define SDF_LOCKED 0x0004 /* subdevice is locked */
+#define SDF_LOCK_OWNER 0x0008 /* you own lock */
+#define SDF_MAXDATA 0x0010 /* maxdata depends on channel */
+#define SDF_FLAGS 0x0020 /* flags depend on channel */
+#define SDF_RANGETYPE 0x0040 /* range type depends on channel */
+#define SDF_MODE0 0x0080 /* can do mode 0 */
+#define SDF_MODE1 0x0100 /* can do mode 1 */
+#define SDF_MODE2 0x0200 /* can do mode 2 */
+#define SDF_MODE3 0x0400 /* can do mode 3 */
+#define SDF_MODE4 0x0800 /* can do mode 4 */
+#define SDF_CMD 0x1000 /* can do commands (deprecated) */
+#define SDF_SOFT_CALIBRATED 0x2000 /* subdevice uses software calibration */
+#define SDF_CMD_WRITE 0x4000 /* can do output commands */
+#define SDF_CMD_READ 0x8000 /* can do input commands */
+
+#define SDF_READABLE 0x00010000 /* subdevice can be read (e.g. analog input) */
+#define SDF_WRITABLE 0x00020000 /* subdevice can be written (e.g. analog output) */
+#define SDF_WRITEABLE SDF_WRITABLE /* spelling error in API */
+#define SDF_INTERNAL 0x00040000 /* subdevice does not have externally visible lines */
+#define SDF_RT 0x00080000 /* DEPRECATED: subdevice is RT capable */
+#define SDF_GROUND 0x00100000 /* can do aref=ground */
+#define SDF_COMMON 0x00200000 /* can do aref=common */
+#define SDF_DIFF 0x00400000 /* can do aref=diff */
+#define SDF_OTHER 0x00800000 /* can do aref=other */
+#define SDF_DITHER 0x01000000 /* can do dithering */
+#define SDF_DEGLITCH 0x02000000 /* can do deglitching */
+#define SDF_MMAP 0x04000000 /* can do mmap() */
+#define SDF_RUNNING 0x08000000 /* subdevice is acquiring data */
+#define SDF_LSAMPL 0x10000000 /* subdevice uses 32-bit samples */
+#define SDF_PACKED 0x20000000 /* subdevice can do packed DIO */
+/* re recyle these flags for PWM */
+#define SDF_PWM_COUNTER SDF_MODE0 /* PWM can automatically switch off */
+#define SDF_PWM_HBRIDGE SDF_MODE1 /* PWM is signed (H-bridge) */
+
+
+
+/* subdevice types */
+
+enum comedi_subdevice_type {
+ COMEDI_SUBD_UNUSED, /* unused by driver */
+ COMEDI_SUBD_AI, /* analog input */
+ COMEDI_SUBD_AO, /* analog output */
+ COMEDI_SUBD_DI, /* digital input */
+ COMEDI_SUBD_DO, /* digital output */
+ COMEDI_SUBD_DIO, /* digital input/output */
+ COMEDI_SUBD_COUNTER, /* counter */
+ COMEDI_SUBD_TIMER, /* timer */
+ COMEDI_SUBD_MEMORY, /* memory, EEPROM, DPRAM */
+ COMEDI_SUBD_CALIB, /* calibration DACs */
+ COMEDI_SUBD_PROC, /* processor, DSP */
+ COMEDI_SUBD_SERIAL, /* serial IO */
+ COMEDI_SUBD_PWM /* PWM */
+};
+
+/* configuration instructions */
+
+enum configuration_ids {
+ INSN_CONFIG_DIO_INPUT = 0,
+ INSN_CONFIG_DIO_OUTPUT = 1,
+ INSN_CONFIG_DIO_OPENDRAIN = 2,
+ INSN_CONFIG_ANALOG_TRIG = 16,
+/* INSN_CONFIG_WAVEFORM = 17, */
+/* INSN_CONFIG_TRIG = 18, */
+/* INSN_CONFIG_COUNTER = 19, */
+ INSN_CONFIG_ALT_SOURCE = 20,
+ INSN_CONFIG_DIGITAL_TRIG = 21,
+ INSN_CONFIG_BLOCK_SIZE = 22,
+ INSN_CONFIG_TIMER_1 = 23,
+ INSN_CONFIG_FILTER = 24,
+ INSN_CONFIG_CHANGE_NOTIFY = 25,
+
+ /*ALPHA*/ INSN_CONFIG_SERIAL_CLOCK = 26,
+ INSN_CONFIG_BIDIRECTIONAL_DATA = 27,
+ INSN_CONFIG_DIO_QUERY = 28,
+ INSN_CONFIG_PWM_OUTPUT = 29,
+ INSN_CONFIG_GET_PWM_OUTPUT = 30,
+ INSN_CONFIG_ARM = 31,
+ INSN_CONFIG_DISARM = 32,
+ INSN_CONFIG_GET_COUNTER_STATUS = 33,
+ INSN_CONFIG_RESET = 34,
+ INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR = 1001, /* Use CTR as single pulsegenerator */
+ INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR = 1002, /* Use CTR as pulsetraingenerator */
+ INSN_CONFIG_GPCT_QUADRATURE_ENCODER = 1003, /* Use the counter as encoder */
+ INSN_CONFIG_SET_GATE_SRC = 2001, /* Set gate source */
+ INSN_CONFIG_GET_GATE_SRC = 2002, /* Get gate source */
+ INSN_CONFIG_SET_CLOCK_SRC = 2003, /* Set master clock source */
+ INSN_CONFIG_GET_CLOCK_SRC = 2004, /* Get master clock source */
+ INSN_CONFIG_SET_OTHER_SRC = 2005, /* Set other source */
+/* INSN_CONFIG_GET_OTHER_SRC = 2006,*/ /* Get other source */
+ INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE, /* Get size in bytes of
+ subdevice's on-board fifos
+ used during streaming
+ input/output */
+ INSN_CONFIG_SET_COUNTER_MODE = 4097,
+ INSN_CONFIG_8254_SET_MODE = INSN_CONFIG_SET_COUNTER_MODE, /* deprecated */
+ INSN_CONFIG_8254_READ_STATUS = 4098,
+ INSN_CONFIG_SET_ROUTING = 4099,
+ INSN_CONFIG_GET_ROUTING = 4109,
+/* PWM */
+ INSN_CONFIG_PWM_SET_PERIOD = 5000, /* sets frequency */
+ INSN_CONFIG_PWM_GET_PERIOD = 5001, /* gets frequency */
+ INSN_CONFIG_GET_PWM_STATUS = 5002, /* is it running? */
+ INSN_CONFIG_PWM_SET_H_BRIDGE = 5003, /* sets H bridge: duty cycle and sign bit for a relay at the same time*/
+ INSN_CONFIG_PWM_GET_H_BRIDGE = 5004 /* gets H bridge data: duty cycle and the sign bit */
+};
+
+enum comedi_io_direction {
+ COMEDI_INPUT = 0,
+ COMEDI_OUTPUT = 1,
+ COMEDI_OPENDRAIN = 2
+};
+
+enum comedi_support_level {
+ COMEDI_UNKNOWN_SUPPORT = 0,
+ COMEDI_SUPPORTED,
+ COMEDI_UNSUPPORTED
+};
+
+/* ioctls */
+
+#define CIO 'd'
+#define COMEDI_DEVCONFIG _IOW(CIO, 0, comedi_devconfig)
+#define COMEDI_DEVINFO _IOR(CIO, 1, comedi_devinfo)
+#define COMEDI_SUBDINFO _IOR(CIO, 2, comedi_subdinfo)
+#define COMEDI_CHANINFO _IOR(CIO, 3, comedi_chaninfo)
+#define COMEDI_TRIG _IOWR(CIO, 4, comedi_trig)
+#define COMEDI_LOCK _IO(CIO, 5)
+#define COMEDI_UNLOCK _IO(CIO, 6)
+#define COMEDI_CANCEL _IO(CIO, 7)
+#define COMEDI_RANGEINFO _IOR(CIO, 8, comedi_rangeinfo)
+#define COMEDI_CMD _IOR(CIO, 9, comedi_cmd)
+#define COMEDI_CMDTEST _IOR(CIO, 10, comedi_cmd)
+#define COMEDI_INSNLIST _IOR(CIO, 11, comedi_insnlist)
+#define COMEDI_INSN _IOR(CIO, 12, comedi_insn)
+#define COMEDI_BUFCONFIG _IOR(CIO, 13, comedi_bufconfig)
+#define COMEDI_BUFINFO _IOWR(CIO, 14, comedi_bufinfo)
+#define COMEDI_POLL _IO(CIO, 15)
+
+/* structures */
+
+typedef struct comedi_trig_struct comedi_trig;
+typedef struct comedi_cmd_struct comedi_cmd;
+typedef struct comedi_insn_struct comedi_insn;
+typedef struct comedi_insnlist_struct comedi_insnlist;
+typedef struct comedi_chaninfo_struct comedi_chaninfo;
+typedef struct comedi_subdinfo_struct comedi_subdinfo;
+typedef struct comedi_devinfo_struct comedi_devinfo;
+typedef struct comedi_devconfig_struct comedi_devconfig;
+typedef struct comedi_rangeinfo_struct comedi_rangeinfo;
+typedef struct comedi_krange_struct comedi_krange;
+typedef struct comedi_bufconfig_struct comedi_bufconfig;
+typedef struct comedi_bufinfo_struct comedi_bufinfo;
+
+struct comedi_trig_struct {
+ unsigned int subdev; /* subdevice */
+ unsigned int mode; /* mode */
+ unsigned int flags;
+ unsigned int n_chan; /* number of channels */
+ unsigned int *chanlist; /* channel/range list */
+ sampl_t *data; /* data list, size depends on subd flags */
+ unsigned int n; /* number of scans */
+ unsigned int trigsrc;
+ unsigned int trigvar;
+ unsigned int trigvar1;
+ unsigned int data_len;
+ unsigned int unused[3];
+};
+
+struct comedi_insn_struct {
+ unsigned int insn;
+ unsigned int n;
+ lsampl_t *data;
+ unsigned int subdev;
+ unsigned int chanspec;
+ unsigned int unused[3];
+};
+
+struct comedi_insnlist_struct {
+ unsigned int n_insns;
+ comedi_insn *insns;
+};
+
+struct comedi_cmd_struct {
+ unsigned int subdev;
+ unsigned int flags;
+
+ unsigned int start_src;
+ unsigned int start_arg;
+
+ unsigned int scan_begin_src;
+ unsigned int scan_begin_arg;
+
+ unsigned int convert_src;
+ unsigned int convert_arg;
+
+ unsigned int scan_end_src;
+ unsigned int scan_end_arg;
+
+ unsigned int stop_src;
+ unsigned int stop_arg;
+
+ unsigned int *chanlist; /* channel/range list */
+ unsigned int chanlist_len;
+
+ sampl_t *data; /* data list, size depends on subd flags */
+ unsigned int data_len;
+};
+
+struct comedi_chaninfo_struct {
+ unsigned int subdev;
+ lsampl_t *maxdata_list;
+ unsigned int *flaglist;
+ unsigned int *rangelist;
+ unsigned int unused[4];
+};
+
+struct comedi_rangeinfo_struct {
+ unsigned int range_type;
+ void *range_ptr;
+};
+
+struct comedi_krange_struct {
+ int min; /* fixed point, multiply by 1e-6 */
+ int max; /* fixed point, multiply by 1e-6 */
+ unsigned int flags;
+};
+
+
+struct comedi_subdinfo_struct {
+ unsigned int type;
+ unsigned int n_chan;
+ unsigned int subd_flags;
+ unsigned int timer_type;
+ unsigned int len_chanlist;
+ lsampl_t maxdata;
+ unsigned int flags; /* channel flags */
+ unsigned int range_type; /* lookup in kernel */
+ unsigned int settling_time_0;
+ unsigned insn_bits_support; /* see support_level enum for values*/
+ unsigned int unused[8];
+};
+
+struct comedi_devinfo_struct {
+ unsigned int version_code;
+ unsigned int n_subdevs;
+ char driver_name[COMEDI_NAMELEN];
+ char board_name[COMEDI_NAMELEN];
+ int read_subdevice;
+ int write_subdevice;
+ int unused[30];
+};
+
+struct comedi_devconfig_struct {
+ char board_name[COMEDI_NAMELEN];
+ int options[COMEDI_NDEVCONFOPTS];
+};
+
+struct comedi_bufconfig_struct {
+ unsigned int subdevice;
+ unsigned int flags;
+
+ unsigned int maximum_size;
+ unsigned int size;
+
+ unsigned int unused[4];
+};
+
+struct comedi_bufinfo_struct {
+ unsigned int subdevice;
+ unsigned int bytes_read;
+
+ unsigned int buf_write_ptr;
+ unsigned int buf_read_ptr;
+ unsigned int buf_write_count;
+ unsigned int buf_read_count;
+
+ unsigned int bytes_written;
+
+ unsigned int unused[4];
+};
+
+/* range stuff */
+
+#define __RANGE(a, b) ((((a)&0xffff)<<16)|((b)&0xffff))
+
+#define RANGE_OFFSET(a) (((a)>>16)&0xffff)
+#define RANGE_LENGTH(b) ((b)&0xffff)
+
+#define RF_UNIT(flags) ((flags)&0xff)
+#define RF_EXTERNAL (1<<8)
+
+#define UNIT_volt 0
+#define UNIT_mA 1
+#define UNIT_none 2
+
+#define COMEDI_MIN_SPEED ((unsigned int)0xffffffff)
+
+/* callback stuff */
+/* only relevant to kernel modules. */
+
+#define COMEDI_CB_EOS 1 /* end of scan */
+#define COMEDI_CB_EOA 2 /* end of acquisition */
+#define COMEDI_CB_BLOCK 4 /* DEPRECATED: convenient block size */
+#define COMEDI_CB_EOBUF 8 /* DEPRECATED: end of buffer */
+#define COMEDI_CB_ERROR 16 /* card error during acquisition */
+#define COMEDI_CB_OVERFLOW 32 /* buffer overflow/underflow */
+
+/**********************************************************/
+/* everything after this line is ALPHA */
+/**********************************************************/
+
+/*
+ 8254 specific configuration.
+
+ It supports two config commands:
+
+ 0 ID: INSN_CONFIG_SET_COUNTER_MODE
+ 1 8254 Mode
+ I8254_MODE0, I8254_MODE1, ..., I8254_MODE5
+ OR'ed with:
+ I8254_BCD, I8254_BINARY
+
+ 0 ID: INSN_CONFIG_8254_READ_STATUS
+ 1 <-- Status byte returned here.
+ B7 = Output
+ B6 = NULL Count
+ B5 - B0 Current mode.
+
+*/
+
+enum i8254_mode {
+ I8254_MODE0 = (0 << 1), /* Interrupt on terminal count */
+ I8254_MODE1 = (1 << 1), /* Hardware retriggerable one-shot */
+ I8254_MODE2 = (2 << 1), /* Rate generator */
+ I8254_MODE3 = (3 << 1), /* Square wave mode */
+ I8254_MODE4 = (4 << 1), /* Software triggered strobe */
+ I8254_MODE5 = (5 << 1), /* Hardware triggered strobe (retriggerable) */
+ I8254_BCD = 1, /* use binary-coded decimal instead of binary (pretty useless) */
+ I8254_BINARY = 0
+};
+
+static inline unsigned NI_USUAL_PFI_SELECT(unsigned pfi_channel)
+{
+ if (pfi_channel < 10)
+ return 0x1 + pfi_channel;
+ else
+ return 0xb + pfi_channel;
+}
+static inline unsigned NI_USUAL_RTSI_SELECT(unsigned rtsi_channel)
+{
+ if (rtsi_channel < 7)
+ return 0xb + rtsi_channel;
+ else
+ return 0x1b;
+}
+/* mode bits for NI general-purpose counters, set with
+ * INSN_CONFIG_SET_COUNTER_MODE */
+#define NI_GPCT_COUNTING_MODE_SHIFT 16
+#define NI_GPCT_INDEX_PHASE_BITSHIFT 20
+#define NI_GPCT_COUNTING_DIRECTION_SHIFT 24
+enum ni_gpct_mode_bits {
+ NI_GPCT_GATE_ON_BOTH_EDGES_BIT = 0x4,
+ NI_GPCT_EDGE_GATE_MODE_MASK = 0x18,
+ NI_GPCT_EDGE_GATE_STARTS_STOPS_BITS = 0x0,
+ NI_GPCT_EDGE_GATE_STOPS_STARTS_BITS = 0x8,
+ NI_GPCT_EDGE_GATE_STARTS_BITS = 0x10,
+ NI_GPCT_EDGE_GATE_NO_STARTS_NO_STOPS_BITS = 0x18,
+ NI_GPCT_STOP_MODE_MASK = 0x60,
+ NI_GPCT_STOP_ON_GATE_BITS = 0x00,
+ NI_GPCT_STOP_ON_GATE_OR_TC_BITS = 0x20,
+ NI_GPCT_STOP_ON_GATE_OR_SECOND_TC_BITS = 0x40,
+ NI_GPCT_LOAD_B_SELECT_BIT = 0x80,
+ NI_GPCT_OUTPUT_MODE_MASK = 0x300,
+ NI_GPCT_OUTPUT_TC_PULSE_BITS = 0x100,
+ NI_GPCT_OUTPUT_TC_TOGGLE_BITS = 0x200,
+ NI_GPCT_OUTPUT_TC_OR_GATE_TOGGLE_BITS = 0x300,
+ NI_GPCT_HARDWARE_DISARM_MASK = 0xc00,
+ NI_GPCT_NO_HARDWARE_DISARM_BITS = 0x000,
+ NI_GPCT_DISARM_AT_TC_BITS = 0x400,
+ NI_GPCT_DISARM_AT_GATE_BITS = 0x800,
+ NI_GPCT_DISARM_AT_TC_OR_GATE_BITS = 0xc00,
+ NI_GPCT_LOADING_ON_TC_BIT = 0x1000,
+ NI_GPCT_LOADING_ON_GATE_BIT = 0x4000,
+ NI_GPCT_COUNTING_MODE_MASK = 0x7 << NI_GPCT_COUNTING_MODE_SHIFT,
+ NI_GPCT_COUNTING_MODE_NORMAL_BITS =
+ 0x0 << NI_GPCT_COUNTING_MODE_SHIFT,
+ NI_GPCT_COUNTING_MODE_QUADRATURE_X1_BITS =
+ 0x1 << NI_GPCT_COUNTING_MODE_SHIFT,
+ NI_GPCT_COUNTING_MODE_QUADRATURE_X2_BITS =
+ 0x2 << NI_GPCT_COUNTING_MODE_SHIFT,
+ NI_GPCT_COUNTING_MODE_QUADRATURE_X4_BITS =
+ 0x3 << NI_GPCT_COUNTING_MODE_SHIFT,
+ NI_GPCT_COUNTING_MODE_TWO_PULSE_BITS =
+ 0x4 << NI_GPCT_COUNTING_MODE_SHIFT,
+ NI_GPCT_COUNTING_MODE_SYNC_SOURCE_BITS =
+ 0x6 << NI_GPCT_COUNTING_MODE_SHIFT,
+ NI_GPCT_INDEX_PHASE_MASK = 0x3 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+ NI_GPCT_INDEX_PHASE_LOW_A_LOW_B_BITS =
+ 0x0 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+ NI_GPCT_INDEX_PHASE_LOW_A_HIGH_B_BITS =
+ 0x1 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+ NI_GPCT_INDEX_PHASE_HIGH_A_LOW_B_BITS =
+ 0x2 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+ NI_GPCT_INDEX_PHASE_HIGH_A_HIGH_B_BITS =
+ 0x3 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+ NI_GPCT_INDEX_ENABLE_BIT = 0x400000,
+ NI_GPCT_COUNTING_DIRECTION_MASK =
+ 0x3 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+ NI_GPCT_COUNTING_DIRECTION_DOWN_BITS =
+ 0x00 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+ NI_GPCT_COUNTING_DIRECTION_UP_BITS =
+ 0x1 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+ NI_GPCT_COUNTING_DIRECTION_HW_UP_DOWN_BITS =
+ 0x2 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+ NI_GPCT_COUNTING_DIRECTION_HW_GATE_BITS =
+ 0x3 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+ NI_GPCT_RELOAD_SOURCE_MASK = 0xc000000,
+ NI_GPCT_RELOAD_SOURCE_FIXED_BITS = 0x0,
+ NI_GPCT_RELOAD_SOURCE_SWITCHING_BITS = 0x4000000,
+ NI_GPCT_RELOAD_SOURCE_GATE_SELECT_BITS = 0x8000000,
+ NI_GPCT_OR_GATE_BIT = 0x10000000,
+ NI_GPCT_INVERT_OUTPUT_BIT = 0x20000000
+};
+
+/* Bits for setting a clock source with
+ * INSN_CONFIG_SET_CLOCK_SRC when using NI general-purpose counters. */
+enum ni_gpct_clock_source_bits {
+ NI_GPCT_CLOCK_SRC_SELECT_MASK = 0x3f,
+ NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS = 0x0,
+ NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS = 0x1,
+ NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS = 0x2,
+ NI_GPCT_LOGIC_LOW_CLOCK_SRC_BITS = 0x3,
+ NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS = 0x4,
+ NI_GPCT_NEXT_TC_CLOCK_SRC_BITS = 0x5,
+ NI_GPCT_SOURCE_PIN_i_CLOCK_SRC_BITS = 0x6, /* NI 660x-specific */
+ NI_GPCT_PXI10_CLOCK_SRC_BITS = 0x7,
+ NI_GPCT_PXI_STAR_TRIGGER_CLOCK_SRC_BITS = 0x8,
+ NI_GPCT_ANALOG_TRIGGER_OUT_CLOCK_SRC_BITS = 0x9,
+ NI_GPCT_PRESCALE_MODE_CLOCK_SRC_MASK = 0x30000000,
+ NI_GPCT_NO_PRESCALE_CLOCK_SRC_BITS = 0x0,
+ NI_GPCT_PRESCALE_X2_CLOCK_SRC_BITS = 0x10000000, /* divide source by 2 */
+ NI_GPCT_PRESCALE_X8_CLOCK_SRC_BITS = 0x20000000, /* divide source by 8 */
+ NI_GPCT_INVERT_CLOCK_SRC_BIT = 0x80000000
+};
+static inline unsigned NI_GPCT_SOURCE_PIN_CLOCK_SRC_BITS(unsigned n)
+{
+ /* NI 660x-specific */
+ return 0x10 + n;
+}
+static inline unsigned NI_GPCT_RTSI_CLOCK_SRC_BITS(unsigned n)
+{
+ return 0x18 + n;
+}
+static inline unsigned NI_GPCT_PFI_CLOCK_SRC_BITS(unsigned n)
+{
+ /* no pfi on NI 660x */
+ return 0x20 + n;
+}
+
+/* Possibilities for setting a gate source with
+INSN_CONFIG_SET_GATE_SRC when using NI general-purpose counters.
+May be bitwise-or'd with CR_EDGE or CR_INVERT. */
+enum ni_gpct_gate_select {
+ /* m-series gates */
+ NI_GPCT_TIMESTAMP_MUX_GATE_SELECT = 0x0,
+ NI_GPCT_AI_START2_GATE_SELECT = 0x12,
+ NI_GPCT_PXI_STAR_TRIGGER_GATE_SELECT = 0x13,
+ NI_GPCT_NEXT_OUT_GATE_SELECT = 0x14,
+ NI_GPCT_AI_START1_GATE_SELECT = 0x1c,
+ NI_GPCT_NEXT_SOURCE_GATE_SELECT = 0x1d,
+ NI_GPCT_ANALOG_TRIGGER_OUT_GATE_SELECT = 0x1e,
+ NI_GPCT_LOGIC_LOW_GATE_SELECT = 0x1f,
+ /* more gates for 660x */
+ NI_GPCT_SOURCE_PIN_i_GATE_SELECT = 0x100,
+ NI_GPCT_GATE_PIN_i_GATE_SELECT = 0x101,
+ /* more gates for 660x "second gate" */
+ NI_GPCT_UP_DOWN_PIN_i_GATE_SELECT = 0x201,
+ NI_GPCT_SELECTED_GATE_GATE_SELECT = 0x21e,
+ /* m-series "second gate" sources are unknown,
+ we should add them here with an offset of 0x300 when known. */
+ NI_GPCT_DISABLED_GATE_SELECT = 0x8000,
+};
+static inline unsigned NI_GPCT_GATE_PIN_GATE_SELECT(unsigned n)
+{
+ return 0x102 + n;
+}
+static inline unsigned NI_GPCT_RTSI_GATE_SELECT(unsigned n)
+{
+ return NI_USUAL_RTSI_SELECT(n);
+}
+static inline unsigned NI_GPCT_PFI_GATE_SELECT(unsigned n)
+{
+ return NI_USUAL_PFI_SELECT(n);
+}
+static inline unsigned NI_GPCT_UP_DOWN_PIN_GATE_SELECT(unsigned n)
+{
+ return 0x202 + n;
+}
+
+/* Possibilities for setting a source with
+INSN_CONFIG_SET_OTHER_SRC when using NI general-purpose counters. */
+enum ni_gpct_other_index {
+ NI_GPCT_SOURCE_ENCODER_A,
+ NI_GPCT_SOURCE_ENCODER_B,
+ NI_GPCT_SOURCE_ENCODER_Z
+};
+enum ni_gpct_other_select {
+ /* m-series gates */
+ /* Still unknown, probably only need NI_GPCT_PFI_OTHER_SELECT */
+ NI_GPCT_DISABLED_OTHER_SELECT = 0x8000,
+};
+static inline unsigned NI_GPCT_PFI_OTHER_SELECT(unsigned n)
+{
+ return NI_USUAL_PFI_SELECT(n);
+}
+
+/* start sources for ni general-purpose counters for use with
+INSN_CONFIG_ARM */
+enum ni_gpct_arm_source {
+ NI_GPCT_ARM_IMMEDIATE = 0x0,
+ NI_GPCT_ARM_PAIRED_IMMEDIATE = 0x1, /* Start both the counter and
+ the adjacent paired counter
+ simultaneously */
+ /* NI doesn't document bits for selecting hardware arm triggers. If
+ * the NI_GPCT_ARM_UNKNOWN bit is set, we will pass the least
+ * significant bits (3 bits for 660x or 5 bits for m-series) through to
+ * the hardware. This will at least allow someone to figure out what
+ * the bits do later. */
+ NI_GPCT_ARM_UNKNOWN = 0x1000,
+};
+
+/* digital filtering options for ni 660x for use with INSN_CONFIG_FILTER. */
+enum ni_gpct_filter_select {
+ NI_GPCT_FILTER_OFF = 0x0,
+ NI_GPCT_FILTER_TIMEBASE_3_SYNC = 0x1,
+ NI_GPCT_FILTER_100x_TIMEBASE_1 = 0x2,
+ NI_GPCT_FILTER_20x_TIMEBASE_1 = 0x3,
+ NI_GPCT_FILTER_10x_TIMEBASE_1 = 0x4,
+ NI_GPCT_FILTER_2x_TIMEBASE_1 = 0x5,
+ NI_GPCT_FILTER_2x_TIMEBASE_3 = 0x6
+};
+
+/* PFI digital filtering options for ni m-series for use with
+ * INSN_CONFIG_FILTER. */
+enum ni_pfi_filter_select {
+ NI_PFI_FILTER_OFF = 0x0,
+ NI_PFI_FILTER_125ns = 0x1,
+ NI_PFI_FILTER_6425ns = 0x2,
+ NI_PFI_FILTER_2550us = 0x3
+};
+
+/* master clock sources for ni mio boards and INSN_CONFIG_SET_CLOCK_SRC */
+enum ni_mio_clock_source {
+ NI_MIO_INTERNAL_CLOCK = 0,
+ NI_MIO_RTSI_CLOCK = 1, /* doesn't work for m-series, use
+ NI_MIO_PLL_RTSI_CLOCK() */
+ /* the NI_MIO_PLL_* sources are m-series only */
+ NI_MIO_PLL_PXI_STAR_TRIGGER_CLOCK = 2,
+ NI_MIO_PLL_PXI10_CLOCK = 3,
+ NI_MIO_PLL_RTSI0_CLOCK = 4
+};
+static inline unsigned NI_MIO_PLL_RTSI_CLOCK(unsigned rtsi_channel)
+{
+ return NI_MIO_PLL_RTSI0_CLOCK + rtsi_channel;
+}
+
+/* Signals which can be routed to an NI RTSI pin with INSN_CONFIG_SET_ROUTING.
+ The numbers assigned are not arbitrary, they correspond to the bits required
+ to program the board. */
+enum ni_rtsi_routing {
+ NI_RTSI_OUTPUT_ADR_START1 = 0,
+ NI_RTSI_OUTPUT_ADR_START2 = 1,
+ NI_RTSI_OUTPUT_SCLKG = 2,
+ NI_RTSI_OUTPUT_DACUPDN = 3,
+ NI_RTSI_OUTPUT_DA_START1 = 4,
+ NI_RTSI_OUTPUT_G_SRC0 = 5,
+ NI_RTSI_OUTPUT_G_GATE0 = 6,
+ NI_RTSI_OUTPUT_RGOUT0 = 7,
+ NI_RTSI_OUTPUT_RTSI_BRD_0 = 8,
+ NI_RTSI_OUTPUT_RTSI_OSC = 12 /* pre-m-series always have RTSI clock
+ on line 7 */
+};
+static inline unsigned NI_RTSI_OUTPUT_RTSI_BRD(unsigned n)
+{
+ return NI_RTSI_OUTPUT_RTSI_BRD_0 + n;
+}
+
+/* Signals which can be routed to an NI PFI pin on an m-series board with
+ * INSN_CONFIG_SET_ROUTING. These numbers are also returned by
+ * INSN_CONFIG_GET_ROUTING on pre-m-series boards, even though their routing
+ * cannot be changed. The numbers assigned are not arbitrary, they correspond
+ * to the bits required to program the board. */
+enum ni_pfi_routing {
+ NI_PFI_OUTPUT_PFI_DEFAULT = 0,
+ NI_PFI_OUTPUT_AI_START1 = 1,
+ NI_PFI_OUTPUT_AI_START2 = 2,
+ NI_PFI_OUTPUT_AI_CONVERT = 3,
+ NI_PFI_OUTPUT_G_SRC1 = 4,
+ NI_PFI_OUTPUT_G_GATE1 = 5,
+ NI_PFI_OUTPUT_AO_UPDATE_N = 6,
+ NI_PFI_OUTPUT_AO_START1 = 7,
+ NI_PFI_OUTPUT_AI_START_PULSE = 8,
+ NI_PFI_OUTPUT_G_SRC0 = 9,
+ NI_PFI_OUTPUT_G_GATE0 = 10,
+ NI_PFI_OUTPUT_EXT_STROBE = 11,
+ NI_PFI_OUTPUT_AI_EXT_MUX_CLK = 12,
+ NI_PFI_OUTPUT_GOUT0 = 13,
+ NI_PFI_OUTPUT_GOUT1 = 14,
+ NI_PFI_OUTPUT_FREQ_OUT = 15,
+ NI_PFI_OUTPUT_PFI_DO = 16,
+ NI_PFI_OUTPUT_I_ATRIG = 17,
+ NI_PFI_OUTPUT_RTSI0 = 18,
+ NI_PFI_OUTPUT_PXI_STAR_TRIGGER_IN = 26,
+ NI_PFI_OUTPUT_SCXI_TRIG1 = 27,
+ NI_PFI_OUTPUT_DIO_CHANGE_DETECT_RTSI = 28,
+ NI_PFI_OUTPUT_CDI_SAMPLE = 29,
+ NI_PFI_OUTPUT_CDO_UPDATE = 30
+};
+static inline unsigned NI_PFI_OUTPUT_RTSI(unsigned rtsi_channel)
+{
+ return NI_PFI_OUTPUT_RTSI0 + rtsi_channel;
+}
+
+/* Signals which can be routed to output on a NI PFI pin on a 660x board
+ with INSN_CONFIG_SET_ROUTING. The numbers assigned are
+ not arbitrary, they correspond to the bits required
+ to program the board. Lines 0 to 7 can only be set to
+ NI_660X_PFI_OUTPUT_DIO. Lines 32 to 39 can only be set to
+ NI_660X_PFI_OUTPUT_COUNTER. */
+enum ni_660x_pfi_routing {
+ NI_660X_PFI_OUTPUT_COUNTER = 1, /* counter */
+ NI_660X_PFI_OUTPUT_DIO = 2, /* static digital output */
+};
+
+/* NI External Trigger lines. These values are not arbitrary, but are related
+ * to the bits required to program the board (offset by 1 for historical
+ * reasons). */
+static inline unsigned NI_EXT_PFI(unsigned pfi_channel)
+{
+ return NI_USUAL_PFI_SELECT(pfi_channel) - 1;
+}
+static inline unsigned NI_EXT_RTSI(unsigned rtsi_channel)
+{
+ return NI_USUAL_RTSI_SELECT(rtsi_channel) - 1;
+}
+
+/* status bits for INSN_CONFIG_GET_COUNTER_STATUS */
+enum comedi_counter_status_flags {
+ COMEDI_COUNTER_ARMED = 0x1,
+ COMEDI_COUNTER_COUNTING = 0x2,
+ COMEDI_COUNTER_TERMINAL_COUNT = 0x4,
+};
+
+/* Clock sources for CDIO subdevice on NI m-series boards. Used as the
+ * scan_begin_arg for a comedi_command. These sources may also be bitwise-or'd
+ * with CR_INVERT to change polarity. */
+enum ni_m_series_cdio_scan_begin_src {
+ NI_CDIO_SCAN_BEGIN_SRC_GROUND = 0,
+ NI_CDIO_SCAN_BEGIN_SRC_AI_START = 18,
+ NI_CDIO_SCAN_BEGIN_SRC_AI_CONVERT = 19,
+ NI_CDIO_SCAN_BEGIN_SRC_PXI_STAR_TRIGGER = 20,
+ NI_CDIO_SCAN_BEGIN_SRC_G0_OUT = 28,
+ NI_CDIO_SCAN_BEGIN_SRC_G1_OUT = 29,
+ NI_CDIO_SCAN_BEGIN_SRC_ANALOG_TRIGGER = 30,
+ NI_CDIO_SCAN_BEGIN_SRC_AO_UPDATE = 31,
+ NI_CDIO_SCAN_BEGIN_SRC_FREQ_OUT = 32,
+ NI_CDIO_SCAN_BEGIN_SRC_DIO_CHANGE_DETECT_IRQ = 33
+};
+static inline unsigned NI_CDIO_SCAN_BEGIN_SRC_PFI(unsigned pfi_channel)
+{
+ return NI_USUAL_PFI_SELECT(pfi_channel);
+}
+static inline unsigned NI_CDIO_SCAN_BEGIN_SRC_RTSI(unsigned rtsi_channel)
+{
+ return NI_USUAL_RTSI_SELECT(rtsi_channel);
+}
+
+/* scan_begin_src for scan_begin_arg==TRIG_EXT with analog output command on NI
+ * boards. These scan begin sources can also be bitwise-or'd with CR_INVERT to
+ * change polarity. */
+static inline unsigned NI_AO_SCAN_BEGIN_SRC_PFI(unsigned pfi_channel)
+{
+ return NI_USUAL_PFI_SELECT(pfi_channel);
+}
+static inline unsigned NI_AO_SCAN_BEGIN_SRC_RTSI(unsigned rtsi_channel)
+{
+ return NI_USUAL_RTSI_SELECT(rtsi_channel);
+}
+
+/* Bits for setting a clock source with
+ * INSN_CONFIG_SET_CLOCK_SRC when using NI frequency output subdevice. */
+enum ni_freq_out_clock_source_bits {
+ NI_FREQ_OUT_TIMEBASE_1_DIV_2_CLOCK_SRC, /* 10 MHz */
+ NI_FREQ_OUT_TIMEBASE_2_CLOCK_SRC /* 100 KHz */
+};
+
+/* Values for setting a clock source with INSN_CONFIG_SET_CLOCK_SRC for
+ * 8254 counter subdevices on Amplicon DIO boards (amplc_dio200 driver). */
+ enum amplc_dio_clock_source {
+ AMPLC_DIO_CLK_CLKN, /* per channel external clock
+ input/output pin (pin is only an
+ input when clock source set to this
+ value, otherwise it is an output) */
+ AMPLC_DIO_CLK_10MHZ, /* 10 MHz internal clock */
+ AMPLC_DIO_CLK_1MHZ, /* 1 MHz internal clock */
+ AMPLC_DIO_CLK_100KHZ, /* 100 kHz internal clock */
+ AMPLC_DIO_CLK_10KHZ, /* 10 kHz internal clock */
+ AMPLC_DIO_CLK_1KHZ, /* 1 kHz internal clock */
+ AMPLC_DIO_CLK_OUTNM1, /* output of preceding counter channel
+ (for channel 0, preceding counter
+ channel is channel 2 on preceding
+ counter subdevice, for first counter
+ subdevice, preceding counter
+ subdevice is the last counter
+ subdevice) */
+ AMPLC_DIO_CLK_EXT /* per chip external input pin */
+ };
+
+/* Values for setting a gate source with INSN_CONFIG_SET_GATE_SRC for
+ * 8254 counter subdevices on Amplicon DIO boards (amplc_dio200 driver). */
+ enum amplc_dio_gate_source {
+ AMPLC_DIO_GAT_VCC, /* internal high logic level */
+ AMPLC_DIO_GAT_GND, /* internal low logic level */
+ AMPLC_DIO_GAT_GATN, /* per channel external gate input */
+ AMPLC_DIO_GAT_NOUTNM2, /* negated output of counter channel
+ minus 2 (for channels 0 or 1,
+ channel minus 2 is channel 1 or 2 on
+ the preceding counter subdevice, for
+ the first counter subdevice the
+ preceding counter subdevice is the
+ last counter subdevice) */
+ AMPLC_DIO_GAT_RESERVED4,
+ AMPLC_DIO_GAT_RESERVED5,
+ AMPLC_DIO_GAT_RESERVED6,
+ AMPLC_DIO_GAT_RESERVED7
+ };
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _COMEDI_H */
diff --git a/drivers/staging/comedi/comedi_compat32.c b/drivers/staging/comedi/comedi_compat32.c
new file mode 100644
index 000000000000..7d0116bcb9ff
--- /dev/null
+++ b/drivers/staging/comedi/comedi_compat32.c
@@ -0,0 +1,597 @@
+/*
+ comedi/comedi_compat32.c
+ 32-bit ioctl compatibility for 64-bit comedi kernel module.
+
+ Author: Ian Abbott, MEV Ltd. <abbotti@mev.co.uk>
+ Copyright (C) 2007 MEV Ltd. <http://www.mev.co.uk/>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-2007 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#define __NO_VERSION__
+#include "comedi.h"
+#include <linux/smp_lock.h>
+#include <asm/uaccess.h>
+
+#include "comedi_compat32.h"
+
+#ifdef CONFIG_COMPAT
+
+#ifndef HAVE_COMPAT_IOCTL
+#include <linux/ioctl32.h> /* for (un)register_ioctl32_conversion */
+#endif
+
+#define COMEDI32_CHANINFO _IOR(CIO,3,comedi32_chaninfo)
+#define COMEDI32_RANGEINFO _IOR(CIO,8,comedi32_rangeinfo)
+/* N.B. COMEDI32_CMD and COMEDI_CMD ought to use _IOWR, not _IOR.
+ * It's too late to change it now, but it only affects the command number. */
+#define COMEDI32_CMD _IOR(CIO,9,comedi32_cmd)
+/* N.B. COMEDI32_CMDTEST and COMEDI_CMDTEST ought to use _IOWR, not _IOR.
+ * It's too late to change it now, but it only affects the command number. */
+#define COMEDI32_CMDTEST _IOR(CIO,10,comedi32_cmd)
+#define COMEDI32_INSNLIST _IOR(CIO,11,comedi32_insnlist)
+#define COMEDI32_INSN _IOR(CIO,12,comedi32_insn)
+
+typedef struct comedi32_chaninfo_struct {
+ unsigned int subdev;
+ compat_uptr_t maxdata_list; /* 32-bit 'lsampl_t *' */
+ compat_uptr_t flaglist; /* 32-bit 'unsigned int *' */
+ compat_uptr_t rangelist; /* 32-bit 'unsigned int *' */
+ unsigned int unused[4];
+} comedi32_chaninfo;
+
+typedef struct comedi32_rangeinfo_struct {
+ unsigned int range_type;
+ compat_uptr_t range_ptr; /* 32-bit 'void *' */
+} comedi32_rangeinfo;
+
+typedef struct comedi32_cmd_struct {
+ unsigned int subdev;
+ unsigned int flags;
+ unsigned int start_src;
+ unsigned int start_arg;
+ unsigned int scan_begin_src;
+ unsigned int scan_begin_arg;
+ unsigned int convert_src;
+ unsigned int convert_arg;
+ unsigned int scan_end_src;
+ unsigned int scan_end_arg;
+ unsigned int stop_src;
+ unsigned int stop_arg;
+ compat_uptr_t chanlist; /* 32-bit 'unsigned int *' */
+ unsigned int chanlist_len;
+ compat_uptr_t data; /* 32-bit 'sampl_t *' */
+ unsigned int data_len;
+} comedi32_cmd;
+
+typedef struct comedi32_insn_struct {
+ unsigned int insn;
+ unsigned int n;
+ compat_uptr_t data; /* 32-bit 'lsampl_t *' */
+ unsigned int subdev;
+ unsigned int chanspec;
+ unsigned int unused[3];
+} comedi32_insn;
+
+typedef struct comedi32_insnlist_struct {
+ unsigned int n_insns;
+ compat_uptr_t insns; /* 32-bit 'comedi_insn *' */
+} comedi32_insnlist;
+
+/* Handle translated ioctl. */
+static int translated_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ if (!file->f_op) {
+ return -ENOTTY;
+ }
+#ifdef HAVE_UNLOCKED_IOCTL
+ if (file->f_op->unlocked_ioctl) {
+ int rc = (int)(*file->f_op->unlocked_ioctl)(file, cmd, arg);
+ if (rc == -ENOIOCTLCMD) {
+ rc = -ENOTTY;
+ }
+ return rc;
+ }
+#endif
+ if (file->f_op->ioctl) {
+ int rc;
+ lock_kernel();
+ rc = (*file->f_op->ioctl)(file->f_dentry->d_inode,
+ file, cmd, arg);
+ unlock_kernel();
+ return rc;
+ }
+ return -ENOTTY;
+}
+
+/* Handle 32-bit COMEDI_CHANINFO ioctl. */
+static int compat_chaninfo(struct file *file, unsigned long arg)
+{
+ comedi_chaninfo __user *chaninfo;
+ comedi32_chaninfo __user *chaninfo32;
+ int err;
+ union {
+ unsigned int uint;
+ compat_uptr_t uptr;
+ } temp;
+
+ chaninfo32 = compat_ptr(arg);
+ chaninfo = compat_alloc_user_space(sizeof(*chaninfo));
+
+ /* Copy chaninfo structure. Ignore unused members. */
+ if (!access_ok(VERIFY_READ, chaninfo32, sizeof(*chaninfo32))
+ || !access_ok(VERIFY_WRITE, chaninfo,
+ sizeof(*chaninfo))) {
+ return -EFAULT;
+ }
+ err = 0;
+ err |= __get_user(temp.uint, &chaninfo32->subdev);
+ err |= __put_user(temp.uint, &chaninfo->subdev);
+ err |= __get_user(temp.uptr, &chaninfo32->maxdata_list);
+ err |= __put_user(compat_ptr(temp.uptr), &chaninfo->maxdata_list);
+ err |= __get_user(temp.uptr, &chaninfo32->flaglist);
+ err |= __put_user(compat_ptr(temp.uptr), &chaninfo->flaglist);
+ err |= __get_user(temp.uptr, &chaninfo32->rangelist);
+ err |= __put_user(compat_ptr(temp.uptr), &chaninfo->rangelist);
+ if (err) {
+ return -EFAULT;
+ }
+
+ return translated_ioctl(file, COMEDI_CHANINFO, (unsigned long)chaninfo);
+}
+
+/* Handle 32-bit COMEDI_RANGEINFO ioctl. */
+static int compat_rangeinfo(struct file *file, unsigned long arg)
+{
+ comedi_rangeinfo __user *rangeinfo;
+ comedi32_rangeinfo __user *rangeinfo32;
+ int err;
+ union {
+ unsigned int uint;
+ compat_uptr_t uptr;
+ } temp;
+
+ rangeinfo32 = compat_ptr(arg);
+ rangeinfo = compat_alloc_user_space(sizeof(*rangeinfo));
+
+ /* Copy rangeinfo structure. */
+ if (!access_ok(VERIFY_READ, rangeinfo32, sizeof(*rangeinfo32))
+ || !access_ok(VERIFY_WRITE, rangeinfo,
+ sizeof(*rangeinfo))) {
+ return -EFAULT;
+ }
+ err = 0;
+ err |= __get_user(temp.uint, &rangeinfo32->range_type);
+ err |= __put_user(temp.uint, &rangeinfo->range_type);
+ err |= __get_user(temp.uptr, &rangeinfo32->range_ptr);
+ err |= __put_user(compat_ptr(temp.uptr), &rangeinfo->range_ptr);
+ if (err) {
+ return -EFAULT;
+ }
+
+ return translated_ioctl(file, COMEDI_RANGEINFO,
+ (unsigned long)rangeinfo);
+}
+
+/* Copy 32-bit cmd structure to native cmd structure. */
+static int get_compat_cmd(comedi_cmd __user *cmd,
+ comedi32_cmd __user *cmd32)
+{
+ int err;
+ union {
+ unsigned int uint;
+ compat_uptr_t uptr;
+ } temp;
+
+ /* Copy cmd structure. */
+ if (!access_ok(VERIFY_READ, cmd32, sizeof(*cmd32))
+ || !access_ok(VERIFY_WRITE, cmd, sizeof(*cmd))) {
+ return -EFAULT;
+ }
+ err = 0;
+ err |= __get_user(temp.uint, &cmd32->subdev);
+ err |= __put_user(temp.uint, &cmd->subdev);
+ err |= __get_user(temp.uint, &cmd32->flags);
+ err |= __put_user(temp.uint, &cmd->flags);
+ err |= __get_user(temp.uint, &cmd32->start_src);
+ err |= __put_user(temp.uint, &cmd->start_src);
+ err |= __get_user(temp.uint, &cmd32->start_arg);
+ err |= __put_user(temp.uint, &cmd->start_arg);
+ err |= __get_user(temp.uint, &cmd32->scan_begin_src);
+ err |= __put_user(temp.uint, &cmd->scan_begin_src);
+ err |= __get_user(temp.uint, &cmd32->scan_begin_arg);
+ err |= __put_user(temp.uint, &cmd->scan_begin_arg);
+ err |= __get_user(temp.uint, &cmd32->convert_src);
+ err |= __put_user(temp.uint, &cmd->convert_src);
+ err |= __get_user(temp.uint, &cmd32->convert_arg);
+ err |= __put_user(temp.uint, &cmd->convert_arg);
+ err |= __get_user(temp.uint, &cmd32->scan_end_src);
+ err |= __put_user(temp.uint, &cmd->scan_end_src);
+ err |= __get_user(temp.uint, &cmd32->scan_end_arg);
+ err |= __put_user(temp.uint, &cmd->scan_end_arg);
+ err |= __get_user(temp.uint, &cmd32->stop_src);
+ err |= __put_user(temp.uint, &cmd->stop_src);
+ err |= __get_user(temp.uint, &cmd32->stop_arg);
+ err |= __put_user(temp.uint, &cmd->stop_arg);
+ err |= __get_user(temp.uptr, &cmd32->chanlist);
+ err |= __put_user(compat_ptr(temp.uptr), &cmd->chanlist);
+ err |= __get_user(temp.uint, &cmd32->chanlist_len);
+ err |= __put_user(temp.uint, &cmd->chanlist_len);
+ err |= __get_user(temp.uptr, &cmd32->data);
+ err |= __put_user(compat_ptr(temp.uptr), &cmd->data);
+ err |= __get_user(temp.uint, &cmd32->data_len);
+ err |= __put_user(temp.uint, &cmd->data_len);
+ return err ? -EFAULT : 0;
+}
+
+/* Copy native cmd structure to 32-bit cmd structure. */
+static int put_compat_cmd(comedi32_cmd __user *cmd32, comedi_cmd __user *cmd)
+{
+ int err;
+ unsigned int temp;
+
+ /* Copy back most of cmd structure. */
+ /* Assume the pointer values are already valid. */
+ /* (Could use ptr_to_compat() to set them, but that wasn't implemented
+ * until kernel version 2.6.11.) */
+ if (!access_ok(VERIFY_READ, cmd, sizeof(*cmd))
+ || !access_ok(VERIFY_WRITE, cmd32, sizeof(*cmd32))) {
+ return -EFAULT;
+ }
+ err = 0;
+ err |= __get_user(temp, &cmd->subdev);
+ err |= __put_user(temp, &cmd32->subdev);
+ err |= __get_user(temp, &cmd->flags);
+ err |= __put_user(temp, &cmd32->flags);
+ err |= __get_user(temp, &cmd->start_src);
+ err |= __put_user(temp, &cmd32->start_src);
+ err |= __get_user(temp, &cmd->start_arg);
+ err |= __put_user(temp, &cmd32->start_arg);
+ err |= __get_user(temp, &cmd->scan_begin_src);
+ err |= __put_user(temp, &cmd32->scan_begin_src);
+ err |= __get_user(temp, &cmd->scan_begin_arg);
+ err |= __put_user(temp, &cmd32->scan_begin_arg);
+ err |= __get_user(temp, &cmd->convert_src);
+ err |= __put_user(temp, &cmd32->convert_src);
+ err |= __get_user(temp, &cmd->convert_arg);
+ err |= __put_user(temp, &cmd32->convert_arg);
+ err |= __get_user(temp, &cmd->scan_end_src);
+ err |= __put_user(temp, &cmd32->scan_end_src);
+ err |= __get_user(temp, &cmd->scan_end_arg);
+ err |= __put_user(temp, &cmd32->scan_end_arg);
+ err |= __get_user(temp, &cmd->stop_src);
+ err |= __put_user(temp, &cmd32->stop_src);
+ err |= __get_user(temp, &cmd->stop_arg);
+ err |= __put_user(temp, &cmd32->stop_arg);
+ /* Assume chanlist pointer is unchanged. */
+ err |= __get_user(temp, &cmd->chanlist_len);
+ err |= __put_user(temp, &cmd32->chanlist_len);
+ /* Assume data pointer is unchanged. */
+ err |= __get_user(temp, &cmd->data_len);
+ err |= __put_user(temp, &cmd32->data_len);
+ return err ? -EFAULT : 0;
+}
+
+/* Handle 32-bit COMEDI_CMD ioctl. */
+static int compat_cmd(struct file *file, unsigned long arg)
+{
+ comedi_cmd __user *cmd;
+ comedi32_cmd __user *cmd32;
+ int rc;
+
+ cmd32 = compat_ptr(arg);
+ cmd = compat_alloc_user_space(sizeof(*cmd));
+
+ rc = get_compat_cmd(cmd, cmd32);
+ if (rc) {
+ return rc;
+ }
+
+ return translated_ioctl(file, COMEDI_CMD, (unsigned long)cmd);
+}
+
+/* Handle 32-bit COMEDI_CMDTEST ioctl. */
+static int compat_cmdtest(struct file *file, unsigned long arg)
+{
+ comedi_cmd __user *cmd;
+ comedi32_cmd __user *cmd32;
+ int rc, err;
+
+ cmd32 = compat_ptr(arg);
+ cmd = compat_alloc_user_space(sizeof(*cmd));
+
+ rc = get_compat_cmd(cmd, cmd32);
+ if (rc) {
+ return rc;
+ }
+
+ rc = translated_ioctl(file, COMEDI_CMDTEST, (unsigned long)cmd);
+ if (rc < 0) {
+ return rc;
+ }
+
+ err = put_compat_cmd(cmd32, cmd);
+ if (err) {
+ rc = err;
+ }
+ return rc;
+}
+
+/* Copy 32-bit insn structure to native insn structure. */
+static int get_compat_insn(comedi_insn __user *insn,
+ comedi32_insn __user *insn32)
+{
+ int err;
+ union {
+ unsigned int uint;
+ compat_uptr_t uptr;
+ } temp;
+
+ /* Copy insn structure. Ignore the unused members. */
+ err = 0;
+ if (!access_ok(VERIFY_READ, insn32, sizeof(*insn32))
+ || !access_ok(VERIFY_WRITE, insn, sizeof(*insn))) {
+ return -EFAULT;
+ }
+ err |= __get_user(temp.uint, &insn32->insn);
+ err |= __put_user(temp.uint, &insn->insn);
+ err |= __get_user(temp.uint, &insn32->n);
+ err |= __put_user(temp.uint, &insn->n);
+ err |= __get_user(temp.uptr, &insn32->data);
+ err |= __put_user(compat_ptr(temp.uptr), &insn->data);
+ err |= __get_user(temp.uint, &insn32->subdev);
+ err |= __put_user(temp.uint, &insn->subdev);
+ err |= __get_user(temp.uint, &insn32->chanspec);
+ err |= __put_user(temp.uint, &insn->chanspec);
+ return err ? -EFAULT : 0;
+}
+
+/* Handle 32-bit COMEDI_INSNLIST ioctl. */
+static int compat_insnlist(struct file *file, unsigned long arg)
+{
+ struct combined_insnlist {
+ comedi_insnlist insnlist;
+ comedi_insn insn[1];
+ } __user *s;
+ comedi32_insnlist __user *insnlist32;
+ comedi32_insn __user *insn32;
+ compat_uptr_t uptr;
+ unsigned int n_insns, n;
+ int err, rc;
+
+ insnlist32 = compat_ptr(arg);
+
+ /* Get 32-bit insnlist structure. */
+ if (!access_ok(VERIFY_READ, insnlist32, sizeof(*insnlist32))) {
+ return -EFAULT;
+ }
+ err = 0;
+ err |= __get_user(n_insns, &insnlist32->n_insns);
+ err |= __get_user(uptr, &insnlist32->insns);
+ insn32 = compat_ptr(uptr);
+ if (err) {
+ return -EFAULT;
+ }
+
+ /* Allocate user memory to copy insnlist and insns into. */
+ s = compat_alloc_user_space(offsetof(struct combined_insnlist,
+ insn[n_insns]));
+
+ /* Set native insnlist structure. */
+ if (!access_ok(VERIFY_WRITE, &s->insnlist, sizeof(s->insnlist))) {
+ return -EFAULT;
+ }
+ err |= __put_user(n_insns, &s->insnlist.n_insns);
+ err |= __put_user(&s->insn[0], &s->insnlist.insns);
+ if (err) {
+ return -EFAULT;
+ }
+
+ /* Copy insn structures. */
+ for (n = 0; n < n_insns; n++) {
+ rc = get_compat_insn(&s->insn[n], &insn32[n]);
+ if (rc) {
+ return rc;
+ }
+ }
+
+ return translated_ioctl(file, COMEDI_INSNLIST,
+ (unsigned long)&s->insnlist);
+}
+
+/* Handle 32-bit COMEDI_INSN ioctl. */
+static int compat_insn(struct file *file, unsigned long arg)
+{
+ comedi_insn __user *insn;
+ comedi32_insn __user *insn32;
+ int rc;
+
+ insn32 = compat_ptr(arg);
+ insn = compat_alloc_user_space(sizeof(*insn));
+
+ rc = get_compat_insn(insn, insn32);
+ if (rc) {
+ return rc;
+ }
+
+ return translated_ioctl(file, COMEDI_INSN, (unsigned long)insn);
+}
+
+/* Process untranslated ioctl. */
+/* Returns -ENOIOCTLCMD for unrecognised ioctl codes. */
+static inline int raw_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc;
+
+ switch (cmd) {
+ case COMEDI_DEVCONFIG:
+ case COMEDI_DEVINFO:
+ case COMEDI_SUBDINFO:
+ case COMEDI_BUFCONFIG:
+ case COMEDI_BUFINFO:
+ /* Just need to translate the pointer argument. */
+ arg = (unsigned long)compat_ptr(arg);
+ rc = translated_ioctl(file, cmd, arg);
+ break;
+ case COMEDI_LOCK:
+ case COMEDI_UNLOCK:
+ case COMEDI_CANCEL:
+ case COMEDI_POLL:
+ /* No translation needed. */
+ rc = translated_ioctl(file, cmd, arg);
+ break;
+ case COMEDI32_CHANINFO:
+ rc = compat_chaninfo(file, arg);
+ break;
+ case COMEDI32_RANGEINFO:
+ rc = compat_rangeinfo(file, arg);
+ break;
+ case COMEDI32_CMD:
+ rc = compat_cmd(file, arg);
+ break;
+ case COMEDI32_CMDTEST:
+ rc = compat_cmdtest(file, arg);
+ break;
+ case COMEDI32_INSNLIST:
+ rc = compat_insnlist(file, arg);
+ break;
+ case COMEDI32_INSN:
+ rc = compat_insn(file, arg);
+ break;
+ default:
+ rc = -ENOIOCTLCMD;
+ break;
+ }
+ return rc;
+}
+
+#ifdef HAVE_COMPAT_IOCTL /* defined in <linux/fs.h> 2.6.11 onwards */
+
+/* compat_ioctl file operation. */
+/* Returns -ENOIOCTLCMD for unrecognised ioctl codes. */
+long comedi_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return raw_ioctl(file, cmd, arg);
+}
+
+#else /* HAVE_COMPAT_IOCTL */
+
+/*
+ * Brain-dead ioctl compatibility for 2.6.10 and earlier.
+ *
+ * It's brain-dead because cmd numbers need to be unique system-wide!
+ * The comedi driver could end up attempting to execute ioctls for non-Comedi
+ * devices because it registered the system-wide cmd code first. Similarly,
+ * another driver could end up attempting to execute ioctls for a Comedi
+ * device because it registered the cmd code first. Chaos ensues.
+ */
+
+/* Handler for all 32-bit ioctl codes registered by this driver. */
+static int mapped_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg,
+ struct file *file)
+{
+ int rc;
+
+ /* Make sure we are dealing with a Comedi device. */
+ if (imajor(file->f_dentry->d_inode) != COMEDI_MAJOR) {
+ return -ENOTTY;
+ }
+ rc = raw_ioctl(file, cmd, arg);
+ /* Do not return -ENOIOCTLCMD. */
+ if (rc == -ENOIOCTLCMD) {
+ rc = -ENOTTY;
+ }
+ return rc;
+}
+
+struct ioctl32_map {
+ unsigned int cmd;
+ int (*handler)(unsigned int, unsigned int, unsigned long,
+ struct file *);
+ int registered;
+};
+
+static struct ioctl32_map comedi_ioctl32_map[] = {
+ { COMEDI_DEVCONFIG, mapped_ioctl, 0 },
+ { COMEDI_DEVINFO, mapped_ioctl, 0 },
+ { COMEDI_SUBDINFO, mapped_ioctl, 0 },
+ { COMEDI_BUFCONFIG, mapped_ioctl, 0 },
+ { COMEDI_BUFINFO, mapped_ioctl, 0 },
+ { COMEDI_LOCK, mapped_ioctl, 0 },
+ { COMEDI_UNLOCK, mapped_ioctl, 0 },
+ { COMEDI_CANCEL, mapped_ioctl, 0 },
+ { COMEDI_POLL, mapped_ioctl, 0 },
+ { COMEDI32_CHANINFO, mapped_ioctl, 0 },
+ { COMEDI32_RANGEINFO, mapped_ioctl, 0 },
+ { COMEDI32_CMD, mapped_ioctl, 0 },
+ { COMEDI32_CMDTEST, mapped_ioctl, 0 },
+ { COMEDI32_INSNLIST, mapped_ioctl, 0 },
+ { COMEDI32_INSN, mapped_ioctl, 0 },
+};
+
+#define NUM_IOCTL32_MAPS ARRAY_SIZE(comedi_ioctl32_map)
+
+/* Register system-wide 32-bit ioctl handlers. */
+void comedi_register_ioctl32(void)
+{
+ int n, rc;
+
+ for (n = 0; n < NUM_IOCTL32_MAPS; n++) {
+ rc = register_ioctl32_conversion(comedi_ioctl32_map[n].cmd,
+ comedi_ioctl32_map[n].handler);
+ if (rc) {
+ printk(KERN_WARNING
+ "comedi: failed to register 32-bit "
+ "compatible ioctl handler for 0x%X - "
+ "expect bad things to happen!\n",
+ comedi_ioctl32_map[n].cmd);
+ }
+ comedi_ioctl32_map[n].registered = !rc;
+ }
+}
+
+/* Unregister system-wide 32-bit ioctl translations. */
+void comedi_unregister_ioctl32(void)
+{
+ int n, rc;
+
+ for (n = 0; n < NUM_IOCTL32_MAPS; n++) {
+ if (comedi_ioctl32_map[n].registered) {
+ rc = unregister_ioctl32_conversion(
+ comedi_ioctl32_map[n].cmd,
+ comedi_ioctl32_map[n].handler);
+ if (rc) {
+ printk(KERN_ERR
+ "comedi: failed to unregister 32-bit "
+ "compatible ioctl handler for 0x%X - "
+ "expect kernel Oops!\n",
+ comedi_ioctl32_map[n].cmd);
+ } else {
+ comedi_ioctl32_map[n].registered = 0;
+ }
+ }
+ }
+}
+
+#endif /* HAVE_COMPAT_IOCTL */
+
+#endif /* CONFIG_COMPAT */
diff --git a/drivers/staging/comedi/comedi_compat32.h b/drivers/staging/comedi/comedi_compat32.h
new file mode 100644
index 000000000000..0ca01642c165
--- /dev/null
+++ b/drivers/staging/comedi/comedi_compat32.h
@@ -0,0 +1,58 @@
+/*
+ comedi/comedi_compat32.h
+ 32-bit ioctl compatibility for 64-bit comedi kernel module.
+
+ Author: Ian Abbott, MEV Ltd. <abbotti@mev.co.uk>
+ Copyright (C) 2007 MEV Ltd. <http://www.mev.co.uk/>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-2007 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef _COMEDI_COMPAT32_H
+#define _COMEDI_COMPAT32_H
+
+#include <linux/compat.h>
+#include <linux/fs.h> /* For HAVE_COMPAT_IOCTL and HAVE_UNLOCKED_IOCTL */
+
+#ifdef CONFIG_COMPAT
+
+#ifdef HAVE_COMPAT_IOCTL
+
+extern long comedi_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg);
+#define comedi_register_ioctl32() do {} while (0)
+#define comedi_unregister_ioctl32() do {} while (0)
+
+#else /* HAVE_COMPAT_IOCTL */
+
+#define comedi_compat_ioctl 0 /* NULL */
+extern void comedi_register_ioctl32(void);
+extern void comedi_unregister_ioctl32(void);
+
+#endif /* HAVE_COMPAT_IOCTL */
+
+#else /* CONFIG_COMPAT */
+
+#define comedi_compat_ioctl 0 /* NULL */
+#define comedi_register_ioctl32() do {} while (0)
+#define comedi_unregister_ioctl32() do {} while (0)
+
+#endif /* CONFIG_COMPAT */
+
+#endif /* _COMEDI_COMPAT32_H */
diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c
new file mode 100644
index 000000000000..018c964396df
--- /dev/null
+++ b/drivers/staging/comedi/comedi_fops.c
@@ -0,0 +1,2244 @@
+/*
+ comedi/comedi_fops.c
+ comedi kernel module
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#undef DEBUG
+
+#define __NO_VERSION__
+#include "comedi_fops.h"
+#include "comedi_compat32.h"
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/kmod.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include "comedidev.h"
+#include <linux/cdev.h>
+
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+/* #include "kvmem.h" */
+
+MODULE_AUTHOR("http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi core module");
+MODULE_LICENSE("GPL");
+
+#ifdef CONFIG_COMEDI_DEBUG
+int comedi_debug;
+module_param(comedi_debug, int, 0644);
+#endif
+
+static DEFINE_SPINLOCK(comedi_file_info_table_lock);
+static struct comedi_device_file_info
+ *comedi_file_info_table[COMEDI_NUM_MINORS];
+
+static int do_devconfig_ioctl(comedi_device *dev, comedi_devconfig *arg);
+static int do_bufconfig_ioctl(comedi_device *dev, void *arg);
+static int do_devinfo_ioctl(comedi_device *dev, comedi_devinfo *arg,
+ struct file *file);
+static int do_subdinfo_ioctl(comedi_device *dev, comedi_subdinfo *arg,
+ void *file);
+static int do_chaninfo_ioctl(comedi_device *dev, comedi_chaninfo *arg);
+static int do_bufinfo_ioctl(comedi_device *dev, void *arg);
+static int do_cmd_ioctl(comedi_device *dev, void *arg, void *file);
+static int do_lock_ioctl(comedi_device *dev, unsigned int arg, void *file);
+static int do_unlock_ioctl(comedi_device *dev, unsigned int arg, void *file);
+static int do_cancel_ioctl(comedi_device *dev, unsigned int arg, void *file);
+static int do_cmdtest_ioctl(comedi_device *dev, void *arg, void *file);
+static int do_insnlist_ioctl(comedi_device *dev, void *arg, void *file);
+static int do_insn_ioctl(comedi_device *dev, void *arg, void *file);
+static int do_poll_ioctl(comedi_device *dev, unsigned int subd, void *file);
+
+extern void do_become_nonbusy(comedi_device *dev, comedi_subdevice *s);
+static int do_cancel(comedi_device *dev, comedi_subdevice *s);
+
+static int comedi_fasync(int fd, struct file *file, int on);
+
+static int is_device_busy(comedi_device *dev);
+
+#ifdef HAVE_UNLOCKED_IOCTL
+static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+#else
+static int comedi_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+#endif
+{
+ const unsigned minor = iminor(file->f_dentry->d_inode);
+ struct comedi_device_file_info *dev_file_info =
+ comedi_get_device_file_info(minor);
+ comedi_device *dev = dev_file_info->device;
+ int rc;
+
+ mutex_lock(&dev->mutex);
+
+ /* Device config is special, because it must work on
+ * an unconfigured device. */
+ if (cmd == COMEDI_DEVCONFIG) {
+ rc = do_devconfig_ioctl(dev, (void *)arg);
+ goto done;
+ }
+
+ if (!dev->attached) {
+ DPRINTK("no driver configured on /dev/comedi%i\n", dev->minor);
+ rc = -ENODEV;
+ goto done;
+ }
+
+ switch (cmd) {
+ case COMEDI_BUFCONFIG:
+ rc = do_bufconfig_ioctl(dev, (void *)arg);
+ break;
+ case COMEDI_DEVINFO:
+ rc = do_devinfo_ioctl(dev, (void *)arg, file);
+ break;
+ case COMEDI_SUBDINFO:
+ rc = do_subdinfo_ioctl(dev, (void *)arg, file);
+ break;
+ case COMEDI_CHANINFO:
+ rc = do_chaninfo_ioctl(dev, (void *)arg);
+ break;
+ case COMEDI_RANGEINFO:
+ rc = do_rangeinfo_ioctl(dev, (void *)arg);
+ break;
+ case COMEDI_BUFINFO:
+ rc = do_bufinfo_ioctl(dev, (void *)arg);
+ break;
+ case COMEDI_LOCK:
+ rc = do_lock_ioctl(dev, arg, file);
+ break;
+ case COMEDI_UNLOCK:
+ rc = do_unlock_ioctl(dev, arg, file);
+ break;
+ case COMEDI_CANCEL:
+ rc = do_cancel_ioctl(dev, arg, file);
+ break;
+ case COMEDI_CMD:
+ rc = do_cmd_ioctl(dev, (void *)arg, file);
+ break;
+ case COMEDI_CMDTEST:
+ rc = do_cmdtest_ioctl(dev, (void *)arg, file);
+ break;
+ case COMEDI_INSNLIST:
+ rc = do_insnlist_ioctl(dev, (void *)arg, file);
+ break;
+ case COMEDI_INSN:
+ rc = do_insn_ioctl(dev, (void *)arg, file);
+ break;
+ case COMEDI_POLL:
+ rc = do_poll_ioctl(dev, arg, file);
+ break;
+ default:
+ rc = -ENOTTY;
+ break;
+ }
+
+done:
+ mutex_unlock(&dev->mutex);
+ return rc;
+}
+
+/*
+ COMEDI_DEVCONFIG
+ device config ioctl
+
+ arg:
+ pointer to devconfig structure
+
+ reads:
+ devconfig structure at arg
+
+ writes:
+ none
+*/
+static int do_devconfig_ioctl(comedi_device *dev, comedi_devconfig *arg)
+{
+ comedi_devconfig it;
+ int ret;
+ unsigned char *aux_data = NULL;
+ int aux_len;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (arg == NULL) {
+ if (is_device_busy(dev))
+ return -EBUSY;
+ if (dev->attached) {
+ struct module *driver_module = dev->driver->module;
+ comedi_device_detach(dev);
+ module_put(driver_module);
+ }
+ return 0;
+ }
+
+ if (copy_from_user(&it, arg, sizeof(comedi_devconfig)))
+ return -EFAULT;
+
+ it.board_name[COMEDI_NAMELEN - 1] = 0;
+
+ if (comedi_aux_data(it.options, 0) &&
+ it.options[COMEDI_DEVCONF_AUX_DATA_LENGTH]) {
+ int bit_shift;
+ aux_len = it.options[COMEDI_DEVCONF_AUX_DATA_LENGTH];
+ if (aux_len < 0)
+ return -EFAULT;
+
+ aux_data = vmalloc(aux_len);
+ if (!aux_data)
+ return -ENOMEM;
+
+ if (copy_from_user(aux_data,
+ comedi_aux_data(it.options, 0), aux_len)) {
+ vfree(aux_data);
+ return -EFAULT;
+ }
+ it.options[COMEDI_DEVCONF_AUX_DATA_LO] =
+ (unsigned long)aux_data;
+ if (sizeof(void *) > sizeof(int)) {
+ bit_shift = sizeof(int) * 8;
+ it.options[COMEDI_DEVCONF_AUX_DATA_HI] =
+ ((unsigned long)aux_data) >> bit_shift;
+ } else
+ it.options[COMEDI_DEVCONF_AUX_DATA_HI] = 0;
+ }
+
+ ret = comedi_device_attach(dev, &it);
+ if (ret == 0) {
+ if (!try_module_get(dev->driver->module)) {
+ comedi_device_detach(dev);
+ return -ENOSYS;
+ }
+ }
+
+ if (aux_data)
+ vfree(aux_data);
+
+ return ret;
+}
+
+/*
+ COMEDI_BUFCONFIG
+ buffer configuration ioctl
+
+ arg:
+ pointer to bufconfig structure
+
+ reads:
+ bufconfig at arg
+
+ writes:
+ modified bufconfig at arg
+
+*/
+static int do_bufconfig_ioctl(comedi_device *dev, void *arg)
+{
+ comedi_bufconfig bc;
+ comedi_async *async;
+ comedi_subdevice *s;
+ int ret = 0;
+
+ if (copy_from_user(&bc, arg, sizeof(comedi_bufconfig)))
+ return -EFAULT;
+
+ if (bc.subdevice >= dev->n_subdevices || bc.subdevice < 0)
+ return -EINVAL;
+
+ s = dev->subdevices + bc.subdevice;
+ async = s->async;
+
+ if (!async) {
+ DPRINTK("subdevice does not have async capability\n");
+ bc.size = 0;
+ bc.maximum_size = 0;
+ goto copyback;
+ }
+
+ if (bc.maximum_size) {
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ async->max_bufsize = bc.maximum_size;
+ }
+
+ if (bc.size) {
+ if (bc.size > async->max_bufsize)
+ return -EPERM;
+
+ if (s->busy) {
+ DPRINTK("subdevice is busy, cannot resize buffer\n");
+ return -EBUSY;
+ }
+ if (async->mmap_count) {
+ DPRINTK("subdevice is mmapped, cannot resize buffer\n");
+ return -EBUSY;
+ }
+
+ if (!async->prealloc_buf)
+ return -EINVAL;
+
+ /* make sure buffer is an integral number of pages
+ * (we round up) */
+ bc.size = (bc.size + PAGE_SIZE - 1) & PAGE_MASK;
+
+ ret = comedi_buf_alloc(dev, s, bc.size);
+ if (ret < 0)
+ return ret;
+
+ if (s->buf_change) {
+ ret = s->buf_change(dev, s, bc.size);
+ if (ret < 0)
+ return ret;
+ }
+
+ DPRINTK("comedi%i subd %d buffer resized to %i bytes\n",
+ dev->minor, bc.subdevice, async->prealloc_bufsz);
+ }
+
+ bc.size = async->prealloc_bufsz;
+ bc.maximum_size = async->max_bufsize;
+
+copyback:
+ if (copy_to_user(arg, &bc, sizeof(comedi_bufconfig)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ COMEDI_DEVINFO
+ device info ioctl
+
+ arg:
+ pointer to devinfo structure
+
+ reads:
+ none
+
+ writes:
+ devinfo structure
+
+*/
+static int do_devinfo_ioctl(comedi_device *dev, comedi_devinfo *arg,
+ struct file *file)
+{
+ comedi_devinfo devinfo;
+ const unsigned minor = iminor(file->f_dentry->d_inode);
+ struct comedi_device_file_info *dev_file_info =
+ comedi_get_device_file_info(minor);
+ comedi_subdevice *read_subdev =
+ comedi_get_read_subdevice(dev_file_info);
+ comedi_subdevice *write_subdev =
+ comedi_get_write_subdevice(dev_file_info);
+
+ memset(&devinfo, 0, sizeof(devinfo));
+
+ /* fill devinfo structure */
+ devinfo.version_code = COMEDI_VERSION_CODE;
+ devinfo.n_subdevs = dev->n_subdevices;
+ memcpy(devinfo.driver_name, dev->driver->driver_name, COMEDI_NAMELEN);
+ memcpy(devinfo.board_name, dev->board_name, COMEDI_NAMELEN);
+
+ if (read_subdev)
+ devinfo.read_subdevice = read_subdev - dev->subdevices;
+ else
+ devinfo.read_subdevice = -1;
+
+ if (write_subdev)
+ devinfo.write_subdevice = write_subdev - dev->subdevices;
+ else
+ devinfo.write_subdevice = -1;
+
+ if (copy_to_user(arg, &devinfo, sizeof(comedi_devinfo)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ COMEDI_SUBDINFO
+ subdevice info ioctl
+
+ arg:
+ pointer to array of subdevice info structures
+
+ reads:
+ none
+
+ writes:
+ array of subdevice info structures at arg
+
+*/
+static int do_subdinfo_ioctl(comedi_device *dev, comedi_subdinfo *arg,
+ void *file)
+{
+ int ret, i;
+ comedi_subdinfo *tmp, *us;
+ comedi_subdevice *s;
+
+ tmp = kcalloc(dev->n_subdevices, sizeof(comedi_subdinfo), GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ /* fill subdinfo structs */
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = dev->subdevices + i;
+ us = tmp + i;
+
+ us->type = s->type;
+ us->n_chan = s->n_chan;
+ us->subd_flags = s->subdev_flags;
+ if (comedi_get_subdevice_runflags(s) & SRF_RUNNING)
+ us->subd_flags |= SDF_RUNNING;
+#define TIMER_nanosec 5 /* backwards compatibility */
+ us->timer_type = TIMER_nanosec;
+ us->len_chanlist = s->len_chanlist;
+ us->maxdata = s->maxdata;
+ if (s->range_table) {
+ us->range_type =
+ (i << 24) | (0 << 16) | (s->range_table->length);
+ } else {
+ us->range_type = 0; /* XXX */
+ }
+ us->flags = s->flags;
+
+ if (s->busy)
+ us->subd_flags |= SDF_BUSY;
+ if (s->busy == file)
+ us->subd_flags |= SDF_BUSY_OWNER;
+ if (s->lock)
+ us->subd_flags |= SDF_LOCKED;
+ if (s->lock == file)
+ us->subd_flags |= SDF_LOCK_OWNER;
+ if (!s->maxdata && s->maxdata_list)
+ us->subd_flags |= SDF_MAXDATA;
+ if (s->flaglist)
+ us->subd_flags |= SDF_FLAGS;
+ if (s->range_table_list)
+ us->subd_flags |= SDF_RANGETYPE;
+ if (s->do_cmd)
+ us->subd_flags |= SDF_CMD;
+
+ if (s->insn_bits != &insn_inval)
+ us->insn_bits_support = COMEDI_SUPPORTED;
+ else
+ us->insn_bits_support = COMEDI_UNSUPPORTED;
+
+ us->settling_time_0 = s->settling_time_0;
+ }
+
+ ret = copy_to_user(arg, tmp,
+ dev->n_subdevices * sizeof(comedi_subdinfo));
+
+ kfree(tmp);
+
+ return ret ? -EFAULT : 0;
+}
+
+/*
+ COMEDI_CHANINFO
+ subdevice info ioctl
+
+ arg:
+ pointer to chaninfo structure
+
+ reads:
+ chaninfo structure at arg
+
+ writes:
+ arrays at elements of chaninfo structure
+
+*/
+static int do_chaninfo_ioctl(comedi_device *dev, comedi_chaninfo *arg)
+{
+ comedi_subdevice *s;
+ comedi_chaninfo it;
+
+ if (copy_from_user(&it, arg, sizeof(comedi_chaninfo)))
+ return -EFAULT;
+
+ if (it.subdev >= dev->n_subdevices)
+ return -EINVAL;
+ s = dev->subdevices + it.subdev;
+
+ if (it.maxdata_list) {
+ if (s->maxdata || !s->maxdata_list)
+ return -EINVAL;
+ if (copy_to_user(it.maxdata_list, s->maxdata_list,
+ s->n_chan * sizeof(lsampl_t)))
+ return -EFAULT;
+ }
+
+ if (it.flaglist) {
+ if (!s->flaglist)
+ return -EINVAL;
+ if (copy_to_user(it.flaglist, s->flaglist,
+ s->n_chan * sizeof(unsigned int)))
+ return -EFAULT;
+ }
+
+ if (it.rangelist) {
+ int i;
+
+ if (!s->range_table_list)
+ return -EINVAL;
+ for (i = 0; i < s->n_chan; i++) {
+ int x;
+
+ x = (dev->minor << 28) | (it.subdev << 24) | (i << 16) |
+ (s->range_table_list[i]->length);
+ put_user(x, it.rangelist + i);
+ }
+#if 0
+ if (copy_to_user(it.rangelist, s->range_type_list,
+ s->n_chan*sizeof(unsigned int)))
+ return -EFAULT;
+#endif
+ }
+
+ return 0;
+}
+
+ /*
+ COMEDI_BUFINFO
+ buffer information ioctl
+
+ arg:
+ pointer to bufinfo structure
+
+ reads:
+ bufinfo at arg
+
+ writes:
+ modified bufinfo at arg
+
+ */
+static int do_bufinfo_ioctl(comedi_device *dev, void *arg)
+{
+ comedi_bufinfo bi;
+ comedi_subdevice *s;
+ comedi_async *async;
+
+ if (copy_from_user(&bi, arg, sizeof(comedi_bufinfo)))
+ return -EFAULT;
+
+ if (bi.subdevice >= dev->n_subdevices || bi.subdevice < 0)
+ return -EINVAL;
+
+ s = dev->subdevices + bi.subdevice;
+ async = s->async;
+
+ if (!async) {
+ DPRINTK("subdevice does not have async capability\n");
+ bi.buf_write_ptr = 0;
+ bi.buf_read_ptr = 0;
+ bi.buf_write_count = 0;
+ bi.buf_read_count = 0;
+ goto copyback;
+ }
+
+ if (bi.bytes_read && (s->subdev_flags & SDF_CMD_READ)) {
+ bi.bytes_read = comedi_buf_read_alloc(async, bi.bytes_read);
+ comedi_buf_read_free(async, bi.bytes_read);
+
+ if (!(comedi_get_subdevice_runflags(s) & (SRF_ERROR |
+ SRF_RUNNING))
+ && async->buf_write_count == async->buf_read_count) {
+ do_become_nonbusy(dev, s);
+ }
+ }
+
+ if (bi.bytes_written && (s->subdev_flags & SDF_CMD_WRITE)) {
+ bi.bytes_written =
+ comedi_buf_write_alloc(async, bi.bytes_written);
+ comedi_buf_write_free(async, bi.bytes_written);
+ }
+
+ bi.buf_write_count = async->buf_write_count;
+ bi.buf_write_ptr = async->buf_write_ptr;
+ bi.buf_read_count = async->buf_read_count;
+ bi.buf_read_ptr = async->buf_read_ptr;
+
+copyback:
+ if (copy_to_user(arg, &bi, sizeof(comedi_bufinfo)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int parse_insn(comedi_device *dev, comedi_insn *insn, lsampl_t *data,
+ void *file);
+/*
+ * COMEDI_INSNLIST
+ * synchronous instructions
+ *
+ * arg:
+ * pointer to sync cmd structure
+ *
+ * reads:
+ * sync cmd struct at arg
+ * instruction list
+ * data (for writes)
+ *
+ * writes:
+ * data (for reads)
+ */
+/* arbitrary limits */
+#define MAX_SAMPLES 256
+static int do_insnlist_ioctl(comedi_device *dev, void *arg, void *file)
+{
+ comedi_insnlist insnlist;
+ comedi_insn *insns = NULL;
+ lsampl_t *data = NULL;
+ int i = 0;
+ int ret = 0;
+
+ if (copy_from_user(&insnlist, arg, sizeof(comedi_insnlist)))
+ return -EFAULT;
+
+ data = kmalloc(sizeof(lsampl_t) * MAX_SAMPLES, GFP_KERNEL);
+ if (!data) {
+ DPRINTK("kmalloc failed\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ insns = kmalloc(sizeof(comedi_insn) * insnlist.n_insns, GFP_KERNEL);
+ if (!insns) {
+ DPRINTK("kmalloc failed\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ if (copy_from_user(insns, insnlist.insns,
+ sizeof(comedi_insn) * insnlist.n_insns)) {
+ DPRINTK("copy_from_user failed\n");
+ ret = -EFAULT;
+ goto error;
+ }
+
+ for (i = 0; i < insnlist.n_insns; i++) {
+ if (insns[i].n > MAX_SAMPLES) {
+ DPRINTK("number of samples too large\n");
+ ret = -EINVAL;
+ goto error;
+ }
+ if (insns[i].insn & INSN_MASK_WRITE) {
+ if (copy_from_user(data, insns[i].data,
+ insns[i].n * sizeof(lsampl_t))) {
+ DPRINTK("copy_from_user failed\n");
+ ret = -EFAULT;
+ goto error;
+ }
+ }
+ ret = parse_insn(dev, insns + i, data, file);
+ if (ret < 0)
+ goto error;
+ if (insns[i].insn & INSN_MASK_READ) {
+ if (copy_to_user(insns[i].data, data,
+ insns[i].n * sizeof(lsampl_t))) {
+ DPRINTK("copy_to_user failed\n");
+ ret = -EFAULT;
+ goto error;
+ }
+ }
+ if (need_resched())
+ schedule();
+ }
+
+error:
+ kfree(insns);
+ kfree(data);
+
+ if (ret < 0)
+ return ret;
+ return i;
+}
+
+static int check_insn_config_length(comedi_insn *insn, lsampl_t *data)
+{
+ if (insn->n < 1)
+ return -EINVAL;
+
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ case INSN_CONFIG_DIO_INPUT:
+ case INSN_CONFIG_DISARM:
+ case INSN_CONFIG_RESET:
+ if (insn->n == 1)
+ return 0;
+ break;
+ case INSN_CONFIG_ARM:
+ case INSN_CONFIG_DIO_QUERY:
+ case INSN_CONFIG_BLOCK_SIZE:
+ case INSN_CONFIG_FILTER:
+ case INSN_CONFIG_SERIAL_CLOCK:
+ case INSN_CONFIG_BIDIRECTIONAL_DATA:
+ case INSN_CONFIG_ALT_SOURCE:
+ case INSN_CONFIG_SET_COUNTER_MODE:
+ case INSN_CONFIG_8254_READ_STATUS:
+ case INSN_CONFIG_SET_ROUTING:
+ case INSN_CONFIG_GET_ROUTING:
+ case INSN_CONFIG_GET_PWM_STATUS:
+ case INSN_CONFIG_PWM_SET_PERIOD:
+ case INSN_CONFIG_PWM_GET_PERIOD:
+ if (insn->n == 2)
+ return 0;
+ break;
+ case INSN_CONFIG_SET_GATE_SRC:
+ case INSN_CONFIG_GET_GATE_SRC:
+ case INSN_CONFIG_SET_CLOCK_SRC:
+ case INSN_CONFIG_GET_CLOCK_SRC:
+ case INSN_CONFIG_SET_OTHER_SRC:
+ case INSN_CONFIG_GET_COUNTER_STATUS:
+ case INSN_CONFIG_PWM_SET_H_BRIDGE:
+ case INSN_CONFIG_PWM_GET_H_BRIDGE:
+ case INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE:
+ if (insn->n == 3)
+ return 0;
+ break;
+ case INSN_CONFIG_PWM_OUTPUT:
+ case INSN_CONFIG_ANALOG_TRIG:
+ if (insn->n == 5)
+ return 0;
+ break;
+ /* by default we allow the insn since we don't have checks for
+ * all possible cases yet */
+ default:
+ rt_printk("comedi: no check for data length of config insn id "
+ "%i is implemented.\n"
+ " Add a check to %s in %s.\n"
+ " Assuming n=%i is correct.\n", data[0], __func__,
+ __FILE__, insn->n);
+ return 0;
+ break;
+ }
+ return -EINVAL;
+}
+
+static int parse_insn(comedi_device *dev, comedi_insn *insn, lsampl_t *data,
+ void *file)
+{
+ comedi_subdevice *s;
+ int ret = 0;
+ int i;
+
+ if (insn->insn & INSN_MASK_SPECIAL) {
+ /* a non-subdevice instruction */
+
+ switch (insn->insn) {
+ case INSN_GTOD:
+ {
+ struct timeval tv;
+
+ if (insn->n != 2) {
+ ret = -EINVAL;
+ break;
+ }
+
+ do_gettimeofday(&tv);
+ data[0] = tv.tv_sec;
+ data[1] = tv.tv_usec;
+ ret = 2;
+
+ break;
+ }
+ case INSN_WAIT:
+ if (insn->n != 1 || data[0] >= 100000) {
+ ret = -EINVAL;
+ break;
+ }
+ udelay(data[0] / 1000);
+ ret = 1;
+ break;
+ case INSN_INTTRIG:
+ if (insn->n != 1) {
+ ret = -EINVAL;
+ break;
+ }
+ if (insn->subdev >= dev->n_subdevices) {
+ DPRINTK("%d not usable subdevice\n",
+ insn->subdev);
+ ret = -EINVAL;
+ break;
+ }
+ s = dev->subdevices + insn->subdev;
+ if (!s->async) {
+ DPRINTK("no async\n");
+ ret = -EINVAL;
+ break;
+ }
+ if (!s->async->inttrig) {
+ DPRINTK("no inttrig\n");
+ ret = -EAGAIN;
+ break;
+ }
+ ret = s->async->inttrig(dev, s, insn->data[0]);
+ if (ret >= 0)
+ ret = 1;
+ break;
+ default:
+ DPRINTK("invalid insn\n");
+ ret = -EINVAL;
+ break;
+ }
+ } else {
+ /* a subdevice instruction */
+ lsampl_t maxdata;
+
+ if (insn->subdev >= dev->n_subdevices) {
+ DPRINTK("subdevice %d out of range\n", insn->subdev);
+ ret = -EINVAL;
+ goto out;
+ }
+ s = dev->subdevices + insn->subdev;
+
+ if (s->type == COMEDI_SUBD_UNUSED) {
+ DPRINTK("%d not usable subdevice\n", insn->subdev);
+ ret = -EIO;
+ goto out;
+ }
+
+ /* are we locked? (ioctl lock) */
+ if (s->lock && s->lock != file) {
+ DPRINTK("device locked\n");
+ ret = -EACCES;
+ goto out;
+ }
+
+ ret = check_chanlist(s, 1, &insn->chanspec);
+ if (ret < 0) {
+ ret = -EINVAL;
+ DPRINTK("bad chanspec\n");
+ goto out;
+ }
+
+ if (s->busy) {
+ ret = -EBUSY;
+ goto out;
+ }
+ /* This looks arbitrary. It is. */
+ s->busy = &parse_insn;
+ switch (insn->insn) {
+ case INSN_READ:
+ ret = s->insn_read(dev, s, insn, data);
+ break;
+ case INSN_WRITE:
+ maxdata = s->maxdata_list
+ ? s->maxdata_list[CR_CHAN(insn->chanspec)]
+ : s->maxdata;
+ for (i = 0; i < insn->n; ++i) {
+ if (data[i] > maxdata) {
+ ret = -EINVAL;
+ DPRINTK("bad data value(s)\n");
+ break;
+ }
+ }
+ if (ret == 0)
+ ret = s->insn_write(dev, s, insn, data);
+ break;
+ case INSN_BITS:
+ if (insn->n != 2) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = s->insn_bits(dev, s, insn, data);
+ break;
+ case INSN_CONFIG:
+ ret = check_insn_config_length(insn, data);
+ if (ret)
+ break;
+ ret = s->insn_config(dev, s, insn, data);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ s->busy = NULL;
+ }
+
+out:
+ return ret;
+}
+
+/*
+ * COMEDI_INSN
+ * synchronous instructions
+ *
+ * arg:
+ * pointer to insn
+ *
+ * reads:
+ * comedi_insn struct at arg
+ * data (for writes)
+ *
+ * writes:
+ * data (for reads)
+ */
+static int do_insn_ioctl(comedi_device *dev, void *arg, void *file)
+{
+ comedi_insn insn;
+ lsampl_t *data = NULL;
+ int ret = 0;
+
+ data = kmalloc(sizeof(lsampl_t) * MAX_SAMPLES, GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ if (copy_from_user(&insn, arg, sizeof(comedi_insn))) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ /* This is where the behavior of insn and insnlist deviate. */
+ if (insn.n > MAX_SAMPLES)
+ insn.n = MAX_SAMPLES;
+ if (insn.insn & INSN_MASK_WRITE) {
+ if (copy_from_user(data, insn.data, insn.n * sizeof(lsampl_t))) {
+ ret = -EFAULT;
+ goto error;
+ }
+ }
+ ret = parse_insn(dev, &insn, data, file);
+ if (ret < 0)
+ goto error;
+ if (insn.insn & INSN_MASK_READ) {
+ if (copy_to_user(insn.data, data, insn.n * sizeof(lsampl_t))) {
+ ret = -EFAULT;
+ goto error;
+ }
+ }
+ ret = insn.n;
+
+error:
+ kfree(data);
+
+ return ret;
+}
+
+/*
+ COMEDI_CMD
+ command ioctl
+
+ arg:
+ pointer to cmd structure
+
+ reads:
+ cmd structure at arg
+ channel/range list
+
+ writes:
+ modified cmd structure at arg
+
+*/
+static int do_cmd_ioctl(comedi_device *dev, void *arg, void *file)
+{
+ comedi_cmd user_cmd;
+ comedi_subdevice *s;
+ comedi_async *async;
+ int ret = 0;
+ unsigned int *chanlist_saver = NULL;
+
+ if (copy_from_user(&user_cmd, arg, sizeof(comedi_cmd))) {
+ DPRINTK("bad cmd address\n");
+ return -EFAULT;
+ }
+ /* save user's chanlist pointer so it can be restored later */
+ chanlist_saver = user_cmd.chanlist;
+
+ if (user_cmd.subdev >= dev->n_subdevices) {
+ DPRINTK("%d no such subdevice\n", user_cmd.subdev);
+ return -ENODEV;
+ }
+
+ s = dev->subdevices + user_cmd.subdev;
+ async = s->async;
+
+ if (s->type == COMEDI_SUBD_UNUSED) {
+ DPRINTK("%d not valid subdevice\n", user_cmd.subdev);
+ return -EIO;
+ }
+
+ if (!s->do_cmd || !s->do_cmdtest || !s->async) {
+ DPRINTK("subdevice %i does not support commands\n",
+ user_cmd.subdev);
+ return -EIO;
+ }
+
+ /* are we locked? (ioctl lock) */
+ if (s->lock && s->lock != file) {
+ DPRINTK("subdevice locked\n");
+ return -EACCES;
+ }
+
+ /* are we busy? */
+ if (s->busy) {
+ DPRINTK("subdevice busy\n");
+ return -EBUSY;
+ }
+ s->busy = file;
+
+ /* make sure channel/gain list isn't too long */
+ if (user_cmd.chanlist_len > s->len_chanlist) {
+ DPRINTK("channel/gain list too long %u > %d\n",
+ user_cmd.chanlist_len, s->len_chanlist);
+ ret = -EINVAL;
+ goto cleanup;
+ }
+
+ /* make sure channel/gain list isn't too short */
+ if (user_cmd.chanlist_len < 1) {
+ DPRINTK("channel/gain list too short %u < 1\n",
+ user_cmd.chanlist_len);
+ ret = -EINVAL;
+ goto cleanup;
+ }
+
+ kfree(async->cmd.chanlist);
+ async->cmd = user_cmd;
+ async->cmd.data = NULL;
+ /* load channel/gain list */
+ async->cmd.chanlist =
+ kmalloc(async->cmd.chanlist_len * sizeof(int), GFP_KERNEL);
+ if (!async->cmd.chanlist) {
+ DPRINTK("allocation failed\n");
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ if (copy_from_user(async->cmd.chanlist, user_cmd.chanlist,
+ async->cmd.chanlist_len * sizeof(int))) {
+ DPRINTK("fault reading chanlist\n");
+ ret = -EFAULT;
+ goto cleanup;
+ }
+
+ /* make sure each element in channel/gain list is valid */
+ ret = check_chanlist(s, async->cmd.chanlist_len, async->cmd.chanlist);
+ if (ret < 0) {
+ DPRINTK("bad chanlist\n");
+ goto cleanup;
+ }
+
+ ret = s->do_cmdtest(dev, s, &async->cmd);
+
+ if (async->cmd.flags & TRIG_BOGUS || ret) {
+ DPRINTK("test returned %d\n", ret);
+ user_cmd = async->cmd;
+ /* restore chanlist pointer before copying back */
+ user_cmd.chanlist = chanlist_saver;
+ user_cmd.data = NULL;
+ if (copy_to_user(arg, &user_cmd, sizeof(comedi_cmd))) {
+ DPRINTK("fault writing cmd\n");
+ ret = -EFAULT;
+ goto cleanup;
+ }
+ ret = -EAGAIN;
+ goto cleanup;
+ }
+
+ if (!async->prealloc_bufsz) {
+ ret = -ENOMEM;
+ DPRINTK("no buffer (?)\n");
+ goto cleanup;
+ }
+
+ comedi_reset_async_buf(async);
+
+ async->cb_mask =
+ COMEDI_CB_EOA | COMEDI_CB_BLOCK | COMEDI_CB_ERROR |
+ COMEDI_CB_OVERFLOW;
+ if (async->cmd.flags & TRIG_WAKE_EOS)
+ async->cb_mask |= COMEDI_CB_EOS;
+
+ comedi_set_subdevice_runflags(s, ~0, SRF_USER | SRF_RUNNING);
+
+#ifdef CONFIG_COMEDI_RT
+ if (async->cmd.flags & TRIG_RT) {
+ if (comedi_switch_to_rt(dev) == 0)
+ comedi_set_subdevice_runflags(s, SRF_RT, SRF_RT);
+ }
+#endif
+
+ ret = s->do_cmd(dev, s);
+ if (ret == 0)
+ return 0;
+
+cleanup:
+ do_become_nonbusy(dev, s);
+
+ return ret;
+}
+
+/*
+ COMEDI_CMDTEST
+ command testing ioctl
+
+ arg:
+ pointer to cmd structure
+
+ reads:
+ cmd structure at arg
+ channel/range list
+
+ writes:
+ modified cmd structure at arg
+
+*/
+static int do_cmdtest_ioctl(comedi_device *dev, void *arg, void *file)
+{
+ comedi_cmd user_cmd;
+ comedi_subdevice *s;
+ int ret = 0;
+ unsigned int *chanlist = NULL;
+ unsigned int *chanlist_saver = NULL;
+
+ if (copy_from_user(&user_cmd, arg, sizeof(comedi_cmd))) {
+ DPRINTK("bad cmd address\n");
+ return -EFAULT;
+ }
+ /* save user's chanlist pointer so it can be restored later */
+ chanlist_saver = user_cmd.chanlist;
+
+ if (user_cmd.subdev >= dev->n_subdevices) {
+ DPRINTK("%d no such subdevice\n", user_cmd.subdev);
+ return -ENODEV;
+ }
+
+ s = dev->subdevices + user_cmd.subdev;
+ if (s->type == COMEDI_SUBD_UNUSED) {
+ DPRINTK("%d not valid subdevice\n", user_cmd.subdev);
+ return -EIO;
+ }
+
+ if (!s->do_cmd || !s->do_cmdtest) {
+ DPRINTK("subdevice %i does not support commands\n",
+ user_cmd.subdev);
+ return -EIO;
+ }
+
+ /* make sure channel/gain list isn't too long */
+ if (user_cmd.chanlist_len > s->len_chanlist) {
+ DPRINTK("channel/gain list too long %d > %d\n",
+ user_cmd.chanlist_len, s->len_chanlist);
+ ret = -EINVAL;
+ goto cleanup;
+ }
+
+ /* load channel/gain list */
+ if (user_cmd.chanlist) {
+ chanlist =
+ kmalloc(user_cmd.chanlist_len * sizeof(int), GFP_KERNEL);
+ if (!chanlist) {
+ DPRINTK("allocation failed\n");
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ if (copy_from_user(chanlist, user_cmd.chanlist,
+ user_cmd.chanlist_len * sizeof(int))) {
+ DPRINTK("fault reading chanlist\n");
+ ret = -EFAULT;
+ goto cleanup;
+ }
+
+ /* make sure each element in channel/gain list is valid */
+ ret = check_chanlist(s, user_cmd.chanlist_len, chanlist);
+ if (ret < 0) {
+ DPRINTK("bad chanlist\n");
+ goto cleanup;
+ }
+
+ user_cmd.chanlist = chanlist;
+ }
+
+ ret = s->do_cmdtest(dev, s, &user_cmd);
+
+ /* restore chanlist pointer before copying back */
+ user_cmd.chanlist = chanlist_saver;
+
+ if (copy_to_user(arg, &user_cmd, sizeof(comedi_cmd))) {
+ DPRINTK("bad cmd address\n");
+ ret = -EFAULT;
+ goto cleanup;
+ }
+cleanup:
+ kfree(chanlist);
+
+ return ret;
+}
+
+/*
+ COMEDI_LOCK
+ lock subdevice
+
+ arg:
+ subdevice number
+
+ reads:
+ none
+
+ writes:
+ none
+
+*/
+
+static int do_lock_ioctl(comedi_device *dev, unsigned int arg, void *file)
+{
+ int ret = 0;
+ unsigned long flags;
+ comedi_subdevice *s;
+
+ if (arg >= dev->n_subdevices)
+ return -EINVAL;
+ s = dev->subdevices + arg;
+
+ comedi_spin_lock_irqsave(&s->spin_lock, flags);
+ if (s->busy || s->lock)
+ ret = -EBUSY;
+ else
+ s->lock = file;
+ comedi_spin_unlock_irqrestore(&s->spin_lock, flags);
+
+ if (ret < 0)
+ return ret;
+
+#if 0
+ if (s->lock_f)
+ ret = s->lock_f(dev, s);
+#endif
+
+ return ret;
+}
+
+/*
+ COMEDI_UNLOCK
+ unlock subdevice
+
+ arg:
+ subdevice number
+
+ reads:
+ none
+
+ writes:
+ none
+
+ This function isn't protected by the semaphore, since
+ we already own the lock.
+*/
+static int do_unlock_ioctl(comedi_device *dev, unsigned int arg, void *file)
+{
+ comedi_subdevice *s;
+
+ if (arg >= dev->n_subdevices)
+ return -EINVAL;
+ s = dev->subdevices + arg;
+
+ if (s->busy)
+ return -EBUSY;
+
+ if (s->lock && s->lock != file)
+ return -EACCES;
+
+ if (s->lock == file) {
+#if 0
+ if (s->unlock)
+ s->unlock(dev, s);
+#endif
+
+ s->lock = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ COMEDI_CANCEL
+ cancel acquisition ioctl
+
+ arg:
+ subdevice number
+
+ reads:
+ nothing
+
+ writes:
+ nothing
+
+*/
+static int do_cancel_ioctl(comedi_device *dev, unsigned int arg, void *file)
+{
+ comedi_subdevice *s;
+
+ if (arg >= dev->n_subdevices)
+ return -EINVAL;
+ s = dev->subdevices + arg;
+ if (s->async == NULL)
+ return -EINVAL;
+
+ if (s->lock && s->lock != file)
+ return -EACCES;
+
+ if (!s->busy)
+ return 0;
+
+ if (s->busy != file)
+ return -EBUSY;
+
+ return do_cancel(dev, s);
+}
+
+/*
+ COMEDI_POLL ioctl
+ instructs driver to synchronize buffers
+
+ arg:
+ subdevice number
+
+ reads:
+ nothing
+
+ writes:
+ nothing
+
+*/
+static int do_poll_ioctl(comedi_device *dev, unsigned int arg, void *file)
+{
+ comedi_subdevice *s;
+
+ if (arg >= dev->n_subdevices)
+ return -EINVAL;
+ s = dev->subdevices + arg;
+
+ if (s->lock && s->lock != file)
+ return -EACCES;
+
+ if (!s->busy)
+ return 0;
+
+ if (s->busy != file)
+ return -EBUSY;
+
+ if (s->poll)
+ return s->poll(dev, s);
+
+ return -EINVAL;
+}
+
+static int do_cancel(comedi_device *dev, comedi_subdevice *s)
+{
+ int ret = 0;
+
+ if ((comedi_get_subdevice_runflags(s) & SRF_RUNNING) && s->cancel)
+ ret = s->cancel(dev, s);
+
+ do_become_nonbusy(dev, s);
+
+ return ret;
+}
+
+void comedi_unmap(struct vm_area_struct *area)
+{
+ comedi_async *async;
+ comedi_device *dev;
+
+ async = area->vm_private_data;
+ dev = async->subdevice->device;
+
+ mutex_lock(&dev->mutex);
+ async->mmap_count--;
+ mutex_unlock(&dev->mutex);
+}
+
+static struct vm_operations_struct comedi_vm_ops = {
+ .close = comedi_unmap,
+};
+
+static int comedi_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ const unsigned minor = iminor(file->f_dentry->d_inode);
+ struct comedi_device_file_info *dev_file_info =
+ comedi_get_device_file_info(minor);
+ comedi_device *dev = dev_file_info->device;
+ comedi_async *async = NULL;
+ unsigned long start = vma->vm_start;
+ unsigned long size;
+ int n_pages;
+ int i;
+ int retval;
+ comedi_subdevice *s;
+
+ mutex_lock(&dev->mutex);
+ if (!dev->attached) {
+ DPRINTK("no driver configured on comedi%i\n", dev->minor);
+ retval = -ENODEV;
+ goto done;
+ }
+ if (vma->vm_flags & VM_WRITE)
+ s = comedi_get_write_subdevice(dev_file_info);
+ else
+ s = comedi_get_read_subdevice(dev_file_info);
+
+ if (s == NULL) {
+ retval = -EINVAL;
+ goto done;
+ }
+ async = s->async;
+ if (async == NULL) {
+ retval = -EINVAL;
+ goto done;
+ }
+
+ if (vma->vm_pgoff != 0) {
+ DPRINTK("comedi: mmap() offset must be 0.\n");
+ retval = -EINVAL;
+ goto done;
+ }
+
+ size = vma->vm_end - vma->vm_start;
+ if (size > async->prealloc_bufsz) {
+ retval = -EFAULT;
+ goto done;
+ }
+ if (size & (~PAGE_MASK)) {
+ retval = -EFAULT;
+ goto done;
+ }
+
+ n_pages = size >> PAGE_SHIFT;
+ for (i = 0; i < n_pages; ++i) {
+ if (remap_pfn_range(vma, start,
+ page_to_pfn(virt_to_page(async->
+ buf_page_list[i].
+ virt_addr)),
+ PAGE_SIZE, PAGE_SHARED)) {
+ retval = -EAGAIN;
+ goto done;
+ }
+ start += PAGE_SIZE;
+ }
+
+ vma->vm_ops = &comedi_vm_ops;
+ vma->vm_private_data = async;
+
+ async->mmap_count++;
+
+ retval = 0;
+done:
+ mutex_unlock(&dev->mutex);
+ return retval;
+}
+
+static unsigned int comedi_poll(struct file *file, poll_table *wait)
+{
+ unsigned int mask = 0;
+ const unsigned minor = iminor(file->f_dentry->d_inode);
+ struct comedi_device_file_info *dev_file_info =
+ comedi_get_device_file_info(minor);
+ comedi_device *dev = dev_file_info->device;
+ comedi_subdevice *read_subdev;
+ comedi_subdevice *write_subdev;
+
+ mutex_lock(&dev->mutex);
+ if (!dev->attached) {
+ DPRINTK("no driver configured on comedi%i\n", dev->minor);
+ mutex_unlock(&dev->mutex);
+ return 0;
+ }
+
+ mask = 0;
+ read_subdev = comedi_get_read_subdevice(dev_file_info);
+ if (read_subdev) {
+ poll_wait(file, &read_subdev->async->wait_head, wait);
+ if (!read_subdev->busy
+ || comedi_buf_read_n_available(read_subdev->async) > 0
+ || !(comedi_get_subdevice_runflags(read_subdev) &
+ SRF_RUNNING)) {
+ mask |= POLLIN | POLLRDNORM;
+ }
+ }
+ write_subdev = comedi_get_write_subdevice(dev_file_info);
+ if (write_subdev) {
+ poll_wait(file, &write_subdev->async->wait_head, wait);
+ comedi_buf_write_alloc(write_subdev->async,
+ write_subdev->async->prealloc_bufsz);
+ if (!write_subdev->busy
+ || !(comedi_get_subdevice_runflags(write_subdev) &
+ SRF_RUNNING)
+ || comedi_buf_write_n_allocated(write_subdev->async) >=
+ bytes_per_sample(write_subdev->async->subdevice)) {
+ mask |= POLLOUT | POLLWRNORM;
+ }
+ }
+
+ mutex_unlock(&dev->mutex);
+ return mask;
+}
+
+static ssize_t comedi_write(struct file *file, const char *buf, size_t nbytes,
+ loff_t *offset)
+{
+ comedi_subdevice *s;
+ comedi_async *async;
+ int n, m, count = 0, retval = 0;
+ DECLARE_WAITQUEUE(wait, current);
+ const unsigned minor = iminor(file->f_dentry->d_inode);
+ struct comedi_device_file_info *dev_file_info =
+ comedi_get_device_file_info(minor);
+ comedi_device *dev = dev_file_info->device;
+
+ if (!dev->attached) {
+ DPRINTK("no driver configured on comedi%i\n", dev->minor);
+ retval = -ENODEV;
+ goto done;
+ }
+
+ s = comedi_get_write_subdevice(dev_file_info);
+ if (s == NULL) {
+ retval = -EIO;
+ goto done;
+ }
+ async = s->async;
+
+ if (!nbytes) {
+ retval = 0;
+ goto done;
+ }
+ if (!s->busy) {
+ retval = 0;
+ goto done;
+ }
+ if (s->busy != file) {
+ retval = -EACCES;
+ goto done;
+ }
+ add_wait_queue(&async->wait_head, &wait);
+ while (nbytes > 0 && !retval) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ n = nbytes;
+
+ m = n;
+ if (async->buf_write_ptr + m > async->prealloc_bufsz)
+ m = async->prealloc_bufsz - async->buf_write_ptr;
+ comedi_buf_write_alloc(async, async->prealloc_bufsz);
+ if (m > comedi_buf_write_n_allocated(async))
+ m = comedi_buf_write_n_allocated(async);
+ if (m < n)
+ n = m;
+
+ if (n == 0) {
+ if (!(comedi_get_subdevice_runflags(s) & SRF_RUNNING)) {
+ if (comedi_get_subdevice_runflags(s) &
+ SRF_ERROR) {
+ retval = -EPIPE;
+ } else {
+ retval = 0;
+ }
+ do_become_nonbusy(dev, s);
+ break;
+ }
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ if (!s->busy)
+ break;
+ if (s->busy != file) {
+ retval = -EACCES;
+ break;
+ }
+ continue;
+ }
+
+ m = copy_from_user(async->prealloc_buf + async->buf_write_ptr,
+ buf, n);
+ if (m) {
+ n -= m;
+ retval = -EFAULT;
+ }
+ comedi_buf_write_free(async, n);
+
+ count += n;
+ nbytes -= n;
+
+ buf += n;
+ break; /* makes device work like a pipe */
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&async->wait_head, &wait);
+
+done:
+ return count ? count : retval;
+}
+
+static ssize_t comedi_read(struct file *file, char *buf, size_t nbytes,
+ loff_t *offset)
+{
+ comedi_subdevice *s;
+ comedi_async *async;
+ int n, m, count = 0, retval = 0;
+ DECLARE_WAITQUEUE(wait, current);
+ const unsigned minor = iminor(file->f_dentry->d_inode);
+ struct comedi_device_file_info *dev_file_info =
+ comedi_get_device_file_info(minor);
+ comedi_device *dev = dev_file_info->device;
+
+ if (!dev->attached) {
+ DPRINTK("no driver configured on comedi%i\n", dev->minor);
+ retval = -ENODEV;
+ goto done;
+ }
+
+ s = comedi_get_read_subdevice(dev_file_info);
+ if (s == NULL) {
+ retval = -EIO;
+ goto done;
+ }
+ async = s->async;
+ if (!nbytes) {
+ retval = 0;
+ goto done;
+ }
+ if (!s->busy) {
+ retval = 0;
+ goto done;
+ }
+ if (s->busy != file) {
+ retval = -EACCES;
+ goto done;
+ }
+
+ add_wait_queue(&async->wait_head, &wait);
+ while (nbytes > 0 && !retval) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ n = nbytes;
+
+ m = comedi_buf_read_n_available(async);
+ /* printk("%d available\n",m); */
+ if (async->buf_read_ptr + m > async->prealloc_bufsz)
+ m = async->prealloc_bufsz - async->buf_read_ptr;
+ /* printk("%d contiguous\n",m); */
+ if (m < n)
+ n = m;
+
+ if (n == 0) {
+ if (!(comedi_get_subdevice_runflags(s) & SRF_RUNNING)) {
+ do_become_nonbusy(dev, s);
+ if (comedi_get_subdevice_runflags(s) &
+ SRF_ERROR) {
+ retval = -EPIPE;
+ } else {
+ retval = 0;
+ }
+ break;
+ }
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ if (!s->busy) {
+ retval = 0;
+ break;
+ }
+ if (s->busy != file) {
+ retval = -EACCES;
+ break;
+ }
+ continue;
+ }
+ m = copy_to_user(buf, async->prealloc_buf +
+ async->buf_read_ptr, n);
+ if (m) {
+ n -= m;
+ retval = -EFAULT;
+ }
+
+ comedi_buf_read_alloc(async, n);
+ comedi_buf_read_free(async, n);
+
+ count += n;
+ nbytes -= n;
+
+ buf += n;
+ break; /* makes device work like a pipe */
+ }
+ if (!(comedi_get_subdevice_runflags(s) & (SRF_ERROR | SRF_RUNNING)) &&
+ async->buf_read_count - async->buf_write_count == 0) {
+ do_become_nonbusy(dev, s);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&async->wait_head, &wait);
+
+done:
+ return count ? count : retval;
+}
+
+/*
+ This function restores a subdevice to an idle state.
+ */
+void do_become_nonbusy(comedi_device *dev, comedi_subdevice *s)
+{
+ comedi_async *async = s->async;
+
+ comedi_set_subdevice_runflags(s, SRF_RUNNING, 0);
+#ifdef CONFIG_COMEDI_RT
+ if (comedi_get_subdevice_runflags(s) & SRF_RT) {
+ comedi_switch_to_non_rt(dev);
+ comedi_set_subdevice_runflags(s, SRF_RT, 0);
+ }
+#endif
+ if (async) {
+ comedi_reset_async_buf(async);
+ async->inttrig = NULL;
+ } else {
+ printk(KERN_ERR
+ "BUG: (?) do_become_nonbusy called with async=0\n");
+ }
+
+ s->busy = NULL;
+}
+
+static int comedi_open(struct inode *inode, struct file *file)
+{
+ char mod[32];
+ const unsigned minor = iminor(inode);
+ struct comedi_device_file_info *dev_file_info =
+ comedi_get_device_file_info(minor);
+ comedi_device *dev = dev_file_info->device;
+ if (dev == NULL) {
+ DPRINTK("invalid minor number\n");
+ return -ENODEV;
+ }
+
+ /* This is slightly hacky, but we want module autoloading
+ * to work for root.
+ * case: user opens device, attached -> ok
+ * case: user opens device, unattached, in_request_module=0 -> autoload
+ * case: user opens device, unattached, in_request_module=1 -> fail
+ * case: root opens device, attached -> ok
+ * case: root opens device, unattached, in_request_module=1 -> ok
+ * (typically called from modprobe)
+ * case: root opens device, unattached, in_request_module=0 -> autoload
+ *
+ * The last could be changed to "-> ok", which would deny root
+ * autoloading.
+ */
+ mutex_lock(&dev->mutex);
+ if (dev->attached)
+ goto ok;
+ if (!capable(CAP_SYS_MODULE) && dev->in_request_module) {
+ DPRINTK("in request module\n");
+ mutex_unlock(&dev->mutex);
+ return -ENODEV;
+ }
+ if (capable(CAP_SYS_MODULE) && dev->in_request_module)
+ goto ok;
+
+ dev->in_request_module = 1;
+
+ sprintf(mod, "char-major-%i-%i", COMEDI_MAJOR, dev->minor);
+#ifdef CONFIG_KMOD
+ mutex_unlock(&dev->mutex);
+ request_module(mod);
+ mutex_lock(&dev->mutex);
+#endif
+
+ dev->in_request_module = 0;
+
+ if (!dev->attached && !capable(CAP_SYS_MODULE)) {
+ DPRINTK("not attached and not CAP_SYS_MODULE\n");
+ mutex_unlock(&dev->mutex);
+ return -ENODEV;
+ }
+ok:
+ __module_get(THIS_MODULE);
+
+ if (dev->attached) {
+ if (!try_module_get(dev->driver->module)) {
+ module_put(THIS_MODULE);
+ mutex_unlock(&dev->mutex);
+ return -ENOSYS;
+ }
+ }
+
+ if (dev->attached && dev->use_count == 0 && dev->open)
+ dev->open(dev);
+
+ dev->use_count++;
+
+ mutex_unlock(&dev->mutex);
+
+ return 0;
+}
+
+static int comedi_close(struct inode *inode, struct file *file)
+{
+ const unsigned minor = iminor(inode);
+ struct comedi_device_file_info *dev_file_info =
+ comedi_get_device_file_info(minor);
+ comedi_device *dev = dev_file_info->device;
+ comedi_subdevice *s = NULL;
+ int i;
+
+ mutex_lock(&dev->mutex);
+
+ if (dev->subdevices) {
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = dev->subdevices + i;
+
+ if (s->busy == file)
+ do_cancel(dev, s);
+ if (s->lock == file)
+ s->lock = NULL;
+ }
+ }
+ if (dev->attached && dev->use_count == 1 && dev->close)
+ dev->close(dev);
+
+ module_put(THIS_MODULE);
+ if (dev->attached)
+ module_put(dev->driver->module);
+
+ dev->use_count--;
+
+ mutex_unlock(&dev->mutex);
+
+ if (file->f_flags & FASYNC)
+ comedi_fasync(-1, file, 0);
+
+ return 0;
+}
+
+static int comedi_fasync(int fd, struct file *file, int on)
+{
+ const unsigned minor = iminor(file->f_dentry->d_inode);
+ struct comedi_device_file_info *dev_file_info =
+ comedi_get_device_file_info(minor);
+
+ comedi_device *dev = dev_file_info->device;
+
+ return fasync_helper(fd, file, on, &dev->async_queue);
+}
+
+const struct file_operations comedi_fops = {
+ .owner = THIS_MODULE,
+#ifdef HAVE_UNLOCKED_IOCTL
+ .unlocked_ioctl = comedi_unlocked_ioctl,
+#else
+ .ioctl = comedi_ioctl,
+#endif
+#ifdef HAVE_COMPAT_IOCTL
+ .compat_ioctl = comedi_compat_ioctl,
+#endif
+ .open = comedi_open,
+ .release = comedi_close,
+ .read = comedi_read,
+ .write = comedi_write,
+ .mmap = comedi_mmap,
+ .poll = comedi_poll,
+ .fasync = comedi_fasync,
+};
+
+struct class *comedi_class;
+static struct cdev comedi_cdev;
+
+static void comedi_cleanup_legacy_minors(void)
+{
+ unsigned i;
+
+ for (i = 0; i < COMEDI_NUM_LEGACY_MINORS; i++)
+ comedi_free_board_minor(i);
+}
+
+static int __init comedi_init(void)
+{
+ int i;
+ int retval;
+
+ printk(KERN_INFO "comedi: version " COMEDI_RELEASE
+ " - http://www.comedi.org\n");
+
+ memset(comedi_file_info_table, 0,
+ sizeof(struct comedi_device_file_info *) * COMEDI_NUM_MINORS);
+
+ retval = register_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
+ COMEDI_NUM_MINORS, "comedi");
+ if (retval)
+ return -EIO;
+ cdev_init(&comedi_cdev, &comedi_fops);
+ comedi_cdev.owner = THIS_MODULE;
+ kobject_set_name(&comedi_cdev.kobj, "comedi");
+ if (cdev_add(&comedi_cdev, MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS)) {
+ unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
+ COMEDI_NUM_MINORS);
+ return -EIO;
+ }
+ comedi_class = class_create(THIS_MODULE, "comedi");
+ if (IS_ERR(comedi_class)) {
+ printk("comedi: failed to create class");
+ cdev_del(&comedi_cdev);
+ unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
+ COMEDI_NUM_MINORS);
+ return PTR_ERR(comedi_class);
+ }
+
+ /* XXX requires /proc interface */
+ comedi_proc_init();
+
+ /* create devices files for legacy/manual use */
+ for (i = 0; i < COMEDI_NUM_LEGACY_MINORS; i++) {
+ int minor;
+ minor = comedi_alloc_board_minor(NULL);
+ if (minor < 0) {
+ comedi_cleanup_legacy_minors();
+ cdev_del(&comedi_cdev);
+ unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
+ COMEDI_NUM_MINORS);
+ return minor;
+ }
+ }
+
+ comedi_rt_init();
+
+ comedi_register_ioctl32();
+
+ return 0;
+}
+
+static void __exit comedi_cleanup(void)
+{
+ int i;
+
+ comedi_cleanup_legacy_minors();
+ for (i = 0; i < COMEDI_NUM_MINORS; ++i)
+ BUG_ON(comedi_file_info_table[i]);
+
+
+ class_destroy(comedi_class);
+ cdev_del(&comedi_cdev);
+ unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS);
+
+ comedi_proc_cleanup();
+
+ comedi_rt_cleanup();
+
+ comedi_unregister_ioctl32();
+}
+
+module_init(comedi_init);
+module_exit(comedi_cleanup);
+
+void comedi_error(const comedi_device *dev, const char *s)
+{
+ rt_printk("comedi%d: %s: %s\n", dev->minor, dev->driver->driver_name,
+ s);
+}
+
+void comedi_event(comedi_device *dev, comedi_subdevice *s)
+{
+ comedi_async *async = s->async;
+ unsigned runflags = 0;
+ unsigned runflags_mask = 0;
+
+ /* DPRINTK("comedi_event 0x%x\n",mask); */
+
+ if ((comedi_get_subdevice_runflags(s) & SRF_RUNNING) == 0)
+ return;
+
+ if (s->async->
+ events & (COMEDI_CB_EOA | COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)) {
+ runflags_mask |= SRF_RUNNING;
+ }
+ /* remember if an error event has occured, so an error
+ * can be returned the next time the user does a read() */
+ if (s->async->events & (COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)) {
+ runflags_mask |= SRF_ERROR;
+ runflags |= SRF_ERROR;
+ }
+ if (runflags_mask) {
+ /*sets SRF_ERROR and SRF_RUNNING together atomically */
+ comedi_set_subdevice_runflags(s, runflags_mask, runflags);
+ }
+
+ if (async->cb_mask & s->async->events) {
+ if (comedi_get_subdevice_runflags(s) & SRF_USER) {
+
+ if (dev->rt) {
+#ifdef CONFIG_COMEDI_RT
+ /* pend wake up */
+ comedi_rt_pend_wakeup(&async->wait_head);
+#else
+ printk
+ ("BUG: comedi_event() code unreachable\n");
+#endif
+ } else {
+ wake_up_interruptible(&async->wait_head);
+ if (s->subdev_flags & SDF_CMD_READ) {
+ kill_fasync(&dev->async_queue, SIGIO,
+ POLL_IN);
+ }
+ if (s->subdev_flags & SDF_CMD_WRITE) {
+ kill_fasync(&dev->async_queue, SIGIO,
+ POLL_OUT);
+ }
+ }
+ } else {
+ if (async->cb_func)
+ async->cb_func(s->async->events, async->cb_arg);
+ /* XXX bug here. If subdevice A is rt, and
+ * subdevice B tries to callback to a normal
+ * linux kernel function, it will be at the
+ * wrong priority. Since this isn't very
+ * common, I'm not going to worry about it. */
+ }
+ }
+ s->async->events = 0;
+}
+
+void comedi_set_subdevice_runflags(comedi_subdevice *s, unsigned mask,
+ unsigned bits)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&s->spin_lock, flags);
+ s->runflags &= ~mask;
+ s->runflags |= (bits & mask);
+ comedi_spin_unlock_irqrestore(&s->spin_lock, flags);
+}
+
+unsigned comedi_get_subdevice_runflags(comedi_subdevice *s)
+{
+ unsigned long flags;
+ unsigned runflags;
+
+ comedi_spin_lock_irqsave(&s->spin_lock, flags);
+ runflags = s->runflags;
+ comedi_spin_unlock_irqrestore(&s->spin_lock, flags);
+ return runflags;
+}
+
+static int is_device_busy(comedi_device *dev)
+{
+ comedi_subdevice *s;
+ int i;
+
+ if (!dev->attached)
+ return 0;
+
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = dev->subdevices + i;
+ if (s->busy)
+ return 1;
+ if (s->async && s->async->mmap_count)
+ return 1;
+ }
+
+ return 0;
+}
+
+void comedi_device_init(comedi_device *dev)
+{
+ memset(dev, 0, sizeof(comedi_device));
+ spin_lock_init(&dev->spinlock);
+ mutex_init(&dev->mutex);
+ dev->minor = -1;
+}
+
+void comedi_device_cleanup(comedi_device *dev)
+{
+ if (dev == NULL)
+ return;
+ mutex_lock(&dev->mutex);
+ comedi_device_detach(dev);
+ mutex_unlock(&dev->mutex);
+ mutex_destroy(&dev->mutex);
+}
+
+int comedi_alloc_board_minor(struct device *hardware_device)
+{
+ unsigned long flags;
+ struct comedi_device_file_info *info;
+ device_create_result_type *csdev;
+ unsigned i;
+
+ info = kzalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
+ if (info == NULL)
+ return -ENOMEM;
+ info->device = kzalloc(sizeof(comedi_device), GFP_KERNEL);
+ if (info->device == NULL) {
+ kfree(info);
+ return -ENOMEM;
+ }
+ comedi_device_init(info->device);
+ comedi_spin_lock_irqsave(&comedi_file_info_table_lock, flags);
+ for (i = 0; i < COMEDI_NUM_BOARD_MINORS; ++i) {
+ if (comedi_file_info_table[i] == NULL) {
+ comedi_file_info_table[i] = info;
+ break;
+ }
+ }
+ comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
+ if (i == COMEDI_NUM_BOARD_MINORS) {
+ comedi_device_cleanup(info->device);
+ kfree(info->device);
+ kfree(info);
+ rt_printk
+ ("comedi: error: ran out of minor numbers for board device files.\n");
+ return -EBUSY;
+ }
+ info->device->minor = i;
+ csdev = COMEDI_DEVICE_CREATE(comedi_class, NULL,
+ MKDEV(COMEDI_MAJOR, i), NULL,
+ hardware_device, "comedi%i", i);
+ if (!IS_ERR(csdev))
+ info->device->class_dev = csdev;
+
+ return i;
+}
+
+void comedi_free_board_minor(unsigned minor)
+{
+ unsigned long flags;
+ struct comedi_device_file_info *info;
+
+ BUG_ON(minor >= COMEDI_NUM_BOARD_MINORS);
+ comedi_spin_lock_irqsave(&comedi_file_info_table_lock, flags);
+ info = comedi_file_info_table[minor];
+ comedi_file_info_table[minor] = NULL;
+ comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
+
+ if (info) {
+ comedi_device *dev = info->device;
+ if (dev) {
+ if (dev->class_dev) {
+ device_destroy(comedi_class,
+ MKDEV(COMEDI_MAJOR, dev->minor));
+ }
+ comedi_device_cleanup(dev);
+ kfree(dev);
+ }
+ kfree(info);
+ }
+}
+
+int comedi_alloc_subdevice_minor(comedi_device *dev, comedi_subdevice *s)
+{
+ unsigned long flags;
+ struct comedi_device_file_info *info;
+ device_create_result_type *csdev;
+ unsigned i;
+
+ info = kmalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
+ if (info == NULL)
+ return -ENOMEM;
+ info->device = dev;
+ info->read_subdevice = s;
+ info->write_subdevice = s;
+ comedi_spin_lock_irqsave(&comedi_file_info_table_lock, flags);
+ for (i = COMEDI_FIRST_SUBDEVICE_MINOR; i < COMEDI_NUM_BOARD_MINORS; ++i) {
+ if (comedi_file_info_table[i] == NULL) {
+ comedi_file_info_table[i] = info;
+ break;
+ }
+ }
+ comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
+ if (i == COMEDI_NUM_MINORS) {
+ kfree(info);
+ rt_printk
+ ("comedi: error: ran out of minor numbers for board device files.\n");
+ return -EBUSY;
+ }
+ s->minor = i;
+ csdev = COMEDI_DEVICE_CREATE(comedi_class, dev->class_dev,
+ MKDEV(COMEDI_MAJOR, i), NULL, NULL,
+ "comedi%i_subd%i", dev->minor,
+ (int)(s - dev->subdevices));
+ if (!IS_ERR(csdev))
+ s->class_dev = csdev;
+
+ return i;
+}
+
+void comedi_free_subdevice_minor(comedi_subdevice *s)
+{
+ unsigned long flags;
+ struct comedi_device_file_info *info;
+
+ if (s == NULL)
+ return;
+ if (s->minor < 0)
+ return;
+
+ BUG_ON(s->minor >= COMEDI_NUM_MINORS);
+ BUG_ON(s->minor < COMEDI_FIRST_SUBDEVICE_MINOR);
+
+ comedi_spin_lock_irqsave(&comedi_file_info_table_lock, flags);
+ info = comedi_file_info_table[s->minor];
+ comedi_file_info_table[s->minor] = NULL;
+ comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
+
+ if (s->class_dev) {
+ device_destroy(comedi_class, MKDEV(COMEDI_MAJOR, s->minor));
+ s->class_dev = NULL;
+ }
+ kfree(info);
+}
+
+struct comedi_device_file_info *comedi_get_device_file_info(unsigned minor)
+{
+ unsigned long flags;
+ struct comedi_device_file_info *info;
+
+ BUG_ON(minor >= COMEDI_NUM_MINORS);
+ comedi_spin_lock_irqsave(&comedi_file_info_table_lock, flags);
+ info = comedi_file_info_table[minor];
+ comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
+ return info;
+}
diff --git a/drivers/staging/comedi/comedi_fops.h b/drivers/staging/comedi/comedi_fops.h
new file mode 100644
index 000000000000..63f8df558e85
--- /dev/null
+++ b/drivers/staging/comedi/comedi_fops.h
@@ -0,0 +1,8 @@
+
+#ifndef _COMEDI_FOPS_H
+#define _COMEDI_FOPS_H
+
+extern struct class *comedi_class;
+extern const struct file_operations comedi_fops;
+
+#endif /* _COMEDI_FOPS_H */
diff --git a/drivers/staging/comedi/comedi_ksyms.c b/drivers/staging/comedi/comedi_ksyms.c
new file mode 100644
index 000000000000..90d57282efb8
--- /dev/null
+++ b/drivers/staging/comedi/comedi_ksyms.c
@@ -0,0 +1,77 @@
+/*
+ module/exp_ioctl.c
+ exported comedi functions
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-8 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#define __NO_VERSION__
+#ifndef EXPORT_SYMTAB
+#define EXPORT_SYMTAB
+#endif
+
+#include "comedidev.h"
+
+/* for drivers */
+EXPORT_SYMBOL(comedi_driver_register);
+EXPORT_SYMBOL(comedi_driver_unregister);
+//EXPORT_SYMBOL(comedi_bufcheck);
+//EXPORT_SYMBOL(comedi_done);
+//EXPORT_SYMBOL(comedi_error_done);
+EXPORT_SYMBOL(comedi_error);
+//EXPORT_SYMBOL(comedi_eobuf);
+//EXPORT_SYMBOL(comedi_eos);
+EXPORT_SYMBOL(comedi_event);
+EXPORT_SYMBOL(comedi_get_subdevice_runflags);
+EXPORT_SYMBOL(comedi_set_subdevice_runflags);
+EXPORT_SYMBOL(range_bipolar10);
+EXPORT_SYMBOL(range_bipolar5);
+EXPORT_SYMBOL(range_bipolar2_5);
+EXPORT_SYMBOL(range_unipolar10);
+EXPORT_SYMBOL(range_unipolar5);
+EXPORT_SYMBOL(range_unknown);
+#ifdef CONFIG_COMEDI_RT
+EXPORT_SYMBOL(comedi_free_irq);
+EXPORT_SYMBOL(comedi_request_irq);
+EXPORT_SYMBOL(comedi_switch_to_rt);
+EXPORT_SYMBOL(comedi_switch_to_non_rt);
+EXPORT_SYMBOL(rt_pend_call);
+#endif
+#ifdef CONFIG_COMEDI_DEBUG
+EXPORT_SYMBOL(comedi_debug);
+#endif
+EXPORT_SYMBOL_GPL(comedi_alloc_board_minor);
+EXPORT_SYMBOL_GPL(comedi_free_board_minor);
+EXPORT_SYMBOL_GPL(comedi_pci_auto_config);
+EXPORT_SYMBOL_GPL(comedi_pci_auto_unconfig);
+
+/* for kcomedilib */
+EXPORT_SYMBOL(check_chanlist);
+EXPORT_SYMBOL_GPL(comedi_get_device_file_info);
+
+EXPORT_SYMBOL(comedi_buf_put);
+EXPORT_SYMBOL(comedi_buf_get);
+EXPORT_SYMBOL(comedi_buf_read_n_available);
+EXPORT_SYMBOL(comedi_buf_write_free);
+EXPORT_SYMBOL(comedi_buf_write_alloc);
+EXPORT_SYMBOL(comedi_buf_read_free);
+EXPORT_SYMBOL(comedi_buf_read_alloc);
+EXPORT_SYMBOL(comedi_buf_memcpy_to);
+EXPORT_SYMBOL(comedi_buf_memcpy_from);
+EXPORT_SYMBOL(comedi_reset_async_buf);
diff --git a/drivers/staging/comedi/comedi_rt.h b/drivers/staging/comedi/comedi_rt.h
new file mode 100644
index 000000000000..61852bf5adcc
--- /dev/null
+++ b/drivers/staging/comedi/comedi_rt.h
@@ -0,0 +1,150 @@
+/*
+ module/comedi_rt.h
+ header file for real-time 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef _COMEDI_RT_H
+#define _COMEDI_RT_H
+
+#ifndef _COMEDIDEV_H
+#error comedi_rt.h should only be included by comedidev.h
+#endif
+
+#include <linux/version.h>
+#include <linux/kdev_t.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+
+#ifdef CONFIG_COMEDI_RT
+
+#ifdef CONFIG_COMEDI_RTAI
+#include <rtai.h>
+#include <rtai_sched.h>
+#include <rtai_version.h>
+#endif
+#ifdef CONFIG_COMEDI_RTL
+#include <rtl_core.h>
+#include <rtl_time.h>
+/* #ifdef RTLINUX_VERSION_CODE */
+#include <rtl_sync.h>
+/* #endif */
+#define rt_printk rtl_printf
+#endif
+#ifdef CONFIG_COMEDI_FUSION
+#define rt_printk(format, args...) printk(format , ## args)
+#endif /* CONFIG_COMEDI_FUSION */
+#ifdef CONFIG_PRIORITY_IRQ
+#define rt_printk printk
+#endif
+
+int comedi_request_irq(unsigned int irq, irqreturn_t(*handler) (int,
+ void *PT_REGS_ARG), unsigned long flags, const char *device,
+ comedi_device *dev_id);
+void comedi_free_irq(unsigned int irq, comedi_device *dev_id);
+void comedi_rt_init(void);
+void comedi_rt_cleanup(void);
+int comedi_switch_to_rt(comedi_device *dev);
+void comedi_switch_to_non_rt(comedi_device *dev);
+void comedi_rt_pend_wakeup(wait_queue_head_t *q);
+extern int rt_pend_call(void (*func) (int arg1, void *arg2), int arg1,
+ void *arg2);
+
+#else
+
+#define comedi_request_irq(a, b, c, d, e) request_irq(a, b, c, d, e)
+#define comedi_free_irq(a, b) free_irq(a, b)
+#define comedi_rt_init() do {} while (0)
+#define comedi_rt_cleanup() do {} while (0)
+#define comedi_switch_to_rt(a) (-1)
+#define comedi_switch_to_non_rt(a) do {} while (0)
+#define comedi_rt_pend_wakeup(a) do {} while (0)
+
+#define rt_printk(format, args...) printk(format, ##args)
+
+#endif
+
+/* Define a spin_lock_irqsave function that will work with rt or without.
+ * Use inline functions instead of just macros to enforce some type checking.
+ */
+#define comedi_spin_lock_irqsave(lock_ptr, flags) \
+ (flags = __comedi_spin_lock_irqsave(lock_ptr))
+
+static inline unsigned long __comedi_spin_lock_irqsave(spinlock_t *lock_ptr)
+{
+ unsigned long flags;
+
+#if defined(CONFIG_COMEDI_RTAI)
+ flags = rt_spin_lock_irqsave(lock_ptr);
+
+#elif defined(CONFIG_COMEDI_RTL)
+ rtl_spin_lock_irqsave(lock_ptr, flags);
+
+#elif defined(CONFIG_COMEDI_RTL_V1)
+ rtl_spin_lock_irqsave(lock_ptr, flags);
+
+#elif defined(CONFIG_COMEDI_FUSION)
+ rthal_spin_lock_irqsave(lock_ptr, flags);
+#else
+ spin_lock_irqsave(lock_ptr, flags);
+
+#endif
+
+ return flags;
+}
+
+static inline void comedi_spin_unlock_irqrestore(spinlock_t *lock_ptr,
+ unsigned long flags)
+{
+
+#if defined(CONFIG_COMEDI_RTAI)
+ rt_spin_unlock_irqrestore(flags, lock_ptr);
+
+#elif defined(CONFIG_COMEDI_RTL)
+ rtl_spin_unlock_irqrestore(lock_ptr, flags);
+
+#elif defined(CONFIG_COMEDI_RTL_V1)
+ rtl_spin_unlock_irqrestore(lock_ptr, flags);
+#elif defined(CONFIG_COMEDI_FUSION)
+ rthal_spin_unlock_irqrestore(lock_ptr, flags);
+#else
+ spin_unlock_irqrestore(lock_ptr, flags);
+
+#endif
+
+}
+
+/* define a RT safe udelay */
+static inline void comedi_udelay(unsigned int usec)
+{
+#if defined(CONFIG_COMEDI_RTAI)
+ static const int nanosec_per_usec = 1000;
+ rt_busy_sleep(usec * nanosec_per_usec);
+#elif defined(CONFIG_COMEDI_RTL)
+ static const int nanosec_per_usec = 1000;
+ rtl_delay(usec * nanosec_per_usec);
+#else
+ udelay(usec);
+#endif
+}
+
+#endif
diff --git a/drivers/staging/comedi/comedidev.h b/drivers/staging/comedi/comedidev.h
new file mode 100644
index 000000000000..3735355d3c58
--- /dev/null
+++ b/drivers/staging/comedi/comedidev.h
@@ -0,0 +1,537 @@
+/*
+ include/linux/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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef _COMEDIDEV_H
+#define _COMEDIDEV_H
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kdev_t.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include "interrupt.h"
+#include <linux/dma-mapping.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include "comedi.h"
+
+#define DPRINTK(format, args...) do { \
+ if (comedi_debug) \
+ printk(KERN_DEBUG "comedi: " format , ## args); \
+} while (0)
+
+#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_INITCLEANUP_NOMODULE(x) \
+ static int __init x ## _init_module(void) \
+ {return comedi_driver_register(&(x));} \
+ static void __exit x ## _cleanup_module(void) \
+ {comedi_driver_unregister(&(x));} \
+ module_init(x ## _init_module); \
+ module_exit(x ## _cleanup_module); \
+
+#define COMEDI_MODULE_MACROS \
+ MODULE_AUTHOR("Comedi http://www.comedi.org"); \
+ MODULE_DESCRIPTION("Comedi low-level driver"); \
+ MODULE_LICENSE("GPL"); \
+
+#define COMEDI_INITCLEANUP(x) \
+ COMEDI_MODULE_MACROS \
+ COMEDI_INITCLEANUP_NOMODULE(x)
+
+#define COMEDI_PCI_INITCLEANUP_NOMODULE(comedi_driver, pci_id_table) \
+ static int __devinit comedi_driver ## _pci_probe(struct pci_dev *dev, \
+ const struct pci_device_id *ent) \
+ { \
+ return comedi_pci_auto_config(dev, comedi_driver.driver_name); \
+ } \
+ static void __devexit comedi_driver ## _pci_remove(struct pci_dev *dev) \
+ { \
+ comedi_pci_auto_unconfig(dev); \
+ } \
+ static struct pci_driver comedi_driver ## _pci_driver = \
+ { \
+ .id_table = pci_id_table, \
+ .probe = &comedi_driver ## _pci_probe, \
+ .remove = __devexit_p(&comedi_driver ## _pci_remove) \
+ }; \
+ static int __init comedi_driver ## _init_module(void) \
+ { \
+ int retval; \
+ retval = comedi_driver_register(&comedi_driver); \
+ if (retval < 0) \
+ return retval; \
+ comedi_driver ## _pci_driver.name = (char *)comedi_driver.driver_name; \
+ return pci_register_driver(&comedi_driver ## _pci_driver); \
+ } \
+ static void __exit comedi_driver ## _cleanup_module(void) \
+ { \
+ pci_unregister_driver(&comedi_driver ## _pci_driver); \
+ comedi_driver_unregister(&comedi_driver); \
+ } \
+ module_init(comedi_driver ## _init_module); \
+ module_exit(comedi_driver ## _cleanup_module);
+
+#define COMEDI_PCI_INITCLEANUP(comedi_driver, pci_id_table) \
+ COMEDI_MODULE_MACROS \
+ COMEDI_PCI_INITCLEANUP_NOMODULE(comedi_driver, pci_id_table)
+
+#define PCI_VENDOR_ID_INOVA 0x104c
+#define PCI_VENDOR_ID_NATINST 0x1093
+#define PCI_VENDOR_ID_DATX 0x1116
+#define PCI_VENDOR_ID_COMPUTERBOARDS 0x1307
+#define PCI_VENDOR_ID_ADVANTECH 0x13fe
+#define PCI_VENDOR_ID_RTD 0x1435
+#define PCI_VENDOR_ID_AMPLICON 0x14dc
+#define PCI_VENDOR_ID_ADLINK 0x144a
+#define PCI_VENDOR_ID_ICP 0x104c
+#define PCI_VENDOR_ID_CONTEC 0x1221
+#define PCI_VENDOR_ID_MEILHAUS 0x1402
+
+#define COMEDI_NUM_MINORS 0x100
+#define COMEDI_NUM_LEGACY_MINORS 0x10
+#define COMEDI_NUM_BOARD_MINORS 0x30
+#define COMEDI_FIRST_SUBDEVICE_MINOR COMEDI_NUM_BOARD_MINORS
+
+typedef struct comedi_device_struct comedi_device;
+typedef struct comedi_subdevice_struct comedi_subdevice;
+typedef struct comedi_async_struct comedi_async;
+typedef struct comedi_driver_struct comedi_driver;
+typedef struct comedi_lrange_struct comedi_lrange;
+
+typedef struct device device_create_result_type;
+
+#define COMEDI_DEVICE_CREATE(cs, parent, devt, drvdata, device, fmt...) \
+ device_create(cs, ((parent) ? (parent) : (device)), devt, drvdata, fmt)
+
+struct comedi_subdevice_struct {
+ comedi_device *device;
+ int type;
+ int n_chan;
+ volatile int subdev_flags;
+ int len_chanlist; /* maximum length of channel/gain list */
+
+ void *private;
+
+ comedi_async *async;
+
+ void *lock;
+ void *busy;
+ unsigned runflags;
+ spinlock_t spin_lock;
+
+ int io_bits;
+
+ lsampl_t maxdata; /* if maxdata==0, use list */
+ const lsampl_t *maxdata_list; /* list is channel specific */
+
+ unsigned int flags;
+ const unsigned int *flaglist;
+
+ unsigned int settling_time_0;
+
+ const comedi_lrange *range_table;
+ const comedi_lrange *const *range_table_list;
+
+ unsigned int *chanlist; /* driver-owned chanlist (not used) */
+
+ int (*insn_read) (comedi_device *, comedi_subdevice *, comedi_insn *,
+ lsampl_t *);
+ int (*insn_write) (comedi_device *, comedi_subdevice *, comedi_insn *,
+ lsampl_t *);
+ int (*insn_bits) (comedi_device *, comedi_subdevice *, comedi_insn *,
+ lsampl_t *);
+ int (*insn_config) (comedi_device *, comedi_subdevice *, comedi_insn *,
+ lsampl_t *);
+
+ int (*do_cmd) (comedi_device *, comedi_subdevice *);
+ int (*do_cmdtest) (comedi_device *, comedi_subdevice *, comedi_cmd *);
+ int (*poll) (comedi_device *, comedi_subdevice *);
+ int (*cancel) (comedi_device *, comedi_subdevice *);
+ /* int (*do_lock)(comedi_device *,comedi_subdevice *); */
+ /* int (*do_unlock)(comedi_device *,comedi_subdevice *); */
+
+ /* called when the buffer changes */
+ int (*buf_change) (comedi_device *dev, comedi_subdevice *s,
+ unsigned long new_size);
+
+ void (*munge) (comedi_device *dev, comedi_subdevice *s, void *data,
+ unsigned int num_bytes, unsigned int start_chan_index);
+ enum dma_data_direction async_dma_dir;
+
+ unsigned int state;
+
+ device_create_result_type *class_dev;
+ int minor;
+};
+
+struct comedi_buf_page {
+ void *virt_addr;
+ dma_addr_t dma_addr;
+};
+
+struct comedi_async_struct {
+ comedi_subdevice *subdevice;
+
+ void *prealloc_buf; /* pre-allocated buffer */
+ unsigned int prealloc_bufsz; /* buffer size, in bytes */
+ struct comedi_buf_page *buf_page_list; /* virtual and dma address of each page */
+ unsigned n_buf_pages; /* num elements in buf_page_list */
+
+ unsigned int max_bufsize; /* maximum buffer size, bytes */
+ unsigned int mmap_count; /* current number of mmaps of prealloc_buf */
+
+ unsigned int buf_write_count; /* byte count for writer (write completed) */
+ unsigned int buf_write_alloc_count; /* byte count for writer (allocated for writing) */
+ unsigned int buf_read_count; /* byte count for reader (read completed) */
+ unsigned int buf_read_alloc_count; /* byte count for reader (allocated for reading) */
+
+ unsigned int buf_write_ptr; /* buffer marker for writer */
+ unsigned int buf_read_ptr; /* buffer marker for reader */
+
+ unsigned int cur_chan; /* useless channel marker for interrupt */
+ /* number of bytes that have been received for current scan */
+ unsigned int scan_progress;
+ /* keeps track of where we are in chanlist as for munging */
+ unsigned int munge_chan;
+ /* number of bytes that have been munged */
+ unsigned int munge_count;
+ /* buffer marker for munging */
+ unsigned int munge_ptr;
+
+ unsigned int events; /* events that have occurred */
+
+ comedi_cmd cmd;
+
+ wait_queue_head_t wait_head;
+
+ /* callback stuff */
+ unsigned int cb_mask;
+ int (*cb_func) (unsigned int flags, void *);
+ void *cb_arg;
+
+ int (*inttrig) (comedi_device *dev, comedi_subdevice *s,
+ unsigned int x);
+};
+
+struct comedi_driver_struct {
+ struct comedi_driver_struct *next;
+
+ const char *driver_name;
+ struct module *module;
+ int (*attach) (comedi_device *, comedi_devconfig *);
+ int (*detach) (comedi_device *);
+
+ /* number of elements in board_name and board_id arrays */
+ unsigned int num_names;
+ const char *const *board_name;
+ /* offset in bytes from one board name pointer to the next */
+ int offset;
+};
+
+struct comedi_device_struct {
+ int use_count;
+ comedi_driver *driver;
+ void *private;
+
+ device_create_result_type *class_dev;
+ int minor;
+ /* hw_dev is passed to dma_alloc_coherent when allocating async buffers
+ * for subdevices that have async_dma_dir set to something other than
+ * DMA_NONE */
+ struct device *hw_dev;
+
+ const char *board_name;
+ const void *board_ptr;
+ int attached;
+ int rt;
+ spinlock_t spinlock;
+ struct mutex mutex;
+ int in_request_module;
+
+ int n_subdevices;
+ comedi_subdevice *subdevices;
+
+ /* dumb */
+ unsigned long iobase;
+ unsigned int irq;
+
+ comedi_subdevice *read_subdev;
+ comedi_subdevice *write_subdev;
+
+ struct fasync_struct *async_queue;
+
+ void (*open) (comedi_device *dev);
+ void (*close) (comedi_device *dev);
+};
+
+struct comedi_device_file_info {
+ comedi_device *device;
+ comedi_subdevice *read_subdevice;
+ comedi_subdevice *write_subdevice;
+};
+
+#ifdef CONFIG_COMEDI_DEBUG
+extern int comedi_debug;
+#else
+static const int comedi_debug;
+#endif
+
+/*
+ * function prototypes
+ */
+
+void comedi_event(comedi_device *dev, comedi_subdevice *s);
+void comedi_error(const comedi_device *dev, const char *s);
+
+/* we can expand the number of bits used to encode devices/subdevices into
+ the minor number soon, after more distros support > 8 bit minor numbers
+ (like after Debian Etch gets released) */
+enum comedi_minor_bits {
+ COMEDI_DEVICE_MINOR_MASK = 0xf,
+ COMEDI_SUBDEVICE_MINOR_MASK = 0xf0
+};
+static const unsigned COMEDI_SUBDEVICE_MINOR_SHIFT = 4;
+static const unsigned COMEDI_SUBDEVICE_MINOR_OFFSET = 1;
+
+struct comedi_device_file_info *comedi_get_device_file_info(unsigned minor);
+
+static inline comedi_subdevice *comedi_get_read_subdevice(
+ const struct comedi_device_file_info *info)
+{
+ if (info->read_subdevice)
+ return info->read_subdevice;
+ if (info->device == NULL)
+ return NULL;
+ return info->device->read_subdev;
+}
+
+static inline comedi_subdevice *comedi_get_write_subdevice(
+ const struct comedi_device_file_info *info)
+{
+ if (info->write_subdevice)
+ return info->write_subdevice;
+ if (info->device == NULL)
+ return NULL;
+ return info->device->write_subdev;
+}
+
+void comedi_device_detach(comedi_device *dev);
+int comedi_device_attach(comedi_device *dev, comedi_devconfig *it);
+int comedi_driver_register(comedi_driver *);
+int comedi_driver_unregister(comedi_driver *);
+
+void init_polling(void);
+void cleanup_polling(void);
+void start_polling(comedi_device *);
+void stop_polling(comedi_device *);
+
+int comedi_buf_alloc(comedi_device *dev, comedi_subdevice *s, unsigned long
+ new_size);
+
+#ifdef CONFIG_PROC_FS
+void comedi_proc_init(void);
+void comedi_proc_cleanup(void);
+#else
+static inline void comedi_proc_init(void)
+{
+}
+static inline void comedi_proc_cleanup(void)
+{
+}
+#endif
+
+/* subdevice runflags */
+enum subdevice_runflags {
+ SRF_USER = 0x00000001,
+ SRF_RT = 0x00000002,
+ /* indicates an COMEDI_CB_ERROR event has occurred since the last
+ * command was started */
+ SRF_ERROR = 0x00000004,
+ SRF_RUNNING = 0x08000000
+};
+
+/*
+ various internal comedi functions
+ */
+
+int do_rangeinfo_ioctl(comedi_device *dev, comedi_rangeinfo *arg);
+int check_chanlist(comedi_subdevice *s, int n, unsigned int *chanlist);
+void comedi_set_subdevice_runflags(comedi_subdevice *s, unsigned mask,
+ unsigned bits);
+unsigned comedi_get_subdevice_runflags(comedi_subdevice *s);
+int insn_inval(comedi_device *dev, comedi_subdevice *s,
+ comedi_insn *insn, lsampl_t *data);
+
+/* 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} /* XXX */
+#define BIP_RANGE(a) {-(a)*1e6, (a)*1e6, 0}
+#define UNI_RANGE(a) {0, (a)*1e6, 0}
+
+extern const comedi_lrange range_bipolar10;
+extern const comedi_lrange range_bipolar5;
+extern const comedi_lrange range_bipolar2_5;
+extern const comedi_lrange range_unipolar10;
+extern const comedi_lrange range_unipolar5;
+extern const 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_struct {
+ int length;
+ comedi_krange range[GCC_ZERO_LENGTH_ARRAY];
+};
+
+/* some silly little inline functions */
+
+static inline int alloc_subdevices(comedi_device *dev,
+ unsigned int num_subdevices)
+{
+ unsigned i;
+
+ dev->n_subdevices = num_subdevices;
+ dev->subdevices =
+ kcalloc(num_subdevices, sizeof(comedi_subdevice), GFP_KERNEL);
+ if (!dev->subdevices)
+ return -ENOMEM;
+ for (i = 0; i < num_subdevices; ++i) {
+ dev->subdevices[i].device = dev;
+ dev->subdevices[i].async_dma_dir = DMA_NONE;
+ spin_lock_init(&dev->subdevices[i].spin_lock);
+ dev->subdevices[i].minor = -1;
+ }
+ return 0;
+}
+
+static inline int alloc_private(comedi_device *dev, int size)
+{
+ dev->private = kzalloc(size, GFP_KERNEL);
+ if (!dev->private)
+ return -ENOMEM;
+ return 0;
+}
+
+static inline unsigned int bytes_per_sample(const comedi_subdevice *subd)
+{
+ if (subd->subdev_flags & SDF_LSAMPL)
+ return sizeof(lsampl_t);
+ else
+ return sizeof(sampl_t);
+}
+
+/* must be used in attach to set dev->hw_dev if you wish to dma directly
+into comedi's buffer */
+static inline void comedi_set_hw_dev(comedi_device *dev, struct device *hw_dev)
+{
+ if (dev->hw_dev)
+ put_device(dev->hw_dev);
+
+ dev->hw_dev = hw_dev;
+ if (dev->hw_dev) {
+ dev->hw_dev = get_device(dev->hw_dev);
+ BUG_ON(dev->hw_dev == NULL);
+ }
+}
+
+int comedi_buf_put(comedi_async *async, sampl_t x);
+int comedi_buf_get(comedi_async *async, sampl_t *x);
+
+unsigned int comedi_buf_write_n_available(comedi_async *async);
+unsigned int comedi_buf_write_alloc(comedi_async *async, unsigned int nbytes);
+unsigned int comedi_buf_write_alloc_strict(comedi_async *async,
+ unsigned int nbytes);
+unsigned comedi_buf_write_free(comedi_async *async, unsigned int nbytes);
+unsigned comedi_buf_read_alloc(comedi_async *async, unsigned nbytes);
+unsigned comedi_buf_read_free(comedi_async *async, unsigned int nbytes);
+unsigned int comedi_buf_read_n_available(comedi_async *async);
+void comedi_buf_memcpy_to(comedi_async *async, unsigned int offset,
+ const void *source, unsigned int num_bytes);
+void comedi_buf_memcpy_from(comedi_async *async, unsigned int offset,
+ void *destination, unsigned int num_bytes);
+static inline unsigned comedi_buf_write_n_allocated(comedi_async *async)
+{
+ return async->buf_write_alloc_count - async->buf_write_count;
+}
+static inline unsigned comedi_buf_read_n_allocated(comedi_async *async)
+{
+ return async->buf_read_alloc_count - async->buf_read_count;
+}
+
+void comedi_reset_async_buf(comedi_async *async);
+
+static inline void *comedi_aux_data(int options[], int n)
+{
+ unsigned long address;
+ unsigned long addressLow;
+ int bit_shift;
+ if (sizeof(int) >= sizeof(void *))
+ address = options[COMEDI_DEVCONF_AUX_DATA_LO];
+ else {
+ address = options[COMEDI_DEVCONF_AUX_DATA_HI];
+ bit_shift = sizeof(int) * 8;
+ address <<= bit_shift;
+ addressLow = options[COMEDI_DEVCONF_AUX_DATA_LO];
+ addressLow &= (1UL << bit_shift) - 1;
+ address |= addressLow;
+ }
+ if (n >= 1)
+ address += options[COMEDI_DEVCONF_AUX_DATA0_LENGTH];
+ if (n >= 2)
+ address += options[COMEDI_DEVCONF_AUX_DATA1_LENGTH];
+ if (n >= 3)
+ address += options[COMEDI_DEVCONF_AUX_DATA2_LENGTH];
+ BUG_ON(n > 3);
+ return (void *)address;
+}
+
+int comedi_alloc_board_minor(struct device *hardware_device);
+void comedi_free_board_minor(unsigned minor);
+int comedi_alloc_subdevice_minor(comedi_device *dev, comedi_subdevice *s);
+void comedi_free_subdevice_minor(comedi_subdevice *s);
+int comedi_pci_auto_config(struct pci_dev *pcidev, const char *board_name);
+void comedi_pci_auto_unconfig(struct pci_dev *pcidev);
+
+#include "comedi_rt.h"
+
+#endif /* _COMEDIDEV_H */
diff --git a/drivers/staging/comedi/comedilib.h b/drivers/staging/comedi/comedilib.h
new file mode 100644
index 000000000000..fc5fc015726b
--- /dev/null
+++ b/drivers/staging/comedi/comedilib.h
@@ -0,0 +1,192 @@
+/*
+ linux/include/comedilib.h
+ header file for kcomedilib
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998-2001 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef _LINUX_COMEDILIB_H
+#define _LINUX_COMEDILIB_H
+
+#include "comedi.h"
+
+/* Kernel internal stuff. Needed by real-time modules and such. */
+
+#ifndef __KERNEL__
+#error linux/comedilib.h should not be included by non-kernel-space code
+#endif
+
+/* exported functions */
+
+#ifndef KCOMEDILIB_DEPRECATED
+
+typedef void comedi_t;
+
+/* these functions may not be called at real-time priority */
+
+comedi_t *comedi_open(const char *path);
+int comedi_close(comedi_t *dev);
+
+/* these functions may be called at any priority, but may fail at
+ real-time priority */
+
+int comedi_lock(comedi_t *dev, unsigned int subdev);
+int comedi_unlock(comedi_t *dev, unsigned int subdev);
+
+/* these functions may be called at any priority, but you must hold
+ the lock for the subdevice */
+
+int comedi_loglevel(int loglevel);
+void comedi_perror(const char *s);
+char *comedi_strerror(int errnum);
+int comedi_errno(void);
+int comedi_fileno(comedi_t *dev);
+
+int comedi_cancel(comedi_t *dev, unsigned int subdev);
+int comedi_register_callback(comedi_t *dev, unsigned int subdev,
+ unsigned int mask, int (*cb) (unsigned int, void *), void *arg);
+
+int comedi_command(comedi_t *dev, comedi_cmd *cmd);
+int comedi_command_test(comedi_t *dev, comedi_cmd *cmd);
+int comedi_trigger(comedi_t *dev, unsigned int subdev, comedi_trig *it);
+int __comedi_trigger(comedi_t *dev, unsigned int subdev, comedi_trig *it);
+int comedi_data_write(comedi_t *dev, unsigned int subdev, unsigned int chan,
+ unsigned int range, unsigned int aref, lsampl_t data);
+int comedi_data_read(comedi_t *dev, unsigned int subdev, unsigned int chan,
+ unsigned int range, unsigned int aref, lsampl_t *data);
+int comedi_data_read_hint(comedi_t *dev, unsigned int subdev,
+ unsigned int chan, unsigned int range, unsigned int aref);
+int comedi_data_read_delayed(comedi_t *dev, unsigned int subdev,
+ unsigned int chan, unsigned int range, unsigned int aref,
+ lsampl_t *data, unsigned int nano_sec);
+int comedi_dio_config(comedi_t *dev, unsigned int subdev, unsigned int chan,
+ unsigned int io);
+int comedi_dio_read(comedi_t *dev, unsigned int subdev, unsigned int chan,
+ unsigned int *val);
+int comedi_dio_write(comedi_t *dev, unsigned int subdev, unsigned int chan,
+ unsigned int val);
+int comedi_dio_bitfield(comedi_t *dev, unsigned int subdev, unsigned int mask,
+ unsigned int *bits);
+int comedi_get_n_subdevices(comedi_t *dev);
+int comedi_get_version_code(comedi_t *dev);
+const char *comedi_get_driver_name(comedi_t *dev);
+const char *comedi_get_board_name(comedi_t *dev);
+int comedi_get_subdevice_type(comedi_t *dev, unsigned int subdevice);
+int comedi_find_subdevice_by_type(comedi_t *dev, int type, unsigned int subd);
+int comedi_get_n_channels(comedi_t *dev, unsigned int subdevice);
+lsampl_t comedi_get_maxdata(comedi_t *dev, unsigned int subdevice, unsigned
+ int chan);
+int comedi_get_n_ranges(comedi_t *dev, unsigned int subdevice, unsigned int
+ chan);
+int comedi_do_insn(comedi_t *dev, comedi_insn *insn);
+int comedi_poll(comedi_t *dev, unsigned int subdev);
+
+/* DEPRECATED functions */
+int comedi_get_rangetype(comedi_t *dev, unsigned int subdevice,
+ unsigned int chan);
+
+/* ALPHA functions */
+unsigned int comedi_get_subdevice_flags(comedi_t *dev, unsigned int subdevice);
+int comedi_get_len_chanlist(comedi_t *dev, unsigned int subdevice);
+int comedi_get_krange(comedi_t *dev, unsigned int subdevice, unsigned int
+ chan, unsigned int range, comedi_krange *krange);
+unsigned int comedi_get_buf_head_pos(comedi_t *dev, unsigned int subdevice);
+int comedi_set_user_int_count(comedi_t *dev, unsigned int subdevice,
+ unsigned int buf_user_count);
+int comedi_map(comedi_t *dev, unsigned int subdev, void *ptr);
+int comedi_unmap(comedi_t *dev, unsigned int subdev);
+int comedi_get_buffer_size(comedi_t *dev, unsigned int subdev);
+int comedi_mark_buffer_read(comedi_t *dev, unsigned int subdevice,
+ unsigned int num_bytes);
+int comedi_mark_buffer_written(comedi_t *d, unsigned int subdevice,
+ unsigned int num_bytes);
+int comedi_get_buffer_contents(comedi_t *dev, unsigned int subdevice);
+int comedi_get_buffer_offset(comedi_t *dev, unsigned int subdevice);
+
+#else
+
+/* these functions may not be called at real-time priority */
+
+int comedi_open(unsigned int minor);
+void comedi_close(unsigned int minor);
+
+/* these functions may be called at any priority, but may fail at
+ real-time priority */
+
+int comedi_lock(unsigned int minor, unsigned int subdev);
+int comedi_unlock(unsigned int minor, unsigned int subdev);
+
+/* these functions may be called at any priority, but you must hold
+ the lock for the subdevice */
+
+int comedi_cancel(unsigned int minor, unsigned int subdev);
+int comedi_register_callback(unsigned int minor, unsigned int subdev,
+ unsigned int mask, int (*cb) (unsigned int, void *), void *arg);
+
+int comedi_command(unsigned int minor, comedi_cmd *cmd);
+int comedi_command_test(unsigned int minor, comedi_cmd *cmd);
+int comedi_trigger(unsigned int minor, unsigned int subdev, comedi_trig *it);
+int __comedi_trigger(unsigned int minor, unsigned int subdev, comedi_trig *it);
+int comedi_data_write(unsigned int dev, unsigned int subdev, unsigned int chan,
+ unsigned int range, unsigned int aref, lsampl_t data);
+int comedi_data_read(unsigned int dev, unsigned int subdev, unsigned int chan,
+ unsigned int range, unsigned int aref, lsampl_t *data);
+int comedi_dio_config(unsigned int dev, unsigned int subdev, unsigned int chan,
+ unsigned int io);
+int comedi_dio_read(unsigned int dev, unsigned int subdev, unsigned int chan,
+ unsigned int *val);
+int comedi_dio_write(unsigned int dev, unsigned int subdev, unsigned int chan,
+ unsigned int val);
+int comedi_dio_bitfield(unsigned int dev, unsigned int subdev,
+ unsigned int mask, unsigned int *bits);
+int comedi_get_n_subdevices(unsigned int dev);
+int comedi_get_version_code(unsigned int dev);
+char *comedi_get_driver_name(unsigned int dev);
+char *comedi_get_board_name(unsigned int minor);
+int comedi_get_subdevice_type(unsigned int minor, unsigned int subdevice);
+int comedi_find_subdevice_by_type(unsigned int minor, int type,
+ unsigned int subd);
+int comedi_get_n_channels(unsigned int minor, unsigned int subdevice);
+lsampl_t comedi_get_maxdata(unsigned int minor, unsigned int subdevice, unsigned
+ int chan);
+int comedi_get_n_ranges(unsigned int minor, unsigned int subdevice, unsigned int
+ chan);
+int comedi_do_insn(unsigned int minor, comedi_insn *insn);
+int comedi_poll(unsigned int minor, unsigned int subdev);
+
+/* DEPRECATED functions */
+int comedi_get_rangetype(unsigned int minor, unsigned int subdevice,
+ unsigned int chan);
+
+/* ALPHA functions */
+unsigned int comedi_get_subdevice_flags(unsigned int minor, unsigned int
+ subdevice);
+int comedi_get_len_chanlist(unsigned int minor, unsigned int subdevice);
+int comedi_get_krange(unsigned int minor, unsigned int subdevice, unsigned int
+ chan, unsigned int range, comedi_krange *krange);
+unsigned int comedi_get_buf_head_pos(unsigned int minor, unsigned int
+ subdevice);
+int comedi_set_user_int_count(unsigned int minor, unsigned int subdevice,
+ unsigned int buf_user_count);
+int comedi_map(unsigned int minor, unsigned int subdev, void **ptr);
+int comedi_unmap(unsigned int minor, unsigned int subdev);
+
+#endif
+
+#endif
diff --git a/drivers/staging/comedi/drivers.c b/drivers/staging/comedi/drivers.c
new file mode 100644
index 000000000000..06372b227bb2
--- /dev/null
+++ b/drivers/staging/comedi/drivers.c
@@ -0,0 +1,846 @@
+/*
+ module/drivers.c
+ functions for manipulating drivers
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#define _GNU_SOURCE
+
+#define __NO_VERSION__
+#include "comedi_fops.h"
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include "comedidev.h"
+#include "wrapper.h"
+#include <linux/highmem.h> /* for SuSE brokenness */
+#include <linux/vmalloc.h>
+#include <linux/cdev.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+static int postconfig(comedi_device * dev);
+static int insn_rw_emulate_bits(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data);
+static void *comedi_recognize(comedi_driver * driv, const char *name);
+static void comedi_report_boards(comedi_driver * driv);
+static int poll_invalid(comedi_device * dev, comedi_subdevice * s);
+int comedi_buf_alloc(comedi_device * dev, comedi_subdevice * s,
+ unsigned long new_size);
+
+comedi_driver *comedi_drivers;
+
+int comedi_modprobe(int minor)
+{
+ return -EINVAL;
+}
+
+static void cleanup_device(comedi_device * dev)
+{
+ int i;
+ comedi_subdevice *s;
+
+ if (dev->subdevices) {
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = dev->subdevices + i;
+ comedi_free_subdevice_minor(s);
+ if (s->async) {
+ comedi_buf_alloc(dev, s, 0);
+ kfree(s->async);
+ }
+ }
+ kfree(dev->subdevices);
+ dev->subdevices = NULL;
+ dev->n_subdevices = 0;
+ }
+ if (dev->private) {
+ kfree(dev->private);
+ dev->private = NULL;
+ }
+ dev->driver = 0;
+ dev->board_name = NULL;
+ dev->board_ptr = NULL;
+ dev->iobase = 0;
+ dev->irq = 0;
+ dev->read_subdev = NULL;
+ dev->write_subdev = NULL;
+ dev->open = NULL;
+ dev->close = NULL;
+ comedi_set_hw_dev(dev, NULL);
+}
+
+static void __comedi_device_detach(comedi_device * dev)
+{
+ dev->attached = 0;
+ if (dev->driver) {
+ dev->driver->detach(dev);
+ } else {
+ printk("BUG: dev->driver=NULL in comedi_device_detach()\n");
+ }
+ cleanup_device(dev);
+}
+
+void comedi_device_detach(comedi_device * dev)
+{
+ if (!dev->attached)
+ return;
+ __comedi_device_detach(dev);
+}
+
+int comedi_device_attach(comedi_device * dev, comedi_devconfig * it)
+{
+ comedi_driver *driv;
+ int ret;
+
+ if (dev->attached)
+ return -EBUSY;
+
+ for (driv = comedi_drivers; driv; driv = driv->next) {
+ if (!try_module_get(driv->module)) {
+ printk("comedi: failed to increment module count, skipping\n");
+ continue;
+ }
+ if (driv->num_names) {
+ dev->board_ptr = comedi_recognize(driv, it->board_name);
+ if (dev->board_ptr == NULL) {
+ module_put(driv->module);
+ continue;
+ }
+ } else {
+ if (strcmp(driv->driver_name, it->board_name)) {
+ module_put(driv->module);
+ continue;
+ }
+ }
+ //initialize dev->driver here so comedi_error() can be called from attach
+ dev->driver = driv;
+ ret = driv->attach(dev, it);
+ if (ret < 0) {
+ module_put(dev->driver->module);
+ __comedi_device_detach(dev);
+ return ret;
+ }
+ goto attached;
+ }
+
+ // recognize has failed if we get here
+ // report valid board names before returning error
+ for (driv = comedi_drivers; driv; driv = driv->next) {
+ if (!try_module_get(driv->module)) {
+ printk("comedi: failed to increment module count\n");
+ continue;
+ }
+ comedi_report_boards(driv);
+ module_put(driv->module);
+ }
+ return -EIO;
+
+attached:
+ /* do a little post-config cleanup */
+ ret = postconfig(dev);
+ module_put(dev->driver->module);
+ if (ret < 0) {
+ __comedi_device_detach(dev);
+ return ret;
+ }
+
+ if (!dev->board_name) {
+ printk("BUG: dev->board_name=<%p>\n", dev->board_name);
+ dev->board_name = "BUG";
+ }
+ smp_wmb();
+ dev->attached = 1;
+
+ return 0;
+}
+
+int comedi_driver_register(comedi_driver * driver)
+{
+ driver->next = comedi_drivers;
+ comedi_drivers = driver;
+
+ return 0;
+}
+
+int comedi_driver_unregister(comedi_driver * driver)
+{
+ comedi_driver *prev;
+ int i;
+
+ /* check for devices using this driver */
+ for (i = 0; i < COMEDI_NUM_BOARD_MINORS; i++) {
+ struct comedi_device_file_info *dev_file_info = comedi_get_device_file_info(i);
+ comedi_device *dev;
+
+ if(dev_file_info == NULL) continue;
+ dev = dev_file_info->device;
+
+ mutex_lock(&dev->mutex);
+ if (dev->attached && dev->driver == driver) {
+ if (dev->use_count)
+ printk("BUG! detaching device with use_count=%d\n", dev->use_count);
+ comedi_device_detach(dev);
+ }
+ mutex_unlock(&dev->mutex);
+ }
+
+ if (comedi_drivers == driver) {
+ comedi_drivers = driver->next;
+ return 0;
+ }
+
+ for (prev = comedi_drivers; prev->next; prev = prev->next) {
+ if (prev->next == driver) {
+ prev->next = driver->next;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int postconfig(comedi_device * dev)
+{
+ int i;
+ comedi_subdevice *s;
+ comedi_async *async = NULL;
+ int ret;
+
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = dev->subdevices + i;
+
+ if (s->type == COMEDI_SUBD_UNUSED)
+ continue;
+
+ if (s->len_chanlist == 0)
+ s->len_chanlist = 1;
+
+ if (s->do_cmd) {
+ BUG_ON((s->subdev_flags & (SDF_CMD_READ |
+ SDF_CMD_WRITE)) == 0);
+ BUG_ON(!s->do_cmdtest);
+
+ async = kzalloc(sizeof(comedi_async), GFP_KERNEL);
+ if (async == NULL) {
+ printk("failed to allocate async struct\n");
+ return -ENOMEM;
+ }
+ init_waitqueue_head(&async->wait_head);
+ async->subdevice = s;
+ s->async = async;
+
+#define DEFAULT_BUF_MAXSIZE (64*1024)
+#define DEFAULT_BUF_SIZE (64*1024)
+
+ async->max_bufsize = DEFAULT_BUF_MAXSIZE;
+
+ async->prealloc_buf = NULL;
+ async->prealloc_bufsz = 0;
+ if (comedi_buf_alloc(dev, s, DEFAULT_BUF_SIZE) < 0) {
+ printk("Buffer allocation failed\n");
+ return -ENOMEM;
+ }
+ if (s->buf_change) {
+ ret = s->buf_change(dev, s, DEFAULT_BUF_SIZE);
+ if (ret < 0)
+ return ret;
+ }
+ comedi_alloc_subdevice_minor(dev, s);
+ }
+
+ if (!s->range_table && !s->range_table_list)
+ s->range_table = &range_unknown;
+
+ if (!s->insn_read && s->insn_bits)
+ s->insn_read = insn_rw_emulate_bits;
+ if (!s->insn_write && s->insn_bits)
+ s->insn_write = insn_rw_emulate_bits;
+
+ if (!s->insn_read)
+ s->insn_read = insn_inval;
+ if (!s->insn_write)
+ s->insn_write = insn_inval;
+ if (!s->insn_bits)
+ s->insn_bits = insn_inval;
+ if (!s->insn_config)
+ s->insn_config = insn_inval;
+
+ if (!s->poll)
+ s->poll = poll_invalid;
+ }
+
+ return 0;
+}
+
+// generic recognize function for drivers that register their supported board names
+void *comedi_recognize(comedi_driver * driv, const char *name)
+{
+ unsigned i;
+ const char *const *name_ptr = driv->board_name;
+ for (i = 0; i < driv->num_names; i++) {
+ if (strcmp(*name_ptr, name) == 0)
+ return (void *)name_ptr;
+ name_ptr =
+ (const char *const *)((const char *)name_ptr +
+ driv->offset);
+ }
+
+ return NULL;
+}
+
+void comedi_report_boards(comedi_driver * driv)
+{
+ unsigned int i;
+ const char *const *name_ptr;
+
+ printk("comedi: valid board names for %s driver are:\n",
+ driv->driver_name);
+
+ name_ptr = driv->board_name;
+ for (i = 0; i < driv->num_names; i++) {
+ printk(" %s\n", *name_ptr);
+ name_ptr = (const char **)((char *)name_ptr + driv->offset);
+ }
+
+ if (driv->num_names == 0)
+ printk(" %s\n", driv->driver_name);
+}
+
+static int poll_invalid(comedi_device * dev, comedi_subdevice * s)
+{
+ return -EINVAL;
+}
+
+int insn_inval(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ return -EINVAL;
+}
+
+static int insn_rw_emulate_bits(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ comedi_insn new_insn;
+ int ret;
+ static const unsigned channels_per_bitfield = 32;
+
+ unsigned chan = CR_CHAN(insn->chanspec);
+ const unsigned base_bitfield_channel =
+ (chan < channels_per_bitfield) ? 0 : chan;
+ lsampl_t new_data[2];
+ memset(new_data, 0, sizeof(new_data));
+ memset(&new_insn, 0, sizeof(new_insn));
+ new_insn.insn = INSN_BITS;
+ new_insn.chanspec = base_bitfield_channel;
+ new_insn.n = 2;
+ new_insn.data = new_data;
+ new_insn.subdev = insn->subdev;
+
+ if (insn->insn == INSN_WRITE) {
+ if (!(s->subdev_flags & SDF_WRITABLE))
+ return -EINVAL;
+ new_data[0] = 1 << (chan - base_bitfield_channel); /* mask */
+ new_data[1] = data[0] ? (1 << (chan - base_bitfield_channel)) : 0; /* bits */
+ }
+
+ ret = s->insn_bits(dev, s, &new_insn, new_data);
+ if (ret < 0)
+ return ret;
+
+ if (insn->insn == INSN_READ) {
+ data[0] = (new_data[1] >> (chan - base_bitfield_channel)) & 1;
+ }
+
+ return 1;
+}
+
+static inline unsigned long uvirt_to_kva(pgd_t * pgd, unsigned long adr)
+{
+ unsigned long ret = 0UL;
+ pmd_t *pmd;
+ pte_t *ptep, pte;
+ pud_t *pud;
+
+ if (!pgd_none(*pgd)) {
+ pud = pud_offset(pgd, adr);
+ pmd = pmd_offset(pud, adr);
+ if (!pmd_none(*pmd)) {
+ ptep = pte_offset_kernel(pmd, adr);
+ pte = *ptep;
+ if (pte_present(pte)) {
+ ret = (unsigned long)
+ page_address(pte_page(pte));
+ ret |= (adr & (PAGE_SIZE - 1));
+ }
+ }
+ }
+ return ret;
+}
+
+static inline unsigned long kvirt_to_kva(unsigned long adr)
+{
+ unsigned long va, kva;
+
+ va = adr;
+ kva = uvirt_to_kva(pgd_offset_k(va), va);
+
+ return kva;
+}
+
+int comedi_buf_alloc(comedi_device * dev, comedi_subdevice * s,
+ unsigned long new_size)
+{
+ comedi_async *async = s->async;
+
+ /* Round up new_size to multiple of PAGE_SIZE */
+ new_size = (new_size + PAGE_SIZE - 1) & PAGE_MASK;
+
+ /* if no change is required, do nothing */
+ if (async->prealloc_buf && async->prealloc_bufsz == new_size) {
+ return 0;
+ }
+ // deallocate old buffer
+ if (async->prealloc_buf) {
+ vunmap(async->prealloc_buf);
+ async->prealloc_buf = NULL;
+ async->prealloc_bufsz = 0;
+ }
+ if (async->buf_page_list) {
+ unsigned i;
+ for (i = 0; i < async->n_buf_pages; ++i) {
+ if (async->buf_page_list[i].virt_addr) {
+ mem_map_unreserve(virt_to_page(async->
+ buf_page_list[i].virt_addr));
+ if (s->async_dma_dir != DMA_NONE) {
+ dma_free_coherent(dev->hw_dev,
+ PAGE_SIZE,
+ async->buf_page_list[i].
+ virt_addr,
+ async->buf_page_list[i].
+ dma_addr);
+ } else {
+ free_page((unsigned long)async->
+ buf_page_list[i].virt_addr);
+ }
+ }
+ }
+ vfree(async->buf_page_list);
+ async->buf_page_list = NULL;
+ async->n_buf_pages = 0;
+ }
+ // allocate new buffer
+ if (new_size) {
+ unsigned i = 0;
+ unsigned n_pages = new_size >> PAGE_SHIFT;
+ struct page **pages = NULL;
+
+ async->buf_page_list =
+ vmalloc(sizeof(struct comedi_buf_page) * n_pages);
+ if (async->buf_page_list) {
+ memset(async->buf_page_list, 0,
+ sizeof(struct comedi_buf_page) * n_pages);
+ pages = vmalloc(sizeof(struct page *) * n_pages);
+ }
+ if (pages) {
+ for (i = 0; i < n_pages; i++) {
+ if (s->async_dma_dir != DMA_NONE) {
+ async->buf_page_list[i].virt_addr =
+ dma_alloc_coherent(dev->hw_dev,
+ PAGE_SIZE,
+ &async->buf_page_list[i].
+ dma_addr,
+ GFP_KERNEL | __GFP_COMP);
+ } else {
+ async->buf_page_list[i].virt_addr =
+ (void *)
+ get_zeroed_page(GFP_KERNEL);
+ }
+ if (async->buf_page_list[i].virt_addr == NULL) {
+ break;
+ }
+ mem_map_reserve(virt_to_page(async->
+ buf_page_list[i].virt_addr));
+ pages[i] =
+ virt_to_page(async->buf_page_list[i].
+ virt_addr);
+ }
+ }
+ if (i == n_pages) {
+ async->prealloc_buf =
+ vmap(pages, n_pages, VM_MAP,
+ PAGE_KERNEL_NOCACHE);
+ }
+ if (pages) {
+ vfree(pages);
+ }
+ if (async->prealloc_buf == NULL) {
+ /* Some allocation failed above. */
+ if (async->buf_page_list) {
+ for (i = 0; i < n_pages; i++) {
+ if (async->buf_page_list[i].virt_addr ==
+ NULL) {
+ break;
+ }
+ mem_map_unreserve(virt_to_page(async->
+ buf_page_list[i].
+ virt_addr));
+ if (s->async_dma_dir != DMA_NONE) {
+ dma_free_coherent(dev->hw_dev,
+ PAGE_SIZE,
+ async->buf_page_list[i].
+ virt_addr,
+ async->buf_page_list[i].
+ dma_addr);
+ } else {
+ free_page((unsigned long)async->
+ buf_page_list[i].
+ virt_addr);
+ }
+ }
+ vfree(async->buf_page_list);
+ async->buf_page_list = NULL;
+ }
+ return -ENOMEM;
+ }
+ async->n_buf_pages = n_pages;
+ }
+ async->prealloc_bufsz = new_size;
+
+ return 0;
+}
+
+/* munging is applied to data by core as it passes between user
+ * and kernel space */
+unsigned int comedi_buf_munge(comedi_async * async, unsigned int num_bytes)
+{
+ comedi_subdevice *s = async->subdevice;
+ unsigned int count = 0;
+ const unsigned num_sample_bytes = bytes_per_sample(s);
+
+ if (s->munge == NULL || (async->cmd.flags & CMDF_RAWDATA)) {
+ async->munge_count += num_bytes;
+ if ((int)(async->munge_count - async->buf_write_count) > 0)
+ BUG();
+ return num_bytes;
+ }
+ /* don't munge partial samples */
+ num_bytes -= num_bytes % num_sample_bytes;
+ while (count < num_bytes) {
+ int block_size;
+
+ block_size = num_bytes - count;
+ if (block_size < 0) {
+ rt_printk("%s: %s: bug! block_size is negative\n",
+ __FILE__, __FUNCTION__);
+ break;
+ }
+ if ((int)(async->munge_ptr + block_size -
+ async->prealloc_bufsz) > 0)
+ block_size = async->prealloc_bufsz - async->munge_ptr;
+
+ s->munge(s->device, s, async->prealloc_buf + async->munge_ptr,
+ block_size, async->munge_chan);
+
+ smp_wmb(); //barrier insures data is munged in buffer before munge_count is incremented
+
+ async->munge_chan += block_size / num_sample_bytes;
+ async->munge_chan %= async->cmd.chanlist_len;
+ async->munge_count += block_size;
+ async->munge_ptr += block_size;
+ async->munge_ptr %= async->prealloc_bufsz;
+ count += block_size;
+ }
+ if ((int)(async->munge_count - async->buf_write_count) > 0)
+ BUG();
+ return count;
+}
+
+unsigned int comedi_buf_write_n_available(comedi_async * async)
+{
+ unsigned int free_end;
+ unsigned int nbytes;
+
+ if (async == NULL)
+ return 0;
+
+ free_end = async->buf_read_count + async->prealloc_bufsz;
+ nbytes = free_end - async->buf_write_alloc_count;
+ nbytes -= nbytes % bytes_per_sample(async->subdevice);
+ /* barrier insures the read of buf_read_count in this
+ query occurs before any following writes to the buffer which
+ might be based on the return value from this query.
+ */
+ smp_mb();
+ return nbytes;
+}
+
+/* allocates chunk for the writer from free buffer space */
+unsigned int comedi_buf_write_alloc(comedi_async * async, unsigned int nbytes)
+{
+ unsigned int free_end = async->buf_read_count + async->prealloc_bufsz;
+
+ if ((int)(async->buf_write_alloc_count + nbytes - free_end) > 0) {
+ nbytes = free_end - async->buf_write_alloc_count;
+ }
+ async->buf_write_alloc_count += nbytes;
+ /* barrier insures the read of buf_read_count above occurs before
+ we write data to the write-alloc'ed buffer space */
+ smp_mb();
+ return nbytes;
+}
+
+/* allocates nothing unless it can completely fulfill the request */
+unsigned int comedi_buf_write_alloc_strict(comedi_async * async,
+ unsigned int nbytes)
+{
+ unsigned int free_end = async->buf_read_count + async->prealloc_bufsz;
+
+ if ((int)(async->buf_write_alloc_count + nbytes - free_end) > 0) {
+ nbytes = 0;
+ }
+ async->buf_write_alloc_count += nbytes;
+ /* barrier insures the read of buf_read_count above occurs before
+ we write data to the write-alloc'ed buffer space */
+ smp_mb();
+ return nbytes;
+}
+
+/* transfers a chunk from writer to filled buffer space */
+unsigned comedi_buf_write_free(comedi_async * async, unsigned int nbytes)
+{
+ if ((int)(async->buf_write_count + nbytes -
+ async->buf_write_alloc_count) > 0) {
+ rt_printk
+ ("comedi: attempted to write-free more bytes than have been write-allocated.\n");
+ nbytes = async->buf_write_alloc_count - async->buf_write_count;
+ }
+ async->buf_write_count += nbytes;
+ async->buf_write_ptr += nbytes;
+ comedi_buf_munge(async, async->buf_write_count - async->munge_count);
+ if (async->buf_write_ptr >= async->prealloc_bufsz) {
+ async->buf_write_ptr %= async->prealloc_bufsz;
+ }
+ return nbytes;
+}
+
+/* allocates a chunk for the reader from filled (and munged) buffer space */
+unsigned comedi_buf_read_alloc(comedi_async * async, unsigned nbytes)
+{
+ if ((int)(async->buf_read_alloc_count + nbytes - async->munge_count) >
+ 0) {
+ nbytes = async->munge_count - async->buf_read_alloc_count;
+ }
+ async->buf_read_alloc_count += nbytes;
+ /* barrier insures read of munge_count occurs before we actually read
+ data out of buffer */
+ smp_rmb();
+ return nbytes;
+}
+
+/* transfers control of a chunk from reader to free buffer space */
+unsigned comedi_buf_read_free(comedi_async * async, unsigned int nbytes)
+{
+ // barrier insures data has been read out of buffer before read count is incremented
+ smp_mb();
+ if ((int)(async->buf_read_count + nbytes -
+ async->buf_read_alloc_count) > 0) {
+ rt_printk
+ ("comedi: attempted to read-free more bytes than have been read-allocated.\n");
+ nbytes = async->buf_read_alloc_count - async->buf_read_count;
+ }
+ async->buf_read_count += nbytes;
+ async->buf_read_ptr += nbytes;
+ async->buf_read_ptr %= async->prealloc_bufsz;
+ return nbytes;
+}
+
+void comedi_buf_memcpy_to(comedi_async * async, unsigned int offset,
+ const void *data, unsigned int num_bytes)
+{
+ unsigned int write_ptr = async->buf_write_ptr + offset;
+
+ if (write_ptr >= async->prealloc_bufsz)
+ write_ptr %= async->prealloc_bufsz;
+
+ while (num_bytes) {
+ unsigned int block_size;
+
+ if (write_ptr + num_bytes > async->prealloc_bufsz)
+ block_size = async->prealloc_bufsz - write_ptr;
+ else
+ block_size = num_bytes;
+
+ memcpy(async->prealloc_buf + write_ptr, data, block_size);
+
+ data += block_size;
+ num_bytes -= block_size;
+
+ write_ptr = 0;
+ }
+}
+
+void comedi_buf_memcpy_from(comedi_async * async, unsigned int offset,
+ void *dest, unsigned int nbytes)
+{
+ void *src;
+ unsigned int read_ptr = async->buf_read_ptr + offset;
+
+ if (read_ptr >= async->prealloc_bufsz)
+ read_ptr %= async->prealloc_bufsz;
+
+ while (nbytes) {
+ unsigned int block_size;
+
+ src = async->prealloc_buf + read_ptr;
+
+ if (nbytes >= async->prealloc_bufsz - read_ptr)
+ block_size = async->prealloc_bufsz - read_ptr;
+ else
+ block_size = nbytes;
+
+ memcpy(dest, src, block_size);
+ nbytes -= block_size;
+ dest += block_size;
+ read_ptr = 0;
+ }
+}
+
+unsigned int comedi_buf_read_n_available(comedi_async * async)
+{
+ unsigned num_bytes;
+
+ if (async == NULL)
+ return 0;
+ num_bytes = async->munge_count - async->buf_read_count;
+ /* barrier insures the read of munge_count in this
+ query occurs before any following reads of the buffer which
+ might be based on the return value from this query.
+ */
+ smp_rmb();
+ return num_bytes;
+}
+
+int comedi_buf_get(comedi_async * async, sampl_t * x)
+{
+ unsigned int n = comedi_buf_read_n_available(async);
+
+ if (n < sizeof(sampl_t))
+ return 0;
+ comedi_buf_read_alloc(async, sizeof(sampl_t));
+ *x = *(sampl_t *) (async->prealloc_buf + async->buf_read_ptr);
+ comedi_buf_read_free(async, sizeof(sampl_t));
+ return 1;
+}
+
+int comedi_buf_put(comedi_async * async, sampl_t x)
+{
+ unsigned int n = comedi_buf_write_alloc_strict(async, sizeof(sampl_t));
+
+ if (n < sizeof(sampl_t)) {
+ async->events |= COMEDI_CB_ERROR;
+ return 0;
+ }
+ *(sampl_t *) (async->prealloc_buf + async->buf_write_ptr) = x;
+ comedi_buf_write_free(async, sizeof(sampl_t));
+ return 1;
+}
+
+void comedi_reset_async_buf(comedi_async * async)
+{
+ async->buf_write_alloc_count = 0;
+ async->buf_write_count = 0;
+ async->buf_read_alloc_count = 0;
+ async->buf_read_count = 0;
+
+ async->buf_write_ptr = 0;
+ async->buf_read_ptr = 0;
+
+ async->cur_chan = 0;
+ async->scan_progress = 0;
+ async->munge_chan = 0;
+ async->munge_count = 0;
+ async->munge_ptr = 0;
+
+ async->events = 0;
+}
+
+int comedi_auto_config(struct device *hardware_device, const char *board_name, const int *options, unsigned num_options)
+{
+ comedi_devconfig it;
+ int minor;
+ struct comedi_device_file_info *dev_file_info;
+ int retval;
+
+ minor = comedi_alloc_board_minor(hardware_device);
+ if(minor < 0) return minor;
+ dev_set_drvdata(hardware_device, (void*)(unsigned long)minor);
+
+ dev_file_info = comedi_get_device_file_info(minor);
+
+ memset(&it, 0, sizeof(it));
+ strncpy(it.board_name, board_name, COMEDI_NAMELEN);
+ it.board_name[COMEDI_NAMELEN - 1] = '\0';
+ BUG_ON(num_options > COMEDI_NDEVCONFOPTS);
+ memcpy(it.options, options, num_options * sizeof(int));
+
+ mutex_lock(&dev_file_info->device->mutex);
+ retval = comedi_device_attach(dev_file_info->device, &it);
+ mutex_unlock(&dev_file_info->device->mutex);
+ if(retval < 0)
+ {
+ comedi_free_board_minor(minor);
+ }
+ return retval;
+}
+
+void comedi_auto_unconfig(struct device *hardware_device)
+{
+ unsigned long minor = (unsigned long)dev_get_drvdata(hardware_device);
+
+ BUG_ON(minor >= COMEDI_NUM_BOARD_MINORS);
+
+ comedi_free_board_minor(minor);
+}
+
+int comedi_pci_auto_config(struct pci_dev *pcidev, const char *board_name)
+{
+ int options[2];
+
+ // pci bus
+ options[0] = pcidev->bus->number;
+ // pci slot
+ options[1] = PCI_SLOT(pcidev->devfn);
+
+ return comedi_auto_config(&pcidev->dev, board_name, options, sizeof(options) / sizeof(options[0]));
+}
+
+void comedi_pci_auto_unconfig(struct pci_dev *pcidev)
+{
+ comedi_auto_unconfig(&pcidev->dev);
+}
diff --git a/drivers/staging/comedi/drivers/Makefile b/drivers/staging/comedi/drivers/Makefile
new file mode 100644
index 000000000000..158660994765
--- /dev/null
+++ b/drivers/staging/comedi/drivers/Makefile
@@ -0,0 +1,16 @@
+# Makefile for individual comedi drivers
+#
+
+# Comedi "helper" modules
+obj-$(CONFIG_COMEDI) += comedi_fc.o
+obj-$(CONFIG_COMEDI) += comedi_bond.o
+obj-$(CONFIG_COMEDI) += comedi_test.o
+obj-$(CONFIG_COMEDI) += comedi_parport.o
+
+# Comedi PCI drivers
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += mite.o
+
+# Comedi USB drivers
+obj-$(CONFIG_COMEDI_USB_DRIVERS) += usbdux.o
+obj-$(CONFIG_COMEDI_USB_DRIVERS) += usbduxfast.o
+obj-$(CONFIG_COMEDI_USB_DRIVERS) += dt9812.o
diff --git a/drivers/staging/comedi/drivers/comedi_bond.c b/drivers/staging/comedi/drivers/comedi_bond.c
new file mode 100644
index 000000000000..3a65f0316fc9
--- /dev/null
+++ b/drivers/staging/comedi/drivers/comedi_bond.c
@@ -0,0 +1,522 @@
+/*
+ comedi/drivers/comedi_bond.c
+ A Comedi driver to 'bond' or merge multiple drivers and devices as one.
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ Copyright (C) 2005 Calin A. Culianu <calin@ajvar.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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: comedi_bond
+Description: A driver to 'bond' (merge) multiple subdevices from multiple devices together as one.
+Devices:
+Author: ds
+Updated: Mon, 10 Oct 00:18:25 -0500
+Status: works
+
+This driver allows you to 'bond' (merge) multiple comedi subdevices
+(coming from possibly difference boards and/or drivers) together. For
+example, if you had a board with 2 different DIO subdevices, and
+another with 1 DIO subdevice, you could 'bond' them with this driver
+so that they look like one big fat DIO subdevice. This makes writing
+applications slightly easier as you don't have to worry about managing
+different subdevices in the application -- you just worry about
+indexing one linear array of channel id's.
+
+Right now only DIO subdevices are supported as that's the personal itch
+I am scratching with this driver. If you want to add support for AI and AO
+subdevs, go right on ahead and do so!
+
+Commands aren't supported -- although it would be cool if they were.
+
+Configuration Options:
+ List of comedi-minors to bond. All subdevices of the same type
+ within each minor will be concatenated together in the order given here.
+*/
+
+/*
+ * The previous block comment is used to automatically generate
+ * documentation in Comedi and Comedilib. The fields:
+ *
+ * Driver: the name of the driver
+ * Description: a short phrase describing the driver. Don't list boards.
+ * Devices: a full list of the boards that attempt to be supported by
+ * the driver. Format is "(manufacturer) board name [comedi name]",
+ * where comedi_name is the name that is used to configure the board.
+ * See the comment near board_name: in the comedi_driver structure
+ * below. If (manufacturer) or [comedi name] is missing, the previous
+ * value is used.
+ * Author: you
+ * Updated: date when the _documentation_ was last updated. Use 'date -R'
+ * to get a value for this.
+ * Status: a one-word description of the status. Valid values are:
+ * works - driver works correctly on most boards supported, and
+ * passes comedi_test.
+ * unknown - unknown. Usually put there by ds.
+ * experimental - may not work in any particular release. Author
+ * probably wants assistance testing it.
+ * bitrotten - driver has not been update in a long time, probably
+ * doesn't work, and probably is missing support for significant
+ * Comedi interface features.
+ * untested - author probably wrote it "blind", and is believed to
+ * work, but no confirmation.
+ *
+ * These headers should be followed by a blank line, and any comments
+ * you wish to say about the driver. The comment area is the place
+ * to put any known bugs, limitations, unsupported features, supported
+ * command triggers, whether or not commands are supported on particular
+ * subdevices, etc.
+ *
+ * Somewhere in the comment should be information about configuration
+ * options that are used with comedi_config.
+ */
+
+#include "../comedilib.h"
+#include "../comedidev.h"
+#include <linux/string.h>
+
+/* The maxiumum number of channels per subdevice. */
+#define MAX_CHANS 256
+
+#define MODULE_NAME "comedi_bond"
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
+#ifndef STR
+# define STR1(x) #x
+# define STR(x) STR1(x)
+#endif
+
+int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,
+ "If true, print extra cryptic debugging output useful only to developers probably.");
+
+#define LOG_MSG(x...) printk(KERN_INFO MODULE_NAME": "x)
+#define DEBUG(x...) do { if(debug) printk(KERN_DEBUG MODULE_NAME": DEBUG: "x); } while(0)
+#define WARNING(x...) printk(KERN_WARNING MODULE_NAME ": WARNING: "x)
+#define ERROR(x...) printk(KERN_ERR MODULE_NAME ": INTERNAL ERROR: "x)
+MODULE_AUTHOR("Calin A. Culianu");
+MODULE_DESCRIPTION(MODULE_NAME
+ ": A driver for COMEDI to bond multiple COMEDI devices together as one. In the words of John Lennon: 'And the world will live as one...'");
+
+/*
+ * Board descriptions for two imaginary boards. Describing the
+ * boards in this way is optional, and completely driver-dependent.
+ * Some drivers use arrays such as this, other do not.
+ */
+struct BondingBoard {
+ const char *name;
+};
+typedef struct BondingBoard BondingBoard;
+
+static const BondingBoard bondingBoards[] = {
+ {
+ name: MODULE_NAME,
+ },
+};
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((const BondingBoard *)dev->board_ptr)
+
+struct BondedDevice {
+ comedi_t *dev;
+ unsigned minor;
+ unsigned subdev;
+ unsigned subdev_type;
+ unsigned nchans;
+ unsigned chanid_offset; /* The offset into our unified linear channel-id's
+ of chanid 0 on this subdevice. */
+};
+typedef struct BondedDevice BondedDevice;
+
+/* this structure is for data unique to this hardware driver. If
+ several hardware drivers keep similar information in this structure,
+ feel free to suggest moving the variable to the comedi_device struct. */
+struct Private {
+# define MAX_BOARD_NAME 256
+ char name[MAX_BOARD_NAME];
+ struct BondedDevice **devs;
+ unsigned ndevs;
+ struct BondedDevice *chanIdDevMap[MAX_CHANS];
+ unsigned nchans;
+};
+typedef struct Private Private;
+
+/*
+ * most drivers define the following macro to make it easy to
+ * access the private structure.
+ */
+#define devpriv ((Private *)dev->private)
+
+/*
+ * The comedi_driver structure tells the Comedi core module
+ * which functions to call to configure/deconfigure (attach/detach)
+ * the board, and also about the kernel module that contains
+ * the device code.
+ */
+static int bonding_attach(comedi_device * dev, comedi_devconfig * it);
+static int bonding_detach(comedi_device * dev);
+/** Build Private array of all devices.. */
+static int doDevConfig(comedi_device * dev, comedi_devconfig * it);
+static void doDevUnconfig(comedi_device * dev);
+/* Ugly implementation of realloc that always copies memory around -- I'm lazy, what can I say? I like to do wasteful memcopies.. :) */
+static void *Realloc(const void *ptr, size_t len, size_t old_len);
+
+static comedi_driver driver_bonding = {
+ driver_name:MODULE_NAME,
+ module:THIS_MODULE,
+ attach:bonding_attach,
+ detach:bonding_detach,
+ /* It is not necessary to implement the following members if you are
+ * writing a driver for a ISA PnP or PCI card */
+ /* Most drivers will support multiple types of boards by
+ * having an array of board structures. These were defined
+ * in skel_boards[] above. Note that the element 'name'
+ * was first in the structure -- Comedi uses this fact to
+ * extract the name of the board without knowing any details
+ * about the structure except for its length.
+ * When a device is attached (by comedi_config), the name
+ * of the device is given to Comedi, and Comedi tries to
+ * match it by going through the list of board names. If
+ * there is a match, the address of the pointer is put
+ * into dev->board_ptr and driver->attach() is called.
+ *
+ * Note that these are not necessary if you can determine
+ * the type of board in software. ISA PnP, PCI, and PCMCIA
+ * devices are such boards.
+ */
+ board_name:&bondingBoards[0].name,
+ offset:sizeof(BondingBoard),
+ num_names:sizeof(bondingBoards) / sizeof(BondingBoard),
+};
+
+static int bonding_dio_insn_bits(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data);
+static int bonding_dio_insn_config(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data);
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a particular board. If you specified a board_name array
+ * in the driver structure, dev->board_ptr contains that
+ * address.
+ */
+static int bonding_attach(comedi_device * dev, comedi_devconfig * it)
+{
+ comedi_subdevice *s;
+
+ LOG_MSG("comedi%d\n", dev->minor);
+
+/*
+ * Allocate the private structure area. alloc_private() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if (alloc_private(dev, sizeof(Private)) < 0)
+ return -ENOMEM;
+
+/*
+ * Setup our bonding from config params.. sets up our Private struct..
+ */
+ if (!doDevConfig(dev, it))
+ return -EINVAL;
+
+/*
+ * Initialize dev->board_name. Note that we can use the "thisboard"
+ * macro now, since we just initialized it in the last line.
+ */
+ dev->board_name = devpriv->name;
+
+/*
+ * Allocate the subdevice structures. alloc_subdevice() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if (alloc_subdevices(dev, 1) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = devpriv->nchans;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = bonding_dio_insn_bits;
+ s->insn_config = bonding_dio_insn_config;
+
+ LOG_MSG("attached with %u DIO channels coming from %u different subdevices all bonded together. John Lennon would be proud!\n", devpriv->nchans, devpriv->ndevs);
+
+ return 1;
+}
+
+/*
+ * _detach is called to deconfigure a device. It should deallocate
+ * resources.
+ * This function is also called when _attach() fails, so it should be
+ * careful not to release resources that were not necessarily
+ * allocated by _attach(). dev->private and dev->subdevices are
+ * deallocated automatically by the core.
+ */
+static int bonding_detach(comedi_device * dev)
+{
+ LOG_MSG("comedi%d: remove\n", dev->minor);
+ doDevUnconfig(dev);
+ return 0;
+}
+
+/* DIO devices are slightly special. Although it is possible to
+ * implement the insn_read/insn_write interface, it is much more
+ * useful to applications if you implement the insn_bits interface.
+ * This allows packed reading/writing of the DIO channels. The
+ * comedi core can convert between insn_bits and insn_read/write */
+static int bonding_dio_insn_bits(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+#define LSAMPL_BITS (sizeof(lsampl_t)*8)
+ unsigned nchans = LSAMPL_BITS, num_done = 0, i;
+ if (insn->n != 2)
+ return -EINVAL;
+
+ if (devpriv->nchans < nchans)
+ nchans = devpriv->nchans;
+
+ /* The insn data is a mask in data[0] and the new data
+ * in data[1], each channel cooresponding to a bit. */
+ for (i = 0; num_done < nchans && i < devpriv->ndevs; ++i) {
+ BondedDevice *bdev = devpriv->devs[i];
+ /* Grab the channel mask and data of only the bits corresponding
+ to this subdevice.. need to shift them to zero position of
+ course. */
+ lsampl_t subdevMask = ((1 << bdev->nchans) - 1); /* Bits corresponding
+ to this subdev. */
+ lsampl_t writeMask, dataBits;
+
+ /* Argh, we have >= LSAMPL_BITS chans.. take all bits */
+ if (bdev->nchans >= LSAMPL_BITS)
+ subdevMask = (lsampl_t) (-1);
+
+ writeMask = (data[0] >> num_done) & subdevMask;
+ dataBits = (data[1] >> num_done) & subdevMask;
+
+ /* Read/Write the new digital lines */
+ if (comedi_dio_bitfield(bdev->dev, bdev->subdev, writeMask,
+ &dataBits) != 2)
+ return -EINVAL;
+
+ /* Make room for the new bits in data[1], the return value */
+ data[1] &= ~(subdevMask << num_done);
+ /* Put the bits in the return value */
+ data[1] |= (dataBits & subdevMask) << num_done;
+ /* Save the new bits to the saved state.. */
+ s->state = data[1];
+
+ num_done += bdev->nchans;
+ }
+
+ return insn->n;
+}
+
+static int bonding_dio_insn_config(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ int chan = CR_CHAN(insn->chanspec), ret, io_bits = s->io_bits;
+ unsigned int io;
+ BondedDevice *bdev;
+
+ if (chan < 0 || chan >= devpriv->nchans)
+ return -EINVAL;
+ bdev = devpriv->chanIdDevMap[chan];
+
+ /* The input or output configuration of each digital line is
+ * configured by a special insn_config instruction. chanspec
+ * contains the channel to be changed, and data[0] contains the
+ * value COMEDI_INPUT or COMEDI_OUTPUT. */
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ io = COMEDI_OUTPUT; /* is this really necessary? */
+ io_bits |= 1 << chan;
+ break;
+ case INSN_CONFIG_DIO_INPUT:
+ io = COMEDI_INPUT; /* is this really necessary? */
+ io_bits &= ~(1 << chan);
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] =
+ (io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
+ return insn->n;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ chan -= bdev->chanid_offset; /* 'real' channel id for this subdev.. */
+ ret = comedi_dio_config(bdev->dev, bdev->subdev, chan, io);
+ if (ret != 1)
+ return -EINVAL;
+ /* Finally, save the new io_bits values since we didn't get
+ an error above. */
+ s->io_bits = io_bits;
+ return insn->n;
+}
+
+static void *Realloc(const void *oldmem, size_t newlen, size_t oldlen)
+{
+#define MIN(a,b) (a < b ? a : b)
+ void *newmem = kmalloc(newlen, GFP_KERNEL);
+ if (newmem && oldmem)
+ memcpy(newmem, oldmem, MIN(oldlen, newlen));
+ if (oldmem)
+ kfree(oldmem);
+ return newmem;
+}
+
+static int doDevConfig(comedi_device * dev, comedi_devconfig * it)
+{
+ int i;
+ comedi_t *devs_opened[COMEDI_NUM_BOARD_MINORS];
+
+ memset(devs_opened, 0, sizeof(devs_opened));
+ devpriv->name[0] = 0;;
+ /* Loop through all comedi devices specified on the command-line,
+ building our device list */
+ for (i = 0; i < COMEDI_NDEVCONFOPTS && (!i || it->options[i]); ++i) {
+ char file[] = "/dev/comediXXXXXX";
+ int minor = it->options[i];
+ comedi_t *d;
+ int sdev = -1, nchans, tmp;
+ BondedDevice *bdev = 0;
+
+ if (minor < 0 || minor > COMEDI_NUM_BOARD_MINORS) {
+ ERROR("Minor %d is invalid!\n", minor);
+ return 0;
+ }
+ if (minor == dev->minor) {
+ ERROR("Cannot bond this driver to itself!\n");
+ return 0;
+ }
+ if (devs_opened[minor]) {
+ ERROR("Minor %d specified more than once!\n", minor);
+ return 0;
+ }
+
+ snprintf(file, sizeof(file), "/dev/comedi%u", minor);
+ file[sizeof(file) - 1] = 0;
+
+ d = devs_opened[minor] = comedi_open(file);
+
+ if (!d) {
+ ERROR("Minor %u could not be opened\n", minor);
+ return 0;
+ }
+
+ /* Do DIO, as that's all we support now.. */
+ while ((sdev = comedi_find_subdevice_by_type(d, COMEDI_SUBD_DIO,
+ sdev + 1)) > -1) {
+ if ((nchans = comedi_get_n_channels(d, sdev)) <= 0) {
+ ERROR("comedi_get_n_channels() returned %d on minor %u subdev %d!\n", nchans, minor, sdev);
+ return 0;
+ }
+ bdev = kmalloc(sizeof(*bdev), GFP_KERNEL);
+ if (!bdev) {
+ ERROR("Out of memory.\n");
+ return 0;
+ }
+ bdev->dev = d;
+ bdev->minor = minor;
+ bdev->subdev = sdev;
+ bdev->subdev_type = COMEDI_SUBD_DIO;
+ bdev->nchans = nchans;
+ bdev->chanid_offset = devpriv->nchans;
+
+ /* map channel id's to BondedDevice * pointer.. */
+ while (nchans--)
+ devpriv->chanIdDevMap[devpriv->nchans++] = bdev;
+
+ /* Now put bdev pointer at end of devpriv->devs array list.. */
+
+ /* ergh.. ugly.. we need to realloc :( */
+ tmp = devpriv->ndevs * sizeof(bdev);
+ devpriv->devs =
+ Realloc(devpriv->devs,
+ ++devpriv->ndevs * sizeof(bdev), tmp);
+ if (!devpriv->devs) {
+ ERROR("Could not allocate memory. Out of memory?");
+ return 0;
+ }
+
+ devpriv->devs[devpriv->ndevs - 1] = bdev;
+ {
+ /** Append dev:subdev to devpriv->name */
+ char buf[20];
+ int left =
+ MAX_BOARD_NAME - strlen(devpriv->name) -
+ 1;
+ snprintf(buf, sizeof(buf), "%d:%d ", dev->minor,
+ bdev->subdev);
+ buf[sizeof(buf) - 1] = 0;
+ strncat(devpriv->name, buf, left);
+ }
+
+ }
+ }
+
+ if (!devpriv->nchans) {
+ ERROR("No channels found!\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static void doDevUnconfig(comedi_device * dev)
+{
+ unsigned long devs_closed = 0;
+
+ if (devpriv) {
+ while (devpriv->ndevs-- && devpriv->devs) {
+ BondedDevice *bdev = devpriv->devs[devpriv->ndevs];
+ if (!bdev)
+ continue;
+ if (!(devs_closed & (0x1 << bdev->minor))) {
+ comedi_close(bdev->dev);
+ devs_closed |= (0x1 << bdev->minor);
+ }
+ kfree(bdev);
+ }
+ if (devpriv->devs) {
+ kfree(devpriv->devs);
+ devpriv->devs = 0;
+ }
+ kfree(devpriv);
+ dev->private = 0;
+ }
+}
+
+int __init init(void)
+{
+ return comedi_driver_register(&driver_bonding);
+}
+
+void __exit cleanup(void)
+{
+ comedi_driver_unregister(&driver_bonding);
+}
+
+module_init(init);
+module_exit(cleanup);
diff --git a/drivers/staging/comedi/drivers/comedi_fc.c b/drivers/staging/comedi/drivers/comedi_fc.c
new file mode 100644
index 000000000000..da8e9d2844f0
--- /dev/null
+++ b/drivers/staging/comedi/drivers/comedi_fc.c
@@ -0,0 +1,118 @@
+/*
+ comedi/drivers/comedi_fc.c
+
+ This is a place for code driver writers wish to share between
+ two or more drivers. fc is short
+ for frank-common.
+
+ Author: Frank Mori Hess <fmhess@users.sourceforge.net>
+ Copyright (C) 2002 Frank Mori Hess
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+************************************************************************/
+
+#include "../comedidev.h"
+
+#include "comedi_fc.h"
+
+static void increment_scan_progress(comedi_subdevice * subd,
+ unsigned int num_bytes)
+{
+ comedi_async *async = subd->async;
+ unsigned int scan_length = cfc_bytes_per_scan(subd);
+
+ async->scan_progress += num_bytes;
+ if (async->scan_progress >= scan_length) {
+ async->scan_progress %= scan_length;
+ async->events |= COMEDI_CB_EOS;
+ }
+}
+
+/* Writes an array of data points to comedi's buffer */
+unsigned int cfc_write_array_to_buffer(comedi_subdevice * subd, void *data,
+ unsigned int num_bytes)
+{
+ comedi_async *async = subd->async;
+ unsigned int retval;
+
+ if (num_bytes == 0)
+ return 0;
+
+ retval = comedi_buf_write_alloc(async, num_bytes);
+ if (retval != num_bytes) {
+ rt_printk("comedi: buffer overrun\n");
+ async->events |= COMEDI_CB_OVERFLOW;
+ return 0;
+ }
+
+ comedi_buf_memcpy_to(async, 0, data, num_bytes);
+ comedi_buf_write_free(async, num_bytes);
+ increment_scan_progress(subd, num_bytes);
+ async->events |= COMEDI_CB_BLOCK;
+
+ return num_bytes;
+}
+
+unsigned int cfc_read_array_from_buffer(comedi_subdevice * subd, void *data,
+ unsigned int num_bytes)
+{
+ comedi_async *async = subd->async;
+
+ if (num_bytes == 0)
+ return 0;
+
+ num_bytes = comedi_buf_read_alloc(async, num_bytes);
+ comedi_buf_memcpy_from(async, 0, data, num_bytes);
+ comedi_buf_read_free(async, num_bytes);
+ increment_scan_progress(subd, num_bytes);
+ async->events |= COMEDI_CB_BLOCK;
+
+ return num_bytes;
+}
+
+unsigned int cfc_handle_events(comedi_device * dev, comedi_subdevice * subd)
+{
+ unsigned int events = subd->async->events;
+
+ if (events == 0)
+ return events;
+
+ if (events & (COMEDI_CB_EOA | COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW))
+ subd->cancel(dev, subd);
+
+ comedi_event(dev, subd);
+
+ return events;
+}
+
+MODULE_AUTHOR("Frank Mori Hess <fmhess@users.sourceforge.net>");
+MODULE_DESCRIPTION("Shared functions for Comedi low-level drivers");
+MODULE_LICENSE("GPL");
+
+static int __init comedi_fc_init_module(void)
+{
+ return 0;
+}
+static void __exit comedi_fc_cleanup_module(void)
+{
+}
+
+module_init(comedi_fc_init_module);
+module_exit(comedi_fc_cleanup_module);
+
+EXPORT_SYMBOL(cfc_write_array_to_buffer);
+EXPORT_SYMBOL(cfc_read_array_from_buffer);
+EXPORT_SYMBOL(cfc_handle_events);
diff --git a/drivers/staging/comedi/drivers/comedi_fc.h b/drivers/staging/comedi/drivers/comedi_fc.h
new file mode 100644
index 000000000000..b8edd673edfd
--- /dev/null
+++ b/drivers/staging/comedi/drivers/comedi_fc.h
@@ -0,0 +1,75 @@
+/*
+ comedi_fc.h
+
+ This is a place for code driver writers wish to share between
+ two or more drivers. These functions are meant to be used only
+ by drivers, they are NOT part of the kcomedilib API!
+
+ Author: Frank Mori Hess <fmhess@users.sourceforge.net>
+ Copyright (C) 2002 Frank Mori Hess
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+************************************************************************/
+
+#ifndef _COMEDI_FC_H
+#define _COMEDI_FC_H
+
+#include "../comedidev.h"
+
+/* Writes an array of data points to comedi's buffer */
+extern unsigned int cfc_write_array_to_buffer(comedi_subdevice * subd,
+ void *data, unsigned int num_bytes);
+
+static inline unsigned int cfc_write_to_buffer(comedi_subdevice * subd,
+ sampl_t data)
+{
+ return cfc_write_array_to_buffer(subd, &data, sizeof(data));
+};
+
+static inline unsigned int cfc_write_long_to_buffer(comedi_subdevice * subd,
+ lsampl_t data)
+{
+ return cfc_write_array_to_buffer(subd, &data, sizeof(data));
+};
+
+extern unsigned int cfc_read_array_from_buffer(comedi_subdevice * subd,
+ void *data, unsigned int num_bytes);
+
+extern unsigned int cfc_handle_events(comedi_device * dev,
+ comedi_subdevice * subd);
+
+static inline unsigned int cfc_bytes_per_scan(comedi_subdevice * subd)
+{
+ int num_samples;
+ int bits_per_sample;
+
+ switch (subd->type) {
+ case COMEDI_SUBD_DI:
+ case COMEDI_SUBD_DO:
+ case COMEDI_SUBD_DIO:
+ bits_per_sample = 8 * bytes_per_sample(subd);
+ num_samples =
+ (subd->async->cmd.chanlist_len + bits_per_sample -
+ 1) / bits_per_sample;
+ break;
+ default:
+ num_samples = subd->async->cmd.chanlist_len;
+ break;
+ }
+ return num_samples * bytes_per_sample(subd);
+}
+
+#endif /* _COMEDI_FC_H */
diff --git a/drivers/staging/comedi/drivers/comedi_parport.c b/drivers/staging/comedi/drivers/comedi_parport.c
new file mode 100644
index 000000000000..9530c47806cb
--- /dev/null
+++ b/drivers/staging/comedi/drivers/comedi_parport.c
@@ -0,0 +1,387 @@
+/*
+ comedi/drivers/comedi_parport.c
+ hardware driver for standard parallel port
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998,2001 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: comedi_parport
+Description: Standard PC parallel port
+Author: ds
+Status: works in immediate mode
+Devices: [standard] parallel port (comedi_parport)
+Updated: Tue, 30 Apr 2002 21:11:45 -0700
+
+A cheap and easy way to get a few more digital I/O lines. Steal
+additional parallel ports from old computers or your neighbors'
+computers.
+
+Option list:
+ 0: I/O port base for the parallel port.
+ 1: IRQ
+
+Parallel Port Lines:
+
+pin subdev chan aka
+--- ------ ---- ---
+1 2 0 strobe
+2 0 0 data 0
+3 0 1 data 1
+4 0 2 data 2
+5 0 3 data 3
+6 0 4 data 4
+7 0 5 data 5
+8 0 6 data 6
+9 0 7 data 7
+10 1 3 acknowledge
+11 1 4 busy
+12 1 2 output
+13 1 1 printer selected
+14 2 1 auto LF
+15 1 0 error
+16 2 2 init
+17 2 3 select printer
+18-25 ground
+
+Notes:
+
+Subdevices 0 is digital I/O, subdevice 1 is digital input, and
+subdevice 2 is digital output. Unlike other Comedi devices,
+subdevice 0 defaults to output.
+
+Pins 13 and 14 are inverted once by Comedi and once by the
+hardware, thus cancelling the effect.
+
+Pin 1 is a strobe, thus acts like one. There's no way in software
+to change this, at least on a standard parallel port.
+
+Subdevice 3 pretends to be a digital input subdevice, but it always
+returns 0 when read. However, if you run a command with
+scan_begin_src=TRIG_EXT, it uses pin 10 as a external triggering
+pin, which can be used to wake up tasks.
+*/
+/*
+ see http://www.beyondlogic.org/ for information.
+ or http://www.linux-magazin.de/ausgabe/1999/10/IO/io.html
+ */
+
+#include "../comedidev.h"
+#include <linux/ioport.h>
+
+#define PARPORT_SIZE 3
+
+#define PARPORT_A 0
+#define PARPORT_B 1
+#define PARPORT_C 2
+
+static int parport_attach(comedi_device * dev, comedi_devconfig * it);
+static int parport_detach(comedi_device * dev);
+static comedi_driver driver_parport = {
+ driver_name:"comedi_parport",
+ module:THIS_MODULE,
+ attach:parport_attach,
+ detach:parport_detach,
+};
+
+COMEDI_INITCLEANUP(driver_parport);
+
+typedef struct parport_private_struct {
+ unsigned int a_data;
+ unsigned int c_data;
+ int enable_irq;
+} parport_private;
+#define devpriv ((parport_private *)(dev->private))
+
+static int parport_insn_a(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ if (data[0]) {
+ devpriv->a_data &= ~data[0];
+ devpriv->a_data |= (data[0] & data[1]);
+
+ outb(devpriv->a_data, dev->iobase + PARPORT_A);
+ }
+
+ data[1] = inb(dev->iobase + PARPORT_A);
+
+ return 2;
+}
+
+static int parport_insn_config_a(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ if (data[0]) {
+ s->io_bits = 0xff;
+ devpriv->c_data &= ~(1 << 5);
+ } else {
+ s->io_bits = 0;
+ devpriv->c_data |= (1 << 5);
+ }
+ outb(devpriv->c_data, dev->iobase + PARPORT_C);
+
+ return 1;
+}
+
+static int parport_insn_b(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ if (data[0]) {
+ // should writes be ignored?
+ }
+
+ data[1] = (inb(dev->iobase + PARPORT_B) >> 3);
+
+ return 2;
+}
+
+static int parport_insn_c(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ data[0] &= 0x0f;
+ if (data[0]) {
+ devpriv->c_data &= ~data[0];
+ devpriv->c_data |= (data[0] & data[1]);
+
+ outb(devpriv->c_data, dev->iobase + PARPORT_C);
+ }
+
+ data[1] = devpriv->c_data & 0xf;
+
+ return 2;
+}
+
+static int parport_intr_insn(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ if (insn->n < 1)
+ return -EINVAL;
+
+ data[1] = 0;
+ return 2;
+}
+
+static int parport_intr_cmdtest(comedi_device * dev, comedi_subdevice * s,
+ comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+
+ /* step 1 */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_FOLLOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: ignored */
+
+ if (err)
+ return 2;
+
+ /* step 3: */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+ if (cmd->convert_arg != 0) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+ if (cmd->scan_end_arg != 1) {
+ cmd->scan_end_arg = 1;
+ err++;
+ }
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: ignored */
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int parport_intr_cmd(comedi_device * dev, comedi_subdevice * s)
+{
+ devpriv->c_data |= 0x10;
+ outb(devpriv->c_data, dev->iobase + PARPORT_C);
+
+ devpriv->enable_irq = 1;
+
+ return 0;
+}
+
+static int parport_intr_cancel(comedi_device * dev, comedi_subdevice * s)
+{
+ printk("parport_intr_cancel()\n");
+
+ devpriv->c_data &= ~0x10;
+ outb(devpriv->c_data, dev->iobase + PARPORT_C);
+
+ devpriv->enable_irq = 0;
+
+ return 0;
+}
+
+static irqreturn_t parport_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ comedi_device *dev = d;
+ comedi_subdevice *s = dev->subdevices + 3;
+
+ if (!devpriv->enable_irq) {
+ printk("comedi_parport: bogus irq, ignored\n");
+ return IRQ_NONE;
+ }
+
+ comedi_buf_put(s->async, 0);
+ s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
+
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+}
+
+static int parport_attach(comedi_device * dev, comedi_devconfig * it)
+{
+ int ret;
+ unsigned int irq;
+ unsigned long iobase;
+ comedi_subdevice *s;
+
+ iobase = it->options[0];
+ printk("comedi%d: parport: 0x%04lx ", dev->minor, iobase);
+ if (!request_region(iobase, PARPORT_SIZE, "parport (comedi)")) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+ dev->iobase = iobase;
+
+ irq = it->options[1];
+ if (irq) {
+ printk(" irq=%u", irq);
+ ret = comedi_request_irq(irq, parport_interrupt, 0,
+ "comedi_parport", dev);
+ if (ret < 0) {
+ printk(" irq not available\n");
+ return -EINVAL;
+ }
+ dev->irq = irq;
+ }
+ dev->board_name = "parport";
+
+ if ((ret = alloc_subdevices(dev, 4)) < 0)
+ return ret;
+ if ((ret = alloc_private(dev, sizeof(parport_private))) < 0)
+ return ret;
+
+ s = dev->subdevices + 0;
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = parport_insn_a;
+ s->insn_config = parport_insn_config_a;
+
+ s = dev->subdevices + 1;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 5;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = parport_insn_b;
+
+ s = dev->subdevices + 2;
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = parport_insn_c;
+
+ s = dev->subdevices + 3;
+ if (irq) {
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
+ s->n_chan = 1;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = parport_intr_insn;
+ s->do_cmdtest = parport_intr_cmdtest;
+ s->do_cmd = parport_intr_cmd;
+ s->cancel = parport_intr_cancel;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ devpriv->a_data = 0;
+ outb(devpriv->a_data, dev->iobase + PARPORT_A);
+ devpriv->c_data = 0;
+ outb(devpriv->c_data, dev->iobase + PARPORT_C);
+
+ printk("\n");
+ return 1;
+}
+
+static int parport_detach(comedi_device * dev)
+{
+ printk("comedi%d: parport: remove\n", dev->minor);
+
+ if (dev->iobase)
+ release_region(dev->iobase, PARPORT_SIZE);
+
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/comedi_pci.h b/drivers/staging/comedi/drivers/comedi_pci.h
new file mode 100644
index 000000000000..140787839153
--- /dev/null
+++ b/drivers/staging/comedi/drivers/comedi_pci.h
@@ -0,0 +1,89 @@
+/*
+ comedi/drivers/comedi_pci.h
+ Various PCI functions for drivers.
+
+ Copyright (C) 2007 MEV Ltd. <http://www.mev.co.uk/>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef _COMEDI_PCI_H_
+#define _COMEDI_PCI_H_
+
+#include <linux/version.h>
+#include <linux/pci.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+#define PCI_ENABLE_IS_REFCOUNTED
+#endif
+
+/*
+ * Enables PCI device without requesting regions. Just a simple wrapper
+ * for pci_enable_device().
+ */
+static inline int comedi_pci_enable_no_regions(struct pci_dev *pdev)
+{
+ return pci_enable_device(pdev);
+}
+
+/*
+ * Called to disable PCI device if PCI device has been enabled, but
+ * PCI regions have not been reserved.
+ *
+ * It only disables the PCI device if the kernel supports reference
+ * counting of PCI enables, otherwise it might stop the device working
+ * in another driver instance.
+ */
+static inline void comedi_pci_disable_no_regions(struct pci_dev *pdev)
+{
+#ifdef PCI_ENABLE_IS_REFCOUNTED
+ pci_disable_device(pdev);
+#endif
+}
+
+/*
+ * Enable the PCI device and request the regions.
+ */
+static inline int comedi_pci_enable(struct pci_dev *pdev, const char *res_name)
+{
+ int rc;
+
+ rc = pci_enable_device(pdev);
+ if (rc < 0) {
+ return rc;
+ }
+ rc = pci_request_regions(pdev, res_name);
+ if (rc < 0) {
+ comedi_pci_disable_no_regions(pdev);
+ }
+ return rc;
+}
+
+/*
+ * Release the regions and disable the PCI device.
+ *
+ * This must be matched with a previous successful call to comedi_pci_enable().
+ */
+static inline void comedi_pci_disable(struct pci_dev *pdev)
+{
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+}
+
+#endif
diff --git a/drivers/staging/comedi/drivers/comedi_test.c b/drivers/staging/comedi/drivers/comedi_test.c
new file mode 100644
index 000000000000..356a9e9c3abd
--- /dev/null
+++ b/drivers/staging/comedi/drivers/comedi_test.c
@@ -0,0 +1,526 @@
+/*
+ comedi/drivers/comedi_test.c
+
+ Generates fake waveform signals that can be read through
+ the command interface. It does _not_ read from any board;
+ it just generates deterministic waveforms.
+ Useful for various testing purposes.
+
+ Copyright (C) 2002 Joachim Wuttke <Joachim.Wuttke@icn.siemens.de>
+ Copyright (C) 2002 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+************************************************************************/
+/*
+Driver: comedi_test
+Description: generates fake waveforms
+Author: Joachim Wuttke <Joachim.Wuttke@icn.siemens.de>, Frank Mori Hess
+ <fmhess@users.sourceforge.net>, ds
+Devices:
+Status: works
+Updated: Sat, 16 Mar 2002 17:34:48 -0800
+
+This driver is mainly for testing purposes, but can also be used to
+generate sample waveforms on systems that don't have data acquisition
+hardware.
+
+Configuration options:
+ [0] - Amplitude in microvolts for fake waveforms (default 1 volt)
+ [1] - Period in microseconds for fake waveforms (default 0.1 sec)
+
+Generates a sawtooth wave on channel 0, square wave on channel 1, additional
+waveforms could be added to other channels (currently they return flatline
+zero volts).
+
+*/
+
+#include "../comedidev.h"
+
+#include <asm/div64.h>
+
+#include "comedi_fc.h"
+
+/* Board descriptions */
+typedef struct waveform_board_struct {
+ const char *name;
+ int ai_chans;
+ int ai_bits;
+ int have_dio;
+} waveform_board;
+
+#define N_CHANS 8
+
+static const waveform_board waveform_boards[] = {
+ {
+ name: "comedi_test",
+ ai_chans:N_CHANS,
+ ai_bits: 16,
+ have_dio:0,
+ },
+};
+
+#define thisboard ((const waveform_board *)dev->board_ptr)
+
+/* Data unique to this driver */
+typedef struct {
+ struct timer_list timer;
+ struct timeval last; // time at which last timer interrupt occured
+ unsigned int uvolt_amplitude; // waveform amplitude in microvolts
+ unsigned long usec_period; // waveform period in microseconds
+ volatile unsigned long usec_current; // current time (modulo waveform period)
+ volatile unsigned long usec_remainder; // usec since last scan;
+ volatile unsigned long ai_count; // number of conversions remaining
+ unsigned int scan_period; // scan period in usec
+ unsigned int convert_period; // conversion period in usec
+ volatile unsigned timer_running:1;
+ volatile lsampl_t ao_loopbacks[N_CHANS];
+} waveform_private;
+#define devpriv ((waveform_private *)dev->private)
+
+static int waveform_attach(comedi_device * dev, comedi_devconfig * it);
+static int waveform_detach(comedi_device * dev);
+static comedi_driver driver_waveform = {
+ driver_name:"comedi_test",
+ module:THIS_MODULE,
+ attach:waveform_attach,
+ detach:waveform_detach,
+ board_name:&waveform_boards[0].name,
+ offset:sizeof(waveform_board),
+ num_names:sizeof(waveform_boards) / sizeof(waveform_board),
+};
+
+COMEDI_INITCLEANUP(driver_waveform);
+
+static int waveform_ai_cmdtest(comedi_device * dev, comedi_subdevice * s,
+ comedi_cmd * cmd);
+static int waveform_ai_cmd(comedi_device * dev, comedi_subdevice * s);
+static int waveform_ai_cancel(comedi_device * dev, comedi_subdevice * s);
+static int waveform_ai_insn_read(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data);
+static int waveform_ao_insn_write(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data);
+static sampl_t fake_sawtooth(comedi_device * dev, unsigned int range,
+ unsigned long current_time);
+static sampl_t fake_squarewave(comedi_device * dev, unsigned int range,
+ unsigned long current_time);
+static sampl_t fake_flatline(comedi_device * dev, unsigned int range,
+ unsigned long current_time);
+static sampl_t fake_waveform(comedi_device * dev, unsigned int channel,
+ unsigned int range, unsigned long current_time);
+
+static const int nano_per_micro = 1000; // 1000 nanosec in a microsec
+
+// fake analog input ranges
+static const comedi_lrange waveform_ai_ranges = {
+ 2,
+ {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ }
+};
+
+/*
+ This is the background routine used to generate arbitrary data.
+ It should run in the background; therefore it is scheduled by
+ a timer mechanism.
+*/
+static void waveform_ai_interrupt(unsigned long arg)
+{
+ comedi_device *dev = (comedi_device *) arg;
+ comedi_async *async = dev->read_subdev->async;
+ comedi_cmd *cmd = &async->cmd;
+ unsigned int i, j;
+ // all times in microsec
+ unsigned long elapsed_time;
+ unsigned int num_scans;
+ struct timeval now;
+
+ do_gettimeofday(&now);
+
+ elapsed_time =
+ 1000000 * (now.tv_sec - devpriv->last.tv_sec) + now.tv_usec -
+ devpriv->last.tv_usec;
+ devpriv->last = now;
+ num_scans =
+ (devpriv->usec_remainder + elapsed_time) / devpriv->scan_period;
+ devpriv->usec_remainder =
+ (devpriv->usec_remainder + elapsed_time) % devpriv->scan_period;
+ async->events = 0;
+
+ for (i = 0; i < num_scans; i++) {
+ for (j = 0; j < cmd->chanlist_len; j++) {
+ cfc_write_to_buffer(dev->read_subdev,
+ fake_waveform(dev, CR_CHAN(cmd->chanlist[j]),
+ CR_RANGE(cmd->chanlist[j]),
+ devpriv->usec_current +
+ i * devpriv->scan_period +
+ j * devpriv->convert_period));
+ }
+ devpriv->ai_count++;
+ if (cmd->stop_src == TRIG_COUNT
+ && devpriv->ai_count >= cmd->stop_arg) {
+ async->events |= COMEDI_CB_EOA;
+ break;
+ }
+ }
+
+ devpriv->usec_current += elapsed_time;
+ devpriv->usec_current %= devpriv->usec_period;
+
+ if ((async->events & COMEDI_CB_EOA) == 0 && devpriv->timer_running)
+ mod_timer(&devpriv->timer, jiffies + 1);
+ else
+ del_timer(&devpriv->timer);
+
+ comedi_event(dev, dev->read_subdev);
+}
+
+static int waveform_attach(comedi_device * dev, comedi_devconfig * it)
+{
+ comedi_subdevice *s;
+ int amplitude = it->options[0];
+ int period = it->options[1];
+
+ printk("comedi%d: comedi_test: ", dev->minor);
+
+ dev->board_name = thisboard->name;
+
+ if (alloc_private(dev, sizeof(waveform_private)) < 0)
+ return -ENOMEM;
+
+ // set default amplitude and period
+ if (amplitude <= 0)
+ amplitude = 1000000; // 1 volt
+ if (period <= 0)
+ period = 100000; // 0.1 sec
+
+ devpriv->uvolt_amplitude = amplitude;
+ devpriv->usec_period = period;
+
+ printk("%i microvolt, %li microsecond waveform ",
+ devpriv->uvolt_amplitude, devpriv->usec_period);
+ dev->n_subdevices = 2;
+ if (alloc_subdevices(dev, dev->n_subdevices) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ dev->read_subdev = s;
+ /* analog input subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
+ s->n_chan = thisboard->ai_chans;
+ s->maxdata = (1 << thisboard->ai_bits) - 1;
+ s->range_table = &waveform_ai_ranges;
+ s->len_chanlist = s->n_chan * 2;
+ s->insn_read = waveform_ai_insn_read;
+ s->do_cmd = waveform_ai_cmd;
+ s->do_cmdtest = waveform_ai_cmdtest;
+ s->cancel = waveform_ai_cancel;
+
+ s = dev->subdevices + 1;
+ dev->write_subdev = s;
+ /* analog output subdevice (loopback) */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITEABLE | SDF_GROUND;
+ s->n_chan = thisboard->ai_chans;
+ s->maxdata = (1 << thisboard->ai_bits) - 1;
+ s->range_table = &waveform_ai_ranges;
+ s->len_chanlist = s->n_chan * 2;
+ s->insn_write = waveform_ao_insn_write;
+ s->do_cmd = 0;
+ s->do_cmdtest = 0;
+ s->cancel = 0;
+ {
+ /* Our default loopback value is just a 0V flatline */
+ int i;
+ for (i = 0; i < s->n_chan; i++)
+ devpriv->ao_loopbacks[i] = s->maxdata / 2;
+ }
+
+ init_timer(&(devpriv->timer));
+ devpriv->timer.function = waveform_ai_interrupt;
+ devpriv->timer.data = (unsigned long)dev;
+
+ printk("attached\n");
+
+ return 1;
+}
+
+static int waveform_detach(comedi_device * dev)
+{
+ printk("comedi%d: comedi_test: remove\n", dev->minor);
+
+ if (dev->private) {
+ waveform_ai_cancel(dev, dev->read_subdev);
+ }
+
+ return 0;
+}
+
+static int waveform_ai_cmdtest(comedi_device * dev, comedi_subdevice * s,
+ comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_NOW | TRIG_TIMER;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ if (cmd->convert_src != TRIG_NOW && cmd->convert_src != TRIG_TIMER)
+ err++;
+ if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+ if (cmd->convert_src == TRIG_NOW) {
+ if (cmd->convert_arg != 0) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+ }
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ if (cmd->scan_begin_arg < nano_per_micro) {
+ cmd->scan_begin_arg = nano_per_micro;
+ err++;
+ }
+ if (cmd->convert_src == TRIG_TIMER &&
+ cmd->scan_begin_arg <
+ cmd->convert_arg * cmd->chanlist_len) {
+ cmd->scan_begin_arg =
+ cmd->convert_arg * cmd->chanlist_len;
+ err++;
+ }
+ }
+ // XXX these checks are generic and should go in core if not there already
+ if (!cmd->chanlist_len) {
+ cmd->chanlist_len = 1;
+ err++;
+ }
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (!cmd->stop_arg) {
+ cmd->stop_arg = 1;
+ err++;
+ }
+ } else { /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tmp = cmd->scan_begin_arg;
+ // round to nearest microsec
+ cmd->scan_begin_arg =
+ nano_per_micro * ((tmp +
+ (nano_per_micro / 2)) / nano_per_micro);
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ tmp = cmd->convert_arg;
+ // round to nearest microsec
+ cmd->convert_arg =
+ nano_per_micro * ((tmp +
+ (nano_per_micro / 2)) / nano_per_micro);
+ if (tmp != cmd->convert_arg)
+ err++;
+ }
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int waveform_ai_cmd(comedi_device * dev, comedi_subdevice * s)
+{
+ comedi_cmd *cmd = &s->async->cmd;
+
+ if (cmd->flags & TRIG_RT) {
+ comedi_error(dev,
+ "commands at RT priority not supported in this driver");
+ return -1;
+ }
+
+ devpriv->timer_running = 1;
+ devpriv->ai_count = 0;
+ devpriv->scan_period = cmd->scan_begin_arg / nano_per_micro;
+
+ if (cmd->convert_src == TRIG_NOW)
+ devpriv->convert_period = 0;
+ else if (cmd->convert_src == TRIG_TIMER)
+ devpriv->convert_period = cmd->convert_arg / nano_per_micro;
+ else {
+ comedi_error(dev, "bug setting conversion period");
+ return -1;
+ }
+
+ do_gettimeofday(&devpriv->last);
+ devpriv->usec_current = devpriv->last.tv_usec % devpriv->usec_period;
+ devpriv->usec_remainder = 0;
+
+ devpriv->timer.expires = jiffies + 1;
+ add_timer(&devpriv->timer);
+ return 0;
+}
+
+static int waveform_ai_cancel(comedi_device * dev, comedi_subdevice * s)
+{
+ devpriv->timer_running = 0;
+ del_timer(&devpriv->timer);
+ return 0;
+}
+
+static sampl_t fake_sawtooth(comedi_device * dev, unsigned int range_index,
+ unsigned long current_time)
+{
+ comedi_subdevice *s = dev->read_subdev;
+ unsigned int offset = s->maxdata / 2;
+ u64 value;
+ const comedi_krange *krange = &s->range_table->range[range_index];
+ u64 binary_amplitude;
+
+ binary_amplitude = s->maxdata;
+ binary_amplitude *= devpriv->uvolt_amplitude;
+ do_div(binary_amplitude, krange->max - krange->min);
+
+ current_time %= devpriv->usec_period;
+ value = current_time;
+ value *= binary_amplitude * 2;
+ do_div(value, devpriv->usec_period);
+ value -= binary_amplitude; // get rid of sawtooth's dc offset
+
+ return offset + value;
+}
+static sampl_t fake_squarewave(comedi_device * dev, unsigned int range_index,
+ unsigned long current_time)
+{
+ comedi_subdevice *s = dev->read_subdev;
+ unsigned int offset = s->maxdata / 2;
+ u64 value;
+ const comedi_krange *krange = &s->range_table->range[range_index];
+ current_time %= devpriv->usec_period;
+
+ value = s->maxdata;
+ value *= devpriv->uvolt_amplitude;
+ do_div(value, krange->max - krange->min);
+
+ if (current_time < devpriv->usec_period / 2)
+ value *= -1;
+
+ return offset + value;
+}
+
+static sampl_t fake_flatline(comedi_device * dev, unsigned int range_index,
+ unsigned long current_time)
+{
+ return dev->read_subdev->maxdata / 2;
+}
+
+// generates a different waveform depending on what channel is read
+static sampl_t fake_waveform(comedi_device * dev, unsigned int channel,
+ unsigned int range, unsigned long current_time)
+{
+ enum {
+ SAWTOOTH_CHAN,
+ SQUARE_CHAN,
+ };
+ switch (channel) {
+ case SAWTOOTH_CHAN:
+ return fake_sawtooth(dev, range, current_time);
+ break;
+ case SQUARE_CHAN:
+ return fake_squarewave(dev, range, current_time);
+ break;
+ default:
+ break;
+ }
+
+ return fake_flatline(dev, range, current_time);
+}
+
+static int waveform_ai_insn_read(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ int i, chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = devpriv->ao_loopbacks[chan];
+
+ return insn->n;
+}
+
+static int waveform_ao_insn_write(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ int i, chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++)
+ devpriv->ao_loopbacks[chan] = data[i];
+
+ return insn->n;
+}
diff --git a/drivers/staging/comedi/drivers/dt9812.c b/drivers/staging/comedi/drivers/dt9812.c
new file mode 100644
index 000000000000..d6fb16240061
--- /dev/null
+++ b/drivers/staging/comedi/drivers/dt9812.c
@@ -0,0 +1,957 @@
+/*
+ * comedi/drivers/dt9812.c
+ * COMEDI driver for DataTranslation DT9812 USB module
+ *
+ * Copyright (C) 2005 Anders Blomdell <anders.blomdell@control.lth.se>
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+Driver: dt9812
+Description: Data Translation DT9812 USB module
+Author: anders.blomdell@control.lth.se (Anders Blomdell)
+Status: in development
+Devices: [Data Translation] DT9812 (dt9812)
+Updated: Sun Nov 20 20:18:34 EST 2005
+
+This driver works, but bulk transfers not implemented. Might be a starting point
+for someone else. I found out too late that USB has too high latencies (>1 ms)
+for my needs.
+*/
+
+/*
+ * Nota Bene:
+ * 1. All writes to command pipe has to be 32 bytes (ISP1181B SHRTP=0 ?)
+ * 2. The DDK source (as of sep 2005) is in error regarding the
+ * input MUX bits (example code says P4, but firmware schematics
+ * says P1).
+ */
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+#include "../comedidev.h"
+#include "dt9812.h"
+
+#define DT9812_NUM_SLOTS 16
+
+static DECLARE_MUTEX(dt9812_mutex);
+
+static struct usb_device_id dt9812_table[] = {
+ {USB_DEVICE(0x0867, 0x9812)},
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, dt9812_table);
+
+typedef struct usb_dt9812 {
+ struct slot_dt9812 *slot;
+ struct usb_device *udev;
+ struct usb_interface *interface;
+ u16 vendor;
+ u16 product;
+ u16 device;
+ u32 serial;
+ struct {
+ __u8 addr;
+ size_t size;
+ } message_pipe, command_write, command_read, write_stream, read_stream;
+ struct kref kref;
+ u16 analog_out_shadow[2];
+ u8 digital_out_shadow;
+} usb_dt9812_t;
+
+typedef struct comedi_dt9812 {
+ struct slot_dt9812 *slot;
+ u32 serial;
+} comedi_dt9812_t;
+
+typedef struct slot_dt9812 {
+ struct semaphore mutex;
+ u32 serial;
+ usb_dt9812_t *usb;
+ comedi_dt9812_t *comedi;
+} slot_dt9812_t;
+
+static const comedi_lrange dt9812_10_ain_range = { 1, {
+ BIP_RANGE(10),
+ }
+};
+
+static const comedi_lrange dt9812_2pt5_ain_range = { 1, {
+ UNI_RANGE(2.5),
+ }
+};
+
+static const comedi_lrange dt9812_10_aout_range = { 1, {
+ BIP_RANGE(10),
+ }
+};
+
+static const comedi_lrange dt9812_2pt5_aout_range = { 1, {
+ UNI_RANGE(2.5),
+ }
+};
+
+static slot_dt9812_t dt9812[DT9812_NUM_SLOTS];
+
+// Useful shorthand access to private data
+#define devpriv ((comedi_dt9812_t *)dev->private)
+
+static inline usb_dt9812_t *to_dt9812_dev(struct kref *d)
+{
+ return container_of(d, usb_dt9812_t, kref);
+}
+
+static void dt9812_delete(struct kref *kref)
+{
+ usb_dt9812_t *dev = to_dt9812_dev(kref);
+
+ usb_put_dev(dev->udev);
+ kfree(dev);
+}
+
+static int dt9812_read_info(usb_dt9812_t * dev,
+ int offset, void *buf, size_t buf_size)
+{
+ dt9812_usb_cmd_t cmd;
+ int count, retval;
+
+ cmd.cmd = cpu_to_le32(DT9812_R_FLASH_DATA);
+ cmd.u.flash_data_info.address =
+ cpu_to_le16(DT9812_DIAGS_BOARD_INFO_ADDR + offset);
+ cmd.u.flash_data_info.numbytes = cpu_to_le16(buf_size);
+
+ count = 32;
+ retval = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, dev->command_write.addr), &cmd, 32, // DT9812 only responds to 32 byte writes!!
+ &count, HZ * 1);
+ if (retval == 0) {
+ retval = usb_bulk_msg(dev->udev,
+ usb_rcvbulkpipe(dev->udev, dev->command_read.addr),
+ buf, buf_size, &count, HZ * 1);
+ }
+ return retval;
+}
+
+static int dt9812_read_multiple_registers(usb_dt9812_t * dev,
+ int reg_count, u8 * address, u8 * value)
+{
+ dt9812_usb_cmd_t cmd;
+ int i, count, retval;
+
+ cmd.cmd = cpu_to_le32(DT9812_R_MULTI_BYTE_REG);
+ cmd.u.read_multi_info.count = reg_count;
+ for (i = 0; i < reg_count; i++) {
+ cmd.u.read_multi_info.address[i] = address[i];
+ }
+ count = 32;
+ retval = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, dev->command_write.addr), &cmd, 32, // DT9812 only responds to 32 byte writes!!
+ &count, HZ * 1);
+ if (retval == 0) {
+ retval = usb_bulk_msg(dev->udev,
+ usb_rcvbulkpipe(dev->udev, dev->command_read.addr),
+ value, reg_count, &count, HZ * 1);
+ }
+ return retval;
+}
+
+static int dt9812_write_multiple_registers(usb_dt9812_t * dev,
+ int reg_count, u8 * address, u8 * value)
+{
+ dt9812_usb_cmd_t cmd;
+ int i, count, retval;
+
+ cmd.cmd = cpu_to_le32(DT9812_W_MULTI_BYTE_REG);
+ cmd.u.read_multi_info.count = reg_count;
+ for (i = 0; i < reg_count; i++) {
+ cmd.u.write_multi_info.write[i].address = address[i];
+ cmd.u.write_multi_info.write[i].value = value[i];
+ }
+ retval = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, dev->command_write.addr), &cmd, 32, // DT9812 only responds to 32 byte writes!!
+ &count, HZ * 1);
+ return retval;
+}
+
+static int dt9812_rmw_multiple_registers(usb_dt9812_t * dev,
+ int reg_count, dt9812_rmw_byte_t rmw[])
+{
+ dt9812_usb_cmd_t cmd;
+ int i, count, retval;
+
+ cmd.cmd = cpu_to_le32(DT9812_RMW_MULTI_BYTE_REG);
+ cmd.u.rmw_multi_info.count = reg_count;
+ for (i = 0; i < reg_count; i++) {
+ cmd.u.rmw_multi_info.rmw[i] = rmw[i];
+ }
+ retval = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, dev->command_write.addr), &cmd, 32, // DT9812 only responds to 32 byte writes!!
+ &count, HZ * 1);
+ return retval;
+}
+
+static int dt9812_digital_in(slot_dt9812_t * slot, u8 * bits)
+{
+ int result = -ENODEV;
+
+ down(&slot->mutex);
+ if (slot->usb) {
+ u8 reg[2] = { F020_SFR_P3, F020_SFR_P1 };
+ u8 value[2];
+
+ result = dt9812_read_multiple_registers(slot->usb, 2, reg,
+ value);
+ if (result == 0) {
+ // bits 0-6 in F020_SFR_P3 are bits 0-6 in the digital input port
+ // bit 3 in F020_SFR_P1 is bit 7 in the digital input port
+ *bits = (value[0] & 0x7f) | ((value[1] & 0x08) << 4);
+// printk("%2.2x, %2.2x -> %2.2x\n", value[0], value[1], *bits);
+ }
+ }
+ up(&slot->mutex);
+
+ return result;
+}
+
+static int dt9812_digital_out(slot_dt9812_t * slot, u8 bits)
+{
+ int result = -ENODEV;
+
+ down(&slot->mutex);
+ if (slot->usb) {
+ u8 reg[1];
+ u8 value[1];
+
+ reg[0] = F020_SFR_P2;
+ value[0] = bits;
+ result = dt9812_write_multiple_registers(slot->usb, 1, reg,
+ value);
+ slot->usb->digital_out_shadow = bits;
+ }
+ up(&slot->mutex);
+ return result;
+}
+
+static int dt9812_digital_out_shadow(slot_dt9812_t * slot, u8 * bits)
+{
+ int result = -ENODEV;
+
+ down(&slot->mutex);
+ if (slot->usb) {
+ *bits = slot->usb->digital_out_shadow;
+ result = 0;
+ }
+ up(&slot->mutex);
+ return result;
+}
+
+static void dt9812_configure_mux(usb_dt9812_t * dev,
+ dt9812_rmw_byte_t * rmw, int channel)
+{
+ if (dev->device == DT9812_DEVID_DT9812_10) {
+ // In the DT9812/10V MUX is selected by P1.5-7
+ rmw->address = F020_SFR_P1;
+ rmw->and_mask = 0xe0;
+ rmw->or_value = channel << 5;
+ } else {
+ // In the DT9812/2.5V, the internal mux is selected by bits 0:2
+ rmw->address = F020_SFR_AMX0SL;
+ rmw->and_mask = 0xff;
+ rmw->or_value = channel & 0x07;
+ }
+}
+
+static void dt9812_configure_gain(usb_dt9812_t * dev,
+ dt9812_rmw_byte_t * rmw, dt9812_gain_t gain)
+{
+ if (dev->device == DT9812_DEVID_DT9812_10) {
+ // In the DT9812/10V, there is an external gain of 0.5
+ gain <<= 1;
+ }
+
+ rmw->address = F020_SFR_ADC0CF;
+ rmw->and_mask =
+ F020_MASK_ADC0CF_AMP0GN2 |
+ F020_MASK_ADC0CF_AMP0GN1 | F020_MASK_ADC0CF_AMP0GN0;
+ switch (gain) {
+ // 000 -> Gain = 1
+ // 001 -> Gain = 2
+ // 010 -> Gain = 4
+ // 011 -> Gain = 8
+ // 10x -> Gain = 16
+ // 11x -> Gain = 0.5
+ case DT9812_GAIN_0PT5:{
+ rmw->or_value = F020_MASK_ADC0CF_AMP0GN2
+ || F020_MASK_ADC0CF_AMP0GN1;
+ }
+ break;
+ case DT9812_GAIN_1:{
+ rmw->or_value = 0x00;
+ }
+ break;
+ case DT9812_GAIN_2:{
+ rmw->or_value = F020_MASK_ADC0CF_AMP0GN0;
+ }
+ break;
+ case DT9812_GAIN_4:{
+ rmw->or_value = F020_MASK_ADC0CF_AMP0GN1;
+ }
+ break;
+ case DT9812_GAIN_8:{
+ rmw->or_value = F020_MASK_ADC0CF_AMP0GN1
+ || F020_MASK_ADC0CF_AMP0GN0;
+ }
+ break;
+ case DT9812_GAIN_16:{
+ rmw->or_value = F020_MASK_ADC0CF_AMP0GN2;
+ }
+ break;
+ default:{
+ err("Illegal gain %d\n", gain);
+ }
+ }
+}
+
+static int dt9812_analog_in(slot_dt9812_t * slot,
+ int channel, u16 * value, dt9812_gain_t gain)
+{
+ int result = -ENODEV;
+
+ down(&slot->mutex);
+ if (slot->usb) {
+ dt9812_rmw_byte_t rmw[3];
+
+ // 1 select the gain
+ dt9812_configure_gain(slot->usb, &rmw[0], gain);
+
+ // 2 set the MUX to select the channel
+ dt9812_configure_mux(slot->usb, &rmw[1], channel);
+
+ // 3 start conversion
+ rmw[2].address = F020_SFR_ADC0CN;
+ rmw[2].and_mask = 0xff;
+ rmw[2].or_value =
+ F020_MASK_ADC0CN_AD0EN | F020_MASK_ADC0CN_AD0BUSY;
+
+ result = dt9812_rmw_multiple_registers(slot->usb, 3, rmw);
+ if (result == 0) {
+ // read the status and ADC
+ u8 reg[3] = { F020_SFR_ADC0CN, F020_SFR_ADC0H,
+ F020_SFR_ADC0L
+ };
+ u8 val[3];
+ result = dt9812_read_multiple_registers(slot->usb, 3,
+ reg, val);
+ if (result == 0) {
+ // An ADC conversion takes 16 SAR clocks cycles, i.e. about 9us.
+ // Therefore, between the instant that AD0BUSY was set via
+ // dt9812_rmw_multiple_registers and the read of AD0BUSY via
+ // dt9812_read_multiple_registers, the conversion
+ // should be complete since these two operations require two USB
+ // transactions each taking at least a millisecond to complete.
+ // However, lets make sure that conversion is finished.
+ if ((val[0] & (F020_MASK_ADC0CN_AD0INT |
+ F020_MASK_ADC0CN_AD0BUSY))
+ == F020_MASK_ADC0CN_AD0INT) {
+ switch (slot->usb->device) {
+ case DT9812_DEVID_DT9812_10:{
+ // For DT9812-10V the personality module set the encoding to 2's
+ // complement. Hence, convert it before returning it
+ *value = ((val[1] << 8)
+ | val[2]) +
+ 0x800;
+ }
+ break;
+ case DT9812_DEVID_DT9812_2PT5:{
+ *value = (val[1] << 8) |
+ val[2];
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ up(&slot->mutex);
+ return result;
+}
+
+static int dt9812_analog_out_shadow(slot_dt9812_t * slot, int channel,
+ u16 * value)
+{
+ int result = -ENODEV;
+
+ down(&slot->mutex);
+ if (slot->usb) {
+ *value = slot->usb->analog_out_shadow[channel];
+ result = 0;
+ }
+ up(&slot->mutex);
+
+ return result;
+}
+
+static int dt9812_analog_out(slot_dt9812_t * slot, int channel, u16 value)
+{
+ int result = -ENODEV;
+
+ down(&slot->mutex);
+ if (slot->usb) {
+ dt9812_rmw_byte_t rmw[3];
+
+ switch (channel) {
+ case 0:{
+ // 1. Set DAC mode
+ rmw[0].address = F020_SFR_DAC0CN;
+ rmw[0].and_mask = 0xff;
+ rmw[0].or_value = F020_MASK_DACxCN_DACxEN;
+
+ // 2 load low byte of DAC value first
+ rmw[1].address = F020_SFR_DAC0L;
+ rmw[1].and_mask = 0xff;
+ rmw[1].or_value = value & 0xff;
+
+ // 3 load high byte of DAC value next to latch the 12-bit value
+ rmw[2].address = F020_SFR_DAC0H;
+ rmw[2].and_mask = 0xff;
+ rmw[2].or_value = (value >> 8) & 0xf;
+ }
+ break;
+ case 1:{
+ // 1. Set DAC mode
+ rmw[0].address = F020_SFR_DAC1CN;
+ rmw[0].and_mask = 0xff;
+ rmw[0].or_value = F020_MASK_DACxCN_DACxEN;
+
+ // 2 load low byte of DAC value first
+ rmw[1].address = F020_SFR_DAC1L;
+ rmw[1].and_mask = 0xff;
+ rmw[1].or_value = value & 0xff;
+
+ // 3 load high byte of DAC value next to latch the 12-bit value
+ rmw[2].address = F020_SFR_DAC1H;
+ rmw[2].and_mask = 0xff;
+ rmw[2].or_value = (value >> 8) & 0xf;
+ }
+ break;
+ }
+ result = dt9812_rmw_multiple_registers(slot->usb, 3, rmw);
+ slot->usb->analog_out_shadow[channel] = value;
+ }
+ up(&slot->mutex);
+
+ return result;
+}
+
+/*
+ * USB framework functions
+ */
+
+static int dt9812_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ int retval = -ENOMEM;
+ usb_dt9812_t *dev = NULL;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int i;
+ u8 fw;
+
+ // allocate memory for our device state and initialize it
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ err("Out of memory");
+ goto error;
+ }
+ kref_init(&dev->kref);
+
+ dev->udev = usb_get_dev(interface_to_usbdev(interface));
+ dev->interface = interface;
+
+ // Check endpoints
+ iface_desc = interface->cur_altsetting;
+
+ if (iface_desc->desc.bNumEndpoints != 5) {
+ err("Wrong number of endpints.");
+ retval = -ENODEV;
+ goto error;
+ }
+
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ int direction = -1;
+ endpoint = &iface_desc->endpoint[i].desc;
+ switch (i) {
+ case 0:{
+ direction = USB_DIR_IN;
+ dev->message_pipe.addr =
+ endpoint->bEndpointAddress;
+ dev->message_pipe.size =
+ le16_to_cpu(endpoint->wMaxPacketSize);
+ }
+ break;
+ case 1:{
+ direction = USB_DIR_OUT;
+ dev->command_write.addr =
+ endpoint->bEndpointAddress;
+ dev->command_write.size =
+ le16_to_cpu(endpoint->wMaxPacketSize);
+ }
+ break;
+ case 2:{
+ direction = USB_DIR_IN;
+ dev->command_read.addr =
+ endpoint->bEndpointAddress;
+ dev->command_read.size =
+ le16_to_cpu(endpoint->wMaxPacketSize);
+ }
+ break;
+ case 3:{
+ direction = USB_DIR_OUT;
+ dev->write_stream.addr =
+ endpoint->bEndpointAddress;
+ dev->write_stream.size =
+ le16_to_cpu(endpoint->wMaxPacketSize);
+ }
+ break;
+ case 4:{
+ direction = USB_DIR_IN;
+ dev->read_stream.addr =
+ endpoint->bEndpointAddress;
+ dev->read_stream.size =
+ le16_to_cpu(endpoint->wMaxPacketSize);
+ }
+ break;
+ }
+ if ((endpoint->bEndpointAddress & USB_DIR_IN) != direction) {
+ err("Endpoint has wrong direction.");
+ retval = -ENODEV;
+ goto error;
+ }
+ }
+ if (dt9812_read_info(dev, 0, &fw, sizeof(fw)) != 0) {
+ // Seems like a configuration reset is necessary if driver
+ // is reloaded while device is attached
+ int i;
+
+ usb_reset_configuration(dev->udev);
+ for (i = 0; i < 10; i++) {
+ retval = dt9812_read_info(dev, 1, &fw, sizeof(fw));
+ if (retval == 0) {
+ printk("usb_reset_configuration succeded after %d iterations\n", i);
+ break;
+ }
+ }
+ }
+
+ if (dt9812_read_info(dev, 1, &dev->vendor, sizeof(dev->vendor)) != 0) {
+ err("Failed to read vendor.");
+ retval = -ENODEV;
+ goto error;
+ }
+ if (dt9812_read_info(dev, 3, &dev->product, sizeof(dev->product)) != 0) {
+ err("Failed to read product.");
+ retval = -ENODEV;
+ goto error;
+ }
+ if (dt9812_read_info(dev, 5, &dev->device, sizeof(dev->device)) != 0) {
+ err("Failed to read device.");
+ retval = -ENODEV;
+ goto error;
+ }
+ if (dt9812_read_info(dev, 7, &dev->serial, sizeof(dev->serial)) != 0) {
+ err("Failed to read serial.");
+ retval = -ENODEV;
+ goto error;
+ }
+
+ dev->vendor = le16_to_cpu(dev->vendor);
+ dev->product = le16_to_cpu(dev->product);
+ dev->device = le16_to_cpu(dev->device);
+ dev->serial = le32_to_cpu(dev->serial);
+ switch (dev->device) {
+ case DT9812_DEVID_DT9812_10:{
+ dev->analog_out_shadow[0] = 0x0800;
+ dev->analog_out_shadow[1] = 0x800;
+ }
+ break;
+ case DT9812_DEVID_DT9812_2PT5:{
+ dev->analog_out_shadow[0] = 0x0000;
+ dev->analog_out_shadow[1] = 0x0000;
+ }
+ break;
+ }
+ dev->digital_out_shadow = 0;
+
+ // save our data pointer in this interface device a
+ usb_set_intfdata(interface, dev);
+
+ // let the user know what node this device is now attached to
+ dev_info(&interface->dev, "USB DT9812 (%4.4x.%4.4x.%4.4x) #0x%8.8x\n",
+ dev->vendor, dev->product, dev->device, dev->serial);
+
+ down(&dt9812_mutex);
+ {
+ // Find a slot for the USB device
+ slot_dt9812_t *first = NULL;
+ slot_dt9812_t *best = NULL;
+
+ for (i = 0; i < DT9812_NUM_SLOTS; i++) {
+ if (!first && !dt9812[i].usb && dt9812[i].serial == 0) {
+ first = &dt9812[i];
+ }
+ if (!best && dt9812[i].serial == dev->serial) {
+ best = &dt9812[i];
+ }
+ }
+
+ if (!best) {
+ best = first;
+ }
+
+ if (best) {
+ down(&best->mutex);
+ best->usb = dev;
+ dev->slot = best;
+ up(&best->mutex);
+ }
+ }
+ up(&dt9812_mutex);
+
+ return 0;
+
+ error:
+ if (dev) {
+ kref_put(&dev->kref, dt9812_delete);
+ }
+ return retval;
+}
+
+static void dt9812_disconnect(struct usb_interface *interface)
+{
+ usb_dt9812_t *dev;
+ int minor = interface->minor;
+
+ down(&dt9812_mutex);
+ dev = usb_get_intfdata(interface);
+ if (dev->slot) {
+ down(&dev->slot->mutex);
+ dev->slot->usb = NULL;
+ up(&dev->slot->mutex);
+ dev->slot = NULL;
+ }
+ usb_set_intfdata(interface, NULL);
+ up(&dt9812_mutex);
+
+ /* queue final destruction */
+ kref_put(&dev->kref, dt9812_delete);
+
+ dev_info(&interface->dev, "USB Dt9812 #%d now disconnected\n", minor);
+}
+
+static struct usb_driver dt9812_usb_driver = {
+#ifdef COMEDI_HAVE_USB_DRIVER_OWNER
+ .owner = THIS_MODULE,
+#endif
+ .name = "dt9812",
+ .probe = dt9812_probe,
+ .disconnect = dt9812_disconnect,
+ .id_table = dt9812_table,
+};
+
+/*
+ * Comedi functions
+ */
+
+static void dt9812_comedi_open(comedi_device * dev)
+{
+ down(&devpriv->slot->mutex);
+ if (devpriv->slot->usb) {
+ // We have an attached device, fill in current range info
+ comedi_subdevice *s;
+
+ s = &dev->subdevices[0];
+ s->n_chan = 8;
+ s->maxdata = 1;
+
+ s = &dev->subdevices[1];
+ s->n_chan = 8;
+ s->maxdata = 1;
+
+ s = &dev->subdevices[2];
+ s->n_chan = 8;
+ switch (devpriv->slot->usb->device) {
+ case 0:{
+ s->maxdata = 4095;
+ s->range_table = &dt9812_10_ain_range;
+ }
+ break;
+ case 1:{
+ s->maxdata = 4095;
+ s->range_table = &dt9812_2pt5_ain_range;
+ }
+ break;
+ }
+
+ s = &dev->subdevices[3];
+ s->n_chan = 2;
+ switch (devpriv->slot->usb->device) {
+ case 0:{
+ s->maxdata = 4095;
+ s->range_table = &dt9812_10_aout_range;
+ }
+ break;
+ case 1:{
+ s->maxdata = 4095;
+ s->range_table = &dt9812_2pt5_aout_range;
+ }
+ break;
+ }
+ }
+ up(&devpriv->slot->mutex);
+}
+
+static int dt9812_di_rinsn(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ int n;
+ u8 bits = 0;
+
+ dt9812_digital_in(devpriv->slot, &bits);
+ for (n = 0; n < insn->n; n++) {
+ data[n] = ((1 << insn->chanspec) & bits) != 0;
+ }
+ return n;
+}
+
+static int dt9812_do_winsn(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ int n;
+ u8 bits = 0;
+
+ dt9812_digital_out_shadow(devpriv->slot, &bits);
+ for (n = 0; n < insn->n; n++) {
+ u8 mask = 1 << insn->chanspec;
+
+ bits &= ~mask;
+ if (data[n]) {
+ bits |= mask;
+ }
+ }
+ dt9812_digital_out(devpriv->slot, bits);
+ return n;
+}
+
+static int dt9812_ai_rinsn(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ int n;
+
+ for (n = 0; n < insn->n; n++) {
+ u16 value = 0;
+
+ dt9812_analog_in(devpriv->slot, insn->chanspec, &value,
+ DT9812_GAIN_1);
+ data[n] = value;
+ }
+ return n;
+}
+
+static int dt9812_ao_rinsn(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ int n;
+
+ for (n = 0; n < insn->n; n++) {
+ u16 value = 0;
+
+ dt9812_analog_out_shadow(devpriv->slot, insn->chanspec, &value);
+ data[n] = value;
+ }
+ return n;
+}
+
+static int dt9812_ao_winsn(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ int n;
+
+ for (n = 0; n < insn->n; n++) {
+ dt9812_analog_out(devpriv->slot, insn->chanspec, data[n]);
+ }
+ return n;
+}
+
+static int dt9812_attach(comedi_device * dev, comedi_devconfig * it)
+{
+ int i;
+ comedi_subdevice *s;
+
+ dev->board_name = "dt9812";
+
+ if (alloc_private(dev, sizeof(comedi_dt9812_t)) < 0) {
+ return -ENOMEM;
+ }
+ // Special open routine, since USB unit may be unattached at
+ // comedi_config time, hence range can not be determined
+ dev->open = dt9812_comedi_open;
+
+ devpriv->serial = it->options[0];
+
+ // Allocate subdevices
+ if (alloc_subdevices(dev, 4) < 0) {
+ return -ENOMEM;
+ }
+
+ /* digital input subdevice */
+ s = dev->subdevices + 0;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 0;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_read = &dt9812_di_rinsn;
+
+ /* digital output subdevice */
+ s = dev->subdevices + 1;
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITEABLE;
+ s->n_chan = 0;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_write = &dt9812_do_winsn;
+
+ /* analog input subdevice */
+ s = dev->subdevices + 2;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = 0;
+ s->maxdata = 1;
+ s->range_table = 0;
+ s->insn_read = &dt9812_ai_rinsn;
+
+ /* analog output subdevice */
+ s = dev->subdevices + 3;
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITEABLE;
+ s->n_chan = 0;
+ s->maxdata = 1;
+ s->range_table = 0;
+ s->insn_write = &dt9812_ao_winsn;
+ s->insn_read = &dt9812_ao_rinsn;
+
+ printk("comedi%d: successfully attached to dt9812.\n", dev->minor);
+
+ down(&dt9812_mutex);
+ // Find a slot for the comedi device
+ {
+ slot_dt9812_t *first = NULL;
+ slot_dt9812_t *best = NULL;
+ for (i = 0; i < DT9812_NUM_SLOTS; i++) {
+ if (!first && !dt9812[i].comedi) {
+ // First free slot from comedi side
+ first = &dt9812[i];
+ }
+ if (!best &&
+ dt9812[i].usb
+ && dt9812[i].usb->serial == devpriv->serial) {
+ // We have an attaced device with matching ID
+ best = &dt9812[i];
+ }
+ }
+ if (!best) {
+ best = first;
+ }
+ if (best) {
+ down(&best->mutex);
+ best->comedi = devpriv;
+ best->serial = devpriv->serial;
+ devpriv->slot = best;
+ up(&best->mutex);
+ }
+ }
+ up(&dt9812_mutex);
+
+ return 0;
+}
+
+static int dt9812_detach(comedi_device * dev)
+{
+
+ return 0;
+}
+
+static comedi_driver dt9812_comedi_driver = {
+ .module = THIS_MODULE,
+ .driver_name = "dt9812",
+ .attach = dt9812_attach,
+ .detach = dt9812_detach,
+};
+
+static int __init usb_dt9812_init(void)
+{
+ int result, i;
+
+ // Initialize all driver slots
+ for (i = 0; i < DT9812_NUM_SLOTS; i++) {
+ init_MUTEX(&dt9812[i].mutex);
+ dt9812[i].serial = 0;
+ dt9812[i].usb = NULL;
+ dt9812[i].comedi = NULL;
+ }
+ dt9812[12].serial = 0x0;
+
+ // register with the USB subsystem
+ result = usb_register(&dt9812_usb_driver);
+ if (result) {
+ err("usb_register failed. Error number %d", result);
+ }
+ // register with comedi
+ result = comedi_driver_register(&dt9812_comedi_driver);
+ if (result) {
+ usb_deregister(&dt9812_usb_driver);
+ err("comedi_driver_register failed. Error number %d", result);
+ }
+
+ return result;
+}
+
+static void __exit usb_dt9812_exit(void)
+{
+ // unregister with comedi
+ comedi_driver_unregister(&dt9812_comedi_driver);
+
+ /* deregister this driver with the USB subsystem */
+ usb_deregister(&dt9812_usb_driver);
+}
+
+module_init(usb_dt9812_init);
+module_exit(usb_dt9812_exit);
+
+MODULE_AUTHOR("Anders Blomdell <anders.blomdell@control.lth.se>");
+MODULE_DESCRIPTION("Comedi DT9812 driver");
+MODULE_LICENSE("GPL");
+
+#endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
diff --git a/drivers/staging/comedi/drivers/dt9812.h b/drivers/staging/comedi/drivers/dt9812.h
new file mode 100644
index 000000000000..180fd5a39575
--- /dev/null
+++ b/drivers/staging/comedi/drivers/dt9812.h
@@ -0,0 +1,176 @@
+#ifndef __DT9812_H__
+#define __DT9812_H__
+
+#define DT9812_DIAGS_BOARD_INFO_ADDR 0xFBFF
+#define DT9812_MAX_WRITE_CMD_PIPE_SIZE 32
+#define DT9812_MAX_READ_CMD_PIPE_SIZE 32
+
+/*
+ * See Silican Laboratories C8051F020/1/2/3 manual
+ */
+#define F020_SFR_P4 0x84
+#define F020_SFR_P1 0x90
+#define F020_SFR_P2 0xa0
+#define F020_SFR_P3 0xb0
+#define F020_SFR_AMX0CF 0xba
+#define F020_SFR_AMX0SL 0xbb
+#define F020_SFR_ADC0CF 0xbc
+#define F020_SFR_ADC0L 0xbe
+#define F020_SFR_ADC0H 0xbf
+#define F020_SFR_DAC0L 0xd2
+#define F020_SFR_DAC0H 0xd3
+#define F020_SFR_DAC0CN 0xd4
+#define F020_SFR_DAC1L 0xd5
+#define F020_SFR_DAC1H 0xd6
+#define F020_SFR_DAC1CN 0xd7
+#define F020_SFR_ADC0CN 0xe8
+
+#define F020_MASK_ADC0CF_AMP0GN0 0x01
+#define F020_MASK_ADC0CF_AMP0GN1 0x02
+#define F020_MASK_ADC0CF_AMP0GN2 0x04
+
+#define F020_MASK_ADC0CN_AD0EN 0x80
+#define F020_MASK_ADC0CN_AD0INT 0x20
+#define F020_MASK_ADC0CN_AD0BUSY 0x10
+
+#define F020_MASK_DACxCN_DACxEN 0x80
+
+typedef enum { // A/D D/A DI DO CT
+ DT9812_DEVID_DT9812_10, // 8 2 8 8 1 +/- 10V
+ DT9812_DEVID_DT9812_2PT5, // 8 2 8 8 1 0-2.44V
+#if 0
+ DT9812_DEVID_DT9813, // 16 2 4 4 1 +/- 10V
+ DT9812_DEVID_DT9814 // 24 2 0 0 1 +/- 10V
+#endif
+} dt9812_devid_t;
+
+typedef enum {
+ DT9812_GAIN_0PT25 = 1,
+ DT9812_GAIN_0PT5 = 2,
+ DT9812_GAIN_1 = 4,
+ DT9812_GAIN_2 = 8,
+ DT9812_GAIN_4 = 16,
+ DT9812_GAIN_8 = 32,
+ DT9812_GAIN_16 = 64,
+} dt9812_gain_t;
+
+typedef enum {
+ DT9812_LEAST_USB_FIRMWARE_CMD_CODE = 0,
+ DT9812_W_FLASH_DATA = 0, // Write Flash memory
+ DT9812_R_FLASH_DATA = 1, // Read Flash memory (misc config info)
+
+ // Register read/write commands for processor
+ DT9812_R_SINGLE_BYTE_REG = 2, // Read a single byte of USB memory
+ DT9812_W_SINGLE_BYTE_REG = 3, // Write a single byte of USB memory
+ DT9812_R_MULTI_BYTE_REG = 4, // Multiple Reads of USB memory
+ DT9812_W_MULTI_BYTE_REG = 5, // Multiple Writes of USB memory
+ DT9812_RMW_SINGLE_BYTE_REG = 6, // Read, (AND) with mask, OR value,
+ // then write (single)
+ DT9812_RMW_MULTI_BYTE_REG = 7, // Read, (AND) with mask, OR value,
+ // then write (multiple)
+
+ // Register read/write commands for SMBus
+ DT9812_R_SINGLE_BYTE_SMBUS = 8, // Read a single byte of SMBus
+ DT9812_W_SINGLE_BYTE_SMBUS = 9, // Write a single byte of SMBus
+ DT9812_R_MULTI_BYTE_SMBUS = 10, // Multiple Reads of SMBus
+ DT9812_W_MULTI_BYTE_SMBUS = 11, // Multiple Writes of SMBus
+
+ // Register read/write commands for a device
+ DT9812_R_SINGLE_BYTE_DEV = 12, // Read a single byte of a device
+ DT9812_W_SINGLE_BYTE_DEV = 13, // Write a single byte of a device
+ DT9812_R_MULTI_BYTE_DEV = 14, // Multiple Reads of a device
+ DT9812_W_MULTI_BYTE_DEV = 15, // Multiple Writes of a device
+
+ DT9812_W_DAC_THRESHOLD = 16, // Not sure if we'll need this
+
+ DT9812_W_INT_ON_CHANGE_MASK = 17, // Set interrupt on change mask
+
+ DT9812_W_CGL = 18, // Write (or Clear) the CGL for the ADC
+ DT9812_R_MULTI_BYTE_USBMEM = 19, // Multiple Reads of USB memory
+ DT9812_W_MULTI_BYTE_USBMEM = 20, // Multiple Writes to USB memory
+
+ DT9812_START_SUBSYSTEM = 21, // Issue a start command to a
+ // given subsystem
+ DT9812_STOP_SUBSYSTEM = 22, // Issue a stop command to a
+ // given subsystem
+
+ DT9812_CALIBRATE_POT = 23, //calibrate the board using CAL_POT_CMD
+ DT9812_W_DAC_FIFO_SIZE = 24, // set the DAC FIFO size
+ DT9812_W_CGL_DAC = 25, // Write (or Clear) the CGL for the DAC
+ DT9812_R_SINGLE_VALUE_CMD = 26, // Read a single value from a subsystem
+ DT9812_W_SINGLE_VALUE_CMD = 27, // Write a single value to a subsystem
+ DT9812_MAX_USB_FIRMWARE_CMD_CODE // Valid DT9812_USB_FIRMWARE_CMD_CODE's
+ // will be less than this number
+} dt9812_usb_firmware_cmd_t;
+
+typedef struct {
+ u16 numbytes;
+ u16 address;
+} dt9812_flash_data_t;
+
+#define DT9812_MAX_NUM_MULTI_BYTE_RDS \
+ ((DT9812_MAX_WRITE_CMD_PIPE_SIZE - 4 - 1) / sizeof(u8))
+
+typedef struct {
+ u8 count;
+ u8 address[DT9812_MAX_NUM_MULTI_BYTE_RDS];
+} dt9812_read_multi_t;
+
+typedef struct {
+ u8 address;
+ u8 value;
+} dt9812_write_byte_t;
+
+#define DT9812_MAX_NUM_MULTI_BYTE_WRTS \
+ ((DT9812_MAX_WRITE_CMD_PIPE_SIZE - 4 - 1) / sizeof(dt9812_write_byte_t))
+
+typedef struct {
+ u8 count;
+ dt9812_write_byte_t write[DT9812_MAX_NUM_MULTI_BYTE_WRTS];
+} dt9812_write_multi_t;
+
+typedef struct {
+ u8 address;
+ u8 and_mask;
+ u8 or_value;
+} dt9812_rmw_byte_t;
+
+#define DT9812_MAX_NUM_MULTI_BYTE_RMWS \
+ ((DT9812_MAX_WRITE_CMD_PIPE_SIZE - 4 - 1) / sizeof(dt9812_rmw_byte_t))
+
+typedef struct {
+ u8 count;
+ dt9812_rmw_byte_t rmw[DT9812_MAX_NUM_MULTI_BYTE_RMWS];
+} dt9812_rmw_multi_t;
+
+typedef struct dt9812_usb_cmd {
+
+ u32 cmd;
+ union {
+ dt9812_flash_data_t flash_data_info;
+ dt9812_read_multi_t read_multi_info;
+ dt9812_write_multi_t write_multi_info;
+ dt9812_rmw_multi_t rmw_multi_info;
+ } u;
+#if 0
+ WRITE_BYTE_INFO WriteByteInfo;
+ READ_BYTE_INFO ReadByteInfo;
+ WRITE_MULTI_INFO WriteMultiInfo;
+ READ_MULTI_INFO ReadMultiInfo;
+ RMW_BYTE_INFO RMWByteInfo;
+ RMW_MULTI_INFO RMWMultiInfo;
+ DAC_THRESHOLD_INFO DacThresholdInfo;
+ INT_ON_CHANGE_MASK_INFO IntOnChangeMaskInfo;
+ CGL_INFO CglInfo;
+ SUBSYSTEM_INFO SubsystemInfo;
+ CAL_POT_CMD CalPotCmd;
+ WRITE_DEV_BYTE_INFO WriteDevByteInfo;
+ READ_DEV_BYTE_INFO ReadDevByteInfo;
+ WRITE_DEV_MULTI_INFO WriteDevMultiInfo;
+ READ_DEV_MULTI_INFO ReadDevMultiInfo;
+ READ_SINGLE_VALUE_INFO ReadSingleValueInfo;
+ WRITE_SINGLE_VALUE_INFO WriteSingleValueInfo;
+#endif
+} dt9812_usb_cmd_t;
+
+#endif
diff --git a/drivers/staging/comedi/drivers/mite.c b/drivers/staging/comedi/drivers/mite.c
new file mode 100644
index 000000000000..9cc527424d04
--- /dev/null
+++ b/drivers/staging/comedi/drivers/mite.c
@@ -0,0 +1,809 @@
+/*
+ comedi/drivers/mite.c
+ Hardware driver for NI Mite PCI interface chip
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-2002 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/*
+ The PCI-MIO E series driver was originally written by
+ Tomasz Motylewski <...>, and ported to comedi by ds.
+
+ References for specifications:
+
+ 321747b.pdf Register Level Programmer Manual (obsolete)
+ 321747c.pdf Register Level Programmer Manual (new)
+ DAQ-STC reference manual
+
+ Other possibly relevant info:
+
+ 320517c.pdf User manual (obsolete)
+ 320517f.pdf User manual (new)
+ 320889a.pdf delete
+ 320906c.pdf maximum signal ratings
+ 321066a.pdf about 16x
+ 321791a.pdf discontinuation of at-mio-16e-10 rev. c
+ 321808a.pdf about at-mio-16e-10 rev P
+ 321837a.pdf discontinuation of at-mio-16de-10 rev d
+ 321838a.pdf about at-mio-16de-10 rev N
+
+ ISSUES:
+
+*/
+
+//#define USE_KMALLOC
+
+#include "mite.h"
+
+#include "comedi_fc.h"
+#include "comedi_pci.h"
+#include "../comedidev.h"
+
+#include <asm/system.h>
+
+#define PCI_MITE_SIZE 4096
+#define PCI_DAQ_SIZE 4096
+#define PCI_DAQ_SIZE_660X 8192
+
+MODULE_LICENSE("GPL");
+
+struct mite_struct *mite_devices = NULL;
+
+#define TOP_OF_PAGE(x) ((x)|(~(PAGE_MASK)))
+
+void mite_init(void)
+{
+ struct pci_dev *pcidev;
+ struct mite_struct *mite;
+
+ for (pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
+ pcidev != NULL;
+ pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
+ if (pcidev->vendor == PCI_VENDOR_ID_NATINST) {
+ unsigned i;
+
+ mite = kzalloc(sizeof(*mite), GFP_KERNEL);
+ if (!mite) {
+ printk("mite: allocation failed\n");
+ pci_dev_put(pcidev);
+ return;
+ }
+ spin_lock_init(&mite->lock);
+ mite->pcidev = pci_dev_get(pcidev);
+ for (i = 0; i < MAX_MITE_DMA_CHANNELS; ++i) {
+ mite->channels[i].mite = mite;
+ mite->channels[i].channel = i;
+ mite->channels[i].done = 1;
+ }
+ mite->next = mite_devices;
+ mite_devices = mite;
+ }
+ }
+}
+
+static void dump_chip_signature(u32 csigr_bits)
+{
+ printk("mite: version = %i, type = %i, mite mode = %i, interface mode = %i\n", mite_csigr_version(csigr_bits), mite_csigr_type(csigr_bits), mite_csigr_mmode(csigr_bits), mite_csigr_imode(csigr_bits));
+ printk("mite: num channels = %i, write post fifo depth = %i, wins = %i, iowins = %i\n", mite_csigr_dmac(csigr_bits), mite_csigr_wpdep(csigr_bits), mite_csigr_wins(csigr_bits), mite_csigr_iowins(csigr_bits));
+}
+
+unsigned mite_fifo_size(struct mite_struct * mite, unsigned channel)
+{
+ unsigned fcr_bits = readl(mite->mite_io_addr +
+ MITE_FCR(channel));
+ unsigned empty_count = (fcr_bits >> 16) & 0xff;
+ unsigned full_count = fcr_bits & 0xff;
+ return empty_count + full_count;
+}
+
+int mite_setup2(struct mite_struct *mite, unsigned use_iodwbsr_1)
+{
+ unsigned long length;
+ resource_size_t addr;
+ int i;
+ u32 csigr_bits;
+ unsigned unknown_dma_burst_bits;
+
+ if (comedi_pci_enable(mite->pcidev, "mite")) {
+ printk("error enabling mite and requesting io regions\n");
+ return -EIO;
+ }
+ pci_set_master(mite->pcidev);
+
+ addr = pci_resource_start(mite->pcidev, 0);
+ mite->mite_phys_addr = addr;
+ mite->mite_io_addr = ioremap(addr, PCI_MITE_SIZE);
+ if (!mite->mite_io_addr) {
+ printk("failed to remap mite io memory address\n");
+ return -ENOMEM;
+ }
+ printk("MITE:0x%08llx mapped to %p ",
+ (unsigned long long)mite->mite_phys_addr, mite->mite_io_addr);
+
+ addr = pci_resource_start(mite->pcidev, 1);
+ mite->daq_phys_addr = addr;
+ length = pci_resource_len(mite->pcidev, 1);
+ // In case of a 660x board, DAQ size is 8k instead of 4k (see as shown by lspci output)
+ mite->daq_io_addr = ioremap(mite->daq_phys_addr, length);
+ if (!mite->daq_io_addr) {
+ printk("failed to remap daq io memory address\n");
+ return -ENOMEM;
+ }
+ printk("DAQ:0x%08llx mapped to %p\n",
+ (unsigned long long)mite->daq_phys_addr, mite->daq_io_addr);
+
+ if (use_iodwbsr_1) {
+ writel(0, mite->mite_io_addr + MITE_IODWBSR);
+ printk("mite: using I/O Window Base Size register 1\n");
+ writel(mite->
+ daq_phys_addr | WENAB |
+ MITE_IODWBSR_1_WSIZE_bits(length),
+ mite->mite_io_addr + MITE_IODWBSR_1);
+ writel(0, mite->mite_io_addr + MITE_IODWCR_1);
+ } else {
+ writel(mite->daq_phys_addr | WENAB,
+ mite->mite_io_addr + MITE_IODWBSR);
+ }
+ /* make sure dma bursts work. I got this from running a bus analyzer
+ on a pxi-6281 and a pxi-6713. 6713 powered up with register value
+ of 0x61f and bursts worked. 6281 powered up with register value of
+ 0x1f and bursts didn't work. The NI windows driver reads the register,
+ then does a bitwise-or of 0x600 with it and writes it back.
+ */
+ unknown_dma_burst_bits =
+ readl(mite->mite_io_addr + MITE_UNKNOWN_DMA_BURST_REG);
+ unknown_dma_burst_bits |= UNKNOWN_DMA_BURST_ENABLE_BITS;
+ writel(unknown_dma_burst_bits,
+ mite->mite_io_addr + MITE_UNKNOWN_DMA_BURST_REG);
+
+ csigr_bits = readl(mite->mite_io_addr + MITE_CSIGR);
+ mite->num_channels = mite_csigr_dmac(csigr_bits);
+ if (mite->num_channels > MAX_MITE_DMA_CHANNELS) {
+ printk("mite: bug? chip claims to have %i dma channels. Setting to %i.\n", mite->num_channels, MAX_MITE_DMA_CHANNELS);
+ mite->num_channels = MAX_MITE_DMA_CHANNELS;
+ }
+ dump_chip_signature(csigr_bits);
+ for (i = 0; i < mite->num_channels; i++) {
+ writel(CHOR_DMARESET, mite->mite_io_addr + MITE_CHOR(i));
+ /* disable interrupts */
+ writel(CHCR_CLR_DMA_IE | CHCR_CLR_LINKP_IE | CHCR_CLR_SAR_IE |
+ CHCR_CLR_DONE_IE | CHCR_CLR_MRDY_IE | CHCR_CLR_DRDY_IE |
+ CHCR_CLR_LC_IE | CHCR_CLR_CONT_RB_IE,
+ mite->mite_io_addr + MITE_CHCR(i));
+ }
+ mite->fifo_size = mite_fifo_size(mite, 0);
+ printk("mite: fifo size is %i.\n", mite->fifo_size);
+ mite->used = 1;
+
+ return 0;
+}
+
+int mite_setup(struct mite_struct *mite)
+{
+ return mite_setup2(mite, 0);
+}
+
+void mite_cleanup(void)
+{
+ struct mite_struct *mite, *next;
+
+ for (mite = mite_devices; mite; mite = next) {
+ pci_dev_put(mite->pcidev);
+ next = mite->next;
+ kfree(mite);
+ }
+}
+
+void mite_unsetup(struct mite_struct *mite)
+{
+ //unsigned long offset, start, length;
+
+ if (!mite)
+ return;
+
+ if (mite->mite_io_addr) {
+ iounmap(mite->mite_io_addr);
+ mite->mite_io_addr = NULL;
+ }
+ if (mite->daq_io_addr) {
+ iounmap(mite->daq_io_addr);
+ mite->daq_io_addr = NULL;
+ }
+ if (mite->mite_phys_addr) {
+ comedi_pci_disable(mite->pcidev);
+ mite->mite_phys_addr = 0;
+ }
+
+ mite->used = 0;
+}
+
+void mite_list_devices(void)
+{
+ struct mite_struct *mite, *next;
+
+ printk("Available NI device IDs:");
+ if (mite_devices)
+ for (mite = mite_devices; mite; mite = next) {
+ next = mite->next;
+ printk(" 0x%04x", mite_device_id(mite));
+ if (mite->used)
+ printk("(used)");
+ }
+ printk("\n");
+
+}
+
+struct mite_channel *mite_request_channel_in_range(struct mite_struct *mite,
+ struct mite_dma_descriptor_ring *ring, unsigned min_channel,
+ unsigned max_channel)
+{
+ int i;
+ unsigned long flags;
+ struct mite_channel *channel = NULL;
+
+ // spin lock so mite_release_channel can be called safely from interrupts
+ comedi_spin_lock_irqsave(&mite->lock, flags);
+ for (i = min_channel; i <= max_channel; ++i) {
+ if (mite->channel_allocated[i] == 0) {
+ mite->channel_allocated[i] = 1;
+ channel = &mite->channels[i];
+ channel->ring = ring;
+ break;
+ }
+ }
+ comedi_spin_unlock_irqrestore(&mite->lock, flags);
+ return channel;
+}
+
+void mite_release_channel(struct mite_channel *mite_chan)
+{
+ struct mite_struct *mite = mite_chan->mite;
+ unsigned long flags;
+
+ // spin lock to prevent races with mite_request_channel
+ comedi_spin_lock_irqsave(&mite->lock, flags);
+ if (mite->channel_allocated[mite_chan->channel]) {
+ mite_dma_disarm(mite_chan);
+ mite_dma_reset(mite_chan);
+/* disable all channel's interrupts (do it after disarm/reset so
+MITE_CHCR reg isn't changed while dma is still active!) */
+ writel(CHCR_CLR_DMA_IE | CHCR_CLR_LINKP_IE |
+ CHCR_CLR_SAR_IE | CHCR_CLR_DONE_IE |
+ CHCR_CLR_MRDY_IE | CHCR_CLR_DRDY_IE |
+ CHCR_CLR_LC_IE | CHCR_CLR_CONT_RB_IE,
+ mite->mite_io_addr + MITE_CHCR(mite_chan->channel));
+ mite->channel_allocated[mite_chan->channel] = 0;
+ mite_chan->ring = NULL;
+ mmiowb();
+ }
+ comedi_spin_unlock_irqrestore(&mite->lock, flags);
+}
+
+void mite_dma_arm(struct mite_channel *mite_chan)
+{
+ struct mite_struct *mite = mite_chan->mite;
+ int chor;
+ unsigned long flags;
+
+ MDPRINTK("mite_dma_arm ch%i\n", channel);
+ /* memory barrier is intended to insure any twiddling with the buffer
+ is done before writing to the mite to arm dma transfer */
+ smp_mb();
+ /* arm */
+ chor = CHOR_START;
+ comedi_spin_lock_irqsave(&mite->lock, flags);
+ mite_chan->done = 0;
+ writel(chor, mite->mite_io_addr + MITE_CHOR(mite_chan->channel));
+ mmiowb();
+ comedi_spin_unlock_irqrestore(&mite->lock, flags);
+// mite_dma_tcr(mite, channel);
+}
+
+/**************************************/
+
+int mite_buf_change(struct mite_dma_descriptor_ring *ring, comedi_async * async)
+{
+ unsigned int n_links;
+ int i;
+
+ if (ring->descriptors) {
+ dma_free_coherent(ring->hw_dev,
+ ring->n_links * sizeof(struct mite_dma_descriptor),
+ ring->descriptors, ring->descriptors_dma_addr);
+ }
+ ring->descriptors = NULL;
+ ring->descriptors_dma_addr = 0;
+ ring->n_links = 0;
+
+ if (async->prealloc_bufsz == 0) {
+ return 0;
+ }
+ n_links = async->prealloc_bufsz >> PAGE_SHIFT;
+
+ MDPRINTK("ring->hw_dev=%p, n_links=0x%04x\n", ring->hw_dev, n_links);
+
+ ring->descriptors =
+ dma_alloc_coherent(ring->hw_dev,
+ n_links * sizeof(struct mite_dma_descriptor),
+ &ring->descriptors_dma_addr, GFP_KERNEL);
+ if (!ring->descriptors) {
+ printk("mite: ring buffer allocation failed\n");
+ return -ENOMEM;
+ }
+ ring->n_links = n_links;
+
+ for (i = 0; i < n_links; i++) {
+ ring->descriptors[i].count = cpu_to_le32(PAGE_SIZE);
+ ring->descriptors[i].addr =
+ cpu_to_le32(async->buf_page_list[i].dma_addr);
+ ring->descriptors[i].next =
+ cpu_to_le32(ring->descriptors_dma_addr + (i +
+ 1) * sizeof(struct mite_dma_descriptor));
+ }
+ ring->descriptors[n_links - 1].next =
+ cpu_to_le32(ring->descriptors_dma_addr);
+ /* barrier is meant to insure that all the writes to the dma descriptors
+ have completed before the dma controller is commanded to read them */
+ smp_wmb();
+ return 0;
+}
+
+void mite_prep_dma(struct mite_channel *mite_chan,
+ unsigned int num_device_bits, unsigned int num_memory_bits)
+{
+ unsigned int chor, chcr, mcr, dcr, lkcr;
+ struct mite_struct *mite = mite_chan->mite;
+
+ MDPRINTK("mite_prep_dma ch%i\n", mite_chan->channel);
+
+ /* reset DMA and FIFO */
+ chor = CHOR_DMARESET | CHOR_FRESET;
+ writel(chor, mite->mite_io_addr + MITE_CHOR(mite_chan->channel));
+
+ /* short link chaining mode */
+ chcr = CHCR_SET_DMA_IE | CHCR_LINKSHORT | CHCR_SET_DONE_IE |
+ CHCR_BURSTEN;
+ /*
+ * Link Complete Interrupt: interrupt every time a link
+ * in MITE_RING is completed. This can generate a lot of
+ * extra interrupts, but right now we update the values
+ * of buf_int_ptr and buf_int_count at each interrupt. A
+ * better method is to poll the MITE before each user
+ * "read()" to calculate the number of bytes available.
+ */
+ chcr |= CHCR_SET_LC_IE;
+ if (num_memory_bits == 32 && num_device_bits == 16) {
+ /* Doing a combined 32 and 16 bit byteswap gets the 16 bit samples into the fifo in the right order.
+ Tested doing 32 bit memory to 16 bit device transfers to the analog out of a pxi-6281,
+ which has mite version = 1, type = 4. This also works for dma reads from the counters
+ on e-series boards. */
+ chcr |= CHCR_BYTE_SWAP_DEVICE | CHCR_BYTE_SWAP_MEMORY;
+ }
+ if (mite_chan->dir == COMEDI_INPUT) {
+ chcr |= CHCR_DEV_TO_MEM;
+ }
+ writel(chcr, mite->mite_io_addr + MITE_CHCR(mite_chan->channel));
+
+ /* to/from memory */
+ mcr = CR_RL(64) | CR_ASEQUP;
+ switch (num_memory_bits) {
+ case 8:
+ mcr |= CR_PSIZE8;
+ break;
+ case 16:
+ mcr |= CR_PSIZE16;
+ break;
+ case 32:
+ mcr |= CR_PSIZE32;
+ break;
+ default:
+ rt_printk
+ ("mite: bug! invalid mem bit width for dma transfer\n");
+ break;
+ }
+ writel(mcr, mite->mite_io_addr + MITE_MCR(mite_chan->channel));
+
+ /* from/to device */
+ dcr = CR_RL(64) | CR_ASEQUP;
+ dcr |= CR_PORTIO | CR_AMDEVICE | CR_REQSDRQ(mite_chan->channel);
+ switch (num_device_bits) {
+ case 8:
+ dcr |= CR_PSIZE8;
+ break;
+ case 16:
+ dcr |= CR_PSIZE16;
+ break;
+ case 32:
+ dcr |= CR_PSIZE32;
+ break;
+ default:
+ rt_printk
+ ("mite: bug! invalid dev bit width for dma transfer\n");
+ break;
+ }
+ writel(dcr, mite->mite_io_addr + MITE_DCR(mite_chan->channel));
+
+ /* reset the DAR */
+ writel(0, mite->mite_io_addr + MITE_DAR(mite_chan->channel));
+
+ /* the link is 32bits */
+ lkcr = CR_RL(64) | CR_ASEQUP | CR_PSIZE32;
+ writel(lkcr, mite->mite_io_addr + MITE_LKCR(mite_chan->channel));
+
+ /* starting address for link chaining */
+ writel(mite_chan->ring->descriptors_dma_addr,
+ mite->mite_io_addr + MITE_LKAR(mite_chan->channel));
+
+ MDPRINTK("exit mite_prep_dma\n");
+}
+
+u32 mite_device_bytes_transferred(struct mite_channel *mite_chan)
+{
+ struct mite_struct *mite = mite_chan->mite;
+ return readl(mite->mite_io_addr + MITE_DAR(mite_chan->channel));
+}
+
+u32 mite_bytes_in_transit(struct mite_channel * mite_chan)
+{
+ struct mite_struct *mite = mite_chan->mite;
+ return readl(mite->mite_io_addr +
+ MITE_FCR(mite_chan->channel)) & 0x000000FF;
+}
+
+// returns lower bound for number of bytes transferred from device to memory
+u32 mite_bytes_written_to_memory_lb(struct mite_channel * mite_chan)
+{
+ u32 device_byte_count;
+
+ device_byte_count = mite_device_bytes_transferred(mite_chan);
+ return device_byte_count - mite_bytes_in_transit(mite_chan);
+}
+
+// returns upper bound for number of bytes transferred from device to memory
+u32 mite_bytes_written_to_memory_ub(struct mite_channel * mite_chan)
+{
+ u32 in_transit_count;
+
+ in_transit_count = mite_bytes_in_transit(mite_chan);
+ return mite_device_bytes_transferred(mite_chan) - in_transit_count;
+}
+
+// returns lower bound for number of bytes read from memory for transfer to device
+u32 mite_bytes_read_from_memory_lb(struct mite_channel * mite_chan)
+{
+ u32 device_byte_count;
+
+ device_byte_count = mite_device_bytes_transferred(mite_chan);
+ return device_byte_count + mite_bytes_in_transit(mite_chan);
+}
+
+// returns upper bound for number of bytes read from memory for transfer to device
+u32 mite_bytes_read_from_memory_ub(struct mite_channel * mite_chan)
+{
+ u32 in_transit_count;
+
+ in_transit_count = mite_bytes_in_transit(mite_chan);
+ return mite_device_bytes_transferred(mite_chan) + in_transit_count;
+}
+
+unsigned mite_dma_tcr(struct mite_channel *mite_chan)
+{
+ struct mite_struct *mite = mite_chan->mite;
+ int tcr;
+ int lkar;
+
+ lkar = readl(mite->mite_io_addr + MITE_LKAR(mite_chan->channel));
+ tcr = readl(mite->mite_io_addr + MITE_TCR(mite_chan->channel));
+ MDPRINTK("mite_dma_tcr ch%i, lkar=0x%08x tcr=%d\n", mite_chan->channel,
+ lkar, tcr);
+
+ return tcr;
+}
+
+void mite_dma_disarm(struct mite_channel *mite_chan)
+{
+ struct mite_struct *mite = mite_chan->mite;
+ unsigned chor;
+
+ /* disarm */
+ chor = CHOR_ABORT;
+ writel(chor, mite->mite_io_addr + MITE_CHOR(mite_chan->channel));
+}
+
+int mite_sync_input_dma(struct mite_channel *mite_chan, comedi_async * async)
+{
+ int count;
+ unsigned int nbytes, old_alloc_count;
+ const unsigned bytes_per_scan = cfc_bytes_per_scan(async->subdevice);
+
+ old_alloc_count = async->buf_write_alloc_count;
+ // write alloc as much as we can
+ comedi_buf_write_alloc(async, async->prealloc_bufsz);
+
+ nbytes = mite_bytes_written_to_memory_lb(mite_chan);
+ if ((int)(mite_bytes_written_to_memory_ub(mite_chan) -
+ old_alloc_count) > 0) {
+ rt_printk("mite: DMA overwrite of free area\n");
+ async->events |= COMEDI_CB_OVERFLOW;
+ return -1;
+ }
+
+ count = nbytes - async->buf_write_count;
+ /* it's possible count will be negative due to
+ * conservative value returned by mite_bytes_written_to_memory_lb */
+ if (count <= 0) {
+ return 0;
+ }
+ comedi_buf_write_free(async, count);
+
+ async->scan_progress += count;
+ if (async->scan_progress >= bytes_per_scan) {
+ async->scan_progress %= bytes_per_scan;
+ async->events |= COMEDI_CB_EOS;
+ }
+ async->events |= COMEDI_CB_BLOCK;
+ return 0;
+}
+
+int mite_sync_output_dma(struct mite_channel *mite_chan, comedi_async * async)
+{
+ int count;
+ u32 nbytes_ub, nbytes_lb;
+ unsigned int old_alloc_count;
+ u32 stop_count =
+ async->cmd.stop_arg * cfc_bytes_per_scan(async->subdevice);
+
+ old_alloc_count = async->buf_read_alloc_count;
+ // read alloc as much as we can
+ comedi_buf_read_alloc(async, async->prealloc_bufsz);
+ nbytes_lb = mite_bytes_read_from_memory_lb(mite_chan);
+ if (async->cmd.stop_src == TRIG_COUNT &&
+ (int)(nbytes_lb - stop_count) > 0)
+ nbytes_lb = stop_count;
+ nbytes_ub = mite_bytes_read_from_memory_ub(mite_chan);
+ if (async->cmd.stop_src == TRIG_COUNT &&
+ (int)(nbytes_ub - stop_count) > 0)
+ nbytes_ub = stop_count;
+ if ((int)(nbytes_ub - old_alloc_count) > 0) {
+ rt_printk("mite: DMA underrun\n");
+ async->events |= COMEDI_CB_OVERFLOW;
+ return -1;
+ }
+ count = nbytes_lb - async->buf_read_count;
+ if (count <= 0) {
+ return 0;
+ }
+ if (count) {
+ comedi_buf_read_free(async, count);
+ async->events |= COMEDI_CB_BLOCK;
+ }
+ return 0;
+}
+
+unsigned mite_get_status(struct mite_channel *mite_chan)
+{
+ struct mite_struct *mite = mite_chan->mite;
+ unsigned status;
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&mite->lock, flags);
+ status = readl(mite->mite_io_addr + MITE_CHSR(mite_chan->channel));
+ if (status & CHSR_DONE) {
+ mite_chan->done = 1;
+ writel(CHOR_CLRDONE,
+ mite->mite_io_addr + MITE_CHOR(mite_chan->channel));
+ }
+ mmiowb();
+ comedi_spin_unlock_irqrestore(&mite->lock, flags);
+ return status;
+}
+
+int mite_done(struct mite_channel *mite_chan)
+{
+ struct mite_struct *mite = mite_chan->mite;
+ unsigned long flags;
+ int done;
+
+ mite_get_status(mite_chan);
+ comedi_spin_lock_irqsave(&mite->lock, flags);
+ done = mite_chan->done;
+ comedi_spin_unlock_irqrestore(&mite->lock, flags);
+ return done;
+}
+
+#ifdef DEBUG_MITE
+
+static void mite_decode(char **bit_str, unsigned int bits);
+
+/* names of bits in mite registers */
+
+static const char *const mite_CHOR_strings[] = {
+ "start", "cont", "stop", "abort",
+ "freset", "clrlc", "clrrb", "clrdone",
+ "clr_lpause", "set_lpause", "clr_send_tc",
+ "set_send_tc", "12", "13", "14",
+ "15", "16", "17", "18",
+ "19", "20", "21", "22",
+ "23", "24", "25", "26",
+ "27", "28", "29", "30",
+ "dmareset",
+};
+
+static const char *const mite_CHCR_strings[] = {
+ "continue", "ringbuff", "2", "3",
+ "4", "5", "6", "7",
+ "8", "9", "10", "11",
+ "12", "13", "bursten", "fifodis",
+ "clr_cont_rb_ie", "set_cont_rb_ie", "clr_lc_ie", "set_lc_ie",
+ "clr_drdy_ie", "set_drdy_ie", "clr_mrdy_ie", "set_mrdy_ie",
+ "clr_done_ie", "set_done_ie", "clr_sar_ie", "set_sar_ie",
+ "clr_linkp_ie", "set_linkp_ie", "clr_dma_ie", "set_dma_ie",
+};
+
+static const char *const mite_MCR_strings[] = {
+ "amdevice", "1", "2", "3",
+ "4", "5", "portio", "portvxi",
+ "psizebyte", "psizehalf (byte & half = word)", "aseqxp1", "11",
+ "12", "13", "blocken", "berhand",
+ "reqsintlim/reqs0", "reqs1", "reqs2", "rd32",
+ "rd512", "rl1", "rl2", "rl8",
+ "24", "25", "26", "27",
+ "28", "29", "30", "stopen",
+};
+
+static const char *const mite_DCR_strings[] = {
+ "amdevice", "1", "2", "3",
+ "4", "5", "portio", "portvxi",
+ "psizebyte", "psizehalf (byte & half = word)", "aseqxp1", "aseqxp2",
+ "aseqxp8", "13", "blocken", "berhand",
+ "reqsintlim", "reqs1", "reqs2", "rd32",
+ "rd512", "rl1", "rl2", "rl8",
+ "23", "24", "25", "27",
+ "28", "wsdevc", "wsdevs", "rwdevpack",
+};
+
+static const char *const mite_LKCR_strings[] = {
+ "amdevice", "1", "2", "3",
+ "4", "5", "portio", "portvxi",
+ "psizebyte", "psizehalf (byte & half = word)", "asequp", "aseqdown",
+ "12", "13", "14", "berhand",
+ "16", "17", "18", "rd32",
+ "rd512", "rl1", "rl2", "rl8",
+ "24", "25", "26", "27",
+ "28", "29", "30", "chngend",
+};
+
+static const char *const mite_CHSR_strings[] = {
+ "d.err0", "d.err1", "m.err0", "m.err1",
+ "l.err0", "l.err1", "drq0", "drq1",
+ "end", "xferr", "operr0", "operr1",
+ "stops", "habort", "sabort", "error",
+ "16", "conts_rb", "18", "linkc",
+ "20", "drdy", "22", "mrdy",
+ "24", "done", "26", "sars",
+ "28", "lpauses", "30", "int",
+};
+
+void mite_dump_regs(struct mite_channel *mite_chan)
+{
+ unsigned long mite_io_addr =
+ (unsigned long)mite_chan->mite->mite_io_addr;
+ unsigned long addr = 0;
+ unsigned long temp = 0;
+
+ printk("mite_dump_regs ch%i\n", mite_chan->channel);
+ printk("mite address is =0x%08lx\n", mite_io_addr);
+
+ addr = mite_io_addr + MITE_CHOR(channel);
+ printk("mite status[CHOR]at 0x%08lx =0x%08lx\n", addr, temp =
+ readl(addr));
+ mite_decode(mite_CHOR_strings, temp);
+ addr = mite_io_addr + MITE_CHCR(channel);
+ printk("mite status[CHCR]at 0x%08lx =0x%08lx\n", addr, temp =
+ readl(addr));
+ mite_decode(mite_CHCR_strings, temp);
+ addr = mite_io_addr + MITE_TCR(channel);
+ printk("mite status[TCR] at 0x%08lx =0x%08x\n", addr, readl(addr));
+ addr = mite_io_addr + MITE_MCR(channel);
+ printk("mite status[MCR] at 0x%08lx =0x%08lx\n", addr, temp =
+ readl(addr));
+ mite_decode(mite_MCR_strings, temp);
+
+ addr = mite_io_addr + MITE_MAR(channel);
+ printk("mite status[MAR] at 0x%08lx =0x%08x\n", addr, readl(addr));
+ addr = mite_io_addr + MITE_DCR(channel);
+ printk("mite status[DCR] at 0x%08lx =0x%08lx\n", addr, temp =
+ readl(addr));
+ mite_decode(mite_DCR_strings, temp);
+ addr = mite_io_addr + MITE_DAR(channel);
+ printk("mite status[DAR] at 0x%08lx =0x%08x\n", addr, readl(addr));
+ addr = mite_io_addr + MITE_LKCR(channel);
+ printk("mite status[LKCR]at 0x%08lx =0x%08lx\n", addr, temp =
+ readl(addr));
+ mite_decode(mite_LKCR_strings, temp);
+ addr = mite_io_addr + MITE_LKAR(channel);
+ printk("mite status[LKAR]at 0x%08lx =0x%08x\n", addr, readl(addr));
+
+ addr = mite_io_addr + MITE_CHSR(channel);
+ printk("mite status[CHSR]at 0x%08lx =0x%08lx\n", addr, temp =
+ readl(addr));
+ mite_decode(mite_CHSR_strings, temp);
+ addr = mite_io_addr + MITE_FCR(channel);
+ printk("mite status[FCR] at 0x%08lx =0x%08x\n\n", addr, readl(addr));
+}
+
+static void mite_decode(char **bit_str, unsigned int bits)
+{
+ int i;
+
+ for (i = 31; i >= 0; i--) {
+ if (bits & (1 << i)) {
+ printk(" %s", bit_str[i]);
+ }
+ }
+ printk("\n");
+}
+#endif
+
+#ifdef MODULE
+int __init init_module(void)
+{
+ mite_init();
+ mite_list_devices();
+
+ return 0;
+}
+
+void __exit cleanup_module(void)
+{
+ mite_cleanup();
+}
+
+EXPORT_SYMBOL(mite_dma_tcr);
+EXPORT_SYMBOL(mite_dma_arm);
+EXPORT_SYMBOL(mite_dma_disarm);
+EXPORT_SYMBOL(mite_sync_input_dma);
+EXPORT_SYMBOL(mite_sync_output_dma);
+EXPORT_SYMBOL(mite_setup);
+EXPORT_SYMBOL(mite_setup2);
+EXPORT_SYMBOL(mite_unsetup);
+#if 0
+EXPORT_SYMBOL(mite_kvmem_segment_load);
+EXPORT_SYMBOL(mite_ll_from_kvmem);
+EXPORT_SYMBOL(mite_setregs);
+#endif
+EXPORT_SYMBOL(mite_devices);
+EXPORT_SYMBOL(mite_list_devices);
+EXPORT_SYMBOL(mite_request_channel_in_range);
+EXPORT_SYMBOL(mite_release_channel);
+EXPORT_SYMBOL(mite_prep_dma);
+EXPORT_SYMBOL(mite_buf_change);
+EXPORT_SYMBOL(mite_bytes_written_to_memory_lb);
+EXPORT_SYMBOL(mite_bytes_written_to_memory_ub);
+EXPORT_SYMBOL(mite_bytes_read_from_memory_lb);
+EXPORT_SYMBOL(mite_bytes_read_from_memory_ub);
+EXPORT_SYMBOL(mite_bytes_in_transit);
+EXPORT_SYMBOL(mite_get_status);
+EXPORT_SYMBOL(mite_done);
+#ifdef DEBUG_MITE
+EXPORT_SYMBOL(mite_decode);
+EXPORT_SYMBOL(mite_dump_regs);
+#endif
+
+#endif
diff --git a/drivers/staging/comedi/drivers/mite.h b/drivers/staging/comedi/drivers/mite.h
new file mode 100644
index 000000000000..90a656b5c5b7
--- /dev/null
+++ b/drivers/staging/comedi/drivers/mite.h
@@ -0,0 +1,453 @@
+/*
+ module/mite.h
+ Hardware driver for NI Mite PCI interface chip
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1999 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef _MITE_H_
+#define _MITE_H_
+
+#include "../comedidev.h"
+#include "../pci.h"
+
+#define PCI_VENDOR_ID_NATINST 0x1093
+
+// #define DEBUG_MITE
+#define PCIMIO_COMPAT
+
+#ifdef DEBUG_MITE
+#define MDPRINTK(format,args...) printk(format , ## args )
+#else
+#define MDPRINTK(format,args...)
+#endif
+
+#define MAX_MITE_DMA_CHANNELS 8
+
+struct mite_dma_descriptor {
+ u32 count;
+ u32 addr;
+ u32 next;
+ u32 dar;
+};
+
+struct mite_dma_descriptor_ring {
+ struct device *hw_dev;
+ unsigned int n_links;
+ struct mite_dma_descriptor *descriptors;
+ dma_addr_t descriptors_dma_addr;
+};
+
+struct mite_channel {
+ struct mite_struct *mite;
+ unsigned channel;
+ int dir;
+ int done;
+ struct mite_dma_descriptor_ring *ring;
+};
+
+struct mite_struct {
+ struct mite_struct *next;
+ int used;
+
+ struct pci_dev *pcidev;
+ resource_size_t mite_phys_addr;
+ void *mite_io_addr;
+ resource_size_t daq_phys_addr;
+ void *daq_io_addr;
+
+ struct mite_channel channels[MAX_MITE_DMA_CHANNELS];
+ short channel_allocated[MAX_MITE_DMA_CHANNELS];
+ int num_channels;
+ unsigned fifo_size;
+ spinlock_t lock;
+};
+
+static inline struct mite_dma_descriptor_ring *mite_alloc_ring(struct
+ mite_struct *mite)
+{
+ struct mite_dma_descriptor_ring *ring =
+ kmalloc(sizeof(struct mite_dma_descriptor_ring), GFP_KERNEL);
+ if (ring == NULL)
+ return ring;
+ ring->hw_dev = get_device(&mite->pcidev->dev);
+ if (ring->hw_dev == NULL) {
+ kfree(ring);
+ return NULL;
+ }
+ ring->n_links = 0;
+ ring->descriptors = NULL;
+ ring->descriptors_dma_addr = 0;
+ return ring;
+};
+
+static inline void mite_free_ring(struct mite_dma_descriptor_ring *ring)
+{
+ if (ring) {
+ if (ring->descriptors) {
+ dma_free_coherent(ring->hw_dev,
+ ring->n_links *
+ sizeof(struct mite_dma_descriptor),
+ ring->descriptors, ring->descriptors_dma_addr);
+ }
+ put_device(ring->hw_dev);
+ kfree(ring);
+ }
+};
+
+extern struct mite_struct *mite_devices;
+
+static inline unsigned int mite_irq(struct mite_struct *mite)
+{
+ return mite->pcidev->irq;
+};
+static inline unsigned int mite_device_id(struct mite_struct *mite)
+{
+ return mite->pcidev->device;
+};
+
+void mite_init(void);
+void mite_cleanup(void);
+int mite_setup(struct mite_struct *mite);
+int mite_setup2(struct mite_struct *mite, unsigned use_iodwbsr_1);
+void mite_unsetup(struct mite_struct *mite);
+void mite_list_devices(void);
+struct mite_channel *mite_request_channel_in_range(struct mite_struct *mite,
+ struct mite_dma_descriptor_ring *ring, unsigned min_channel,
+ unsigned max_channel);
+static inline struct mite_channel *mite_request_channel(struct mite_struct
+ *mite, struct mite_dma_descriptor_ring *ring)
+{
+ return mite_request_channel_in_range(mite, ring, 0,
+ mite->num_channels - 1);
+}
+void mite_release_channel(struct mite_channel *mite_chan);
+
+unsigned mite_dma_tcr(struct mite_channel *mite_chan);
+void mite_dma_arm(struct mite_channel *mite_chan);
+void mite_dma_disarm(struct mite_channel *mite_chan);
+int mite_sync_input_dma(struct mite_channel *mite_chan, comedi_async * async);
+int mite_sync_output_dma(struct mite_channel *mite_chan, comedi_async * async);
+u32 mite_bytes_written_to_memory_lb(struct mite_channel *mite_chan);
+u32 mite_bytes_written_to_memory_ub(struct mite_channel *mite_chan);
+u32 mite_bytes_read_from_memory_lb(struct mite_channel *mite_chan);
+u32 mite_bytes_read_from_memory_ub(struct mite_channel *mite_chan);
+u32 mite_bytes_in_transit(struct mite_channel *mite_chan);
+unsigned mite_get_status(struct mite_channel *mite_chan);
+int mite_done(struct mite_channel *mite_chan);
+
+#if 0
+unsigned long mite_ll_from_kvmem(struct mite_struct *mite, comedi_async * async,
+ int len);
+void mite_setregs(struct mite_struct *mite, unsigned long ll_start, int chan,
+ int dir);
+#endif
+
+void mite_prep_dma(struct mite_channel *mite_chan,
+ unsigned int num_device_bits, unsigned int num_memory_bits);
+int mite_buf_change(struct mite_dma_descriptor_ring *ring,
+ comedi_async * async);
+
+#ifdef DEBUG_MITE
+void mite_print_chsr(unsigned int chsr);
+void mite_dump_regs(struct mite_channel *mite_chan);
+#endif
+
+static inline int CHAN_OFFSET(int channel)
+{
+ return 0x500 + 0x100 * channel;
+};
+
+enum mite_registers {
+ /* The bits 0x90180700 in MITE_UNKNOWN_DMA_BURST_REG can be
+ written and read back. The bits 0x1f always read as 1.
+ The rest always read as zero. */
+ MITE_UNKNOWN_DMA_BURST_REG = 0x28,
+ MITE_IODWBSR = 0xc0, //IO Device Window Base Size Register
+ MITE_IODWBSR_1 = 0xc4, // IO Device Window Base Size Register 1
+ MITE_IODWCR_1 = 0xf4,
+ MITE_PCI_CONFIG_OFFSET = 0x300,
+ MITE_CSIGR = 0x460 //chip signature
+};
+static inline int MITE_CHOR(int channel) // channel operation
+{
+ return CHAN_OFFSET(channel) + 0x0;
+};
+static inline int MITE_CHCR(int channel) // channel control
+{
+ return CHAN_OFFSET(channel) + 0x4;
+};
+static inline int MITE_TCR(int channel) // transfer count
+{
+ return CHAN_OFFSET(channel) + 0x8;
+};
+static inline int MITE_MCR(int channel) // memory configuration
+{
+ return CHAN_OFFSET(channel) + 0xc;
+};
+static inline int MITE_MAR(int channel) // memory address
+{
+ return CHAN_OFFSET(channel) + 0x10;
+};
+static inline int MITE_DCR(int channel) // device configuration
+{
+ return CHAN_OFFSET(channel) + 0x14;
+};
+static inline int MITE_DAR(int channel) // device address
+{
+ return CHAN_OFFSET(channel) + 0x18;
+};
+static inline int MITE_LKCR(int channel) // link configuration
+{
+ return CHAN_OFFSET(channel) + 0x1c;
+};
+static inline int MITE_LKAR(int channel) // link address
+{
+ return CHAN_OFFSET(channel) + 0x20;
+};
+static inline int MITE_LLKAR(int channel) // see mite section of tnt5002 manual
+{
+ return CHAN_OFFSET(channel) + 0x24;
+};
+static inline int MITE_BAR(int channel) // base address
+{
+ return CHAN_OFFSET(channel) + 0x28;
+};
+static inline int MITE_BCR(int channel) // base count
+{
+ return CHAN_OFFSET(channel) + 0x2c;
+};
+static inline int MITE_SAR(int channel) // ? address
+{
+ return CHAN_OFFSET(channel) + 0x30;
+};
+static inline int MITE_WSCR(int channel) // ?
+{
+ return CHAN_OFFSET(channel) + 0x34;
+};
+static inline int MITE_WSER(int channel) // ?
+{
+ return CHAN_OFFSET(channel) + 0x38;
+};
+static inline int MITE_CHSR(int channel) // channel status
+{
+ return CHAN_OFFSET(channel) + 0x3c;
+};
+static inline int MITE_FCR(int channel) // fifo count
+{
+ return CHAN_OFFSET(channel) + 0x40;
+};
+
+enum MITE_IODWBSR_bits {
+ WENAB = 0x80, // window enable
+};
+
+static inline unsigned MITE_IODWBSR_1_WSIZE_bits(unsigned size)
+{
+ unsigned order = 0;
+ while (size >>= 1)
+ ++order;
+ BUG_ON(order < 1);
+ return (order - 1) & 0x1f;
+}
+
+enum MITE_UNKNOWN_DMA_BURST_bits {
+ UNKNOWN_DMA_BURST_ENABLE_BITS = 0x600
+};
+
+static inline int mite_csigr_version(u32 csigr_bits)
+{
+ return csigr_bits & 0xf;
+};
+static inline int mite_csigr_type(u32 csigr_bits)
+{ // original mite = 0, minimite = 1
+ return (csigr_bits >> 4) & 0xf;
+};
+static inline int mite_csigr_mmode(u32 csigr_bits)
+{ // mite mode, minimite = 1
+ return (csigr_bits >> 8) & 0x3;
+};
+static inline int mite_csigr_imode(u32 csigr_bits)
+{ // cpu port interface mode, pci = 0x3
+ return (csigr_bits >> 12) & 0x3;
+};
+static inline int mite_csigr_dmac(u32 csigr_bits)
+{ // number of dma channels
+ return (csigr_bits >> 16) & 0xf;
+};
+static inline int mite_csigr_wpdep(u32 csigr_bits)
+{ // write post fifo depth
+ unsigned int wpdep_bits = (csigr_bits >> 20) & 0x7;
+ if (wpdep_bits == 0)
+ return 0;
+ else
+ return 1 << (wpdep_bits - 1);
+};
+static inline int mite_csigr_wins(u32 csigr_bits)
+{
+ return (csigr_bits >> 24) & 0x1f;
+};
+static inline int mite_csigr_iowins(u32 csigr_bits)
+{ // number of io windows
+ return (csigr_bits >> 29) & 0x7;
+};
+
+enum MITE_MCR_bits {
+ MCRPON = 0,
+};
+
+enum MITE_DCR_bits {
+ DCR_NORMAL = (1 << 29),
+ DCRPON = 0,
+};
+
+enum MITE_CHOR_bits {
+ CHOR_DMARESET = (1 << 31),
+ CHOR_SET_SEND_TC = (1 << 11),
+ CHOR_CLR_SEND_TC = (1 << 10),
+ CHOR_SET_LPAUSE = (1 << 9),
+ CHOR_CLR_LPAUSE = (1 << 8),
+ CHOR_CLRDONE = (1 << 7),
+ CHOR_CLRRB = (1 << 6),
+ CHOR_CLRLC = (1 << 5),
+ CHOR_FRESET = (1 << 4),
+ CHOR_ABORT = (1 << 3), /* stop without emptying fifo */
+ CHOR_STOP = (1 << 2), /* stop after emptying fifo */
+ CHOR_CONT = (1 << 1),
+ CHOR_START = (1 << 0),
+ CHOR_PON = (CHOR_CLR_SEND_TC | CHOR_CLR_LPAUSE),
+};
+
+enum MITE_CHCR_bits {
+ CHCR_SET_DMA_IE = (1 << 31),
+ CHCR_CLR_DMA_IE = (1 << 30),
+ CHCR_SET_LINKP_IE = (1 << 29),
+ CHCR_CLR_LINKP_IE = (1 << 28),
+ CHCR_SET_SAR_IE = (1 << 27),
+ CHCR_CLR_SAR_IE = (1 << 26),
+ CHCR_SET_DONE_IE = (1 << 25),
+ CHCR_CLR_DONE_IE = (1 << 24),
+ CHCR_SET_MRDY_IE = (1 << 23),
+ CHCR_CLR_MRDY_IE = (1 << 22),
+ CHCR_SET_DRDY_IE = (1 << 21),
+ CHCR_CLR_DRDY_IE = (1 << 20),
+ CHCR_SET_LC_IE = (1 << 19),
+ CHCR_CLR_LC_IE = (1 << 18),
+ CHCR_SET_CONT_RB_IE = (1 << 17),
+ CHCR_CLR_CONT_RB_IE = (1 << 16),
+ CHCR_FIFODIS = (1 << 15),
+ CHCR_FIFO_ON = 0,
+ CHCR_BURSTEN = (1 << 14),
+ CHCR_NO_BURSTEN = 0,
+ CHCR_BYTE_SWAP_DEVICE = (1 << 6),
+ CHCR_BYTE_SWAP_MEMORY = (1 << 4),
+ CHCR_DIR = (1 << 3),
+ CHCR_DEV_TO_MEM = CHCR_DIR,
+ CHCR_MEM_TO_DEV = 0,
+ CHCR_NORMAL = (0 << 0),
+ CHCR_CONTINUE = (1 << 0),
+ CHCR_RINGBUFF = (2 << 0),
+ CHCR_LINKSHORT = (4 << 0),
+ CHCR_LINKLONG = (5 << 0),
+ CHCRPON =
+ (CHCR_CLR_DMA_IE | CHCR_CLR_LINKP_IE | CHCR_CLR_SAR_IE |
+ CHCR_CLR_DONE_IE | CHCR_CLR_MRDY_IE | CHCR_CLR_DRDY_IE |
+ CHCR_CLR_LC_IE | CHCR_CLR_CONT_RB_IE),
+};
+
+enum ConfigRegister_bits {
+ CR_REQS_MASK = 0x7 << 16,
+ CR_ASEQDONT = 0x0 << 10,
+ CR_ASEQUP = 0x1 << 10,
+ CR_ASEQDOWN = 0x2 << 10,
+ CR_ASEQ_MASK = 0x3 << 10,
+ CR_PSIZE8 = (1 << 8),
+ CR_PSIZE16 = (2 << 8),
+ CR_PSIZE32 = (3 << 8),
+ CR_PORTCPU = (0 << 6),
+ CR_PORTIO = (1 << 6),
+ CR_PORTVXI = (2 << 6),
+ CR_PORTMXI = (3 << 6),
+ CR_AMDEVICE = (1 << 0),
+};
+static inline int CR_REQS(int source)
+{
+ return (source & 0x7) << 16;
+};
+static inline int CR_REQSDRQ(unsigned drq_line)
+{
+ /* This also works on m-series when
+ using channels (drq_line) 4 or 5. */
+ return CR_REQS((drq_line & 0x3) | 0x4);
+}
+static inline int CR_RL(unsigned int retry_limit)
+{
+ int value = 0;
+
+ while (retry_limit) {
+ retry_limit >>= 1;
+ value++;
+ }
+ if (value > 0x7)
+ rt_printk("comedi: bug! retry_limit too large\n");
+ return (value & 0x7) << 21;
+}
+
+enum CHSR_bits {
+ CHSR_INT = (1 << 31),
+ CHSR_LPAUSES = (1 << 29),
+ CHSR_SARS = (1 << 27),
+ CHSR_DONE = (1 << 25),
+ CHSR_MRDY = (1 << 23),
+ CHSR_DRDY = (1 << 21),
+ CHSR_LINKC = (1 << 19),
+ CHSR_CONTS_RB = (1 << 17),
+ CHSR_ERROR = (1 << 15),
+ CHSR_SABORT = (1 << 14),
+ CHSR_HABORT = (1 << 13),
+ CHSR_STOPS = (1 << 12),
+ CHSR_OPERR_mask = (3 << 10),
+ CHSR_OPERR_NOERROR = (0 << 10),
+ CHSR_OPERR_FIFOERROR = (1 << 10),
+ CHSR_OPERR_LINKERROR = (1 << 10), /* ??? */
+ CHSR_XFERR = (1 << 9),
+ CHSR_END = (1 << 8),
+ CHSR_DRQ1 = (1 << 7),
+ CHSR_DRQ0 = (1 << 6),
+ CHSR_LxERR_mask = (3 << 4),
+ CHSR_LBERR = (1 << 4),
+ CHSR_LRERR = (2 << 4),
+ CHSR_LOERR = (3 << 4),
+ CHSR_MxERR_mask = (3 << 2),
+ CHSR_MBERR = (1 << 2),
+ CHSR_MRERR = (2 << 2),
+ CHSR_MOERR = (3 << 2),
+ CHSR_DxERR_mask = (3 << 0),
+ CHSR_DBERR = (1 << 0),
+ CHSR_DRERR = (2 << 0),
+ CHSR_DOERR = (3 << 0),
+};
+
+static inline void mite_dma_reset(struct mite_channel *mite_chan)
+{
+ writel(CHOR_DMARESET | CHOR_FRESET,
+ mite_chan->mite->mite_io_addr + MITE_CHOR(mite_chan->channel));
+};
+
+#endif
diff --git a/drivers/staging/comedi/drivers/usbdux.c b/drivers/staging/comedi/drivers/usbdux.c
new file mode 100644
index 000000000000..33205530b0d3
--- /dev/null
+++ b/drivers/staging/comedi/drivers/usbdux.c
@@ -0,0 +1,2981 @@
+#define DRIVER_VERSION "v2.1"
+#define DRIVER_AUTHOR "Bernd Porr, BerndPorr@f2s.com"
+#define DRIVER_DESC "Stirling/ITL USB-DUX -- Bernd.Porr@f2s.com"
+/*
+ comedi/drivers/usbdux.c
+ Copyright (C) 2003-2007 Bernd Porr, Bernd.Porr@f2s.com
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+/*
+Driver: usbdux
+Description: University of Stirling USB DAQ & INCITE Technology Limited
+Devices: [ITL] USB-DUX (usbdux.o)
+Author: Bernd Porr <BerndPorr@f2s.com>
+Updated: 25 Nov 2007
+Status: Testing
+Configuration options:
+ You have to upload firmware with the -i option. The
+ firmware is usually installed under /usr/share/usb or
+ /usr/local/share/usb or /lib/firmware.
+
+Connection scheme for the counter at the digital port:
+ 0=/CLK0, 1=UP/DOWN0, 2=RESET0, 4=/CLK1, 5=UP/DOWN1, 6=RESET1.
+ The sampling rate of the counter is approximately 500Hz.
+
+Please note that under USB2.0 the length of the channel list determines
+the max sampling rate. If you sample only one channel you get 8kHz
+sampling rate. If you sample two channels you get 4kHz and so on.
+*/
+/*
+ * I must give credit here to Chris Baugher who
+ * wrote the driver for AT-MIO-16d. I used some parts of this
+ * driver. I also must give credits to David Brownell
+ * who supported me with the USB development.
+ *
+ * Bernd Porr
+ *
+ *
+ * Revision history:
+ * 0.94: D/A output should work now with any channel list combinations
+ * 0.95: .owner commented out for kernel vers below 2.4.19
+ * sanity checks in ai/ao_cmd
+ * 0.96: trying to get it working with 2.6, moved all memory alloc to comedi's attach final USB IDs
+ * moved memory allocation completely to the corresponding comedi functions
+ * firmware upload is by fxload and no longer by comedi (due to enumeration)
+ * 0.97: USB IDs received, adjusted table
+ * 0.98: SMP, locking, memroy alloc: moved all usb memory alloc
+ * to the usb subsystem and moved all comedi related memory
+ * alloc to comedi.
+ * | kernel | registration | usbdux-usb | usbdux-comedi | comedi |
+ * 0.99: USB 2.0: changed protocol to isochronous transfer
+ * IRQ transfer is too buggy and too risky in 2.0
+ * for the high speed ISO transfer is now a working version available
+ * 0.99b: Increased the iso transfer buffer for high sp.to 10 buffers. Some VIA
+ * chipsets miss out IRQs. Deeper buffering is needed.
+ * 1.00: full USB 2.0 support for the A/D converter. Now: max 8kHz sampling rate.
+ * Firmware vers 1.00 is needed for this.
+ * Two 16 bit up/down/reset counter with a sampling rate of 1kHz
+ * And loads of cleaning up, in particular streamlining the
+ * bulk transfers.
+ * 1.1: moved EP4 transfers to EP1 to make space for a PWM output on EP4
+ * 1.2: added PWM suport via EP4
+ * 2.0: PWM seems to be stable and is not interfering with the other functions
+ * 2.1: changed PWM API
+ *
+ */
+
+// generates loads of debug info
+// #define NOISY_DUX_DEBUGBUG
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/usb.h>
+#include <linux/smp_lock.h>
+#include <linux/fcntl.h>
+#include <linux/compiler.h>
+
+#include "../comedidev.h"
+#include "../usb.h"
+
+#define BOARDNAME "usbdux"
+
+// timeout for the USB-transfer
+#define EZTIMEOUT 30
+
+// constants for "firmware" upload and download
+#define USBDUXSUB_FIRMWARE 0xA0
+#define VENDOR_DIR_IN 0xC0
+#define VENDOR_DIR_OUT 0x40
+
+// internal adresses of the 8051 processor
+#define USBDUXSUB_CPUCS 0xE600
+
+// the minor device number, major is 180
+// only for debugging purposes and to
+// upload special firmware (programming the
+// eeprom etc) which is not compatible with
+// the comedi framwork
+#define USBDUXSUB_MINOR 32
+
+// max lenghth of the transfer-buffer for software upload
+#define TB_LEN 0x2000
+
+// Input endpoint number: ISO/IRQ
+#define ISOINEP 6
+
+// Output endpoint number: ISO/IRQ
+#define ISOOUTEP 2
+
+// This EP sends DUX commands to USBDUX
+#define COMMAND_OUT_EP 1
+
+// This EP receives the DUX commands from USBDUX
+#define COMMAND_IN_EP 8
+
+// Output endpoint for PWM
+#define PWM_EP 4
+
+// 300Hz max frequ under PWM
+#define MIN_PWM_PERIOD ((long)(1E9/300))
+
+// Default PWM frequency
+#define PWM_DEFAULT_PERIOD ((long)(1E9/100))
+
+// Number of channels
+#define NUMCHANNELS 8
+
+// Size of one A/D value
+#define SIZEADIN ((sizeof(int16_t)))
+
+// Size of the input-buffer IN BYTES
+// Always multiple of 8 for 8 microframes which is needed in the highspeed mode
+#define SIZEINBUF ((8*SIZEADIN))
+
+// 16 bytes.
+#define SIZEINSNBUF 16
+
+// Number of DA channels
+#define NUMOUTCHANNELS 8
+
+// size of one value for the D/A converter: channel and value
+#define SIZEDAOUT ((sizeof(int8_t)+sizeof(int16_t)))
+
+// Size of the output-buffer in bytes
+// Actually only the first 4 triplets are used but for the
+// high speed mode we need to pad it to 8 (microframes).
+#define SIZEOUTBUF ((8*SIZEDAOUT))
+
+// Size of the buffer for the dux commands: just now max size is determined
+// by the analogue out + command byte + panic bytes...
+#define SIZEOFDUXBUFFER ((8*SIZEDAOUT+2))
+
+// Number of in-URBs which receive the data: min=2
+#define NUMOFINBUFFERSFULL 5
+
+// Number of out-URBs which send the data: min=2
+#define NUMOFOUTBUFFERSFULL 5
+
+// Number of in-URBs which receive the data: min=5
+#define NUMOFINBUFFERSHIGH 10 // must have more buffers due to buggy USB ctr
+
+// Number of out-URBs which send the data: min=5
+#define NUMOFOUTBUFFERSHIGH 10 // must have more buffers due to buggy USB ctr
+
+// Total number of usbdux devices
+#define NUMUSBDUX 16
+
+// Analogue in subdevice
+#define SUBDEV_AD 0
+
+// Analogue out subdevice
+#define SUBDEV_DA 1
+
+// Digital I/O
+#define SUBDEV_DIO 2
+
+// counter
+#define SUBDEV_COUNTER 3
+
+// timer aka pwm output
+#define SUBDEV_PWM 4
+
+// number of retries to get the right dux command
+#define RETRIES 10
+
+/////////////////////////////////////////////
+// comedi constants
+static const comedi_lrange range_usbdux_ai_range = { 4, {
+ BIP_RANGE(4.096),
+ BIP_RANGE(4.096 / 2),
+ UNI_RANGE(4.096),
+ UNI_RANGE(4.096 / 2)
+ }
+};
+
+static const comedi_lrange range_usbdux_ao_range = { 2, {
+ BIP_RANGE(4.096),
+ UNI_RANGE(4.096),
+ }
+};
+
+/*
+ * private structure of one subdevice
+ */
+
+// This is the structure which holds all the data of this driver
+// one sub device just now: A/D
+typedef struct {
+ // attached?
+ int attached;
+ // is it associated with a subdevice?
+ int probed;
+ // pointer to the usb-device
+ struct usb_device *usbdev;
+ // actual number of in-buffers
+ int numOfInBuffers;
+ // actual number of out-buffers
+ int numOfOutBuffers;
+ // ISO-transfer handling: buffers
+ struct urb **urbIn;
+ struct urb **urbOut;
+ // pwm-transfer handling
+ struct urb *urbPwm;
+ // PWM period
+ lsampl_t pwmPeriod;
+ // PWM internal delay for the GPIF in the FX2
+ int8_t pwmDelay;
+ // size of the PWM buffer which holds the bit pattern
+ int sizePwmBuf;
+ // input buffer for the ISO-transfer
+ int16_t *inBuffer;
+ // input buffer for single insn
+ int16_t *insnBuffer;
+ // output buffer for single DA outputs
+ int16_t *outBuffer;
+ // interface number
+ int ifnum;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ // interface structure in 2.6
+ struct usb_interface *interface;
+#endif
+ // comedi device for the interrupt context
+ comedi_device *comedidev;
+ // is it USB_SPEED_HIGH or not?
+ short int high_speed;
+ // asynchronous command is running
+ short int ai_cmd_running;
+ short int ao_cmd_running;
+ // pwm is running
+ short int pwm_cmd_running;
+ // continous aquisition
+ short int ai_continous;
+ short int ao_continous;
+ // number of samples to aquire
+ int ai_sample_count;
+ int ao_sample_count;
+ // time between samples in units of the timer
+ unsigned int ai_timer;
+ unsigned int ao_timer;
+ // counter between aquisitions
+ unsigned int ai_counter;
+ unsigned int ao_counter;
+ // interval in frames/uframes
+ unsigned int ai_interval;
+ // D/A commands
+ int8_t *dac_commands;
+ // commands
+ int8_t *dux_commands;
+ struct semaphore sem;
+} usbduxsub_t;
+
+// The pointer to the private usb-data of the driver
+// is also the private data for the comedi-device.
+// This has to be global as the usb subsystem needs
+// global variables. The other reason is that this
+// structure must be there _before_ any comedi
+// command is issued. The usb subsystem must be
+// initialised before comedi can access it.
+static usbduxsub_t usbduxsub[NUMUSBDUX];
+
+static DECLARE_MUTEX(start_stop_sem);
+
+// Stops the data acquision
+// It should be safe to call this function from any context
+static int usbduxsub_unlink_InURBs(usbduxsub_t * usbduxsub_tmp)
+{
+ int i = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8)
+ int j = 0;
+#endif
+ int err = 0;
+
+ if (usbduxsub_tmp && usbduxsub_tmp->urbIn) {
+ for (i = 0; i < usbduxsub_tmp->numOfInBuffers; i++) {
+ if (usbduxsub_tmp->urbIn[i]) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8)
+ j = usb_unlink_urb(usbduxsub_tmp->urbIn[i]);
+ if (j < 0) {
+ err = j;
+ }
+#else
+ // We wait here until all transfers
+ // have been cancelled.
+ usb_kill_urb(usbduxsub_tmp->urbIn[i]);
+#endif
+ }
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi: usbdux: unlinked InURB %d, err=%d\n",
+ i, err);
+#endif
+ }
+ }
+ return err;
+}
+
+/* This will stop a running acquisition operation */
+// Is called from within this driver from both the
+// interrupt context and from comedi
+static int usbdux_ai_stop(usbduxsub_t * this_usbduxsub, int do_unlink)
+{
+ int ret = 0;
+
+ if (!this_usbduxsub) {
+ printk("comedi?: usbdux_ai_stop: this_usbduxsub=NULL!\n");
+ return -EFAULT;
+ }
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi: usbdux_ai_stop\n");
+#endif
+
+ if (do_unlink) {
+ // stop aquistion
+ ret = usbduxsub_unlink_InURBs(this_usbduxsub);
+ }
+
+ this_usbduxsub->ai_cmd_running = 0;
+
+ return ret;
+}
+
+// This will cancel a running acquisition operation.
+// This is called by comedi but never from inside the
+// driver.
+static int usbdux_ai_cancel(comedi_device * dev, comedi_subdevice * s)
+{
+ usbduxsub_t *this_usbduxsub;
+ int res = 0;
+
+ // force unlink of all urbs
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi: usbdux_ai_cancel\n");
+#endif
+ this_usbduxsub = dev->private;
+ if (!this_usbduxsub) {
+ printk("comedi: usbdux_ai_cancel: this_usbduxsub=NULL\n");
+ return -EFAULT;
+ }
+ // prevent other CPUs from submitting new commands just now
+ down(&this_usbduxsub->sem);
+ if (!(this_usbduxsub->probed)) {
+ up(&this_usbduxsub->sem);
+ return -ENODEV;
+ }
+ // unlink only if the urb really has been submitted
+ res = usbdux_ai_stop(this_usbduxsub, this_usbduxsub->ai_cmd_running);
+ up(&this_usbduxsub->sem);
+ return res;
+}
+
+// analogue IN
+// interrupt service routine
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+static void usbduxsub_ai_IsocIrq(struct urb *urb)
+#else
+static void usbduxsub_ai_IsocIrq(struct urb *urb PT_REGS_ARG)
+#endif
+{
+ int i, err, n;
+ usbduxsub_t *this_usbduxsub;
+ comedi_device *this_comedidev;
+ comedi_subdevice *s;
+
+ // sanity checks
+ // is the urb there?
+ if (!urb) {
+ printk("comedi_: usbdux_: ao int-handler called with urb=NULL!\n");
+ return;
+ }
+ // the context variable points to the subdevice
+ this_comedidev = urb->context;
+ if (unlikely(!this_comedidev)) {
+ printk("comedi_: usbdux_: BUG! urb context is a NULL pointer!\n");
+ return;
+ }
+ // the private structure of the subdevice is usbduxsub_t
+ this_usbduxsub = this_comedidev->private;
+ if (unlikely(!this_usbduxsub)) {
+ printk("comedi_: usbdux_: BUG! private of comedi subdev is a NULL pointer!\n");
+ return;
+ }
+ // subdevice which is the AD converter
+ s = this_comedidev->subdevices + SUBDEV_AD;
+
+ // first we test if something unusual has just happened
+ switch (urb->status) {
+ case 0:
+ // copy the result in the transfer buffer
+ memcpy(this_usbduxsub->inBuffer,
+ urb->transfer_buffer, SIZEINBUF);
+ break;
+ case -EILSEQ:
+ // error in the ISOchronous data
+ // we don't copy the data into the transfer buffer
+ // and recycle the last data byte
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi%d: usbdux: CRC error in ISO IN stream.\n",
+ this_usbduxsub->comedidev->minor);
+#endif
+
+ break;
+
+ // happens after an unlink command
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -ECONNABORTED:
+ if (this_usbduxsub->ai_cmd_running) {
+ // we are still running a command
+ // tell this comedi
+ s->async->events |= COMEDI_CB_EOA;
+ s->async->events |= COMEDI_CB_ERROR;
+ comedi_event(this_usbduxsub->comedidev, s);
+ // stop the transfer w/o unlink
+ usbdux_ai_stop(this_usbduxsub, 0);
+ }
+ return;
+
+ // a real error on the bus
+ default:
+ // pass error to comedi if we are really running a command
+ if (this_usbduxsub->ai_cmd_running) {
+ printk("Non-zero urb status received in ai intr context: %d\n", urb->status);
+ s->async->events |= COMEDI_CB_EOA;
+ s->async->events |= COMEDI_CB_ERROR;
+ comedi_event(this_usbduxsub->comedidev, s);
+ // don't do an unlink here
+ usbdux_ai_stop(this_usbduxsub, 0);
+ }
+ return;
+ }
+
+ // at this point we are reasonably sure that nothing dodgy has happened
+ // are we running a command?
+ if (unlikely((!(this_usbduxsub->ai_cmd_running)))) {
+ // not running a command
+ // do not continue execution if no asynchronous command is running
+ // in particular not resubmit
+ return;
+ }
+
+ urb->dev = this_usbduxsub->usbdev;
+
+ // resubmit the urb
+ err = USB_SUBMIT_URB(urb);
+ if (unlikely(err < 0)) {
+ printk("comedi_: usbdux_: urb resubmit failed in int-context! err=%d ", err);
+ if (err == -EL2NSYNC) {
+ printk("--> buggy USB host controller or bug in IRQ handler!\n");
+ } else {
+ printk("\n");
+ }
+ s->async->events |= COMEDI_CB_EOA;
+ s->async->events |= COMEDI_CB_ERROR;
+ comedi_event(this_usbduxsub->comedidev, s);
+ // don't do an unlink here
+ usbdux_ai_stop(this_usbduxsub, 0);
+ return;
+ }
+
+ this_usbduxsub->ai_counter--;
+ if (likely(this_usbduxsub->ai_counter > 0)) {
+ return;
+ }
+ // timer zero, transfer measurements to comedi
+ this_usbduxsub->ai_counter = this_usbduxsub->ai_timer;
+
+ // test, if we transmit only a fixed number of samples
+ if (!(this_usbduxsub->ai_continous)) {
+ // not continous, fixed number of samples
+ this_usbduxsub->ai_sample_count--;
+ // all samples received?
+ if (this_usbduxsub->ai_sample_count < 0) {
+ // prevent a resubmit next time
+ usbdux_ai_stop(this_usbduxsub, 0);
+ // say comedi that the acquistion is over
+ s->async->events |= COMEDI_CB_EOA;
+ comedi_event(this_usbduxsub->comedidev, s);
+ return;
+ }
+ }
+ // get the data from the USB bus and hand it over
+ // to comedi
+ n = s->async->cmd.chanlist_len;
+ for (i = 0; i < n; i++) {
+ // transfer data
+ if (CR_RANGE(s->async->cmd.chanlist[i]) <= 1) {
+ comedi_buf_put
+ (s->async,
+ le16_to_cpu(this_usbduxsub->
+ inBuffer[i]) ^ 0x800);
+ } else {
+ comedi_buf_put
+ (s->async,
+ le16_to_cpu(this_usbduxsub->inBuffer[i]));
+ }
+ }
+ // tell comedi that data is there
+ comedi_event(this_usbduxsub->comedidev, s);
+}
+
+static int usbduxsub_unlink_OutURBs(usbduxsub_t * usbduxsub_tmp)
+{
+ int i = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8)
+ int j = 0;
+#endif
+
+ int err = 0;
+
+ if (usbduxsub_tmp && usbduxsub_tmp->urbOut) {
+ for (i = 0; i < usbduxsub_tmp->numOfOutBuffers; i++) {
+ if (usbduxsub_tmp->urbOut[i]) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8)
+ j = usb_unlink_urb(usbduxsub_tmp->urbOut[i]);
+ if (j < err) {
+ err = j;
+ }
+#else
+ usb_kill_urb(usbduxsub_tmp->urbOut[i]);
+#endif
+ }
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi: usbdux: unlinked OutURB %d: res=%d\n",
+ i, err);
+#endif
+ }
+ }
+ return err;
+}
+
+/* This will cancel a running acquisition operation
+ * in any context.
+ */
+static int usbdux_ao_stop(usbduxsub_t * this_usbduxsub, int do_unlink)
+{
+ int ret = 0;
+
+ if (!this_usbduxsub) {
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi?: usbdux_ao_stop: this_usbduxsub=NULL!\n");
+#endif
+ return -EFAULT;
+ }
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi: usbdux_ao_cancel\n");
+#endif
+ if (do_unlink) {
+ ret = usbduxsub_unlink_OutURBs(this_usbduxsub);
+ }
+
+ this_usbduxsub->ao_cmd_running = 0;
+
+ return ret;
+}
+
+// force unlink
+// is called by comedi
+static int usbdux_ao_cancel(comedi_device * dev, comedi_subdevice * s)
+{
+ usbduxsub_t *this_usbduxsub = dev->private;
+ int res = 0;
+
+ if (!this_usbduxsub) {
+ printk("comedi: usbdux_ao_cancel: this_usbduxsub=NULL\n");
+ return -EFAULT;
+ }
+ // prevent other CPUs from submitting a command just now
+ down(&this_usbduxsub->sem);
+ if (!(this_usbduxsub->probed)) {
+ up(&this_usbduxsub->sem);
+ return -ENODEV;
+ }
+ // unlink only if it is really running
+ res = usbdux_ao_stop(this_usbduxsub, this_usbduxsub->ao_cmd_running);
+ up(&this_usbduxsub->sem);
+ return res;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+static void usbduxsub_ao_IsocIrq(struct urb *urb)
+{
+#else
+static void usbduxsub_ao_IsocIrq(struct urb *urb PT_REGS_ARG)
+{
+#endif
+ int i, ret;
+ int8_t *datap;
+ usbduxsub_t *this_usbduxsub;
+ comedi_device *this_comedidev;
+ comedi_subdevice *s;
+
+ if (!urb) {
+ printk("comedi_: usbdux_: ao urb handler called with NULL ptr.\n");
+ return;
+ }
+ // the context variable points to the subdevice
+ this_comedidev = urb->context;
+ if (!this_comedidev) {
+ printk("comedi_: usbdux_: ao urb int-context is a NULL pointer.\n");
+ return;
+ }
+ // the private structure of the subdevice is usbduxsub_t
+ this_usbduxsub = this_comedidev->private;
+ if (!this_usbduxsub) {
+ printk("comedi_: usbdux_: private data structure of ao subdev is NULL p.\n");
+ return;
+ }
+
+ s = this_comedidev->subdevices + SUBDEV_DA;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+
+ // after an unlink command, unplug, ... etc
+ // no unlink needed here. Already shutting down.
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -ECONNABORTED:
+ if (this_usbduxsub->ao_cmd_running) {
+ s->async->events |= COMEDI_CB_EOA;
+ comedi_event(this_usbduxsub->comedidev, s);
+ usbdux_ao_stop(this_usbduxsub, 0);
+ }
+ return;
+
+ // a real error
+ default:
+ if (this_usbduxsub->ao_cmd_running) {
+ printk("comedi_: usbdux_: Non-zero urb status received in ao intr context: %d\n", urb->status);
+ s->async->events |= COMEDI_CB_ERROR;
+ s->async->events |= COMEDI_CB_EOA;
+ comedi_event(this_usbduxsub->comedidev, s);
+ // we do an unlink if we are in the high speed mode
+ usbdux_ao_stop(this_usbduxsub, 0);
+ }
+ return;
+ }
+
+ // are we actually running?
+ if (!(this_usbduxsub->ao_cmd_running)) {
+ return;
+ }
+ // normal operation: executing a command in this subdevice
+ this_usbduxsub->ao_counter--;
+ if (this_usbduxsub->ao_counter <= 0) {
+ // timer zero
+ this_usbduxsub->ao_counter = this_usbduxsub->ao_timer;
+
+ // handle non continous aquisition
+ if (!(this_usbduxsub->ao_continous)) {
+ // fixed number of samples
+ this_usbduxsub->ao_sample_count--;
+ if (this_usbduxsub->ao_sample_count < 0) {
+ // all samples transmitted
+ usbdux_ao_stop(this_usbduxsub, 0);
+ s->async->events |= COMEDI_CB_EOA;
+ comedi_event(this_usbduxsub->comedidev, s);
+ // no resubmit of the urb
+ return;
+ }
+ }
+ // transmit data to the USB bus
+ ((uint8_t *) (urb->transfer_buffer))[0] =
+ s->async->cmd.chanlist_len;
+ for (i = 0; i < s->async->cmd.chanlist_len; i++) {
+ sampl_t temp;
+ if (i >= NUMOUTCHANNELS) {
+ break;
+ }
+ // pointer to the DA
+ datap = (&(((int8_t *) urb->transfer_buffer)[i * 3 + 1]));
+ // get the data from comedi
+ ret = comedi_buf_get(s->async, &temp);
+ datap[0] = temp;
+ datap[1] = temp >> 8;
+ datap[2] = this_usbduxsub->dac_commands[i];
+ // printk("data[0]=%x, data[1]=%x, data[2]=%x\n",
+ // datap[0],datap[1],datap[2]);
+ if (ret < 0) {
+ printk("comedi: usbdux: buffer underflow\n");
+ s->async->events |= COMEDI_CB_EOA;
+ s->async->events |= COMEDI_CB_OVERFLOW;
+ }
+ // transmit data to comedi
+ s->async->events |= COMEDI_CB_BLOCK;
+ comedi_event(this_usbduxsub->comedidev, s);
+ }
+ }
+ urb->transfer_buffer_length = SIZEOUTBUF;
+ urb->dev = this_usbduxsub->usbdev;
+ urb->status = 0;
+ if (this_usbduxsub->ao_cmd_running) {
+ if (this_usbduxsub->high_speed) {
+ // uframes
+ urb->interval = 8;
+ } else {
+ // frames
+ urb->interval = 1;
+ }
+ urb->number_of_packets = 1;
+ urb->iso_frame_desc[0].offset = 0;
+ urb->iso_frame_desc[0].length = SIZEOUTBUF;
+ urb->iso_frame_desc[0].status = 0;
+ if ((ret = USB_SUBMIT_URB(urb)) < 0) {
+ printk("comedi_: usbdux_: ao urb resubm failed in int-cont.");
+ printk("ret=%d", ret);
+ if (ret == EL2NSYNC) {
+ printk("--> buggy USB host controller or bug in IRQ handling!\n");
+ } else {
+ printk("\n");
+ }
+ s->async->events |= COMEDI_CB_EOA;
+ s->async->events |= COMEDI_CB_ERROR;
+ comedi_event(this_usbduxsub->comedidev, s);
+ // don't do an unlink here
+ usbdux_ao_stop(this_usbduxsub, 0);
+ }
+ }
+}
+
+static int usbduxsub_start(usbduxsub_t * usbduxsub)
+{
+ int errcode = 0;
+ uint8_t local_transfer_buffer[16];
+
+ if (usbduxsub->probed) {
+ // 7f92 to zero
+ local_transfer_buffer[0] = 0;
+ errcode = USB_CONTROL_MSG(usbduxsub->usbdev,
+ // create a pipe for a control transfer
+ usb_sndctrlpipe(usbduxsub->usbdev, 0),
+ // bRequest, "Firmware"
+ USBDUXSUB_FIRMWARE,
+ // bmRequestType
+ VENDOR_DIR_OUT,
+ // Value
+ USBDUXSUB_CPUCS,
+ // Index
+ 0x0000,
+ // address of the transfer buffer
+ local_transfer_buffer,
+ // Length
+ 1,
+ // Timeout
+ EZTIMEOUT);
+ if (errcode < 0) {
+ printk("comedi_: usbdux_: control msg failed (start)\n");
+ return errcode;
+ }
+ }
+ return 0;
+}
+
+static int usbduxsub_stop(usbduxsub_t * usbduxsub)
+{
+ int errcode = 0;
+
+ uint8_t local_transfer_buffer[16];
+ if (usbduxsub->probed) {
+ // 7f92 to one
+ local_transfer_buffer[0] = 1;
+ errcode = USB_CONTROL_MSG
+ (usbduxsub->usbdev,
+ usb_sndctrlpipe(usbduxsub->usbdev, 0),
+ // bRequest, "Firmware"
+ USBDUXSUB_FIRMWARE,
+ // bmRequestType
+ VENDOR_DIR_OUT,
+ // Value
+ USBDUXSUB_CPUCS,
+ // Index
+ 0x0000, local_transfer_buffer,
+ // Length
+ 1,
+ // Timeout
+ EZTIMEOUT);
+ if (errcode < 0) {
+ printk("comedi_: usbdux: control msg failed (stop)\n");
+ return errcode;
+ }
+ }
+ return 0;
+}
+
+static int usbduxsub_upload(usbduxsub_t * usbduxsub,
+ uint8_t * local_transfer_buffer,
+ unsigned int startAddr, unsigned int len)
+{
+ int errcode;
+
+ if (usbduxsub->probed) {
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi%d: usbdux: uploading %d bytes",
+ usbduxsub->comedidev->minor, len);
+ printk(" to addr %d, first byte=%d.\n",
+ startAddr, local_transfer_buffer[0]);
+#endif
+ errcode = USB_CONTROL_MSG
+ (usbduxsub->usbdev,
+ usb_sndctrlpipe(usbduxsub->usbdev, 0),
+ // brequest, firmware
+ USBDUXSUB_FIRMWARE,
+ // bmRequestType
+ VENDOR_DIR_OUT,
+ // value
+ startAddr,
+ // index
+ 0x0000,
+ // our local safe buffer
+ local_transfer_buffer,
+ // length
+ len,
+ // timeout
+ EZTIMEOUT);
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi_: usbdux: result=%d\n", errcode);
+#endif
+ if (errcode < 0) {
+ printk("comedi_: usbdux: uppload failed\n");
+ return errcode;
+ }
+ } else {
+ // no device on the bus for this index
+ return -EFAULT;
+ }
+ return 0;
+}
+
+int firmwareUpload(usbduxsub_t * usbduxsub,
+ uint8_t * firmwareBinary, int sizeFirmware)
+{
+ int ret;
+
+ if (!firmwareBinary) {
+ return 0;
+ }
+ ret = usbduxsub_stop(usbduxsub);
+ if (ret < 0) {
+ printk("comedi_: usbdux: can not stop firmware\n");
+ return ret;
+ }
+ ret = usbduxsub_upload(usbduxsub, firmwareBinary, 0, sizeFirmware);
+ if (ret < 0) {
+ printk("comedi_: usbdux: firmware upload failed\n");
+ return ret;
+ }
+ ret = usbduxsub_start(usbduxsub);
+ if (ret < 0) {
+ printk("comedi_: usbdux: can not start firmware\n");
+ return ret;
+ }
+ return 0;
+}
+
+int usbduxsub_submit_InURBs(usbduxsub_t * usbduxsub)
+{
+ int i, errFlag;
+
+ if (!usbduxsub) {
+ return -EFAULT;
+ }
+ /* Submit all URBs and start the transfer on the bus */
+ for (i = 0; i < usbduxsub->numOfInBuffers; i++) {
+ // in case of a resubmission after an unlink...
+ usbduxsub->urbIn[i]->interval = usbduxsub->ai_interval;
+ usbduxsub->urbIn[i]->context = usbduxsub->comedidev;
+ usbduxsub->urbIn[i]->dev = usbduxsub->usbdev;
+ usbduxsub->urbIn[i]->status = 0;
+ usbduxsub->urbIn[i]->transfer_flags = URB_ISO_ASAP;
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi%d: usbdux: submitting in-urb[%d]: %p,%p intv=%d\n", usbduxsub->comedidev->minor, i, (usbduxsub->urbIn[i]->context), (usbduxsub->urbIn[i]->dev), (usbduxsub->urbIn[i]->interval));
+#endif
+ errFlag = USB_SUBMIT_URB(usbduxsub->urbIn[i]);
+ if (errFlag) {
+ printk("comedi_: usbdux: ai: ");
+ printk("USB_SUBMIT_URB(%d)", i);
+ printk(" error %d\n", errFlag);
+ return errFlag;
+ }
+ }
+ return 0;
+}
+
+int usbduxsub_submit_OutURBs(usbduxsub_t * usbduxsub)
+{
+ int i, errFlag;
+
+ if (!usbduxsub) {
+ return -EFAULT;
+ }
+ for (i = 0; i < usbduxsub->numOfOutBuffers; i++) {
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi_: usbdux: submitting out-urb[%d]\n", i);
+#endif
+ // in case of a resubmission after an unlink...
+ usbduxsub->urbOut[i]->context = usbduxsub->comedidev;
+ usbduxsub->urbOut[i]->dev = usbduxsub->usbdev;
+ usbduxsub->urbOut[i]->status = 0;
+ usbduxsub->urbOut[i]->transfer_flags = URB_ISO_ASAP;
+ errFlag = USB_SUBMIT_URB(usbduxsub->urbOut[i]);
+ if (errFlag) {
+ printk("comedi_: usbdux: ao: ");
+ printk("USB_SUBMIT_URB(%d)", i);
+ printk(" error %d\n", errFlag);
+ return errFlag;
+ }
+ }
+ return 0;
+}
+
+static int usbdux_ai_cmdtest(comedi_device * dev,
+ comedi_subdevice * s, comedi_cmd * cmd)
+{
+ int err = 0, tmp, i;
+ unsigned int tmpTimer;
+ usbduxsub_t *this_usbduxsub = dev->private;
+ if (!(this_usbduxsub->probed)) {
+ return -ENODEV;
+ }
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi%d: usbdux_ai_cmdtest\n", dev->minor);
+#endif
+ /* make sure triggers are valid */
+ // Only immediate triggers are allowed
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW | TRIG_INT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ // trigger should happen timed
+ tmp = cmd->scan_begin_src;
+ // start a new _scan_ with a timer
+ cmd->scan_begin_src &= TRIG_TIMER;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ // scanning is continous
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_NOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ // issue a trigger when scan is finished and start a new scan
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ // trigger at the end of count events or not, stop condition or not
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+ /* note that mutual compatiblity is not an issue here */
+ if (cmd->scan_begin_src != TRIG_FOLLOW &&
+ cmd->scan_begin_src != TRIG_EXT &&
+ cmd->scan_begin_src != TRIG_TIMER)
+ err++;
+ if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW) {
+ /* internal trigger */
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+ }
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ if (this_usbduxsub->high_speed) {
+ // In high speed mode microframes are possible.
+ // However, during one microframe we can roughly
+ // sample one channel. Thus, the more channels
+ // are in the channel list the more time we need.
+ i = 1;
+ // find a power of 2 for the number of channels
+ while (i < (cmd->chanlist_len)) {
+ i = i * 2;
+ }
+ if (cmd->scan_begin_arg < (1000000 / 8 * i)) {
+ cmd->scan_begin_arg = 1000000 / 8 * i;
+ err++;
+ }
+ // now calc the real sampling rate with all the rounding errors
+ tmpTimer =
+ ((unsigned int)(cmd->scan_begin_arg / 125000)) *
+ 125000;
+ if (cmd->scan_begin_arg != tmpTimer) {
+ cmd->scan_begin_arg = tmpTimer;
+ err++;
+ }
+ } else { // full speed
+ // 1kHz scans every USB frame
+ if (cmd->scan_begin_arg < 1000000) {
+ cmd->scan_begin_arg = 1000000;
+ err++;
+ }
+ // calc the real sampling rate with the rounding errors
+ tmpTimer =
+ ((unsigned int)(cmd->scan_begin_arg /
+ 1000000)) * 1000000;
+ if (cmd->scan_begin_arg != tmpTimer) {
+ cmd->scan_begin_arg = tmpTimer;
+ err++;
+ }
+ }
+ }
+ // the same argument
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ /* any count is allowed */
+ } else {
+ /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ return 0;
+}
+
+// creates the ADC command for the MAX1271
+// range is the range value from comedi
+static int8_t create_adc_command(unsigned int chan, int range)
+{
+ int8_t p = (range <= 1);
+ int8_t r = ((range % 2) == 0);
+ return (chan << 4) | ((p == 1) << 2) | ((r == 1) << 3);
+}
+
+// bulk transfers to usbdux
+
+#define SENDADCOMMANDS 0
+#define SENDDACOMMANDS 1
+#define SENDDIOCONFIGCOMMAND 2
+#define SENDDIOBITSCOMMAND 3
+#define SENDSINGLEAD 4
+#define READCOUNTERCOMMAND 5
+#define WRITECOUNTERCOMMAND 6
+#define SENDPWMON 7
+#define SENDPWMOFF 8
+
+static int send_dux_commands(usbduxsub_t * this_usbduxsub, int cmd_type)
+{
+ int result, nsent;
+
+ this_usbduxsub->dux_commands[0] = cmd_type;
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi%d: usbdux: dux_commands: ",
+ this_usbduxsub->comedidev->minor);
+ for (result = 0; result < SIZEOFDUXBUFFER; result++) {
+ printk(" %02x", this_usbduxsub->dux_commands[result]);
+ }
+ printk("\n");
+#endif
+ result = USB_BULK_MSG(this_usbduxsub->usbdev,
+ usb_sndbulkpipe(this_usbduxsub->usbdev,
+ COMMAND_OUT_EP),
+ this_usbduxsub->dux_commands, SIZEOFDUXBUFFER, &nsent, 10 * HZ);
+ if (result < 0) {
+ printk("comedi%d: could not transmit dux_command to the usb-device, err=%d\n", this_usbduxsub->comedidev->minor, result);
+ }
+ return result;
+}
+
+static int receive_dux_commands(usbduxsub_t * this_usbduxsub, int command)
+{
+ int result = (-EFAULT);
+ int nrec;
+ int i;
+
+ for (i = 0; i < RETRIES; i++) {
+ result = USB_BULK_MSG(this_usbduxsub->usbdev,
+ usb_rcvbulkpipe(this_usbduxsub->usbdev,
+ COMMAND_IN_EP),
+ this_usbduxsub->insnBuffer, SIZEINSNBUF, &nrec, 1 * HZ);
+ if (result < 0) {
+ printk("comedi%d: insn: USB error %d while receiving DUX command\n", this_usbduxsub->comedidev->minor, result);
+ return result;
+ }
+ if (le16_to_cpu(this_usbduxsub->insnBuffer[0]) == command) {
+ return result;
+ }
+ }
+ // this is only reached if the data has been requested a couple of times
+ printk("comedi%d: insn: wrong data returned from firmware: want cmd %d, got cmd %d.\n", this_usbduxsub->comedidev->minor, command, le16_to_cpu(this_usbduxsub->insnBuffer[0]));
+ return -EFAULT;
+}
+
+static int usbdux_ai_inttrig(comedi_device * dev,
+ comedi_subdevice * s, unsigned int trignum)
+{
+ int ret;
+ usbduxsub_t *this_usbduxsub = dev->private;
+ if (!this_usbduxsub) {
+ return -EFAULT;
+ }
+ down(&this_usbduxsub->sem);
+ if (!(this_usbduxsub->probed)) {
+ up(&this_usbduxsub->sem);
+ return -ENODEV;
+ }
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi%d: usbdux_ai_inttrig\n", dev->minor);
+#endif
+
+ if (trignum != 0) {
+ printk("comedi%d: usbdux_ai_inttrig: invalid trignum\n",
+ dev->minor);
+ up(&this_usbduxsub->sem);
+ return -EINVAL;
+ }
+ if (!(this_usbduxsub->ai_cmd_running)) {
+ this_usbduxsub->ai_cmd_running = 1;
+ ret = usbduxsub_submit_InURBs(this_usbduxsub);
+ if (ret < 0) {
+ printk("comedi%d: usbdux_ai_inttrig: urbSubmit: err=%d\n", dev->minor, ret);
+ this_usbduxsub->ai_cmd_running = 0;
+ up(&this_usbduxsub->sem);
+ return ret;
+ }
+ s->async->inttrig = NULL;
+ } else {
+ printk("comedi%d: ai_inttrig but acqu is already running\n",
+ dev->minor);
+ }
+ up(&this_usbduxsub->sem);
+ return 1;
+}
+
+static int usbdux_ai_cmd(comedi_device * dev, comedi_subdevice * s)
+{
+ comedi_cmd *cmd = &s->async->cmd;
+ unsigned int chan, range;
+ int i, ret;
+ usbduxsub_t *this_usbduxsub = dev->private;
+ int result;
+
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi%d: usbdux_ai_cmd\n", dev->minor);
+#endif
+ if (!this_usbduxsub) {
+ return -EFAULT;
+ }
+ // block other CPUs from starting an ai_cmd
+ down(&this_usbduxsub->sem);
+
+ if (!(this_usbduxsub->probed)) {
+ up(&this_usbduxsub->sem);
+ return -ENODEV;
+ }
+ if (this_usbduxsub->ai_cmd_running) {
+ printk("comedi%d: ai_cmd not possible. Another ai_cmd is running.\n", dev->minor);
+ up(&this_usbduxsub->sem);
+ return -EBUSY;
+ }
+ // set current channel of the running aquisition to zero
+ s->async->cur_chan = 0;
+
+ this_usbduxsub->dux_commands[1] = cmd->chanlist_len;
+ for (i = 0; i < cmd->chanlist_len; ++i) {
+ chan = CR_CHAN(cmd->chanlist[i]);
+ range = CR_RANGE(cmd->chanlist[i]);
+ if (i >= NUMCHANNELS) {
+ printk("comedi%d: channel list too long\n", dev->minor);
+ break;
+ }
+ this_usbduxsub->dux_commands[i + 2] =
+ create_adc_command(chan, range);
+ }
+
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi %d: sending commands to the usb device: ", dev->minor);
+ printk("size=%u\n", NUMCHANNELS);
+#endif
+ if ((result = send_dux_commands(this_usbduxsub, SENDADCOMMANDS)) < 0) {
+ up(&this_usbduxsub->sem);
+ return result;
+ }
+
+ if (this_usbduxsub->high_speed) {
+ // every channel gets a time window of 125us. Thus, if we
+ // sample all 8 channels we need 1ms. If we sample only
+ // one channel we need only 125us
+ this_usbduxsub->ai_interval = 1;
+ // find a power of 2 for the interval
+ while ((this_usbduxsub->ai_interval) < (cmd->chanlist_len)) {
+ this_usbduxsub->ai_interval =
+ (this_usbduxsub->ai_interval) * 2;
+ }
+ this_usbduxsub->ai_timer =
+ cmd->scan_begin_arg / (125000 *
+ (this_usbduxsub->ai_interval));
+ } else {
+ // interval always 1ms
+ this_usbduxsub->ai_interval = 1;
+ this_usbduxsub->ai_timer = cmd->scan_begin_arg / 1000000;
+ }
+ if (this_usbduxsub->ai_timer < 1) {
+ printk("comedi%d: usbdux: ai_cmd: timer=%d, scan_begin_arg=%d. Not properly tested by cmdtest?\n", dev->minor, this_usbduxsub->ai_timer, cmd->scan_begin_arg);
+ up(&this_usbduxsub->sem);
+ return -EINVAL;
+ }
+ this_usbduxsub->ai_counter = this_usbduxsub->ai_timer;
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ // data arrives as one packet
+ this_usbduxsub->ai_sample_count = cmd->stop_arg;
+ this_usbduxsub->ai_continous = 0;
+ } else {
+ // continous aquisition
+ this_usbduxsub->ai_continous = 1;
+ this_usbduxsub->ai_sample_count = 0;
+ }
+
+ if (cmd->start_src == TRIG_NOW) {
+ // enable this acquisition operation
+ this_usbduxsub->ai_cmd_running = 1;
+ ret = usbduxsub_submit_InURBs(this_usbduxsub);
+ if (ret < 0) {
+ this_usbduxsub->ai_cmd_running = 0;
+ // fixme: unlink here??
+ up(&this_usbduxsub->sem);
+ return ret;
+ }
+ s->async->inttrig = NULL;
+ } else {
+ /* TRIG_INT */
+ // don't enable the acquision operation
+ // wait for an internal signal
+ s->async->inttrig = usbdux_ai_inttrig;
+ }
+ up(&this_usbduxsub->sem);
+ return 0;
+}
+
+/* Mode 0 is used to get a single conversion on demand */
+static int usbdux_ai_insn_read(comedi_device * dev,
+ comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
+{
+ int i;
+ lsampl_t one = 0;
+ int chan, range;
+ int err;
+ usbduxsub_t *this_usbduxsub = dev->private;
+
+ if (!this_usbduxsub) {
+ printk("comedi%d: ai_insn_read: no usb dev.\n", dev->minor);
+ return 0;
+ }
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi%d: ai_insn_read, insn->n=%d, insn->subdev=%d\n",
+ dev->minor, insn->n, insn->subdev);
+#endif
+ down(&this_usbduxsub->sem);
+ if (!(this_usbduxsub->probed)) {
+ up(&this_usbduxsub->sem);
+ return -ENODEV;
+ }
+ if (this_usbduxsub->ai_cmd_running) {
+ printk("comedi%d: ai_insn_read not possible. Async Command is running.\n", dev->minor);
+ up(&this_usbduxsub->sem);
+ return 0;
+ }
+
+ // sample one channel
+ chan = CR_CHAN(insn->chanspec);
+ range = CR_RANGE(insn->chanspec);
+ // set command for the first channel
+ this_usbduxsub->dux_commands[1] = create_adc_command(chan, range);
+
+ // adc commands
+ if ((err = send_dux_commands(this_usbduxsub, SENDSINGLEAD)) < 0) {
+ up(&this_usbduxsub->sem);
+ return err;
+ }
+
+ for (i = 0; i < insn->n; i++) {
+ if ((err = receive_dux_commands(this_usbduxsub,
+ SENDSINGLEAD)) < 0) {
+ up(&this_usbduxsub->sem);
+ return 0;
+ }
+ one = le16_to_cpu(this_usbduxsub->insnBuffer[1]);
+ if (CR_RANGE(insn->chanspec) <= 1) {
+ one = one ^ 0x800;
+ }
+ data[i] = one;
+ }
+ up(&this_usbduxsub->sem);
+ return i;
+}
+
+//////////////////
+// analog out
+
+static int usbdux_ao_insn_read(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+ usbduxsub_t *this_usbduxsub = dev->private;
+
+ if (!this_usbduxsub) {
+ return -EFAULT;
+ }
+ down(&this_usbduxsub->sem);
+ if (!(this_usbduxsub->probed)) {
+ up(&this_usbduxsub->sem);
+ return -ENODEV;
+ }
+ for (i = 0; i < insn->n; i++) {
+ data[i] = this_usbduxsub->outBuffer[chan];
+ }
+ up(&this_usbduxsub->sem);
+ return i;
+}
+
+static int usbdux_ao_insn_write(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ int i, err;
+ int chan = CR_CHAN(insn->chanspec);
+ usbduxsub_t *this_usbduxsub = dev->private;
+
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi%d: ao_insn_write\n", dev->minor);
+#endif
+ if (!this_usbduxsub) {
+ return -EFAULT;
+ }
+ down(&this_usbduxsub->sem);
+ if (!(this_usbduxsub->probed)) {
+ up(&this_usbduxsub->sem);
+ return -ENODEV;
+ }
+ if (this_usbduxsub->ao_cmd_running) {
+ printk("comedi%d: ao_insn_write: ERROR: asynchronous ao_cmd is running\n", dev->minor);
+ up(&this_usbduxsub->sem);
+ return 0;
+ }
+
+ for (i = 0; i < insn->n; i++) {
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi%d: ao_insn_write: data[chan=%d,i=%d]=%d\n",
+ dev->minor, chan, i, data[i]);
+#endif
+ // number of channels: 1
+ this_usbduxsub->dux_commands[1] = 1;
+ // one 16 bit value
+ *((int16_t *) (this_usbduxsub->dux_commands + 2)) =
+ cpu_to_le16(data[i]);
+ this_usbduxsub->outBuffer[chan] = data[i];
+ // channel number
+ this_usbduxsub->dux_commands[4] = (chan << 6);
+ if ((err = send_dux_commands(this_usbduxsub,
+ SENDDACOMMANDS)) < 0) {
+ up(&this_usbduxsub->sem);
+ return err;
+ }
+ }
+ up(&this_usbduxsub->sem);
+
+ return i;
+}
+
+static int usbdux_ao_inttrig(comedi_device * dev, comedi_subdevice * s,
+ unsigned int trignum)
+{
+ int ret;
+ usbduxsub_t *this_usbduxsub = dev->private;
+
+ if (!this_usbduxsub) {
+ return -EFAULT;
+ }
+ down(&this_usbduxsub->sem);
+ if (!(this_usbduxsub->probed)) {
+ up(&this_usbduxsub->sem);
+ return -ENODEV;
+ }
+ if (trignum != 0) {
+ printk("comedi%d: usbdux_ao_inttrig: invalid trignum\n",
+ dev->minor);
+ return -EINVAL;
+ }
+ if (!(this_usbduxsub->ao_cmd_running)) {
+ this_usbduxsub->ao_cmd_running = 1;
+ ret = usbduxsub_submit_OutURBs(this_usbduxsub);
+ if (ret < 0) {
+ printk("comedi%d: usbdux_ao_inttrig: submitURB: err=%d\n", dev->minor, ret);
+ this_usbduxsub->ao_cmd_running = 0;
+ up(&this_usbduxsub->sem);
+ return ret;
+ }
+ s->async->inttrig = NULL;
+ } else {
+ printk("comedi%d: ao_inttrig but acqu is already running.\n",
+ dev->minor);
+ }
+ up(&this_usbduxsub->sem);
+ return 1;
+}
+
+static int usbdux_ao_cmdtest(comedi_device * dev,
+ comedi_subdevice * s, comedi_cmd * cmd)
+{
+ int err = 0, tmp;
+ usbduxsub_t *this_usbduxsub = dev->private;
+
+ if (!this_usbduxsub) {
+ return -EFAULT;
+ }
+ if (!(this_usbduxsub->probed)) {
+ return -ENODEV;
+ }
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi%d: usbdux_ao_cmdtest\n", dev->minor);
+#endif
+ /* make sure triggers are valid */
+ // Only immediate triggers are allowed
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW | TRIG_INT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ // trigger should happen timed
+ tmp = cmd->scan_begin_src;
+ // just now we scan also in the high speed mode every frame
+ // this is due to ehci driver limitations
+ if (0) { /* (this_usbduxsub->high_speed) */
+ // start immidiately a new scan
+ // the sampling rate is set by the coversion rate
+ cmd->scan_begin_src &= TRIG_FOLLOW;
+ } else {
+ // start a new scan (output at once) with a timer
+ cmd->scan_begin_src &= TRIG_TIMER;
+ }
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ // scanning is continous
+ tmp = cmd->convert_src;
+ // we always output at 1kHz just now all channels at once
+ if (0) { /* (this_usbduxsub->high_speed) */
+ // in usb-2.0 only one conversion it tranmitted but with 8kHz/n
+ cmd->convert_src &= TRIG_TIMER;
+ } else {
+ // all conversion events happen simultaneously with a rate of 1kHz/n
+ cmd->convert_src &= TRIG_NOW;
+ }
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ // issue a trigger when scan is finished and start a new scan
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ // trigger at the end of count events or not, stop condition or not
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+ /* note that mutual compatiblity is not an issue here */
+ if (cmd->scan_begin_src != TRIG_FOLLOW &&
+ cmd->scan_begin_src != TRIG_EXT &&
+ cmd->scan_begin_src != TRIG_TIMER)
+ err++;
+ if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW) {
+ /* internal trigger */
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+ }
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ /* timer */
+ if (cmd->scan_begin_arg < 1000000) {
+ cmd->scan_begin_arg = 1000000;
+ err++;
+ }
+ }
+ // not used now, is for later use
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (cmd->convert_arg < 125000) {
+ cmd->convert_arg = 125000;
+ err++;
+ }
+ }
+
+ // the same argument
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ /* any count is allowed */
+ } else {
+ /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi%d: err=%d, scan_begin_src=%d, scan_begin_arg=%d, convert_src=%d, convert_arg=%d\n", dev->minor, err, cmd->scan_begin_src, cmd->scan_begin_arg, cmd->convert_src, cmd->convert_arg);
+#endif
+
+ if (err)
+ return 3;
+
+ return 0;
+}
+
+static int usbdux_ao_cmd(comedi_device * dev, comedi_subdevice * s)
+{
+ comedi_cmd *cmd = &s->async->cmd;
+ unsigned int chan, gain;
+ int i, ret;
+ usbduxsub_t *this_usbduxsub = dev->private;
+
+ if (!this_usbduxsub) {
+ return -EFAULT;
+ }
+ down(&this_usbduxsub->sem);
+ if (!(this_usbduxsub->probed)) {
+ up(&this_usbduxsub->sem);
+ return -ENODEV;
+ }
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi%d: usbdux_ao_cmd\n", dev->minor);
+#endif
+
+ // set current channel of the running aquisition to zero
+ s->async->cur_chan = 0;
+ for (i = 0; i < cmd->chanlist_len; ++i) {
+ chan = CR_CHAN(cmd->chanlist[i]);
+ gain = CR_RANGE(cmd->chanlist[i]);
+ if (i >= NUMOUTCHANNELS) {
+ printk("comedi%d: usbdux_ao_cmd: channel list too long\n", dev->minor);
+ break;
+ }
+ this_usbduxsub->dac_commands[i] = (chan << 6);
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi%d: dac command for ch %d is %x\n",
+ dev->minor, i, this_usbduxsub->dac_commands[i]);
+#endif
+ }
+
+ // we count in steps of 1ms (125us)
+ // 125us mode not used yet
+ if (0) { /* (this_usbduxsub->high_speed) */
+ // 125us
+ // timing of the conversion itself: every 125 us
+ this_usbduxsub->ao_timer = cmd->convert_arg / 125000;
+ } else {
+ // 1ms
+ // timing of the scan: we get all channels at once
+ this_usbduxsub->ao_timer = cmd->scan_begin_arg / 1000000;
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi%d: usbdux: scan_begin_src=%d, scan_begin_arg=%d, convert_src=%d, convert_arg=%d\n", dev->minor, cmd->scan_begin_src, cmd->scan_begin_arg, cmd->convert_src, cmd->convert_arg);
+ printk("comedi%d: usbdux: ao_timer=%d (ms)\n",
+ dev->minor, this_usbduxsub->ao_timer);
+#endif
+ if (this_usbduxsub->ao_timer < 1) {
+ printk("comedi%d: usbdux: ao_timer=%d, scan_begin_arg=%d. Not properly tested by cmdtest?\n", dev->minor, this_usbduxsub->ao_timer, cmd->scan_begin_arg);
+ up(&this_usbduxsub->sem);
+ return -EINVAL;
+ }
+ }
+ this_usbduxsub->ao_counter = this_usbduxsub->ao_timer;
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ // not continous
+ // counter
+ // high speed also scans everything at once
+ if (0) { /* (this_usbduxsub->high_speed) */
+ this_usbduxsub->ao_sample_count =
+ (cmd->stop_arg) * (cmd->scan_end_arg);
+ } else {
+ // there's no scan as the scan has been
+ // perf inside the FX2
+ // data arrives as one packet
+ this_usbduxsub->ao_sample_count = cmd->stop_arg;
+ }
+ this_usbduxsub->ao_continous = 0;
+ } else {
+ // continous aquisition
+ this_usbduxsub->ao_continous = 1;
+ this_usbduxsub->ao_sample_count = 0;
+ }
+
+ if (cmd->start_src == TRIG_NOW) {
+ // enable this acquisition operation
+ this_usbduxsub->ao_cmd_running = 1;
+ ret = usbduxsub_submit_OutURBs(this_usbduxsub);
+ if (ret < 0) {
+ this_usbduxsub->ao_cmd_running = 0;
+ // fixme: unlink here??
+ up(&this_usbduxsub->sem);
+ return ret;
+ }
+ s->async->inttrig = NULL;
+ } else {
+ /* TRIG_INT */
+ // submit the urbs later
+ // wait for an internal signal
+ s->async->inttrig = usbdux_ao_inttrig;
+ }
+
+ up(&this_usbduxsub->sem);
+ return 0;
+}
+
+static int usbdux_dio_insn_config(comedi_device * dev,
+ comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
+{
+ int chan = CR_CHAN(insn->chanspec);
+
+ /* The input or output configuration of each digital line is
+ * configured by a special insn_config instruction. chanspec
+ * contains the channel to be changed, and data[0] contains the
+ * value COMEDI_INPUT or COMEDI_OUTPUT. */
+
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ s->io_bits |= 1 << chan; /* 1 means Out */
+ break;
+ case INSN_CONFIG_DIO_INPUT:
+ s->io_bits &= ~(1 << chan);
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] =
+ (s->
+ io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ // we don't tell the firmware here as it would take 8 frames
+ // to submit the information. We do it in the insn_bits.
+ return insn->n;
+}
+
+static int usbdux_dio_insn_bits(comedi_device * dev,
+ comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
+{
+
+ usbduxsub_t *this_usbduxsub = dev->private;
+ int err;
+
+ if (!this_usbduxsub) {
+ return -EFAULT;
+ }
+
+ if (insn->n != 2)
+ return -EINVAL;
+
+ down(&this_usbduxsub->sem);
+
+ if (!(this_usbduxsub->probed)) {
+ up(&this_usbduxsub->sem);
+ return -ENODEV;
+ }
+
+ /* The insn data is a mask in data[0] and the new data
+ * in data[1], each channel cooresponding to a bit. */
+ s->state &= ~data[0];
+ s->state |= data[0] & data[1];
+ this_usbduxsub->dux_commands[1] = s->io_bits;
+ this_usbduxsub->dux_commands[2] = s->state;
+
+ // This command also tells the firmware to return
+ // the digital input lines
+ if ((err = send_dux_commands(this_usbduxsub, SENDDIOBITSCOMMAND)) < 0) {
+ up(&this_usbduxsub->sem);
+ return err;
+ }
+ if ((err = receive_dux_commands(this_usbduxsub,
+ SENDDIOBITSCOMMAND)) < 0) {
+ up(&this_usbduxsub->sem);
+ return err;
+ }
+
+ data[1] = le16_to_cpu(this_usbduxsub->insnBuffer[1]);
+ up(&this_usbduxsub->sem);
+ return 2;
+}
+
+// reads the 4 counters
+// only two are used just now
+static int usbdux_counter_read(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ usbduxsub_t *this_usbduxsub = dev->private;
+ int chan = insn->chanspec;
+ int err;
+
+ if (!this_usbduxsub) {
+ return -EFAULT;
+ }
+
+ down(&this_usbduxsub->sem);
+
+ if (!(this_usbduxsub->probed)) {
+ up(&this_usbduxsub->sem);
+ return -ENODEV;
+ }
+
+ if ((err = send_dux_commands(this_usbduxsub, READCOUNTERCOMMAND)) < 0) {
+ up(&this_usbduxsub->sem);
+ return err;
+ }
+
+ if ((err = receive_dux_commands(this_usbduxsub,
+ READCOUNTERCOMMAND)) < 0) {
+ up(&this_usbduxsub->sem);
+ return err;
+ }
+
+ data[0] = le16_to_cpu(this_usbduxsub->insnBuffer[chan + 1]);
+ up(&this_usbduxsub->sem);
+ return 1;
+}
+
+static int usbdux_counter_write(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ usbduxsub_t *this_usbduxsub = dev->private;
+ int err;
+
+ if (!this_usbduxsub) {
+ return -EFAULT;
+ }
+
+ down(&this_usbduxsub->sem);
+
+ if (!(this_usbduxsub->probed)) {
+ up(&this_usbduxsub->sem);
+ return -ENODEV;
+ }
+
+ this_usbduxsub->dux_commands[1] = insn->chanspec;
+ *((int16_t *) (this_usbduxsub->dux_commands + 2)) = cpu_to_le16(*data);
+
+ if ((err = send_dux_commands(this_usbduxsub, WRITECOUNTERCOMMAND)) < 0) {
+ up(&this_usbduxsub->sem);
+ return err;
+ }
+
+ up(&this_usbduxsub->sem);
+
+ return 1;
+}
+
+static int usbdux_counter_config(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ // nothing to do so far
+ return 2;
+}
+
+/////////////////////////////
+// PWM
+
+static int usbduxsub_unlink_PwmURBs(usbduxsub_t * usbduxsub_tmp)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8)
+ int j = 0;
+#endif
+
+ int err = 0;
+
+ if (usbduxsub_tmp && usbduxsub_tmp->urbPwm) {
+ if (usbduxsub_tmp->urbPwm) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8)
+ j = usb_unlink_urb(usbduxsub_tmp->urbPwm);
+ if (j < err) {
+ err = j;
+ }
+#else
+ usb_kill_urb(usbduxsub_tmp->urbPwm);
+#endif
+ }
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi: usbdux: unlinked PwmURB: res=%d\n", err);
+#endif
+ }
+ return err;
+}
+
+/* This cancels a running acquisition operation
+ * in any context.
+ */
+static int usbdux_pwm_stop(usbduxsub_t * this_usbduxsub, int do_unlink)
+{
+ int ret = 0;
+
+ if (!this_usbduxsub) {
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi?: usbdux_pwm_stop: this_usbduxsub=NULL!\n");
+#endif
+ return -EFAULT;
+ }
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi: usbdux_pwm_cancel\n");
+#endif
+ if (do_unlink) {
+ ret = usbduxsub_unlink_PwmURBs(this_usbduxsub);
+ }
+
+ this_usbduxsub->pwm_cmd_running = 0;
+
+ return ret;
+}
+
+// force unlink
+// is called by comedi
+static int usbdux_pwm_cancel(comedi_device * dev, comedi_subdevice * s)
+{
+ usbduxsub_t *this_usbduxsub = dev->private;
+ int res = 0;
+
+ // unlink only if it is really running
+ res = usbdux_pwm_stop(this_usbduxsub, this_usbduxsub->pwm_cmd_running);
+
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi %d: sending pwm off command to the usb device.\n",
+ dev->minor);
+#endif
+ if ((res = send_dux_commands(this_usbduxsub, SENDPWMOFF)) < 0) {
+ return res;
+ }
+
+ return res;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) || LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
+static void usbduxsub_pwm_irq(struct urb *urb)
+{
+#else
+static void usbduxsub_pwm_irq(struct urb *urb, struct pt_regs *regs)
+{
+#endif
+ int ret;
+ usbduxsub_t *this_usbduxsub;
+ comedi_device *this_comedidev;
+ comedi_subdevice *s;
+
+ // printk("PWM: IRQ\n");
+
+ if (!urb) {
+ printk("comedi_: usbdux_: pwm urb handler called with NULL ptr.\n");
+ return;
+ }
+ // the context variable points to the subdevice
+ this_comedidev = urb->context;
+ if (!this_comedidev) {
+ printk("comedi_: usbdux_: pwm urb int-context is a NULL pointer.\n");
+ return;
+ }
+ // the private structure of the subdevice is usbduxsub_t
+ this_usbduxsub = this_comedidev->private;
+ if (!this_usbduxsub) {
+ printk("comedi_: usbdux_: private data structure of pwm subdev is NULL p.\n");
+ return;
+ }
+
+ s = this_comedidev->subdevices + SUBDEV_DA;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+
+ // after an unlink command, unplug, ... etc
+ // no unlink needed here. Already shutting down.
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -ECONNABORTED:
+ if (this_usbduxsub->pwm_cmd_running) {
+ usbdux_pwm_stop(this_usbduxsub, 0);
+ }
+ return;
+
+ // a real error
+ default:
+ if (this_usbduxsub->pwm_cmd_running) {
+ printk("comedi_: usbdux_: Non-zero urb status received in pwm intr context: %d\n", urb->status);
+ usbdux_pwm_stop(this_usbduxsub, 0);
+ }
+ return;
+ }
+
+ // are we actually running?
+ if (!(this_usbduxsub->pwm_cmd_running)) {
+ return;
+ }
+
+ urb->transfer_buffer_length = this_usbduxsub->sizePwmBuf;
+ urb->dev = this_usbduxsub->usbdev;
+ urb->status = 0;
+ if (this_usbduxsub->pwm_cmd_running) {
+ if ((ret = USB_SUBMIT_URB(urb)) < 0) {
+ printk("comedi_: usbdux_: pwm urb resubm failed in int-cont.");
+ printk("ret=%d", ret);
+ if (ret == EL2NSYNC) {
+ printk("--> buggy USB host controller or bug in IRQ handling!\n");
+ } else {
+ printk("\n");
+ }
+ // don't do an unlink here
+ usbdux_pwm_stop(this_usbduxsub, 0);
+ }
+ }
+}
+
+int usbduxsub_submit_PwmURBs(usbduxsub_t * usbduxsub)
+{
+ int errFlag;
+
+ if (!usbduxsub) {
+ return -EFAULT;
+ }
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi_: usbdux: submitting pwm-urb\n");
+#endif
+ // in case of a resubmission after an unlink...
+
+ usb_fill_bulk_urb(usbduxsub->urbPwm,
+ usbduxsub->usbdev,
+ usb_sndbulkpipe(usbduxsub->usbdev, PWM_EP),
+ usbduxsub->urbPwm->transfer_buffer,
+ usbduxsub->sizePwmBuf, usbduxsub_pwm_irq, usbduxsub->comedidev);
+
+ errFlag = USB_SUBMIT_URB(usbduxsub->urbPwm);
+ if (errFlag) {
+ printk("comedi_: usbdux: pwm: ");
+ printk("USB_SUBMIT_URB");
+ printk(" error %d\n", errFlag);
+ return errFlag;
+ }
+ return 0;
+}
+
+static int usbdux_pwm_period(comedi_device * dev, comedi_subdevice * s,
+ lsampl_t period)
+{
+ usbduxsub_t *this_usbduxsub = dev->private;
+ int fx2delay=255;
+ if (period < MIN_PWM_PERIOD)
+ {
+ printk("comedi%d: illegal period setting for pwm.\n", dev->minor);
+ return -EAGAIN;
+ } else {
+ fx2delay = period / ((int)(6*512*(1.0/0.033))) - 6;
+ if (fx2delay > 255) {
+ printk("comedi%d: period %d for pwm is too low.\n",
+ dev->minor, period);
+ return -EAGAIN;
+ }
+ }
+ this_usbduxsub->pwmDelay=fx2delay;
+ this_usbduxsub->pwmPeriod=period;
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("usbdux_pwm_period: frequ=%d, period=%d\n",period,fx2delay);
+#endif
+ return 0;
+}
+
+
+// is called from insn so there's no need to do all the sanity checks
+static int usbdux_pwm_start(comedi_device * dev, comedi_subdevice * s)
+{
+ int ret, i;
+ usbduxsub_t *this_usbduxsub = dev->private;
+
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi%d: usbdux_pwm_start\n", dev->minor);
+#endif
+ if (this_usbduxsub->pwm_cmd_running) {
+ // already running
+ return 0;
+ }
+
+ this_usbduxsub->dux_commands[1] = ((int8_t) this_usbduxsub->pwmDelay);
+ if ((ret = send_dux_commands(this_usbduxsub, SENDPWMON)) < 0) {
+ return ret;
+ }
+ // initalise the buffer
+ for (i = 0; i < this_usbduxsub->sizePwmBuf; i++) {
+ ((char *)(this_usbduxsub->urbPwm->transfer_buffer))[i] = 0;
+ }
+
+ this_usbduxsub->pwm_cmd_running = 1;
+ ret = usbduxsub_submit_PwmURBs(this_usbduxsub);
+ if (ret < 0) {
+ this_usbduxsub->pwm_cmd_running = 0;
+ return ret;
+ }
+ return 0;
+}
+
+
+// generates the bit pattern for PWM with the optional sign bit
+static int usbdux_pwm_pattern(comedi_device * dev, comedi_subdevice * s,
+ int channel, lsampl_t value, lsampl_t sign)
+{
+ usbduxsub_t *this_usbduxsub = dev->private;
+ int i, szbuf;
+ char *pBuf;
+ char pwm_mask,sgn_mask,c;
+
+ if (!this_usbduxsub) {
+ return -EFAULT;
+ }
+ // this is the DIO bit which carries the PWM data
+ pwm_mask = (1 << channel);
+ // this is the DIO bit which carries the optional direction bit
+ sgn_mask = (16 << channel);
+ // this is the buffer which will be filled with the with bit
+ // pattern for one period
+ szbuf = this_usbduxsub->sizePwmBuf;
+ pBuf = (char *)(this_usbduxsub->urbPwm->transfer_buffer);
+ for (i = 0; i < szbuf; i++) {
+ c = *pBuf;
+ // reset bits
+ c = c & (~pwm_mask);
+ // set the bit as long as the index is lower than the value
+ if (i < value)
+ c = c | pwm_mask;
+ // set the optional sign bit for a relay
+ if (!sign) {
+ // positive value
+ c = c & (~sgn_mask);
+ } else {
+ // negative value
+ c = c | sgn_mask;
+ }
+ *(pBuf++) = c;
+ }
+ return 1;
+}
+
+static int usbdux_pwm_write(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ usbduxsub_t *this_usbduxsub = dev->private;
+
+ if (!this_usbduxsub) {
+ return -EFAULT;
+ }
+
+ if ((insn->n)!=1) {
+ // doesn't make sense to have more than one value here
+ // because it would just overwrite the PWM buffer a couple of times
+ return -EINVAL;
+ }
+
+ // the sign is set via a special INSN only, this gives us 8 bits for
+ // normal operation
+ return usbdux_pwm_pattern(dev,s,
+ CR_CHAN(insn->chanspec),
+ data[0],
+ 0); // relay sign 0 by default
+}
+
+
+static int usbdux_pwm_read(comedi_device * x1, comedi_subdevice * x2,
+ comedi_insn * x3, lsampl_t * x4)
+{
+ // not needed
+ return -EINVAL;
+};
+
+// switches on/off PWM
+static int usbdux_pwm_config(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn * insn, lsampl_t * data)
+{
+ usbduxsub_t *this_usbduxsub = dev->private;
+ switch (data[0]) {
+ case INSN_CONFIG_ARM:
+#ifdef NOISY_DUX_DEBUGBUG
+ // switch it on
+ printk("comedi%d: pwm_insn_config: pwm on\n",
+ dev->minor);
+#endif
+ // if not zero the PWM is limited to a certain time which is
+ // not supported here
+ if (data[1]!=0) {
+ return -EINVAL;
+ }
+ return usbdux_pwm_start(dev, s);
+ case INSN_CONFIG_DISARM:
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi%d: pwm_insn_config: pwm off\n",
+ dev->minor);
+#endif
+ return usbdux_pwm_cancel(dev, s);
+ case INSN_CONFIG_GET_PWM_STATUS:
+ // to check if the USB transmission has failed or in case
+ // PWM was limited to n cycles to check if it has terminated
+ data[1] = this_usbduxsub->pwm_cmd_running;
+ return 0;
+ case INSN_CONFIG_PWM_SET_PERIOD:
+#ifdef NOISY_DUX_DEBUGBUG
+ printk("comedi%d: pwm_insn_config: setting period\n",
+ dev->minor);
+#endif
+ return usbdux_pwm_period(dev,s,data[1]);
+ case INSN_CONFIG_PWM_GET_PERIOD:
+ data[1] = this_usbduxsub->pwmPeriod;
+ return 0;
+ case INSN_CONFIG_PWM_SET_H_BRIDGE:
+ // value in the first byte and the sign in the second for a relay
+ return usbdux_pwm_pattern(dev, s,
+ CR_CHAN(insn->chanspec), // the channel number
+ data[1], // actual PWM data
+ (data[2]!=0)); // just a sign
+ case INSN_CONFIG_PWM_GET_H_BRIDGE:
+ // values are not kept in this driver, nothing to return here
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+// end of PWM
+///////////////////////////////////////////////////////////////////
+
+static void tidy_up(usbduxsub_t * usbduxsub_tmp)
+{
+ int i;
+
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi_: usbdux: tiding up\n");
+#endif
+ if (!usbduxsub_tmp) {
+ return;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ // shows the usb subsystem that the driver is down
+ if (usbduxsub_tmp->interface) {
+ usb_set_intfdata(usbduxsub_tmp->interface, NULL);
+ }
+#endif
+
+ usbduxsub_tmp->probed = 0;
+
+ if (usbduxsub_tmp->urbIn) {
+ if (usbduxsub_tmp->ai_cmd_running) {
+ usbduxsub_tmp->ai_cmd_running = 0;
+ usbduxsub_unlink_InURBs(usbduxsub_tmp);
+ }
+ for (i = 0; i < usbduxsub_tmp->numOfInBuffers; i++) {
+ if (usbduxsub_tmp->urbIn[i]->transfer_buffer) {
+ kfree(usbduxsub_tmp->urbIn[i]->transfer_buffer);
+ usbduxsub_tmp->urbIn[i]->transfer_buffer = NULL;
+ }
+ if (usbduxsub_tmp->urbIn[i]) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,8)
+ usb_kill_urb(usbduxsub_tmp->urbIn[i]);
+#endif
+ usb_free_urb(usbduxsub_tmp->urbIn[i]);
+ usbduxsub_tmp->urbIn[i] = NULL;
+ }
+ }
+ kfree(usbduxsub_tmp->urbIn);
+ usbduxsub_tmp->urbIn = NULL;
+ }
+ if (usbduxsub_tmp->urbOut) {
+ if (usbduxsub_tmp->ao_cmd_running) {
+ usbduxsub_tmp->ao_cmd_running = 0;
+ usbduxsub_unlink_OutURBs(usbduxsub_tmp);
+ }
+ for (i = 0; i < usbduxsub_tmp->numOfOutBuffers; i++) {
+ if (usbduxsub_tmp->urbOut[i]->transfer_buffer) {
+ kfree(usbduxsub_tmp->urbOut[i]->
+ transfer_buffer);
+ usbduxsub_tmp->urbOut[i]->transfer_buffer =
+ NULL;
+ }
+ if (usbduxsub_tmp->urbOut[i]) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,8)
+ usb_kill_urb(usbduxsub_tmp->urbOut[i]);
+#endif
+ usb_free_urb(usbduxsub_tmp->urbOut[i]);
+ usbduxsub_tmp->urbOut[i] = NULL;
+ }
+ }
+ kfree(usbduxsub_tmp->urbOut);
+ usbduxsub_tmp->urbOut = NULL;
+ }
+ if (usbduxsub_tmp->urbPwm) {
+ if (usbduxsub_tmp->pwm_cmd_running) {
+ usbduxsub_tmp->pwm_cmd_running = 0;
+ usbduxsub_unlink_PwmURBs(usbduxsub_tmp);
+ }
+ if (usbduxsub_tmp->urbPwm->transfer_buffer) {
+ kfree(usbduxsub_tmp->urbPwm->transfer_buffer);
+ usbduxsub_tmp->urbPwm->transfer_buffer = NULL;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,8)
+ usb_kill_urb(usbduxsub_tmp->urbPwm);
+#endif
+ usb_free_urb(usbduxsub_tmp->urbPwm);
+ usbduxsub_tmp->urbPwm = NULL;
+ }
+ if (usbduxsub_tmp->inBuffer) {
+ kfree(usbduxsub_tmp->inBuffer);
+ usbduxsub_tmp->inBuffer = NULL;
+ }
+ if (usbduxsub_tmp->insnBuffer) {
+ kfree(usbduxsub_tmp->insnBuffer);
+ usbduxsub_tmp->insnBuffer = NULL;
+ }
+ if (usbduxsub_tmp->inBuffer) {
+ kfree(usbduxsub_tmp->inBuffer);
+ usbduxsub_tmp->inBuffer = NULL;
+ }
+ if (usbduxsub_tmp->dac_commands) {
+ kfree(usbduxsub_tmp->dac_commands);
+ usbduxsub_tmp->dac_commands = NULL;
+ }
+ if (usbduxsub_tmp->dux_commands) {
+ kfree(usbduxsub_tmp->dux_commands);
+ usbduxsub_tmp->dux_commands = NULL;
+ }
+ usbduxsub_tmp->ai_cmd_running = 0;
+ usbduxsub_tmp->ao_cmd_running = 0;
+ usbduxsub_tmp->pwm_cmd_running = 0;
+}
+
+static unsigned hex2unsigned(char *h)
+{
+ unsigned hi, lo;
+ if (h[0] > '9') {
+ hi = h[0] - 'A' + 0x0a;
+ } else {
+ hi = h[0] - '0';
+ }
+ if (h[1] > '9') {
+ lo = h[1] - 'A' + 0x0a;
+ } else {
+ lo = h[1] - '0';
+ }
+ return hi * 0x10 + lo;
+}
+
+// for FX2
+#define FIRMWARE_MAX_LEN 0x2000
+
+// taken from David Brownell's fxload and adjusted for this driver
+static int read_firmware(usbduxsub_t * usbduxsub, void *firmwarePtr, long size)
+{
+ int i = 0;
+ unsigned char *fp = (char *)firmwarePtr;
+ unsigned char *firmwareBinary = NULL;
+ int res = 0;
+ int maxAddr = 0;
+
+ firmwareBinary = kzalloc(FIRMWARE_MAX_LEN, GFP_KERNEL);
+ if (!firmwareBinary) {
+ printk("comedi_: usbdux: mem alloc for firmware failed\n");
+ return -ENOMEM;
+ }
+
+ for (;;) {
+ char buf[256], *cp;
+ char type;
+ int len;
+ int idx, off;
+ int j = 0;
+
+ // get one line
+ while ((i < size) && (fp[i] != 13) && (fp[i] != 10)) {
+ buf[j] = fp[i];
+ i++;
+ j++;
+ if (j >= sizeof(buf)) {
+ printk("comedi_: usbdux: bogus firmware file!\n");
+ return -1;
+ }
+ }
+ // get rid of LF/CR/...
+ while ((i < size) && ((fp[i] == 13) || (fp[i] == 10)
+ || (fp[i] == 0))) {
+ i++;
+ }
+
+ buf[j] = 0;
+ //printk("comedi_: buf=%s\n",buf);
+
+ /* EXTENSION: "# comment-till-end-of-line", for copyrights etc */
+ if (buf[0] == '#')
+ continue;
+
+ if (buf[0] != ':') {
+ printk("comedi_: usbdux: upload: not an ihex record: %s", buf);
+ return -EFAULT;
+ }
+
+ /* Read the length field (up to 16 bytes) */
+ len = hex2unsigned(buf + 1);
+
+ /* Read the target offset */
+ off = (hex2unsigned(buf + 3) * 0x0100) + hex2unsigned(buf + 5);
+
+ if ((off + len) > maxAddr) {
+ maxAddr = off + len;
+ }
+
+ if (maxAddr >= FIRMWARE_MAX_LEN) {
+ printk("comedi_: usbdux: firmware upload goes beyond FX2 RAM boundaries.");
+ return -EFAULT;
+ }
+ //printk("comedi_: usbdux: off=%x, len=%x:",off,len);
+
+ /* Read the record type */
+ type = hex2unsigned(buf + 7);
+
+ /* If this is an EOF record, then make it so. */
+ if (type == 1) {
+ break;
+ }
+
+ if (type != 0) {
+ printk("comedi_: usbdux: unsupported record type: %u\n",
+ type);
+ return -EFAULT;
+ }
+
+ for (idx = 0, cp = buf + 9; idx < len; idx += 1, cp += 2) {
+ firmwareBinary[idx + off] = hex2unsigned(cp);
+ //printk("%02x ",firmwareBinary[idx+off]);
+ }
+ //printk("\n");
+
+ if (i >= size) {
+ printk("comedi_: usbdux: unexpected end of hex file\n");
+ break;
+ }
+
+ }
+ res = firmwareUpload(usbduxsub, firmwareBinary, maxAddr + 1);
+ kfree(firmwareBinary);
+ return res;
+}
+
+// allocate memory for the urbs and initialise them
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+static void *usbduxsub_probe(struct usb_device *udev,
+ unsigned int interfnum, const struct usb_device_id *id)
+{
+#else
+static int usbduxsub_probe(struct usb_interface *uinterf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(uinterf);
+#endif
+ int i;
+ int index;
+
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi_: usbdux_: finding a free structure for the usb-device\n");
+#endif
+ down(&start_stop_sem);
+ // look for a free place in the usbdux array
+ index = -1;
+ for (i = 0; i < NUMUSBDUX; i++) {
+ if (!(usbduxsub[i].probed)) {
+ index = i;
+ break;
+ }
+ }
+
+ // no more space
+ if (index == -1) {
+ printk("Too many usbdux-devices connected.\n");
+ up(&start_stop_sem);
+ return PROBE_ERR_RETURN(-EMFILE);
+ }
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi_: usbdux: usbduxsub[%d] is ready to connect to comedi.\n", index);
+#endif
+
+ init_MUTEX(&(usbduxsub[index].sem));
+ // save a pointer to the usb device
+ usbduxsub[index].usbdev = udev;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ // save the interface number
+ usbduxsub[index].ifnum = interfnum;
+#else
+ // 2.6: save the interface itself
+ usbduxsub[index].interface = uinterf;
+ // get the interface number from the interface
+ usbduxsub[index].ifnum = uinterf->altsetting->desc.bInterfaceNumber;
+ // hand the private data over to the usb subsystem
+ // will be needed for disconnect
+ usb_set_intfdata(uinterf, &(usbduxsub[index]));
+#endif
+
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi_: usbdux: ifnum=%d\n", usbduxsub[index].ifnum);
+#endif
+ // test if it is high speed (USB 2.0)
+ usbduxsub[index].high_speed =
+ (usbduxsub[index].usbdev->speed == USB_SPEED_HIGH);
+
+ // create space for the commands of the DA converter
+ usbduxsub[index].dac_commands = kzalloc(NUMOUTCHANNELS, GFP_KERNEL);
+ if (!usbduxsub[index].dac_commands) {
+ printk("comedi_: usbdux: error alloc space for dac commands\n");
+ tidy_up(&(usbduxsub[index]));
+ up(&start_stop_sem);
+ return PROBE_ERR_RETURN(-ENOMEM);
+ }
+ // create space for the commands going to the usb device
+ usbduxsub[index].dux_commands = kzalloc(SIZEOFDUXBUFFER, GFP_KERNEL);
+ if (!usbduxsub[index].dux_commands) {
+ printk("comedi_: usbdux: error alloc space for dac commands\n");
+ tidy_up(&(usbduxsub[index]));
+ up(&start_stop_sem);
+ return PROBE_ERR_RETURN(-ENOMEM);
+ }
+ // create space for the in buffer and set it to zero
+ usbduxsub[index].inBuffer = kzalloc(SIZEINBUF, GFP_KERNEL);
+ if (!(usbduxsub[index].inBuffer)) {
+ printk("comedi_: usbdux: could not alloc space for inBuffer\n");
+ tidy_up(&(usbduxsub[index]));
+ up(&start_stop_sem);
+ return PROBE_ERR_RETURN(-ENOMEM);
+ }
+ // create space of the instruction buffer
+ usbduxsub[index].insnBuffer = kzalloc(SIZEINSNBUF, GFP_KERNEL);
+ if (!(usbduxsub[index].insnBuffer)) {
+ printk("comedi_: usbdux: could not alloc space for insnBuffer\n");
+ tidy_up(&(usbduxsub[index]));
+ up(&start_stop_sem);
+ return PROBE_ERR_RETURN(-ENOMEM);
+ }
+ // create space for the outbuffer
+ usbduxsub[index].outBuffer = kzalloc(SIZEOUTBUF, GFP_KERNEL);
+ if (!(usbduxsub[index].outBuffer)) {
+ printk("comedi_: usbdux: could not alloc space for outBuffer\n");
+ tidy_up(&(usbduxsub[index]));
+ up(&start_stop_sem);
+ return PROBE_ERR_RETURN(-ENOMEM);
+ }
+ // setting to alternate setting 3: enabling iso ep and bulk ep.
+ i = usb_set_interface(usbduxsub[index].usbdev,
+ usbduxsub[index].ifnum, 3);
+ if (i < 0) {
+ printk("comedi_: usbdux%d: could not set alternate setting 3 in high speed.\n", index);
+ tidy_up(&(usbduxsub[index]));
+ up(&start_stop_sem);
+ return PROBE_ERR_RETURN(-ENODEV);
+ }
+ if (usbduxsub[index].high_speed) {
+ usbduxsub[index].numOfInBuffers = NUMOFINBUFFERSHIGH;
+ } else {
+ usbduxsub[index].numOfInBuffers = NUMOFINBUFFERSFULL;
+ }
+ usbduxsub[index].urbIn =
+ kzalloc(sizeof(struct urb *) * usbduxsub[index].numOfInBuffers,
+ GFP_KERNEL);
+ if (!(usbduxsub[index].urbIn)) {
+ printk("comedi_: usbdux: Could not alloc. urbIn array\n");
+ tidy_up(&(usbduxsub[index]));
+ up(&start_stop_sem);
+ return PROBE_ERR_RETURN(-ENOMEM);
+ }
+ for (i = 0; i < usbduxsub[index].numOfInBuffers; i++) {
+ // one frame: 1ms
+ usbduxsub[index].urbIn[i] = USB_ALLOC_URB(1);
+ if (usbduxsub[index].urbIn[i] == NULL) {
+ printk("comedi_: usbdux%d: Could not alloc. urb(%d)\n",
+ index, i);
+ tidy_up(&(usbduxsub[index]));
+ up(&start_stop_sem);
+ return PROBE_ERR_RETURN(-ENOMEM);
+ }
+ usbduxsub[index].urbIn[i]->dev = usbduxsub[index].usbdev;
+ // will be filled later with a pointer to the comedi-device
+ // and ONLY then the urb should be submitted
+ usbduxsub[index].urbIn[i]->context = NULL;
+ usbduxsub[index].urbIn[i]->pipe =
+ usb_rcvisocpipe(usbduxsub[index].usbdev, ISOINEP);
+ usbduxsub[index].urbIn[i]->transfer_flags = URB_ISO_ASAP;
+ usbduxsub[index].urbIn[i]->transfer_buffer =
+ kzalloc(SIZEINBUF, GFP_KERNEL);
+ if (!(usbduxsub[index].urbIn[i]->transfer_buffer)) {
+ printk("comedi_: usbdux%d: could not alloc. transb.\n",
+ index);
+ tidy_up(&(usbduxsub[index]));
+ up(&start_stop_sem);
+ return PROBE_ERR_RETURN(-ENOMEM);
+ }
+ usbduxsub[index].urbIn[i]->complete = usbduxsub_ai_IsocIrq;
+ usbduxsub[index].urbIn[i]->number_of_packets = 1;
+ usbduxsub[index].urbIn[i]->transfer_buffer_length = SIZEINBUF;
+ usbduxsub[index].urbIn[i]->iso_frame_desc[0].offset = 0;
+ usbduxsub[index].urbIn[i]->iso_frame_desc[0].length = SIZEINBUF;
+ }
+
+ // out
+ if (usbduxsub[index].high_speed) {
+ usbduxsub[index].numOfOutBuffers = NUMOFOUTBUFFERSHIGH;
+ } else {
+ usbduxsub[index].numOfOutBuffers = NUMOFOUTBUFFERSFULL;
+ }
+ usbduxsub[index].urbOut =
+ kzalloc(sizeof(struct urb *) * usbduxsub[index].numOfOutBuffers,
+ GFP_KERNEL);
+ if (!(usbduxsub[index].urbOut)) {
+ printk("comedi_: usbdux: Could not alloc. urbOut array\n");
+ tidy_up(&(usbduxsub[index]));
+ up(&start_stop_sem);
+ return PROBE_ERR_RETURN(-ENOMEM);
+ }
+ for (i = 0; i < usbduxsub[index].numOfOutBuffers; i++) {
+ // one frame: 1ms
+ usbduxsub[index].urbOut[i] = USB_ALLOC_URB(1);
+ if (usbduxsub[index].urbOut[i] == NULL) {
+ printk("comedi_: usbdux%d: Could not alloc. urb(%d)\n",
+ index, i);
+ tidy_up(&(usbduxsub[index]));
+ up(&start_stop_sem);
+ return PROBE_ERR_RETURN(-ENOMEM);
+ }
+ usbduxsub[index].urbOut[i]->dev = usbduxsub[index].usbdev;
+ // will be filled later with a pointer to the comedi-device
+ // and ONLY then the urb should be submitted
+ usbduxsub[index].urbOut[i]->context = NULL;
+ usbduxsub[index].urbOut[i]->pipe =
+ usb_sndisocpipe(usbduxsub[index].usbdev, ISOOUTEP);
+ usbduxsub[index].urbOut[i]->transfer_flags = URB_ISO_ASAP;
+ usbduxsub[index].urbOut[i]->transfer_buffer =
+ kzalloc(SIZEOUTBUF, GFP_KERNEL);
+ if (!(usbduxsub[index].urbOut[i]->transfer_buffer)) {
+ printk("comedi_: usbdux%d: could not alloc. transb.\n",
+ index);
+ tidy_up(&(usbduxsub[index]));
+ up(&start_stop_sem);
+ return PROBE_ERR_RETURN(-ENOMEM);
+ }
+ usbduxsub[index].urbOut[i]->complete = usbduxsub_ao_IsocIrq;
+ usbduxsub[index].urbOut[i]->number_of_packets = 1;
+ usbduxsub[index].urbOut[i]->transfer_buffer_length = SIZEOUTBUF;
+ usbduxsub[index].urbOut[i]->iso_frame_desc[0].offset = 0;
+ usbduxsub[index].urbOut[i]->iso_frame_desc[0].length =
+ SIZEOUTBUF;
+ if (usbduxsub[index].high_speed) {
+ // uframes
+ usbduxsub[index].urbOut[i]->interval = 8;
+ } else {
+ // frames
+ usbduxsub[index].urbOut[i]->interval = 1;
+ }
+ }
+
+ // pwm
+ if (usbduxsub[index].high_speed) {
+ usbduxsub[index].sizePwmBuf = 512; // max bulk ep size in high speed
+ usbduxsub[index].urbPwm = USB_ALLOC_URB(0);
+ if (usbduxsub[index].urbPwm == NULL) {
+ printk("comedi_: usbdux%d: Could not alloc. pwm urb\n",
+ index);
+ tidy_up(&(usbduxsub[index]));
+ up(&start_stop_sem);
+ return PROBE_ERR_RETURN(-ENOMEM);
+ }
+ usbduxsub[index].urbPwm->transfer_buffer =
+ kzalloc(usbduxsub[index].sizePwmBuf, GFP_KERNEL);
+ if (!(usbduxsub[index].urbPwm->transfer_buffer)) {
+ printk("comedi_: usbdux%d: could not alloc. transb. for pwm\n", index);
+ tidy_up(&(usbduxsub[index]));
+ up(&start_stop_sem);
+ return PROBE_ERR_RETURN(-ENOMEM);
+ }
+ } else {
+ usbduxsub[index].urbPwm = NULL;
+ usbduxsub[index].sizePwmBuf = 0;
+ }
+
+ usbduxsub[index].ai_cmd_running = 0;
+ usbduxsub[index].ao_cmd_running = 0;
+ usbduxsub[index].pwm_cmd_running = 0;
+
+ // we've reached the bottom of the function
+ usbduxsub[index].probed = 1;
+ up(&start_stop_sem);
+ printk("comedi_: usbdux%d has been successfully initialised.\n", index);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ return (void *)(&usbduxsub[index]);
+#else
+ // success
+ return 0;
+#endif
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+static void usbduxsub_disconnect(struct usb_device *udev, void *ptr)
+{
+ usbduxsub_t *usbduxsub_tmp = (usbduxsub_t *) ptr;
+#else
+static void usbduxsub_disconnect(struct usb_interface *intf)
+{
+ usbduxsub_t *usbduxsub_tmp = usb_get_intfdata(intf);
+ struct usb_device *udev = interface_to_usbdev(intf);
+#endif
+ if (!usbduxsub_tmp) {
+ printk("comedi_: usbdux: disconnect called with null pointer.\n");
+ return;
+ }
+ if (usbduxsub_tmp->usbdev != udev) {
+ printk("comedi_: usbdux: BUG! called with wrong ptr!!!\n");
+ return;
+ }
+ down(&start_stop_sem);
+ down(&usbduxsub_tmp->sem);
+ tidy_up(usbduxsub_tmp);
+ up(&usbduxsub_tmp->sem);
+ up(&start_stop_sem);
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi_: usbdux: disconnected from the usb\n");
+#endif
+}
+
+// is called when comedi-config is called
+static int usbdux_attach(comedi_device * dev, comedi_devconfig * it)
+{
+ int ret;
+ int index;
+ int i;
+ comedi_subdevice *s = NULL;
+ dev->private = NULL;
+
+ down(&start_stop_sem);
+ // find a valid device which has been detected by the probe function of the usb
+ index = -1;
+ for (i = 0; i < NUMUSBDUX; i++) {
+ if ((usbduxsub[i].probed) && (!usbduxsub[i].attached)) {
+ index = i;
+ break;
+ }
+ }
+
+ if (index < 0) {
+ printk("comedi%d: usbdux: error: attach failed, no usbdux devs connected to the usb bus.\n", dev->minor);
+ up(&start_stop_sem);
+ return -ENODEV;
+ }
+
+ down(&(usbduxsub[index].sem));
+ // pointer back to the corresponding comedi device
+ usbduxsub[index].comedidev = dev;
+
+ // trying to upload the firmware into the chip
+ if (comedi_aux_data(it->options, 0) &&
+ it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH]) {
+ read_firmware(usbduxsub + index,
+ comedi_aux_data(it->options, 0),
+ it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH]);
+ }
+
+ dev->board_name = BOARDNAME;
+
+ /* set number of subdevices */
+ if (usbduxsub[index].high_speed) {
+ // with pwm
+ dev->n_subdevices = 5;
+ } else {
+ // without pwm
+ dev->n_subdevices = 4;
+ }
+
+ // allocate space for the subdevices
+ if ((ret = alloc_subdevices(dev, dev->n_subdevices)) < 0) {
+ printk("comedi%d: usbdux: error alloc space for subdev\n",
+ dev->minor);
+ up(&start_stop_sem);
+ return ret;
+ }
+
+ printk("comedi%d: usbdux: usb-device %d is attached to comedi.\n",
+ dev->minor, index);
+ // private structure is also simply the usb-structure
+ dev->private = usbduxsub + index;
+
+ // the first subdevice is the A/D converter
+ s = dev->subdevices + SUBDEV_AD;
+ // the URBs get the comedi subdevice
+ // which is responsible for reading
+ // this is the subdevice which reads data
+ dev->read_subdev = s;
+ // the subdevice receives as private structure the
+ // usb-structure
+ s->private = NULL;
+ // analog input
+ s->type = COMEDI_SUBD_AI;
+ // readable and ref is to ground
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
+ // 8 channels
+ s->n_chan = 8;
+ // length of the channellist
+ s->len_chanlist = 8;
+ // callback functions
+ s->insn_read = usbdux_ai_insn_read;
+ s->do_cmdtest = usbdux_ai_cmdtest;
+ s->do_cmd = usbdux_ai_cmd;
+ s->cancel = usbdux_ai_cancel;
+ // max value from the A/D converter (12bit)
+ s->maxdata = 0xfff;
+ // range table to convert to physical units
+ s->range_table = (&range_usbdux_ai_range);
+ //
+
+ // analog out
+ s = dev->subdevices + SUBDEV_DA;
+ // analog out
+ s->type = COMEDI_SUBD_AO;
+ // backward pointer
+ dev->write_subdev = s;
+ // the subdevice receives as private structure the
+ // usb-structure
+ s->private = NULL;
+ // are writable
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_CMD_WRITE;
+ // 4 channels
+ s->n_chan = 4;
+ // length of the channellist
+ s->len_chanlist = 4;
+ // 12 bit resolution
+ s->maxdata = 0x0fff;
+ // bipolar range
+ s->range_table = (&range_usbdux_ao_range);
+ // callback
+ s->do_cmdtest = usbdux_ao_cmdtest;
+ s->do_cmd = usbdux_ao_cmd;
+ s->cancel = usbdux_ao_cancel;
+ s->insn_read = usbdux_ao_insn_read;
+ s->insn_write = usbdux_ao_insn_write;
+
+ // digital I/O
+ s = dev->subdevices + SUBDEV_DIO;
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = (&range_digital);
+ s->insn_bits = usbdux_dio_insn_bits;
+ s->insn_config = usbdux_dio_insn_config;
+ // we don't use it
+ s->private = NULL;
+
+ //counter
+ s = dev->subdevices + SUBDEV_COUNTER;
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = 4;
+ s->maxdata = 0xFFFF;
+ s->insn_read = usbdux_counter_read;
+ s->insn_write = usbdux_counter_write;
+ s->insn_config = usbdux_counter_config;
+
+ if (usbduxsub[index].high_speed) {
+ //timer / pwm
+ s = dev->subdevices + SUBDEV_PWM;
+ s->type = COMEDI_SUBD_PWM;
+ s->subdev_flags = SDF_WRITABLE | SDF_PWM_HBRIDGE;
+ s->n_chan = 8;
+ // this defines the max duty cycle resolution
+ s->maxdata = usbduxsub[index].sizePwmBuf;
+ s->insn_write = usbdux_pwm_write;
+ s->insn_read = usbdux_pwm_read;
+ s->insn_config = usbdux_pwm_config;
+ usbdux_pwm_period(dev, s, PWM_DEFAULT_PERIOD);
+ }
+ // finally decide that it's attached
+ usbduxsub[index].attached = 1;
+
+ up(&(usbduxsub[index].sem));
+
+ up(&start_stop_sem);
+
+ printk("comedi%d: attached to usbdux.\n", dev->minor);
+
+ return 0;
+}
+
+static int usbdux_detach(comedi_device * dev)
+{
+ usbduxsub_t *usbduxsub_tmp;
+
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi%d: usbdux: detach usb device\n", dev->minor);
+#endif
+
+ if (!dev) {
+ printk("comedi?: usbdux: detach without dev variable...\n");
+ return -EFAULT;
+ }
+
+ usbduxsub_tmp = dev->private;
+ if (!usbduxsub_tmp) {
+ printk("comedi?: usbdux: detach without ptr to usbduxsub[]\n");
+ return -EFAULT;
+ }
+
+ down(&usbduxsub_tmp->sem);
+ // Don't allow detach to free the private structure
+ // It's one entry of of usbduxsub[]
+ dev->private = NULL;
+ usbduxsub_tmp->attached = 0;
+ usbduxsub_tmp->comedidev = NULL;
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi%d: usbdux: detach: successfully removed\n", dev->minor);
+#endif
+ up(&usbduxsub_tmp->sem);
+ return 0;
+}
+
+/* main driver struct */
+static comedi_driver driver_usbdux = {
+ driver_name:"usbdux",
+ module:THIS_MODULE,
+ attach:usbdux_attach,
+ detach:usbdux_detach,
+};
+
+static void init_usb_devices(void)
+{
+ int index;
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi_: usbdux: setting all possible devs to invalid\n");
+#endif
+ // all devices entries are invalid to begin with
+ // they will become valid by the probe function
+ // and then finally by the attach-function
+ for (index = 0; index < NUMUSBDUX; index++) {
+ memset(&(usbduxsub[index]), 0x00, sizeof(usbduxsub[index]));
+ init_MUTEX(&(usbduxsub[index].sem));
+ }
+}
+
+// Table with the USB-devices: just now only testing IDs
+static struct usb_device_id usbduxsub_table[] = {
+ {USB_DEVICE(0x13d8, 0x0001),
+ },
+ {USB_DEVICE(0x13d8, 0x0002)
+ },
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, usbduxsub_table);
+
+// The usbduxsub-driver
+static struct usb_driver usbduxsub_driver = {
+#ifdef COMEDI_HAVE_USB_DRIVER_OWNER
+ owner:THIS_MODULE,
+#endif
+ name:BOARDNAME,
+ probe:usbduxsub_probe,
+ disconnect:usbduxsub_disconnect,
+ id_table:usbduxsub_table,
+};
+
+// Can't use the nice macro as I have also to initialise the USB
+// subsystem:
+// registering the usb-system _and_ the comedi-driver
+static int init_usbdux(void)
+{
+ printk(KERN_INFO KBUILD_MODNAME ": "
+ DRIVER_VERSION ":" DRIVER_DESC "\n");
+ init_usb_devices();
+ usb_register(&usbduxsub_driver);
+ comedi_driver_register(&driver_usbdux);
+ return 0;
+}
+
+// deregistering the comedi driver and the usb-subsystem
+static void exit_usbdux(void)
+{
+ comedi_driver_unregister(&driver_usbdux);
+ usb_deregister(&usbduxsub_driver);
+}
+
+module_init(init_usbdux);
+module_exit(exit_usbdux);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/usbduxfast.c b/drivers/staging/comedi/drivers/usbduxfast.c
new file mode 100644
index 000000000000..403a614000b1
--- /dev/null
+++ b/drivers/staging/comedi/drivers/usbduxfast.c
@@ -0,0 +1,1779 @@
+#define DRIVER_VERSION "v0.99a"
+#define DRIVER_AUTHOR "Bernd Porr, BerndPorr@f2s.com"
+#define DRIVER_DESC "USB-DUXfast, BerndPorr@f2s.com"
+/*
+ comedi/drivers/usbduxfast.c
+ Copyright (C) 2004 Bernd Porr, Bernd.Porr@f2s.com
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+Driver: usbduxfast
+Description: ITL USB-DUXfast
+Devices: [ITL] USB-DUX (usbduxfast.o)
+Author: Bernd Porr <BerndPorr@f2s.com>
+Updated: 04 Dec 2006
+Status: testing
+*/
+
+/*
+ * I must give credit here to Chris Baugher who
+ * wrote the driver for AT-MIO-16d. I used some parts of this
+ * driver. I also must give credits to David Brownell
+ * who supported me with the USB development.
+ *
+ * Bernd Porr
+ *
+ *
+ * Revision history:
+ * 0.9: Dropping the first data packet which seems to be from the last transfer.
+ * Buffer overflows in the FX2 are handed over to comedi.
+ * 0.92: Dropping now 4 packets. The quad buffer has to be emptied.
+ * Added insn command basically for testing. Sample rate is 1MHz/16ch=62.5kHz
+ * 0.99: Ian Abbott pointed out a bug which has been corrected. Thanks!
+ * 0.99a: added external trigger.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/usb.h>
+#include <linux/smp_lock.h>
+#include <linux/fcntl.h>
+#include <linux/compiler.h>
+#include "comedi_fc.h"
+#include "../comedidev.h"
+#include "../usb.h"
+
+// (un)comment this if you want to have debug info.
+//#define CONFIG_COMEDI_DEBUG
+#undef CONFIG_COMEDI_DEBUG
+
+#define BOARDNAME "usbduxfast"
+
+// timeout for the USB-transfer
+#define EZTIMEOUT 30
+
+// constants for "firmware" upload and download
+#define USBDUXFASTSUB_FIRMWARE 0xA0
+#define VENDOR_DIR_IN 0xC0
+#define VENDOR_DIR_OUT 0x40
+
+// internal adresses of the 8051 processor
+#define USBDUXFASTSUB_CPUCS 0xE600
+
+// max lenghth of the transfer-buffer for software upload
+#define TB_LEN 0x2000
+
+// Input endpoint number
+#define BULKINEP 6
+
+// Endpoint for the A/D channellist: bulk OUT
+#define CHANNELLISTEP 4
+
+// Number of channels
+#define NUMCHANNELS 32
+
+// size of the waveform descriptor
+#define WAVESIZE 0x20
+
+// Size of one A/D value
+#define SIZEADIN ((sizeof(int16_t)))
+
+// Size of the input-buffer IN BYTES
+#define SIZEINBUF 512
+
+// 16 bytes.
+#define SIZEINSNBUF 512
+
+// Size of the buffer for the dux commands
+#define SIZEOFDUXBUFFER 256 // bytes
+
+// Number of in-URBs which receive the data: min=5
+#define NUMOFINBUFFERSHIGH 10
+
+// Total number of usbduxfast devices
+#define NUMUSBDUXFAST 16
+
+// Number of subdevices
+#define N_SUBDEVICES 1
+
+// Analogue in subdevice
+#define SUBDEV_AD 0
+
+// min delay steps for more than one channel
+// basically when the mux gives up. ;-)
+#define MIN_SAMPLING_PERIOD 9 // steps at 30MHz in the FX2
+
+// Max number of 1/30MHz delay steps:
+#define MAX_SAMPLING_PERIOD 500
+
+// Number of received packets to ignore before we start handing data over to comedi.
+// It's quad buffering and we have to ignore 4 packets.
+#define PACKETS_TO_IGNORE 4
+
+/////////////////////////////////////////////
+// comedi constants
+static const comedi_lrange range_usbduxfast_ai_range = { 2, {
+ BIP_RANGE(0.75),
+ BIP_RANGE(0.5),
+ }
+};
+
+/*
+ * private structure of one subdevice
+ */
+
+// This is the structure which holds all the data of this driver
+// one sub device just now: A/D
+typedef struct {
+ // attached?
+ int attached;
+ // is it associated with a subdevice?
+ int probed;
+ // pointer to the usb-device
+ struct usb_device *usbdev;
+ // BULK-transfer handling: urb
+ struct urb *urbIn;
+ int8_t *transfer_buffer;
+ // input buffer for single insn
+ int16_t *insnBuffer;
+ // interface number
+ int ifnum;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ // interface structure in 2.6
+ struct usb_interface *interface;
+#endif
+ // comedi device for the interrupt context
+ comedi_device *comedidev;
+ // asynchronous command is running
+ short int ai_cmd_running;
+ // continous aquisition
+ short int ai_continous;
+ // number of samples to aquire
+ long int ai_sample_count;
+ // commands
+ uint8_t *dux_commands;
+ // counter which ignores the first buffers
+ int ignore;
+ struct semaphore sem;
+} usbduxfastsub_t;
+
+// The pointer to the private usb-data of the driver
+// is also the private data for the comedi-device.
+// This has to be global as the usb subsystem needs
+// global variables. The other reason is that this
+// structure must be there _before_ any comedi
+// command is issued. The usb subsystem must be
+// initialised before comedi can access it.
+static usbduxfastsub_t usbduxfastsub[NUMUSBDUXFAST];
+
+static DECLARE_MUTEX(start_stop_sem);
+
+// bulk transfers to usbduxfast
+
+#define SENDADCOMMANDS 0
+#define SENDINITEP6 1
+
+static int send_dux_commands(usbduxfastsub_t * this_usbduxfastsub, int cmd_type)
+{
+ int result, nsent;
+ this_usbduxfastsub->dux_commands[0] = cmd_type;
+#ifdef CONFIG_COMEDI_DEBUG
+ int i;
+ printk("comedi%d: usbduxfast: dux_commands: ",
+ this_usbduxfastsub->comedidev->minor);
+ for (i = 0; i < SIZEOFDUXBUFFER; i++) {
+ printk(" %02x", this_usbduxfastsub->dux_commands[i]);
+ }
+ printk("\n");
+#endif
+ result = USB_BULK_MSG(this_usbduxfastsub->usbdev,
+ usb_sndbulkpipe(this_usbduxfastsub->usbdev,
+ CHANNELLISTEP),
+ this_usbduxfastsub->dux_commands,
+ SIZEOFDUXBUFFER, &nsent, 10000);
+ if (result < 0) {
+ printk("comedi%d: could not transmit dux_commands to the usb-device, err=%d\n", this_usbduxfastsub->comedidev->minor, result);
+ }
+ return result;
+}
+
+// Stops the data acquision
+// It should be safe to call this function from any context
+static int usbduxfastsub_unlink_InURBs(usbduxfastsub_t * usbduxfastsub_tmp)
+{
+ int j = 0;
+ int err = 0;
+
+ if (usbduxfastsub_tmp && usbduxfastsub_tmp->urbIn) {
+ usbduxfastsub_tmp->ai_cmd_running = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8)
+ j = usb_unlink_urb(usbduxfastsub_tmp->urbIn);
+ if (j < 0) {
+ err = j;
+ }
+#else
+ // waits until a running transfer is over
+ usb_kill_urb(usbduxfastsub_tmp->urbIn);
+ j = 0;
+#endif
+ }
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi: usbduxfast: unlinked InURB: res=%d\n", j);
+#endif
+ return err;
+}
+
+/* This will stop a running acquisition operation */
+// Is called from within this driver from both the
+// interrupt context and from comedi
+static int usbduxfast_ai_stop(usbduxfastsub_t * this_usbduxfastsub,
+ int do_unlink)
+{
+ int ret = 0;
+
+ if (!this_usbduxfastsub) {
+ printk("comedi?: usbduxfast_ai_stop: this_usbduxfastsub=NULL!\n");
+ return -EFAULT;
+ }
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi: usbduxfast_ai_stop\n");
+#endif
+
+ this_usbduxfastsub->ai_cmd_running = 0;
+
+ if (do_unlink) {
+ // stop aquistion
+ ret = usbduxfastsub_unlink_InURBs(this_usbduxfastsub);
+ }
+
+ return ret;
+}
+
+// This will cancel a running acquisition operation.
+// This is called by comedi but never from inside the
+// driver.
+static int usbduxfast_ai_cancel(comedi_device * dev, comedi_subdevice * s)
+{
+ usbduxfastsub_t *this_usbduxfastsub;
+ int res = 0;
+
+ // force unlink of all urbs
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi: usbduxfast_ai_cancel\n");
+#endif
+ this_usbduxfastsub = dev->private;
+ if (!this_usbduxfastsub) {
+ printk("comedi: usbduxfast_ai_cancel: this_usbduxfastsub=NULL\n");
+ return -EFAULT;
+ }
+ down(&this_usbduxfastsub->sem);
+ if (!(this_usbduxfastsub->probed)) {
+ up(&this_usbduxfastsub->sem);
+ return -ENODEV;
+ }
+ // unlink
+ res = usbduxfast_ai_stop(this_usbduxfastsub, 1);
+ up(&this_usbduxfastsub->sem);
+
+ return res;
+}
+
+// analogue IN
+// interrupt service routine
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+static void usbduxfastsub_ai_Irq(struct urb *urb)
+#else
+static void usbduxfastsub_ai_Irq(struct urb *urb PT_REGS_ARG)
+#endif
+{
+ int n, err;
+ usbduxfastsub_t *this_usbduxfastsub;
+ comedi_device *this_comedidev;
+ comedi_subdevice *s;
+ uint16_t *p;
+
+ // sanity checks
+ // is the urb there?
+ if (!urb) {
+ printk("comedi_: usbduxfast_: ao int-handler called with urb=NULL!\n");
+ return;
+ }
+ // the context variable points to the subdevice
+ this_comedidev = urb->context;
+ if (!this_comedidev) {
+ printk("comedi_: usbduxfast_: urb context is a NULL pointer!\n");
+ return;
+ }
+ // the private structure of the subdevice is usbduxfastsub_t
+ this_usbduxfastsub = this_comedidev->private;
+ if (!this_usbduxfastsub) {
+ printk("comedi_: usbduxfast_: private of comedi subdev is a NULL pointer!\n");
+ return;
+ }
+ // are we running a command?
+ if (unlikely(!(this_usbduxfastsub->ai_cmd_running))) {
+ // not running a command
+ // do not continue execution if no asynchronous command is running
+ // in particular not resubmit
+ return;
+ }
+
+ if (unlikely(!(this_usbduxfastsub->attached))) {
+ // no comedi device there
+ return;
+ }
+ // subdevice which is the AD converter
+ s = this_comedidev->subdevices + SUBDEV_AD;
+
+ // first we test if something unusual has just happened
+ switch (urb->status) {
+ case 0:
+ break;
+
+ // happens after an unlink command or when the device is plugged out
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -ECONNABORTED:
+ // tell this comedi
+ s->async->events |= COMEDI_CB_EOA;
+ s->async->events |= COMEDI_CB_ERROR;
+ comedi_event(this_usbduxfastsub->comedidev, s);
+ // stop the transfer w/o unlink
+ usbduxfast_ai_stop(this_usbduxfastsub, 0);
+ return;
+
+ default:
+ printk("comedi%d: usbduxfast: non-zero urb status received in ai intr context: %d\n", this_usbduxfastsub->comedidev->minor, urb->status);
+ s->async->events |= COMEDI_CB_EOA;
+ s->async->events |= COMEDI_CB_ERROR;
+ comedi_event(this_usbduxfastsub->comedidev, s);
+ usbduxfast_ai_stop(this_usbduxfastsub, 0);
+ return;
+ }
+
+ p = urb->transfer_buffer;
+ if (!this_usbduxfastsub->ignore) {
+ if (!(this_usbduxfastsub->ai_continous)) {
+ // not continous, fixed number of samples
+ n = urb->actual_length / sizeof(uint16_t);
+ if (unlikely(this_usbduxfastsub->ai_sample_count < n)) {
+ // we have send only a fraction of the bytes received
+ cfc_write_array_to_buffer(s,
+ urb->transfer_buffer,
+ this_usbduxfastsub->ai_sample_count *
+ sizeof(uint16_t));
+ usbduxfast_ai_stop(this_usbduxfastsub, 0);
+ // say comedi that the acquistion is over
+ s->async->events |= COMEDI_CB_EOA;
+ comedi_event(this_usbduxfastsub->comedidev, s);
+ return;
+ }
+ this_usbduxfastsub->ai_sample_count -= n;
+ }
+ // write the full buffer to comedi
+ cfc_write_array_to_buffer(s,
+ urb->transfer_buffer, urb->actual_length);
+
+ // tell comedi that data is there
+ comedi_event(this_usbduxfastsub->comedidev, s);
+
+ } else {
+ // ignore this packet
+ this_usbduxfastsub->ignore--;
+ }
+
+ // command is still running
+ // resubmit urb for BULK transfer
+ urb->dev = this_usbduxfastsub->usbdev;
+ urb->status = 0;
+ if ((err = USB_SUBMIT_URB(urb)) < 0) {
+ printk("comedi%d: usbduxfast: urb resubm failed: %d",
+ this_usbduxfastsub->comedidev->minor, err);
+ s->async->events |= COMEDI_CB_EOA;
+ s->async->events |= COMEDI_CB_ERROR;
+ comedi_event(this_usbduxfastsub->comedidev, s);
+ usbduxfast_ai_stop(this_usbduxfastsub, 0);
+ }
+}
+
+static int usbduxfastsub_start(usbduxfastsub_t * usbduxfastsub)
+{
+ int errcode = 0;
+ unsigned char local_transfer_buffer[16];
+
+ if (usbduxfastsub->probed) {
+ // 7f92 to zero
+ local_transfer_buffer[0] = 0;
+ errcode = USB_CONTROL_MSG(usbduxfastsub->usbdev,
+ // create a pipe for a control transfer
+ usb_sndctrlpipe(usbduxfastsub->usbdev, 0),
+ // bRequest, "Firmware"
+ USBDUXFASTSUB_FIRMWARE,
+ // bmRequestType
+ VENDOR_DIR_OUT,
+ // Value
+ USBDUXFASTSUB_CPUCS,
+ // Index
+ 0x0000,
+ // address of the transfer buffer
+ local_transfer_buffer,
+ // Length
+ 1,
+ // Timeout
+ EZTIMEOUT);
+ if (errcode < 0) {
+ printk("comedi_: usbduxfast_: control msg failed (start)\n");
+ return errcode;
+ }
+ }
+ return 0;
+}
+
+static int usbduxfastsub_stop(usbduxfastsub_t * usbduxfastsub)
+{
+ int errcode = 0;
+
+ unsigned char local_transfer_buffer[16];
+ if (usbduxfastsub->probed) {
+ // 7f92 to one
+ local_transfer_buffer[0] = 1;
+ errcode = USB_CONTROL_MSG
+ (usbduxfastsub->usbdev,
+ usb_sndctrlpipe(usbduxfastsub->usbdev, 0),
+ // bRequest, "Firmware"
+ USBDUXFASTSUB_FIRMWARE,
+ // bmRequestType
+ VENDOR_DIR_OUT,
+ // Value
+ USBDUXFASTSUB_CPUCS,
+ // Index
+ 0x0000, local_transfer_buffer,
+ // Length
+ 1,
+ // Timeout
+ EZTIMEOUT);
+ if (errcode < 0) {
+ printk("comedi_: usbduxfast: control msg failed (stop)\n");
+ return errcode;
+ }
+ }
+ return 0;
+}
+
+static int usbduxfastsub_upload(usbduxfastsub_t * usbduxfastsub,
+ unsigned char *local_transfer_buffer,
+ unsigned int startAddr, unsigned int len)
+{
+ int errcode;
+
+ if (usbduxfastsub->probed) {
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi%d: usbduxfast: uploading %d bytes",
+ usbduxfastsub->comedidev->minor, len);
+ printk(" to addr %d, first byte=%d.\n",
+ startAddr, local_transfer_buffer[0]);
+#endif
+ errcode = USB_CONTROL_MSG
+ (usbduxfastsub->usbdev,
+ usb_sndctrlpipe(usbduxfastsub->usbdev, 0),
+ // brequest, firmware
+ USBDUXFASTSUB_FIRMWARE,
+ // bmRequestType
+ VENDOR_DIR_OUT,
+ // value
+ startAddr,
+ // index
+ 0x0000,
+ // our local safe buffer
+ local_transfer_buffer,
+ // length
+ len,
+ // timeout
+ EZTIMEOUT);
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi_: usbduxfast: result=%d\n", errcode);
+#endif
+ if (errcode < 0) {
+ printk("comedi_: usbduxfast: uppload failed\n");
+ return errcode;
+ }
+ } else {
+ // no device on the bus for this index
+ return -EFAULT;
+ }
+ return 0;
+}
+
+int firmwareUpload(usbduxfastsub_t * usbduxfastsub,
+ unsigned char *firmwareBinary, int sizeFirmware)
+{
+ int ret;
+
+ if (!firmwareBinary) {
+ return 0;
+ }
+ ret = usbduxfastsub_stop(usbduxfastsub);
+ if (ret < 0) {
+ printk("comedi_: usbduxfast: can not stop firmware\n");
+ return ret;
+ }
+ ret = usbduxfastsub_upload(usbduxfastsub,
+ firmwareBinary, 0, sizeFirmware);
+ if (ret < 0) {
+ printk("comedi_: usbduxfast: firmware upload failed\n");
+ return ret;
+ }
+ ret = usbduxfastsub_start(usbduxfastsub);
+ if (ret < 0) {
+ printk("comedi_: usbduxfast: can not start firmware\n");
+ return ret;
+ }
+ return 0;
+}
+
+int usbduxfastsub_submit_InURBs(usbduxfastsub_t * usbduxfastsub)
+{
+ int errFlag;
+
+ if (!usbduxfastsub) {
+ return -EFAULT;
+ }
+ usb_fill_bulk_urb(usbduxfastsub->urbIn,
+ usbduxfastsub->usbdev,
+ usb_rcvbulkpipe(usbduxfastsub->usbdev, BULKINEP),
+ usbduxfastsub->transfer_buffer,
+ SIZEINBUF, usbduxfastsub_ai_Irq, usbduxfastsub->comedidev);
+
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi%d: usbduxfast: submitting in-urb: %x,%x\n",
+ usbduxfastsub->comedidev->minor,
+ (int)(usbduxfastsub->urbIn->context),
+ (int)(usbduxfastsub->urbIn->dev));
+#endif
+ errFlag = USB_SUBMIT_URB(usbduxfastsub->urbIn);
+ if (errFlag) {
+ printk("comedi_: usbduxfast: ai: ");
+ printk("USB_SUBMIT_URB");
+ printk(" error %d\n", errFlag);
+ return errFlag;
+ }
+ return 0;
+}
+
+static int usbduxfast_ai_cmdtest(comedi_device * dev,
+ comedi_subdevice * s, comedi_cmd * cmd)
+{
+ int err = 0, stop_mask = 0;
+ long int steps, tmp = 0;
+ int minSamplPer;
+ usbduxfastsub_t *this_usbduxfastsub = dev->private;
+ if (!(this_usbduxfastsub->probed)) {
+ return -ENODEV;
+ }
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi%d: usbduxfast_ai_cmdtest\n", dev->minor);
+ printk("comedi%d: usbduxfast: convert_arg=%u scan_begin_arg=%u\n",
+ dev->minor, cmd->convert_arg, cmd->scan_begin_arg);
+#endif
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW | TRIG_EXT | TRIG_INT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ stop_mask = TRIG_COUNT | TRIG_NONE;
+ cmd->stop_src &= stop_mask;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ if (cmd->start_src != TRIG_NOW &&
+ cmd->start_src != TRIG_EXT && cmd->start_src != TRIG_INT)
+ err++;
+ if (cmd->scan_begin_src != TRIG_TIMER &&
+ cmd->scan_begin_src != TRIG_FOLLOW &&
+ cmd->scan_begin_src != TRIG_EXT)
+ err++;
+ if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
+ err++;
+ if (cmd->stop_src != TRIG_COUNT &&
+ cmd->stop_src != TRIG_EXT && cmd->stop_src != TRIG_NONE)
+ err++;
+
+ // can't have external stop and start triggers at once
+ if (cmd->start_src == TRIG_EXT && cmd->stop_src == TRIG_EXT)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_src == TRIG_NOW && cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+
+ if (!cmd->chanlist_len) {
+ err++;
+ }
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+
+ if (cmd->chanlist_len == 1) {
+ minSamplPer = 1;
+ } else {
+ minSamplPer = MIN_SAMPLING_PERIOD;
+ }
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ steps = cmd->convert_arg * 30;
+ if (steps < (minSamplPer * 1000)) {
+ steps = minSamplPer * 1000;
+ }
+ if (steps > (MAX_SAMPLING_PERIOD * 1000)) {
+ steps = MAX_SAMPLING_PERIOD * 1000;
+ }
+ // calc arg again
+ tmp = steps / 30;
+ if (cmd->convert_arg != tmp) {
+ cmd->convert_arg = tmp;
+ err++;
+ }
+ }
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ err++;
+ }
+ // stop source
+ switch (cmd->stop_src) {
+ case TRIG_COUNT:
+ if (!cmd->stop_arg) {
+ cmd->stop_arg = 1;
+ err++;
+ }
+ break;
+ case TRIG_NONE:
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ break;
+ // TRIG_EXT doesn't care since it doesn't trigger off a numbered channel
+ default:
+ break;
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ return 0;
+
+}
+
+static int usbduxfast_ai_inttrig(comedi_device * dev,
+ comedi_subdevice * s, unsigned int trignum)
+{
+ int ret;
+ usbduxfastsub_t *this_usbduxfastsub = dev->private;
+ if (!this_usbduxfastsub) {
+ return -EFAULT;
+ }
+ down(&this_usbduxfastsub->sem);
+ if (!(this_usbduxfastsub->probed)) {
+ up(&this_usbduxfastsub->sem);
+ return -ENODEV;
+ }
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi%d: usbduxfast_ai_inttrig\n", dev->minor);
+#endif
+
+ if (trignum != 0) {
+ printk("comedi%d: usbduxfast_ai_inttrig: invalid trignum\n",
+ dev->minor);
+ up(&this_usbduxfastsub->sem);
+ return -EINVAL;
+ }
+ if (!(this_usbduxfastsub->ai_cmd_running)) {
+ this_usbduxfastsub->ai_cmd_running = 1;
+ ret = usbduxfastsub_submit_InURBs(this_usbduxfastsub);
+ if (ret < 0) {
+ printk("comedi%d: usbduxfast_ai_inttrig: urbSubmit: err=%d\n", dev->minor, ret);
+ this_usbduxfastsub->ai_cmd_running = 0;
+ up(&this_usbduxfastsub->sem);
+ return ret;
+ }
+ s->async->inttrig = NULL;
+ } else {
+ printk("comedi%d: ai_inttrig but acqu is already running\n",
+ dev->minor);
+ }
+ up(&this_usbduxfastsub->sem);
+ return 1;
+}
+
+// offsets for the GPIF bytes
+// the first byte is the command byte
+#define LENBASE 1+0x00
+#define OPBASE 1+0x08
+#define OUTBASE 1+0x10
+#define LOGBASE 1+0x18
+
+static int usbduxfast_ai_cmd(comedi_device * dev, comedi_subdevice * s)
+{
+ comedi_cmd *cmd = &s->async->cmd;
+ unsigned int chan, gain, rngmask = 0xff;
+ int i, j, ret;
+ usbduxfastsub_t *this_usbduxfastsub = dev->private;
+ int result;
+ long steps, steps_tmp;
+
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi%d: usbduxfast_ai_cmd\n", dev->minor);
+#endif
+ if (!this_usbduxfastsub) {
+ return -EFAULT;
+ }
+ down(&this_usbduxfastsub->sem);
+ if (!(this_usbduxfastsub->probed)) {
+ up(&this_usbduxfastsub->sem);
+ return -ENODEV;
+ }
+ if (this_usbduxfastsub->ai_cmd_running) {
+ printk("comedi%d: ai_cmd not possible. Another ai_cmd is running.\n", dev->minor);
+ up(&this_usbduxfastsub->sem);
+ return -EBUSY;
+ }
+ // set current channel of the running aquisition to zero
+ s->async->cur_chan = 0;
+
+ // ignore the first buffers from the device if there is an error condition
+ this_usbduxfastsub->ignore = PACKETS_TO_IGNORE;
+
+ if (cmd->chanlist_len > 0) {
+ gain = CR_RANGE(cmd->chanlist[0]);
+ for (i = 0; i < cmd->chanlist_len; ++i) {
+ chan = CR_CHAN(cmd->chanlist[i]);
+ if (chan != i) {
+ printk("comedi%d: cmd is accepting only consecutive channels.\n", dev->minor);
+ up(&this_usbduxfastsub->sem);
+ return -EINVAL;
+ }
+ if ((gain != CR_RANGE(cmd->chanlist[i]))
+ && (cmd->chanlist_len > 3)) {
+ printk("comedi%d: the gain must be the same for all channels.\n", dev->minor);
+ up(&this_usbduxfastsub->sem);
+ return -EINVAL;
+ }
+ if (i >= NUMCHANNELS) {
+ printk("comedi%d: channel list too long\n",
+ dev->minor);
+ break;
+ }
+ }
+ }
+ steps = 0;
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ printk("comedi%d: usbduxfast: scan_begin_src==TRIG_TIMER not valid.\n", dev->minor);
+ up(&this_usbduxfastsub->sem);
+ return -EINVAL;
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ steps = (cmd->convert_arg * 30) / 1000;
+ }
+ if ((steps < MIN_SAMPLING_PERIOD) && (cmd->chanlist_len != 1)) {
+ printk("comedi%d: usbduxfast: ai_cmd: steps=%ld, scan_begin_arg=%d. Not properly tested by cmdtest?\n", dev->minor, steps, cmd->scan_begin_arg);
+ up(&this_usbduxfastsub->sem);
+ return -EINVAL;
+ }
+ if (steps > MAX_SAMPLING_PERIOD) {
+ printk("comedi%d: usbduxfast: ai_cmd: sampling rate too low.\n",
+ dev->minor);
+ up(&this_usbduxfastsub->sem);
+ return -EINVAL;
+ }
+ if ((cmd->start_src == TRIG_EXT) && (cmd->chanlist_len != 1)
+ && (cmd->chanlist_len != 16)) {
+ printk("comedi%d: usbduxfast: ai_cmd: TRIG_EXT only with 1 or 16 channels possible.\n", dev->minor);
+ up(&this_usbduxfastsub->sem);
+ return -EINVAL;
+ }
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi%d: usbduxfast: steps=%ld, convert_arg=%u, ai_timer=%u\n",
+ dev->minor,
+ steps, cmd->convert_arg, this_usbduxfastsub->ai_timer);
+#endif
+
+ switch (cmd->chanlist_len) {
+ // one channel
+ case 1:
+ if (CR_RANGE(cmd->chanlist[0]) > 0)
+ rngmask = 0xff - 0x04;
+ else
+ rngmask = 0xff;
+
+ // for external trigger: looping in this state until the RDY0 pin
+ // becomes zero
+ if (cmd->start_src == TRIG_EXT) { // we loop here until ready has been set
+ this_usbduxfastsub->dux_commands[LENBASE + 0] = 0x01; // branch back to state 0
+ this_usbduxfastsub->dux_commands[OPBASE + 0] = 0x01; // deceision state w/o data
+ this_usbduxfastsub->dux_commands[OUTBASE + 0] =
+ 0xFF & rngmask;
+ this_usbduxfastsub->dux_commands[LOGBASE + 0] = 0x00; // RDY0 = 0
+ } else { // we just proceed to state 1
+ this_usbduxfastsub->dux_commands[LENBASE + 0] = 1;
+ this_usbduxfastsub->dux_commands[OPBASE + 0] = 0;
+ this_usbduxfastsub->dux_commands[OUTBASE + 0] =
+ 0xFF & rngmask;
+ this_usbduxfastsub->dux_commands[LOGBASE + 0] = 0;
+ }
+
+ if (steps < MIN_SAMPLING_PERIOD) {
+ // for fast single channel aqu without mux
+ if (steps <= 1) {
+ // we just stay here at state 1 and rexecute the same state
+ // this gives us 30MHz sampling rate
+ this_usbduxfastsub->dux_commands[LENBASE + 1] = 0x89; // branch back to state 1
+ this_usbduxfastsub->dux_commands[OPBASE + 1] = 0x03; // deceision state with data
+ this_usbduxfastsub->dux_commands[OUTBASE + 1] =
+ 0xFF & rngmask;
+ this_usbduxfastsub->dux_commands[LOGBASE + 1] = 0xFF; // doesn't matter
+ } else {
+ // we loop through two states: data and delay: max rate is 15Mhz
+ this_usbduxfastsub->dux_commands[LENBASE + 1] =
+ steps - 1;
+ this_usbduxfastsub->dux_commands[OPBASE + 1] = 0x02; // data
+ this_usbduxfastsub->dux_commands[OUTBASE + 1] =
+ 0xFF & rngmask;
+ this_usbduxfastsub->dux_commands[LOGBASE + 1] = 0; // doesn't matter
+
+ this_usbduxfastsub->dux_commands[LENBASE + 2] = 0x09; // branch back to state 1
+ this_usbduxfastsub->dux_commands[OPBASE + 2] = 0x01; // deceision state w/o data
+ this_usbduxfastsub->dux_commands[OUTBASE + 2] =
+ 0xFF & rngmask;
+ this_usbduxfastsub->dux_commands[LOGBASE + 2] = 0xFF; // doesn't matter
+ }
+ } else {
+ // we loop through 3 states: 2x delay and 1x data. This gives a min
+ // sampling rate of 60kHz.
+
+ // we have 1 state with duration 1
+ steps = steps - 1;
+
+ // do the first part of the delay
+ this_usbduxfastsub->dux_commands[LENBASE + 1] =
+ steps / 2;
+ this_usbduxfastsub->dux_commands[OPBASE + 1] = 0;
+ this_usbduxfastsub->dux_commands[OUTBASE + 1] =
+ 0xFF & rngmask;
+ this_usbduxfastsub->dux_commands[LOGBASE + 1] = 0;
+
+ // and the second part
+ this_usbduxfastsub->dux_commands[LENBASE + 2] =
+ steps - steps / 2;
+ this_usbduxfastsub->dux_commands[OPBASE + 2] = 0;
+ this_usbduxfastsub->dux_commands[OUTBASE + 2] =
+ 0xFF & rngmask;
+ this_usbduxfastsub->dux_commands[LOGBASE + 2] = 0;
+
+ // get the data and branch back
+ this_usbduxfastsub->dux_commands[LENBASE + 3] = 0x09; // branch back to state 1
+ this_usbduxfastsub->dux_commands[OPBASE + 3] = 0x03; // deceision state w data
+ this_usbduxfastsub->dux_commands[OUTBASE + 3] =
+ 0xFF & rngmask;
+ this_usbduxfastsub->dux_commands[LOGBASE + 3] = 0xFF; // doesn't matter
+ }
+ break;
+
+ case 2:
+ // two channels
+ // commit data to the FIFO
+ if (CR_RANGE(cmd->chanlist[0]) > 0)
+ rngmask = 0xff - 0x04;
+ else
+ rngmask = 0xff;
+ this_usbduxfastsub->dux_commands[LENBASE + 0] = 1;
+ this_usbduxfastsub->dux_commands[OPBASE + 0] = 0x02; // data
+ this_usbduxfastsub->dux_commands[OUTBASE + 0] = 0xFF & rngmask;
+ this_usbduxfastsub->dux_commands[LOGBASE + 0] = 0;
+
+ // we have 1 state with duration 1: state 0
+ steps_tmp = steps - 1;
+
+ if (CR_RANGE(cmd->chanlist[1]) > 0)
+ rngmask = 0xff - 0x04;
+ else
+ rngmask = 0xff;
+ // do the first part of the delay
+ this_usbduxfastsub->dux_commands[LENBASE + 1] = steps_tmp / 2;
+ this_usbduxfastsub->dux_commands[OPBASE + 1] = 0;
+ this_usbduxfastsub->dux_commands[OUTBASE + 1] = 0xFE & rngmask; //count
+ this_usbduxfastsub->dux_commands[LOGBASE + 1] = 0;
+
+ // and the second part
+ this_usbduxfastsub->dux_commands[LENBASE + 2] =
+ steps_tmp - steps_tmp / 2;
+ this_usbduxfastsub->dux_commands[OPBASE + 2] = 0;
+ this_usbduxfastsub->dux_commands[OUTBASE + 2] = 0xFF & rngmask;
+ this_usbduxfastsub->dux_commands[LOGBASE + 2] = 0;
+
+ this_usbduxfastsub->dux_commands[LENBASE + 3] = 1;
+ this_usbduxfastsub->dux_commands[OPBASE + 3] = 0x02; // data
+ this_usbduxfastsub->dux_commands[OUTBASE + 3] = 0xFF & rngmask;
+ this_usbduxfastsub->dux_commands[LOGBASE + 3] = 0;
+
+ // we have 2 states with duration 1: step 6 and the IDLE state
+ steps_tmp = steps - 2;
+
+ if (CR_RANGE(cmd->chanlist[0]) > 0)
+ rngmask = 0xff - 0x04;
+ else
+ rngmask = 0xff;
+ // do the first part of the delay
+ this_usbduxfastsub->dux_commands[LENBASE + 4] = steps_tmp / 2;
+ this_usbduxfastsub->dux_commands[OPBASE + 4] = 0;
+ this_usbduxfastsub->dux_commands[OUTBASE + 4] = (0xFF - 0x02) & rngmask; //reset
+ this_usbduxfastsub->dux_commands[LOGBASE + 4] = 0;
+
+ // and the second part
+ this_usbduxfastsub->dux_commands[LENBASE + 5] =
+ steps_tmp - steps_tmp / 2;
+ this_usbduxfastsub->dux_commands[OPBASE + 5] = 0;
+ this_usbduxfastsub->dux_commands[OUTBASE + 5] = 0xFF & rngmask;
+ this_usbduxfastsub->dux_commands[LOGBASE + 5] = 0;
+
+ this_usbduxfastsub->dux_commands[LENBASE + 6] = 1;
+ this_usbduxfastsub->dux_commands[OPBASE + 6] = 0;
+ this_usbduxfastsub->dux_commands[OUTBASE + 6] = 0xFF & rngmask;
+ this_usbduxfastsub->dux_commands[LOGBASE + 6] = 0;
+ break;
+
+ case 3:
+ // three channels
+ for (j = 0; j < 1; j++) {
+ if (CR_RANGE(cmd->chanlist[j]) > 0)
+ rngmask = 0xff - 0x04;
+ else
+ rngmask = 0xff;
+ // commit data to the FIFO and do the first part of the delay
+ this_usbduxfastsub->dux_commands[LENBASE + j * 2] =
+ steps / 2;
+ this_usbduxfastsub->dux_commands[OPBASE + j * 2] = 0x02; // data
+ this_usbduxfastsub->dux_commands[OUTBASE + j * 2] = 0xFF & rngmask; // no change
+ this_usbduxfastsub->dux_commands[LOGBASE + j * 2] = 0;
+
+ if (CR_RANGE(cmd->chanlist[j + 1]) > 0)
+ rngmask = 0xff - 0x04;
+ else
+ rngmask = 0xff;
+ // do the second part of the delay
+ this_usbduxfastsub->dux_commands[LENBASE + j * 2 + 1] =
+ steps - steps / 2;
+ this_usbduxfastsub->dux_commands[OPBASE + j * 2 + 1] = 0; // no data
+ this_usbduxfastsub->dux_commands[OUTBASE + j * 2 + 1] = 0xFE & rngmask; //count
+ this_usbduxfastsub->dux_commands[LOGBASE + j * 2 + 1] =
+ 0;
+ }
+
+ // 2 steps with duration 1: the idele step and step 6:
+ steps_tmp = steps - 2;
+ // commit data to the FIFO and do the first part of the delay
+ this_usbduxfastsub->dux_commands[LENBASE + 4] = steps_tmp / 2;
+ this_usbduxfastsub->dux_commands[OPBASE + 4] = 0x02; // data
+ this_usbduxfastsub->dux_commands[OUTBASE + 4] = 0xFF & rngmask; // no change
+ this_usbduxfastsub->dux_commands[LOGBASE + 4] = 0;
+
+ if (CR_RANGE(cmd->chanlist[0]) > 0)
+ rngmask = 0xff - 0x04;
+ else
+ rngmask = 0xff;
+ // do the second part of the delay
+ this_usbduxfastsub->dux_commands[LENBASE + 5] =
+ steps_tmp - steps_tmp / 2;
+ this_usbduxfastsub->dux_commands[OPBASE + 5] = 0; // no data
+ this_usbduxfastsub->dux_commands[OUTBASE + 5] = (0xFF - 0x02) & rngmask; // reset
+ this_usbduxfastsub->dux_commands[LOGBASE + 5] = 0;
+
+ this_usbduxfastsub->dux_commands[LENBASE + 6] = 1;
+ this_usbduxfastsub->dux_commands[OPBASE + 6] = 0;
+ this_usbduxfastsub->dux_commands[OUTBASE + 6] = 0xFF & rngmask;
+ this_usbduxfastsub->dux_commands[LOGBASE + 6] = 0;
+
+ case 16:
+ if (CR_RANGE(cmd->chanlist[0]) > 0)
+ rngmask = 0xff - 0x04;
+ else
+ rngmask = 0xff;
+ if (cmd->start_src == TRIG_EXT) { // we loop here until ready has been set
+ this_usbduxfastsub->dux_commands[LENBASE + 0] = 0x01; // branch back to state 0
+ this_usbduxfastsub->dux_commands[OPBASE + 0] = 0x01; // deceision state w/o data
+ this_usbduxfastsub->dux_commands[OUTBASE + 0] = (0xFF - 0x02) & rngmask; // reset
+ this_usbduxfastsub->dux_commands[LOGBASE + 0] = 0x00; // RDY0 = 0
+ } else { // we just proceed to state 1
+ this_usbduxfastsub->dux_commands[LENBASE + 0] = 255; // 30us reset pulse
+ this_usbduxfastsub->dux_commands[OPBASE + 0] = 0;
+ this_usbduxfastsub->dux_commands[OUTBASE + 0] = (0xFF - 0x02) & rngmask; // reset
+ this_usbduxfastsub->dux_commands[LOGBASE + 0] = 0;
+ }
+
+ // commit data to the FIFO
+ this_usbduxfastsub->dux_commands[LENBASE + 1] = 1;
+ this_usbduxfastsub->dux_commands[OPBASE + 1] = 0x02; // data
+ this_usbduxfastsub->dux_commands[OUTBASE + 1] = 0xFF & rngmask;
+ this_usbduxfastsub->dux_commands[LOGBASE + 1] = 0;
+
+ // we have 2 states with duration 1
+ steps = steps - 2;
+
+ // do the first part of the delay
+ this_usbduxfastsub->dux_commands[LENBASE + 2] = steps / 2;
+ this_usbduxfastsub->dux_commands[OPBASE + 2] = 0;
+ this_usbduxfastsub->dux_commands[OUTBASE + 2] = 0xFE & rngmask;
+ this_usbduxfastsub->dux_commands[LOGBASE + 2] = 0;
+
+ // and the second part
+ this_usbduxfastsub->dux_commands[LENBASE + 3] =
+ steps - steps / 2;
+ this_usbduxfastsub->dux_commands[OPBASE + 3] = 0;
+ this_usbduxfastsub->dux_commands[OUTBASE + 3] = 0xFF & rngmask;
+ this_usbduxfastsub->dux_commands[LOGBASE + 3] = 0;
+
+ this_usbduxfastsub->dux_commands[LENBASE + 4] = 0x09; // branch back to state 1
+ this_usbduxfastsub->dux_commands[OPBASE + 4] = 0x01; // deceision state w/o data
+ this_usbduxfastsub->dux_commands[OUTBASE + 4] = 0xFF & rngmask;
+ this_usbduxfastsub->dux_commands[LOGBASE + 4] = 0xFF; // doesn't matter
+
+ break;
+
+ default:
+ printk("comedi %d: unsupported combination of channels\n",
+ dev->minor);
+ up(&this_usbduxfastsub->sem);
+ return -EFAULT;
+ }
+
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi %d: sending commands to the usb device\n", dev->minor);
+#endif
+ // 0 means that the AD commands are sent
+ result = send_dux_commands(this_usbduxfastsub, SENDADCOMMANDS);
+ if (result < 0) {
+ printk("comedi%d: adc command could not be submitted. Aborting...\n", dev->minor);
+ up(&this_usbduxfastsub->sem);
+ return result;
+ }
+ if (cmd->stop_src == TRIG_COUNT) {
+ this_usbduxfastsub->ai_sample_count =
+ (cmd->stop_arg) * (cmd->scan_end_arg);
+ if (usbduxfastsub->ai_sample_count < 1) {
+ printk("comedi%d: (cmd->stop_arg)*(cmd->scan_end_arg)<1, aborting.\n", dev->minor);
+ up(&this_usbduxfastsub->sem);
+ return -EFAULT;
+ }
+ this_usbduxfastsub->ai_continous = 0;
+ } else {
+ // continous aquisition
+ this_usbduxfastsub->ai_continous = 1;
+ this_usbduxfastsub->ai_sample_count = 0;
+ }
+
+ if ((cmd->start_src == TRIG_NOW) || (cmd->start_src == TRIG_EXT)) {
+ // enable this acquisition operation
+ this_usbduxfastsub->ai_cmd_running = 1;
+ ret = usbduxfastsub_submit_InURBs(this_usbduxfastsub);
+ if (ret < 0) {
+ this_usbduxfastsub->ai_cmd_running = 0;
+ // fixme: unlink here??
+ up(&this_usbduxfastsub->sem);
+ return ret;
+ }
+ s->async->inttrig = NULL;
+ } else {
+ /* TRIG_INT */
+ // don't enable the acquision operation
+ // wait for an internal signal
+ s->async->inttrig = usbduxfast_ai_inttrig;
+ }
+ up(&this_usbduxfastsub->sem);
+
+ return 0;
+}
+
+/* Mode 0 is used to get a single conversion on demand */
+static int usbduxfast_ai_insn_read(comedi_device * dev,
+ comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
+{
+ int i, j, n, actual_length;
+ int chan, range, rngmask;
+ int err;
+ usbduxfastsub_t *usbduxfastsub = dev->private;
+
+ if (!usbduxfastsub) {
+ printk("comedi%d: ai_insn_read: no usb dev.\n", dev->minor);
+ return -ENODEV;
+ }
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi%d: ai_insn_read, insn->n=%d, insn->subdev=%d\n",
+ dev->minor, insn->n, insn->subdev);
+#endif
+ down(&usbduxfastsub->sem);
+ if (!(usbduxfastsub->probed)) {
+ up(&usbduxfastsub->sem);
+ return -ENODEV;
+ }
+ if (usbduxfastsub->ai_cmd_running) {
+ printk("comedi%d: ai_insn_read not possible. Async Command is running.\n", dev->minor);
+ up(&usbduxfastsub->sem);
+ return -EBUSY;
+ }
+ // sample one channel
+ chan = CR_CHAN(insn->chanspec);
+ range = CR_RANGE(insn->chanspec);
+ // set command for the first channel
+
+ if (range > 0)
+ rngmask = 0xff - 0x04;
+ else
+ rngmask = 0xff;
+ // commit data to the FIFO
+ usbduxfastsub->dux_commands[LENBASE + 0] = 1;
+ usbduxfastsub->dux_commands[OPBASE + 0] = 0x02; // data
+ usbduxfastsub->dux_commands[OUTBASE + 0] = 0xFF & rngmask;
+ usbduxfastsub->dux_commands[LOGBASE + 0] = 0;
+
+ // do the first part of the delay
+ usbduxfastsub->dux_commands[LENBASE + 1] = 12;
+ usbduxfastsub->dux_commands[OPBASE + 1] = 0;
+ usbduxfastsub->dux_commands[OUTBASE + 1] = 0xFE & rngmask;
+ usbduxfastsub->dux_commands[LOGBASE + 1] = 0;
+
+ usbduxfastsub->dux_commands[LENBASE + 2] = 1;
+ usbduxfastsub->dux_commands[OPBASE + 2] = 0;
+ usbduxfastsub->dux_commands[OUTBASE + 2] = 0xFE & rngmask;
+ usbduxfastsub->dux_commands[LOGBASE + 2] = 0;
+
+ usbduxfastsub->dux_commands[LENBASE + 3] = 1;
+ usbduxfastsub->dux_commands[OPBASE + 3] = 0;
+ usbduxfastsub->dux_commands[OUTBASE + 3] = 0xFE & rngmask;
+ usbduxfastsub->dux_commands[LOGBASE + 3] = 0;
+
+ usbduxfastsub->dux_commands[LENBASE + 4] = 1;
+ usbduxfastsub->dux_commands[OPBASE + 4] = 0;
+ usbduxfastsub->dux_commands[OUTBASE + 4] = 0xFE & rngmask;
+ usbduxfastsub->dux_commands[LOGBASE + 4] = 0;
+
+ // second part
+ usbduxfastsub->dux_commands[LENBASE + 5] = 12;
+ usbduxfastsub->dux_commands[OPBASE + 5] = 0;
+ usbduxfastsub->dux_commands[OUTBASE + 5] = 0xFF & rngmask;
+ usbduxfastsub->dux_commands[LOGBASE + 5] = 0;
+
+ usbduxfastsub->dux_commands[LENBASE + 6] = 1;
+ usbduxfastsub->dux_commands[OPBASE + 6] = 0;
+ usbduxfastsub->dux_commands[OUTBASE + 6] = 0xFF & rngmask;
+ usbduxfastsub->dux_commands[LOGBASE + 0] = 0;
+
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi %d: sending commands to the usb device\n", dev->minor);
+#endif
+ // 0 means that the AD commands are sent
+ err = send_dux_commands(usbduxfastsub, SENDADCOMMANDS);
+ if (err < 0) {
+ printk("comedi%d: adc command could not be submitted. Aborting...\n", dev->minor);
+ up(&usbduxfastsub->sem);
+ return err;
+ }
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi%d: usbduxfast: submitting in-urb: %x,%x\n",
+ usbduxfastsub->comedidev->minor,
+ (int)(usbduxfastsub->urbIn->context),
+ (int)(usbduxfastsub->urbIn->dev));
+#endif
+ for (i = 0; i < PACKETS_TO_IGNORE; i++) {
+ err = USB_BULK_MSG(usbduxfastsub->usbdev,
+ usb_rcvbulkpipe(usbduxfastsub->usbdev, BULKINEP),
+ usbduxfastsub->transfer_buffer,
+ SIZEINBUF, &actual_length, 10000);
+ if (err < 0) {
+ printk("comedi%d: insn timeout. No data.\n",
+ dev->minor);
+ up(&usbduxfastsub->sem);
+ return err;
+ }
+ }
+ // data points
+ for (i = 0; i < insn->n;) {
+ err = USB_BULK_MSG(usbduxfastsub->usbdev,
+ usb_rcvbulkpipe(usbduxfastsub->usbdev, BULKINEP),
+ usbduxfastsub->transfer_buffer,
+ SIZEINBUF, &actual_length, 10000);
+ if (err < 0) {
+ printk("comedi%d: insn data error: %d\n",
+ dev->minor, err);
+ up(&usbduxfastsub->sem);
+ return err;
+ }
+ n = actual_length / sizeof(uint16_t);
+ if ((n % 16) != 0) {
+ printk("comedi%d: insn data packet corrupted.\n",
+ dev->minor);
+ up(&usbduxfastsub->sem);
+ return -EINVAL;
+ }
+ for (j = chan; (j < n) && (i < insn->n); j = j + 16) {
+ data[i] =
+ ((uint16_t *) (usbduxfastsub->
+ transfer_buffer))[j];
+ i++;
+ }
+ }
+ up(&usbduxfastsub->sem);
+ return i;
+}
+
+static unsigned hex2unsigned(char *h)
+{
+ unsigned hi, lo;
+ if (h[0] > '9') {
+ hi = h[0] - 'A' + 0x0a;
+ } else {
+ hi = h[0] - '0';
+ }
+ if (h[1] > '9') {
+ lo = h[1] - 'A' + 0x0a;
+ } else {
+ lo = h[1] - '0';
+ }
+ return hi * 0x10 + lo;
+}
+
+// for FX2
+#define FIRMWARE_MAX_LEN 0x2000
+
+// taken from David Brownell's fxload and adjusted for this driver
+static int read_firmware(usbduxfastsub_t * usbduxfastsub, void *firmwarePtr,
+ long size)
+{
+ int i = 0;
+ unsigned char *fp = (char *)firmwarePtr;
+ unsigned char *firmwareBinary = NULL;
+ int res = 0;
+ int maxAddr = 0;
+
+ firmwareBinary = kmalloc(FIRMWARE_MAX_LEN, GFP_KERNEL);
+ if (!firmwareBinary) {
+ printk("comedi_: usbduxfast: mem alloc for firmware failed\n");
+ return -ENOMEM;
+ }
+
+ for (;;) {
+ char buf[256], *cp;
+ char type;
+ int len;
+ int idx, off;
+ int j = 0;
+
+ // get one line
+ while ((i < size) && (fp[i] != 13) && (fp[i] != 10)) {
+ buf[j] = fp[i];
+ i++;
+ j++;
+ if (j >= sizeof(buf)) {
+ printk("comedi_: usbduxfast: bogus firmware file!\n");
+ return -1;
+ }
+ }
+ // get rid of LF/CR/...
+ while ((i < size) && ((fp[i] == 13) || (fp[i] == 10)
+ || (fp[i] == 0))) {
+ i++;
+ }
+
+ buf[j] = 0;
+ //printk("comedi_: buf=%s\n",buf);
+
+ /* EXTENSION: "# comment-till-end-of-line", for copyrights etc */
+ if (buf[0] == '#')
+ continue;
+
+ if (buf[0] != ':') {
+ printk("comedi_: usbduxfast: upload: not an ihex record: %s", buf);
+ return -EFAULT;
+ }
+
+ /* Read the length field (up to 16 bytes) */
+ len = hex2unsigned(buf + 1);
+
+ /* Read the target offset */
+ off = (hex2unsigned(buf + 3) * 0x0100) + hex2unsigned(buf + 5);
+
+ if ((off + len) > maxAddr) {
+ maxAddr = off + len;
+ }
+
+ if (maxAddr >= FIRMWARE_MAX_LEN) {
+ printk("comedi_: usbduxfast: firmware upload goes beyond FX2 RAM boundaries.");
+ return -EFAULT;
+ }
+ //printk("comedi_: usbduxfast: off=%x, len=%x:",off,len);
+
+ /* Read the record type */
+ type = hex2unsigned(buf + 7);
+
+ /* If this is an EOF record, then make it so. */
+ if (type == 1) {
+ break;
+ }
+
+ if (type != 0) {
+ printk("comedi_: usbduxfast: unsupported record type: %u\n", type);
+ return -EFAULT;
+ }
+
+ for (idx = 0, cp = buf + 9; idx < len; idx += 1, cp += 2) {
+ firmwareBinary[idx + off] = hex2unsigned(cp);
+ //printk("%02x ",firmwareBinary[idx+off]);
+ }
+ //printk("\n");
+
+ if (i >= size) {
+ printk("comedi_: usbduxfast: unexpected end of hex file\n");
+ break;
+ }
+
+ }
+ res = firmwareUpload(usbduxfastsub, firmwareBinary, maxAddr + 1);
+ kfree(firmwareBinary);
+ return res;
+}
+
+static void tidy_up(usbduxfastsub_t * usbduxfastsub_tmp)
+{
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi_: usbduxfast: tiding up\n");
+#endif
+ if (!usbduxfastsub_tmp) {
+ return;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ // shows the usb subsystem that the driver is down
+ if (usbduxfastsub_tmp->interface) {
+ usb_set_intfdata(usbduxfastsub_tmp->interface, NULL);
+ }
+#endif
+
+ usbduxfastsub_tmp->probed = 0;
+
+ if (usbduxfastsub_tmp->urbIn) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,8)
+ // waits until a running transfer is over
+ // thus, under 2.4 hotplugging while a command
+ // is running is not safe
+ usb_kill_urb(usbduxfastsub_tmp->urbIn);
+#endif
+ if (usbduxfastsub_tmp->transfer_buffer) {
+ kfree(usbduxfastsub_tmp->transfer_buffer);
+ usbduxfastsub_tmp->transfer_buffer = NULL;
+ }
+ usb_free_urb(usbduxfastsub_tmp->urbIn);
+ usbduxfastsub_tmp->urbIn = NULL;
+ }
+ if (usbduxfastsub_tmp->insnBuffer) {
+ kfree(usbduxfastsub_tmp->insnBuffer);
+ usbduxfastsub_tmp->insnBuffer = NULL;
+ }
+ if (usbduxfastsub_tmp->dux_commands) {
+ kfree(usbduxfastsub_tmp->dux_commands);
+ usbduxfastsub_tmp->dux_commands = NULL;
+ }
+ usbduxfastsub_tmp->ai_cmd_running = 0;
+}
+
+// allocate memory for the urbs and initialise them
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+static void *usbduxfastsub_probe(struct usb_device *udev,
+ unsigned int interfnum, const struct usb_device_id *id)
+{
+#else
+static int usbduxfastsub_probe(struct usb_interface *uinterf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(uinterf);
+#endif
+ int i;
+ int index;
+
+ if (udev->speed != USB_SPEED_HIGH) {
+ printk("comedi_: usbduxfast_: This driver needs USB 2.0 to operate. Aborting...\n");
+ return PROBE_ERR_RETURN(-ENODEV);
+ }
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi_: usbduxfast_: finding a free structure for the usb-device\n");
+#endif
+ down(&start_stop_sem);
+ // look for a free place in the usbduxfast array
+ index = -1;
+ for (i = 0; i < NUMUSBDUXFAST; i++) {
+ if (!(usbduxfastsub[i].probed)) {
+ index = i;
+ break;
+ }
+ }
+
+ // no more space
+ if (index == -1) {
+ printk("Too many usbduxfast-devices connected.\n");
+ up(&start_stop_sem);
+ return PROBE_ERR_RETURN(-EMFILE);
+ }
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi_: usbduxfast: usbduxfastsub[%d] is ready to connect to comedi.\n", index);
+#endif
+
+ init_MUTEX(&(usbduxfastsub[index].sem));
+ // save a pointer to the usb device
+ usbduxfastsub[index].usbdev = udev;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ // save the interface number
+ usbduxfastsub[index].ifnum = interfnum;
+#else
+ // 2.6: save the interface itself
+ usbduxfastsub[index].interface = uinterf;
+ // get the interface number from the interface
+ usbduxfastsub[index].ifnum = uinterf->altsetting->desc.bInterfaceNumber;
+ // hand the private data over to the usb subsystem
+ // will be needed for disconnect
+ usb_set_intfdata(uinterf, &(usbduxfastsub[index]));
+#endif
+
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi_: usbduxfast: ifnum=%d\n", usbduxfastsub[index].ifnum);
+#endif
+ // create space for the commands going to the usb device
+ usbduxfastsub[index].dux_commands = kmalloc(SIZEOFDUXBUFFER,
+ GFP_KERNEL);
+ if (!usbduxfastsub[index].dux_commands) {
+ printk("comedi_: usbduxfast: error alloc space for dac commands\n");
+ tidy_up(&(usbduxfastsub[index]));
+ up(&start_stop_sem);
+ return PROBE_ERR_RETURN(-ENOMEM);
+ }
+ // create space of the instruction buffer
+ usbduxfastsub[index].insnBuffer = kmalloc(SIZEINSNBUF, GFP_KERNEL);
+ if (!(usbduxfastsub[index].insnBuffer)) {
+ printk("comedi_: usbduxfast: could not alloc space for insnBuffer\n");
+ tidy_up(&(usbduxfastsub[index]));
+ up(&start_stop_sem);
+ return PROBE_ERR_RETURN(-ENOMEM);
+ }
+ // setting to alternate setting 1: enabling bulk ep
+ i = usb_set_interface(usbduxfastsub[index].usbdev,
+ usbduxfastsub[index].ifnum, 1);
+ if (i < 0) {
+ printk("comedi_: usbduxfast%d: could not switch to alternate setting 1.\n", index);
+ tidy_up(&(usbduxfastsub[index]));
+ up(&start_stop_sem);
+ return PROBE_ERR_RETURN(-ENODEV);
+ }
+ usbduxfastsub[index].urbIn = USB_ALLOC_URB(0);
+ if (usbduxfastsub[index].urbIn == NULL) {
+ printk("comedi_: usbduxfast%d: Could not alloc. urb\n", index);
+ tidy_up(&(usbduxfastsub[index]));
+ up(&start_stop_sem);
+ return PROBE_ERR_RETURN(-ENOMEM);
+ }
+ usbduxfastsub[index].transfer_buffer = kmalloc(SIZEINBUF, GFP_KERNEL);
+ if (!(usbduxfastsub[index].transfer_buffer)) {
+ printk("comedi_: usbduxfast%d: could not alloc. transb.\n",
+ index);
+ tidy_up(&(usbduxfastsub[index]));
+ up(&start_stop_sem);
+ return PROBE_ERR_RETURN(-ENOMEM);
+ }
+ // we've reached the bottom of the function
+ usbduxfastsub[index].probed = 1;
+ up(&start_stop_sem);
+ printk("comedi_: usbduxfast%d has been successfully initialized.\n",
+ index);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ return (void *)(&usbduxfastsub[index]);
+#else
+ // success
+ return 0;
+#endif
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+static void usbduxfastsub_disconnect(struct usb_device *udev, void *ptr)
+{
+ usbduxfastsub_t *usbduxfastsub_tmp = (usbduxfastsub_t *) ptr;
+#else
+static void usbduxfastsub_disconnect(struct usb_interface *intf)
+{
+ usbduxfastsub_t *usbduxfastsub_tmp = usb_get_intfdata(intf);
+ struct usb_device *udev = interface_to_usbdev(intf);
+#endif
+ if (!usbduxfastsub_tmp) {
+ printk("comedi_: usbduxfast: disconnect called with null pointer.\n");
+ return;
+ }
+ if (usbduxfastsub_tmp->usbdev != udev) {
+ printk("comedi_: usbduxfast: BUG! called with wrong ptr!!!\n");
+ return;
+ }
+ down(&start_stop_sem);
+ down(&usbduxfastsub_tmp->sem);
+ tidy_up(usbduxfastsub_tmp);
+ up(&usbduxfastsub_tmp->sem);
+ up(&start_stop_sem);
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi_: usbduxfast: disconnected from the usb\n");
+#endif
+}
+
+// is called when comedi-config is called
+static int usbduxfast_attach(comedi_device * dev, comedi_devconfig * it)
+{
+ int ret;
+ int index;
+ int i;
+ comedi_subdevice *s = NULL;
+ dev->private = NULL;
+
+ down(&start_stop_sem);
+ // find a valid device which has been detected by the probe function of the usb
+ index = -1;
+ for (i = 0; i < NUMUSBDUXFAST; i++) {
+ if ((usbduxfastsub[i].probed) && (!usbduxfastsub[i].attached)) {
+ index = i;
+ break;
+ }
+ }
+
+ if (index < 0) {
+ printk("comedi%d: usbduxfast: error: attach failed, no usbduxfast devs connected to the usb bus.\n", dev->minor);
+ up(&start_stop_sem);
+ return -ENODEV;
+ }
+
+ down(&(usbduxfastsub[index].sem));
+ // pointer back to the corresponding comedi device
+ usbduxfastsub[index].comedidev = dev;
+
+ // trying to upload the firmware into the chip
+ if (comedi_aux_data(it->options, 0) &&
+ it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH]) {
+ read_firmware(usbduxfastsub,
+ comedi_aux_data(it->options, 0),
+ it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH]);
+ }
+
+ dev->board_name = BOARDNAME;
+
+ /* set number of subdevices */
+ dev->n_subdevices = N_SUBDEVICES;
+
+ // allocate space for the subdevices
+ if ((ret = alloc_subdevices(dev, N_SUBDEVICES)) < 0) {
+ printk("comedi%d: usbduxfast: error alloc space for subdev\n",
+ dev->minor);
+ up(&start_stop_sem);
+ return ret;
+ }
+
+ printk("comedi%d: usbduxfast: usb-device %d is attached to comedi.\n",
+ dev->minor, index);
+ // private structure is also simply the usb-structure
+ dev->private = usbduxfastsub + index;
+ // the first subdevice is the A/D converter
+ s = dev->subdevices + SUBDEV_AD;
+ // the URBs get the comedi subdevice
+ // which is responsible for reading
+ // this is the subdevice which reads data
+ dev->read_subdev = s;
+ // the subdevice receives as private structure the
+ // usb-structure
+ s->private = NULL;
+ // analog input
+ s->type = COMEDI_SUBD_AI;
+ // readable and ref is to ground
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
+ // 16 channels
+ s->n_chan = 16;
+ // length of the channellist
+ s->len_chanlist = 16;
+ // callback functions
+ s->insn_read = usbduxfast_ai_insn_read;
+ s->do_cmdtest = usbduxfast_ai_cmdtest;
+ s->do_cmd = usbduxfast_ai_cmd;
+ s->cancel = usbduxfast_ai_cancel;
+ // max value from the A/D converter (12bit+1 bit for overflow)
+ s->maxdata = 0x1000;
+ // range table to convert to physical units
+ s->range_table = &range_usbduxfast_ai_range;
+
+ // finally decide that it's attached
+ usbduxfastsub[index].attached = 1;
+
+ up(&(usbduxfastsub[index].sem));
+
+ up(&start_stop_sem);
+
+ printk("comedi%d: successfully attached to usbduxfast.\n", dev->minor);
+
+ return 0;
+}
+
+static int usbduxfast_detach(comedi_device * dev)
+{
+ usbduxfastsub_t *usbduxfastsub_tmp;
+
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi%d: usbduxfast: detach usb device\n", dev->minor);
+#endif
+
+ if (!dev) {
+ printk("comedi?: usbduxfast: detach without dev variable...\n");
+ return -EFAULT;
+ }
+
+ usbduxfastsub_tmp = dev->private;
+ if (!usbduxfastsub_tmp) {
+ printk("comedi?: usbduxfast: detach without ptr to usbduxfastsub[]\n");
+ return -EFAULT;
+ }
+
+ down(&usbduxfastsub_tmp->sem);
+ down(&start_stop_sem);
+ // Don't allow detach to free the private structure
+ // It's one entry of of usbduxfastsub[]
+ dev->private = NULL;
+ usbduxfastsub_tmp->attached = 0;
+ usbduxfastsub_tmp->comedidev = NULL;
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi%d: usbduxfast: detach: successfully removed\n",
+ dev->minor);
+#endif
+ up(&start_stop_sem);
+ up(&usbduxfastsub_tmp->sem);
+ return 0;
+}
+
+/* main driver struct */
+static comedi_driver driver_usbduxfast = {
+ driver_name:"usbduxfast",
+ module:THIS_MODULE,
+ attach:usbduxfast_attach,
+ detach:usbduxfast_detach,
+};
+
+static void init_usb_devices(void)
+{
+ int index;
+#ifdef CONFIG_COMEDI_DEBUG
+ printk("comedi_: usbduxfast: setting all possible devs to invalid\n");
+#endif
+ // all devices entries are invalid to begin with
+ // they will become valid by the probe function
+ // and then finally by the attach-function
+ for (index = 0; index < NUMUSBDUXFAST; index++) {
+ memset(&(usbduxfastsub[index]), 0x00,
+ sizeof(usbduxfastsub[index]));
+ init_MUTEX(&(usbduxfastsub[index].sem));
+ }
+}
+
+// Table with the USB-devices: just now only testing IDs
+static struct usb_device_id usbduxfastsub_table[] = {
+ // { USB_DEVICE(0x4b4, 0x8613), //testing
+ // },
+ {USB_DEVICE(0x13d8, 0x0010) //real ID
+ },
+ {USB_DEVICE(0x13d8, 0x0011) //real ID
+ },
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, usbduxfastsub_table);
+
+// The usbduxfastsub-driver
+static struct usb_driver usbduxfastsub_driver = {
+#ifdef COMEDI_HAVE_USB_DRIVER_OWNER
+ owner:THIS_MODULE,
+#endif
+ name:BOARDNAME,
+ probe:usbduxfastsub_probe,
+ disconnect:usbduxfastsub_disconnect,
+ id_table:usbduxfastsub_table,
+};
+
+// Can't use the nice macro as I have also to initialise the USB
+// subsystem:
+// registering the usb-system _and_ the comedi-driver
+static int init_usbduxfast(void)
+{
+ printk(KERN_INFO KBUILD_MODNAME ": "
+ DRIVER_VERSION ":" DRIVER_DESC "\n");
+ init_usb_devices();
+ usb_register(&usbduxfastsub_driver);
+ comedi_driver_register(&driver_usbduxfast);
+ return 0;
+}
+
+// deregistering the comedi driver and the usb-subsystem
+static void exit_usbduxfast(void)
+{
+ comedi_driver_unregister(&driver_usbduxfast);
+ usb_deregister(&usbduxfastsub_driver);
+}
+
+module_init(init_usbduxfast);
+module_exit(exit_usbduxfast);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/interrupt.h b/drivers/staging/comedi/interrupt.h
new file mode 100644
index 000000000000..16532ec7a4b1
--- /dev/null
+++ b/drivers/staging/comedi/interrupt.h
@@ -0,0 +1,67 @@
+/*
+ linux/interrupt.h compatibility header
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __COMPAT_LINUX_INTERRUPT_H_
+#define __COMPAT_LINUX_INTERRUPT_H_
+
+#include <linux/interrupt.h>
+
+#include <linux/version.h>
+
+#ifndef IRQ_NONE
+typedef void irqreturn_t;
+#define IRQ_NONE
+#define IRQ_HANDLED
+#define IRQ_RETVAL(x) (void)(x)
+#endif
+
+#ifndef IRQF_DISABLED
+#define IRQF_DISABLED SA_INTERRUPT
+#define IRQF_SAMPLE_RANDOM SA_SAMPLE_RANDOM
+#define IRQF_SHARED SA_SHIRQ
+#define IRQF_PROBE_SHARED SA_PROBEIRQ
+#define IRQF_PERCPU SA_PERCPU
+#ifdef SA_TRIGGER_MASK
+#define IRQF_TRIGGER_NONE 0
+#define IRQF_TRIGGER_LOW SA_TRIGGER_LOW
+#define IRQF_TRIGGER_HIGH SA_TRIGGER_HIGH
+#define IRQF_TRIGGER_FALLING SA_TRIGGER_FALLING
+#define IRQF_TRIGGER_RISING SA_TRIGGER_RISING
+#define IRQF_TRIGGER_MASK SA_TRIGGER_MASK
+#else
+#define IRQF_TRIGGER_NONE 0
+#define IRQF_TRIGGER_LOW 0
+#define IRQF_TRIGGER_HIGH 0
+#define IRQF_TRIGGER_FALLING 0
+#define IRQF_TRIGGER_RISING 0
+#define IRQF_TRIGGER_MASK 0
+#endif
+#endif
+
+/* if interrupt handler prototype has pt_regs* parameter */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+#define PT_REGS_ARG , struct pt_regs *regs
+#define PT_REGS_CALL , regs
+#define PT_REGS_NULL , NULL
+#else
+#define PT_REGS_ARG
+#define PT_REGS_CALL
+#define PT_REGS_NULL
+#endif
+
+#endif
diff --git a/drivers/staging/comedi/kcomedilib/Makefile b/drivers/staging/comedi/kcomedilib/Makefile
new file mode 100644
index 000000000000..ffcc9ad32adb
--- /dev/null
+++ b/drivers/staging/comedi/kcomedilib/Makefile
@@ -0,0 +1,8 @@
+obj-$(CONFIG_COMEDI) += kcomedilib.o
+
+kcomedilib-objs := \
+ data.o \
+ ksyms.o \
+ dio.o \
+ kcomedilib_main.o \
+ get.o
diff --git a/drivers/staging/comedi/kcomedilib/data.c b/drivers/staging/comedi/kcomedilib/data.c
new file mode 100644
index 000000000000..79aec2041150
--- /dev/null
+++ b/drivers/staging/comedi/kcomedilib/data.c
@@ -0,0 +1,89 @@
+/*
+ kcomedilib/data.c
+ implements comedi_data_*() functions
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "../comedi.h"
+#include "../comedilib.h"
+#include "../comedidev.h" /* for comedi_udelay() */
+
+#include <linux/string.h>
+
+int comedi_data_write(comedi_t * dev, unsigned int subdev, unsigned int chan,
+ unsigned int range, unsigned int aref, lsampl_t data)
+{
+ comedi_insn insn;
+
+ memset(&insn, 0, sizeof(insn));
+ insn.insn = INSN_WRITE;
+ insn.n = 1;
+ insn.data = &data;
+ insn.subdev = subdev;
+ insn.chanspec = CR_PACK(chan, range, aref);
+
+ return comedi_do_insn(dev, &insn);
+}
+
+int comedi_data_read(comedi_t * dev, unsigned int subdev, unsigned int chan,
+ unsigned int range, unsigned int aref, lsampl_t * data)
+{
+ comedi_insn insn;
+
+ memset(&insn, 0, sizeof(insn));
+ insn.insn = INSN_READ;
+ insn.n = 1;
+ insn.data = data;
+ insn.subdev = subdev;
+ insn.chanspec = CR_PACK(chan, range, aref);
+
+ return comedi_do_insn(dev, &insn);
+}
+
+int comedi_data_read_hint(comedi_t * dev, unsigned int subdev,
+ unsigned int chan, unsigned int range, unsigned int aref)
+{
+ comedi_insn insn;
+ lsampl_t dummy_data;
+
+ memset(&insn, 0, sizeof(insn));
+ insn.insn = INSN_READ;
+ insn.n = 0;
+ insn.data = &dummy_data;
+ insn.subdev = subdev;
+ insn.chanspec = CR_PACK(chan, range, aref);
+
+ return comedi_do_insn(dev, &insn);
+}
+
+int comedi_data_read_delayed(comedi_t * dev, unsigned int subdev,
+ unsigned int chan, unsigned int range, unsigned int aref,
+ lsampl_t * data, unsigned int nano_sec)
+{
+ int retval;
+
+ retval = comedi_data_read_hint(dev, subdev, chan, range, aref);
+ if (retval < 0)
+ return retval;
+
+ comedi_udelay((nano_sec + 999) / 1000);
+
+ return comedi_data_read(dev, subdev, chan, range, aref, data);
+}
diff --git a/drivers/staging/comedi/kcomedilib/dio.c b/drivers/staging/comedi/kcomedilib/dio.c
new file mode 100644
index 000000000000..a9f488aed369
--- /dev/null
+++ b/drivers/staging/comedi/kcomedilib/dio.c
@@ -0,0 +1,95 @@
+/*
+ kcomedilib/dio.c
+ implements comedi_dio_*() functions
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "../comedi.h"
+#include "../comedilib.h"
+
+#include <linux/string.h>
+
+int comedi_dio_config(comedi_t * dev, unsigned int subdev, unsigned int chan,
+ unsigned int io)
+{
+ comedi_insn insn;
+
+ memset(&insn, 0, sizeof(insn));
+ insn.insn = INSN_CONFIG;
+ insn.n = 1;
+ insn.data = &io;
+ insn.subdev = subdev;
+ insn.chanspec = CR_PACK(chan, 0, 0);
+
+ return comedi_do_insn(dev, &insn);
+}
+
+int comedi_dio_read(comedi_t * dev, unsigned int subdev, unsigned int chan,
+ unsigned int *val)
+{
+ comedi_insn insn;
+
+ memset(&insn, 0, sizeof(insn));
+ insn.insn = INSN_READ;
+ insn.n = 1;
+ insn.data = val;
+ insn.subdev = subdev;
+ insn.chanspec = CR_PACK(chan, 0, 0);
+
+ return comedi_do_insn(dev, &insn);
+}
+
+int comedi_dio_write(comedi_t * dev, unsigned int subdev, unsigned int chan,
+ unsigned int val)
+{
+ comedi_insn insn;
+
+ memset(&insn, 0, sizeof(insn));
+ insn.insn = INSN_WRITE;
+ insn.n = 1;
+ insn.data = &val;
+ insn.subdev = subdev;
+ insn.chanspec = CR_PACK(chan, 0, 0);
+
+ return comedi_do_insn(dev, &insn);
+}
+
+int comedi_dio_bitfield(comedi_t * dev, unsigned int subdev, unsigned int mask,
+ unsigned int *bits)
+{
+ comedi_insn insn;
+ lsampl_t data[2];
+ int ret;
+
+ memset(&insn, 0, sizeof(insn));
+ insn.insn = INSN_BITS;
+ insn.n = 2;
+ insn.data = data;
+ insn.subdev = subdev;
+
+ data[0] = mask;
+ data[1] = *bits;
+
+ ret = comedi_do_insn(dev, &insn);
+
+ *bits = data[1];
+
+ return ret;
+}
diff --git a/drivers/staging/comedi/kcomedilib/get.c b/drivers/staging/comedi/kcomedilib/get.c
new file mode 100644
index 000000000000..2004ad4480c0
--- /dev/null
+++ b/drivers/staging/comedi/kcomedilib/get.c
@@ -0,0 +1,294 @@
+/*
+ kcomedilib/get.c
+ a comedlib interface for kernel modules
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#define __NO_VERSION__
+#include "../comedi.h"
+#include "../comedilib.h"
+#include "../comedidev.h"
+
+int comedi_get_n_subdevices(comedi_t * d)
+{
+ comedi_device *dev = (comedi_device *) d;
+
+ return dev->n_subdevices;
+}
+
+int comedi_get_version_code(comedi_t * d)
+{
+ return COMEDI_VERSION_CODE;
+}
+
+const char *comedi_get_driver_name(comedi_t * d)
+{
+ comedi_device *dev = (comedi_device *) d;
+
+ return dev->driver->driver_name;
+}
+
+const char *comedi_get_board_name(comedi_t * d)
+{
+ comedi_device *dev = (comedi_device *) d;
+
+ return dev->board_name;
+}
+
+int comedi_get_subdevice_type(comedi_t * d, unsigned int subdevice)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s = dev->subdevices + subdevice;
+
+ return s->type;
+}
+
+unsigned int comedi_get_subdevice_flags(comedi_t * d, unsigned int subdevice)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s = dev->subdevices + subdevice;
+
+ return s->subdev_flags;
+}
+
+int comedi_find_subdevice_by_type(comedi_t * d, int type, unsigned int subd)
+{
+ comedi_device *dev = (comedi_device *) d;
+
+ if (subd > dev->n_subdevices)
+ return -ENODEV;
+
+ for (; subd < dev->n_subdevices; subd++) {
+ if (dev->subdevices[subd].type == type)
+ return subd;
+ }
+ return -1;
+}
+
+int comedi_get_n_channels(comedi_t * d, unsigned int subdevice)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s = dev->subdevices + subdevice;
+
+ return s->n_chan;
+}
+
+int comedi_get_len_chanlist(comedi_t * d, unsigned int subdevice)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s = dev->subdevices + subdevice;
+
+ return s->len_chanlist;
+}
+
+lsampl_t comedi_get_maxdata(comedi_t * d, unsigned int subdevice,
+ unsigned int chan)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s = dev->subdevices + subdevice;
+
+ if (s->maxdata_list)
+ return s->maxdata_list[chan];
+
+ return s->maxdata;
+}
+
+#ifdef KCOMEDILIB_DEPRECATED
+int comedi_get_rangetype(comedi_t * d, unsigned int subdevice,
+ unsigned int chan)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s = dev->subdevices + subdevice;
+ int ret;
+
+ if (s->range_table_list) {
+ ret = s->range_table_list[chan]->length;
+ } else {
+ ret = s->range_table->length;
+ }
+
+ ret = ret | (dev->minor << 28) | (subdevice << 24) | (chan << 16);
+
+ return ret;
+}
+#endif
+
+int comedi_get_n_ranges(comedi_t * d, unsigned int subdevice, unsigned int chan)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s = dev->subdevices + subdevice;
+ int ret;
+
+ if (s->range_table_list) {
+ ret = s->range_table_list[chan]->length;
+ } else {
+ ret = s->range_table->length;
+ }
+
+ return ret;
+}
+
+/*
+ * ALPHA (non-portable)
+*/
+int comedi_get_krange(comedi_t * d, unsigned int subdevice, unsigned int chan,
+ unsigned int range, comedi_krange * krange)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s = dev->subdevices + subdevice;
+ const comedi_lrange *lr;
+
+ if (s->range_table_list) {
+ lr = s->range_table_list[chan];
+ } else {
+ lr = s->range_table;
+ }
+ if (range >= lr->length) {
+ return -EINVAL;
+ }
+ memcpy(krange, lr->range + range, sizeof(comedi_krange));
+
+ return 0;
+}
+
+/*
+ * ALPHA (may be renamed)
+*/
+unsigned int comedi_get_buf_head_pos(comedi_t * d, unsigned int subdevice)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s = dev->subdevices + subdevice;
+ comedi_async *async;
+
+ async = s->async;
+ if (async == NULL)
+ return 0;
+
+ return async->buf_write_count;
+}
+
+int comedi_get_buffer_contents(comedi_t * d, unsigned int subdevice)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s = dev->subdevices + subdevice;
+ comedi_async *async;
+ unsigned int num_bytes;
+
+ if (subdevice >= dev->n_subdevices)
+ return -1;
+ async = s->async;
+ if (async == NULL)
+ return 0;
+ num_bytes = comedi_buf_read_n_available(s->async);
+ return num_bytes;
+}
+
+/*
+ * ALPHA
+*/
+int comedi_set_user_int_count(comedi_t * d, unsigned int subdevice,
+ unsigned int buf_user_count)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s = dev->subdevices + subdevice;
+ comedi_async *async;
+ int num_bytes;
+
+ async = s->async;
+ if (async == NULL)
+ return -1;
+
+ num_bytes = buf_user_count - async->buf_read_count;
+ if (num_bytes < 0)
+ return -1;
+ comedi_buf_read_alloc(async, num_bytes);
+ comedi_buf_read_free(async, num_bytes);
+
+ return 0;
+}
+
+int comedi_mark_buffer_read(comedi_t * d, unsigned int subdevice,
+ unsigned int num_bytes)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s = dev->subdevices + subdevice;
+ comedi_async *async;
+
+ if (subdevice >= dev->n_subdevices)
+ return -1;
+ async = s->async;
+ if (async == NULL)
+ return -1;
+
+ comedi_buf_read_alloc(async, num_bytes);
+ comedi_buf_read_free(async, num_bytes);
+
+ return 0;
+}
+
+int comedi_mark_buffer_written(comedi_t * d, unsigned int subdevice,
+ unsigned int num_bytes)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s = dev->subdevices + subdevice;
+ comedi_async *async;
+ int bytes_written;
+
+ if (subdevice >= dev->n_subdevices)
+ return -1;
+ async = s->async;
+ if (async == NULL)
+ return -1;
+ bytes_written = comedi_buf_write_alloc(async, num_bytes);
+ comedi_buf_write_free(async, bytes_written);
+ if (bytes_written != num_bytes)
+ return -1;
+ return 0;
+}
+
+int comedi_get_buffer_size(comedi_t * d, unsigned int subdev)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s = dev->subdevices + subdev;
+ comedi_async *async;
+
+ if (subdev >= dev->n_subdevices)
+ return -1;
+ async = s->async;
+ if (async == NULL)
+ return 0;
+
+ return async->prealloc_bufsz;
+}
+
+int comedi_get_buffer_offset(comedi_t * d, unsigned int subdevice)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s = dev->subdevices + subdevice;
+ comedi_async *async;
+
+ if (subdevice >= dev->n_subdevices)
+ return -1;
+ async = s->async;
+ if (async == NULL)
+ return 0;
+
+ return async->buf_read_ptr;
+}
diff --git a/drivers/staging/comedi/kcomedilib/kcomedilib_main.c b/drivers/staging/comedi/kcomedilib/kcomedilib_main.c
new file mode 100644
index 000000000000..510fbdcec493
--- /dev/null
+++ b/drivers/staging/comedi/kcomedilib/kcomedilib_main.c
@@ -0,0 +1,567 @@
+/*
+ kcomedilib/kcomedilib.c
+ a comedlib interface for kernel modules
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+
+#include "../comedi.h"
+#include "../comedilib.h"
+#include "../comedidev.h"
+
+MODULE_AUTHOR("David Schleef <ds@schleef.org>");
+MODULE_DESCRIPTION("Comedi kernel library");
+MODULE_LICENSE("GPL");
+
+comedi_t *comedi_open(const char *filename)
+{
+ struct comedi_device_file_info *dev_file_info;
+ comedi_device *dev;
+ unsigned int minor;
+
+ if (strncmp(filename, "/dev/comedi", 11) != 0)
+ return NULL;
+
+ minor = simple_strtoul(filename + 11, NULL, 0);
+
+ if (minor >= COMEDI_NUM_BOARD_MINORS)
+ return NULL;
+
+ dev_file_info = comedi_get_device_file_info(minor);
+ if(dev_file_info == NULL)
+ return NULL;
+ dev = dev_file_info->device;
+
+ if(dev == NULL || !dev->attached)
+ return NULL;
+
+ if (!try_module_get(dev->driver->module))
+ return NULL;
+
+ return (comedi_t *) dev;
+}
+
+comedi_t *comedi_open_old(unsigned int minor)
+{
+ struct comedi_device_file_info *dev_file_info;
+ comedi_device *dev;
+
+ if (minor >= COMEDI_NUM_MINORS)
+ return NULL;
+
+ dev_file_info = comedi_get_device_file_info(minor);
+ if(dev_file_info == NULL)
+ return NULL;
+ dev = dev_file_info->device;
+
+ if(dev == NULL || !dev->attached)
+ return NULL;
+
+ return (comedi_t *) dev;
+}
+
+int comedi_close(comedi_t * d)
+{
+ comedi_device *dev = (comedi_device *) d;
+
+ module_put(dev->driver->module);
+
+ return 0;
+}
+
+int comedi_loglevel(int newlevel)
+{
+ return 0;
+}
+
+void comedi_perror(const char *message)
+{
+ rt_printk("%s: unknown error\n", message);
+}
+
+char *comedi_strerror(int err)
+{
+ return "unknown error";
+}
+
+int comedi_fileno(comedi_t * d)
+{
+ comedi_device *dev = (comedi_device *) d;
+
+ /* return something random */
+ return dev->minor;
+}
+
+int comedi_command(comedi_t * d, comedi_cmd * cmd)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s;
+ comedi_async *async;
+ unsigned runflags;
+
+ if (cmd->subdev >= dev->n_subdevices)
+ return -ENODEV;
+
+ s = dev->subdevices + cmd->subdev;
+ if (s->type == COMEDI_SUBD_UNUSED)
+ return -EIO;
+
+ async = s->async;
+ if (async == NULL)
+ return -ENODEV;
+
+ if (s->busy)
+ return -EBUSY;
+ s->busy = d;
+
+ if (async->cb_mask & COMEDI_CB_EOS)
+ cmd->flags |= TRIG_WAKE_EOS;
+
+ async->cmd = *cmd;
+
+ runflags = SRF_RUNNING;
+
+#ifdef CONFIG_COMEDI_RT
+ if (comedi_switch_to_rt(dev) == 0)
+ runflags |= SRF_RT;
+#endif
+ comedi_set_subdevice_runflags(s, ~0, runflags);
+
+ comedi_reset_async_buf(async);
+
+ return s->do_cmd(dev, s);
+}
+
+int comedi_command_test(comedi_t * d, comedi_cmd * cmd)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s;
+
+ if (cmd->subdev >= dev->n_subdevices)
+ return -ENODEV;
+
+ s = dev->subdevices + cmd->subdev;
+ if (s->type == COMEDI_SUBD_UNUSED)
+ return -EIO;
+
+ if (s->async == NULL)
+ return -ENODEV;
+
+ return s->do_cmdtest(dev, s, cmd);
+}
+
+/*
+ * COMEDI_INSN
+ * perform an instruction
+ */
+int comedi_do_insn(comedi_t * d, comedi_insn * insn)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s;
+ int ret = 0;
+
+ if (insn->insn & INSN_MASK_SPECIAL) {
+ switch (insn->insn) {
+ case INSN_GTOD:
+ {
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+ insn->data[0] = tv.tv_sec;
+ insn->data[1] = tv.tv_usec;
+ ret = 2;
+
+ break;
+ }
+ case INSN_WAIT:
+ /* XXX isn't the value supposed to be nanosecs? */
+ if (insn->n != 1 || insn->data[0] >= 100) {
+ ret = -EINVAL;
+ break;
+ }
+ comedi_udelay(insn->data[0]);
+ ret = 1;
+ break;
+ case INSN_INTTRIG:
+ if (insn->n != 1) {
+ ret = -EINVAL;
+ break;
+ }
+ if (insn->subdev >= dev->n_subdevices) {
+ rt_printk("%d not usable subdevice\n",
+ insn->subdev);
+ ret = -EINVAL;
+ break;
+ }
+ s = dev->subdevices + insn->subdev;
+ if (!s->async) {
+ rt_printk("no async\n");
+ ret = -EINVAL;
+ break;
+ }
+ if (!s->async->inttrig) {
+ rt_printk("no inttrig\n");
+ ret = -EAGAIN;
+ break;
+ }
+ ret = s->async->inttrig(dev, s, insn->data[0]);
+ if (ret >= 0)
+ ret = 1;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ } else {
+ /* a subdevice instruction */
+ if (insn->subdev >= dev->n_subdevices) {
+ ret = -EINVAL;
+ goto error;
+ }
+ s = dev->subdevices + insn->subdev;
+
+ if (s->type == COMEDI_SUBD_UNUSED) {
+ rt_printk("%d not useable subdevice\n", insn->subdev);
+ ret = -EIO;
+ goto error;
+ }
+
+ /* XXX check lock */
+
+ if ((ret = check_chanlist(s, 1, &insn->chanspec)) < 0) {
+ rt_printk("bad chanspec\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (s->busy) {
+ ret = -EBUSY;
+ goto error;
+ }
+ s->busy = d;
+
+ switch (insn->insn) {
+ case INSN_READ:
+ ret = s->insn_read(dev, s, insn, insn->data);
+ break;
+ case INSN_WRITE:
+ ret = s->insn_write(dev, s, insn, insn->data);
+ break;
+ case INSN_BITS:
+ ret = s->insn_bits(dev, s, insn, insn->data);
+ break;
+ case INSN_CONFIG:
+ /* XXX should check instruction length */
+ ret = s->insn_config(dev, s, insn, insn->data);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ s->busy = NULL;
+ }
+ if (ret < 0)
+ goto error;
+#if 0
+ /* XXX do we want this? -- abbotti #if'ed it out for now. */
+ if (ret != insn->n) {
+ rt_printk("BUG: result of insn != insn.n\n");
+ ret = -EINVAL;
+ goto error;
+ }
+#endif
+ error:
+
+ return ret;
+}
+
+/*
+ COMEDI_LOCK
+ lock subdevice
+
+ arg:
+ subdevice number
+
+ reads:
+ none
+
+ writes:
+ none
+
+ necessary locking:
+ - ioctl/rt lock (this type)
+ - lock while subdevice busy
+ - lock while subdevice being programmed
+
+*/
+int comedi_lock(comedi_t * d, unsigned int subdevice)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s;
+ unsigned long flags;
+ int ret = 0;
+
+ if (subdevice >= dev->n_subdevices) {
+ return -EINVAL;
+ }
+ s = dev->subdevices + subdevice;
+
+ comedi_spin_lock_irqsave(&s->spin_lock, flags);
+
+ if (s->busy) {
+ ret = -EBUSY;
+ } else {
+ if (s->lock) {
+ ret = -EBUSY;
+ } else {
+ s->lock = d;
+ }
+ }
+
+ comedi_spin_unlock_irqrestore(&s->spin_lock, flags);
+
+ return ret;
+}
+
+/*
+ COMEDI_UNLOCK
+ unlock subdevice
+
+ arg:
+ subdevice number
+
+ reads:
+ none
+
+ writes:
+ none
+
+*/
+int comedi_unlock(comedi_t * d, unsigned int subdevice)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s;
+ unsigned long flags;
+ comedi_async *async;
+ int ret;
+
+ if (subdevice >= dev->n_subdevices) {
+ return -EINVAL;
+ }
+ s = dev->subdevices + subdevice;
+
+ async = s->async;
+
+ comedi_spin_lock_irqsave(&s->spin_lock, flags);
+
+ if (s->busy) {
+ ret = -EBUSY;
+ } else if (s->lock && s->lock != (void *)d) {
+ ret = -EACCES;
+ } else {
+ s->lock = NULL;
+
+ if (async) {
+ async->cb_mask = 0;
+ async->cb_func = NULL;
+ async->cb_arg = NULL;
+ }
+
+ ret = 0;
+ }
+
+ comedi_spin_unlock_irqrestore(&s->spin_lock, flags);
+
+ return ret;
+}
+
+/*
+ COMEDI_CANCEL
+ cancel acquisition ioctl
+
+ arg:
+ subdevice number
+
+ reads:
+ nothing
+
+ writes:
+ nothing
+
+*/
+int comedi_cancel(comedi_t * d, unsigned int subdevice)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s;
+ int ret = 0;
+
+ if (subdevice >= dev->n_subdevices) {
+ return -EINVAL;
+ }
+ s = dev->subdevices + subdevice;
+
+ if (s->lock && s->lock != d)
+ return -EACCES;
+
+#if 0
+ if (!s->busy)
+ return 0;
+
+ if (s->busy != d)
+ return -EBUSY;
+#endif
+
+ if (!s->cancel || !s->async)
+ return -EINVAL;
+
+ if ((ret = s->cancel(dev, s)))
+ return ret;
+
+#ifdef CONFIG_COMEDI_RT
+ if (comedi_get_subdevice_runflags(s) & SRF_RT) {
+ comedi_switch_to_non_rt(dev);
+ }
+#endif
+ comedi_set_subdevice_runflags(s, SRF_RUNNING | SRF_RT, 0);
+ s->async->inttrig = NULL;
+ s->busy = NULL;
+
+ return 0;
+}
+
+/*
+ registration of callback functions
+ */
+int comedi_register_callback(comedi_t * d, unsigned int subdevice,
+ unsigned int mask, int (*cb) (unsigned int, void *), void *arg)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s;
+ comedi_async *async;
+
+ if (subdevice >= dev->n_subdevices) {
+ return -EINVAL;
+ }
+ s = dev->subdevices + subdevice;
+
+ async = s->async;
+ if (s->type == COMEDI_SUBD_UNUSED || !async)
+ return -EIO;
+
+ /* are we locked? (ioctl lock) */
+ if (s->lock && s->lock != d)
+ return -EACCES;
+
+ /* are we busy? */
+ if (s->busy)
+ return -EBUSY;
+
+ if (!mask) {
+ async->cb_mask = 0;
+ async->cb_func = NULL;
+ async->cb_arg = NULL;
+ } else {
+ async->cb_mask = mask;
+ async->cb_func = cb;
+ async->cb_arg = arg;
+ }
+
+ return 0;
+}
+
+int comedi_poll(comedi_t * d, unsigned int subdevice)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s = dev->subdevices;
+ comedi_async *async;
+
+ if (subdevice >= dev->n_subdevices) {
+ return -EINVAL;
+ }
+ s = dev->subdevices + subdevice;
+
+ async = s->async;
+ if (s->type == COMEDI_SUBD_UNUSED || !async)
+ return -EIO;
+
+ /* are we locked? (ioctl lock) */
+ if (s->lock && s->lock != d)
+ return -EACCES;
+
+ /* are we running? XXX wrong? */
+ if (!s->busy)
+ return -EIO;
+
+ return s->poll(dev, s);
+}
+
+/* WARNING: not portable */
+int comedi_map(comedi_t * d, unsigned int subdevice, void *ptr)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s;
+
+ if (subdevice >= dev->n_subdevices) {
+ return -EINVAL;
+ }
+ s = dev->subdevices + subdevice;
+
+ if (!s->async)
+ return -EINVAL;
+
+ if (ptr) {
+ *((void **)ptr) = s->async->prealloc_buf;
+ }
+
+ /* XXX no reference counting */
+
+ return 0;
+}
+
+/* WARNING: not portable */
+int comedi_unmap(comedi_t * d, unsigned int subdevice)
+{
+ comedi_device *dev = (comedi_device *) d;
+ comedi_subdevice *s;
+
+ if (subdevice >= dev->n_subdevices) {
+ return -EINVAL;
+ }
+ s = dev->subdevices + subdevice;
+
+ if (!s->async)
+ return -EINVAL;
+
+ /* XXX no reference counting */
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/kcomedilib/ksyms.c b/drivers/staging/comedi/kcomedilib/ksyms.c
new file mode 100644
index 000000000000..76b45063a9fc
--- /dev/null
+++ b/drivers/staging/comedi/kcomedilib/ksyms.c
@@ -0,0 +1,144 @@
+/*
+ comedi/kcomedilib/ksyms.c
+ a comedlib interface for kernel modules
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-2001 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef EXPORT_SYMTAB
+#define EXPORT_SYMTAB
+#endif
+
+#include "../comedi.h"
+#include "../comedilib.h"
+#include "../comedidev.h"
+
+#include <linux/module.h>
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+
+#if LINUX_VERSION_CODE >= 0x020200
+
+/* functions specific to kcomedilib */
+
+EXPORT_SYMBOL(comedi_register_callback);
+EXPORT_SYMBOL(comedi_get_krange);
+EXPORT_SYMBOL(comedi_get_buf_head_pos);
+EXPORT_SYMBOL(comedi_set_user_int_count);
+EXPORT_SYMBOL(comedi_map);
+EXPORT_SYMBOL(comedi_unmap);
+
+/* This list comes from user-space comedilib, to show which
+ * functions are not ported yet. */
+
+EXPORT_SYMBOL(comedi_open);
+EXPORT_SYMBOL(comedi_close);
+
+/* logging */
+EXPORT_SYMBOL(comedi_loglevel);
+EXPORT_SYMBOL(comedi_perror);
+EXPORT_SYMBOL(comedi_strerror);
+//EXPORT_SYMBOL(comedi_errno);
+EXPORT_SYMBOL(comedi_fileno);
+
+/* device queries */
+EXPORT_SYMBOL(comedi_get_n_subdevices);
+EXPORT_SYMBOL(comedi_get_version_code);
+EXPORT_SYMBOL(comedi_get_driver_name);
+EXPORT_SYMBOL(comedi_get_board_name);
+
+/* subdevice queries */
+EXPORT_SYMBOL(comedi_get_subdevice_type);
+EXPORT_SYMBOL(comedi_find_subdevice_by_type);
+EXPORT_SYMBOL(comedi_get_subdevice_flags);
+EXPORT_SYMBOL(comedi_get_n_channels);
+//EXPORT_SYMBOL(comedi_range_is_chan_specific);
+//EXPORT_SYMBOL(comedi_maxdata_is_chan_specific);
+
+/* channel queries */
+EXPORT_SYMBOL(comedi_get_maxdata);
+#ifdef KCOMEDILIB_DEPRECATED
+EXPORT_SYMBOL(comedi_get_rangetype);
+#endif
+EXPORT_SYMBOL(comedi_get_n_ranges);
+//EXPORT_SYMBOL(comedi_find_range);
+
+/* buffer queries */
+EXPORT_SYMBOL(comedi_get_buffer_size);
+//EXPORT_SYMBOL(comedi_get_max_buffer_size);
+//EXPORT_SYMBOL(comedi_set_buffer_size);
+EXPORT_SYMBOL(comedi_get_buffer_contents);
+EXPORT_SYMBOL(comedi_get_buffer_offset);
+
+/* low-level stuff */
+//EXPORT_SYMBOL(comedi_trigger);
+//EXPORT_SYMBOL(comedi_do_insnlist);
+EXPORT_SYMBOL(comedi_do_insn);
+EXPORT_SYMBOL(comedi_lock);
+EXPORT_SYMBOL(comedi_unlock);
+
+/* physical units */
+//EXPORT_SYMBOL(comedi_to_phys);
+//EXPORT_SYMBOL(comedi_from_phys);
+
+/* synchronous stuff */
+EXPORT_SYMBOL(comedi_data_read);
+EXPORT_SYMBOL(comedi_data_read_hint);
+EXPORT_SYMBOL(comedi_data_read_delayed);
+EXPORT_SYMBOL(comedi_data_write);
+EXPORT_SYMBOL(comedi_dio_config);
+EXPORT_SYMBOL(comedi_dio_read);
+EXPORT_SYMBOL(comedi_dio_write);
+EXPORT_SYMBOL(comedi_dio_bitfield);
+
+/* slowly varying stuff */
+//EXPORT_SYMBOL(comedi_sv_init);
+//EXPORT_SYMBOL(comedi_sv_update);
+//EXPORT_SYMBOL(comedi_sv_measure);
+
+/* commands */
+//EXPORT_SYMBOL(comedi_get_cmd_src_mask);
+//EXPORT_SYMBOL(comedi_get_cmd_generic_timed);
+EXPORT_SYMBOL(comedi_cancel);
+EXPORT_SYMBOL(comedi_command);
+EXPORT_SYMBOL(comedi_command_test);
+EXPORT_SYMBOL(comedi_poll);
+
+/* buffer configuration */
+EXPORT_SYMBOL(comedi_mark_buffer_read);
+EXPORT_SYMBOL(comedi_mark_buffer_written);
+
+//EXPORT_SYMBOL(comedi_get_range);
+EXPORT_SYMBOL(comedi_get_len_chanlist);
+
+/* deprecated */
+//EXPORT_SYMBOL(comedi_get_timer);
+//EXPORT_SYMBOL(comedi_timed_1chan);
+
+/* alpha */
+//EXPORT_SYMBOL(comedi_set_global_oor_behavior);
+
+#endif
diff --git a/drivers/staging/comedi/pci.h b/drivers/staging/comedi/pci.h
new file mode 100644
index 000000000000..962e4348ac22
--- /dev/null
+++ b/drivers/staging/comedi/pci.h
@@ -0,0 +1,31 @@
+/*
+ * linux/pci.h compatibility header
+ */
+
+#ifndef _COMPAT_PCI_H
+#define _COMPAT_PCI_H
+
+#include <linux/version.h>
+
+#include <linux/pci.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#define pci_get_device pci_find_device
+#define pci_get_subsys pci_find_subsys
+#define pci_dev_get(x) (x)
+#define pci_dev_put(x)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,22)
+static inline char *pci_name(struct pci_dev *pdev)
+{
+ return pdev->slot_name;
+}
+#endif
+
+#ifndef DEFINE_PCI_DEVICE_TABLE
+#define DEFINE_PCI_DEVICE_TABLE(_table) \
+ struct pci_device_id _table[]
+#endif
+
+#endif /* _COMPAT_PCI_H */
diff --git a/drivers/staging/comedi/pci_ids.h b/drivers/staging/comedi/pci_ids.h
new file mode 100644
index 000000000000..c61ba90f9601
--- /dev/null
+++ b/drivers/staging/comedi/pci_ids.h
@@ -0,0 +1,31 @@
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef __COMPAT_LINUX_PCI_IDS_H
+#define __COMPAT_LINUX_PCI_IDS_H
+
+#include <linux/pci_ids.h>
+
+#ifndef PCI_VENDOR_ID_AMCC
+#define PCI_VENDOR_ID_AMCC 0x10e8
+#endif
+
+#ifndef PCI_VENDOR_ID_CBOARDS
+#define PCI_VENDOR_ID_CBOARDS 0x1307
+#endif
+
+#ifndef PCI_VENDOR_ID_QUANCOM
+#define PCI_VENDOR_ID_QUANCOM 0x8008
+#endif
+
+#ifndef PCI_DEVICE_ID_QUANCOM_GPIB
+#define PCI_DEVICE_ID_QUANCOM_GPIB 0x3302
+#endif
+
+#endif // __COMPAT_LINUX_PCI_IDS_H
diff --git a/drivers/staging/comedi/proc.c b/drivers/staging/comedi/proc.c
new file mode 100644
index 000000000000..7db12ac0d280
--- /dev/null
+++ b/drivers/staging/comedi/proc.c
@@ -0,0 +1,100 @@
+/*
+ module/proc.c
+ /proc interface for comedi
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/*
+ This is some serious bloatware.
+
+ Taken from Dave A.'s PCL-711 driver, 'cuz I thought it
+ was cool.
+*/
+
+#define __NO_VERSION__
+#include "comedidev.h"
+#include <linux/proc_fs.h>
+//#include <linux/string.h>
+
+int comedi_read_procmem(char *buf, char **start, off_t offset, int len,
+ int *eof, void *data);
+
+extern comedi_driver *comedi_drivers;
+
+int comedi_read_procmem(char *buf, char **start, off_t offset, int len,
+ int *eof, void *data)
+{
+ int i;
+ int devices_q = 0;
+ int l = 0;
+ comedi_driver *driv;
+
+ l += sprintf(buf + l,
+ "comedi version " COMEDI_RELEASE "\n"
+ "format string: %s\n",
+ "\"%2d: %-20s %-20s %4d\",i,driver_name,board_name,n_subdevices");
+
+ for (i = 0; i < COMEDI_NUM_BOARD_MINORS; i++) {
+ struct comedi_device_file_info *dev_file_info = comedi_get_device_file_info(i);
+ comedi_device *dev;
+
+ if(dev_file_info == NULL) continue;
+ dev = dev_file_info->device;
+
+ if (dev->attached) {
+ devices_q = 1;
+ l += sprintf(buf + l, "%2d: %-20s %-20s %4d\n",
+ i,
+ dev->driver->driver_name,
+ dev->board_name, dev->n_subdevices);
+ }
+ }
+ if (!devices_q) {
+ l += sprintf(buf + l, "no devices\n");
+ }
+
+ for (driv = comedi_drivers; driv; driv = driv->next) {
+ l += sprintf(buf + l, "%s:\n", driv->driver_name);
+ for (i = 0; i < driv->num_names; i++) {
+ l += sprintf(buf + l, " %s\n",
+ *(char **)((char *)driv->board_name +
+ i * driv->offset));
+ }
+ if (!driv->num_names) {
+ l += sprintf(buf + l, " %s\n", driv->driver_name);
+ }
+ }
+
+ return l;
+}
+
+void comedi_proc_init(void)
+{
+ struct proc_dir_entry *comedi_proc;
+
+ comedi_proc = create_proc_entry("comedi", S_IFREG | S_IRUGO, 0);
+ if (comedi_proc)
+ comedi_proc->read_proc = comedi_read_procmem;
+}
+
+void comedi_proc_cleanup(void)
+{
+ remove_proc_entry("comedi", 0);
+}
diff --git a/drivers/staging/comedi/range.c b/drivers/staging/comedi/range.c
new file mode 100644
index 000000000000..61dc3cd6a9fd
--- /dev/null
+++ b/drivers/staging/comedi/range.c
@@ -0,0 +1,161 @@
+/*
+ module/range.c
+ comedi routines for voltage ranges
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-8 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "comedidev.h"
+#include <asm/uaccess.h>
+
+const comedi_lrange range_bipolar10 = { 1, {BIP_RANGE(10)} };
+const comedi_lrange range_bipolar5 = { 1, {BIP_RANGE(5)} };
+const comedi_lrange range_bipolar2_5 = { 1, {BIP_RANGE(2.5)} };
+const comedi_lrange range_unipolar10 = { 1, {UNI_RANGE(10)} };
+const comedi_lrange range_unipolar5 = { 1, {UNI_RANGE(5)} };
+const comedi_lrange range_unknown = { 1, {{0, 1000000, UNIT_none}} };
+
+/*
+ COMEDI_RANGEINFO
+ range information ioctl
+
+ arg:
+ pointer to rangeinfo structure
+
+ reads:
+ range info structure
+
+ writes:
+ n comedi_krange structures to rangeinfo->range_ptr
+*/
+int do_rangeinfo_ioctl(comedi_device * dev, comedi_rangeinfo * arg)
+{
+ comedi_rangeinfo it;
+ int subd, chan;
+ const comedi_lrange *lr;
+ comedi_subdevice *s;
+
+ if (copy_from_user(&it, arg, sizeof(comedi_rangeinfo)))
+ return -EFAULT;
+ subd = (it.range_type >> 24) & 0xf;
+ chan = (it.range_type >> 16) & 0xff;
+
+ if (!dev->attached)
+ return -EINVAL;
+ if (subd >= dev->n_subdevices)
+ return -EINVAL;
+ s = dev->subdevices + subd;
+ if (s->range_table) {
+ lr = s->range_table;
+ } else if (s->range_table_list) {
+ if (chan >= s->n_chan)
+ return -EINVAL;
+ lr = s->range_table_list[chan];
+ } else {
+ return -EINVAL;
+ }
+
+ if (RANGE_LENGTH(it.range_type) != lr->length) {
+ DPRINTK("wrong length %d should be %d (0x%08x)\n",
+ RANGE_LENGTH(it.range_type), lr->length, it.range_type);
+ return -EINVAL;
+ }
+
+ if (copy_to_user(it.range_ptr, lr->range,
+ sizeof(comedi_krange) * lr->length))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int aref_invalid(comedi_subdevice * s, unsigned int chanspec)
+{
+ unsigned int aref;
+
+ // disable reporting invalid arefs... maybe someday
+ return 0;
+
+ aref = CR_AREF(chanspec);
+ switch (aref) {
+ case AREF_DIFF:
+ if (s->subdev_flags & SDF_DIFF)
+ return 0;
+ break;
+ case AREF_COMMON:
+ if (s->subdev_flags & SDF_COMMON)
+ return 0;
+ break;
+ case AREF_GROUND:
+ if (s->subdev_flags & SDF_GROUND)
+ return 0;
+ break;
+ case AREF_OTHER:
+ if (s->subdev_flags & SDF_OTHER)
+ return 0;
+ break;
+ default:
+ break;
+ }
+ DPRINTK("subdevice does not support aref %i", aref);
+ return 1;
+}
+
+/*
+ This function checks each element in a channel/gain list to make
+ make sure it is valid.
+*/
+int check_chanlist(comedi_subdevice * s, int n, unsigned int *chanlist)
+{
+ int i;
+ int chan;
+
+ if (s->range_table) {
+ for (i = 0; i < n; i++)
+ if (CR_CHAN(chanlist[i]) >= s->n_chan ||
+ CR_RANGE(chanlist[i]) >= s->range_table->length
+ || aref_invalid(s, chanlist[i])) {
+ rt_printk
+ ("bad chanlist[%d]=0x%08x n_chan=%d range length=%d\n",
+ i, chanlist[i], s->n_chan,
+ s->range_table->length);
+#if 0
+ for (i = 0; i < n; i++) {
+ printk("[%d]=0x%08x\n", i, chanlist[i]);
+ }
+#endif
+ return -EINVAL;
+ }
+ } else if (s->range_table_list) {
+ for (i = 0; i < n; i++) {
+ chan = CR_CHAN(chanlist[i]);
+ if (chan >= s->n_chan ||
+ CR_RANGE(chanlist[i]) >=
+ s->range_table_list[chan]->length
+ || aref_invalid(s, chanlist[i])) {
+ rt_printk("bad chanlist[%d]=0x%08x\n", i,
+ chanlist[i]);
+ return -EINVAL;
+ }
+ }
+ } else {
+ rt_printk("comedi: (bug) no range type list!\n");
+ return -EINVAL;
+ }
+ return 0;
+}
diff --git a/drivers/staging/comedi/rt.c b/drivers/staging/comedi/rt.c
new file mode 100644
index 000000000000..385b81b94ac5
--- /dev/null
+++ b/drivers/staging/comedi/rt.c
@@ -0,0 +1,412 @@
+/*
+ comedi/rt.c
+ comedi kernel module
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#undef DEBUG
+
+#define __NO_VERSION__
+#include <linux/comedidev.h>
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+
+#include "rt_pend_tq.h"
+
+#ifdef CONFIG_COMEDI_RTAI
+#include <rtai.h>
+#endif
+
+#ifdef CONFIG_COMEDI_FUSION
+#include <nucleus/asm/hal.h>
+#endif
+
+#ifdef CONFIG_COMEDI_RTL
+#include <rtl_core.h>
+#include <rtl_sync.h>
+#endif
+
+struct comedi_irq_struct {
+ int rt;
+ int irq;
+ irqreturn_t(*handler) (int irq, void *dev_id PT_REGS_ARG);
+ unsigned long flags;
+ const char *device;
+ comedi_device *dev_id;
+};
+
+static int comedi_rt_get_irq(struct comedi_irq_struct *it);
+static int comedi_rt_release_irq(struct comedi_irq_struct *it);
+
+static struct comedi_irq_struct *comedi_irqs[NR_IRQS];
+
+int comedi_request_irq(unsigned irq, irqreturn_t(*handler) (int,
+ void *PT_REGS_ARG), unsigned long flags, const char *device,
+ comedi_device * dev_id)
+{
+ struct comedi_irq_struct *it;
+ int ret;
+ /* null shared interrupt flag, since rt interrupt handlers do not
+ * support it, and this version of comedi_request_irq() is only
+ * called for kernels with rt support */
+ unsigned long unshared_flags = flags & ~IRQF_SHARED;
+
+ ret = request_irq(irq, handler, unshared_flags, device, dev_id);
+ if (ret < 0) {
+ // we failed, so fall back on allowing shared interrupt (which we won't ever make RT)
+ if (flags & IRQF_SHARED) {
+ rt_printk
+ ("comedi: cannot get unshared interrupt, will not use RT interrupts.\n");
+ ret = request_irq(irq, handler, flags, device, dev_id);
+ }
+ if (ret < 0) {
+ return ret;
+ }
+ } else {
+ it = kzalloc(sizeof(struct comedi_irq_struct), GFP_KERNEL);
+ if (!it)
+ return -ENOMEM;
+
+ it->handler = handler;
+ it->irq = irq;
+ it->dev_id = dev_id;
+ it->device = device;
+ it->flags = unshared_flags;
+ comedi_irqs[irq] = it;
+ }
+ return 0;
+}
+
+void comedi_free_irq(unsigned int irq, comedi_device * dev_id)
+{
+ struct comedi_irq_struct *it;
+
+ free_irq(irq, dev_id);
+
+ it = comedi_irqs[irq];
+ if (it == NULL)
+ return;
+
+ if (it->rt) {
+ printk("real-time IRQ allocated at board removal (ignore)\n");
+ comedi_rt_release_irq(it);
+ }
+
+ kfree(it);
+ comedi_irqs[irq] = NULL;
+}
+
+int comedi_switch_to_rt(comedi_device * dev)
+{
+ struct comedi_irq_struct *it;
+ unsigned long flags;
+
+ it = comedi_irqs[dev->irq];
+ /* drivers might not be using an interrupt for commands,
+ or we might not have been able to get an unshared irq */
+ if (it == NULL)
+ return -1;
+
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+
+ if (!dev->rt)
+ comedi_rt_get_irq(it);
+
+ dev->rt++;
+ it->rt = 1;
+
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return 0;
+}
+
+void comedi_switch_to_non_rt(comedi_device * dev)
+{
+ struct comedi_irq_struct *it;
+ unsigned long flags;
+
+ it = comedi_irqs[dev->irq];
+ if (it == NULL)
+ return;
+
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+
+ dev->rt--;
+ if (!dev->rt)
+ comedi_rt_release_irq(it);
+
+ it->rt = 0;
+
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+}
+
+void wake_up_int_handler(int arg1, void *arg2)
+{
+ wake_up_interruptible((wait_queue_head_t *) arg2);
+}
+
+void comedi_rt_pend_wakeup(wait_queue_head_t * q)
+{
+ rt_pend_call(wake_up_int_handler, 0, q);
+}
+
+/* RTAI section */
+#ifdef CONFIG_COMEDI_RTAI
+
+#ifndef HAVE_RT_REQUEST_IRQ_WITH_ARG
+#define DECLARE_VOID_IRQ(irq) \
+static void handle_void_irq_ ## irq (void){ handle_void_irq(irq);}
+
+static void handle_void_irq(int irq)
+{
+ struct comedi_irq_struct *it;
+
+ it = comedi_irqs[irq];
+ if (it == NULL) {
+ rt_printk("comedi: null irq struct?\n");
+ return;
+ }
+ it->handler(irq, it->dev_id PT_REGS_NULL);
+ rt_enable_irq(irq); //needed by rtai-adeos, seems like it shouldn't hurt earlier versions
+}
+
+DECLARE_VOID_IRQ(0);
+DECLARE_VOID_IRQ(1);
+DECLARE_VOID_IRQ(2);
+DECLARE_VOID_IRQ(3);
+DECLARE_VOID_IRQ(4);
+DECLARE_VOID_IRQ(5);
+DECLARE_VOID_IRQ(6);
+DECLARE_VOID_IRQ(7);
+DECLARE_VOID_IRQ(8);
+DECLARE_VOID_IRQ(9);
+DECLARE_VOID_IRQ(10);
+DECLARE_VOID_IRQ(11);
+DECLARE_VOID_IRQ(12);
+DECLARE_VOID_IRQ(13);
+DECLARE_VOID_IRQ(14);
+DECLARE_VOID_IRQ(15);
+DECLARE_VOID_IRQ(16);
+DECLARE_VOID_IRQ(17);
+DECLARE_VOID_IRQ(18);
+DECLARE_VOID_IRQ(19);
+DECLARE_VOID_IRQ(20);
+DECLARE_VOID_IRQ(21);
+DECLARE_VOID_IRQ(22);
+DECLARE_VOID_IRQ(23);
+
+typedef void (*V_FP_V) (void);
+static V_FP_V handle_void_irq_ptrs[] = {
+ handle_void_irq_0,
+ handle_void_irq_1,
+ handle_void_irq_2,
+ handle_void_irq_3,
+ handle_void_irq_4,
+ handle_void_irq_5,
+ handle_void_irq_6,
+ handle_void_irq_7,
+ handle_void_irq_8,
+ handle_void_irq_9,
+ handle_void_irq_10,
+ handle_void_irq_11,
+ handle_void_irq_12,
+ handle_void_irq_13,
+ handle_void_irq_14,
+ handle_void_irq_15,
+ handle_void_irq_16,
+ handle_void_irq_17,
+ handle_void_irq_18,
+ handle_void_irq_19,
+ handle_void_irq_20,
+ handle_void_irq_21,
+ handle_void_irq_22,
+ handle_void_irq_23,
+};
+
+static int comedi_rt_get_irq(struct comedi_irq_struct *it)
+{
+ rt_request_global_irq(it->irq, handle_void_irq_ptrs[it->irq]);
+ rt_startup_irq(it->irq);
+
+ return 0;
+}
+
+static int comedi_rt_release_irq(struct comedi_irq_struct *it)
+{
+ rt_shutdown_irq(it->irq);
+ rt_free_global_irq(it->irq);
+ return 0;
+}
+#else
+
+static int comedi_rt_get_irq(struct comedi_irq_struct *it)
+{
+ int ret;
+
+ ret = rt_request_global_irq_arg(it->irq, it->handler, it->flags,
+ it->device, it->dev_id);
+ if (ret < 0) {
+ rt_printk("rt_request_global_irq_arg() returned %d\n", ret);
+ return ret;
+ }
+ rt_startup_irq(it->irq);
+
+ return 0;
+}
+
+static int comedi_rt_release_irq(struct comedi_irq_struct *it)
+{
+ rt_shutdown_irq(it->irq);
+ rt_free_global_irq(it->irq);
+ return 0;
+}
+#endif
+
+void comedi_rt_init(void)
+{
+ rt_mount_rtai();
+ rt_pend_tq_init();
+}
+
+void comedi_rt_cleanup(void)
+{
+ rt_umount_rtai();
+ rt_pend_tq_cleanup();
+}
+
+#endif
+
+/* Fusion section */
+#ifdef CONFIG_COMEDI_FUSION
+
+static void fusion_handle_irq(unsigned int irq, void *cookie)
+{
+ struct comedi_irq_struct *it = cookie;
+
+ it->handler(irq, it->dev_id PT_REGS_NULL);
+ rthal_irq_enable(irq);
+}
+
+static int comedi_rt_get_irq(struct comedi_irq_struct *it)
+{
+ rthal_irq_request(it->irq, fusion_handle_irq, it);
+ rthal_irq_enable(it->irq);
+ return 0;
+}
+
+static int comedi_rt_release_irq(struct comedi_irq_struct *it)
+{
+ rthal_irq_disable(it->irq);
+ rthal_irq_release(it->irq);
+ return 0;
+}
+
+void comedi_rt_init(void)
+{
+ rt_pend_tq_init();
+}
+
+void comedi_rt_cleanup(void)
+{
+ rt_pend_tq_cleanup();
+}
+
+#endif /*CONFIG_COMEDI_FUSION */
+
+/* RTLinux section */
+#ifdef CONFIG_COMEDI_RTL
+
+static unsigned int handle_rtl_irq(unsigned int irq PT_REGS_ARG)
+{
+ struct comedi_irq_struct *it;
+
+ it = comedi_irqs[irq];
+ if (it == NULL)
+ return 0;
+ it->handler(irq, it->dev_id PT_REGS_NULL);
+ rtl_hard_enable_irq(irq);
+ return 0;
+}
+
+static int comedi_rt_get_irq(struct comedi_irq_struct *it)
+{
+ rtl_request_global_irq(it->irq, handle_rtl_irq);
+ return 0;
+}
+
+static int comedi_rt_release_irq(struct comedi_irq_struct *it)
+{
+ rtl_free_global_irq(it->irq);
+ return 0;
+}
+
+void comedi_rt_init(void)
+{
+ rt_pend_tq_init();
+}
+
+void comedi_rt_cleanup(void)
+{
+ rt_pend_tq_cleanup();
+}
+
+#endif
+
+#ifdef CONFIG_COMEDI_PIRQ
+static int comedi_rt_get_irq(struct comedi_irq_struct *it)
+{
+ int ret;
+
+ free_irq(it->irq, it->dev_id);
+ ret = request_irq(it->irq, it->handler, it->flags | SA_PRIORITY,
+ it->device, it->dev_id);
+
+ return ret;
+}
+
+static int comedi_rt_release_irq(struct comedi_irq_struct *it)
+{
+ int ret;
+
+ free_irq(it->irq, it->dev_id);
+ ret = request_irq(it->irq, it->handler, it->flags,
+ it->device, it->dev_id);
+
+ return ret;
+}
+
+void comedi_rt_init(void)
+{
+ //rt_pend_tq_init();
+}
+
+void comedi_rt_cleanup(void)
+{
+ //rt_pend_tq_cleanup();
+}
+#endif
diff --git a/drivers/staging/comedi/rt_pend_tq.c b/drivers/staging/comedi/rt_pend_tq.c
new file mode 100644
index 000000000000..995f076e0af3
--- /dev/null
+++ b/drivers/staging/comedi/rt_pend_tq.c
@@ -0,0 +1,113 @@
+#define __NO_VERSION__
+/* rt_pend_tq.c */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include "comedidev.h" // for rt spinlocks
+#include "rt_pend_tq.h"
+#ifdef CONFIG_COMEDI_RTAI
+#include <rtai.h>
+#endif
+#ifdef CONFIG_COMEDI_FUSION
+#include <nucleus/asm/hal.h>
+#endif
+#ifdef CONFIG_COMEDI_RTL
+#include <rtl_core.h>
+#endif
+
+#ifdef standalone
+#include <linux/module.h>
+#define rt_pend_tq_init init_module
+#define rt_pend_tq_cleanup cleanup_module
+#endif
+
+volatile static struct rt_pend_tq rt_pend_tq[RT_PEND_TQ_SIZE];
+volatile static struct rt_pend_tq *volatile rt_pend_head = rt_pend_tq,
+ *volatile rt_pend_tail = rt_pend_tq;
+int rt_pend_tq_irq = 0;
+spinlock_t rt_pend_tq_lock = SPIN_LOCK_UNLOCKED;
+
+// WARNING: following code not checked against race conditions yet.
+#define INC_CIRCULAR_PTR(ptr,begin,size) do {if(++(ptr)>=(begin)+(size)) (ptr)=(begin); } while(0)
+#define DEC_CIRCULAR_PTR(ptr,begin,size) do {if(--(ptr)<(begin)) (ptr)=(begin)+(size)-1; } while(0)
+
+int rt_pend_call(void (*func) (int arg1, void *arg2), int arg1, void *arg2)
+{
+ unsigned long flags;
+
+ if (func == NULL)
+ return -EINVAL;
+ if (rt_pend_tq_irq <= 0)
+ return -ENODEV;
+ comedi_spin_lock_irqsave(&rt_pend_tq_lock, flags);
+ INC_CIRCULAR_PTR(rt_pend_head, rt_pend_tq, RT_PEND_TQ_SIZE);
+ if (rt_pend_head == rt_pend_tail) {
+ // overflow, we just refuse to take this request
+ DEC_CIRCULAR_PTR(rt_pend_head, rt_pend_tq, RT_PEND_TQ_SIZE);
+ comedi_spin_unlock_irqrestore(&rt_pend_tq_lock, flags);
+ return -EAGAIN;
+ }
+ rt_pend_head->func = func;
+ rt_pend_head->arg1 = arg1;
+ rt_pend_head->arg2 = arg2;
+ comedi_spin_unlock_irqrestore(&rt_pend_tq_lock, flags);
+#ifdef CONFIG_COMEDI_RTAI
+ rt_pend_linux_srq(rt_pend_tq_irq);
+#endif
+#ifdef CONFIG_COMEDI_FUSION
+ rthal_apc_schedule(rt_pend_tq_irq);
+#endif
+#ifdef CONFIG_COMEDI_RTL
+ rtl_global_pend_irq(rt_pend_tq_irq);
+
+#endif
+ return 0;
+}
+
+#ifdef CONFIG_COMEDI_RTAI
+void rt_pend_irq_handler(void)
+#elif defined(CONFIG_COMEDI_FUSION)
+void rt_pend_irq_handler(void *cookie)
+#elif defined(CONFIG_COMEDI_RTL)
+void rt_pend_irq_handler(int irq, void *dev PT_REGS_ARG)
+#endif
+{
+ while (rt_pend_head != rt_pend_tail) {
+ INC_CIRCULAR_PTR(rt_pend_tail, rt_pend_tq, RT_PEND_TQ_SIZE);
+ rt_pend_tail->func(rt_pend_tail->arg1, rt_pend_tail->arg2);
+ }
+}
+
+int rt_pend_tq_init(void)
+{
+ rt_pend_head = rt_pend_tail = rt_pend_tq;
+#ifdef CONFIG_COMEDI_RTAI
+ rt_pend_tq_irq = rt_request_srq(0, rt_pend_irq_handler, NULL);
+#endif
+#ifdef CONFIG_COMEDI_FUSION
+ rt_pend_tq_irq =
+ rthal_apc_alloc("comedi APC", rt_pend_irq_handler, NULL);
+#endif
+#ifdef CONFIG_COMEDI_RTL
+ rt_pend_tq_irq = rtl_get_soft_irq(rt_pend_irq_handler, "rt_pend_irq");
+#endif
+ if (rt_pend_tq_irq > 0)
+ printk("rt_pend_tq: RT bottom half scheduler initialized OK\n");
+ else
+ printk("rt_pend_tq: rtl_get_soft_irq failed\n");
+ return 0;
+}
+
+void rt_pend_tq_cleanup(void)
+{
+ printk("rt_pend_tq: unloading\n");
+#ifdef CONFIG_COMEDI_RTAI
+ rt_free_srq(rt_pend_tq_irq);
+#endif
+#ifdef CONFIG_COMEDI_FUSION
+ rthal_apc_free(rt_pend_tq_irq);
+#endif
+#ifdef CONFIG_COMEDI_RTL
+ free_irq(rt_pend_tq_irq, NULL);
+#endif
+}
diff --git a/drivers/staging/comedi/rt_pend_tq.h b/drivers/staging/comedi/rt_pend_tq.h
new file mode 100644
index 000000000000..01ed71bf409d
--- /dev/null
+++ b/drivers/staging/comedi/rt_pend_tq.h
@@ -0,0 +1,10 @@
+#define RT_PEND_TQ_SIZE 16
+struct rt_pend_tq {
+ void (*func) (int arg1, void *arg2);
+ int arg1;
+ void *arg2;
+};
+extern int rt_pend_call(void (*func) (int arg1, void *arg2), int arg1,
+ void *arg2);
+extern int rt_pend_tq_init(void);
+extern void rt_pend_tq_cleanup(void);
diff --git a/drivers/staging/comedi/usb.h b/drivers/staging/comedi/usb.h
new file mode 100644
index 000000000000..40ad651d86c0
--- /dev/null
+++ b/drivers/staging/comedi/usb.h
@@ -0,0 +1,75 @@
+/*
+
+ linux/usb.h compatibility header
+
+ Copyright (C) 2003 Bernd Porr, Bernd.Porr@cn.stir.ac.uk
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef __COMPAT_LINUX_USB_H_
+#define __COMPAT_LINUX_USB_H_
+
+#include <linux/version.h>
+#include <linux/time.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#include <linux/kernel.h>
+
+#define USB_ALLOC_URB(x) usb_alloc_urb(x)
+#define USB_SUBMIT_URB(x) usb_submit_urb(x)
+#define URB_ISO_ASAP USB_ISO_ASAP
+#define PROBE_ERR_RETURN(x) NULL
+#define usb_get_dev(x) (x)
+#define usb_put_dev(x)
+#define interface_to_usbdev(intf) NULL
+#else
+#define USB_ALLOC_URB(x) usb_alloc_urb(x,GFP_KERNEL)
+#define USB_SUBMIT_URB(x) usb_submit_urb(x,GFP_ATOMIC)
+#define PROBE_ERR_RETURN(x) (x)
+#endif
+
+#include <linux/usb.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)
+static inline int USB_CONTROL_MSG(struct usb_device *dev, unsigned int pipe,
+ __u8 request, __u8 requesttype, __u16 value, __u16 index,
+ void *data, __u16 size, int millisec_timeout)
+{
+ return usb_control_msg(dev, pipe, request, requesttype, value, index,
+ data, size, msecs_to_jiffies(millisec_timeout));
+}
+static inline int USB_BULK_MSG(struct usb_device *usb_dev, unsigned int pipe,
+ void *data, int len, int *actual_length, int millisec_timeout)
+{
+ return usb_bulk_msg(usb_dev, pipe, data, len, actual_length,
+ msecs_to_jiffies(millisec_timeout));
+}
+#else
+#define USB_CONTROL_MSG usb_control_msg
+#define USB_BULK_MSG usb_bulk_msg
+#endif
+
+/*
+ * Determine whether we need the "owner" member of struct usb_driver and
+ * define COMEDI_HAVE_USB_DRIVER_OWNER if we need it.
+ */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,19) \
+ && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
+#define COMEDI_HAVE_USB_DRIVER_OWNER
+#endif
+
+#endif
diff --git a/drivers/staging/comedi/wrapper.h b/drivers/staging/comedi/wrapper.h
new file mode 100644
index 000000000000..77fc673900e9
--- /dev/null
+++ b/drivers/staging/comedi/wrapper.h
@@ -0,0 +1,25 @@
+/*
+ linux/wrapper.h compatibility header
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __COMPAT_LINUX_WRAPPER_H_
+#define __COMPAT_LINUX_WRAPPER_H_
+
+#define mem_map_reserve(p) set_bit(PG_reserved, &((p)->flags))
+#define mem_map_unreserve(p) clear_bit(PG_reserved, &((p)->flags))
+
+#endif /* __COMPAT_LINUX_WRAPPER_H_ */