2007-10-16 12:27:34 +04:00
/*
* Copyright ( C ) 2002 Steve Schmidtke
2005-04-17 02:20:36 +04:00
* Licensed under the GPL
*/
2012-10-08 06:27:32 +04:00
# include <linux/fs.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/sound.h>
# include <linux/soundcard.h>
# include <linux/mutex.h>
# include <asm/uaccess.h>
# include <init.h>
# include <os.h>
2005-04-17 02:20:36 +04:00
struct hostaudio_state {
2007-02-10 12:44:00 +03:00
int fd ;
2005-04-17 02:20:36 +04:00
} ;
struct hostmixer_state {
2007-02-10 12:44:00 +03:00
int fd ;
2005-04-17 02:20:36 +04:00
} ;
# define HOSTAUDIO_DEV_DSP " / dev / sound / dsp"
# define HOSTAUDIO_DEV_MIXER " / dev / sound / mixer"
2007-10-16 12:27:34 +04:00
/*
* Changed either at boot time or module load time . At boot , this is
2007-02-10 12:43:59 +03:00
* single - threaded ; at module load , multiple modules would each have
* their own copy of these variables .
*/
static char * dsp = HOSTAUDIO_DEV_DSP ;
static char * mixer = HOSTAUDIO_DEV_MIXER ;
2005-04-17 02:20:36 +04:00
# define DSP_HELP \
" This is used to specify the host dsp device to the hostaudio driver. \n " \
" The default is \" " HOSTAUDIO_DEV_DSP " \" . \n \n "
# define MIXER_HELP \
" This is used to specify the host mixer device to the hostaudio driver. \n " \
" The default is \" " HOSTAUDIO_DEV_MIXER " \" . \n \n "
2010-10-16 01:34:13 +04:00
module_param ( dsp , charp , 0644 ) ;
MODULE_PARM_DESC ( dsp , DSP_HELP ) ;
module_param ( mixer , charp , 0644 ) ;
MODULE_PARM_DESC ( mixer , MIXER_HELP ) ;
2005-04-17 02:20:36 +04:00
# ifndef MODULE
static int set_dsp ( char * name , int * add )
{
dsp = name ;
2007-10-16 12:27:34 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
__uml_setup ( " dsp= " , set_dsp , " dsp=<dsp device> \n " DSP_HELP ) ;
static int set_mixer ( char * name , int * add )
{
mixer = name ;
2007-10-16 12:27:34 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
__uml_setup ( " mixer= " , set_mixer , " mixer=<mixer device> \n " MIXER_HELP ) ;
# endif
2010-09-11 20:38:03 +04:00
static DEFINE_MUTEX ( hostaudio_mutex ) ;
2005-04-17 02:20:36 +04:00
/* /dev/dsp file operations */
2006-03-31 14:30:15 +04:00
static ssize_t hostaudio_read ( struct file * file , char __user * buffer ,
size_t count , loff_t * ppos )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:44:00 +03:00
struct hostaudio_state * state = file - > private_data ;
2005-04-17 02:20:36 +04:00
void * kbuf ;
int err ;
# ifdef DEBUG
2007-10-16 12:27:34 +04:00
printk ( KERN_DEBUG " hostaudio: read called, count = %d \n " , count ) ;
2005-04-17 02:20:36 +04:00
# endif
kbuf = kmalloc ( count , GFP_KERNEL ) ;
2007-10-16 12:27:34 +04:00
if ( kbuf = = NULL )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2007-05-07 01:51:43 +04:00
err = os_read_file ( state - > fd , kbuf , count ) ;
2007-10-16 12:27:34 +04:00
if ( err < 0 )
2005-04-17 02:20:36 +04:00
goto out ;
2007-10-16 12:27:34 +04:00
if ( copy_to_user ( buffer , kbuf , err ) )
2005-04-17 02:20:36 +04:00
err = - EFAULT ;
2007-02-10 12:44:00 +03:00
out :
2005-04-17 02:20:36 +04:00
kfree ( kbuf ) ;
2007-10-16 12:27:34 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
2006-03-31 14:30:15 +04:00
static ssize_t hostaudio_write ( struct file * file , const char __user * buffer ,
2005-04-17 02:20:36 +04:00
size_t count , loff_t * ppos )
{
2007-02-10 12:44:00 +03:00
struct hostaudio_state * state = file - > private_data ;
2005-04-17 02:20:36 +04:00
void * kbuf ;
int err ;
# ifdef DEBUG
2007-10-16 12:27:34 +04:00
printk ( KERN_DEBUG " hostaudio: write called, count = %d \n " , count ) ;
2005-04-17 02:20:36 +04:00
# endif
kbuf = kmalloc ( count , GFP_KERNEL ) ;
2007-10-16 12:27:34 +04:00
if ( kbuf = = NULL )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
err = - EFAULT ;
2007-10-16 12:27:34 +04:00
if ( copy_from_user ( kbuf , buffer , count ) )
2005-04-17 02:20:36 +04:00
goto out ;
2007-05-07 01:51:43 +04:00
err = os_write_file ( state - > fd , kbuf , count ) ;
2007-10-16 12:27:34 +04:00
if ( err < 0 )
2005-04-17 02:20:36 +04:00
goto out ;
* ppos + = err ;
out :
kfree ( kbuf ) ;
2007-10-16 12:27:34 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:27:34 +04:00
static unsigned int hostaudio_poll ( struct file * file ,
2005-04-17 02:20:36 +04:00
struct poll_table_struct * wait )
{
2007-02-10 12:44:00 +03:00
unsigned int mask = 0 ;
2005-04-17 02:20:36 +04:00
# ifdef DEBUG
2007-10-16 12:27:34 +04:00
printk ( KERN_DEBUG " hostaudio: poll called (unimplemented) \n " ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-10-16 12:27:34 +04:00
return mask ;
2005-04-17 02:20:36 +04:00
}
2010-05-07 19:34:28 +04:00
static long hostaudio_ioctl ( struct file * file ,
2005-04-17 02:20:36 +04:00
unsigned int cmd , unsigned long arg )
{
2007-02-10 12:44:00 +03:00
struct hostaudio_state * state = file - > private_data ;
2005-04-17 02:20:36 +04:00
unsigned long data = 0 ;
int err ;
# ifdef DEBUG
2007-10-16 12:27:34 +04:00
printk ( KERN_DEBUG " hostaudio: ioctl called, cmd = %u \n " , cmd ) ;
2005-04-17 02:20:36 +04:00
# endif
switch ( cmd ) {
case SNDCTL_DSP_SPEED :
case SNDCTL_DSP_STEREO :
case SNDCTL_DSP_GETBLKSIZE :
case SNDCTL_DSP_CHANNELS :
case SNDCTL_DSP_SUBDIVIDE :
case SNDCTL_DSP_SETFRAGMENT :
2007-10-16 12:27:34 +04:00
if ( get_user ( data , ( int __user * ) arg ) )
2008-05-13 01:01:51 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
break ;
default :
break ;
}
err = os_ioctl_generic ( state - > fd , cmd , ( unsigned long ) & data ) ;
switch ( cmd ) {
case SNDCTL_DSP_SPEED :
case SNDCTL_DSP_STEREO :
case SNDCTL_DSP_GETBLKSIZE :
case SNDCTL_DSP_CHANNELS :
case SNDCTL_DSP_SUBDIVIDE :
case SNDCTL_DSP_SETFRAGMENT :
2007-10-16 12:27:34 +04:00
if ( put_user ( data , ( int __user * ) arg ) )
return - EFAULT ;
2005-04-17 02:20:36 +04:00
break ;
default :
break ;
}
2007-10-16 12:27:34 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
static int hostaudio_open ( struct inode * inode , struct file * file )
{
2007-02-10 12:44:00 +03:00
struct hostaudio_state * state ;
int r = 0 , w = 0 ;
int ret ;
2005-04-17 02:20:36 +04:00
# ifdef DEBUG
2010-08-12 09:04:27 +04:00
kparam_block_sysfs_write ( dsp ) ;
2007-10-16 12:27:34 +04:00
printk ( KERN_DEBUG " hostaudio: open called (host: %s) \n " , dsp ) ;
2010-08-12 09:04:27 +04:00
kparam_unblock_sysfs_write ( dsp ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:44:00 +03:00
state = kmalloc ( sizeof ( struct hostaudio_state ) , GFP_KERNEL ) ;
2007-10-16 12:27:34 +04:00
if ( state = = NULL )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:27:34 +04:00
if ( file - > f_mode & FMODE_READ )
r = 1 ;
if ( file - > f_mode & FMODE_WRITE )
w = 1 ;
2005-04-17 02:20:36 +04:00
2010-08-12 09:04:27 +04:00
kparam_block_sysfs_write ( dsp ) ;
2010-09-11 20:38:03 +04:00
mutex_lock ( & hostaudio_mutex ) ;
2005-04-17 02:20:36 +04:00
ret = os_open_file ( dsp , of_set_rw ( OPENFLAGS ( ) , r , w ) , 0 ) ;
2010-09-11 20:38:03 +04:00
mutex_unlock ( & hostaudio_mutex ) ;
2010-08-12 09:04:27 +04:00
kparam_unblock_sysfs_write ( dsp ) ;
2010-07-11 14:16:36 +04:00
2007-10-16 12:27:34 +04:00
if ( ret < 0 ) {
2005-04-17 02:20:36 +04:00
kfree ( state ) ;
2007-10-16 12:27:34 +04:00
return ret ;
2007-02-10 12:44:00 +03:00
}
2005-04-17 02:20:36 +04:00
state - > fd = ret ;
2007-02-10 12:44:00 +03:00
file - > private_data = state ;
2007-10-16 12:27:34 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int hostaudio_release ( struct inode * inode , struct file * file )
{
2007-02-10 12:44:00 +03:00
struct hostaudio_state * state = file - > private_data ;
2005-04-17 02:20:36 +04:00
# ifdef DEBUG
2007-10-16 12:27:34 +04:00
printk ( KERN_DEBUG " hostaudio: release called \n " ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:44:00 +03:00
os_close_file ( state - > fd ) ;
kfree ( state ) ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:27:34 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/* /dev/mixer file operations */
2010-05-07 19:34:28 +04:00
static long hostmixer_ioctl_mixdev ( struct file * file ,
2005-04-17 02:20:36 +04:00
unsigned int cmd , unsigned long arg )
{
2007-02-10 12:44:00 +03:00
struct hostmixer_state * state = file - > private_data ;
2005-04-17 02:20:36 +04:00
# ifdef DEBUG
2007-10-16 12:27:34 +04:00
printk ( KERN_DEBUG " hostmixer: ioctl called \n " ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-10-16 12:27:34 +04:00
return os_ioctl_generic ( state - > fd , cmd , arg ) ;
2005-04-17 02:20:36 +04:00
}
static int hostmixer_open_mixdev ( struct inode * inode , struct file * file )
{
2007-02-10 12:44:00 +03:00
struct hostmixer_state * state ;
int r = 0 , w = 0 ;
int ret ;
2005-04-17 02:20:36 +04:00
# ifdef DEBUG
2007-10-16 12:27:34 +04:00
printk ( KERN_DEBUG " hostmixer: open called (host: %s) \n " , mixer ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:44:00 +03:00
state = kmalloc ( sizeof ( struct hostmixer_state ) , GFP_KERNEL ) ;
2007-10-16 12:27:34 +04:00
if ( state = = NULL )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:27:34 +04:00
if ( file - > f_mode & FMODE_READ )
r = 1 ;
if ( file - > f_mode & FMODE_WRITE )
w = 1 ;
2005-04-17 02:20:36 +04:00
2010-08-12 09:04:27 +04:00
kparam_block_sysfs_write ( mixer ) ;
2010-09-11 20:38:03 +04:00
mutex_lock ( & hostaudio_mutex ) ;
2005-04-17 02:20:36 +04:00
ret = os_open_file ( mixer , of_set_rw ( OPENFLAGS ( ) , r , w ) , 0 ) ;
2010-09-11 20:38:03 +04:00
mutex_unlock ( & hostaudio_mutex ) ;
2010-08-12 09:04:27 +04:00
kparam_unblock_sysfs_write ( mixer ) ;
2007-10-16 12:27:34 +04:00
if ( ret < 0 ) {
2010-08-12 09:04:27 +04:00
kparam_block_sysfs_write ( dsp ) ;
2007-10-16 12:27:34 +04:00
printk ( KERN_ERR " hostaudio_open_mixdev failed to open '%s', "
" err = %d \n " , dsp , - ret ) ;
2010-08-12 09:04:27 +04:00
kparam_unblock_sysfs_write ( dsp ) ;
2005-04-17 02:20:36 +04:00
kfree ( state ) ;
2007-10-16 12:27:34 +04:00
return ret ;
2007-02-10 12:44:00 +03:00
}
2005-04-17 02:20:36 +04:00
2007-02-10 12:44:00 +03:00
file - > private_data = state ;
2007-10-16 12:27:34 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int hostmixer_release ( struct inode * inode , struct file * file )
{
2007-02-10 12:44:00 +03:00
struct hostmixer_state * state = file - > private_data ;
2005-04-17 02:20:36 +04:00
# ifdef DEBUG
2007-10-16 12:27:34 +04:00
printk ( KERN_DEBUG " hostmixer: release called \n " ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:44:00 +03:00
os_close_file ( state - > fd ) ;
kfree ( state ) ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:27:34 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/* kernel module operations */
2006-09-27 12:50:33 +04:00
static const struct file_operations hostaudio_fops = {
2007-02-10 12:44:00 +03:00
. owner = THIS_MODULE ,
. llseek = no_llseek ,
. read = hostaudio_read ,
. write = hostaudio_write ,
. poll = hostaudio_poll ,
2010-05-07 19:34:28 +04:00
. unlocked_ioctl = hostaudio_ioctl ,
2007-02-10 12:44:00 +03:00
. mmap = NULL ,
. open = hostaudio_open ,
. release = hostaudio_release ,
2005-04-17 02:20:36 +04:00
} ;
2006-09-27 12:50:33 +04:00
static const struct file_operations hostmixer_fops = {
2007-02-10 12:44:00 +03:00
. owner = THIS_MODULE ,
. llseek = no_llseek ,
2010-05-07 19:34:28 +04:00
. unlocked_ioctl = hostmixer_ioctl_mixdev ,
2007-02-10 12:44:00 +03:00
. open = hostmixer_open_mixdev ,
. release = hostmixer_release ,
2005-04-17 02:20:36 +04:00
} ;
struct {
int dev_audio ;
int dev_mixer ;
} module_data ;
MODULE_AUTHOR ( " Steve Schmidtke " ) ;
MODULE_DESCRIPTION ( " UML Audio Relay " ) ;
MODULE_LICENSE ( " GPL " ) ;
static int __init hostaudio_init_module ( void )
{
2010-08-12 09:04:27 +04:00
__kernel_param_lock ( ) ;
2007-02-10 12:44:00 +03:00
printk ( KERN_INFO " UML Audio Relay (host dsp = %s, host mixer = %s) \n " ,
2005-04-17 02:20:36 +04:00
dsp , mixer ) ;
2010-08-12 09:04:27 +04:00
__kernel_param_unlock ( ) ;
2005-04-17 02:20:36 +04:00
module_data . dev_audio = register_sound_dsp ( & hostaudio_fops , - 1 ) ;
2007-10-16 12:27:34 +04:00
if ( module_data . dev_audio < 0 ) {
2007-02-10 12:44:00 +03:00
printk ( KERN_ERR " hostaudio: couldn't register DSP device! \n " ) ;
return - ENODEV ;
}
2005-04-17 02:20:36 +04:00
module_data . dev_mixer = register_sound_mixer ( & hostmixer_fops , - 1 ) ;
2007-10-16 12:27:34 +04:00
if ( module_data . dev_mixer < 0 ) {
2007-02-10 12:44:00 +03:00
printk ( KERN_ERR " hostmixer: couldn't register mixer "
2005-04-17 02:20:36 +04:00
" device! \n " ) ;
2007-02-10 12:44:00 +03:00
unregister_sound_dsp ( module_data . dev_audio ) ;
return - ENODEV ;
}
2005-04-17 02:20:36 +04:00
2007-02-10 12:44:00 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static void __exit hostaudio_cleanup_module ( void )
{
2007-02-10 12:44:00 +03:00
unregister_sound_mixer ( module_data . dev_mixer ) ;
unregister_sound_dsp ( module_data . dev_audio ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( hostaudio_init_module ) ;
module_exit ( hostaudio_cleanup_module ) ;