2012-05-26 11:43:19 +02:00
/*
* Stanton Control System 1 MIDI driver
*
* Copyright ( c ) Clemens Ladisch < clemens @ ladisch . de >
* Licensed under the terms of the GNU General Public License , version 2.
*/
# include <linux/device.h>
# include <linux/firewire.h>
# include <linux/firewire-constants.h>
# include <linux/interrupt.h>
# include <linux/module.h>
# include <linux/mod_devicetable.h>
# include <linux/slab.h>
# include <linux/string.h>
# include <linux/wait.h>
# include <sound/core.h>
# include <sound/initval.h>
# include <sound/rawmidi.h>
# include "lib.h"
# define OUI_STANTON 0x001260
# define MODEL_SCS_1M 0x001000
# define MODEL_SCS_1D 0x002000
# define HSS1394_ADDRESS 0xc007dedadadaULL
# define HSS1394_MAX_PACKET_SIZE 64
# define HSS1394_TAG_USER_DATA 0x00
# define HSS1394_TAG_CHANGE_ADDRESS 0xf1
struct scs {
struct snd_card * card ;
struct fw_unit * unit ;
struct fw_address_handler hss_handler ;
struct fw_transaction transaction ;
bool transaction_running ;
bool output_idle ;
u8 output_status ;
u8 output_bytes ;
bool output_escaped ;
bool output_escape_high_nibble ;
u8 input_escape_count ;
struct snd_rawmidi_substream * output ;
struct snd_rawmidi_substream * input ;
struct tasklet_struct tasklet ;
wait_queue_head_t idle_wait ;
u8 * buffer ;
} ;
static const u8 sysex_escape_prefix [ ] = {
0xf0 , /* SysEx begin */
0x00 , 0x01 , 0x60 , /* Stanton DJ */
0x48 , 0x53 , 0x53 , /* "HSS" */
} ;
static int scs_output_open ( struct snd_rawmidi_substream * stream )
{
struct scs * scs = stream - > rmidi - > private_data ;
scs - > output_status = 0 ;
scs - > output_bytes = 1 ;
scs - > output_escaped = false ;
return 0 ;
}
static int scs_output_close ( struct snd_rawmidi_substream * stream )
{
return 0 ;
}
static void scs_output_trigger ( struct snd_rawmidi_substream * stream , int up )
{
struct scs * scs = stream - > rmidi - > private_data ;
ACCESS_ONCE ( scs - > output ) = up ? stream : NULL ;
if ( up ) {
scs - > output_idle = false ;
tasklet_schedule ( & scs - > tasklet ) ;
}
}
static void scs_write_callback ( struct fw_card * card , int rcode ,
void * data , size_t length , void * callback_data )
{
struct scs * scs = callback_data ;
if ( rcode = = RCODE_GENERATION ) {
/* TODO: retry this packet */
}
scs - > transaction_running = false ;
tasklet_schedule ( & scs - > tasklet ) ;
}
static bool is_valid_running_status ( u8 status )
{
return status > = 0x80 & & status < = 0xef ;
}
static bool is_one_byte_cmd ( u8 status )
{
return status = = 0xf6 | |
status > = 0xf8 ;
}
static bool is_two_bytes_cmd ( u8 status )
{
return ( status > = 0xc0 & & status < = 0xdf ) | |
status = = 0xf1 | |
status = = 0xf3 ;
}
static bool is_three_bytes_cmd ( u8 status )
{
return ( status > = 0x80 & & status < = 0xbf ) | |
( status > = 0xe0 & & status < = 0xef ) | |
status = = 0xf2 ;
}
static bool is_invalid_cmd ( u8 status )
{
return status = = 0xf4 | |
status = = 0xf5 | |
status = = 0xf9 | |
status = = 0xfd ;
}
static void scs_output_tasklet ( unsigned long data )
{
struct scs * scs = ( void * ) data ;
struct snd_rawmidi_substream * stream ;
unsigned int i ;
u8 byte ;
struct fw_device * dev ;
int generation ;
if ( scs - > transaction_running )
return ;
stream = ACCESS_ONCE ( scs - > output ) ;
if ( ! stream ) {
scs - > output_idle = true ;
wake_up ( & scs - > idle_wait ) ;
return ;
}
i = scs - > output_bytes ;
for ( ; ; ) {
if ( snd_rawmidi_transmit ( stream , & byte , 1 ) ! = 1 ) {
scs - > output_bytes = i ;
scs - > output_idle = true ;
wake_up ( & scs - > idle_wait ) ;
return ;
}
/*
* Convert from real MIDI to what I think the device expects ( no
* running status , one command per packet , unescaped SysExs ) .
*/
if ( scs - > output_escaped & & byte < 0x80 ) {
if ( scs - > output_escape_high_nibble ) {
if ( i < HSS1394_MAX_PACKET_SIZE ) {
scs - > buffer [ i ] = byte < < 4 ;
scs - > output_escape_high_nibble = false ;
}
} else {
scs - > buffer [ i + + ] | = byte & 0x0f ;
scs - > output_escape_high_nibble = true ;
}
} else if ( byte < 0x80 ) {
if ( i = = 1 ) {
if ( ! is_valid_running_status ( scs - > output_status ) )
continue ;
scs - > buffer [ 0 ] = HSS1394_TAG_USER_DATA ;
scs - > buffer [ i + + ] = scs - > output_status ;
}
scs - > buffer [ i + + ] = byte ;
if ( ( i = = 3 & & is_two_bytes_cmd ( scs - > output_status ) ) | |
( i = = 4 & & is_three_bytes_cmd ( scs - > output_status ) ) )
break ;
if ( i = = 1 + ARRAY_SIZE ( sysex_escape_prefix ) & &
! memcmp ( scs - > buffer + 1 , sysex_escape_prefix ,
ARRAY_SIZE ( sysex_escape_prefix ) ) ) {
scs - > output_escaped = true ;
scs - > output_escape_high_nibble = true ;
i = 0 ;
}
if ( i > = HSS1394_MAX_PACKET_SIZE )
i = 1 ;
} else if ( byte = = 0xf7 ) {
if ( scs - > output_escaped ) {
if ( i > = 1 & & scs - > output_escape_high_nibble & &
scs - > buffer [ 0 ] ! = HSS1394_TAG_CHANGE_ADDRESS )
break ;
} else {
if ( i > 1 & & scs - > output_status = = 0xf0 ) {
scs - > buffer [ i + + ] = 0xf7 ;
break ;
}
}
i = 1 ;
scs - > output_escaped = false ;
} else if ( ! is_invalid_cmd ( byte ) & &
byte < 0xf8 ) {
i = 1 ;
scs - > buffer [ 0 ] = HSS1394_TAG_USER_DATA ;
scs - > buffer [ i + + ] = byte ;
scs - > output_status = byte ;
scs - > output_escaped = false ;
if ( is_one_byte_cmd ( byte ) )
break ;
}
}
scs - > output_bytes = 1 ;
scs - > output_escaped = false ;
scs - > transaction_running = true ;
dev = fw_parent_device ( scs - > unit ) ;
generation = dev - > generation ;
smp_rmb ( ) ; /* node_id vs. generation */
fw_send_request ( dev - > card , & scs - > transaction , TCODE_WRITE_BLOCK_REQUEST ,
dev - > node_id , generation , dev - > max_speed ,
HSS1394_ADDRESS , scs - > buffer , i ,
scs_write_callback , scs ) ;
}
static void scs_output_drain ( struct snd_rawmidi_substream * stream )
{
struct scs * scs = stream - > rmidi - > private_data ;
wait_event ( scs - > idle_wait , scs - > output_idle ) ;
}
static struct snd_rawmidi_ops output_ops = {
. open = scs_output_open ,
. close = scs_output_close ,
. trigger = scs_output_trigger ,
. drain = scs_output_drain ,
} ;
static int scs_input_open ( struct snd_rawmidi_substream * stream )
{
struct scs * scs = stream - > rmidi - > private_data ;
scs - > input_escape_count = 0 ;
return 0 ;
}
static int scs_input_close ( struct snd_rawmidi_substream * stream )
{
return 0 ;
}
static void scs_input_trigger ( struct snd_rawmidi_substream * stream , int up )
{
struct scs * scs = stream - > rmidi - > private_data ;
ACCESS_ONCE ( scs - > input ) = up ? stream : NULL ;
}
static void scs_input_escaped_byte ( struct snd_rawmidi_substream * stream ,
u8 byte )
{
u8 nibbles [ 2 ] ;
nibbles [ 0 ] = byte > > 4 ;
nibbles [ 1 ] = byte & 0x0f ;
snd_rawmidi_receive ( stream , nibbles , 2 ) ;
}
static void scs_input_midi_byte ( struct scs * scs ,
struct snd_rawmidi_substream * stream ,
u8 byte )
{
if ( scs - > input_escape_count > 0 ) {
scs_input_escaped_byte ( stream , byte ) ;
scs - > input_escape_count - - ;
if ( scs - > input_escape_count = = 0 )
snd_rawmidi_receive ( stream , ( const u8 [ ] ) { 0xf7 } , 1 ) ;
} else if ( byte = = 0xf9 ) {
snd_rawmidi_receive ( stream , sysex_escape_prefix ,
ARRAY_SIZE ( sysex_escape_prefix ) ) ;
scs_input_escaped_byte ( stream , 0x00 ) ;
scs_input_escaped_byte ( stream , 0xf9 ) ;
scs - > input_escape_count = 3 ;
} else {
snd_rawmidi_receive ( stream , & byte , 1 ) ;
}
}
static void scs_input_packet ( struct scs * scs ,
struct snd_rawmidi_substream * stream ,
const u8 * data , unsigned int bytes )
{
unsigned int i ;
if ( data [ 0 ] = = HSS1394_TAG_USER_DATA ) {
for ( i = 1 ; i < bytes ; + + i )
scs_input_midi_byte ( scs , stream , data [ i ] ) ;
} else {
snd_rawmidi_receive ( stream , sysex_escape_prefix ,
ARRAY_SIZE ( sysex_escape_prefix ) ) ;
for ( i = 0 ; i < bytes ; + + i )
scs_input_escaped_byte ( stream , data [ i ] ) ;
snd_rawmidi_receive ( stream , ( const u8 [ ] ) { 0xf7 } , 1 ) ;
}
}
static struct snd_rawmidi_ops input_ops = {
. open = scs_input_open ,
. close = scs_input_close ,
. trigger = scs_input_trigger ,
} ;
static int scs_create_midi ( struct scs * scs )
{
struct snd_rawmidi * rmidi ;
int err ;
err = snd_rawmidi_new ( scs - > card , " SCS.1x " , 0 , 1 , 1 , & rmidi ) ;
if ( err < 0 )
return err ;
snprintf ( rmidi - > name , sizeof ( rmidi - > name ) ,
" %s MIDI " , scs - > card - > shortname ) ;
rmidi - > info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
SNDRV_RAWMIDI_INFO_INPUT |
SNDRV_RAWMIDI_INFO_DUPLEX ;
rmidi - > private_data = scs ;
snd_rawmidi_set_ops ( rmidi , SNDRV_RAWMIDI_STREAM_OUTPUT , & output_ops ) ;
snd_rawmidi_set_ops ( rmidi , SNDRV_RAWMIDI_STREAM_INPUT , & input_ops ) ;
return 0 ;
}
static void handle_hss ( struct fw_card * card , struct fw_request * request ,
int tcode , int destination , int source , int generation ,
unsigned long long offset , void * data , size_t length ,
void * callback_data )
{
struct scs * scs = callback_data ;
struct snd_rawmidi_substream * stream ;
if ( offset ! = scs - > hss_handler . offset ) {
fw_send_response ( card , request , RCODE_ADDRESS_ERROR ) ;
return ;
}
if ( tcode ! = TCODE_WRITE_QUADLET_REQUEST & &
tcode ! = TCODE_WRITE_BLOCK_REQUEST ) {
fw_send_response ( card , request , RCODE_TYPE_ERROR ) ;
return ;
}
if ( length > = 1 ) {
stream = ACCESS_ONCE ( scs - > input ) ;
if ( stream )
scs_input_packet ( scs , stream , data , length ) ;
}
fw_send_response ( card , request , RCODE_COMPLETE ) ;
}
static int scs_init_hss_address ( struct scs * scs )
{
__be64 data ;
int err ;
data = cpu_to_be64 ( ( ( u64 ) HSS1394_TAG_CHANGE_ADDRESS < < 56 ) |
scs - > hss_handler . offset ) ;
err = snd_fw_transaction ( scs - > unit , TCODE_WRITE_BLOCK_REQUEST ,
2011-09-04 22:17:38 +02:00
HSS1394_ADDRESS , & data , 8 , 0 ) ;
2012-05-26 11:43:19 +02:00
if ( err < 0 )
dev_err ( & scs - > unit - > device , " HSS1394 communication failed \n " ) ;
return err ;
}
static void scs_card_free ( struct snd_card * card )
{
struct scs * scs = card - > private_data ;
fw_core_remove_address_handler ( & scs - > hss_handler ) ;
kfree ( scs - > buffer ) ;
}
2013-06-09 18:15:00 +02:00
static int scs_probe ( struct fw_unit * unit , const struct ieee1394_device_id * id )
2012-05-26 11:43:19 +02:00
{
struct fw_device * fw_dev = fw_parent_device ( unit ) ;
struct snd_card * card ;
struct scs * scs ;
int err ;
2014-01-29 14:23:55 +01:00
err = snd_card_new ( & unit - > device , - 16 , NULL , THIS_MODULE ,
sizeof ( * scs ) , & card ) ;
2012-05-26 11:43:19 +02:00
if ( err < 0 )
return err ;
scs = card - > private_data ;
scs - > card = card ;
scs - > unit = unit ;
tasklet_init ( & scs - > tasklet , scs_output_tasklet , ( unsigned long ) scs ) ;
init_waitqueue_head ( & scs - > idle_wait ) ;
scs - > output_idle = true ;
scs - > buffer = kmalloc ( HSS1394_MAX_PACKET_SIZE , GFP_KERNEL ) ;
2013-06-18 21:09:42 +08:00
if ( ! scs - > buffer ) {
err = - ENOMEM ;
2012-05-26 11:43:19 +02:00
goto err_card ;
2013-06-18 21:09:42 +08:00
}
2012-05-26 11:43:19 +02:00
scs - > hss_handler . length = HSS1394_MAX_PACKET_SIZE ;
scs - > hss_handler . address_callback = handle_hss ;
scs - > hss_handler . callback_data = scs ;
err = fw_core_add_address_handler ( & scs - > hss_handler ,
& fw_high_memory_region ) ;
if ( err < 0 )
goto err_buffer ;
card - > private_free = scs_card_free ;
strcpy ( card - > driver , " SCS.1x " ) ;
strcpy ( card - > shortname , " SCS.1x " ) ;
fw_csr_string ( unit - > directory , CSR_MODEL ,
card - > shortname , sizeof ( card - > shortname ) ) ;
snprintf ( card - > longname , sizeof ( card - > longname ) ,
" Stanton DJ %s (GUID %08x%08x) at %s, S%d " ,
card - > shortname , fw_dev - > config_rom [ 3 ] , fw_dev - > config_rom [ 4 ] ,
dev_name ( & unit - > device ) , 100 < < fw_dev - > max_speed ) ;
strcpy ( card - > mixername , card - > shortname ) ;
err = scs_init_hss_address ( scs ) ;
if ( err < 0 )
goto err_card ;
err = scs_create_midi ( scs ) ;
if ( err < 0 )
goto err_card ;
err = snd_card_register ( card ) ;
if ( err < 0 )
goto err_card ;
2013-06-09 18:15:00 +02:00
dev_set_drvdata ( & unit - > device , scs ) ;
2012-05-26 11:43:19 +02:00
return 0 ;
err_buffer :
kfree ( scs - > buffer ) ;
err_card :
snd_card_free ( card ) ;
return err ;
}
2013-06-09 18:15:00 +02:00
static void scs_update ( struct fw_unit * unit )
2012-05-26 11:43:19 +02:00
{
2013-06-09 18:15:00 +02:00
struct scs * scs = dev_get_drvdata ( & unit - > device ) ;
2011-09-04 22:17:38 +02:00
int generation ;
2013-06-09 18:15:00 +02:00
__be64 data ;
data = cpu_to_be64 ( ( ( u64 ) HSS1394_TAG_CHANGE_ADDRESS < < 56 ) |
scs - > hss_handler . offset ) ;
2011-09-04 22:17:38 +02:00
generation = fw_parent_device ( unit ) - > generation ;
smp_rmb ( ) ; /* node_id vs. generation */
2013-06-09 18:15:00 +02:00
snd_fw_transaction ( scs - > unit , TCODE_WRITE_BLOCK_REQUEST ,
2011-09-04 22:17:38 +02:00
HSS1394_ADDRESS , & data , 8 ,
FW_FIXED_GENERATION | generation ) ;
2013-06-09 18:15:00 +02:00
}
static void scs_remove ( struct fw_unit * unit )
{
struct scs * scs = dev_get_drvdata ( & unit - > device ) ;
2012-05-26 11:43:19 +02:00
snd_card_disconnect ( scs - > card ) ;
ACCESS_ONCE ( scs - > output ) = NULL ;
ACCESS_ONCE ( scs - > input ) = NULL ;
wait_event ( scs - > idle_wait , scs - > output_idle ) ;
tasklet_kill ( & scs - > tasklet ) ;
snd_card_free_when_closed ( scs - > card ) ;
}
static const struct ieee1394_device_id scs_id_table [ ] = {
{
. match_flags = IEEE1394_MATCH_VENDOR_ID |
IEEE1394_MATCH_MODEL_ID ,
. vendor_id = OUI_STANTON ,
. model_id = MODEL_SCS_1M ,
} ,
{
. match_flags = IEEE1394_MATCH_VENDOR_ID |
IEEE1394_MATCH_MODEL_ID ,
. vendor_id = OUI_STANTON ,
. model_id = MODEL_SCS_1D ,
} ,
{ }
} ;
MODULE_DEVICE_TABLE ( ieee1394 , scs_id_table ) ;
MODULE_DESCRIPTION ( " SCS.1x MIDI driver " ) ;
MODULE_AUTHOR ( " Clemens Ladisch <clemens@ladisch.de> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
static struct fw_driver scs_driver = {
. driver = {
. owner = THIS_MODULE ,
. name = KBUILD_MODNAME ,
. bus = & fw_bus_type ,
} ,
2013-06-09 18:15:00 +02:00
. probe = scs_probe ,
2012-05-26 11:43:19 +02:00
. update = scs_update ,
2013-06-09 18:15:00 +02:00
. remove = scs_remove ,
2012-05-26 11:43:19 +02:00
. id_table = scs_id_table ,
} ;
static int __init alsa_scs1x_init ( void )
{
return driver_register ( & scs_driver . driver ) ;
}
static void __exit alsa_scs1x_exit ( void )
{
driver_unregister ( & scs_driver . driver ) ;
}
module_init ( alsa_scs1x_init ) ;
module_exit ( alsa_scs1x_exit ) ;