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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c799138 was c799138, checked in by Jan Vesely <jano.vesely@…>, 12 years ago

hound: doxygen and cleanup

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