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

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

libhound: Implement context unregister, disallow stream drain.

  • Property mode set to 100644
File size: 14.8 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 const 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_CONTEXT_UNREGISTER: {
186 if (!server_iface || !server_iface->rem_context) {
187 async_answer_0(callid, ENOTSUP);
188 break;
189 }
190 hound_context_id_t id = IPC_GET_ARG1(call);
191 const int ret =
192 server_iface->rem_context(server_iface->server, id);
193 async_answer_0(callid, ret);
194 }
195 case IPC_M_HOUND_STREAM_ENTER: {
196 if (!server_iface || !server_iface->add_stream) {
197 async_answer_0(callid, ENOTSUP);
198 break;
199 }
200
201 hound_context_id_t id = IPC_GET_ARG1(call);
202 int flags = IPC_GET_ARG2(call);
203 union {
204 sysarg_t arg;
205 pcm_format_t format;
206 } convert = { .arg = IPC_GET_ARG3(call) };
207 size_t bsize = IPC_GET_ARG4(call);
208 void *stream;
209 int ret = server_iface->add_stream(server_iface->server,
210 id, flags, convert.format, bsize, &stream);
211 if (ret != EOK) {
212 async_answer_0(callid, ret);
213 break;
214 }
215 //TODO consider record context
216 hound_server_read_data(stream);
217 break;
218 }
219 case IPC_M_HOUND_STREAM_EXIT:
220 case IPC_M_HOUND_STREAM_DRAIN:
221 /* Stream exit/drain is only allowed in stream context*/
222 async_answer_0(callid, EINVAL);
223 break;
224 default:
225 async_answer_0(callid, ENOTSUP);
226 return;
227 }
228 }
229}
230
231static int hound_server_read_data(void *stream)
232{
233 if (!server_iface || !server_iface->stream_data_write)
234 return ENOTSUP;
235
236 ipc_callid_t callid;
237 ipc_call_t call;
238 size_t size = 0;
239 while (async_data_write_receive_call(&callid, &call, &size)) {
240 char *buffer = malloc(size);
241 if (!buffer) {
242 async_answer_0(callid, ENOMEM);
243 continue;
244 }
245 int ret = async_data_write_finalize(callid, buffer, size);
246 if (ret == EOK) {
247 server_iface->stream_data_write(stream, buffer, size);
248 } else {
249 async_answer_0(callid, ret);
250 }
251 }
252 //TODO drain?
253 const int ret = IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_EXIT
254 ? EOK : EINVAL;
255
256 async_answer_0(callid, ret);
257 return ret;
258}
259
260/***
261 * CLIENT SIDE -DEPRECATED
262 ***/
263
264typedef struct {
265 data_callback_t cb;
266 void *arg;
267} callback_t;
268
269hound_sess_t *hound_get_session(void)
270{
271 return hound_service_connect(HOUND_SERVICE);
272}
273
274void hound_release_session(hound_sess_t *sess)
275{
276 hound_service_disconnect(sess);
277}
278
279
280static int hound_register(hound_sess_t *sess, unsigned cmd, const char *name,
281 unsigned channels, unsigned rate, pcm_sample_format_t format,
282 data_callback_t data_callback, void *arg);
283static int hound_unregister(hound_sess_t *sess, unsigned cmd, const char *name);
284static int hound_connection(hound_sess_t *sess, unsigned cmd,
285 const char *source, const char *sink);
286static void callback_pb(ipc_callid_t iid, ipc_call_t *call, void *arg);
287static void callback_rec(ipc_callid_t iid, ipc_call_t *call, void *arg);
288
289
290int hound_register_playback(hound_sess_t *sess, const char *name,
291 unsigned channels, unsigned rate, pcm_sample_format_t format,
292 data_callback_t data_callback, void *arg)
293{
294 return hound_register(sess, HOUND_REGISTER_PLAYBACK, name, channels,
295 rate, format, data_callback, arg);
296}
297int hound_register_recording(hound_sess_t *sess, const char *name,
298 unsigned channels, unsigned rate, pcm_sample_format_t format,
299 data_callback_t data_callback, void *arg)
300{
301 return hound_register(sess, HOUND_REGISTER_RECORDING, name, channels,
302 rate, format, data_callback, arg);
303
304}
305int hound_unregister_playback(hound_sess_t *sess, const char *name)
306{
307 return hound_unregister(sess, HOUND_UNREGISTER_PLAYBACK, name);
308}
309int hound_unregister_recording(hound_sess_t *sess, const char *name)
310{
311 return hound_unregister(sess, HOUND_UNREGISTER_RECORDING, name);
312}
313int hound_create_connection(hound_sess_t *sess, const char *source, const char *sink)
314{
315 return hound_connection(sess, HOUND_CONNECT, source, sink);
316}
317int hound_destroy_connection(hound_sess_t *sess, const char *source, const char *sink)
318{
319 return hound_connection(sess, HOUND_DISCONNECT, source, sink);
320}
321
322static int hound_register(hound_sess_t *sess, unsigned cmd, const char *name,
323 unsigned channels, unsigned rate, pcm_sample_format_t format,
324 data_callback_t data_callback, void *arg)
325{
326 assert(cmd == HOUND_REGISTER_PLAYBACK || cmd == HOUND_REGISTER_RECORDING);
327 if (!name || !data_callback || !sess)
328 return EINVAL;
329
330 async_exch_t *exch = async_exchange_begin(sess);
331 if (!exch)
332 return ENOMEM;
333
334 aid_t id = async_send_0(exch, cmd, NULL);
335
336 int ret = async_data_write_start(exch, name, str_size(name));
337 if (ret != EOK) {
338 async_forget(id);
339 async_exchange_end(exch);
340 return ret;
341 }
342
343 callback_t *cb = malloc(sizeof(callback_t));
344 if (!cb) {
345 async_forget(id);
346 async_exchange_end(exch);
347 return ENOMEM;
348 }
349
350 cb->cb = data_callback;
351 cb->arg = arg;
352 async_client_conn_t callback =
353 (cmd == HOUND_REGISTER_PLAYBACK) ? callback_pb : callback_rec;
354
355 ret = async_connect_to_me(exch, channels, rate, format, callback, cb);
356 if (ret != EOK) {
357 async_forget(id);
358 async_exchange_end(exch);
359 free(cb);
360 return ret;
361 }
362
363 async_wait_for(id, (sysarg_t*)&ret);
364 if (ret != EOK) {
365 async_exchange_end(exch);
366 free(cb);
367 return ret;
368 }
369
370 async_exchange_end(exch);
371 return EOK;
372}
373
374static int hound_unregister(hound_sess_t *sess, unsigned cmd, const char *name)
375{
376 assert(cmd == HOUND_UNREGISTER_PLAYBACK || cmd == HOUND_UNREGISTER_RECORDING);
377 if (!name || !sess)
378 return EINVAL;
379
380 async_exch_t *exch = async_exchange_begin(sess);
381 if (!exch)
382 return ENOMEM;
383 aid_t id = async_send_0(exch, cmd, NULL);
384 sysarg_t ret = async_data_write_start(exch, name, str_size(name));
385 if (ret != EOK) {
386 async_forget(id);
387 async_exchange_end(exch);
388 return ret;
389 }
390
391 async_wait_for(id, &ret);
392 async_exchange_end(exch);
393 return ret;
394}
395
396static int hound_connection(hound_sess_t *sess, unsigned cmd,
397 const char *source, const char *sink)
398{
399 assert(cmd == HOUND_CONNECT || cmd == HOUND_DISCONNECT);
400 if (!source || !sink || !sess)
401 return EINVAL;
402
403 async_exch_t *exch = async_exchange_begin(sess);
404 if (!exch)
405 return ENOMEM;
406
407 aid_t id = async_send_0(exch, cmd, NULL);
408 sysarg_t ret = async_data_write_start(exch, source, str_size(source));
409 if (ret != EOK) {
410 async_forget(id);
411 async_exchange_end(exch);
412 return ret;
413 }
414 ret = async_data_write_start(exch, sink, str_size(sink));
415 if (ret != EOK) {
416 async_forget(id);
417 async_exchange_end(exch);
418 return ret;
419 }
420 async_wait_for(id, &ret);
421 async_exchange_end(exch);
422 return ret;
423}
424static void callback_gen(ipc_callid_t iid, ipc_call_t *call, void *arg,
425 bool read)
426{
427 async_answer_0(iid, EOK);
428 callback_t *cb = arg;
429 assert(cb);
430 void *buffer = NULL;
431 size_t buffer_size = 0;
432
433 bool (*receive)(ipc_callid_t *, size_t *) = read ?
434 async_data_read_receive : async_data_write_receive;
435
436 while (1) {
437 size_t size = 0;
438 ipc_callid_t id = 0;
439 if (!receive(&id, &size)) {
440 ipc_call_t failed_call;
441 async_get_call(&failed_call);
442 /* Protocol error or hangup */
443 if (IPC_GET_IMETHOD(failed_call) != 0)
444 cb->cb(cb->arg, NULL, EIO);
445 free(cb);
446 return;
447 }
448
449 if (buffer_size < size) {
450 buffer = realloc(buffer, size);
451 if (!buffer) {
452 cb->cb(cb->arg, NULL, ENOMEM);
453 free(cb);
454 return;
455 }
456 buffer_size = size;
457 }
458 if (read)
459 cb->cb(cb->arg, buffer, size);
460 const int ret = read ?
461 async_data_read_finalize(id, buffer, size):
462 async_data_write_finalize(id, buffer, size);
463 if (ret != EOK) {
464 cb->cb(cb->arg, NULL, ret);
465 free(cb);
466 return;
467 }
468 if (!read)
469 cb->cb(cb->arg, buffer, size);
470 }
471}
472
473static void callback_pb(ipc_callid_t iid, ipc_call_t *call, void *arg)
474{
475 callback_gen(iid, call, arg, true);
476}
477
478static void callback_rec(ipc_callid_t iid, ipc_call_t *call, void *arg)
479{
480 callback_gen(iid, call, arg, false);
481}
482
483/***
484 * SERVER SIDE - DEPRECATED
485 ***/
486
487static const char * get_name(void);
488
489int hound_server_register(const char *name, service_id_t *id)
490{
491 if (!name || !id)
492 return EINVAL;
493
494 int ret = loc_server_register(name);
495 if (ret != EOK)
496 return ret;
497
498 return loc_service_register(HOUND_SERVICE, id);
499}
500
501void hound_server_unregister(service_id_t id)
502{
503 loc_service_unregister(id);
504}
505
506int hound_server_set_device_change_callback(dev_change_callback_t cb)
507{
508 return loc_register_cat_change_cb(cb);
509}
510
511int hound_server_devices_iterate(device_callback_t callback)
512{
513 if (!callback)
514 return EINVAL;
515 static bool resolved = false;
516 static category_id_t cat_id = 0;
517
518 if (!resolved) {
519 const int ret = loc_category_get_id("audio-pcm", &cat_id,
520 IPC_FLAG_BLOCKING);
521 if (ret != EOK)
522 return ret;
523 resolved = true;
524 }
525
526 service_id_t *svcs = NULL;
527 size_t count = 0;
528 const int ret = loc_category_get_svcs(cat_id, &svcs, &count);
529 if (ret != EOK)
530 return ret;
531
532 for (unsigned i = 0; i < count; ++i) {
533 char *name = NULL;
534 loc_service_get_name(svcs[i], &name);
535 callback(svcs[i], name);
536 free(name);
537 }
538 free(svcs);
539 return EOK;
540}
541
542int hound_server_get_register_params(const char **name, async_sess_t **sess,
543 unsigned *channels, unsigned *rate, pcm_sample_format_t *format)
544{
545 if (!name || !sess || !channels || !rate || !format)
546 return EINVAL;
547
548 const char *n = get_name();
549 if (!n)
550 return ENOMEM;
551
552 ipc_call_t call;
553 ipc_callid_t callid = async_get_call(&call);
554
555 unsigned ch = IPC_GET_ARG1(call);
556 unsigned r = IPC_GET_ARG2(call);
557 pcm_sample_format_t f = IPC_GET_ARG3(call);
558
559 async_sess_t *s = async_callback_receive_start(EXCHANGE_ATOMIC, &call);
560 async_answer_0(callid, s ? EOK : ENOMEM);
561
562 *name = n;
563 *sess = s;
564 *channels = ch;
565 *rate = r;
566 *format = f;
567
568 return ENOTSUP;
569}
570
571int hound_server_get_unregister_params(const char **name)
572{
573 if (!name)
574 return EINVAL;
575 *name = get_name();
576 if (!*name)
577 return ENOMEM;
578 return EOK;
579}
580
581int hound_server_get_connection_params(const char **source, const char **sink)
582{
583 if (!source || !sink)
584 return EINVAL;
585
586 const char *source_name = get_name();
587 if (!source_name)
588 return ENOMEM;
589
590 const char *sink_name = get_name();
591 if (!sink_name)
592 return ENOMEM;
593
594 *source = source_name;
595 *sink = sink_name;
596 return EOK;
597}
598
599static const char * get_name(void)
600{
601 size_t size = 0;
602 ipc_callid_t callid;
603 async_data_write_receive(&callid, &size);
604 char *buffer = malloc(size + 1);
605 if (buffer) {
606 async_data_write_finalize(callid, buffer, size);
607 buffer[size] = 0;
608 }
609 return buffer;
610}
611
612/**
613 * @}
614 */
Note: See TracBrowser for help on using the repository browser.