linux/sound/oss/sound_timer.c
<<
>>
Prefs
   1/*
   2 * sound/oss/sound_timer.c
   3 */
   4/*
   5 * Copyright (C) by Hannu Savolainen 1993-1997
   6 *
   7 * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
   8 * Version 2 (June 1991). See the "COPYING" file distributed with this software
   9 * for more info.
  10 */
  11/*
  12 * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
  13 */
  14#include <linux/string.h>
  15#include <linux/spinlock.h>
  16
  17#include "sound_config.h"
  18
  19static volatile int initialized, opened, tmr_running;
  20static volatile time_t tmr_offs, tmr_ctr;
  21static volatile unsigned long ticks_offs;
  22static volatile int curr_tempo, curr_timebase;
  23static volatile unsigned long curr_ticks;
  24static volatile unsigned long next_event_time;
  25static unsigned long prev_event_time;
  26static volatile unsigned long usecs_per_tmr;    /* Length of the current interval */
  27
  28static struct sound_lowlev_timer *tmr;
  29static spinlock_t lock;
  30
  31static unsigned long tmr2ticks(int tmr_value)
  32{
  33        /*
  34         *    Convert timer ticks to MIDI ticks
  35         */
  36
  37        unsigned long tmp;
  38        unsigned long scale;
  39
  40        tmp = tmr_value * usecs_per_tmr;        /* Convert to usecs */
  41        scale = (60 * 1000000) / (curr_tempo * curr_timebase);  /* usecs per MIDI tick */
  42        return (tmp + (scale / 2)) / scale;
  43}
  44
  45void reprogram_timer(void)
  46{
  47        unsigned long   usecs_per_tick;
  48
  49        /*
  50         *      The user is changing the timer rate before setting a timer
  51         *      slap, bad bad not allowed.
  52         */
  53         
  54        if(!tmr)
  55                return;
  56                
  57        usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase);
  58
  59        /*
  60         * Don't kill the system by setting too high timer rate
  61         */
  62        if (usecs_per_tick < 2000)
  63                usecs_per_tick = 2000;
  64
  65        usecs_per_tmr = tmr->tmr_start(tmr->dev, usecs_per_tick);
  66}
  67
  68void sound_timer_syncinterval(unsigned int new_usecs)
  69{
  70        /*
  71         *    This routine is called by the hardware level if
  72         *      the clock frequency has changed for some reason.
  73         */
  74        tmr_offs = tmr_ctr;
  75        ticks_offs += tmr2ticks(tmr_ctr);
  76        tmr_ctr = 0;
  77        usecs_per_tmr = new_usecs;
  78}
  79EXPORT_SYMBOL(sound_timer_syncinterval);
  80
  81static void tmr_reset(void)
  82{
  83        unsigned long   flags;
  84
  85        spin_lock_irqsave(&lock,flags);
  86        tmr_offs = 0;
  87        ticks_offs = 0;
  88        tmr_ctr = 0;
  89        next_event_time = (unsigned long) -1;
  90        prev_event_time = 0;
  91        curr_ticks = 0;
  92        spin_unlock_irqrestore(&lock,flags);
  93}
  94
  95static int timer_open(int dev, int mode)
  96{
  97        if (opened)
  98                return -EBUSY;
  99        tmr_reset();
 100        curr_tempo = 60;
 101        curr_timebase = 100;
 102        opened = 1;
 103        reprogram_timer();
 104        return 0;
 105}
 106
 107static void timer_close(int dev)
 108{
 109        opened = tmr_running = 0;
 110        tmr->tmr_disable(tmr->dev);
 111}
 112
 113static int timer_event(int dev, unsigned char *event)
 114{
 115        unsigned char cmd = event[1];
 116        unsigned long parm = *(int *) &event[4];
 117
 118        switch (cmd)
 119        {
 120                case TMR_WAIT_REL:
 121                        parm += prev_event_time;
 122                case TMR_WAIT_ABS:
 123                        if (parm > 0)
 124                        {
 125                                long time;
 126
 127                                if (parm <= curr_ticks) /* It's the time */
 128                                        return TIMER_NOT_ARMED;
 129                                time = parm;
 130                                next_event_time = prev_event_time = time;
 131                                return TIMER_ARMED;
 132                        }
 133                        break;
 134
 135                case TMR_START:
 136                        tmr_reset();
 137                        tmr_running = 1;
 138                        reprogram_timer();
 139                        break;
 140
 141                case TMR_STOP:
 142                        tmr_running = 0;
 143                        break;
 144
 145                case TMR_CONTINUE:
 146                        tmr_running = 1;
 147                        reprogram_timer();
 148                        break;
 149
 150                case TMR_TEMPO:
 151                        if (parm)
 152                        {
 153                                if (parm < 8)
 154                                        parm = 8;
 155                                if (parm > 250)
 156                                        parm = 250;
 157                                tmr_offs = tmr_ctr;
 158                                ticks_offs += tmr2ticks(tmr_ctr);
 159                                tmr_ctr = 0;
 160                                curr_tempo = parm;
 161                                reprogram_timer();
 162                        }
 163                        break;
 164
 165                case TMR_ECHO:
 166                        seq_copy_to_input(event, 8);
 167                        break;
 168
 169                default:;
 170        }
 171        return TIMER_NOT_ARMED;
 172}
 173
 174static unsigned long timer_get_time(int dev)
 175{
 176        if (!opened)
 177                return 0;
 178        return curr_ticks;
 179}
 180
 181static int timer_ioctl(int dev, unsigned int cmd, void __user *arg)
 182{
 183        int __user *p = arg;
 184        int val;
 185
 186        switch (cmd) 
 187        {
 188                case SNDCTL_TMR_SOURCE:
 189                        val = TMR_INTERNAL;
 190                        break;
 191
 192                case SNDCTL_TMR_START:
 193                        tmr_reset();
 194                        tmr_running = 1;
 195                        return 0;
 196                
 197                case SNDCTL_TMR_STOP:
 198                        tmr_running = 0;
 199                        return 0;
 200
 201                case SNDCTL_TMR_CONTINUE:
 202                        tmr_running = 1;
 203                        return 0;
 204
 205                case SNDCTL_TMR_TIMEBASE:
 206                        if (get_user(val, p))
 207                                return -EFAULT;
 208                        if (val) 
 209                        {
 210                                if (val < 1)
 211                                        val = 1;
 212                                if (val > 1000)
 213                                        val = 1000;
 214                                curr_timebase = val;
 215                        }
 216                        val = curr_timebase;
 217                        break;
 218
 219                case SNDCTL_TMR_TEMPO:
 220                        if (get_user(val, p))
 221                                return -EFAULT;
 222                        if (val) 
 223                        {
 224                                if (val < 8)
 225                                        val = 8;
 226                                if (val > 250)
 227                                        val = 250;
 228                                tmr_offs = tmr_ctr;
 229                                ticks_offs += tmr2ticks(tmr_ctr);
 230                                tmr_ctr = 0;
 231                                curr_tempo = val;
 232                                reprogram_timer();
 233                        }
 234                        val = curr_tempo;
 235                        break;
 236
 237                case SNDCTL_SEQ_CTRLRATE:
 238                        if (get_user(val, p))
 239                                return -EFAULT;
 240                        if (val != 0)   /* Can't change */
 241                                return -EINVAL;
 242                        val = ((curr_tempo * curr_timebase) + 30) / 60;
 243                        break;
 244                
 245                case SNDCTL_SEQ_GETTIME:
 246                        val = curr_ticks;
 247                        break;
 248                
 249                case SNDCTL_TMR_METRONOME:
 250                default:
 251                        return -EINVAL;
 252        }
 253        return put_user(val, p);
 254}
 255
 256static void timer_arm(int dev, long time)
 257{
 258        if (time < 0)
 259                time = curr_ticks + 1;
 260        else if (time <= curr_ticks)    /* It's the time */
 261                return;
 262
 263        next_event_time = prev_event_time = time;
 264        return;
 265}
 266
 267static struct sound_timer_operations sound_timer =
 268{
 269        .owner          = THIS_MODULE,
 270        .info           = {"Sound Timer", 0},
 271        .priority       = 1,    /* Priority */
 272        .devlink        = 0,    /* Local device link */
 273        .open           = timer_open,
 274        .close          = timer_close,
 275        .event          = timer_event,
 276        .get_time       = timer_get_time,
 277        .ioctl          = timer_ioctl,
 278        .arm_timer      = timer_arm
 279};
 280
 281void sound_timer_interrupt(void)
 282{
 283        unsigned long flags;
 284        
 285        if (!opened)
 286                return;
 287
 288        tmr->tmr_restart(tmr->dev);
 289
 290        if (!tmr_running)
 291                return;
 292
 293        spin_lock_irqsave(&lock,flags);
 294        tmr_ctr++;
 295        curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
 296
 297        if (curr_ticks >= next_event_time)
 298        {
 299                next_event_time = (unsigned long) -1;
 300                sequencer_timer(0);
 301        }
 302        spin_unlock_irqrestore(&lock,flags);
 303}
 304EXPORT_SYMBOL(sound_timer_interrupt);
 305
 306void  sound_timer_init(struct sound_lowlev_timer *t, char *name)
 307{
 308        int n;
 309
 310        if (initialized)
 311        {
 312                if (t->priority <= tmr->priority)
 313                        return; /* There is already a similar or better timer */
 314                tmr = t;
 315                return;
 316        }
 317        initialized = 1;
 318        tmr = t;
 319
 320        n = sound_alloc_timerdev();
 321        if (n == -1)
 322                n = 0;          /* Overwrite the system timer */
 323        strcpy(sound_timer.info.name, name);
 324        sound_timer_devs[n] = &sound_timer;
 325}
 326EXPORT_SYMBOL(sound_timer_init);
 327
 328