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, &params);
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, &params);
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, &params);
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