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

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

libhound: Make protocol.h public, add server side iface skeleton.

Implement few more client side functions.

  • Property mode set to 100644
File size: 14.3 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 <str.h>
40#include <stdlib.h>
41#include <stdio.h>
42#include <libarch/types.h>
43
44#include "protocol.h"
45#include "client.h"
46#include "server.h"
47
48enum ipc_methods {
49 IPC_M_HOUND_CONTEXT_REGISTER = IPC_FIRST_USER_METHOD,
50 IPC_M_HOUND_CONTEXT_UNREGISTER,
51 IPC_M_HOUND_STREAM_ENTER,
52 IPC_M_HOUND_STREAM_EXIT,
53 IPC_M_HOUND_STREAM_DRAIN,
54};
55
56/****
57 * CLIENT
58 ****/
59
60const char *HOUND_SERVICE = "audio/hound";
61
62hound_sess_t *hound_service_connect(const char *service)
63{
64 service_id_t id = 0;
65 const int ret =
66 loc_service_get_id(service, &id, IPC_FLAG_BLOCKING);
67 if (ret != EOK)
68 return NULL;
69 return loc_service_connect(EXCHANGE_PARALLEL, id, IPC_FLAG_BLOCKING);
70}
71
72void hound_service_disconnect(hound_sess_t *sess)
73{
74 if (sess)
75 async_hangup(sess);
76}
77
78hound_context_id_t hound_service_register_context(hound_sess_t *sess,
79 const char *name, bool record)
80{
81 assert(sess);
82 assert(name);
83 async_exch_t *exch = async_exchange_begin(sess);
84 sysarg_t id;
85 int ret =
86 async_req_1_1(exch, IPC_M_HOUND_CONTEXT_REGISTER, record, &id);
87 if (ret == EOK)
88 ret = async_data_write_start(exch, name, str_size(name));
89 async_exchange_end(exch);
90 return ret == EOK ? (hound_context_id_t)id : ret;
91}
92
93int hound_service_unregister_context(hound_sess_t *sess, hound_context_id_t id)
94{
95 assert(sess);
96 async_exch_t *exch = async_exchange_begin(sess);
97 const int ret =
98 async_req_1_0(exch, IPC_M_HOUND_CONTEXT_UNREGISTER, id);
99 async_exchange_end(exch);
100 return ret;
101}
102
103int hound_service_stream_enter(async_exch_t *exch, hound_context_id_t id,
104 int flags, pcm_format_t format, size_t bsize)
105{
106 union {
107 sysarg_t arg;
108 pcm_format_t format;
109 } convert = { .format = format };
110 return async_req_4_0(exch, IPC_M_HOUND_STREAM_ENTER, id, flags,
111 convert.arg, bsize);
112}
113
114int hound_service_stream_exit(async_exch_t *exch)
115{
116 return async_req_0_0(exch, IPC_M_HOUND_STREAM_EXIT);
117}
118
119int hound_service_stream_drain(async_exch_t *exch)
120{
121 //TODO implement
122 return ENOTSUP;
123}
124
125int hound_service_stream_write(async_exch_t *exch, const void *data, size_t size)
126{
127 return async_data_write_start(exch, data, size);
128}
129
130int hound_service_stream_read(async_exch_t *exch, void *data, size_t size)
131{
132 //TODO implement
133 return ENOTSUP;
134}
135
136/****
137 * SERVER
138 ****/
139
140static int hound_server_read_data(void *stream);
141static hound_server_iface_t *server_iface;
142
143void hound_service_set_server_iface(hound_server_iface_t *iface)
144{
145 server_iface = iface;
146}
147
148void hound_connection_handler(ipc_callid_t iid, ipc_call_t *icall, void *arg)
149{
150 /* Accept connection if there is a valid iface*/
151 if (server_iface) {
152 async_answer_0(iid, EOK);
153 } else {
154 async_answer_0(iid, ENOTSUP);
155 return;
156 }
157
158 while (1) {
159 ipc_call_t call;
160 ipc_callid_t callid = async_get_call(&call);
161 switch(IPC_GET_IMETHOD(call)) {
162 case IPC_M_HOUND_CONTEXT_REGISTER: {
163 if (!server_iface || !server_iface->add_context) {
164 async_answer_0(callid, ENOTSUP);
165 break;
166 }
167 bool record = IPC_GET_ARG1(call);
168 void *name;
169 int ret =
170 async_data_write_accept(&name, true, 0, 0, 0, 0);
171 if (ret != EOK) {
172 async_answer_0(callid, ret);
173 break;
174 }
175 hound_context_id_t id = 0;
176 ret = server_iface->add_context(server_iface->server,
177 &id, name, record);
178 if (ret != EOK) {
179 free(name);
180 async_answer_0(callid, ret);
181 break;
182 }
183 async_answer_1(callid, EOK, id);
184 }
185 case IPC_M_HOUND_STREAM_ENTER: {
186 if (!server_iface || !server_iface->add_stream) {
187 async_answer_0(callid, ENOTSUP);
188 break;
189 }
190
191 hound_context_id_t id = IPC_GET_ARG1(call);
192 int flags = IPC_GET_ARG2(call);
193 union {
194 sysarg_t arg;
195 pcm_format_t format;
196 } convert = { .arg = IPC_GET_ARG3(call) };
197 size_t bsize = IPC_GET_ARG4(call);
198 void *stream;
199 int ret = server_iface->add_stream(server_iface->server,
200 id, flags, convert.format, bsize, &stream);
201 if (ret != EOK) {
202 async_answer_0(callid, ret);
203 break;
204 }
205 hound_server_read_data(stream);
206 break;
207 }
208 case IPC_M_HOUND_CONTEXT_UNREGISTER:
209 case IPC_M_HOUND_STREAM_EXIT:
210 case IPC_M_HOUND_STREAM_DRAIN:
211 default:
212 async_answer_0(callid, ENOTSUP);
213 return;
214 }
215 }
216}
217
218static int hound_server_read_data(void *stream)
219{
220 if (!server_iface || !server_iface->stream_data_write)
221 return ENOTSUP;
222
223 ipc_callid_t callid;
224 size_t size = 0;
225 while (async_data_write_receive(&callid, &size)) {
226 char *buffer = malloc(size);
227 if (!buffer) {
228 async_answer_0(callid, ENOMEM);
229 continue;
230 }
231 int ret = async_data_write_finalize(callid, buffer, size);
232 if (ret == EOK) {
233 server_iface->stream_data_write(stream, buffer, size);
234 } else {
235 // TODO did answering fail?
236 async_answer_0(callid, ret);
237 }
238 }
239 //TODO we assume that the fail was caused by IPC_M_HOUND_STREAM_EXIT
240 async_answer_0(callid, EOK);
241 return EOK;
242}
243
244/***
245 * CLIENT SIDE -DEPRECATED
246 ***/
247
248typedef struct {
249 data_callback_t cb;
250 void *arg;
251} callback_t;
252
253hound_sess_t *hound_get_session(void)
254{
255 return hound_service_connect(HOUND_SERVICE);
256}
257
258void hound_release_session(hound_sess_t *sess)
259{
260 hound_service_disconnect(sess);
261}
262
263
264static int hound_register(hound_sess_t *sess, unsigned cmd, const char *name,
265 unsigned channels, unsigned rate, pcm_sample_format_t format,
266 data_callback_t data_callback, void *arg);
267static int hound_unregister(hound_sess_t *sess, unsigned cmd, const char *name);
268static int hound_connection(hound_sess_t *sess, unsigned cmd,
269 const char *source, const char *sink);
270static void callback_pb(ipc_callid_t iid, ipc_call_t *call, void *arg);
271static void callback_rec(ipc_callid_t iid, ipc_call_t *call, void *arg);
272
273
274int hound_register_playback(hound_sess_t *sess, const char *name,
275 unsigned channels, unsigned rate, pcm_sample_format_t format,
276 data_callback_t data_callback, void *arg)
277{
278 return hound_register(sess, HOUND_REGISTER_PLAYBACK, name, channels,
279 rate, format, data_callback, arg);
280}
281int hound_register_recording(hound_sess_t *sess, const char *name,
282 unsigned channels, unsigned rate, pcm_sample_format_t format,
283 data_callback_t data_callback, void *arg)
284{
285 return hound_register(sess, HOUND_REGISTER_RECORDING, name, channels,
286 rate, format, data_callback, arg);
287
288}
289int hound_unregister_playback(hound_sess_t *sess, const char *name)
290{
291 return hound_unregister(sess, HOUND_UNREGISTER_PLAYBACK, name);
292}
293int hound_unregister_recording(hound_sess_t *sess, const char *name)
294{
295 return hound_unregister(sess, HOUND_UNREGISTER_RECORDING, name);
296}
297int hound_create_connection(hound_sess_t *sess, const char *source, const char *sink)
298{
299 return hound_connection(sess, HOUND_CONNECT, source, sink);
300}
301int hound_destroy_connection(hound_sess_t *sess, const char *source, const char *sink)
302{
303 return hound_connection(sess, HOUND_DISCONNECT, source, sink);
304}
305
306static int hound_register(hound_sess_t *sess, unsigned cmd, const char *name,
307 unsigned channels, unsigned rate, pcm_sample_format_t format,
308 data_callback_t data_callback, void *arg)
309{
310 assert(cmd == HOUND_REGISTER_PLAYBACK || cmd == HOUND_REGISTER_RECORDING);
311 if (!name || !data_callback || !sess)
312 return EINVAL;
313
314 async_exch_t *exch = async_exchange_begin(sess);
315 if (!exch)
316 return ENOMEM;
317
318 aid_t id = async_send_0(exch, cmd, NULL);
319
320 int ret = async_data_write_start(exch, name, str_size(name));
321 if (ret != EOK) {
322 async_forget(id);
323 async_exchange_end(exch);
324 return ret;
325 }
326
327 callback_t *cb = malloc(sizeof(callback_t));
328 if (!cb) {
329 async_forget(id);
330 async_exchange_end(exch);
331 return ENOMEM;
332 }
333
334 cb->cb = data_callback;
335 cb->arg = arg;
336 async_client_conn_t callback =
337 (cmd == HOUND_REGISTER_PLAYBACK) ? callback_pb : callback_rec;
338
339 ret = async_connect_to_me(exch, channels, rate, format, callback, cb);
340 if (ret != EOK) {
341 async_forget(id);
342 async_exchange_end(exch);
343 free(cb);
344 return ret;
345 }
346
347 async_wait_for(id, (sysarg_t*)&ret);
348 if (ret != EOK) {
349 async_exchange_end(exch);
350 free(cb);
351 return ret;
352 }
353
354 async_exchange_end(exch);
355 return EOK;
356}
357
358static int hound_unregister(hound_sess_t *sess, unsigned cmd, const char *name)
359{
360 assert(cmd == HOUND_UNREGISTER_PLAYBACK || cmd == HOUND_UNREGISTER_RECORDING);
361 if (!name || !sess)
362 return EINVAL;
363
364 async_exch_t *exch = async_exchange_begin(sess);
365 if (!exch)
366 return ENOMEM;
367 aid_t id = async_send_0(exch, cmd, NULL);
368 sysarg_t ret = async_data_write_start(exch, name, str_size(name));
369 if (ret != EOK) {
370 async_forget(id);
371 async_exchange_end(exch);
372 return ret;
373 }
374
375 async_wait_for(id, &ret);
376 async_exchange_end(exch);
377 return ret;
378}
379
380static int hound_connection(hound_sess_t *sess, unsigned cmd,
381 const char *source, const char *sink)
382{
383 assert(cmd == HOUND_CONNECT || cmd == HOUND_DISCONNECT);
384 if (!source || !sink || !sess)
385 return EINVAL;
386
387 async_exch_t *exch = async_exchange_begin(sess);
388 if (!exch)
389 return ENOMEM;
390
391 aid_t id = async_send_0(exch, cmd, NULL);
392 sysarg_t ret = async_data_write_start(exch, source, str_size(source));
393 if (ret != EOK) {
394 async_forget(id);
395 async_exchange_end(exch);
396 return ret;
397 }
398 ret = async_data_write_start(exch, sink, str_size(sink));
399 if (ret != EOK) {
400 async_forget(id);
401 async_exchange_end(exch);
402 return ret;
403 }
404 async_wait_for(id, &ret);
405 async_exchange_end(exch);
406 return ret;
407}
408static void callback_gen(ipc_callid_t iid, ipc_call_t *call, void *arg,
409 bool read)
410{
411 async_answer_0(iid, EOK);
412 callback_t *cb = arg;
413 assert(cb);
414 void *buffer = NULL;
415 size_t buffer_size = 0;
416
417 bool (*receive)(ipc_callid_t *, size_t *) = read ?
418 async_data_read_receive : async_data_write_receive;
419
420 while (1) {
421 size_t size = 0;
422 ipc_callid_t id = 0;
423 if (!receive(&id, &size)) {
424 ipc_call_t failed_call;
425 async_get_call(&failed_call);
426 /* Protocol error or hangup */
427 if (IPC_GET_IMETHOD(failed_call) != 0)
428 cb->cb(cb->arg, NULL, EIO);
429 free(cb);
430 return;
431 }
432
433 if (buffer_size < size) {
434 buffer = realloc(buffer, size);
435 if (!buffer) {
436 cb->cb(cb->arg, NULL, ENOMEM);
437 free(cb);
438 return;
439 }
440 buffer_size = size;
441 }
442 if (read)
443 cb->cb(cb->arg, buffer, size);
444 const int ret = read ?
445 async_data_read_finalize(id, buffer, size):
446 async_data_write_finalize(id, buffer, size);
447 if (ret != EOK) {
448 cb->cb(cb->arg, NULL, ret);
449 free(cb);
450 return;
451 }
452 if (!read)
453 cb->cb(cb->arg, buffer, size);
454 }
455}
456
457static void callback_pb(ipc_callid_t iid, ipc_call_t *call, void *arg)
458{
459 callback_gen(iid, call, arg, true);
460}
461
462static void callback_rec(ipc_callid_t iid, ipc_call_t *call, void *arg)
463{
464 callback_gen(iid, call, arg, false);
465}
466
467/***
468 * SERVER SIDE - DEPRECATED
469 ***/
470
471static const char * get_name(void);
472
473int hound_server_register(const char *name, service_id_t *id)
474{
475 if (!name || !id)
476 return EINVAL;
477
478 int ret = loc_server_register(name);
479 if (ret != EOK)
480 return ret;
481
482 return loc_service_register(HOUND_SERVICE, id);
483}
484
485void hound_server_unregister(service_id_t id)
486{
487 loc_service_unregister(id);
488}
489
490int hound_server_set_device_change_callback(dev_change_callback_t cb)
491{
492 return loc_register_cat_change_cb(cb);
493}
494
495int hound_server_devices_iterate(device_callback_t callback)
496{
497 if (!callback)
498 return EINVAL;
499 static bool resolved = false;
500 static category_id_t cat_id = 0;
501
502 if (!resolved) {
503 const int ret = loc_category_get_id("audio-pcm", &cat_id,
504 IPC_FLAG_BLOCKING);
505 if (ret != EOK)
506 return ret;
507 resolved = true;
508 }
509
510 service_id_t *svcs = NULL;
511 size_t count = 0;
512 const int ret = loc_category_get_svcs(cat_id, &svcs, &count);
513 if (ret != EOK)
514 return ret;
515
516 for (unsigned i = 0; i < count; ++i) {
517 char *name = NULL;
518 loc_service_get_name(svcs[i], &name);
519 callback(svcs[i], name);
520 free(name);
521 }
522 free(svcs);
523 return EOK;
524}
525
526int hound_server_get_register_params(const char **name, async_sess_t **sess,
527 unsigned *channels, unsigned *rate, pcm_sample_format_t *format)
528{
529 if (!name || !sess || !channels || !rate || !format)
530 return EINVAL;
531
532 const char *n = get_name();
533 if (!n)
534 return ENOMEM;
535
536 ipc_call_t call;
537 ipc_callid_t callid = async_get_call(&call);
538
539 unsigned ch = IPC_GET_ARG1(call);
540 unsigned r = IPC_GET_ARG2(call);
541 pcm_sample_format_t f = IPC_GET_ARG3(call);
542
543 async_sess_t *s = async_callback_receive_start(EXCHANGE_ATOMIC, &call);
544 async_answer_0(callid, s ? EOK : ENOMEM);
545
546 *name = n;
547 *sess = s;
548 *channels = ch;
549 *rate = r;
550 *format = f;
551
552 return ENOTSUP;
553}
554
555int hound_server_get_unregister_params(const char **name)
556{
557 if (!name)
558 return EINVAL;
559 *name = get_name();
560 if (!*name)
561 return ENOMEM;
562 return EOK;
563}
564
565int hound_server_get_connection_params(const char **source, const char **sink)
566{
567 if (!source || !sink)
568 return EINVAL;
569
570 const char *source_name = get_name();
571 if (!source_name)
572 return ENOMEM;
573
574 const char *sink_name = get_name();
575 if (!sink_name)
576 return ENOMEM;
577
578 *source = source_name;
579 *sink = sink_name;
580 return EOK;
581}
582
583static const char * get_name(void)
584{
585 size_t size = 0;
586 ipc_callid_t callid;
587 async_data_write_receive(&callid, &size);
588 char *buffer = malloc(size + 1);
589 if (buffer) {
590 async_data_write_finalize(callid, buffer, size);
591 buffer[size] = 0;
592 }
593 return buffer;
594}
595
596/**
597 * @}
598 */
Note: See TracBrowser for help on using the repository browser.