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

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

libdrv: Add audio pcm buffer interface.

  • Property mode set to 100644
File size: 15.8 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 <errno.h>
36#include <assert.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)
86{
87 if (!exch || !buffer || !size)
88 return EINVAL;
89 sysarg_t buffer_size, buffer_id;
90 const int ret = async_req_1_2(exch,
91 DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE), IPC_M_AUDIO_PCM_GET_BUFFER,
92 &buffer_size, &buffer_id);
93 if (ret == EOK) {
94 void *dst = as_get_mappable_page(buffer_size);
95 // FIXME Should we create as_area?
96 // FIXME Do we need to know the flags?
97 const int ret =
98 async_share_in_start_0_0(exch, dst, buffer_size);
99 if (ret != EOK) {
100 return ret;
101 }
102 *buffer = dst;
103 *size = buffer_size;
104 }
105 if (ret == EOK && id)
106 *id = buffer_id;
107 return ret;
108}
109/*----------------------------------------------------------------------------*/
110int audio_pcm_buffer_release_buffer(async_exch_t *exch, unsigned id)
111{
112 if (!exch)
113 return EINVAL;
114 return async_req_2_0(exch, DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
115 IPC_M_AUDIO_PCM_RELEASE_BUFFER, id);
116}
117/*----------------------------------------------------------------------------*/
118int audio_pcm_buffer_start_playback(async_exch_t *exch, unsigned id,
119 unsigned sample_rate, uint16_t sample_size, uint8_t channels, bool sign)
120{
121 if (!exch)
122 return EINVAL;
123 sysarg_t packed =
124 (sample_size << 16) | (channels << 8) | (sign ? 1 : 0);
125 return async_req_4_0(exch, DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
126 IPC_M_AUDIO_PCM_START_PLAYBACK, id, sample_rate, packed);
127}
128/*----------------------------------------------------------------------------*/
129int audio_pcm_buffer_stop_playback(async_exch_t *exch, unsigned id)
130{
131 if (!exch)
132 return EINVAL;
133 return async_req_2_0(exch, DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
134 IPC_M_AUDIO_PCM_STOP_PLAYBACK, id);
135}
136/*----------------------------------------------------------------------------*/
137int audio_pcm_buffer_start_record(async_exch_t *exch, unsigned id,
138 unsigned sample_rate, unsigned sample_size, unsigned channels, bool sign)
139{
140 if (!exch || sample_size > UINT16_MAX || channels > (UINT16_MAX >> 1))
141 return EINVAL;
142 sysarg_t packed = sample_size << 16 | channels << 1 | sign ? 1 : 0;
143 return async_req_4_0(exch, DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
144 IPC_M_AUDIO_PCM_START_RECORD, id, sample_rate, packed);
145}
146/*----------------------------------------------------------------------------*/
147int audio_pcm_buffer_stop_record(async_exch_t *exch, unsigned id)
148{
149 if (!exch)
150 return EINVAL;
151 return async_req_2_0(exch, DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
152 IPC_M_AUDIO_PCM_STOP_RECORD, id);
153}
154
155/*
156 * SERVER SIDE
157 */
158static void remote_audio_pcm_get_info_str(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
159static void remote_audio_pcm_get_buffer(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
160static void remote_audio_pcm_release_buffer(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
161static void remote_audio_pcm_start_playback(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
162static void remote_audio_pcm_stop_playback(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
163static void remote_audio_pcm_start_record(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
164static void remote_audio_pcm_stop_record(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
165
166/** Remote audio pcm buffer interface operations. */
167static remote_iface_func_ptr_t remote_audio_pcm_iface_ops[] = {
168 [IPC_M_AUDIO_PCM_GET_INFO_STR] = remote_audio_pcm_get_info_str,
169 [IPC_M_AUDIO_PCM_GET_BUFFER] = remote_audio_pcm_get_buffer,
170 [IPC_M_AUDIO_PCM_RELEASE_BUFFER] = remote_audio_pcm_release_buffer,
171 [IPC_M_AUDIO_PCM_START_PLAYBACK] = remote_audio_pcm_start_playback,
172 [IPC_M_AUDIO_PCM_STOP_PLAYBACK] = remote_audio_pcm_stop_playback,
173 [IPC_M_AUDIO_PCM_START_RECORD] = remote_audio_pcm_start_record,
174 [IPC_M_AUDIO_PCM_STOP_RECORD] = remote_audio_pcm_stop_record,
175};
176
177/** Remote audio mixer interface structure. */
178remote_iface_t remote_audio_pcm_buffer_iface = {
179 .method_count = sizeof(remote_audio_pcm_iface_ops) /
180 sizeof(remote_audio_pcm_iface_ops[0]),
181 .methods = remote_audio_pcm_iface_ops
182};
183/*----------------------------------------------------------------------------*/
184void remote_audio_pcm_get_info_str(ddf_fun_t *fun, void *iface,
185 ipc_callid_t callid, ipc_call_t *call)
186{
187 const audio_pcm_buffer_iface_t *pcm_iface = iface;
188
189 if (!pcm_iface->get_info_str) {
190 async_answer_0(callid, ENOTSUP);
191 return;
192 }
193 const char *name = NULL;
194 const int ret = pcm_iface->get_info_str(fun, &name);
195 const size_t name_size = name ? str_size(name) + 1 : 0;
196 async_answer_1(callid, ret, name_size);
197 /* Send the string. */
198 if (ret == EOK && name_size > 0) {
199 size_t size;
200 ipc_callid_t name_id;
201 if (!async_data_read_receive(&name_id, &size)) {
202 async_answer_0(name_id, EPARTY);
203 return;
204 }
205 if (size != name_size) {
206 async_answer_0(name_id, ELIMIT);
207 return;
208 }
209 async_data_read_finalize(name_id, name, name_size);
210 }
211}
212/*----------------------------------------------------------------------------*/
213void remote_audio_pcm_get_buffer(ddf_fun_t *fun, void *iface,
214 ipc_callid_t callid, ipc_call_t *call)
215{
216 const audio_pcm_buffer_iface_t *pcm_iface = iface;
217
218 if (!pcm_iface->get_buffer) {
219 async_answer_0(callid, ENOTSUP);
220 return;
221 }
222 void *buffer = NULL;
223 size_t size = 0;
224 unsigned id = 0;
225 const int ret = pcm_iface->get_buffer(fun, &buffer, &size, &id);
226 async_answer_2(callid, ret, size, id);
227 /* Share the buffer. */
228 if (ret == EOK && size > 0) {
229 size_t share_size;
230 ipc_callid_t name_id;
231 if (!async_share_in_receive(&name_id, &share_size)) {
232 async_answer_0(name_id, EPARTY);
233 return;
234 }
235 if (share_size != size) {
236 async_answer_0(name_id, ELIMIT);
237 return;
238 }
239 async_share_in_finalize(name_id, buffer, PROTO_READ | PROTO_WRITE);
240 }
241}
242/*----------------------------------------------------------------------------*/
243void remote_audio_pcm_release_buffer(ddf_fun_t *fun, void *iface,
244 ipc_callid_t callid, ipc_call_t *call)
245{
246 const audio_pcm_buffer_iface_t *pcm_iface = iface;
247
248 const unsigned id = DEV_IPC_GET_ARG1(*call);
249 const int ret = pcm_iface->release_buffer ?
250 pcm_iface->release_buffer(fun, id) : ENOTSUP;
251 async_answer_0(callid, ret);
252}
253/*----------------------------------------------------------------------------*/
254void remote_audio_pcm_start_playback(ddf_fun_t *fun, void *iface,
255 ipc_callid_t callid, ipc_call_t *call)
256{
257 const audio_pcm_buffer_iface_t *pcm_iface = iface;
258
259 const unsigned id = DEV_IPC_GET_ARG1(*call);
260 const unsigned rate = DEV_IPC_GET_ARG2(*call);
261 const unsigned size = DEV_IPC_GET_ARG3(*call) >> 16;
262 const unsigned channels = (DEV_IPC_GET_ARG3(*call) >> 8) && UINT8_MAX;
263 const bool sign = (bool)(DEV_IPC_GET_ARG3(*call) & 1);
264
265 const int ret = pcm_iface->start_playback
266 ? pcm_iface->start_playback(fun, id, rate, size, channels, sign)
267 : ENOTSUP;
268 async_answer_0(callid, ret);
269}
270/*----------------------------------------------------------------------------*/
271void remote_audio_pcm_stop_playback(ddf_fun_t *fun, void *iface,
272 ipc_callid_t callid, ipc_call_t *call)
273{
274 const audio_pcm_buffer_iface_t *pcm_iface = iface;
275
276 const unsigned id = DEV_IPC_GET_ARG1(*call);
277 const int ret = pcm_iface->stop_playback ?
278 pcm_iface->stop_playback(fun, id) : ENOTSUP;
279 async_answer_0(callid, ret);
280}
281/*----------------------------------------------------------------------------*/
282void remote_audio_pcm_start_record(ddf_fun_t *fun, void *iface,
283 ipc_callid_t callid, ipc_call_t *call)
284{
285 const audio_pcm_buffer_iface_t *pcm_iface = iface;
286
287 const unsigned id = DEV_IPC_GET_ARG1(*call);
288 const unsigned rate = DEV_IPC_GET_ARG2(*call);
289 const unsigned size = DEV_IPC_GET_ARG3(*call) >> 16;
290 const unsigned channels = (DEV_IPC_GET_ARG3(*call) & UINT16_MAX) >> 1;
291 const bool sign = (bool)(DEV_IPC_GET_ARG3(*call) & 1);
292
293 const int ret = pcm_iface->start_record
294 ? pcm_iface->start_record(fun, id, rate, size, channels, sign)
295 : ENOTSUP;
296 async_answer_0(callid, ret);
297}
298/*----------------------------------------------------------------------------*/
299void remote_audio_pcm_stop_record(ddf_fun_t *fun, void *iface,
300 ipc_callid_t callid, ipc_call_t *call)
301{
302 const audio_pcm_buffer_iface_t *pcm_iface = iface;
303
304 const unsigned id = DEV_IPC_GET_ARG1(*call);
305 const int ret = pcm_iface->stop_record ?
306 pcm_iface->stop_record(fun, id) : ENOTSUP;
307 async_answer_0(callid, ret);
308}
309
310#if 0
311/*----------------------------------------------------------------------------*/
312void remote_audio_mixer_get_info(
313 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
314{
315 audio_mixer_iface_t *mixer_iface = iface;
316
317 if (!mixer_iface->get_info) {
318 async_answer_0(callid, ENOTSUP);
319 return;
320 }
321 const char *name = NULL;
322 unsigned items = 0;
323 const int ret = mixer_iface->get_info(fun, &name, &items);
324 const size_t name_size = name ? str_size(name) + 1 : 0;
325 async_answer_2(callid, ret, name_size, items);
326 /* Send the name. */
327 if (ret == EOK && name_size > 0) {
328 size_t size;
329 ipc_callid_t name_id;
330 if (!async_data_read_receive(&name_id, &size)) {
331 async_answer_0(name_id, EPARTY);
332 return;
333 }
334 if (size != name_size) {
335 async_answer_0(name_id, ELIMIT);
336 return;
337 }
338 async_data_read_finalize(name_id, name, name_size);
339 }
340}
341/*----------------------------------------------------------------------------*/
342void remote_audio_mixer_get_item_info(
343 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
344{
345 audio_mixer_iface_t *mixer_iface = iface;
346
347 if (!mixer_iface->get_item_info) {
348 async_answer_0(callid, ENOTSUP);
349 return;
350 }
351
352 const unsigned item = DEV_IPC_GET_ARG1(*call);
353 const char *name = NULL;
354 unsigned channels = 0;
355 const int ret = mixer_iface->get_item_info(fun, item, &name, &channels);
356 const size_t name_size = name ? str_size(name) + 1 : 0;
357 async_answer_2(callid, ret, name_size, channels);
358 /* Send the name. */
359 if (ret == EOK && name_size > 0) {
360 size_t size;
361 ipc_callid_t name_id;
362 if (!async_data_read_receive(&name_id, &size)) {
363 async_answer_0(name_id, EPARTY);
364 return;
365 }
366 if (size != name_size) {
367 async_answer_0(name_id, ELIMIT);
368 return;
369 }
370 async_data_read_finalize(name_id, name, name_size);
371 }
372}
373/*----------------------------------------------------------------------------*/
374void remote_audio_mixer_get_channel_info(
375 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
376{
377 audio_mixer_iface_t *mixer_iface = iface;
378
379 if (!mixer_iface->get_channel_info) {
380 async_answer_0(callid, ENOTSUP);
381 return;
382 }
383
384 const unsigned item = DEV_IPC_GET_ARG1(*call);
385 const unsigned channel = DEV_IPC_GET_ARG2(*call);
386 const char *name = NULL;
387 unsigned levels = 0;
388 const int ret =
389 mixer_iface->get_channel_info(fun, item, channel, &name, &levels);
390 const size_t name_size = name ? str_size(name) + 1 : 0;
391 async_answer_2(callid, ret, name_size, levels);
392 /* Send the name. */
393 if (ret == EOK && name_size > 0) {
394 size_t size;
395 ipc_callid_t name_id;
396 if (!async_data_read_receive(&name_id, &size)) {
397 async_answer_0(name_id, EPARTY);
398 return;
399 }
400 if (size != name_size) {
401 async_answer_0(name_id, ELIMIT);
402 return;
403 }
404 async_data_read_finalize(name_id, name, name_size);
405 }
406}
407/*----------------------------------------------------------------------------*/
408void remote_audio_mixer_channel_mute_set(
409 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
410{
411 audio_mixer_iface_t *mixer_iface = iface;
412
413 if (!mixer_iface->channel_mute_set) {
414 async_answer_0(callid, ENOTSUP);
415 return;
416 }
417 const unsigned item = DEV_IPC_GET_ARG1(*call);
418 const unsigned channel = DEV_IPC_GET_ARG2(*call);
419 const bool mute = DEV_IPC_GET_ARG3(*call);
420 const int ret = mixer_iface->channel_mute_set(fun, item, channel, mute);
421 async_answer_0(callid, ret);
422}
423/*----------------------------------------------------------------------------*/
424void remote_audio_mixer_channel_mute_get(
425 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
426{
427 audio_mixer_iface_t *mixer_iface = iface;
428
429 if (!mixer_iface->channel_mute_get) {
430 async_answer_0(callid, ENOTSUP);
431 return;
432 }
433 const unsigned item = DEV_IPC_GET_ARG1(*call);
434 const unsigned channel = DEV_IPC_GET_ARG2(*call);
435 bool mute = false;
436 const int ret =
437 mixer_iface->channel_mute_get(fun, item, channel, &mute);
438 async_answer_1(callid, ret, mute);
439}
440/*----------------------------------------------------------------------------*/
441void remote_audio_mixer_channel_volume_set(
442 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
443{
444 audio_mixer_iface_t *mixer_iface = iface;
445
446 if (!mixer_iface->channel_volume_set) {
447 async_answer_0(callid, ENOTSUP);
448 return;
449 }
450 const unsigned item = DEV_IPC_GET_ARG1(*call);
451 const unsigned channel = DEV_IPC_GET_ARG2(*call);
452 const unsigned level = DEV_IPC_GET_ARG3(*call);
453 const int ret =
454 mixer_iface->channel_volume_set(fun, item, channel, level);
455 async_answer_0(callid, ret);
456}
457/*----------------------------------------------------------------------------*/
458void remote_audio_mixer_channel_volume_get(
459 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
460{
461 audio_mixer_iface_t *mixer_iface = iface;
462
463 if (!mixer_iface->channel_volume_get) {
464 async_answer_0(callid, ENOTSUP);
465 return;
466 }
467 const unsigned item = DEV_IPC_GET_ARG1(*call);
468 const unsigned channel = DEV_IPC_GET_ARG2(*call);
469 unsigned current = 0, max = 0;
470 const int ret =
471 mixer_iface->channel_volume_get(fun, item, channel, &current, &max);
472 async_answer_2(callid, ret, current, max);
473}
474#endif
475
476/**
477 * @}
478 */
479
Note: See TracBrowser for help on using the repository browser.