[funini.com] -> [kei@sodan] -> Kernel Reading

root/sound/oss/uart401.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. uart401_status
  2. uart401_cmd
  3. uart401_read
  4. uart401_write
  5. uart401_input_loop
  6. uart401intr
  7. uart401_open
  8. uart401_close
  9. uart401_out
  10. uart401_start_read
  11. uart401_end_read
  12. uart401_kick
  13. uart401_buffer_status
  14. enter_uart_mode
  15. reset_uart401
  16. probe_uart401
  17. unload_uart401
  18. init_uart401
  19. cleanup_uart401
  20. setup_uart401

/*
 * 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/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] = 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");

/* [<][>][^][v][top][bottom][index][help] */

[funini.com] -> [kei@sodan] -> Kernel Reading