2017-03-31 16:06:11 +03:00
/*
* ff - hwdep . c - a part of driver for RME Fireface series
*
* Copyright ( c ) 2015 - 2017 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License , version 2.
*/
/*
* This codes give three functionality .
*
* 1. get firewire node information
* 2. get notification about starting / stopping stream
* 3. lock / unlock stream
*/
# include "ff.h"
static long hwdep_read ( struct snd_hwdep * hwdep , char __user * buf , long count ,
loff_t * offset )
{
struct snd_ff * ff = hwdep - > private_data ;
DEFINE_WAIT ( wait ) ;
union snd_firewire_event event ;
spin_lock_irq ( & ff - > lock ) ;
while ( ! ff - > dev_lock_changed ) {
prepare_to_wait ( & ff - > hwdep_wait , & wait , TASK_INTERRUPTIBLE ) ;
spin_unlock_irq ( & ff - > lock ) ;
schedule ( ) ;
finish_wait ( & ff - > hwdep_wait , & wait ) ;
if ( signal_pending ( current ) )
return - ERESTARTSYS ;
spin_lock_irq ( & ff - > lock ) ;
}
memset ( & event , 0 , sizeof ( event ) ) ;
if ( ff - > dev_lock_changed ) {
event . lock_status . type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS ;
event . lock_status . status = ( ff - > dev_lock_count > 0 ) ;
ff - > dev_lock_changed = false ;
count = min_t ( long , count , sizeof ( event . lock_status ) ) ;
}
spin_unlock_irq ( & ff - > lock ) ;
if ( copy_to_user ( buf , & event , count ) )
return - EFAULT ;
return count ;
}
2017-07-03 06:27:36 +03:00
static __poll_t hwdep_poll ( struct snd_hwdep * hwdep , struct file * file ,
2017-03-31 16:06:11 +03:00
poll_table * wait )
{
struct snd_ff * ff = hwdep - > private_data ;
2017-07-03 06:27:36 +03:00
__poll_t events ;
2017-03-31 16:06:11 +03:00
poll_wait ( file , & ff - > hwdep_wait , wait ) ;
spin_lock_irq ( & ff - > lock ) ;
if ( ff - > dev_lock_changed )
2018-02-12 01:34:03 +03:00
events = EPOLLIN | EPOLLRDNORM ;
2017-03-31 16:06:11 +03:00
else
events = 0 ;
spin_unlock_irq ( & ff - > lock ) ;
return events ;
}
static int hwdep_get_info ( struct snd_ff * ff , void __user * arg )
{
struct fw_device * dev = fw_parent_device ( ff - > unit ) ;
struct snd_firewire_get_info info ;
memset ( & info , 0 , sizeof ( info ) ) ;
info . type = SNDRV_FIREWIRE_TYPE_FIREFACE ;
info . card = dev - > card - > index ;
* ( __be32 * ) & info . guid [ 0 ] = cpu_to_be32 ( dev - > config_rom [ 3 ] ) ;
* ( __be32 * ) & info . guid [ 4 ] = cpu_to_be32 ( dev - > config_rom [ 4 ] ) ;
strlcpy ( info . device_name , dev_name ( & dev - > device ) ,
sizeof ( info . device_name ) ) ;
if ( copy_to_user ( arg , & info , sizeof ( info ) ) )
return - EFAULT ;
return 0 ;
}
static int hwdep_lock ( struct snd_ff * ff )
{
int err ;
spin_lock_irq ( & ff - > lock ) ;
if ( ff - > dev_lock_count = = 0 ) {
ff - > dev_lock_count = - 1 ;
err = 0 ;
} else {
err = - EBUSY ;
}
spin_unlock_irq ( & ff - > lock ) ;
return err ;
}
static int hwdep_unlock ( struct snd_ff * ff )
{
int err ;
spin_lock_irq ( & ff - > lock ) ;
if ( ff - > dev_lock_count = = - 1 ) {
ff - > dev_lock_count = 0 ;
err = 0 ;
} else {
err = - EBADFD ;
}
spin_unlock_irq ( & ff - > lock ) ;
return err ;
}
static int hwdep_release ( struct snd_hwdep * hwdep , struct file * file )
{
struct snd_ff * ff = hwdep - > private_data ;
spin_lock_irq ( & ff - > lock ) ;
if ( ff - > dev_lock_count = = - 1 )
ff - > dev_lock_count = 0 ;
spin_unlock_irq ( & ff - > lock ) ;
return 0 ;
}
static int hwdep_ioctl ( struct snd_hwdep * hwdep , struct file * file ,
unsigned int cmd , unsigned long arg )
{
struct snd_ff * ff = hwdep - > private_data ;
switch ( cmd ) {
case SNDRV_FIREWIRE_IOCTL_GET_INFO :
return hwdep_get_info ( ff , ( void __user * ) arg ) ;
case SNDRV_FIREWIRE_IOCTL_LOCK :
return hwdep_lock ( ff ) ;
case SNDRV_FIREWIRE_IOCTL_UNLOCK :
return hwdep_unlock ( ff ) ;
default :
return - ENOIOCTLCMD ;
}
}
# ifdef CONFIG_COMPAT
static int hwdep_compat_ioctl ( struct snd_hwdep * hwdep , struct file * file ,
unsigned int cmd , unsigned long arg )
{
return hwdep_ioctl ( hwdep , file , cmd ,
( unsigned long ) compat_ptr ( arg ) ) ;
}
# else
# define hwdep_compat_ioctl NULL
# endif
int snd_ff_create_hwdep_devices ( struct snd_ff * ff )
{
static const struct snd_hwdep_ops hwdep_ops = {
. read = hwdep_read ,
. release = hwdep_release ,
. poll = hwdep_poll ,
. ioctl = hwdep_ioctl ,
. ioctl_compat = hwdep_compat_ioctl ,
} ;
struct snd_hwdep * hwdep ;
int err ;
err = snd_hwdep_new ( ff - > card , ff - > card - > driver , 0 , & hwdep ) ;
if ( err < 0 )
return err ;
strcpy ( hwdep - > name , ff - > card - > driver ) ;
hwdep - > iface = SNDRV_HWDEP_IFACE_FW_FIREFACE ;
hwdep - > ops = hwdep_ops ;
hwdep - > private_data = ff ;
hwdep - > exclusive = true ;
return 0 ;
}