• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/sound/core/seq/oss/
1/*
2 * OSS compatible sequencer driver
3 *
4 * Timer control routines
5 *
6 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
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 */
37static void calc_alsa_tempo(struct seq_oss_timer *timer);
38static 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 */
45struct seq_oss_timer *
46snd_seq_oss_timer_new(struct seq_oss_devinfo *dp)
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 */
70void
71snd_seq_oss_timer_delete(struct seq_oss_timer *rec)
72{
73	if (rec) {
74		snd_seq_oss_timer_stop(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 */
85int
86snd_seq_oss_process_timer_event(struct seq_oss_timer *rec, union evrec *ev)
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:
106			snd_seq_oss_timer_start(rec);
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 */
128static void
129calc_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 */
139static int
140send_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 */
159int
160snd_seq_oss_timer_start(struct seq_oss_timer *timer)
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 */
184int
185snd_seq_oss_timer_stop(struct seq_oss_timer *timer)
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 */
198int
199snd_seq_oss_timer_continue(struct seq_oss_timer *timer)
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 */
212int
213snd_seq_oss_timer_tempo(struct seq_oss_timer *timer, int value)
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 */
230int
231snd_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
276	case SNDCTL_TMR_METRONOME:
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}
285