input_event 和 ioctl

本文深入探讨了Linux Input子系统的四个关键函数:input_allocate_device()用于分配和初始化输入设备结构体;input_register_device()用于注册输入设备并设置相关值;input_attach_handler()用于匹配输入设备和处理器;input_match_device()则负责对比设备ID信息,确保正确的设备和处理器匹配。文章还详细解释了事件上报流程,包括按键事件和同步事件的上报机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • Input子系统的关键函数

1,input_allocate_device()

struct input_dev *input_allocate_device(void)  
{  
    struct input_dev *dev;  
    dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);              
    /*分配一个input_dev结构体,并初始化为0*/ 
    if (dev) {  
        dev->dev.type = &input_dev_type;        /*初始化设备的类型*/ 
        dev->dev.class = &input_class;          /*设置为输入设备类*/ 
        device_initialize(&dev->dev);           /*初始化device结构*/ 
        mutex_init(&dev->mutex);                /*初始化互斥锁*/ 
        spin_lock_init(&dev->event_lock);       /*初始化事件自旋锁*/ 
        INIT_LIST_HEAD(&dev->h_list);           /*初始化链表*/ 
        INIT_LIST_HEAD(&dev->node);             /*初始化链表*/ 
        __module_get(THIS_MODULE);              /*模块引用技术加1*/ 
    }  
    return dev;  
}

其返回一个指向 input_dev 类型的指针,该结构体是一个输入设备结构体,包含了输入设备的相关信息(按键码、设备名、支持的事件)

2,input_register_device()

注册一个输入设备,然后设置相关值

 

 __set_bit(EV_SYN, dev->evbit); 

 __set_bit(KEY_MENU, dev->keybit);

用来标记哪些键值能够使用

3input_attatch_handler()

匹配 input_dev 和 handler,调用input_match_device()

struct input_device_id {
    kernel_ulong_t flags;           /*标志信息*/ 
    __u16 bustype;                  /*总线类型*/ 
    __u16 vendor;                   /*制造商ID*/ 
    __u16 product;                  /*产品ID*/ 
    __u16 version;                  /*版本号*/ 
kernel_ulong_t driver_info;     /*驱动额外的信息*/
}

4,input_match_device()

进行对比id的信息,如果相同,返回id,如果不相同,返回0.

这时候handler->connect(handler,dev,id)开始匹配handler和dev。

 

PS:只有当 input deviceinput handler ID 成员在 evbitkeybit… swbit 项相同才会匹配成功。而且匹配的顺序是从evbitkeybitswbit。只要有一项不同,就会循环到ID中的下一项进行比较。

 

  • 对于input 上报事件(键值)

1,先上报一个按键事件,然后上报一个同步事件,完成一个按键的发生      

Input_report_key(dev->input_dev,KEY_HOME,1);

Input_sunc(dev->input_dev);

2,input_report_key调用input_event。在乎input_event中存在一个if

if (is_event_supported(type, dev->evbit, EV_MAX))

判断事件是否支持type类型,这个要在事先进行注册,如__set_bit(EV_SYN, dev->evbit);确定事件支持同步,__set_bit(EV_KEY, dev->evbit);确定事件支持按键

 

spin_lock_irqsave(&dev->event_lock, flags);

中断保护自旋锁,保护一次input event事件上报完成后,屏蔽掉本地的中断事件,直到解锁。即input_handle_event(dev, type, code, value);

 

3,input_handle_event(dev, type, code, value);

首先通过 disposition去判断事件的处理方式

如果是ignore,调用add_input_randomness,对随机数熵池增加事件(对事件发生没有用处)

如果是pass to device,代表事件给device去处理

如果是pass to handlers,代表事件给handler去处理(一般事件成功都是给handler处理),其中INPUT_SLOT,代表这个是多点输入设备

如果是INPUT_FLUSH代表这个事件会被下一次事件覆盖。

当事件是能被下一次按键覆盖的事件时,将会调用 input_pass_values,进而调用input_to_handler。

4,input_to_handler

先经过handler->filter过滤掉值,然后经过handler->event(s)将事件传递

 

上层可以通过如下查看内核的消息

cat /dev/input/event(x) |hexdump

前四列代表 触发的时间点,

第六列表示上报事件: 0001 --> EV_KEY; 0000 ---> EV_SYN

第七列表示键值: 0004 --> KEY_3

第八列表示按键事件: 0001 --> PRESS, 0000 --> RELEASE

# cat /proc/bus/input/devices   查看输入设备类型

 

getevent event(x)//adb 查看event事件
  • IOCTL控制

int ioctl(int fd,unsigned long cmd,...);

/*
fd:文件描述符
cmd:控制命令
...:可选参数:插入*argp,具体内容依赖于cmd
*/

int (*ioctl) (struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg);
/*
inode与filp两个指针对应于应用程序传递的文件描述符fd,这和传递open方法的参数一样。
cmd 由用户空间直接不经修改的传递给驱动程序
arg 可选。
*/

//nr为序号,datatype为数据类型,如int
_IO(type, nr ) //没有参数的命令
_IOR(type, nr, datatype) //从驱动中读数据
_IOW(type, nr, datatype) //写数据到驱动
_IOWR(type,nr, datatype) //双向传送

Ioctl匹配设备和命令,如果不符合的话,返回-EINVAL错误值

 

用户使用  int ioctl(int fd,unsinged long cmd,...)  时,这个...就是要传递的参数;

 再通过  int (*ioctl)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)  中的arg传递;
  如果arg是一个整数,可以直接使用;
  如果是指针,我们必须确保这个用户地址是有效的,因此,使用之前需要进行正确检查。
  内部有检查的,不需要检测的:

 

实例

xxx.C

#include <stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

int main()
{
fd = open("/dev/xxx",O_RDWR);
if(fd <0)  
{
    printf(“error \n”);return -1
}
if (ioctl(fd, cmd, &arg) < 0)
    {
            printf("Call cmd \n");
            return -1;
    }
return 0;
}

 

 

 

%%{init:{'themeVariables':{'fontSize':'22px'}}}%% graph TD %% ===== 客户端流程 ===== subgraph 客户端流程 start_client[客户端] --> init_event[cs_client_event_init<br>事件初始化模块] init_event --> create_udp[socket_server_dgram_create<br>创建UDP快速响应socket] init_event --> connect_tcp[cs_client_event_connect_server<br>TCP连接服务端] init_event --> create_thread[创建事件线程<br>l_tidClientEvent] create_thread --> poll_event[cs_client_poll_event_proc<br>轮询服务器事件] start_client --> init_order[cs_client_order_init<br>订单初始化] start_client --> main_loop[cs_client_order_enter<br>主业务循环] main_loop --> get_input[获取用户输入] get_input --> parse_cmd[解析命令] parse_cmd -->|exit/quit/e/q| exit[设置退出标志] parse_cmd -->|server/exit/quit/e/q 命令| send_server[通过TCP发送至服务端] parse_cmd -->|client/help/?命令| local_exec[client本地执行命令] parse_cmd --> print_cmd_result[通过client打印执行结果到屏幕] end %% ===== 服务端流程 ===== subgraph 服务端流程 start_server --> init_event_srv[cs_server_event_init] init_event_srv --> open_dev[打开设备文件 /dev/csmsimple] open_dev --> create_event_thread[创建事件轮询线程] create_event_thread --> poll_dev[轮询设备事件] start_server[服务端启动] --> init_cmd[cs_server_cmd_init] init_cmd --> create_udp_resp[socket_client_dgram_create<br>创建UDP快速响应socket] init_cmd --> create_tcp[socket_server_stream_create<br>创建TCP监听socket] init_cmd --> accept_thread[创建连接线程 l_tidAcceptCmd] init_cmd --> exec_thread[创建命令线程 l_tidExecCmd] start_server --> main_loop_srv[主循环] main_loop_srv --> sleep_20[sleep(20)保活] accept_thread --> accept_conn[接受客户端连接] exec_thread --> process_cmd[处理客户端命令] end %% ===== 内核模块流程 ===== subgraph 内核模块 insmod[加载驱动] --> init_driver[csm_simple_init] init_driver --> create_dev[创建设备号] create_dev --> reg_cdev[注册字符设备] open_dev -->|用户空间调用| driver_open[驱动open操作] driver_open -->|触发| ioctl_call[ioctl操作] ioctl_call --> handle_ioctl[csm_simple_ioctl] handle_ioctl -->|CSM_SIMPLE_GET_VERSION| get_version[返回驱动版本] end %% ===== 跨模块交互 ===== connect_tcp --> accept_conn send_server --> process_cmd open_dev --> driver_open %% poll_dev -->|读取事件| driver_read[csm_simple_read] 怎么调整三个子框的graph
最新发布
08-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值