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 DEFINE_SPINLOCK(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 strlcpy(sound_timer.info.name, name, sizeof(sound_timer.info.name)); 324 sound_timer_devs[n] = &sound_timer; 325} 326EXPORT_SYMBOL(sound_timer_init); 327 328