summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Stewart <pstew@google.com>2015-09-05 08:20:53 -0700
committerLee Campbell <leecam@google.com>2015-11-11 11:08:59 -0800
commita71321b887224a53524a0b9400a6a1ece672faf3 (patch)
treefbbf7f8375e7ef3d90bb1eacbcd6a71cab14b0fb
parent16eea1e3a0b145f8d7eb7dedf1e728b3b86b9074 (diff)
downloadqcom-a71321b887224a53524a0b9400a6a1ece672faf3.tar.gz
Add WiFI driver HAL for qcom wcn3620
Adds startup and driver mode switching code, as well as selinux policy for the files it touches. When our partners start taking over the BSP, this should be replaced by something more comprehensive, e.g., setting calbiration files like the wcnss_service executable does. BUG: 23801071 Change-Id: I6402f1413837d8a8923b7d13cbce05ea8ccf18d1 TEST: Boot dragon board and ensure AP mode is running
-rw-r--r--peripheral/wifi/wcn3620/Android.mk28
-rw-r--r--peripheral/wifi/wcn3620/peripheral.mk9
-rw-r--r--peripheral/wifi/wcn3620/sepolicy/file_contexts3
-rw-r--r--peripheral/wifi/wcn3620/wifi_driver_hal_qcwcn.cpp211
4 files changed, 248 insertions, 3 deletions
diff --git a/peripheral/wifi/wcn3620/Android.mk b/peripheral/wifi/wcn3620/Android.mk
new file mode 100644
index 0000000..fd86093
--- /dev/null
+++ b/peripheral/wifi/wcn3620/Android.mk
@@ -0,0 +1,28 @@
+# Copyright (C) 2015 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_CFLAGS += -Wno-unused-parameter
+LOCAL_C_INCLUDES += device/generic/brillo/wifi_driver_hal/include
+LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_CPPFLAGS += -DLOG_TAG=\"hal_qcwcn\"
+LOCAL_SRC_FILES := wifi_driver_hal_qcwcn.cpp
+LOCAL_MODULE := wifi_driver.$(soc_name)
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/peripheral/wifi/wcn3620/peripheral.mk b/peripheral/wifi/wcn3620/peripheral.mk
index 4bd615d..cf37e4d 100644
--- a/peripheral/wifi/wcn3620/peripheral.mk
+++ b/peripheral/wifi/wcn3620/peripheral.mk
@@ -14,13 +14,13 @@
# limitations under the License.
#
-WLAN_FIRMWARE_SRC = hardware/bsp/qcom/peripheral/wifi/wcn3620
+WLAN_BSP_SRC = hardware/bsp/qcom/peripheral/wifi/wcn3620
FIRMWARE_DST = system/vendor/firmware
# Wireless peripheral firmware -- loaded by drivers/net/wireless/wcnss.
WCNSS_FILES := mdt b00 b01 b02 b04 b06 b09 b10 b11
WCNSS_COPY_FILES += \
- $(join $(patsubst %, $(WLAN_FIRMWARE_SRC)/wcnss.%, $(WCNSS_FILES)), $(patsubst %, :$(FIRMWARE_DST)/wcnss.%, $(WCNSS_FILES)))
+ $(join $(patsubst %, $(WLAN_BSP_SRC)/wcnss.%, $(WCNSS_FILES)), $(patsubst %, :$(FIRMWARE_DST)/wcnss.%, $(WCNSS_FILES)))
PRODUCT_COPY_FILES += $(WCNSS_COPY_FILES)
@@ -32,6 +32,9 @@ PRIMA_FILES = \
WCNSS_qcom_wlan_nv.bin \
WCNSS_wlan_dictionary.dat
PRIMA_COPY_FILES += \
- $(join $(patsubst %, $(WLAN_FIRMWARE_SRC)/%, $(PRIMA_FILES)), $(patsubst %, :$(WLAN_FIRMWARE_DST)/%, $(PRIMA_FILES)))
+ $(join $(patsubst %, $(WLAN_BSP_SRC)/%, $(PRIMA_FILES)), $(patsubst %, :$(WLAN_FIRMWARE_DST)/%, $(PRIMA_FILES)))
PRODUCT_COPY_FILES += $(PRIMA_COPY_FILES)
+WIFI_DRIVER_HAL_MODULE := wifi_driver.$(soc_name)
+
+BOARD_SEPOLICY_DIRS += $(WLAN_BSP_SRC)/sepolicy
diff --git a/peripheral/wifi/wcn3620/sepolicy/file_contexts b/peripheral/wifi/wcn3620/sepolicy/file_contexts
new file mode 100644
index 0000000..e5e94f5
--- /dev/null
+++ b/peripheral/wifi/wcn3620/sepolicy/file_contexts
@@ -0,0 +1,3 @@
+# These files will be accessed by the HAL during wifi_init setup.
+/dev/wcnss_wlan u:object_r:connectivity_device:s0
+/sys/devices/soc.0/a000000.qcom,wcnss-wlan/serial_number u:object_r:connectivity_sysfs_entry:s0
diff --git a/peripheral/wifi/wcn3620/wifi_driver_hal_qcwcn.cpp b/peripheral/wifi/wcn3620/wifi_driver_hal_qcwcn.cpp
new file mode 100644
index 0000000..537ee77
--- /dev/null
+++ b/peripheral/wifi/wcn3620/wifi_driver_hal_qcwcn.cpp
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2015 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 <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <hardware_brillo/wifi_driver_hal.h>
+
+#include <string>
+
+namespace {
+
+const char kWcnssWlanEnableFile[] = "/dev/wcnss_wlan";
+const char kDevicePathPrefix[] = "/sys/class/net/";
+const char kApDeviceName[] = "softap.0";
+const char kStationDeviceName[] = "wlan0";
+const char kDefaultSerialNumber[] = "00000000";
+const char kSerialNumberProperty[] = "ro.boot.serialno";
+const int kSerialNumberSize = sizeof(kDefaultSerialNumber) - 1;
+const char kApConnectionMode[] = "1";
+const char kStationConnectionMode[] = "0";
+const char kConnectionModeFile[] = "/sys/module/wlan/parameters/con_mode";
+
+// Writing the 32-bit serial number of the device to this file will cause
+// the driver to compute the correct MAC address for the wireless interface.
+// TODO(pstew): This device path might be different per reference board.
+const char kSerialNumberCtrl[] =
+ "/sys/devices/soc.0/a000000.qcom,wcnss-wlan/serial_number";
+
+
+static bool open_and_close_file(const std::string& filename) {
+ int fd = open(filename.c_str(), O_RDONLY);
+ if (fd < 0) {
+ ALOGE("Cannot open %s", filename.c_str());
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+static bool write_file(
+ const std::string& filename, const std::string& content) {
+ int fd = open(filename.c_str(), O_WRONLY);
+ if (fd < 0) {
+ ALOGE("Cannot open %s for writing", filename.c_str());
+ return false;
+ }
+ ssize_t write_count = content.size();
+ ssize_t actual_count = write(fd, content.c_str(), write_count);
+ close(fd);
+
+ if (actual_count != write_count) {
+ ALOGE("Expected to write %d bytes to %s but write returns %d",
+ write_count, filename.c_str(), actual_count);
+ return false;
+ }
+ return true;
+}
+
+static bool read_file(const std::string& filename, std::string* buffer,
+ size_t buffer_size) {
+ int fd = open(filename.c_str(), O_RDONLY);
+ if (fd < 0) {
+ ALOGE("Cannot open %s for reading", filename.c_str());
+ return false;
+ }
+ char buffer_data[buffer_size];
+ ssize_t count = read(fd, buffer_data, buffer_size);
+ close(fd);
+
+ if (count <= 0) {
+ ALOGE("Cannot read any data from %s", filename.c_str());
+ return false;
+ }
+
+ buffer->assign(buffer_data, count);
+ return true;
+}
+
+static bool interface_exists(const char* interface_name) {
+ std::string interface_path = std::string(kDevicePathPrefix) + interface_name;
+ return access(interface_path.c_str(), F_OK) == 0;
+}
+
+static wifi_driver_error wifi_driver_initialize_qcwcn() {
+ // Simply opening the file enables it.
+ if (!open_and_close_file(kWcnssWlanEnableFile))
+ return WIFI_ERROR_UNKNOWN;
+
+ // Although we may not be able to get or set the serial number below,
+ // we may be able to have a working device, so do not return an error
+ // for failures below.
+ std::string serial_number;
+ if (!read_file(kSerialNumberCtrl, &serial_number, kSerialNumberSize)) {
+ ALOGE("Could not check device serial number");
+ } else if (serial_number == kDefaultSerialNumber) {
+ char system_serial_number_buf[PROPERTY_VALUE_MAX];
+ property_get("ro.serialno", system_serial_number_buf, "");
+ std::string system_serial_number(system_serial_number_buf);
+ if (system_serial_number.empty()) {
+ ALOGE("Could not retrieve device serial number");
+ } else if (!write_file(kSerialNumberCtrl, system_serial_number)) {
+ ALOGE("Could not set device serial number");
+ }
+ }
+
+ return WIFI_SUCCESS;
+}
+
+static wifi_driver_error wifi_driver_set_mode_qcwcn(
+ wifi_driver_mode mode,
+ char* wifi_device_name,
+ size_t wifi_device_name_size) {
+ const char* connection_mode = nullptr;
+ const char* device_name = nullptr;
+ switch (mode) {
+ case WIFI_MODE_AP:
+ connection_mode = kApConnectionMode;
+ device_name = kApDeviceName;
+ break;
+
+ case WIFI_MODE_STATION:
+ connection_mode = kStationConnectionMode;
+ device_name = kStationDeviceName;
+ break;
+
+ default:
+ ALOGE("Unkonwn WiFi driver mode %d", mode);
+ return WIFI_ERROR_INVALID_ARGS;
+ }
+
+ strlcpy(wifi_device_name, device_name, wifi_device_name_size);
+ if (interface_exists(device_name))
+ return WIFI_SUCCESS;
+
+ // It's been observed that writing to the connection mode file toggles
+ // between modes. Therefore we try a few times.
+ for (int retry = 0; retry < 5; ++retry) {
+ if (!write_file(kConnectionModeFile, connection_mode))
+ return WIFI_ERROR_UNKNOWN;
+
+ // The write above is blocked until the operation completes so there
+ // is no need to sleep before checking the result.
+ if (interface_exists(device_name))
+ return WIFI_SUCCESS;
+ }
+
+ return WIFI_ERROR_TIMED_OUT;
+}
+
+static int close_wcnss_driver(struct hw_device_t* device) {
+ wifi_driver_device_t* dev = reinterpret_cast<wifi_driver_device_t*>(device);
+ if (dev)
+ free(dev);
+ return 0;
+}
+
+static int open_wcnss_driver(const struct hw_module_t* module, const char*,
+ struct hw_device_t** device) {
+ wifi_driver_device_t* dev = reinterpret_cast<wifi_driver_device_t*>(
+ calloc(1, sizeof(wifi_driver_device_t)));
+
+ dev->common.tag = HARDWARE_DEVICE_TAG;
+ dev->common.version = WIFI_DRIVER_DEVICE_API_VERSION_0_1;
+ // We're forced into this cast by the existing API. This pattern is
+ // common among users of the HAL.
+ dev->common.module = const_cast<hw_module_t*>(module);
+ dev->common.close = close_wcnss_driver;
+ dev->wifi_driver_initialize = wifi_driver_initialize_qcwcn;
+ dev->wifi_driver_set_mode = wifi_driver_set_mode_qcwcn;
+
+ *device = &dev->common;
+
+ return 0;
+}
+
+static struct hw_module_methods_t wcnss_driver_module_methods = {
+ open: open_wcnss_driver
+};
+
+} // namespace {}
+
+hw_module_t HAL_MODULE_INFO_SYM = {
+ tag: HARDWARE_MODULE_TAG,
+ version_major: 1,
+ version_minor: 0,
+ id: WIFI_DRIVER_HARDWARE_MODULE_ID,
+ name: "WCNSS / PRIMA module",
+ author: "Prima",
+ methods: &wcnss_driver_module_methods,
+ dso: NULL,
+ reserved: {0},
+};