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

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

Cleanup audio_pcm interface.

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