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

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

Mechanically lowercase IPC_SET_*/IPC_GET_*

  • 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>
[76d0981d]41#include <stdbool.h>
[afa7c17]42#include <stdlib.h>
[89d3946f]43#include <stdio.h>
[cf13b17]44#include <types/common.h>
[afa7c17]45
[059490c2]46#include "protocol.h"
[afa7c17]47#include "client.h"
48#include "server.h"
49
[504f1ea3]50enum ipc_methods {
[876f5561]51 /** Create new context representation on the server side */
[504f1ea3]52 IPC_M_HOUND_CONTEXT_REGISTER = IPC_FIRST_USER_METHOD,
[876f5561]53 /** Release existing context representation on the server side */
[504f1ea3]54 IPC_M_HOUND_CONTEXT_UNREGISTER,
[876f5561]55 /** Request list of objects */
[13318d1]56 IPC_M_HOUND_GET_LIST,
[876f5561]57 /** Create new connection */
[13318d1]58 IPC_M_HOUND_CONNECT,
[876f5561]59 /** Destroy connection */
[13318d1]60 IPC_M_HOUND_DISCONNECT,
[876f5561]61 /** Switch IPC pipe to stream mode */
[b66d43b]62 IPC_M_HOUND_STREAM_ENTER,
[876f5561]63 /** Switch IPC pipe back to general mode */
[b66d43b]64 IPC_M_HOUND_STREAM_EXIT,
[876f5561]65 /** Wait until there is no data in the stream */
[504f1ea3]66 IPC_M_HOUND_STREAM_DRAIN,
67};
68
[876f5561]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
[904b1bc]79/*
[b66d43b]80 * CLIENT
[904b1bc]81 */
[b66d43b]82
[876f5561]83/** Well defined service name */
[059490c2]84const char *HOUND_SERVICE = "audio/hound";
85
[876f5561]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 */
[059490c2]91hound_sess_t *hound_service_connect(const char *service)
92{
93 service_id_t id = 0;
[b7fd2a0]94 const errno_t ret =
[059490c2]95 loc_service_get_id(service, &id, IPC_FLAG_BLOCKING);
96 if (ret != EOK)
97 return NULL;
[f9b2cb4c]98 return loc_service_connect(id, INTERFACE_HOUND, IPC_FLAG_BLOCKING);
[059490c2]99}
100
[876f5561]101/**
102 * End an existing audio session.
103 * @param sess The session.
104 */
[059490c2]105void hound_service_disconnect(hound_sess_t *sess)
106{
107 if (sess)
108 async_hangup(sess);
109}
[afa7c17]110
[876f5561]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.
[fed5a9b]116 *
117 * @param[out] id Return context ID.
118 *
119 * @return EOK on success, Error code on failure.
[876f5561]120 */
[b7fd2a0]121errno_t hound_service_register_context(hound_sess_t *sess,
[fed5a9b]122 const char *name, bool record, hound_context_id_t *id)
[504f1ea3]123{
124 assert(sess);
125 assert(name);
[e6e5f4e]126 ipc_call_t call;
[504f1ea3]127 async_exch_t *exch = async_exchange_begin(sess);
[e6e5f4e]128 aid_t mid =
129 async_send_1(exch, IPC_M_HOUND_CONTEXT_REGISTER, record, &call);
[b7fd2a0]130 errno_t ret = mid ? EOK : EPARTY;
[e6e5f4e]131
[b66d43b]132 if (ret == EOK)
133 ret = async_data_write_start(exch, name, str_size(name));
[e6e5f4e]134 else
135 async_forget(mid);
136
137 if (ret == EOK)
[fed5a9b]138 async_wait_for(mid, &ret);
[e6e5f4e]139
[504f1ea3]140 async_exchange_end(exch);
[fed5a9b]141 if (ret == EOK) {
[fafb8e5]142 *id = (hound_context_id_t) ipc_get_arg1(&call);
[fed5a9b]143 }
144
145 return ret;
[504f1ea3]146}
147
[876f5561]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 */
[eadaeae8]154errno_t hound_service_unregister_context(hound_sess_t *sess,
155 hound_context_id_t id)
[504f1ea3]156{
157 assert(sess);
158 async_exch_t *exch = async_exchange_begin(sess);
[eadaeae8]159 const errno_t ret = async_req_1_0(exch, IPC_M_HOUND_CONTEXT_UNREGISTER,
[bb97118]160 cap_handle_raw(id));
[504f1ea3]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 }
[fafb8e5]205 unsigned name_count = ipc_get_arg1(&res_call);
[13318d1]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{
[904b1bc]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
[bb97118]317 return async_req_4_0(exch, IPC_M_HOUND_STREAM_ENTER, cap_handle_raw(id),
[eadaeae8]318 flags, c.arg, bsize);
[504f1ea3]319}
320
[876f5561]321/**
322 * Destroy existing stream and return IPC exchange to general mode.
323 * @param exch IPC exchange.
324 * @return Error code.
325 */
[b7fd2a0]326errno_t hound_service_stream_exit(async_exch_t *exch)
[504f1ea3]327{
[b66d43b]328 return async_req_0_0(exch, IPC_M_HOUND_STREAM_EXIT);
[504f1ea3]329}
330
[876f5561]331/**
332 * Wait until the server side buffer is empty.
333 * @param exch IPC exchange.
334 * @return Error code.
335 */
[b7fd2a0]336errno_t hound_service_stream_drain(async_exch_t *exch)
[504f1ea3]337{
[fd7c98b]338 return async_req_0_0(exch, IPC_M_HOUND_STREAM_DRAIN);
[504f1ea3]339}
340
[876f5561]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 */
[b7fd2a0]348errno_t hound_service_stream_write(async_exch_t *exch, const void *data, size_t size)
[504f1ea3]349{
[b66d43b]350 return async_data_write_start(exch, data, size);
[504f1ea3]351}
352
[876f5561]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 */
[b7fd2a0]360errno_t hound_service_stream_read(async_exch_t *exch, void *data, size_t size)
[504f1ea3]361{
[fd7c98b]362 return async_data_read_start(exch, data, size);
[504f1ea3]363}
364
[904b1bc]365/*
[b66d43b]366 * SERVER
[904b1bc]367 */
[b66d43b]368
[5bf4310]369static void hound_server_read_data(void *stream);
370static void hound_server_write_data(void *stream);
[939871a]371static const hound_server_iface_t *server_iface;
[b66d43b]372
[876f5561]373/**
374 * Set hound server interface implementation.
375 * @param iface Initialized Hound server interface.
376 */
[e6e5f4e]377void hound_service_set_server_iface(const hound_server_iface_t *iface)
[b66d43b]378{
379 server_iface = iface;
380}
381
[eed4139]382/** Server side implementation of the hound protocol. IPC connection handler.
383 *
[984a9ba]384 * @param icall Pointer to initial call structure.
385 * @param arg (unused)
386 *
[876f5561]387 */
[984a9ba]388void hound_connection_handler(ipc_call_t *icall, void *arg)
[b66d43b]389{
[984a9ba]390 hound_context_id_t context;
[84239b1]391 errno_t ret;
392 int flags;
393 void *source;
394 void *sink;
395
[b66d43b]396 /* Accept connection if there is a valid iface*/
397 if (server_iface) {
[beb83c1]398 async_accept_0(icall);
[b66d43b]399 } else {
[984a9ba]400 async_answer_0(icall, ENOTSUP);
[b66d43b]401 return;
402 }
403
[76d0981d]404 while (true) {
[b66d43b]405 ipc_call_t call;
[984a9ba]406 async_get_call(&call);
407
[fafb8e5]408 switch (ipc_get_imethod(&call)) {
[84239b1]409 case IPC_M_HOUND_CONTEXT_REGISTER:
[876f5561]410 /* check interface functions */
[b66d43b]411 if (!server_iface || !server_iface->add_context) {
[984a9ba]412 async_answer_0(&call, ENOTSUP);
[b66d43b]413 break;
414 }
[fafb8e5]415 bool record = ipc_get_arg1(&call);
[b66d43b]416 void *name;
[876f5561]417
418 /* Get context name */
[84239b1]419 ret = async_data_write_accept(&name, true, 0, 0, 0, 0);
[b66d43b]420 if (ret != EOK) {
[984a9ba]421 async_answer_0(&call, ret);
[b66d43b]422 break;
423 }
[84239b1]424
[984a9ba]425 context = 0;
[b66d43b]426 ret = server_iface->add_context(server_iface->server,
[984a9ba]427 &context, name, record);
[876f5561]428 /** new context should create a copy */
429 free(name);
[b66d43b]430 if (ret != EOK) {
[984a9ba]431 async_answer_0(&call, ret);
[e6e5f4e]432 } else {
[bb97118]433 async_answer_1(&call, EOK, cap_handle_raw(context));
[b66d43b]434 }
[e6e5f4e]435 break;
[84239b1]436 case IPC_M_HOUND_CONTEXT_UNREGISTER:
[876f5561]437 /* check interface functions */
[a4165561]438 if (!server_iface || !server_iface->rem_context) {
[984a9ba]439 async_answer_0(&call, ENOTSUP);
[a4165561]440 break;
441 }
[876f5561]442
443 /* get id, 1st param */
[fafb8e5]444 context = (hound_context_id_t) ipc_get_arg1(&call);
[84239b1]445 ret = server_iface->rem_context(server_iface->server,
[984a9ba]446 context);
447 async_answer_0(&call, ret);
[6ec1d48]448 break;
[84239b1]449 case IPC_M_HOUND_GET_LIST:
[876f5561]450 /* check interface functions */
[6ec1d48]451 if (!server_iface || !server_iface->get_list) {
[984a9ba]452 async_answer_0(&call, ENOTSUP);
[6ec1d48]453 break;
454 }
[876f5561]455
[33b8d024]456 char **list = NULL;
[fafb8e5]457 flags = ipc_get_arg1(&call);
458 size_t count = ipc_get_arg2(&call);
459 const bool conn = ipc_get_arg3(&call);
[6ec1d48]460 char *conn_name = NULL;
[84239b1]461 ret = EOK;
[876f5561]462
463 /* get connected actor name if provided */
[6ec1d48]464 if (conn)
465 ret = async_data_write_accept(
[904b1bc]466 (void **)&conn_name, true, 0, 0, 0, 0);
[6ec1d48]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);
[876f5561]473
474 /* Alloc string sizes array */
[6ec1d48]475 size_t *sizes = NULL;
476 if (count)
477 sizes = calloc(count, sizeof(size_t));
478 if (count && !sizes)
479 ret = ENOMEM;
[984a9ba]480 async_answer_1(&call, ret, count);
[6ec1d48]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 */
[984a9ba]491 ipc_call_t id;
[6ec1d48]492 if (async_data_read_receive(&id, NULL)) {
[984a9ba]493 ret = async_data_read_finalize(&id, sizes,
[6ec1d48]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]);
[984a9ba]501 ipc_call_t id;
[6ec1d48]502 if (ret == EOK &&
503 async_data_read_receive(&id, NULL)) {
[984a9ba]504 ret = async_data_read_finalize(&id,
[6ec1d48]505 list[i], size);
506 }
507 free(list[i]);
508 }
509 free(list);
510 break;
[84239b1]511 case IPC_M_HOUND_CONNECT:
[876f5561]512 /* check interface functions */
[6ec1d48]513 if (!server_iface || !server_iface->connect) {
[984a9ba]514 async_answer_0(&call, ENOTSUP);
[6ec1d48]515 break;
516 }
[876f5561]517
[84239b1]518 source = NULL;
519 sink = NULL;
[876f5561]520
521 /* read source name */
[84239b1]522 ret = async_data_write_accept(&source, true, 0, 0, 0,
523 0);
[876f5561]524 /* read sink name */
[6ec1d48]525 if (ret == EOK)
526 ret = async_data_write_accept(&sink,
527 true, 0, 0, 0, 0);
[876f5561]528
[6ec1d48]529 if (ret == EOK)
530 ret = server_iface->connect(
531 server_iface->server, source, sink);
532 free(source);
533 free(sink);
[984a9ba]534 async_answer_0(&call, ret);
[6ec1d48]535 break;
[84239b1]536 case IPC_M_HOUND_DISCONNECT:
[876f5561]537 /* check interface functions */
[6ec1d48]538 if (!server_iface || !server_iface->disconnect) {
[984a9ba]539 async_answer_0(&call, ENOTSUP);
[6ec1d48]540 break;
541 }
[876f5561]542
[84239b1]543 source = NULL;
544 sink = NULL;
[876f5561]545
546 /* read source name */
[84239b1]547 ret = async_data_write_accept(&source, true, 0, 0, 0,
548 0);
[876f5561]549 /*read sink name */
[6ec1d48]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);
[984a9ba]558 async_answer_0(&call, ret);
[6ec1d48]559 break;
[84239b1]560 case IPC_M_HOUND_STREAM_ENTER:
[876f5561]561 /* check interface functions */
[904b1bc]562 if (!server_iface || !server_iface->is_record_context ||
563 !server_iface->add_stream ||
564 !server_iface->rem_stream) {
[984a9ba]565 async_answer_0(&call, ENOTSUP);
[b66d43b]566 break;
567 }
568
[876f5561]569 /* get parameters */
[fafb8e5]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) };
[6133470]573 const pcm_format_t f = {
[904b1bc]574 .sampling_rate = c.f.rate * 100,
575 .channels = c.f.channels,
576 .sample_format = c.f.format,
[6133470]577 };
[fafb8e5]578 size_t bsize = ipc_get_arg4(&call);
[876f5561]579
[b66d43b]580 void *stream;
[84239b1]581 ret = server_iface->add_stream(server_iface->server,
[984a9ba]582 context, flags, f, bsize, &stream);
[b66d43b]583 if (ret != EOK) {
[984a9ba]584 async_answer_0(&call, ret);
[b66d43b]585 break;
586 }
[37ea333]587 const bool rec = server_iface->is_record_context(
[984a9ba]588 server_iface->server, context);
[37ea333]589 if (rec) {
[904b1bc]590 if (server_iface->stream_data_read) {
[984a9ba]591 async_answer_0(&call, EOK);
[876f5561]592 /* start answering read calls */
[5bf4310]593 hound_server_write_data(stream);
[cc3c27ad]594 server_iface->rem_stream(
595 server_iface->server, stream);
[5bf4310]596 } else {
[984a9ba]597 async_answer_0(&call, ENOTSUP);
[5bf4310]598 }
[37ea333]599 } else {
[5bf4310]600 if (server_iface->stream_data_write) {
[984a9ba]601 async_answer_0(&call, EOK);
[876f5561]602 /* accept write calls */
[5bf4310]603 hound_server_read_data(stream);
[cc3c27ad]604 server_iface->rem_stream(
605 server_iface->server, stream);
[5bf4310]606 } else {
[984a9ba]607 async_answer_0(&call, ENOTSUP);
[5bf4310]608 }
[37ea333]609 }
[b66d43b]610 break;
611 case IPC_M_HOUND_STREAM_EXIT:
[a4165561]612 case IPC_M_HOUND_STREAM_DRAIN:
613 /* Stream exit/drain is only allowed in stream context*/
[984a9ba]614 async_answer_0(&call, EINVAL);
[939871a]615 break;
[b66d43b]616 default:
[1b555f0f]617 /*
618 * In case we called async_get_call() after we had
619 * already received IPC_M_PHONE_HUNGUP deeper in the
620 * protocol handling, the capability handle will be
621 * invalid, so act carefully here.
622 */
623 if (call.cap_handle != CAP_NIL)
624 async_answer_0(&call, ENOTSUP);
[b66d43b]625 return;
626 }
627 }
628}
629
[876f5561]630/**
631 * Read data and push it to the stream.
632 * @param stream target stream, will push data there.
633 */
[5bf4310]634static void hound_server_read_data(void *stream)
[b66d43b]635{
[71780e0]636 ipc_call_t call;
[b66d43b]637 size_t size = 0;
[b7fd2a0]638 errno_t ret_answer = EOK;
[984a9ba]639
[876f5561]640 /* accept data write or drain */
[984a9ba]641 while (async_data_write_receive(&call, &size) ||
[fafb8e5]642 (ipc_get_imethod(&call) == IPC_M_HOUND_STREAM_DRAIN)) {
[876f5561]643 /* check drain first */
[fafb8e5]644 if (ipc_get_imethod(&call) == IPC_M_HOUND_STREAM_DRAIN) {
[b7fd2a0]645 errno_t ret = ENOTSUP;
[704baed]646 if (server_iface->drain_stream)
647 ret = server_iface->drain_stream(stream);
[984a9ba]648 async_answer_0(&call, ret);
[704baed]649 continue;
650 }
[876f5561]651
652 /* there was an error last time */
[6d74977]653 if (ret_answer != EOK) {
[984a9ba]654 async_answer_0(&call, ret_answer);
[6d74977]655 continue;
656 }
[876f5561]657
[b66d43b]658 char *buffer = malloc(size);
659 if (!buffer) {
[984a9ba]660 async_answer_0(&call, ENOMEM);
[b66d43b]661 continue;
662 }
[984a9ba]663 const errno_t ret = async_data_write_finalize(&call, buffer, size);
[b66d43b]664 if (ret == EOK) {
[876f5561]665 /* push data to stream */
[6d74977]666 ret_answer = server_iface->stream_data_write(
[704baed]667 stream, buffer, size);
[b66d43b]668 }
669 }
[fafb8e5]670 const errno_t ret = ipc_get_imethod(&call) == IPC_M_HOUND_STREAM_EXIT ?
[904b1bc]671 EOK : EINVAL;
[71780e0]672
[984a9ba]673 async_answer_0(&call, ret);
[b66d43b]674}
[504f1ea3]675
[876f5561]676/**
677 * Accept reads and pull data from the stream.
678 * @param stream target stream, will pull data from there.
679 */
[5bf4310]680static void hound_server_write_data(void *stream)
[37ea333]681{
682
683 ipc_call_t call;
684 size_t size = 0;
[b7fd2a0]685 errno_t ret_answer = EOK;
[984a9ba]686
[876f5561]687 /* accept data read and drain */
[984a9ba]688 while (async_data_read_receive(&call, &size) ||
[fafb8e5]689 (ipc_get_imethod(&call) == IPC_M_HOUND_STREAM_DRAIN)) {
[876f5561]690 /* drain does not make much sense but it is allowed */
[fafb8e5]691 if (ipc_get_imethod(&call) == IPC_M_HOUND_STREAM_DRAIN) {
[b7fd2a0]692 errno_t ret = ENOTSUP;
[9e40d443]693 if (server_iface->drain_stream)
694 ret = server_iface->drain_stream(stream);
[984a9ba]695 async_answer_0(&call, ret);
[9e40d443]696 continue;
697 }
[876f5561]698 /* there was an error last time */
[6d74977]699 if (ret_answer != EOK) {
[984a9ba]700 async_answer_0(&call, ret_answer);
[6d74977]701 continue;
702 }
[37ea333]703 char *buffer = malloc(size);
704 if (!buffer) {
[984a9ba]705 async_answer_0(&call, ENOMEM);
[37ea333]706 continue;
707 }
[b7fd2a0]708 errno_t ret = server_iface->stream_data_read(stream, buffer, size);
[37ea333]709 if (ret == EOK) {
[6d74977]710 ret_answer =
[984a9ba]711 async_data_read_finalize(&call, buffer, size);
[37ea333]712 }
713 }
[fafb8e5]714 const errno_t ret = ipc_get_imethod(&call) == IPC_M_HOUND_STREAM_EXIT ?
[904b1bc]715 EOK : EINVAL;
[37ea333]716
[984a9ba]717 async_answer_0(&call, ret);
[37ea333]718}
719
[904b1bc]720/*
[7e7def5]721 * SERVER SIDE
[904b1bc]722 */
[b66d43b]723
[876f5561]724/**
725 * Register new hound service to the location service.
726 * @param[in] name server name
727 * @param[out] id assigned service id.
728 * @return Error code.
729 */
[b7fd2a0]730errno_t hound_server_register(const char *name, service_id_t *id)
[afa7c17]731{
732 if (!name || !id)
733 return EINVAL;
734
[b7fd2a0]735 errno_t ret = loc_server_register(name);
[afa7c17]736 if (ret != EOK)
737 return ret;
738
739 return loc_service_register(HOUND_SERVICE, id);
740}
741
[876f5561]742/**
743 * Unregister server from the location service.
744 * @param id previously assigned service id.
745 */
[afa7c17]746void hound_server_unregister(service_id_t id)
747{
748 loc_service_unregister(id);
749}
750
[876f5561]751/**
752 * Set callback on device category change event.
753 * @param cb Callback function.
754 * @return Error code.
755 */
[e89a06a]756errno_t hound_server_set_device_change_callback(dev_change_callback_t cb,
757 void *arg)
[afa7c17]758{
[e89a06a]759 return loc_register_cat_change_cb(cb, arg);
[afa7c17]760}
761
[876f5561]762/**
763 * Walk through all device in the audio-pcm category.
764 * @param callback Function to call on every device.
765 * @return Error code.
766 */
[b7fd2a0]767errno_t hound_server_devices_iterate(device_callback_t callback)
[afa7c17]768{
769 if (!callback)
770 return EINVAL;
771 static bool resolved = false;
772 static category_id_t cat_id = 0;
773
774 if (!resolved) {
[b7fd2a0]775 const errno_t ret = loc_category_get_id("audio-pcm", &cat_id,
[afa7c17]776 IPC_FLAG_BLOCKING);
777 if (ret != EOK)
778 return ret;
779 resolved = true;
780 }
781
782 service_id_t *svcs = NULL;
783 size_t count = 0;
[b7fd2a0]784 const errno_t ret = loc_category_get_svcs(cat_id, &svcs, &count);
[afa7c17]785 if (ret != EOK)
786 return ret;
787
788 for (unsigned i = 0; i < count; ++i) {
789 char *name = NULL;
790 loc_service_get_name(svcs[i], &name);
791 callback(svcs[i], name);
792 free(name);
793 }
794 free(svcs);
795 return EOK;
796}
797/**
798 * @}
799 */
Note: See TracBrowser for help on using the repository browser.