123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478 |
- /*
- * 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"
- 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;
- };
- #define DATAPORT (devc->base)
- #define COMDPORT (devc->base+1)
- #define STATPORT (devc->base+1)
- static int uart401_status(struct 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(struct uart401_devc *devc, unsigned char cmd)
- {
- outb((cmd), COMDPORT);
- }
- static int uart401_read(struct uart401_devc *devc)
- {
- return inb(DATAPORT);
- }
- static void uart401_write(struct 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(struct uart401_devc *devc);
- static void enter_uart_mode(struct uart401_devc *devc);
- static void uart401_input_loop(struct 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 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)
- )
- {
- struct uart401_devc *devc = (struct 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)
- {
- struct uart401_devc *devc = (struct 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;
- struct uart401_devc *devc = (struct 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(struct 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(struct 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;
- }
- }
- }
- /* Flush input before enabling interrupts */
- if (ok)
- uart401_input_loop(devc);
- else
- DDB(printk("Reset UART401 failed - No hardware detected.\n"));
- return ok;
- }
- int probe_uart401(struct address_info *hw_config, struct module *owner)
- {
- struct 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(struct 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)
- {
- struct 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);
- kfree(midi_devs[devc->my_dev]->converter);
- kfree(midi_devs[devc->my_dev]);
- kfree(devc);
- /* 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_hw(io, int, ioport, 0444);
- module_param_hw(irq, int, irq, 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");
|