[alsa-devel] RFC: PCM extra attributes

Takashi Iwai tiwai at suse.de
Fri Jun 19 10:18:20 CEST 2009


Hi,

this is yet another topic I'm (currently) working on -- the addition
of PCM ioctls to get/set some extra attributes.  Basically, it adds
two simple ioctls for getting/setting extra attributes to the PCM
substream.  The attribute has a sort of TLV form,

  /* PCM extra attributes */
  struct snd_pcm_attr {
  	unsigned int type;	/* SNDRC_PCM_TYPE_ATTR_XXX */
  	unsigned int len;	/* GET R: the max elements in value array
  				 *     W: the actually written # elements
  				 * SET R/W: # elements to store
  				 */
  	unsigned int value[0];	/* value(s) to read / write */
  };

And corresponding two ioctls
  #define SNDRV_PCM_IOCTL_GET_ATTR	_IOWR('A', 0x14, struct snd_pcm_attr)
  #define SNDRV_PCM_IOCTL_SET_ATTR	_IOWR('A', 0x15, struct snd_pcm_attr)

The caller sets type and len fields.  For getting attributes, the len
points the max buffer length, and in return, the actual size is stored
there.  For setting, it's the size to set.

Now, for getting the control ids associated to a PCM substream, it'll
be like

	unsigned int buf[32];
	buf[0] = SNDRV_PCM_ATTR_ASSOC_CTLS;
	buf[1] = ARRAY_SIZE(buf) - 2; /* minus header size */
	ioctl(fd, SNDRV_PCM_IOCTL_GET_ATTR, buf);

The kernel side implementation (a core part) is attached below.
In this prototype, each driver is supposed to allocate and set
substream->assoc_ctls and substream->num_assoc_ctls appropriately.
I chose it since the control ids are statically associated to the PCM
substreams at the driver loading time.  It could be, of course, a
liked list with kmalloc, but I thought it's overkill.

Well, that's a pretty subtle thing and can be changed at any time.
But, we need to define ABI properly from the beginning.

So, if you have any comments on ABI or on the whole design, please
speak up.

An important note is that I'm planning to use this framework for
getting/setting the PCM (surround) channel mapping.  It's
straightforward for this ABI.  The internal implementation may vary,
but the interface is the question now.  For example, one could imagine
that the channel mapping belongs to hw_params.  But, it looks
difficult because of the nature of channel mapping; it doesn't match
with a simple integer parameter as hw_parmas has.

The PCM channel mapping feature will be certainly useful for PA (and
even for normal apps with a new alsa-lib plugin to convert the correct
the channel mapping properly on demand).  So, this is rather more
interesting stuff to me, too...


thanks,

Takashi

---
diff --git a/include/sound/asound.h b/include/sound/asound.h
index 82aed3f..159826b 100644
--- a/include/sound/asound.h
+++ b/include/sound/asound.h
@@ -445,6 +445,21 @@ struct snd_xfern {
 	snd_pcm_uframes_t frames;
 };
 
+/* PCM extra attributes */
+struct snd_pcm_attr {
+	unsigned int type;	/* SNDRC_PCM_TYPE_ATTR_XXX */
+	unsigned int len;	/* GET R: the max elements in value array
+				 *     W: the actually written # elements
+				 * SET R/W: # elements to store
+				 */
+	unsigned int value[0];	/* value(s) to read / write */
+};
+
+/* PCM attribute types */
+enum {
+	SNDRV_PCM_ATTR_ASSOC_CTLS,	/* array of associated ctl ids */
+};
+
 enum {
 	SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY = 0,	/* gettimeofday equivalent */
 	SNDRV_PCM_TSTAMP_TYPE_MONOTONIC,	/* posix_clock_monotonic equivalent */
@@ -459,6 +474,8 @@ enum {
 #define SNDRV_PCM_IOCTL_HW_PARAMS	_IOWR('A', 0x11, struct snd_pcm_hw_params)
 #define SNDRV_PCM_IOCTL_HW_FREE		_IO('A', 0x12)
 #define SNDRV_PCM_IOCTL_SW_PARAMS	_IOWR('A', 0x13, struct snd_pcm_sw_params)
+#define SNDRV_PCM_IOCTL_GET_ATTR	_IOWR('A', 0x14, struct snd_pcm_attr)
+#define SNDRV_PCM_IOCTL_SET_ATTR	_IOWR('A', 0x15, struct snd_pcm_attr)
 #define SNDRV_PCM_IOCTL_STATUS		_IOR('A', 0x20, struct snd_pcm_status)
 #define SNDRV_PCM_IOCTL_DELAY		_IOR('A', 0x21, snd_pcm_sframes_t)
 #define SNDRV_PCM_IOCTL_HWSYNC		_IO('A', 0x22)
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 2389352..98416b0 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -392,6 +392,10 @@ struct snd_pcm_substream {
 	struct snd_info_entry *proc_prealloc_entry;
 	struct snd_info_entry *proc_prealloc_max_entry;
 #endif
+	/* associated controls */
+	unsigned int num_assoc_ctls;
+	unsigned int *assoc_ctls;
+
 	/* misc flags */
 	unsigned int hw_opened: 1;
 };
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 145931a..7a46674 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -748,6 +748,7 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
 		substream_next = substream->next;
 		snd_pcm_timer_done(substream);
 		snd_pcm_substream_proc_done(substream);
+		kfree(substream->assoc_ctls);
 		kfree(substream);
 		substream = substream_next;
 	}
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c
index 08bfed5..07512b3 100644
--- a/sound/core/pcm_compat.c
+++ b/sound/core/pcm_compat.c
@@ -485,6 +485,8 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
 	case SNDRV_PCM_IOCTL_TTSTAMP:
 	case SNDRV_PCM_IOCTL_HWSYNC:
 	case SNDRV_PCM_IOCTL_PREPARE:
+	case SNDRV_PCM_IOCTL_GET_ATTR:
+	case SNDRV_PCM_IOCTL_SET_ATTR:
 	case SNDRV_PCM_IOCTL_RESET:
 	case SNDRV_PCM_IOCTL_START:
 	case SNDRV_PCM_IOCTL_DROP:
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 84da3ba..46129c7 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -2492,6 +2492,43 @@ static int snd_pcm_tstamp(struct snd_pcm_substream *substream, int __user *_arg)
 	return 0;
 }
 		
+/* store back the associated ctl ids to the given PCM substream */
+static int snd_pcm_get_attr_assoc_ctls(struct snd_pcm_substream *substream,
+				       unsigned int __user *arg)
+{
+	unsigned int len, i;
+
+	arg++;
+	if (get_user(len, arg))
+		return -EFAULT;
+	for (i = 0; i < len && i < substream->num_assoc_ctls; i++)
+		put_user(substream->assoc_ctls[i], arg + i + 1);
+	put_user(i, arg);
+	return 0;
+}
+
+/* GET_ATTR ioctl */
+static int snd_pcm_get_attr(struct snd_pcm_substream *substream,
+			    unsigned int __user *arg)
+{
+	unsigned int type;
+
+	if (get_user(type, arg))
+		return -EFAULT;
+	switch (type) {
+	case SNDRV_PCM_ATTR_ASSOC_CTLS:
+		return snd_pcm_get_attr_assoc_ctls(substream, arg);
+	}
+	return -ENOENT;
+}
+
+/* SET_ATTR ioctl */
+static int snd_pcm_set_attr(struct snd_pcm_substream *substream,
+			    struct snd_pcm_attr __user *arg)
+{
+	return -ENOENT;
+}
+
 static int snd_pcm_common_ioctl1(struct file *file,
 				 struct snd_pcm_substream *substream,
 				 unsigned int cmd, void __user *arg)
@@ -2519,6 +2556,10 @@ static int snd_pcm_common_ioctl1(struct file *file,
 		return snd_pcm_channel_info_user(substream, arg);
 	case SNDRV_PCM_IOCTL_PREPARE:
 		return snd_pcm_prepare(substream, file);
+	case SNDRV_PCM_IOCTL_GET_ATTR:
+		return snd_pcm_get_attr(substream, arg);
+	case SNDRV_PCM_IOCTL_SET_ATTR:
+		return snd_pcm_set_attr(substream, arg);
 	case SNDRV_PCM_IOCTL_RESET:
 		return snd_pcm_reset(substream);
 	case SNDRV_PCM_IOCTL_START:


More information about the Alsa-devel mailing list