219 lines
4.9 KiB
C
219 lines
4.9 KiB
C
/*
|
|
* Copyright (c) 2019 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <logging/log.h>
|
|
LOG_MODULE_REGISTER(conn_mgr, CONFIG_NET_CONNECTION_MANAGER_LOG_LEVEL);
|
|
|
|
#include <init.h>
|
|
#include <kernel.h>
|
|
#include <errno.h>
|
|
#include <net/net_core.h>
|
|
#include <net/net_if.h>
|
|
#include <net/net_mgmt.h>
|
|
|
|
#include <conn_mgr.h>
|
|
|
|
#if IS_ENABLED(CONFIG_NET_TC_THREAD_COOPERATIVE)
|
|
#define THREAD_PRIORITY K_PRIO_COOP(CONFIG_NUM_COOP_PRIORITIES - 1)
|
|
#else
|
|
#define THREAD_PRIORITY K_PRIO_PREEMPT(7)
|
|
#endif
|
|
|
|
uint16_t iface_states[CONN_MGR_IFACE_MAX];
|
|
|
|
K_SEM_DEFINE(conn_mgr_lock, 1, K_SEM_MAX_LIMIT);
|
|
|
|
static enum net_conn_mgr_state conn_mgr_iface_status(int index)
|
|
{
|
|
if (iface_states[index] & NET_STATE_IFACE_UP) {
|
|
return NET_CONN_MGR_STATE_CONNECTED;
|
|
}
|
|
|
|
return NET_CONN_MGR_STATE_DISCONNECTED;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_IPV6)
|
|
static enum net_conn_mgr_state conn_mgr_ipv6_status(int index)
|
|
{
|
|
if ((iface_states[index] & CONN_MGR_IPV6_STATUS_MASK) ==
|
|
CONN_MGR_IPV6_STATUS_MASK) {
|
|
NET_DBG("IPv6 connected on iface index %u", index + 1);
|
|
return NET_CONN_MGR_STATE_CONNECTED;
|
|
}
|
|
|
|
return NET_CONN_MGR_STATE_DISCONNECTED;
|
|
}
|
|
#else
|
|
#define conn_mgr_ipv6_status(...) NET_CONN_MGR_STATE_CONNECTED
|
|
#endif /* CONFIG_NET_IPV6 */
|
|
|
|
#if defined(CONFIG_NET_IPV4)
|
|
static enum net_conn_mgr_state conn_mgr_ipv4_status(int index)
|
|
{
|
|
if ((iface_states[index] & CONN_MGR_IPV4_STATUS_MASK) ==
|
|
CONN_MGR_IPV4_STATUS_MASK) {
|
|
NET_DBG("IPv4 connected on iface index %u", index + 1);
|
|
return NET_CONN_MGR_STATE_CONNECTED;
|
|
}
|
|
|
|
return NET_CONN_MGR_STATE_DISCONNECTED;
|
|
}
|
|
#else
|
|
#define conn_mgr_ipv4_status(...) NET_CONN_MGR_STATE_CONNECTED
|
|
#endif /* CONFIG_NET_IPV4 */
|
|
|
|
static void conn_mgr_notify_status(int index)
|
|
{
|
|
struct net_if *iface = net_if_get_by_index(index + 1);
|
|
|
|
if (iface == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (iface_states[index] & NET_STATE_CONNECTED) {
|
|
NET_DBG("Iface %d (%p) connected",
|
|
net_if_get_by_iface(iface), iface);
|
|
net_mgmt_event_notify(NET_EVENT_L4_CONNECTED, iface);
|
|
} else {
|
|
NET_DBG("Iface %d (%p) disconnected",
|
|
net_if_get_by_iface(iface), iface);
|
|
net_mgmt_event_notify(NET_EVENT_L4_DISCONNECTED, iface);
|
|
}
|
|
}
|
|
|
|
static void conn_mgr_act_on_changes(void)
|
|
{
|
|
int idx;
|
|
|
|
for (idx = 0; idx < ARRAY_SIZE(iface_states); idx++) {
|
|
enum net_conn_mgr_state state;
|
|
|
|
if (iface_states[idx] == 0) {
|
|
/* This interface is not used */
|
|
continue;
|
|
}
|
|
|
|
if (!(iface_states[idx] & NET_STATE_CHANGED)) {
|
|
continue;
|
|
}
|
|
|
|
state = NET_CONN_MGR_STATE_CONNECTED;
|
|
|
|
state &= conn_mgr_iface_status(idx);
|
|
if (state) {
|
|
enum net_conn_mgr_state ip_state =
|
|
NET_CONN_MGR_STATE_DISCONNECTED;
|
|
|
|
if (IS_ENABLED(CONFIG_NET_IPV6)) {
|
|
ip_state |= conn_mgr_ipv6_status(idx);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_NET_IPV4)) {
|
|
ip_state |= conn_mgr_ipv4_status(idx);
|
|
}
|
|
|
|
state &= ip_state;
|
|
}
|
|
|
|
iface_states[idx] &= ~NET_STATE_CHANGED;
|
|
|
|
if (state == NET_CONN_MGR_STATE_CONNECTED &&
|
|
!(iface_states[idx] & NET_STATE_CONNECTED)) {
|
|
iface_states[idx] |= NET_STATE_CONNECTED;
|
|
|
|
conn_mgr_notify_status(idx);
|
|
} else if (state != NET_CONN_MGR_STATE_CONNECTED &&
|
|
(iface_states[idx] & NET_STATE_CONNECTED)) {
|
|
iface_states[idx] &= ~NET_STATE_CONNECTED;
|
|
|
|
conn_mgr_notify_status(idx);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void conn_mgr_initial_state(struct net_if *iface)
|
|
{
|
|
int idx = net_if_get_by_iface(iface) - 1;
|
|
|
|
if (net_if_is_up(iface)) {
|
|
NET_DBG("Iface %p UP", iface);
|
|
iface_states[idx] = NET_STATE_IFACE_UP;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_NET_NATIVE_IPV6)) {
|
|
if (net_if_ipv6_get_global_addr(NET_ADDR_PREFERRED, &iface)) {
|
|
NET_DBG("IPv6 addr set");
|
|
iface_states[idx] |= NET_STATE_IPV6_ADDR_SET |
|
|
NET_STATE_IPV6_DAD_OK;
|
|
} else if (net_if_ipv6_get_global_addr(NET_ADDR_TENTATIVE,
|
|
&iface)) {
|
|
iface_states[idx] |= NET_STATE_IPV6_ADDR_SET;
|
|
}
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_NET_NATIVE_IPV4)) {
|
|
if (net_if_ipv4_get_global_addr(iface, NET_ADDR_PREFERRED)) {
|
|
NET_DBG("IPv4 addr set");
|
|
iface_states[idx] |= NET_STATE_IPV4_ADDR_SET;
|
|
}
|
|
|
|
}
|
|
|
|
iface_states[idx] |= NET_STATE_CHANGED;
|
|
}
|
|
|
|
static void conn_mgr_init_cb(struct net_if *iface, void *user_data)
|
|
{
|
|
ARG_UNUSED(user_data);
|
|
|
|
conn_mgr_initial_state(iface);
|
|
}
|
|
|
|
static void conn_mgr_handler(void)
|
|
{
|
|
conn_mgr_init_events_handler();
|
|
|
|
net_if_foreach(conn_mgr_init_cb, NULL);
|
|
|
|
NET_DBG("Connection Manager started");
|
|
|
|
while (true) {
|
|
k_sem_take(&conn_mgr_lock, K_FOREVER);
|
|
|
|
conn_mgr_act_on_changes();
|
|
}
|
|
}
|
|
|
|
K_THREAD_DEFINE(conn_mgr, CONFIG_NET_CONNECTION_MANAGER_STACK_SIZE,
|
|
(k_thread_entry_t)conn_mgr_handler, NULL, NULL, NULL,
|
|
THREAD_PRIORITY, 0, 0);
|
|
|
|
void net_conn_mgr_resend_status(void)
|
|
{
|
|
int idx;
|
|
|
|
for (idx = 0; idx < ARRAY_SIZE(iface_states); idx++) {
|
|
conn_mgr_notify_status(idx);
|
|
}
|
|
}
|
|
|
|
static int conn_mgr_init(const struct device *dev)
|
|
{
|
|
int i;
|
|
|
|
ARG_UNUSED(dev);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(iface_states); i++) {
|
|
iface_states[i] = 0;
|
|
}
|
|
|
|
k_thread_start(conn_mgr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
SYS_INIT(conn_mgr_init, APPLICATION, CONFIG_NET_CONNECTION_MANAGER_PRIORITY);
|