Subject: Linux-Gpib: Patch to support select() and Tcl event handler for SRQ.


Hi all,

Here is a rough and ready patch for Claus' linux-gpib-2.03.

This patch
 - adds support for the select() system call
   (only for NI atgpib cards - other card drivers could be modified to
   support similar functionality)

 - Adds a new gpib Tcl command: "gpib onsrq <dev> <command>"
   This command installs a Tcl event handler which is invoked when an
   SRQ condition is asserted.

   In this way, a Tcl program can wait for an SRQ from a device
   without blocking the standard Tcl event loop.

Select() is the standard method for IO multiplexing in Unix (see "man
2 select"). Select() can wait until any of a number of conditions are
true for any of a number of file descriptors.

This patch causes the following IEEE conditions to generate the
corresponding conditions on the associated file descriptor:

  Card state			condition on the file descriptor
  ------------------------------------------------------------------
  Listener (LACS)		ready for reading
  Talker (TACS)			ready for writing
  Service Request (SRQ)		exception

The select() system call can be used to block until selected
conditions are true for that file descriptor.

This is useful for event loops, where a program can wait for an SRQ,
or for more user input, etc. It is also useful for using one program
to service many devices in a computer (eg. serial port and GPIB).

The patch also adds a new gpib sub-command to the Tcl Gpib loadable
module, which uses the functionality added by the select() driver
patch:

	gpib onsrq <dev> <command>

This command causes an event handler for the GPIB card to be installed
in the Tcl event loop. When an SRQ is asserted by a device on the bus,
the supplied Tcl command will be invoked from the event loop.

eg:
  If a device is programmed to generate an SRQ when it has data
  available for reading, you can use something like:

    proc read_data {} {
        global ddata
	global device

        # Serial poll the device
        set sp [gpib rsp $device]
        if {$sp & 64} {
	    # This device generated the SRQ
	    gpib write $device "r1x";		# ask device to write it's data
	    set ddata [gpib read $device];	# read the data
        } else {
	    puts stderr "Serial Poll Byte = $sp"
        }
    }
    gpib onsrq $device read_data

  to read out the data from the event loop.

Note: Though a device id is supplied to the "gpib onsrq" command, it
is currently unused. The handler will be invoked if any device
generates an SRQ. A future extension will allow the use of the device
id to restrict the call to a particular device.

To apply the patch, just save this email to a file (eg.
~/gpib-select.diff) and:

     cd /usr/local/src		# change to your linux-gpib directory location
     tar -zxvf linux-gpib-2.03.tar.gz
     patch -p0 < ~/gpib-select.diff

The patch may only apply cleanly to a freshly unpacked linux-gpib archive.

					cheers,
					glenn.
-- 
  Glenn Moloney				glenn@physics.unimelb.edu.au
  School of Physics,			http://www.ph.unimelb.edu.au/~glenn
  University of Melbourne,		Phone: +61 3 9344 5455
  Parkville, Australia 3052.		Fax:   +61 3 9347 4783


diff -u --recursive --new-file linux-gpib.orig/driver/atgpib/wait.c linux-gpib/driver/atgpib/wait.c
--- linux-gpib.orig/driver/atgpib/wait.c	Thu Feb  8 09:07:57 1996
+++ linux-gpib/driver/atgpib/wait.c	Fri Mar 20 11:27:34 1998
@@ -14,6 +14,47 @@
  * BDWAIT
  * Wait for a GPIB event to occur ('mask' must be non-zero).
  */
+void bdclearints( void )
+{
+	uint8 s;
+
+	DBGin("bdclearints");
+	s = GPIBin(isr1);           /* clear ISRs by reading */
+	s = GPIBin(isr2);
+	DBGout();
+}
+
+int bdconfigureints( unsigned int mask )
+{
+	uint8 imr2mask;
+
+	DBGin("bdenableints");
+	imr2mask = (((mask & SRQI) ? HR_SRQIE : 0) |
+		    ((mask & (CIC | TACS | LACS)) ? HR_ACIE : 0));
+	DBGprint(DBG_DATA, ("imr2mask=0x%x  ", imr2mask));
+
+	/* Test if condition is already present */
+	if (ibstat() & mask) {
+		DBGout();
+		return 0;
+	}
+	ibsta = CMPL;
+	GPIBout(imr2, imr2mask);
+	DBGout();
+	return 1;
+}
+
+IBLCL bddisableints( void )
+{
+	uint8 s;
+
+	DBGin("bddisableints");
+	GPIBout(imr2, 0);   /* clear imr2 IE bits */
+	s = GPIBin(isr2);   /* clear isr2 status bits */
+	DBGprint(DBG_DATA, ("isr2=0x%x  ", s));
+	DBGout();
+}
+
 IBLCL bdwait(mask)
 unsigned int mask;
 {
@@ -23,22 +64,11 @@
 #if USEINTS
 	if (!(pgmstat & PS_NOINTS)) {
 		DBGprint(DBG_BRANCH, ("use ints  "));
-		s = GPIBin(isr1);           /* clear ISRs by reading */
-		s = GPIBin(isr2);
-		imr2mask = 0;
-		if (mask & SRQI)
-			imr2mask |= HR_SRQIE;
-		if (mask & (CIC | TACS | LACS))
-			imr2mask |= HR_ACIE;
-		DBGprint(DBG_DATA, ("imr2mask=0x%x  ", imr2mask));
-		while (!(ibstat() & mask) && NotTimedOut()) {
-			ibsta = CMPL;
-			GPIBout(imr2, imr2mask);
-			osWaitForInt(HR_TLCI);
-			GPIBout(imr2, 0);   /* clear imr2 IE bits */
-			s = GPIBin(isr2);   /* clear isr2 status bits */
-			DBGprint(DBG_DATA, ("isr2=0x%x  ", s));
-		}
+ 		bdclearints();
+ 		while (bdconfigureints( mask ) && NotTimedOut()) {
+  			osWaitForInt(HR_TLCI);
+ 			bddisableints();
+  		}
 	}
 	else
 #endif
diff -u --recursive --new-file linux-gpib.orig/driver/sys/ibsys.h linux-gpib/driver/sys/ibsys.h
--- linux-gpib.orig/driver/sys/ibsys.h	Wed May 28 17:38:41 1997
+++ linux-gpib/driver/sys/ibsys.h	Fri Mar 20 11:24:22 1998
@@ -69,6 +69,10 @@
 extern int       espsemid;		/* semaphore ID for ESP interrupt support */
 #endif
 
+extern struct wait_queue *selectwait_in;
+extern struct wait_queue *selectwait_out;
+extern struct wait_queue *selectwait_ex;
+
 extern int   gpib_dma_size;
 extern int   gpib_default_dma_size;
 
diff -u --recursive --new-file linux-gpib.orig/driver/sys/osfuncs.c linux-gpib/driver/sys/osfuncs.c
--- linux-gpib.orig/driver/sys/osfuncs.c	Thu Jul 31 16:43:27 1997
+++ linux-gpib/driver/sys/osfuncs.c	Fri Mar 20 11:24:22 1998
@@ -10,6 +10,7 @@
 int ib_opened=0;
 int ib_exclusive=0;
 
+extern char *osGetDMABuffer( int *size );
 
 ibopen(LINUX_STD_PARAM)
 {
@@ -83,7 +84,7 @@
           ib_exclusive = 0;
 
 	DBGout();
-        return (OK);
+        return;
 }
 
 
@@ -405,14 +406,14 @@
 
 /***********************************************************************/
 
-ibVFSwrite( LINUX_STD_PARAM, char *buffer, int count)
+ibVFSwrite( LINUX_STD_PARAM, const char *buffer, int count)
   {
     int minor = MINOR(inode->i_rdev);
     int retval = 0;
 	int	bufsize;
 	int	remain;
 	char 	*buf;
-	char 	*userbuf;
+	const char 	*userbuf;
 	char 	c;
 
 
@@ -500,3 +501,51 @@
     GIVE_UP(ibcnt);
   }
 
+struct wait_queue *selectwait_in = NULL;
+struct wait_queue *selectwait_out = NULL;
+struct wait_queue *selectwait_ex = NULL;
+
+int ibselect( struct inode *inode, struct file *file,
+	      int sel_type, select_table *wait )
+{
+	unsigned int mask;
+	struct wait_queue **waitp;
+
+	DBGin("ibselect");
+	switch (sel_type) {
+	case SEL_IN:
+		mask = LACS;
+		waitp = &selectwait_in;
+		break;
+	case SEL_OUT:
+		mask = TACS;
+		waitp = &selectwait_out;
+		break;
+	case SEL_EX:
+		mask = SRQI;
+		waitp = &selectwait_ex;
+		break;
+	default:
+		printk("GPIB: ibselect(): bad sel_type %d\n", sel_type);
+		DBGout();
+		return -(EINVAL);
+	}
+
+	if (fnInit( (mask & SRQI) ? HR_CIC : 0 ) & ERR) {
+		DBGout();
+		return -(EIO);
+	}
+	cli();
+	bdclearints();
+	if (!bdconfigureints( mask )) {
+		sti();
+		DBGout();
+		return 1;
+	}
+	select_wait( waitp, wait );
+	osEnableIntsOnBoard(HR_TLCI);
+	sti();
+	DBGout();
+
+	return 0;
+}
diff -u --recursive --new-file linux-gpib.orig/driver/sys/osinit.c linux-gpib/driver/sys/osinit.c
--- linux-gpib.orig/driver/sys/osinit.c	Sat Aug 16 19:46:09 1997
+++ linux-gpib/driver/sys/osinit.c	Fri Mar 20 11:24:22 1998
@@ -69,7 +69,11 @@
 IBLCL int       espwdid   = 0;		/* watchdog timer ID for ESP routines */
 #endif
 #if USEINTS
-IBLCL struct semaphore espsemid;		/* semaphore ID for ESP interrupt support */
+#if HAS_SEMAPHORES
+struct semaphore espsemid;		/* semaphore ID for ESP interrupt support */
+#else
+int       espsemid;		/* semaphore ID for ESP interrupt support */
+#endif
 IBLCL int       espintcon = 0;		        /* ESP interrupt routine is "connected" */
 #endif
 
@@ -92,7 +96,7 @@
 {
 	int	s;
 extern  struct timer_list ibtimer_list;
-extern  void ibintr(int);
+extern  void ibintr(int irq, void *dev_id, struct pt_regs *resgisterp);
 
 
 	DBGin("osInit");
@@ -200,14 +204,17 @@
 extern int  ibopen     (struct inode *, struct file *);
 extern void ibclose    (struct inode *, struct file *);
 extern int  ibVFSread  (struct inode *, struct file *, char *, int );
-extern int  ibVFSwrite (struct inode *, struct file *, char *, int );
+extern int  ibVFSwrite (struct inode *, struct file *, const char *, int );
+
+extern int ibselect( struct inode *inode, struct file *file,
+		     int sel_type, select_table *wait );
 
 struct file_operations ib_fops = {
   NULL,               /* seek */
   ibVFSread,          /* read */
   ibVFSwrite,         /* write */
   NULL,               /* readdir */
-  NULL,               /* select */
+  ibselect,           /* select */
   ibioctl,            /* ioctl */
   NULL,               /* mmap */
   ibopen,             /* open */
diff -u --recursive --new-file linux-gpib.orig/driver/sys/osinterrupt.c linux-gpib/driver/sys/osinterrupt.c
--- linux-gpib.orig/driver/sys/osinterrupt.c	Sat Aug 16 19:32:12 1997
+++ linux-gpib/driver/sys/osinterrupt.c	Fri Mar 20 11:24:22 1998
@@ -27,19 +27,8 @@
 long serial=0L;
 
 #if USEINTS
-/*
- * GPIB interrupt service routine -- fast and simple
- */
-IBLCL void ibintr(int irq, struct pt_regs *registerp )
+IBLCL osDisableIntsOnBoard(void)
 {
-int s;
-
-/*printk("***IRQ %ld! st=0x%x \n",serial++,GPIBin(hs_status));*/
-#if DEBUG
-	if (dbgMask & DBG_INTR)
-	        printk("GPIB INTERRUPT! semaphore id = %d\n", espsemid.count);
-#endif
-
 #ifdef NIAT
 	GPIBout(imr3, 0);		                /* disable interrupts */
 #endif
@@ -48,9 +37,10 @@
 	GPIBout(imr1, 0);
 #endif
 #ifdef NIPCIIa
-/*
- * Seem to need both imr1 and imr2 to be  reset to avoid getting in loop.
- */
+	/*
+	 * Seem to need both imr1 and imr2 to be  reset to
+	 * avoid getting in loop.
+	 */
 	GPIBout(imr2, 0);
 	GPIBout(imr1, 0);
 #if DEBUG
@@ -74,12 +64,31 @@
 #if defined(CBI_PCI)
         pci_ResetIRQ ();
 #endif
+}
 
-	
+/*
+ * GPIB interrupt service routine -- fast and simple
+ */
 
+IBLCL void ibintr(int irq, struct pt_regs *registerp )
+{
+/*printk("***IRQ %ld! st=0x%x \n",serial++,GPIBin(hs_status));*/
+#if DEBUG
+	if (dbgMask & DBG_INTR)
+	        printk("GPIB INTERRUPT! semaphore id = %d\n", espsemid.count);
+#endif
+	osDisableIntsOnBoard();
 
+	up(&espsemid); /* wake up sleeping process */
 
-	    up(&espsemid); /* wake up sleeping process */
+	ibstat();
+	if (ibsta & LACS)
+		wake_up( &selectwait_in );
+	if (ibsta & TACS)
+		wake_up( &selectwait_out );
+	if (ibsta & SRQI) {
+		wake_up( &selectwait_ex );
+	}
 }
 
 
@@ -88,53 +97,58 @@
  * either ibintr or ibtmintr.
  */
 
-
-
-IBLCL osWaitForInt(imr3mask)
-int imr3mask;
+IBLCL osEnableIntsOnBoard(int imr3mask)
 {
-        struct wait_queue wait = {current, NULL}; /*@*/
-
-	DBGin("osWaitForInt");
-	if (espsemid.count <= 0) {
-	/*
-	 *	If semaphore not already available, enable
-	 *	requested interrupts and wait until it is...
-	 */
-
-	  espsemid.count = 1;
-          down(&espsemid); /* spurious interrups calling up() while irq's are enabled ? */
-
-          /* now it's time to enable board interrupts */
+	DBGin("osEnableIntsOnBoard");
+	/* now it's time to enable board interrupts */
 
 #if defined(CBI_PCI)
-		pci_EnableIRQ();
+	pci_EnableIRQ();
 #endif
 #if defined(CBI_4882)
-		GPIBout( hs_mode, HS_CLR_SRQ_INT );
-		GPIBout( hs_mode, CurHSMode );
-		/*
-		*/
+	GPIBout( hs_mode, HS_CLR_SRQ_INT );
+	GPIBout( hs_mode, CurHSMode );
+	/*
+	 */
 #endif
 
 #ifdef NIAT    
-	        DBGprint(DBG_DATA, ("imr3mask=0x%x  ", imr3mask));		
-                GPIBout(imr3, imr3mask);
+	DBGprint(DBG_DATA, ("imr3mask=0x%x  ", imr3mask));		
+	GPIBout(imr3, imr3mask);
 #endif
 #ifdef NIPCII
-                DBGprint(DBG_DATA, ("imr3mask=0x%x  ", imr3mask));		
-		GPIBout(imr2, imr3mask); /* sorry */	        
+	DBGprint(DBG_DATA, ("imr3mask=0x%x  ", imr3mask));		
+	GPIBout(imr2, imr3mask); /* sorry */	        
 #endif
 #ifdef HP82335
 #if 0
-	        GPIBout(ccr, ccrbits & ~HR_INTEN);/* disable interrupt */
-                GPIBout(ccir[0x37f7], 0x0);      /* clear card interrupt IP bit */
-                GPIBout(ccr, ccrbits);            /* re-enable interrupts if set */
-                DBGprint(DBG_DATA,("===>csr=0x%x",GPIBin(csr)));
+	GPIBout(ccr, ccrbits & ~HR_INTEN);/* disable interrupt */
+	GPIBout(ccir[0x37f7], 0x0);      /* clear card interrupt IP bit */
+	GPIBout(ccr, ccrbits);            /* re-enable interrupts if set */
+	DBGprint(DBG_DATA,("===>csr=0x%x",GPIBin(csr)));
 #endif
-                DBGprint(DBG_DATA, ("imr3mask=0x%x  ", imr3mask));		
-		GPIBout(imr1, imr3mask); /* sorry */	        
+	DBGprint(DBG_DATA, ("imr3mask=0x%x  ", imr3mask));		
+	GPIBout(imr1, imr3mask); /* sorry */	        
 #endif
+	DBGout();
+}
+
+IBLCL osWaitForInt(imr3mask)
+int imr3mask;
+{
+        struct wait_queue wait = {current, NULL}; /*@*/
+
+	DBGin("osWaitForInt");
+	if (espsemid.count <= 0) {
+		/*
+		 *	If semaphore not already available, enable
+		 *	requested interrupts and wait until it is...
+		 */
+		espsemid.count = 1;
+		down(&espsemid); /* spurious interrups calling up() while irq's are enabled ? */
+
+		osEnableIntsOnBoard(imr3mask);
+
 		/* now push process to sleep */
 		down(&espsemid);
 		up(&espsemid); /* wakeup process */
@@ -146,8 +160,8 @@
 
 	}
         else	{ 
-	  espsemid.count = 0;
-          espsemid.wait  = NULL;
+		espsemid.count = 0;
+		espsemid.wait  = NULL;
 	}
 	DBGout();
 }
diff -u --recursive --new-file linux-gpib.orig/language/tcl/ibCmds.c linux-gpib/language/tcl/ibCmds.c
--- linux-gpib.orig/language/tcl/ibCmds.c	Wed Nov  6 21:47:28 1996
+++ linux-gpib/language/tcl/ibCmds.c	Fri Mar 20 11:55:03 1998
@@ -345,7 +345,46 @@
   return TCL_OK;
 }
 /**********************************************************************/
+static char	*srqhandler = NULL;
+static Tcl_Interp *srqhandler_interp;
 
+static void ibSrqHandler( ClientData clientdata, int mask )
+{
+	if (srqhandler)
+		Tcl_GlobalEval( srqhandler_interp, srqhandler );
+}
+
+int ibOnSrq  _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, int argc,char *argv[])){
+  int fd;
+
+  if (argc == 2) {
+	  if (srqhandler) {
+		  Tcl_SetResult( interp, srqhandler, TCL_VOLATILE );
+	  } else {
+		  Tcl_SetResult( interp, "", TCL_STATIC );
+	  }
+	  return TCL_OK;
+  }
+  if( argc != 3){
+    Tcl_SetResult(interp, "Error: onsrq <dev> <string>", TCL_STATIC);
+    return TCL_ERROR;
+  }
+
+  fd = ibfiledescriptor( atoi(argv[1]) );
+  if (srqhandler) {
+	  Tcl_DeleteFileHandler( fd );
+	  free( srqhandler );
+	  srqhandler = NULL;
+  }
+  if (strlen( argv[2] ) > 0) {
+	  srqhandler = strdup( argv[2] );
+	  srqhandler_interp = interp;
+	  Tcl_CreateFileHandler( fd, TCL_EXCEPTION, ibSrqHandler,
+				 (ClientData) NULL );
+  }
+  return TCL_OK;
+}
+/**********************************************************************/
 
 
 
@@ -464,34 +503,11 @@
 if( *argv[1]=='d' && !strcmp(argv[1],"debug")){
   return ibSetDBG( clientData, interp, argc-1,argv+1 );
 }
+if( *argv[1]=='o' && !strcmp(argv[1],"onsrq")){
+  return ibOnSrq( clientData, interp, argc-1,argv+1 );
+}
 
 
 Tcl_SetResult(interp,"Error: gpib without command",TCL_STATIC);
 return TCL_ERROR;
 }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff -u --recursive --new-file linux-gpib.orig/lib/Makefile linux-gpib/lib/Makefile
--- linux-gpib.orig/lib/Makefile	Fri May 30 16:54:05 1997
+++ linux-gpib/lib/Makefile	Fri Mar 20 11:55:03 1998
@@ -42,12 +42,14 @@
 LDFLAGS = -L.
 
 SRCS =  ibCac.c ibClr.c ibCmd.c ibConfYacc.c ibConfLex.c  ibEos.c ibEot.c \
+	ibFd.c \
 	ibFind.c ibLines.c ibOnl.c ibPad.c ibRd.c ibRpp.c ibRsp.c ibRsv.c \
 	ibSad.c ibSic.c ibSre.c ibTmo.c ibTrg.c ibWait.c ibWrt.c ibBdChrConfig.c\
 	ibOpt.c ibGts.c ibBoard.c ibError.c ibutil.c ibAPE.c ibSdbg.c ibFlags.c #ibEvent.c
 
 
 OBJS =  ibCac.o ibClr.o ibCmd.o ibConfYacc.o ibConfLex.o  ibEos.o ibEot.o \
+	ibFd.o \
 	ibFind.o ibLines.o ibOnl.o ibPad.o ibRd.o ibRpp.o ibRsp.o ibRsv.o \
 	ibSad.o ibSic.o ibSre.o ibTmo.o ibTrg.o ibWait.o ibWrt.o ibBdChrConfig.o \
 	ibOpt.o ibGts.o ibBoard.o ibError.o ibutil.o  ibAPE.o ibSdbg.o ibFlags.o \
@@ -141,7 +143,6 @@
 
 rcsget:
 	for i in  ./RCS/*,v ; do co -l `basename $$i ,v` ;done
-	
 
 ######################################################################
 #
@@ -191,8 +192,6 @@
 	/usr/dll/bin/mkstubs -l $(SHD_NAME) -v $(SHD_VER) -a $(SHD_ADR) -j $(SHD_JTS) -g $(SHD_GOT) -- $(SHD_NAME)
 	/usr/dll/bin/verify-shlib -l $(SHD_NAME).so.$(SHD_VER) $(SHD_NAME).sa
 	touch shared_2.done
-	
-	
 
 include .depend
 
diff -u --recursive --new-file linux-gpib.orig/lib/ibFd.c linux-gpib/lib/ibFd.c
--- linux-gpib.orig/lib/ibFd.c	Thu Jan  1 09:00:00 1970
+++ linux-gpib/lib/ibFd.c	Fri Mar 20 11:55:03 1998
@@ -0,0 +1,7 @@
+#include <ib.h>
+#include <ibP.h>
+
+PUBLIC int ibfiledescriptor(int ud)
+{
+	return ibBoard[CONF(ud,board)].fileno;
+}

