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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 84239b1 was 84239b1, checked in by Jiri Svoboda <jiri@…>, 7 years ago

And there was much fixing.

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