linux驱动input子系统学习四(输入事件驱动层)

输入事件层目前系统帮我们区分了三种,

通用各种类型输入类设备的evdev,主流,也是将来大方向

mousedev,joydev。

我们学习就以evdev来学习,将来使用通用性也更高。

一、首先是框架性的调用输入核心层实现的input_register_handler和input_unregister_handler来注册evdev层。

static struct input_handler evdev_handler = {
	.event		= evdev_event,            /* 打包数据,并上报事件 */
	.connect	= evdev_connect,          /* 和dev匹配后做响应连接 */
	.disconnect	= evdev_disconnect,       /* exit使用,卸载使用 */
	.fops		= &evdev_fops,            /* 提供具体的read,write,sync之类的操作 */
	.minor		= EVDEV_MINOR_BASE,       /* 64,evdev的minor base */
	.name		= "evdev",
	.id_table	= evdev_ids,              /* 匹配规则,evdev是所有都可以匹配 */
};

static int __init evdev_init(void)
{
	return input_register_handler(&evdev_handler);
}

static void __exit evdev_exit(void)
{
	input_unregister_handler(&evdev_handler);
}

module_init(evdev_init);
module_exit(evdev_exit);

evdev有个重要的数据结构

/*evdev结构体再配对成功的时候生成,由handler->connect生成*/
struct evdev {
	int open;                      //打开引用计数
	int minor;                     //次设备号(实际的-EVDEV_MINOR_BASE)
	struct input_handle handle;    //关联的input_handle
	wait_queue_head_t wait;        //等待队列
	struct evdev_client __rcu *grab;
	struct list_head client_list;  //evdev_client链表,说明一个evdev设备可以处理多个evdev_client,可以有多个进程访问
	spinlock_t client_lock; /* protects client_list */
	struct mutex mutex;
	struct device dev;
	bool exist;
};

二、接口实现

1.首先我们看一下connect函数,它在注册handler和dev时,如果匹配上了就会调用。(具体可以查看我的输入子系统学习三)

/* 在endev里面定义的全局变量,它可以存放32的evdev指针 */
static struct evdev *evdev_table[EVDEV_MINORS];

/*
 * Create new evdev device. Note that input core serializes calls
 * to connect and disconnect so we don't need to lock evdev_table here.
 */
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
			 const struct input_device_id *id)
{
	struct evdev *evdev;
	int minor;
	int error;

	for (minor = 0; minor < EVDEV_MINORS; minor++)
		if (!evdev_table[minor])        /* 从小到大找到一个新的未使用的minor */
			break;
    /* 最多只有32个evdev设备,这里面minor是相对EVDEV_MINOR_BASE来说的,实际的次设备号应该加上EVDEV_MINOR_BASE */
	if (minor == EVDEV_MINORS) {
		printk(KERN_ERR "evdev: no more free evdev devices\n");
		return -ENFILE;
	}
    /* 申请空间 */
	evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
	if (!evdev)
		return -ENOMEM;
    /* 初始化新申请的evdev */
	INIT_LIST_HEAD(&evdev->client_list);
	spin_lock_init(&evdev->client_lock);
	mutex_init(&evdev->mutex);
	init_waitqueue_head(&evdev->wait);    /* 注意这里初始化了一个等待队列,将来肯定会有evdev被放入等待队列和唤醒等待队列操作 */

    /*evdev的名字是从0开始增加  */
	dev_set_name(&evdev->dev, "event%d", minor);
	evdev->exist = 1;
	evdev->minor = minor;

	evdev->handle.dev = input_get_device(dev);	/* input_dev绑定到 evdev里面的handle里 */
	evdev->handle.name = dev_name(&evdev->dev);
	evdev->handle.handler = handler;      /* 绑定handler到handle里 */
	evdev->handle.private = evdev;        /* handle的私有数据就是evdev本身 */

	evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
	evdev->dev.class = &input_class;      /* evdev里面的设备属于input_class,这样endev所有设备才会出现在sysfs下的input文件夹下面 */
	evdev->dev.parent = &dev->dev;        /* 父节点是设备的dev */
	evdev->dev.release = evdev_free;     
	device_initialize(&evdev->dev);       /* 初始化evdev里面的dev通用数据 */

	error = input_register_handle(&evdev->handle);    /* 注册handle,核心层有分析过,把handler和dev通过链表增加到handler里面的handle里面的链表节点 */
	if (error)
		goto err_free_evdev;

	error = evdev_install_chrdev(evdev);    /* 把该evdev添加到前面定义的evdev_table表中 */
	if (error)
		goto err_unregister_handle;

	error = device_add(&evdev->dev);        /* 真正的创建设备层次和attribute在sysfs中,学习完驱动学习sysfs的时候统一分析这个 */
	if (error)
		goto err_cleanup_evdev;

	return 0;

 err_cleanup_evdev:
	evdev_cleanup(evdev);
 err_unregister_handle:
	input_unregister_handle(&evdev->handle);
 err_free_evdev:
	put_device(&evdev->dev);
	return error;
}

2.disconnect,和connect相反

static void evdev_disconnect(struct input_handle *handle)
{
	struct evdev *evdev = handle->private;      /* 在connect把evdev本身绑定在handle里面的private */

	device_del(&evdev->dev);
	evdev_cleanup(evdev);        /* 对应于evdev_install_chrdev */
	input_unregister_handle(handle);
	put_device(&evdev->dev);
}

3.对一个设备驱动层发送过来的事件打包

/*
 * Pass incoming event to all connected clients.
 */
static void evdev_event(struct input_handle *handle,
			unsigned int type, unsigned int code, int value)
{
	struct evdev *evdev = handle->private;  /*  evdev本身绑定在handle里面的private */
	struct evdev_client *client;
	struct input_event event;
	struct timespec ts;

    /* 输入信息打包成标准格式 */
	ktime_get_ts(&ts);
	event.time.tv_sec = ts.tv_sec;
	event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
	event.type = type;
	event.code = code;
	event.value = value;

	rcu_read_lock();
     
     /* 如果该evdev有个专用的client,那么就将事件发给它如果该evdev不存在专用的client,那个就把该事件发送给evdev上client_list链表上所有的client */  
	client = rcu_dereference(evdev->grab);
	if (client)
		evdev_pass_event(client, &event);
	else
		list_for_each_entry_rcu(client, &evdev->client_list, node)
			evdev_pass_event(client, &event);    /* 打包数据 */

	rcu_read_unlock();
        /* 唤醒等到队列上的进程,注意这里是唤醒,肯定有别的地方定义(connect)和让它睡眠(read),注意我会标记的 */
	wake_up_interruptible(&evdev->wait);
}

发送数据给client函数

static void evdev_pass_event(struct evdev_client *client,
			     struct input_event *event)
{
	/*
	 * Interrupts are disabled, just acquire the lock
	 */
	spin_lock(&client->buffer_lock);
	wake_lock_timeout(&client->wake_lock, 5 * HZ);
	client->buffer[client->head++] = *event;    /* 把键值写入client缓冲队列中,方便app层读(app层可能一次读多个输入信息) */
	client->head &= EVDEV_BUFFER_SIZE - 1;
	spin_unlock(&client->buffer_lock);

	if (event->type == EV_SYN)
		kill_fasync(&client->fasync, SIGIO, POLL_IN);    /* 发送一个异步通知,通知该打开该client的应用程序,执行信号处理函数*/
}


4.键值的缓存,方便read,ioctl函数操作。5.应用层的调用接口file_operation

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
};

操作函数比较多,我们主要分析连个最常用的,open,和read

static int evdev_open(struct inode *inode, struct file *file)
{
	struct evdev *evdev;
	struct evdev_client *client;
	int i = iminor(inode) - EVDEV_MINOR_BASE;    /* 如果是event0,对于evdev设备来说,次设备号当然是0 */
	int error;
    /* 如果大于32,说明超出了evdev能够容纳的最大输入设备个数 */  
	if (i >= EVDEV_MINORS)
		return -ENODEV;
    
	error = mutex_lock_interruptible(&evdev_table_mutex);
	if (error)
		return error;
    /* 由于evdev中能容纳32个输入设备,因此通过设备号event0中的0定位到是要处理的是哪一个输入设备,evdev_table中的内容在输入设备驱动注册时通过evdev_connect填充 */
	evdev = evdev_table[i];
    /* 判断是否设备接口存在 */ 
	if (evdev)
		get_device(&evdev->dev);
	mutex_unlock(&evdev_table_mutex);

	if (!evdev)
		return -ENODEV;
    /* 存在则分配evdev中的client来处理event*,同一个文件打开n次要分配n个client */
	client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
	if (!client) {
		error = -ENOMEM;
		goto err_put_evdev;
	}
    /* 初始化上面新分配的client */
	spin_lock_init(&client->buffer_lock);
	snprintf(client->name, sizeof(client->name), "%s-%d",
			dev_name(&evdev->dev), task_tgid_vnr(current));
	wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name);
	client->evdev = evdev;                /* client和具体的event绑定 */
	evdev_attach_client(evdev, client);   /* 把clinet添加到evdev的client_list链表末尾 */

	error = evdev_open_device(evdev);    /* 将evdev设置为打开状态,下面分析  */
	if (error)
		goto err_free_client;

	file->private_data = client;        /* 将文件的私有数据指针指向该client */
	nonseekable_open(inode, file);      /* 给该inode设置不能使用seek的属性(指定位置读写) */

	return 0;

 err_free_client:
	evdev_detach_client(evdev, client);
	kfree(client);
 err_put_evdev:
	put_device(&evdev->dev);
	return error;
}

open_device

static int evdev_open_device(struct evdev *evdev)
{
	int retval;

	retval = mutex_lock_interruptible(&evdev->mutex);
	if (retval)
		return retval;

        /* evdev->exist在connevt里面职位1的 */
	if (!evdev->exist)		
		retval = -ENODEV;
        /* 第一次打开的话要打开设备文件,其它时候open做打开计数 */
	else if (!evdev->open++) {		
                /* 这个打开设备的是再核心层定义的,其实它也是一个假的函数,又要调用设备驱动层的open函数 */
		retval = input_open_device(&evdev->handle);
		if (retval)
			evdev->open--;
	}

	mutex_unlock(&evdev->mutex);
	return retval;
}

核心层的open函数


/**
 * input_open_device - open input device
 * @handle: handle through which device is being accessed
 *
 * This function should be called by input handlers when they
 * want to start receive events from given input device.
 */
int input_open_device(struct input_handle *handle)
{
	struct input_dev *dev = handle->dev;
	int retval;

	retval = mutex_lock_interruptible(&dev->mutex);
	if (retval)
		return retval;

	if (dev->going_away) {
		retval = -ENODEV;
		goto out;
	}

	handle->open++;        /* 统计打开次数 */

	if (!dev->users++ && dev->open)
		retval = dev->open(dev);    /* 真正的open函数在设备驱动层,当然设备驱动层也可以不实现这个函数 */
    /* 如果没有在设备驱动层定义open函数,不会执行执行到open函数,open进行自减操作,表示没有调用过open,这个值主要是为了close中判断open为0时释放资源使用。 */
	if (retval) {
		dev->users--;
		if (!--handle->open) {
			/*
			 * Make sure we are not delivering any more events
			 * through this handle
			 */
			synchronize_rcu();
		}
	}

 out:
	mutex_unlock(&dev->mutex);
	return retval;
}

对应应用层的read函数


static ssize_t evdev_read(struct file *file, char __user *buffer,
			  size_t count, loff_t *ppos)
{
	struct evdev_client *client = file->private_data;
	struct evdev *evdev = client->evdev;;    /* 得到应用程序读取数据的设备 */
	struct input_event event;
	int retval;

	if (count < input_event_size())        /* 熟读的数据长度至少要满足一个input_event的大小 */
		return -EINVAL;
    
     /* 当client缓冲区无数据;evdev不存在;文件非阻塞打开,那个read直接返回错误 */
	if (client->head == client->tail && evdev->exist &&
	    (file->f_flags & O_NONBLOCK))
		return -EAGAIN;
    /* 当client里面没有数据时,将应用程序请到evdev->wait等待队列休息,还记得在哪唤醒的吗(evdev_event的最后一行) */  
	retval = wait_event_interruptible(evdev->wait,
		client->head != client->tail || !evdev->exist);
	if (retval)
		return retval;

	if (!evdev->exist)
		return -ENODEV;

    /* 当要读取的数据大于struct input_event且client里面的buffer有数据,则把数据拷贝到用户空间*/
	while (retval + input_event_size() <= count &&
	       evdev_fetch_next_event(client, &event)) {

		if (input_event_to_user(buffer + retval, &event))    /* 对copy_to_user的封装 */
			return -EFAULT;

		retval += input_event_size();
	}

	return retval;
}

查看有没有数据,有的话返回1,且*event参数会把数据带出来一包


static int evdev_fetch_next_event(struct evdev_client *client,
				  struct input_event *event)
{
	int have_event;

	spin_lock_irq(&client->buffer_lock);

	have_event = client->head != client->tail;    /* 头不等于尾说明有数据 */
	if (have_event) {
		*event = client->buffer[client->tail++];    /* 读数据到*event,每次只能读一个包 */
		client->tail &= EVDEV_BUFFER_SIZE - 1;      /* 么次读完一个包,尾指针都要移动 */
		if (client->head == client->tail)
			wake_unlock(&client->wake_lock);
	}

	spin_unlock_irq(&client->buffer_lock);

	return have_event;
}

事件驱动层就分析道这里,虽然不由全部函数分析。但主要的都了解了,其它用到来再学。

猜你喜欢

转载自blog.csdn.net/qq_16777851/article/details/81263658