2005-04-16 15:20:36 -07:00
/*
2006-10-03 23:01:26 +02:00
* sound / oss / uart401 . c
2005-04-16 15:20:36 -07:00
*
* MPU - 401 UART driver ( formerly uart401_midi . c )
*
*
* Copyright ( C ) by Hannu Savolainen 1993 - 1997
*
* OSS / Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE ( GPL )
* Version 2 ( June 1991 ) . See the " COPYING " file distributed with this software
* for more info .
*
* Changes :
* Alan Cox Reformatted , removed sound_mem usage , use normal Linux
* interrupt allocation . Protect against bogus unload
* Fixed to allow IRQ > 15
* Christoph Hellwig Adapted to module_init / module_exit
* Arnaldo C . de Melo got rid of check_region
*
* Status :
* Untested
*/
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/module.h>
# include <linux/spinlock.h>
# include "sound_config.h"
# include "mpu401.h"
typedef struct uart401_devc
{
int base ;
int irq ;
int * osp ;
void ( * midi_input_intr ) ( int dev , unsigned char data ) ;
int opened , disabled ;
volatile unsigned char input_byte ;
int my_dev ;
int share_irq ;
spinlock_t lock ;
}
uart401_devc ;
# define DATAPORT (devc->base)
# define COMDPORT (devc->base+1)
# define STATPORT (devc->base+1)
static int uart401_status ( uart401_devc * devc )
{
return inb ( STATPORT ) ;
}
# define input_avail(devc) (!(uart401_status(devc)&INPUT_AVAIL))
# define output_ready(devc) (!(uart401_status(devc)&OUTPUT_READY))
static void uart401_cmd ( uart401_devc * devc , unsigned char cmd )
{
outb ( ( cmd ) , COMDPORT ) ;
}
static int uart401_read ( uart401_devc * devc )
{
return inb ( DATAPORT ) ;
}
static void uart401_write ( uart401_devc * devc , unsigned char byte )
{
outb ( ( byte ) , DATAPORT ) ;
}
# define OUTPUT_READY 0x40
# define INPUT_AVAIL 0x80
# define MPU_ACK 0xFE
# define MPU_RESET 0xFF
# define UART_MODE_ON 0x3F
static int reset_uart401 ( uart401_devc * devc ) ;
static void enter_uart_mode ( uart401_devc * devc ) ;
static void uart401_input_loop ( uart401_devc * devc )
{
int work_limit = 30000 ;
while ( input_avail ( devc ) & & - - work_limit )
{
unsigned char c = uart401_read ( devc ) ;
if ( c = = MPU_ACK )
devc - > input_byte = c ;
else if ( devc - > opened & OPEN_READ & & devc - > midi_input_intr )
devc - > midi_input_intr ( devc - > my_dev , c ) ;
}
if ( work_limit = = 0 )
printk ( KERN_WARNING " Too much work in interrupt on uart401 (0x%X). UART jabbering ?? \n " , devc - > base ) ;
}
irqreturn_t uart401intr ( int irq , void * dev_id , struct pt_regs * dummy )
{
uart401_devc * devc = dev_id ;
if ( devc = = NULL )
{
printk ( KERN_ERR " uart401: bad devc \n " ) ;
return IRQ_NONE ;
}
if ( input_avail ( devc ) )
uart401_input_loop ( devc ) ;
return IRQ_HANDLED ;
}
static int
uart401_open ( int dev , int mode ,
void ( * input ) ( int dev , unsigned char data ) ,
void ( * output ) ( int dev )
)
{
uart401_devc * devc = ( uart401_devc * ) midi_devs [ dev ] - > devc ;
if ( devc - > opened )
return - EBUSY ;
/* Flush the UART */
while ( input_avail ( devc ) )
uart401_read ( devc ) ;
devc - > midi_input_intr = input ;
devc - > opened = mode ;
enter_uart_mode ( devc ) ;
devc - > disabled = 0 ;
return 0 ;
}
static void uart401_close ( int dev )
{
uart401_devc * devc = ( uart401_devc * ) midi_devs [ dev ] - > devc ;
reset_uart401 ( devc ) ;
devc - > opened = 0 ;
}
static int uart401_out ( int dev , unsigned char midi_byte )
{
int timeout ;
unsigned long flags ;
uart401_devc * devc = ( uart401_devc * ) midi_devs [ dev ] - > devc ;
if ( devc - > disabled )
return 1 ;
/*
* Test for input since pending input seems to block the output .
*/
spin_lock_irqsave ( & devc - > lock , flags ) ;
if ( input_avail ( devc ) )
uart401_input_loop ( devc ) ;
spin_unlock_irqrestore ( & devc - > lock , flags ) ;
/*
* Sometimes it takes about 13000 loops before the output becomes ready
* ( After reset ) . Normally it takes just about 10 loops .
*/
for ( timeout = 30000 ; timeout > 0 & & ! output_ready ( devc ) ; timeout - - ) ;
if ( ! output_ready ( devc ) )
{
printk ( KERN_WARNING " uart401: Timeout - Device not responding \n " ) ;
devc - > disabled = 1 ;
reset_uart401 ( devc ) ;
enter_uart_mode ( devc ) ;
return 1 ;
}
uart401_write ( devc , midi_byte ) ;
return 1 ;
}
static inline int uart401_start_read ( int dev )
{
return 0 ;
}
static inline int uart401_end_read ( int dev )
{
return 0 ;
}
static inline void uart401_kick ( int dev )
{
}
static inline int uart401_buffer_status ( int dev )
{
return 0 ;
}
# define MIDI_SYNTH_NAME "MPU-401 UART"
# define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
# include "midi_synth.h"
static const struct midi_operations uart401_operations =
{
. owner = THIS_MODULE ,
. info = { " MPU-401 (UART) MIDI " , 0 , 0 , SNDCARD_MPU401 } ,
. converter = & std_midi_synth ,
. in_info = { 0 } ,
. open = uart401_open ,
. close = uart401_close ,
. outputc = uart401_out ,
. start_read = uart401_start_read ,
. end_read = uart401_end_read ,
. kick = uart401_kick ,
. buffer_status = uart401_buffer_status ,
} ;
static void enter_uart_mode ( uart401_devc * devc )
{
int ok , timeout ;
unsigned long flags ;
spin_lock_irqsave ( & devc - > lock , flags ) ;
for ( timeout = 30000 ; timeout > 0 & & ! output_ready ( devc ) ; timeout - - ) ;
devc - > input_byte = 0 ;
uart401_cmd ( devc , UART_MODE_ON ) ;
ok = 0 ;
for ( timeout = 50000 ; timeout > 0 & & ! ok ; timeout - - )
if ( devc - > input_byte = = MPU_ACK )
ok = 1 ;
else if ( input_avail ( devc ) )
if ( uart401_read ( devc ) = = MPU_ACK )
ok = 1 ;
spin_unlock_irqrestore ( & devc - > lock , flags ) ;
}
static int reset_uart401 ( uart401_devc * devc )
{
int ok , timeout , n ;
/*
* Send the RESET command . Try again if no success at the first time .
*/
ok = 0 ;
for ( n = 0 ; n < 2 & & ! ok ; n + + )
{
for ( timeout = 30000 ; timeout > 0 & & ! output_ready ( devc ) ; timeout - - ) ;
devc - > input_byte = 0 ;
uart401_cmd ( devc , MPU_RESET ) ;
/*
* Wait at least 25 msec . This method is not accurate so let ' s make the
* loop bit longer . Cannot sleep since this is called during boot .
*/
for ( timeout = 50000 ; timeout > 0 & & ! ok ; timeout - - )
{
if ( devc - > input_byte = = MPU_ACK ) /* Interrupt */
ok = 1 ;
else if ( input_avail ( devc ) )
{
if ( uart401_read ( devc ) = = MPU_ACK )
ok = 1 ;
}
}
}
if ( ok )
{
DEB ( printk ( " Reset UART401 OK \n " ) ) ;
}
else
DDB ( printk ( " Reset UART401 failed - No hardware detected. \n " ) ) ;
if ( ok )
uart401_input_loop ( devc ) ; /*
* Flush input before enabling interrupts
*/
return ok ;
}
int probe_uart401 ( struct address_info * hw_config , struct module * owner )
{
uart401_devc * devc ;
char * name = " MPU-401 (UART) MIDI " ;
int ok = 0 ;
unsigned long flags ;
DDB ( printk ( " Entered probe_uart401() \n " ) ) ;
/* Default to "not found" */
hw_config - > slots [ 4 ] = - 1 ;
if ( ! request_region ( hw_config - > io_base , 4 , " MPU-401 UART " ) ) {
printk ( KERN_INFO " uart401: could not request_region(%d, 4) \n " , hw_config - > io_base ) ;
return 0 ;
}
devc = kmalloc ( sizeof ( uart401_devc ) , GFP_KERNEL ) ;
if ( ! devc ) {
printk ( KERN_WARNING " uart401: Can't allocate memory \n " ) ;
goto cleanup_region ;
}
devc - > base = hw_config - > io_base ;
devc - > irq = hw_config - > irq ;
devc - > osp = hw_config - > osp ;
devc - > midi_input_intr = NULL ;
devc - > opened = 0 ;
devc - > input_byte = 0 ;
devc - > my_dev = 0 ;
devc - > share_irq = 0 ;
spin_lock_init ( & devc - > lock ) ;
spin_lock_irqsave ( & devc - > lock , flags ) ;
ok = reset_uart401 ( devc ) ;
spin_unlock_irqrestore ( & devc - > lock , flags ) ;
if ( ! ok )
goto cleanup_devc ;
if ( hw_config - > name )
name = hw_config - > name ;
if ( devc - > irq < 0 ) {
devc - > share_irq = 1 ;
devc - > irq * = - 1 ;
} else
devc - > share_irq = 0 ;
if ( ! devc - > share_irq )
if ( request_irq ( devc - > irq , uart401intr , 0 , " MPU-401 UART " , devc ) < 0 ) {
printk ( KERN_WARNING " uart401: Failed to allocate IRQ%d \n " , devc - > irq ) ;
devc - > share_irq = 1 ;
}
devc - > my_dev = sound_alloc_mididev ( ) ;
enter_uart_mode ( devc ) ;
if ( devc - > my_dev = = - 1 ) {
printk ( KERN_INFO " uart401: Too many midi devices detected \n " ) ;
goto cleanup_irq ;
}
conf_printf ( name , hw_config ) ;
midi_devs [ devc - > my_dev ] = kmalloc ( sizeof ( struct midi_operations ) , GFP_KERNEL ) ;
if ( ! midi_devs [ devc - > my_dev ] ) {
printk ( KERN_ERR " uart401: Failed to allocate memory \n " ) ;
goto cleanup_unload_mididev ;
}
memcpy ( midi_devs [ devc - > my_dev ] , & uart401_operations , sizeof ( struct midi_operations ) ) ;
if ( owner )
midi_devs [ devc - > my_dev ] - > owner = owner ;
midi_devs [ devc - > my_dev ] - > devc = devc ;
midi_devs [ devc - > my_dev ] - > converter = kmalloc ( sizeof ( struct synth_operations ) , GFP_KERNEL ) ;
if ( ! midi_devs [ devc - > my_dev ] - > converter ) {
printk ( KERN_WARNING " uart401: Failed to allocate memory \n " ) ;
goto cleanup_midi_devs ;
}
memcpy ( midi_devs [ devc - > my_dev ] - > converter , & std_midi_synth , sizeof ( struct synth_operations ) ) ;
strcpy ( midi_devs [ devc - > my_dev ] - > info . name , name ) ;
midi_devs [ devc - > my_dev ] - > converter - > id = " UART401 " ;
midi_devs [ devc - > my_dev ] - > converter - > midi_dev = devc - > my_dev ;
if ( owner )
midi_devs [ devc - > my_dev ] - > converter - > owner = owner ;
hw_config - > slots [ 4 ] = devc - > my_dev ;
sequencer_init ( ) ;
devc - > opened = 0 ;
return 1 ;
cleanup_midi_devs :
kfree ( midi_devs [ devc - > my_dev ] ) ;
cleanup_unload_mididev :
sound_unload_mididev ( devc - > my_dev ) ;
cleanup_irq :
if ( ! devc - > share_irq )
free_irq ( devc - > irq , devc ) ;
cleanup_devc :
kfree ( devc ) ;
cleanup_region :
release_region ( hw_config - > io_base , 4 ) ;
return 0 ;
}
void unload_uart401 ( struct address_info * hw_config )
{
uart401_devc * devc ;
int n = hw_config - > slots [ 4 ] ;
/* Not set up */
if ( n = = - 1 | | midi_devs [ n ] = = NULL )
return ;
/* Not allocated (erm ??) */
devc = midi_devs [ hw_config - > slots [ 4 ] ] - > devc ;
if ( devc = = NULL )
return ;
reset_uart401 ( devc ) ;
release_region ( hw_config - > io_base , 4 ) ;
if ( ! devc - > share_irq )
free_irq ( devc - > irq , devc ) ;
if ( devc )
{
kfree ( midi_devs [ devc - > my_dev ] - > converter ) ;
kfree ( midi_devs [ devc - > my_dev ] ) ;
kfree ( devc ) ;
devc = NULL ;
}
/* This kills midi_devs[x] */
sound_unload_mididev ( hw_config - > slots [ 4 ] ) ;
}
EXPORT_SYMBOL ( probe_uart401 ) ;
EXPORT_SYMBOL ( unload_uart401 ) ;
EXPORT_SYMBOL ( uart401intr ) ;
static struct address_info cfg_mpu ;
static int io = - 1 ;
static int irq = - 1 ;
module_param ( io , int , 0444 ) ;
module_param ( irq , int , 0444 ) ;
static int __init init_uart401 ( void )
{
cfg_mpu . irq = irq ;
cfg_mpu . io_base = io ;
/* Can be loaded either for module use or to provide functions
to others */
if ( cfg_mpu . io_base ! = - 1 & & cfg_mpu . irq ! = - 1 ) {
printk ( KERN_INFO " MPU-401 UART driver Copyright (C) Hannu Savolainen 1993-1997 " ) ;
if ( ! probe_uart401 ( & cfg_mpu , THIS_MODULE ) )
return - ENODEV ;
}
return 0 ;
}
static void __exit cleanup_uart401 ( void )
{
if ( cfg_mpu . io_base ! = - 1 & & cfg_mpu . irq ! = - 1 )
unload_uart401 ( & cfg_mpu ) ;
}
module_init ( init_uart401 ) ;
module_exit ( cleanup_uart401 ) ;
# ifndef MODULE
static int __init setup_uart401 ( char * str )
{
/* io, irq */
int ints [ 3 ] ;
str = get_options ( str , ARRAY_SIZE ( ints ) , ints ) ;
io = ints [ 1 ] ;
irq = ints [ 2 ] ;
return 1 ;
}
__setup ( " uart401= " , setup_uart401 ) ;
# endif
MODULE_LICENSE ( " GPL " ) ;