From b79063da276eecd1c3683ad3fbe9de0f66094898 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 6 Nov 2006 14:31:31 +0100 Subject: Alsa support for Maemo SDK (n770): External PCM IO plugin This patch file adds an ALSA External PCM I/O plugin. This source uses the dsp-protocol implementation. The plugin probes for a free communication channel at the start time. It will probe only for channels specified into the configuration file for the plugin. An configuration example is: # PCM pcm.!default { type alsa_dsp playback_device_file ["/dev/dsptask/pcm2"] recording_device_file ["/dev/dsptask/pcm_rec"] } The plugin supports the following: * Playback: o 16-bit PCM formats: + S16_LE + S16_BE + U16_LE + U16_BE o 8-bit PCM formats: + A_LAW + MU_LAW + U8 + S8 o Rates: + 8 KHz + 11.025 KHz + 12 KHz + 16 KHz + 22.050 KHz + 24 KHz + 32 KHz + 44.1 KHz + 48 KHz o Channels: + Mono + Stereo * Recording: o 16-bit PCM formats: + S16_LE o 8-bit PCM formats: + A_LAW + MU_LAW o Rates: + 8 KHz o Channels + Mono Signed-off-by: Eduardo Valentin --- maemo/alsa-dsp.c | 772 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 772 insertions(+) create mode 100644 maemo/alsa-dsp.c diff --git a/maemo/alsa-dsp.c b/maemo/alsa-dsp.c new file mode 100644 index 0000000..f23741c --- /dev/null +++ b/maemo/alsa-dsp.c @@ -0,0 +1,772 @@ +/** + * @file alsa-dsp.c + * @brief Alsa External plugin: I/O plugin + *

+ * Copyright (C) 2006 Nokia Corporation + *

+ * Contact: Eduardo Bezerra Valentin + * + * 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 +#include +#include +#include +#include "list.h" +#include "debug.h" +#include "dsp-protocol.h" +#include "constants.h" + +#define ARRAY_SIZE(ary) (sizeof(ary)/sizeof(ary[0])) +/** + * Device node file name list. + */ +typedef struct { + char *device; + struct list_head list; +} device_list_t; + +/** + * Holds the need information: list of playback and recording devices, + * current format, sample_rate, bytes per frame and pointer to ring + * buffer. + */ +typedef struct snd_pcm_alsa_dsp { + snd_pcm_ioplug_t io; + dsp_protocol_t *dsp_protocol; + int format; + int sample_rate; + int bytes_per_frame; + snd_pcm_sframes_t hw_pointer; + device_list_t playback_devices; + device_list_t recording_devices; +} snd_pcm_alsa_dsp_t; + +static snd_pcm_alsa_dsp_t *free_ref; +/** + * @param io pcm io plugin configured to Alsa libs. + * + * It starts the playback sending a DSP_CMD_PLAY. + * + * @return zero if success, otherwise a negative error code. + */ +static int alsa_dsp_start(snd_pcm_ioplug_t * io) +{ + snd_pcm_alsa_dsp_t *alsa_dsp = io->private_data; + int ret; + DENTER(); + DPRINT("IO_STREAM %d == SND_PCM_STREAM_PLAYBACK %d\n", io->stream, + io->stream == SND_PCM_STREAM_PLAYBACK); + if (io->stream != SND_PCM_STREAM_PLAYBACK) + dsp_protocol_set_mic_enabled(alsa_dsp->dsp_protocol, 1); + ret = dsp_protocol_send_play(alsa_dsp->dsp_protocol); + DLEAVE(ret); + return ret; +} + +/** + * @param io the pcm io plugin we configured to Alsa libs. + * + * It starts the playback sending a DSP_CMD_STOP. + * + * @return zero if success, otherwise a negative error code. + */ +static int alsa_dsp_stop(snd_pcm_ioplug_t * io) +{ + snd_pcm_alsa_dsp_t *alsa_dsp = io->private_data; + int ret; + DENTER(); + ret = dsp_protocol_send_stop(alsa_dsp->dsp_protocol); + if (io->stream != SND_PCM_STREAM_PLAYBACK) + dsp_protocol_set_mic_enabled(alsa_dsp->dsp_protocol, 0); + + DLEAVE(ret); + return ret; +} + +/** + * @param io the pcm io plugin we configured to Alsa libs. + * + * It returns the position of current period consuming. + * + * @return on success, returns current position, otherwise a negative + * error code. + */ +static snd_pcm_sframes_t alsa_dsp_pointer(snd_pcm_ioplug_t * io) +{ + snd_pcm_alsa_dsp_t *alsa_dsp = io->private_data; + snd_pcm_sframes_t ret; + DENTER(); + ret = alsa_dsp->hw_pointer; + if (alsa_dsp->hw_pointer == 0) + alsa_dsp->hw_pointer = + io->period_size * alsa_dsp->bytes_per_frame; + else + alsa_dsp->hw_pointer = 0; + DLEAVE((int)ret); + return ret; +} + +/** + * @param io the pcm io plugin we configured to Alsa libs. + * + * It transfers the audio data to dsp side. + * + * @return on success, returns amount of data transfered, + * otherwise a negative error code. + */ +static snd_pcm_sframes_t alsa_dsp_transfer(snd_pcm_ioplug_t * io, + const snd_pcm_channel_area_t * areas, + snd_pcm_uframes_t offset, + snd_pcm_uframes_t size) +{ + snd_pcm_alsa_dsp_t *alsa_dsp = io->private_data; + DENTER(); + char *buf; + int words; + ssize_t result; + + words = size * alsa_dsp->bytes_per_frame; + words /= 2; + DPRINT("***** Info: words %d size %lu bpf: %d\n", words, size, + alsa_dsp->bytes_per_frame); + if (words > alsa_dsp->dsp_protocol->mmap_buffer_size) { + DERROR("Requested too much data transfer (playing only %d)\n", + alsa_dsp->dsp_protocol->mmap_buffer_size); + words = alsa_dsp->dsp_protocol->mmap_buffer_size; + } + if (alsa_dsp->dsp_protocol->state != STATE_PLAYING) { + DPRINT("I did nothing - No start sent\n"); + alsa_dsp_start(io); + } + /* we handle only an interleaved buffer */ + buf = (char *)areas->addr + (areas->first + areas->step * offset) / 8; + if (io->stream == SND_PCM_STREAM_PLAYBACK) + result = + dsp_protocol_send_audio_data(alsa_dsp->dsp_protocol, buf, + words); + else + result = + dsp_protocol_receive_audio_data(alsa_dsp->dsp_protocol, buf, + words); + result *= 2; + result /= alsa_dsp->bytes_per_frame; + out: + alsa_dsp->hw_pointer += result; + DLEAVE(result); + return result; +} + +/** + * @param device_list a list of device names to be freed. + * + * It passes a list of device names and frees each node. + * + * @return zero (success). + */ +static int free_device_list(device_list_t * device_list) +{ + struct list_head *pos, *q; + device_list_t *tmp; + list_for_each_safe(pos, q, &device_list->list) { + tmp = list_entry(pos, device_list_t, list); + list_del(pos); + free(tmp->device); + free(tmp); + } + return 0; +} + +/** + * @param io the pcm io plugin we configured to Alsa libs. + * + * Closes the connection with the pcm dsp task. It + * destroies all allocated data. + * + * @return zero if success, otherwise a negative error code. + */ +static int alsa_dsp_close(snd_pcm_ioplug_t * io) +{ + snd_pcm_alsa_dsp_t *alsa_dsp = io->private_data; + int ret = 0; + DENTER(); + ret = dsp_protocol_close_node(alsa_dsp->dsp_protocol); + dsp_protocol_destroy(&(alsa_dsp->dsp_protocol)); + free_device_list(&(alsa_dsp->playback_devices)); + free_device_list(&(alsa_dsp->recording_devices)); + DLEAVE(ret); + return ret; +} + +/** + * @param map the values to be mapped + * @param value the search key + * @param steps how many keys should be checked + * + * Maps a value to another. + * + * @return on success, returns mapped value, otherwise a negative error code. + */ +static int map_value(int *map, int value, int steps) +{ + int i; + for (i = 0; i < steps; i++) + if (map[i * 2] == value) + return map[i * 2 + 1]; + return -1; +} + +/** + * @param io the pcm io plugin we configured to Alsa libs. + * @param params + * + * It checks if the pcm format and rate are supported. + * + * @return zero if success, otherwise a negative error code. + */ +static int alsa_dsp_hw_params(snd_pcm_ioplug_t * io, + snd_pcm_hw_params_t * params) +{ + snd_pcm_alsa_dsp_t *alsa_dsp = io->private_data; + int ret = 0; + int map_sample_rates[] = { + 8000, SAMPLE_RATE_8KHZ, + 11025, SAMPLE_RATE_11_025KHZ, + 12000, SAMPLE_RATE_12KHZ, + 16000, SAMPLE_RATE_16KHZ, + 22050, SAMPLE_RATE_22_05KHZ, + 24000, SAMPLE_RATE_24KHZ, + 32000, SAMPLE_RATE_32KHZ, + 44100, SAMPLE_RATE_44_1KHZ, + 48000, SAMPLE_RATE_48KHZ + }; + int map_formats[] = { + SND_PCM_FORMAT_A_LAW, DSP_AFMT_ALAW, + SND_PCM_FORMAT_MU_LAW, DSP_AFMT_ULAW, + SND_PCM_FORMAT_S16_LE, DSP_AFMT_S16_LE, + SND_PCM_FORMAT_U8, DSP_AFMT_U8, + SND_PCM_FORMAT_S8, DSP_AFMT_S8, + SND_PCM_FORMAT_S16_BE, DSP_AFMT_S16_BE, + SND_PCM_FORMAT_U16_LE, DSP_AFMT_U16_LE, + SND_PCM_FORMAT_U16_BE, DSP_AFMT_U16_BE + }; + DENTER(); + DPRINT("Checking Format- Ret %d\n", ret); + alsa_dsp->format = map_value(map_formats, io->format, + io->stream == + SND_PCM_STREAM_PLAYBACK ? + ARRAY_SIZE(map_formats) : 3); + if (alsa_dsp->format < 0) { + DERROR("*** ALSA-DSP: unsupported format %s\n", + snd_pcm_format_name(io->format)); + ret = -EINVAL; + } + DPRINT("Format is Ok. Checking rate. Ret %d\n", ret); + + alsa_dsp->sample_rate = map_value(map_sample_rates, io->rate, + io->stream == + SND_PCM_STREAM_PLAYBACK ? + ARRAY_SIZE(map_sample_rates) : 1); + if (alsa_dsp->sample_rate < 0) { + ret = -EINVAL; + DERROR("** ALSA - DSP - Unsuported Sample Rate! **\n"); + } + DPRINT("Rate is ok. Calculating WPF. Ret %d\n", ret); + + alsa_dsp->bytes_per_frame = + ((snd_pcm_format_physical_width(io->format) * io->channels) / 8); + DPRINT("WPF: %d width %d channels %d\n", alsa_dsp->bytes_per_frame, + snd_pcm_format_physical_width(io->format), io->channels); + + DLEAVE(ret); + return ret; +} + +/** + * @param io the pcm io plugin we configured to Alsa libs. + * + * It sends the audio parameters to pcm task node (formats, channels, + * access, rates). It is assumed that everything is proper set. + * + * @return zero if success, otherwise a negative error code. + */ +static int alsa_dsp_prepare(snd_pcm_ioplug_t * io) +{ + snd_pcm_alsa_dsp_t *alsa_dsp = io->private_data; + audio_params_data_t params; + speech_params_data_t sparams; + int ret = 0; + char *tmp; + DENTER(); + + alsa_dsp->hw_pointer = 0; + if (alsa_dsp->dsp_protocol->state != STATE_INITIALISED) { + tmp = strdup(alsa_dsp->dsp_protocol->device); + ret = dsp_protocol_close_node(alsa_dsp->dsp_protocol); + if (!ret) + dsp_protocol_open_node(alsa_dsp->dsp_protocol, tmp); + free(tmp); + } + if (ret == 0) { + if (io->stream == SND_PCM_STREAM_PLAYBACK) { + params.dsp_cmd = DSP_CMD_SET_PARAMS; + params.dsp_audio_fmt = alsa_dsp->format; + params.sample_rate = alsa_dsp->sample_rate; + params.number_channels = io->channels; + params.ds_stream_id = 0; + params.stream_priority = 0; + if (dsp_protocol_send_audio_params + (alsa_dsp->dsp_protocol, ¶ms) < 0) { + ret = -EIO; + DERROR("Error in send params data\n"); + } else + DPRINT("Sending params data is ok\n"); + } else { + sparams.dsp_cmd = DSP_CMD_SET_SPEECH_PARAMS; + sparams.audio_fmt = alsa_dsp->format; + sparams.sample_rate = alsa_dsp->sample_rate; + sparams.ds_stream_id = 0; + sparams.stream_priority = 0; + sparams.frame_size = io->period_size; + DPRINT("frame size %u\n", sparams.frame_size); + if (dsp_protocol_send_speech_params + (alsa_dsp->dsp_protocol, &sparams) < 0) { + ret = -EIO; + DERROR("Error in send speech params data\n"); + } else + DPRINT("Sending speech params data is ok\n"); + + } + } + DLEAVE(ret); + return ret; +} + +/** + * @param io the pcm io plugin we configured to Alsa libs. + * + * It pauses the playback sending a DSP_CMD_PAUSE. + * + * @return zero if success, otherwise a negative error code. + */ +static int alsa_dsp_pause(snd_pcm_ioplug_t * io, int enable) +{ + snd_pcm_alsa_dsp_t *alsa_dsp = io->private_data; + int ret; + DENTER(); + ret = dsp_protocol_send_pause(alsa_dsp->dsp_protocol); + DLEAVE(ret); + return ret; +} + +/** + * @param io the pcm io plugin we configured to Alsa libs. + * + * It starts the playback sending a DSP_CMD_PLAY. + * + * @return zero if success, otherwise a negative error code. + */ +static int alsa_dsp_resume(snd_pcm_ioplug_t * io) +{ + snd_pcm_alsa_dsp_t *alsa_dsp = io->private_data; + int ret; + DENTER(); + ret = dsp_protocol_send_play(alsa_dsp->dsp_protocol); + DLEAVE(ret); + return ret; +} + +/** + * @param alsa_dsp the structure to be configured. + * + * It configures constraints about formats, channels, access, rates, + * periods and buffer size. It exports the supported constraints by the + * dsp task node to the alsa plugin library. + * + * @return zero if success, otherwise a negative error code. + */ +static int alsa_dsp_configure_constraints(snd_pcm_alsa_dsp_t * alsa_dsp) +{ + snd_pcm_ioplug_t *io = &alsa_dsp->io; + static snd_pcm_access_t access_list[] = { + SND_PCM_ACCESS_RW_INTERLEAVED + }; + const unsigned int formats[] = { + SND_PCM_FORMAT_U8, /* DSP_AFMT_U8 */ + SND_PCM_FORMAT_S16_LE, /* DSP_AFMT_S16_LE */ + SND_PCM_FORMAT_S16_BE, /* DSP_AFMT_S16_BE */ + SND_PCM_FORMAT_S8, /* DSP_AFMT_S8 */ + SND_PCM_FORMAT_U16_LE, /* DSP_AFMT_U16_LE */ + SND_PCM_FORMAT_U16_BE, /* DSP_AFMT_U16_BE */ + SND_PCM_FORMAT_A_LAW, /* DSP_AFMT_ALAW */ + SND_PCM_FORMAT_MU_LAW /* DSP_AFMT_ULAW */ + }; + const unsigned int formats_recor[] = { + SND_PCM_FORMAT_S16_LE, /* DSP_AFMT_S16_LE */ + SND_PCM_FORMAT_A_LAW, /* DSP_AFMT_ALAW */ + SND_PCM_FORMAT_MU_LAW /* DSP_AFMT_ULAW */ + }; + static unsigned int bytes_list[] = { + 1U << 11, 1U << 12 + }; + static unsigned int bytes_list_rec_8bit[] = { + /* It must be multiple of 80... less than or equal to 800 */ + 80, 160, 240, 320, 400, 480, 560, 640, 720, 800 + }; + + int ret, err; + DENTER(); + /* Configuring access */ + if ((err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS, + ARRAY_SIZE(access_list), + access_list)) < 0) { + ret = err; + goto out; + } + if (io->stream == SND_PCM_STREAM_PLAYBACK) { + /* Configuring formats */ + if ((err = + snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT, + ARRAY_SIZE(formats), + formats)) < 0) { + ret = err; + goto out; + } + /* Configuring channels */ + if ((err = + snd_pcm_ioplug_set_param_minmax(io, + SND_PCM_IOPLUG_HW_CHANNELS, + 1, 2)) < 0) { + ret = err; + goto out; + } + + /* Configuring rates */ + if ((err = + snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE, + 8000, 48000)) < 0) { + ret = err; + goto out; + } + /* Configuring periods */ + if ((err = + snd_pcm_ioplug_set_param_list(io, + SND_PCM_IOPLUG_HW_PERIOD_BYTES, + ARRAY_SIZE(bytes_list), + bytes_list)) < 0) { + ret = err; + goto out; + } + /* Configuring buffer size */ + if ((err = + snd_pcm_ioplug_set_param_list(io, + SND_PCM_IOPLUG_HW_BUFFER_BYTES, + ARRAY_SIZE(bytes_list), + bytes_list)) < 0) { + ret = err; + goto out; + } + + } else { + /* Configuring formats */ + if ((err = + snd_pcm_ioplug_set_param_list(io, + SND_PCM_IOPLUG_HW_FORMAT, + ARRAY_SIZE(formats_recor), + formats_recor)) < 0) { + ret = err; + goto out; + } + /* Configuring channels */ + if ((err = snd_pcm_ioplug_set_param_minmax(io, + SND_PCM_IOPLUG_HW_CHANNELS, + 1, 1)) < 0) { + ret = err; + goto out; + } + + /* Configuring rates */ + if ((err = + snd_pcm_ioplug_set_param_minmax(io, + SND_PCM_IOPLUG_HW_RATE, + 8000, 8000)) < 0) { + ret = err; + goto out; + } + /* Configuring periods */ + if ((err = + snd_pcm_ioplug_set_param_list(io, + SND_PCM_IOPLUG_HW_PERIOD_BYTES, + ARRAY_SIZE + (bytes_list_rec_8bit), + bytes_list_rec_8bit)) < 0) { + ret = err; + goto out; + } + /* Configuring buffer size */ + if ((err = + snd_pcm_ioplug_set_param_list(io, + SND_PCM_IOPLUG_HW_BUFFER_BYTES, + ARRAY_SIZE + (bytes_list_rec_8bit), + bytes_list_rec_8bit)) < 0) { + ret = err; + goto out; + } + + } + + if ((err = snd_pcm_ioplug_set_param_minmax(io, + SND_PCM_IOPLUG_HW_PERIODS, + 2, 1024)) < 0) { + ret = err; + goto out; + } + ret = 0; + out: + DLEAVE(ret); + return ret; +} + +/** + * Alsa-lib callback structure. + */ +static snd_pcm_ioplug_callback_t alsa_dsp_callback = { + .start = alsa_dsp_start, + .stop = alsa_dsp_stop, + .pointer = alsa_dsp_pointer, + .transfer = alsa_dsp_transfer, + .close = alsa_dsp_close, + .hw_params = alsa_dsp_hw_params, + .prepare = alsa_dsp_prepare, + .pause = alsa_dsp_pause, + .resume = alsa_dsp_resume, +}; + +/** + * @param alsa_dsp the structure to be configured. + * + * It probes all configured dsp task devices to be available for + * this plugin. It will use first dsp task device whose is in + * UNINITIALISED state. + * + * @return zero if success, otherwise a negative error code. + */ +static int alsa_dsp_open_dsp_task(snd_pcm_alsa_dsp_t * alsa_dsp, + device_list_t * device_list) +{ + int err = -EINVAL; + device_list_t *tmp; + DENTER(); + DPRINT("Looking for a dsp device node \n"); + list_for_each_entry(tmp, &(device_list->list), list) { + DPRINT("Trying to use %s\n", tmp->device); + if ((err = + dsp_protocol_open_node(alsa_dsp->dsp_protocol, + tmp->device)) < 0) { + DPRINT("%s is not available now\n", tmp->device); + dsp_protocol_close_node(alsa_dsp->dsp_protocol); + } else + break; + } + if (err < 0) { + DPRINT("No valid dsp task nodes for now. Exiting.\n"); + } + DLEAVE(err); + return err; +} + +/** + * @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_string_list(snd_config_t * n, device_list_t * device_list) +{ + snd_config_iterator_t j, nextj; + device_list_t *tmp; + long idx = 0; + int ret; + DENTER(); + INIT_LIST_HEAD(&device_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); + ret = -EINVAL; + goto out; + } + if (k == idx) { + idx++; + /* add to available dsp task nodes */ + tmp = (device_list_t *) malloc(sizeof(device_list_t)); + if (snd_config_get_ascii(s, &(tmp->device)) < 0) { + SNDERR("invalid ascii string for id %s\n", + id_number); + ret = -EINVAL; + goto out; + } + + list_add(&(tmp->list), &(device_list->list)); + } + + } + ret = 0; + out: + DLEAVE(ret); + return ret; +} + +/** + * It initializes the alsa plugin. It reads the parameters and creates the + * connection with the pcm device file. + * + * @return zero if success, otherwise a negative error code. + */ +SND_PCM_PLUGIN_DEFINE_FUNC(alsa_dsp) +{ + snd_config_iterator_t i, next; + snd_pcm_alsa_dsp_t *alsa_dsp; + int err; + int ret; + DENTER(); + + /* Allocate the structure */ + alsa_dsp = calloc(1, sizeof(snd_pcm_alsa_dsp_t)); + if (alsa_dsp == 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_device_file") == 0) { + if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND){ + if ((err = + fill_string_list(n, + &(alsa_dsp->playback_devices))) < 0) { + SNDERR("Could not fill string" + " list for playback devices\n"); + goto error; + } + } else { + SNDERR("Invalid type for %s", id); + err = -EINVAL; + goto error; + } + + continue; + } + if (strcmp(id, "recording_device_file") == 0) { + if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND){ + if ((err = + fill_string_list(n, + &(alsa_dsp->recording_devices))) < 0){ + SNDERR("Could not fill string" + " list for recording devices\n"); + goto error; + } + } else { + SNDERR("Invalid type for %s", id); + err = -EINVAL; + goto error; + } + + continue; + } + SNDERR("Unknown field %s", id); + err = -EINVAL; + goto error; + } + /* Initialise the dsp_protocol and create connection */ + if ((err = dsp_protocol_create(&(alsa_dsp->dsp_protocol))) < 0) + goto error; + if ((err = alsa_dsp_open_dsp_task(alsa_dsp, + (stream == SND_PCM_STREAM_PLAYBACK) ? + &(alsa_dsp->playback_devices) : + &(alsa_dsp->recording_devices))) < 0) + goto error; + /* Initialise the snd_pcm_ioplug_t */ + alsa_dsp->io.version = SND_PCM_IOPLUG_VERSION; + alsa_dsp->io.name = "Alsa - DSP PCM Plugin"; + alsa_dsp->io.mmap_rw = 0; + alsa_dsp->io.callback = &alsa_dsp_callback; + alsa_dsp->io.poll_fd = alsa_dsp->dsp_protocol->fd; + alsa_dsp->io.poll_events = stream == SND_PCM_STREAM_PLAYBACK ? + POLLOUT : POLLIN; + + alsa_dsp->io.private_data = alsa_dsp; + free_ref = alsa_dsp; + + if ((err = snd_pcm_ioplug_create(&alsa_dsp->io, name, + stream, mode)) < 0) + goto error; + + /* Configure the plugin */ + if ((err = alsa_dsp_configure_constraints(alsa_dsp)) < 0) { + snd_pcm_ioplug_delete(&alsa_dsp->io); + goto error; + } + *pcmp = alsa_dsp->io.pcm; + ret = 0; + goto out; + error: + ret = err; + free(alsa_dsp); + out: + DLEAVE(ret); + return ret; +} + + +void alsa_dsp_descructor(void) __attribute__ ((destructor)); + +void alsa_dsp_descructor(void) +{ + DENTER(); + DPRINT("alsa dsp destructor\n"); + DPRINT("checking for memories leaks and releasing resources\n"); + if (free_ref) { + if (free_ref->dsp_protocol) { + dsp_protocol_close_node(free_ref->dsp_protocol); + dsp_protocol_destroy(&(free_ref->dsp_protocol)); + } + free_device_list(&(free_ref->playback_devices)); + + free_device_list(&(free_ref->recording_devices)); + + free(free_ref); + free_ref = NULL; + } + DLEAVE(0); + +} + +SND_PCM_PLUGIN_SYMBOL(alsa_dsp); -- cgit