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