mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-04-09 08:21:08 -04:00
test: add AVB protocol test suite with loopback transport
Add a test suite for the AVB (Audio Video Bridging) protocol stack that runs entirely in software, requiring no hardware, root privileges, or running PipeWire daemon. The loopback transport (avb-transport-loopback.h) replaces raw AF_PACKET sockets with in-memory packet capture, using a synthetic MAC address and eventfd for protocol handlers that need a valid fd. Test utilities (test-avb-utils.h) provide helpers for creating test servers, injecting packets, advancing time, and building ADP packets. Tests cover: - ADP entity available/departing/discover/timeout - MRP attribute lifecycle (create, begin, join) - Milan v1.2 mode server creation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a73988d38d
commit
ef4ff8cfd0
4 changed files with 730 additions and 0 deletions
149
src/modules/module-avb/avb-transport-loopback.h
Normal file
149
src/modules/module-avb/avb-transport-loopback.h
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
/* AVB support */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2026 PipeWire contributors */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef AVB_TRANSPORT_LOOPBACK_H
|
||||
#define AVB_TRANSPORT_LOOPBACK_H
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/eventfd.h>
|
||||
|
||||
#include "internal.h"
|
||||
#include "packets.h"
|
||||
|
||||
#define AVB_LOOPBACK_MAX_PACKETS 64
|
||||
#define AVB_LOOPBACK_MAX_PACKET_SIZE 2048
|
||||
|
||||
struct avb_loopback_packet {
|
||||
uint8_t dest[6];
|
||||
uint16_t type;
|
||||
size_t size;
|
||||
uint8_t data[AVB_LOOPBACK_MAX_PACKET_SIZE];
|
||||
};
|
||||
|
||||
struct avb_loopback_transport {
|
||||
struct avb_loopback_packet packets[AVB_LOOPBACK_MAX_PACKETS];
|
||||
int packet_count;
|
||||
int packet_read;
|
||||
};
|
||||
|
||||
static inline int avb_loopback_setup(struct server *server)
|
||||
{
|
||||
struct avb_loopback_transport *t;
|
||||
static const uint8_t test_mac[6] = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x01 };
|
||||
|
||||
t = calloc(1, sizeof(*t));
|
||||
if (t == NULL)
|
||||
return -errno;
|
||||
|
||||
server->transport_data = t;
|
||||
|
||||
memcpy(server->mac_addr, test_mac, 6);
|
||||
server->ifindex = 1;
|
||||
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];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int avb_loopback_send_packet(struct server *server,
|
||||
const uint8_t dest[6], uint16_t type, void *data, size_t size)
|
||||
{
|
||||
struct avb_loopback_transport *t = server->transport_data;
|
||||
struct avb_loopback_packet *pkt;
|
||||
struct avb_ethernet_header *hdr = (struct avb_ethernet_header*)data;
|
||||
|
||||
if (t->packet_count >= AVB_LOOPBACK_MAX_PACKETS)
|
||||
return -ENOSPC;
|
||||
if (size > AVB_LOOPBACK_MAX_PACKET_SIZE)
|
||||
return -EMSGSIZE;
|
||||
|
||||
/* Fill in the ethernet header like the raw transport does */
|
||||
memcpy(hdr->dest, dest, 6);
|
||||
memcpy(hdr->src, server->mac_addr, 6);
|
||||
hdr->type = htons(type);
|
||||
|
||||
pkt = &t->packets[t->packet_count % AVB_LOOPBACK_MAX_PACKETS];
|
||||
memcpy(pkt->dest, dest, 6);
|
||||
pkt->type = type;
|
||||
pkt->size = size;
|
||||
memcpy(pkt->data, data, size);
|
||||
t->packet_count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a dummy fd for protocol handlers that create their own sockets.
|
||||
* Uses eventfd so pw_loop_add_io() has a valid fd to work with.
|
||||
*/
|
||||
static inline int avb_loopback_make_socket(struct server *server,
|
||||
uint16_t type, const uint8_t mac[6])
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static inline void avb_loopback_destroy(struct server *server)
|
||||
{
|
||||
free(server->transport_data);
|
||||
server->transport_data = NULL;
|
||||
}
|
||||
|
||||
static const struct avb_transport_ops avb_transport_loopback = {
|
||||
.setup = avb_loopback_setup,
|
||||
.send_packet = avb_loopback_send_packet,
|
||||
.make_socket = avb_loopback_make_socket,
|
||||
.destroy = avb_loopback_destroy,
|
||||
};
|
||||
|
||||
/** Get the number of captured sent packets */
|
||||
static inline int avb_loopback_get_packet_count(struct server *server)
|
||||
{
|
||||
struct avb_loopback_transport *t = server->transport_data;
|
||||
return t->packet_count - t->packet_read;
|
||||
}
|
||||
|
||||
/** Read the next captured sent packet, returns packet size or -1 */
|
||||
static inline int avb_loopback_get_packet(struct server *server,
|
||||
void *buf, size_t bufsize)
|
||||
{
|
||||
struct avb_loopback_transport *t = server->transport_data;
|
||||
struct avb_loopback_packet *pkt;
|
||||
|
||||
if (t->packet_read >= t->packet_count)
|
||||
return -1;
|
||||
|
||||
pkt = &t->packets[t->packet_read % AVB_LOOPBACK_MAX_PACKETS];
|
||||
t->packet_read++;
|
||||
|
||||
if (pkt->size > bufsize)
|
||||
return -1;
|
||||
|
||||
memcpy(buf, pkt->data, pkt->size);
|
||||
return pkt->size;
|
||||
}
|
||||
|
||||
/** Clear all captured packets */
|
||||
static inline void avb_loopback_clear_packets(struct server *server)
|
||||
{
|
||||
struct avb_loopback_transport *t = server->transport_data;
|
||||
t->packet_count = 0;
|
||||
t->packet_read = 0;
|
||||
}
|
||||
|
||||
#endif /* AVB_TRANSPORT_LOOPBACK_H */
|
||||
Loading…
Add table
Add a link
Reference in a new issue