summaryrefslogtreecommitdiffstats
path: root/sys/i386/isa/alog.c
blob: 8fbe5768811a9eeb0f1130297f65c5e9f34f9942 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
/*
 * Copyright (c) 1998 Scottibox 
 * All rights reserved.
 *
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer
 *    in this position and unchanged.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Industrial Computer Source model AIO8-P
 * 8 channel, moderate speed analog to digital converter board with
 * 128 channel MUX capability via daisy chained AT-16P units
 * alog.c, character device driver, last revised January 6 1998
 * See http://www.scottibox.com
 *     http://www.indcompsrc.com/products/data/html/aio8g-p.html
 *     http://www.indcompsrc.com/products/data/html/at16-p.html
 *
 * Written by: Jamil J. Weatherbee <jamil@scottibox.com>
 *
 */


/* Include Files */

#include "alog.h"
#if NALOG > 0

#include <sys/param.h> 
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#include <sys/malloc.h>
#include <sys/poll.h>
#include <sys/vnode.h>
#include <sys/filio.h>
#include <i386/isa/isa_device.h>
#include <sys/alogio.h>
#include <sys/dataacq.h>

#include "opt_devfs.h"
#ifdef DEVFS
#include <sys/devfsext.h>
#endif 

/* Local Defines */

/* Tests have shown that increasing the fifo size 
 * beyond 64 entries for this particular piece of hardware is
 * unproductive */

#ifdef ALOG_FIFOSIZE
#define FIFOSIZE ALOG_FIFOSIZE
#else
#define FIFOSIZE 64
#endif

#ifdef ALOG_FIFO_TRIGGER
#define DEFAULT_FIFO_TRIGGER ALOG_FIFO_TRIGGER
#else
#define DEFAULT_FIFO_TRIGGER 1
#endif

#ifdef ALOG_CHANNELS 
#define NUMCHANNELS ALOG_CHANNELS
#else
#define NUMCHANNELS 128
#endif

#ifdef ALOG_TIMO
#define READTIMO ALOG_TIMO
#else
#define READTIMO (MAX_MICRO_PERIOD*NUMCHANNELS/500000*hz)
#endif

#define CDEV_MAJOR 86
#define NUMPORTS 8
#define MAXUNITS 2
#define NUMIMUXES 8

#define ADLOW 0x0
#define ADHIGH 0x1
#define STATUS 0x2
#define CNTR0 0x4
#define CNTR1 0x5
#define CNTR2 0x6
#define CNTRCNTRL 0x7

#define DEVFORMAT "alog%d%c%d"
#define CLOCK2FREQ 4.165
#define MIN_MICRO_PERIOD 25  
#define MAX_MICRO_PERIOD (65535/CLOCK2FREQ*PRIMARY_STATES)
#define DEFAULT_MICRO_PERIOD MAX_MICRO_PERIOD
#define READMAXTRIG 0.75*FIFOSIZE
#define ALOGPRI PRIBIO
#define ALOGMSG "alogio"

#define PRIMARY_STATES 2  /* Setup and conversion are clock tick consuming */
#define STATE_SETUP 0
#define STATE_CONVERT 1
#define STATE_READ 2

/* Notes on interrupt driven A/D conversion:
 * On the AIO8-P, interrupt driven conversion (the only type supported by this
 * driver) is facilitated through 8253 timer #2.  In order for interrrupts to
 * be generated you must connect line 6 to line 24 (counter 2 output to 
 * interrupt input) and line 23 to line 29 (counter 2 gate to +5VDC). 
 * Due to the design of the AIO8-P this precludes the use of programmable 
 * gain control.
 */

/* mode bits for the status register */

#define EOC 0x80 
#define IEN 0x08  
#define IMUXMASK 0x07
#define EMUXMASK 0xf0

/* mode bits for counter controller */

#define LD2MODE4 0xb8

/* Minor allocations:
 * UCCCCMMM
 * U: board unit (0-1)
 * CCCC: external multiplexer channel (0-15) (on AT-16P units)
 * MMM: internal multiplexer channel (0-7) (on AIO8-P card)
 */

#define UNIT(dev) ((minor(dev) & 0x80) >> 7)
#define CHANNEL(dev) (minor(dev) & 0x7f) 
#define EMUX(chan) ((chan & 0x78) >> 3)
#define EMUXMAKE(chan) ((chan & 0x78) << 1)
#define IMUX(chan) (chan & 0x07)
#define LMINOR(unit, chan) ((unit << 7)+chan)

/* port statuses */

#define STATUS_UNUSED 0 
#define STATUS_INUSE 1
#define STATUS_STOPPED 2
#define STATUS_INIT 3

/* Type definitions */

typedef struct
{
  short status; /* the status of this chan */
  struct selinfo readpoll; /* the poll() info */
  u_short fifo[FIFOSIZE]; /* fifo for this chan */
  int fifostart, fifoend; /* the ptrs showing where info is stored in fifo */
  int fifosize, fifotrig; /* the current and trigger size of the fifo */
  void *devfs_token; /* the devfs token for this chan */
  int nextchan;  
} talog_chan;

typedef struct 
{ 
  struct isa_device *isaunit; /* ptr to isa device information */     
  talog_chan chan[NUMCHANNELS]; /* the device nodes */
  int curchan; /* the current chan being intr handled */
  int firstchan; /* the first chan to go to in list */
  int state; /* is the node in setup or convert mode */ 
  long microperiod; /* current microsecond period setting */ 
  u_char perlo, perhi; /* current values to send to clock 2 after every intr */
   
} talog_unit;

/* Function Prototypes */

static int alog_probe (struct isa_device *idp);  /* Check for alog board */
static int alog_attach (struct isa_device *idp);  /* Take alog board */
static int sync_clock2 (int unit, long period); /* setup clock 2 period */
static int putfifo (talog_chan *pchan, u_short fifoent);
static int alog_open (dev_t dev, int oflags, int devtype, struct proc *p);
static int alog_close (dev_t dev, int fflag, int devtype, struct proc *p);
static int alog_ioctl (dev_t dev, u_long cmd, caddr_t data,
		        int fflag, struct proc *p);
static int alog_read (dev_t dev, struct uio *uio, int ioflag);
static int alog_poll (dev_t dev, int events, struct proc *p);

/* Global Data */

static int alog_devsw_installed = 0;  /* Protect against reinit multiunit */
static talog_unit *alog_unit[NALOG]; /* data structs for each unit */ 

/* Character device switching structure */
static struct cdevsw alog_cdevsw = { alog_open, alog_close, alog_read,
                                     nowrite, alog_ioctl, nostop, noreset,
                                     nodevtotty, alog_poll, nommap,
                                     nostrategy, "alog", NULL, -1 };

/* Structure expected to tell how to probe and attach the driver
 * Must be published externally (cannot be static) */
struct isa_driver alogdriver = { alog_probe, alog_attach, "alog", 0 };


/* handle the ioctls */
static int alog_ioctl (dev_t dev, u_long cmd, caddr_t data,
		        int fflag, struct proc *p)
{
  int unit = UNIT(dev);
  int chan = CHANNEL(dev);
  talog_unit *info = alog_unit[unit];
  int s;
   
  switch (cmd)
   {  
    case FIONBIO: return 0; /* this allows for non-blocking ioctls */ 

    case AD_NCHANS_GET: *(int *)data = NUMCHANNELS;
                        return 0;
    case AD_FIFOSIZE_GET: *(int *)data = FIFOSIZE;
                          return 0;
      
    case AD_FIFO_TRIGGER_GET: s = spltty();
	                      *(int *)data = info->chan[chan].fifotrig;
	                      splx(s);
                              return 0;
      
    case AD_FIFO_TRIGGER_SET: 
                  s = spltty();
                  if ((*(int *)data < 1) || (*(int *)data > FIFOSIZE))
	           {   
	             splx(s);
		     return EPERM; 
		   }
                  info->chan[chan].fifotrig = *(int *)data;
                  splx(s);
                  return 0;
      
    case AD_STOP: s = spltty();
                  info->chan[chan].status = STATUS_STOPPED;              
                  splx(s);
                  return 0;
      
    case AD_START: s = spltty();
                   info->chan[chan].status = STATUS_INUSE;
                   splx(s);
                   return 0;

    case AD_MICRO_PERIOD_SET: 
                   s = spltty();
                   if (sync_clock2 (unit, *(long *) data))
	            {          
                      splx(s);
		      return EPERM;
                    }
                   splx(s);
                   return 0;
      
    case AD_MICRO_PERIOD_GET: s = spltty();
                              *(long *)data = info->microperiod;
                              splx(s);
                              return 0;    
                              
   }
   
  return ENOTTY;  
}


/* handle poll() based read polling */
static int alog_poll (dev_t dev, int events, struct proc *p)
{
  int unit = UNIT(dev);
  int chan = CHANNEL(dev);
  talog_unit *info = alog_unit[unit];
  int s;
   
  s = spltty();
  if (events & (POLLIN | POLLRDNORM)) /* if polling for any/normal data */
   if (info->chan[chan].fifosize >= info->chan[chan].fifotrig)
    { 
      splx(s);
       
      return events & (POLLIN | POLLRDNORM); /* ready for any/read */
    }
   else    
    {
      /* record this request */
      selrecord (p, &(info->chan[chan].readpoll));
      splx(s);
      return 0; /* not ready, yet */
    }
     
  splx(s);
  return 0; /* not ready (any I never will be) */
}


/* how to read from the board */
static int alog_read (dev_t dev, struct uio *uio, int ioflag)
{
  int unit = UNIT(dev);
  int chan = CHANNEL(dev);
  talog_unit *info = alog_unit[unit];
  int s, oldtrig, toread, err = 0;

  s = spltty();
      
  oldtrig = info->chan[chan].fifotrig; /* save official trigger value */   
  while (uio->uio_resid >= sizeof(u_short)) /* while uio has space */
   {
     if (!info->chan[chan].fifosize) /* if we have an empty fifo */
      {
        if (ioflag & IO_NDELAY) /* exit if we are non-blocking */
	   { err = EWOULDBLOCK;
	     break; 
           }
        /* Start filling fifo on first blocking read */
	if (info->chan[chan].status == STATUS_INIT)
	     info->chan[chan].status = STATUS_INUSE;	 
        /* temporarily adjust the fifo trigger to be optimal size */
	info->chan[chan].fifotrig = 
	  min (READMAXTRIG, uio->uio_resid / sizeof(u_short));
	/* lets sleep until we have some io available or timeout */
        err = tsleep (&(info->chan[chan].fifo), ALOGPRI | PCATCH, ALOGMSG,
			 info->chan[chan].fifotrig*READTIMO);
	if (err == EWOULDBLOCK)
	 {    printf (DEVFORMAT ": read timeout\n", unit,
	 	       'a'+EMUX(chan), IMUX(chan));	    
	 }
        if (err == ERESTART) err = EINTR; /* don't know how to restart */
        if (err) break; /* exit if any kind of error or signal */
      }
	 
     /* ok, now if we got here there is something to read from the fifo */
     
     /* calculate how many entries we can read out from the fifostart
      * pointer */ 
     toread = min (uio->uio_resid / sizeof(u_short), 
		   min (info->chan[chan].fifosize, 
		         FIFOSIZE - info->chan[chan].fifostart));
     /* perform the move, if there is an error then exit */
     if (err = uiomove((caddr_t)
			&(info->chan[chan].fifo[info->chan[chan].fifostart]), 
	                   toread * sizeof(u_short), uio)) break;
     info->chan[chan].fifosize -= toread; /* fifo this much smaller */ 
     info->chan[chan].fifostart += toread; /* we got this many more */
     if (info->chan[chan].fifostart == FIFOSIZE)
       info->chan[chan].fifostart = 0; /* wrap around fifostart */
      
   }
  info->chan[chan].fifotrig = oldtrig; /* restore trigger changes */
  splx(s);		       
  return err;
}

   
/* open a channel */
static int alog_open (dev_t dev, int oflags, int devtype, struct proc *p)
{
  int unit = UNIT(dev); /* get unit no */
  int chan = CHANNEL(dev); /* get channel no */
  talog_unit *info; 
  int s; /* priority */
  int cur;
   
  if ((unit >= NALOG) || (unit >= MAXUNITS) || (chan >= NUMCHANNELS))
      return ENXIO; /* unit and channel no ok ? */
  if (!alog_unit[unit]) return ENXIO; /* unit attached */
  info = alog_unit[unit]; /* ok, this is valid now */
  
  if (info->chan[chan].status) return EBUSY; /* channel busy */
  if (oflags & FREAD)
   {
     s=spltty();
     info->chan[chan].status = STATUS_INIT; /* channel open, read waiting */
     info->chan[chan].fifostart = info->chan[chan].fifoend =
	info->chan[chan].fifosize = 0;/* fifo empty */
     info->chan[chan].fifotrig = DEFAULT_FIFO_TRIGGER;
     if (info->firstchan < 0) /* if empty chain */
      {
	info->firstchan = info->curchan = chan; /* rev up the list */
	info->chan[chan].nextchan = -1; /* end of the list */
      }
     else /* non empty list must insert */
      {	 
	if (chan < info->firstchan) /* this one must become first in list */
	 {
	   info->chan[chan].nextchan = info->firstchan;
	   info->firstchan = chan;  
	 }
 	else /* insert this one as second - last in chan list */
	 {
	   cur = info->firstchan;
	    
	   /* traverse list as long as cur is less than chan and cur is
	    * not last in list */
	   while ((info->chan[cur].nextchan < chan) && 
	           (info->chan[cur].nextchan >= 0))
	     cur = info->chan[cur].nextchan; 
	   
	   /* now cur should point to the entry right before yours */
	   info->chan[chan].nextchan = info->chan[cur].nextchan;
	   info->chan[cur].nextchan = chan; /* insert yours in */
	 }
      }
     splx(s); 
     return 0; /* open successful */  
   }
  return EPERM; /* this is a read only device */ 
}


/* close a channel */
static int alog_close (dev_t dev, int fflag, int devtype, struct proc *p)
{
  int unit = UNIT(dev);
  int chan = CHANNEL(dev);
  talog_unit *info = alog_unit[unit];
  int s;
  int cur; 
   
  s = spltty();
  info->chan[chan].status = STATUS_UNUSED; 
  
  /* what if we are in the middle of a conversion ?
   * then smoothly get us out of it: */
  if (info->curchan == chan)
   { /* if we are last in list set curchan to first in list */
     if ((info->curchan = info->chan[chan].nextchan) < 0)
      info->curchan = info->firstchan;

     info->state = STATE_SETUP;
   }
   
  /* if this is the first channel, then make the second channel the first
   * channel (note that if this is also the only channel firstchan becomes
   * -1 and so the list is marked as empty */
   
  if (chan == info->firstchan) 
   info->firstchan = info->chan[chan].nextchan;
  else /* ok, so there must be at least 2 channels (and it is not the first) */
   {  
     cur = info->firstchan;

     /* find the entry before it (which must exist if you are closing) */ 
     while (info->chan[cur].nextchan < chan) 
	cur = info->chan[cur].nextchan;
     /* at this point we must have the entry before ours */
     info->chan[cur].nextchan = info->chan[chan].nextchan; /* give our link */       
   
   }
  
  splx(s);
   
  return 0; /* close always successful */
}


/* The probing routine - returns number of bytes needed */
static int alog_probe (struct isa_device *idp)
{
  int unit = idp->id_unit;  /* this device unit number */
  int iobase = idp->id_iobase; /* the base address of the unit */
  int addr; 
   
  if ((unit < 0) || (unit >= NALOG) || (unit >= MAXUNITS))
   { 
     printf ("alog: invalid unit number (%d)\n", unit);
     return 0;
   }
   
  /* the unit number is ok, lets check if used */
  if (alog_unit[unit]) 
   {
     printf ("alog: unit (%d) already attached\n", unit);
     return 0;
   }

  if (inb (iobase+STATUS) & EOC) return 0; /* End of conv bit should be 0 */
  for (addr=0; addr<NUMIMUXES; addr++)
   {
     outb (iobase+STATUS, EMUXMASK|addr);/* output ones to upper nibbl+addr */ 
     /* get back a zero in MSB and the addr where you put it */
     if ((inb (iobase+STATUS) & (EOC|IMUXMASK)) != addr) return 0;
   }
 
  return NUMPORTS; /* this device needs this many ports */ 
}


/* setup the info structure correctly for reloading clock 2 after interrupt */
static int sync_clock2 (int unit, long period)
{
  int clockper;
  talog_unit *info = alog_unit[unit];
   
  if ((period > MAX_MICRO_PERIOD) || (period < MIN_MICRO_PERIOD))
     return -1; /* error period too long */
  info->microperiod = period; /* record the period */   
  clockper = (CLOCK2FREQ * period) / PRIMARY_STATES;
  info->perlo = clockper & 0xff; /* least sig byte of clock period */
  info->perhi = ((clockper & 0xff00) >> 8); /* most sig byte of clock period */
  return 0;
}


/* The attachment routine - returns true on success */
static int alog_attach (struct isa_device *idp)
{
  int unit = idp->id_unit;  /* this device unit number */   
  int iobase = idp->id_iobase; /* the base address of the unit */ 
  talog_unit *info; /* pointer to driver specific info for unit */
  int chan; /* the channel used for creating devfs nodes */
   
  if (!(info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT)))
   {
     printf ("alog%d: cannot allocate driver storage\n", unit);
     return 0;
   }
  alog_unit[unit] = info; /* make sure to save the pointer */
  bzero (info, sizeof(*info)); /* clear info structure to all false */
  info->isaunit = idp;  /* store ptr to isa device information */
  sync_clock2 (unit, DEFAULT_MICRO_PERIOD); /* setup perlo and perhi */ 
  info->firstchan = -1; /* channel lists are empty */
   
  /* insert devfs nodes */
  
#ifdef DEVFS   
  for (chan=0; chan<NUMCHANNELS; chan++)
    info->chan[chan].devfs_token = 
     devfs_add_devswf(&alog_cdevsw, LMINOR(unit, chan), DV_CHR,
	              UID_ROOT, GID_WHEEL, 0400, DEVFORMAT,
		        unit, 'a'+EMUX(chan), IMUX(chan));
#endif   

  printf ("alog%d: %d channels, %d bytes/FIFO, %d entry trigger\n",
            unit, NUMCHANNELS, FIFOSIZE*sizeof(u_short), 
            DEFAULT_FIFO_TRIGGER);
  alogintr (unit); /* start the periodic interrupting process */    
  return 1; /* obviously successful */
}


/* Unit interrupt handling routine (interrupts generated by clock 2) */
void alogintr (int unit)
{
  talog_unit *info = alog_unit[unit];
  int iobase = info->isaunit->id_iobase;
  u_short fifoent;
   
   
  if (info->firstchan >= 0) /* ? is there even a chan list to traverse */
   switch (info->state)
    {  
      case STATE_READ: 
       if (info->chan[info->curchan].status == STATUS_INUSE)
	{ 
         if (inb (iobase+STATUS) & EOC) /* check that conversion finished */
	  printf (DEVFORMAT ": incomplete conversion\n", unit,
	 	   'a'+EMUX(info->curchan), IMUX(info->curchan));	    
	 else /* conversion is finished (should always be) */
	  {   
	   fifoent = (inb (iobase+ADHIGH) << 8) +
	               inb (iobase+ADLOW);
           if (putfifo(&(info->chan[info->curchan]), fifoent))
    	    {
	       printf (DEVFORMAT ": fifo overflow\n", unit,
	 	       'a'+EMUX(info->curchan), IMUX(info->curchan));	    
	    }
           if (info->chan[info->curchan].fifosize >=
	        info->chan[info->curchan].fifotrig)
	    {
	      /* if we've reached trigger levels */
	      selwakeup (&(info->chan[info->curchan].readpoll));
	      wakeup (&(info->chan[info->curchan].fifo));
	    }
	  }
	 }
       /* goto setup state for next channel on list */
       if ((info->curchan = info->chan[info->curchan].nextchan) < 0)
	info->curchan = info->firstchan;  
       /* notice lack of break here this implys a STATE_SETUP */ 
      case STATE_SETUP: /* set the muxes and let them settle */
#if NUMCHANNELS > NUMIMUXES    /* only do this if using external muxes */
       outb (iobase+STATUS, 
	      EMUXMAKE(info->curchan) | IMUX(info->curchan) | IEN);
       info->state = STATE_CONVERT;
       break;
#endif
      case STATE_CONVERT: 
       outb (iobase+STATUS, 
	      EMUXMAKE(info->curchan) | IMUX(info->curchan) | IEN);
       outb (iobase+ADHIGH, 0); /* start the conversion */
       info->state = STATE_READ;
       break;
    }
  else /* this is kind of like an idle mode */ 
   {
      outb (iobase+STATUS, IEN); /* no list keep getting interrupts though */
      /* since we have no open channels spin clock rate down to 
       * minimum to save interrupt overhead */
      outb (iobase+CNTRCNTRL, LD2MODE4); /* counter 2 to mode 4 strobe */
      outb (iobase+CNTR2, 0xff); /* longest period we can generate */
      outb (iobase+CNTR2, 0xff); 
      return;   
   }
  outb (iobase+CNTRCNTRL, LD2MODE4); /* counter 2 to mode 4 strobe */
  outb (iobase+CNTR2, info->perlo); /* low part of the period count */
  outb (iobase+CNTR2, info->perhi); /* high part of the period count */
}

   
/* this will put an entry in fifo, returns 1 if the first item in 
 * fifo was wiped (overflow) or 0 if everything went fine */
static int putfifo (talog_chan *pchan, u_short fifoent)
{   
   pchan->fifo[pchan->fifoend] = fifoent; /* insert the entry in */
   pchan->fifoend++; /* one more in fifo */
   if (pchan->fifoend == FIFOSIZE) pchan->fifoend = 0; /* wrap around */ 
   /* note: I did intend to write over the oldest entry on overflow */
   if (pchan->fifosize == FIFOSIZE) /* overflowing state already */
    {
       pchan->fifostart++;
       if (pchan->fifostart == FIFOSIZE) pchan->fifostart = 0;
       return 1; /* we overflowed */
    }
   pchan->fifosize++; /* actually one bigger, else same size */
   return 0; /* went in just fine */ 
}
   

/* Driver initialization */
static void alog_drvinit (void *unused)
{
  dev_t dev;  /* Type for holding device major/minor numbers (int) */

  if (!alog_devsw_installed)
   {
     dev = makedev (CDEV_MAJOR, 0);  /* description of device major */
     cdevsw_add (&dev, &alog_cdevsw, NULL);  /* put driver in cdev table */
     alog_devsw_installed=1;
   }
}

/* System initialization call instance */

SYSINIT (alogdev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE+CDEV_MAJOR,
         alog_drvinit,NULL);

#endif
OpenPOWER on IntegriCloud