blob: 0d8e0fbe004fc19a010f610b480f3c8dce8eb6e8 [file] [log] [blame]
Christopher Wileya5915062016-06-16 16:31:01 -07001/*
2 * Copyright 2016, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "hardware_legacy/wifi.h"
18
Christopher Wileya5915062016-06-16 16:31:01 -070019#include <fcntl.h>
20#include <stdlib.h>
21#include <unistd.h>
22
Christopher Wiley86105fb2016-09-09 13:48:15 -070023#include <android-base/logging.h>
Christopher Wileya5915062016-06-16 16:31:01 -070024#include <cutils/misc.h>
25#include <cutils/properties.h>
Ajit Kumar93c106d2017-07-31 16:53:46 +053026#include <sys/syscall.h>
Christopher Wileya5915062016-06-16 16:31:01 -070027
Alex Vakulenko217f61b2016-07-27 09:54:12 -070028extern "C" int init_module(void *, unsigned long, const char *);
29extern "C" int delete_module(const char *, unsigned int);
Christopher Wileya5915062016-06-16 16:31:01 -070030
31#ifndef WIFI_DRIVER_FW_PATH_STA
Christopher Wileyaf529022016-09-09 13:39:40 -070032#define WIFI_DRIVER_FW_PATH_STA NULL
Christopher Wileya5915062016-06-16 16:31:01 -070033#endif
34#ifndef WIFI_DRIVER_FW_PATH_AP
Christopher Wileyaf529022016-09-09 13:39:40 -070035#define WIFI_DRIVER_FW_PATH_AP NULL
Christopher Wileya5915062016-06-16 16:31:01 -070036#endif
37#ifndef WIFI_DRIVER_FW_PATH_P2P
Christopher Wileyaf529022016-09-09 13:39:40 -070038#define WIFI_DRIVER_FW_PATH_P2P NULL
Christopher Wileya5915062016-06-16 16:31:01 -070039#endif
Christopher Wileya5915062016-06-16 16:31:01 -070040
41#ifndef WIFI_DRIVER_MODULE_ARG
Christopher Wileyaf529022016-09-09 13:39:40 -070042#define WIFI_DRIVER_MODULE_ARG ""
Christopher Wileya5915062016-06-16 16:31:01 -070043#endif
44
Christopher Wileyaf529022016-09-09 13:39:40 -070045static const char DRIVER_PROP_NAME[] = "wlan.driver.status";
Ahmed ElArabawy708d5002018-06-05 10:45:53 -070046static bool is_driver_loaded = false;
Christopher Wileya5915062016-06-16 16:31:01 -070047#ifdef WIFI_DRIVER_MODULE_PATH
Christopher Wileyaf529022016-09-09 13:39:40 -070048static const char DRIVER_MODULE_NAME[] = WIFI_DRIVER_MODULE_NAME;
49static const char DRIVER_MODULE_TAG[] = WIFI_DRIVER_MODULE_NAME " ";
50static const char DRIVER_MODULE_PATH[] = WIFI_DRIVER_MODULE_PATH;
51static const char DRIVER_MODULE_ARG[] = WIFI_DRIVER_MODULE_ARG;
52static const char MODULE_FILE[] = "/proc/modules";
Christopher Wileya5915062016-06-16 16:31:01 -070053#endif
54
Christopher Wileyaf529022016-09-09 13:39:40 -070055static int insmod(const char *filename, const char *args) {
Christopher Wileyaf529022016-09-09 13:39:40 -070056 int ret;
Ajit Kumar93c106d2017-07-31 16:53:46 +053057 int fd;
Christopher Wileya5915062016-06-16 16:31:01 -070058
Ajit Kumar93c106d2017-07-31 16:53:46 +053059 fd = TEMP_FAILURE_RETRY(open(filename, O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
60 if (fd < 0) {
61 PLOG(ERROR) << "Failed to open " << filename;
62 return -1;
63 }
Christopher Wileya5915062016-06-16 16:31:01 -070064
Ajit Kumar93c106d2017-07-31 16:53:46 +053065 ret = syscall(__NR_finit_module, fd, args, 0);
Christopher Wileya5915062016-06-16 16:31:01 -070066
Ajit Kumar93c106d2017-07-31 16:53:46 +053067 close(fd);
68 if (ret < 0) {
69 PLOG(ERROR) << "finit_module return: " << ret;
70 }
Christopher Wileya5915062016-06-16 16:31:01 -070071
Christopher Wileyaf529022016-09-09 13:39:40 -070072 return ret;
Christopher Wileya5915062016-06-16 16:31:01 -070073}
74
Christopher Wileyaf529022016-09-09 13:39:40 -070075static int rmmod(const char *modname) {
76 int ret = -1;
77 int maxtry = 10;
Christopher Wileya5915062016-06-16 16:31:01 -070078
Christopher Wileyaf529022016-09-09 13:39:40 -070079 while (maxtry-- > 0) {
80 ret = delete_module(modname, O_NONBLOCK | O_EXCL);
81 if (ret < 0 && errno == EAGAIN)
82 usleep(500000);
83 else
84 break;
85 }
Christopher Wileya5915062016-06-16 16:31:01 -070086
Christopher Wileyaf529022016-09-09 13:39:40 -070087 if (ret != 0)
Christopher Wiley86105fb2016-09-09 13:48:15 -070088 PLOG(DEBUG) << "Unable to unload driver module '" << modname << "'";
Christopher Wileyaf529022016-09-09 13:39:40 -070089 return ret;
Christopher Wileya5915062016-06-16 16:31:01 -070090}
91
92#ifdef WIFI_DRIVER_STATE_CTRL_PARAM
Christopher Wileyaf529022016-09-09 13:39:40 -070093int wifi_change_driver_state(const char *state) {
94 int len;
95 int fd;
96 int ret = 0;
Christopher Wileya5915062016-06-16 16:31:01 -070097
Christopher Wileyaf529022016-09-09 13:39:40 -070098 if (!state) return -1;
99 fd = TEMP_FAILURE_RETRY(open(WIFI_DRIVER_STATE_CTRL_PARAM, O_WRONLY));
100 if (fd < 0) {
Christopher Wiley86105fb2016-09-09 13:48:15 -0700101 PLOG(ERROR) << "Failed to open driver state control param";
Christopher Wileyaf529022016-09-09 13:39:40 -0700102 return -1;
103 }
104 len = strlen(state) + 1;
105 if (TEMP_FAILURE_RETRY(write(fd, state, len)) != len) {
Christopher Wiley86105fb2016-09-09 13:48:15 -0700106 PLOG(ERROR) << "Failed to write driver state control param";
Christopher Wileyaf529022016-09-09 13:39:40 -0700107 ret = -1;
108 }
109 close(fd);
110 return ret;
Christopher Wileya5915062016-06-16 16:31:01 -0700111}
112#endif
113
114int is_wifi_driver_loaded() {
Christopher Wileyaf529022016-09-09 13:39:40 -0700115 char driver_status[PROPERTY_VALUE_MAX];
Christopher Wileya5915062016-06-16 16:31:01 -0700116#ifdef WIFI_DRIVER_MODULE_PATH
Christopher Wileyaf529022016-09-09 13:39:40 -0700117 FILE *proc;
118 char line[sizeof(DRIVER_MODULE_TAG) + 10];
Christopher Wileya5915062016-06-16 16:31:01 -0700119#endif
120
Ahmed ElArabawy708d5002018-06-05 10:45:53 -0700121 if (!property_get(DRIVER_PROP_NAME, driver_status, NULL)) {
Christopher Wileyaf529022016-09-09 13:39:40 -0700122 return 0; /* driver not loaded */
123 }
Ahmed ElArabawy708d5002018-06-05 10:45:53 -0700124
125 if (!is_driver_loaded) {
126 return 0;
127 } /* driver not loaded */
128
Christopher Wileya5915062016-06-16 16:31:01 -0700129#ifdef WIFI_DRIVER_MODULE_PATH
Christopher Wileyaf529022016-09-09 13:39:40 -0700130 /*
131 * If the property says the driver is loaded, check to
132 * make sure that the property setting isn't just left
133 * over from a previous manual shutdown or a runtime
134 * crash.
135 */
136 if ((proc = fopen(MODULE_FILE, "r")) == NULL) {
Christopher Wiley86105fb2016-09-09 13:48:15 -0700137 PLOG(WARNING) << "Could not open " << MODULE_FILE;
Ahmed ElArabawy708d5002018-06-05 10:45:53 -0700138 is_driver_loaded = false;
139 if (strcmp(driver_status, "unloaded") != 0) {
140 property_set(DRIVER_PROP_NAME, "unloaded");
141 }
Christopher Wileya5915062016-06-16 16:31:01 -0700142 return 0;
Christopher Wileyaf529022016-09-09 13:39:40 -0700143 }
144 while ((fgets(line, sizeof(line), proc)) != NULL) {
145 if (strncmp(line, DRIVER_MODULE_TAG, strlen(DRIVER_MODULE_TAG)) == 0) {
146 fclose(proc);
147 return 1;
148 }
149 }
150 fclose(proc);
Ahmed ElArabawy708d5002018-06-05 10:45:53 -0700151 is_driver_loaded = false;
152 if (strcmp(driver_status, "unloaded") != 0) {
153 property_set(DRIVER_PROP_NAME, "unloaded");
154 }
Christopher Wileyaf529022016-09-09 13:39:40 -0700155 return 0;
Christopher Wileya5915062016-06-16 16:31:01 -0700156#else
Christopher Wileyaf529022016-09-09 13:39:40 -0700157 return 1;
Christopher Wileya5915062016-06-16 16:31:01 -0700158#endif
159}
160
Christopher Wileyaf529022016-09-09 13:39:40 -0700161int wifi_load_driver() {
Christopher Wileya5915062016-06-16 16:31:01 -0700162#ifdef WIFI_DRIVER_MODULE_PATH
Christopher Wileyaf529022016-09-09 13:39:40 -0700163 if (is_wifi_driver_loaded()) {
164 return 0;
165 }
Christopher Wileya5915062016-06-16 16:31:01 -0700166
Christopher Wileyaf529022016-09-09 13:39:40 -0700167 if (insmod(DRIVER_MODULE_PATH, DRIVER_MODULE_ARG) < 0) return -1;
Kumar Anand278ed482017-02-27 13:24:13 -0800168#endif
Christopher Wileya5915062016-06-16 16:31:01 -0700169
Kumar Anand278ed482017-02-27 13:24:13 -0800170#ifdef WIFI_DRIVER_STATE_CTRL_PARAM
Christopher Wileyaf529022016-09-09 13:39:40 -0700171 if (is_wifi_driver_loaded()) {
Christopher Wileya5915062016-06-16 16:31:01 -0700172 return 0;
Christopher Wileyaf529022016-09-09 13:39:40 -0700173 }
174
Abhishek Srivastavaacf68362018-04-09 18:29:49 +0530175 if (wifi_change_driver_state(WIFI_DRIVER_STATE_ON) < 0) {
176#ifdef WIFI_DRIVER_MODULE_PATH
177 PLOG(WARNING) << "Driver unloading, err='fail to change driver state'";
178 if (rmmod(DRIVER_MODULE_NAME) == 0) {
179 PLOG(DEBUG) << "Driver unloaded";
180 } else {
181 // Set driver prop to "ok", expect HL to restart Wi-Fi.
182 PLOG(DEBUG) << "Driver unload failed! set driver prop to 'ok'.";
183 property_set(DRIVER_PROP_NAME, "ok");
184 }
185#endif
186 return -1;
187 }
Christopher Wileyaf529022016-09-09 13:39:40 -0700188#endif
Ahmed ElArabawy708d5002018-06-05 10:45:53 -0700189 is_driver_loaded = true;
Christopher Wileyaf529022016-09-09 13:39:40 -0700190 return 0;
Christopher Wileya5915062016-06-16 16:31:01 -0700191}
192
Christopher Wileyaf529022016-09-09 13:39:40 -0700193int wifi_unload_driver() {
Wei Wang823a7742016-12-19 21:49:01 -0800194 if (!is_wifi_driver_loaded()) {
195 return 0;
196 }
Christopher Wileya5915062016-06-16 16:31:01 -0700197#ifdef WIFI_DRIVER_MODULE_PATH
Christopher Wileyaf529022016-09-09 13:39:40 -0700198 if (rmmod(DRIVER_MODULE_NAME) == 0) {
199 int count = 20; /* wait at most 10 seconds for completion */
200 while (count-- > 0) {
201 if (!is_wifi_driver_loaded()) break;
202 usleep(500000);
203 }
204 usleep(500000); /* allow card removal */
205 if (count) {
206 return 0;
207 }
208 return -1;
209 } else
210 return -1;
Christopher Wileya5915062016-06-16 16:31:01 -0700211#else
212#ifdef WIFI_DRIVER_STATE_CTRL_PARAM
Christopher Wileyaf529022016-09-09 13:39:40 -0700213 if (is_wifi_driver_loaded()) {
214 if (wifi_change_driver_state(WIFI_DRIVER_STATE_OFF) < 0) return -1;
215 }
Christopher Wileya5915062016-06-16 16:31:01 -0700216#endif
Ahmed ElArabawy708d5002018-06-05 10:45:53 -0700217 is_driver_loaded = false;
Christopher Wileyaf529022016-09-09 13:39:40 -0700218 property_set(DRIVER_PROP_NAME, "unloaded");
219 return 0;
Christopher Wileya5915062016-06-16 16:31:01 -0700220#endif
221}
222
Christopher Wileyaf529022016-09-09 13:39:40 -0700223const char *wifi_get_fw_path(int fw_type) {
224 switch (fw_type) {
Christopher Wileya5915062016-06-16 16:31:01 -0700225 case WIFI_GET_FW_PATH_STA:
Christopher Wileyaf529022016-09-09 13:39:40 -0700226 return WIFI_DRIVER_FW_PATH_STA;
Christopher Wileya5915062016-06-16 16:31:01 -0700227 case WIFI_GET_FW_PATH_AP:
Christopher Wileyaf529022016-09-09 13:39:40 -0700228 return WIFI_DRIVER_FW_PATH_AP;
Christopher Wileya5915062016-06-16 16:31:01 -0700229 case WIFI_GET_FW_PATH_P2P:
Christopher Wileyaf529022016-09-09 13:39:40 -0700230 return WIFI_DRIVER_FW_PATH_P2P;
231 }
232 return NULL;
Christopher Wileya5915062016-06-16 16:31:01 -0700233}
234
Christopher Wileyaf529022016-09-09 13:39:40 -0700235int wifi_change_fw_path(const char *fwpath) {
236 int len;
237 int fd;
238 int ret = 0;
Christopher Wileya5915062016-06-16 16:31:01 -0700239
Christopher Wileyaf529022016-09-09 13:39:40 -0700240 if (!fwpath) return ret;
241 fd = TEMP_FAILURE_RETRY(open(WIFI_DRIVER_FW_PATH_PARAM, O_WRONLY));
242 if (fd < 0) {
Christopher Wiley86105fb2016-09-09 13:48:15 -0700243 PLOG(ERROR) << "Failed to open wlan fw path param";
Christopher Wileyaf529022016-09-09 13:39:40 -0700244 return -1;
245 }
246 len = strlen(fwpath) + 1;
247 if (TEMP_FAILURE_RETRY(write(fd, fwpath, len)) != len) {
Christopher Wiley86105fb2016-09-09 13:48:15 -0700248 PLOG(ERROR) << "Failed to write wlan fw path param";
Christopher Wileyaf529022016-09-09 13:39:40 -0700249 ret = -1;
250 }
251 close(fd);
252 return ret;
Christopher Wileya5915062016-06-16 16:31:01 -0700253}