From 31f6f73a691ff49fc3aeba062f8561e6c981baff Mon Sep 17 00:00:00 2001 From: Gatis Paeglis Date: Fri, 28 Mar 2014 13:32:05 +0100 Subject: Port QtWifi to eLinux Current limitations: - Wifi adapter must be connected before staring the device (and ethernet cable should not be connected at the same time). "allow-hotplug wlan0" in /etc/network/interfaces doesn't seem to help for detecting wifi adapter on-the-fly. Change-Id: I8be407e9e042fc86136efd3e220680fb2ce64bd6 Reviewed-by: Eirik Aavitsland --- src/imports/wifi/qwifi_elinux.cpp | 264 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 src/imports/wifi/qwifi_elinux.cpp (limited to 'src/imports/wifi/qwifi_elinux.cpp') diff --git a/src/imports/wifi/qwifi_elinux.cpp b/src/imports/wifi/qwifi_elinux.cpp new file mode 100644 index 0000000..551d887 --- /dev/null +++ b/src/imports/wifi/qwifi_elinux.cpp @@ -0,0 +1,264 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use the contact form at +** http://qt.digia.com/ +** +** This file is part of Qt Enterprise Embedded. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** the contact form at http://qt.digia.com/ +** +****************************************************************************/ +#include "qwifi_elinux.h" + +#include + +#include "wpa-supplicant/wpa_ctrl.h" + +#include +#include +#include + +static const char SUPP_CONFIG_FILE[] = "/etc/wpa_supplicant.conf"; +static const char IFACE_DIR[] = "/var/run/wpa_supplicant/"; +static const char WIFI[] = "wlan0"; + +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 const char IFNAME[] = "IFNAME="; +#define IFNAMELEN (sizeof(IFNAME) - 1) +static const char WPA_EVENT_IGNORE[] = "CTRL-EVENT-IGNORE "; + +int wifi_connect_on_socket_path(const char *path); +int wifi_ctrl_recv(char *reply, size_t *reply_len); +int wifi_wait_on_socket(char *buf, size_t buflen); +int wifi_send_command(const char *cmd, char *reply, size_t *reply_len); +void wifi_close_sockets(); + +int q_wifi_start_supplicant() +{ + // NOTE: supplicant started when bringing up the wifi interface in + // QWifiManager::connectToBackend() by: + // QProcess::execute(QStringLiteral("ifup") + + /* 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; + return 0; +} + +int q_wifi_stop_supplicant() +{ + // NOTE: supplicant stopped when bringing down the wifi + // interface in QWifiManager::disconnectFromBackend() by: + // QProcess::execute(QStringLiteral("ifdown") + return 0; +} + +int q_wifi_connect_to_supplicant(const char *ifname) +{ + Q_UNUSED(ifname); + static char path[4096]; + snprintf(path, sizeof(path), "%s/%s", IFACE_DIR, WIFI); + return wifi_connect_on_socket_path(path); +} + +int wifi_connect_on_socket_path(const char *path) +{ + // establishes the control and monitor socket connections on the interface + ctrl_conn = wpa_ctrl_open(path); + if (ctrl_conn == NULL) { + qWarning("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; +} + +int q_wifi_wait_for_event(const char *ifname, char *buf, size_t buflen) +{ + Q_UNUSED(ifname); + return wifi_wait_on_socket(buf, buflen); +} + +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, WPA_EVENT_TERMINATING " - connection closed"); + } + + result = wifi_ctrl_recv(buf, &nread); + + /* Terminate reception on exit socket */ + if (result == -2) { + return snprintf(buf, buflen, WPA_EVENT_TERMINATING " - connection closed"); + } + + if (result < 0) { + qWarning("wifi_ctrl_recv failed: %s\n", strerror(errno)); + return snprintf(buf, buflen, WPA_EVENT_TERMINATING " - recv error"); + } + buf[nread] = '\0'; + /* Check for EOF on the socket */ + if (result == 0 && nread == 0) { + /* Fabricate an event to pass up */ + qWarning("Received EOF on supplicant socket\n"); + return snprintf(buf, buflen, WPA_EVENT_TERMINATING " - signal 0 received"); + } + /* + * Events strings are in the format + * + * IFNAME=iface CTRL-EVENT-XXX + * or + * 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); + //qWarning("supplicant generated event without interface - %s\n", buf); + } + } else { + /* let the event go as is! */ + qWarning("supplicant generated event without interface and without message level - %s\n", buf); + } + + return nread; +} + +int wifi_ctrl_recv(char *reply, size_t *reply_len) +{ + int res = 0; + 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; + res = TEMP_FAILURE_RETRY(poll(rfds, 2, -1)); + if (res < 0) { + qWarning("Error poll = %d", res); + return res; + } + 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_send_command(const char *cmd, char *reply, size_t *reply_len) +{ + int ret; + if (ctrl_conn == NULL) { + qWarning("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) { + qWarning("'%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 q_wifi_command(const char *ifname, const char *command, char *reply, size_t *reply_len) +{ + Q_UNUSED(ifname); + return wifi_send_command(command, reply, reply_len); +} + +void q_wifi_close_supplicant_connection(const char *ifname) +{ + Q_UNUSED(ifname) + wifi_close_sockets(); +} + +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; + } +} -- cgit v1.2.3