mirror of https://github.com/ARMmbed/mbed-os.git
566 lines
19 KiB
C++
566 lines
19 KiB
C++
/*
|
|
* Copyright (c) 2006-2017, Arm Limited and affiliates.
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* 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 MUX_H
|
|
#define MUX_H
|
|
|
|
#include <stdbool.h>
|
|
#include "FileHandle.h"
|
|
#include "PlatformMutex.h"
|
|
#include "Semaphore.h"
|
|
#include "EventQueue.h"
|
|
|
|
#define MUX_DLCI_INVALID_ID 0 /* Invalid DLCI ID. Used to invalidate MuxDataService object. */
|
|
#define MUX_CRC_TABLE_LEN 256u /* CRC table length in number of bytes. */
|
|
|
|
#ifndef MBED_CONF_MUX_DLCI_COUNT
|
|
#define MBED_CONF_MUX_DLCI_COUNT 3u /* Number of supported DLCI IDs. */
|
|
#endif
|
|
#ifndef MBED_CONF_MUX_BUFFER_SIZE
|
|
#define MBED_CONF_MUX_BUFFER_SIZE 31u /* Size of TX/Rx buffers in number of bytes. */
|
|
#endif
|
|
|
|
/* More RAM needs to allocated if more than 4 DLCI ID to be supported see @ref tx_callback_context for details. */
|
|
MBED_STATIC_ASSERT(MBED_CONF_MUX_DLCI_COUNT <= 4u, "");
|
|
|
|
|
|
/* @todo:
|
|
I assume that we need to export some kind of #defines for EVENT_SIZE and MAX_EVENT_COUNT (max number of events that can
|
|
be queued at the same time by the module inside EventQueue, so that the application designer can calculate the RAM
|
|
storage requirements correctly at compile time.
|
|
*/
|
|
|
|
namespace mbed {
|
|
|
|
class MuxDataService : public FileHandle
|
|
{
|
|
friend class Mux;
|
|
public:
|
|
|
|
/** Enqueue user data for transmission.
|
|
*
|
|
* @note: This is API is only meant to be used for the multiplexer (user) data service tx. Supplied buffer can be
|
|
* reused/freed upon call return.
|
|
*
|
|
* @param buffer Begin of the user data.
|
|
* @param size The number of bytes to write.
|
|
* @return The number of bytes written.
|
|
*/
|
|
virtual ssize_t write(const void* buffer, size_t size);
|
|
|
|
/** Read user data into a buffer.
|
|
*
|
|
* @note: This is API is only meant to be used for the multiplexer (user) data service rx.
|
|
*
|
|
* @param buffer The buffer to read in to.
|
|
* @param size The number of bytes to read.
|
|
* @return The number of bytes read, -EAGAIN if no data available for read.
|
|
*/
|
|
virtual ssize_t read(void *buffer, size_t size);
|
|
|
|
/** Not supported by the implementation. */
|
|
virtual off_t seek(off_t offset, int whence = SEEK_SET);
|
|
|
|
/** Not supported by the implementation. */
|
|
virtual int close();
|
|
|
|
/** Register a callback on completion of enqueued write and read operations.
|
|
*
|
|
* @note: The registered callback is called within thread context supplied in eventqueue_attach.
|
|
*
|
|
* @param func Function to call upon event generation.
|
|
*/
|
|
virtual void sigio(Callback<void()> func);
|
|
|
|
/** Constructor. */
|
|
MuxDataService() : _dlci(MUX_DLCI_INVALID_ID) {};
|
|
|
|
private:
|
|
|
|
/* Deny copy constructor. */
|
|
MuxDataService(const MuxDataService& obj);
|
|
|
|
/* Deny assignment operator. */
|
|
MuxDataService& operator=(const MuxDataService& obj);
|
|
|
|
uint8_t _dlci; /* DLCI number. Valid range 1 - 63. */
|
|
Callback<void()> _sigio_cb; /* Registered signal callback. */
|
|
};
|
|
|
|
|
|
class Mux
|
|
{
|
|
friend class MuxDataService;
|
|
public:
|
|
|
|
/* Definition for multiplexer establishment status type. */
|
|
enum MuxEstablishStatus {
|
|
MUX_ESTABLISH_SUCCESS = 0, /* Peer accepted the request. */
|
|
MUX_ESTABLISH_REJECT, /* Peer rejected the request. */
|
|
MUX_ESTABLISH_TIMEOUT, /* Timeout occurred for the request. */
|
|
MUX_ESTABLISH_MAX /* Enumeration upper bound. */
|
|
};
|
|
|
|
/* @ref MuxEstablishStatus type assigned to _shared_memory variable, which has sizeof(uint8_t) storage class. Enforce
|
|
expected behaviour compile time. */
|
|
MBED_STATIC_ASSERT(sizeof(MuxEstablishStatus) == sizeof(uint8_t), "");
|
|
|
|
/* Definition for multiplexer establishment return code type. */
|
|
enum MuxReturnStatus {
|
|
MUX_STATUS_SUCCESS = 0,
|
|
MUX_STATUS_INPROGRESS,
|
|
MUX_STATUS_INVALID_RANGE,
|
|
MUX_STATUS_MUX_NOT_OPEN,
|
|
MUX_STATUS_NO_RESOURCE,
|
|
MUX_STATUS_MAX
|
|
};
|
|
|
|
/** Module init. */
|
|
static void module_init();
|
|
|
|
/** Establish the multiplexer control channel.
|
|
*
|
|
* @note: Relevant request specific parameters are fixed at compile time within multiplexer component.
|
|
* @note: Call returns when response from the peer is received, timeout or write error occurs.
|
|
*
|
|
* @param status Operation completion code.
|
|
*
|
|
* @return MUX_STATUS_SUCCESS Operation completed, check status for completion code.
|
|
* @return MUX_STATUS_INPROGRESS Operation not started, control channel open already in progress.
|
|
* @return MUX_STATUS_NO_RESOURCE Operation not started, multiplexer control channel already open.
|
|
*/
|
|
static MuxReturnStatus mux_start(MuxEstablishStatus &status);
|
|
|
|
/** Establish a DLCI.
|
|
*
|
|
* @note: Relevant request specific parameters are fixed at compile time within multiplexer component.
|
|
* @note: Call returns when response from the peer is received or timeout occurs.
|
|
*
|
|
* @warning: Not allowed to be called from callback context.
|
|
*
|
|
* @param dlci_id ID of the DLCI to establish. Valid range 1 - 63.
|
|
* @param status Operation completion code.
|
|
* @param obj Valid object upon status having MUX_ESTABLISH_SUCCESS, NULL upon failure.
|
|
*
|
|
* @return MUX_STATUS_SUCCESS Operation completed, check status for completion code.
|
|
* @return MUX_STATUS_INPROGRESS Operation not started, DLCI establishment already in progress.
|
|
* @return MUX_STATUS_INVALID_RANGE Operation not started, DLCI ID not in valid range.
|
|
* @return MUX_STATUS_MUX_NOT_OPEN Operation not started, no established multiplexer control channel exists.
|
|
* @return MUX_STATUS_NO_RESOURCE Operation not started, dlci_id, or all available DLCI ID resources,
|
|
* already in use.
|
|
*/
|
|
static MuxReturnStatus dlci_establish(uint8_t dlci_id, MuxEstablishStatus &status, FileHandle **obj);
|
|
|
|
/** Attach serial interface to the object.
|
|
*
|
|
* @param serial Serial interface to be used.
|
|
*/
|
|
static void serial_attach(FileHandle *serial);
|
|
|
|
/** Attach EventQueue interface to the object.
|
|
*
|
|
* @param event_queue Event queue interface to be used.
|
|
*/
|
|
static void eventqueue_attach(events::EventQueue *event_queue);
|
|
|
|
private:
|
|
|
|
/* Definition for Rx event type. */
|
|
enum RxEvent {
|
|
RX_READ = 0,
|
|
RX_RESUME,
|
|
RX_EVENT_MAX
|
|
};
|
|
|
|
/* Definition for Tx state machine. */
|
|
enum TxState {
|
|
TX_IDLE = 0,
|
|
TX_RETRANSMIT_ENQUEUE,
|
|
TX_RETRANSMIT_DONE,
|
|
TX_INTERNAL_RESP,
|
|
TX_NORETRANSMIT,
|
|
TX_STATE_MAX
|
|
};
|
|
|
|
/* Definition for Rx state machine. */
|
|
enum RxState {
|
|
RX_FRAME_START = 0,
|
|
RX_HEADER_READ,
|
|
RX_TRAILER_READ,
|
|
RX_SUSPEND,
|
|
RX_STATE_MAX
|
|
};
|
|
|
|
/* Definition for frame type within rx path. */
|
|
enum FrameRxType {
|
|
FRAME_RX_TYPE_SABM = 0,
|
|
FRAME_RX_TYPE_UA,
|
|
FRAME_RX_TYPE_DM,
|
|
FRAME_RX_TYPE_DISC,
|
|
FRAME_RX_TYPE_UIH,
|
|
FRAME_RX_TYPE_NOT_SUPPORTED,
|
|
FRAME_RX_TYPE_MAX
|
|
};
|
|
|
|
/* Definition for frame type within tx path. */
|
|
enum FrameTxType {
|
|
FRAME_TX_TYPE_SABM = 0,
|
|
FRAME_TX_TYPE_DM,
|
|
FRAME_TX_TYPE_UIH,
|
|
FRAME_TX_TYPE_MAX
|
|
};
|
|
|
|
/** Registered time-out expiration event. */
|
|
static void on_timeout();
|
|
|
|
/** Registered deferred call event in safe (thread context) supplied in eventqueue_attach. */
|
|
static void on_deferred_call();
|
|
|
|
/** Registered sigio callback from FileHandle. */
|
|
static void on_sigio();
|
|
|
|
/** Calculate fcs.
|
|
*
|
|
* @param buffer Input buffer.
|
|
* @param input_len Input length in number of bytes.
|
|
*
|
|
* @return Calculated fcs.
|
|
*/
|
|
static uint8_t fcs_calculate(const uint8_t *buffer, uint8_t input_len);
|
|
|
|
/** Construct sabm request message.
|
|
*
|
|
* @param dlci_id ID of the DLCI to establish
|
|
*/
|
|
static void sabm_request_construct(uint8_t dlci_id);
|
|
|
|
/** Construct dm response message.
|
|
*/
|
|
static void dm_response_construct();
|
|
|
|
/** Construct user information frame.
|
|
*
|
|
* @param dlci_id ID of the DLCI to establish
|
|
* @param buffer
|
|
* @param size
|
|
*/
|
|
static void user_information_construct(uint8_t dlci_id, const void *buffer, size_t size);
|
|
|
|
/** Do write operation if pending data available.
|
|
*/
|
|
static void write_do();
|
|
|
|
/** Generate Rx event.
|
|
*
|
|
* @param event Rx event
|
|
*/
|
|
static void rx_event_do(RxEvent event);
|
|
|
|
/** Rx event frame start read state.
|
|
*
|
|
* @return Number of bytes read, -EAGAIN if no data available.
|
|
*/
|
|
static ssize_t on_rx_read_state_frame_start();
|
|
|
|
/** Rx event header read state.
|
|
*
|
|
* @return Number of bytes read, -EAGAIN if no data available.
|
|
*/
|
|
static ssize_t on_rx_read_state_header_read();
|
|
|
|
/** Rx event trailer read state.
|
|
*
|
|
* @return Number of bytes read, -EAGAIN if no data available.
|
|
*/
|
|
static ssize_t on_rx_read_state_trailer_read();
|
|
|
|
/** Rx event suspend read state.
|
|
*
|
|
* @return Number of bytes read, -EAGAIN if no data available.
|
|
*/
|
|
static ssize_t on_rx_read_state_suspend();
|
|
|
|
/** Process received SABM frame. */
|
|
static void on_rx_frame_sabm();
|
|
|
|
/** Process received UA frame. */
|
|
static void on_rx_frame_ua();
|
|
|
|
/** Process received DM frame. */
|
|
static void on_rx_frame_dm();
|
|
|
|
/** Process received DISC frame. */
|
|
static void on_rx_frame_disc();
|
|
|
|
/** Process received UIH frame. */
|
|
static void on_rx_frame_uih();
|
|
|
|
/** Process received frame, which is not supported. */
|
|
static void on_rx_frame_not_supported();
|
|
|
|
/** Process valid received frame. */
|
|
static void valid_rx_frame_decode();
|
|
|
|
/** SABM frame tx path post processing. */
|
|
static void on_post_tx_frame_sabm();
|
|
|
|
/** DM frame tx path post processing. */
|
|
static void on_post_tx_frame_dm();
|
|
|
|
/** UIH frame tx path post processing. */
|
|
static void on_post_tx_frame_uih();
|
|
|
|
/** Resolve rx frame type.
|
|
*
|
|
* @return Frame type.
|
|
*/
|
|
static Mux::FrameRxType frame_rx_type_resolve();
|
|
|
|
/** Resolve tx frame type.
|
|
*
|
|
* @return Frame type.
|
|
*/
|
|
static Mux::FrameTxType frame_tx_type_resolve();
|
|
|
|
/** Begin the frame retransmit sequence. */
|
|
static void frame_retransmit_begin();
|
|
|
|
/** TX state entry functions. */
|
|
static void tx_retransmit_enqueu_entry_run();
|
|
static void tx_retransmit_done_entry_run();
|
|
static void tx_idle_entry_run();
|
|
static void tx_internal_resp_entry_run();
|
|
static void tx_noretransmit_entry_run();
|
|
typedef void (*tx_state_entry_func_t)();
|
|
|
|
/** TX state exit function. */
|
|
static void tx_idle_exit_run();
|
|
typedef void (*tx_state_exit_func_t)();
|
|
|
|
/** Change Tx state machine state.
|
|
*
|
|
* @param new_state State to transit.
|
|
* @param entry_func State entry function.
|
|
* @param exit_func State exit function.
|
|
*/
|
|
static void tx_state_change(TxState new_state, tx_state_entry_func_t entry_func, tx_state_exit_func_t exit_func);
|
|
|
|
/** RX state entry functions. */
|
|
static void rx_header_read_entry_run();
|
|
typedef void (*rx_state_entry_func_t)();
|
|
|
|
/** Null action function. */
|
|
static void null_action();
|
|
|
|
/** Change Rx state machine state.
|
|
*
|
|
* @param new_state State to transit.
|
|
* @param entry_func State entry function.
|
|
*/
|
|
static void rx_state_change(RxState new_state, rx_state_entry_func_t entry_func);
|
|
|
|
/** Begin DM frame transmit sequence. */
|
|
static void dm_response_send();
|
|
|
|
/** Append DLCI ID to storage.
|
|
*
|
|
* @param dlci_id ID of the DLCI to append.
|
|
*/
|
|
static void dlci_id_append(uint8_t dlci_id);
|
|
|
|
/** Get file handle based on DLCI ID.
|
|
*
|
|
* @param dlci_id ID of the DLCI used as the key
|
|
*
|
|
* @return Valid object reference or NULL if not found.
|
|
*/
|
|
static MuxDataService* file_handle_get(uint8_t dlci_id);
|
|
|
|
/** Evaluate is DLCI ID in use.
|
|
*
|
|
* @param dlci_id ID of the DLCI yo evaluate
|
|
*
|
|
* @return True if in use, false otherwise.
|
|
*/
|
|
static bool is_dlci_in_use(uint8_t dlci_id);
|
|
|
|
/** Evaluate is DLCI ID queue full.
|
|
*
|
|
* @return True if full, false otherwise.
|
|
*/
|
|
static bool is_dlci_q_full();
|
|
|
|
/** Begin pending self iniated multiplexer open sequence. */
|
|
static void pending_self_iniated_mux_open_start();
|
|
|
|
/** Begin pending self iniated DLCI establishment sequence. */
|
|
static void pending_self_iniated_dlci_open_start();
|
|
|
|
/** Begin pending peer iniated DLCI establishment sequence.
|
|
*
|
|
* @param dlci_id ID of the DLCI to establish.
|
|
*/
|
|
static void pending_peer_iniated_dlci_open_start(uint8_t dlci_id);
|
|
|
|
/** Enqueue user data for transmission.
|
|
*
|
|
* @note: This is API is only meant to be used for the multiplexer (user) data service tx. Supplied buffer can be
|
|
* reused/freed upon call return.
|
|
*
|
|
* @param dlci_id ID of the DLCI to use.
|
|
* @param buffer Begin of the user data.
|
|
* @param size The number of bytes to write.
|
|
* @return The number of bytes written, negative error on failure.
|
|
*/
|
|
static ssize_t user_data_tx(uint8_t dlci_id, const void* buffer, size_t size);
|
|
|
|
/** Read user data into a buffer.
|
|
*
|
|
* @note: This is API is only meant to be used for the multiplexer (user) data service rx.
|
|
*
|
|
* @param buffer The buffer to read in to.
|
|
* @param size The number of bytes to read.
|
|
* @return The number of bytes read, -EAGAIN if no data availabe for read.
|
|
*/
|
|
static ssize_t user_data_rx(void* buffer, size_t size);
|
|
|
|
/** Clear TX callback pending bit.
|
|
*
|
|
* @param bit Bit to clear.
|
|
*/
|
|
static void tx_callback_pending_bit_clear(uint8_t bit);
|
|
|
|
/** Set TX callback pending bit for supplied DLCI ID.
|
|
*
|
|
* @param dlci_id DLCI ID for bit to set.
|
|
*/
|
|
static void tx_callback_pending_bit_set(uint8_t dlci_id);
|
|
|
|
/** Advance the current TX callback index bit.
|
|
*
|
|
* @return The current TX callback index bit after advanced.
|
|
*/
|
|
static uint8_t tx_callback_index_advance();
|
|
|
|
/** Get the TX callback pending bitmask.
|
|
*
|
|
* @return TX callback pending bitmask.
|
|
*/
|
|
static uint8_t tx_callback_pending_mask_get();
|
|
|
|
/** Dispatch TX callback based on supplied bit.
|
|
*
|
|
* @param bit Bit indetifier of callback to dispatch.
|
|
*/
|
|
static void tx_callback_dispatch(uint8_t bit);
|
|
|
|
/** Run main processing loop for resolving pending TX callbacks and dispatching them if they exists.
|
|
*/
|
|
static void tx_callbacks_run();
|
|
|
|
/** Get data service object based on supplied bit id.
|
|
*
|
|
* @param bit Bit indetifier of data service object to get.
|
|
* @return Data service object reference.
|
|
*/
|
|
static MuxDataService& tx_callback_lookup(uint8_t bit);
|
|
|
|
/** Get minimum of 2 supplied paramaters.
|
|
*
|
|
* @param size_1 1st param for comparisation.
|
|
* @param size_2 2nd param for comparisation.
|
|
* @return Minimum of supplied paramaters.
|
|
*/
|
|
static size_t min(uint8_t size_1, size_t size_2);
|
|
|
|
/** Enqueue callback to event queue.
|
|
*/
|
|
static void event_queue_enqueue();
|
|
|
|
/** Verify is FCS valid in RX frame.
|
|
*
|
|
* @return True upon valid FCS, false otherwise.
|
|
*/
|
|
static bool is_rx_fcs_valid();
|
|
|
|
/* Deny object creation. */
|
|
Mux();
|
|
|
|
/* Deny copy constructor. */
|
|
Mux(const Mux& obj);
|
|
|
|
/* Deny assignment operator. */
|
|
Mux& operator=(const Mux& obj);
|
|
|
|
/* Definition for Tx context type. */
|
|
struct tx_context_t {
|
|
int timer_id; /* Timer id. */
|
|
union {
|
|
uint32_t align_4_byte; /* Force 4-byte alignment. */
|
|
uint8_t buffer[MBED_CONF_MUX_BUFFER_SIZE]; /* Rx buffer. */
|
|
};
|
|
uint8_t retransmit_counter; /* Frame retransmission counter. */
|
|
uint8_t bytes_remaining; /* Bytes remaining in the buffer to write. */
|
|
uint8_t offset; /* Offset in the buffer where to write from. */
|
|
uint8_t tx_callback_context; /* Context for the TX callback dispatching logic as follows:
|
|
- 4 LO bits contain the pending callback mask
|
|
- 4 HI bits contain the current bit used for masking */
|
|
TxState tx_state; /* Tx state machine current state. */
|
|
|
|
};
|
|
|
|
/* Definition for Rx context type. */
|
|
struct rx_context_t {
|
|
union {
|
|
uint32_t align_4_byte; /* Force 4-byte alignment. */
|
|
uint8_t buffer[MBED_CONF_MUX_BUFFER_SIZE]; /* Rx buffer. */
|
|
};
|
|
uint8_t offset; /* Offset in the buffer where to read to. */
|
|
uint8_t read_length; /* Amount to read in number of bytes. */
|
|
RxState rx_state; /* Rx state machine current state. */
|
|
};
|
|
|
|
/* Definition for state type. */
|
|
struct state_t {
|
|
uint16_t is_mux_open : 1; /* True when multiplexer is open. */
|
|
uint16_t is_mux_open_pending : 1; /* True when multiplexer open is pending. */
|
|
uint16_t is_mux_open_running : 1; /* True when multiplexer open is running. */
|
|
uint16_t is_dlci_open_pending : 1; /* True when DLCI open is pending. */
|
|
uint16_t is_dlci_open_running : 1; /* True when DLCI open is running. */
|
|
uint16_t is_system_thread_context : 1; /* True when current context is system thread context. */
|
|
uint16_t is_tx_callback_context : 1; /* True when current context is TX callback context. */
|
|
uint16_t is_user_tx_pending : 1; /* True when user TX is pending. */
|
|
uint16_t is_user_rx_ready : 1; /* True when user RX is ready/available. */
|
|
};
|
|
|
|
static FileHandle* _serial; /* Serial used. */
|
|
static events::EventQueue* _event_q; /* Event queue used. */
|
|
static rtos::Semaphore _semaphore; /* Semaphore used. */
|
|
static PlatformMutex _mutex; /* Mutex used. */
|
|
static MuxDataService _mux_objects[MBED_CONF_MUX_DLCI_COUNT]; /* Number of supported DLCIs. */
|
|
static tx_context_t _tx_context; /* Tx context. */
|
|
static rx_context_t _rx_context; /* Rx context. */
|
|
static state_t _state; /* General state context. */
|
|
static const uint8_t _crctable[MUX_CRC_TABLE_LEN]; /* CRC table used for frame FCS. */
|
|
static uint8_t _shared_memory; /* Shared memory used passing data between user and
|
|
system threads. */
|
|
};
|
|
|
|
} // namespace mbed
|
|
|
|
#endif
|