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
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
47/** Stream structure */
48typedef struct hound_stream {
49 /** link in context's list */
50 link_t link;
51 /** audio data format fo the stream */
52 pcm_format_t format;
53 /** IPC exchange representing the stream (in STREAM MODE) */
54 async_exch_t *exch;
55 /** parent context */
56 hound_context_t *context;
57 /** Stream flags */
58 int flags;
59} hound_stream_t;
60
61/**
62 * Linked list isntacen helper function.
63 * @param l link
64 * @return hound stream isntance.
65 */
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
71/** Hound client context structure */
72typedef struct hound_context {
73 /** Audio session */
74 hound_sess_t *session;
75 /** context name, reported to the daemon */
76 const char *name;
77 /** True if the instance is record context */
78 bool record;
79 /** List of associated streams */
80 list_t stream_list;
81 /** Main stream helper structure */
82 struct {
83 hound_stream_t *stream;
84 pcm_format_t format;
85 size_t bsize;
86 } main;
87 /** Assigned context id */
88 hound_context_id_t id;
89} hound_context_t;
90
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 */
99static hound_context_t *hound_context_create(const char *name, bool record,
100 pcm_format_t format, size_t bsize)
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;
113 new_context->session = hound_service_connect(HOUND_SERVICE);
114 new_context->main.stream = NULL;
115 new_context->main.format = format;
116 new_context->main.bsize = bsize;
117 if (!new_context->session) {
118 free(new_context->name);
119 free(new_context);
120 return NULL;
121 }
122 new_context->id = hound_service_register_context(
123 new_context->session, new_context->name, record);
124 if (hound_context_id_err(new_context->id) != EOK) {
125 hound_service_disconnect(new_context->session);
126 free(new_context->name);
127 free(new_context);
128 return NULL;
129 }
130 }
131 return new_context;
132}
133
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 */
141hound_context_t * hound_context_create_playback(const char *name,
142 pcm_format_t format, size_t bsize)
143{
144 return hound_context_create(name, false, format, bsize);
145}
146
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 */
154hound_context_t * hound_context_create_capture(const char *name,
155 pcm_format_t format, size_t bsize)
156{
157 return hound_context_create(name, true, format, bsize);
158}
159
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 */
168void hound_context_destroy(hound_context_t *hound)
169{
170 assert(hound);
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
178 hound_service_unregister_context(hound->session, hound->id);
179 hound_service_disconnect(hound->session);
180 free(hound->name);
181 free(hound);
182}
183
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 */
193int hound_context_get_available_targets(hound_context_t *hound,
194 const char ***names, size_t *count)
195{
196 assert(hound);
197 assert(names);
198 assert(count);
199 return hound_service_get_list_all(hound->session, names, count,
200 hound->record ? HOUND_SOURCE_DEVS : HOUND_SINK_DEVS);
201}
202
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 */
210int hound_context_get_connected_targets(hound_context_t *hound,
211 const char ***names, size_t *count)
212{
213 assert(hound);
214 assert(names);
215 assert(count);
216 return hound_service_get_list(hound->session, names, count,
217 HOUND_CONNECTED | (hound->record ?
218 HOUND_SOURCE_DEVS : HOUND_SINK_DEVS), hound->name);
219}
220
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 */
230int hound_context_connect_target(hound_context_t *hound, const char* target)
231{
232 assert(hound);
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(
248 hound->session, target, hound->name);
249 } else {
250 ret = hound_service_connect_source_sink(
251 hound->session, hound->name, target);
252 }
253 if (tgt)
254 free(tgt[0]);
255 free(tgt);
256 return ret;
257}
258
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 */
265int hound_context_disconnect_target(hound_context_t *hound, const char* target)
266{
267 assert(hound);
268 assert(target);
269 //TODO handle all-targets
270 if (hound->record) {
271 return hound_service_disconnect_source_sink(
272 hound->session, target, hound->name);
273 } else {
274 return hound_service_disconnect_source_sink(
275 hound->session, hound->name, target);
276 }
277}
278
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 */
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;
300 new_stream->flags = flags;
301 const int ret = hound_service_stream_enter(new_stream->exch,
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
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 */
320void hound_stream_destroy(hound_stream_t *stream)
321{
322 if (stream) {
323 if (stream->flags & HOUND_STREAM_DRAIN_ON_EXIT)
324 hound_service_stream_drain(stream->exch);
325 hound_service_stream_exit(stream->exch);
326 async_exchange_end(stream->exch);
327 list_remove(&stream->link);
328 free(stream);
329 }
330}
331
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 */
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
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 */
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
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
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 */
381static hound_stream_t * hound_get_main_stream(hound_context_t *hound)
382{
383 assert(hound);
384 if (!hound->main.stream)
385 hound->main.stream = hound_stream_create(hound,
386 HOUND_STREAM_DRAIN_ON_EXIT,hound->main.format,
387 hound->main.bsize);
388 return hound->main.stream;
389}
390
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 */
398int hound_write_main_stream(hound_context_t *hound,
399 const void *data, size_t size)
400{
401 assert(hound);
402 if (hound->record)
403 return EINVAL;
404
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
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 */
418int hound_read_main_stream(hound_context_t *hound, void *data, size_t size)
419{
420 assert(hound);
421 if (!hound->record)
422 return EINVAL;
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
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 */
438int hound_write_replace_main_stream(hound_context_t *hound,
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
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)
458{
459 assert(hound);
460 // TODO implement
461 return ENOTSUP;
462}
463
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 */
475int hound_write_immediate(hound_context_t *hound, pcm_format_t format,
476 const void *data, size_t size)
477{
478 assert(hound);
479 if (hound->record)
480 return EINVAL;
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}
492/**
493 * @}
494 */
Note: See TracBrowser for help on using the repository browser.