source: mainline/uspace/lib/hound/src/client.c@ 1c635d6

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1c635d6 was 1c635d6, checked in by Martin Sucha <sucha14@…>, 11 years ago

Do not hold a task's return value after it has disconnected.

Holding the task's return value meant that if nobody waited
for task's result, it polluted NS's memory. This was apparently
done because of a race between spawning a task and waiting for it.

We solve this problem in another way: ns discards the return value
as soon as the task disconnects from it. This typically happens
when the task finishes its execution. In order to avoid the race,
we send the wait request to ns while spawning the task (i.e. when
we talk to the loader), but before we allow the loaded program
to run.

Fixes #132

  • Property mode set to 100644
File size: 13.7 KB
RevLine 
[36774cf]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>
[e5bc912]38#include <inttypes.h>
[36774cf]39#include <loc.h>
40#include <str.h>
41#include <stdlib.h>
42#include <stdio.h>
43#include <libarch/types.h>
[1c635d6]44#include <task.h>
[36774cf]45
[059490c2]46#include "protocol.h"
[9e1800c]47#include "client.h"
[36774cf]48
[876f5561]49/** Stream structure */
[36774cf]50typedef struct hound_stream {
[876f5561]51 /** link in context's list */
[36774cf]52 link_t link;
[876f5561]53 /** audio data format fo the stream */
[9e1800c]54 pcm_format_t format;
[876f5561]55 /** IPC exchange representing the stream (in STREAM MODE) */
[36774cf]56 async_exch_t *exch;
[876f5561]57 /** parent context */
[9e1800c]58 hound_context_t *context;
[876f5561]59 /** Stream flags */
[c5d6f9cf]60 int flags;
[36774cf]61} hound_stream_t;
62
[876f5561]63/**
64 * Linked list isntacen helper function.
65 * @param l link
66 * @return hound stream isntance.
67 */
[cc3c27ad]68static inline hound_stream_t * hound_stream_from_link(link_t *l)
69{
70 return l ? list_get_instance(l, hound_stream_t, link) : NULL;
71}
72
[876f5561]73/** Hound client context structure */
[36774cf]74typedef struct hound_context {
[876f5561]75 /** Audio session */
[059490c2]76 hound_sess_t *session;
[876f5561]77 /** context name, reported to the daemon */
[36774cf]78 const char *name;
[876f5561]79 /** True if the instance is record context */
[36774cf]80 bool record;
[876f5561]81 /** List of associated streams */
[36774cf]82 list_t stream_list;
[876f5561]83 /** Main stream helper structure */
[36774cf]84 struct {
[9e1800c]85 hound_stream_t *stream;
86 pcm_format_t format;
[36774cf]87 size_t bsize;
[9e1800c]88 } main;
[876f5561]89 /** Assigned context id */
[9e1800c]90 hound_context_id_t id;
[36774cf]91} hound_context_t;
92
[876f5561]93/**
94 * Alloc and initialize context structure.
95 * @param name Base for the real context name, will add task id.
96 * @param record True if the new context should capture audio data.
97 * @param format PCM format of the main pipe.
98 * @param bsize Server size buffer size of the main stream.
99 * @return valid pointer to initialized structure on success, NULL on failure
100 */
[36774cf]101static hound_context_t *hound_context_create(const char *name, bool record,
[9e1800c]102 pcm_format_t format, size_t bsize)
[36774cf]103{
104 hound_context_t *new_context = malloc(sizeof(hound_context_t));
105 if (new_context) {
106 char *cont_name;
[e5bc912]107 int ret = asprintf(&cont_name, "%" PRIu64 ":%s",
108 task_get_id(), name);
[36774cf]109 if (ret < 0) {
110 free(new_context);
111 return NULL;
112 }
113 list_initialize(&new_context->stream_list);
114 new_context->name = cont_name;
115 new_context->record = record;
[059490c2]116 new_context->session = hound_service_connect(HOUND_SERVICE);
[9e1800c]117 new_context->main.stream = NULL;
118 new_context->main.format = format;
119 new_context->main.bsize = bsize;
[36774cf]120 if (!new_context->session) {
121 free(new_context->name);
122 free(new_context);
123 return NULL;
124 }
[9e1800c]125 new_context->id = hound_service_register_context(
[493817b0]126 new_context->session, new_context->name, record);
[7294b5b]127 if (hound_context_id_err(new_context->id) != EOK) {
[2ba4d45c]128 hound_service_disconnect(new_context->session);
[9e1800c]129 free(new_context->name);
130 free(new_context);
131 return NULL;
132 }
[36774cf]133 }
134 return new_context;
135}
136
[876f5561]137/**
138 * Playback context helper function.
139 * @param name Base for the real context name, will add task id.
140 * @param format PCM format of the main pipe.
141 * @param bsize Server size buffer size of the main stream.
142 * @return valid pointer to initialized structure on success, NULL on failure
143 */
[36774cf]144hound_context_t * hound_context_create_playback(const char *name,
[9e1800c]145 pcm_format_t format, size_t bsize)
[36774cf]146{
[9e1800c]147 return hound_context_create(name, false, format, bsize);
[36774cf]148}
149
[876f5561]150/**
151 * Record context helper function.
152 * @param name Base for the real context name, will add task id.
153 * @param format PCM format of the main pipe.
154 * @param bsize Server size buffer size of the main stream.
155 * @return valid pointer to initialized structure on success, NULL on failure
156 */
[36774cf]157hound_context_t * hound_context_create_capture(const char *name,
[9e1800c]158 pcm_format_t format, size_t bsize)
[36774cf]159{
[9e1800c]160 return hound_context_create(name, true, format, bsize);
[36774cf]161}
162
[876f5561]163/**
164 * Correctly dispose of the hound context structure.
165 * @param hound context to remove.
166 *
167 * The function will destroy all associated streams first. Pointers
168 * to these structures will become invalid and the function will block
169 * if any of these stream needs to be drained first.
170 */
[36774cf]171void hound_context_destroy(hound_context_t *hound)
172{
173 assert(hound);
[cc3c27ad]174
175 while (!list_empty(&hound->stream_list)) {
176 link_t *first = list_first(&hound->stream_list);
177 hound_stream_t *stream = hound_stream_from_link(first);
178 hound_stream_destroy(stream);
179 }
180
[9e1800c]181 hound_service_unregister_context(hound->session, hound->id);
182 hound_service_disconnect(hound->session);
183 free(hound->name);
184 free(hound);
[36774cf]185}
186
[876f5561]187/**
188 * Get a list of possible connection targets.
189 * @param[in] hound Hound context.
190 * @param[out] names list of target string ids.
191 * @param[out] count Number of elements in @p names list
192 * @return Error code.
193 *
194 * The function will return deice sinks or source based on the context type.
195 */
[7294b5b]196int hound_context_get_available_targets(hound_context_t *hound,
197 const char ***names, size_t *count)
[36774cf]198{
[7294b5b]199 assert(hound);
[36774cf]200 assert(names);
201 assert(count);
[7294b5b]202 return hound_service_get_list_all(hound->session, names, count,
203 hound->record ? HOUND_SOURCE_DEVS : HOUND_SINK_DEVS);
[36774cf]204}
205
[876f5561]206/**
207 * Get a list of targets connected to the context.
208 * @param[in] hound Hound context.
209 * @param[out] names list of target string ids.
210 * @param[out] count Number of elements in @p names list
211 * @return Error code.
212 */
[7294b5b]213int hound_context_get_connected_targets(hound_context_t *hound,
214 const char ***names, size_t *count)
[36774cf]215{
[7294b5b]216 assert(hound);
[36774cf]217 assert(names);
218 assert(count);
[7294b5b]219 return hound_service_get_list(hound->session, names, count,
220 HOUND_CONNECTED | (hound->record ?
221 HOUND_SOURCE_DEVS : HOUND_SINK_DEVS), hound->name);
[36774cf]222}
223
[876f5561]224/**
225 * Create a new connection to the target.
226 * @param hound Hound context.
227 * @param target String identifier of the desired target.
228 * @return Error code.
229 *
230 * The function recognizes special 'HOUND_DEFAULT_TARGET' and will
231 * connect to the first possible target if it is passed this value.
232 */
[36774cf]233int hound_context_connect_target(hound_context_t *hound, const char* target)
234{
235 assert(hound);
[ec81221]236 assert(target);
237
238 const char **tgt = NULL;
239 size_t count = 1;
240 int ret = EOK;
241 if (str_cmp(target, HOUND_DEFAULT_TARGET) == 0) {
242 ret = hound_context_get_available_targets(hound, &tgt, &count);
243 if (ret != EOK)
244 return ret;
245 target = tgt[0];
246 }
247 //TODO handle all-targets
248
249 if (hound->record) {
250 ret = hound_service_connect_source_sink(
[7294b5b]251 hound->session, target, hound->name);
[ec81221]252 } else {
253 ret = hound_service_connect_source_sink(
[7294b5b]254 hound->session, hound->name, target);
[ec81221]255 }
256 if (tgt)
257 free(tgt[0]);
258 free(tgt);
259 return ret;
[36774cf]260}
261
[876f5561]262/**
263 * Destroy a connection to the target.
264 * @param hound Hound context.
265 * @param target String identifier of the desired target.
266 * @return Error code.
267 */
[36774cf]268int hound_context_disconnect_target(hound_context_t *hound, const char* target)
269{
270 assert(hound);
[ec81221]271 assert(target);
[2ba4d45c]272 //TODO handle all-targets
[ec81221]273 if (hound->record) {
[7294b5b]274 return hound_service_disconnect_source_sink(
275 hound->session, target, hound->name);
[ec81221]276 } else {
[7294b5b]277 return hound_service_disconnect_source_sink(
278 hound->session, hound->name, target);
[ec81221]279 }
[36774cf]280}
281
[876f5561]282/**
283 * Create a new stream associated with the context.
284 * @param hound Hound context.
285 * @param flags new stream flags.
286 * @param format new stream PCM format.
287 * @param bzise new stream server side buffer size (in bytes)
288 * @return Valid pointer to a stream instance, NULL on failure.
289 */
[9e1800c]290hound_stream_t *hound_stream_create(hound_context_t *hound, unsigned flags,
291 pcm_format_t format, size_t bsize)
292{
293 assert(hound);
294 async_exch_t *stream_exch = async_exchange_begin(hound->session);
295 if (!stream_exch)
296 return NULL;
297 hound_stream_t *new_stream = malloc(sizeof(hound_stream_t));
298 if (new_stream) {
299 link_initialize(&new_stream->link);
300 new_stream->exch = stream_exch;
301 new_stream->format = format;
302 new_stream->context = hound;
[c5d6f9cf]303 new_stream->flags = flags;
[5bf4310]304 const int ret = hound_service_stream_enter(new_stream->exch,
[9e1800c]305 hound->id, flags, format, bsize);
306 if (ret != EOK) {
307 async_exchange_end(new_stream->exch);
308 free(new_stream);
309 return NULL;
310 }
311 list_append(&new_stream->link, &hound->stream_list);
312 }
313 return new_stream;
314}
315
[876f5561]316/**
317 * Destroy existing stream
318 * @param stream The stream to destroy.
319 *
320 * Function will wait until the server side buffer is empty if the
321 * HOUND_STREAM_DRAIN_ON_EXIT flag was set on creation.
322 */
[9e1800c]323void hound_stream_destroy(hound_stream_t *stream)
324{
325 if (stream) {
[c5d6f9cf]326 if (stream->flags & HOUND_STREAM_DRAIN_ON_EXIT)
327 hound_service_stream_drain(stream->exch);
[9e1800c]328 hound_service_stream_exit(stream->exch);
329 async_exchange_end(stream->exch);
330 list_remove(&stream->link);
331 free(stream);
332 }
333}
334
[876f5561]335/**
336 * Send new data to a stream.
337 * @param stream The target stream
338 * @param data data buffer
339 * @param size size of the @p data buffer.
340 * @return error code.
341 */
[9e1800c]342int hound_stream_write(hound_stream_t *stream, const void *data, size_t size)
343{
344 assert(stream);
345 if (!data || size == 0)
346 return EBADMEM;
347 return hound_service_stream_write(stream->exch, data, size);
348}
349
[876f5561]350/**
351 * Get data from a stream.
352 * @param stream The target stream.
353 * @param data data buffer.
354 * @param size size of the @p data buffer.
355 * @return error code.
356 */
[9e1800c]357int hound_stream_read(hound_stream_t *stream, void *data, size_t size)
358{
359 assert(stream);
360 if (!data || size == 0)
361 return EBADMEM;
362 return hound_service_stream_read(stream->exch, data, size);
363}
364
[4e72a4c]365/**
366 * Wait until the server side buffer is empty.
367 * @param stream The stream that shoulod be drained.
368 * @return Error code.
369 */
370int hound_stream_drain(hound_stream_t *stream)
371{
372 assert(stream);
373 return hound_service_stream_drain(stream->exch);
374}
375
[876f5561]376/**
377 * Main stream getter function.
378 * @param hound Houndcontext.
379 * @return Valid stream pointer, NULL on failure.
380 *
381 * The function will create new stream, or return a pointer to the exiting one
382 * if it exists.
383 */
[9e1800c]384static hound_stream_t * hound_get_main_stream(hound_context_t *hound)
385{
386 assert(hound);
387 if (!hound->main.stream)
[c5d6f9cf]388 hound->main.stream = hound_stream_create(hound,
389 HOUND_STREAM_DRAIN_ON_EXIT,hound->main.format,
390 hound->main.bsize);
[9e1800c]391 return hound->main.stream;
392}
393
[876f5561]394/**
395 * Send new data to the main stream.
396 * @param stream The target stream
397 * @param data data buffer
398 * @param size size of the @p data buffer.
399 * @return error code.
400 */
[36774cf]401int hound_write_main_stream(hound_context_t *hound,
[9e1800c]402 const void *data, size_t size)
403{
404 assert(hound);
[876f5561]405 if (hound->record)
406 return EINVAL;
407
[9e1800c]408 hound_stream_t *mstream = hound_get_main_stream(hound);
409 if (!mstream)
410 return ENOMEM;
411 return hound_stream_write(mstream, data, size);
412}
413
[876f5561]414/**
415 * Get data from the main stream.
416 * @param stream The target stream
417 * @param data data buffer
418 * @param size size of the @p data buffer.
419 * @return error code.
420 */
[9e1800c]421int hound_read_main_stream(hound_context_t *hound, void *data, size_t size)
422{
423 assert(hound);
[876f5561]424 if (!hound->record)
425 return EINVAL;
[9e1800c]426 hound_stream_t *mstream = hound_get_main_stream(hound);
427 if (!mstream)
428 return ENOMEM;
429 return hound_stream_read(mstream, data, size);
430}
431
[876f5561]432/**
433 * Destroy the old main stream and replace it with a new one with fresh data.
434 * @param hound Hound context.
435 * @param data data buffer.
436 * @param size size of the @p data buffer.
437 * @return error code.
438 *
439 * NOT IMPLEMENTED
440 */
[36774cf]441int hound_write_replace_main_stream(hound_context_t *hound,
[9e1800c]442 const void *data, size_t size)
443{
444 assert(hound);
445 if (!data || size == 0)
446 return EBADMEM;
447 // TODO implement
448 return ENOTSUP;
449}
450
[876f5561]451/**
452 * Destroy the old main stream and replace it with a new one using new params.
453 * @param hound Hound context.
454 * @param channels
455 * @return error code.
456 *
457 * NOT IMPLEMENTED
458 */
459int hound_context_set_main_stream_params(hound_context_t *hound,
460 pcm_format_t format, size_t bsize)
[9e1800c]461{
462 assert(hound);
463 // TODO implement
464 return ENOTSUP;
465}
466
[876f5561]467/**
468 * Write data immediately to a new stream, and wait for it to drain.
469 * @param hound Hound context.
470 * @param format pcm data format.
471 * @param data data buffer
472 * @param size @p data buffer size
473 * @return Error code.
474 *
475 * This functnion creates a new stream writes the data, ti waits for the stream
476 * to drain and destroys it before returning.
477 */
[9e1800c]478int hound_write_immediate(hound_context_t *hound, pcm_format_t format,
479 const void *data, size_t size)
480{
481 assert(hound);
[876f5561]482 if (hound->record)
483 return EINVAL;
[9e1800c]484 hound_stream_t *tmpstream = hound_stream_create(hound, 0, format, size);
485 if (!tmpstream)
486 return ENOMEM;
487 const int ret = hound_stream_write(tmpstream, data, size);
488 if (ret == EOK) {
489 //TODO drain?
490 hound_service_stream_drain(tmpstream->exch);
491 }
492 hound_stream_destroy(tmpstream);
493 return ret;
494}
[36774cf]495/**
496 * @}
497 */
Note: See TracBrowser for help on using the repository browser.