1 /*
2  * Copyright (c) 2025 Titouan Christophe
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #include <zephyr/kernel.h>
7 #include <errno.h>
8 #include <stdio.h>
9 
10 #include <zephyr/audio/midi.h>
11 #include <zephyr/net/dns_sd.h>
12 #include <zephyr/net/midi2.h>
13 
14 #include <ump_stream_responder.h>
15 
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_REGISTER(net_midi2_sample, LOG_LEVEL_DBG);
18 
19 #define ACT_LED_NODE DT_NODELABEL(midi_green_led)
20 #define SERIAL_NODE  DT_NODELABEL(midi_serial)
21 
22 #if !DT_NODE_EXISTS(ACT_LED_NODE)
23 #define CONFIGURE_LED()
24 #define SET_LED(_state)
25 #else /* DT_NODE_EXISTS(ACT_LED_NODE) */
26 #include <zephyr/drivers/gpio.h>
27 
28 static const struct gpio_dt_spec act_led = GPIO_DT_SPEC_GET(ACT_LED_NODE, gpios);
29 
30 #define CONFIGURE_LED() gpio_pin_configure_dt(&act_led, GPIO_OUTPUT_INACTIVE)
31 #define SET_LED(_state) gpio_pin_set_dt(&act_led, (_state))
32 #endif  /* DT_NODE_EXISTS(ACT_LED_NODE) */
33 
34 
35 #if !DT_NODE_EXISTS(SERIAL_NODE)
36 #define send_external_midi1(...)
37 #else /* DT_NODE_EXISTS(SERIAL_NODE) */
38 #include <zephyr/drivers/uart.h>
39 
40 static const struct device *const uart_dev = DEVICE_DT_GET(SERIAL_NODE);
41 
send_external_midi1(const struct midi_ump ump)42 static inline void send_external_midi1(const struct midi_ump ump)
43 {
44 	/* Only send MIDI events aimed at the external output */
45 	if (UMP_GROUP(ump) != DT_REG_ADDR(DT_NODELABEL(ext_midi_out))) {
46 		return;
47 	}
48 
49 	switch (UMP_MIDI_COMMAND(ump)) {
50 	case UMP_MIDI_PROGRAM_CHANGE:
51 		SET_LED(1);
52 		uart_poll_out(uart_dev, UMP_MIDI_STATUS(ump));
53 		uart_poll_out(uart_dev, UMP_MIDI1_P1(ump));
54 		SET_LED(0);
55 		break;
56 
57 	case UMP_MIDI_NOTE_OFF:
58 	case UMP_MIDI_NOTE_ON:
59 	case UMP_MIDI_AFTERTOUCH:
60 	case UMP_MIDI_CONTROL_CHANGE:
61 	case UMP_MIDI_PITCH_BEND:
62 		SET_LED(1);
63 		uart_poll_out(uart_dev, UMP_MIDI_STATUS(ump));
64 		uart_poll_out(uart_dev, UMP_MIDI1_P1(ump));
65 		uart_poll_out(uart_dev, UMP_MIDI1_P2(ump));
66 		SET_LED(0);
67 		break;
68 	}
69 }
70 #endif /* DT_NODE_EXISTS(SERIAL_NODE) */
71 
72 
73 static const struct ump_endpoint_dt_spec ump_ep_dt =
74 	UMP_ENDPOINT_DT_SPEC_GET(DT_NODELABEL(midi2));
75 
handle_ump_stream(struct netmidi2_session * session,const struct midi_ump ump)76 static inline void handle_ump_stream(struct netmidi2_session *session,
77 				     const struct midi_ump ump)
78 {
79 	const struct ump_stream_responder_cfg responder_cfg =
80 		UMP_STREAM_RESPONDER(session, netmidi2_send, &ump_ep_dt);
81 	ump_stream_respond(&responder_cfg, ump);
82 }
83 
netmidi2_callback(struct netmidi2_session * session,const struct midi_ump ump)84 static void netmidi2_callback(struct netmidi2_session *session,
85 			      const struct midi_ump ump)
86 {
87 	switch (UMP_MT(ump)) {
88 	case UMP_MT_MIDI1_CHANNEL_VOICE:
89 		send_external_midi1(ump);
90 		break;
91 	case UMP_MT_UMP_STREAM:
92 		handle_ump_stream(session, ump);
93 		break;
94 	}
95 }
96 
97 #if defined(CONFIG_NET_SAMPLE_MIDI2_AUTH_NONE)
98 /* Simple Network MIDI 2.0 endpoint without authentication */
99 NETMIDI2_EP_DEFINE(midi_server, ump_ep_dt.name, NULL, 0);
100 
101 #elif defined(CONFIG_NET_SAMPLE_MIDI2_AUTH_SHARED_SECRET)
102 /* Network MIDI 2.0 endpoint with shared secret authentication */
103 BUILD_ASSERT(
104 	sizeof(CONFIG_NET_SAMPLE_MIDI2_SHARED_SECRET) > 1,
105 	"CONFIG_NET_SAMPLE_MIDI2_SHARED_SECRET must be not empty"
106 );
107 
108 NETMIDI2_EP_DEFINE_WITH_AUTH(midi_server, ump_ep_dt.name, NULL, 0,
109 	CONFIG_NET_SAMPLE_MIDI2_SHARED_SECRET);
110 
111 #elif defined(CONFIG_NET_SAMPLE_MIDI2_AUTH_USER_PASSWORD)
112 /* Network MIDI 2.0 endpoint with a single user/password*/
113 BUILD_ASSERT(
114 	sizeof(CONFIG_NET_SAMPLE_MIDI2_USERNAME) > 1,
115 	"CONFIG_NET_SAMPLE_MIDI2_USERNAME must be not empty"
116 );
117 BUILD_ASSERT(
118 	sizeof(CONFIG_NET_SAMPLE_MIDI2_PASSWORD) > 1,
119 	"CONFIG_NET_SAMPLE_MIDI2_PASSWORD must be not empty"
120 );
121 
122 NETMIDI2_EP_DEFINE_WITH_USERS(midi_server, ump_ep_dt.name, NULL, 0,
123 	{.name = CONFIG_NET_SAMPLE_MIDI2_USERNAME,
124 	 .password = CONFIG_NET_SAMPLE_MIDI2_PASSWORD});
125 
126 #endif
127 
128 DNS_SD_REGISTER_SERVICE(midi_dns, CONFIG_NET_HOSTNAME "-" CONFIG_BOARD,
129 			"_midi2", "_udp", "local", DNS_SD_EMPTY_TXT,
130 			&midi_server.addr4.sin_port);
131 
main(void)132 int main(void)
133 {
134 	CONFIGURE_LED();
135 
136 	midi_server.rx_packet_cb = netmidi2_callback;
137 	netmidi2_host_ep_start(&midi_server);
138 
139 	return 0;
140 }
141