输入子系统分析

我们学的都是简单的字符驱动,涉及的内容有字符驱动的框架自动创建设备节点linux中断poll机制异步通知同步互斥/非阻塞定时器去抖动

其中驱动框架如下:

1)写file_operations结构体的成员函数: .open()、.read()、.write()

2)在入口函数里通过register_chrdev()创建驱动名,生成主设备号,赋入file_operations结构体

3)在出口函数里通过unregister_chrdev() 卸载驱动

若有多个不同的驱动程序时,应用程序就要打开多个不同的驱动设备,由于是自己写肯定会很清楚,如果给别人来使用时是不是很麻烦?

所以需要使用输入子系统, 使应用程序无需打开多个不同的驱动设备便能实现

驱动的入口函数包括四部分

1.分配核心结构体
2.填充结构体成员
3.注册input_dev结构体
4.硬件相关的设置

1.输入子系统简介

同样的输入子系统也需要输入驱动的框架,好来辨认应用程序要打开的是哪个输入驱动

比如: 鼠标、键盘、游戏手柄等等这些都属于输入设备;这些输入设备的驱动都是通过输入子系统来实现的(当然,这些设备也依赖于usb子系统)

这些输入设备都各有不同,那么输入子系统也就只能实现他们的共性,差异性则由设备驱动来实现。差异性又体现在哪里?

最直观的就表现在这些设备功能上的不同了。对于我们写驱动的人来说在设备驱动中就只要使用输入子系统提供的工具(也就是函数)来完成这些“差异”就行了,其他的则是输入子系统的工作。这个思想不仅存在于输入子系统,其他子系统也是一样(比如:usb子系统、video子系统等)

所以我们先来分析下输入子系统input.c的代码,然后怎么来使用输入子系统(在内核中以input来形容输入子系统)

2.打开input.c,位于内核deivers/input
显然输入子系统是作为一个模块存在,我们先来分析下input_int()入口函数

static int __init input_init(void)//入口函数
{
	int err;

	err = class_register(&input_class);//注册类放在/sys/class下面是在/sys/class 里创建一个 input类
	if (err) {
		printk(KERN_ERR "input: unable to register input_dev class\n");
		return err;
	}

	err = input_proc_init(); //在/proc下面建立相关的文件
		goto fail1;

	err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
	//创建主设备号为13的input字符设备
	if (err) {
		printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
		goto fail2;
	}
}

为什么这里代码只创建类,没有使用class_device_create()函数在类下面创建驱动设备?

在下面第8小结会详细讲到,这里简单描述:当注册input子系统的驱动后,才会有驱动设备,此时这里的代码是没有驱动的

(2)上面第14行通过register_chrdev创建驱动设备,其中变量INPUT_MAJOR =13,所以创建了一个主设备为13的"input"设备。

然后我们来看看它的操作结构体input_fops

static const struct file_operations input_fops = {
	.owner = THIS_MODULE,
	.open = input_open_file,//打开设备进入input_open_file文件
};

只有一个.open函数,比如当我们挂载一个新的input驱动,则内核便会调用该.open函数,接下来分析该.open函数

3 然后进入input_open_file函数(drivers/input/input.c);

//当我们挂载一个新的input驱动,则内核便会调用该.open函数
static int input_open_file(struct inode *inode, struct file *file)
{
	struct input_handler *handler = input_table[iminor(inode) >> 5];//放着input的驱动的数组号
	//取子设备号,然后将子设备除以32,找到新挂载的input驱动的数组号,然后放在input_handler 驱动处理函数handler中 
	const struct file_operations *old_fops, *new_fops = NULL;
	int err;

	/* No load-on-demand here? */
	if (!handler || !(new_fops = fops_get(handler->fops)))//更新设备的file_oprarions
	驱动处理 input_handler
	//若handler有值,说明挂载有这个驱动,就将handler结构体里的成员file_operations * fops赋到新的file_operations *old_fops里面
		return -ENODEV;

	/*
	 * That's _really_ odd. Usually NULL ->open means "nothing special",
	 * not "no device". Oh, well...
	 */
	if (!new_fops->open) {
		fops_put(new_fops);
		return -ENODEV;
	}
	old_fops = file->f_op;//
	file->f_op = new_fops;
	//再将新的file_operations *old_fops赋到file-> file_operations  *f_op里, 此时input子系统的file_operations就等于新挂载的input驱动的file_operations结构体,实现一个偷天换日的效果.

   //前面两句就相当于file->operation  *f_op=file_operation  *fops;
   //使input子系统的file_operations就等于新挂载的input驱动的file_operations结构体
   
   
	err = new_fops->open(inode, file);//执行file_opatrtions->open然后调用新挂载的input驱动的*old_fops里面的成员.open函数 

	if (err) {
		fops_put(file->f_op);
		file->f_op = fops_get(old_fops); 
	}
	fops_put(old_fops);
	return err;
}

这段代码实现的是input子系统的file_operations就等于新挂载的input驱动的file_operations结构体,当打开app的时候 ,最终调动新挂载的input驱动的*old_fops里面的成员.open函数,当app读的售后,最终的也是调用新挂载的input驱动的*old_fops里面的成员.read函数,

4.上面代码的input_table[]数组在初始时是没有值的,所以我们来看看input_table数组里面的数据又是在哪个函数里被赋值,它由input_register_handler构造

int input_register_handler(struct input_handler *handler)//注册函数
{
	struct input_dev *dev;

	INIT_LIST_HEAD(&handler->h_list);

	if (handler->fops != NULL) {
		if (input_table[handler->minor >> 5])
			return -EBUSY;

		input_table[handler->minor >> 5] = handler;// //input_table[]被赋值
		//将驱动处理程序input_handler注册到input_table[]中
	}

	list_add_tail(&handler->node, &input_handler_list);//然后将这个input_handler放到input_handler_list链表中
    //将input_handler驱动函数放入到的input_handler_list链表中
  
	list_for_each_entry(dev, &input_dev_list, node)//遍历查找input_dev_list链表中所有input_dev
		input_attach_handler(dev, handler);//判断两者的ID若两者的ID支持便进行连接
		
   //判断iput_dev_list链表中的所有的input_dev,是否支持这个新添加的input
_handler处理驱动结构体
   //不管新添加input_dev还是input_handler,都会进入input_attach_handler()判断两者id是否有支持, 若两者支持便进行连接。
}

5继续来搜索input_register_handler,看看这个函数被谁来调用

如下图所示,有evdev.c(事件设备),tsdev.c(触摸屏设备),joydev.c(joystick操作杆设备),keyboard.c(键盘设备),mousedev.c(鼠标设备) 这5个内核自带的设备处理函数注册到input子系统中

我们以evdev.c为例,它在evdev_ini()函数中注册:

static int __init evdev_init(void)
{
	return input_register_handler(&evdev_handler);//注册函数
}

6我们来看看这个evdev_handler变量是什么结构体,:

static struct input_handler evdev_handler = {
	.event =	evdev_event,
	.connect =	evdev_connect,连接函数,将设备input_dev和某个input_handler建立连接
	.disconnect =	evdev_disconnect,
	.fops =		&evdev_fops,//文件操作结构体,用于写自己的操作函数
	.minor =	EVDEV_MINOR_BASE,//存放次设备号
	.name =		"evdev",
	.id_table =	evdev_ids,//表示支持哪些设备
};

.id_table : 表示能支持哪些输入设备,比如某个驱动设备的input_dev->的id和某个input_handler的id_table相匹配,就会调用.connect连接函数,如下图

connect:连接函数,将设备input_dev和某个input_handler建立连接,如下图

 

6.1我们先来看看上图的input_register_device()函数,如何创建驱动设备的

int input_register_device(struct input_dev *dev)   //*dev:要注册的驱动设备
{
 ... ...
       list_add_tail(&dev->node, &input_dev_list);   //将要注册的input_dev驱动设备放在input_dev_list链表中
 ... ...
       list_for_each_entry(handler, &input_handler_list, node)  //
//遍历查找input_handler_list链表中所有input_handler
       input_attach_handler(dev, handler); 
//判断两者的ID若两者的ID支持便进行连接
/不管新添加input_dev还是input_handler,都会进入input_attach_handler()判断两者id是否有支持, 若两者支持便进行连接。
 ... ...
}

6.2然后我们在回过头来看注册input_handler的input_register_handler()函数,如下图所示

int input_register_handler(struct input_handler *handler)//注册函数
{
	
        input_table[handler->minor >> 5] = handler;// //input_table[]被赋值
      //将驱动处理程序input_handler注册到input_table[]中


	list_add_tail(&handler->node, &input_handler_list);
       //然后将这个input_handler放到input_handler_list链表中
    
      list_for_each_entry(dev, &input_dev_list, node)
       //遍历查找input_dev_list链表中所有input_dev
      input_attach_handler(dev, handler);//判断两者的ID若两者的ID支持便进行连接
		
 
}

不管新添加input_dev还是input_handler,都会进入input_attach_handler()判断两者id是否有支持, 若两者支持便进行连接。

6.3我们来看看input_attach_handler()如何实现匹配两者id的:

//input_handler和input_dev的id,进入input_attach_handler
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
    
  //看看input_attach_handler()如何实现匹配两者id的:
  
	const struct input_device_id *id;
	int error;

	if (handler->blacklist && input_match_device(handler->blacklist, dev))
		return -ENODEV;

	id = input_match_device(handler->id_table, dev);//匹配两者id
	
	if (!id)//若不匹配则退出
		return -ENODEV;

	error = handler->connect(handler, dev, id); //调用input_handler ->connect函数建立连接
	//建立input_handler和input_dev的连接,进入input_handler 的connect函数
	//若两者匹配成功,就会自动进入input_handler 的connect函数建立连接
}

若两者匹配成功,就会自动进入input_handler 的connect函数建立连接

7.我们还是以evdev.c(事件驱动) 的evdev_handler->connect函数

来分析是怎样建立连接的,如下图:

static struct input_handler evdev_handler = {
	.event =	evdev_event,
	.connect =	evdev_connect,//建立连接
	.disconnect =	evdev_disconnect,
	.fops =		&evdev_fops,//文件操作结构体,用于写自己的操作函数
	.minor =	EVDEV_MINOR_BASE,//存放次设备号
	.name =		"evdev",
	.id_table =	evdev_ids,//表示支持哪些设备
};

7.1evdev_handler的.connect函数是evdev_connect(),代码如下:

static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id)     
{
... ... 
for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++); //查找驱动设备的子设备号
    if (minor == EVDEV_MINORS) {  // EVDEV_MINORS=32,所以该事件下的驱动设备最多存32个,
        printk(KERN_ERR "evdev: no more free evdev devices\n");
        return -ENFILE;                //没找到驱动设备
    }
 ... ...
 evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);   //分配一个input_handle全局结构体(没有r)
 ... ...
 evdev->handle.dev = dev;              //指向参数input_dev驱动设备
evdev->handle.name = evdev->name;
evdev->handle.handler = handler;    //指向参数 input_handler驱动处理结构体
evdev->handle.private = evdev;
sprintf(evdev->name, "event%d", minor);    //(1)保存驱动设备名字, event%d
... ...
devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),  //(2) 将主设备号和次设备号转换成dev_t类型
cdev = class_device_create(&input_class, &dev->cdev, devt,dev->cdev.dev, evdev->name); 
                                                           // (3)在input类下创建驱动设备

... ...
error = input_register_handle(&evdev->handle); //(4)注册这个input_handle结构体

... ...
}

7.2 input_register_handle()函数如下:

int input_register_handle(struct input_handle *handle)
{
      struct input_handler *handler = handle->handler; //handler= input_handler驱动处理结构体

      list_add_tail(&handle->d_node, &handle->dev->h_list); 
     //因为handle->dev指向input_dev驱动设备,所以就是将handle->d_node放入到input_dev驱动设备的      h_list链表中,
//即input_dev驱动设备的h_list链表就指向handle->d_node
      list_add_tail(&handle->h_node, &handler->h_list);    
      // 同样, input_handler驱动处理结构体的h_list也指向了handle->h_node
 
      if (handler->start)
             handler->start(handle);
      return 0;
}

两者的.h_list都指向了同一个handle结构体,然后通过.h_list 来找到handle的成员.dev和handler,便能找到对方,便建立了连接

8建立了连接后,又如何读取evdev.c(事件驱动) 的evdev_handler->.fops->.read函数?

事件驱动的.read函数是evdev_read()函数,我们来分析下:

static ssize_t evdev_read(struct file *file, char __user *      buffer, size_t count, loff_t *ppos)
{
 ... ...
/*判断应用层要读取的数据是否正确*/
if (count < evdev_event_size())
return -EINVAL;

/*在非阻塞操作情况下,若client->head == client->tail|| evdev->exist时(没有数据),则return返回*/
 if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
 
/*若client->head == client->tail|| evdev->exist时(没有数据),等待中断进入睡眠状态  */
  retval = wait_event_interruptible(evdev->wait,client->head != client->tail || !evdev->exist);

  ... ...           //上传数据

}

9若read函数进入了休眠状态,又是谁来唤醒?

我们搜索这个evdev->wait这个等待队列变量,找到evdev_event函数里唤醒:



static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
... ...
 wake_up_interruptible(&evdev->wait);   //有事件触发,便唤醒等待中断
}

其中evdev_event()是evdev.c(事件驱动) 的evdev_handler->.event成员,如下图所示:

static struct input_handler evdev_handler = {
	.event =	evdev_event,//调用的事件
	.connect =	evdev_connect,
	.disconnect =	evdev_disconnect,
	.fops =		&evdev_fops,//文件操作结构体,用于写自己的操作函数
	.minor =	EVDEV_MINOR_BASE,//存放次设备号
	.name =		"evdev",
	.id_table =	evdev_ids,//表示支持哪些设备
};

当有事件发生了,比如对于按键驱动,当有按键按下时,就会进入.event函数中处理事件

10分析下,是谁调用evdev_event()这个.event事件驱动函数

应该就是之前分析的input_dev那层调用的.显然就是通过input_event()来调用.event事件函数,我们来看看:

void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
struct input_handle *handle;
... ...

/* 通过input_dev ->h_list链表找到input_handle驱动处理结构体*/
list_for_each_entry(handle, &dev->h_list, d_node)    
if (handle->open)  //如果input_handle之前open 过,那么这个就是我们的驱动处理结构体
    handle->handler->event(handle, type, code, value); //调用evdev_event()的.event事件函数 

}

若之前驱动input_dev和处理input_handler已经通过input_handler 的.connect函数建立起了连接,那么就调用evdev_event()的.event事件函数,如下图所示:

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值