]> rtime.felk.cvut.cz Git - mf6xx.git/commitdiff
Initial commit of Comedi driver for MF624 card; Original author Francois Poulain...
authorRostislav Lisovy <lisovy@gmail.com>
Thu, 17 Mar 2011 18:24:01 +0000 (19:24 +0100)
committerRostislav Lisovy <lisovy@gmail.com>
Thu, 17 Mar 2011 18:25:57 +0000 (19:25 +0100)
12 files changed:
src/comedi/fpoulain/code_configuration/Makefile [new file with mode: 0755]
src/comedi/fpoulain/code_configuration/configCtr.c [new file with mode: 0755]
src/comedi/fpoulain/code_configuration/configEnc.c [new file with mode: 0755]
src/comedi/fpoulain/code_configuration/mf624.h [new file with mode: 0755]
src/comedi/fpoulain/comedi+rtai/Makefile [new file with mode: 0755]
src/comedi/fpoulain/comedi+rtai/configEnc.c [new file with mode: 0755]
src/comedi/fpoulain/comedi+rtai/no-rt.c [new file with mode: 0755]
src/comedi/fpoulain/comedi+rtai/rt.c [new file with mode: 0755]
src/comedi/fpoulain/documentation/Makefile [new file with mode: 0755]
src/comedi/fpoulain/documentation/comedi_mf624.tex [new file with mode: 0755]
src/comedi/fpoulain/mf624.c [new file with mode: 0755]
src/comedi/fpoulain/mf624.h [new file with mode: 0755]

diff --git a/src/comedi/fpoulain/code_configuration/Makefile b/src/comedi/fpoulain/code_configuration/Makefile
new file mode 100755 (executable)
index 0000000..8911d1a
--- /dev/null
@@ -0,0 +1,7 @@
+all : configEnc configCtr
+
+configEnc : configEnc.c
+       gcc  configEnc.c -o configEnc -lcomedi -lm 
+
+configCtr : configCtr.c
+       gcc  configCtr.c -o configCtr -lcomedi -lm 
diff --git a/src/comedi/fpoulain/code_configuration/configCtr.c b/src/comedi/fpoulain/code_configuration/configCtr.c
new file mode 100755 (executable)
index 0000000..0d8fc2f
--- /dev/null
@@ -0,0 +1,138 @@
+/***********************************************************************
+Copyright (C) 2007  Francois Poulain, <fpoulain AT gmail DOT 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
+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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+***********************************************************************/
+
+#include <stdio.h>
+#include <time.h>
+#include <math.h>
+#include <comedilib.h>
+#include "mf624.h"
+
+void mf624_timer_PWM_config(comedi_t *device, int channel, float freq, float ratio){
+       comedi_insn instruction;
+       unsigned int clk_source = 0;
+       unsigned int registerA, registerB;
+       float period = 1/freq, clk_freq = 0;
+       lsampl_t data[13];
+       lsampl_t registers[2];
+
+       if(ratio < 0 || ratio > 1) {printf("bad ratio: %f\n", ratio); return;}
+
+       if(freq > 50000000 || freq < 100000/powl(2,32)) {printf("Can't configure timer for such a frequency !\n"); return;}
+
+       else if(freq > 100000){
+               clk_source = MF624_CTR_CKSOURCE_50M;
+               clk_freq = 50000000;
+       }
+       else if(freq > 10000){
+               clk_source = MF624_CTR_CKSOURCE_10M;
+               clk_freq = 10000000;
+       }
+       else if(freq > 1000){
+               clk_source = MF624_CTR_CKSOURCE_1M;
+               clk_freq = 1000000;
+       }
+       else{
+               clk_source = MF624_CTR_CKSOURCE_100K;
+               clk_freq = 100000;
+       }
+
+       registerA = -1-clk_freq*period*ratio;
+       registerB = -1-clk_freq*period*(1-ratio);
+       printf("freq = %f, clk_freq*period = %f, -A register = %u, -B register = %u\n", freq, clk_freq*period, -registerA, -registerB);
+
+       registers[0] = registerA;
+       registers[1] = registerB;
+
+       instruction.insn = INSN_WRITE;
+       instruction.subdev = MF624_CTR_SUBDEV;
+       instruction.chanspec = CR_PACK(channel, 0, 0);
+       instruction.n = 2;
+       instruction.data = registers;
+
+       comedi_do_insn(device , &instruction);
+
+       data[0] = MF624_CTR_DIRECTION_UP;
+       data[1] = MF624_CTR_REPETITION_ON;
+       data[2] = MF624_CTR_PRELOAD_AB;
+       data[3] = MF624_CTR_OUTPUT_BISTABLE;
+       data[4] = MF624_CTR_TRIGGER_DISABLED;
+       data[5] = MF624_CTR_TRIGGER_DISABLE;
+       data[6] = MF624_CTR_RETRIGGER_OFF;
+       data[7] = MF624_CTR_GATE_HIGHT;
+       data[8] = MF624_CTR_GATEPOLARITY_LOW;
+       data[9] = clk_source;
+       data[10] = MF624_CTR_OUTPUT_DIRECT;
+       data[11] = MF624_CTR_CTRL_LOAD;
+
+       instruction.insn = INSN_CONFIG;
+       instruction.n = 12;
+       instruction.data = data;
+       comedi_do_insn(device , &instruction);
+
+       data[0] = MF624_CTR_CTRL_START;
+       instruction.n = 1;
+       
+       comedi_do_insn(device , &instruction);
+}
+
+void mf624_timer_freq_config(comedi_t *device, int channel, float freq){
+       mf624_timer_PWM_config(device, channel, freq, 0.5);
+       return;
+}
+
+void mf624_timer_reset(comedi_t *device, int channel){
+       comedi_insn instruction;
+       lsampl_t data = MF624_CTR_CTRL_RESET;
+
+       instruction.insn = INSN_CONFIG;
+       instruction.n = 1;
+       instruction.data = &data;
+       instruction.subdev = MF624_CTR_SUBDEV;
+       instruction.chanspec = CR_PACK(channel, 0, 0);;
+
+       comedi_do_insn(device , &instruction);
+}
+
+int main(void)
+{
+       comedi_t *device;
+       unsigned int chan = 0;
+       float freq = 100.0, ratio = 0.0; /* in Hz */
+       char filename[] = "/dev/comedi0";
+       int dt = 0;
+       struct timespec ts={0,20000000};
+
+       /* Open comedi device */
+       device = comedi_open(filename);
+       if(!device){
+               comedi_perror(filename);
+               return(1);
+       }
+
+       mf624_timer_reset(device, chan);
+       mf624_timer_freq_config(device, chan, freq);
+
+       while (1){
+               ratio = ratio + 1.0/20;
+               if (ratio >= 1.0 || ratio < 0) ratio = 0;
+               mf624_timer_PWM_config(device, chan, freq, ratio);
+               nanosleep(&ts, NULL);
+       }
+
+       return 0;
+}
+
diff --git a/src/comedi/fpoulain/code_configuration/configEnc.c b/src/comedi/fpoulain/code_configuration/configEnc.c
new file mode 100755 (executable)
index 0000000..e45eacc
--- /dev/null
@@ -0,0 +1,126 @@
+/***********************************************************************
+Copyright (C) 2007  Francois Poulain, <fpoulain AT gmail DOT 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
+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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+***********************************************************************/
+
+#include <stdio.h>
+#include <time.h>
+#include <comedilib.h>
+#include "mf624.h"
+
+#define PERIOD         025000000.0     /* in nanoseconds       */
+#define TIME   0.0             /* in seconds           */
+#define INFINITE 0             /* don't stop           */
+
+#define PLOT_LEN 512           /* length of plot data  */
+
+lsampl_t tab[PLOT_LEN];
+
+void plotDatas(lsampl_t* tableau){
+       int i = 0;
+       printf("plot [0:512][-512:512] '-' using 1:2 with lines\n");
+       for(i = 0 ; i < PLOT_LEN ; i++) printf("%d %d \n", i, tableau[i]);
+       printf("e \n");
+}
+
+void mf624_enc_std_config(comedi_t *device, int subdev, int chanspec){
+       comedi_insn instruction;
+       lsampl_t data[4];
+       data[0] = MF624_ENC_MODE_RISING;
+       data[1] = MF624_ENC_COUNT_ENABLE;
+       data[2] = MF624_ENC_RESET_ONRAISING_INPUT;
+//     data[2] = MF624_ENC_RESET_DISABLE;
+       data[3] = MF624_ENC_FILTER_ON;
+
+       instruction.insn = INSN_CONFIG;
+       instruction.n = 4;
+       instruction.data = data;
+       instruction.subdev = subdev;
+       instruction.chanspec = chanspec;
+
+       comedi_do_insn(device , &instruction);
+}
+
+void mf624_enc_reset(comedi_t *device, int subdev, int chanspec){
+       comedi_insn instruction;
+       lsampl_t data = MF624_ENC_RESET_ENABLE;
+
+       instruction.insn = INSN_CONFIG;
+       instruction.n = 1;
+       instruction.data = &data;
+       instruction.subdev = subdev;
+       instruction.chanspec = chanspec;
+
+       comedi_do_insn(device , &instruction);
+
+       data = MF624_ENC_RESET_DISABLE;
+       instruction.data = &data;
+       comedi_do_insn(device , &instruction);
+}
+
+int main(void)
+{
+       comedi_t *device;
+       lsampl_t dataReaded;
+       int subdev = MF624_ENC_SUBDEV;
+       int chan = 1;
+       int range = 0;
+       int aref = 0;
+       char filename[] = "/dev/comedi0";
+
+       int toc = 0, tic = 0;
+       struct timespec tempo;
+       
+       printf("# You should pipe this program with gnuplot \n");
+
+       // init tab
+       for (tic = 0 ; tic < PLOT_LEN ; tic ++) tab[tic] = 0;
+
+       tempo.tv_sec = PERIOD/1e9;
+       tempo.tv_nsec = PERIOD - tempo.tv_sec*1e9;
+
+//     comedi_loglevel(4);
+
+       /* Open comedi device */
+       device = comedi_open(filename);
+       if(!device){
+               comedi_perror(filename);
+               return(1);
+       }
+
+//     mf624_enc_reset(device, subdev, CR_PACK(chan,range,aref));
+       mf624_enc_std_config(device, subdev, CR_PACK(chan,range,aref));
+       mf624_enc_std_config(device, subdev, CR_PACK(chan+1,range,aref));
+
+       // read configuration register using virtual channel
+       // comedi_data_read(device, subdev, 4, range, aref, &dataReaded);
+       // printf("registre de configuration : %x\n", dataReaded);
+
+       while(toc++ < TIME/(PERIOD/1000000000) || INFINITE){
+               comedi_data_read(device, subdev, chan, range, aref, &dataReaded);
+               tab[tic] = dataReaded;
+               nanosleep(&tempo, NULL);
+               tic++;
+               if (tic >= 512) {
+                       tic = 0;
+               }
+               if (tic%8 == 0) plotDatas(tab);
+       }
+
+       comedi_close(device);
+
+       return 0;
+}
+
diff --git a/src/comedi/fpoulain/code_configuration/mf624.h b/src/comedi/fpoulain/code_configuration/mf624.h
new file mode 100755 (executable)
index 0000000..8ffab9f
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * comedi/drivers/mf624.c
+ * Code for a Humusoft MF624 driver
+ *
+ * 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: mf624.o
+Description: A comedi driver for Humusoft MF624 PCI Card
+Devices: Humusoft MF624 Multifunction I/O Card
+Author: Jean-Matthieu Bourgeot - François Poulain
+Updated: Wed, 19 Sep 2007 14:29:43 +0200
+Status: experimental
+
+This driver is a driver for the Humusoft MF624 PCI Card.
+
+It has :
+ * 8 channel 14 bits ADC,
+ * 8 channel 14 bits DAC,
+ * 8 digital inputs,
+ * 8 digital outputs,
+ * 4 quadrature encoder input,
+ * 5 timers/counter.
+
+Status:
+A/D Converter                  : Supported on subdevice 0,
+D/A Converter                  : Supported on subdevice 1,
+Digital Inputs                 : Supported on subdevice 2,
+Digital Outputs                        : Supported on subdevice 3,
+Quadrature Encoder Inputs      : Not yet supported,
+Counters/Timers                        : Not yet supported,
+IRQ                            : Not yet supported.
+
+Remarks:
+Simultaneous D/A update not yet supported.
+
+Configuration Options:
+none
+ */
+
+/*
+ * This file provide useful declaration like some constants
+ * for configuration needed both in user's space and in kernel space
+ */
+
+/* Subdevice numbers */
+#define MF624_ENC_SUBDEV       5
+#define MF624_CTR_SUBDEV       4
+#define MF624_DO_SUBDEV                3
+#define MF624_DI_SUBDEV                2
+#define MF624_AO_SUBDEV                1
+#define MF624_AI_SUBDEV                0
+
+/* Encoders Configuration */
+#define MF624_ENC                      0x0001
+
+/* some unnecessary masks */
+#define MF624_ENC_MODE                 0x000100
+#define MF624_ENC_COUNT                        0x000101
+#define MF624_ENC_RESET                        0x000102
+#define MF624_ENC_FILTER               0x000103
+
+/* some opcodes */
+#define MF624_ENC_MODE_4EDGE           0x00010000
+#define MF624_ENC_MODE_RISING          0x00010001
+#define MF624_ENC_MODE_FALLING         0x00010002
+#define MF624_ENC_MODE_EITHER          0x00010003
+#define MF624_ENC_COUNT_ENABLE         0x00010100
+#define MF624_ENC_COUNT_DISABLE                0x00010101
+#define MF624_ENC_COUNT_ENABLE_BYINPUT 0x00010102
+#define MF624_ENC_COUNT_DISABLE_BYINPUT        0x00010103
+#define MF624_ENC_RESET_DISABLE                0x00010200
+#define MF624_ENC_RESET_ENABLE         0x00010201
+#define MF624_ENC_RESET_DISABLE_BYINPUT        0x00010202
+#define MF624_ENC_RESET_ENABLE_BYINPUT 0x00010203
+#define MF624_ENC_RESET_ONRAISING_INPUT        0x00010204
+#define MF624_ENC_RESET_ONFALLING_INPUT        0x00010205
+#define MF624_ENC_RESET_ONEITHER_INPUT 0x00010206
+#define MF624_ENC_FILTER_OFF           0x00010300
+#define MF624_ENC_FILTER_ON            0x00010301
+
+/* Counters Configration */
+#define MF624_CTR                      0x0002
+
+/* some unnecessary masks */
+#define MF624_CTR_DIRECTION            0x000200
+#define MF624_CTR_REPETITION           0x000201
+#define MF624_CTR_PRELOAD              0x000202
+#define MF624_CTR_OUTPUT               0x000203
+#define MF624_CTR_TRIGGER              0x000204
+#define MF624_CTR_RETRIGGER            0x000205
+#define MF624_CTR_GATE                 0x000206
+#define MF624_CTR_GATEPOLARITY         0x000207
+#define MF624_CTR_CKSOURCE             0x000208
+#define MF624_CTR_ADTRIGSRC            0x000209
+#define MF624_CTR_CTR4INTSRC           0x00020A
+#define MF624_CTR_CTRL                 0x00020B
+
+/* some opcodes */
+#define MF624_CTR_DIRECTION_UP         0x00020000
+#define MF624_CTR_DIRECTION_DOWN       0x00020001
+#define MF624_CTR_REPETITION_ON                0x00020100
+#define MF624_CTR_REPETITION_OFF       0x00020101
+#define MF624_CTR_PRELOAD_A            0x00020200
+#define MF624_CTR_PRELOAD_AB           0x00020201
+#define MF624_CTR_OUTPUT_MONOSTABLE    0x00020300
+#define MF624_CTR_OUTPUT_BISTABLE      0x00020301
+#define MF624_CTR_OUTPUT_DIRECT                0x00020302
+#define MF624_CTR_OUTPUT_INVERSE       0x00020304
+#define MF624_CTR_OUTPUT_OFF           0x00020305
+#define MF624_CTR_OUTPUT_ON            0x00020306
+#define MF624_CTR_TRIGGER_DISABLED     0x00020400
+#define MF624_CTR_TRIGGER_BYINPUT      0x00020401
+#define MF624_CTR_TRIGGER_BYLOWER      0x00020402
+#define MF624_CTR_TRIGGER_BYUPPER      0x00020403
+#define MF624_CTR_TRIGGER_RAISING      0x00020404
+#define MF624_CTR_TRIGGER_FALLING      0x00020405
+#define MF624_CTR_TRIGGER_EITHER       0x00020406
+#define MF624_CTR_TRIGGER_DISABLE      0x00020407
+#define MF624_CTR_RETRIGGER_ON         0x00020500
+#define MF624_CTR_RETRIGGER_OFF                0x00020501
+#define MF624_CTR_GATE_HIGHT           0x00020600
+#define MF624_CTR_GATE_BYINPUT         0x00020601
+#define MF624_CTR_GATE_BYLOWER         0x00020602
+#define MF624_CTR_GATE_BYUPPER         0x00020603
+#define MF624_CTR_GATEPOLARITY_LOW     0x00020700
+#define MF624_CTR_GATEPOLARITY_HIGH    0x00020701
+#define MF624_CTR_CKSOURCE_50M         0x00020800
+#define MF624_CTR_CKSOURCE_10M         0x00020801
+#define MF624_CTR_CKSOURCE_1M          0x00020802
+#define MF624_CTR_CKSOURCE_100K                0x00020803
+#define MF624_CTR_CKSOURCE_RAISINGINPUT        0x00020804
+#define MF624_CTR_CKSOURCE_FALLINGINPUT        0x00020805
+#define MF624_CTR_CKSOURCE_EITHERINPUT 0x00020806
+#define MF624_CTR_CKSOURCE_RAISINGLOWER        0x00020807
+#define MF624_CTR_CKSOURCE_FALLINGLOWER        0x00020808
+#define MF624_CTR_CKSOURCE_EITHERLOWER 0x00020809
+#define MF624_CTR_CKSOURCE_RAISINGUPPER        0x0002080A
+#define MF624_CTR_CKSOURCE_FALLINGUPPER        0x0002080B
+#define MF624_CTR_CKSOURCE_EITHERUPPER 0x0002080C
+#define MF624_CTR_ADTRIGSRC_EXT                0x00020900
+#define MF624_CTR_ADTRIGSRC_INT                0x00020901
+#define MF624_CTR_CTR4INTSRC_EXT       0x00020A00
+#define MF624_CTR_CTR4INTSRC_INT       0x00020A01
+#define MF624_CTR_CTRL_START           0x00020B00
+#define MF624_CTR_CTRL_STOP            0x00020B01
+#define MF624_CTR_CTRL_LOAD            0x00020B02
+#define MF624_CTR_CTRL_RESET           0x00020B03
+#define MF624_CTR_CTRL_TSET            0x00020B04
+#define MF624_CTR_CTRL_TRESET          0x00020B05
diff --git a/src/comedi/fpoulain/comedi+rtai/Makefile b/src/comedi/fpoulain/comedi+rtai/Makefile
new file mode 100755 (executable)
index 0000000..a32176c
--- /dev/null
@@ -0,0 +1,10 @@
+all : no-rt rt configEnc
+
+no-rt : no-rt.c 
+       gcc no-rt.c -o no-rt -lcomedi -lm
+
+rt : rt.c
+       gcc rt.c -o rt -lcomedi -lm `/usr/realtime/bin/rtai-config --lxrt-cflags --lxrt-ldflags`
+
+configEnc : configEnc.c
+       gcc configEnc.c -o configEnc -lcomedi -lm `/usr/realtime/bin/rtai-config --lxrt-cflags --lxrt-ldflags`
diff --git a/src/comedi/fpoulain/comedi+rtai/configEnc.c b/src/comedi/fpoulain/comedi+rtai/configEnc.c
new file mode 100755 (executable)
index 0000000..9a44259
--- /dev/null
@@ -0,0 +1,75 @@
+/***********************************************************************
+Copyright (C) 2007  Francois Poulain, <fpoulain AT gmail DOT 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
+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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+***********************************************************************/
+
+#include <stdio.h>
+#include <time.h>
+#include <comedilib.h>
+#include "mf624.h"
+
+#define PERIOD         250000000.0     /* in nanoseconds       */
+#define TIME   2.0             /* in seconds           */
+
+int main(void)
+{
+       comedi_t *device;
+       comedi_insn instruction;
+       lsampl_t data[MF624_ENC_DATA_SIZE], dataReaded;
+       int subdev = MF624_ENC_SUBDEV;
+       int chan = 2;
+       int range = 0;
+       int aref = 0;
+       char filename[] = "/dev/comedi0";
+       
+       int toc = 0;
+       struct timespec tempo;
+
+       tempo.tv_sec = PERIOD/1e9;
+       tempo.tv_nsec = PERIOD - tempo.tv_sec*1e9;
+
+       data[0] = MF624_ENC_MODE_RISING;                /* Encoder mode         */
+       data[1] = MF624_ENC_COUNT_ENABLE;               /* Encoder counting     */
+       data[2] = MF624_ENC_RESET_ENABLE_BYINPUT;       /* Encoder reset        */
+       data[3] = MF624_ENC_FILTER_ON;                  /* Encoder filtering    */
+
+       instruction.insn = INSN_CONFIG;
+       instruction.n = MF624_ENC_DATA_SIZE;
+       instruction.data = data;
+       instruction.subdev = subdev;
+       instruction.chanspec=CR_PACK(chan,range,aref);
+
+//     comedi_loglevel(4);
+
+       /* Open comedi device */
+       device = comedi_open(filename);
+       if(!device){
+               comedi_perror(filename);
+               return(1);
+       }
+
+       comedi_do_insn(device , &instruction);
+
+       while(toc++ < TIME/(PERIOD/1000000000)){
+               comedi_data_read(device, subdev, chan, range, aref, &dataReaded);
+               printf("retour : %x\n", dataReaded);
+               nanosleep(&tempo, NULL);
+       }
+
+       comedi_close(device);
+
+       return 0;
+}
+
diff --git a/src/comedi/fpoulain/comedi+rtai/no-rt.c b/src/comedi/fpoulain/comedi+rtai/no-rt.c
new file mode 100755 (executable)
index 0000000..993721d
--- /dev/null
@@ -0,0 +1,87 @@
+/***********************************************************************
+Copyright (C) 2007  Francois Poulain, <fpoulain AT gmail DOT 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
+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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+***********************************************************************/
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <time.h>
+#include <math.h>
+
+#define TMAX           10.0                    /* in seconds           */
+#define FREQ           1.0                     /* in seconds^-1        */
+#define SAMPLE_PERIOD  1.0e09/50.0/FREQ        /* in nano seconds      */
+
+float function(int toc){
+       float time=toc*SAMPLE_PERIOD/1e09;
+       return 0x2000 + 4000*sin(time*FREQ*2*M_PI);
+}
+
+int main()
+{
+       comedi_t *device;
+       lsampl_t data=0x1000;
+       int ret;
+       int subdev = 1;         /* change this to your input subdevice */
+       int chan = 0;           /* change this to your channel */
+       int range = 0;          /* more on this later */
+       int aref = 0;           /* more on this later */
+       char filename[] = "/dev/comedi0";
+       int toc=0;
+
+       /* Structure for nanosleep() argument, see 'man nanosleep' for more details */
+       struct timespec tempo;
+       struct timeval before, after;
+
+       tempo.tv_sec=0;
+       tempo.tv_nsec=SAMPLE_PERIOD;
+
+       /* Open comedi device */
+       device = comedi_open(filename);
+       if(!device){
+               comedi_perror(filename);
+               return(1);
+       }
+
+       printf("# Required : %f microseconds, error in us :\n", SAMPLE_PERIOD/1000);
+
+       while(toc*SAMPLE_PERIOD/1e09 < TMAX)
+       {
+               if(toc%50==10){gettimeofday(&before, NULL);}
+               if(toc%50==11){
+                       gettimeofday(&after, NULL);;
+                       if (after.tv_usec > before.tv_usec) {printf("%f\n", SAMPLE_PERIOD/1000 - after.tv_usec + before.tv_usec);}
+               }
+
+               /* command computation */
+               data = function(toc);
+
+               /* sending to board */
+               ret = comedi_data_write(device, subdev, chan, range, aref, data);
+               if(ret < 0){
+                       comedi_perror(filename);
+                       return(1);
+               }
+
+               /* beautiful tempo ... */
+               nanosleep(&tempo, NULL);
+
+               /* post iteration */
+               toc++;
+       }
+
+       return 0;
+}
+
diff --git a/src/comedi/fpoulain/comedi+rtai/rt.c b/src/comedi/fpoulain/comedi+rtai/rt.c
new file mode 100755 (executable)
index 0000000..688f95d
--- /dev/null
@@ -0,0 +1,140 @@
+/***********************************************************************
+Copyright (C) 2007  Francois Poulain, <fpoulain AT gmail DOT 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
+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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+***********************************************************************/
+#include <stdio.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sched.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <rtai_lxrt.h>
+#include <rtai_sched.h>
+#include <pthread.h>
+#include <signal.h>
+#include  <math.h>
+#include <stdio.h>
+#include <comedilib.h>
+
+/* Some global variables */
+static pthread_t thread_ptr;
+
+int priority=20;               /* Highest              */
+int stack_size=0;              /* Use default (512)    */
+int msg_size=0;                /* Use default (256)    */
+
+#define TMAX           10.0                    /* in seconds           */
+#define FREQ           100.0                   /* in seconds^-1        */
+#define SAMPLE_PERIOD  1.0e09/50.0/FREQ        /* in nano seconds      */
+
+float function(int toc){
+       float time=toc*SAMPLE_PERIOD/1e09;
+       return 0x2000 + 4000*sin(time*FREQ*2*M_PI);
+
+}
+
+static void* task(void *unused)
+{
+       RT_TASK *handler;
+       comedi_t *device;
+       lsampl_t data=0x1000;
+       int ret;
+       int subdev = 1;         /* change this to your input subdevice */
+       int chan = 0;           /* change this to your channel */
+       int range = 0;          /* more on this later */
+       int aref = 0;           /* more on this later */
+       char filename[] = "/dev/comedi0";
+       int toc=0;
+       long before=0, after=0;
+
+       printf("# Required : %f nanoseconds, error in ns :\n", SAMPLE_PERIOD);
+
+       /* Open comedi device */
+       device = comedi_open(filename);
+       if(!device){comedi_perror(filename);}
+
+       /* Init RT Task */
+       if(!(handler = rt_task_init( nam2num("rtTask"), priority, stack_size, msg_size))){
+               printf("CANNOT INIT RT TASK\n"); 
+       }
+
+       /* Make thread periodic */
+       rt_task_make_periodic (handler, rt_get_time()+nano2count(SAMPLE_PERIOD), nano2count(SAMPLE_PERIOD));
+       
+       /* By now we can do serious things */
+       rt_make_hard_real_time();
+       
+       while(toc*SAMPLE_PERIOD/1e09 < TMAX)
+       {
+               /* wait the timer period */
+               rt_task_wait_period();
+               if(toc%5000==0){before = rt_get_time_ns();}
+               if(toc%5000==1){
+                       after = rt_get_time_ns();
+                       if (after>before) {printf("%f\n", SAMPLE_PERIOD - after + before);}
+               }
+
+               /* Command computation */
+               data = function(toc);
+               
+               /* Sending it to board */
+               ret = comedi_data_write(device, subdev, chan, range, aref, data);
+               if(ret < 0){comedi_perror(filename);}
+
+               /* Post iteration */
+               toc++;
+       }
+
+       /* Clean up ... */      
+       rt_make_soft_real_time();
+       rt_task_delete(handler);
+
+       return unused;
+}
+
+int main (void)
+{
+       struct sched_param mysched;
+
+       /* Verification */
+       if(getuid() != 0){fprintf(stderr, "must be root !\n"); exit(1);}
+       
+
+       /* Avoid swapping */
+       mlockall(MCL_CURRENT | MCL_FUTURE);             
+
+       /* Config scheduler */
+       mysched.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1;
+       if( sched_setscheduler( 0, SCHED_FIFO, &mysched ) == -1 ){
+               printf("ERROR IN SETTING THE SCHEDULER\n");
+               perror("errno");
+               exit(1);
+       }
+
+       /* Set and start timer */
+       rt_set_periodic_mode();
+       start_rt_timer(0);
+       
+       /* Launch main loop ... */
+       if(pthread_create(&thread_ptr, NULL, task, NULL) != 0) {perror("Creation du thread");}
+       /* ... and wait for it's end */
+       pthread_join(thread_ptr, NULL);
+
+       /* Terminus */
+       stop_rt_timer();
+       return(0);
+}
+
diff --git a/src/comedi/fpoulain/documentation/Makefile b/src/comedi/fpoulain/documentation/Makefile
new file mode 100755 (executable)
index 0000000..eed2a02
--- /dev/null
@@ -0,0 +1,4 @@
+all : comedi_mf624.pdf
+
+comedi_mf624.pdf : comedi_mf624.tex
+       pdflatex comedi_mf624.tex
diff --git a/src/comedi/fpoulain/documentation/comedi_mf624.tex b/src/comedi/fpoulain/documentation/comedi_mf624.tex
new file mode 100755 (executable)
index 0000000..c7aa691
--- /dev/null
@@ -0,0 +1,131 @@
+\documentclass[a4paper]{article}
+
+\usepackage{hyperref,xcolor,listings}
+
+\newcommand{\comedi}{\href{http://www.comedi.org}{\texttt{Comedi}}}
+
+\title{MF624 \comedi\ driver documentation}
+\author{Fran\c cois Poulain -- \href{mailto:Francois Poulain <fpoulain@gmail.com>?subject=MF624 Comedi Driver}{\tt fpoulain@gmail.com}}
+
+\definecolor{grey}{rgb}{0.97,0.97,0.97}
+\lstset{basicstyle=\tt\small, identifierstyle=\tt\color{green!50!darkgray}, commentstyle=\tt\color{blue!50!darkgray}, keywordstyle=\tt\color{red!50!darkgray}, stringstyle=\tt\color{red}, language=C, tabsize=4, showstringspaces=false, backgroundcolor=\color{grey}, xleftmargin=1.5em, xrightmargin=1.5em}
+
+\hypersetup{colorlinks, breaklinks, citecolor=black, linkcolor=blue!25!darkgray, urlcolor=green!25!darkgray, pdftitle={MF624 \comedi\ driver documentation}, pdfauthor={Fran\c cois Poulain}, pdfsubject={\comedi\ device driver}}
+
+\begin{document}
+\maketitle
+
+\tableofcontents
+
+\section{Introduction}
+
+The Humusoft MF624 PCI Card is a multifunction I/O Card available at url \url{http://www.humusoft.com/datacq/mf624.htm}. It has some features, like
+\begin{itemize}
+\item 8 channel 14 bits ADC,
+\item 8 channel 14 bits DAC,
+\item 8 digital inputs,
+\item 8 digital outputs,
+\item 4 quadrature encoder input,
+\item 5 timers/counter.
+\end{itemize}
+
+The status of this driver is quite experimental, and not all the feature explained here have been heavily tested. All the six main features of the board are supported on a \comedi\ subdevice: the classical analog and digital I/O are fully supported in the usual way of \comedi, whereas the timers/counters and the quadrature encoder inputs are implemented in a more specific way, which explain such a document.
+
+\section{Analog Output}
+
+\subsection{Overview}
+
+The D/A converter is supported on the \comedi\ subdevice \texttt{MF624\_AO\_SUBDEV}, and provide 8 different channels. This subdevice is writable and readable, writing provoke the changing of output, whereas the reading is provided by the software of the driver.
+
+\subsection{configuration}
+
+There is no configuration available on this subdevice.
+
+\subsection{Writing a sample}
+
+After having open the \comedi\ device, you can write a sample by calling the function \texttt{comedi\_data\_write}. There is an example below, where the \texttt{data} have to be a \comedi\ \texttt{lsample\_t} (which is like an \texttt{unsigned int}).
+
+\begin{lstlisting}
+/* sending to board */
+ret = comedi_data_write(device, subdev, chan, range, aref, data);
+if(ret < 0){
+       comedi_perror(filename);
+       return(1);
+}
+\end{lstlisting}
+
+The output of the channel is given by ${\rm out} = 1.2\; {\rm mV}\; ({\tt data} - {\tt 0x2000})$. The following code show how to trade with that mangling, without using \comedi\ \texttt{chanspec} features\footnote{None of them has been tested for the moment.}.
+
+\begin{lstlisting}
+lsampl_t volt2sample(float tension){
+       /* saturate output */
+       if(tension < -10)
+               return 0x0000;
+       if(tension > 9.9988)
+               return 0x3FFF;
+       return 0x2000 + tension/1.2e-3;
+}
+\end{lstlisting}
+
+\section{Analog Input}
+
+\subsection{Overview}
+
+The A/D converter is supported on the \comedi\ subdevice \texttt{MF624\_AI\_SUBDEV}, and provide 8 different channels with a resolution of 14 bit. This subdevice is only readable.
+
+\subsection{configuration}
+
+There is no configuration available on this subdevice.
+
+\subsection{Acquiring a sample}
+
+After having open the \comedi\ device, you can read a sample by calling the function \texttt{comedi\_data\_read}. There is an example below, where the \texttt{data} have to be a \comedi\ \texttt{lsample\_t} (which is like an \texttt{unsigned int}).
+
+\begin{lstlisting}
+/* sending to board */
+ret = comedi_data_write(device, subdev, chan, range, aref, data);
+if(ret < 0){
+       comedi_perror(filename);
+       return(1);
+}
+\end{lstlisting}
+
+The output of the channel is given by ${\rm out} = 1.2\; {\rm mV}\; ({\tt data} - {\tt 0x2000})$. The following code show how to trade with that mangling, without using \comedi\ \texttt{chanspec} features\footnote{None of them has been tested for the moment.}.
+
+\section{Digital Inputs}
+
+\subsection{Overview}
+
+\subsection{configuration}
+
+\subsection{Reading the inputs}
+
+\section{Digital Outputs}
+
+\subsection{Overview}
+
+\subsection{configuration}
+
+\subsection{Writing the outputs}
+
+\section{Counters/Timers}
+
+\subsection{Overview}
+
+\subsection{configuration}
+
+\subsection{Writing the preload registers}
+
+\subsection{Reading the counters' content}
+
+\subsection{Using counters to generate PWM signals}
+
+\section{Quadrature Encoder Inputs}
+
+\subsection{Overview}
+
+\subsection{configuration}
+
+\subsection{Reading the encoders' content}
+
+\end{document}
diff --git a/src/comedi/fpoulain/mf624.c b/src/comedi/fpoulain/mf624.c
new file mode 100755 (executable)
index 0000000..59069ab
--- /dev/null
@@ -0,0 +1,932 @@
+/*
+ * comedi/drivers/mf624.c
+ * Code for a Humusoft MF624 driver
+ *
+ * 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: mf624.o
+Description: A comedi driver for Humusoft MF624 PCI Card
+Devices: Humusoft MF624 Multifunction I/O Card
+Author: Jean-Matthieu Bourgeot - Francois Poulain
+Updated: Wed, 19 Sep 2007 14:29:43 +0200
+Status: experimental
+
+This driver is a driver for the Humusoft MF624 PCI Card.
+
+It has :
+ * 8 channel 14 bits ADC,
+ * 8 channel 14 bits DAC,
+ * 8 digital inputs,
+ * 8 digital outputs,
+ * 4 quadrature encoder input,
+ * 5 timers/counter.
+
+Status:
+A/D Converter                  : Supported on subdevice 0, 
+D/A Converter                  : Supported on subdevice 1,
+Digital Inputs                 : Supported on subdevice 2,
+Digital Outputs                        : Supported on subdevice 3,
+Counters/Timers                        : Supported on subdevice 4,
+Quadrature Encoder Inputs      : Supported on subdevice 5,
+IRQ                            : Not yet supported.
+
+Remarks:
+Simultaneous D/A update not yet supported.
+
+Configuration Options:
+none
+ */
+
+#include <linux/comedidev.h>
+#include <linux/pci.h>
+#include "mf624.h"
+
+MODULE_AUTHOR("Francois Poulain <fpoulain AT gmail DOT com>");
+MODULE_DESCRIPTION("Humusoft MF624 Multifunction I/O Card");
+MODULE_LICENSE("GPL");
+
+/* Some definition ... */
+/* FIXME : adopt a REAL guideline for naming macros ... */
+/* FIXME : adopt a REAL guideline for presenting functions */
+/* FIXME : verify ALL types used in the functions */
+
+#define EXTDEBUG 
+
+#define MF624_EXIT_FAILURE     -1
+
+/*  Addresses of the registers and  usefull definitions
+ *  for more informations, see the programming manual
+ *  available near http://www2.humusoft.cz/www/datacq/manuals/mf624um.pdf */
+
+/* BADR0 Memory Map */
+#define MF624_GPIOC            0x54
+#define MF624_INTCSR           0x4C
+
+/* BADR1 Memory Map */
+#define MF624_ADCTRL           0x00
+#define MF624_ADDATA           0x00
+#define MF624_DIN              0x10
+#define MF624_DOUT             0x10
+#define MF624_ADSTART          0x20
+#define MF624_DA0              0x20
+
+/* BADR2 Memory Map */
+#define MF624_CTRXMODE         0x00
+#define MF624_CTRXSTATUS       0x00
+#define MF624_CTRXCTRL         0x60
+#define MF624_CTRXA            0x04
+#define MF624_CTRXDATA         0x04
+#define MF624_CTRXB            0x08
+#define MF624_IRCCTRL          0x6C
+#define MF624_IRCSTATUS                0x6C
+#define MF624_ENC_READ         0x70
+
+/* MASK */
+#define MF624_GPIOC_EOLC       0x00020000      /* End of last conversion, bit 17 */
+#define MF624_GPIOC_LDAC       0x00800000      /* Enable Load D/A converter, bit 23 */
+#define MF624_GPIOC_DACEN      0x04000000      /* Enable D/A outputs, bit 26 */
+
+/* Some constants for handling Counter Control Register */
+#define MF624_CTR_START                0x01
+#define MF624_CTR_STOP         0x02
+#define MF624_CTR_LOAD         0x04
+#define MF624_CTR_RESET                0x08
+#define MF624_CTR_OUT_SET      0x10
+#define MF624_CTR_OUT_RESET    0x20
+
+/* Multiply the following constant by one above to obtain global behavior on all timers' channels */
+#define MF624_CTR_GLOB         0x01041041
+
+/* Some constants for handling IRC Control Register (quadrature encoders) */
+/* All are not present, it's under heavy development */
+#define MF624_IRC_MODE_RAISING 0x01
+#define MF624_IRC_COUNT_ENABLE 0x00
+#define MF624_IRC_COUNT_DISABLE        0x04
+#define MF624_IRC_RESET_ENABLE 0x10
+#define MF624_IRC_RESET_DISABLE        0x00
+#define MF624_IRC_RESET_ON_TOP 0x30
+#define MF624_IRC_FILTER       0x80
+
+/* Multiply the following constant by one above to obtain global behavior on all encoders' channels */
+#define MF624_IRC_GLOB         0x01010101
+
+/* Structure for board description */
+typedef struct mf624_board_struct{
+       char *name;
+       unsigned short device_id;       
+       unsigned int ai_chans;
+       unsigned int ai_bits;
+       unsigned int ao_chans;
+       unsigned int ao_bits;
+       unsigned int di_chans;
+       unsigned int do_chans;
+       unsigned int have_dio;
+       unsigned int timer_chans;
+       unsigned int timer_config[5];
+       unsigned int timer_loadA[5];
+       unsigned int timer_loadB[5];
+       unsigned int timer_control;
+       unsigned int enc_chans;
+       unsigned int enc_config;
+       unsigned int cnt_bits;
+}mf624_board;
+
+/* Board description */
+/* FIXME: timer_config, timer_control, and enc_config should be in the private structure */
+static const mf624_board mf624_boards[] = {
+       {
+               name:           "mf624",        /* device name          */
+               device_id:      0x0624,         /* PCI dev ID           */
+               ai_chans:       8,              /* Num of ADC channels  */
+               ai_bits:        14,             /* Num of ADC bits      */
+               ao_chans:       8,              /* Num of DAC channels  */
+               ao_bits:        14,             /* Num of DAC bits      */
+               di_chans:       8,              /* Num of digital in    */
+               do_chans:       8,              /* Num of digital out   */
+               have_dio:       0,              /* Num of DIO           */
+               timer_chans:    5,              /* Num of timers/cnt    */
+               timer_config:   {0,0,0,0},      /* Save of the timer configuration */
+               timer_loadA:    {0,0,0,0},      /* Save of the timer loadA registers */
+               timer_loadB:    {0,0,0,0},      /* Save of the timer loadB registers */
+               timer_control:  0,              /* Save counter state control */
+               enc_chans:      6,              /* Num of encoders : 4 + 1 virtual channel containing index register 
+                                                  + 1 virtual channel containing IRC status */
+               enc_config:     0,              /* Save of the encoder configuration */
+               cnt_bits:       32,             /* Size of timers/enc   */
+       },
+};
+
+/* Number of boards */
+#define N_BOARDS       (sizeof(mf624_boards) / sizeof(mf624_board))
+
+/* PCI vendor and device ID */
+#define PCI_VENDOR_ID_MF624 0x186c
+#define PCI_DEVICE_ID_MF624 0x0624
+
+static struct pci_device_id mf624_pci_table[] __devinitdata = {
+       { PCI_VENDOR_ID_MF624, PCI_DEVICE_ID_MF624, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+       { 0 }
+};
+MODULE_DEVICE_TABLE(pci, mf624_pci_table);
+
+/* Useful for shorthand access to the particular board structure */
+#define thisboard ((mf624_board *)dev->board_ptr)
+
+/* Private data structure */
+typedef struct{
+       int data;
+
+       /* would be useful for a PCI device */
+       struct pci_dev *pci_dev;
+
+       /* base addresses */
+       resource_size_t iobase;
+
+       unsigned long BADR0;    
+       unsigned long BADR1;
+       unsigned long BADR2;    
+
+       void* IO_BADR0; 
+       void* IO_BADR1;
+       void* IO_BADR2; 
+
+       /* Used for AO readback */
+       lsampl_t ao_readback[8];
+}mf624_private;
+
+/*
+ * most drivers define the following macro to make it easy to
+ * access the private structure.
+ */
+#define devpriv ((mf624_private *)dev->private)
+
+/* Attach/detach functions declaration */
+static int mf624_attach(comedi_device *dev,comedi_devconfig *it);
+static int mf624_detach(comedi_device *dev);
+
+/* Analog functions operation to be attached */
+static int mf624_ai_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
+static int mf624_ai_cfg(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
+static int mf624_ao_winsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
+static int mf624_ao_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
+static int mf624_ao_cfg(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
+
+/* Digital functions operation to be attached */
+static int mf624_di_insn_bits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
+static int mf624_do_insn_bits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
+
+/* Timers/counters functions operation to be attached */
+static int mf624_timer_winsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
+static int mf624_timer_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
+static int mf624_timer_cfg(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
+
+/* Encoder functions operation to be attached */
+static int mf624_enc_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
+static int mf624_enc_cfg(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
+
+/*static irqreturn_t mf624_interrupt(int irq, void *d, struct pt_regs *regs);*/
+
+/*
+ * 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 comedi_driver driver_mf624={
+       driver_name:    "mf624",
+       module:         THIS_MODULE,
+       attach:         mf624_attach,
+       detach:         mf624_detach,
+};
+
+/*
+ * 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 mf624_attach(comedi_device *dev,comedi_devconfig *it){
+       comedi_subdevice *s;
+       struct pci_dev* pcidev;
+       unsigned int index, channel, status;
+
+       int regA, regB;
+
+       rt_printk("comedi%d: mf624: driver: Bourgeot - Poulain 2006-2007\n", dev->minor);
+       rt_printk("This is an experimental version, you can report some remarks or problems to fpoulain@gmail.com\n");
+
+       /*
+        * Allocate the private structure area.  alloc_private() is a
+        * convenient macro defined in comedidev.h.
+        * FIXME : do this allocation in a dedicated function
+        */
+       if(alloc_private(dev,sizeof(mf624_private))<0) {return -ENOMEM;}
+
+       /* Probe the device to determine what device in the series it is */
+       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)) {
+               /* is it not a computer boards card ? */
+               if(pcidev->vendor != PCI_VENDOR_ID_MF624){continue;}
+               /* loop through cards supported by this driver */
+               for(index = 0; index < N_BOARDS ; index++)
+               {
+                       if(mf624_boards[index].device_id != pcidev->device){continue;}
+                       /* was a particular bus/slot requested ? */
+                       if(it->options[0] || it->options[1])
+                       {
+                               /* are we on the wrong bus/slot ? */
+                               if(pcidev->bus->number != it->options[0] ||
+                                  PCI_SLOT(pcidev->devfn) != it->options[1]){continue;}
+                       }
+                       devpriv->pci_dev = pcidev;
+                       dev->board_ptr = mf624_boards + index;
+                       goto found;
+               }
+       }
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: No supported Humusoft card found on requested position\n", dev->minor);
+#endif
+       comedi_error(dev, "No supported Humusoft card found on requested position\n");
+       return -EIO;
+
+found:
+       rt_printk("comedi%d: mf624: Found %s on bus %i, slot %i\n", dev->minor, mf624_boards[index].name, pcidev->bus->number, PCI_SLOT(pcidev->devfn));
+
+       /* Enable PCI device and reserve I/O ports. */
+       if(pci_enable_device(pcidev)){
+#ifdef EXTDEBUG
+               rt_printk("comedi%d: mf624: Failed to enable PCI device\n", dev->minor);
+#endif
+               comedi_error(dev, "Failed to enable PCI device\n");
+               return -EIO;
+       }
+       if(pci_request_regions(pcidev, "mf624")){
+#ifdef EXTDEBUG
+               rt_printk("comedi%d: mf624: I/O port conflict\n", dev->minor);
+#endif
+               comedi_error(dev, "I/O port conflict\n");
+               return -EIO;
+       }
+
+       /* Initialize devpriv->control_status and to point to their base address */
+       devpriv->BADR0 = pci_resource_start(devpriv->pci_dev, 0);
+       devpriv->BADR1 = pci_resource_start(devpriv->pci_dev, 1);
+       devpriv->BADR2 = pci_resource_start(devpriv->pci_dev, 2);
+
+       devpriv->IO_BADR0 = ioremap_nocache(pci_resource_start(devpriv->pci_dev, 0),
+               pci_resource_len(devpriv->pci_dev, 0)); 
+       devpriv->IO_BADR1 = ioremap_nocache(pci_resource_start(devpriv->pci_dev, 2),
+               pci_resource_len(devpriv->pci_dev, 2)); 
+       devpriv->IO_BADR2 = ioremap_nocache(pci_resource_start(devpriv->pci_dev, 4),
+               pci_resource_len(devpriv->pci_dev, 4)); 
+
+#ifdef EXTDEBUG
+       rt_printk ( "comedi%d: mf624: PCI Resource 0    addr %lx \n", dev->minor, devpriv->BADR0 );
+       rt_printk ( "comedi%d: mf624: PCI Resource 1    addr %lx \n", dev->minor, devpriv->BADR1 );
+       rt_printk ( "comedi%d: mf624: PCI Resource 2    addr %lx \n", dev->minor, devpriv->BADR2 );
+
+       rt_printk ( "comedi%d: mf624: IO_BADR0    addr %p \n", dev->minor, devpriv->IO_BADR0 );
+       rt_printk ( "comedi%d: mf624: IO_BADR1(2) addr %p \n", dev->minor, devpriv->IO_BADR1 );
+       rt_printk ( "comedi%d: mf624: IO_BADR2(4) addr %p \n", dev->minor, devpriv->IO_BADR2 ); 
+#endif
+
+       /*
+        * 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 = thisboard->name;
+
+       /*
+        * Allocate the subdevice structures.  alloc_subdevice() is a
+        * convenient macro defined in comedidev.h.
+        */
+       if(alloc_subdevices(dev, 6)<0)
+               return -ENOMEM;
+
+       s=dev->subdevices+0;
+       /* analog single ended (ground) input subdevice */
+       s->type = COMEDI_SUBD_AI;
+       s->subdev_flags = SDF_READABLE|SDF_GROUND;
+       s->n_chan = thisboard->ai_chans;
+       s->maxdata = (1<<thisboard->ai_bits)-1;
+       s->range_table = &range_bipolar10;
+       s->len_chanlist = 8;
+       s->insn_read = mf624_ai_rinsn;
+       s->insn_config = mf624_ai_cfg;
+
+       s=dev->subdevices+1;
+       /* analog output subdevice */
+       s->type = COMEDI_SUBD_AO;
+       s->subdev_flags = SDF_WRITABLE;
+       s->n_chan = thisboard->ao_chans;
+       s->maxdata = (1<<thisboard->ao_bits)-1;
+       s->range_table = &range_bipolar10;
+       s->insn_write = mf624_ao_winsn;
+       s->insn_read = mf624_ao_rinsn;
+       s->insn_config = mf624_ao_cfg;
+
+       s=dev->subdevices+2;
+       /* digital input subdevice */   
+       s->type = COMEDI_SUBD_DI;
+       s->subdev_flags = SDF_READABLE;
+       s->n_chan = thisboard->di_chans;
+       s->maxdata = 1;
+       s->range_table = &range_digital;
+       s->insn_bits = mf624_di_insn_bits;
+
+       s=dev->subdevices+3;
+       /* digital output subdevice */  
+       s->type = COMEDI_SUBD_DO;
+       s->subdev_flags = SDF_WRITABLE;
+       s->n_chan = thisboard->do_chans;
+       s->maxdata = 1;
+       s->range_table = &range_digital;
+       s->insn_bits  = mf624_do_insn_bits;
+
+       s=dev->subdevices+4;
+       /* timer device */
+       s->type = COMEDI_SUBD_TIMER;
+       s->subdev_flags = SDF_WRITABLE|SDF_READABLE;
+       s->n_chan = thisboard->timer_chans;
+       s->maxdata = (1<<thisboard->cnt_bits)-1;
+       s->range_table = &range_digital;
+       s->insn_config = mf624_timer_cfg;
+       s->insn_write = mf624_timer_winsn;
+       s->insn_read = mf624_timer_rinsn;
+
+       s=dev->subdevices+5;
+       /* encoder device */
+       s->type = COMEDI_SUBD_COUNTER;
+       s->subdev_flags = SDF_READABLE|SDF_GROUND|SDF_DIFF;
+       s->n_chan = thisboard->enc_chans;
+       s->maxdata = (1<<thisboard->cnt_bits)-1;
+       s->range_table = &range_digital;
+       s->insn_config = mf624_enc_cfg;
+       s->insn_read = mf624_enc_rinsn;
+
+       rt_printk("comedi%d: mf624: Driver attached\n", dev->minor);
+
+       /* FIXME : do those initialisation in some dedicated functions */
+
+       /* Enable DAC */
+       status = readl(devpriv->IO_BADR0 + MF624_GPIOC) | MF624_GPIOC_DACEN;  
+       writel(status, devpriv->IO_BADR0 + MF624_GPIOC);
+
+       /* Initialise Interrupt Control Status */
+       writel(0x00000000, devpriv->IO_BADR0 + MF624_INTCSR);
+
+       /* Initialise analog outputs to zero */
+       for(channel=0 ; channel<thisboard->ao_chans ; channel++){
+               writew(0x2000,devpriv->IO_BADR1 + MF624_DA0 + 2*channel);
+               /* Readback save */
+               devpriv->ao_readback[channel] = 0x2000;
+       }
+
+       /* Initialise counters to initial state and reset them */
+       for(channel=0 ; channel<thisboard->timer_chans ; channel++){
+               /* Restore initial state */
+               writel(0x00000020, devpriv->IO_BADR2 + MF624_CTRXMODE + 0x10*channel);
+               /* Reset A register */
+               writel(0x00000000, devpriv->IO_BADR2 + MF624_CTRXB + 0x10*channel);
+               /* Reset B register (don't exist for timer 4) */
+               if(channel!=4) {writel(0x00000000, devpriv->IO_BADR2 + MF624_CTRXB + 0x10*channel);}
+       }
+       /* Reset and stop all timers in one shot !*/
+       writel((MF624_CTR_RESET|MF624_CTR_OUT_RESET|MF624_CTR_STOP) * MF624_CTR_GLOB, devpriv->IO_BADR2 + MF624_CTRXCTRL);
+
+       /* test output 1kHz frequency */
+       /* calc preset register : Reg = (long)(1<<chanBits) -
+          Fclk/Fout */
+#define RAPPORT_CYCLIQUE 0.5
+#define PERIODE 0.001
+       regA = (long)(1<<32) - 100000*PERIODE*RAPPORT_CYCLIQUE;
+       regB = (long)(1<<32) - 100000*PERIODE*(1-RAPPORT_CYCLIQUE);
+       rt_printk("Preset registers for PWM output : A = %x and B = %x \n", regA, regB);
+       /* load preset in A reg */
+       writel(regA, devpriv->IO_BADR2 + MF624_CTRXA);
+       /* load preset/2 in B reg */
+       writel(regB, devpriv->IO_BADR2 + MF624_CTRXB);
+       /* Configure CTR0 */
+       writel(0x0000C00F, devpriv->IO_BADR2 + MF624_CTRXMODE);
+       /* Load register and start counter 0 */
+       writel(MF624_CTR_LOAD|MF624_CTR_START, devpriv->IO_BADR2 + MF624_CTRXCTRL);
+       rt_printk("Counter 0 status = %x\n", readl(devpriv->IO_BADR2));
+
+       /* Stop and keep reset all encoders */
+       thisboard->enc_config = (MF624_IRC_COUNT_DISABLE|MF624_IRC_RESET_ENABLE) * MF624_IRC_GLOB;
+       writel(thisboard->enc_config, devpriv->IO_BADR2 + MF624_IRCCTRL);
+
+       /* End of initialisation */
+       rt_printk("comedi%d: mf624: Board initialized\n", dev->minor);
+       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 mf624_detach(comedi_device *dev)
+{
+       rt_printk("comedi%d: mf624: remove\n", dev->minor);
+
+       if(dev->irq){comedi_free_irq(dev->irq, dev);}
+
+       if(devpriv && devpriv->pci_dev)
+       {
+               if(devpriv->BADR0)
+               {
+                       pci_release_regions(devpriv->pci_dev);
+                       pci_disable_device(devpriv->pci_dev);
+                       rt_printk("comedi%d: mf624: remove2\n", dev->minor);
+               }
+               pci_dev_put(devpriv->pci_dev);
+               rt_printk("comedi%d: mf624: remove3\n", dev->minor);
+       }
+return 0;
+}
+
+/* read n samples on Analog Input channel */
+static int mf624_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
+{
+       unsigned int dat, n, i, status, chan = CR_CHAN(insn->chanspec);
+
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: mf624_ai_rinsn called \n", dev->minor);
+#endif
+
+       /* write channel to multiplexer */
+       writew(1<<chan,devpriv->IO_BADR1 + MF624_ADCTRL);
+
+       /* convert n samples */
+       for(n=0;n<insn->n;n++){
+               /* trigger conversion */
+               dat = readw(devpriv->IO_BADR1 + MF624_ADSTART);
+
+#define TIMEOUT 100 
+               /* wait for conversion to end */
+               for(i=0;i<TIMEOUT;i++){
+                       status = 1;
+                       status = readl(devpriv->IO_BADR0 + MF624_GPIOC);
+                       if(!(status & MF624_GPIOC_EOLC))break;
+               }
+               if(i==TIMEOUT){
+                       rt_printk("comedi%d: mf624: _ai_rinsn: conversion timeout !\n", dev->minor);
+                       comedi_error(dev, "Conversion timeout !\n");
+                       return -ETIMEDOUT;
+               }
+
+               /* read data */
+               dat = readw(devpriv->IO_BADR1 + MF624_ADDATA);
+
+               /* mangle the data as necessary */
+               dat ^= 1<<(thisboard->ai_bits-1);
+
+               data[n] = dat;
+       }
+
+       /* return the number of samples read/written */
+       return n;
+}
+
+/* Analog input configuration */
+static int mf624_ai_cfg(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data){
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _insn_ai_cfg called\n", dev->minor);
+#endif
+       return insn->n;
+}
+
+/* write n samples on Analog Output channel */
+static int mf624_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
+{
+       unsigned int i, chan = CR_CHAN(insn->chanspec), status;
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _ao_winsn called\n", dev->minor);
+#endif
+       status = readl(devpriv->IO_BADR0 + MF624_GPIOC);
+       status = status | MF624_GPIOC_DACEN;
+       writel(status, devpriv->IO_BADR0 + MF624_GPIOC);
+       /* Writing a list of values to an AO channel is probably not
+        * very useful, but that's how the interface is defined. */
+       for(i=0;i<insn->n;i++){
+               /* a typical programming sequence */
+               writew(data[i],devpriv->IO_BADR1 + MF624_DA0 + 2*chan);
+               devpriv->ao_readback[chan] = data[i];
+#ifdef EXTDEBUG
+               rt_printk("comedi%d: mf624: _ao_winsn: wrote at address %i data %i  ", dev->minor, MF624_DA0 + 2*chan , data[i] );
+#endif
+       }
+
+       /* return the number of samples read/written */
+       return i;
+}
+
+/* Analog output configuration */
+static int mf624_ao_cfg(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data){
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _ao_cfg called\n, dev->minor");
+#endif
+       return insn->n;
+}
+
+/* AO subdevices should have a read insn as well as a write insn.
+ * Usually this means copying a value stored in devpriv. */
+static int mf624_ao_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
+{
+       unsigned int i, chan = CR_CHAN(insn->chanspec);
+
+       for(i=0;i<insn->n;i++)
+               data[i] = devpriv->ao_readback[chan];
+
+       return i;
+}
+
+/* Write digital data */
+static int mf624_do_insn_bits(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data)
+{
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _do_insn_bits called with data: %d %d\n", dev->minor, data[0], data[1]);
+#endif
+       if(insn->n!=2)return -EINVAL;
+
+       if(data[0]){
+               s->state &= ~data[0];
+               s->state |= data[0]&data[1];
+#ifdef EXTDEBUG
+               rt_printk ("comedi%d: mf624: _do_insn_bits: out: %d on %lx\n", dev->minor, s->state, devpriv->IO_BADR1 + MF624_DOUT );
+#endif
+               writew(s->state, devpriv->IO_BADR1 + MF624_DOUT);                                       
+       }
+       return 2;
+}
+
+/* Read digital data */
+static int mf624_di_insn_bits(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data)
+{
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _di_insn_bits called with data: %d %d\n", dev->minor, data[0], data[1]);
+#endif
+       if(insn->n!=2)return -EINVAL;
+
+       data[1] = readw(devpriv->IO_BADR1 + MF624_DIN );
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _di_insn_bits read data: %d\n", dev->minor, data[1]);
+#endif
+       return 2;
+}
+
+/* Set the preload registers */
+static int mf624_timer_winsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data){
+       unsigned int channel = CR_CHAN(insn->chanspec);
+
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _timer_winsn called for channel %d, with n = %d\n", dev->minor, channel, insn->n);
+#endif
+
+       if(insn->n > 2)return -EINVAL;
+
+       thisboard->timer_loadA[channel] = data[0];
+       writel(thisboard->timer_loadA[channel], devpriv->IO_BADR2 + MF624_CTRXA + 0x10*channel);
+
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _timer_winsn: preset register A set at %8x\n", dev->minor, thisboard->timer_loadA[channel]);
+#endif
+
+       if(insn->n == 2){
+               thisboard->timer_loadB[channel] = data[1];
+               writel(thisboard->timer_loadB[channel], devpriv->IO_BADR2 + MF624_CTRXB + 0x10*channel);
+
+#ifdef EXTDEBUG
+               rt_printk("comedi%d: mf624: _timer_winsn: preset register B set at %8x\n", dev->minor, thisboard->timer_loadB[channel]);
+#endif
+       }
+
+       return insn->n;
+}
+
+/* Read the timer or the preload registers */
+static int mf624_timer_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data){
+       unsigned int channel = CR_CHAN(insn->chanspec), i = 0, status = 0, loadA = 0, loadB = 0, config = 0;
+
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _timer_rinsn called for channel %d, with n = %d\n", dev->minor, channel, insn->n);
+#endif
+
+       if(insn->n == 4){
+               /* Read the counter status, the preloads registers and the counter configuration */
+               config = thisboard->timer_config[channel];
+               loadA = thisboard->timer_loadA[channel];
+               loadB = thisboard->timer_loadB[channel];
+               status = readl(devpriv->IO_BADR2 + MF624_CTRXSTATUS + 0x10*channel);
+#ifdef EXTDEBUG
+               rt_printk("comedi%d: mf624: _timer_rinsn: status = %8x, loadA = %8x, loadB = %8x, config = %8x\n", dev->minor, status, loadA, loadB, config);
+#endif
+
+               data[0] = status;
+               data[1] = loadA;
+               data[2] = loadB;
+               data[3] = config;
+       }
+       else
+               for(i = 0 ; i < insn->n ; i++) data[i] = readl(devpriv->IO_BADR2 + MF624_CTRXDATA + 0x10*channel);                      
+
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _timer_rinsn: readed %x\n", dev->minor, data[insn->n-1]);
+#endif
+       return insn->n;
+}
+
+/* Configure timers */
+static int mf624_timer_cfg(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data){
+
+       unsigned int bitfield = 0, bitfield2 = 0, status = 0, mask = 0, mask2 = 0, i = 0, dataIndex = 0, channel = CR_CHAN(insn->chanspec);
+
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _timer_cfg called with %d data for channel %d and datas : ", dev->minor, insn->n, channel);
+       for(i=0 ; i < insn->n ; i++) rt_printk("%x ", data[i]);
+       rt_printk("\n");
+#endif
+
+       /*  For more informations, see the programming manual
+        *  available near http://www2.humusoft.cz/www/datacq/manuals/mf624um.pdf */
+
+       /* FIXME : test and fix if maximum one opcode is given for each subconfiguration */
+       /* FIXME : thereis an *nicer* way to do this, using some unnecessary flags defined in mf624.h */
+       for(dataIndex = 0 ; dataIndex < insn->n ; dataIndex++){
+               switch(data[dataIndex]){
+                       /* Mode register configuration */
+
+                       /* Configure direction                  */
+                       case MF624_CTR_DIRECTION_DOWN           : bitfield |= 0x00; mask |= 0x01; break ;
+                       case MF624_CTR_DIRECTION_UP             : bitfield |= 0x01; mask |= 0x01; break ;
+
+                       /* Configure repetition                 */
+                       case MF624_CTR_REPETITION_OFF           : bitfield |= 0x00<<1; mask |= 0x01<<1; break ;
+                       case MF624_CTR_REPETITION_ON            : bitfield |= 0x01<<1; mask |= 0x01<<1; break ;
+
+                       /* Configure load toggle                */
+                       case MF624_CTR_PRELOAD_A                : bitfield |= 0x00<<2; mask |= 0x01<<2; break ;
+                       case MF624_CTR_PRELOAD_AB               : bitfield |= 0x01<<2; mask |= 0x01<<2; break ;
+
+                       /* Configure output toggle              */
+                       case MF624_CTR_OUTPUT_MONOSTABLE        : bitfield |= 0x00<<3; mask |= 0x01<<3; break ;
+                       case MF624_CTR_OUTPUT_BISTABLE          : bitfield |= 0x01<<3; mask |= 0x01<<3; break ;
+
+                       /* Configure output control             */
+                       case MF624_CTR_OUTPUT_DIRECT            : bitfield |= 0x00<<4; mask |= 0x03<<4; break ;
+                       case MF624_CTR_OUTPUT_INVERSE           : bitfield |= 0x01<<4; mask |= 0x03<<4; break ;
+                       case MF624_CTR_OUTPUT_OFF               : bitfield |= 0x02<<4; mask |= 0x03<<4; break ;
+                       case MF624_CTR_OUTPUT_ON                : bitfield |= 0x03<<4; mask |= 0x03<<4; break ;
+
+                       /* Configure trigger source             */
+                       case MF624_CTR_TRIGGER_DISABLED         : bitfield |= 0x00<<6; mask |= 0x03<<6; break ;
+                       case MF624_CTR_TRIGGER_BYINPUT          : bitfield |= 0x01<<6; mask |= 0x03<<6; break ;
+                       case MF624_CTR_TRIGGER_BYLOWER          : bitfield |= 0x02<<6; mask |= 0x03<<6; break ;
+                       case MF624_CTR_TRIGGER_BYUPPER          : bitfield |= 0x03<<6; mask |= 0x03<<6; break ;
+
+                       /* Configure trigger type               */
+                       case MF624_CTR_TRIGGER_DISABLE          : bitfield |= 0x00<<8; mask |= 0x03<<8; break ;
+                       case MF624_CTR_TRIGGER_RAISING          : bitfield |= 0x01<<8; mask |= 0x03<<8; break ;
+                       case MF624_CTR_TRIGGER_FALLING          : bitfield |= 0x02<<8; mask |= 0x03<<8; break ;
+                       case MF624_CTR_TRIGGER_EITHER           : bitfield |= 0x03<<8; mask |= 0x03<<8; break ;
+
+                       /* Configure retrigger                  */
+                       case MF624_CTR_RETRIGGER_OFF            : bitfield |= 0x00<<10; mask |= 0x01<<10; break ;
+                       case MF624_CTR_RETRIGGER_ON             : bitfield |= 0x01<<10; mask |= 0x01<<10; break ;
+
+                       /* Configure gate source                */
+                       case MF624_CTR_GATE_HIGHT               : bitfield |= 0x00<<11; mask |= 0x03<<11; break ;
+                       case MF624_CTR_GATE_BYINPUT             : bitfield |= 0x01<<11; mask |= 0x03<<11; break ;
+                       case MF624_CTR_GATE_BYLOWER             : bitfield |= 0x02<<11; mask |= 0x03<<11; break ;
+                       case MF624_CTR_GATE_BYUPPER             : bitfield |= 0x03<<11; mask |= 0x03<<11; break ;
+
+                       /* Configure gate polarity              */
+                       case MF624_CTR_GATEPOLARITY_LOW         : bitfield |= 0x00<<13; mask |= 0x01<<13; break ;
+                       case MF624_CTR_GATEPOLARITY_HIGH        : bitfield |= 0x01<<13; mask |= 0x01<<13; break ;
+
+                       /* Configure clock source               */
+                       case MF624_CTR_CKSOURCE_50M             : bitfield |= 0x00<<14; mask |=0x0F<<14 ; break ;
+                       case MF624_CTR_CKSOURCE_10M             : bitfield |= 0x01<<14; mask |=0x0F<<14 ; break ;
+                       case MF624_CTR_CKSOURCE_1M              : bitfield |= 0x02<<14; mask |=0x0F<<14 ; break ;
+                       case MF624_CTR_CKSOURCE_100K            : bitfield |= 0x03<<14; mask |=0x0F<<14 ; break ;
+                       case MF624_CTR_CKSOURCE_RAISINGINPUT    : bitfield |= 0x05<<14; mask |=0x0F<<14 ; break ;
+                       case MF624_CTR_CKSOURCE_FALLINGINPUT    : bitfield |= 0x06<<14; mask |=0x0F<<14 ; break ;
+                       case MF624_CTR_CKSOURCE_EITHERINPUT     : bitfield |= 0x07<<14; mask |=0x0F<<14 ; break ;
+                       case MF624_CTR_CKSOURCE_RAISINGLOWER    : bitfield |= 0x09<<14; mask |=0x0F<<14 ; break ;
+                       case MF624_CTR_CKSOURCE_FALLINGLOWER    : bitfield |= 0x0A<<14; mask |=0x0F<<14 ; break ;
+                       case MF624_CTR_CKSOURCE_EITHERLOWER     : bitfield |= 0x0B<<14; mask |=0x0F<<14 ; break ;
+                       case MF624_CTR_CKSOURCE_RAISINGUPPER    : bitfield |= 0x0D<<14; mask |=0x0F<<14 ; break ;
+                       case MF624_CTR_CKSOURCE_FALLINGUPPER    : bitfield |= 0x0E<<14; mask |=0x0F<<14 ; break ;
+                       case MF624_CTR_CKSOURCE_EITHERUPPER     : bitfield |= 0x0F<<14; mask |=0x0F<<14 ; break ;
+
+                       /* Configure analog trigger             */
+                       case MF624_CTR_ADTRIGSRC_INT            : bitfield |= 0x00<<30; mask |= 0x01<<30; break ;
+                       case MF624_CTR_ADTRIGSRC_EXT            : bitfield |= 0x01<<30; mask |= 0x01<<30; break ;
+
+                       /* Configure CTR4 interrupt source      */
+                       case MF624_CTR_CTR4INTSRC_INT           : bitfield |= 0x00<<31; mask |= 0x01<<31; break ;
+                       case MF624_CTR_CTR4INTSRC_EXT           : bitfield |= 0x01<<31; mask |= 0x01<<31; break ;
+
+                       /* Control register affection           */
+                       case MF624_CTR_CTRL_START               : bitfield2 |= 0x01; break;
+                       case MF624_CTR_CTRL_STOP                : bitfield2 |= 0x02; break;
+                       case MF624_CTR_CTRL_LOAD                : bitfield2 |= 0x04; break;
+                       case MF624_CTR_CTRL_RESET               : bitfield2 |= 0x08; break;
+                       case MF624_CTR_CTRL_TSET                : bitfield2 |= 0x10; break;
+                       case MF624_CTR_CTRL_TRESET              : bitfield2 |= 0x20; break;
+
+                       /* Error handling                       */
+                       default : return MF624_EXIT_FAILURE;
+               }
+       }
+       status = thisboard->timer_config[channel];
+       mask = ~mask;
+
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _timer_cfg: bitfield = %8x, mask = %8x, status = %8x\n", dev->minor, bitfield, mask, status);
+       rt_printk("comedi%d: mf624: _timer_cfg: writing %8x in %8x \n", dev->minor, (status & mask) | bitfield, devpriv->IO_BADR2 + MF624_CTRXMODE + 0x10*channel);
+#endif
+
+       thisboard->timer_config[channel] = (status & mask) | bitfield;
+       if (channel != 4 && (thisboard->timer_config[channel] & 0xC0000000) !=0){
+               rt_printk("comedi%d: mf624: _timer_cfg: Only 4th counter implement those features!\n", dev->minor);
+               comedi_error(dev, "Only 4th counter implement those features!\n");
+       }
+       writel(thisboard->timer_config[channel], devpriv->IO_BADR2 + MF624_CTRXMODE + 0x10*channel);
+
+       mask2 = ~(0x3F << (6*channel));
+       status = thisboard->timer_control;
+
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _timer_cfg: bitfield2 = %8x, mask2 = %8x, status2 = %8x\n", dev->minor, bitfield2, mask2, status);
+       rt_printk("comedi%d: mf624: _timer_cfg: writing %8x in %8x \n", dev->minor, (status & mask2) | (bitfield2 << (6*channel)), devpriv->IO_BADR2 + MF624_CTRXCTRL);
+#endif
+
+       thisboard->timer_control = (status & mask2) + (bitfield << (6*channel));
+       writel(thisboard->timer_control, devpriv->IO_BADR2 + MF624_CTRXCTRL);
+
+       return insn->n;
+}
+
+/* Read an encoder */
+static int mf624_enc_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data){
+       unsigned int i, channel = CR_CHAN(insn->chanspec);
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _enc_rinsn called for channel %d, with n = %d\n", dev->minor, channel, insn->n);
+#endif
+       for(i=0;i<insn->n;i++){
+               if(channel == (thisboard->enc_chans - 1)){
+                       /* virtual channel 5 : read index register */
+                       data[i] = readl(devpriv->IO_BADR2 + MF624_IRCSTATUS) & 0x01010101;
+               }
+               if(channel == (thisboard->enc_chans - 2)){
+                       /* virtual channel 4 : read configuration register */
+                       data[i] = thisboard->enc_config;
+               }
+               else
+                       data[i] = readl(devpriv->IO_BADR2 + MF624_ENC_READ + 0x04*channel);
+       }
+
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _enc_rinsn: readed %x\n", dev->minor, data[insn->n-1]);
+#endif
+
+       return insn->n;
+}
+
+/* Configure encoders */
+static int mf624_enc_cfg(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data){
+
+       unsigned char bitfield = 0;
+       unsigned int status = 0, mask = 0, i = 0, dataIndex = 0, channel = CR_CHAN(insn->chanspec);
+
+       /* You cannot configure virtual channel */
+       if(channel >= (thisboard->enc_chans - 2)){
+               rt_printk("comedi%d: mf624: trying to configure virtual channel!\n", dev->minor);
+               comedi_error(dev, "Trying to configure virtual channel!\n");
+               return MF624_EXIT_FAILURE;
+       }
+
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _enc_cfg called with %d data for channel %d and datas : ", dev->minor, insn->n, channel);
+       for(i=0 ; i < insn->n ; i++) rt_printk("%x ", data[i]);
+       rt_printk("\n");
+#endif
+
+       /*  For more informations, see the programming manual
+        *  available near http://www2.humusoft.cz/www/datacq/manuals/mf624um.pdf */
+
+       /* FIXME : test and fix if maximum one opcode is given for each subconfiguration */
+       for(dataIndex = 0 ; dataIndex < insn->n ; dataIndex++){
+               switch(data[dataIndex]){
+                       /* Configure encoder mode       */
+                       case MF624_ENC_MODE_4EDGE :             bitfield |= 0x00 ; break ;
+                       case MF624_ENC_MODE_RISING :            bitfield |= 0x01 ; break ;
+                       case MF624_ENC_MODE_FALLING :           bitfield |= 0x02 ; break ;
+                       case MF624_ENC_MODE_EITHER :            bitfield |= 0x03 ; break ;
+
+                       /* Configure encoder counting   */
+                       case MF624_ENC_COUNT_ENABLE :           bitfield |= 0x00 ; break ;
+                       case MF624_ENC_COUNT_DISABLE :          bitfield |= 0x04 ; break ;
+                       case MF624_ENC_COUNT_ENABLE_BYINPUT :   bitfield |= 0x08 ; break ;
+                       case MF624_ENC_COUNT_DISABLE_BYINPUT :  bitfield |= 0x0C ; break ;
+
+                       /* Configure encoder reset      */
+                       case MF624_ENC_RESET_DISABLE :          bitfield |= 0x00 ; break ;
+                       case MF624_ENC_RESET_ENABLE :           bitfield |= 0x10 ; break ;
+                       case MF624_ENC_RESET_DISABLE_BYINPUT :  bitfield |= 0x20 ; break ;
+                       case MF624_ENC_RESET_ENABLE_BYINPUT :   bitfield |= 0x30 ; break ;
+                       case MF624_ENC_RESET_ONRAISING_INPUT :  bitfield |= 0x40 ; break ;
+                       case MF624_ENC_RESET_ONFALLING_INPUT :  bitfield |= 0x50 ; break ;
+                       case MF624_ENC_RESET_ONEITHER_INPUT :   bitfield |= 0x60 ; break ;
+
+                       /* Configure encoder reset      */
+                       case MF624_ENC_FILTER_OFF :             bitfield |= 0x00 ; break ;
+                       case MF624_ENC_FILTER_ON :              bitfield |= 0x80 ; break ;
+
+                       /* Error handling               */
+                       default : return MF624_EXIT_FAILURE;
+               }
+       }
+
+       mask = ~(0xFF << (8*channel));
+       status = thisboard->enc_config;
+
+#ifdef EXTDEBUG
+       rt_printk("comedi%d: mf624: _enc_cfg: bitfield = %8x, mask = %8x, status = %8x\n", dev->minor, bitfield, mask, status);
+       rt_printk("comedi%d: mf624: _enc_cfg: writing %8x in %8x \n", dev->minor, (status & mask) + (bitfield << (8*channel)), devpriv->IO_BADR2 + MF624_IRCCTRL);
+#endif
+
+       thisboard->enc_config = (status & mask) + (bitfield << (8*channel));
+       writel(thisboard->enc_config, devpriv->IO_BADR2 + MF624_IRCCTRL);
+
+       return insn->n;
+}
+
+/*
+ * A convenient macro that defines init_module() and cleanup_module(),
+ * as necessary.
+ */
+COMEDI_INITCLEANUP(driver_mf624);
diff --git a/src/comedi/fpoulain/mf624.h b/src/comedi/fpoulain/mf624.h
new file mode 100755 (executable)
index 0000000..8ffab9f
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * comedi/drivers/mf624.c
+ * Code for a Humusoft MF624 driver
+ *
+ * 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: mf624.o
+Description: A comedi driver for Humusoft MF624 PCI Card
+Devices: Humusoft MF624 Multifunction I/O Card
+Author: Jean-Matthieu Bourgeot - François Poulain
+Updated: Wed, 19 Sep 2007 14:29:43 +0200
+Status: experimental
+
+This driver is a driver for the Humusoft MF624 PCI Card.
+
+It has :
+ * 8 channel 14 bits ADC,
+ * 8 channel 14 bits DAC,
+ * 8 digital inputs,
+ * 8 digital outputs,
+ * 4 quadrature encoder input,
+ * 5 timers/counter.
+
+Status:
+A/D Converter                  : Supported on subdevice 0,
+D/A Converter                  : Supported on subdevice 1,
+Digital Inputs                 : Supported on subdevice 2,
+Digital Outputs                        : Supported on subdevice 3,
+Quadrature Encoder Inputs      : Not yet supported,
+Counters/Timers                        : Not yet supported,
+IRQ                            : Not yet supported.
+
+Remarks:
+Simultaneous D/A update not yet supported.
+
+Configuration Options:
+none
+ */
+
+/*
+ * This file provide useful declaration like some constants
+ * for configuration needed both in user's space and in kernel space
+ */
+
+/* Subdevice numbers */
+#define MF624_ENC_SUBDEV       5
+#define MF624_CTR_SUBDEV       4
+#define MF624_DO_SUBDEV                3
+#define MF624_DI_SUBDEV                2
+#define MF624_AO_SUBDEV                1
+#define MF624_AI_SUBDEV                0
+
+/* Encoders Configuration */
+#define MF624_ENC                      0x0001
+
+/* some unnecessary masks */
+#define MF624_ENC_MODE                 0x000100
+#define MF624_ENC_COUNT                        0x000101
+#define MF624_ENC_RESET                        0x000102
+#define MF624_ENC_FILTER               0x000103
+
+/* some opcodes */
+#define MF624_ENC_MODE_4EDGE           0x00010000
+#define MF624_ENC_MODE_RISING          0x00010001
+#define MF624_ENC_MODE_FALLING         0x00010002
+#define MF624_ENC_MODE_EITHER          0x00010003
+#define MF624_ENC_COUNT_ENABLE         0x00010100
+#define MF624_ENC_COUNT_DISABLE                0x00010101
+#define MF624_ENC_COUNT_ENABLE_BYINPUT 0x00010102
+#define MF624_ENC_COUNT_DISABLE_BYINPUT        0x00010103
+#define MF624_ENC_RESET_DISABLE                0x00010200
+#define MF624_ENC_RESET_ENABLE         0x00010201
+#define MF624_ENC_RESET_DISABLE_BYINPUT        0x00010202
+#define MF624_ENC_RESET_ENABLE_BYINPUT 0x00010203
+#define MF624_ENC_RESET_ONRAISING_INPUT        0x00010204
+#define MF624_ENC_RESET_ONFALLING_INPUT        0x00010205
+#define MF624_ENC_RESET_ONEITHER_INPUT 0x00010206
+#define MF624_ENC_FILTER_OFF           0x00010300
+#define MF624_ENC_FILTER_ON            0x00010301
+
+/* Counters Configration */
+#define MF624_CTR                      0x0002
+
+/* some unnecessary masks */
+#define MF624_CTR_DIRECTION            0x000200
+#define MF624_CTR_REPETITION           0x000201
+#define MF624_CTR_PRELOAD              0x000202
+#define MF624_CTR_OUTPUT               0x000203
+#define MF624_CTR_TRIGGER              0x000204
+#define MF624_CTR_RETRIGGER            0x000205
+#define MF624_CTR_GATE                 0x000206
+#define MF624_CTR_GATEPOLARITY         0x000207
+#define MF624_CTR_CKSOURCE             0x000208
+#define MF624_CTR_ADTRIGSRC            0x000209
+#define MF624_CTR_CTR4INTSRC           0x00020A
+#define MF624_CTR_CTRL                 0x00020B
+
+/* some opcodes */
+#define MF624_CTR_DIRECTION_UP         0x00020000
+#define MF624_CTR_DIRECTION_DOWN       0x00020001
+#define MF624_CTR_REPETITION_ON                0x00020100
+#define MF624_CTR_REPETITION_OFF       0x00020101
+#define MF624_CTR_PRELOAD_A            0x00020200
+#define MF624_CTR_PRELOAD_AB           0x00020201
+#define MF624_CTR_OUTPUT_MONOSTABLE    0x00020300
+#define MF624_CTR_OUTPUT_BISTABLE      0x00020301
+#define MF624_CTR_OUTPUT_DIRECT                0x00020302
+#define MF624_CTR_OUTPUT_INVERSE       0x00020304
+#define MF624_CTR_OUTPUT_OFF           0x00020305
+#define MF624_CTR_OUTPUT_ON            0x00020306
+#define MF624_CTR_TRIGGER_DISABLED     0x00020400
+#define MF624_CTR_TRIGGER_BYINPUT      0x00020401
+#define MF624_CTR_TRIGGER_BYLOWER      0x00020402
+#define MF624_CTR_TRIGGER_BYUPPER      0x00020403
+#define MF624_CTR_TRIGGER_RAISING      0x00020404
+#define MF624_CTR_TRIGGER_FALLING      0x00020405
+#define MF624_CTR_TRIGGER_EITHER       0x00020406
+#define MF624_CTR_TRIGGER_DISABLE      0x00020407
+#define MF624_CTR_RETRIGGER_ON         0x00020500
+#define MF624_CTR_RETRIGGER_OFF                0x00020501
+#define MF624_CTR_GATE_HIGHT           0x00020600
+#define MF624_CTR_GATE_BYINPUT         0x00020601
+#define MF624_CTR_GATE_BYLOWER         0x00020602
+#define MF624_CTR_GATE_BYUPPER         0x00020603
+#define MF624_CTR_GATEPOLARITY_LOW     0x00020700
+#define MF624_CTR_GATEPOLARITY_HIGH    0x00020701
+#define MF624_CTR_CKSOURCE_50M         0x00020800
+#define MF624_CTR_CKSOURCE_10M         0x00020801
+#define MF624_CTR_CKSOURCE_1M          0x00020802
+#define MF624_CTR_CKSOURCE_100K                0x00020803
+#define MF624_CTR_CKSOURCE_RAISINGINPUT        0x00020804
+#define MF624_CTR_CKSOURCE_FALLINGINPUT        0x00020805
+#define MF624_CTR_CKSOURCE_EITHERINPUT 0x00020806
+#define MF624_CTR_CKSOURCE_RAISINGLOWER        0x00020807
+#define MF624_CTR_CKSOURCE_FALLINGLOWER        0x00020808
+#define MF624_CTR_CKSOURCE_EITHERLOWER 0x00020809
+#define MF624_CTR_CKSOURCE_RAISINGUPPER        0x0002080A
+#define MF624_CTR_CKSOURCE_FALLINGUPPER        0x0002080B
+#define MF624_CTR_CKSOURCE_EITHERUPPER 0x0002080C
+#define MF624_CTR_ADTRIGSRC_EXT                0x00020900
+#define MF624_CTR_ADTRIGSRC_INT                0x00020901
+#define MF624_CTR_CTR4INTSRC_EXT       0x00020A00
+#define MF624_CTR_CTR4INTSRC_INT       0x00020A01
+#define MF624_CTR_CTRL_START           0x00020B00
+#define MF624_CTR_CTRL_STOP            0x00020B01
+#define MF624_CTR_CTRL_LOAD            0x00020B02
+#define MF624_CTR_CTRL_RESET           0x00020B03
+#define MF624_CTR_CTRL_TSET            0x00020B04
+#define MF624_CTR_CTRL_TRESET          0x00020B05