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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 5595841 was 33b8d024, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 8 years ago

Remove const qualifier from the argument of free() and realloc(),
as well as in numerous other variables that hold ownership of memory.

By convention, a pointer that holds ownership is _never_ qualified by const.
This is reflected in the standard type signature of free() and realloc().
Allowing const pointers to hold ownership may seem superficially convenient,
but is actually quite confusing to experienced C programmers.

  • 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.