• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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