source: mainline/uspace/lib/hound/src/client.c@ 537620a8

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

libhound: add stream drain function

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