O_NONBLOCK is implemented in the read method.
Some Perl-Scripts added for testing.
Documented the read method ...
--- a/me8100.c Mon Jan 21 21:52:00 2002 +0100
+++ b/me8100.c Tue Jan 22 20:30:44 2002 +0100
@@ -474,12 +474,13 @@
info->file_ptr = NULL;
info->board_in_use = 0;
+
+ /* Set default values for the subinfo. */
info->subinfo[0].regbase = info->me8100_regbase;
info->subinfo[1].regbase = info->me8100_regbase + 0x0C;
for (i = 0; i <= 1; ++i) {
info->subinfo[i].int_seen = jiffies; /* suppose we got just the first irq */
- info->subinfo[i].int_count = 0;
info->subinfo[i].fasync_ptr = NULL;
init_waitqueue_head(&info->subinfo[i].readq);
}
@@ -528,6 +529,7 @@
* Modification:
*/
static int me8100_reset_board(me8100_info_type *info){
+ int i;
unsigned char icsr = 0x12;
PDEBUG("me8100_reset_board() is executed\n");
@@ -536,19 +538,16 @@
PDEBUG("me8100_reset_board(): plx_mode = 0x%X\n", icsr);
outb(icsr, info->plx_regbase + PLX_ICSR);
- /* Ports to high impedance, interrupts deactivated */
- outw(0x00, info->me8100_regbase + ME8100_CTRL_REG_A);
- outw(0x0000, info->me8100_regbase + ME8100_CTRL_REG_B);
-
- /* Reset any pending interrupt */
- inw(info->me8100_regbase + ME8100_RES_INT_REG_A);
- inw(info->me8100_regbase + ME8100_RES_INT_REG_B);
+ for (i = 0; i <= 1; ++i) {
+ /* Ports to high impedance, interrupts deactivated */
+ outw(0x00, info->subinfo[i].regbase + ME8100_CTRL_REG);
+ /* Reset any pending interrupt */
+ inw(info->subinfo[i].regbase + ME8100_RES_INT_REG);
+ }
return 0;
}
-
-
/*
* Routine:
* me8100_open
@@ -581,7 +580,7 @@
device = DEVICE(MINOR(inode_ptr->i_rdev));
subdevice = SUBDEVICE(MINOR(inode_ptr->i_rdev));
- PDEBUG("*** device: %d, subdevice %d\n", device, subdevice);
+ PDEBUG("*** device: %d, subdevice %d with flags %0x\n", device, subdevice, file_ptr->f_flags);
if(device >= me8100_board_count){
printk(KERN_ERR"ME8100:me8100_open():Board %d doesn't exist\n", device);
@@ -2205,40 +2204,55 @@
{
int err;
unsigned short val;
- struct me8100_private_data *priv = file_ptr->private_data;
int minor;
+ struct me8100_private_data *priv;
struct me8100_subinfo *subinfo;
- PDEBUG("me8100_read(%d) called\n", len);
+ PDEBUG("me8100_read() called\n");
minor = MINOR(file_ptr->f_dentry->d_inode->i_rdev);
subinfo = &info_vec[DEVICE(minor)].subinfo[SUBDEVICE(minor)];
+ priv = file_ptr->private_data;
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->int_seen) { /* alread seen, sleep */
- PDEBUG("*** going to sleep\n");
- if (wait_event_interruptible(subinfo->readq, (priv->last_read != subinfo->int_seen))) {
- PDEBUG("** awoken on signal?\n");
+ /* If the time we did the last read operation is more recent than the
+ * last interupt, we have to go to sleep (or return -EAGAIN in non blockin
+ * mode). Then, if we're awoken, we return the value that triggered the
+ * interrupt. (If awoken from a signal, we return -ERESTARTSYS.)
+ *
+ * If we haven't read any data since the last irq occured, we return
+ * the current(!) value visible on the port!
+ *
+ * Is this some inconsistency? What does happen if somebody changes the
+ * interrupt behaviour. What, if intterupts are disabled at all? Should
+ * we check this an return the current value visible at the port then?
+ */
+ if (priv->last_read >= subinfo->int_seen) {
+ if (file_ptr->f_flags & O_NONBLOCK) return -EAGAIN;
+
+ if (wait_event_interruptible(subinfo->readq, (priv->last_read != subinfo->int_seen)))
return -ERESTARTSYS;
- }
- val = subinfo->int_di; /* the value that caused the interrupt */
- } else { /* not yet seen ... */
- val = inw(subinfo->regbase + ME8100_DI_REG);
- PDEBUG("me8100_read: val=0x%04x\n", val);
- }
+ val = subinfo->int_di;
+
+ } else val = inw(subinfo->regbase + ME8100_DI_REG);
+ PDEBUG("me8100_read: val=0x%04x\n", val);
+
+ /* Remember the time of the last interrupt we've seen. (It might be
+ * 0 if there was no interrupt yet. This doesn't hurt, since the next
+ * read will see this (see above) and will wait until an irq raises.)
+ */
priv->last_read = subinfo->int_seen;
+ /* Return at most 2 byte, but check if the read want's them both! */
if (len >= sizeof(val)) {
err = put_user(val, (unsigned short*) buffer);
len = sizeof(val);
- } else {
- err = put_user(val, (char*) buffer);
- }
+ } else err = put_user(val, (char*) buffer);
- return len;
+ return err ? err : len;
}
/* Writing: we do only write one word (an unsigned short) and return immediatly. Yes,
--- a/me8100_test_dio/rtest.c Mon Jan 21 21:52:00 2002 +0100
+++ b/me8100_test_dio/rtest.c Tue Jan 22 20:30:44 2002 +0100
@@ -35,6 +35,7 @@
#ifdef USE_READ
printf("Using read()\n");
#endif
+ /* file_handle = open("/dev/me8100_0a", O_RDONLY | O_NDELAY, 0); */
file_handle = open("/dev/me8100_0a", O_RDONLY, 0);
if(file_handle < 0){
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/me8100_test_dio/rtest.pl Tue Jan 22 20:30:44 2002 +0100
@@ -0,0 +1,11 @@
+#! /usr/bin/perl -w
+use strict;
+
+my $DEV = "/dev/me8100_0a";
+open(DEV, $DEV) or die "Can't open $DEV: $!\n";
+
+my $data;
+while (sysread(DEV, $data, 2)) {
+ my $value = unpack "S", $data;
+ print "* read $value\n";
+}
--- a/me8100_test_int/test.c Mon Jan 21 21:52:00 2002 +0100
+++ b/me8100_test_int/test.c Tue Jan 22 20:30:44 2002 +0100
@@ -45,11 +45,6 @@
int err = 0;
int oflags = 0;
- unsigned short mask_a;
- unsigned short ctrl_a;
-
- unsigned char icsr;
-
printf("IRQ Test %d\n", getpid());
file_handle = open("/dev/me8100_0a", O_RDONLY, 0);
@@ -73,41 +68,10 @@
/* Inform the driver to put the current process on the fasync queue */
fcntl(file_handle, F_SETFL, oflags | FASYNC);
- /* enable both interrupts on the plx, set interrupts to high active */
- icsr =
- LOCAL_INT1_EN |
- LOCAL_INT1_POL |
- PCI_INT_EN;
-
- err = ioctl(file_handle, ME8100_SETUP_ICSR, &icsr);
- if(err){
- printf("Cannot setup PLX\n");
- return 1;
- }
-
- /*-------------------- Interrupt caused by bit mask -----------------*/
+ printf("Waiting for Interrupts\n\n");
- /* Set the proper bit mask for port a */
- mask_a = 0xffff;
- err = ioctl(file_handle, ME8100_WRITE_MASK_A, &mask_a);
- if(err){
- printf("Cannot write mask a\n");
- return 1;
- }
-
- /* Enable interrupt signalling by bit mask for port a */
- ctrl_a = ME8100_CTRL_ENIO | ME8100_CTRL_SOURCE | ME8100_CTRL_IRQ_MASK;
- err = ioctl(file_handle, ME8100_WRITE_CTRL_A, &ctrl_a);
- if(err){
- printf("Cannot write ctrl a\n");
- return 1;
- }
-
- printf("<<<--- WAITING FOR INTERRUPTS BY BIT MASK --->>>\n\n");
-
- i = 0;
- while(i < 1000) {
- select(0, NULL, NULL, NULL, NULL);
+ for(;;) {
+ sleep(10);
}
printf("Close path to me8100_0\n");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/me8100_test_int/test.pl Tue Jan 22 20:30:44 2002 +0100
@@ -0,0 +1,25 @@
+#! /usr/bin/perl -w
+
+use strict;
+use Fcntl;
+
+my $DEV = "/dev/me8100_0a";
+my $flags;
+
+$SIG{IO} = sub {
+ my $val;
+ sysread(DEV, $val, 2);
+ $val = unpack("s", $val);
+ print "Hi, got a signal, read $val\n";
+ print "<", unpack("b16", pack("N", $val)), ">\n";
+};
+
+open(DEV, $DEV)
+ or die "Can't open $DEV: $!\n";
+
+fcntl(DEV, F_SETOWN, $$) or die "Can't set owner: $!\n";
+$flags = fcntl(DEV, F_GETFL, 0) or die "Can't get flags: $!\n";
+fcntl(DEV, F_SETFL, $flags | O_ASYNC) or die "Can't set flags: $!\n";
+
+while(sleep(1000)) {
+}