58217f5900
WOO HOO!
355 lines
7.8 KiB
C
355 lines
7.8 KiB
C
|
|
/*
|
|
* 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 <linux/major.h>
|
|
|
|
extern long seq_time;
|
|
|
|
static int soundcards_installed = 0; /* Number of installed
|
|
* soundcards */
|
|
static int soundcard_configured = 0;
|
|
|
|
static struct fileinfo files[SND_NDEVS];
|
|
|
|
extern char *snd_raw_buf[MAX_DSP_DEV][DSP_BUFFCOUNT];
|
|
extern unsigned long snd_raw_buf_phys[MAX_DSP_DEV][DSP_BUFFCOUNT];
|
|
extern int snd_raw_count[MAX_DSP_DEV];
|
|
|
|
int
|
|
snd_ioctl_return (int *addr, int value)
|
|
{
|
|
if (value < 0)
|
|
return value;
|
|
|
|
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)
|
|
{
|
|
case SND_DEV_SEQ:
|
|
return sequencer_select (dev, &files[dev], sel_type, wait);
|
|
break;
|
|
|
|
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 */
|
|
|
|
if (num_dspdevs) /* Audio devices present */
|
|
{
|
|
mem_start = DMAbuf_init (mem_start);
|
|
mem_start = audio_init (mem_start);
|
|
}
|
|
|
|
#ifndef EXCLUDE_MPU401
|
|
if (num_midis)
|
|
mem_start = MIDIbuf_init (mem_start);
|
|
#endif
|
|
|
|
if (num_midis + num_synths)
|
|
mem_start = sequencer_init (mem_start);
|
|
|
|
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))
|
|
{
|
|
int retcode;
|
|
|
|
struct sigaction sa;
|
|
|
|
sa.sa_handler = hndlr;
|
|
|
|
#ifdef SND_SA_INTERRUPT
|
|
sa.sa_flags = SA_INTERRUPT;
|
|
#else
|
|
sa.sa_flags = 0;
|
|
#endif
|
|
|
|
sa.sa_mask = 0;
|
|
sa.sa_restorer = NULL;
|
|
|
|
retcode = irqaction (interrupt_level, &sa);
|
|
|
|
if (retcode < 0)
|
|
{
|
|
printk ("Sound: IRQ%d already in use\n", interrupt_level);
|
|
}
|
|
|
|
return retcode;
|
|
}
|
|
|
|
void
|
|
snd_release_irq(int vect)
|
|
{
|
|
free_irq(vect);
|
|
}
|
|
|
|
void
|
|
request_sound_timer (int count)
|
|
{
|
|
#ifndef EXCLUDE_SEQUENCER
|
|
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
|
|
}
|
|
|
|
void
|
|
sound_stop_timer (void)
|
|
{
|
|
#ifndef EXCLUDE_SEQUENCER
|
|
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;
|
|
|
|
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_dspdevs; dev++) /* Enumerate devices */
|
|
if (sound_buffcounts[dev] > 0 && sound_dsp_dmachan[dev] > 0)
|
|
{
|
|
if (sound_dma_automode[dev])
|
|
sound_buffcounts[dev] = 1;
|
|
|
|
if (sound_dsp_dmachan[dev] > 3 && sound_buffsizes[dev] > 65536)
|
|
dma_pagesize = 131072;/* 128k */
|
|
else
|
|
dma_pagesize = 65536;
|
|
|
|
/* More sanity checks */
|
|
|
|
if (sound_buffsizes[dev] > dma_pagesize)
|
|
sound_buffsizes[dev] = dma_pagesize;
|
|
sound_buffsizes[dev] &= 0xfffff000; /* Truncate to n*4k */
|
|
if (sound_buffsizes[dev] < 4096)
|
|
sound_buffsizes[dev] = 4096;
|
|
|
|
/* Now allocate the buffers */
|
|
|
|
for (snd_raw_count[dev] = 0; snd_raw_count[dev] < sound_buffcounts[dev]; snd_raw_count[dev]++)
|
|
{
|
|
start_addr = mem_ptr - sound_buffsizes[dev];
|
|
if (!valid_dma_page (start_addr, sound_buffsizes[dev], dma_pagesize))
|
|
start_addr &= ~(dma_pagesize - 1); /* Align address to
|
|
* dma_pagesize */
|
|
|
|
end_addr = start_addr + sound_buffsizes[dev] - 1;
|
|
|
|
snd_raw_buf[dev][snd_raw_count[dev]] = (char *) start_addr;
|
|
snd_raw_buf_phys[dev][snd_raw_count[dev]] = 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
|