--- a/me8100.c Thu Jan 17 20:25:34 2002 +0100
+++ b/me8100.c Fri Jan 18 20:22:52 2002 +0100
@@ -482,8 +482,6 @@
info->file_ptr = NULL;
info->fasync_ptr = NULL;
info->board_in_use = 0;
- spin_lock_init(&info->use_lock);
-
/*--------------------------- Reset the board -----------------------------*/
@@ -571,9 +569,6 @@
* On success the return value is 0 else failure.
*--------------------------------------------------------------------------
* Author: GG
- * Modification:
- * 01.10.04 Guard board_in_use with spin_lock, cause of race condition
- * when compiling for SMP.
*/
static int me8100_open(struct inode *inode_ptr, struct file *file_ptr){
int device,subdevice;
@@ -584,38 +579,76 @@
device = DEVICE(MINOR(inode_ptr->i_rdev));
subdevice = SUBDEVICE(MINOR(inode_ptr->i_rdev));
+ PDEBUG("*** device: %d, subdevice %d\n", device, subdevice);
+
info = &info_vec[device];
subinfo = &info->subinfo[subdevice];
- PDEBUG("*** device: %d, subdevice %d\n", device, subdevice);
if(device >= me8100_board_count){
printk(KERN_ERR"ME8100:me8100_open():Board %d doesn't exist\n", device);
return -ENODEV;
}
- spin_lock(&info->use_lock);
- if(info->board_in_use){
- printk(KERN_ERR "WARNING: ME8100:me8100_open():Board %d already in use\n", device);
- }
- ++info->board_in_use;
- spin_unlock(&info->use_lock);
+ MOD_INC_USE_COUNT;
if (file_ptr->f_mode & FMODE_WRITE) {
+ /* If we're the first write, the control register has to be
+ * setup properly. */
+
PDEBUG("*** open for writing\n");
if (0 == subinfo->num_writer++) {
subinfo->ctrl_reg |= ME8100_CTRL_ENIO | ME8100_CTRL_SOURCE;
- PDEBUG("*** setting ENIO+SOURCE mode: ctrl: 0x%x\n", subinfo->ctrl_reg);
+ PDEBUG("*** adding ENIO+SOURCE mode: ctrl: 0x%x\n", subinfo->ctrl_reg);
outw(subinfo->ctrl_reg, subinfo->regbase + ME8100_CTRL_REG);
}
}
+
if (file_ptr->f_mode & FMODE_READ) {
+ /* If we're a reader, the IRQ should be setup -- if not done already
+ * (might be done by some ioctl()s. The file_ptr get a private data
+ * pointer passed. We need this to keep track of timestamps for
+ * read data. */
+
+ struct me8100_private_data *priv;
+
PDEBUG("*** open for reading\n");
+
+ if (!(priv = kmalloc(sizeof(*priv), GFP_KERNEL))) {
+ printk(KERN_ERR"ME8100:me8100_open: kmalloc() failed.\n");
+ me8100_release(inode_ptr, file_ptr);
+ return -EIO;
+ }
+
+ priv->last_read = 0;
+ file_ptr->private_data = priv;
+
+ /* Now we've to setup the IRQ line. We suppose that it should be done
+ * in "mask" manner. If somebody wishes to do it the otherway or if somebody
+ * wants to change the mask, ioctl() should be used. */
+ {
+ int mask = 0xffff;
+ unsigned short icsr = PCI_INT_EN | ((LOCAL_INT_EN | LOCAL_INT_POL) << (3 * subdevice));
+
+
+ /* 1) setup the PLX register */
+ PDEBUG("*** plx = 0x%0x\n", icsr);
+ outb(icsr, info->plx_regbase + PLX_ICSR);
+
+ /* 2) setup the irq flags in regbase */
+ subinfo->ctrl_reg |= ME8100_CTRL_IRQ_MASK;
+ PDEBUG("*** ctrl = 0x%04x\n", subinfo->ctrl_reg);
+ outw(subinfo->ctrl_reg, subinfo->regbase + ME8100_CTRL_REG);
+
+ /* 3) setup the interrupt mask */
+ PDEBUG("*** irqmask = 0x%04x\n", mask);
+ outw(mask, subinfo->regbase + ME8100_MASK_REG);
+
+ }
}
- MOD_INC_USE_COUNT;
return 0;
}
@@ -681,6 +714,8 @@
}
}
me8100_fasync(-1, file_ptr, 0);
+ if (file_ptr->private_data) kfree(file_ptr->private_data);
+ file_ptr->private_data = NULL;
MOD_DEC_USE_COUNT;
PDEBUG("me8100_release() is leaved\n");
@@ -2034,7 +2069,7 @@
unsigned short dummy;
me8100_info_type *info;
- PDEBUG("me8100_isr() is executed\n");
+ PDEBUG("*** => me8100_isr() is executed\n");
info = (me8100_info_type *) dev_id;
@@ -2113,21 +2148,35 @@
}
}
+/* Reading: we do only read (at most) one word (an usigned short) and return immediatly,
+ * thus passing the responsibility for reading a bunch of words back to the caller.
+ * This seems to be ok, since it's not expected to read more than one word at once
+ * (the port is one word wide only!)
+ *
+ * If a a reader (identified by the file_ptr) calls this functions the first time,
+ * the current input is returned. Every succsessive read is then put to sleep until
+ * the status of the input changed. (Unless the IRQ behaviour if the device is changed by
+ * some ioctl()s.)
+ */
ssize_t me8100_read(struct file * file_ptr, char *buffer, size_t len, loff_t *offset)
{
int err;
unsigned short val;
+ struct me8100_private_data *priv = file_ptr->private_data;
int minor;
+ struct me8100_subinfo *subinfo;
PDEBUG("me8100_read(%d) called\n", len);
minor = MINOR(file_ptr->f_dentry->d_inode->i_rdev);
+ subinfo = &info_vec[DEVICE(minor)].subinfo[SUBDEVICE(minor)];
if (len == 0) return 0; /* do we really need this check? */
if (len < 0) return -EINVAL; /* do we really need this check? */
+ if (priv->last_read >= subinfo->last_change) return 0; /* nothing has changed */
- val = inw(info_vec[DEVICE(minor)].subinfo[SUBDEVICE(minor)].regbase + ME8100_DI_REG);
- PDEBUG("me8100_read: val=0x%0x\n", val);
+ val = inw(subinfo->regbase + ME8100_DI_REG);
+ PDEBUG("me8100_read: val=0x%04x\n", val);
if (len >= sizeof(val)) {
err = put_user(val, (unsigned short*) buffer);
@@ -2137,7 +2186,6 @@
}
return len;
-
}
/* Writing: we do only write one word (an unsigned short) and return immediatly. Yes,
--- a/me8100.h Thu Jan 17 20:25:34 2002 +0100
+++ b/me8100.h Fri Jan 18 20:22:52 2002 +0100
@@ -135,6 +135,7 @@
#define ME8100_CTRL_IRQ_MASK 0x60
#define ME8100_CTRL_REG 0x00 //( ,w)
+#define ME8100_MASK_REG 0x02 //(r, )
#define ME8100_DI_REG 0x04 //(r, )
#define ME8100_DO_REG 0x06 //( ,w)
@@ -171,19 +172,19 @@
#define LOCAL_INT1_EN 0x01 // local interrupt 1 enabled (r,w)
#define LOCAL_INT1_POL 0x02 // local interrupt 1 polarity (r,w)
#define LOCAL_INT1_STATE 0x04 // local interrupt 1 state (r, )
+
#define LOCAL_INT2_EN 0x08 // local interrupt 2 enabled (r,w)
#define LOCAL_INT2_POL 0x10 // local interrupt 2 polarity (r,w)
#define LOCAL_INT2_STATE 0x20 // local interrupt 2 state (r, )
+
+/* Sould be shifted left by 3 if it's used for device B */
+#define LOCAL_INT_EN 0x01
+#define LOCAL_INT_POL 0x02
+#define LOCAL_INT_STATE 0x04
+
#define PCI_INT_EN 0x40 // PCI interrupt enable (r,w)
#define SOFT_INT 0x80 // Software interrupt (r,w)
-
-typedef enum {
- ME8100_A,
- ME8100_B
-} me8100_version_enum_type;
-
-
typedef struct{
int int1;
int int2;
@@ -191,9 +192,19 @@
#ifdef __KERNEL__
+typedef enum {
+ ME8100_A,
+ ME8100_B
+} me8100_version_enum_type;
+
+struct me8100_private_data {
+ unsigned long last_read; /* jiffies */
+};
+
struct me8100_subinfo {
unsigned int regbase;
unsigned short ctrl_reg;
+ unsigned long last_change;
int num_writer;
};
@@ -217,7 +228,6 @@
int int_count_1; /* Count of interrupt 1 */
int int_count_2; /* Count of interrupt 2 */
int board_in_use; /* Indicates if board is already in use */
- spinlock_t use_lock; /* Guards board in use */
struct file *file_ptr; /* Pointer to file structure of path */
struct fasync_struct *fasync_ptr; /* .hs */
struct me8100_subinfo subinfo[2]; /* .hs */
--- a/me8100_test_dio/rtest.c Thu Jan 17 20:25:34 2002 +0100
+++ b/me8100_test_dio/rtest.c Fri Jan 18 20:22:52 2002 +0100
@@ -23,6 +23,8 @@
#include "me8100.h"
+#define USE_READ
+
int main(void){
int err = 0;
static int file_handle = -1;
@@ -30,6 +32,9 @@
unsigned short value_a;
printf("Read test, PID: %d\n", getpid());
+#ifdef USE_READ
+ printf("Using read()\n");
+#endif
file_handle = open("/dev/me8100_0a", O_RDONLY, 0);
if(file_handle < 0){
@@ -38,7 +43,11 @@
}
for (;;) {
+#ifdef USE_READ
+ read(file_handle, &value_a, sizeof(value_a));
+#else
ioctl(file_handle, ME8100_READ_DI_A, &value_a);
+#endif
printf("Read %04x\n", value_a);
sleep(1);
}
--- a/me8100_test_dio/wtest.c Thu Jan 17 20:25:34 2002 +0100
+++ b/me8100_test_dio/wtest.c Fri Jan 18 20:22:52 2002 +0100
@@ -55,10 +55,10 @@
#endif
for (value_a = 0x01; value_a < 0xffff; ++value_a) {
-#ifndef USE_WRITE
+#ifdef USE_WRITE
+ write(file_handle, &value_a, sizeof(value_a));
+#else
ioctl(file_handle, ME8100_WRITE_DO_A, &value_a);
-#else
- write(file_handle, &value_a, sizeof(value_a));
#endif
printf("Wrote %04x\n", value_a);
sleep(3);