1 /*
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
19 #include <fcntl.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <time.h>
23
24 #include <android-base/logging.h>
25 #include <cutils/misc.h>
26 #include <cutils/properties.h>
27 #include <sys/syscall.h>
28
29 extern "C" int init_module(void *, unsigned long, const char *);
30 extern "C" int delete_module(const char *, unsigned int);
31
32 #ifndef WIFI_DRIVER_FW_PATH_STA
33 #define WIFI_DRIVER_FW_PATH_STA NULL
34 #endif
35 #ifndef WIFI_DRIVER_FW_PATH_AP
36 #define WIFI_DRIVER_FW_PATH_AP NULL
37 #endif
38 #ifndef WIFI_DRIVER_FW_PATH_P2P
39 #define WIFI_DRIVER_FW_PATH_P2P NULL
40 #endif
41
42 #ifndef WIFI_DRIVER_MODULE_ARG
43 #define WIFI_DRIVER_MODULE_ARG ""
44 #endif
45
46 static const char DRIVER_PROP_NAME[] = "wlan.driver.status";
47 static bool is_driver_loaded = false;
48 #ifdef WIFI_DRIVER_MODULE_PATH
49 static const char DRIVER_MODULE_NAME[] = WIFI_DRIVER_MODULE_NAME;
50 static const char DRIVER_MODULE_TAG[] = WIFI_DRIVER_MODULE_NAME " ";
51 static const char DRIVER_MODULE_PATH[] = WIFI_DRIVER_MODULE_PATH;
52 static const char DRIVER_MODULE_ARG[] = WIFI_DRIVER_MODULE_ARG;
53 static const char MODULE_FILE[] = "/proc/modules";
54 #endif
55
56 #ifdef WIFI_DRIVER_STATE_CTRL_PARAM
57 int kDriverStateAccessRetrySleepMillis = 200;
58 #endif
59
insmod(const char * filename,const char * args)60 static int insmod(const char *filename, const char *args) {
61 int ret;
62 int fd;
63
64 fd = TEMP_FAILURE_RETRY(open(filename, O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
65 if (fd < 0) {
66 PLOG(ERROR) << "Failed to open " << filename;
67 return -1;
68 }
69
70 ret = syscall(__NR_finit_module, fd, args, 0);
71
72 close(fd);
73 if (ret < 0) {
74 PLOG(ERROR) << "finit_module return: " << ret;
75 }
76
77 return ret;
78 }
79
rmmod(const char * modname)80 static int rmmod(const char *modname) {
81 int ret = -1;
82 int maxtry = 10;
83
84 while (maxtry-- > 0) {
85 ret = delete_module(modname, O_NONBLOCK | O_EXCL);
86 if (ret < 0 && errno == EAGAIN)
87 usleep(500000);
88 else
89 break;
90 }
91
92 if (ret != 0)
93 PLOG(DEBUG) << "Unable to unload driver module '" << modname << "'";
94 return ret;
95 }
96
97 #ifdef WIFI_DRIVER_STATE_CTRL_PARAM
wifi_change_driver_state(const char * state)98 int wifi_change_driver_state(const char *state) {
99 int len;
100 int fd;
101 int ret = 0;
102 struct timespec req;
103 req.tv_sec = 0;
104 req.tv_nsec = kDriverStateAccessRetrySleepMillis * 1000000L;
105 int count = 5; /* wait at most 1 second for completion. */
106
107 if (!state) return -1;
108 do {
109 if (access(WIFI_DRIVER_STATE_CTRL_PARAM, W_OK) == 0)
110 break;
111 nanosleep(&req, (struct timespec *)NULL);
112 } while (--count > 0);
113 if (count == 0) {
114 PLOG(ERROR) << "Failed to access driver state control param "
115 << strerror(errno) << ", " << errno;
116 return -1;
117 }
118 fd = TEMP_FAILURE_RETRY(open(WIFI_DRIVER_STATE_CTRL_PARAM, O_WRONLY));
119 if (fd < 0) {
120 PLOG(ERROR) << "Failed to open driver state control param";
121 return -1;
122 }
123 len = strlen(state) + 1;
124 if (TEMP_FAILURE_RETRY(write(fd, state, len)) != len) {
125 PLOG(ERROR) << "Failed to write driver state control param";
126 ret = -1;
127 }
128 close(fd);
129 return ret;
130 }
131 #endif
132
is_wifi_driver_loaded()133 int is_wifi_driver_loaded() {
134 char driver_status[PROPERTY_VALUE_MAX];
135 #ifdef WIFI_DRIVER_MODULE_PATH
136 FILE *proc;
137 char line[sizeof(DRIVER_MODULE_TAG) + 10];
138 #endif
139
140 if (!property_get(DRIVER_PROP_NAME, driver_status, NULL)) {
141 return 0; /* driver not loaded */
142 }
143
144 if (!is_driver_loaded) {
145 return 0;
146 } /* driver not loaded */
147
148 #ifdef WIFI_DRIVER_MODULE_PATH
149 /*
150 * If the property says the driver is loaded, check to
151 * make sure that the property setting isn't just left
152 * over from a previous manual shutdown or a runtime
153 * crash.
154 */
155 if ((proc = fopen(MODULE_FILE, "r")) == NULL) {
156 PLOG(WARNING) << "Could not open " << MODULE_FILE;
157 is_driver_loaded = false;
158 if (strcmp(driver_status, "unloaded") != 0) {
159 property_set(DRIVER_PROP_NAME, "unloaded");
160 }
161 return 0;
162 }
163 while ((fgets(line, sizeof(line), proc)) != NULL) {
164 if (strncmp(line, DRIVER_MODULE_TAG, strlen(DRIVER_MODULE_TAG)) == 0) {
165 fclose(proc);
166 return 1;
167 }
168 }
169 fclose(proc);
170 is_driver_loaded = false;
171 if (strcmp(driver_status, "unloaded") != 0) {
172 property_set(DRIVER_PROP_NAME, "unloaded");
173 }
174 return 0;
175 #else
176 return 1;
177 #endif
178 }
179
wifi_load_driver()180 int wifi_load_driver() {
181 #ifdef WIFI_DRIVER_MODULE_PATH
182 if (is_wifi_driver_loaded()) {
183 return 0;
184 }
185
186 if (insmod(DRIVER_MODULE_PATH, DRIVER_MODULE_ARG) < 0) return -1;
187 #endif
188
189 #ifdef WIFI_DRIVER_STATE_CTRL_PARAM
190 if (is_wifi_driver_loaded()) {
191 return 0;
192 }
193
194 if (wifi_change_driver_state(WIFI_DRIVER_STATE_ON) < 0) {
195 #ifdef WIFI_DRIVER_MODULE_PATH
196 PLOG(WARNING) << "Driver unloading, err='fail to change driver state'";
197 if (rmmod(DRIVER_MODULE_NAME) == 0) {
198 PLOG(DEBUG) << "Driver unloaded";
199 } else {
200 // Set driver prop to "ok", expect HL to restart Wi-Fi.
201 PLOG(DEBUG) << "Driver unload failed! set driver prop to 'ok'.";
202 property_set(DRIVER_PROP_NAME, "ok");
203 }
204 #endif
205 return -1;
206 }
207 #endif
208 is_driver_loaded = true;
209 return 0;
210 }
211
wifi_unload_driver()212 int wifi_unload_driver() {
213 if (!is_wifi_driver_loaded()) {
214 return 0;
215 }
216 #ifdef WIFI_DRIVER_MODULE_PATH
217 if (rmmod(DRIVER_MODULE_NAME) == 0) {
218 int count = 20; /* wait at most 10 seconds for completion */
219 while (count-- > 0) {
220 if (!is_wifi_driver_loaded()) break;
221 usleep(500000);
222 }
223 usleep(500000); /* allow card removal */
224 if (count) {
225 return 0;
226 }
227 return -1;
228 } else
229 return -1;
230 #else
231 #ifdef WIFI_DRIVER_STATE_CTRL_PARAM
232 if (is_wifi_driver_loaded()) {
233 if (wifi_change_driver_state(WIFI_DRIVER_STATE_OFF) < 0) return -1;
234 }
235 #endif
236 is_driver_loaded = false;
237 property_set(DRIVER_PROP_NAME, "unloaded");
238 return 0;
239 #endif
240 }
241
wifi_get_fw_path(int fw_type)242 const char *wifi_get_fw_path(int fw_type) {
243 switch (fw_type) {
244 case WIFI_GET_FW_PATH_STA:
245 return WIFI_DRIVER_FW_PATH_STA;
246 case WIFI_GET_FW_PATH_AP:
247 return WIFI_DRIVER_FW_PATH_AP;
248 case WIFI_GET_FW_PATH_P2P:
249 return WIFI_DRIVER_FW_PATH_P2P;
250 }
251 return NULL;
252 }
253
wifi_change_fw_path(const char * fwpath)254 int wifi_change_fw_path(const char *fwpath) {
255 int len;
256 int fd;
257 int ret = 0;
258
259 if (!fwpath) return ret;
260 fd = TEMP_FAILURE_RETRY(open(WIFI_DRIVER_FW_PATH_PARAM, O_WRONLY));
261 if (fd < 0) {
262 PLOG(ERROR) << "Failed to open wlan fw path param";
263 return -1;
264 }
265 len = strlen(fwpath) + 1;
266 if (TEMP_FAILURE_RETRY(write(fd, fwpath, len)) != len) {
267 PLOG(ERROR) << "Failed to write wlan fw path param";
268 ret = -1;
269 }
270 close(fd);
271 return ret;
272 }
273