mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
spa: node-driver: Expose the clock id as param properties
This commit is contained in:
parent
ec11859a48
commit
752de866ae
1 changed files with 301 additions and 58 deletions
|
|
@ -26,6 +26,8 @@
|
|||
#include <spa/node/io.h>
|
||||
#include <spa/node/utils.h>
|
||||
#include <spa/param/param.h>
|
||||
#include <spa/pod/filter.h>
|
||||
#include <spa/pod/parser.h>
|
||||
|
||||
SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.driver");
|
||||
|
||||
|
|
@ -48,12 +50,16 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.driver");
|
|||
#define BW_PERIOD (3 * SPA_NSEC_PER_SEC)
|
||||
#define MAX_ERROR_MS 1
|
||||
|
||||
#define CLOCK_NAME_MAX 64
|
||||
|
||||
struct props {
|
||||
bool freewheel;
|
||||
char clock_name[64];
|
||||
char clock_name[CLOCK_NAME_MAX];
|
||||
clockid_t clock_id;
|
||||
uint32_t freewheel_wait;
|
||||
float resync_ms;
|
||||
char clock_device[CLOCK_NAME_MAX];
|
||||
char clock_interface[CLOCK_NAME_MAX];
|
||||
};
|
||||
|
||||
struct clock_offset {
|
||||
|
|
@ -73,7 +79,10 @@ struct impl {
|
|||
|
||||
uint64_t info_all;
|
||||
struct spa_node_info info;
|
||||
struct spa_param_info params[1];
|
||||
#define NODE_PropInfo 0
|
||||
#define NODE_Props 1
|
||||
#define N_NODE_PARAMS 2
|
||||
struct spa_param_info params[N_NODE_PARAMS];
|
||||
|
||||
struct spa_hook_list hooks;
|
||||
struct spa_callbacks callbacks;
|
||||
|
|
@ -99,13 +108,20 @@ struct impl {
|
|||
struct clock_offset nsec_offset;
|
||||
};
|
||||
|
||||
static void reset_props_strings(struct props *props)
|
||||
{
|
||||
spa_zero(props->clock_name);
|
||||
spa_zero(props->clock_device);
|
||||
spa_zero(props->clock_interface);
|
||||
}
|
||||
|
||||
static void reset_props(struct props *props)
|
||||
{
|
||||
props->freewheel = DEFAULT_FREEWHEEL;
|
||||
spa_zero(props->clock_name);
|
||||
props->clock_id = CLOCK_MONOTONIC;
|
||||
props->freewheel_wait = DEFAULT_FREEWHEEL_WAIT;
|
||||
props->resync_ms = DEFAULT_RESYNC_MS;
|
||||
reset_props_strings(props);
|
||||
}
|
||||
|
||||
static const struct clock_info {
|
||||
|
|
@ -598,10 +614,280 @@ static int impl_node_process(void *object)
|
|||
return SPA_STATUS_HAVE_DATA | SPA_STATUS_NEED_DATA;
|
||||
}
|
||||
|
||||
static int impl_node_enum_params(void *object, int seq,
|
||||
uint32_t id, uint32_t start, uint32_t num,
|
||||
const struct spa_pod *filter)
|
||||
{
|
||||
struct impl *this = object;
|
||||
struct spa_pod *param;
|
||||
struct spa_pod_builder b = { 0 };
|
||||
uint8_t buffer[4096];
|
||||
struct spa_result_node_params result;
|
||||
uint32_t count = 0;
|
||||
|
||||
spa_return_val_if_fail(this != NULL, -EINVAL);
|
||||
spa_return_val_if_fail(num != 0, -EINVAL);
|
||||
|
||||
result.id = id;
|
||||
result.next = start;
|
||||
next:
|
||||
result.index = result.next++;
|
||||
|
||||
spa_pod_builder_init(&b, buffer, sizeof(buffer));
|
||||
|
||||
switch (id) {
|
||||
case SPA_PARAM_PropInfo:
|
||||
{
|
||||
struct props *p = &this->props;
|
||||
|
||||
switch (result.index) {
|
||||
case 0:
|
||||
param = spa_pod_builder_add_object(&b,
|
||||
SPA_TYPE_OBJECT_PropInfo, id,
|
||||
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_clockId),
|
||||
SPA_PROP_INFO_description, SPA_POD_String("The clock id (monotonic, realtime, etc.)"),
|
||||
SPA_PROP_INFO_type, SPA_POD_String(clock_id_to_name(p->clock_id)));
|
||||
break;
|
||||
case 1:
|
||||
param = spa_pod_builder_add_object(&b,
|
||||
SPA_TYPE_OBJECT_PropInfo, id,
|
||||
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_clockDevice),
|
||||
SPA_PROP_INFO_description, SPA_POD_String("The clock device (eg. /dev/ptp0)"),
|
||||
SPA_PROP_INFO_type, SPA_POD_Stringn(p->clock_device, sizeof(p->clock_device)));
|
||||
break;
|
||||
case 2:
|
||||
param = spa_pod_builder_add_object(&b,
|
||||
SPA_TYPE_OBJECT_PropInfo, id,
|
||||
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_clockInterface),
|
||||
SPA_PROP_INFO_description, SPA_POD_String("The clock network interface (eg. eth0)"),
|
||||
SPA_PROP_INFO_type, SPA_POD_Stringn(p->clock_interface, sizeof(p->clock_interface)));
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SPA_PARAM_Props:
|
||||
{
|
||||
struct props *p = &this->props;
|
||||
|
||||
switch (result.index) {
|
||||
case 0:
|
||||
param = spa_pod_builder_add_object(&b,
|
||||
SPA_TYPE_OBJECT_Props, id,
|
||||
SPA_PROP_clockId, SPA_POD_String(clock_id_to_name(p->clock_id))
|
||||
);
|
||||
break;
|
||||
case 1:
|
||||
param = spa_pod_builder_add_object(&b,
|
||||
SPA_TYPE_OBJECT_Props, id,
|
||||
SPA_PROP_clockDevice, SPA_POD_Stringn(p->clock_device, sizeof(p->clock_device))
|
||||
);
|
||||
break;
|
||||
case 2:
|
||||
param = spa_pod_builder_add_object(&b,
|
||||
SPA_TYPE_OBJECT_Props, id,
|
||||
SPA_PROP_clockInterface, SPA_POD_Stringn(p->clock_interface, sizeof(p->clock_interface))
|
||||
);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (spa_pod_filter(&b, &result.param, param, filter) < 0)
|
||||
goto next;
|
||||
|
||||
spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
|
||||
|
||||
if (++count != num)
|
||||
goto next;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_phc_index(struct spa_system *s, const char *name) {
|
||||
#ifdef ETHTOOL_GET_TS_INFO
|
||||
struct ethtool_ts_info info = {0};
|
||||
struct ifreq ifr = {0};
|
||||
int fd, err;
|
||||
|
||||
info.cmd = ETHTOOL_GET_TS_INFO;
|
||||
strncpy(ifr.ifr_name, name, IFNAMSIZ - 1);
|
||||
ifr.ifr_data = (char *) &info;
|
||||
|
||||
fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
err = spa_system_ioctl(s, fd, SIOCETHTOOL, &ifr);
|
||||
close(fd);
|
||||
if (err < 0)
|
||||
return -errno;
|
||||
|
||||
return info.phc_index;
|
||||
#else
|
||||
return -ENOTSUP;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool parse_clock_id(struct impl *this, const char *s)
|
||||
{
|
||||
int id = clock_name_to_id(s);
|
||||
if (id == -1) {
|
||||
spa_log_info(this->log, "unknown clock id '%s'", s);
|
||||
return false;
|
||||
}
|
||||
this->props.clock_id = id;
|
||||
if (this->clock_fd >= 0) {
|
||||
close(this->clock_fd);
|
||||
this->clock_fd = -1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_clock_device(struct impl *this, const char *s)
|
||||
{
|
||||
int fd = open(s, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
spa_log_info(this->log, "failed to open clock device '%s': %m", s);
|
||||
return false;
|
||||
}
|
||||
if (this->clock_fd >= 0) {
|
||||
close(this->clock_fd);
|
||||
}
|
||||
this->clock_fd = fd;
|
||||
this->props.clock_id = FD_TO_CLOCKID(this->clock_fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_clock_interface(struct impl *this, const char *s)
|
||||
{
|
||||
int phc_index = get_phc_index(this->data_system, s);
|
||||
if (phc_index < 0) {
|
||||
spa_log_info(this->log, "failed to get phc device index for interface '%s': %s",
|
||||
s, spa_strerror(phc_index));
|
||||
return false;
|
||||
} else {
|
||||
char dev[19];
|
||||
spa_scnprintf(dev, sizeof(dev), "/dev/ptp%d", phc_index);
|
||||
if (!parse_clock_device(this, dev)) {
|
||||
spa_log_info(this->log, "failed to open clock device '%s' "
|
||||
"for interface '%s': %m", dev, s);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ensure_clock_name(struct impl *this)
|
||||
{
|
||||
struct props *p = &this->props;
|
||||
if (p->clock_name[0] == '\0') {
|
||||
const char *name = clock_id_to_name(p->clock_id);
|
||||
if (p->clock_device[0])
|
||||
name = p->clock_device;
|
||||
if (p->clock_interface[0])
|
||||
name = p->clock_interface;
|
||||
spa_scnprintf(p->clock_name, sizeof(p->clock_name),
|
||||
"%s.%s", DEFAULT_CLOCK_PREFIX, name);
|
||||
}
|
||||
}
|
||||
|
||||
static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
|
||||
const struct spa_pod *param)
|
||||
{
|
||||
struct impl *this = object;
|
||||
|
||||
spa_return_val_if_fail(this != NULL, -EINVAL);
|
||||
|
||||
switch (id) {
|
||||
case SPA_PARAM_Props:
|
||||
{
|
||||
struct props *p = &this->props;
|
||||
bool notify = false;
|
||||
char buffer[CLOCK_NAME_MAX];
|
||||
int count;
|
||||
|
||||
if (param == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Note that the length passed to SPA_POD_OPT_Stringn() also
|
||||
* includes room for the null terminator, so the content of the
|
||||
* buffer variable is always guaranteed to be null terminated. */
|
||||
|
||||
spa_zero(buffer);
|
||||
count = spa_pod_parse_object(param,
|
||||
SPA_TYPE_OBJECT_Props, NULL,
|
||||
SPA_PROP_clockId, SPA_POD_OPT_Stringn(buffer, sizeof(buffer))
|
||||
);
|
||||
if (count && parse_clock_id(this, buffer))
|
||||
{
|
||||
reset_props_strings(p);
|
||||
notify = true;
|
||||
}
|
||||
|
||||
spa_zero(buffer);
|
||||
count = spa_pod_parse_object(param,
|
||||
SPA_TYPE_OBJECT_Props, NULL,
|
||||
SPA_PROP_clockDevice, SPA_POD_OPT_Stringn(buffer, sizeof(buffer))
|
||||
);
|
||||
if (count && parse_clock_device(this, buffer))
|
||||
{
|
||||
reset_props_strings(p);
|
||||
strncpy(p->clock_device, buffer, sizeof(p->clock_device));
|
||||
notify = true;
|
||||
}
|
||||
|
||||
spa_zero(buffer);
|
||||
count = spa_pod_parse_object(param,
|
||||
SPA_TYPE_OBJECT_Props, NULL,
|
||||
SPA_PROP_clockInterface, SPA_POD_OPT_Stringn(buffer, sizeof(buffer))
|
||||
);
|
||||
if (count && parse_clock_interface(this, buffer))
|
||||
{
|
||||
reset_props_strings(p);
|
||||
strncpy(p->clock_interface, buffer, sizeof(p->clock_interface));
|
||||
notify = true;
|
||||
}
|
||||
|
||||
if (notify)
|
||||
{
|
||||
ensure_clock_name(this);
|
||||
spa_log_info(this->log, "%p: setting clock to '%s'", this, p->clock_name);
|
||||
if (this->started) {
|
||||
do_stop(this);
|
||||
do_start(this);
|
||||
}
|
||||
emit_node_info(this, true);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return -ENOENT;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spa_node_methods impl_node = {
|
||||
SPA_VERSION_NODE_METHODS,
|
||||
.add_listener = impl_node_add_listener,
|
||||
.set_callbacks = impl_node_set_callbacks,
|
||||
.enum_params = impl_node_enum_params,
|
||||
.set_param = impl_node_set_param,
|
||||
.set_io = impl_node_set_io,
|
||||
.send_command = impl_node_send_command,
|
||||
.process = impl_node_process,
|
||||
|
|
@ -655,31 +941,6 @@ impl_get_size(const struct spa_handle_factory *factory,
|
|||
return sizeof(struct impl);
|
||||
}
|
||||
|
||||
static int get_phc_index(struct spa_system *s, const char *name) {
|
||||
#ifdef ETHTOOL_GET_TS_INFO
|
||||
struct ethtool_ts_info info = {0};
|
||||
struct ifreq ifr = {0};
|
||||
int fd, err;
|
||||
|
||||
info.cmd = ETHTOOL_GET_TS_INFO;
|
||||
strncpy(ifr.ifr_name, name, IFNAMSIZ - 1);
|
||||
ifr.ifr_data = (char *) &info;
|
||||
|
||||
fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
err = spa_system_ioctl(s, fd, SIOCETHTOOL, &ifr);
|
||||
close(fd);
|
||||
if (err < 0)
|
||||
return -errno;
|
||||
|
||||
return info.phc_index;
|
||||
#else
|
||||
return -ENOTSUP;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
impl_init(const struct spa_handle_factory *factory,
|
||||
struct spa_handle *handle,
|
||||
|
|
@ -727,9 +988,10 @@ impl_init(const struct spa_handle_factory *factory,
|
|||
this->info.max_input_ports = 0;
|
||||
this->info.max_output_ports = 0;
|
||||
this->info.flags = SPA_NODE_FLAG_RT;
|
||||
this->params[0] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE);
|
||||
this->params[NODE_PropInfo] = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ);
|
||||
this->params[NODE_Props] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE);
|
||||
this->info.params = this->params;
|
||||
this->info.n_params = 0;
|
||||
this->info.n_params = N_NODE_PARAMS;
|
||||
|
||||
reset_props(&this->props);
|
||||
|
||||
|
|
@ -742,37 +1004,17 @@ impl_init(const struct spa_handle_factory *factory,
|
|||
spa_scnprintf(this->props.clock_name,
|
||||
sizeof(this->props.clock_name), "%s", s);
|
||||
} else if (spa_streq(k, "clock.id") && this->clock_fd < 0) {
|
||||
this->props.clock_id = clock_name_to_id(s);
|
||||
if (this->props.clock_id == -1) {
|
||||
spa_log_warn(this->log, "unknown clock id '%s'", s);
|
||||
this->props.clock_id = DEFAULT_CLOCK_ID;
|
||||
}
|
||||
if (parse_clock_id(this, s))
|
||||
reset_props_strings(&this->props);
|
||||
} else if (spa_streq(k, "clock.device")) {
|
||||
if (this->clock_fd >= 0) {
|
||||
close(this->clock_fd);
|
||||
}
|
||||
this->clock_fd = open(s, O_RDONLY);
|
||||
|
||||
if (this->clock_fd == -1) {
|
||||
spa_log_warn(this->log, "failed to open clock device '%s': %m", s);
|
||||
} else {
|
||||
this->props.clock_id = FD_TO_CLOCKID(this->clock_fd);
|
||||
if (parse_clock_device(this, s)) {
|
||||
reset_props_strings(&this->props);
|
||||
strncpy(this->props.clock_device, s, sizeof(this->props.clock_device)-1);
|
||||
}
|
||||
} else if (spa_streq(k, "clock.interface") && this->clock_fd < 0) {
|
||||
int phc_index = get_phc_index(this->data_system, s);
|
||||
if (phc_index < 0) {
|
||||
spa_log_warn(this->log, "failed to get phc device index for interface '%s': %s",
|
||||
s, spa_strerror(phc_index));
|
||||
} else {
|
||||
char dev[19];
|
||||
spa_scnprintf(dev, sizeof(dev), "/dev/ptp%d", phc_index);
|
||||
this->clock_fd = open(dev, O_RDONLY);
|
||||
if (this->clock_fd == -1) {
|
||||
spa_log_warn(this->log, "failed to open clock device '%s' "
|
||||
"for interface '%s': %m", dev, s);
|
||||
} else {
|
||||
this->props.clock_id = FD_TO_CLOCKID(this->clock_fd);
|
||||
}
|
||||
if (parse_clock_interface(this, s)) {
|
||||
reset_props_strings(&this->props);
|
||||
strncpy(this->props.clock_interface, s, sizeof(this->props.clock_interface)-1);
|
||||
}
|
||||
} else if (spa_streq(k, "freewheel.wait")) {
|
||||
this->props.freewheel_wait = atoi(s);
|
||||
|
|
@ -785,6 +1027,7 @@ impl_init(const struct spa_handle_factory *factory,
|
|||
"%s.%s", DEFAULT_CLOCK_PREFIX,
|
||||
clock_id_to_name(this->props.clock_id));
|
||||
}
|
||||
ensure_clock_name(this);
|
||||
|
||||
this->tracking = !clock_for_timerfd(this->props.clock_id);
|
||||
this->timer_clockid = this->tracking ? CLOCK_MONOTONIC : this->props.clock_id;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue