2019-05-30 02:57:59 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2015-10-01 16:02:11 +03:00
/*
* tascam . c - a part of driver for TASCAM FireWire series
*
* Copyright ( c ) 2015 Takashi Sakamoto
*/
# include "tascam.h"
MODULE_DESCRIPTION ( " TASCAM FireWire series Driver " ) ;
MODULE_AUTHOR ( " Takashi Sakamoto <o-takashi@sakamocchi.jp> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
2017-08-22 16:58:15 +03:00
static const struct snd_tscm_spec model_specs [ ] = {
2015-10-01 16:02:12 +03:00
{
. name = " FW-1884 " ,
. has_adat = true ,
. has_spdif = true ,
. pcm_capture_analog_channels = 8 ,
. pcm_playback_analog_channels = 8 ,
. midi_capture_ports = 4 ,
. midi_playback_ports = 4 ,
} ,
{
. name = " FW-1082 " ,
. has_adat = false ,
. has_spdif = true ,
. pcm_capture_analog_channels = 8 ,
. pcm_playback_analog_channels = 2 ,
. midi_capture_ports = 2 ,
. midi_playback_ports = 2 ,
} ,
2016-02-05 03:56:06 +03:00
{
. name = " FW-1804 " ,
. has_adat = true ,
. has_spdif = true ,
. pcm_capture_analog_channels = 8 ,
. pcm_playback_analog_channels = 2 ,
. midi_capture_ports = 2 ,
. midi_playback_ports = 4 ,
} ,
2015-10-01 16:02:12 +03:00
} ;
2015-10-12 13:10:25 +03:00
static int identify_model ( struct snd_tscm * tscm )
2015-10-01 16:02:11 +03:00
{
struct fw_device * fw_dev = fw_parent_device ( tscm - > unit ) ;
2015-10-12 13:10:25 +03:00
const u32 * config_rom = fw_dev - > config_rom ;
2015-10-19 14:29:27 +03:00
char model [ 9 ] ;
2015-10-12 13:10:25 +03:00
unsigned int i ;
u8 c ;
if ( fw_dev - > config_rom_length < 30 ) {
dev_err ( & tscm - > unit - > device ,
" Configuration ROM is too short. \n " ) ;
return - ENODEV ;
}
/* Pick up model name from certain addresses. */
for ( i = 0 ; i < 8 ; i + + ) {
c = config_rom [ 28 + i / 4 ] > > ( 24 - 8 * ( i % 4 ) ) ;
if ( c = = ' \0 ' )
break ;
model [ i ] = c ;
}
model [ i ] = ' \0 ' ;
for ( i = 0 ; i < ARRAY_SIZE ( model_specs ) ; i + + ) {
if ( strcmp ( model , model_specs [ i ] . name ) = = 0 ) {
tscm - > spec = & model_specs [ i ] ;
break ;
}
}
if ( tscm - > spec = = NULL )
return - ENODEV ;
2015-10-01 16:02:11 +03:00
strcpy ( tscm - > card - > driver , " FW-TASCAM " ) ;
strcpy ( tscm - > card - > shortname , model ) ;
strcpy ( tscm - > card - > mixername , model ) ;
snprintf ( tscm - > card - > longname , sizeof ( tscm - > card - > longname ) ,
2015-10-12 13:10:25 +03:00
" TASCAM %s, GUID %08x%08x at %s, S%d " , model ,
2015-10-18 16:39:53 +03:00
fw_dev - > config_rom [ 3 ] , fw_dev - > config_rom [ 4 ] ,
2015-10-01 16:02:11 +03:00
dev_name ( & tscm - > unit - > device ) , 100 < < fw_dev - > max_speed ) ;
return 0 ;
}
2018-10-10 09:35:02 +03:00
static void tscm_card_free ( struct snd_card * card )
2015-10-01 16:02:11 +03:00
{
2018-10-10 09:35:02 +03:00
struct snd_tscm * tscm = card - > private_data ;
2015-10-12 13:10:21 +03:00
snd_tscm_transaction_unregister ( tscm ) ;
2015-10-01 16:02:15 +03:00
snd_tscm_stream_destroy_duplex ( tscm ) ;
2015-10-01 16:02:11 +03:00
}
2016-03-31 02:47:09 +03:00
static void do_registration ( struct work_struct * work )
{
struct snd_tscm * tscm = container_of ( work , struct snd_tscm , dwork . work ) ;
2015-10-01 16:02:11 +03:00
int err ;
2016-03-31 02:47:09 +03:00
err = snd_card_new ( & tscm - > unit - > device , - 1 , NULL , THIS_MODULE , 0 ,
& tscm - > card ) ;
2015-10-01 16:02:11 +03:00
if ( err < 0 )
2016-03-31 02:47:09 +03:00
return ;
2018-10-10 09:35:02 +03:00
tscm - > card - > private_free = tscm_card_free ;
tscm - > card - > private_data = tscm ;
2015-10-01 16:02:11 +03:00
2015-10-12 13:10:25 +03:00
err = identify_model ( tscm ) ;
2015-10-01 16:02:11 +03:00
if ( err < 0 )
goto error ;
2016-03-31 02:47:09 +03:00
err = snd_tscm_transaction_register ( tscm ) ;
2015-10-01 16:02:15 +03:00
if ( err < 0 )
goto error ;
2016-03-31 02:47:09 +03:00
err = snd_tscm_stream_init_duplex ( tscm ) ;
2015-10-01 16:02:16 +03:00
if ( err < 0 )
goto error ;
2016-03-31 02:47:09 +03:00
snd_tscm_proc_init ( tscm ) ;
err = snd_tscm_create_pcm_devices ( tscm ) ;
2015-10-12 13:10:21 +03:00
if ( err < 0 )
goto error ;
2015-10-12 13:10:23 +03:00
err = snd_tscm_create_midi_devices ( tscm ) ;
if ( err < 0 )
goto error ;
2015-10-01 16:02:17 +03:00
err = snd_tscm_create_hwdep_device ( tscm ) ;
if ( err < 0 )
goto error ;
2016-03-31 02:47:09 +03:00
err = snd_card_register ( tscm - > card ) ;
2015-10-01 16:02:11 +03:00
if ( err < 0 )
goto error ;
2016-03-31 02:47:09 +03:00
tscm - > registered = true ;
2015-10-01 16:02:11 +03:00
2016-03-31 02:47:09 +03:00
return ;
2015-10-01 16:02:11 +03:00
error :
2016-03-31 02:47:09 +03:00
snd_card_free ( tscm - > card ) ;
dev_info ( & tscm - > unit - > device ,
" Sound card registration failed: %d \n " , err ) ;
}
static int snd_tscm_probe ( struct fw_unit * unit ,
const struct ieee1394_device_id * entry )
{
struct snd_tscm * tscm ;
/* Allocate this independent of sound card instance. */
2018-10-03 02:21:50 +03:00
tscm = devm_kzalloc ( & unit - > device , sizeof ( struct snd_tscm ) , GFP_KERNEL ) ;
if ( ! tscm )
2016-03-31 02:47:09 +03:00
return - ENOMEM ;
tscm - > unit = fw_unit_get ( unit ) ;
dev_set_drvdata ( & unit - > device , tscm ) ;
mutex_init ( & tscm - > mutex ) ;
spin_lock_init ( & tscm - > lock ) ;
init_waitqueue_head ( & tscm - > hwdep_wait ) ;
/* Allocate and register this sound card later. */
INIT_DEFERRABLE_WORK ( & tscm - > dwork , do_registration ) ;
snd_fw_schedule_registration ( unit , & tscm - > dwork ) ;
return 0 ;
2015-10-01 16:02:11 +03:00
}
static void snd_tscm_update ( struct fw_unit * unit )
{
2015-10-01 16:02:15 +03:00
struct snd_tscm * tscm = dev_get_drvdata ( & unit - > device ) ;
2016-03-31 02:47:09 +03:00
/* Postpone a workqueue for deferred registration. */
if ( ! tscm - > registered )
snd_fw_schedule_registration ( unit , & tscm - > dwork ) ;
2015-10-12 13:10:21 +03:00
snd_tscm_transaction_reregister ( tscm ) ;
2016-03-31 02:47:09 +03:00
/*
* After registration , userspace can start packet streaming , then this
* code block works fine .
*/
if ( tscm - > registered ) {
mutex_lock ( & tscm - > mutex ) ;
snd_tscm_stream_update_duplex ( tscm ) ;
mutex_unlock ( & tscm - > mutex ) ;
}
2015-10-01 16:02:11 +03:00
}
static void snd_tscm_remove ( struct fw_unit * unit )
{
struct snd_tscm * tscm = dev_get_drvdata ( & unit - > device ) ;
2016-03-31 02:47:09 +03:00
/*
* Confirm to stop the work for registration before the sound card is
* going to be released . The work is not scheduled again because bus
* reset handler is not called anymore .
*/
cancel_delayed_work_sync ( & tscm - > dwork ) ;
if ( tscm - > registered ) {
2018-10-10 09:34:59 +03:00
// Block till all of ALSA character devices are released.
snd_card_free ( tscm - > card ) ;
2016-03-31 02:47:09 +03:00
}
2018-10-10 09:35:00 +03:00
mutex_destroy ( & tscm - > mutex ) ;
fw_unit_put ( tscm - > unit ) ;
2015-10-01 16:02:11 +03:00
}
static const struct ieee1394_device_id snd_tscm_id_table [ ] = {
{
. match_flags = IEEE1394_MATCH_VENDOR_ID |
2015-10-12 13:10:25 +03:00
IEEE1394_MATCH_SPECIFIER_ID ,
2015-10-01 16:02:11 +03:00
. vendor_id = 0x00022e ,
. specifier_id = 0x00022e ,
} ,
/* FE-08 requires reverse-engineering because it just has faders. */
{ }
} ;
MODULE_DEVICE_TABLE ( ieee1394 , snd_tscm_id_table ) ;
static struct fw_driver tscm_driver = {
. driver = {
. owner = THIS_MODULE ,
. name = " snd-firewire-tascam " ,
. bus = & fw_bus_type ,
} ,
. probe = snd_tscm_probe ,
. update = snd_tscm_update ,
. remove = snd_tscm_remove ,
. id_table = snd_tscm_id_table ,
} ;
static int __init snd_tscm_init ( void )
{
return driver_register ( & tscm_driver . driver ) ;
}
static void __exit snd_tscm_exit ( void )
{
driver_unregister ( & tscm_driver . driver ) ;
}
module_init ( snd_tscm_init ) ;
module_exit ( snd_tscm_exit ) ;