输入子系统3-作业

自行跟读输入子系统的三层代码实验

判断通关:
上传跟读代码笔记

//第一小节内容合并到上一节LEVEL12DAY3中去了
输入子系统驱动流程梗概:

输入子系统的工作原理和代码分析:

目的:
    a, 学会如何分析内核中子系统的代码,从而举一反三
    b,整体把握框架思想,理解分层中各层的配合方式
    c,掌握子系统,增强排错能力

代码跟读方法:
    1,带着问题去读
    2,做好笔记和画图
    3,驱动联系应用,应用是调用的,驱动是实现的

分层分析:

input handler层:evdev.c 内核自带 编译内核前可自行设置 详见Level12day2

module_init(evdev_init);
module_exit(evdev_exit);    //查阅一个驱动源代码的时候第一件事先找出口函数 即搜索module_exit 然后顺藤摸瓜找到入口函数 

    static struct input_handler evdev_handler = {
        .event		= evdev_event,
        .events		= evdev_events,
        .connect	= evdev_connect,
        .disconnect	= evdev_disconnect,
        .legacy_minors	= true,
        .minor		= EVDEV_MINOR_BASE,
        .name		= "evdev",
        .id_table	= evdev_ids,
    };

    evdev_init(void)
        |
        input_register_handler(&evdev_handler);
                |
                //添加互斥锁
                error = mutex_lock_interruptible(&input_mutex);
                if (error)
                    return error;

                //初始化h_list
                INIT_LIST_HEAD(&handler->h_list);

                //将当前的 handler 加入到一个input_handler_list
                list_add_tail(&handler->node, &input_handler_list);

                //遍历链表input_dev_list,将handler与对应的simple_dev相匹配
                list_for_each_entry(dev, &input_dev_list, node)
                    input_attach_handler(dev, handler);	
                            |
                            //将当前的handler 和  input dev进行匹配,event handler能够匹配所有的input dev
	                        input_match_device(handler, dev);

                            //匹配成功,之后要调用handler种connect方法
                            //这里的handler实际就是event handler,实际调用了endev_connect
                            handler->connect(handler, dev, id);

                //将当前的handler加入到/proc/bus/input/handlers文件中 
                input_wakeup_procfs_readers();

                //释放互斥锁
                mutex_unlock(&input_mutex);

input核心层:input.c 内核自带 编译内核前可自行设置 详见Level12day2

subsys_initcall(input_init);    //subsys_initcall 与 module_init 相比,功能相同但优先级更高。
module_exit(input_exit); 

    static int __init input_init(void)
    {
        //注册类,类似于class_create(); 
        //class_create(); -> __class_create(); -> 	__class_register();
        //class_register();		------------->		__class_register();
        err = class_register(&input_class);	//创建好了之后在 /sys/class 目录下可以看到创建的input类

        //技巧:不认识 先放一边 继续向下看
        //在/proc目录下创建一些文件: bus/input/device    s 和      bus/input/handlers
        err = input_proc_init();

        //申请设备号 类似于register_chrdev_dev(); 主设备号是 INPUT_MAJOR=13。
        err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),INPUT_MAX_CHAR_DEVICES, "input");
    }

    总结:   1.注册了主设备号
            2.注册了input class

simple_input.c驱动代码层: 我们自己写的代码

    //注册驱动信息到链表input_dev_list
    input_register_device(pInputdev);	

    //上锁
	error = mutex_lock_interruptible(&input_mutex);
	if (error)
		goto err_device_del;

	//将input_dev 加入到链表input_dev_list
	list_add_tail(&dev->node, &input_dev_list);

	//遍历input_handler_list, 进行匹配
	list_for_each_entry(handler, &input_handler_list, node)
		input_attach_handler(dev, handler);
                |
                //将当前的handler 和  input dev进行匹配,event handler能够匹配所有的input dev
                input_match_device(handler, dev);

                //匹配成功,之后要调用handler种connect方法
                //这里的handler实际就是event handler,实际调用了endev_connect
                handler->connect(handler, dev, id);

	//将input_dev添加进/proc/bus/input/dev文件中 
	input_wakeup_procfs_readers();

	//解锁
	mutex_unlock(&input_mutex);

分析: evdev.c中, evdev_connect()函数 --属于input_handler层

evdev_connect(struct input_handler *handler, struct input_dev *dev,
			 const struct input_device_id *id)
	|
	//找到一个没有被使用的次设备号, 从64开始, 65,66 
	minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);

	// 实例化 一个evdev对象
	evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
	//初始化evdev对象
	INIT_LIST_HEAD(&evdev->client_list);
	spin_lock_init(&evdev->client_lock);
	mutex_init(&evdev->mutex);
	//等待队列是完成阻塞
	init_waitqueue_head(&evdev->wait);
	evdev->exist = true;

	dev_no = minor;
	dev_no -= EVDEV_MINOR_BASE; //减去了64

	// 创建设备文件/dev/event0,1,2
	dev_set_name(&evdev->dev, "event%d", dev_no);
	evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);// 12, 64
	evdev->dev.class = &input_class;
	evdev->dev.parent = &dev->dev;
	evdev->dev.release = evdev_free;
	device_initialize(&evdev->dev)
	device_add(&evdev->dev); 
	//以上代码和device_create是一样

	//利用handle记录input device和input handler
	evdev->handle.dev = input_get_device(dev);
	evdev->handle.name = dev_name(&evdev->dev);
	evdev->handle.handler = handler;
	//你中有有我,我中有你
	evdev->handle.private = evdev;

	
	//将儿子关联到父亲(input handler)和母亲(input dev)
	error = input_register_handle(&evdev->handle);
					|
				list_add_tail_rcu(&handle->d_node, &dev->h_list);
				list_add_tail_rcu(&handle->h_node, &handler->h_list);

	
	//初始化了cdev,完成了fops, 为用户提供文件io
	cdev_init(&evdev->cdev, &evdev_fops);
	evdev->cdev.kobj.parent = &evdev->dev.kobj;
	error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);

	总结:
		1,分配evdev,并初始化,记录input device和handler的关系
		2,创建设备节点/dev/event0
		3, 注册cdev,并实现fops
        4,关系:
			多个input device可以对应一个event handler
			一个input device对应一个 evdev,对应于一个设备节点/dev/event0,1,2
		5, 所有的设备节点调用open,read,write文件io的时候
			实际是调用cdev中fops中各个接口,最终都调用了
			static const struct file_operations evdev_fops = {
					.owner		= THIS_MODULE,
					.read		= evdev_read,
					.write		= evdev_write,
					.poll		= evdev_poll,
					.open		= evdev_open,
					.release	= evdev_release,
					.unlocked_ioctl	= evdev_ioctl,
				#ifdef CONFIG_COMPAT
					.compat_ioctl	= evdev_ioctl_compat,
				#endif
					.fasync		= evdev_fasync,
					.flush		= evdev_flush,
					.llseek		= no_llseek,
				};
    ```

```cpp
device_create(struct class * class, struct device * parent, dev_t devt, void * drvdata, const char * fmt, ...)
	|
	dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
			|
			device_create_groups_vargs(class, parent, devt, drvdata, NULL,  fmt, args);
				|
				struct device *dev = NULL;
				dev = kzalloc(sizeof(*dev), GFP_KERNEL);

				device_initialize(dev);
				dev->devt = devt;
				dev->class = class;
				dev->parent = parent;
				dev->groups = groups;
				dev->release = device_create_release;
				dev_set_drvdata(dev, drvdata);
				kobject_set_name_vargs(&dev->kobj, fmt, args);//设置名字
				device_add(dev);//注册到系统

应用程序中调用了输入子系统的代码,数据是如何传递给用户层的?

 open("/dev/event1", O_RDWR);
---------------------------------------------
vfs
	sys_open();
		struct file  file->f_ops = cdev->ops;
		file->f_ops->open();
-------------------------------------------
设备驱动层:输入子系统
 input handler 层:evdev.c
		cdev;
	   xxx_ops = {
			.open = xxx_open,
			.write = xxx_write,
	   }


	 static const struct file_operations evdev_fops = {
					.owner		= THIS_MODULE,
					.read		= evdev_read,
					.write		= evdev_write,
					.poll		= evdev_poll,
					.open		= evdev_open,
	 }
	
	实际最终调用了evdev_open();
		|
		// 实际cdev是谁,就是evdev_connect注册的那个
		struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);

		// 通过儿子,找到老母input device
		unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);

		// size就包含了很多input_event
		unsigned int size = sizeof(struct evdev_client) +
						bufsize * sizeof(struct input_event);

		struct evdev_client *client;

		// 分配一个client对像,描述一个缓冲队列,存放的就是input_event
		client = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);

		// client中有一个缓冲区
		client->bufsize = bufsize;
		spin_lock_init(&client->buffer_lock);
		//在client中记录evdev
		client->evdev = evdev;
		// 将client加入到evdev中一个小链表
		evdev_attach_client(evdev, client);
				|
				list_add_tail_rcu(&client->node, &evdev->client_list);

		// 将client记录到file,方面其他的接口使用
		file->private_data = client;

		总结:
			1,为输入设备分配一个缓冲区evdev_client,用户存放input device层上报的数据
			2,evdev_client记录到evdev中
			3,evdev_client记录到file中,方面其他的接口使用