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
Line 
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>
39#include <str.h>
40
41#include "hound.h"
42#include "audio_device.h"
43#include "audio_sink.h"
44#include "audio_source.h"
45#include "connection.h"
46#include "log.h"
47#include "errno.h"
48#include "str_error.h"
49
50/**
51 * Search devices by name.
52 * @param name String identifier.
53 * @return Pointer to the found device, NULL on failure.
54 */
55static audio_device_t *find_device_by_name(list_t *list, const char *name)
56{
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;
69}
70
71/**
72 * Search sources by name.
73 * @param name String identifier.
74 * @return Pointer to the found source, NULL on failure.
75 */
76static audio_source_t *find_source_by_name(list_t *list, const char *name)
77{
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;
90}
91
92/**
93 * Search sinks by name.
94 * @param name String identifier.
95 * @return Pointer to the found sink, NULL on failure.
96 */
97static audio_sink_t *find_sink_by_name(list_t *list, const char *name)
98{
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;
111}
112
113static errno_t hound_disconnect_internal(hound_t *hound, const char *source_name, const char *sink_name);
114
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 */
122static void hound_remove_sink_internal(hound_t *hound, audio_sink_t *sink)
123{
124 assert(hound);
125 assert(sink);
126 assert(fibril_mutex_is_locked(&hound->list_guard));
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)) {
131 connection_t *conn = list_get_instance(
132 list_first(&sink->connections), connection_t, sink_link);
133 list_remove(&conn->hound_link);
134 connection_destroy(conn);
135 }
136 list_remove(&sink->link);
137}
138
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 */
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)) {
152 connection_t *conn = list_get_instance(
153 list_first(&source->connections), connection_t, source_link);
154 list_remove(&conn->hound_link);
155 connection_destroy(conn);
156 }
157 list_remove(&source->link);
158}
159
160/**
161 * Initialize hound structure.
162 * @param hound The structure to initialize.
163 * @return Error code.
164 */
165errno_t hound_init(hound_t *hound)
166{
167 assert(hound);
168 fibril_mutex_initialize(&hound->list_guard);
169 list_initialize(&hound->devices);
170 list_initialize(&hound->contexts);
171 list_initialize(&hound->sources);
172 list_initialize(&hound->sinks);
173 list_initialize(&hound->connections);
174 return EOK;
175}
176
177/**
178 * Add a new application context.
179 * @param hound Hound structure.
180 * @param ctx Context to add.
181 * @return Error code.
182 */
183errno_t hound_add_ctx(hound_t *hound, hound_ctx_t *ctx)
184{
185 log_info("Trying to add context %p", ctx);
186 assert(hound);
187 if (!ctx)
188 return EINVAL;
189 fibril_mutex_lock(&hound->list_guard);
190 list_append(&ctx->link, &hound->contexts);
191 fibril_mutex_unlock(&hound->list_guard);
192 errno_t ret = EOK;
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);
197 if (ret != EOK) {
198 fibril_mutex_lock(&hound->list_guard);
199 list_remove(&ctx->link);
200 fibril_mutex_unlock(&hound->list_guard);
201 }
202 return ret;
203}
204
205/**
206 * Remove existing application context.
207 * @param hound Hound structure.
208 * @param ctx Context to remove.
209 * @return Error code.
210 */
211errno_t hound_remove_ctx(hound_t *hound, hound_ctx_t *ctx)
212{
213 assert(hound);
214 if (!ctx)
215 return EINVAL;
216 if (!list_empty(&ctx->streams))
217 return EBUSY;
218 fibril_mutex_lock(&hound->list_guard);
219 list_remove(&ctx->link);
220 if (ctx->source)
221 hound_remove_source_internal(hound, ctx->source);
222 if (ctx->sink)
223 hound_remove_sink_internal(hound, ctx->sink);
224 fibril_mutex_unlock(&hound->list_guard);
225 return EOK;
226}
227
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 */
234hound_ctx_t *hound_get_ctx_by_id(hound_t *hound, hound_context_id_t id)
235{
236 assert(hound);
237
238 fibril_mutex_lock(&hound->list_guard);
239 hound_ctx_t *res = NULL;
240 list_foreach(hound->contexts, link, hound_ctx_t, ctx) {
241 if (hound_ctx_get_id(ctx) == id) {
242 res = ctx;
243 break;
244 }
245 }
246 fibril_mutex_unlock(&hound->list_guard);
247 return res;
248}
249
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 */
257errno_t hound_add_device(hound_t *hound, service_id_t id, const char *name)
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
267 list_foreach(hound->devices, link, audio_device_t, dev) {
268 if (dev->id == id) {
269 log_debug("Device with id %zu is already present", id);
270 return EEXIST;
271 }
272 }
273
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);
277 return EEXIST;
278 }
279
280 dev = malloc(sizeof(audio_device_t));
281 if (!dev) {
282 log_debug("Failed to malloc device structure.");
283 return ENOMEM;
284 }
285
286 const errno_t ret = audio_device_init(dev, id, name);
287 if (ret != EOK) {
288 log_debug("Failed to initialize new audio device: %s",
289 str_error(ret));
290 free(dev);
291 return ret;
292 }
293
294 list_append(&dev->link, &hound->devices);
295 log_info("Added new device: '%s'", dev->name);
296
297 audio_source_t *source = audio_device_get_source(dev);
298 if (source) {
299 const errno_t ret = hound_add_source(hound, source);
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 }
306 log_verbose("Added source: '%s'.", source->name);
307 }
308
309 audio_sink_t *sink = audio_device_get_sink(dev);
310 if (sink) {
311 const errno_t ret = hound_add_sink(hound, sink);
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 }
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
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 */
333errno_t hound_add_source(hound_t *hound, audio_source_t *source)
334{
335 assert(hound);
336 if (!source || !source->name || str_cmp(source->name, "default") == 0) {
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);
344 return EEXIST;
345 }
346 list_append(&source->link, &hound->sources);
347 fibril_mutex_unlock(&hound->list_guard);
348 return EOK;
349}
350
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 */
357errno_t hound_add_sink(hound_t *hound, audio_sink_t *sink)
358{
359 assert(hound);
360 if (!sink || !sink->name || str_cmp(sink->name, "default") == 0) {
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);
368 return EEXIST;
369 }
370 list_append(&sink->link, &hound->sinks);
371 fibril_mutex_unlock(&hound->list_guard);
372 return EOK;
373}
374
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 */
381errno_t hound_remove_source(hound_t *hound, audio_source_t *source)
382{
383 assert(hound);
384 if (!source)
385 return EINVAL;
386 fibril_mutex_lock(&hound->list_guard);
387 hound_remove_source_internal(hound, source);
388 fibril_mutex_unlock(&hound->list_guard);
389 return EOK;
390}
391
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 */
398errno_t hound_remove_sink(hound_t *hound, audio_sink_t *sink)
399{
400 assert(hound);
401 if (!sink)
402 return EINVAL;
403 fibril_mutex_lock(&hound->list_guard);
404 hound_remove_sink_internal(hound, sink);
405 fibril_mutex_unlock(&hound->list_guard);
406 return EOK;
407}
408
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 */
416errno_t hound_list_sources(hound_t *hound, char ***list, size_t *size)
417{
418 assert(hound);
419 if (!list || !size)
420 return EINVAL;
421
422 fibril_mutex_lock(&hound->list_guard);
423 const unsigned long count = list_count(&hound->sources);
424 if (count == 0) {
425 *list = NULL;
426 *size = 0;
427 fibril_mutex_unlock(&hound->list_guard);
428 return EOK;
429 }
430 char **names = calloc(count, sizeof(char *));
431 errno_t ret = names ? EOK : ENOMEM;
432 for (unsigned long i = 0; i < count && ret == EOK; ++i) {
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
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 */
458errno_t hound_list_sinks(hound_t *hound, char ***list, size_t *size)
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 }
472 char **names = calloc(count, sizeof(char *));
473 errno_t ret = names ? EOK : ENOMEM;
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
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 */
504errno_t hound_list_connections(hound_t *hound, const char ***sources,
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
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 */
519errno_t hound_connect(hound_t *hound, const char *source_name, const char *sink_name)
520{
521 assert(hound);
522 log_verbose("Connecting '%s' to '%s'.", source_name, sink_name);
523 fibril_mutex_lock(&hound->list_guard);
524
525 audio_source_t *source =
526 audio_source_list_instance(list_first(&hound->sources));
527 if (str_cmp(source_name, "default") != 0)
528 source = find_source_by_name(&hound->sources, source_name);
529
530 audio_sink_t *sink =
531 audio_sink_list_instance(list_first(&hound->sinks));
532 if (str_cmp(sink_name, "default") != 0)
533 sink = find_sink_by_name(&hound->sinks, sink_name);
534
535 if (!source || !sink) {
536 fibril_mutex_unlock(&hound->list_guard);
537 log_debug("Source (%p), or sink (%p) not found", source, sink);
538 return ENOENT;
539 }
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;
545 }
546 list_append(&conn->hound_link, &hound->connections);
547 fibril_mutex_unlock(&hound->list_guard);
548 return EOK;
549}
550
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 */
558errno_t hound_disconnect(hound_t *hound, const char *source_name, const char *sink_name)
559{
560 assert(hound);
561 fibril_mutex_lock(&hound->list_guard);
562 const errno_t ret = hound_disconnect_internal(hound, source_name, sink_name);
563 fibril_mutex_unlock(&hound->list_guard);
564 return ret;
565}
566
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 */
576static errno_t hound_disconnect_internal(hound_t *hound, const char *source_name,
577 const char *sink_name)
578{
579 assert(hound);
580 assert(fibril_mutex_is_locked(&hound->list_guard));
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) {
587 log_debug("Removing %s -> %s", connection_source_name(conn),
588 connection_sink_name(conn));
589 list_remove(it);
590 connection_destroy(conn);
591 }
592 }
593
594 return EOK;
595}
596/**
597 * @}
598 */
Note: See TracBrowser for help on using the repository browser.