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

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

Drop id parameter from audio_pcm interface.

Independent buffer should have separate nodes.

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