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