2 * Copyright 2008, The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
22 #include <sys/socket.h>
26 #include "hardware_legacy/wifi.h"
27 #ifdef LIBWPA_CLIENT_EXISTS
28 #include "libwpa_client/wpa_ctrl.h"
31 #define LOG_TAG "WifiHW"
32 #include "cutils/log.h"
33 #include "cutils/memory.h"
34 #include "cutils/misc.h"
35 #include "cutils/properties.h"
36 #include "private/android_filesystem_config.h"
38 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
39 #include <sys/_system_properties.h>
42 extern int ifc_init();
43 extern void ifc_close();
44 extern char *dhcp_lasterror();
45 extern void get_dhcp_info();
46 extern int init_module(void *, unsigned long, const char *);
47 extern int delete_module(const char *, unsigned int);
48 void wifi_close_sockets();
50 #ifndef LIBWPA_CLIENT_EXISTS
51 #define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING "
53 void wpa_ctrl_cleanup(void) {}
54 struct wpa_ctrl *wpa_ctrl_open(const char *ctrl_path) { return NULL; }
55 void wpa_ctrl_close(struct wpa_ctrl *ctrl) {}
56 int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
57 char *reply, size_t *reply_len, void (*msg_cb)(char *msg, size_t len))
59 int wpa_ctrl_attach(struct wpa_ctrl *ctrl) { return 0; }
60 int wpa_ctrl_detach(struct wpa_ctrl *ctrl) { return 0; }
61 int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len)
63 int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl) { return 0; }
66 static struct wpa_ctrl *ctrl_conn;
67 static struct wpa_ctrl *monitor_conn;
69 /* socket pair used to exit from a blocking read */
70 static int exit_sockets[2];
72 static char primary_iface[PROPERTY_VALUE_MAX];
73 // TODO: use new ANDROID_SOCKET mechanism, once support for multiple
76 #ifndef WIFI_DRIVER_MODULE_ARG
77 #define WIFI_DRIVER_MODULE_ARG ""
79 #ifndef WIFI_FIRMWARE_LOADER
80 #define WIFI_FIRMWARE_LOADER ""
82 #define WIFI_TEST_INTERFACE "sta"
84 #ifndef WIFI_DRIVER_FW_PATH_STA
85 #define WIFI_DRIVER_FW_PATH_STA NULL
87 #ifndef WIFI_DRIVER_FW_PATH_AP
88 #define WIFI_DRIVER_FW_PATH_AP NULL
90 #ifndef WIFI_DRIVER_FW_PATH_P2P
91 #define WIFI_DRIVER_FW_PATH_P2P NULL
94 #ifndef WIFI_DRIVER_FW_PATH_PARAM
95 #define WIFI_DRIVER_FW_PATH_PARAM "/sys/module/wlan/parameters/fwpath"
98 #define WIFI_DRIVER_LOADER_DELAY 1000000
100 static const char IFACE_DIR[] = "/data/system/wpa_supplicant";
101 #ifdef WIFI_DRIVER_MODULE_PATH
102 static const char DRIVER_MODULE_NAME[] = WIFI_DRIVER_MODULE_NAME;
103 static const char DRIVER_MODULE_TAG[] = WIFI_DRIVER_MODULE_NAME " ";
104 static const char DRIVER_MODULE_PATH[] = WIFI_DRIVER_MODULE_PATH;
105 static const char DRIVER_MODULE_ARG[] = WIFI_DRIVER_MODULE_ARG;
107 static const char FIRMWARE_LOADER[] = WIFI_FIRMWARE_LOADER;
108 static const char DRIVER_PROP_NAME[] = "wlan.driver.status";
109 static const char SUPPLICANT_NAME[] = "wpa_supplicant";
110 static const char SUPP_PROP_NAME[] = "init.svc.wpa_supplicant";
111 static const char P2P_SUPPLICANT_NAME[] = "p2p_supplicant";
112 static const char P2P_PROP_NAME[] = "init.svc.p2p_supplicant";
113 static const char SUPP_CONFIG_TEMPLATE[]= "/system/etc/wifi/wpa_supplicant.conf";
114 static const char SUPP_CONFIG_FILE[] = "/data/misc/wifi/wpa_supplicant.conf";
115 static const char P2P_CONFIG_FILE[] = "/data/misc/wifi/p2p_supplicant.conf";
116 static const char CONTROL_IFACE_PATH[] = "/data/misc/wifi/sockets";
117 static const char MODULE_FILE[] = "/proc/modules";
119 static const char IFNAME[] = "IFNAME=";
120 #define IFNAMELEN (sizeof(IFNAME) - 1)
121 static const char WPA_EVENT_IGNORE[] = "CTRL-EVENT-IGNORE ";
123 static const char SUPP_ENTROPY_FILE[] = WIFI_ENTROPY_FILE;
124 static unsigned char dummy_key[21] = { 0x02, 0x11, 0xbe, 0x33, 0x43, 0x35,
125 0x68, 0x47, 0x84, 0x99, 0xa9, 0x2b,
126 0x1c, 0xd3, 0xee, 0xff, 0xf1, 0xe2,
129 /* Is either SUPPLICANT_NAME or P2P_SUPPLICANT_NAME */
130 static char supplicant_name[PROPERTY_VALUE_MAX];
131 /* Is either SUPP_PROP_NAME or P2P_PROP_NAME */
132 static char supplicant_prop_name[PROPERTY_KEY_MAX];
134 static int insmod(const char *filename, const char *args)
140 module = load_file(filename, &size);
144 ret = init_module(module, size, args);
151 static int rmmod(const char *modname)
156 while (maxtry-- > 0) {
157 ret = delete_module(modname, O_NONBLOCK | O_EXCL);
158 if (ret < 0 && errno == EAGAIN)
165 ALOGD("Unable to unload driver module \"%s\": %s\n",
166 modname, strerror(errno));
170 int do_dhcp_request(int *ipaddr, int *gateway, int *mask,
171 int *dns1, int *dns2, int *server, int *lease) {
172 /* For test driver, always report success */
173 if (strcmp(primary_iface, WIFI_TEST_INTERFACE) == 0)
179 if (do_dhcp(primary_iface) < 0) {
184 get_dhcp_info(ipaddr, gateway, mask, dns1, dns2, server, lease);
188 const char *get_dhcp_error_string() {
189 return dhcp_lasterror();
192 int is_wifi_driver_loaded() {
193 char driver_status[PROPERTY_VALUE_MAX];
194 #ifdef WIFI_DRIVER_MODULE_PATH
196 char line[sizeof(DRIVER_MODULE_TAG)+10];
199 if (!property_get(DRIVER_PROP_NAME, driver_status, NULL)
200 || strcmp(driver_status, "ok") != 0) {
201 return 0; /* driver not loaded */
203 #ifdef WIFI_DRIVER_MODULE_PATH
205 * If the property says the driver is loaded, check to
206 * make sure that the property setting isn't just left
207 * over from a previous manual shutdown or a runtime
210 if ((proc = fopen(MODULE_FILE, "r")) == NULL) {
211 ALOGW("Could not open %s: %s", MODULE_FILE, strerror(errno));
212 property_set(DRIVER_PROP_NAME, "unloaded");
215 while ((fgets(line, sizeof(line), proc)) != NULL) {
216 if (strncmp(line, DRIVER_MODULE_TAG, strlen(DRIVER_MODULE_TAG)) == 0) {
222 property_set(DRIVER_PROP_NAME, "unloaded");
229 int wifi_load_driver()
231 #ifdef WIFI_DRIVER_MODULE_PATH
232 char driver_status[PROPERTY_VALUE_MAX];
233 int count = 100; /* wait at most 20 seconds for completion */
235 if (is_wifi_driver_loaded()) {
239 if (insmod(DRIVER_MODULE_PATH, DRIVER_MODULE_ARG) < 0)
242 if (strcmp(FIRMWARE_LOADER,"") == 0) {
243 /* usleep(WIFI_DRIVER_LOADER_DELAY); */
244 property_set(DRIVER_PROP_NAME, "ok");
247 property_set("ctl.start", FIRMWARE_LOADER);
250 while (count-- > 0) {
251 if (property_get(DRIVER_PROP_NAME, driver_status, NULL)) {
252 if (strcmp(driver_status, "ok") == 0)
254 else if (strcmp(DRIVER_PROP_NAME, "failed") == 0) {
255 wifi_unload_driver();
261 property_set(DRIVER_PROP_NAME, "timeout");
262 wifi_unload_driver();
265 property_set(DRIVER_PROP_NAME, "ok");
270 int wifi_unload_driver()
272 usleep(200000); /* allow to finish interface down */
273 #ifdef WIFI_DRIVER_MODULE_PATH
274 if (rmmod(DRIVER_MODULE_NAME) == 0) {
275 int count = 20; /* wait at most 10 seconds for completion */
276 while (count-- > 0) {
277 if (!is_wifi_driver_loaded())
281 usleep(500000); /* allow card removal */
289 property_set(DRIVER_PROP_NAME, "unloaded");
294 int ensure_entropy_file_exists()
299 ret = access(SUPP_ENTROPY_FILE, R_OK|W_OK);
300 if ((ret == 0) || (errno == EACCES)) {
302 (chmod(SUPP_ENTROPY_FILE, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) != 0)) {
303 ALOGE("Cannot set RW to \"%s\": %s", SUPP_ENTROPY_FILE, strerror(errno));
308 destfd = TEMP_FAILURE_RETRY(open(SUPP_ENTROPY_FILE, O_CREAT|O_RDWR, 0660));
310 ALOGE("Cannot create \"%s\": %s", SUPP_ENTROPY_FILE, strerror(errno));
314 if (TEMP_FAILURE_RETRY(write(destfd, dummy_key, sizeof(dummy_key))) != sizeof(dummy_key)) {
315 ALOGE("Error writing \"%s\": %s", SUPP_ENTROPY_FILE, strerror(errno));
321 /* chmod is needed because open() didn't set permisions properly */
322 if (chmod(SUPP_ENTROPY_FILE, 0660) < 0) {
323 ALOGE("Error changing permissions of %s to 0660: %s",
324 SUPP_ENTROPY_FILE, strerror(errno));
325 unlink(SUPP_ENTROPY_FILE);
329 if (chown(SUPP_ENTROPY_FILE, AID_SYSTEM, AID_WIFI) < 0) {
330 ALOGE("Error changing group ownership of %s to %d: %s",
331 SUPP_ENTROPY_FILE, AID_WIFI, strerror(errno));
332 unlink(SUPP_ENTROPY_FILE);
338 int ensure_config_file_exists(const char *config_file)
346 ret = access(config_file, R_OK|W_OK);
347 if ((ret == 0) || (errno == EACCES)) {
349 (chmod(config_file, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) != 0)) {
350 ALOGE("Cannot set RW to \"%s\": %s", config_file, strerror(errno));
354 } else if (errno != ENOENT) {
355 ALOGE("Cannot access \"%s\": %s", config_file, strerror(errno));
359 srcfd = TEMP_FAILURE_RETRY(open(SUPP_CONFIG_TEMPLATE, O_RDONLY));
361 ALOGE("Cannot open \"%s\": %s", SUPP_CONFIG_TEMPLATE, strerror(errno));
365 destfd = TEMP_FAILURE_RETRY(open(config_file, O_CREAT|O_RDWR, 0660));
368 ALOGE("Cannot create \"%s\": %s", config_file, strerror(errno));
372 while ((nread = TEMP_FAILURE_RETRY(read(srcfd, buf, sizeof(buf)))) != 0) {
374 ALOGE("Error reading \"%s\": %s", SUPP_CONFIG_TEMPLATE, strerror(errno));
380 TEMP_FAILURE_RETRY(write(destfd, buf, nread));
386 /* chmod is needed because open() didn't set permisions properly */
387 if (chmod(config_file, 0660) < 0) {
388 ALOGE("Error changing permissions of %s to 0660: %s",
389 config_file, strerror(errno));
394 if (chown(config_file, AID_SYSTEM, AID_WIFI) < 0) {
395 ALOGE("Error changing group ownership of %s to %d: %s",
396 config_file, AID_WIFI, strerror(errno));
403 int wifi_start_supplicant(int p2p_supported)
405 char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
406 int count = 200; /* wait at most 20 seconds for completion */
408 unsigned serial = 0, i;
411 strcpy(supplicant_name, P2P_SUPPLICANT_NAME);
412 strcpy(supplicant_prop_name, P2P_PROP_NAME);
414 /* Ensure p2p config file is created */
415 if (ensure_config_file_exists(P2P_CONFIG_FILE) < 0) {
416 ALOGE("Failed to create a p2p config file");
421 strcpy(supplicant_name, SUPPLICANT_NAME);
422 strcpy(supplicant_prop_name, SUPP_PROP_NAME);
425 /* Check whether already running */
426 if (property_get(supplicant_prop_name, supp_status, NULL)
427 && strcmp(supp_status, "running") == 0) {
431 /* Before starting the daemon, make sure its config file exists */
432 if (ensure_config_file_exists(SUPP_CONFIG_FILE) < 0) {
433 ALOGE("Wi-Fi will not be enabled");
437 if (ensure_entropy_file_exists() < 0) {
438 ALOGE("Wi-Fi entropy file was not created");
441 /* Clear out any stale socket files that might be left over. */
444 /* Reset sockets used for exiting from hung state */
445 exit_sockets[0] = exit_sockets[1] = -1;
448 * Get a reference to the status property, so we can distinguish
449 * the case where it goes stopped => running => stopped (i.e.,
450 * it start up, but fails right away) from the case in which
451 * it starts in the stopped state and never manages to start
454 pi = __system_property_find(supplicant_prop_name);
456 serial = __system_property_serial(pi);
458 property_get("wifi.interface", primary_iface, WIFI_TEST_INTERFACE);
460 property_set("ctl.start", supplicant_name);
463 while (count-- > 0) {
465 pi = __system_property_find(supplicant_prop_name);
469 * property serial updated means that init process is scheduled
470 * after we sched_yield, further property status checking is based on this */
471 if (__system_property_serial(pi) != serial) {
472 __system_property_read(pi, NULL, supp_status);
473 if (strcmp(supp_status, "running") == 0) {
475 } else if (strcmp(supp_status, "stopped") == 0) {
485 int wifi_stop_supplicant(int p2p_supported)
487 char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
488 int count = 50; /* wait at most 5 seconds for completion */
491 strcpy(supplicant_name, P2P_SUPPLICANT_NAME);
492 strcpy(supplicant_prop_name, P2P_PROP_NAME);
494 strcpy(supplicant_name, SUPPLICANT_NAME);
495 strcpy(supplicant_prop_name, SUPP_PROP_NAME);
498 /* Check whether supplicant already stopped */
499 if (property_get(supplicant_prop_name, supp_status, NULL)
500 && strcmp(supp_status, "stopped") == 0) {
504 property_set("ctl.stop", supplicant_name);
507 while (count-- > 0) {
508 if (property_get(supplicant_prop_name, supp_status, NULL)) {
509 if (strcmp(supp_status, "stopped") == 0)
514 ALOGE("Failed to stop supplicant");
518 int wifi_connect_on_socket_path(const char *path)
520 char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
522 /* Make sure supplicant is running */
523 if (!property_get(supplicant_prop_name, supp_status, NULL)
524 || strcmp(supp_status, "running") != 0) {
525 ALOGE("Supplicant not running, cannot connect");
529 ctrl_conn = wpa_ctrl_open(path);
530 if (ctrl_conn == NULL) {
531 ALOGE("Unable to open connection to supplicant on \"%s\": %s",
532 path, strerror(errno));
535 monitor_conn = wpa_ctrl_open(path);
536 if (monitor_conn == NULL) {
537 wpa_ctrl_close(ctrl_conn);
541 if (wpa_ctrl_attach(monitor_conn) != 0) {
542 wpa_ctrl_close(monitor_conn);
543 wpa_ctrl_close(ctrl_conn);
544 ctrl_conn = monitor_conn = NULL;
548 if (socketpair(AF_UNIX, SOCK_STREAM, 0, exit_sockets) == -1) {
549 wpa_ctrl_close(monitor_conn);
550 wpa_ctrl_close(ctrl_conn);
551 ctrl_conn = monitor_conn = NULL;
558 /* Establishes the control and monitor socket connections on the interface */
559 int wifi_connect_to_supplicant()
561 static char path[PATH_MAX];
563 if (access(IFACE_DIR, F_OK) == 0) {
564 snprintf(path, sizeof(path), "%s/%s", IFACE_DIR, primary_iface);
566 snprintf(path, sizeof(path), "@android:wpa_%s", primary_iface);
568 return wifi_connect_on_socket_path(path);
571 int wifi_send_command(const char *cmd, char *reply, size_t *reply_len)
574 if (ctrl_conn == NULL) {
575 ALOGV("Not connected to wpa_supplicant - \"%s\" command dropped.\n", cmd);
578 ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), reply, reply_len, NULL);
580 ALOGD("'%s' command timed out.\n", cmd);
581 /* unblocks the monitor receive socket for termination */
582 TEMP_FAILURE_RETRY(write(exit_sockets[0], "T", 1));
584 } else if (ret < 0 || strncmp(reply, "FAIL", 4) == 0) {
587 if (strncmp(cmd, "PING", 4) == 0) {
588 reply[*reply_len] = '\0';
593 int wifi_supplicant_connection_active()
595 char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
597 if (property_get(supplicant_prop_name, supp_status, NULL)) {
598 if (strcmp(supp_status, "stopped") == 0)
605 int wifi_ctrl_recv(char *reply, size_t *reply_len)
608 int ctrlfd = wpa_ctrl_get_fd(monitor_conn);
609 struct pollfd rfds[2];
611 memset(rfds, 0, 2 * sizeof(struct pollfd));
613 rfds[0].events |= POLLIN;
614 rfds[1].fd = exit_sockets[1];
615 rfds[1].events |= POLLIN;
617 res = TEMP_FAILURE_RETRY(poll(rfds, 2, 30000));
619 ALOGE("Error poll = %d", res);
621 } else if (res == 0) {
622 /* timed out, check if supplicant is active
625 res = wifi_supplicant_connection_active();
631 if (rfds[0].revents & POLLIN) {
632 return wpa_ctrl_recv(monitor_conn, reply, reply_len);
635 /* it is not rfds[0], then it must be rfts[1] (i.e. the exit socket)
636 * or we timed out. In either case, this call has failed ..
641 int wifi_wait_on_socket(char *buf, size_t buflen)
643 size_t nread = buflen - 1;
645 char *match, *match2;
647 if (monitor_conn == NULL) {
648 return snprintf(buf, buflen, "IFNAME=%s %s - connection closed",
649 primary_iface, WPA_EVENT_TERMINATING);
652 result = wifi_ctrl_recv(buf, &nread);
654 /* Terminate reception on exit socket */
656 return snprintf(buf, buflen, "IFNAME=%s %s - connection closed",
657 primary_iface, WPA_EVENT_TERMINATING);
661 ALOGD("wifi_ctrl_recv failed: %s\n", strerror(errno));
662 return snprintf(buf, buflen, "IFNAME=%s %s - recv error",
663 primary_iface, WPA_EVENT_TERMINATING);
666 /* Check for EOF on the socket */
667 if (result == 0 && nread == 0) {
668 /* Fabricate an event to pass up */
669 ALOGD("Received EOF on supplicant socket\n");
670 return snprintf(buf, buflen, "IFNAME=%s %s - signal 0 received",
671 primary_iface, WPA_EVENT_TERMINATING);
674 * Events strings are in the format
676 * IFNAME=iface <N>CTRL-EVENT-XXX
680 * where N is the message level in numerical form (0=VERBOSE, 1=DEBUG,
681 * etc.) and XXX is the event name. The level information is not useful
682 * to us, so strip it off.
685 if (strncmp(buf, IFNAME, IFNAMELEN) == 0) {
686 match = strchr(buf, ' ');
688 if (match[1] == '<') {
689 match2 = strchr(match + 2, '>');
690 if (match2 != NULL) {
691 nread -= (match2 - match);
692 memmove(match + 1, match2 + 1, nread - (match - buf) + 1);
696 return snprintf(buf, buflen, "%s", WPA_EVENT_IGNORE);
698 } else if (buf[0] == '<') {
699 match = strchr(buf, '>');
701 nread -= (match + 1 - buf);
702 memmove(buf, match + 1, nread + 1);
703 ALOGV("supplicant generated event without interface - %s\n", buf);
706 /* let the event go as is! */
707 ALOGW("supplicant generated event without interface and without message level - %s\n", buf);
713 int wifi_wait_for_event(char *buf, size_t buflen)
715 return wifi_wait_on_socket(buf, buflen);
718 void wifi_close_sockets()
720 if (ctrl_conn != NULL) {
721 wpa_ctrl_close(ctrl_conn);
725 if (monitor_conn != NULL) {
726 wpa_ctrl_close(monitor_conn);
730 if (exit_sockets[0] >= 0) {
731 close(exit_sockets[0]);
732 exit_sockets[0] = -1;
735 if (exit_sockets[1] >= 0) {
736 close(exit_sockets[1]);
737 exit_sockets[1] = -1;
741 void wifi_close_supplicant_connection()
743 char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
744 int count = 50; /* wait at most 5 seconds to ensure init has stopped stupplicant */
746 wifi_close_sockets();
748 while (count-- > 0) {
749 if (property_get(supplicant_prop_name, supp_status, NULL)) {
750 if (strcmp(supp_status, "stopped") == 0)
757 int wifi_command(const char *command, char *reply, size_t *reply_len)
759 return wifi_send_command(command, reply, reply_len);
762 const char *wifi_get_fw_path(int fw_type)
765 case WIFI_GET_FW_PATH_STA:
766 return WIFI_DRIVER_FW_PATH_STA;
767 case WIFI_GET_FW_PATH_AP:
768 return WIFI_DRIVER_FW_PATH_AP;
769 case WIFI_GET_FW_PATH_P2P:
770 return WIFI_DRIVER_FW_PATH_P2P;
775 int wifi_change_fw_path(const char *fwpath)
783 fd = TEMP_FAILURE_RETRY(open(WIFI_DRIVER_FW_PATH_PARAM, O_WRONLY));
785 ALOGE("Failed to open wlan fw path param (%s)", strerror(errno));
788 len = strlen(fwpath) + 1;
789 if (TEMP_FAILURE_RETRY(write(fd, fwpath, len)) != len) {
790 ALOGE("Failed to write wlan fw path param (%s)", strerror(errno));