source: mainline/uspace/srv/audio/hound/hound.c@ a76e76b

ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a76e76b was a0765f6, checked in by Jakub Jermar <jakub@…>, 7 years ago

Add assert to match the precondition from comment

  • Property mode set to 100644
File size: 15.8 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{
[a0765f6]148 assert(fibril_mutex_is_locked(&hound->list_guard));
[6da3baec]149 log_verbose("Removing source '%s'.", source->name);
150 if (!list_empty(&source->connections))
151 log_warning("Removing source '%s' while still connected.", source->name);
152 while (!list_empty(&source->connections)) {
[feeac0d]153 connection_t *conn = list_get_instance(
154 list_first(&source->connections), connection_t, source_link);
[6da3baec]155 list_remove(&conn->hound_link);
156 connection_destroy(conn);
157 }
158 list_remove(&source->link);
159}
160
[c799138]161/**
162 * Initialize hound structure.
163 * @param hound The structure to initialize.
164 * @return Error code.
165 */
[b7fd2a0]166errno_t hound_init(hound_t *hound)
[737b4c0]167{
168 assert(hound);
[1df3018a]169 fibril_mutex_initialize(&hound->list_guard);
[737b4c0]170 list_initialize(&hound->devices);
[3b6c1d4]171 list_initialize(&hound->contexts);
[1df3018a]172 list_initialize(&hound->sources);
[737b4c0]173 list_initialize(&hound->sinks);
[fa60cd69]174 list_initialize(&hound->connections);
[737b4c0]175 return EOK;
176}
177
[c799138]178/**
179 * Add a new application context.
180 * @param hound Hound structure.
181 * @param ctx Context to add.
182 * @return Error code.
183 */
[b7fd2a0]184errno_t hound_add_ctx(hound_t *hound, hound_ctx_t *ctx)
[3b6c1d4]185{
186 log_info("Trying to add context %p", ctx);
187 assert(hound);
188 if (!ctx)
189 return EINVAL;
[5ffcbaf]190 fibril_mutex_lock(&hound->list_guard);
[3b6c1d4]191 list_append(&ctx->link, &hound->contexts);
[5ffcbaf]192 fibril_mutex_unlock(&hound->list_guard);
[b7fd2a0]193 errno_t ret = EOK;
[8f8ec69]194 if (ret == EOK && ctx->source)
195 ret = hound_add_source(hound, ctx->source);
196 if (ret == EOK && ctx->sink)
197 ret = hound_add_sink(hound, ctx->sink);
[c799138]198 if (ret != EOK) {
199 fibril_mutex_lock(&hound->list_guard);
200 list_remove(&ctx->link);
201 fibril_mutex_unlock(&hound->list_guard);
202 }
[8f8ec69]203 return ret;
[3b6c1d4]204}
205
[c799138]206/**
207 * Remove existing application context.
208 * @param hound Hound structure.
209 * @param ctx Context to remove.
210 * @return Error code.
211 */
[b7fd2a0]212errno_t hound_remove_ctx(hound_t *hound, hound_ctx_t *ctx)
[3b6c1d4]213{
214 assert(hound);
215 if (!ctx)
216 return EINVAL;
[353f8cc]217 if (!list_empty(&ctx->streams))
218 return EBUSY;
[5ffcbaf]219 fibril_mutex_lock(&hound->list_guard);
[3b6c1d4]220 list_remove(&ctx->link);
[6da3baec]221 if (ctx->source)
222 hound_remove_source_internal(hound, ctx->source);
223 if (ctx->sink)
224 hound_remove_sink_internal(hound, ctx->sink);
[5ffcbaf]225 fibril_mutex_unlock(&hound->list_guard);
[3b6c1d4]226 return EOK;
227}
228
[c799138]229/**
230 * Search registered contexts for the matching id.
231 * @param hound The hound structure.
232 * @param id Requested id.
233 * @return Pointer to the found structure, NULL on failure.
234 */
[3b6c1d4]235hound_ctx_t *hound_get_ctx_by_id(hound_t *hound, hound_context_id_t id)
236{
237 assert(hound);
238
[5ffcbaf]239 fibril_mutex_lock(&hound->list_guard);
240 hound_ctx_t *res = NULL;
[feeac0d]241 list_foreach(hound->contexts, link, hound_ctx_t, ctx) {
[5ffcbaf]242 if (hound_ctx_get_id(ctx) == id) {
243 res = ctx;
244 break;
245 }
[3b6c1d4]246 }
[5ffcbaf]247 fibril_mutex_unlock(&hound->list_guard);
248 return res;
[3b6c1d4]249}
250
[c799138]251/**
252 * Add a new device.
253 * @param hound The hound structure.
254 * @param id Locations service id representing the device driver.
255 * @param name String identifier.
256 * @return Error code.
257 */
[b7fd2a0]258errno_t hound_add_device(hound_t *hound, service_id_t id, const char *name)
[737b4c0]259{
260 log_verbose("Adding device \"%s\", service: %zu", name, id);
261
262 assert(hound);
263 if (!name || !id) {
264 log_debug("Incorrect parameters.");
265 return EINVAL;
266 }
267
[feeac0d]268 list_foreach(hound->devices, link, audio_device_t, dev) {
[737b4c0]269 if (dev->id == id) {
270 log_debug("Device with id %zu is already present", id);
[8a637a4]271 return EEXIST;
[737b4c0]272 }
273 }
274
[1df3018a]275 audio_device_t *dev = find_device_by_name(&hound->devices, name);
276 if (dev) {
277 log_debug("Device with name %s is already present", name);
[8a637a4]278 return EEXIST;
[1df3018a]279 }
280
281 dev = malloc(sizeof(audio_device_t));
[737b4c0]282 if (!dev) {
283 log_debug("Failed to malloc device structure.");
284 return ENOMEM;
285 }
[d93a5a6f]286
[b7fd2a0]287 const errno_t ret = audio_device_init(dev, id, name);
[737b4c0]288 if (ret != EOK) {
289 log_debug("Failed to initialize new audio device: %s",
[1433ecda]290 str_error(ret));
[737b4c0]291 free(dev);
292 return ret;
293 }
294
295 list_append(&dev->link, &hound->devices);
[1df3018a]296 log_info("Added new device: '%s'", dev->name);
[737b4c0]297
298 audio_source_t *source = audio_device_get_source(dev);
299 if (source) {
[b7fd2a0]300 const errno_t ret = hound_add_source(hound, source);
[1df3018a]301 if (ret != EOK) {
302 log_debug("Failed to add device source: %s",
303 str_error(ret));
304 audio_device_fini(dev);
305 return ret;
306 }
[737b4c0]307 log_verbose("Added source: '%s'.", source->name);
308 }
309
310 audio_sink_t *sink = audio_device_get_sink(dev);
311 if (sink) {
[b7fd2a0]312 const errno_t ret = hound_add_sink(hound, sink);
[1df3018a]313 if (ret != EOK) {
314 log_debug("Failed to add device sink: %s",
315 str_error(ret));
316 audio_device_fini(dev);
317 return ret;
318 }
[737b4c0]319 log_verbose("Added sink: '%s'.", sink->name);
320 }
321
322 if (!source && !sink)
323 log_warning("Neither sink nor source on device '%s'.", name);
324
325 return ret;
326}
327
[c799138]328/**
329 * Register a new source.
330 * @param hound The hound structure.
331 * @param source A new source to add.
332 * @return Error code.
333 */
[b7fd2a0]334errno_t hound_add_source(hound_t *hound, audio_source_t *source)
[1df3018a]335{
336 assert(hound);
[6906f61]337 if (!source || !source->name || str_cmp(source->name, "default") == 0) {
[1df3018a]338 log_debug("Invalid source specified.");
339 return EINVAL;
340 }
341 fibril_mutex_lock(&hound->list_guard);
342 if (find_source_by_name(&hound->sources, source->name)) {
343 log_debug("Source by that name already exists");
344 fibril_mutex_unlock(&hound->list_guard);
[8a637a4]345 return EEXIST;
[1df3018a]346 }
347 list_append(&source->link, &hound->sources);
348 fibril_mutex_unlock(&hound->list_guard);
349 return EOK;
350}
351
[c799138]352/**
353 * Register a new sink.
354 * @param hound The hound structure.
355 * @param sink A new sink to add.
356 * @return Error code.
357 */
[b7fd2a0]358errno_t hound_add_sink(hound_t *hound, audio_sink_t *sink)
[1df3018a]359{
360 assert(hound);
[6906f61]361 if (!sink || !sink->name || str_cmp(sink->name, "default") == 0) {
[1df3018a]362 log_debug("Invalid source specified.");
363 return EINVAL;
364 }
365 fibril_mutex_lock(&hound->list_guard);
366 if (find_sink_by_name(&hound->sinks, sink->name)) {
367 log_debug("Sink by that name already exists");
368 fibril_mutex_unlock(&hound->list_guard);
[8a637a4]369 return EEXIST;
[1df3018a]370 }
371 list_append(&sink->link, &hound->sinks);
372 fibril_mutex_unlock(&hound->list_guard);
373 return EOK;
374}
375
[c799138]376/**
377 * Remove a registered source.
378 * @param hound The hound structure.
379 * @param source A registered source to remove.
380 * @return Error code.
381 */
[b7fd2a0]382errno_t hound_remove_source(hound_t *hound, audio_source_t *source)
[6424800]383{
384 assert(hound);
385 if (!source)
386 return EINVAL;
387 fibril_mutex_lock(&hound->list_guard);
[6da3baec]388 hound_remove_source_internal(hound, source);
[6424800]389 fibril_mutex_unlock(&hound->list_guard);
390 return EOK;
391}
392
[c799138]393/**
394 * Remove a registered sink.
395 * @param hound The hound structure.
396 * @param sink A registered sink to remove.
397 * @return Error code.
398 */
[b7fd2a0]399errno_t hound_remove_sink(hound_t *hound, audio_sink_t *sink)
[6424800]400{
401 assert(hound);
402 if (!sink)
403 return EINVAL;
404 fibril_mutex_lock(&hound->list_guard);
[6da3baec]405 hound_remove_sink_internal(hound, sink);
[6424800]406 fibril_mutex_unlock(&hound->list_guard);
407 return EOK;
408}
409
[c799138]410/**
411 * List all registered sources.
412 * @param[in] hound The hound structure.
413 * @param[out] list List of the string identifiers.
414 * @param[out] size Number of identifiers int he @p list.
415 * @return Error code.
416 */
[33b8d024]417errno_t hound_list_sources(hound_t *hound, char ***list, size_t *size)
[5ffcbaf]418{
419 assert(hound);
420 if (!list || !size)
421 return EINVAL;
422
423 fibril_mutex_lock(&hound->list_guard);
[be12474]424 const unsigned long count = list_count(&hound->sources);
[5ffcbaf]425 if (count == 0) {
426 *list = NULL;
427 *size = 0;
428 fibril_mutex_unlock(&hound->list_guard);
429 return EOK;
430 }
[33b8d024]431 char **names = calloc(count, sizeof(char *));
[b7fd2a0]432 errno_t ret = names ? EOK : ENOMEM;
[be12474]433 for (unsigned long i = 0; i < count && ret == EOK; ++i) {
[5ffcbaf]434 link_t *slink = list_nth(&hound->sources, i);
435 audio_source_t *source = audio_source_list_instance(slink);
436 names[i] = str_dup(source->name);
437 if (names[i])
438 ret = ENOMEM;
439 }
440 if (ret == EOK) {
441 *size = count;
442 *list = names;
443 } else {
444 for (size_t i = 0; i < count; ++i)
445 free(names[i]);
446 free(names);
447 }
448 fibril_mutex_unlock(&hound->list_guard);
449 return ret;
450}
451
[c799138]452/**
453 * List all registered sinks.
454 * @param[in] hound The hound structure.
455 * @param[out] list List of the string identifiers.
456 * @param[out] size Number of identifiers int he @p list.
457 * @return Error code.
458 */
[33b8d024]459errno_t hound_list_sinks(hound_t *hound, char ***list, size_t *size)
[5ffcbaf]460{
461 assert(hound);
462 if (!list || !size)
463 return EINVAL;
464
465 fibril_mutex_lock(&hound->list_guard);
466 const size_t count = list_count(&hound->sinks);
467 if (count == 0) {
468 *list = NULL;
469 *size = 0;
470 fibril_mutex_unlock(&hound->list_guard);
471 return EOK;
472 }
[33b8d024]473 char **names = calloc(count, sizeof(char *));
[b7fd2a0]474 errno_t ret = names ? EOK : ENOMEM;
[5ffcbaf]475 for (size_t i = 0; i < count && ret == EOK; ++i) {
476 link_t *slink = list_nth(&hound->sinks, i);
477 audio_sink_t *sink = audio_sink_list_instance(slink);
478 names[i] = str_dup(sink->name);
479 if (!names[i])
480 ret = ENOMEM;
481 }
482 if (ret == EOK) {
483 *size = count;
484 *list = names;
485 } else {
486 for (size_t i = 0; i < count; ++i)
487 free(names[i]);
488 free(names);
489 }
490 fibril_mutex_unlock(&hound->list_guard);
491 return ret;
492}
493
[c799138]494/**
495 * List all connections
496 * @param[in] hound The hound structure.
497 * @param[out] sources List of the source string identifiers.
498 * @param[out] sinks List of the sinks string identifiers.
499 * @param[out] size Number of identifiers int he @p list.
500 * @return Error code.
501 *
502 * Lists include duplicit name entries. The order of entries is important,
503 * identifiers with the same index are connected.
504 */
[b7fd2a0]505errno_t hound_list_connections(hound_t *hound, const char ***sources,
[5ffcbaf]506 const char ***sinks, size_t *size)
507{
508 fibril_mutex_lock(&hound->list_guard);
509 fibril_mutex_unlock(&hound->list_guard);
510 return ENOTSUP;
511}
512
[c799138]513/**
514 * Create and register a new connection.
515 * @param hound The hound structure.
516 * @param source_name Source's string id.
517 * @param sink_name Sink's string id.
518 * @return Error code.
519 */
[1433ecda]520errno_t hound_connect(hound_t *hound, const char *source_name, const char *sink_name)
[f3fced0]521{
522 assert(hound);
523 log_verbose("Connecting '%s' to '%s'.", source_name, sink_name);
524 fibril_mutex_lock(&hound->list_guard);
[6906f61]525
[f3fced0]526 audio_source_t *source =
[6906f61]527 audio_source_list_instance(list_first(&hound->sources));
528 if (str_cmp(source_name, "default") != 0)
[1433ecda]529 source = find_source_by_name(&hound->sources, source_name);
[6906f61]530
531 audio_sink_t *sink =
532 audio_sink_list_instance(list_first(&hound->sinks));
533 if (str_cmp(sink_name, "default") != 0)
[1433ecda]534 sink = find_sink_by_name(&hound->sinks, sink_name);
[6906f61]535
[f3fced0]536 if (!source || !sink) {
537 fibril_mutex_unlock(&hound->list_guard);
[1c33539]538 log_debug("Source (%p), or sink (%p) not found", source, sink);
[f3fced0]539 return ENOENT;
540 }
[fa60cd69]541 connection_t *conn = connection_create(source, sink);
542 if (!conn) {
543 fibril_mutex_unlock(&hound->list_guard);
544 log_debug("Failed to create connection");
545 return ENOMEM;
[f3fced0]546 }
[fa60cd69]547 list_append(&conn->hound_link, &hound->connections);
[f3fced0]548 fibril_mutex_unlock(&hound->list_guard);
549 return EOK;
550}
551
[c799138]552/**
553 * Find and destroy connection between source and sink.
554 * @param hound The hound structure.
555 * @param source_name Source's string id.
556 * @param sink_name Sink's string id.
557 * @return Error code.
558 */
[1433ecda]559errno_t hound_disconnect(hound_t *hound, const char *source_name, const char *sink_name)
[f3fced0]560{
[1c33539]561 assert(hound);
562 fibril_mutex_lock(&hound->list_guard);
[b7fd2a0]563 const errno_t ret = hound_disconnect_internal(hound, source_name, sink_name);
[13df13c8]564 fibril_mutex_unlock(&hound->list_guard);
565 return ret;
566}
567
[c799138]568/**
569 * Internal disconnect helper.
570 * @param hound The hound structure.
571 * @param source_name Source's string id.
572 * @param sink_name Sink's string id.
573 * @return Error code.
574 *
575 * This function has to be called with the list_guard lock held.
576 */
[1433ecda]577static errno_t hound_disconnect_internal(hound_t *hound, const char *source_name,
578 const char *sink_name)
[13df13c8]579{
580 assert(hound);
581 assert(fibril_mutex_is_locked(&hound->list_guard));
[fa60cd69]582 log_debug("Disconnecting '%s' to '%s'.", source_name, sink_name);
583
584 list_foreach_safe(hound->connections, it, next) {
585 connection_t *conn = connection_from_hound_list(it);
586 if (str_cmp(connection_source_name(conn), source_name) == 0 ||
587 str_cmp(connection_sink_name(conn), sink_name) == 0) {
[1433ecda]588 log_debug("Removing %s -> %s", connection_source_name(conn),
589 connection_sink_name(conn));
590 list_remove(it);
591 connection_destroy(conn);
[fa60cd69]592 }
[1c33539]593 }
[fa60cd69]594
[1c33539]595 return EOK;
[f3fced0]596}
[737b4c0]597/**
598 * @}
599 */
Note: See TracBrowser for help on using the repository browser.