Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
seq_oss_timer.c
Go to the documentation of this file.
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 #include <linux/slab.h>
27 
28 /*
29  */
30 #define MIN_OSS_TEMPO 8
31 #define MAX_OSS_TEMPO 360
32 #define MIN_OSS_TIMEBASE 1
33 #define MAX_OSS_TIMEBASE 1000
34 
35 /*
36  */
37 static void calc_alsa_tempo(struct seq_oss_timer *timer);
38 static int send_timer_event(struct seq_oss_devinfo *dp, int type, int value);
39 
40 
41 /*
42  * create and register a new timer.
43  * if queue is not started yet, start it.
44  */
45 struct seq_oss_timer *
47 {
48  struct seq_oss_timer *rec;
49 
50  rec = kzalloc(sizeof(*rec), GFP_KERNEL);
51  if (rec == NULL)
52  return NULL;
53 
54  rec->dp = dp;
55  rec->cur_tick = 0;
56  rec->realtime = 0;
57  rec->running = 0;
58  rec->oss_tempo = 60;
59  rec->oss_timebase = 100;
60  calc_alsa_tempo(rec);
61 
62  return rec;
63 }
64 
65 
66 /*
67  * delete timer.
68  * if no more timer exists, stop the queue.
69  */
70 void
72 {
73  if (rec) {
75  kfree(rec);
76  }
77 }
78 
79 
80 /*
81  * process one timing event
82  * return 1 : event proceseed -- skip this event
83  * 0 : not a timer event -- enqueue this event
84  */
85 int
87 {
88  abstime_t parm = ev->t.time;
89 
90  if (ev->t.code == EV_TIMING) {
91  switch (ev->t.cmd) {
92  case TMR_WAIT_REL:
93  parm += rec->cur_tick;
94  rec->realtime = 0;
95  /* continue to next */
96  case TMR_WAIT_ABS:
97  if (parm == 0) {
98  rec->realtime = 1;
99  } else if (parm >= rec->cur_tick) {
100  rec->realtime = 0;
101  rec->cur_tick = parm;
102  }
103  return 1; /* skip this event */
104 
105  case TMR_START:
107  return 1;
108 
109  }
110  } else if (ev->s.code == SEQ_WAIT) {
111  /* time = from 1 to 3 bytes */
112  parm = (ev->echo >> 8) & 0xffffff;
113  if (parm > rec->cur_tick) {
114  /* set next event time */
115  rec->cur_tick = parm;
116  rec->realtime = 0;
117  }
118  return 1;
119  }
120 
121  return 0;
122 }
123 
124 
125 /*
126  * convert tempo units
127  */
128 static void
129 calc_alsa_tempo(struct seq_oss_timer *timer)
130 {
131  timer->tempo = (60 * 1000000) / timer->oss_tempo;
132  timer->ppq = timer->oss_timebase;
133 }
134 
135 
136 /*
137  * dispatch a timer event
138  */
139 static int
140 send_timer_event(struct seq_oss_devinfo *dp, int type, int value)
141 {
142  struct snd_seq_event ev;
143 
144  memset(&ev, 0, sizeof(ev));
145  ev.type = type;
146  ev.source.client = dp->cseq;
147  ev.source.port = 0;
148  ev.dest.client = SNDRV_SEQ_CLIENT_SYSTEM;
149  ev.dest.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
150  ev.queue = dp->queue;
151  ev.data.queue.queue = dp->queue;
152  ev.data.queue.param.value = value;
153  return snd_seq_kernel_client_dispatch(dp->cseq, &ev, 1, 0);
154 }
155 
156 /*
157  * set queue tempo and start queue
158  */
159 int
161 {
162  struct seq_oss_devinfo *dp = timer->dp;
163  struct snd_seq_queue_tempo tmprec;
164 
165  if (timer->running)
166  snd_seq_oss_timer_stop(timer);
167 
168  memset(&tmprec, 0, sizeof(tmprec));
169  tmprec.queue = dp->queue;
170  tmprec.ppq = timer->ppq;
171  tmprec.tempo = timer->tempo;
172  snd_seq_set_queue_tempo(dp->cseq, &tmprec);
173 
174  send_timer_event(dp, SNDRV_SEQ_EVENT_START, 0);
175  timer->running = 1;
176  timer->cur_tick = 0;
177  return 0;
178 }
179 
180 
181 /*
182  * stop queue
183  */
184 int
186 {
187  if (! timer->running)
188  return 0;
189  send_timer_event(timer->dp, SNDRV_SEQ_EVENT_STOP, 0);
190  timer->running = 0;
191  return 0;
192 }
193 
194 
195 /*
196  * continue queue
197  */
198 int
200 {
201  if (timer->running)
202  return 0;
203  send_timer_event(timer->dp, SNDRV_SEQ_EVENT_CONTINUE, 0);
204  timer->running = 1;
205  return 0;
206 }
207 
208 
209 /*
210  * change queue tempo
211  */
212 int
214 {
215  if (value < MIN_OSS_TEMPO)
216  value = MIN_OSS_TEMPO;
217  else if (value > MAX_OSS_TEMPO)
218  value = MAX_OSS_TEMPO;
219  timer->oss_tempo = value;
220  calc_alsa_tempo(timer);
221  if (timer->running)
222  send_timer_event(timer->dp, SNDRV_SEQ_EVENT_TEMPO, timer->tempo);
223  return 0;
224 }
225 
226 
227 /*
228  * ioctls
229  */
230 int
231 snd_seq_oss_timer_ioctl(struct seq_oss_timer *timer, unsigned int cmd, int __user *arg)
232 {
233  int value;
234 
235  if (cmd == SNDCTL_SEQ_CTRLRATE) {
236  debug_printk(("ctrl rate\n"));
237  /* if *arg == 0, just return the current rate */
238  if (get_user(value, arg))
239  return -EFAULT;
240  if (value)
241  return -EINVAL;
242  value = ((timer->oss_tempo * timer->oss_timebase) + 30) / 60;
243  return put_user(value, arg) ? -EFAULT : 0;
244  }
245 
246  if (timer->dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH)
247  return 0;
248 
249  switch (cmd) {
250  case SNDCTL_TMR_START:
251  debug_printk(("timer start\n"));
252  return snd_seq_oss_timer_start(timer);
253  case SNDCTL_TMR_STOP:
254  debug_printk(("timer stop\n"));
255  return snd_seq_oss_timer_stop(timer);
256  case SNDCTL_TMR_CONTINUE:
257  debug_printk(("timer continue\n"));
258  return snd_seq_oss_timer_continue(timer);
259  case SNDCTL_TMR_TEMPO:
260  debug_printk(("timer tempo\n"));
261  if (get_user(value, arg))
262  return -EFAULT;
263  return snd_seq_oss_timer_tempo(timer, value);
264  case SNDCTL_TMR_TIMEBASE:
265  debug_printk(("timer timebase\n"));
266  if (get_user(value, arg))
267  return -EFAULT;
268  if (value < MIN_OSS_TIMEBASE)
269  value = MIN_OSS_TIMEBASE;
270  else if (value > MAX_OSS_TIMEBASE)
271  value = MAX_OSS_TIMEBASE;
272  timer->oss_timebase = value;
273  calc_alsa_tempo(timer);
274  return 0;
275 
277  case SNDCTL_TMR_SELECT:
278  case SNDCTL_TMR_SOURCE:
279  debug_printk(("timer XXX\n"));
280  /* not supported */
281  return 0;
282  }
283  return 0;
284 }