17
17
#define DEBUG_PRINT (fmt , args ...)
18
18
#endif
19
19
20
- uint8_t frame_buffer [4196 ]; // buffer a whole frame of MSP commands until we get a draw command
21
- uint8_t message_buffer [256 ]; // only needs to be the maximum size of an MSP packet, we only care to fwd MSP
22
- uint8_t cursor = 0 ;
23
- uint32_t fb_cursor = 0 ;
20
+ typedef struct msp_cache_entry_s {
21
+ struct timespec time ;
22
+ msp_msg_t message ;
23
+ } msp_cache_entry_t ;
24
+
25
+ static msp_cache_entry_t * msp_message_cache [256 ]; // make a slot for all possible messages
26
+
27
+ static uint8_t frame_buffer [4196 ]; // buffer a whole frame of MSP commands until we get a draw command
28
+ static uint32_t fb_cursor = 0 ;
29
+
30
+ static uint8_t rx_message_buffer [256 ]; // only needs to be the maximum size of an MSP packet, we only care to fwd MSP
31
+ static uint8_t rx_cursor = 0 ;
32
+
33
+ static uint8_t tx_message_buffer [256 ];
34
+ static uint8_t tx_cursor = 0 ;
24
35
25
36
int pty_fd ;
26
37
int serial_fd ;
27
38
int socket_fd ;
28
39
29
40
static volatile sig_atomic_t quit = 0 ;
41
+ static uint8_t serial_passthrough = 1 ;
30
42
31
43
static void sig_handler (int _ )
32
44
{
33
45
quit = 1 ;
34
46
}
35
47
36
- static void msp_callback (msp_msg_t * msp_message )
37
- {
38
- DEBUG_PRINT ("MSP cmd %d with data len %d \n" , msp_message -> cmd , msp_message -> size );
48
+ static uint8_t cache_msp_message (msp_msg_t * msp_message ) {
49
+ // 0 -> cache overwritten
50
+ // 1 -> freshly cached
51
+ uint8_t retval = 0 ;
52
+ msp_cache_entry_t * cache_message = msp_message_cache [msp_message -> cmd ];
53
+ if (cache_message == NULL ) {
54
+ DEBUG_PRINT ("no entry for msg %d, allocating\n" , msp_message -> cmd );
55
+ cache_message = calloc (1 , sizeof (msp_cache_entry_t ));
56
+ msp_message_cache [msp_message -> cmd ] = cache_message ;
57
+ retval = 1 ;
58
+ }
59
+ memcpy (& cache_message -> message , msp_message , sizeof (msp_message ));
60
+ clock_gettime (CLOCK_MONOTONIC , & cache_message -> time );
61
+ return retval ;
62
+ }
39
63
40
- // Process a received MSP message and decide whether to send it to the PTY (DJI) or UDP port (MSP-OSD on Goggles)
64
+ static int16_t msp_msg_from_cache (uint8_t msg_buffer [], uint8_t cmd_id ) {
65
+ // returns size of message or -1
66
+ msp_cache_entry_t * cache_message = msp_message_cache [cmd_id ];
67
+ if (cache_message == NULL ) {
68
+ // cache missed, return -1 to trigger a serial send
69
+ return -1 ;
70
+ } else {
71
+ // cache hit, let's see if it's too old
72
+ // messages we care to expire after second boundary
73
+ if (cmd_id == MSP_CMD_ANALOG
74
+ || cmd_id == MSP_CMD_STATUS
75
+ || cmd_id == MSP_CMD_BATTERY_STATE ) {
76
+ struct timespec now ;
77
+ clock_gettime (CLOCK_MONOTONIC , & now );
78
+ if (now .tv_sec > cache_message -> time .tv_sec ) {
79
+ // message is too old, invalidate cache and force a resend
80
+ free (cache_message );
81
+ msp_message_cache [cmd_id ] = 0 ;
82
+ return -1 ;
83
+ }
84
+ }
85
+ return msp_data_from_msg (msg_buffer , & cache_message -> message );
86
+ }
87
+ }
88
+
89
+ static void rx_msp_callback (msp_msg_t * msp_message )
90
+ {
91
+ // Process a received MSP message from FC and decide whether to send it to the PTY (DJI) or UDP port (MSP-OSD on Goggles)
92
+ DEBUG_PRINT ("FC->AU MSP msg %d with data len %d \n" , msp_message -> cmd , msp_message -> size );
41
93
if (msp_message -> cmd == MSP_CMD_DISPLAYPORT ) {
94
+ // This was an MSP DisplayPort message, so buffer it until we get a whole frame.
42
95
if (fb_cursor > sizeof (frame_buffer )) {
43
96
printf ("Exhausted frame buffer!\n" );
44
97
}
45
- memcpy (& frame_buffer [fb_cursor ], message_buffer , cursor );
46
- fb_cursor += cursor ;
47
- cursor = 0 ;
48
- if (msp_message -> payload [0 ] == 4 ) { // Draw command
98
+ memcpy (& frame_buffer [fb_cursor ], rx_message_buffer , rx_cursor );
99
+ fb_cursor += rx_cursor ;
100
+ rx_cursor = 0 ;
101
+ if (msp_message -> payload [0 ] == 4 ) {
102
+ // Once we have a whole frame of data, send it to the goggles.
49
103
write (socket_fd , frame_buffer , fb_cursor );
50
104
DEBUG_PRINT ("DRAW! wrote %d bytes\n" , fb_cursor );
51
105
fb_cursor = 0 ;
52
106
}
53
107
} else {
54
- write (pty_fd , message_buffer , cursor );
55
- cursor = 0 ;
108
+ // This isn't an MSP DisplayPort message, so send it to either DJI directly or to the cache.
109
+ if (serial_passthrough ) {
110
+ // Serial passthrough is on, so send it straight to DJI.
111
+ write (pty_fd , rx_message_buffer , rx_cursor );
112
+ } else {
113
+ // Serial passthrough is off, so cache the response we got.
114
+ if (cache_msp_message (msp_message )) {
115
+ // 1 -> cache miss, so this message expired or hasn't been seen. this means DJI is waiting for it, so send it over
116
+ write (pty_fd , rx_message_buffer , rx_cursor );
117
+ }
118
+ }
119
+ rx_cursor = 0 ;
120
+ }
121
+ }
122
+
123
+ static void tx_msp_callback (msp_msg_t * msp_message )
124
+ {
125
+ // We got a valid message from DJI asking for something. See if there's a response in the cache or not.
126
+ // We can only get here if serial passthrough is off and caching is on, so no need to check again.
127
+ DEBUG_PRINT ("DJI->FC MSP msg %d with data len %d \n" , msp_message -> cmd , msp_message -> size );
128
+ uint8_t send_buffer [256 ];
129
+ uint16_t size ;
130
+ if (0 < (size = msp_msg_from_cache (send_buffer , msp_message -> cmd ))) {
131
+ // cache hit, so write the cached message straight back to DJI
132
+ DEBUG_PRINT ("DJI->FC MSP CACHE HIT %d with data len %d \n" , msp_message -> cmd , size );
133
+ write (pty_fd , send_buffer , size );
134
+ } else {
135
+ // cache miss, so write the DJI request to serial and wait for the FC to come back.
136
+ DEBUG_PRINT ("DJI->FC MSP CACHE MISS %d\n" ,msp_message -> cmd );
137
+ write (serial_fd , tx_message_buffer , tx_cursor );
56
138
}
139
+ tx_cursor = 0 ;
57
140
}
58
141
59
142
int main (int argc , char * argv []) {
60
143
int opt ;
61
144
uint8_t fast_serial = 0 ;
62
- uint8_t serial_passthrough = 1 ;
63
- uint8_t poll_manually = 0 ;
64
- uint8_t msg_flip = 0 ;
145
+ uint8_t msp_command_number = 0 ;
65
146
while ((opt = getopt (argc , argv , "fsp" )) != -1 ){
66
147
switch (opt ){
67
148
case 'f' :
@@ -70,11 +151,7 @@ int main(int argc, char *argv[]) {
70
151
break ;
71
152
case 's' :
72
153
serial_passthrough = 0 ;
73
- printf ("Disabling serial passthrough, enabling filtering\n" );
74
- break ;
75
- case 'p' :
76
- poll_manually = 1 ;
77
- printf ("Enabling manual MSP polling every 500ms\n" );
154
+ printf ("Disabling serial passthrough, enabling caching\n" );
78
155
break ;
79
156
case '?' :
80
157
printf ("unknown option: %c\n" , optopt );
@@ -83,7 +160,7 @@ int main(int argc, char *argv[]) {
83
160
}
84
161
85
162
if ((argc - optind ) < 2 ) {
86
- printf ("usage: msp_displayport_mux [-f] [-s] [-p] ipaddr serial_port [pty_target]\n-s : enable serial filtering \n-f : 230400 baud serial\n-p: enable manual polling \n" );
163
+ printf ("usage: msp_displayport_mux [-f] [-s] [-p] ipaddr serial_port [pty_target]\n-s : enable serial caching \n-f : 230400 baud serial\n" );
87
164
return 0 ;
88
165
}
89
166
@@ -92,8 +169,10 @@ int main(int argc, char *argv[]) {
92
169
signal (SIGINT , sig_handler );
93
170
struct pollfd poll_fds [2 ];
94
171
const char * pty_name_ptr ;
95
- msp_state_t * msp_state = calloc (1 , sizeof (msp_state_t ));
96
- msp_state -> cb = & msp_callback ;
172
+ msp_state_t * rx_msp_state = calloc (1 , sizeof (msp_state_t ));
173
+ msp_state_t * tx_msp_state = calloc (1 , sizeof (msp_state_t ));
174
+ rx_msp_state -> cb = & rx_msp_callback ;
175
+ tx_msp_state -> cb = & tx_msp_callback ;
97
176
serial_fd = open_serial_port (serial_port , fast_serial ? B230400 : B115200 );
98
177
if (serial_fd <= 0 ) {
99
178
printf ("Failed to open serial port!\n" );
@@ -115,47 +194,49 @@ int main(int argc, char *argv[]) {
115
194
poll_fds [1 ].fd = pty_fd ;
116
195
poll_fds [0 ].events = POLLIN ;
117
196
poll_fds [1 ].events = POLLIN ;
197
+
118
198
poll (poll_fds , 2 , 250 );
119
- // We got serial data, process it as MSP data.
199
+ // We got inbound serial data, process it as MSP data.
120
200
if (0 < (serial_data_size = read (serial_fd , serial_data , sizeof (serial_data )))) {
121
201
DEBUG_PRINT ("RECEIVED data! length %d\n" , serial_data_size );
122
202
for (ssize_t i = 0 ; i < serial_data_size ; i ++ ) {
123
- if (msp_process_data (msp_state , serial_data [i ]) == 0 ) {
124
- // 0 -> MSP data was valid, so buffer it to forward on later
125
- message_buffer [ cursor ] = serial_data [i ];
126
- cursor ++ ;
203
+ if (msp_process_data (rx_msp_state , serial_data [i ]) == 0 ) {
204
+ // 0 -> MSP data was valid, so buffer it to forward on to either goggles or DJI later
205
+ rx_message_buffer [ rx_cursor ] = serial_data [i ];
206
+ rx_cursor ++ ;
127
207
} else {
128
- cursor = 0 ;
208
+ rx_cursor = 0 ;
129
209
}
130
210
}
131
211
}
132
-
133
- // If serial passthrough is enabled, send the message through verbatim.
134
- if (0 < (serial_data_size = read (pty_fd , serial_data , sizeof (serial_data ))) && serial_passthrough ) {
135
- DEBUG_PRINT ("SEND data! length %d\n" , serial_data_size );
136
- for (ssize_t i = 0 ; i < serial_data_size ; i ++ ) {
137
- DEBUG_PRINT ("%02X " , serial_data [i ]);
138
- }
139
- DEBUG_PRINT ("\n" );
140
- write (serial_fd , & serial_data , serial_data_size );
141
- }
142
-
143
- // If manual MSP polling is enabled, poll only for a few messages: arming status and battery voltage
144
- clock_gettime (CLOCK_MONOTONIC , & now );
145
- if (poll_manually && (now .tv_sec > last .tv_sec || now .tv_nsec > (last .tv_nsec + 50000000000 ))) {
146
- construct_msp_command (& serial_data , msg_flip ? MSP_CMD_STATUS : MSP_CMD_BATTERY_STATE , 0 , 0 );
147
- msg_flip = !msg_flip ;
148
- DEBUG_PRINT ("Polling: " );
149
- for (ssize_t i = 0 ; i < 6 ; i ++ ) {
150
- DEBUG_PRINT ("%02X " , serial_data [i ]);
212
+ // We got data from DJI (the pty), so see what to do next:
213
+ if (0 < (serial_data_size = read (pty_fd , serial_data , sizeof (serial_data )))) {
214
+ if (serial_passthrough ) {
215
+ // If serial passthrough is enabled, send the message through verbatim.
216
+ DEBUG_PRINT ("SEND data! length %d\n" , serial_data_size );
217
+ for (ssize_t i = 0 ; i < serial_data_size ; i ++ ) {
218
+ DEBUG_PRINT ("%02X " , serial_data [i ]);
219
+ }
220
+ DEBUG_PRINT ("\n" );
221
+ write (serial_fd , & serial_data , serial_data_size );
222
+ } else {
223
+ // Otherwise, queue it up for processing by the MSP layer.
224
+ DEBUG_PRINT ("SEND data to MSP buffer! length %d\n" , serial_data_size );
225
+ for (ssize_t i = 0 ; i < serial_data_size ; i ++ ) {
226
+ if (msp_process_data (tx_msp_state , serial_data [i ]) == 0 ) {
227
+ // 0 -> MSP data was valid, so buffer it to forward on later
228
+ tx_message_buffer [tx_cursor ] = serial_data [i ];
229
+ tx_cursor ++ ;
230
+ } else {
231
+ tx_cursor = 0 ;
232
+ }
233
+ }
151
234
}
152
- DEBUG_PRINT ("\n" );
153
- write (serial_fd , & serial_data , 6 );
154
235
}
155
- last = now ;
156
236
}
157
237
close (serial_fd );
158
238
close (pty_fd );
159
239
close (socket_fd );
160
- free (msp_state );
240
+ free (rx_msp_state );
241
+ free (tx_msp_state );
161
242
}
0 commit comments