2012-02-02 22:01:34 +05:30
/*
* f_uac2 . c - - USB Audio Class 2.0 Function
*
* Copyright ( C ) 2011
* Yadwinder Singh ( yadi . brar01 @ gmail . com )
* Jaswinder Singh ( jaswinder . singh @ linaro . org )
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/usb/audio.h>
# include <linux/usb/audio-v2.h>
# include <linux/platform_device.h>
# include <linux/module.h>
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
/* Playback(USB-IN) Default Stereo - Fl/Fr */
static int p_chmask = 0x3 ;
module_param ( p_chmask , uint , S_IRUGO ) ;
MODULE_PARM_DESC ( p_chmask , " Playback Channel Mask " ) ;
/* Playback Default 48 KHz */
static int p_srate = 48000 ;
module_param ( p_srate , uint , S_IRUGO ) ;
MODULE_PARM_DESC ( p_srate , " Playback Sampling Rate " ) ;
/* Playback Default 16bits/sample */
static int p_ssize = 2 ;
module_param ( p_ssize , uint , S_IRUGO ) ;
MODULE_PARM_DESC ( p_ssize , " Playback Sample Size(bytes) " ) ;
/* Capture(USB-OUT) Default Stereo - Fl/Fr */
static int c_chmask = 0x3 ;
module_param ( c_chmask , uint , S_IRUGO ) ;
MODULE_PARM_DESC ( c_chmask , " Capture Channel Mask " ) ;
/* Capture Default 64 KHz */
static int c_srate = 64000 ;
module_param ( c_srate , uint , S_IRUGO ) ;
MODULE_PARM_DESC ( c_srate , " Capture Sampling Rate " ) ;
/* Capture Default 16bits/sample */
static int c_ssize = 2 ;
module_param ( c_ssize , uint , S_IRUGO ) ;
MODULE_PARM_DESC ( c_ssize , " Capture Sample Size(bytes) " ) ;
/* Keep everyone on toes */
# define USB_XFERS 2
/*
* The driver implements a simple UAC_2 topology .
* USB - OUT - > IT_1 - > OT_3 - > ALSA_Capture
* ALSA_Playback - > IT_2 - > OT_4 - > USB - IN
* Capture and Playback sampling rates are independently
* controlled by two clock sources :
* CLK_5 : = c_srate , and CLK_6 : = p_srate
*/
# define USB_OUT_IT_ID 1
# define IO_IN_IT_ID 2
# define IO_OUT_OT_ID 3
# define USB_IN_OT_ID 4
# define USB_OUT_CLK_ID 5
# define USB_IN_CLK_ID 6
# define CONTROL_ABSENT 0
# define CONTROL_RDONLY 1
# define CONTROL_RDWR 3
# define CLK_FREQ_CTRL 0
# define CLK_VLD_CTRL 2
# define COPY_CTRL 0
# define CONN_CTRL 2
# define OVRLD_CTRL 4
# define CLSTR_CTRL 6
# define UNFLW_CTRL 8
# define OVFLW_CTRL 10
const char * uac2_name = " snd_uac2 " ;
struct uac2_req {
struct uac2_rtd_params * pp ; /* parent param */
struct usb_request * req ;
} ;
struct uac2_rtd_params {
2013-05-30 18:23:33 +05:30
struct snd_uac2_chip * uac2 ; /* parent chip */
2012-02-02 22:01:34 +05:30
bool ep_enabled ; /* if the ep is enabled */
/* Size of the ring buffer */
size_t dma_bytes ;
unsigned char * dma_area ;
struct snd_pcm_substream * ss ;
/* Ring buffer */
ssize_t hw_ptr ;
void * rbuf ;
size_t period_size ;
unsigned max_psize ;
struct uac2_req ureq [ USB_XFERS ] ;
spinlock_t lock ;
} ;
struct snd_uac2_chip {
struct platform_device pdev ;
struct platform_driver pdrv ;
struct uac2_rtd_params p_prm ;
struct uac2_rtd_params c_prm ;
struct snd_card * card ;
struct snd_pcm * pcm ;
} ;
# define BUFF_SIZE_MAX (PAGE_SIZE * 16)
# define PRD_SIZE_MAX PAGE_SIZE
# define MIN_PERIODS 4
static struct snd_pcm_hardware uac2_pcm_hardware = {
. info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER
| SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID
| SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME ,
. rates = SNDRV_PCM_RATE_CONTINUOUS ,
. periods_max = BUFF_SIZE_MAX / PRD_SIZE_MAX ,
. buffer_bytes_max = BUFF_SIZE_MAX ,
. period_bytes_max = PRD_SIZE_MAX ,
. periods_min = MIN_PERIODS ,
} ;
struct audio_dev {
2012-10-22 22:15:08 +02:00
u8 ac_intf , ac_alt ;
u8 as_out_intf , as_out_alt ;
u8 as_in_intf , as_in_alt ;
2012-02-02 22:01:34 +05:30
struct usb_ep * in_ep , * out_ep ;
struct usb_function func ;
/* The ALSA Sound Card it represents on the USB-Client side */
struct snd_uac2_chip uac2 ;
} ;
static struct audio_dev * agdev_g ;
static inline
struct audio_dev * func_to_agdev ( struct usb_function * f )
{
return container_of ( f , struct audio_dev , func ) ;
}
static inline
struct audio_dev * uac2_to_agdev ( struct snd_uac2_chip * u )
{
return container_of ( u , struct audio_dev , uac2 ) ;
}
static inline
struct snd_uac2_chip * pdev_to_uac2 ( struct platform_device * p )
{
return container_of ( p , struct snd_uac2_chip , pdev ) ;
}
static inline
uint num_channels ( uint chanmask )
{
uint num = 0 ;
while ( chanmask ) {
num + = ( chanmask & 1 ) ;
chanmask > > = 1 ;
}
return num ;
}
static void
agdev_iso_complete ( struct usb_ep * ep , struct usb_request * req )
{
unsigned pending ;
unsigned long flags ;
bool update_alsa = false ;
unsigned char * src , * dst ;
int status = req - > status ;
struct uac2_req * ur = req - > context ;
struct snd_pcm_substream * substream ;
struct uac2_rtd_params * prm = ur - > pp ;
2013-05-30 18:23:33 +05:30
struct snd_uac2_chip * uac2 = prm - > uac2 ;
2012-02-02 22:01:34 +05:30
/* i/f shutting down */
if ( ! prm - > ep_enabled )
return ;
/*
* We can ' t really do much about bad xfers .
* Afterall , the ISOCH xfers could fail legitimately .
*/
if ( status )
pr_debug ( " %s: iso_complete status(%d) %d/%d \n " ,
__func__ , status , req - > actual , req - > length ) ;
substream = prm - > ss ;
/* Do nothing if ALSA isn't active */
if ( ! substream )
goto exit ;
spin_lock_irqsave ( & prm - > lock , flags ) ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
src = prm - > dma_area + prm - > hw_ptr ;
req - > actual = req - > length ;
dst = req - > buf ;
} else {
dst = prm - > dma_area + prm - > hw_ptr ;
src = req - > buf ;
}
pending = prm - > hw_ptr % prm - > period_size ;
pending + = req - > actual ;
if ( pending > = prm - > period_size )
update_alsa = true ;
prm - > hw_ptr = ( prm - > hw_ptr + req - > actual ) % prm - > dma_bytes ;
spin_unlock_irqrestore ( & prm - > lock , flags ) ;
/* Pack USB load in ALSA ring buffer */
memcpy ( dst , src , req - > actual ) ;
exit :
if ( usb_ep_queue ( ep , req , GFP_ATOMIC ) )
dev_err ( & uac2 - > pdev . dev , " %d Error! \n " , __LINE__ ) ;
if ( update_alsa )
snd_pcm_period_elapsed ( substream ) ;
return ;
}
static int
uac2_pcm_trigger ( struct snd_pcm_substream * substream , int cmd )
{
struct snd_uac2_chip * uac2 = snd_pcm_substream_chip ( substream ) ;
struct uac2_rtd_params * prm ;
unsigned long flags ;
int err = 0 ;
2012-12-02 05:33:08 -05:00
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
2012-02-02 22:01:34 +05:30
prm = & uac2 - > p_prm ;
2012-12-02 05:33:08 -05:00
else
2012-02-02 22:01:34 +05:30
prm = & uac2 - > c_prm ;
spin_lock_irqsave ( & prm - > lock , flags ) ;
/* Reset */
prm - > hw_ptr = 0 ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
prm - > ss = substream ;
break ;
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
prm - > ss = NULL ;
break ;
default :
err = - EINVAL ;
}
spin_unlock_irqrestore ( & prm - > lock , flags ) ;
/* Clear buffer after Play stops */
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK & & ! prm - > ss )
memset ( prm - > rbuf , 0 , prm - > max_psize * USB_XFERS ) ;
return err ;
}
static snd_pcm_uframes_t uac2_pcm_pointer ( struct snd_pcm_substream * substream )
{
struct snd_uac2_chip * uac2 = snd_pcm_substream_chip ( substream ) ;
struct uac2_rtd_params * prm ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
prm = & uac2 - > p_prm ;
else
prm = & uac2 - > c_prm ;
return bytes_to_frames ( substream - > runtime , prm - > hw_ptr ) ;
}
static int uac2_pcm_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * hw_params )
{
struct snd_uac2_chip * uac2 = snd_pcm_substream_chip ( substream ) ;
struct uac2_rtd_params * prm ;
int err ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
prm = & uac2 - > p_prm ;
else
prm = & uac2 - > c_prm ;
err = snd_pcm_lib_malloc_pages ( substream ,
params_buffer_bytes ( hw_params ) ) ;
if ( err > = 0 ) {
prm - > dma_bytes = substream - > runtime - > dma_bytes ;
prm - > dma_area = substream - > runtime - > dma_area ;
prm - > period_size = params_period_bytes ( hw_params ) ;
}
return err ;
}
static int uac2_pcm_hw_free ( struct snd_pcm_substream * substream )
{
struct snd_uac2_chip * uac2 = snd_pcm_substream_chip ( substream ) ;
struct uac2_rtd_params * prm ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
prm = & uac2 - > p_prm ;
else
prm = & uac2 - > c_prm ;
prm - > dma_area = NULL ;
prm - > dma_bytes = 0 ;
prm - > period_size = 0 ;
return snd_pcm_lib_free_pages ( substream ) ;
}
static int uac2_pcm_open ( struct snd_pcm_substream * substream )
{
struct snd_uac2_chip * uac2 = snd_pcm_substream_chip ( substream ) ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
runtime - > hw = uac2_pcm_hardware ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
spin_lock_init ( & uac2 - > p_prm . lock ) ;
runtime - > hw . rate_min = p_srate ;
runtime - > hw . formats = SNDRV_PCM_FMTBIT_S16_LE ; /* ! p_ssize ! */
runtime - > hw . channels_min = num_channels ( p_chmask ) ;
runtime - > hw . period_bytes_min = 2 * uac2 - > p_prm . max_psize
/ runtime - > hw . periods_min ;
} else {
spin_lock_init ( & uac2 - > c_prm . lock ) ;
runtime - > hw . rate_min = c_srate ;
runtime - > hw . formats = SNDRV_PCM_FMTBIT_S16_LE ; /* ! c_ssize ! */
runtime - > hw . channels_min = num_channels ( c_chmask ) ;
runtime - > hw . period_bytes_min = 2 * uac2 - > c_prm . max_psize
/ runtime - > hw . periods_min ;
}
runtime - > hw . rate_max = runtime - > hw . rate_min ;
runtime - > hw . channels_max = runtime - > hw . channels_min ;
snd_pcm_hw_constraint_integer ( runtime , SNDRV_PCM_HW_PARAM_PERIODS ) ;
return 0 ;
}
/* ALSA cries without these function pointers */
static int uac2_pcm_null ( struct snd_pcm_substream * substream )
{
return 0 ;
}
static struct snd_pcm_ops uac2_pcm_ops = {
. open = uac2_pcm_open ,
. close = uac2_pcm_null ,
. ioctl = snd_pcm_lib_ioctl ,
. hw_params = uac2_pcm_hw_params ,
. hw_free = uac2_pcm_hw_free ,
. trigger = uac2_pcm_trigger ,
. pointer = uac2_pcm_pointer ,
. prepare = uac2_pcm_null ,
} ;
2012-11-19 13:21:48 -05:00
static int snd_uac2_probe ( struct platform_device * pdev )
2012-02-02 22:01:34 +05:30
{
struct snd_uac2_chip * uac2 = pdev_to_uac2 ( pdev ) ;
struct snd_card * card ;
struct snd_pcm * pcm ;
int err ;
/* Choose any slot, with no id */
2014-01-29 15:04:31 +01:00
err = snd_card_new ( & pdev - > dev , - 1 , NULL , THIS_MODULE , 0 , & card ) ;
2012-02-02 22:01:34 +05:30
if ( err < 0 )
return err ;
uac2 - > card = card ;
/*
* Create first PCM device
* Create a substream only for non - zero channel streams
*/
err = snd_pcm_new ( uac2 - > card , " UAC2 PCM " , 0 ,
p_chmask ? 1 : 0 , c_chmask ? 1 : 0 , & pcm ) ;
if ( err < 0 )
goto snd_fail ;
strcpy ( pcm - > name , " UAC2 PCM " ) ;
pcm - > private_data = uac2 ;
uac2 - > pcm = pcm ;
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_PLAYBACK , & uac2_pcm_ops ) ;
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_CAPTURE , & uac2_pcm_ops ) ;
strcpy ( card - > driver , " UAC2_Gadget " ) ;
strcpy ( card - > shortname , " UAC2_Gadget " ) ;
sprintf ( card - > longname , " UAC2_Gadget %i " , pdev - > id ) ;
snd_pcm_lib_preallocate_pages_for_all ( pcm , SNDRV_DMA_TYPE_CONTINUOUS ,
snd_dma_continuous_data ( GFP_KERNEL ) , 0 , BUFF_SIZE_MAX ) ;
err = snd_card_register ( card ) ;
if ( ! err ) {
platform_set_drvdata ( pdev , card ) ;
return 0 ;
}
snd_fail :
snd_card_free ( card ) ;
uac2 - > pcm = NULL ;
uac2 - > card = NULL ;
return err ;
}
2012-09-06 20:11:02 +02:00
static int snd_uac2_remove ( struct platform_device * pdev )
2012-02-02 22:01:34 +05:30
{
struct snd_card * card = platform_get_drvdata ( pdev ) ;
if ( card )
return snd_card_free ( card ) ;
return 0 ;
}
static int alsa_uac2_init ( struct audio_dev * agdev )
{
struct snd_uac2_chip * uac2 = & agdev - > uac2 ;
int err ;
uac2 - > pdrv . probe = snd_uac2_probe ;
uac2 - > pdrv . remove = snd_uac2_remove ;
uac2 - > pdrv . driver . name = uac2_name ;
uac2 - > pdev . id = 0 ;
uac2 - > pdev . name = uac2_name ;
/* Register snd_uac2 driver */
err = platform_driver_register ( & uac2 - > pdrv ) ;
if ( err )
return err ;
/* Register snd_uac2 device */
err = platform_device_register ( & uac2 - > pdev ) ;
if ( err )
platform_driver_unregister ( & uac2 - > pdrv ) ;
return err ;
}
static void alsa_uac2_exit ( struct audio_dev * agdev )
{
struct snd_uac2_chip * uac2 = & agdev - > uac2 ;
platform_driver_unregister ( & uac2 - > pdrv ) ;
platform_device_unregister ( & uac2 - > pdev ) ;
}
/* --------- USB Function Interface ------------- */
enum {
STR_ASSOC ,
STR_IF_CTRL ,
STR_CLKSRC_IN ,
STR_CLKSRC_OUT ,
STR_USB_IT ,
STR_IO_IT ,
STR_USB_OT ,
STR_IO_OT ,
STR_AS_OUT_ALT0 ,
STR_AS_OUT_ALT1 ,
STR_AS_IN_ALT0 ,
STR_AS_IN_ALT1 ,
} ;
static char clksrc_in [ 8 ] ;
static char clksrc_out [ 8 ] ;
static struct usb_string strings_fn [ ] = {
2012-10-22 22:15:09 +02:00
[ STR_ASSOC ] . s = " Source/Sink " ,
[ STR_IF_CTRL ] . s = " Topology Control " ,
2012-02-02 22:01:34 +05:30
[ STR_CLKSRC_IN ] . s = clksrc_in ,
[ STR_CLKSRC_OUT ] . s = clksrc_out ,
2012-10-22 22:15:09 +02:00
[ STR_USB_IT ] . s = " USBH Out " ,
[ STR_IO_IT ] . s = " USBD Out " ,
[ STR_USB_OT ] . s = " USBH In " ,
[ STR_IO_OT ] . s = " USBD In " ,
[ STR_AS_OUT_ALT0 ] . s = " Playback Inactive " ,
[ STR_AS_OUT_ALT1 ] . s = " Playback Active " ,
[ STR_AS_IN_ALT0 ] . s = " Capture Inactive " ,
[ STR_AS_IN_ALT1 ] . s = " Capture Active " ,
2012-02-02 22:01:34 +05:30
{ } ,
} ;
static struct usb_gadget_strings str_fn = {
. language = 0x0409 , /* en-us */
. strings = strings_fn ,
} ;
static struct usb_gadget_strings * fn_strings [ ] = {
& str_fn ,
NULL ,
} ;
static struct usb_qualifier_descriptor devqual_desc = {
. bLength = sizeof devqual_desc ,
. bDescriptorType = USB_DT_DEVICE_QUALIFIER ,
. bcdUSB = cpu_to_le16 ( 0x200 ) ,
. bDeviceClass = USB_CLASS_MISC ,
. bDeviceSubClass = 0x02 ,
. bDeviceProtocol = 0x01 ,
. bNumConfigurations = 1 ,
. bRESERVED = 0 ,
} ;
static struct usb_interface_assoc_descriptor iad_desc = {
. bLength = sizeof iad_desc ,
. bDescriptorType = USB_DT_INTERFACE_ASSOCIATION ,
. bFirstInterface = 0 ,
. bInterfaceCount = 3 ,
. bFunctionClass = USB_CLASS_AUDIO ,
. bFunctionSubClass = UAC2_FUNCTION_SUBCLASS_UNDEFINED ,
. bFunctionProtocol = UAC_VERSION_2 ,
} ;
/* Audio Control Interface */
static struct usb_interface_descriptor std_ac_if_desc = {
. bLength = sizeof std_ac_if_desc ,
. bDescriptorType = USB_DT_INTERFACE ,
. bAlternateSetting = 0 ,
. bNumEndpoints = 0 ,
. bInterfaceClass = USB_CLASS_AUDIO ,
. bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL ,
. bInterfaceProtocol = UAC_VERSION_2 ,
} ;
/* Clock source for IN traffic */
struct uac_clock_source_descriptor in_clk_src_desc = {
. bLength = sizeof in_clk_src_desc ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
. bDescriptorSubtype = UAC2_CLOCK_SOURCE ,
. bClockID = USB_IN_CLK_ID ,
. bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED ,
. bmControls = ( CONTROL_RDONLY < < CLK_FREQ_CTRL ) ,
. bAssocTerminal = 0 ,
} ;
/* Clock source for OUT traffic */
struct uac_clock_source_descriptor out_clk_src_desc = {
. bLength = sizeof out_clk_src_desc ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
. bDescriptorSubtype = UAC2_CLOCK_SOURCE ,
. bClockID = USB_OUT_CLK_ID ,
. bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED ,
. bmControls = ( CONTROL_RDONLY < < CLK_FREQ_CTRL ) ,
. bAssocTerminal = 0 ,
} ;
/* Input Terminal for USB_OUT */
struct uac2_input_terminal_descriptor usb_out_it_desc = {
. bLength = sizeof usb_out_it_desc ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
. bDescriptorSubtype = UAC_INPUT_TERMINAL ,
. bTerminalID = USB_OUT_IT_ID ,
. wTerminalType = cpu_to_le16 ( UAC_TERMINAL_STREAMING ) ,
. bAssocTerminal = 0 ,
. bCSourceID = USB_OUT_CLK_ID ,
. iChannelNames = 0 ,
. bmControls = ( CONTROL_RDWR < < COPY_CTRL ) ,
} ;
/* Input Terminal for I/O-In */
struct uac2_input_terminal_descriptor io_in_it_desc = {
. bLength = sizeof io_in_it_desc ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
. bDescriptorSubtype = UAC_INPUT_TERMINAL ,
. bTerminalID = IO_IN_IT_ID ,
. wTerminalType = cpu_to_le16 ( UAC_INPUT_TERMINAL_UNDEFINED ) ,
. bAssocTerminal = 0 ,
. bCSourceID = USB_IN_CLK_ID ,
. iChannelNames = 0 ,
. bmControls = ( CONTROL_RDWR < < COPY_CTRL ) ,
} ;
/* Ouput Terminal for USB_IN */
struct uac2_output_terminal_descriptor usb_in_ot_desc = {
. bLength = sizeof usb_in_ot_desc ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
. bDescriptorSubtype = UAC_OUTPUT_TERMINAL ,
. bTerminalID = USB_IN_OT_ID ,
. wTerminalType = cpu_to_le16 ( UAC_TERMINAL_STREAMING ) ,
. bAssocTerminal = 0 ,
. bSourceID = IO_IN_IT_ID ,
. bCSourceID = USB_IN_CLK_ID ,
. bmControls = ( CONTROL_RDWR < < COPY_CTRL ) ,
} ;
/* Ouput Terminal for I/O-Out */
struct uac2_output_terminal_descriptor io_out_ot_desc = {
. bLength = sizeof io_out_ot_desc ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
. bDescriptorSubtype = UAC_OUTPUT_TERMINAL ,
. bTerminalID = IO_OUT_OT_ID ,
. wTerminalType = cpu_to_le16 ( UAC_OUTPUT_TERMINAL_UNDEFINED ) ,
. bAssocTerminal = 0 ,
. bSourceID = USB_OUT_IT_ID ,
. bCSourceID = USB_OUT_CLK_ID ,
. bmControls = ( CONTROL_RDWR < < COPY_CTRL ) ,
} ;
struct uac2_ac_header_descriptor ac_hdr_desc = {
. bLength = sizeof ac_hdr_desc ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
. bDescriptorSubtype = UAC_MS_HEADER ,
. bcdADC = cpu_to_le16 ( 0x200 ) ,
. bCategory = UAC2_FUNCTION_IO_BOX ,
. wTotalLength = sizeof in_clk_src_desc + sizeof out_clk_src_desc
+ sizeof usb_out_it_desc + sizeof io_in_it_desc
+ sizeof usb_in_ot_desc + sizeof io_out_ot_desc ,
. bmControls = 0 ,
} ;
/* Audio Streaming OUT Interface - Alt0 */
static struct usb_interface_descriptor std_as_out_if0_desc = {
. bLength = sizeof std_as_out_if0_desc ,
. bDescriptorType = USB_DT_INTERFACE ,
. bAlternateSetting = 0 ,
. bNumEndpoints = 0 ,
. bInterfaceClass = USB_CLASS_AUDIO ,
. bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING ,
. bInterfaceProtocol = UAC_VERSION_2 ,
} ;
/* Audio Streaming OUT Interface - Alt1 */
static struct usb_interface_descriptor std_as_out_if1_desc = {
. bLength = sizeof std_as_out_if1_desc ,
. bDescriptorType = USB_DT_INTERFACE ,
. bAlternateSetting = 1 ,
. bNumEndpoints = 1 ,
. bInterfaceClass = USB_CLASS_AUDIO ,
. bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING ,
. bInterfaceProtocol = UAC_VERSION_2 ,
} ;
/* Audio Stream OUT Intface Desc */
struct uac2_as_header_descriptor as_out_hdr_desc = {
. bLength = sizeof as_out_hdr_desc ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
. bDescriptorSubtype = UAC_AS_GENERAL ,
. bTerminalLink = USB_OUT_IT_ID ,
. bmControls = 0 ,
. bFormatType = UAC_FORMAT_TYPE_I ,
. bmFormats = cpu_to_le32 ( UAC_FORMAT_TYPE_I_PCM ) ,
. iChannelNames = 0 ,
} ;
/* Audio USB_OUT Format */
struct uac2_format_type_i_descriptor as_out_fmt1_desc = {
. bLength = sizeof as_out_fmt1_desc ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
. bDescriptorSubtype = UAC_FORMAT_TYPE ,
. bFormatType = UAC_FORMAT_TYPE_I ,
} ;
/* STD AS ISO OUT Endpoint */
struct usb_endpoint_descriptor fs_epout_desc = {
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bEndpointAddress = USB_DIR_OUT ,
. bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC ,
. bInterval = 1 ,
} ;
struct usb_endpoint_descriptor hs_epout_desc = {
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC ,
. bInterval = 4 ,
} ;
/* CS AS ISO OUT Endpoint */
static struct uac2_iso_endpoint_descriptor as_iso_out_desc = {
. bLength = sizeof as_iso_out_desc ,
. bDescriptorType = USB_DT_CS_ENDPOINT ,
. bDescriptorSubtype = UAC_EP_GENERAL ,
. bmAttributes = 0 ,
. bmControls = 0 ,
. bLockDelayUnits = 0 ,
. wLockDelay = 0 ,
} ;
/* Audio Streaming IN Interface - Alt0 */
static struct usb_interface_descriptor std_as_in_if0_desc = {
. bLength = sizeof std_as_in_if0_desc ,
. bDescriptorType = USB_DT_INTERFACE ,
. bAlternateSetting = 0 ,
. bNumEndpoints = 0 ,
. bInterfaceClass = USB_CLASS_AUDIO ,
. bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING ,
. bInterfaceProtocol = UAC_VERSION_2 ,
} ;
/* Audio Streaming IN Interface - Alt1 */
static struct usb_interface_descriptor std_as_in_if1_desc = {
. bLength = sizeof std_as_in_if1_desc ,
. bDescriptorType = USB_DT_INTERFACE ,
. bAlternateSetting = 1 ,
. bNumEndpoints = 1 ,
. bInterfaceClass = USB_CLASS_AUDIO ,
. bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING ,
. bInterfaceProtocol = UAC_VERSION_2 ,
} ;
/* Audio Stream IN Intface Desc */
struct uac2_as_header_descriptor as_in_hdr_desc = {
. bLength = sizeof as_in_hdr_desc ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
. bDescriptorSubtype = UAC_AS_GENERAL ,
. bTerminalLink = USB_IN_OT_ID ,
. bmControls = 0 ,
. bFormatType = UAC_FORMAT_TYPE_I ,
. bmFormats = cpu_to_le32 ( UAC_FORMAT_TYPE_I_PCM ) ,
. iChannelNames = 0 ,
} ;
/* Audio USB_IN Format */
struct uac2_format_type_i_descriptor as_in_fmt1_desc = {
. bLength = sizeof as_in_fmt1_desc ,
. bDescriptorType = USB_DT_CS_INTERFACE ,
. bDescriptorSubtype = UAC_FORMAT_TYPE ,
. bFormatType = UAC_FORMAT_TYPE_I ,
} ;
/* STD AS ISO IN Endpoint */
struct usb_endpoint_descriptor fs_epin_desc = {
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bEndpointAddress = USB_DIR_IN ,
. bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC ,
. bInterval = 1 ,
} ;
struct usb_endpoint_descriptor hs_epin_desc = {
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC ,
. bInterval = 4 ,
} ;
/* CS AS ISO IN Endpoint */
static struct uac2_iso_endpoint_descriptor as_iso_in_desc = {
. bLength = sizeof as_iso_in_desc ,
. bDescriptorType = USB_DT_CS_ENDPOINT ,
. bDescriptorSubtype = UAC_EP_GENERAL ,
. bmAttributes = 0 ,
. bmControls = 0 ,
. bLockDelayUnits = 0 ,
. wLockDelay = 0 ,
} ;
static struct usb_descriptor_header * fs_audio_desc [ ] = {
( struct usb_descriptor_header * ) & iad_desc ,
( struct usb_descriptor_header * ) & std_ac_if_desc ,
( struct usb_descriptor_header * ) & ac_hdr_desc ,
( struct usb_descriptor_header * ) & in_clk_src_desc ,
( struct usb_descriptor_header * ) & out_clk_src_desc ,
( struct usb_descriptor_header * ) & usb_out_it_desc ,
( struct usb_descriptor_header * ) & io_in_it_desc ,
( struct usb_descriptor_header * ) & usb_in_ot_desc ,
( struct usb_descriptor_header * ) & io_out_ot_desc ,
( struct usb_descriptor_header * ) & std_as_out_if0_desc ,
( struct usb_descriptor_header * ) & std_as_out_if1_desc ,
( struct usb_descriptor_header * ) & as_out_hdr_desc ,
( struct usb_descriptor_header * ) & as_out_fmt1_desc ,
( struct usb_descriptor_header * ) & fs_epout_desc ,
( struct usb_descriptor_header * ) & as_iso_out_desc ,
( struct usb_descriptor_header * ) & std_as_in_if0_desc ,
( struct usb_descriptor_header * ) & std_as_in_if1_desc ,
( struct usb_descriptor_header * ) & as_in_hdr_desc ,
( struct usb_descriptor_header * ) & as_in_fmt1_desc ,
( struct usb_descriptor_header * ) & fs_epin_desc ,
( struct usb_descriptor_header * ) & as_iso_in_desc ,
NULL ,
} ;
static struct usb_descriptor_header * hs_audio_desc [ ] = {
( struct usb_descriptor_header * ) & iad_desc ,
( struct usb_descriptor_header * ) & std_ac_if_desc ,
( struct usb_descriptor_header * ) & ac_hdr_desc ,
( struct usb_descriptor_header * ) & in_clk_src_desc ,
( struct usb_descriptor_header * ) & out_clk_src_desc ,
( struct usb_descriptor_header * ) & usb_out_it_desc ,
( struct usb_descriptor_header * ) & io_in_it_desc ,
( struct usb_descriptor_header * ) & usb_in_ot_desc ,
( struct usb_descriptor_header * ) & io_out_ot_desc ,
( struct usb_descriptor_header * ) & std_as_out_if0_desc ,
( struct usb_descriptor_header * ) & std_as_out_if1_desc ,
( struct usb_descriptor_header * ) & as_out_hdr_desc ,
( struct usb_descriptor_header * ) & as_out_fmt1_desc ,
( struct usb_descriptor_header * ) & hs_epout_desc ,
( struct usb_descriptor_header * ) & as_iso_out_desc ,
( struct usb_descriptor_header * ) & std_as_in_if0_desc ,
( struct usb_descriptor_header * ) & std_as_in_if1_desc ,
( struct usb_descriptor_header * ) & as_in_hdr_desc ,
( struct usb_descriptor_header * ) & as_in_fmt1_desc ,
( struct usb_descriptor_header * ) & hs_epin_desc ,
( struct usb_descriptor_header * ) & as_iso_in_desc ,
NULL ,
} ;
struct cntrl_cur_lay3 {
__u32 dCUR ;
} ;
struct cntrl_range_lay3 {
__u16 wNumSubRanges ;
__u32 dMIN ;
__u32 dMAX ;
__u32 dRES ;
} __packed ;
static inline void
free_ep ( struct uac2_rtd_params * prm , struct usb_ep * ep )
{
2013-05-30 18:23:33 +05:30
struct snd_uac2_chip * uac2 = prm - > uac2 ;
2012-02-02 22:01:34 +05:30
int i ;
prm - > ep_enabled = false ;
for ( i = 0 ; i < USB_XFERS ; i + + ) {
if ( prm - > ureq [ i ] . req ) {
usb_ep_dequeue ( ep , prm - > ureq [ i ] . req ) ;
usb_ep_free_request ( ep , prm - > ureq [ i ] . req ) ;
prm - > ureq [ i ] . req = NULL ;
}
}
if ( usb_ep_disable ( ep ) )
dev_err ( & uac2 - > pdev . dev ,
" %s:%d Error! \n " , __func__ , __LINE__ ) ;
}
static int __init
afunc_bind ( struct usb_configuration * cfg , struct usb_function * fn )
{
struct audio_dev * agdev = func_to_agdev ( fn ) ;
struct snd_uac2_chip * uac2 = & agdev - > uac2 ;
struct usb_composite_dev * cdev = cfg - > cdev ;
struct usb_gadget * gadget = cdev - > gadget ;
struct uac2_rtd_params * prm ;
int ret ;
ret = usb_interface_id ( cfg , fn ) ;
if ( ret < 0 ) {
dev_err ( & uac2 - > pdev . dev ,
" %s:%d Error! \n " , __func__ , __LINE__ ) ;
return ret ;
}
std_ac_if_desc . bInterfaceNumber = ret ;
2012-10-22 22:15:08 +02:00
agdev - > ac_intf = ret ;
agdev - > ac_alt = 0 ;
2012-02-02 22:01:34 +05:30
ret = usb_interface_id ( cfg , fn ) ;
if ( ret < 0 ) {
dev_err ( & uac2 - > pdev . dev ,
" %s:%d Error! \n " , __func__ , __LINE__ ) ;
return ret ;
}
std_as_out_if0_desc . bInterfaceNumber = ret ;
std_as_out_if1_desc . bInterfaceNumber = ret ;
2012-10-22 22:15:08 +02:00
agdev - > as_out_intf = ret ;
agdev - > as_out_alt = 0 ;
2012-02-02 22:01:34 +05:30
ret = usb_interface_id ( cfg , fn ) ;
if ( ret < 0 ) {
dev_err ( & uac2 - > pdev . dev ,
" %s:%d Error! \n " , __func__ , __LINE__ ) ;
return ret ;
}
std_as_in_if0_desc . bInterfaceNumber = ret ;
std_as_in_if1_desc . bInterfaceNumber = ret ;
2012-10-22 22:15:08 +02:00
agdev - > as_in_intf = ret ;
agdev - > as_in_alt = 0 ;
2012-02-02 22:01:34 +05:30
agdev - > out_ep = usb_ep_autoconfig ( gadget , & fs_epout_desc ) ;
2012-10-22 22:14:59 +02:00
if ( ! agdev - > out_ep ) {
2012-02-02 22:01:34 +05:30
dev_err ( & uac2 - > pdev . dev ,
" %s:%d Error! \n " , __func__ , __LINE__ ) ;
2012-10-22 22:14:59 +02:00
goto err ;
}
2012-02-02 22:01:34 +05:30
agdev - > out_ep - > driver_data = agdev ;
agdev - > in_ep = usb_ep_autoconfig ( gadget , & fs_epin_desc ) ;
2012-10-22 22:14:59 +02:00
if ( ! agdev - > in_ep ) {
2012-02-02 22:01:34 +05:30
dev_err ( & uac2 - > pdev . dev ,
" %s:%d Error! \n " , __func__ , __LINE__ ) ;
2012-10-22 22:14:59 +02:00
goto err ;
}
2012-02-02 22:01:34 +05:30
agdev - > in_ep - > driver_data = agdev ;
2013-05-30 18:23:33 +05:30
uac2 - > p_prm . uac2 = uac2 ;
uac2 - > c_prm . uac2 = uac2 ;
2012-02-02 22:01:34 +05:30
hs_epout_desc . bEndpointAddress = fs_epout_desc . bEndpointAddress ;
hs_epout_desc . wMaxPacketSize = fs_epout_desc . wMaxPacketSize ;
hs_epin_desc . bEndpointAddress = fs_epin_desc . bEndpointAddress ;
hs_epin_desc . wMaxPacketSize = fs_epin_desc . wMaxPacketSize ;
2012-10-22 22:15:06 +02:00
ret = usb_assign_descriptors ( fn , fs_audio_desc , hs_audio_desc , NULL ) ;
if ( ret )
goto err ;
2012-02-02 22:01:34 +05:30
prm = & agdev - > uac2 . c_prm ;
prm - > max_psize = hs_epout_desc . wMaxPacketSize ;
prm - > rbuf = kzalloc ( prm - > max_psize * USB_XFERS , GFP_KERNEL ) ;
if ( ! prm - > rbuf ) {
prm - > max_psize = 0 ;
dev_err ( & uac2 - > pdev . dev ,
" %s:%d Error! \n " , __func__ , __LINE__ ) ;
2012-10-22 22:14:59 +02:00
goto err ;
2012-02-02 22:01:34 +05:30
}
prm = & agdev - > uac2 . p_prm ;
prm - > max_psize = hs_epin_desc . wMaxPacketSize ;
prm - > rbuf = kzalloc ( prm - > max_psize * USB_XFERS , GFP_KERNEL ) ;
if ( ! prm - > rbuf ) {
prm - > max_psize = 0 ;
dev_err ( & uac2 - > pdev . dev ,
" %s:%d Error! \n " , __func__ , __LINE__ ) ;
2012-10-22 22:14:59 +02:00
goto err ;
2012-02-02 22:01:34 +05:30
}
2012-10-22 22:14:59 +02:00
ret = alsa_uac2_init ( agdev ) ;
if ( ret )
goto err ;
return 0 ;
err :
kfree ( agdev - > uac2 . p_prm . rbuf ) ;
kfree ( agdev - > uac2 . c_prm . rbuf ) ;
2012-10-22 22:15:06 +02:00
usb_free_all_descriptors ( fn ) ;
2012-10-22 22:14:59 +02:00
if ( agdev - > in_ep )
agdev - > in_ep - > driver_data = NULL ;
if ( agdev - > out_ep )
agdev - > out_ep - > driver_data = NULL ;
return - EINVAL ;
2012-02-02 22:01:34 +05:30
}
static void
afunc_unbind ( struct usb_configuration * cfg , struct usb_function * fn )
{
struct audio_dev * agdev = func_to_agdev ( fn ) ;
struct uac2_rtd_params * prm ;
alsa_uac2_exit ( agdev ) ;
prm = & agdev - > uac2 . p_prm ;
kfree ( prm - > rbuf ) ;
prm = & agdev - > uac2 . c_prm ;
kfree ( prm - > rbuf ) ;
2012-10-22 22:15:06 +02:00
usb_free_all_descriptors ( fn ) ;
2012-02-02 22:01:34 +05:30
if ( agdev - > in_ep )
agdev - > in_ep - > driver_data = NULL ;
if ( agdev - > out_ep )
agdev - > out_ep - > driver_data = NULL ;
}
static int
afunc_set_alt ( struct usb_function * fn , unsigned intf , unsigned alt )
{
struct usb_composite_dev * cdev = fn - > config - > cdev ;
struct audio_dev * agdev = func_to_agdev ( fn ) ;
struct snd_uac2_chip * uac2 = & agdev - > uac2 ;
struct usb_gadget * gadget = cdev - > gadget ;
struct usb_request * req ;
struct usb_ep * ep ;
struct uac2_rtd_params * prm ;
int i ;
/* No i/f has more than 2 alt settings */
if ( alt > 1 ) {
dev_err ( & uac2 - > pdev . dev ,
" %s:%d Error! \n " , __func__ , __LINE__ ) ;
return - EINVAL ;
}
2012-10-22 22:15:08 +02:00
if ( intf = = agdev - > ac_intf ) {
2012-02-02 22:01:34 +05:30
/* Control I/f has only 1 AltSetting - 0 */
if ( alt ) {
dev_err ( & uac2 - > pdev . dev ,
" %s:%d Error! \n " , __func__ , __LINE__ ) ;
return - EINVAL ;
}
return 0 ;
}
2012-10-22 22:15:08 +02:00
if ( intf = = agdev - > as_out_intf ) {
2012-02-02 22:01:34 +05:30
ep = agdev - > out_ep ;
prm = & uac2 - > c_prm ;
config_ep_by_speed ( gadget , fn , ep ) ;
2012-10-22 22:15:08 +02:00
agdev - > as_out_alt = alt ;
} else if ( intf = = agdev - > as_in_intf ) {
2012-02-02 22:01:34 +05:30
ep = agdev - > in_ep ;
prm = & uac2 - > p_prm ;
config_ep_by_speed ( gadget , fn , ep ) ;
2012-10-22 22:15:08 +02:00
agdev - > as_in_alt = alt ;
2012-02-02 22:01:34 +05:30
} else {
dev_err ( & uac2 - > pdev . dev ,
" %s:%d Error! \n " , __func__ , __LINE__ ) ;
return - EINVAL ;
}
if ( alt = = 0 ) {
free_ep ( prm , ep ) ;
return 0 ;
}
prm - > ep_enabled = true ;
usb_ep_enable ( ep ) ;
for ( i = 0 ; i < USB_XFERS ; i + + ) {
if ( prm - > ureq [ i ] . req ) {
if ( usb_ep_queue ( ep , prm - > ureq [ i ] . req , GFP_ATOMIC ) )
dev_err ( & uac2 - > pdev . dev , " %d Error! \n " ,
__LINE__ ) ;
continue ;
}
req = usb_ep_alloc_request ( ep , GFP_ATOMIC ) ;
if ( req = = NULL ) {
dev_err ( & uac2 - > pdev . dev ,
" %s:%d Error! \n " , __func__ , __LINE__ ) ;
return - EINVAL ;
}
prm - > ureq [ i ] . req = req ;
prm - > ureq [ i ] . pp = prm ;
req - > zero = 0 ;
req - > context = & prm - > ureq [ i ] ;
req - > length = prm - > max_psize ;
req - > complete = agdev_iso_complete ;
req - > buf = prm - > rbuf + i * req - > length ;
if ( usb_ep_queue ( ep , req , GFP_ATOMIC ) )
dev_err ( & uac2 - > pdev . dev , " %d Error! \n " , __LINE__ ) ;
}
return 0 ;
}
static int
afunc_get_alt ( struct usb_function * fn , unsigned intf )
{
struct audio_dev * agdev = func_to_agdev ( fn ) ;
struct snd_uac2_chip * uac2 = & agdev - > uac2 ;
2012-10-22 22:15:08 +02:00
if ( intf = = agdev - > ac_intf )
return agdev - > ac_alt ;
else if ( intf = = agdev - > as_out_intf )
return agdev - > as_out_alt ;
else if ( intf = = agdev - > as_in_intf )
return agdev - > as_in_alt ;
2012-02-02 22:01:34 +05:30
else
dev_err ( & uac2 - > pdev . dev ,
" %s:%d Invalid Interface %d! \n " ,
__func__ , __LINE__ , intf ) ;
return - EINVAL ;
}
static void
afunc_disable ( struct usb_function * fn )
{
struct audio_dev * agdev = func_to_agdev ( fn ) ;
struct snd_uac2_chip * uac2 = & agdev - > uac2 ;
free_ep ( & uac2 - > p_prm , agdev - > in_ep ) ;
2012-10-22 22:15:08 +02:00
agdev - > as_in_alt = 0 ;
2012-02-02 22:01:34 +05:30
free_ep ( & uac2 - > c_prm , agdev - > out_ep ) ;
2012-10-22 22:15:08 +02:00
agdev - > as_out_alt = 0 ;
2012-02-02 22:01:34 +05:30
}
static int
in_rq_cur ( struct usb_function * fn , const struct usb_ctrlrequest * cr )
{
struct usb_request * req = fn - > config - > cdev - > req ;
struct audio_dev * agdev = func_to_agdev ( fn ) ;
struct snd_uac2_chip * uac2 = & agdev - > uac2 ;
u16 w_length = le16_to_cpu ( cr - > wLength ) ;
u16 w_index = le16_to_cpu ( cr - > wIndex ) ;
u16 w_value = le16_to_cpu ( cr - > wValue ) ;
u8 entity_id = ( w_index > > 8 ) & 0xff ;
u8 control_selector = w_value > > 8 ;
int value = - EOPNOTSUPP ;
if ( control_selector = = UAC2_CS_CONTROL_SAM_FREQ ) {
struct cntrl_cur_lay3 c ;
if ( entity_id = = USB_IN_CLK_ID )
c . dCUR = p_srate ;
else if ( entity_id = = USB_OUT_CLK_ID )
c . dCUR = c_srate ;
value = min_t ( unsigned , w_length , sizeof c ) ;
memcpy ( req - > buf , & c , value ) ;
} else if ( control_selector = = UAC2_CS_CONTROL_CLOCK_VALID ) {
* ( u8 * ) req - > buf = 1 ;
value = min_t ( unsigned , w_length , 1 ) ;
} else {
dev_err ( & uac2 - > pdev . dev ,
" %s:%d control_selector=%d TODO! \n " ,
__func__ , __LINE__ , control_selector ) ;
}
return value ;
}
static int
in_rq_range ( struct usb_function * fn , const struct usb_ctrlrequest * cr )
{
struct usb_request * req = fn - > config - > cdev - > req ;
struct audio_dev * agdev = func_to_agdev ( fn ) ;
struct snd_uac2_chip * uac2 = & agdev - > uac2 ;
u16 w_length = le16_to_cpu ( cr - > wLength ) ;
u16 w_index = le16_to_cpu ( cr - > wIndex ) ;
u16 w_value = le16_to_cpu ( cr - > wValue ) ;
u8 entity_id = ( w_index > > 8 ) & 0xff ;
u8 control_selector = w_value > > 8 ;
struct cntrl_range_lay3 r ;
int value = - EOPNOTSUPP ;
if ( control_selector = = UAC2_CS_CONTROL_SAM_FREQ ) {
if ( entity_id = = USB_IN_CLK_ID )
r . dMIN = p_srate ;
else if ( entity_id = = USB_OUT_CLK_ID )
r . dMIN = c_srate ;
else
return - EOPNOTSUPP ;
r . dMAX = r . dMIN ;
r . dRES = 0 ;
r . wNumSubRanges = 1 ;
value = min_t ( unsigned , w_length , sizeof r ) ;
memcpy ( req - > buf , & r , value ) ;
} else {
dev_err ( & uac2 - > pdev . dev ,
" %s:%d control_selector=%d TODO! \n " ,
__func__ , __LINE__ , control_selector ) ;
}
return value ;
}
static int
ac_rq_in ( struct usb_function * fn , const struct usb_ctrlrequest * cr )
{
if ( cr - > bRequest = = UAC2_CS_CUR )
return in_rq_cur ( fn , cr ) ;
else if ( cr - > bRequest = = UAC2_CS_RANGE )
return in_rq_range ( fn , cr ) ;
else
return - EOPNOTSUPP ;
}
static int
out_rq_cur ( struct usb_function * fn , const struct usb_ctrlrequest * cr )
{
u16 w_length = le16_to_cpu ( cr - > wLength ) ;
u16 w_value = le16_to_cpu ( cr - > wValue ) ;
u8 control_selector = w_value > > 8 ;
if ( control_selector = = UAC2_CS_CONTROL_SAM_FREQ )
return w_length ;
return - EOPNOTSUPP ;
}
static int
setup_rq_inf ( struct usb_function * fn , const struct usb_ctrlrequest * cr )
{
struct audio_dev * agdev = func_to_agdev ( fn ) ;
struct snd_uac2_chip * uac2 = & agdev - > uac2 ;
u16 w_index = le16_to_cpu ( cr - > wIndex ) ;
u8 intf = w_index & 0xff ;
2012-10-22 22:15:08 +02:00
if ( intf ! = agdev - > ac_intf ) {
2012-02-02 22:01:34 +05:30
dev_err ( & uac2 - > pdev . dev ,
" %s:%d Error! \n " , __func__ , __LINE__ ) ;
return - EOPNOTSUPP ;
}
if ( cr - > bRequestType & USB_DIR_IN )
return ac_rq_in ( fn , cr ) ;
else if ( cr - > bRequest = = UAC2_CS_CUR )
return out_rq_cur ( fn , cr ) ;
return - EOPNOTSUPP ;
}
static int
afunc_setup ( struct usb_function * fn , const struct usb_ctrlrequest * cr )
{
struct usb_composite_dev * cdev = fn - > config - > cdev ;
struct audio_dev * agdev = func_to_agdev ( fn ) ;
struct snd_uac2_chip * uac2 = & agdev - > uac2 ;
struct usb_request * req = cdev - > req ;
u16 w_length = le16_to_cpu ( cr - > wLength ) ;
int value = - EOPNOTSUPP ;
/* Only Class specific requests are supposed to reach here */
if ( ( cr - > bRequestType & USB_TYPE_MASK ) ! = USB_TYPE_CLASS )
return - EOPNOTSUPP ;
if ( ( cr - > bRequestType & USB_RECIP_MASK ) = = USB_RECIP_INTERFACE )
value = setup_rq_inf ( fn , cr ) ;
else
dev_err ( & uac2 - > pdev . dev , " %s:%d Error! \n " , __func__ , __LINE__ ) ;
if ( value > = 0 ) {
req - > length = value ;
req - > zero = value < w_length ;
value = usb_ep_queue ( cdev - > gadget - > ep0 , req , GFP_ATOMIC ) ;
if ( value < 0 ) {
dev_err ( & uac2 - > pdev . dev ,
" %s:%d Error! \n " , __func__ , __LINE__ ) ;
req - > status = 0 ;
}
}
return value ;
}
static int audio_bind_config ( struct usb_configuration * cfg )
{
2012-10-22 22:15:10 +02:00
int res ;
2012-02-02 22:01:34 +05:30
agdev_g = kzalloc ( sizeof * agdev_g , GFP_KERNEL ) ;
if ( agdev_g = = NULL ) {
printk ( KERN_ERR " Unable to allocate audio gadget \n " ) ;
return - ENOMEM ;
}
2012-10-22 22:15:10 +02:00
res = usb_string_ids_tab ( cfg - > cdev , strings_fn ) ;
if ( res )
return res ;
iad_desc . iFunction = strings_fn [ STR_ASSOC ] . id ;
std_ac_if_desc . iInterface = strings_fn [ STR_IF_CTRL ] . id ;
in_clk_src_desc . iClockSource = strings_fn [ STR_CLKSRC_IN ] . id ;
out_clk_src_desc . iClockSource = strings_fn [ STR_CLKSRC_OUT ] . id ;
usb_out_it_desc . iTerminal = strings_fn [ STR_USB_IT ] . id ;
io_in_it_desc . iTerminal = strings_fn [ STR_IO_IT ] . id ;
usb_in_ot_desc . iTerminal = strings_fn [ STR_USB_OT ] . id ;
io_out_ot_desc . iTerminal = strings_fn [ STR_IO_OT ] . id ;
std_as_out_if0_desc . iInterface = strings_fn [ STR_AS_OUT_ALT0 ] . id ;
std_as_out_if1_desc . iInterface = strings_fn [ STR_AS_OUT_ALT1 ] . id ;
std_as_in_if0_desc . iInterface = strings_fn [ STR_AS_IN_ALT0 ] . id ;
std_as_in_if1_desc . iInterface = strings_fn [ STR_AS_IN_ALT1 ] . id ;
2012-02-02 22:01:34 +05:30
agdev_g - > func . name = " uac2_func " ;
agdev_g - > func . strings = fn_strings ;
agdev_g - > func . bind = afunc_bind ;
agdev_g - > func . unbind = afunc_unbind ;
agdev_g - > func . set_alt = afunc_set_alt ;
agdev_g - > func . get_alt = afunc_get_alt ;
agdev_g - > func . disable = afunc_disable ;
agdev_g - > func . setup = afunc_setup ;
/* Initialize the configurable parameters */
usb_out_it_desc . bNrChannels = num_channels ( c_chmask ) ;
usb_out_it_desc . bmChannelConfig = cpu_to_le32 ( c_chmask ) ;
io_in_it_desc . bNrChannels = num_channels ( p_chmask ) ;
io_in_it_desc . bmChannelConfig = cpu_to_le32 ( p_chmask ) ;
as_out_hdr_desc . bNrChannels = num_channels ( c_chmask ) ;
as_out_hdr_desc . bmChannelConfig = cpu_to_le32 ( c_chmask ) ;
as_in_hdr_desc . bNrChannels = num_channels ( p_chmask ) ;
as_in_hdr_desc . bmChannelConfig = cpu_to_le32 ( p_chmask ) ;
as_out_fmt1_desc . bSubslotSize = c_ssize ;
as_out_fmt1_desc . bBitResolution = c_ssize * 8 ;
as_in_fmt1_desc . bSubslotSize = p_ssize ;
as_in_fmt1_desc . bBitResolution = p_ssize * 8 ;
snprintf ( clksrc_in , sizeof ( clksrc_in ) , " %uHz " , p_srate ) ;
snprintf ( clksrc_out , sizeof ( clksrc_out ) , " %uHz " , c_srate ) ;
res = usb_add_function ( cfg , & agdev_g - > func ) ;
if ( res < 0 )
kfree ( agdev_g ) ;
return res ;
}
static void
uac2_unbind_config ( struct usb_configuration * cfg )
{
kfree ( agdev_g ) ;
agdev_g = NULL ;
}