浅析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】.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值