// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Initialization routines
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*/
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/file.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/ctype.h>
#include <linux/pm.h>
#include <linux/debugfs.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/info.h>
/* monitor files for graceful shutdown (hotplug) */
struct snd_monitor_file {
struct file *file;
const struct file_operations *disconnected_f_op;
struct list_head shutdown_list; /* still need to shutdown */
struct list_head list; /* link of monitor files */
};
static DEFINE_SPINLOCK(shutdown_lock);
static LIST_HEAD(shutdown_files);
static const struct file_operations snd_shutdown_f_ops;
/* locked for registering/using */
static DECLARE_BITMAP(snd_cards_lock, SNDRV_CARDS);
static struct snd_card *snd_cards[SNDRV_CARDS];
static DEFINE_MUTEX(snd_card_mutex);
static char *slots[SNDRV_CARDS];
module_param_array(slots, charp, NULL, 0444);
MODULE_PARM_DESC(slots, "Module names assigned to the slots.");
/* return non-zero if the given index is reserved for the given
* module via slots option
*/
static int module_slot_match(struct module *module, int idx)
{
int match = 1;
#ifdef MODULE
const char *s1, *s2;
if (!module || !*module->name || !slots[idx])
return 0;
s1 = module->name;
s2 = slots[idx];
if (*s2 == '!') {
match = 0; /* negative match */
s2++;
}
/* compare module name strings
* hyphens are handled as equivalent with underscore
*/
for (;;) {
char c1 = *s1++;
char c2 = *s2++;
if (c1 == '-')
c1 = '_';
if (c2 == '-')
c2 = '_';
if (c1 != c2)
return !match;
if (!c1)
break;
}
#endif /* MODULE */
return match;
}
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag);
EXPORT_SYMBOL(snd_mixer_oss_notify_callback);
#endif
static int check_empty_slot(struct module *module, int slot)
{
return !slots[slot] || !*slots[slot];
}
/* return an empty slot number (>= 0) found in the given bitmask @mask.
* @mask == -1 == 0xffffffff means: take any free slot up to 32
* when no slot is available, return the original @mask as is.
*/
static int get_slot_from_bitmask(int mask, int (*check)(struct module *, int),
struct module *module)
{
int slot;
for (slot = 0; slot < SNDRV_CARDS; slot++) {
if (slot < 32 && !(mask & (1U << slot)))
continue;
if (!test_bit(slot, snd_cards_lock)) {
if (check(module, slot))
return slot; /* found */
}
}
return mask; /* unchanged */
}
/* the default release callback set in snd_device_initialize() below;
* this is just NOP for now, as almost all jobs are already done in
* dev_free callback of snd_device chain instead.
*/
static void default_release(struct device *dev)
{
}
/**
* snd_device_initialize - Initialize struct device for sound devices
* @dev: device to initialize
* @card: card to assign, optional
*/
void snd_device_initialize(struct device *dev, struct snd_card *card)
{
device_initialize(dev);
if (card)
dev->parent = &card->card_dev;
dev->class = sound_class;
dev->release = default_release;
}
EXPORT_SYMBOL_GPL(snd_device_initialize);
static int snd_card_do_free(struct snd_card *card);
static const struct attribute_group card_dev_attr_group;
static void release_card_device(struct device *dev)
{
snd_card_do_free(dev_to_snd_card(dev));
}
/**
* snd_card_new - create and initialize a soundcard structure
* @parent: the parent device object
* @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
* @xid: card identification (ASCII string)
* @module: top level module for locking
* @extra_size: allocate this extra size after the main soundcard structure
* @card_ret: the pointer to store the created card instance
*
* The function allocates snd_card instance via kzalloc with the given
* space for the driver to use freely. The allocated struct is stored
* in the given card_ret pointer.
*
* Return: Zero if successful or a negative error code.
*/
int snd_card_new(struct device *parent, int idx, const char *xid,
struct module *module, int extra_size,
struct snd_card **card_ret)
{
struct snd_card *card;
int err;
#ifdef CONFIG_SND_DEBUG
char name[8];
#endif
if (snd_BUG_ON(!card_ret))
return -EINVAL;
*card_ret = NULL;
if (extra_size < 0)
extra_size = 0;
card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
if (!card)
return -ENOMEM;
if (extra_size > 0)
card->private_data = (char *)card + sizeof(struct snd_card);
if (xid)
strscpy(card->id,