diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
commit | beb116954b9b7f3bb56412b2494b562f02b864b1 (patch) | |
tree | 120e997879884e1b9d93b265221b939d2ef1ade1 /drivers/sound/uart401.c | |
parent | 908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff) |
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'drivers/sound/uart401.c')
-rw-r--r-- | drivers/sound/uart401.c | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/drivers/sound/uart401.c b/drivers/sound/uart401.c new file mode 100644 index 000000000..1d6abfb5c --- /dev/null +++ b/drivers/sound/uart401.c @@ -0,0 +1,453 @@ +/* + * sound/uart401.c + * + * MPU-401 UART driver (formerly uart401_midi.c) + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1996 + * + * 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. + */ +#include <linux/config.h> + + +#include "sound_config.h" + +#if defined(CONFIG_UART401) && defined(CONFIG_MIDI) + +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; + } +uart401_devc; + +static uart401_devc *detected_devc = NULL; +static uart401_devc *irq2devc[16] = +{NULL}; + +#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) +{ + while (input_avail (devc)) + { + 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); + } +} + +void +uart401intr (int irq, void *dev_id, struct pt_regs *dummy) +{ + uart401_devc *devc; + + if (irq < 1 || irq > 15) + return; + + devc = irq2devc[irq]; + + if (devc == NULL) + return; + + if (input_avail (devc)) + uart401_input_loop (devc); +} + +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; + } + + 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. + */ + + save_flags (flags); + cli (); + + if (input_avail (devc)) + uart401_input_loop (devc); + + restore_flags (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 ("MPU-401: 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 int +uart401_start_read (int dev) +{ + return 0; +} + +static int +uart401_end_read (int dev) +{ + return 0; +} + +static int +uart401_ioctl (int dev, unsigned cmd, caddr_t arg) +{ + return -EINVAL; +} + +static void +uart401_kick (int dev) +{ +} + +static 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 struct midi_operations uart401_operations = +{ + {"MPU-401 (UART) MIDI", 0, 0, SNDCARD_MPU401}, + &std_midi_synth, + {0}, + uart401_open, + uart401_close, + uart401_ioctl, + uart401_out, + uart401_start_read, + uart401_end_read, + uart401_kick, + NULL, + uart401_buffer_status, + NULL +}; + +static void +enter_uart_mode (uart401_devc * devc) +{ + int ok, timeout; + unsigned long flags; + + save_flags (flags); + cli (); + 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; + + restore_flags (flags); +} + +void +attach_uart401 (struct address_info *hw_config) +{ + uart401_devc *devc; + char *name = "MPU-401 (UART) MIDI"; + + if (hw_config->name) + name = hw_config->name; + + if (detected_devc == NULL) + return; + + + devc = (uart401_devc *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (uart401_devc))); + if (sound_nblocks < 1024) + sound_nblocks++;; + if (devc == NULL) + { + printk ("uart401: Can't allocate memory\n"); + return; + } + + memcpy ((char *) devc, (char *) detected_devc, sizeof (uart401_devc)); + detected_devc = NULL; + + devc->irq = hw_config->irq; + if (devc->irq < 0) + { + devc->share_irq = 1; + devc->irq *= -1; + } + else + devc->share_irq = 0; + + if (devc->irq < 1 || devc->irq > 15) + return; + + if (!devc->share_irq) + if (snd_set_irq_handler (devc->irq, uart401intr, "uart401", devc->osp) < 0) + { + printk ("uart401: Failed to allocate IRQ%d\n", devc->irq); + return; + } + + irq2devc[devc->irq] = devc; + devc->my_dev = num_midis; + + request_region (hw_config->io_base, 4, "SB MIDI"); + enter_uart_mode (devc); + + if (num_midis >= MAX_MIDI_DEV) + { + printk ("Sound: Too many midi devices detected\n"); + return; + } + + conf_printf (name, hw_config); + + std_midi_synth.midi_dev = devc->my_dev = num_midis; + + + midi_devs[num_midis] = (struct midi_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct midi_operations))); + + if (sound_nblocks < 1024) + sound_nblocks++;; + if (midi_devs[num_midis] == NULL) + { + printk ("uart401: Failed to allocate memory\n"); + return; + } + + memcpy ((char *) midi_devs[num_midis], (char *) &uart401_operations, + sizeof (struct midi_operations)); + + midi_devs[num_midis]->devc = devc; + + + midi_devs[num_midis]->converter = (struct synth_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct synth_operations))); + + if (sound_nblocks < 1024) + sound_nblocks++;; + + if (midi_devs[num_midis]->converter == NULL) + { + printk ("uart401: Failed to allocate memory\n"); + return; + } + + memcpy ((char *) midi_devs[num_midis]->converter, (char *) &std_midi_synth, + sizeof (struct synth_operations)); + + strcpy (midi_devs[num_midis]->info.name, name); + num_midis++; + devc->opened = 0; +} + +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) + { + DDB (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) +{ + int ok = 0; + + static uart401_devc hw_info; + uart401_devc *devc = &hw_info; + + DDB (printk ("Entered probe_uart401()\n")); + + detected_devc = NULL; + + if (check_region (hw_config->io_base, 4)) + return 0; + + 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; + + ok = reset_uart401 (devc); + + if (ok) + detected_devc = devc; + + return ok; +} + +void +unload_uart401 (struct address_info *hw_config) +{ + uart401_devc *devc; + + int irq = hw_config->irq; + + if (irq < 0) + irq *= -1; + + if (irq < 1 || irq > 15) + return; + + devc = irq2devc[irq]; + if (devc == NULL) + return; + + reset_uart401 (devc); + release_region (hw_config->io_base, 4); + + if (!devc->share_irq) + snd_release_irq (devc->irq); +} + + +#endif |