source: mainline/uspace/lib/drv/generic/remote_audio_pcm.c@ f787c8e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since f787c8e was 984a9ba, checked in by Martin Decky <martin@…>, 7 years ago

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

  • Property mode set to 100644
File size: 24.1 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/** @addtogroup libdrv
29 * @{
30 */
31/** @file
32 */
33
34#include <async.h>
35#include <devman.h>
36#include <ddf/log.h>
37#include <errno.h>
38#include <macros.h>
39#include <str.h>
40#include <as.h>
41
42#include "audio_pcm_iface.h"
43#include "ddf/driver.h"
44
45typedef enum {
46 IPC_M_AUDIO_PCM_GET_INFO_STR,
47 IPC_M_AUDIO_PCM_QUERY_CAPS,
48 IPC_M_AUDIO_PCM_REGISTER_EVENTS,
49 IPC_M_AUDIO_PCM_UNREGISTER_EVENTS,
50 IPC_M_AUDIO_PCM_TEST_FORMAT,
51 IPC_M_AUDIO_PCM_GET_BUFFER,
52 IPC_M_AUDIO_PCM_RELEASE_BUFFER,
53 IPC_M_AUDIO_PCM_GET_BUFFER_POS,
54 IPC_M_AUDIO_PCM_START_PLAYBACK,
55 IPC_M_AUDIO_PCM_STOP_PLAYBACK,
56 IPC_M_AUDIO_PCM_START_CAPTURE,
57 IPC_M_AUDIO_PCM_STOP_CAPTURE,
58} audio_pcm_iface_funcs_t;
59
60/**
61 * Get human readable capability name.
62 * @param cap audio capability.
63 * @return Valid string
64 */
65const char *audio_pcm_cap_str(audio_cap_t cap)
66{
67 static const char *caps[] = {
68 [AUDIO_CAP_CAPTURE] = "CAPTURE",
69 [AUDIO_CAP_PLAYBACK] = "PLAYBACK",
70 [AUDIO_CAP_MAX_BUFFER] = "MAXIMUM BUFFER SIZE",
71 [AUDIO_CAP_BUFFER_POS] = "KNOWS BUFFER POSITION",
72 [AUDIO_CAP_INTERRUPT] = "FRAGMENT INTERRUPTS",
73 [AUDIO_CAP_INTERRUPT_MIN_FRAMES] = "MINIMUM FRAGMENT SIZE",
74 [AUDIO_CAP_INTERRUPT_MAX_FRAMES] = "MAXIMUM FRAGMENT SIZE",
75 };
76 if (cap >= ARRAY_SIZE(caps))
77 return "UNKNOWN CAP";
78 return caps[cap];
79
80}
81
82/**
83 * Get human readable event name.
84 * @param event Audio device event
85 * @return Valid string
86 */
87const char *audio_pcm_event_str(pcm_event_t event)
88{
89 static const char *events[] = {
90 [PCM_EVENT_PLAYBACK_STARTED] = "PLAYBACK STARTED",
91 [PCM_EVENT_CAPTURE_STARTED] = "CAPTURE STARTED",
92 [PCM_EVENT_FRAMES_PLAYED] = "FRAGMENT PLAYED",
93 [PCM_EVENT_FRAMES_CAPTURED] = "FRAGMENT CAPTURED",
94 [PCM_EVENT_PLAYBACK_TERMINATED] = "PLAYBACK TERMINATED",
95 [PCM_EVENT_CAPTURE_TERMINATED] = "CAPTURE TERMINATED",
96 };
97 if (event >= ARRAY_SIZE(events))
98 return "UNKNOWN EVENT";
99 return events[event];
100}
101
102/*
103 * CLIENT SIDE
104 */
105
106/**
107 * Open audio session with the first registered device.
108 *
109 * @return Pointer to a new audio device session, NULL on failure.
110 */
111audio_pcm_sess_t *audio_pcm_open_default(void)
112{
113 static bool resolved = false;
114 static category_id_t pcm_id = 0;
115 if (!resolved) {
116 const errno_t ret = loc_category_get_id("audio-pcm", &pcm_id,
117 IPC_FLAG_BLOCKING);
118 if (ret != EOK)
119 return NULL;
120 resolved = true;
121 }
122
123 service_id_t *svcs = NULL;
124 size_t count = 0;
125 const errno_t ret = loc_category_get_svcs(pcm_id, &svcs, &count);
126 if (ret != EOK)
127 return NULL;
128
129 audio_pcm_sess_t *session = NULL;
130 if (count)
131 session = audio_pcm_open_service(svcs[0]);
132 free(svcs);
133 return session;
134}
135
136/**
137 * Open audio session with device identified by location service string.
138 *
139 * @param name Location service string.
140 * @return Pointer to a new audio device session, NULL on failure.
141 */
142audio_pcm_sess_t *audio_pcm_open(const char *name)
143{
144 devman_handle_t device_handle = 0;
145 const errno_t ret = devman_fun_get_handle(name, &device_handle, 0);
146 if (ret != EOK)
147 return NULL;
148 return devman_device_connect(device_handle, IPC_FLAG_BLOCKING);
149}
150
151/**
152 * Open audio session with device identified by location service id
153 *
154 * @param name Location service id.
155 * @return Pointer to a new audio device session, NULL on failure.
156 */
157audio_pcm_sess_t *audio_pcm_open_service(service_id_t id)
158{
159 return loc_service_connect(id, INTERFACE_DDF, IPC_FLAG_BLOCKING);
160}
161
162/**
163 * Close open audio device session.
164 *
165 * @param name Open audio device session.
166 *
167 * @note Calling this function on already closed or invalid session results
168 * in undefined behavior.
169 */
170void audio_pcm_close(audio_pcm_sess_t *sess)
171{
172 if (sess)
173 async_hangup(sess);
174}
175
176/**
177 * Get a short description string.
178 *
179 * @param sess Audio device session.
180 * @param name Place to store newly allocated string.
181 *
182 * @return Error code.
183 *
184 * @note Caller is responsible for freeing newly allocated memory.
185 */
186errno_t audio_pcm_get_info_str(audio_pcm_sess_t *sess, char **name)
187{
188 if (!name)
189 return EINVAL;
190 async_exch_t *exch = async_exchange_begin(sess);
191 sysarg_t name_size;
192 const errno_t ret = async_req_1_1(exch,
193 DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
194 IPC_M_AUDIO_PCM_GET_INFO_STR, &name_size);
195 if (ret == EOK) {
196 char *name_place = calloc(1, name_size);
197 if (!name_place) {
198 /*
199 * Make the other side fail
200 * as it waits for read request
201 */
202 async_data_read_start(exch, (void *)-1, 0);
203 async_exchange_end(exch);
204 return ENOMEM;
205 }
206 const errno_t ret =
207 async_data_read_start(exch, name_place, name_size);
208 if (ret != EOK) {
209 free(name_place);
210 async_exchange_end(exch);
211 return ret;
212 }
213 *name = name_place;
214 }
215 async_exchange_end(exch);
216 return ret;
217}
218
219
220/**
221 * Query value of specified capability.
222 *
223 * @param sess Audio device session.
224 * @param cap Audio device capability.
225 * @param[out] val Place to store queried value.
226 *
227 * @return Error code.
228 */
229errno_t audio_pcm_query_cap(audio_pcm_sess_t *sess, audio_cap_t cap, sysarg_t *value)
230{
231 async_exch_t *exch = async_exchange_begin(sess);
232 const errno_t ret = async_req_2_1(exch,
233 DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE), IPC_M_AUDIO_PCM_QUERY_CAPS,
234 cap, value);
235 async_exchange_end(exch);
236 return ret;
237}
238
239/**
240 * Query current position in device buffer.
241 *
242 * @param sess Audio device session.
243 * @param pos Place to store the result.
244 *
245 * @return Error code.
246 *
247 * Works for both playback and capture.
248 */
249errno_t audio_pcm_get_buffer_pos(audio_pcm_sess_t *sess, size_t *pos)
250{
251 if (!pos)
252 return EINVAL;
253 async_exch_t *exch = async_exchange_begin(sess);
254 sysarg_t value = 0;
255 const errno_t ret = async_req_1_1(exch,
256 DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
257 IPC_M_AUDIO_PCM_GET_BUFFER_POS, &value);
258 if (ret == EOK)
259 *pos = value;
260 async_exchange_end(exch);
261 return ret;
262}
263
264/**
265 * Test format parameters for device support.
266 *
267 * @param sess Audio device session.
268 * @param channels Number of channels
269 * @param rate Sampling rate.
270 * @format Sample format.
271 *
272 * @return Error code.
273 *
274 * Works for both playback and capture. This function modifies provided
275 * parameters to the nearest values supported by the device.
276 */
277errno_t audio_pcm_test_format(audio_pcm_sess_t *sess, unsigned *channels,
278 unsigned *rate, pcm_sample_format_t *format)
279{
280 async_exch_t *exch = async_exchange_begin(sess);
281 sysarg_t channels_arg = channels ? *channels : 0;
282 sysarg_t rate_arg = rate ? *rate : 0;
283 sysarg_t format_arg = format ? *format : 0;
284 const errno_t ret = async_req_4_3(exch,
285 DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
286 IPC_M_AUDIO_PCM_TEST_FORMAT, channels_arg, rate_arg, format_arg,
287 &channels_arg, &rate_arg, &format_arg);
288 async_exchange_end(exch);
289
290 /*
291 * All OK or something has changed. Verify that it was not one of the
292 * params we care about
293 */
294 if ((ret == EOK || ret == ELIMIT) &&
295 (!channels || *channels == channels_arg) &&
296 (!rate || *rate == rate_arg) &&
297 (!format || *format == format_arg))
298 return EOK;
299 if (channels)
300 *channels = channels_arg;
301 if (rate)
302 *rate = rate_arg;
303 if (format)
304 *format = format_arg;
305 return ret;
306}
307
308/**
309 * Register callback for device generated events.
310 *
311 * @param sess Audio device session.
312 * @param event_rec Event callback function.
313 * @param arg Event callback custom parameter.
314 *
315 * @return Error code.
316 */
317errno_t audio_pcm_register_event_callback(audio_pcm_sess_t *sess,
318 async_port_handler_t event_callback, void *arg)
319{
320 if (!event_callback)
321 return EINVAL;
322
323 async_exch_t *exch = async_exchange_begin(sess);
324
325 errno_t ret = async_req_1_0(exch, DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
326 IPC_M_AUDIO_PCM_REGISTER_EVENTS);
327 if (ret == EOK) {
328 port_id_t port;
329 ret = async_create_callback_port(exch, INTERFACE_AUDIO_PCM_CB, 0, 0,
330 event_callback, arg, &port);
331 }
332
333 async_exchange_end(exch);
334 return ret;
335}
336
337/**
338 * Unregister callback for device generated events.
339 *
340 * @param sess Audio device session.
341 *
342 * @return Error code.
343 */
344errno_t audio_pcm_unregister_event_callback(audio_pcm_sess_t *sess)
345{
346 async_exch_t *exch = async_exchange_begin(sess);
347 const errno_t ret = async_req_1_0(exch,
348 DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
349 IPC_M_AUDIO_PCM_UNREGISTER_EVENTS);
350 async_exchange_end(exch);
351 return ret;
352}
353
354/**
355 * Get device accessible playback/capture buffer.
356 *
357 * @param sess Audio device session.
358 * @param buffer Place to store pointer to the buffer.
359 * @param size Place to store buffer size (bytes).
360 *
361 * @return Error code.
362 */
363errno_t audio_pcm_get_buffer(audio_pcm_sess_t *sess, void **buffer, size_t *size)
364{
365 if (!buffer || !size)
366 return EINVAL;
367
368 async_exch_t *exch = async_exchange_begin(sess);
369
370 sysarg_t buffer_size = *size;
371 errno_t ret = async_req_2_1(exch, DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
372 IPC_M_AUDIO_PCM_GET_BUFFER, (sysarg_t)buffer_size, &buffer_size);
373 if (ret == EOK) {
374 void *dst = NULL;
375 ret = async_share_in_start_0_0(exch, buffer_size, &dst);
376 if (ret != EOK) {
377 async_exchange_end(exch);
378 return ret;
379 }
380 *buffer = dst;
381 *size = buffer_size;
382 }
383 async_exchange_end(exch);
384 return ret;
385}
386
387/**
388 * Release device accessible playback/capture buffer.
389 *
390 * @param sess Audio device session.
391 *
392 * @return Error code.
393 */
394errno_t audio_pcm_release_buffer(audio_pcm_sess_t *sess)
395{
396 async_exch_t *exch = async_exchange_begin(sess);
397 const errno_t ret = async_req_1_0(exch,
398 DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
399 IPC_M_AUDIO_PCM_RELEASE_BUFFER);
400 async_exchange_end(exch);
401 return ret;
402}
403
404/**
405 * Start playback on buffer from position 0.
406 *
407 * @param sess Audio device session.
408 * @param frames Size of fragment (in frames).
409 * @param channels Number of channels.
410 * @param sample_rate Sampling rate (for one channel).
411 * @param format Sample format.
412 *
413 * @return Error code.
414 *
415 * Event will be generated after every fragment. Set fragment size to
416 * 0 to turn off event generation.
417 */
418errno_t audio_pcm_start_playback_fragment(audio_pcm_sess_t *sess, unsigned frames,
419 unsigned channels, unsigned sample_rate, pcm_sample_format_t format)
420{
421 if (channels > UINT16_MAX)
422 return EINVAL;
423 assert((format & UINT16_MAX) == format);
424 const sysarg_t packed = (channels << 16) | (format & UINT16_MAX);
425 async_exch_t *exch = async_exchange_begin(sess);
426 const errno_t ret = async_req_4_0(exch,
427 DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
428 IPC_M_AUDIO_PCM_START_PLAYBACK,
429 frames, sample_rate, packed);
430 async_exchange_end(exch);
431 return ret;
432}
433/**
434 * Stops playback after current fragment.
435 *
436 * @param sess Audio device session.
437 *
438 * @return Error code.
439 */
440errno_t audio_pcm_last_playback_fragment(audio_pcm_sess_t *sess)
441{
442 async_exch_t *exch = async_exchange_begin(sess);
443 const errno_t ret = async_req_2_0(exch,
444 DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
445 IPC_M_AUDIO_PCM_STOP_PLAYBACK, false);
446 async_exchange_end(exch);
447 return ret;
448}
449
450/**
451 * Start playback on buffer from the current position.
452 *
453 * @param sess Audio device session.
454 * @param channels Number of channels.
455 * @param sample_rate Sampling rate (for one channel).
456 * @param format Sample format.
457 *
458 * @return Error code.
459 */
460errno_t audio_pcm_start_playback(audio_pcm_sess_t *sess,
461 unsigned channels, unsigned sample_rate, pcm_sample_format_t format)
462{
463 return audio_pcm_start_playback_fragment(
464 sess, 0, channels, sample_rate, format);
465}
466
467/**
468 * Immediately stops current playback.
469 *
470 * @param sess Audio device session.
471 *
472 * @return Error code.
473 */
474errno_t audio_pcm_stop_playback_immediate(audio_pcm_sess_t *sess)
475{
476 async_exch_t *exch = async_exchange_begin(sess);
477 const errno_t ret = async_req_2_0(exch,
478 DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
479 IPC_M_AUDIO_PCM_STOP_PLAYBACK, true);
480 async_exchange_end(exch);
481 return ret;
482}
483
484/**
485 * Stops playback at the end of the current fragment.
486 *
487 * @param sess Audio device session.
488 *
489 * @return Error code.
490 */
491errno_t audio_pcm_stop_playback(audio_pcm_sess_t *sess)
492{
493 async_exch_t *exch = async_exchange_begin(sess);
494 const errno_t ret = async_req_2_0(exch,
495 DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
496 IPC_M_AUDIO_PCM_STOP_PLAYBACK, false);
497 async_exchange_end(exch);
498 return ret;
499}
500
501/**
502 * Start capture on buffer from the current position.
503 *
504 * @param sess Audio device session.
505 * @param frames Size of fragment (in frames).
506 * @param channels Number of channels.
507 * @param sample_rate Sampling rate (for one channel).
508 * @param format Sample format.
509 *
510 * @return Error code.
511 *
512 * Event will be generated after every fragment. Set fragment size to
513 * 0 to turn off event generation.
514 */
515errno_t audio_pcm_start_capture_fragment(audio_pcm_sess_t *sess, unsigned frames,
516 unsigned channels, unsigned sample_rate, pcm_sample_format_t format)
517{
518 if (channels > UINT16_MAX)
519 return EINVAL;
520 assert((format & UINT16_MAX) == format);
521 const sysarg_t packed = (channels << 16) | (format & UINT16_MAX);
522 async_exch_t *exch = async_exchange_begin(sess);
523 const errno_t ret = async_req_4_0(exch,
524 DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE), IPC_M_AUDIO_PCM_START_CAPTURE,
525 frames, sample_rate, packed);
526 async_exchange_end(exch);
527 return ret;
528}
529
530/**
531 * Start capture on buffer from the current position.
532 *
533 * @param sess Audio device session.
534 * @param channels Number of channels.
535 * @param sample_rate Sampling rate (for one channel).
536 * @param format Sample format.
537 *
538 * @return Error code.
539 */
540errno_t audio_pcm_start_capture(audio_pcm_sess_t *sess,
541 unsigned channels, unsigned sample_rate, pcm_sample_format_t format)
542{
543 return audio_pcm_start_capture_fragment(
544 sess, 0, channels, sample_rate, format);
545}
546
547/**
548 * Stops capture at the end of current fragment.
549 *
550 * Won't work if capture was started with fragment size 0.
551 * @param sess Audio device session.
552 *
553 * @return Error code.
554 */
555errno_t audio_pcm_last_capture_fragment(audio_pcm_sess_t *sess)
556{
557 async_exch_t *exch = async_exchange_begin(sess);
558 const errno_t ret = async_req_2_0(exch,
559 DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
560 IPC_M_AUDIO_PCM_STOP_CAPTURE, false);
561 async_exchange_end(exch);
562 return ret;
563}
564
565/**
566 * Immediately stops current capture.
567 *
568 * @param sess Audio device session.
569 *
570 * @return Error code.
571 */
572errno_t audio_pcm_stop_capture_immediate(audio_pcm_sess_t *sess)
573{
574 async_exch_t *exch = async_exchange_begin(sess);
575 const errno_t ret = async_req_2_0(exch,
576 DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
577 IPC_M_AUDIO_PCM_STOP_CAPTURE, true);
578 async_exchange_end(exch);
579 return ret;
580}
581
582/**
583 * Stops capture at the end of the current fragment.
584 *
585 * @param sess Audio device session.
586 *
587 * @return Error code.
588 */
589errno_t audio_pcm_stop_capture(audio_pcm_sess_t *sess)
590{
591 async_exch_t *exch = async_exchange_begin(sess);
592 const errno_t ret = async_req_2_0(exch,
593 DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
594 IPC_M_AUDIO_PCM_STOP_CAPTURE, false);
595 async_exchange_end(exch);
596 return ret;
597}
598
599/*
600 * SERVER SIDE
601 */
602static void remote_audio_pcm_get_info_str(ddf_fun_t *, void *, ipc_call_t *);
603static void remote_audio_pcm_query_caps(ddf_fun_t *, void *, ipc_call_t *);
604static void remote_audio_pcm_events_register(ddf_fun_t *, void *, ipc_call_t *);
605static void remote_audio_pcm_events_unregister(ddf_fun_t *, void *, ipc_call_t *);
606static void remote_audio_pcm_get_buffer_pos(ddf_fun_t *, void *, ipc_call_t *);
607static void remote_audio_pcm_test_format(ddf_fun_t *, void *, ipc_call_t *);
608static void remote_audio_pcm_get_buffer(ddf_fun_t *, void *, ipc_call_t *);
609static void remote_audio_pcm_release_buffer(ddf_fun_t *, void *, ipc_call_t *);
610static void remote_audio_pcm_start_playback(ddf_fun_t *, void *, ipc_call_t *);
611static void remote_audio_pcm_stop_playback(ddf_fun_t *, void *, ipc_call_t *);
612static void remote_audio_pcm_start_capture(ddf_fun_t *, void *, ipc_call_t *);
613static void remote_audio_pcm_stop_capture(ddf_fun_t *, void *, ipc_call_t *);
614
615/** Remote audio pcm buffer interface operations. */
616static const remote_iface_func_ptr_t remote_audio_pcm_iface_ops[] = {
617 [IPC_M_AUDIO_PCM_GET_INFO_STR] = remote_audio_pcm_get_info_str,
618 [IPC_M_AUDIO_PCM_QUERY_CAPS] = remote_audio_pcm_query_caps,
619 [IPC_M_AUDIO_PCM_REGISTER_EVENTS] = remote_audio_pcm_events_register,
620 [IPC_M_AUDIO_PCM_UNREGISTER_EVENTS] = remote_audio_pcm_events_unregister,
621 [IPC_M_AUDIO_PCM_GET_BUFFER_POS] = remote_audio_pcm_get_buffer_pos,
622 [IPC_M_AUDIO_PCM_TEST_FORMAT] = remote_audio_pcm_test_format,
623 [IPC_M_AUDIO_PCM_GET_BUFFER] = remote_audio_pcm_get_buffer,
624 [IPC_M_AUDIO_PCM_RELEASE_BUFFER] = remote_audio_pcm_release_buffer,
625 [IPC_M_AUDIO_PCM_START_PLAYBACK] = remote_audio_pcm_start_playback,
626 [IPC_M_AUDIO_PCM_STOP_PLAYBACK] = remote_audio_pcm_stop_playback,
627 [IPC_M_AUDIO_PCM_START_CAPTURE] = remote_audio_pcm_start_capture,
628 [IPC_M_AUDIO_PCM_STOP_CAPTURE] = remote_audio_pcm_stop_capture,
629};
630
631/** Remote audio mixer interface structure. */
632const remote_iface_t remote_audio_pcm_iface = {
633 .method_count = ARRAY_SIZE(remote_audio_pcm_iface_ops),
634 .methods = remote_audio_pcm_iface_ops
635};
636
637void remote_audio_pcm_get_info_str(ddf_fun_t *fun, void *iface,
638 ipc_call_t *call)
639{
640 const audio_pcm_iface_t *pcm_iface = iface;
641
642 if (!pcm_iface->get_info_str) {
643 async_answer_0(call, ENOTSUP);
644 return;
645 }
646
647 const char *name = NULL;
648 const errno_t ret = pcm_iface->get_info_str(fun, &name);
649 const size_t name_size = name ? str_size(name) + 1 : 0;
650 async_answer_1(call, ret, name_size);
651
652 /* Send the string. */
653 if (ret == EOK && name_size > 0) {
654 ipc_call_t call;
655 size_t size;
656 if (!async_data_read_receive(&call, &size)) {
657 async_answer_0(&call, EPARTY);
658 return;
659 }
660
661 if (size != name_size) {
662 async_answer_0(&call, ELIMIT);
663 return;
664 }
665
666 async_data_read_finalize(&call, name, name_size);
667 }
668}
669
670void remote_audio_pcm_query_caps(ddf_fun_t *fun, void *iface,
671 ipc_call_t *call)
672{
673 const audio_pcm_iface_t *pcm_iface = iface;
674 const audio_cap_t cap = DEV_IPC_GET_ARG1(*call);
675 if (pcm_iface->query_cap) {
676 const unsigned value = pcm_iface->query_cap(fun, cap);
677 async_answer_1(call, EOK, value);
678 } else {
679 async_answer_0(call, ENOTSUP);
680 }
681}
682
683static void remote_audio_pcm_events_register(ddf_fun_t *fun, void *iface,
684 ipc_call_t *call)
685{
686 const audio_pcm_iface_t *pcm_iface = iface;
687 if (!pcm_iface->get_event_session ||
688 !pcm_iface->set_event_session) {
689 async_answer_0(call, ENOTSUP);
690 return;
691 }
692
693 async_answer_0(call, EOK);
694
695 ipc_call_t callback_call;
696 async_get_call(&callback_call);
697 async_sess_t *sess =
698 async_callback_receive_start(EXCHANGE_ATOMIC, &callback_call);
699 if (sess == NULL) {
700 ddf_msg(LVL_DEBUG, "Failed to create event callback");
701 async_answer_0(&callback_call, EAGAIN);
702 return;
703 }
704
705 const errno_t ret = pcm_iface->set_event_session(fun, sess);
706 if (ret != EOK) {
707 ddf_msg(LVL_DEBUG, "Failed to set event callback.");
708 async_hangup(sess);
709 async_answer_0(&callback_call, ret);
710 return;
711 }
712
713 async_answer_0(&callback_call, EOK);
714}
715
716static void remote_audio_pcm_events_unregister(ddf_fun_t *fun, void *iface,
717 ipc_call_t *call)
718{
719 const audio_pcm_iface_t *pcm_iface = iface;
720 if (!pcm_iface->get_event_session ||
721 !pcm_iface->set_event_session) {
722 async_answer_0(call, ENOTSUP);
723 return;
724 }
725
726 async_sess_t *sess = pcm_iface->get_event_session(fun);
727 if (sess) {
728 async_hangup(sess);
729 pcm_iface->set_event_session(fun, NULL);
730 }
731
732 async_answer_0(call, EOK);
733}
734
735void remote_audio_pcm_get_buffer_pos(ddf_fun_t *fun, void *iface,
736 ipc_call_t *call)
737{
738 const audio_pcm_iface_t *pcm_iface = iface;
739 size_t pos = 0;
740 const errno_t ret = pcm_iface->get_buffer_pos ?
741 pcm_iface->get_buffer_pos(fun, &pos) : ENOTSUP;
742 async_answer_1(call, ret, pos);
743}
744
745void remote_audio_pcm_test_format(ddf_fun_t *fun, void *iface,
746 ipc_call_t *call)
747{
748 const audio_pcm_iface_t *pcm_iface = iface;
749 unsigned channels = DEV_IPC_GET_ARG1(*call);
750 unsigned rate = DEV_IPC_GET_ARG2(*call);
751 pcm_sample_format_t format = DEV_IPC_GET_ARG3(*call);
752 const errno_t ret = pcm_iface->test_format ?
753 pcm_iface->test_format(fun, &channels, &rate, &format) : ENOTSUP;
754 async_answer_3(call, ret, channels, rate, format);
755}
756
757void remote_audio_pcm_get_buffer(ddf_fun_t *fun, void *iface,
758 ipc_call_t *call)
759{
760 const audio_pcm_iface_t *pcm_iface = iface;
761
762 if (!pcm_iface->get_buffer ||
763 !pcm_iface->release_buffer) {
764 async_answer_0(call, ENOTSUP);
765 return;
766 }
767 void *buffer = NULL;
768 size_t size = DEV_IPC_GET_ARG1(*call);
769 errno_t ret = pcm_iface->get_buffer(fun, &buffer, &size);
770 async_answer_1(call, ret, size);
771 if (ret != EOK || size == 0)
772 return;
773
774 /* Share the buffer. */
775 ipc_call_t share;
776 size_t share_size = 0;
777
778 ddf_msg(LVL_DEBUG2, "Receiving share request.");
779 if (!async_share_in_receive(&share, &share_size)) {
780 ddf_msg(LVL_DEBUG, "Failed to share pcm buffer.");
781 pcm_iface->release_buffer(fun);
782 async_answer_0(&share, EPARTY);
783 return;
784 }
785
786 ddf_msg(LVL_DEBUG2, "Checking requested share size.");
787 if (share_size != size) {
788 ddf_msg(LVL_DEBUG, "Incorrect pcm buffer size requested.");
789 pcm_iface->release_buffer(fun);
790 async_answer_0(&share, ELIMIT);
791 return;
792 }
793
794 ddf_msg(LVL_DEBUG2, "Calling share finalize.");
795 ret = async_share_in_finalize(&share, buffer, AS_AREA_WRITE |
796 AS_AREA_READ);
797 if (ret != EOK) {
798 ddf_msg(LVL_DEBUG, "Failed to share buffer.");
799 pcm_iface->release_buffer(fun);
800 return;
801 }
802
803 ddf_msg(LVL_DEBUG2, "Buffer shared with size %zu.", share_size);
804}
805
806void remote_audio_pcm_release_buffer(ddf_fun_t *fun, void *iface,
807 ipc_call_t *call)
808{
809 const audio_pcm_iface_t *pcm_iface = iface;
810
811 const errno_t ret = pcm_iface->release_buffer ?
812 pcm_iface->release_buffer(fun) : ENOTSUP;
813 async_answer_0(call, ret);
814}
815
816void remote_audio_pcm_start_playback(ddf_fun_t *fun, void *iface,
817 ipc_call_t *call)
818{
819 const audio_pcm_iface_t *pcm_iface = iface;
820
821 const unsigned frames = DEV_IPC_GET_ARG1(*call);
822 const unsigned rate = DEV_IPC_GET_ARG2(*call);
823 const unsigned channels = (DEV_IPC_GET_ARG3(*call) >> 16) & UINT8_MAX;
824 const pcm_sample_format_t format = DEV_IPC_GET_ARG3(*call) & UINT16_MAX;
825
826 const errno_t ret = pcm_iface->start_playback ?
827 pcm_iface->start_playback(fun, frames, channels, rate, format) :
828 ENOTSUP;
829 async_answer_0(call, ret);
830}
831
832void remote_audio_pcm_stop_playback(ddf_fun_t *fun, void *iface,
833 ipc_call_t *call)
834{
835 const audio_pcm_iface_t *pcm_iface = iface;
836 const bool immediate = DEV_IPC_GET_ARG1(*call);
837
838 const errno_t ret = pcm_iface->stop_playback ?
839 pcm_iface->stop_playback(fun, immediate) : ENOTSUP;
840 async_answer_0(call, ret);
841}
842
843void remote_audio_pcm_start_capture(ddf_fun_t *fun, void *iface,
844 ipc_call_t *call)
845{
846 const audio_pcm_iface_t *pcm_iface = iface;
847
848 const unsigned frames = DEV_IPC_GET_ARG1(*call);
849 const unsigned rate = DEV_IPC_GET_ARG2(*call);
850 const unsigned channels = (DEV_IPC_GET_ARG3(*call) >> 16) & UINT16_MAX;
851 const pcm_sample_format_t format = DEV_IPC_GET_ARG3(*call) & UINT16_MAX;
852
853 const errno_t ret = pcm_iface->start_capture ?
854 pcm_iface->start_capture(fun, frames, channels, rate, format) :
855 ENOTSUP;
856 async_answer_0(call, ret);
857}
858
859void remote_audio_pcm_stop_capture(ddf_fun_t *fun, void *iface,
860 ipc_call_t *call)
861{
862 const audio_pcm_iface_t *pcm_iface = iface;
863 const bool immediate = DEV_IPC_GET_ARG1(*call);
864
865 const errno_t ret = pcm_iface->stop_capture ?
866 pcm_iface->stop_capture(fun, immediate) : ENOTSUP;
867 async_answer_0(call, ret);
868}
869
870/**
871 * @}
872 */
Note: See TracBrowser for help on using the repository browser.