linux 假输入设备,Linux Input子系统分析之eventX设备创建和事件传递

Linux Input子系统整体架构

0818b9ca8b590ca3270a3433284dd417.png

0818b9ca8b590ca3270a3433284dd417.png

注册eventX设备

注册过程大致如下:

input_register_device -> input_attach_handler -> input_match_device -> connect

input_register_device对input_handler_list中的每一个handler尝试input_attach_handler

list_for_each_entry(handler,&input_handler_list,node)input_attach_handler(dev,handler);

input_attach_handler调用input_match_device,如果match,则调用该handler的connect

evdev默认match所有的input_dev设备,evdev_handler的connect为evdev_handler

它会分配一个evdev,初始化handle的dev和handler,将input_dev和input_handler绑定在一起,并将该handle分别挂到input_dev的h_list和input_handler的h_list上

staticintevdev_connect(structinput_handler*handler,structinput_dev*dev,conststructinput_device_id*id){structevdev*evdev;...// 寻找可用minor值for(minor=0;minorclient_list);spin_lock_init(&evdev->client_lock);mutex_init(&evdev->mutex);init_waitqueue_head(&evdev->wait);dev_set_name(&evdev->dev,"event%d",minor);evdev->exist=true;evdev->minor=minor;evdev->hw_ts_sec=-1;evdev->hw_ts_nsec=-1;// 初始化evdev->handleevdev->handle.dev=input_get_device(dev);evdev->handle.name=dev_name(&evdev->dev);evdev->handle.handler=handler;evdev->handle.private=evdev;// 初始化evdev->devevdev->dev.devt=MKDEV(INPUT_MAJOR,EVDEV_MINOR_BASE+minor);evdev->dev.class=&input_class;evdev->dev.parent=&dev->dev;evdev->dev.release=evdev_free;device_initialize(&evdev->dev);// 将handle分别挂到input_dev的h_list和input_handler的h_list上error=input_register_handle(&evdev->handle);// 保存evdev到evdev_table数组error=evdev_install_chrdev(evdev);// 创建/dev/input/eventX文件error=device_add(&evdev->dev);...}intinput_register_handle(structinput_handle*handle){...list_add_tail_rcu(&handle->d_node,&dev->h_list);list_add_tail_rcu(&handle->h_node,&handler->h_list);...}// 保存evdev到evdev_table数组staticintevdev_install_chrdev(structevdev*evdev){evdev_table[evdev->minor]=evdev;return0;}

最后在/dev/input/目录下创建出eventX设备

需要注意的是evdev和input_dev各自拥有自己的struct device dev成员,调用device_add创建eventX设备时使用的是evdev->dev

evdev->dev.parent = dev->dev

两个dev的class都是input_class

每个eventX都对应一对input_dev和input_handler(由input_handle绑定在一起),一个input_dev可以对应多个input_handler,一个input_handler也可以对应多个input_dev

0818b9ca8b590ca3270a3433284dd417.png

注:与eventX对应的input_dev包含多个handler:sysrq、rfkill、kbd、evdev,evdev只是其中之一

<3>handler: c0817300, sysrq drivers/tty/sysrq.c

<3>handler: c084a7e0, rfkill net/rfkill/input.c

<3>handler: c081752c, kbd drivers/tty/vt/keyboard.c

<3>handler: c08215d4, evdev drivers/input/evdev.c

其他handler,如kbd在kbd_connect时并没有创建设备文件

通过/dev/input/eventX注入和接收输入事件

直接写/dev/input/eventX,或者通过uinput创建完eventX设备后直接写/dev/uinput,

都能让eventX产生输入事件,进而Android通过EventHub从eventX中收到新的key或者motion事件

调用流程如下:

input_inject_event->input_handle_event->input_pass_event

drivers/input/input.cvoidinput_inject_event(structinput_handle*handle,unsignedinttype,unsignedintcode,intvalue);staticvoidinput_handle_event(structinput_dev*dev,unsignedinttype,unsignedintcode,intvalue);staticvoidinput_pass_event(structinput_dev*dev,unsignedinttype,unsignedintcode,intvalue);

从dev的h_list中取出每一个handle,进而取出handle中的handler,调用handler->event

list_for_each_entry_rcu(handle,&dev->h_list,d_node){if(!handle->open)continue;handler=handle->handler;if(!handler->filter){if(filtered)break;handler->event(handle,type,code,value);}elseif(handler->filter(handle,type,code,value))filtered=true;}

对于eventX,handler为evdev_handler,event为evdev_event,

drivers/input/evdev.c

list_for_each_entry_rcu(client,&evdev->client_list,node)evdev_pass_event(client,&event,time_mono,time_real);

对evdev上每一个evdev_client调用evdev_pass_event,注意,每次open eventX都会分配一个evdev_client挂在evdev的client_list上

evdev_pass_event将新event事件加入到evdev_client的buffer中,并唤醒buffer上所有读等待的任务

这样,每一个open eventX的进程都能从buffer中读出事件

从以上流程可以看到,对evdev创建的eventX设备write时,会导致所有的handler的event被调用,而不仅仅是evdev的evdev_event

每个eventX都对应一个evdev(input_device_registe调用evdev_connect产生),每次open eventX都会分配一个evdev_client挂在evdev的client_list上

0818b9ca8b590ca3270a3433284dd417.png

Input Kernel Driver Example

event-injector-kmod是一个内核驱动demo,insmod后会注册一个/dev/input/eventX设备

假设我们创建出的设备为/dev/input/event2,在Android中可以通过以下脚本模拟调节音量键

$ catevent.sh

sendevent/dev/input/event21$11sendevent/dev/input/event2000sendevent/dev/input/event21$10sendevent/dev/input/event2000

执行adb shell后执行sh event.sh 114,就会让Android系统收到音量调节按键事件

也可以通过Android jni代码向eventX写入事件(需要root,先chmod 666 /dev/input/event2)

#include/* struct input_event {

struct timeval time;

__u16 type;

__u16 code;

__s32 value;

}; */voidsend_event(intfd,uint16_ttype,uint16_tcode,int32_tvalue){debug("SendEvent call (%d,%d,%d,%d)",fd,type,code,value);if(fd<=fileno(stderr))return;structinput_eventevent;intlen;memset(&event,0,sizeof(event));gettimeofday(&event.time,NULL);// event (type, code, value)event.type=type;event.code=code;event.value=value;if(write(fd,&event,sizeof(event))<0){debug("send_event error");}// sync (0,0,0)event.type=EV_SYN;event.code=SYN_REPORT;event.value=0;if(write(fd,&event,sizeof(event))<0){debug("send_event error");}}voidinject(){intfd=open("/dev/input/event2",O_RDWR|O_NDELAY);send_event(fd,1,114,1);// send volume-down key down eventsend_event(fd,1,114,0);// send volume-down key up eventclose(fd);}

参考

From: http://www.pickbox.me/2014/09/04/linux-input%E5%AD%90%E7%B3%BB%E7%BB%9F%E5%88%86%E6%9E%90%E4%B9%8Beventx%E8%AE%BE%E5%A4%87%E5%88%9B%E5%BB%BA%E5%92%8C%E4%BA%8B%E4%BB%B6%E4%BC%A0%E9%80%92/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值