一、USB固件和USB傳輸方式
USB固件:
USB固件一般不需要我們編寫,在此不做程序分析。
USB固件中包含USB設備的出廠信息,如廠商ID、產品ID、主版本號和次版本號等。這就是為什么當我們把U盤插入USB口的時候主機可以知道這是一個U盤設備。
除包含出廠信息外,固件中還包含處理USB協議和設備讀寫操作的程序,如將數據從設備發送到總線上或從總線中將數據讀取到設備中。驅動程序只是將USB規范定義的請求發送給固件程序,固件程序負責將數據寫入設備中。USB規范定義了USB設備間的通信方式。
USB結構:
USB是主從結構的。所有的USB傳輸,都是從USB主機這方發起,USB設備沒有“主動”通知USB主機的能力。
如USB鼠標滑動一下產生了數據,但它沒有能力通知PC機來讀數據,只能被動地等待PC來讀。
USB傳輸類型:
1. 控制傳輸:可靠,時間有保證。如:USB設備的識別過程
2. 批量傳輸: 可靠, 時間沒有保證。如:U盤
3. 中斷傳輸:可靠,實時。如:USB鼠標
4. 實時傳輸:不可靠,實時。如:USB攝像頭
二、Linux USB設備驅動模型
USB設備驅動模型采用總線設備驅動模型,所以它具有三部分:
1. USB Bus
2. USB Device
3. USB Driver
usb_bus和usb_bus_type:
每一條USB總線對應一個struct usb_bus。

1 struct usb_bus { 2 struct device *controller; /* host/master side hardware */ 3 int busnum; /* Bus number (in order of reg) */ 4 const char *bus_name; /* stable id (PCI slot_name etc) */ 5 u8 uses_dma; /* Does the host controller use DMA? */ 6 u8 uses_pio_for_control; /* 7 * Does the host controller use PIO 8 * for control transfers? 9 */ 10 u8 otg_port; /* 0, or number of OTG/HNP port */ 11 unsigned is_b_host:1; /* true during some HNP roleswitches */ 12 unsigned b_hnp_enable:1; /* OTG: did A-Host enable HNP? */ 13 unsigned sg_tablesize; /* 0 or largest number of sg list entries */ 14 15 int devnum_next; /* Next open device number in 16 * round-robin allocation */ 17 18 struct usb_devmap devmap; /* device address allocation map */ 19 struct usb_device *root_hub; /* Root hub */ 20 struct usb_bus *hs_companion; /* Companion EHCI bus, if any */ 21 struct list_head bus_list; /* list of busses */ 22 23 int bandwidth_allocated; /* on this bus: how much of the time 24 * reserved for periodic (intr/iso) 25 * requests is used, on average? 26 * Units: microseconds/frame. 27 * Limits: Full/low speed reserve 90%, 28 * while high speed reserves 80%. 29 */ 30 int bandwidth_int_reqs; /* number of Interrupt requests */ 31 int bandwidth_isoc_reqs; /* number of Isoc. requests */ 32 33 #ifdef CONFIG_USB_DEVICEFS 34 struct dentry *usbfs_dentry; /* usbfs dentry entry for the bus */ 35 #endif 36 37 #if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE) 38 struct mon_bus *mon_bus; /* non-null when associated */ 39 int monitored; /* non-zero when monitored */ 40 #endif 41 };
usb_bus_type定義了一種usb總線類型。在軟件層次中,usb_bus屬於usb_bus_type。
usb_bus_type通過bus_register(&usb_bus_type)函數向內核注冊usb總線。
struct bus_type usb_bus_type = { .name = "usb", .match = usb_device_match, .uevent = usb_uevent, .pm = &usb_bus_pm_ops, };
其中,match()函數通過id_table來匹配device和driver。

1 static int usb_device_match(struct device *dev, struct device_driver *drv) 2 { 3 /* devices and interfaces are handled separately */ 4 if (is_usb_device(dev)) { 5 6 /* interface drivers never match devices */ 7 if (!is_usb_device_driver(drv)) 8 return 0; 9 10 /* TODO: Add real matching code */ 11 return 1; 12 13 } else if (is_usb_interface(dev)) { 14 struct usb_interface *intf; 15 struct usb_driver *usb_drv; 16 const struct usb_device_id *id; 17 18 /* device drivers never match interfaces */ 19 if (is_usb_device_driver(drv)) 20 return 0; 21 22 intf = to_usb_interface(dev); 23 usb_drv = to_usb_driver(drv); 24 25 /* 匹配interface和driver->id_table */ 26 id = usb_match_id(intf, usb_drv->id_table); 27 if (id) 28 return 1; 29 30 id = usb_match_dynamic_id(intf, usb_drv); 31 if (id) 32 return 1; 33 } 34 35 return 0; 36 }
USB總線的任務有:
1. 識別USB設備
a. 分配地址並將地址告訴USB設備
b. 發出命令獲取描述符
2. 查找並安裝對應的設備驅動程序
3. 提供USB讀寫接口
我們現在來分析總線框架:
usb_init() -> bus_register(&usb_bus_type); /* 注冊usb總線 */ -> bus_register_notifier(&usb_bus_type, &usb_bus_nb); -> usb_major_init() -> register_chrdev(180, "usb", &usb_fops); -> usb_register(&usbfs_driver); /* usbfs驅動 */ -> usb_hub_init(); /* 初始化USB集線器 */ -> usb_register(&hub_driver); -> kthread_run(hub_thread, NULL, "khubd"); /* 執行線程 */ -> hub_events(); /* 如果有插拔USB設備操作 */ -> hub_port_connect_change(hub, i, portstatus, portchange); -> usb_alloc_dev(hdev, hdev->bus, port1); /* 分配usb內存 */ -> choose_address(udev); /* 設置地址 */ -> hub_port_init(hub, udev, port1, i); /* 獲取設備描述符 */ -> usb_deregister(&hub_driver); -> usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
usb_device:
每一個USB設備對應一個struct usb_device。

1 struct usb_device { 2 int devnum; /* 設備號 */ 3 char devpath[16]; /* 設備路徑 */ 4 u32 route; 5 enum usb_device_state state; 6 enum usb_device_speed speed; 7 8 struct usb_tt *tt; 9 int ttport; 10 11 unsigned int toggle[2]; 12 13 struct usb_device *parent; 14 struct usb_bus *bus; /* 設備所屬總線 */ 15 struct usb_host_endpoint ep0; /* 設備端點 */ 16 17 struct device dev; 18 19 struct usb_device_descriptor descriptor; 20 struct usb_host_config *config; /* 設備配置 */ 21 22 struct usb_host_config *actconfig; 23 struct usb_host_endpoint *ep_in[16]; 24 struct usb_host_endpoint *ep_out[16]; 25 26 char **rawdescriptors; 27 28 unsigned short bus_mA; 29 u8 portnum; 30 u8 level; 31 32 unsigned can_submit:1; 33 unsigned persist_enabled:1; 34 unsigned have_langid:1; 35 unsigned authorized:1; 36 unsigned authenticated:1; 37 unsigned wusb:1; 38 int string_langid; 39 40 /* static strings from the device */ 41 char *product; 42 char *manufacturer; 43 char *serial; 44 45 struct list_head filelist; 46 ... 47 int maxchild; 48 struct usb_device *children[USB_MAXCHILDREN]; 49 50 u32 quirks; 51 atomic_t urbnum; 52 ... 53 struct wusb_dev *wusb_dev; 54 int slot_id; 55 };
其注冊函數為int usb_new_device(struct usb_device *udev)。
在USB設備的邏輯組織中,包含設備、配置、接口和端點四個層次。在此我使用宋寶華老師書中圖片作為解釋:
在Linux內核中,這四個層次通過一組標准的描述符來描述,如下所示。
1. 設備描述符:usb_device用於定義USB設備,usb_device_descriptor用於定義產品ID等。
struct usb_device_descriptor { __u8 bLength; /* 描述符長度 */ __u8 bDescriptorType; /* 描述符類型編號 */ __le16 bcdUSB; /* USB版本號 */ __u8 bDeviceClass; /* USB分配的設備類 */ __u8 bDeviceSubClass; /* USB分配的子類 */ __u8 bDeviceProtocol; /* USB分配的協議 */ __u8 bMaxPacketSize0; /* 端點0的最大包大小 */ __le16 idVendor; /* 廠商編號 */ __le16 idProduct; /* 產品編號 */ __le16 bcdDevice; /* 出廠編號 */ __u8 iManufacturer; __u8 iProduct; __u8 iSerialNumber; __u8 bNumConfigurations; } __attribute__ ((packed));
當一個USB設備插入時,默認的設備編號為0,在未分配新的編號前,PC使用編號0與它通信。
由於PC可能擁有多個USB設備,因此在PC需要訪問某個USB設備時,發出的命令含有此USB設備對應的編號地址。
2. 配置描述符:usb_host_config用於定義USB配置,usb_config_descriptor用於定義此配置下的接口數,供電模式和功率要求等。
struct usb_config_descriptor { __u8 bLength; /* 描述符長度 */ __u8 bDescriptorType; /* 描述符類型編號 */ __le16 wTotalLength; /* 配置返回的所有數據大小 */ __u8 bNumInterfaces; /* 配置支持的接口數 */ __u8 bConfigurationValue; __u8 iConfiguration; /* 配置索引值 */ __u8 bmAttributes; /* 供電模式 */ __u8 bMaxPower; /* 最大電流 */ } __attribute__ ((packed));
3. 接口描述符:usb_interface用於定義USB接口,usb_interface_descriptor用於定義此接口下的端點數,協議等。
struct usb_interface_descriptor { __u8 bLength; /* 描述符長度 */ __u8 bDescriptorType; /* 描述符類型 */ __u8 bInterfaceNumber; /* 接口編號 */ __u8 bAlternateSetting; __u8 bNumEndpoints; /* 端點數 */ __u8 bInterfaceClass; /* 接口類型 */ __u8 bInterfaceSubClass; __u8 bInterfaceProtocol; __u8 iInterface; /* 接口索引值 */ } __attribute__ ((packed));
4. 端點描述符:usb_host_endpoint用於定義USB端點,usb_endpoint_descriptor用於定義此端點下的端點地址,方向和類型等。
struct usb_endpoint_descriptor { __u8 bLength; /* 描述符長度 */ __u8 bDescriptorType; /* 描述符類型 */ __u8 bEndpointAddress; /* 端點地址 */ __u8 bmAttributes; /* 端點屬性 */ __le16 wMaxPacketSize; /* 最大信息包大小 */ __u8 bInterval; /* NOTE: these two are _only_ in audio endpoints. */ /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */ __u8 bRefresh; __u8 bSynchAddress; } __attribute__ ((packed));
我們可以使用“lsusb -v -s 總線號:設備號”查看設備的詳細信息
usb_driver:
每一個USB設備驅動對應一個struct usb_driver。

1 struct usb_driver { 2 const char *name; 3 4 int (*probe) (struct usb_interface *intf, 5 const struct usb_device_id *id); 6 7 void (*disconnect) (struct usb_interface *intf); 8 9 int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code, 10 void *buf); 11 12 int (*suspend) (struct usb_interface *intf, pm_message_t message); 13 int (*resume) (struct usb_interface *intf); 14 int (*reset_resume)(struct usb_interface *intf); 15 16 int (*pre_reset)(struct usb_interface *intf); 17 int (*post_reset)(struct usb_interface *intf); 18 19 const struct usb_device_id *id_table; 20 21 struct usb_dynids dynids; 22 struct usbdrv_wrap drvwrap; 23 unsigned int no_dynamic_id:1; 24 unsigned int supports_autosuspend:1; 25 unsigned int soft_unbind:1; 26 };
其注冊函數為int usb_register(struct usb_driver *driver)。
1 #define usb_register(driver) \ 2 usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
三、USB數據傳輸
USB設備驅動和USB設備通信所有的結構體為USB請求塊(USB Request Block,URB)。
struct urb { /* private: usb core and host controller only fields in the urb */ struct kref kref; /* reference count of the URB */ void *hcpriv; /* private data for host controller */ atomic_t use_count; /* 用於計數,URB若提交則加1,URB若返回驅動程序減1 */ atomic_t reject; /* submissions will fail */ int unlinked; /* unlink error code */ /* public: documented fields in the urb that can be used by drivers */ struct list_head urb_list; /* list head for use by the urb's * current owner */ struct list_head anchor_list; /* the URB may be anchored */ struct usb_anchor *anchor; struct usb_device *dev; /* URB需要發送到的設備 */ struct usb_host_endpoint *ep; /* (internal) pointer to endpoint */ unsigned int pipe; /* (in) pipe information */ unsigned int stream_id; /* (in) stream ID */ int status; /* (return) non-ISO status */ unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/ void *transfer_buffer; /* 緩沖區,其數據可以從設備發送到主機,也可以從主機發送到設備 */ dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */ struct scatterlist *sg; /* (in) scatter gather buffer list */ int num_sgs; /* (in) number of entries in the sg list */ u32 transfer_buffer_length; /* 表示transfer_buffer或transfer_dma的長度 */ u32 actual_length; /* (return) actual transfer length */ unsigned char *setup_packet; /* (in) setup packet (control only) */ dma_addr_t setup_dma; /* (in) dma addr for setup_packet */ int start_frame; /* (modify) start frame (ISO) */ int number_of_packets; /* (in) number of ISO packets */ int interval; /* (modify) transfer interval * (INT/ISO) */ int error_count; /* (return) number of ISO errors */ void *context; /* (in) context for completion */ usb_complete_t complete; /* (in) completion routine */ struct usb_iso_packet_descriptor iso_frame_desc[0]; /* (in) ISO ONLY */ };
URB的處理流程:
在USB設備的邏輯層次中,interface是邏輯上的設備,它使用endpoint傳輸urb。
也就是說,USB設備中每個endpoint都處理一個urb隊列,在隊列清空之前,一個urb的傳輸過程如下:
當要進行數據傳輸時,需要分配、設置和提交一個urb給USB核心。核心對urb進行解析,將控制信息提交給主機控制器,由主機控制器完成數據到設備的傳輸。此時,驅動程序只需等待。當數據回傳到主機控制器后,會轉發給USB核心,喚醒等待的驅動程序。
具體函數操作如下圖:
各函數聲明如下:
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags) inline void usb_fill_int_urb (struct urb *urb, /* 要初始化的urb指針 */ struct usb_device *dev, /* 所要訪問的設備 */ unsigned int pipe, /* 要訪問的端點所對應的管道,使用usb_sndintpipe()或usb_rcvintpipe()創建 */ void *transfer_buffer, /* 要傳輸的數據緩沖區 */ int buffer_length, /* 緩沖區長度 */ usb_complete_t complete_fn, /* 當完成該urb所請求的操作時,要調用的回調函數 */ void *context, /* complet_fn函數所需的上下文,通常取值為dev */ int interval) /*urb被調度的時間間隔 */ 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) 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) int usb_submit_urb(struct urb *urb, gfp_t mem_flags); void usb_kill_urb(struct urb *urb); void usb_free_urb(struct urb *urb);
其中complete_fn()就是傳輸完成函數
四、USB鼠標設備驅動
在熟悉各個函數后,我們在本節完成USB鼠標設備驅動,步驟如下:
1. 分配設置usb_driver
.id_table
.probe
.disconnect
2. 使用usb_register()注冊usb_driver
3. 在probe()函數中完成輸入子系統和urb設置,最后提交urb
4. 在傳輸完成函數函數中判斷數據,上報數據,重新提交urb
5. 在disconnect()函數中注銷urb和輸入子系統
6. 使用usb_deregister()注銷usb_driver
在編寫驅動時,可參考例子drivers/hid/usbhid/usbmouse.c
USB鼠標驅動源代碼:

1 #include <linux/module.h> 2 #include <linux/fs.h> 3 #include <linux/init.h> 4 #include <linux/cdev.h> 5 #include <linux/slab.h> 6 #include <linux/device.h> 7 #include <linux/usb/input.h> 8 #include <linux/hid.h> 9 10 #include <asm/uaccess.h> 11 12 static struct usb_device_id mouse_table [] = { 13 { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, 14 USB_INTERFACE_PROTOCOL_MOUSE) }, 15 { } /* Terminating entry */ 16 }; 17 18 static struct input_dev *usbinput; 19 static dma_addr_t usb_buf_phys; 20 static char *buf_mem; 21 static struct urb *usb_urb; 22 static int len; 23 24 static unsigned int cnt; 25 26 static void usb_mouse_irq(struct urb *purb) 27 { 28 #if 0 29 /* 用於測試哪個bit是用於表示左、中、右鍵 30 * 我所使用的鼠標使用bit[0]表示左、中、右鍵 31 */ 32 int i; 33 34 printk("cnt: %02d, ", cnt++); 35 for(i = 0; i < len; ++i) 36 { 37 printk("%02x ", buf_mem[i]); 38 } 39 printk("\n"); 40 #else 41 static unsigned char preval = 0; /* 用於存儲上一個數據 */ 42 if((preval & 1) != (buf_mem[0] & 1)) { /* 鼠標左鍵數據有變化 */ 43 input_report_key(usbinput, KEY_L, (buf_mem[0] & 1) ? 1 : 0); 44 input_sync(usbinput); 45 } 46 if((preval & (1 << 1)) != (buf_mem[0] & (1 << 1))) { 47 input_report_key(usbinput, KEY_S, (buf_mem[0] & (1 << 1)) ? 1 : 0); 48 input_sync(usbinput); 49 } 50 if((preval & (1 << 2)) != (buf_mem[0] & (1 << 2))) { 51 input_report_key(usbinput, KEY_ENTER, (buf_mem[0] & (1 << 2)) ? 1 : 0); 52 input_sync(usbinput); 53 } 54 preval = buf_mem[0]; 55 #endif 56 57 usb_submit_urb(usb_urb, GFP_KERNEL); 58 59 } 60 61 static int mouse_probe(struct usb_interface *intf, const struct usb_device_id *id) 62 { 63 struct usb_device *dev = interface_to_usbdev(intf); 64 struct usb_host_interface *interface; 65 struct usb_endpoint_descriptor *endpoint; 66 unsigned int pipe; 67 int ret; 68 69 interface = intf->cur_altsetting; 70 endpoint = &interface->endpoint[0].desc; 71 72 /* 1. 輸入子系統設置 */ 73 usbinput = input_allocate_device(); 74 75 set_bit(EV_KEY, usbinput->evbit); 76 set_bit(EV_REP, usbinput->evbit); 77 78 set_bit(KEY_L, usbinput->keybit); 79 set_bit(KEY_S, usbinput->keybit); 80 set_bit(KEY_ENTER, usbinput->keybit); 81 82 ret = input_register_device(usbinput); 83 84 /* 2. urb設置 */ 85 usb_urb = usb_alloc_urb(0, GFP_KERNEL); 86 pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); 87 88 len = endpoint->wMaxPacketSize; 89 buf_mem = usb_alloc_coherent(dev, len, GFP_ATOMIC, &usb_buf_phys); 90 91 usb_fill_int_urb(usb_urb, dev, pipe, buf_mem, len, 92 usb_mouse_irq, NULL, endpoint->bInterval); 93 usb_urb->transfer_dma = usb_buf_phys; 94 usb_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 95 96 return usb_submit_urb(usb_urb, GFP_KERNEL); 97 } 98 99 static void mouse_disconnect(struct usb_interface *intf) 100 { 101 struct usb_device *dev = interface_to_usbdev(intf); 102 103 usb_kill_urb(usb_urb); 104 usb_free_urb(usb_urb); 105 106 usb_free_coherent(dev, len, buf_mem, usb_buf_phys); 107 input_unregister_device(usbinput); 108 input_free_device(usbinput); 109 } 110 111 static struct usb_driver mouse_driver = { 112 .name = "itop4412_mouse", 113 .probe = mouse_probe, 114 .disconnect = mouse_disconnect, 115 .id_table = mouse_table, 116 }; 117 118 static int __init usbmouse_init(void) 119 { 120 return usb_register(&mouse_driver); 121 } 122 123 static void __exit usbmouse_exit(void) 124 { 125 usb_deregister(&mouse_driver); 126 } 127 128 /* 聲明段屬性 */ 129 module_init(usbmouse_init); 130 module_exit(usbmouse_exit); 131 132 MODULE_LICENSE("GPL");
Makefile:

1 KERN_DIR = /work/itop4412/tools/linux-3.5 2 3 all: 4 make -C $(KERN_DIR) M=`pwd` modules 5 6 clean: 7 make -C $(KERN_DIR) M=`pwd` modules clean 8 rm -rf modules.order 9 10 obj-m += usbmouse.o
測試:
在編譯並在開發板上insmod后,會出現如下信息:
input: Unspecified device as /devices/virtual/input/input4
插入USB鼠標后,會出現如下信息:
sbcore: registered new interface driver itop4412_mouse
接下來執行:
# ls /dev/event*
最后一個/dev/event2為USB鼠標對應事件
執行:
#hexdump /dev/event2
接下來按鍵,效果如下圖:
在此我以第一行數據為例解釋一下各個數字的意義:
0000000 0393 0000 55a4 0008 0001 001f 0001 0000
次數 秒 微秒 按鍵類 哪個按鍵 按下
下一章 17、塊設備驅動