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, ¶ms);
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, ¶ms);
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, ¶ms);
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