1/*
2 * FST module implementation
3 * Copyright (c) 2014, Qualcomm Atheros, Inc.
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9#include "utils/includes.h"
10
11#include "utils/common.h"
12#include "utils/eloop.h"
13#include "fst/fst.h"
14#include "fst/fst_internal.h"
15#include "fst/fst_defs.h"
16#include "fst/fst_ctrl_iface.h"
17
18static int fst_global_initialized = 0;
19struct dl_list fst_global_ctrls_list;
20
21
22static void fst_ctrl_iface_notify_peer_state_change(struct fst_iface *iface,
23						    bool connected,
24						    const u8 *peer_addr)
25{
26	union fst_event_extra extra;
27
28	extra.peer_state.connected = connected;
29	os_strlcpy(extra.peer_state.ifname, fst_iface_get_name(iface),
30		   sizeof(extra.peer_state.ifname));
31	os_memcpy(extra.peer_state.addr, peer_addr, ETH_ALEN);
32
33	foreach_fst_ctrl_call(on_event, EVENT_PEER_STATE_CHANGED,
34			      iface, NULL, &extra);
35}
36
37
38struct fst_iface * fst_attach(const char *ifname, const u8 *own_addr,
39			      const struct fst_wpa_obj *iface_obj,
40			      const struct fst_iface_cfg *cfg)
41{
42	struct fst_group *g;
43	struct fst_group *group = NULL;
44	struct fst_iface *iface = NULL;
45	bool new_group = false;
46
47	WPA_ASSERT(ifname != NULL);
48	WPA_ASSERT(iface_obj != NULL);
49	WPA_ASSERT(cfg != NULL);
50
51	foreach_fst_group(g) {
52		if (os_strcmp(cfg->group_id, fst_group_get_id(g)) == 0) {
53			group = g;
54			break;
55		}
56	}
57
58	if (!group) {
59		group = fst_group_create(cfg->group_id);
60		if (!group) {
61			fst_printf(MSG_ERROR, "%s: FST group cannot be created",
62				   cfg->group_id);
63			return NULL;
64		}
65		new_group = true;
66	}
67
68	iface = fst_iface_create(group, ifname, own_addr, iface_obj, cfg);
69	if (!iface) {
70		fst_printf_group(group, MSG_ERROR, "cannot create iface for %s",
71				 ifname);
72		if (new_group)
73			fst_group_delete(group);
74		return NULL;
75	}
76
77	fst_group_attach_iface(group, iface);
78	fst_group_update_ie(group);
79
80	foreach_fst_ctrl_call(on_iface_added, iface);
81
82	fst_printf_iface(iface, MSG_DEBUG,
83			 "iface attached to group %s (prio=%d, llt=%d)",
84			 cfg->group_id, cfg->priority, cfg->llt);
85
86	return iface;
87}
88
89
90void fst_detach(struct fst_iface *iface)
91{
92	struct fst_group *group = fst_iface_get_group(iface);
93
94	fst_printf_iface(iface, MSG_DEBUG, "iface detached from group %s",
95			 fst_group_get_id(group));
96	fst_session_global_on_iface_detached(iface);
97	foreach_fst_ctrl_call(on_iface_removed, iface);
98	fst_group_detach_iface(group, iface);
99	fst_iface_delete(iface);
100	fst_group_update_ie(group);
101	fst_group_delete_if_empty(group);
102}
103
104
105int fst_global_init(void)
106{
107	dl_list_init(&fst_global_groups_list);
108	dl_list_init(&fst_global_ctrls_list);
109	fst_session_global_init();
110	fst_global_initialized = 1;
111	return 0;
112}
113
114
115void fst_global_deinit(void)
116{
117	struct fst_group *group;
118	struct fst_ctrl_handle *h;
119
120	if (!fst_global_initialized)
121		return;
122
123	fst_session_global_deinit();
124	while ((group = fst_first_group()) != NULL)
125		fst_group_delete(group);
126	while ((h = dl_list_first(&fst_global_ctrls_list,
127				  struct fst_ctrl_handle,
128				  global_ctrls_lentry)))
129		fst_global_del_ctrl(h);
130	fst_global_initialized = 0;
131}
132
133
134struct fst_ctrl_handle * fst_global_add_ctrl(const struct fst_ctrl *ctrl)
135{
136	struct fst_ctrl_handle *h;
137
138	if (!ctrl)
139		return NULL;
140
141	h = os_zalloc(sizeof(*h));
142	if (!h)
143		return NULL;
144
145	if (ctrl->init && ctrl->init()) {
146		os_free(h);
147		return NULL;
148	}
149
150	h->ctrl = *ctrl;
151	dl_list_add_tail(&fst_global_ctrls_list, &h->global_ctrls_lentry);
152
153	return h;
154}
155
156
157void fst_global_del_ctrl(struct fst_ctrl_handle *h)
158{
159	dl_list_del(&h->global_ctrls_lentry);
160	if (h->ctrl.deinit)
161		h->ctrl.deinit();
162	os_free(h);
163}
164
165
166void fst_rx_action(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt,
167		   size_t len)
168{
169	if (fst_iface_is_connected(iface, mgmt->sa, false))
170		fst_session_on_action_rx(iface, mgmt, len);
171	else
172		wpa_printf(MSG_DEBUG,
173			   "FST: Ignore FST Action frame - no FST connection with "
174			   MACSTR, MAC2STR(mgmt->sa));
175}
176
177
178void fst_notify_peer_connected(struct fst_iface *iface, const u8 *addr)
179{
180	if (is_zero_ether_addr(addr))
181		return;
182
183#ifndef HOSTAPD
184	fst_group_update_ie(fst_iface_get_group(iface));
185#endif /* HOSTAPD */
186
187	fst_printf_iface(iface, MSG_DEBUG, MACSTR " became connected",
188			 MAC2STR(addr));
189
190	fst_ctrl_iface_notify_peer_state_change(iface, true, addr);
191}
192
193
194void fst_notify_peer_disconnected(struct fst_iface *iface, const u8 *addr)
195{
196	if (is_zero_ether_addr(addr))
197		return;
198
199#ifndef HOSTAPD
200	fst_group_update_ie(fst_iface_get_group(iface));
201#endif /* HOSTAPD */
202
203	fst_printf_iface(iface, MSG_DEBUG, MACSTR " became disconnected",
204			 MAC2STR(addr));
205
206	fst_ctrl_iface_notify_peer_state_change(iface, false, addr);
207}
208
209
210bool fst_are_ifaces_aggregated(struct fst_iface *iface1,
211			       struct fst_iface *iface2)
212{
213	return fst_iface_get_group(iface1) == fst_iface_get_group(iface2);
214}
215
216
217void fst_update_mac_addr(struct fst_iface *iface, const u8 *addr)
218{
219	fst_printf_iface(iface, MSG_DEBUG, "new MAC address " MACSTR,
220			 MAC2STR(addr));
221	os_memcpy(iface->own_addr, addr, sizeof(iface->own_addr));
222	fst_group_update_ie(fst_iface_get_group(iface));
223}
224
225
226enum mb_band_id fst_hw_mode_to_band(enum hostapd_hw_mode mode)
227{
228	switch (mode) {
229	case HOSTAPD_MODE_IEEE80211B:
230	case HOSTAPD_MODE_IEEE80211G:
231		return MB_BAND_ID_WIFI_2_4GHZ;
232	case HOSTAPD_MODE_IEEE80211A:
233		return MB_BAND_ID_WIFI_5GHZ;
234	case HOSTAPD_MODE_IEEE80211AD:
235		return MB_BAND_ID_WIFI_60GHZ;
236	default:
237		WPA_ASSERT(0);
238		return MB_BAND_ID_WIFI_2_4GHZ;
239	}
240}
241