]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/i386/isa/alog.c
This commit was generated by cvs2svn to compensate for changes in r42788,
[FreeBSD/FreeBSD.git] / sys / i386 / isa / alog.c
1 /*
2  * Copyright (c) 1998 Scottibox 
3  * All rights reserved.
4  *
5  * 
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
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
17  * 
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.
28  *
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
36  *
37  * Written by: Jamil J. Weatherbee <jamil@scottibox.com>
38  *
39  */
40
41
42 /* Include Files */
43
44 #include "alog.h"
45 #if NALOG > 0
46
47 #include <sys/param.h> 
48 #include <sys/systm.h>
49 #include <sys/kernel.h>
50 #include <sys/conf.h>
51 #include <sys/fcntl.h>
52 #include <sys/malloc.h>
53 #include <sys/poll.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>
59
60 #include "opt_devfs.h"
61 #ifdef DEVFS
62 #include <sys/devfsext.h>
63 #endif 
64
65 /* Local Defines */
66
67 /* Tests have shown that increasing the fifo size 
68  * beyond 64 entries for this particular piece of hardware is
69  * unproductive */
70
71 #ifdef ALOG_FIFOSIZE
72 #define FIFOSIZE ALOG_FIFOSIZE
73 #else
74 #define FIFOSIZE 64
75 #endif
76
77 #ifdef ALOG_FIFO_TRIGGER
78 #define DEFAULT_FIFO_TRIGGER ALOG_FIFO_TRIGGER
79 #else
80 #define DEFAULT_FIFO_TRIGGER 1
81 #endif
82
83 #ifdef ALOG_CHANNELS 
84 #define NUMCHANNELS ALOG_CHANNELS
85 #else
86 #define NUMCHANNELS 128
87 #endif
88
89 #ifdef ALOG_TIMO
90 #define READTIMO ALOG_TIMO
91 #else
92 #define READTIMO (MAX_MICRO_PERIOD*NUMCHANNELS/500000*hz)
93 #endif
94
95 #define CDEV_MAJOR 86
96 #define NUMPORTS 8
97 #define MAXUNITS 2
98 #define NUMIMUXES 8
99
100 #define ADLOW 0x0
101 #define ADHIGH 0x1
102 #define STATUS 0x2
103 #define CNTR0 0x4
104 #define CNTR1 0x5
105 #define CNTR2 0x6
106 #define CNTRCNTRL 0x7
107
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"
116
117 #define PRIMARY_STATES 2  /* Setup and conversion are clock tick consuming */
118 #define STATE_SETUP 0
119 #define STATE_CONVERT 1
120 #define STATE_READ 2
121
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 
128  * gain control.
129  */
130
131 /* mode bits for the status register */
132
133 #define EOC 0x80 
134 #define IEN 0x08  
135 #define IMUXMASK 0x07
136 #define EMUXMASK 0xf0
137
138 /* mode bits for counter controller */
139
140 #define LD2MODE4 0xb8
141
142 /* Minor allocations:
143  * UCCCCMMM
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)
147  */
148
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)
155
156 /* port statuses */
157
158 #define STATUS_UNUSED 0 
159 #define STATUS_INUSE 1
160 #define STATUS_STOPPED 2
161 #define STATUS_INIT 3
162
163 /* Type definitions */
164
165 typedef struct
166 {
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 */
173   int nextchan;  
174 } talog_chan;
175
176 typedef struct 
177
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 */
185    
186 } talog_unit;
187
188 /* Function Prototypes */
189
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);
201
202 /* Global Data */
203
204 static int alog_devsw_installed = 0;  /* Protect against reinit multiunit */
205 static talog_unit *alog_unit[NALOG]; /* data structs for each unit */ 
206
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 };
212
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 };
216
217
218 /* handle the ioctls */
219 static int alog_ioctl (dev_t dev, u_long cmd, caddr_t data,
220                         int fflag, struct proc *p)
221 {
222   int unit = UNIT(dev);
223   int chan = CHANNEL(dev);
224   talog_unit *info = alog_unit[unit];
225   int s;
226    
227   switch (cmd)
228    {  
229     case FIONBIO: return 0; /* this allows for non-blocking ioctls */ 
230
231     case AD_NCHANS_GET: *(int *)data = NUMCHANNELS;
232                         return 0;
233     case AD_FIFOSIZE_GET: *(int *)data = FIFOSIZE;
234                           return 0;
235       
236     case AD_FIFO_TRIGGER_GET: s = spltty();
237                               *(int *)data = info->chan[chan].fifotrig;
238                               splx(s);
239                               return 0;
240       
241     case AD_FIFO_TRIGGER_SET: 
242                   s = spltty();
243                   if ((*(int *)data < 1) || (*(int *)data > FIFOSIZE))
244                    {   
245                      splx(s);
246                      return EPERM; 
247                    }
248                   info->chan[chan].fifotrig = *(int *)data;
249                   splx(s);
250                   return 0;
251       
252     case AD_STOP: s = spltty();
253                   info->chan[chan].status = STATUS_STOPPED;              
254                   splx(s);
255                   return 0;
256       
257     case AD_START: s = spltty();
258                    info->chan[chan].status = STATUS_INUSE;
259                    splx(s);
260                    return 0;
261
262     case AD_MICRO_PERIOD_SET: 
263                    s = spltty();
264                    if (sync_clock2 (unit, *(long *) data))
265                     {          
266                       splx(s);
267                       return EPERM;
268                     }
269                    splx(s);
270                    return 0;
271       
272     case AD_MICRO_PERIOD_GET: s = spltty();
273                               *(long *)data = info->microperiod;
274                               splx(s);
275                               return 0;    
276                               
277    }
278    
279   return ENOTTY;  
280 }
281
282
283 /* handle poll() based read polling */
284 static int alog_poll (dev_t dev, int events, struct proc *p)
285 {
286   int unit = UNIT(dev);
287   int chan = CHANNEL(dev);
288   talog_unit *info = alog_unit[unit];
289   int s;
290    
291   s = spltty();
292   if (events & (POLLIN | POLLRDNORM)) /* if polling for any/normal data */
293    if (info->chan[chan].fifosize >= info->chan[chan].fifotrig)
294     { 
295       splx(s);
296        
297       return events & (POLLIN | POLLRDNORM); /* ready for any/read */
298     }
299    else    
300     {
301       /* record this request */
302       selrecord (p, &(info->chan[chan].readpoll));
303       splx(s);
304       return 0; /* not ready, yet */
305     }
306      
307   splx(s);
308   return 0; /* not ready (any I never will be) */
309 }
310
311
312 /* how to read from the board */
313 static int alog_read (dev_t dev, struct uio *uio, int ioflag)
314 {
315   int unit = UNIT(dev);
316   int chan = CHANNEL(dev);
317   talog_unit *info = alog_unit[unit];
318   int s, oldtrig, toread, err = 0;
319
320   s = spltty();
321       
322   oldtrig = info->chan[chan].fifotrig; /* save official trigger value */   
323   while (uio->uio_resid >= sizeof(u_short)) /* while uio has space */
324    {
325      if (!info->chan[chan].fifosize) /* if we have an empty fifo */
326       {
327         if (ioflag & IO_NDELAY) /* exit if we are non-blocking */
328            { err = EWOULDBLOCK;
329              break; 
330            }
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));         
343          }
344         if (err == ERESTART) err = EINTR; /* don't know how to restart */
345         if (err) break; /* exit if any kind of error or signal */
346       }
347          
348      /* ok, now if we got here there is something to read from the fifo */
349      
350      /* calculate how many entries we can read out from the fifostart
351       * pointer */ 
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 */
363       
364    }
365   info->chan[chan].fifotrig = oldtrig; /* restore trigger changes */
366   splx(s);                     
367   return err;
368 }
369
370    
371 /* open a channel */
372 static int alog_open (dev_t dev, int oflags, int devtype, struct proc *p)
373 {
374   int unit = UNIT(dev); /* get unit no */
375   int chan = CHANNEL(dev); /* get channel no */
376   talog_unit *info; 
377   int s; /* priority */
378   int cur;
379    
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 */
384   
385   if (info->chan[chan].status) return EBUSY; /* channel busy */
386   if (oflags & FREAD)
387    {
388      s=spltty();
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 */
394       {
395         info->firstchan = info->curchan = chan; /* rev up the list */
396         info->chan[chan].nextchan = -1; /* end of the list */
397       }
398      else /* non empty list must insert */
399       {  
400         if (chan < info->firstchan) /* this one must become first in list */
401          {
402            info->chan[chan].nextchan = info->firstchan;
403            info->firstchan = chan;  
404          }
405         else /* insert this one as second - last in chan list */
406          {
407            cur = info->firstchan;
408             
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; 
414            
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 */
418          }
419       }
420      splx(s); 
421      return 0; /* open successful */  
422    }
423   return EPERM; /* this is a read only device */ 
424 }
425
426
427 /* close a channel */
428 static int alog_close (dev_t dev, int fflag, int devtype, struct proc *p)
429 {
430   int unit = UNIT(dev);
431   int chan = CHANNEL(dev);
432   talog_unit *info = alog_unit[unit];
433   int s;
434   int cur; 
435    
436   s = spltty();
437   info->chan[chan].status = STATUS_UNUSED; 
438   
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;
445
446      info->state = STATE_SETUP;
447    }
448    
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 */
452    
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) */
456    {  
457      cur = info->firstchan;
458
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 */       
464    
465    }
466   
467   splx(s);
468    
469   return 0; /* close always successful */
470 }
471
472
473 /* The probing routine - returns number of bytes needed */
474 static int alog_probe (struct isa_device *idp)
475 {
476   int unit = idp->id_unit;  /* this device unit number */
477   int iobase = idp->id_iobase; /* the base address of the unit */
478   int addr; 
479    
480   if ((unit < 0) || (unit >= NALOG) || (unit >= MAXUNITS))
481    { 
482      printf ("alog: invalid unit number (%d)\n", unit);
483      return 0;
484    }
485    
486   /* the unit number is ok, lets check if used */
487   if (alog_unit[unit]) 
488    {
489      printf ("alog: unit (%d) already attached\n", unit);
490      return 0;
491    }
492
493   if (inb (iobase+STATUS) & EOC) return 0; /* End of conv bit should be 0 */
494   for (addr=0; addr<NUMIMUXES; addr++)
495    {
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;
499    }
500  
501   return NUMPORTS; /* this device needs this many ports */ 
502 }
503
504
505 /* setup the info structure correctly for reloading clock 2 after interrupt */
506 static int sync_clock2 (int unit, long period)
507 {
508   int clockper;
509   talog_unit *info = alog_unit[unit];
510    
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 */
517   return 0;
518 }
519
520
521 /* The attachment routine - returns true on success */
522 static int alog_attach (struct isa_device *idp)
523 {
524   int unit = idp->id_unit;  /* this device unit number */   
525   talog_unit *info; /* pointer to driver specific info for unit */
526   int chan; /* the channel used for creating devfs nodes */
527    
528   idp->id_ointr = alogintr;
529   if (!(info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT)))
530    {
531      printf ("alog%d: cannot allocate driver storage\n", unit);
532      return 0;
533    }
534   alog_unit[unit] = info; /* make sure to save the pointer */
535   bzero (info, sizeof(*info)); /* clear info structure to all false */
536   info->isaunit = idp;  /* store ptr to isa device information */
537   sync_clock2 (unit, DEFAULT_MICRO_PERIOD); /* setup perlo and perhi */ 
538   info->firstchan = -1; /* channel lists are empty */
539    
540   /* insert devfs nodes */
541   
542 #ifdef DEVFS   
543   for (chan=0; chan<NUMCHANNELS; chan++)
544     info->chan[chan].devfs_token = 
545      devfs_add_devswf(&alog_cdevsw, LMINOR(unit, chan), DV_CHR,
546                       UID_ROOT, GID_WHEEL, 0400, DEVFORMAT,
547                         unit, 'a'+EMUX(chan), IMUX(chan));
548 #endif   
549
550   printf ("alog%d: %d channels, %d bytes/FIFO, %d entry trigger\n",
551             unit, NUMCHANNELS, FIFOSIZE*sizeof(u_short), 
552             DEFAULT_FIFO_TRIGGER);
553   alogintr (unit); /* start the periodic interrupting process */    
554   return 1; /* obviously successful */
555 }
556
557
558 /* Unit interrupt handling routine (interrupts generated by clock 2) */
559 static void alogintr (int unit)
560 {
561   talog_unit *info = alog_unit[unit];
562   int iobase = info->isaunit->id_iobase;
563   u_short fifoent;
564    
565    
566   if (info->firstchan >= 0) /* ? is there even a chan list to traverse */
567    switch (info->state)
568     {  
569       case STATE_READ: 
570        if (info->chan[info->curchan].status == STATUS_INUSE)
571         { 
572          if (inb (iobase+STATUS) & EOC) /* check that conversion finished */
573           printf (DEVFORMAT ": incomplete conversion\n", unit,
574                    'a'+EMUX(info->curchan), IMUX(info->curchan));           
575          else /* conversion is finished (should always be) */
576           {   
577            fifoent = (inb (iobase+ADHIGH) << 8) +
578                        inb (iobase+ADLOW);
579            if (putfifo(&(info->chan[info->curchan]), fifoent))
580             {
581                printf (DEVFORMAT ": fifo overflow\n", unit,
582                        'a'+EMUX(info->curchan), IMUX(info->curchan));       
583             }
584            if (info->chan[info->curchan].fifosize >=
585                 info->chan[info->curchan].fifotrig)
586             {
587               /* if we've reached trigger levels */
588               selwakeup (&(info->chan[info->curchan].readpoll));
589               wakeup (&(info->chan[info->curchan].fifo));
590             }
591           }
592          }
593        /* goto setup state for next channel on list */
594        if ((info->curchan = info->chan[info->curchan].nextchan) < 0)
595         info->curchan = info->firstchan;  
596        /* notice lack of break here this implys a STATE_SETUP */ 
597       case STATE_SETUP: /* set the muxes and let them settle */
598 #if NUMCHANNELS > NUMIMUXES    /* only do this if using external muxes */
599        outb (iobase+STATUS, 
600               EMUXMAKE(info->curchan) | IMUX(info->curchan) | IEN);
601        info->state = STATE_CONVERT;
602        break;
603 #endif
604       case STATE_CONVERT: 
605        outb (iobase+STATUS, 
606               EMUXMAKE(info->curchan) | IMUX(info->curchan) | IEN);
607        outb (iobase+ADHIGH, 0); /* start the conversion */
608        info->state = STATE_READ;
609        break;
610     }
611   else /* this is kind of like an idle mode */ 
612    {
613       outb (iobase+STATUS, IEN); /* no list keep getting interrupts though */
614       /* since we have no open channels spin clock rate down to 
615        * minimum to save interrupt overhead */
616       outb (iobase+CNTRCNTRL, LD2MODE4); /* counter 2 to mode 4 strobe */
617       outb (iobase+CNTR2, 0xff); /* longest period we can generate */
618       outb (iobase+CNTR2, 0xff); 
619       return;   
620    }
621   outb (iobase+CNTRCNTRL, LD2MODE4); /* counter 2 to mode 4 strobe */
622   outb (iobase+CNTR2, info->perlo); /* low part of the period count */
623   outb (iobase+CNTR2, info->perhi); /* high part of the period count */
624 }
625
626    
627 /* this will put an entry in fifo, returns 1 if the first item in 
628  * fifo was wiped (overflow) or 0 if everything went fine */
629 static int putfifo (talog_chan *pchan, u_short fifoent)
630 {   
631    pchan->fifo[pchan->fifoend] = fifoent; /* insert the entry in */
632    pchan->fifoend++; /* one more in fifo */
633    if (pchan->fifoend == FIFOSIZE) pchan->fifoend = 0; /* wrap around */ 
634    /* note: I did intend to write over the oldest entry on overflow */
635    if (pchan->fifosize == FIFOSIZE) /* overflowing state already */
636     {
637        pchan->fifostart++;
638        if (pchan->fifostart == FIFOSIZE) pchan->fifostart = 0;
639        return 1; /* we overflowed */
640     }
641    pchan->fifosize++; /* actually one bigger, else same size */
642    return 0; /* went in just fine */ 
643 }
644    
645
646 /* Driver initialization */
647 static void alog_drvinit (void *unused)
648 {
649   dev_t dev;  /* Type for holding device major/minor numbers (int) */
650
651   if (!alog_devsw_installed)
652    {
653      dev = makedev (CDEV_MAJOR, 0);  /* description of device major */
654      cdevsw_add (&dev, &alog_cdevsw, NULL);  /* put driver in cdev table */
655      alog_devsw_installed=1;
656    }
657 }
658
659 /* System initialization call instance */
660
661 SYSINIT (alogdev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE+CDEV_MAJOR,
662          alog_drvinit,NULL);
663
664 #endif