123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- /*
- * OSS compatible sequencer driver
- *
- * Timer control routines
- *
- * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- #include "seq_oss_timer.h"
- #include "seq_oss_event.h"
- #include <sound/seq_oss_legacy.h>
- #include <linux/slab.h>
- /*
- */
- #define MIN_OSS_TEMPO 8
- #define MAX_OSS_TEMPO 360
- #define MIN_OSS_TIMEBASE 1
- #define MAX_OSS_TIMEBASE 1000
- /*
- */
- static void calc_alsa_tempo(struct seq_oss_timer *timer);
- static int send_timer_event(struct seq_oss_devinfo *dp, int type, int value);
- /*
- * create and register a new timer.
- * if queue is not started yet, start it.
- */
- struct seq_oss_timer *
- snd_seq_oss_timer_new(struct seq_oss_devinfo *dp)
- {
- struct seq_oss_timer *rec;
- rec = kzalloc(sizeof(*rec), GFP_KERNEL);
- if (rec == NULL)
- return NULL;
- rec->dp = dp;
- rec->cur_tick = 0;
- rec->realtime = 0;
- rec->running = 0;
- rec->oss_tempo = 60;
- rec->oss_timebase = 100;
- calc_alsa_tempo(rec);
- return rec;
- }
- /*
- * delete timer.
- * if no more timer exists, stop the queue.
- */
- void
- snd_seq_oss_timer_delete(struct seq_oss_timer *rec)
- {
- if (rec) {
- snd_seq_oss_timer_stop(rec);
- kfree(rec);
- }
- }
- /*
- * process one timing event
- * return 1 : event proceseed -- skip this event
- * 0 : not a timer event -- enqueue this event
- */
- int
- snd_seq_oss_process_timer_event(struct seq_oss_timer *rec, union evrec *ev)
- {
- abstime_t parm = ev->t.time;
- if (ev->t.code == EV_TIMING) {
- switch (ev->t.cmd) {
- case TMR_WAIT_REL:
- parm += rec->cur_tick;
- rec->realtime = 0;
- /* continue to next */
- case TMR_WAIT_ABS:
- if (parm == 0) {
- rec->realtime = 1;
- } else if (parm >= rec->cur_tick) {
- rec->realtime = 0;
- rec->cur_tick = parm;
- }
- return 1; /* skip this event */
-
- case TMR_START:
- snd_seq_oss_timer_start(rec);
- return 1;
- }
- } else if (ev->s.code == SEQ_WAIT) {
- /* time = from 1 to 3 bytes */
- parm = (ev->echo >> 8) & 0xffffff;
- if (parm > rec->cur_tick) {
- /* set next event time */
- rec->cur_tick = parm;
- rec->realtime = 0;
- }
- return 1;
- }
- return 0;
- }
- /*
- * convert tempo units
- */
- static void
- calc_alsa_tempo(struct seq_oss_timer *timer)
- {
- timer->tempo = (60 * 1000000) / timer->oss_tempo;
- timer->ppq = timer->oss_timebase;
- }
- /*
- * dispatch a timer event
- */
- static int
- send_timer_event(struct seq_oss_devinfo *dp, int type, int value)
- {
- struct snd_seq_event ev;
- memset(&ev, 0, sizeof(ev));
- ev.type = type;
- ev.source.client = dp->cseq;
- ev.source.port = 0;
- ev.dest.client = SNDRV_SEQ_CLIENT_SYSTEM;
- ev.dest.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
- ev.queue = dp->queue;
- ev.data.queue.queue = dp->queue;
- ev.data.queue.param.value = value;
- return snd_seq_kernel_client_dispatch(dp->cseq, &ev, 1, 0);
- }
- /*
- * set queue tempo and start queue
- */
- int
- snd_seq_oss_timer_start(struct seq_oss_timer *timer)
- {
- struct seq_oss_devinfo *dp = timer->dp;
- struct snd_seq_queue_tempo tmprec;
- if (timer->running)
- snd_seq_oss_timer_stop(timer);
- memset(&tmprec, 0, sizeof(tmprec));
- tmprec.queue = dp->queue;
- tmprec.ppq = timer->ppq;
- tmprec.tempo = timer->tempo;
- snd_seq_set_queue_tempo(dp->cseq, &tmprec);
- send_timer_event(dp, SNDRV_SEQ_EVENT_START, 0);
- timer->running = 1;
- timer->cur_tick = 0;
- return 0;
- }
- /*
- * stop queue
- */
- int
- snd_seq_oss_timer_stop(struct seq_oss_timer *timer)
- {
- if (! timer->running)
- return 0;
- send_timer_event(timer->dp, SNDRV_SEQ_EVENT_STOP, 0);
- timer->running = 0;
- return 0;
- }
- /*
- * continue queue
- */
- int
- snd_seq_oss_timer_continue(struct seq_oss_timer *timer)
- {
- if (timer->running)
- return 0;
- send_timer_event(timer->dp, SNDRV_SEQ_EVENT_CONTINUE, 0);
- timer->running = 1;
- return 0;
- }
- /*
- * change queue tempo
- */
- int
- snd_seq_oss_timer_tempo(struct seq_oss_timer *timer, int value)
- {
- if (value < MIN_OSS_TEMPO)
- value = MIN_OSS_TEMPO;
- else if (value > MAX_OSS_TEMPO)
- value = MAX_OSS_TEMPO;
- timer->oss_tempo = value;
- calc_alsa_tempo(timer);
- if (timer->running)
- send_timer_event(timer->dp, SNDRV_SEQ_EVENT_TEMPO, timer->tempo);
- return 0;
- }
- /*
- * ioctls
- */
- int
- snd_seq_oss_timer_ioctl(struct seq_oss_timer *timer, unsigned int cmd, int __user *arg)
- {
- int value;
- if (cmd == SNDCTL_SEQ_CTRLRATE) {
- /* if *arg == 0, just return the current rate */
- if (get_user(value, arg))
- return -EFAULT;
- if (value)
- return -EINVAL;
- value = ((timer->oss_tempo * timer->oss_timebase) + 30) / 60;
- return put_user(value, arg) ? -EFAULT : 0;
- }
- if (timer->dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH)
- return 0;
- switch (cmd) {
- case SNDCTL_TMR_START:
- return snd_seq_oss_timer_start(timer);
- case SNDCTL_TMR_STOP:
- return snd_seq_oss_timer_stop(timer);
- case SNDCTL_TMR_CONTINUE:
- return snd_seq_oss_timer_continue(timer);
- case SNDCTL_TMR_TEMPO:
- if (get_user(value, arg))
- return -EFAULT;
- return snd_seq_oss_timer_tempo(timer, value);
- case SNDCTL_TMR_TIMEBASE:
- if (get_user(value, arg))
- return -EFAULT;
- if (value < MIN_OSS_TIMEBASE)
- value = MIN_OSS_TIMEBASE;
- else if (value > MAX_OSS_TIMEBASE)
- value = MAX_OSS_TIMEBASE;
- timer->oss_timebase = value;
- calc_alsa_tempo(timer);
- return 0;
- case SNDCTL_TMR_METRONOME:
- case SNDCTL_TMR_SELECT:
- case SNDCTL_TMR_SOURCE:
- /* not supported */
- return 0;
- }
- return 0;
- }
|