1 /*
2 * Copyright (c) 2019-2025 Allwinner Technology Co., Ltd. ALL rights reserved.
3 *
4 * Allwinner is a trademark of Allwinner Technology Co.,Ltd., registered in
5 * the the people's Republic of China and other countries.
6 * All Allwinner Technology Co.,Ltd. trademarks are used with permission.
7 *
8 * DISCLAIMER
9 * THIRD PARTY LICENCES MAY BE REQUIRED TO IMPLEMENT THE SOLUTION/PRODUCT.
10 * IF YOU NEED TO INTEGRATE THIRD PARTY’S TECHNOLOGY (SONY, DTS, DOLBY, AVS OR MPEGLA, ETC.)
11 * IN ALLWINNERS’SDK OR PRODUCTS, YOU SHALL BE SOLELY RESPONSIBLE TO OBTAIN
12 * ALL APPROPRIATELY REQUIRED THIRD PARTY LICENCES.
13 * ALLWINNER SHALL HAVE NO WARRANTY, INDEMNITY OR OTHER OBLIGATIONS WITH RESPECT TO MATTERS
14 * COVERED UNDER ANY REQUIRED THIRD PARTY LICENSE.
15 * YOU ARE SOLELY RESPONSIBLE FOR YOUR USAGE OF THIRD PARTY’S TECHNOLOGY.
16 *
17 *
18 * THIS SOFTWARE IS PROVIDED BY ALLWINNER"AS IS" AND TO THE MAXIMUM EXTENT
19 * PERMITTED BY LAW, ALLWINNER EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY KIND,
20 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION REGARDING
21 * THE TITLE, NON-INFRINGEMENT, ACCURACY, CONDITION, COMPLETENESS, PERFORMANCE
22 * OR MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23 * IN NO EVENT SHALL ALLWINNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <errno.h>
35 #include <aw-alsa-lib/pcm_plugin.h>
36 #include <aw-alsa-lib/pcm_config.h>
37 #include "pcm_local.h"
38 #include "pcm_direct.h"
39 #include <aw_list.h>
40 #include <aw_common.h>
41
snd_pcm_slave_conf_hw_params(const snd_pcm_direct_slave_config_t * conf,struct slave_params * params)42 int snd_pcm_slave_conf_hw_params(const snd_pcm_direct_slave_config_t *conf, struct slave_params *params)
43 {
44 if (conf->format > SND_PCM_FORMAT_UNKNOWN)
45 params->format = conf->format;
46 if (conf->rate != 0)
47 params->rate = conf->rate;
48 if (conf->channels)
49 params->channels = conf->channels;
50 if (conf->period_size)
51 params->period_size = conf->period_size;
52 if (conf->buffer_size)
53 params->buffer_size = conf->buffer_size;
54 if (conf->periods)
55 params->periods = conf->periods;
56
57 if (params->period_size == -1 && params->period_time == -1)
58 params->period_time = 125000;
59 if (params->format == -2)
60 params->format = SND_PCM_FORMAT_UNKNOWN;
61
62 return 0;
63 }
64
65 typedef struct snd_pcm_direct_ipc {
66 key_t ipc_key;
67 snd_pcm_type_t type;
68 snd_pcm_direct_share_t shmptr;
69 sem_t semid;
70 int use_count;
71 pthread_mutex_t mutex;
72 struct list_head list;
73 }snd_pcm_direct_ipc_t;
74
75 static LIST_HEAD(g_snd_pcm_direct_ipcs);
76 pthread_mutex_t g_snd_pcm_direct_ipcs_mutex;
77
snd_pcm_direct_ipcs_lock_init(void)78 __attribute__((constructor)) static void snd_pcm_direct_ipcs_lock_init(void) {
79 int ret;
80 ret = pthread_mutex_init(&g_snd_pcm_direct_ipcs_mutex, NULL);
81 if (ret != 0)
82 awalsa_err("pthread_mutex_init failed (return: %d)\n", ret);
83 }
84
snd_pcm_direct_ipcs_lock_destroy(void)85 __attribute__((destructor)) static void snd_pcm_direct_ipcs_lock_destroy(void) {
86 pthread_mutex_destroy(&g_snd_pcm_direct_ipcs_mutex);
87 }
88
89 /* lock by mutex */
snd_pcm_direct_ipc_find(snd_pcm_direct_t * dmix)90 static snd_pcm_direct_ipc_t *snd_pcm_direct_ipc_find(snd_pcm_direct_t *dmix)
91 {
92 snd_pcm_direct_ipc_t *ipc;
93
94 list_for_each_entry(ipc, &g_snd_pcm_direct_ipcs, list) {
95 if (ipc->ipc_key == dmix->ipc_key &&
96 ipc->type == dmix->type) {
97 return ipc;
98 }
99 }
100 return NULL;
101 }
102
snd_pcm_direct_ipc_create(snd_pcm_direct_t * dmix)103 static snd_pcm_direct_ipc_t *snd_pcm_direct_ipc_create(snd_pcm_direct_t *dmix)
104 {
105 int ret;
106 snd_pcm_direct_ipc_t *ipc;
107
108 ipc = snd_malloc(sizeof(snd_pcm_direct_ipc_t));
109 if (!ipc) {
110 awalsa_err("no memory\n");
111 return NULL;
112 }
113 ipc->ipc_key = dmix->ipc_key;
114 ipc->type = dmix->type;
115 ipc->use_count = 0;
116 ret = sem_init(&ipc->semid, 0, 1);
117 if (ret != 0) {
118 awalsa_err("sem_init failed\n");
119 snd_free(ipc);
120 return NULL;
121 }
122 pthread_mutex_init(&ipc->mutex, NULL);
123
124 return ipc;
125 }
126
snd_pcm_direct_semaphore_shm_discard(snd_pcm_direct_t * dmix)127 int snd_pcm_direct_semaphore_shm_discard(snd_pcm_direct_t *dmix)
128 {
129 snd_pcm_direct_ipc_t *ipc;
130 ipc = container_of(dmix->semid, snd_pcm_direct_ipc_t, semid);
131
132 if (!ipc) {
133 awalsa_err("can't find sem...\n");
134 return -1;
135 }
136
137 awalsa_debug("before decrease use_count=%d\n", ipc->use_count);
138 if (--ipc->use_count == 0) {
139 /* don't release ipc source */
140 snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
141 dmix->semid = NULL;
142 dmix->shmptr = NULL;
143 awalsa_debug("free ipc, dmix=%p\n", dmix);
144 } else {
145 /* isn't the last sem, sem up */
146 snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
147 dmix->semid = NULL;
148 }
149 dmix->shmptr = NULL;
150 return 0;
151 }
152
153 /* noneed, donot release ipc, avoid open, close crash */
snd_pcm_direct_ipc_release(void)154 int snd_pcm_direct_ipc_release(void)
155 {
156 /* list each */
157 #if 0
158 pthread_mutex_destroy(&ipc->mutex);
159 sem_destroy(&ipc->semid);
160 free(ipc);
161 #endif
162 return 0;
163 }
164
165
166 #define SND_PCM_DIRECT_MAGIC (0xa15ad300 + sizeof(snd_pcm_direct_share_t))
167
snd_pcm_direct_semaphore_shm_create_or_connect(snd_pcm_direct_t * dmix)168 int snd_pcm_direct_semaphore_shm_create_or_connect(snd_pcm_direct_t *dmix)
169 {
170 snd_pcm_direct_ipc_t *ipc;
171 int first_instance = 0;
172
173 pthread_mutex_lock(&g_snd_pcm_direct_ipcs_mutex);
174 ipc = snd_pcm_direct_ipc_find(dmix);
175 if (!ipc) {
176 first_instance = 1;
177 ipc = snd_pcm_direct_ipc_create(dmix);
178 if (!ipc) {
179 pthread_mutex_unlock(&g_snd_pcm_direct_ipcs_mutex);
180 return -1;
181 }
182 list_add(&ipc->list, &g_snd_pcm_direct_ipcs);
183 }
184 pthread_mutex_unlock(&g_snd_pcm_direct_ipcs_mutex);
185
186 dmix->semid = &ipc->semid;
187 snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
188 if (ipc->use_count == 0)
189 first_instance = 1;
190
191 ipc->use_count++;
192 awalsa_debug("use_count=%d\n", ipc->use_count);
193
194 dmix->shmptr = &ipc->shmptr;
195
196 if (first_instance || ipc->use_count == 1) {
197 memset(dmix->shmptr, 0, sizeof(snd_pcm_direct_share_t));
198 dmix->shmptr->magic = SND_PCM_DIRECT_MAGIC;
199 return 1;
200 } else {
201 if (dmix->shmptr->magic != SND_PCM_DIRECT_MAGIC) {
202 awalsa_err("magic error\n");
203 return -EINVAL;
204 }
205 }
206
207 return 0;
208 }
209
snd_pcm_direct_last_pcm_drop(snd_pcm_direct_t * dmix)210 int snd_pcm_direct_last_pcm_drop(snd_pcm_direct_t *dmix)
211 {
212 int ret = 0;
213 snd_pcm_direct_ipc_t *ipc;
214 ipc = container_of(dmix->semid, snd_pcm_direct_ipc_t, semid);
215 if (!ipc) {
216 awalsa_err("can't find sem...\n");
217 return -1;
218 }
219
220 pthread_mutex_lock(&g_snd_pcm_direct_ipcs_mutex);
221 if (ipc->use_count == 1) {
222 awalsa_debug("\n");
223 ret = snd_pcm_drop(dmix->spcm);
224 }
225 pthread_mutex_unlock(&g_snd_pcm_direct_ipcs_mutex);
226 return ret;
227 }
228
229 #if 0
230 int sem_count = 1;
231 #include <console.h>
232 static int cmd_dmixsem(int argc, char *argv[])
233 {
234 snd_pcm_direct_ipc_t *ipc;
235 int ret;
236
237 if (argc == 1)
238 return 0;
239 pthread_mutex_lock(&g_snd_pcm_direct_ipcs_mutex);
240 list_for_each_entry(ipc, &g_snd_pcm_direct_ipcs, list) {
241 if (ipc->ipc_key == 2222 &&
242 ipc->type == SND_PCM_TYPE_DMIX) {
243 goto find_ipc;
244 }
245 }
246 pthread_mutex_unlock(&g_snd_pcm_direct_ipcs_mutex);
247 return 0;
248 find_ipc:
249 printf("found..\n");
250 pthread_mutex_unlock(&g_snd_pcm_direct_ipcs_mutex);
251
252 if (!strcmp("wait", argv[1])) {
253 ret = sem_wait(&ipc->semid);
254 printf("sem wait return=%d\n", ret);
255 } else if (!strcmp("post", argv[1])) {
256 ret = sem_post(&ipc->semid);
257 printf("sem post return=%d\n", ret);
258 }
259 }
260 FINSH_FUNCTION_EXPORT_CMD(cmd_dmixsem, dmixsem, dmix sem cmd);
261 #endif
262
263 #define COPY_SLAVE(field) (dmix->shmptr->s.field = spcm->field)
264
save_slave_setting(snd_pcm_direct_t * dmix,snd_pcm_t * spcm)265 static void save_slave_setting(snd_pcm_direct_t *dmix, snd_pcm_t *spcm)
266 {
267 #if 0
268 spcm->info &= ~SND_PCM_INFO_PAUSE;
269 #endif
270 COPY_SLAVE(access);
271 COPY_SLAVE(format);
272 COPY_SLAVE(channels);
273 COPY_SLAVE(rate);
274 COPY_SLAVE(period_size);
275 COPY_SLAVE(period_time);
276 COPY_SLAVE(periods);
277 COPY_SLAVE(avail_min);
278 COPY_SLAVE(start_threshold);
279 COPY_SLAVE(stop_threshold);
280 COPY_SLAVE(silence_size);
281 COPY_SLAVE(boundary);
282 COPY_SLAVE(buffer_size);
283 COPY_SLAVE(buffer_time);
284 COPY_SLAVE(sample_bits);
285 COPY_SLAVE(frame_bits);
286
287 #if 0
288 dmix->shmptr->s.info &= ~SND_PCM_INFO_RESUME;
289 #endif
290
291 }
292
293 #undef COPY_SLAVE
294
snd_pcm_direct_initialize_slave(snd_pcm_direct_t * dmix,snd_pcm_t * spcm,struct slave_params * params)295 int snd_pcm_direct_initialize_slave(snd_pcm_direct_t *dmix, snd_pcm_t *spcm, struct slave_params *params)
296 {
297 int ret, buffer_is_not_initialized;
298 snd_pcm_hw_params_t hw_params = {0};
299 snd_pcm_sw_params_t sw_params = {0};
300 snd_pcm_uframes_t boundary;
301
302 ret = snd_pcm_hw_params_any(spcm, &hw_params);
303 if (ret < 0) {
304 awalsa_err("snd_pcm_hw_params_any failed\n");
305 return ret;
306 }
307 ret = snd_pcm_hw_params_set_access(spcm, &hw_params,
308 SND_PCM_ACCESS_MMAP_INTERLEAVED);
309 if (ret < 0) {
310 awalsa_err("snd_pcm_hw_params_set_access MMAP_INTERLEAVED failed\n");
311 return ret;
312 }
313
314 if (params->format == SND_PCM_FORMAT_UNKNOWN)
315 ret = -EINVAL;
316 else
317 ret = snd_pcm_hw_params_set_format(spcm, &hw_params,
318 params->format);
319 if (ret < 0) {
320 awalsa_err("snd_pcm_hw_params_set_format %u failed\n", params->format);
321 return ret;
322 }
323 ret = snd_pcm_hw_params_set_channels(spcm, &hw_params, params->channels);
324 if (ret < 0) {
325 awalsa_err("snd_pcm_hw_params_set_channels %u failed\n", params->channels);
326 return ret;
327 }
328 ret = snd_pcm_hw_params_set_rate(spcm, &hw_params, params->rate, 0);
329 if (ret < 0) {
330 awalsa_err("snd_pcm_hw_params_set_rate %u failed\n", params->rate);
331 return ret;
332 }
333 buffer_is_not_initialized = 0;
334 if (params->buffer_time > 0) {
335 ret = snd_pcm_hw_params_set_buffer_time(spcm, &hw_params, params->buffer_time);
336 if (ret < 0) {
337 awalsa_err("snd_pcm_hw_params_set_buffer_time %u failed\n", params->buffer_time);
338 return ret;
339 }
340 } else if (params->buffer_size > 0) {
341 ret = snd_pcm_hw_params_set_buffer_size(spcm, &hw_params, params->buffer_size);
342 if (ret < 0) {
343 awalsa_err("snd_pcm_hw_params_set_buffer_size %lu failed\n", params->buffer_size);
344 return ret;
345 }
346 } else {
347 buffer_is_not_initialized = 1;
348 }
349
350 if (params->period_time > 0) {
351 ret = snd_pcm_hw_params_set_period_time(spcm, &hw_params, params->period_time, 0);
352 if (ret < 0) {
353 awalsa_err("snd_pcm_hw_params_set_period_time %u failed\n", params->period_time);
354 return ret;
355 }
356 } else if (params->period_size > 0) {
357 ret = snd_pcm_hw_params_set_period_size(spcm, &hw_params, params->period_size, 0);
358 if (ret < 0) {
359 awalsa_err("snd_pcm_hw_params_set_period_size %lu failed\n", params->period_size);
360 return ret;
361 }
362 }
363 if (buffer_is_not_initialized && params->periods > 0) {
364 ret = snd_pcm_hw_params_set_periods(spcm, &hw_params, params->periods, 0);
365 if (ret < 0) {
366 awalsa_err("snd_pcm_hw_params_set_periods %u failed\n", params->periods);
367 return ret;
368 }
369 }
370
371 ret = snd_pcm_hw_params(spcm, &hw_params);
372 if (ret < 0) {
373 awalsa_err("unable to install hw params\n");
374 return ret;
375 }
376
377 dmix->shmptr->hw.format =
378 snd_mask_value(hw_param_interval(&hw_params, SND_PCM_HW_PARAM_FORMAT));
379 dmix->shmptr->hw.rate =
380 *hw_param_interval(&hw_params, SND_PCM_HW_PARAM_RATE);
381 dmix->shmptr->hw.buffer_size =
382 *hw_param_interval(&hw_params, SND_PCM_HW_PARAM_BUFFER_SIZE);
383 dmix->shmptr->hw.buffer_time =
384 *hw_param_interval(&hw_params, SND_PCM_HW_PARAM_BUFFER_TIME);
385 dmix->shmptr->hw.period_size =
386 *hw_param_interval(&hw_params, SND_PCM_HW_PARAM_PERIOD_SIZE);
387 dmix->shmptr->hw.period_time =
388 *hw_param_interval(&hw_params, SND_PCM_HW_PARAM_PERIOD_TIME);
389 dmix->shmptr->hw.periods =
390 *hw_param_interval(&hw_params, SND_PCM_HW_PARAM_PERIODS);
391
392 ret = snd_pcm_sw_params_current(spcm, &sw_params);
393 if (ret < 0) {
394 awalsa_err("unable to get current sw_params\n");
395 return ret;
396 }
397 ret = snd_pcm_sw_params_get_boundary(&sw_params, &boundary);
398 if (ret < 0) {
399 awalsa_err("unable to get boundary\n");
400 return ret;
401 }
402 ret = snd_pcm_sw_params_set_stop_threshold(spcm, &sw_params, boundary);
403 if (ret < 0) {
404 awalsa_err("unable to set stop threshold\n");
405 return ret;
406 }
407
408 if (dmix->type != SND_PCM_TYPE_DMIX)
409 goto __skip_silencing;
410 #if 0
411 ret = snd_pcm_sw_params_set_silence_threshold(spcm, &sw_params, 0);
412 if (ret < 0) {
413 awalsa_err("unable to set silence threshold\n");
414 return ret;
415 }
416 #endif
417 ret = snd_pcm_sw_params_set_silence_size(spcm, &sw_params, boundary);
418 if (ret < 0) {
419 awalsa_err("unable to set silence size\n");
420 return ret;
421 }
422
423 __skip_silencing:
424 ret = snd_pcm_sw_params(spcm, &sw_params);
425 if (ret < 0) {
426 awalsa_err("unable to install sw params\n");
427 return ret;
428 }
429
430 /* ensure ringbuffer empty, avoid noise */
431 if (dmix->type == SND_PCM_TYPE_DMIX) {
432 const snd_pcm_channel_area_t *dst_areas;
433 dst_areas = snd_pcm_mmap_areas(spcm);
434 snd_pcm_areas_silence(dst_areas, 0, spcm->channels,
435 spcm->buffer_size, spcm->format);
436 }
437
438 /* start PCM stream (dma start) */
439 ret = snd_pcm_start(spcm);
440 if (ret < 0) {
441 awalsa_err("unable to start PCM stream\n");
442 return ret;
443 }
444
445 save_slave_setting(dmix, spcm);
446
447 dmix->slave_buffer_size = spcm->buffer_size;
448 dmix->slave_period_size = spcm->period_size;
449 dmix->slave_boundary = spcm->boundary;
450
451 spcm->donot_close = 1;
452
453 return 0;
454 }
455
456 #define COPY_SLAVE(field) (spcm->field = dmix->shmptr->s.field)
457
copy_slave_setting(snd_pcm_direct_t * dmix,snd_pcm_t * spcm)458 static void copy_slave_setting(snd_pcm_direct_t *dmix, snd_pcm_t *spcm)
459 {
460 COPY_SLAVE(access);
461 COPY_SLAVE(format);
462 COPY_SLAVE(channels);
463 COPY_SLAVE(rate);
464 COPY_SLAVE(period_size);
465 COPY_SLAVE(period_time);
466 COPY_SLAVE(periods);
467 COPY_SLAVE(avail_min);
468 COPY_SLAVE(start_threshold);
469 COPY_SLAVE(stop_threshold);
470 COPY_SLAVE(silence_size);
471 COPY_SLAVE(boundary);
472 COPY_SLAVE(buffer_size);
473 COPY_SLAVE(buffer_time);
474 COPY_SLAVE(sample_bits);
475 COPY_SLAVE(frame_bits);
476 }
477
478 #undef COPY_SLAVE
479
snd_pcm_direct_initialize_secondary_slave(snd_pcm_direct_t * dmix,snd_pcm_t * spcm,struct slave_params * params)480 int snd_pcm_direct_initialize_secondary_slave(snd_pcm_direct_t *dmix,
481 snd_pcm_t *spcm,
482 struct slave_params *params)
483 {
484 int ret;
485
486 spcm->donot_close = 1;
487 spcm->setup = 1;
488
489 copy_slave_setting(dmix, spcm);
490
491 dmix->slave_buffer_size = spcm->buffer_size;
492 dmix->slave_period_size = spcm->period_size;
493 dmix->slave_boundary = spcm->boundary;
494
495 ret = snd_pcm_mmap(spcm);
496 if (ret < 0) {
497 awalsa_err("unable to mmap channels\n");
498 return ret;
499 }
500 return 0;
501 }
502
snd_pcm_direct_slave_recover(snd_pcm_direct_t * direct)503 int snd_pcm_direct_slave_recover(snd_pcm_direct_t *direct)
504 {
505 int ret;
506 int semerr;
507
508 semerr = snd_pcm_direct_semaphore_down(direct,
509 DIRECT_IPC_SEM_CLIENT);
510 if (semerr < 0) {
511 awalsa_err("SEMDOWN FAILED with err %d\n", semerr);
512 return semerr;
513 }
514
515 if (snd_pcm_state(direct->spcm) != SND_PCM_STATE_XRUN) {
516 /* ignore... someone else already did recovery */
517 semerr = snd_pcm_direct_semaphore_up(direct,
518 DIRECT_IPC_SEM_CLIENT);
519 if (semerr < 0) {
520 awalsa_err("SEMUP FAILED with err %d\n", semerr);
521 return semerr;
522 }
523 return 0;
524 }
525
526 ret = snd_pcm_prepare(direct->spcm);
527 if (ret < 0) {
528 awalsa_err("recover: unable to prepare slave\n");
529 semerr = snd_pcm_direct_semaphore_up(direct,
530 DIRECT_IPC_SEM_CLIENT);
531 if (semerr < 0) {
532 awalsa_err("SEMUP FAILED with err %d\n", semerr);
533 return semerr;
534 }
535 return ret;
536 }
537
538 #if 0
539 if (direct->type == SND_PCM_TYPE_DSHARE) {
540 const snd_pcm_channel_area_t *dst_areas;
541 dst_areas = snd_pcm_mmap_areas(direct->spcm);
542 snd_pcm_areas_silence(dst_areas, 0, direct->spcm->channels,
543 direct->spcm->buffer_size,
544 direct->spcm->format);
545 }
546 #endif
547
548 ret = snd_pcm_start(direct->spcm);
549 if (ret < 0) {
550 awalsa_err("recover: unable to start slave\n");
551 semerr = snd_pcm_direct_semaphore_up(direct,
552 DIRECT_IPC_SEM_CLIENT);
553 if (semerr < 0) {
554 awalsa_err("SEMUP FAILED with err %d\n", semerr);
555 return semerr;
556 }
557 return ret;
558 }
559 direct->shmptr->s.recoveries++;
560 semerr = snd_pcm_direct_semaphore_up(direct,
561 DIRECT_IPC_SEM_CLIENT);
562 if (semerr < 0) {
563 awalsa_err("SEMUP FAILED with err %d\n", semerr);
564 return semerr;
565 }
566 return 0;
567 }
568
569 /*
570 * enter xrun state, if slave xrun occurred
571 * @return: 0 - no xrun >0: xrun happened
572 */
snd_pcm_direct_client_chk_xrun(snd_pcm_direct_t * direct,snd_pcm_t * pcm)573 int snd_pcm_direct_client_chk_xrun(snd_pcm_direct_t *direct, snd_pcm_t *pcm)
574 {
575 if (direct->shmptr->s.recoveries != direct->recoveries) {
576 /* no matter how many xruns we missed -
577 * so don't increment but just update to actual counter
578 */
579 direct->recoveries = direct->shmptr->s.recoveries;
580 pcm->fast_ops->drop(pcm);
581 #if 0
582 /* trigger_tstamp update is missing in drop callbacks */
583 gettimestamp(&direct->trigger_tstamp, pcm->tstamp_type);
584 #endif
585 /* no timer clear:
586 * if slave already entered xrun again the event is lost.
587 * snd_pcm_direct_clear_timer_queue(direct);
588 */
589 direct->state = SND_PCM_STATE_XRUN;
590 return 1;
591 }
592 return 0;
593 }
594
595 /*
596 * ring buffer operation
597 */
snd_pcm_direct_check_interleave(snd_pcm_direct_t * dmix,snd_pcm_t * pcm)598 int snd_pcm_direct_check_interleave(snd_pcm_direct_t *dmix, snd_pcm_t *pcm)
599 {
600 unsigned int chn, channels;
601 int bits, interleaved = 1;
602 const snd_pcm_channel_area_t *dst_areas;
603 const snd_pcm_channel_area_t *src_areas;
604
605 bits = snd_pcm_format_physical_width(pcm->format);
606 if ((bits % 8) != 0)
607 interleaved = 0;
608 channels = dmix->channels;
609 dst_areas = snd_pcm_mmap_areas(dmix->spcm);
610 src_areas = snd_pcm_mmap_areas(pcm);
611 for (chn = 1; chn < channels; chn++) {
612 if (dst_areas[chn-1].addr != dst_areas[chn].addr) {
613 interleaved = 0;
614 break;
615 }
616 if (src_areas[chn-1].addr != src_areas[chn].addr) {
617 interleaved = 0;
618 break;
619 }
620 }
621 for (chn = 0; chn < channels; chn++) {
622 #if 0
623 if (dmix->bindings && dmix->bindings[chn] != chn) {
624 interleaved = 0;
625 break;
626 }
627 #endif
628 if (dst_areas[chn].first != chn * bits ||
629 dst_areas[chn].step != channels * bits) {
630 interleaved = 0;
631 break;
632 }
633 if (src_areas[chn].first != chn * bits ||
634 src_areas[chn].step != channels * bits) {
635 interleaved = 0;
636 break;
637 }
638 }
639 return dmix->interleaved = interleaved;
640 }
641
hw_param_range_refine_one(snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var,snd_interval_t * src)642 static int hw_param_range_refine_one(snd_pcm_hw_params_t *params,
643 snd_pcm_hw_param_t var,
644 snd_interval_t *src)
645 {
646 snd_interval_t *i;
647
648 if (!(params->rmask & (1<<var))) /* nothing to do? */
649 return 0;
650 i = hw_param_interval(params, var);
651 if (snd_range_empty(i)) {
652 awalsa_err("direct range %i empty?\n", (int)var);
653 return -EINVAL;
654 }
655 if (snd_range_refine(i, src))
656 params->cmask |= 1<<var;
657 return 0;
658 }
659
hw_param_range_refine_minmax(snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var,unsigned int imin,unsigned int imax)660 static int hw_param_range_refine_minmax(snd_pcm_hw_params_t *params,
661 snd_pcm_hw_param_t var,
662 unsigned int imin,
663 unsigned int imax)
664 {
665 snd_interval_t t;
666 memset(&t, 0, sizeof(t));
667 snd_range_set_minmax(&t, imin, imax);
668 t.range.integer = 1;
669 return hw_param_range_refine_one(params, var, &t);
670 }
671
snd_range_step(snd_interval_t * i,unsigned int min,unsigned int step)672 static int snd_range_step(snd_interval_t *i, unsigned int min, unsigned int step)
673 {
674 unsigned int n;
675 int changed = 0;
676 n = (i->range.min - min) % step;
677 if (n != 0 || i->range.openmin) {
678 i->range.min += step - n;
679 changed = 1;
680 }
681 n = (i->range.max - min) % step;
682 if (n != 0 || i->range.openmax) {
683 i->range.max -= n;
684 changed = 1;
685 }
686 if (snd_range_checkempty(i)) {
687 i->range.empty = 1;
688 return -EINVAL;
689 }
690 return changed;
691 }
692
snd_pcm_direct_hw_refine(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)693 int snd_pcm_direct_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
694 {
695 snd_pcm_direct_t *direct = pcm->private_data;
696 static const snd_interval_t access = {
697 .mask = (1<<SND_PCM_ACCESS_MMAP_INTERLEAVED) |
698 (1<<SND_PCM_ACCESS_MMAP_NONINTERLEAVED) |
699 (1<<SND_PCM_ACCESS_RW_INTERLEAVED) |
700 (1<<SND_PCM_ACCESS_RW_NONINTERLEAVED)
701 };
702 int err;
703
704 awalsa_debug("\n");
705 #ifdef REFINE_DEBUG
706 awalsa_info("DMIX REFINE (begin):\n");
707 snd_pcm_hw_params_dump(params);
708 #endif
709 if (params->rmask & (1<<SND_PCM_HW_PARAM_ACCESS)) {
710 if (snd_mask_empty(hw_param_interval(params, SND_PCM_HW_PARAM_ACCESS))) {
711 awalsa_err("direct access mask empty?\n");
712 return -EINVAL;
713 }
714 if (snd_mask_refine(hw_param_interval(params, SND_PCM_HW_PARAM_ACCESS), &access))
715 params->cmask |= 1<<SND_PCM_HW_PARAM_ACCESS;
716 }
717 if (params->rmask & (1<<SND_PCM_HW_PARAM_FORMAT)) {
718 if (snd_mask_empty(hw_param_interval(params, SND_PCM_HW_PARAM_FORMAT))) {
719 awalsa_err("direct format mask empty?\n");
720 return -EINVAL;
721 }
722 if (snd_mask_refine_set(hw_param_interval(params, SND_PCM_HW_PARAM_FORMAT),
723 direct->shmptr->hw.format))
724 params->cmask |= 1<<SND_PCM_HW_PARAM_FORMAT;
725 }
726 if (params->rmask & (1<<SND_PCM_HW_PARAM_CHANNELS)) {
727 if (snd_range_empty(hw_param_interval(params, SND_PCM_HW_PARAM_CHANNELS))) {
728 awalsa_err("direct channels mask empty?\n");
729 return -EINVAL;
730 }
731 err = snd_range_refine_set(hw_param_interval(params, SND_PCM_HW_PARAM_CHANNELS),
732 direct->channels);
733 if (err < 0)
734 return err;
735 }
736 err = hw_param_range_refine_one(params, SND_PCM_HW_PARAM_RATE,
737 &direct->shmptr->hw.rate);
738 if (err < 0)
739 return err;
740
741 if (direct->max_periods < 0) {
742 err = hw_param_range_refine_one(params, SND_PCM_HW_PARAM_PERIOD_SIZE,
743 &direct->shmptr->hw.period_size);
744 if (err < 0)
745 return err;
746 err = hw_param_range_refine_one(params, SND_PCM_HW_PARAM_PERIOD_TIME,
747 &direct->shmptr->hw.period_time);
748 if (err < 0)
749 return err;
750 err = hw_param_range_refine_one(params, SND_PCM_HW_PARAM_BUFFER_SIZE,
751 &direct->shmptr->hw.buffer_size);
752 if (err < 0)
753 return err;
754 err = hw_param_range_refine_one(params, SND_PCM_HW_PARAM_BUFFER_TIME,
755 &direct->shmptr->hw.buffer_time);
756 if (err < 0)
757 return err;
758 } else if (params->rmask & ((1<<SND_PCM_HW_PARAM_PERIODS)|
759 (1<<SND_PCM_HW_PARAM_BUFFER_BYTES)|
760 (1<<SND_PCM_HW_PARAM_BUFFER_SIZE)|
761 (1<<SND_PCM_HW_PARAM_BUFFER_TIME)|
762 (1<<SND_PCM_HW_PARAM_PERIOD_TIME)|
763 (1<<SND_PCM_HW_PARAM_PERIOD_SIZE)|
764 (1<<SND_PCM_HW_PARAM_PERIOD_BYTES))) {
765 snd_interval_t period_size = direct->shmptr->hw.period_size;
766 snd_interval_t period_time = direct->shmptr->hw.period_time;
767 int changed;
768 unsigned int max_periods = direct->max_periods;
769 if (max_periods < 2)
770 max_periods = direct->slave_buffer_size / direct->slave_period_size;
771
772 /* make sure buffer size does not exceed slave buffer size */
773 err = hw_param_range_refine_minmax(params, SND_PCM_HW_PARAM_BUFFER_SIZE,
774 2 * direct->slave_period_size, direct->slave_buffer_size);
775 if (err < 0)
776 return err;
777 if (direct->var_periodsize) {
778 /* more tolerant settings... */
779 if (direct->shmptr->hw.buffer_size.range.max / 2 > period_size.range.max)
780 period_size.range.max =
781 direct->shmptr->hw.buffer_size.range.max / 2;
782 if (direct->shmptr->hw.buffer_time.range.max / 2 > period_time.range.max)
783 period_time.range.max =
784 direct->shmptr->hw.buffer_time.range.max / 2;
785 }
786
787 err = hw_param_range_refine_one(params, SND_PCM_HW_PARAM_PERIOD_SIZE,
788 &period_size);
789 if (err < 0)
790 return err;
791 err = hw_param_range_refine_one(params, SND_PCM_HW_PARAM_PERIOD_TIME,
792 &period_time);
793 if (err < 0)
794 return err;
795 do {
796 changed = 0;
797 err = hw_param_range_refine_minmax(params, SND_PCM_HW_PARAM_PERIODS,
798 2, max_periods);
799 if (err < 0)
800 return err;
801 changed |= err;
802 err = snd_pcm_hw_refine_soft(pcm, params);
803 if (err < 0)
804 return err;
805 changed |= err;
806 err = snd_range_step(
807 hw_param_interval(params, SND_PCM_HW_PARAM_PERIOD_SIZE),
808 0, direct->slave_period_size);
809 if (err < 0)
810 return err;
811 changed |= err;
812 if (err)
813 params->rmask |= (1 << SND_PCM_HW_PARAM_PERIOD_SIZE);
814 } while (changed);
815 }
816 #if 0
817 dshare->timer_ticks = hw_param_interval(params, SND_PCM_HW_PARAM_PERIOD_SIZE)->max / dshare->slave_period_size;
818 params->info = dshare->shmptr->s.info;
819 #endif
820
821 #ifdef REFINE_DEBUG
822 awalsa_info("DMIX REFINE (end):\n");
823 snd_pcm_hw_params_dump(params);
824 #endif
825 return 0;
826 }
827
snd_pcm_direct_prepare(snd_pcm_t * pcm)828 int snd_pcm_direct_prepare(snd_pcm_t *pcm)
829 {
830 snd_pcm_direct_t *dmix = pcm->private_data;
831 int err;
832
833 switch (snd_pcm_state(dmix->spcm)) {
834 case SND_PCM_STATE_SETUP:
835 case SND_PCM_STATE_XRUN:
836 case SND_PCM_STATE_SUSPENDED:
837 err = snd_pcm_prepare(dmix->spcm);
838 if (err < 0)
839 return err;
840 snd_pcm_start(dmix->spcm);
841 break;
842 case SND_PCM_STATE_OPEN:
843 case SND_PCM_STATE_DISCONNECTED:
844 return -EBADFD;
845 default:
846 break;
847 }
848 snd_pcm_direct_check_interleave(dmix, pcm);
849 dmix->state = SND_PCM_STATE_PREPARED;
850 dmix->appl_ptr = dmix->last_appl_ptr = 0;
851 dmix->hw_ptr = 0;
852 #if 0
853 return snd_pcm_direct_set_timer_params(dmix);
854 #endif
855 return 0;
856 }
857
snd_pcm_direct_hw_params(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)858 int snd_pcm_direct_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
859 {
860 /*snd_pcm_direct_t *dmix = pcm->private_data;*/
861
862 awalsa_debug("\n");
863 params->can_paused = 0;
864
865 #if 0
866 /*TODO*/
867 snd_pcm_hw_param_change(params, SND_PCM_HW_PARAM_FORMAT);
868 snd_pcm_hw_params_set_format(pcm, params, dmix->spcm->format);
869 snd_pcm_hw_param_change(params, SND_PCM_HW_PARAM_CHANNELS);
870 snd_pcm_hw_params_set_channels(pcm, params, dmix->spcm->channels);
871 snd_pcm_hw_param_change(params, SND_PCM_HW_PARAM_RATE);
872 snd_pcm_hw_params_set_rate(pcm, params, dmix->spcm->rate, NULL);
873 snd_pcm_hw_param_change(params, SND_PCM_HW_PARAM_PERIOD_SIZE);
874 snd_pcm_hw_params_set_period_size(pcm, params, dmix->spcm->period_size, NULL);
875 snd_pcm_hw_param_change(params, SND_PCM_HW_PARAM_PERIODS);
876 snd_pcm_hw_params_set_periods(pcm, params, dmix->spcm->periods, NULL);
877 snd_pcm_hw_param_change(params, SND_PCM_HW_PARAM_BUFFER_SIZE);
878 snd_pcm_hw_params_set_buffer_size(pcm, params, dmix->spcm->buffer_size);
879 #endif
880
881 return 0;
882 }
883
snd_pcm_direct_hw_free(snd_pcm_t * pcm ATTRIBUTE_UNUSED)884 int snd_pcm_direct_hw_free(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
885 {
886 return 0;
887 }
888
snd_pcm_direct_sw_params(snd_pcm_t * pcm,snd_pcm_sw_params_t * params)889 int snd_pcm_direct_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
890 {
891 return 0;
892 }
893
snd_pcm_direct_channel_info(snd_pcm_t * pcm,snd_pcm_channel_info_t * info)894 int snd_pcm_direct_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
895 {
896 return snd_pcm_channel_info_shm(pcm, info, -1);
897 }
898
snd_pcm_direct_mmap(snd_pcm_t * pcm ATTRIBUTE_UNUSED)899 int snd_pcm_direct_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
900 {
901 return 0;
902 }
903
snd_pcm_direct_munmap(snd_pcm_t * pcm ATTRIBUTE_UNUSED)904 int snd_pcm_direct_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
905 {
906 return 0;
907 }
908
snd_pcm_direct_wait(snd_pcm_t * pcm,int timeout)909 int snd_pcm_direct_wait(snd_pcm_t *pcm, int timeout)
910 {
911 snd_pcm_direct_t *dmix = pcm->private_data;
912 snd_pcm_t *spcm = dmix->spcm;
913
914 return snd_pcm_hw_wait_with_index(spcm, dmix->poll_index, timeout);
915 }
916
snd_pcm_direct_initialize_poll_index(snd_pcm_direct_t * dmix)917 int snd_pcm_direct_initialize_poll_index(snd_pcm_direct_t *dmix)
918 {
919 snd_pcm_t *spcm = dmix->spcm;
920
921 dmix->poll_index = snd_pcm_hw_poll_index_init(spcm);
922 if (dmix->poll_index < 0) {
923 awalsa_err("init poll index failed\n");
924 return -1;
925 }
926 return 0;
927 }
928
929
snd_pcm_direct_destroy_poll_index(snd_pcm_direct_t * dmix)930 int snd_pcm_direct_destroy_poll_index(snd_pcm_direct_t *dmix)
931 {
932 snd_pcm_t *spcm = dmix->spcm;
933
934 if (dmix->poll_index <= 0)
935 return 0;
936 return snd_pcm_hw_poll_index_release(spcm, dmix->poll_index);
937 }
938