2 * Copyright (c) 1998 Scottibox
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer
11 * in this position and unchanged.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 * Industrial Computer Source model AIO8-P
30 * 8 channel, moderate speed analog to digital converter board with
31 * 128 channel MUX capability via daisy chained AT-16P units
32 * alog.c, character device driver, last revised January 6 1998
33 * See http://www.scottibox.com
34 * http://www.indcompsrc.com/products/data/html/aio8g-p.html
35 * http://www.indcompsrc.com/products/data/html/at16-p.html
37 * Written by: Jamil J. Weatherbee <jamil@scottibox.com>
47 #include <sys/param.h>
48 #include <sys/systm.h>
49 #include <sys/kernel.h>
51 #include <sys/fcntl.h>
52 #include <sys/malloc.h>
54 #include <sys/vnode.h>
55 #include <sys/filio.h>
56 #include <i386/isa/isa_device.h>
57 #include <sys/alogio.h>
58 #include <sys/dataacq.h>
60 #include "opt_devfs.h"
62 #include <sys/devfsext.h>
67 /* Tests have shown that increasing the fifo size
68 * beyond 64 entries for this particular piece of hardware is
72 #define FIFOSIZE ALOG_FIFOSIZE
77 #ifdef ALOG_FIFO_TRIGGER
78 #define DEFAULT_FIFO_TRIGGER ALOG_FIFO_TRIGGER
80 #define DEFAULT_FIFO_TRIGGER 1
84 #define NUMCHANNELS ALOG_CHANNELS
86 #define NUMCHANNELS 128
90 #define READTIMO ALOG_TIMO
92 #define READTIMO (MAX_MICRO_PERIOD*NUMCHANNELS/500000*hz)
106 #define CNTRCNTRL 0x7
108 #define DEVFORMAT "alog%d%c%d"
109 #define CLOCK2FREQ 4.165
110 #define MIN_MICRO_PERIOD 25
111 #define MAX_MICRO_PERIOD (65535/CLOCK2FREQ*PRIMARY_STATES)
112 #define DEFAULT_MICRO_PERIOD MAX_MICRO_PERIOD
113 #define READMAXTRIG 0.75*FIFOSIZE
114 #define ALOGPRI PRIBIO
115 #define ALOGMSG "alogio"
117 #define PRIMARY_STATES 2 /* Setup and conversion are clock tick consuming */
118 #define STATE_SETUP 0
119 #define STATE_CONVERT 1
122 /* Notes on interrupt driven A/D conversion:
123 * On the AIO8-P, interrupt driven conversion (the only type supported by this
124 * driver) is facilitated through 8253 timer #2. In order for interrrupts to
125 * be generated you must connect line 6 to line 24 (counter 2 output to
126 * interrupt input) and line 23 to line 29 (counter 2 gate to +5VDC).
127 * Due to the design of the AIO8-P this precludes the use of programmable
131 /* mode bits for the status register */
135 #define IMUXMASK 0x07
136 #define EMUXMASK 0xf0
138 /* mode bits for counter controller */
140 #define LD2MODE4 0xb8
142 /* Minor allocations:
144 * U: board unit (0-1)
145 * CCCC: external multiplexer channel (0-15) (on AT-16P units)
146 * MMM: internal multiplexer channel (0-7) (on AIO8-P card)
149 #define UNIT(dev) ((minor(dev) & 0x80) >> 7)
150 #define CHANNEL(dev) (minor(dev) & 0x7f)
151 #define EMUX(chan) ((chan & 0x78) >> 3)
152 #define EMUXMAKE(chan) ((chan & 0x78) << 1)
153 #define IMUX(chan) (chan & 0x07)
154 #define LMINOR(unit, chan) ((unit << 7)+chan)
158 #define STATUS_UNUSED 0
159 #define STATUS_INUSE 1
160 #define STATUS_STOPPED 2
161 #define STATUS_INIT 3
163 /* Type definitions */
167 short status; /* the status of this chan */
168 struct selinfo readpoll; /* the poll() info */
169 u_short fifo[FIFOSIZE]; /* fifo for this chan */
170 int fifostart, fifoend; /* the ptrs showing where info is stored in fifo */
171 int fifosize, fifotrig; /* the current and trigger size of the fifo */
172 void *devfs_token; /* the devfs token for this chan */
178 struct isa_device *isaunit; /* ptr to isa device information */
179 talog_chan chan[NUMCHANNELS]; /* the device nodes */
180 int curchan; /* the current chan being intr handled */
181 int firstchan; /* the first chan to go to in list */
182 int state; /* is the node in setup or convert mode */
183 long microperiod; /* current microsecond period setting */
184 u_char perlo, perhi; /* current values to send to clock 2 after every intr */
188 /* Function Prototypes */
190 static int alog_probe (struct isa_device *idp); /* Check for alog board */
191 static int alog_attach (struct isa_device *idp); /* Take alog board */
192 static ointhand2_t alogintr;
193 static int sync_clock2 (int unit, long period); /* setup clock 2 period */
194 static int putfifo (talog_chan *pchan, u_short fifoent);
195 static int alog_open (dev_t dev, int oflags, int devtype, struct proc *p);
196 static int alog_close (dev_t dev, int fflag, int devtype, struct proc *p);
197 static int alog_ioctl (dev_t dev, u_long cmd, caddr_t data,
198 int fflag, struct proc *p);
199 static int alog_read (dev_t dev, struct uio *uio, int ioflag);
200 static int alog_poll (dev_t dev, int events, struct proc *p);
204 static int alog_devsw_installed = 0; /* Protect against reinit multiunit */
205 static talog_unit *alog_unit[NALOG]; /* data structs for each unit */
207 /* Character device switching structure */
208 static struct cdevsw alog_cdevsw = { alog_open, alog_close, alog_read,
209 nowrite, alog_ioctl, nostop, noreset,
210 nodevtotty, alog_poll, nommap,
211 nostrategy, "alog", NULL, -1 };
213 /* Structure expected to tell how to probe and attach the driver
214 * Must be published externally (cannot be static) */
215 struct isa_driver alogdriver = { alog_probe, alog_attach, "alog", 0 };
218 /* handle the ioctls */
219 static int alog_ioctl (dev_t dev, u_long cmd, caddr_t data,
220 int fflag, struct proc *p)
222 int unit = UNIT(dev);
223 int chan = CHANNEL(dev);
224 talog_unit *info = alog_unit[unit];
229 case FIONBIO: return 0; /* this allows for non-blocking ioctls */
231 case AD_NCHANS_GET: *(int *)data = NUMCHANNELS;
233 case AD_FIFOSIZE_GET: *(int *)data = FIFOSIZE;
236 case AD_FIFO_TRIGGER_GET: s = spltty();
237 *(int *)data = info->chan[chan].fifotrig;
241 case AD_FIFO_TRIGGER_SET:
243 if ((*(int *)data < 1) || (*(int *)data > FIFOSIZE))
248 info->chan[chan].fifotrig = *(int *)data;
252 case AD_STOP: s = spltty();
253 info->chan[chan].status = STATUS_STOPPED;
257 case AD_START: s = spltty();
258 info->chan[chan].status = STATUS_INUSE;
262 case AD_MICRO_PERIOD_SET:
264 if (sync_clock2 (unit, *(long *) data))
272 case AD_MICRO_PERIOD_GET: s = spltty();
273 *(long *)data = info->microperiod;
283 /* handle poll() based read polling */
284 static int alog_poll (dev_t dev, int events, struct proc *p)
286 int unit = UNIT(dev);
287 int chan = CHANNEL(dev);
288 talog_unit *info = alog_unit[unit];
292 if (events & (POLLIN | POLLRDNORM)) /* if polling for any/normal data */
293 if (info->chan[chan].fifosize >= info->chan[chan].fifotrig)
297 return events & (POLLIN | POLLRDNORM); /* ready for any/read */
301 /* record this request */
302 selrecord (p, &(info->chan[chan].readpoll));
304 return 0; /* not ready, yet */
308 return 0; /* not ready (any I never will be) */
312 /* how to read from the board */
313 static int alog_read (dev_t dev, struct uio *uio, int ioflag)
315 int unit = UNIT(dev);
316 int chan = CHANNEL(dev);
317 talog_unit *info = alog_unit[unit];
318 int s, oldtrig, toread, err = 0;
322 oldtrig = info->chan[chan].fifotrig; /* save official trigger value */
323 while (uio->uio_resid >= sizeof(u_short)) /* while uio has space */
325 if (!info->chan[chan].fifosize) /* if we have an empty fifo */
327 if (ioflag & IO_NDELAY) /* exit if we are non-blocking */
331 /* Start filling fifo on first blocking read */
332 if (info->chan[chan].status == STATUS_INIT)
333 info->chan[chan].status = STATUS_INUSE;
334 /* temporarily adjust the fifo trigger to be optimal size */
335 info->chan[chan].fifotrig =
336 min (READMAXTRIG, uio->uio_resid / sizeof(u_short));
337 /* lets sleep until we have some io available or timeout */
338 err = tsleep (&(info->chan[chan].fifo), ALOGPRI | PCATCH, ALOGMSG,
339 info->chan[chan].fifotrig*READTIMO);
340 if (err == EWOULDBLOCK)
341 { printf (DEVFORMAT ": read timeout\n", unit,
342 'a'+EMUX(chan), IMUX(chan));
344 if (err == ERESTART) err = EINTR; /* don't know how to restart */
345 if (err) break; /* exit if any kind of error or signal */
348 /* ok, now if we got here there is something to read from the fifo */
350 /* calculate how many entries we can read out from the fifostart
352 toread = min (uio->uio_resid / sizeof(u_short),
353 min (info->chan[chan].fifosize,
354 FIFOSIZE - info->chan[chan].fifostart));
355 /* perform the move, if there is an error then exit */
356 if (err = uiomove((caddr_t)
357 &(info->chan[chan].fifo[info->chan[chan].fifostart]),
358 toread * sizeof(u_short), uio)) break;
359 info->chan[chan].fifosize -= toread; /* fifo this much smaller */
360 info->chan[chan].fifostart += toread; /* we got this many more */
361 if (info->chan[chan].fifostart == FIFOSIZE)
362 info->chan[chan].fifostart = 0; /* wrap around fifostart */
365 info->chan[chan].fifotrig = oldtrig; /* restore trigger changes */
372 static int alog_open (dev_t dev, int oflags, int devtype, struct proc *p)
374 int unit = UNIT(dev); /* get unit no */
375 int chan = CHANNEL(dev); /* get channel no */
377 int s; /* priority */
380 if ((unit >= NALOG) || (unit >= MAXUNITS) || (chan >= NUMCHANNELS))
381 return ENXIO; /* unit and channel no ok ? */
382 if (!alog_unit[unit]) return ENXIO; /* unit attached */
383 info = alog_unit[unit]; /* ok, this is valid now */
385 if (info->chan[chan].status) return EBUSY; /* channel busy */
389 info->chan[chan].status = STATUS_INIT; /* channel open, read waiting */
390 info->chan[chan].fifostart = info->chan[chan].fifoend =
391 info->chan[chan].fifosize = 0;/* fifo empty */
392 info->chan[chan].fifotrig = DEFAULT_FIFO_TRIGGER;
393 if (info->firstchan < 0) /* if empty chain */
395 info->firstchan = info->curchan = chan; /* rev up the list */
396 info->chan[chan].nextchan = -1; /* end of the list */
398 else /* non empty list must insert */
400 if (chan < info->firstchan) /* this one must become first in list */
402 info->chan[chan].nextchan = info->firstchan;
403 info->firstchan = chan;
405 else /* insert this one as second - last in chan list */
407 cur = info->firstchan;
409 /* traverse list as long as cur is less than chan and cur is
410 * not last in list */
411 while ((info->chan[cur].nextchan < chan) &&
412 (info->chan[cur].nextchan >= 0))
413 cur = info->chan[cur].nextchan;
415 /* now cur should point to the entry right before yours */
416 info->chan[chan].nextchan = info->chan[cur].nextchan;
417 info->chan[cur].nextchan = chan; /* insert yours in */
421 return 0; /* open successful */
423 return EPERM; /* this is a read only device */
427 /* close a channel */
428 static int alog_close (dev_t dev, int fflag, int devtype, struct proc *p)
430 int unit = UNIT(dev);
431 int chan = CHANNEL(dev);
432 talog_unit *info = alog_unit[unit];
437 info->chan[chan].status = STATUS_UNUSED;
439 /* what if we are in the middle of a conversion ?
440 * then smoothly get us out of it: */
441 if (info->curchan == chan)
442 { /* if we are last in list set curchan to first in list */
443 if ((info->curchan = info->chan[chan].nextchan) < 0)
444 info->curchan = info->firstchan;
446 info->state = STATE_SETUP;
449 /* if this is the first channel, then make the second channel the first
450 * channel (note that if this is also the only channel firstchan becomes
451 * -1 and so the list is marked as empty */
453 if (chan == info->firstchan)
454 info->firstchan = info->chan[chan].nextchan;
455 else /* ok, so there must be at least 2 channels (and it is not the first) */
457 cur = info->firstchan;
459 /* find the entry before it (which must exist if you are closing) */
460 while (info->chan[cur].nextchan < chan)
461 cur = info->chan[cur].nextchan;
462 /* at this point we must have the entry before ours */
463 info->chan[cur].nextchan = info->chan[chan].nextchan; /* give our link */
469 return 0; /* close always successful */
473 /* The probing routine - returns number of bytes needed */
474 static int alog_probe (struct isa_device *idp)
476 int unit = idp->id_unit; /* this device unit number */
477 int iobase = idp->id_iobase; /* the base address of the unit */
480 if ((unit < 0) || (unit >= NALOG) || (unit >= MAXUNITS))
482 printf ("alog: invalid unit number (%d)\n", unit);
486 /* the unit number is ok, lets check if used */
489 printf ("alog: unit (%d) already attached\n", unit);
493 if (inb (iobase+STATUS) & EOC) return 0; /* End of conv bit should be 0 */
494 for (addr=0; addr<NUMIMUXES; addr++)
496 outb (iobase+STATUS, EMUXMASK|addr);/* output ones to upper nibbl+addr */
497 /* get back a zero in MSB and the addr where you put it */
498 if ((inb (iobase+STATUS) & (EOC|IMUXMASK)) != addr) return 0;
501 return NUMPORTS; /* this device needs this many ports */
505 /* setup the info structure correctly for reloading clock 2 after interrupt */
506 static int sync_clock2 (int unit, long period)
509 talog_unit *info = alog_unit[unit];
511 if ((period > MAX_MICRO_PERIOD) || (period < MIN_MICRO_PERIOD))
512 return -1; /* error period too long */
513 info->microperiod = period; /* record the period */
514 clockper = (CLOCK2FREQ * period) / PRIMARY_STATES;
515 info->perlo = clockper & 0xff; /* least sig byte of clock period */
516 info->perhi = ((clockper & 0xff00) >> 8); /* most sig byte of clock period */
521 /* The attachment routine - returns true on success */
522 static int alog_attach (struct isa_device *idp)
524 int unit = idp->id_unit; /* this device unit number */
525 int iobase = idp->id_iobase; /* the base address of the unit */
526 talog_unit *info; /* pointer to driver specific info for unit */
527 int chan; /* the channel used for creating devfs nodes */
529 idp->id_ointr = alogintr;
530 if (!(info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT)))
532 printf ("alog%d: cannot allocate driver storage\n", unit);
535 alog_unit[unit] = info; /* make sure to save the pointer */
536 bzero (info, sizeof(*info)); /* clear info structure to all false */
537 info->isaunit = idp; /* store ptr to isa device information */
538 sync_clock2 (unit, DEFAULT_MICRO_PERIOD); /* setup perlo and perhi */
539 info->firstchan = -1; /* channel lists are empty */
541 /* insert devfs nodes */
544 for (chan=0; chan<NUMCHANNELS; chan++)
545 info->chan[chan].devfs_token =
546 devfs_add_devswf(&alog_cdevsw, LMINOR(unit, chan), DV_CHR,
547 UID_ROOT, GID_WHEEL, 0400, DEVFORMAT,
548 unit, 'a'+EMUX(chan), IMUX(chan));
551 printf ("alog%d: %d channels, %d bytes/FIFO, %d entry trigger\n",
552 unit, NUMCHANNELS, FIFOSIZE*sizeof(u_short),
553 DEFAULT_FIFO_TRIGGER);
554 alogintr (unit); /* start the periodic interrupting process */
555 return 1; /* obviously successful */
559 /* Unit interrupt handling routine (interrupts generated by clock 2) */
560 static void alogintr (int unit)
562 talog_unit *info = alog_unit[unit];
563 int iobase = info->isaunit->id_iobase;
567 if (info->firstchan >= 0) /* ? is there even a chan list to traverse */
571 if (info->chan[info->curchan].status == STATUS_INUSE)
573 if (inb (iobase+STATUS) & EOC) /* check that conversion finished */
574 printf (DEVFORMAT ": incomplete conversion\n", unit,
575 'a'+EMUX(info->curchan), IMUX(info->curchan));
576 else /* conversion is finished (should always be) */
578 fifoent = (inb (iobase+ADHIGH) << 8) +
580 if (putfifo(&(info->chan[info->curchan]), fifoent))
582 printf (DEVFORMAT ": fifo overflow\n", unit,
583 'a'+EMUX(info->curchan), IMUX(info->curchan));
585 if (info->chan[info->curchan].fifosize >=
586 info->chan[info->curchan].fifotrig)
588 /* if we've reached trigger levels */
589 selwakeup (&(info->chan[info->curchan].readpoll));
590 wakeup (&(info->chan[info->curchan].fifo));
594 /* goto setup state for next channel on list */
595 if ((info->curchan = info->chan[info->curchan].nextchan) < 0)
596 info->curchan = info->firstchan;
597 /* notice lack of break here this implys a STATE_SETUP */
598 case STATE_SETUP: /* set the muxes and let them settle */
599 #if NUMCHANNELS > NUMIMUXES /* only do this if using external muxes */
601 EMUXMAKE(info->curchan) | IMUX(info->curchan) | IEN);
602 info->state = STATE_CONVERT;
607 EMUXMAKE(info->curchan) | IMUX(info->curchan) | IEN);
608 outb (iobase+ADHIGH, 0); /* start the conversion */
609 info->state = STATE_READ;
612 else /* this is kind of like an idle mode */
614 outb (iobase+STATUS, IEN); /* no list keep getting interrupts though */
615 /* since we have no open channels spin clock rate down to
616 * minimum to save interrupt overhead */
617 outb (iobase+CNTRCNTRL, LD2MODE4); /* counter 2 to mode 4 strobe */
618 outb (iobase+CNTR2, 0xff); /* longest period we can generate */
619 outb (iobase+CNTR2, 0xff);
622 outb (iobase+CNTRCNTRL, LD2MODE4); /* counter 2 to mode 4 strobe */
623 outb (iobase+CNTR2, info->perlo); /* low part of the period count */
624 outb (iobase+CNTR2, info->perhi); /* high part of the period count */
628 /* this will put an entry in fifo, returns 1 if the first item in
629 * fifo was wiped (overflow) or 0 if everything went fine */
630 static int putfifo (talog_chan *pchan, u_short fifoent)
632 pchan->fifo[pchan->fifoend] = fifoent; /* insert the entry in */
633 pchan->fifoend++; /* one more in fifo */
634 if (pchan->fifoend == FIFOSIZE) pchan->fifoend = 0; /* wrap around */
635 /* note: I did intend to write over the oldest entry on overflow */
636 if (pchan->fifosize == FIFOSIZE) /* overflowing state already */
639 if (pchan->fifostart == FIFOSIZE) pchan->fifostart = 0;
640 return 1; /* we overflowed */
642 pchan->fifosize++; /* actually one bigger, else same size */
643 return 0; /* went in just fine */
647 /* Driver initialization */
648 static void alog_drvinit (void *unused)
650 dev_t dev; /* Type for holding device major/minor numbers (int) */
652 if (!alog_devsw_installed)
654 dev = makedev (CDEV_MAJOR, 0); /* description of device major */
655 cdevsw_add (&dev, &alog_cdevsw, NULL); /* put driver in cdev table */
656 alog_devsw_installed=1;
660 /* System initialization call instance */
662 SYSINIT (alogdev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE+CDEV_MAJOR,