[alsa-devel] [RFC PATCH 02/11] ALSA: implement MEDIA_IOC_ENUM_ENTITIES

Clemens Ladisch clemens at ladisch.de
Tue Aug 28 00:30:07 CEST 2012


Allow drivers to create entities.  To avoid bloat, the snd_media_entity
structure stores only basic information and retrieves the full entitiy
information on demand with a callback.

Signed-off-by: Clemens Ladisch <clemens at ladisch.de>
---
 include/sound/core.h  |    5 ++
 include/sound/media.h |   23 ++++++++++
 sound/core/init.c     |    4 ++
 sound/core/media.c    |  114 +++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 145 insertions(+), 1 deletions(-)

diff --git a/include/sound/core.h b/include/sound/core.h
index 5eca6f5..072b642 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -51,7 +51,8 @@ struct snd_media_card_ops;
 typedef int __bitwise snd_device_type_t;
 #define	SNDRV_DEV_TOPLEVEL	((__force snd_device_type_t) 0)
 #define	SNDRV_DEV_CONTROL	((__force snd_device_type_t) 1)
-#define	SNDRV_DEV_LOWLEVEL_PRE	((__force snd_device_type_t) 2)
+#define	SNDRV_DEV_MEDIA		((__force snd_device_type_t) 2)
+#define	SNDRV_DEV_LOWLEVEL_PRE	((__force snd_device_type_t) 3)
 #define	SNDRV_DEV_LOWLEVEL_NORMAL ((__force snd_device_type_t) 0x1000)
 #define	SNDRV_DEV_PCM		((__force snd_device_type_t) 0x1001)
 #define	SNDRV_DEV_RAWMIDI	((__force snd_device_type_t) 0x1002)
@@ -149,6 +150,8 @@ struct snd_card {

 #ifdef CONFIG_SND_MEDIA
 	const struct snd_media_card_ops *media_ops;
+	struct mutex media_mutex;
+	struct list_head media_entities;
 #endif
 };

diff --git a/include/sound/media.h b/include/sound/media.h
index d196219..65dd068 100644
--- a/include/sound/media.h
+++ b/include/sound/media.h
@@ -23,16 +23,29 @@
 #include <linux/init.h>

 struct snd_card;
+struct snd_media_entity;
 struct media_device_info;
+struct media_entity_desc;

 struct snd_media_card_ops {
 	int (*get_info)(struct snd_card *card, struct media_device_info *info);
 };

+typedef int (*snd_media_entity_get_desc_t)(struct snd_card *card,
+					   void *private_data,
+					   struct media_entity_desc *desc);
+
 #ifdef CONFIG_SND_MEDIA

 #define snd_card_set_media_ops(card, ops) ((card)->media_ops = (ops))

+int snd_media_entity_create(struct snd_card *card,
+			    snd_media_entity_get_desc_t get_desc,
+			    unsigned int id,
+			    unsigned int sinks, unsigned int sources,
+			    void *private_data);
+
+int snd_media_create(struct snd_card *card);
 void __init snd_media_init(void);
 void __exit snd_media_exit(void);

@@ -40,6 +53,16 @@ void __exit snd_media_exit(void);

 #define snd_card_set_media_ops(card, ops)

+static inline int snd_media_entity_create(struct snd_card *card,
+					  snd_media_entity_get_desc_t get_desc,
+					  unsigned int id,
+					  unsigned int sinks, unsigned int sources,
+					  void *private_data)
+{ return 0; }
+
+
+static inline int snd_media_create(struct snd_card *card)
+{ return 0; }
 static inline void snd_media_init()
 { }
 static inline void snd_media_exit()
diff --git a/sound/core/init.c b/sound/core/init.c
index d8ec849..e16ef16 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -32,6 +32,7 @@
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/info.h>
+#include <sound/media.h>

 /* monitor files for graceful shutdown (hotplug) */
 struct snd_monitor_file {
@@ -224,6 +225,9 @@ int snd_card_create(int idx, const char *xid,
 		snd_printk(KERN_ERR "unable to register control minors\n");
 		goto __error;
 	}
+	err = snd_media_create(card);
+	if (err < 0)
+		goto __error_ctl;
 	err = snd_info_card_create(card);
 	if (err < 0) {
 		snd_printk(KERN_ERR "unable to create card info\n");
diff --git a/sound/core/media.c b/sound/core/media.c
index 6ceb12a..aa94175 100644
--- a/sound/core/media.c
+++ b/sound/core/media.c
@@ -20,6 +20,7 @@
 #include <linux/device.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
+#include <linux/list.h>
 #include <linux/media.h>
 #include <linux/string.h>
 #include <linux/uaccess.h>
@@ -27,6 +28,87 @@
 #include <sound/control.h>
 #include <sound/media.h>

+struct snd_media_entity {
+	struct list_head list;
+	snd_media_entity_get_desc_t get_desc;
+	void *private_data;
+	unsigned int id;
+	unsigned int sinks, sources;
+};
+
+static struct snd_media_entity *search_entity(struct snd_card *card,
+					      unsigned int id)
+{
+	struct snd_media_entity *e;
+	bool next = id & MEDIA_ENT_ID_FLAG_NEXT;
+
+	id &= ~MEDIA_ENT_ID_FLAG_NEXT;
+	if (next)
+		id++;
+
+	list_for_each_entry(e, &card->media_entities, list)
+		if (e->id >= id)
+			return next || e->id == id ? e : NULL;
+
+	return NULL;
+}
+
+int snd_media_entity_create(struct snd_card *card,
+			    snd_media_entity_get_desc_t get_desc,
+			    unsigned int id,
+			    unsigned int sinks, unsigned int sources,
+			    void *private_data)
+{
+	struct snd_media_entity *entity;
+	struct list_head *pos;
+
+	entity = kzalloc(sizeof(*entity), GFP_KERNEL);
+	if (!entity)
+		return -ENOMEM;
+	entity->get_desc = get_desc;
+	entity->id = id;
+	entity->sinks = sinks;
+	entity->sources = sources;
+	entity->private_data = private_data;
+
+	mutex_lock(&card->media_mutex);
+	list_for_each_prev(pos, &card->media_entities)
+		if (list_entry(pos, struct snd_media_entity, list)->id < id)
+			break;
+	list_add(&entity->list, pos);
+	mutex_unlock(&card->media_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL(snd_media_entity_create);
+
+static int snd_media_dev_free(struct snd_device *device)
+{
+	struct snd_card *card = device->device_data;
+	struct snd_media_entity *e;
+
+	while (!list_empty(&card->media_entities)) {
+		e = list_first_entry(&card->media_entities,
+				     struct snd_media_entity, list);
+		list_del(&e->list);
+		kfree(e);
+	}
+
+	return 0;
+}
+
+int snd_media_create(struct snd_card *card)
+{
+	static struct snd_device_ops ops = {
+		.dev_free = snd_media_dev_free,
+	};
+
+	mutex_init(&card->media_mutex);
+	INIT_LIST_HEAD(&card->media_entities);
+
+	return snd_device_new(card, SNDRV_DEV_MEDIA, card, &ops);
+}
+
 static int snd_media_device_info(struct snd_card *card,
 				 struct media_device_info __user *infop)
 {
@@ -49,6 +131,36 @@ static int snd_media_device_info(struct snd_card *card,
 	return copy_to_user(infop, &info, sizeof(info));
 }

+static int snd_media_enum_entities(struct snd_card *card,
+				   struct media_entity_desc __user *descp)
+{
+	struct media_entity_desc desc;
+	struct snd_media_entity *entity;
+	int err;
+
+	if (copy_from_user(&desc, descp, sizeof(desc)))
+		return -EFAULT;
+
+	mutex_lock(&card->media_mutex);
+	entity = search_entity(card, desc.id);
+	mutex_unlock(&card->media_mutex);
+	if (!entity)
+		return -EINVAL;
+
+	desc.id = entity->id;
+	desc.revision = 0;
+	desc.flags = 0;
+	desc.group_id = 0;
+	desc.pads = entity->sinks + entity->sources;
+	desc.links = 0;
+
+	err = entity->get_desc(card, entity->private_data, &desc);
+	if (err < 0)
+		return err;
+
+	return copy_to_user(descp, &desc, sizeof(desc));
+}
+
 static int snd_media_control_ioctl(struct snd_card *card,
 				   struct snd_ctl_file *ctl_file,
 				   unsigned int cmd, unsigned long arg)
@@ -58,6 +170,8 @@ static int snd_media_control_ioctl(struct snd_card *card,
 	switch (cmd) {
 	case MEDIA_IOC_DEVICE_INFO:
 		return snd_media_device_info(card, argp);
+	case MEDIA_IOC_ENUM_ENTITIES:
+		return snd_media_enum_entities(card, argp);
 	default:
 		return -ENOIOCTLCMD;
 	}


More information about the Alsa-devel mailing list