xref: /third_party/alsa-lib/src/pcm/pcm_extplug.c (revision d5ac70f0)
1d5ac70f0Sopenharmony_ci/**
2d5ac70f0Sopenharmony_ci * \file pcm/pcm_extplug.c
3d5ac70f0Sopenharmony_ci * \ingroup Plugin_SDK
4d5ac70f0Sopenharmony_ci * \brief External Filter Plugin SDK
5d5ac70f0Sopenharmony_ci * \author Takashi Iwai <tiwai@suse.de>
6d5ac70f0Sopenharmony_ci * \date 2005
7d5ac70f0Sopenharmony_ci */
8d5ac70f0Sopenharmony_ci/*
9d5ac70f0Sopenharmony_ci *  PCM - External Filter Plugin SDK
10d5ac70f0Sopenharmony_ci *  Copyright (c) 2005 by Takashi Iwai <tiwai@suse.de>
11d5ac70f0Sopenharmony_ci *
12d5ac70f0Sopenharmony_ci *
13d5ac70f0Sopenharmony_ci *   This library is free software; you can redistribute it and/or modify
14d5ac70f0Sopenharmony_ci *   it under the terms of the GNU Lesser General Public License as
15d5ac70f0Sopenharmony_ci *   published by the Free Software Foundation; either version 2.1 of
16d5ac70f0Sopenharmony_ci *   the License, or (at your option) any later version.
17d5ac70f0Sopenharmony_ci *
18d5ac70f0Sopenharmony_ci *   This program is distributed in the hope that it will be useful,
19d5ac70f0Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
20d5ac70f0Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21d5ac70f0Sopenharmony_ci *   GNU Lesser General Public License for more details.
22d5ac70f0Sopenharmony_ci *
23d5ac70f0Sopenharmony_ci *   You should have received a copy of the GNU Lesser General Public
24d5ac70f0Sopenharmony_ci *   License along with this library; if not, write to the Free Software
25d5ac70f0Sopenharmony_ci *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
26d5ac70f0Sopenharmony_ci *
27d5ac70f0Sopenharmony_ci */
28d5ac70f0Sopenharmony_ci
29d5ac70f0Sopenharmony_ci#include "pcm_local.h"
30d5ac70f0Sopenharmony_ci#include "pcm_plugin.h"
31d5ac70f0Sopenharmony_ci#include "pcm_extplug.h"
32d5ac70f0Sopenharmony_ci#include "pcm_ext_parm.h"
33d5ac70f0Sopenharmony_ci
34d5ac70f0Sopenharmony_ci#ifndef PIC
35d5ac70f0Sopenharmony_ci/* entry for static linking */
36d5ac70f0Sopenharmony_ciconst char *_snd_module_pcm_extplug = "";
37d5ac70f0Sopenharmony_ci#endif
38d5ac70f0Sopenharmony_ci
39d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN
40d5ac70f0Sopenharmony_ci
41d5ac70f0Sopenharmony_citypedef struct snd_pcm_extplug_priv {
42d5ac70f0Sopenharmony_ci	snd_pcm_plugin_t plug;
43d5ac70f0Sopenharmony_ci	snd_pcm_extplug_t *data;
44d5ac70f0Sopenharmony_ci	struct snd_ext_parm params[SND_PCM_EXTPLUG_HW_PARAMS];
45d5ac70f0Sopenharmony_ci	struct snd_ext_parm sparams[SND_PCM_EXTPLUG_HW_PARAMS];
46d5ac70f0Sopenharmony_ci} extplug_priv_t;
47d5ac70f0Sopenharmony_ci
48d5ac70f0Sopenharmony_cistatic const int hw_params_type[SND_PCM_EXTPLUG_HW_PARAMS] = {
49d5ac70f0Sopenharmony_ci	[SND_PCM_EXTPLUG_HW_FORMAT] = SND_PCM_HW_PARAM_FORMAT,
50d5ac70f0Sopenharmony_ci	[SND_PCM_EXTPLUG_HW_CHANNELS] = SND_PCM_HW_PARAM_CHANNELS
51d5ac70f0Sopenharmony_ci};
52d5ac70f0Sopenharmony_ci
53d5ac70f0Sopenharmony_ci#define is_mask_type(i) (hw_params_type[i] < SND_PCM_HW_PARAM_FIRST_INTERVAL)
54d5ac70f0Sopenharmony_ci
55d5ac70f0Sopenharmony_cistatic const unsigned int excl_parbits[SND_PCM_EXTPLUG_HW_PARAMS] = {
56d5ac70f0Sopenharmony_ci	[SND_PCM_EXTPLUG_HW_FORMAT] = (SND_PCM_HW_PARBIT_FORMAT|
57d5ac70f0Sopenharmony_ci				       SND_PCM_HW_PARBIT_SUBFORMAT |
58d5ac70f0Sopenharmony_ci				       SND_PCM_HW_PARBIT_SAMPLE_BITS),
59d5ac70f0Sopenharmony_ci	[SND_PCM_EXTPLUG_HW_CHANNELS] = (SND_PCM_HW_PARBIT_CHANNELS|
60d5ac70f0Sopenharmony_ci					 SND_PCM_HW_PARBIT_FRAME_BITS),
61d5ac70f0Sopenharmony_ci};
62d5ac70f0Sopenharmony_ci
63d5ac70f0Sopenharmony_ci/*
64d5ac70f0Sopenharmony_ci * set min/max values for the given parameter
65d5ac70f0Sopenharmony_ci */
66d5ac70f0Sopenharmony_ciint snd_ext_parm_set_minmax(struct snd_ext_parm *parm, unsigned int min, unsigned int max)
67d5ac70f0Sopenharmony_ci{
68d5ac70f0Sopenharmony_ci	parm->num_list = 0;
69d5ac70f0Sopenharmony_ci	free(parm->list);
70d5ac70f0Sopenharmony_ci	parm->list = NULL;
71d5ac70f0Sopenharmony_ci	parm->min = min;
72d5ac70f0Sopenharmony_ci	parm->max = max;
73d5ac70f0Sopenharmony_ci	parm->active = 1;
74d5ac70f0Sopenharmony_ci	return 0;
75d5ac70f0Sopenharmony_ci}
76d5ac70f0Sopenharmony_ci
77d5ac70f0Sopenharmony_ci/*
78d5ac70f0Sopenharmony_ci * set the list of available values for the given parameter
79d5ac70f0Sopenharmony_ci */
80d5ac70f0Sopenharmony_cistatic int val_compar(const void *ap, const void *bp)
81d5ac70f0Sopenharmony_ci{
82d5ac70f0Sopenharmony_ci	return *(const unsigned int *)ap - *(const unsigned int *)bp;
83d5ac70f0Sopenharmony_ci}
84d5ac70f0Sopenharmony_ci
85d5ac70f0Sopenharmony_ciint snd_ext_parm_set_list(struct snd_ext_parm *parm, unsigned int num_list, const unsigned int *list)
86d5ac70f0Sopenharmony_ci{
87d5ac70f0Sopenharmony_ci	unsigned int *new_list;
88d5ac70f0Sopenharmony_ci
89d5ac70f0Sopenharmony_ci	new_list = malloc(sizeof(*new_list) * num_list);
90d5ac70f0Sopenharmony_ci	if (new_list == NULL)
91d5ac70f0Sopenharmony_ci		return -ENOMEM;
92d5ac70f0Sopenharmony_ci	memcpy(new_list, list, sizeof(*new_list) * num_list);
93d5ac70f0Sopenharmony_ci	qsort(new_list, num_list, sizeof(*new_list), val_compar);
94d5ac70f0Sopenharmony_ci
95d5ac70f0Sopenharmony_ci	free(parm->list);
96d5ac70f0Sopenharmony_ci	parm->num_list = num_list;
97d5ac70f0Sopenharmony_ci	parm->list = new_list;
98d5ac70f0Sopenharmony_ci	parm->active = 1;
99d5ac70f0Sopenharmony_ci	return 0;
100d5ac70f0Sopenharmony_ci}
101d5ac70f0Sopenharmony_ci
102d5ac70f0Sopenharmony_civoid snd_ext_parm_clear(struct snd_ext_parm *parm)
103d5ac70f0Sopenharmony_ci{
104d5ac70f0Sopenharmony_ci	free(parm->list);
105d5ac70f0Sopenharmony_ci	memset(parm, 0, sizeof(*parm));
106d5ac70f0Sopenharmony_ci}
107d5ac70f0Sopenharmony_ci
108d5ac70f0Sopenharmony_ci/*
109d5ac70f0Sopenharmony_ci * limit the interval to the given list
110d5ac70f0Sopenharmony_ci */
111d5ac70f0Sopenharmony_ciint snd_interval_list(snd_interval_t *ival, int num_list, unsigned int *list)
112d5ac70f0Sopenharmony_ci{
113d5ac70f0Sopenharmony_ci	int imin, imax;
114d5ac70f0Sopenharmony_ci	int changed = 0;
115d5ac70f0Sopenharmony_ci
116d5ac70f0Sopenharmony_ci	if (snd_interval_empty(ival))
117d5ac70f0Sopenharmony_ci		return -ENOENT;
118d5ac70f0Sopenharmony_ci	for (imin = 0; imin < num_list; imin++) {
119d5ac70f0Sopenharmony_ci		if (ival->min == list[imin] && ! ival->openmin)
120d5ac70f0Sopenharmony_ci			break;
121d5ac70f0Sopenharmony_ci		if (ival->min <= list[imin]) {
122d5ac70f0Sopenharmony_ci			ival->min = list[imin];
123d5ac70f0Sopenharmony_ci			ival->openmin = 0;
124d5ac70f0Sopenharmony_ci			changed = 1;
125d5ac70f0Sopenharmony_ci			break;
126d5ac70f0Sopenharmony_ci		}
127d5ac70f0Sopenharmony_ci	}
128d5ac70f0Sopenharmony_ci	if (imin >= num_list)
129d5ac70f0Sopenharmony_ci		return -EINVAL;
130d5ac70f0Sopenharmony_ci	for (imax = num_list - 1; imax >= imin; imax--) {
131d5ac70f0Sopenharmony_ci		if (ival->max == list[imax] && ! ival->openmax)
132d5ac70f0Sopenharmony_ci			break;
133d5ac70f0Sopenharmony_ci		if (ival->max >= list[imax]) {
134d5ac70f0Sopenharmony_ci			ival->max = list[imax];
135d5ac70f0Sopenharmony_ci			ival->openmax = 0;
136d5ac70f0Sopenharmony_ci			changed = 1;
137d5ac70f0Sopenharmony_ci			break;
138d5ac70f0Sopenharmony_ci		}
139d5ac70f0Sopenharmony_ci	}
140d5ac70f0Sopenharmony_ci	if (imax < imin)
141d5ac70f0Sopenharmony_ci		return -EINVAL;
142d5ac70f0Sopenharmony_ci	return changed;
143d5ac70f0Sopenharmony_ci}
144d5ac70f0Sopenharmony_ci
145d5ac70f0Sopenharmony_ci/*
146d5ac70f0Sopenharmony_ci * refine the interval parameter
147d5ac70f0Sopenharmony_ci */
148d5ac70f0Sopenharmony_ciint snd_ext_parm_interval_refine(snd_interval_t *ival, struct snd_ext_parm *parm, int type)
149d5ac70f0Sopenharmony_ci{
150d5ac70f0Sopenharmony_ci	parm += type;
151d5ac70f0Sopenharmony_ci	if (! parm->active)
152d5ac70f0Sopenharmony_ci		return 0;
153d5ac70f0Sopenharmony_ci	ival->integer |= parm->integer;
154d5ac70f0Sopenharmony_ci	if (parm->num_list) {
155d5ac70f0Sopenharmony_ci		return snd_interval_list(ival, parm->num_list, parm->list);
156d5ac70f0Sopenharmony_ci	} else if (parm->min || parm->max) {
157d5ac70f0Sopenharmony_ci		snd_interval_t t;
158d5ac70f0Sopenharmony_ci		memset(&t, 0, sizeof(t));
159d5ac70f0Sopenharmony_ci		snd_interval_set_minmax(&t, parm->min, parm->max);
160d5ac70f0Sopenharmony_ci		t.integer = ival->integer;
161d5ac70f0Sopenharmony_ci		return snd_interval_refine(ival, &t);
162d5ac70f0Sopenharmony_ci	}
163d5ac70f0Sopenharmony_ci	return 0;
164d5ac70f0Sopenharmony_ci}
165d5ac70f0Sopenharmony_ci
166d5ac70f0Sopenharmony_ci/*
167d5ac70f0Sopenharmony_ci * refine the mask parameter
168d5ac70f0Sopenharmony_ci */
169d5ac70f0Sopenharmony_ciint snd_ext_parm_mask_refine(snd_mask_t *mask, struct snd_ext_parm *parm, int type)
170d5ac70f0Sopenharmony_ci{
171d5ac70f0Sopenharmony_ci	snd_mask_t bits;
172d5ac70f0Sopenharmony_ci	unsigned int i;
173d5ac70f0Sopenharmony_ci
174d5ac70f0Sopenharmony_ci	parm += type;
175d5ac70f0Sopenharmony_ci	if (!parm->active)
176d5ac70f0Sopenharmony_ci		return 0;
177d5ac70f0Sopenharmony_ci	memset(&bits, 0, sizeof(bits));
178d5ac70f0Sopenharmony_ci	for (i = 0; i < parm->num_list; i++)
179d5ac70f0Sopenharmony_ci		bits.bits[parm->list[i] / 32] |= 1U << (parm->list[i] % 32);
180d5ac70f0Sopenharmony_ci	return snd_mask_refine(mask, &bits);
181d5ac70f0Sopenharmony_ci}
182d5ac70f0Sopenharmony_ci
183d5ac70f0Sopenharmony_ci
184d5ac70f0Sopenharmony_ci/*
185d5ac70f0Sopenharmony_ci * hw_refine callback
186d5ac70f0Sopenharmony_ci */
187d5ac70f0Sopenharmony_cistatic int extplug_hw_refine(snd_pcm_hw_params_t *hw_params,
188d5ac70f0Sopenharmony_ci			     struct snd_ext_parm *parm)
189d5ac70f0Sopenharmony_ci{
190d5ac70f0Sopenharmony_ci	int i, err, change = 0;
191d5ac70f0Sopenharmony_ci	for (i = 0; i < SND_PCM_EXTPLUG_HW_PARAMS; i++) {
192d5ac70f0Sopenharmony_ci		int type = hw_params_type[i];
193d5ac70f0Sopenharmony_ci		if (is_mask_type(i))
194d5ac70f0Sopenharmony_ci			err = snd_ext_parm_mask_refine(hw_param_mask(hw_params, type),
195d5ac70f0Sopenharmony_ci						       parm, i);
196d5ac70f0Sopenharmony_ci		else
197d5ac70f0Sopenharmony_ci			err = snd_ext_parm_interval_refine(hw_param_interval(hw_params, type),
198d5ac70f0Sopenharmony_ci							   parm, i);
199d5ac70f0Sopenharmony_ci		if (err < 0)
200d5ac70f0Sopenharmony_ci			return err;
201d5ac70f0Sopenharmony_ci		change |= err;
202d5ac70f0Sopenharmony_ci	}
203d5ac70f0Sopenharmony_ci	return change;
204d5ac70f0Sopenharmony_ci}
205d5ac70f0Sopenharmony_ci
206d5ac70f0Sopenharmony_cistatic int snd_pcm_extplug_hw_refine_cprepare(snd_pcm_t *pcm,
207d5ac70f0Sopenharmony_ci					      snd_pcm_hw_params_t *params)
208d5ac70f0Sopenharmony_ci{
209d5ac70f0Sopenharmony_ci	extplug_priv_t *ext = pcm->private_data;
210d5ac70f0Sopenharmony_ci	int err;
211d5ac70f0Sopenharmony_ci	snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
212d5ac70f0Sopenharmony_ci	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
213d5ac70f0Sopenharmony_ci					 &access_mask);
214d5ac70f0Sopenharmony_ci	if (err < 0)
215d5ac70f0Sopenharmony_ci		return err;
216d5ac70f0Sopenharmony_ci	err = extplug_hw_refine(params, ext->params);
217d5ac70f0Sopenharmony_ci	if (err < 0)
218d5ac70f0Sopenharmony_ci		return err;
219d5ac70f0Sopenharmony_ci	params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
220d5ac70f0Sopenharmony_ci	return 0;
221d5ac70f0Sopenharmony_ci}
222d5ac70f0Sopenharmony_ci
223d5ac70f0Sopenharmony_cistatic int snd_pcm_extplug_hw_refine_sprepare(snd_pcm_t *pcm,
224d5ac70f0Sopenharmony_ci					      snd_pcm_hw_params_t *sparams)
225d5ac70f0Sopenharmony_ci{
226d5ac70f0Sopenharmony_ci	extplug_priv_t *ext = pcm->private_data;
227d5ac70f0Sopenharmony_ci	snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
228d5ac70f0Sopenharmony_ci	_snd_pcm_hw_params_any(sparams);
229d5ac70f0Sopenharmony_ci	_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
230d5ac70f0Sopenharmony_ci				   &saccess_mask);
231d5ac70f0Sopenharmony_ci	extplug_hw_refine(sparams, ext->sparams);
232d5ac70f0Sopenharmony_ci	return 0;
233d5ac70f0Sopenharmony_ci}
234d5ac70f0Sopenharmony_ci
235d5ac70f0Sopenharmony_cistatic unsigned int get_links(struct snd_ext_parm *params)
236d5ac70f0Sopenharmony_ci{
237d5ac70f0Sopenharmony_ci	int i;
238d5ac70f0Sopenharmony_ci	unsigned int links = (SND_PCM_HW_PARBIT_FORMAT |
239d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_SUBFORMAT |
240d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_SAMPLE_BITS |
241d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_CHANNELS |
242d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_FRAME_BITS |
243d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_RATE |
244d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_PERIODS |
245d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
246d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_PERIOD_TIME |
247d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
248d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_BUFFER_TIME |
249d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_TICK_TIME);
250d5ac70f0Sopenharmony_ci
251d5ac70f0Sopenharmony_ci	for (i = 0; i < SND_PCM_EXTPLUG_HW_PARAMS; i++) {
252d5ac70f0Sopenharmony_ci		if (params[i].active && !params[i].keep_link)
253d5ac70f0Sopenharmony_ci			links &= ~excl_parbits[i];
254d5ac70f0Sopenharmony_ci	}
255d5ac70f0Sopenharmony_ci	return links;
256d5ac70f0Sopenharmony_ci}
257d5ac70f0Sopenharmony_ci
258d5ac70f0Sopenharmony_cistatic int snd_pcm_extplug_hw_refine_schange(snd_pcm_t *pcm,
259d5ac70f0Sopenharmony_ci					     snd_pcm_hw_params_t *params,
260d5ac70f0Sopenharmony_ci					     snd_pcm_hw_params_t *sparams)
261d5ac70f0Sopenharmony_ci{
262d5ac70f0Sopenharmony_ci	extplug_priv_t *ext = pcm->private_data;
263d5ac70f0Sopenharmony_ci	unsigned int links = get_links(ext->sparams);
264d5ac70f0Sopenharmony_ci
265d5ac70f0Sopenharmony_ci	return _snd_pcm_hw_params_refine(sparams, links, params);
266d5ac70f0Sopenharmony_ci}
267d5ac70f0Sopenharmony_ci
268d5ac70f0Sopenharmony_cistatic int snd_pcm_extplug_hw_refine_cchange(snd_pcm_t *pcm,
269d5ac70f0Sopenharmony_ci					     snd_pcm_hw_params_t *params,
270d5ac70f0Sopenharmony_ci					     snd_pcm_hw_params_t *sparams)
271d5ac70f0Sopenharmony_ci{
272d5ac70f0Sopenharmony_ci	extplug_priv_t *ext = pcm->private_data;
273d5ac70f0Sopenharmony_ci	unsigned int links = get_links(ext->params);
274d5ac70f0Sopenharmony_ci
275d5ac70f0Sopenharmony_ci	return _snd_pcm_hw_params_refine(params, links, sparams);
276d5ac70f0Sopenharmony_ci}
277d5ac70f0Sopenharmony_ci
278d5ac70f0Sopenharmony_cistatic int snd_pcm_extplug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
279d5ac70f0Sopenharmony_ci{
280d5ac70f0Sopenharmony_ci	int err = snd_pcm_hw_refine_slave(pcm, params,
281d5ac70f0Sopenharmony_ci				       snd_pcm_extplug_hw_refine_cprepare,
282d5ac70f0Sopenharmony_ci				       snd_pcm_extplug_hw_refine_cchange,
283d5ac70f0Sopenharmony_ci				       snd_pcm_extplug_hw_refine_sprepare,
284d5ac70f0Sopenharmony_ci				       snd_pcm_extplug_hw_refine_schange,
285d5ac70f0Sopenharmony_ci				       snd_pcm_generic_hw_refine);
286d5ac70f0Sopenharmony_ci	return err;
287d5ac70f0Sopenharmony_ci}
288d5ac70f0Sopenharmony_ci
289d5ac70f0Sopenharmony_ci/*
290d5ac70f0Sopenharmony_ci * hw_params callback
291d5ac70f0Sopenharmony_ci */
292d5ac70f0Sopenharmony_cistatic int snd_pcm_extplug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
293d5ac70f0Sopenharmony_ci{
294d5ac70f0Sopenharmony_ci
295d5ac70f0Sopenharmony_ci	extplug_priv_t *ext = pcm->private_data;
296d5ac70f0Sopenharmony_ci	snd_pcm_t *slave = ext->plug.gen.slave;
297d5ac70f0Sopenharmony_ci	int err = snd_pcm_hw_params_slave(pcm, params,
298d5ac70f0Sopenharmony_ci					  snd_pcm_extplug_hw_refine_cchange,
299d5ac70f0Sopenharmony_ci					  snd_pcm_extplug_hw_refine_sprepare,
300d5ac70f0Sopenharmony_ci					  snd_pcm_extplug_hw_refine_schange,
301d5ac70f0Sopenharmony_ci					  snd_pcm_generic_hw_params);
302d5ac70f0Sopenharmony_ci	if (err < 0)
303d5ac70f0Sopenharmony_ci		return err;
304d5ac70f0Sopenharmony_ci	ext->data->slave_format = slave->format;
305d5ac70f0Sopenharmony_ci	ext->data->slave_subformat = slave->subformat;
306d5ac70f0Sopenharmony_ci	ext->data->slave_channels = slave->channels;
307d5ac70f0Sopenharmony_ci	ext->data->rate = slave->rate;
308d5ac70f0Sopenharmony_ci	INTERNAL(snd_pcm_hw_params_get_format)(params, &ext->data->format);
309d5ac70f0Sopenharmony_ci	INTERNAL(snd_pcm_hw_params_get_subformat)(params, &ext->data->subformat);
310d5ac70f0Sopenharmony_ci	INTERNAL(snd_pcm_hw_params_get_channels)(params, &ext->data->channels);
311d5ac70f0Sopenharmony_ci
312d5ac70f0Sopenharmony_ci	if (ext->data->callback->hw_params) {
313d5ac70f0Sopenharmony_ci		err = ext->data->callback->hw_params(ext->data, params);
314d5ac70f0Sopenharmony_ci		if (err < 0)
315d5ac70f0Sopenharmony_ci			return err;
316d5ac70f0Sopenharmony_ci	}
317d5ac70f0Sopenharmony_ci	return 0;
318d5ac70f0Sopenharmony_ci}
319d5ac70f0Sopenharmony_ci
320d5ac70f0Sopenharmony_ci/*
321d5ac70f0Sopenharmony_ci * hw_free callback
322d5ac70f0Sopenharmony_ci */
323d5ac70f0Sopenharmony_cistatic int snd_pcm_extplug_hw_free(snd_pcm_t *pcm)
324d5ac70f0Sopenharmony_ci{
325d5ac70f0Sopenharmony_ci	extplug_priv_t *ext = pcm->private_data;
326d5ac70f0Sopenharmony_ci
327d5ac70f0Sopenharmony_ci	snd_pcm_hw_free(ext->plug.gen.slave);
328d5ac70f0Sopenharmony_ci	if (ext->data->callback->hw_free)
329d5ac70f0Sopenharmony_ci		return ext->data->callback->hw_free(ext->data);
330d5ac70f0Sopenharmony_ci	return 0;
331d5ac70f0Sopenharmony_ci}
332d5ac70f0Sopenharmony_ci
333d5ac70f0Sopenharmony_ci/*
334d5ac70f0Sopenharmony_ci * write_areas skeleton - call transfer callback
335d5ac70f0Sopenharmony_ci */
336d5ac70f0Sopenharmony_cistatic snd_pcm_uframes_t
337d5ac70f0Sopenharmony_cisnd_pcm_extplug_write_areas(snd_pcm_t *pcm,
338d5ac70f0Sopenharmony_ci			    const snd_pcm_channel_area_t *areas,
339d5ac70f0Sopenharmony_ci			    snd_pcm_uframes_t offset,
340d5ac70f0Sopenharmony_ci			    snd_pcm_uframes_t size,
341d5ac70f0Sopenharmony_ci			    const snd_pcm_channel_area_t *slave_areas,
342d5ac70f0Sopenharmony_ci			    snd_pcm_uframes_t slave_offset,
343d5ac70f0Sopenharmony_ci			    snd_pcm_uframes_t *slave_sizep)
344d5ac70f0Sopenharmony_ci{
345d5ac70f0Sopenharmony_ci	extplug_priv_t *ext = pcm->private_data;
346d5ac70f0Sopenharmony_ci
347d5ac70f0Sopenharmony_ci	if (size > *slave_sizep)
348d5ac70f0Sopenharmony_ci		size = *slave_sizep;
349d5ac70f0Sopenharmony_ci	size = ext->data->callback->transfer(ext->data, slave_areas, slave_offset,
350d5ac70f0Sopenharmony_ci					     areas, offset, size);
351d5ac70f0Sopenharmony_ci	*slave_sizep = size;
352d5ac70f0Sopenharmony_ci	return size;
353d5ac70f0Sopenharmony_ci}
354d5ac70f0Sopenharmony_ci
355d5ac70f0Sopenharmony_ci/*
356d5ac70f0Sopenharmony_ci * read_areas skeleton - call transfer callback
357d5ac70f0Sopenharmony_ci */
358d5ac70f0Sopenharmony_cistatic snd_pcm_uframes_t
359d5ac70f0Sopenharmony_cisnd_pcm_extplug_read_areas(snd_pcm_t *pcm,
360d5ac70f0Sopenharmony_ci			   const snd_pcm_channel_area_t *areas,
361d5ac70f0Sopenharmony_ci			   snd_pcm_uframes_t offset,
362d5ac70f0Sopenharmony_ci			   snd_pcm_uframes_t size,
363d5ac70f0Sopenharmony_ci			   const snd_pcm_channel_area_t *slave_areas,
364d5ac70f0Sopenharmony_ci			   snd_pcm_uframes_t slave_offset,
365d5ac70f0Sopenharmony_ci			   snd_pcm_uframes_t *slave_sizep)
366d5ac70f0Sopenharmony_ci{
367d5ac70f0Sopenharmony_ci	extplug_priv_t *ext = pcm->private_data;
368d5ac70f0Sopenharmony_ci
369d5ac70f0Sopenharmony_ci	if (size > *slave_sizep)
370d5ac70f0Sopenharmony_ci		size = *slave_sizep;
371d5ac70f0Sopenharmony_ci	size = ext->data->callback->transfer(ext->data, areas, offset,
372d5ac70f0Sopenharmony_ci					     slave_areas, slave_offset, size);
373d5ac70f0Sopenharmony_ci	*slave_sizep = size;
374d5ac70f0Sopenharmony_ci	return size;
375d5ac70f0Sopenharmony_ci}
376d5ac70f0Sopenharmony_ci
377d5ac70f0Sopenharmony_ci/*
378d5ac70f0Sopenharmony_ci * call init callback
379d5ac70f0Sopenharmony_ci */
380d5ac70f0Sopenharmony_cistatic int snd_pcm_extplug_init(snd_pcm_t *pcm)
381d5ac70f0Sopenharmony_ci{
382d5ac70f0Sopenharmony_ci	extplug_priv_t *ext = pcm->private_data;
383d5ac70f0Sopenharmony_ci	return ext->data->callback->init(ext->data);
384d5ac70f0Sopenharmony_ci}
385d5ac70f0Sopenharmony_ci
386d5ac70f0Sopenharmony_ci/*
387d5ac70f0Sopenharmony_ci * dump setup
388d5ac70f0Sopenharmony_ci */
389d5ac70f0Sopenharmony_cistatic void snd_pcm_extplug_dump(snd_pcm_t *pcm, snd_output_t *out)
390d5ac70f0Sopenharmony_ci{
391d5ac70f0Sopenharmony_ci	extplug_priv_t *ext = pcm->private_data;
392d5ac70f0Sopenharmony_ci
393d5ac70f0Sopenharmony_ci	if (ext->data->callback->dump)
394d5ac70f0Sopenharmony_ci		ext->data->callback->dump(ext->data, out);
395d5ac70f0Sopenharmony_ci	else {
396d5ac70f0Sopenharmony_ci		if (ext->data->name)
397d5ac70f0Sopenharmony_ci			snd_output_printf(out, "%s\n", ext->data->name);
398d5ac70f0Sopenharmony_ci		else
399d5ac70f0Sopenharmony_ci			snd_output_printf(out, "External PCM Plugin\n");
400d5ac70f0Sopenharmony_ci		if (pcm->setup) {
401d5ac70f0Sopenharmony_ci			snd_output_printf(out, "Its setup is:\n");
402d5ac70f0Sopenharmony_ci			snd_pcm_dump_setup(pcm, out);
403d5ac70f0Sopenharmony_ci		}
404d5ac70f0Sopenharmony_ci	}
405d5ac70f0Sopenharmony_ci	snd_output_printf(out, "Slave: ");
406d5ac70f0Sopenharmony_ci	snd_pcm_dump(ext->plug.gen.slave, out);
407d5ac70f0Sopenharmony_ci}
408d5ac70f0Sopenharmony_ci
409d5ac70f0Sopenharmony_cistatic void clear_ext_params(extplug_priv_t *ext)
410d5ac70f0Sopenharmony_ci{
411d5ac70f0Sopenharmony_ci	int i;
412d5ac70f0Sopenharmony_ci	for (i = 0; i < SND_PCM_EXTPLUG_HW_PARAMS; i++) {
413d5ac70f0Sopenharmony_ci		snd_ext_parm_clear(&ext->params[i]);
414d5ac70f0Sopenharmony_ci		snd_ext_parm_clear(&ext->sparams[i]);
415d5ac70f0Sopenharmony_ci	}
416d5ac70f0Sopenharmony_ci}
417d5ac70f0Sopenharmony_ci
418d5ac70f0Sopenharmony_cistatic int snd_pcm_extplug_close(snd_pcm_t *pcm)
419d5ac70f0Sopenharmony_ci{
420d5ac70f0Sopenharmony_ci	extplug_priv_t *ext = pcm->private_data;
421d5ac70f0Sopenharmony_ci
422d5ac70f0Sopenharmony_ci	snd_pcm_close(ext->plug.gen.slave);
423d5ac70f0Sopenharmony_ci	clear_ext_params(ext);
424d5ac70f0Sopenharmony_ci	if (ext->data->callback->close)
425d5ac70f0Sopenharmony_ci		ext->data->callback->close(ext->data);
426d5ac70f0Sopenharmony_ci	free(ext);
427d5ac70f0Sopenharmony_ci	return 0;
428d5ac70f0Sopenharmony_ci}
429d5ac70f0Sopenharmony_ci
430d5ac70f0Sopenharmony_cistatic snd_pcm_chmap_query_t **snd_pcm_extplug_query_chmaps(snd_pcm_t *pcm)
431d5ac70f0Sopenharmony_ci{
432d5ac70f0Sopenharmony_ci	extplug_priv_t *ext = pcm->private_data;
433d5ac70f0Sopenharmony_ci
434d5ac70f0Sopenharmony_ci	if (ext->data->version >= 0x010002 &&
435d5ac70f0Sopenharmony_ci	    ext->data->callback->query_chmaps)
436d5ac70f0Sopenharmony_ci		return ext->data->callback->query_chmaps(ext->data);
437d5ac70f0Sopenharmony_ci	return snd_pcm_generic_query_chmaps(pcm);
438d5ac70f0Sopenharmony_ci}
439d5ac70f0Sopenharmony_ci
440d5ac70f0Sopenharmony_cistatic snd_pcm_chmap_t *snd_pcm_extplug_get_chmap(snd_pcm_t *pcm)
441d5ac70f0Sopenharmony_ci{
442d5ac70f0Sopenharmony_ci	extplug_priv_t *ext = pcm->private_data;
443d5ac70f0Sopenharmony_ci
444d5ac70f0Sopenharmony_ci	if (ext->data->version >= 0x010002 &&
445d5ac70f0Sopenharmony_ci	    ext->data->callback->get_chmap)
446d5ac70f0Sopenharmony_ci		return ext->data->callback->get_chmap(ext->data);
447d5ac70f0Sopenharmony_ci	return snd_pcm_generic_get_chmap(pcm);
448d5ac70f0Sopenharmony_ci}
449d5ac70f0Sopenharmony_ci
450d5ac70f0Sopenharmony_cistatic int snd_pcm_extplug_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map)
451d5ac70f0Sopenharmony_ci{
452d5ac70f0Sopenharmony_ci	extplug_priv_t *ext = pcm->private_data;
453d5ac70f0Sopenharmony_ci
454d5ac70f0Sopenharmony_ci	if (ext->data->version >= 0x010002 &&
455d5ac70f0Sopenharmony_ci	    ext->data->callback->set_chmap)
456d5ac70f0Sopenharmony_ci		return ext->data->callback->set_chmap(ext->data, map);
457d5ac70f0Sopenharmony_ci	return snd_pcm_generic_set_chmap(pcm, map);
458d5ac70f0Sopenharmony_ci}
459d5ac70f0Sopenharmony_ci
460d5ac70f0Sopenharmony_cistatic const snd_pcm_ops_t snd_pcm_extplug_ops = {
461d5ac70f0Sopenharmony_ci	.close = snd_pcm_extplug_close,
462d5ac70f0Sopenharmony_ci	.info = snd_pcm_generic_info,
463d5ac70f0Sopenharmony_ci	.hw_refine = snd_pcm_extplug_hw_refine,
464d5ac70f0Sopenharmony_ci	.hw_params = snd_pcm_extplug_hw_params,
465d5ac70f0Sopenharmony_ci	.hw_free = snd_pcm_extplug_hw_free,
466d5ac70f0Sopenharmony_ci	.sw_params = snd_pcm_generic_sw_params,
467d5ac70f0Sopenharmony_ci	.channel_info = snd_pcm_generic_channel_info,
468d5ac70f0Sopenharmony_ci	.dump = snd_pcm_extplug_dump,
469d5ac70f0Sopenharmony_ci	.nonblock = snd_pcm_generic_nonblock,
470d5ac70f0Sopenharmony_ci	.async = snd_pcm_generic_async,
471d5ac70f0Sopenharmony_ci	.mmap = snd_pcm_generic_mmap,
472d5ac70f0Sopenharmony_ci	.munmap = snd_pcm_generic_munmap,
473d5ac70f0Sopenharmony_ci	.query_chmaps = snd_pcm_extplug_query_chmaps,
474d5ac70f0Sopenharmony_ci	.get_chmap = snd_pcm_extplug_get_chmap,
475d5ac70f0Sopenharmony_ci	.set_chmap = snd_pcm_extplug_set_chmap,
476d5ac70f0Sopenharmony_ci};
477d5ac70f0Sopenharmony_ci
478d5ac70f0Sopenharmony_ci#endif /* !DOC_HIDDEN */
479d5ac70f0Sopenharmony_ci
480d5ac70f0Sopenharmony_ci/*
481d5ac70f0Sopenharmony_ci * Exported functions
482d5ac70f0Sopenharmony_ci */
483d5ac70f0Sopenharmony_ci
484d5ac70f0Sopenharmony_ci/*! \page pcm_external_plugins PCM External Plugin SDK
485d5ac70f0Sopenharmony_ci
486d5ac70f0Sopenharmony_ci\section pcm_externals External Plugins
487d5ac70f0Sopenharmony_ci
488d5ac70f0Sopenharmony_ciThe external plugins are implemented in a shared object file located
489d5ac70f0Sopenharmony_ciat /usr/lib/alsa-lib (the exact location depends on the build option
490d5ac70f0Sopenharmony_ciand asoundrc configuration).  It has to be the file like
491d5ac70f0Sopenharmony_cilibasound_module_pcm_MYPLUGIN.so, where MYPLUGIN corresponds to your
492d5ac70f0Sopenharmony_ciown plugin name.
493d5ac70f0Sopenharmony_ci
494d5ac70f0Sopenharmony_ciThe entry point of the plugin is defined via
495d5ac70f0Sopenharmony_ci#SND_PCM_PLUGIN_DEFINE_FUNC() macro.  This macro defines the function
496d5ac70f0Sopenharmony_ciwith a proper name to be referred from alsa-lib.  The function takes
497d5ac70f0Sopenharmony_cithe following 6 arguments:
498d5ac70f0Sopenharmony_ci\code
499d5ac70f0Sopenharmony_ciint (snd_pcm_t **pcmp, const char *name, snd_config_t *root,
500d5ac70f0Sopenharmony_ci	snd_config_t *conf, snd_pcm_stream_t stream, int mode)
501d5ac70f0Sopenharmony_ci\endcode
502d5ac70f0Sopenharmony_ciThe first argument, pcmp, is the pointer to store the resultant PCM
503d5ac70f0Sopenharmony_cihandle.  The arguments name, root, stream and mode are the parameters
504d5ac70f0Sopenharmony_cito be passed to the plugin constructor.  The conf is the configuration
505d5ac70f0Sopenharmony_citree for the plugin.  The arguments above are defined in the macro
506d5ac70f0Sopenharmony_ciitself, so don't use variables with the same names to shadow
507d5ac70f0Sopenharmony_ciparameters.
508d5ac70f0Sopenharmony_ci
509d5ac70f0Sopenharmony_ciAfter parsing the configuration parameters in the given conf tree,
510d5ac70f0Sopenharmony_ciusually you will call the external plugin API function,
511d5ac70f0Sopenharmony_ci#snd_pcm_extplug_create() or #snd_pcm_ioplug_create(), depending
512d5ac70f0Sopenharmony_cion the plugin type.  The PCM handle must be filled *pcmp in return.
513d5ac70f0Sopenharmony_ciThen this function must return either a value 0 when succeeded, or a
514d5ac70f0Sopenharmony_cinegative value as the error code.
515d5ac70f0Sopenharmony_ci
516d5ac70f0Sopenharmony_ciFinally, add #SND_PCM_PLUGIN_SYMBOL() with the name of your
517d5ac70f0Sopenharmony_ciplugin as the argument at the end.  This defines the proper versioned
518d5ac70f0Sopenharmony_cisymbol as the reference.
519d5ac70f0Sopenharmony_ci
520d5ac70f0Sopenharmony_ciThe typical code would look like below:
521d5ac70f0Sopenharmony_ci\code
522d5ac70f0Sopenharmony_cistruct myplug_info {
523d5ac70f0Sopenharmony_ci	snd_pcm_extplug_t ext;
524d5ac70f0Sopenharmony_ci	int my_own_data;
525d5ac70f0Sopenharmony_ci	...
526d5ac70f0Sopenharmony_ci};
527d5ac70f0Sopenharmony_ci
528d5ac70f0Sopenharmony_ciSND_PCM_PLUGIN_DEFINE_FUNC(myplug)
529d5ac70f0Sopenharmony_ci{
530d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
531d5ac70f0Sopenharmony_ci	snd_config_t *slave = NULL;
532d5ac70f0Sopenharmony_ci	struct myplug_info *myplug;
533d5ac70f0Sopenharmony_ci	int err;
534d5ac70f0Sopenharmony_ci
535d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, conf) {
536d5ac70f0Sopenharmony_ci		snd_config_t *n = snd_config_iterator_entry(i);
537d5ac70f0Sopenharmony_ci		const char *id;
538d5ac70f0Sopenharmony_ci		if (snd_config_get_id(n, &id) < 0)
539d5ac70f0Sopenharmony_ci			continue;
540d5ac70f0Sopenharmony_ci		if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0)
541d5ac70f0Sopenharmony_ci			continue;
542d5ac70f0Sopenharmony_ci		if (strcmp(id, "slave") == 0) {
543d5ac70f0Sopenharmony_ci			slave = n;
544d5ac70f0Sopenharmony_ci			continue;
545d5ac70f0Sopenharmony_ci		}
546d5ac70f0Sopenharmony_ci		if (strcmp(id, "my_own_parameter") == 0) {
547d5ac70f0Sopenharmony_ci			....
548d5ac70f0Sopenharmony_ci			continue;
549d5ac70f0Sopenharmony_ci		}
550d5ac70f0Sopenharmony_ci		SNDERR("Unknown field %s", id);
551d5ac70f0Sopenharmony_ci		return -EINVAL;
552d5ac70f0Sopenharmony_ci	}
553d5ac70f0Sopenharmony_ci
554d5ac70f0Sopenharmony_ci	if (! slave) {
555d5ac70f0Sopenharmony_ci		SNDERR("No slave defined for myplug");
556d5ac70f0Sopenharmony_ci		return -EINVAL;
557d5ac70f0Sopenharmony_ci	}
558d5ac70f0Sopenharmony_ci
559d5ac70f0Sopenharmony_ci	myplug = calloc(1, sizeof(*myplug));
560d5ac70f0Sopenharmony_ci	if (myplug == NULL)
561d5ac70f0Sopenharmony_ci		return -ENOMEM;
562d5ac70f0Sopenharmony_ci
563d5ac70f0Sopenharmony_ci	myplug->ext.version = SND_PCM_EXTPLUG_VERSION;
564d5ac70f0Sopenharmony_ci	myplug->ext.name = "My Own Plugin";
565d5ac70f0Sopenharmony_ci	myplug->ext.callback = &my_own_callback;
566d5ac70f0Sopenharmony_ci	myplug->ext.private_data = myplug;
567d5ac70f0Sopenharmony_ci	....
568d5ac70f0Sopenharmony_ci
569d5ac70f0Sopenharmony_ci	err = snd_pcm_extplug_create(&myplug->ext, name, root, conf, stream, mode);
570d5ac70f0Sopenharmony_ci	if (err < 0) {
571d5ac70f0Sopenharmony_ci		myplug_free(myplug);
572d5ac70f0Sopenharmony_ci		return err;
573d5ac70f0Sopenharmony_ci	}
574d5ac70f0Sopenharmony_ci
575d5ac70f0Sopenharmony_ci	*pcmp = myplug->ext.pcm;
576d5ac70f0Sopenharmony_ci	return 0;
577d5ac70f0Sopenharmony_ci}
578d5ac70f0Sopenharmony_ci
579d5ac70f0Sopenharmony_ciSND_PCM_PLUGIN_SYMBOL(myplug);
580d5ac70f0Sopenharmony_ci\endcode
581d5ac70f0Sopenharmony_ci
582d5ac70f0Sopenharmony_ciRead the codes in alsa-plugins package for the real examples.
583d5ac70f0Sopenharmony_ci
584d5ac70f0Sopenharmony_ci
585d5ac70f0Sopenharmony_ci\section pcm_extplug External Plugin: Filter-Type Plugin
586d5ac70f0Sopenharmony_ci
587d5ac70f0Sopenharmony_ciThe filter-type plugin is a plugin to convert the PCM signals from the input
588d5ac70f0Sopenharmony_ciand feeds to the output.  Thus, this plugin always needs a slave PCM as its output.
589d5ac70f0Sopenharmony_ci
590d5ac70f0Sopenharmony_ciThe plugin can modify the format and the channels of the input/output PCM.
591d5ac70f0Sopenharmony_ciIt can <i>not</i> modify the sample rate (because of simplicity reason).
592d5ac70f0Sopenharmony_ci
593d5ac70f0Sopenharmony_ciThe following fields have to be filled in extplug record before calling
594d5ac70f0Sopenharmony_ci#snd_pcm_extplug_create() : version, name, callback.
595d5ac70f0Sopenharmony_ciOtherfields are optional and should be initialized with zero.
596d5ac70f0Sopenharmony_ci
597d5ac70f0Sopenharmony_ciThe constant #SND_PCM_EXTPLUG_VERSION must be passed to the version
598d5ac70f0Sopenharmony_cifield for the version check in alsa-lib.  A non-NULL ASCII string
599d5ac70f0Sopenharmony_cihas to be passed to the name field.  The callback field contains the
600d5ac70f0Sopenharmony_citable of callback functions for this plugin (defined as
601d5ac70f0Sopenharmony_ci#snd_pcm_extplug_callback_t).
602d5ac70f0Sopenharmony_ci
603d5ac70f0Sopenharmony_ciThe driver can set an arbitrary value (pointer) to private_data
604d5ac70f0Sopenharmony_cifield to refer its own data in the callbacks.
605d5ac70f0Sopenharmony_ci
606d5ac70f0Sopenharmony_ciThe rest fields are filled by #snd_pcm_extplug_create().  The pcm field
607d5ac70f0Sopenharmony_ciis the resultant PCM handle.  The others are the current status of the
608d5ac70f0Sopenharmony_ciPCM.
609d5ac70f0Sopenharmony_ci
610d5ac70f0Sopenharmony_ciThe callback functions in #snd_pcm_extplug_callback_t define the real
611d5ac70f0Sopenharmony_cibehavior of the driver.
612d5ac70f0Sopenharmony_ciAt least, transfer callback must be given.  This callback is called
613d5ac70f0Sopenharmony_ciat each time certain size of data block is transfered to the slave
614d5ac70f0Sopenharmony_ciPCM.  Other callbacks are optional.
615d5ac70f0Sopenharmony_ci
616d5ac70f0Sopenharmony_ciThe close callback is called when the PCM is closed.  If the plugin
617d5ac70f0Sopenharmony_ciallocates private resources, this is the place to release them
618d5ac70f0Sopenharmony_ciagain.  The hw_params and hw_free callbacks are called at
619d5ac70f0Sopenharmony_ci#snd_pcm_hw_params() and #snd_pcm_hw_free() API calls,
620d5ac70f0Sopenharmony_cirespectively.  The last, dump callback, is called for printing the
621d5ac70f0Sopenharmony_ciinformation of the given plugin.
622d5ac70f0Sopenharmony_ci
623d5ac70f0Sopenharmony_ciThe init callback is called when the PCM is at prepare state or any
624d5ac70f0Sopenharmony_ciinitialization is issued.  Use this callback to reset the PCM instance
625d5ac70f0Sopenharmony_cito a sane initial state.
626d5ac70f0Sopenharmony_ci
627d5ac70f0Sopenharmony_ciThe hw_params constraints can be defined via either
628d5ac70f0Sopenharmony_ci#snd_pcm_extplug_set_param_minmax() and #snd_pcm_extplug_set_param_list()
629d5ac70f0Sopenharmony_cifunctions after calling #snd_pcm_extplug_create().
630d5ac70f0Sopenharmony_ciThe former defines the minimal and maximal acceptable values for the
631d5ac70f0Sopenharmony_cigiven hw_params parameter (SND_PCM_EXTPLUG_HW_XXX).
632d5ac70f0Sopenharmony_ciThis function can't be used for the format parameter.  The latter
633d5ac70f0Sopenharmony_cifunction specifies the available parameter values as the list.
634d5ac70f0Sopenharmony_ciAs mentioned above, the rate can't be changed.  Only changeable
635d5ac70f0Sopenharmony_ciparameters are sample format and channels.
636d5ac70f0Sopenharmony_ci
637d5ac70f0Sopenharmony_ciTo define the constraints of the slave PCM configuration, use
638d5ac70f0Sopenharmony_cieither #snd_pcm_extplug_set_slave_param_minmax() and
639d5ac70f0Sopenharmony_ci#snd_pcm_extplug_set_slave_param_list().  The arguments are as same
640d5ac70f0Sopenharmony_cias former functions.
641d5ac70f0Sopenharmony_ci
642d5ac70f0Sopenharmony_ciTo clear the parameter constraints, call #snd_pcm_extplug_params_reset()
643d5ac70f0Sopenharmony_cifunction.
644d5ac70f0Sopenharmony_ci
645d5ac70f0Sopenharmony_ciWhen using snd_pcm_extplug_set_param_*() or snd_pcm_extplug_set_slave_param_*()
646d5ac70f0Sopenharmony_cifor any parameter. This parameter is no longer linked between the client and
647d5ac70f0Sopenharmony_cislave PCM. Therefore it could differ and the extplug has to support conversion
648d5ac70f0Sopenharmony_cibetween all valid parameter configurations. To keep the client and slave
649d5ac70f0Sopenharmony_ciparameter linked #snd_pcm_extplug_set_param_link() can be used for the
650d5ac70f0Sopenharmony_cicorresponding parameter. For example if the extplug does not support channel nor
651d5ac70f0Sopenharmony_ciformat conversion the supported client parameters can be limited with
652d5ac70f0Sopenharmony_cisnd_pcm_extplug_set_param_*() and afterwards
653d5ac70f0Sopenharmony_cisnd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_FORMAT, 1) and
654d5ac70f0Sopenharmony_cisnd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_CHANNELS, 1) should be
655d5ac70f0Sopenharmony_cicalled to keep the client and slave parameters the same.
656d5ac70f0Sopenharmony_ci*/
657d5ac70f0Sopenharmony_ci
658d5ac70f0Sopenharmony_ci/**
659d5ac70f0Sopenharmony_ci * \brief Create an extplug instance
660d5ac70f0Sopenharmony_ci * \param extplug the extplug handle
661d5ac70f0Sopenharmony_ci * \param name name of the PCM
662d5ac70f0Sopenharmony_ci * \param root configuration tree root
663d5ac70f0Sopenharmony_ci * \param slave_conf slave configuration root
664d5ac70f0Sopenharmony_ci * \param stream stream direction
665d5ac70f0Sopenharmony_ci * \param mode PCM open mode
666d5ac70f0Sopenharmony_ci * \return 0 if successful, or a negative error code
667d5ac70f0Sopenharmony_ci *
668d5ac70f0Sopenharmony_ci * Creates the extplug instance based on the given handle.
669d5ac70f0Sopenharmony_ci * The slave_conf argument is mandatory, and usually taken from the config tree of the
670d5ac70f0Sopenharmony_ci * PCM plugin as "slave" config value.
671d5ac70f0Sopenharmony_ci * name, root, stream and mode arguments are the values used for opening the PCM.
672d5ac70f0Sopenharmony_ci *
673d5ac70f0Sopenharmony_ci * The callback is the mandatory field of extplug handle.  At least, start, stop and
674d5ac70f0Sopenharmony_ci * pointer callbacks must be set before calling this function.
675d5ac70f0Sopenharmony_ci */
676d5ac70f0Sopenharmony_ciint snd_pcm_extplug_create(snd_pcm_extplug_t *extplug, const char *name,
677d5ac70f0Sopenharmony_ci			   snd_config_t *root, snd_config_t *slave_conf,
678d5ac70f0Sopenharmony_ci			   snd_pcm_stream_t stream, int mode)
679d5ac70f0Sopenharmony_ci{
680d5ac70f0Sopenharmony_ci	extplug_priv_t *ext;
681d5ac70f0Sopenharmony_ci	int err;
682d5ac70f0Sopenharmony_ci	snd_pcm_t *spcm, *pcm;
683d5ac70f0Sopenharmony_ci	snd_config_t *sconf;
684d5ac70f0Sopenharmony_ci
685d5ac70f0Sopenharmony_ci	assert(root);
686d5ac70f0Sopenharmony_ci	assert(extplug && extplug->callback);
687d5ac70f0Sopenharmony_ci	assert(extplug->callback->transfer);
688d5ac70f0Sopenharmony_ci	assert(slave_conf);
689d5ac70f0Sopenharmony_ci
690d5ac70f0Sopenharmony_ci	/* We support 1.0.0 to current */
691d5ac70f0Sopenharmony_ci	if (extplug->version < 0x010000 ||
692d5ac70f0Sopenharmony_ci	    extplug->version > SND_PCM_EXTPLUG_VERSION) {
693d5ac70f0Sopenharmony_ci		SNDERR("extplug: Plugin version mismatch: 0x%x",
694d5ac70f0Sopenharmony_ci		       extplug->version);
695d5ac70f0Sopenharmony_ci		return -ENXIO;
696d5ac70f0Sopenharmony_ci	}
697d5ac70f0Sopenharmony_ci
698d5ac70f0Sopenharmony_ci	err = snd_pcm_slave_conf(root, slave_conf, &sconf, 0);
699d5ac70f0Sopenharmony_ci	if (err < 0)
700d5ac70f0Sopenharmony_ci		return err;
701d5ac70f0Sopenharmony_ci	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, NULL);
702d5ac70f0Sopenharmony_ci	snd_config_delete(sconf);
703d5ac70f0Sopenharmony_ci	if (err < 0)
704d5ac70f0Sopenharmony_ci		return err;
705d5ac70f0Sopenharmony_ci
706d5ac70f0Sopenharmony_ci	ext = calloc(1, sizeof(*ext));
707d5ac70f0Sopenharmony_ci	if (! ext)
708d5ac70f0Sopenharmony_ci		return -ENOMEM;
709d5ac70f0Sopenharmony_ci
710d5ac70f0Sopenharmony_ci	ext->data = extplug;
711d5ac70f0Sopenharmony_ci	extplug->stream = stream;
712d5ac70f0Sopenharmony_ci
713d5ac70f0Sopenharmony_ci	snd_pcm_plugin_init(&ext->plug);
714d5ac70f0Sopenharmony_ci	ext->plug.read = snd_pcm_extplug_read_areas;
715d5ac70f0Sopenharmony_ci	ext->plug.write = snd_pcm_extplug_write_areas;
716d5ac70f0Sopenharmony_ci	ext->plug.undo_read = snd_pcm_plugin_undo_read_generic;
717d5ac70f0Sopenharmony_ci	ext->plug.undo_write = snd_pcm_plugin_undo_write_generic;
718d5ac70f0Sopenharmony_ci	ext->plug.gen.slave = spcm;
719d5ac70f0Sopenharmony_ci	ext->plug.gen.close_slave = 1;
720d5ac70f0Sopenharmony_ci	if (extplug->version >= 0x010001 && extplug->callback->init)
721d5ac70f0Sopenharmony_ci		ext->plug.init = snd_pcm_extplug_init;
722d5ac70f0Sopenharmony_ci
723d5ac70f0Sopenharmony_ci	err = snd_pcm_new(&pcm, SND_PCM_TYPE_EXTPLUG, name, stream, mode);
724d5ac70f0Sopenharmony_ci	if (err < 0) {
725d5ac70f0Sopenharmony_ci		free(ext);
726d5ac70f0Sopenharmony_ci		return err;
727d5ac70f0Sopenharmony_ci	}
728d5ac70f0Sopenharmony_ci
729d5ac70f0Sopenharmony_ci	extplug->pcm = pcm;
730d5ac70f0Sopenharmony_ci	pcm->ops = &snd_pcm_extplug_ops;
731d5ac70f0Sopenharmony_ci	pcm->fast_ops = &snd_pcm_plugin_fast_ops;
732d5ac70f0Sopenharmony_ci	pcm->private_data = ext;
733d5ac70f0Sopenharmony_ci	pcm->poll_fd = spcm->poll_fd;
734d5ac70f0Sopenharmony_ci	pcm->poll_events = spcm->poll_events;
735d5ac70f0Sopenharmony_ci	pcm->tstamp_type = spcm->tstamp_type;
736d5ac70f0Sopenharmony_ci	snd_pcm_set_hw_ptr(pcm, &ext->plug.hw_ptr, -1, 0);
737d5ac70f0Sopenharmony_ci	snd_pcm_set_appl_ptr(pcm, &ext->plug.appl_ptr, -1, 0);
738d5ac70f0Sopenharmony_ci
739d5ac70f0Sopenharmony_ci	return 0;
740d5ac70f0Sopenharmony_ci}
741d5ac70f0Sopenharmony_ci
742d5ac70f0Sopenharmony_ci/**
743d5ac70f0Sopenharmony_ci * \brief Delete the extplug instance
744d5ac70f0Sopenharmony_ci * \param extplug the extplug handle to delete
745d5ac70f0Sopenharmony_ci * \return 0 if successful, or a negative error code
746d5ac70f0Sopenharmony_ci *
747d5ac70f0Sopenharmony_ci * The destructor of extplug instance.
748d5ac70f0Sopenharmony_ci * Closes the PCM and deletes the associated resources.
749d5ac70f0Sopenharmony_ci */
750d5ac70f0Sopenharmony_ciint snd_pcm_extplug_delete(snd_pcm_extplug_t *extplug)
751d5ac70f0Sopenharmony_ci{
752d5ac70f0Sopenharmony_ci	return snd_pcm_close(extplug->pcm);
753d5ac70f0Sopenharmony_ci}
754d5ac70f0Sopenharmony_ci
755d5ac70f0Sopenharmony_ci
756d5ac70f0Sopenharmony_ci/**
757d5ac70f0Sopenharmony_ci * \brief Reset extplug parameters
758d5ac70f0Sopenharmony_ci * \param extplug the extplug handle
759d5ac70f0Sopenharmony_ci *
760d5ac70f0Sopenharmony_ci * Resets the all parameters for the given extplug handle.
761d5ac70f0Sopenharmony_ci */
762d5ac70f0Sopenharmony_civoid snd_pcm_extplug_params_reset(snd_pcm_extplug_t *extplug)
763d5ac70f0Sopenharmony_ci{
764d5ac70f0Sopenharmony_ci	extplug_priv_t *ext = extplug->pcm->private_data;
765d5ac70f0Sopenharmony_ci	clear_ext_params(ext);
766d5ac70f0Sopenharmony_ci}
767d5ac70f0Sopenharmony_ci
768d5ac70f0Sopenharmony_ci/**
769d5ac70f0Sopenharmony_ci * \brief Set slave parameter as the list
770d5ac70f0Sopenharmony_ci * \param extplug the extplug handle
771d5ac70f0Sopenharmony_ci * \param type parameter type
772d5ac70f0Sopenharmony_ci * \param num_list number of available values
773d5ac70f0Sopenharmony_ci * \param list the list of available values
774d5ac70f0Sopenharmony_ci * \return 0 if successful, or a negative error code
775d5ac70f0Sopenharmony_ci *
776d5ac70f0Sopenharmony_ci * Sets the slave parameter as the list.
777d5ac70f0Sopenharmony_ci * The available values of the given parameter type of the slave PCM is restricted
778d5ac70f0Sopenharmony_ci * to the ones of the given list.
779d5ac70f0Sopenharmony_ci */
780d5ac70f0Sopenharmony_ciint snd_pcm_extplug_set_slave_param_list(snd_pcm_extplug_t *extplug, int type, unsigned int num_list, const unsigned int *list)
781d5ac70f0Sopenharmony_ci{
782d5ac70f0Sopenharmony_ci	extplug_priv_t *ext = extplug->pcm->private_data;
783d5ac70f0Sopenharmony_ci	if (type < 0 || type >= SND_PCM_EXTPLUG_HW_PARAMS) {
784d5ac70f0Sopenharmony_ci		SNDERR("EXTPLUG: invalid parameter type %d", type);
785d5ac70f0Sopenharmony_ci		return -EINVAL;
786d5ac70f0Sopenharmony_ci	}
787d5ac70f0Sopenharmony_ci	return snd_ext_parm_set_list(&ext->sparams[type], num_list, list);
788d5ac70f0Sopenharmony_ci}
789d5ac70f0Sopenharmony_ci
790d5ac70f0Sopenharmony_ci/**
791d5ac70f0Sopenharmony_ci * \brief Set slave parameter as the min/max values
792d5ac70f0Sopenharmony_ci * \param extplug the extplug handle
793d5ac70f0Sopenharmony_ci * \param type parameter type
794d5ac70f0Sopenharmony_ci * \param min the minimum value
795d5ac70f0Sopenharmony_ci * \param max the maximum value
796d5ac70f0Sopenharmony_ci * \return 0 if successful, or a negative error code
797d5ac70f0Sopenharmony_ci *
798d5ac70f0Sopenharmony_ci * Sets the slave parameter as the min/max values.
799d5ac70f0Sopenharmony_ci * The available values of the given parameter type of the slave PCM is restricted
800d5ac70f0Sopenharmony_ci * between the given minimum and maximum values.
801d5ac70f0Sopenharmony_ci */
802d5ac70f0Sopenharmony_ciint snd_pcm_extplug_set_slave_param_minmax(snd_pcm_extplug_t *extplug, int type, unsigned int min, unsigned int max)
803d5ac70f0Sopenharmony_ci{
804d5ac70f0Sopenharmony_ci	extplug_priv_t *ext = extplug->pcm->private_data;
805d5ac70f0Sopenharmony_ci	if (type < 0 || type >= SND_PCM_EXTPLUG_HW_PARAMS) {
806d5ac70f0Sopenharmony_ci		SNDERR("EXTPLUG: invalid parameter type %d", type);
807d5ac70f0Sopenharmony_ci		return -EINVAL;
808d5ac70f0Sopenharmony_ci	}
809d5ac70f0Sopenharmony_ci	if (is_mask_type(type)) {
810d5ac70f0Sopenharmony_ci		SNDERR("EXTPLUG: invalid parameter type %d", type);
811d5ac70f0Sopenharmony_ci		return -EINVAL;
812d5ac70f0Sopenharmony_ci	}
813d5ac70f0Sopenharmony_ci	return snd_ext_parm_set_minmax(&ext->sparams[type], min, max);
814d5ac70f0Sopenharmony_ci}
815d5ac70f0Sopenharmony_ci
816d5ac70f0Sopenharmony_ci/**
817d5ac70f0Sopenharmony_ci * \brief Set master parameter as the list
818d5ac70f0Sopenharmony_ci * \param extplug the extplug handle
819d5ac70f0Sopenharmony_ci * \param type parameter type
820d5ac70f0Sopenharmony_ci * \param num_list number of available values
821d5ac70f0Sopenharmony_ci * \param list the list of available values
822d5ac70f0Sopenharmony_ci * \return 0 if successful, or a negative error code
823d5ac70f0Sopenharmony_ci *
824d5ac70f0Sopenharmony_ci * Sets the master parameter as the list.
825d5ac70f0Sopenharmony_ci * The available values of the given parameter type of this PCM (as input) is restricted
826d5ac70f0Sopenharmony_ci * to the ones of the given list.
827d5ac70f0Sopenharmony_ci */
828d5ac70f0Sopenharmony_ciint snd_pcm_extplug_set_param_list(snd_pcm_extplug_t *extplug, int type, unsigned int num_list, const unsigned int *list)
829d5ac70f0Sopenharmony_ci{
830d5ac70f0Sopenharmony_ci	extplug_priv_t *ext = extplug->pcm->private_data;
831d5ac70f0Sopenharmony_ci	if (type < 0 || type >= SND_PCM_EXTPLUG_HW_PARAMS) {
832d5ac70f0Sopenharmony_ci		SNDERR("EXTPLUG: invalid parameter type %d", type);
833d5ac70f0Sopenharmony_ci		return -EINVAL;
834d5ac70f0Sopenharmony_ci	}
835d5ac70f0Sopenharmony_ci	return snd_ext_parm_set_list(&ext->params[type], num_list, list);
836d5ac70f0Sopenharmony_ci}
837d5ac70f0Sopenharmony_ci
838d5ac70f0Sopenharmony_ci/**
839d5ac70f0Sopenharmony_ci * \brief Set master parameter as the min/max values
840d5ac70f0Sopenharmony_ci * \param extplug the extplug handle
841d5ac70f0Sopenharmony_ci * \param type parameter type
842d5ac70f0Sopenharmony_ci * \param min the minimum value
843d5ac70f0Sopenharmony_ci * \param max the maximum value
844d5ac70f0Sopenharmony_ci * \return 0 if successful, or a negative error code
845d5ac70f0Sopenharmony_ci *
846d5ac70f0Sopenharmony_ci * Sets the master parameter as the min/max values.
847d5ac70f0Sopenharmony_ci * The available values of the given parameter type of this PCM (as input) is restricted
848d5ac70f0Sopenharmony_ci * between the given minimum and maximum values.
849d5ac70f0Sopenharmony_ci */
850d5ac70f0Sopenharmony_ciint snd_pcm_extplug_set_param_minmax(snd_pcm_extplug_t *extplug, int type, unsigned int min, unsigned int max)
851d5ac70f0Sopenharmony_ci{
852d5ac70f0Sopenharmony_ci	extplug_priv_t *ext = extplug->pcm->private_data;
853d5ac70f0Sopenharmony_ci	if (type < 0 || type >= SND_PCM_EXTPLUG_HW_PARAMS) {
854d5ac70f0Sopenharmony_ci		SNDERR("EXTPLUG: invalid parameter type %d", type);
855d5ac70f0Sopenharmony_ci		return -EINVAL;
856d5ac70f0Sopenharmony_ci	}
857d5ac70f0Sopenharmony_ci	if (is_mask_type(type)) {
858d5ac70f0Sopenharmony_ci		SNDERR("EXTPLUG: invalid parameter type %d", type);
859d5ac70f0Sopenharmony_ci		return -EINVAL;
860d5ac70f0Sopenharmony_ci	}
861d5ac70f0Sopenharmony_ci	return snd_ext_parm_set_minmax(&ext->params[type], min, max);
862d5ac70f0Sopenharmony_ci}
863d5ac70f0Sopenharmony_ci
864d5ac70f0Sopenharmony_ci/**
865d5ac70f0Sopenharmony_ci * @brief Keep the client and slave format/channels the same if requested. This
866d5ac70f0Sopenharmony_ci * is for example useful if this extplug does not support any channel
867d5ac70f0Sopenharmony_ci * conversion.
868d5ac70f0Sopenharmony_ci * @param extplug the extplug handle
869d5ac70f0Sopenharmony_ci * @param type parameter type
870d5ac70f0Sopenharmony_ci * @param keep_link if 1 the parameter identified by type will be kept the same
871d5ac70f0Sopenharmony_ci * for the client and slave PCM of this extplug
872d5ac70f0Sopenharmony_ci * @return 0 if successful, or a negative error code
873d5ac70f0Sopenharmony_ci */
874d5ac70f0Sopenharmony_ciint snd_pcm_extplug_set_param_link(snd_pcm_extplug_t *extplug, int type,
875d5ac70f0Sopenharmony_ci				   int keep_link)
876d5ac70f0Sopenharmony_ci{
877d5ac70f0Sopenharmony_ci	extplug_priv_t *ext = extplug->pcm->private_data;
878d5ac70f0Sopenharmony_ci
879d5ac70f0Sopenharmony_ci	if (type < 0 || type >= SND_PCM_EXTPLUG_HW_PARAMS) {
880d5ac70f0Sopenharmony_ci		SNDERR("EXTPLUG: invalid parameter type %d", type);
881d5ac70f0Sopenharmony_ci		return -EINVAL;
882d5ac70f0Sopenharmony_ci	}
883d5ac70f0Sopenharmony_ci	ext->params[type].keep_link = keep_link ? 1 : 0;
884d5ac70f0Sopenharmony_ci	ext->sparams[type].keep_link = keep_link ? 1 : 0;
885d5ac70f0Sopenharmony_ci	return 0;
886d5ac70f0Sopenharmony_ci}
887