• 9 Linux usbmouse设备驱动程序


    前一章节对linux内核中USB驱动程序的框架进行了分析,这一节以USB鼠标为对象,编写USB鼠标驱动程序。

    实验内容:编写USB鼠标设备驱动程序。并将USB鼠标左键定义为"L"功能,右键定义为"S"功能,中间滚轮键定义为"ENTER"功能,方便测试。

    参考内核中/driver/hid/usbhid/usbmouse.c文件。

    从入口函数usbmouse_as_key_init开始。按照之前编写字符驱动程序的惯例,入口函数中需要实现usb_driver结构体的分配,配置、注册以及和硬件相关的操作。

    那么,首先需要定义一个usb_driver结构体。

    其中probe函数是整个驱动程序的重点,后面再讲。disconnect函数是当设备断开连接时调用,后面再讲。

    id_table用于保存usb设备的id信息,其结构体定义如下:

    这里usbmouse_as_key_id_table的定义如下:

    定义中使用宏USB_INTERFACE_INFO,具体定义如下:

    对宏USB_INTERFACE_INFO进行展开,usbmouse_as_key_id_table [] ={

    {

    .match_flags = USB_DEVICE_ID_MATCH_INT_INFO,

    .bInterfaceClass = USB_INTERFACE_CLASS_HID,    

    .bInterfaceSubClass = USB_INTERFACE_SUBCLASS_BOOT,

    .bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE },

         },即我们编写的USB鼠标驱动的支持接口类是USB_INTERFACE_CLASS_HID0x03,支持的接口子类是USB_INTERFACE_SUBCLASS_BOOT0x01,接口协议为USB_INTERFACE_PROTOCOL_MOUSE0x03

    probe函数是整个驱动的核心,实现设备的分配、设置、注册以及硬件相关的操作。

    1)分配输入设备

    这里对USB鼠标是按照按键类输入事件进行处理的,因此需要分配一个输入设备uk_dev

    static struct input_dev *uk_dev;

    uk_dev = input_allocate_device();

    2)设置事件

    USB鼠标产生按键类事件,并且支持长按重复操作。左键按下对应"L"功能,邮件按下对应"S"功能,中键对应"ENTER"功能。

    3)注册

    调用input_register_device函数注册输入设备uk_dev

    4)与硬件相关的操作

    USB设备驱动的硬件操作与之前的LCD、按键等有所区别,不是直接操作寄存器的,这里是调用USB总线驱动程序的接口来实现数据的访问。

    urblinux内核中USB驱动程序中实现数据传输的一种数据结构,全称USB request block,其定义如下:

    1. struct urb  
    2. {  
    3.     /* private: usb core and host controller only fields in the urb */  
    4.     struct kref kref;       /* reference count of the URB */  
    5.     spinlock_t lock;        /* lock for the URB */  
    6.     void *hcpriv;           /* private data for host controller */  
    7.     atomic_t use_count;     /* concurrent submissions counter */  
    8.     u8 reject;          /* submissions will fail */  
    9.     
    10.     /* public: documented fields in the urb that can be used by drivers */  
    11.     struct list_head urb_list;  /* list head for use by the urb's 
    12.                      * current owner */  
    13.     struct usb_device *dev;     /* (in) pointer to associated device */  
    14.     unsigned int pipe;      /* (in) pipe information */  
    15.     int status;         /* (return) non-ISO status */  
    16.     unsigned int transfer_flags;    /* (in) URB_SHORT_NOT_OK | ...*/  
    17.     void *transfer_buffer;      /* (in) associated data buffer */  
    18.     dma_addr_t transfer_dma;    /* (in) dma addr for transfer_buffer */  
    19.     int transfer_buffer_length; /* (in) data buffer length */  
    20.     int actual_length;      /* (return) actual transfer length */  
    21.     unsigned char *setup_packet;    /* (in) setup packet (control only) */  
    22.     dma_addr_t setup_dma;       /* (in) dma addr for setup_packet */  
    23.     int start_frame;        /* (modify) start frame (ISO) */  
    24.     int number_of_packets;      /* (in) number of ISO packets */  
    25.     int interval;           /* (modify) transfer interval 
    26.                      * (INT/ISO) */  
    27.     int error_count;        /* (return) number of ISO errors */  
    28.     void *context;          /* (in) context for completion */  
    29.     usb_complete_t complete;    /* (in) completion routine */  
    30.     struct usb_iso_packet_descriptor iso_frame_desc[0];  
    31.                     /* (in) ISO ONLY */  
    32. };  

    使用urb实现数据传输的过程如下:

    1. 分配一个urb结构体

    调用usb_alloc_urb函数,分配一个uk_urb结构体空间,并初始化结构体。

    1. 设置urb结构体

    USB2.0中定义了控制、中断、批量、同步四种传输方式。Linux内核中对应这四种传输方式定义了对应的urb的接口函数。

    static inline void usb_fill_control_urb (struct urb *urb,

                         struct usb_device *dev,

                         unsigned int pipe,

                         unsigned char *setup_packet,

                         void *transfer_buffer,

                         int buffer_length,

                         usb_complete_t complete_fn,

                         void *context)

     

    static inline void usb_fill_bulk_urb (struct urb *urb,

                     struct usb_device *dev,

                     unsigned int pipe,

                     void *transfer_buffer,

                     int buffer_length,

                     usb_complete_t complete_fn,

                     void *context)

     

    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)

    usb鼠标因其数据量少,对传输的实时性要求较高,因此选用中断传输的方式。这里重点讲解usb_fill_int_urb函数。

    urb:需要设置的urb结构体;

    devurb要发送到的usb设备;

    pipeurb要被发送到的USB设备的特定端点,使用usb_rcvintpipe函数或者usb_sndintpipe函数创建端点;

    transfer_buffer:指向发送数据或者接收数据的虚拟缓冲区;

    buffer_lengthtransfer_buffer数据缓冲区的长度;

    complete_fn:指向urb完成时被调用的完成处理函数;

    context:完成处理函数的上下文;

    intervalurb调度的间隔时间。

    1. 提交urb

    调用usb_submit_urb函数,将urb提交到usb主机控制器驱动程序。

    urb完成处理函数usbmouse_as_key_irq中执行如下操作:

    1)判断按键是否是否发生变化,若变化,则上传对应的按键事件。

    2)重新提交urb

    usbmouse_as_key_disconnect函数中完成如下操作:

    1. 调用usb_kill_urb函数杀死urb
    2. 调用usb_free_urb函数释放urb空间;
    3. 调用usb_buffer_free释放缓冲区;
    4. 调用input_unregister_device,卸载设备;
    5. 调用input_free_device,释放dev设备。

    代码如下:

    1. #include <linux/kernel.h>  
    2. #include <linux/slab.h>  
    3. #include <linux/module.h>  
    4. #include <linux/init.h>  
    5. #include <linux/usb/input.h>  
    6. #include <linux/hid.h>  
    7.     
    8. static struct input_dev *uk_dev;  
    9. static char * usb_buf;  
    10. static dma_addr_t usb_buf_phys;  
    11. static int len;  
    12. static struct urb *uk_urb;  
    13.     
    14. static struct usb_device_id usbmouse_as_key_id_table [] = {  
    15.     { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,  
    16.         USB_INTERFACE_PROTOCOL_MOUSE) },  
    17. };  
    18.     
    19. static void usbmouse_as_key_irq(struct urb *urb)  
    20. {  
    21. static unsigned char pre_val ;  
    22.     
    23. #if 0  
    24.     int i;  
    25.     static int cnt = 0;  
    26.     printk("data cnt %d:",++cnt);  
    27.     
    28.     for(i = 0; i < len; i++)  
    29.     {  
    30.         printk("%02x", usb_buf[i]);  
    31.     }  
    32.     printk(" ");  
    33. #endif  
    34.     
    35.     /*USB鼠标数据含义 
    36.      * data[0]: bit0-左键 1-按下,0-松开 
    37.      *          bit1-右键 1-按下,0-松开 
    38.      *          bit2-中键 1-按下,0-松开 
    39.      */  
    40.      if((pre_val & (1<<0)) != (usb_buf[0] & (1<<0)))  
    41.      {  
    42.         input_event(uk_dev, EV_KEY, KEY_L, (usb_buf[0] & (1<<0))?1:0);  
    43.         input_sync(uk_dev);  
    44.      }  
    45.      if((pre_val & (1<<1)) != (usb_buf[0] & (1<<1)))  
    46.      {  
    47.         input_event(uk_dev, EV_KEY, KEY_S, (usb_buf[0] & (1<<1))?1:0);  
    48.         input_sync(uk_dev);  
    49.      }  
    50.      if((pre_val & (1<<2)) != (usb_buf[0] & (1<<2)))  
    51.      {  
    52.         input_event(uk_dev, EV_KEY, KEY_ENTER, (usb_buf[0] & (1<<2))?1:0);  
    53.         input_sync(uk_dev);  
    54.      }  
    55.          
    56.      pre_val = usb_buf[0];  
    57.     
    58.     /* 重新提交urb */  
    59.     usb_submit_urb(uk_urb, GFP_KERNEL);   
    60. }  
    61.     
    62. static int usbmouse_as_key_probe(struct usb_interface *intf, const struct usb_device_id *id)  
    63. {  
    64.     struct usb_device *dev = interface_to_usbdev(intf);  
    65.     struct usb_host_interface *interface;  
    66.     struct usb_endpoint_descriptor *endpoint;  
    67.     int pipe;  
    68.     
    69.     interface = intf->cur_altsetting;  
    70.     endpoint = &interface->endpoint[0].desc;  
    71.         
    72.     /* a、分配一个input_dev */  
    73.     uk_dev = input_allocate_device();  
    74.         
    75.     /* b、设置 */  
    76.     /* b.1 能产生哪类事件 */  
    77.     set_bit(EV_KEY, uk_dev->evbit);  
    78.     set_bit(EV_REP, uk_dev->evbit);  
    79.     /* b.2 能产生哪些事件 */  
    80.     set_bit(KEY_L, uk_dev->keybit);  
    81.     set_bit(KEY_S, uk_dev->keybit);  
    82.     set_bit(KEY_ENTER, uk_dev->keybit);  
    83.         
    84.     /* c、注册 */  
    85.     input_register_device(uk_dev);  
    86.         
    87.     /* d、硬件相关的操作 */  
    88.     /* 数据传输三要素源、目的、长度 */  
    89.     /* :  */  
    90.     pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);  
    91.     /* 长度 */  
    92.     len = endpoint->wMaxPacketSize;  
    93.     /* 目的 */  
    94.     usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys);  
    95.     
    96.     /* 分配urb: usb request block */  
    97.     uk_urb = usb_alloc_urb(0, GFP_KERNEL);  
    98.     /* 使用"数据传输三要素"设置urb */  
    99.     usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_as_key_irq, NULL, endpoint->bInterval);  
    100.     uk_urb->transfer_dma = usb_buf_phys;  
    101.     uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;  
    102.     
    103.     /* 使用urb */  
    104.     usb_submit_urb(uk_urb, GFP_KERNEL);   
    105.         
    106.     return 0;  
    107. }  
    108.     
    109.     
    110. static void usbmouse_as_key_disconnect(struct usb_interface *intf)  
    111. {  
    112.     struct usb_device *dev = interface_to_usbdev(intf);  
    113.         
    114.     usb_kill_urb(uk_urb);  
    115.     usb_free_urb(uk_urb);  
    116.     usb_buffer_free(dev, len, usb_buf, usb_buf_phys);  
    117.     input_unregister_device(uk_dev);  
    118.     input_free_device(uk_dev);  
    119. }  
    120.     
    121.     
    122. static struct usb_driver usbmouse_as_key_driver = {  
    123.     .name       = "usbmouse_as_key",  
    124.     .probe      = usbmouse_as_key_probe,  
    125.     .disconnect = usbmouse_as_key_disconnect,  
    126.     .id_table   = usbmouse_as_key_id_table,  
    127. };  
    128.     
    129. static int usbmouse_as_key_init()  
    130. {  
    131.     usb_register(&usbmouse_as_key_driver);  
    132.     return 0;  
    133. }  
    134.     
    135. static void usbmouse_as_key_exit()  
    136. {  
    137.     usb_deregister(&usbmouse_as_key_driver);  
    138. }  
    139.     
    140. module_init(usbmouse_as_key_init);  
    141. module_exit(usbmouse_as_key_exit);  
    142.     
    143. MODULE_LICENSE("GPL");  

    测试截图如下:

  • 相关阅读:
    理解Linux虚拟文件系统VFS
    Linux进程间通信(IPC)
    为 区域添加 Tag
    html 模板 swig 预编译插件 grunt-swig-precompile
    如何开发 Grunt 插件
    Web开发常见的漏洞
    CSS实现不固定宽度和高度的自动居中
    Sublime Text 前端插件推荐
    JavaScript 防止事件冒泡
    HTML标签篇
  • 原文地址:https://www.cnblogs.com/beijiqie1104/p/11732601.html
Copyright © 2020-2023  润新知