目录
1、usb硬件框架
2、linux的usb驱动的软件框架(总线部分负责的工作、设备驱动部分负责的工作)
3、usb设备插入usb主机到进入usb设备驱动probe函数的大致流程
4、以usb鼠标为例说明usb设备驱动怎么和usb设备通信
5、以鼠标驱动为例说明怎么编写一个usb设备驱动程序
1、usb硬件框架
usb设备的硬件框架如下图所示,有4根线D-、D+、VCC和GND,usb设备接入usb主机后,引起主机内的D-或D+电位变化,从而通知usb主机有设备接入
2、linux的usb驱动的软件框架(总线部分负责的工作、设备驱动部分负责的工作)
类似spi设备驱动,在linux内核中,将usb的设备驱动分为usb总线驱动和usb设备驱动两个部分,usb总线驱动是内核中usb设备驱动公有的部分,负责usb协议具体的实现,比如热插拔、设备发现、设备和驱动的匹配以及提供数据读写接口给usb设备驱动程序;总的来说,usb总线驱动程序负责如下3个功能:(1)负责识别USB设备,(2)给USB设备找到对应的驱动程序,(3)提供usb数据读写函数;
具体的usb设备驱动程序负责使用usb总线驱动提供的接口读写usb设备。
3、usb设备插入usb主机到进入usb设备驱动probe函数的大致流程,调用probe前的动作都在usb总线驱动程序中完成
1)usb设备接入usb主机,引起主机D-或D+的电位跳变,产生中断,进入hub_irq函数;
2)给新设备分配地址 choose_address;
3)把设备地址告诉usb设备 hub_set_address;
4)获取usb设备的设备描述符 get_hub_descriptor;
5)把所有的描述符读出来并解析;
6)创建设备device;
7)把设备device放入usb总线usb_bus_type的dev链表,从usb_bus_type的driver链表里取出usb_driver,把usb_interface和usb_driver的id_table比较,如果能匹配,调用usb_driver的probe函数。
4、以usb鼠标为例说明usb设备驱动怎么和usb设备通信
1)usb通信是通过端点进行的,端点标识了usb设备具备的通信方式,每种设备都不一样,比如usb鼠标只有一个中断类型的端点,所以通信的第一步是获取usb设备的端点信息;
比如usb鼠标:
1 /* 端点个数保存在接口描述符里面 */ 2 struct usb_host_interface *interface; 3 interface = intf->cur_altsetting; 4 interface->desc.bNumEndpoints 5 /* 端点描述符 */ 6 endpoint = &interface->endpoint[0].desc
2)对通信要使用的端点进行设置,将usb设备编号和端点地址进行关联
1 pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
3)分配usb通信的buf
1 len = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); 2 usb_buf = usb_alloc_coherent(dev, len, GFP_ATOMIC, &usb_buf_phys);
4)分配,使用端点、usb通信buf、物理地址以及中断函数填充urb
1 uk_urb = usb_alloc_urb(0, GFP_KERNEL); 2 usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, 3 len, usbmouse_as_key_irq, NULL, endpoint->bInterval); 4 uk_urb->transfer_dma = usb_buf_phys; 5 uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
5)提交urb,这个步骤完成后,usb主机会不断查询usb设备端点是否有数据,有数据就给cpu产生中断,从而调用urb绑定的中断处理函数usbmouse_as_key_irq,每次中断处理后,都要在中断函数中重新提交urb
1 usb_submit_urb(uk_urb, GFP_KERNEL)
5、以鼠标驱动为例说明怎么编写一个usb设备驱动程序
1)创建并注册一个usb_driver结构体,以和usb_device匹配
2)获取usb设备的端点信息,创建usb设备请求块urb
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 /* 9 * Version Information 10 */ 11 #define DRIVER_VERSION "v1.6" 12 #define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>" 13 #define DRIVER_DESC "USB HID Boot Protocol mouse driver" 14 15 static struct input_dev *uk_dev; 16 static char *usb_buf; 17 static dma_addr_t usb_buf_phys; 18 static int len; 19 static struct urb *uk_urb; 20 21 static void usbmouse_as_key_irq(struct urb *urb) 22 { 23 int i; 24 static int cnt = 0; 25 26 printk("data cnt %d : ", ++cnt); 27 for (i = 0; i < 4; i++) { 28 printk("%02X ", usb_buf[i]); 29 } 30 printk(" "); 31 32 /* 重新提交urb */ 33 usb_submit_urb(urb, GFP_ATOMIC); 34 } 35 36 static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id) 37 { 38 /* 39 * usb_interface表示逻辑上的设备 40 */ 41 struct usb_device *dev = interface_to_usbdev(intf); 42 struct usb_host_interface *interface; 43 struct usb_endpoint_descriptor *endpoint; 44 int error; 45 int pipe; 46 47 interface = intf->cur_altsetting; 48 49 /* 通过端点数判断是否是鼠标,鼠标设备的端点只有一个,且是中断类型 */ 50 if (interface->desc.bNumEndpoints != 1) 51 return -ENODEV; 52 53 endpoint = &interface->endpoint[0].desc; 54 if (!usb_endpoint_is_int_in(endpoint)) 55 return -ENODEV; 56 57 /* a. 分配一个input_dev */ 58 uk_dev = input_allocate_device(); 59 /* b. 设置 */ 60 /* b.1 能产生哪类事件 */ 61 set_bit(EV_KEY, uk_dev->evbit); 62 set_bit(EV_REP, uk_dev->evbit); 63 /* b.2 能产生哪些事件 */ 64 set_bit(KEY_L, uk_dev->keybit); 65 set_bit(KEY_S, uk_dev->keybit); 66 set_bit(KEY_ENTER, uk_dev->keybit); 67 //input_set_capability(uk_dev, EV_KEY, KEY_HOME); 68 /* c. 注册 */ 69 error = input_register_device(uk_dev); 70 if (error) { 71 dev_err(dev, "Unable to register uk_dev device, error: %d ", 72 error); 73 return error; 74 } 75 76 /* d. 硬件相关的操作 */ 77 /* 数据传输3要素:源,目的,长度 */ 78 /* 源:USB设备的某个端点 */ 79 pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); 80 81 /* 长度 */ 82 len = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); //endpoint->wMaxPacketSize; 83 printk("len = %d ", len); 84 // len = 8; 85 86 /* 目的 */ 87 usb_buf = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &usb_buf_phys); 88 89 /* 使用3要素 */ 90 /* 分配usb request block */ 91 uk_urb = usb_alloc_urb(0, GFP_KERNEL); 92 /* 使用3要素设置urb */ 93 usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, 94 len, usbmouse_as_key_irq, NULL, endpoint->bInterval); 95 uk_urb->transfer_dma = usb_buf_phys; 96 uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 97 98 /* 使用urb */ 99 usb_submit_urb(uk_urb, GFP_KERNEL); 100 printk("found usbmouse11! "); 101 102 return 0; 103 } 104 105 static void usb_mouse_disconnect(struct usb_interface *intf) 106 { 107 printk("usb_mouse_disconnect usbmouse! "); 108 usb_kill_urb(uk_urb); 109 usb_free_urb(uk_urb); 110 usb_free_coherent(interface_to_usbdev(intf), len, usb_buf, usb_buf_phys); 111 input_free_device(uk_dev); 112 } 113 114 static const struct usb_device_id usb_mouse_id_table[] = { 115 { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, 116 USB_INTERFACE_PROTOCOL_MOUSE) }, 117 //{USB_DEVICE(0x1234, 0x5678)}, 118 { } /* Terminating entry */ 119 }; 120 121 static struct usb_driver usb_mouse_driver = { 122 .name = "usbmouse", 123 .probe = usb_mouse_probe, 124 .disconnect = usb_mouse_disconnect, 125 .id_table = usb_mouse_id_table, 126 }; 127 128 module_usb_driver(usb_mouse_driver); 129 130 MODULE_AUTHOR(DRIVER_AUTHOR); 131 MODULE_DESCRIPTION(DRIVER_DESC); 132 MODULE_LICENSE("GPL");
参考
韦东山老师的usb设备驱动视频