Skip to content

Commit 0b8fe05

Browse files
committed
net: lib: midi2: new Network MIDI 2.0 host stack
Add a new network protocol for MIDI2.0 over the network, using UDP sockets. This allows Zephyr to host a UMP endpoint on the network, which can be invited by UMP clients to exchange MIDI2.0 data. Signed-off-by: Titouan Christophe <[email protected]>
1 parent 0a641ed commit 0b8fe05

File tree

6 files changed

+1107
-0
lines changed

6 files changed

+1107
-0
lines changed

include/zephyr/net/midi2.h

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
/*
2+
* Copyright (c) 2025 Titouan Christophe
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#ifndef ZEPHYR_INCLUDE_NET_MIDI2_H_
7+
#define ZEPHYR_INCLUDE_NET_MIDI2_H_
8+
9+
/**
10+
* @defgroup net_midi2 Network MIDI 2.0
11+
* @since 4.3
12+
* @version 0.1.0
13+
* @ingroup networking
14+
* @{
15+
*/
16+
17+
/**
18+
* @defgroup netmidi10 User Datagram Protocol for Universal MIDI Packets
19+
* @ingroup net_midi2
20+
* @{
21+
* @details Definitions based on the following document
22+
* <a href="https://drive.google.com/file/d/1dtsOgMLbtif9Fp-OaZhwnRs9an4dn3uv/view">
23+
* User Datagram Protocol for Universal MIDI Packets
24+
* Network MIDI 2.0 (UDP) Transport Specification - Document version 1.0
25+
* (MIDI Association Document: M2-124-UM)
26+
* </a>
27+
* @}
28+
*/
29+
30+
31+
#include <stdint.h>
32+
#include <zephyr/audio/midi.h>
33+
#include <zephyr/net/socket.h>
34+
#include <zephyr/posix/poll.h>
35+
36+
/**
37+
* Size, in bytes, of the nonce sent to the client for authentication
38+
* @see netmidi10: 6.7 Invitation Reply: Authentication Required
39+
*/
40+
#define NETMIDI2_NONCE_SIZE 16
41+
42+
/**
43+
* @brief Statically declare a Network (UDP) MIDI 2.0 endpoint host
44+
* @param _var_name The name of the variable holding the server data
45+
* @param _ep_name The UMP endpoint name
46+
* @param _piid The UMP Product Instance ID. If NULL,
47+
* HWINFO device ID will be used at runtime.
48+
* @param _port The UDP port to listen to, or 0 for automatic assignment
49+
*/
50+
#define NETMIDI2_EP_DECLARE(_var_name, _ep_name, _piid, _port) \
51+
static struct netmidi2_ep _var_name = { \
52+
.name = (_ep_name), \
53+
.piid = (_piid), \
54+
.addr4.sin_port = (_port), \
55+
.auth_type = NETMIDI2_AUTH_NONE, \
56+
}
57+
58+
#if CONFIG_NETMIDI2_HOST_AUTH
59+
60+
/**
61+
* @brief Statically declare a Network (UDP) MIDI 2.0 endpoint host,
62+
* with a predefined shared secret key for authentication
63+
* @param _var_name The name of the variable holding the server data
64+
* @param _ep_name The UMP endpoint name
65+
* @param _piid The UMP Product Instance ID. If NULL,
66+
* HWINFO device ID will be used at runtime.
67+
* @param _port The UDP port to listen to, or 0 for automatic assignment
68+
* @param _secret The shared secret key clients must provide to connect
69+
*/
70+
#define NETMIDI2_EP_DECLARE_WITH_AUTH(_var_name, _ep_name, _piid, _port, _secret) \
71+
static struct netmidi2_ep _var_name = { \
72+
.name = (_ep_name), \
73+
.piid = (_piid), \
74+
.addr4.sin_port = (_port), \
75+
.auth_type = NETMIDI2_AUTH_SHARED_SECRET, \
76+
.shared_auth_secret = (_secret), \
77+
}
78+
79+
/**
80+
* @brief Statically declare a Network (UDP) MIDI 2.0 endpoint host,
81+
* with a predefined list of users/passwords for authentication
82+
* @param _var_name The name of the variable holding the server data
83+
* @param _ep_name The UMP endpoint name
84+
* @param _piid The UMP Product Instance ID. If NULL,
85+
* HWINFO device ID will be used at runtime.
86+
* @param _port The UDP port to listen to, or 0 for automatic assignment
87+
* @param ... The username/password pairs (struct netmidi2_user)
88+
*
89+
* Example usage:
90+
* @code
91+
* NETMIDI2_EP_DECLARE_WITH_USERS(my_server, "endpoint", NULL, 0,
92+
* {.name="user1", .password="passwd1"},
93+
* {.name="user2", .password="passwd2"})
94+
* @endcode
95+
*/
96+
#define NETMIDI2_EP_DECLARE_WITH_USERS(_var_name, _ep_name, _piid, _port, ...) \
97+
static const struct netmidi2_userlist users_of_##_var_name = { \
98+
.n_users = ARRAY_SIZE(((struct netmidi2_user []) { __VA_ARGS__ })), \
99+
.users = { __VA_ARGS__ }, \
100+
}; \
101+
static struct netmidi2_ep _var_name = { \
102+
.name = (_ep_name), \
103+
.piid = (_piid), \
104+
.addr4.sin_port = (_port), \
105+
.auth_type = NETMIDI2_AUTH_USER_PASSWORD, \
106+
.userlist = &users_of_##_var_name, \
107+
}
108+
#endif /* CONFIG_NETMIDI2_HOST_AUTH */
109+
110+
struct netmidi2_ep;
111+
112+
/**
113+
* @brief A username/password pair for user-based authentication
114+
*/
115+
struct netmidi2_user {
116+
const char *name;
117+
const char *password;
118+
};
119+
120+
/**
121+
* @brief A list of users for user-based authentication
122+
*/
123+
struct netmidi2_userlist {
124+
/** Number of users in the list */
125+
size_t n_users;
126+
/** The user/password pairs */
127+
const struct netmidi2_user users[];
128+
};
129+
130+
/**
131+
* @brief A Network MIDI2 session, representing a connection to a peer
132+
*/
133+
struct netmidi2_session {
134+
/**
135+
* State of this session
136+
* @see netmidi10: 6.1 Session States
137+
*/
138+
enum {
139+
NOT_INITIALIZED = 0,
140+
IDLE,
141+
PENDING_INVITATION,
142+
AUTHENTICATION_REQUIRED,
143+
ESTABLISHED_SESSION,
144+
PENDING_SESSION_RESET,
145+
PENDING_BYE,
146+
} state;
147+
/** Sequence number of the next universal MIDI packet to send */
148+
uint16_t tx_ump_seq;
149+
/** Sequence number of the next universal MIDI packet to receive */
150+
uint16_t rx_ump_seq;
151+
/** Remote address of the peer */
152+
struct sockaddr addr;
153+
/** Length of the peer's remote address */
154+
socklen_t addr_len;
155+
/** The Network MIDI2 endpoint to which this session belongs */
156+
struct netmidi2_ep *ep;
157+
#if CONFIG_NETMIDI2_HOST_AUTH
158+
/** The username to which this session belongs */
159+
const struct netmidi2_user *user;
160+
/** The crypto nonce used to authorize this session */
161+
char nonce[NETMIDI2_NONCE_SIZE];
162+
#endif
163+
/** The transmission buffer for that peer */
164+
struct net_buf *tx_buf;
165+
/** The transmission work for that peer */
166+
struct k_work tx_work;
167+
};
168+
169+
/**
170+
* @brief Type of authentication in Network MIDI2
171+
*/
172+
enum netmidi2_auth_type {
173+
/** No authentication required */
174+
NETMIDI2_AUTH_NONE,
175+
/** Authentication with a shared secret key */
176+
NETMIDI2_AUTH_SHARED_SECRET,
177+
/** Authentication with username and password */
178+
NETMIDI2_AUTH_USER_PASSWORD,
179+
};
180+
181+
/**
182+
* @brief A Network MIDI2.0 Endpoint
183+
*/
184+
struct netmidi2_ep {
185+
/** The endpoint name */
186+
const char *name;
187+
/** The endpoint product instance id */
188+
const char *piid;
189+
/** The endpoint local address (ipv4) */
190+
struct sockaddr_in addr4;
191+
/** The listening socket wrapped in a poll descriptor */
192+
struct pollfd pollsock;
193+
/** The function to call when data is received from a client */
194+
void (*rx_packet_cb)(struct netmidi2_session *session,
195+
const struct midi_ump ump);
196+
/** List of peers to this endpoint */
197+
struct netmidi2_session peers[CONFIG_NETMIDI2_HOST_MAX_CLIENTS];
198+
/** The type of authentication required to establish a session
199+
* with this host endpoint
200+
*/
201+
enum netmidi2_auth_type auth_type;
202+
#if CONFIG_NETMIDI2_HOST_AUTH
203+
union {
204+
/** A shared authentication key */
205+
const char *shared_auth_secret;
206+
/** A list of users/passwords */
207+
const struct netmidi2_userlist *userlist;
208+
};
209+
#endif
210+
};
211+
212+
/**
213+
* @brief Start hosting a network (UDP) Universal MIDI Packet endpoint
214+
* @param ep The network endpoint to start
215+
* @return 0 on success, -errno on error
216+
*/
217+
int netmidi2_host_ep_start(struct netmidi2_ep *ep);
218+
219+
/**
220+
* @brief Send a Universal MIDI Packet to all clients connected to the endpoint
221+
* @param ep The endpoint
222+
* @param[in] ump The packet to send
223+
*/
224+
void netmidi2_broadcast(struct netmidi2_ep *ep, const struct midi_ump ump);
225+
226+
/**
227+
* @brief Send a Universal MIDI Packet to a single client
228+
* @param sess The session identifying the single client
229+
* @param[in] ump The packet to send
230+
*/
231+
void netmidi2_send(struct netmidi2_session *sess, const struct midi_ump ump);
232+
233+
/** @} */
234+
235+
#endif

subsys/net/lib/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ add_subdirectory_ifdef(CONFIG_NET_TRICKLE trickle)
2020
add_subdirectory_ifdef(CONFIG_NET_DHCPV6 dhcpv6)
2121
add_subdirectory_ifdef(CONFIG_PROMETHEUS prometheus)
2222
add_subdirectory_ifdef(CONFIG_WIFI_CREDENTIALS wifi_credentials)
23+
add_subdirectory_ifdef(CONFIG_NETMIDI2_HOST midi2)
24+
2325

2426
if (CONFIG_NET_DHCPV4 OR CONFIG_NET_DHCPV4_SERVER)
2527
add_subdirectory(dhcpv4)

subsys/net/lib/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ source "subsys/net/lib/dns/Kconfig"
99

1010
source "subsys/net/lib/latmon/Kconfig"
1111

12+
source "subsys/net/lib/midi2/Kconfig"
13+
1214
source "subsys/net/lib/mqtt/Kconfig"
1315

1416
source "subsys/net/lib/mqtt_sn/Kconfig"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Copyright (c) 2025 Titouan Christophe
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
zephyr_library()
5+
6+
zephyr_library_sources(netmidi2.c)

subsys/net/lib/midi2/Kconfig

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Copyright (c) 2025 Titouan Christophe
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config NETMIDI2_HOST
5+
bool "Network MIDI2 (UDP) host [EXPERIMENTAL]"
6+
select NET_SOCKETS
7+
select NET_IPV4
8+
select NET_UDP
9+
help
10+
Host library of User Datagram Protocol for Universal MIDI Packets.
11+
Provides the following features:
12+
- Exposing an UMP endpoint over UDP
13+
- Accepting inbound client invitations
14+
- Sending/Receiving Universal MIDI packets
15+
Following "Network MIDI 2.0 (UDP) Transport Specification" v1.0
16+
17+
if NETMIDI2_HOST
18+
config NETMIDI2_HOST_MAX_CLIENTS
19+
int "Maximum number of clients supported by the Network MIDI2 host"
20+
default 5
21+
22+
config NETMIDI2_HOST_AUTH
23+
bool "Support for authentication (shared key or user/password)"
24+
select MBEDTLS
25+
select MBEDTLS_CIPHER_CCM_ENABLED
26+
select CRYPTO
27+
select CRYPTO_MBEDTLS_SHIM
28+
29+
module=NET_MIDI2
30+
module-dep=NET_LOG
31+
module-str=Log level for network MIDI2
32+
module-help=Enables midi2 debug messages.
33+
source "subsys/net/Kconfig.template.log_config.net"
34+
endif

0 commit comments

Comments
 (0)