| /* |
| * FST module implementation |
| * Copyright (c) 2014, Qualcomm Atheros, Inc. |
| * |
| * This software may be distributed under the terms of the BSD license. |
| * See README for more details. |
| */ |
| |
| #include "utils/includes.h" |
| |
| #include "utils/common.h" |
| #include "utils/eloop.h" |
| #include "fst/fst.h" |
| #include "fst/fst_internal.h" |
| #include "fst/fst_defs.h" |
| #include "fst/fst_ctrl_iface.h" |
| |
| static int fst_global_initialized = 0; |
| struct dl_list fst_global_ctrls_list; |
| |
| |
| static void fst_ctrl_iface_notify_peer_state_change(struct fst_iface *iface, |
| bool connected, |
| const u8 *peer_addr) |
| { |
| union fst_event_extra extra; |
| |
| extra.peer_state.connected = connected; |
| os_strlcpy(extra.peer_state.ifname, fst_iface_get_name(iface), |
| sizeof(extra.peer_state.ifname)); |
| os_memcpy(extra.peer_state.addr, peer_addr, ETH_ALEN); |
| |
| foreach_fst_ctrl_call(on_event, EVENT_PEER_STATE_CHANGED, |
| iface, NULL, &extra); |
| } |
| |
| |
| struct fst_iface * fst_attach(const char *ifname, const u8 *own_addr, |
| const struct fst_wpa_obj *iface_obj, |
| const struct fst_iface_cfg *cfg) |
| { |
| struct fst_group *g; |
| struct fst_group *group = NULL; |
| struct fst_iface *iface = NULL; |
| bool new_group = false; |
| |
| WPA_ASSERT(ifname != NULL); |
| WPA_ASSERT(iface_obj != NULL); |
| WPA_ASSERT(cfg != NULL); |
| |
| foreach_fst_group(g) { |
| if (os_strcmp(cfg->group_id, fst_group_get_id(g)) == 0) { |
| group = g; |
| break; |
| } |
| } |
| |
| if (!group) { |
| group = fst_group_create(cfg->group_id); |
| if (!group) { |
| fst_printf(MSG_ERROR, "%s: FST group cannot be created", |
| cfg->group_id); |
| return NULL; |
| } |
| new_group = true; |
| } |
| |
| iface = fst_iface_create(group, ifname, own_addr, iface_obj, cfg); |
| if (!iface) { |
| fst_printf_group(group, MSG_ERROR, "cannot create iface for %s", |
| ifname); |
| if (new_group) |
| fst_group_delete(group); |
| return NULL; |
| } |
| |
| fst_group_attach_iface(group, iface); |
| fst_group_update_ie(group); |
| |
| foreach_fst_ctrl_call(on_iface_added, iface); |
| |
| fst_printf_iface(iface, MSG_DEBUG, |
| "iface attached to group %s (prio=%d, llt=%d)", |
| cfg->group_id, cfg->priority, cfg->llt); |
| |
| return iface; |
| } |
| |
| |
| void fst_detach(struct fst_iface *iface) |
| { |
| struct fst_group *group = fst_iface_get_group(iface); |
| |
| fst_printf_iface(iface, MSG_DEBUG, "iface detached from group %s", |
| fst_group_get_id(group)); |
| fst_session_global_on_iface_detached(iface); |
| foreach_fst_ctrl_call(on_iface_removed, iface); |
| fst_group_detach_iface(group, iface); |
| fst_iface_delete(iface); |
| fst_group_update_ie(group); |
| fst_group_delete_if_empty(group); |
| } |
| |
| |
| int fst_global_init(void) |
| { |
| dl_list_init(&fst_global_groups_list); |
| dl_list_init(&fst_global_ctrls_list); |
| fst_session_global_init(); |
| fst_global_initialized = 1; |
| return 0; |
| } |
| |
| |
| void fst_global_deinit(void) |
| { |
| struct fst_group *group; |
| struct fst_ctrl_handle *h; |
| |
| if (!fst_global_initialized) |
| return; |
| |
| fst_session_global_deinit(); |
| while ((group = fst_first_group()) != NULL) |
| fst_group_delete(group); |
| while ((h = dl_list_first(&fst_global_ctrls_list, |
| struct fst_ctrl_handle, |
| global_ctrls_lentry))) |
| fst_global_del_ctrl(h); |
| fst_global_initialized = 0; |
| } |
| |
| |
| struct fst_ctrl_handle * fst_global_add_ctrl(const struct fst_ctrl *ctrl) |
| { |
| struct fst_ctrl_handle *h; |
| |
| if (!ctrl) |
| return NULL; |
| |
| h = os_zalloc(sizeof(*h)); |
| if (!h) |
| return NULL; |
| |
| if (ctrl->init && ctrl->init()) { |
| os_free(h); |
| return NULL; |
| } |
| |
| h->ctrl = *ctrl; |
| dl_list_add_tail(&fst_global_ctrls_list, &h->global_ctrls_lentry); |
| |
| return h; |
| } |
| |
| |
| void fst_global_del_ctrl(struct fst_ctrl_handle *h) |
| { |
| dl_list_del(&h->global_ctrls_lentry); |
| if (h->ctrl.deinit) |
| h->ctrl.deinit(); |
| os_free(h); |
| } |
| |
| |
| void fst_rx_action(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt, |
| size_t len) |
| { |
| if (fst_iface_is_connected(iface, mgmt->sa, false)) |
| fst_session_on_action_rx(iface, mgmt, len); |
| else |
| wpa_printf(MSG_DEBUG, |
| "FST: Ignore FST Action frame - no FST connection with " |
| MACSTR, MAC2STR(mgmt->sa)); |
| } |
| |
| |
| void fst_notify_peer_connected(struct fst_iface *iface, const u8 *addr) |
| { |
| if (is_zero_ether_addr(addr)) |
| return; |
| |
| #ifndef HOSTAPD |
| fst_group_update_ie(fst_iface_get_group(iface)); |
| #endif /* HOSTAPD */ |
| |
| fst_printf_iface(iface, MSG_DEBUG, MACSTR " became connected", |
| MAC2STR(addr)); |
| |
| fst_ctrl_iface_notify_peer_state_change(iface, true, addr); |
| } |
| |
| |
| void fst_notify_peer_disconnected(struct fst_iface *iface, const u8 *addr) |
| { |
| if (is_zero_ether_addr(addr)) |
| return; |
| |
| #ifndef HOSTAPD |
| fst_group_update_ie(fst_iface_get_group(iface)); |
| #endif /* HOSTAPD */ |
| |
| fst_printf_iface(iface, MSG_DEBUG, MACSTR " became disconnected", |
| MAC2STR(addr)); |
| |
| fst_ctrl_iface_notify_peer_state_change(iface, false, addr); |
| } |
| |
| |
| bool fst_are_ifaces_aggregated(struct fst_iface *iface1, |
| struct fst_iface *iface2) |
| { |
| return fst_iface_get_group(iface1) == fst_iface_get_group(iface2); |
| } |
| |
| |
| void fst_update_mac_addr(struct fst_iface *iface, const u8 *addr) |
| { |
| fst_printf_iface(iface, MSG_DEBUG, "new MAC address " MACSTR, |
| MAC2STR(addr)); |
| os_memcpy(iface->own_addr, addr, sizeof(iface->own_addr)); |
| fst_group_update_ie(fst_iface_get_group(iface)); |
| } |
| |
| |
| enum mb_band_id fst_hw_mode_to_band(enum hostapd_hw_mode mode) |
| { |
| switch (mode) { |
| case HOSTAPD_MODE_IEEE80211B: |
| case HOSTAPD_MODE_IEEE80211G: |
| return MB_BAND_ID_WIFI_2_4GHZ; |
| case HOSTAPD_MODE_IEEE80211A: |
| return MB_BAND_ID_WIFI_5GHZ; |
| case HOSTAPD_MODE_IEEE80211AD: |
| return MB_BAND_ID_WIFI_60GHZ; |
| default: |
| WPA_ASSERT(0); |
| return MB_BAND_ID_WIFI_2_4GHZ; |
| } |
| } |