A.4. Test Application for the DVN Driver

The test application for the Cogent SST-DVN Driver for SST DeviceNet Cards is a C program which comes with the DVN Driver distribution. It can be run from the command line using the command dvn_test. The command-line options are as follow:

-q          Interactive mode - query between steps
-i iter     Repeat test this many times
-n taskname Connect to this published driver name
-p point    Name of test point
            (must be specified to invoke point and direct interface tests)
-b addr     Base address of test block (dflt: 0)
-l len      Length of test block (bytes)
            (must be specified to perform block tests)
-a addr     Byte offset in block of analog word to write (dflt 0)
-d addr     Byte offset in block of digital word to write (dflt 4)
-s          Block data words are high-low byte format

The code for the test application is shown here.

/* **********************************************************************
 *
 *  Filename:     dvn_test.c
 *  Description:  Sample application for SST-DVN administrator
 *		  - tests functionality of driver and interfaces
 *
 ********************************************************************** */

/* C library headers */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#ifdef HAVE_SYS_NAME_H
#include <sys/name.h>
#endif
#ifdef HAVE_SYS_KERNEL_H
#include <sys/kernel.h>
#endif

/* Cogent library headers */		 
#include <cogent/machdep.h>
#include <cogent/ioport.h>
#include <cogent/util.h>
#include <cogent/dr_api.h>

/* local headers */
#include "dvn_api.h"

#define MAX_DATA_SIZE 	80
#define MAX_MSG_SIZE 	(sizeof(DR_ApBlkHdr_t) -1 + MAX_DATA_SIZE)
#define DATA_MSG_SIZE	(sizeof(DR_ApBlkHdr_t) -1 + sizeof(short))

static IP_Task* AdminTask;


/* ****************************************************************************
 *  ToggleOutputBit
 *		uses direct QNX IPC messages (bypasses API) to read	and then write
 *		a pair of bytes from/to the specified process
 */   
int	ToggleOutputBit(IP_Task* admin_task, int device, int buffer,
			unsigned short ofs, int bit)
{
  int 			result=0;
  unsigned short	*data_word;
  
  char			msg [MAX_MSG_SIZE];
  DR_ApBlkHdr_t	*hdr;
  
  /* setup a message to read the specified 2 bytes from an 
     output buffer */
  hdr = (DR_ApBlkHdr_t *) msg;
  data_word = (unsigned short *)hdr->data;
  hdr->cmd = DR_API_BLK_RD;
  hdr->dev = device;
  hdr->buffer = buffer;
  hdr->buf_size = MAX_MSG_SIZE;
  hdr->offset = ofs;
  hdr->size = sizeof(short);
  
  result = IP_TaskSendRaw (admin_task, msg, DATA_MSG_SIZE,
			   msg, MAX_MSG_SIZE);
  if (result == 0)
    {
      if (!(result = hdr->status))
	{
	  /* toggle the bit */
	  (*data_word) ^= 1<<(bit-1);
	  
	  /* write it back out */
	  hdr->cmd = DR_API_BLK_WR;
	  result = IP_TaskSendRaw (admin_task, msg, DATA_MSG_SIZE, msg,
				   MAX_MSG_SIZE);
	}
      else
	printf ("     Administrator response: %s\n", hdr->data);
    }
  else
    printf ("     Error %d sending to administrator\n",
	    result);
  
  return (result);
}

/* ****************************************************************************
 *  ReadBufferWord
 *		uses direct QNX IPC messages (bypasses API) to read
 *		a pair of bytes from/to the specified process
 */   
int	ReadBufferWord(IP_Task* admin_task, int device,
		       unsigned short buffer, unsigned short ofs)
{
  int 			result=0;
  unsigned short	*data_word;
  
  char			msg [MAX_MSG_SIZE];
  DR_ApBlkHdr_t	*hdr;
  
  /* setup a message to read the specified 2 bytes from the buffer */
  hdr = (DR_ApBlkHdr_t *) msg;
  data_word = (unsigned short *)hdr->data;
  hdr->cmd = DR_API_BLK_RD;
  hdr->dev = device;
  hdr->buffer = buffer;
  hdr->buf_size = MAX_MSG_SIZE;
  hdr->offset = ofs;
  hdr->size = 2;
  
  result = IP_TaskSendRaw (admin_task, msg, DATA_MSG_SIZE,
			   msg, MAX_MSG_SIZE);
  if (result == 0)
    {
      if (!(result = hdr->status))
	{
	  printf ("     Data at %X[%X] = %X\n", buffer, ofs, *data_word);
	}
      else
	printf ("     Administrator error %d %s\n", hdr->status, "FIXME");
    }
  else
    printf ("     Error %d sending to administrator\n",
	    result);
  
  return (result);
}

void dump_data (int indent, char *data, int length, int width, int addr_ofs)
{
  int i,j;
  
  for (i=0; i<length;)
    {
      printf("%*s%04X: ", indent, "|", addr_ofs + i);
      for (j=0; j<width && i<length; j+=2, i+=2)
	printf("%02X%02X ",
	       (unsigned char)data[i], (unsigned char)data[i+1]);
      printf("\n");
    }
}

void wait_for_kybd (int single_step)
{
  if (single_step)
    {
      printf ("Press ENTER to continue");
      fflush (stdout);
      getchar();
      printf ("\n");
    }
}


static const char UT_USAGE[] =
"Usage: %s [-hq] [-n admin_name] options\n"
;

static const char *UT_HELP =
"\nOptions:\n"
"    -q          Interactive mode - query between steps\n"
"    -i iter     Repeat test this many times\n"
"    -n taskname Connect to this published driver name\n"
"    -p point    Name of test point\n"
"                (must be specified to invoke point and direct interface tests)\n"
"    -b addr     Base address of test block (dflt: 0)\n"
"    -l len      Length of test block (bytes)\n"
"                (must be specified to perform block tests)\n"
"    -a addr     Byte offset in block of analog word to write (dflt 0)\n"
"    -d addr     Byte offset in block of digital word to write (dflt 4)\n"
"    -s          Block data words are high-low byte format\n"
"\n"
"Test functionality of the Cogent SST-DVN Driver\n\n"
;

static const char* OPTCMDS = "hqn:p:b:l:a:d:si:";

int main (int argc, char ** argv)
{
  /* test parameters & command line options */
  int			 	opt;
  char			*taskname;
  char			*admin_name = "/dr/dvn";
  char			*test_pnt = NULL;
  unsigned short	block_addr=0, block_length=0;
  unsigned short	analog_word_ofs=0, digital_word_ofs=4;
  int		 		iter = 1, swap=0, single_step = 0;
  
  /* common vars */
  int			 	result;
  char			*error_str;
  char			data[1024];
  int				nargs;
  char			*nargv[128];
  
  DR_ApValue_t	value;
  DR_ApValue_t	ivalue;
  unsigned short	dvalue;
  
  if (argc == 1)
    {
      UT_Help (argv[0], UT_USAGE, UT_HELP);
    }
  
  /*
   * 	Extract command line parameters
   */
  
  /* read command line options/configuration */
  taskname = argv[0];
  while ((opt = getopt(argc, argv, OPTCMDS)) != -1)
    {
      switch (opt)
	{
	case 'h':
	  UT_Help (argv[0], UT_USAGE, UT_HELP);
	  exit(0);
	case 'q':
	  single_step = 1;
	  printf ("Single stepping mode on\n");
	  break;
	case 'n':
	  admin_name = optarg;
	  printf ("Administrator name set to %s\n", admin_name);
	  break;
	case 'p':
	  test_pnt = optarg;
	  printf ("Test point %s will be used\n", test_pnt);
	  break;
	case 'b':
	  block_addr = strtoul(optarg, NULL, 0);
	  printf ("Test block base address set to %0X\n", block_addr);
	  break;
	case 'l':
	  block_length = strtoul(optarg, NULL, 0);
	  printf ("Test block length set to %d bytes\n", block_length);
	  break;
	case 'a':
	  analog_word_ofs = strtoul(optarg, NULL, 0);
	  printf ("Analog Test Word located at byte %0X of test block\n",
		  analog_word_ofs);
	  break;
	case 'd':
	  digital_word_ofs = strtoul(optarg, NULL, 0);
	  printf ("Digital Test Word located at byte %0X of test block\n",
		  digital_word_ofs);
	  break;
	case 's':
	  swap = 1;
	  break;
	case 'i':
	  iter = atoi(optarg);
	  break;
	default:
	  UT_Help (argv[0], UT_USAGE, UT_HELP);
	  exit(1);
	}
    }
  
  /*
   *	Connect to target process
   */
  
  if (!DR_ApInitIPC (taskname, admin_name))
    {
      int 	count;
      
      printf ("Successfully connected %s to %s\n\n", taskname, admin_name);
      for (count=0; count<iter; count++)
	{
	  
	  /* **************************************
	   *   test group A: command interface
	   ************************************** */
	  
	  wait_for_kybd (single_step);
	  
	  printf ("\nA) Testing command interface.\n");
	  
	  {
	    /*** test A.1 send a command to list available commands	*/
	    
	    result = DR_ApCommand ("(apropos *)", data, 1024, &error_str);
	    if (result == ST_OK)
	      printf ("   Administrator commands available:\n%s\n",data);
	    else
	      printf ("     Error@ApCommand (%d, %s)\n",
		      result, error_str);
	  }
	  
	  /* **************************************
	   *   test group B: named point interface
	   ************************************** */
	  
	  wait_for_kybd (single_step);
	  
	  printf ("\nB) Testing Point Interface\n");
	  
	  /*** test B.1: get point names */
	  {
	    int				j, type, enabled, readable, writeable;
	    int				device, buffer;
	    unsigned short 	offset, bit;
	    char			address[16];
	    
	    nargs = 0;
	    result = DR_ApListPoints (NULL, &nargs, nargv, 128,
				      data, 1024, &error_str);
	    printf ("   Listing points available:\n");
	    for (j = 1; j<nargs; j++)
	      {
		/*** test B.2: read specified point values */
		result = DR_ApReadPoint(nargv[j], &type, &value,
					&error_str);
		if (result == ST_OK)
		  {
		    printf ("     Pnt: %s ", nargv[j]);
		    switch(type)
		      {
		      case DR_API_DOUBLE_TYPE:
			printf ("(real) = %f\n", value.d);
			break;
		      case DR_API_INT32_TYPE:
			printf ("(int) = %d\n", value.i);
			break;
		      case DR_API_INT16_TYPE:
			printf ("(short) = %d\n", value.s);
			break;
		      case DR_API_BIT_TYPE:
			printf ("(bit) = %1X\n", value.s);
			break;
		      default:
			printf ("(%d) = %X\n", type, value.i);
		      }
		  }
		else
		  {
		    printf ("     Error: %s value not available\n",
			    nargv[j]);
		    //	printf ("     Error@ListPoints:%s (%d, %s): \n",
		    //	nargv[j], result, error_str);
		  }
	      }
	    
	    if (test_pnt)
	      {
		wait_for_kybd (single_step);
		
		/***	test B.3: describe a specified point */
		
		printf ("   Describing point %s: \n", test_pnt);
		result = DR_ApDescribePoint (test_pnt, &type, &enabled,
					     &readable, &writeable,
					     address, sizeof(address),
					     &error_str);
		if (result == ST_OK)
		  {
		    printf ("     Point %s: type %d, %s%s, addr: %s\n",
			    test_pnt, type, (readable?"R":""),
			    (writeable?"W":""),	address);
		    
		    result = DR_ApPointBufAddress (test_pnt, &device,
						   &buffer, &offset, &bit,
						   &error_str);
		    if (result == ST_OK)
		      {
			printf ("     Maps to: device %d, buffer %d, offset: %d",
				device, buffer, offset);
			if (type == DR_API_BIT_TYPE)
			  printf (", bit: %d", bit);
			printf ("\n");
		      }
		    else
		      printf ("     Error@PointBufAddress (%d, %s)\n",
			      result, error_str);
		  }
		else
		  printf ("     Error@DescribePoint (%d, %s)\n",
			  result, error_str);
		
		/*** test B.4: twiddle specified point value */
		
		wait_for_kybd (single_step);
		
		printf ("   Activating %s\n", test_pnt);
		ivalue.s = 1;
		result = DR_ApWritePoint (test_pnt, DR_API_BIT_TYPE,
					  &ivalue, &error_str);
		if (result == ST_OK)
		  {
		    delay (1000);
		    printf ("   Deactivating %s\n", test_pnt);
		    ivalue.s = 0;
		    result = DR_ApWritePoint (test_pnt, DR_API_BIT_TYPE,
					      &ivalue, &error_str);
		    /* need this delay to stabilize point? */
		    delay (100);
		  }
		if (result != ST_OK)
		  printf ("     Error@WritePoint (%d, %s)\n",
			  result, error_str);
	      }
	  }
	  
	  /* ********************************************
	   *   test group C: binary block data transfer
	   ******************************************** */
	  
	  wait_for_kybd (single_step);
	  
	  printf ("\nC) Testing Block interface.\n");
	  
	  {
	    int 					num_blks = 0;
	    int						i, j, n_attr;
	    unsigned short			buf_size[4];
	    DR_ApSegAttributes_t 	seg_attr[4][32];
	    
	    /*** test C.1: list buffer info */
	    
	    result = DR_ApListBuffers (0, 4, &num_blks, buf_size,
				       &error_str);
	    if (result == ST_OK)
	      {
		printf ("   Device 0 has %d buffers with the following sizes:\n",
			num_blks);
		for (i=0; i<num_blks; i++)
		  printf ("      Buffer %d: %d bytes.\n",
			  i, buf_size[i]);
		printf ("\n");
	      }
	    else
	      printf ("     Error@ListBuffers (%d,%s)\n",
		      result, error_str);
	    
	    /*** test C.2 list buffer segment attributes */
	    
	    for (i=0; i<num_blks; i++)
	      {
		result = DR_ApDescribeBuffer (0, i, 0, 32, &n_attr,
					      &(seg_attr[i][0]),
					      &error_str);
		if (result == ST_OK)
		  {
		    if (n_attr == 0)
		      printf ("   Buffer %d has not defined any segments.\n", i);
		    else
		      {
			printf ("   Buffer %d has defined the following segments:\n", i);
			for (j=0; j<n_attr; j++)
			  {
			    printf ("      Buffer %d, segment %d:",
				    (seg_attr[i][j].buf_id), j);
			    
			    printf (" %s%s%s", 
				    (seg_attr[i][j].readable?"reads":""),
				    ((seg_attr[i][j].readable &&
				      seg_attr[i][j].writeable) ? " and ":""),
				    (seg_attr[i][j].writeable?"writes":""));
			    printf (" %s data",
				    ((DR_API_BIT_TYPE == seg_attr[i][j].type)
				     ?"digital":
				     ((DR_API_INT16_TYPE == seg_attr[i][j].type)
				      ? "analog" : "unknown type")) );
			    printf (" from offset %d with length %d bytes\n",
				    seg_attr[i][j].offset,
				    seg_attr[i][j].size);
			  }
		      }
		  }
		else
		  printf ("     Error@DescribeBuffer (%d,%s)\n",
			  result, error_str);
	      }
	    
	    if (num_blks > 0 && n_attr > 0)
	      {
		printf ("   Digital Inputs (bit) are available from the following segments:\n");
		for (i=0; i<num_blks; i++)
		  {
		    for (j=0; j<n_attr; j++)
		      {
			if (seg_attr[i][j].readable &&
			    seg_attr[i][j].type==DR_API_BIT_TYPE)
			  {
			    printf("     buffer %d, from offset %d for %d bytes\n",
				   seg_attr[i][j].buf_id,
				   seg_attr[i][j].offset,
				   seg_attr[i][j].size);
			  }
		      }
		  }
		
		printf ("   Analog Outputs are available from the following segments:\n");
		for (i=0; i<num_blks; i++)
		  {
		    for (j=0; j<n_attr; j++)
		      {
			if (seg_attr[i][j].writeable &&
			    seg_attr[i][j].type==DR_API_INT16_TYPE)
			  {
			    printf("     buffer %d, from offset %d for %d bytes\n",
				   seg_attr[i][j].buf_id,
				   seg_attr[i][j].offset,
				   seg_attr[i][j].size);
			  }
		      }
		  }
		printf ("\n");
	      }
	  }
	  
	  if (block_length > 0)
	    {
	      /*** test C.3: read data blocks */
	      
	      wait_for_kybd (single_step);
	      
	      DR_ApUpdateBuffers ();
	      
	      printf ("   Reading input block: device %d, buffer %d, offset %d, length %d bytes \n",
		      0, 1, block_addr, block_length);
	      result = DR_ApReadBlock (0, 1, block_addr, block_length,
				       data, &error_str);
	      if (result == ST_OK)
		dump_data (6, data, block_length, 16, block_addr);
	      else
		printf ("     Error@ReadBlock.input (%d,%s)\n",
			result, error_str);
	      
	      wait_for_kybd (single_step);
	      
	      printf ("   Reading output block: device %d, buffer %d, offset %d, length %d bytes \n",
		      0, 0, block_addr, block_length);
	      result = DR_ApReadBlock (0, 0, block_addr, block_length,
				       data, &error_str);
	      if (result == ST_OK)
		dump_data (6, data, block_length, 16, block_addr);
	      else
		printf ("     Error@ReadBlock.output (%d,%s)\n",
			result, error_str);
	      
	      /*** test C.4: write data blocks */
	      
	      wait_for_kybd (single_step);
	      
	      {
		int				i, j, a_incr = 100;
		unsigned short	blk_addr_a = block_addr + analog_word_ofs;
		unsigned short	blk_addr_d = block_addr + digital_word_ofs;
		
		printf ("   Writing to output block: "
			"device %d, buffer %d, offset %d, length %d bytes \n",
			0, 0, block_addr, block_length);
		
		printf ("     incrementing word at block offset 0x%02X in steps of %d)\n",
			blk_addr_a, a_incr);
		
		*(short *)&data[blk_addr_a] = 0;
		for (i=0; i<=100 && result==ST_OK; i++)
		  {
		    if (swap)
		      {
			/* swab(src,dst,n) */
			swab (&(data[blk_addr_a]), (char *)&ivalue, 2);
			ivalue.s += a_incr;
			swab ((char *)&ivalue, &(data[blk_addr_a]), 2);
		      }
		    else
		      {
			/* memcpy(dst,src,n) */
			memcpy ((char *)&ivalue, &(data[blk_addr_a]), 2);
			ivalue.s += a_incr;
			memcpy (&(data[blk_addr_a]), (char *)&ivalue, 2);
		      }
		    printf ("\r     %+06d    ", ivalue.s);
		    for (j=0; j<block_length; j+=2)
		      printf("%02X%02X ",
			     (unsigned char)data[block_addr + j],
			     (unsigned char)data[block_addr + j+1]);
		    fflush (stdout);
		    
		    result = DR_ApWriteBlock (0, 0, block_addr, block_length,
					      data, &error_str);
		    if (result != ST_OK)
		      printf ("     Error@WriteBlock (%d,%s)\n",
			      result, error_str);
		    
		    DR_ApUpdateBuffers ();
		    delay (50);
		  }
		printf ("\n");
		*(short *)&data[blk_addr_a] = 0;
		
		printf ("     cycling bit through byte at block byte offset 0x%02X.\n",
			blk_addr_d);
		
		for (i=0; i<=8 && result==ST_OK; i++)
		  {
		    dvalue = data[blk_addr_d] & 0x00FF;
		    if (dvalue == 0)
		      dvalue = 1;
		    else
		      dvalue <<= 1;
		    data[blk_addr_d] = dvalue;
		    
		    printf ("\r     %04X      ", dvalue);
		    for (j=0; j<block_length; j+=2)
		      printf("%02X%02X ",
			     (unsigned char)data[block_addr + j],
			     (unsigned char)data[block_addr + j+1]);
		    fflush (stdout);
		    
		    result = DR_ApWriteBlock (0, 0, block_addr, block_length,
					      data, &error_str);
		    if (result != ST_OK)
		      printf ("     Error@WriteBlock (%d,%s)\n",
			      result, error_str);
		    
		    DR_ApUpdateBuffers ();
		    delay (500);
		  }
		printf ("\n");
		
		/* clear output digitals */
		data[blk_addr_d] = 0;
		result = DR_ApWriteBlock (0, 0, block_addr, block_length,
					  data, &error_str);
		DR_ApUpdateBuffers ();
	      }
	    }
	  else
	    printf ("   Block data read/write test skipped (block_length = 0)\n");
	  
	  /* ********************************************
	   *   test group D: control and status
	   ******************************************** */
	  wait_for_kybd (single_step);
	  
	  printf ("\nD) Testing Control & Status interface.\n");
	  
	  {
	    int					i;
	    dr5136dn_ApState_t	status;
	    
	    printf ("   Reading Status:\n");
	    if ((result = DR_ApReadStatus (0, 1, 0,
					   sizeof(dr5136dn_ApState_t),
					   &status, &error_str)))
	      printf ("     Error@ReadStatus (%d,%s)\n",
		      result, error_str);
	    else
	      {
		printf ("     CAN status: 0x%0X\n", status.CanStatus);
		if (status.CanStatus & AP_CAN_ONLINE)
		  printf ("%*s%s\n", 17, "", "CAN_ONLINE");
		if (status.CanStatus & AP_CAN_BUSWARN)
		  printf ("%*s%s\n", 17, "", "CAN_BUSWARN");
		if (status.CanStatus & AP_CAN_BUSOFF)
		  printf ("%*s%s\n", 17, "", "CAN_BUSOFF");
		if (status.CanStatus & AP_CAN_ACTIVITY)
		  printf ("%*s%s\n", 17, "", "CAN_ACTIVITY");
		if (status.CanStatus & AP_CAN_TACKERR)
		  printf ("%*s%s\n", 17, "", "CAN_TACKERR");
		if (status.CanStatus & AP_CAN_TIMEOUT)
		  printf ("%*s%s\n", 17, "", "CAN_TIMEOUT");
		if (status.CanStatus & AP_CAN_OVERRUN)
		  printf ("%*s%s\n", 17, "", "CAN_OVERRUN");
		if (status.CanStatus & AP_CAN_LOST)
		  printf ("%*s%s\n", 17, "", "CAN_LOST");
		if (status.CanStatus & AP_CAN_ERROR)
		  printf ("%*s%s\n", 17, "", "CAN_ERROR");
		if (status.CanStatus & AP_CAN_BUSPOWER)
		  printf ("%*s%s\n", 17, "", "CAN_BUSPOWER");
		if (status.CanStatus & AP_CAN_ONLINE125)
		  printf ("%*s%s\n", 17, "", "CAN_ONLINE125");
		if (status.CanStatus & AP_CAN_ONLINE250)
		  printf ("%*s%s\n", 17, "", "CAN_ONLINE250");
		if (status.CanStatus & AP_CAN_ONLINE500)
		  printf ("%*s%s\n", 17, "", "CAN_ONLINE500");
		if (status.CanStatus & AP_CAN_SCANACTIVE)
		  printf ("%*s%s\n", 17, "", "CAN_SCANACTIVE");
		
		printf ("     CAN transmit counter:     %d\n",
			status.CanTx);
		printf ("     CAN ACK error counter:    %d\n",
			status.CanAck);
		printf ("     CAN receive counter:      %d\n",
			status.CanRx);
		printf ("     CAN error counter:        %d\n",
			status.CanErr);
		printf ("     CAN msgs lost counter:    %d\n",
			status.CanLost);
		printf ("     CAN RcvQ overrun counter: %d\n",
			status.CanOverrun);
		
		printf ("\n");
		printf ("     Client status:    %s\n",
			status.ClientStatus==0 ? "IDLE" : "ACTIVE");
		printf ("     Server status:    %s\n",
			status.ServerStatus==0 ? "IDLE" : "ACTIVE");
		for (i=0; i<63; i++)
		  {
		    if (status.DeviceStatus[i])
		      {
			printf ("     Device %2d status: ", i);
			if (status.DeviceStatus[i]<=3)
			  printf ("%s\n", 
				  status.DeviceStatus[i]==1 ? "IDLE" :
				  status.DeviceStatus[i]==2 ? "SCANNED" :
				  "TIMEOUT");
			else
			  printf ("Error %d\n",
				  status.DeviceStatus[i]);
		      }
		  }
	      }
	  }
	  
	  /* ********************************************
	   *   test group E: direct interface
	   ******************************************** */
	  
	  wait_for_kybd (single_step);
	  
	  printf ("\nE) Testing Direct interface: toggle test point.\n");
	  
	  if (test_pnt)
	    {
	      int				type, enabled, readable, writeable;
	      int				device, buffer;
	      unsigned short 	offset, bit;
	      
	      result = DR_ApDescribePoint (test_pnt, &type, &enabled,
					   &readable, &writeable,
					   NULL, 0, &error_str);
	      result = DR_ApPointBufAddress (test_pnt, &device,
					     &buffer, &offset, &bit,
					     &error_str);
	      
	      if (writeable && type==DR_API_BIT_TYPE)
		{
		  AdminTask = IP_TaskNew();
		  if (IP_NserveLookup(admin_name, AdminTask) == -1)
		    {
		      IP_TaskDestroy (AdminTask);
		      AdminTask = NULL;
		    }
		  if (AdminTask)
		    {
		      printf ("   Toggle bit of %s at %d:%d:%d:%d\n",
			      test_pnt, 0, 0, offset, bit);
		      /* remember: named point addresses are in word offsets;
			 direct and block level interfaces need byte offsets
			 */
		      offset *= 2;
		      
		      ToggleOutputBit(AdminTask, device, buffer, offset,bit);
		      DR_ApUpdateBuffers();
		      delay(1000);
		      ToggleOutputBit(AdminTask, device, buffer, offset,bit);
		      DR_ApUpdateBuffers();
		      
		      IP_TaskDestroy (AdminTask);
		      AdminTask = NULL;
		    }
		  else
		    printf ("   Could not locate administrator %s\n",
			    admin_name);
		}
	      else
		printf ("   Select another point that is of type digital and writeable \n");
	      
	      delay (1000);
	    }
	  else
	    printf ("   Test skipped - no test point specified\n");
	}
      
      DR_ApCloseIPC();
    }
  else
    printf ("Failed to connect %s to %s\n", argv[0], argv[1]);
  
  return (0);
}