Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / sound / oss / sound_timer.c
diff --git a/kernel/sound/oss/sound_timer.c b/kernel/sound/oss/sound_timer.c
new file mode 100644 (file)
index 0000000..8021c85
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * sound/oss/sound_timer.c
+ */
+/*
+ * 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/string.h>
+#include <linux/spinlock.h>
+
+#include "sound_config.h"
+
+static volatile int initialized, opened, tmr_running;
+static volatile time_t tmr_offs, tmr_ctr;
+static volatile unsigned long ticks_offs;
+static volatile int curr_tempo, curr_timebase;
+static volatile unsigned long curr_ticks;
+static volatile unsigned long next_event_time;
+static unsigned long prev_event_time;
+static volatile unsigned long usecs_per_tmr;   /* Length of the current interval */
+
+static struct sound_lowlev_timer *tmr;
+static DEFINE_SPINLOCK(lock);
+
+static unsigned long tmr2ticks(int tmr_value)
+{
+       /*
+        *    Convert timer ticks to MIDI ticks
+        */
+
+       unsigned long tmp;
+       unsigned long scale;
+
+       tmp = tmr_value * usecs_per_tmr;        /* Convert to usecs */
+       scale = (60 * 1000000) / (curr_tempo * curr_timebase);  /* usecs per MIDI tick */
+       return (tmp + (scale / 2)) / scale;
+}
+
+void reprogram_timer(void)
+{
+       unsigned long   usecs_per_tick;
+
+       /*
+        *      The user is changing the timer rate before setting a timer
+        *      slap, bad bad not allowed.
+        */
+        
+       if(!tmr)
+               return;
+               
+       usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase);
+
+       /*
+        * Don't kill the system by setting too high timer rate
+        */
+       if (usecs_per_tick < 2000)
+               usecs_per_tick = 2000;
+
+       usecs_per_tmr = tmr->tmr_start(tmr->dev, usecs_per_tick);
+}
+
+void sound_timer_syncinterval(unsigned int new_usecs)
+{
+       /*
+        *    This routine is called by the hardware level if
+        *      the clock frequency has changed for some reason.
+        */
+       tmr_offs = tmr_ctr;
+       ticks_offs += tmr2ticks(tmr_ctr);
+       tmr_ctr = 0;
+       usecs_per_tmr = new_usecs;
+}
+EXPORT_SYMBOL(sound_timer_syncinterval);
+
+static void tmr_reset(void)
+{
+       unsigned long   flags;
+
+       spin_lock_irqsave(&lock,flags);
+       tmr_offs = 0;
+       ticks_offs = 0;
+       tmr_ctr = 0;
+       next_event_time = (unsigned long) -1;
+       prev_event_time = 0;
+       curr_ticks = 0;
+       spin_unlock_irqrestore(&lock,flags);
+}
+
+static int timer_open(int dev, int mode)
+{
+       if (opened)
+               return -EBUSY;
+       tmr_reset();
+       curr_tempo = 60;
+       curr_timebase = 100;
+       opened = 1;
+       reprogram_timer();
+       return 0;
+}
+
+static void timer_close(int dev)
+{
+       opened = tmr_running = 0;
+       tmr->tmr_disable(tmr->dev);
+}
+
+static int timer_event(int dev, unsigned char *event)
+{
+       unsigned char cmd = event[1];
+       unsigned long parm = *(int *) &event[4];
+
+       switch (cmd)
+       {
+               case TMR_WAIT_REL:
+                       parm += prev_event_time;
+               case TMR_WAIT_ABS:
+                       if (parm > 0)
+                       {
+                               long time;
+
+                               if (parm <= curr_ticks) /* It's the time */
+                                       return TIMER_NOT_ARMED;
+                               time = parm;
+                               next_event_time = prev_event_time = time;
+                               return TIMER_ARMED;
+                       }
+                       break;
+
+               case TMR_START:
+                       tmr_reset();
+                       tmr_running = 1;
+                       reprogram_timer();
+                       break;
+
+               case TMR_STOP:
+                       tmr_running = 0;
+                       break;
+
+               case TMR_CONTINUE:
+                       tmr_running = 1;
+                       reprogram_timer();
+                       break;
+
+               case TMR_TEMPO:
+                       if (parm)
+                       {
+                               if (parm < 8)
+                                       parm = 8;
+                               if (parm > 250)
+                                       parm = 250;
+                               tmr_offs = tmr_ctr;
+                               ticks_offs += tmr2ticks(tmr_ctr);
+                               tmr_ctr = 0;
+                               curr_tempo = parm;
+                               reprogram_timer();
+                       }
+                       break;
+
+               case TMR_ECHO:
+                       seq_copy_to_input(event, 8);
+                       break;
+
+               default:;
+       }
+       return TIMER_NOT_ARMED;
+}
+
+static unsigned long timer_get_time(int dev)
+{
+       if (!opened)
+               return 0;
+       return curr_ticks;
+}
+
+static int timer_ioctl(int dev, unsigned int cmd, void __user *arg)
+{
+       int __user *p = arg;
+       int val;
+
+       switch (cmd) 
+       {
+               case SNDCTL_TMR_SOURCE:
+                       val = TMR_INTERNAL;
+                       break;
+
+               case SNDCTL_TMR_START:
+                       tmr_reset();
+                       tmr_running = 1;
+                       return 0;
+               
+               case SNDCTL_TMR_STOP:
+                       tmr_running = 0;
+                       return 0;
+
+               case SNDCTL_TMR_CONTINUE:
+                       tmr_running = 1;
+                       return 0;
+
+               case SNDCTL_TMR_TIMEBASE:
+                       if (get_user(val, p))
+                               return -EFAULT;
+                       if (val) 
+                       {
+                               if (val < 1)
+                                       val = 1;
+                               if (val > 1000)
+                                       val = 1000;
+                               curr_timebase = val;
+                       }
+                       val = curr_timebase;
+                       break;
+
+               case SNDCTL_TMR_TEMPO:
+                       if (get_user(val, p))
+                               return -EFAULT;
+                       if (val) 
+                       {
+                               if (val < 8)
+                                       val = 8;
+                               if (val > 250)
+                                       val = 250;
+                               tmr_offs = tmr_ctr;
+                               ticks_offs += tmr2ticks(tmr_ctr);
+                               tmr_ctr = 0;
+                               curr_tempo = val;
+                               reprogram_timer();
+                       }
+                       val = curr_tempo;
+                       break;
+
+               case SNDCTL_SEQ_CTRLRATE:
+                       if (get_user(val, p))
+                               return -EFAULT;
+                       if (val != 0)   /* Can't change */
+                               return -EINVAL;
+                       val = ((curr_tempo * curr_timebase) + 30) / 60;
+                       break;
+               
+               case SNDCTL_SEQ_GETTIME:
+                       val = curr_ticks;
+                       break;
+               
+               case SNDCTL_TMR_METRONOME:
+               default:
+                       return -EINVAL;
+       }
+       return put_user(val, p);
+}
+
+static void timer_arm(int dev, long time)
+{
+       if (time < 0)
+               time = curr_ticks + 1;
+       else if (time <= curr_ticks)    /* It's the time */
+               return;
+
+       next_event_time = prev_event_time = time;
+       return;
+}
+
+static struct sound_timer_operations sound_timer =
+{
+       .owner          = THIS_MODULE,
+       .info           = {"Sound Timer", 0},
+       .priority       = 1,    /* Priority */
+       .devlink        = 0,    /* Local device link */
+       .open           = timer_open,
+       .close          = timer_close,
+       .event          = timer_event,
+       .get_time       = timer_get_time,
+       .ioctl          = timer_ioctl,
+       .arm_timer      = timer_arm
+};
+
+void sound_timer_interrupt(void)
+{
+       unsigned long flags;
+       
+       if (!opened)
+               return;
+
+       tmr->tmr_restart(tmr->dev);
+
+       if (!tmr_running)
+               return;
+
+       spin_lock_irqsave(&lock,flags);
+       tmr_ctr++;
+       curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
+
+       if (curr_ticks >= next_event_time)
+       {
+               next_event_time = (unsigned long) -1;
+               sequencer_timer(0);
+       }
+       spin_unlock_irqrestore(&lock,flags);
+}
+EXPORT_SYMBOL(sound_timer_interrupt);
+
+void  sound_timer_init(struct sound_lowlev_timer *t, char *name)
+{
+       int n;
+
+       if (initialized)
+       {
+               if (t->priority <= tmr->priority)
+                       return; /* There is already a similar or better timer */
+               tmr = t;
+               return;
+       }
+       initialized = 1;
+       tmr = t;
+
+       n = sound_alloc_timerdev();
+       if (n == -1)
+               n = 0;          /* Overwrite the system timer */
+       strlcpy(sound_timer.info.name, name, sizeof(sound_timer.info.name));
+       sound_timer_devs[n] = &sound_timer;
+}
+EXPORT_SYMBOL(sound_timer_init);
+