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
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <errno.h>
36 #include <pthread.h>
37 #include <aw-alsa-lib/plugin/pcm_dmix.h>
38 #include <aw-alsa-lib/pcm_config.h>
39 #include "pcm_local.h"
40 #include "pcm_direct.h"
41
42 #include <aw_list.h>
43 #include <aw_common.h>
44
45
46 /* TODO: whether support STATE_RUN_PENDING ? */
47 #define USE_STATE_RUN_PENDING 1
48
49 #if USE_STATE_RUN_PENDING
50 /* start is pending - this state happens when rate plugin does a delayed commit */
51 #define STATE_RUN_PENDING SND_PCM_STATE_LAST
52 #endif
53
54 typedef struct {
55 key_t ipc_key;
56 int shm_sum_id;
57 signed int *sum_buffer;
58 int use_count;
59 pthread_mutex_t lock;
60 struct list_head list;
61 } shm_sum_ipc_t;
62
63 static LIST_HEAD(g_shm_sum_ipcs);
64 static pthread_mutex_t g_shm_sum_ipcs_lock;
65
shm_sum_ipcs_lock_init(void)66 __attribute__((constructor)) static void shm_sum_ipcs_lock_init(void) {
67 int ret;
68 ret = pthread_mutex_init(&g_shm_sum_ipcs_lock, NULL);
69 if (ret != 0)
70 awalsa_err("pthread_mutex_init failed (return: %d)\n", ret);
71 }
72
shm_sum_ipcs_lock_destroy(void)73 __attribute__((destructor)) static void shm_sum_ipcs_lock_destroy(void) {
74 pthread_mutex_destroy(&g_shm_sum_ipcs_lock);
75 }
76
shm_sum_ipc_get(snd_pcm_direct_t * dmix,size_t sum_buffer_size)77 static shm_sum_ipc_t *shm_sum_ipc_get(snd_pcm_direct_t *dmix, size_t sum_buffer_size)
78 {
79 int ret = 0;
80 shm_sum_ipc_t *ipc = NULL;
81
82 pthread_mutex_lock(&g_shm_sum_ipcs_lock);
83 list_for_each_entry(ipc, &g_shm_sum_ipcs, list) {
84 if (ipc->ipc_key == dmix->ipc_key) {
85 pthread_mutex_unlock(&g_shm_sum_ipcs_lock);
86 return ipc;
87 }
88 }
89
90 ipc = snd_malloc(sizeof(shm_sum_ipc_t));
91 if (!ipc) {
92 awalsa_err("no memory\n");
93 goto err_unlock_ipcs;
94 }
95 ipc->ipc_key = dmix->ipc_key;
96 ipc->use_count = 0;
97 awalsa_debug("use_count=%d\n", ipc->use_count);
98 ipc->sum_buffer = snd_malloc(sum_buffer_size);
99 if (!ipc->sum_buffer) {
100 awalsa_err("no memory\n");
101 goto err_free_ipc;
102 }
103 ret = pthread_mutex_init(&ipc->lock, NULL);
104 if (ret != 0) {
105 awalsa_err("pthread_mutex_init failed (return: %d)\n", ret);
106 goto err_free_sum_buffer;
107 }
108
109 list_add(&ipc->list, &g_shm_sum_ipcs);
110 pthread_mutex_unlock(&g_shm_sum_ipcs_lock);
111 return ipc;
112
113 err_free_sum_buffer:
114 free(ipc->sum_buffer);
115 err_free_ipc:
116 free(ipc);
117 err_unlock_ipcs:
118 pthread_mutex_unlock(&g_shm_sum_ipcs_lock);
119 return NULL;
120 }
121
shm_sum_create_or_connect(snd_pcm_direct_t * dmix)122 static int shm_sum_create_or_connect(snd_pcm_direct_t *dmix)
123 {
124 size_t size;
125 shm_sum_ipc_t *ipc = NULL;
126
127 size = dmix->shmptr->s.channels *
128 dmix->shmptr->s.buffer_size *
129 sizeof(signed int);
130
131 ipc = shm_sum_ipc_get(dmix, size);
132 if (!ipc)
133 return -1;
134
135 pthread_mutex_lock(&ipc->lock);
136 ipc->use_count++;
137 awalsa_debug("use_count=%d\n", ipc->use_count);
138 pthread_mutex_unlock(&ipc->lock);
139
140 dmix->u.dmix.shm_sum_id = &ipc->shm_sum_id;
141 dmix->u.dmix.sum_buffer = ipc->sum_buffer;
142 return 0;
143 }
144
shm_sum_discard(snd_pcm_direct_t * dmix)145 static int shm_sum_discard(snd_pcm_direct_t *dmix)
146 {
147 shm_sum_ipc_t *ipc;
148 ipc = container_of(dmix->u.dmix.shm_sum_id, shm_sum_ipc_t, shm_sum_id);
149
150 if (!ipc) {
151 awalsa_err("can't find shm_sum_ipc\n");
152 return -1;
153 }
154
155 pthread_mutex_lock(&ipc->lock);
156 ipc->use_count--;
157 if (ipc->use_count > 0) {
158 pthread_mutex_unlock(&ipc->lock);
159 dmix->u.dmix.sum_buffer = NULL;
160 dmix->u.dmix.shm_sum_id = NULL;
161 return 0;
162 }
163
164 snd_free(ipc->sum_buffer);
165 pthread_mutex_unlock(&ipc->lock);
166 pthread_mutex_destroy(&ipc->lock);
167 pthread_mutex_lock(&g_shm_sum_ipcs_lock);
168 list_del(&ipc->list);
169 snd_free(ipc);
170 pthread_mutex_unlock(&g_shm_sum_ipcs_lock);
171
172 dmix->u.dmix.sum_buffer = NULL;
173 dmix->u.dmix.shm_sum_id = NULL;
174 return 0;
175 }
176
177 #include "pcm_dmix_generic.c"
178 #define mix_select_callbacks(x) generic_mix_select_callbacks(x)
179
mix_areas(snd_pcm_direct_t * dmix,const snd_pcm_channel_area_t * src_areas,const snd_pcm_channel_area_t * dst_areas,snd_pcm_uframes_t src_ofs,snd_pcm_uframes_t dst_ofs,snd_pcm_uframes_t size)180 static void mix_areas(snd_pcm_direct_t *dmix,
181 const snd_pcm_channel_area_t *src_areas,
182 const snd_pcm_channel_area_t *dst_areas,
183 snd_pcm_uframes_t src_ofs,
184 snd_pcm_uframes_t dst_ofs,
185 snd_pcm_uframes_t size)
186 {
187 unsigned int src_step, dst_step;
188 unsigned int chn, dchn, channels, sample_size;
189 mix_areas_t *do_mix_areas;
190
191 channels = dmix->channels;
192 switch (dmix->shmptr->s.format) {
193 case SND_PCM_FORMAT_S16_LE:
194 case SND_PCM_FORMAT_S16_BE:
195 sample_size = 2;
196 do_mix_areas = (mix_areas_t *)dmix->u.dmix.mix_areas_16;
197 break;
198 case SND_PCM_FORMAT_S32_LE:
199 case SND_PCM_FORMAT_S32_BE:
200 sample_size = 4;
201 do_mix_areas = (mix_areas_t *)dmix->u.dmix.mix_areas_32;
202 break;
203 case SND_PCM_FORMAT_S24_LE:
204 sample_size = 4;
205 do_mix_areas = (mix_areas_t *)dmix->u.dmix.mix_areas_24;
206 break;
207 #if 0
208 case SND_PCM_FORMAT_S24_3LE:
209 sample_size = 3;
210 do_mix_areas = (mix_areas_t *)dmix->u.dmix.mix_areas_24;
211 break;
212 #endif
213 case SND_PCM_FORMAT_U8:
214 sample_size = 1;
215 do_mix_areas = (mix_areas_t *)dmix->u.dmix.mix_areas_u8;
216 break;
217 default:
218 return;
219 }
220 if (dmix->interleaved) {
221 /*
222 * process all areas in one loop
223 * it optimizes the memory accesses for this case
224 */
225 do_mix_areas(size * channels,
226 (unsigned char *)dst_areas[0].addr + sample_size * dst_ofs * channels,
227 (unsigned char *)src_areas[0].addr + sample_size * src_ofs * channels,
228 dmix->u.dmix.sum_buffer + dst_ofs * channels,
229 sample_size,
230 sample_size,
231 sizeof(signed int));
232 return;
233 }
234 for (chn = 0; chn < channels; chn++) {
235 #if 0
236 dchn = dmix->bindings ? dmix->bindings[chn] : chn;
237 #else
238 dchn = chn;
239 #endif
240 if (dchn >= dmix->shmptr->s.channels)
241 continue;
242 src_step = src_areas[chn].step / 8;
243 dst_step = dst_areas[dchn].step / 8;
244 do_mix_areas(size,
245 ((unsigned char *)dst_areas[dchn].addr + dst_areas[dchn].first / 8) + dst_ofs * dst_step,
246 ((unsigned char *)src_areas[chn].addr + src_areas[chn].first / 8) + src_ofs * src_step,
247 dmix->u.dmix.sum_buffer + channels * dst_ofs + chn,
248 dst_step,
249 src_step,
250 channels * sizeof(signed int));
251 }
252 }
253
254 #define dmix_down_sem(dmix) snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT)
255 #define dmix_up_sem(dmix) snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT)
256
257 /*
258 * synchronize shm ring buffer with hardware
259 */
snd_pcm_dmix_sync_area(snd_pcm_t * pcm)260 static void snd_pcm_dmix_sync_area(snd_pcm_t *pcm)
261 {
262 snd_pcm_direct_t *dmix = pcm->private_data;
263 snd_pcm_uframes_t slave_hw_ptr, slave_appl_ptr, slave_size;
264 snd_pcm_uframes_t appl_ptr, size, transfer;
265 const snd_pcm_channel_area_t *src_areas, *dst_areas;
266
267 /* calculate the size to transfer */
268 /* check the available size in the local buffer
269 * last_appl_ptr keeps the last updated position
270 */
271 size = dmix->appl_ptr - dmix->last_appl_ptr;
272 if (! size)
273 return;
274 if (size >= pcm->boundary / 2)
275 size = pcm->boundary - size;
276
277 /* the slave_app_ptr can be far behind the slave_hw_ptr */
278 /* reduce mixing and errors here - just skip not catched writes */
279 if (dmix->slave_hw_ptr <= dmix->slave_appl_ptr)
280 slave_size = dmix->slave_appl_ptr - dmix->slave_hw_ptr;
281 else
282 slave_size = dmix->slave_appl_ptr + (dmix->slave_boundary - dmix->slave_hw_ptr);
283 if (slave_size > dmix->slave_buffer_size) {
284 transfer = dmix->slave_buffer_size - slave_size;
285 if (transfer > size)
286 transfer = size;
287 dmix->last_appl_ptr += transfer;
288 dmix->last_appl_ptr %= pcm->boundary;
289 dmix->slave_appl_ptr += transfer;
290 dmix->slave_appl_ptr %= dmix->slave_boundary;
291 size = dmix->appl_ptr - dmix->last_appl_ptr;
292 if (! size)
293 return;
294 if (size >= pcm->boundary / 2)
295 size = pcm->boundary - size;
296 }
297
298 /* check the available size in the slave PCM buffer */
299 slave_hw_ptr = dmix->slave_hw_ptr;
300 /* don't write on the last active period - this area may be cleared
301 * by the driver during mix operation...
302 */
303 slave_hw_ptr -= slave_hw_ptr % dmix->slave_period_size;
304 slave_hw_ptr += dmix->slave_buffer_size;
305 if (slave_hw_ptr >= dmix->slave_boundary)
306 slave_hw_ptr -= dmix->slave_boundary;
307 if (slave_hw_ptr < dmix->slave_appl_ptr)
308 slave_size = slave_hw_ptr + (dmix->slave_boundary - dmix->slave_appl_ptr);
309 else
310 slave_size = slave_hw_ptr - dmix->slave_appl_ptr;
311 if (slave_size < size)
312 size = slave_size;
313 if (! size)
314 return;
315
316 /* add sample areas here */
317 src_areas = snd_pcm_mmap_areas(pcm);
318 dst_areas = snd_pcm_mmap_areas(dmix->spcm);
319 appl_ptr = dmix->last_appl_ptr % pcm->buffer_size;
320 dmix->last_appl_ptr += size;
321 dmix->last_appl_ptr %= pcm->boundary;
322 slave_appl_ptr = dmix->slave_appl_ptr % dmix->slave_buffer_size;
323 dmix->slave_appl_ptr += size;
324 dmix->slave_appl_ptr %= dmix->slave_boundary;
325 dmix_down_sem(dmix);
326 for (;;) {
327 transfer = size;
328 if (appl_ptr + transfer > pcm->buffer_size)
329 transfer = pcm->buffer_size - appl_ptr;
330 if (slave_appl_ptr + transfer > dmix->slave_buffer_size)
331 transfer = dmix->slave_buffer_size - slave_appl_ptr;
332 mix_areas(dmix, src_areas, dst_areas, appl_ptr, slave_appl_ptr, transfer);
333 /* flush cache */
334 if (dmix->spcm->fast_ops->cache_update)
335 dmix->spcm->fast_ops->cache_update(
336 dmix->spcm->fast_op_arg, slave_appl_ptr, transfer);
337 size -= transfer;
338 if (! size)
339 break;
340 slave_appl_ptr += transfer;
341 slave_appl_ptr %= dmix->slave_buffer_size;
342 appl_ptr += transfer;
343 appl_ptr %= pcm->buffer_size;
344 }
345 dmix_up_sem(dmix);
346 }
347
348 /*
349 * synchronize hardware pointer (hw_ptr) with ours
350 */
snd_pcm_dmix_sync_ptr0(snd_pcm_t * pcm,snd_pcm_uframes_t slave_hw_ptr)351 static int snd_pcm_dmix_sync_ptr0(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr)
352 {
353 snd_pcm_direct_t *dmix = pcm->private_data;
354 snd_pcm_uframes_t old_slave_hw_ptr, avail;
355 snd_pcm_sframes_t diff;
356
357 old_slave_hw_ptr = dmix->slave_hw_ptr;
358 dmix->slave_hw_ptr = *dmix->spcm->hw.ptr;
359 diff = slave_hw_ptr - old_slave_hw_ptr;
360 if (diff == 0) /* fast path */
361 return 0;
362 if (dmix->state != SND_PCM_STATE_RUNNING &&
363 dmix->state != SND_PCM_STATE_DRAINING)
364 /* not really started yet - don't update hw_ptr */
365 return 0;
366 if (diff < 0) {
367 slave_hw_ptr += dmix->slave_boundary;
368 diff = slave_hw_ptr - old_slave_hw_ptr;
369 }
370 dmix->hw_ptr += diff;
371 dmix->hw_ptr %= pcm->boundary;
372 if (pcm->stop_threshold >= pcm->boundary) /* don't care */
373 return 0;
374 avail = snd_pcm_mmap_playback_avail(pcm);
375 if (avail > dmix->avail_max)
376 dmix->avail_max = avail;
377 if (avail >= pcm->stop_threshold) {
378 #if 0
379 snd_timer_stop(dmix->timer);
380 gettimestamp(&dmix->trigger_tstamp, pcm->tstamp_type);
381 #endif
382 if (dmix->state == SND_PCM_STATE_RUNNING) {
383 dmix->state = SND_PCM_STATE_XRUN;
384 awalsa_err("avail=%lu, stop=%lu, slave_hw_ptr=%lu, old=%lu, diff=%ld\n",
385 avail, pcm->stop_threshold,
386 slave_hw_ptr, old_slave_hw_ptr, diff);
387 return -EPIPE;
388 }
389 dmix->state = SND_PCM_STATE_SETUP;
390 #if 0
391 /* clear queue to remove pending poll events */
392 snd_pcm_direct_clear_timer_queue(dmix);
393 #endif
394 }
395 return 0;
396 }
397
snd_pcm_dmix_sync_ptr(snd_pcm_t * pcm)398 static int snd_pcm_dmix_sync_ptr(snd_pcm_t *pcm)
399 {
400 snd_pcm_direct_t *dmix = pcm->private_data;
401 int err;
402
403 switch (snd_pcm_state(dmix->spcm)) {
404 case SND_PCM_STATE_DISCONNECTED:
405 dmix->state = SND_PCM_STATE_DISCONNECTED;
406 return -ENODEV;
407 case SND_PCM_STATE_XRUN:
408 if ((err = snd_pcm_direct_slave_recover(dmix)) < 0)
409 return err;
410 break;
411 default:
412 break;
413 }
414 if (snd_pcm_direct_client_chk_xrun(dmix, pcm))
415 return -EPIPE;
416
417 if (dmix->slowptr)
418 snd_pcm_hwsync(dmix->spcm);
419
420 return snd_pcm_dmix_sync_ptr0(pcm, *dmix->spcm->hw.ptr);
421 }
422
423 /*
424 * plugin implementation
425 */
426
snd_pcm_dmix_state(snd_pcm_t * pcm)427 static snd_pcm_state_t snd_pcm_dmix_state(snd_pcm_t *pcm)
428 {
429 snd_pcm_direct_t *dmix = pcm->private_data;
430 int err;
431 snd_pcm_state_t state;
432
433 awalsa_debug("\n");
434 state = snd_pcm_state(dmix->spcm);
435 switch (state) {
436 case SND_PCM_STATE_SUSPENDED:
437 case SND_PCM_STATE_DISCONNECTED:
438 dmix->state = state;
439 return state;
440 case SND_PCM_STATE_XRUN:
441 if ((err = snd_pcm_direct_slave_recover(dmix)) < 0)
442 return err;
443 break;
444 default:
445 break;
446 }
447 snd_pcm_direct_client_chk_xrun(dmix, pcm);
448 #if USE_STATE_RUN_PENDING
449 if (dmix->state == STATE_RUN_PENDING)
450 return SND_PCM_STATE_RUNNING;
451 #endif
452 return dmix->state;
453 }
454
snd_pcm_dmix_delay(snd_pcm_t * pcm,snd_pcm_sframes_t * delayp)455 static int snd_pcm_dmix_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
456 {
457 snd_pcm_direct_t *dmix = pcm->private_data;
458 int err;
459
460 awalsa_debug("\n");
461 switch(dmix->state) {
462 case SND_PCM_STATE_DRAINING:
463 case SND_PCM_STATE_RUNNING:
464 err = snd_pcm_dmix_sync_ptr(pcm);
465 if (err < 0)
466 return err;
467 /* fallthru */
468 case SND_PCM_STATE_PREPARED:
469 case SND_PCM_STATE_SUSPENDED:
470 #if USE_STATE_RUN_PENDING
471 case STATE_RUN_PENDING:
472 *delayp = snd_pcm_mmap_playback_hw_avail(pcm);
473 return 0;
474 #endif
475 case SND_PCM_STATE_XRUN:
476 return -EPIPE;
477 case SND_PCM_STATE_DISCONNECTED:
478 return -ENODEV;
479 default:
480 return -EBADFD;
481 }
482 }
483
snd_pcm_dmix_hwsync(snd_pcm_t * pcm)484 static int snd_pcm_dmix_hwsync(snd_pcm_t *pcm)
485 {
486 snd_pcm_direct_t *dmix = pcm->private_data;
487
488 awalsa_debug("\n");
489 switch(dmix->state) {
490 case SND_PCM_STATE_DRAINING:
491 case SND_PCM_STATE_RUNNING:
492 /* sync slave PCM */
493 return snd_pcm_dmix_sync_ptr(pcm);
494 case SND_PCM_STATE_PREPARED:
495 case SND_PCM_STATE_SUSPENDED:
496 #if USE_STATE_RUN_PENDING
497 case STATE_RUN_PENDING:
498 return 0;
499 #endif
500 case SND_PCM_STATE_XRUN:
501 return -EPIPE;
502 case SND_PCM_STATE_DISCONNECTED:
503 return -ENODEV;
504 default:
505 return -EBADFD;
506 }
507 }
508
reset_slave_ptr(snd_pcm_t * pcm,snd_pcm_direct_t * dmix)509 static void reset_slave_ptr(snd_pcm_t *pcm, snd_pcm_direct_t *dmix)
510 {
511 dmix->slave_appl_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr;
512 if (pcm->buffer_size > pcm->period_size * 2)
513 return;
514 /* If we have too litte periods, better to align the start position
515 * to the period boundary so that the interrupt can be handled properly
516 * at the right time.
517 */
518 dmix->slave_appl_ptr = ((dmix->slave_appl_ptr + dmix->slave_period_size - 1)
519 / dmix->slave_period_size) * dmix->slave_period_size;
520 }
521
snd_pcm_dmix_reset(snd_pcm_t * pcm)522 static int snd_pcm_dmix_reset(snd_pcm_t *pcm)
523 {
524 snd_pcm_direct_t *dmix = pcm->private_data;
525 dmix->hw_ptr %= pcm->period_size;
526 dmix->appl_ptr = dmix->last_appl_ptr = dmix->hw_ptr;
527 reset_slave_ptr(pcm, dmix);
528 return 0;
529 }
530
snd_pcm_dmix_start_timer(snd_pcm_t * pcm,snd_pcm_direct_t * dmix)531 static int snd_pcm_dmix_start_timer(snd_pcm_t *pcm, snd_pcm_direct_t *dmix)
532 {
533
534 snd_pcm_hwsync(dmix->spcm);
535 reset_slave_ptr(pcm, dmix);
536 #if 0
537 err = snd_timer_start(dmix->timer);
538 if (err < 0)
539 return err;
540 #endif
541 dmix->state = SND_PCM_STATE_RUNNING;
542 return 0;
543 }
544
snd_pcm_dmix_avail_update(snd_pcm_t * pcm)545 static snd_pcm_sframes_t snd_pcm_dmix_avail_update(snd_pcm_t *pcm)
546 {
547 snd_pcm_direct_t *dmix = pcm->private_data;
548 int err;
549
550 awalsa_debug("\n");
551 if (dmix->state == SND_PCM_STATE_RUNNING ||
552 dmix->state == SND_PCM_STATE_DRAINING) {
553 if ((err = snd_pcm_dmix_sync_ptr(pcm)) < 0)
554 return err;
555 }
556 if (dmix->state == SND_PCM_STATE_XRUN)
557 return -EPIPE;
558
559 return snd_pcm_mmap_playback_avail(pcm);
560 }
561
snd_pcm_dmix_mmap_commit(snd_pcm_t * pcm,snd_pcm_uframes_t offset,snd_pcm_uframes_t size)562 static snd_pcm_sframes_t snd_pcm_dmix_mmap_commit(snd_pcm_t *pcm,
563 snd_pcm_uframes_t offset, snd_pcm_uframes_t size)
564 {
565 snd_pcm_direct_t *dmix = pcm->private_data;
566 int err;
567
568 awalsa_debug("\n");
569 switch (snd_pcm_state(dmix->spcm)) {
570 case SND_PCM_STATE_XRUN:
571 if ((err = snd_pcm_direct_slave_recover(dmix)) < 0)
572 return err;
573 break;
574 case SND_PCM_STATE_SUSPENDED:
575 return -ESTRPIPE;
576 default:
577 break;
578 }
579 if (snd_pcm_direct_client_chk_xrun(dmix, pcm))
580 return -EPIPE;
581 if (! size)
582 return 0;
583 snd_pcm_mmap_appl_forward(pcm, size);
584 #if USE_STATE_RUN_PENDING
585 if (dmix->state == STATE_RUN_PENDING) {
586 if ((err = snd_pcm_dmix_start_timer(pcm, dmix)) < 0)
587 return err;
588 } else if (dmix->state == SND_PCM_STATE_RUNNING ||
589 dmix->state == SND_PCM_STATE_DRAINING) {
590 if ((err = snd_pcm_dmix_sync_ptr(pcm)) < 0)
591 return err;
592 }
593 #else
594 if (dmix->state == SND_PCM_STATE_RUNNING ||
595 dmix->state == SND_PCM_STATE_DRAINING) {
596 if ((err = snd_pcm_dmix_sync_ptr(pcm)) < 0)
597 return err;
598 }
599 #endif
600 if (dmix->state == SND_PCM_STATE_RUNNING ||
601 dmix->state == SND_PCM_STATE_DRAINING) {
602 /* ok, we commit the changes after the validation of area */
603 /* it's intended, although the result might be crappy */
604 snd_pcm_dmix_sync_area(pcm);
605 #if 0
606 /* clear timer queue to avoid a bogus return from poll */
607 if (snd_pcm_mmap_playback_avail(pcm) < pcm->avail_min)
608 snd_pcm_direct_clear_timer_queue(dmix);
609 #endif
610 }
611 return size;
612 }
613
snd_pcm_dmix_start(snd_pcm_t * pcm)614 static int snd_pcm_dmix_start(snd_pcm_t *pcm)
615 {
616 snd_pcm_direct_t *dmix = pcm->private_data;
617 snd_pcm_sframes_t avail;
618 int err;
619
620 awalsa_debug("\n");
621 if (dmix->state != SND_PCM_STATE_PREPARED)
622 return -EBADFD;
623 avail = snd_pcm_mmap_playback_hw_avail(pcm);
624 if (avail == 0) {
625 #if USE_STATE_RUN_PENDING
626 dmix->state = STATE_RUN_PENDING;
627 #else
628 return 0;
629 #endif
630 } else if (avail < 0) {
631 return 0;
632 } else {
633 if ((err = snd_pcm_dmix_start_timer(pcm, dmix)) < 0)
634 return err;
635 snd_pcm_dmix_sync_area(pcm);
636 }
637 #if 0
638 gettimestamp(&dmix->trigger_tstamp, pcm->tstamp_type);
639 #endif
640 return 0;
641 }
642
snd_pcm_dmix_drop(snd_pcm_t * pcm)643 static int snd_pcm_dmix_drop(snd_pcm_t *pcm)
644 {
645 snd_pcm_direct_t *dmix = pcm->private_data;
646
647 awalsa_debug("\n");
648 if (dmix->state == SND_PCM_STATE_OPEN)
649 return -EBADFD;
650 dmix->state = SND_PCM_STATE_SETUP;
651
652 awalsa_debug("\n");
653 return snd_pcm_direct_last_pcm_drop(dmix);
654 }
655
656 /* locked version */
__snd_pcm_dmix_drain(snd_pcm_t * pcm)657 static int __snd_pcm_dmix_drain(snd_pcm_t *pcm)
658 {
659 snd_pcm_direct_t *dmix = pcm->private_data;
660 snd_pcm_uframes_t stop_threshold;
661 int err = 0;
662
663 switch (snd_pcm_state(dmix->spcm)) {
664 case SND_PCM_STATE_SUSPENDED:
665 return -ESTRPIPE;
666 default:
667 break;
668 }
669
670 if (dmix->state == SND_PCM_STATE_OPEN)
671 return -EBADFD;
672 if (dmix->state == SND_PCM_STATE_PREPARED) {
673 if (snd_pcm_mmap_playback_hw_avail(pcm) > 0)
674 snd_pcm_dmix_start(pcm);
675 else {
676 snd_pcm_dmix_drop(pcm);
677 return 0;
678 }
679 }
680
681 if (dmix->state == SND_PCM_STATE_XRUN) {
682 snd_pcm_dmix_drop(pcm);
683 return 0;
684 }
685
686 stop_threshold = pcm->stop_threshold;
687 if (pcm->stop_threshold > pcm->buffer_size)
688 pcm->stop_threshold = pcm->buffer_size;
689 dmix->state = SND_PCM_STATE_DRAINING;
690 do {
691 err = snd_pcm_dmix_sync_ptr(pcm);
692 if (err < 0) {
693 snd_pcm_dmix_drop(pcm);
694 goto done;
695 }
696 if (dmix->state == SND_PCM_STATE_DRAINING) {
697 snd_pcm_dmix_sync_area(pcm);
698 #if 0
699 if ((pcm->mode & SND_PCM_NONBLOCK) == 0) {
700 snd_pcm_wait_nocheck(pcm, -1);
701 snd_pcm_direct_clear_timer_queue(dmix); /* force poll to wait */
702 }
703 #else
704 snd_pcm_wait_nocheck(pcm, -1);
705 #endif
706
707 switch (snd_pcm_state(dmix->spcm)) {
708 case SND_PCM_STATE_SUSPENDED:
709 err = -ESTRPIPE;
710 goto done;
711 default:
712 break;
713 }
714 }
715 #if 0
716 if (pcm->mode & SND_PCM_NONBLOCK) {
717 if (dmix->state == SND_PCM_STATE_DRAINING) {
718 err = -EAGAIN;
719 goto done;
720 }
721 }
722 #endif
723 } while (dmix->state == SND_PCM_STATE_DRAINING);
724 done:
725 pcm->stop_threshold = stop_threshold;
726 return err;
727 }
728
snd_pcm_dmix_drain(snd_pcm_t * pcm)729 static int snd_pcm_dmix_drain(snd_pcm_t *pcm)
730 {
731 int err;
732
733 awalsa_debug("\n");
734 snd_pcm_lock(pcm);
735 err = __snd_pcm_dmix_drain(pcm);
736 snd_pcm_unlock(pcm);
737 return err;
738 }
739
snd_pcm_dmix_pause(snd_pcm_t * pcm,int enable)740 static int snd_pcm_dmix_pause(snd_pcm_t *pcm, int enable)
741 {
742 awalsa_debug("\n");
743 return -EIO;
744 }
745
snd_pcm_dmix_close(snd_pcm_t * pcm)746 static int snd_pcm_dmix_close(snd_pcm_t *pcm)
747 {
748 int ret;
749 snd_pcm_direct_t *dmix = pcm->private_data;
750
751 snd_pcm_direct_destroy_poll_index(dmix);
752 snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
753 snd_pcm_direct_last_pcm_drop(dmix);
754 ret = snd_pcm_close(dmix->spcm);
755 if (ret < 0)
756 return ret;
757 ret = shm_sum_discard(dmix);
758 if (ret < 0)
759 return ret;
760 ret = snd_pcm_direct_semaphore_shm_discard(dmix);
761 if (ret < 0)
762 return ret;
763
764 pcm->private_data = NULL;
765 free(dmix);
766
767 return 0;
768 }
769
snd_pcm_dmix_readi(snd_pcm_t * pcm,void * buffer,snd_pcm_uframes_t size)770 static snd_pcm_sframes_t snd_pcm_dmix_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
771 {
772 return -ENODEV;
773 }
774
snd_pcm_dmix_may_wait_for_avail_min(snd_pcm_t * pcm,snd_pcm_uframes_t avail)775 static int snd_pcm_dmix_may_wait_for_avail_min(snd_pcm_t *pcm, snd_pcm_uframes_t avail)
776 {
777 awalsa_debug("\n");
778 return 1;
779 }
780
snd_pcm_dmix_dump(snd_pcm_t * pcm)781 static void snd_pcm_dmix_dump(snd_pcm_t *pcm)
782 {
783 snd_pcm_direct_t *dmix = pcm->private_data;
784
785 printf("Direct Stream Mixing PCM\n");
786 if (pcm->setup) {
787 printf("Its setup is:\n");
788 snd_pcm_dump_setup(pcm);
789 }
790 if (dmix->spcm)
791 snd_pcm_dump(dmix->spcm);
792 }
793
794 static const snd_pcm_ops_t snd_pcm_dmix_ops = {
795 .close = snd_pcm_dmix_close,
796 .hw_refine = snd_pcm_direct_hw_refine,
797 .hw_params = snd_pcm_direct_hw_params,
798 .hw_free = snd_pcm_direct_hw_free,
799 .sw_params = snd_pcm_direct_sw_params,
800 .channel_info = snd_pcm_direct_channel_info,
801 .dump = snd_pcm_dmix_dump,
802 .mmap = snd_pcm_direct_mmap,
803 .munmap = snd_pcm_direct_munmap,
804 };
805
806 static const snd_pcm_fast_ops_t snd_pcm_dmix_fast_ops = {
807 .state = snd_pcm_dmix_state,
808 .hwsync = snd_pcm_dmix_hwsync,
809 .delay = snd_pcm_dmix_delay,
810 .prepare = snd_pcm_direct_prepare,
811 .reset = snd_pcm_dmix_reset,
812 .start = snd_pcm_dmix_start,
813 .drop = snd_pcm_dmix_drop,
814 .drain = snd_pcm_dmix_drain,
815 .pause = snd_pcm_dmix_pause,
816 //.resume = snd_pcm_direct_resume,
817 .writei = snd_pcm_mmap_writei,
818 .readi = snd_pcm_dmix_readi,
819 .avail_update = snd_pcm_dmix_avail_update,
820 .mmap_commit = snd_pcm_dmix_mmap_commit,
821 .may_wait_for_avail_min = snd_pcm_dmix_may_wait_for_avail_min,
822 .wait = snd_pcm_direct_wait,
823 };
824
_snd_pcm_dmix_open(snd_pcm_t ** pcmp,const snd_pcm_config_t * pcm_config,snd_pcm_stream_t stream,int mode)825 int _snd_pcm_dmix_open(snd_pcm_t **pcmp, const snd_pcm_config_t *pcm_config,
826 snd_pcm_stream_t stream, int mode)
827 {
828 const snd_pcm_dmix_config_t *dmix_config =
829 (const snd_pcm_dmix_config_t *)(pcm_config->config);
830 snd_pcm_t *pcm = NULL, *spcm = NULL;
831 snd_pcm_direct_t *dmix = NULL;
832 const snd_pcm_config_t *sconf = NULL;
833 struct slave_params params;
834 int ret = -1;
835 int first_instance;
836
837 awalsa_debug("\n");
838 if (stream != SND_PCM_STREAM_PLAYBACK) {
839 awalsa_err("The dmix plugin supports only playback stream");
840 return -EINVAL;
841 }
842
843 sconf = snd_pcm_config_get_config(dmix_config->slave.pcm);
844 if (!sconf) {
845 awalsa_err("can't find pcm slave:%s\n", dmix_config->slave.pcm);
846 return -EINVAL;
847 }
848 if (strcmp(sconf->type, "hw") != 0) {
849 awalsa_err("unsupport slave type:%s, only support hw\n", sconf->type);
850 return -EINVAL;
851 }
852
853 params.format = SND_PCM_FORMAT_S16_LE;
854 params.rate = 48000;
855 params.channels = 2;
856 params.period_size = -1;
857 params.buffer_size = -1;
858 params.period_time = -1;
859 params.buffer_time = -1;
860
861 ret = snd_pcm_slave_conf_hw_params(&dmix_config->slave, ¶ms);
862 if (ret < 0)
863 return ret;
864
865 dmix = snd_malloc(sizeof(snd_pcm_direct_t));
866 if (!dmix) {
867 awalsa_err("no memory\n");
868 ret = -ENOMEM;
869 goto _err_nosem;
870 }
871
872 dmix->ipc_key = dmix_config->ipc_key;
873 dmix->type = SND_PCM_TYPE_DMIX;
874
875 ret = snd_pcm_new(&pcm, dmix->type, pcm_config->name, stream, mode);
876 if (ret < 0) {
877 awalsa_err("failed to new dmix pcm\n");
878 goto _err_nosem;
879 }
880
881 ret = snd_pcm_direct_semaphore_shm_create_or_connect(dmix);
882 if (ret < 0) {
883 awalsa_err("unable to create IPC semaphore shm instance (return: %d)\n", ret);
884 goto _err;
885 }
886
887 first_instance = ret;
888
889 pcm->ops = &snd_pcm_dmix_ops;
890 pcm->fast_ops = &snd_pcm_dmix_fast_ops;
891 pcm->private_data = dmix;
892
893 // TODO: is complete?
894 dmix->state = SND_PCM_STATE_OPEN;
895 dmix->sync_ptr = snd_pcm_dmix_sync_ptr;
896
897 retry:
898 if (first_instance) {
899 awalsa_debug("first open, dmix=%p\n", dmix);
900 ret = snd_pcm_open_config(&spcm, sconf, stream, mode);
901 if (ret < 0) {
902 awalsa_err("unable to open slave\n");
903 goto _err;
904 }
905 if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
906 awalsa_err("dmix plugin can be only connected to hw plugin\n");
907 goto _err;
908 }
909 ret = snd_pcm_direct_initialize_slave(dmix, spcm, ¶ms);
910 if (ret < 0) {
911 awalsa_err("unable to initialize slave\n");
912 goto _err;
913 }
914 dmix->spcm = spcm;
915 dmix->shmptr->type = spcm->type;
916 } else {
917 awalsa_debug("second open, dmix=%p\n", dmix);
918 ret = snd_pcm_open_config(&spcm, sconf, stream, mode | SND_PCM_APPEND);
919 if (ret < 0) {
920 if (ret == -EBADFD) {
921 first_instance = 1;
922 goto retry;
923 }
924 awalsa_err("unable to open slave\n");
925 goto _err;
926 }
927 if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
928 awalsa_err("dmix plugin can be only connected to hw plugin\n");
929 ret = -EINVAL;
930 goto _err;
931 }
932 ret = snd_pcm_direct_initialize_secondary_slave(dmix, spcm, ¶ms);
933 if (ret < 0) {
934 awalsa_err("unable to initialize slave\n");
935 goto _err;
936 }
937 dmix->spcm = spcm;
938 }
939
940 ret = shm_sum_create_or_connect(dmix);
941 if (ret < 0) {
942 awalsa_err("unable to initialize sum ring buffer\n");
943 goto _err;
944 }
945
946 mix_select_callbacks(dmix);
947
948 ret = snd_pcm_direct_initialize_poll_index(dmix);
949 if (ret < 0) {
950 awalsa_err("unable to initialize poll index\n");
951 goto _err;
952 }
953
954 pcm->mmap_rw = 1;
955 snd_pcm_set_hw_ptr(pcm, &dmix->hw_ptr, 0, 0);
956 snd_pcm_set_appl_ptr(pcm, &dmix->appl_ptr, 0, 0);
957 dmix->slowptr = 1;
958 dmix->max_periods = 0;
959 dmix->var_periodsize = 0;
960
961 dmix->channels = dmix->shmptr->s.channels;
962
963 snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
964
965 *pcmp = pcm;
966 return 0;
967
968 _err:
969 if (spcm)
970 snd_pcm_close(spcm);
971 snd_pcm_direct_semaphore_shm_discard(dmix);
972
973 _err_nosem:
974 if (dmix)
975 snd_free(dmix);
976 return ret;
977 }
978