diff options
-rw-r--r-- | include/hardware_legacy/wifi_fst.h | 162 | ||||
-rw-r--r-- | wifi/wifi_fst.c | 834 |
2 files changed, 996 insertions, 0 deletions
diff --git a/include/hardware_legacy/wifi_fst.h b/include/hardware_legacy/wifi_fst.h new file mode 100644 index 0000000..a60718d --- /dev/null +++ b/include/hardware_legacy/wifi_fst.h @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _WIFI_H +#define _WIFI_H + +#if __cplusplus +extern "C" { +#endif + +/** + * Load the Wi-Fi driver. + * + * @return 0 on success, < 0 on failure. + */ +int wifi_load_driver(); + +/** + * Unload the Wi-Fi driver. + * + * @return 0 on success, < 0 on failure. + */ +int wifi_unload_driver(); + +/** + * Check if the Wi-Fi driver is loaded. + * Check if the Wi-Fi driver is loaded. + + * @return 0 on success, < 0 on failure. + */ +int is_wifi_driver_loaded(); + + +/** + * Start supplicant. + * + * @return 0 on success, < 0 on failure. + */ +int wifi_start_supplicant(int p2pSupported); + +/** + * Stop supplicant. + * + * @return 0 on success, < 0 on failure. + */ +int wifi_stop_supplicant(int p2pSupported); + +/** + * Open a connection to supplicant + * + * @return 0 on success, < 0 on failure. + */ +int wifi_connect_to_supplicant(); + +/** + * Close connection to supplicant + * + * @return 0 on success, < 0 on failure. + */ +void wifi_close_supplicant_connection(); + +/** + * wifi_wait_for_event() performs a blocking call to + * get a Wi-Fi event and returns a string representing + * a Wi-Fi event when it occurs. + * + * @param buf is the buffer that receives the event + * @param len is the maximum length of the buffer + * + * @returns number of bytes in buffer, 0 if no + * event (for instance, no connection), and less than 0 + * if there is an error. + */ +int wifi_wait_for_event(char *buf, size_t len); + +/** + * wifi_command() issues a command to the Wi-Fi driver. + * + * Android extends the standard commands listed at + * /link http://hostap.epitest.fi/wpa_supplicant/devel/ctrl_iface_page.html + * to include support for sending commands to the driver: + * + * See wifi/java/android/net/wifi/WifiNative.java for the details of + * driver commands that are supported + * + * @param command is the string command (preallocated with 32 bytes) + * @param commandlen is command buffer length + * @param reply is a buffer to receive a reply string + * @param reply_len on entry, this is the maximum length of + * the reply buffer. On exit, the number of + * bytes in the reply buffer. + * + * @return 0 if successful, < 0 if an error. + */ +int wifi_command(const char *command, char *reply, size_t *reply_len); + +/** + * do_dhcp_request() issues a dhcp request and returns the acquired + * information. + * + * All IPV4 addresses/mask are in network byte order. + * + * @param ipaddr return the assigned IPV4 address + * @param gateway return the gateway being used + * @param mask return the IPV4 mask + * @param dns1 return the IPV4 address of a DNS server + * @param dns2 return the IPV4 address of a DNS server + * @param server return the IPV4 address of DHCP server + * @param lease return the length of lease in seconds. + * + * @return 0 if successful, < 0 if error. + */ +int do_dhcp_request(int *ipaddr, int *gateway, int *mask, + int *dns1, int *dns2, int *server, int *lease); + +/** + * Return the error string of the last do_dhcp_request(). + */ +const char *get_dhcp_error_string(); + +/** + * Return the path to requested firmware + */ +#define WIFI_GET_FW_PATH_STA 0 +#define WIFI_GET_FW_PATH_AP 1 +#define WIFI_GET_FW_PATH_P2P 2 +const char *wifi_get_fw_path(int fw_type); + +/** + * Change the path to firmware for the wlan driver + */ +int wifi_change_fw_path(const char *fwpath); + +/** + * Check and create if necessary initial entropy file + */ +#define WIFI_ENTROPY_FILE "/data/misc/wifi/entropy.bin" +int ensure_entropy_file_exists(); + +/** +* Check and create if necessary the desired configuration file +*/ +int ensure_config_file_exists(const char *config_file, const char *config_file_template); + +#if __cplusplus +}; // extern "C" +#endif + +#endif // _WIFI_H diff --git a/wifi/wifi_fst.c b/wifi/wifi_fst.c new file mode 100644 index 0000000..89e9397 --- /dev/null +++ b/wifi/wifi_fst.c @@ -0,0 +1,834 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <dirent.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <unistd.h> +#include <poll.h> + +#include "hardware_legacy/wifi.h" +#ifdef LIBWPA_CLIENT_EXISTS +#include "libwpa_client/wpa_ctrl.h" +#endif + +#define LOG_TAG "WifiHW" +#include "cutils/log.h" +#include "cutils/memory.h" +#include "cutils/misc.h" +#include "cutils/properties.h" +#include "private/android_filesystem_config.h" + +#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ +#include <sys/_system_properties.h> + +extern int do_dhcp(); +extern int ifc_init(); +extern void ifc_close(); +extern char *dhcp_lasterror(); +extern void get_dhcp_info(); +extern int init_module(void *, unsigned long, const char *); +extern int delete_module(const char *, unsigned int); +void wifi_close_sockets(); + +#ifndef LIBWPA_CLIENT_EXISTS +#define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING " +struct wpa_ctrl {}; +void wpa_ctrl_cleanup(void) {} +struct wpa_ctrl *wpa_ctrl_open(const char *ctrl_path) { return NULL; } +void wpa_ctrl_close(struct wpa_ctrl *ctrl) {} +int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, + char *reply, size_t *reply_len, void (*msg_cb)(char *msg, size_t len)) + { return 0; } +int wpa_ctrl_attach(struct wpa_ctrl *ctrl) { return 0; } +int wpa_ctrl_detach(struct wpa_ctrl *ctrl) { return 0; } +int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len) + { return 0; } +int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl) { return 0; } +#endif + +static struct wpa_ctrl *ctrl_conn; +static struct wpa_ctrl *monitor_conn; + +/* socket pair used to exit from a blocking read */ +static int exit_sockets[2]; + +static char primary_iface[PROPERTY_VALUE_MAX]; +// TODO: use new ANDROID_SOCKET mechanism, once support for multiple +// sockets is in + +#ifndef WIFI_DRIVER_MODULE_ARG +#define WIFI_DRIVER_MODULE_ARG "" +#endif +#ifndef WIFI_FIRMWARE_LOADER +#define WIFI_FIRMWARE_LOADER "" +#endif +#define WIFI_TEST_INTERFACE "sta" + +#ifndef WIFI_DRIVER_FW_PATH_STA +#define WIFI_DRIVER_FW_PATH_STA NULL +#endif +#ifndef WIFI_DRIVER_FW_PATH_AP +#define WIFI_DRIVER_FW_PATH_AP NULL +#endif +#ifndef WIFI_DRIVER_FW_PATH_P2P +#define WIFI_DRIVER_FW_PATH_P2P NULL +#endif + +#ifndef WIFI_DRIVER_FW_PATH_PARAM +#define WIFI_DRIVER_FW_PATH_PARAM "/sys/module/wlan/parameters/fwpath" +#endif + +#define WIFI_DRIVER_LOADER_DELAY 1000000 + +static const char IFACE_DIR[] = "/data/system/wpa_supplicant"; +#ifdef WIFI_DRIVER_MODULE_PATH +static const char DRIVER_MODULE_NAME[] = WIFI_DRIVER_MODULE_NAME; +static const char DRIVER_MODULE_TAG[] = WIFI_DRIVER_MODULE_NAME " "; +static const char DRIVER_MODULE_PATH[] = WIFI_DRIVER_MODULE_PATH; +static const char DRIVER_MODULE_ARG[] = WIFI_DRIVER_MODULE_ARG; +#endif +static const char FIRMWARE_LOADER[] = WIFI_FIRMWARE_LOADER; +static const char DRIVER_PROP_NAME[] = "wlan.driver.status"; +static const char SUPPLICANT_NAME[] = "wpa_supplicant"; +static const char SUPP_PROP_NAME[] = "init.svc.wpa_supplicant"; +static const char P2P_SUPPLICANT_NAME[] = "p2p_supplicant"; +static const char P2P_PROP_NAME[] = "init.svc.p2p_supplicant"; +static const char SUPP_CONFIG_TEMPLATE[]= "/system/etc/wifi/wpa_supplicant.conf"; +static const char SUPP_CONFIG_FILE[] = "/data/misc/wifi/wpa_supplicant.conf"; +static const char P2P_CONFIG_FILE[] = "/data/misc/wifi/p2p_supplicant.conf"; +static const char CONTROL_IFACE_PATH[] = "/data/misc/wifi/sockets"; +static const char MODULE_FILE[] = "/proc/modules"; + +static const char IFNAME[] = "IFNAME="; +#define IFNAMELEN (sizeof(IFNAME) - 1) +static const char WPA_EVENT_IGNORE[] = "CTRL-EVENT-IGNORE "; + +static const char SUPP_ENTROPY_FILE[] = WIFI_ENTROPY_FILE; +static unsigned char dummy_key[21] = { 0x02, 0x11, 0xbe, 0x33, 0x43, 0x35, + 0x68, 0x47, 0x84, 0x99, 0xa9, 0x2b, + 0x1c, 0xd3, 0xee, 0xff, 0xf1, 0xe2, + 0xf3, 0xf4, 0xf5 }; + +/* Is either SUPPLICANT_NAME or P2P_SUPPLICANT_NAME */ +static char supplicant_name[PROPERTY_VALUE_MAX]; +/* Is either SUPP_PROP_NAME or P2P_PROP_NAME */ +static char supplicant_prop_name[PROPERTY_KEY_MAX]; + +static int insmod(const char *filename, const char *args) +{ + void *module; + unsigned int size; + int ret; + + module = load_file(filename, &size); + if (!module) + return -1; + + ret = init_module(module, size, args); + + free(module); + + return ret; +} + +static int rmmod(const char *modname) +{ + int ret = -1; + int maxtry = 10; + + while (maxtry-- > 0) { + ret = delete_module(modname, O_NONBLOCK | O_EXCL); + if (ret < 0 && errno == EAGAIN) + usleep(500000); + else + break; + } + + if (ret != 0) + ALOGD("Unable to unload driver module \"%s\": %s\n", + modname, strerror(errno)); + return ret; +} + +int do_dhcp_request(int *ipaddr, int *gateway, int *mask, + int *dns1, int *dns2, int *server, int *lease) { + /* For test driver, always report success */ + if (strcmp(primary_iface, WIFI_TEST_INTERFACE) == 0) + return 0; + + if (ifc_init() < 0) + return -1; + + if (do_dhcp(primary_iface) < 0) { + ifc_close(); + return -1; + } + ifc_close(); + get_dhcp_info(ipaddr, gateway, mask, dns1, dns2, server, lease); + return 0; +} + +const char *get_dhcp_error_string() { + return dhcp_lasterror(); +} + +#ifdef WIFI_DRIVER_STATE_CTRL_PARAM +int wifi_change_driver_state(const char *state) +{ + int len; + int fd; + int ret = 0; + + if (!state) + return -1; + fd = TEMP_FAILURE_RETRY(open(WIFI_DRIVER_STATE_CTRL_PARAM, O_WRONLY)); + if (fd < 0) { + ALOGE("Failed to open driver state control param (%s)", strerror(errno)); + return -1; + } + len = strlen(state) + 1; + if (TEMP_FAILURE_RETRY(write(fd, state, len)) != len) { + ALOGE("Failed to write driver state control param (%s)", strerror(errno)); + ret = -1; + } + close(fd); + return ret; +} +#endif + +int is_wifi_driver_loaded() { + char driver_status[PROPERTY_VALUE_MAX]; +#ifdef WIFI_DRIVER_MODULE_PATH + FILE *proc; + char line[sizeof(DRIVER_MODULE_TAG)+10]; +#endif + + if (!property_get(DRIVER_PROP_NAME, driver_status, NULL) + || strcmp(driver_status, "ok") != 0) { + return 0; /* driver not loaded */ + } +#ifdef WIFI_DRIVER_MODULE_PATH + /* + * If the property says the driver is loaded, check to + * make sure that the property setting isn't just left + * over from a previous manual shutdown or a runtime + * crash. + */ + if ((proc = fopen(MODULE_FILE, "r")) == NULL) { + ALOGW("Could not open %s: %s", MODULE_FILE, strerror(errno)); + property_set(DRIVER_PROP_NAME, "unloaded"); + return 0; + } + while ((fgets(line, sizeof(line), proc)) != NULL) { + if (strncmp(line, DRIVER_MODULE_TAG, strlen(DRIVER_MODULE_TAG)) == 0) { + fclose(proc); + return 1; + } + } + fclose(proc); + property_set(DRIVER_PROP_NAME, "unloaded"); + return 0; +#else + return 1; +#endif +} + +int wifi_load_driver() +{ +#ifdef WIFI_DRIVER_MODULE_PATH + char driver_status[PROPERTY_VALUE_MAX]; + int count = 100; /* wait at most 20 seconds for completion */ + + if (is_wifi_driver_loaded()) { + return 0; + } + + if (insmod(DRIVER_MODULE_PATH, DRIVER_MODULE_ARG) < 0) + return -1; + + if (strcmp(FIRMWARE_LOADER,"") == 0) { + /* usleep(WIFI_DRIVER_LOADER_DELAY); */ + property_set(DRIVER_PROP_NAME, "ok"); + } + else { + property_set("ctl.start", FIRMWARE_LOADER); + } + sched_yield(); + while (count-- > 0) { + if (property_get(DRIVER_PROP_NAME, driver_status, NULL)) { + if (strcmp(driver_status, "ok") == 0) + return 0; + else if (strcmp(driver_status, "failed") == 0) { + wifi_unload_driver(); + return -1; + } + } + usleep(200000); + } + property_set(DRIVER_PROP_NAME, "timeout"); + wifi_unload_driver(); + return -1; +#else +#ifdef WIFI_DRIVER_STATE_CTRL_PARAM + if (is_wifi_driver_loaded()) { + return 0; + } + + if (wifi_change_driver_state(WIFI_DRIVER_STATE_ON) < 0) + return -1; +#endif + property_set(DRIVER_PROP_NAME, "ok"); + return 0; +#endif +} + +int wifi_unload_driver() +{ + usleep(200000); /* allow to finish interface down */ +#ifdef WIFI_DRIVER_MODULE_PATH + if (rmmod(DRIVER_MODULE_NAME) == 0) { + int count = 20; /* wait at most 10 seconds for completion */ + while (count-- > 0) { + if (!is_wifi_driver_loaded()) + break; + usleep(500000); + } + usleep(500000); /* allow card removal */ + if (count) { + return 0; + } + return -1; + } else + return -1; +#else +#ifdef WIFI_DRIVER_STATE_CTRL_PARAM + if (is_wifi_driver_loaded()) { + if (wifi_change_driver_state(WIFI_DRIVER_STATE_OFF) < 0) + return -1; + } +#endif + property_set(DRIVER_PROP_NAME, "unloaded"); + return 0; +#endif +} + +int ensure_entropy_file_exists() +{ + int ret; + int destfd; + + ret = access(SUPP_ENTROPY_FILE, R_OK|W_OK); + if ((ret == 0) || (errno == EACCES)) { + if ((ret != 0) && + (chmod(SUPP_ENTROPY_FILE, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) != 0)) { + ALOGE("Cannot set RW to \"%s\": %s", SUPP_ENTROPY_FILE, strerror(errno)); + return -1; + } + return 0; + } + destfd = TEMP_FAILURE_RETRY(open(SUPP_ENTROPY_FILE, O_CREAT|O_RDWR, 0660)); + if (destfd < 0) { + ALOGE("Cannot create \"%s\": %s", SUPP_ENTROPY_FILE, strerror(errno)); + return -1; + } + + if (TEMP_FAILURE_RETRY(write(destfd, dummy_key, sizeof(dummy_key))) != sizeof(dummy_key)) { + ALOGE("Error writing \"%s\": %s", SUPP_ENTROPY_FILE, strerror(errno)); + close(destfd); + return -1; + } + close(destfd); + + /* chmod is needed because open() didn't set permisions properly */ + if (chmod(SUPP_ENTROPY_FILE, 0660) < 0) { + ALOGE("Error changing permissions of %s to 0660: %s", + SUPP_ENTROPY_FILE, strerror(errno)); + unlink(SUPP_ENTROPY_FILE); + return -1; + } + + if (chown(SUPP_ENTROPY_FILE, AID_SYSTEM, AID_WIFI) < 0) { + ALOGE("Error changing group ownership of %s to %d: %s", + SUPP_ENTROPY_FILE, AID_WIFI, strerror(errno)); + unlink(SUPP_ENTROPY_FILE); + return -1; + } + return 0; +} + +int ensure_config_file_exists(const char *config_file, const char *config_file_template) +{ + char buf[2048]; + int srcfd, destfd; + struct stat sb; + int nread; + int ret; + + ret = access(config_file, R_OK|W_OK); + if ((ret == 0) || (errno == EACCES)) { + if ((ret != 0) && + (chmod(config_file, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) != 0)) { + ALOGE("Cannot set RW to \"%s\": %s", config_file, strerror(errno)); + return -1; + } + return 0; + } else if (errno != ENOENT) { + ALOGE("Cannot access \"%s\": %s", config_file, strerror(errno)); + return -1; + } + + srcfd = TEMP_FAILURE_RETRY(open(config_file_template, O_RDONLY)); + if (srcfd < 0) { + ALOGE("Cannot open \"%s\": %s", config_file_template, strerror(errno)); + return -1; + } + + destfd = TEMP_FAILURE_RETRY(open(config_file, O_CREAT|O_RDWR, 0660)); + if (destfd < 0) { + close(srcfd); + ALOGE("Cannot create \"%s\": %s", config_file, strerror(errno)); + return -1; + } + + while ((nread = TEMP_FAILURE_RETRY(read(srcfd, buf, sizeof(buf)))) != 0) { + if (nread < 0) { + ALOGE("Error reading \"%s\": %s", config_file_template, strerror(errno)); + close(srcfd); + close(destfd); + unlink(config_file); + return -1; + } + TEMP_FAILURE_RETRY(write(destfd, buf, nread)); + } + + close(destfd); + close(srcfd); + + /* chmod is needed because open() didn't set permisions properly */ + if (chmod(config_file, 0660) < 0) { + ALOGE("Error changing permissions of %s to 0660: %s", + config_file, strerror(errno)); + unlink(config_file); + return -1; + } + + if (chown(config_file, AID_SYSTEM, AID_WIFI) < 0) { + ALOGE("Error changing group ownership of %s to %d: %s", + config_file, AID_WIFI, strerror(errno)); + unlink(config_file); + return -1; + } + return 0; +} + +int wifi_start_supplicant(int p2p_supported) +{ + char supp_status[PROPERTY_VALUE_MAX] = {'\0'}; + int count = 200; /* wait at most 20 seconds for completion */ + const prop_info *pi; + unsigned serial = 0, i; + + if (p2p_supported) { + strcpy(supplicant_name, P2P_SUPPLICANT_NAME); + strcpy(supplicant_prop_name, P2P_PROP_NAME); + + /* Ensure p2p config file is created */ + if (ensure_config_file_exists(P2P_CONFIG_FILE, SUPP_CONFIG_TEMPLATE) < 0) { + ALOGE("Failed to create a p2p config file"); + return -1; + } + + } else { + strcpy(supplicant_name, SUPPLICANT_NAME); + strcpy(supplicant_prop_name, SUPP_PROP_NAME); + } + + /* Check whether already running */ + if (property_get(supplicant_prop_name, supp_status, NULL) + && strcmp(supp_status, "running") == 0) { + return 0; + } + + /* Before starting the daemon, make sure its config file exists */ + if (ensure_config_file_exists(SUPP_CONFIG_FILE, SUPP_CONFIG_TEMPLATE) < 0) { + ALOGE("Wi-Fi will not be enabled"); + return -1; + } + + if (ensure_entropy_file_exists() < 0) { + ALOGE("Wi-Fi entropy file was not created"); + } + + /* Clear out any stale socket files that might be left over. */ + wpa_ctrl_cleanup(); + + /* Reset sockets used for exiting from hung state */ + exit_sockets[0] = exit_sockets[1] = -1; + + /* + * Get a reference to the status property, so we can distinguish + * the case where it goes stopped => running => stopped (i.e., + * it start up, but fails right away) from the case in which + * it starts in the stopped state and never manages to start + * running at all. + */ + pi = __system_property_find(supplicant_prop_name); + if (pi != NULL) { + serial = __system_property_serial(pi); + } + property_get("wifi.interface", primary_iface, WIFI_TEST_INTERFACE); + + property_set("ctl.start", supplicant_name); + sched_yield(); + + while (count-- > 0) { + if (pi == NULL) { + pi = __system_property_find(supplicant_prop_name); + } + if (pi != NULL) { + /* + * property serial updated means that init process is scheduled + * after we sched_yield, further property status checking is based on this */ + if (__system_property_serial(pi) != serial) { + __system_property_read(pi, NULL, supp_status); + if (strcmp(supp_status, "running") == 0) { + return 0; + } else if (strcmp(supp_status, "stopped") == 0) { + return -1; + } + } + } + usleep(100000); + } + return -1; +} + +int wifi_stop_supplicant(int p2p_supported) +{ + char supp_status[PROPERTY_VALUE_MAX] = {'\0'}; + int count = 50; /* wait at most 5 seconds for completion */ + + if (p2p_supported) { + strcpy(supplicant_name, P2P_SUPPLICANT_NAME); + strcpy(supplicant_prop_name, P2P_PROP_NAME); + } else { + strcpy(supplicant_name, SUPPLICANT_NAME); + strcpy(supplicant_prop_name, SUPP_PROP_NAME); + } + + /* Check whether supplicant already stopped */ + if (property_get(supplicant_prop_name, supp_status, NULL) + && strcmp(supp_status, "stopped") == 0) { + return 0; + } + + property_set("ctl.stop", supplicant_name); + sched_yield(); + + while (count-- > 0) { + if (property_get(supplicant_prop_name, supp_status, NULL)) { + if (strcmp(supp_status, "stopped") == 0) + return 0; + } + usleep(100000); + } + ALOGE("Failed to stop supplicant"); + return -1; +} + +int wifi_connect_on_socket_path(const char *path) +{ + char supp_status[PROPERTY_VALUE_MAX] = {'\0'}; + + /* Make sure supplicant is running */ + if (!property_get(supplicant_prop_name, supp_status, NULL) + || strcmp(supp_status, "running") != 0) { + ALOGE("Supplicant not running, cannot connect"); + return -1; + } + + ctrl_conn = wpa_ctrl_open(path); + if (ctrl_conn == NULL) { + ALOGE("Unable to open connection to supplicant on \"%s\": %s", + path, strerror(errno)); + return -1; + } + monitor_conn = wpa_ctrl_open(path); + if (monitor_conn == NULL) { + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; + return -1; + } + if (wpa_ctrl_attach(monitor_conn) != 0) { + wpa_ctrl_close(monitor_conn); + wpa_ctrl_close(ctrl_conn); + ctrl_conn = monitor_conn = NULL; + return -1; + } + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, exit_sockets) == -1) { + wpa_ctrl_close(monitor_conn); + wpa_ctrl_close(ctrl_conn); + ctrl_conn = monitor_conn = NULL; + return -1; + } + + return 0; +} + +/* Establishes the control and monitor socket connections on the interface */ +int wifi_connect_to_supplicant() +{ + static char path[PATH_MAX]; + + if (access(IFACE_DIR, F_OK) == 0) { + snprintf(path, sizeof(path), "%s/%s", IFACE_DIR, primary_iface); + } else { + snprintf(path, sizeof(path), "@android:wpa_%s", primary_iface); + } + return wifi_connect_on_socket_path(path); +} + +int wifi_send_command(const char *cmd, char *reply, size_t *reply_len) +{ + int ret; + if (ctrl_conn == NULL) { + ALOGV("Not connected to wpa_supplicant - \"%s\" command dropped.\n", cmd); + return -1; + } + ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), reply, reply_len, NULL); + if (ret == -2) { + ALOGD("'%s' command timed out.\n", cmd); + /* unblocks the monitor receive socket for termination */ + TEMP_FAILURE_RETRY(write(exit_sockets[0], "T", 1)); + return -2; + } else if (ret < 0 || strncmp(reply, "FAIL", 4) == 0) { + return -1; + } + if (strncmp(cmd, "PING", 4) == 0) { + reply[*reply_len] = '\0'; + } + return 0; +} + +int wifi_supplicant_connection_active() +{ + char supp_status[PROPERTY_VALUE_MAX] = {'\0'}; + + if (property_get(supplicant_prop_name, supp_status, NULL)) { + if (strcmp(supp_status, "stopped") == 0) + return -1; + } + + return 0; +} + +int wifi_ctrl_recv(char *reply, size_t *reply_len) +{ + int res; + int ctrlfd = wpa_ctrl_get_fd(monitor_conn); + struct pollfd rfds[2]; + + memset(rfds, 0, 2 * sizeof(struct pollfd)); + rfds[0].fd = ctrlfd; + rfds[0].events |= POLLIN; + rfds[1].fd = exit_sockets[1]; + rfds[1].events |= POLLIN; + do { + res = TEMP_FAILURE_RETRY(poll(rfds, 2, 30000)); + if (res < 0) { + ALOGE("Error poll = %d", res); + return res; + } else if (res == 0) { + /* timed out, check if supplicant is active + * or not .. + */ + res = wifi_supplicant_connection_active(); + if (res < 0) + return -2; + } + } while (res == 0); + + if (rfds[0].revents & POLLIN) { + return wpa_ctrl_recv(monitor_conn, reply, reply_len); + } + + /* it is not rfds[0], then it must be rfts[1] (i.e. the exit socket) + * or we timed out. In either case, this call has failed .. + */ + return -2; +} + +int wifi_wait_on_socket(char *buf, size_t buflen) +{ + size_t nread = buflen - 1; + int result; + char *match, *match2; + + if (monitor_conn == NULL) { + return snprintf(buf, buflen, "IFNAME=%s %s - connection closed", + primary_iface, WPA_EVENT_TERMINATING); + } + + result = wifi_ctrl_recv(buf, &nread); + + /* Terminate reception on exit socket */ + if (result == -2) { + return snprintf(buf, buflen, "IFNAME=%s %s - connection closed", + primary_iface, WPA_EVENT_TERMINATING); + } + + if (result < 0) { + ALOGD("wifi_ctrl_recv failed: %s\n", strerror(errno)); + return snprintf(buf, buflen, "IFNAME=%s %s - recv error", + primary_iface, WPA_EVENT_TERMINATING); + } + buf[nread] = '\0'; + /* Check for EOF on the socket */ + if (result == 0 && nread == 0) { + /* Fabricate an event to pass up */ + ALOGD("Received EOF on supplicant socket\n"); + return snprintf(buf, buflen, "IFNAME=%s %s - signal 0 received", + primary_iface, WPA_EVENT_TERMINATING); + } + /* + * Events strings are in the format + * + * IFNAME=iface <N>CTRL-EVENT-XXX + * or + * <N>CTRL-EVENT-XXX + * + * where N is the message level in numerical form (0=VERBOSE, 1=DEBUG, + * etc.) and XXX is the event name. The level information is not useful + * to us, so strip it off. + */ + + if (strncmp(buf, IFNAME, IFNAMELEN) == 0) { + match = strchr(buf, ' '); + if (match != NULL) { + if (match[1] == '<') { + match2 = strchr(match + 2, '>'); + if (match2 != NULL) { + nread -= (match2 - match); + memmove(match + 1, match2 + 1, nread - (match - buf) + 1); + } + } + } else { + return snprintf(buf, buflen, "%s", WPA_EVENT_IGNORE); + } + } else if (buf[0] == '<') { + match = strchr(buf, '>'); + if (match != NULL) { + nread -= (match + 1 - buf); + memmove(buf, match + 1, nread + 1); + ALOGV("supplicant generated event without interface - %s\n", buf); + } + } else { + /* let the event go as is! */ + ALOGW("supplicant generated event without interface and without message level - %s\n", buf); + } + + return nread; +} + +int wifi_wait_for_event(char *buf, size_t buflen) +{ + return wifi_wait_on_socket(buf, buflen); +} + +void wifi_close_sockets() +{ + if (ctrl_conn != NULL) { + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; + } + + if (monitor_conn != NULL) { + wpa_ctrl_close(monitor_conn); + monitor_conn = NULL; + } + + if (exit_sockets[0] >= 0) { + close(exit_sockets[0]); + exit_sockets[0] = -1; + } + + if (exit_sockets[1] >= 0) { + close(exit_sockets[1]); + exit_sockets[1] = -1; + } +} + +void wifi_close_supplicant_connection() +{ + char supp_status[PROPERTY_VALUE_MAX] = {'\0'}; + int count = 50; /* wait at most 5 seconds to ensure init has stopped stupplicant */ + + wifi_close_sockets(); + + while (count-- > 0) { + if (property_get(supplicant_prop_name, supp_status, NULL)) { + if (strcmp(supp_status, "stopped") == 0) + return; + } + usleep(100000); + } +} + +int wifi_command(const char *command, char *reply, size_t *reply_len) +{ + return wifi_send_command(command, reply, reply_len); +} + +const char *wifi_get_fw_path(int fw_type) +{ + switch (fw_type) { + case WIFI_GET_FW_PATH_STA: + return WIFI_DRIVER_FW_PATH_STA; + case WIFI_GET_FW_PATH_AP: + return WIFI_DRIVER_FW_PATH_AP; + case WIFI_GET_FW_PATH_P2P: + return WIFI_DRIVER_FW_PATH_P2P; + } + return NULL; +} + +int wifi_change_fw_path(const char *fwpath) +{ + int len; + int fd; + int ret = 0; + + if (!fwpath) + return ret; + fd = TEMP_FAILURE_RETRY(open(WIFI_DRIVER_FW_PATH_PARAM, O_WRONLY)); + if (fd < 0) { + ALOGE("Failed to open wlan fw path param (%s)", strerror(errno)); + return -1; + } + len = strlen(fwpath) + 1; + if (TEMP_FAILURE_RETRY(write(fd, fwpath, len)) != len) { + ALOGE("Failed to write wlan fw path param (%s)", strerror(errno)); + ret = -1; + } + close(fd); + return ret; +} |