linux-sg2042/sound/oss/uart401.c

486 lines
10 KiB
C

/*
* sound/oss/uart401.c
*
* 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/slab.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)
{
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] = kmemdup(&uart401_operations,
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;
}
if (owner)
midi_devs[devc->my_dev]->owner = owner;
midi_devs[devc->my_dev]->devc = devc;
midi_devs[devc->my_dev]->converter = kmemdup(&std_midi_synth,
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;
}
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");