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

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

libhound: fix data transfer answers

finalize answers EOK for us so postpone real answer until next request

  • Property mode set to 100644
File size: 22.1 KB
Line 
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 */
36#include <adt/list.h>
37#include <errno.h>
38#include <loc.h>
39#include <macros.h>
40#include <str.h>
41#include <stdlib.h>
42#include <stdio.h>
43#include <libarch/types.h>
44
45#include "protocol.h"
46#include "client.h"
47#include "server.h"
48
49enum ipc_methods {
50 IPC_M_HOUND_CONTEXT_REGISTER = IPC_FIRST_USER_METHOD,
51 IPC_M_HOUND_CONTEXT_UNREGISTER,
52 IPC_M_HOUND_GET_LIST,
53 IPC_M_HOUND_CONNECT,
54 IPC_M_HOUND_DISCONNECT,
55 IPC_M_HOUND_STREAM_ENTER,
56 IPC_M_HOUND_STREAM_EXIT,
57 IPC_M_HOUND_STREAM_DRAIN,
58};
59
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
70/****
71 * CLIENT
72 ****/
73
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}
91
92hound_context_id_t hound_service_register_context(hound_sess_t *sess,
93 const char *name, bool record)
94{
95 assert(sess);
96 assert(name);
97 ipc_call_t call;
98 async_exch_t *exch = async_exchange_begin(sess);
99 aid_t mid =
100 async_send_1(exch, IPC_M_HOUND_CONTEXT_REGISTER, record, &call);
101 int ret = mid ? EOK : EPARTY;
102
103 if (ret == EOK)
104 ret = async_data_write_start(exch, name, str_size(name));
105 else
106 async_forget(mid);
107
108 if (ret == EOK)
109 async_wait_for(mid, (sysarg_t *)&ret);
110
111 async_exchange_end(exch);
112 return ret == EOK ? (hound_context_id_t)IPC_GET_ARG1(call) : ret;
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
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 */
158 const char **names = NULL;
159 if (name_count) {
160 size_t *sizes = calloc(name_count, sizeof(size_t));
161 names = calloc(name_count, sizeof(char *));
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) {
171 bzero(name, sizes[i] + 1);
172 ret = async_data_read_start(exch, name, sizes[i]);
173 names[i] = name;
174 } else {
175 ret = ENOMEM;
176 }
177 }
178 free(sizes);
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;
202 ipc_call_t call;
203 aid_t id = async_send_0(exch, IPC_M_HOUND_CONNECT, &call);
204 int ret = id ? EOK : EPARTY;
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));
209 async_wait_for(id, (sysarg_t*)&ret);
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;
221 ipc_call_t call;
222 aid_t id = async_send_0(exch, IPC_M_HOUND_DISCONNECT, &call);
223 int ret = id ? EOK : EPARTY;
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));
228 async_wait_for(id, (sysarg_t*)&ret);
229 async_exchange_end(exch);
230 return ENOTSUP;
231}
232
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{
236 const format_convert_t c = { .f = {
237 .channels = format.channels,
238 .rate = format.sampling_rate / 100,
239 .format = format.sample_format,
240 }};
241 return async_req_4_0(exch, IPC_M_HOUND_STREAM_ENTER, id, flags,
242 c.arg, bsize);
243}
244
245int hound_service_stream_exit(async_exch_t *exch)
246{
247 return async_req_0_0(exch, IPC_M_HOUND_STREAM_EXIT);
248}
249
250int hound_service_stream_drain(async_exch_t *exch)
251{
252 return async_req_0_0(exch, IPC_M_HOUND_STREAM_DRAIN);
253}
254
255int hound_service_stream_write(async_exch_t *exch, const void *data, size_t size)
256{
257 return async_data_write_start(exch, data, size);
258}
259
260int hound_service_stream_read(async_exch_t *exch, void *data, size_t size)
261{
262 return async_data_read_start(exch, data, size);
263}
264
265/****
266 * SERVER
267 ****/
268
269static void hound_server_read_data(void *stream);
270static void hound_server_write_data(void *stream);
271static const hound_server_iface_t *server_iface;
272
273void hound_service_set_server_iface(const hound_server_iface_t *iface)
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);
291 switch (IPC_GET_IMETHOD(call)) {
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);
311 } else {
312 async_answer_1(callid, EOK, id);
313 }
314 break;
315 }
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);
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;
423 }
424 case IPC_M_HOUND_STREAM_ENTER: {
425 if (!server_iface || !server_iface->is_record_context
426 || !server_iface->add_stream
427 || !server_iface->rem_stream) {
428 async_answer_0(callid, ENOTSUP);
429 break;
430 }
431
432 hound_context_id_t id = IPC_GET_ARG1(call);
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 };
440 size_t bsize = IPC_GET_ARG4(call);
441 void *stream;
442 int ret = server_iface->add_stream(server_iface->server,
443 id, flags, f, bsize, &stream);
444 if (ret != EOK) {
445 async_answer_0(callid, ret);
446 break;
447 }
448 const bool rec = server_iface->is_record_context(
449 server_iface->server, id);
450 if (rec) {
451 if(server_iface->stream_data_read) {
452 async_answer_0(callid, EOK);
453 hound_server_write_data(stream);
454 server_iface->rem_stream(
455 server_iface->server, stream);
456 } else {
457 async_answer_0(callid, ENOTSUP);
458 }
459 } else {
460 if (server_iface->stream_data_write) {
461 async_answer_0(callid, EOK);
462 hound_server_read_data(stream);
463 server_iface->rem_stream(
464 server_iface->server, stream);
465 } else {
466 async_answer_0(callid, ENOTSUP);
467 }
468 }
469 break;
470 }
471 case IPC_M_HOUND_STREAM_EXIT:
472 case IPC_M_HOUND_STREAM_DRAIN:
473 /* Stream exit/drain is only allowed in stream context*/
474 async_answer_0(callid, EINVAL);
475 break;
476 default:
477 async_answer_0(callid, ENOTSUP);
478 return;
479 }
480 }
481}
482
483static void hound_server_read_data(void *stream)
484{
485 ipc_callid_t callid;
486 ipc_call_t call;
487 size_t size = 0;
488 int ret_answer = EOK;
489 while (async_data_write_receive_call(&callid, &call, &size)
490 || (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN)) {
491 if (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN) {
492 int ret = ENOTSUP;
493 if (server_iface->drain_stream)
494 ret = server_iface->drain_stream(stream);
495 async_answer_0(callid, ret);
496 continue;
497 }
498 if (ret_answer != EOK) {
499 async_answer_0(callid, ret_answer);
500 continue;
501 }
502 char *buffer = malloc(size);
503 if (!buffer) {
504 async_answer_0(callid, ENOMEM);
505 continue;
506 }
507 const int ret = async_data_write_finalize(callid, buffer, size);
508 if (ret == EOK) {
509 ret_answer = server_iface->stream_data_write(
510 stream, buffer, size);
511 }
512 }
513 const int ret = IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_EXIT
514 ? EOK : EINVAL;
515
516 async_answer_0(callid, ret);
517}
518
519static void hound_server_write_data(void *stream)
520{
521
522 ipc_callid_t callid;
523 ipc_call_t call;
524 size_t size = 0;
525 int ret_answer = EOK;
526 while (async_data_read_receive_call(&callid, &call, &size)
527 || (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN)) {
528 if (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN) {
529 int ret = ENOTSUP;
530 if (server_iface->drain_stream)
531 ret = server_iface->drain_stream(stream);
532 async_answer_0(callid, ret);
533 continue;
534 }
535 if (ret_answer != EOK) {
536 async_answer_0(callid, ret_answer);
537 continue;
538 }
539 char *buffer = malloc(size);
540 if (!buffer) {
541 async_answer_0(callid, ENOMEM);
542 continue;
543 }
544 int ret = server_iface->stream_data_read(stream, buffer, size);
545 if (ret == EOK) {
546 ret_answer =
547 async_data_read_finalize(callid, buffer, size);
548 }
549 }
550 const int ret = IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_EXIT
551 ? EOK : EINVAL;
552
553 async_answer_0(callid, ret);
554}
555
556/***
557 * CLIENT SIDE -DEPRECATED
558 ***/
559
560typedef struct {
561 data_callback_t cb;
562 void *arg;
563} callback_t;
564
565hound_sess_t *hound_get_session(void)
566{
567 return hound_service_connect(HOUND_SERVICE);
568}
569
570void hound_release_session(hound_sess_t *sess)
571{
572 hound_service_disconnect(sess);
573}
574
575
576static int hound_register(hound_sess_t *sess, unsigned cmd, const char *name,
577 unsigned channels, unsigned rate, pcm_sample_format_t format,
578 data_callback_t data_callback, void *arg);
579static int hound_unregister(hound_sess_t *sess, unsigned cmd, const char *name);
580static int hound_connection(hound_sess_t *sess, unsigned cmd,
581 const char *source, const char *sink);
582static void callback_pb(ipc_callid_t iid, ipc_call_t *call, void *arg);
583static void callback_rec(ipc_callid_t iid, ipc_call_t *call, void *arg);
584
585
586int hound_register_playback(hound_sess_t *sess, const char *name,
587 unsigned channels, unsigned rate, pcm_sample_format_t format,
588 data_callback_t data_callback, void *arg)
589{
590 return hound_register(sess, HOUND_REGISTER_PLAYBACK, name, channels,
591 rate, format, data_callback, arg);
592}
593int hound_register_recording(hound_sess_t *sess, const char *name,
594 unsigned channels, unsigned rate, pcm_sample_format_t format,
595 data_callback_t data_callback, void *arg)
596{
597 return hound_register(sess, HOUND_REGISTER_RECORDING, name, channels,
598 rate, format, data_callback, arg);
599
600}
601int hound_unregister_playback(hound_sess_t *sess, const char *name)
602{
603 return hound_unregister(sess, HOUND_UNREGISTER_PLAYBACK, name);
604}
605int hound_unregister_recording(hound_sess_t *sess, const char *name)
606{
607 return hound_unregister(sess, HOUND_UNREGISTER_RECORDING, name);
608}
609int hound_create_connection(hound_sess_t *sess, const char *source, const char *sink)
610{
611 return hound_connection(sess, HOUND_CONNECT, source, sink);
612}
613int hound_destroy_connection(hound_sess_t *sess, const char *source, const char *sink)
614{
615 return hound_connection(sess, HOUND_DISCONNECT, source, sink);
616}
617
618static int hound_register(hound_sess_t *sess, unsigned cmd, const char *name,
619 unsigned channels, unsigned rate, pcm_sample_format_t format,
620 data_callback_t data_callback, void *arg)
621{
622 assert(cmd == HOUND_REGISTER_PLAYBACK || cmd == HOUND_REGISTER_RECORDING);
623 if (!name || !data_callback || !sess)
624 return EINVAL;
625
626 async_exch_t *exch = async_exchange_begin(sess);
627 if (!exch)
628 return ENOMEM;
629
630 aid_t id = async_send_0(exch, cmd, NULL);
631
632 int ret = async_data_write_start(exch, name, str_size(name));
633 if (ret != EOK) {
634 async_forget(id);
635 async_exchange_end(exch);
636 return ret;
637 }
638
639 callback_t *cb = malloc(sizeof(callback_t));
640 if (!cb) {
641 async_forget(id);
642 async_exchange_end(exch);
643 return ENOMEM;
644 }
645
646 cb->cb = data_callback;
647 cb->arg = arg;
648 async_client_conn_t callback =
649 (cmd == HOUND_REGISTER_PLAYBACK) ? callback_pb : callback_rec;
650
651 ret = async_connect_to_me(exch, channels, rate, format, callback, cb);
652 if (ret != EOK) {
653 async_forget(id);
654 async_exchange_end(exch);
655 free(cb);
656 return ret;
657 }
658
659 async_wait_for(id, (sysarg_t*)&ret);
660 if (ret != EOK) {
661 async_exchange_end(exch);
662 free(cb);
663 return ret;
664 }
665
666 async_exchange_end(exch);
667 return EOK;
668}
669
670static int hound_unregister(hound_sess_t *sess, unsigned cmd, const char *name)
671{
672 assert(cmd == HOUND_UNREGISTER_PLAYBACK || cmd == HOUND_UNREGISTER_RECORDING);
673 if (!name || !sess)
674 return EINVAL;
675
676 async_exch_t *exch = async_exchange_begin(sess);
677 if (!exch)
678 return ENOMEM;
679 aid_t id = async_send_0(exch, cmd, NULL);
680 sysarg_t ret = async_data_write_start(exch, name, str_size(name));
681 if (ret != EOK) {
682 async_forget(id);
683 async_exchange_end(exch);
684 return ret;
685 }
686
687 async_wait_for(id, &ret);
688 async_exchange_end(exch);
689 return ret;
690}
691
692static int hound_connection(hound_sess_t *sess, unsigned cmd,
693 const char *source, const char *sink)
694{
695 assert(cmd == HOUND_CONNECT || cmd == HOUND_DISCONNECT);
696 if (!source || !sink || !sess)
697 return EINVAL;
698
699 async_exch_t *exch = async_exchange_begin(sess);
700 if (!exch)
701 return ENOMEM;
702
703 aid_t id = async_send_0(exch, cmd, NULL);
704 sysarg_t ret = async_data_write_start(exch, source, str_size(source));
705 if (ret != EOK) {
706 async_forget(id);
707 async_exchange_end(exch);
708 return ret;
709 }
710 ret = async_data_write_start(exch, sink, str_size(sink));
711 if (ret != EOK) {
712 async_forget(id);
713 async_exchange_end(exch);
714 return ret;
715 }
716 async_wait_for(id, &ret);
717 async_exchange_end(exch);
718 return ret;
719}
720static void callback_gen(ipc_callid_t iid, ipc_call_t *call, void *arg,
721 bool read)
722{
723 async_answer_0(iid, EOK);
724 callback_t *cb = arg;
725 assert(cb);
726 void *buffer = NULL;
727 size_t buffer_size = 0;
728
729 bool (*receive)(ipc_callid_t *, size_t *) = read ?
730 async_data_read_receive : async_data_write_receive;
731
732 while (1) {
733 size_t size = 0;
734 ipc_callid_t id = 0;
735 if (!receive(&id, &size)) {
736 ipc_call_t failed_call;
737 async_get_call(&failed_call);
738 /* Protocol error or hangup */
739 if (IPC_GET_IMETHOD(failed_call) != 0)
740 cb->cb(cb->arg, NULL, EIO);
741 free(cb);
742 return;
743 }
744
745 if (buffer_size < size) {
746 buffer = realloc(buffer, size);
747 if (!buffer) {
748 cb->cb(cb->arg, NULL, ENOMEM);
749 free(cb);
750 return;
751 }
752 buffer_size = size;
753 }
754 if (read)
755 cb->cb(cb->arg, buffer, size);
756 const int ret = read ?
757 async_data_read_finalize(id, buffer, size):
758 async_data_write_finalize(id, buffer, size);
759 if (ret != EOK) {
760 cb->cb(cb->arg, NULL, ret);
761 free(cb);
762 return;
763 }
764 if (!read)
765 cb->cb(cb->arg, buffer, size);
766 }
767}
768
769static void callback_pb(ipc_callid_t iid, ipc_call_t *call, void *arg)
770{
771 callback_gen(iid, call, arg, true);
772}
773
774static void callback_rec(ipc_callid_t iid, ipc_call_t *call, void *arg)
775{
776 callback_gen(iid, call, arg, false);
777}
778
779/***
780 * SERVER SIDE - DEPRECATED
781 ***/
782
783static const char * get_name(void);
784
785int hound_server_register(const char *name, service_id_t *id)
786{
787 if (!name || !id)
788 return EINVAL;
789
790 int ret = loc_server_register(name);
791 if (ret != EOK)
792 return ret;
793
794 return loc_service_register(HOUND_SERVICE, id);
795}
796
797void hound_server_unregister(service_id_t id)
798{
799 loc_service_unregister(id);
800}
801
802int hound_server_set_device_change_callback(dev_change_callback_t cb)
803{
804 return loc_register_cat_change_cb(cb);
805}
806
807int hound_server_devices_iterate(device_callback_t callback)
808{
809 if (!callback)
810 return EINVAL;
811 static bool resolved = false;
812 static category_id_t cat_id = 0;
813
814 if (!resolved) {
815 const int ret = loc_category_get_id("audio-pcm", &cat_id,
816 IPC_FLAG_BLOCKING);
817 if (ret != EOK)
818 return ret;
819 resolved = true;
820 }
821
822 service_id_t *svcs = NULL;
823 size_t count = 0;
824 const int ret = loc_category_get_svcs(cat_id, &svcs, &count);
825 if (ret != EOK)
826 return ret;
827
828 for (unsigned i = 0; i < count; ++i) {
829 char *name = NULL;
830 loc_service_get_name(svcs[i], &name);
831 callback(svcs[i], name);
832 free(name);
833 }
834 free(svcs);
835 return EOK;
836}
837
838int hound_server_get_register_params(const char **name, async_sess_t **sess,
839 unsigned *channels, unsigned *rate, pcm_sample_format_t *format)
840{
841 if (!name || !sess || !channels || !rate || !format)
842 return EINVAL;
843
844 const char *n = get_name();
845 if (!n)
846 return ENOMEM;
847
848 ipc_call_t call;
849 ipc_callid_t callid = async_get_call(&call);
850
851 unsigned ch = IPC_GET_ARG1(call);
852 unsigned r = IPC_GET_ARG2(call);
853 pcm_sample_format_t f = IPC_GET_ARG3(call);
854
855 async_sess_t *s = async_callback_receive_start(EXCHANGE_ATOMIC, &call);
856 async_answer_0(callid, s ? EOK : ENOMEM);
857
858 *name = n;
859 *sess = s;
860 *channels = ch;
861 *rate = r;
862 *format = f;
863
864 return ENOTSUP;
865}
866
867int hound_server_get_unregister_params(const char **name)
868{
869 if (!name)
870 return EINVAL;
871 *name = get_name();
872 if (!*name)
873 return ENOMEM;
874 return EOK;
875}
876
877int hound_server_get_connection_params(const char **source, const char **sink)
878{
879 if (!source || !sink)
880 return EINVAL;
881
882 const char *source_name = get_name();
883 if (!source_name)
884 return ENOMEM;
885
886 const char *sink_name = get_name();
887 if (!sink_name)
888 return ENOMEM;
889
890 *source = source_name;
891 *sink = sink_name;
892 return EOK;
893}
894
895static const char * get_name(void)
896{
897 size_t size = 0;
898 ipc_callid_t callid;
899 async_data_write_receive(&callid, &size);
900 char *buffer = malloc(size + 1);
901 if (buffer) {
902 async_data_write_finalize(callid, buffer, size);
903 buffer[size] = 0;
904 }
905 return buffer;
906}
907
908/**
909 * @}
910 */
Note: See TracBrowser for help on using the repository browser.