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