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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b3cf946 was d120133, checked in by Jan Vesely <jano.vesely@…>, 12 years ago

Use memset instead of bzero

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