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 #ifdef WIFI_DRIVER_MODULE_PATH
47 static const char DRIVER_MODULE_NAME[] = WIFI_DRIVER_MODULE_NAME;
48 static const char DRIVER_MODULE_TAG[] = WIFI_DRIVER_MODULE_NAME " ";
49 static const char DRIVER_MODULE_PATH[] = WIFI_DRIVER_MODULE_PATH;
50 static const char DRIVER_MODULE_ARG[] = WIFI_DRIVER_MODULE_ARG;
51 static const char MODULE_FILE[] = "/proc/modules";
52 #endif
53
insmod(const char * filename,const char * args)54 static int insmod(const char *filename, const char *args) {
55 int ret;
56 int fd;
57
58 fd = TEMP_FAILURE_RETRY(open(filename, O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
59 if (fd < 0) {
60 PLOG(ERROR) << "Failed to open " << filename;
61 return -1;
62 }
63
64 ret = syscall(__NR_finit_module, fd, args, 0);
65
66 close(fd);
67 if (ret < 0) {
68 PLOG(ERROR) << "finit_module return: " << ret;
69 }
70
71 return ret;
72 }
73
rmmod(const char * modname)74 static int rmmod(const char *modname) {
75 int ret = -1;
76 int maxtry = 10;
77
78 while (maxtry-- > 0) {
79 ret = delete_module(modname, O_NONBLOCK | O_EXCL);
80 if (ret < 0 && errno == EAGAIN)
81 usleep(500000);
82 else
83 break;
84 }
85
86 if (ret != 0)
87 PLOG(DEBUG) << "Unable to unload driver module '" << modname << "'";
88 return ret;
89 }
90
91 #ifdef WIFI_DRIVER_STATE_CTRL_PARAM
wifi_change_driver_state(const char * state)92 int wifi_change_driver_state(const char *state) {
93 int len;
94 int fd;
95 int ret = 0;
96
97 if (!state) return -1;
98 fd = TEMP_FAILURE_RETRY(open(WIFI_DRIVER_STATE_CTRL_PARAM, O_WRONLY));
99 if (fd < 0) {
100 PLOG(ERROR) << "Failed to open driver state control param";
101 return -1;
102 }
103 len = strlen(state) + 1;
104 if (TEMP_FAILURE_RETRY(write(fd, state, len)) != len) {
105 PLOG(ERROR) << "Failed to write driver state control param";
106 ret = -1;
107 }
108 close(fd);
109 return ret;
110 }
111 #endif
112
is_wifi_driver_loaded()113 int is_wifi_driver_loaded() {
114 char driver_status[PROPERTY_VALUE_MAX];
115 #ifdef WIFI_DRIVER_MODULE_PATH
116 FILE *proc;
117 char line[sizeof(DRIVER_MODULE_TAG) + 10];
118 #endif
119
120 if (!property_get(DRIVER_PROP_NAME, driver_status, NULL) ||
121 strcmp(driver_status, "ok") != 0) {
122 return 0; /* driver not loaded */
123 }
124 #ifdef WIFI_DRIVER_MODULE_PATH
125 /*
126 * If the property says the driver is loaded, check to
127 * make sure that the property setting isn't just left
128 * over from a previous manual shutdown or a runtime
129 * crash.
130 */
131 if ((proc = fopen(MODULE_FILE, "r")) == NULL) {
132 PLOG(WARNING) << "Could not open " << MODULE_FILE;
133 property_set(DRIVER_PROP_NAME, "unloaded");
134 return 0;
135 }
136 while ((fgets(line, sizeof(line), proc)) != NULL) {
137 if (strncmp(line, DRIVER_MODULE_TAG, strlen(DRIVER_MODULE_TAG)) == 0) {
138 fclose(proc);
139 return 1;
140 }
141 }
142 fclose(proc);
143 property_set(DRIVER_PROP_NAME, "unloaded");
144 return 0;
145 #else
146 return 1;
147 #endif
148 }
149
wifi_load_driver()150 int wifi_load_driver() {
151 #ifdef WIFI_DRIVER_MODULE_PATH
152 if (is_wifi_driver_loaded()) {
153 return 0;
154 }
155
156 if (insmod(DRIVER_MODULE_PATH, DRIVER_MODULE_ARG) < 0) return -1;
157 #endif
158
159 #ifdef WIFI_DRIVER_STATE_CTRL_PARAM
160 if (is_wifi_driver_loaded()) {
161 return 0;
162 }
163
164 if (wifi_change_driver_state(WIFI_DRIVER_STATE_ON) < 0) return -1;
165 #endif
166 property_set(DRIVER_PROP_NAME, "ok");
167 return 0;
168 }
169
wifi_unload_driver()170 int wifi_unload_driver() {
171 if (!is_wifi_driver_loaded()) {
172 return 0;
173 }
174 usleep(200000); /* allow to finish interface down */
175 #ifdef WIFI_DRIVER_MODULE_PATH
176 if (rmmod(DRIVER_MODULE_NAME) == 0) {
177 int count = 20; /* wait at most 10 seconds for completion */
178 while (count-- > 0) {
179 if (!is_wifi_driver_loaded()) break;
180 usleep(500000);
181 }
182 usleep(500000); /* allow card removal */
183 if (count) {
184 return 0;
185 }
186 return -1;
187 } else
188 return -1;
189 #else
190 #ifdef WIFI_DRIVER_STATE_CTRL_PARAM
191 if (is_wifi_driver_loaded()) {
192 if (wifi_change_driver_state(WIFI_DRIVER_STATE_OFF) < 0) return -1;
193 }
194 #endif
195 property_set(DRIVER_PROP_NAME, "unloaded");
196 return 0;
197 #endif
198 }
199
wifi_get_fw_path(int fw_type)200 const char *wifi_get_fw_path(int fw_type) {
201 switch (fw_type) {
202 case WIFI_GET_FW_PATH_STA:
203 return WIFI_DRIVER_FW_PATH_STA;
204 case WIFI_GET_FW_PATH_AP:
205 return WIFI_DRIVER_FW_PATH_AP;
206 case WIFI_GET_FW_PATH_P2P:
207 return WIFI_DRIVER_FW_PATH_P2P;
208 }
209 return NULL;
210 }
211
wifi_change_fw_path(const char * fwpath)212 int wifi_change_fw_path(const char *fwpath) {
213 int len;
214 int fd;
215 int ret = 0;
216
217 if (!fwpath) return ret;
218 fd = TEMP_FAILURE_RETRY(open(WIFI_DRIVER_FW_PATH_PARAM, O_WRONLY));
219 if (fd < 0) {
220 PLOG(ERROR) << "Failed to open wlan fw path param";
221 return -1;
222 }
223 len = strlen(fwpath) + 1;
224 if (TEMP_FAILURE_RETRY(write(fd, fwpath, len)) != len) {
225 PLOG(ERROR) << "Failed to write wlan fw path param";
226 ret = -1;
227 }
228 close(fd);
229 return ret;
230 }
231