• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * \file control/control_ext.c
3  * \ingroup CtlPlugin_SDK
4  * \brief External Control Plugin SDK
5  * \author Takashi Iwai <tiwai@suse.de>
6  * \date 2005
7  */
8 /*
9  *  Control Interface - External Control Plugin SDK
10  *
11  *  Copyright (c) 2005 Takashi Iwai <tiwai@suse.de>
12  *
13  *
14  *   This library is free software; you can redistribute it and/or modify
15  *   it under the terms of the GNU Lesser General Public License as
16  *   published by the Free Software Foundation; either version 2.1 of
17  *   the License, or (at your option) any later version.
18  *
19  *   This program is distributed in the hope that it will be useful,
20  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
21  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  *   GNU Lesser General Public License for more details.
23  *
24  *   You should have received a copy of the GNU Lesser General Public
25  *   License along with this library; if not, write to the Free Software
26  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
27  *
28  */
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include "control_local.h"
35 #include "control_external.h"
36 
37 #ifndef PIC
38 /* entry for static linking */
39 const char *_snd_module_control_ext = "";
40 #endif
41 
snd_ctl_ext_close(snd_ctl_t * handle)42 static int snd_ctl_ext_close(snd_ctl_t *handle)
43 {
44 	snd_ctl_ext_t *ext = handle->private_data;
45 
46 	if (ext->callback->close)
47 		ext->callback->close(ext);
48 	return 0;
49 }
50 
snd_ctl_ext_nonblock(snd_ctl_t * handle,int nonblock)51 static int snd_ctl_ext_nonblock(snd_ctl_t *handle, int nonblock)
52 {
53 	snd_ctl_ext_t *ext = handle->private_data;
54 
55 	ext->nonblock = nonblock;
56 	return 0;
57 }
58 
snd_ctl_ext_async(snd_ctl_t * ctl ATTRIBUTE_UNUSED,int sig ATTRIBUTE_UNUSED,pid_t pid ATTRIBUTE_UNUSED)59 static int snd_ctl_ext_async(snd_ctl_t *ctl ATTRIBUTE_UNUSED,
60 			     int sig ATTRIBUTE_UNUSED,
61 			     pid_t pid ATTRIBUTE_UNUSED)
62 {
63 	return -ENOSYS;
64 }
65 
snd_ctl_ext_subscribe_events(snd_ctl_t * handle,int subscribe)66 static int snd_ctl_ext_subscribe_events(snd_ctl_t *handle, int subscribe)
67 {
68 	snd_ctl_ext_t *ext = handle->private_data;
69 
70 	if (subscribe < 0)
71 		return ext->subscribed;
72 	ext->subscribed = !!subscribe;
73 	if (ext->callback->subscribe_events)
74 		ext->callback->subscribe_events(ext, subscribe);
75 	return 0;
76 }
77 
snd_ctl_ext_card_info(snd_ctl_t * handle,snd_ctl_card_info_t * info)78 static int snd_ctl_ext_card_info(snd_ctl_t *handle, snd_ctl_card_info_t *info)
79 {
80 	snd_ctl_ext_t *ext = handle->private_data;
81 
82 	memset(info, 0, sizeof(*info));
83 	info->card = ext->card_idx;
84 	memcpy(info->id, ext->id, sizeof(info->id));
85 	memcpy(info->driver, ext->driver, sizeof(info->driver));
86 	memcpy(info->name, ext->name, sizeof(info->name));
87 	memcpy(info->longname, ext->longname, sizeof(info->longname));
88 	memcpy(info->mixername, ext->mixername, sizeof(info->mixername));
89 	return 0;
90 }
91 
snd_ctl_ext_elem_list(snd_ctl_t * handle,snd_ctl_elem_list_t * list)92 static int snd_ctl_ext_elem_list(snd_ctl_t *handle, snd_ctl_elem_list_t *list)
93 {
94 	snd_ctl_ext_t *ext = handle->private_data;
95 	int ret;
96 	unsigned int i, offset;
97 	snd_ctl_elem_id_t *ids;
98 
99 	list->count = ext->callback->elem_count(ext);
100 	list->used = 0;
101 	ids = list->pids;
102 	offset = list->offset;
103 	for (i = 0; i < list->space; i++) {
104 		if (offset >= list->count)
105 			break;
106 		snd_ctl_elem_id_clear(ids);
107 		ret = ext->callback->elem_list(ext, offset, ids);
108 		if (ret < 0)
109 			return ret;
110 		ids->numid = offset + 1; /* fake number */
111 		list->used++;
112 		offset++;
113 		ids++;
114 	}
115 	return 0;
116 }
117 
get_elem(snd_ctl_ext_t * ext,snd_ctl_elem_id_t * id)118 static snd_ctl_ext_key_t get_elem(snd_ctl_ext_t *ext, snd_ctl_elem_id_t *id)
119 {
120 	int numid = id->numid;
121 	if (numid > 0) {
122 		ext->callback->elem_list(ext, numid - 1, id);
123 		id->numid = numid;
124 	} else
125 		id->numid = 0;
126 	return ext->callback->find_elem(ext, id);
127 }
128 
snd_ctl_ext_elem_info(snd_ctl_t * handle,snd_ctl_elem_info_t * info)129 static int snd_ctl_ext_elem_info(snd_ctl_t *handle, snd_ctl_elem_info_t *info)
130 {
131 	snd_ctl_ext_t *ext = handle->private_data;
132 	snd_ctl_ext_key_t key;
133 	int type, ret;
134 
135 	key = get_elem(ext, &info->id);
136 	if (key == SND_CTL_EXT_KEY_NOT_FOUND)
137 		return -ENOENT;
138 	ret = ext->callback->get_attribute(ext, key, &type, &info->access, &info->count);
139 	if (ret < 0)
140 		goto err;
141 	info->type = type;
142 	ret = -EINVAL;
143 	switch (info->type) {
144 	case SND_CTL_ELEM_TYPE_BOOLEAN:
145 		info->value.integer.min = 0;
146 		info->value.integer.max = 1;
147 		ret = 0;
148 		break;
149 	case SND_CTL_ELEM_TYPE_INTEGER:
150 		if (! ext->callback->get_integer_info)
151 			goto err;
152 		ret = ext->callback->get_integer_info(ext, key, &info->value.integer.min,
153 						      &info->value.integer.max,
154 						      &info->value.integer.step);
155 		break;
156 	case SND_CTL_ELEM_TYPE_INTEGER64:
157 		if (! ext->callback->get_integer64_info)
158 			goto err;
159 		{
160 			int64_t xmin, xmax, xstep;
161 			ret = ext->callback->get_integer64_info(ext, key,
162 								&xmin,
163 								&xmax,
164 								&xstep);
165 			info->value.integer64.min = xmin;
166 			info->value.integer64.max = xmax;
167 			info->value.integer64.step = xstep;
168 		}
169 		break;
170 	case SND_CTL_ELEM_TYPE_ENUMERATED:
171 		if (! ext->callback->get_enumerated_info)
172 			goto err;
173 		ret = ext->callback->get_enumerated_info(ext, key, &info->value.enumerated.items);
174 		ext->callback->get_enumerated_name(ext, key, info->value.enumerated.item,
175 						   info->value.enumerated.name,
176 						   sizeof(info->value.enumerated.name));
177 		break;
178 	default:
179 		ret = 0;
180 		break;
181 	}
182 
183  err:
184 	if (ext->callback->free_key)
185 		ext->callback->free_key(ext, key);
186 
187 	return ret;
188 }
189 
snd_ctl_ext_elem_add(snd_ctl_t * handle ATTRIBUTE_UNUSED,snd_ctl_elem_info_t * info ATTRIBUTE_UNUSED)190 static int snd_ctl_ext_elem_add(snd_ctl_t *handle ATTRIBUTE_UNUSED,
191 				snd_ctl_elem_info_t *info ATTRIBUTE_UNUSED)
192 {
193 	return -ENXIO;
194 }
195 
snd_ctl_ext_elem_replace(snd_ctl_t * handle ATTRIBUTE_UNUSED,snd_ctl_elem_info_t * info ATTRIBUTE_UNUSED)196 static int snd_ctl_ext_elem_replace(snd_ctl_t *handle ATTRIBUTE_UNUSED,
197 				    snd_ctl_elem_info_t *info ATTRIBUTE_UNUSED)
198 {
199 	return -ENXIO;
200 }
201 
snd_ctl_ext_elem_remove(snd_ctl_t * handle ATTRIBUTE_UNUSED,snd_ctl_elem_id_t * id ATTRIBUTE_UNUSED)202 static int snd_ctl_ext_elem_remove(snd_ctl_t *handle ATTRIBUTE_UNUSED,
203 				   snd_ctl_elem_id_t *id ATTRIBUTE_UNUSED)
204 {
205 	return -ENXIO;
206 }
207 
snd_ctl_ext_elem_read(snd_ctl_t * handle,snd_ctl_elem_value_t * control)208 static int snd_ctl_ext_elem_read(snd_ctl_t *handle, snd_ctl_elem_value_t *control)
209 {
210 	snd_ctl_ext_t *ext = handle->private_data;
211 	snd_ctl_ext_key_t key;
212 	int type, ret;
213 	unsigned int access, count;
214 
215 	key = get_elem(ext, &control->id);
216 	if (key == SND_CTL_EXT_KEY_NOT_FOUND)
217 		return -ENOENT;
218 	ret = ext->callback->get_attribute(ext, key, &type, &access, &count);
219 	if (ret < 0)
220 		goto err;
221 	ret = -EINVAL;
222 	switch (type) {
223 	case SND_CTL_ELEM_TYPE_BOOLEAN:
224 	case SND_CTL_ELEM_TYPE_INTEGER:
225 		if (! ext->callback->read_integer)
226 			goto err;
227 		ret = ext->callback->read_integer(ext, key, control->value.integer.value);
228 		break;
229 	case SND_CTL_ELEM_TYPE_INTEGER64:
230 		if (! ext->callback->read_integer64)
231 			goto err;
232 		ret = ext->callback->read_integer64(ext, key,
233 						    (int64_t*)control->value.integer64.value);
234 		break;
235 	case SND_CTL_ELEM_TYPE_ENUMERATED:
236 		if (! ext->callback->read_enumerated)
237 			goto err;
238 		ret = ext->callback->read_enumerated(ext, key, control->value.enumerated.item);
239 		break;
240 	case SND_CTL_ELEM_TYPE_BYTES:
241 		if (! ext->callback->read_bytes)
242 			goto err;
243 		ret = ext->callback->read_bytes(ext, key, control->value.bytes.data,
244 						sizeof(control->value.bytes.data));
245 		break;
246 	case SND_CTL_ELEM_TYPE_IEC958:
247 		if (! ext->callback->read_iec958)
248 			goto err;
249 		ret = ext->callback->read_iec958(ext, key, (snd_aes_iec958_t *)&control->value.iec958);
250 		break;
251 	default:
252 		break;
253 	}
254 
255  err:
256 	if (ext->callback->free_key)
257 		ext->callback->free_key(ext, key);
258 
259 	return ret;
260 }
261 
snd_ctl_ext_elem_write(snd_ctl_t * handle,snd_ctl_elem_value_t * control)262 static int snd_ctl_ext_elem_write(snd_ctl_t *handle, snd_ctl_elem_value_t *control)
263 {
264 	snd_ctl_ext_t *ext = handle->private_data;
265 	snd_ctl_ext_key_t key;
266 	int type, ret;
267 	unsigned int access, count;
268 
269 	key = get_elem(ext, &control->id);
270 	if (key == SND_CTL_EXT_KEY_NOT_FOUND)
271 		return -ENOENT;
272 	ret = ext->callback->get_attribute(ext, key, &type, &access, &count);
273 	if (ret < 0)
274 		goto err;
275 	ret = -EINVAL;
276 	switch (type) {
277 	case SND_CTL_ELEM_TYPE_BOOLEAN:
278 	case SND_CTL_ELEM_TYPE_INTEGER:
279 		if (! ext->callback->write_integer)
280 			goto err;
281 		ret = ext->callback->write_integer(ext, key, control->value.integer.value);
282 		break;
283 	case SND_CTL_ELEM_TYPE_INTEGER64:
284 		if (! ext->callback->write_integer64)
285 			goto err;
286 		ret = ext->callback->write_integer64(ext, key, (int64_t *)control->value.integer64.value);
287 		break;
288 	case SND_CTL_ELEM_TYPE_ENUMERATED:
289 		if (! ext->callback->write_enumerated)
290 			goto err;
291 		ret = ext->callback->write_enumerated(ext, key, control->value.enumerated.item);
292 		break;
293 	case SND_CTL_ELEM_TYPE_BYTES:
294 		if (! ext->callback->write_bytes)
295 			goto err;
296 		ret = ext->callback->write_bytes(ext, key, control->value.bytes.data,
297 						sizeof(control->value.bytes.data));
298 		break;
299 	case SND_CTL_ELEM_TYPE_IEC958:
300 		if (! ext->callback->write_iec958)
301 			goto err;
302 		ret = ext->callback->write_iec958(ext, key, (snd_aes_iec958_t *)&control->value.iec958);
303 		break;
304 	default:
305 		break;
306 	}
307 
308  err:
309 	if (ext->callback->free_key)
310 		ext->callback->free_key(ext, key);
311 
312 	return ret;
313 }
314 
snd_ctl_ext_elem_lock(snd_ctl_t * handle ATTRIBUTE_UNUSED,snd_ctl_elem_id_t * id ATTRIBUTE_UNUSED)315 static int snd_ctl_ext_elem_lock(snd_ctl_t *handle ATTRIBUTE_UNUSED,
316 				 snd_ctl_elem_id_t *id ATTRIBUTE_UNUSED)
317 {
318 	return -ENXIO;
319 }
320 
snd_ctl_ext_elem_unlock(snd_ctl_t * handle ATTRIBUTE_UNUSED,snd_ctl_elem_id_t * id ATTRIBUTE_UNUSED)321 static int snd_ctl_ext_elem_unlock(snd_ctl_t *handle ATTRIBUTE_UNUSED,
322 				   snd_ctl_elem_id_t *id ATTRIBUTE_UNUSED)
323 {
324 	return -ENXIO;
325 }
326 
snd_ctl_ext_elem_tlv(snd_ctl_t * handle,int op_flag,unsigned int numid,unsigned int * tlv,unsigned int tlv_size)327 static int snd_ctl_ext_elem_tlv(snd_ctl_t *handle, int op_flag,
328 				unsigned int numid,
329 				unsigned int *tlv, unsigned int tlv_size)
330 {
331 	snd_ctl_ext_t *ext = handle->private_data;
332 	snd_ctl_ext_key_t key;
333 	int type, ret;
334 	unsigned int access, count, len;
335 	snd_ctl_elem_id_t id;
336 
337 	/* we don't support TLV on protocol ver 1.0.0 or earlier */
338 	if (ext->version <= SNDRV_PROTOCOL_VERSION(1, 0, 0))
339 		return -ENXIO;
340 
341 	snd_ctl_elem_id_clear(&id);
342 	if (numid > 0) {
343 		ext->callback->elem_list(ext, numid - 1, &id);
344 		id.numid = numid;
345 	} else
346 		id.numid = 0;
347 	key = ext->callback->find_elem(ext, &id);
348 
349 	if (key == SND_CTL_EXT_KEY_NOT_FOUND)
350 		return -ENOENT;
351 	ret = ext->callback->get_attribute(ext, key, &type, &access, &count);
352 	if (ret < 0)
353 		return ret;
354 
355 	if ((op_flag == 0 && (access & SND_CTL_EXT_ACCESS_TLV_READ) == 0) ||
356 	    (op_flag > 0 && (access & SND_CTL_EXT_ACCESS_TLV_WRITE) == 0) ||
357 	    (op_flag < 0 && (access & SND_CTL_EXT_ACCESS_TLV_COMMAND) == 0))
358 		return -ENXIO;
359 	if (access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
360 		return ext->tlv.c(ext, key, op_flag, numid, tlv, tlv_size);
361 	} else {
362 		if (op_flag)
363 			return -ENXIO;
364 		len = ext->tlv.p[1] + 2 * sizeof(unsigned int);
365 		if (tlv_size < len)
366 			return -ENOMEM;
367 		memcpy(tlv, ext->tlv.p, len);
368 		return 0;
369 	}
370 }
371 
snd_ctl_ext_next_device(snd_ctl_t * handle ATTRIBUTE_UNUSED,int * device ATTRIBUTE_UNUSED)372 static int snd_ctl_ext_next_device(snd_ctl_t *handle ATTRIBUTE_UNUSED,
373 				   int *device ATTRIBUTE_UNUSED)
374 {
375 	return -ENXIO;
376 }
377 
snd_ctl_ext_prefer_subdevice(snd_ctl_t * handle ATTRIBUTE_UNUSED,int subdev ATTRIBUTE_UNUSED)378 static int snd_ctl_ext_prefer_subdevice(snd_ctl_t *handle ATTRIBUTE_UNUSED,
379 					int subdev ATTRIBUTE_UNUSED)
380 {
381 	return -ENXIO;
382 }
383 
snd_ctl_ext_hwdep_info(snd_ctl_t * handle ATTRIBUTE_UNUSED,snd_hwdep_info_t * info ATTRIBUTE_UNUSED)384 static int snd_ctl_ext_hwdep_info(snd_ctl_t *handle ATTRIBUTE_UNUSED,
385 				  snd_hwdep_info_t *info ATTRIBUTE_UNUSED)
386 {
387 	return -ENXIO;
388 }
389 
snd_ctl_ext_pcm_info(snd_ctl_t * handle ATTRIBUTE_UNUSED,snd_pcm_info_t * info ATTRIBUTE_UNUSED)390 static int snd_ctl_ext_pcm_info(snd_ctl_t *handle ATTRIBUTE_UNUSED,
391 				snd_pcm_info_t *info ATTRIBUTE_UNUSED)
392 {
393 	return -ENXIO;
394 }
395 
snd_ctl_ext_rawmidi_info(snd_ctl_t * handle ATTRIBUTE_UNUSED,snd_rawmidi_info_t * info ATTRIBUTE_UNUSED)396 static int snd_ctl_ext_rawmidi_info(snd_ctl_t *handle ATTRIBUTE_UNUSED,
397 				    snd_rawmidi_info_t *info ATTRIBUTE_UNUSED)
398 {
399 	return -ENXIO;
400 }
401 
snd_ctl_ext_set_power_state(snd_ctl_t * handle ATTRIBUTE_UNUSED,unsigned int state ATTRIBUTE_UNUSED)402 static int snd_ctl_ext_set_power_state(snd_ctl_t *handle ATTRIBUTE_UNUSED,
403 				       unsigned int state ATTRIBUTE_UNUSED)
404 {
405 	return 0;
406 }
407 
snd_ctl_ext_get_power_state(snd_ctl_t * handle ATTRIBUTE_UNUSED,unsigned int * state ATTRIBUTE_UNUSED)408 static int snd_ctl_ext_get_power_state(snd_ctl_t *handle ATTRIBUTE_UNUSED,
409 				       unsigned int *state ATTRIBUTE_UNUSED)
410 {
411 	return 0;
412 }
413 
snd_ctl_ext_read(snd_ctl_t * handle,snd_ctl_event_t * event)414 static int snd_ctl_ext_read(snd_ctl_t *handle, snd_ctl_event_t *event)
415 {
416 	snd_ctl_ext_t *ext = handle->private_data;
417 
418 	if (ext->callback->read_event) {
419 		memset(event, 0, sizeof(*event));
420 		return ext->callback->read_event(ext, &event->data.elem.id, &event->data.elem.mask);
421 	}
422 
423 	return -EINVAL;
424 }
425 
snd_ctl_ext_poll_descriptors_count(snd_ctl_t * handle)426 static int snd_ctl_ext_poll_descriptors_count(snd_ctl_t *handle)
427 {
428 	snd_ctl_ext_t *ext = handle->private_data;
429 
430 	if (ext->callback->poll_descriptors_count)
431 		return ext->callback->poll_descriptors_count(ext);
432 	if (ext->poll_fd >= 0)
433 		return 1;
434 	return 0;
435 }
436 
snd_ctl_ext_poll_descriptors(snd_ctl_t * handle,struct pollfd * pfds,unsigned int space)437 static int snd_ctl_ext_poll_descriptors(snd_ctl_t *handle, struct pollfd *pfds, unsigned int space)
438 {
439 	snd_ctl_ext_t *ext = handle->private_data;
440 
441 	if (ext->callback->poll_descriptors)
442 		return ext->callback->poll_descriptors(ext, pfds, space);
443 	if (ext->poll_fd < 0)
444 		return 0;
445 	if (space > 0) {
446 		pfds->fd = ext->poll_fd;
447 		pfds->events = POLLIN|POLLERR|POLLNVAL;
448 		return 1;
449 	}
450 	return 0;
451 }
452 
snd_ctl_ext_poll_revents(snd_ctl_t * handle,struct pollfd * pfds,unsigned int nfds,unsigned short * revents)453 static int snd_ctl_ext_poll_revents(snd_ctl_t *handle, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
454 {
455 	snd_ctl_ext_t *ext = handle->private_data;
456 
457 	if (ext->callback->poll_revents)
458 		return ext->callback->poll_revents(ext, pfds, nfds, revents);
459 	if (nfds == 1) {
460 		*revents = pfds->revents;
461                 return 0;
462 	}
463 	return -EINVAL;
464 }
465 
466 static const snd_ctl_ops_t snd_ctl_ext_ops = {
467 	.close = snd_ctl_ext_close,
468 	.nonblock = snd_ctl_ext_nonblock,
469 	.async = snd_ctl_ext_async,
470 	.subscribe_events = snd_ctl_ext_subscribe_events,
471 	.card_info = snd_ctl_ext_card_info,
472 	.element_list = snd_ctl_ext_elem_list,
473 	.element_info = snd_ctl_ext_elem_info,
474 	.element_add = snd_ctl_ext_elem_add,
475 	.element_replace = snd_ctl_ext_elem_replace,
476 	.element_remove = snd_ctl_ext_elem_remove,
477 	.element_read = snd_ctl_ext_elem_read,
478 	.element_write = snd_ctl_ext_elem_write,
479 	.element_lock = snd_ctl_ext_elem_lock,
480 	.element_unlock = snd_ctl_ext_elem_unlock,
481 	.element_tlv = snd_ctl_ext_elem_tlv,
482 	.hwdep_next_device = snd_ctl_ext_next_device,
483 	.hwdep_info = snd_ctl_ext_hwdep_info,
484 	.pcm_next_device = snd_ctl_ext_next_device,
485 	.pcm_info = snd_ctl_ext_pcm_info,
486 	.pcm_prefer_subdevice = snd_ctl_ext_prefer_subdevice,
487 	.rawmidi_next_device = snd_ctl_ext_next_device,
488 	.rawmidi_info = snd_ctl_ext_rawmidi_info,
489 	.rawmidi_prefer_subdevice = snd_ctl_ext_prefer_subdevice,
490 	.set_power_state = snd_ctl_ext_set_power_state,
491 	.get_power_state = snd_ctl_ext_get_power_state,
492 	.read = snd_ctl_ext_read,
493 	.poll_descriptors_count = snd_ctl_ext_poll_descriptors_count,
494 	.poll_descriptors = snd_ctl_ext_poll_descriptors,
495 	.poll_revents = snd_ctl_ext_poll_revents,
496 };
497 
498 /*
499  * Exported functions
500  */
501 
502 /*! \page ctl_external_plugins External Control Plugin SDK
503 
504 \section ctl_externals External Control Plugins
505 
506 The external plugins are implemented in a shared object file located
507 at /usr/lib/alsa-lib (the exact location depends on the build option
508 and asoundrc configuration).  It has to be the file like
509 libasound_module_ctl_MYPLUGIN.so, where MYPLUGIN corresponds to your
510 own plugin name.
511 
512 The entry point of the plugin is defined via
513 #SND_CTL_PLUGIN_DEFINE_FUNC() macro.  This macro defines the function
514 with a proper name to be referred from alsa-lib.  The function takes
515 the following 5 arguments:
516 \code
517 int (snd_ctl_t **phandle, const char *name, snd_config_t *root,
518 	snd_config_t *conf, int mode)
519 \endcode
520 The first argument, phandle, is the pointer to store the resultant control
521 handle.  The arguments name, root and mode are the parameters
522 to be passed to the plugin constructor.  The conf is the configuration
523 tree for the plugin.  The arguments above are defined in the macro
524 itself, so don't use variables with the same names to shadow
525 parameters.
526 
527 After parsing the configuration parameters in the given conf tree,
528 usually you will call the external plugin API function
529 #snd_ctl_ext_create().
530 The control handle must be filled *phandle in return.
531 Then this function must return either a value 0 when succeeded, or a
532 negative value as the error code.
533 
534 Finally, add #SND_CTL_PLUGIN_SYMBOL() with the name of your
535 plugin as the argument at the end.  This defines the proper versioned
536 symbol as the reference.
537 
538 The typical code would look like below:
539 \code
540 struct myctl_info {
541 	snd_ctl_ext_t ext;
542 	int my_own_data;
543 	...
544 };
545 
546 SND_CTL_PLUGIN_DEFINE_FUNC(myctl)
547 {
548 	snd_config_iterator_t i, next;
549 	struct myctl_info *myctl;
550 	int err;
551 
552 	snd_config_for_each(i, next, conf) {
553 		snd_config_t *n = snd_config_iterator_entry(i);
554 		const char *id;
555 		if (snd_config_get_id(n, &id) < 0)
556 			continue;
557 		if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0)
558 			continue;
559 		if (strcmp(id, "my_own_parameter") == 0) {
560 			....
561 			continue;
562 		}
563 		SNDERR("Unknown field %s", id);
564 		return -EINVAL;
565 	}
566 
567 	myctl = calloc(1, sizeof(*myctl));
568 	if (myctl == NULL)
569 		return -ENOMEM;
570 
571 	myctl->ext.version = SND_CTL_EXT_VERSION;
572 	myctl->ext.card_idx = 0;
573 	strcpy(myctl->ext.id, "Myctl");
574 	strcpy(myctl->ext.name, "My Control");
575 	strcpy(myctl->ext.longname, "My External Control for Foobar");
576 	strcpy(myctl->ext.mixername, "My Control");
577 	myctl->ext.callback = &my_own_callback;
578 	myctl->ext.private_data = myctl;
579 	....
580 
581 	err = snd_pcm_extplug_create(&myctl->ext, name, mode);
582 	if (err < 0) {
583 		myctl_free(myctl);
584 		return err;
585 	}
586 
587 	*phandle = myctl->ext.handle;
588 	return 0;
589 }
590 
591 SND_CTL_PLUGIN_SYMBOL(myctl);
592 \endcode
593 
594 Read the codes in alsa-plugins package for the real examples.
595 
596 
597 \section ctl_ext_impl Implementation of External Control Plugins
598 
599 The following fields have to be filled in external control record before calling
600 #snd_ctl_ext_create() : version, card_idx, id, name, longname, mixername, poll_fd and callback.
601 Otherfields are optional and should be initialized with zero.
602 
603 The constant #SND_CTL_EXT_VERSION must be passed to the version
604 field for the version check in alsa-lib.  The card_idx field specifies the card
605 index of this control.  [FIXME: solve confliction of card index in alsa-lib?]
606 
607 The id, name, longname and mixername fields are the strings shown in the card_info
608 inqurirys.  They are the char arrays, so you have to <i>copy</i> strings to these
609 fields.
610 
611 The callback field contains the  table of callback functions for this plugin (defined as
612 #snd_ctl_ext_callback_t).
613 The poll_fd can be used to specify the poll file descriptor for this control.
614 Set -1 if not available.  Alternatively, you can define poll_descriptors_count and
615 poll_descriptors callbacks in the callback table for handling the poll descriptor(s)
616 dynamically after the creation of plugin instance.
617 
618 The driver can set an arbitrary value (pointer) to private_data
619 field to refer its own data in the callbacks.
620 
621 The rest fields are filled by #snd_ctl_ext_create().  The handle field
622 is the resultant PCM handle.  The others are the current status of the
623 PCM.
624 
625 \section ctl_ext_impl Callback Functions of External Control Plugins
626 
627 The callback functions in #snd_ctl_ext_callback_t define the real
628 behavior of the driver.  There are many callbacks but many of them are optional.
629 
630 The close callback is called when the PCM is closed.  If the plugin
631 allocates private resources, this is the place to release them
632 again.  This callback is optional.
633 
634 The elem_count and elem_list callbacks are mandatory.  The elem_count returns the
635 total number of control elements.  The elem_list returns the control element ID
636 of the corresponding element offset (the offset is from 0 to elem_count - 1).
637 The id field is initialized to all zero in prior to elem_list callback.  The callback
638 has to fill the necessary field (typically iface, name and index) in return via the
639 standard control API functions like #snd_ctl_elem_id_set_interface,
640 #snd_ctl_elem_id_set_name and #snd_ctl_elem_id_set_index, etc.  The callbacks should
641 return 0 if successful, or a negative error code.
642 
643 The find_elem callback is used to convert the given control element ID to the
644 certain key value for the faster access to get, read and write callbacks.
645 The key type is alias of unsigned long, so you can assign some static number
646 (e.g. index of the array) to this value of the corresponding element, or
647 assign the pointer (cast to #snd_ctl_ext_key_t).  When no key is defined or found,
648 return #SND_CTL_EXT_KEY_NOT_FOUND.  This callback is (very likely) required
649 if you use get, read and write callbacks as follows.
650 If you need to create a record dynamically (e.g. via malloc) at each find_elem call,
651 the allocated record can be released with the optional free_key callback.
652 
653 The get_attribute is a mandatory callback, which returns the attribute of the
654 control element given via a key value (converted with find_elem callback).
655 It must fill the control element type (#snd_ctl_elem_type_t), the access type
656 (#snd_ctl_ext_access_t), and the count (element array size).  The callback returns
657 0 if successful, or a negative error code, as usual.
658 
659 The get_integer_info, get_integetr64_info and get_enumerated_info callbacks are called
660 to return the information of the given control element for each element type.
661 For integer and integer64 types, the callbacks need to fill the minimal (imin),
662 maximal (imax) and the step (istep) values of the control.  For the enumerated type,
663 the number of enum items must be filled.  Additionally, the enum control has to define
664 get_enumerated_name callback to store the name of the enumerated item of the given control
665 element.  All functions return 0 if successful, or a negative error code.
666 
667 For reading the current values of a control element, read_integer, read_integer64,
668 read_enumerated, read_bytes and read_iec958 callbacks are called depending on the
669 element type.  These callbacks have to fill the current values of the element in return.
670 Note that a control element can be an array.  If it contains more than one values
671 (i.e. the count value in get_attribute callback is more than 1), <i>all</i> values
672 must be filled on the given value pointer as an array.  Also, note that the boolean type
673 is handled as integer here (although boolean type doesn't need to define the corresponding
674 info callback since it's obvious).  These callbacks return 0 if successful, or
675 a negative error code.
676 
677 For writing the current values, write_integer, write_integer64, write_bytes, and
678 write_iec958 callbacks are called as well as for read.  The callbacks should check the
679 current values and compare with the given values.  If they are identical, the callbacks
680 should do nothing and return 0.  If they differ, update the current values and return 1,
681 instead.  For any errors, return a negative error code.
682 
683 The subscribe_events callback is called when the application subscribes or cancels
684 the event notifications (e.g. through mixer API).  The current value of event
685 subscription is kept in the subscribed field.
686 The read_event callback is called for reading a pending notification event.
687 The callback needs to fill the event_mask value, a bit-field defined as SND_CTL_EVENT_MASK_XXX.
688 If no event is pending, return -EAGAIN.  These two callbacks are optional.
689 
690 The poll_descriptors_count and poll_descriptors callbacks are used to return
691 the poll descriptor(s) via callbacks.  As already mentioned, if the callback cannot
692 set the static poll_fd, you can define these callbacks to return dynamically.
693 Also, when multiple poll descriptors are required, use these callbacks.
694 The poll_revents callback is used for handle poll revents.
695 
696 */
697 
698 /**
699  * \brief Create an external control plugin instance
700  * \param ext the plugin handle
701  * \param name name of control
702  * \param mode control open mode
703  * \return 0 if successful, or a negative error code
704  *
705  * Creates the external control instance.
706  *
707  */
snd_ctl_ext_create(snd_ctl_ext_t * ext,const char * name,int mode)708 int snd_ctl_ext_create(snd_ctl_ext_t *ext, const char *name, int mode)
709 {
710 	snd_ctl_t *ctl;
711 	int err;
712 
713 	if (ext->version < SNDRV_PROTOCOL_VERSION(1, 0, 0) ||
714 	    ext->version > SND_CTL_EXT_VERSION) {
715 		SNDERR("ctl_ext: Plugin version mismatch\n");
716 		return -ENXIO;
717 	}
718 
719 	err = snd_ctl_new(&ctl, SND_CTL_TYPE_EXT, name);
720 	if (err < 0)
721 		return err;
722 
723 	ext->handle = ctl;
724 
725 	ctl->ops = &snd_ctl_ext_ops;
726 	ctl->private_data = ext;
727 	ctl->poll_fd = ext->poll_fd;
728 	if (mode & SND_CTL_NONBLOCK)
729 		ext->nonblock = 1;
730 
731 	return 0;
732 }
733 
734 /**
735  * \brief Delete the external control plugin
736  * \param ext the plugin handle
737  * \return 0 if successful, or a negative error code
738  */
snd_ctl_ext_delete(snd_ctl_ext_t * ext)739 int snd_ctl_ext_delete(snd_ctl_ext_t *ext)
740 {
741 	return snd_ctl_close(ext->handle);
742 }
743