| /* |
| * Copyright (c) 2019 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(conn_mgr, CONFIG_NET_CONNECTION_MANAGER_LOG_LEVEL); |
| |
| #include <zephyr/init.h> |
| #include <zephyr/kernel.h> |
| #include <errno.h> |
| #include <zephyr/net/net_core.h> |
| #include <zephyr/net/net_if.h> |
| #include <zephyr/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); |