LCOV - code coverage report
Current view: top level - AP_MSP - msp.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 111 188 59.0 %
Date: 2026-09-09 11:02:08 Functions: 4 4 100.0 %

          Line data    Source code
       1             : #include <AP_Math/AP_Math.h>
       2             : #include <AP_SerialManager/AP_SerialManager.h>
       3             : 
       4             : #include "msp.h"
       5             : 
       6             : #include <stdint.h>
       7             : 
       8             : #if HAL_MSP_ENABLED
       9             : 
      10             : /*
      11             :  ported from betaflight/src/main/msp/msp_serial.c
      12             :  */
      13       43944 : uint8_t MSP::msp_serial_checksum_buf(uint8_t checksum, const uint8_t *data, uint32_t len)
      14             : {
      15      256446 :     while (len-- > 0) {
      16      212502 :         checksum ^= *data++;
      17             :     }
      18       43944 :     return checksum;
      19             : }
      20             : 
      21             : /*
      22             :  ported from betaflight/src/main/msp/msp_serial.c
      23             :  */
      24       21982 : uint32_t MSP::msp_serial_send_frame(msp_port_t *msp, const uint8_t * hdr, uint32_t hdr_len, const uint8_t * data, uint32_t data_len, const uint8_t * crc, uint32_t crc_len)
      25             : {
      26       21982 :     if (msp->uart == nullptr) {
      27           0 :         return 0;
      28             :     }
      29             : 
      30             :     // only send the response if it fits in the transmit buffer. Writes do not
      31             :     // block, so writing a frame which does not fit would put a truncated frame
      32             :     // on the wire for the other end to choke on
      33       21982 :     const uint32_t total_frame_length = hdr_len + data_len + crc_len;
      34             : 
      35       21982 :     if (msp->uart->txspace() < total_frame_length) {
      36       19443 :         return 0;
      37             :     }
      38             : 
      39             :     // Transmit frame
      40        2539 :     msp->uart->write(hdr, hdr_len);
      41        2539 :     msp->uart->write(data, data_len);
      42        2539 :     msp->uart->write(crc, crc_len);
      43             : 
      44        2539 :     return total_frame_length;
      45             : }
      46             : 
      47             : /*
      48             :  ported from betaflight/src/main/msp/msp_serial.c
      49             :  */
      50       21972 : uint32_t MSP::msp_serial_encode(msp_port_t *msp, msp_packet_t *packet, msp_version_e msp_version, bool is_request)
      51             : {
      52             :     static const uint8_t msp_magic[MSP_VERSION_COUNT] = MSP_VERSION_MAGIC_INITIALIZER;
      53             :     /*
      54             :         Note: after calling sbuf_switch_to_reader() sbuf_bytes_remaining() returns the size of the packet
      55             :     */
      56       21972 :     const uint16_t data_len = sbuf_bytes_remaining(&packet->buf);
      57             :     uint8_t code;
      58       21972 :     if (is_request) {
      59           0 :         code = '<';
      60       21972 :     } else if (packet->result == MSP_RESULT_ERROR) {
      61           0 :         code = '!';
      62             :     } else {
      63       21972 :         code = '>';
      64             :     }
      65       21972 :     const uint8_t hdr_buf[16] = { '$', msp_magic[msp_version], code };
      66             :     uint8_t crc_buf[2];
      67       21972 :     uint32_t hdr_len = 3;
      68       21972 :     uint32_t crc_len = 0;
      69             : 
      70             : #define V1_CHECKSUM_STARTPOS 3
      71       21972 :     if (msp_version == MSP_V1) {
      72       21972 :         msp_header_v1_t * hdr_v1 = (msp_header_v1_t *)&hdr_buf[hdr_len];
      73       21972 :         hdr_len += sizeof(msp_header_v1_t);
      74       21972 :         hdr_v1->cmd = packet->cmd;
      75             : 
      76             :         // Add JUMBO-frame header if necessary
      77       21972 :         if (data_len >= JUMBO_FRAME_SIZE_LIMIT) {
      78           0 :             msp_header_jumbo_t * hdr_jumbo = (msp_header_jumbo_t *)&hdr_buf[hdr_len];
      79           0 :             hdr_len += sizeof(msp_header_jumbo_t);
      80             : 
      81           0 :             hdr_v1->size = JUMBO_FRAME_SIZE_LIMIT;
      82           0 :             hdr_jumbo->size = data_len;
      83             :         } else {
      84       21972 :             hdr_v1->size = data_len;
      85             :         }
      86             : 
      87             :         // Pre-calculate CRC
      88       21972 :         crc_buf[crc_len] = msp_serial_checksum_buf(0, hdr_buf + V1_CHECKSUM_STARTPOS, hdr_len - V1_CHECKSUM_STARTPOS);
      89       21972 :         crc_buf[crc_len] = msp_serial_checksum_buf(crc_buf[crc_len], sbuf_ptr(&packet->buf), data_len);
      90       21972 :         crc_len++;
      91           0 :     } else if (msp_version == MSP_V2_OVER_V1) {
      92           0 :         msp_header_v1_t * hdr_v1 = (msp_header_v1_t *)&hdr_buf[hdr_len];
      93             : 
      94           0 :         hdr_len += sizeof(msp_header_v1_t);
      95             : 
      96           0 :         msp_header_v2_t * hdr_v2 = (msp_header_v2_t *)&hdr_buf[hdr_len];
      97           0 :         hdr_len += sizeof(msp_header_v2_t);
      98             : 
      99           0 :         const uint32_t v1_payload_size = sizeof(msp_header_v2_t) + data_len + 1;  // MSPv2 header + data payload + MSPv2 checksum
     100           0 :         hdr_v1->cmd = MSP_V2_FRAME_ID;
     101             : 
     102             :         // Add JUMBO-frame header if necessary
     103           0 :         if (v1_payload_size >= JUMBO_FRAME_SIZE_LIMIT) {
     104           0 :             msp_header_jumbo_t * hdr_jumbo = (msp_header_jumbo_t *)&hdr_buf[hdr_len];
     105           0 :             hdr_len += sizeof(msp_header_jumbo_t);
     106             : 
     107           0 :             hdr_v1->size = JUMBO_FRAME_SIZE_LIMIT;
     108           0 :             hdr_jumbo->size = v1_payload_size;
     109             :         } else {
     110           0 :             hdr_v1->size = v1_payload_size;
     111             :         }
     112             : 
     113             :         // Fill V2 header
     114           0 :         hdr_v2->flags = packet->flags;
     115           0 :         hdr_v2->cmd = packet->cmd;
     116           0 :         hdr_v2->size = data_len;
     117             : 
     118             :         // V2 CRC: only V2 header + data payload
     119           0 :         crc_buf[crc_len] = crc8_dvb_s2_update(0, (uint8_t *)hdr_v2, sizeof(msp_header_v2_t));
     120           0 :         crc_buf[crc_len] = crc8_dvb_s2_update(crc_buf[crc_len], sbuf_ptr(&packet->buf), data_len);
     121           0 :         crc_len++;
     122             : 
     123             :         // V1 CRC: All headers + data payload + V2 CRC byte
     124           0 :         crc_buf[crc_len] = msp_serial_checksum_buf(0, hdr_buf + V1_CHECKSUM_STARTPOS, hdr_len - V1_CHECKSUM_STARTPOS);
     125           0 :         crc_buf[crc_len] = msp_serial_checksum_buf(crc_buf[crc_len], sbuf_ptr(&packet->buf), data_len);
     126           0 :         crc_buf[crc_len] = msp_serial_checksum_buf(crc_buf[crc_len], crc_buf, crc_len);
     127           0 :         crc_len++;
     128           0 :     } else if (msp_version == MSP_V2_NATIVE) {
     129           0 :         msp_header_v2_t * hdr_v2 = (msp_header_v2_t *)&hdr_buf[hdr_len];
     130           0 :         hdr_len += sizeof(msp_header_v2_t);
     131             : 
     132           0 :         hdr_v2->flags = packet->flags;
     133           0 :         hdr_v2->cmd = packet->cmd;
     134           0 :         hdr_v2->size = data_len;
     135             : 
     136           0 :         crc_buf[crc_len] = crc8_dvb_s2_update(0, (uint8_t *)hdr_v2, sizeof(msp_header_v2_t));
     137           0 :         crc_buf[crc_len] = crc8_dvb_s2_update(crc_buf[crc_len], sbuf_ptr(&packet->buf), data_len);
     138           0 :         crc_len++;
     139             :     } else {
     140             :         // Shouldn't get here
     141           0 :         return 0;
     142             :     }
     143             : 
     144             :     // Send the frame
     145       21972 :     return msp_serial_send_frame(msp, hdr_buf, hdr_len, sbuf_ptr(&packet->buf), data_len, crc_buf, crc_len);
     146             : }
     147             : 
     148             : /*
     149             :  ported from betaflight/src/main/msp/msp_serial.c
     150             :  */
     151        8116 : bool MSP::msp_parse_received_data(msp_port_t *msp, uint8_t c)
     152             : {
     153        8116 :     switch (msp->c_state) {
     154        2766 :     default:
     155             :     case MSP_IDLE:      // Waiting for '$' character
     156        2766 :         if (c == '$') {
     157         224 :             msp->msp_version = MSP_V1;
     158         224 :             msp->c_state = MSP_HEADER_START;
     159             :         } else {
     160        2542 :             return false;
     161             :         }
     162         224 :         break;
     163             : 
     164         224 :     case MSP_HEADER_START:  // Waiting for 'M' (MSPv1 / MSPv2_over_v1) or 'X' (MSPv2 native)
     165         224 :         switch (c) {
     166         141 :         case 'M':
     167         141 :             msp->c_state = MSP_HEADER_M;
     168         141 :             break;
     169          76 :         case 'X':
     170          76 :             msp->c_state = MSP_HEADER_X;
     171          76 :             break;
     172           7 :         default:
     173           7 :             msp->c_state = MSP_IDLE;
     174           7 :             break;
     175             :         }
     176         224 :         break;
     177             : 
     178         141 :     case MSP_HEADER_M:      // Waiting for '<'
     179         141 :         if (c == '<') {
     180         141 :             msp->offset = 0;
     181         141 :             msp->checksum1 = 0;
     182         141 :             msp->checksum2 = 0;
     183         141 :             msp->c_state = MSP_HEADER_V1;
     184             :         } else {
     185           0 :             msp->c_state = MSP_IDLE;
     186             :         }
     187         141 :         break;
     188             : 
     189          76 :     case MSP_HEADER_X:
     190          76 :         if (c == '<') {
     191          76 :             msp->offset = 0;
     192          76 :             msp->checksum2 = 0;
     193          76 :             msp->msp_version = MSP_V2_NATIVE;
     194          76 :             msp->c_state = MSP_HEADER_V2_NATIVE;
     195             :         } else {
     196           0 :             msp->c_state = MSP_IDLE;
     197             :         }
     198          76 :         break;
     199             : 
     200         282 :     case MSP_HEADER_V1:     // Now receive v1 header (size/cmd), this is already checksummable
     201         282 :         msp->in_buf[msp->offset++] = c;
     202         282 :         msp->checksum1 ^= c;
     203         282 :         if (msp->offset == sizeof(msp_header_v1_t)) {
     204         141 :             msp_header_v1_t * hdr = (msp_header_v1_t *)&msp->in_buf[0];
     205             :             // Check incoming buffer size limit
     206         141 :             if (hdr->size > MSP_PORT_INBUF_SIZE) {
     207           0 :                 msp->c_state = MSP_IDLE;
     208         141 :             } else if (hdr->cmd == MSP_V2_FRAME_ID) {
     209             :                 // MSPv1 payload must be big enough to hold V2 header + extra checksum
     210           0 :                 if (hdr->size >= sizeof(msp_header_v2_t) + 1) {
     211           0 :                     msp->msp_version = MSP_V2_OVER_V1;
     212           0 :                     msp->c_state = MSP_HEADER_V2_OVER_V1;
     213             :                 } else {
     214           0 :                     msp->c_state = MSP_IDLE;
     215             :                 }
     216             :             } else {
     217         141 :                 msp->data_size = hdr->size;
     218         141 :                 msp->cmd_msp = hdr->cmd;
     219         141 :                 msp->cmd_flags = 0;
     220         141 :                 msp->offset = 0;                // re-use buffer
     221         141 :                 msp->c_state = msp->data_size > 0 ? MSP_PAYLOAD_V1 : MSP_CHECKSUM_V1;    // If no payload - jump to checksum byte
     222             :             }
     223             :         }
     224         282 :         break;
     225             : 
     226          78 :     case MSP_PAYLOAD_V1:
     227          78 :         msp->in_buf[msp->offset++] = c;
     228          78 :         msp->checksum1 ^= c;
     229          78 :         if (msp->offset == msp->data_size) {
     230          16 :             msp->c_state = MSP_CHECKSUM_V1;
     231             :         }
     232          78 :         break;
     233             : 
     234         141 :     case MSP_CHECKSUM_V1:
     235         141 :         if (msp->checksum1 == c) {
     236         141 :             msp->c_state = MSP_COMMAND_RECEIVED;
     237             :         } else {
     238           0 :             msp->c_state = MSP_IDLE;
     239             :         }
     240         141 :         break;
     241             : 
     242           0 :     case MSP_HEADER_V2_OVER_V1:     // V2 header is part of V1 payload - we need to calculate both checksums now
     243           0 :         msp->in_buf[msp->offset++] = c;
     244           0 :         msp->checksum1 ^= c;
     245           0 :         msp->checksum2 = crc8_dvb_s2(msp->checksum2, c);
     246           0 :         if (msp->offset == (sizeof(msp_header_v2_t) + sizeof(msp_header_v1_t))) {
     247           0 :             msp_header_v2_t * hdrv2 = (msp_header_v2_t *)&msp->in_buf[sizeof(msp_header_v1_t)];
     248           0 :             msp->data_size = hdrv2->size;
     249             : 
     250             :             // Check for potential buffer overflow
     251           0 :             if (hdrv2->size > MSP_PORT_INBUF_SIZE) {
     252           0 :                 msp->c_state = MSP_IDLE;
     253             :             } else {
     254           0 :                 msp->cmd_msp = hdrv2->cmd;
     255           0 :                 msp->cmd_flags = hdrv2->flags;
     256           0 :                 msp->offset = 0;                // re-use buffer
     257           0 :                 msp->c_state = msp->data_size > 0 ? MSP_PAYLOAD_V2_OVER_V1 : MSP_CHECKSUM_V2_OVER_V1;
     258             :             }
     259             :         }
     260           0 :         break;
     261             : 
     262           0 :     case MSP_PAYLOAD_V2_OVER_V1:
     263           0 :         msp->checksum2 = crc8_dvb_s2(msp->checksum2, c);
     264           0 :         msp->checksum1 ^= c;
     265           0 :         msp->in_buf[msp->offset++] = c;
     266             : 
     267           0 :         if (msp->offset == msp->data_size) {
     268           0 :             msp->c_state = MSP_CHECKSUM_V2_OVER_V1;
     269             :         }
     270           0 :         break;
     271             : 
     272           0 :     case MSP_CHECKSUM_V2_OVER_V1:
     273           0 :         msp->checksum1 ^= c;
     274           0 :         if (msp->checksum2 == c) {
     275           0 :             msp->c_state = MSP_CHECKSUM_V1; // Checksum 2 correct - verify v1 checksum
     276             :         } else {
     277           0 :             msp->c_state = MSP_IDLE;
     278             :         }
     279           0 :         break;
     280             : 
     281         380 :     case MSP_HEADER_V2_NATIVE:
     282         380 :         msp->in_buf[msp->offset++] = c;
     283         380 :         msp->checksum2 = crc8_dvb_s2(msp->checksum2, c);
     284         380 :         if (msp->offset == sizeof(msp_header_v2_t)) {
     285          76 :             msp_header_v2_t * hdrv2 = (msp_header_v2_t *)&msp->in_buf[0];
     286             : 
     287             :             // Check for potential buffer overflow
     288          76 :             if (hdrv2->size > MSP_PORT_INBUF_SIZE) {
     289           0 :                 msp->c_state = MSP_IDLE;
     290             :             } else {
     291          76 :                 msp->data_size = hdrv2->size;
     292          76 :                 msp->cmd_msp = hdrv2->cmd;
     293          76 :                 msp->cmd_flags = hdrv2->flags;
     294          76 :                 msp->offset = 0;                // re-use buffer
     295          76 :                 msp->c_state = msp->data_size > 0 ? MSP_PAYLOAD_V2_NATIVE : MSP_CHECKSUM_V2_NATIVE;
     296             :             }
     297             :         }
     298         380 :         break;
     299             : 
     300        3952 :     case MSP_PAYLOAD_V2_NATIVE:
     301        3952 :         msp->checksum2 = crc8_dvb_s2(msp->checksum2, c);
     302        3952 :         msp->in_buf[msp->offset++] = c;
     303             : 
     304        3952 :         if (msp->offset == msp->data_size) {
     305          76 :             msp->c_state = MSP_CHECKSUM_V2_NATIVE;
     306             :         }
     307        3952 :         break;
     308             : 
     309          76 :     case MSP_CHECKSUM_V2_NATIVE:
     310          76 :         if (msp->checksum2 == c) {
     311          76 :             msp->c_state = MSP_COMMAND_RECEIVED;
     312             :         } else {
     313           0 :             msp->c_state = MSP_IDLE;
     314             :         }
     315          76 :         break;
     316             :     }
     317        5574 :     return true;
     318             : }
     319             : 
     320             : #endif //HAL_MSP_ENABLED

Generated by: LCOV version 1.14