source: mainline/uspace/lib/hound/src/protocol.c@ eec201d

Last change on this file since eec201d was f5837524, checked in by Jakub Jermar <jakub@…>, 7 years ago

Use user-defined labels instead of phone hashes

This commit changes the way how the async framework maps incomming calls
to connections. Instead of abusing the kernel addresses of attached
phones as identifiers, the IPC_M_CONNECT_TO_ME and IPC_M_CONNECT_ME_TO
messages allow the server to specify an arbitrary label which is
remembered in the connected phone and consequently imprinted on each
call which is routed through this phone.

The async framework uses the address of the connection structure as the
label. This removes the need for a connection hash table because each
incoming call already remembers the connection in its label.

To disambiguate this new label and the other user-defined label used for
answers, the call structure now has the request_label member for the
former and answer_label member for the latter.

This commit also moves the kernel definition of ipc_data_t to abi/ and
removes the uspace redefinition thereof. Finally, when forwarding the
IPC_M_CONNECT_TO_ME call, the phone capability and the kernel object
allocated in request_process are now correctly disposed of.

  • Property mode set to 100644
File size: 19.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/** @addtogroup libhound
30 * @addtogroup audio
31 * @{
32 */
33/** @file
34 * Common USB functions.
35 */
36#include <adt/list.h>
37#include <errno.h>
38#include <loc.h>
39#include <macros.h>
40#include <str.h>
41#include <stdbool.h>
42#include <stdlib.h>
43#include <stdio.h>
44#include <types/common.h>
45
46#include "protocol.h"
47#include "client.h"
48#include "server.h"
49
50enum ipc_methods {
51 /** Create new context representation on the server side */
52 IPC_M_HOUND_CONTEXT_REGISTER = IPC_FIRST_USER_METHOD,
53 /** Release existing context representation on the server side */
54 IPC_M_HOUND_CONTEXT_UNREGISTER,
55 /** Request list of objects */
56 IPC_M_HOUND_GET_LIST,
57 /** Create new connection */
58 IPC_M_HOUND_CONNECT,
59 /** Destroy connection */
60 IPC_M_HOUND_DISCONNECT,
61 /** Switch IPC pipe to stream mode */
62 IPC_M_HOUND_STREAM_ENTER,
63 /** Switch IPC pipe back to general mode */
64 IPC_M_HOUND_STREAM_EXIT,
65 /** Wait until there is no data in the stream */
66 IPC_M_HOUND_STREAM_DRAIN,
67};
68
69/** PCM format conversion helper structure */
70typedef union {
71 struct {
72 uint16_t rate;
73 uint8_t channels;
74 uint8_t format;
75 } __attribute__((packed)) f;
76 sysarg_t arg;
77} format_convert_t;
78
79/*
80 * CLIENT
81 */
82
83/** Well defined service name */
84const char *HOUND_SERVICE = "audio/hound";
85
86/**
87 * Start a new audio session.
88 * @param service Named service typically 'HOUND_SERVICE' constant.
89 * @return Valid session on success, NULL on failure.
90 */
91hound_sess_t *hound_service_connect(const char *service)
92{
93 service_id_t id = 0;
94 const errno_t ret =
95 loc_service_get_id(service, &id, IPC_FLAG_BLOCKING);
96 if (ret != EOK)
97 return NULL;
98 return loc_service_connect(id, INTERFACE_HOUND, IPC_FLAG_BLOCKING);
99}
100
101/**
102 * End an existing audio session.
103 * @param sess The session.
104 */
105void hound_service_disconnect(hound_sess_t *sess)
106{
107 if (sess)
108 async_hangup(sess);
109}
110
111/**
112 * Register a named application context to the audio server.
113 * @param sess Valid audio session.
114 * @param name Valid string identifier
115 * @param record True if the application context wishes to receive data.
116 *
117 * @param[out] id Return context ID.
118 *
119 * @return EOK on success, Error code on failure.
120 */
121errno_t hound_service_register_context(hound_sess_t *sess,
122 const char *name, bool record, hound_context_id_t *id)
123{
124 assert(sess);
125 assert(name);
126 ipc_call_t call;
127 async_exch_t *exch = async_exchange_begin(sess);
128 aid_t mid =
129 async_send_1(exch, IPC_M_HOUND_CONTEXT_REGISTER, record, &call);
130 errno_t ret = mid ? EOK : EPARTY;
131
132 if (ret == EOK)
133 ret = async_data_write_start(exch, name, str_size(name));
134 else
135 async_forget(mid);
136
137 if (ret == EOK)
138 async_wait_for(mid, &ret);
139
140 async_exchange_end(exch);
141 if (ret == EOK) {
142 *id = (hound_context_id_t) IPC_GET_ARG1(call);
143 }
144
145 return ret;
146}
147
148/**
149 * Remove application context from the server's list.
150 * @param sess Valid audio session.
151 * @param id Valid context id.
152 * @return Error code.
153 */
154errno_t hound_service_unregister_context(hound_sess_t *sess,
155 hound_context_id_t id)
156{
157 assert(sess);
158 async_exch_t *exch = async_exchange_begin(sess);
159 const errno_t ret = async_req_1_0(exch, IPC_M_HOUND_CONTEXT_UNREGISTER,
160 CAP_HANDLE_RAW(id));
161 async_exchange_end(exch);
162 return ret;
163}
164
165/**
166 * Retrieve a list of server side actors.
167 * @param[in] sess Valid audio session.
168 * @param[out] ids list of string identifiers.
169 * @param[out] count Number of elements int the @p ids list.
170 * @param[in] flags list requirements.
171 * @param[in] connection name of target actor. Used only if the list should
172 * contain connected actors.
173 * @retval Error code.
174 */
175errno_t hound_service_get_list(hound_sess_t *sess, char ***ids, size_t *count,
176 int flags, const char *connection)
177{
178 assert(sess);
179 assert(ids);
180 assert(count);
181
182 if (connection && !(flags & HOUND_CONNECTED))
183 return EINVAL;
184
185 async_exch_t *exch = async_exchange_begin(sess);
186 if (!exch)
187 return ENOMEM;
188
189 ipc_call_t res_call;
190 aid_t mid = async_send_3(exch, IPC_M_HOUND_GET_LIST, flags, *count,
191 connection != NULL, &res_call);
192
193 errno_t ret = EOK;
194 if (mid && connection)
195 ret = async_data_write_start(exch, connection,
196 str_size(connection));
197
198 if (ret == EOK)
199 async_wait_for(mid, &ret);
200
201 if (ret != EOK) {
202 async_exchange_end(exch);
203 return ret;
204 }
205 unsigned name_count = IPC_GET_ARG1(res_call);
206
207 /* Start receiving names */
208 char **names = NULL;
209 if (name_count) {
210 size_t *sizes = calloc(name_count, sizeof(size_t));
211 names = calloc(name_count, sizeof(char *));
212 if (!names || !sizes)
213 ret = ENOMEM;
214
215 if (ret == EOK)
216 ret = async_data_read_start(exch, sizes,
217 name_count * sizeof(size_t));
218 for (unsigned i = 0; i < name_count && ret == EOK; ++i) {
219 char *name = malloc(sizes[i] + 1);
220 if (name) {
221 memset(name, 0, sizes[i] + 1);
222 ret = async_data_read_start(exch, name, sizes[i]);
223 names[i] = name;
224 } else {
225 ret = ENOMEM;
226 }
227 }
228 free(sizes);
229 }
230 async_exchange_end(exch);
231 if (ret != EOK) {
232 for (unsigned i = 0; i < name_count; ++i)
233 free(names[i]);
234 free(names);
235 } else {
236 *ids = names;
237 *count = name_count;
238 }
239 return ret;
240}
241
242/**
243 * Create a new connection between a source and a sink.
244 * @param sess Valid audio session.
245 * @param source Source name, valid string.
246 * @param sink Sink name, valid string.
247 * @return Error code.
248 */
249errno_t hound_service_connect_source_sink(hound_sess_t *sess, const char *source,
250 const char *sink)
251{
252 assert(sess);
253 assert(source);
254 assert(sink);
255
256 async_exch_t *exch = async_exchange_begin(sess);
257 if (!exch)
258 return ENOMEM;
259 ipc_call_t call;
260 aid_t id = async_send_0(exch, IPC_M_HOUND_CONNECT, &call);
261 errno_t ret = id ? EOK : EPARTY;
262 if (ret == EOK)
263 ret = async_data_write_start(exch, source, str_size(source));
264 if (ret == EOK)
265 ret = async_data_write_start(exch, sink, str_size(sink));
266 async_wait_for(id, &ret);
267 async_exchange_end(exch);
268 return ret;
269}
270
271/**
272 * Destroy an existing connection between a source and a sink.
273 * @param sess Valid audio session.
274 * @param source Source name, valid string.
275 * @param sink Sink name, valid string.
276 * @return Error code.
277 */
278errno_t hound_service_disconnect_source_sink(hound_sess_t *sess, const char *source,
279 const char *sink)
280{
281 assert(sess);
282 async_exch_t *exch = async_exchange_begin(sess);
283 if (!exch)
284 return ENOMEM;
285 ipc_call_t call;
286 aid_t id = async_send_0(exch, IPC_M_HOUND_DISCONNECT, &call);
287 errno_t ret = id ? EOK : EPARTY;
288 if (ret == EOK)
289 ret = async_data_write_start(exch, source, str_size(source));
290 if (ret == EOK)
291 ret = async_data_write_start(exch, sink, str_size(sink));
292 async_wait_for(id, &ret);
293 async_exchange_end(exch);
294 return ENOTSUP;
295}
296
297/**
298 * Switch IPC exchange to a STREAM mode.
299 * @param exch IPC exchange.
300 * @param id context id this stream should be associated with
301 * @param flags set stream properties
302 * @param format format of the new stream.
303 * @param bsize size of the server side buffer.
304 * @return Error code.
305 */
306errno_t hound_service_stream_enter(async_exch_t *exch, hound_context_id_t id,
307 int flags, pcm_format_t format, size_t bsize)
308{
309 const format_convert_t c = {
310 .f = {
311 .channels = format.channels,
312 .rate = format.sampling_rate / 100,
313 .format = format.sample_format,
314 }
315 };
316
317 return async_req_4_0(exch, IPC_M_HOUND_STREAM_ENTER, CAP_HANDLE_RAW(id),
318 flags, c.arg, bsize);
319}
320
321/**
322 * Destroy existing stream and return IPC exchange to general mode.
323 * @param exch IPC exchange.
324 * @return Error code.
325 */
326errno_t hound_service_stream_exit(async_exch_t *exch)
327{
328 return async_req_0_0(exch, IPC_M_HOUND_STREAM_EXIT);
329}
330
331/**
332 * Wait until the server side buffer is empty.
333 * @param exch IPC exchange.
334 * @return Error code.
335 */
336errno_t hound_service_stream_drain(async_exch_t *exch)
337{
338 return async_req_0_0(exch, IPC_M_HOUND_STREAM_DRAIN);
339}
340
341/**
342 * Write audio data to a stream.
343 * @param exch IPC exchange in STREAM MODE.
344 * @param data Audio data buffer.
345 * @size size of the buffer
346 * @return Error code.
347 */
348errno_t hound_service_stream_write(async_exch_t *exch, const void *data, size_t size)
349{
350 return async_data_write_start(exch, data, size);
351}
352
353/**
354 * Read data from a stream.
355 * @param exch IPC exchange in STREAM MODE.
356 * @param data Audio data buffer.
357 * @size size of the buffer
358 * @return Error code.
359 */
360errno_t hound_service_stream_read(async_exch_t *exch, void *data, size_t size)
361{
362 return async_data_read_start(exch, data, size);
363}
364
365/*
366 * SERVER
367 */
368
369static void hound_server_read_data(void *stream);
370static void hound_server_write_data(void *stream);
371static const hound_server_iface_t *server_iface;
372
373/**
374 * Set hound server interface implementation.
375 * @param iface Initialized Hound server interface.
376 */
377void hound_service_set_server_iface(const hound_server_iface_t *iface)
378{
379 server_iface = iface;
380}
381
382/** Server side implementation of the hound protocol. IPC connection handler.
383 *
384 * @param icall Pointer to initial call structure.
385 * @param arg (unused)
386 *
387 */
388void hound_connection_handler(ipc_call_t *icall, void *arg)
389{
390 hound_context_id_t context;
391 errno_t ret;
392 int flags;
393 void *source;
394 void *sink;
395
396 /* Accept connection if there is a valid iface*/
397 if (server_iface) {
398 async_answer_5(icall, EOK, 0, 0, 0, 0, async_get_label());
399 } else {
400 async_answer_0(icall, ENOTSUP);
401 return;
402 }
403
404 while (true) {
405 ipc_call_t call;
406 async_get_call(&call);
407
408 switch (IPC_GET_IMETHOD(call)) {
409 case IPC_M_HOUND_CONTEXT_REGISTER:
410 /* check interface functions */
411 if (!server_iface || !server_iface->add_context) {
412 async_answer_0(&call, ENOTSUP);
413 break;
414 }
415 bool record = IPC_GET_ARG1(call);
416 void *name;
417
418 /* Get context name */
419 ret = async_data_write_accept(&name, true, 0, 0, 0, 0);
420 if (ret != EOK) {
421 async_answer_0(&call, ret);
422 break;
423 }
424
425 context = 0;
426 ret = server_iface->add_context(server_iface->server,
427 &context, name, record);
428 /** new context should create a copy */
429 free(name);
430 if (ret != EOK) {
431 async_answer_0(&call, ret);
432 } else {
433 async_answer_1(&call, EOK, CAP_HANDLE_RAW(context));
434 }
435 break;
436 case IPC_M_HOUND_CONTEXT_UNREGISTER:
437 /* check interface functions */
438 if (!server_iface || !server_iface->rem_context) {
439 async_answer_0(&call, ENOTSUP);
440 break;
441 }
442
443 /* get id, 1st param */
444 context = (hound_context_id_t) IPC_GET_ARG1(call);
445 ret = server_iface->rem_context(server_iface->server,
446 context);
447 async_answer_0(&call, ret);
448 break;
449 case IPC_M_HOUND_GET_LIST:
450 /* check interface functions */
451 if (!server_iface || !server_iface->get_list) {
452 async_answer_0(&call, ENOTSUP);
453 break;
454 }
455
456 char **list = NULL;
457 flags = IPC_GET_ARG1(call);
458 size_t count = IPC_GET_ARG2(call);
459 const bool conn = IPC_GET_ARG3(call);
460 char *conn_name = NULL;
461 ret = EOK;
462
463 /* get connected actor name if provided */
464 if (conn)
465 ret = async_data_write_accept(
466 (void **)&conn_name, true, 0, 0, 0, 0);
467
468 if (ret == EOK)
469 ret = server_iface->get_list(
470 server_iface->server, &list, &count,
471 conn_name, flags);
472 free(conn_name);
473
474 /* Alloc string sizes array */
475 size_t *sizes = NULL;
476 if (count)
477 sizes = calloc(count, sizeof(size_t));
478 if (count && !sizes)
479 ret = ENOMEM;
480 async_answer_1(&call, ret, count);
481
482 /* We are done */
483 if (count == 0 || ret != EOK)
484 break;
485
486 /* Prepare sizes table */
487 for (unsigned i = 0; i < count; ++i)
488 sizes[i] = str_size(list[i]);
489
490 /* Send sizes table */
491 ipc_call_t id;
492 if (async_data_read_receive(&id, NULL)) {
493 ret = async_data_read_finalize(&id, sizes,
494 count * sizeof(size_t));
495 }
496 free(sizes);
497
498 /* Proceed to send names */
499 for (unsigned i = 0; i < count; ++i) {
500 size_t size = str_size(list[i]);
501 ipc_call_t id;
502 if (ret == EOK &&
503 async_data_read_receive(&id, NULL)) {
504 ret = async_data_read_finalize(&id,
505 list[i], size);
506 }
507 free(list[i]);
508 }
509 free(list);
510 break;
511 case IPC_M_HOUND_CONNECT:
512 /* check interface functions */
513 if (!server_iface || !server_iface->connect) {
514 async_answer_0(&call, ENOTSUP);
515 break;
516 }
517
518 source = NULL;
519 sink = NULL;
520
521 /* read source name */
522 ret = async_data_write_accept(&source, true, 0, 0, 0,
523 0);
524 /* read sink name */
525 if (ret == EOK)
526 ret = async_data_write_accept(&sink,
527 true, 0, 0, 0, 0);
528
529 if (ret == EOK)
530 ret = server_iface->connect(
531 server_iface->server, source, sink);
532 free(source);
533 free(sink);
534 async_answer_0(&call, ret);
535 break;
536 case IPC_M_HOUND_DISCONNECT:
537 /* check interface functions */
538 if (!server_iface || !server_iface->disconnect) {
539 async_answer_0(&call, ENOTSUP);
540 break;
541 }
542
543 source = NULL;
544 sink = NULL;
545
546 /* read source name */
547 ret = async_data_write_accept(&source, true, 0, 0, 0,
548 0);
549 /*read sink name */
550 if (ret == EOK)
551 ret = async_data_write_accept(&sink,
552 true, 0, 0, 0, 0);
553 if (ret == EOK)
554 ret = server_iface->connect(
555 server_iface->server, source, sink);
556 free(source);
557 free(sink);
558 async_answer_0(&call, ret);
559 break;
560 case IPC_M_HOUND_STREAM_ENTER:
561 /* check interface functions */
562 if (!server_iface || !server_iface->is_record_context ||
563 !server_iface->add_stream ||
564 !server_iface->rem_stream) {
565 async_answer_0(&call, ENOTSUP);
566 break;
567 }
568
569 /* get parameters */
570 context = (hound_context_id_t) IPC_GET_ARG1(call);
571 flags = IPC_GET_ARG2(call);
572 const format_convert_t c = { .arg = IPC_GET_ARG3(call) };
573 const pcm_format_t f = {
574 .sampling_rate = c.f.rate * 100,
575 .channels = c.f.channels,
576 .sample_format = c.f.format,
577 };
578 size_t bsize = IPC_GET_ARG4(call);
579
580 void *stream;
581 ret = server_iface->add_stream(server_iface->server,
582 context, flags, f, bsize, &stream);
583 if (ret != EOK) {
584 async_answer_0(&call, ret);
585 break;
586 }
587 const bool rec = server_iface->is_record_context(
588 server_iface->server, context);
589 if (rec) {
590 if (server_iface->stream_data_read) {
591 async_answer_0(&call, EOK);
592 /* start answering read calls */
593 hound_server_write_data(stream);
594 server_iface->rem_stream(
595 server_iface->server, stream);
596 } else {
597 async_answer_0(&call, ENOTSUP);
598 }
599 } else {
600 if (server_iface->stream_data_write) {
601 async_answer_0(&call, EOK);
602 /* accept write calls */
603 hound_server_read_data(stream);
604 server_iface->rem_stream(
605 server_iface->server, stream);
606 } else {
607 async_answer_0(&call, ENOTSUP);
608 }
609 }
610 break;
611 case IPC_M_HOUND_STREAM_EXIT:
612 case IPC_M_HOUND_STREAM_DRAIN:
613 /* Stream exit/drain is only allowed in stream context*/
614 async_answer_0(&call, EINVAL);
615 break;
616 default:
617 async_answer_0(&call, ENOTSUP);
618 return;
619 }
620 }
621}
622
623/**
624 * Read data and push it to the stream.
625 * @param stream target stream, will push data there.
626 */
627static void hound_server_read_data(void *stream)
628{
629 ipc_call_t call;
630 size_t size = 0;
631 errno_t ret_answer = EOK;
632
633 /* accept data write or drain */
634 while (async_data_write_receive(&call, &size) ||
635 (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN)) {
636 /* check drain first */
637 if (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN) {
638 errno_t ret = ENOTSUP;
639 if (server_iface->drain_stream)
640 ret = server_iface->drain_stream(stream);
641 async_answer_0(&call, ret);
642 continue;
643 }
644
645 /* there was an error last time */
646 if (ret_answer != EOK) {
647 async_answer_0(&call, ret_answer);
648 continue;
649 }
650
651 char *buffer = malloc(size);
652 if (!buffer) {
653 async_answer_0(&call, ENOMEM);
654 continue;
655 }
656 const errno_t ret = async_data_write_finalize(&call, buffer, size);
657 if (ret == EOK) {
658 /* push data to stream */
659 ret_answer = server_iface->stream_data_write(
660 stream, buffer, size);
661 }
662 }
663 const errno_t ret = IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_EXIT ?
664 EOK : EINVAL;
665
666 async_answer_0(&call, ret);
667}
668
669/**
670 * Accept reads and pull data from the stream.
671 * @param stream target stream, will pull data from there.
672 */
673static void hound_server_write_data(void *stream)
674{
675
676 ipc_call_t call;
677 size_t size = 0;
678 errno_t ret_answer = EOK;
679
680 /* accept data read and drain */
681 while (async_data_read_receive(&call, &size) ||
682 (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN)) {
683 /* drain does not make much sense but it is allowed */
684 if (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN) {
685 errno_t ret = ENOTSUP;
686 if (server_iface->drain_stream)
687 ret = server_iface->drain_stream(stream);
688 async_answer_0(&call, ret);
689 continue;
690 }
691 /* there was an error last time */
692 if (ret_answer != EOK) {
693 async_answer_0(&call, ret_answer);
694 continue;
695 }
696 char *buffer = malloc(size);
697 if (!buffer) {
698 async_answer_0(&call, ENOMEM);
699 continue;
700 }
701 errno_t ret = server_iface->stream_data_read(stream, buffer, size);
702 if (ret == EOK) {
703 ret_answer =
704 async_data_read_finalize(&call, buffer, size);
705 }
706 }
707 const errno_t ret = IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_EXIT ?
708 EOK : EINVAL;
709
710 async_answer_0(&call, ret);
711}
712
713/*
714 * SERVER SIDE
715 */
716
717/**
718 * Register new hound service to the location service.
719 * @param[in] name server name
720 * @param[out] id assigned service id.
721 * @return Error code.
722 */
723errno_t hound_server_register(const char *name, service_id_t *id)
724{
725 if (!name || !id)
726 return EINVAL;
727
728 errno_t ret = loc_server_register(name);
729 if (ret != EOK)
730 return ret;
731
732 return loc_service_register(HOUND_SERVICE, id);
733}
734
735/**
736 * Unregister server from the location service.
737 * @param id previously assigned service id.
738 */
739void hound_server_unregister(service_id_t id)
740{
741 loc_service_unregister(id);
742}
743
744/**
745 * Set callback on device category change event.
746 * @param cb Callback function.
747 * @return Error code.
748 */
749errno_t hound_server_set_device_change_callback(dev_change_callback_t cb,
750 void *arg)
751{
752 return loc_register_cat_change_cb(cb, arg);
753}
754
755/**
756 * Walk through all device in the audio-pcm category.
757 * @param callback Function to call on every device.
758 * @return Error code.
759 */
760errno_t hound_server_devices_iterate(device_callback_t callback)
761{
762 if (!callback)
763 return EINVAL;
764 static bool resolved = false;
765 static category_id_t cat_id = 0;
766
767 if (!resolved) {
768 const errno_t ret = loc_category_get_id("audio-pcm", &cat_id,
769 IPC_FLAG_BLOCKING);
770 if (ret != EOK)
771 return ret;
772 resolved = true;
773 }
774
775 service_id_t *svcs = NULL;
776 size_t count = 0;
777 const errno_t ret = loc_category_get_svcs(cat_id, &svcs, &count);
778 if (ret != EOK)
779 return ret;
780
781 for (unsigned i = 0; i < count; ++i) {
782 char *name = NULL;
783 loc_service_get_name(svcs[i], &name);
784 callback(svcs[i], name);
785 free(name);
786 }
787 free(svcs);
788 return EOK;
789}
790/**
791 * @}
792 */
Note: See TracBrowser for help on using the repository browser.