2019-05-30 02:57:59 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2014-11-28 18:59:27 +03:00
/*
* oxfw_stream . c - a part of driver for OXFW970 / 971 based devices
*
* Copyright ( c ) 2014 Takashi Sakamoto
*/
# include "oxfw.h"
2014-12-08 18:10:44 +03:00
# include <linux/delay.h>
2014-11-28 18:59:27 +03:00
2014-12-08 18:10:42 +03:00
# define AVC_GENERIC_FRAME_MAXIMUM_BYTES 512
2014-12-08 18:10:44 +03:00
# define CALLBACK_TIMEOUT 200
2014-12-08 18:10:42 +03:00
/*
* According to datasheet of Oxford Semiconductor :
* OXFW970 : 32.0 / 44.1 / 48.0 / 96.0 Khz , 8 audio channels I / O
* OXFW971 : 32.0 / 44.1 / 48.0 / 88.2 / 96.0 / 192.0 kHz , 16 audio channels I / O , MIDI I / O
*/
static const unsigned int oxfw_rate_table [ ] = {
[ 0 ] = 32000 ,
[ 1 ] = 44100 ,
[ 2 ] = 48000 ,
[ 3 ] = 88200 ,
[ 4 ] = 96000 ,
[ 5 ] = 192000 ,
} ;
/*
* See Table 5.7 – Sampling frequency for Multi - bit Audio
* in AV / C Stream Format Information Specification 1.1 ( Apr 2005 , 1394 TA )
*/
static const unsigned int avc_stream_rate_table [ ] = {
[ 0 ] = 0x02 ,
[ 1 ] = 0x03 ,
[ 2 ] = 0x04 ,
[ 3 ] = 0x0a ,
[ 4 ] = 0x05 ,
[ 5 ] = 0x07 ,
} ;
2014-12-08 18:10:46 +03:00
static int set_rate ( struct snd_oxfw * oxfw , unsigned int rate )
{
int err ;
err = avc_general_set_sig_fmt ( oxfw - > unit , rate ,
AVC_GENERAL_PLUG_DIR_IN , 0 ) ;
if ( err < 0 )
goto end ;
if ( oxfw - > has_output )
err = avc_general_set_sig_fmt ( oxfw - > unit , rate ,
AVC_GENERAL_PLUG_DIR_OUT , 0 ) ;
end :
return err ;
}
2014-12-08 18:10:44 +03:00
static int set_stream_format ( struct snd_oxfw * oxfw , struct amdtp_stream * s ,
unsigned int rate , unsigned int pcm_channels )
{
u8 * * formats ;
struct snd_oxfw_stream_formation formation ;
enum avc_general_plug_dir dir ;
2014-12-12 22:27:03 +03:00
unsigned int len ;
int i , err ;
2014-12-08 18:10:44 +03:00
2014-12-08 18:10:46 +03:00
if ( s = = & oxfw - > tx_stream ) {
formats = oxfw - > tx_stream_formats ;
dir = AVC_GENERAL_PLUG_DIR_OUT ;
} else {
formats = oxfw - > rx_stream_formats ;
dir = AVC_GENERAL_PLUG_DIR_IN ;
}
2014-12-08 18:10:44 +03:00
/* Seek stream format for requirements. */
for ( i = 0 ; i < SND_OXFW_STREAM_FORMAT_ENTRIES ; i + + ) {
err = snd_oxfw_stream_parse_format ( formats [ i ] , & formation ) ;
if ( err < 0 )
return err ;
if ( ( formation . rate = = rate ) & & ( formation . pcm = = pcm_channels ) )
break ;
}
if ( i = = SND_OXFW_STREAM_FORMAT_ENTRIES )
return - EINVAL ;
/* If assumed, just change rate. */
if ( oxfw - > assumed )
2014-12-08 18:10:46 +03:00
return set_rate ( oxfw , rate ) ;
2014-12-08 18:10:44 +03:00
/* Calculate format length. */
len = 5 + formats [ i ] [ 4 ] * 2 ;
err = avc_stream_set_format ( oxfw - > unit , dir , 0 , formats [ i ] , len ) ;
if ( err < 0 )
return err ;
/* Some requests just after changing format causes freezing. */
msleep ( 100 ) ;
return 0 ;
}
2019-06-12 11:44:15 +03:00
static int start_stream ( struct snd_oxfw * oxfw , struct amdtp_stream * stream )
2014-11-28 18:59:27 +03:00
{
2014-12-08 18:10:44 +03:00
struct cmp_connection * conn ;
int err ;
2014-11-28 18:59:27 +03:00
2019-06-12 11:44:22 +03:00
if ( stream = = & oxfw - > rx_stream )
2014-12-08 18:10:46 +03:00
conn = & oxfw - > in_conn ;
2019-06-12 11:44:22 +03:00
else
2014-12-08 18:10:46 +03:00
conn = & oxfw - > out_conn ;
2014-11-28 18:59:27 +03:00
2019-06-15 12:11:01 +03:00
err = cmp_connection_establish ( conn ) ;
2014-11-28 18:59:27 +03:00
if ( err < 0 )
2019-06-12 11:44:18 +03:00
return err ;
2014-11-28 18:59:27 +03:00
2019-06-12 11:44:18 +03:00
err = amdtp_stream_start ( stream , conn - > resources . channel , conn - > speed ) ;
2014-12-08 18:10:44 +03:00
if ( err < 0 ) {
cmp_connection_break ( conn ) ;
2019-06-12 11:44:18 +03:00
return err ;
2014-12-08 18:10:44 +03:00
}
2019-06-12 11:44:18 +03:00
// Wait first packet.
2015-02-27 03:39:32 +03:00
if ( ! amdtp_stream_wait_callback ( stream , CALLBACK_TIMEOUT ) ) {
2019-06-12 11:44:18 +03:00
amdtp_stream_stop ( stream ) ;
cmp_connection_break ( conn ) ;
return - ETIMEDOUT ;
2015-02-27 03:39:32 +03:00
}
2019-06-12 11:44:18 +03:00
return 0 ;
2014-12-08 18:10:44 +03:00
}
2014-12-08 18:10:46 +03:00
static int check_connection_used_by_others ( struct snd_oxfw * oxfw ,
struct amdtp_stream * stream )
2014-12-08 18:10:44 +03:00
{
2014-12-08 18:10:46 +03:00
struct cmp_connection * conn ;
bool used ;
int err ;
if ( stream = = & oxfw - > tx_stream )
conn = & oxfw - > out_conn ;
else
conn = & oxfw - > in_conn ;
err = cmp_connection_check_used ( conn , & used ) ;
if ( ( err > = 0 ) & & used & & ! amdtp_stream_running ( stream ) ) {
dev_err ( & oxfw - > unit - > device ,
" Connection established by others: %cPCR[%d] \n " ,
( conn - > direction = = CMP_OUTPUT ) ? ' o ' : ' i ' ,
conn - > pcr_index ) ;
err = - EBUSY ;
}
return err ;
}
2019-06-12 11:44:19 +03:00
static int init_stream ( struct snd_oxfw * oxfw , struct amdtp_stream * stream )
2014-12-08 18:10:46 +03:00
{
struct cmp_connection * conn ;
enum cmp_direction c_dir ;
enum amdtp_stream_direction s_dir ;
int err ;
if ( stream = = & oxfw - > tx_stream ) {
conn = & oxfw - > out_conn ;
c_dir = CMP_OUTPUT ;
s_dir = AMDTP_IN_STREAM ;
} else {
conn = & oxfw - > in_conn ;
c_dir = CMP_INPUT ;
s_dir = AMDTP_OUT_STREAM ;
}
err = cmp_connection_init ( conn , oxfw - > unit , c_dir , 0 ) ;
if ( err < 0 )
2019-06-12 11:44:19 +03:00
return err ;
2014-12-08 18:10:46 +03:00
2015-09-19 05:21:55 +03:00
err = amdtp_am824_init ( stream , oxfw - > unit , s_dir , CIP_NONBLOCKING ) ;
2014-12-08 18:10:46 +03:00
if ( err < 0 ) {
cmp_connection_destroy ( conn ) ;
2019-06-12 11:44:19 +03:00
return err ;
2014-12-08 18:10:46 +03:00
}
ALSA: firewire-lib: add buffer-over-run protection at receiving more data blocks than expected
In IEC 61883-6, the number of data blocks in a packet is limited up to
the value of SYT_INTERVAL. Current implementation is compliant to the
limitation, while it can cause buffer-over-run when the value of dbs
field in received packet is illegally large.
This commit adds a validator to detect such illegal packets to prevent
the buffer-over-run. Actually, the buffer is aligned to the size of memory
page, thus this issue hardly causes system errors due to the room to page
alignment, as long as a few packets includes such jumbo payload; i.e.
a packet to several received packets.
Here, Behringer F-Control Audio 202 (based on OXFW 960) has a quirk to
postpone transferring isochronous packet till finish handling any
asynchronous packets. In this case, this model is lazy, transfers no
packets according to several cycle-start packets. After finishing, this
model pushes required data in next isochronous packet. As a result, the
packet include more data blocks than IEC 61883-6 defines.
To continue to support this model, this commit adds a new flag to extend
the length of calculated payload. This flag allows the size of payload
5 times as large as IEC 61883-6 defines. As a result, packets from this
model passed the validator successfully.
Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-05-22 17:00:50 +03:00
/*
* OXFW starts to transmit packets with non - zero dbc .
* OXFW postpone transferring packets till handling any asynchronous
* packets . As a result , next isochronous packet includes more data
* blocks than IEC 61883 - 6 defines .
*/
2015-09-20 15:18:55 +03:00
if ( stream = = & oxfw - > tx_stream ) {
2016-05-09 17:15:56 +03:00
oxfw - > tx_stream . flags | = CIP_JUMBO_PAYLOAD ;
2015-09-20 15:18:55 +03:00
if ( oxfw - > wrong_dbs )
oxfw - > tx_stream . flags | = CIP_WRONG_DBS ;
}
2019-06-12 11:44:19 +03:00
return 0 ;
2014-12-08 18:10:46 +03:00
}
2019-06-12 11:44:22 +03:00
static int keep_resources ( struct snd_oxfw * oxfw , struct amdtp_stream * stream )
{
enum avc_general_plug_dir dir ;
u8 * * formats ;
struct snd_oxfw_stream_formation formation ;
2019-06-15 12:11:01 +03:00
struct cmp_connection * conn ;
2019-06-12 11:44:22 +03:00
int i ;
int err ;
if ( stream = = & oxfw - > rx_stream ) {
dir = AVC_GENERAL_PLUG_DIR_IN ;
formats = oxfw - > rx_stream_formats ;
2019-06-15 12:11:01 +03:00
conn = & oxfw - > in_conn ;
2019-06-12 11:44:22 +03:00
} else {
dir = AVC_GENERAL_PLUG_DIR_OUT ;
formats = oxfw - > tx_stream_formats ;
2019-06-15 12:11:01 +03:00
conn = & oxfw - > out_conn ;
2019-06-12 11:44:22 +03:00
}
err = snd_oxfw_stream_get_current_formation ( oxfw , dir , & formation ) ;
if ( err < 0 )
return err ;
for ( i = 0 ; i < SND_OXFW_STREAM_FORMAT_ENTRIES ; i + + ) {
struct snd_oxfw_stream_formation fmt ;
if ( formats [ i ] = = NULL )
break ;
err = snd_oxfw_stream_parse_format ( formats [ i ] , & fmt ) ;
if ( err < 0 )
return err ;
if ( fmt . rate = = formation . rate & & fmt . pcm = = formation . pcm & &
fmt . midi = = formation . midi )
break ;
}
if ( i = = SND_OXFW_STREAM_FORMAT_ENTRIES )
return - EINVAL ;
// The stream should have one pcm channels at least.
if ( formation . pcm = = 0 )
return - EINVAL ;
2019-06-15 12:11:01 +03:00
err = amdtp_am824_set_parameters ( stream , formation . rate , formation . pcm ,
2019-06-12 11:44:22 +03:00
formation . midi * 8 , false ) ;
2019-06-15 12:11:01 +03:00
if ( err < 0 )
return err ;
return cmp_connection_reserve ( conn , amdtp_stream_get_max_payload ( stream ) ) ;
2019-06-12 11:44:22 +03:00
}
2019-06-12 11:44:21 +03:00
int snd_oxfw_stream_reserve_duplex ( struct snd_oxfw * oxfw ,
struct amdtp_stream * stream ,
unsigned int rate , unsigned int pcm_channels )
2014-12-08 18:10:46 +03:00
{
2014-12-08 18:10:44 +03:00
struct snd_oxfw_stream_formation formation ;
2014-12-08 18:10:46 +03:00
enum avc_general_plug_dir dir ;
2019-06-12 11:44:21 +03:00
int err ;
2014-12-08 18:10:46 +03:00
2019-06-12 11:44:16 +03:00
// Considering JACK/FFADO streaming:
// TODO: This can be removed hwdep functionality becomes popular.
err = check_connection_used_by_others ( oxfw , & oxfw - > rx_stream ) ;
if ( err < 0 )
return err ;
if ( oxfw - > has_output ) {
err = check_connection_used_by_others ( oxfw , & oxfw - > tx_stream ) ;
if ( err < 0 )
return err ;
2014-12-08 18:10:46 +03:00
}
2019-06-12 11:44:16 +03:00
if ( stream = = & oxfw - > tx_stream )
dir = AVC_GENERAL_PLUG_DIR_OUT ;
else
dir = AVC_GENERAL_PLUG_DIR_IN ;
2014-12-08 18:10:46 +03:00
err = snd_oxfw_stream_get_current_formation ( oxfw , dir , & formation ) ;
2014-11-28 18:59:27 +03:00
if ( err < 0 )
2019-06-12 11:44:16 +03:00
return err ;
2019-06-12 11:44:21 +03:00
if ( rate = = 0 ) {
2014-12-08 18:10:48 +03:00
rate = formation . rate ;
pcm_channels = formation . pcm ;
2019-06-12 11:44:21 +03:00
}
if ( formation . rate ! = rate | | formation . pcm ! = pcm_channels ) {
2019-06-12 11:44:18 +03:00
amdtp_stream_stop ( & oxfw - > rx_stream ) ;
cmp_connection_break ( & oxfw - > in_conn ) ;
2019-06-18 16:26:22 +03:00
cmp_connection_release ( & oxfw - > in_conn ) ;
2019-06-12 11:44:18 +03:00
if ( oxfw - > has_output ) {
amdtp_stream_stop ( & oxfw - > tx_stream ) ;
cmp_connection_break ( & oxfw - > out_conn ) ;
2019-06-18 16:26:22 +03:00
cmp_connection_release ( & oxfw - > out_conn ) ;
2019-06-12 11:44:18 +03:00
}
2019-06-12 11:44:21 +03:00
}
2014-12-08 18:10:44 +03:00
2019-06-12 11:44:21 +03:00
if ( oxfw - > substreams_count = = 0 | |
formation . rate ! = rate | | formation . pcm ! = pcm_channels ) {
2014-12-08 18:10:46 +03:00
err = set_stream_format ( oxfw , stream , rate , pcm_channels ) ;
2014-12-08 18:10:44 +03:00
if ( err < 0 ) {
dev_err ( & oxfw - > unit - > device ,
" fail to set stream format: %d \n " , err ) ;
2019-06-12 11:44:16 +03:00
return err ;
2014-12-08 18:10:44 +03:00
}
2019-06-12 11:44:22 +03:00
err = keep_resources ( oxfw , & oxfw - > rx_stream ) ;
if ( err < 0 )
return err ;
if ( oxfw - > has_output ) {
err = keep_resources ( oxfw , & oxfw - > tx_stream ) ;
2019-06-15 12:11:01 +03:00
if ( err < 0 ) {
cmp_connection_release ( & oxfw - > in_conn ) ;
2019-06-12 11:44:22 +03:00
return err ;
2019-06-15 12:11:01 +03:00
}
2019-06-12 11:44:22 +03:00
}
2019-06-12 11:44:16 +03:00
}
2014-12-08 18:10:46 +03:00
2019-06-12 11:44:21 +03:00
return 0 ;
}
int snd_oxfw_stream_start_duplex ( struct snd_oxfw * oxfw )
{
int err ;
if ( oxfw - > substreams_count = = 0 )
return - EIO ;
if ( amdtp_streaming_error ( & oxfw - > rx_stream ) | |
amdtp_streaming_error ( & oxfw - > tx_stream ) ) {
amdtp_stream_stop ( & oxfw - > rx_stream ) ;
cmp_connection_break ( & oxfw - > in_conn ) ;
if ( oxfw - > has_output ) {
amdtp_stream_stop ( & oxfw - > tx_stream ) ;
cmp_connection_break ( & oxfw - > out_conn ) ;
}
}
2019-06-12 11:44:16 +03:00
if ( ! amdtp_stream_running ( & oxfw - > rx_stream ) ) {
err = start_stream ( oxfw , & oxfw - > rx_stream ) ;
if ( err < 0 ) {
dev_err ( & oxfw - > unit - > device ,
" fail to start rx stream: %d \n " , err ) ;
goto error ;
}
}
if ( oxfw - > has_output ) {
if ( ! amdtp_stream_running ( & oxfw - > tx_stream ) ) {
err = start_stream ( oxfw , & oxfw - > tx_stream ) ;
2014-12-08 18:10:46 +03:00
if ( err < 0 ) {
dev_err ( & oxfw - > unit - > device ,
2019-06-12 11:44:16 +03:00
" fail to start tx stream: %d \n " , err ) ;
goto error ;
2014-12-08 18:10:46 +03:00
}
}
2014-12-08 18:10:44 +03:00
}
2019-06-12 11:44:16 +03:00
return 0 ;
error :
2019-06-12 11:44:18 +03:00
amdtp_stream_stop ( & oxfw - > rx_stream ) ;
2019-06-12 11:44:16 +03:00
cmp_connection_break ( & oxfw - > in_conn ) ;
if ( oxfw - > has_output ) {
2019-06-12 11:44:18 +03:00
amdtp_stream_stop ( & oxfw - > tx_stream ) ;
2019-06-12 11:44:16 +03:00
cmp_connection_break ( & oxfw - > out_conn ) ;
2014-12-08 18:10:46 +03:00
}
2014-11-28 18:59:27 +03:00
return err ;
}
2019-06-12 11:44:19 +03:00
void snd_oxfw_stream_stop_duplex ( struct snd_oxfw * oxfw )
2014-11-28 18:59:27 +03:00
{
2019-06-12 11:44:20 +03:00
if ( oxfw - > substreams_count = = 0 ) {
2019-06-12 11:44:18 +03:00
amdtp_stream_stop ( & oxfw - > rx_stream ) ;
cmp_connection_break ( & oxfw - > in_conn ) ;
2019-06-15 12:11:01 +03:00
cmp_connection_release ( & oxfw - > in_conn ) ;
2014-12-08 18:10:46 +03:00
2019-06-12 11:44:18 +03:00
if ( oxfw - > has_output ) {
amdtp_stream_stop ( & oxfw - > tx_stream ) ;
cmp_connection_break ( & oxfw - > out_conn ) ;
2019-06-15 12:11:01 +03:00
cmp_connection_release ( & oxfw - > out_conn ) ;
2019-06-12 11:44:18 +03:00
}
2019-06-12 11:44:16 +03:00
}
2014-11-28 18:59:27 +03:00
}
2019-06-12 11:44:19 +03:00
static void destroy_stream ( struct snd_oxfw * oxfw , struct amdtp_stream * stream )
2014-11-28 18:59:27 +03:00
{
2014-12-08 18:10:46 +03:00
struct cmp_connection * conn ;
if ( stream = = & oxfw - > tx_stream )
conn = & oxfw - > out_conn ;
else
conn = & oxfw - > in_conn ;
2014-11-28 18:59:27 +03:00
2014-12-08 18:10:46 +03:00
amdtp_stream_destroy ( stream ) ;
cmp_connection_destroy ( conn ) ;
2014-11-28 18:59:27 +03:00
}
2019-06-12 11:44:19 +03:00
int snd_oxfw_stream_init_duplex ( struct snd_oxfw * oxfw )
{
int err ;
err = init_stream ( oxfw , & oxfw - > rx_stream ) ;
if ( err < 0 )
return err ;
if ( oxfw - > has_output ) {
err = init_stream ( oxfw , & oxfw - > tx_stream ) ;
if ( err < 0 ) {
destroy_stream ( oxfw , & oxfw - > rx_stream ) ;
return err ;
}
}
return 0 ;
}
// This function should be called before starting the stream or after stopping
// the streams.
void snd_oxfw_stream_destroy_duplex ( struct snd_oxfw * oxfw )
{
destroy_stream ( oxfw , & oxfw - > rx_stream ) ;
if ( oxfw - > has_output )
destroy_stream ( oxfw , & oxfw - > tx_stream ) ;
}
void snd_oxfw_stream_update_duplex ( struct snd_oxfw * oxfw )
2014-11-28 18:59:27 +03:00
{
2019-06-12 11:44:18 +03:00
amdtp_stream_stop ( & oxfw - > rx_stream ) ;
cmp_connection_break ( & oxfw - > in_conn ) ;
2014-12-08 18:10:46 +03:00
2019-06-12 11:44:18 +03:00
amdtp_stream_pcm_abort ( & oxfw - > rx_stream ) ;
if ( oxfw - > has_output ) {
amdtp_stream_stop ( & oxfw - > tx_stream ) ;
cmp_connection_break ( & oxfw - > out_conn ) ;
amdtp_stream_pcm_abort ( & oxfw - > tx_stream ) ;
}
2014-11-28 18:59:27 +03:00
}
2014-12-08 18:10:42 +03:00
2014-12-08 18:10:43 +03:00
int snd_oxfw_stream_get_current_formation ( struct snd_oxfw * oxfw ,
enum avc_general_plug_dir dir ,
struct snd_oxfw_stream_formation * formation )
{
u8 * format ;
unsigned int len ;
int err ;
len = AVC_GENERIC_FRAME_MAXIMUM_BYTES ;
format = kmalloc ( len , GFP_KERNEL ) ;
if ( format = = NULL )
return - ENOMEM ;
err = avc_stream_get_format_single ( oxfw - > unit , dir , 0 , format , & len ) ;
if ( err < 0 )
goto end ;
if ( len < 3 ) {
err = - EIO ;
goto end ;
}
err = snd_oxfw_stream_parse_format ( format , formation ) ;
end :
kfree ( format ) ;
return err ;
}
2014-12-08 18:10:42 +03:00
/*
* See Table 6.16 - AM824 Stream Format
* Figure 6.19 - format_information field for AM824 Compound
* in AV / C Stream Format Information Specification 1.1 ( Apr 2005 , 1394 TA )
* Also ' Clause 12 AM824 sequence adaption layers ' in IEC 61883 - 6 : 2005
*/
int snd_oxfw_stream_parse_format ( u8 * format ,
struct snd_oxfw_stream_formation * formation )
{
unsigned int i , e , channels , type ;
memset ( formation , 0 , sizeof ( struct snd_oxfw_stream_formation ) ) ;
/*
* this module can support a hierarchy combination that :
* Root : Audio and Music ( 0x90 )
* Level 1 : AM824 Compound ( 0x40 )
*/
if ( ( format [ 0 ] ! = 0x90 ) | | ( format [ 1 ] ! = 0x40 ) )
return - ENOSYS ;
/* check the sampling rate */
for ( i = 0 ; i < ARRAY_SIZE ( avc_stream_rate_table ) ; i + + ) {
if ( format [ 2 ] = = avc_stream_rate_table [ i ] )
break ;
}
if ( i = = ARRAY_SIZE ( avc_stream_rate_table ) )
return - ENOSYS ;
formation - > rate = oxfw_rate_table [ i ] ;
for ( e = 0 ; e < format [ 4 ] ; e + + ) {
channels = format [ 5 + e * 2 ] ;
type = format [ 6 + e * 2 ] ;
switch ( type ) {
/* IEC 60958 Conformant, currently handled as MBLA */
case 0x00 :
/* Multi Bit Linear Audio (Raw) */
case 0x06 :
formation - > pcm + = channels ;
break ;
/* MIDI Conformant */
case 0x0d :
formation - > midi = channels ;
break ;
/* IEC 61937-3 to 7 */
case 0x01 :
case 0x02 :
case 0x03 :
case 0x04 :
case 0x05 :
/* Multi Bit Linear Audio */
case 0x07 : /* DVD-Audio */
case 0x0c : /* High Precision */
/* One Bit Audio */
case 0x08 : /* (Plain) Raw */
case 0x09 : /* (Plain) SACD */
case 0x0a : /* (Encoded) Raw */
case 0x0b : /* (Encoded) SACD */
/* SMPTE Time-Code conformant */
case 0x0e :
/* Sample Count */
case 0x0f :
/* Anciliary Data */
case 0x10 :
/* Synchronization Stream (Stereo Raw audio) */
case 0x40 :
/* Don't care */
case 0xff :
default :
return - ENOSYS ; /* not supported */
}
}
2015-09-19 05:22:01 +03:00
if ( formation - > pcm > AM824_MAX_CHANNELS_FOR_PCM | |
formation - > midi > AM824_MAX_CHANNELS_FOR_MIDI )
2014-12-08 18:10:42 +03:00
return - ENOSYS ;
return 0 ;
}
static int
assume_stream_formats ( struct snd_oxfw * oxfw , enum avc_general_plug_dir dir ,
unsigned int pid , u8 * buf , unsigned int * len ,
u8 * * formats )
{
struct snd_oxfw_stream_formation formation ;
unsigned int i , eid ;
int err ;
/* get format at current sampling rate */
err = avc_stream_get_format_single ( oxfw - > unit , dir , pid , buf , len ) ;
if ( err < 0 ) {
dev_err ( & oxfw - > unit - > device ,
" fail to get current stream format for isoc %s plug %d:%d \n " ,
( dir = = AVC_GENERAL_PLUG_DIR_IN ) ? " in " : " out " ,
pid , err ) ;
goto end ;
}
/* parse and set stream format */
eid = 0 ;
err = snd_oxfw_stream_parse_format ( buf , & formation ) ;
if ( err < 0 )
goto end ;
2018-10-03 02:21:54 +03:00
formats [ eid ] = devm_kmemdup ( & oxfw - > card - > card_dev , buf , * len ,
GFP_KERNEL ) ;
if ( ! formats [ eid ] ) {
2014-12-08 18:10:42 +03:00
err = - ENOMEM ;
goto end ;
}
/* apply the format for each available sampling rate */
for ( i = 0 ; i < ARRAY_SIZE ( oxfw_rate_table ) ; i + + ) {
if ( formation . rate = = oxfw_rate_table [ i ] )
continue ;
err = avc_general_inquiry_sig_fmt ( oxfw - > unit ,
oxfw_rate_table [ i ] ,
dir , pid ) ;
if ( err < 0 )
continue ;
eid + + ;
2018-10-03 02:21:54 +03:00
formats [ eid ] = devm_kmemdup ( & oxfw - > card - > card_dev , buf , * len ,
GFP_KERNEL ) ;
2014-12-08 18:10:42 +03:00
if ( formats [ eid ] = = NULL ) {
err = - ENOMEM ;
goto end ;
}
formats [ eid ] [ 2 ] = avc_stream_rate_table [ i ] ;
}
err = 0 ;
oxfw - > assumed = true ;
end :
return err ;
}
static int fill_stream_formats ( struct snd_oxfw * oxfw ,
enum avc_general_plug_dir dir ,
unsigned short pid )
{
u8 * buf , * * formats ;
unsigned int len , eid = 0 ;
struct snd_oxfw_stream_formation dummy ;
int err ;
buf = kmalloc ( AVC_GENERIC_FRAME_MAXIMUM_BYTES , GFP_KERNEL ) ;
if ( buf = = NULL )
return - ENOMEM ;
2014-12-08 18:10:46 +03:00
if ( dir = = AVC_GENERAL_PLUG_DIR_OUT )
formats = oxfw - > tx_stream_formats ;
else
formats = oxfw - > rx_stream_formats ;
2014-12-08 18:10:42 +03:00
/* get first entry */
len = AVC_GENERIC_FRAME_MAXIMUM_BYTES ;
err = avc_stream_get_format_list ( oxfw - > unit , dir , 0 , buf , & len , 0 ) ;
if ( err = = - ENOSYS ) {
/* LIST subfunction is not implemented */
len = AVC_GENERIC_FRAME_MAXIMUM_BYTES ;
err = assume_stream_formats ( oxfw , dir , pid , buf , & len ,
formats ) ;
goto end ;
} else if ( err < 0 ) {
dev_err ( & oxfw - > unit - > device ,
" fail to get stream format %d for isoc %s plug %d:%d \n " ,
eid , ( dir = = AVC_GENERAL_PLUG_DIR_IN ) ? " in " : " out " ,
pid , err ) ;
goto end ;
}
/* LIST subfunction is implemented */
while ( eid < SND_OXFW_STREAM_FORMAT_ENTRIES ) {
/* The format is too short. */
if ( len < 3 ) {
err = - EIO ;
break ;
}
/* parse and set stream format */
err = snd_oxfw_stream_parse_format ( buf , & dummy ) ;
if ( err < 0 )
break ;
2018-10-03 02:21:54 +03:00
formats [ eid ] = devm_kmemdup ( & oxfw - > card - > card_dev , buf , len ,
GFP_KERNEL ) ;
if ( ! formats [ eid ] ) {
2014-12-08 18:10:42 +03:00
err = - ENOMEM ;
break ;
}
/* get next entry */
len = AVC_GENERIC_FRAME_MAXIMUM_BYTES ;
err = avc_stream_get_format_list ( oxfw - > unit , dir , 0 ,
buf , & len , + + eid ) ;
/* No entries remained. */
if ( err = = - EINVAL ) {
err = 0 ;
break ;
} else if ( err < 0 ) {
dev_err ( & oxfw - > unit - > device ,
" fail to get stream format %d for isoc %s plug %d:%d \n " ,
eid , ( dir = = AVC_GENERAL_PLUG_DIR_IN ) ? " in " :
" out " ,
pid , err ) ;
break ;
}
}
end :
kfree ( buf ) ;
return err ;
}
int snd_oxfw_stream_discover ( struct snd_oxfw * oxfw )
{
u8 plugs [ AVC_PLUG_INFO_BUF_BYTES ] ;
2015-10-18 11:09:38 +03:00
struct snd_oxfw_stream_formation formation ;
u8 * format ;
unsigned int i ;
2014-12-08 18:10:42 +03:00
int err ;
/* the number of plugs for isoc in/out, ext in/out */
err = avc_general_get_plug_info ( oxfw - > unit , 0x1f , 0x07 , 0x00 , plugs ) ;
if ( err < 0 ) {
dev_err ( & oxfw - > unit - > device ,
" fail to get info for isoc/external in/out plugs: %d \n " ,
err ) ;
goto end ;
2014-12-08 18:10:46 +03:00
} else if ( ( plugs [ 0 ] = = 0 ) & & ( plugs [ 1 ] = = 0 ) ) {
2014-12-08 18:10:42 +03:00
err = - ENOSYS ;
goto end ;
}
2014-12-08 18:10:46 +03:00
/* use oPCR[0] if exists */
if ( plugs [ 1 ] > 0 ) {
err = fill_stream_formats ( oxfw , AVC_GENERAL_PLUG_DIR_OUT , 0 ) ;
if ( err < 0 )
goto end ;
2015-10-18 11:09:38 +03:00
for ( i = 0 ; i < SND_OXFW_STREAM_FORMAT_ENTRIES ; i + + ) {
format = oxfw - > tx_stream_formats [ i ] ;
if ( format = = NULL )
continue ;
err = snd_oxfw_stream_parse_format ( format , & formation ) ;
if ( err < 0 )
continue ;
/* Add one MIDI port. */
if ( formation . midi > 0 )
oxfw - > midi_input_ports = 1 ;
}
2014-12-08 18:10:46 +03:00
oxfw - > has_output = true ;
}
2014-12-08 18:10:42 +03:00
/* use iPCR[0] if exists */
2015-10-18 11:09:38 +03:00
if ( plugs [ 0 ] > 0 ) {
2014-12-08 18:10:42 +03:00
err = fill_stream_formats ( oxfw , AVC_GENERAL_PLUG_DIR_IN , 0 ) ;
2015-10-18 11:09:38 +03:00
if ( err < 0 )
goto end ;
for ( i = 0 ; i < SND_OXFW_STREAM_FORMAT_ENTRIES ; i + + ) {
format = oxfw - > rx_stream_formats [ i ] ;
if ( format = = NULL )
continue ;
err = snd_oxfw_stream_parse_format ( format , & formation ) ;
if ( err < 0 )
continue ;
/* Add one MIDI port. */
if ( formation . midi > 0 )
oxfw - > midi_output_ports = 1 ;
}
}
2014-12-08 18:10:42 +03:00
end :
return err ;
}
2014-12-08 18:10:49 +03:00
void snd_oxfw_stream_lock_changed ( struct snd_oxfw * oxfw )
{
oxfw - > dev_lock_changed = true ;
wake_up ( & oxfw - > hwdep_wait ) ;
}
int snd_oxfw_stream_lock_try ( struct snd_oxfw * oxfw )
{
int err ;
spin_lock_irq ( & oxfw - > lock ) ;
/* user land lock this */
if ( oxfw - > dev_lock_count < 0 ) {
err = - EBUSY ;
goto end ;
}
/* this is the first time */
if ( oxfw - > dev_lock_count + + = = 0 )
snd_oxfw_stream_lock_changed ( oxfw ) ;
err = 0 ;
end :
spin_unlock_irq ( & oxfw - > lock ) ;
return err ;
}
void snd_oxfw_stream_lock_release ( struct snd_oxfw * oxfw )
{
spin_lock_irq ( & oxfw - > lock ) ;
if ( WARN_ON ( oxfw - > dev_lock_count < = 0 ) )
goto end ;
if ( - - oxfw - > dev_lock_count = = 0 )
snd_oxfw_stream_lock_changed ( oxfw ) ;
end :
spin_unlock_irq ( & oxfw - > lock ) ;
}