root/drivers/sound/sound_timer.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. timer_command
  2. arm_timer
  3. tmr2ticks
  4. reprogram_timer
  5. tmr_reset
  6. timer_open
  7. timer_close
  8. timer_event
  9. timer_get_time
  10. timer_ioctl
  11. timer_arm
  12. sound_timer_interrupt
  13. sound_timer_init

   1 /*
   2  * sound/sound_timer.c
   3  *
   4  * Timer for the level 2 interface of the /dev/sequencer. Uses the
   5  * 80 and 320 usec timers of OPL-3 (PAS16 only) and GUS.
   6  *
   7  * Copyright by Hannu Savolainen 1993
   8  *
   9  * Redistribution and use in source and binary forms, with or without
  10  * modification, are permitted provided that the following conditions are
  11  * met: 1. Redistributions of source code must retain the above copyright
  12  * notice, this list of conditions and the following disclaimer. 2.
  13  * Redistributions in binary form must reproduce the above copyright notice,
  14  * this list of conditions and the following disclaimer in the documentation
  15  * and/or other materials provided with the distribution.
  16  *
  17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
  18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  20  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
  21  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  24  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27  * SUCH DAMAGE.
  28  *
  29  */
  30 
  31 #define SEQUENCER_C
  32 #include "sound_config.h"
  33 
  34 #ifdef CONFIGURE_SOUNDCARD
  35 
  36 #if !defined(EXCLUDE_SEQUENCER) && (!defined(EXCLUDE_GUS) || (!defined(EXCLUDE_PAS) && !defined(EXCLUDE_YM3812)))
  37 
  38 static volatile int initialized = 0, opened = 0, tmr_running = 0;
  39 static volatile time_t tmr_offs, tmr_ctr;
  40 static volatile unsigned long ticks_offs;
  41 static volatile int curr_tempo, curr_timebase;
  42 static volatile unsigned long curr_ticks;
  43 static volatile unsigned long next_event_time;
  44 static unsigned long prev_event_time;
  45 static volatile int select_addr, data_addr;
  46 static volatile int curr_timer = 0;
  47 static volatile unsigned long usecs_per_tmr;    /* Length of the current interval */
  48 
  49 
  50 static void
  51 timer_command (unsigned int addr, unsigned int val)
     /* [previous][next][first][last][top][bottom][index][help] */
  52 {
  53   int             i;
  54 
  55   OUTB ((unsigned char) (addr & 0xff), select_addr);
  56 
  57   for (i = 0; i < 2; i++)
  58     INB (select_addr);
  59 
  60   OUTB ((unsigned char) (val & 0xff), data_addr);
  61 
  62   for (i = 0; i < 2; i++)
  63     INB (select_addr);
  64 }
  65 
  66 static void
  67 arm_timer (int timer, unsigned int interval)
     /* [previous][next][first][last][top][bottom][index][help] */
  68 {
  69 
  70   curr_timer = timer;
  71 
  72   if (timer == 1)
  73     {
  74       gus_write8 (0x46, 256 - interval);        /* Set counter for timer 1 */
  75       gus_write8 (0x45, 0x04);  /* Enable timer 1 IRQ */
  76       timer_command (0x04, 0x01);       /* Start timer 1 */
  77     }
  78   else
  79     {
  80       gus_write8 (0x47, 256 - interval);        /* Set counter for timer 2 */
  81       gus_write8 (0x45, 0x08);  /* Enable timer 2 IRQ */
  82       timer_command (0x04, 0x02);       /* Start timer 2 */
  83     }
  84 }
  85 
  86 static unsigned long
  87 tmr2ticks (int tmr_value)
     /* [previous][next][first][last][top][bottom][index][help] */
  88 {
  89   /*
  90  *    Convert timer ticks to MIDI ticks
  91  */
  92 
  93   unsigned long   tmp;
  94   unsigned long   scale;
  95 
  96   tmp = tmr_value * usecs_per_tmr;      /* Convert to usecs */
  97 
  98   scale = (60 * 1000000) / (curr_tempo * curr_timebase);        /* usecs per MIDI tick */
  99 
 100   return (tmp + (scale / 2)) / scale;
 101 }
 102 
 103 static void
 104 reprogram_timer (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 105 {
 106   unsigned long   usecs_per_tick;
 107   int             timer_no, resolution;
 108   int             divisor;
 109 
 110   usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase);
 111 
 112   /*
 113  * Don't kill the system by setting too high timer rate
 114  */
 115   if (usecs_per_tick < 2000)
 116     usecs_per_tick = 2000;
 117 
 118   if (usecs_per_tick > (256 * 80))
 119     {
 120       timer_no = 2;
 121       resolution = 320;         /* usec */
 122     }
 123   else
 124     {
 125       timer_no = 1;
 126       resolution = 80;          /* usec */
 127     }
 128 
 129   divisor = (usecs_per_tick + (resolution / 2)) / resolution;
 130   usecs_per_tmr = divisor * resolution;
 131 
 132   arm_timer (timer_no, divisor);
 133 }
 134 
 135 static void
 136 tmr_reset (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 137 {
 138   unsigned long   flags;
 139 
 140   DISABLE_INTR (flags);
 141   tmr_offs = 0;
 142   ticks_offs = 0;
 143   tmr_ctr = 0;
 144   next_event_time = 0xffffffff;
 145   prev_event_time = 0;
 146   curr_ticks = 0;
 147   RESTORE_INTR (flags);
 148 }
 149 
 150 static int
 151 timer_open (int dev, int mode)
     /* [previous][next][first][last][top][bottom][index][help] */
 152 {
 153   if (opened)
 154     return RET_ERROR (EBUSY);
 155 
 156   tmr_reset ();
 157   curr_tempo = 60;
 158   curr_timebase = HZ;
 159   opened = 1;
 160   reprogram_timer ();
 161 
 162   return 0;
 163 }
 164 
 165 static void
 166 timer_close (int dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 167 {
 168   opened = tmr_running = 0;
 169   gus_write8 (0x45, 0);         /* Disable both timers */
 170 }
 171 
 172 static int
 173 timer_event (int dev, unsigned char *event)
     /* [previous][next][first][last][top][bottom][index][help] */
 174 {
 175   unsigned char   cmd = event[1];
 176   unsigned long   parm = *(int *) &event[4];
 177 
 178   switch (cmd)
 179     {
 180     case TMR_WAIT_REL:
 181       parm += prev_event_time;
 182     case TMR_WAIT_ABS:
 183       if (parm > 0)
 184         {
 185           long            time;
 186 
 187           if (parm <= curr_ticks)       /* It's the time */
 188             return TIMER_NOT_ARMED;
 189 
 190           time = parm;
 191           next_event_time = prev_event_time = time;
 192 
 193           return TIMER_ARMED;
 194         }
 195       break;
 196 
 197     case TMR_START:
 198       tmr_reset ();
 199       tmr_running = 1;
 200       reprogram_timer ();
 201       break;
 202 
 203     case TMR_STOP:
 204       tmr_running = 0;
 205       break;
 206 
 207     case TMR_CONTINUE:
 208       tmr_running = 1;
 209       reprogram_timer ();
 210       break;
 211 
 212     case TMR_TEMPO:
 213       if (parm)
 214         {
 215           if (parm < 8)
 216             parm = 8;
 217           if (parm > 250)
 218             parm = 250;
 219           tmr_offs = tmr_ctr;
 220           ticks_offs += tmr2ticks (tmr_ctr);
 221           tmr_ctr = 0;
 222           curr_tempo = parm;
 223           reprogram_timer ();
 224         }
 225       break;
 226 
 227     case TMR_ECHO:
 228       seq_copy_to_input (event, 8);
 229       break;
 230 
 231     default:;
 232     }
 233 
 234   return TIMER_NOT_ARMED;
 235 }
 236 
 237 static unsigned long
 238 timer_get_time (int dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 239 {
 240   if (!opened)
 241     return 0;
 242 
 243   return curr_ticks;
 244 }
 245 
 246 static int
 247 timer_ioctl (int dev,
     /* [previous][next][first][last][top][bottom][index][help] */
 248              unsigned int cmd, unsigned int arg)
 249 {
 250   switch (cmd)
 251     {
 252     case SNDCTL_TMR_SOURCE:
 253       return IOCTL_OUT (arg, TMR_INTERNAL);
 254       break;
 255 
 256     case SNDCTL_TMR_START:
 257       tmr_reset ();
 258       tmr_running = 1;
 259       return 0;
 260       break;
 261 
 262     case SNDCTL_TMR_STOP:
 263       tmr_running = 0;
 264       return 0;
 265       break;
 266 
 267     case SNDCTL_TMR_CONTINUE:
 268       tmr_running = 1;
 269       return 0;
 270       break;
 271 
 272     case SNDCTL_TMR_TIMEBASE:
 273       {
 274         int             val = IOCTL_IN (arg);
 275 
 276         if (val)
 277           {
 278             if (val < 1)
 279               val = 1;
 280             if (val > 1000)
 281               val = 1000;
 282             curr_timebase = val;
 283           }
 284 
 285         return IOCTL_OUT (arg, curr_timebase);
 286       }
 287       break;
 288 
 289     case SNDCTL_TMR_TEMPO:
 290       {
 291         int             val = IOCTL_IN (arg);
 292 
 293         if (val)
 294           {
 295             if (val < 8)
 296               val = 8;
 297             if (val > 250)
 298               val = 250;
 299             tmr_offs = tmr_ctr;
 300             ticks_offs += tmr2ticks (tmr_ctr);
 301             tmr_ctr = 0;
 302             curr_tempo = val;
 303             reprogram_timer ();
 304           }
 305 
 306         return IOCTL_OUT (arg, curr_tempo);
 307       }
 308       break;
 309 
 310     case SNDCTL_SEQ_CTRLRATE:
 311       if (IOCTL_IN (arg) != 0)  /* Can't change */
 312         return RET_ERROR (EINVAL);
 313 
 314       return IOCTL_OUT (arg, ((curr_tempo * curr_timebase) + 30) / 60);
 315       break;
 316 
 317     case SNDCTL_TMR_METRONOME:
 318       /* NOP */
 319       break;
 320 
 321     default:
 322     }
 323 
 324   return RET_ERROR (EINVAL);
 325 }
 326 
 327 static void
 328 timer_arm (int dev, long time)
     /* [previous][next][first][last][top][bottom][index][help] */
 329 {
 330   if (time < 0)
 331     time = curr_ticks + 1;
 332   else if (time <= curr_ticks)  /* It's the time */
 333     return;
 334 
 335   next_event_time = prev_event_time = time;
 336 
 337   return;
 338 }
 339 
 340 static struct sound_timer_operations sound_timer =
 341 {
 342   {"OPL-3/GUS Timer", 0},
 343   1,                            /* Priority */
 344   0,                            /* Local device link */
 345   timer_open,
 346   timer_close,
 347   timer_event,
 348   timer_get_time,
 349   timer_ioctl,
 350   timer_arm
 351 };
 352 
 353 void
 354 sound_timer_interrupt (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 355 {
 356   gus_write8 (0x45, 0);         /* Ack IRQ */
 357   timer_command (4, 0x80);      /* Reset IRQ flags */
 358 
 359   if (!opened)
 360     return;
 361 
 362   if (curr_timer == 1)
 363     gus_write8 (0x45, 0x04);    /* Start timer 1 again */
 364   else
 365     gus_write8 (0x45, 0x08);    /* Start timer 2 again */
 366 
 367   if (!tmr_running)
 368     return;
 369 
 370   tmr_ctr++;
 371   curr_ticks = ticks_offs + tmr2ticks (tmr_ctr);
 372 
 373   if (curr_ticks >= next_event_time)
 374     {
 375       next_event_time = 0xffffffff;
 376       sequencer_timer ();
 377     }
 378 }
 379 
 380 void
 381 sound_timer_init (int io_base)
     /* [previous][next][first][last][top][bottom][index][help] */
 382 {
 383   int             n;
 384 
 385   if (initialized)
 386     return;                     /* There is already a similar timer */
 387 
 388   select_addr = io_base;
 389   data_addr = io_base + 1;
 390 
 391   initialized = 1;
 392 
 393 #if 1
 394   if (num_sound_timers >= MAX_TIMER_DEV)
 395     n = 0;                      /* Overwrite the system timer */
 396   else
 397     n = num_sound_timers++;
 398 #else
 399   n = 0;
 400 #endif
 401 
 402   sound_timer_devs[n] = &sound_timer;
 403 }
 404 
 405 #endif
 406 #endif

/* [previous][next][first][last][top][bottom][index][help] */