DEV messaging stage 1/4: Rearrange struct cdevsw and add a message port
[dragonfly.git] / sys / dev / sound / isa / i386 / 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  * $FreeBSD: src/sys/i386/isa/sound/soundcard.c,v 1.87 1999/12/20 18:05:01 eivind Exp $
29  * $DragonFly: src/sys/dev/sound/isa/i386/Attic/soundcard.c,v 1.3 2003/07/21 05:50:41 dillon Exp $
30  *
31  */
32 #include <i386/isa/sound/sound_config.h>
33 #if NSND > 0    /* from "snd.h" */
34 #include "uart.h"
35
36 #include <sys/select.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
81 static d_open_t sndopen;
82 static d_close_t sndclose;
83 static d_ioctl_t sndioctl;
84 static d_read_t sndread;
85 static d_write_t sndwrite;
86 static d_poll_t sndpoll;
87 static d_mmap_t sndmmap;
88
89 static char     driver_name[] = "snd";
90
91 #define CDEV_MAJOR 30
92 static struct cdevsw snd_cdevsw = {
93         /* name */      driver_name,
94         /* maj */       CDEV_MAJOR,
95         /* flags */     0,
96         /* port */      NULL,
97         /* autoq */     0,
98
99         /* open */      sndopen,
100         /* close */     sndclose,
101         /* read */      sndread,
102         /* write */     sndwrite,
103         /* ioctl */     sndioctl,
104         /* poll */      sndpoll,
105         /* mmap */      sndmmap,
106         /* strategy */  nostrategy,
107         /* dump */      nodump,
108         /* psize */     nopsize
109 };
110
111
112
113
114 static void     sound_mem_init(void);
115
116 /*
117  * for each "device XXX" entry in the config file, we have
118  * a struct isa_driver which is linked into isa_devtab_null[]
119  *
120  * XXX It is a bit stupid to call the generic routine so many times and
121  * switch then to the specific one, but the alternative way would be
122  * to replicate some code in the probe/attach routines.
123  */
124
125 struct isa_driver opldriver = {sndprobe, sndattach, "opl"};
126 struct isa_driver trixdriver = {sndprobe, sndattach, "trix"};
127 struct isa_driver trixsbdriver = {sndprobe, sndattach, "trixsb"};
128 struct isa_driver sbdriver = {sndprobe, sndattach, "sb"};
129 struct isa_driver sbxvidriver = {sndprobe, sndattach, "sbxvi"};
130 struct isa_driver sbmididriver = {sndprobe, sndattach, "sbmidi"};
131 struct isa_driver awedriver    = {sndprobe, sndattach, "awe"};
132 struct isa_driver pasdriver = {sndprobe, sndattach, "pas"};
133 struct isa_driver mpudriver = {sndprobe, sndattach, "mpu"};
134 struct isa_driver gusdriver = {sndprobe, sndattach, "gus"};
135 struct isa_driver gusxvidriver = {sndprobe, sndattach, "gusxvi"};
136 struct isa_driver gusmaxdriver = {sndprobe, sndattach, "gusmax"};
137 struct isa_driver uartdriver = {sndprobe, sndattach, "uart"};
138 struct isa_driver mssdriver = {sndprobe, sndattach, "mss"};
139 struct isa_driver cssdriver = {sndprobe, sndattach, "css"};
140 struct isa_driver sscapedriver = {sndprobe, sndattach, "sscape"};
141 struct isa_driver sscape_mssdriver = {sndprobe, sndattach, "sscape_mss"};
142 struct isa_driver nssdriver = {sndprobe, sndattach, "nss"};
143
144 short ipri_to_irq(u_short ipri);
145
146 static ointhand2_t sndintr;
147
148 u_long
149 get_time(void)
150 {
151     struct timeval  timecopy;
152
153     getmicrotime(&timecopy);
154     return timecopy.tv_usec / (1000000 / hz) +
155                 (u_long) timecopy.tv_sec * hz;
156 }
157
158 static int
159 sndmmap( dev_t dev, vm_offset_t offset, int nprot )
160 {
161         struct dma_buffparms * dmap;
162         u_int min = minor(dev) >> 4;
163
164         if (min > 0 ) return (-1);
165
166         dmap =  audio_devs[min]->dmap_out;
167
168         if (nprot & PROT_EXEC)
169                 return( -1 );
170         dmap->mapping_flags |= DMA_MAP_MAPPED ;
171         return( i386_btop(vtophys(dmap->raw_buf) + offset) );
172 }
173
174
175 static int
176 sndread(dev_t dev, struct uio * buf, int flag)
177 {
178     int             count = buf->uio_resid;
179     u_int min = minor(dev);
180
181     FIX_RETURN(sound_read_sw(min, &files[min], buf, count));
182 }
183
184
185 static int
186 sndwrite(dev_t dev, struct uio * buf, int flag)
187 {
188     int             count = buf->uio_resid;
189     u_int min = minor(dev);
190
191     FIX_RETURN(sound_write_sw(min, &files[min], buf, count));
192 }
193
194 static int
195 sndopen(dev_t dev, int flags, int mode, struct proc * p)
196 {
197     int             retval;
198     struct fileinfo tmp_file;
199     u_int min = minor(dev);
200
201     if (!soundcard_configured && min) {
202         printf("SoundCard Error: soundcard system has not been configured\n");
203         return ENODEV ;
204     }
205     tmp_file.mode = 0;
206
207     if (flags & FREAD && flags & FWRITE)
208         tmp_file.mode = OPEN_READWRITE;
209     else if (flags & FREAD)
210         tmp_file.mode = OPEN_READ;
211     else if (flags & FWRITE)
212         tmp_file.mode = OPEN_WRITE;
213
214     selinfo[min >> 4].si_pid = 0;
215     selinfo[min >> 4].si_flags = 0;
216     if ((retval = sound_open_sw(min, &tmp_file)) < 0)
217         FIX_RETURN(retval);
218
219     bcopy((char *) &tmp_file, (char *) &files[min], sizeof(tmp_file));
220
221     FIX_RETURN(retval);
222 }
223
224
225 static int
226 sndclose(dev_t dev, int flags, int mode, struct proc * p)
227 {
228     u_int min = minor(dev);
229
230     sound_release_sw(min, &files[min]);
231     return 0 ;
232 }
233
234 static int
235 sndioctl(dev_t dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
236 {
237     u_int min = minor(dev);
238     FIX_RETURN(sound_ioctl_sw(min, &files[min], cmd, arg));
239 }
240
241 int
242 sndpoll(dev_t dev, int events, struct proc * p)
243 {
244     u_int min = minor(dev);
245
246     /* printf ("snd_select(dev=%d, rw=%d, pid=%d)\n", min, rw, p->p_pid); */
247 #ifdef ALLOW_POLL
248     switch (min & 0x0f) {
249 #ifdef CONFIG_SEQUENCER
250     case SND_DEV_SEQ:
251     case SND_DEV_SEQ2:
252         return sequencer_poll(min, &files[min], events, p);
253         break;
254 #endif
255
256 #ifdef CONFIG_MIDI
257     case SND_DEV_MIDIN:
258         return MIDIbuf_poll(min, &files[min], events, p);
259         break;
260 #endif
261
262 #ifdef CONFIG_AUDIO
263     case SND_DEV_DSP:
264     case SND_DEV_DSP16:
265     case SND_DEV_AUDIO:
266
267         return audio_poll(min, &files[min], events, p);
268         break;
269 #endif
270
271     default:
272         return 0;
273     }
274
275 #endif  /* ALLOW_POLL */
276     DEB(printf("sound_ioctl(min=%d, cmd=0x%x, arg=0x%x)\n", min, cmd, arg));
277
278     return 0 ;
279 }
280
281 /* XXX this should become ffs(ipri), perhaps -1 lr 970705 */
282 short
283 ipri_to_irq(u_short ipri)
284 {
285     /*
286      * Converts the ipri (bitmask) to the corresponding irq number
287      */
288     int             irq;
289
290     for (irq = 0; irq < 16; irq++)
291         if (ipri == (1 << irq))
292             return irq;
293
294     return -1;          /* Invalid argument */
295 }
296
297 static int
298 driver_to_voxunit(struct isa_driver * driver)
299 {
300     /*
301      * converts a sound driver pointer into the equivalent VoxWare device
302      * unit number
303      */
304     if (driver == &opldriver)
305         return (SNDCARD_ADLIB);
306     else if (driver == &sbdriver)
307         return (SNDCARD_SB);
308     else if (driver == &pasdriver)
309         return (SNDCARD_PAS);
310     else if (driver == &gusdriver)
311         return (SNDCARD_GUS);
312     else if (driver == &mpudriver)
313         return (SNDCARD_MPU401);
314     else if (driver == &sbxvidriver)
315         return (SNDCARD_SB16);
316     else if (driver == &sbmididriver)
317         return (SNDCARD_SB16MIDI);
318     else if(driver == &awedriver)
319         return(SNDCARD_AWE32);
320     else if (driver == &uartdriver)
321         return (SNDCARD_UART6850);
322     else if (driver == &gusdriver)
323         return (SNDCARD_GUS16);
324     else if (driver == &mssdriver)
325         return (SNDCARD_MSS);
326     else if (driver == &cssdriver)
327         return (SNDCARD_CS4232);
328     else if (driver == &sscapedriver)
329         return(SNDCARD_SSCAPE);
330     else if (driver == &sscape_mssdriver)
331         return(SNDCARD_SSCAPE_MSS);
332     else if (driver == &trixdriver)
333         return (SNDCARD_TRXPRO);
334     else if (driver == &trixsbdriver)
335         return (SNDCARD_TRXPRO_SB);
336     else if (driver == &nssdriver)
337         return (SNDCARD_NSS);
338     else
339         return (0);
340 }
341
342 /*
343  * very dirty: tmp_osp is allocated in sndprobe, and used at the next
344  * call in sndattach
345  */
346
347 static sound_os_info *temp_osp;
348
349 /*
350  * sndprobe is called for each isa_device. From here, a voxware unit
351  * number is determined, and the appropriate probe routine is selected.
352  * The parameters from the config line are passed to the hw_config struct.
353  */
354
355 static int
356 sndprobe(struct isa_device * dev)
357 {
358     struct address_info hw_config;
359     int             unit;
360
361     temp_osp = (sound_os_info *)malloc(sizeof(sound_os_info),
362             M_DEVBUF, M_NOWAIT);
363     if (!temp_osp)
364         panic("SOUND: Cannot allocate memory\n");
365
366     /*
367      * get config info from the kernel config. These may be overridden
368      * by the local autoconfiguration routines though (e.g. pnp stuff).
369      */
370
371     hw_config.io_base = dev->id_iobase;
372     hw_config.irq = ipri_to_irq(dev->id_irq);
373     hw_config.dma = dev->id_drq;
374
375     /*
376      * misuse the flags field for read dma. Note that, to use 0 as
377      * read dma channel, one of the high bits should be set.  lr970705 XXX
378      */
379
380     if (dev->id_flags != 0)
381         hw_config.dma2 = dev->id_flags & 0x7;
382     else
383         hw_config.dma2 = -1;
384
385     hw_config.always_detect = 0;
386     hw_config.name = NULL;
387     hw_config.card_subtype = 0;
388
389     temp_osp->unit = dev->id_unit;
390     hw_config.osp = temp_osp;
391     unit = driver_to_voxunit(dev->id_driver);
392
393     if (sndtable_probe(unit, &hw_config)) {
394         dev->id_iobase = hw_config.io_base;
395         dev->id_irq =  hw_config.irq == -1 ? 0 : (1 << hw_config.irq);
396         dev->id_drq = hw_config.dma;
397
398         if (hw_config.dma != hw_config.dma2 && ( hw_config.dma2 != -1))
399             dev->id_flags = hw_config.dma2 | 0x100; /* XXX lr */
400         else
401             dev->id_flags = 0;
402         return TRUE;
403     }
404     return 0;
405 }
406
407 static int
408 sndattach(struct isa_device * dev)
409 {
410     int             unit;
411     static int      midi_initialized = 0;
412     static int      seq_initialized = 0;
413     struct address_info hw_config;
414     char   *dname;
415
416     /*
417      * Associate interrupt handlers with devices.  XXX this may be incomplete.
418      */
419     dname = dev->id_driver->name;
420 #if defined(CONFIG_AD1848)
421     if (strcmp(dname, "css") == 0 || strcmp(dname, "gusxvi") == 0 ||
422         strcmp(dname, "mss") == 0)
423         dev->id_ointr = adintr;
424 #endif
425 #ifdef CONFIG_GUS
426     if (strcmp(dname, "gus") == 0)
427         dev->id_ointr = gusintr;
428 #endif
429 #ifdef CONFIG_PAS
430     if (strcmp(dname, "pas") == 0)
431         dev->id_ointr = pasintr;
432 #endif
433 #if NSB > 0 && (defined(CONFIG_MIDI) || defined(CONFIG_AUDIO))
434     if (strcmp(dname, "sb") == 0)
435         dev->id_ointr = sbintr;
436 #endif
437     if (strcmp(dname, "sscape_mss") == 0)
438         dev->id_ointr = sndintr;
439 #if NSSCAPE > 0
440     if (strcmp(dname, "sscape") == 0 || strcmp(dname, "trix") == 0)
441         dev->id_ointr = sscapeintr;
442 #endif
443 #if NUART > 0
444     if (strcmp(dname, "uart0") == 0)
445         dev->id_ointr = m6850intr;
446 #endif
447 #if NMPU > 0 && defined(CONFIG_MIDI)
448     if (strcmp(dname, "mpu") == 0)
449         dev->id_ointr = mpuintr;
450 #endif
451 #if NNSS > 0
452     if (strcmp(dname, "nss") == 0)
453         dev->id_ointr = nssintr;
454 #endif
455
456     unit = driver_to_voxunit(dev->id_driver);
457     hw_config.io_base = dev->id_iobase;
458     hw_config.irq = ipri_to_irq(dev->id_irq);
459     hw_config.dma = dev->id_drq;
460
461     /* misuse the flags field for read dma */
462     if (dev->id_flags != 0)
463         hw_config.dma2 = dev->id_flags & 0x7;
464     else
465         hw_config.dma2 = -1;
466
467     hw_config.card_subtype = 0;
468     hw_config.osp = temp_osp;
469
470     if (!unit)
471         return FALSE;
472
473     if (!(sndtable_init_card(unit, &hw_config))) {      /* init card */
474         printf(" <Driver not configured>");
475         return FALSE;
476     }
477     /*
478      * Init the high level sound driver
479      */
480
481     if (!(soundcards_installed = sndtable_get_cardcount())) {
482         DDB(printf("No drivers actually installed\n"));
483         return FALSE;   /* No cards detected */
484     }
485     printf("\n");
486
487 #ifdef CONFIG_AUDIO
488     if (num_audiodevs) {        /* Audio devices present */
489         DMAbuf_init();
490         sound_mem_init();
491     }
492     soundcard_configured = 1;
493 #endif
494
495     if (num_midis && !midi_initialized)
496         midi_initialized = 1;
497
498     if ((num_midis + num_synths) && !seq_initialized) {
499         seq_initialized = 1;
500         sequencer_init();
501     }
502
503     cdevsw_add(&snd_cdevsw);
504 #define GID_SND GID_GAMES
505 #define UID_SND UID_ROOT
506 #define PERM_SND 0660
507     /*
508      *  make links to first successfully probed device, don't do it if
509      *  duplicate creation of same node failed (ie. bad cookie returned)
510      */
511     if (dev->id_driver == &opldriver){
512         make_dev(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_SEQ,
513             UID_SND, GID_SND, PERM_SND, "sequencer%r", dev->id_unit);
514     } else if (dev->id_driver == &mpudriver || 
515                dev->id_driver == &sbmididriver ||
516                dev->id_driver == &uartdriver){
517         make_dev(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_MIDIN,
518             UID_SND, GID_SND, PERM_SND, "midi%r", dev->id_unit);
519     } else {
520         make_dev(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_DSP,
521             UID_SND, GID_SND, PERM_SND, "dsp%r", dev->id_unit);
522         make_dev(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_DSP16,
523             UID_SND, GID_SND, PERM_SND, "dspW%r", dev->id_unit);
524         make_dev(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_AUDIO,
525             UID_SND, GID_SND, PERM_SND, "audio%r", dev->id_unit);
526         make_dev(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_CTL,
527             UID_SND, GID_SND, PERM_SND, "mixer%r", dev->id_unit);
528         make_dev(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_STATUS,
529             UID_SND, GID_SND, PERM_SND, "sndstat%r", dev->id_unit);
530     }
531     return TRUE;
532 }
533
534
535 #ifdef CONFIG_AUDIO
536
537 static void
538 alloc_dmap(int dev, int chan, struct dma_buffparms * dmap)
539 {
540     char           *tmpbuf;
541     int            i;
542
543     tmpbuf = contigmalloc(audio_devs[dev]->buffsize, M_DEVBUF, M_NOWAIT,
544                 0ul, 0xfffffful, 1ul, chan & 4 ? 0x20000ul : 0x10000ul);
545     if (tmpbuf == NULL)
546         printf("soundcard buffer alloc failed \n");
547
548     if (tmpbuf == NULL) {
549         printf("snd: Unable to allocate %d bytes of buffer\n",
550                2 * (int) audio_devs[dev]->buffsize);
551         return;
552     }
553     dmap->raw_buf = tmpbuf;
554     /*
555      * Use virtual address as the physical address, since isa_dmastart
556      * performs the phys address computation.
557      */
558
559     dmap->raw_buf_phys = (uintptr_t) tmpbuf;
560     for (i = 0; i < audio_devs[dev]->buffsize; i++)   *tmpbuf++ = 0x80; 
561
562 }
563
564 static void
565 sound_mem_init(void)
566 {
567     int             dev;
568     static u_long dsp_init_mask = 0;
569
570     for (dev = 0; dev < num_audiodevs; dev++)   /* Enumerate devices */
571         if (!(dsp_init_mask & (1 << dev)))      /* Not already done */
572             if (audio_devs[dev]->dmachan1 >= 0) {
573                 dsp_init_mask |= (1 << dev);
574                 audio_devs[dev]->buffsize = DSP_BUFFSIZE;
575                 /* Now allocate the buffers */
576                 alloc_dmap(dev, audio_devs[dev]->dmachan1,
577                         audio_devs[dev]->dmap_out);
578                 if (audio_devs[dev]->flags & DMA_DUPLEX)
579                     alloc_dmap(dev, audio_devs[dev]->dmachan2,
580                             audio_devs[dev]->dmap_in);
581             }   /* for dev */
582 }
583
584 #endif
585
586
587 int
588 snd_ioctl_return(int *addr, int value)
589 {
590     if (value < 0)
591         return value;   /* Error */
592     suword(addr, value);
593     return 0;
594 }
595
596 #define MAX_UNIT 50
597 typedef void    (*irq_proc_t) (int irq);
598 static irq_proc_t irq_proc[MAX_UNIT] = {NULL};
599 static int      irq_irq[MAX_UNIT] = {0};
600
601 int
602 snd_set_irq_handler(int int_lvl, void (*hndlr) (int), sound_os_info * osp)
603 {
604     if (osp->unit >= MAX_UNIT) {
605         printf("Sound error: Unit number too high (%d)\n", osp->unit);
606         return 0;
607     }
608     irq_proc[osp->unit] = hndlr;
609     irq_irq[osp->unit] = int_lvl;
610     return 1;
611 }
612
613 static void
614 sndintr(int unit)
615 {
616     if ( (unit >= MAX_UNIT) || (irq_proc[unit] == NULL) )
617         return;
618
619     irq_proc[unit] (irq_irq[unit]);     /* Call the installed handler */
620 }
621
622 void
623 conf_printf(char *name, struct address_info * hw_config)
624 {
625     if (!trace_init)
626         return;
627
628     printf("snd0: <%s> ", name);
629 #if 0
630     if (hw_config->io_base != -1 ) 
631     printf("at 0x%03x", hw_config->io_base);
632
633     if (hw_config->irq != -1 )
634         printf(" irq %d", hw_config->irq);
635
636     if (hw_config->dma != -1 || hw_config->dma2 != -1) {
637         printf(" dma %d", hw_config->dma);
638         if (hw_config->dma2 != -1)
639             printf(",%d", hw_config->dma2);
640     }
641 #endif
642
643 }
644
645 void
646 conf_printf2(char *name, int base, int irq, int dma, int dma2)
647 {
648     if (!trace_init)
649         return;
650
651     printf("snd0: <%s> ", name);
652 #if 0
653     if (hw_config->io_base != -1 ) 
654     printf("at 0x%03x", hw_config->io_base);
655
656     if (irq)
657         printf(" irq %d", irq);
658
659     if (dma != -1 || dma2 != -1) {
660         printf(" dma %d", dma);
661         if (dma2 != -1)
662             printf(",%d", dma2);
663     }
664 #endif
665
666 }
667
668
669 void tenmicrosec (int j)
670 {
671   int             i, k;
672   for (k = 0; k < j/10 ; k++) {
673       for (i = 0; i < 16; i++)
674           inb (0x80);
675   }
676 }
677
678 #endif  /* NSND > 0 */
679
680
681
682