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

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

libhound: remove deprecated client interface

  • Property mode set to 100644
File size: 16.6 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 * SERVER SIDE - DEPRECATED
558 ***/
559
560static const char * get_name(void);
561
562int hound_server_register(const char *name, service_id_t *id)
563{
564 if (!name || !id)
565 return EINVAL;
566
567 int ret = loc_server_register(name);
568 if (ret != EOK)
569 return ret;
570
571 return loc_service_register(HOUND_SERVICE, id);
572}
573
574void hound_server_unregister(service_id_t id)
575{
576 loc_service_unregister(id);
577}
578
579int hound_server_set_device_change_callback(dev_change_callback_t cb)
580{
581 return loc_register_cat_change_cb(cb);
582}
583
584int hound_server_devices_iterate(device_callback_t callback)
585{
586 if (!callback)
587 return EINVAL;
588 static bool resolved = false;
589 static category_id_t cat_id = 0;
590
591 if (!resolved) {
592 const int ret = loc_category_get_id("audio-pcm", &cat_id,
593 IPC_FLAG_BLOCKING);
594 if (ret != EOK)
595 return ret;
596 resolved = true;
597 }
598
599 service_id_t *svcs = NULL;
600 size_t count = 0;
601 const int ret = loc_category_get_svcs(cat_id, &svcs, &count);
602 if (ret != EOK)
603 return ret;
604
605 for (unsigned i = 0; i < count; ++i) {
606 char *name = NULL;
607 loc_service_get_name(svcs[i], &name);
608 callback(svcs[i], name);
609 free(name);
610 }
611 free(svcs);
612 return EOK;
613}
614
615int hound_server_get_register_params(const char **name, async_sess_t **sess,
616 unsigned *channels, unsigned *rate, pcm_sample_format_t *format)
617{
618 if (!name || !sess || !channels || !rate || !format)
619 return EINVAL;
620
621 const char *n = get_name();
622 if (!n)
623 return ENOMEM;
624
625 ipc_call_t call;
626 ipc_callid_t callid = async_get_call(&call);
627
628 unsigned ch = IPC_GET_ARG1(call);
629 unsigned r = IPC_GET_ARG2(call);
630 pcm_sample_format_t f = IPC_GET_ARG3(call);
631
632 async_sess_t *s = async_callback_receive_start(EXCHANGE_ATOMIC, &call);
633 async_answer_0(callid, s ? EOK : ENOMEM);
634
635 *name = n;
636 *sess = s;
637 *channels = ch;
638 *rate = r;
639 *format = f;
640
641 return ENOTSUP;
642}
643
644int hound_server_get_unregister_params(const char **name)
645{
646 if (!name)
647 return EINVAL;
648 *name = get_name();
649 if (!*name)
650 return ENOMEM;
651 return EOK;
652}
653
654int hound_server_get_connection_params(const char **source, const char **sink)
655{
656 if (!source || !sink)
657 return EINVAL;
658
659 const char *source_name = get_name();
660 if (!source_name)
661 return ENOMEM;
662
663 const char *sink_name = get_name();
664 if (!sink_name)
665 return ENOMEM;
666
667 *source = source_name;
668 *sink = sink_name;
669 return EOK;
670}
671
672static const char * get_name(void)
673{
674 size_t size = 0;
675 ipc_callid_t callid;
676 async_data_write_receive(&callid, &size);
677 char *buffer = malloc(size + 1);
678 if (buffer) {
679 async_data_write_finalize(callid, buffer, size);
680 buffer[size] = 0;
681 }
682 return buffer;
683}
684
685/**
686 * @}
687 */
Note: See TracBrowser for help on using the repository browser.