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
23 #include <android-base/logging.h>
24 #include <cutils/misc.h>
25 #include <cutils/properties.h>
26 #include <sys/syscall.h>
27
28 extern "C" int init_module(void *, unsigned long, const char *);
29 extern "C" int delete_module(const char *, unsigned int);
30
31 #ifndef WIFI_DRIVER_FW_PATH_STA
32 #define WIFI_DRIVER_FW_PATH_STA NULL
33 #endif
34 #ifndef WIFI_DRIVER_FW_PATH_AP
35 #define WIFI_DRIVER_FW_PATH_AP NULL
36 #endif
37 #ifndef WIFI_DRIVER_FW_PATH_P2P
38 #define WIFI_DRIVER_FW_PATH_P2P NULL
39 #endif
40
41 #ifndef WIFI_DRIVER_MODULE_ARG
42 #define WIFI_DRIVER_MODULE_ARG ""
43 #endif
44
45 static const char DRIVER_PROP_NAME[] = "wlan.driver.status";
46 static bool is_driver_loaded = false;
47 #ifdef WIFI_DRIVER_MODULE_PATH
48 static const char DRIVER_MODULE_NAME[] = WIFI_DRIVER_MODULE_NAME;
49 static const char DRIVER_MODULE_TAG[] = WIFI_DRIVER_MODULE_NAME " ";
50 static const char DRIVER_MODULE_PATH[] = WIFI_DRIVER_MODULE_PATH;
51 static const char DRIVER_MODULE_ARG[] = WIFI_DRIVER_MODULE_ARG;
52 static const char MODULE_FILE[] = "/proc/modules";
53 #endif
54
insmod(const char * filename,const char * args)55 static int insmod(const char *filename, const char *args) {
56 int ret;
57 int fd;
58
59 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 }
64
65 ret = syscall(__NR_finit_module, fd, args, 0);
66
67 close(fd);
68 if (ret < 0) {
69 PLOG(ERROR) << "finit_module return: " << ret;
70 }
71
72 return ret;
73 }
74
rmmod(const char * modname)75 static int rmmod(const char *modname) {
76 int ret = -1;
77 int maxtry = 10;
78
79 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 }
86
87 if (ret != 0)
88 PLOG(DEBUG) << "Unable to unload driver module '" << modname << "'";
89 return ret;
90 }
91
92 #ifdef WIFI_DRIVER_STATE_CTRL_PARAM
wifi_change_driver_state(const char * state)93 int wifi_change_driver_state(const char *state) {
94 int len;
95 int fd;
96 int ret = 0;
97
98 if (!state) return -1;
99 fd = TEMP_FAILURE_RETRY(open(WIFI_DRIVER_STATE_CTRL_PARAM, O_WRONLY));
100 if (fd < 0) {
101 PLOG(ERROR) << "Failed to open driver state control param";
102 return -1;
103 }
104 len = strlen(state) + 1;
105 if (TEMP_FAILURE_RETRY(write(fd, state, len)) != len) {
106 PLOG(ERROR) << "Failed to write driver state control param";
107 ret = -1;
108 }
109 close(fd);
110 return ret;
111 }
112 #endif
113
is_wifi_driver_loaded()114 int is_wifi_driver_loaded() {
115 char driver_status[PROPERTY_VALUE_MAX];
116 #ifdef WIFI_DRIVER_MODULE_PATH
117 FILE *proc;
118 char line[sizeof(DRIVER_MODULE_TAG) + 10];
119 #endif
120
121 if (!property_get(DRIVER_PROP_NAME, driver_status, NULL)) {
122 return 0; /* driver not loaded */
123 }
124
125 if (!is_driver_loaded) {
126 return 0;
127 } /* driver not loaded */
128
129 #ifdef WIFI_DRIVER_MODULE_PATH
130 /*
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) {
137 PLOG(WARNING) << "Could not open " << MODULE_FILE;
138 is_driver_loaded = false;
139 if (strcmp(driver_status, "unloaded") != 0) {
140 property_set(DRIVER_PROP_NAME, "unloaded");
141 }
142 return 0;
143 }
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);
151 is_driver_loaded = false;
152 if (strcmp(driver_status, "unloaded") != 0) {
153 property_set(DRIVER_PROP_NAME, "unloaded");
154 }
155 return 0;
156 #else
157 return 1;
158 #endif
159 }
160
wifi_load_driver()161 int wifi_load_driver() {
162 #ifdef WIFI_DRIVER_MODULE_PATH
163 if (is_wifi_driver_loaded()) {
164 return 0;
165 }
166
167 if (insmod(DRIVER_MODULE_PATH, DRIVER_MODULE_ARG) < 0) return -1;
168 #endif
169
170 #ifdef WIFI_DRIVER_STATE_CTRL_PARAM
171 if (is_wifi_driver_loaded()) {
172 return 0;
173 }
174
175 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 }
188 #endif
189 is_driver_loaded = true;
190 return 0;
191 }
192
wifi_unload_driver()193 int wifi_unload_driver() {
194 if (!is_wifi_driver_loaded()) {
195 return 0;
196 }
197 #ifdef WIFI_DRIVER_MODULE_PATH
198 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;
211 #else
212 #ifdef WIFI_DRIVER_STATE_CTRL_PARAM
213 if (is_wifi_driver_loaded()) {
214 if (wifi_change_driver_state(WIFI_DRIVER_STATE_OFF) < 0) return -1;
215 }
216 #endif
217 is_driver_loaded = false;
218 property_set(DRIVER_PROP_NAME, "unloaded");
219 return 0;
220 #endif
221 }
222
wifi_get_fw_path(int fw_type)223 const char *wifi_get_fw_path(int fw_type) {
224 switch (fw_type) {
225 case WIFI_GET_FW_PATH_STA:
226 return WIFI_DRIVER_FW_PATH_STA;
227 case WIFI_GET_FW_PATH_AP:
228 return WIFI_DRIVER_FW_PATH_AP;
229 case WIFI_GET_FW_PATH_P2P:
230 return WIFI_DRIVER_FW_PATH_P2P;
231 }
232 return NULL;
233 }
234
wifi_change_fw_path(const char * fwpath)235 int wifi_change_fw_path(const char *fwpath) {
236 int len;
237 int fd;
238 int ret = 0;
239
240 if (!fwpath) return ret;
241 fd = TEMP_FAILURE_RETRY(open(WIFI_DRIVER_FW_PATH_PARAM, O_WRONLY));
242 if (fd < 0) {
243 PLOG(ERROR) << "Failed to open wlan fw path param";
244 return -1;
245 }
246 len = strlen(fwpath) + 1;
247 if (TEMP_FAILURE_RETRY(write(fd, fwpath, len)) != len) {
248 PLOG(ERROR) << "Failed to write wlan fw path param";
249 ret = -1;
250 }
251 close(fd);
252 return ret;
253 }
254