精华内容
下载资源
问答
  • usb鼠标驱动完全注释

    2013-05-28 13:32:25
    usb鼠标驱动完全注释 适合usb初学者
  • usb鼠标驱动

    2012-06-28 10:05:45
    驱动为开发usb类型鼠标所使用,包含添加设备等多个例程。有详细的代码描述内容。
  • Linux USB鼠标驱动注解及测试Linux USB鼠标驱动注解及测试Linux USB鼠标驱动注解及测试
  • USB鼠标驱动简单程序

    2009-08-09 11:22:43
    基于单片的USB鼠标驱动,十分的好,我好不容易找到的。 大家一起来学USb吧。
  • 自己实现的一个linux usb鼠标驱动,能够基本完成鼠标的功能,驱动中加入了比较详细的注释和readme使用说明。
  • Linux USB 鼠标驱动程序详解,Linux USB 鼠标驱动程序详解Linux USB 鼠标驱动程序详解
  • USB鼠标驱动程序可以参考内核中的鼠标驱动,路径为linux-3.0.86\drivers\hid\usbhid\usbmouse.cUSB鼠标驱动编写步骤为: 1、创建usb_driver结构体变量,设置id_table为usb鼠标设备,并注册usb驱动。static struct ...

    USB子系统相关内容参考《精通Linux设备驱动程序》第11章。

    USB鼠标驱动程序可以参考内核中的鼠标驱动,路径为linux-3.0.86\drivers\hid\usbhid\usbmouse.c

    USB鼠标驱动编写步骤为:
    1、创建usb_driver结构体变量,设置id_table为usb鼠标设备,并注册usb驱动。

    static struct usb_device_id usb_mouse_id_table [] = {
        { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
            USB_INTERFACE_PROTOCOL_MOUSE) },
        { } /* Terminating entry */
    };
    
    usb_register(&my_usb_mouse_driver);

    在probe函数中:

    2、创建并分配input_device输入设备。

    my_mouse_dev = input_allocate_device();

    3、设置input_device,支持按键和相对位移输入。

    set_bit(EV_KEY, my_mouse_dev->evbit);
    set_bit(EV_REL, my_mouse_dev->evbit);
    
    set_bit(BTN_LEFT, my_mouse_dev->keybit);
    set_bit(BTN_RIGHT, my_mouse_dev->keybit);
    set_bit(BTN_MIDDLE, my_mouse_dev->keybit);
    set_bit(REL_X, my_mouse_dev->relbit);
    set_bit(REL_Y, my_mouse_dev->relbit);

    3、注册input_device输入设备.

    input_register_device(my_mouse_dev);

    4、创建urb结构体变量,为urb分配内存。

    data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &data_dma);

    5、为urb中数据缓冲区分配内存。

    urb = usb_alloc_urb(0, GFP_KERNEL);

    6、设置pipe,表示终端节点地址,是接收还是发送,是什么模式,比如int中断传输模式。

    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

    7、填充urb,report_mouse为函数指针,表示urb出错或者接收数据完成,要执行的这个函数。bInterval表示中断传输的轮询间隔时间。

    usb_fill_int_urb(urb, dev, pipe, data, len, 
            report_mouse, dev, endpoint->bInterval);

    8、提交urb。

    usb_submit_urb(urb, GFP_KERNEL);

    8、在report_mouse()函数中上报数据并再次提交urb。

        // 左键
        input_report_key(my_mouse_dev, BTN_LEFT, data[0] && 0x01);
        // 右键
        input_report_key(my_mouse_dev, BTN_LEFT, data[0] && 0x02);
        // 中键
        input_report_key(my_mouse_dev, BTN_LEFT, data[0] && 0x04);
        // 左右移动
        input_report_rel(my_mouse_dev, REL_X, data[1]);
        // 前后移动
        input_report_rel(my_mouse_dev, REL_Y, data[2]);
        input_sync(my_mouse_dev);
    
        usb_submit_urb (urb, GFP_ATOMIC);

    完整源码如下:

    /* my usbmouse */
    #include <linux/kernel.h>
    #include <linux/slab.h>
    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/usb/input.h>
    #include <linux/hid.h>
    
    static struct input_dev *my_mouse_dev;
    static unsigned char *data;
    static dma_addr_t data_dma;
    static struct urb *urb;
    
    static struct usb_device_id usb_mouse_id_table [] = {
        { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
            USB_INTERFACE_PROTOCOL_MOUSE) },
        { } /* Terminating entry */
    };
    
    void report_mouse(struct urb * urb)
    {
    #if 0
        //printk("report _mouse data\n");
        int cnt = 0;
        int i = 0;
        printk("report data cnt:%d\n",++cnt);
        for(i=0; i < urb->actual_length; i++)
        {
            printk("  0x%x", data[i]);
        }
        printk("\n");
    
    #endif
        // 上报数据
        // 左键
        input_report_key(my_mouse_dev, BTN_LEFT, data[0] && 0x01);
        // 右键
        input_report_key(my_mouse_dev, BTN_LEFT, data[0] && 0x02);
        // 中键
        input_report_key(my_mouse_dev, BTN_LEFT, data[0] && 0x04);
        // 左右移动
        input_report_rel(my_mouse_dev, REL_X, data[1]);
        // 前后移动
        input_report_rel(my_mouse_dev, REL_Y, data[2]);
        // 同步信号
        input_sync(my_mouse_dev);
        // 再次提交urb,不可休眠
        usb_submit_urb (urb, GFP_ATOMIC);
    }
    
    
    static int my_usb_mouse_probe(struct usb_interface *intf,
                  const struct usb_device_id *id)
    {
        int ret;
        int pipe, len;
        struct usb_device *dev = interface_to_usbdev(intf);
        struct usb_device_descriptor des = dev->descriptor;
        struct usb_host_interface *interface;
        struct usb_endpoint_descriptor *endpoint;
        ret = 0;
        printk("bcdDevice:%x   \nidVender:%x  \nidProduce:%x  \niManufacturer:%d\n",des.bcdDevice,des.idVendor, \
            des.idProduct,des.iManufacturer);
    
        interface = intf->cur_altsetting;
        // 终端描述符
        endpoint = &interface->endpoint[0].desc;
    
        // 1、 input 设备分配
        my_mouse_dev = input_allocate_device();
        if(NULL == my_mouse_dev){
            printk("allocate input dev error.\n");
        }
        my_mouse_dev->name = "myusbmouse";
    
        set_bit(EV_KEY, my_mouse_dev->evbit);
        set_bit(EV_REL, my_mouse_dev->evbit);
    
        set_bit(BTN_LEFT, my_mouse_dev->keybit);
        set_bit(BTN_RIGHT, my_mouse_dev->keybit);
        set_bit(BTN_MIDDLE, my_mouse_dev->keybit);
        set_bit(REL_X, my_mouse_dev->relbit);
        set_bit(REL_Y, my_mouse_dev->relbit);
        // 1.2、注册输入设备ret
        ret = input_register_device(my_mouse_dev);
        if(ret )
            goto fail1;
        // 2、 urb操作
        // 2.1、为urb中数据缓冲区分配空间
        data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &data_dma);
        if(NULL == data)
            goto fail2;
        // 2.2、设置管道,端点为数据流向地址,rcv为接收,int表示中断
        pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
        len = endpoint->wMaxPacketSize;
        printk("rcv data len is %d\n", len);
        // 2.3 为urb分配内存
        urb = usb_alloc_urb(0, GFP_KERNEL);
        if(NULL == urb )
            goto fail3;
        // 2.4、填充urb结构体
        usb_fill_int_urb(urb, dev, pipe, data, len, 
            report_mouse, dev, endpoint->bInterval);
        // dma方式传输数据
        urb->transfer_dma = data_dma;
        urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
        // 2.5、提交urb
        usb_submit_urb(urb, GFP_KERNEL);
        return 0;
    fail3:
        usb_free_urb(urb);
    fail2:
        usb_free_coherent(dev, 8, data, data_dma);
    fail1:
        input_free_device(my_mouse_dev);
        input_unregister_device(my_mouse_dev);
        return ret;
    }
    
    static void my_usb_mouse_disconnect(struct usb_interface *intf)
    {
        usb_kill_urb(urb);
        usb_free_coherent(interface_to_usbdev(intf), 8, data, data_dma);
        usb_free_urb(urb);
        input_unregister_device(my_mouse_dev);
        input_free_device(my_mouse_dev);
        usb_set_intfdata(intf, NULL);
    }
    
    static struct usb_driver my_usb_mouse_driver = {
        .name       = "my_usbmouse",
        .probe      = my_usb_mouse_probe,
        .disconnect = my_usb_mouse_disconnect,
        .id_table   = usb_mouse_id_table,
    };
    
    
    static int __init my_usb_mouse_init(void)
    {
        // 注册USB设备
        int retval = usb_register(&my_usb_mouse_driver);
        if (retval < 0)
            printk(KERN_INFO "register usb deivce error \n");
        return retval;
    }
    
    static void __exit my_usb_mouse_exit(void)
    {
        usb_deregister(&my_usb_mouse_driver);
    }
    
    
    module_init(my_usb_mouse_init);
    module_exit(my_usb_mouse_exit);
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("Zhangdalei");
    
    展开全文
  • 驱动程序-其它驱动-万能USB鼠标驱动 win7.zip
  • 嵌入式Linux —— usb鼠标驱动

    千次阅读 2018-01-16 10:03:47
    声明:本文章是看完韦东山老师的usb鼠标驱动视频所写的关于usb鼠标的驱动,因此如果有相关内容与其他网友相同,敬请原谅。同时我还是想说本文只是总结自己的学习所得,同时也将自己所学到的知识写下来,所以如果这篇...

    声明:本文章是看完韦东山老师的usb鼠标驱动视频所写的关于usb鼠标的驱动,因此如果有相关内容与其他网友相同,敬请原谅。同时我还是想说本文只是总结自己的学习所得,同时也将自己所学到的知识写下来,所以如果这篇文章对你有帮助,那是我的荣幸。

    在介绍驱动程序前我想向大家介绍一下usb_bus_type(usb总线驱动类),内核中有不同的总线类型,不同的总线有不同的匹配方式,如我们前面所学的platform_bus_type是使用名字来匹配的,而这里要讲的usb_bus_type的匹配是通过id_table来匹配的,但是各种总线的匹配流程大致还是一样的。因为要想将设备和驱动通过总线连接起来就不可避免的用到了match函数。就像你要相亲你就要将你的要求都写出来,而女方也要将自己对另一半的要求写出来,然后你们双方都把各自的请求交给婚介所,而婚介所所做的事就是将你的要求与每一个女士的请求进行比较,注意这里就用到了比较(match),当他们发现有一个女士满足你的要求,而你也正好满足这个女士的要求时,他就会对你说“给你匹配到了合适的女士”,而同时他也会对那位女士说“给你匹配到了合适的男士”,然后就安排你们相亲了。而我们的总线——设备——驱动模型就类似这个相亲模型。其中你是设备,那些女士是驱动,而婚介所就是总线了。

    有了上面的例子,我们结合这个例子分析一下这个匹配流程:

    上面这幅图就将,usb_bus_type的框架大致描绘出来了,下面我们详细的说一下。如上图所示,总线模型的最主要部分就是位于上层的总线,总线中有一个match函数,他会将通过usb_new_device向上注册的usb_interface和通过usb_register向上注册的usb_driver中的id_table一一比较(这个过程就类似于你和那些女士分别向婚介所投递个人信息),当发现设备和驱动匹配时,他就会调用的driver中的probe函数(这就相当于当发现你与其中一位女士匹配时就会通知你们相亲)。很多朋友可能会问“两个人相亲他们匹配的可能是性格,三观,收入等等,而设备和驱动他们匹配的是什么那?”,我们说了,不同的总线类型匹配的标准不一样,但总要有个可以匹配的吧。是的,在usb总线类型中,我们匹配的是id_table,可能很多人会问可以讲的细点吗?就像你说的相亲的时候匹配三观,可三观太大了,可以细分一下吗?,这个是可以的我们打开id_table的代码就会发现有:

     

    <span style="color:#333300">static struct usb_device_id usb_mouse_id_table [] = {
    	{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
    		USB_INTERFACE_PROTOCOL_MOUSE) },
    	{ }	/* Terminating entry */
    };</span>

     

     

    #define USB_INTERFACE_INFO(cl,sc,pr) \
    	.match_flags = USB_DEVICE_ID_MATCH_INT_INFO, .bInterfaceClass = (cl), \
    	.bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr)

     

    上面就是详细的比较类型了,他们有接口类,接口子类,以及接口协议,三部分组成。不过你还可以加附加条件比如:

     

    {USB_DEVICE(设备ID,产品ID)},

    这样就可以在原有的基础上缩小范围了。

     

    而具体的代码描述为:

    在hub.c的hub_port_connect_change函数中,有udev = usb_alloc_dev(hdev, hdev->bus, port1);而usb_alloc_dev就是分配一个设备结构体,而这个设备中都分配了什么那?我们可以进去看一下,我们可以在usb_alloc_dev函数中看到这样的代码:

     

    dev->dev.bus = &usb_bus_type;

     这行代码就定义了usb_bus_type,那么我们进去看看这个总线中又定义了什么:

     

     

    struct bus_type usb_bus_type = {
    	.name =		"usb",
    	.match =	usb_device_match,     //非常重要的match函数
    	.uevent =	usb_uevent,
    	.suspend =	usb_suspend,
    	.resume =	usb_resume,
    };

    我们可以从中看到一个match函数,我们在前面说过,虽然不同的总线他们匹配的数据可能不一样,但是他们的大致过程是一样的。下面我们进入usb_device_match函数中:

     

     

    static int usb_device_match(struct device *dev, struct device_driver *drv)
    		intf = to_usb_interface(dev);
    		usb_drv = to_usb_driver(drv);
    
    		id = usb_match_id(intf, usb_drv->id_table);

    通过上面的代码我们就可以知道他们所匹配的是dev的信息和id_table的 信息。

    有了上面的了解,我们现在来写一个简单的usb鼠标驱动,我们的目标是:当按下左键时上报按键L,当按下右键时上报S,中键ENTRR。

     

    从上面的目标可以看出我们需要用到输入子系统来上报按键。从usb_bus_type中我们知道usb的总线和设备部分已经写好,而我们可以做的就是写出驱动程序,下面我们开始写驱动程序。与其他的驱动程序一样,我们还是先搭好这个驱动程序的框架,然后在向其中填入想要做的事情的代码,而usb驱动的框架为:

    1.分配/设置usb_driver结构体

    2.在入口函数中注册这个结构体,在出口函数中注销这个结构体

    而详细的代码为:

     

    static struct usb_driver usb_mouse_drv = {        /* 分配设置usb_driver结构体 */
    	.name		= "usbmouse",
    	.probe		= usb_mouse_probe,
    	.disconnect	= usb_mouse_disconnect,
    	.id_table	= usb_mouse_id_table,       
    };
    
    int usb_drv_init(void)                            
    {
    	usb_register(&usb_mouse_drv);             /* 注册usb_driver结构体 */
    	return 0;
    }
    
    void usb_drv_exit(void)                           
    {
    	usb_deregister(&usb_mouse_drv);/* 注销usb_driver结构体 */
    }


    通过上面的代码我们可以看出,usb_driver结构体中有id_table(用于匹配设备),probe函数(当匹配成功时调用),disconnect函数(当匹配的设备离开时调用),注意:这三个是必不可少的。而其他的选项则是可以选择的。在上面我已经说过id_table了。所以这里并不详细说,只是介绍一下他的内容,代码如下:

     

     

    static struct usb_device_id usb_mouse_id_table [] = {
      { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
    	  USB_INTERFACE_PROTOCOL_MOUSE) },
      { } /* Terminating entry */
    };

    而其中USB_INTERFACE_INFO是一个宏:

     

     

    /**
     * USB_INTERFACE_INFO - macro used to describe a class of usb interfaces 
     * @cl: bInterfaceClass value
     * @sc: bInterfaceSubClass value
     * @pr: bInterfaceProtocol value
     *
     * This macro is used to create a struct usb_device_id that matches a
     * specific class of interfaces.
     */
    #define USB_INTERFACE_INFO(cl,sc,pr) \
    	.match_flags = USB_DEVICE_ID_MATCH_INT_INFO, .bInterfaceClass = (cl), \
    	.bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr)

     

     

     

    通过介绍上面的注解可以看出,他提供的是 接口类,接口子类,和接口协议。所以你可以提供相应的这三项来进行匹配。同时如果你对厂家ID和设备ID有其他的要求也可以通过id_table中的USB_DEVICE选项设置。

    写好id_table 后设备就可以与驱动程序进行匹配了,那么我们接下来就应该写匹配成功后要进入的probe函数了。那么进入probe后我们应该干什么那?我们要想一下自己的目的是通过按下鼠标实现按键功能,既然是按键功能就要用到输入子系统了,那么输入子系统的框架又是什么那?

    1.分配 input_dev结构体

    2.设置input_dev结构体

    3.注册input_dev结构体

    4.硬件相关的操作

    此处硬件相关的操作与以往不同,以前的按键,触摸屏是通过读寄存器或者ADC值,而现在的硬件相关操作是在usb驱动框架中的操作,所以此处应当由usb总线驱动提供usb读写函数来进行数据传输。

    下面是probe函数的代码:

     

    int usb_mouse_probe (struct usb_interface *intf,
    		      const struct usb_device_id *id)
    {
    	struct usb_device *dev = interface_to_usbdev(intf);
    	struct usb_host_interface *interface;
    	struct usb_endpoint_descriptor *endpoint;
    	
    	interface = intf->cur_altsetting;
    	endpoint = &interface->endpoint[0].desc;
    	
    	/*1   分配一个input_dev结构体 */
    	uk_dev = input_allocate_device();
    	
    	/*2   设置 */
    	/*2.1 产生哪类事件 */
    	set_bit(EV_KEY,uk_dev->evbit);    //产生按键类事件
    	set_bit(EV_REP,uk_dev->evbit);    //产生重复类事件
    	
    	/*2.2 产生这类事件中的那个 */
    	set_bit(KEY_L,uk_dev->keybit);   //按键类中的按键L
    	set_bit(KEY_S,uk_dev->keybit);   //按键类中的按键S
    	set_bit(KEY_ENTER,uk_dev->keybit); //按键类中的按键ENTER
    	
    	/*3  注册 */
    	input_register_device(uk_dev);
    
    	/*4 硬件相关设置:通过使用USB设备总线获取读写函数 */
    	/* 数据传输三要素:源,目的,长度 */
    
    	/* 源:USB设备的某个端点 */
    	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
    
    	/* 长度 */
    	len = endpoint->wMaxPacketSize;
    
    	/* 目的:  */
    	usb_buf = usb_buffer_alloc(dev,len,GFP_KERNEL,&usb_buf_phys);
    
    	/* 使用三要素 */
    	/* 分配一个urb(USB request block) */
    	uk_urb = usb_alloc_urb(0,GFP_KERNEL);
    
    	/* 设置使用urb */
    	usb_fill_int_urb(uk_urb, dev, pipe, usb_buf,
    			 len,
    			 usb_mouse_irq, NULL, endpoint->bInterval);
    	uk_urb->transfer_dma = usb_buf_phys;
    	uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
    
    	/* 使用urb */
    	usb_submit_urb(uk_urb,GFP_KERNEL);
    
    	return 0;
    }


    上面的程序已经说明了input_dev的框架,只是有些同学可能会问这代码中的第四部分是怎么回事?

     

    这就要介绍另一个非常有用的结构体USB请求块(USB request block,URB),URB是USB设备驱动中用来描述与USB设备通信所用的基本载体和核心数据结构,与网络设备驱动中的sk_buff结构体类似,是USB主机与设备之间传输数据的封装。

    一个urb包含了执行usb传输所需要的所有信息。当要进行数据传输时,需要分配一个urb结构体,对其进行初始化,然后将其提交给usb核心。USB核心对urb进行解析,将控制信息提交给主机控制器,由主机控制器负责数据到设备的传输。这时,驱动程序只需等待,当数据回传到主机控制器后,会转发给USB核心,唤醒等待的驱动程序,由驱动程序完成剩下的工作。

    更为具体地说,Linux中的设备驱动程序只要为每一次请求准备一个urb结构体,然后把它填充好,就可以调用函数usb_submit_urb()提交给USB核心。然后USB核心将urb传递给USB主机控制器,最终传递给USB设备。USB设备获得urb结构体后,会解析这个结构体,并以相反的路线将数据返回给Linux内核

     

     

     

     

     

    注:上面的描述是从百度百科抄来的,我看他写的比较简单就抄下来分享给大家了。

    写完这些我们就应该完成usb_fill_int_urb函数中的usb_mouse_irq函数了,也许有些朋友可能会问这是中断函数吗?我的回答是这是中断函数,只不过这是主机控制器产生的中断而不是从机设备:

    像上面这个示意图一样,主机控制器不断的查询usb设备获得数据后将数据放入buffer中,然后主机控制器就会产生一个中断从而使是上面的这个usb_mouse_irq函数被调用。

    而在usb_mouse_irq函数中要做的就是将将获得的按键值上报:

     

    static void usb_mouse_irq(struct urb *urb)
    {
    	static unsigned char pre_val;
    
    	if((pre_val & (1<<0)) != (usb_buf[0] & (1<<0))){
    		
    		/* 左键发生了变化 */
    		input_event(uk_dev,EV_KEY,KEY_L,usb_buf[0] & (1<<0) ? 1 : 0);
    		input_sync(uk_dev);
    	}
    
    	if((pre_val & (1<<1)) != (usb_buf[0] & (1<<1))){
    		/* 右键发生了变化 */
    		input_event(uk_dev,EV_KEY,KEY_S,usb_buf[0] & (1<<1) ? 1 : 0);
    		input_sync(uk_dev);
    	}
    
    	if((pre_val & (1<<2)) != (usb_buf[0] & (1<<2))){
    		/* 中键发生了变化 */
    		input_event(uk_dev,EV_KEY,KEY_ENTER,usb_buf[0] & (1<<2) ? 1 : 0);
    		input_sync(uk_dev);
    	}
    
    	pre_val = usb_buf[0];
    	/* 从新提交urb */
    	usb_submit_urb(uk_urb,GFP_KERNEL);
    }
    

     

     

     

     

     

    上面就是这个驱动的主要部分了,但是当设备离开时会调用相应的disconnect函数,下面我们来写disconcert函数:

     

    void usb_mouse_disconnect(struct usb_interface *intf)
    {
    	struct usb_device *dev = interface_to_usbdev(intf);
    
    	usb_kill_urb(uk_urb);
    	usb_free_urb(uk_urb);
    	usb_buffer_free(dev,len, usb_buf, usb_buf_phys);
    	input_unregister_device(uk_dev);
    	input_free_device(uk_dev);
    	
    }

    完成disconnect函数,这个驱动程序就算完成了。

     

    而这里还有几篇文章是我参考过得:

    ARM-Linux开发之USB驱动鼠标控制 :这篇文章也是按韦东山老师的方式所写的驱动 

    嵌入式Linux USB驱动开发之教你一步步编写USB驱动程序

    嵌入式linux下usb驱动开发方法--看完少走弯路 :这篇文章虽然文字不多,对学习usb驱动的很有帮助

    嵌入式Linux驱动学习之路(二十)USB设备驱动:这篇文章是对老师上课内容的总结,并有完整的代码

     

     

    Linux USB 驱动开发(五)—— USB驱动程序开发过程简单总结:这篇文章虽然不是讲的usb鼠标,但就usb驱动的很多的原理做了介绍

     

     

    嵌入式linux下如何使用usb键盘和鼠标

      :这篇文章是一篇很好的就是usb鼠标的文章。

     

     

     

     

     

     

     

     

     

     

    展开全文
  • linux下自己写的usb鼠标驱动程序,编译加载后,能实现识别鼠标,和usb鼠标通信
  • USB鼠标驱动 usbmouse.c 原文链接:http://www.linuxidc.com/Linux/2012-12/76197p7.htm drivers/hid/usbhid/usbmouse.c 下面我们分析下USB鼠标驱动,鼠标输入HID类型,其数据传输采用中断URB,鼠标端点类型为...

    USB鼠标驱动 usbmouse.c

    原文链接:http://www.linuxidc.com/Linux/2012-12/76197p7.htm

    drivers/hid/usbhid/usbmouse.c

    下面我们分析下USB鼠标驱动,鼠标输入HID类型,其数据传输采用中断URB,鼠标端点类型为IN。我们先看看这个驱动的模块加载部分。

    static int __init usb_mouse_init(void)
    {
    	int retval = usb_register(&usb_mouse_driver);
    	if (retval == 0)
    		printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
    				DRIVER_DESC "\n");
    	return retval;
    }

    模块加载部分仍然是调用usb_register注册USB驱动,我们跟踪看看被注册的usb_mouse_driver

    static struct usb_driver usb_mouse_driver = {
    	.name		= "usbmouse",           //驱动名
    	.probe		= usb_mouse_probe,
    	.disconnect	= usb_mouse_disconnect,
    	.id_table	= usb_mouse_id_table,   //支持项
    };
    关于设备支持项我们前面已经讨论过了

    static struct usb_device_id usb_mouse_id_table [] = {
    	{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
    		USB_INTERFACE_PROTOCOL_MOUSE) },
    	{ }	/* Terminating entry */
    };
    
    MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);
    再细细看看USB_INTERFACE_INFO宏的定义

    /**
     * USB_INTERFACE_INFO - macro used to describe a class of usb interfaces
     * @cl: bInterfaceClass value
     * @sc: bInterfaceSubClass value
     * @pr: bInterfaceProtocol value
     *
     * This macro is used to create a struct usb_device_id that matches a
     * specific class of interfaces.
     */
    #define USB_INTERFACE_INFO(cl, sc, pr) \
    	.match_flags = USB_DEVICE_ID_MATCH_INT_INFO, \
    	.bInterfaceClass = (cl), \
    	.bInterfaceSubClass = (sc), \
    	.bInterfaceProtocol = (pr)

    根据宏,我们知道,我们设置的支持项包括接口类,接口子类,接口协议三个匹配项。

    主要看看usb_driver中定义的probe函数

    static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
    {
    	struct usb_device *dev = interface_to_usbdev(intf);//由接口获取usb_dev
    	struct usb_host_interface *interface;
    	struct usb_endpoint_descriptor *endpoint;
    	struct usb_mouse *mouse;                           //该驱动私有结构体
    	struct input_dev *input_dev;                       //输入结构体
    	int pipe, maxp;
    	int error = -ENOMEM;
    
    	interface = intf->cur_altsetting;                   //获取设置
    
    	if (interface->desc.bNumEndpoints != 1)             //鼠标端点只有1个
    		return -ENODEV;
    
    	endpoint = &interface->endpoint[0].desc;            //获取端点描述符
    	if (!usb_endpoint_is_int_in(endpoint))              //检查该端点是否是中断输入端点
    		return -ENODEV;
    
    	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);  //建立中断输入端点
    	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));      //端点能传输的最大数据包(Mouse为4个)
    
    	mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);   //分配usb_mouse结构体
    	input_dev = input_allocate_device();                     //分配input设备空间
    	if (!mouse || !input_dev)
    		goto fail1;
    
    	mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma); //分配缓冲区
    	if (!mouse->data)
    		goto fail1;
    
    	mouse->irq = usb_alloc_urb(0, GFP_KERNEL);                              //分配urb
    	if (!mouse->irq)
    		goto fail2;
    
    	mouse->usbdev = dev;         //填充mouse的usb_device结构体
    	mouse->dev = input_dev;      //填充mouse的input结构体
    
    	if (dev->manufacturer)       //复制厂商ID
    		strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));
    
    	if (dev->product) {          //复制产品ID
    		if (dev->manufacturer)
    			strlcat(mouse->name, " ", sizeof(mouse->name));
    		strlcat(mouse->name, dev->product, sizeof(mouse->name));
    	}
    
    	if (!strlen(mouse->name))
    		snprintf(mouse->name, sizeof(mouse->name),
    			 "USB HIDBP Mouse %04x:%04x",
    			 le16_to_cpu(dev->descriptor.idVendor),
    			 le16_to_cpu(dev->descriptor.idProduct));
    
    	usb_make_path(dev, mouse->phys, sizeof(mouse->phys));
    	strlcat(mouse->phys, "/input0", sizeof(mouse->phys)); //获取usb_mouse的设备节点
    
    	input_dev->name = mouse->name;                        //将鼠标名赋给内嵌input结构体
    	input_dev->phys = mouse->phys;                        //将鼠标设备节点名赋给内嵌input结构体
    	usb_to_input_id(dev, &input_dev->id);                 //将usb_driver的支持项拷贝给input
    	input_dev->dev.parent = &intf->dev;
    
    	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);     //支持按键事件和相对坐标事件
    	input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
    		BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);            //表明按键值包括左键、中键和右键
    	input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);      //表明相对坐标包括X坐标和Y坐标
    	input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |
    		BIT_MASK(BTN_EXTRA);                                   //表明除了左键、右键和中键,还支持其他按键
    	input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);                   //表明还支持中键滚轮的滚动值
    
    	input_set_drvdata(input_dev, mouse);                           //将mouse设为input的私有数据
    
    	input_dev->open = usb_mouse_open;                              //input设备的open操作函数
    	input_dev->close = usb_mouse_close;
    
    	usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,
    			 (maxp > 8 ? 8 : maxp),
    			 usb_mouse_irq, mouse, endpoint->bInterval);   //填充urb
    	mouse->irq->transfer_dma = mouse->data_dma;
    	mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;         //使用transfer_dma
    
    	error = input_register_device(mouse->dev);                     //注册input设备
    	if (error)
    		goto fail3;
    
    	usb_set_intfdata(intf, mouse);
    	return 0;
    
    fail3:	
    	usb_free_urb(mouse->irq);
    fail2:	
    	usb_free_coherent(dev, 8, mouse->data, mouse->data_dma);
    fail1:	
    	input_free_device(input_dev);
    	kfree(mouse);
    	return error;
    }

    在探讨probe实现的功能时,我们先看看urb填充函数usb_fill_int_urb

    /**
     * usb_fill_int_urb - macro to help initialize a interrupt urb
     * @urb: pointer to the urb to initialize.
     * @dev: pointer to the struct usb_device for this urb.
     * @pipe: the endpoint pipe
     * @transfer_buffer: pointer to the transfer buffer
     * @buffer_length: length of the transfer buffer
     * @complete_fn: pointer to the usb_complete_t function
     * @context: what to set the urb context to.
     * @interval: what to set the urb interval to, encoded like
     *	the endpoint descriptor's bInterval value.
     *
     * Initializes a interrupt urb with the proper information needed to submit
     * it to a device.
     *
     * Note that High Speed and SuperSpeed interrupt endpoints use a logarithmic
     * encoding of the endpoint interval, and express polling intervals in
     * microframes (eight per millisecond) rather than in frames (one per
     * millisecond).
     *
     * Wireless USB also uses the logarithmic encoding, but specifies it in units of
     * 128us instead of 125us.  For Wireless USB devices, the interval is passed
     * through to the host controller, rather than being translated into microframe
     * units.
     */
    static inline void usb_fill_int_urb(struct urb *urb,
    				    struct usb_device *dev,
    				    unsigned int pipe,
    				    void *transfer_buffer,
    				    int buffer_length,
    				    usb_complete_t complete_fn,
    				    void *context,
    				    int interval)
    {
    	urb->dev = dev;
    	urb->pipe = pipe;
    	urb->transfer_buffer = transfer_buffer;
    	urb->transfer_buffer_length = buffer_length;
    	urb->complete = complete_fn;
    	urb->context = context;
    	if (dev->speed == USB_SPEED_HIGH || dev->speed == USB_SPEED_SUPER)
    		urb->interval = 1 << (interval - 1);
    	else
    		urb->interval = interval;
    	urb->start_frame = -1;
    }
    其实probe主要是初始化usb设备和input设备,终极目标是为了完成urb的提交和input设备的注册。由于注册为input设备类型,那么当用户层open打开设备时候,最终会调用input中的open实现打开,我们看看input中open的实现

    static int usb_mouse_open(struct input_dev *dev)
    {
    	struct usb_mouse *mouse = input_get_drvdata(dev);   //获取私有数据
    
    	mouse->irq->dev = mouse->usbdev;                    //获取utb指针
    	if (usb_submit_urb(mouse->irq, GFP_KERNEL))         //提交urb
    		return -EIO;
    
    	return 0;
    }
    当用户层open打开这个USB鼠标后,我们就已经将urb提交给了USB core,那么根据USB数据处理流程知道,当处理完毕后,USB core会通知USB设备驱动程序,这里我们是响应中断服务程序,这就相当于该URB的回调函数。我们在提交urb时候定义了中断服务程序 usb_mouse_irq,我们跟踪看看

    static void usb_mouse_irq(struct urb *urb)
    {
    	struct usb_mouse *mouse = urb->context;
    	signed char *data = mouse->data;
    	struct input_dev *dev = mouse->dev;
    	int status;
    
    	switch (urb->status) {
    	case 0:			/* success */
    		break;
    	case -ECONNRESET:	/* unlink */
    	case -ENOENT:
    	case -ESHUTDOWN:
    		return;
    	/* -EPIPE:  should clear the halt */
    	default:		/* error */
    		goto resubmit;                             //数据处理没成功,重新提交urb
    	}
    
    	input_report_key(dev, BTN_LEFT,   data[0] & 0x01); //左键
    	input_report_key(dev, BTN_RIGHT,  data[0] & 0x02); //
    	input_report_key(dev, BTN_MIDDLE, data[0] & 0x04); //
    	input_report_key(dev, BTN_SIDE,   data[0] & 0x08); //
    	input_report_key(dev, BTN_EXTRA,  data[0] & 0x10); //
    
    	input_report_rel(dev, REL_X,     data[1]);         //鼠标的水平位移
    	input_report_rel(dev, REL_Y,     data[2]);         //鼠标的垂直位移
    	input_report_rel(dev, REL_WHEEL, data[3]);         //鼠标滚轮的滚动值
    
    	input_sync(dev);                                   //同步事件,完成一次上报
    resubmit:
    	status = usb_submit_urb (urb, GFP_ATOMIC);         //再次提交urb,等待下次响应
    	if (status)
    		err ("can't resubmit intr, %s-%s/input0, status %d",
    				mouse->usbdev->bus->bus_name,
    				mouse->usbdev->devpath, status);
    }

    根据上面的中断服务程序,我们应该知道,系统是周期性地获取鼠标的事件信息,因此在URB回调函数的末尾再次提交URB请求块,这样又会调用新的回调函数,周而复始。在回调函数中提交URB只能是GFP_ATOMIC优先级,因为URB回调函数运行于中断上下文中禁止导致睡眠的行为。而在提交URB 过程中可能会需要申请内存、保持信号量,这些操作或许会导致USB内核睡眠。

    最后我们再看看这个驱动的私有数据mouse的定义

    struct usb_mouse {
    	char name[128];             //名字
    	char phys[64];              //设备节点
    	struct usb_device *usbdev;  //内嵌usb_device设备
    	struct input_dev *dev;      //内嵌input_dev设备
    	struct urb *irq;            //urb结构体
    
    	signed char *data;          //transfer_buffer缓冲区
    	dma_addr_t data_dma;        //transfer _dma缓冲区
    };

    在上面这个结构体中,每一个成员的作用都应该很清楚了,尤其最后两个的使用区别和作用,前面也已经说过。

    如果最终需要测试这个USB鼠标驱动,需要在内核中配置USB支持、对HID接口的支持、对OHCI HCD驱动的支持。另外,将驱动移植到开发板之后,由于采用的是input设备模型,所以还需要开发板带LCD屏才能测试。

    展开全文
  • 作者:刘洪涛,华清远见嵌入式学院高级讲师,ARM ATC授权培训讲师。 参考2.6.14版本中的driver/usb/input/usbmouse.c。鼠标驱动可分为:驱动加载部分、probe部分、open部分、urb回调函数处理部分。
  • Linux2.6内核的USB鼠标驱动开发; 用于linux2.6内核下USB鼠标驱动开发.
  • USB鼠标驱动

    千次阅读 2011-12-03 13:59:46
    二、实现功能:用USB鼠标模拟按键输入,按键左键输入l,按下右键输入s,按下中键为回车,并在LCD屏幕上显示文件信息。 三、实验原理:  在linux内核中有USB总线驱动程序,当有USB设备插入时,USB总线驱动程序会...
    一、实验平台:开发板fs2410,采用三星s3c2410的CPU,linux操作系统。 
    

    二、实现功能:用USB鼠标模拟按键输入,按键左键输入l,按下右键输入s,按下中键为回车,并在LCD屏幕上显示文件信息。

    三、实验原理:

           在linux内核中有USB总线驱动程序,当有USB设备插入时,USB总线驱动程序会检测到该设备,并创建一个struct usb_device结构体,并寻找匹配的驱动程序。

           这里我们只写USB设备驱动程序,使用总线驱动程序的一些函数来获得数据。获取数据后再将这些数据赋予一定的意义(比如右键为s等),再上报给输入子系统。

     

    四、实验现象:

          加载模块后,插入USB鼠标,可以看到有检测到设备的提示信息。再按下鼠标左键、右键,LCD屏幕上出现ls,再按鼠标中键,LCD屏幕上显示文件信息。

     

    五、实验总结:

           USB鼠标也是输入设备,与案件不同的是数据来源。USB鼠标的数据来源是USB总线,而按键的数据来源就是s3c2410的GPIO管脚。得到数据后,写代码的思路和步骤与按键驱动的后部分一样。

           USB设备的数据的传送是通过中断的方式告诉CPU的,而实际上是采用查询的方式,因为所有USB设备都没有主动发送数据的功能,只有USB主机要接收数据时,USB设备才被动发送数据。USB控制器负责了查询了工作,它会对USB设备不断的查询,当获取了有效数据后,USB控制器产生中断告诉CPU有了数据。

    六、示例代码:

    /*驱动代码usbmouse.c*/
    #include <linux/kernel.h>
    #include <linux/slab.h>
    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/usb/input.h>
    #include <linux/hid.h>
    
    static struct usb_device_id uk_id_table [] = {
    	{USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
    		USB_INTERFACE_PROTOCOL_MOUSE)},
    	{ }	/* Terminating entry */
    };
    
    static struct input_dev *uk_dev;
    static unsigned char *buf;
    static unsigned long buf_phys;
    static struct urb *uk_urb;
    
    static void uk_irq(struct urb *urb)
    {
    	static int cnt  = 0;
    	int i;
    	static unsigned char pre_val;
    
    	if ((pre_val & (1<<0)) != (buf[0] & (1<<0)))
    	{
    		input_event(uk_dev, EV_KEY, KEY_L, (buf[0] & (1<<0)) ? 1 : 0);		
    	}
    
    	if ((pre_val & (1<<1)) != (buf[0] & (1<<1)))
    	{
    		input_event(uk_dev, EV_KEY, KEY_S, (buf[0] & (1<<1)) ? 1 : 0);
    	}
    
    	if ((pre_val & (1<<2)) != (buf[0] & (1<<2)))
    	{
    		input_event(uk_dev, EV_KEY, KEY_ENTER, (buf[0] & (1<<2)) ? 1 : 0);
    	}
    
    	if (pre_val != buf[0])
    		input_sync(uk_dev);
    
    	pre_val = buf[0];
    
    	usb_submit_urb (uk_urb, GFP_ATOMIC);
    }
    
    static int uk_probe(struct usb_interface *intf, const struct usb_device_id *id)
    {
    	struct usb_device *dev = interface_to_usbdev(intf);
    	struct usb_host_interface *interface;
    	struct usb_endpoint_descriptor *endpoint;
    	int pipe;
    	int len;
    
    	interface = intf->cur_altsetting;
    	endpoint = &interface->endpoint[0].desc;
    
    	uk_dev = input_allocate_device();
    
    	__set_bit(EV_KEY, uk_dev->evbit);
    	__set_bit(EV_REP, uk_dev->evbit);
    	
    	__set_bit(KEY_L, uk_dev->keybit);
    	__set_bit(KEY_S, uk_dev->keybit);
    	__set_bit(KEY_ENTER, uk_dev->keybit);
    
    	input_register_device(uk_dev);
    
    	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
    
    	buf = usb_buffer_alloc(dev, 4, GFP_ATOMIC, &buf_phys);
    
    	len = 4;
    
    	uk_urb = usb_alloc_urb(0, GFP_KERNEL);
    	usb_fill_int_urb(uk_urb, dev, pipe, buf,len,uk_irq, NULL, endpoint->bInterval);
    	
    	uk_urb->transfer_dma = buf_phys;
    	uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
    
    	usb_submit_urb(uk_urb, GFP_KERNEL);
    		
    	return 0;
    }
    
    static void uk_disconnect (struct usb_interface *intf)
    {
    	struct usb_device *dev = interface_to_usbdev(intf);
    	
    	input_unregister_device(uk_dev);
    	input_free_device(uk_dev);
    
    	usb_kill_urb(uk_urb);
    	usb_buffer_free(dev, 4, buf, buf_phys);
    	usb_free_urb(uk_urb);
    }
    
    static struct usb_driver usbmouse_key_drv = {
    	.name		= "usbmouse_key",	
    	.id_table   = uk_id_table,
    	.probe      = uk_probe,
    	.disconnect = uk_disconnect,
    };
    
    static int usbmouse_as_key_init(void)
    {
    	usb_register(&usbmouse_key_drv);
    	return 0;
    }
    
    static void usbmouse_as_key_exit(void)
    {
    	usb_deregister(&usbmouse_key_drv);
    }
    
    module_init(usbmouse_as_key_init);
    module_exit(usbmouse_as_key_exit);
    
    MODULE_LICENSE("GPL");
    

     

    展开全文
  • 这是一个Linux系统下的USB鼠标驱动,具有通用性强,简单易懂的特点
  • 参考 2.6.14 版本中的 driver/usb/input/usbmouse.c。鼠标驱动可分为几个部分: 驱动加载部分、probe 部分、open 部分、urb 回调函数处理部分。 下文阴影部分为注解。
  • Linux-USB鼠标驱动

    千次阅读 2017-12-19 16:54:46
    参考/drivers/hid/usbhid/usbmouse.c(内核自带的USB鼠标驱动) 1.本节需要用到的宏如下: struct usb_device_id usbmouse_id_table []=USB_INTERFACE_INFO(cl,sc,pr); USB_INTERFACE_INFO()设置usb_driver驱动...
  • PS2USB鼠标驱动源代码

    2010-05-06 10:24:52
    鼠标驱动的实现代码,支持Ps2/USB端口,亦可模拟鼠标执行操作。
  • 鼠标驱动可分为几个部分:驱动加载部分、probe部分、open部分、urb回调函数处理部分。  一、驱动加载部分static int __init usb_mouse_init(void) { int retval = usb_register(&usb_mouse_driver);//注册鼠标...
  • Linux2.6内核的USB鼠标驱动开发.pdf
  • 1、USB HID HID:Human Interface Devices的缩写,即人体学输入设备或人体学接入设备。USB HID则为USB人体学输入设备,最典型的是USB鼠标和USB键盘。...usbkbd.c为usb键盘驱动,usbmouse.c为usb鼠标驱动
  • usb鼠标驱动注解及获取鼠标坐标

    千次阅读 2015-03-26 15:34:24
    USB鼠标驱动 /drivers/hid/usbhid/usbmouse.c 下面我们分析下USB鼠标驱动,鼠标输入HID类型,其数据传输采用中断URB,鼠标端点类型为IN。好了,我们先看看这个驱动的模块加载部分。 static int __init usb_...
  • Windows 系统 usb3.0驱动(不用鼠标只用键盘就可安装
  •  鼠标驱动可分为几个部分:驱动加载部分、probe部分、open部分、urb回调函数处理部分。  下文阴影部分为注解。  一、驱动加载部分  static int __init usb_mouse_init(void)  {  int retval = usb_...
  • linux下基于USB的HID实现了鼠标的驱动,可以用于嵌入式linux下编写自己的鼠标驱动。提高鼠标响应的实时性。
  • USB 鼠标驱动源码分析

    千次阅读 2018-09-04 17:46:23
    kernel:kernel-3.4.39 UHCI: intel, 低速(1.5Mbps)/全速(12Mbps)OHCI: microsoft 低速/全速EHCI: 高速(480Mbps)    USB总线驱动程序的作用: ...3、查找和安装对应的设备驱动程序 4、提供USB读写函...
  • USB鼠标驱动源代码分析

    千次阅读 2013-06-05 16:57:32
    学了URB,试着去分析了一下USB鼠标驱动源代码,更好去理解URB处理流程。 /* * $Id: usbmouse.c,v 1.15 2001/12/27 10:37:41 vojtech Exp $ * * Copyright (c) 1999-2001 Vojtech Pavlik * * USB HIDBP ...
  • 安装完windows后,认鼠标键盘,是因为没有安装usb3.0驱动。处理方法:拷此工具到U盘,然后U盘启动进入winpe,在winpe里运行此工具,提示成功后重启进入windows后就认到鼠标键盘的了。
  • usb hid驱动 - usb鼠标

    千次阅读 2019-09-17 16:22:12
    usb鼠标符合usb hid协议, 一般是单一功能(即一个usb 接口) 该接口下有一个默认的双向控制端点, 以及1个中断in端点 bulk in端点读取数据定义: (具体定义需参考hid report描述符,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 26,090
精华内容 10,436
关键字:

usb鼠标驱动安装不了