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