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
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>
[e5bc912]38#include <inttypes.h>
[36774cf]39#include <loc.h>
40#include <str.h>
41#include <stdlib.h>
42#include <stdio.h>
[cf13b17]43#include <types/common.h>
[1c635d6]44#include <task.h>
[36774cf]45
[059490c2]46#include "protocol.h"
[9e1800c]47#include "client.h"
[36774cf]48
[876f5561]49/** Stream structure */
[05b59393]50struct hound_stream {
[876f5561]51 /** link in context's list */
[36774cf]52 link_t link;
[876f5561]53 /** audio data format fo the stream */
[9e1800c]54 pcm_format_t format;
[876f5561]55 /** IPC exchange representing the stream (in STREAM MODE) */
[36774cf]56 async_exch_t *exch;
[876f5561]57 /** parent context */
[9e1800c]58 hound_context_t *context;
[876f5561]59 /** Stream flags */
[c5d6f9cf]60 int flags;
[05b59393]61};
[36774cf]62
[876f5561]63/**
64 * Linked list isntacen helper function.
65 * @param l link
66 * @return hound stream isntance.
67 */
[1433ecda]68static inline hound_stream_t *hound_stream_from_link(link_t *l)
[cc3c27ad]69{
70 return l ? list_get_instance(l, hound_stream_t, link) : NULL;
71}
72
[876f5561]73/** Hound client context structure */
[05b59393]74struct hound_context {
[876f5561]75 /** Audio session */
[059490c2]76 hound_sess_t *session;
[876f5561]77 /** context name, reported to the daemon */
[33b8d024]78 char *name;
[876f5561]79 /** True if the instance is record context */
[36774cf]80 bool record;
[876f5561]81 /** List of associated streams */
[36774cf]82 list_t stream_list;
[876f5561]83 /** Main stream helper structure */
[36774cf]84 struct {
[9e1800c]85 hound_stream_t *stream;
86 pcm_format_t format;
[36774cf]87 size_t bsize;
[9e1800c]88 } main;
[876f5561]89 /** Assigned context id */
[9e1800c]90 hound_context_id_t id;
[05b59393]91};
[36774cf]92
[876f5561]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 */
[36774cf]101static hound_context_t *hound_context_create(const char *name, bool record,
[9e1800c]102 pcm_format_t format, size_t bsize)
[36774cf]103{
104 hound_context_t *new_context = malloc(sizeof(hound_context_t));
105 if (new_context) {
106 char *cont_name;
[e5bc912]107 int ret = asprintf(&cont_name, "%" PRIu64 ":%s",
108 task_get_id(), name);
[36774cf]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;
[059490c2]116 new_context->session = hound_service_connect(HOUND_SERVICE);
[9e1800c]117 new_context->main.stream = NULL;
118 new_context->main.format = format;
119 new_context->main.bsize = bsize;
[36774cf]120 if (!new_context->session) {
121 free(new_context->name);
122 free(new_context);
123 return NULL;
124 }
[b7fd2a0]125 errno_t rc = hound_service_register_context(
[fed5a9b]126 new_context->session, new_context->name, record,
127 &new_context->id);
128 if (rc != EOK) {
[2ba4d45c]129 hound_service_disconnect(new_context->session);
[9e1800c]130 free(new_context->name);
131 free(new_context);
132 return NULL;
133 }
[36774cf]134 }
135 return new_context;
136}
137
[876f5561]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 */
[1433ecda]145hound_context_t *hound_context_create_playback(const char *name,
[9e1800c]146 pcm_format_t format, size_t bsize)
[36774cf]147{
[9e1800c]148 return hound_context_create(name, false, format, bsize);
[36774cf]149}
150
[876f5561]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 */
[1433ecda]158hound_context_t *hound_context_create_capture(const char *name,
[9e1800c]159 pcm_format_t format, size_t bsize)
[36774cf]160{
[9e1800c]161 return hound_context_create(name, true, format, bsize);
[36774cf]162}
163
[876f5561]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 */
[36774cf]172void hound_context_destroy(hound_context_t *hound)
173{
174 assert(hound);
[cc3c27ad]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
[9e1800c]182 hound_service_unregister_context(hound->session, hound->id);
183 hound_service_disconnect(hound->session);
184 free(hound->name);
185 free(hound);
[36774cf]186}
187
[876f5561]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 */
[b7fd2a0]197errno_t hound_context_get_available_targets(hound_context_t *hound,
[33b8d024]198 char ***names, size_t *count)
[36774cf]199{
[7294b5b]200 assert(hound);
[36774cf]201 assert(names);
202 assert(count);
[7294b5b]203 return hound_service_get_list_all(hound->session, names, count,
204 hound->record ? HOUND_SOURCE_DEVS : HOUND_SINK_DEVS);
[36774cf]205}
206
[876f5561]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 */
[b7fd2a0]214errno_t hound_context_get_connected_targets(hound_context_t *hound,
[33b8d024]215 char ***names, size_t *count)
[36774cf]216{
[7294b5b]217 assert(hound);
[36774cf]218 assert(names);
219 assert(count);
[7294b5b]220 return hound_service_get_list(hound->session, names, count,
221 HOUND_CONNECTED | (hound->record ?
[1433ecda]222 HOUND_SOURCE_DEVS : HOUND_SINK_DEVS), hound->name);
[36774cf]223}
224
[876f5561]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 */
[1433ecda]234errno_t hound_context_connect_target(hound_context_t *hound, const char *target)
[36774cf]235{
236 assert(hound);
[ec81221]237 assert(target);
238
[33b8d024]239 char **tgt = NULL;
[ec81221]240 size_t count = 1;
[b7fd2a0]241 errno_t ret = EOK;
[ec81221]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;
[07e28f8]246 if (count == 0)
247 return ENOENT;
[ec81221]248 target = tgt[0];
249 }
250 //TODO handle all-targets
251
252 if (hound->record) {
253 ret = hound_service_connect_source_sink(
[7294b5b]254 hound->session, target, hound->name);
[ec81221]255 } else {
256 ret = hound_service_connect_source_sink(
[7294b5b]257 hound->session, hound->name, target);
[ec81221]258 }
259 if (tgt)
260 free(tgt[0]);
261 free(tgt);
262 return ret;
[36774cf]263}
264
[876f5561]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 */
[1433ecda]271errno_t hound_context_disconnect_target(hound_context_t *hound, const char *target)
[36774cf]272{
273 assert(hound);
[ec81221]274 assert(target);
[2ba4d45c]275 //TODO handle all-targets
[ec81221]276 if (hound->record) {
[7294b5b]277 return hound_service_disconnect_source_sink(
278 hound->session, target, hound->name);
[ec81221]279 } else {
[7294b5b]280 return hound_service_disconnect_source_sink(
281 hound->session, hound->name, target);
[ec81221]282 }
[36774cf]283}
284
[876f5561]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 */
[9e1800c]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;
[c5d6f9cf]306 new_stream->flags = flags;
[b7fd2a0]307 const errno_t ret = hound_service_stream_enter(new_stream->exch,
[9e1800c]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
[876f5561]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 */
[9e1800c]326void hound_stream_destroy(hound_stream_t *stream)
327{
328 if (stream) {
[c5d6f9cf]329 if (stream->flags & HOUND_STREAM_DRAIN_ON_EXIT)
330 hound_service_stream_drain(stream->exch);
[9e1800c]331 hound_service_stream_exit(stream->exch);
332 async_exchange_end(stream->exch);
333 list_remove(&stream->link);
334 free(stream);
335 }
336}
337
[876f5561]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 */
[b7fd2a0]345errno_t hound_stream_write(hound_stream_t *stream, const void *data, size_t size)
[9e1800c]346{
347 assert(stream);
348 if (!data || size == 0)
349 return EBADMEM;
350 return hound_service_stream_write(stream->exch, data, size);
351}
352
[876f5561]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 */
[b7fd2a0]360errno_t hound_stream_read(hound_stream_t *stream, void *data, size_t size)
[9e1800c]361{
362 assert(stream);
363 if (!data || size == 0)
364 return EBADMEM;
365 return hound_service_stream_read(stream->exch, data, size);
366}
367
[4e72a4c]368/**
369 * Wait until the server side buffer is empty.
370 * @param stream The stream that shoulod be drained.
371 * @return Error code.
372 */
[b7fd2a0]373errno_t hound_stream_drain(hound_stream_t *stream)
[4e72a4c]374{
375 assert(stream);
376 return hound_service_stream_drain(stream->exch);
377}
378
[876f5561]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 */
[1433ecda]387static hound_stream_t *hound_get_main_stream(hound_context_t *hound)
[9e1800c]388{
389 assert(hound);
390 if (!hound->main.stream)
[c5d6f9cf]391 hound->main.stream = hound_stream_create(hound,
[1433ecda]392 HOUND_STREAM_DRAIN_ON_EXIT, hound->main.format,
[c5d6f9cf]393 hound->main.bsize);
[9e1800c]394 return hound->main.stream;
395}
396
[876f5561]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 */
[b7fd2a0]404errno_t hound_write_main_stream(hound_context_t *hound,
[9e1800c]405 const void *data, size_t size)
406{
407 assert(hound);
[876f5561]408 if (hound->record)
409 return EINVAL;
410
[9e1800c]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
[876f5561]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 */
[b7fd2a0]424errno_t hound_read_main_stream(hound_context_t *hound, void *data, size_t size)
[9e1800c]425{
426 assert(hound);
[876f5561]427 if (!hound->record)
428 return EINVAL;
[9e1800c]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
[876f5561]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 */
[b7fd2a0]444errno_t hound_write_replace_main_stream(hound_context_t *hound,
[9e1800c]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
[876f5561]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 */
[b7fd2a0]462errno_t hound_context_set_main_stream_params(hound_context_t *hound,
[876f5561]463 pcm_format_t format, size_t bsize)
[9e1800c]464{
465 assert(hound);
466 // TODO implement
467 return ENOTSUP;
468}
469
[876f5561]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 */
[b7fd2a0]481errno_t hound_write_immediate(hound_context_t *hound, pcm_format_t format,
[9e1800c]482 const void *data, size_t size)
483{
484 assert(hound);
[876f5561]485 if (hound->record)
486 return EINVAL;
[9e1800c]487 hound_stream_t *tmpstream = hound_stream_create(hound, 0, format, size);
488 if (!tmpstream)
489 return ENOMEM;
[b7fd2a0]490 const errno_t ret = hound_stream_write(tmpstream, data, size);
[9e1800c]491 if (ret == EOK) {
492 //TODO drain?
493 hound_service_stream_drain(tmpstream->exch);
494 }
495 hound_stream_destroy(tmpstream);
496 return ret;
497}
[36774cf]498/**
499 * @}
500 */
Note: See TracBrowser for help on using the repository browser.