pipewire/spa/lib/control.c
Wim Taymans 05829f33e6 Work on memory allocation
We now only allow per port preallocated buffers. We exchange the index
into the array instead of passing the buffers around. We still use the
refcount to track when a buffer can be reused.
Improve API a little, allow passing the node as the first argument of
the interface call.
Implement alloc_buffer in v4l2 and improve the test.
2016-07-30 20:35:34 +02:00

1048 lines
26 KiB
C

/* Simple Plugin API
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <spa/control.h>
#if 0
#define SPA_DEBUG_CONTROL(format,args...) fprintf(stderr,format,##args)
#else
#define SPA_DEBUG_CONTROL(format,args...)
#endif
typedef struct {
uint32_t version;
uint32_t flags;
uint32_t length;
} SpaStackHeader;
typedef struct {
void *data;
size_t size;
size_t max_size;
void *free_data;
int *fds;
int n_fds;
int max_fds;
void *free_fds;
size_t magic;
} SpaStackControl;
#define SSC(c) ((SpaStackControl *) (c))
#define SSC_MAGIC ((size_t) 5493683301u)
#define is_valid_control(c) (c != NULL && \
SSC(c)->magic == SSC_MAGIC)
/**
* spa_control_init_data:
* @control: a #SpaControl
* @data: data
* @size: size of @data
* @fds: file descriptors
* @n_fds: number of file descriptors
*
* Initialize @control with @data and @size and @fds and @n_fds.
* The memory pointer to by @data and @fds becomes property of @control
* and should not be freed or modified until all references to the control
* are gone.
*/
SpaResult
spa_control_init_data (SpaControl *control,
void *data,
size_t size,
int *fds,
unsigned int n_fds)
{
SpaStackControl *sc = SSC (control);
SPA_DEBUG_CONTROL ("control %p: init", control);
sc->magic = SSC_MAGIC;
sc->data = data;
sc->size = size;
sc->max_size = size;
sc->free_data = NULL;
sc->fds = fds;
sc->n_fds = n_fds;
sc->max_fds = n_fds;
sc->free_fds = NULL;
return SPA_RESULT_OK;
}
/**
* spa_control_get_version
* @control: a #SpaControl
*
* Get the control version
*
* Returns: the control version.
*/
uint32_t
spa_control_get_version (SpaControl *control)
{
SpaStackControl *sc = SSC (control);
SpaStackHeader *hdr;
if (!is_valid_control (control))
return -1;
hdr = sc->data;
return hdr->version;
}
/**
* spa_control_get_fd:
* @control: a #SpaControl
* @index: an index
* @steal: steal the fd
*
* Get the file descriptor at @index in @control.
*
* Returns: a file descriptor at @index in @control. The file descriptor
* is not duplicated in any way. -1 is returned on error.
*/
int
spa_control_get_fd (SpaControl *control,
unsigned int index,
bool close)
{
SpaStackControl *sc = SSC (control);
int fd;
if (!is_valid_control (control))
return -1;
if (sc->fds == NULL || sc->n_fds < index)
return -1;
fd = sc->fds[index];
if (fd < 0)
fd = -fd;
sc->fds[index] = close ? fd : -fd;
return fd;
}
SpaResult
spa_control_clear (SpaControl *control)
{
SpaStackControl *sc = SSC (control);
int i;
if (!is_valid_control (control))
return SPA_RESULT_INVALID_ARGUMENTS;
sc->magic = 0;
free (sc->free_data);
for (i = 0; i < sc->n_fds; i++) {
if (sc->fds[i] > 0) {
SPA_DEBUG_CONTROL ("%p: close %d %d", control, i, sc->fds[i]);
if (close (sc->fds[i]) < 0)
perror ("close");
}
}
free (sc->free_fds);
sc->n_fds = 0;
return SPA_RESULT_OK;
}
/**
* SpaControlIter:
*
* #SpaControlIter is an opaque data structure and can only be accessed
* using the following functions.
*/
struct stack_iter {
size_t magic;
uint32_t version;
SpaStackControl *control;
size_t offset;
SpaControlCmd cmd;
size_t size;
void *data;
};
#define SCSI(i) ((struct stack_iter *) (i))
#define SCSI_MAGIC ((size_t) 6739527471u)
#define is_valid_iter(i) (i != NULL && \
SCSI(i)->magic == SCSI_MAGIC)
/**
* spa_control_iter_init:
* @iter: a #SpaControlIter
* @control: a #SpaControl
*
* Initialize @iter to iterate the packets in @control.
*/
SpaResult
spa_control_iter_init_full (SpaControlIter *iter,
SpaControl *control,
uint32_t version)
{
struct stack_iter *si = SCSI (iter);
if (iter == NULL || !is_valid_control (control))
return SPA_RESULT_INVALID_ARGUMENTS;
si->magic = SCSI_MAGIC;
si->version = version;
si->control = SSC (control);
si->offset = 0;
si->cmd = SPA_CONTROL_CMD_INVALID;
si->size = sizeof (SpaStackHeader);
si->data = NULL;
return SPA_RESULT_OK;
}
static bool
read_length (uint8_t * data, unsigned int size, size_t * length, size_t * skip)
{
size_t len, offset;
uint8_t b;
/* start reading the length, we need this to skip to the data later */
len = offset = 0;
do {
if (offset >= size)
return false;
b = data[offset++];
len = (len << 7) | (b & 0x7f);
} while (b & 0x80);
/* check remaining control size */
if (size - offset < len)
return false;
*length = len;
*skip = offset;
return true;
}
/**
* spa_control_iter_next:
* @iter: a #SpaControlIter
*
* Move to the next packet in @iter.
*
* Returns: %SPA_RESULT_OK if more packets are available.
*/
SpaResult
spa_control_iter_next (SpaControlIter *iter)
{
struct stack_iter *si = SCSI (iter);
size_t len, size, skip;
uint8_t *data;
if (!is_valid_iter (iter))
return SPA_RESULT_INVALID_ARGUMENTS;
/* move to next packet */
si->offset += si->size;
/* now read packet */
data = si->control->data;
size = si->control->size;
if (si->offset >= size)
return SPA_RESULT_ERROR;
data += si->offset;
size -= si->offset;
if (size < 1)
return SPA_RESULT_ERROR;
si->cmd = *data;
data++;
size--;
if (!read_length (data, size, &len, &skip))
return SPA_RESULT_ERROR;
si->size = len;
si->data = data + skip;
si->offset += 1 + skip;
return SPA_RESULT_OK;
}
/**
* spa_control_iter_done:
* @iter: a #SpaControlIter
*
* End iterations on @iter.
*/
SpaResult
spa_control_iter_end (SpaControlIter *iter)
{
struct stack_iter *si = SCSI (iter);
if (!is_valid_iter (iter))
return SPA_RESULT_INVALID_ARGUMENTS;
si->magic = 0;
return SPA_RESULT_OK;
}
SpaControlCmd
spa_control_iter_get_cmd (SpaControlIter *iter)
{
struct stack_iter *si = SCSI (iter);
if (!is_valid_iter (iter))
return SPA_CONTROL_CMD_INVALID;
return si->cmd;
}
void *
spa_control_iter_get_data (SpaControlIter *iter, size_t *size)
{
struct stack_iter *si = SCSI (iter);
if (!is_valid_iter (iter))
return NULL;
if (size)
*size = si->size;
return si->data;
}
typedef struct {
SpaBuffer buffer;
SpaMeta metas[16];
SpaData datas[16];
int memid[16];
} MyBuffer;
static SpaResult
parse_add_buffer (struct stack_iter *si,
SpaControlCmdAddBuffer *command)
{
MyBuffer *b;
uint32_t *p = si->data;
unsigned int i;
command->port = *p++;
b = malloc (sizeof (MyBuffer));
b->buffer.refcount = 1;
b->buffer.notify = free;
b->buffer.id = *(uint32_t *)p++;
b->buffer.size = *(uint32_t *)p++;
b->buffer.n_metas = *(uint32_t *)p++;
b->buffer.metas = b->metas;
b->buffer.n_datas = *(uint32_t *)p++;
b->buffer.datas = b->datas;
for (i = 0; i < b->buffer.n_metas; i++) {
SpaMeta *m = &b->metas[i];
m->type = *p++;
m->size = *p++;
m->data = p;
p = p + m->size / 4;
}
for (i = 0; i < b->buffer.n_datas; i++) {
SpaData *d = &b->datas[i];
d->type = SPA_DATA_TYPE_MEMID;
d->ptr_type = NULL;
b->memid[i] = *p++;
d->ptr = &b->memid[i];
d->offset = *p++;
d->size = *p++;
d->stride = *p++;
}
command->buffer = &b->buffer;
return SPA_RESULT_OK;
}
SpaResult
spa_control_iter_parse_cmd (SpaControlIter *iter,
void *command)
{
struct stack_iter *si = SCSI (iter);
SpaResult res = SPA_RESULT_OK;
if (!is_valid_iter (iter))
return SPA_RESULT_INVALID_ARGUMENTS;
switch (si->cmd) {
/* C -> S */
case SPA_CONTROL_CMD_NODE_UPDATE:
case SPA_CONTROL_CMD_PORT_UPDATE:
fprintf (stderr, "implement iter of %d\n", si->cmd);
break;
case SPA_CONTROL_CMD_PORT_REMOVED:
if (si->size < sizeof (SpaControlCmdPortRemoved))
return SPA_RESULT_ERROR;
memcpy (command, si->data, sizeof (SpaControlCmdPortRemoved));
break;
case SPA_CONTROL_CMD_START_CONFIGURE:
case SPA_CONTROL_CMD_PORT_STATUS_CHANGE:
case SPA_CONTROL_CMD_START_ALLOC:
break;
case SPA_CONTROL_CMD_NEED_INPUT:
if (si->size < sizeof (SpaControlCmdNeedInput))
return SPA_RESULT_ERROR;
memcpy (command, si->data, sizeof (SpaControlCmdNeedInput));
break;
case SPA_CONTROL_CMD_HAVE_OUTPUT:
if (si->size < sizeof (SpaControlCmdHaveOutput))
return SPA_RESULT_ERROR;
memcpy (command, si->data, sizeof (SpaControlCmdHaveOutput));
break;
/* S -> C */
case SPA_CONTROL_CMD_ADD_PORT:
if (si->size < sizeof (SpaControlCmdAddPort))
return SPA_RESULT_ERROR;
memcpy (command, si->data, sizeof (SpaControlCmdAddPort));
break;
case SPA_CONTROL_CMD_REMOVE_PORT:
if (si->size < sizeof (SpaControlCmdRemovePort))
return SPA_RESULT_ERROR;
memcpy (command, si->data, sizeof (SpaControlCmdRemovePort));
break;
case SPA_CONTROL_CMD_SET_FORMAT:
{
SpaControlCmdSetFormat *cmd;
uint32_t *p = si->data;
cmd = command;
cmd->port = *p++;
cmd->str = (char *)p;
break;
}
case SPA_CONTROL_CMD_SET_PROPERTY:
fprintf (stderr, "implement iter of %d\n", si->cmd);
break;
case SPA_CONTROL_CMD_END_CONFIGURE:
case SPA_CONTROL_CMD_PAUSE:
case SPA_CONTROL_CMD_START:
case SPA_CONTROL_CMD_STOP:
break;
/* bidirectional */
case SPA_CONTROL_CMD_ADD_MEM:
if (si->size < sizeof (SpaControlCmdAddMem))
return SPA_RESULT_ERROR;
memcpy (command, si->data, sizeof (SpaControlCmdAddMem));
break;
case SPA_CONTROL_CMD_REMOVE_MEM:
if (si->size < sizeof (SpaControlCmdRemoveMem))
return SPA_RESULT_ERROR;
memcpy (command, si->data, sizeof (SpaControlCmdRemoveMem));
break;
case SPA_CONTROL_CMD_ADD_BUFFER:
res = parse_add_buffer (si, command);
break;
case SPA_CONTROL_CMD_REMOVE_BUFFER:
if (si->size < sizeof (SpaControlCmdRemoveBuffer))
return SPA_RESULT_ERROR;
memcpy (command, si->data, sizeof (SpaControlCmdRemoveBuffer));
break;
case SPA_CONTROL_CMD_PROCESS_BUFFER:
if (si->size < sizeof (SpaControlCmdProcessBuffer))
return SPA_RESULT_ERROR;
memcpy (command, si->data, sizeof (SpaControlCmdProcessBuffer));
break;
case SPA_CONTROL_CMD_REUSE_BUFFER:
if (si->size < sizeof (SpaControlCmdReuseBuffer))
return SPA_RESULT_ERROR;
memcpy (command, si->data, sizeof (SpaControlCmdReuseBuffer));
break;
case SPA_CONTROL_CMD_INVALID:
return SPA_RESULT_ERROR;
}
return res;
}
struct stack_builder {
size_t magic;
SpaStackHeader *sh;
SpaStackControl control;
SpaControlCmd cmd;
size_t offset;
};
#define SCSB(b) ((struct stack_builder *) (b))
#define SCSB_MAGIC ((size_t) 8103647428u)
#define is_valid_builder(b) (b != NULL && \
SCSB(b)->magic == SCSB_MAGIC)
/**
* spa_control_builder_init_full:
* @builder: a #SpaControlBuilder
* @version: a version
* @data: data to build into or %NULL to allocate
* @max_data: allocated size of @data
* @fds: memory for fds
* @max_fds: maximum number of fds in @fds
*
* Initialize a stack allocated @builder and set the @version.
*/
SpaResult
spa_control_builder_init_full (SpaControlBuilder *builder,
uint32_t version,
void *data,
size_t max_data,
int *fds,
unsigned int max_fds)
{
struct stack_builder *sb = SCSB (builder);
SpaStackHeader *sh;
if (builder == NULL)
return SPA_RESULT_INVALID_ARGUMENTS;
sb->magic = SCSB_MAGIC;
if (max_data < sizeof (SpaStackHeader) || data == NULL) {
sb->control.max_size = sizeof (SpaStackHeader) + 128;
sb->control.data = malloc (sb->control.max_size);
sb->control.free_data = sb->control.data;
fprintf (stderr, "builder %p: realloc control memory %zd -> %zd\n",
builder, max_data, sb->control.max_size);
} else {
sb->control.max_size = max_data;
sb->control.data = data;
sb->control.free_data = NULL;
}
sb->control.size = sizeof (SpaStackHeader);
sb->control.fds = fds;
sb->control.max_fds = max_fds;
sb->control.n_fds = 0;
sb->control.free_fds = NULL;
sh = sb->sh = sb->control.data;
sh->version = version;
sh->flags = 0;
sh->length = 0;
sb->cmd = 0;
sb->offset = 0;
return SPA_RESULT_OK;
}
/**
* spa_control_builder_clear:
* @builder: a #SpaControlBuilder
*
* Clear the memory used by @builder. This can be used to abort building the
* control.
*
* @builder becomes invalid after this function and can be reused with
* spa_control_builder_init()
*/
SpaResult
spa_control_builder_clear (SpaControlBuilder *builder)
{
struct stack_builder *sb = SCSB (builder);
if (!is_valid_builder (builder))
return SPA_RESULT_INVALID_ARGUMENTS;
sb->magic = 0;
free (sb->control.free_data);
free (sb->control.free_fds);
return SPA_RESULT_OK;
}
/**
* spa_control_builder_end:
* @builder: a #SpaControlBuilder
* @control: a #SpaControl
*
* Ends the building process and fills @control with the constructed
* #SpaControl.
*
* @builder becomes invalid after this function and can be reused with
* spa_control_builder_init()
*/
SpaResult
spa_control_builder_end (SpaControlBuilder *builder,
SpaControl *control)
{
struct stack_builder *sb = SCSB (builder);
SpaStackControl *sc = SSC (control);
if (!is_valid_builder (builder) || control == NULL)
return SPA_RESULT_INVALID_ARGUMENTS;
sb->magic = 0;
sb->sh->length = sb->control.size - sizeof (SpaStackHeader);
sc->magic = SSC_MAGIC;
sc->data = sb->control.data;
sc->size = sb->control.size;
sc->max_size = sb->control.max_size;
sc->free_data = sb->control.free_data;
sc->fds = sb->control.fds;
sc->n_fds = sb->control.n_fds;
sc->max_fds = sb->control.max_fds;
sc->free_fds = sb->control.free_fds;
SPA_DEBUG_CONTROL ("builder %p: control %p init", builder, control);
return SPA_RESULT_OK;
}
/**
* spa_control_builder_add_fd:
* @builder: a #SpaControlBuilder
* @fd: a valid fd
*
* Add the file descriptor @fd to @builder.
*
* Returns: the index of the file descriptor in @builder.
*/
int
spa_control_builder_add_fd (SpaControlBuilder *builder,
int fd,
bool close)
{
struct stack_builder *sb = SCSB (builder);
int index;
if (!is_valid_builder (builder) || fd < 0)
return -1;
if (sb->control.n_fds >= sb->control.max_fds) {
int new_size = sb->control.max_fds + 8;
fprintf (stderr, "builder %p: realloc control fds %d -> %d\n",
builder, sb->control.max_fds, new_size);
sb->control.max_fds = new_size;
sb->control.free_fds = realloc (sb->control.free_fds, new_size * sizeof (int));
sb->control.fds = sb->control.free_fds;
}
index = sb->control.n_fds;
sb->control.fds[index] = close ? fd : -fd;
sb->control.n_fds++;
return index;
}
#define MAX(a,b) ((a) > (b) ? (a) : (b))
static void *
builder_ensure_size (struct stack_builder *sb, size_t size)
{
if (sb->control.size + size > sb->control.max_size) {
size_t new_size = sb->control.size + MAX (size, 1024);
fprintf (stderr, "builder %p: realloc control memory %zd -> %zd\n",
sb, sb->control.max_size, new_size);
sb->control.max_size = new_size;
sb->control.free_data = realloc (sb->control.free_data, new_size);
sb->sh = sb->control.data = sb->control.free_data;
}
return (uint8_t *) sb->control.data + sb->control.size;
}
static void *
builder_add_cmd (struct stack_builder *sb, SpaControlCmd cmd, size_t size)
{
uint8_t *p;
unsigned int plen;
plen = 1;
while (size >> (7 * plen))
plen++;
/* 1 for cmd, plen for size and size for payload */
p = builder_ensure_size (sb, 1 + plen + size);
sb->cmd = cmd;
sb->offset = sb->control.size;
sb->control.size += 1 + plen + size;
*p++ = cmd;
/* write length */
while (plen) {
plen--;
*p++ = ((plen > 0) ? 0x80 : 0) | ((size >> (7 * plen)) & 0x7f);
}
return p;
}
static SpaResult
build_add_buffer (struct stack_builder *sb,
SpaControlCmdAddBuffer *command)
{
unsigned int i;
size_t size;
SpaBuffer *b = command->buffer;
uint32_t *p;
/* port + id + size + n_metas + n_datas */
size = 4 + 4 + 4 + 4 + 4;
for (i = 0; i < b->n_metas; i++) {
SpaMeta *m = &b->metas[i];
/* type + size + data */
size += 4 + 4 + m->size;
}
for (i = 0; i < b->n_datas; i++) {
SpaData *d = &b->datas[i];
if (d->type != SPA_DATA_TYPE_MEMID)
continue;
/* memidx + offset + size + stride */
size += 4 + 4 + 4 + 4;
}
p = builder_add_cmd (sb, SPA_CONTROL_CMD_ADD_BUFFER, size);
*p++ = command->port;
*p++ = b->id;
*p++ = b->size;
*p++ = b->n_metas;
*p++ = b->n_datas;
for (i = 0; i < b->n_metas; i++) {
SpaMeta *m = &b->metas[i];
*p++ = m->type;
*p++ = m->size;
memcpy (p, m->data, m->size);
p = p + m->size / 4;
}
for (i = 0; i < b->n_datas; i++) {
SpaData *d = &b->datas[i];
if (d->type != SPA_DATA_TYPE_MEMID)
continue;
*p++ = *((uint32_t*)(d->ptr));
*p++ = d->offset;
*p++ = d->size;
*p++ = d->stride;
}
return SPA_RESULT_OK;
}
/**
* spa_control_builder_add_cmd:
* @builder: a #SpaControlBuilder
* @cmd: a #SpaControlCmd
* @command: a command
*
* Add a @cmd to @builder with data from @command.
*
* Returns: %TRUE on success.
*/
SpaResult
spa_control_builder_add_cmd (SpaControlBuilder *builder,
SpaControlCmd cmd,
void *command)
{
struct stack_builder *sb = SCSB (builder);
void *p;
SpaResult res = SPA_RESULT_OK;
if (!is_valid_builder (builder))
return SPA_RESULT_INVALID_ARGUMENTS;
switch (cmd) {
/* C -> S */
case SPA_CONTROL_CMD_NODE_UPDATE:
case SPA_CONTROL_CMD_PORT_UPDATE:
fprintf (stderr, "implement builder of %d\n", cmd);
break;
case SPA_CONTROL_CMD_PORT_REMOVED:
p = builder_add_cmd (sb, cmd, sizeof (SpaControlCmdPortRemoved));
memcpy (p, command, sizeof (SpaControlCmdPortRemoved));
break;
case SPA_CONTROL_CMD_START_CONFIGURE:
p = builder_add_cmd (sb, cmd, 0);
break;
case SPA_CONTROL_CMD_PORT_STATUS_CHANGE:
case SPA_CONTROL_CMD_START_ALLOC:
p = builder_add_cmd (sb, cmd, 0);
break;
case SPA_CONTROL_CMD_NEED_INPUT:
p = builder_add_cmd (sb, cmd, sizeof (SpaControlCmdNeedInput));
memcpy (p, command, sizeof (SpaControlCmdNeedInput));
break;
case SPA_CONTROL_CMD_HAVE_OUTPUT:
p = builder_add_cmd (sb, cmd, sizeof (SpaControlCmdHaveOutput));
memcpy (p, command, sizeof (SpaControlCmdHaveOutput));
break;
/* S -> C */
case SPA_CONTROL_CMD_ADD_PORT:
p = builder_add_cmd (sb, cmd, sizeof (SpaControlCmdAddPort));
memcpy (p, command, sizeof (SpaControlCmdAddPort));
break;
case SPA_CONTROL_CMD_REMOVE_PORT:
p = builder_add_cmd (sb, cmd, sizeof (SpaControlCmdRemovePort));
memcpy (p, command, sizeof (SpaControlCmdRemovePort));
break;
case SPA_CONTROL_CMD_SET_FORMAT:
{
size_t len, slen;
SpaControlCmdSetFormat *sf = command;
uint32_t *p;
slen = strlen (sf->str)+1;
/* port + string */
len = 4 + slen;
p = builder_add_cmd (sb, cmd, len);
*p++ = sf->port;
memcpy ((char*)p, sf->str, slen);
break;
}
case SPA_CONTROL_CMD_SET_PROPERTY:
fprintf (stderr, "implement builder of %d\n", cmd);
break;
case SPA_CONTROL_CMD_END_CONFIGURE:
p = builder_add_cmd (sb, cmd, 0);
break;
case SPA_CONTROL_CMD_PAUSE:
case SPA_CONTROL_CMD_START:
case SPA_CONTROL_CMD_STOP:
p = builder_add_cmd (sb, cmd, 0);
break;
/* bidirectional */
case SPA_CONTROL_CMD_ADD_MEM:
p = builder_add_cmd (sb, cmd, sizeof (SpaControlCmdAddMem));
memcpy (p, command, sizeof (SpaControlCmdAddMem));
break;
case SPA_CONTROL_CMD_REMOVE_MEM:
p = builder_add_cmd (sb, cmd, sizeof (SpaControlCmdRemoveMem));
memcpy (p, command, sizeof (SpaControlCmdRemoveMem));
break;
case SPA_CONTROL_CMD_ADD_BUFFER:
res = build_add_buffer (sb, (SpaControlCmdAddBuffer *) command);
break;
case SPA_CONTROL_CMD_REMOVE_BUFFER:
p = builder_add_cmd (sb, cmd, sizeof (SpaControlCmdRemoveBuffer));
memcpy (p, command, sizeof (SpaControlCmdRemoveBuffer));
break;
case SPA_CONTROL_CMD_PROCESS_BUFFER:
p = builder_add_cmd (sb, cmd, sizeof (SpaControlCmdProcessBuffer));
memcpy (p, command, sizeof (SpaControlCmdProcessBuffer));
break;
case SPA_CONTROL_CMD_REUSE_BUFFER:
p = builder_add_cmd (sb, cmd, sizeof (SpaControlCmdReuseBuffer));
memcpy (p, command, sizeof (SpaControlCmdReuseBuffer));
break;
default:
case SPA_CONTROL_CMD_INVALID:
return SPA_RESULT_INVALID_ARGUMENTS;
}
return res;
}
SpaResult
spa_control_read (SpaControl *control,
int fd,
void *data,
size_t max_data,
int *fds,
unsigned int max_fds)
{
ssize_t len;
SpaStackHeader *hdr;
SpaStackControl *sc = (SpaStackControl *) control;
size_t need;
struct cmsghdr *cmsg;
struct msghdr msg = {0};
struct iovec iov[1];
char cmsgbuf[CMSG_SPACE (max_fds * sizeof (int))];
sc->data = data;
sc->max_size = max_data;
sc->size = 0;
sc->free_data = NULL;
sc->fds = fds;
sc->max_fds = max_fds;
sc->n_fds = 0;
sc->free_fds = NULL;
hdr = sc->data;
/* read header and control messages first */
iov[0].iov_base = hdr;
iov[0].iov_len = sizeof (SpaStackHeader);;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof (cmsgbuf);
msg.msg_flags = MSG_CMSG_CLOEXEC;
while (true) {
len = recvmsg (fd, &msg, msg.msg_flags);
if (len < 0) {
if (errno == EINTR)
continue;
else
goto recv_error;
}
break;
}
if (len != sizeof (SpaStackHeader))
return SPA_RESULT_ERROR;
/* now we know the total length */
need = sizeof (SpaStackHeader) + hdr->length;
if (sc->max_size < need) {
fprintf (stderr, "control: realloc receive memory %zd -> %zd\n", sc->max_size, need);
sc->max_size = need;
hdr = sc->data = sc->free_data = realloc (sc->free_data, need);
}
sc->size = need;
if (hdr->length > 0) {
/* read data */
while (true) {
len = recv (fd, (uint8_t *)sc->data + sizeof (SpaStackHeader), hdr->length, 0);
if (len < 0) {
if (errno == EINTR)
continue;
else
goto recv_error;
}
break;
}
if (len != hdr->length)
return SPA_RESULT_ERROR;
}
/* handle control messages */
for (cmsg = CMSG_FIRSTHDR (&msg); cmsg != NULL; cmsg = CMSG_NXTHDR (&msg, cmsg)) {
if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
continue;
sc->n_fds = (cmsg->cmsg_len - ((char *)CMSG_DATA (cmsg) - (char *)cmsg)) / sizeof (int);
memcpy (sc->fds, CMSG_DATA (cmsg), sc->n_fds * sizeof (int));
}
sc->magic = SSC_MAGIC;
return SPA_RESULT_OK;
/* ERRORS */
recv_error:
{
fprintf (stderr, "could not recvmsg: %s\n", strerror (errno));
return SPA_RESULT_ERROR;
}
}
SpaResult
spa_control_write (SpaControl *control,
int fd)
{
SpaStackControl *sc = (SpaStackControl *) control;
ssize_t len;
struct msghdr msg = {0};
struct iovec iov[1];
struct cmsghdr *cmsg;
char cmsgbuf[CMSG_SPACE (sc->n_fds * sizeof (int))];
int fds_len = sc->n_fds * sizeof (int), *cm, i;
iov[0].iov_base = sc->data;
iov[0].iov_len = sc->size;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = cmsgbuf;
msg.msg_controllen = CMSG_SPACE (fds_len);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN (fds_len);
cm = (int*)CMSG_DATA (cmsg);
for (i = 0; i < fds_len; i++)
cm[i] = sc->fds[i] > 0 ? sc->fds[i] : -sc->fds[i];
msg.msg_controllen = cmsg->cmsg_len;
while (true) {
len = sendmsg (fd, &msg, 0);
if (len < 0) {
if (errno == EINTR)
continue;
else
goto send_error;
}
break;
}
if (len != (ssize_t) sc->size)
return SPA_RESULT_ERROR;
return SPA_RESULT_OK;
/* ERRORS */
send_error:
{
fprintf (stderr, "could not sendmsg: %s\n", strerror (errno));
return SPA_RESULT_ERROR;
}
}