diff options
Diffstat (limited to 'sound/oss/midibuf.c')
-rw-r--r-- | sound/oss/midibuf.c | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/sound/oss/midibuf.c b/sound/oss/midibuf.c new file mode 100644 index 000000000..8f45cd999 --- /dev/null +++ b/sound/oss/midibuf.c @@ -0,0 +1,425 @@ +/* + * sound/oss/midibuf.c + * + * Device file manager for /dev/midi# + */ +/* + * 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. + */ +/* + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + */ +#include <linux/stddef.h> +#include <linux/kmod.h> +#include <linux/spinlock.h> +#define MIDIBUF_C + +#include "sound_config.h" + + +/* + * Don't make MAX_QUEUE_SIZE larger than 4000 + */ + +#define MAX_QUEUE_SIZE 4000 + +static wait_queue_head_t midi_sleeper[MAX_MIDI_DEV]; +static wait_queue_head_t input_sleeper[MAX_MIDI_DEV]; + +struct midi_buf +{ + int len, head, tail; + unsigned char queue[MAX_QUEUE_SIZE]; +}; + +struct midi_parms +{ + long 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); + + +static DEFINE_TIMER(poll_timer, midi_poll, 0, 0); + +static volatile int open_devs; +static DEFINE_SPINLOCK(lock); + +#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; \ + spin_lock_irqsave(&lock, flags); \ + q->queue[q->tail] = (data); \ + q->len++; q->tail = (q->tail+1) % MAX_QUEUE_SIZE; \ + spin_unlock_irqrestore(&lock, flags); \ + } + +#define REMOVE_BYTE(q, data) \ + if (DATA_AVAIL(q)) \ + { \ + unsigned long flags; \ + spin_lock_irqsave(&lock, flags); \ + data = q->queue[q->head]; \ + q->len--; q->head = (q->head+1) % MAX_QUEUE_SIZE; \ + spin_unlock_irqrestore(&lock, flags); \ + } + +static void drain_midi_queue(int dev) +{ + + /* + * Give the Midi driver time to drain its output queues + */ + + if (midi_devs[dev]->buffer_status != NULL) + wait_event_interruptible_timeout(midi_sleeper[dev], + !midi_devs[dev]->buffer_status(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); + wake_up(&input_sleeper[dev]); + } +} + +static void midi_output_intr(int dev) +{ + /* + * Currently NOP + */ +} + +static void midi_poll(unsigned long dummy) +{ + unsigned long flags; + int dev; + + spin_lock_irqsave(&lock, flags); + if (open_devs) + { + for (dev = 0; dev < num_midis; dev++) + if (midi_devs[dev] != NULL && midi_out_buf[dev] != NULL) + { + while (DATA_AVAIL(midi_out_buf[dev])) + { + int ok; + int c = midi_out_buf[dev]->queue[midi_out_buf[dev]->head]; + + spin_unlock_irqrestore(&lock,flags);/* Give some time to others */ + ok = midi_devs[dev]->outputc(dev, c); + spin_lock_irqsave(&lock, flags); + if (!ok) + break; + 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) + wake_up(&midi_sleeper[dev]); + } + poll_timer.expires = (1) + jiffies; + add_timer(&poll_timer); + /* + * Come back later + */ + } + spin_unlock_irqrestore(&lock, flags); +} + +int MIDIbuf_open(int dev, struct file *file) +{ + int mode, err; + + dev = dev >> 4; + mode = translate_mode(file); + + if (num_midis > MAX_MIDI_DEV) + { + printk(KERN_ERR "midi: Too many midi interfaces\n"); + num_midis = MAX_MIDI_DEV; + } + if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL) + return -ENXIO; + /* + * Interrupts disabled. Be careful + */ + + module_put(midi_devs[dev]->owner); + + if ((err = midi_devs[dev]->open(dev, mode, + midi_input_intr, midi_output_intr)) < 0) + return err; + + parms[dev].prech_timeout = MAX_SCHEDULE_TIMEOUT; + midi_in_buf[dev] = vmalloc(sizeof(struct midi_buf)); + + if (midi_in_buf[dev] == NULL) + { + printk(KERN_WARNING "midi: Can't allocate buffer\n"); + midi_devs[dev]->close(dev); + return -EIO; + } + midi_in_buf[dev]->len = midi_in_buf[dev]->head = midi_in_buf[dev]->tail = 0; + + midi_out_buf[dev] = vmalloc(sizeof(struct midi_buf)); + + if (midi_out_buf[dev] == NULL) + { + printk(KERN_WARNING "midi: Can't allocate buffer\n"); + midi_devs[dev]->close(dev); + vfree(midi_in_buf[dev]); + midi_in_buf[dev] = NULL; + return -EIO; + } + midi_out_buf[dev]->len = midi_out_buf[dev]->head = midi_out_buf[dev]->tail = 0; + open_devs++; + + init_waitqueue_head(&midi_sleeper[dev]); + init_waitqueue_head(&input_sleeper[dev]); + + if (open_devs < 2) /* This was first open */ + { + poll_timer.expires = 1 + jiffies; + add_timer(&poll_timer); /* Start polling */ + } + return err; +} + +void MIDIbuf_release(int dev, struct file *file) +{ + int mode; + + dev = dev >> 4; + mode = translate_mode(file); + + if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL) + return; + + /* + * Wait until the queue is empty + */ + + if (mode != OPEN_READ) + { + midi_devs[dev]->outputc(dev, 0xfe); /* + * Active sensing to shut the + * devices + */ + + wait_event_interruptible(midi_sleeper[dev], + !DATA_AVAIL(midi_out_buf[dev])); + /* + * Sync + */ + + drain_midi_queue(dev); /* + * Ensure the output queues are empty + */ + } + + midi_devs[dev]->close(dev); + + open_devs--; + if (open_devs == 0) + del_timer_sync(&poll_timer); + vfree(midi_in_buf[dev]); + vfree(midi_out_buf[dev]); + midi_in_buf[dev] = NULL; + midi_out_buf[dev] = NULL; + + module_put(midi_devs[dev]->owner); +} + +int MIDIbuf_write(int dev, struct file *file, const char __user *buf, int count) +{ + int c, n, i; + unsigned char tmp_data; + + dev = dev >> 4; + + if (!count) + return 0; + + c = 0; + + while (c < count) + { + n = SPACE_AVAIL(midi_out_buf[dev]); + + if (n == 0) { /* + * No space just now. + */ + + if (file->f_flags & O_NONBLOCK) { + c = -EAGAIN; + goto out; + } + + if (wait_event_interruptible(midi_sleeper[dev], + SPACE_AVAIL(midi_out_buf[dev]))) + { + c = -EINTR; + goto out; + } + n = SPACE_AVAIL(midi_out_buf[dev]); + } + if (n > (count - c)) + n = count - c; + + for (i = 0; i < n; i++) + { + /* BROKE BROKE BROKE - CAN'T DO THIS WITH CLI !! */ + /* yes, think the same, so I removed the cli() brackets + QUEUE_BYTE is protected against interrupts */ + if (copy_from_user((char *) &tmp_data, &(buf)[c], 1)) { + c = -EFAULT; + goto out; + } + QUEUE_BYTE(midi_out_buf[dev], tmp_data); + c++; + } + } +out: + return c; +} + + +int MIDIbuf_read(int dev, struct file *file, char __user *buf, int count) +{ + int n, c = 0; + unsigned char tmp_data; + + dev = dev >> 4; + + if (!DATA_AVAIL(midi_in_buf[dev])) { /* + * No data yet, wait + */ + if (file->f_flags & O_NONBLOCK) { + c = -EAGAIN; + goto out; + } + wait_event_interruptible_timeout(input_sleeper[dev], + DATA_AVAIL(midi_in_buf[dev]), + parms[dev].prech_timeout); + + if (signal_pending(current)) + c = -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) + { + char *fixit; + REMOVE_BYTE(midi_in_buf[dev], tmp_data); + fixit = (char *) &tmp_data; + /* BROKE BROKE BROKE */ + /* yes removed the cli() brackets again + should q->len,tail&head be atomic_t? */ + if (copy_to_user(&(buf)[c], fixit, 1)) { + c = -EFAULT; + goto out; + } + c++; + } + } +out: + return c; +} + +int MIDIbuf_ioctl(int dev, struct file *file, + unsigned int cmd, void __user *arg) +{ + int val; + + dev = dev >> 4; + + if (((cmd >> 8) & 0xff) == 'C') + { + if (midi_devs[dev]->coproc) /* Coprocessor ioctl */ + return midi_devs[dev]->coproc->ioctl(midi_devs[dev]->coproc->devc, cmd, arg, 0); +/* printk("/dev/midi%d: No coprocessor for this device\n", dev);*/ + return -ENXIO; + } + else + { + switch (cmd) + { + case SNDCTL_MIDI_PRETIME: + if (get_user(val, (int __user *)arg)) + return -EFAULT; + if (val < 0) + val = 0; + val = (HZ * val) / 10; + parms[dev].prech_timeout = val; + return put_user(val, (int __user *)arg); + + default: + if (!midi_devs[dev]->ioctl) + return -EINVAL; + return midi_devs[dev]->ioctl(dev, cmd, arg); + } + } +} + +/* No kernel lock - fine */ +unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait) +{ + unsigned int mask = 0; + + dev = dev >> 4; + + /* input */ + poll_wait(file, &input_sleeper[dev], wait); + if (DATA_AVAIL(midi_in_buf[dev])) + mask |= POLLIN | POLLRDNORM; + + /* output */ + poll_wait(file, &midi_sleeper[dev], wait); + if (!SPACE_AVAIL(midi_out_buf[dev])) + mask |= POLLOUT | POLLWRNORM; + + return mask; +} + + +int MIDIbuf_avail(int dev) +{ + if (midi_in_buf[dev]) + return DATA_AVAIL (midi_in_buf[dev]); + return 0; +} +EXPORT_SYMBOL(MIDIbuf_avail); + |