diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-01-15 16:12:14 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-01-15 16:12:14 -0800 |
commit | 5ce01674d44addab570009ceddeaa08d64534092 (patch) | |
tree | 7efa8951d39fd254c9a0eba20180548670228ae5 /wifi/wifi.c | |
download | hardware_libhardware_legacy-5ce01674d44addab570009ceddeaa08d64534092.zip hardware_libhardware_legacy-5ce01674d44addab570009ceddeaa08d64534092.tar.gz hardware_libhardware_legacy-5ce01674d44addab570009ceddeaa08d64534092.tar.bz2 |
auto import from //branches/cupcake/...@126645
Diffstat (limited to 'wifi/wifi.c')
-rw-r--r-- | wifi/wifi.c | 413 |
1 files changed, 413 insertions, 0 deletions
diff --git a/wifi/wifi.c b/wifi/wifi.c new file mode 100644 index 0000000..91ff4cd --- /dev/null +++ b/wifi/wifi.c @@ -0,0 +1,413 @@ +/* + * 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" + +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; +} + +static int control_supplicant(int startIt) +{ + char supp_status[PROPERTY_VALUE_MAX] = {'\0'}; + const char *ctrl_prop = (startIt ? "ctl.start" : "ctl.stop"); + const char *desired_status = (startIt ? "running" : "stopped"); + int count = 200; /* wait at most 20 seconds for completion */ + + if (property_get(SUPP_PROP_NAME, supp_status, NULL) + && strcmp(supp_status, desired_status) == 0) { + return 0; /* supplicant already running */ + } + property_set(ctrl_prop, SUPPLICANT_NAME); + sched_yield(); + + while (count-- > 0) { + if (property_get(SUPP_PROP_NAME, supp_status, NULL)) { + if (strcmp(supp_status, desired_status) == 0) + return 0; + } + usleep(100000); + } + 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() +{ + /* 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; + } + return control_supplicant(1); +} + +int wifi_stop_supplicant() +{ + return control_supplicant(0); +} + +int wifi_connect_to_supplicant() +{ + char ifname[256]; + static int cleaned_up = 0; + + 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) { + LOGD("Unable to open connection to supplicant on \"%s\": %s", + ifname, strerror(errno)); + /* + * errno == ENOENT means the supplicant daemon isn't + * running. Take this opportunity to clear out any + * stale socket files that might be left over. Note + * there's a possible race with the command line client + * trying to connect to the daemon, but it would require + * that the supplicant be started and the command line + * client connect to it during the window between the + * error check and the removal of the files. And in + * any event, the remedy is that the user would simply + * have to run the command line program again. + */ + if (!cleaned_up && (errno == ENOENT || errno == EADDRINUSE)) { + cleaned_up = 1; /* do this just once */ + wpa_ctrl_cleanup(); + } + 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); +} |