/* * sound/386bsd/soundcard.c * * Soundcard driver for 386BSD. * * Copyright by Hannu Savolainen 1993 * * 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. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. * * $FreeBSD: src/sys/i386/isa/sound/soundcard.c,v 1.87 1999/12/20 18:05:01 eivind Exp $ * $DragonFly: src/sys/dev/sound/isa/i386/Attic/soundcard.c,v 1.6 2004/05/13 23:49:20 dillon Exp $ * */ #include "use_snd.h" #include "sound_config.h" #if NSND > 0 /* from "snd.h" */ #include "use_uart.h" #include #include #include #include #include /* ** Register definitions for DMA controller 1 (channels 0..3): */ #define DMA1_CHN(c) (IO_DMA1 + 1*(2*(c))) /* addr reg for channel c */ #define DMA1_SMSK (IO_DMA1 + 1*10) /* single mask register */ #define DMA1_MODE (IO_DMA1 + 1*11) /* mode register */ #define DMA1_FFC (IO_DMA1 + 1*12) /* clear first/last FF */ /* ** Register definitions for DMA controller 2 (channels 4..7): */ #define DMA2_CHN(c) (IO_DMA2 + 2*(2*(c))) /* addr reg for channel c */ #define DMA2_SMSK (IO_DMA2 + 2*10) /* single mask register */ #define DMA2_MODE (IO_DMA2 + 2*11) /* mode register */ #define DMA2_FFC (IO_DMA2 + 2*12) /* clear first/last FF */ #define FIX_RETURN(ret) {if ((ret)<0) return -(ret); else return 0;} static int soundcards_installed = 0; /* Number of installed soundcards */ static int soundcard_configured = 0; static struct fileinfo files[SND_NDEVS]; struct selinfo selinfo[SND_NDEVS >> 4]; int MIDIbuf_poll (int dev, struct fileinfo *file, int events, select_table * wait); int audio_poll(int dev, struct fileinfo * file, int events, select_table * wait); int sequencer_poll (int dev, struct fileinfo *file, int events, select_table * wait); static int sndprobe (struct isa_device *); static int sndattach (struct isa_device *); static d_open_t sndopen; static d_close_t sndclose; static d_ioctl_t sndioctl; static d_read_t sndread; static d_write_t sndwrite; static d_poll_t sndpoll; static d_mmap_t sndmmap; static char driver_name[] = "snd"; #define CDEV_MAJOR 30 static struct cdevsw snd_cdevsw = { /* name */ driver_name, /* maj */ CDEV_MAJOR, /* flags */ 0, /* port */ NULL, /* clone */ NULL, /* open */ sndopen, /* close */ sndclose, /* read */ sndread, /* write */ sndwrite, /* ioctl */ sndioctl, /* poll */ sndpoll, /* mmap */ sndmmap, /* strategy */ nostrategy, /* dump */ nodump, /* psize */ nopsize }; static void sound_mem_init(void); /* * for each "device XXX" entry in the config file, we have * a struct isa_driver which is linked into isa_devtab_null[] * * XXX It is a bit stupid to call the generic routine so many times and * switch then to the specific one, but the alternative way would be * to replicate some code in the probe/attach routines. */ struct isa_driver opldriver = {sndprobe, sndattach, "opl"}; struct isa_driver trixdriver = {sndprobe, sndattach, "trix"}; struct isa_driver trixsbdriver = {sndprobe, sndattach, "trixsb"}; struct isa_driver sbdriver = {sndprobe, sndattach, "sb"}; struct isa_driver sbxvidriver = {sndprobe, sndattach, "sbxvi"}; struct isa_driver sbmididriver = {sndprobe, sndattach, "sbmidi"}; struct isa_driver awedriver = {sndprobe, sndattach, "awe"}; struct isa_driver pasdriver = {sndprobe, sndattach, "pas"}; struct isa_driver mpudriver = {sndprobe, sndattach, "mpu"}; struct isa_driver gusdriver = {sndprobe, sndattach, "gus"}; struct isa_driver gusxvidriver = {sndprobe, sndattach, "gusxvi"}; struct isa_driver gusmaxdriver = {sndprobe, sndattach, "gusmax"}; struct isa_driver uartdriver = {sndprobe, sndattach, "uart"}; struct isa_driver mssdriver = {sndprobe, sndattach, "mss"}; struct isa_driver cssdriver = {sndprobe, sndattach, "css"}; struct isa_driver sscapedriver = {sndprobe, sndattach, "sscape"}; struct isa_driver sscape_mssdriver = {sndprobe, sndattach, "sscape_mss"}; struct isa_driver nssdriver = {sndprobe, sndattach, "nss"}; short ipri_to_irq(u_short ipri); static ointhand2_t sndintr; u_long get_time(void) { struct timeval timecopy; getmicrotime(&timecopy); return timecopy.tv_usec / (1000000 / hz) + (u_long) timecopy.tv_sec * hz; } static int sndmmap( dev_t dev, vm_offset_t offset, int nprot ) { struct dma_buffparms * dmap; u_int min = minor(dev) >> 4; if (min > 0 ) return (-1); dmap = audio_devs[min]->dmap_out; if (nprot & PROT_EXEC) return( -1 ); dmap->mapping_flags |= DMA_MAP_MAPPED ; return( i386_btop(vtophys(dmap->raw_buf) + offset) ); } static int sndread(dev_t dev, struct uio * buf, int flag) { int count = buf->uio_resid; u_int min = minor(dev); FIX_RETURN(sound_read_sw(min, &files[min], buf, count)); } static int sndwrite(dev_t dev, struct uio * buf, int flag) { int count = buf->uio_resid; u_int min = minor(dev); FIX_RETURN(sound_write_sw(min, &files[min], buf, count)); } static int sndopen(dev_t dev, int flags, int mode, struct proc * p) { int retval; struct fileinfo tmp_file; u_int min = minor(dev); if (!soundcard_configured && min) { printf("SoundCard Error: soundcard system has not been configured\n"); return ENODEV ; } tmp_file.mode = 0; if (flags & FREAD && flags & FWRITE) tmp_file.mode = OPEN_READWRITE; else if (flags & FREAD) tmp_file.mode = OPEN_READ; else if (flags & FWRITE) tmp_file.mode = OPEN_WRITE; selinfo[min >> 4].si_pid = 0; selinfo[min >> 4].si_flags = 0; if ((retval = sound_open_sw(min, &tmp_file)) < 0) FIX_RETURN(retval); bcopy((char *) &tmp_file, (char *) &files[min], sizeof(tmp_file)); FIX_RETURN(retval); } static int sndclose(dev_t dev, int flags, int mode, struct proc * p) { u_int min = minor(dev); sound_release_sw(min, &files[min]); return 0 ; } static int sndioctl(dev_t dev, u_long cmd, caddr_t arg, int mode, struct proc * p) { u_int min = minor(dev); FIX_RETURN(sound_ioctl_sw(min, &files[min], cmd, arg)); } int sndpoll(dev_t dev, int events, struct proc * p) { u_int min = minor(dev); /* printf ("snd_select(dev=%d, rw=%d, pid=%d)\n", min, rw, p->p_pid); */ #ifdef ALLOW_POLL switch (min & 0x0f) { #ifdef CONFIG_SEQUENCER case SND_DEV_SEQ: case SND_DEV_SEQ2: return sequencer_poll(min, &files[min], events, p); break; #endif #ifdef CONFIG_MIDI case SND_DEV_MIDIN: return MIDIbuf_poll(min, &files[min], events, p); break; #endif #ifdef CONFIG_AUDIO case SND_DEV_DSP: case SND_DEV_DSP16: case SND_DEV_AUDIO: return audio_poll(min, &files[min], events, p); break; #endif default: return 0; } #endif /* ALLOW_POLL */ DEB(printf("sound_ioctl(min=%d, cmd=0x%x, arg=0x%x)\n", min, cmd, arg)); return 0 ; } /* XXX this should become ffs(ipri), perhaps -1 lr 970705 */ short ipri_to_irq(u_short ipri) { /* * Converts the ipri (bitmask) to the corresponding irq number */ int irq; for (irq = 0; irq < 16; irq++) if (ipri == (1 << irq)) return irq; return -1; /* Invalid argument */ } static int driver_to_voxunit(struct isa_driver * driver) { /* * converts a sound driver pointer into the equivalent VoxWare device * unit number */ if (driver == &opldriver) return (SNDCARD_ADLIB); else if (driver == &sbdriver) return (SNDCARD_SB); else if (driver == &pasdriver) return (SNDCARD_PAS); else if (driver == &gusdriver) return (SNDCARD_GUS); else if (driver == &mpudriver) return (SNDCARD_MPU401); else if (driver == &sbxvidriver) return (SNDCARD_SB16); else if (driver == &sbmididriver) return (SNDCARD_SB16MIDI); else if(driver == &awedriver) return(SNDCARD_AWE32); else if (driver == &uartdriver) return (SNDCARD_UART6850); else if (driver == &gusdriver) return (SNDCARD_GUS16); else if (driver == &mssdriver) return (SNDCARD_MSS); else if (driver == &cssdriver) return (SNDCARD_CS4232); else if (driver == &sscapedriver) return(SNDCARD_SSCAPE); else if (driver == &sscape_mssdriver) return(SNDCARD_SSCAPE_MSS); else if (driver == &trixdriver) return (SNDCARD_TRXPRO); else if (driver == &trixsbdriver) return (SNDCARD_TRXPRO_SB); else if (driver == &nssdriver) return (SNDCARD_NSS); else return (0); } /* * very dirty: tmp_osp is allocated in sndprobe, and used at the next * call in sndattach */ static sound_os_info *temp_osp; /* * sndprobe is called for each isa_device. From here, a voxware unit * number is determined, and the appropriate probe routine is selected. * The parameters from the config line are passed to the hw_config struct. */ static int sndprobe(struct isa_device * dev) { struct address_info hw_config; int unit; temp_osp = (sound_os_info *)malloc(sizeof(sound_os_info), M_DEVBUF, M_NOWAIT); if (!temp_osp) panic("SOUND: Cannot allocate memory\n"); /* * get config info from the kernel config. These may be overridden * by the local autoconfiguration routines though (e.g. pnp stuff). */ hw_config.io_base = dev->id_iobase; hw_config.irq = ipri_to_irq(dev->id_irq); hw_config.dma = dev->id_drq; /* * misuse the flags field for read dma. Note that, to use 0 as * read dma channel, one of the high bits should be set. lr970705 XXX */ if (dev->id_flags != 0) hw_config.dma2 = dev->id_flags & 0x7; else hw_config.dma2 = -1; hw_config.always_detect = 0; hw_config.name = NULL; hw_config.card_subtype = 0; temp_osp->unit = dev->id_unit; hw_config.osp = temp_osp; unit = driver_to_voxunit(dev->id_driver); if (sndtable_probe(unit, &hw_config)) { dev->id_iobase = hw_config.io_base; dev->id_irq = hw_config.irq == -1 ? 0 : (1 << hw_config.irq); dev->id_drq = hw_config.dma; if (hw_config.dma != hw_config.dma2 && ( hw_config.dma2 != -1)) dev->id_flags = hw_config.dma2 | 0x100; /* XXX lr */ else dev->id_flags = 0; return TRUE; } return 0; } static int sndattach(struct isa_device * dev) { int unit; static int midi_initialized = 0; static int seq_initialized = 0; struct address_info hw_config; char *dname; /* * Associate interrupt handlers with devices. XXX this may be incomplete. */ dname = dev->id_driver->name; #if defined(CONFIG_AD1848) if (strcmp(dname, "css") == 0 || strcmp(dname, "gusxvi") == 0 || strcmp(dname, "mss") == 0) dev->id_ointr = adintr; #endif #ifdef CONFIG_GUS if (strcmp(dname, "gus") == 0) dev->id_ointr = gusintr; #endif #ifdef CONFIG_PAS if (strcmp(dname, "pas") == 0) dev->id_ointr = pasintr; #endif #if NSB > 0 && (defined(CONFIG_MIDI) || defined(CONFIG_AUDIO)) if (strcmp(dname, "sb") == 0) dev->id_ointr = sbintr; #endif if (strcmp(dname, "sscape_mss") == 0) dev->id_ointr = sndintr; #if NSSCAPE > 0 if (strcmp(dname, "sscape") == 0 || strcmp(dname, "trix") == 0) dev->id_ointr = sscapeintr; #endif #if NUART > 0 if (strcmp(dname, "uart0") == 0) dev->id_ointr = m6850intr; #endif #if NMPU > 0 && defined(CONFIG_MIDI) if (strcmp(dname, "mpu") == 0) dev->id_ointr = mpuintr; #endif #if NNSS > 0 if (strcmp(dname, "nss") == 0) dev->id_ointr = nssintr; #endif unit = driver_to_voxunit(dev->id_driver); hw_config.io_base = dev->id_iobase; hw_config.irq = ipri_to_irq(dev->id_irq); hw_config.dma = dev->id_drq; /* misuse the flags field for read dma */ if (dev->id_flags != 0) hw_config.dma2 = dev->id_flags & 0x7; else hw_config.dma2 = -1; hw_config.card_subtype = 0; hw_config.osp = temp_osp; if (!unit) return FALSE; if (!(sndtable_init_card(unit, &hw_config))) { /* init card */ printf(" "); return FALSE; } /* * Init the high level sound driver */ if (!(soundcards_installed = sndtable_get_cardcount())) { DDB(printf("No drivers actually installed\n")); return FALSE; /* No cards detected */ } printf("\n"); #ifdef CONFIG_AUDIO if (num_audiodevs) { /* Audio devices present */ DMAbuf_init(); sound_mem_init(); } soundcard_configured = 1; #endif if (num_midis && !midi_initialized) midi_initialized = 1; if ((num_midis + num_synths) && !seq_initialized) { seq_initialized = 1; sequencer_init(); } cdevsw_add(&snd_cdevsw); #define GID_SND GID_GAMES #define UID_SND UID_ROOT #define PERM_SND 0660 /* * make links to first successfully probed device, don't do it if * duplicate creation of same node failed (ie. bad cookie returned) */ if (dev->id_driver == &opldriver){ make_dev(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_SEQ, UID_SND, GID_SND, PERM_SND, "sequencer%r", dev->id_unit); } else if (dev->id_driver == &mpudriver || dev->id_driver == &sbmididriver || dev->id_driver == &uartdriver){ make_dev(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_MIDIN, UID_SND, GID_SND, PERM_SND, "midi%r", dev->id_unit); } else { make_dev(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_DSP, UID_SND, GID_SND, PERM_SND, "dsp%r", dev->id_unit); make_dev(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_DSP16, UID_SND, GID_SND, PERM_SND, "dspW%r", dev->id_unit); make_dev(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_AUDIO, UID_SND, GID_SND, PERM_SND, "audio%r", dev->id_unit); make_dev(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_CTL, UID_SND, GID_SND, PERM_SND, "mixer%r", dev->id_unit); make_dev(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_STATUS, UID_SND, GID_SND, PERM_SND, "sndstat%r", dev->id_unit); } return TRUE; } #ifdef CONFIG_AUDIO static void alloc_dmap(int dev, int chan, struct dma_buffparms * dmap) { char *tmpbuf; int i; tmpbuf = contigmalloc(audio_devs[dev]->buffsize, M_DEVBUF, M_NOWAIT, 0ul, 0xfffffful, 1ul, chan & 4 ? 0x20000ul : 0x10000ul); if (tmpbuf == NULL) printf("soundcard buffer alloc failed \n"); if (tmpbuf == NULL) { printf("snd: Unable to allocate %d bytes of buffer\n", 2 * (int) audio_devs[dev]->buffsize); return; } dmap->raw_buf = tmpbuf; /* * Use virtual address as the physical address, since isa_dmastart * performs the phys address computation. */ dmap->raw_buf_phys = (uintptr_t) tmpbuf; for (i = 0; i < audio_devs[dev]->buffsize; i++) *tmpbuf++ = 0x80; } static void sound_mem_init(void) { int dev; static u_long dsp_init_mask = 0; for (dev = 0; dev < num_audiodevs; dev++) /* Enumerate devices */ if (!(dsp_init_mask & (1 << dev))) /* Not already done */ if (audio_devs[dev]->dmachan1 >= 0) { dsp_init_mask |= (1 << dev); audio_devs[dev]->buffsize = DSP_BUFFSIZE; /* Now allocate the buffers */ alloc_dmap(dev, audio_devs[dev]->dmachan1, audio_devs[dev]->dmap_out); if (audio_devs[dev]->flags & DMA_DUPLEX) alloc_dmap(dev, audio_devs[dev]->dmachan2, audio_devs[dev]->dmap_in); } /* for dev */ } #endif int snd_ioctl_return(int *addr, int value) { if (value < 0) return value; /* Error */ suword(addr, value); return 0; } #define MAX_UNIT 50 typedef void (*irq_proc_t) (int irq); static irq_proc_t irq_proc[MAX_UNIT] = {NULL}; static int irq_irq[MAX_UNIT] = {0}; int snd_set_irq_handler(int int_lvl, void (*hndlr) (int), sound_os_info * osp) { if (osp->unit >= MAX_UNIT) { printf("Sound error: Unit number too high (%d)\n", osp->unit); return 0; } irq_proc[osp->unit] = hndlr; irq_irq[osp->unit] = int_lvl; return 1; } static void sndintr(int unit) { if ( (unit >= MAX_UNIT) || (irq_proc[unit] == NULL) ) return; irq_proc[unit] (irq_irq[unit]); /* Call the installed handler */ } void conf_printf(char *name, struct address_info * hw_config) { if (!trace_init) return; printf("snd0: <%s> ", name); #if 0 if (hw_config->io_base != -1 ) printf("at 0x%03x", hw_config->io_base); if (hw_config->irq != -1 ) printf(" irq %d", hw_config->irq); if (hw_config->dma != -1 || hw_config->dma2 != -1) { printf(" dma %d", hw_config->dma); if (hw_config->dma2 != -1) printf(",%d", hw_config->dma2); } #endif } void conf_printf2(char *name, int base, int irq, int dma, int dma2) { if (!trace_init) return; printf("snd0: <%s> ", name); #if 0 if (hw_config->io_base != -1 ) printf("at 0x%03x", hw_config->io_base); if (irq) printf(" irq %d", irq); if (dma != -1 || dma2 != -1) { printf(" dma %d", dma); if (dma2 != -1) printf(",%d", dma2); } #endif } void tenmicrosec (int j) { int i, k; for (k = 0; k < j/10 ; k++) { for (i = 0; i < 16; i++) inb (0x80); } } #endif /* NSND > 0 */