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