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

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

Fix printf compile issues

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