2011-03-15 09:53:21 +03:00
/*
* miscellaneous helper functions
*
* Copyright ( c ) Clemens Ladisch < clemens @ ladisch . de >
* Licensed under the terms of the GNU General Public License , version 2.
*/
# include <linux/delay.h>
# include <linux/device.h>
# include <linux/firewire.h>
# include <linux/module.h>
2015-10-09 02:10:25 +03:00
# include <linux/slab.h>
2011-03-15 09:53:21 +03:00
# include "lib.h"
2011-09-05 00:17:38 +04:00
# define ERROR_RETRY_DELAY_MS 20
2011-03-15 09:53:21 +03:00
/**
* snd_fw_transaction - send a request and wait for its completion
* @ unit : the driver ' s unit on the target device
* @ tcode : the transaction code
* @ offset : the address in the target ' s address space
* @ buffer : input / output data
* @ length : length of @ buffer
2011-09-05 00:17:38 +04:00
* @ flags : use % FW_FIXED_GENERATION and add the generation value to attempt the
* request only in that generation ; use % FW_QUIET to suppress error
* messages
2011-03-15 09:53:21 +03:00
*
* Submits an asynchronous request to the target device , and waits for the
* response . The node ID and the current generation are derived from @ unit .
* On a bus reset or an error , the transaction is retried a few times .
* Returns zero on success , or a negative error code .
*/
int snd_fw_transaction ( struct fw_unit * unit , int tcode ,
2011-09-05 00:17:38 +04:00
u64 offset , void * buffer , size_t length ,
unsigned int flags )
2011-03-15 09:53:21 +03:00
{
struct fw_device * device = fw_parent_device ( unit ) ;
int generation , rcode , tries = 0 ;
2011-09-05 00:17:38 +04:00
generation = flags & FW_GENERATION_MASK ;
2011-03-15 09:53:21 +03:00
for ( ; ; ) {
2011-09-05 00:17:38 +04:00
if ( ! ( flags & FW_FIXED_GENERATION ) ) {
generation = device - > generation ;
smp_rmb ( ) ; /* node_id vs. generation */
}
2011-03-15 09:53:21 +03:00
rcode = fw_run_transaction ( device - > card , tcode ,
device - > node_id , generation ,
device - > max_speed , offset ,
buffer , length ) ;
if ( rcode = = RCODE_COMPLETE )
return 0 ;
2011-09-05 00:17:38 +04:00
if ( rcode = = RCODE_GENERATION & & ( flags & FW_FIXED_GENERATION ) )
return - EAGAIN ;
2011-03-15 09:53:21 +03:00
if ( rcode_is_permanent_error ( rcode ) | | + + tries > = 3 ) {
2011-09-05 00:17:38 +04:00
if ( ! ( flags & FW_QUIET ) )
dev_err ( & unit - > device ,
" transaction failed: %s \n " ,
fw_rcode_string ( rcode ) ) ;
2011-03-15 09:53:21 +03:00
return - EIO ;
}
msleep ( ERROR_RETRY_DELAY_MS ) ;
}
}
EXPORT_SYMBOL ( snd_fw_transaction ) ;
2015-10-09 02:10:25 +03:00
static void async_midi_port_callback ( struct fw_card * card , int rcode ,
void * data , size_t length ,
void * callback_data )
{
struct snd_fw_async_midi_port * port = callback_data ;
struct snd_rawmidi_substream * substream = ACCESS_ONCE ( port - > substream ) ;
2015-10-18 16:58:37 +03:00
/* This port is closed. */
if ( substream = = NULL )
return ;
if ( rcode = = RCODE_COMPLETE )
2015-10-09 02:10:25 +03:00
snd_rawmidi_transmit_ack ( substream , port - > consume_bytes ) ;
2015-10-09 02:10:28 +03:00
else if ( ! rcode_is_permanent_error ( rcode ) )
/* To start next transaction immediately for recovery. */
port - > next_ktime = ktime_set ( 0 , 0 ) ;
2015-10-09 02:10:29 +03:00
else
/* Don't continue processing. */
port - > error = true ;
2015-10-09 02:10:26 +03:00
port - > idling = true ;
2015-10-09 02:10:27 +03:00
if ( ! snd_rawmidi_transmit_empty ( substream ) )
schedule_work ( & port - > work ) ;
2015-10-09 02:10:25 +03:00
}
static void midi_port_work ( struct work_struct * work )
{
struct snd_fw_async_midi_port * port =
container_of ( work , struct snd_fw_async_midi_port , work ) ;
struct snd_rawmidi_substream * substream = ACCESS_ONCE ( port - > substream ) ;
int generation ;
int type ;
2015-10-09 02:10:29 +03:00
/* Under transacting or error state. */
if ( ! port - > idling | | port - > error )
2015-10-09 02:10:26 +03:00
return ;
2015-10-09 02:10:25 +03:00
/* Nothing to do. */
if ( substream = = NULL | | snd_rawmidi_transmit_empty ( substream ) )
return ;
2015-10-09 02:10:28 +03:00
/* Do it in next chance. */
if ( ktime_after ( port - > next_ktime , ktime_get ( ) ) ) {
schedule_work ( & port - > work ) ;
return ;
}
2015-10-09 02:10:25 +03:00
/*
* Fill the buffer . The callee must use snd_rawmidi_transmit_peek ( ) .
* Later , snd_rawmidi_transmit_ack ( ) is called .
*/
memset ( port - > buf , 0 , port - > len ) ;
port - > consume_bytes = port - > fill ( substream , port - > buf ) ;
if ( port - > consume_bytes < = 0 ) {
/* Do it in next chance, immediately. */
2015-10-09 02:10:28 +03:00
if ( port - > consume_bytes = = 0 ) {
port - > next_ktime = ktime_set ( 0 , 0 ) ;
2015-10-09 02:10:25 +03:00
schedule_work ( & port - > work ) ;
2015-10-09 02:10:29 +03:00
} else {
/* Fatal error. */
port - > error = true ;
2015-10-09 02:10:28 +03:00
}
2015-10-09 02:10:25 +03:00
return ;
}
/* Calculate type of transaction. */
if ( port - > len = = 4 )
type = TCODE_WRITE_QUADLET_REQUEST ;
else
type = TCODE_WRITE_BLOCK_REQUEST ;
2015-10-09 02:10:28 +03:00
/* Set interval to next transaction. */
port - > next_ktime = ktime_add_ns ( ktime_get ( ) ,
port - > consume_bytes * 8 * NSEC_PER_SEC / 31250 ) ;
2015-10-09 02:10:25 +03:00
/* Start this transaction. */
2015-10-09 02:10:26 +03:00
port - > idling = false ;
2015-10-09 02:10:25 +03:00
/*
* In Linux FireWire core , when generation is updated with memory
* barrier , node id has already been updated . In this module , After
* this smp_rmb ( ) , load / store instructions to memory are completed .
* Thus , both of generation and node id are available with recent
* values . This is a light - serialization solution to handle bus reset
* events on IEEE 1394 bus .
*/
generation = port - > parent - > generation ;
smp_rmb ( ) ;
fw_send_request ( port - > parent - > card , & port - > transaction , type ,
port - > parent - > node_id , generation ,
port - > parent - > max_speed , port - > addr ,
port - > buf , port - > len , async_midi_port_callback ,
port ) ;
}
/**
* snd_fw_async_midi_port_init - initialize asynchronous MIDI port structure
* @ port : the asynchronous MIDI port to initialize
* @ unit : the target of the asynchronous transaction
* @ addr : the address to which transactions are transferred
* @ len : the length of transaction
* @ fill : the callback function to fill given buffer , and returns the
* number of consumed bytes for MIDI message .
*
*/
int snd_fw_async_midi_port_init ( struct snd_fw_async_midi_port * port ,
struct fw_unit * unit , u64 addr , unsigned int len ,
snd_fw_async_midi_port_fill fill )
{
port - > len = DIV_ROUND_UP ( len , 4 ) * 4 ;
port - > buf = kzalloc ( port - > len , GFP_KERNEL ) ;
if ( port - > buf = = NULL )
return - ENOMEM ;
port - > parent = fw_parent_device ( unit ) ;
port - > addr = addr ;
port - > fill = fill ;
2015-10-09 02:10:26 +03:00
port - > idling = true ;
2015-10-09 02:10:28 +03:00
port - > next_ktime = ktime_set ( 0 , 0 ) ;
2015-10-09 02:10:29 +03:00
port - > error = false ;
2015-10-09 02:10:25 +03:00
INIT_WORK ( & port - > work , midi_port_work ) ;
return 0 ;
}
EXPORT_SYMBOL ( snd_fw_async_midi_port_init ) ;
/**
* snd_fw_async_midi_port_destroy - free asynchronous MIDI port structure
* @ port : the asynchronous MIDI port structure
*/
void snd_fw_async_midi_port_destroy ( struct snd_fw_async_midi_port * port )
{
snd_fw_async_midi_port_finish ( port ) ;
cancel_work_sync ( & port - > work ) ;
kfree ( port - > buf ) ;
}
EXPORT_SYMBOL ( snd_fw_async_midi_port_destroy ) ;
2011-03-15 09:53:21 +03:00
MODULE_DESCRIPTION ( " FireWire audio helper functions " ) ;
MODULE_AUTHOR ( " Clemens Ladisch <clemens@ladisch.de> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;