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