Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 31 Jul 1998 15:31:37 -0600
From:      chanders@timing.com (Craig Anderson)
To:        Mike Smith <mike@smith.net.au>
Cc:        freebsd-hackers@FreeBSD.ORG
Subject:   Re: Using FreeBSD for data acquisition? (long) 
Message-ID:  <199807312131.PAA04940@count.timing.com>
In-Reply-To: Your message of "Fri, 31 Jul 1998 13:55:30 PDT." <199807312055.NAA00498@dingo.cdrom.com> 

next in thread | previous in thread | raw e-mail | index | archive | help
This is a multipart MIME message.

--==_Exmh_18363059200
Content-Type: text/plain; charset=us-ascii


I've attached all the source.

Thanks again.

> > 
> > My company is considering using FreeBSD for a data acquisition
> > application.  We want to know if an upper bound be placed on the
> > latency of an application reading data from a data acquisition
> > board? FreeBSD is being considered first and then commercial RTOS's
> > will be tried.
> 
> No, there is no upper bound.  Your response time will vary depending on 
> a large number of factors, eg. swap activity, other driver activity, 
> hardware errors, etc.
> 
> > A test project is being done.  The project uses an ISA data
> > acquisition board.  The board interrupts 500 times a second, an 8
> > byte data block is read from the board on each interrupt.
> > 
> > There is a driver lkm for the board:  a) driver buffers 4k data
> > blocks.  b) driver interrupt function reads 1 data block, stores
> > data block in buffer, wakes the reader.  c) driver read function
> > waits for at least 1 data block, returns minimum of requested data
> > blocks and available data blocks.
> > 
> > A test application:  a) Loops forever requesting 4096 data blocks.
> > b) Saves the number of data blocks returned on each read to a file.
> > 
> > Because the interrupts/data blocks arrive at 500 HZ, I am interpreting
> > the number of data blocks returned on a read as a measure of read
> > latency of the application.  If the read returns 50 data blocks, it
> > has been .1 second since the last read.  (50/500 = .1)
> > 
> > Below are code fragments from the test application.  The rtprio()
> > call is to put the process into RTP_PRIO_REALTIME.  Should this
> > make a difference?  Is the code correct?
> 
> You haven't included enough code to be certain that your device driver 
> is working properly.  I suspect that it might not be.
> 
> > At the end of the message is histogram data for a test run of about
> > 15 hours.  I plot the data with gnuplot, using a logscale for y.
> > Note that the longest read is 480 (480/500 = .960 seconds).  Note
> > that the most common read is 50 (50/500 = .100 seconds).
> 
> This would tend to indicate a possible problem with the interaction 
> between your interrupt handler and the read handler, but it's 
> impossible to tell without seeing the code.
> 
> > I'd like comments from FreeBSD hackers out there.  Can an upper
> > bound be put on the read latency? An upper bound of about .100 seconds
> > is probably acceptable.  Why is a read latency of .100 seconds (50
> > data blocks) so common? Should RTP_PRIO_REALTIME help? Does FreeBSD
> > 3 have realtime features that will help? Is there a problem with
> > the code or a better approach?
> 
> In a soft-realtime project I was working on last year, we used the
> rtprio facility to expedite the data handling process.  We were able to 
> meet a hardware-mandated 100msec delay with light loading under some 
> circumstances, but this was not sustainable if disk I/O was being 
> performed (we were saving incoming data at >1MB/sec).
> 
> There are enhanced realtime facilities in FreeBSD 3.0 which would help 
> in some cases, however it's not clear from your message what your real 
> goals are.
> 

> 
> 


--==_Exmh_18363059200
Content-Type: text/plain ; name="gt401.h"; charset=us-ascii
Content-Description: gt401.h
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment; filename="gt401.h"

#if !defined(gt401_h)
#  define gt401_h

#include <sys/ioccom.h>

/* units of pt4 are 0.4 x 10^-6 sec */

#define D_TYPE_NONE 99
#define D_TYPE_ERROR 1
#define D_TYPE_TIMETAG 2

#define MIN_PERIOD_X_10000 1
#define MAX_PERIOD_X_10000 50000

typedef struct gt401_read_s
{
  short overflow;
  short d_type;
  union
  {
    struct
    {
      unsigned int board_index;
      long long board_time_tag_usec;
      unsigned int driver_index;
      long long driver_board_usec;
    } tt;
    struct
    {
      short err_code;
      char err_str[58];
    } ee;
  } dd;
}
gt401_read;

#define GT401_START_EVENTS \
		_IOW('G', 1, int)
#define GT401_STOP_EVENTS \
		_IO('G', 2)
#define GT401_GET_HIGH_WATER \
		_IOR('G', 3, int)
#define GT401_CLEAR_HIGH_WATER \
		_IO('G', 4)

#endif /* #if !defined(gt401_h) */

--==_Exmh_18363059200
Content-Type: text/plain ; name="Makefile"; charset=us-ascii
Content-Description: Makefile
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment; filename="Makefile"

# $Id: Makefile,v 1.12 1998/07/30 17:19:23 chanders Exp $

=2EPATH:	.
KMOD	=3D gt401_mod
SRCS	=3D gt401.c gt401.h ngt401.h
#MAN4	=3D gt401.4
#MAN8	=3D gt401in.8
LN	=3D ln -f -s

CFLAGS	+=3D \
		-I. \
		-I/usr/src/sys \
		-DGT401_MODULE

CLEANFILES	+=3D ngt401.h

ngt401.h:
	echo "#define NGT401 1" > ngt401.h

r0:	gt401_mod.o
	cp gt401_mod.o \
		/usr/local/tsc/rel/bin


test03: test03.c
	cc -o test03 test03.c

test04: test04.c
	cc -o test04 test04.c

test05: test05.c
	cc -o test05 test05.c

test06: test06.c
	cc -g -o test06 test06.c

test07: test07.c
	cc -g -o test07 test07.c

test08: test08.c
	cc -g -o test08 test08.c

log_read_counts_980726:	log_read_counts_980726.c
	cc -g -o log_read_counts_980726 log_read_counts_980726.c

counts_to_histogram: counts_to_histogram.c
	cc -g -o counts_to_histogram counts_to_histogram.c

kmem_gt401: kmem_gt401.c
	cc -g -o kmem_gt401 kmem_gt401.c

=2Einclude <bsd.kmod.mk>


--==_Exmh_18363059200
Content-Type: text/plain ; name="gt401.c"; charset=us-ascii
Content-Description: gt401.c
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment; filename="gt401.c"


#include <sys/errno.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/signalvar.h>
#include <sys/proc.h>
#include <sys/kernel.h>

#include <machine/clock.h>
#include <machine/endian.h>

#include <i386/isa/isa.h>
#include <i386/isa/isa_device.h>
#include <i386/isa/timerreg.h>
#include <i386/isa/icu.h>

#include "ngt401.h"
#include "gt401.h"

#define UNIT(d)		(minor(d) >> 16)

#define CDEV_MAJOR 90
#define MAX_GT401_RBUF (4*1024)

enum
{
  io_addr_status_reg =3D 0,
  io_addr_ram_addr_reg =3D 0,
  io_addr_cont_reg_1 =3D 1,
  io_addr_cont_reg_2 =3D 2,
  io_addr_mux_addr_reg =3D 3,
  io_addr_mux_data_reg =3D 4,
  io_addr_8254_data_reg =3D 5,
  io_addr_us_count0 =3D 6,
  io_addr_us_count1 =3D 7,
  io_addr_cmd_ready =3D 7,
  io_addr_dp_ram =3D 8,
  io_addr_err_reg =3D 13,

  dp_ram_lus_page =3D 0,
  dp_ram_command_first_page =3D 144,
  dp_ram_status_page =3D 146,

  mux_54_ctr_0 =3D 0,
  mux_54_ctr_1 =3D 8,
  mux_54_ctr_2 =3D 16,
  mux_54_cont =3D 24,

  cmd_poll_inner_loop =3D 512,
  cmd_poll_sleep_loop =3D 20,
  cmd_poll_usleep =3D 10000,
  cmd_buf_sz =3D 20,

  cmd_init =3D 1,
  cmd_self_test =3D 2,
  cmd_nop =3D 3,
  cmd_ext_osc =3D 7,
  cmd_pulse =3D 8,
  cmd_tt =3D 9,
  cmd_chan_off =3D 12,
  cmd_tt_size =3D 13,
  cmd_clr_tt =3D 15,
  cmd_clr_err =3D 20,

  cont_reg1_src_mask =3D 7,
  cont_reg1_pulse_en =3D 0x08,
  cont_reg1_sw_pulse =3D 0x10,

  status_reg_ready_for_cmd =3D 1,

  cont_reg2_irq_src_mask =3D 7,
  cont_reg2_irq_src_chan0 =3D 0,
  cont_reg2_irq_src_chan1 =3D 1,
  cont_reg2_irq_src_chan2 =3D 2,
  cont_reg2_irq_src_chan3 =3D 3,
  cont_reg2_irq_src_board_proc =3D 4,
  cont_reg2_irq_src_out0 =3D 5,
  cont_reg2_irq_src_out1 =3D 6,
  cont_reg2_irq_src_out2 =3D 7,
  cont_reg2_intr_en_bit =3D 0x08,
  cont_reg2_intr_edge_bit =3D 0x10,

  conn_CLK0 =3D 0,   /* All CLKn values must be consecutive */
  conn_CLK1 =3D 1,
  conn_CLK2 =3D 2,
  conn_EXT_CLK0 =3D 3,   /* All EXT_CLKn values must be consecutive */
  conn_EXT_CLK1 =3D 4,
  conn_EXT_CLK2 =3D 5,
  conn_GATE0 =3D 6,   /* All GATEn values must be consecutive */
  conn_GATE1 =3D 7,
  conn_GATE2 =3D 8,
  conn_EXT_GATE0 =3D 9,   /* All EXT_GATEn values must be consecutive */
  conn_EXT_GATE1 =3D 10,
  conn_EXT_GATE2 =3D 11,
  conn_OUT0 =3D 12,  /* All OUTn values must be consecutive */
  conn_OUT1 =3D 13,
  conn_OUT2 =3D 14,
  conn_IO_CHAN0 =3D 15,  /* All IO_CHANn values must be consecutive */
  conn_IO_CHAN1 =3D 16,
  conn_IO_CHAN2 =3D 17,
  conn_IO_CHAN3 =3D 18,
  conn_EXT_CHAN0 =3D 19,  /* all EXT_CHANNn values must be consecutive */=

  conn_EXT_CHAN1 =3D 20,
  conn_EXT_CHAN2 =3D 21,
  conn_EXT_CHAN3 =3D 22,
  conn_TEN_MHZ =3D 23,
  conn_GATED_TEN_MHZ =3D 24,
  conn_SW_LOW =3D 25,  /* To force an input low */
  conn_SW_HIGH =3D 26,  /* To force an input high */
  conn_SW_INTR =3D 27,  /* For selecting interrupts from */

  tt_ptr_reg =3D 12,   /* in dpram status page */
  err_reg =3D 13, /* in dpram status page */

  tt_edge_neg =3D 0x08,
  tt_edge_pos =3D 0x10,

  ten_int =3D 2,
  ten_int_out =3D 3,
  ten_ext =3D 4,

  err_success =3D 0,
  err_no_io_fd =3D 1,
  err_timeout =3D 2,
  err_cmd_sz =3D 3,
  err_invalid_8254_ctr =3D 4,
  err_connect_dst =3D 5,
  err_connect_src =3D 6,
  err_io_open =3D 7,
  err_ten_mhz =3D 8,
  err_irq_src =3D 9
};

/* One dev per board */
static struct isa_device devices[NGT401];

/*
 * Setup the per-device structure defining routines used at 'boot' time.
 */
static int gt401attach(struct isa_device* devices);
static int gt401probe(struct isa_device* devices);

void		gt401intr(int unit);

static int get_lus (int base_io_port, long long *lus);
static int mux_matrix_connect (int unit,
			       u_short src,
			       u_short dst);
static int pulse_out (int unit,
		      int enable,
		      int src);
static int removeIntr(int irq);
static int send_cmd (int unit, u_char *cmd_buf, u_int size);
static int set_8254 (int unit,
		     u_short counter,
		     u_short mode,
		     u_short initial_count);
static int set_irq (int unit,
		    int enable,
		    int src,
		    int edge);
static int setupIntr(struct isa_device* device,
		     int irq,
		     inthand2_t* hand,
		     int unit,
		     u_int* maskp);

static int start_tt (int unit);
static int stop_tt (int unit);
static int get_tt_count (int unit);
static int get_tt (int unit, long long *tt);
static int get_status(int unit);

struct isa_driver gt401driver =3D
{
    gt401probe,
    gt401attach,
    "gt401"
};

static	d_open_t	gt401open;
static	d_close_t	gt401close;
static	d_read_t	gt401read;
static	d_ioctl_t	gt401ioctl;
static	d_select_t	gt401select;

static struct cdevsw gt401_cdevsw =3D
{
  gt401open,
  gt401close,
  gt401read,
  nowrite,
  gt401ioctl,
  nostop,
  nullreset,
  nodevtotty,
  gt401select,
  nommap,
  NULL,
  "gt401",
  NULL,
  -1
};

struct gt401Unit
{
  short inuse;
  short tagging_active;

  char * sleep_address;

  u_char cont_reg1;
  u_char cont_reg2;
  u_char mux_control[8];
  int counts_8254[2];

  u_int driver_index;
  u_int max_rbuf_count;

  /* rbuf_XXX is a ring buffer for reads */
  short rbuf_count;
  short rbuf_first;
  short rbuf_last;
  gt401_read rbuf [MAX_GT401_RBUF];
};

#if 1 /* debug */
static char * save_sleep_address =3D 0;
#endif
static struct gt401Unit gt401Unit[NGT401];

static int
get_lus (int base_io_port, long long *lus)
{
  char us_count[8] =3D {0};
  char bb[8] =3D {0};
  long long *ll_us =3D (long long *)&(us_count[0]);
  long long *ll_bb =3D (long long *)&(bb[0]);

  us_count [0] =3D inb (base_io_port + io_addr_us_count0);

  outb((base_io_port + io_addr_ram_addr_reg), dp_ram_lus_page);

  inb(base_io_port); /* need a 1us delay */
  inb(base_io_port);

  us_count [1] =3D inb (base_io_port + io_addr_us_count1);

  bb[0] =3D inb (base_io_port + io_addr_dp_ram + 0);
  bb[1] =3D inb (base_io_port + io_addr_dp_ram + 1);
  bb[2] =3D inb (base_io_port + io_addr_dp_ram + 2);
  bb[3] =3D inb (base_io_port + io_addr_dp_ram + 3);
  bb[4] =3D inb (base_io_port + io_addr_dp_ram + 4);
  bb[5] =3D inb (base_io_port + io_addr_dp_ram + 5);
  bb[6] =3D inb (base_io_port + io_addr_dp_ram + 6);
  bb[7] =3D inb (base_io_port + io_addr_dp_ram + 7);

  *lus =3D *ll_bb + *ll_us;

  return 0;
}

static int
gt401attach(struct isa_device* devices)
{
  int ii,jj;

  uprintf ("gt401attach\n");

  for (ii=3D0; ii<NGT401; ++ii)
    {
      gt401Unit[ii].inuse =3D 0;
      gt401Unit[ii].tagging_active =3D 0;
      gt401Unit[ii].sleep_address =3D (char *)&(gt401Unit[ii].inuse);
      gt401Unit[ii].cont_reg1 =3D 0;
      gt401Unit[ii].cont_reg2 =3D 0;
      for (jj=3D0; jj<8; ++jj)
	gt401Unit[ii].mux_control[jj] =3D 0;
      gt401Unit[ii].counts_8254[0] =3D 0;
      gt401Unit[ii].counts_8254[1] =3D 0;
      gt401Unit[ii].driver_index =3D 0;
      gt401Unit[ii].max_rbuf_count =3D 0;
      gt401Unit[ii].rbuf_count =3D 0;
      gt401Unit[ii].rbuf_first =3D 0;
      gt401Unit[ii].rbuf_last =3D MAX_GT401_RBUF - 1;
      for (jj=3D0; jj<MAX_GT401_RBUF; ++jj)
	{
	  gt401Unit[ii].rbuf[jj].d_type =3D D_TYPE_NONE;
	  gt401Unit[ii].rbuf[jj].overflow =3D 0;
	}
    }
#if 1 /* debug */
  save_sleep_address =3D gt401Unit[0].sleep_address;
#endif
  return 1;
}

static int
gt401close (dev_t dev,
	    int flags,
	    int fmt,
	    struct proc* p)
{
  int  unit =3D UNIT(dev);

#if 1 /* debug */
  uprintf ("gt401close unit=3D%d\n",unit);
#endif

  gt401Unit[unit].tagging_active =3D 0;
  gt401Unit[unit].inuse =3D 0;
  set_irq (unit,
	   0,
	   -1,
	   1);

  return 0;
}
static int
gt401ioctl (dev_t dev,
	    int cmd,
	    caddr_t data,
	    int flag,
	    struct proc* p)
{
  int  unit =3D UNIT(dev);

  if ((unit < 0) || (unit >=3D NGT401))
    return (ENODEV);

  if (!(devices[unit].id_alive))
    return (ENODEV);

  if (!gt401Unit[unit].inuse)
    return (ENODEV);

  switch (cmd)
    {
    case GT401_START_EVENTS:
      {
	int *       count_x_10000_i;
	int         max_counter =3D 0xffff;
	double      period;
	double      period_x_10000;
	double      target_counts_d;
	int         target_counts_i;

#if 0
	stop_tt(unit);
#endif

	count_x_10000_i =3D ((int *)data);

#if 1 /* debug */
	uprintf("gt401ioctl: GT401_START_EVENTS arg=3D%d\n",*count_x_10000_i);
#endif

	if (*count_x_10000_i < MIN_PERIOD_X_10000)
	  {
	    return (EINVAL);
	  }
	if (*count_x_10000_i > MAX_PERIOD_X_10000)
	  {
	    return (EINVAL);
	  }
	period_x_10000 =3D (double)(*count_x_10000_i);
	period =3D period_x_10000 / 10000.;
	target_counts_d =3D period * 10e6;
	target_counts_i =3D (int)target_counts_d;

	if (target_counts_i < max_counter)
	  {
	    gt401Unit[unit].counts_8254[0] =3D target_counts_i;
	    gt401Unit[unit].counts_8254[1] =3D 0;
	  }
	else
	  {
	    gt401Unit[unit].counts_8254[0] =3D target_counts_i / max_counter;
	    ++gt401Unit[unit].counts_8254[0];
	    gt401Unit[unit].counts_8254[1] =3D (target_counts_i
					      / gt401Unit[unit].counts_8254[0]);
	  }
#if 1 /* debug */
	uprintf("gt401ioctl: 8254[0]=3D%d 8254[1]=3D%d\n",
		gt401Unit[unit].counts_8254[0],
		gt401Unit[unit].counts_8254[1]);
#endif
	if (gt401Unit[unit].counts_8254[1] <=3D 0)
	  { /* using one 8254 */
	    mux_matrix_connect (unit,
				conn_TEN_MHZ,
				conn_CLK1);
	    mux_matrix_connect (unit,
				conn_SW_HIGH,
				conn_GATE1);
	    set_8254 (unit,
		      1,
		      3,
		      gt401Unit[unit].counts_8254[0]);
	    set_irq (unit,
		     1,
		     conn_OUT1,
		     1);
	  } /* using one 8254 */
	else
	  { /* using two 8254's */
	    mux_matrix_connect (unit,
				conn_TEN_MHZ,
				conn_CLK0);
	    mux_matrix_connect (unit,
				conn_SW_HIGH,
				conn_GATE0);
	    set_8254 (unit,
		      0,
		      3,
		      gt401Unit[unit].counts_8254[0]);
	    mux_matrix_connect (unit,
				conn_OUT0,
				conn_CLK1);
	    mux_matrix_connect (unit,
				conn_SW_HIGH,
				conn_GATE1);
	    set_8254 (unit,
		      1,
		      3,
		      gt401Unit[unit].counts_8254[1]);
	    set_irq (unit,
		     1,
		     conn_OUT1,
		     1);
	  } /* using two 8254's */

#if 0 /* debug */
	uprintf("gt401ioctl: disable 8254 clock for debug.\n");
	mux_matrix_connect (unit,
			    conn_SW_LOW,
			    conn_GATE0);
	mux_matrix_connect (unit,
			    conn_SW_LOW,
			    conn_GATE1);
#endif
	gt401Unit[unit].tagging_active =3D 1;

#if 0 /* not yet */
	start_tt(unit);
#endif

      }
      break;
    case GT401_STOP_EVENTS:
      gt401Unit[unit].tagging_active =3D 0;
#if 1 /* debug */
      uprintf("gt401ioctl: GT401_STOP_EVENTS\n");
#endif
#if 0 /* not yet */
      stop_tt(unit);
#endif
      set_irq (unit,
	       0,
	       -1,
	       1);
      break;
    case GT401_GET_HIGH_WATER:
      {
	int * iarg =3D ((int *)data);
	*iarg =3D gt401Unit[unit].max_rbuf_count;
      }
      break;
    case GT401_CLEAR_HIGH_WATER:
      gt401Unit[unit].max_rbuf_count =3D 0;
      break;
    default:
      return (ENODEV);
      break;
    }
  return 0;
}

void
gt401intr(int unit)
{
  int        base_io_port;
  int        io_cont_reg2;
  long long  btime_intr;
  int        rbuf_index;

  unit =3D 0; /* debug */
  base_io_port =3D devices[0].id_iobase;
  get_lus(base_io_port,&btime_intr);

  if (gt401Unit[0].tagging_active =3D=3D 0)
    return;

  ++gt401Unit[0].driver_index;

  rbuf_index =3D gt401Unit[0].rbuf_last + 1;
  if (rbuf_index >=3D MAX_GT401_RBUF)
    rbuf_index =3D 0;

  if (gt401Unit[0].rbuf_count >=3D MAX_GT401_RBUF)
    {
      gt401Unit[0].rbuf_count =3D MAX_GT401_RBUF - 1;
      ++gt401Unit[0].rbuf_first;
      if(gt401Unit[0].rbuf_first >=3D MAX_GT401_RBUF)
	gt401Unit[0].rbuf_first =3D 0;
      gt401Unit[0].rbuf [gt401Unit[0].rbuf_first].overflow =3D 1;
    }
  gt401Unit[0].rbuf[rbuf_index].d_type =3D D_TYPE_TIMETAG;
  gt401Unit[0].rbuf[rbuf_index].dd.tt.driver_index =3D gt401Unit[0].drive=
r_index;
  gt401Unit[0].rbuf[rbuf_index].dd.tt.driver_board_usec =3D btime_intr;
  ++gt401Unit[0].rbuf_count;
  gt401Unit[0].rbuf_last =3D rbuf_index;
  if (gt401Unit[0].max_rbuf_count < gt401Unit[0].rbuf_count)
    gt401Unit[0].max_rbuf_count =3D gt401Unit[0].rbuf_count;

  io_cont_reg2 =3D base_io_port + io_addr_cont_reg_2;

  gt401Unit[0].cont_reg2 &=3D ~(cont_reg2_intr_en_bit);
  outb (io_cont_reg2,
	gt401Unit[0].cont_reg2);

  send_cmd (unit, NULL, 0);

  if (gt401Unit[0].tagging_active !=3D 0)
    {
      gt401Unit[0].cont_reg2 |=3D cont_reg2_intr_en_bit;
      outb (io_cont_reg2,
	    gt401Unit[0].cont_reg2);
    }
#if 1 /* debug */
  if (gt401Unit[0].sleep_address !=3D save_sleep_address)
    {
      panic("gt401intr: sleep_address %x !=3D %x",
	    gt401Unit[0].sleep_address,
	    save_sleep_address);
    }
#endif
  wakeup (gt401Unit[0].sleep_address);
}

static int
gt401open (dev_t dev,
	   int flags,
	   int fmt,
	   struct proc* p)
{
  int  unit =3D UNIT(dev);
  int jj;

#if 1 /* debug */
  uprintf("gt401open unit=3D%d\n",unit);
#endif

  if ((unit < 0) || (unit >=3D NGT401))
    return (ENXIO);

  if (!(devices[unit].id_alive))
    return (ENXIO);

  if (gt401Unit[unit].inuse)
    return (EBUSY);

  gt401Unit[unit].inuse =3D 1;
  gt401Unit[unit].tagging_active =3D 0;
  gt401Unit[unit].rbuf_count =3D 0;
  gt401Unit[unit].rbuf_first =3D 0;
  gt401Unit[unit].rbuf_last =3D MAX_GT401_RBUF - 1;
  for (jj=3D0; jj<MAX_GT401_RBUF; ++jj)
    {
      gt401Unit[unit].rbuf[jj].d_type =3D D_TYPE_NONE;
      gt401Unit[unit].rbuf[jj].overflow =3D 0;
    }

#if 1 /* debug */
  {
    int addr =3D devices[unit].id_iobase;
    uprintf("gt401open unit=3D%d io_addr=3D0x%x\n",unit,addr);
  }
#endif

  return 0;
}

static int
gt401probe(struct isa_device* devices)
{
  int addr =3D 0x100;
  int ii;
  long long time_check[9];
  long long time_diffs[8];
  int units =3D 0;
  long long duration_max;
  long long duration_min;
  long long min_max_range;
  long long min_max_range_check;

  for (ii=3D0; ii<NGT401; ++ii)
    {
      devices[ii].id_alive =3D 0;
      devices[ii].id_enabled =3D 0;
    }

  /* check for gt401 at addr by reading it's time */
  for (ii=3D0; ii<9; ++ii)
    {
      get_lus (addr,&(time_check[ii]));
      tsleep (&ii, PZERO, "gt401", 10);
    }

  for (ii=3D1; ii<9; ++ii)
    {
      time_diffs[ii-1] =3D time_check[ii] - time_check[ii-1];
    }
  duration_max =3D duration_min =3D time_diffs[0];
  for (ii=3D0; ii<8; ++ii)
    {
      if (duration_max < time_diffs[ii])
	duration_max =3D time_diffs[ii];
      if (duration_min > time_diffs[ii])
	duration_min =3D time_diffs[ii];
    }
  min_max_range_check =3D duration_min / 2;
  min_max_range =3D duration_max - duration_min;

#if 1 /* debug */
  uprintf("gt401probe addr 0x%x min %d max %d check %d range %d\n",
	  addr,
	  (int)duration_min,
	  (int)duration_max,
	  (int)min_max_range_check,
	  (int)min_max_range);
#endif

  if (duration_min < 5000)
    {
      uprintf("gt401probe time diff too small\n");
      return 0;
    }
  if (min_max_range > min_max_range_check)
    {
      uprintf("gt401probe time diff too variable\n");
      return 0;
    }

  devices[0].id_unit =3D 0;
  devices[0].id_iobase =3D addr;
  devices[0].id_driver =3D &gt401driver;
  devices[0].id_irq =3D 0;
  devices[0].id_intr =3D gt401intr;
  devices[0].id_drq =3D -1;
#if 0 /* debug */
  devices[0].id_ri_flags =3D RI_FAST;
#endif

  devices[0].id_alive =3D 1;
  devices[0].id_enabled =3D 1;

  ++units;

  return units;
}

static int
gt401read (dev_t dev,
	   struct uio* uio,
	   int flag)
{
  int     left_to_move =3D 0;
  int     status;
  int     unit =3D UNIT(dev);
  int     i_timeout =3D hz / 4;

  if ((unit < 0) || (unit >=3D NGT401))
    return (ENODEV);
  if (!(devices[0].id_alive))
    return (ENODEV);
  if (!gt401Unit[0].inuse)
    return (ENODEV);

  disable_intr();
  for (;;)
    {
      if (gt401Unit[0].rbuf_count > 0)
	break;
      enable_intr();
      status =3D tsleep (gt401Unit[0].sleep_address,
		       (PSWP | PCATCH),
		       "gt401",
		       i_timeout);
      if (!((status =3D=3D 0)
	    || (status =3D=3D EWOULDBLOCK)))
	{
	  return (status);
	}
      disable_intr();
    }

  left_to_move =3D uio->uio_resid / sizeof(struct gt401_read_s);

  while (left_to_move > 0)
    {
      if (gt401Unit[0].rbuf_count <=3D 0)
	break;

      status =3D uiomove((caddr_t)&(gt401Unit[0].rbuf[gt401Unit[0].rbuf_f=
irst]),
		       sizeof(struct gt401_read_s),
		       uio);
      if (status)
	{
	  enable_intr();
	  return (EFAULT);
	}

      --left_to_move;

      --gt401Unit[0].rbuf_count;
      ++gt401Unit[0].rbuf_first;
      if(gt401Unit[0].rbuf_first >=3D MAX_GT401_RBUF)
	gt401Unit[0].rbuf_first =3D 0;

      if (gt401Unit[0].rbuf_count <=3D 0)
	break;
    }

  enable_intr();
  return 0;
}

static int
gt401select (dev_t dev,
	     int rw,
	     struct proc* p)
{
  return 0;
}

/*
 * =

 */
#ifdef GT401_MODULE

#include <sys/exec.h>
#include <sys/sysent.h>
#include <sys/sysproto.h>
#include <sys/lkm.h>

MOD_DEV(gt401, LM_DT_CHAR, CDEV_MAJOR, &gt401_cdevsw);

int =

gt401_load(struct lkm_table* lkmtp, int cmd)
{
  int units;
  int irq =3D 11;
  int istat;

  uprintf("Gt401 driver loading, capable of %d board(s)\n", NGT401);

  units =3D gt401probe (&devices[0]);
  if (units <=3D 0)
    {
      uprintf("gt401 driver: probe failed\n");
      return (1);
    }

  gt401attach (&devices[0]);

  istat =3D setupIntr (&devices[0],
		     irq,
		     gt401intr,
		     0,
		     NULL);
  if (istat !=3D irq)
    {
      uprintf ("gt401 driver: INT %d register failed\n",irq);
      return (1);
    }

  return (0);
}

int
gt401_unload(struct lkm_table* lkmtp, int cmd)
{
  int unit;

  uprintf("Gt401 driver unloading...\n");

  for (unit=3D0; unit<NGT401; ++unit)
    {
      if (devices[unit].id_alive =3D=3D 1)
	{
	  /** disable interrupts on board */

	  if (devices[unit].id_irq !=3D 0)
	    {
	      uprintf ("removeINTR 0x%x\n",(ffs(devices[unit].id_irq) - 1));
	      removeIntr(ffs(devices[unit].id_irq) - 1);
	    }
	}
    }

  return (0);
}

/** XXX: /usr/include/lkm.h fails to define this: */
int gt401_stat(struct lkm_table* lkmtp, int cmd);
int
gt401_stat(struct lkm_table* lkmtp, int cmd)
{
    return (0);
}

int
gt401_mod(struct lkm_table* lkmtp, int cmd, int ver)
{
  DISPATCH(lkmtp, cmd, ver, gt401_load, gt401_unload, gt401_stat);
}

#endif /* GT401_MODULE */

static int
mux_matrix_connect (int unit,
		    u_short src,
		    u_short dst)
{
  int        base_io_port;
  int        err;
  u_short    mux_addr_reg;
  u_char     mux_addr_reg_val;
  u_short    mux_data_reg;
  u_char     src_val;

  base_io_port =3D devices[unit].id_iobase;

  err =3D 0;

  mux_addr_reg =3D base_io_port + io_addr_mux_addr_reg;
  mux_data_reg =3D base_io_port + io_addr_mux_data_reg;

  // verify that dst is valid
  switch (dst)
    {
    case conn_IO_CHAN0:
    case conn_IO_CHAN1:
    case conn_IO_CHAN2:
    case conn_IO_CHAN3:
    case conn_EXT_CHAN0:
    case conn_EXT_CHAN1:
    case conn_EXT_CHAN2:
    case conn_EXT_CHAN3:
    case conn_CLK0:
    case conn_CLK1:
    case conn_CLK2:
    case conn_GATE0:
    case conn_GATE1:
    case conn_GATE2:
      break;
    default:
      err =3D err_connect_dst;
      return err;
    }

  // verify that src is valid
  switch (dst)
    {
    case conn_IO_CHAN0:
    case conn_IO_CHAN1:
    case conn_IO_CHAN2:
    case conn_IO_CHAN3:
      switch (src)
	{
	case conn_EXT_CHAN0:
	  src_val =3D 0;
	  break;
	case conn_EXT_CHAN1:
	  src_val =3D 1;
	  break;
	case conn_EXT_CHAN2:
	  src_val =3D 2;
	  break;
	case conn_EXT_CHAN3:
	  src_val =3D 3;
	  break;
	case conn_OUT0:
	  src_val =3D 4;
	  break;
	case conn_OUT1:
	  src_val =3D 5;
	  break;
	case conn_OUT2:
	  src_val =3D 6;
	  break;
	case conn_SW_LOW:
	  src_val =3D 7;
	  break;
	case conn_SW_HIGH:
	  src_val =3D 7;
	  break;
	default:
	  err =3D err_connect_src;
	  return err;
	}
      break;
    case conn_EXT_CHAN0:
      if (src !=3D conn_IO_CHAN0)
	{
	  err =3D err_connect_src;
	  return err;
	}
      break;
    case conn_EXT_CHAN1:
      if (src !=3D conn_IO_CHAN1)
	{
	  err =3D err_connect_src;
	  return err;
	}
      break;
    case conn_EXT_CHAN2:
      if (src !=3D conn_IO_CHAN2)
	{
	  err =3D err_connect_src;
	  return err;
	}
      break;
    case conn_EXT_CHAN3:
      if (src !=3D conn_IO_CHAN3)
	{
	  err =3D err_connect_src;
	  return err;
	}
      break;
    case conn_CLK0:
      switch (src)
	{
	case conn_IO_CHAN0:
	  src_val =3D 0;
	  break;
	case conn_IO_CHAN1:
	  src_val =3D 1;
	  break;
	case conn_IO_CHAN2:
	  src_val =3D 2;
	  break;
	case conn_IO_CHAN3:
	  src_val =3D 3;
	  break;
	case conn_OUT2:
	  src_val =3D 4;
	  break;
	case conn_TEN_MHZ:
	  src_val =3D 5;
	  break;
	case conn_GATED_TEN_MHZ:
	  src_val =3D 6;
	  break;
	case conn_EXT_CLK2:
	  src_val =3D 7;
	  break;
	default:
	  err =3D err_connect_dst;
	  return err;
	}
      break;
    case conn_CLK1:
      switch (src)
	{
	case conn_IO_CHAN0:
	  src_val =3D 0;
	  break;
	case conn_IO_CHAN1:
	  src_val =3D 1;
	  break;
	case conn_IO_CHAN2:
	  src_val =3D 2;
	  break;
	case conn_IO_CHAN3:
	  src_val =3D 3;
	  break;
	case conn_OUT0:
	  src_val =3D 4;
	  break;
	case conn_TEN_MHZ:
	  src_val =3D 5;
	  break;
	case conn_GATED_TEN_MHZ:
	  src_val =3D 6;
	  break;
	case conn_EXT_CLK2:
	  src_val =3D 7;
	  break;
	default:
	  err =3D err_connect_dst;
	  return err;
	}
      break;
    case conn_CLK2:
      switch (src)
	{
	case conn_IO_CHAN0:
	  src_val =3D 0;
	  break;
	case conn_IO_CHAN1:
	  src_val =3D 1;
	  break;
	case conn_IO_CHAN2:
	  src_val =3D 2;
	  break;
	case conn_IO_CHAN3:
	  src_val =3D 3;
	  break;
	case conn_OUT1:
	  src_val =3D 4;
	  break;
	case conn_TEN_MHZ:
	  src_val =3D 5;
	  break;
	case conn_GATED_TEN_MHZ:
	  src_val =3D 6;
	  break;
	case conn_EXT_CLK2:
	  src_val =3D 7;
	  break;
	default:
	  err =3D err_connect_dst;
	  return err;
	}
      break;
    case conn_GATE0:
      switch (src)
	{
	case conn_IO_CHAN0:
	  src_val =3D 0;
	  break;
	case conn_IO_CHAN1:
	  src_val =3D 1;
	  break;
	case conn_IO_CHAN2:
	  src_val =3D 2;
	  break;
	case conn_IO_CHAN3:
	  src_val =3D 3;
	  break;
	case conn_OUT1:
	  src_val =3D 4;
	  break;
	case conn_OUT2:
	  src_val =3D 5;
	  break;
	case conn_SW_LOW:
	  src_val =3D 6;
	  break;
	case conn_SW_HIGH:
	  src_val =3D 6;
	  break;
	case conn_EXT_GATE0:
	  src_val =3D 7;
	  break;
	default:
	  err =3D err_connect_dst;
	  return err;
	}
      break;
    case conn_GATE1:
      switch (src)
	{
	case conn_IO_CHAN0:
	  src_val =3D 0;
	  break;
	case conn_IO_CHAN1:
	  src_val =3D 1;
	  break;
	case conn_IO_CHAN2:
	  src_val =3D 2;
	  break;
	case conn_IO_CHAN3:
	  src_val =3D 3;
	  break;
	case conn_OUT0:
	  src_val =3D 5;
	  break;
	case conn_OUT2:
	  src_val =3D 4;
	  break;
	case conn_SW_LOW:
	  src_val =3D 6;
	  break;
	case conn_SW_HIGH:
	  src_val =3D 6;
	  break;
	case conn_EXT_GATE1:
	  src_val =3D 7;
	  break;
	default:
	  err =3D err_connect_dst;
	  return err;
	}
      break;
    case conn_GATE2:
      switch (src)
	{
	case conn_IO_CHAN0:
	  src_val =3D 0;
	  break;
	case conn_IO_CHAN1:
	  src_val =3D 1;
	  break;
	case conn_IO_CHAN2:
	  src_val =3D 2;
	  break;
	case conn_IO_CHAN3:
	  src_val =3D 3;
	  break;
	case conn_OUT0:
	  src_val =3D 4;
	  break;
	case conn_OUT1:
	  src_val =3D 5;
	  break;
	case conn_SW_LOW:
	  src_val =3D 6;
	  break;
	case conn_SW_HIGH:
	  src_val =3D 6;
	  break;
	case conn_EXT_GATE2:
	  src_val =3D 7;
	  break;
	default:
	  err =3D err_connect_dst;
	  return err;
	}
      break;
    }

  // handle sw set gate and chan
  switch (src)
    {
    case conn_SW_LOW:
      switch (dst)
	{
	case conn_IO_CHAN0:
	  mux_addr_reg_val =3D 6;
	  outb (mux_addr_reg, mux_addr_reg_val);
	  gt401Unit[unit].mux_control[mux_addr_reg_val] &=3D (~1);
	  outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
	  break;
	case conn_IO_CHAN1:
	  mux_addr_reg_val =3D 6;
	  outb (mux_addr_reg, mux_addr_reg_val);
	  gt401Unit[unit].mux_control[mux_addr_reg_val] &=3D (~2);
	  outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
	  break;
	case conn_IO_CHAN2:
	  mux_addr_reg_val =3D 6;
	  outb (mux_addr_reg, mux_addr_reg_val);
	  gt401Unit[unit].mux_control[mux_addr_reg_val] &=3D (~4);
	  outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
	  break;
	case conn_IO_CHAN3:
	  mux_addr_reg_val =3D 6;
	  outb (mux_addr_reg, mux_addr_reg_val);
	  gt401Unit[unit].mux_control[mux_addr_reg_val] &=3D (~8);
	  outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
	  break;
	case conn_GATE0:
	  mux_addr_reg_val =3D 3;
	  outb (mux_addr_reg, mux_addr_reg_val);
	  gt401Unit[unit].mux_control[mux_addr_reg_val] &=3D (~1);
	  outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
	  break;
	case conn_GATE1:
	  mux_addr_reg_val =3D 3;
	  outb (mux_addr_reg, mux_addr_reg_val);
	  gt401Unit[unit].mux_control[mux_addr_reg_val] &=3D (~2);
	  outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
	  break;
	case conn_GATE2:
	  mux_addr_reg_val =3D 3;
	  outb (mux_addr_reg, mux_addr_reg_val);
	  gt401Unit[unit].mux_control[mux_addr_reg_val] &=3D (~4);
	  outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
	  break;
	}
      break;
    case conn_SW_HIGH:
      switch (dst)
	{
	case conn_IO_CHAN0:
	  mux_addr_reg_val =3D 6;
	  outb (mux_addr_reg, mux_addr_reg_val);
	  gt401Unit[unit].mux_control[mux_addr_reg_val] |=3D 1;
	  outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
	  break;
	case conn_IO_CHAN1:
	  mux_addr_reg_val =3D 6;
	  outb (mux_addr_reg, mux_addr_reg_val);
	  gt401Unit[unit].mux_control[mux_addr_reg_val] |=3D 2;
	  outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
	  break;
	case conn_IO_CHAN2:
	  mux_addr_reg_val =3D 6;
	  outb (mux_addr_reg, mux_addr_reg_val);
	  gt401Unit[unit].mux_control[mux_addr_reg_val] |=3D 4;
	  outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
	  break;
	case conn_IO_CHAN3:
	  mux_addr_reg_val =3D 6;
	  outb (mux_addr_reg, mux_addr_reg_val);
	  gt401Unit[unit].mux_control[mux_addr_reg_val] |=3D 8;
	  outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
	  break;
	case conn_GATE0:
	  mux_addr_reg_val =3D 3;
	  outb (mux_addr_reg, mux_addr_reg_val);
	  gt401Unit[unit].mux_control[mux_addr_reg_val] |=3D 1;
	  outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
	  break;
	case conn_GATE1:
	  mux_addr_reg_val =3D 3;
	  outb (mux_addr_reg, mux_addr_reg_val);
	  gt401Unit[unit].mux_control[mux_addr_reg_val] |=3D 2;
	  outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
	  break;
	case conn_GATE2:
	  mux_addr_reg_val =3D 3;
	  outb (mux_addr_reg, mux_addr_reg_val);
	  gt401Unit[unit].mux_control[mux_addr_reg_val] |=3D 4;
	  outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
	  break;
	}
      break;
    }

  // make the connection

  switch (dst)
    {
    case conn_IO_CHAN0:
      mux_addr_reg_val =3D 4;
      outb (mux_addr_reg, mux_addr_reg_val);
      gt401Unit[unit].mux_control[mux_addr_reg_val] &=3D ~7;
      gt401Unit[unit].mux_control[mux_addr_reg_val] |=3D src_val;
      outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);

      mux_addr_reg_val =3D 7;
      outb (mux_addr_reg, mux_addr_reg_val);
      gt401Unit[unit].mux_control[mux_addr_reg_val] &=3D ~1;
      outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
      break;
    case conn_IO_CHAN1:
      mux_addr_reg_val =3D 4;
      outb (mux_addr_reg, mux_addr_reg_val);
      gt401Unit[unit].mux_control[mux_addr_reg_val] &=3D ~0x38;
      gt401Unit[unit].mux_control[mux_addr_reg_val] |=3D (src_val << 3);
      outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);

      mux_addr_reg_val =3D 7;
      outb (mux_addr_reg, mux_addr_reg_val);
      gt401Unit[unit].mux_control[mux_addr_reg_val] &=3D ~2;
      outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
      break;
    case conn_IO_CHAN2:
      mux_addr_reg_val =3D 5;
      outb (mux_addr_reg, mux_addr_reg_val);
      gt401Unit[unit].mux_control[mux_addr_reg_val] &=3D ~7;
      gt401Unit[unit].mux_control[mux_addr_reg_val] |=3D src_val;
      outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);

      mux_addr_reg_val =3D 7;
      outb (mux_addr_reg, mux_addr_reg_val);
      gt401Unit[unit].mux_control[mux_addr_reg_val] &=3D ~4;
      outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
      break;
    case conn_IO_CHAN3:
      mux_addr_reg_val =3D 5;
      outb (mux_addr_reg, mux_addr_reg_val);
      gt401Unit[unit].mux_control[mux_addr_reg_val] &=3D ~0x38;
      gt401Unit[unit].mux_control[mux_addr_reg_val] |=3D (src_val << 3);
      outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);

      mux_addr_reg_val =3D 7;
      outb (mux_addr_reg, mux_addr_reg_val);
      gt401Unit[unit].mux_control[mux_addr_reg_val] &=3D ~8;
      outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
      break;

    case conn_EXT_CHAN0:
      mux_addr_reg_val =3D 7;
      outb (mux_addr_reg, mux_addr_reg_val);
      gt401Unit[unit].mux_control[mux_addr_reg_val] |=3D 1;
      outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
      break;
    case conn_EXT_CHAN1:
      mux_addr_reg_val =3D 7;
      outb (mux_addr_reg, mux_addr_reg_val);
      gt401Unit[unit].mux_control[mux_addr_reg_val] |=3D 2;
      outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
      break;
    case conn_EXT_CHAN2:
      mux_addr_reg_val =3D 7;
      outb (mux_addr_reg, mux_addr_reg_val);
      gt401Unit[unit].mux_control[mux_addr_reg_val] |=3D 4;
      outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
      break;
    case conn_EXT_CHAN3:
      mux_addr_reg_val =3D 7;
      outb (mux_addr_reg, mux_addr_reg_val);
      gt401Unit[unit].mux_control[mux_addr_reg_val] |=3D 8;
      outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
      break;

    case conn_CLK0:
      mux_addr_reg_val =3D 0;
      outb (mux_addr_reg, mux_addr_reg_val);
      gt401Unit[unit].mux_control[mux_addr_reg_val] &=3D (~7);
      gt401Unit[unit].mux_control[mux_addr_reg_val] |=3D src_val;
      outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
      break;
    case conn_CLK1:
      mux_addr_reg_val =3D 1;
      outb (mux_addr_reg, mux_addr_reg_val);
      gt401Unit[unit].mux_control[mux_addr_reg_val] &=3D (~7);
      gt401Unit[unit].mux_control[mux_addr_reg_val] |=3D src_val;
      outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
      break;
    case conn_CLK2:
      mux_addr_reg_val =3D 2;
      outb (mux_addr_reg, mux_addr_reg_val);
      gt401Unit[unit].mux_control[mux_addr_reg_val] &=3D (~7);
      gt401Unit[unit].mux_control[mux_addr_reg_val] |=3D src_val;
      outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
      break;
    case conn_GATE0:
      mux_addr_reg_val =3D 0;
      outb (mux_addr_reg, mux_addr_reg_val);
      gt401Unit[unit].mux_control[mux_addr_reg_val] &=3D (~0x38);
      gt401Unit[unit].mux_control[mux_addr_reg_val] |=3D (src_val << 3);
      outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
      break;
    case conn_GATE1:
      mux_addr_reg_val =3D 1;
      outb (mux_addr_reg, mux_addr_reg_val);
      gt401Unit[unit].mux_control[mux_addr_reg_val] &=3D (~0x38);
      gt401Unit[unit].mux_control[mux_addr_reg_val] |=3D (src_val << 3);
      outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
      break;
    case conn_GATE2:
      mux_addr_reg_val =3D 2;
      outb (mux_addr_reg, mux_addr_reg_val);
      gt401Unit[unit].mux_control[mux_addr_reg_val] &=3D (~0x38);
      gt401Unit[unit].mux_control[mux_addr_reg_val] |=3D (src_val << 3);
      outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
      break;
    }

  return err;
}

static int
pulse_out (int unit,
	   int enable,
	   int src)
{
  int       base_io_port;
  u_short   cont_reg1;
  int       err;
  u_char    src_code;

  base_io_port =3D devices[unit].id_iobase;
  err =3D err_success;

  cont_reg1 =3D base_io_port + io_addr_cont_reg_1;
  if (enable =3D=3D 0)
    { /* disable pulse output */
      gt401Unit[unit].cont_reg1 &=3D (~cont_reg1_pulse_en);
      outb (cont_reg1, gt401Unit[unit].cont_reg1);
      return err;
    }

  // enable pulse output
  switch (src)
    {
    case conn_IO_CHAN0:
      src_code =3D 0;
      break;
    case conn_IO_CHAN1:
      src_code =3D 1;
      break;
    case conn_IO_CHAN2:
      src_code =3D 2;
      break;
    case conn_IO_CHAN3:
      src_code =3D 3;
      break;
    case conn_SW_LOW:
    case conn_SW_HIGH:
      src_code =3D 4;
      break;
    case conn_OUT0:
      src_code =3D 5;
      break;
    case conn_OUT1:
      src_code =3D 6;
      break;
    case conn_OUT2:
      src_code =3D 7;
      break;
    default:
      err =3D err_connect_src;
      return err;
    }

  gt401Unit[unit].cont_reg1 &=3D cont_reg1_src_mask;
  gt401Unit[unit].cont_reg1 |=3D src_code;

  switch (src)
    {
    case conn_SW_LOW:
      gt401Unit[unit].cont_reg1 &=3D (~cont_reg1_sw_pulse);
      break;
    case conn_SW_HIGH:
      gt401Unit[unit].cont_reg1 |=3D cont_reg1_sw_pulse;
      break;
    }

  outb (cont_reg1,
	gt401Unit[unit].cont_reg1);

  tsleep (&err, PZERO, "gt401", 100);

  gt401Unit[unit].cont_reg1 |=3D cont_reg1_pulse_en; // then enable pulse=
 out

  outb (cont_reg1,
	gt401Unit[unit].cont_reg1);

  return err;
}

static int
set_irq (int unit,
	 int enable,
	 int src,
	 int edge)
{
  int       base_io_port;
  int       err;
  int       io_cont_reg2;
  int       src_code;

  base_io_port =3D devices[unit].id_iobase;
  err =3D err_success;

  io_cont_reg2 =3D base_io_port + io_addr_cont_reg_2;

  if (enable =3D=3D 0)
    { /* turn off interrupts */
      gt401Unit[unit].cont_reg2 &=3D ~(cont_reg2_intr_en_bit);
#if 0 /* debug */
      printf ("outb (%x,%x)\n",
	      io_cont_reg2,
	      gt401Unit[unit].cont_reg2);
#endif
      outb (io_cont_reg2,
	    gt401Unit[unit].cont_reg2);
      return err;
    } /* turn off interrupts */

  /* turn on interrupts */
  switch (src)
    {
    case conn_IO_CHAN0:
      src_code =3D cont_reg2_irq_src_chan0;
      break;
    case conn_IO_CHAN1:
      src_code =3D cont_reg2_irq_src_chan1;
      break;
    case conn_IO_CHAN2:
      src_code =3D cont_reg2_irq_src_chan2;
      break;
    case conn_IO_CHAN3:
      src_code =3D cont_reg2_irq_src_chan3;
      break;
    case conn_OUT0:
      src_code =3D cont_reg2_irq_src_out0;
      break;
    case conn_OUT1:
      src_code =3D cont_reg2_irq_src_out1;
      break;
    case conn_OUT2:
      src_code =3D cont_reg2_irq_src_out2;
      break;
    default:
      err =3D err_irq_src;
      return err;
    }
  gt401Unit[unit].cont_reg2 &=3D ~(cont_reg2_irq_src_mask);
  gt401Unit[unit].cont_reg2 |=3D src_code;

  if (edge =3D=3D 0)
    gt401Unit[unit].cont_reg2 &=3D ~(cont_reg2_intr_edge_bit);
  else
    gt401Unit[unit].cont_reg2 |=3D cont_reg2_intr_edge_bit;

  gt401Unit[unit].cont_reg2 &=3D ~(cont_reg2_intr_en_bit);
#if 0 /* debug */
      printf ("outb (%x,%x)\n",
	      io_cont_reg2,
	      gt401Unit[unit].cont_reg2);
#endif
  outb (io_cont_reg2,
	gt401Unit[unit].cont_reg2);

  gt401Unit[unit].cont_reg2 |=3D cont_reg2_intr_en_bit;
#if 0 /* debug */
      printf ("outb (%x,%x)\n",
	      io_cont_reg2,
	      gt401Unit[unit].cont_reg2);
#endif
  outb (io_cont_reg2,
	gt401Unit[unit].cont_reg2);

  return err;
}

/*
 * Register the appropriate INTerrupt.
 */
static int
setupIntr(struct isa_device* device,
          int irq, inthand2_t* hand, int unit, u_int* maskp)
{
    u_int mask;
    u_int flags;

    mask =3D 1ul << irq;
    flags =3D device->id_ri_flags;

    if (maskp)
        INTRMASK(*maskp, mask);

    if (register_intr(irq, 0, flags, hand, maskp, unit) =3D=3D 0) {
        device->id_irq =3D mask;
        INTREN(mask);
        return (irq);
    }

    if (maskp)
        INTRUNMASK(*maskp, mask);

    return (-1);
}


/*
 * Unregister the appropriate INTerrupt.
 */
static int
removeIntr(int irq)
{
    if (unregister_intr(irq, intr_handler[irq])) {
        uprintf("\tINT #%d failed to unregister\n", irq);
        return (EPERM);
    }
    else {
        INTRDIS(1 << irq);
        uprintf("\tINT #%d unregistered\n", irq);
        return (0);
    }
}

static int
send_cmd (int unit, u_char *cmd_buf, u_int size)
{
  int         base_io_port;
  u_short     cmd_ready_reg;
  u_short     dual_port_bank_index;
  u_short     dual_port_bank_reg;
  u_short     dual_port_data_reg;
  int         err;
  int         ii;
  int         jj;
  u_short     status_reg;
  u_char      status_val;
  int         ready;
  int         ready_loop_max =3D 5000;

  err =3D err_success;

  base_io_port =3D devices[unit].id_iobase;

  // Wait for command ready bit
  status_reg =3D base_io_port + io_addr_status_reg;
  ready =3D 0;
  for (ii=3D0; ii<cmd_poll_sleep_loop; ++ii)
    {
      for (jj=3D0; jj<cmd_poll_inner_loop; ++jj)
	{
	  if (jj > ready_loop_max)
	    break;
	  status_val =3D inb(status_reg);
	  if ((status_val & status_reg_ready_for_cmd) !=3D 0)
	    {
	      ready =3D 1;
	      break;
	    }
	}
      if (ready)
	break;
    }
  if (!ready)
    {
      err =3D err_timeout;
      return err;
    }
  if (size =3D=3D 0)
    {
      return err;
    }
  if (size > cmd_buf_sz)
    {
      err =3D err_cmd_sz;
      return err;
    }

  // place command bytes into dual port ram
  dual_port_bank_reg =3D base_io_port + io_addr_ram_addr_reg;
  dual_port_bank_index =3D dp_ram_command_first_page;
  for (ii=3D0; ii<size; )
    {
      dual_port_data_reg =3D base_io_port + io_addr_dp_ram;
      outb(dual_port_bank_reg, dual_port_bank_index);

      for (jj=3D0; jj<8; ++jj)
	{ // 8 bytes in each bank
	  outb(dual_port_data_reg, cmd_buf[ii]);
	  ++ii;
	  if (ii >=3D size)
	    break;
	  ++dual_port_data_reg;
	}
      if (ii >=3D size)
	break;
      ++dual_port_bank_index;
    }

  // signal command
  cmd_ready_reg =3D base_io_port + io_addr_cmd_ready;
  outb(cmd_ready_reg, 0);

  return err;
}

static int
set_8254 (int unit,
	  u_short counter,
	  u_short mode,
	  u_short initial_count)
{
  u_short   addr54;
  int       base_io_port;
  u_char    control_byte;
  int       err;
  u_char    ic_hi;
  u_char    ic_lo;
  u_short   i8254_data_reg;
  u_short   mux_addr_reg;

  err =3D err_success;

  base_io_port =3D devices[unit].id_iobase;

  switch (counter)
    {
    case 0:
      addr54 =3D mux_54_ctr_0;
      break;
    case 1:
      addr54 =3D mux_54_ctr_1;
      break;
    case 2:
      addr54 =3D mux_54_ctr_2;
      break;
    default:
      err =3D err_invalid_8254_ctr;
      return err;
    }

  mux_addr_reg =3D base_io_port + io_addr_mux_addr_reg;
  i8254_data_reg =3D base_io_port + io_addr_8254_data_reg;

  control_byte =3D 0;
  control_byte |=3D ((counter & 3) << 6);
  control_byte |=3D (3 << 4);
  control_byte |=3D ((mode & 7) << 1);
  // BCD bit is 0, meaning counter it 16 bit unsigned binary

  outb(mux_addr_reg, mux_54_cont); // map in 8254 control register
  outb(i8254_data_reg, control_byte);
  outb(mux_addr_reg, addr54);      // map in 8254 counter register.

  ic_lo =3D initial_count & 0xff;
  ic_hi =3D (initial_count >> 8) & 0xff;

  outb(i8254_data_reg, ic_lo);
  outb(i8254_data_reg, ic_hi);

  return err;
}

static int start_tt (int unit)
{
  u_char cmd_buf[8] =3D {0};
  int    istat;

  cmd_buf[0] =3D cmd_clr_err;
  send_cmd(unit,cmd_buf,1);
  istat =3D get_status(unit);
  printf ("start_tt after clr_err stat=3D%d\n",istat);

  cmd_buf[0] =3D cmd_tt_size;
  cmd_buf[1] =3D 12;
  cmd_buf[2] =3D 0;   /* no wrap */
  send_cmd(unit, cmd_buf, 3);
  istat =3D get_status(unit);
  printf ("start_tt tt_size stat=3D%d\n",istat);

  cmd_buf[0] =3D cmd_tt;
  cmd_buf[1] =3D tt_edge_pos; /* chan=3D0 << 6 | edge */
  send_cmd(unit, cmd_buf, 2);
  istat =3D get_status(unit);
  printf ("start_tt tt stat=3D%d\n",istat);

  return 0;
}

static int stop_tt (int unit)
{
  u_char cmd_buf[8] =3D {0};
  int istat;

  cmd_buf[0] =3D cmd_clr_err;
  send_cmd(unit,cmd_buf,1);
  istat =3D get_status(unit);
  printf ("stop_tt stat after cmd_clr_err=3D%d\n",istat);

  cmd_buf[0] =3D cmd_chan_off;
  cmd_buf[1] =3D 0xc0;
  send_cmd(unit, cmd_buf, 2);
  istat =3D get_status(unit);
  printf ("stop_tt stat after cmd_chan_off=3D%d\n",istat);

#if 0
  cmd_buf[0] =3D cmd_clr_tt;
  cmd_buf[1] =3D 0;     /* clear time tag buffer and time tag counter */
  send_cmd(unit, cmd_buf, 2);
  istat =3D get_status(unit);
  printf ("stop_tt stat after cmd_clr_tt=3D%d\n",istat);
#endif

  return 0;
}

static int get_tt_count (int unit)
{
  return 0;
}

static int get_tt (int unit, long long *tt)
{
  int        base_io_port;
  int        ram_addr_reg;
  int        next_tt;

  send_cmd(unit,NULL,0);
  base_io_port =3D devices[unit].id_iobase;

  ram_addr_reg =3D base_io_port + io_addr_ram_addr_reg;
  outb (ram_addr_reg, dp_ram_status_page);

  {
    int ii;
    printf ("status page : ");
    for (ii=3D0; ii<8; ++ii)
      {
	next_tt =3D inb(base_io_port + 8 + ii);
	printf (" 0x%x");
      }
    printf ("\n");
  }

  {
    int ii;
    printf ("tag buf :");
    outb (ram_addr_reg, 156);
    for (ii=3D0; ii<8; ++ii)
      {
	next_tt =3D inb(base_io_port + 8 + ii);
	printf (" 0x%x");
      }
    outb (ram_addr_reg, 157);
    for (ii=3D0; ii<4; ++ii)
      {
	next_tt =3D inb(base_io_port + 8 + ii);
	printf (" 0x%x");
      }
    printf ("\n");
  }

  return 0;
}

static int get_status(int unit)
{
  int        base_io_port;
  int        ram_addr_reg;
  int        status_reg;
  int        istat;

  base_io_port =3D devices[unit].id_iobase;
  ram_addr_reg =3D base_io_port + io_addr_ram_addr_reg;

  send_cmd(unit,NULL,0);

  outb (ram_addr_reg, dp_ram_status_page);

  status_reg =3D base_io_port + err_reg;
  istat =3D inb(status_reg) & 0xff;

  return istat;
}


--==_Exmh_18363059200
Content-Type: text/plain ; name="log_read_counts_980726.c"; charset=us-ascii
Content-Description: log_read_counts_980726.c
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment; filename="log_read_counts_980726.c"

#if 0
cc -o test04 test04.c

Experiment with /dev/gt401 not doing time tags
Run continuously
#endif

#include <sys/types.h>
#include <sys/rtprio.h>
#include <stdio.h>

#include <fcntl.h>
#include "gt401.h"

#define DEV_PATH "/dev/gt401"
int fd =3D -1;

int arg_ioctl =3D 0;

int read_count =3D 0;

#define MAX_TTAG_BUF (4*1024)

main (int argc, char **argv)
{
  int istat;
  int ii;
  gt401_read * ttag;
  gt401_read ttag_buf[MAX_TTAG_BUF], last_ttag;
  int log_mod;
  double period_d;
  double freq_d;
  int first_flag =3D 1;
  int read_req =3D (sizeof(gt401_read) * MAX_TTAG_BUF);
  int read_data_count;
  int max_read_data_count =3D 0;

  long long sample_period =3D -1;
  long long sample_period_hi;
  long long sample_period_low;
  int read_dropped =3D 0;
  int driver_dropped =3D 0;

  long long index_diff_driver;
  long long index_diff_read_error;
  long long calc_usec_diff_driver;
  long long calc_index_diff_driver;
  long long index_diff_driver_error;

  struct rtprio rtp;
  int rtp_flag =3D 0;

#if 1
  rtp_flag =3D 1;
  rtp.type =3D RTP_PRIO_REALTIME;
  rtp.prio =3D 0;
  istat =3D rtprio (RTP_SET,
		  0,
		  &rtp);
  if (istat !=3D 0)
    {
      perror("rtprio");
      exit (1);
    }
  fprintf (stderr,"rtprio\n");
#else
  fprintf (stderr, "No rtprio\n");
#endif

  fd =3D open (DEV_PATH, O_RDONLY, 0);
  if (fd < 0)
    {
      fprintf(stderr,"open of %s failed\n",DEV_PATH);
      exit (__LINE__);
    }

  arg_ioctl =3D 20;

  period_d =3D ((double)arg_ioctl) / 10000.0;
  freq_d =3D 1.0 / period_d;
  log_mod =3D (int)(freq_d * 30.0);

  sample_period =3D (long long)(period_d * 1e6);
  sample_period_low =3D sample_period - (sample_period / 8);
  sample_period_hi =3D sample_period + (sample_period / 8);

  fprintf (stderr,"periodX10000=3D%d period=3D%f freq=3D%f\n",arg_ioctl,p=
eriod_d,freq_d);
  fprintf (stderr,"sizeof(gt401_read) =3D %d\n",sizeof(gt401_read));

  for (ii=3D1; ii<argc; ++ii)
    {
      printf ("# %s\n",argv[ii]); /* gnuplot comment line */
    }
  printf ("# period=3D%f freq=3D%f",
	  period_d, freq_d);
  if (rtp_flag)
    printf(" RTPRIO");
  else
    printf(" NO RTPRIO");
  printf ("\n");

  istat =3D ioctl(fd,GT401_START_EVENTS,&arg_ioctl);

  for (;;)
    {
      istat =3D read (fd,(void *)&(ttag_buf[0]),read_req);
      if (istat <=3D 0)
	{
	  fprintf (stderr,"read fail istat=3D%d\n",istat);
	  perror("read fail");
	  exit (__LINE__);
	}
      read_data_count =3D istat / sizeof(gt401_read);
      printf ("%d\n",read_data_count);
      fflush(stdout);
    } /* for (;;) */

  istat =3D ioctl(fd,GT401_STOP_EVENTS,&arg_ioctl);

  return (0);
}

--==_Exmh_18363059200
Content-Type: text/plain; name="log_read_counts_980726.c"; charset=us-ascii
Content-Description: log_read_counts_980726.c
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment; filename="log_read_counts_980726.c"

#if 0
cc -o test04 test04.c

Experiment with /dev/gt401 not doing time tags
Run continuously
#endif

#include <sys/types.h>
#include <sys/rtprio.h>
#include <stdio.h>

#include <fcntl.h>
#include "gt401.h"

#define DEV_PATH "/dev/gt401"
int fd =3D -1;

int arg_ioctl =3D 0;

int read_count =3D 0;

#define MAX_TTAG_BUF (4*1024)

main (int argc, char **argv)
{
  int istat;
  int ii;
  gt401_read * ttag;
  gt401_read ttag_buf[MAX_TTAG_BUF], last_ttag;
  int log_mod;
  double period_d;
  double freq_d;
  int first_flag =3D 1;
  int read_req =3D (sizeof(gt401_read) * MAX_TTAG_BUF);
  int read_data_count;
  int max_read_data_count =3D 0;

  long long sample_period =3D -1;
  long long sample_period_hi;
  long long sample_period_low;
  int read_dropped =3D 0;
  int driver_dropped =3D 0;

  long long index_diff_driver;
  long long index_diff_read_error;
  long long calc_usec_diff_driver;
  long long calc_index_diff_driver;
  long long index_diff_driver_error;

  struct rtprio rtp;
  int rtp_flag =3D 0;

#if 1
  rtp_flag =3D 1;
  rtp.type =3D RTP_PRIO_REALTIME;
  rtp.prio =3D 0;
  istat =3D rtprio (RTP_SET,
		  0,
		  &rtp);
  if (istat !=3D 0)
    {
      perror("rtprio");
      exit (1);
    }
  fprintf (stderr,"rtprio\n");
#else
  fprintf (stderr, "No rtprio\n");
#endif

  fd =3D open (DEV_PATH, O_RDONLY, 0);
  if (fd < 0)
    {
      fprintf(stderr,"open of %s failed\n",DEV_PATH);
      exit (__LINE__);
    }

  arg_ioctl =3D 20;

  period_d =3D ((double)arg_ioctl) / 10000.0;
  freq_d =3D 1.0 / period_d;
  log_mod =3D (int)(freq_d * 30.0);

  sample_period =3D (long long)(period_d * 1e6);
  sample_period_low =3D sample_period - (sample_period / 8);
  sample_period_hi =3D sample_period + (sample_period / 8);

  fprintf (stderr,"periodX10000=3D%d period=3D%f freq=3D%f\n",arg_ioctl,p=
eriod_d,freq_d);
  fprintf (stderr,"sizeof(gt401_read) =3D %d\n",sizeof(gt401_read));

  for (ii=3D1; ii<argc; ++ii)
    {
      printf ("# %s\n",argv[ii]); /* gnuplot comment line */
    }
  printf ("# period=3D%f freq=3D%f",
	  period_d, freq_d);
  if (rtp_flag)
    printf(" RTPRIO");
  else
    printf(" NO RTPRIO");
  printf ("\n");

  istat =3D ioctl(fd,GT401_START_EVENTS,&arg_ioctl);

  for (;;)
    {
      istat =3D read (fd,(void *)&(ttag_buf[0]),read_req);
      if (istat <=3D 0)
	{
	  fprintf (stderr,"read fail istat=3D%d\n",istat);
	  perror("read fail");
	  exit (__LINE__);
	}
      read_data_count =3D istat / sizeof(gt401_read);
      printf ("%d\n",read_data_count);
      fflush(stdout);
    } /* for (;;) */

  istat =3D ioctl(fd,GT401_STOP_EVENTS,&arg_ioctl);

  return (0);
}

--==_Exmh_18363059200--



To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-hackers" in the body of the message



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199807312131.PAA04940>