zoukankan      html  css  js  c++  java
  • Android 虚拟按键驱动实现

    最近将Android touchscreen virtualkey驱动,向上层report keyvalue 改成 向上层report X,Y的坐标值。对sysfs文件系统进行了一番研究。

    virtualkey 是基于sysfs文件系统实现的。上层要想访问到virtualkey,必须在sys下面实现这样的文件结点:

    /sys/board_properties/virtualkeys.{deviceName}

     

    virtualkey的实现方式为什么必须基于sysfs文件结点,这个是由上层的调用决定的。

    在frameworks层,InputManger.java这个函数里定义了上层是怎么访问virtualkey的。

    public VirtualKeyDefinition[] getVirtualKeyDefinitions(String deviceName) {
                ArrayList<VirtualKeyDefinition> keys = new ArrayList<VirtualKeyDefinition>();
                
                try {
                    FileInputStream fis = new FileInputStream(
                            "/sys/board_properties/virtualkeys." + deviceName);
        /*....*/
                    }
         /*....*/
    }

    所以,要实现sys文件结点有2个函数

    int sysfs_create_files(struct kobject *kobj, const struct attribute **ptr);
    int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp);

    这里我们使用的是

    int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp);

    因为这个函数kobject对应的目录里,还可以创建子目录:virtualkeys.{deviceName}Linux内核里是用attribute_group来实现子目录.

    第一个参数是struct kobject *kobj,KobjectLinux内核里的定义如下:

    struct kobject {
        const char        * k_name;
        char            name[KOBJ_NAME_LEN];
        struct kref        kref;
        struct list_head    entry;
        struct kobject        * parent;
        struct kset        * kset;
        struct kobj_type    * ktype;
        struct dentry        * dentry;
        wait_queue_head_t    poll;
    };

    一个kobject对应sysfs里的一个目录,而目录下的文件就是由sysfs_opsattribute来实现的,其中,attribute定义了kobject的属性,在sysfs里对应一个文件,sysfs_ops用来定义读写这个文件的方法。

    基于此理解,所以board_properties目录,就定义为kobject:

    struct kobject *properties_kobj;
        
    properties_kobj = kobject_create_and_add("board_properties", NULL);

     这样就实现了sys/board_properties目录.

    因为目录下的文件是由sysfs_opsattribute来实现的,所以目录下的文件virtualkeys.{deviceName}将定义成attribute,那么对attribute的fops怎么定义呢?

    fops是定义了对以“virtualkeys.{deviceName}”为name的attribute的操作函数。 在内核的定义中,attribute 的fops就是对应每个attribute自己的show/store函数.

    内核定义了一个kobj_attribute 的结构体。

    struct kobj_attribute {
        struct attribute attr;
        ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
                char *buf);
        ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
                 const char *buf, size_t count);
    };

    这样,每一个attribute就会有各自属于自己的show/store函数,这样就极大的提高了灵活性。

    可是,sysfs是通过kobject里的kobj_type->sysfs_ops来读写attribute的,那如果要利用kobj_attribute中的show/store来读写attribute的话,就必须在kobj_type->sysfs_ops里指定。

     kobj_attribute是内核提供给我们的一种更加灵活的处理attribute的方式。只有当我们使用kobject_create来创建kobject时,使用kobj_attribute才比较方便。

    而在此处,由上面的代码可知,我们在定义

    int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp);

    这个函数的第一个参数kobject时正是使用了

    struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)

    来创建kobject,所以我们在此处使用kobj_attribute会很方便。

    所以在此处我们对kobj_attribute的定义是:

    static struct kobj_attribute virtual_keys_attr = {
        .attr = {
            .name = "virtualkeys.{deviceName}",    //这里的devicename一定要与设备的devicename一致,否则访问不到touchscreen的virtualkey
            .mode = S_IRUGO,
        },
        .show = &virtual_keys_show,  //相当于read操作
    };

    show相当于read操作,store相当于write操作。

    这里我们并没有定义store操作,上层只要read virtualkey的X,Y坐标值就可以了。不需要write操作。

    同时一个kobject可能会对应多个attribute,所以同时定义了一个attribute的数组,来管理attribute。

    struct attribute {
        const char        * name;
        struct module     * owner;
        mode_t            mode;
    };
    
    struct attribute_group {
        const char        * name;
        struct attribute    ** attrs;
    };
    static struct attribute *properties_attrs[] = {
            &virtual_keys_attr.attr,
            NULL
    }; //properties_attrs的最后一项一定要赋为NULL,否则会造成内核oops
    static struct attribute_group properties_attr_group = {
        .attrs = properties_attrs,
    };

    这样函数

    int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp);

    的2个参数就全部定义好了,可以在驱动的probe函数里调用virtualkey init类的函数就可以了。

    下面是完整的代码(以FT5X0X的touchscreen为例):

    #define FT5X0X_KEY_HOME    102
    #define FT5X0X_KEY_MENU    139
    #define FT5X0X_KEY_BACK    158
    #define FT5X0X_KEY_SEARCH  217
    
    
    static ssize_t virtual_keys_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
    {
        return sprintf(buf,
        __stringify(EV_KEY) ":" __stringify(FT5X0X_KEY_HOME) ":0:849:120:40"
         ":" __stringify(EV_KEY) ":" __stringify(FT5X0X_KEY_MENU) ":160:849:120:40"
         ":" __stringify(EV_KEY) ":" __stringify(FT5X0X_KEY_BACK) ":300:849:120:40"
         ":" __stringify(EV_KEY) ":" __stringify(FT5X0X_KEY_SEARCH) ":450:849:120:40"
         "\n");
    }
    
    static struct kobj_attribute virtual_keys_attr = {
        .attr = {
            .name = "virtualkeys.ft5x0x_ts",    //FT5X0X_NAME 一定要与触摸屏设备名称一致,不然会找不到指定的sys文件
            .mode = S_IRUGO,
        },
        .show = &virtual_keys_show,
    };
    
    
    static struct attribute *properties_attrs[] = {
            &virtual_keys_attr.attr,
            NULL
    };
    
    static struct attribute_group properties_attr_group = {
        .attrs = properties_attrs,
    };
    
    static void ft5x0x_ts_virtual_keys_init(void)
    {
        int ret;
        struct kobject *properties_kobj;
        
        properties_kobj = kobject_create_and_add("board_properties", NULL);
        if (properties_kobj)
            ret = sysfs_create_group(properties_kobj,  &properties_attr_group);
        if (!properties_kobj || ret)
            pr_err("failed to create board_properties\n");    
    }
    static int ft5x0x_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
    {
        /*...*/
    
        ft5206_ts_virtual_keys_init();
     
      /*...*/
    set_bit(KEY_HOME, input_dev
    ->keybit); set_bit(KEY_MENU, input_dev->keybit); set_bit(KEY_BACK, input_dev->keybit); set_bit(KEY_SEARCH, input_dev->keybit);//这里我把4个virtualkey的位图初始化设置了一下,开始这里忘了,其它地方都正确,4个virtualkey还是没效果 /*...*/
    }

    每一个虚拟按键有六个参数:

    0x01: A version code. Must always be 0x01.
    <Linux key code>: The Linux key code of the virtual key.
    <centerX>: The X pixel coordinate of the center of the virtual key.
    <centerY>: The Y pixel coordinate of the center of the virtual key.
    <width>: The width of the virtual key in pixels.
    <height>: The height of the virtual key in pixels.
    #define FT5X0X_KEY_HOME    102
    #define FT5X0X_KEY_MENU    139
    #define FT5X0X_KEY_BACK    158
    #define FT5X0X_KEY_SEARCH  217 //定义key code
        __stringify(EV_KEY) ":" __stringify(FT5X0X_KEY_HOME) ":0:849:120:40"
         ":" __stringify(EV_KEY) ":" __stringify(FT5X0X_KEY_MENU) ":160:849:120:40"
         ":" __stringify(EV_KEY) ":" __stringify(FT5X0X_KEY_BACK) ":300:849:120:40"
         ":" __stringify(EV_KEY) ":" __stringify(FT5X0X_KEY_SEARCH) ":450:849:120:40" //定义X,Y,width,height

    下面还要配置 /system/usr/keylayout .kl文件和.kcm文件

    如果没有指定,Android会自动android系统会自动选择默认的\android\sdk\emulator\keymaps\目录下的qwerty.kl和qwerty.kcm文件。

     这个就是利用了 android 系统默认的 qwerty.kl 映射表。如果需要自己建立“驱动名称.kl”文件,则可以按照 qwerty.kl

    文件的样式自己建立,把“驱动名称.kl”文件放放置在 android 文件系统中/system/usr/keylayout 目录下即可。如果想要android 源码编译过程中将“驱动名称.kl”编译到文件系统中,则只需将文件放置到 android 源码
    /vender/sec/sec_proprietary/utc100/keychars 目录中,并修改该目录下的 Android.mk 文件。

    这是Android 系统对底层按键的处理方法

    Android 按键的处理是由 Window Manager 负责,主要的键值映射转换实现是在 android 源代码

    frameworks/base/libs/ui/EventHub.cpp 此文件处理来自底层的所有输入事件,并根据来源对事件进行分类处理,

    对于按键事件处理过程如下:
    a)记录驱动名称(例如上面的驱动程序的驱动名称为ft5x0x_ts)
    b)获取环境变量 ANDROID_ROOT 为系统路径(默认是/system,在 android 源代码 system/core/rootdir/init.rc 文件中)
    c)查找路径为“system/usr/keylayout/ft5x0x_ts.kl”的按键映射文件,如果该文件(“ft5x0x_ts.kl”)不存在,则默认用路径为“系统路径/usr/keylayout/qwerty.kl”文件中的键值映射表。

    这样关于virtualkey的底层驱动就实现了。同时基于此驱动,按4个virtualkey时,如果硬件上实现了马达,并且上层有实现按键振动功能,就会有振动产生。

  • 相关阅读:
    Ubuntu 16.04配置vncviewer
    Ubuntu中可以卸载的软件(持续更新)
    MySQL入门常用命令
    数据库学习笔记(一)
    TensorFlow学习笔记(一)
    ubuntu安装deb包(dpkg)
    Linux中的bin文件夹
    常对象成员和常成员函数
    this指针
    对象成员指针
  • 原文地址:https://www.cnblogs.com/aceheart/p/2742309.html
Copyright © 2011-2022 走看看