Path: blob/master/arch/um/drivers/hostaudio_kern.c
10817 views
/*1* Copyright (C) 2002 Steve Schmidtke2* Licensed under the GPL3*/45#include "linux/fs.h"6#include "linux/module.h"7#include "linux/slab.h"8#include "linux/sound.h"9#include "linux/soundcard.h"10#include "linux/mutex.h"11#include "asm/uaccess.h"12#include "init.h"13#include "os.h"1415struct hostaudio_state {16int fd;17};1819struct hostmixer_state {20int fd;21};2223#define HOSTAUDIO_DEV_DSP "/dev/sound/dsp"24#define HOSTAUDIO_DEV_MIXER "/dev/sound/mixer"2526/*27* Changed either at boot time or module load time. At boot, this is28* single-threaded; at module load, multiple modules would each have29* their own copy of these variables.30*/31static char *dsp = HOSTAUDIO_DEV_DSP;32static char *mixer = HOSTAUDIO_DEV_MIXER;3334#define DSP_HELP \35" This is used to specify the host dsp device to the hostaudio driver.\n" \36" The default is \"" HOSTAUDIO_DEV_DSP "\".\n\n"3738#define MIXER_HELP \39" This is used to specify the host mixer device to the hostaudio driver.\n"\40" The default is \"" HOSTAUDIO_DEV_MIXER "\".\n\n"4142module_param(dsp, charp, 0644);43MODULE_PARM_DESC(dsp, DSP_HELP);44module_param(mixer, charp, 0644);45MODULE_PARM_DESC(mixer, MIXER_HELP);4647#ifndef MODULE48static int set_dsp(char *name, int *add)49{50dsp = name;51return 0;52}5354__uml_setup("dsp=", set_dsp, "dsp=<dsp device>\n" DSP_HELP);5556static int set_mixer(char *name, int *add)57{58mixer = name;59return 0;60}6162__uml_setup("mixer=", set_mixer, "mixer=<mixer device>\n" MIXER_HELP);63#endif6465static DEFINE_MUTEX(hostaudio_mutex);6667/* /dev/dsp file operations */6869static ssize_t hostaudio_read(struct file *file, char __user *buffer,70size_t count, loff_t *ppos)71{72struct hostaudio_state *state = file->private_data;73void *kbuf;74int err;7576#ifdef DEBUG77printk(KERN_DEBUG "hostaudio: read called, count = %d\n", count);78#endif7980kbuf = kmalloc(count, GFP_KERNEL);81if (kbuf == NULL)82return -ENOMEM;8384err = os_read_file(state->fd, kbuf, count);85if (err < 0)86goto out;8788if (copy_to_user(buffer, kbuf, err))89err = -EFAULT;9091out:92kfree(kbuf);93return err;94}9596static ssize_t hostaudio_write(struct file *file, const char __user *buffer,97size_t count, loff_t *ppos)98{99struct hostaudio_state *state = file->private_data;100void *kbuf;101int err;102103#ifdef DEBUG104printk(KERN_DEBUG "hostaudio: write called, count = %d\n", count);105#endif106107kbuf = kmalloc(count, GFP_KERNEL);108if (kbuf == NULL)109return -ENOMEM;110111err = -EFAULT;112if (copy_from_user(kbuf, buffer, count))113goto out;114115err = os_write_file(state->fd, kbuf, count);116if (err < 0)117goto out;118*ppos += err;119120out:121kfree(kbuf);122return err;123}124125static unsigned int hostaudio_poll(struct file *file,126struct poll_table_struct *wait)127{128unsigned int mask = 0;129130#ifdef DEBUG131printk(KERN_DEBUG "hostaudio: poll called (unimplemented)\n");132#endif133134return mask;135}136137static long hostaudio_ioctl(struct file *file,138unsigned int cmd, unsigned long arg)139{140struct hostaudio_state *state = file->private_data;141unsigned long data = 0;142int err;143144#ifdef DEBUG145printk(KERN_DEBUG "hostaudio: ioctl called, cmd = %u\n", cmd);146#endif147switch(cmd){148case SNDCTL_DSP_SPEED:149case SNDCTL_DSP_STEREO:150case SNDCTL_DSP_GETBLKSIZE:151case SNDCTL_DSP_CHANNELS:152case SNDCTL_DSP_SUBDIVIDE:153case SNDCTL_DSP_SETFRAGMENT:154if (get_user(data, (int __user *) arg))155return -EFAULT;156break;157default:158break;159}160161err = os_ioctl_generic(state->fd, cmd, (unsigned long) &data);162163switch(cmd){164case SNDCTL_DSP_SPEED:165case SNDCTL_DSP_STEREO:166case SNDCTL_DSP_GETBLKSIZE:167case SNDCTL_DSP_CHANNELS:168case SNDCTL_DSP_SUBDIVIDE:169case SNDCTL_DSP_SETFRAGMENT:170if (put_user(data, (int __user *) arg))171return -EFAULT;172break;173default:174break;175}176177return err;178}179180static int hostaudio_open(struct inode *inode, struct file *file)181{182struct hostaudio_state *state;183int r = 0, w = 0;184int ret;185186#ifdef DEBUG187kparam_block_sysfs_write(dsp);188printk(KERN_DEBUG "hostaudio: open called (host: %s)\n", dsp);189kparam_unblock_sysfs_write(dsp);190#endif191192state = kmalloc(sizeof(struct hostaudio_state), GFP_KERNEL);193if (state == NULL)194return -ENOMEM;195196if (file->f_mode & FMODE_READ)197r = 1;198if (file->f_mode & FMODE_WRITE)199w = 1;200201kparam_block_sysfs_write(dsp);202mutex_lock(&hostaudio_mutex);203ret = os_open_file(dsp, of_set_rw(OPENFLAGS(), r, w), 0);204mutex_unlock(&hostaudio_mutex);205kparam_unblock_sysfs_write(dsp);206207if (ret < 0) {208kfree(state);209return ret;210}211state->fd = ret;212file->private_data = state;213return 0;214}215216static int hostaudio_release(struct inode *inode, struct file *file)217{218struct hostaudio_state *state = file->private_data;219220#ifdef DEBUG221printk(KERN_DEBUG "hostaudio: release called\n");222#endif223os_close_file(state->fd);224kfree(state);225226return 0;227}228229/* /dev/mixer file operations */230231static long hostmixer_ioctl_mixdev(struct file *file,232unsigned int cmd, unsigned long arg)233{234struct hostmixer_state *state = file->private_data;235236#ifdef DEBUG237printk(KERN_DEBUG "hostmixer: ioctl called\n");238#endif239240return os_ioctl_generic(state->fd, cmd, arg);241}242243static int hostmixer_open_mixdev(struct inode *inode, struct file *file)244{245struct hostmixer_state *state;246int r = 0, w = 0;247int ret;248249#ifdef DEBUG250printk(KERN_DEBUG "hostmixer: open called (host: %s)\n", mixer);251#endif252253state = kmalloc(sizeof(struct hostmixer_state), GFP_KERNEL);254if (state == NULL)255return -ENOMEM;256257if (file->f_mode & FMODE_READ)258r = 1;259if (file->f_mode & FMODE_WRITE)260w = 1;261262kparam_block_sysfs_write(mixer);263mutex_lock(&hostaudio_mutex);264ret = os_open_file(mixer, of_set_rw(OPENFLAGS(), r, w), 0);265mutex_unlock(&hostaudio_mutex);266kparam_unblock_sysfs_write(mixer);267268if (ret < 0) {269kparam_block_sysfs_write(dsp);270printk(KERN_ERR "hostaudio_open_mixdev failed to open '%s', "271"err = %d\n", dsp, -ret);272kparam_unblock_sysfs_write(dsp);273kfree(state);274return ret;275}276277file->private_data = state;278return 0;279}280281static int hostmixer_release(struct inode *inode, struct file *file)282{283struct hostmixer_state *state = file->private_data;284285#ifdef DEBUG286printk(KERN_DEBUG "hostmixer: release called\n");287#endif288289os_close_file(state->fd);290kfree(state);291292return 0;293}294295/* kernel module operations */296297static const struct file_operations hostaudio_fops = {298.owner = THIS_MODULE,299.llseek = no_llseek,300.read = hostaudio_read,301.write = hostaudio_write,302.poll = hostaudio_poll,303.unlocked_ioctl = hostaudio_ioctl,304.mmap = NULL,305.open = hostaudio_open,306.release = hostaudio_release,307};308309static const struct file_operations hostmixer_fops = {310.owner = THIS_MODULE,311.llseek = no_llseek,312.unlocked_ioctl = hostmixer_ioctl_mixdev,313.open = hostmixer_open_mixdev,314.release = hostmixer_release,315};316317struct {318int dev_audio;319int dev_mixer;320} module_data;321322MODULE_AUTHOR("Steve Schmidtke");323MODULE_DESCRIPTION("UML Audio Relay");324MODULE_LICENSE("GPL");325326static int __init hostaudio_init_module(void)327{328__kernel_param_lock();329printk(KERN_INFO "UML Audio Relay (host dsp = %s, host mixer = %s)\n",330dsp, mixer);331__kernel_param_unlock();332333module_data.dev_audio = register_sound_dsp(&hostaudio_fops, -1);334if (module_data.dev_audio < 0) {335printk(KERN_ERR "hostaudio: couldn't register DSP device!\n");336return -ENODEV;337}338339module_data.dev_mixer = register_sound_mixer(&hostmixer_fops, -1);340if (module_data.dev_mixer < 0) {341printk(KERN_ERR "hostmixer: couldn't register mixer "342"device!\n");343unregister_sound_dsp(module_data.dev_audio);344return -ENODEV;345}346347return 0;348}349350static void __exit hostaudio_cleanup_module (void)351{352unregister_sound_mixer(module_data.dev_mixer);353unregister_sound_dsp(module_data.dev_audio);354}355356module_init(hostaudio_init_module);357module_exit(hostaudio_cleanup_module);358359360