O_NONBLOCK is implemented in the read method.
authorheiko
Tue, 22 Jan 2002 20:30:44 +0100
changeset 15 b9baa645576a
parent 14 c3f6d5e9713a
child 16 6b2a0de29990
O_NONBLOCK is implemented in the read method. Some Perl-Scripts added for testing. Documented the read method ...
me8100.c
me8100_test_dio/rtest.c
me8100_test_dio/rtest.pl
me8100_test_int/test.c
me8100_test_int/test.pl
--- 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)) {
+}