diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:32:14 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:32:14 -0800 |
commit | cc490161f6af9e4a6842ee827e4bfc43bc4509d5 (patch) | |
tree | a60e0ed36ef1d3a43530c46562c86c9cd431744c /wifi | |
parent | 3bbe5328cd738976443822502e23fb3971c5f87b (diff) | |
download | hardware_libhardware_legacy-cc490161f6af9e4a6842ee827e4bfc43bc4509d5.zip hardware_libhardware_legacy-cc490161f6af9e4a6842ee827e4bfc43bc4509d5.tar.gz hardware_libhardware_legacy-cc490161f6af9e4a6842ee827e4bfc43bc4509d5.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'wifi')
-rw-r--r-- | wifi/Android.mk | 5 | ||||
-rw-r--r-- | wifi/wifi.c | 458 |
2 files changed, 463 insertions, 0 deletions
diff --git a/wifi/Android.mk b/wifi/Android.mk new file mode 100644 index 0000000..8444a40 --- /dev/null +++ b/wifi/Android.mk @@ -0,0 +1,5 @@ +# Copyright 2006 The Android Open Source Project + +LOCAL_SRC_FILES += wifi/wifi.c + +LOCAL_SHARED_LIBRARIES += libnetutils diff --git a/wifi/wifi.c b/wifi/wifi.c new file mode 100644 index 0000000..0778e77 --- /dev/null +++ b/wifi/wifi.c @@ -0,0 +1,458 @@ +/* + * 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 "hardware_legacy/wifi.h" +#include "libwpa_client/wpa_ctrl.h" + +#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" +#ifdef HAVE_LIBC_SYSTEM_PROPERTIES +#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ +#include <sys/_system_properties.h> +#endif + +static struct wpa_ctrl *ctrl_conn; +static struct wpa_ctrl *monitor_conn; + +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); + +static char iface[PROPERTY_VALUE_MAX]; +// TODO: use new ANDROID_SOCKET mechanism, once support for multiple +// sockets is in + +static const char IFACE_DIR[] = "/data/system/wpa_supplicant"; +static const char DRIVER_MODULE_NAME[] = "wlan"; +static const char DRIVER_MODULE_TAG[] = "wlan "; +static const char DRIVER_MODULE_PATH[] = "/system/lib/modules/wlan.ko"; +static const char FIRMWARE_LOADER[] = "wlan_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 SUPP_CONFIG_TEMPLATE[]= "/system/etc/wifi/wpa_supplicant.conf"; +static const char SUPP_CONFIG_FILE[] = "/data/misc/wifi/wpa_supplicant.conf"; +static const char MODULE_FILE[] = "/proc/modules"; + +static int insmod(const char *filename) +{ + void *module; + unsigned int size; + int ret; + + module = load_file(filename, &size); + if (!module) + return -1; + + ret = init_module(module, size, ""); + + 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) + LOGD("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(iface, "sta") == 0) + return 0; + + if (ifc_init() < 0) + return -1; + + if (do_dhcp(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(); +} + +static int check_driver_loaded() { + char driver_status[PROPERTY_VALUE_MAX]; + FILE *proc; + char line[sizeof(DRIVER_MODULE_TAG)+10]; + + if (!property_get(DRIVER_PROP_NAME, driver_status, NULL) + || strcmp(driver_status, "ok") != 0) { + return 0; /* driver not loaded */ + } + /* + * 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) { + LOGW("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; +} + +int wifi_load_driver() +{ + char driver_status[PROPERTY_VALUE_MAX]; + int count = 100; /* wait at most 20 seconds for completion */ + + if (check_driver_loaded()) { + return 0; + } + + if (insmod(DRIVER_MODULE_PATH) < 0) + return -1; + + 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_PROP_NAME, "failed") == 0) + return -1; + } + usleep(200000); + } + property_set(DRIVER_PROP_NAME, "timeout"); + return -1; +} + +int wifi_unload_driver() +{ + int count = 20; /* wait at most 10 seconds for completion */ + + if (rmmod(DRIVER_MODULE_NAME) == 0) { + while (count-- > 0) { + if (!check_driver_loaded()) + break; + usleep(500000); + } + if (count) { + return 0; + } + return -1; + } else + return -1; +} + +int ensure_config_file_exists() +{ + char buf[2048]; + int srcfd, destfd; + int nread; + + if (access(SUPP_CONFIG_FILE, R_OK|W_OK) == 0) { + return 0; + } else if (errno != ENOENT) { + LOGE("Cannot access \"%s\": %s", SUPP_CONFIG_FILE, strerror(errno)); + return -1; + } + + srcfd = open(SUPP_CONFIG_TEMPLATE, O_RDONLY); + if (srcfd < 0) { + LOGE("Cannot open \"%s\": %s", SUPP_CONFIG_TEMPLATE, strerror(errno)); + return -1; + } + + destfd = open(SUPP_CONFIG_FILE, O_CREAT|O_WRONLY, 0660); + if (destfd < 0) { + close(srcfd); + LOGE("Cannot create \"%s\": %s", SUPP_CONFIG_FILE, strerror(errno)); + return -1; + } + + while ((nread = read(srcfd, buf, sizeof(buf))) != 0) { + if (nread < 0) { + LOGE("Error reading \"%s\": %s", SUPP_CONFIG_TEMPLATE, strerror(errno)); + close(srcfd); + close(destfd); + unlink(SUPP_CONFIG_FILE); + return -1; + } + write(destfd, buf, nread); + } + + close(destfd); + close(srcfd); + + if (chown(SUPP_CONFIG_FILE, AID_SYSTEM, AID_WIFI) < 0) { + LOGE("Error changing group ownership of %s to %d: %s", + SUPP_CONFIG_FILE, AID_WIFI, strerror(errno)); + unlink(SUPP_CONFIG_FILE); + return -1; + } + return 0; +} + +int wifi_start_supplicant() +{ + char supp_status[PROPERTY_VALUE_MAX] = {'\0'}; + int count = 200; /* wait at most 20 seconds for completion */ +#ifdef HAVE_LIBC_SYSTEM_PROPERTIES + const prop_info *pi; + unsigned serial = 0; +#endif + + /* Check whether already running */ + if (property_get(SUPP_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() < 0) { + LOGE("Wi-Fi will not be enabled"); + return -1; + } + + /* Clear out any stale socket files that might be left over. */ + wpa_ctrl_cleanup(); + +#ifdef HAVE_LIBC_SYSTEM_PROPERTIES + /* + * 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(SUPP_PROP_NAME); + if (pi != NULL) { + serial = pi->serial; + } +#endif + property_set("ctl.start", SUPPLICANT_NAME); + sched_yield(); + + while (count-- > 0) { + #ifdef HAVE_LIBC_SYSTEM_PROPERTIES + if (pi == NULL) { + pi = __system_property_find(SUPP_PROP_NAME); + } + if (pi != NULL) { + __system_property_read(pi, NULL, supp_status); + if (strcmp(supp_status, "running") == 0) { + return 0; + } else if (pi->serial != serial && + strcmp(supp_status, "stopped") == 0) { + return -1; + } + } +#else + if (property_get(SUPP_PROP_NAME, supp_status, NULL)) { + if (strcmp(supp_status, "running") == 0) + return 0; + } +#endif + usleep(100000); + } + return -1; +} + +int wifi_stop_supplicant() +{ + char supp_status[PROPERTY_VALUE_MAX] = {'\0'}; + int count = 50; /* wait at most 5 seconds for completion */ + + /* Check whether supplicant already stopped */ + if (property_get(SUPP_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(SUPP_PROP_NAME, supp_status, NULL)) { + if (strcmp(supp_status, "stopped") == 0) + return 0; + } + usleep(100000); + } + return -1; +} + +int wifi_connect_to_supplicant() +{ + char ifname[256]; + char supp_status[PROPERTY_VALUE_MAX] = {'\0'}; + + /* Make sure supplicant is running */ + if (!property_get(SUPP_PROP_NAME, supp_status, NULL) + || strcmp(supp_status, "running") != 0) { + LOGE("Supplicant not running, cannot connect"); + return -1; + } + + property_get("wifi.interface", iface, "sta"); + + if (access(IFACE_DIR, F_OK) == 0) { + snprintf(ifname, sizeof(ifname), "%s/%s", IFACE_DIR, iface); + } else { + strlcpy(ifname, iface, sizeof(ifname)); + } + + ctrl_conn = wpa_ctrl_open(ifname); + if (ctrl_conn == NULL) { + LOGE("Unable to open connection to supplicant on \"%s\": %s", + ifname, strerror(errno)); + return -1; + } + monitor_conn = wpa_ctrl_open(ifname); + 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; + } + return 0; +} + +int wifi_send_command(struct wpa_ctrl *ctrl, const char *cmd, char *reply, size_t *reply_len) +{ + int ret; + + if (ctrl_conn == NULL) { + LOGV("Not connected to wpa_supplicant - \"%s\" command dropped.\n", cmd); + return -1; + } + ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), reply, reply_len, NULL); + if (ret == -2) { + LOGD("'%s' command timed out.\n", cmd); + 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_wait_for_event(char *buf, size_t buflen) +{ + size_t nread = buflen - 1; + int fd; + fd_set rfds; + int result; + struct timeval tval; + struct timeval *tptr; + + if (monitor_conn == NULL) + return 0; + + result = wpa_ctrl_recv(monitor_conn, buf, &nread); + if (result < 0) { + LOGD("wpa_ctrl_recv failed: %s\n", strerror(errno)); + return -1; + } + buf[nread] = '\0'; + /* LOGD("wait_for_event: result=%d nread=%d string=\"%s\"\n", result, nread, buf); */ + /* Check for EOF on the socket */ + if (result == 0 && nread == 0) { + /* Fabricate an event to pass up */ + LOGD("Received EOF on supplicant socket\n"); + strncpy(buf, WPA_EVENT_TERMINATING " - signal 0 received", buflen-1); + buf[buflen-1] = '\0'; + return strlen(buf); + } + /* + * Events strings are in the format + * + * <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 (buf[0] == '<') { + char *match = strchr(buf, '>'); + if (match != NULL) { + nread -= (match+1-buf); + memmove(buf, match+1, nread+1); + } + } + return nread; +} + +void wifi_close_supplicant_connection() +{ + if (ctrl_conn != NULL) { + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; + } + if (monitor_conn != NULL) { + wpa_ctrl_close(monitor_conn); + monitor_conn = NULL; + } +} + +int wifi_command(const char *command, char *reply, size_t *reply_len) +{ + return wifi_send_command(ctrl_conn, command, reply, reply_len); +} |