]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/i386/isa/sound/soundcard.c
Initialize isa_devtab entries for interrupt handlers in individual
[FreeBSD/FreeBSD.git] / sys / i386 / isa / sound / soundcard.c
1 /*
2  * sound/386bsd/soundcard.c
3  * 
4  * Soundcard driver for 386BSD.
5  * 
6  * Copyright by Hannu Savolainen 1993
7  * 
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are
10  * met: 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer. 2.
12  * Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution.
15  * 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  * 
28  */
29 #include "opt_devfs.h"
30
31 #include <i386/isa/sound/sound_config.h>
32 #ifdef DEVFS
33 #include <sys/devfsext.h>
34 #endif /* DEVFS */
35
36 #if NSND > 0    /* from "snd.h" */
37 #include <vm/vm.h>
38 #include <vm/pmap.h>
39 #include <sys/mman.h>
40
41 #include <i386/isa/isa_device.h>
42
43
44 /*
45 **  Register definitions for DMA controller 1 (channels 0..3):
46 */
47 #define DMA1_CHN(c)     (IO_DMA1 + 1*(2*(c)))   /* addr reg for channel c */
48 #define DMA1_SMSK       (IO_DMA1 + 1*10)        /* single mask register */
49 #define DMA1_MODE       (IO_DMA1 + 1*11)        /* mode register */
50 #define DMA1_FFC        (IO_DMA1 + 1*12)        /* clear first/last FF */
51
52 /*
53 **  Register definitions for DMA controller 2 (channels 4..7):
54 */
55 #define DMA2_CHN(c)     (IO_DMA2 + 2*(2*(c)))   /* addr reg for channel c */
56 #define DMA2_SMSK       (IO_DMA2 + 2*10)        /* single mask register */
57 #define DMA2_MODE       (IO_DMA2 + 2*11)        /* mode register */
58 #define DMA2_FFC        (IO_DMA2 + 2*12)        /* clear first/last FF */
59
60
61 #define FIX_RETURN(ret) {if ((ret)<0) return -(ret); else return 0;}
62
63 static int      soundcards_installed = 0; /* Number of installed soundcards */
64 static int      soundcard_configured = 0;
65
66 static struct fileinfo files[SND_NDEVS];
67 struct selinfo  selinfo[SND_NDEVS >> 4];
68
69 int
70 MIDIbuf_poll (int dev, struct fileinfo *file, int events, select_table * wait);
71
72 int
73 audio_poll(int dev, struct fileinfo * file, int events, select_table * wait);
74
75 int
76 sequencer_poll (int dev, struct fileinfo *file, int events, select_table * wait);
77
78 static int sndprobe    __P((struct isa_device *));
79 static int sndattach   __P((struct isa_device *));
80 static int sndmmap __P((dev_t dev, int offset, int nprot ));
81
82 static d_open_t sndopen;
83 static d_close_t sndclose;
84 static d_ioctl_t sndioctl;
85 static d_read_t sndread;
86 static d_write_t sndwrite;
87 static d_poll_t sndpoll;
88
89 static char     driver_name[] = "snd";
90
91 #define CDEV_MAJOR 30
92 static struct cdevsw snd_cdevsw = {
93         sndopen, sndclose, sndread, sndwrite,
94         sndioctl, nostop, noreset, nodevtotty,
95         sndpoll, sndmmap, nostrategy, driver_name,
96         NULL, -1,
97 };
98
99
100
101
102 static void     sound_mem_init(void);
103
104 /*
105  * for each "device XXX" entry in the config file, we have
106  * a struct isa_driver which is linked into isa_devtab_null[]
107  *
108  * XXX It is a bit stupid to call the generic routine so many times and
109  * switch then to the specific one, but the alternative way would be
110  * to replicate some code in the probe/attach routines.
111  */
112
113 struct isa_driver opldriver = {sndprobe, sndattach, "opl"};
114 struct isa_driver trixdriver = {sndprobe, sndattach, "trix"};
115 struct isa_driver trixsbdriver = {sndprobe, sndattach, "trixsb"};
116 struct isa_driver sbdriver = {sndprobe, sndattach, "sb"};
117 struct isa_driver sbxvidriver = {sndprobe, sndattach, "sbxvi"};
118 struct isa_driver sbmididriver = {sndprobe, sndattach, "sbmidi"};
119 struct isa_driver awedriver    = {sndprobe, sndattach, "awe"};
120 struct isa_driver pasdriver = {sndprobe, sndattach, "pas"};
121 struct isa_driver mpudriver = {sndprobe, sndattach, "mpu"};
122 struct isa_driver gusdriver = {sndprobe, sndattach, "gus"};
123 struct isa_driver gusxvidriver = {sndprobe, sndattach, "gusxvi"};
124 struct isa_driver gusmaxdriver = {sndprobe, sndattach, "gusmax"};
125 struct isa_driver uartdriver = {sndprobe, sndattach, "uart"};
126 struct isa_driver mssdriver = {sndprobe, sndattach, "mss"};
127 struct isa_driver cssdriver = {sndprobe, sndattach, "css"};
128 struct isa_driver sscapedriver = {sndprobe, sndattach, "sscape"};
129 struct isa_driver sscape_mssdriver = {sndprobe, sndattach, "sscape_mss"};
130
131 short ipri_to_irq(u_short ipri);
132
133 static ointhand2_t sndintr;
134
135 u_long
136 get_time(void)
137 {
138     struct timeval  timecopy;
139
140     getmicrotime(&timecopy);
141     return timecopy.tv_usec / (1000000 / hz) +
142                 (u_long) timecopy.tv_sec * hz;
143 }
144
145 static int
146 sndmmap( dev_t dev, int offset, int nprot )
147 {
148         int             unit;
149         struct dma_buffparms * dmap;
150
151         dev = minor(dev) >> 4;
152         if (dev > 0 ) return (-1);
153
154         dmap =  audio_devs[dev]->dmap_out;
155
156         if (nprot & PROT_EXEC)
157                 return( -1 );
158         dmap->mapping_flags |= DMA_MAP_MAPPED ;
159         return( i386_btop(vtophys(dmap->raw_buf) + offset) );
160 }
161
162
163 static int
164 sndread(dev_t dev, struct uio * buf, int flag)
165 {
166     int             count = buf->uio_resid;
167
168     dev = minor(dev);
169     FIX_RETURN(sound_read_sw(dev, &files[dev], buf, count));
170 }
171
172
173 static int
174 sndwrite(dev_t dev, struct uio * buf, int flag)
175 {
176     int             count = buf->uio_resid;
177
178     dev = minor(dev);
179     FIX_RETURN(sound_write_sw(dev, &files[dev], buf, count));
180 }
181
182 static int
183 sndopen(dev_t dev, int flags, int mode, struct proc * p)
184 {
185     int             retval;
186     struct fileinfo tmp_file;
187
188     dev = minor(dev);
189     if (!soundcard_configured && dev) {
190         printf("SoundCard Error: soundcard system has not been configured\n");
191         return ENODEV ;
192     }
193     tmp_file.mode = 0;
194
195     if (flags & FREAD && flags & FWRITE)
196         tmp_file.mode = OPEN_READWRITE;
197     else if (flags & FREAD)
198         tmp_file.mode = OPEN_READ;
199     else if (flags & FWRITE)
200         tmp_file.mode = OPEN_WRITE;
201
202     selinfo[dev >> 4].si_pid = 0;
203     selinfo[dev >> 4].si_flags = 0;
204     if ((retval = sound_open_sw(dev, &tmp_file)) < 0)
205         FIX_RETURN(retval);
206
207     bcopy((char *) &tmp_file, (char *) &files[dev], sizeof(tmp_file));
208
209     FIX_RETURN(retval);
210 }
211
212
213 static int
214 sndclose(dev_t dev, int flags, int mode, struct proc * p)
215 {
216     dev = minor(dev);
217     sound_release_sw(dev, &files[dev]);
218
219     return 0 ;
220 }
221
222 static int
223 sndioctl(dev_t dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
224 {
225     dev = minor(dev);
226     FIX_RETURN(sound_ioctl_sw(dev, &files[dev], cmd, arg));
227 }
228
229 int
230 sndpoll(dev_t dev, int events, struct proc * p)
231 {
232     dev = minor(dev);
233     dev = minor(dev);
234
235     /* printf ("snd_select(dev=%d, rw=%d, pid=%d)\n", dev, rw, p->p_pid); */
236 #ifdef ALLOW_POLL
237     switch (dev & 0x0f) {
238 #ifdef CONFIG_SEQUENCER
239     case SND_DEV_SEQ:
240     case SND_DEV_SEQ2:
241         return sequencer_poll(dev, &files[dev], events, p);
242         break;
243 #endif
244
245 #ifdef CONFIG_MIDI
246     case SND_DEV_MIDIN:
247         return MIDIbuf_poll(dev, &files[dev], events, p);
248         break;
249 #endif
250
251 #ifdef CONFIG_AUDIO
252     case SND_DEV_DSP:
253     case SND_DEV_DSP16:
254     case SND_DEV_AUDIO:
255
256         return audio_poll(dev, &files[dev], events, p);
257         break;
258 #endif
259
260     default:
261         return 0;
262     }
263
264 #endif  /* ALLOW_POLL */
265     DEB(printf("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
266
267     return 0 ;
268 }
269
270 /* XXX this should become ffs(ipri), perhaps -1 lr 970705 */
271 short
272 ipri_to_irq(u_short ipri)
273 {
274     /*
275      * Converts the ipri (bitmask) to the corresponding irq number
276      */
277     int             irq;
278
279     for (irq = 0; irq < 16; irq++)
280         if (ipri == (1 << irq))
281             return irq;
282
283     return -1;          /* Invalid argument */
284 }
285
286 static int
287 driver_to_voxunit(struct isa_driver * driver)
288 {
289     /*
290      * converts a sound driver pointer into the equivalent VoxWare device
291      * unit number
292      */
293     if (driver == &opldriver)
294         return (SNDCARD_ADLIB);
295     else if (driver == &sbdriver)
296         return (SNDCARD_SB);
297     else if (driver == &pasdriver)
298         return (SNDCARD_PAS);
299     else if (driver == &gusdriver)
300         return (SNDCARD_GUS);
301     else if (driver == &mpudriver)
302         return (SNDCARD_MPU401);
303     else if (driver == &sbxvidriver)
304         return (SNDCARD_SB16);
305     else if (driver == &sbmididriver)
306         return (SNDCARD_SB16MIDI);
307     else if(driver == &awedriver)
308         return(SNDCARD_AWE32);
309     else if (driver == &uartdriver)
310         return (SNDCARD_UART6850);
311     else if (driver == &gusdriver)
312         return (SNDCARD_GUS16);
313     else if (driver == &mssdriver)
314         return (SNDCARD_MSS);
315     else if (driver == &cssdriver)
316         return (SNDCARD_CS4232);
317     else if (driver == &sscapedriver)
318         return(SNDCARD_SSCAPE);
319     else if (driver == &sscape_mssdriver)
320         return(SNDCARD_SSCAPE_MSS);
321     else if (driver == &trixdriver)
322         return (SNDCARD_TRXPRO);
323     else if (driver == &trixsbdriver)
324         return (SNDCARD_TRXPRO_SB);
325     else
326         return (0);
327 }
328
329 /*
330  * very dirty: tmp_osp is allocated in sndprobe, and used at the next
331  * call in sndattach
332  */
333
334 static sound_os_info *temp_osp;
335
336 /*
337  * sndprobe is called for each isa_device. From here, a voxware unit
338  * number is determined, and the appropriate probe routine is selected.
339  * The parameters from the config line are passed to the hw_config struct.
340  */
341
342 static int
343 sndprobe(struct isa_device * dev)
344 {
345     struct address_info hw_config;
346     int             unit;
347
348     temp_osp = (sound_os_info *)malloc(sizeof(sound_os_info),
349             M_DEVBUF, M_NOWAIT);
350     if (!temp_osp)
351         panic("SOUND: Cannot allocate memory\n");
352
353     /*
354      * get config info from the kernel config. These may be overridden
355      * by the local autoconfiguration routines though (e.g. pnp stuff).
356      */
357
358     hw_config.io_base = dev->id_iobase;
359     hw_config.irq = ipri_to_irq(dev->id_irq);
360     hw_config.dma = dev->id_drq;
361
362     /*
363      * misuse the flags field for read dma. Note that, to use 0 as
364      * read dma channel, one of the high bits should be set.  lr970705 XXX
365      */
366
367     if (dev->id_flags != 0)
368         hw_config.dma2 = dev->id_flags & 0x7;
369     else
370         hw_config.dma2 = -1;
371
372     hw_config.always_detect = 0;
373     hw_config.name = NULL;
374     hw_config.card_subtype = 0;
375
376     temp_osp->unit = dev->id_unit;
377     hw_config.osp = temp_osp;
378     unit = driver_to_voxunit(dev->id_driver);
379
380     if (sndtable_probe(unit, &hw_config)) {
381         dev->id_iobase = hw_config.io_base;
382         dev->id_irq =  hw_config.irq == -1 ? 0 : (1 << hw_config.irq);
383         dev->id_drq = hw_config.dma;
384
385         if (hw_config.dma != hw_config.dma2 && ( hw_config.dma2 != -1))
386             dev->id_flags = hw_config.dma2 | 0x100; /* XXX lr */
387         else
388             dev->id_flags = 0;
389         return TRUE;
390     }
391     return 0;
392 }
393
394 static int
395 sndattach(struct isa_device * dev)
396 {
397     int             unit;
398     static int      midi_initialized = 0;
399     static int      seq_initialized = 0;
400     struct address_info hw_config;
401     char   *dname;
402     void   *tmp;
403
404     /* XXX this is probably incomplete. */
405     dname = dev->id_driver->name;
406     if (strcmp(dname, "css") == 0 || strcmp(dname, "gusxvi") == 0 ||
407         strcmp(dname, "mss") == 0)
408         dev->id_ointr = adintr;
409     if (strcmp(dname, "gus") == 0)
410         dev->id_ointr = gusintr;
411     if (strcmp(dname, "pas") == 0)
412         dev->id_ointr = pasintr;
413     if (strcmp(dname, "sb") == 0)
414         dev->id_ointr = sbintr;
415     if (strcmp(dname, "sscape_mss") == 0)
416         dev->id_ointr = sndintr;
417     if (strcmp(dname, "sscape") == 0 || strcmp(dname, "trix") == 0)
418         dev->id_ointr = sscapeintr;
419     if (strcmp(dname, "uart0") == 0)
420         dev->id_ointr = m6850intr;
421
422     unit = driver_to_voxunit(dev->id_driver);
423     hw_config.io_base = dev->id_iobase;
424     hw_config.irq = ipri_to_irq(dev->id_irq);
425     hw_config.dma = dev->id_drq;
426
427     /* misuse the flags field for read dma */
428     if (dev->id_flags != 0)
429         hw_config.dma2 = dev->id_flags & 0x7;
430     else
431         hw_config.dma2 = -1;
432
433     hw_config.card_subtype = 0;
434     hw_config.osp = temp_osp;
435
436     if (!unit)
437         return FALSE;
438
439     if (!(sndtable_init_card(unit, &hw_config))) {      /* init card */
440         printf(" <Driver not configured>");
441         return FALSE;
442     }
443     /*
444      * Init the high level sound driver
445      */
446
447     if (!(soundcards_installed = sndtable_get_cardcount())) {
448         DDB(printf("No drivers actually installed\n"));
449         return FALSE;   /* No cards detected */
450     }
451     printf("\n");
452
453 #ifdef CONFIG_AUDIO
454     if (num_audiodevs) {        /* Audio devices present */
455         DMAbuf_init();
456         sound_mem_init();
457     }
458     soundcard_configured = 1;
459 #endif
460
461     if (num_midis && !midi_initialized)
462         midi_initialized = 1;
463
464     if ((num_midis + num_synths) && !seq_initialized) {
465         seq_initialized = 1;
466         sequencer_init();
467     }
468
469     {
470         dev_t           dev;
471
472         dev = makedev(CDEV_MAJOR, 0);
473         cdevsw_add(&dev, &snd_cdevsw, NULL);
474     }
475 #ifdef DEVFS
476 #define GID_SND GID_GAMES
477 #define UID_SND UID_ROOT
478 #define PERM_SND 0660
479     /*
480      *  make links to first successfully probed device, don't do it if
481      *  duplicate creation of same node failed (ie. bad cookie returned)
482      */
483     if (dev->id_driver == &opldriver){
484         tmp = devfs_add_devswf(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_SEQ,
485                          DV_CHR, UID_SND, GID_SND, PERM_SND,
486                          "sequencer%r", dev->id_unit);
487         if (tmp) devfs_link(tmp, "sequencer");
488     } else if (dev->id_driver == &mpudriver || 
489                dev->id_driver == &sbmididriver ||
490                dev->id_driver == &uartdriver){
491         tmp = devfs_add_devswf(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_MIDIN,
492                          DV_CHR, UID_SND, GID_SND, PERM_SND,
493                          "midi%r", dev->id_unit);
494         if (tmp) devfs_link(tmp, "midi");
495     } else {
496         tmp = devfs_add_devswf(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_DSP,
497                          DV_CHR, UID_SND, GID_SND, PERM_SND,
498                          "dsp%r", dev->id_unit);
499         if (tmp) devfs_link(tmp, "dsp");
500         tmp = devfs_add_devswf(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_DSP16,
501                          DV_CHR, UID_SND, GID_SND, PERM_SND,
502                          "dspW%r", dev->id_unit);
503         if (tmp) devfs_link(tmp, "dspW");
504         tmp = devfs_add_devswf(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_AUDIO,
505                          DV_CHR, UID_SND, GID_SND, PERM_SND,
506                          "audio%r", dev->id_unit);
507         if (tmp) devfs_link(tmp, "audio");
508         tmp = devfs_add_devswf(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_CTL,
509                          DV_CHR, UID_SND, GID_SND, PERM_SND,
510                          "mixer%r", dev->id_unit);
511         if (tmp) devfs_link(tmp, "mixer");
512         tmp = devfs_add_devswf(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_STATUS,
513                          DV_CHR, UID_SND, GID_SND, PERM_SND,
514                          "sndstat%r", dev->id_unit);
515         if (tmp) devfs_link(tmp, "sndstat");
516     }
517 #endif /* DEVFS */
518     return TRUE;
519 }
520
521
522 #ifdef CONFIG_AUDIO
523
524 static void
525 alloc_dmap(int dev, int chan, struct dma_buffparms * dmap)
526 {
527     char           *tmpbuf;
528     int            i;
529
530     tmpbuf = contigmalloc(audio_devs[dev]->buffsize, M_DEVBUF, M_NOWAIT,
531                 0ul, 0xfffffful, 1ul, chan & 4 ? 0x20000ul : 0x10000ul);
532     if (tmpbuf == NULL)
533         printf("soundcard buffer alloc failed \n");
534
535     if (tmpbuf == NULL) {
536         printf("snd: Unable to allocate %d bytes of buffer\n",
537                2 * (int) audio_devs[dev]->buffsize);
538         return;
539     }
540     dmap->raw_buf = tmpbuf;
541     /*
542      * Use virtual address as the physical address, since isa_dmastart
543      * performs the phys address computation.
544      */
545
546     dmap->raw_buf_phys = (uintptr_t) tmpbuf;
547     for (i = 0; i < audio_devs[dev]->buffsize; i++)   *tmpbuf++ = 0x80; 
548
549 }
550
551 static void
552 sound_mem_init(void)
553 {
554     int             dev;
555     static u_long dsp_init_mask = 0;
556
557     for (dev = 0; dev < num_audiodevs; dev++)   /* Enumerate devices */
558         if (!(dsp_init_mask & (1 << dev)))      /* Not already done */
559             if (audio_devs[dev]->dmachan1 >= 0) {
560                 dsp_init_mask |= (1 << dev);
561                 audio_devs[dev]->buffsize = DSP_BUFFSIZE;
562                 /* Now allocate the buffers */
563                 alloc_dmap(dev, audio_devs[dev]->dmachan1,
564                         audio_devs[dev]->dmap_out);
565                 if (audio_devs[dev]->flags & DMA_DUPLEX)
566                     alloc_dmap(dev, audio_devs[dev]->dmachan2,
567                             audio_devs[dev]->dmap_in);
568             }   /* for dev */
569 }
570
571 #endif
572
573
574 int
575 snd_ioctl_return(int *addr, int value)
576 {
577     if (value < 0)
578         return value;   /* Error */
579     suword(addr, value);
580     return 0;
581 }
582
583 #define MAX_UNIT 50
584 typedef void    (*irq_proc_t) (int irq);
585 static irq_proc_t irq_proc[MAX_UNIT] = {NULL};
586 static int      irq_irq[MAX_UNIT] = {0};
587
588 int
589 snd_set_irq_handler(int int_lvl, void (*hndlr) (int), sound_os_info * osp)
590 {
591     if (osp->unit >= MAX_UNIT) {
592         printf("Sound error: Unit number too high (%d)\n", osp->unit);
593         return 0;
594     }
595     irq_proc[osp->unit] = hndlr;
596     irq_irq[osp->unit] = int_lvl;
597     return 1;
598 }
599
600 static void
601 sndintr(int unit)
602 {
603     if ( (unit >= MAX_UNIT) || (irq_proc[unit] == NULL) )
604         return;
605
606     irq_proc[unit] (irq_irq[unit]);     /* Call the installed handler */
607 }
608
609 void
610 conf_printf(char *name, struct address_info * hw_config)
611 {
612     if (!trace_init)
613         return;
614
615     printf("snd0: <%s> ", name);
616 #if 0
617     if (hw_config->io_base != -1 ) 
618     printf("at 0x%03x", hw_config->io_base);
619
620     if (hw_config->irq != -1 )
621         printf(" irq %d", hw_config->irq);
622
623     if (hw_config->dma != -1 || hw_config->dma2 != -1) {
624         printf(" dma %d", hw_config->dma);
625         if (hw_config->dma2 != -1)
626             printf(",%d", hw_config->dma2);
627     }
628 #endif
629
630 }
631
632 void
633 conf_printf2(char *name, int base, int irq, int dma, int dma2)
634 {
635     if (!trace_init)
636         return;
637
638     printf("snd0: <%s> ", name);
639 #if 0
640     if (hw_config->io_base != -1 ) 
641     printf("at 0x%03x", hw_config->io_base);
642
643     if (irq)
644         printf(" irq %d", irq);
645
646     if (dma != -1 || dma2 != -1) {
647         printf(" dma %d", dma);
648         if (dma2 != -1)
649             printf(",%d", dma2);
650     }
651 #endif
652
653 }
654
655
656 void tenmicrosec (int j)
657 {
658   int             i, k;
659   for (k = 0; k < j/10 ; k++) {
660       for (i = 0; i < 16; i++)
661           inb (0x80);
662   }
663 }
664
665 #endif  /* NSND > 0 */
666
667
668
669