# HG changeset patch # User heiko # Date 1011727844 -3600 # Node ID b9baa645576a7937aea0323ccccf510600c27cf3 # Parent c3f6d5e9713a4e812435192ac445bfcf54f8b79f O_NONBLOCK is implemented in the read method. Some Perl-Scripts added for testing. Documented the read method ... diff -r c3f6d5e9713a -r b9baa645576a me8100.c --- 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, diff -r c3f6d5e9713a -r b9baa645576a me8100_test_dio/rtest.c --- 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){ diff -r c3f6d5e9713a -r b9baa645576a me8100_test_dio/rtest.pl --- /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"; +} diff -r c3f6d5e9713a -r b9baa645576a me8100_test_int/test.c --- 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"); diff -r c3f6d5e9713a -r b9baa645576a me8100_test_int/test.pl --- /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)) { +}