diff --git a/include/zephyr/modem/ppp.h b/include/zephyr/modem/ppp.h index b3dbcfdeb7fb3..69b35897e2d27 100644 --- a/include/zephyr/modem/ppp.h +++ b/include/zephyr/modem/ppp.h @@ -50,27 +50,10 @@ enum modem_ppp_receive_state { }; enum modem_ppp_transmit_state { - /* Idle */ MODEM_PPP_TRANSMIT_STATE_IDLE = 0, - /* Writing header */ MODEM_PPP_TRANSMIT_STATE_SOF, - MODEM_PPP_TRANSMIT_STATE_HDR_FF, - MODEM_PPP_TRANSMIT_STATE_HDR_7D, - MODEM_PPP_TRANSMIT_STATE_HDR_23, - /* Writing protocol */ - MODEM_PPP_TRANSMIT_STATE_PROTOCOL_HIGH, - MODEM_PPP_TRANSMIT_STATE_ESCAPING_PROTOCOL_HIGH, - MODEM_PPP_TRANSMIT_STATE_PROTOCOL_LOW, - MODEM_PPP_TRANSMIT_STATE_ESCAPING_PROTOCOL_LOW, - /* Writing data */ + MODEM_PPP_TRANSMIT_STATE_PROTOCOL, MODEM_PPP_TRANSMIT_STATE_DATA, - MODEM_PPP_TRANSMIT_STATE_ESCAPING_DATA, - /* Writing FCS */ - MODEM_PPP_TRANSMIT_STATE_FCS_LOW, - MODEM_PPP_TRANSMIT_STATE_ESCAPING_FCS_LOW, - MODEM_PPP_TRANSMIT_STATE_FCS_HIGH, - MODEM_PPP_TRANSMIT_STATE_ESCAPING_FCS_HIGH, - /* Writing end of frame */ MODEM_PPP_TRANSMIT_STATE_EOF, }; @@ -100,8 +83,6 @@ struct modem_ppp { /* Packet being sent */ enum modem_ppp_transmit_state transmit_state; struct net_pkt *tx_pkt; - uint8_t tx_pkt_escaped; - uint16_t tx_pkt_protocol; uint16_t tx_pkt_fcs; /* Ring buffer used for transmitting partial PPP frame */ diff --git a/subsys/modem/modem_ppp.c b/subsys/modem/modem_ppp.c index 9e2e2b5d71b7c..cd13d4ac8b1ac 100644 --- a/subsys/modem/modem_ppp.c +++ b/subsys/modem/modem_ppp.c @@ -49,148 +49,134 @@ static uint16_t modem_ppp_ppp_protocol(struct net_pkt *pkt) return 0; } -static uint8_t modem_ppp_wrap_net_pkt_byte(struct modem_ppp *ppp) +static bool modem_ppp_needs_escape(uint8_t byte) { - uint8_t byte; - - switch (ppp->transmit_state) { - case MODEM_PPP_TRANSMIT_STATE_IDLE: - LOG_WRN("Invalid transmit state"); - return 0; - - /* Writing header */ - case MODEM_PPP_TRANSMIT_STATE_SOF: - ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_HDR_FF; - return MODEM_PPP_CODE_DELIMITER; - - case MODEM_PPP_TRANSMIT_STATE_HDR_FF: - net_pkt_cursor_init(ppp->tx_pkt); - ppp->tx_pkt_fcs = modem_ppp_fcs_init(0xFF); - ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_HDR_7D; - return 0xFF; - - case MODEM_PPP_TRANSMIT_STATE_HDR_7D: - ppp->tx_pkt_fcs = modem_ppp_fcs_update(ppp->tx_pkt_fcs, 0x03); - ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_HDR_23; - return MODEM_PPP_CODE_ESCAPE; - - case MODEM_PPP_TRANSMIT_STATE_HDR_23: - if (net_pkt_is_ppp(ppp->tx_pkt) == true) { - ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_DATA; - } else { - ppp->tx_pkt_protocol = modem_ppp_ppp_protocol(ppp->tx_pkt); - ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_PROTOCOL_HIGH; - } - - return 0x23; - - /* Writing protocol */ - case MODEM_PPP_TRANSMIT_STATE_PROTOCOL_HIGH: - byte = (ppp->tx_pkt_protocol >> 8) & 0xFF; - ppp->tx_pkt_fcs = modem_ppp_fcs_update(ppp->tx_pkt_fcs, byte); - - if ((byte == MODEM_PPP_CODE_DELIMITER) || (byte == MODEM_PPP_CODE_ESCAPE) || - (byte < MODEM_PPP_VALUE_ESCAPE)) { - ppp->tx_pkt_escaped = byte ^ MODEM_PPP_VALUE_ESCAPE; - ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_ESCAPING_PROTOCOL_HIGH; - return MODEM_PPP_CODE_ESCAPE; - } - - ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_PROTOCOL_LOW; - return byte; - - case MODEM_PPP_TRANSMIT_STATE_ESCAPING_PROTOCOL_HIGH: - ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_PROTOCOL_LOW; - return ppp->tx_pkt_escaped; - - case MODEM_PPP_TRANSMIT_STATE_PROTOCOL_LOW: - byte = ppp->tx_pkt_protocol & 0xFF; - ppp->tx_pkt_fcs = modem_ppp_fcs_update(ppp->tx_pkt_fcs, byte); - - if ((byte == MODEM_PPP_CODE_DELIMITER) || (byte == MODEM_PPP_CODE_ESCAPE) || - (byte < MODEM_PPP_VALUE_ESCAPE)) { - ppp->tx_pkt_escaped = byte ^ MODEM_PPP_VALUE_ESCAPE; - ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_ESCAPING_PROTOCOL_LOW; - return MODEM_PPP_CODE_ESCAPE; - } - - ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_DATA; - return byte; - - case MODEM_PPP_TRANSMIT_STATE_ESCAPING_PROTOCOL_LOW: - ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_DATA; - return ppp->tx_pkt_escaped; - - /* Writing data */ - case MODEM_PPP_TRANSMIT_STATE_DATA: - (void)net_pkt_read_u8(ppp->tx_pkt, &byte); - ppp->tx_pkt_fcs = modem_ppp_fcs_update(ppp->tx_pkt_fcs, byte); - - if ((byte == MODEM_PPP_CODE_DELIMITER) || (byte == MODEM_PPP_CODE_ESCAPE) || - (byte < MODEM_PPP_VALUE_ESCAPE)) { - ppp->tx_pkt_escaped = byte ^ MODEM_PPP_VALUE_ESCAPE; - ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_ESCAPING_DATA; - return MODEM_PPP_CODE_ESCAPE; - } + return (byte == MODEM_PPP_CODE_DELIMITER) || (byte == MODEM_PPP_CODE_ESCAPE) || + (byte < MODEM_PPP_VALUE_ESCAPE); +} - if (net_pkt_remaining_data(ppp->tx_pkt) == 0) { - ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_FCS_LOW; - } +static uint32_t modem_ppp_wrap(struct modem_ppp *ppp, uint8_t *buffer, uint32_t available) +{ + uint32_t offset = 0; + uint32_t remaining; + uint16_t protocol; + uint8_t upper; + uint8_t lower; + uint8_t byte; - return byte; + while (offset < available) { + remaining = available - offset; - case MODEM_PPP_TRANSMIT_STATE_ESCAPING_DATA: - if (net_pkt_remaining_data(ppp->tx_pkt) == 0) { - ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_FCS_LOW; - } else { + switch (ppp->transmit_state) { + case MODEM_PPP_TRANSMIT_STATE_SOF: + if (remaining < 4) { + /* Insufficient space for constant header prefix */ + goto end; + } + /* Init cursor for later phases */ + net_pkt_cursor_init(ppp->tx_pkt); + /* 3 byte common header */ + buffer[offset++] = MODEM_PPP_CODE_DELIMITER; + buffer[offset++] = 0xFF; + buffer[offset++] = MODEM_PPP_CODE_ESCAPE; + buffer[offset++] = 0x23; + /* Initialise the FCS. + * This value is always the same at this point, so use the constant value. + * Equivelent to: + * ppp->tx_pkt_fcs = modem_ppp_fcs_init(0xFF); + * ppp->tx_pkt_fcs = modem_ppp_fcs_update(ppp->tx_pkt_fcs, 0x03); + */ + ARG_UNUSED(modem_ppp_fcs_init); + ppp->tx_pkt_fcs = 0x3DE3; + /* Next state */ + if (net_pkt_is_ppp(ppp->tx_pkt)) { + ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_DATA; + } else { + ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_PROTOCOL; + } + break; + case MODEM_PPP_TRANSMIT_STATE_PROTOCOL: + /* If both protocol bytes need escaping, it could be 4 bytes */ + if (remaining < 4) { + /* Insufficient space for protocol bytes */ + goto end; + } + /* Extract protocol bytes */ + protocol = modem_ppp_ppp_protocol(ppp->tx_pkt); + upper = (protocol >> 8) & 0xFF; + lower = (protocol >> 0) & 0xFF; + /* FCS is computed without the escape/modification */ + ppp->tx_pkt_fcs = modem_ppp_fcs_update(ppp->tx_pkt_fcs, upper); + ppp->tx_pkt_fcs = modem_ppp_fcs_update(ppp->tx_pkt_fcs, lower); + /* Push protocol bytes (with required escaping) */ + if (modem_ppp_needs_escape(upper)) { + buffer[offset++] = MODEM_PPP_CODE_ESCAPE; + upper ^= MODEM_PPP_VALUE_ESCAPE; + } + buffer[offset++] = upper; + if (modem_ppp_needs_escape(lower)) { + buffer[offset++] = MODEM_PPP_CODE_ESCAPE; + lower ^= MODEM_PPP_VALUE_ESCAPE; + } + buffer[offset++] = lower; + /* Next state */ ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_DATA; + break; + case MODEM_PPP_TRANSMIT_STATE_DATA: + /* Push all data bytes into the buffer */ + while (net_pkt_remaining_data(ppp->tx_pkt) > 0) { + /* Space available, taking into account possible escapes */ + if (remaining < 2) { + goto end; + } + /* Pull next byte we're sending */ + (void)net_pkt_read_u8(ppp->tx_pkt, &byte); + /* FCS is computed without the escape/modification */ + ppp->tx_pkt_fcs = modem_ppp_fcs_update(ppp->tx_pkt_fcs, byte); + /* Push encoded bytes into buffer */ + if (modem_ppp_needs_escape(byte)) { + buffer[offset++] = MODEM_PPP_CODE_ESCAPE; + byte ^= MODEM_PPP_VALUE_ESCAPE; + remaining--; + } + buffer[offset++] = byte; + remaining--; + } + /* Data phase finished */ + ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_EOF; + break; + case MODEM_PPP_TRANSMIT_STATE_EOF: + /* If both FCS bytes need escaping, it could be 5 bytes */ + if (remaining < 5) { + /* Insufficient space for protocol bytes */ + goto end; + } + /* Push FCS (order is [lower, upper] unlike the protocol) */ + ppp->tx_pkt_fcs = modem_ppp_fcs_final(ppp->tx_pkt_fcs); + lower = (ppp->tx_pkt_fcs >> 0) & 0xFF; + upper = (ppp->tx_pkt_fcs >> 8) & 0xFF; + if (modem_ppp_needs_escape(lower)) { + buffer[offset++] = MODEM_PPP_CODE_ESCAPE; + lower ^= MODEM_PPP_VALUE_ESCAPE; + } + buffer[offset++] = lower; + if (modem_ppp_needs_escape(upper)) { + buffer[offset++] = MODEM_PPP_CODE_ESCAPE; + upper ^= MODEM_PPP_VALUE_ESCAPE; + } + buffer[offset++] = upper; + buffer[offset++] = MODEM_PPP_CODE_DELIMITER; + + /* Packet has finished */ + ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_IDLE; + goto end; + default: + LOG_DBG("Invalid transmit state (%d)", ppp->transmit_state); + goto end; } - - return ppp->tx_pkt_escaped; - - /* Writing FCS */ - case MODEM_PPP_TRANSMIT_STATE_FCS_LOW: - ppp->tx_pkt_fcs = modem_ppp_fcs_final(ppp->tx_pkt_fcs); - byte = ppp->tx_pkt_fcs & 0xFF; - - if ((byte == MODEM_PPP_CODE_DELIMITER) || (byte == MODEM_PPP_CODE_ESCAPE) || - (byte < MODEM_PPP_VALUE_ESCAPE)) { - ppp->tx_pkt_escaped = byte ^ MODEM_PPP_VALUE_ESCAPE; - ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_ESCAPING_FCS_LOW; - return MODEM_PPP_CODE_ESCAPE; - } - - ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_FCS_HIGH; - return byte; - - case MODEM_PPP_TRANSMIT_STATE_ESCAPING_FCS_LOW: - ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_FCS_HIGH; - return ppp->tx_pkt_escaped; - - case MODEM_PPP_TRANSMIT_STATE_FCS_HIGH: - byte = (ppp->tx_pkt_fcs >> 8) & 0xFF; - - if ((byte == MODEM_PPP_CODE_DELIMITER) || (byte == MODEM_PPP_CODE_ESCAPE) || - (byte < MODEM_PPP_VALUE_ESCAPE)) { - ppp->tx_pkt_escaped = byte ^ MODEM_PPP_VALUE_ESCAPE; - ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_ESCAPING_FCS_HIGH; - return MODEM_PPP_CODE_ESCAPE; - } - - ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_EOF; - return byte; - - case MODEM_PPP_TRANSMIT_STATE_ESCAPING_FCS_HIGH: - ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_EOF; - return ppp->tx_pkt_escaped; - - /* Writing end of frame */ - case MODEM_PPP_TRANSMIT_STATE_EOF: - ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_IDLE; - return MODEM_PPP_CODE_DELIMITER; } - - return 0; +end: + return offset; } static bool modem_ppp_is_byte_expected(uint8_t byte, uint8_t expected_byte) @@ -357,32 +343,36 @@ static void modem_ppp_pipe_callback(struct modem_pipe *pipe, enum modem_pipe_eve static void modem_ppp_send_handler(struct k_work *item) { struct modem_ppp *ppp = CONTAINER_OF(item, struct modem_ppp, send_work); - uint8_t byte; uint8_t *reserved; uint32_t reserved_size; + uint32_t pushed; int ret; if (ppp->tx_pkt == NULL) { ppp->tx_pkt = k_fifo_get(&ppp->tx_pkt_fifo, K_NO_WAIT); } + if (ring_buf_is_empty(&ppp->transmit_rb)) { + /* Reset to initial state to maximise contiguous claim */ + ring_buf_reset(&ppp->transmit_rb); + } + if (ppp->tx_pkt != NULL) { /* Initialize wrap */ if (ppp->transmit_state == MODEM_PPP_TRANSMIT_STATE_IDLE) { ppp->transmit_state = MODEM_PPP_TRANSMIT_STATE_SOF; } - /* Fill transmit ring buffer */ - while (ring_buf_space_get(&ppp->transmit_rb) > 0) { - byte = modem_ppp_wrap_net_pkt_byte(ppp); - - ring_buf_put(&ppp->transmit_rb, &byte, 1); + /* Claim as much space as possible */ + reserved_size = ring_buf_put_claim(&ppp->transmit_rb, &reserved, UINT32_MAX); + /* Push wrapped data into claimed buffer */ + pushed = modem_ppp_wrap(ppp, reserved, reserved_size); + /* Limit claimed data to what was actually pushed */ + ring_buf_put_finish(&ppp->transmit_rb, pushed); - if (ppp->transmit_state == MODEM_PPP_TRANSMIT_STATE_IDLE) { - net_pkt_unref(ppp->tx_pkt); - ppp->tx_pkt = k_fifo_get(&ppp->tx_pkt_fifo, K_NO_WAIT); - break; - } + if (ppp->transmit_state == MODEM_PPP_TRANSMIT_STATE_IDLE) { + net_pkt_unref(ppp->tx_pkt); + ppp->tx_pkt = k_fifo_get(&ppp->tx_pkt_fifo, K_NO_WAIT); } }