浅析linux下鼠标驱动的实现

http://blog.chinaunix.net/u1/38994/showart_1132089.html

浅析linux下鼠标驱动的实现

【浅析linux下键盘设备工作和注册流程】

对于鼠标驱动和前面分析过的键盘驱动都是共用input模型,所以,对于事件上报和处理的方式都没有区别,只是mouse鼠标驱动当上报完dx,dy,left,middle,right之后,需要调用input_sync(),将前面上报的仅仅填充在缓冲区中的数据,通过mousedev_notify_readers()发送给open了的挂接在mousedev->client_list链表上等待获取鼠标信息的client门,鼠标设备和键盘设备类似都是在/dev/input/目录下创建了一个char类型的设备节点,由应用程序使用read或者poll来阻塞调用,对于键盘设备为/dev/input/event0,...,/dev/input/eventx,对于鼠标设备为/dev/input/mouse0,...,/dev/input/mousex,可以使用sudo cat /dev/input/event0来从终端上截获显示按键的信息,使用sudo cat /dev/input/mouse0来捕捉鼠标的信息.让我们来看看驱动源码【gliethttp.Leith】:============drivers/input/mouse/amimouse.c============input_report_rel(amimouse_dev, REL_X, dx);input_report_rel(amimouse_dev, REL_Y, dy);input_report_key(amimouse_dev, BTN_LEFT, ciaa.pra & 0x40);input_report_key(amimouse_dev, BTN_MIDDLE, potgor & 0x0100);input_report_key(amimouse_dev, BTN_RIGHT, potgor & 0x0400);input_sync(amimouse_dev);//拷贝到open了的每个client的client->packets[16]环形缓冲区,每个应用程序在调用open时,mousedev_open都会调用kzalloc来申请一个独立的mousedev_client结构体,然后将该client挂接到mousedev->client_list链表,最后由mousedev_notify_readers向mousedev->client_list链表上挂接的每个client拷贝鼠标信息,最后wake_up唤醒read或poll.============drivers/input/mousedev.c============mousedev_read=>mousedev_packet=>如果dx,dy,dz同时都为0,说明鼠标停止了,那么client->ready = 0;mousedev_event(dev, EV_SYN, SYN_REPORT, 0)=>mousedev_notify_readers=>如果dx,dy,dz有一个发生了移动或者鼠标按键上一次的按键不同,那么client->ready = 1;拷贝数据到mousedev->client_list链表上挂接的每个client的环形缓冲区,最后调用wake_up_interruptible(&mousedev->wait);唤醒因为read或者poll操作而被pending住的应用程序,比如xWindows系统或者MiniGUI系统.mousedev_write=>mousedev_generate_response=>向client->ps2[6]缓冲区填充数据,有效数据的个数为client->bufsiz,之后执行如下赋值client->buffer = client->bufsiz;让client->buffer等于client->ps2[6]数据缓冲区中有效数据的个数.static ssize_t mousedev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos){struct mousedev_client *client = file->private_data;struct mousedev *mousedev = client->mousedev;signed char data[sizeof(client->ps2)];int retval = 0;if (!client->ready && !client->buffer && mousedev->exist && (file->f_flags & O_NONBLOCK))return -EAGAIN;retval = wait_event_interruptible(mousedev->wait,!mousedev->exist || client->ready || client->buffer);//等待条件满足或者信号发生,client->ready和client->buffer都可以在调用wake_up_interruptible(&mousedev->wait)之后,因为为真,而继续往下执行.if (retval)return retval;if (!mousedev->exist)return -ENODEV;spin_lock_irq(&client->packet_lock);//禁止中断if (!client->buffer && client->ready) {mousedev_packet(client, client->ps2);client->buffer = client->bufsiz;}if (count > client->buffer)count = client->buffer;memcpy(data, client->ps2 + client->bufsiz - client->buffer, count);//所以从这里可以看出,client->bufsiz为ps2[]数组有效数据索引的上限值,//client->buffer为ps2[]数组索引的下限值client->buffer -= count;//这样之后,再次执行read时,将会接续该buffer偏移位置继续读取.spin_unlock_irq(&client->packet_lock);//打开中断if (copy_to_user(buffer, data, count))//拷贝到用户空间return -EFAULT;return count;}对于mouse和keyboard来说poll方法是同时处理多项输入的相当高效的信息处理方法,应用程序可以使用select或者poll甚至epoll来等待多个事件的发生,比如同时等待mouse和key的发生,然后来统一处理【gliethttp.Leith】.static unsigned int mousedev_poll(struct file *file, poll_table *wait){struct mousedev_client *client = file->private_data;struct mousedev *mousedev = client->mousedev;poll_wait(file, &mousedev->wait, wait);return ((client->ready || client->buffer) ? (POLLIN | POLLRDNORM) : 0) |(mousedev->exist ? 0 : (POLLHUP | POLLERR));}以上鼠标input事件和键盘的input时间基本一致,最后都是调用input_report_rel()、input_report_key()等,不同的是mousedev_event只有当调用input_sync才会发生向client的数据拷贝动作,而键盘的evdev_event的事件处理函数不管是什么信息都会执行如下遍历:list_for_each_entry_rcu(client, &evdev->client_list, node)evdev_pass_event(client, &event);来完成向每个client数据buffer拷贝数据【gliethttp.Leith】.

享受每一刻的感觉,欣赏每一处的风景,这就是人生。

浅析linux下鼠标驱动的实现

相关文章:

你感兴趣的文章:

标签云: