1/*
2 * OSS compatible sequencer driver
3 *
4 * OSS compatible i/o control
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_device.h"
24#include "seq_oss_readq.h"
25#include "seq_oss_writeq.h"
26#include "seq_oss_timer.h"
27#include "seq_oss_synth.h"
28#include "seq_oss_midi.h"
29#include "seq_oss_event.h"
30
31static int snd_seq_oss_synth_info_user(struct seq_oss_devinfo *dp, void __user *arg)
32{
33	struct synth_info info;
34
35	if (copy_from_user(&info, arg, sizeof(info)))
36		return -EFAULT;
37	if (snd_seq_oss_synth_make_info(dp, info.device, &info) < 0)
38		return -EINVAL;
39	if (copy_to_user(arg, &info, sizeof(info)))
40		return -EFAULT;
41	return 0;
42}
43
44static int snd_seq_oss_midi_info_user(struct seq_oss_devinfo *dp, void __user *arg)
45{
46	struct midi_info info;
47
48	if (copy_from_user(&info, arg, sizeof(info)))
49		return -EFAULT;
50	if (snd_seq_oss_midi_make_info(dp, info.device, &info) < 0)
51		return -EINVAL;
52	if (copy_to_user(arg, &info, sizeof(info)))
53		return -EFAULT;
54	return 0;
55}
56
57static int snd_seq_oss_oob_user(struct seq_oss_devinfo *dp, void __user *arg)
58{
59	unsigned char ev[8];
60	struct snd_seq_event tmpev;
61
62	if (copy_from_user(ev, arg, 8))
63		return -EFAULT;
64	memset(&tmpev, 0, sizeof(tmpev));
65	snd_seq_oss_fill_addr(dp, &tmpev, dp->addr.port, dp->addr.client);
66	tmpev.time.tick = 0;
67	if (! snd_seq_oss_process_event(dp, (union evrec *)ev, &tmpev)) {
68		snd_seq_oss_dispatch(dp, &tmpev, 0, 0);
69	}
70	return 0;
71}
72
73int
74snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long carg)
75{
76	int dev, val;
77	void __user *arg = (void __user *)carg;
78	int __user *p = arg;
79
80	switch (cmd) {
81	case SNDCTL_TMR_TIMEBASE:
82	case SNDCTL_TMR_TEMPO:
83	case SNDCTL_TMR_START:
84	case SNDCTL_TMR_STOP:
85	case SNDCTL_TMR_CONTINUE:
86	case SNDCTL_TMR_METRONOME:
87	case SNDCTL_TMR_SOURCE:
88	case SNDCTL_TMR_SELECT:
89	case SNDCTL_SEQ_CTRLRATE:
90		return snd_seq_oss_timer_ioctl(dp->timer, cmd, arg);
91
92	case SNDCTL_SEQ_PANIC:
93		debug_printk(("panic\n"));
94		snd_seq_oss_reset(dp);
95		return -EINVAL;
96
97	case SNDCTL_SEQ_SYNC:
98		debug_printk(("sync\n"));
99		if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
100			return 0;
101		while (snd_seq_oss_writeq_sync(dp->writeq))
102			;
103		if (signal_pending(current))
104			return -ERESTARTSYS;
105		return 0;
106
107	case SNDCTL_SEQ_RESET:
108		debug_printk(("reset\n"));
109		snd_seq_oss_reset(dp);
110		return 0;
111
112	case SNDCTL_SEQ_TESTMIDI:
113		debug_printk(("test midi\n"));
114		if (get_user(dev, p))
115			return -EFAULT;
116		return snd_seq_oss_midi_open(dp, dev, dp->file_mode);
117
118	case SNDCTL_SEQ_GETINCOUNT:
119		debug_printk(("get in count\n"));
120		if (dp->readq == NULL || ! is_read_mode(dp->file_mode))
121			return 0;
122		return put_user(dp->readq->qlen, p) ? -EFAULT : 0;
123
124	case SNDCTL_SEQ_GETOUTCOUNT:
125		debug_printk(("get out count\n"));
126		if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
127			return 0;
128		return put_user(snd_seq_oss_writeq_get_free_size(dp->writeq), p) ? -EFAULT : 0;
129
130	case SNDCTL_SEQ_GETTIME:
131		debug_printk(("get time\n"));
132		return put_user(snd_seq_oss_timer_cur_tick(dp->timer), p) ? -EFAULT : 0;
133
134	case SNDCTL_SEQ_RESETSAMPLES:
135		debug_printk(("reset samples\n"));
136		if (get_user(dev, p))
137			return -EFAULT;
138		return snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
139
140	case SNDCTL_SEQ_NRSYNTHS:
141		debug_printk(("nr synths\n"));
142		return put_user(dp->max_synthdev, p) ? -EFAULT : 0;
143
144	case SNDCTL_SEQ_NRMIDIS:
145		debug_printk(("nr midis\n"));
146		return put_user(dp->max_mididev, p) ? -EFAULT : 0;
147
148	case SNDCTL_SYNTH_MEMAVL:
149		debug_printk(("mem avail\n"));
150		if (get_user(dev, p))
151			return -EFAULT;
152		val = snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
153		return put_user(val, p) ? -EFAULT : 0;
154
155	case SNDCTL_FM_4OP_ENABLE:
156		debug_printk(("4op\n"));
157		if (get_user(dev, p))
158			return -EFAULT;
159		snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
160		return 0;
161
162	case SNDCTL_SYNTH_INFO:
163	case SNDCTL_SYNTH_ID:
164		debug_printk(("synth info\n"));
165		return snd_seq_oss_synth_info_user(dp, arg);
166
167	case SNDCTL_SEQ_OUTOFBAND:
168		debug_printk(("out of band\n"));
169		return snd_seq_oss_oob_user(dp, arg);
170
171	case SNDCTL_MIDI_INFO:
172		debug_printk(("midi info\n"));
173		return snd_seq_oss_midi_info_user(dp, arg);
174
175	case SNDCTL_SEQ_THRESHOLD:
176		debug_printk(("threshold\n"));
177		if (! is_write_mode(dp->file_mode))
178			return 0;
179		if (get_user(val, p))
180			return -EFAULT;
181		if (val < 1)
182			val = 1;
183		if (val >= dp->writeq->maxlen)
184			val = dp->writeq->maxlen - 1;
185		snd_seq_oss_writeq_set_output(dp->writeq, val);
186		return 0;
187
188	case SNDCTL_MIDI_PRETIME:
189		debug_printk(("pretime\n"));
190		if (dp->readq == NULL || !is_read_mode(dp->file_mode))
191			return 0;
192		if (get_user(val, p))
193			return -EFAULT;
194		if (val <= 0)
195			val = -1;
196		else
197			val = (HZ * val) / 10;
198		dp->readq->pre_event_timeout = val;
199		return put_user(val, p) ? -EFAULT : 0;
200
201	default:
202		debug_printk(("others\n"));
203		if (! is_write_mode(dp->file_mode))
204			return -EIO;
205		return snd_seq_oss_synth_ioctl(dp, 0, cmd, carg);
206	}
207	return 0;
208}
209