blob: b996803b263ef599f64380b63115265450b8789e [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CHPP_TRANSPORT_H_
#define CHPP_TRANSPORT_H_
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "chpp/app.h"
#include "chpp/link.h"
#include "chpp/macros.h"
#include "chpp/memory.h"
#include "chpp/mutex.h"
#include "chpp/notifier.h"
#include "chpp/platform/log.h"
#ifdef __cplusplus
extern "C" {
#endif
/************************************************
* Public Definitions
***********************************************/
/**
* CHPP Transport header flags bitmap
*
* @defgroup CHPP_TRANSPORT_FLAG
* @{
*/
// This packet concludes a (fragmented or unfragmented) datagram
#define CHPP_TRANSPORT_FLAG_FINISHED_DATAGRAM 0x00
// Set if packet is part of a fragmented datagram, except for the last fragment
#define CHPP_TRANSPORT_FLAG_UNFINISHED_DATAGRAM 0x01
// Set for first packet after bootup or to reset after irrecoverable error
#define CHPP_TRANSPORT_FLAG_RESET 0x02
// Reserved for future use
#define CHPP_TRANSPORT_FLAG_RESERVED 0xfc
/** @} */
/**
* Preamble (i.e. packet start delimiter).
* Any future backwards-incompatible versions of CHPP Transport will use a
* different preamble.
*
* @defgroup CHPP_PREAMBLE
* @{
*/
#define CHPP_PREAMBLE_DATA 0x6843
#define CHPP_PREAMBLE_LEN_BYTES 2
/** @} */
/**
* Maximum number of datagrams in the Tx queue.
* CHPP will return an error if it is provided with a new Tx datagram when this
* queue is full.
* To be safe, this should be less than half of the maximum uint8_t value.
* Otherwise, ChppTxDatagramQueue should be updated accordingly.
*/
#define CHPP_TX_DATAGRAM_QUEUE_LEN 16
/**
* Maximum payload of packets at the link layer.
* TODO: Negotiate or advertise MTU
*/
#define CHPP_LINK_TX_MTU_BYTES \
(1024 + CHPP_PREAMBLE_LEN_BYTES + sizeof(struct ChppTransportHeader) + \
sizeof(struct ChppTransportFooter))
/**
* Maximum payload of packets at the transport layer.
*/
#define CHPP_TRANSPORT_TX_MTU_BYTES \
(CHPP_LINK_TX_MTU_BYTES - CHPP_PREAMBLE_LEN_BYTES - \
sizeof(struct ChppTransportHeader) - sizeof(struct ChppTransportFooter))
/************************************************
* Status variables to store context in lieu of global variables (this)
***********************************************/
/**
* Error codes optionally reported in ChppTransportHeader
*/
enum ChppErrorCode {
// No error reported (either ACK or implicit NACK)
CHPP_ERROR_NONE = 0,
// Checksum failure
CHPP_ERROR_CHECKSUM = 1,
// Out of memory
CHPP_ERROR_OOM = 2,
// Busy
CHPP_ERROR_BUSY = 3,
// Invalid header
CHPP_ERROR_HEADER = 4,
// Out of order
CHPP_ERROR_ORDER = 5,
// Timeout (implicit, deduced and used internally only)
CHPP_ERROR_TIMEOUT = 0xF,
};
/**
* CHPP Transport Layer header (not including the preamble)
*/
CHPP_PACKED_START
struct ChppTransportHeader {
// Flags bitmap, defined as CHPP_TRANSPORT_FLAG_...
uint8_t flags;
// Error info (2 nibbles)
// LS Nibble: Defined in ChppErrorCode enum
// MS Nibble: Reserved
uint8_t errorCode;
// Next expected sequence number for a payload-bearing packet
uint8_t ackSeq;
// Sequence number
uint8_t seq;
// Payload length in bytes (not including header / footer)
uint16_t length;
// Reserved? TBD
uint16_t reserved;
} CHPP_PACKED_ATTR;
CHPP_PACKED_END
/**
* CHPP Transport Layer footer (containing the checksum)
*/
CHPP_PACKED_START
struct ChppTransportFooter {
// Checksum algo TBD. Maybe IEEE CRC-32?
uint32_t checksum;
} CHPP_PACKED_ATTR;
CHPP_PACKED_END
enum ChppRxState {
// Waiting for, or processing, the preamble (i.e. packet start delimiter)
// Moves to CHPP_STATE_HEADER as soon as it has seen a complete preamble.
CHPP_STATE_PREAMBLE = 0,
// Processing the packet header. Moves to CHPP_STATE_PAYLOAD after processing
// the expected length of the header.
CHPP_STATE_HEADER = 1,
// Copying the packet payload. The payload length is determined by the header.
// Moves to CHPP_STATE_FOOTER afterwards.
CHPP_STATE_PAYLOAD = 2,
// Processing the packet footer (checksum) and responding accordingly. Moves
// to CHPP_STATE_PREAMBLE afterwards.
CHPP_STATE_FOOTER = 3,
};
struct ChppRxStatus {
// Current receiving state, as described in ChppRxState.
enum ChppRxState state;
// Location counter in bytes within each state. Must always be reinitialized
// to 0 when switching states.
size_t locInState;
// Next expected sequence number (for a payload-bearing packet)
uint8_t expectedSeq;
// Error code, if any, of the last received packet
enum ChppErrorCode receivedErrorCode;
// Location counter in bytes within the current Rx datagram.
size_t locInDatagram;
// Last received ACK sequence number (i.e. next expected sequence number for
// an outgoing payload-bearing packet)
uint8_t receivedAckSeq;
};
struct ChppTxStatus {
// Last sent ACK sequence number (i.e. next expected sequence number for
// an incoming payload-bearing packet)
uint8_t sentAckSeq;
// Last sent sequence number (irrespective of whether it has been received /
// ACKed or not)
uint8_t sentSeq;
// Does the transport layer have any packets (with or without payload) it
// needs to send out?
bool hasPacketsToSend;
// Error code, if any, of the next packet the transport layer will send out.
enum ChppErrorCode errorCodeToSend;
// How many bytes of the front-of-queue datagram has been sent out
size_t sentLocInDatagram;
// Note: For a future ACK window >1, sentLocInDatagram doesn't always apply to
// the front-of-queue datagram. Instead, we need to track the queue position
// the datagram being sent as well (relative to the front-of-queue). e.g.
// uint8_t datagramBeingSent
// How many bytes of the front-of-queue datagram has been acked
size_t ackedLocInDatagram;
// Whether the link layer is still processing pendingTxPacket
bool linkBusy;
};
struct PendingTxPacket {
// Length of outgoing packet to the Link Layer
size_t length;
// Payload of outgoing packet to the Link Layer
uint8_t payload[CHPP_LINK_TX_MTU_BYTES];
};
struct ChppDatagram {
// Length of datagram payload in bytes (A datagram can be constituted from one
// or more packets)
size_t length;
// Datagram payload
uint8_t *payload;
};
struct ChppTxDatagramQueue {
// Number of pending datagrams in the queue.
uint8_t pending;
// Index of the datagram at the front of the queue.
uint8_t front;
// Location counter within the front datagram (i.e. the datagram at the front
// of the queue), showing how many bytes of this datagram have already been
// packetized and processed.
size_t loc;
// Array of datagrams
struct ChppDatagram datagram[CHPP_TX_DATAGRAM_QUEUE_LEN];
};
struct ChppTransportState {
struct ChppAppState *appContext; // Pointer to app layer context
struct ChppRxStatus rxStatus; // Rx state and location within
struct ChppTransportHeader rxHeader; // Rx packet header
struct ChppTransportFooter rxFooter; // Rx packet footer (checksum)
struct ChppDatagram rxDatagram; // Rx datagram
struct ChppTxStatus txStatus; // Tx state
struct ChppTxDatagramQueue txDatagramQueue; // Queue of datagrams to be Tx
struct PendingTxPacket pendingTxPacket; // Outgoing packet to Link Layer
struct ChppMutex mutex; // Lock for transport state (i.e. context)
struct ChppNotifier notifier; // Notifier for main thread
struct ChppPlatformLinkParameters linkParams; // For corresponding link layer
};
/************************************************
* Public functions
***********************************************/
/**
* Initializes the CHPP transport layer state stored in the parameter
* transportContext.
* It is necessary to initialize state for each transport layer instance on
* every platform.
* Each transport layer instance is associated with a single application layer
* instance. appContext points to the application layer status struct associated
* with this transport layer instance.
*
* Note: After calling this function, it is also necessary to initialize the
* platform-specific values of transportContext.linkParams.
*
* @param transportContext Maintains status for each transport layer instance.
* @param appContext The app layer status struct associated with this transport
* layer instance.
*/
void chppTransportInit(struct ChppTransportState *transportContext,
struct ChppAppState *appContext);
/**
* Processes all incoming data on the serial port based on the Rx state.
* stream. Checks checksum, triggering the correct response (ACK / NACK).
* Moves the state to CHPP_STATE_PREAMBLE afterwards.
*
* TODO: Add requirements, e.g. context must not be modified unless locked via
* mutex.
*
* TODO: Add sufficient outward facing documentation
*
* @param context Maintains status for each transport layer instance.
* @param buf Input data. Cannot be null.
* @param len Length of input data in bytes.
*
* @return true informs the serial port driver that we are waiting for a
* preamble. This allows the driver to (optionally) filter incoming zeros and
* save processing
*/
bool chppRxDataCb(struct ChppTransportState *context, const uint8_t *buf,
size_t len);
/**
* Callback function for the timer that detects timeouts during transmit.
*
* @param context Maintains status for each transport layer instance.
*/
void chppTxTimeoutTimerCb(struct ChppTransportState *context);
/**
* Callback function for the timer that detects timeouts during receive.
*
* @param context Maintains status for each transport layer instance.
*/
void chppRxTimeoutTimerCb(struct ChppTransportState *context);
/**
* Enqueues an outgoing datagram of a specified length. The payload must have
* been allocated by the caller using chppMalloc. If enqueueing is successful,
* the payload shall be freed by the transport layer once it has been sent out.
* If enqueueing is unsuccessful, it is up to the sender to decide whether to
* free the payload and/or resend it later.
*
* @param context Maintains status for each transport layer instance.
* @param buf Datagram payload allocated through chppMalloc. Cannot be null.
* @param len Datagram length in bytes.
*
* @return True informs the sender that the datagram was successfully enqueued.
* False informs the sender that the queue was full.
*/
bool chppEnqueueTxDatagram(struct ChppTransportState *context, uint8_t *buf,
size_t len);
/**
* Starts the main thread for CHPP's Transport Layer. This thread needs to be
* started after the Transport Layer is initialized through chppTransportInit().
* Note that a platform may implement this as a new thread or as part of an
* existing thread.
*
* If needed (e.g. for testing and debugging), this thread can be stopped by
* calling chppWorkThreadStop().
*
* @param context Maintains status for each transport layer instance.
*/
void chppWorkThreadStart(struct ChppTransportState *context);
/**
* Stops the main thread for CHPP's Transport Layer that has been started by
* calling chppWorkThreadStart(). Stopping this thread may be necessary for
* testing and debugging purposes.
*
* @param context Maintains status for each transport layer instance.
*/
void chppWorkThreadStop(struct ChppTransportState *context);
/*
* Notifies the transport layer that the link layer is done sending the previous
* payload (as provided to platformLinkSend() through buf and len) and can
* accept more data.
*
* On systems that implement the link layer Tx asynchronously, where
* platformLinkSend() returns False before consuming the payload provided to it
* (i.e. buf and len), the platform implementation must call this function after
* platformLinkSend() is done with the payload (i.e. buf and len).
*
* @param params Platform-specific struct with link details / parameters.
*/
void chppLinkSendDoneCb(struct ChppPlatformLinkParameters *params);
/*
* Notifies the transport layer that the app layer is done with the previous
* payload (as provided to chppProcessRxDatagram() through buf and len), so it
* is freed appropriately etc.
*
* @param context Maintains status for each transport layer instance.
* @param buf Pointer to the buf given to chppProcessRxDatagram. Cannot be null.
*/
void chppAppProcessDoneCb(struct ChppTransportState *context, uint8_t *buf);
#ifdef __cplusplus
}
#endif
#endif // CHPP_TRANSPORT_H_