11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Advanced Linux Sound Architecture
4c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
51da177e4SLinus Torvalds */
61da177e4SLinus Torvalds
71da177e4SLinus Torvalds #include <linux/init.h>
8d81a6d71SPaul Gortmaker #include <linux/export.h>
91da177e4SLinus Torvalds #include <linux/slab.h>
101da177e4SLinus Torvalds #include <linux/time.h>
111da177e4SLinus Torvalds #include <sound/core.h>
121da177e4SLinus Torvalds #include <sound/minors.h>
131da177e4SLinus Torvalds #include <sound/info.h>
141da177e4SLinus Torvalds #include <linux/sound.h>
151a60d4c5SIngo Molnar #include <linux/mutex.h>
161da177e4SLinus Torvalds
17779ae5a0STakashi Iwai #define SNDRV_OSS_MINORS 256
181da177e4SLinus Torvalds
196983b724SClemens Ladisch static struct snd_minor *snd_oss_minors[SNDRV_OSS_MINORS];
201a60d4c5SIngo Molnar static DEFINE_MUTEX(sound_oss_mutex);
211da177e4SLinus Torvalds
22a0830dbdSTakashi Iwai /* NOTE: This function increments the refcount of the associated card like
23a0830dbdSTakashi Iwai * snd_lookup_minor_data(); the caller must call snd_card_unref() appropriately
24a0830dbdSTakashi Iwai */
snd_lookup_oss_minor_data(unsigned int minor,int type)25f87135f5SClemens Ladisch void *snd_lookup_oss_minor_data(unsigned int minor, int type)
26f87135f5SClemens Ladisch {
27f87135f5SClemens Ladisch struct snd_minor *mreg;
28f87135f5SClemens Ladisch void *private_data;
29f87135f5SClemens Ladisch
303a63e444SAdrian Bunk if (minor >= ARRAY_SIZE(snd_oss_minors))
31f87135f5SClemens Ladisch return NULL;
321a60d4c5SIngo Molnar mutex_lock(&sound_oss_mutex);
33f87135f5SClemens Ladisch mreg = snd_oss_minors[minor];
34a0830dbdSTakashi Iwai if (mreg && mreg->type == type) {
35f87135f5SClemens Ladisch private_data = mreg->private_data;
368bb4d9ceSTakashi Iwai if (private_data && mreg->card_ptr)
37f2464064STakashi Iwai get_device(&mreg->card_ptr->card_dev);
38a0830dbdSTakashi Iwai } else
39f87135f5SClemens Ladisch private_data = NULL;
401a60d4c5SIngo Molnar mutex_unlock(&sound_oss_mutex);
41f87135f5SClemens Ladisch return private_data;
42f87135f5SClemens Ladisch }
43c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_lookup_oss_minor_data);
44c0d3fb39STakashi Iwai
snd_oss_kernel_minor(int type,struct snd_card * card,int dev)45174c1f65STakashi Iwai static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev)
461da177e4SLinus Torvalds {
471da177e4SLinus Torvalds int minor;
481da177e4SLinus Torvalds
491da177e4SLinus Torvalds switch (type) {
501da177e4SLinus Torvalds case SNDRV_OSS_DEVICE_TYPE_MIXER:
517eaa943cSTakashi Iwai if (snd_BUG_ON(!card || dev < 0 || dev > 1))
527eaa943cSTakashi Iwai return -EINVAL;
531da177e4SLinus Torvalds minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIXER1 : SNDRV_MINOR_OSS_MIXER));
541da177e4SLinus Torvalds break;
551da177e4SLinus Torvalds case SNDRV_OSS_DEVICE_TYPE_SEQUENCER:
561da177e4SLinus Torvalds minor = SNDRV_MINOR_OSS_SEQUENCER;
571da177e4SLinus Torvalds break;
581da177e4SLinus Torvalds case SNDRV_OSS_DEVICE_TYPE_MUSIC:
591da177e4SLinus Torvalds minor = SNDRV_MINOR_OSS_MUSIC;
601da177e4SLinus Torvalds break;
611da177e4SLinus Torvalds case SNDRV_OSS_DEVICE_TYPE_PCM:
627eaa943cSTakashi Iwai if (snd_BUG_ON(!card || dev < 0 || dev > 1))
637eaa943cSTakashi Iwai return -EINVAL;
641da177e4SLinus Torvalds minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_PCM1 : SNDRV_MINOR_OSS_PCM));
651da177e4SLinus Torvalds break;
661da177e4SLinus Torvalds case SNDRV_OSS_DEVICE_TYPE_MIDI:
677eaa943cSTakashi Iwai if (snd_BUG_ON(!card || dev < 0 || dev > 1))
687eaa943cSTakashi Iwai return -EINVAL;
691da177e4SLinus Torvalds minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIDI1 : SNDRV_MINOR_OSS_MIDI));
701da177e4SLinus Torvalds break;
711da177e4SLinus Torvalds case SNDRV_OSS_DEVICE_TYPE_DMFM:
721da177e4SLinus Torvalds minor = SNDRV_MINOR_OSS(card->number, SNDRV_MINOR_OSS_DMFM);
731da177e4SLinus Torvalds break;
741da177e4SLinus Torvalds case SNDRV_OSS_DEVICE_TYPE_SNDSTAT:
751da177e4SLinus Torvalds minor = SNDRV_MINOR_OSS_SNDSTAT;
761da177e4SLinus Torvalds break;
771da177e4SLinus Torvalds default:
781da177e4SLinus Torvalds return -EINVAL;
791da177e4SLinus Torvalds }
80808c569fSTakashi Iwai if (minor < 0 || minor >= SNDRV_OSS_MINORS)
817eaa943cSTakashi Iwai return -EINVAL;
821da177e4SLinus Torvalds return minor;
831da177e4SLinus Torvalds }
841da177e4SLinus Torvalds
snd_register_oss_device(int type,struct snd_card * card,int dev,const struct file_operations * f_ops,void * private_data)85174c1f65STakashi Iwai int snd_register_oss_device(int type, struct snd_card *card, int dev,
8680d7d771STakashi Iwai const struct file_operations *f_ops, void *private_data)
871da177e4SLinus Torvalds {
881da177e4SLinus Torvalds int minor = snd_oss_kernel_minor(type, card, dev);
891da177e4SLinus Torvalds int minor_unit;
90174c1f65STakashi Iwai struct snd_minor *preg;
911da177e4SLinus Torvalds int cidx = SNDRV_MINOR_OSS_CARD(minor);
921da177e4SLinus Torvalds int track2 = -1;
931da177e4SLinus Torvalds int register1 = -1, register2 = -1;
947d2aae1eSTakashi Iwai struct device *carddev = snd_card_get_device_link(card);
951da177e4SLinus Torvalds
96779ae5a0STakashi Iwai if (card && card->number >= SNDRV_MINOR_OSS_DEVICES)
97d001544dSClemens Ladisch return 0; /* ignore silently */
981da177e4SLinus Torvalds if (minor < 0)
991da177e4SLinus Torvalds return minor;
1006983b724SClemens Ladisch preg = kmalloc(sizeof(struct snd_minor), GFP_KERNEL);
1011da177e4SLinus Torvalds if (preg == NULL)
1021da177e4SLinus Torvalds return -ENOMEM;
1032af677fcSClemens Ladisch preg->type = type;
1046983b724SClemens Ladisch preg->card = card ? card->number : -1;
1051da177e4SLinus Torvalds preg->device = dev;
1062af677fcSClemens Ladisch preg->f_ops = f_ops;
107f87135f5SClemens Ladisch preg->private_data = private_data;
108a0830dbdSTakashi Iwai preg->card_ptr = card;
1091a60d4c5SIngo Molnar mutex_lock(&sound_oss_mutex);
1106983b724SClemens Ladisch snd_oss_minors[minor] = preg;
1111da177e4SLinus Torvalds minor_unit = SNDRV_MINOR_OSS_DEVICE(minor);
1121da177e4SLinus Torvalds switch (minor_unit) {
1131da177e4SLinus Torvalds case SNDRV_MINOR_OSS_PCM:
1141da177e4SLinus Torvalds track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO);
1151da177e4SLinus Torvalds break;
1161da177e4SLinus Torvalds case SNDRV_MINOR_OSS_MIDI:
1171da177e4SLinus Torvalds track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI);
1181da177e4SLinus Torvalds break;
1191da177e4SLinus Torvalds case SNDRV_MINOR_OSS_MIDI1:
1201da177e4SLinus Torvalds track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1);
1211da177e4SLinus Torvalds break;
1221da177e4SLinus Torvalds }
1232af677fcSClemens Ladisch register1 = register_sound_special_device(f_ops, minor, carddev);
1241da177e4SLinus Torvalds if (register1 != minor)
1251da177e4SLinus Torvalds goto __end;
1261da177e4SLinus Torvalds if (track2 >= 0) {
1272af677fcSClemens Ladisch register2 = register_sound_special_device(f_ops, track2,
1282af677fcSClemens Ladisch carddev);
1291da177e4SLinus Torvalds if (register2 != track2)
1301da177e4SLinus Torvalds goto __end;
131f87135f5SClemens Ladisch snd_oss_minors[track2] = preg;
1321da177e4SLinus Torvalds }
1331a60d4c5SIngo Molnar mutex_unlock(&sound_oss_mutex);
1341da177e4SLinus Torvalds return 0;
1351da177e4SLinus Torvalds
1361da177e4SLinus Torvalds __end:
1371da177e4SLinus Torvalds if (register2 >= 0)
1381da177e4SLinus Torvalds unregister_sound_special(register2);
1391da177e4SLinus Torvalds if (register1 >= 0)
1401da177e4SLinus Torvalds unregister_sound_special(register1);
1416983b724SClemens Ladisch snd_oss_minors[minor] = NULL;
1421a60d4c5SIngo Molnar mutex_unlock(&sound_oss_mutex);
1431da177e4SLinus Torvalds kfree(preg);
1441da177e4SLinus Torvalds return -EBUSY;
1451da177e4SLinus Torvalds }
146c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_register_oss_device);
147c0d3fb39STakashi Iwai
snd_unregister_oss_device(int type,struct snd_card * card,int dev)148174c1f65STakashi Iwai int snd_unregister_oss_device(int type, struct snd_card *card, int dev)
1491da177e4SLinus Torvalds {
1501da177e4SLinus Torvalds int minor = snd_oss_kernel_minor(type, card, dev);
1511da177e4SLinus Torvalds int cidx = SNDRV_MINOR_OSS_CARD(minor);
1521da177e4SLinus Torvalds int track2 = -1;
153174c1f65STakashi Iwai struct snd_minor *mptr;
1541da177e4SLinus Torvalds
155779ae5a0STakashi Iwai if (card && card->number >= SNDRV_MINOR_OSS_DEVICES)
156d001544dSClemens Ladisch return 0;
1571da177e4SLinus Torvalds if (minor < 0)
1581da177e4SLinus Torvalds return minor;
1591a60d4c5SIngo Molnar mutex_lock(&sound_oss_mutex);
1606983b724SClemens Ladisch mptr = snd_oss_minors[minor];
1611da177e4SLinus Torvalds if (mptr == NULL) {
1621a60d4c5SIngo Molnar mutex_unlock(&sound_oss_mutex);
1631da177e4SLinus Torvalds return -ENOENT;
1641da177e4SLinus Torvalds }
1651da177e4SLinus Torvalds switch (SNDRV_MINOR_OSS_DEVICE(minor)) {
1661da177e4SLinus Torvalds case SNDRV_MINOR_OSS_PCM:
1671da177e4SLinus Torvalds track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO);
1681da177e4SLinus Torvalds break;
1691da177e4SLinus Torvalds case SNDRV_MINOR_OSS_MIDI:
1701da177e4SLinus Torvalds track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI);
1711da177e4SLinus Torvalds break;
1721da177e4SLinus Torvalds case SNDRV_MINOR_OSS_MIDI1:
1731da177e4SLinus Torvalds track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1);
1741da177e4SLinus Torvalds break;
1751da177e4SLinus Torvalds }
176*97d91787STakashi Iwai if (track2 >= 0)
177f87135f5SClemens Ladisch snd_oss_minors[track2] = NULL;
1786983b724SClemens Ladisch snd_oss_minors[minor] = NULL;
1791a60d4c5SIngo Molnar mutex_unlock(&sound_oss_mutex);
180*97d91787STakashi Iwai
181*97d91787STakashi Iwai /* call unregister_sound_special() outside sound_oss_mutex;
182*97d91787STakashi Iwai * otherwise may deadlock, as it can trigger the release of a card
183*97d91787STakashi Iwai */
184*97d91787STakashi Iwai unregister_sound_special(minor);
185*97d91787STakashi Iwai if (track2 >= 0)
186*97d91787STakashi Iwai unregister_sound_special(track2);
187*97d91787STakashi Iwai
1881da177e4SLinus Torvalds kfree(mptr);
1891da177e4SLinus Torvalds return 0;
1901da177e4SLinus Torvalds }
191c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_unregister_oss_device);
192c0d3fb39STakashi Iwai
1931da177e4SLinus Torvalds /*
1941da177e4SLinus Torvalds * INFO PART
1951da177e4SLinus Torvalds */
1961da177e4SLinus Torvalds
197cd6a6503SJie Yang #ifdef CONFIG_SND_PROC_FS
snd_oss_device_type_name(int type)1982af677fcSClemens Ladisch static const char *snd_oss_device_type_name(int type)
1992af677fcSClemens Ladisch {
2002af677fcSClemens Ladisch switch (type) {
2012af677fcSClemens Ladisch case SNDRV_OSS_DEVICE_TYPE_MIXER:
2022af677fcSClemens Ladisch return "mixer";
2032af677fcSClemens Ladisch case SNDRV_OSS_DEVICE_TYPE_SEQUENCER:
2042af677fcSClemens Ladisch case SNDRV_OSS_DEVICE_TYPE_MUSIC:
2052af677fcSClemens Ladisch return "sequencer";
2062af677fcSClemens Ladisch case SNDRV_OSS_DEVICE_TYPE_PCM:
2072af677fcSClemens Ladisch return "digital audio";
2082af677fcSClemens Ladisch case SNDRV_OSS_DEVICE_TYPE_MIDI:
2092af677fcSClemens Ladisch return "raw midi";
2102af677fcSClemens Ladisch case SNDRV_OSS_DEVICE_TYPE_DMFM:
2112af677fcSClemens Ladisch return "hardware dependent";
2122af677fcSClemens Ladisch default:
2132af677fcSClemens Ladisch return "?";
2142af677fcSClemens Ladisch }
2152af677fcSClemens Ladisch }
2162af677fcSClemens Ladisch
snd_minor_info_oss_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)217174c1f65STakashi Iwai static void snd_minor_info_oss_read(struct snd_info_entry *entry,
218174c1f65STakashi Iwai struct snd_info_buffer *buffer)
2191da177e4SLinus Torvalds {
2206983b724SClemens Ladisch int minor;
221174c1f65STakashi Iwai struct snd_minor *mptr;
2221da177e4SLinus Torvalds
2231a60d4c5SIngo Molnar mutex_lock(&sound_oss_mutex);
2246983b724SClemens Ladisch for (minor = 0; minor < SNDRV_OSS_MINORS; ++minor) {
225e3ded899STakashi Iwai mptr = snd_oss_minors[minor];
226e3ded899STakashi Iwai if (!mptr)
2276983b724SClemens Ladisch continue;
2286983b724SClemens Ladisch if (mptr->card >= 0)
2296983b724SClemens Ladisch snd_iprintf(buffer, "%3i: [%i-%2i]: %s\n", minor,
2306983b724SClemens Ladisch mptr->card, mptr->device,
2316983b724SClemens Ladisch snd_oss_device_type_name(mptr->type));
2321da177e4SLinus Torvalds else
2336983b724SClemens Ladisch snd_iprintf(buffer, "%3i: : %s\n", minor,
2346983b724SClemens Ladisch snd_oss_device_type_name(mptr->type));
2351da177e4SLinus Torvalds }
2361a60d4c5SIngo Molnar mutex_unlock(&sound_oss_mutex);
2371da177e4SLinus Torvalds }
2381da177e4SLinus Torvalds
2391da177e4SLinus Torvalds
snd_minor_info_oss_init(void)2401da177e4SLinus Torvalds int __init snd_minor_info_oss_init(void)
2411da177e4SLinus Torvalds {
242174c1f65STakashi Iwai struct snd_info_entry *entry;
2431da177e4SLinus Torvalds
2441da177e4SLinus Torvalds entry = snd_info_create_module_entry(THIS_MODULE, "devices", snd_oss_root);
245b591b6e9STakashi Iwai if (!entry)
246b591b6e9STakashi Iwai return -ENOMEM;
2471da177e4SLinus Torvalds entry->c.text.read = snd_minor_info_oss_read;
248b591b6e9STakashi Iwai return snd_info_register(entry); /* freed in error path */
2491da177e4SLinus Torvalds }
250cd6a6503SJie Yang #endif /* CONFIG_SND_PROC_FS */
251