2019-05-27 08:55:06 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2009-03-03 14:37:50 -03:00
/*
* Conexant Cx231xx audio extension
*
* Copyright ( C ) 2008 < srinivasa . deevi at conexant dot com >
* Based on em28xx driver
*/
2014-11-01 08:09:44 -03:00
# include "cx231xx.h"
2009-03-03 14:37:50 -03:00
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/sound.h>
# include <linux/spinlock.h>
# include <linux/soundcard.h>
# include <linux/slab.h>
# include <linux/module.h>
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/info.h>
# include <sound/initval.h>
# include <sound/control.h>
# include <media/v4l2-common.h>
static int debug ;
module_param ( debug , int , 0644 ) ;
MODULE_PARM_DESC ( debug , " activates debug info " ) ;
static int index [ SNDRV_CARDS ] = SNDRV_DEFAULT_IDX ;
static int cx231xx_isoc_audio_deinit ( struct cx231xx * dev )
{
int i ;
2014-11-03 06:07:38 -03:00
dev_dbg ( dev - > dev , " Stopping isoc \n " ) ;
2009-03-03 14:37:50 -03:00
for ( i = 0 ; i < CX231XX_AUDIO_BUFS ; i + + ) {
2009-03-03 06:14:34 -03:00
if ( dev - > adev . urb [ i ] ) {
if ( ! irqs_disabled ( ) )
usb_kill_urb ( dev - > adev . urb [ i ] ) ;
else
usb_unlink_urb ( dev - > adev . urb [ i ] ) ;
2009-03-03 14:37:50 -03:00
2009-03-03 06:14:34 -03:00
usb_free_urb ( dev - > adev . urb [ i ] ) ;
dev - > adev . urb [ i ] = NULL ;
2009-03-03 14:37:50 -03:00
2009-03-03 06:14:34 -03:00
kfree ( dev - > adev . transfer_buffer [ i ] ) ;
dev - > adev . transfer_buffer [ i ] = NULL ;
}
2009-03-03 14:37:50 -03:00
}
return 0 ;
}
2010-07-06 18:12:25 -03:00
static int cx231xx_bulk_audio_deinit ( struct cx231xx * dev )
{
int i ;
2014-11-03 06:07:38 -03:00
dev_dbg ( dev - > dev , " Stopping bulk \n " ) ;
2010-07-06 18:12:25 -03:00
for ( i = 0 ; i < CX231XX_AUDIO_BUFS ; i + + ) {
if ( dev - > adev . urb [ i ] ) {
if ( ! irqs_disabled ( ) )
usb_kill_urb ( dev - > adev . urb [ i ] ) ;
else
usb_unlink_urb ( dev - > adev . urb [ i ] ) ;
usb_free_urb ( dev - > adev . urb [ i ] ) ;
dev - > adev . urb [ i ] = NULL ;
kfree ( dev - > adev . transfer_buffer [ i ] ) ;
dev - > adev . transfer_buffer [ i ] = NULL ;
}
}
return 0 ;
}
2009-03-03 14:37:50 -03:00
static void cx231xx_audio_isocirq ( struct urb * urb )
{
2009-03-03 06:14:34 -03:00
struct cx231xx * dev = urb - > context ;
int i ;
unsigned int oldptr ;
int period_elapsed = 0 ;
int status ;
unsigned char * cp ;
unsigned int stride ;
2009-03-03 14:37:50 -03:00
struct snd_pcm_substream * substream ;
2009-03-03 06:14:34 -03:00
struct snd_pcm_runtime * runtime ;
2012-01-10 09:48:50 -02:00
if ( dev - > state & DEV_DISCONNECTED )
return ;
2009-03-03 06:14:34 -03:00
switch ( urb - > status ) {
case 0 : /* success */
case - ETIMEDOUT : /* NAK */
break ;
case - ECONNRESET : /* kill */
case - ENOENT :
case - ESHUTDOWN :
return ;
default : /* error */
2018-08-03 10:35:59 -04:00
dev_dbg ( dev - > dev , " urb completion error %d. \n " ,
2014-11-02 07:44:47 -03:00
urb - > status ) ;
2009-03-03 06:14:34 -03:00
break ;
2009-03-03 14:37:50 -03:00
}
2010-09-27 03:07:22 -03:00
if ( atomic_read ( & dev - > stream_started ) = = 0 )
return ;
2009-03-03 14:37:50 -03:00
if ( dev - > adev . capture_pcm_substream ) {
substream = dev - > adev . capture_pcm_substream ;
runtime = substream - > runtime ;
stride = runtime - > frame_bits > > 3 ;
for ( i = 0 ; i < urb - > number_of_packets ; i + + ) {
2018-07-10 12:18:29 -04:00
unsigned long flags ;
2009-03-03 13:31:36 -03:00
int length = urb - > iso_frame_desc [ i ] . actual_length /
stride ;
2009-03-03 14:37:50 -03:00
cp = ( unsigned char * ) urb - > transfer_buffer +
2009-03-03 13:31:36 -03:00
urb - > iso_frame_desc [ i ] . offset ;
2009-03-03 14:37:50 -03:00
if ( ! length )
continue ;
oldptr = dev - > adev . hwptr_done_capture ;
if ( oldptr + length > = runtime - > buffer_size ) {
2009-03-03 13:31:36 -03:00
unsigned int cnt ;
cnt = runtime - > buffer_size - oldptr ;
2009-03-03 14:37:50 -03:00
memcpy ( runtime - > dma_area + oldptr * stride , cp ,
cnt * stride ) ;
memcpy ( runtime - > dma_area , cp + cnt * stride ,
length * stride - cnt * stride ) ;
} else {
memcpy ( runtime - > dma_area + oldptr * stride , cp ,
length * stride ) ;
}
2018-07-10 12:18:29 -04:00
snd_pcm_stream_lock_irqsave ( substream , flags ) ;
2009-03-03 14:37:50 -03:00
dev - > adev . hwptr_done_capture + = length ;
2009-03-04 17:49:01 -03:00
if ( dev - > adev . hwptr_done_capture > =
runtime - > buffer_size )
dev - > adev . hwptr_done_capture - =
runtime - > buffer_size ;
2009-03-03 14:37:50 -03:00
dev - > adev . capture_transfer_done + = length ;
2009-03-04 17:49:01 -03:00
if ( dev - > adev . capture_transfer_done > =
runtime - > period_size ) {
dev - > adev . capture_transfer_done - =
runtime - > period_size ;
2009-03-03 14:37:50 -03:00
period_elapsed = 1 ;
}
2018-07-10 12:18:29 -04:00
snd_pcm_stream_unlock_irqrestore ( substream , flags ) ;
2009-03-03 14:37:50 -03:00
}
if ( period_elapsed )
snd_pcm_period_elapsed ( substream ) ;
}
urb - > status = 0 ;
status = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( status < 0 ) {
2014-11-03 06:07:38 -03:00
dev_err ( dev - > dev ,
2014-11-02 07:21:44 -03:00
" resubmit of audio urb failed (error=%i) \n " ,
status ) ;
2009-03-03 14:37:50 -03:00
}
return ;
}
2010-07-06 18:12:25 -03:00
static void cx231xx_audio_bulkirq ( struct urb * urb )
{
struct cx231xx * dev = urb - > context ;
unsigned int oldptr ;
int period_elapsed = 0 ;
int status ;
unsigned char * cp ;
unsigned int stride ;
struct snd_pcm_substream * substream ;
struct snd_pcm_runtime * runtime ;
2012-01-10 09:48:50 -02:00
if ( dev - > state & DEV_DISCONNECTED )
return ;
2010-07-06 18:12:25 -03:00
switch ( urb - > status ) {
case 0 : /* success */
case - ETIMEDOUT : /* NAK */
break ;
case - ECONNRESET : /* kill */
case - ENOENT :
case - ESHUTDOWN :
return ;
default : /* error */
2018-08-03 10:35:59 -04:00
dev_dbg ( dev - > dev , " urb completion error %d. \n " ,
2014-11-02 07:44:47 -03:00
urb - > status ) ;
2010-07-06 18:12:25 -03:00
break ;
}
2010-09-27 03:07:22 -03:00
if ( atomic_read ( & dev - > stream_started ) = = 0 )
return ;
2010-07-06 18:12:25 -03:00
if ( dev - > adev . capture_pcm_substream ) {
substream = dev - > adev . capture_pcm_substream ;
runtime = substream - > runtime ;
stride = runtime - > frame_bits > > 3 ;
if ( 1 ) {
2018-07-10 12:18:29 -04:00
unsigned long flags ;
2010-07-06 18:12:25 -03:00
int length = urb - > actual_length /
stride ;
cp = ( unsigned char * ) urb - > transfer_buffer ;
oldptr = dev - > adev . hwptr_done_capture ;
if ( oldptr + length > = runtime - > buffer_size ) {
unsigned int cnt ;
cnt = runtime - > buffer_size - oldptr ;
memcpy ( runtime - > dma_area + oldptr * stride , cp ,
cnt * stride ) ;
memcpy ( runtime - > dma_area , cp + cnt * stride ,
length * stride - cnt * stride ) ;
} else {
memcpy ( runtime - > dma_area + oldptr * stride , cp ,
length * stride ) ;
}
2018-07-10 12:18:29 -04:00
snd_pcm_stream_lock_irqsave ( substream , flags ) ;
2010-07-06 18:12:25 -03:00
dev - > adev . hwptr_done_capture + = length ;
if ( dev - > adev . hwptr_done_capture > =
runtime - > buffer_size )
dev - > adev . hwptr_done_capture - =
runtime - > buffer_size ;
dev - > adev . capture_transfer_done + = length ;
if ( dev - > adev . capture_transfer_done > =
runtime - > period_size ) {
dev - > adev . capture_transfer_done - =
runtime - > period_size ;
period_elapsed = 1 ;
}
2018-07-10 12:18:29 -04:00
snd_pcm_stream_unlock_irqrestore ( substream , flags ) ;
2010-07-06 18:12:25 -03:00
}
if ( period_elapsed )
snd_pcm_period_elapsed ( substream ) ;
}
urb - > status = 0 ;
status = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( status < 0 ) {
2014-11-03 06:07:38 -03:00
dev_err ( dev - > dev ,
2014-11-02 07:21:44 -03:00
" resubmit of audio urb failed (error=%i) \n " ,
status ) ;
2010-07-06 18:12:25 -03:00
}
return ;
}
2009-03-03 14:37:50 -03:00
static int cx231xx_init_audio_isoc ( struct cx231xx * dev )
{
2009-03-03 06:14:34 -03:00
int i , errCode ;
int sb_size ;
2009-03-03 14:37:50 -03:00
2014-11-03 06:07:38 -03:00
dev_dbg ( dev - > dev ,
2014-11-02 07:21:44 -03:00
" %s: Starting ISO AUDIO transfers \n " , __func__ ) ;
2009-03-03 14:37:50 -03:00
2012-01-10 09:48:50 -02:00
if ( dev - > state & DEV_DISCONNECTED )
return - ENODEV ;
2010-07-06 18:12:25 -03:00
sb_size = CX231XX_ISO_NUM_AUDIO_PACKETS * dev - > adev . max_pkt_size ;
2009-03-03 14:37:50 -03:00
for ( i = 0 ; i < CX231XX_AUDIO_BUFS ; i + + ) {
struct urb * urb ;
int j , k ;
dev - > adev . transfer_buffer [ i ] = kmalloc ( sb_size , GFP_ATOMIC ) ;
if ( ! dev - > adev . transfer_buffer [ i ] )
return - ENOMEM ;
memset ( dev - > adev . transfer_buffer [ i ] , 0x80 , sb_size ) ;
2010-07-06 18:12:25 -03:00
urb = usb_alloc_urb ( CX231XX_ISO_NUM_AUDIO_PACKETS , GFP_ATOMIC ) ;
2009-03-03 14:37:50 -03:00
if ( ! urb ) {
for ( j = 0 ; j < i ; j + + ) {
usb_free_urb ( dev - > adev . urb [ j ] ) ;
kfree ( dev - > adev . transfer_buffer [ j ] ) ;
}
return - ENOMEM ;
}
urb - > dev = dev - > udev ;
urb - > context = dev ;
2009-03-04 17:49:01 -03:00
urb - > pipe = usb_rcvisocpipe ( dev - > udev ,
dev - > adev . end_point_addr ) ;
2012-06-18 00:15:21 -03:00
urb - > transfer_flags = URB_ISO_ASAP ;
2009-03-03 14:37:50 -03:00
urb - > transfer_buffer = dev - > adev . transfer_buffer [ i ] ;
urb - > interval = 1 ;
urb - > complete = cx231xx_audio_isocirq ;
2010-07-06 18:12:25 -03:00
urb - > number_of_packets = CX231XX_ISO_NUM_AUDIO_PACKETS ;
2009-03-03 14:37:50 -03:00
urb - > transfer_buffer_length = sb_size ;
2010-07-06 18:12:25 -03:00
for ( j = k = 0 ; j < CX231XX_ISO_NUM_AUDIO_PACKETS ;
2009-03-04 17:49:01 -03:00
j + + , k + = dev - > adev . max_pkt_size ) {
2009-03-03 14:37:50 -03:00
urb - > iso_frame_desc [ j ] . offset = k ;
2009-03-03 06:14:34 -03:00
urb - > iso_frame_desc [ j ] . length = dev - > adev . max_pkt_size ;
2009-03-03 14:37:50 -03:00
}
dev - > adev . urb [ i ] = urb ;
}
for ( i = 0 ; i < CX231XX_AUDIO_BUFS ; i + + ) {
errCode = usb_submit_urb ( dev - > adev . urb [ i ] , GFP_ATOMIC ) ;
if ( errCode < 0 ) {
cx231xx_isoc_audio_deinit ( dev ) ;
return errCode ;
}
}
return errCode ;
}
2010-07-06 18:12:25 -03:00
static int cx231xx_init_audio_bulk ( struct cx231xx * dev )
{
int i , errCode ;
int sb_size ;
2014-11-03 06:07:38 -03:00
dev_dbg ( dev - > dev ,
2014-11-02 07:21:44 -03:00
" %s: Starting BULK AUDIO transfers \n " , __func__ ) ;
2010-07-06 18:12:25 -03:00
2012-01-10 09:48:50 -02:00
if ( dev - > state & DEV_DISCONNECTED )
return - ENODEV ;
2010-07-06 18:12:25 -03:00
sb_size = CX231XX_NUM_AUDIO_PACKETS * dev - > adev . max_pkt_size ;
for ( i = 0 ; i < CX231XX_AUDIO_BUFS ; i + + ) {
struct urb * urb ;
int j ;
dev - > adev . transfer_buffer [ i ] = kmalloc ( sb_size , GFP_ATOMIC ) ;
if ( ! dev - > adev . transfer_buffer [ i ] )
return - ENOMEM ;
memset ( dev - > adev . transfer_buffer [ i ] , 0x80 , sb_size ) ;
urb = usb_alloc_urb ( CX231XX_NUM_AUDIO_PACKETS , GFP_ATOMIC ) ;
if ( ! urb ) {
for ( j = 0 ; j < i ; j + + ) {
usb_free_urb ( dev - > adev . urb [ j ] ) ;
kfree ( dev - > adev . transfer_buffer [ j ] ) ;
}
return - ENOMEM ;
}
urb - > dev = dev - > udev ;
urb - > context = dev ;
urb - > pipe = usb_rcvbulkpipe ( dev - > udev ,
dev - > adev . end_point_addr ) ;
2012-06-18 00:15:21 -03:00
urb - > transfer_flags = 0 ;
2010-07-06 18:12:25 -03:00
urb - > transfer_buffer = dev - > adev . transfer_buffer [ i ] ;
urb - > complete = cx231xx_audio_bulkirq ;
urb - > transfer_buffer_length = sb_size ;
dev - > adev . urb [ i ] = urb ;
}
for ( i = 0 ; i < CX231XX_AUDIO_BUFS ; i + + ) {
errCode = usb_submit_urb ( dev - > adev . urb [ i ] , GFP_ATOMIC ) ;
if ( errCode < 0 ) {
cx231xx_bulk_audio_deinit ( dev ) ;
return errCode ;
}
}
return errCode ;
}
2017-08-13 08:43:08 -04:00
static const struct snd_pcm_hardware snd_cx231xx_hw_capture = {
2018-01-04 13:08:56 -05:00
. info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
2009-03-10 21:16:26 -03:00
SNDRV_PCM_INFO_MMAP_VALID ,
2009-03-03 14:37:50 -03:00
. formats = SNDRV_PCM_FMTBIT_S16_LE ,
. rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT ,
. rate_min = 48000 ,
. rate_max = 48000 ,
. channels_min = 2 ,
. channels_max = 2 ,
. buffer_bytes_max = 62720 * 8 , /* just about the value in usbaudio.c */
2009-03-03 13:31:36 -03:00
. period_bytes_min = 64 , /* 12544/2, */
2009-03-03 14:37:50 -03:00
. period_bytes_max = 12544 ,
. periods_min = 2 ,
2009-03-03 13:31:36 -03:00
. periods_max = 98 , /* 12544, */
2009-03-03 14:37:50 -03:00
} ;
static int snd_cx231xx_capture_open ( struct snd_pcm_substream * substream )
{
struct cx231xx * dev = snd_pcm_substream_chip ( substream ) ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
int ret = 0 ;
2014-11-03 06:07:38 -03:00
dev_dbg ( dev - > dev ,
2014-11-02 07:44:47 -03:00
" opening device and trying to acquire exclusive lock \n " ) ;
2009-03-03 14:37:50 -03:00
2012-01-10 09:48:50 -02:00
if ( dev - > state & DEV_DISCONNECTED ) {
2014-11-03 06:07:38 -03:00
dev_err ( dev - > dev ,
2014-11-02 07:21:44 -03:00
" Can't open. the device was removed. \n " ) ;
2012-01-10 09:48:50 -02:00
return - ENODEV ;
}
2009-03-03 06:14:34 -03:00
/* set alternate setting for audio interface */
2009-03-04 17:49:01 -03:00
/* 1 - 48000 samples per sec */
2010-09-27 03:07:22 -03:00
mutex_lock ( & dev - > lock ) ;
2010-07-06 18:12:25 -03:00
if ( dev - > USE_ISO )
ret = cx231xx_set_alt_setting ( dev , INDEX_AUDIO , 1 ) ;
else
ret = cx231xx_set_alt_setting ( dev , INDEX_AUDIO , 0 ) ;
2010-09-27 03:07:22 -03:00
mutex_unlock ( & dev - > lock ) ;
2009-03-03 06:14:34 -03:00
if ( ret < 0 ) {
2014-11-03 06:07:38 -03:00
dev_err ( dev - > dev ,
2014-11-02 07:21:44 -03:00
" failed to set alternate setting ! \n " ) ;
2009-03-03 14:37:50 -03:00
2009-03-03 06:14:34 -03:00
return ret ;
}
2009-03-03 14:37:50 -03:00
runtime - > hw = snd_cx231xx_hw_capture ;
2009-03-03 06:14:34 -03:00
mutex_lock ( & dev - > lock ) ;
2010-09-27 03:07:22 -03:00
/* inform hardware to start streaming */
ret = cx231xx_capture_start ( dev , 1 , Audio ) ;
2009-03-03 14:37:50 -03:00
dev - > adev . users + + ;
2009-03-03 06:14:34 -03:00
mutex_unlock ( & dev - > lock ) ;
2009-03-03 14:37:50 -03:00
snd_pcm_hw_constraint_integer ( runtime , SNDRV_PCM_HW_PARAM_PERIODS ) ;
dev - > adev . capture_pcm_substream = substream ;
runtime - > private_data = dev ;
return 0 ;
}
static int snd_cx231xx_pcm_close ( struct snd_pcm_substream * substream )
{
2009-03-03 06:14:34 -03:00
int ret ;
2009-03-03 14:37:50 -03:00
struct cx231xx * dev = snd_pcm_substream_chip ( substream ) ;
2014-11-03 06:07:38 -03:00
dev_dbg ( dev - > dev , " closing device \n " ) ;
2009-03-03 14:37:50 -03:00
2010-09-27 03:07:22 -03:00
/* inform hardware to stop streaming */
mutex_lock ( & dev - > lock ) ;
2010-07-06 18:12:25 -03:00
ret = cx231xx_capture_start ( dev , 0 , Audio ) ;
2009-03-03 06:14:34 -03:00
/* set alternate setting for audio interface */
2009-03-04 17:49:01 -03:00
/* 1 - 48000 samples per sec */
ret = cx231xx_set_alt_setting ( dev , INDEX_AUDIO , 0 ) ;
2009-03-03 06:14:34 -03:00
if ( ret < 0 ) {
2014-11-03 06:07:38 -03:00
dev_err ( dev - > dev ,
2014-11-02 07:21:44 -03:00
" failed to set alternate setting ! \n " ) ;
2009-03-03 14:37:50 -03:00
2010-09-27 03:07:22 -03:00
mutex_unlock ( & dev - > lock ) ;
2009-03-03 06:14:34 -03:00
return ret ;
}
2009-03-03 14:37:50 -03:00
2009-03-03 06:14:34 -03:00
dev - > adev . users - - ;
2009-03-03 14:37:50 -03:00
mutex_unlock ( & dev - > lock ) ;
if ( dev - > adev . users = = 0 & & dev - > adev . shutdown = = 1 ) {
2014-11-03 06:07:38 -03:00
dev_dbg ( dev - > dev , " audio users: %d \n " , dev - > adev . users ) ;
dev_dbg ( dev - > dev , " disabling audio stream! \n " ) ;
2009-03-03 14:37:50 -03:00
dev - > adev . shutdown = 0 ;
2014-11-03 06:07:38 -03:00
dev_dbg ( dev - > dev , " released lock \n " ) ;
2010-09-27 03:07:22 -03:00
if ( atomic_read ( & dev - > stream_started ) > 0 ) {
atomic_set ( & dev - > stream_started , 0 ) ;
schedule_work ( & dev - > wq_trigger ) ;
}
2009-03-03 14:37:50 -03:00
}
return 0 ;
}
static int snd_cx231xx_prepare ( struct snd_pcm_substream * substream )
{
2010-07-06 18:12:25 -03:00
struct cx231xx * dev = snd_pcm_substream_chip ( substream ) ;
dev - > adev . hwptr_done_capture = 0 ;
dev - > adev . capture_transfer_done = 0 ;
2009-03-03 14:37:50 -03:00
return 0 ;
}
2010-09-27 03:07:22 -03:00
static void audio_trigger ( struct work_struct * work )
{
struct cx231xx * dev = container_of ( work , struct cx231xx , wq_trigger ) ;
if ( atomic_read ( & dev - > stream_started ) ) {
2014-11-03 06:07:38 -03:00
dev_dbg ( dev - > dev , " starting capture " ) ;
2010-09-27 03:07:22 -03:00
if ( is_fw_load ( dev ) = = 0 )
cx25840_call ( dev , core , load_fw ) ;
if ( dev - > USE_ISO )
cx231xx_init_audio_isoc ( dev ) ;
else
cx231xx_init_audio_bulk ( dev ) ;
} else {
2014-11-03 06:07:38 -03:00
dev_dbg ( dev - > dev , " stopping capture " ) ;
2010-09-27 03:07:22 -03:00
cx231xx_isoc_audio_deinit ( dev ) ;
}
}
2009-03-03 14:37:50 -03:00
static int snd_cx231xx_capture_trigger ( struct snd_pcm_substream * substream ,
2009-03-03 06:14:34 -03:00
int cmd )
2009-03-03 14:37:50 -03:00
{
struct cx231xx * dev = snd_pcm_substream_chip ( substream ) ;
2012-05-14 10:14:53 -03:00
int retval = 0 ;
2009-03-03 14:37:50 -03:00
2012-01-10 09:48:50 -02:00
if ( dev - > state & DEV_DISCONNECTED )
return - ENODEV ;
2009-03-03 06:14:34 -03:00
spin_lock ( & dev - > adev . slock ) ;
2009-03-03 14:37:50 -03:00
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
2010-09-27 03:07:22 -03:00
atomic_set ( & dev - > stream_started , 1 ) ;
2009-03-03 14:37:50 -03:00
break ;
case SNDRV_PCM_TRIGGER_STOP :
2010-09-27 03:07:22 -03:00
atomic_set ( & dev - > stream_started , 0 ) ;
2009-03-03 06:14:34 -03:00
break ;
2009-03-03 14:37:50 -03:00
default :
retval = - EINVAL ;
2012-05-14 10:14:53 -03:00
break ;
2009-03-03 14:37:50 -03:00
}
2009-03-03 06:14:34 -03:00
spin_unlock ( & dev - > adev . slock ) ;
2010-09-27 03:07:22 -03:00
schedule_work ( & dev - > wq_trigger ) ;
2012-05-14 10:14:53 -03:00
return retval ;
2009-03-03 14:37:50 -03:00
}
static snd_pcm_uframes_t snd_cx231xx_capture_pointer ( struct snd_pcm_substream
2009-03-03 06:14:34 -03:00
* substream )
2009-03-03 14:37:50 -03:00
{
struct cx231xx * dev ;
2009-03-03 06:14:34 -03:00
unsigned long flags ;
2009-03-03 14:37:50 -03:00
snd_pcm_uframes_t hwptr_done ;
dev = snd_pcm_substream_chip ( substream ) ;
2009-03-03 06:14:34 -03:00
spin_lock_irqsave ( & dev - > adev . slock , flags ) ;
2009-03-03 14:37:50 -03:00
hwptr_done = dev - > adev . hwptr_done_capture ;
2009-03-03 06:14:34 -03:00
spin_unlock_irqrestore ( & dev - > adev . slock , flags ) ;
2009-03-03 14:37:50 -03:00
return hwptr_done ;
}
2016-09-07 21:59:06 -03:00
static const struct snd_pcm_ops snd_cx231xx_pcm_capture = {
2009-03-03 06:14:34 -03:00
. open = snd_cx231xx_capture_open ,
. close = snd_cx231xx_pcm_close ,
. prepare = snd_cx231xx_prepare ,
. trigger = snd_cx231xx_capture_trigger ,
. pointer = snd_cx231xx_capture_pointer ,
2009-03-03 14:37:50 -03:00
} ;
static int cx231xx_audio_init ( struct cx231xx * dev )
{
struct cx231xx_audio * adev = & dev - > adev ;
2009-03-03 06:14:34 -03:00
struct snd_pcm * pcm ;
struct snd_card * card ;
static int devnr ;
int err ;
struct usb_interface * uif ;
int i , isoc_pipe = 0 ;
2009-03-03 14:37:50 -03:00
if ( dev - > has_alsa_audio ! = 1 ) {
/* This device does not support the extension (in this case
the device is expecting the snd - usb - audio module or
doesn ' t have analog audio support at all ) */
return 0 ;
}
2014-11-03 06:07:38 -03:00
dev_dbg ( dev - > dev ,
2014-11-02 07:21:44 -03:00
" probing for cx231xx non standard usbaudio \n " ) ;
2009-03-03 14:37:50 -03:00
2014-11-03 06:07:38 -03:00
err = snd_card_new ( dev - > dev , index [ devnr ] , " Cx231xx Audio " ,
2014-01-29 14:48:43 +01:00
THIS_MODULE , 0 , & card ) ;
2009-04-06 21:25:29 -03:00
if ( err < 0 )
return err ;
2009-03-03 14:37:50 -03:00
spin_lock_init ( & adev - > slock ) ;
err = snd_pcm_new ( card , " Cx231xx Audio " , 0 , 0 , 1 , & pcm ) ;
2017-03-13 09:53:57 -03:00
if ( err < 0 )
goto err_free_card ;
2009-03-03 14:37:50 -03:00
2009-03-03 06:14:34 -03:00
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_CAPTURE ,
& snd_cx231xx_pcm_capture ) ;
2019-12-10 14:58:42 +01:00
snd_pcm_set_managed_buffer_all ( pcm , SNDRV_DMA_TYPE_VMALLOC , NULL , 0 , 0 ) ;
2009-03-03 14:37:50 -03:00
pcm - > info_flags = 0 ;
pcm - > private_data = dev ;
2018-09-10 16:20:42 -04:00
strscpy ( pcm - > name , " Conexant cx231xx Capture " , sizeof ( pcm - > name ) ) ;
strscpy ( card - > driver , " Cx231xx-Audio " , sizeof ( card - > driver ) ) ;
strscpy ( card - > shortname , " Cx231xx Audio " , sizeof ( card - > shortname ) ) ;
strscpy ( card - > longname , " Conexant cx231xx Audio " , sizeof ( card - > longname ) ) ;
2009-03-03 14:37:50 -03:00
2010-09-27 03:07:22 -03:00
INIT_WORK ( & dev - > wq_trigger , audio_trigger ) ;
2009-03-03 14:37:50 -03:00
err = snd_card_register ( card ) ;
2017-03-13 09:53:57 -03:00
if ( err < 0 )
goto err_free_card ;
2009-03-03 14:37:50 -03:00
adev - > sndcard = card ;
adev - > udev = dev - > udev ;
2009-03-03 06:14:34 -03:00
/* compute alternate max packet sizes for Audio */
uif =
dev - > udev - > actconfig - > interface [ dev - > current_pcb_config .
hs_config_info [ 0 ] . interface_info .
audio_index + 1 ] ;
2009-03-03 14:37:50 -03:00
2017-03-13 09:53:58 -03:00
if ( uif - > altsetting [ 0 ] . desc . bNumEndpoints < isoc_pipe + 1 ) {
err = - ENODEV ;
goto err_free_card ;
}
2009-03-03 06:14:34 -03:00
adev - > end_point_addr =
2013-02-07 15:28:53 -03:00
uif - > altsetting [ 0 ] . endpoint [ isoc_pipe ] . desc .
bEndpointAddress ;
2009-03-03 14:37:50 -03:00
2009-03-03 06:14:34 -03:00
adev - > num_alt = uif - > num_altsetting ;
2014-11-03 06:07:38 -03:00
dev_info ( dev - > dev ,
2014-11-02 07:21:44 -03:00
" audio EndPoint Addr 0x%x, Alternate settings: %i \n " ,
2014-11-01 08:59:03 -03:00
adev - > end_point_addr , adev - > num_alt ) ;
treewide: kmalloc() -> kmalloc_array()
The kmalloc() function has a 2-factor argument form, kmalloc_array(). This
patch replaces cases of:
kmalloc(a * b, gfp)
with:
kmalloc_array(a * b, gfp)
as well as handling cases of:
kmalloc(a * b * c, gfp)
with:
kmalloc(array3_size(a, b, c), gfp)
as it's slightly less ugly than:
kmalloc_array(array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
kmalloc(4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The tools/ directory was manually excluded, since it has its own
implementation of kmalloc().
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
kmalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
kmalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
kmalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
- kmalloc
+ kmalloc_array
(
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
kmalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
kmalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kmalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
kmalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
kmalloc(C1 * C2 * C3, ...)
|
kmalloc(
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
kmalloc(sizeof(THING) * C2, ...)
|
kmalloc(sizeof(TYPE) * C2, ...)
|
kmalloc(C1 * C2 * C3, ...)
|
kmalloc(C1 * C2, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- (E1) * E2
+ E1, E2
, ...)
|
- kmalloc
+ kmalloc_array
(
- (E1) * (E2)
+ E1, E2
, ...)
|
- kmalloc
+ kmalloc_array
(
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-12 13:55:00 -07:00
adev - > alt_max_pkt_size = kmalloc_array ( 32 , adev - > num_alt , GFP_KERNEL ) ;
2017-03-13 09:53:57 -03:00
if ( ! adev - > alt_max_pkt_size ) {
err = - ENOMEM ;
goto err_free_card ;
}
2009-03-03 14:37:50 -03:00
2009-03-03 06:14:34 -03:00
for ( i = 0 ; i < adev - > num_alt ; i + + ) {
2017-03-13 09:53:58 -03:00
u16 tmp ;
if ( uif - > altsetting [ i ] . desc . bNumEndpoints < isoc_pipe + 1 ) {
err = - ENODEV ;
goto err_free_pkt_size ;
}
tmp = le16_to_cpu ( uif - > altsetting [ i ] . endpoint [ isoc_pipe ] . desc .
2009-03-03 06:14:34 -03:00
wMaxPacketSize ) ;
adev - > alt_max_pkt_size [ i ] =
( tmp & 0x07ff ) * ( ( ( tmp & 0x1800 ) > > 11 ) + 1 ) ;
2014-11-03 06:07:38 -03:00
dev_dbg ( dev - > dev ,
2014-11-02 07:21:44 -03:00
" audio alternate setting %i, max size= %i \n " , i ,
adev - > alt_max_pkt_size [ i ] ) ;
2009-03-03 06:14:34 -03:00
}
2009-03-03 14:37:50 -03:00
return 0 ;
2017-03-13 09:53:57 -03:00
2017-03-13 09:53:58 -03:00
err_free_pkt_size :
kfree ( adev - > alt_max_pkt_size ) ;
2017-03-13 09:53:57 -03:00
err_free_card :
snd_card_free ( card ) ;
return err ;
2009-03-03 14:37:50 -03:00
}
static int cx231xx_audio_fini ( struct cx231xx * dev )
{
if ( dev = = NULL )
return 0 ;
if ( dev - > has_alsa_audio ! = 1 ) {
/* This device does not support the extension (in this case
the device is expecting the snd - usb - audio module or
doesn ' t have analog audio support at all ) */
return 0 ;
}
if ( dev - > adev . sndcard ) {
2020-11-10 04:04:03 +01:00
snd_card_free_when_closed ( dev - > adev . sndcard ) ;
2009-03-03 06:14:34 -03:00
kfree ( dev - > adev . alt_max_pkt_size ) ;
2009-03-03 14:37:50 -03:00
dev - > adev . sndcard = NULL ;
}
return 0 ;
}
static struct cx231xx_ops audio_ops = {
2009-03-03 06:14:34 -03:00
. id = CX231XX_AUDIO ,
2009-03-03 14:37:50 -03:00
. name = " Cx231xx Audio Extension " ,
. init = cx231xx_audio_init ,
. fini = cx231xx_audio_fini ,
} ;
static int __init cx231xx_alsa_register ( void )
{
return cx231xx_register_extension ( & audio_ops ) ;
}
static void __exit cx231xx_alsa_unregister ( void )
{
cx231xx_unregister_extension ( & audio_ops ) ;
}
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Srinivasa Deevi <srinivasa.deevi@conexant.com> " ) ;
MODULE_DESCRIPTION ( " Cx231xx Audio driver " ) ;
module_init ( cx231xx_alsa_register ) ;
module_exit ( cx231xx_alsa_unregister ) ;