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

Last change on this file since ca48672 was ca48672, checked in by Jiri Svoboda <jiri@…>, 8 days ago

loc_service_register() needs to take a port ID argument.

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