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

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

libhound: implement stream drain for playback streams

  • Property mode set to 100644
File size: 22.0 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 {
50 IPC_M_HOUND_CONTEXT_REGISTER = IPC_FIRST_USER_METHOD,
51 IPC_M_HOUND_CONTEXT_UNREGISTER,
[13318d1]52 IPC_M_HOUND_GET_LIST,
53 IPC_M_HOUND_CONNECT,
54 IPC_M_HOUND_DISCONNECT,
[b66d43b]55 IPC_M_HOUND_STREAM_ENTER,
56 IPC_M_HOUND_STREAM_EXIT,
[504f1ea3]57 IPC_M_HOUND_STREAM_DRAIN,
58};
59
[6133470]60typedef union {
61 struct {
62 uint16_t rate;
63 uint8_t channels;
64 uint8_t format;
65 } f __attribute__((packed));
66 sysarg_t arg;
67} format_convert_t;
68
69
[b66d43b]70/****
71 * CLIENT
72 ****/
73
[059490c2]74const char *HOUND_SERVICE = "audio/hound";
75
76hound_sess_t *hound_service_connect(const char *service)
77{
78 service_id_t id = 0;
79 const int ret =
80 loc_service_get_id(service, &id, IPC_FLAG_BLOCKING);
81 if (ret != EOK)
82 return NULL;
83 return loc_service_connect(EXCHANGE_PARALLEL, id, IPC_FLAG_BLOCKING);
84}
85
86void hound_service_disconnect(hound_sess_t *sess)
87{
88 if (sess)
89 async_hangup(sess);
90}
[afa7c17]91
[504f1ea3]92hound_context_id_t hound_service_register_context(hound_sess_t *sess,
[b66d43b]93 const char *name, bool record)
[504f1ea3]94{
95 assert(sess);
96 assert(name);
[e6e5f4e]97 ipc_call_t call;
[504f1ea3]98 async_exch_t *exch = async_exchange_begin(sess);
[e6e5f4e]99 aid_t mid =
100 async_send_1(exch, IPC_M_HOUND_CONTEXT_REGISTER, record, &call);
101 int ret = mid ? EOK : EPARTY;
102
[b66d43b]103 if (ret == EOK)
104 ret = async_data_write_start(exch, name, str_size(name));
[e6e5f4e]105 else
106 async_forget(mid);
107
108 if (ret == EOK)
109 async_wait_for(mid, (sysarg_t *)&ret);
110
[504f1ea3]111 async_exchange_end(exch);
[e6e5f4e]112 return ret == EOK ? (hound_context_id_t)IPC_GET_ARG1(call) : ret;
[504f1ea3]113}
114
115int hound_service_unregister_context(hound_sess_t *sess, hound_context_id_t id)
116{
117 assert(sess);
118 async_exch_t *exch = async_exchange_begin(sess);
119 const int ret =
120 async_req_1_0(exch, IPC_M_HOUND_CONTEXT_UNREGISTER, id);
121 async_exchange_end(exch);
122 return ret;
123}
124
[13318d1]125int hound_service_get_list(hound_sess_t *sess, const char ***ids, size_t *count,
126 int flags, const char *connection)
127{
128 assert(sess);
129 assert(ids);
130 assert(count);
131
132 if (connection && !(flags & HOUND_CONNECTED))
133 return EINVAL;
134
135 async_exch_t *exch = async_exchange_begin(sess);
136 if (!exch)
137 return ENOMEM;
138
139 ipc_call_t res_call;
140 aid_t mid = async_send_3(exch, IPC_M_HOUND_GET_LIST, flags, *count,
141 (bool)connection, &res_call);
142
143 int ret = EOK;
144 if (mid && connection)
145 ret = async_data_write_start(exch, connection,
146 str_size(connection));
147
148 if (ret == EOK)
149 async_wait_for(mid, (sysarg_t*)&ret);
150
151 if (ret != EOK) {
152 async_exchange_end(exch);
153 return ret;
154 }
155 unsigned name_count = IPC_GET_ARG1(res_call);
156
157 /* Start receiving names */
[6ec1d48]158 const char **names = NULL;
[13318d1]159 if (name_count) {
[6ec1d48]160 size_t *sizes = calloc(name_count, sizeof(size_t));
[13318d1]161 names = calloc(name_count, sizeof(char *));
[6ec1d48]162 if (!names || !sizes)
163 ret = ENOMEM;
164
165 if (ret == EOK)
166 ret = async_data_read_start(exch, sizes,
167 name_count * sizeof(size_t));
168 for (unsigned i = 0; i < name_count && ret == EOK; ++i) {
169 char *name = malloc(sizes[i] + 1);
170 if (name) {
[35ab943]171 bzero(name, sizes[i] + 1);
[6ec1d48]172 ret = async_data_read_start(exch, name, sizes[i]);
173 names[i] = name;
174 } else {
175 ret = ENOMEM;
[13318d1]176 }
177 }
[6ec1d48]178 free(sizes);
[13318d1]179 }
180 async_exchange_end(exch);
181 if (ret != EOK) {
182 for (unsigned i = 0; i < name_count; ++i)
183 free(names[i]);
184 free(names);
185 } else {
186 *ids = names;
187 *count = name_count;
188 }
189 return ret;
190}
191
192int hound_service_connect_source_sink(hound_sess_t *sess, const char *source,
193 const char *sink)
194{
195 assert(sess);
196 assert(source);
197 assert(sink);
198
199 async_exch_t *exch = async_exchange_begin(sess);
200 if (!exch)
201 return ENOMEM;
[6ec1d48]202 ipc_call_t call;
203 aid_t id = async_send_0(exch, IPC_M_HOUND_CONNECT, &call);
204 int ret = id ? EOK : EPARTY;
[13318d1]205 if (ret == EOK)
206 ret = async_data_write_start(exch, source, str_size(source));
207 if (ret == EOK)
208 ret = async_data_write_start(exch, sink, str_size(sink));
[6ec1d48]209 async_wait_for(id, (sysarg_t*)&ret);
[13318d1]210 async_exchange_end(exch);
211 return ret;
212}
213
214int hound_service_disconnect_source_sink(hound_sess_t *sess, const char *source,
215 const char *sink)
216{
217 assert(sess);
218 async_exch_t *exch = async_exchange_begin(sess);
219 if (!exch)
220 return ENOMEM;
[6ec1d48]221 ipc_call_t call;
222 aid_t id = async_send_0(exch, IPC_M_HOUND_DISCONNECT, &call);
223 int ret = id ? EOK : EPARTY;
[13318d1]224 if (ret == EOK)
225 ret = async_data_write_start(exch, source, str_size(source));
226 if (ret == EOK)
227 ret = async_data_write_start(exch, sink, str_size(sink));
[6ec1d48]228 async_wait_for(id, (sysarg_t*)&ret);
[13318d1]229 async_exchange_end(exch);
230 return ENOTSUP;
231}
232
[504f1ea3]233int hound_service_stream_enter(async_exch_t *exch, hound_context_id_t id,
234 int flags, pcm_format_t format, size_t bsize)
235{
[6133470]236 const format_convert_t c = { .f = {
237 .channels = format.channels,
238 .rate = format.sampling_rate / 100,
239 .format = format.sample_format,
240 }};
[b66d43b]241 return async_req_4_0(exch, IPC_M_HOUND_STREAM_ENTER, id, flags,
[6133470]242 c.arg, bsize);
[504f1ea3]243}
244
245int hound_service_stream_exit(async_exch_t *exch)
246{
[b66d43b]247 return async_req_0_0(exch, IPC_M_HOUND_STREAM_EXIT);
[504f1ea3]248}
249
250int hound_service_stream_drain(async_exch_t *exch)
251{
[fd7c98b]252 return async_req_0_0(exch, IPC_M_HOUND_STREAM_DRAIN);
[504f1ea3]253}
254
255int hound_service_stream_write(async_exch_t *exch, const void *data, size_t size)
256{
[b66d43b]257 return async_data_write_start(exch, data, size);
[504f1ea3]258}
259
260int hound_service_stream_read(async_exch_t *exch, void *data, size_t size)
261{
[fd7c98b]262 return async_data_read_start(exch, data, size);
[504f1ea3]263}
264
[b66d43b]265/****
266 * SERVER
267 ****/
268
[5bf4310]269static void hound_server_read_data(void *stream);
270static void hound_server_write_data(void *stream);
[939871a]271static const hound_server_iface_t *server_iface;
[b66d43b]272
[e6e5f4e]273void hound_service_set_server_iface(const hound_server_iface_t *iface)
[b66d43b]274{
275 server_iface = iface;
276}
277
278void hound_connection_handler(ipc_callid_t iid, ipc_call_t *icall, void *arg)
279{
280 /* Accept connection if there is a valid iface*/
281 if (server_iface) {
282 async_answer_0(iid, EOK);
283 } else {
284 async_answer_0(iid, ENOTSUP);
285 return;
286 }
287
288 while (1) {
289 ipc_call_t call;
290 ipc_callid_t callid = async_get_call(&call);
[a4165561]291 switch (IPC_GET_IMETHOD(call)) {
[b66d43b]292 case IPC_M_HOUND_CONTEXT_REGISTER: {
293 if (!server_iface || !server_iface->add_context) {
294 async_answer_0(callid, ENOTSUP);
295 break;
296 }
297 bool record = IPC_GET_ARG1(call);
298 void *name;
299 int ret =
300 async_data_write_accept(&name, true, 0, 0, 0, 0);
301 if (ret != EOK) {
302 async_answer_0(callid, ret);
303 break;
304 }
305 hound_context_id_t id = 0;
306 ret = server_iface->add_context(server_iface->server,
307 &id, name, record);
308 if (ret != EOK) {
309 free(name);
310 async_answer_0(callid, ret);
[e6e5f4e]311 } else {
312 async_answer_1(callid, EOK, id);
[b66d43b]313 }
[e6e5f4e]314 break;
[b66d43b]315 }
[a4165561]316 case IPC_M_HOUND_CONTEXT_UNREGISTER: {
317 if (!server_iface || !server_iface->rem_context) {
318 async_answer_0(callid, ENOTSUP);
319 break;
320 }
321 hound_context_id_t id = IPC_GET_ARG1(call);
322 const int ret =
323 server_iface->rem_context(server_iface->server, id);
324 async_answer_0(callid, ret);
[6ec1d48]325 break;
326 }
327 case IPC_M_HOUND_GET_LIST: {
328 if (!server_iface || !server_iface->get_list) {
329 async_answer_0(callid, ENOTSUP);
330 break;
331 }
332 const char **list = NULL;
333 const int flags = IPC_GET_ARG1(call);
334 size_t count = IPC_GET_ARG2(call);
335 const bool conn = IPC_GET_ARG3(call);
336 char *conn_name = NULL;
337 int ret = EOK;
338 if (conn)
339 ret = async_data_write_accept(
340 (void**)&conn_name, true, 0, 0, 0, 0);
341
342 if (ret == EOK)
343 ret = server_iface->get_list(
344 server_iface->server, &list, &count,
345 conn_name, flags);
346 free(conn_name);
347 size_t *sizes = NULL;
348 if (count)
349 sizes = calloc(count, sizeof(size_t));
350 if (count && !sizes)
351 ret = ENOMEM;
352 async_answer_1(callid, ret, count);
353
354 /* We are done */
355 if (count == 0 || ret != EOK)
356 break;
357
358 /* Prepare sizes table */
359 for (unsigned i = 0; i < count; ++i)
360 sizes[i] = str_size(list[i]);
361
362 /* Send sizes table */
363 ipc_callid_t id;
364 if (async_data_read_receive(&id, NULL)) {
365 ret = async_data_read_finalize(id, sizes,
366 count * sizeof(size_t));
367 }
368 free(sizes);
369
370 /* Proceed to send names */
371 for (unsigned i = 0; i < count; ++i) {
372 size_t size = str_size(list[i]);
373 ipc_callid_t id;
374 if (ret == EOK &&
375 async_data_read_receive(&id, NULL)) {
376 ret = async_data_read_finalize(id,
377 list[i], size);
378 }
379 free(list[i]);
380 }
381 free(list);
382 break;
383 }
384 case IPC_M_HOUND_CONNECT: {
385 if (!server_iface || !server_iface->connect) {
386 async_answer_0(callid, ENOTSUP);
387 break;
388 }
389 void *source = NULL;
390 void *sink = NULL;
391 int ret =
392 async_data_write_accept(&source, true, 0, 0, 0, 0);
393 if (ret == EOK)
394 ret = async_data_write_accept(&sink,
395 true, 0, 0, 0, 0);
396 if (ret == EOK)
397 ret = server_iface->connect(
398 server_iface->server, source, sink);
399 free(source);
400 free(sink);
401 async_answer_0(callid, ret);
402 break;
403 }
404 case IPC_M_HOUND_DISCONNECT: {
405 if (!server_iface || !server_iface->disconnect) {
406 async_answer_0(callid, ENOTSUP);
407 break;
408 }
409 void *source = NULL;
410 void *sink = NULL;
411 int ret =
412 async_data_write_accept(&source, true, 0, 0, 0, 0);
413 if (ret == EOK)
414 ret = async_data_write_accept(&sink,
415 true, 0, 0, 0, 0);
416 if (ret == EOK)
417 ret = server_iface->connect(
418 server_iface->server, source, sink);
419 free(source);
420 free(sink);
421 async_answer_0(callid, ret);
422 break;
[a4165561]423 }
[b66d43b]424 case IPC_M_HOUND_STREAM_ENTER: {
[cc3c27ad]425 if (!server_iface || !server_iface->is_record_context
426 || !server_iface->add_stream
427 || !server_iface->rem_stream) {
[b66d43b]428 async_answer_0(callid, ENOTSUP);
429 break;
430 }
431
432 hound_context_id_t id = IPC_GET_ARG1(call);
[6133470]433 const int flags = IPC_GET_ARG2(call);
434 const format_convert_t c = {.arg = IPC_GET_ARG3(call)};
435 const pcm_format_t f = {
436 .sampling_rate = c.f.rate * 100,
437 .channels = c.f.channels,
438 .sample_format = c.f.format,
439 };
[b66d43b]440 size_t bsize = IPC_GET_ARG4(call);
441 void *stream;
442 int ret = server_iface->add_stream(server_iface->server,
[6133470]443 id, flags, f, bsize, &stream);
[b66d43b]444 if (ret != EOK) {
445 async_answer_0(callid, ret);
446 break;
447 }
[37ea333]448 const bool rec = server_iface->is_record_context(
449 server_iface->server, id);
450 if (rec) {
[5bf4310]451 if(server_iface->stream_data_read) {
452 async_answer_0(callid, EOK);
453 hound_server_write_data(stream);
[cc3c27ad]454 server_iface->rem_stream(
455 server_iface->server, stream);
[5bf4310]456 } else {
457 async_answer_0(callid, ENOTSUP);
458 }
[37ea333]459 } else {
[5bf4310]460 if (server_iface->stream_data_write) {
461 async_answer_0(callid, EOK);
462 hound_server_read_data(stream);
[cc3c27ad]463 server_iface->rem_stream(
464 server_iface->server, stream);
[5bf4310]465 } else {
466 async_answer_0(callid, ENOTSUP);
467 }
[37ea333]468 }
[b66d43b]469 break;
470 }
471 case IPC_M_HOUND_STREAM_EXIT:
[a4165561]472 case IPC_M_HOUND_STREAM_DRAIN:
473 /* Stream exit/drain is only allowed in stream context*/
[939871a]474 async_answer_0(callid, EINVAL);
475 break;
[b66d43b]476 default:
477 async_answer_0(callid, ENOTSUP);
478 return;
479 }
480 }
481}
482
[5bf4310]483static void hound_server_read_data(void *stream)
[b66d43b]484{
485 ipc_callid_t callid;
[71780e0]486 ipc_call_t call;
[b66d43b]487 size_t size = 0;
[704baed]488 while (async_data_write_receive_call(&callid, &call, &size)
489 || (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN)) {
490 if (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN) {
491 int ret = ENOTSUP;
492 if (server_iface->drain_stream)
493 ret = server_iface->drain_stream(stream);
494 async_answer_0(callid, ret);
495 continue;
496 }
[b66d43b]497 char *buffer = malloc(size);
498 if (!buffer) {
499 async_answer_0(callid, ENOMEM);
500 continue;
501 }
502 int ret = async_data_write_finalize(callid, buffer, size);
503 if (ret == EOK) {
[704baed]504 ret = server_iface->stream_data_write(
505 stream, buffer, size);
[b66d43b]506 }
[37ea333]507 async_answer_0(callid, ret);
[b66d43b]508 }
[71780e0]509 const int ret = IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_EXIT
510 ? EOK : EINVAL;
511
512 async_answer_0(callid, ret);
[b66d43b]513}
[504f1ea3]514
[5bf4310]515static void hound_server_write_data(void *stream)
[37ea333]516{
517
518 ipc_callid_t callid;
519 ipc_call_t call;
520 size_t size = 0;
[9e40d443]521 while (async_data_read_receive_call(&callid, &call, &size)
522 || (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN)) {
523 if (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN) {
524 int ret = ENOTSUP;
525 if (server_iface->drain_stream)
526 ret = server_iface->drain_stream(stream);
527 async_answer_0(callid, ret);
528 continue;
529 }
[37ea333]530 char *buffer = malloc(size);
531 if (!buffer) {
532 async_answer_0(callid, ENOMEM);
533 continue;
534 }
535 int ret = server_iface->stream_data_read(stream, buffer, size);
536 if (ret == EOK) {
537 ret = async_data_read_finalize(callid, buffer, size);
538 }
539 async_answer_0(callid, ret);
540 }
541 const int ret = IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_EXIT
542 ? EOK : EINVAL;
543
544 async_answer_0(callid, ret);
545}
546
[afa7c17]547/***
[b66d43b]548 * CLIENT SIDE -DEPRECATED
[afa7c17]549 ***/
550
551typedef struct {
552 data_callback_t cb;
553 void *arg;
554} callback_t;
555
[059490c2]556hound_sess_t *hound_get_session(void)
557{
558 return hound_service_connect(HOUND_SERVICE);
559}
560
561void hound_release_session(hound_sess_t *sess)
562{
563 hound_service_disconnect(sess);
564}
565
[afa7c17]566
567static int hound_register(hound_sess_t *sess, unsigned cmd, const char *name,
568 unsigned channels, unsigned rate, pcm_sample_format_t format,
569 data_callback_t data_callback, void *arg);
570static int hound_unregister(hound_sess_t *sess, unsigned cmd, const char *name);
571static int hound_connection(hound_sess_t *sess, unsigned cmd,
572 const char *source, const char *sink);
573static void callback_pb(ipc_callid_t iid, ipc_call_t *call, void *arg);
574static void callback_rec(ipc_callid_t iid, ipc_call_t *call, void *arg);
575
576
577int hound_register_playback(hound_sess_t *sess, const char *name,
578 unsigned channels, unsigned rate, pcm_sample_format_t format,
579 data_callback_t data_callback, void *arg)
580{
581 return hound_register(sess, HOUND_REGISTER_PLAYBACK, name, channels,
582 rate, format, data_callback, arg);
583}
584int hound_register_recording(hound_sess_t *sess, const char *name,
585 unsigned channels, unsigned rate, pcm_sample_format_t format,
586 data_callback_t data_callback, void *arg)
587{
588 return hound_register(sess, HOUND_REGISTER_RECORDING, name, channels,
589 rate, format, data_callback, arg);
590
591}
592int hound_unregister_playback(hound_sess_t *sess, const char *name)
593{
594 return hound_unregister(sess, HOUND_UNREGISTER_PLAYBACK, name);
595}
596int hound_unregister_recording(hound_sess_t *sess, const char *name)
597{
598 return hound_unregister(sess, HOUND_UNREGISTER_RECORDING, name);
599}
600int hound_create_connection(hound_sess_t *sess, const char *source, const char *sink)
601{
602 return hound_connection(sess, HOUND_CONNECT, source, sink);
603}
604int hound_destroy_connection(hound_sess_t *sess, const char *source, const char *sink)
605{
606 return hound_connection(sess, HOUND_DISCONNECT, source, sink);
607}
608
609static int hound_register(hound_sess_t *sess, unsigned cmd, const char *name,
610 unsigned channels, unsigned rate, pcm_sample_format_t format,
611 data_callback_t data_callback, void *arg)
612{
613 assert(cmd == HOUND_REGISTER_PLAYBACK || cmd == HOUND_REGISTER_RECORDING);
614 if (!name || !data_callback || !sess)
615 return EINVAL;
616
617 async_exch_t *exch = async_exchange_begin(sess);
618 if (!exch)
619 return ENOMEM;
620
621 aid_t id = async_send_0(exch, cmd, NULL);
622
623 int ret = async_data_write_start(exch, name, str_size(name));
624 if (ret != EOK) {
625 async_forget(id);
626 async_exchange_end(exch);
627 return ret;
628 }
629
630 callback_t *cb = malloc(sizeof(callback_t));
631 if (!cb) {
632 async_forget(id);
633 async_exchange_end(exch);
634 return ENOMEM;
635 }
636
637 cb->cb = data_callback;
638 cb->arg = arg;
639 async_client_conn_t callback =
640 (cmd == HOUND_REGISTER_PLAYBACK) ? callback_pb : callback_rec;
641
642 ret = async_connect_to_me(exch, channels, rate, format, callback, cb);
643 if (ret != EOK) {
644 async_forget(id);
645 async_exchange_end(exch);
646 free(cb);
647 return ret;
648 }
649
650 async_wait_for(id, (sysarg_t*)&ret);
651 if (ret != EOK) {
652 async_exchange_end(exch);
653 free(cb);
654 return ret;
655 }
656
657 async_exchange_end(exch);
658 return EOK;
659}
660
661static int hound_unregister(hound_sess_t *sess, unsigned cmd, const char *name)
662{
663 assert(cmd == HOUND_UNREGISTER_PLAYBACK || cmd == HOUND_UNREGISTER_RECORDING);
664 if (!name || !sess)
665 return EINVAL;
666
667 async_exch_t *exch = async_exchange_begin(sess);
668 if (!exch)
669 return ENOMEM;
670 aid_t id = async_send_0(exch, cmd, NULL);
671 sysarg_t ret = async_data_write_start(exch, name, str_size(name));
672 if (ret != EOK) {
673 async_forget(id);
674 async_exchange_end(exch);
675 return ret;
676 }
677
678 async_wait_for(id, &ret);
679 async_exchange_end(exch);
680 return ret;
681}
682
683static int hound_connection(hound_sess_t *sess, unsigned cmd,
684 const char *source, const char *sink)
685{
686 assert(cmd == HOUND_CONNECT || cmd == HOUND_DISCONNECT);
687 if (!source || !sink || !sess)
688 return EINVAL;
689
690 async_exch_t *exch = async_exchange_begin(sess);
691 if (!exch)
692 return ENOMEM;
693
694 aid_t id = async_send_0(exch, cmd, NULL);
695 sysarg_t ret = async_data_write_start(exch, source, str_size(source));
696 if (ret != EOK) {
697 async_forget(id);
698 async_exchange_end(exch);
699 return ret;
700 }
701 ret = async_data_write_start(exch, sink, str_size(sink));
702 if (ret != EOK) {
703 async_forget(id);
704 async_exchange_end(exch);
705 return ret;
706 }
707 async_wait_for(id, &ret);
708 async_exchange_end(exch);
709 return ret;
710}
711static void callback_gen(ipc_callid_t iid, ipc_call_t *call, void *arg,
712 bool read)
713{
714 async_answer_0(iid, EOK);
715 callback_t *cb = arg;
716 assert(cb);
717 void *buffer = NULL;
718 size_t buffer_size = 0;
719
720 bool (*receive)(ipc_callid_t *, size_t *) = read ?
721 async_data_read_receive : async_data_write_receive;
722
723 while (1) {
724 size_t size = 0;
725 ipc_callid_t id = 0;
726 if (!receive(&id, &size)) {
[a9aae16e]727 ipc_call_t failed_call;
728 async_get_call(&failed_call);
729 /* Protocol error or hangup */
730 if (IPC_GET_IMETHOD(failed_call) != 0)
731 cb->cb(cb->arg, NULL, EIO);
[afa7c17]732 free(cb);
733 return;
734 }
735
736 if (buffer_size < size) {
737 buffer = realloc(buffer, size);
738 if (!buffer) {
739 cb->cb(cb->arg, NULL, ENOMEM);
740 free(cb);
741 return;
742 }
743 buffer_size = size;
744 }
745 if (read)
746 cb->cb(cb->arg, buffer, size);
747 const int ret = read ?
748 async_data_read_finalize(id, buffer, size):
749 async_data_write_finalize(id, buffer, size);
750 if (ret != EOK) {
751 cb->cb(cb->arg, NULL, ret);
752 free(cb);
753 return;
754 }
755 if (!read)
756 cb->cb(cb->arg, buffer, size);
757 }
758}
759
760static void callback_pb(ipc_callid_t iid, ipc_call_t *call, void *arg)
761{
762 callback_gen(iid, call, arg, true);
763}
764
765static void callback_rec(ipc_callid_t iid, ipc_call_t *call, void *arg)
766{
767 callback_gen(iid, call, arg, false);
768}
769
770/***
[b66d43b]771 * SERVER SIDE - DEPRECATED
[afa7c17]772 ***/
[b66d43b]773
[afa7c17]774static const char * get_name(void);
775
776int hound_server_register(const char *name, service_id_t *id)
777{
778 if (!name || !id)
779 return EINVAL;
780
781 int ret = loc_server_register(name);
782 if (ret != EOK)
783 return ret;
784
785 return loc_service_register(HOUND_SERVICE, id);
786}
787
788void hound_server_unregister(service_id_t id)
789{
790 loc_service_unregister(id);
791}
792
793int hound_server_set_device_change_callback(dev_change_callback_t cb)
794{
795 return loc_register_cat_change_cb(cb);
796}
797
798int hound_server_devices_iterate(device_callback_t callback)
799{
800 if (!callback)
801 return EINVAL;
802 static bool resolved = false;
803 static category_id_t cat_id = 0;
804
805 if (!resolved) {
806 const int ret = loc_category_get_id("audio-pcm", &cat_id,
807 IPC_FLAG_BLOCKING);
808 if (ret != EOK)
809 return ret;
810 resolved = true;
811 }
812
813 service_id_t *svcs = NULL;
814 size_t count = 0;
815 const int ret = loc_category_get_svcs(cat_id, &svcs, &count);
816 if (ret != EOK)
817 return ret;
818
819 for (unsigned i = 0; i < count; ++i) {
820 char *name = NULL;
821 loc_service_get_name(svcs[i], &name);
822 callback(svcs[i], name);
823 free(name);
824 }
825 free(svcs);
826 return EOK;
827}
828
829int hound_server_get_register_params(const char **name, async_sess_t **sess,
830 unsigned *channels, unsigned *rate, pcm_sample_format_t *format)
831{
832 if (!name || !sess || !channels || !rate || !format)
833 return EINVAL;
834
835 const char *n = get_name();
836 if (!n)
837 return ENOMEM;
838
839 ipc_call_t call;
840 ipc_callid_t callid = async_get_call(&call);
841
842 unsigned ch = IPC_GET_ARG1(call);
843 unsigned r = IPC_GET_ARG2(call);
844 pcm_sample_format_t f = IPC_GET_ARG3(call);
845
846 async_sess_t *s = async_callback_receive_start(EXCHANGE_ATOMIC, &call);
847 async_answer_0(callid, s ? EOK : ENOMEM);
848
849 *name = n;
850 *sess = s;
851 *channels = ch;
852 *rate = r;
853 *format = f;
854
855 return ENOTSUP;
856}
857
858int hound_server_get_unregister_params(const char **name)
859{
860 if (!name)
861 return EINVAL;
862 *name = get_name();
863 if (!*name)
864 return ENOMEM;
865 return EOK;
866}
867
868int hound_server_get_connection_params(const char **source, const char **sink)
869{
870 if (!source || !sink)
871 return EINVAL;
872
873 const char *source_name = get_name();
874 if (!source_name)
875 return ENOMEM;
876
877 const char *sink_name = get_name();
878 if (!sink_name)
879 return ENOMEM;
880
881 *source = source_name;
882 *sink = sink_name;
883 return EOK;
884}
885
886static const char * get_name(void)
887{
888 size_t size = 0;
889 ipc_callid_t callid;
890 async_data_write_receive(&callid, &size);
891 char *buffer = malloc(size + 1);
892 if (buffer) {
893 async_data_write_finalize(callid, buffer, size);
894 buffer[size] = 0;
895 }
896 return buffer;
897}
898
899/**
900 * @}
901 */
Note: See TracBrowser for help on using the repository browser.