diff --git a/impl/src/layer2/connection_list.c b/impl/src/layer2/connection_list.c new file mode 100644 index 0000000..1a58990 --- /dev/null +++ b/impl/src/layer2/connection_list.c @@ -0,0 +1,142 @@ +#include +#include + +#include "connection_list.h" + +#include "results.h" + +#define SEQ_NR_MASK 0xF + +// find the location where the timestamp should be inserted to maintain +// ascending timestamp order and return the then-previous entry. +// +// If NULL is returned, the list is either empty or the entry should be +// inserted before the head. This must be checked externally. +static connection_list_entry_t* find_prev_for_timestamp(connection_list_t *list, uint64_t timestamp) +{ + if(!list->head) { + return NULL; + } + + if(timestamp < list->head->next_access_time) { + return NULL; + } + + connection_list_entry_t *prev = list->head; + while(prev->next) { + if(prev->next_access_time < timestamp + && prev->next->next_access_time >= timestamp) { + // location found! + break; + } + + prev = prev->next; + } + + // either the correct location was found or prev now points to the last entry + // in the list. + return prev; +} + +result_t connection_list_init(connection_list_t *list) +{ + list->head = NULL; + return OK; +} + + +void connection_list_destroy(connection_list_t *list) +{ + // delete all list entries + while(list->head) { + connection_list_entry_t *next = list->head->next; + free(list->head); + list->head = next; + } +} + + +result_t connection_list_insert(connection_list_t *list, const connection_ctx_t *conn, uint64_t next_access_time) +{ + connection_list_entry_t *new_entry = malloc(sizeof(connection_list_entry_t)); + if(!new_entry) { + return ERR_NO_MEM; + } + + new_entry->connection = *conn; + new_entry->next_access_time = next_access_time; + + connection_list_entry_t *prev = find_prev_for_timestamp(list, next_access_time); + + if(!prev) { + // the new entry should become the list’s head (also works if the list is empty!) + new_entry->next = list->head; + list->head = new_entry; + } else { + // insert after prev + new_entry->next = prev->next; + prev->next = new_entry; + } + + return OK; +} + + +connection_list_entry_t* connection_list_get_head(connection_list_t *list) +{ + return list->head; +} + + +result_t connection_list_reschedule_head(connection_list_t *list, uint64_t next_access_timestamp) +{ + if(!list->head) { + return ERR_INVALID_STATE; + } + + if(!list->head->next) { + // nothing to do because there is only one entry. + return OK; + } + + if(next_access_timestamp < list->head->next->next_access_time) { + // the head does not need to be moved because the new timestamp is still + // smaller than that of the second entry => only update the timestamp. + list->head->next_access_time = next_access_timestamp; + return OK; + } + + // detach the entry from the list + connection_list_entry_t *reloc_entry = list->head; + list->head = list->head->next; + + // find the location where the entry should be reinserted + connection_list_entry_t *prev = find_prev_for_timestamp(list, next_access_timestamp); + + if(!prev) { + // the relocated entry should become the list’s head (also works if the list is empty!) + reloc_entry->next = list->head; + list->head = reloc_entry; + } else { + // insert after prev + reloc_entry->next = prev->next; + prev->next = reloc_entry; + } + + return OK; +} + + +result_t connection_list_delete_head(connection_list_t *list) +{ + if(!list->head) { + return ERR_INVALID_STATE; + } + + connection_list_entry_t *new_head = list->head->next; + + free(list->head); + list->head = new_head; + + return OK; +} diff --git a/impl/src/layer2/connection_list.h b/impl/src/layer2/connection_list.h new file mode 100644 index 0000000..3b16807 --- /dev/null +++ b/impl/src/layer2/connection_list.h @@ -0,0 +1,78 @@ +/* + * This file contains functions to manage a list of layer 2 connections for + * scheduling purposes. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Copyright (C) 2024 Thomas Kolb + */ + +#ifndef CONNECTION_LIST_H +#define CONNECTION_LIST_H + +#include +#include + +#include "connection.h" + +typedef struct connection_list_entry_s { + uint64_t next_access_time; //!< When to next activate this connection + connection_ctx_t connection; //!< The actual connection entry + + struct connection_list_entry_s *next; //!< pointer to the next list element +} connection_list_entry_t; + +typedef struct connection_list_s { + connection_list_entry_t *head; //!< pointer to the first list element +} connection_list_t; + + +/*!\brief Initialize a connection list. + * + * \param list The list to initialize. + * \returns OK if everything worked or a fitting error code. + */ +result_t connection_list_init(connection_list_t *list); + +/*!\brief Destroy the given layer 2 connection list, deleting all internal data. + */ +void connection_list_destroy(connection_list_t *list); + +/*!\brief Insert a connection context into the list. + * + * The connection context will be copied internally. The original copy should + * no longer be used after the context was inserted into the list. + * + * \param list Pointer to the list where the entry shall be inserted. + * \param conn Pointer to the connection context. + * \param next_access_time Timestamp when this connection should be activated. + * \returns OK if everything worked or a fitting error code. + */ +result_t connection_list_insert(connection_list_t *list, const connection_ctx_t *conn, uint64_t next_access_time); + +/*!\brief Get the head (first entry) of the list. + * + * \param list Pointer to the list to access. + * + * \returns A pointer to the head entry or NULL if the list is empty. + */ +connection_list_entry_t* connection_list_get_head(connection_list_t *list); + +/*!\brief Re-schedule the current head entry. + * + * \param list Pointer to the list to access. + * \param next_access_time Timestamp when this connection should be activated again. + * + * \returns OK if everything worked or a fitting error code. + */ +result_t connection_list_reschedule_head(connection_list_t *list, uint64_t next_access_timestamp); + +/*!\brief Delete the current head entry. + * + * \param list Pointer to the list to access. + * + * \returns OK if everything worked or a fitting error code. + */ +result_t connection_list_delete_head(connection_list_t *list); + +#endif // CONNECTION_LIST_H