1/* 2 * OSS compatible sequencer driver 3 * 4 * Timer control routines 5 * 6 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 */ 22 23#include "seq_oss_timer.h" 24#include "seq_oss_event.h" 25#include <sound/seq_oss_legacy.h> 26 27/* 28 */ 29#define MIN_OSS_TEMPO 8 30#define MAX_OSS_TEMPO 360 31#define MIN_OSS_TIMEBASE 1 32#define MAX_OSS_TIMEBASE 1000 33 34/* 35 */ 36static void calc_alsa_tempo(struct seq_oss_timer *timer); 37static int send_timer_event(struct seq_oss_devinfo *dp, int type, int value); 38 39 40/* 41 * create and register a new timer. 42 * if queue is not started yet, start it. 43 */ 44struct seq_oss_timer * 45snd_seq_oss_timer_new(struct seq_oss_devinfo *dp) 46{ 47 struct seq_oss_timer *rec; 48 49 rec = kzalloc(sizeof(*rec), GFP_KERNEL); 50 if (rec == NULL) 51 return NULL; 52 53 rec->dp = dp; 54 rec->cur_tick = 0; 55 rec->realtime = 0; 56 rec->running = 0; 57 rec->oss_tempo = 60; 58 rec->oss_timebase = 100; 59 calc_alsa_tempo(rec); 60 61 return rec; 62} 63 64 65/* 66 * delete timer. 67 * if no more timer exists, stop the queue. 68 */ 69void 70snd_seq_oss_timer_delete(struct seq_oss_timer *rec) 71{ 72 if (rec) { 73 snd_seq_oss_timer_stop(rec); 74 kfree(rec); 75 } 76} 77 78 79/* 80 * process one timing event 81 * return 1 : event proceseed -- skip this event 82 * 0 : not a timer event -- enqueue this event 83 */ 84int 85snd_seq_oss_process_timer_event(struct seq_oss_timer *rec, union evrec *ev) 86{ 87 abstime_t parm = ev->t.time; 88 89 if (ev->t.code == EV_TIMING) { 90 switch (ev->t.cmd) { 91 case TMR_WAIT_REL: 92 parm += rec->cur_tick; 93 rec->realtime = 0; 94 /* continue to next */ 95 case TMR_WAIT_ABS: 96 if (parm == 0) { 97 rec->realtime = 1; 98 } else if (parm >= rec->cur_tick) { 99 rec->realtime = 0; 100 rec->cur_tick = parm; 101 } 102 return 1; /* skip this event */ 103 104 case TMR_START: 105 snd_seq_oss_timer_start(rec); 106 return 1; 107 108 } 109 } else if (ev->s.code == SEQ_WAIT) { 110 /* time = from 1 to 3 bytes */ 111 parm = (ev->echo >> 8) & 0xffffff; 112 if (parm > rec->cur_tick) { 113 /* set next event time */ 114 rec->cur_tick = parm; 115 rec->realtime = 0; 116 } 117 return 1; 118 } 119 120 return 0; 121} 122 123 124/* 125 * convert tempo units 126 */ 127static void 128calc_alsa_tempo(struct seq_oss_timer *timer) 129{ 130 timer->tempo = (60 * 1000000) / timer->oss_tempo; 131 timer->ppq = timer->oss_timebase; 132} 133 134 135/* 136 * dispatch a timer event 137 */ 138static int 139send_timer_event(struct seq_oss_devinfo *dp, int type, int value) 140{ 141 struct snd_seq_event ev; 142 143 memset(&ev, 0, sizeof(ev)); 144 ev.type = type; 145 ev.source.client = dp->cseq; 146 ev.source.port = 0; 147 ev.dest.client = SNDRV_SEQ_CLIENT_SYSTEM; 148 ev.dest.port = SNDRV_SEQ_PORT_SYSTEM_TIMER; 149 ev.queue = dp->queue; 150 ev.data.queue.queue = dp->queue; 151 ev.data.queue.param.value = value; 152 return snd_seq_kernel_client_dispatch(dp->cseq, &ev, 1, 0); 153} 154 155/* 156 * set queue tempo and start queue 157 */ 158int 159snd_seq_oss_timer_start(struct seq_oss_timer *timer) 160{ 161 struct seq_oss_devinfo *dp = timer->dp; 162 struct snd_seq_queue_tempo tmprec; 163 164 if (timer->running) 165 snd_seq_oss_timer_stop(timer); 166 167 memset(&tmprec, 0, sizeof(tmprec)); 168 tmprec.queue = dp->queue; 169 tmprec.ppq = timer->ppq; 170 tmprec.tempo = timer->tempo; 171 snd_seq_set_queue_tempo(dp->cseq, &tmprec); 172 173 send_timer_event(dp, SNDRV_SEQ_EVENT_START, 0); 174 timer->running = 1; 175 timer->cur_tick = 0; 176 return 0; 177} 178 179 180/* 181 * stop queue 182 */ 183int 184snd_seq_oss_timer_stop(struct seq_oss_timer *timer) 185{ 186 if (! timer->running) 187 return 0; 188 send_timer_event(timer->dp, SNDRV_SEQ_EVENT_STOP, 0); 189 timer->running = 0; 190 return 0; 191} 192 193 194/* 195 * continue queue 196 */ 197int 198snd_seq_oss_timer_continue(struct seq_oss_timer *timer) 199{ 200 if (timer->running) 201 return 0; 202 send_timer_event(timer->dp, SNDRV_SEQ_EVENT_CONTINUE, 0); 203 timer->running = 1; 204 return 0; 205} 206 207 208/* 209 * change queue tempo 210 */ 211int 212snd_seq_oss_timer_tempo(struct seq_oss_timer *timer, int value) 213{ 214 if (value < MIN_OSS_TEMPO) 215 value = MIN_OSS_TEMPO; 216 else if (value > MAX_OSS_TEMPO) 217 value = MAX_OSS_TEMPO; 218 timer->oss_tempo = value; 219 calc_alsa_tempo(timer); 220 if (timer->running) 221 send_timer_event(timer->dp, SNDRV_SEQ_EVENT_TEMPO, timer->tempo); 222 return 0; 223} 224 225 226/* 227 * ioctls 228 */ 229int 230snd_seq_oss_timer_ioctl(struct seq_oss_timer *timer, unsigned int cmd, int __user *arg) 231{ 232 int value; 233 234 if (cmd == SNDCTL_SEQ_CTRLRATE) { 235 debug_printk(("ctrl rate\n")); 236 /* if *arg == 0, just return the current rate */ 237 if (get_user(value, arg)) 238 return -EFAULT; 239 if (value) 240 return -EINVAL; 241 value = ((timer->oss_tempo * timer->oss_timebase) + 30) / 60; 242 return put_user(value, arg) ? -EFAULT : 0; 243 } 244 245 if (timer->dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) 246 return 0; 247 248 switch (cmd) { 249 case SNDCTL_TMR_START: 250 debug_printk(("timer start\n")); 251 return snd_seq_oss_timer_start(timer); 252 case SNDCTL_TMR_STOP: 253 debug_printk(("timer stop\n")); 254 return snd_seq_oss_timer_stop(timer); 255 case SNDCTL_TMR_CONTINUE: 256 debug_printk(("timer continue\n")); 257 return snd_seq_oss_timer_continue(timer); 258 case SNDCTL_TMR_TEMPO: 259 debug_printk(("timer tempo\n")); 260 if (get_user(value, arg)) 261 return -EFAULT; 262 return snd_seq_oss_timer_tempo(timer, value); 263 case SNDCTL_TMR_TIMEBASE: 264 debug_printk(("timer timebase\n")); 265 if (get_user(value, arg)) 266 return -EFAULT; 267 if (value < MIN_OSS_TIMEBASE) 268 value = MIN_OSS_TIMEBASE; 269 else if (value > MAX_OSS_TIMEBASE) 270 value = MAX_OSS_TIMEBASE; 271 timer->oss_timebase = value; 272 calc_alsa_tempo(timer); 273 return 0; 274 275 case SNDCTL_TMR_METRONOME: 276 case SNDCTL_TMR_SELECT: 277 case SNDCTL_TMR_SOURCE: 278 debug_printk(("timer XXX\n")); 279 /* not supported */ 280 return 0; 281 } 282 return 0; 283} 284