• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
2  *
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions
5  * are met:
6  *  * Redistributions of source code must retain the above copyright
7  *    notice, this list of conditions and the following disclaimer.
8  *  * Redistributions in binary form must reproduce the above copyright
9  *    notice, this list of conditions and the following disclaimer in
10  *    the documentation and/or other materials provided with the
11  *    distribution.
12  *  * Neither the name of The Linux Foundation nor the names of its
13  *    contributors may be used to endorse or promote products derived
14  *    from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28  * Changes from Qualcomm Innovation Center are provided under the following license:
29 
30  * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
31  * SPDX-License-Identifier: BSD-3-Clause-Clear
32  */
33 
34 #include "sync.h"
35 
36 #include <utils/Log.h>
37 
38 #include <hardware_legacy/wifi_hal.h>
39 #include "common.h"
40 #include "cpp_bindings.h"
41 #include "vendor_definitions.h"
42 #include <netlink/genl/genl.h>
43 #include <string.h>
44 #include <net/if.h>
45 #include "tcp_params_update.h"
46 
TCPParamCommand(wifi_handle handle,int id,u32 vendor_id,u32 subcmd)47 TCPParamCommand::TCPParamCommand(wifi_handle handle, int id,
48                     u32 vendor_id, u32 subcmd)
49     : WifiVendorCommand(handle, id, vendor_id, subcmd)
50 {
51     if (registerVendorHandler(vendor_id, subcmd)) {
52         /* Error case should not happen print log */
53         ALOGE("%s: Unable to register Vendor Handler Vendor Id=0x%x subcmd=%u",
54                         __FUNCTION__, vendor_id, subcmd);
55     }
56     memset(def_tcp_limit_output_bytes, 0, SIZE_TCP_PARAM);
57     memset(def_tcp_adv_win_scale, 0, SIZE_TCP_PARAM);
58     def_tcp_limit_output_bytes_valid = false;
59     def_tcp_adv_win_scale_valid = false;
60 }
61 
~TCPParamCommand()62 TCPParamCommand::~TCPParamCommand()
63 {
64     unregisterVendorHandler(OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT);
65 }
66 
instance(wifi_handle handle,wifi_request_id id)67 TCPParamCommand *TCPParamCommand::instance(wifi_handle handle, wifi_request_id id)
68 {
69     TCPParamCommand* mTCPParamCommandInstance;
70 
71     mTCPParamCommandInstance = new TCPParamCommand(handle, id, OUI_QCA,
72                         QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT);
73     return mTCPParamCommandInstance;
74 }
75 
76 /* This function will be the main handler for incoming event.
77  * Call the appropriate callback handler after parsing the vendor data.
78  */
handleEvent(WifiEvent & event)79 int TCPParamCommand::handleEvent(WifiEvent &event)
80 {
81     wifi_error ret = WIFI_ERROR_UNKNOWN;
82     WifiVendorCommand::handleEvent(event);
83 
84     u8 tpDirection, tpLevel;
85     u32 tcpLimitOutputBytes;
86     u8 tcpLimitOutputBytesFlag = 0;
87     s8 tcpAdvWinScale;
88     u8 tcpAdvWinScaleFlag = 0;
89     u32 tcpDelackSeg;
90     u8 tcpDelackSegFlag = 0;
91     char value_to_str[100];
92     int ret_val = 0;
93 
94     /* Parse the vendordata and get the attribute */
95     switch(mSubcmd) {
96     case QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT:
97     {
98         struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_MAX + 1];
99 
100         nla_parse(tb, QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_MAX,
101                     (struct nlattr *)mVendorData, mDataLen, NULL);
102 
103         if (!tb[QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_DIRECTION] ||
104             !tb[QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_THROUGHPUT_LEVEL]) {
105             ALOGE("Invalid event, didn't receive mandatory attributes");
106             return WIFI_ERROR_INVALID_ARGS;
107         }
108         tpDirection = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_DIRECTION]);
109         tpLevel = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_THROUGHPUT_LEVEL]);
110 
111         if (tb[QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_LIMIT_OUTPUT_BYTES]) {
112             tcpLimitOutputBytes = nla_get_u32(tb[
113             QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_LIMIT_OUTPUT_BYTES]);
114             tcpLimitOutputBytesFlag = 1;
115         }
116 
117         if (tb[QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_ADV_WIN_SCALE]) {
118             tcpAdvWinScale = *(s8 *)nla_data(tb[
119             QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_ADV_WIN_SCALE]);
120             tcpAdvWinScaleFlag = 1;
121         }
122 
123         if (tb[QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_DELACK_SEG]) {
124             tcpDelackSeg = nla_get_u32(tb[
125             QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_DELACK_SEG]);
126             tcpDelackSegFlag = 1;
127         }
128         if (tpDirection == TP_CHANGE_RX) {
129             switch(tpLevel) {
130             case QCA_WLAN_THROUGHPUT_LEVEL_LOW:
131             {
132                 if (def_tcp_adv_win_scale_valid)
133                     wlan_service_set_tcp_adv_win_scale(def_tcp_adv_win_scale);
134                 wlan_service_set_tcp_use_userconfig("0");
135             }
136             break;
137             case QCA_WLAN_THROUGHPUT_LEVEL_MEDIUM:
138             case QCA_WLAN_THROUGHPUT_LEVEL_HIGH:
139             {
140                 if (tcpAdvWinScaleFlag) {
141                     ret_val = snprintf(value_to_str, sizeof(value_to_str), "%d",
142                                             tcpAdvWinScale);
143                     if (ret_val < 0 || ret_val >= (int)sizeof(value_to_str)) {
144                         ALOGE("Error in converting value to string: %d", ret_val);
145                         ret = WIFI_ERROR_UNKNOWN;
146                         goto cleanup;
147                     }
148                     wlan_service_set_tcp_adv_win_scale(value_to_str);
149                 }
150                 if (tcpDelackSegFlag && wlan_service_set_tcp_use_userconfig("1") == 0) {
151                     ret_val = snprintf(value_to_str, sizeof(value_to_str), "%d",
152                                             tcpDelackSeg);
153                     if (ret_val < 0 || ret_val >= (int)sizeof(value_to_str)) {
154                         ALOGE("Error in converting value to string: %d", ret_val);
155                         ret = WIFI_ERROR_UNKNOWN;
156                         goto cleanup;
157                     }
158                     wlan_service_set_tcp_delack_seg(value_to_str);
159                  }
160             }
161             break;
162             default:
163             {
164                 /* Error case should not happen print log */
165                 ALOGE("%s: Invalid throughput level value", __FUNCTION__);
166                 return WIFI_ERROR_INVALID_ARGS;
167             }
168             }
169         } else if (tpDirection == TP_CHANGE_TX) {
170             switch(tpLevel) {
171             case QCA_WLAN_THROUGHPUT_LEVEL_LOW:
172             {
173                 if (def_tcp_limit_output_bytes_valid)
174                     wlan_service_set_tcp_limit_output_bytes(
175                                     def_tcp_limit_output_bytes);
176             }
177             break;
178             case QCA_WLAN_THROUGHPUT_LEVEL_MEDIUM:
179             case QCA_WLAN_THROUGHPUT_LEVEL_HIGH:
180             {
181                 if (tcpLimitOutputBytesFlag) {
182                     ret_val = snprintf(value_to_str, sizeof(value_to_str), "%d",
183                                             tcpLimitOutputBytes);
184                     if (ret_val < 0 || ret_val >= (int)sizeof(value_to_str)) {
185                         ALOGE("Error in converting value to string: %d", ret_val);
186                         ret = WIFI_ERROR_UNKNOWN;
187                         goto cleanup;
188                     }
189                     wlan_service_set_tcp_limit_output_bytes(value_to_str);
190                 }
191             }
192             break;
193             default:
194             {
195                 /* Error case should not happen print log */
196                 ALOGE("%s: Invalid throughput level value", __FUNCTION__);
197                 return WIFI_ERROR_INVALID_ARGS;
198             }
199             }
200         } else {
201             /* Error case should not happen print log */
202             ALOGE("%s: Invalid throughput change direction", __FUNCTION__);
203             return ret;
204         }
205         ret = WIFI_SUCCESS;
206     }
207     break;
208     default:
209         /* Error case should not happen print log */
210         ALOGE("%s: Wrong subcmd received %d", __FUNCTION__, mSubcmd);
211         break;
212     }
213 
214     return ret;
215 
216 cleanup:
217     return ret;
218 }
219 
wifi_init_tcp_param_change_event_handler(wifi_interface_handle iface)220 wifi_error wifi_init_tcp_param_change_event_handler(wifi_interface_handle iface)
221 {
222     wifi_handle wifiHandle = getWifiHandle(iface);
223     TCPParamCommand *tcpParamCommand;
224 
225     if (wifiHandle == NULL) {
226         ALOGE("%s: Interface Handle is invalid", __func__);
227         return WIFI_ERROR_UNKNOWN;
228     }
229 
230     hal_info *info = getHalInfo(wifiHandle);
231     if (!info)
232         return WIFI_ERROR_UNKNOWN;
233 
234     tcpParamCommand = TCPParamCommand::instance(wifiHandle, 0);
235     if (tcpParamCommand == NULL) {
236         ALOGE("%s: Error TcpParamCommand NULL", __FUNCTION__);
237         return WIFI_ERROR_OUT_OF_MEMORY;
238     }
239 
240     info->tcp_param_handler = (tcp_param_cmd_handler *)malloc(sizeof(tcp_param_cmd_handler));
241     if (info->tcp_param_handler == NULL) {
242         ALOGE("%s: Allocation of tcp handler failed",__FUNCTION__);
243         delete tcpParamCommand;
244         return WIFI_ERROR_OUT_OF_MEMORY;
245     }
246     info->tcp_param_handler->tcpParamCommand = tcpParamCommand;
247 
248     if (wlan_service_read_sys_param("/proc/sys/net/ipv4/tcp_limit_output_bytes",
249                     tcpParamCommand->def_tcp_limit_output_bytes,
250                     SIZE_TCP_PARAM) == 0) {
251         tcpParamCommand->def_tcp_limit_output_bytes_valid = true;
252     }
253 
254     if (wlan_service_read_sys_param("/proc/sys/net/ipv4/tcp_adv_win_scale",
255                     tcpParamCommand->def_tcp_adv_win_scale,
256                     SIZE_TCP_PARAM) == 0) {
257         tcpParamCommand->def_tcp_adv_win_scale_valid = true;
258     }
259 
260     return WIFI_SUCCESS;
261 }
262 
cleanupTCPParamCommand(hal_info * info)263 void cleanupTCPParamCommand(hal_info *info) {
264 
265     TCPParamCommand *tcpParamCommand;
266 
267     if (info == NULL || info->tcp_param_handler == NULL)
268         return;
269 
270     tcpParamCommand = info->tcp_param_handler->tcpParamCommand;
271 
272     if (tcpParamCommand) {
273         if (tcpParamCommand->def_tcp_limit_output_bytes_valid)
274             wlan_service_update_sys_param("/proc/sys/net/ipv4/tcp_limit_output_bytes",
275                               tcpParamCommand->def_tcp_limit_output_bytes);
276         wlan_service_update_sys_param("/proc/sys/net/ipv4/tcp_use_userconfig", "0");
277         if (tcpParamCommand->def_tcp_adv_win_scale_valid)
278             wlan_service_update_sys_param("/proc/sys/net/ipv4/tcp_adv_win_scale",
279                               tcpParamCommand->def_tcp_adv_win_scale);
280         delete tcpParamCommand;
281     }
282 
283     free(info->tcp_param_handler);
284 
285     return;
286 }
287 
288 /**
289  * wlan_service_update_sys_param()
290  * @path: path on the file system to be modified
291  * @str:  value to be written to the path
292  *
293  * Generic function to update a system parameter
294  * Return: WIFI_SUCCESS code if read is successful
295  *         Eror code if read is failure
296  */
wlan_service_update_sys_param(const char * path,const char * str)297 wifi_error wlan_service_update_sys_param(const char *path, const char *str)
298 {
299     int ret;
300     FILE *fp;
301     fp = fopen(path, "w");
302 
303     if (fp == NULL) {
304         ALOGE("%s: unable to open %s", __FUNCTION__, path);
305         return WIFI_ERROR_UNKNOWN;
306     }
307 
308     ALOGD("%s: %s %s", __FUNCTION__,  path, str);
309 
310     ret = fputs(str, fp);
311     fclose(fp);
312 
313     if (ret < 0) {
314         ALOGE("%s: failed to write %s to %s with err %d", __FUNCTION__, str, path, ret);
315         return mapKernelErrortoWifiHalError(ret);
316     }
317 
318     return WIFI_SUCCESS;
319 }
320 
321 /**
322  * wlan_service_read_sys_param()
323  * @path: path on the file system to be read
324  * @str:  value read from the path
325  *
326  * Generic function to read a system parameter
327  * Return: WIFI_SUCCESS code if read is successful
328  *         Eror code if read is failure
329  */
wlan_service_read_sys_param(const char * path,char * str,size_t max_size)330 wifi_error wlan_service_read_sys_param(const char *path, char *str, size_t max_size)
331 {
332     size_t ret_len;
333     FILE *fp = fopen(path, "r");
334 
335     if (fp == NULL) {
336         ALOGE("%s: unable to open %s", __FUNCTION__, path);
337         return WIFI_ERROR_UNKNOWN;
338     }
339 
340     ret_len = fread(str, 1, max_size, fp);
341     fclose(fp);
342 
343     if (ret_len == 0 || ret_len == max_size) {
344         ALOGE("Faild to read %s, ret_len = %zu", path, ret_len);
345         return WIFI_ERROR_UNKNOWN;
346     }
347 
348     ALOGD("%s: %s %s", __FUNCTION__,  path, str);
349     return WIFI_SUCCESS;
350 }
351 
wlan_service_set_tcp_adv_win_scale(char * str)352 int TCPParamCommand::wlan_service_set_tcp_adv_win_scale(char *str)
353 {
354     return wlan_service_update_sys_param(
355         "/proc/sys/net/ipv4/tcp_adv_win_scale", str);
356 }
357 
wlan_service_set_tcp_use_userconfig(const char * str)358 int TCPParamCommand::wlan_service_set_tcp_use_userconfig(const char *str)
359 {
360     return wlan_service_update_sys_param(
361         "/proc/sys/net/ipv4/tcp_use_userconfig", str);
362 }
363 
wlan_service_set_tcp_delack_seg(char * str)364 int TCPParamCommand::wlan_service_set_tcp_delack_seg(char *str)
365 {
366     return wlan_service_update_sys_param(
367         "/proc/sys/net/ipv4/tcp_delack_seg", str);
368 }
369 
wlan_service_set_tcp_limit_output_bytes(char * str)370 int TCPParamCommand::wlan_service_set_tcp_limit_output_bytes(char *str)
371 {
372     return wlan_service_update_sys_param (
373         "/proc/sys/net/ipv4/tcp_limit_output_bytes", str);
374 }
375