Skip to content

Commit

Permalink
ALSA: control - add layer registration routines
Browse files Browse the repository at this point in the history
The layer registration allows to handle an extra functionality
on top of the control API. It can be used for the audio
LED control for example.

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20210317172945.842280-3-perex@perex.cz
Signed-off-by: Takashi Iwai <tiwai@suse.de>
  • Loading branch information
Jaroslav Kysela authored and Takashi Iwai committed Mar 30, 2021
1 parent 1fa4445 commit 3f0638a
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 2 deletions.
12 changes: 12 additions & 0 deletions include/sound/control.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,14 @@ struct snd_ctl_file {
struct list_head events; /* waiting events for read */
};

struct snd_ctl_layer_ops {
struct snd_ctl_layer_ops *next;
const char *module_name;
void (*lregister)(struct snd_card *card);
void (*ldisconnect)(struct snd_card *card);
void (*lnotify)(struct snd_card *card, unsigned int mask, struct snd_kcontrol *kctl, unsigned int ioff);
};

#define snd_ctl_file(n) list_entry(n, struct snd_ctl_file, list)

typedef int (*snd_kctl_ioctl_func_t) (struct snd_card * card,
Expand Down Expand Up @@ -140,6 +148,10 @@ int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn);
#define snd_ctl_unregister_ioctl_compat(fcn)
#endif

int snd_ctl_request_layer(const char *module_name);
void snd_ctl_register_layer(struct snd_ctl_layer_ops *lops);
void snd_ctl_disconnect_layer(struct snd_ctl_layer_ops *lops);

int snd_ctl_get_preferred_subdevice(struct snd_card *card, int type);

static inline unsigned int snd_ctl_get_ioffnum(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id)
Expand Down
110 changes: 108 additions & 2 deletions sound/core/control.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ struct snd_kctl_ioctl {
};

static DECLARE_RWSEM(snd_ioctl_rwsem);
static DECLARE_RWSEM(snd_ctl_layer_rwsem);
static LIST_HEAD(snd_control_ioctls);
#ifdef CONFIG_COMPAT
static LIST_HEAD(snd_control_compat_ioctls);
#endif
static struct snd_ctl_layer_ops *snd_ctl_layer;

static int snd_ctl_open(struct inode *inode, struct file *file)
{
Expand Down Expand Up @@ -195,10 +197,15 @@ void snd_ctl_notify_one(struct snd_card *card, unsigned int mask,
struct snd_kcontrol *kctl, unsigned int ioff)
{
struct snd_ctl_elem_id id = kctl->id;
struct snd_ctl_layer_ops *lops;

id.index += ioff;
id.numid += ioff;
snd_ctl_notify(card, mask, &id);
down_read(&snd_ctl_layer_rwsem);
for (lops = snd_ctl_layer; lops; lops = lops->next)
lops->lnotify(card, mask, kctl, ioff);
up_read(&snd_ctl_layer_rwsem);
}
EXPORT_SYMBOL(snd_ctl_notify_one);

Expand Down Expand Up @@ -1997,6 +2004,86 @@ EXPORT_SYMBOL_GPL(snd_ctl_get_preferred_subdevice);
#define snd_ctl_ioctl_compat NULL
#endif

/*
* control layers (audio LED etc.)
*/

/**
* snd_ctl_request_layer - request to use the layer
* @module_name: Name of the kernel module (NULL == build-in)
*
* Return an error code when the module cannot be loaded.
*/
int snd_ctl_request_layer(const char *module_name)
{
struct snd_ctl_layer_ops *lops;

if (module_name == NULL)
return 0;
down_read(&snd_ctl_layer_rwsem);
for (lops = snd_ctl_layer; lops; lops = lops->next)
if (strcmp(lops->module_name, module_name) == 0)
break;
up_read(&snd_ctl_layer_rwsem);
if (lops)
return 0;
return request_module(module_name);
}
EXPORT_SYMBOL_GPL(snd_ctl_request_layer);

/**
* snd_ctl_register_layer - register new control layer
* @lops: operation structure
*
* The new layer can track all control elements and do additional
* operations on top (like audio LED handling).
*/
void snd_ctl_register_layer(struct snd_ctl_layer_ops *lops)
{
struct snd_card *card;
int card_number;

down_write(&snd_ctl_layer_rwsem);
lops->next = snd_ctl_layer;
snd_ctl_layer = lops;
up_write(&snd_ctl_layer_rwsem);
for (card_number = 0; card_number < SNDRV_CARDS; card_number++) {
card = snd_card_ref(card_number);
if (card) {
down_read(&card->controls_rwsem);
lops->lregister(card);
up_read(&card->controls_rwsem);
snd_card_unref(card);
}
}
}
EXPORT_SYMBOL_GPL(snd_ctl_register_layer);

/**
* snd_ctl_disconnect_layer - disconnect control layer
* @lops: operation structure
*
* It is expected that the information about tracked cards
* is freed before this call (the disconnect callback is
* not called here).
*/
void snd_ctl_disconnect_layer(struct snd_ctl_layer_ops *lops)
{
struct snd_ctl_layer_ops *lops2, *prev_lops2;

down_write(&snd_ctl_layer_rwsem);
for (lops2 = snd_ctl_layer, prev_lops2 = NULL; lops2; lops2 = lops2->next)
if (lops2 == lops) {
if (!prev_lops2)
snd_ctl_layer = lops->next;
else
prev_lops2->next = lops->next;
break;
}
up_write(&snd_ctl_layer_rwsem);
}
EXPORT_SYMBOL_GPL(snd_ctl_disconnect_layer);

/*
* INIT PART
*/
Expand All @@ -2020,9 +2107,20 @@ static const struct file_operations snd_ctl_f_ops =
static int snd_ctl_dev_register(struct snd_device *device)
{
struct snd_card *card = device->device_data;
struct snd_ctl_layer_ops *lops;
int err;

return snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
&snd_ctl_f_ops, card, &card->ctl_dev);
err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
&snd_ctl_f_ops, card, &card->ctl_dev);
if (err < 0)
return err;
down_read(&card->controls_rwsem);
down_read(&snd_ctl_layer_rwsem);
for (lops = snd_ctl_layer; lops; lops = lops->next)
lops->lregister(card);
up_read(&snd_ctl_layer_rwsem);
up_read(&card->controls_rwsem);
return 0;
}

/*
Expand All @@ -2032,6 +2130,7 @@ static int snd_ctl_dev_disconnect(struct snd_device *device)
{
struct snd_card *card = device->device_data;
struct snd_ctl_file *ctl;
struct snd_ctl_layer_ops *lops;
unsigned long flags;

read_lock_irqsave(&card->ctl_files_rwlock, flags);
Expand All @@ -2041,6 +2140,13 @@ static int snd_ctl_dev_disconnect(struct snd_device *device)
}
read_unlock_irqrestore(&card->ctl_files_rwlock, flags);

down_read(&card->controls_rwsem);
down_read(&snd_ctl_layer_rwsem);
for (lops = snd_ctl_layer; lops; lops = lops->next)
lops->ldisconnect(card);
up_read(&snd_ctl_layer_rwsem);
up_read(&card->controls_rwsem);

return snd_unregister_device(&card->ctl_dev);
}

Expand Down

0 comments on commit 3f0638a

Please sign in to comment.