2 * This file is part of INAV Project.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
6 * You can obtain one at http://mozilla.org/MPL/2.0/.
8 * Alternatively, the contents of this file may be used under the terms
9 * of the GNU General Public License Version 3, as described below:
11 * This file is free software: you may copy, redistribute and/or modify
12 * it under the terms of the GNU General Public License as published by the
13 * Free Software Foundation, either version 3 of the License, or (at your
14 * option) any later version.
16 * This file is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
19 * Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see http://www.gnu.org/licenses/.
25 #include "serial_proxy.h"
27 #if defined(SITL_BUILD)
34 #include <sys/select.h>
40 #include <sys/types.h>
46 #include "drivers/time.h"
47 #include "msp/msp_serial.h"
48 #include "msp/msp_protocol.h"
49 #include "common/crc.h"
52 #include <sys/ioctl.h>
55 # define __BSD_VISIBLE 1
59 #include <asm/termbits.h>
61 #include <asm-generic/ioctls.h>
68 #include <IOKit/serial/ioss.h>
71 #include "drivers/serial_tcp.h"
74 #define SYM_PROTO_V1 'M'
75 #define SYM_PROTO_V2 'X'
76 #define SYM_FROM_MWC '>'
77 #define SYM_TO_MWC '<'
78 #define SYM_UNSUPPORTED '!'
80 #define JUMBO_FRAME_MIN_SIZE 255
81 #define MAX_MSP_MESSAGE 1024
82 #define RX_CONFIG_SIZE 24
91 DS_PAYLOAD_LENGTH_JUMBO_LOW
,
92 DS_PAYLOAD_LENGTH_JUMBO_HIGH
,
93 DS_PAYLOAD_LENGTH_V2_LOW
,
94 DS_PAYLOAD_LENGTH_V2_HIGH
,
105 static TDecoderState decoderState
= DS_IDLE
;
113 static TRXConfigState rxConfigState
= RXC_IDLE
;
115 static int message_length_expected
;
116 static unsigned char message_buffer
[MAX_MSP_MESSAGE
];
117 static int message_length_received
;
118 static int unsupported
;
120 static int message_direction
;
121 static uint8_t message_checksum
;
122 static int reqCount
= 0;
123 static uint16_t rssi
= 0;
124 static uint8_t rxConfigBuffer
[RX_CONFIG_SIZE
];
126 static timeMs_t lastWarning
= 0;
128 int serialUartIndex
= -1;
129 char serialPort
[64] = "";
130 int serialBaudRate
= 115200;
131 OptSerialStopBits_e serialStopBits
= OPT_SERIAL_STOP_BITS_ONE
; //0:None|1:One|2:OnePointFive|3:Two
132 OptSerialParity_e serialParity
= OPT_SERIAL_PARITY_NONE
;
133 bool serialFCProxy
= false;
135 #define FC_PROXY_REQUEST_PERIOD_MIN_MS 20 //inav can handle 100 msp messages per second max
136 #define FC_PROXY_REQUEST_PERIOD_TIMEOUT_MS 200
137 #define FC_PROXY_REQUEST_PERIOD_RSSI_MS 300
138 #define SERIAL_BUFFER_SIZE 256
140 #if defined(__CYGWIN__)
142 static HANDLE hSerial
;
147 static bool connected
= false;
148 static bool started
= false;
150 static timeMs_t nextProxyRequestTimeout
= 0;
151 static timeMs_t nextProxyRequestMin
= 0;
152 static timeMs_t nextProxyRequestRssi
= 0;
154 static timeMs_t lastVisit
= 0;
156 #if !defined(__CYGWIN__)
157 #if !defined( __linux__) && !defined(__APPLE__)
158 static int rate_to_constant(int baudrate
)
163 #define B(x) case x: return B##x
191 static void close_serial(void)
194 ioctl(fd
, TCFLSH
, TCIOFLUSH
);
196 tcflush(fd
, TCIOFLUSH
);
201 static int set_fd_speed(void)
205 // Just user BOTHER for everything (allows non-standard speeds)
207 if ((res
= ioctl(fd
, TCGETS2
, &t
)) != -1) {
210 t
.c_ospeed
= t
.c_ispeed
= serialBaudRate
;
211 res
= ioctl(fd
, TCSETS2
, &t
);
214 speed_t speed
= serialBaudRate
;
215 res
= ioctl(fd
, IOSSIOSPEED
, &speed
);
217 int speed
= rate_to_constant(serialBaudRate
);
219 if (tcgetattr(fd
, &term
)) return -1;
223 if (cfsetispeed(&term
, speed
) != -1) {
224 cfsetospeed(&term
, speed
);
225 res
= tcsetattr(fd
, TCSANOW
, &term
);
228 memset(&term
, 0, sizeof(term
));
229 res
= (tcgetattr(fd
, &term
));
236 static int set_attributes(void)
239 memset (&tio
, 0, sizeof(tio
));
242 res
= ioctl(fd
, TCGETS
, &tio
);
244 res
= tcgetattr(fd
, &tio
);
248 tio
.c_iflag
&= ~(IGNBRK
| BRKINT
| PARMRK
| ISTRIP
| INLCR
| IGNCR
| ICRNL
| IXON
);
249 tio
.c_oflag
&= ~OPOST
;
250 tio
.c_lflag
&= ~(ECHO
| ECHONL
| ICANON
| ISIG
| IEXTEN
);
251 tio
.c_cflag
&= ~(CSIZE
| PARENB
);
252 tio
.c_cflag
|= CS8
| CREAD
| CLOCAL
;
256 switch (serialStopBits
) {
257 case OPT_SERIAL_STOP_BITS_ONE
:
258 tio
.c_cflag
&= ~CSTOPB
;
260 case OPT_SERIAL_STOP_BITS_TWO
:
261 tio
.c_cflag
|= CSTOPB
;
263 case OPT_SERIAL_STOP_BITS_INVALID
:
267 switch (serialParity
) {
268 case OPT_SERIAL_PARITY_EVEN
:
269 tio
.c_cflag
|= PARENB
;
270 tio
.c_cflag
&= ~PARODD
;
272 case OPT_SERIAL_PARITY_NONE
:
273 tio
.c_cflag
&= ~PARENB
;
274 tio
.c_cflag
&= ~PARODD
;
276 case OPT_SERIAL_PARITY_ODD
:
277 tio
.c_cflag
|= PARENB
;
278 tio
.c_cflag
|= PARODD
;
280 case OPT_SERIAL_PARITY_INVALID
:
284 res
= ioctl(fd
, TCSETS
, &tio
);
286 res
= tcsetattr(fd
, TCSANOW
, &tio
);
290 res
= set_fd_speed();
296 void serialProxyInit(void)
298 char portName
[64 + 20];
299 if ( strlen(serialPort
) < 1) {
304 #if defined(__CYGWIN__)
305 sprintf(portName
, "\\\\.\\%s", serialPort
);
307 hSerial
= CreateFile(portName
,
308 GENERIC_READ
| GENERIC_WRITE
,
312 FILE_ATTRIBUTE_NORMAL
,
315 if (hSerial
== INVALID_HANDLE_VALUE
) {
316 if (GetLastError() == ERROR_FILE_NOT_FOUND
) {
317 fprintf(stderr
, "[SERIALPROXY] ERROR: Sserial port was not attached. Reason: %s not available.\n", portName
);
319 fprintf(stderr
, "[SERIALPROXY] ERROR: Can not connect to serial port, unknown error.\n");
323 DCB dcbSerialParams
= { 0 };
324 if (!GetCommState(hSerial
, &dcbSerialParams
)) {
325 fprintf(stderr
, "[SERIALPROXY] ALERT: failed to get current serial parameters!\n");
327 dcbSerialParams
.BaudRate
= serialBaudRate
;
328 dcbSerialParams
.ByteSize
= 8;
330 // Disable software flow control (XON/XOFF)
331 dcbSerialParams
.fOutX
= FALSE
;
332 dcbSerialParams
.fInX
= FALSE
;
334 // Disable hardware flow control (RTS/CTS)
335 dcbSerialParams
.fRtsControl
= RTS_CONTROL_DISABLE
;
336 dcbSerialParams
.fDtrControl
= DTR_CONTROL_DISABLE
;
338 // Disable any special processing of bytes
339 dcbSerialParams
.fBinary
= TRUE
;
341 switch (serialStopBits
) {
342 case OPT_SERIAL_STOP_BITS_ONE
:
343 dcbSerialParams
.StopBits
= ONESTOPBIT
;
345 case OPT_SERIAL_STOP_BITS_TWO
:
346 dcbSerialParams
.StopBits
= TWOSTOPBITS
;
348 case OPT_SERIAL_STOP_BITS_INVALID
:
352 switch (serialParity
) {
353 case OPT_SERIAL_PARITY_EVEN
:
354 dcbSerialParams
.Parity
= EVENPARITY
;
356 case OPT_SERIAL_PARITY_NONE
:
357 dcbSerialParams
.Parity
= NOPARITY
;
359 case OPT_SERIAL_PARITY_ODD
:
360 dcbSerialParams
.Parity
= ODDPARITY
;
362 case OPT_SERIAL_PARITY_INVALID
:
366 if (!SetCommState(hSerial
, &dcbSerialParams
)) {
367 fprintf(stderr
, "[SERIALPROXY] ALERT: Could not set Serial Port parameters\n");
369 COMMTIMEOUTS comTimeOut
;
370 comTimeOut
.ReadIntervalTimeout
= MAXDWORD
;
371 comTimeOut
.ReadTotalTimeoutMultiplier
= 0;
372 comTimeOut
.ReadTotalTimeoutConstant
= 0;
373 comTimeOut
.WriteTotalTimeoutMultiplier
= 0;
374 comTimeOut
.WriteTotalTimeoutConstant
= 0;
375 SetCommTimeouts(hSerial
, &comTimeOut
);
380 strcpy(portName
, serialPort
); // because, windows ...
381 fd
= open(serialPort
, O_RDWR
| O_NOCTTY
);
384 res
= set_attributes();
386 fprintf(stderr
, "[SERIALPROXY] ALERT: Failed to configure device: %s %s\n", portName
, strerror(errno
));
391 fprintf(stderr
, "[SERIALPROXY] ERROR: Can not connect to serial port %s\n", portName
);
397 if ( !serialFCProxy
) {
398 fprintf(stderr
, "[SERIALPROXY] Connected %s to UART%d\n", portName
, serialUartIndex
);
400 fprintf(stderr
, "[SERIALPROXY] Using proxy flight controller on %s\n", portName
);
404 void serialProxyStart(void)
409 void serialProxyClose(void)
413 #if defined(__CYGWIN__)
414 CloseHandle(hSerial
);
419 nextProxyRequestTimeout
= 0;
420 nextProxyRequestMin
= 0;
421 nextProxyRequestRssi
= 0;
427 static bool canOutputWarning(void)
429 if ( millis() > lastWarning
) {
430 lastWarning
= millis() + 5000;
436 int serialProxyReadData(unsigned char *buffer
, unsigned int nbChar
)
438 if (!connected
) return 0;
440 #if defined(__CYGWIN__)
445 ClearCommError(hSerial
, &errors
, &status
);
446 if (status
.cbInQue
> 0) {
447 unsigned int toRead
= (status
.cbInQue
> nbChar
) ? nbChar
: status
.cbInQue
;
448 if (ReadFile(hSerial
, buffer
, toRead
, &bytesRead
, NULL
) && (bytesRead
!= 0)) {
454 if (nbChar
== 0) return 0;
455 int bytesRead
= read(fd
, buffer
, nbChar
);
460 bool serialProxyWriteData(unsigned char *buffer
, unsigned int nbChar
)
462 if (!connected
) return false;
464 #if defined(__CYGWIN__)
468 if (!WriteFile(hSerial
, (void *)buffer
, nbChar
, &bytesSent
, 0)) {
469 ClearCommError(hSerial
, &errors
, &status
);
470 if ( canOutputWarning() ) {
471 fprintf(stderr
, "[SERIALPROXY] ERROR: Can not write to serial port\n");
475 if ( bytesSent
!= nbChar
) {
476 if ( canOutputWarning() ) {
477 fprintf(stderr
, "[SERIALPROXY] ERROR: Can not write to serial port\n");
481 ssize_t l
= write(fd
, buffer
, nbChar
);
482 if ( l
!= (ssize_t
)nbChar
) {
483 if ( canOutputWarning() ) {
484 fprintf(stderr
, "[SERIALPROXY] ERROR: Can not write to serial port\n");
492 bool serialProxyIsConnected(void)
497 static void mspSendCommand(int cmd
, unsigned char *data
, int dataLen
)
499 uint8_t msgBuf
[MAX_MSP_MESSAGE
] = { '$', 'X', '<' };
502 mspHeaderV2_t
*hdrV2
= (mspHeaderV2_t
*)&msgBuf
[msgLen
];
505 hdrV2
->size
= dataLen
;
506 msgLen
+= sizeof(mspHeaderV2_t
);
508 for ( int i
= 0; i
< dataLen
; i
++ ) {
509 msgBuf
[msgLen
++] = data
[i
];
512 uint8_t crc
= crc8_dvb_s2_update(0, (uint8_t *)hdrV2
, sizeof(mspHeaderV2_t
));
513 crc
= crc8_dvb_s2_update(crc
, data
, dataLen
);
514 msgBuf
[msgLen
] = crc
;
517 serialProxyWriteData((unsigned char *)&msgBuf
, msgLen
);
520 static void mspRequestChannels(void)
522 mspSendCommand(MSP_RC
, NULL
, 0);
525 static void mspRequestRssi(void)
527 mspSendCommand(MSP_ANALOG
, NULL
, 0);
530 static void requestRXConfigState(void)
532 mspSendCommand(MSP_RX_CONFIG
, NULL
, 0);
533 rxConfigState
= RXC_RQ
;
534 fprintf(stderr
, "[SERIALPROXY] Requesting RX config from proxy FC...\n");
537 static void processMessage(void)
539 if ( code
== MSP_RC
) {
540 if ( reqCount
> 0 ) reqCount
--;
541 int count
= message_length_received
/ 2;
542 if ( count
<= MAX_SUPPORTED_RC_CHANNEL_COUNT
) {
543 uint16_t *channels
= (uint16_t *)(&message_buffer
[0]);
545 uint v
= channels
[2];
546 channels
[2] = channels
[3];
549 rxSimSetChannelValue(channels
, count
);
552 } else if ( code
== MSP_ANALOG
) {
553 if ( reqCount
> 0 ) reqCount
--;
554 if ( message_length_received
>= 7 ) {
555 rssi
= *((uint16_t *)(&message_buffer
[3]));
556 rxSimSetRssi( rssi
);
558 } else if ( code
== MSP_RX_CONFIG
) {
559 memcpy( &(rxConfigBuffer
[0]), &(message_buffer
[0]), RX_CONFIG_SIZE
);
560 *((uint16_t *) & (rxConfigBuffer
[1])) = 2500; //maxcheck
561 *((uint16_t *) & (rxConfigBuffer
[5])) = 500; //mincheck
563 mspSendCommand( MSP_SET_RX_CONFIG
, rxConfigBuffer
, RX_CONFIG_SIZE
);
564 rxConfigState
= RXC_DONE
;
565 fprintf(stderr
, "[SERIALPROXY] Setting RX config on proxy FC...\n");
569 static void decodeProxyMessages(unsigned char *data
, int len
)
571 for (int i
= 0; i
< len
; i
++) {
572 switch (decoderState
) {
573 case DS_IDLE
: // sync char 1
574 if (data
[i
] == SYM_BEGIN
) {
575 decoderState
= DS_PROTO_IDENTIFIER
;
579 case DS_PROTO_IDENTIFIER
: // sync char 2
582 decoderState
= DS_DIRECTION_V1
;
585 decoderState
= DS_DIRECTION_V2
;
589 decoderState
= DS_IDLE
;
593 case DS_DIRECTION_V1
: // direction (should be >)
595 case DS_DIRECTION_V2
:
599 message_direction
= 1;
602 message_direction
= 0;
604 case SYM_UNSUPPORTED
:
608 decoderState
= decoderState
== DS_DIRECTION_V1
? DS_PAYLOAD_LENGTH_V1
: DS_FLAG_V2
;
613 decoderState
= DS_CODE_V2_LOW
;
616 case DS_PAYLOAD_LENGTH_V1
:
617 message_length_expected
= data
[i
];
619 if (message_length_expected
== JUMBO_FRAME_MIN_SIZE
) {
620 decoderState
= DS_CODE_JUMBO_V1
;
622 message_length_received
= 0;
623 decoderState
= DS_CODE_V1
;
627 case DS_PAYLOAD_LENGTH_V2_LOW
:
628 message_length_expected
= data
[i
];
629 decoderState
= DS_PAYLOAD_LENGTH_V2_HIGH
;
632 case DS_PAYLOAD_LENGTH_V2_HIGH
:
633 message_length_expected
|= data
[i
] << 8;
634 message_length_received
= 0;
635 if (message_length_expected
<= MAX_MSP_MESSAGE
) {
636 decoderState
= message_length_expected
> 0 ? DS_PAYLOAD_V2
: DS_CHECKSUM_V2
;
639 decoderState
= DS_IDLE
;
644 case DS_CODE_JUMBO_V1
:
646 if (message_length_expected
> 0) {
648 if (decoderState
== DS_CODE_JUMBO_V1
) {
649 decoderState
= DS_PAYLOAD_LENGTH_JUMBO_LOW
;
651 decoderState
= DS_PAYLOAD_V1
;
655 decoderState
= DS_CHECKSUM_V1
;
661 decoderState
= DS_CODE_V2_HIGH
;
664 case DS_CODE_V2_HIGH
:
665 code
|= data
[i
] << 8;
666 decoderState
= DS_PAYLOAD_LENGTH_V2_LOW
;
669 case DS_PAYLOAD_LENGTH_JUMBO_LOW
:
670 message_length_expected
= data
[i
];
671 decoderState
= DS_PAYLOAD_LENGTH_JUMBO_HIGH
;
674 case DS_PAYLOAD_LENGTH_JUMBO_HIGH
:
675 message_length_expected
|= data
[i
] << 8;
676 message_length_received
= 0;
677 decoderState
= DS_PAYLOAD_V1
;
682 message_buffer
[message_length_received
] = data
[i
];
683 message_length_received
++;
685 if (message_length_received
>= message_length_expected
) {
686 decoderState
= decoderState
== DS_PAYLOAD_V1
? DS_CHECKSUM_V1
: DS_CHECKSUM_V2
;
691 if (message_length_expected
>= JUMBO_FRAME_MIN_SIZE
) {
692 message_checksum
= JUMBO_FRAME_MIN_SIZE
;
694 message_checksum
= message_length_expected
;
696 message_checksum
^= code
;
697 if (message_length_expected
>= JUMBO_FRAME_MIN_SIZE
) {
698 message_checksum
^= message_length_expected
& 0xFF;
699 message_checksum
^= (message_length_expected
& 0xFF00) >> 8;
701 for (int ii
= 0; ii
< message_length_received
; ii
++) {
702 message_checksum
^= message_buffer
[ii
];
704 if (message_checksum
== data
[i
]) {
707 decoderState
= DS_IDLE
;
711 message_checksum
= 0;
712 message_checksum
= crc8_dvb_s2(message_checksum
, 0); // flag
713 message_checksum
= crc8_dvb_s2(message_checksum
, code
& 0xFF);
714 message_checksum
= crc8_dvb_s2(message_checksum
, (code
& 0xFF00) >> 8);
715 message_checksum
= crc8_dvb_s2(message_checksum
, message_length_expected
& 0xFF);
716 message_checksum
= crc8_dvb_s2(message_checksum
, (message_length_expected
& 0xFF00) >> 8);
717 for (int ii
= 0; ii
< message_length_received
; ii
++) {
718 message_checksum
= crc8_dvb_s2(message_checksum
, message_buffer
[ii
]);
720 if (message_checksum
== data
[i
]) {
723 decoderState
= DS_IDLE
;
732 void serialProxyProcess(void)
735 if (!started
) return;
736 if (!connected
) return;
738 if ((millis() - lastVisit
) > 500) {
739 if ( lastVisit
> 0 ) {
740 fprintf(stderr
, "timeout=%dms\n", millis() - lastVisit
);
743 lastVisit
= millis();
745 if ( serialFCProxy
) {
746 //first we have to change FC min_check and max_check thresholds to avoid activating stick commands on proxy FC
747 if ( rxConfigState
== RXC_IDLE
) {
748 requestRXConfigState();
749 } else if ( rxConfigState
== RXC_DONE
) {
750 if ( (nextProxyRequestTimeout
<= millis()) || ((reqCount
== 0) && (nextProxyRequestMin
<= millis()))) {
751 nextProxyRequestTimeout
= millis() + FC_PROXY_REQUEST_PERIOD_TIMEOUT_MS
;
752 nextProxyRequestMin
= millis() + FC_PROXY_REQUEST_PERIOD_MIN_MS
;
753 mspRequestChannels();
757 if ( nextProxyRequestRssi
<= millis()) {
758 nextProxyRequestRssi
= millis() + FC_PROXY_REQUEST_PERIOD_RSSI_MS
;
764 unsigned char buf
[SERIAL_BUFFER_SIZE
];
765 int count
= serialProxyReadData(buf
, SERIAL_BUFFER_SIZE
);
766 if (count
== 0) return;
767 decodeProxyMessages(buf
, count
);
771 if ( serialUartIndex
== -1 ) return;
772 unsigned char buf
[SERIAL_BUFFER_SIZE
];
773 uint32_t avail
= tcpRXBytesFree(serialUartIndex
- 1);
774 if ( avail
== 0 ) return;
775 if (avail
> SERIAL_BUFFER_SIZE
) avail
= SERIAL_BUFFER_SIZE
;
777 int count
= serialProxyReadData(buf
, avail
);
778 if (count
== 0) return;
780 tcpReceiveBytesEx( serialUartIndex
- 1, buf
, count
);