source: mainline/uspace/lib/drv/generic/remote_audio_pcm_buffer.c@ 6be06d0

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 6be06d0 was 6be06d0, checked in by Jan Vesely <jano.vesely@…>, 13 years ago

libdrv,audio pcm: Fix sending number of buffer parts.

  • Property mode set to 100644
File size: 15.7 KB
Line 
1/*
2 * Copyright (c) 2011 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 <ddf/log.h>
36#include <errno.h>
37#include <str.h>
38#include <as.h>
39#include <sys/mman.h>
40
41#include "audio_pcm_buffer_iface.h"
42#include "ddf/driver.h"
43
44typedef enum {
45 IPC_M_AUDIO_PCM_GET_INFO_STR,
46 IPC_M_AUDIO_PCM_GET_BUFFER,
47 IPC_M_AUDIO_PCM_RELEASE_BUFFER,
48 IPC_M_AUDIO_PCM_START_PLAYBACK,
49 IPC_M_AUDIO_PCM_STOP_PLAYBACK,
50 IPC_M_AUDIO_PCM_START_RECORD,
51 IPC_M_AUDIO_PCM_STOP_RECORD,
52} audio_pcm_iface_funcs_t;
53
54/*
55 * CLIENT SIDE
56 */
57int audio_pcm_buffer_get_info_str(async_exch_t *exch, const char **name)
58{
59 if (!exch)
60 return EINVAL;
61 sysarg_t name_size;
62 const int ret = async_req_1_1(exch,
63 DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
64 IPC_M_AUDIO_PCM_GET_INFO_STR, &name_size);
65 if (ret == EOK && name) {
66 char *name_place = calloc(1, name_size);
67 if (!name_place) {
68 /* Make the other side fail
69 * as it waits for read request */
70 async_data_read_start(exch, (void*)-1, 0);
71 return ENOMEM;
72 }
73 const int ret =
74 async_data_read_start(exch, name_place, name_size);
75 if (ret != EOK) {
76 free(name_place);
77 return ret;
78 }
79 *name = name_place;
80 }
81 return ret;
82}
83
84int audio_pcm_buffer_get_buffer(async_exch_t *exch, void **buffer, size_t *size,
85 unsigned *id, async_client_conn_t event_rec, void* arg)
86{
87 if (!exch || !buffer || !size || !id)
88 return EINVAL;
89
90 sysarg_t buffer_size = *size, buffer_id = 0;
91 const int ret = async_req_2_2(exch,
92 DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE), IPC_M_AUDIO_PCM_GET_BUFFER,
93 (sysarg_t)buffer_size, &buffer_size, &buffer_id);
94 if (ret == EOK) {
95 void *dst = NULL;
96 int ret = async_share_in_start_0_0(exch, buffer_size, &dst);
97 if (ret != EOK) {
98 return ret;
99 }
100 ret = async_connect_to_me(exch, 0, 0, 0, event_rec, arg);
101 if (ret != EOK) {
102 return ret;
103 }
104
105 *buffer = dst;
106 *size = buffer_size;
107 *id = buffer_id;
108 }
109 return ret;
110}
111
112int audio_pcm_buffer_release_buffer(async_exch_t *exch, unsigned id)
113{
114 if (!exch)
115 return EINVAL;
116 return async_req_2_0(exch, DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
117 IPC_M_AUDIO_PCM_RELEASE_BUFFER, id);
118}
119
120int audio_pcm_buffer_start_playback(async_exch_t *exch, unsigned id,
121 unsigned parts, unsigned sample_rate, uint16_t sample_size,
122 uint8_t channels, bool sign)
123{
124 if (!exch)
125 return EINVAL;
126 const sysarg_t packed =
127 (sample_size << 16) | (channels << 8) |
128 ((parts & 0x7f) << 1) | (sign ? 1 : 0);
129 return async_req_4_0(exch, DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
130 IPC_M_AUDIO_PCM_START_PLAYBACK, id, sample_rate, packed);
131}
132
133int audio_pcm_buffer_stop_playback(async_exch_t *exch, unsigned id)
134{
135 if (!exch)
136 return EINVAL;
137 return async_req_2_0(exch, DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
138 IPC_M_AUDIO_PCM_STOP_PLAYBACK, id);
139}
140
141int audio_pcm_buffer_start_record(async_exch_t *exch, unsigned id,
142 unsigned sample_rate, unsigned sample_size, unsigned channels, bool sign)
143{
144 if (!exch || sample_size > UINT16_MAX || channels > (UINT16_MAX >> 1))
145 return EINVAL;
146 sysarg_t packed = sample_size << 16 | channels << 1 | sign ? 1 : 0;
147 return async_req_4_0(exch, DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
148 IPC_M_AUDIO_PCM_START_RECORD, id, sample_rate, packed);
149}
150
151int audio_pcm_buffer_stop_record(async_exch_t *exch, unsigned id)
152{
153 if (!exch)
154 return EINVAL;
155 return async_req_2_0(exch, DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
156 IPC_M_AUDIO_PCM_STOP_RECORD, id);
157}
158
159/*
160 * SERVER SIDE
161 */
162static void remote_audio_pcm_get_info_str(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
163static void remote_audio_pcm_get_buffer(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
164static void remote_audio_pcm_release_buffer(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
165static void remote_audio_pcm_start_playback(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
166static void remote_audio_pcm_stop_playback(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
167static void remote_audio_pcm_start_record(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
168static void remote_audio_pcm_stop_record(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
169
170/** Remote audio pcm buffer interface operations. */
171static remote_iface_func_ptr_t remote_audio_pcm_iface_ops[] = {
172 [IPC_M_AUDIO_PCM_GET_INFO_STR] = remote_audio_pcm_get_info_str,
173 [IPC_M_AUDIO_PCM_GET_BUFFER] = remote_audio_pcm_get_buffer,
174 [IPC_M_AUDIO_PCM_RELEASE_BUFFER] = remote_audio_pcm_release_buffer,
175 [IPC_M_AUDIO_PCM_START_PLAYBACK] = remote_audio_pcm_start_playback,
176 [IPC_M_AUDIO_PCM_STOP_PLAYBACK] = remote_audio_pcm_stop_playback,
177 [IPC_M_AUDIO_PCM_START_RECORD] = remote_audio_pcm_start_record,
178 [IPC_M_AUDIO_PCM_STOP_RECORD] = remote_audio_pcm_stop_record,
179};
180
181/** Remote audio mixer interface structure. */
182remote_iface_t remote_audio_pcm_buffer_iface = {
183 .method_count = sizeof(remote_audio_pcm_iface_ops) /
184 sizeof(remote_audio_pcm_iface_ops[0]),
185 .methods = remote_audio_pcm_iface_ops
186};
187
188void remote_audio_pcm_get_info_str(ddf_fun_t *fun, void *iface,
189 ipc_callid_t callid, ipc_call_t *call)
190{
191 const audio_pcm_buffer_iface_t *pcm_iface = iface;
192
193 if (!pcm_iface->get_info_str) {
194 async_answer_0(callid, ENOTSUP);
195 return;
196 }
197 const char *name = NULL;
198 const int ret = pcm_iface->get_info_str(fun, &name);
199 const size_t name_size = name ? str_size(name) + 1 : 0;
200 async_answer_1(callid, ret, name_size);
201 /* Send the string. */
202 if (ret == EOK && name_size > 0) {
203 size_t size;
204 ipc_callid_t name_id;
205 if (!async_data_read_receive(&name_id, &size)) {
206 async_answer_0(name_id, EPARTY);
207 return;
208 }
209 if (size != name_size) {
210 async_answer_0(name_id, ELIMIT);
211 return;
212 }
213 async_data_read_finalize(name_id, name, name_size);
214 }
215}
216
217void remote_audio_pcm_get_buffer(ddf_fun_t *fun, void *iface,
218 ipc_callid_t callid, ipc_call_t *call)
219{
220 const audio_pcm_buffer_iface_t *pcm_iface = iface;
221
222 if (!pcm_iface->get_buffer ||
223 !pcm_iface->release_buffer ||
224 !pcm_iface->set_event_session) {
225 async_answer_0(callid, ENOTSUP);
226 return;
227 }
228 void *buffer = NULL;
229 size_t size = DEV_IPC_GET_ARG1(*call);
230 unsigned id = 0;
231 int ret = pcm_iface->get_buffer(fun, &buffer, &size, &id);
232 async_answer_2(callid, ret, size, id);
233 if (ret != EOK || size == 0)
234 return;
235
236 /* Share the buffer. */
237 size_t share_size = 0;
238 ipc_callid_t share_id = 0;
239
240 ddf_msg(LVL_DEBUG2, "Receiving share request.");
241 if (!async_share_in_receive(&share_id, &share_size)) {
242 ddf_msg(LVL_DEBUG, "Failed to share pcm buffer.");
243 pcm_iface->release_buffer(fun, id);
244 async_answer_0(share_id, EPARTY);
245 return;
246 }
247
248 ddf_msg(LVL_DEBUG2, "Checking requested share size.");
249 if (share_size != size) {
250 ddf_msg(LVL_DEBUG, "Incorrect pcm buffer size requested.");
251 pcm_iface->release_buffer(fun, id);
252 async_answer_0(share_id, ELIMIT);
253 return;
254 }
255
256 ddf_msg(LVL_DEBUG2, "Calling share finalize.");
257 ret = async_share_in_finalize(share_id, buffer, AS_AREA_WRITE
258 | AS_AREA_READ);
259 if (ret != EOK) {
260 ddf_msg(LVL_DEBUG, "Failed to share buffer.");
261 pcm_iface->release_buffer(fun, id);
262 return;
263 }
264
265 ddf_msg(LVL_DEBUG2, "Buffer shared with size %zu, creating callback.",
266 share_size);
267 {
268 ipc_call_t call;
269 ipc_callid_t callid = async_get_call(&call);
270 async_sess_t *sess =
271 async_callback_receive_start(EXCHANGE_ATOMIC, &call);
272 if (sess == NULL) {
273 ddf_msg(LVL_DEBUG, "Failed to create event callback");
274 pcm_iface->release_buffer(fun, id);
275 async_answer_0(callid, EAGAIN);
276 return;
277 }
278 ret = pcm_iface->set_event_session(fun, id, sess);
279 if (ret != EOK) {
280 ddf_msg(LVL_DEBUG, "Failed to set event callback.");
281 pcm_iface->release_buffer(fun, id);
282 async_answer_0(callid, ret);
283 return;
284 }
285 ddf_msg(LVL_DEBUG2, "Buffer and event session setup OK.");
286 async_answer_0(callid, EOK);
287 }
288}
289
290void remote_audio_pcm_release_buffer(ddf_fun_t *fun, void *iface,
291 ipc_callid_t callid, ipc_call_t *call)
292{
293 const audio_pcm_buffer_iface_t *pcm_iface = iface;
294
295 const unsigned id = DEV_IPC_GET_ARG1(*call);
296 const int ret = pcm_iface->release_buffer ?
297 pcm_iface->release_buffer(fun, id) : ENOTSUP;
298 async_answer_0(callid, ret);
299}
300
301void remote_audio_pcm_start_playback(ddf_fun_t *fun, void *iface,
302 ipc_callid_t callid, ipc_call_t *call)
303{
304 const audio_pcm_buffer_iface_t *pcm_iface = iface;
305
306 const unsigned id = DEV_IPC_GET_ARG1(*call);
307 const unsigned rate = DEV_IPC_GET_ARG2(*call);
308 const unsigned size = DEV_IPC_GET_ARG3(*call) >> 16;
309 const unsigned channels = (DEV_IPC_GET_ARG3(*call) >> 8) & UINT8_MAX;
310 const unsigned parts = (DEV_IPC_GET_ARG3(*call) >> 1) & 0x7f;
311 const bool sign = (bool)(DEV_IPC_GET_ARG3(*call) & 1);
312
313 const int ret = pcm_iface->start_playback
314 ? pcm_iface->start_playback(fun, id, parts, rate, size, channels, sign)
315 : ENOTSUP;
316 async_answer_0(callid, ret);
317}
318
319void remote_audio_pcm_stop_playback(ddf_fun_t *fun, void *iface,
320 ipc_callid_t callid, ipc_call_t *call)
321{
322 const audio_pcm_buffer_iface_t *pcm_iface = iface;
323
324 const unsigned id = DEV_IPC_GET_ARG1(*call);
325 const int ret = pcm_iface->stop_playback ?
326 pcm_iface->stop_playback(fun, id) : ENOTSUP;
327 async_answer_0(callid, ret);
328}
329
330void remote_audio_pcm_start_record(ddf_fun_t *fun, void *iface,
331 ipc_callid_t callid, ipc_call_t *call)
332{
333 const audio_pcm_buffer_iface_t *pcm_iface = iface;
334
335 const unsigned id = DEV_IPC_GET_ARG1(*call);
336 const unsigned rate = DEV_IPC_GET_ARG2(*call);
337 const unsigned size = DEV_IPC_GET_ARG3(*call) >> 16;
338 const unsigned channels = (DEV_IPC_GET_ARG3(*call) & UINT16_MAX) >> 1;
339 const bool sign = (bool)(DEV_IPC_GET_ARG3(*call) & 1);
340
341 const int ret = pcm_iface->start_record
342 ? pcm_iface->start_record(fun, id, rate, size, channels, sign)
343 : ENOTSUP;
344 async_answer_0(callid, ret);
345}
346
347void remote_audio_pcm_stop_record(ddf_fun_t *fun, void *iface,
348 ipc_callid_t callid, ipc_call_t *call)
349{
350 const audio_pcm_buffer_iface_t *pcm_iface = iface;
351
352 const unsigned id = DEV_IPC_GET_ARG1(*call);
353 const int ret = pcm_iface->stop_record ?
354 pcm_iface->stop_record(fun, id) : ENOTSUP;
355 async_answer_0(callid, ret);
356}
357
358#if 0
359
360void remote_audio_mixer_get_info(
361 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
362{
363 audio_mixer_iface_t *mixer_iface = iface;
364
365 if (!mixer_iface->get_info) {
366 async_answer_0(callid, ENOTSUP);
367 return;
368 }
369 const char *name = NULL;
370 unsigned items = 0;
371 const int ret = mixer_iface->get_info(fun, &name, &items);
372 const size_t name_size = name ? str_size(name) + 1 : 0;
373 async_answer_2(callid, ret, name_size, items);
374 /* Send the name. */
375 if (ret == EOK && name_size > 0) {
376 size_t size;
377 ipc_callid_t name_id;
378 if (!async_data_read_receive(&name_id, &size)) {
379 async_answer_0(name_id, EPARTY);
380 return;
381 }
382 if (size != name_size) {
383 async_answer_0(name_id, ELIMIT);
384 return;
385 }
386 async_data_read_finalize(name_id, name, name_size);
387 }
388}
389
390void remote_audio_mixer_get_item_info(
391 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
392{
393 audio_mixer_iface_t *mixer_iface = iface;
394
395 if (!mixer_iface->get_item_info) {
396 async_answer_0(callid, ENOTSUP);
397 return;
398 }
399
400 const unsigned item = DEV_IPC_GET_ARG1(*call);
401 const char *name = NULL;
402 unsigned channels = 0;
403 const int ret = mixer_iface->get_item_info(fun, item, &name, &channels);
404 const size_t name_size = name ? str_size(name) + 1 : 0;
405 async_answer_2(callid, ret, name_size, channels);
406 /* Send the name. */
407 if (ret == EOK && name_size > 0) {
408 size_t size;
409 ipc_callid_t name_id;
410 if (!async_data_read_receive(&name_id, &size)) {
411 async_answer_0(name_id, EPARTY);
412 return;
413 }
414 if (size != name_size) {
415 async_answer_0(name_id, ELIMIT);
416 return;
417 }
418 async_data_read_finalize(name_id, name, name_size);
419 }
420}
421
422void remote_audio_mixer_get_channel_info(
423 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
424{
425 audio_mixer_iface_t *mixer_iface = iface;
426
427 if (!mixer_iface->get_channel_info) {
428 async_answer_0(callid, ENOTSUP);
429 return;
430 }
431
432 const unsigned item = DEV_IPC_GET_ARG1(*call);
433 const unsigned channel = DEV_IPC_GET_ARG2(*call);
434 const char *name = NULL;
435 unsigned levels = 0;
436 const int ret =
437 mixer_iface->get_channel_info(fun, item, channel, &name, &levels);
438 const size_t name_size = name ? str_size(name) + 1 : 0;
439 async_answer_2(callid, ret, name_size, levels);
440 /* Send the name. */
441 if (ret == EOK && name_size > 0) {
442 size_t size;
443 ipc_callid_t name_id;
444 if (!async_data_read_receive(&name_id, &size)) {
445 async_answer_0(name_id, EPARTY);
446 return;
447 }
448 if (size != name_size) {
449 async_answer_0(name_id, ELIMIT);
450 return;
451 }
452 async_data_read_finalize(name_id, name, name_size);
453 }
454}
455
456void remote_audio_mixer_channel_mute_set(
457 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
458{
459 audio_mixer_iface_t *mixer_iface = iface;
460
461 if (!mixer_iface->channel_mute_set) {
462 async_answer_0(callid, ENOTSUP);
463 return;
464 }
465 const unsigned item = DEV_IPC_GET_ARG1(*call);
466 const unsigned channel = DEV_IPC_GET_ARG2(*call);
467 const bool mute = DEV_IPC_GET_ARG3(*call);
468 const int ret = mixer_iface->channel_mute_set(fun, item, channel, mute);
469 async_answer_0(callid, ret);
470}
471
472void remote_audio_mixer_channel_mute_get(
473 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
474{
475 audio_mixer_iface_t *mixer_iface = iface;
476
477 if (!mixer_iface->channel_mute_get) {
478 async_answer_0(callid, ENOTSUP);
479 return;
480 }
481 const unsigned item = DEV_IPC_GET_ARG1(*call);
482 const unsigned channel = DEV_IPC_GET_ARG2(*call);
483 bool mute = false;
484 const int ret =
485 mixer_iface->channel_mute_get(fun, item, channel, &mute);
486 async_answer_1(callid, ret, mute);
487}
488
489void remote_audio_mixer_channel_volume_set(
490 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
491{
492 audio_mixer_iface_t *mixer_iface = iface;
493
494 if (!mixer_iface->channel_volume_set) {
495 async_answer_0(callid, ENOTSUP);
496 return;
497 }
498 const unsigned item = DEV_IPC_GET_ARG1(*call);
499 const unsigned channel = DEV_IPC_GET_ARG2(*call);
500 const unsigned level = DEV_IPC_GET_ARG3(*call);
501 const int ret =
502 mixer_iface->channel_volume_set(fun, item, channel, level);
503 async_answer_0(callid, ret);
504}
505
506void remote_audio_mixer_channel_volume_get(
507 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
508{
509 audio_mixer_iface_t *mixer_iface = iface;
510
511 if (!mixer_iface->channel_volume_get) {
512 async_answer_0(callid, ENOTSUP);
513 return;
514 }
515 const unsigned item = DEV_IPC_GET_ARG1(*call);
516 const unsigned channel = DEV_IPC_GET_ARG2(*call);
517 unsigned current = 0, max = 0;
518 const int ret =
519 mixer_iface->channel_volume_get(fun, item, channel, &current, &max);
520 async_answer_2(callid, ret, current, max);
521}
522#endif
523
524/**
525 * @}
526 */
527
Note: See TracBrowser for help on using the repository browser.