1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Portions copyright (C) 2017 Broadcom Limited
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 #include <stdint.h>
20 #include <fcntl.h>
21 #include <sys/socket.h>
22 #include <netlink/genl/genl.h>
23 #include <netlink/genl/family.h>
24 #include <netlink/genl/ctrl.h>
25 #include <linux/rtnetlink.h>
26 #include <netpacket/packet.h>
27 #include <linux/filter.h>
28 #include <linux/errqueue.h>
29
30 #include <linux/pkt_sched.h>
31 #include <netlink/object-api.h>
32 #include <netlink/netlink.h>
33 #include <netlink/socket.h>
34 #include <netlink/handlers.h>
35
36 #include "wifi_hal.h"
37 #include "common.h"
38 #include "cpp_bindings.h"
39
40 /* test mode flag for halutil only */
41 bool halutil_mode = false;
getIfaceInfo(wifi_interface_handle handle)42 interface_info *getIfaceInfo(wifi_interface_handle handle)
43 {
44 return (interface_info *)handle;
45 }
46
getWifiHandle(wifi_interface_handle handle)47 wifi_handle getWifiHandle(wifi_interface_handle handle)
48 {
49 return getIfaceInfo(handle)->handle;
50 }
51
getHalInfo(wifi_handle handle)52 hal_info *getHalInfo(wifi_handle handle)
53 {
54 return (hal_info *)handle;
55 }
56
getHalInfo(wifi_interface_handle handle)57 hal_info *getHalInfo(wifi_interface_handle handle)
58 {
59 return getHalInfo(getWifiHandle(handle));
60 }
61
getWifiHandle(hal_info * info)62 wifi_handle getWifiHandle(hal_info *info)
63 {
64 return (wifi_handle)info;
65 }
66
getIfaceHandle(interface_info * info)67 wifi_interface_handle getIfaceHandle(interface_info *info)
68 {
69 return (wifi_interface_handle)info;
70 }
71
wifi_register_handler(wifi_handle handle,int cmd,nl_recvmsg_msg_cb_t func,void * arg)72 wifi_error wifi_register_handler(wifi_handle handle, int cmd, nl_recvmsg_msg_cb_t func, void *arg)
73 {
74 hal_info *info = (hal_info *)handle;
75
76 /* TODO: check for multiple handlers? */
77 pthread_mutex_lock(&info->cb_lock);
78
79 wifi_error result = WIFI_ERROR_OUT_OF_MEMORY;
80
81 if (info->num_event_cb < info->alloc_event_cb) {
82 info->event_cb[info->num_event_cb].nl_cmd = cmd;
83 info->event_cb[info->num_event_cb].vendor_id = 0;
84 info->event_cb[info->num_event_cb].vendor_subcmd = 0;
85 info->event_cb[info->num_event_cb].cb_func = func;
86 info->event_cb[info->num_event_cb].cb_arg = arg;
87 ALOGV("Successfully added event handler %p:%p for command %d at %d",
88 arg, func, cmd, info->num_event_cb);
89 info->num_event_cb++;
90 result = WIFI_SUCCESS;
91 }
92
93 pthread_mutex_unlock(&info->cb_lock);
94 return result;
95 }
96
wifi_register_vendor_handler(wifi_handle handle,uint32_t id,int subcmd,nl_recvmsg_msg_cb_t func,void * arg)97 wifi_error wifi_register_vendor_handler(wifi_handle handle,
98 uint32_t id, int subcmd, nl_recvmsg_msg_cb_t func, void *arg)
99 {
100 hal_info *info = (hal_info *)handle;
101
102 /* TODO: check for multiple handlers? */
103 pthread_mutex_lock(&info->cb_lock);
104
105 wifi_error result = WIFI_ERROR_OUT_OF_MEMORY;
106
107 if (info->num_event_cb < info->alloc_event_cb) {
108 /* To avoid an unwanted duplication of the record, find first.
109 * Update it if the same record is already exist.
110 * KEY => [nl_cmd, vendor_id, vendor_subcmd]
111 */
112 int i = 0;
113 bool is_update = false;
114 for (i = 0; i < info->num_event_cb; i++) {
115 if ((info->event_cb[i].nl_cmd == NL80211_CMD_VENDOR) &&
116 (info->event_cb[i].vendor_id == id) &&
117 (info->event_cb[i].vendor_subcmd == subcmd)) {
118 is_update = true;
119 break;
120 }
121 }
122
123 if (is_update) {
124 info->event_cb[i].cb_func = func;
125 info->event_cb[i].cb_arg = arg;
126 } else {
127 info->event_cb[info->num_event_cb].nl_cmd = NL80211_CMD_VENDOR;
128 info->event_cb[info->num_event_cb].vendor_id = id;
129 info->event_cb[info->num_event_cb].vendor_subcmd = subcmd;
130 info->event_cb[info->num_event_cb].cb_func = func;
131 info->event_cb[info->num_event_cb].cb_arg = arg;
132 info->num_event_cb++;
133 }
134 ALOGI("%s ""event handler %p:%p for vendor 0x%0x and subcmd 0x%0x at %d",
135 is_update ? "Updated" : "Added", arg, func, id, subcmd, info->num_event_cb);
136 result = WIFI_SUCCESS;
137 }
138
139 pthread_mutex_unlock(&info->cb_lock);
140 return result;
141 }
142
wifi_unregister_handler(wifi_handle handle,int cmd)143 void wifi_unregister_handler(wifi_handle handle, int cmd)
144 {
145 hal_info *info = (hal_info *)handle;
146
147 if (cmd == NL80211_CMD_VENDOR) {
148 ALOGE("Must use wifi_unregister_vendor_handler to remove vendor handlers");
149 return;
150 }
151
152 pthread_mutex_lock(&info->cb_lock);
153
154 for (int i = 0; i < info->num_event_cb; i++) {
155 if (info->event_cb[i].nl_cmd == cmd) {
156 ALOGV("Successfully removed event handler %p:%p for cmd = 0x%0x from %d",
157 info->event_cb[i].cb_arg, info->event_cb[i].cb_func, cmd, i);
158
159 memmove(&info->event_cb[i], &info->event_cb[i+1],
160 (info->num_event_cb - i - 1) * sizeof(cb_info));
161 info->num_event_cb--;
162 break;
163 }
164 }
165
166 pthread_mutex_unlock(&info->cb_lock);
167 }
168
wifi_unregister_vendor_handler(wifi_handle handle,uint32_t id,int subcmd)169 void wifi_unregister_vendor_handler(wifi_handle handle, uint32_t id, int subcmd)
170 {
171 hal_info *info = (hal_info *)handle;
172
173 pthread_mutex_lock(&info->cb_lock);
174
175 for (int i = 0; i < info->num_event_cb; i++) {
176
177 if (info->event_cb[i].nl_cmd == NL80211_CMD_VENDOR
178 && info->event_cb[i].vendor_id == id
179 && info->event_cb[i].vendor_subcmd == subcmd) {
180 ALOGI("Successfully removed event handler %p:%p for vendor 0x%0x, subcmd 0x%0x from %d",
181 info->event_cb[i].cb_arg, info->event_cb[i].cb_func, id, subcmd, i);
182 memmove(&info->event_cb[i], &info->event_cb[i+1],
183 (info->num_event_cb - i - 1) * sizeof(cb_info));
184 info->num_event_cb--;
185 break;
186 }
187 }
188
189 pthread_mutex_unlock(&info->cb_lock);
190 }
191
192
wifi_register_cmd(wifi_handle handle,int id,WifiCommand * cmd)193 wifi_error wifi_register_cmd(wifi_handle handle, int id, WifiCommand *cmd)
194 {
195 hal_info *info = (hal_info *)handle;
196
197 ALOGV("registering command %d", id);
198
199 wifi_error result = WIFI_ERROR_OUT_OF_MEMORY;
200
201 if (info->num_cmd < info->alloc_cmd) {
202 info->cmd[info->num_cmd].id = id;
203 info->cmd[info->num_cmd].cmd = cmd;
204 ALOGV("Successfully added command %d: %p at %d", id, cmd, info->num_cmd);
205 info->num_cmd++;
206 result = WIFI_SUCCESS;
207 } else {
208 ALOGE("Failed to add command %d: %p at %d, reached max limit %d",
209 id, cmd, info->num_cmd, info->alloc_cmd);
210 }
211
212 return result;
213 }
214
wifi_unregister_cmd(wifi_handle handle,int id)215 WifiCommand *wifi_unregister_cmd(wifi_handle handle, int id)
216 {
217 hal_info *info = (hal_info *)handle;
218
219 ALOGV("un-registering command %d", id);
220
221 WifiCommand *cmd = NULL;
222
223 for (int i = 0; i < info->num_cmd; i++) {
224 if (info->cmd[i].id == id) {
225 cmd = info->cmd[i].cmd;
226 memmove(&info->cmd[i], &info->cmd[i+1], (info->num_cmd - i - 1) * sizeof(cmd_info));
227 info->num_cmd--;
228 ALOGV("Successfully removed command %d: %p from %d", id, cmd, i);
229 break;
230 }
231 }
232
233 if (!cmd) {
234 ALOGI("Failed to remove command %d: %p", id, cmd);
235 }
236
237 return cmd;
238 }
239
wifi_get_cmd(wifi_handle handle,int id)240 WifiCommand *wifi_get_cmd(wifi_handle handle, int id)
241 {
242 hal_info *info = (hal_info *)handle;
243
244 WifiCommand *cmd = NULL;
245
246 for (int i = 0; i < info->num_cmd; i++) {
247 if (info->cmd[i].id == id) {
248 cmd = info->cmd[i].cmd;
249 break;
250 }
251 }
252
253 return cmd;
254 }
255
wifi_unregister_cmd(wifi_handle handle,WifiCommand * cmd)256 void wifi_unregister_cmd(wifi_handle handle, WifiCommand *cmd)
257 {
258 hal_info *info = (hal_info *)handle;
259
260 for (int i = 0; i < info->num_cmd; i++) {
261 if (info->cmd[i].cmd == cmd) {
262 int id = info->cmd[i].id;
263 memmove(&info->cmd[i], &info->cmd[i+1], (info->num_cmd - i - 1) * sizeof(cmd_info));
264 info->num_cmd--;
265 ALOGV("Successfully removed command %d: %p from %d", id, cmd, i);
266 break;
267 }
268 }
269 }
270
wifi_cancel_cmd(wifi_request_id id,wifi_interface_handle iface)271 wifi_error wifi_cancel_cmd(wifi_request_id id, wifi_interface_handle iface)
272 {
273 wifi_handle handle = getWifiHandle(iface);
274
275 WifiCommand *cmd = wifi_unregister_cmd(handle, id);
276 ALOGV("Cancel WifiCommand = %p", cmd);
277 if (cmd) {
278 cmd->cancel();
279 cmd->releaseRef();
280 return WIFI_SUCCESS;
281 }
282
283 return WIFI_ERROR_INVALID_ARGS;
284 }
285
wifi_get_cancel_cmd(wifi_request_id id,wifi_interface_handle iface)286 wifi_error wifi_get_cancel_cmd(wifi_request_id id, wifi_interface_handle iface)
287 {
288 wifi_handle handle = getWifiHandle(iface);
289 WifiCommand *cmd = wifi_get_cmd(handle, id);
290 ALOGV("Get Cancel WifiCommand = %p", cmd);
291 if (cmd) {
292 cmd->cancel();
293 cmd->releaseRef();
294 return WIFI_SUCCESS;
295 }
296
297 return WIFI_ERROR_INVALID_ARGS;
298 }
set_hautil_mode(bool util_mode)299 void set_hautil_mode(bool util_mode)
300 {
301 halutil_mode = util_mode;
302 }
get_halutil_mode()303 bool get_halutil_mode()
304 {
305 return halutil_mode;
306 }
307