18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Advanced Linux Sound Architecture
48c2ecf20Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/init.h>
88c2ecf20Sopenharmony_ci#include <linux/export.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include <linux/time.h>
118c2ecf20Sopenharmony_ci#include <sound/core.h>
128c2ecf20Sopenharmony_ci#include <sound/minors.h>
138c2ecf20Sopenharmony_ci#include <sound/info.h>
148c2ecf20Sopenharmony_ci#include <linux/sound.h>
158c2ecf20Sopenharmony_ci#include <linux/mutex.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define SNDRV_OSS_MINORS 256
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic struct snd_minor *snd_oss_minors[SNDRV_OSS_MINORS];
208c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(sound_oss_mutex);
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* NOTE: This function increments the refcount of the associated card like
238c2ecf20Sopenharmony_ci * snd_lookup_minor_data(); the caller must call snd_card_unref() appropriately
248c2ecf20Sopenharmony_ci */
258c2ecf20Sopenharmony_civoid *snd_lookup_oss_minor_data(unsigned int minor, int type)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	struct snd_minor *mreg;
288c2ecf20Sopenharmony_ci	void *private_data;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	if (minor >= ARRAY_SIZE(snd_oss_minors))
318c2ecf20Sopenharmony_ci		return NULL;
328c2ecf20Sopenharmony_ci	mutex_lock(&sound_oss_mutex);
338c2ecf20Sopenharmony_ci	mreg = snd_oss_minors[minor];
348c2ecf20Sopenharmony_ci	if (mreg && mreg->type == type) {
358c2ecf20Sopenharmony_ci		private_data = mreg->private_data;
368c2ecf20Sopenharmony_ci		if (private_data && mreg->card_ptr)
378c2ecf20Sopenharmony_ci			get_device(&mreg->card_ptr->card_dev);
388c2ecf20Sopenharmony_ci	} else
398c2ecf20Sopenharmony_ci		private_data = NULL;
408c2ecf20Sopenharmony_ci	mutex_unlock(&sound_oss_mutex);
418c2ecf20Sopenharmony_ci	return private_data;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_lookup_oss_minor_data);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic int snd_oss_kernel_minor(int type, struct snd_card *card, int dev)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	int minor;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	switch (type) {
508c2ecf20Sopenharmony_ci	case SNDRV_OSS_DEVICE_TYPE_MIXER:
518c2ecf20Sopenharmony_ci		if (snd_BUG_ON(!card || dev < 0 || dev > 1))
528c2ecf20Sopenharmony_ci			return -EINVAL;
538c2ecf20Sopenharmony_ci		minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIXER1 : SNDRV_MINOR_OSS_MIXER));
548c2ecf20Sopenharmony_ci		break;
558c2ecf20Sopenharmony_ci	case SNDRV_OSS_DEVICE_TYPE_SEQUENCER:
568c2ecf20Sopenharmony_ci		minor = SNDRV_MINOR_OSS_SEQUENCER;
578c2ecf20Sopenharmony_ci		break;
588c2ecf20Sopenharmony_ci	case SNDRV_OSS_DEVICE_TYPE_MUSIC:
598c2ecf20Sopenharmony_ci		minor = SNDRV_MINOR_OSS_MUSIC;
608c2ecf20Sopenharmony_ci		break;
618c2ecf20Sopenharmony_ci	case SNDRV_OSS_DEVICE_TYPE_PCM:
628c2ecf20Sopenharmony_ci		if (snd_BUG_ON(!card || dev < 0 || dev > 1))
638c2ecf20Sopenharmony_ci			return -EINVAL;
648c2ecf20Sopenharmony_ci		minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_PCM1 : SNDRV_MINOR_OSS_PCM));
658c2ecf20Sopenharmony_ci		break;
668c2ecf20Sopenharmony_ci	case SNDRV_OSS_DEVICE_TYPE_MIDI:
678c2ecf20Sopenharmony_ci		if (snd_BUG_ON(!card || dev < 0 || dev > 1))
688c2ecf20Sopenharmony_ci			return -EINVAL;
698c2ecf20Sopenharmony_ci		minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIDI1 : SNDRV_MINOR_OSS_MIDI));
708c2ecf20Sopenharmony_ci		break;
718c2ecf20Sopenharmony_ci	case SNDRV_OSS_DEVICE_TYPE_DMFM:
728c2ecf20Sopenharmony_ci		minor = SNDRV_MINOR_OSS(card->number, SNDRV_MINOR_OSS_DMFM);
738c2ecf20Sopenharmony_ci		break;
748c2ecf20Sopenharmony_ci	case SNDRV_OSS_DEVICE_TYPE_SNDSTAT:
758c2ecf20Sopenharmony_ci		minor = SNDRV_MINOR_OSS_SNDSTAT;
768c2ecf20Sopenharmony_ci		break;
778c2ecf20Sopenharmony_ci	default:
788c2ecf20Sopenharmony_ci		return -EINVAL;
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci	if (minor < 0 || minor >= SNDRV_OSS_MINORS)
818c2ecf20Sopenharmony_ci		return -EINVAL;
828c2ecf20Sopenharmony_ci	return minor;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ciint snd_register_oss_device(int type, struct snd_card *card, int dev,
868c2ecf20Sopenharmony_ci			    const struct file_operations *f_ops, void *private_data)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	int minor = snd_oss_kernel_minor(type, card, dev);
898c2ecf20Sopenharmony_ci	int minor_unit;
908c2ecf20Sopenharmony_ci	struct snd_minor *preg;
918c2ecf20Sopenharmony_ci	int cidx = SNDRV_MINOR_OSS_CARD(minor);
928c2ecf20Sopenharmony_ci	int track2 = -1;
938c2ecf20Sopenharmony_ci	int register1 = -1, register2 = -1;
948c2ecf20Sopenharmony_ci	struct device *carddev = snd_card_get_device_link(card);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (card && card->number >= SNDRV_MINOR_OSS_DEVICES)
978c2ecf20Sopenharmony_ci		return 0; /* ignore silently */
988c2ecf20Sopenharmony_ci	if (minor < 0)
998c2ecf20Sopenharmony_ci		return minor;
1008c2ecf20Sopenharmony_ci	preg = kmalloc(sizeof(struct snd_minor), GFP_KERNEL);
1018c2ecf20Sopenharmony_ci	if (preg == NULL)
1028c2ecf20Sopenharmony_ci		return -ENOMEM;
1038c2ecf20Sopenharmony_ci	preg->type = type;
1048c2ecf20Sopenharmony_ci	preg->card = card ? card->number : -1;
1058c2ecf20Sopenharmony_ci	preg->device = dev;
1068c2ecf20Sopenharmony_ci	preg->f_ops = f_ops;
1078c2ecf20Sopenharmony_ci	preg->private_data = private_data;
1088c2ecf20Sopenharmony_ci	preg->card_ptr = card;
1098c2ecf20Sopenharmony_ci	mutex_lock(&sound_oss_mutex);
1108c2ecf20Sopenharmony_ci	snd_oss_minors[minor] = preg;
1118c2ecf20Sopenharmony_ci	minor_unit = SNDRV_MINOR_OSS_DEVICE(minor);
1128c2ecf20Sopenharmony_ci	switch (minor_unit) {
1138c2ecf20Sopenharmony_ci	case SNDRV_MINOR_OSS_PCM:
1148c2ecf20Sopenharmony_ci		track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO);
1158c2ecf20Sopenharmony_ci		break;
1168c2ecf20Sopenharmony_ci	case SNDRV_MINOR_OSS_MIDI:
1178c2ecf20Sopenharmony_ci		track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI);
1188c2ecf20Sopenharmony_ci		break;
1198c2ecf20Sopenharmony_ci	case SNDRV_MINOR_OSS_MIDI1:
1208c2ecf20Sopenharmony_ci		track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1);
1218c2ecf20Sopenharmony_ci		break;
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci	register1 = register_sound_special_device(f_ops, minor, carddev);
1248c2ecf20Sopenharmony_ci	if (register1 != minor)
1258c2ecf20Sopenharmony_ci		goto __end;
1268c2ecf20Sopenharmony_ci	if (track2 >= 0) {
1278c2ecf20Sopenharmony_ci		register2 = register_sound_special_device(f_ops, track2,
1288c2ecf20Sopenharmony_ci							  carddev);
1298c2ecf20Sopenharmony_ci		if (register2 != track2)
1308c2ecf20Sopenharmony_ci			goto __end;
1318c2ecf20Sopenharmony_ci		snd_oss_minors[track2] = preg;
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci	mutex_unlock(&sound_oss_mutex);
1348c2ecf20Sopenharmony_ci	return 0;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci      __end:
1378c2ecf20Sopenharmony_ci      	if (register2 >= 0)
1388c2ecf20Sopenharmony_ci      		unregister_sound_special(register2);
1398c2ecf20Sopenharmony_ci      	if (register1 >= 0)
1408c2ecf20Sopenharmony_ci      		unregister_sound_special(register1);
1418c2ecf20Sopenharmony_ci	snd_oss_minors[minor] = NULL;
1428c2ecf20Sopenharmony_ci	mutex_unlock(&sound_oss_mutex);
1438c2ecf20Sopenharmony_ci	kfree(preg);
1448c2ecf20Sopenharmony_ci      	return -EBUSY;
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_register_oss_device);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ciint snd_unregister_oss_device(int type, struct snd_card *card, int dev)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	int minor = snd_oss_kernel_minor(type, card, dev);
1518c2ecf20Sopenharmony_ci	int cidx = SNDRV_MINOR_OSS_CARD(minor);
1528c2ecf20Sopenharmony_ci	int track2 = -1;
1538c2ecf20Sopenharmony_ci	struct snd_minor *mptr;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	if (card && card->number >= SNDRV_MINOR_OSS_DEVICES)
1568c2ecf20Sopenharmony_ci		return 0;
1578c2ecf20Sopenharmony_ci	if (minor < 0)
1588c2ecf20Sopenharmony_ci		return minor;
1598c2ecf20Sopenharmony_ci	mutex_lock(&sound_oss_mutex);
1608c2ecf20Sopenharmony_ci	mptr = snd_oss_minors[minor];
1618c2ecf20Sopenharmony_ci	if (mptr == NULL) {
1628c2ecf20Sopenharmony_ci		mutex_unlock(&sound_oss_mutex);
1638c2ecf20Sopenharmony_ci		return -ENOENT;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci	switch (SNDRV_MINOR_OSS_DEVICE(minor)) {
1668c2ecf20Sopenharmony_ci	case SNDRV_MINOR_OSS_PCM:
1678c2ecf20Sopenharmony_ci		track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO);
1688c2ecf20Sopenharmony_ci		break;
1698c2ecf20Sopenharmony_ci	case SNDRV_MINOR_OSS_MIDI:
1708c2ecf20Sopenharmony_ci		track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI);
1718c2ecf20Sopenharmony_ci		break;
1728c2ecf20Sopenharmony_ci	case SNDRV_MINOR_OSS_MIDI1:
1738c2ecf20Sopenharmony_ci		track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1);
1748c2ecf20Sopenharmony_ci		break;
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci	if (track2 >= 0)
1778c2ecf20Sopenharmony_ci		snd_oss_minors[track2] = NULL;
1788c2ecf20Sopenharmony_ci	snd_oss_minors[minor] = NULL;
1798c2ecf20Sopenharmony_ci	mutex_unlock(&sound_oss_mutex);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	/* call unregister_sound_special() outside sound_oss_mutex;
1828c2ecf20Sopenharmony_ci	 * otherwise may deadlock, as it can trigger the release of a card
1838c2ecf20Sopenharmony_ci	 */
1848c2ecf20Sopenharmony_ci	unregister_sound_special(minor);
1858c2ecf20Sopenharmony_ci	if (track2 >= 0)
1868c2ecf20Sopenharmony_ci		unregister_sound_special(track2);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	kfree(mptr);
1898c2ecf20Sopenharmony_ci	return 0;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_unregister_oss_device);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci/*
1948c2ecf20Sopenharmony_ci *  INFO PART
1958c2ecf20Sopenharmony_ci */
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_PROC_FS
1988c2ecf20Sopenharmony_cistatic const char *snd_oss_device_type_name(int type)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	switch (type) {
2018c2ecf20Sopenharmony_ci	case SNDRV_OSS_DEVICE_TYPE_MIXER:
2028c2ecf20Sopenharmony_ci		return "mixer";
2038c2ecf20Sopenharmony_ci	case SNDRV_OSS_DEVICE_TYPE_SEQUENCER:
2048c2ecf20Sopenharmony_ci	case SNDRV_OSS_DEVICE_TYPE_MUSIC:
2058c2ecf20Sopenharmony_ci		return "sequencer";
2068c2ecf20Sopenharmony_ci	case SNDRV_OSS_DEVICE_TYPE_PCM:
2078c2ecf20Sopenharmony_ci		return "digital audio";
2088c2ecf20Sopenharmony_ci	case SNDRV_OSS_DEVICE_TYPE_MIDI:
2098c2ecf20Sopenharmony_ci		return "raw midi";
2108c2ecf20Sopenharmony_ci	case SNDRV_OSS_DEVICE_TYPE_DMFM:
2118c2ecf20Sopenharmony_ci		return "hardware dependent";
2128c2ecf20Sopenharmony_ci	default:
2138c2ecf20Sopenharmony_ci		return "?";
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic void snd_minor_info_oss_read(struct snd_info_entry *entry,
2188c2ecf20Sopenharmony_ci				    struct snd_info_buffer *buffer)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	int minor;
2218c2ecf20Sopenharmony_ci	struct snd_minor *mptr;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	mutex_lock(&sound_oss_mutex);
2248c2ecf20Sopenharmony_ci	for (minor = 0; minor < SNDRV_OSS_MINORS; ++minor) {
2258c2ecf20Sopenharmony_ci		if (!(mptr = snd_oss_minors[minor]))
2268c2ecf20Sopenharmony_ci			continue;
2278c2ecf20Sopenharmony_ci		if (mptr->card >= 0)
2288c2ecf20Sopenharmony_ci			snd_iprintf(buffer, "%3i: [%i-%2i]: %s\n", minor,
2298c2ecf20Sopenharmony_ci				    mptr->card, mptr->device,
2308c2ecf20Sopenharmony_ci				    snd_oss_device_type_name(mptr->type));
2318c2ecf20Sopenharmony_ci		else
2328c2ecf20Sopenharmony_ci			snd_iprintf(buffer, "%3i:       : %s\n", minor,
2338c2ecf20Sopenharmony_ci				    snd_oss_device_type_name(mptr->type));
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci	mutex_unlock(&sound_oss_mutex);
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ciint __init snd_minor_info_oss_init(void)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	struct snd_info_entry *entry;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	entry = snd_info_create_module_entry(THIS_MODULE, "devices", snd_oss_root);
2448c2ecf20Sopenharmony_ci	if (!entry)
2458c2ecf20Sopenharmony_ci		return -ENOMEM;
2468c2ecf20Sopenharmony_ci	entry->c.text.read = snd_minor_info_oss_read;
2478c2ecf20Sopenharmony_ci	return snd_info_register(entry); /* freed in error path */
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci#endif /* CONFIG_SND_PROC_FS */
250