sound_oss.c 7.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2
/*
 *  Advanced Linux Sound Architecture
3
 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
L
Linus Torvalds 已提交
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
 *
 *
 *   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
 *
 */

#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>
35
#include <linux/mutex.h>
L
Linus Torvalds 已提交
36

37
#define SNDRV_OSS_MINORS 128
L
Linus Torvalds 已提交
38

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

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

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

59 60
EXPORT_SYMBOL(snd_lookup_oss_minor_data);

61
static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev)
L
Linus Torvalds 已提交
62 63 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
{
	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;
	}
93
	snd_assert(minor >= 0 && minor < SNDRV_OSS_MINORS, return -EINVAL);
L
Linus Torvalds 已提交
94 95 96
	return minor;
}

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

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

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

159 160
EXPORT_SYMBOL(snd_register_oss_device);

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

168 169
	if (card && card->number >= 8)
		return 0;
L
Linus Torvalds 已提交
170 171
	if (minor < 0)
		return minor;
172
	mutex_lock(&sound_oss_mutex);
173
	mptr = snd_oss_minors[minor];
L
Linus Torvalds 已提交
174
	if (mptr == NULL) {
175
		mutex_unlock(&sound_oss_mutex);
L
Linus Torvalds 已提交
176 177 178 179 180 181 182 183 184 185 186 187 188 189
		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;
	}
190
	if (track2 >= 0) {
L
Linus Torvalds 已提交
191
		unregister_sound_special(track2);
192 193
		snd_oss_minors[track2] = NULL;
	}
194
	snd_oss_minors[minor] = NULL;
195
	mutex_unlock(&sound_oss_mutex);
L
Linus Torvalds 已提交
196 197 198 199
	kfree(mptr);
	return 0;
}

200 201
EXPORT_SYMBOL(snd_unregister_oss_device);

L
Linus Torvalds 已提交
202 203 204 205 206 207
/*
 *  INFO PART
 */

#ifdef CONFIG_PROC_FS

208
static struct snd_info_entry *snd_minor_info_oss_entry;
L
Linus Torvalds 已提交
209

210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
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 "?";
	}
}

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

235
	mutex_lock(&sound_oss_mutex);
236 237 238 239 240 241 242 243 244 245
	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 已提交
246
	}
247
	mutex_unlock(&sound_oss_mutex);
L
Linus Torvalds 已提交
248 249 250 251 252
}


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

	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)
{
269
	snd_info_free_entry(snd_minor_info_oss_entry);
L
Linus Torvalds 已提交
270 271
	return 0;
}
272
#endif /* CONFIG_PROC_FS */
L
Linus Torvalds 已提交
273 274

#endif /* CONFIG_SND_OSSEMUL */