Re: Help implementing counter

Hi!

Here is a second version of the 8254 counter stuff.
I commented the useless spin locks and added some 8254 specific 
configuration.
The configuration command is INSN_CONFIG_8254 and it have 2 subcommands.
One is used to select the mode (0 to 5 and BCD/BINARY) and the other to 
read the status (Output and NULL Count).
They seem to be working. I wrote a small program that sets a counter 
into mode 0 (interrupt on terminal count), loads 6 and then sends 6 
pulses (using a DO of the same board) and reads the status after each 
pulse. I got the "Output" bit when the count reached 0.

SET

-- 
Salvador Eduardo Tropea (SET). (Electronics Engineer)
Visit my home page: http://welcome.to/SetSoft or
http://www.geocities.com/SiliconValley/Vista/6552/
Alternative e-mail: set_at_computer.org set_at_ieee.org 
Address: Curapaligue 2124, Caseros, 3 de Febrero
Buenos Aires, (1678), ARGENTINA Phone: +(5411) 4759 0013
Index: comedi/drivers/comedi_counter_unstable.h
===================================================================
RCS file: /var/cvs/comedi/comedi/drivers/comedi_counter_unstable.h,v
retrieving revision 1.2
diff -u -r1.2 comedi_counter_unstable.h
--- comedi/drivers/comedi_counter_unstable.h	23 Nov 2003 19:12:26 -0000	1.2
+++ comedi/drivers/comedi_counter_unstable.h	12 Jan 2004 16:40:08 -0000
_at__at_ -103,3 +103,36 _at__at_
 	COMEDI_DEC_ACCUMULATOR,
 	COMEDI_RESET_ACCUMULATOR,
 };
+
+/*
+  8254 specific configuration.
+
+  It supports one config command with two subcommands:
+  
+  0 ID: INSN_CONFIG_8254
+  1 SUB-ID: INSN_CONFIG_8254_SET_MODE
+  2 8254 Mode
+    I8254_MODE0, I8254_MODE1, ..., I8254_MODE5
+    I8254_BCD, I8254_BINARY
+    
+  0 ID: INSN_CONFIG_8254
+  1 SUB-ID: INSN_CONFIG_8254_READ_STATUS
+  2 <-- Status byte returned here.
+    B7=Output
+    B6=NULL Count
+    B5-B0 Current mode.
+  
+*/
+#define INSN_CONFIG_8254 0x1001
+#define INSN_CONFIG_8254_SET_MODE    0x00
+#define INSN_CONFIG_8254_READ_STATUS 0x01
+
+#define I8254_MODE0 (0<<1)
+#define I8254_MODE1 (1<<1)
+#define I8254_MODE2 (2<<1)
+#define I8254_MODE3 (3<<1)
+#define I8254_MODE4 (4<<1)
+#define I8254_MODE5 (5<<1)
+#define I8254_BCD    1
+#define I8254_BINARY 0
+
Index: comedi/drivers/das08.c
===================================================================
RCS file: /var/cvs/comedi/comedi/drivers/das08.c,v
retrieving revision 1.47
diff -u -r1.47 das08.c
--- comedi/drivers/das08.c	4 Jul 2003 20:00:26 -0000	1.47
+++ comedi/drivers/das08.c	12 Jan 2004 16:39:32 -0000
_at__at_ -5,6 +5,7 _at__at_
     COMEDI - Linux Control and Measurement Device Interface
     Copyright (C) 2000 David A. Schleef <ds_at_schleef.org>
     Copyright (C) 2001,2002,2003 Frank Mori Hess <fmhess_at_users.sourceforge.net>
+    Copyright (C) 2004 Salvador E. Tropea <set_at_users.sf.net> <set_at_ieee.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
_at__at_ -57,6 +58,7 _at__at_
 #include <linux/delay.h>
 #include <linux/pci.h>
 
+#include "comedi_counter_unstable.h"
 #include "8255.h"
 #include "das08.h"
 
_at__at_ -667,6 +669,136 _at__at_
 	return n;
 }
 
+
+static unsigned int i8254_read_channel_low(unsigned int base, int chan)
+{
+	unsigned int msb, lsb;
+
+        /* The following instructions must be in order.
+           We must avoid other process reading the counter's value in the
+           middle.
+           The spin_lock isn't needed since ioctl calls grab the big kernel 
+           lock automatically */
+        /*spin_lock(sp);*/
+        outb(chan<<6,base+I8254_CTRL);
+        base+=chan;
+	lsb=inb(base);
+	msb=inb(base);
+        /*spin_unlock(sp);*/
+
+	return lsb | (msb<<8);
+}
+
+static void i8254_write_channel_low(unsigned int base, int chan, unsigned int value)
+{
+	unsigned int msb, lsb;
+
+	lsb=value & 0xFF;
+	msb=value>>8;
+
+	/* write lsb, then msb */
+        base+=chan;
+        /* See comments in i8254_read_channel_low */
+        /*spin_lock(sp);*/
+	outb(lsb,base);
+	outb(msb,base);
+        /*spin_unlock(sp);*/
+}
+
+static unsigned int i8254_read_channel(struct i8254_struct *st, int channel)
+{
+        int chan=st->logic2phys[channel];
+
+        return i8254_read_channel_low(st->iobase,chan);
+}
+
+static void i8254_write_channel(struct i8254_struct *st, int channel, unsigned int value)
+{
+        int chan=st->logic2phys[channel];
+
+        i8254_write_channel_low(st->iobase,chan,value);
+}
+
+#define I8254_CH0_LM  0x30
+#define I8254_CH1_LM  0x60
+#define I8254_CH2_LM  0x80
+
+static void i8254_initialize(struct i8254_struct *st)
+{
+        unsigned int port=st->iobase+I8254_CTRL;
+        outb(I8254_CH0_LM | st->mode[0],port);
+        outb(I8254_CH1_LM | st->mode[1],port);
+        outb(I8254_CH2_LM | st->mode[2],port);
+}
+
+
+static void i8254_set_mode_low(unsigned int base, int channel, unsigned int mode)
+{
+        outb((channel<<6) | 0x30 | (mode & 0x0F),base+I8254_CTRL);
+}
+
+static void i8254_set_mode(struct i8254_struct *st, int channel, unsigned int mode)
+{
+        int chan=st->logic2phys[channel];
+
+        st->mode[chan]=mode;
+        return i8254_set_mode_low(st->iobase,chan,mode);
+}
+
+static unsigned int i8254_read_status_low(unsigned int base, int channel)
+{
+        outb(0xE0 | (2<<channel),base+I8254_CTRL);
+        return inb(base+channel);
+}
+
+static unsigned int i8254_read_status(struct i8254_struct *st, int channel)
+{
+        int chan=st->logic2phys[channel];
+        
+        return i8254_read_status_low(st->iobase,chan);
+}
+
+static int das08_counter_read(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data)
+{
+        int chan=insn->chanspec;
+
+        //printk("Reading counter channel %d ",chan);
+        data[0]=i8254_read_channel(&devpriv->i8254,chan);
+        //printk("=> 0x%08X\n",data[0]);
+           
+	return 1;
+}
+
+static int das08_counter_write(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data)
+{
+        int chan=insn->chanspec;
+
+        //printk("Writing counter channel %d with 0x%04X\n",chan,data[0]);
+        i8254_write_channel(&devpriv->i8254,chan,data[0]);
+           
+	return 1;
+}
+
+static int das08_counter_config(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
+{
+        int chan=insn->chanspec;
+        
+	if(data[0] != INSN_CONFIG_8254)return -EINVAL;
+	if(insn->n != 3)return -EINVAL;
+
+        switch (data[1]){
+                case INSN_CONFIG_8254_SET_MODE:
+                        i8254_set_mode(&devpriv->i8254,chan,data[2]);
+                        break;
+                case INSN_CONFIG_8254_READ_STATUS:
+                        data[2]=i8254_read_status(&devpriv->i8254,chan);
+                        break;
+                default:
+                        return -EINVAL;
+        }
+        return 1;
+}
+
 static int das08_attach(comedi_device *dev,comedi_devconfig *it);
 
 static comedi_driver driver_das08={
_at__at_ -698,7 +830,7 _at__at_
 
 	dev->board_name = thisboard->name;
 
-	if((ret=alloc_subdevices(dev, 5))<0)
+	if((ret=alloc_subdevices(dev, 6))<0)
 		return ret;
 
 	s=dev->subdevices+0;
_at__at_ -762,6 +894,30 _at__at_
 	if(thisboard->i8255_offset!=0){
 		subdev_8255_init(dev,s,NULL,(unsigned long)(dev->iobase+
 			thisboard->i8255_offset));
+	}else{
+		s->type=COMEDI_SUBD_UNUSED;
+	}
+
+	s=dev->subdevices+5;
+	/* 8254 */
+	if(thisboard->i8254_offset!=0){
+		s->type = COMEDI_SUBD_COUNTER;
+		s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+		s->n_chan = 3;
+		s->maxdata = 0xFFFF;
+		s->insn_read = das08_counter_read;
+		s->insn_write = das08_counter_write;
+		s->insn_config = das08_counter_config;
+                /* Set-up the 8254 structure */
+                devpriv->i8254.channels=3;
+                devpriv->i8254.logic2phys[0]=0;
+                devpriv->i8254.logic2phys[1]=1;
+                devpriv->i8254.logic2phys[2]=2;
+                devpriv->i8254.iobase=iobase+thisboard->i8254_offset;
+                devpriv->i8254.mode[0]=
+                devpriv->i8254.mode[1]=
+                devpriv->i8254.mode[2]=I8254_MODE0 | I8254_BINARY;
+                i8254_initialize(&devpriv->i8254);
 	}else{
 		s->type=COMEDI_SUBD_UNUSED;
 	}
Index: comedi/drivers/das08.h
===================================================================
RCS file: /var/cvs/comedi/comedi/drivers/das08.h,v
retrieving revision 1.2
diff -u -r1.2 das08.h
--- comedi/drivers/das08.h	21 Nov 2003 02:27:34 -0000	1.2
+++ comedi/drivers/das08.h	12 Jan 2004 16:39:39 -0000
_at__at_ -47,12 +47,26 _at__at_
 	unsigned int	iosize;	// number of ioports used
 } das08_board;
 
+struct i8254_struct
+{
+ int channels;        // available channels. Some could be used internally.
+ int logic2phys[3];   // to know which physical channel is.
+ int mode[3];         // the index is the real counter.
+ unsigned int iobase;
+};
+
+#define I8254_CNT0 0
+#define I8254_CNT1 1
+#define I8254_CNT2 2
+#define I8254_CTRL 3
+
 struct das08_private_struct{
 	unsigned int	do_mux_bits;	// bits for do/mux register on boards without seperate do register
 	unsigned int	do_bits;	// bits for do register on boards with register dedicated to digital out only
 	unsigned int	*pg_gainlist;
 	struct pci_dev *pdev;	// struct for pci-das08
 	unsigned int	pci_iobase;	// additional base address for pci-das08
+        struct i8254_struct i8254;
 };
 
 #define NUM_DAS08_CS_BOARDS 2

Received on 2004-01-12Z16:45:52