Android 通过input系统上报键值

 

简要说明:

本调试以上报门锁状态和烟雾状态为例,实现底层键值到Android层的上报。Android 通过Input键值的方式实现门锁、烟雾传感器的状态上报这个调试主要是记录一下Android系统对输入设备键值的捕获、上报、处理的过程,对于外面要接什么设备其实是不重要的。捕获和上报键值主要是靠kernel层去做,处理主要是Android系统层去做。原本是想自定义一个键值,想到系统自带的键值,很多都没有用上,没必要在自定义了,就直接用系统自带的一些键值对。如果要自己去增加键值也可以,主要的步骤是kernel添加键值,系统层添加对应的键值,然后再使用,由于这篇调试报告,没涉及到自定义的键值,后面有时间再展开如何添加自定义键值,以下列出添加自定义键值需要修改的文件

Android 层

./frameworks/base/data/keyboards/Generic.kl // 具体使用哪些kl,一个比较快的方法就是查看一下out目录下的kl文件

./frameworks/base/data/keyboards/qwerty.kl

./frameworks/native/include/android/keycodes.h

./frameworks/base/core/java/android/view/KeyEvent.java

./frameworks/base/core/res/res/values/attrs.xml

Kernel层

kernel/msm-4.9/include/uapi/linux/input-event-codes.h

 

本次调试不去详细分析电路,合不合理,只是看软件的实现

=====================开始正式的调试报告记录======================================

 

一、目的

应用层能够正常对门锁和烟雾状态的捕获

 

二、调试步骤

1、分析硬件原理图,得出门锁和烟雾状态的变化

2、根据硬件设计,实现驱动开发

3、Android系统层捕获输入状态并做相应的处理

 

三、硬件原理图分析

如图1原理图,由于这个需求是客户的定制需求,因而使用的是WG_D0_IN管脚(主控的GPIO90) 接烟雾传感器

使用WG_D1_IN(主控的GPIO96) 接门锁状态

 

图1

如图2,WD_IN接到烟雾传感器,KEY_IN接到门锁状态,这里就不具体去分析这个电路了,这个电路原本是接韦根设备的,只是在这上面接了我们的烟雾和门锁

 

图2

总结一下原理图,我们的烟雾传感器接到主控的GPIO90,正常情况下,GPIO90是低电平,当烟雾出发后,GPIO90是高电平;门状态传感器接到主控的GPIO96,门锁打开时GPIO96是高电平,门锁闭合时GPIO96是低电平

 

四、驱动的设计

通过原理图的分析后,我们采用中断的方式处理这些GPIO口的状态变化,并通过input系统向系统层上报键值,主要代码涉及以下方面,其中1部分在dtsi上面实现,2到5部分在驱动上面实现

1、设备树描述设备硬件信息

2、注册平台驱动

3、在probe函数里获取设备树的信息得到GPIO并注册中断(上下沿触发)

4、注册input并设置好要上报的键值

4、触发中断后开启定时器进行防抖

5、上报键值

 

说明: 由于项目隐私问题,代码中有些名字换了,应该是可以正常使用的,也可能会出现编译错误,具体实现是没有问题的

a、设备硬件信息(也就是我们平时说的设备,设备在dtsi上面描述)

kernel/msm-4.9/arch/arm64/boot/dts/qcom/msm8953.dtsi
......
customer-fuction{
    compatible = "customer";
    pinctrl-names = "customer_active","customer_suspend";
    smoke = <&tlmm 90 0x00>;
    door_status = <&tlmm 96 0x00>;
    pinctrl-0 = <&customer_active>;
    pinctrl-1 = <&customer_suspend>;
    status="okay";
};
........
kernel/msm-4.9/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi
............
customer_fnc {
customer_active: customer_active {
mux {
		pins = "gpio90","gpio96";
		function = "gpio";
	};
	config {
		pins = "gpio90","gpio96";
		drive-strength = <2>;
		bias-disable;
	};
};
...........

b、驱动部分,主要实现第2到5点,这个驱动用到了我们开发中经常用到的一些知识点,包括平台驱动、input子系统、中断、定时器等,可作为一个基础驱动

kernel/msm-4.9/drivers/misc/customer_fuc.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/uaccess.h>

struct customer_fuc_data {
	int smoke_status;
	int door_status;
	int smoke_irq;
	int door_irq;
	int smoke_irq_disabled;
	int door_irq_disabled;
	struct input_dev *input_device;
	struct pinctrl *customer_pinctrl;
	spinlock_t smoke_irq_lock;
	spinlock_t door_irq_lock;
};

struct customer_fuc_data *customer_data;
static int smoke_old_status = -1;
static int door_old_status = -1;

static struct timer_list door_input_timer;
static void door_input_timer_function(unsigned long data);
static DEFINE_TIMER(door_input_timer,door_input_timer_function,0,0);

static struct timer_list smoke_input_timer;
static void smoke_input_timer_function(unsigned long data);
static DEFINE_TIMER(smoke_input_timer,smoke_input_timer_function,0,0);

//关闭中断
static void input_irq_disable(struct customer_fuc_data *data, int dx)
{
	
	unsigned long irqflags;
	spinlock_t *lock;
	int irq;
	int *irq_disabled;

	if (dx == data->smoke_status){
		lock = &data->smoke_irq_lock;
		irq = data->smoke_irq;
		irq_disabled = &data->smoke_irq_disabled;
	}
	else{
		lock = &data->door_irq_lock;
		irq = data->door_irq;
		irq_disabled = &data->door_irq_disabled;
	}
	spin_lock_irqsave(lock, irqflags);
	if (!(*irq_disabled)){
		disable_irq_nosync(irq);
		*irq_disabled = 1;
	}
	spin_unlock_irqrestore(lock, irqflags);
}

//开启中断
static void input_irq_enable(struct customer_fuc_data *data, int dx)
{

	unsigned long irqflags;
	spinlock_t *lock;
	int irq;
	int *irq_disabled;

	if (dx == data->smoke_status){
		lock = &data->smoke_irq_lock;
		irq = data->smoke_irq;
		irq_disabled = &data->smoke_irq_disabled;
	}
	else{
		lock = &data->door_irq_lock;
		irq = data->door_irq;
		irq_disabled = &data->door_irq_disabled;
	}
	spin_lock_irqsave(lock, irqflags);
	if (*irq_disabled){
		enable_irq(irq);
		*irq_disabled = 0;
	}
	spin_unlock_irqrestore(lock, irqflags);
}

//上报按键键值
static void report_event(unsigned int event_value)
{
printk("---------event_value =%d------------\n",event_value);
input_report_key(customer_data->input_device,event_value, 1);
input_sync(customer_data->input_device);
input_report_key(customer_data->input_device,event_value, 0);
input_sync(customer_data->input_device);
}

//烟雾传感器消抖后上报键值
static void smoke_input_timer_function(unsigned long data)
{
	int value = gpio_get_value(customer_data->smoke_status);
	
//消抖
	if(value != smoke_old_status){
		smoke_old_status = -1;
		input_irq_enable(customer_data, customer_data->smoke_status);
		return;
	}
	
if(0 == value){
    		report_event(KEY_F5);  // 触发烟雾上报KEY_F5
	}else{
		report_event(KEY_F4);  //烟雾恢复正常上报KEY_F4
	}

	smoke_old_status = -1;
	input_irq_enable(customer_data, customer_data->smoke_status);
}

//door 消抖处理
static void door_input_timer_function(unsigned long data)
{
	int value = gpio_get_value(customer_data->door_status);
//消抖
	if(value != door_old_status){
		door_old_status = -1;
		input_irq_enable(customer_data, customer_data->door_status);
		return;
	}

	if(0 == value){
    		report_event(KEY_F1);   //门开启
	}else{
		report_event(KEY_F2);   //门关闭
	}

	door_old_status = -1;
	input_irq_enable(customer_data, customer_data->door_status);
}

// 烟雾状态改变触发的中断处理函数
static irqreturn_t input_irq_smoke_handler(int irq, void *dev_id)
{
	input_irq_disable(customer_data, customer_data->smoke_status);
	if(irq==customer_data->smoke_irq){
       		smoke_old_status = gpio_get_value(customer_data->smoke_status);
        	mod_timer(&smoke_input_timer,jiffies+HZ/50); //开启20ms防抖定时器
    	}

	return IRQ_HANDLED;
}

// 门状态改变触发的中断处理函数
static irqreturn_t input_irq_door_status_handler(int irq, void *dev_id)
{

	input_irq_disable(customer_data, customer_data->door_status);
	if(irq==customer_data->door_irq){
       		door_old_status = gpio_get_value(customer_data->door_status);
        	mod_timer(&door_input_timer,jiffies+HZ/50); //开启20ms的防抖处理
    	}

	return IRQ_HANDLED;
}

//获取设备树中关于IO口并设置管脚的初始功能,这里主要关注上下拉状态
static int customer_gpio_configure(struct customer_fuc_data *ddata, bool active)
{
	struct pinctrl_state *set_state;
	int retval;

	if (active) {
		set_state = pinctrl_lookup_state(ddata->customer_pinctrl, "customer_active");
		if (IS_ERR(set_state)) {
			printk(KERN_ERR " %s: cannot get ts pinctrl active state\n", __func__);
			return PTR_ERR(set_state);
		}
	} else {
		set_state = pinctrl_lookup_state(ddata->customer_pinctrl, "customer_suspend");
		if (IS_ERR(set_state)) {
			printk(KERN_ERR "%s: cannot get gpiokey pinctrl sleep state\n", __func__);
			return PTR_ERR(set_state);
		}
	}

retval = pinctrl_select_state(ddata->customer_pinctrl, set_state);
	if (retval) {
		printk(KERN_ERR "%s: cannot set ts pinctrl active state\n", __func__);
		return retval;
	}
	return 0;
}

static int gpio_parse_dt(struct device *dev, struct customer_fuc_data *pdata)
{
	enum of_gpio_flags flags;
	struct device_node *np = dev->of_node;
	int error;

	//获取烟雾管脚,并设置成输入状态
	pdata->smoke_status = of_get_named_gpio_flags(np, "smoke", 0, &flags);
	if (pdata->smoke_status < 0)
		return pdata->smoke_status;
	if (gpio_is_valid(pdata->smoke_status)) {
		error = gpio_request(pdata->smoke_status, "smoke");
		if (error) {
			printk(KERN_ERR "%s %d: gpio request failed", __func__,__LINE__);
			return error;
		}
		error = gpio_direction_input(pdata->smoke_status);
		if (error) {
			printk(KERN_ERR "%s %d: gpio request failed", __func__,__LINE__);
			return error;
		}
	}

	//设置门状态管脚并设置成输入状态
	pdata->door_status = of_get_named_gpio_flags(np, "door_status", 0, &flags);
	if (pdata->door_status < 0)
		return pdata->door_status;
	if (gpio_is_valid(pdata->door_status)) {
		error = gpio_request(pdata->door_status, "door_status");
		if (error) {
			printk(KERN_ERR "%s %d: gpio request failed", __func__,__LINE__);
			return error;
		}
		error = gpio_direction_input(pdata->door_status);
		if (error) {
			printk(KERN_ERR "%s %d: gpio request failed", __func__,__LINE__);
			return error;
		}
	}
	return 0;
}

/初始化输入系统
static int init_input_dev(struct platform_device *pdev)
{
	struct input_dev *_input_device = NULL;

	_input_device = input_allocate_device();
	if (_input_device == NULL){
		printk("*%s %d* _input_device allocate device err!\n", __FUNCTION__, __LINE__);
		return -1;
	}
//设置需要上报的键值,主要有F1 F2 F4 F5
	set_bit(EV_KEY, _input_device->evbit);
	set_bit(KEY_F1, _input_device->keybit);
	set_bit(KEY_F2, _input_device->keybit);
	set_bit(KEY_F4, _input_device->keybit);
	set_bit(KEY_F5, _input_device->keybit);

input_set_drvdata(_input_device, customer_data);
    if (input_register_device(_input_device)){
		printk("*%s %d* Unable to register %s _input_device device!\n", __FUNCTION__, __LINE__, _input_device->name);
        input_free_device(_input_device);
        return -1;
   }

	customer_data->input_device = _input_device;
    return 0;
}

// 驱动主入口 probe函数,调用上面的各种方法
static int customer_fuc_probe(struct platform_device *pdev)
{
	int error;

	customer_data = kzalloc(sizeof(struct customer_fuc_data), GFP_KERNEL);
	if (customer_data == NULL){
		return -ENOMEM;
	}

	customer_data->door_irq_disabled = 0;
	customer_data->smoke_irq_disabled = 0;

	/* get pinctrl*/
	customer_data->customer_pinctrl = devm_pinctrl_get(&pdev->dev);
	if (IS_ERR(customer_data->customer_pinctrl)) {
		if (PTR_ERR(customer_data->customer_pinctrl) == -EPROBE_DEFER)
			return -EPROBE_DEFER;
		printk(KERN_ERR "%s: Target does not use pinctrl\n", __func__);
		customer_data->customer_pinctrl = NULL;
	}
	/*set gpio default state*/
	if (customer_data->customer_pinctrl) {
		error = customer_gpio_configure(customer_data, true);
		if (error) {
			printk(KERN_ERR "%s: cannot set ts pinctrl active state\n", __func__);
			return error;
		}
	}
	/* get dts config*/
	error = gpio_parse_dt(&pdev->dev, customer_data);
	if (error) {
		printk(KERN_ERR "%s: Failed to parse dt\n", __func__);
		error = -ENOMEM;
		return error;
	}
	/* set irq*/
	customer_data->smoke_irq = gpio_to_irq(customer_data->smoke_status);
	if (customer_data->smoke_irq<=0) 
    		printk(KERN_ERR "%s: smoke_status  request failed", __func__);

	customer_data->door_irq = gpio_to_irq(customer_data->door_status);
	if (customer_data->door_irq<=0) 
    		printk(KERN_ERR "%s: door_irq  request failed", __func__);

	spin_lock_init(&customer_data->smoke_irq_lock);
	spin_lock_init(&customer_data->door_irq_lock);

	/* request irq*/
	if (request_irq(customer_data->smoke_irq, input_irq_smoke_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "smoke_irq", customer_data)) {
		printk("*%s %d* request smoke_irq  err\n", __FUNCTION__, __LINE__);
		kfree(customer_data);
		return -EINVAL;
    }
    if (request_irq(customer_data->door_irq, input_irq_door_status_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "door_irq", customer_data)){
		printk("*%s %d* request door_irq err\n", __FUNCTION__, __LINE__);
		free_irq(customer_data->smoke_irq, customer_data);
		kfree(customer_data);
		return -EINVAL;
    }
	/* init input device */
	if (init_input_dev(pdev))
    {
		printk("*%s %d* init  input device err\n", __FUNCTION__, __LINE__);
		free_irq(customer_data->smoke_irq, customer_data);
		free_irq(customer_data->door_irq, customer_data);
		kfree(customer_data);
		return -EINVAL;
    }

	printk("%s success\n", __FUNCTION__);
	
	return 0;
}

//驱动退出函数
static int  customer_fuc_remove(struct platform_device *pdev)
{
	free_irq(customer_data->smoke_irq, customer_data);
	free_irq(customer_data->door_irq, customer_data);
	kfree(customer_data);
	del_timer_sync(&door_input_timer);
	del_timer_sync(&smoke_input_timer);
	return 0;
}

//驱动和设备树匹配ID,只要匹配到dts的设备才会调用probe函数
static struct of_device_id customer_fuc_of_match[] = {
        { .compatible = "customer" },
        { }
};
MODULE_DEVICE_TABLE(of, customer_fuc_of_match);

//驱动结构体
static struct platform_driver customer_fuc_driver = {
        .driver         = {
                .name           = "customer_fuc",
                .owner          = THIS_MODULE,
                .of_match_table = of_match_ptr(customer_fuc_of_match),
        },
        .probe          = customer_fuc_probe,
        .remove         = customer_fuc_remove,
};

//注册平台设备驱动
static int __init customer_fuc_init(void)
{
	return platform_driver_register(&customer_fuc_driver);
}

static void __exit customer_fuc_exit(void)
{
	platform_driver_unregister(&customer_fuc_driver);
}
late_initcall(customer_fuc_init);
module_exit(customer_fuc_exit);
MODULE_LICENSE("GPL");

驱动总结:当烟雾出发后,GPIO90被拉高,触发烟雾上升沿中断,开启定时器进行消抖,然后上报KEY_F5键值;当烟雾恢复后,GPIO90被拉低,触发下降沿中断,开启定时器,然后上报KEY_F4;

当门锁被打开时,GPIO96被拉低,触发门锁下降沿中断,开启定时器进行消抖,然后上报KEY_F1键值;当烟雾恢复后,GPIO96被拉高,触发上升沿中断,开启定时器,然后上报KEY_F2;

可以用adb shell getevent 驱动层的查看键值上报

 

另外从开机log也可以看到我们的输入设备是注册到event1的,开机信息没保存,因而不贴出来

 

五、Android层处理

驱动层将键值上报后,会被系统捕获到并处理,具体的分析就不在展开,这里只是通过广播的方式通知应用层

1、添加广播

 

frameworks/base/core/java/android/content/Intent.java


    /**
     * Used as a boolean extra field in {@link #ACTION_CHOOSER} intents to specify
     * whether to show the chooser or not when there is only one application available
     * to choose from.
     *
     * @hide
     */
........
public static final String ACTION_BELL_STATUS = "android.intent.action.BELL_STATUS";
public static final String ACTION_SMOKE_STATUS = "android.intent.action.SMOKE_STATUS";
........

2、接收键值并发送相应的广播

frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

//这个方法会处理系统上几乎所有的键值,我们只需要在这里判断自己的键值,然后发广播通知应用
public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
........
        } else if (keyCode == KeyEvent.KEYCODE_F1) {
          if (down) {
                Intent mIntent = new Intent(Intent.ACTION_BELL_STATUS);
                mIntent.putExtra("bell_status", 1);
                mContext.sendBroadcastAsUser(mIntent, UserHandle.CURRENT);
                Slog.w(TAG, "chenqw debug ACTION_BELL_STATUS 1");
          }
            return -1;
        } else if (keyCode == KeyEvent.KEYCODE_F2) {
          if (down) {
                Intent mIntent = new Intent(Intent.ACTION_BELL_STATUS);
                mIntent.putExtra("bell_status", 0);
                mContext.sendBroadcastAsUser(mIntent, UserHandle.CURRENT);
                Slog.w(TAG, "chenqw debug ACTION_BELL_STATUS 0");
          }
        return -1;
        } else if (keyCode == KeyEvent.KEYCODE_F4) {
          if (down) {
                Intent mIntent = new Intent(Intent.ACTION_SMOKE_STATUS);
                mIntent.putExtra("smoke_status", 1);
                mContext.sendBroadcastAsUser(mIntent, UserHandle.CURRENT);
                Slog.w(TAG, "chenqw debug ACTION_SMOKE_STATUS 1");
          }
            return -1;
       } else if (keyCode == KeyEvent.KEYCODE_F5) {
            if (down) {
                Intent mIntent = new Intent(Intent.ACTION_SMOKE_STATUS);
                mIntent.putExtra("smoke_status", 0);
                mContext.sendBroadcastAsUser(mIntent, UserHandle.CURRENT);
                Slog.w(TAG, "chenqw debug ACTION_SMOKE_STATUS 0");
           }
            return -1;        
      }
........

总结:这里是用四个状态值来上报4中状态,原本想着用两个键值的up和down来表示四种状态,考虑到down的时候,键值一直没有释放,因此就用了F1 F2 F4 F5来做两个器件的四种状态,到此就已经调试完成了,一下是Android层捕获到键值然后发送广播的log

 

 

 

 

 

 

 

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值