Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 21 Nov 1997 21:40:12 -0800 (PST)
From:      jamil@trojanhorse.ml.org
To:        FreeBSD-gnats-submit@FreeBSD.ORG
Subject:   kern/5123: New Driver: Industrial Computer Source AIO8-P (alog)
Message-ID:  <199711220540.VAA01633@trojanhorse.ml.org>
Resent-Message-ID: <199711220550.VAA14315@hub.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         5123
>Category:       kern
>Synopsis:       New Driver: Industrial Computer Source AIO8-P (alog)
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Fri Nov 21 21:50:01 PST 1997
>Last-Modified:
>Originator:     Jamil J. Weatherbee
>Organization:
>Release:        FreeBSD 2.2.5-STABLE i386
>Environment:

i386 FreeBSD, ISA Bus, AIO8-P Card and Optionally AT16-P muxes

>Description:

Solve the lack of decent support for low cost analog acquisition cards
under freebsd.  This driver operates very similarily to the labpc driver
with the exception of: 1) supports non-blocking i/o   2) support of
external muxes (up to 128 channels at about $26/channel)  3) software
fifos  4) select call support  5) select "block" triggering enhancement to
make multichannel high frequency acquisition cpu efficient 6) 12 bit
channels

The biggest limit is the lack of hardware fifo, frequencies are generally
limited to around 3500 samples/sec without consuming excessive interrupt
time.  Operation to 15000 samples/sec has been observed.


>How-To-Repeat:

See Above.

>Fix:
	
# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	Makefile
#	alog.4
#	alog.c
#	alogio.h
#
echo x - Makefile
sed 's/^X//' >Makefile << 'END-of-Makefile'
X# Make file to install the alog driver files in their proper places
X
Xinstall:
X	install -c -o root -g wheel -m 0644 alog.c /sys/i386/isa
X	install -c -o root -g bin -m 0644 alogio.h /sys/sys
X	install -c -o bin -g bin -m 0444 alogio.h /usr/include/sys
X	install -c -o bin -g bin -m 444 alog.4 /usr/share/man/man4/
X	gzip -f /usr/share/man/man4/alog.4
X	install -c -o bin -g bin -m 444 alog.4 /usr/share/man/man4/i386/
X	gzip -f /usr/share/man/man4/i386/alog.4
END-of-Makefile
echo x - alog.4
sed 's/^X//' >alog.4 << 'END-of-alog.4'
X.\"
X.\" Copyright (c) 1997 Jamil J. Weatherbee
X.\" All rights reserved.
X.\"
X.\" 
X.\" Redistribution and use in source and binary forms, with or without
X.\" modification, are permitted provided that the following conditions
X.\" are met:
X.\" 1. Redistributions of source code must retain the above copyright
X.\"    notice, this list of conditions and the following disclaimer
X.\"    in this position and unchanged.
X.\" 2. Redistributions in binary form must reproduce the above copyright
X.\"    notice, this list of conditions and the following disclaimer in the
X.\"    documentation and/or other materials provided with the distribution.
X.\" 3. The name of the author may not be used to endorse or promote products
X.\"    derived from this software without specific prior written permission
X.\" 
X.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
X.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
X.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
X.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
X.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
X.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
X.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
X.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
X.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
X.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
X.\"
X.\" Industrial Computer Source model AIO8-P
X.\" 8 channel, moderate speed analog to digital converter board with
X.\" 128 channel MUX capability via daisy-chained AT-16P units
X.\" alog.c, character device driver, last revised November 19 1997
X.\" See http://www.indcompsrc.com/products/data/html/aio8g-p.html
X.\"
X.\" Written by: Jamil J. Weatherbee <jamil@trojanhorse.ml.org>
X.\"
X.\"
X.Dd November 19, 1997
X.Dt ALOG 4 i386
X.Os FreeBSD
X.Sh NAME
X.Nm alog
X.Nd
XIndustrial Computer Source AIO8-P driver
X.Sh SYNOPSIS
X.Cd "device alog0 at isa? port 0x260 tty irq 5 vector alogintr"
X.Sh DESCRIPTION
XThis driver supports the Industrial Computer Source \fIAIO8-P 8-Channel
X12-Bit Analog Input board\fP.
X.Pp
XThis board provides 8 12 bit, single-ended analog input ports.
XThe driver also directly provides support for up to 8 daisy chained 
X\fIAT16-P Programmable Analog Multiplexers with 16 Differential Inputs\fP.
XThis makes it possible to sample up to 128 differential channels with a single
Xinterface board.  
XUse of at least one \fIAT16-P\fP is highly recommended as the \fIAIO8-P\fP
Xoffers no signal conditioning options and only operates in a -5 to +5 Volt
Xinput range.  However, if you wish to use the \fIAIO8-P\fP standalone,
Xinsert the following into your kernel
X.Xr config 8
Xfile:
X.Bd -literal -offset indent
Xoptions  ALOG_CHANNELS=8
X.Ed
X.Pp
XSelection of the input port is through the minor number:
X.Pp
X.Bd -literal -offset indent
XThe 8 bit minor number format is UCCCCMMM, where
X   U: board unit (0-1)
XCCCC: external multiplexer channel (0-15) (on AT-16P units) 
X MMM: internal multiplexer channel (0-7) (on AIO8-P card)
X.Ed
X.Pp
X.Xr devfs 5
Xdevice node names are of the form: alog[0-1][a-p][0-7] 
X.Pp
X.Sh IOCTL
XThe following
X.Xr ioctl 2
Xcalls apply to
X.Nm
Xdevices.  Their declaration can be found in the header files
X.Pa <sys/alogio.h>
Xand
X.Pa <sys/dataacq.h>
X.Bl -tag -width AD_MICRO_PERIOD_SET
X.It Dv AD_MICRO_PERIOD_SET
XTakes a pointer to a long argument specifying the number of microseconds
Xbetween samples.  Half of this is used as the external multiplexer
Xsettling time and the other half as conversion time.
X.It Dv AD_MICRO_PERIOD_GET
XTakes a pointer to a long argument and returns the current number of
Xmicroseconds between samples.
X.It Dv AD_NCHANS_GET
XTakes a pointer to an integer and returns the number of channels the board
Xsupports.  This should be 8 for a standalone \fIAIO8-P\fP or 128 for any
Xother setup.
X.It Dv AD_FIFOSIZE_GET
XTakes a pointer to an integer and returns the size of the fifo in
Xentries.  The compile time option ALOG_FIFOSIZE is by default set to 64.
X.It Dv AD_FIFO_TRIGGER_GET
XTakes a pointer to an integer and returns the minimum number of entries a
Xfifo must contain to cause
X.Xr select 2
Xto return.  This by default is set to 1.
X.It Dv AD_FIFO_TRIGGER_SET
XTakes a pointer to an integer specifying the minimum number of entries a
Xfifo must contain to cause
X.Xr select 2
Xto return.
X.It Dv AD_START
XStarts the clocked accumulation of sample values into a channel's driver fifo.
XWhen a channel is first opened its software fifo is initialized in the
Xstopped state.  This is to prevent high sample clocks from overrunning the
Xfifos before the user is ready to read from the channel.  The driver
Xautomatically performs an AD_START when the user issues the first read,
Xexcept for channels opened with the O_NONBLOCK flag which must explicitly
Xhave an AD_START issued.
X.It Dv AD_STOP
XStops the clocked accumulation of sample values into a channel's driver fifo.
X.Sh BUGS
XOn the \fIAIO8-P\fP, interrupt driven conversion (the only type
Xsupported by the
X.Nm
Xdriver) is facilitated through 8253 timer #2.  In order for interrrupts to
Xbe generated you must connect line 6 to line 24 (counter 2 output to
Xinterrupt input) and line 23 to line 29 (counter 2 gate to +5VDC).
XThe design of the \fIAIO8-P\fP precludes the use of programmable
Xgain control.    
X.Pp
XUse the combination of non-blocking i/o, 
X.Xr select 2
Xand a custom fifo trigger
Xwherever possible, especially with large numbers of open channels.
XUsing this method, multichannel sample rates as high as 16,000 samples/sec
Xhave been observed.
X.Pp
XSample rates lower than 32 Hz are not supported.
X.Sh SEE ALSO
X.Bd -literal
Xhttp://www.indcompsrc.com/products/data/html/aio8g-p.html
Xhttp://www.indcompsrc.com/products/data/html/at16-p.html
X.Ed
X.Sh AUTHOR
XJamil J. Weatherbee <jamil@trojanhorse.ml.org>.
END-of-alog.4
echo x - alog.c
sed 's/^X//' >alog.c << 'END-of-alog.c'
X/*
X * Copyright (c) 1997 Jamil J. Weatherbee
X * All rights reserved.
X *
X * 
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer
X *    in this position and unchanged.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. The name of the author may not be used to endorse or promote products
X *    derived from this software without specific prior written permission
X * 
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
X * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
X * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
X * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
X * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
X * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
X * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
X * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
X * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
X *
X * Industrial Computer Source model AIO8-P
X * 8 channel, moderate speed analog to digital converter board with
X * 128 channel MUX capability via daisy chained AT-16P units
X * alog.c, character device driver, last revised November 21 1997
X * See http://www.indcompsrc.com/products/data/html/aio8g-p.html
X *     http://www.indcompsrc.com/products/data/html/at16-p.html
X *
X * Written by: Jamil J. Weatherbee <jamil@trojanhorse.ml.org>
X *
X */
X
X
X/* Include Files */
X
X#include "alog.h"
X#if NALOG > 0
X
X#include <sys/param.h> 
X#include <sys/systm.h>
X#include <sys/proc.h>
X#include <sys/kernel.h>
X#include <sys/conf.h>
X#include <sys/fcntl.h>
X#include <sys/malloc.h>
X#include <sys/buf.h> 
X#include <sys/vnode.h>
X#include <sys/filio.h>
X#include <machine/clock.h>
X#include <i386/isa/isa.h>
X#include <i386/isa/isa_device.h>
X#include <sys/alogio.h>
X#include <sys/dataacq.h>
X
X#ifdef DEVFS
X#include <sys/devfsext.h>
X#endif 
X
X/* Local Defines */
X
X/* Tests have shown that increasing the fifo size 
X * beyond 64 entries for this particular piece of hardware is
X * unproductive */
X
X#ifdef ALOG_FIFOSIZE
X#define FIFOSIZE ALOG_FIFOSIZE
X#else
X#define FIFOSIZE 64
X#endif
X
X#ifdef ALOG_FIFO_TRIGGER
X#define DEFAULT_FIFO_TRIGGER ALOG_FIFO_TRIGGER
X#else
X#define DEFAULT_FIFO_TRIGGER 1
X#endif
X
X#ifdef ALOG_CHANNELS 
X#define NUMCHANNELS ALOG_CHANNELS
X#else
X#define NUMCHANNELS 128
X#endif
X
X#ifdef ALOG_TIMO
X#define READTIMO ALOG_TIMO
X#else
X#define READTIMO (MAX_MICRO_PERIOD*NUMCHANNELS/500000*hz)
X#endif
X
X#define CDEV_MAJOR 86
X#define NUMPORTS 8
X#define MAXUNITS 2
X#define NUMIMUXES 8
X
X#define ADLOW 0x0
X#define ADHIGH 0x1
X#define STATUS 0x2
X#define CNTR0 0x4
X#define CNTR1 0x5
X#define CNTR2 0x6
X#define CNTRCNTRL 0x7
X
X#define DEVFORMAT "alog%d%c%d"
X#define CLOCK2FREQ 4.165
X#define MIN_MICRO_PERIOD 25  
X#define MAX_MICRO_PERIOD (65535/CLOCK2FREQ*PRIMARY_STATES)
X#define DEFAULT_MICRO_PERIOD MAX_MICRO_PERIOD
X#define READMAXTRIG 0.75*FIFOSIZE
X#define ALOGPRI PRIBIO
X#define ALOGMSG "alogio"
X
X#define PRIMARY_STATES 2  /* Setup and conversion are clock tick consuming */
X#define STATE_SETUP 0
X#define STATE_CONVERT 1
X#define STATE_READ 2
X
X/* Notes on interrupt driven A/D conversion:
X * On the AIO8-P, interrupt driven conversion (the only type supported by this
X * driver) is facilitated through 8253 timer #2.  In order for interrrupts to
X * be generated you must connect line 6 to line 24 (counter 2 output to 
X * interrupt input) and line 23 to line 29 (counter 2 gate to +5VDC). 
X * Due to the design of the AIO8-P this precludes the use of programmable 
X * gain control.
X */
X
X/* mode bits for the status register */
X
X#define EOC 0x80 
X#define IEN 0x08  
X#define IMUXMASK 0x07
X#define EMUXMASK 0xf0
X
X/* mode bits for counter controller */
X
X#define LD2MODE4 0xb8
X
X/* Minor allocations:
X * UCCCCMMM
X * U: board unit (0-1)
X * CCCC: external multiplexer channel (0-15) (on AT-16P units)
X * MMM: internal multiplexer channel (0-7) (on AIO8-P card)
X */
X
X#define UNIT(dev) ((minor(dev) & 0x80) >> 7)
X#define CHANNEL(dev) (minor(dev) & 0x7f) 
X#define EMUX(chan) ((chan & 0x78) >> 3)
X#define EMUXMAKE(chan) ((chan & 0x78) << 1)
X#define IMUX(chan) (chan & 0x07)
X#define LMINOR(unit, chan) ((unit << 7)+chan)
X
X/* port statuses */
X
X#define STATUS_UNUSED 0 
X#define STATUS_INUSE 1
X#define STATUS_STOPPED 2
X#define STATUS_INIT 3
X
X/* Type definitions */
X
Xtypedef struct
X{
X  short status; /* the status of this chan */
X  struct selinfo readselect; /* the select() polling info */
X  u_short fifo[FIFOSIZE]; /* fifo for this chan */
X  int fifostart, fifoend; /* the ptrs showing where info is stored in fifo */
X  int fifosize, fifotrig; /* the current and trigger size of the fifo */
X  void *devfs_token; /* the devfs token for this chan */
X  int nextchan;  
X} talog_chan;
X
Xtypedef struct 
X{ 
X  struct isa_device *isaunit; /* ptr to isa device information */     
X  talog_chan chan[NUMCHANNELS]; /* the device nodes */
X  int curchan; /* the current chan being intr handled */
X  int firstchan; /* the first chan to go to in list */
X  int state; /* is the node in setup or convert mode */ 
X  long microperiod; /* current microsecond period setting */ 
X  u_char perlo, perhi; /* current values to send to clock 2 after every intr */
X   
X} talog_unit;
X
X/* Function Prototypes */
X
Xstatic int alog_probe (struct isa_device *idp);  /* Check for alog board */
Xstatic int alog_attach (struct isa_device *idp);  /* Take alog board */
Xstatic int sync_clock2 (int unit, long period); /* setup clock 2 period */
Xstatic __inline int putfifo (talog_chan *pchan, u_short fifoent);
Xstatic int alog_open (dev_t dev, int oflags, int devtype, struct proc *p);
Xstatic int alog_close (dev_t dev, int fflag, int devtype, struct proc *p);
Xstatic int alog_ioctl (dev_t dev, int cmd, caddr_t data,
X		        int fflag, struct proc *p);
Xstatic int alog_read (dev_t dev, struct uio *uio, int ioflag);
Xstatic int alog_select (dev_t dev, int which, struct proc *p);
X
X/* Global Data */
X
Xstatic int alog_devsw_installed = 0;  /* Protect against reinit multiunit */
Xstatic talog_unit *alog_unit[NALOG]; /* data structs for each unit */ 
X
X/* Character device switching structure */
Xstatic struct cdevsw alog_cdevsw = { alog_open, alog_close, alog_read,
X                                     nowrite, alog_ioctl, nostop, noreset,
X                                     nodevtotty, alog_select, nommap,
X                                     nostrategy, "alog", NULL, -1 };
X
X/* Structure expected to tell how to probe and attach the driver
X * Must be published externally (cannot be static) */
Xstruct isa_driver alogdriver = { alog_probe, alog_attach, "alog", 0 };
X
X
X/* handle the ioctls */
Xstatic int alog_ioctl (dev_t dev, int cmd, caddr_t data,
X		        int fflag, struct proc *p)
X{
X  int unit = UNIT(dev);
X  int chan = CHANNEL(dev);
X  talog_unit *info = alog_unit[unit];
X  int s;
X   
X  switch (cmd)
X   {  
X    case FIONBIO: return 0; /* this allows for non-blocking ioctls */ 
X
X    case AD_NCHANS_GET: *(int *)data = NUMCHANNELS;
X                        return 0;
X    case AD_FIFOSIZE_GET: *(int *)data = FIFOSIZE;
X                          return 0;
X      
X    case AD_FIFO_TRIGGER_GET: s = spltty();
X	                      *(int *)data = info->chan[chan].fifotrig;
X	                      splx(s);
X                              return 0;
X      
X    case AD_FIFO_TRIGGER_SET: 
X                  s = spltty();
X                  if ((*(int *)data < 1) || (*(int *)data > FIFOSIZE))
X	           {   
X	             splx(s);
X		     return EPERM; 
X		   }
X                  info->chan[chan].fifotrig = *(int *)data;
X                  splx(s);
X                  return 0;
X      
X    case AD_STOP: s = spltty();
X                  info->chan[chan].status = STATUS_STOPPED;              
X                  splx(s);
X                  return 0;
X      
X    case AD_START: s = spltty();
X                   info->chan[chan].status = STATUS_INUSE;
X                   splx(s);
X                   return 0;
X
X    case AD_MICRO_PERIOD_SET: 
X                   s = spltty();
X                   if (sync_clock2 (unit, *(long *) data))
X	            {          
X                      splx(s);
X		      return EPERM;
X                    }
X                   splx(s);
X                   return 0;
X      
X    case AD_MICRO_PERIOD_GET: s = spltty();
X                              *(long *)data = info->microperiod;
X                              splx(s);
X                              return 0;    
X                              
X   }
X   
X  return ENOTTY;  
X}
X
X
X/* handle select() based read polling */
Xstatic int alog_select (dev_t dev, int which, struct proc *p)
X{
X  int unit = UNIT(dev);
X  int chan = CHANNEL(dev);
X  talog_unit *info = alog_unit[unit];
X  int s;
X   
X  s = spltty();
X  if (which == FREAD)
X   if (info->chan[chan].fifosize >= info->chan[chan].fifotrig)
X    { 
X      splx(s);
X      return 1; /* ready for read */
X    }
X   else    
X    {
X      /* record this request */
X      selrecord (p, &(info->chan[chan].readselect));
X      splx(s);
X      return 0; /* not ready, yet */
X    }
X     
X  splx(s);
X  return 0; /* not ready (any I never will be) */
X}
X
X
X/* how to read from the board */
Xstatic int alog_read (dev_t dev, struct uio *uio, int ioflag)
X{
X  int unit = UNIT(dev);
X  int chan = CHANNEL(dev);
X  talog_unit *info = alog_unit[unit];
X  int s, oldtrig, toread, err = 0;
X
X  s = spltty();
X      
X  oldtrig = info->chan[chan].fifotrig; /* save official trigger value */   
X  while (uio->uio_resid >= sizeof(u_short)) /* while uio has space */
X   {
X     if (!info->chan[chan].fifosize) /* if we have an empty fifo */
X      {
X        if (ioflag & IO_NDELAY) /* exit if we are non-blocking */
X	   { err = EWOULDBLOCK;
X	     break; 
X           }
X        /* Start filling fifo on first blocking read */
X	if (info->chan[chan].status == STATUS_INIT)
X	     info->chan[chan].status = STATUS_INUSE;	 
X        /* temporarily adjust the fifo trigger to be optimal size */
X	info->chan[chan].fifotrig = 
X	  min (READMAXTRIG, uio->uio_resid / sizeof(u_short));
X	/* lets sleep until we have some io available or timeout */
X        err = tsleep (&(info->chan[chan].fifo), ALOGPRI | PCATCH, ALOGMSG,
X			 info->chan[chan].fifotrig*READTIMO);
X	if (err == EWOULDBLOCK)
X	 {    printf (DEVFORMAT ": read timeout\n", unit,
X	 	       'a'+EMUX(chan), IMUX(chan));	    
X	 }
X        if (err == ERESTART) err = EINTR; /* don't know how to restart */
X        if (err) break; /* exit if any kind of error or signal */
X      }
X	 
X     /* ok, now if we got here there is something to read from the fifo */
X     
X     /* calculate how many entries we can read out from the fifostart
X      * pointer */ 
X     toread = min (uio->uio_resid / sizeof(u_short), 
X		   min (info->chan[chan].fifosize, 
X		         FIFOSIZE - info->chan[chan].fifostart));
X     /* perform the move, if there is an error then exit */
X     if (err = uiomove((caddr_t)
X			&(info->chan[chan].fifo[info->chan[chan].fifostart]), 
X	                   toread * sizeof(u_short), uio)) break;
X     info->chan[chan].fifosize -= toread; /* fifo this much smaller */ 
X     info->chan[chan].fifostart += toread; /* we got this many more */
X     if (info->chan[chan].fifostart == FIFOSIZE)
X       info->chan[chan].fifostart = 0; /* wrap around fifostart */
X      
X   }
X  info->chan[chan].fifotrig = oldtrig; /* restore trigger changes */
X  splx(s);		       
X  return err;
X}
X
X   
X/* open a channel */
Xstatic int alog_open (dev_t dev, int oflags, int devtype, struct proc *p)
X{
X  int unit = UNIT(dev); /* get unit no */
X  int chan = CHANNEL(dev); /* get channel no */
X  talog_unit *info; 
X  int s; /* priority */
X  int cur;
X   
X  if ((unit >= NALOG) || (unit >= MAXUNITS) || (chan >= NUMCHANNELS))
X      return ENXIO; /* unit and channel no ok ? */
X  if (!alog_unit[unit]) return ENXIO; /* unit attached */
X  info = alog_unit[unit]; /* ok, this is valid now */
X  
X  if (info->chan[chan].status) return EBUSY; /* channel busy */
X  if (oflags & FREAD)
X   {
X     s=spltty();
X     info->chan[chan].status = STATUS_INIT; /* channel open, read waiting */
X     info->chan[chan].fifostart = info->chan[chan].fifoend =
X	info->chan[chan].fifosize = 0;/* fifo empty */
X     info->chan[chan].fifotrig = DEFAULT_FIFO_TRIGGER;
X     if (info->firstchan < 0) /* if empty chain */
X      {
X	info->firstchan = info->curchan = chan; /* rev up the list */
X	info->chan[chan].nextchan = -1; /* end of the list */
X      }
X     else /* non empty list must insert */
X      {	 
X	if (chan < info->firstchan) /* this one must become first in list */
X	 {
X	   info->chan[chan].nextchan = info->firstchan;
X	   info->firstchan = chan;  
X	 }
X 	else /* insert this one as second - last in chan list */
X	 {
X	   cur = info->firstchan;
X	    
X	   /* traverse list as long as cur is less than chan and cur is
X	    * not last in list */
X	   while ((info->chan[cur].nextchan < chan) && 
X	           (info->chan[cur].nextchan >= 0))
X	     cur = info->chan[cur].nextchan; 
X	   
X	   /* now cur should point to the entry right before yours */
X	   info->chan[chan].nextchan = info->chan[cur].nextchan;
X	   info->chan[cur].nextchan = chan; /* insert yours in */
X	 }
X      }
X     splx(s); 
X     return 0; /* open successful */  
X   }
X  return EPERM; /* this is a read only device */ 
X}
X
X
X/* close a channel */
Xstatic int alog_close (dev_t dev, int fflag, int devtype, struct proc *p)
X{
X  int unit = UNIT(dev);
X  int chan = CHANNEL(dev);
X  talog_unit *info = alog_unit[unit];
X  int s;
X  int cur; 
X   
X  s = spltty();
X  info->chan[chan].status = STATUS_UNUSED; 
X  
X  /* what if we are in the middle of a conversion ?
X   * then smoothly get us out of it: */
X  if (info->curchan == chan)
X   { /* if we are last in list set curchan to first in list */
X     if ((info->curchan = info->chan[chan].nextchan) < 0)
X      info->curchan = info->firstchan;
X
X     info->state = STATE_SETUP;
X   }
X   
X  /* if this is the first channel, then make the second channel the first
X   * channel (note that if this is also the only channel firstchan becomes
X   * -1 and so the list is marked as empty */
X   
X  if (chan == info->firstchan) 
X   info->firstchan = info->chan[chan].nextchan;
X  else /* ok, so there must be at least 2 channels (and it is not the first) */
X   {  
X     cur = info->firstchan;
X
X     /* find the entry before it (which must exist if you are closing) */ 
X     while (info->chan[cur].nextchan < chan) 
X	cur = info->chan[cur].nextchan;
X     /* at this point we must have the entry before ours */
X     info->chan[cur].nextchan = info->chan[chan].nextchan; /* give our link */       
X   
X   }
X  
X  splx(s);
X   
X  return 0; /* close always successful */
X}
X
X
X/* The probing routine - returns number of bytes needed */
Xstatic int alog_probe (struct isa_device *idp)
X{
X  int unit = idp->id_unit;  /* this device unit number */
X  int iobase = idp->id_iobase; /* the base address of the unit */
X  int addr; 
X   
X  if ((unit < 0) || (unit >= NALOG) || (unit >= MAXUNITS))
X   { 
X     printf ("alog: invalid unit number (%d)\n", unit);
X     return 0;
X   }
X   
X  /* the unit number is ok, lets check if used */
X  if (alog_unit[unit]) 
X   {
X     printf ("alog: unit (%d) already attached\n", unit);
X     return 0;
X   }
X
X  if (inb (iobase+STATUS) & EOC) return 0; /* End of conv bit should be 0 */
X  for (addr=0; addr<NUMIMUXES; addr++)
X   {
X     outb (iobase+STATUS, EMUXMASK|addr);/* output ones to upper nibbl+addr */ 
X     /* get back a zero in MSB and the addr where you put it */
X     if ((inb (iobase+STATUS) & (EOC|IMUXMASK)) != addr) return 0;
X   }
X 
X  return NUMPORTS; /* this device needs this many ports */ 
X}
X
X
X/* setup the info structure correctly for reloading clock 2 after interrupt */
Xstatic int sync_clock2 (int unit, long period)
X{
X  int clockper;
X  talog_unit *info = alog_unit[unit];
X   
X  if ((period > MAX_MICRO_PERIOD) || (period < MIN_MICRO_PERIOD))
X     return -1; /* error period too long */
X  info->microperiod = period; /* record the period */   
X  clockper = (CLOCK2FREQ * period) / PRIMARY_STATES;
X  info->perlo = clockper & 0xff; /* least sig byte of clock period */
X  info->perhi = ((clockper & 0xff00) >> 8); /* most sig byte of clock period */
X  return 0;
X}
X
X
X/* The attachment routine - returns true on success */
Xstatic int alog_attach (struct isa_device *idp)
X{
X  int unit = idp->id_unit;  /* this device unit number */   
X  int iobase = idp->id_iobase; /* the base address of the unit */ 
X  talog_unit *info; /* pointer to driver specific info for unit */
X  int chan; /* the channel used for creating devfs nodes */
X   
X  if (!(info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT)))
X   {
X     printf ("alog%d: cannot allocate driver storage\n", unit);
X     return 0;
X   }
X  alog_unit[unit] = info; /* make sure to save the pointer */
X  bzero (info, sizeof(*info)); /* clear info structure to all false */
X  info->isaunit = idp;  /* store ptr to isa device information */
X  sync_clock2 (unit, DEFAULT_MICRO_PERIOD); /* setup perlo and perhi */ 
X  info->firstchan = -1; /* channel lists are empty */
X   
X  /* insert devfs nodes */
X  
X#ifdef DEVFS   
X  for (chan=0; chan<NUMCHANNELS; chan++)
X    info->chan[chan].devfs_token = 
X     devfs_add_devswf(&alog_cdevsw, LMINOR(unit, chan), DV_CHR,
X	              UID_ROOT, GID_WHEEL, 0400, DEVFORMAT,
X		        unit, 'a'+EMUX(chan), IMUX(chan));
X#endif   
X
X  printf ("alog%d: %d channels, %d bytes/FIFO, %d entry trigger\n",
X            unit, NUMCHANNELS, FIFOSIZE*sizeof(u_short), 
X            DEFAULT_FIFO_TRIGGER);
X  alogintr (unit); /* start the periodic interrupting process */    
X  return 1; /* obviously successful */
X}
X
X
X/* Unit interrupt handling routine (interrupts generated by clock 2) */
Xvoid alogintr (int unit)
X{
X  talog_unit *info = alog_unit[unit];
X  int iobase = info->isaunit->id_iobase;
X  u_short fifoent;
X   
X   
X  if (info->firstchan >= 0) /* ? is there even a chan list to traverse */
X   switch (info->state)
X    {  
X      case STATE_READ: 
X       if (info->chan[info->curchan].status == STATUS_INUSE)
X	{ 
X         if (inb (iobase+STATUS) & EOC) /* check that conversion finished */
X	  printf (DEVFORMAT ": incomplete conversion\n", unit,
X	 	   'a'+EMUX(info->curchan), IMUX(info->curchan));	    
X	 else /* conversion is finished (should always be) */
X	  {   
X	   fifoent = (inb (iobase+ADHIGH) << 8) +
X	               inb (iobase+ADLOW);
X           if (putfifo(&(info->chan[info->curchan]), fifoent))
X    	    {
X	       printf (DEVFORMAT ": fifo overflow\n", unit,
X	 	       'a'+EMUX(info->curchan), IMUX(info->curchan));	    
X	    }
X           if (info->chan[info->curchan].fifosize >=
X	        info->chan[info->curchan].fifotrig)
X	    {
X	      /* if we've reached trigger levels */
X	      selwakeup (&(info->chan[info->curchan].readselect));
X	      wakeup (&(info->chan[info->curchan].fifo));
X	    }
X	  }
X	 }
X       /* goto setup state for next channel on list */
X       if ((info->curchan = info->chan[info->curchan].nextchan) < 0)
X	info->curchan = info->firstchan;  
X       /* notice lack of break here this implys a STATE_SETUP */ 
X      case STATE_SETUP: /* set the muxes and let them settle */
X#if NUMCHANNELS > NUMIMUXES    /* only do this if using external muxes */
X       outb (iobase+STATUS, 
X	      EMUXMAKE(info->curchan) | IMUX(info->curchan) | IEN);
X       info->state = STATE_CONVERT;
X       break;
X#endif
X      case STATE_CONVERT: 
X       outb (iobase+STATUS, 
X	      EMUXMAKE(info->curchan) | IMUX(info->curchan) | IEN);
X       outb (iobase+ADHIGH, 0); /* start the conversion */
X       info->state = STATE_READ;
X       break;
X    }
X  else /* this is kind of like an idle mode */ 
X   {
X      outb (iobase+STATUS, IEN); /* no list keep getting interrupts though */
X      /* since we have no open channels spin clock rate down to 
X       * minimum to save interrupt overhead */
X      outb (iobase+CNTRCNTRL, LD2MODE4); /* counter 2 to mode 4 strobe */
X      outb (iobase+CNTR2, 0xff); /* longest period we can generate */
X      outb (iobase+CNTR2, 0xff); 
X      return;   
X   }
X  outb (iobase+CNTRCNTRL, LD2MODE4); /* counter 2 to mode 4 strobe */
X  outb (iobase+CNTR2, info->perlo); /* low part of the period count */
X  outb (iobase+CNTR2, info->perhi); /* high part of the period count */
X}
X
X   
X/* this will put an entry in fifo, returns 1 if the first item in 
X * fifo was wiped (overflow) or 0 if everything went fine */
Xstatic int __inline putfifo (talog_chan *pchan, u_short fifoent)
X{   
X   pchan->fifo[pchan->fifoend] = fifoent; /* insert the entry in */
X   pchan->fifoend++; /* one more in fifo */
X   if (pchan->fifoend == FIFOSIZE) pchan->fifoend = 0; /* wrap around */ 
X   /* note: I did intend to write over the oldest entry on overflow */
X   if (pchan->fifosize == FIFOSIZE) /* overflowing state already */
X    {
X       pchan->fifostart++;
X       if (pchan->fifostart == FIFOSIZE) pchan->fifostart = 0;
X       return 1; /* we overflowed */
X    }
X   pchan->fifosize++; /* actually one bigger, else same size */
X   return 0; /* went in just fine */ 
X}
X   
X
X/* Driver initialization */
Xstatic void alog_drvinit (void *unused)
X{
X  dev_t dev;  /* Type for holding device major/minor numbers (int) */
X
X  if (!alog_devsw_installed)
X   {
X     dev = makedev (CDEV_MAJOR, 0);  /* description of device major */
X     cdevsw_add (&dev, &alog_cdevsw, NULL);  /* put driver in cdev table */
X     alog_devsw_installed=1;
X   }
X}
X
X/* System initialization call instance */
X
XSYSINIT (alogdev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE+CDEV_MAJOR,
X         alog_drvinit,NULL);
X
X#endif
END-of-alog.c
echo x - alogio.h
sed 's/^X//' >alogio.h << 'END-of-alogio.h'
X/*
X * Copyright (c) 1997 Jamil J. Weatherbee
X * All rights reserved.
X *
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer
X *    in this position and unchanged.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. The name of the author may not be used to endorse or promote products
X *    derived from this software without specific prior written permission
X * 
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
X * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
X * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
X * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
X * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
X * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
X * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
X * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
X * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
X *
X * Industrial Computer Source model AIO8-P
X * 128 channel MUX capability via daisy chained AT-16P units
X * alogio.h, definitions for alog ioctl(), last revised November 19 1997
X * See http://www.indcompsrc.com/products/data/html/aio8g-p.html
X *     http://www.indcompsrc.com/products/data/html/at16-p.html
X *
X * Written by: Jamil J. Weatherbee <jamil@trojanhorse.ml.org>
X *
X */
X
X#ifndef _SYS_ALOGIO_H_
X#define _SYS_ALOGIO_H_
X
X#ifndef KERNEL
X#include <sys/types.h>
X#endif
X#include <sys/ioccom.h>
X
X/* Note: By default A/D conversions are started when a channel is open */
X
X/* Halt clocked A/D conversion on an open channel */
X#define AD_STOP _IO('A', 100) 
X/* Restart clocked A/D conversion on an open channel */
X#define AD_START _IO('A', 101)
X/* Get the number of entries the fifo for this channel will hold */
X#define AD_FIFOSIZE_GET _IOR('A', 102, int)
X/* Set the minimum number of entries a fifo must contain before it
X * notifies a select() or read() that is waiting for it to fill */
X#define AD_FIFO_TRIGGER_SET _IOW('A', 103, int)
X/* This gets the the fifo trigger setting */
X#define AD_FIFO_TRIGGER_GET _IOR('A', 104, int)
X
X#endif
END-of-alogio.h
exit

>Audit-Trail:
>Unformatted:



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