source: mainline/uspace/srv/audio/hound/hound.c@ 1433ecda

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1433ecda was 1433ecda, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Fix cstyle: make ccheck-fix and commit only files where all the changes are good.

  • Property mode set to 100644
File size: 15.7 KB
RevLine 
[737b4c0]1/*
2 * Copyright (c) 2012 Jan Vesely
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/**
30 * @addtogroup audio
31 * @brief HelenOS sound server.
32 * @{
33 */
34/** @file
35 */
36
37#include <assert.h>
38#include <stdlib.h>
[1d6dd2a]39#include <str.h>
[737b4c0]40
41#include "hound.h"
42#include "audio_device.h"
43#include "audio_sink.h"
44#include "audio_source.h"
[fa60cd69]45#include "connection.h"
[737b4c0]46#include "log.h"
47#include "errno.h"
48#include "str_error.h"
49
[c799138]50/**
51 * Search devices by name.
52 * @param name String identifier.
53 * @return Pointer to the found device, NULL on failure.
54 */
[feeac0d]55static audio_device_t *find_device_by_name(list_t *list, const char *name)
[1df3018a]56{
[feeac0d]57 assert(list);
58 assert(name);
59
60 list_foreach(*list, link, audio_device_t, dev) {
61 if (str_cmp(name, dev->name) == 0) {
62 log_debug("device with name '%s' is in the list",
63 name);
64 return dev;
65 }
66 }
67
68 return NULL;
[1df3018a]69}
[c799138]70
71/**
72 * Search sources by name.
73 * @param name String identifier.
74 * @return Pointer to the found source, NULL on failure.
75 */
[feeac0d]76static audio_source_t *find_source_by_name(list_t *list, const char *name)
[1df3018a]77{
[feeac0d]78 assert(list);
79 assert(name);
80
81 list_foreach(*list, link, audio_source_t, dev) {
82 if (str_cmp(name, dev->name) == 0) {
83 log_debug("source with name '%s' is in the list",
84 name);
85 return dev;
86 }
87 }
88
89 return NULL;
[1df3018a]90}
[c799138]91
92/**
93 * Search sinks by name.
94 * @param name String identifier.
95 * @return Pointer to the found sink, NULL on failure.
96 */
[feeac0d]97static audio_sink_t *find_sink_by_name(list_t *list, const char *name)
[1df3018a]98{
[feeac0d]99 assert(list);
100 assert(name);
101
102 list_foreach(*list, link, audio_sink_t, dev) {
103 if (str_cmp(name, dev->name) == 0) {
104 log_debug("sink with name '%s' is in the list",
105 name);
106 return dev;
107 }
108 }
109
110 return NULL;
[1df3018a]111}
[3b6c1d4]112
[1433ecda]113static errno_t hound_disconnect_internal(hound_t *hound, const char *source_name, const char *sink_name);
[1df3018a]114
[c799138]115/**
116 * Remove provided sink.
117 * @param hound The hound structure.
118 * @param sink Target sink to remove.
119 *
120 * This function has to be called with the list_guard lock held.
121 */
[6da3baec]122static void hound_remove_sink_internal(hound_t *hound, audio_sink_t *sink)
123{
124 assert(hound);
125 assert(sink);
[c799138]126 assert(fibril_mutex_is_locked(&hound->list_guard));
[6da3baec]127 log_verbose("Removing sink '%s'.", sink->name);
128 if (!list_empty(&sink->connections))
129 log_warning("Removing sink '%s' while still connected.", sink->name);
130 while (!list_empty(&sink->connections)) {
[feeac0d]131 connection_t *conn = list_get_instance(
132 list_first(&sink->connections), connection_t, sink_link);
[6da3baec]133 list_remove(&conn->hound_link);
134 connection_destroy(conn);
135 }
136 list_remove(&sink->link);
137}
138
[c799138]139/**
140 * Remove provided source.
141 * @param hound The hound structure.
142 * @param sink Target source to remove.
143 *
144 * This function has to be called with the guard lock held.
145 */
[6da3baec]146static void hound_remove_source_internal(hound_t *hound, audio_source_t *source)
147{
148 log_verbose("Removing source '%s'.", source->name);
149 if (!list_empty(&source->connections))
150 log_warning("Removing source '%s' while still connected.", source->name);
151 while (!list_empty(&source->connections)) {
[feeac0d]152 connection_t *conn = list_get_instance(
153 list_first(&source->connections), connection_t, source_link);
[6da3baec]154 list_remove(&conn->hound_link);
155 connection_destroy(conn);
156 }
157 list_remove(&source->link);
158}
159
[c799138]160/**
161 * Initialize hound structure.
162 * @param hound The structure to initialize.
163 * @return Error code.
164 */
[b7fd2a0]165errno_t hound_init(hound_t *hound)
[737b4c0]166{
167 assert(hound);
[1df3018a]168 fibril_mutex_initialize(&hound->list_guard);
[737b4c0]169 list_initialize(&hound->devices);
[3b6c1d4]170 list_initialize(&hound->contexts);
[1df3018a]171 list_initialize(&hound->sources);
[737b4c0]172 list_initialize(&hound->sinks);
[fa60cd69]173 list_initialize(&hound->connections);
[737b4c0]174 return EOK;
175}
176
[c799138]177/**
178 * Add a new application context.
179 * @param hound Hound structure.
180 * @param ctx Context to add.
181 * @return Error code.
182 */
[b7fd2a0]183errno_t hound_add_ctx(hound_t *hound, hound_ctx_t *ctx)
[3b6c1d4]184{
185 log_info("Trying to add context %p", ctx);
186 assert(hound);
187 if (!ctx)
188 return EINVAL;
[5ffcbaf]189 fibril_mutex_lock(&hound->list_guard);
[3b6c1d4]190 list_append(&ctx->link, &hound->contexts);
[5ffcbaf]191 fibril_mutex_unlock(&hound->list_guard);
[b7fd2a0]192 errno_t ret = EOK;
[8f8ec69]193 if (ret == EOK && ctx->source)
194 ret = hound_add_source(hound, ctx->source);
195 if (ret == EOK && ctx->sink)
196 ret = hound_add_sink(hound, ctx->sink);
[c799138]197 if (ret != EOK) {
198 fibril_mutex_lock(&hound->list_guard);
199 list_remove(&ctx->link);
200 fibril_mutex_unlock(&hound->list_guard);
201 }
[8f8ec69]202 return ret;
[3b6c1d4]203}
204
[c799138]205/**
206 * Remove existing application context.
207 * @param hound Hound structure.
208 * @param ctx Context to remove.
209 * @return Error code.
210 */
[b7fd2a0]211errno_t hound_remove_ctx(hound_t *hound, hound_ctx_t *ctx)
[3b6c1d4]212{
213 assert(hound);
214 if (!ctx)
215 return EINVAL;
[353f8cc]216 if (!list_empty(&ctx->streams))
217 return EBUSY;
[5ffcbaf]218 fibril_mutex_lock(&hound->list_guard);
[3b6c1d4]219 list_remove(&ctx->link);
[6da3baec]220 if (ctx->source)
221 hound_remove_source_internal(hound, ctx->source);
222 if (ctx->sink)
223 hound_remove_sink_internal(hound, ctx->sink);
[5ffcbaf]224 fibril_mutex_unlock(&hound->list_guard);
[3b6c1d4]225 return EOK;
226}
227
[c799138]228/**
229 * Search registered contexts for the matching id.
230 * @param hound The hound structure.
231 * @param id Requested id.
232 * @return Pointer to the found structure, NULL on failure.
233 */
[3b6c1d4]234hound_ctx_t *hound_get_ctx_by_id(hound_t *hound, hound_context_id_t id)
235{
236 assert(hound);
237
[5ffcbaf]238 fibril_mutex_lock(&hound->list_guard);
239 hound_ctx_t *res = NULL;
[feeac0d]240 list_foreach(hound->contexts, link, hound_ctx_t, ctx) {
[5ffcbaf]241 if (hound_ctx_get_id(ctx) == id) {
242 res = ctx;
243 break;
244 }
[3b6c1d4]245 }
[5ffcbaf]246 fibril_mutex_unlock(&hound->list_guard);
247 return res;
[3b6c1d4]248}
249
[c799138]250/**
251 * Add a new device.
252 * @param hound The hound structure.
253 * @param id Locations service id representing the device driver.
254 * @param name String identifier.
255 * @return Error code.
256 */
[b7fd2a0]257errno_t hound_add_device(hound_t *hound, service_id_t id, const char *name)
[737b4c0]258{
259 log_verbose("Adding device \"%s\", service: %zu", name, id);
260
261 assert(hound);
262 if (!name || !id) {
263 log_debug("Incorrect parameters.");
264 return EINVAL;
265 }
266
[feeac0d]267 list_foreach(hound->devices, link, audio_device_t, dev) {
[737b4c0]268 if (dev->id == id) {
269 log_debug("Device with id %zu is already present", id);
[8a637a4]270 return EEXIST;
[737b4c0]271 }
272 }
273
[1df3018a]274 audio_device_t *dev = find_device_by_name(&hound->devices, name);
275 if (dev) {
276 log_debug("Device with name %s is already present", name);
[8a637a4]277 return EEXIST;
[1df3018a]278 }
279
280 dev = malloc(sizeof(audio_device_t));
[737b4c0]281 if (!dev) {
282 log_debug("Failed to malloc device structure.");
283 return ENOMEM;
284 }
[d93a5a6f]285
[b7fd2a0]286 const errno_t ret = audio_device_init(dev, id, name);
[737b4c0]287 if (ret != EOK) {
288 log_debug("Failed to initialize new audio device: %s",
[1433ecda]289 str_error(ret));
[737b4c0]290 free(dev);
291 return ret;
292 }
293
294 list_append(&dev->link, &hound->devices);
[1df3018a]295 log_info("Added new device: '%s'", dev->name);
[737b4c0]296
297 audio_source_t *source = audio_device_get_source(dev);
298 if (source) {
[b7fd2a0]299 const errno_t ret = hound_add_source(hound, source);
[1df3018a]300 if (ret != EOK) {
301 log_debug("Failed to add device source: %s",
302 str_error(ret));
303 audio_device_fini(dev);
304 return ret;
305 }
[737b4c0]306 log_verbose("Added source: '%s'.", source->name);
307 }
308
309 audio_sink_t *sink = audio_device_get_sink(dev);
310 if (sink) {
[b7fd2a0]311 const errno_t ret = hound_add_sink(hound, sink);
[1df3018a]312 if (ret != EOK) {
313 log_debug("Failed to add device sink: %s",
314 str_error(ret));
315 audio_device_fini(dev);
316 return ret;
317 }
[737b4c0]318 log_verbose("Added sink: '%s'.", sink->name);
319 }
320
321 if (!source && !sink)
322 log_warning("Neither sink nor source on device '%s'.", name);
323
324 return ret;
325}
326
[c799138]327/**
328 * Register a new source.
329 * @param hound The hound structure.
330 * @param source A new source to add.
331 * @return Error code.
332 */
[b7fd2a0]333errno_t hound_add_source(hound_t *hound, audio_source_t *source)
[1df3018a]334{
335 assert(hound);
[6906f61]336 if (!source || !source->name || str_cmp(source->name, "default") == 0) {
[1df3018a]337 log_debug("Invalid source specified.");
338 return EINVAL;
339 }
340 fibril_mutex_lock(&hound->list_guard);
341 if (find_source_by_name(&hound->sources, source->name)) {
342 log_debug("Source by that name already exists");
343 fibril_mutex_unlock(&hound->list_guard);
[8a637a4]344 return EEXIST;
[1df3018a]345 }
346 list_append(&source->link, &hound->sources);
347 fibril_mutex_unlock(&hound->list_guard);
348 return EOK;
349}
350
[c799138]351/**
352 * Register a new sink.
353 * @param hound The hound structure.
354 * @param sink A new sink to add.
355 * @return Error code.
356 */
[b7fd2a0]357errno_t hound_add_sink(hound_t *hound, audio_sink_t *sink)
[1df3018a]358{
359 assert(hound);
[6906f61]360 if (!sink || !sink->name || str_cmp(sink->name, "default") == 0) {
[1df3018a]361 log_debug("Invalid source specified.");
362 return EINVAL;
363 }
364 fibril_mutex_lock(&hound->list_guard);
365 if (find_sink_by_name(&hound->sinks, sink->name)) {
366 log_debug("Sink by that name already exists");
367 fibril_mutex_unlock(&hound->list_guard);
[8a637a4]368 return EEXIST;
[1df3018a]369 }
370 list_append(&sink->link, &hound->sinks);
371 fibril_mutex_unlock(&hound->list_guard);
372 return EOK;
373}
374
[c799138]375/**
376 * Remove a registered source.
377 * @param hound The hound structure.
378 * @param source A registered source to remove.
379 * @return Error code.
380 */
[b7fd2a0]381errno_t hound_remove_source(hound_t *hound, audio_source_t *source)
[6424800]382{
383 assert(hound);
384 if (!source)
385 return EINVAL;
386 fibril_mutex_lock(&hound->list_guard);
[6da3baec]387 hound_remove_source_internal(hound, source);
[6424800]388 fibril_mutex_unlock(&hound->list_guard);
389 return EOK;
390}
391
[c799138]392/**
393 * Remove a registered sink.
394 * @param hound The hound structure.
395 * @param sink A registered sink to remove.
396 * @return Error code.
397 */
[b7fd2a0]398errno_t hound_remove_sink(hound_t *hound, audio_sink_t *sink)
[6424800]399{
400 assert(hound);
401 if (!sink)
402 return EINVAL;
403 fibril_mutex_lock(&hound->list_guard);
[6da3baec]404 hound_remove_sink_internal(hound, sink);
[6424800]405 fibril_mutex_unlock(&hound->list_guard);
406 return EOK;
407}
408
[c799138]409/**
410 * List all registered sources.
411 * @param[in] hound The hound structure.
412 * @param[out] list List of the string identifiers.
413 * @param[out] size Number of identifiers int he @p list.
414 * @return Error code.
415 */
[33b8d024]416errno_t hound_list_sources(hound_t *hound, char ***list, size_t *size)
[5ffcbaf]417{
418 assert(hound);
419 if (!list || !size)
420 return EINVAL;
421
422 fibril_mutex_lock(&hound->list_guard);
[be12474]423 const unsigned long count = list_count(&hound->sources);
[5ffcbaf]424 if (count == 0) {
425 *list = NULL;
426 *size = 0;
427 fibril_mutex_unlock(&hound->list_guard);
428 return EOK;
429 }
[33b8d024]430 char **names = calloc(count, sizeof(char *));
[b7fd2a0]431 errno_t ret = names ? EOK : ENOMEM;
[be12474]432 for (unsigned long i = 0; i < count && ret == EOK; ++i) {
[5ffcbaf]433 link_t *slink = list_nth(&hound->sources, i);
434 audio_source_t *source = audio_source_list_instance(slink);
435 names[i] = str_dup(source->name);
436 if (names[i])
437 ret = ENOMEM;
438 }
439 if (ret == EOK) {
440 *size = count;
441 *list = names;
442 } else {
443 for (size_t i = 0; i < count; ++i)
444 free(names[i]);
445 free(names);
446 }
447 fibril_mutex_unlock(&hound->list_guard);
448 return ret;
449}
450
[c799138]451/**
452 * List all registered sinks.
453 * @param[in] hound The hound structure.
454 * @param[out] list List of the string identifiers.
455 * @param[out] size Number of identifiers int he @p list.
456 * @return Error code.
457 */
[33b8d024]458errno_t hound_list_sinks(hound_t *hound, char ***list, size_t *size)
[5ffcbaf]459{
460 assert(hound);
461 if (!list || !size)
462 return EINVAL;
463
464 fibril_mutex_lock(&hound->list_guard);
465 const size_t count = list_count(&hound->sinks);
466 if (count == 0) {
467 *list = NULL;
468 *size = 0;
469 fibril_mutex_unlock(&hound->list_guard);
470 return EOK;
471 }
[33b8d024]472 char **names = calloc(count, sizeof(char *));
[b7fd2a0]473 errno_t ret = names ? EOK : ENOMEM;
[5ffcbaf]474 for (size_t i = 0; i < count && ret == EOK; ++i) {
475 link_t *slink = list_nth(&hound->sinks, i);
476 audio_sink_t *sink = audio_sink_list_instance(slink);
477 names[i] = str_dup(sink->name);
478 if (!names[i])
479 ret = ENOMEM;
480 }
481 if (ret == EOK) {
482 *size = count;
483 *list = names;
484 } else {
485 for (size_t i = 0; i < count; ++i)
486 free(names[i]);
487 free(names);
488 }
489 fibril_mutex_unlock(&hound->list_guard);
490 return ret;
491}
492
[c799138]493/**
494 * List all connections
495 * @param[in] hound The hound structure.
496 * @param[out] sources List of the source string identifiers.
497 * @param[out] sinks List of the sinks string identifiers.
498 * @param[out] size Number of identifiers int he @p list.
499 * @return Error code.
500 *
501 * Lists include duplicit name entries. The order of entries is important,
502 * identifiers with the same index are connected.
503 */
[b7fd2a0]504errno_t hound_list_connections(hound_t *hound, const char ***sources,
[5ffcbaf]505 const char ***sinks, size_t *size)
506{
507 fibril_mutex_lock(&hound->list_guard);
508 fibril_mutex_unlock(&hound->list_guard);
509 return ENOTSUP;
510}
511
[c799138]512/**
513 * Create and register a new connection.
514 * @param hound The hound structure.
515 * @param source_name Source's string id.
516 * @param sink_name Sink's string id.
517 * @return Error code.
518 */
[1433ecda]519errno_t hound_connect(hound_t *hound, const char *source_name, const char *sink_name)
[f3fced0]520{
521 assert(hound);
522 log_verbose("Connecting '%s' to '%s'.", source_name, sink_name);
523 fibril_mutex_lock(&hound->list_guard);
[6906f61]524
[f3fced0]525 audio_source_t *source =
[6906f61]526 audio_source_list_instance(list_first(&hound->sources));
527 if (str_cmp(source_name, "default") != 0)
[1433ecda]528 source = find_source_by_name(&hound->sources, source_name);
[6906f61]529
530 audio_sink_t *sink =
531 audio_sink_list_instance(list_first(&hound->sinks));
532 if (str_cmp(sink_name, "default") != 0)
[1433ecda]533 sink = find_sink_by_name(&hound->sinks, sink_name);
[6906f61]534
[f3fced0]535 if (!source || !sink) {
536 fibril_mutex_unlock(&hound->list_guard);
[1c33539]537 log_debug("Source (%p), or sink (%p) not found", source, sink);
[f3fced0]538 return ENOENT;
539 }
[fa60cd69]540 connection_t *conn = connection_create(source, sink);
541 if (!conn) {
542 fibril_mutex_unlock(&hound->list_guard);
543 log_debug("Failed to create connection");
544 return ENOMEM;
[f3fced0]545 }
[fa60cd69]546 list_append(&conn->hound_link, &hound->connections);
[f3fced0]547 fibril_mutex_unlock(&hound->list_guard);
548 return EOK;
549}
550
[c799138]551/**
552 * Find and destroy connection between source and sink.
553 * @param hound The hound structure.
554 * @param source_name Source's string id.
555 * @param sink_name Sink's string id.
556 * @return Error code.
557 */
[1433ecda]558errno_t hound_disconnect(hound_t *hound, const char *source_name, const char *sink_name)
[f3fced0]559{
[1c33539]560 assert(hound);
561 fibril_mutex_lock(&hound->list_guard);
[b7fd2a0]562 const errno_t ret = hound_disconnect_internal(hound, source_name, sink_name);
[13df13c8]563 fibril_mutex_unlock(&hound->list_guard);
564 return ret;
565}
566
[c799138]567/**
568 * Internal disconnect helper.
569 * @param hound The hound structure.
570 * @param source_name Source's string id.
571 * @param sink_name Sink's string id.
572 * @return Error code.
573 *
574 * This function has to be called with the list_guard lock held.
575 */
[1433ecda]576static errno_t hound_disconnect_internal(hound_t *hound, const char *source_name,
577 const char *sink_name)
[13df13c8]578{
579 assert(hound);
580 assert(fibril_mutex_is_locked(&hound->list_guard));
[fa60cd69]581 log_debug("Disconnecting '%s' to '%s'.", source_name, sink_name);
582
583 list_foreach_safe(hound->connections, it, next) {
584 connection_t *conn = connection_from_hound_list(it);
585 if (str_cmp(connection_source_name(conn), source_name) == 0 ||
586 str_cmp(connection_sink_name(conn), sink_name) == 0) {
[1433ecda]587 log_debug("Removing %s -> %s", connection_source_name(conn),
588 connection_sink_name(conn));
589 list_remove(it);
590 connection_destroy(conn);
[fa60cd69]591 }
[1c33539]592 }
[fa60cd69]593
[1c33539]594 return EOK;
[f3fced0]595}
[737b4c0]596/**
597 * @}
598 */
Note: See TracBrowser for help on using the repository browser.