3 * @brief CTL External plugin implementation.
5 * Copyright (C) 2006 Nokia Corporation
7 * Contact: Eduardo Bezerra Valentin <eduardo.valentin@indt.org.br>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
25 #include <sys/ioctl.h>
26 #include <alsa/asoundlib.h>
27 #include <alsa/control_external.h>
31 #include "dsp-protocol.h"
32 #include "constants.h"
34 #define PLAYBACK_VOLUME_CONTROL_NAME "PCM Playback Volume"
35 #define PLAYBACK_MUTE_CONTROL_NAME "PCM Playback Switch"
36 #define RECORDING_CONTROL_NAME "Capture Switch"
39 * data structure to represent a dsp task device node.
42 dsp_protocol_t *dsp_protocol;
45 struct list_head list;
49 * data structure to represent this plugin information.
51 typedef struct snd_ctl_dsp {
55 control_list_t **controls;
56 control_list_t playback_devices;
57 control_list_t recording_devices;
60 static snd_ctl_dsp_t *free_ref;
62 * @param control_list control list to be freed.
64 * It passes through this control list and frees
67 * @return zero. success.
69 static int free_control_list(control_list_t * control_list)
71 struct list_head *pos, *q;
73 list_for_each_safe(pos, q, &control_list->list) {
74 tmp = list_entry(pos, control_list_t, list);
77 close(tmp->dsp_protocol->fd);
78 dsp_protocol_destroy(&(tmp->dsp_protocol));
85 * @param ext snd_ctl_ext_t structure.
87 * It is the close event handler for this plugin.
88 * It frees all the allocated memory.
90 * @return zero. success.
92 static void dsp_ctl_close(snd_ctl_ext_t * ext)
94 snd_ctl_dsp_t *dsp_ctl = ext->private_data;
96 free(dsp_ctl->controls);
97 free_control_list(&(dsp_ctl->playback_devices));
98 free_control_list(&(dsp_ctl->recording_devices));
104 * @param ext snd_ctl_ext_t structure.
106 * It returns number of controls to be used by this
107 * plugin. It is based on number of recording and playback
108 * device nodes configured to be handled by this plugin.
110 * @return number of controls.
112 static int dsp_ctl_elem_count(snd_ctl_ext_t * ext)
114 snd_ctl_dsp_t *dsp_ctl = ext->private_data;
115 int ret = 2 * dsp_ctl->num_playbacks + dsp_ctl->num_recordings;
122 * @param ext snd_ctl_ext_t structure.
123 * @param offset offset of current control.
124 * @param id id of current control element.
126 * It sets name and index for a control based
129 * @return zero. success.
131 static int dsp_ctl_elem_list(snd_ctl_ext_t * ext, unsigned int offset,
132 snd_ctl_elem_id_t * id)
134 snd_ctl_dsp_t *dsp_ctl = ext->private_data;
138 snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
139 if (offset < 2 * dsp_ctl->num_playbacks) {
141 snd_ctl_elem_id_set_name(id,
142 PLAYBACK_MUTE_CONTROL_NAME);
144 snd_ctl_elem_id_set_name(id,
145 PLAYBACK_VOLUME_CONTROL_NAME);
148 offset -= 2 * dsp_ctl->num_playbacks;
149 snd_ctl_elem_id_set_name(id, RECORDING_CONTROL_NAME);
151 snd_ctl_elem_id_set_index(id, offset);
157 * @param ext snd_ctl_ext_t structure.
158 * @param id control element id from alsa-lib
160 * It searches for an control element using
161 * its name and index. If this control can
162 * be found, it returns a key to represent it
163 * for future use. This key is an index of an
164 * array of controls whose is composed of three
165 * types of elements: PCM Volume, PCM Switch and
166 * Capture Switch. This array is organized as
168 * pv0, ps0, pv1, ps1, ..., pv_n, ps_n, cs0, cs1, ..., cs_m
170 * where, pvi is the i-th PCM Volume Control
171 * psi is the i-th PCM Switch Control
172 * csi is the i-th Capture Switch Control
173 * n - is the number of Playback devices
174 * m - is the number of Recording devices
176 * @return if control is not found, returns SND_CTL_EXT_KEY_NOT_FOUND.
177 * Otherwise, returns a key of control.
179 static snd_ctl_ext_key_t dsp_ctl_find_elem(snd_ctl_ext_t * ext,
180 const snd_ctl_elem_id_t * id)
182 snd_ctl_dsp_t *dsp_ctl = ext->private_data;
183 snd_ctl_ext_key_t ret = SND_CTL_EXT_KEY_NOT_FOUND;
188 idx = snd_ctl_elem_id_get_index(id);
189 name = snd_ctl_elem_id_get_name(id);
190 if (strcmp(PLAYBACK_VOLUME_CONTROL_NAME, name) == 0)
192 else if (strcmp(PLAYBACK_MUTE_CONTROL_NAME, name) == 0)
195 ret = 2 * dsp_ctl->num_playbacks + idx;
196 if (ret == SND_CTL_EXT_KEY_NOT_FOUND)
197 DPRINT("Not Found name %s, index %d\n",
198 snd_ctl_elem_id_get_name(id), idx);
204 * @param ext snd_ctl_ext_t structure.
205 * @param key current control key to be handled.
206 * @param type type of this control.
207 * @param acc access type of this control.
208 * @param count number of channels of this control.
210 * It provides information about a control.
211 * Playback device node has an integer and a boolean
212 * control. Recording has only boolean control.
214 * @return zero. success.
216 static int dsp_ctl_get_attribute(snd_ctl_ext_t * ext, snd_ctl_ext_key_t key,
217 int *type, unsigned int *acc,
220 snd_ctl_dsp_t *dsp_ctl = ext->private_data;
224 if (key >= 2 * dsp_ctl->num_playbacks || key % 2 == 1)
225 *type = SND_CTL_ELEM_TYPE_BOOLEAN;
227 *type = SND_CTL_ELEM_TYPE_INTEGER;
229 *count = dsp_ctl->controls[key]->channels;
230 *acc = SND_CTL_EXT_ACCESS_READWRITE;
236 * @param ext snd_ctl_ext_t structure.
237 * @param key current control key to be handled.
238 * @param imin minimum value of this integer control.
239 * @param imax maximum value of this integer control.
240 * @param istep steps value of this integer control.
242 * It provides information about integer control. It
243 * consideres both boolean and integer controls.
245 * @return zero. success.
247 static int dsp_ctl_get_integer_info(snd_ctl_ext_t * ext, snd_ctl_ext_key_t key,
248 long *imin, long *imax, long *istep)
250 snd_ctl_dsp_t *dsp_ctl = ext->private_data;
253 if (key >= 2 * dsp_ctl->num_playbacks || key % 2 == 1)
263 * @param ext snd_ctl_ext_t structure.
264 * @param key current control key to be handled.
265 * @param value return value holder.
267 * It reads volume information from dsp task node and
268 * fills it into value to alsa-lib.
270 * @return zero. success.
272 static int dsp_ctl_read_integer(snd_ctl_ext_t * ext, snd_ctl_ext_key_t key,
276 unsigned char left, right;
277 snd_ctl_dsp_t *dsp_ctl = ext->private_data;
278 control_list_t *tmp = dsp_ctl->controls[key];
281 if (key >= 2 * dsp_ctl->num_playbacks || key % 2 == 1) {
282 ret = dsp_protocol_get_mute(tmp->dsp_protocol);
284 left = ret == 0 ? 1 : 0;
288 ret = dsp_protocol_get_volume(tmp->dsp_protocol, &left, &right);
291 if (tmp->channels == 2)
295 if (tmp->channels == 2)
305 * @param ext snd_ctl_ext_t structure.
306 * @param key current control key to be handled.
307 * @param value volume value to be written.
309 * It writes volume information to dsp task node from
310 * value that comes from alsa-lib. It checks if value
311 * coming from alsa-lib is different of value in dsp
314 * @return zero if not updated. one if updated.
316 static int dsp_ctl_write_integer(snd_ctl_ext_t * ext, snd_ctl_ext_key_t key,
320 unsigned char left, right;
321 snd_ctl_dsp_t *dsp_ctl = ext->private_data;
322 control_list_t *tmp = dsp_ctl->controls[key];
325 if (key >= 2 * dsp_ctl->num_playbacks || key % 2 == 1) {
326 if ((ret = dsp_protocol_get_mute(tmp->dsp_protocol)) < 0)
328 left = ret == 0 ? 1 : 0;
332 dsp_protocol_get_volume(tmp->dsp_protocol, &left, &right)) < 0)
335 if (tmp->channels == 2) {
336 if (left == value[0] && right == value[1]) {
343 if (left == value[0]) {
347 right = left = value[0];
350 if (key >= 2 * dsp_ctl->num_playbacks || key % 2 == 1) {
352 dsp_protocol_set_mute(tmp->dsp_protocol,
353 left == 0 ? 1 : 0)) < 0)
357 dsp_protocol_set_volume(tmp->dsp_protocol, left, right)) < 0)
363 if (tmp->channels == 2)
378 static int dsp_ctl_read_event(snd_ctl_ext_t * ext, snd_ctl_elem_id_t * id,
379 unsigned int *event_mask)
385 * @param n configuration file parse tree.
386 * @param device_list list of device files to be filled.
388 * It searches for device file names in given configuration parse
389 * tree. When one device file name is found, it is filled into device_list.
391 * @return zero if success, otherwise a negative error code.
393 static int fill_control_list(snd_config_t * n, control_list_t * control_list)
395 snd_config_iterator_t j, nextj;
399 INIT_LIST_HEAD(&control_list->list);
400 snd_config_for_each(j, nextj, n) {
401 snd_config_t *s = snd_config_iterator_entry(j);
402 const char *id_number;
404 if (snd_config_get_id(s, &id_number) < 0)
406 if (safe_strtol(id_number, &k) < 0) {
407 SNDERR("id of field %s is not an integer", id_number);
413 /* add to available dsp task nodes */
414 tmp = (control_list_t *) malloc(sizeof(control_list_t));
415 if (snd_config_get_ascii(s, &(tmp->name)) < 0) {
416 SNDERR("invalid ascii string for id %s\n",
421 list_add(&(tmp->list), &(control_list->list));
430 * @param dsp_ctl snd_dsp_t structure.
432 * It probes all dsp tasks configured to be handled by this
433 * plugin. It gets all needed information about volume controling.
435 * @return zero if success, otherwise a negative error code.
437 static int dsp_ctl_probe_dsp_task(snd_ctl_dsp_t * dsp_ctl)
441 struct list_head *lists[2] =
442 { &(dsp_ctl->playback_devices.list),
443 &(dsp_ctl->recording_devices.list) };
445 DPRINT("Probing dsp device nodes \n");
446 for (i = 0; i < 2; i++) {
447 list_for_each_entry(tmp, lists[i], list) {
448 DPRINT("Trying to use %s\n", tmp->name);
449 /* Initialise the dsp_protocol and create connection */
451 dsp_protocol_create(&(tmp->dsp_protocol))) < 0)
454 dsp_protocol_probe_node(tmp->dsp_protocol,
456 DPRINT("%s is not available now\n", tmp->name);
458 close(tmp->dsp_protocol->fd); //memory leak?!?
464 DPRINT("No valid dsp task nodes for now. Exiting.\n");
472 * @param dsp_ctl snd_dsp_t structure.
474 * It fills an array of controls to represent PCM Volume,
475 * PCM Switch and Capture Switch controls.
477 * @return zero if success, otherwise a negative error code.
479 static int dsp_ctl_fill_controls(snd_ctl_dsp_t * dsp_ctl)
483 int num_controls = 2 * dsp_ctl->num_playbacks + dsp_ctl->num_recordings;
486 dsp_ctl->controls = calloc(num_controls, sizeof(control_list_t *));
487 if (dsp_ctl->controls == NULL) {
492 /* Each pcm task node should have a PCM Volume and PCM Switch control */
493 list_for_each_entry(tmp, &(dsp_ctl->playback_devices.list), list) {
494 dsp_ctl->controls[i] = tmp;
495 dsp_ctl->controls[i + 1] = tmp;
498 /* Each pcm_rec node should have a Capture Switch control */
499 list_for_each_entry(tmp, &(dsp_ctl->recording_devices.list), list)
500 dsp_ctl->controls[i++] = tmp;
508 * Alsa-lib callback structure.
510 static snd_ctl_ext_callback_t dsp_ctl_ext_callback = {
511 .close = dsp_ctl_close,
512 .elem_count = dsp_ctl_elem_count,
513 .elem_list = dsp_ctl_elem_list,
514 .find_elem = dsp_ctl_find_elem,
515 .get_attribute = dsp_ctl_get_attribute,
516 .get_integer_info = dsp_ctl_get_integer_info,
517 .read_integer = dsp_ctl_read_integer,
518 .write_integer = dsp_ctl_write_integer,
519 .read_event = dsp_ctl_read_event,
523 * It initializes the alsa ctl plugin. It reads the parameters and creates the
524 * connection with the pcm device file.
526 * @return zero if success, otherwise a negative error code.
528 SND_CTL_PLUGIN_DEFINE_FUNC(dsp_ctl)
530 snd_config_iterator_t i, next;
531 snd_ctl_dsp_t *dsp_ctl;
536 /* Allocate the structure */
537 dsp_ctl = calloc(1, sizeof(*dsp_ctl));
538 if (dsp_ctl == NULL) {
542 /* Read the configuration searching for configurated devices */
543 snd_config_for_each(i, next, conf) {
544 snd_config_t *n = snd_config_iterator_entry(i);
546 if (snd_config_get_id(n, &id) < 0)
548 if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0 || strcmp(id, "hint") == 0)
550 if (strcmp(id, "playback_devices") == 0) {
551 if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND){
552 if ((dsp_ctl->num_playbacks =
555 playback_devices))) < 0) {
556 SNDERR("Could not fill control"
557 " list for playback devices\n");
562 SNDERR("Invalid type for %s", id);
568 if (strcmp(id, "recording_devices") == 0) {
569 if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND){
570 if ((dsp_ctl->num_recordings =
573 recording_devices))) < 0) {
574 SNDERR("Could not fill string "
575 "list for recording devices\n");
580 SNDERR("Invalid type for %s", id);
586 SNDERR("Unknown field %s", id);
591 if ((err = dsp_ctl_probe_dsp_task(dsp_ctl)) < 0)
594 if ((err = dsp_ctl_fill_controls(dsp_ctl)) < 0)
596 dsp_ctl->ext.version = SND_CTL_EXT_VERSION;
597 dsp_ctl->ext.card_idx = 0; /* FIXME */
598 strcpy(dsp_ctl->ext.id, "ALSA-DSP-CTL");
599 strcpy(dsp_ctl->ext.name, "Alsa-DSP external ctl plugin");
600 strcpy(dsp_ctl->ext.longname, "External Control Alsa plugin for DSP");
601 strcpy(dsp_ctl->ext.mixername, "ALSA-DSP plugin Mixer");
602 dsp_ctl->ext.callback = &dsp_ctl_ext_callback;
603 dsp_ctl->ext.private_data = dsp_ctl;
606 if ((err = snd_ctl_ext_create(&dsp_ctl->ext, name, mode)) < 0)
609 *handlep = dsp_ctl->ext.handle;
619 static void dsp_ctl_descructor(void) __attribute__ ((destructor));
621 static void dsp_ctl_descructor(void)
624 DPRINT("dsp ctl destructor\n");
625 DPRINT("checking for memories leaks and releasing resources\n");
627 if (free_ref->controls)
628 free(free_ref->controls);
630 free_control_list(&(free_ref->playback_devices));
632 free_control_list(&(free_ref->recording_devices));
641 SND_CTL_PLUGIN_SYMBOL(dsp_ctl);