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 <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <errno.h>
37 #include "pcm_local.h"
38
snd_pcm_mmap_appl_backward(snd_pcm_t * pcm,snd_pcm_uframes_t frames)39 void snd_pcm_mmap_appl_backward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
40 {
41 snd_pcm_sframes_t appl_ptr = *pcm->appl.ptr;
42 appl_ptr -= frames;
43 if (appl_ptr < 0)
44 appl_ptr += pcm->boundary;
45 *pcm->appl.ptr = appl_ptr;
46 }
47
snd_pcm_mmap_appl_forward(snd_pcm_t * pcm,snd_pcm_uframes_t frames)48 void snd_pcm_mmap_appl_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
49 {
50 snd_pcm_uframes_t appl_ptr = *pcm->appl.ptr;
51 appl_ptr += frames;
52 if (appl_ptr >= pcm->boundary)
53 appl_ptr -= pcm->boundary;
54 *pcm->appl.ptr = appl_ptr;
55 }
56
snd_pcm_mmap_hw_backward(snd_pcm_t * pcm,snd_pcm_uframes_t frames)57 void snd_pcm_mmap_hw_backward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
58 {
59 snd_pcm_sframes_t hw_ptr = *pcm->hw.ptr;
60 hw_ptr -= frames;
61 if (hw_ptr < 0)
62 hw_ptr += pcm->boundary;
63 *pcm->hw.ptr = hw_ptr;
64 }
65
snd_pcm_mmap_hw_forward(snd_pcm_t * pcm,snd_pcm_uframes_t frames)66 void snd_pcm_mmap_hw_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
67 {
68 snd_pcm_uframes_t hw_ptr = *pcm->hw.ptr;
69 hw_ptr += frames;
70 if (hw_ptr >= pcm->boundary)
71 hw_ptr -= pcm->boundary;
72 *pcm->hw.ptr = hw_ptr;
73 }
74
snd_pcm_mmap_write_areas(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,snd_pcm_uframes_t size)75 static snd_pcm_sframes_t snd_pcm_mmap_write_areas(snd_pcm_t *pcm,
76 const snd_pcm_channel_area_t *areas,
77 snd_pcm_uframes_t offset,
78 snd_pcm_uframes_t size)
79 {
80 snd_pcm_uframes_t xfer = 0;
81
82 if (snd_pcm_mmap_playback_avail(pcm) < size) {
83 awalsa_err("too short avail %ld to size %ld\n", snd_pcm_mmap_playback_avail(pcm), size);
84 return -EPIPE;
85 }
86 awalsa_debug("size: %lu\n", size);
87 while (size > 0) {
88 const snd_pcm_channel_area_t *pcm_areas;
89 snd_pcm_uframes_t pcm_offset;
90 snd_pcm_uframes_t frames = size;
91 snd_pcm_sframes_t result;
92
93 __snd_pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
94 snd_pcm_areas_copy(pcm_areas, pcm_offset,
95 areas, offset,
96 pcm->channels,
97 frames, pcm->format);
98 result = __snd_pcm_mmap_commit(pcm, pcm_offset, frames);
99 if (result < 0)
100 return xfer > 0 ? (snd_pcm_sframes_t)xfer : result;
101 offset += result;
102 xfer += result;
103 size -= result;
104 }
105 return (snd_pcm_sframes_t)xfer;
106 }
107
snd_pcm_mmap_writei(snd_pcm_t * pcm,const void * buffer,snd_pcm_uframes_t size)108 snd_pcm_sframes_t snd_pcm_mmap_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
109 {
110 snd_pcm_channel_area_t areas[pcm->channels];
111 snd_pcm_areas_from_buf(pcm, areas, (void*)buffer);
112 return snd_pcm_write_areas(pcm, areas, 0, size,
113 snd_pcm_mmap_write_areas);
114 }
115
snd_pcm_mmap_read_areas(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,snd_pcm_uframes_t size)116 static snd_pcm_sframes_t snd_pcm_mmap_read_areas(snd_pcm_t *pcm,
117 const snd_pcm_channel_area_t *areas,
118 snd_pcm_uframes_t offset,
119 snd_pcm_uframes_t size)
120 {
121 snd_pcm_uframes_t xfer = 0;
122
123 if (snd_pcm_mmap_capture_avail(pcm) < size) {
124 awalsa_err("too short avail %ld to size %ld\n", snd_pcm_mmap_capture_avail(pcm), size);
125 return -EPIPE;
126 }
127 while (size > 0) {
128 const snd_pcm_channel_area_t *pcm_areas;
129 snd_pcm_uframes_t pcm_offset;
130 snd_pcm_uframes_t frames = size;
131 snd_pcm_sframes_t result;
132
133 __snd_pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
134 snd_pcm_areas_copy(areas, offset,
135 pcm_areas, pcm_offset,
136 pcm->channels,
137 frames, pcm->format);
138 result = __snd_pcm_mmap_commit(pcm, pcm_offset, frames);
139 if (result < 0)
140 return xfer > 0 ? (snd_pcm_sframes_t)xfer : result;
141 offset += result;
142 xfer += result;
143 size -= result;
144 }
145 return (snd_pcm_sframes_t)xfer;
146 }
147
snd_pcm_mmap_readi(snd_pcm_t * pcm,void * buffer,snd_pcm_uframes_t size)148 snd_pcm_sframes_t snd_pcm_mmap_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
149 {
150 snd_pcm_channel_area_t areas[pcm->channels];
151 snd_pcm_areas_from_buf(pcm, areas, buffer);
152 return snd_pcm_read_areas(pcm, areas, 0, size,
153 snd_pcm_mmap_read_areas);
154 }
155
snd_pcm_channel_info_shm(snd_pcm_t * pcm,snd_pcm_channel_info_t * info,int shmid)156 int snd_pcm_channel_info_shm(snd_pcm_t *pcm, snd_pcm_channel_info_t *info, int shmid)
157 {
158 switch (pcm->access) {
159 case SND_PCM_ACCESS_MMAP_INTERLEAVED:
160 case SND_PCM_ACCESS_RW_INTERLEAVED:
161 info->first = info->channel * pcm->sample_bits;
162 info->step = pcm->frame_bits;
163 break;
164 case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
165 case SND_PCM_ACCESS_RW_NONINTERLEAVED:
166 info->first = 0;
167 info->step = pcm->sample_bits;
168 break;
169 default:
170 awalsa_err("invalid access type %d\n", pcm->access);
171 return -EINVAL;
172 }
173 info->addr = 0;
174 info->type = SND_PCM_AREA_LOCAL;
175 return 0;
176 }
177
snd_pcm_mmap(snd_pcm_t * pcm)178 int snd_pcm_mmap(snd_pcm_t *pcm)
179 {
180 int err;
181 unsigned int c;
182 assert(pcm);
183 assert(pcm->ops->mmap);
184
185 if (!pcm->setup) {
186 awalsa_err("PCM not set up\n");
187 return -EIO;
188 }
189 if (pcm->mmap_channels || pcm->running_areas) {
190 awalsa_err("Already mmapped\n");
191 return -EBUSY;
192 }
193 err = pcm->ops->mmap(pcm);
194 if (err < 0)
195 return err;
196 if (pcm->mmap_shadow)
197 return 0;
198 pcm->mmap_channels = calloc(pcm->channels, sizeof(pcm->mmap_channels[0]));
199 if (!pcm->mmap_channels)
200 return -ENOMEM;
201 pcm->running_areas = calloc(pcm->channels, sizeof(pcm->running_areas[0]));
202 if (!pcm->running_areas) {
203 free(pcm->mmap_channels);
204 pcm->mmap_channels = NULL;
205 return -ENOMEM;
206 }
207 for (c = 0; c < pcm->channels; ++c) {
208 snd_pcm_channel_info_t *i = &pcm->mmap_channels[c];
209 i->channel = c;
210 err = snd_pcm_channel_info(pcm, i);
211 if (err < 0) {
212 free(pcm->mmap_channels);
213 free(pcm->running_areas);
214 pcm->mmap_channels = NULL;
215 pcm->running_areas = NULL;
216 return err;
217 }
218 }
219
220 for (c = 0; c < pcm->channels; ++c) {
221 snd_pcm_channel_info_t *i = &pcm->mmap_channels[c];
222 snd_pcm_channel_area_t *a = &pcm->running_areas[c];
223 char *ptr;
224 size_t size;
225 unsigned int c1;
226 if (i->addr) {
227 a->addr = i->addr;
228 a->first = i->first;
229 a->step = i->step;
230 continue;
231 }
232
233 size = i->first + i->step * (pcm->buffer_size - 1) + pcm->sample_bits;
234 for (c1 = c + 1; c1 < pcm->channels; ++c1) {
235 snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
236 size_t s;
237 if (i1->type != i->type)
238 continue;
239 switch (i1->type) {
240 case SND_PCM_AREA_MMAP:
241 case SND_PCM_AREA_LOCAL:
242 break;
243 default:
244 assert(0);
245 }
246 s = i1->first + i1->step * (pcm->buffer_size - 1) + pcm->sample_bits;
247 if (s > size)
248 size = s;
249 }
250 size = (size + 7) / 8;
251 switch (i->type) {
252 case SND_PCM_AREA_MMAP:
253 /* i->addr has got from snd_pcm_channel_info() */
254 break;
255 case SND_PCM_AREA_LOCAL:
256 awalsa_debug("pcm_type:%s, size=%d, buffer_size=%d, period_size=%d\n",
257 snd_pcm_type_name(snd_pcm_type(pcm)),
258 size, pcm->buffer_size, pcm->period_size);
259 ptr = malloc(size);
260 if (ptr == NULL) {
261 awalsa_err("malloc failed\n");
262 return -ENOMEM;
263 }
264 i->addr = ptr;
265 break;
266 default:
267 assert(0);
268 }
269
270 for (c1 = c + 1; c1 < pcm->channels; ++c1) {
271 snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
272 if (i1->type != i->type)
273 continue;
274 switch (i1->type) {
275 case SND_PCM_AREA_MMAP:
276 break;
277 case SND_PCM_AREA_LOCAL:
278 if (pcm->access != SND_PCM_ACCESS_MMAP_INTERLEAVED &&
279 pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED)
280 continue;
281 break;
282 default:
283 assert(0);
284 }
285 i1->addr = i->addr;
286 }
287
288 a->addr = i->addr;
289 a->first = i->first;
290 a->step = i->step;
291 }
292 return 0;
293 }
294
snd_pcm_munmap(snd_pcm_t * pcm)295 int snd_pcm_munmap(snd_pcm_t *pcm)
296 {
297 int err;
298 unsigned int c;
299 assert(pcm);
300 assert(pcm->ops->mmap);
301
302 if (!pcm->mmap_channels) {
303 awalsa_err("Not mmapped\n");
304 return -ENXIO;
305 }
306 if (pcm->mmap_shadow)
307 return pcm->ops->munmap(pcm);
308
309 for (c = 0; c < pcm->channels; ++c) {
310 snd_pcm_channel_info_t *i = &pcm->mmap_channels[c];
311 unsigned int c1;
312 if (!i->addr)
313 continue;
314 for (c1 = c + 1; c1 < pcm->channels; ++c1) {
315 snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
316 if (i1->addr != i->addr)
317 continue;
318 i1->addr = NULL;
319 }
320 switch (i->type) {
321 case SND_PCM_AREA_MMAP:
322 /* do nothing, the spaces are managed by driver */
323 break;
324 case SND_PCM_AREA_LOCAL:
325 free(i->addr);
326 break;
327 default:
328 assert(0);
329 }
330 i->addr = NULL;
331 }
332
333 err = pcm->ops->munmap(pcm);
334 if (err < 0)
335 return err;
336 free(pcm->mmap_channels);
337 free(pcm->running_areas);
338 pcm->mmap_channels = NULL;
339 pcm->running_areas = NULL;
340 return 0;
341 }
342