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 
snoop_timestamp(snd_pcm_t * pcm)40 static int snoop_timestamp(snd_pcm_t *pcm)
41 {
42     snd_pcm_direct_t *dsnoop = pcm->private_data;
43     snd_pcm_uframes_t ptr1 = -2LL /* invalid value */, ptr2;
44 
45     while (1) {
46         ptr2 = *dsnoop->spcm->hw.ptr;
47         if (ptr1 == ptr2)
48             break;
49         ptr1 = ptr2;
50     }
51     dsnoop->slave_hw_ptr = ptr1;
52     return 0;
53 }
54 
snoop_areas(snd_pcm_direct_t * dsnoop,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)55 static void snoop_areas(snd_pcm_direct_t *dsnoop,
56             const snd_pcm_channel_area_t *src_areas,
57             const snd_pcm_channel_area_t *dst_areas,
58             snd_pcm_uframes_t src_ofs,
59             snd_pcm_uframes_t dst_ofs,
60             snd_pcm_uframes_t size)
61 {
62     unsigned int chn, schn, channels;
63     snd_pcm_format_t format;
64 
65     channels = dsnoop->channels;
66     format = dsnoop->shmptr->s.format;
67 
68     if (dsnoop->spcm->fast_ops->cache_update)
69         dsnoop->spcm->fast_ops->cache_update(
70             dsnoop->spcm->fast_op_arg, src_ofs, size);
71     if (dsnoop->interleaved) {
72         unsigned int fbytes = snd_pcm_format_physical_width(format) / 8;
73         memcpy(((char *)dst_areas[0].addr) + (dst_ofs * channels * fbytes),
74                ((char *)src_areas[0].addr) + (src_ofs * channels * fbytes),
75                size * channels * fbytes);
76     } else {
77 #if 0
78         awalsa_info("src_ofs:%u(0x%x), size:%u(0x%x)\n",
79                 src_ofs, src_ofs,
80                 size, size);
81 #endif
82         for (chn = 0; chn < channels; chn++) {
83             schn = dsnoop->bindings ? dsnoop->bindings[chn] : chn;
84             snd_pcm_area_copy(&dst_areas[chn], dst_ofs, &src_areas[schn], src_ofs, size, format);
85         }
86     }
87 }
88 
snd_pcm_dsnoop_sync_area(snd_pcm_t * pcm,snd_pcm_uframes_t slave_hw_ptr,snd_pcm_uframes_t size)89 static void snd_pcm_dsnoop_sync_area(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr, snd_pcm_uframes_t size)
90 {
91     snd_pcm_direct_t *dsnoop = pcm->private_data;
92     snd_pcm_uframes_t hw_ptr = dsnoop->hw_ptr;
93     snd_pcm_uframes_t transfer;
94     const snd_pcm_channel_area_t *src_areas, *dst_areas;
95 
96     /* add sample areas here */
97     dst_areas = snd_pcm_mmap_areas(pcm);
98     src_areas = snd_pcm_mmap_areas(dsnoop->spcm);
99 
100     hw_ptr %= pcm->buffer_size;
101     slave_hw_ptr %= dsnoop->slave_buffer_size;
102 
103     while (size > 0) {
104         transfer = hw_ptr + size > pcm->buffer_size ? pcm->buffer_size - hw_ptr : size;
105         transfer = slave_hw_ptr + transfer > dsnoop->slave_buffer_size ?
106             dsnoop->slave_buffer_size - slave_hw_ptr : transfer;
107         size -= transfer;
108         snoop_areas(dsnoop, src_areas, dst_areas, slave_hw_ptr, hw_ptr, transfer);
109         slave_hw_ptr += transfer;
110         slave_hw_ptr %= dsnoop->slave_buffer_size;
111         hw_ptr += transfer;
112         hw_ptr %= pcm->buffer_size;
113     }
114 
115 }
116 
snd_pcm_dsnoop_sync_ptr(snd_pcm_t * pcm)117 static int snd_pcm_dsnoop_sync_ptr(snd_pcm_t *pcm)
118 {
119     snd_pcm_direct_t *dsnoop = pcm->private_data;
120     snd_pcm_uframes_t slave_hw_ptr, old_slave_hw_ptr, avail;
121     snd_pcm_sframes_t diff;
122     int err;
123 
124     switch (snd_pcm_state(dsnoop->spcm)) {
125     case SND_PCM_STATE_DISCONNECTED:
126         dsnoop->state = SNDRV_PCM_STATE_DISCONNECTED;
127         return -ENODEV;
128     case SND_PCM_STATE_XRUN:
129         if ((err = snd_pcm_direct_slave_recover(dsnoop)) < 0)
130             return err;
131         break;
132     default:
133         break;
134     }
135 
136     if (dsnoop->slowptr)
137         snd_pcm_hwsync(dsnoop->spcm);
138     old_slave_hw_ptr = dsnoop->slave_hw_ptr;
139     snoop_timestamp(pcm);
140     slave_hw_ptr = dsnoop->slave_hw_ptr;
141     diff = slave_hw_ptr - old_slave_hw_ptr;
142     if (diff > pcm->stop_threshold) {
143         awalsa_debug("old:%lu, new:%lu\n", old_slave_hw_ptr, slave_hw_ptr);
144     }
145     if (diff == 0)      /* fast path */
146         return 0;
147     if (diff < 0) {
148         slave_hw_ptr += dsnoop->slave_boundary;
149         diff = slave_hw_ptr - old_slave_hw_ptr;
150     }
151     snd_pcm_dsnoop_sync_area(pcm, old_slave_hw_ptr, diff);
152     dsnoop->hw_ptr += diff;
153     dsnoop->hw_ptr %= pcm->boundary;
154     // printf("sync ptr diff = %li\n", diff);
155     if (pcm->stop_threshold >= pcm->boundary)   /* don't care */
156         return 0;
157     /*awalsa_debug("hw_ptr=%p, appl_ptr=%p, diff=%d, avail=%d, stop_threshold=%u\n",*/
158         /*dsnoop->hw_ptr, dsnoop->appl_ptr,*/
159         /*diff, snd_pcm_mmap_capture_hw_avail(pcm), pcm->stop_threshold);*/
160     if ((avail = snd_pcm_mmap_capture_hw_avail(pcm)) >= pcm->stop_threshold) {
161         /*gettimestamp(&dsnoop->trigger_tstamp, pcm->tstamp_type);*/
162         dsnoop->state = SND_PCM_STATE_XRUN;
163         dsnoop->avail_max = avail;
164         return -EPIPE;
165     }
166     if (avail > dsnoop->avail_max)
167         dsnoop->avail_max = avail;
168     return 0;
169 
170 
171     return 0;
172 }
173 
snd_pcm_dsnoop_close(snd_pcm_t * pcm)174 static int snd_pcm_dsnoop_close(snd_pcm_t *pcm)
175 {
176     snd_pcm_direct_t *dsnoop = pcm->private_data;
177 
178     snd_pcm_direct_destroy_poll_index(dsnoop);
179     snd_pcm_direct_semaphore_down(dsnoop, DIRECT_IPC_SEM_CLIENT);
180     snd_pcm_direct_last_pcm_drop(dsnoop);
181     snd_pcm_close(dsnoop->spcm);
182     snd_pcm_direct_semaphore_shm_discard(dsnoop);
183     pcm->private_data = NULL;
184     free(dsnoop);
185 
186     return 0;
187 }
188 
snd_pcm_dsnoop_state(snd_pcm_t * pcm)189 static snd_pcm_state_t snd_pcm_dsnoop_state(snd_pcm_t *pcm)
190 {
191     snd_pcm_direct_t *dsnoop = pcm->private_data;
192 
193     snd_pcm_state_t state;
194     state = snd_pcm_state(dsnoop->spcm);
195     switch(state) {
196     case SND_PCM_STATE_SUSPENDED:
197     case SND_PCM_STATE_DISCONNECTED:
198         dsnoop->state = state;
199         return state;
200     case SND_PCM_STATE_XRUN:
201         snd_pcm_direct_slave_recover(dsnoop);
202         break;
203     default:
204         break;
205     }
206     return dsnoop->state;
207 }
208 
snd_pcm_dsnoop_delay(snd_pcm_t * pcm,snd_pcm_sframes_t * delayp)209 static int snd_pcm_dsnoop_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
210 {
211     snd_pcm_direct_t *dsnoop = pcm->private_data;
212     int err;
213 
214     switch(dsnoop->state) {
215     case SNDRV_PCM_STATE_DRAINING:
216     case SNDRV_PCM_STATE_RUNNING:
217         err = snd_pcm_dsnoop_sync_ptr(pcm);
218         if (err < 0)
219             return err;
220         /* Fall through */
221     case SNDRV_PCM_STATE_PREPARED:
222     case SNDRV_PCM_STATE_SUSPENDED:
223         *delayp = snd_pcm_mmap_capture_hw_avail(pcm);
224         return 0;
225     case SNDRV_PCM_STATE_XRUN:
226         return -EPIPE;
227     case SNDRV_PCM_STATE_DISCONNECTED:
228         return -ENODEV;
229     default:
230         return -EBADFD;
231     }
232 }
233 
snd_pcm_dsnoop_hwsync(snd_pcm_t * pcm)234 static int snd_pcm_dsnoop_hwsync(snd_pcm_t *pcm)
235 {
236     snd_pcm_direct_t *dsnoop = pcm->private_data;
237 
238     switch(dsnoop->state) {
239     case SNDRV_PCM_STATE_DRAINING:
240     case SNDRV_PCM_STATE_RUNNING:
241         return snd_pcm_dsnoop_sync_ptr(pcm);
242     case SNDRV_PCM_STATE_PREPARED:
243     case SNDRV_PCM_STATE_SUSPENDED:
244         return 0;
245     case SNDRV_PCM_STATE_XRUN:
246         return -EPIPE;
247     case SNDRV_PCM_STATE_DISCONNECTED:
248         return -ENODEV;
249     default:
250         return -EBADFD;
251     }
252 }
253 
snd_pcm_dsnoop_reset(snd_pcm_t * pcm)254 static int snd_pcm_dsnoop_reset(snd_pcm_t *pcm)
255 {
256     snd_pcm_direct_t *dsnoop = pcm->private_data;
257     dsnoop->hw_ptr %= pcm->period_size;
258     dsnoop->appl_ptr = dsnoop->hw_ptr;
259     dsnoop->slave_appl_ptr = dsnoop->slave_hw_ptr;
260     return 0;
261 }
262 
snd_pcm_dsnoop_start(snd_pcm_t * pcm)263 static int snd_pcm_dsnoop_start(snd_pcm_t *pcm)
264 {
265     snd_pcm_direct_t *dsnoop = pcm->private_data;
266 
267     awalsa_debug("\n");
268     if (dsnoop->state != SND_PCM_STATE_PREPARED)
269         return -EBADFD;
270     snd_pcm_hwsync(dsnoop->spcm);
271     snoop_timestamp(pcm);
272     dsnoop->slave_appl_ptr = dsnoop->slave_hw_ptr;
273     dsnoop->state = SND_PCM_STATE_RUNNING;
274 
275     return 0;
276 }
277 
snd_pcm_dsnoop_drop(snd_pcm_t * pcm)278 static int snd_pcm_dsnoop_drop(snd_pcm_t *pcm)
279 {
280     snd_pcm_direct_t *dsnoop = pcm->private_data;
281     if (dsnoop->state == SND_PCM_STATE_OPEN)
282         return -EBADFD;
283     dsnoop->state = SND_PCM_STATE_SETUP;
284 
285     awalsa_debug("\n");
286     return snd_pcm_direct_last_pcm_drop(dsnoop);
287 }
288 
__snd_pcm_dsnoop_drain(snd_pcm_t * pcm)289 static int __snd_pcm_dsnoop_drain(snd_pcm_t *pcm)
290 {
291     snd_pcm_direct_t *dsnoop = pcm->private_data;
292     snd_pcm_uframes_t stop_threshold;
293     int err;
294 
295     awalsa_debug("\n");
296     if (dsnoop->state == SND_PCM_STATE_OPEN)
297         return -EBADFD;
298     stop_threshold = pcm->stop_threshold;
299     if (pcm->stop_threshold > pcm->buffer_size)
300         pcm->stop_threshold = pcm->buffer_size;
301     while (dsnoop->state == SND_PCM_STATE_RUNNING) {
302         awalsa_debug("\n");
303         err = snd_pcm_dsnoop_sync_ptr(pcm);
304         if (err < 0)
305             break;
306         __snd_pcm_wait_in_lock(pcm, -1);
307     }
308     pcm->stop_threshold = stop_threshold;
309 
310     return snd_pcm_dsnoop_drop(pcm);
311 }
312 
snd_pcm_dsnoop_drain(snd_pcm_t * pcm)313 static int snd_pcm_dsnoop_drain(snd_pcm_t *pcm)
314 {
315     int err;
316 
317     awalsa_debug("\n");
318     snd_pcm_lock(pcm);
319     err = __snd_pcm_dsnoop_drain(pcm);
320     snd_pcm_unlock(pcm);
321     return err;
322 }
323 
snd_pcm_dsnoop_pause(snd_pcm_t * pcm ATTRIBUTE_UNUSED,int enable ATTRIBUTE_UNUSED)324 static int snd_pcm_dsnoop_pause(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int enable ATTRIBUTE_UNUSED)
325 {
326     return -EIO;
327 }
328 
snd_pcm_dsnoop_writei(snd_pcm_t * pcm ATTRIBUTE_UNUSED,const void * buffer ATTRIBUTE_UNUSED,snd_pcm_uframes_t size ATTRIBUTE_UNUSED)329 static snd_pcm_sframes_t snd_pcm_dsnoop_writei(snd_pcm_t *pcm ATTRIBUTE_UNUSED, const void *buffer ATTRIBUTE_UNUSED, snd_pcm_uframes_t size ATTRIBUTE_UNUSED)
330 {
331     return -ENODEV;
332 }
333 
snd_pcm_dsnoop_avail_update(snd_pcm_t * pcm)334 static snd_pcm_sframes_t snd_pcm_dsnoop_avail_update(snd_pcm_t *pcm)
335 {
336     snd_pcm_direct_t *dsnoop = pcm->private_data;
337     int err;
338 
339     if (dsnoop->state == SND_PCM_STATE_RUNNING) {
340         err = snd_pcm_dsnoop_sync_ptr(pcm);
341         if (err < 0)
342             return err;
343     }
344     if (dsnoop->state == SND_PCM_STATE_XRUN)
345         return -EPIPE;
346 
347     return snd_pcm_mmap_capture_avail(pcm);
348 }
349 
snd_pcm_dsnoop_mmap_commit(snd_pcm_t * pcm,snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,snd_pcm_uframes_t size)350 static snd_pcm_sframes_t snd_pcm_dsnoop_mmap_commit(snd_pcm_t *pcm,
351                             snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
352                             snd_pcm_uframes_t size)
353 {
354     snd_pcm_direct_t *dsnoop = pcm->private_data;
355     int err;
356 
357     switch (snd_pcm_state(dsnoop->spcm)) {
358     case SND_PCM_STATE_XRUN:
359         if ((err = snd_pcm_direct_slave_recover(dsnoop)) < 0)
360             return err;
361         break;
362     case SND_PCM_STATE_SUSPENDED:
363         return -ESTRPIPE;
364     default:
365         break;
366     }
367     if (dsnoop->state == SND_PCM_STATE_RUNNING) {
368         err = snd_pcm_dsnoop_sync_ptr(pcm);
369         if (err < 0)
370             return err;
371     }
372 
373     snd_pcm_mmap_appl_forward(pcm, size);
374 
375     return size;
376 }
377 
snd_pcm_dsnoop_dump(snd_pcm_t * pcm)378 static void snd_pcm_dsnoop_dump(snd_pcm_t *pcm)
379 {
380     snd_pcm_direct_t *dsnoop = pcm->private_data;
381 
382     printf("Direct Snoop PCM\n");
383     if (pcm->setup) {
384         printf("Its setup is:\n");
385         snd_pcm_dump_setup(pcm);
386     }
387     if (dsnoop->spcm)
388         snd_pcm_dump(dsnoop->spcm);
389 }
390 
391 static const snd_pcm_ops_t snd_pcm_dsnoop_ops = {
392     .close      = snd_pcm_dsnoop_close,
393     .hw_refine  = snd_pcm_direct_hw_refine,
394     .hw_params  = snd_pcm_direct_hw_params,
395     .hw_free    = snd_pcm_direct_hw_free,
396     .sw_params  = snd_pcm_direct_sw_params,
397     .channel_info   = snd_pcm_direct_channel_info,
398     .dump       = snd_pcm_dsnoop_dump,
399     .mmap       = snd_pcm_direct_mmap,
400     .munmap     = snd_pcm_direct_munmap,
401 };
402 
403 static const snd_pcm_fast_ops_t snd_pcm_dsnoop_fast_ops = {
404     .state      = snd_pcm_dsnoop_state,
405     .hwsync     = snd_pcm_dsnoop_hwsync,
406     .delay      = snd_pcm_dsnoop_delay,
407     .prepare    = snd_pcm_direct_prepare,
408     .reset      = snd_pcm_dsnoop_reset,
409     .start      = snd_pcm_dsnoop_start,
410     .drop       = snd_pcm_dsnoop_drop,
411     .drain      = snd_pcm_dsnoop_drain,
412     .pause      = snd_pcm_dsnoop_pause,
413     .writei     = snd_pcm_dsnoop_writei,
414     .readi      = snd_pcm_mmap_readi,
415     .avail_update   = snd_pcm_dsnoop_avail_update,
416     .mmap_commit    = snd_pcm_dsnoop_mmap_commit,
417     .wait       = snd_pcm_direct_wait,
418 };
419 
_snd_pcm_dsnoop_open(snd_pcm_t ** pcmp,const snd_pcm_config_t * pcm_config,snd_pcm_stream_t stream,int mode)420 int _snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const snd_pcm_config_t *pcm_config,
421         snd_pcm_stream_t stream, int mode)
422 {
423     const snd_pcm_dsnoop_config_t *dsnoop_config = (const snd_pcm_dsnoop_config_t *)(pcm_config->config);
424     int ret = -1, first_instance;
425     snd_pcm_t *pcm = NULL, *spcm = NULL;
426     snd_pcm_direct_t *dsnoop = NULL;
427     const snd_pcm_config_t *sconf = NULL;
428     struct slave_params params;
429 
430     awalsa_debug("\n");
431     if (stream != SND_PCM_STREAM_CAPTURE) {
432         awalsa_err("The dsnoop plugin supports only capture stream");
433         return -EINVAL;
434     }
435 
436     sconf = snd_pcm_config_get_config(dsnoop_config->slave.pcm);
437     if (!sconf) {
438         awalsa_err("can't find pcm slave:%s\n", dsnoop_config->slave.pcm);
439         return -EINVAL;
440     }
441     if (strcmp(sconf->type, "hw") != 0) {
442         awalsa_err("unsupport slave type:%s, only support hw\n", sconf->type);
443         return -EINVAL;
444     }
445 
446     params.format = SND_PCM_FORMAT_S16_LE;
447         params.rate = 48000;
448         params.channels = 2;
449         params.period_size = -1;
450         params.buffer_size = -1;
451         params.period_time = -1;
452         params.buffer_time = -1;
453     awalsa_debug("\n");
454 
455     ret = snd_pcm_slave_conf_hw_params(&dsnoop_config->slave, &params);
456     if (ret < 0)
457         return ret;
458 
459     dsnoop = snd_malloc(sizeof(snd_pcm_direct_t));
460     if (!dsnoop) {
461         awalsa_err("no memory\n");
462         ret = -ENOMEM;
463         goto _err_nosem;
464     }
465 
466     dsnoop->ipc_key = dsnoop_config->ipc_key;
467 
468     ret = snd_pcm_new(&pcm, dsnoop->type = SND_PCM_TYPE_DSNOOP, pcm_config->name, stream, mode);
469     if (ret < 0) {
470         awalsa_err("failed to new hw pcm\n");
471         goto _err_nosem;
472     }
473 
474     first_instance = ret = snd_pcm_direct_semaphore_shm_create_or_connect(dsnoop);
475     if (ret < 0) {
476         awalsa_err("unable to create IPC semaphore shm instance (return: %d)\n", ret);
477         goto _err;
478     }
479 
480     pcm->ops = &snd_pcm_dsnoop_ops;
481     pcm->fast_ops = &snd_pcm_dsnoop_fast_ops;
482     pcm->private_data = dsnoop;
483 
484     dsnoop->state = SND_PCM_STATE_OPEN;
485     dsnoop->sync_ptr = snd_pcm_dsnoop_sync_ptr;
486 
487 retry:
488     if (first_instance) {
489         awalsa_debug("first open, dsnoop=%p\n", dsnoop);
490         ret = snd_pcm_open_config(&spcm, sconf, stream, mode);
491         if (ret < 0) {
492             awalsa_err("unable to open slave\n");
493             goto _err;
494         }
495         if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
496             awalsa_err("dsnoop plugin can be only connected to hw plugin");
497             goto _err;
498         }
499         ret = snd_pcm_direct_initialize_slave(dsnoop, spcm, &params);
500         if (ret < 0) {
501             awalsa_err("unable to initialize slave\n");
502             goto _err;
503         }
504         dsnoop->spcm = spcm;
505 
506         dsnoop->shmptr->type = spcm->type;
507     } else {
508         awalsa_debug("second open, dsnoop=%p\n", dsnoop);
509         ret = snd_pcm_open_config(&spcm, sconf, stream, mode | SND_PCM_APPEND);
510         if (ret < 0) {
511             if (ret == -EBADFD) {
512                 first_instance = 1;
513                 goto retry;
514             }
515             awalsa_err("unable to open slave\n");
516             goto _err;
517         }
518         if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
519             awalsa_err("dsnoop plugin can be only connected to hw plugin");
520             ret = -EINVAL;
521             goto _err;
522         }
523         ret = snd_pcm_direct_initialize_secondary_slave(dsnoop, spcm, &params);
524         if (ret < 0) {
525             awalsa_err("unable to initialize slave\n");
526             goto _err;
527         }
528         dsnoop->spcm = spcm;
529     }
530 
531     /* no need init timer */
532     ret = snd_pcm_direct_initialize_poll_index(dsnoop);
533     if (ret < 0) {
534         awalsa_err("unable to initialize poll index\n");
535         goto _err;
536     }
537 
538     snd_pcm_set_hw_ptr(pcm, &dsnoop->hw_ptr, 0, 0);
539     snd_pcm_set_appl_ptr(pcm, &dsnoop->appl_ptr, 0, 0);
540 
541     pcm->mmap_rw = 1;
542     dsnoop->channels = dsnoop->shmptr->s.channels;
543     dsnoop->slowptr = 1;
544     dsnoop->max_periods = 0;
545     dsnoop->var_periodsize = 0;
546 
547     snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT);
548 
549     *pcmp = pcm;
550     return 0;
551 
552 _err:
553     if (spcm)
554         snd_pcm_close(spcm);
555     snd_pcm_direct_semaphore_shm_discard(dsnoop);
556 
557 _err_nosem:
558     if (dsnoop) {
559         snd_free(dsnoop);
560     }
561     if (pcm)
562         snd_pcm_free(pcm);
563 
564     return ret;
565 }
566