source: mainline/uspace/srv/audio/hound/hound.c@ 35b7d86e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 35b7d86e was 8a637a4, checked in by Martin Decky <martin@…>, 10 years ago

remove EEXISTS in favor of EEXIST

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