source: mainline/uspace/srv/audio/hound/hound.c@ 338d54a7

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 338d54a7 was 33b8d024, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 8 years ago

Remove const qualifier from the argument of free() and realloc(),
as well as in numerous other variables that hold ownership of memory.

By convention, a pointer that holds ownership is _never_ qualified by const.
This is reflected in the standard type signature of free() and realloc().
Allowing const pointers to hold ownership may seem superficially convenient,
but is actually quite confusing to experienced C programmers.

  • 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.