diff options
author | swallace <swallace@FreeBSD.org> | 1994-10-01 02:17:17 +0000 |
---|---|---|
committer | swallace <swallace@FreeBSD.org> | 1994-10-01 02:17:17 +0000 |
commit | 3b7740a11ff2432cc8bbcfa2524ebc859cc4b643 (patch) | |
tree | 09d7913c895c85fdedc9a099ad0b7ac3ee4b3ef1 /sys/i386/isa/sound/midibuf.c | |
parent | c2da26491490b8f15ad302a5fdf2a9532a07df5d (diff) | |
download | FreeBSD-src-3b7740a11ff2432cc8bbcfa2524ebc859cc4b643.zip FreeBSD-src-3b7740a11ff2432cc8bbcfa2524ebc859cc4b643.tar.gz |
Merged in changes to Hannu Savolainen's VoxWare sound drivers, version 2.9.
Diffstat (limited to 'sys/i386/isa/sound/midibuf.c')
-rw-r--r-- | sys/i386/isa/sound/midibuf.c | 370 |
1 files changed, 345 insertions, 25 deletions
diff --git a/sys/i386/isa/sound/midibuf.c b/sys/i386/isa/sound/midibuf.c index 399fdfd..74067b6 100644 --- a/sys/i386/isa/sound/midibuf.c +++ b/sys/i386/isa/sound/midibuf.c @@ -1,9 +1,7 @@ /* * sound/midibuf.c * - * Device file manager for /dev/midi - * - * NOTE! This part of the driver is currently just a stub. + * Device file manager for /dev/midi# * * Copyright by Hannu Savolainen 1993 * @@ -27,94 +25,416 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id$ + * $Id: midibuf.c,v 1.5 1994/08/02 07:40:10 davidg Exp $ */ #include "sound_config.h" -#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_MPU401) +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_MIDI) -#if 0 -#include "midiioctl.h" -#include "midivar.h" -#endif +/* + * 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) -static int midibuf_busy = 0; +#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 (midibuf_busy) - return RET_ERROR (EBUSY); + if (num_midis > MAX_MIDI_DEV) + { + printk ("Sound: FATAL ERROR: Too many midi interfaces\n"); + num_midis = MAX_MIDI_DEV; + } - if (!mpu401_dev) + if (dev < 0 || dev >= num_midis) { - printk ("Midi: MPU-401 compatible Midi interface not present\n"); + printk ("Sound: Nonexistent MIDI interface %d\n", dev); return RET_ERROR (ENXIO); } - if ((err = midi_devs[mpu401_dev]->open (mpu401_dev, mode, NULL, NULL)) < 0) - return err; + /* + * 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)); - midibuf_busy = 1; + 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 RET_ERROR (ENXIO); + return err; } void MIDIbuf_release (int dev, struct fileinfo *file) { int mode; + unsigned long flags; dev = dev >> 4; mode = file->mode & O_ACCMODE; - midi_devs[mpu401_dev]->close (mpu401_dev); - midibuf_busy = 0; + 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; - return count; + 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; - return RET_ERROR (EIO); + 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[0]->ioctl (dev, cmd, arg); + return midi_devs[dev]->ioctl (dev, cmd, arg); } } -void -MIDIbuf_bytes_received (int dev, unsigned char *buf, int count) +#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) { |