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