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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since eed4139 was eed4139, checked in by Jakub Jermar <jakub@…>, 7 years ago

Fix some comments mentioning 'call IDs'

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