hostapd: reimplement AP/STA support via ucode
[openwrt/staging/nbd.git] / package / network / services / hostapd / src / wpa_supplicant / ucode.c
1 #include "utils/includes.h"
2 #include "utils/common.h"
3 #include "utils/ucode.h"
4 #include "drivers/driver.h"
5 #include "wpa_supplicant_i.h"
6 #include "wps_supplicant.h"
7 #include "bss.h"
8 #include "ucode.h"
9
10 static struct wpa_global *wpa_global;
11 static uc_resource_type_t *global_type, *iface_type;
12 static uc_value_t *global, *iface_registry;
13 static uc_vm_t *vm;
14
15 static uc_value_t *
16 wpas_ucode_iface_get_uval(struct wpa_supplicant *wpa_s)
17 {
18 uc_value_t *val;
19
20 if (wpa_s->ucode.idx)
21 return wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
22
23 val = uc_resource_new(iface_type, wpa_s);
24 wpa_ucode_registry_add(iface_registry, val, &wpa_s->ucode.idx);
25
26 return val;
27 }
28
29 static void
30 wpas_ucode_update_interfaces(void)
31 {
32 uc_value_t *ifs = ucv_object_new(vm);
33 struct wpa_supplicant *wpa_s;
34 int i;
35
36 for (wpa_s = wpa_global->ifaces; wpa_s; wpa_s = wpa_s->next)
37 ucv_object_add(ifs, wpa_s->ifname, ucv_get(wpas_ucode_iface_get_uval(wpa_s)));
38
39 ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs));
40 ucv_gc(vm);
41 }
42
43 void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s)
44 {
45 uc_value_t *val;
46
47 if (wpa_ucode_call_prepare("iface_add"))
48 return;
49
50 uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
51 uc_value_push(ucv_get(wpas_ucode_iface_get_uval(wpa_s)));
52 ucv_put(wpa_ucode_call(2));
53 ucv_gc(vm);
54 }
55
56 void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s)
57 {
58 uc_value_t *val;
59
60 val = wpa_ucode_registry_remove(iface_registry, wpa_s->ucode.idx);
61 if (!val)
62 return;
63
64 wpa_s->ucode.idx = 0;
65 if (wpa_ucode_call_prepare("iface_remove"))
66 return;
67
68 uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
69 uc_value_push(ucv_get(val));
70 ucv_put(wpa_ucode_call(2));
71 ucv_gc(vm);
72 }
73
74 void wpas_ucode_update_state(struct wpa_supplicant *wpa_s)
75 {
76 const char *state;
77 uc_value_t *val;
78
79 val = wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
80 if (!val)
81 return;
82
83 if (wpa_ucode_call_prepare("state"))
84 return;
85
86 state = wpa_supplicant_state_txt(wpa_s->wpa_state);
87 uc_value_push(ucv_get(val));
88 uc_value_push(ucv_get(ucv_string_new(state)));
89 ucv_put(wpa_ucode_call(2));
90 ucv_gc(vm);
91 }
92
93 void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data)
94 {
95 const char *state;
96 uc_value_t *val;
97
98 if (event != EVENT_CH_SWITCH_STARTED)
99 return;
100
101 val = wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
102 if (!val)
103 return;
104
105 if (wpa_ucode_call_prepare("event"))
106 return;
107
108 uc_value_push(ucv_get(val));
109 uc_value_push(ucv_get(ucv_string_new(event_to_string(event))));
110 val = ucv_object_new(vm);
111 uc_value_push(ucv_get(val));
112
113 if (event == EVENT_CH_SWITCH_STARTED) {
114 ucv_object_add(val, "csa_count", ucv_int64_new(data->ch_switch.count));
115 ucv_object_add(val, "frequency", ucv_int64_new(data->ch_switch.freq));
116 ucv_object_add(val, "sec_chan_offset", ucv_int64_new(data->ch_switch.ch_offset));
117 ucv_object_add(val, "center_freq1", ucv_int64_new(data->ch_switch.cf1));
118 ucv_object_add(val, "center_freq2", ucv_int64_new(data->ch_switch.cf2));
119 }
120
121 ucv_put(wpa_ucode_call(3));
122 ucv_gc(vm);
123 }
124
125 static const char *obj_stringval(uc_value_t *obj, const char *name)
126 {
127 uc_value_t *val = ucv_object_get(obj, name, NULL);
128
129 return ucv_string_get(val);
130 }
131
132 static uc_value_t *
133 uc_wpas_add_iface(uc_vm_t *vm, size_t nargs)
134 {
135 uc_value_t *info = uc_fn_arg(0);
136 uc_value_t *ifname = ucv_object_get(info, "iface", NULL);
137 uc_value_t *bridge = ucv_object_get(info, "bridge", NULL);
138 uc_value_t *config = ucv_object_get(info, "config", NULL);
139 uc_value_t *ctrl = ucv_object_get(info, "ctrl", NULL);
140 struct wpa_interface iface;
141 int ret = -1;
142
143 if (ucv_type(info) != UC_OBJECT)
144 goto out;
145
146 iface = (struct wpa_interface){
147 .driver = "nl80211",
148 .ifname = ucv_string_get(ifname),
149 .bridge_ifname = ucv_string_get(bridge),
150 .confname = ucv_string_get(config),
151 .ctrl_interface = ucv_string_get(ctrl),
152 };
153
154 if (!iface.ifname || !iface.confname)
155 goto out;
156
157 ret = wpa_supplicant_add_iface(wpa_global, &iface, 0) ? 0 : -1;
158 wpas_ucode_update_interfaces();
159
160 out:
161 return ucv_int64_new(ret);
162 }
163
164 static uc_value_t *
165 uc_wpas_remove_iface(uc_vm_t *vm, size_t nargs)
166 {
167 struct wpa_supplicant *wpa_s = NULL;
168 uc_value_t *ifname_arg = uc_fn_arg(0);
169 const char *ifname = ucv_string_get(ifname_arg);
170 int ret = -1;
171
172 if (!ifname)
173 goto out;
174
175 for (wpa_s = wpa_global->ifaces; wpa_s; wpa_s = wpa_s->next)
176 if (!strcmp(wpa_s->ifname, ifname))
177 break;
178
179 if (!wpa_s)
180 goto out;
181
182 ret = wpa_supplicant_remove_iface(wpa_global, wpa_s, 0);
183 wpas_ucode_update_interfaces();
184
185 out:
186 return ucv_int64_new(ret);
187 }
188
189 static uc_value_t *
190 uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
191 {
192 struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
193 struct wpa_bss *bss;
194 uc_value_t *ret, *val;
195
196 if (!wpa_s)
197 return NULL;
198
199 ret = ucv_object_new(vm);
200
201 val = ucv_string_new(wpa_supplicant_state_txt(wpa_s->wpa_state));
202 ucv_object_add(ret, "state", ucv_get(val));
203
204 bss = wpa_s->current_bss;
205 if (bss) {
206 int sec_chan = 0;
207 const u8 *ie;
208
209 ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
210 if (ie && ie[1] >= 2) {
211 const struct ieee80211_ht_operation *ht_oper;
212
213 ht_oper = (const void *) (ie + 2);
214 if (ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
215 sec_chan = 1;
216 else if (ht_oper->ht_param &
217 HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
218 sec_chan = -1;
219 }
220
221 ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
222 ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
223 }
224
225 return ret;
226 }
227
228 int wpas_ucode_init(struct wpa_global *gl)
229 {
230 static const uc_function_list_t global_fns[] = {
231 { "printf", uc_wpa_printf },
232 { "getpid", uc_wpa_getpid },
233 { "add_iface", uc_wpas_add_iface },
234 { "remove_iface", uc_wpas_remove_iface },
235 };
236 static const uc_function_list_t iface_fns[] = {
237 { "status", uc_wpas_iface_status },
238 };
239 uc_value_t *data, *proto;
240
241 wpa_global = gl;
242 vm = wpa_ucode_create_vm();
243
244 global_type = uc_type_declare(vm, "wpas.global", global_fns, NULL);
245 iface_type = uc_type_declare(vm, "wpas.iface", iface_fns, NULL);
246
247 iface_registry = ucv_array_new(vm);
248 uc_vm_registry_set(vm, "hostap.iface_registry", iface_registry);
249
250 global = wpa_ucode_global_init("wpas", global_type);
251
252 if (wpa_ucode_run(HOSTAPD_UC_PATH "wpa_supplicant.uc"))
253 goto free_vm;
254
255 ucv_gc(vm);
256 return 0;
257
258 free_vm:
259 wpa_ucode_free_vm();
260 return -1;
261 }
262
263 void wpas_ucode_free(void)
264 {
265 if (wpa_ucode_call_prepare("shutdown") == 0)
266 ucv_put(wpa_ucode_call(0));
267 wpa_ucode_free_vm();
268 }