2005-04-17 02:20:36 +04:00
/*
2006-10-04 01:01:26 +04:00
* sound / oss / audio . c
2005-04-17 02:20:36 +04:00
*
* Device file manager for / dev / audio
*/
/*
* Copyright ( C ) by Hannu Savolainen 1993 - 1997
*
* OSS / Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE ( GPL )
* Version 2 ( June 1991 ) . See the " COPYING " file distributed with this software
* for more info .
*/
/*
* Thomas Sailer : ioctl code reworked ( vmalloc / vfree removed )
* Thomas Sailer : moved several static variables into struct audio_operations
* ( which is grossly misnamed btw . ) because they have the same
* lifetime as the rest in there and dynamic allocation saves
* 12 k or so
* Thomas Sailer : use more logical O_NONBLOCK semantics
* Daniel Rodriksson : reworked the use of the device specific copy_user
* still generic
* Horst von Brand : Add missing # include < linux / string . h >
* Chris Rankin : Update the module - usage counter for the coprocessor ,
* and decrement the counters again if we cannot open
* the audio device .
*/
# include <linux/stddef.h>
# include <linux/string.h>
# include <linux/kmod.h>
# include "sound_config.h"
# include "ulaw.h"
# include "coproc.h"
# define NEUTRAL8 0x80
# define NEUTRAL16 0x00
static int dma_ioctl ( int dev , unsigned int cmd , void __user * arg ) ;
static int set_format ( int dev , int fmt )
{
if ( fmt ! = AFMT_QUERY )
{
audio_devs [ dev ] - > local_conversion = 0 ;
if ( ! ( audio_devs [ dev ] - > format_mask & fmt ) ) /* Not supported */
{
if ( fmt = = AFMT_MU_LAW )
{
fmt = AFMT_U8 ;
audio_devs [ dev ] - > local_conversion = CNV_MU_LAW ;
}
else
fmt = AFMT_U8 ; /* This is always supported */
}
audio_devs [ dev ] - > audio_format = audio_devs [ dev ] - > d - > set_bits ( dev , fmt ) ;
audio_devs [ dev ] - > local_format = fmt ;
}
else
return audio_devs [ dev ] - > local_format ;
if ( audio_devs [ dev ] - > local_conversion )
return audio_devs [ dev ] - > local_conversion ;
else
return audio_devs [ dev ] - > local_format ;
}
int audio_open ( int dev , struct file * file )
{
int ret ;
int bits ;
int dev_type = dev & 0x0f ;
int mode = translate_mode ( file ) ;
const struct audio_driver * driver ;
const struct coproc_operations * coprocessor ;
dev = dev > > 4 ;
if ( dev_type = = SND_DEV_DSP16 )
bits = 16 ;
else
bits = 8 ;
if ( dev < 0 | | dev > = num_audiodevs )
return - ENXIO ;
driver = audio_devs [ dev ] - > d ;
if ( ! try_module_get ( driver - > owner ) )
return - ENODEV ;
if ( ( ret = DMAbuf_open ( dev , mode ) ) < 0 )
goto error_1 ;
if ( ( coprocessor = audio_devs [ dev ] - > coproc ) ! = NULL ) {
if ( ! try_module_get ( coprocessor - > owner ) )
goto error_2 ;
if ( ( ret = coprocessor - > open ( coprocessor - > devc , COPR_PCM ) ) < 0 ) {
printk ( KERN_WARNING " Sound: Can't access coprocessor device \n " ) ;
goto error_3 ;
}
}
audio_devs [ dev ] - > local_conversion = 0 ;
if ( dev_type = = SND_DEV_AUDIO )
set_format ( dev , AFMT_MU_LAW ) ;
else
set_format ( dev , bits ) ;
audio_devs [ dev ] - > audio_mode = AM_NONE ;
return 0 ;
/*
* Clean - up stack : this is what needs ( un ) doing if
* we can ' t open the audio device . . .
*/
error_3 :
module_put ( coprocessor - > owner ) ;
error_2 :
DMAbuf_release ( dev , mode ) ;
error_1 :
module_put ( driver - > owner ) ;
return ret ;
}
static void sync_output ( int dev )
{
int p , i ;
int l ;
struct dma_buffparms * dmap = audio_devs [ dev ] - > dmap_out ;
if ( dmap - > fragment_size < = 0 )
return ;
dmap - > flags | = DMA_POST ;
/* Align the write pointer with fragment boundaries */
if ( ( l = dmap - > user_counter % dmap - > fragment_size ) > 0 )
{
int len ;
unsigned long offs = dmap - > user_counter % dmap - > bytes_in_use ;
len = dmap - > fragment_size - l ;
memset ( dmap - > raw_buf + offs , dmap - > neutral_byte , len ) ;
DMAbuf_move_wrpointer ( dev , len ) ;
}
/*
* Clean all unused buffer fragments .
*/
p = dmap - > qtail ;
dmap - > flags | = DMA_POST ;
for ( i = dmap - > qlen + 1 ; i < dmap - > nbufs ; i + + )
{
p = ( p + 1 ) % dmap - > nbufs ;
if ( ( ( dmap - > raw_buf + p * dmap - > fragment_size ) + dmap - > fragment_size ) >
( dmap - > raw_buf + dmap - > buffsize ) )
printk ( KERN_ERR " audio: Buffer error 2 \n " ) ;
memset ( dmap - > raw_buf + p * dmap - > fragment_size ,
dmap - > neutral_byte ,
dmap - > fragment_size ) ;
}
dmap - > flags | = DMA_DIRTY ;
}
void audio_release ( int dev , struct file * file )
{
const struct coproc_operations * coprocessor ;
int mode = translate_mode ( file ) ;
dev = dev > > 4 ;
/*
* We do this in DMAbuf_release ( ) . Why are we doing it
* here ? Why don ' t we test the file mode before setting
* both flags ? DMAbuf_release ( ) does .
* . . . pester . . . pester . . . pester . . .
*/
audio_devs [ dev ] - > dmap_out - > closing = 1 ;
audio_devs [ dev ] - > dmap_in - > closing = 1 ;
/*
* We need to make sure we allocated the dmap_out buffer
* before we go mucking around with it in sync_output ( ) .
*/
if ( mode & OPEN_WRITE )
sync_output ( dev ) ;
if ( ( coprocessor = audio_devs [ dev ] - > coproc ) ! = NULL ) {
coprocessor - > close ( coprocessor - > devc , COPR_PCM ) ;
module_put ( coprocessor - > owner ) ;
}
DMAbuf_release ( dev , mode ) ;
module_put ( audio_devs [ dev ] - > d - > owner ) ;
}
static void translate_bytes ( const unsigned char * table , unsigned char * buff , int n )
{
unsigned long i ;
if ( n < = 0 )
return ;
for ( i = 0 ; i < n ; + + i )
buff [ i ] = table [ buff [ i ] ] ;
}
int audio_write ( int dev , struct file * file , const char __user * buf , int count )
{
int c , p , l , buf_size , used , returned ;
int err ;
char * dma_buf ;
dev = dev > > 4 ;
p = 0 ;
c = count ;
if ( count < 0 )
return - EINVAL ;
if ( ! ( audio_devs [ dev ] - > open_mode & OPEN_WRITE ) )
return - EPERM ;
if ( audio_devs [ dev ] - > flags & DMA_DUPLEX )
audio_devs [ dev ] - > audio_mode | = AM_WRITE ;
else
audio_devs [ dev ] - > audio_mode = AM_WRITE ;
if ( ! count ) /* Flush output */
{
sync_output ( dev ) ;
return 0 ;
}
while ( c )
{
if ( ( err = DMAbuf_getwrbuffer ( dev , & dma_buf , & buf_size , ! ! ( file - > f_flags & O_NONBLOCK ) ) ) < 0 )
{
/* Handle nonblocking mode */
if ( ( file - > f_flags & O_NONBLOCK ) & & err = = - EAGAIN )
return p ? p : - EAGAIN ; /* No more space. Return # of accepted bytes */
return err ;
}
l = c ;
if ( l > buf_size )
l = buf_size ;
returned = l ;
used = l ;
if ( ! audio_devs [ dev ] - > d - > copy_user )
{
if ( ( dma_buf + l ) >
( audio_devs [ dev ] - > dmap_out - > raw_buf + audio_devs [ dev ] - > dmap_out - > buffsize ) )
{
printk ( KERN_ERR " audio: Buffer error 3 (%lx,%d), (%lx, %d) \n " , ( long ) dma_buf , l , ( long ) audio_devs [ dev ] - > dmap_out - > raw_buf , ( int ) audio_devs [ dev ] - > dmap_out - > buffsize ) ;
return - EDOM ;
}
if ( dma_buf < audio_devs [ dev ] - > dmap_out - > raw_buf )
{
printk ( KERN_ERR " audio: Buffer error 13 (%lx<%lx) \n " , ( long ) dma_buf , ( long ) audio_devs [ dev ] - > dmap_out - > raw_buf ) ;
return - EDOM ;
}
if ( copy_from_user ( dma_buf , & ( buf ) [ p ] , l ) )
return - EFAULT ;
}
else audio_devs [ dev ] - > d - > copy_user ( dev ,
dma_buf , 0 ,
buf , p ,
c , buf_size ,
& used , & returned ,
l ) ;
l = returned ;
if ( audio_devs [ dev ] - > local_conversion & CNV_MU_LAW )
{
translate_bytes ( ulaw_dsp , ( unsigned char * ) dma_buf , l ) ;
}
c - = used ;
p + = used ;
DMAbuf_move_wrpointer ( dev , l ) ;
}
return count ;
}
int audio_read ( int dev , struct file * file , char __user * buf , int count )
{
int c , p , l ;
char * dmabuf ;
int buf_no ;
dev = dev > > 4 ;
p = 0 ;
c = count ;
if ( ! ( audio_devs [ dev ] - > open_mode & OPEN_READ ) )
return - EPERM ;
if ( ( audio_devs [ dev ] - > audio_mode & AM_WRITE ) & & ! ( audio_devs [ dev ] - > flags & DMA_DUPLEX ) )
sync_output ( dev ) ;
if ( audio_devs [ dev ] - > flags & DMA_DUPLEX )
audio_devs [ dev ] - > audio_mode | = AM_READ ;
else
audio_devs [ dev ] - > audio_mode = AM_READ ;
while ( c )
{
if ( ( buf_no = DMAbuf_getrdbuffer ( dev , & dmabuf , & l , ! ! ( file - > f_flags & O_NONBLOCK ) ) ) < 0 )
{
/*
* Nonblocking mode handling . Return current # of bytes
*/
if ( p > 0 ) /* Avoid throwing away data */
return p ; /* Return it instead */
if ( ( file - > f_flags & O_NONBLOCK ) & & buf_no = = - EAGAIN )
return - EAGAIN ;
return buf_no ;
}
if ( l > c )
l = c ;
/*
* Insert any local processing here .
*/
if ( audio_devs [ dev ] - > local_conversion & CNV_MU_LAW )
{
translate_bytes ( dsp_ulaw , ( unsigned char * ) dmabuf , l ) ;
}
{
char * fixit = dmabuf ;
if ( copy_to_user ( & ( buf ) [ p ] , fixit , l ) )
return - EFAULT ;
2012-09-28 13:24:57 +04:00
}
2005-04-17 02:20:36 +04:00
DMAbuf_rmchars ( dev , buf_no , l ) ;
p + = l ;
c - = l ;
}
return count - c ;
}
int audio_ioctl ( int dev , struct file * file , unsigned int cmd , void __user * arg )
{
int val , count ;
unsigned long flags ;
struct dma_buffparms * dmap ;
int __user * p = arg ;
dev = dev > > 4 ;
if ( _IOC_TYPE ( cmd ) = = ' C ' ) {
if ( audio_devs [ dev ] - > coproc ) /* Coprocessor ioctl */
return audio_devs [ dev ] - > coproc - > ioctl ( audio_devs [ dev ] - > coproc - > devc , cmd , arg , 0 ) ;
/* else
printk ( KERN_DEBUG " /dev/dsp%d: No coprocessor for this device \n " , dev ) ; */
return - ENXIO ;
}
else switch ( cmd )
{
case SNDCTL_DSP_SYNC :
if ( ! ( audio_devs [ dev ] - > open_mode & OPEN_WRITE ) )
return 0 ;
if ( audio_devs [ dev ] - > dmap_out - > fragment_size = = 0 )
return 0 ;
sync_output ( dev ) ;
DMAbuf_sync ( dev ) ;
DMAbuf_reset ( dev ) ;
return 0 ;
case SNDCTL_DSP_POST :
if ( ! ( audio_devs [ dev ] - > open_mode & OPEN_WRITE ) )
return 0 ;
if ( audio_devs [ dev ] - > dmap_out - > fragment_size = = 0 )
return 0 ;
audio_devs [ dev ] - > dmap_out - > flags | = DMA_POST | DMA_DIRTY ;
sync_output ( dev ) ;
dma_ioctl ( dev , SNDCTL_DSP_POST , NULL ) ;
return 0 ;
case SNDCTL_DSP_RESET :
audio_devs [ dev ] - > audio_mode = AM_NONE ;
DMAbuf_reset ( dev ) ;
return 0 ;
case SNDCTL_DSP_GETFMTS :
val = audio_devs [ dev ] - > format_mask | AFMT_MU_LAW ;
break ;
case SNDCTL_DSP_SETFMT :
if ( get_user ( val , p ) )
return - EFAULT ;
val = set_format ( dev , val ) ;
break ;
case SNDCTL_DSP_GETISPACE :
if ( ! ( audio_devs [ dev ] - > open_mode & OPEN_READ ) )
return 0 ;
if ( ( audio_devs [ dev ] - > audio_mode & AM_WRITE ) & & ! ( audio_devs [ dev ] - > flags & DMA_DUPLEX ) )
return - EBUSY ;
return dma_ioctl ( dev , cmd , arg ) ;
case SNDCTL_DSP_GETOSPACE :
if ( ! ( audio_devs [ dev ] - > open_mode & OPEN_WRITE ) )
return - EPERM ;
if ( ( audio_devs [ dev ] - > audio_mode & AM_READ ) & & ! ( audio_devs [ dev ] - > flags & DMA_DUPLEX ) )
return - EBUSY ;
return dma_ioctl ( dev , cmd , arg ) ;
case SNDCTL_DSP_NONBLOCK :
2009-02-07 01:25:24 +03:00
spin_lock ( & file - > f_lock ) ;
2005-04-17 02:20:36 +04:00
file - > f_flags | = O_NONBLOCK ;
2009-02-07 01:25:24 +03:00
spin_unlock ( & file - > f_lock ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
case SNDCTL_DSP_GETCAPS :
val = 1 | DSP_CAP_MMAP ; /* Revision level of this ioctl() */
if ( audio_devs [ dev ] - > flags & DMA_DUPLEX & &
audio_devs [ dev ] - > open_mode = = OPEN_READWRITE )
val | = DSP_CAP_DUPLEX ;
if ( audio_devs [ dev ] - > coproc )
val | = DSP_CAP_COPROC ;
if ( audio_devs [ dev ] - > d - > local_qlen ) /* Device has hidden buffers */
val | = DSP_CAP_BATCH ;
if ( audio_devs [ dev ] - > d - > trigger ) /* Supports SETTRIGGER */
val | = DSP_CAP_TRIGGER ;
break ;
case SOUND_PCM_WRITE_RATE :
if ( get_user ( val , p ) )
return - EFAULT ;
val = audio_devs [ dev ] - > d - > set_speed ( dev , val ) ;
break ;
case SOUND_PCM_READ_RATE :
val = audio_devs [ dev ] - > d - > set_speed ( dev , 0 ) ;
break ;
case SNDCTL_DSP_STEREO :
if ( get_user ( val , p ) )
return - EFAULT ;
if ( val > 1 | | val < 0 )
return - EINVAL ;
val = audio_devs [ dev ] - > d - > set_channels ( dev , val + 1 ) - 1 ;
break ;
case SOUND_PCM_WRITE_CHANNELS :
if ( get_user ( val , p ) )
return - EFAULT ;
val = audio_devs [ dev ] - > d - > set_channels ( dev , val ) ;
break ;
case SOUND_PCM_READ_CHANNELS :
val = audio_devs [ dev ] - > d - > set_channels ( dev , 0 ) ;
break ;
case SOUND_PCM_READ_BITS :
val = audio_devs [ dev ] - > d - > set_bits ( dev , 0 ) ;
break ;
case SNDCTL_DSP_SETDUPLEX :
if ( audio_devs [ dev ] - > open_mode ! = OPEN_READWRITE )
return - EPERM ;
return ( audio_devs [ dev ] - > flags & DMA_DUPLEX ) ? 0 : - EIO ;
case SNDCTL_DSP_PROFILE :
if ( get_user ( val , p ) )
return - EFAULT ;
if ( audio_devs [ dev ] - > open_mode & OPEN_WRITE )
audio_devs [ dev ] - > dmap_out - > applic_profile = val ;
if ( audio_devs [ dev ] - > open_mode & OPEN_READ )
audio_devs [ dev ] - > dmap_in - > applic_profile = val ;
return 0 ;
case SNDCTL_DSP_GETODELAY :
dmap = audio_devs [ dev ] - > dmap_out ;
if ( ! ( audio_devs [ dev ] - > open_mode & OPEN_WRITE ) )
return - EINVAL ;
if ( ! ( dmap - > flags & DMA_ALLOC_DONE ) )
{
val = 0 ;
break ;
}
spin_lock_irqsave ( & dmap - > lock , flags ) ;
/* Compute number of bytes that have been played */
count = DMAbuf_get_buffer_pointer ( dev , dmap , DMODE_OUTPUT ) ;
if ( count < dmap - > fragment_size & & dmap - > qhead ! = 0 )
count + = dmap - > bytes_in_use ; /* Pointer wrap not handled yet */
count + = dmap - > byte_counter ;
2011-03-31 05:57:33 +04:00
/* Subtract current count from the number of bytes written by app */
2005-04-17 02:20:36 +04:00
count = dmap - > user_counter - count ;
if ( count < 0 )
count = 0 ;
spin_unlock_irqrestore ( & dmap - > lock , flags ) ;
val = count ;
break ;
default :
return dma_ioctl ( dev , cmd , arg ) ;
}
return put_user ( val , p ) ;
}
void audio_init_devices ( void )
{
/*
* NOTE ! This routine could be called several times during boot .
*/
}
void reorganize_buffers ( int dev , struct dma_buffparms * dmap , int recording )
{
/*
* This routine breaks the physical device buffers to logical ones .
*/
struct audio_operations * dsp_dev = audio_devs [ dev ] ;
unsigned i , n ;
unsigned sr , nc , sz , bsz ;
sr = dsp_dev - > d - > set_speed ( dev , 0 ) ;
nc = dsp_dev - > d - > set_channels ( dev , 0 ) ;
sz = dsp_dev - > d - > set_bits ( dev , 0 ) ;
if ( sz = = 8 )
dmap - > neutral_byte = NEUTRAL8 ;
else
dmap - > neutral_byte = NEUTRAL16 ;
if ( sr < 1 | | nc < 1 | | sz < 1 )
{
/* printk(KERN_DEBUG "Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz);*/
sr = DSP_DEFAULT_SPEED ;
nc = 1 ;
sz = 8 ;
}
sz = sr * nc * sz ;
sz / = 8 ; /* #bits -> #bytes */
dmap - > data_rate = sz ;
if ( ! dmap - > needs_reorg )
return ;
dmap - > needs_reorg = 0 ;
if ( dmap - > fragment_size = = 0 )
{
/* Compute the fragment size using the default algorithm */
/*
* Compute a buffer size for time not exceeding 1 second .
* Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds
* of sound ( using the current speed , sample size and # channels ) .
*/
bsz = dmap - > buffsize ;
while ( bsz > sz )
bsz / = 2 ;
if ( bsz = = dmap - > buffsize )
bsz / = 2 ; /* Needs at least 2 buffers */
/*
* Split the computed fragment to smaller parts . After 3.5 a9
* the default subdivision is 4 which should give better
* results when recording .
*/
if ( dmap - > subdivision = = 0 ) /* Not already set */
{
dmap - > subdivision = 4 ; /* Init to the default value */
if ( ( bsz / dmap - > subdivision ) > 4096 )
dmap - > subdivision * = 2 ;
if ( ( bsz / dmap - > subdivision ) < 4096 )
dmap - > subdivision = 1 ;
}
bsz / = dmap - > subdivision ;
if ( bsz < 16 )
bsz = 16 ; /* Just a sanity check */
dmap - > fragment_size = bsz ;
}
else
{
/*
* The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or
* the buffer size computation has already been done .
*/
if ( dmap - > fragment_size > ( dmap - > buffsize / 2 ) )
dmap - > fragment_size = ( dmap - > buffsize / 2 ) ;
bsz = dmap - > fragment_size ;
}
if ( audio_devs [ dev ] - > min_fragment )
if ( bsz < ( 1 < < audio_devs [ dev ] - > min_fragment ) )
bsz = 1 < < audio_devs [ dev ] - > min_fragment ;
if ( audio_devs [ dev ] - > max_fragment )
if ( bsz > ( 1 < < audio_devs [ dev ] - > max_fragment ) )
bsz = 1 < < audio_devs [ dev ] - > max_fragment ;
bsz & = ~ 0x07 ; /* Force size which is multiple of 8 bytes */
# ifdef OS_DMA_ALIGN_CHECK
OS_DMA_ALIGN_CHECK ( bsz ) ;
# endif
n = dmap - > buffsize / bsz ;
if ( n > MAX_SUB_BUFFERS )
n = MAX_SUB_BUFFERS ;
if ( n > dmap - > max_fragments )
n = dmap - > max_fragments ;
if ( n < 2 )
{
n = 2 ;
bsz / = 2 ;
}
dmap - > nbufs = n ;
dmap - > bytes_in_use = n * bsz ;
dmap - > fragment_size = bsz ;
dmap - > max_byte_counter = ( dmap - > data_rate * 60 * 60 ) +
dmap - > bytes_in_use ; /* Approximately one hour */
if ( dmap - > raw_buf )
{
memset ( dmap - > raw_buf , dmap - > neutral_byte , dmap - > bytes_in_use ) ;
}
for ( i = 0 ; i < dmap - > nbufs ; i + + )
{
dmap - > counts [ i ] = 0 ;
}
dmap - > flags | = DMA_ALLOC_DONE | DMA_EMPTY ;
}
static int dma_subdivide ( int dev , struct dma_buffparms * dmap , int fact )
{
if ( fact = = 0 )
{
fact = dmap - > subdivision ;
if ( fact = = 0 )
fact = 1 ;
return fact ;
}
if ( dmap - > subdivision ! = 0 | | dmap - > fragment_size ) /* Too late to change */
return - EINVAL ;
if ( fact > MAX_REALTIME_FACTOR )
return - EINVAL ;
if ( fact ! = 1 & & fact ! = 2 & & fact ! = 4 & & fact ! = 8 & & fact ! = 16 )
return - EINVAL ;
dmap - > subdivision = fact ;
return fact ;
}
static int dma_set_fragment ( int dev , struct dma_buffparms * dmap , int fact )
{
int bytes , count ;
if ( fact = = 0 )
return - EIO ;
if ( dmap - > subdivision ! = 0 | |
dmap - > fragment_size ) /* Too late to change */
return - EINVAL ;
bytes = fact & 0xffff ;
count = ( fact > > 16 ) & 0x7fff ;
if ( count = = 0 )
count = MAX_SUB_BUFFERS ;
else if ( count < MAX_SUB_BUFFERS )
count + + ;
if ( bytes < 4 | | bytes > 17 ) /* <16 || > 512k */
return - EINVAL ;
if ( count < 2 )
return - EINVAL ;
if ( audio_devs [ dev ] - > min_fragment > 0 )
if ( bytes < audio_devs [ dev ] - > min_fragment )
bytes = audio_devs [ dev ] - > min_fragment ;
if ( audio_devs [ dev ] - > max_fragment > 0 )
if ( bytes > audio_devs [ dev ] - > max_fragment )
bytes = audio_devs [ dev ] - > max_fragment ;
# ifdef OS_DMA_MINBITS
if ( bytes < OS_DMA_MINBITS )
bytes = OS_DMA_MINBITS ;
# endif
dmap - > fragment_size = ( 1 < < bytes ) ;
dmap - > max_fragments = count ;
if ( dmap - > fragment_size > dmap - > buffsize )
dmap - > fragment_size = dmap - > buffsize ;
if ( dmap - > fragment_size = = dmap - > buffsize & &
audio_devs [ dev ] - > flags & DMA_AUTOMODE )
dmap - > fragment_size / = 2 ; /* Needs at least 2 buffers */
dmap - > subdivision = 1 ; /* Disable SNDCTL_DSP_SUBDIVIDE */
return bytes | ( ( count - 1 ) < < 16 ) ;
}
static int dma_ioctl ( int dev , unsigned int cmd , void __user * arg )
{
struct dma_buffparms * dmap_out = audio_devs [ dev ] - > dmap_out ;
struct dma_buffparms * dmap_in = audio_devs [ dev ] - > dmap_in ;
struct dma_buffparms * dmap ;
audio_buf_info info ;
count_info cinfo ;
int fact , ret , changed , bits , count , err ;
unsigned long flags ;
switch ( cmd )
{
case SNDCTL_DSP_SUBDIVIDE :
ret = 0 ;
if ( get_user ( fact , ( int __user * ) arg ) )
return - EFAULT ;
if ( audio_devs [ dev ] - > open_mode & OPEN_WRITE )
ret = dma_subdivide ( dev , dmap_out , fact ) ;
if ( ret < 0 )
return ret ;
if ( audio_devs [ dev ] - > open_mode ! = OPEN_WRITE | |
( audio_devs [ dev ] - > flags & DMA_DUPLEX & &
audio_devs [ dev ] - > open_mode & OPEN_READ ) )
ret = dma_subdivide ( dev , dmap_in , fact ) ;
if ( ret < 0 )
return ret ;
break ;
case SNDCTL_DSP_GETISPACE :
case SNDCTL_DSP_GETOSPACE :
dmap = dmap_out ;
if ( cmd = = SNDCTL_DSP_GETISPACE & & ! ( audio_devs [ dev ] - > open_mode & OPEN_READ ) )
return - EINVAL ;
if ( cmd = = SNDCTL_DSP_GETOSPACE & & ! ( audio_devs [ dev ] - > open_mode & OPEN_WRITE ) )
return - EINVAL ;
if ( cmd = = SNDCTL_DSP_GETISPACE & & audio_devs [ dev ] - > flags & DMA_DUPLEX )
dmap = dmap_in ;
if ( dmap - > mapping_flags & DMA_MAP_MAPPED )
return - EINVAL ;
if ( ! ( dmap - > flags & DMA_ALLOC_DONE ) )
reorganize_buffers ( dev , dmap , ( cmd = = SNDCTL_DSP_GETISPACE ) ) ;
info . fragstotal = dmap - > nbufs ;
if ( cmd = = SNDCTL_DSP_GETISPACE )
info . fragments = dmap - > qlen ;
else
{
if ( ! DMAbuf_space_in_queue ( dev ) )
info . fragments = 0 ;
else
{
info . fragments = DMAbuf_space_in_queue ( dev ) ;
if ( audio_devs [ dev ] - > d - > local_qlen )
{
int tmp = audio_devs [ dev ] - > d - > local_qlen ( dev ) ;
if ( tmp & & info . fragments )
tmp - - ; /*
* This buffer has been counted twice
*/
info . fragments - = tmp ;
}
}
}
if ( info . fragments < 0 )
info . fragments = 0 ;
else if ( info . fragments > dmap - > nbufs )
info . fragments = dmap - > nbufs ;
info . fragsize = dmap - > fragment_size ;
info . bytes = info . fragments * dmap - > fragment_size ;
if ( cmd = = SNDCTL_DSP_GETISPACE & & dmap - > qlen )
info . bytes - = dmap - > counts [ dmap - > qhead ] ;
else
{
info . fragments = info . bytes / dmap - > fragment_size ;
info . bytes - = dmap - > user_counter % dmap - > fragment_size ;
}
if ( copy_to_user ( arg , & info , sizeof ( info ) ) )
return - EFAULT ;
return 0 ;
case SNDCTL_DSP_SETTRIGGER :
if ( get_user ( bits , ( int __user * ) arg ) )
return - EFAULT ;
bits & = audio_devs [ dev ] - > open_mode ;
if ( audio_devs [ dev ] - > d - > trigger = = NULL )
return - EINVAL ;
if ( ! ( audio_devs [ dev ] - > flags & DMA_DUPLEX ) & & ( bits & PCM_ENABLE_INPUT ) & &
( bits & PCM_ENABLE_OUTPUT ) )
return - EINVAL ;
if ( bits & PCM_ENABLE_INPUT )
{
spin_lock_irqsave ( & dmap_in - > lock , flags ) ;
changed = ( audio_devs [ dev ] - > enable_bits ^ bits ) & PCM_ENABLE_INPUT ;
if ( changed & & audio_devs [ dev ] - > go )
{
reorganize_buffers ( dev , dmap_in , 1 ) ;
if ( ( err = audio_devs [ dev ] - > d - > prepare_for_input ( dev ,
dmap_in - > fragment_size , dmap_in - > nbufs ) ) < 0 ) {
spin_unlock_irqrestore ( & dmap_in - > lock , flags ) ;
2009-11-12 19:43:11 +03:00
return err ;
2005-04-17 02:20:36 +04:00
}
dmap_in - > dma_mode = DMODE_INPUT ;
audio_devs [ dev ] - > enable_bits | = PCM_ENABLE_INPUT ;
DMAbuf_activate_recording ( dev , dmap_in ) ;
} else
audio_devs [ dev ] - > enable_bits & = ~ PCM_ENABLE_INPUT ;
spin_unlock_irqrestore ( & dmap_in - > lock , flags ) ;
}
if ( bits & PCM_ENABLE_OUTPUT )
{
spin_lock_irqsave ( & dmap_out - > lock , flags ) ;
changed = ( audio_devs [ dev ] - > enable_bits ^ bits ) & PCM_ENABLE_OUTPUT ;
if ( changed & &
( dmap_out - > mapping_flags & DMA_MAP_MAPPED | | dmap_out - > qlen > 0 ) & &
audio_devs [ dev ] - > go )
{
if ( ! ( dmap_out - > flags & DMA_ALLOC_DONE ) )
reorganize_buffers ( dev , dmap_out , 0 ) ;
dmap_out - > dma_mode = DMODE_OUTPUT ;
audio_devs [ dev ] - > enable_bits | = PCM_ENABLE_OUTPUT ;
dmap_out - > counts [ dmap_out - > qhead ] = dmap_out - > fragment_size ;
DMAbuf_launch_output ( dev , dmap_out ) ;
} else
audio_devs [ dev ] - > enable_bits & = ~ PCM_ENABLE_OUTPUT ;
spin_unlock_irqrestore ( & dmap_out - > lock , flags ) ;
}
#if 0
if ( changed & & audio_devs [ dev ] - > d - > trigger )
audio_devs [ dev ] - > d - > trigger ( dev , bits * audio_devs [ dev ] - > go ) ;
# endif
/* Falls through... */
case SNDCTL_DSP_GETTRIGGER :
ret = audio_devs [ dev ] - > enable_bits ;
break ;
case SNDCTL_DSP_SETSYNCRO :
if ( ! audio_devs [ dev ] - > d - > trigger )
return - EINVAL ;
audio_devs [ dev ] - > d - > trigger ( dev , 0 ) ;
audio_devs [ dev ] - > go = 0 ;
return 0 ;
case SNDCTL_DSP_GETIPTR :
if ( ! ( audio_devs [ dev ] - > open_mode & OPEN_READ ) )
return - EINVAL ;
spin_lock_irqsave ( & dmap_in - > lock , flags ) ;
cinfo . bytes = dmap_in - > byte_counter ;
cinfo . ptr = DMAbuf_get_buffer_pointer ( dev , dmap_in , DMODE_INPUT ) & ~ 3 ;
if ( cinfo . ptr < dmap_in - > fragment_size & & dmap_in - > qtail ! = 0 )
cinfo . bytes + = dmap_in - > bytes_in_use ; /* Pointer wrap not handled yet */
cinfo . blocks = dmap_in - > qlen ;
cinfo . bytes + = cinfo . ptr ;
if ( dmap_in - > mapping_flags & DMA_MAP_MAPPED )
dmap_in - > qlen = 0 ; /* Reset interrupt counter */
spin_unlock_irqrestore ( & dmap_in - > lock , flags ) ;
if ( copy_to_user ( arg , & cinfo , sizeof ( cinfo ) ) )
return - EFAULT ;
return 0 ;
case SNDCTL_DSP_GETOPTR :
if ( ! ( audio_devs [ dev ] - > open_mode & OPEN_WRITE ) )
return - EINVAL ;
spin_lock_irqsave ( & dmap_out - > lock , flags ) ;
cinfo . bytes = dmap_out - > byte_counter ;
cinfo . ptr = DMAbuf_get_buffer_pointer ( dev , dmap_out , DMODE_OUTPUT ) & ~ 3 ;
if ( cinfo . ptr < dmap_out - > fragment_size & & dmap_out - > qhead ! = 0 )
cinfo . bytes + = dmap_out - > bytes_in_use ; /* Pointer wrap not handled yet */
cinfo . blocks = dmap_out - > qlen ;
cinfo . bytes + = cinfo . ptr ;
if ( dmap_out - > mapping_flags & DMA_MAP_MAPPED )
dmap_out - > qlen = 0 ; /* Reset interrupt counter */
spin_unlock_irqrestore ( & dmap_out - > lock , flags ) ;
if ( copy_to_user ( arg , & cinfo , sizeof ( cinfo ) ) )
return - EFAULT ;
return 0 ;
case SNDCTL_DSP_GETODELAY :
if ( ! ( audio_devs [ dev ] - > open_mode & OPEN_WRITE ) )
return - EINVAL ;
if ( ! ( dmap_out - > flags & DMA_ALLOC_DONE ) )
{
ret = 0 ;
break ;
}
spin_lock_irqsave ( & dmap_out - > lock , flags ) ;
/* Compute number of bytes that have been played */
count = DMAbuf_get_buffer_pointer ( dev , dmap_out , DMODE_OUTPUT ) ;
if ( count < dmap_out - > fragment_size & & dmap_out - > qhead ! = 0 )
count + = dmap_out - > bytes_in_use ; /* Pointer wrap not handled yet */
count + = dmap_out - > byte_counter ;
2011-03-31 05:57:33 +04:00
/* Subtract current count from the number of bytes written by app */
2005-04-17 02:20:36 +04:00
count = dmap_out - > user_counter - count ;
if ( count < 0 )
count = 0 ;
spin_unlock_irqrestore ( & dmap_out - > lock , flags ) ;
ret = count ;
break ;
case SNDCTL_DSP_POST :
if ( audio_devs [ dev ] - > dmap_out - > qlen > 0 )
if ( ! ( audio_devs [ dev ] - > dmap_out - > flags & DMA_ACTIVE ) )
DMAbuf_launch_output ( dev , audio_devs [ dev ] - > dmap_out ) ;
return 0 ;
case SNDCTL_DSP_GETBLKSIZE :
dmap = dmap_out ;
if ( audio_devs [ dev ] - > open_mode & OPEN_WRITE )
reorganize_buffers ( dev , dmap_out , ( audio_devs [ dev ] - > open_mode = = OPEN_READ ) ) ;
if ( audio_devs [ dev ] - > open_mode = = OPEN_READ | |
( audio_devs [ dev ] - > flags & DMA_DUPLEX & &
audio_devs [ dev ] - > open_mode & OPEN_READ ) )
reorganize_buffers ( dev , dmap_in , ( audio_devs [ dev ] - > open_mode = = OPEN_READ ) ) ;
if ( audio_devs [ dev ] - > open_mode = = OPEN_READ )
dmap = dmap_in ;
ret = dmap - > fragment_size ;
break ;
case SNDCTL_DSP_SETFRAGMENT :
ret = 0 ;
if ( get_user ( fact , ( int __user * ) arg ) )
return - EFAULT ;
if ( audio_devs [ dev ] - > open_mode & OPEN_WRITE )
ret = dma_set_fragment ( dev , dmap_out , fact ) ;
if ( ret < 0 )
return ret ;
if ( audio_devs [ dev ] - > open_mode = = OPEN_READ | |
( audio_devs [ dev ] - > flags & DMA_DUPLEX & &
audio_devs [ dev ] - > open_mode & OPEN_READ ) )
ret = dma_set_fragment ( dev , dmap_in , fact ) ;
if ( ret < 0 )
return ret ;
if ( ! arg ) /* don't know what this is good for, but preserve old semantics */
return 0 ;
break ;
default :
if ( ! audio_devs [ dev ] - > d - > ioctl )
return - EINVAL ;
return audio_devs [ dev ] - > d - > ioctl ( dev , cmd , arg ) ;
}
return put_user ( ret , ( int __user * ) arg ) ;
}