Line data Source code
1 0 : /*
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://midi.org/network-midi-2-0">
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 1 : #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 1 : #define NETMIDI2_EP_DEFINE(_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 : /**
59 : * @brief Statically declare a Network (UDP) MIDI 2.0 endpoint host,
60 : * with a predefined shared secret key for authentication
61 : * @param _var_name The name of the variable holding the server data
62 : * @param _ep_name The UMP endpoint name
63 : * @param _piid The UMP Product Instance ID. If NULL,
64 : * HWINFO device ID will be used at runtime.
65 : * @param _port The UDP port to listen to, or 0 for automatic assignment
66 : * @param _secret The shared secret key clients must provide to connect
67 : */
68 1 : #define NETMIDI2_EP_DEFINE_WITH_AUTH(_var_name, _ep_name, _piid, _port, _secret) \
69 : static struct netmidi2_ep _var_name = { \
70 : .name = (_ep_name), \
71 : .piid = (_piid), \
72 : .addr4.sin_port = (_port), \
73 : .auth_type = NETMIDI2_AUTH_SHARED_SECRET, \
74 : .shared_auth_secret = (_secret), \
75 : }
76 :
77 : /**
78 : * @brief Statically declare a Network (UDP) MIDI 2.0 endpoint host,
79 : * with a predefined list of users/passwords for authentication
80 : * @param _var_name The name of the variable holding the server data
81 : * @param _ep_name The UMP endpoint name
82 : * @param _piid The UMP Product Instance ID. If NULL,
83 : * HWINFO device ID will be used at runtime.
84 : * @param _port The UDP port to listen to, or 0 for automatic assignment
85 : * @param ... The username/password pairs (struct netmidi2_user)
86 : *
87 : * Example usage:
88 : * @code
89 : * NETMIDI2_EP_DEFINE_WITH_USERS(my_server, "endpoint", NULL, 0,
90 : * {.name="user1", .password="passwd1"},
91 : * {.name="user2", .password="passwd2"})
92 : * @endcode
93 : */
94 1 : #define NETMIDI2_EP_DEFINE_WITH_USERS(_var_name, _ep_name, _piid, _port, ...) \
95 : static const struct netmidi2_userlist users_of_##_var_name = { \
96 : .n_users = ARRAY_SIZE(((struct netmidi2_user []) { __VA_ARGS__ })), \
97 : .users = { __VA_ARGS__ }, \
98 : }; \
99 : static struct netmidi2_ep _var_name = { \
100 : .name = (_ep_name), \
101 : .piid = (_piid), \
102 : .addr4.sin_port = (_port), \
103 : .auth_type = NETMIDI2_AUTH_USER_PASSWORD, \
104 : .userlist = &users_of_##_var_name, \
105 : }
106 :
107 : struct netmidi2_ep;
108 :
109 : /**
110 : * @brief A username/password pair for user-based authentication
111 : */
112 1 : struct netmidi2_user {
113 : /** The user name for authentication */
114 1 : const char *name;
115 : /** The password for authentication */
116 1 : const char *password;
117 : };
118 :
119 : /**
120 : * @brief A list of users for user-based authentication
121 : */
122 1 : struct netmidi2_userlist {
123 : /** Number of users in the list */
124 1 : size_t n_users;
125 : /** The user/password pairs */
126 1 : const struct netmidi2_user users[];
127 : };
128 :
129 : /**
130 : * @brief A Network MIDI2 session, representing a connection to a peer
131 : */
132 1 : struct netmidi2_session {
133 : /**
134 : * State of this session
135 : * @see netmidi10: 6.1 Session States
136 : */
137 1 : enum {
138 : /** The session is not in use */
139 : NETMIDI2_SESSION_NOT_INITIALIZED = 0,
140 : /** Device may be aware of each other (e.g. through mDNS),
141 : * however neither device has sent an Invitation.
142 : * The two Devices are not in a Session.
143 : */
144 : NETMIDI2_SESSION_IDLE,
145 : /** Client has sent Invitation, however the Host has not
146 : * accepted the Invitation. A Bye Command has not been
147 : * sent or received.
148 : */
149 : NETMIDI2_SESSION_PENDING_INVITATION,
150 : /** Host has replied with Invitation Reply: Authentication
151 : * Required or Invitation Reply: User Authentication Required.
152 : * No timeout has yet occurred. This state is different from
153 : * Idle, because the Client and Host need to store the Nonce.
154 : */
155 : NETMIDI2_SESSION_AUTH_REQUIRED,
156 : /** Invitation accepted, UMP Commands can be exchanged. */
157 : NETMIDI2_SESSION_ESTABLISHED,
158 : /** One Device has sent the Session Reset Command and is
159 : * waiting for Session Reset Reply, and a timeout has not
160 : * yet occurred.
161 : */
162 : NETMIDI2_SESSION_PENDING_RESET,
163 : /** one Endpoint has sent Bye and is waiting for Bye Reply,
164 : * and a timeout has not yet occurred.
165 : */
166 : NETMIDI2_SESSION_PENDING_BYE,
167 1 : } state;
168 : /** Sequence number of the next universal MIDI packet to send */
169 1 : uint16_t tx_ump_seq;
170 : /** Sequence number of the next universal MIDI packet to receive */
171 1 : uint16_t rx_ump_seq;
172 : /** Remote address of the peer */
173 1 : struct sockaddr_storage addr;
174 : /** Length of the peer's remote address */
175 1 : socklen_t addr_len;
176 : /** The Network MIDI2 endpoint to which this session belongs */
177 1 : struct netmidi2_ep *ep;
178 : #if defined(CONFIG_NETMIDI2_HOST_AUTH) || defined(__DOXYGEN__)
179 : /** The username to which this session belongs */
180 1 : const struct netmidi2_user *user;
181 : /** The crypto nonce used to authorize this session */
182 1 : char nonce[NETMIDI2_NONCE_SIZE];
183 : #endif
184 : /** The transmission buffer for that peer */
185 1 : struct net_buf *tx_buf;
186 : /** The transmission work for that peer */
187 1 : struct k_work tx_work;
188 : };
189 :
190 : /**
191 : * @brief Type of authentication in Network MIDI2
192 : */
193 1 : enum netmidi2_auth_type {
194 : /** No authentication required */
195 : NETMIDI2_AUTH_NONE,
196 : /** Authentication with a shared secret key */
197 : NETMIDI2_AUTH_SHARED_SECRET,
198 : /** Authentication with username and password */
199 : NETMIDI2_AUTH_USER_PASSWORD,
200 : };
201 :
202 : /**
203 : * @brief A Network MIDI2.0 Endpoint
204 : */
205 1 : struct netmidi2_ep {
206 : /** The endpoint name */
207 1 : const char *name;
208 : /** The endpoint product instance id */
209 1 : const char *piid;
210 : /** The local endpoint address */
211 : union {
212 0 : struct sockaddr addr;
213 0 : struct sockaddr_in addr4;
214 0 : struct sockaddr_in6 addr6;
215 1 : };
216 : /** The listening socket wrapped in a poll descriptor */
217 1 : struct pollfd pollsock;
218 : /** The function to call when data is received from a client */
219 1 : void (*rx_packet_cb)(struct netmidi2_session *session,
220 : const struct midi_ump ump);
221 : /** List of peers to this endpoint */
222 1 : struct netmidi2_session peers[CONFIG_NETMIDI2_HOST_MAX_CLIENTS];
223 : /** The type of authentication required to establish a session
224 : * with this host endpoint
225 : */
226 1 : enum netmidi2_auth_type auth_type;
227 : #if defined(CONFIG_NETMIDI2_HOST_AUTH) || defined(__DOXYGEN__)
228 : union {
229 : /** A shared authentication key */
230 1 : const char *shared_auth_secret;
231 : /** A list of users/passwords */
232 1 : const struct netmidi2_userlist *userlist;
233 0 : };
234 : #endif
235 : };
236 :
237 : /**
238 : * @brief Start hosting a network (UDP) Universal MIDI Packet endpoint
239 : * @param ep The network endpoint to start
240 : * @return 0 on success, -errno on error
241 : */
242 1 : int netmidi2_host_ep_start(struct netmidi2_ep *ep);
243 :
244 : /**
245 : * @brief Send a Universal MIDI Packet to all clients connected to the endpoint
246 : * @param ep The endpoint
247 : * @param[in] ump The packet to send
248 : */
249 1 : void netmidi2_broadcast(struct netmidi2_ep *ep, const struct midi_ump ump);
250 :
251 : /**
252 : * @brief Send a Universal MIDI Packet to a single client
253 : * @param sess The session identifying the single client
254 : * @param[in] ump The packet to send
255 : */
256 1 : void netmidi2_send(struct netmidi2_session *sess, const struct midi_ump ump);
257 :
258 : /** @} */
259 :
260 : #endif
|