summaryrefslogtreecommitdiffstats
path: root/kernel/sound/oss/midibuf.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/sound/oss/midibuf.c')
-rw-r--r--kernel/sound/oss/midibuf.c425
1 files changed, 425 insertions, 0 deletions
diff --git a/kernel/sound/oss/midibuf.c b/kernel/sound/oss/midibuf.c
new file mode 100644
index 000000000..8f45cd999
--- /dev/null
+++ b/kernel/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);
+