Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * OSS compatible sequencer driver
0004  *
0005  * Timer control routines
0006  *
0007  * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
0008  */
0009 
0010 #include "seq_oss_timer.h"
0011 #include "seq_oss_event.h"
0012 #include <sound/seq_oss_legacy.h>
0013 #include <linux/slab.h>
0014 
0015 /*
0016  */
0017 #define MIN_OSS_TEMPO       8
0018 #define MAX_OSS_TEMPO       360
0019 #define MIN_OSS_TIMEBASE    1
0020 #define MAX_OSS_TIMEBASE    1000
0021 
0022 /*
0023  */
0024 static void calc_alsa_tempo(struct seq_oss_timer *timer);
0025 static int send_timer_event(struct seq_oss_devinfo *dp, int type, int value);
0026 
0027 
0028 /*
0029  * create and register a new timer.
0030  * if queue is not started yet, start it.
0031  */
0032 struct seq_oss_timer *
0033 snd_seq_oss_timer_new(struct seq_oss_devinfo *dp)
0034 {
0035     struct seq_oss_timer *rec;
0036 
0037     rec = kzalloc(sizeof(*rec), GFP_KERNEL);
0038     if (rec == NULL)
0039         return NULL;
0040 
0041     rec->dp = dp;
0042     rec->cur_tick = 0;
0043     rec->realtime = 0;
0044     rec->running = 0;
0045     rec->oss_tempo = 60;
0046     rec->oss_timebase = 100;
0047     calc_alsa_tempo(rec);
0048 
0049     return rec;
0050 }
0051 
0052 
0053 /*
0054  * delete timer.
0055  * if no more timer exists, stop the queue.
0056  */
0057 void
0058 snd_seq_oss_timer_delete(struct seq_oss_timer *rec)
0059 {
0060     if (rec) {
0061         snd_seq_oss_timer_stop(rec);
0062         kfree(rec);
0063     }
0064 }
0065 
0066 
0067 /*
0068  * process one timing event
0069  * return 1 : event proceseed -- skip this event
0070  *        0 : not a timer event -- enqueue this event
0071  */
0072 int
0073 snd_seq_oss_process_timer_event(struct seq_oss_timer *rec, union evrec *ev)
0074 {
0075     abstime_t parm = ev->t.time;
0076 
0077     if (ev->t.code == EV_TIMING) {
0078         switch (ev->t.cmd) {
0079         case TMR_WAIT_REL:
0080             parm += rec->cur_tick;
0081             rec->realtime = 0;
0082             fallthrough;
0083         case TMR_WAIT_ABS:
0084             if (parm == 0) {
0085                 rec->realtime = 1;
0086             } else if (parm >= rec->cur_tick) {
0087                 rec->realtime = 0;
0088                 rec->cur_tick = parm;
0089             }
0090             return 1;   /* skip this event */
0091             
0092         case TMR_START:
0093             snd_seq_oss_timer_start(rec);
0094             return 1;
0095 
0096         }
0097     } else if (ev->s.code == SEQ_WAIT) {
0098         /* time = from 1 to 3 bytes */
0099         parm = (ev->echo >> 8) & 0xffffff;
0100         if (parm > rec->cur_tick) {
0101             /* set next event time */
0102             rec->cur_tick = parm;
0103             rec->realtime = 0;
0104         }
0105         return 1;
0106     }
0107 
0108     return 0;
0109 }
0110 
0111 
0112 /*
0113  * convert tempo units
0114  */
0115 static void
0116 calc_alsa_tempo(struct seq_oss_timer *timer)
0117 {
0118     timer->tempo = (60 * 1000000) / timer->oss_tempo;
0119     timer->ppq = timer->oss_timebase;
0120 }
0121 
0122 
0123 /*
0124  * dispatch a timer event
0125  */
0126 static int
0127 send_timer_event(struct seq_oss_devinfo *dp, int type, int value)
0128 {
0129     struct snd_seq_event ev;
0130 
0131     memset(&ev, 0, sizeof(ev));
0132     ev.type = type;
0133     ev.source.client = dp->cseq;
0134     ev.source.port = 0;
0135     ev.dest.client = SNDRV_SEQ_CLIENT_SYSTEM;
0136     ev.dest.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
0137     ev.queue = dp->queue;
0138     ev.data.queue.queue = dp->queue;
0139     ev.data.queue.param.value = value;
0140     return snd_seq_kernel_client_dispatch(dp->cseq, &ev, 1, 0);
0141 }
0142 
0143 /*
0144  * set queue tempo and start queue
0145  */
0146 int
0147 snd_seq_oss_timer_start(struct seq_oss_timer *timer)
0148 {
0149     struct seq_oss_devinfo *dp = timer->dp;
0150     struct snd_seq_queue_tempo tmprec;
0151 
0152     if (timer->running)
0153         snd_seq_oss_timer_stop(timer);
0154 
0155     memset(&tmprec, 0, sizeof(tmprec));
0156     tmprec.queue = dp->queue;
0157     tmprec.ppq = timer->ppq;
0158     tmprec.tempo = timer->tempo;
0159     snd_seq_set_queue_tempo(dp->cseq, &tmprec);
0160 
0161     send_timer_event(dp, SNDRV_SEQ_EVENT_START, 0);
0162     timer->running = 1;
0163     timer->cur_tick = 0;
0164     return 0;
0165 }
0166 
0167 
0168 /*
0169  * stop queue
0170  */
0171 int
0172 snd_seq_oss_timer_stop(struct seq_oss_timer *timer)
0173 {
0174     if (! timer->running)
0175         return 0;
0176     send_timer_event(timer->dp, SNDRV_SEQ_EVENT_STOP, 0);
0177     timer->running = 0;
0178     return 0;
0179 }
0180 
0181 
0182 /*
0183  * continue queue
0184  */
0185 int
0186 snd_seq_oss_timer_continue(struct seq_oss_timer *timer)
0187 {
0188     if (timer->running)
0189         return 0;
0190     send_timer_event(timer->dp, SNDRV_SEQ_EVENT_CONTINUE, 0);
0191     timer->running = 1;
0192     return 0;
0193 }
0194 
0195 
0196 /*
0197  * change queue tempo
0198  */
0199 int
0200 snd_seq_oss_timer_tempo(struct seq_oss_timer *timer, int value)
0201 {
0202     if (value < MIN_OSS_TEMPO)
0203         value = MIN_OSS_TEMPO;
0204     else if (value > MAX_OSS_TEMPO)
0205         value = MAX_OSS_TEMPO;
0206     timer->oss_tempo = value;
0207     calc_alsa_tempo(timer);
0208     if (timer->running)
0209         send_timer_event(timer->dp, SNDRV_SEQ_EVENT_TEMPO, timer->tempo);
0210     return 0;
0211 }
0212 
0213 
0214 /*
0215  * ioctls
0216  */
0217 int
0218 snd_seq_oss_timer_ioctl(struct seq_oss_timer *timer, unsigned int cmd, int __user *arg)
0219 {
0220     int value;
0221 
0222     if (cmd == SNDCTL_SEQ_CTRLRATE) {
0223         /* if *arg == 0, just return the current rate */
0224         if (get_user(value, arg))
0225             return -EFAULT;
0226         if (value)
0227             return -EINVAL;
0228         value = ((timer->oss_tempo * timer->oss_timebase) + 30) / 60;
0229         return put_user(value, arg) ? -EFAULT : 0;
0230     }
0231 
0232     if (timer->dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH)
0233         return 0;
0234 
0235     switch (cmd) {
0236     case SNDCTL_TMR_START:
0237         return snd_seq_oss_timer_start(timer);
0238     case SNDCTL_TMR_STOP:
0239         return snd_seq_oss_timer_stop(timer);
0240     case SNDCTL_TMR_CONTINUE:
0241         return snd_seq_oss_timer_continue(timer);
0242     case SNDCTL_TMR_TEMPO:
0243         if (get_user(value, arg))
0244             return -EFAULT;
0245         return snd_seq_oss_timer_tempo(timer, value);
0246     case SNDCTL_TMR_TIMEBASE:
0247         if (get_user(value, arg))
0248             return -EFAULT;
0249         if (value < MIN_OSS_TIMEBASE)
0250             value = MIN_OSS_TIMEBASE;
0251         else if (value > MAX_OSS_TIMEBASE)
0252             value = MAX_OSS_TIMEBASE;
0253         timer->oss_timebase = value;
0254         calc_alsa_tempo(timer);
0255         return 0;
0256 
0257     case SNDCTL_TMR_METRONOME:
0258     case SNDCTL_TMR_SELECT:
0259     case SNDCTL_TMR_SOURCE:
0260         /* not supported */
0261         return 0;
0262     }
0263     return 0;
0264 }