泰山派双击亮屏支持

参考链接https://www.bilibili.com/read/cv39354143/?opus_fallback=1

代码分析

  1. 配置Setting中的显示
    TapToWakePreferenceController.java

    @Override
    public boolean isAvailable() {
        return mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_supportDoubleTapWake);
    }
    

    修改config_supportDoubleTapWake值,一般不建议在源文件上改,aosp有overload机制,可以在

    device/rockchip/rk356x/overlay/frameworks/base/core/res/res/values/config.xml(没有文件可以创建)中添加配置,这样就会覆盖掉aosp中的配置

    <!-- Whether device supports double tap to wake -->
    <bool name="config_supportDoubleTapWake">true</bool>
    

    重新编译framework.jar替换掉/system/framework/framework.jar后打开设置→显示→更多就可以看到双击亮屏的设置了

image

  1. 触摸驱动中增加双击相关代码

    踩坑,亮屏的时候不需要执行双击逻辑,最初考虑的是通过dev_pm_ops设置pm_runtime时间来更改双击的状态,无奈pm_runtime事件没有回调

    最终通过代码和Log分析到回到Power中的setMode通知hal层系统*MODE_DISPLAY_INACTIVE* 的状态,我们在display状态改变的时候去同步更新触摸驱动里面的状态值就可以实现息屏的时候双击亮屏,正常显示的状态不去处理双击逻辑,这样就避免了影响正常亮屏状态下的操作逻辑

    PowerManagerService.java中屏幕状态的改变

    @Override
    public void onDisplayStateChange(int state) {
        // This method is only needed to support legacy display blanking behavior
        // where the display's power state is coupled to suspend or to the power HAL.
        // The order of operations matters here.
        synchronized (mLock) {
            if (mDisplayState != state) {
                mDisplayState = state;
                setPowerModeInternal(MODE_DISPLAY_INACTIVE,
                        !Display.isActiveState(state));
            }
        }
    }
    
    private boolean setPowerModeInternal(int mode, boolean enabled) {
        // Maybe filter the event.
        return mNativeWrapper.nativeSetPowerMode(mode, enabled);
    }
    public boolean nativeSetPowerMode(int mode, boolean enabled) {
        return PowerManagerService.nativeSetPowerMode(mode, enabled);
    }
    private static native boolean nativeSetPowerMode(int mode, boolean enabled);
    

PowerManagerService.cpp native层代码

注册native接口

static const JNINativeMethod gPowerManagerServiceMethods[] = {
        /* name, signature, funcPtr */
        {"nativeInit", "()V", (void*)nativeInit},
        {"nativeAcquireSuspendBlocker", "(Ljava/lang/String;)V",
         (void*)nativeAcquireSuspendBlocker},
        {"nativeForceSuspend", "()Z", (void*)nativeForceSuspend},
        {"nativeReleaseSuspendBlocker", "(Ljava/lang/String;)V",
         (void*)nativeReleaseSuspendBlocker},
        {"nativeSetInteractive", "(Z)V", (void*)nativeSetInteractive},
        {"nativeSetAutoSuspend", "(Z)V", (void*)nativeSetAutoSuspend},
        {"nativeSendPowerHint", "(II)V", (void*)nativeSendPowerHint},
        {"nativeSetPowerBoost", "(II)V", (void*)nativeSetPowerBoost},
        {"nativeSetPowerMode", "(IZ)Z", (void*)nativeSetPowerMode},
        {"nativeSetFeature", "(II)V", (void*)nativeSetFeature},
};

可以看到最终实现是PowerManagerService.cpp中对应的实现nativeSetPowerMode方法

static jboolean nativeSetPowerMode(JNIEnv* /* env */, jclass /* clazz */, jint mode,
                                   jboolean enabled) {
    return setPowerMode(static_cast<Mode>(mode), enabled);
}

static bool setPowerMode(Mode mode, bool enabled) {
    std::unique_lock<std::mutex> lock(gPowerHalMutex);
    //找到Power HAL的实现,并且赋值给gPowerHalAidl_,并且支持了AIDL代码,并不支持HIDL版本的驱动
    if (connectPowerHalLocked() != HalVersion::AIDL) {
        ALOGV("Power HAL AIDL not available");
        return false;
    }
    //这里拿到的hal就是hardware/rockchip/power_aidl/Power.cpp实现
    sp<IPowerAidl> handle = gPowerHalAidl_;
    lock.unlock();
    return setPowerModeWithHandle(handle, mode, enabled);
}

static bool setPowerModeWithHandle(sp<IPowerAidl> handle, Mode mode, bool enabled) {
    // Android framework only sends mode upto DISPLAY_INACTIVE.
    // Need to increase the array if more mode supported.
    static std::array<std::atomic<HalSupport>, static_cast<int32_t>(Mode::DISPLAY_INACTIVE) + 1>
            modeSupportedArray = {HalSupport::UNKNOWN};
    size_t idx = static_cast<size_t>(mode);

    // Quick return if mode is not supported by HAL
    if (idx >= modeSupportedArray.size() || modeSupportedArray[idx] == HalSupport::OFF) {
        ALOGV("Skipped setPowerMode %s because HAL doesn't support it", toString(mode).c_str());
        return false;
    }

    if (modeSupportedArray[idx] == HalSupport::UNKNOWN) {
        bool isSupported = false;
        //判断是否支持mode
        handle->isModeSupported(mode, &isSupported);
        modeSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF;
        if (!isSupported) {
            ALOGV("Skipped setPowerMode %s because HAL doesn't support it", toString(mode).c_str());
            return false;
        }
    }
		//调用hal中的setMode函数
    auto ret = handle->setMode(mode, enabled);
    processPowerHalReturn(ret.isOk(), "setPowerMode");
    return ret.isOk();
}

结论

也就是想要实现setting中的双击亮屏配置,还要看hal层代码是否支持

PowerHAL的实现

static HalVersion connectPowerHalLocked() {
    static bool gPowerHalHidlExists = true;
    static bool gPowerHalAidlExists = true;
    //这里连个bool都是true 永远不会进入到if中
    if (!gPowerHalHidlExists && !gPowerHalAidlExists) {
        return HalVersion::NONE;
    }
    //先判断AIDL版本的代码
    if (gPowerHalAidlExists) {
        if (!gPowerHalAidl_) {
		        //获取aidl版本的hal代码
            gPowerHalAidl_ = waitForVintfService<IPowerAidl>();
        }
        if (gPowerHalAidl_) {
            ALOGV("Successfully connected to Power HAL AIDL service.");
            return HalVersion::AIDL;
        } else {
            gPowerHalAidlExists = false;
        }
    }
    //同理这是获hidl版本的 hal代码
    if (gPowerHalHidlExists && gPowerHalHidlV1_0_ == nullptr) {
        gPowerHalHidlV1_0_ = IPowerV1_0::getService();
        if (gPowerHalHidlV1_0_) {
            ALOGV("Successfully connected to Power HAL HIDL 1.0 service.");
            // Try cast to powerHAL HIDL V1_1
            gPowerHalHidlV1_1_ = IPowerV1_1::castFrom(gPowerHalHidlV1_0_);
            if (gPowerHalHidlV1_1_) {
                ALOGV("Successfully connected to Power HAL HIDL 1.1 service.");
            }
        } else {
            ALOGV("Couldn't load power HAL HIDL service");
            gPowerHalHidlExists = false;
            return HalVersion::NONE;
        }
    }
    if (gPowerHalHidlV1_1_) {
        return HalVersion::HIDL_1_1;
    } else if (gPowerHalHidlV1_0_) {
        return HalVersion::HIDL_1_0;
    }
    return HalVersion::NONE;
}

AIDL版本的代码

template<typename INTERFACE>
sp<INTERFACE> waitForVintfService(
        const String16& instance = String16("default")) {
    return waitForDeclaredService<INTERFACE>(
        INTERFACE::descriptor + String16("/") + instance);
}

template<typename INTERFACE>
sp<INTERFACE> waitForDeclaredService(const String16& name) {
    const sp<IServiceManager> sm = defaultServiceManager();
    if (!sm->isDeclared(name)) return nullptr;
    return interface_cast<INTERFACE>(sm->waitForService(name));
}

virtual sp<IBinder> waitForService(const String16& name) = 0;

这里的INTERFACE根据前面waitForVintfService<IPowerAidl>()这里可以确认是IPowerAidl,
PowerManagerService中有对IPowerAidl是IPower的别名,
using IPowerAidl = android::hardware::power::IPower;

于是我们知道最后是调用IServiceManager的waitForService代码来获取IPower的

通过代码查找可以找到IPower的实现位置为hardware/rockchip/power_aidl/

main.cpp中的注册Power代码为

int main() {
    ABinderProcess_setThreadPoolMaxThreadCount(0);
    //Power实现了IPower接口
    std::shared_ptr<Power> vib = ndk::SharedRefBase::make<Power>();

    const std::string instance = std::string() + Power::descriptor + "/default";
    binder_status_t status = AServiceManager_addService(vib->asBinder().get(), instance.c_str());
    CHECK(status == STATUS_OK);

    ABinderProcess_joinThreadPool();
    return EXIT_FAILURE;  // should not reach
}
binder_status_t AServiceManager_addService(AIBinder* binder, const char* instance) {
    if (binder == nullptr || instance == nullptr) {
        return STATUS_UNEXPECTED_NULL;
    }

    sp<IServiceManager> sm = defaultServiceManager();
    status_t status = sm->addService(String16(instance), binder->getBinder());
    return PruneStatusT(status);
}

通过IServiceManager注册了Power对象,前面的waitForVintfService<IPowerAidl>(); 函数会通过IServiceManager的waitForService来拿到Power对象,然后赋值给gPowerHalAidl_ 最后通过过setPowerModeWithHandle 函数来调用Power的handle->isModeSupported(mode, &isSupported); 以及auto ret = handle->setMode(mode, enabled);

Power.cpp HAL层代码分析

代码路径为hardware/rockchip/power_aidl/Power.cpp

isModeSupported

ndk::ScopedAStatus Power::isModeSupported(Mode type, bool* _aidl_return) {
    LOGI("Power isModeSupported: %d", static_cast<int32_t>(type));
    getSupportedPlatform();
    switch (type) {
        case Mode::DOUBLE_TAP_TO_WAKE:
            *_aidl_return = 0x4000 & _mode_support_int;
        break;
        //....省略后面的
    }
    return ndk::ScopedAStatus::ok();
}

返回的是0x4000 & _mode_support_int的结构,_mode_support_int是在哪里进行初始化的?

void Power::getSupportedPlatform() {
    char platform[PROPERTY_VALUE_MAX] = {0};
    //property_get("ro.board.platform", platform, "");
    if (_mode_support_int < 0) {
        //if (strncmp(platform, "rk3399", PROPERTY_VALUE_MAX) == 0) {
            _boost_support_int = 0x003F;
            //这里的0x3FFF & 0x4000的结果为0,默认是不支持的
            _mode_support_int = 0x3FFF;
        //}
    }
}

修改Power HAL层代码

前面可以知道需要两个步骤

  • 添加支持的mode,通过isModeSupported函数来实现
  • 调用setMode函数来实现具体的代码

添加HAL 双击亮屏模式的支持

为了不影响其他模式,这里修改_mode_support_int代码如下

_mode_support_int = 0x3FFF & 0x4000;

增加setMode的实现

原来的Power HAL setMode的代码实现

ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
    LOGI("Power setMode: %d to: %s", static_cast<int32_t>(type), (enabled?"on":"off"));
    getSupportedPlatform();
    switch (type) {
        case Mode::DOUBLE_TAP_TO_WAKE:
        break;
        case Mode::INTERACTIVE:
            if (enabled) interactive();
        break;
    }
    return ndk::ScopedAStatus::ok();
}

可以看到DOUBLE_TAP_TO_WAKE分支是空的,什么也没有,我们要做的就是在这里添加代码实现和触摸驱动之间的联动

  1. 这里我们事先定义了touch触摸的sysfs参数,其路径为/sys/devices/platform/fe5a0000.i2c/i2c-1/1-0038/double_tap,用来设置是否支持双击亮屏
#define DOUBLE_TAP_WAKE_PATH "/sys/devices/platform/fe5a0000.i2c/i2c-1/1-0038/double_tap"
  1. 在Power.h中添加相关函数的声明
void doubleTapSave(bool on);
  1. 在Power.cpp中实现doubleTapSave函数
void Power::doubleTapSave(bool on) {
    LOGI("doubleTapSave: %d",on);
    sysfs_write(DOUBLE_TAP_WAKE_PATH,on? "1": "0");
}

sysfs_write函数是简单的对文件的写操作

static void sysfs_write(const char *path, const char *s) {
    if (access(path, F_OK) < 0) return;
    char buf[80];
    int len;
    int fd = open(path, O_WRONLY);

    if (fd < 0) {
        strerror_r(errno, buf, sizeof(buf));
        ALOGE("Error opening %s: %s\n", path, buf);
        return;
    }

    len = write(fd, s, strlen(s));
    if (len < 0) {
        strerror_r(errno, buf, sizeof(buf));
        ALOGE("Error writing to %s: %s\n", path, buf);
    }
    close(fd);
}

可以看到我们最终的目的是在我们的驱动文件中设置/sys/devices/platform/fe5a0000.i2c/i2c-1/1-0038/double_tap的状态,开启就为1,关闭为0,然后在驱动中去控制双击事件

增加屏幕显示状态同步

前面的提到过PowerManagerService.java中的onDisplayStateChange 是通过调用IPower hal中的setPowerModeInternal(MODE_DISPLAY_INACTIVE,state) 代码来实现的,最终和双击亮屏开关一样都是通过HAL层的setPowerMode处理相关逻辑,区别就是双击亮屏开关出入的Mode值为
Mode::*DOUBLE_TAP_TO_WAKE 而屏幕显示状态的Mode值为*case Mode::*INTERACTIVE*: ,所以我们也要在setMode中增加同步屏幕状态到触摸驱动中的代码

  • 在Power.h中添加函数声明
void updateScreenStatus(bool on);
  • 在Poser.cpp中实现updateScreenStatus函数
#define SCREEN_WORK_PATH "/sys/devices/platform/fe5a0000.i2c/i2c-1/1-0038/screen_work"

ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
    LOGI("Power setMode: %d to: %s", static_cast<int32_t>(type), (enabled?"on":"off"));
    getSupportedPlatform();
    switch (type) {
        case Mode::INTERACTIVE:
            updateScreenStatus(enabled);
            if (enabled) interactive();
        break;
    }
    return ndk::ScopedAStatus::ok();
}

void Power::updateScreenStatus(bool on) {
    LOGI("updateScreenStatus: %d",on);
    sysfs_write(SCREEN_WORK_PATH, on? "1" : "0");
}

SCREEN_WORK_PATH是我们在驱动中定义的屏幕工作状态sysfs属性值,在setMode的时候去通过sysfs_write来同步更新到触摸驱动中

至此 framework中的状态通过native通知到了hal层代码,hal层通过sysfs文件系统同步到触摸驱动,最后由触摸驱动来实现双击亮屏的操作

触摸驱动的实现

包括的功能模块有

  • 定义sysfs属性,用来同步双击亮屏设置以及屏幕显示状态
  • 实现双击逻辑,通过timer定时器来实现双击逻辑
  • 上报电源事件,实现双击亮屏操作

定义sysfs属性

我们定义了三个属性,分别是

  • timeout,用来控制双击检测时间
  • screen_work,用来更新屏幕显示状态
  • double_tap,用来设置双击亮屏是否开启
  1. 定义读写函数
//读取timeout属性
static ssize_t double_time_out_show(struct device* dev, struct device_attribute* attr, char* buf)
{
    struct my_touch_dev* ts = dev_get_drvdata(dev);
    MY_DEBUG("read timeout: %d", ts->double_touch_timeout);
    return sprintf(buf, "%d\n", ts->double_touch_timeout);
}

//写入timeout属性
static ssize_t double_time_out_store(struct device* dev, struct device_attribute* attr,
                                     const char* buf, size_t count)
{
    int16_t val;
    struct my_touch_dev* ts = dev_get_drvdata(dev);
    if (!kstrtou16(buf, 10, &val)) {
        ts->double_touch_timeout = val;
        MY_DEBUG("store timeout: %d", ts->double_touch_timeout);
    }
    return count;
}
static ssize_t double_tap_show(struct device* dev, struct device_attribute* attr, char* buf)
{
    struct my_touch_dev* ts = dev_get_drvdata(dev);
    MY_DEBUG("read double_tap: %d", ts->double_tap);
    return sprintf(buf, "%d\n", ts->double_tap);
}

static ssize_t double_tap_store(struct device* dev, struct device_attribute* attr, const char* buf,
                                size_t count)
{
    bool val;
    struct my_touch_dev* ts = dev_get_drvdata(dev);
    if (!kstrtobool(buf, &val)) {
        ts->double_tap = val;
        MY_DEBUG("store double_tap: %d", ts->double_tap);
    }
    return count;
}

//写入screen_work属性
static ssize_t screen_work_store(struct device* dev, struct device_attribute* attr, const char* buf,
                                 size_t count)
{
    struct my_touch_dev* ts = dev_get_drvdata(dev);
    bool val;
    if (!kstrtobool(buf, &val)) {
        ts->screen_work = val;
        MY_DEBUG("store screen: %d", ts->screen_work);
    }
    return count;
}

//读取screen_work属性
static ssize_t screen_work_show(struct device* dev, struct device_attribute* attr, char* buf)
{
    struct my_touch_dev* ts = dev_get_drvdata(dev);
    MY_DEBUG("read scrren_work: %d", ts->screen_work);
    return sprintf(buf, "%d\n", ts->screen_work);
}
  1. 声明属性
//声明timeout属性
static DEVICE_ATTR(timeout, 0664, double_time_out_show, double_time_out_store);
//声明screen_work属性
static DEVICE_ATTR(screen_work, 0664, screen_work_show, screen_work_store);
static DEVICE_ATTR(double_tap, 0664, double_tap_show, double_tap_store);
  1. 创建sysfs class file

可以直接在/sys/class下面注册,也可以注册到device下面的sysfs属性中这里以device关联的sysfs为例在驱动的probe函数中注册

static int my_touch_probe(struct i2c_client* client, const struct i2c_device_id* id)
{
    //....
    //注册sysfs
    ret = device_create_file(&client->dev, &dev_attr_timeout);
    if (ret) {
        dev_err(&client->dev, "create file %s error", dev_attr_timeout.attr.name);
    }
    ret = device_create_file(&client->dev, &dev_attr_screen_work);
    if (ret) {
        dev_err(&client->dev, "create file %s error", dev_attr_screen_work.attr.name);
    }
    ret = device_create_file(&client->dev, &dev_attr_double_tap);
    if (ret) {
        dev_err(&client->dev, "create file %s error", dev_attr_double_tap.attr.name);
    }
    //...

    return ret;
}
  1. 同样也要在remove中去删除
static int my_touch_remove(struct i2c_client* client)
{
		//...
    //删除sysfs
    device_remove_file(&client->dev, &dev_attr_timeout);
    device_remove_file(&client->dev, &dev_attr_screen_work);
    device_remove_file(&client->dev, &dev_attr_double_tap);
		//...
    return 0;
}

my_touch_dev结构体

struct my_touch_dev {

    struct i2c_client* client;
    struct input_dev* input_dev;
    struct timer_list timer;
    //屏幕是是否工作
    bool screen_work;
    int8_t ready_double_click;
    int16_t double_touch_timeout;
    //启用双击和setting中的设置联动
    bool double_tap;

    //复位引脚
    int rst_pin;
    //中断引脚
    int irq_pin;
    // x最大值
    u32 abs_max_x;
    // y最大值
    u32 abs_max_y;
    //中断号
    int irq;
    //一个点占6个字节,一共5个点,+TP_STATUS一个字节
    u8 point_data[1 + 6 * 5];
};

此时编译kernel烧录就会看到sysfs目录下面的属性值了

image 1

可以看到已经创建了double_tap,timeout,和screen_work三个文件

注意:具体的路径和自己的设备属性有关系,例如我的是设备是挂载i2c1外设上的,设备地址是0x38,所以对应的就是/sys/devices/platform/fe5a0000.i2c/i2c-1/1-0038,fe5a0000则是rk3566 i2c1的地址,所以具体的路径还是要根据的soc平台以及设备挂载的总线以及设备的寄存器地址来决定的,最简单直接的办法是直接搜索,通过find命令来直接找到对应属性的具体路径

find /sys -name "double_tap"

找到位置后就可以通过cat和echo来查看和设置属性值了,

cat timeout
echo 200 > timeout

有问题的时候可以通过命令改变来快速的调试驱动中的逻辑,逻辑调通了然后再和hal层的数据同步更新验证完整的逻辑

双击逻辑的实现

需要再触摸中断里面去处理双击逻辑的实现

实现思路是通过定时器,来判断双击超时,控制双击时间的长短

  1. 定义定时器
//定时器回调函数,用来重置双击判定
void my_timer_callback(struct timer_list* timer)
{
    struct my_touch_dev* ts = from_timer(ts, timer, timer);
    ts->ready_double_click = 0;
    MY_DEBUG("double timeout");
}

在驱动的probe函数中初始化定时器

//初始化定时器
timer_setup(&ts->timer, my_timer_callback, 0);

在驱动的remove函数中删除定时器

//删除定时器
del_timer_sync(&ts->timer);

在触摸中断函数中处理双击逻辑以及上报电源键

//屏幕不亮的时候处理双击事件,
if (!ts->screen_work) {
    //开启了双击亮屏
    if (ts->double_tap) {
        MY_DEBUG("screen work: %d", ts->screen_work);
        //只有一个点击按下事件 才会触发double click
        if (event_flag == 0 && touch_num == 1) {
            if (ts->ready_double_click == 1) {
		            //如果是第二次点击,就上报电源事件
                ts->ready_double_click = 0;
                pm_wakeup_event(&ts->client->dev, 0);
                input_report_key(ts->input_dev, KEY_POWER, 1);
                input_report_key(ts->input_dev, KEY_POWER, 0);
                input_sync(ts->input_dev);

                MY_DEBUG("double click");
            }
            //开始响应double click事件
            ts->ready_double_click = 1;
            //超时就把ready_double_cliek置为0,重新开始double click判定
            mod_timer(&ts->timer, jiffies + msecs_to_jiffies(ts->double_touch_timeout));
        }
    }
    return IRQ_HANDLED;
}

触摸中断函数全部代码如下

int my_touch_i2c_read(struct i2c_client* client, u8* addr, u8 addr_len, u8* buf, u8 buf_len)
{
    int ret = -1;
    struct i2c_msg msg[2];
    //读取数据信息
    msg[0].addr = client->addr;
    msg[0].flags = !I2C_M_RD;
    msg[0].len = addr_len;
    msg[0].buf = &addr[0];

    msg[1].addr = client->addr;
    msg[1].flags = I2C_M_RD;
    msg[1].len = buf_len;
    msg[1].buf = &buf[0];

    ret = i2c_transfer(client->adapter, msg, 2);
    if (ret == 2)
        return 0;
    if (addr_len == 2) {
        MY_DEBUG("I2C Read: 0x%04X, %d bytes failed, errcode: %d! Process reset.",
                 (((u16)(addr[0] << 8) | addr[1])),
                 buf_len,
                 ret);
    } else {
        MY_DEBUG("I2C Read: 0x%02X, %d bytes failed, errcode: %d! Process reset.",
                 addr[0],
                 buf_len,
                 ret);
    }

    return ret;
}

void my_timer_callback(struct timer_list* timer)
{
    struct my_touch_dev* ts = from_timer(ts, timer, timer);
    ts->ready_double_click = 0;
    MY_DEBUG("double timeout");
}
irqreturn_t my_touch_irq_handler(int irq, void* data)
{
    s32 ret = -1;
    struct my_touch_dev* ts = data;
    u8 addr[1] = {TOUCH_REG_ADDR};   //读取touch num
    u8* point_data = ts->point_data;
    // u8 point_data[1 + 6 * 5] = {0};
    u8 touch_num = 0;
    u8* touch_data;
    int i = 0;
    int event_flag, touch_id, input_x, input_y;

    memset(point_data, 0, sizeof(ts->point_data) / sizeof(ts->point_data[0]));

    ret = my_touch_i2c_read(ts->client,
                            addr,
                            sizeof(addr),
                            point_data,
                            sizeof(ts->point_data) / sizeof(ts->point_data[0]));
    if (ret < 0) {
        MY_DEBUG("i2c wirte end_cmd error\n");
    }

    //读取触摸点数
    touch_num = point_data[0] & 0x0f;
    MY_DEBUG("touch_num:%d", touch_num);

    for (i = 0; i < 5; i++) {
        //获取触摸数据
        touch_data = &point_data[1 + i * 6];

        //读取event flag
        event_flag = touch_data[0] >> 6;

        //屏幕不亮的时候处理双击事件,
        if (!ts->screen_work) {
            //开启了双击亮屏
            if (ts->double_tap) {
                MY_DEBUG("screen work: %d", ts->screen_work);
                //只有一个点击按下事件 才会触发double click
                if (event_flag == 0 && touch_num == 1) {
                    //如果是第二次点击,就上报电源事件
                    if (ts->ready_double_click == 1) {
                        ts->ready_double_click = 0;
                        pm_wakeup_event(&ts->client->dev, 0);
                        input_report_key(ts->input_dev, KEY_POWER, 1);
                        input_report_key(ts->input_dev, KEY_POWER, 0);
                        input_sync(ts->input_dev);

                        MY_DEBUG("double click");
                    }
                    //开始响应double click事件
                    ts->ready_double_click = 1;
                    //超时就把ready_double_cliek置为0,重新开始double click判定
                    mod_timer(&ts->timer, jiffies + msecs_to_jiffies(ts->double_touch_timeout));
                }
            }
            return IRQ_HANDLED;
        }

        if (event_flag == 0x03)
            continue;
        //获取touch id
        touch_id = touch_data[2] >> 4;
        // 00: down
        // 01: up
        // 10: contact
        input_x = ((int)(touch_data[0] & 0x0f) << 8) | touch_data[1];
        input_x = 480 - input_x;
        input_y = ((int)(touch_data[2] & 0x0f)) << 8 | touch_data[3];
        MY_DEBUG("id: %d, event: %d,x: %d, y: %d\n", touch_id, event_flag, input_x, input_y);

        //交换xy
        // MY_SWAP(input_x, input_y);
        input_mt_slot(ts->input_dev, touch_id);
        // down
        if (event_flag == 0) {
            //设置按下状态
            input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
            input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x);
            input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y);
        } else if (event_flag == 2) {
            // contact 长按
            input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x);
            input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y);
        } else if (event_flag == 1) {
            //报告抬起
            input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false);
        }
    }

    //报告输入设备的指针仿真信息
    input_mt_report_pointer_emulation(ts->input_dev, true);
    //同步事件
    input_sync(ts->input_dev);

    //返回已经处理事件
    return IRQ_HANDLED;
}

总结

实现的目标是通过Setting中的双击亮屏开关来动态控制双击亮屏的操作

  • 需要同步Setting→显示设置→更多里面的双击亮屏开关
  • 需要获取到屏幕显示的状态,同步更新触摸驱动的代码中

framework修改

  1. 打开设置→显示→更多→双击亮屏开关

    默认这个开关是关闭的,需要手动打开

    修改代码device/rockchip/rk356x/overlay/frameworks/base/core/res/res/values/config.xml

    增加配置文件

    <bool name="config_supportDoubleTapWake">true</bool>
    

Power HAL中需要修改的地方

Power HAL文件位置为hardware/rockchip/power_aidl/

  1. 添加Power HAL中的对双击唤醒的支持

    修改文件位置hardware/rockchip/power_aidl/Power.cpp

    找到getSupportedPlatform 函数把_mode_support_int 的值改为0x3FFF & 0x4000

      void Power::getSupportedPlatform() {
          char platform[PROPERTY_VALUE_MAX] = {0};
          //property_get("ro.board.platform", platform, "");
          if (_mode_support_int < 0) {
              //if (strncmp(platform, "rk3399", PROPERTY_VALUE_MAX) == 0) {
                  _boost_support_int = 0x003F;
                  //_mode_support_int = 0x3FFF;
                  _mode_support_int = 0x3FFF & 0x4000;
              //}
          }
      }
    

    这样在isModeSupported 函数中才会返回true,进而会走到Power::setMode函数中

  2. 增加Power HAL中更新双击唤醒开关、以及屏幕显示状态同步

    • 在Power.cpp中定义驱动代码中的两个sysfs属性(注意这两个文件的位置要和自己实际的sysys所对应)

      #define DOUBLE_TAP_WAKE_PATH "/sys/devices/platform/fe5a0000.i2c/i2c-1/1-0038/double_tap"
      #define SCREEN_WORK_PATH "/sys/devices/platform/fe5a0000.i2c/i2c-1/1-0038/screen_work"
      
    • 在Power.h中添加两个函数定义,用来同步状态

      void doubleTapSave(bool on);
      void updateScreenStatus(bool on);
      
    • 在Power.cpp中增加函数实现

      void Power::doubleTapSave(bool on) {
          LOGI("doubleTapSave: %d",on);
          sysfs_write(DOUBLE_TAP_WAKE_PATH,on? "1": "0");
      }
      
      void Power::updateScreenStatus(bool on) {
          LOGI("updateScreenStatus: %d",on);
          sysfs_write(SCREEN_WORK_PATH, on? "1" : "0");
      }
      
    • 在Power.cpp的setMode函数中调用同步更新函数

      ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
          LOGI("Power setMode: %d to: %s", static_cast<int32_t>(type), (enabled?"on":"off"));
          getSupportedPlatform();
          switch (type) {
              case Mode::DOUBLE_TAP_TO_WAKE:
                  doubleTapSave(enabled);
              break;
              case Mode::INTERACTIVE:
                  updateScreenStatus(enabled);
                  if (enabled) interactive();
          }
          return ndk::ScopedAStatus::ok();
      }
      

最后就是编译烧录验证了!!!

posted @ 2025-09-04 15:16  JRobot  阅读(50)  评论(0)    收藏  举报