[PATCH 5/6] Alsa support for Maemo SDK (n770): External Control plugin

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch file adds an ALSA External Control plugin. This source uses
the dsp-protocol
implementation.

The plugin probes for all communication channel at the start time. It
will handle only
channels specified into alsa configuration file. An configuration example is:
       # Mixer
       ctl.!default {
               type dsp_ctl
               playback_devices ["/dev/dsptask/pcm2"]
               recording_devices ["/dev/dsptask/pcm_rec"]
       }


Signed-off-by: Eduardo Valentin <eduardo.valentin@xxxxxxxxxxx>

diffstat:
dsp-ctl.c |  641 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 641 insertions(+)


--
Eduardo Bezerra Valentin
diff -pruN a/maemo/dsp-ctl.c b/maemo/dsp-ctl.c
--- a/maemo/dsp-ctl.c	1969-12-31 20:00:00.000000000 -0400
+++ b/maemo/dsp-ctl.c	2006-10-11 14:36:58.000000000 -0400
@@ -0,0 +1,641 @@
+/**
+ * @file dsp-ctl.c
+ * @brief CTL External plugin implementation.
+ * <p>
+ * Copyright (C) 2006 Nokia Corporation
+ * <p>
+ * Contact: Eduardo Bezerra Valentin <eduardo.valentin@xxxxxxxxxxx>
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * */
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <alsa/asoundlib.h>
+#include <alsa/control_external.h>
+#include <string.h>
+#include "list.h"
+#include "debug.h"
+#include "dsp-protocol.h"
+#include "constants.h"
+
+#define PLAYBACK_VOLUME_CONTROL_NAME 	"PCM Playback Volume"
+#define PLAYBACK_MUTE_CONTROL_NAME 	"PCM Playback Switch"
+#define RECORDING_CONTROL_NAME		"Capture Switch"
+
+/**
+ * data structure to represent a dsp task device node.
+ */
+typedef struct {
+	dsp_protocol_t *dsp_protocol;
+	char *name;
+	int channels;
+	struct list_head list;
+} control_list_t;
+
+/**
+ * data structure to represent this plugin information.
+ */ 
+typedef struct snd_ctl_dsp {
+	snd_ctl_ext_t ext;
+	int num_playbacks;
+	int num_recordings;
+	control_list_t **controls;
+	control_list_t playback_devices;
+	control_list_t recording_devices;
+} snd_ctl_dsp_t;
+
+static snd_ctl_dsp_t *free_ref;
+/**
+ * @param control_list control list to be freed.
+ *
+ * It passes through this control list and frees
+ * all its nodes.
+ * 
+ * @return zero. success.
+ */ 
+int free_control_list(control_list_t * control_list)
+{
+	struct list_head *pos, *q;
+	control_list_t *tmp;
+	list_for_each_safe(pos, q, &control_list->list) {
+		tmp = list_entry(pos, control_list_t, list);
+		list_del(pos);
+		free(tmp->name);
+		close(tmp->dsp_protocol->fd);
+		dsp_protocol_destroy(&(tmp->dsp_protocol));
+		free(tmp);
+	}
+	return 0;
+}
+
+/**
+ * @param ext snd_ctl_ext_t structure.
+ *
+ * It is the close event handler for this plugin.
+ * It frees all the allocated memory.
+ * 
+ * @return zero. success.
+ */ 
+static void dsp_ctl_close(snd_ctl_ext_t * ext)
+{
+	snd_ctl_dsp_t *dsp_ctl = ext->private_data;
+	DENTER();
+	free(dsp_ctl->controls);
+	free_control_list(&(dsp_ctl->playback_devices));
+	free_control_list(&(dsp_ctl->recording_devices));
+//      free(dsp_ctl);
+	DLEAVE(0);
+}
+
+/**
+ * @param ext snd_ctl_ext_t structure.
+ *
+ * It returns number of controls to be used by this
+ * plugin. It is based on number of recording and playback
+ * device nodes configured to be handled by this plugin.
+ * 
+ * @return number of controls.
+ */ 
+static int dsp_ctl_elem_count(snd_ctl_ext_t * ext)
+{
+	snd_ctl_dsp_t *dsp_ctl = ext->private_data;
+	int ret = 2 * dsp_ctl->num_playbacks + dsp_ctl->num_recordings;
+	DENTER();
+	DLEAVE(ret);
+	return ret;
+}
+
+/**
+ * @param ext snd_ctl_ext_t structure.
+ * @param offset offset of current control.
+ * @param id id of current control element.
+ *
+ * It sets name and index for a control based
+ * of its offset.
+ * 
+ * @return zero. success.
+ */ 
+static int dsp_ctl_elem_list(snd_ctl_ext_t * ext, unsigned int offset,
+			     snd_ctl_elem_id_t * id)
+{
+	snd_ctl_dsp_t *dsp_ctl = ext->private_data;
+	int ret = 0;
+
+	DENTER();
+	snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
+	if (offset < 2 * dsp_ctl->num_playbacks) {
+		if (offset % 2 == 1)
+			snd_ctl_elem_id_set_name(id,
+						 PLAYBACK_MUTE_CONTROL_NAME);
+		else
+			snd_ctl_elem_id_set_name(id,
+						 PLAYBACK_VOLUME_CONTROL_NAME);
+		offset /= 2;
+	} else {
+		offset -= 2 * dsp_ctl->num_playbacks;
+		snd_ctl_elem_id_set_name(id, RECORDING_CONTROL_NAME);
+	}
+	snd_ctl_elem_id_set_index(id, offset);
+	DLEAVE(ret);
+	return ret;
+}
+
+/**
+ * @param ext snd_ctl_ext_t structure.
+ * @param id control element id from alsa-lib
+ *
+ * It searches for an control element using 
+ * its name and index. If this control can
+ * be found, it returns a key to represent it
+ * for future use. This key is an index of an
+ * array of controls whose is composed of three
+ * types of elements: PCM Volume, PCM Switch and
+ * Capture Switch. This array is organized as 
+ * follows:
+ * pv0, ps0, pv1, ps1, ..., pv_n, ps_n, cs0, cs1, ..., cs_m
+ *
+ * where, pvi is the i-th PCM Volume Control
+ *        psi is the i-th PCM Switch Control
+ *        csi is the i-th Capture Switch Control
+ *        n - is the number of Playback devices
+ *        m - is the number of Recording devices
+ *
+ * @return if control is not found, returns SND_CTL_EXT_KEY_NOT_FOUND.
+ * Otherwise, returns a key of control.
+ */
+static snd_ctl_ext_key_t dsp_ctl_find_elem(snd_ctl_ext_t * ext,
+					   const snd_ctl_elem_id_t * id)
+{
+	snd_ctl_dsp_t *dsp_ctl = ext->private_data;
+	snd_ctl_ext_key_t ret = SND_CTL_EXT_KEY_NOT_FOUND;
+	int idx;
+	const char *name;
+
+	DENTER();
+	idx = snd_ctl_elem_id_get_index(id);
+	name = snd_ctl_elem_id_get_name(id);
+	if (strcmp(PLAYBACK_VOLUME_CONTROL_NAME, name) == 0)
+		ret = idx * 2;
+	else if (strcmp(PLAYBACK_MUTE_CONTROL_NAME, name) == 0)
+		ret = idx * 2 + 1;
+	else
+		ret = 2 * dsp_ctl->num_playbacks + idx;
+	if (ret == SND_CTL_EXT_KEY_NOT_FOUND)
+		DPRINT("Not Found name %s, index %d\n",
+		       snd_ctl_elem_id_get_name(id), idx);
+	DLEAVE((int)ret);
+	return ret;
+}
+
+/**
+ * @param ext snd_ctl_ext_t structure.
+ * @param key current control key to be handled.
+ * @param type type of this control.
+ * @param acc access type of this control.
+ * @param count number of channels of this control.
+ *
+ * It provides information about a control. 
+ * Playback device node has an integer and a boolean
+ * control. Recording has only boolean control.
+ *
+ * @return zero. success.
+ */
+static int dsp_ctl_get_attribute(snd_ctl_ext_t * ext, snd_ctl_ext_key_t key,
+				 int *type, unsigned int *acc,
+				 unsigned int *count)
+{
+	snd_ctl_dsp_t *dsp_ctl = ext->private_data;
+	int ret = 0;
+	DENTER();
+
+	if (key >= 2 * dsp_ctl->num_playbacks || key % 2 == 1)
+		*type = SND_CTL_ELEM_TYPE_BOOLEAN;
+	else
+		*type = SND_CTL_ELEM_TYPE_INTEGER;
+
+	*count = dsp_ctl->controls[key]->channels;
+	*acc = SND_CTL_EXT_ACCESS_READWRITE;
+	DLEAVE(ret);
+	return ret;
+}
+
+/**
+ * @param ext snd_ctl_ext_t structure.
+ * @param key current control key to be handled.
+ * @param imin minimum value of this integer control.
+ * @param imax maximum value of this integer control.
+ * @param istep steps value of this integer control.
+ *
+ * It provides information about integer control. It
+ * consideres both boolean and integer controls.
+ *
+ * @return  zero. success.
+ */
+static int dsp_ctl_get_integer_info(snd_ctl_ext_t * ext, snd_ctl_ext_key_t key,
+				    long *imin, long *imax, long *istep)
+{
+	snd_ctl_dsp_t *dsp_ctl = ext->private_data;
+	DENTER();
+	*imin = 0;
+	if (key >= 2 * dsp_ctl->num_playbacks || key % 2 == 1)
+		*imax = 1;
+	else
+		*imax = 100;
+	*istep = 0;
+	DLEAVE(0);
+	return 0;
+}
+
+/**
+ * @param ext snd_ctl_ext_t structure.
+ * @param key current control key to be handled.
+ * @param value return value holder.
+ *
+ * It reads volume information from dsp task node and 
+ * fills it into value to alsa-lib.
+ *
+ * @return zero. success.
+ */
+static int dsp_ctl_read_integer(snd_ctl_ext_t * ext, snd_ctl_ext_key_t key,
+				long *value)
+{
+	int ret = 0;
+	char left, right;
+	snd_ctl_dsp_t *dsp_ctl = ext->private_data;
+	control_list_t *tmp = dsp_ctl->controls[key];
+
+	DENTER();
+	if (key >= 2 * dsp_ctl->num_playbacks || key % 2 == 1) {
+		ret = dsp_protocol_get_mute(tmp->dsp_protocol);
+		if (ret >= 0) {
+			left = ret == 0 ? 1 : 0;
+			right = left;
+		}
+	} else
+		ret = dsp_protocol_get_volume(tmp->dsp_protocol, &left, &right);
+	if (ret >= 0) {
+		value[0] = left;
+		if (tmp->channels == 2)
+			value[1] = right;
+	} else {
+		value[0] = 0;
+		if (tmp->channels == 2)
+			value[1] = 0;
+		ret = 0;
+	}
+
+	DLEAVE(ret);
+	return ret;
+}
+
+/**
+ * @param ext snd_ctl_ext_t structure.
+ * @param key current control key to be handled.
+ * @param value volume value to be written.
+ *
+ * It writes volume information to dsp task node from
+ * value that comes from alsa-lib. It checks if value
+ * coming from alsa-lib is different of value in dsp 
+ * side.
+ *
+ * @return zero if not updated. one if updated.
+ */
+static int dsp_ctl_write_integer(snd_ctl_ext_t * ext, snd_ctl_ext_key_t key,
+				 long *value)
+{
+	int ret;
+	char left, right;
+	snd_ctl_dsp_t *dsp_ctl = ext->private_data;
+	control_list_t *tmp = dsp_ctl->controls[key];
+
+	DENTER();
+	if (key >= 2 * dsp_ctl->num_playbacks || key % 2 == 1) {
+		if ((ret = dsp_protocol_get_mute(tmp->dsp_protocol)) < 0)
+			goto zero;
+		left = ret == 0 ? 1 : 0;
+		right = left;
+	} else
+	    if ((ret =
+		 dsp_protocol_get_volume(tmp->dsp_protocol, &left, &right)) < 0)
+		goto zero;
+
+	if (tmp->channels == 2) {
+		if (left == value[0] && right == value[1]) {
+			ret = 0;
+			goto out;
+		}
+		right = value[1];
+		left = value[0];
+	} else {
+		if (left == value[0]) {
+			ret = 0;
+			goto out;
+		}
+		right = left = value[0];
+	}
+
+	if (key >= 2 * dsp_ctl->num_playbacks || key % 2 == 1) {
+		if ((ret =
+		     dsp_protocol_set_mute(tmp->dsp_protocol,
+					   left == 0 ? 1 : 0)) < 0)
+			goto zero;
+	} else
+	    if ((ret =
+		 dsp_protocol_set_volume(tmp->dsp_protocol, left, right)) < 0)
+		goto zero;
+	ret = 1;
+	goto out;
+      zero:
+	value[0] = 0;
+	if (tmp->channels == 2)
+		value[1] = 0;
+	ret = 0;
+      out:
+	DLEAVE(ret);
+	return ret;
+}
+
+/**
+ * @param ext
+ * @param id
+ * @param event_mask
+ *
+ * @return -EIO
+ */
+static int dsp_ctl_read_event(snd_ctl_ext_t * ext, snd_ctl_elem_id_t * id,
+			      unsigned int *event_mask)
+{
+	return -EIO;
+}
+
+/**
+ * @param n configuration file parse tree. 
+ * @param device_list list of device files to be filled.
+ *
+ * It searches for device file names in given configuration parse
+ * tree. When one device file name is found, it is filled into device_list.
+ *
+ * @return zero if success, otherwise a negative error code.
+ */
+static int fill_control_list(snd_config_t * n, control_list_t * control_list)
+{
+	snd_config_iterator_t j, nextj;
+	control_list_t *tmp;
+	long idx = 0;
+	DENTER();
+	INIT_LIST_HEAD(&control_list->list);
+	snd_config_for_each(j, nextj, n) {
+		snd_config_t *s = snd_config_iterator_entry(j);
+		const char *id_number;
+		long k;
+		if (snd_config_get_id(s, &id_number) < 0)
+			continue;
+		if (safe_strtol(id_number, &k) < 0) {
+			SNDERR("id of field %s is not an integer", id_number);
+			idx = -EINVAL;
+			goto out;
+		}
+		if (k == idx) {
+			idx++;
+			/* add to available dsp task nodes */
+			tmp = (control_list_t *) malloc(sizeof(control_list_t));
+			if (snd_config_get_ascii(s, &(tmp->name)) < 0) {
+				SNDERR("invalid ascii string for id %s\n",
+				       id_number);
+				idx = -EINVAL;
+				goto out;
+			}
+			list_add(&(tmp->list), &(control_list->list));
+		}
+	}
+      out:
+	DLEAVE((int)idx);
+	return idx;
+}
+
+/**
+ * @param dsp_ctl snd_dsp_t structure.
+ *
+ * It probes all dsp tasks configured to be handled by this
+ * plugin. It gets all needed information about volume controling.
+ *
+ * @return zero if success, otherwise a negative error code.
+ */
+static int dsp_ctl_probe_dsp_task(snd_ctl_dsp_t * dsp_ctl)
+{
+	int err = 0, i;
+	control_list_t *tmp;
+	struct list_head *lists[2] =
+	    { &(dsp_ctl->playback_devices.list),
+&(dsp_ctl->recording_devices.list) };
+	DENTER();
+	DPRINT("Probing dsp device nodes \n");
+	for (i = 0; i < 2; i++) {
+		list_for_each_entry(tmp, lists[i], list) {
+			DPRINT("Trying to use %s\n", tmp->name);
+			/* Initialise the dsp_protocol and create connection */
+			if ((err =
+			     dsp_protocol_create(&(tmp->dsp_protocol))) < 0)
+				goto out;
+			if ((tmp->channels =
+			     dsp_protocol_probe_node(tmp->dsp_protocol,
+						     tmp->name)) < 0) {
+				DPRINT("%s is not available now\n", tmp->name);
+				err = tmp->channels;
+				close(tmp->dsp_protocol->fd);	//memory leak?!?
+				goto out;
+			}
+		}
+	}
+	if (err < 0) {
+		DPRINT("No valid dsp task nodes for now. Exiting.\n");
+	}
+      out:
+	DLEAVE(err);
+	return err;
+}
+
+/**
+ * @param dsp_ctl snd_dsp_t structure.
+ *
+ * It fills an array of controls to represent PCM Volume,
+ * PCM Switch and Capture Switch controls.
+ *
+ * @return zero if success, otherwise a negative error code.
+ */
+static int dsp_ctl_fill_controls(snd_ctl_dsp_t * dsp_ctl)
+{
+	int ret = 0;
+	int i;
+	int num_controls = 2 * dsp_ctl->num_playbacks + dsp_ctl->num_recordings;
+	DENTER();
+	control_list_t *tmp;
+	dsp_ctl->controls = calloc(num_controls, sizeof(control_list_t *));
+	if (dsp_ctl->controls == NULL) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	i = 0;
+	/* Each pcm task node should have a PCM Volume and PCM Switch control */
+	list_for_each_entry(tmp, &(dsp_ctl->playback_devices.list), list) {
+		dsp_ctl->controls[i] = tmp;
+		dsp_ctl->controls[i + 1] = tmp;
+		i += 2;
+	}
+	/* Each pcm_rec node should have a Capture Switch control */
+	list_for_each_entry(tmp, &(dsp_ctl->recording_devices.list), list)
+	    dsp_ctl->controls[i++] = tmp;
+
+      out:
+	DLEAVE(ret);
+	return ret;
+}
+
+/**
+ * Alsa-lib callback structure.
+ */
+static snd_ctl_ext_callback_t dsp_ctl_ext_callback = {
+	.close = dsp_ctl_close,
+	.elem_count = dsp_ctl_elem_count,
+	.elem_list = dsp_ctl_elem_list,
+	.find_elem = dsp_ctl_find_elem,
+	.get_attribute = dsp_ctl_get_attribute,
+	.get_integer_info = dsp_ctl_get_integer_info,
+	.read_integer = dsp_ctl_read_integer,
+	.write_integer = dsp_ctl_write_integer,
+	.read_event = dsp_ctl_read_event,
+};
+
+/**
+ * It initializes the alsa ctl plugin. It reads the parameters and creates the 
+ * connection with the pcm device file.
+ *
+ * @return zero if success, otherwise a negative error code.
+ */
+SND_CTL_PLUGIN_DEFINE_FUNC(dsp_ctl)
+{
+	snd_config_iterator_t i, next;
+	snd_ctl_dsp_t *dsp_ctl;
+	int err;
+	int ret;
+
+	DENTER();
+	/* Allocate the structure */
+	dsp_ctl = calloc(1, sizeof(*dsp_ctl));
+	if (dsp_ctl == NULL) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	/* Read the configuration searching for configurated devices */
+	snd_config_for_each(i, next, conf) {
+		snd_config_t *n = snd_config_iterator_entry(i);
+		const char *id;
+		if (snd_config_get_id(n, &id) < 0)
+			continue;
+		if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0)
+			continue;
+		if (strcmp(id, "playback_devices") == 0) {
+			if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND){
+				if ((dsp_ctl->num_playbacks =
+				     fill_control_list(n,
+						&(dsp_ctl->
+						playback_devices))) < 0) {
+					SNDERR("Could not fill control"
+						" list for playback devices\n");
+					err = -EINVAL;
+					goto error;
+				}
+			} else {
+				SNDERR("Invalid type for %s", id);
+				err = -EINVAL;
+				goto error;
+			}
+			continue;
+		}
+		if (strcmp(id, "recording_devices") == 0) {
+			if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND){
+				if ((dsp_ctl->num_recordings =
+				     fill_control_list(n,
+						&(dsp_ctl->
+						recording_devices))) < 0) {
+					SNDERR("Could not fill string "
+						"list for recording devices\n");
+					err = -EINVAL;
+					goto error;
+				}
+			} else {
+				SNDERR("Invalid type for %s", id);
+				err = -EINVAL;
+				goto error;
+			}
+			continue;
+		}
+		SNDERR("Unknown field %s", id);
+		err = -EINVAL;
+		goto error;
+	}
+
+	if ((err = dsp_ctl_probe_dsp_task(dsp_ctl)) < 0)
+		goto error;
+
+	if ((err = dsp_ctl_fill_controls(dsp_ctl)) < 0)
+		goto error;
+	dsp_ctl->ext.version = SND_CTL_EXT_VERSION;
+	dsp_ctl->ext.card_idx = 0;	/* FIXME */
+	strcpy(dsp_ctl->ext.id, "ALSA-DSP-CTL");
+	strcpy(dsp_ctl->ext.name, "Alsa-DSP external ctl plugin");
+	strcpy(dsp_ctl->ext.longname, "External Control Alsa plugin for DSP");
+	strcpy(dsp_ctl->ext.mixername, "ALSA-DSP plugin Mixer");
+	dsp_ctl->ext.callback = &dsp_ctl_ext_callback;
+	dsp_ctl->ext.private_data = dsp_ctl;
+	free_ref = dsp_ctl;
+
+	if ((err = snd_ctl_ext_create(&dsp_ctl->ext, name, mode)) < 0)
+		goto error;
+
+	*handlep = dsp_ctl->ext.handle;
+	ret = 0;
+	goto out;
+      error:
+	ret = err;
+	free(dsp_ctl);
+      out:
+	DLEAVE(ret);
+	return ret;
+}
+void dsp_ctl_descructor(void) __attribute__ ((destructor));
+
+void dsp_ctl_descructor(void)
+{
+	DENTER();
+	DPRINT("dsp ctl destructor\n");
+	DPRINT("checking for memories leaks and releasing resources\n");
+	if (free_ref) {
+		if (free_ref->controls) 
+			free(free_ref->controls);
+
+		free_control_list(&(free_ref->playback_devices));
+
+		free_control_list(&(free_ref->recording_devices));
+		
+		free(free_ref);
+		free_ref = NULL;
+	}
+	DLEAVE(0);
+
+}
+
+SND_CTL_PLUGIN_SYMBOL(dsp_ctl);
-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Alsa-devel mailing list
Alsa-devel@xxxxxxxxxxxxxxxxxxxxx
https://lists.sourceforge.net/lists/listinfo/alsa-devel

[Index of Archives]     [ALSA User]     [Linux Audio Users]     [Kernel Archive]     [Asterisk PBX]     [Photo Sharing]     [Linux Sound]     [Video 4 Linux]     [Gimp]     [Yosemite News]

  Powered by Linux