/* * linux/kernel/chr_drv/sound/soundcard.c * * Soundcard driver for Linux * * 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. * */ #include "sound_config.h" #ifdef CONFIGURE_SOUNDCARD #include #include static int soundcards_installed = 0; /* Number of installed * soundcards */ static int soundcard_configured = 0; static struct fileinfo files[SND_NDEVS]; int snd_ioctl_return (int *addr, int value) { int error; if (value < 0) return value; error = verify_area(VERIFY_WRITE, addr, sizeof(int)); if (error) return error; PUT_WORD_TO_USER (addr, 0, value); return 0; } static int sound_read (struct inode *inode, struct file *file, char *buf, int count) { int dev; dev = inode->i_rdev; dev = MINOR (dev); return sound_read_sw (dev, &files[dev], buf, count); } static int sound_write (struct inode *inode, struct file *file, char *buf, int count) { int dev; dev = inode->i_rdev; dev = MINOR (dev); return sound_write_sw (dev, &files[dev], buf, count); } static int sound_lseek (struct inode *inode, struct file *file, off_t offset, int orig) { return RET_ERROR (EPERM); } static int sound_open (struct inode *inode, struct file *file) { int dev; dev = inode->i_rdev; dev = MINOR (dev); if (!soundcard_configured && dev != SND_DEV_CTL && dev != SND_DEV_STATUS) { printk ("SoundCard Error: The soundcard system has not been configured\n"); return RET_ERROR (ENXIO); } files[dev].mode = 0; if ((file->f_flags & O_ACCMODE) == O_RDWR) files[dev].mode = OPEN_READWRITE; if ((file->f_flags & O_ACCMODE) == O_RDONLY) files[dev].mode = OPEN_READ; if ((file->f_flags & O_ACCMODE) == O_WRONLY) files[dev].mode = OPEN_WRITE; return sound_open_sw (dev, &files[dev]); } static void sound_release (struct inode *inode, struct file *file) { int dev; dev = inode->i_rdev; dev = MINOR (dev); sound_release_sw (dev, &files[dev]); } static int sound_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int dev; dev = inode->i_rdev; dev = MINOR (dev); return sound_ioctl_sw (dev, &files[dev], cmd, arg); } static int sound_select (struct inode *inode, struct file *file, int sel_type, select_table * wait) { int dev; dev = inode->i_rdev; dev = MINOR (dev); DEB (printk ("sound_select(dev=%d, type=0x%x)\n", dev, sel_type)); switch (dev & 0x0f) { #ifndef EXCLUDE_SEQUENCER case SND_DEV_SEQ: return sequencer_select (dev, &files[dev], sel_type, wait); break; #endif #ifndef EXCLUDE_MIDI case SND_DEV_MIDIN: return MIDIbuf_select (dev, &files[dev], sel_type, wait); break; #endif default: return 0; } return 0; } static struct file_operations sound_fops = { sound_lseek, sound_read, sound_write, NULL, /* sound_readdir */ sound_select, sound_ioctl, NULL, sound_open, sound_release }; long soundcard_init (long mem_start) { register_chrdev (SOUND_MAJOR, "sound", &sound_fops); soundcard_configured = 1; mem_start = sndtable_init (mem_start); /* Initialize call tables and * detect cards */ if (!(soundcards_installed = sndtable_get_cardcount ())) return mem_start; /* No cards detected */ #ifndef EXCLUDE_AUDIO if (num_audiodevs) /* Audio devices present */ { mem_start = DMAbuf_init (mem_start); mem_start = audio_init (mem_start); } #endif #ifndef EXCLUDE_MIDI if (num_midis) mem_start = MIDIbuf_init (mem_start); #endif #ifndef EXCLUDE_SEQUENCER if (num_midis + num_synths) mem_start = sequencer_init (mem_start); #endif return mem_start; } void tenmicrosec (void) { int i; for (i = 0; i < 16; i++) inb (0x80); } int snd_set_irq_handler (int interrupt_level, void (*hndlr) (int, struct pt_regs *)) { int retcode; retcode = request_irq(interrupt_level, hndlr, #ifdef SND_SA_INTERRUPT SA_INTERRUPT, #else 0, #endif "sound"); if (retcode < 0) { printk ("Sound: IRQ%d already in use\n", interrupt_level); } return retcode; } void snd_release_irq (int vect) { free_irq (vect); } #ifndef EXCLUDE_SEQUENCER void request_sound_timer (int count) { extern unsigned long seq_time; #if 1 if (count < 0) count = jiffies + (-count); else count += seq_time; timer_table[SOUND_TIMER].fn = sequencer_timer; timer_table[SOUND_TIMER].expires = count; timer_active |= 1 << SOUND_TIMER; #endif } #endif void sound_stop_timer (void) { #if 1 timer_table[SOUND_TIMER].expires = 0; timer_active &= ~(1 << SOUND_TIMER); #endif } #ifndef EXCLUDE_AUDIO static int valid_dma_page (unsigned long addr, unsigned long dev_buffsize, unsigned long dma_pagesize) { if (((addr & (dma_pagesize - 1)) + dev_buffsize) <= dma_pagesize) return 1; else return 0; } void sound_mem_init (void) { int i, dev; unsigned long start_addr, end_addr, mem_ptr, dma_pagesize; struct dma_buffparms *dmap; mem_ptr = high_memory; /* Some sanity checks */ if (mem_ptr > (16 * 1024 * 1024)) mem_ptr = 16 * 1024 * 1024; /* Limit to 16M */ for (dev = 0; dev < num_audiodevs; dev++) /* Enumerate devices */ if (audio_devs[dev]->buffcount > 0 && audio_devs[dev]->dmachan >= 0) { dmap = audio_devs[dev]->dmap; if (audio_devs[dev]->flags & DMA_AUTOMODE) audio_devs[dev]->buffcount = 1; if (audio_devs[dev]->dmachan > 3 && audio_devs[dev]->buffsize > 65536) dma_pagesize = 131072;/* 128k */ else dma_pagesize = 65536; /* More sanity checks */ if (audio_devs[dev]->buffsize > dma_pagesize) audio_devs[dev]->buffsize = dma_pagesize; audio_devs[dev]->buffsize &= 0xfffff000; /* Truncate to n*4k */ if (audio_devs[dev]->buffsize < 4096) audio_devs[dev]->buffsize = 4096; /* Now allocate the buffers */ for (dmap->raw_count = 0; dmap->raw_count < audio_devs[dev]->buffcount; dmap->raw_count++) { start_addr = mem_ptr - audio_devs[dev]->buffsize; if (!valid_dma_page (start_addr, audio_devs[dev]->buffsize, dma_pagesize)) start_addr &= ~(dma_pagesize - 1); /* Align address to * dma_pagesize */ end_addr = start_addr + audio_devs[dev]->buffsize - 1; dmap->raw_buf[dmap->raw_count] = (char *) start_addr; dmap->raw_buf_phys[dmap->raw_count] = start_addr; mem_ptr = start_addr; for (i = MAP_NR (start_addr); i <= MAP_NR (end_addr); i++) { if (mem_map[i]) panic ("sound_mem_init: Page not free (driver incompatible with kernel).\n"); mem_map[i] = MAP_PAGE_RESERVED; } } } /* for dev */ } #endif #else long soundcard_init (long mem_start) /* Dummy version */ { return mem_start; } #endif #if !defined(CONFIGURE_SOUNDCARD) || defined(EXCLUDE_AUDIO) void sound_mem_init (void) { /* Dummy version */ } #endif