Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4261150
  • 博文数量: 776
  • 博客积分: 13014
  • 博客等级: 上将
  • 技术积分: 10391
  • 用 户 组: 普通用户
  • 注册时间: 2010-02-22 17:00
文章分类

全部博文(776)

文章存档

2015年(55)

2014年(43)

2013年(147)

2012年(20)

2011年(82)

2010年(429)

分类: LINUX

2013-09-04 11:00:32

浅析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来捕捉鼠标的信息.

  让我们来看看驱动源码
============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的发生,然后来统一处理
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拷贝数据

阅读(7110) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~