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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 07e28f8 was 07e28f8, checked in by Jiri Svoboda <jiri@…>, 11 years ago

Do not crash when there is no hound target.

  • Property mode set to 100644
File size: 13.7 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#include <task.h>
45
46#include "protocol.h"
47#include "client.h"
48
49/** Stream structure */
50typedef struct hound_stream {
51 /** link in context's list */
52 link_t link;
53 /** audio data format fo the stream */
54 pcm_format_t format;
55 /** IPC exchange representing the stream (in STREAM MODE) */
56 async_exch_t *exch;
57 /** parent context */
58 hound_context_t *context;
59 /** Stream flags */
60 int flags;
61} hound_stream_t;
62
63/**
64 * Linked list isntacen helper function.
65 * @param l link
66 * @return hound stream isntance.
67 */
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
73/** Hound client context structure */
74typedef struct hound_context {
75 /** Audio session */
76 hound_sess_t *session;
77 /** context name, reported to the daemon */
78 const char *name;
79 /** True if the instance is record context */
80 bool record;
81 /** List of associated streams */
82 list_t stream_list;
83 /** Main stream helper structure */
84 struct {
85 hound_stream_t *stream;
86 pcm_format_t format;
87 size_t bsize;
88 } main;
89 /** Assigned context id */
90 hound_context_id_t id;
91} hound_context_t;
92
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 */
101static hound_context_t *hound_context_create(const char *name, bool record,
102 pcm_format_t format, size_t bsize)
103{
104 hound_context_t *new_context = malloc(sizeof(hound_context_t));
105 if (new_context) {
106 char *cont_name;
107 int ret = asprintf(&cont_name, "%" PRIu64 ":%s",
108 task_get_id(), name);
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;
116 new_context->session = hound_service_connect(HOUND_SERVICE);
117 new_context->main.stream = NULL;
118 new_context->main.format = format;
119 new_context->main.bsize = bsize;
120 if (!new_context->session) {
121 free(new_context->name);
122 free(new_context);
123 return NULL;
124 }
125 new_context->id = hound_service_register_context(
126 new_context->session, new_context->name, record);
127 if (hound_context_id_err(new_context->id) != EOK) {
128 hound_service_disconnect(new_context->session);
129 free(new_context->name);
130 free(new_context);
131 return NULL;
132 }
133 }
134 return new_context;
135}
136
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 */
144hound_context_t * hound_context_create_playback(const char *name,
145 pcm_format_t format, size_t bsize)
146{
147 return hound_context_create(name, false, format, bsize);
148}
149
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 */
157hound_context_t * hound_context_create_capture(const char *name,
158 pcm_format_t format, size_t bsize)
159{
160 return hound_context_create(name, true, format, bsize);
161}
162
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 */
171void hound_context_destroy(hound_context_t *hound)
172{
173 assert(hound);
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
181 hound_service_unregister_context(hound->session, hound->id);
182 hound_service_disconnect(hound->session);
183 free(hound->name);
184 free(hound);
185}
186
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 */
196int hound_context_get_available_targets(hound_context_t *hound,
197 const char ***names, size_t *count)
198{
199 assert(hound);
200 assert(names);
201 assert(count);
202 return hound_service_get_list_all(hound->session, names, count,
203 hound->record ? HOUND_SOURCE_DEVS : HOUND_SINK_DEVS);
204}
205
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 */
213int hound_context_get_connected_targets(hound_context_t *hound,
214 const char ***names, size_t *count)
215{
216 assert(hound);
217 assert(names);
218 assert(count);
219 return hound_service_get_list(hound->session, names, count,
220 HOUND_CONNECTED | (hound->record ?
221 HOUND_SOURCE_DEVS : HOUND_SINK_DEVS), hound->name);
222}
223
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 */
233int hound_context_connect_target(hound_context_t *hound, const char* target)
234{
235 assert(hound);
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 if (count == 0)
246 return ENOENT;
247 target = tgt[0];
248 }
249 //TODO handle all-targets
250
251 if (hound->record) {
252 ret = hound_service_connect_source_sink(
253 hound->session, target, hound->name);
254 } else {
255 ret = hound_service_connect_source_sink(
256 hound->session, hound->name, target);
257 }
258 if (tgt)
259 free(tgt[0]);
260 free(tgt);
261 return ret;
262}
263
264/**
265 * Destroy a connection to the target.
266 * @param hound Hound context.
267 * @param target String identifier of the desired target.
268 * @return Error code.
269 */
270int hound_context_disconnect_target(hound_context_t *hound, const char* target)
271{
272 assert(hound);
273 assert(target);
274 //TODO handle all-targets
275 if (hound->record) {
276 return hound_service_disconnect_source_sink(
277 hound->session, target, hound->name);
278 } else {
279 return hound_service_disconnect_source_sink(
280 hound->session, hound->name, target);
281 }
282}
283
284/**
285 * Create a new stream associated with the context.
286 * @param hound Hound context.
287 * @param flags new stream flags.
288 * @param format new stream PCM format.
289 * @param bzise new stream server side buffer size (in bytes)
290 * @return Valid pointer to a stream instance, NULL on failure.
291 */
292hound_stream_t *hound_stream_create(hound_context_t *hound, unsigned flags,
293 pcm_format_t format, size_t bsize)
294{
295 assert(hound);
296 async_exch_t *stream_exch = async_exchange_begin(hound->session);
297 if (!stream_exch)
298 return NULL;
299 hound_stream_t *new_stream = malloc(sizeof(hound_stream_t));
300 if (new_stream) {
301 link_initialize(&new_stream->link);
302 new_stream->exch = stream_exch;
303 new_stream->format = format;
304 new_stream->context = hound;
305 new_stream->flags = flags;
306 const int ret = hound_service_stream_enter(new_stream->exch,
307 hound->id, flags, format, bsize);
308 if (ret != EOK) {
309 async_exchange_end(new_stream->exch);
310 free(new_stream);
311 return NULL;
312 }
313 list_append(&new_stream->link, &hound->stream_list);
314 }
315 return new_stream;
316}
317
318/**
319 * Destroy existing stream
320 * @param stream The stream to destroy.
321 *
322 * Function will wait until the server side buffer is empty if the
323 * HOUND_STREAM_DRAIN_ON_EXIT flag was set on creation.
324 */
325void hound_stream_destroy(hound_stream_t *stream)
326{
327 if (stream) {
328 if (stream->flags & HOUND_STREAM_DRAIN_ON_EXIT)
329 hound_service_stream_drain(stream->exch);
330 hound_service_stream_exit(stream->exch);
331 async_exchange_end(stream->exch);
332 list_remove(&stream->link);
333 free(stream);
334 }
335}
336
337/**
338 * Send new data to a stream.
339 * @param stream The target stream
340 * @param data data buffer
341 * @param size size of the @p data buffer.
342 * @return error code.
343 */
344int hound_stream_write(hound_stream_t *stream, const void *data, size_t size)
345{
346 assert(stream);
347 if (!data || size == 0)
348 return EBADMEM;
349 return hound_service_stream_write(stream->exch, data, size);
350}
351
352/**
353 * Get data from a stream.
354 * @param stream The target stream.
355 * @param data data buffer.
356 * @param size size of the @p data buffer.
357 * @return error code.
358 */
359int hound_stream_read(hound_stream_t *stream, void *data, size_t size)
360{
361 assert(stream);
362 if (!data || size == 0)
363 return EBADMEM;
364 return hound_service_stream_read(stream->exch, data, size);
365}
366
367/**
368 * Wait until the server side buffer is empty.
369 * @param stream The stream that shoulod be drained.
370 * @return Error code.
371 */
372int hound_stream_drain(hound_stream_t *stream)
373{
374 assert(stream);
375 return hound_service_stream_drain(stream->exch);
376}
377
378/**
379 * Main stream getter function.
380 * @param hound Houndcontext.
381 * @return Valid stream pointer, NULL on failure.
382 *
383 * The function will create new stream, or return a pointer to the exiting one
384 * if it exists.
385 */
386static hound_stream_t * hound_get_main_stream(hound_context_t *hound)
387{
388 assert(hound);
389 if (!hound->main.stream)
390 hound->main.stream = hound_stream_create(hound,
391 HOUND_STREAM_DRAIN_ON_EXIT,hound->main.format,
392 hound->main.bsize);
393 return hound->main.stream;
394}
395
396/**
397 * Send new data to the main stream.
398 * @param stream The target stream
399 * @param data data buffer
400 * @param size size of the @p data buffer.
401 * @return error code.
402 */
403int hound_write_main_stream(hound_context_t *hound,
404 const void *data, size_t size)
405{
406 assert(hound);
407 if (hound->record)
408 return EINVAL;
409
410 hound_stream_t *mstream = hound_get_main_stream(hound);
411 if (!mstream)
412 return ENOMEM;
413 return hound_stream_write(mstream, data, size);
414}
415
416/**
417 * Get data from the main stream.
418 * @param stream The target stream
419 * @param data data buffer
420 * @param size size of the @p data buffer.
421 * @return error code.
422 */
423int hound_read_main_stream(hound_context_t *hound, void *data, size_t size)
424{
425 assert(hound);
426 if (!hound->record)
427 return EINVAL;
428 hound_stream_t *mstream = hound_get_main_stream(hound);
429 if (!mstream)
430 return ENOMEM;
431 return hound_stream_read(mstream, data, size);
432}
433
434/**
435 * Destroy the old main stream and replace it with a new one with fresh data.
436 * @param hound Hound context.
437 * @param data data buffer.
438 * @param size size of the @p data buffer.
439 * @return error code.
440 *
441 * NOT IMPLEMENTED
442 */
443int hound_write_replace_main_stream(hound_context_t *hound,
444 const void *data, size_t size)
445{
446 assert(hound);
447 if (!data || size == 0)
448 return EBADMEM;
449 // TODO implement
450 return ENOTSUP;
451}
452
453/**
454 * Destroy the old main stream and replace it with a new one using new params.
455 * @param hound Hound context.
456 * @param channels
457 * @return error code.
458 *
459 * NOT IMPLEMENTED
460 */
461int hound_context_set_main_stream_params(hound_context_t *hound,
462 pcm_format_t format, size_t bsize)
463{
464 assert(hound);
465 // TODO implement
466 return ENOTSUP;
467}
468
469/**
470 * Write data immediately to a new stream, and wait for it to drain.
471 * @param hound Hound context.
472 * @param format pcm data format.
473 * @param data data buffer
474 * @param size @p data buffer size
475 * @return Error code.
476 *
477 * This functnion creates a new stream writes the data, ti waits for the stream
478 * to drain and destroys it before returning.
479 */
480int hound_write_immediate(hound_context_t *hound, pcm_format_t format,
481 const void *data, size_t size)
482{
483 assert(hound);
484 if (hound->record)
485 return EINVAL;
486 hound_stream_t *tmpstream = hound_stream_create(hound, 0, format, size);
487 if (!tmpstream)
488 return ENOMEM;
489 const int ret = hound_stream_write(tmpstream, data, size);
490 if (ret == EOK) {
491 //TODO drain?
492 hound_service_stream_drain(tmpstream->exch);
493 }
494 hound_stream_destroy(tmpstream);
495 return ret;
496}
497/**
498 * @}
499 */
Note: See TracBrowser for help on using the repository browser.