mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-02 09:01:50 -05:00
avb: add beginnings of MRP/MMRP/MSRP/MVRP/SRP
This commit is contained in:
parent
911378651b
commit
893251c8eb
19 changed files with 1034 additions and 42 deletions
|
|
@ -528,7 +528,13 @@ pipewire_module_avbtp = shared_library('pipewire-module-avbtp',
|
|||
'module-avbtp/aecp.c',
|
||||
'module-avbtp/aecp-aem.c',
|
||||
'module-avbtp/avdecc.c',
|
||||
'module-avbtp/maap.c' ],
|
||||
'module-avbtp/maap.c',
|
||||
'module-avbtp/mmrp.c',
|
||||
'module-avbtp/mrp.c',
|
||||
'module-avbtp/msrp.c',
|
||||
'module-avbtp/mvrp.c',
|
||||
'module-avbtp/srp.c'
|
||||
],
|
||||
include_directories : [configinc],
|
||||
install : true,
|
||||
install_dir : modules_install_dir,
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@
|
|||
#include "acmp.h"
|
||||
#include "internal.h"
|
||||
|
||||
static const uint8_t mac[6] = AVB_BROADCAST_MAC;
|
||||
|
||||
struct pending {
|
||||
struct spa_list link;
|
||||
uint64_t last_time;
|
||||
|
|
@ -104,7 +106,8 @@ static int reply_not_supported(struct acmp *acmp, const void *m, int len)
|
|||
memcpy(reply, m, len);
|
||||
AVBTP_PACKET_ACMP_SET_STATUS(reply, AVBTP_ACMP_STATUS_NOT_SUPPORTED);
|
||||
|
||||
return avbtp_server_send_packet(server, reply->hdr.eth.src, reply, len);
|
||||
return avbtp_server_send_packet(server, reply->hdr.eth.src,
|
||||
AVB_TSN_ETH, reply, len);
|
||||
}
|
||||
|
||||
static int retry_pending(struct acmp *acmp, uint64_t now, struct pending *p)
|
||||
|
|
@ -113,7 +116,8 @@ static int retry_pending(struct acmp *acmp, uint64_t now, struct pending *p)
|
|||
struct avbtp_packet_acmp *cmd = p->ptr;
|
||||
p->retry++;
|
||||
p->last_time = now;
|
||||
return avbtp_server_send_packet(server, cmd->hdr.eth.dest, cmd, p->size);
|
||||
return avbtp_server_send_packet(server, cmd->hdr.eth.dest,
|
||||
AVB_TSN_ETH, cmd, p->size);
|
||||
}
|
||||
|
||||
static int handle_connect_tx_command(struct acmp *acmp, uint64_t now, const void *m, int len)
|
||||
|
|
@ -130,7 +134,8 @@ static int handle_connect_tx_command(struct acmp *acmp, uint64_t now, const void
|
|||
AVBTP_PACKET_ACMP_SET_MESSAGE_TYPE(reply, AVBTP_ACMP_MESSAGE_TYPE_CONNECT_TX_RESPONSE);
|
||||
AVBTP_PACKET_ACMP_SET_STATUS(reply, AVBTP_ACMP_STATUS_SUCCESS);
|
||||
|
||||
return avbtp_server_send_packet(server, reply->hdr.eth.dest, reply, len);
|
||||
return avbtp_server_send_packet(server, reply->hdr.eth.dest,
|
||||
AVB_TSN_ETH, reply, len);
|
||||
}
|
||||
|
||||
static int handle_connect_tx_response(struct acmp *acmp, uint64_t now, const void *m, int len)
|
||||
|
|
@ -156,7 +161,8 @@ static int handle_connect_tx_response(struct acmp *acmp, uint64_t now, const voi
|
|||
reply->sequence_id = htons(pending->old_sequence_id);
|
||||
AVBTP_PACKET_ACMP_SET_MESSAGE_TYPE(reply, AVBTP_ACMP_MESSAGE_TYPE_CONNECT_RX_RESPONSE);
|
||||
|
||||
res = avbtp_server_send_packet(server, reply->hdr.eth.dest, reply, pending->size);
|
||||
res = avbtp_server_send_packet(server, reply->hdr.eth.dest,
|
||||
AVB_TSN_ETH, reply, pending->size);
|
||||
|
||||
pending_free(acmp, pending);
|
||||
|
||||
|
|
@ -177,7 +183,8 @@ static int handle_disconnect_tx_command(struct acmp *acmp, uint64_t now, const v
|
|||
AVBTP_PACKET_ACMP_SET_MESSAGE_TYPE(reply, AVBTP_ACMP_MESSAGE_TYPE_DISCONNECT_TX_RESPONSE);
|
||||
AVBTP_PACKET_ACMP_SET_STATUS(reply, AVBTP_ACMP_STATUS_SUCCESS);
|
||||
|
||||
return avbtp_server_send_packet(server, reply->hdr.eth.dest, reply, len);
|
||||
return avbtp_server_send_packet(server, reply->hdr.eth.dest,
|
||||
AVB_TSN_ETH, reply, len);
|
||||
}
|
||||
|
||||
static int handle_disconnect_tx_response(struct acmp *acmp, uint64_t now, const void *m, int len)
|
||||
|
|
@ -203,7 +210,8 @@ static int handle_disconnect_tx_response(struct acmp *acmp, uint64_t now, const
|
|||
reply->sequence_id = htons(pending->old_sequence_id);
|
||||
AVBTP_PACKET_ACMP_SET_MESSAGE_TYPE(reply, AVBTP_ACMP_MESSAGE_TYPE_DISCONNECT_RX_RESPONSE);
|
||||
|
||||
res = avbtp_server_send_packet(server, reply->hdr.eth.dest, reply, pending->size);
|
||||
res = avbtp_server_send_packet(server, reply->hdr.eth.dest,
|
||||
AVB_TSN_ETH, reply, pending->size);
|
||||
|
||||
pending_free(acmp, pending);
|
||||
|
||||
|
|
@ -227,7 +235,8 @@ static int handle_connect_rx_command(struct acmp *acmp, uint64_t now, const void
|
|||
AVBTP_PACKET_ACMP_SET_MESSAGE_TYPE(cmd, AVBTP_ACMP_MESSAGE_TYPE_CONNECT_TX_COMMAND);
|
||||
AVBTP_PACKET_ACMP_SET_STATUS(cmd, AVBTP_ACMP_STATUS_SUCCESS);
|
||||
|
||||
return avbtp_server_send_packet(server, cmd->hdr.eth.dest, cmd, len);
|
||||
return avbtp_server_send_packet(server, cmd->hdr.eth.dest,
|
||||
AVB_TSN_ETH, cmd, len);
|
||||
}
|
||||
|
||||
static int handle_connect_rx_response(struct acmp *acmp, uint64_t now, const void *m, int len)
|
||||
|
|
@ -252,7 +261,8 @@ static int handle_disconnect_rx_command(struct acmp *acmp, uint64_t now, const v
|
|||
AVBTP_PACKET_ACMP_SET_MESSAGE_TYPE(cmd, AVBTP_ACMP_MESSAGE_TYPE_DISCONNECT_TX_COMMAND);
|
||||
AVBTP_PACKET_ACMP_SET_STATUS(cmd, AVBTP_ACMP_STATUS_SUCCESS);
|
||||
|
||||
return avbtp_server_send_packet(server, cmd->hdr.eth.dest, cmd, len);
|
||||
return avbtp_server_send_packet(server, cmd->hdr.eth.dest,
|
||||
AVB_TSN_ETH, cmd, len);
|
||||
}
|
||||
|
||||
static int handle_disconnect_rx_response(struct acmp *acmp, uint64_t now, const void *p, int len)
|
||||
|
|
@ -291,10 +301,17 @@ static inline const struct msg_info *find_msg_info(uint16_t type, const char *na
|
|||
static int acmp_message(void *data, uint64_t now, const void *message, int len)
|
||||
{
|
||||
struct acmp *acmp = data;
|
||||
struct server *server = acmp->server;
|
||||
const struct avbtp_packet_acmp *p = message;
|
||||
const struct msg_info *info;
|
||||
int message_type;
|
||||
|
||||
if (ntohs(p->hdr.eth.type) != AVB_TSN_ETH)
|
||||
return 0;
|
||||
if (memcmp(p->hdr.eth.dest, mac, 6) != 0 &&
|
||||
memcmp(p->hdr.eth.dest, server->mac_addr, 6) != 0)
|
||||
return 0;
|
||||
|
||||
if (AVBTP_PACKET_GET_SUBTYPE(&p->hdr) != AVBTP_SUBTYPE_ACMP)
|
||||
return 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@
|
|||
#include "internal.h"
|
||||
#include "utils.h"
|
||||
|
||||
static const uint8_t mac[6] = AVB_BROADCAST_MAC;
|
||||
|
||||
struct entity {
|
||||
struct spa_list link;
|
||||
struct avbtp_packet_adp packet;
|
||||
|
|
@ -64,7 +66,7 @@ static int send_departing(struct adp *adp, uint64_t now, struct entity *e)
|
|||
{
|
||||
AVBTP_PACKET_ADP_SET_MESSAGE_TYPE(&e->packet, AVBTP_ADP_MESSAGE_TYPE_ENTITY_DEPARTING);
|
||||
e->packet.available_index = htonl(adp->available_index++);
|
||||
avbtp_server_broadcast_packet(adp->server, &e->packet, sizeof(e->packet));
|
||||
avbtp_server_send_packet(adp->server, mac, AVB_TSN_ETH, &e->packet, sizeof(e->packet));
|
||||
e->last_time = now;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -73,7 +75,7 @@ static int send_advertise(struct adp *adp, uint64_t now, struct entity *e)
|
|||
{
|
||||
AVBTP_PACKET_ADP_SET_MESSAGE_TYPE(&e->packet, AVBTP_ADP_MESSAGE_TYPE_ENTITY_AVAILABLE);
|
||||
e->packet.available_index = htonl(adp->available_index++);
|
||||
avbtp_server_broadcast_packet(adp->server, &e->packet, sizeof(e->packet));
|
||||
avbtp_server_send_packet(adp->server, mac, AVB_TSN_ETH, &e->packet, sizeof(e->packet));
|
||||
e->last_time = now;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -86,19 +88,26 @@ static int send_discover(struct adp *adp, uint64_t entity_id)
|
|||
AVBTP_PACKET_SET_LENGTH(&p.hdr, AVBTP_ADP_CONTROL_DATA_LENGTH);
|
||||
AVBTP_PACKET_ADP_SET_MESSAGE_TYPE(&p, AVBTP_ADP_MESSAGE_TYPE_ENTITY_DISCOVER);
|
||||
p.entity_id = htonl(entity_id);
|
||||
avbtp_server_broadcast_packet(adp->server, &p, sizeof(p));
|
||||
avbtp_server_send_packet(adp->server, mac, AVB_TSN_ETH, &p, sizeof(p));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adp_message(void *data, uint64_t now, const void *message, int len)
|
||||
{
|
||||
struct adp *adp = data;
|
||||
struct server *server = adp->server;
|
||||
const struct avbtp_packet_adp *p = message;
|
||||
struct entity *e;
|
||||
int message_type;
|
||||
char buf[128];
|
||||
uint64_t entity_id;
|
||||
|
||||
if (ntohs(p->hdr.eth.type) != AVB_TSN_ETH)
|
||||
return 0;
|
||||
if (memcmp(p->hdr.eth.dest, mac, 6) != 0 &&
|
||||
memcmp(p->hdr.eth.dest, server->mac_addr, 6) != 0)
|
||||
return 0;
|
||||
|
||||
if (AVBTP_PACKET_GET_SUBTYPE(&p->hdr) != AVBTP_SUBTYPE_ADP ||
|
||||
AVBTP_PACKET_GET_LENGTH(&p->hdr) < AVBTP_ADP_CONTROL_DATA_LENGTH)
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ static int reply_status(struct aecp *aecp, int status, const void *m, int len)
|
|||
AVBTP_PACKET_AECP_SET_MESSAGE_TYPE(reply, AVBTP_AECP_MESSAGE_TYPE_AEM_RESPONSE);
|
||||
AVBTP_PACKET_AECP_SET_STATUS(reply, status);
|
||||
|
||||
return avbtp_server_send_packet(server, reply->hdr.eth.src, reply, len);
|
||||
return avbtp_server_send_packet(server, reply->hdr.eth.src,
|
||||
AVB_TSN_ETH, reply, len);
|
||||
}
|
||||
|
||||
static int reply_not_implemented(struct aecp *aecp, const void *m, int len)
|
||||
|
|
@ -133,7 +134,8 @@ static int handle_read_descriptor(struct aecp *aecp, const void *m, int len)
|
|||
AVBTP_PACKET_AECP_SET_STATUS(&reply->aecp, AVBTP_AECP_AEM_STATUS_SUCCESS);
|
||||
AVBTP_PACKET_SET_LENGTH(&reply->aecp.hdr, psize + 12);
|
||||
|
||||
return avbtp_server_send_packet(server, reply->aecp.hdr.eth.src, reply, size);
|
||||
return avbtp_server_send_packet(server, reply->aecp.hdr.eth.src,
|
||||
AVB_TSN_ETH, reply, size);
|
||||
}
|
||||
|
||||
/* GET_AVB_INFO */
|
||||
|
|
@ -180,7 +182,8 @@ static int handle_get_avb_info(struct aecp *aecp, const void *m, int len)
|
|||
i->flags = 0;
|
||||
i->msrp_mappings_count = htons(0);
|
||||
|
||||
return avbtp_server_send_packet(server, reply->aecp.hdr.eth.src, reply, size);
|
||||
return avbtp_server_send_packet(server, reply->aecp.hdr.eth.src,
|
||||
AVB_TSN_ETH, reply, size);
|
||||
}
|
||||
|
||||
/* AEM_COMMAND */
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@
|
|||
#include "aecp-aem.h"
|
||||
#include "internal.h"
|
||||
|
||||
static const uint8_t mac[6] = AVB_BROADCAST_MAC;
|
||||
|
||||
struct msg_info {
|
||||
uint16_t type;
|
||||
const char *name;
|
||||
|
|
@ -46,7 +48,8 @@ static int reply_not_implemented(struct aecp *aecp, const void *p, int len)
|
|||
memcpy(reply, p, len);
|
||||
AVBTP_PACKET_AECP_SET_STATUS(reply, AVBTP_AECP_STATUS_NOT_IMPLEMENTED);
|
||||
|
||||
return avbtp_server_send_packet(server, reply->hdr.eth.src, reply, len);
|
||||
return avbtp_server_send_packet(server, reply->hdr.eth.src,
|
||||
AVB_TSN_ETH, reply, len);
|
||||
}
|
||||
|
||||
static const struct msg_info msg_info[] = {
|
||||
|
|
@ -76,10 +79,16 @@ static inline const struct msg_info *find_msg_info(uint16_t type, const char *na
|
|||
static int aecp_message(void *data, uint64_t now, const void *message, int len)
|
||||
{
|
||||
struct aecp *aecp = data;
|
||||
struct server *server = aecp->server;
|
||||
const struct avbtp_packet_aecp_header *p = message;
|
||||
const struct msg_info *info;
|
||||
int message_type;
|
||||
|
||||
if (ntohs(p->hdr.eth.type) != AVB_TSN_ETH)
|
||||
return 0;
|
||||
if (memcmp(p->hdr.eth.dest, mac, 6) != 0 &&
|
||||
memcmp(p->hdr.eth.dest, server->mac_addr, 6) != 0)
|
||||
return 0;
|
||||
if (AVBTP_PACKET_GET_SUBTYPE(&p->hdr) != AVBTP_SUBTYPE_AECP)
|
||||
return 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -43,12 +43,13 @@
|
|||
#include "adp.h"
|
||||
#include "aecp.h"
|
||||
#include "maap.h"
|
||||
#include "mmrp.h"
|
||||
#include "msrp.h"
|
||||
#include "mvrp.h"
|
||||
#include "descriptors.h"
|
||||
|
||||
#define DEFAULT_INTERVAL 1
|
||||
|
||||
static const uint8_t AVB_MAC_BROADCAST[6] = { 0x91, 0xe0, 0xf0, 0x01, 0x00, 0x00 };
|
||||
|
||||
#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)
|
||||
#define server_emit_message(s,n,m,l) server_emit(s, message, 0, n, m, l)
|
||||
|
|
@ -81,28 +82,21 @@ static void on_socket_data(void *data, int fd, uint32_t mask)
|
|||
pw_log_warn("short packet received (%d < %d)", len,
|
||||
(int)sizeof(struct avbtp_packet_header));
|
||||
} else {
|
||||
struct avbtp_ethernet_header *hdr = (struct avbtp_ethernet_header*)buffer;
|
||||
|
||||
if (htons(hdr->type) != ETH_P_TSN)
|
||||
return;
|
||||
if (memcmp(hdr->dest, AVB_MAC_BROADCAST, ETH_ALEN) != 0 &&
|
||||
memcmp(hdr->dest, server->mac_addr, ETH_ALEN) != 0)
|
||||
return;
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
server_emit_message(server, SPA_TIMESPEC_TO_NSEC(&now), buffer, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int avbtp_server_send_packet(struct server *server, const uint8_t dest[6], void *data, size_t size)
|
||||
int avbtp_server_send_packet(struct server *server, const uint8_t dest[6],
|
||||
uint16_t type, void *data, size_t size)
|
||||
{
|
||||
struct avbtp_ethernet_header *hdr = (struct avbtp_ethernet_header*)data;
|
||||
int res = 0;
|
||||
|
||||
memcpy(hdr->dest, dest, ETH_ALEN);
|
||||
memcpy(hdr->src, server->mac_addr, ETH_ALEN);
|
||||
hdr->type = htons(ETH_P_TSN);
|
||||
hdr->type = htons(type);
|
||||
|
||||
if (send(server->source->fd, data, size, 0) < 0) {
|
||||
res = -errno;
|
||||
|
|
@ -111,11 +105,6 @@ int avbtp_server_send_packet(struct server *server, const uint8_t dest[6], void
|
|||
return res;
|
||||
}
|
||||
|
||||
int avbtp_server_broadcast_packet(struct server *server, void *data, size_t size)
|
||||
{
|
||||
return avbtp_server_send_packet(server, AVB_MAC_BROADCAST, data, size);
|
||||
}
|
||||
|
||||
static int setup_socket(struct server *server)
|
||||
{
|
||||
struct impl *impl = server->impl;
|
||||
|
|
@ -193,10 +182,10 @@ static int setup_socket(struct server *server)
|
|||
goto error_close;
|
||||
}
|
||||
value.tv_sec = 0;
|
||||
value.tv_nsec = 1;
|
||||
value.tv_nsec = 1;
|
||||
interval.tv_sec = DEFAULT_INTERVAL;
|
||||
interval.tv_nsec = 0;
|
||||
pw_loop_update_timer(impl->loop, server->timer, &value, &interval, false);
|
||||
interval.tv_nsec = 0;
|
||||
pw_loop_update_timer(impl->loop, server->timer, &value, &interval, false);
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
@ -227,8 +216,13 @@ struct server *avdecc_server_new(struct impl *impl, const char *ifname, struct s
|
|||
|
||||
init_descriptors(server);
|
||||
|
||||
server->mrp = avbtp_mrp_new(server);
|
||||
|
||||
avbtp_aecp_register(server);
|
||||
avbtp_maap_register(server);
|
||||
avbtp_mmrp_register(server);
|
||||
avbtp_msrp_register(server);
|
||||
avbtp_mvrp_register(server);
|
||||
avbtp_adp_register(server);
|
||||
avbtp_acmp_register(server);
|
||||
|
||||
|
|
@ -241,10 +235,8 @@ error_free:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void avdecc_server_add_listener(struct server *server,
|
||||
struct spa_hook *listener,
|
||||
const struct server_events *events,
|
||||
void *data)
|
||||
void avdecc_server_add_listener(struct server *server, struct spa_hook *listener,
|
||||
const struct server_events *events, void *data)
|
||||
{
|
||||
spa_hook_list_append(&server->listener_list, listener, events, data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,10 @@ extern "C" {
|
|||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
#define AVB_TSN_ETH 0x22f0
|
||||
#define AVB_BROADCAST_MAC { 0x91, 0xe0, 0xf0, 0x01, 0x00, 0x00 };
|
||||
|
||||
|
||||
struct impl {
|
||||
struct pw_loop *loop;
|
||||
struct pw_context *context;
|
||||
|
|
@ -64,6 +68,7 @@ struct descriptor {
|
|||
void *ptr;
|
||||
};
|
||||
|
||||
|
||||
struct server {
|
||||
struct spa_list link;
|
||||
struct impl *impl;
|
||||
|
|
@ -81,6 +86,8 @@ struct server {
|
|||
struct spa_list descriptors;
|
||||
|
||||
unsigned debug_messages:1;
|
||||
|
||||
struct avbtp_mrp *mrp;
|
||||
};
|
||||
|
||||
static inline const struct descriptor *server_find_descriptor(struct server *server,
|
||||
|
|
@ -118,8 +125,8 @@ void avdecc_server_free(struct server *server);
|
|||
void avdecc_server_add_listener(struct server *server, struct spa_hook *listener,
|
||||
const struct server_events *events, void *data);
|
||||
|
||||
int avbtp_server_broadcast_packet(struct server *server, void *data, size_t size);
|
||||
int avbtp_server_send_packet(struct server *server, const uint8_t dest[6], void *data, size_t size);
|
||||
int avbtp_server_send_packet(struct server *server, const uint8_t dest[6],
|
||||
uint16_t type, void *data, size_t size);
|
||||
|
||||
struct aecp {
|
||||
struct server *server;
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@
|
|||
|
||||
#include "maap.h"
|
||||
|
||||
static const uint8_t mac[6] = AVB_BROADCAST_MAC;
|
||||
|
||||
struct maap {
|
||||
struct server *server;
|
||||
struct spa_hook server_listener;
|
||||
|
|
@ -68,8 +70,14 @@ static void maap_message_debug(struct maap *maap, const struct avbtp_packet_maap
|
|||
static int maap_message(void *data, uint64_t now, const void *message, int len)
|
||||
{
|
||||
struct maap *maap = data;
|
||||
struct server *server = maap->server;
|
||||
const struct avbtp_packet_maap *p = message;
|
||||
|
||||
if (ntohs(p->hdr.eth.type) != AVB_TSN_ETH)
|
||||
return 0;
|
||||
if (memcmp(p->hdr.eth.dest, mac, 6) != 0 &&
|
||||
memcmp(p->hdr.eth.dest, server->mac_addr, 6) != 0)
|
||||
return 0;
|
||||
if (AVBTP_PACKET_GET_SUBTYPE(&p->hdr) != AVBTP_SUBTYPE_MAAP)
|
||||
return 0;
|
||||
|
||||
|
|
|
|||
76
src/modules/module-avbtp/mmrp.c
Normal file
76
src/modules/module-avbtp/mmrp.c
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/* AVB support
|
||||
*
|
||||
* Copyright © 2022 Wim Taymans
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
#include "mmrp.h"
|
||||
|
||||
static const uint8_t mac[6] = AVB_MMRP_MAC;
|
||||
|
||||
struct mmrp {
|
||||
struct server *server;
|
||||
struct spa_hook server_listener;
|
||||
};
|
||||
|
||||
static int mmrp_message(void *data, uint64_t now, const void *message, int len)
|
||||
{
|
||||
const struct avbtp_packet_mrp *p = message;
|
||||
|
||||
if (ntohs(p->eth.type) != AVB_MMRP_ETH)
|
||||
return 0;
|
||||
if (memcmp(p->eth.dest, mac, 6) != 0)
|
||||
return 0;
|
||||
|
||||
pw_log_info("MMRP");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mmrp_destroy(void *data)
|
||||
{
|
||||
struct mmrp *mmrp = data;
|
||||
spa_hook_remove(&mmrp->server_listener);
|
||||
free(mmrp);
|
||||
}
|
||||
|
||||
static const struct server_events server_events = {
|
||||
AVBTP_VERSION_SERVER_EVENTS,
|
||||
.destroy = mmrp_destroy,
|
||||
.message = mmrp_message
|
||||
};
|
||||
|
||||
int avbtp_mmrp_register(struct server *server)
|
||||
{
|
||||
struct mmrp *mmrp;
|
||||
|
||||
mmrp = calloc(1, sizeof(*mmrp));
|
||||
if (mmrp == NULL)
|
||||
return -errno;
|
||||
|
||||
mmrp->server = server;
|
||||
|
||||
avdecc_server_add_listener(server, &mmrp->server_listener, &server_events, mmrp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
42
src/modules/module-avbtp/mmrp.h
Normal file
42
src/modules/module-avbtp/mmrp.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/* AVB support
|
||||
*
|
||||
* Copyright © 2022 Wim Taymans
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef AVBTP_MMRP_H
|
||||
#define AVBTP_MMRP_H
|
||||
|
||||
#include "mrp.h"
|
||||
#include "internal.h"
|
||||
|
||||
#define AVB_MMRP_ETH 0x88f6
|
||||
#define AVB_MMRP_MAC { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x20 }
|
||||
|
||||
struct avbtp_packet_mmrp_msg {
|
||||
uint8_t attribute_type;
|
||||
uint8_t attribute_length;
|
||||
uint8_t attribute_list[0];
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
int avbtp_mmrp_register(struct server *server);
|
||||
|
||||
#endif /* AVBTP_MMRP_H */
|
||||
151
src/modules/module-avbtp/mrp.c
Normal file
151
src/modules/module-avbtp/mrp.c
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
/* AVB support
|
||||
*
|
||||
* Copyright © 2022 Wim Taymans
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
#include "mrp.h"
|
||||
|
||||
struct mrp {
|
||||
struct server *server;
|
||||
struct spa_hook server_listener;
|
||||
};
|
||||
|
||||
static void mrp_destroy(void *data)
|
||||
{
|
||||
struct mrp *mrp = data;
|
||||
spa_hook_remove(&mrp->server_listener);
|
||||
free(mrp);
|
||||
}
|
||||
|
||||
static const struct server_events server_events = {
|
||||
AVBTP_VERSION_SERVER_EVENTS,
|
||||
.destroy = mrp_destroy,
|
||||
};
|
||||
|
||||
void avbtp_mrp_attribute_init(struct avbtp_mrp *mrp,
|
||||
struct avbtp_mrp_attribute *attr,
|
||||
uint8_t type, void *info)
|
||||
{
|
||||
}
|
||||
|
||||
static void stop_avb_timer(void)
|
||||
{
|
||||
}
|
||||
static void start_avb_timer(void)
|
||||
{
|
||||
}
|
||||
|
||||
void avbtp_mrp_update_state(struct avbtp_mrp *mrp,
|
||||
struct avbtp_mrp_attribute *attr, int event, uint8_t param)
|
||||
{
|
||||
switch (event) {
|
||||
case AVBTP_MRP_EVENT_BEGIN:
|
||||
attr->registrar_state = AVBTP_MRP_MT;
|
||||
break;
|
||||
case AVBTP_MRP_EVENT_RX_NEW:
|
||||
if (attr->registrar_state == AVBTP_MRP_LV)
|
||||
stop_avb_timer();
|
||||
attr->registrar_state = AVBTP_MRP_IN;
|
||||
attr->pending_indications |= AVBTP_PENDING_JOIN_NEW;
|
||||
attr->param = param;
|
||||
break;
|
||||
case AVBTP_MRP_EVENT_RX_JOININ:
|
||||
case AVBTP_MRP_EVENT_RX_JOINMT:
|
||||
if (attr->registrar_state == AVBTP_MRP_LV)
|
||||
stop_avb_timer();
|
||||
if (attr->registrar_state == AVBTP_MRP_MT) {
|
||||
attr->pending_indications |= AVBTP_PENDING_JOIN;
|
||||
attr->param = param;
|
||||
}
|
||||
attr->registrar_state = AVBTP_MRP_IN;
|
||||
break;
|
||||
case AVBTP_MRP_EVENT_RX_LV:
|
||||
case AVBTP_MRP_EVENT_RX_LVA:
|
||||
case AVBTP_MRP_EVENT_TX_LVA:
|
||||
case AVBTP_MRP_EVENT_REDECLARE:
|
||||
if (attr->registrar_state == AVBTP_MRP_IN) {
|
||||
start_avb_timer();
|
||||
attr->registrar_state = AVBTP_MRP_LV;
|
||||
}
|
||||
break;
|
||||
case AVBTP_MRP_EVENT_LV_TIMER:
|
||||
case AVBTP_MRP_EVENT_FLUSH:
|
||||
if (attr->registrar_state == AVBTP_MRP_LV) {
|
||||
attr->pending_indications |= AVBTP_PENDING_LEAVE;
|
||||
attr->param = param;
|
||||
}
|
||||
attr->registrar_state = AVBTP_MRP_MT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void avbtp_mrp_event(struct avbtp_mrp *mrp, struct avbtp_mrp_attribute *attr,
|
||||
uint8_t event, uint8_t param)
|
||||
{
|
||||
static const int map[] = {
|
||||
[AVBTP_MRP_ATTRIBUTE_EVENT_NEW] = AVBTP_MRP_EVENT_RX_NEW,
|
||||
[AVBTP_MRP_ATTRIBUTE_EVENT_JOININ] = AVBTP_MRP_EVENT_RX_JOININ,
|
||||
[AVBTP_MRP_ATTRIBUTE_EVENT_IN] = AVBTP_MRP_EVENT_RX_IN,
|
||||
[AVBTP_MRP_ATTRIBUTE_EVENT_JOINMT] = AVBTP_MRP_EVENT_RX_JOINMT,
|
||||
[AVBTP_MRP_ATTRIBUTE_EVENT_MT] = AVBTP_MRP_EVENT_RX_MT,
|
||||
[AVBTP_MRP_ATTRIBUTE_EVENT_LV] = AVBTP_MRP_EVENT_RX_LV,
|
||||
};
|
||||
avbtp_mrp_update_state(mrp, attr, map[event], param);
|
||||
}
|
||||
|
||||
void avbtp_mrp_mad_begin(struct avbtp_mrp *mrp, struct avbtp_mrp_attribute *attr)
|
||||
{
|
||||
}
|
||||
|
||||
void avbtp_mrp_mad_join(struct avbtp_mrp *mrp, struct avbtp_mrp_attribute *attr, bool is_new)
|
||||
{
|
||||
}
|
||||
|
||||
void avbtp_mrp_mad_leave(struct avbtp_mrp *mrp, struct avbtp_mrp_attribute *attr)
|
||||
{
|
||||
}
|
||||
|
||||
void avbtp_mrp_destroy(struct avbtp_mrp *mrp)
|
||||
{
|
||||
mrp_destroy(mrp);
|
||||
}
|
||||
|
||||
struct avbtp_mrp *avbtp_mrp_new(struct server *server)
|
||||
{
|
||||
struct mrp *mrp;
|
||||
|
||||
mrp = calloc(1, sizeof(*mrp));
|
||||
if (mrp == NULL)
|
||||
return NULL;
|
||||
|
||||
mrp->server = server;
|
||||
|
||||
avdecc_server_add_listener(server, &mrp->server_listener, &server_events, mrp);
|
||||
|
||||
return (struct avbtp_mrp*)mrp;
|
||||
}
|
||||
134
src/modules/module-avbtp/mrp.h
Normal file
134
src/modules/module-avbtp/mrp.h
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/* AVB support
|
||||
*
|
||||
* Copyright © 2022 Wim Taymans
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef AVBTP_MRP_H
|
||||
#define AVBTP_MRP_H
|
||||
|
||||
#include "packets.h"
|
||||
#include "internal.h"
|
||||
|
||||
struct avbtp_packet_mrp {
|
||||
struct avbtp_ethernet_header eth;
|
||||
uint8_t version;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct avbtp_packet_mrp_vector {
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
unsigned lva:3;
|
||||
unsigned nv1:5;
|
||||
#elif __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
unsigned nv1:5;
|
||||
unsigned lva:3;
|
||||
#endif
|
||||
uint8_t nv2;
|
||||
uint8_t first_value[0];
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
#define AVBTP_MRP_VECTOR_SET_NUM_VALUES(a,v) ((a)->nv1 = ((v) >> 8),(p)->nv2 = (v))
|
||||
#define AVBTP_MRP_VECTOR_GET_NUM_VALUES(a) ((a)->nv1 << 8 | (a)->nv2)
|
||||
|
||||
struct avbtp_packet_mrp_footer {
|
||||
uint16_t end_mark;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
/* applicant states */
|
||||
#define AVBTP_MRP_VO 0 /* Very anxious Observer */
|
||||
#define AVBTP_MRP_VP 1 /* Very anxious Passive */
|
||||
#define AVBTP_MRP_VN 2 /* Very anxious New */
|
||||
#define AVBTP_MRP_AN 3 /* Anxious New */
|
||||
#define AVBTP_MRP_AA 4 /* Anxious Active */
|
||||
#define AVBTP_MRP_QA 5 /* Quiet Active */
|
||||
#define AVBTP_MRP_LA 6 /* Leaving Active */
|
||||
#define AVBTP_MRP_AO 7 /* Anxious Observer */
|
||||
#define AVBTP_MRP_QO 8 /* Quiet Observer */
|
||||
#define AVBTP_MRP_AP 9 /* Anxious Passive */
|
||||
#define AVBTP_MRP_QP 10 /* Quiet Passive */
|
||||
#define AVBTP_MRP_LO 11 /* Leaving Observer */
|
||||
|
||||
/* registrar states */
|
||||
#define AVBTP_MRP_IN 16
|
||||
#define AVBTP_MRP_LV 17
|
||||
#define AVBTP_MRP_MT 18
|
||||
|
||||
/* events */
|
||||
#define AVBTP_MRP_EVENT_BEGIN 0
|
||||
#define AVBTP_MRP_EVENT_NEW 1
|
||||
#define AVBTP_MRP_EVENT_JOIN 2
|
||||
#define AVBTP_MRP_EVENT_LV 3
|
||||
#define AVBTP_MRP_EVENT_TX 4
|
||||
#define AVBTP_MRP_EVENT_TX_LVA 5
|
||||
#define AVBTP_MRP_EVENT_TX_LVAF 6
|
||||
#define AVBTP_MRP_EVENT_RX_NEW 7
|
||||
#define AVBTP_MRP_EVENT_RX_JOININ 8
|
||||
#define AVBTP_MRP_EVENT_RX_IN 9
|
||||
#define AVBTP_MRP_EVENT_RX_JOINMT 10
|
||||
#define AVBTP_MRP_EVENT_RX_MT 11
|
||||
#define AVBTP_MRP_EVENT_RX_LV 12
|
||||
#define AVBTP_MRP_EVENT_RX_LVA 13
|
||||
#define AVBTP_MRP_EVENT_FLUSH 14
|
||||
#define AVBTP_MRP_EVENT_REDECLARE 15
|
||||
#define AVBTP_MRP_EVENT_PERIODIC 16
|
||||
#define AVBTP_MRP_EVENT_LV_TIMER 17
|
||||
#define AVBTP_MRP_EVENT_LVA_TIMER 18
|
||||
|
||||
/* attribute events */
|
||||
#define AVBTP_MRP_ATTRIBUTE_EVENT_NEW 0
|
||||
#define AVBTP_MRP_ATTRIBUTE_EVENT_JOININ 1
|
||||
#define AVBTP_MRP_ATTRIBUTE_EVENT_IN 2
|
||||
#define AVBTP_MRP_ATTRIBUTE_EVENT_JOINMT 3
|
||||
#define AVBTP_MRP_ATTRIBUTE_EVENT_MT 4
|
||||
#define AVBTP_MRP_ATTRIBUTE_EVENT_LV 5
|
||||
|
||||
#define AVBTP_PENDING_JOIN_NEW (1u<<0)
|
||||
#define AVBTP_PENDING_JOIN (1u<<1)
|
||||
#define AVBTP_PENDING_LEAVE (1u<<2)
|
||||
|
||||
struct avbtp_mrp_attribute {
|
||||
uint8_t type;
|
||||
uint8_t applicant_state;
|
||||
uint8_t registrar_state;
|
||||
uint16_t pending_indications;
|
||||
uint8_t param;
|
||||
struct avbtp_mrp_attribute *next;
|
||||
void *attribute_info;
|
||||
};
|
||||
|
||||
void avbtp_mrp_attribute_init(struct avbtp_mrp *mrp,
|
||||
struct avbtp_mrp_attribute *attr,
|
||||
uint8_t type, void *info);
|
||||
|
||||
void avbtp_mrp_update_state(struct avbtp_mrp *mrp,
|
||||
struct avbtp_mrp_attribute *attr, int event, uint8_t param);
|
||||
|
||||
void avbtp_mrp_event(struct avbtp_mrp *mrp, struct avbtp_mrp_attribute *attr,
|
||||
uint8_t event, uint8_t param);
|
||||
|
||||
void avbtp_mrp_mad_begin(struct avbtp_mrp *mrp, struct avbtp_mrp_attribute *attr);
|
||||
void avbtp_mrp_mad_join(struct avbtp_mrp *mrp, struct avbtp_mrp_attribute *attr, bool is_new);
|
||||
void avbtp_mrp_mad_leave(struct avbtp_mrp *mrp, struct avbtp_mrp_attribute *attr);
|
||||
|
||||
struct avbtp_mrp *avbtp_mrp_new(struct server *server);
|
||||
void avbtp_mrp_destroy(struct avbtp_mrp *mrp);
|
||||
|
||||
#endif /* AVBTP_MRP_H */
|
||||
218
src/modules/module-avbtp/msrp.c
Normal file
218
src/modules/module-avbtp/msrp.c
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
/* AVB support
|
||||
*
|
||||
* Copyright © 2022 Wim Taymans
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <spa/debug/mem.h>
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "msrp.h"
|
||||
|
||||
static const uint8_t mac[6] = AVB_MSRP_MAC;
|
||||
|
||||
struct attr {
|
||||
struct spa_list link;
|
||||
struct avbtp_mrp_attribute attr;
|
||||
uint64_t stream_id;
|
||||
};
|
||||
|
||||
struct msrp {
|
||||
struct server *server;
|
||||
struct spa_hook server_listener;
|
||||
|
||||
struct spa_list attributes;
|
||||
};
|
||||
|
||||
static struct attr *find_attr_by_stream_id(struct msrp *msrp, uint64_t stream_id)
|
||||
{
|
||||
struct attr *a;
|
||||
spa_list_for_each(a, &msrp->attributes, link)
|
||||
if (a->stream_id == stream_id)
|
||||
return a;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void attr_event(struct msrp *msrp, uint8_t type, int event)
|
||||
{
|
||||
struct attr *a;
|
||||
spa_list_for_each(a, &msrp->attributes, link)
|
||||
if (a->attr.type == type)
|
||||
avbtp_mrp_update_state(msrp->server->mrp, &a->attr, event, 0);
|
||||
}
|
||||
|
||||
static void process_talker(struct msrp *msrp, uint8_t attr_type,
|
||||
const void *m, uint8_t event, uint8_t param, int num)
|
||||
{
|
||||
const struct avbtp_packet_msrp_talker *t = m;
|
||||
char buf[128];
|
||||
struct attr *a;
|
||||
|
||||
pw_log_info("talker");
|
||||
pw_log_info(" %s", avbtp_utils_format_id(buf, sizeof(buf), t->stream_id));
|
||||
|
||||
a = find_attr_by_stream_id(msrp, be64toh(t->stream_id));
|
||||
if (a)
|
||||
avbtp_mrp_event(msrp->server->mrp, &a->attr, event, param);
|
||||
}
|
||||
|
||||
static void process_talker_fail(struct msrp *msrp, uint8_t attr_type,
|
||||
const void *m, uint8_t event, uint8_t param, int num)
|
||||
{
|
||||
const struct avbtp_packet_msrp_talker_fail *t = m;
|
||||
char buf[128];
|
||||
pw_log_info("talker fail");
|
||||
pw_log_info(" %s", avbtp_utils_format_id(buf, sizeof(buf), t->talker.stream_id));
|
||||
}
|
||||
|
||||
static void process_listener(struct msrp *msrp, uint8_t attr_type,
|
||||
const void *m, uint8_t event, uint8_t param, int num)
|
||||
{
|
||||
const struct avbtp_packet_msrp_listener *l = m;
|
||||
char buf[128];
|
||||
pw_log_info("listener");
|
||||
pw_log_info(" %s", avbtp_utils_format_id(buf, sizeof(buf), l->stream_id));
|
||||
}
|
||||
|
||||
static void process_domain(struct msrp *msrp, uint8_t attr_type,
|
||||
const void *m, uint8_t event, uint8_t param, int num)
|
||||
{
|
||||
const struct avbtp_packet_msrp_domain *d = m;
|
||||
pw_log_info("domain");
|
||||
pw_log_info(" %d", d->sr_class_id);
|
||||
pw_log_info(" %d", d->sr_class_priority);
|
||||
pw_log_info(" %d", ntohs(d->sr_class_vid));
|
||||
}
|
||||
|
||||
static const struct {
|
||||
void (*dispatch) (struct msrp *msrp, uint8_t attr_type, const void *m, uint8_t event, uint8_t param, int num);
|
||||
} dispatch[] = {
|
||||
[AVBTP_MSRP_ATTRIBUTE_TYPE_TALKER_ADVERTISE] = { process_talker, },
|
||||
[AVBTP_MSRP_ATTRIBUTE_TYPE_TALKER_FAILED] = { process_talker_fail, },
|
||||
[AVBTP_MSRP_ATTRIBUTE_TYPE_LISTENER] = { process_listener, },
|
||||
[AVBTP_MSRP_ATTRIBUTE_TYPE_DOMAIN] = { process_domain, },
|
||||
};
|
||||
|
||||
static inline bool has_params(uint16_t type)
|
||||
{
|
||||
return type == AVBTP_MSRP_ATTRIBUTE_TYPE_LISTENER;
|
||||
}
|
||||
|
||||
static int process(struct msrp *msrp, uint64_t now, const void *message, int len)
|
||||
{
|
||||
uint8_t *e = SPA_PTROFF(message, len, uint8_t);
|
||||
uint8_t *m = SPA_PTROFF(message, sizeof(struct avbtp_packet_mrp), uint8_t);
|
||||
|
||||
while (m < e && (m[0] != 0 || m[1] != 0)) {
|
||||
const struct avbtp_packet_msrp_msg *msg = (const struct avbtp_packet_msrp_msg*)m;
|
||||
uint8_t attr_len = msg->attribute_length;
|
||||
uint8_t attr_type = msg->attribute_type;
|
||||
bool has_param = has_params(attr_type);
|
||||
|
||||
if (!AVBTP_MSRP_ATTRIBUTE_TYPE_VALID(attr_type))
|
||||
return -EINVAL;
|
||||
|
||||
m += sizeof(*msg);
|
||||
|
||||
while (m < e && (m[0] != 0 || m[1] != 0)) {
|
||||
const struct avbtp_packet_mrp_vector *v =
|
||||
(const struct avbtp_packet_mrp_vector*)m;
|
||||
uint16_t i, num_values = AVBTP_MRP_VECTOR_GET_NUM_VALUES(v);
|
||||
uint8_t event_len = (num_values+2)/3;
|
||||
uint8_t param_len = has_param ? (num_values+3)/4 : 0;
|
||||
int len = sizeof(*v) + attr_len + event_len + param_len;
|
||||
const uint8_t *first = v->first_value;
|
||||
uint8_t event[3], param[4] = { 0, };
|
||||
|
||||
if (m + len > e)
|
||||
return -EPROTO;
|
||||
|
||||
if (v->lva)
|
||||
attr_event(msrp, attr_type, AVBTP_MRP_EVENT_RX_LVA);
|
||||
|
||||
for (i = 0; i < num_values; i++) {
|
||||
if (i % 3 == 0) {
|
||||
uint8_t ep = first[attr_len + i/3];
|
||||
event[2] = ep % 6; ep /= 6;
|
||||
event[1] = ep % 6; ep /= 6;
|
||||
event[0] = ep % 6;
|
||||
}
|
||||
if (has_param && (i % 4 == 0)) {
|
||||
uint8_t ep = first[attr_len + event_len + i/4];
|
||||
param[3] = ep % 4; ep /= 4;
|
||||
param[2] = ep % 4; ep /= 4;
|
||||
param[1] = ep % 4; ep /= 4;
|
||||
param[0] = ep % 4;
|
||||
}
|
||||
dispatch[attr_type].dispatch(msrp,
|
||||
attr_type, first, event[i%3], param[i%4], i);
|
||||
}
|
||||
m += len;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int msrp_message(void *data, uint64_t now, const void *message, int len)
|
||||
{
|
||||
struct msrp *msrp = data;
|
||||
const struct avbtp_packet_mrp *p = message;
|
||||
|
||||
if (ntohs(p->eth.type) != AVB_MSRP_ETH)
|
||||
return 0;
|
||||
if (memcmp(p->eth.dest, mac, 6) != 0)
|
||||
return 0;
|
||||
|
||||
pw_log_info("MSRP");
|
||||
return process(msrp, now, message, len);
|
||||
}
|
||||
|
||||
static void msrp_destroy(void *data)
|
||||
{
|
||||
struct msrp *msrp = data;
|
||||
spa_hook_remove(&msrp->server_listener);
|
||||
free(msrp);
|
||||
}
|
||||
|
||||
static const struct server_events server_events = {
|
||||
AVBTP_VERSION_SERVER_EVENTS,
|
||||
.destroy = msrp_destroy,
|
||||
.message = msrp_message
|
||||
};
|
||||
|
||||
int avbtp_msrp_register(struct server *server)
|
||||
{
|
||||
struct msrp *msrp;
|
||||
|
||||
msrp = calloc(1, sizeof(*msrp));
|
||||
if (msrp == NULL)
|
||||
return -errno;
|
||||
|
||||
msrp->server = server;
|
||||
spa_list_init(&msrp->attributes);
|
||||
|
||||
avdecc_server_add_listener(server, &msrp->server_listener, &server_events, msrp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
109
src/modules/module-avbtp/msrp.h
Normal file
109
src/modules/module-avbtp/msrp.h
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
/* AVB support
|
||||
*
|
||||
* Copyright © 2022 Wim Taymans
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef AVBTP_MSRP_H
|
||||
#define AVBTP_MSRP_H
|
||||
|
||||
#include "internal.h"
|
||||
#include "mrp.h"
|
||||
|
||||
#define AVB_MSRP_ETH 0x22ea
|
||||
#define AVB_MSRP_MAC { 0x01, 0x80, 0xc2, 0x00, 0x00, 0xe };
|
||||
|
||||
#define AVBTP_MSRP_ATTRIBUTE_TYPE_TALKER_ADVERTISE 1
|
||||
#define AVBTP_MSRP_ATTRIBUTE_TYPE_TALKER_FAILED 2
|
||||
#define AVBTP_MSRP_ATTRIBUTE_TYPE_LISTENER 3
|
||||
#define AVBTP_MSRP_ATTRIBUTE_TYPE_DOMAIN 4
|
||||
#define AVBTP_MSRP_ATTRIBUTE_TYPE_VALID(t) ((t)>=1 && (t)<=4)
|
||||
|
||||
struct avbtp_packet_msrp_msg {
|
||||
uint8_t attribute_type;
|
||||
uint8_t attribute_length;
|
||||
uint16_t attribute_list_length;
|
||||
uint8_t attribute_list[0];
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
#define AVBTP_MSRP_TSPEC_MAX_INTERVAL_FRAMES_DEFAULT 1
|
||||
#define AVBTP_MSRP_RANK_DEFAULT 1
|
||||
#define AVBTP_MSRP_PRIORITY_DEFAULT 3
|
||||
|
||||
struct avbtp_packet_msrp_talker {
|
||||
uint64_t stream_id;
|
||||
uint8_t dest_addr[6];
|
||||
uint16_t vlan_id;
|
||||
uint16_t tspec_max_frame_size;
|
||||
uint16_t tspec_max_interval_frames;
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
unsigned priority:3;
|
||||
unsigned rank:1;
|
||||
unsigned reserved:4;
|
||||
#elif __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
unsigned reserved:4;
|
||||
unsigned rank:1;
|
||||
unsigned priority:3;
|
||||
#endif
|
||||
uint32_t accumulated_latency;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
/* failure codes */
|
||||
#define AVBTP_MRP_FAIL_BANDWIDTH 1
|
||||
#define AVBTP_MRP_FAIL_BRIDGE 2
|
||||
#define AVBTP_MRP_FAIL_TC_BANDWIDTH 3
|
||||
#define AVBTP_MRP_FAIL_ID_BUSY 4
|
||||
#define AVBTP_MRP_FAIL_DSTADDR_BUSY 5
|
||||
#define AVBTP_MRP_FAIL_PREEMPTED 6
|
||||
#define AVBTP_MRP_FAIL_LATENCY_CHNG 7
|
||||
#define AVBTP_MRP_FAIL_PORT_NOT_AVB 8
|
||||
#define AVBTP_MRP_FAIL_DSTADDR_FULL 9
|
||||
#define AVBTP_MRP_FAIL_AVBTP_MRP_RESOURCE 10
|
||||
#define AVBTP_MRP_FAIL_MMRP_RESOURCE 11
|
||||
#define AVBTP_MRP_FAIL_DSTADDR_FAIL 12
|
||||
#define AVBTP_MRP_FAIL_PRIO_NOT_SR 13
|
||||
#define AVBTP_MRP_FAIL_FRAME_SIZE 14
|
||||
#define AVBTP_MRP_FAIL_FANIN_EXCEED 15
|
||||
#define AVBTP_MRP_FAIL_STREAM_CHANGE 16
|
||||
#define AVBTP_MRP_FAIL_VLAN_BLOCKED 17
|
||||
#define AVBTP_MRP_FAIL_VLAN_DISABLED 18
|
||||
#define AVBTP_MRP_FAIL_SR_PRIO_ERR 19
|
||||
|
||||
struct avbtp_packet_msrp_talker_fail {
|
||||
struct avbtp_packet_msrp_talker talker;
|
||||
uint64_t bridge_id;
|
||||
uint8_t failure_code;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct avbtp_packet_msrp_listener {
|
||||
uint64_t stream_id;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
/* domain discovery */
|
||||
struct avbtp_packet_msrp_domain {
|
||||
uint8_t sr_class_id;
|
||||
uint8_t sr_class_priority;
|
||||
uint16_t sr_class_vid;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
int avbtp_msrp_register(struct server *server);
|
||||
|
||||
#endif /* AVBTP_MSRP_H */
|
||||
76
src/modules/module-avbtp/mvrp.c
Normal file
76
src/modules/module-avbtp/mvrp.c
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/* AVB support
|
||||
*
|
||||
* Copyright © 2022 Wim Taymans
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
#include "mvrp.h"
|
||||
|
||||
static const uint8_t mac[6] = AVB_MVRP_MAC;
|
||||
|
||||
struct mvrp {
|
||||
struct server *server;
|
||||
struct spa_hook server_listener;
|
||||
};
|
||||
|
||||
static int mvrp_message(void *data, uint64_t now, const void *message, int len)
|
||||
{
|
||||
const struct avbtp_packet_mrp *p = message;
|
||||
|
||||
if (ntohs(p->eth.type) != AVB_MVRP_ETH)
|
||||
return 0;
|
||||
if (memcmp(p->eth.dest, mac, 6) != 0)
|
||||
return 0;
|
||||
|
||||
pw_log_info("MVRP");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mvrp_destroy(void *data)
|
||||
{
|
||||
struct mvrp *mvrp = data;
|
||||
spa_hook_remove(&mvrp->server_listener);
|
||||
free(mvrp);
|
||||
}
|
||||
|
||||
static const struct server_events server_events = {
|
||||
AVBTP_VERSION_SERVER_EVENTS,
|
||||
.destroy = mvrp_destroy,
|
||||
.message = mvrp_message
|
||||
};
|
||||
|
||||
int avbtp_mvrp_register(struct server *server)
|
||||
{
|
||||
struct mvrp *mvrp;
|
||||
|
||||
mvrp = calloc(1, sizeof(*mvrp));
|
||||
if (mvrp == NULL)
|
||||
return -errno;
|
||||
|
||||
mvrp->server = server;
|
||||
|
||||
avdecc_server_add_listener(server, &mvrp->server_listener, &server_events, mvrp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
42
src/modules/module-avbtp/mvrp.h
Normal file
42
src/modules/module-avbtp/mvrp.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/* AVB support
|
||||
*
|
||||
* Copyright © 2022 Wim Taymans
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef AVBTP_MVRP_H
|
||||
#define AVBTP_MVRP_H
|
||||
|
||||
#include "mrp.h"
|
||||
#include "internal.h"
|
||||
|
||||
#define AVB_MVRP_ETH 0x88f5
|
||||
#define AVB_MVRP_MAC { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x21 };
|
||||
|
||||
struct avbtp_packet_mvrp_msg {
|
||||
uint8_t attribute_type;
|
||||
uint8_t attribute_length;
|
||||
uint8_t attribute_list[0];
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
int avbtp_mvrp_register(struct server *server);
|
||||
|
||||
#endif /* AVBTP_MVRP_H */
|
||||
59
src/modules/module-avbtp/srp.c
Normal file
59
src/modules/module-avbtp/srp.c
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/* AVB support
|
||||
*
|
||||
* Copyright © 2022 Wim Taymans
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
#include "srp.h"
|
||||
|
||||
struct srp {
|
||||
struct server *server;
|
||||
struct spa_hook server_listener;
|
||||
};
|
||||
|
||||
static void srp_destroy(void *data)
|
||||
{
|
||||
struct srp *srp = data;
|
||||
spa_hook_remove(&srp->server_listener);
|
||||
free(srp);
|
||||
}
|
||||
|
||||
static const struct server_events server_events = {
|
||||
AVBTP_VERSION_SERVER_EVENTS,
|
||||
.destroy = srp_destroy,
|
||||
};
|
||||
|
||||
int avbtp_srp_register(struct server *server)
|
||||
{
|
||||
struct srp *srp;
|
||||
|
||||
srp = calloc(1, sizeof(*srp));
|
||||
if (srp == NULL)
|
||||
return -errno;
|
||||
|
||||
srp->server = server;
|
||||
|
||||
avdecc_server_add_listener(server, &srp->server_listener, &server_events, srp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
32
src/modules/module-avbtp/srp.h
Normal file
32
src/modules/module-avbtp/srp.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/* AVB support
|
||||
*
|
||||
* Copyright © 2022 Wim Taymans
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef AVBTP_SRP_H
|
||||
#define AVBTP_SRP_H
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
int avbtp_srp_register(struct server *server);
|
||||
|
||||
#endif /* AVBTP_SRP_H */
|
||||
|
|
@ -25,6 +25,8 @@
|
|||
#ifndef AVBTP_UTILS_H
|
||||
#define AVBTP_UTILS_H
|
||||
|
||||
#include <spa/utils/json.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
static inline char *avbtp_utils_format_id(char *str, size_t size, const uint64_t id)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue