| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Sysfs APIs for Google Pixel devices. | |
| * | |
| * Copyright 2022 Google LLC. | |
| */ | |
| #include <linux/device.h> | |
| #include <linux/fs.h> | |
| #include <linux/kernel.h> | |
| #include <linux/slab.h> | |
| #include "touch_apis.h" | |
| struct touch_apis_data *apis; | |
| static ssize_t fw_ver_show( | |
| struct device *dev, struct device_attribute *attr, char *buf) | |
| { | |
| char fw_ver[0x100]; | |
| int ret = 0; | |
| if (apis->get_fw_version != NULL) { | |
| ret = apis->get_fw_version(dev, fw_ver, 0x100); | |
| if (ret < 0) { | |
| ret = snprintf(buf, PAGE_SIZE, "error: %d\n", ret); | |
| } else { | |
| ret = snprintf(buf, PAGE_SIZE, "result: %s\n", fw_ver); | |
| } | |
| } else { | |
| ret = snprintf(buf, PAGE_SIZE, "error: not support\n"); | |
| } | |
| return ret; | |
| } | |
| static ssize_t help_show( | |
| struct device *dev, struct device_attribute *attr, char *buf) | |
| { | |
| char fw_ver[0x100]; | |
| int ret = 0; | |
| char *help_str = "APIs\n" | |
| " fw_ver\n" | |
| " help\n" | |
| " irq_enabled\n" | |
| " list_scan_mode\n" | |
| " ping\n" | |
| " reset\n" | |
| " scan_mode\n" | |
| " sensing_enabled\n" | |
| " wake_lock\n"; | |
| ret = snprintf(buf, PAGE_SIZE, help_str, fw_ver); | |
| return ret; | |
| } | |
| static ssize_t irq_enabled_show( | |
| struct device *dev, struct device_attribute *attr, char *buf) | |
| { | |
| int ret = 0; | |
| if (apis->get_irq_enabled != NULL && apis->set_irq_enabled != NULL) { | |
| ret = apis->get_irq_enabled(dev); | |
| if (ret < 0) { | |
| ret = snprintf(buf, PAGE_SIZE, "error: %d\n", ret); | |
| } else { | |
| ret = snprintf(buf, PAGE_SIZE, "result: %s\n", | |
| ret ? "enabled" : "disabled"); | |
| } | |
| } else { | |
| ret = snprintf(buf, PAGE_SIZE, "error: not support\n"); | |
| } | |
| return ret; | |
| } | |
| static ssize_t irq_enabled_store(struct device *dev, | |
| struct device_attribute *attr, const char *buf, size_t count) | |
| { | |
| bool enabled = false; | |
| if (buf == NULL || count <= 0) | |
| return -EINVAL; | |
| if (strncmp(buf, "1", 1) == 0) { | |
| enabled = true; | |
| } else if (strncmp(buf, "0", 1) == 0) { | |
| enabled = false; | |
| } else { | |
| return -EINVAL; | |
| } | |
| if (apis->set_irq_enabled != NULL) { | |
| apis->set_irq_enabled(dev, enabled); | |
| } | |
| return count; | |
| } | |
| static ssize_t list_scan_mode_show( | |
| struct device *dev, struct device_attribute *attr, char *buf) | |
| { | |
| int ret = 0; | |
| enum scan_mode mode = SCAN_MODE_AUTO; | |
| int len = 0; | |
| static const char *mode_name[SCAN_MODE_MAX] = { | |
| "auto mode", | |
| "normal active mode", | |
| "normal idle mode", | |
| "low power active mode", | |
| "low power idle mode", | |
| }; | |
| if (apis->is_scan_mode_supported != NULL) { | |
| len = snprintf(buf, PAGE_SIZE, "result:\n"); | |
| for (mode = SCAN_MODE_AUTO; mode < SCAN_MODE_MAX; mode++) { | |
| ret = apis->is_scan_mode_supported(dev, mode); | |
| if (ret) { | |
| len += snprintf(buf + len, PAGE_SIZE - len, | |
| "%d: %s\n", mode, mode_name[mode]); | |
| } | |
| } | |
| ret = len; | |
| } else { | |
| ret = snprintf(buf, PAGE_SIZE, "error: not support\n"); | |
| } | |
| return ret; | |
| } | |
| #if IS_ENABLED(CONFIG_TOUCHSCREEN_MOTION_FILTER) | |
| static ssize_t mf_mode_show( | |
| struct device *dev, struct device_attribute *attr, char *buf) | |
| { | |
| return snprintf(buf, PAGE_SIZE, "result: %d\n", apis->mf_mode); | |
| } | |
| static ssize_t mf_mode_store(struct device *dev, struct device_attribute *attr, | |
| const char *buf, size_t count) | |
| { | |
| enum touch_mf_mode mode = 0; | |
| int ret = 0; | |
| if (buf == NULL || count < 0) | |
| return -EINVAL; | |
| if (kstrtoint(buf, 10, (int *)&mode)) { | |
| return -EINVAL; | |
| } | |
| if (mode < TOUCH_MF_MODE_UNFILTERED || | |
| mode > TOUCH_MF_MODE_AUTO_REPORT) { | |
| return -EINVAL; | |
| } | |
| ret = touch_mf_set_mode(apis->tmf, mode); | |
| if (ret != 0) { | |
| return ret; | |
| } | |
| apis->mf_mode = mode; | |
| return count; | |
| } | |
| #endif | |
| static ssize_t ping_show( | |
| struct device *dev, struct device_attribute *attr, char *buf) | |
| { | |
| int ret = 0; | |
| if (apis->ping != NULL) { | |
| ret = apis->ping(dev); | |
| ret = snprintf(buf, PAGE_SIZE, "result: %s\n", | |
| ret == 0 ? "ack" : "non ack"); | |
| } else { | |
| ret = snprintf(buf, PAGE_SIZE, "error: not support\n"); | |
| } | |
| return ret; | |
| } | |
| static ssize_t reset_show( | |
| struct device *dev, struct device_attribute *attr, char *buf) | |
| { | |
| int ret = 0; | |
| if (apis->reset_result > RESET_RESULT_NOT_SUPPORT) { | |
| ret = snprintf(buf, PAGE_SIZE, "result: %s\n", | |
| apis->reset_result == RESET_RESULT_SUCCESS ? "success" | |
| : apis->reset_result == RESET_RESULT_FAIL | |
| ? "fail" | |
| : "haven't reset"); | |
| } else { | |
| ret = snprintf(buf, PAGE_SIZE, "error: not support\n"); | |
| } | |
| return ret; | |
| } | |
| static ssize_t reset_store(struct device *dev, struct device_attribute *attr, | |
| const char *buf, size_t count) | |
| { | |
| int ret = 0; | |
| if (buf == NULL || count <= 0) | |
| return -EINVAL; | |
| if (strncmp(buf, "1", 1) == 0) { | |
| if (apis->hardware_reset != NULL) { | |
| ret = apis->hardware_reset(dev); | |
| apis->reset_result = ret == 0 ? RESET_RESULT_SUCCESS | |
| : RESET_RESULT_FAIL; | |
| } else { | |
| apis->reset_result = RESET_RESULT_NOT_SUPPORT; | |
| } | |
| } else if (strncmp(buf, "2", 0) == 0) { | |
| if (apis->software_reset != NULL) { | |
| ret = apis->software_reset(dev); | |
| apis->reset_result = ret == 0 ? RESET_RESULT_SUCCESS | |
| : RESET_RESULT_FAIL; | |
| } else { | |
| apis->reset_result = RESET_RESULT_NOT_SUPPORT; | |
| } | |
| } else { | |
| apis->reset_result = RESET_RESULT_NOT_SUPPORT; | |
| return -EINVAL; | |
| } | |
| return count; | |
| } | |
| static ssize_t scan_mode_show( | |
| struct device *dev, struct device_attribute *attr, char *buf) | |
| { | |
| return snprintf(buf, PAGE_SIZE, "result: %d\n", apis->scan_mode); | |
| } | |
| static ssize_t scan_mode_store(struct device *dev, | |
| struct device_attribute *attr, const char *buf, size_t count) | |
| { | |
| enum scan_mode mode = 0; | |
| int ret = 0; | |
| if (buf == NULL || count < 0) | |
| return -EINVAL; | |
| if (kstrtoint(buf, 10, (int *)&mode)) { | |
| return -EINVAL; | |
| } | |
| if (apis->is_scan_mode_supported == NULL || | |
| !apis->is_scan_mode_supported(dev, mode)) { | |
| return -EINVAL; | |
| } | |
| if (apis->set_scan_mode != NULL) { | |
| ret = apis->set_scan_mode(dev, mode); | |
| if (ret != 0) { | |
| return ret; | |
| } | |
| apis->scan_mode = mode; | |
| } | |
| return count; | |
| } | |
| static ssize_t sensing_enabled_show( | |
| struct device *dev, struct device_attribute *attr, char *buf) | |
| { | |
| int ret = 0; | |
| if (apis->ping != NULL) { | |
| ret = apis->ping(dev); | |
| ret = snprintf(buf, PAGE_SIZE, "result: %s\n", | |
| ret == 0 ? "enabled" : "disabled"); | |
| } else { | |
| ret = snprintf(buf, PAGE_SIZE, "error: not support\n"); | |
| } | |
| return ret; | |
| } | |
| static ssize_t sensing_enabled_store(struct device *dev, | |
| struct device_attribute *attr, const char *buf, size_t count) | |
| { | |
| bool enabled = false; | |
| if (buf == NULL || count <= 0) | |
| return -EINVAL; | |
| if (strncmp(buf, "1", 1) == 0) { | |
| enabled = true; | |
| } else if (strncmp(buf, "0", 1) == 0) { | |
| enabled = false; | |
| } else { | |
| return -EINVAL; | |
| } | |
| if (apis->set_sensing_enabled != NULL) { | |
| apis->set_sensing_enabled(dev, enabled); | |
| } | |
| return count; | |
| } | |
| #if IS_ENABLED(CONFIG_GOOG_TOUCH_INTERFACE) && IS_ENABLED(CONFIG_GTI_PM) | |
| static ssize_t wake_lock_show( | |
| struct device *dev, struct device_attribute *attr, char *buf) | |
| { | |
| int ret = 0; | |
| bool locked = false; | |
| if (apis->get_wake_lock_state != NULL) { | |
| locked = apis->get_wake_lock_state( | |
| dev, GTI_PM_WAKELOCK_TYPE_FORCE_ACTIVE); | |
| ret = snprintf(buf, PAGE_SIZE, "result: %s\n", | |
| locked ? "locked" : "unlocked"); | |
| } else { | |
| ret = snprintf(buf, PAGE_SIZE, "error: not support\n"); | |
| } | |
| return ret; | |
| } | |
| static ssize_t wake_lock_store(struct device *dev, | |
| struct device_attribute *attr, const char *buf, size_t count) | |
| { | |
| bool locked = false; | |
| int ret = 0; | |
| if (buf == NULL || count <= 0) | |
| return -EINVAL; | |
| if (strncmp(buf, "1", 1) == 0) { | |
| locked = true; | |
| } else if (strncmp(buf, "0", 1) == 0) { | |
| locked = false; | |
| } else { | |
| return -EINVAL; | |
| } | |
| if (apis->set_wake_lock_state != NULL) { | |
| ret = apis->set_wake_lock_state( | |
| dev, GTI_PM_WAKELOCK_TYPE_FORCE_ACTIVE, locked); | |
| if (ret < 0) { | |
| return ret; | |
| } | |
| } | |
| return count; | |
| } | |
| #endif | |
| static DEVICE_ATTR_RO(fw_ver); | |
| static DEVICE_ATTR_RO(help); | |
| static DEVICE_ATTR_RW(irq_enabled); | |
| static DEVICE_ATTR_RO(list_scan_mode); | |
| #if IS_ENABLED(CONFIG_TOUCHSCREEN_MOTION_FILTER) | |
| static DEVICE_ATTR_RW(mf_mode); | |
| #endif | |
| static DEVICE_ATTR_RO(ping); | |
| static DEVICE_ATTR_RW(reset); | |
| static DEVICE_ATTR_RW(scan_mode); | |
| static DEVICE_ATTR_RW(sensing_enabled); | |
| #if IS_ENABLED(CONFIG_GOOG_TOUCH_INTERFACE) && IS_ENABLED(CONFIG_GTI_PM) | |
| static DEVICE_ATTR_RW(wake_lock); | |
| #endif | |
| static struct attribute *sysfs_attrs[] = { | |
| &dev_attr_fw_ver.attr, | |
| &dev_attr_help.attr, | |
| &dev_attr_irq_enabled.attr, | |
| &dev_attr_list_scan_mode.attr, | |
| #if IS_ENABLED(CONFIG_TOUCHSCREEN_MOTION_FILTER) | |
| &dev_attr_mf_mode.attr, | |
| #endif | |
| &dev_attr_ping.attr, | |
| &dev_attr_reset.attr, | |
| &dev_attr_scan_mode.attr, | |
| &dev_attr_sensing_enabled.attr, | |
| #if IS_ENABLED(CONFIG_GOOG_TOUCH_INTERFACE) && IS_ENABLED(CONFIG_GTI_PM) | |
| &dev_attr_wake_lock.attr, | |
| #endif | |
| NULL, | |
| }; | |
| static const struct attribute_group sysfs_group = { | |
| .attrs = sysfs_attrs, | |
| }; | |
| int touch_apis_init(struct device *dev, struct touch_apis_data *data) | |
| { | |
| int ret = 0; | |
| apis = data; | |
| apis->scan_mode = SCAN_MODE_AUTO; | |
| apis->reset_result = RESET_RESULT_NOT_READY; | |
| ret = sysfs_create_group(&dev->kobj, &sysfs_group); | |
| if (ret) { | |
| dev_err(dev, "failed create core sysfs group"); | |
| return ret; | |
| } | |
| return ret; | |
| } | |
| void touch_apis_deinit(struct device *dev) | |
| { | |
| sysfs_remove_group(&dev->kobj, &sysfs_group); | |
| if (apis != NULL) { | |
| apis = NULL; | |
| } | |
| } |