1 /* 2 * OSS compatible sequencer driver 3 * 4 * Timer control routines 5 * 6 * Copyright (C) 1998,99 Takashi Iwai <[email protected]> 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 */ 36 static void calc_alsa_tempo(struct seq_oss_timer *timer); 37 static 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 */ 44 struct seq_oss_timer * 45 snd_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 */ 69 void 70 snd_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 */ 84 int 85 snd_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 */ 127 static void 128 calc_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 */ 138 static int 139 send_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 */ 158 int 159 snd_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 */ 183 int 184 snd_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 */ 197 int 198 snd_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 */ 211 int 212 snd_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 */ 229 int 230 snd_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