sound_oss.c 7.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
/*
 *  Advanced Linux Sound Architecture
 *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
 *
 *
 *   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 <sound/driver.h>

#ifdef CONFIG_SND_OSSEMUL

#if !defined(CONFIG_SOUND) && !(defined(MODULE) && defined(CONFIG_SOUND_MODULE))
#error "Enable the OSS soundcore multiplexer (CONFIG_SOUND) in the kernel."
#endif

#include <linux/init.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/info.h>
#include <linux/sound.h>
37
#include <linux/mutex.h>
L
Linus Torvalds 已提交
38

39
#define SNDRV_OSS_MINORS 128
L
Linus Torvalds 已提交
40

41
static struct snd_minor *snd_oss_minors[SNDRV_OSS_MINORS];
42
static DEFINE_MUTEX(sound_oss_mutex);
L
Linus Torvalds 已提交
43

44 45 46 47 48
void *snd_lookup_oss_minor_data(unsigned int minor, int type)
{
	struct snd_minor *mreg;
	void *private_data;

49
	if (minor >= ARRAY_SIZE(snd_oss_minors))
50
		return NULL;
51
	mutex_lock(&sound_oss_mutex);
52 53 54 55 56
	mreg = snd_oss_minors[minor];
	if (mreg && mreg->type == type)
		private_data = mreg->private_data;
	else
		private_data = NULL;
57
	mutex_unlock(&sound_oss_mutex);
58 59 60
	return private_data;
}

61 62
EXPORT_SYMBOL(snd_lookup_oss_minor_data);

63
static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev)
L
Linus Torvalds 已提交
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
{
	int minor;

	switch (type) {
	case SNDRV_OSS_DEVICE_TYPE_MIXER:
		snd_assert(card != NULL && dev <= 1, return -EINVAL);
		minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIXER1 : SNDRV_MINOR_OSS_MIXER));
		break;
	case SNDRV_OSS_DEVICE_TYPE_SEQUENCER:
		minor = SNDRV_MINOR_OSS_SEQUENCER;
		break;
	case SNDRV_OSS_DEVICE_TYPE_MUSIC:
		minor = SNDRV_MINOR_OSS_MUSIC;
		break;
	case SNDRV_OSS_DEVICE_TYPE_PCM:
		snd_assert(card != NULL && dev <= 1, return -EINVAL);
		minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_PCM1 : SNDRV_MINOR_OSS_PCM));
		break;
	case SNDRV_OSS_DEVICE_TYPE_MIDI:
		snd_assert(card != NULL && dev <= 1, return -EINVAL);
		minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIDI1 : SNDRV_MINOR_OSS_MIDI));
		break;
	case SNDRV_OSS_DEVICE_TYPE_DMFM:
		minor = SNDRV_MINOR_OSS(card->number, SNDRV_MINOR_OSS_DMFM);
		break;
	case SNDRV_OSS_DEVICE_TYPE_SNDSTAT:
		minor = SNDRV_MINOR_OSS_SNDSTAT;
		break;
	default:
		return -EINVAL;
	}
95
	snd_assert(minor >= 0 && minor < SNDRV_OSS_MINORS, return -EINVAL);
L
Linus Torvalds 已提交
96 97 98
	return minor;
}

99
int snd_register_oss_device(int type, struct snd_card *card, int dev,
100
			    const struct file_operations *f_ops, void *private_data,
101
			    const char *name)
L
Linus Torvalds 已提交
102 103 104
{
	int minor = snd_oss_kernel_minor(type, card, dev);
	int minor_unit;
105
	struct snd_minor *preg;
L
Linus Torvalds 已提交
106 107 108
	int cidx = SNDRV_MINOR_OSS_CARD(minor);
	int track2 = -1;
	int register1 = -1, register2 = -1;
T
Takashi Iwai 已提交
109
	struct device *carddev = snd_card_get_device_link(card);
L
Linus Torvalds 已提交
110

111 112
	if (card && card->number >= 8)
		return 0; /* ignore silently */
L
Linus Torvalds 已提交
113 114
	if (minor < 0)
		return minor;
115
	preg = kmalloc(sizeof(struct snd_minor), GFP_KERNEL);
L
Linus Torvalds 已提交
116 117
	if (preg == NULL)
		return -ENOMEM;
118
	preg->type = type;
119
	preg->card = card ? card->number : -1;
L
Linus Torvalds 已提交
120
	preg->device = dev;
121
	preg->f_ops = f_ops;
122
	preg->private_data = private_data;
123
	mutex_lock(&sound_oss_mutex);
124
	snd_oss_minors[minor] = preg;
L
Linus Torvalds 已提交
125 126 127 128 129 130 131 132 133 134 135 136
	minor_unit = SNDRV_MINOR_OSS_DEVICE(minor);
	switch (minor_unit) {
	case SNDRV_MINOR_OSS_PCM:
		track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO);
		break;
	case SNDRV_MINOR_OSS_MIDI:
		track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI);
		break;
	case SNDRV_MINOR_OSS_MIDI1:
		track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1);
		break;
	}
137
	register1 = register_sound_special_device(f_ops, minor, carddev);
L
Linus Torvalds 已提交
138 139 140
	if (register1 != minor)
		goto __end;
	if (track2 >= 0) {
141 142
		register2 = register_sound_special_device(f_ops, track2,
							  carddev);
L
Linus Torvalds 已提交
143 144
		if (register2 != track2)
			goto __end;
145
		snd_oss_minors[track2] = preg;
L
Linus Torvalds 已提交
146
	}
147
	mutex_unlock(&sound_oss_mutex);
L
Linus Torvalds 已提交
148 149 150 151 152 153 154
	return 0;

      __end:
      	if (register2 >= 0)
      		unregister_sound_special(register2);
      	if (register1 >= 0)
      		unregister_sound_special(register1);
155
	snd_oss_minors[minor] = NULL;
156
	mutex_unlock(&sound_oss_mutex);
L
Linus Torvalds 已提交
157 158 159 160
	kfree(preg);
      	return -EBUSY;
}

161 162
EXPORT_SYMBOL(snd_register_oss_device);

163
int snd_unregister_oss_device(int type, struct snd_card *card, int dev)
L
Linus Torvalds 已提交
164 165 166 167
{
	int minor = snd_oss_kernel_minor(type, card, dev);
	int cidx = SNDRV_MINOR_OSS_CARD(minor);
	int track2 = -1;
168
	struct snd_minor *mptr;
L
Linus Torvalds 已提交
169

170 171
	if (card && card->number >= 8)
		return 0;
L
Linus Torvalds 已提交
172 173
	if (minor < 0)
		return minor;
174
	mutex_lock(&sound_oss_mutex);
175
	mptr = snd_oss_minors[minor];
L
Linus Torvalds 已提交
176
	if (mptr == NULL) {
177
		mutex_unlock(&sound_oss_mutex);
L
Linus Torvalds 已提交
178 179 180 181 182 183 184 185 186 187 188 189 190 191
		return -ENOENT;
	}
	unregister_sound_special(minor);
	switch (SNDRV_MINOR_OSS_DEVICE(minor)) {
	case SNDRV_MINOR_OSS_PCM:
		track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO);
		break;
	case SNDRV_MINOR_OSS_MIDI:
		track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI);
		break;
	case SNDRV_MINOR_OSS_MIDI1:
		track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1);
		break;
	}
192
	if (track2 >= 0) {
L
Linus Torvalds 已提交
193
		unregister_sound_special(track2);
194 195
		snd_oss_minors[track2] = NULL;
	}
196
	snd_oss_minors[minor] = NULL;
197
	mutex_unlock(&sound_oss_mutex);
L
Linus Torvalds 已提交
198 199 200 201
	kfree(mptr);
	return 0;
}

202 203
EXPORT_SYMBOL(snd_unregister_oss_device);

L
Linus Torvalds 已提交
204 205 206 207 208 209
/*
 *  INFO PART
 */

#ifdef CONFIG_PROC_FS

210
static struct snd_info_entry *snd_minor_info_oss_entry;
L
Linus Torvalds 已提交
211

212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
static const char *snd_oss_device_type_name(int type)
{
	switch (type) {
	case SNDRV_OSS_DEVICE_TYPE_MIXER:
		return "mixer";
	case SNDRV_OSS_DEVICE_TYPE_SEQUENCER:
	case SNDRV_OSS_DEVICE_TYPE_MUSIC:
		return "sequencer";
	case SNDRV_OSS_DEVICE_TYPE_PCM:
		return "digital audio";
	case SNDRV_OSS_DEVICE_TYPE_MIDI:
		return "raw midi";
	case SNDRV_OSS_DEVICE_TYPE_DMFM:
		return "hardware dependent";
	default:
		return "?";
	}
}

231 232
static void snd_minor_info_oss_read(struct snd_info_entry *entry,
				    struct snd_info_buffer *buffer)
L
Linus Torvalds 已提交
233
{
234
	int minor;
235
	struct snd_minor *mptr;
L
Linus Torvalds 已提交
236

237
	mutex_lock(&sound_oss_mutex);
238 239 240 241 242 243 244 245 246 247
	for (minor = 0; minor < SNDRV_OSS_MINORS; ++minor) {
		if (!(mptr = snd_oss_minors[minor]))
			continue;
		if (mptr->card >= 0)
			snd_iprintf(buffer, "%3i: [%i-%2i]: %s\n", minor,
				    mptr->card, mptr->device,
				    snd_oss_device_type_name(mptr->type));
		else
			snd_iprintf(buffer, "%3i:       : %s\n", minor,
				    snd_oss_device_type_name(mptr->type));
L
Linus Torvalds 已提交
248
	}
249
	mutex_unlock(&sound_oss_mutex);
L
Linus Torvalds 已提交
250 251 252 253 254
}


int __init snd_minor_info_oss_init(void)
{
255
	struct snd_info_entry *entry;
L
Linus Torvalds 已提交
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270

	entry = snd_info_create_module_entry(THIS_MODULE, "devices", snd_oss_root);
	if (entry) {
		entry->c.text.read = snd_minor_info_oss_read;
		if (snd_info_register(entry) < 0) {
			snd_info_free_entry(entry);
			entry = NULL;
		}
	}
	snd_minor_info_oss_entry = entry;
	return 0;
}

int __exit snd_minor_info_oss_done(void)
{
271
	snd_info_free_entry(snd_minor_info_oss_entry);
L
Linus Torvalds 已提交
272 273
	return 0;
}
274
#endif /* CONFIG_PROC_FS */
L
Linus Torvalds 已提交
275 276

#endif /* CONFIG_SND_OSSEMUL */