/** * \file pcm/pcm_shm.c * \ingroup PCM_Plugins * \brief PCM Shared Memory Plugin Interface * \author Abramo Bagnara * \date 2000-2001 */ /* * PCM - Shared Memory Client * Copyright (c) 2000 by Abramo Bagnara * * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "aserver.h" #ifndef PIC /* entry for static linking */ const char *_snd_module_pcm_shm = ""; #endif #ifndef DOC_HIDDEN typedef struct { int socket; volatile snd_pcm_shm_ctrl_t *ctrl; } snd_pcm_shm_t; #endif static long snd_pcm_shm_action_fd0(snd_pcm_t *pcm, int *fd) { snd_pcm_shm_t *shm = pcm->private_data; int err; char buf[1]; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; err = write(shm->socket, buf, 1); if (err != 1) return -EBADFD; err = snd_receive_fd(shm->socket, buf, 1, fd); if (err != 1) return -EBADFD; if (ctrl->cmd) { SNDERR("Server has not done the cmd"); return -EBADFD; } return ctrl->result; } static int 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) { if (!shm_rbptr->use_mmap) { if (&pcm->hw == rbptr) snd_pcm_set_hw_ptr(pcm, &shm_rbptr->ptr, -1, 0); else snd_pcm_set_appl_ptr(pcm, &shm_rbptr->ptr, -1, 0); } else { void *ptr; size_t mmap_size, mmap_offset, offset; int fd; long result; shm->ctrl->cmd = &pcm->hw == rbptr ? SND_PCM_IOCTL_HW_PTR_FD : SND_PCM_IOCTL_APPL_PTR_FD; result = snd_pcm_shm_action_fd0(pcm, &fd); if (result < 0) return result; mmap_size = page_ptr(shm_rbptr->offset, sizeof(snd_pcm_uframes_t), &offset, &mmap_offset); ptr = mmap(NULL, mmap_size, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, fd, mmap_offset); if (ptr == MAP_FAILED || ptr == NULL) { SYSERR("shm rbptr mmap failed"); return -errno; } if (&pcm->hw == rbptr) snd_pcm_set_hw_ptr(pcm, (snd_pcm_uframes_t *)((char *)ptr + offset), fd, shm_rbptr->offset); else snd_pcm_set_appl_ptr(pcm, (snd_pcm_uframes_t *)((char *)ptr + offset), fd, shm_rbptr->offset); } return 0; } static long snd_pcm_shm_action(snd_pcm_t *pcm) { snd_pcm_shm_t *shm = pcm->private_data; int err, result; char buf[1]; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; if (ctrl->hw.changed || ctrl->appl.changed) return -EBADFD; err = write(shm->socket, buf, 1); if (err != 1) return -EBADFD; err = read(shm->socket, buf, 1); if (err != 1) return -EBADFD; if (ctrl->cmd) { SNDERR("Server has not done the cmd"); return -EBADFD; } result = ctrl->result; if (ctrl->hw.changed) { err = snd_pcm_shm_new_rbptr(pcm, shm, &pcm->hw, &ctrl->hw); if (err < 0) return err; ctrl->hw.changed = 0; } if (ctrl->appl.changed) { err = snd_pcm_shm_new_rbptr(pcm, shm, &pcm->appl, &ctrl->appl); if (err < 0) return err; ctrl->appl.changed = 0; } return result; } static long snd_pcm_shm_action_fd(snd_pcm_t *pcm, int *fd) { snd_pcm_shm_t *shm = pcm->private_data; int err; char buf[1]; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; if (ctrl->hw.changed || ctrl->appl.changed) return -EBADFD; err = write(shm->socket, buf, 1); if (err != 1) return -EBADFD; err = snd_receive_fd(shm->socket, buf, 1, fd); if (err != 1) return -EBADFD; if (ctrl->cmd) { SNDERR("Server has not done the cmd"); return -EBADFD; } if (ctrl->hw.changed) { err = snd_pcm_shm_new_rbptr(pcm, shm, &pcm->hw, &ctrl->hw); if (err < 0) return err; ctrl->hw.changed = 0; } if (ctrl->appl.changed) { err = snd_pcm_shm_new_rbptr(pcm, shm, &pcm->appl, &ctrl->appl); if (err < 0) return err; ctrl->appl.changed = 0; } return ctrl->result; } static int snd_pcm_shm_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED) { return 0; } static int snd_pcm_shm_async(snd_pcm_t *pcm, int sig, pid_t pid) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; ctrl->cmd = SND_PCM_IOCTL_ASYNC; ctrl->u.async.sig = sig; ctrl->u.async.pid = pid; return snd_pcm_shm_action(pcm); } static int snd_pcm_shm_info(snd_pcm_t *pcm, snd_pcm_info_t * info) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; int err; // ctrl->u.info = *info; ctrl->cmd = SNDRV_PCM_IOCTL_INFO; err = snd_pcm_shm_action(pcm); if (err < 0) return err; *info = ctrl->u.info; return err; } static int snd_pcm_shm_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params ATTRIBUTE_UNUSED) { return 0; } static int snd_pcm_shm_hw_refine_sprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *sparams) { snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP }; _snd_pcm_hw_params_any(sparams); _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS, &saccess_mask); return 0; } static int snd_pcm_shm_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params, snd_pcm_hw_params_t *sparams) { int err; unsigned int links = ~SND_PCM_HW_PARBIT_ACCESS; const snd_pcm_access_mask_t *access_mask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS); if (!snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_RW_INTERLEAVED) && !snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED)) { err = _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS, access_mask); if (err < 0) return err; } err = _snd_pcm_hw_params_refine(sparams, links, params); if (err < 0) return err; return 0; } static int snd_pcm_shm_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params, snd_pcm_hw_params_t *sparams) { int err; unsigned int links = ~SND_PCM_HW_PARBIT_ACCESS; snd_pcm_access_mask_t access_mask; snd_mask_copy(&access_mask, snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_ACCESS)); snd_pcm_access_mask_set(&access_mask, SND_PCM_ACCESS_RW_INTERLEAVED); snd_pcm_access_mask_set(&access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED); err = _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS, &access_mask); if (err < 0) return err; err = _snd_pcm_hw_params_refine(params, links, sparams); if (err < 0) return err; return 0; } static int snd_pcm_shm_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; int err; ctrl->u.hw_refine = *params; ctrl->cmd = SNDRV_PCM_IOCTL_HW_REFINE; err = snd_pcm_shm_action(pcm); *params = ctrl->u.hw_refine; return err; } static int snd_pcm_shm_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { return snd_pcm_hw_refine_slave(pcm, params, snd_pcm_shm_hw_refine_cprepare, snd_pcm_shm_hw_refine_cchange, snd_pcm_shm_hw_refine_sprepare, snd_pcm_shm_hw_refine_schange, snd_pcm_shm_hw_refine_slave); } static int snd_pcm_shm_hw_params_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; int err; params->flags |= SND_PCM_HW_PARAMS_EXPORT_BUFFER; ctrl->cmd = SNDRV_PCM_IOCTL_HW_PARAMS; ctrl->u.hw_params = *params; err = snd_pcm_shm_action(pcm); *params = ctrl->u.hw_params; return err; } static int snd_pcm_shm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) { return snd_pcm_hw_params_slave(pcm, params, snd_pcm_shm_hw_refine_cchange, snd_pcm_shm_hw_refine_sprepare, snd_pcm_shm_hw_refine_schange, snd_pcm_shm_hw_params_slave); } static int snd_pcm_shm_hw_free(snd_pcm_t *pcm) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; ctrl->cmd = SNDRV_PCM_IOCTL_HW_FREE; return snd_pcm_shm_action(pcm); } static int snd_pcm_shm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; int err; ctrl->cmd = SNDRV_PCM_IOCTL_SW_PARAMS; ctrl->u.sw_params = *params; err = snd_pcm_shm_action(pcm); *params = ctrl->u.sw_params; if (err < 0) return err; return err; } static int snd_pcm_shm_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED) { return 0; } static int snd_pcm_shm_munmap(snd_pcm_t *pcm) { unsigned int c; for (c = 0; c < pcm->channels; ++c) { snd_pcm_channel_info_t *i = &pcm->mmap_channels[c]; unsigned int c1; int err; if (i->type != SND_PCM_AREA_MMAP) continue; if (i->u.mmap.fd < 0) continue; for (c1 = c + 1; c1 < pcm->channels; ++c1) { snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1]; if (i1->type != SND_PCM_AREA_MMAP) continue; if (i1->u.mmap.fd != i->u.mmap.fd) continue; i1->u.mmap.fd = -1; } err = close(i->u.mmap.fd); if (err < 0) { SYSERR("close failed"); return -errno; } } return 0; } static int snd_pcm_shm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; int err; int fd; ctrl->cmd = SNDRV_PCM_IOCTL_CHANNEL_INFO; ctrl->u.channel_info = *info; err = snd_pcm_shm_action_fd(pcm, &fd); if (err < 0) return err; *info = ctrl->u.channel_info; info->addr = 0; switch (info->type) { case SND_PCM_AREA_MMAP: info->u.mmap.fd = fd; break; case SND_PCM_AREA_SHM: break; default: assert(0); break; } return err; } static int snd_pcm_shm_status(snd_pcm_t *pcm, snd_pcm_status_t * status) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; int err; ctrl->cmd = SNDRV_PCM_IOCTL_STATUS; // ctrl->u.status = *status; err = snd_pcm_shm_action(pcm); if (err < 0) return err; *status = ctrl->u.status; return err; } static snd_pcm_state_t snd_pcm_shm_state(snd_pcm_t *pcm) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; ctrl->cmd = SND_PCM_IOCTL_STATE; return snd_pcm_shm_action(pcm); } static int snd_pcm_shm_hwsync(snd_pcm_t *pcm) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; ctrl->cmd = SND_PCM_IOCTL_HWSYNC; return snd_pcm_shm_action(pcm); } static int snd_pcm_shm_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; int err; ctrl->cmd = SNDRV_PCM_IOCTL_DELAY; err = snd_pcm_shm_action(pcm); if (err < 0) return err; *delayp = ctrl->u.delay.frames; return err; } static snd_pcm_sframes_t snd_pcm_shm_avail_update(snd_pcm_t *pcm) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; int err; ctrl->cmd = SND_PCM_IOCTL_AVAIL_UPDATE; err = snd_pcm_shm_action(pcm); if (err < 0) return err; return err; } static int snd_pcm_shm_htimestamp(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_uframes_t *avail ATTRIBUTE_UNUSED, snd_htimestamp_t *tstamp ATTRIBUTE_UNUSED) { return -EIO; /* not implemented yet */ } static int snd_pcm_shm_prepare(snd_pcm_t *pcm) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; ctrl->cmd = SNDRV_PCM_IOCTL_PREPARE; return snd_pcm_shm_action(pcm); } static int snd_pcm_shm_reset(snd_pcm_t *pcm) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; ctrl->cmd = SNDRV_PCM_IOCTL_RESET; return snd_pcm_shm_action(pcm); } static int snd_pcm_shm_start(snd_pcm_t *pcm) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; ctrl->cmd = SNDRV_PCM_IOCTL_START; return snd_pcm_shm_action(pcm); } static int snd_pcm_shm_drop(snd_pcm_t *pcm) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; ctrl->cmd = SNDRV_PCM_IOCTL_DROP; return snd_pcm_shm_action(pcm); } static int snd_pcm_shm_drain(snd_pcm_t *pcm) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; int err; do { ctrl->cmd = SNDRV_PCM_IOCTL_DRAIN; err = snd_pcm_shm_action(pcm); if (err != -EAGAIN) break; usleep(10000); } while (1); if (err < 0) return err; if (!(pcm->mode & SND_PCM_NONBLOCK)) snd_pcm_wait(pcm, -1); return err; } static int snd_pcm_shm_pause(snd_pcm_t *pcm, int enable) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; ctrl->cmd = SNDRV_PCM_IOCTL_PAUSE; ctrl->u.pause.enable = enable; return snd_pcm_shm_action(pcm); } static snd_pcm_sframes_t snd_pcm_shm_rewindable(snd_pcm_t *pcm ATTRIBUTE_UNUSED) { return 0; /* FIX ME */ } static snd_pcm_sframes_t snd_pcm_shm_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; ctrl->cmd = SNDRV_PCM_IOCTL_REWIND; ctrl->u.rewind.frames = frames; return snd_pcm_shm_action(pcm); } static snd_pcm_sframes_t snd_pcm_shm_forwardable(snd_pcm_t *pcm ATTRIBUTE_UNUSED) { return 0; /* FIX ME */ } static snd_pcm_sframes_t snd_pcm_shm_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; ctrl->cmd = SND_PCM_IOCTL_FORWARD; ctrl->u.forward.frames = frames; return snd_pcm_shm_action(pcm); } static int snd_pcm_shm_resume(snd_pcm_t *pcm) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; ctrl->cmd = SNDRV_PCM_IOCTL_RESUME; return snd_pcm_shm_action(pcm); } static snd_pcm_sframes_t snd_pcm_shm_mmap_commit(snd_pcm_t *pcm, snd_pcm_uframes_t offset ATTRIBUTE_UNUSED, snd_pcm_uframes_t size) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; ctrl->cmd = SND_PCM_IOCTL_MMAP_COMMIT; ctrl->u.mmap_commit.offset = offset; ctrl->u.mmap_commit.frames = size; return snd_pcm_shm_action(pcm); } static int snd_pcm_shm_poll_descriptor(snd_pcm_t *pcm) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; int fd, err; ctrl->cmd = SND_PCM_IOCTL_POLL_DESCRIPTOR; err = snd_pcm_shm_action_fd(pcm, &fd); if (err < 0) return err; return fd; } static int snd_pcm_shm_close(snd_pcm_t *pcm) { snd_pcm_shm_t *shm = pcm->private_data; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; int result; ctrl->cmd = SND_PCM_IOCTL_CLOSE; result = snd_pcm_shm_action(pcm); shmdt((void *)ctrl); close(shm->socket); close(pcm->poll_fd); free(shm); return result; } static void snd_pcm_shm_dump(snd_pcm_t *pcm, snd_output_t *out) { snd_output_printf(out, "Shm PCM\n"); if (pcm->setup) { snd_output_printf(out, "Its setup is:\n"); snd_pcm_dump_setup(pcm, out); } } static const snd_pcm_ops_t snd_pcm_shm_ops = { .close = snd_pcm_shm_close, .info = snd_pcm_shm_info, .hw_refine = snd_pcm_shm_hw_refine, .hw_params = snd_pcm_shm_hw_params, .hw_free = snd_pcm_shm_hw_free, .sw_params = snd_pcm_shm_sw_params, .channel_info = snd_pcm_shm_channel_info, .dump = snd_pcm_shm_dump, .nonblock = snd_pcm_shm_nonblock, .async = snd_pcm_shm_async, .mmap = snd_pcm_shm_mmap, .munmap = snd_pcm_shm_munmap, }; static const snd_pcm_fast_ops_t snd_pcm_shm_fast_ops = { .status = snd_pcm_shm_status, .state = snd_pcm_shm_state, .hwsync = snd_pcm_shm_hwsync, .delay = snd_pcm_shm_delay, .prepare = snd_pcm_shm_prepare, .reset = snd_pcm_shm_reset, .start = snd_pcm_shm_start, .drop = snd_pcm_shm_drop, .drain = snd_pcm_shm_drain, .pause = snd_pcm_shm_pause, .rewindable = snd_pcm_shm_rewindable, .rewind = snd_pcm_shm_rewind, .forwardable = snd_pcm_shm_forwardable, .forward = snd_pcm_shm_forward, .resume = snd_pcm_shm_resume, .writei = snd_pcm_mmap_writei, .writen = snd_pcm_mmap_writen, .readi = snd_pcm_mmap_readi, .readn = snd_pcm_mmap_readn, .avail_update = snd_pcm_shm_avail_update, .mmap_commit = snd_pcm_shm_mmap_commit, .htimestamp = snd_pcm_shm_htimestamp, }; static int make_local_socket(const char *filename) { size_t l = strlen(filename); size_t size = offsetof(struct sockaddr_un, sun_path) + l; struct sockaddr_un *addr = alloca(size); int sock; sock = socket(PF_LOCAL, SOCK_STREAM, 0); if (sock < 0) { SYSERR("socket failed"); return -errno; } addr->sun_family = AF_LOCAL; memcpy(addr->sun_path, filename, l); if (connect(sock, (struct sockaddr *) addr, size) < 0) { SYSERR("connect failed"); return -errno; } return sock; } /** * \brief Creates a new shared memory PCM * \param pcmp Returns created PCM handle * \param name Name of PCM * \param sockname Unix socket name * \param sname Server name * \param stream PCM Stream * \param mode PCM Mode * \retval zero on success otherwise a negative error code * \warning Using of this function might be dangerous in the sense * of compatibility reasons. The prototype might be freely * changed in future. */ int snd_pcm_shm_open(snd_pcm_t **pcmp, const char *name, const char *sockname, const char *sname, snd_pcm_stream_t stream, int mode) { snd_pcm_t *pcm; snd_pcm_shm_t *shm = NULL; snd_client_open_request_t *req; snd_client_open_answer_t ans; size_t snamelen, reqlen; int err; int result; snd_pcm_shm_ctrl_t *ctrl = NULL; int sock = -1; snamelen = strlen(sname); if (snamelen > 255) return -EINVAL; result = make_local_socket(sockname); if (result < 0) { SNDERR("server for socket %s is not running", sockname); goto _err; } sock = result; reqlen = sizeof(*req) + snamelen; req = alloca(reqlen); memcpy(req->name, sname, snamelen); req->dev_type = SND_DEV_TYPE_PCM; req->transport_type = SND_TRANSPORT_TYPE_SHM; req->stream = stream; req->mode = mode; req->namelen = snamelen; err = write(sock, req, reqlen); if (err < 0) { SYSERR("write error"); result = -errno; goto _err; } if ((size_t) err != reqlen) { SNDERR("write size error"); result = -EINVAL; goto _err; } err = read(sock, &ans, sizeof(ans)); if (err < 0) { SYSERR("read error"); result = -errno; goto _err; } if (err != sizeof(ans)) { SNDERR("read size error"); result = -EINVAL; goto _err; } result = ans.result; if (result < 0) goto _err; ctrl = shmat(ans.cookie, 0, 0); if (!ctrl) { SYSERR("shmat error"); result = -errno; goto _err; } shm = calloc(1, sizeof(snd_pcm_shm_t)); if (!shm) { result = -ENOMEM; goto _err; } shm->socket = sock; shm->ctrl = ctrl; err = snd_pcm_new(&pcm, SND_PCM_TYPE_SHM, name, stream, mode); if (err < 0) { result = err; goto _err; } pcm->mmap_rw = 1; pcm->ops = &snd_pcm_shm_ops; pcm->fast_ops = &snd_pcm_shm_fast_ops; pcm->private_data = shm; err = snd_pcm_shm_poll_descriptor(pcm); if (err < 0) { snd_pcm_close(pcm); return err; } pcm->poll_fd = err; pcm->poll_events = stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN; snd_pcm_set_hw_ptr(pcm, &ctrl->hw.ptr, -1, 0); snd_pcm_set_appl_ptr(pcm, &ctrl->appl.ptr, -1, 0); *pcmp = pcm; return 0; _err: close(sock); if (ctrl) shmdt(ctrl); free(shm); return result; } /*! \page pcm_plugins \section pcm_plugins_shm Plugin: shm This plugin communicates with aserver via shared memory. It is a raw communication without any conversions, but it can be expected worse performance. \code pcm.name { type shm # Shared memory PCM server STR # Server name pcm STR # PCM name } \endcode \subsection pcm_plugins_shm_funcref Function reference
  • snd_pcm_shm_open()
  • _snd_pcm_shm_open()
*/ /** * \brief Creates a new shm PCM * \param pcmp Returns created PCM handle * \param name Name of PCM * \param root Root configuration node * \param conf Configuration node with hw PCM description * \param stream PCM Stream * \param mode PCM Mode * \warning Using of this function might be dangerous in the sense * of compatibility reasons. The prototype might be freely * changed in future. */ int _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) { snd_config_iterator_t i, next; const char *server = NULL; const char *pcm_name = NULL; snd_config_t *sconfig; const char *sockname = NULL; long port = -1; int err; snd_config_for_each(i, next, conf) { snd_config_t *n = snd_config_iterator_entry(i); const char *id; if (snd_config_get_id(n, &id) < 0) continue; if (snd_pcm_conf_generic_id(id)) continue; if (strcmp(id, "server") == 0) { err = snd_config_get_string(n, &server); if (err < 0) { SNDERR("Invalid type for %s", id); return -EINVAL; } continue; } if (strcmp(id, "pcm") == 0) { err = snd_config_get_string(n, &pcm_name); if (err < 0) { SNDERR("Invalid type for %s", id); return -EINVAL; } continue; } SNDERR("Unknown field %s", id); return -EINVAL; } if (!pcm_name) { SNDERR("pcm is not defined"); return -EINVAL; } if (!server) { SNDERR("server is not defined"); return -EINVAL; } err = snd_config_search_definition(root, "server", server, &sconfig); if (err < 0) { SNDERR("Unknown server %s", server); return -EINVAL; } if (snd_config_get_type(sconfig) != SND_CONFIG_TYPE_COMPOUND) { SNDERR("Invalid type for server %s definition", server); goto _err; } snd_config_for_each(i, next, sconfig) { snd_config_t *n = snd_config_iterator_entry(i); const char *id; if (snd_config_get_id(n, &id) < 0) continue; if (strcmp(id, "comment") == 0) continue; if (strcmp(id, "host") == 0) continue; if (strcmp(id, "socket") == 0) { err = snd_config_get_string(n, &sockname); if (err < 0) { SNDERR("Invalid type for %s", id); goto _err; } continue; } if (strcmp(id, "port") == 0) { err = snd_config_get_integer(n, &port); if (err < 0) { SNDERR("Invalid type for %s", id); goto _err; } continue; } SNDERR("Unknown field %s", id); _err: err = -EINVAL; goto __error; } if (!sockname) { SNDERR("socket is not defined"); goto _err; } err = snd_pcm_shm_open(pcmp, name, sockname, pcm_name, stream, mode); __error: snd_config_delete(sconfig); return err; } #ifndef DOC_HIDDEN SND_DLSYM_BUILD_VERSION(_snd_pcm_shm_open, SND_PCM_DLSYM_VERSION); #endif