forked from Minki/linux
9c2e08c592
Many struct file_operations in the kernel can be "const". Marking them const moves these to the .rodata section, which avoids false sharing with potential dirty data. In addition it'll catch accidental writes at compile time to these shared resources. Signed-off-by: Arjan van de Ven <arjan@linux.intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
310 lines
7.3 KiB
C
310 lines
7.3 KiB
C
/*
|
|
* OSS compatible sequencer driver
|
|
*
|
|
* registration of device and proc
|
|
*
|
|
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include <sound/driver.h>
|
|
#include <linux/init.h>
|
|
#include <linux/smp_lock.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/mutex.h>
|
|
#include <sound/core.h>
|
|
#include <sound/minors.h>
|
|
#include <sound/initval.h>
|
|
#include "seq_oss_device.h"
|
|
#include "seq_oss_synth.h"
|
|
|
|
/*
|
|
* module option
|
|
*/
|
|
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
|
|
MODULE_DESCRIPTION("OSS-compatible sequencer module");
|
|
MODULE_LICENSE("GPL");
|
|
/* Takashi says this is really only for sound-service-0-, but this is OK. */
|
|
MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_SEQUENCER);
|
|
MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MUSIC);
|
|
|
|
#ifdef SNDRV_SEQ_OSS_DEBUG
|
|
module_param(seq_oss_debug, int, 0644);
|
|
MODULE_PARM_DESC(seq_oss_debug, "debug option");
|
|
int seq_oss_debug = 0;
|
|
#endif
|
|
|
|
|
|
/*
|
|
* prototypes
|
|
*/
|
|
static int register_device(void);
|
|
static void unregister_device(void);
|
|
#ifdef CONFIG_PROC_FS
|
|
static int register_proc(void);
|
|
static void unregister_proc(void);
|
|
#else
|
|
static inline int register_proc(void) { return 0; }
|
|
static inline void unregister_proc(void) {}
|
|
#endif
|
|
|
|
static int odev_open(struct inode *inode, struct file *file);
|
|
static int odev_release(struct inode *inode, struct file *file);
|
|
static ssize_t odev_read(struct file *file, char __user *buf, size_t count, loff_t *offset);
|
|
static ssize_t odev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset);
|
|
static long odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
|
static unsigned int odev_poll(struct file *file, poll_table * wait);
|
|
|
|
|
|
/*
|
|
* module interface
|
|
*/
|
|
|
|
static int __init alsa_seq_oss_init(void)
|
|
{
|
|
int rc;
|
|
static struct snd_seq_dev_ops ops = {
|
|
snd_seq_oss_synth_register,
|
|
snd_seq_oss_synth_unregister,
|
|
};
|
|
|
|
snd_seq_autoload_lock();
|
|
if ((rc = register_device()) < 0)
|
|
goto error;
|
|
if ((rc = register_proc()) < 0) {
|
|
unregister_device();
|
|
goto error;
|
|
}
|
|
if ((rc = snd_seq_oss_create_client()) < 0) {
|
|
unregister_proc();
|
|
unregister_device();
|
|
goto error;
|
|
}
|
|
|
|
if ((rc = snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OSS, &ops,
|
|
sizeof(struct snd_seq_oss_reg))) < 0) {
|
|
snd_seq_oss_delete_client();
|
|
unregister_proc();
|
|
unregister_device();
|
|
goto error;
|
|
}
|
|
|
|
/* success */
|
|
snd_seq_oss_synth_init();
|
|
|
|
error:
|
|
snd_seq_autoload_unlock();
|
|
return rc;
|
|
}
|
|
|
|
static void __exit alsa_seq_oss_exit(void)
|
|
{
|
|
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OSS);
|
|
snd_seq_oss_delete_client();
|
|
unregister_proc();
|
|
unregister_device();
|
|
}
|
|
|
|
module_init(alsa_seq_oss_init)
|
|
module_exit(alsa_seq_oss_exit)
|
|
|
|
/*
|
|
* ALSA minor device interface
|
|
*/
|
|
|
|
static DEFINE_MUTEX(register_mutex);
|
|
|
|
static int
|
|
odev_open(struct inode *inode, struct file *file)
|
|
{
|
|
int level, rc;
|
|
|
|
if (iminor(inode) == SNDRV_MINOR_OSS_MUSIC)
|
|
level = SNDRV_SEQ_OSS_MODE_MUSIC;
|
|
else
|
|
level = SNDRV_SEQ_OSS_MODE_SYNTH;
|
|
|
|
mutex_lock(®ister_mutex);
|
|
rc = snd_seq_oss_open(file, level);
|
|
mutex_unlock(®ister_mutex);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
odev_release(struct inode *inode, struct file *file)
|
|
{
|
|
struct seq_oss_devinfo *dp;
|
|
|
|
if ((dp = file->private_data) == NULL)
|
|
return 0;
|
|
|
|
snd_seq_oss_drain_write(dp);
|
|
|
|
mutex_lock(®ister_mutex);
|
|
snd_seq_oss_release(dp);
|
|
mutex_unlock(®ister_mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t
|
|
odev_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
|
|
{
|
|
struct seq_oss_devinfo *dp;
|
|
dp = file->private_data;
|
|
snd_assert(dp != NULL, return -EIO);
|
|
return snd_seq_oss_read(dp, buf, count);
|
|
}
|
|
|
|
|
|
static ssize_t
|
|
odev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
|
|
{
|
|
struct seq_oss_devinfo *dp;
|
|
dp = file->private_data;
|
|
snd_assert(dp != NULL, return -EIO);
|
|
return snd_seq_oss_write(dp, buf, count, file);
|
|
}
|
|
|
|
static long
|
|
odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
struct seq_oss_devinfo *dp;
|
|
dp = file->private_data;
|
|
snd_assert(dp != NULL, return -EIO);
|
|
return snd_seq_oss_ioctl(dp, cmd, arg);
|
|
}
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
#define odev_ioctl_compat odev_ioctl
|
|
#else
|
|
#define odev_ioctl_compat NULL
|
|
#endif
|
|
|
|
static unsigned int
|
|
odev_poll(struct file *file, poll_table * wait)
|
|
{
|
|
struct seq_oss_devinfo *dp;
|
|
dp = file->private_data;
|
|
snd_assert(dp != NULL, return 0);
|
|
return snd_seq_oss_poll(dp, file, wait);
|
|
}
|
|
|
|
/*
|
|
* registration of sequencer minor device
|
|
*/
|
|
|
|
static const struct file_operations seq_oss_f_ops =
|
|
{
|
|
.owner = THIS_MODULE,
|
|
.read = odev_read,
|
|
.write = odev_write,
|
|
.open = odev_open,
|
|
.release = odev_release,
|
|
.poll = odev_poll,
|
|
.unlocked_ioctl = odev_ioctl,
|
|
.compat_ioctl = odev_ioctl_compat,
|
|
};
|
|
|
|
static int __init
|
|
register_device(void)
|
|
{
|
|
int rc;
|
|
|
|
mutex_lock(®ister_mutex);
|
|
if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER,
|
|
NULL, 0,
|
|
&seq_oss_f_ops, NULL,
|
|
SNDRV_SEQ_OSS_DEVNAME)) < 0) {
|
|
snd_printk(KERN_ERR "can't register device seq\n");
|
|
mutex_unlock(®ister_mutex);
|
|
return rc;
|
|
}
|
|
if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC,
|
|
NULL, 0,
|
|
&seq_oss_f_ops, NULL,
|
|
SNDRV_SEQ_OSS_DEVNAME)) < 0) {
|
|
snd_printk(KERN_ERR "can't register device music\n");
|
|
snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0);
|
|
mutex_unlock(®ister_mutex);
|
|
return rc;
|
|
}
|
|
debug_printk(("device registered\n"));
|
|
mutex_unlock(®ister_mutex);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
unregister_device(void)
|
|
{
|
|
mutex_lock(®ister_mutex);
|
|
debug_printk(("device unregistered\n"));
|
|
if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC, NULL, 0) < 0)
|
|
snd_printk(KERN_ERR "error unregister device music\n");
|
|
if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0) < 0)
|
|
snd_printk(KERN_ERR "error unregister device seq\n");
|
|
mutex_unlock(®ister_mutex);
|
|
}
|
|
|
|
/*
|
|
* /proc interface
|
|
*/
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
|
|
static struct snd_info_entry *info_entry;
|
|
|
|
static void
|
|
info_read(struct snd_info_entry *entry, struct snd_info_buffer *buf)
|
|
{
|
|
mutex_lock(®ister_mutex);
|
|
snd_iprintf(buf, "OSS sequencer emulation version %s\n", SNDRV_SEQ_OSS_VERSION_STR);
|
|
snd_seq_oss_system_info_read(buf);
|
|
snd_seq_oss_synth_info_read(buf);
|
|
snd_seq_oss_midi_info_read(buf);
|
|
mutex_unlock(®ister_mutex);
|
|
}
|
|
|
|
|
|
static int __init
|
|
register_proc(void)
|
|
{
|
|
struct snd_info_entry *entry;
|
|
|
|
entry = snd_info_create_module_entry(THIS_MODULE, SNDRV_SEQ_OSS_PROCNAME, snd_seq_root);
|
|
if (entry == NULL)
|
|
return -ENOMEM;
|
|
|
|
entry->content = SNDRV_INFO_CONTENT_TEXT;
|
|
entry->private_data = NULL;
|
|
entry->c.text.read = info_read;
|
|
if (snd_info_register(entry) < 0) {
|
|
snd_info_free_entry(entry);
|
|
return -ENOMEM;
|
|
}
|
|
info_entry = entry;
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
unregister_proc(void)
|
|
{
|
|
snd_info_free_entry(info_entry);
|
|
info_entry = NULL;
|
|
}
|
|
#endif /* CONFIG_PROC_FS */
|