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

Last change on this file since ca48672 was 1433ecda, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Fix cstyle: make ccheck-fix and commit only files where all the changes are good.

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