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

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

libhound: add drain iface

  • Property mode set to 100644
File size: 21.7 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;
[71780e0]488 while (async_data_write_receive_call(&callid, &call, &size)) {
[b66d43b]489 char *buffer = malloc(size);
490 if (!buffer) {
491 async_answer_0(callid, ENOMEM);
492 continue;
493 }
494 int ret = async_data_write_finalize(callid, buffer, size);
495 if (ret == EOK) {
[37ea333]496 ret = server_iface->stream_data_write(stream, buffer, size);
[b66d43b]497 }
[37ea333]498 async_answer_0(callid, ret);
[b66d43b]499 }
[71780e0]500 //TODO drain?
501 const int ret = IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_EXIT
502 ? EOK : EINVAL;
503
504 async_answer_0(callid, ret);
[b66d43b]505}
[504f1ea3]506
[5bf4310]507static void hound_server_write_data(void *stream)
[37ea333]508{
509
510 ipc_callid_t callid;
511 ipc_call_t call;
512 size_t size = 0;
[9e40d443]513 while (async_data_read_receive_call(&callid, &call, &size)
514 || (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN)) {
515 if (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN) {
516 int ret = ENOTSUP;
517 if (server_iface->drain_stream)
518 ret = server_iface->drain_stream(stream);
519 async_answer_0(callid, ret);
520 continue;
521 }
[37ea333]522 char *buffer = malloc(size);
523 if (!buffer) {
524 async_answer_0(callid, ENOMEM);
525 continue;
526 }
527 int ret = server_iface->stream_data_read(stream, buffer, size);
528 if (ret == EOK) {
529 ret = async_data_read_finalize(callid, buffer, size);
530 }
531 async_answer_0(callid, ret);
532 }
533 const int ret = IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_EXIT
534 ? EOK : EINVAL;
535
536 async_answer_0(callid, ret);
537}
538
[afa7c17]539/***
[b66d43b]540 * CLIENT SIDE -DEPRECATED
[afa7c17]541 ***/
542
543typedef struct {
544 data_callback_t cb;
545 void *arg;
546} callback_t;
547
[059490c2]548hound_sess_t *hound_get_session(void)
549{
550 return hound_service_connect(HOUND_SERVICE);
551}
552
553void hound_release_session(hound_sess_t *sess)
554{
555 hound_service_disconnect(sess);
556}
557
[afa7c17]558
559static int hound_register(hound_sess_t *sess, unsigned cmd, const char *name,
560 unsigned channels, unsigned rate, pcm_sample_format_t format,
561 data_callback_t data_callback, void *arg);
562static int hound_unregister(hound_sess_t *sess, unsigned cmd, const char *name);
563static int hound_connection(hound_sess_t *sess, unsigned cmd,
564 const char *source, const char *sink);
565static void callback_pb(ipc_callid_t iid, ipc_call_t *call, void *arg);
566static void callback_rec(ipc_callid_t iid, ipc_call_t *call, void *arg);
567
568
569int hound_register_playback(hound_sess_t *sess, const char *name,
570 unsigned channels, unsigned rate, pcm_sample_format_t format,
571 data_callback_t data_callback, void *arg)
572{
573 return hound_register(sess, HOUND_REGISTER_PLAYBACK, name, channels,
574 rate, format, data_callback, arg);
575}
576int hound_register_recording(hound_sess_t *sess, const char *name,
577 unsigned channels, unsigned rate, pcm_sample_format_t format,
578 data_callback_t data_callback, void *arg)
579{
580 return hound_register(sess, HOUND_REGISTER_RECORDING, name, channels,
581 rate, format, data_callback, arg);
582
583}
584int hound_unregister_playback(hound_sess_t *sess, const char *name)
585{
586 return hound_unregister(sess, HOUND_UNREGISTER_PLAYBACK, name);
587}
588int hound_unregister_recording(hound_sess_t *sess, const char *name)
589{
590 return hound_unregister(sess, HOUND_UNREGISTER_RECORDING, name);
591}
592int hound_create_connection(hound_sess_t *sess, const char *source, const char *sink)
593{
594 return hound_connection(sess, HOUND_CONNECT, source, sink);
595}
596int hound_destroy_connection(hound_sess_t *sess, const char *source, const char *sink)
597{
598 return hound_connection(sess, HOUND_DISCONNECT, source, sink);
599}
600
601static int hound_register(hound_sess_t *sess, unsigned cmd, const char *name,
602 unsigned channels, unsigned rate, pcm_sample_format_t format,
603 data_callback_t data_callback, void *arg)
604{
605 assert(cmd == HOUND_REGISTER_PLAYBACK || cmd == HOUND_REGISTER_RECORDING);
606 if (!name || !data_callback || !sess)
607 return EINVAL;
608
609 async_exch_t *exch = async_exchange_begin(sess);
610 if (!exch)
611 return ENOMEM;
612
613 aid_t id = async_send_0(exch, cmd, NULL);
614
615 int ret = async_data_write_start(exch, name, str_size(name));
616 if (ret != EOK) {
617 async_forget(id);
618 async_exchange_end(exch);
619 return ret;
620 }
621
622 callback_t *cb = malloc(sizeof(callback_t));
623 if (!cb) {
624 async_forget(id);
625 async_exchange_end(exch);
626 return ENOMEM;
627 }
628
629 cb->cb = data_callback;
630 cb->arg = arg;
631 async_client_conn_t callback =
632 (cmd == HOUND_REGISTER_PLAYBACK) ? callback_pb : callback_rec;
633
634 ret = async_connect_to_me(exch, channels, rate, format, callback, cb);
635 if (ret != EOK) {
636 async_forget(id);
637 async_exchange_end(exch);
638 free(cb);
639 return ret;
640 }
641
642 async_wait_for(id, (sysarg_t*)&ret);
643 if (ret != EOK) {
644 async_exchange_end(exch);
645 free(cb);
646 return ret;
647 }
648
649 async_exchange_end(exch);
650 return EOK;
651}
652
653static int hound_unregister(hound_sess_t *sess, unsigned cmd, const char *name)
654{
655 assert(cmd == HOUND_UNREGISTER_PLAYBACK || cmd == HOUND_UNREGISTER_RECORDING);
656 if (!name || !sess)
657 return EINVAL;
658
659 async_exch_t *exch = async_exchange_begin(sess);
660 if (!exch)
661 return ENOMEM;
662 aid_t id = async_send_0(exch, cmd, NULL);
663 sysarg_t ret = async_data_write_start(exch, name, str_size(name));
664 if (ret != EOK) {
665 async_forget(id);
666 async_exchange_end(exch);
667 return ret;
668 }
669
670 async_wait_for(id, &ret);
671 async_exchange_end(exch);
672 return ret;
673}
674
675static int hound_connection(hound_sess_t *sess, unsigned cmd,
676 const char *source, const char *sink)
677{
678 assert(cmd == HOUND_CONNECT || cmd == HOUND_DISCONNECT);
679 if (!source || !sink || !sess)
680 return EINVAL;
681
682 async_exch_t *exch = async_exchange_begin(sess);
683 if (!exch)
684 return ENOMEM;
685
686 aid_t id = async_send_0(exch, cmd, NULL);
687 sysarg_t ret = async_data_write_start(exch, source, str_size(source));
688 if (ret != EOK) {
689 async_forget(id);
690 async_exchange_end(exch);
691 return ret;
692 }
693 ret = async_data_write_start(exch, sink, str_size(sink));
694 if (ret != EOK) {
695 async_forget(id);
696 async_exchange_end(exch);
697 return ret;
698 }
699 async_wait_for(id, &ret);
700 async_exchange_end(exch);
701 return ret;
702}
703static void callback_gen(ipc_callid_t iid, ipc_call_t *call, void *arg,
704 bool read)
705{
706 async_answer_0(iid, EOK);
707 callback_t *cb = arg;
708 assert(cb);
709 void *buffer = NULL;
710 size_t buffer_size = 0;
711
712 bool (*receive)(ipc_callid_t *, size_t *) = read ?
713 async_data_read_receive : async_data_write_receive;
714
715 while (1) {
716 size_t size = 0;
717 ipc_callid_t id = 0;
718 if (!receive(&id, &size)) {
[a9aae16e]719 ipc_call_t failed_call;
720 async_get_call(&failed_call);
721 /* Protocol error or hangup */
722 if (IPC_GET_IMETHOD(failed_call) != 0)
723 cb->cb(cb->arg, NULL, EIO);
[afa7c17]724 free(cb);
725 return;
726 }
727
728 if (buffer_size < size) {
729 buffer = realloc(buffer, size);
730 if (!buffer) {
731 cb->cb(cb->arg, NULL, ENOMEM);
732 free(cb);
733 return;
734 }
735 buffer_size = size;
736 }
737 if (read)
738 cb->cb(cb->arg, buffer, size);
739 const int ret = read ?
740 async_data_read_finalize(id, buffer, size):
741 async_data_write_finalize(id, buffer, size);
742 if (ret != EOK) {
743 cb->cb(cb->arg, NULL, ret);
744 free(cb);
745 return;
746 }
747 if (!read)
748 cb->cb(cb->arg, buffer, size);
749 }
750}
751
752static void callback_pb(ipc_callid_t iid, ipc_call_t *call, void *arg)
753{
754 callback_gen(iid, call, arg, true);
755}
756
757static void callback_rec(ipc_callid_t iid, ipc_call_t *call, void *arg)
758{
759 callback_gen(iid, call, arg, false);
760}
761
762/***
[b66d43b]763 * SERVER SIDE - DEPRECATED
[afa7c17]764 ***/
[b66d43b]765
[afa7c17]766static const char * get_name(void);
767
768int hound_server_register(const char *name, service_id_t *id)
769{
770 if (!name || !id)
771 return EINVAL;
772
773 int ret = loc_server_register(name);
774 if (ret != EOK)
775 return ret;
776
777 return loc_service_register(HOUND_SERVICE, id);
778}
779
780void hound_server_unregister(service_id_t id)
781{
782 loc_service_unregister(id);
783}
784
785int hound_server_set_device_change_callback(dev_change_callback_t cb)
786{
787 return loc_register_cat_change_cb(cb);
788}
789
790int hound_server_devices_iterate(device_callback_t callback)
791{
792 if (!callback)
793 return EINVAL;
794 static bool resolved = false;
795 static category_id_t cat_id = 0;
796
797 if (!resolved) {
798 const int ret = loc_category_get_id("audio-pcm", &cat_id,
799 IPC_FLAG_BLOCKING);
800 if (ret != EOK)
801 return ret;
802 resolved = true;
803 }
804
805 service_id_t *svcs = NULL;
806 size_t count = 0;
807 const int ret = loc_category_get_svcs(cat_id, &svcs, &count);
808 if (ret != EOK)
809 return ret;
810
811 for (unsigned i = 0; i < count; ++i) {
812 char *name = NULL;
813 loc_service_get_name(svcs[i], &name);
814 callback(svcs[i], name);
815 free(name);
816 }
817 free(svcs);
818 return EOK;
819}
820
821int hound_server_get_register_params(const char **name, async_sess_t **sess,
822 unsigned *channels, unsigned *rate, pcm_sample_format_t *format)
823{
824 if (!name || !sess || !channels || !rate || !format)
825 return EINVAL;
826
827 const char *n = get_name();
828 if (!n)
829 return ENOMEM;
830
831 ipc_call_t call;
832 ipc_callid_t callid = async_get_call(&call);
833
834 unsigned ch = IPC_GET_ARG1(call);
835 unsigned r = IPC_GET_ARG2(call);
836 pcm_sample_format_t f = IPC_GET_ARG3(call);
837
838 async_sess_t *s = async_callback_receive_start(EXCHANGE_ATOMIC, &call);
839 async_answer_0(callid, s ? EOK : ENOMEM);
840
841 *name = n;
842 *sess = s;
843 *channels = ch;
844 *rate = r;
845 *format = f;
846
847 return ENOTSUP;
848}
849
850int hound_server_get_unregister_params(const char **name)
851{
852 if (!name)
853 return EINVAL;
854 *name = get_name();
855 if (!*name)
856 return ENOMEM;
857 return EOK;
858}
859
860int hound_server_get_connection_params(const char **source, const char **sink)
861{
862 if (!source || !sink)
863 return EINVAL;
864
865 const char *source_name = get_name();
866 if (!source_name)
867 return ENOMEM;
868
869 const char *sink_name = get_name();
870 if (!sink_name)
871 return ENOMEM;
872
873 *source = source_name;
874 *sink = sink_name;
875 return EOK;
876}
877
878static const char * get_name(void)
879{
880 size_t size = 0;
881 ipc_callid_t callid;
882 async_data_write_receive(&callid, &size);
883 char *buffer = malloc(size + 1);
884 if (buffer) {
885 async_data_write_finalize(callid, buffer, size);
886 buffer[size] = 0;
887 }
888 return buffer;
889}
890
891/**
892 * @}
893 */
Note: See TracBrowser for help on using the repository browser.