2023-02-08 18:12:00 +01:00
|
|
|
/* PipeWire */
|
|
|
|
|
/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans */
|
|
|
|
|
/* SPDX-License-Identifier: MIT */
|
2022-03-15 18:16:00 +01:00
|
|
|
|
|
|
|
|
#include <linux/if_ether.h>
|
|
|
|
|
#include <linux/if_packet.h>
|
2022-04-01 21:28:43 +02:00
|
|
|
#include <linux/filter.h>
|
2022-03-15 18:16:00 +01:00
|
|
|
#include <linux/net_tstamp.h>
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
#include <net/if.h>
|
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
|
|
#include <spa/support/cpu.h>
|
|
|
|
|
#include <spa/debug/mem.h>
|
|
|
|
|
|
|
|
|
|
#include <pipewire/pipewire.h>
|
|
|
|
|
|
|
|
|
|
#include "avb.h"
|
|
|
|
|
#include "packets.h"
|
|
|
|
|
#include "internal.h"
|
2022-04-05 18:10:27 +02:00
|
|
|
#include "stream.h"
|
2022-03-18 18:02:18 +01:00
|
|
|
#include "acmp.h"
|
2022-03-15 18:16:00 +01:00
|
|
|
#include "adp.h"
|
2022-03-16 10:53:01 +01:00
|
|
|
#include "aecp.h"
|
2022-03-15 18:16:00 +01:00
|
|
|
#include "maap.h"
|
2022-03-22 19:40:23 +01:00
|
|
|
#include "mmrp.h"
|
|
|
|
|
#include "msrp.h"
|
|
|
|
|
#include "mvrp.h"
|
2022-03-17 19:13:43 +01:00
|
|
|
#include "descriptors.h"
|
2022-04-01 21:28:43 +02:00
|
|
|
#include "utils.h"
|
2022-03-15 18:16:00 +01:00
|
|
|
|
|
|
|
|
#define DEFAULT_INTERVAL 1
|
|
|
|
|
|
|
|
|
|
#define server_emit(s,m,v,...) spa_hook_list_call(&s->listener_list, struct server_events, m, v, ##__VA_ARGS__)
|
|
|
|
|
#define server_emit_destroy(s) server_emit(s, destroy, 0)
|
2022-03-17 19:13:43 +01:00
|
|
|
#define server_emit_message(s,n,m,l) server_emit(s, message, 0, n, m, l)
|
2022-03-15 18:16:00 +01:00
|
|
|
#define server_emit_periodic(s,n) server_emit(s, periodic, 0, n)
|
2022-03-18 15:18:11 +01:00
|
|
|
#define server_emit_command(s,n,c,a,f) server_emit(s, command, 0, n, c, a, f)
|
2022-03-15 18:16:00 +01:00
|
|
|
|
|
|
|
|
static void on_timer_event(void *data, uint64_t expirations)
|
|
|
|
|
{
|
|
|
|
|
struct server *server = data;
|
|
|
|
|
struct timespec now;
|
|
|
|
|
clock_gettime(CLOCK_REALTIME, &now);
|
|
|
|
|
server_emit_periodic(server, SPA_TIMESPEC_TO_NSEC(&now));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void on_socket_data(void *data, int fd, uint32_t mask)
|
|
|
|
|
{
|
|
|
|
|
struct server *server = data;
|
|
|
|
|
struct timespec now;
|
|
|
|
|
|
|
|
|
|
if (mask & SPA_IO_IN) {
|
2022-03-17 19:13:43 +01:00
|
|
|
int len;
|
2022-03-15 18:16:00 +01:00
|
|
|
uint8_t buffer[2048];
|
2022-03-16 17:34:27 +01:00
|
|
|
|
2022-03-17 19:13:43 +01:00
|
|
|
len = recv(fd, buffer, sizeof(buffer), 0);
|
2022-03-15 18:16:00 +01:00
|
|
|
|
|
|
|
|
if (len < 0) {
|
2022-03-16 17:34:27 +01:00
|
|
|
pw_log_warn("got recv error: %m");
|
2022-03-15 18:16:00 +01:00
|
|
|
}
|
2022-03-25 10:28:18 +01:00
|
|
|
else if (len < (int)sizeof(struct avb_packet_header)) {
|
2022-03-15 18:16:00 +01:00
|
|
|
pw_log_warn("short packet received (%d < %d)", len,
|
2022-03-25 10:28:18 +01:00
|
|
|
(int)sizeof(struct avb_packet_header));
|
2022-03-15 18:16:00 +01:00
|
|
|
} else {
|
|
|
|
|
clock_gettime(CLOCK_REALTIME, &now);
|
2022-03-17 19:13:43 +01:00
|
|
|
server_emit_message(server, SPA_TIMESPEC_TO_NSEC(&now), buffer, len);
|
2022-03-15 18:16:00 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-25 10:28:18 +01:00
|
|
|
int avb_server_send_packet(struct server *server, const uint8_t dest[6],
|
2022-03-22 19:40:23 +01:00
|
|
|
uint16_t type, void *data, size_t size)
|
2022-03-16 10:53:01 +01:00
|
|
|
{
|
2022-03-25 10:28:18 +01:00
|
|
|
struct avb_ethernet_header *hdr = (struct avb_ethernet_header*)data;
|
2022-03-16 17:34:27 +01:00
|
|
|
int res = 0;
|
2022-03-16 10:53:01 +01:00
|
|
|
|
2022-03-17 19:13:43 +01:00
|
|
|
memcpy(hdr->dest, dest, ETH_ALEN);
|
|
|
|
|
memcpy(hdr->src, server->mac_addr, ETH_ALEN);
|
2022-03-22 19:40:23 +01:00
|
|
|
hdr->type = htons(type);
|
2022-03-16 10:53:01 +01:00
|
|
|
|
2022-03-17 19:13:43 +01:00
|
|
|
if (send(server->source->fd, data, size, 0) < 0) {
|
2022-03-16 17:34:27 +01:00
|
|
|
res = -errno;
|
|
|
|
|
pw_log_warn("got send error: %m");
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-01 21:28:43 +02:00
|
|
|
static int load_filter(int fd, uint16_t eth, const uint8_t dest[6], const uint8_t mac[6])
|
|
|
|
|
{
|
|
|
|
|
struct sock_fprog filter;
|
|
|
|
|
struct sock_filter bpf_code[] = {
|
|
|
|
|
BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 12),
|
|
|
|
|
BPF_JUMP(BPF_JMP|BPF_JEQ, eth, 0, 8),
|
|
|
|
|
BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2),
|
|
|
|
|
BPF_JUMP(BPF_JMP|BPF_JEQ, (dest[2] << 24) |
|
|
|
|
|
(dest[3] << 16) |
|
|
|
|
|
(dest[4] << 8) |
|
|
|
|
|
(dest[5]), 0, 2),
|
|
|
|
|
BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 0),
|
|
|
|
|
BPF_JUMP(BPF_JMP|BPF_JEQ, (dest[0] << 8) |
|
|
|
|
|
(dest[1]), 3, 4),
|
|
|
|
|
BPF_JUMP(BPF_JMP|BPF_JEQ, (mac[2] << 24) |
|
|
|
|
|
(mac[3] << 16) |
|
|
|
|
|
(mac[4] << 8) |
|
|
|
|
|
(mac[5]), 0, 3),
|
|
|
|
|
BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 0),
|
|
|
|
|
BPF_JUMP(BPF_JMP|BPF_JEQ, (mac[0] << 8) |
|
|
|
|
|
(mac[1]), 0, 1),
|
|
|
|
|
BPF_STMT(BPF_RET, 0x00040000),
|
|
|
|
|
BPF_STMT(BPF_RET, 0x00000000),
|
|
|
|
|
};
|
|
|
|
|
filter.len = sizeof(bpf_code) / 8;
|
|
|
|
|
filter.filter = bpf_code;
|
|
|
|
|
|
|
|
|
|
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER,
|
|
|
|
|
&filter, sizeof(filter)) < 0) {
|
|
|
|
|
pw_log_error("setsockopt(ATTACH_FILTER) failed: %m");
|
|
|
|
|
return -errno;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-03 12:32:25 +02:00
|
|
|
int avb_server_make_socket(struct server *server, uint16_t type, const uint8_t mac[6])
|
2022-03-15 18:16:00 +01:00
|
|
|
{
|
2022-03-16 10:53:01 +01:00
|
|
|
int fd, res;
|
2022-03-15 18:16:00 +01:00
|
|
|
struct ifreq req;
|
|
|
|
|
struct packet_mreq mreq;
|
|
|
|
|
struct sockaddr_ll sll;
|
|
|
|
|
|
2022-03-17 19:13:43 +01:00
|
|
|
fd = socket(AF_PACKET, SOCK_RAW|SOCK_NONBLOCK, htons(ETH_P_ALL));
|
2022-03-15 18:16:00 +01:00
|
|
|
if (fd < 0) {
|
|
|
|
|
pw_log_error("socket() failed: %m");
|
|
|
|
|
return -errno;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
spa_zero(req);
|
|
|
|
|
snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", server->ifname);
|
|
|
|
|
if (ioctl(fd, SIOCGIFINDEX, &req) < 0) {
|
|
|
|
|
res = -errno;
|
|
|
|
|
pw_log_error("SIOCGIFINDEX %s failed: %m", server->ifname);
|
|
|
|
|
goto error_close;
|
|
|
|
|
}
|
2022-03-16 10:53:01 +01:00
|
|
|
server->ifindex = req.ifr_ifindex;
|
2022-03-15 18:16:00 +01:00
|
|
|
|
|
|
|
|
spa_zero(req);
|
|
|
|
|
snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", server->ifname);
|
|
|
|
|
if (ioctl(fd, SIOCGIFHWADDR, &req) < 0) {
|
|
|
|
|
res = -errno;
|
|
|
|
|
pw_log_error("SIOCGIFHWADDR %s failed: %m", server->ifname);
|
|
|
|
|
goto error_close;
|
|
|
|
|
}
|
|
|
|
|
memcpy(server->mac_addr, req.ifr_hwaddr.sa_data, sizeof(server->mac_addr));
|
|
|
|
|
|
2022-03-16 10:53:01 +01:00
|
|
|
server->entity_id = (uint64_t)server->mac_addr[0] << 56 |
|
|
|
|
|
(uint64_t)server->mac_addr[1] << 48 |
|
|
|
|
|
(uint64_t)server->mac_addr[2] << 40 |
|
|
|
|
|
(uint64_t)0xff << 32 |
|
|
|
|
|
(uint64_t)0xfe << 24 |
|
|
|
|
|
(uint64_t)server->mac_addr[3] << 16 |
|
|
|
|
|
(uint64_t)server->mac_addr[4] << 8 |
|
|
|
|
|
(uint64_t)server->mac_addr[5];
|
|
|
|
|
|
2022-04-01 21:28:43 +02:00
|
|
|
spa_zero(sll);
|
|
|
|
|
sll.sll_family = AF_PACKET;
|
|
|
|
|
sll.sll_protocol = htons(ETH_P_ALL);
|
|
|
|
|
sll.sll_ifindex = server->ifindex;
|
|
|
|
|
if (bind(fd, (struct sockaddr *) &sll, sizeof(sll)) < 0) {
|
|
|
|
|
res = -errno;
|
|
|
|
|
pw_log_error("bind() failed: %m");
|
|
|
|
|
goto error_close;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-16 17:34:27 +01:00
|
|
|
spa_zero(mreq);
|
|
|
|
|
mreq.mr_ifindex = server->ifindex;
|
2022-04-01 21:28:43 +02:00
|
|
|
mreq.mr_type = PACKET_MR_MULTICAST;
|
|
|
|
|
mreq.mr_alen = ETH_ALEN;
|
2022-04-03 12:32:25 +02:00
|
|
|
memcpy(mreq.mr_address, mac, ETH_ALEN);
|
2022-04-01 21:28:43 +02:00
|
|
|
|
2022-03-16 17:34:27 +01:00
|
|
|
if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
|
|
|
|
|
&mreq, sizeof(mreq)) < 0) {
|
|
|
|
|
res = -errno;
|
|
|
|
|
pw_log_error("setsockopt(ADD_MEMBERSHIP) failed: %m");
|
|
|
|
|
goto error_close;
|
|
|
|
|
}
|
2022-03-16 10:53:01 +01:00
|
|
|
|
2022-04-03 12:32:25 +02:00
|
|
|
if ((res = load_filter(fd, type, mac, server->mac_addr)) < 0)
|
2022-03-15 18:16:00 +01:00
|
|
|
goto error_close;
|
|
|
|
|
|
2022-04-03 12:32:25 +02:00
|
|
|
return fd;
|
|
|
|
|
|
|
|
|
|
error_close:
|
|
|
|
|
close(fd);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int setup_socket(struct server *server)
|
|
|
|
|
{
|
|
|
|
|
struct impl *impl = server->impl;
|
|
|
|
|
int fd, res;
|
|
|
|
|
static const uint8_t bmac[6] = AVB_BROADCAST_MAC;
|
|
|
|
|
struct timespec value, interval;
|
|
|
|
|
|
|
|
|
|
fd = avb_server_make_socket(server, AVB_TSN_ETH, bmac);
|
|
|
|
|
if (fd < 0)
|
|
|
|
|
return fd;
|
|
|
|
|
|
2022-07-18 15:39:06 +02:00
|
|
|
pw_log_info("0x%"PRIx64" %d", server->entity_id, server->ifindex);
|
2022-04-03 12:32:25 +02:00
|
|
|
|
2022-03-15 18:16:00 +01:00
|
|
|
server->source = pw_loop_add_io(impl->loop, fd, SPA_IO_IN, true, on_socket_data, server);
|
|
|
|
|
if (server->source == NULL) {
|
|
|
|
|
res = -errno;
|
|
|
|
|
pw_log_error("server %p: can't create server source: %m", impl);
|
2022-04-03 12:32:25 +02:00
|
|
|
goto error_no_source;
|
2022-03-15 18:16:00 +01:00
|
|
|
}
|
|
|
|
|
server->timer = pw_loop_add_timer(impl->loop, on_timer_event, server);
|
|
|
|
|
if (server->timer == NULL) {
|
|
|
|
|
res = -errno;
|
|
|
|
|
pw_log_error("server %p: can't create timer source: %m", impl);
|
2022-04-03 12:32:25 +02:00
|
|
|
goto error_no_timer;
|
2022-03-15 18:16:00 +01:00
|
|
|
}
|
|
|
|
|
value.tv_sec = 0;
|
2022-03-22 19:40:23 +01:00
|
|
|
value.tv_nsec = 1;
|
2022-03-15 18:16:00 +01:00
|
|
|
interval.tv_sec = DEFAULT_INTERVAL;
|
2022-03-22 19:40:23 +01:00
|
|
|
interval.tv_nsec = 0;
|
|
|
|
|
pw_loop_update_timer(impl->loop, server->timer, &value, &interval, false);
|
2022-03-15 18:16:00 +01:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
2022-04-03 12:32:25 +02:00
|
|
|
error_no_timer:
|
|
|
|
|
pw_loop_destroy_source(impl->loop, server->source);
|
|
|
|
|
server->source = NULL;
|
|
|
|
|
error_no_source:
|
2022-03-15 18:16:00 +01:00
|
|
|
close(fd);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-07 15:14:45 +02:00
|
|
|
struct server *avdecc_server_new(struct impl *impl, struct spa_dict *props)
|
2022-03-15 18:16:00 +01:00
|
|
|
{
|
|
|
|
|
struct server *server;
|
2023-03-16 12:54:50 +01:00
|
|
|
const char *str;
|
2022-03-15 18:16:00 +01:00
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
|
|
server = calloc(1, sizeof(*server));
|
|
|
|
|
if (server == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
server->impl = impl;
|
|
|
|
|
spa_list_append(&impl->servers, &server->link);
|
2023-03-16 12:54:50 +01:00
|
|
|
str = spa_dict_lookup(props, "ifname");
|
|
|
|
|
server->ifname = str ? strdup(str) : NULL;
|
2022-03-15 18:16:00 +01:00
|
|
|
spa_hook_list_init(&server->listener_list);
|
2022-03-18 15:18:11 +01:00
|
|
|
spa_list_init(&server->descriptors);
|
2022-04-05 18:10:27 +02:00
|
|
|
spa_list_init(&server->streams);
|
2022-03-15 18:16:00 +01:00
|
|
|
|
2022-03-18 15:18:11 +01:00
|
|
|
server->debug_messages = false;
|
2022-03-15 18:16:00 +01:00
|
|
|
|
|
|
|
|
if ((res = setup_socket(server)) < 0)
|
|
|
|
|
goto error_free;
|
|
|
|
|
|
2022-03-17 19:13:43 +01:00
|
|
|
init_descriptors(server);
|
|
|
|
|
|
2022-03-25 10:28:18 +01:00
|
|
|
server->mrp = avb_mrp_new(server);
|
2022-03-23 19:57:25 +01:00
|
|
|
if (server->mrp == NULL)
|
|
|
|
|
goto error_free;
|
2022-03-22 19:40:23 +01:00
|
|
|
|
2022-03-25 10:28:18 +01:00
|
|
|
avb_aecp_register(server);
|
2022-04-06 12:58:47 +02:00
|
|
|
server->maap = avb_maap_register(server);
|
2022-03-25 10:28:18 +01:00
|
|
|
server->mmrp = avb_mmrp_register(server);
|
|
|
|
|
server->msrp = avb_msrp_register(server);
|
|
|
|
|
server->mvrp = avb_mvrp_register(server);
|
|
|
|
|
avb_adp_register(server);
|
|
|
|
|
avb_acmp_register(server);
|
2022-03-15 18:16:00 +01:00
|
|
|
|
2022-03-25 10:28:18 +01:00
|
|
|
server->domain_attr = avb_msrp_attribute_new(server->msrp,
|
|
|
|
|
AVB_MSRP_ATTRIBUTE_TYPE_DOMAIN);
|
2022-04-07 15:14:45 +02:00
|
|
|
server->domain_attr->attr.domain.sr_class_id = AVB_MSRP_CLASS_ID_DEFAULT;
|
|
|
|
|
server->domain_attr->attr.domain.sr_class_priority = AVB_MSRP_PRIORITY_DEFAULT;
|
|
|
|
|
server->domain_attr->attr.domain.sr_class_vid = htons(AVB_DEFAULT_VLAN);
|
2022-03-23 19:57:25 +01:00
|
|
|
|
2022-03-25 17:01:51 +01:00
|
|
|
avb_mrp_attribute_begin(server->domain_attr->mrp, 0);
|
|
|
|
|
avb_mrp_attribute_join(server->domain_attr->mrp, 0, true);
|
2022-03-23 19:57:25 +01:00
|
|
|
|
2022-04-05 18:10:27 +02:00
|
|
|
server_create_stream(server, SPA_DIRECTION_INPUT, 0);
|
|
|
|
|
server_create_stream(server, SPA_DIRECTION_OUTPUT, 0);
|
2022-03-24 17:00:40 +01:00
|
|
|
|
2022-04-06 12:58:47 +02:00
|
|
|
avb_maap_reserve(server->maap, 1);
|
|
|
|
|
|
2022-03-15 18:16:00 +01:00
|
|
|
return server;
|
|
|
|
|
|
|
|
|
|
error_free:
|
|
|
|
|
free(server);
|
|
|
|
|
if (res < 0)
|
|
|
|
|
errno = -res;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-22 19:40:23 +01:00
|
|
|
void avdecc_server_add_listener(struct server *server, struct spa_hook *listener,
|
|
|
|
|
const struct server_events *events, void *data)
|
2022-03-15 18:16:00 +01:00
|
|
|
{
|
|
|
|
|
spa_hook_list_append(&server->listener_list, listener, events, data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void avdecc_server_free(struct server *server)
|
|
|
|
|
{
|
|
|
|
|
struct impl *impl = server->impl;
|
|
|
|
|
|
|
|
|
|
spa_list_remove(&server->link);
|
|
|
|
|
if (server->source)
|
|
|
|
|
pw_loop_destroy_source(impl->loop, server->source);
|
|
|
|
|
if (server->timer)
|
2023-03-16 12:55:10 +01:00
|
|
|
pw_loop_destroy_source(impl->loop, server->timer);
|
2022-03-15 18:16:00 +01:00
|
|
|
spa_hook_list_clean(&server->listener_list);
|
|
|
|
|
free(server);
|
|
|
|
|
}
|