1 /**
2 * \file pcm/pcm_shm.c
3 * \ingroup PCM_Plugins
4 * \brief PCM Shared Memory Plugin Interface
5 * \author Abramo Bagnara <abramo@alsa-project.org>
6 * \date 2000-2001
7 */
8 /*
9 * PCM - Shared Memory Client
10 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
11 *
12 *
13 * This library is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU Lesser General Public License as
15 * published by the Free Software Foundation; either version 2.1 of
16 * the License, or (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License for more details.
22 *
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 *
27 */
28
29 #include "pcm_local.h"
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stddef.h>
33 #include <limits.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <fcntl.h>
37 #include <sys/ioctl.h>
38 #include <sys/shm.h>
39 #include <sys/socket.h>
40 #include <poll.h>
41 #include <sys/un.h>
42 #include <sys/mman.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 #include <net/if.h>
46 #include <netdb.h>
47 #include "aserver.h"
48
49 #ifndef PIC
50 /* entry for static linking */
51 const char *_snd_module_pcm_shm = "";
52 #endif
53
54 #ifndef DOC_HIDDEN
55 typedef struct {
56 int socket;
57 volatile snd_pcm_shm_ctrl_t *ctrl;
58 } snd_pcm_shm_t;
59 #endif
60
snd_pcm_shm_action_fd0(snd_pcm_t *pcm, int *fd)61 static long snd_pcm_shm_action_fd0(snd_pcm_t *pcm, int *fd)
62 {
63 snd_pcm_shm_t *shm = pcm->private_data;
64 int err;
65 char buf[1] = "";
66 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
67
68 err = write(shm->socket, buf, 1);
69 if (err != 1)
70 return -EBADFD;
71 err = snd_receive_fd(shm->socket, buf, 1, fd);
72 if (err != 1)
73 return -EBADFD;
74 if (ctrl->cmd) {
75 SNDERR("Server has not done the cmd");
76 return -EBADFD;
77 }
78 return ctrl->result;
79 }
80
snd_pcm_shm_new_rbptr(snd_pcm_t *pcm, snd_pcm_shm_t *shm, snd_pcm_rbptr_t *rbptr, volatile snd_pcm_shm_rbptr_t *shm_rbptr)81 static int snd_pcm_shm_new_rbptr(snd_pcm_t *pcm, snd_pcm_shm_t *shm,
82 snd_pcm_rbptr_t *rbptr, volatile snd_pcm_shm_rbptr_t *shm_rbptr)
83 {
84 if (!shm_rbptr->use_mmap) {
85 if (&pcm->hw == rbptr)
86 snd_pcm_set_hw_ptr(pcm, &shm_rbptr->ptr, -1, 0);
87 else
88 snd_pcm_set_appl_ptr(pcm, &shm_rbptr->ptr, -1, 0);
89 } else {
90 void *ptr;
91 size_t mmap_size, mmap_offset, offset;
92 int fd;
93 long result;
94
95 shm->ctrl->cmd = &pcm->hw == rbptr ? SND_PCM_IOCTL_HW_PTR_FD : SND_PCM_IOCTL_APPL_PTR_FD;
96 result = snd_pcm_shm_action_fd0(pcm, &fd);
97 if (result < 0)
98 return result;
99 mmap_size = page_ptr(shm_rbptr->offset, sizeof(snd_pcm_uframes_t), &offset, &mmap_offset);
100 ptr = mmap(NULL, mmap_size, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, fd, mmap_offset);
101 if (ptr == MAP_FAILED || ptr == NULL) {
102 SYSERR("shm rbptr mmap failed");
103 return -errno;
104 }
105 if (&pcm->hw == rbptr)
106 snd_pcm_set_hw_ptr(pcm, (snd_pcm_uframes_t *)((char *)ptr + offset), fd, shm_rbptr->offset);
107 else
108 snd_pcm_set_appl_ptr(pcm, (snd_pcm_uframes_t *)((char *)ptr + offset), fd, shm_rbptr->offset);
109 }
110 return 0;
111 }
112
snd_pcm_shm_action(snd_pcm_t *pcm)113 static long snd_pcm_shm_action(snd_pcm_t *pcm)
114 {
115 snd_pcm_shm_t *shm = pcm->private_data;
116 int err, result;
117 char buf[1] = "";
118 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
119
120 if (ctrl->hw.changed || ctrl->appl.changed)
121 return -EBADFD;
122 err = write(shm->socket, buf, 1);
123 if (err != 1)
124 return -EBADFD;
125 err = read(shm->socket, buf, 1);
126 if (err != 1)
127 return -EBADFD;
128 if (ctrl->cmd) {
129 SNDERR("Server has not done the cmd");
130 return -EBADFD;
131 }
132 result = ctrl->result;
133 if (ctrl->hw.changed) {
134 err = snd_pcm_shm_new_rbptr(pcm, shm, &pcm->hw, &ctrl->hw);
135 if (err < 0)
136 return err;
137 ctrl->hw.changed = 0;
138 }
139 if (ctrl->appl.changed) {
140 err = snd_pcm_shm_new_rbptr(pcm, shm, &pcm->appl, &ctrl->appl);
141 if (err < 0)
142 return err;
143 ctrl->appl.changed = 0;
144 }
145 return result;
146 }
147
snd_pcm_shm_action_fd(snd_pcm_t *pcm, int *fd)148 static long snd_pcm_shm_action_fd(snd_pcm_t *pcm, int *fd)
149 {
150 snd_pcm_shm_t *shm = pcm->private_data;
151 int err;
152 char buf[1] = "";
153 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
154
155 if (ctrl->hw.changed || ctrl->appl.changed)
156 return -EBADFD;
157 err = write(shm->socket, buf, 1);
158 if (err != 1)
159 return -EBADFD;
160 err = snd_receive_fd(shm->socket, buf, 1, fd);
161 if (err != 1)
162 return -EBADFD;
163 if (ctrl->cmd) {
164 SNDERR("Server has not done the cmd");
165 return -EBADFD;
166 }
167 if (ctrl->hw.changed) {
168 err = snd_pcm_shm_new_rbptr(pcm, shm, &pcm->hw, &ctrl->hw);
169 if (err < 0)
170 return err;
171 ctrl->hw.changed = 0;
172 }
173 if (ctrl->appl.changed) {
174 err = snd_pcm_shm_new_rbptr(pcm, shm, &pcm->appl, &ctrl->appl);
175 if (err < 0)
176 return err;
177 ctrl->appl.changed = 0;
178 }
179 return ctrl->result;
180 }
181
snd_pcm_shm_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)182 static int snd_pcm_shm_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)
183 {
184 return 0;
185 }
186
snd_pcm_shm_async(snd_pcm_t *pcm, int sig, pid_t pid)187 static int snd_pcm_shm_async(snd_pcm_t *pcm, int sig, pid_t pid)
188 {
189 snd_pcm_shm_t *shm = pcm->private_data;
190 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
191 ctrl->cmd = SND_PCM_IOCTL_ASYNC;
192 ctrl->u.async.sig = sig;
193 ctrl->u.async.pid = pid;
194 return snd_pcm_shm_action(pcm);
195 }
196
snd_pcm_shm_info(snd_pcm_t *pcm, snd_pcm_info_t * info)197 static int snd_pcm_shm_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
198 {
199 snd_pcm_shm_t *shm = pcm->private_data;
200 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
201 int err;
202 // ctrl->u.info = *info;
203 ctrl->cmd = SNDRV_PCM_IOCTL_INFO;
204 err = snd_pcm_shm_action(pcm);
205 if (err < 0)
206 return err;
207 *info = ctrl->u.info;
208 return err;
209 }
210
snd_pcm_shm_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params ATTRIBUTE_UNUSED)211 static int snd_pcm_shm_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params ATTRIBUTE_UNUSED)
212 {
213 return 0;
214 }
215
snd_pcm_shm_hw_refine_sprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *sparams)216 static int snd_pcm_shm_hw_refine_sprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *sparams)
217 {
218 snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
219 _snd_pcm_hw_params_any(sparams);
220 _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
221 &saccess_mask);
222 return 0;
223 }
224
snd_pcm_shm_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params, snd_pcm_hw_params_t *sparams)225 static int snd_pcm_shm_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
226 snd_pcm_hw_params_t *sparams)
227 {
228 int err;
229 unsigned int links = ~SND_PCM_HW_PARBIT_ACCESS;
230 const snd_pcm_access_mask_t *access_mask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS);
231 if (!snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_RW_INTERLEAVED) &&
232 !snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED)) {
233 err = _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
234 access_mask);
235 if (err < 0)
236 return err;
237 }
238 err = _snd_pcm_hw_params_refine(sparams, links, params);
239 if (err < 0)
240 return err;
241 return 0;
242 }
243
snd_pcm_shm_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params, snd_pcm_hw_params_t *sparams)244 static int snd_pcm_shm_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
245 snd_pcm_hw_params_t *sparams)
246 {
247 int err;
248 unsigned int links = ~SND_PCM_HW_PARBIT_ACCESS;
249 snd_pcm_access_mask_t access_mask;
250 snd_mask_copy(&access_mask, snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_ACCESS));
251 snd_pcm_access_mask_set(&access_mask, SND_PCM_ACCESS_RW_INTERLEAVED);
252 snd_pcm_access_mask_set(&access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED);
253 err = _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
254 &access_mask);
255 if (err < 0)
256 return err;
257 err = _snd_pcm_hw_params_refine(params, links, sparams);
258 if (err < 0)
259 return err;
260 return 0;
261 }
262
snd_pcm_shm_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)263 static int snd_pcm_shm_hw_refine_slave(snd_pcm_t *pcm,
264 snd_pcm_hw_params_t *params)
265 {
266 snd_pcm_shm_t *shm = pcm->private_data;
267 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
268 int err;
269 ctrl->u.hw_refine = *params;
270 ctrl->cmd = SNDRV_PCM_IOCTL_HW_REFINE;
271 err = snd_pcm_shm_action(pcm);
272 *params = ctrl->u.hw_refine;
273 return err;
274 }
275
snd_pcm_shm_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)276 static int snd_pcm_shm_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
277 {
278 return snd_pcm_hw_refine_slave(pcm, params,
279 snd_pcm_shm_hw_refine_cprepare,
280 snd_pcm_shm_hw_refine_cchange,
281 snd_pcm_shm_hw_refine_sprepare,
282 snd_pcm_shm_hw_refine_schange,
283 snd_pcm_shm_hw_refine_slave);
284 }
285
snd_pcm_shm_hw_params_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)286 static int snd_pcm_shm_hw_params_slave(snd_pcm_t *pcm,
287 snd_pcm_hw_params_t *params)
288 {
289 snd_pcm_shm_t *shm = pcm->private_data;
290 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
291 int err;
292 params->flags |= SND_PCM_HW_PARAMS_EXPORT_BUFFER;
293 ctrl->cmd = SNDRV_PCM_IOCTL_HW_PARAMS;
294 ctrl->u.hw_params = *params;
295 err = snd_pcm_shm_action(pcm);
296 *params = ctrl->u.hw_params;
297 return err;
298 }
299
snd_pcm_shm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)300 static int snd_pcm_shm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
301 {
302 return snd_pcm_hw_params_slave(pcm, params,
303 snd_pcm_shm_hw_refine_cchange,
304 snd_pcm_shm_hw_refine_sprepare,
305 snd_pcm_shm_hw_refine_schange,
306 snd_pcm_shm_hw_params_slave);
307 }
308
snd_pcm_shm_hw_free(snd_pcm_t *pcm)309 static int snd_pcm_shm_hw_free(snd_pcm_t *pcm)
310 {
311 snd_pcm_shm_t *shm = pcm->private_data;
312 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
313 ctrl->cmd = SNDRV_PCM_IOCTL_HW_FREE;
314 return snd_pcm_shm_action(pcm);
315 }
316
snd_pcm_shm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)317 static int snd_pcm_shm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
318 {
319 snd_pcm_shm_t *shm = pcm->private_data;
320 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
321 int err;
322 ctrl->cmd = SNDRV_PCM_IOCTL_SW_PARAMS;
323 ctrl->u.sw_params = *params;
324 err = snd_pcm_shm_action(pcm);
325 *params = ctrl->u.sw_params;
326 if (err < 0)
327 return err;
328 return err;
329 }
330
snd_pcm_shm_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)331 static int snd_pcm_shm_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
332 {
333 return 0;
334 }
335
snd_pcm_shm_munmap(snd_pcm_t *pcm)336 static int snd_pcm_shm_munmap(snd_pcm_t *pcm)
337 {
338 unsigned int c;
339 for (c = 0; c < pcm->channels; ++c) {
340 snd_pcm_channel_info_t *i = &pcm->mmap_channels[c];
341 unsigned int c1;
342 int err;
343 if (i->type != SND_PCM_AREA_MMAP)
344 continue;
345 if (i->u.mmap.fd < 0)
346 continue;
347 for (c1 = c + 1; c1 < pcm->channels; ++c1) {
348 snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
349 if (i1->type != SND_PCM_AREA_MMAP)
350 continue;
351 if (i1->u.mmap.fd != i->u.mmap.fd)
352 continue;
353 i1->u.mmap.fd = -1;
354 }
355 err = close(i->u.mmap.fd);
356 if (err < 0) {
357 SYSERR("close failed");
358 return -errno;
359 }
360 }
361 return 0;
362 }
363
snd_pcm_shm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)364 static int snd_pcm_shm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
365 {
366 snd_pcm_shm_t *shm = pcm->private_data;
367 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
368 int err;
369 int fd;
370 ctrl->cmd = SNDRV_PCM_IOCTL_CHANNEL_INFO;
371 ctrl->u.channel_info = *info;
372 err = snd_pcm_shm_action_fd(pcm, &fd);
373 if (err < 0)
374 return err;
375 *info = ctrl->u.channel_info;
376 info->addr = 0;
377 switch (info->type) {
378 case SND_PCM_AREA_MMAP:
379 info->u.mmap.fd = fd;
380 break;
381 case SND_PCM_AREA_SHM:
382 break;
383 default:
384 assert(0);
385 break;
386 }
387 return err;
388 }
389
snd_pcm_shm_status(snd_pcm_t *pcm, snd_pcm_status_t * status)390 static int snd_pcm_shm_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
391 {
392 snd_pcm_shm_t *shm = pcm->private_data;
393 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
394 int err;
395 ctrl->cmd = SNDRV_PCM_IOCTL_STATUS;
396 // ctrl->u.status = *status;
397 err = snd_pcm_shm_action(pcm);
398 if (err < 0)
399 return err;
400 *status = ctrl->u.status;
401 return err;
402 }
403
snd_pcm_shm_state(snd_pcm_t *pcm)404 static snd_pcm_state_t snd_pcm_shm_state(snd_pcm_t *pcm)
405 {
406 snd_pcm_shm_t *shm = pcm->private_data;
407 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
408 ctrl->cmd = SND_PCM_IOCTL_STATE;
409 return snd_pcm_shm_action(pcm);
410 }
411
snd_pcm_shm_hwsync(snd_pcm_t *pcm)412 static int snd_pcm_shm_hwsync(snd_pcm_t *pcm)
413 {
414 snd_pcm_shm_t *shm = pcm->private_data;
415 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
416 ctrl->cmd = SND_PCM_IOCTL_HWSYNC;
417 return snd_pcm_shm_action(pcm);
418 }
419
snd_pcm_shm_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)420 static int snd_pcm_shm_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
421 {
422 snd_pcm_shm_t *shm = pcm->private_data;
423 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
424 int err;
425 ctrl->cmd = SNDRV_PCM_IOCTL_DELAY;
426 err = snd_pcm_shm_action(pcm);
427 if (err < 0)
428 return err;
429 *delayp = ctrl->u.delay.frames;
430 return err;
431 }
432
snd_pcm_shm_avail_update(snd_pcm_t *pcm)433 static snd_pcm_sframes_t snd_pcm_shm_avail_update(snd_pcm_t *pcm)
434 {
435 snd_pcm_shm_t *shm = pcm->private_data;
436 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
437 int err;
438 ctrl->cmd = SND_PCM_IOCTL_AVAIL_UPDATE;
439 err = snd_pcm_shm_action(pcm);
440 if (err < 0)
441 return err;
442 return err;
443 }
444
snd_pcm_shm_htimestamp(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_uframes_t *avail ATTRIBUTE_UNUSED, snd_htimestamp_t *tstamp ATTRIBUTE_UNUSED)445 static int snd_pcm_shm_htimestamp(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
446 snd_pcm_uframes_t *avail ATTRIBUTE_UNUSED,
447 snd_htimestamp_t *tstamp ATTRIBUTE_UNUSED)
448 {
449 return -EIO; /* not implemented yet */
450 }
451
snd_pcm_shm_prepare(snd_pcm_t *pcm)452 static int snd_pcm_shm_prepare(snd_pcm_t *pcm)
453 {
454 snd_pcm_shm_t *shm = pcm->private_data;
455 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
456 ctrl->cmd = SNDRV_PCM_IOCTL_PREPARE;
457 return snd_pcm_shm_action(pcm);
458 }
459
snd_pcm_shm_reset(snd_pcm_t *pcm)460 static int snd_pcm_shm_reset(snd_pcm_t *pcm)
461 {
462 snd_pcm_shm_t *shm = pcm->private_data;
463 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
464 ctrl->cmd = SNDRV_PCM_IOCTL_RESET;
465 return snd_pcm_shm_action(pcm);
466 }
467
snd_pcm_shm_start(snd_pcm_t *pcm)468 static int snd_pcm_shm_start(snd_pcm_t *pcm)
469 {
470 snd_pcm_shm_t *shm = pcm->private_data;
471 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
472 ctrl->cmd = SNDRV_PCM_IOCTL_START;
473 return snd_pcm_shm_action(pcm);
474 }
475
snd_pcm_shm_drop(snd_pcm_t *pcm)476 static int snd_pcm_shm_drop(snd_pcm_t *pcm)
477 {
478 snd_pcm_shm_t *shm = pcm->private_data;
479 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
480 ctrl->cmd = SNDRV_PCM_IOCTL_DROP;
481 return snd_pcm_shm_action(pcm);
482 }
483
snd_pcm_shm_drain(snd_pcm_t *pcm)484 static int snd_pcm_shm_drain(snd_pcm_t *pcm)
485 {
486 snd_pcm_shm_t *shm = pcm->private_data;
487 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
488 int err;
489 do {
490 ctrl->cmd = SNDRV_PCM_IOCTL_DRAIN;
491 err = snd_pcm_shm_action(pcm);
492 if (err != -EAGAIN)
493 break;
494 usleep(10000);
495 } while (1);
496 if (err < 0)
497 return err;
498 if (!(pcm->mode & SND_PCM_NONBLOCK))
499 snd_pcm_wait(pcm, SND_PCM_WAIT_DRAIN);
500 return err;
501 }
502
snd_pcm_shm_pause(snd_pcm_t *pcm, int enable)503 static int snd_pcm_shm_pause(snd_pcm_t *pcm, int enable)
504 {
505 snd_pcm_shm_t *shm = pcm->private_data;
506 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
507 ctrl->cmd = SNDRV_PCM_IOCTL_PAUSE;
508 ctrl->u.pause.enable = enable;
509 return snd_pcm_shm_action(pcm);
510 }
511
snd_pcm_shm_rewindable(snd_pcm_t *pcm ATTRIBUTE_UNUSED)512 static snd_pcm_sframes_t snd_pcm_shm_rewindable(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
513 {
514 return 0; /* FIX ME */
515 }
516
snd_pcm_shm_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)517 static snd_pcm_sframes_t snd_pcm_shm_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
518 {
519 snd_pcm_shm_t *shm = pcm->private_data;
520 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
521 ctrl->cmd = SNDRV_PCM_IOCTL_REWIND;
522 ctrl->u.rewind.frames = frames;
523 return snd_pcm_shm_action(pcm);
524 }
525
snd_pcm_shm_forwardable(snd_pcm_t *pcm ATTRIBUTE_UNUSED)526 static snd_pcm_sframes_t snd_pcm_shm_forwardable(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
527 {
528 return 0; /* FIX ME */
529 }
530
snd_pcm_shm_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)531 static snd_pcm_sframes_t snd_pcm_shm_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
532 {
533 snd_pcm_shm_t *shm = pcm->private_data;
534 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
535 ctrl->cmd = SND_PCM_IOCTL_FORWARD;
536 ctrl->u.forward.frames = frames;
537 return snd_pcm_shm_action(pcm);
538 }
539
snd_pcm_shm_resume(snd_pcm_t *pcm)540 static int snd_pcm_shm_resume(snd_pcm_t *pcm)
541 {
542 snd_pcm_shm_t *shm = pcm->private_data;
543 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
544 ctrl->cmd = SNDRV_PCM_IOCTL_RESUME;
545 return snd_pcm_shm_action(pcm);
546 }
547
snd_pcm_shm_mmap_commit(snd_pcm_t *pcm, snd_pcm_uframes_t offset ATTRIBUTE_UNUSED, snd_pcm_uframes_t size)548 static snd_pcm_sframes_t snd_pcm_shm_mmap_commit(snd_pcm_t *pcm,
549 snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
550 snd_pcm_uframes_t size)
551 {
552 snd_pcm_shm_t *shm = pcm->private_data;
553 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
554 ctrl->cmd = SND_PCM_IOCTL_MMAP_COMMIT;
555 ctrl->u.mmap_commit.offset = offset;
556 ctrl->u.mmap_commit.frames = size;
557 return snd_pcm_shm_action(pcm);
558 }
559
snd_pcm_shm_poll_descriptor(snd_pcm_t *pcm)560 static int snd_pcm_shm_poll_descriptor(snd_pcm_t *pcm)
561 {
562 snd_pcm_shm_t *shm = pcm->private_data;
563 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
564 int fd, err;
565 ctrl->cmd = SND_PCM_IOCTL_POLL_DESCRIPTOR;
566 err = snd_pcm_shm_action_fd(pcm, &fd);
567 if (err < 0)
568 return err;
569 return fd;
570 }
571
snd_pcm_shm_close(snd_pcm_t *pcm)572 static int snd_pcm_shm_close(snd_pcm_t *pcm)
573 {
574 snd_pcm_shm_t *shm = pcm->private_data;
575 volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
576 int result;
577 ctrl->cmd = SND_PCM_IOCTL_CLOSE;
578 result = snd_pcm_shm_action(pcm);
579 shmdt((void *)ctrl);
580 close(shm->socket);
581 close(pcm->poll_fd);
582 free(shm);
583 return result;
584 }
585
snd_pcm_shm_dump(snd_pcm_t *pcm, snd_output_t *out)586 static void snd_pcm_shm_dump(snd_pcm_t *pcm, snd_output_t *out)
587 {
588 snd_output_printf(out, "Shm PCM\n");
589 if (pcm->setup) {
590 snd_output_printf(out, "Its setup is:\n");
591 snd_pcm_dump_setup(pcm, out);
592 }
593 }
594
595 static const snd_pcm_ops_t snd_pcm_shm_ops = {
596 .close = snd_pcm_shm_close,
597 .info = snd_pcm_shm_info,
598 .hw_refine = snd_pcm_shm_hw_refine,
599 .hw_params = snd_pcm_shm_hw_params,
600 .hw_free = snd_pcm_shm_hw_free,
601 .sw_params = snd_pcm_shm_sw_params,
602 .channel_info = snd_pcm_shm_channel_info,
603 .dump = snd_pcm_shm_dump,
604 .nonblock = snd_pcm_shm_nonblock,
605 .async = snd_pcm_shm_async,
606 .mmap = snd_pcm_shm_mmap,
607 .munmap = snd_pcm_shm_munmap,
608 };
609
610 static const snd_pcm_fast_ops_t snd_pcm_shm_fast_ops = {
611 .status = snd_pcm_shm_status,
612 .state = snd_pcm_shm_state,
613 .hwsync = snd_pcm_shm_hwsync,
614 .delay = snd_pcm_shm_delay,
615 .prepare = snd_pcm_shm_prepare,
616 .reset = snd_pcm_shm_reset,
617 .start = snd_pcm_shm_start,
618 .drop = snd_pcm_shm_drop,
619 .drain = snd_pcm_shm_drain,
620 .pause = snd_pcm_shm_pause,
621 .rewindable = snd_pcm_shm_rewindable,
622 .rewind = snd_pcm_shm_rewind,
623 .forwardable = snd_pcm_shm_forwardable,
624 .forward = snd_pcm_shm_forward,
625 .resume = snd_pcm_shm_resume,
626 .writei = snd_pcm_mmap_writei,
627 .writen = snd_pcm_mmap_writen,
628 .readi = snd_pcm_mmap_readi,
629 .readn = snd_pcm_mmap_readn,
630 .avail_update = snd_pcm_shm_avail_update,
631 .mmap_commit = snd_pcm_shm_mmap_commit,
632 .htimestamp = snd_pcm_shm_htimestamp,
633 };
634
make_local_socket(const char *filename)635 static int make_local_socket(const char *filename)
636 {
637 size_t l = strlen(filename);
638 size_t size = offsetof(struct sockaddr_un, sun_path) + l;
639 struct sockaddr_un *addr = alloca(size);
640 int sock;
641
642 sock = socket(PF_LOCAL, SOCK_STREAM, 0);
643 if (sock < 0) {
644 SYSERR("socket failed");
645 return -errno;
646 }
647
648 addr->sun_family = AF_LOCAL;
649 memcpy(addr->sun_path, filename, l);
650
651 if (connect(sock, (struct sockaddr *) addr, size) < 0) {
652 SYSERR("connect failed");
653 return -errno;
654 }
655 return sock;
656 }
657
658 /**
659 * \brief Creates a new shared memory PCM
660 * \param pcmp Returns created PCM handle
661 * \param name Name of PCM
662 * \param sockname Unix socket name
663 * \param sname Server name
664 * \param stream PCM Stream
665 * \param mode PCM Mode
666 * \retval zero on success otherwise a negative error code
667 * \warning Using of this function might be dangerous in the sense
668 * of compatibility reasons. The prototype might be freely
669 * changed in future.
670 */
snd_pcm_shm_open(snd_pcm_t **pcmp, const char *name, const char *sockname, const char *sname, snd_pcm_stream_t stream, int mode)671 int snd_pcm_shm_open(snd_pcm_t **pcmp, const char *name,
672 const char *sockname, const char *sname,
673 snd_pcm_stream_t stream, int mode)
674 {
675 snd_pcm_t *pcm;
676 snd_pcm_shm_t *shm = NULL;
677 snd_client_open_request_t *req;
678 snd_client_open_answer_t ans;
679 size_t snamelen, reqlen;
680 int err;
681 int result;
682 snd_pcm_shm_ctrl_t *ctrl = NULL;
683 int sock = -1;
684 snamelen = strlen(sname);
685 if (snamelen > 255)
686 return -EINVAL;
687
688 result = make_local_socket(sockname);
689 if (result < 0) {
690 SNDERR("server for socket %s is not running", sockname);
691 goto _err;
692 }
693 sock = result;
694
695 reqlen = sizeof(*req) + snamelen;
696 req = alloca(reqlen);
697 memcpy(req->name, sname, snamelen);
698 req->dev_type = SND_DEV_TYPE_PCM;
699 req->transport_type = SND_TRANSPORT_TYPE_SHM;
700 req->stream = stream;
701 req->mode = mode;
702 req->namelen = snamelen;
703 err = write(sock, req, reqlen);
704 if (err < 0) {
705 SYSERR("write error");
706 result = -errno;
707 goto _err;
708 }
709 if ((size_t) err != reqlen) {
710 SNDERR("write size error");
711 result = -EINVAL;
712 goto _err;
713 }
714 err = read(sock, &ans, sizeof(ans));
715 if (err < 0) {
716 SYSERR("read error");
717 result = -errno;
718 goto _err;
719 }
720 if (err != sizeof(ans)) {
721 SNDERR("read size error");
722 result = -EINVAL;
723 goto _err;
724 }
725 result = ans.result;
726 if (result < 0)
727 goto _err;
728
729 ctrl = shmat(ans.cookie, 0, 0);
730 if (!ctrl) {
731 SYSERR("shmat error");
732 result = -errno;
733 goto _err;
734 }
735
736 shm = calloc(1, sizeof(snd_pcm_shm_t));
737 if (!shm) {
738 result = -ENOMEM;
739 goto _err;
740 }
741
742 shm->socket = sock;
743 shm->ctrl = ctrl;
744
745 err = snd_pcm_new(&pcm, SND_PCM_TYPE_SHM, name, stream, mode);
746 if (err < 0) {
747 result = err;
748 goto _err;
749 }
750 pcm->mmap_rw = 1;
751 pcm->ops = &snd_pcm_shm_ops;
752 pcm->fast_ops = &snd_pcm_shm_fast_ops;
753 pcm->private_data = shm;
754 err = snd_pcm_shm_poll_descriptor(pcm);
755 if (err < 0) {
756 snd_pcm_close(pcm);
757 return err;
758 }
759 pcm->poll_fd = err;
760 pcm->poll_events = stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN;
761 snd_pcm_set_hw_ptr(pcm, &ctrl->hw.ptr, -1, 0);
762 snd_pcm_set_appl_ptr(pcm, &ctrl->appl.ptr, -1, 0);
763 *pcmp = pcm;
764 return 0;
765
766 _err:
767 close(sock);
768 if (ctrl)
769 shmdt(ctrl);
770 free(shm);
771 return result;
772 }
773
774 /*! \page pcm_plugins
775
776 \section pcm_plugins_shm Plugin: shm
777
778 This plugin communicates with aserver via shared memory. It is a raw
779 communication without any conversions, but it can be expected worse
780 performance.
781
782 \code
783 pcm.name {
784 type shm # Shared memory PCM
785 server STR # Server name
786 pcm STR # PCM name
787 }
788 \endcode
789
790 \subsection pcm_plugins_shm_funcref Function reference
791
792 <UL>
793 <LI>snd_pcm_shm_open()
794 <LI>_snd_pcm_shm_open()
795 </UL>
796
797 */
798
799 /**
800 * \brief Creates a new shm PCM
801 * \param pcmp Returns created PCM handle
802 * \param name Name of PCM
803 * \param root Root configuration node
804 * \param conf Configuration node with hw PCM description
805 * \param stream PCM Stream
806 * \param mode PCM Mode
807 * \warning Using of this function might be dangerous in the sense
808 * of compatibility reasons. The prototype might be freely
809 * changed in future.
810 */
_snd_pcm_shm_open(snd_pcm_t **pcmp, const char *name, snd_config_t *root, snd_config_t *conf, snd_pcm_stream_t stream, int mode)811 int _snd_pcm_shm_open(snd_pcm_t **pcmp, const char *name,
812 snd_config_t *root, snd_config_t *conf,
813 snd_pcm_stream_t stream, int mode)
814 {
815 snd_config_iterator_t i, next;
816 const char *server = NULL;
817 const char *pcm_name = NULL;
818 snd_config_t *sconfig;
819 const char *sockname = NULL;
820 long port = -1;
821 int err;
822
823 snd_config_for_each(i, next, conf) {
824 snd_config_t *n = snd_config_iterator_entry(i);
825 const char *id;
826 if (snd_config_get_id(n, &id) < 0)
827 continue;
828 if (snd_pcm_conf_generic_id(id))
829 continue;
830 if (strcmp(id, "server") == 0) {
831 err = snd_config_get_string(n, &server);
832 if (err < 0) {
833 SNDERR("Invalid type for %s", id);
834 return -EINVAL;
835 }
836 continue;
837 }
838 if (strcmp(id, "pcm") == 0) {
839 err = snd_config_get_string(n, &pcm_name);
840 if (err < 0) {
841 SNDERR("Invalid type for %s", id);
842 return -EINVAL;
843 }
844 continue;
845 }
846 SNDERR("Unknown field %s", id);
847 return -EINVAL;
848 }
849 if (!pcm_name) {
850 SNDERR("pcm is not defined");
851 return -EINVAL;
852 }
853 if (!server) {
854 SNDERR("server is not defined");
855 return -EINVAL;
856 }
857 err = snd_config_search_definition(root, "server", server, &sconfig);
858 if (err < 0) {
859 SNDERR("Unknown server %s", server);
860 return -EINVAL;
861 }
862 if (snd_config_get_type(sconfig) != SND_CONFIG_TYPE_COMPOUND) {
863 SNDERR("Invalid type for server %s definition", server);
864 goto _err;
865 }
866 snd_config_for_each(i, next, sconfig) {
867 snd_config_t *n = snd_config_iterator_entry(i);
868 const char *id;
869 if (snd_config_get_id(n, &id) < 0)
870 continue;
871 if (strcmp(id, "comment") == 0)
872 continue;
873 if (strcmp(id, "host") == 0)
874 continue;
875 if (strcmp(id, "socket") == 0) {
876 err = snd_config_get_string(n, &sockname);
877 if (err < 0) {
878 SNDERR("Invalid type for %s", id);
879 goto _err;
880 }
881 continue;
882 }
883 if (strcmp(id, "port") == 0) {
884 err = snd_config_get_integer(n, &port);
885 if (err < 0) {
886 SNDERR("Invalid type for %s", id);
887 goto _err;
888 }
889 continue;
890 }
891 SNDERR("Unknown field %s", id);
892 _err:
893 err = -EINVAL;
894 goto __error;
895 }
896
897 if (!sockname) {
898 SNDERR("socket is not defined");
899 goto _err;
900 }
901 err = snd_pcm_shm_open(pcmp, name, sockname, pcm_name, stream, mode);
902 __error:
903 snd_config_delete(sconfig);
904 return err;
905 }
906 #ifndef DOC_HIDDEN
907 SND_DLSYM_BUILD_VERSION(_snd_pcm_shm_open, SND_PCM_DLSYM_VERSION);
908 #endif
909