pipewire/spa/lib/control.c
Wim Taymans 27acab7532 Improve memory handling
Reserve 0,0 for stack allocated mem that we never free
Improve allocation in the link
Parse format and properties in-line, there is no need to allocate
memory.
Improve buffer cleanup in alsa
Add timestamps and offset to alsa
2016-09-20 19:52:05 +02:00

1440 lines
36 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>
#include <spa/debug.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) {
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;
}
static SpaProps *
parse_props (void *p, off_t offset)
{
SpaProps *tp;
unsigned int i, j;
SpaPropInfo *pi;
SpaPropRangeInfo *ri;
tp = SPA_MEMBER (p, offset, SpaProps);
tp->prop_info = SPA_MEMBER (tp, SPA_PTR_TO_INT (tp->prop_info), SpaPropInfo);
/* now fix all the pointers */
for (i = 0; i < tp->n_prop_info; i++) {
pi = (SpaPropInfo *) &tp->prop_info[i];
if (pi->name)
pi->name = SPA_MEMBER (tp, SPA_PTR_TO_INT (pi->name), char);
if (pi->description)
pi->description = SPA_MEMBER (tp, SPA_PTR_TO_INT (pi->description), char);
if (pi->range_values)
pi->range_values = SPA_MEMBER (tp, SPA_PTR_TO_INT (pi->range_values), SpaPropRangeInfo);
for (j = 0; j < pi->n_range_values; j++) {
ri = (SpaPropRangeInfo *) &pi->range_values[j];
if (ri->name)
ri->name = SPA_MEMBER (tp, SPA_PTR_TO_INT (ri->name), char);
if (ri->description)
ri->description = SPA_MEMBER (tp, SPA_PTR_TO_INT (ri->description), char);
if (ri->val.value)
ri->val.value = SPA_MEMBER (tp, SPA_PTR_TO_INT (ri->val.value), void);
}
}
return tp;
}
static SpaFormat *
parse_format (void *p, size_t size, off_t offset)
{
SpaFormat *f;
if (offset == 0)
return NULL;
f = SPA_MEMBER (p, offset, SpaFormat);
f->mem.mem.pool_id = 0;
f->mem.mem.id = 0;
f->mem.offset = offset;
f->mem.size = size - offset;
parse_props (f, offsetof (SpaFormat, props));
return f;
}
static void
iter_parse_node_update (struct stack_iter *si, SpaControlCmdNodeUpdate *nu)
{
memcpy (nu, si->data, sizeof (SpaControlCmdNodeUpdate));
if (nu->props)
nu->props = parse_props (si->data, SPA_PTR_TO_INT (nu->props));
}
static void
iter_parse_port_update (struct stack_iter *si, SpaControlCmdPortUpdate *pu)
{
void *p;
unsigned int i;
memcpy (pu, si->data, sizeof (SpaControlCmdPortUpdate));
p = si->data;
if (pu->possible_formats)
pu->possible_formats = SPA_MEMBER (p,
SPA_PTR_TO_INT (pu->possible_formats), SpaFormat *);
for (i = 0; i < pu->n_possible_formats; i++) {
pu->possible_formats[i] = parse_format (p, si->size,
SPA_PTR_TO_INT (pu->possible_formats[i]));
}
if (pu->props)
pu->props = parse_props (p, SPA_PTR_TO_INT (pu->props));
if (pu->info) {
SpaPortInfo *pi;
pu->info = p = SPA_MEMBER (p, SPA_PTR_TO_INT (pu->info), SpaPortInfo);
pi = (SpaPortInfo *) pu->info;
pi->params = SPA_MEMBER (p, SPA_PTR_TO_INT (pi->params), SpaAllocParam *);
for (i = 0; i < pi->n_params; i++) {
pi->params[i] = SPA_MEMBER (p, SPA_PTR_TO_INT (pi->params[i]), SpaAllocParam);
}
}
}
static void
iter_parse_set_format (struct stack_iter *si, SpaControlCmdSetFormat *cmd)
{
memcpy (cmd, si->data, sizeof (SpaControlCmdSetFormat));
cmd->format = parse_format (si->data, si->size, SPA_PTR_TO_INT (cmd->format));
}
static void
iter_parse_use_buffers (struct stack_iter *si, SpaControlCmdUseBuffers *cmd)
{
void *p;
int i;
p = si->data;
memcpy (cmd, p, sizeof (SpaControlCmdUseBuffers));
if (cmd->buffers)
cmd->buffers = SPA_MEMBER (p, SPA_PTR_TO_INT (cmd->buffers), SpaBuffer *);
for (i = 0; i < cmd->n_buffers; i++) {
SpaMemoryChunk *mc;
SpaMemory *mem;
mc = SPA_MEMBER (p, SPA_PTR_TO_INT (cmd->buffers[i]), SpaMemoryChunk);
mem = spa_memory_find (&mc->mem);
cmd->buffers[i] = mem ? SPA_MEMBER (spa_memory_ensure_ptr (mem), mc->offset, SpaBuffer) : NULL;
}
}
static void
iter_parse_node_event (struct stack_iter *si, SpaControlCmdNodeEvent *cmd)
{
void *p = si->data;
memcpy (cmd, p, sizeof (SpaControlCmdNodeEvent));
if (cmd->event)
cmd->event = SPA_MEMBER (p, SPA_PTR_TO_INT (cmd->event), SpaNodeEvent);
if (cmd->event->data)
cmd->event->data = SPA_MEMBER (p, SPA_PTR_TO_INT (cmd->event->data), void);
}
static void
iter_parse_node_command (struct stack_iter *si, SpaControlCmdNodeCommand *cmd)
{
void *p = si->data;
memcpy (cmd, p, sizeof (SpaControlCmdNodeCommand));
if (cmd->command)
cmd->command = SPA_MEMBER (p, SPA_PTR_TO_INT (cmd->command), SpaNodeCommand);
if (cmd->command->data)
cmd->command->data = SPA_MEMBER (p, SPA_PTR_TO_INT (cmd->command->data), void);
}
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:
iter_parse_node_update (si, command);
break;
case SPA_CONTROL_CMD_PORT_UPDATE:
iter_parse_port_update (si, command);
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_PORT_STATUS_CHANGE:
fprintf (stderr, "implement iter of %d\n", si->cmd);
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:
iter_parse_set_format (si, command);
break;
case SPA_CONTROL_CMD_SET_PROPERTY:
fprintf (stderr, "implement iter of %d\n", si->cmd);
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_USE_BUFFERS:
iter_parse_use_buffers (si, command);
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_NODE_EVENT:
iter_parse_node_event (si, command);
break;
case SPA_CONTROL_CMD_NODE_COMMAND:
iter_parse_node_command (si, command);
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: alloc 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;
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, i;
if (!is_valid_builder (builder) || fd < 0)
return -1;
for (i = 0; i < sb->control.n_fds; i++) {
if (sb->control.fds[i] == fd || sb->control.fds[i] == -fd)
return i;
}
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;
if (sb->control.free_fds == NULL) {
sb->control.free_fds = malloc (new_size * sizeof (int));
memcpy (sb->control.free_fds, sb->control.fds, sb->control.n_fds * sizeof (int));
} else {
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;
if (sb->control.free_data == NULL) {
sb->control.free_data = malloc (new_size);
memcpy (sb->control.free_data, sb->control.data, sb->control.size);
} else {
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 size_t
calc_props_len (const SpaProps *props)
{
size_t len;
unsigned int i, j;
SpaPropInfo *pi;
SpaPropRangeInfo *ri;
if (props == NULL)
return 0;
/* props and unset mask */
len = sizeof (SpaProps) + sizeof (uint32_t);
for (i = 0; i < props->n_prop_info; i++) {
pi = (SpaPropInfo *) &props->prop_info[i];
len += sizeof (SpaPropInfo);
len += pi->name ? strlen (pi->name) + 1 : 0;
len += pi->description ? strlen (pi->description) + 1 : 0;
/* for the value */
len += pi->maxsize;
for (j = 0; j < pi->n_range_values; j++) {
ri = (SpaPropRangeInfo *)&pi->range_values[j];
len += sizeof (SpaPropRangeInfo);
len += ri->name ? strlen (ri->name) + 1 : 0;
len += ri->description ? strlen (ri->description) + 1 : 0;
/* the size of the range value */
len += ri->val.size;
}
}
return len;
}
static size_t
write_props (void *p, const SpaProps *props, off_t offset)
{
size_t len, slen;
unsigned int i, j;
SpaProps *tp;
SpaPropInfo *pi, *bpi;
SpaPropRangeInfo *ri, *bri;
tp = p;
memcpy (tp, props, sizeof (SpaProps));
tp->prop_info = SPA_INT_TO_PTR (offset + sizeof (uint32_t));
/* write propinfo array */
bpi = pi = SPA_MEMBER (tp, SPA_PTR_TO_INT (tp->prop_info), SpaPropInfo);
for (i = 0; i < tp->n_prop_info; i++) {
memcpy (pi, &props->prop_info[i], sizeof (SpaPropInfo));
pi++;
}
bri = ri = (SpaPropRangeInfo *) pi;
pi = bpi;
/* write range info arrays, adjust offset to it */
for (i = 0; i < tp->n_prop_info; i++) {
pi->range_values = SPA_INT_TO_PTR (SPA_PTRDIFF (ri, tp));
for (j = 0; j < pi->n_range_values; j++) {
memcpy (ri, &props->prop_info[i].range_values[j], sizeof (SpaPropRangeInfo));
ri++;
}
pi++;
}
p = ri;
pi = bpi;
ri = bri;
/* strings and default values from props and ranges */
for (i = 0; i < tp->n_prop_info; i++) {
if (pi->name) {
slen = strlen (pi->name) + 1;
memcpy (p, pi->name, slen);
pi->name = SPA_INT_TO_PTR (SPA_PTRDIFF (p, tp));
p += slen;
} else {
pi->name = 0;
}
if (pi->description) {
slen = strlen (pi->description) + 1;
memcpy (p, pi->description, slen);
pi->description = SPA_INT_TO_PTR (SPA_PTRDIFF (p, tp));
p += slen;
} else {
pi->description = 0;
}
for (j = 0; j < pi->n_range_values; j++) {
if (ri->name) {
slen = strlen (ri->name) + 1;
memcpy (p, ri->name, slen);
ri->name = SPA_INT_TO_PTR (SPA_PTRDIFF (p, tp));
p += slen;
} else {
ri->name = 0;
}
if (ri->description) {
slen = strlen (ri->description) + 1;
memcpy (p, ri->description, slen);
ri->description = SPA_INT_TO_PTR (SPA_PTRDIFF (p, tp));
p += slen;
} else {
ri->description = 0;
}
if (ri->val.size) {
memcpy (p, ri->val.value, ri->val.size);
ri->val.value = SPA_INT_TO_PTR (SPA_PTRDIFF (p, tp));
p += ri->val.size;
} else {
ri->val.value = 0;
}
ri++;
}
pi++;
}
/* and the actual values */
pi = bpi;
for (i = 0; i < tp->n_prop_info; i++) {
if (pi->offset) {
memcpy (p, SPA_MEMBER (props, pi->offset, void), pi->maxsize);
pi->offset = SPA_PTRDIFF (p, tp);
p += pi->maxsize;
} else {
pi->offset = 0;
}
pi++;
}
len = SPA_PTRDIFF (p, tp);
return len;
}
static size_t
calc_format_len (const SpaFormat *format)
{
if (format == NULL)
return 0;
return calc_props_len (&format->props) - sizeof (SpaProps) + sizeof (SpaFormat);
}
static size_t
write_format (void *p, const SpaFormat *format)
{
SpaFormat *tf;
tf = p;
tf->media_type = format->media_type;
tf->media_subtype = format->media_subtype;
tf->mem.mem.pool_id = SPA_ID_INVALID;
tf->mem.mem.id = SPA_ID_INVALID;
tf->mem.offset = 0;
tf->mem.size = 0;
p = SPA_MEMBER (tf, offsetof (SpaFormat, props), void);
return write_props (p, &format->props, sizeof (SpaFormat));
}
static size_t
write_port_info (void *p, const SpaPortInfo *info)
{
SpaPortInfo *tp;
SpaAllocParam **ap;
int i;
size_t len;
if (info == NULL)
return 0;
tp = p;
memcpy (tp, info, sizeof (SpaPortInfo));
p = SPA_MEMBER (tp, sizeof (SpaPortInfo), SpaAllocParam *);
ap = p;
if (info->n_params)
tp->params = SPA_INT_TO_PTR (SPA_PTRDIFF (p, tp));
else
tp->params = 0;
tp->features = 0;
p = SPA_MEMBER (p, sizeof (SpaAllocParam*) * info->n_params, void);
for (i = 0; i < info->n_params; i++) {
len = info->params[i]->size;
memcpy (p, info->params[i], len);
ap[i] = SPA_INT_TO_PTR (SPA_PTRDIFF (p, tp));
p = SPA_MEMBER (p, len, void);
}
return SPA_PTRDIFF (p, tp);
}
static void
builder_add_node_update (struct stack_builder *sb, SpaControlCmdNodeUpdate *nu)
{
size_t len;
void *p;
SpaControlCmdNodeUpdate *d;
/* calc len */
len = sizeof (SpaControlCmdNodeUpdate);
len += calc_props_len (nu->props);
p = builder_add_cmd (sb, SPA_CONTROL_CMD_NODE_UPDATE, len);
memcpy (p, nu, sizeof (SpaControlCmdNodeUpdate));
d = p;
p = SPA_MEMBER (d, sizeof (SpaControlCmdNodeUpdate), void);
if (nu->props) {
len = write_props (p, nu->props, sizeof (SpaProps));
d->props = SPA_INT_TO_PTR (SPA_PTRDIFF (p, d));
} else {
d->props = 0;
}
}
static void
builder_add_port_update (struct stack_builder *sb, SpaControlCmdPortUpdate *pu)
{
size_t len;
void *p;
int i;
SpaFormat **bfa;
SpaControlCmdPortUpdate *d;
/* calc len */
len = sizeof (SpaControlCmdPortUpdate);
len += pu->n_possible_formats * sizeof (SpaFormat *);
for (i = 0; i < pu->n_possible_formats; i++)
len += calc_format_len (pu->possible_formats[i]);
len += calc_props_len (pu->props);
if (pu->info) {
len += sizeof (SpaPortInfo);
len += pu->info->n_params * sizeof (SpaAllocParam *);
for (i = 0; i < pu->info->n_params; i++)
len += pu->info->params[i]->size;
}
p = builder_add_cmd (sb, SPA_CONTROL_CMD_PORT_UPDATE, len);
memcpy (p, pu, sizeof (SpaControlCmdPortUpdate));
d = p;
p = SPA_MEMBER (d, sizeof (SpaControlCmdPortUpdate), void);
bfa = p;
if (pu->n_possible_formats)
d->possible_formats = SPA_INT_TO_PTR (SPA_PTRDIFF (p, d));
else
d->possible_formats = 0;
p = SPA_MEMBER (p, sizeof (SpaFormat*) * pu->n_possible_formats, void);
for (i = 0; i < pu->n_possible_formats; i++) {
len = write_format (p, pu->possible_formats[i]);
bfa[i] = SPA_INT_TO_PTR (SPA_PTRDIFF (p, d));
p = SPA_MEMBER (p, len, void);
}
if (pu->props) {
len = write_props (p, pu->props, sizeof (SpaProps));
d->props = SPA_INT_TO_PTR (SPA_PTRDIFF (p, d));
p = SPA_MEMBER (p, len, void);
} else {
d->props = 0;
}
if (pu->info) {
len = write_port_info (p, pu->info);
d->info = SPA_INT_TO_PTR (SPA_PTRDIFF (p, d));
p = SPA_MEMBER (p, len, void);
} else {
d->info = 0;
}
}
static void
builder_add_set_format (struct stack_builder *sb, SpaControlCmdSetFormat *sf)
{
size_t len;
void *p;
/* calculate length */
/* port_id + format + mask */
len = sizeof (SpaControlCmdSetFormat) + calc_format_len (sf->format);
p = builder_add_cmd (sb, SPA_CONTROL_CMD_SET_FORMAT, len);
memcpy (p, sf, sizeof (SpaControlCmdSetFormat));
sf = p;
p = SPA_MEMBER (sf, sizeof (SpaControlCmdSetFormat), void);
if (sf->format) {
len = write_format (p, sf->format);
sf->format = SPA_INT_TO_PTR (SPA_PTRDIFF (p, sf));
} else
sf->format = 0;
}
static void
builder_add_use_buffers (struct stack_builder *sb, SpaControlCmdUseBuffers *ub)
{
size_t len;
void *p;
int i;
SpaMemoryChunk **bmc;
SpaControlCmdUseBuffers *d;
/* calculate length */
/* port_id + format + mask */
len = sizeof (SpaControlCmdUseBuffers);
len += ub->n_buffers * sizeof (SpaBuffer *);
len += ub->n_buffers * sizeof (SpaMemoryChunk);
p = builder_add_cmd (sb, SPA_CONTROL_CMD_USE_BUFFERS, len);
memcpy (p, ub, sizeof (SpaControlCmdUseBuffers));
d = p;
p = SPA_MEMBER (d, sizeof (SpaControlCmdUseBuffers), void);
bmc = p;
if (d->n_buffers)
d->buffers = SPA_INT_TO_PTR (SPA_PTRDIFF (bmc, d));
else
d->buffers = 0;
p = SPA_MEMBER (p, sizeof (SpaMemoryChunk*) * ub->n_buffers, void);
for (i = 0; i < ub->n_buffers; i++) {
memcpy (p, &ub->buffers[i]->mem, sizeof (SpaMemoryChunk));
bmc[i] = SPA_INT_TO_PTR (SPA_PTRDIFF (p, d));
p = SPA_MEMBER (p, sizeof (SpaMemoryChunk), void);
}
}
static void
builder_add_node_event (struct stack_builder *sb, SpaControlCmdNodeEvent *ev)
{
size_t len;
void *p;
SpaControlCmdNodeEvent *d;
SpaNodeEvent *ne;
/* calculate length */
len = sizeof (SpaControlCmdNodeEvent);
len += sizeof (SpaNodeEvent);
len += ev->event->size;
p = builder_add_cmd (sb, SPA_CONTROL_CMD_NODE_EVENT, len);
memcpy (p, ev, sizeof (SpaControlCmdNodeEvent));
d = p;
p = SPA_MEMBER (d, sizeof (SpaControlCmdNodeEvent), void);
d->event = SPA_INT_TO_PTR (SPA_PTRDIFF (p, d));
ne = p;
memcpy (p, ev->event, sizeof (SpaNodeEvent));
p = SPA_MEMBER (p, sizeof (SpaNodeEvent), void);
ne->data = SPA_INT_TO_PTR (SPA_PTRDIFF (p, d));
memcpy (p, ev->event->data, ev->event->size);
}
static void
builder_add_node_command (struct stack_builder *sb, SpaControlCmdNodeCommand *cm)
{
size_t len;
void *p;
SpaControlCmdNodeCommand *d;
SpaNodeCommand *nc;
/* calculate length */
len = sizeof (SpaControlCmdNodeCommand);
len += sizeof (SpaNodeCommand);
len += cm->command->size;
p = builder_add_cmd (sb, SPA_CONTROL_CMD_NODE_COMMAND, len);
memcpy (p, cm, sizeof (SpaControlCmdNodeCommand));
d = p;
p = SPA_MEMBER (d, sizeof (SpaControlCmdNodeCommand), void);
d->command = SPA_INT_TO_PTR (SPA_PTRDIFF (p, d));
nc = p;
memcpy (p, cm->command, sizeof (SpaNodeCommand));
p = SPA_MEMBER (p, sizeof (SpaNodeCommand), void);
nc->data = SPA_INT_TO_PTR (SPA_PTRDIFF (p, d));
memcpy (p, cm->command->data, cm->command->size);
}
/**
* 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:
builder_add_node_update (sb, command);
break;
case SPA_CONTROL_CMD_PORT_UPDATE:
builder_add_port_update (sb, command);
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_PORT_STATUS_CHANGE:
p = builder_add_cmd (sb, cmd, 0);
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:
builder_add_set_format (sb, command);
break;
case SPA_CONTROL_CMD_SET_PROPERTY:
fprintf (stderr, "implement builder of %d\n", cmd);
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_USE_BUFFERS:
builder_add_use_buffers (sb, command);
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_NODE_EVENT:
builder_add_node_event (sb, command);
break;
case SPA_CONTROL_CMD_NODE_COMMAND:
builder_add_node_command (sb, command);
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;
if (sc->free_data == NULL) {
sc->free_data = malloc (need);
memcpy (sc->free_data, sc->data, len);
} else {
sc->free_data = realloc (sc->free_data, need);
}
hdr = sc->data = sc->free_data;
}
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)
goto wrong_length;
}
/* 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;
SPA_DEBUG_CONTROL ("control %p: read %zd bytes and %d fds\n", sc, len, sc->n_fds);
return SPA_RESULT_OK;
/* ERRORS */
recv_error:
{
fprintf (stderr, "could not recvmsg: %s\n", strerror (errno));
return SPA_RESULT_ERROR;
}
wrong_length:
{
fprintf (stderr, "wrong header length %zd != %u\n", len, hdr->length);
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;
if (sc->n_fds > 0) {
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 < sc->n_fds; i++)
cm[i] = sc->fds[i] > 0 ? sc->fds[i] : -sc->fds[i];
msg.msg_controllen = cmsg->cmsg_len;
} else {
msg.msg_control = NULL;
msg.msg_controllen = 0;
}
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;
SPA_DEBUG_CONTROL ("control %p: written %zd bytes and %d fds\n", sc, len, sc->n_fds);
return SPA_RESULT_OK;
/* ERRORS */
send_error:
{
fprintf (stderr, "could not sendmsg: %s\n", strerror (errno));
return SPA_RESULT_ERROR;
}
}