diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1994-11-28 11:59:19 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1994-11-28 11:59:19 +0000 |
commit | 1513ff9b7899ab588401c89db0e99903dbf5f886 (patch) | |
tree | f69cc81a940a502ea23d664c3ffb2d215a479667 /drivers/sound/midibuf.c |
Import of Linus's Linux 1.1.68
Diffstat (limited to 'drivers/sound/midibuf.c')
-rw-r--r-- | drivers/sound/midibuf.c | 443 |
1 files changed, 443 insertions, 0 deletions
diff --git a/drivers/sound/midibuf.c b/drivers/sound/midibuf.c new file mode 100644 index 000000000..ae0ddc3ea --- /dev/null +++ b/drivers/sound/midibuf.c @@ -0,0 +1,443 @@ +/* + * sound/midibuf.c + * + * Device file manager for /dev/midi# + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "sound_config.h" + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_MIDI) + +/* + * Don't make MAX_QUEUE_SIZE larger than 4000 + */ + +#define MAX_QUEUE_SIZE 4000 + +DEFINE_WAIT_QUEUES (midi_sleeper[MAX_MIDI_DEV], midi_sleep_flag[MAX_MIDI_DEV]); +DEFINE_WAIT_QUEUES (input_sleeper[MAX_MIDI_DEV], input_sleep_flag[MAX_MIDI_DEV]); + +struct midi_buf + { + int len, head, tail; + unsigned char queue[MAX_QUEUE_SIZE]; + }; + +struct midi_parms + { + int prech_timeout; /* + * Timeout before the first ch + */ + }; + +static struct midi_buf *midi_out_buf[MAX_MIDI_DEV] = +{NULL}; +static struct midi_buf *midi_in_buf[MAX_MIDI_DEV] = +{NULL}; +static struct midi_parms parms[MAX_MIDI_DEV]; + +static void midi_poll (unsigned long dummy); + +DEFINE_TIMER (poll_timer, midi_poll); +static volatile int open_devs = 0; + +#define DATA_AVAIL(q) (q->len) +#define SPACE_AVAIL(q) (MAX_QUEUE_SIZE - q->len) + +#define QUEUE_BYTE(q, data) \ + if (SPACE_AVAIL(q)) \ + { \ + unsigned long flags; \ + DISABLE_INTR(flags); \ + q->queue[q->tail] = (data); \ + q->len++; q->tail = (q->tail+1) % MAX_QUEUE_SIZE; \ + RESTORE_INTR(flags); \ + } + +#define REMOVE_BYTE(q, data) \ + if (DATA_AVAIL(q)) \ + { \ + unsigned long flags; \ + DISABLE_INTR(flags); \ + data = q->queue[q->head]; \ + q->len--; q->head = (q->head+1) % MAX_QUEUE_SIZE; \ + RESTORE_INTR(flags); \ + } + +void +drain_midi_queue (int dev) +{ + + /* + * Give the Midi driver time to drain its output queues + */ + + if (midi_devs[dev]->buffer_status != NULL) + while (!PROCESS_ABORTING (midi_sleeper[dev], midi_sleep_flag[dev]) && + midi_devs[dev]->buffer_status (dev)) + DO_SLEEP (midi_sleeper[dev], midi_sleep_flag[dev], HZ / 10); +} + +static void +midi_input_intr (int dev, unsigned char data) +{ + if (midi_in_buf[dev] == NULL) + return; + + if (data == 0xfe) /* + * Active sensing + */ + return; /* + * Ignore + */ + + if (SPACE_AVAIL (midi_in_buf[dev])) + { + QUEUE_BYTE (midi_in_buf[dev], data); + if (SOMEONE_WAITING (input_sleeper[dev], input_sleep_flag[dev])) + WAKE_UP (input_sleeper[dev], input_sleep_flag[dev]); + } + +} + +static void +midi_output_intr (int dev) +{ + /* + * Currently NOP + */ +} + +static void +midi_poll (unsigned long dummy) +{ + unsigned long flags; + int dev; + + DISABLE_INTR (flags); + if (open_devs) + { + for (dev = 0; dev < num_midis; dev++) + if (midi_out_buf[dev] != NULL) + { + while (DATA_AVAIL (midi_out_buf[dev]) && + midi_devs[dev]->putc (dev, + midi_out_buf[dev]->queue[midi_out_buf[dev]->head])) + { + midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE; + midi_out_buf[dev]->len--; + } + + if (DATA_AVAIL (midi_out_buf[dev]) < 100 && + SOMEONE_WAITING (midi_sleeper[dev], midi_sleep_flag[dev])) + WAKE_UP (midi_sleeper[dev], midi_sleep_flag[dev]); + } + ACTIVATE_TIMER (poll_timer, midi_poll, 1); /* + * Come back later + */ + } + RESTORE_INTR (flags); +} + +int +MIDIbuf_open (int dev, struct fileinfo *file) +{ + int mode, err; + unsigned long flags; + + dev = dev >> 4; + mode = file->mode & O_ACCMODE; + + if (num_midis > MAX_MIDI_DEV) + { + printk ("Sound: FATAL ERROR: Too many midi interfaces\n"); + num_midis = MAX_MIDI_DEV; + } + + if (dev < 0 || dev >= num_midis) + { + printk ("Sound: Nonexistent MIDI interface %d\n", dev); + return RET_ERROR (ENXIO); + } + + /* + * Interrupts disabled. Be careful + */ + + DISABLE_INTR (flags); + if ((err = midi_devs[dev]->open (dev, mode, + midi_input_intr, midi_output_intr)) < 0) + { + RESTORE_INTR (flags); + return err; + } + + parms[dev].prech_timeout = 0; + + RESET_WAIT_QUEUE (midi_sleeper[dev], midi_sleep_flag[dev]); + RESET_WAIT_QUEUE (input_sleeper[dev], input_sleep_flag[dev]); + + midi_in_buf[dev] = (struct midi_buf *) KERNEL_MALLOC (sizeof (struct midi_buf)); + + if (midi_in_buf[dev] == NULL) + { + printk ("midi: Can't allocate buffer\n"); + midi_devs[dev]->close (dev); + RESTORE_INTR (flags); + return RET_ERROR (EIO); + } + midi_in_buf[dev]->len = midi_in_buf[dev]->head = midi_in_buf[dev]->tail = 0; + + midi_out_buf[dev] = (struct midi_buf *) KERNEL_MALLOC (sizeof (struct midi_buf)); + + if (midi_out_buf[dev] == NULL) + { + printk ("midi: Can't allocate buffer\n"); + midi_devs[dev]->close (dev); + KERNEL_FREE (midi_in_buf[dev]); + midi_in_buf[dev] = NULL; + RESTORE_INTR (flags); + return RET_ERROR (EIO); + } + midi_out_buf[dev]->len = midi_out_buf[dev]->head = midi_out_buf[dev]->tail = 0; + if (!open_devs) + ACTIVATE_TIMER (poll_timer, midi_poll, 1); /* + * Come back later + */ + open_devs++; + RESTORE_INTR (flags); + + return err; +} + +void +MIDIbuf_release (int dev, struct fileinfo *file) +{ + int mode; + unsigned long flags; + + dev = dev >> 4; + mode = file->mode & O_ACCMODE; + + DISABLE_INTR (flags); + + /* + * Wait until the queue is empty + */ + + if (mode != OPEN_READ) + { + midi_devs[dev]->putc (dev, 0xfe); /* + * Active sensing to shut the + * devices + */ + + while (!PROCESS_ABORTING (midi_sleeper[dev], midi_sleep_flag[dev]) && + DATA_AVAIL (midi_out_buf[dev])) + DO_SLEEP (midi_sleeper[dev], midi_sleep_flag[dev], 0); /* + * Sync + */ + + drain_midi_queue (dev); /* + * Ensure the output queues are empty + */ + } + + midi_devs[dev]->close (dev); + KERNEL_FREE (midi_in_buf[dev]); + KERNEL_FREE (midi_out_buf[dev]); + midi_in_buf[dev] = NULL; + midi_out_buf[dev] = NULL; + open_devs--; + RESTORE_INTR (flags); +} + +int +MIDIbuf_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + unsigned long flags; + int c, n, i; + unsigned char tmp_data; + + dev = dev >> 4; + + if (!count) + return 0; + + DISABLE_INTR (flags); + + c = 0; + + while (c < count) + { + n = SPACE_AVAIL (midi_out_buf[dev]); + + if (n == 0) /* + * No space just now. We have to sleep + */ + { + DO_SLEEP (midi_sleeper[dev], midi_sleep_flag[dev], 0); + if (PROCESS_ABORTING (midi_sleeper[dev], midi_sleep_flag[dev])) + { + RESTORE_INTR (flags); + return RET_ERROR (EINTR); + } + + n = SPACE_AVAIL (midi_out_buf[dev]); + } + + if (n > (count - c)) + n = count - c; + + for (i = 0; i < n; i++) + { + COPY_FROM_USER (&tmp_data, buf, c, 1); + QUEUE_BYTE (midi_out_buf[dev], tmp_data); + c++; + } + } + + RESTORE_INTR (flags); + + return c; +} + + +int +MIDIbuf_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count) +{ + int n, c = 0; + unsigned long flags; + unsigned char tmp_data; + + dev = dev >> 4; + + DISABLE_INTR (flags); + + if (!DATA_AVAIL (midi_in_buf[dev])) /* + * No data yet, wait + */ + { + DO_SLEEP (input_sleeper[dev], input_sleep_flag[dev], + parms[dev].prech_timeout); + if (PROCESS_ABORTING (input_sleeper[dev], input_sleep_flag[dev])) + c = RET_ERROR (EINTR); /* + * The user is getting restless + */ + } + + if (c == 0 && DATA_AVAIL (midi_in_buf[dev])) /* + * Got some bytes + */ + { + n = DATA_AVAIL (midi_in_buf[dev]); + if (n > count) + n = count; + c = 0; + + while (c < n) + { + REMOVE_BYTE (midi_in_buf[dev], tmp_data); + COPY_TO_USER (buf, c, &tmp_data, 1); + c++; + } + } + + RESTORE_INTR (flags); + + return c; +} + +int +MIDIbuf_ioctl (int dev, struct fileinfo *file, + unsigned int cmd, unsigned int arg) +{ + int val; + + dev = dev >> 4; + + switch (cmd) + { + + case SNDCTL_MIDI_PRETIME: + val = IOCTL_IN (arg); + if (val < 0) + val = 0; + + val = (HZ * val) / 10; + parms[dev].prech_timeout = val; + return IOCTL_OUT (arg, val); + break; + + default: + return midi_devs[dev]->ioctl (dev, cmd, arg); + } +} + +#ifdef ALLOW_SELECT +int +MIDIbuf_select (int dev, struct fileinfo *file, int sel_type, select_table * wait) +{ + dev = dev >> 4; + + switch (sel_type) + { + case SEL_IN: + if (!DATA_AVAIL (midi_in_buf[dev])) + { + input_sleep_flag[dev].mode = WK_SLEEP; + select_wait (&input_sleeper[dev], wait); + return 0; + } + return 1; + break; + + case SEL_OUT: + if (SPACE_AVAIL (midi_out_buf[dev])) + { + midi_sleep_flag[dev].mode = WK_SLEEP; + select_wait (&midi_sleeper[dev], wait); + return 0; + } + return 1; + break; + + case SEL_EX: + return 0; + } + + return 0; +} + +#endif /* ALLOW_SELECT */ + +long +MIDIbuf_init (long mem_start) +{ + return mem_start; +} + +#endif |