OSDN Git Service

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