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
28extern "C" int init_module(void *, unsigned long, const char *);
29extern "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
45static const char DRIVER_PROP_NAME[] = "wlan.driver.status";
46#ifdef WIFI_DRIVER_MODULE_PATH
47static const char DRIVER_MODULE_NAME[] = WIFI_DRIVER_MODULE_NAME;
48static const char DRIVER_MODULE_TAG[] = WIFI_DRIVER_MODULE_NAME " ";
49static const char DRIVER_MODULE_PATH[] = WIFI_DRIVER_MODULE_PATH;
50static const char DRIVER_MODULE_ARG[] = WIFI_DRIVER_MODULE_ARG;
51static const char MODULE_FILE[] = "/proc/modules";
52#endif
53
54static 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
74static 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
92int 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
113int 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
150int 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
170int 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
200const 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
212int 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