8 Linux usbmouse设备驱动程序

試著忘記壹切 提交于 2019-12-02 05:45:30

前一章节对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("\n");  
  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");  

测试截图如下:

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!