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 
getIfaceInfo(wifi_interface_handle handle)40 interface_info *getIfaceInfo(wifi_interface_handle handle)
41 {
42     return (interface_info *)handle;
43 }
44 
getWifiHandle(wifi_interface_handle handle)45 wifi_handle getWifiHandle(wifi_interface_handle handle)
46 {
47     return getIfaceInfo(handle)->handle;
48 }
49 
getHalInfo(wifi_handle handle)50 hal_info *getHalInfo(wifi_handle handle)
51 {
52     return (hal_info *)handle;
53 }
54 
getHalInfo(wifi_interface_handle handle)55 hal_info *getHalInfo(wifi_interface_handle handle)
56 {
57     return getHalInfo(getWifiHandle(handle));
58 }
59 
getWifiHandle(hal_info * info)60 wifi_handle getWifiHandle(hal_info *info)
61 {
62     return (wifi_handle)info;
63 }
64 
getIfaceHandle(interface_info * info)65 wifi_interface_handle getIfaceHandle(interface_info *info)
66 {
67     return (wifi_interface_handle)info;
68 }
69 
wifi_register_handler(wifi_handle handle,int cmd,nl_recvmsg_msg_cb_t func,void * arg)70 wifi_error wifi_register_handler(wifi_handle handle, int cmd, nl_recvmsg_msg_cb_t func, void *arg)
71 {
72     hal_info *info = (hal_info *)handle;
73 
74     /* TODO: check for multiple handlers? */
75     pthread_mutex_lock(&info->cb_lock);
76 
77     wifi_error result = WIFI_ERROR_OUT_OF_MEMORY;
78 
79     if (info->num_event_cb < info->alloc_event_cb) {
80         info->event_cb[info->num_event_cb].nl_cmd  = cmd;
81         info->event_cb[info->num_event_cb].vendor_id  = 0;
82         info->event_cb[info->num_event_cb].vendor_subcmd  = 0;
83         info->event_cb[info->num_event_cb].cb_func = func;
84         info->event_cb[info->num_event_cb].cb_arg  = arg;
85         ALOGV("Successfully added event handler %p:%p for command %d at %d",
86                 arg, func, cmd, info->num_event_cb);
87         info->num_event_cb++;
88         result = WIFI_SUCCESS;
89     }
90 
91     pthread_mutex_unlock(&info->cb_lock);
92     return result;
93 }
94 
wifi_register_vendor_handler(wifi_handle handle,uint32_t id,int subcmd,nl_recvmsg_msg_cb_t func,void * arg)95 wifi_error wifi_register_vendor_handler(wifi_handle handle,
96         uint32_t id, int subcmd, nl_recvmsg_msg_cb_t func, void *arg)
97 {
98     hal_info *info = (hal_info *)handle;
99 
100     /* TODO: check for multiple handlers? */
101     pthread_mutex_lock(&info->cb_lock);
102 
103     wifi_error result = WIFI_ERROR_OUT_OF_MEMORY;
104 
105     if (info->num_event_cb < info->alloc_event_cb) {
106         info->event_cb[info->num_event_cb].nl_cmd  = NL80211_CMD_VENDOR;
107         info->event_cb[info->num_event_cb].vendor_id  = id;
108         info->event_cb[info->num_event_cb].vendor_subcmd  = subcmd;
109         info->event_cb[info->num_event_cb].cb_func = func;
110         info->event_cb[info->num_event_cb].cb_arg  = arg;
111         ALOGV("Added event handler %p:%p for vendor 0x%0x and subcmd 0x%0x at %d",
112                 arg, func, id, subcmd, info->num_event_cb);
113         info->num_event_cb++;
114         result = WIFI_SUCCESS;
115     }
116 
117     pthread_mutex_unlock(&info->cb_lock);
118     return result;
119 }
120 
wifi_unregister_handler(wifi_handle handle,int cmd)121 void wifi_unregister_handler(wifi_handle handle, int cmd)
122 {
123     hal_info *info = (hal_info *)handle;
124 
125     if (cmd == NL80211_CMD_VENDOR) {
126         ALOGE("Must use wifi_unregister_vendor_handler to remove vendor handlers");
127         return;
128     }
129 
130     pthread_mutex_lock(&info->cb_lock);
131 
132     for (int i = 0; i < info->num_event_cb; i++) {
133         if (info->event_cb[i].nl_cmd == cmd) {
134             ALOGV("Successfully removed event handler %p:%p for cmd = 0x%0x from %d",
135                     info->event_cb[i].cb_arg, info->event_cb[i].cb_func, cmd, i);
136 
137             memmove(&info->event_cb[i], &info->event_cb[i+1],
138                 (info->num_event_cb - i - 1) * sizeof(cb_info));
139             info->num_event_cb--;
140             break;
141         }
142     }
143 
144     pthread_mutex_unlock(&info->cb_lock);
145 }
146 
wifi_unregister_vendor_handler(wifi_handle handle,uint32_t id,int subcmd)147 void wifi_unregister_vendor_handler(wifi_handle handle, uint32_t id, int subcmd)
148 {
149     hal_info *info = (hal_info *)handle;
150 
151     pthread_mutex_lock(&info->cb_lock);
152 
153     for (int i = 0; i < info->num_event_cb; i++) {
154 
155         if (info->event_cb[i].nl_cmd == NL80211_CMD_VENDOR
156                 && info->event_cb[i].vendor_id == id
157                 && info->event_cb[i].vendor_subcmd == subcmd) {
158             ALOGV("Successfully removed event handler %p:%p for vendor 0x%0x, subcmd 0x%0x from %d",
159                     info->event_cb[i].cb_arg, info->event_cb[i].cb_func, id, subcmd, i);
160             memmove(&info->event_cb[i], &info->event_cb[i+1],
161                 (info->num_event_cb - i - 1) * sizeof(cb_info));
162             info->num_event_cb--;
163             break;
164         }
165     }
166 
167     pthread_mutex_unlock(&info->cb_lock);
168 }
169 
170 
wifi_register_cmd(wifi_handle handle,int id,WifiCommand * cmd)171 wifi_error wifi_register_cmd(wifi_handle handle, int id, WifiCommand *cmd)
172 {
173     hal_info *info = (hal_info *)handle;
174 
175     ALOGV("registering command %d", id);
176 
177     wifi_error result = WIFI_ERROR_OUT_OF_MEMORY;
178 
179     if (info->num_cmd < info->alloc_cmd) {
180         info->cmd[info->num_cmd].id   = id;
181         info->cmd[info->num_cmd].cmd  = cmd;
182         ALOGV("Successfully added command %d: %p at %d", id, cmd, info->num_cmd);
183         info->num_cmd++;
184         result = WIFI_SUCCESS;
185     } else {
186         ALOGE("Failed to add command %d: %p at %d, reached max limit %d",
187                 id, cmd, info->num_cmd, info->alloc_cmd);
188     }
189 
190     return result;
191 }
192 
wifi_unregister_cmd(wifi_handle handle,int id)193 WifiCommand *wifi_unregister_cmd(wifi_handle handle, int id)
194 {
195     hal_info *info = (hal_info *)handle;
196 
197     ALOGV("un-registering command %d", id);
198 
199     WifiCommand *cmd = NULL;
200 
201     for (int i = 0; i < info->num_cmd; i++) {
202         if (info->cmd[i].id == id) {
203             cmd = info->cmd[i].cmd;
204             memmove(&info->cmd[i], &info->cmd[i+1], (info->num_cmd - i - 1) * sizeof(cmd_info));
205             info->num_cmd--;
206             ALOGV("Successfully removed command %d: %p from %d", id, cmd, i);
207             break;
208         }
209     }
210 
211     if (!cmd) {
212         ALOGI("Failed to remove command %d: %p", id, cmd);
213     }
214 
215     return cmd;
216 }
217 
wifi_get_cmd(wifi_handle handle,int id)218 WifiCommand *wifi_get_cmd(wifi_handle handle, int id)
219 {
220     hal_info *info = (hal_info *)handle;
221 
222     WifiCommand *cmd = NULL;
223 
224     for (int i = 0; i < info->num_cmd; i++) {
225         if (info->cmd[i].id == id) {
226             cmd = info->cmd[i].cmd;
227             break;
228         }
229     }
230 
231     return cmd;
232 }
233 
wifi_unregister_cmd(wifi_handle handle,WifiCommand * cmd)234 void wifi_unregister_cmd(wifi_handle handle, WifiCommand *cmd)
235 {
236     hal_info *info = (hal_info *)handle;
237 
238     for (int i = 0; i < info->num_cmd; i++) {
239         if (info->cmd[i].cmd == cmd) {
240             int id = info->cmd[i].id;
241             memmove(&info->cmd[i], &info->cmd[i+1], (info->num_cmd - i - 1) * sizeof(cmd_info));
242             info->num_cmd--;
243             ALOGV("Successfully removed command %d: %p from %d", id, cmd, i);
244             break;
245         }
246     }
247 }
248 
wifi_cancel_cmd(wifi_request_id id,wifi_interface_handle iface)249 wifi_error wifi_cancel_cmd(wifi_request_id id, wifi_interface_handle iface)
250 {
251     wifi_handle handle = getWifiHandle(iface);
252 
253     WifiCommand *cmd = wifi_unregister_cmd(handle, id);
254     ALOGV("Cancel WifiCommand = %p", cmd);
255     if (cmd) {
256         cmd->cancel();
257         cmd->releaseRef();
258         return WIFI_SUCCESS;
259     }
260 
261     return WIFI_ERROR_INVALID_ARGS;
262 }
263 
264