• 《驱动学习


    参考/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驱动的id_table成员。

    • cl:接口类,我们USB鼠标为HID类,所以填入0X03,也就是USB_INTERFACE_CLASS_HID
    • sc:接口子类为启动设备,填入USB_INTERFACE_SUBCLASS_BOOT
    • pr:接口协议为鼠标协议,填入USB_INTERFACE_PROTOCOL_MOUSE 
    struct usb_device *dev=interface_to_usbdev(intf);  

      通过usb_ interface接口获取usb_device设备,为后面设置USB数据传输用。

    pipe=usb_rcvintpipe(dev,endpoint);

    创建一个接收(rcv)中断(int)类型的端点管道(pipe),用来端点和数据缓冲区之间的连接,鼠标为接收中断型

    dev: usb_device设备结构体

    endpoint:为端点描述符的成员endpoint->bEndpointAddress   //端点地址

    • 对于控制类型的端点管道使用: usb_sndctrlpipe()/usb_rcvctrlpipe()
    • 对于实时类型的端点管道使用: usb_sndisocpipe()/usb_sndisocpipe()
    • 对于批量类型的端点管道使用: usb_sndbulkpipe()/usb_rcvbulkpipe()

    2.本节需要用到的函数如下:

    usb_deregister(struct usb_driver *driver);

      注册一个usb_driver驱动,然后内核会通过usb_driver的成员.id_table函数匹配一次USB设备,匹配成功就会调用usb_driver的成员.probe函数。

    usb_deregister(struct usb_driver *driver);

      注销一个usb_driver驱动,在出口函数中写。

    *usb_buffer_alloc(struct usb_device *dev,size_t size,gfp_t mem_flags,dma_addr_t *dma);

      分配一个usb缓冲区,该缓存区的物理地址会与虚拟地址的数据一致,分配成功返回一个char型缓冲区虚拟地址

      *dev: usb_device设备结构体

      size:分配的缓冲区大小,这里填端点描述符的成员endpoint->wMaxPacketSize          //端点最大包长

      mem_flags:分配内存的参数,这里填GFP_ATOMIC,表示从不睡眠

      dma:分配成功则会返回一个DMA缓冲区物理地址

      void usb_buffer_free(struct usb_device *dev,size_t size,void *addr,dma_addr_t dma);

      注销分配的usb缓冲区,在usb_driver的disconnect成员函数中使用

      addr:要注销的缓冲区虚拟地址

      dma: 要注销的DMA缓冲区虚拟地址

    struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);

      分配一个urb数据结构体, 分配成功返回一个urb结构体

      urb全称为usb request block,USB传输数据时,就是打包成urb结构体来传输

      iso_packets:表示iso类型的包个数,这里我们不是iso类型包,直接填0

      mem_flags:分配内存的参数,这里填入GFP_KERNEL,正常分配  

      其中urb结构体如下所示:

    struct urb
    {
     ... ...
     struct usb_device *dev;             //指向usb设备
     struct usb_host_endpoint *ep;    //指向端点的数据结构 
     unsigned int pipe;                  //指向端点管道(pipe), 本节的pipe通过usb_rcvintpipe()宏获取
    
     int status;                                 //状态,当status==0,表示数据被成功地收到/发送
     
     unsigned int transfer_flags;     //传输状态
     ... ...
    /*以下两个缓冲区通过usb_buffer_alloc ()函数获取 */
    //urb结构体默认的transfer_flags是URB_NO_SETUP_DMA_MAP ,也就是说没有提供DMA的缓冲区
    //就会使用transfer_buffer虚拟地址缓冲区来当缓冲区
    //当支持DMA缓冲区时,就需要手动设置transfer_flags =URB_NO_TRANSFER_DMA_MAP,并手动设置transfer_dma等于获取到的DMA物理地址
    
     void *transfer_buffer;                //虚拟缓冲区
     dma_addr_t transfer_dma;          //DMA物理缓冲区 
    ... ...
    };
    void usb_free_urb(struct urb *urb);

      释放申请的urb,在usb_driver的disconnect成员函数中使用。

    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数据结构体

      针对批量型端点的urb使用usb_fill_bulk_urb()
      针对控制型端点的urb使用usb_fill_control_urb()
      针对等时型端点的urb  需要手动初始化。

      urb:指向要初始化的urb

      dev:指向要传输的usb设备

      pipe:要传输的端点管道, 本节的pipe通过usb_rcvintpipe()宏获取

      transfer_buffer:指向要传输数据的虚拟地址缓冲区

      buffer_length:数据大小, 这里填端点描述符的成员endpoint->wMaxPacketS //端点最大包长

      complete_fn:数据传输完成后产生的中断函数

      context:会放在urb->context结构成员中,用来给中断函数用,本节不需要,填NULL即可

      interval:间隔时间,表示间隔多少时间读一次数据,填入endpoint-> bInterval即可

    int usb_submit_urb(struct urb *urb,gfp_t mem_flags);

      提交urb到内核,初始化urb和中断函数退出时,都要重新提交一次,告诉内核初始化内存缓存等。

    void usb_kill_urb(struct urb *urb);

      杀掉urb,在usb_driver的disconnect成员函数中使用。

    3.步骤如下:

    首先先定义全局变量:usb_driver结构体,input_dev指针结构体 ,虚拟地址缓存区,DMA地址缓存区

    3.1在入口函数中

    1)通过usb_register()函数注册usb_driver结构体

    3.2在usb_driver的probe函数中

    1)分配一个input_dev结构体

    2)设置input_dev支持L、S、回车、3个按键事件

    3)注册input_dev结构体

    4)设置USB数据传输:

     ->4.1)通过usb_rcvintpipe()创建一个接收中断类型的端点管道,用来端点和数据缓冲区之间的连接

     ->4.2)通过usb_buffer_alloc()申请USB缓冲区

     ->4.3)申请并初始化urb结构体,urb:用来传输数据

     ->4.4) 因为我们2440支持DMA,所以要告诉urb结构体,使用DMA缓冲区地址

     ->4.5)使用usb_submit_urb()提交urb

    3.3在鼠标中断函数中

    1)判断缓存区数据是否改变,若改变则上传鼠标事件

    2)使用usb_submit_urb()提交urb

    3.4.在usb_driver的disconnect函数中

    1)通过usb_kill_urb()杀掉提交到内核中的urb

    2)释放urb

    3)释放USB缓存区

    4)注销input_device,释放input_device

    3.5在出口函数中

    1)通过usb_deregister ()函数注销usb_driver结构体

    #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 *myusb_mouse_dev;              //input_dev 
    
    static char *myusb_mouse_buf;                         //虚拟地址缓存区
    static dma_addr_t myusb_mouse_phyc;                     //DMA缓存区;
    
    static __le16 myusb_mouse_size;                        //数据包长度
    
    static struct urb  *myusb_mouse_urb;                     //urb
    
    
    static void myusb_mouse_irq(struct urb *urb)               //鼠标中断函数
    {
       static char buf1=0;
       //for(i=0;i<myusb_mouse_size;i++)
      // printk("%02x  ",myusb_mouse_buf[i]);
     //  printk("
    ");
    
    
        /*bit 1-左右中键  0X01:左键   0X02:右键     0x04:中键   */
         if((buf1&(0X01))    !=      (myusb_mouse_buf[1]&(0X01)))
            {         
                    input_report_key(myusb_mouse_dev, KEY_L, buf1&(0X01)? 1:0);
                    input_sync(myusb_mouse_dev);  
            }
         if((buf1&(0X02))    !=    (myusb_mouse_buf[1]&(0X02)))
            {
                    input_report_key(myusb_mouse_dev, KEY_S, buf1&(0X02)? 1:0);
                    input_sync(myusb_mouse_dev);  
            }
         if((buf1&(0X04))    !=    (myusb_mouse_buf[1]&(0X04))  )
              {
                    input_report_key(myusb_mouse_dev, KEY_ENTER, buf1&(0X04)? 1:0);
                    input_sync(myusb_mouse_dev);  
              }
    
         buf1=myusb_mouse_buf[1];                                   //更新数据
    
         usb_submit_urb(myusb_mouse_urb, GFP_KERNEL);
    }
    
    
    static int myusb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
    {
           struct usb_device *dev = interface_to_usbdev(intf);             //设备
           struct usb_endpoint_descriptor *endpoint;                            
           struct usb_host_interface *interface;                         //当前接口
           int pipe;                                                   //端点管道
    
           interface=intf->cur_altsetting;                                                                   
           endpoint = &interface->endpoint[0].desc;                                    //当前接口下的端点描述符
       
    
           printk("VID=%x,PID=%x
    ",dev->descriptor.idVendor,dev->descriptor.idProduct); //打印VID,PID
     
    
     /*   1)分配一个input_dev结构体  */
           myusb_mouse_dev=input_allocate_device();
    
     
     /*   2)设置input_dev支持L、S,回车、3个按键事件*/
           set_bit(EV_KEY, myusb_mouse_dev->evbit);
           set_bit(EV_REP, myusb_mouse_dev->evbit);        //支持重复按功能
    
           set_bit(KEY_L, myusb_mouse_dev->keybit);       
           set_bit(KEY_S, myusb_mouse_dev->keybit);
           set_bit(KEY_ENTER, myusb_mouse_dev->keybit);    
    
     /*   3)注册input_dev结构体*/
           input_register_device(myusb_mouse_dev);
    
     
     /*   4)设置USB数据传输 */
     /*->4.1)通过usb_rcvintpipe()创建一个端点管道*/
           pipe=usb_rcvintpipe(dev,endpoint->bEndpointAddress); 
    
      /*->4.2)通过usb_buffer_alloc()申请USB缓冲区*/
           myusb_mouse_size=endpoint->wMaxPacketSize;
           myusb_mouse_buf=usb_buffer_alloc(dev,myusb_mouse_size,GFP_ATOMIC,&myusb_mouse_phyc);
    
      /*->4.3)通过usb_alloc_urb()和usb_fill_int_urb()申请并初始化urb结构体 */
           myusb_mouse_urb=usb_alloc_urb(0,GFP_KERNEL);
    
           usb_fill_int_urb (myusb_mouse_urb,                       //urb结构体
                                     dev,                           //usb设备
                                     pipe,                          //端点管道
                                     myusb_mouse_buf,               //缓存区地址
                                     myusb_mouse_size,              //数据长度
                                     myusb_mouse_irq,               //中断函数
                                     0,
                                     endpoint->bInterval);          //中断间隔时间
    
    
      /*->4.4) 因为我们2440支持DMA,所以要告诉urb结构体,使用DMA缓冲区地址*/
            myusb_mouse_urb->transfer_dma   =myusb_mouse_phyc;             //设置DMA地址
            myusb_mouse_urb->transfer_flags   =URB_NO_TRANSFER_DMA_MAP;     //设置使用DMA地址
    
      /*->4.5)使用usb_submit_urb()提交urb*/
            usb_submit_urb(myusb_mouse_urb, GFP_KERNEL);
           return 0;
    }
    
    static void myusb_mouse_disconnect(struct usb_interface *intf)
    {
        struct usb_device *dev = interface_to_usbdev(intf);                  //设备
    
        usb_kill_urb(myusb_mouse_urb);
        usb_free_urb(myusb_mouse_urb);
        usb_buffer_free(dev, myusb_mouse_size, myusb_mouse_buf,myusb_mouse_phyc);
    
        input_unregister_device(myusb_mouse_dev);         //注销内核中的input_dev
        input_free_device(myusb_mouse_dev);             //释放input_dev
    } 
    
    static struct usb_device_id myusb_mouse_id_table [] = {
           { USB_INTERFACE_INFO(
                  USB_INTERFACE_CLASS_HID,                 //接口类:hid类
                  USB_INTERFACE_SUBCLASS_BOOT,             //子类:启动设备类
                  USB_INTERFACE_PROTOCOL_MOUSE) },      //USB协议:鼠标协议
    };
    
    
    static struct usb_driver myusb_mouse_drv = {
           .name            = "myusb_mouse",
           .probe           = myusb_mouse_probe,                           
           .disconnect     = myusb_mouse_disconnect,
           .id_table  = myusb_mouse_id_table,
    };
     
    /*入口函数*/
    static int myusb_mouse_init(void)
    {
           usb_register(&myusb_mouse_drv);
           return 0;
    }
    
    /*出口函数*/
    static void myusb_mouse_exit(void)
    {
           usb_deregister(&myusb_mouse_drv);
    }
     
    module_init(myusb_mouse_init);
    module_exit(myusb_mouse_exit);
    MODULE_LICENSE("GPL");

    4.测试运行

    5.1 重新设置编译内核(去掉默认的hid_USB驱动)

      make menuconfig ,进入menu菜单重新设置内核参数:

      进入-> Device Drivers -> HID Devices 

      <> USB Human Interface Device (full HID) support //hid:人机交互的USB驱动,比如鼠标,键盘等

      然后make uImage 编译内核

      将新的触摸屏驱动模块放入nfs文件系统目录中

    5.2然后烧写内核,装载触摸屏驱动模块

      如下图,当我们插上USB鼠标时,可以看到该VID和PID,和电脑上的鼠标的参数一样

     

    5.3使用hexdump命令来调试

    (hexdump命令调试代码详解地址:http://www.cnblogs.com/lifexy/p/7553550.html)

     5.4 使用tty1进程测试

  • 相关阅读:
    JAVA面试题
    Io流
    初识线程池
    理解事务的4种隔离级别
    简单认识Git与GitHub
    JAVA自动装箱和拆箱
    代码块以及他们的执行顺序
    反射机制
    java Excel表格
    访问修饰符的含义分析
  • 原文地址:https://www.cnblogs.com/zhuangquan/p/11685453.html
Copyright © 2020-2023  润新知