2005-10-21 00:57:51 +04:00
/*
* Copyright 10 / 16 / 2005 Tilman Kranz < tilde @ tk - sls . de >
* Creative Audio MIDI , for the CA0106 Driver
* Version : 0.0 .1
*
* Changelog :
* Implementation is based on mpu401 and emu10k1x and
* tested with ca0106 .
2007-10-15 11:50:19 +04:00
* mpu401 : Copyright ( c ) by Jaroslav Kysela < perex @ perex . cz >
2005-10-22 13:49:51 +04:00
* emu10k1x : Copyright ( c ) by Francisco Moraes < fmoraes @ nc . rr . com >
2005-10-21 00:57:51 +04:00
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
*
*/
# include <linux/spinlock.h>
# include <sound/core.h>
# include <sound/rawmidi.h>
# include "ca_midi.h"
# define ca_midi_write_data(midi, data) midi->write(midi, data, 0)
# define ca_midi_write_cmd(midi, data) midi->write(midi, data, 1)
# define ca_midi_read_data(midi) midi->read(midi, 0)
# define ca_midi_read_stat(midi) midi->read(midi, 1)
# define ca_midi_input_avail(midi) (!(ca_midi_read_stat(midi) & midi->input_avail))
# define ca_midi_output_ready(midi) (!(ca_midi_read_stat(midi) & midi->output_ready))
2005-11-17 16:55:40 +03:00
static void ca_midi_clear_rx ( struct snd_ca_midi * midi )
2005-10-21 00:57:51 +04:00
{
int timeout = 100000 ;
for ( ; timeout > 0 & & ca_midi_input_avail ( midi ) ; timeout - - )
ca_midi_read_data ( midi ) ;
# ifdef CONFIG_SND_DEBUG
if ( timeout < = 0 )
2014-02-25 19:04:05 +04:00
pr_err ( " ca_midi_clear_rx: timeout (status = 0x%x) \n " ,
2005-11-17 16:55:40 +03:00
ca_midi_read_stat ( midi ) ) ;
2005-10-21 00:57:51 +04:00
# endif
}
2005-11-17 16:55:40 +03:00
static void ca_midi_interrupt ( struct snd_ca_midi * midi , unsigned int status )
{
2005-10-21 00:57:51 +04:00
unsigned char byte ;
if ( midi - > rmidi = = NULL ) {
midi - > interrupt_disable ( midi , midi - > tx_enable | midi - > rx_enable ) ;
return ;
}
spin_lock ( & midi - > input_lock ) ;
if ( ( status & midi - > ipr_rx ) & & ca_midi_input_avail ( midi ) ) {
if ( ! ( midi - > midi_mode & CA_MIDI_MODE_INPUT ) ) {
ca_midi_clear_rx ( midi ) ;
} else {
byte = ca_midi_read_data ( midi ) ;
if ( midi - > substream_input )
snd_rawmidi_receive ( midi - > substream_input , & byte , 1 ) ;
}
}
spin_unlock ( & midi - > input_lock ) ;
spin_lock ( & midi - > output_lock ) ;
if ( ( status & midi - > ipr_tx ) & & ca_midi_output_ready ( midi ) ) {
if ( midi - > substream_output & &
snd_rawmidi_transmit ( midi - > substream_output , & byte , 1 ) = = 1 ) {
ca_midi_write_data ( midi , byte ) ;
} else {
midi - > interrupt_disable ( midi , midi - > tx_enable ) ;
}
}
spin_unlock ( & midi - > output_lock ) ;
}
2005-11-17 16:55:40 +03:00
static void ca_midi_cmd ( struct snd_ca_midi * midi , unsigned char cmd , int ack )
2005-10-21 00:57:51 +04:00
{
unsigned long flags ;
int timeout , ok ;
spin_lock_irqsave ( & midi - > input_lock , flags ) ;
ca_midi_write_data ( midi , 0x00 ) ;
/* ca_midi_clear_rx(midi); */
ca_midi_write_cmd ( midi , cmd ) ;
if ( ack ) {
ok = 0 ;
timeout = 10000 ;
while ( ! ok & & timeout - - > 0 ) {
if ( ca_midi_input_avail ( midi ) ) {
if ( ca_midi_read_data ( midi ) = = midi - > ack )
ok = 1 ;
}
}
if ( ! ok & & ca_midi_read_data ( midi ) = = midi - > ack )
ok = 1 ;
} else {
ok = 1 ;
}
spin_unlock_irqrestore ( & midi - > input_lock , flags ) ;
if ( ! ok )
2014-02-25 19:04:05 +04:00
pr_err ( " ca_midi_cmd: 0x%x failed at 0x%x (status = 0x%x, data = 0x%x)!!! \n " ,
2005-10-21 00:57:51 +04:00
cmd ,
midi - > get_dev_id_port ( midi - > dev_id ) ,
ca_midi_read_stat ( midi ) ,
ca_midi_read_data ( midi ) ) ;
}
2005-11-17 16:55:40 +03:00
static int ca_midi_input_open ( struct snd_rawmidi_substream * substream )
2005-10-21 00:57:51 +04:00
{
2005-11-17 16:55:40 +03:00
struct snd_ca_midi * midi = substream - > rmidi - > private_data ;
2005-10-21 00:57:51 +04:00
unsigned long flags ;
2008-08-08 19:12:14 +04:00
if ( snd_BUG_ON ( ! midi - > dev_id ) )
return - ENXIO ;
2005-10-21 00:57:51 +04:00
spin_lock_irqsave ( & midi - > open_lock , flags ) ;
midi - > midi_mode | = CA_MIDI_MODE_INPUT ;
midi - > substream_input = substream ;
if ( ! ( midi - > midi_mode & CA_MIDI_MODE_OUTPUT ) ) {
spin_unlock_irqrestore ( & midi - > open_lock , flags ) ;
ca_midi_cmd ( midi , midi - > reset , 1 ) ;
ca_midi_cmd ( midi , midi - > enter_uart , 1 ) ;
} else {
spin_unlock_irqrestore ( & midi - > open_lock , flags ) ;
}
return 0 ;
}
2005-11-17 16:55:40 +03:00
static int ca_midi_output_open ( struct snd_rawmidi_substream * substream )
2005-10-21 00:57:51 +04:00
{
2005-11-17 16:55:40 +03:00
struct snd_ca_midi * midi = substream - > rmidi - > private_data ;
2005-10-21 00:57:51 +04:00
unsigned long flags ;
2008-08-08 19:12:14 +04:00
if ( snd_BUG_ON ( ! midi - > dev_id ) )
return - ENXIO ;
2005-10-21 00:57:51 +04:00
spin_lock_irqsave ( & midi - > open_lock , flags ) ;
midi - > midi_mode | = CA_MIDI_MODE_OUTPUT ;
midi - > substream_output = substream ;
if ( ! ( midi - > midi_mode & CA_MIDI_MODE_INPUT ) ) {
spin_unlock_irqrestore ( & midi - > open_lock , flags ) ;
ca_midi_cmd ( midi , midi - > reset , 1 ) ;
ca_midi_cmd ( midi , midi - > enter_uart , 1 ) ;
} else {
spin_unlock_irqrestore ( & midi - > open_lock , flags ) ;
}
return 0 ;
}
2005-11-17 16:55:40 +03:00
static int ca_midi_input_close ( struct snd_rawmidi_substream * substream )
2005-10-21 00:57:51 +04:00
{
2005-11-17 16:55:40 +03:00
struct snd_ca_midi * midi = substream - > rmidi - > private_data ;
2005-10-21 00:57:51 +04:00
unsigned long flags ;
2008-08-08 19:12:14 +04:00
if ( snd_BUG_ON ( ! midi - > dev_id ) )
return - ENXIO ;
2005-10-21 00:57:51 +04:00
spin_lock_irqsave ( & midi - > open_lock , flags ) ;
midi - > interrupt_disable ( midi , midi - > rx_enable ) ;
midi - > midi_mode & = ~ CA_MIDI_MODE_INPUT ;
midi - > substream_input = NULL ;
if ( ! ( midi - > midi_mode & CA_MIDI_MODE_OUTPUT ) ) {
spin_unlock_irqrestore ( & midi - > open_lock , flags ) ;
ca_midi_cmd ( midi , midi - > reset , 0 ) ;
} else {
spin_unlock_irqrestore ( & midi - > open_lock , flags ) ;
}
return 0 ;
}
2005-11-17 16:55:40 +03:00
static int ca_midi_output_close ( struct snd_rawmidi_substream * substream )
2005-10-21 00:57:51 +04:00
{
2005-11-17 16:55:40 +03:00
struct snd_ca_midi * midi = substream - > rmidi - > private_data ;
2005-10-21 00:57:51 +04:00
unsigned long flags ;
2008-08-08 19:12:14 +04:00
if ( snd_BUG_ON ( ! midi - > dev_id ) )
return - ENXIO ;
2005-10-21 00:57:51 +04:00
spin_lock_irqsave ( & midi - > open_lock , flags ) ;
midi - > interrupt_disable ( midi , midi - > tx_enable ) ;
midi - > midi_mode & = ~ CA_MIDI_MODE_OUTPUT ;
midi - > substream_output = NULL ;
if ( ! ( midi - > midi_mode & CA_MIDI_MODE_INPUT ) ) {
spin_unlock_irqrestore ( & midi - > open_lock , flags ) ;
ca_midi_cmd ( midi , midi - > reset , 0 ) ;
} else {
spin_unlock_irqrestore ( & midi - > open_lock , flags ) ;
}
return 0 ;
}
2005-11-17 16:55:40 +03:00
static void ca_midi_input_trigger ( struct snd_rawmidi_substream * substream , int up )
2005-10-21 00:57:51 +04:00
{
2005-11-17 16:55:40 +03:00
struct snd_ca_midi * midi = substream - > rmidi - > private_data ;
2008-08-08 19:12:14 +04:00
if ( snd_BUG_ON ( ! midi - > dev_id ) )
return ;
2005-10-21 00:57:51 +04:00
if ( up ) {
midi - > interrupt_enable ( midi , midi - > rx_enable ) ;
} else {
midi - > interrupt_disable ( midi , midi - > rx_enable ) ;
}
}
2005-11-17 16:55:40 +03:00
static void ca_midi_output_trigger ( struct snd_rawmidi_substream * substream , int up )
2005-10-21 00:57:51 +04:00
{
2005-11-17 16:55:40 +03:00
struct snd_ca_midi * midi = substream - > rmidi - > private_data ;
2005-10-21 00:57:51 +04:00
unsigned long flags ;
2008-08-08 19:12:14 +04:00
if ( snd_BUG_ON ( ! midi - > dev_id ) )
return ;
2005-10-21 00:57:51 +04:00
if ( up ) {
int max = 4 ;
unsigned char byte ;
spin_lock_irqsave ( & midi - > output_lock , flags ) ;
/* try to send some amount of bytes here before interrupts */
while ( max > 0 ) {
if ( ca_midi_output_ready ( midi ) ) {
if ( ! ( midi - > midi_mode & CA_MIDI_MODE_OUTPUT ) | |
snd_rawmidi_transmit ( substream , & byte , 1 ) ! = 1 ) {
/* no more data */
spin_unlock_irqrestore ( & midi - > output_lock , flags ) ;
return ;
}
ca_midi_write_data ( midi , byte ) ;
max - - ;
} else {
break ;
}
}
spin_unlock_irqrestore ( & midi - > output_lock , flags ) ;
midi - > interrupt_enable ( midi , midi - > tx_enable ) ;
} else {
midi - > interrupt_disable ( midi , midi - > tx_enable ) ;
}
}
2005-11-17 16:55:40 +03:00
static struct snd_rawmidi_ops ca_midi_output =
2005-10-21 00:57:51 +04:00
{
. open = ca_midi_output_open ,
. close = ca_midi_output_close ,
. trigger = ca_midi_output_trigger ,
} ;
2005-11-17 16:55:40 +03:00
static struct snd_rawmidi_ops ca_midi_input =
2005-10-21 00:57:51 +04:00
{
. open = ca_midi_input_open ,
. close = ca_midi_input_close ,
. trigger = ca_midi_input_trigger ,
} ;
2005-11-17 16:55:40 +03:00
static void ca_midi_free ( struct snd_ca_midi * midi )
{
2005-10-21 00:57:51 +04:00
midi - > interrupt = NULL ;
midi - > interrupt_enable = NULL ;
midi - > interrupt_disable = NULL ;
midi - > read = NULL ;
midi - > write = NULL ;
midi - > get_dev_id_card = NULL ;
midi - > get_dev_id_port = NULL ;
midi - > rmidi = NULL ;
}
2005-11-17 16:55:40 +03:00
static void ca_rmidi_free ( struct snd_rawmidi * rmidi )
2005-10-21 00:57:51 +04:00
{
2005-11-17 16:55:40 +03:00
ca_midi_free ( rmidi - > private_data ) ;
2005-10-21 00:57:51 +04:00
}
2012-12-06 21:35:10 +04:00
int ca_midi_init ( void * dev_id , struct snd_ca_midi * midi , int device , char * name )
2005-10-21 00:57:51 +04:00
{
2005-11-17 16:55:40 +03:00
struct snd_rawmidi * rmidi ;
2005-10-21 00:57:51 +04:00
int err ;
if ( ( err = snd_rawmidi_new ( midi - > get_dev_id_card ( midi - > dev_id ) , name , device , 1 , 1 , & rmidi ) ) < 0 )
return err ;
midi - > dev_id = dev_id ;
midi - > interrupt = ca_midi_interrupt ;
spin_lock_init ( & midi - > open_lock ) ;
spin_lock_init ( & midi - > input_lock ) ;
spin_lock_init ( & midi - > output_lock ) ;
strcpy ( rmidi - > name , name ) ;
snd_rawmidi_set_ops ( rmidi , SNDRV_RAWMIDI_STREAM_OUTPUT , & ca_midi_output ) ;
snd_rawmidi_set_ops ( rmidi , SNDRV_RAWMIDI_STREAM_INPUT , & ca_midi_input ) ;
rmidi - > info_flags | = SNDRV_RAWMIDI_INFO_OUTPUT |
SNDRV_RAWMIDI_INFO_INPUT |
SNDRV_RAWMIDI_INFO_DUPLEX ;
rmidi - > private_data = midi ;
rmidi - > private_free = ca_rmidi_free ;
midi - > rmidi = rmidi ;
return 0 ;
}