Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / sound / oss / midibuf.c
diff --git a/kernel/sound/oss/midibuf.c b/kernel/sound/oss/midibuf.c
new file mode 100644 (file)
index 0000000..8f45cd9
--- /dev/null
@@ -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);
+