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