USB總線是一種典型的熱插拔的總線標準,因爲其優異的性能幾乎成爲了當下大小設備中的標配。
USB的驅動能夠分爲3類:SoC的USB控制器的驅動,主機端USB設備的驅動,設備上的USB Gadget驅動,一般,對於USB這種標準化的設備,內核已經將主機控制器的驅動編寫好了,設備上的Gadget驅動一般只運行固件程序而不是基於Linux, 因此驅動工程師的主要工做就是編寫主機端的USB設備驅動。linux
下圖表示了Linux中USB子系統的框架結構,和i2c同樣,USB子系統也可分爲三層:**設備驅動層--USB核心--控制器驅動層*api
做爲熱插拔總線, USB和非熱插拔總線最大的區別就是總線沒法事前獲知設備的信息以及設備什麼時候被插入或拔出,因此也就不能使用任意一種形式將設備信息事前寫入內核。
爲了解決因爲熱插拔引發的設備識別問題,USB總線經過枚舉的方式來獲取一個接入總線的USB設備的設備信息——一個由device->config->interface->endpoint逐級描述的設備,基於分離的思想,USB子系統中設計了一組結構來描述這幾個維度的設備信息,相比之下,i2c總線只要一個i2c_client便可描述一個設備.數組
USB總線上的全部通訊都是由主機發起的,因此本質上,USB都是採用輪詢的方式進行的。USB總線會使用輪詢的方式不斷檢測總線上是否有設備接入,若是有設備接入相應的D+D-就會有電平變化。而後總線就會按照USB規定的協議與設備進行通訊,設備將存儲在自身的設備信息依次交給主機,主機將這些信息按照4層模型組織起來。上報到內核,內核中的USB子系統再去匹配相應的驅動,USB設備驅動是面向interface這一層次的信息的緩存
做爲一種高度標準化的設備, 雖然USB自己十分複雜, 可是內核已經爲咱們完成了至關多的工做, 下述的經常使用設備驅動在內核中已經實現了。不少時候, 驅動的難度不是看設備的複雜程度, 而是看標準化程度網絡
基於分離的思想,USB子系統也提供了描述一個USB設備的結構,只不過基於USB協議,完整描述一個USB設備信息須要9個結構,這些結構中,前4個用來描述一個USB設備的硬件信息,即設備自己的信息,這些信息是寫入到設備的eeprom的,在任何USB主機中看到的都同樣,這些信息可使用lsusb -v命令來查看; 後5個描述一個USB設備的軟件信息,即除了硬件信息以外,Linux爲了管理一個USB設備還要封裝一些信息,是OS-specific的信息; USB設備硬件信息和軟件信息的關係相似於中斷子系統中的硬件中斷和內核中斷,只不過更復雜一點。框架
usb_endpoint_descriptor來描述一個interface的endpoint信息函數
usb_host_endpoint描述一個interdace的endpoint信息,包括usb_endpoint_descriptor,這是USB通訊的最小單位,咱們讀寫一個設備就是針對一個endpointpost
usb_hcd描述一個SoC中的USB控制器驅動性能
首先說的是那9個描述設備信息的結構, 其中的硬件信息是相互獨立的, 分別使用 這些結構在內核"include/uapi/linux/usbch9.h"有定義, 我就不貼代碼了ui
//include/uapi/linux/usbch9.h 258 struct usb_device_descriptor { 259 __u8 bLength; 260 __u8 bDescriptorType; 262 __le16 bcdUSB; 263 __u8 bDeviceClass; 264 __u8 bDeviceSubClass; 265 __u8 bDeviceProtocol; 266 __u8 bMaxPacketSize0; 267 __le16 idVendor; 268 __le16 idProduct; 269 __le16 bcdDevice; 270 __u8 iManufacturer; 271 __u8 iProduct; 272 __u8 iSerialNumber; 273 __u8 bNumConfigurations; 274 } __attribute__ ((packed));
struct usb_device_descriptor
--263-->設備類別
--264-->設備子類
--265-->通訊協議
--267-->銷售商
--268-->產品ID
--272-->序列號
//include/uapi/linux/usbch9.h 314 struct usb_config_descriptor { 315 __u8 bLength; 316 __u8 bDescriptorType; 317 318 __le16 wTotalLength; 319 __u8 bNumInterfaces; 320 __u8 bConfigurationValue; 321 __u8 iConfiguration; 322 __u8 bmAttributes; 323 __u8 bMaxPower; 324 } __attribute__ ((packed));
//include/uapi/linux/usbch9.h 351 struct usb_interface_descriptor { 352 __u8 bLength; 353 __u8 bDescriptorType; 354 355 __u8 bInterfaceNumber; 356 __u8 bAlternateSetting; 357 __u8 bNumEndpoints; 358 __u8 bInterfaceClass; 359 __u8 bInterfaceSubClass; 360 __u8 bInterfaceProtocol; 361 __u8 iInterface; 362 } __attribute__ ((packed));
//include/uapi/linux/usbch9.h 369 struct usb_endpoint_descriptor { 370 __u8 bLength; 371 __u8 bDescriptorType; 372 373 __u8 bEndpointAddress; 374 __u8 bmAttributes; 375 __le16 wMaxPacketSize; 376 __u8 bInterval; 377 378 /* NOTE: these two are _only_ in audio endpoints. */ 379 /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */ 380 __u8 bRefresh; 381 __u8 bSynchAddress; 382 } __attribute__ ((packed));
//include/linux/usb.h 510 struct usb_device { 511 int devnum; 512 char devpath[16]; 513 u32 route; 522 struct usb_device *parent; 523 struct usb_bus *bus; 524 struct usb_host_endpoint ep0; 526 struct device dev; 528 struct usb_device_descriptor descriptor; 529 struct usb_host_bos *bos; 530 struct usb_host_config *config; 532 struct usb_host_config *actconfig; 557 char *product; 558 char *manufacturer; 559 char *serial; 561 struct list_head filelist; 563 int maxchild; 568 unsigned long active_duration; 569 584 };
struct usb_device
--522-->這個設備的父設備, 一般就是usb塔形結構的上一個節點設備
--523-->所屬的總線是usb總線
--526-->這是一個device, 會掛接到相應的鏈表
--528-->這個軟件device結構包含的硬件device對象
--530-->軟件device擁有的全部軟件config對象, 對應硬件device擁有的全部硬件config
--531-->當下, 這個device正在使用的config
--557-->產品名
--558-->產品製造商
--559-->產品序列號
--561-->在這個設備上打開的usbfs文件的鏈表節點
--563-->子設備的最大數量
//include/linux/usb.h 275 struct usb_host_config { 276 struct usb_config_descriptor desc; 278 char *string; /* iConfiguration string, if present */ 282 struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS]; 286 struct usb_interface *interface[USB_MAXINTERFACES]; 290 struct usb_interface_cache *intf_cache[USB_MAXINTERFACES]; 292 unsigned char *extra; /* Extra descriptors */ 293 int extralen; 294 };
struct usb_host_config
--276-->軟件config對象包含的硬件config對象
--278-->config的名稱
--282-->這個config上關聯的Interface Association Descriptor
--283-->這個config上關聯的下一級的軟件interface數組,
下面這個就是與驅動直接匹配的描述
160 struct usb_interface { 163 struct usb_host_interface *altsetting; 165 struct usb_host_interface *cur_altsetting; 167 unsigned num_altsetting; /* number of alternate settings */ 171 struct usb_interface_assoc_descriptor *intf_assoc; 173 int minor; 175 enum usb_interface_condition condition; /* state of binding */ 176 unsigned sysfs_files_created:1; /* the sysfs attributes exist */ 177 unsigned ep_devs_created:1; /* endpoint "devices" exist */ 178 unsigned unregistering:1; /* unregistration is in progress */ 179 unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */ 180 unsigned needs_altsetting0:1; /* switch to altsetting 0 is pending */ 181 unsigned needs_binding:1; /* needs delayed unbind/rebind */ 182 unsigned reset_running:1; 183 unsigned resetting_device:1; /* true: bandwidth alloc after reset */ 185 struct device dev; /* interface specific device info */ 186 struct device *usb_dev; 187 atomic_t pm_usage_cnt; /* usage counter for autosuspend */ 188 struct work_struct reset_ws; /* for resets in atomic context */ 189 };
struct usb_interface
--163-->這個interface包含的全部的setting
--164-->這個interface當前正在使用的setting
--165-->若是這個interface與一個使用了主設備號的驅動綁定了, 這個域就是interface的次設備號; 反之則沒用. 驅動應該在probe中設置這個參數
77 struct usb_host_interface { 78 struct usb_interface_descriptor desc; 80 int extralen; 81 unsigned char *extra; /* Extra descriptors */ 86 struct usb_host_endpoint *endpoint; 88 char *string; /* iInterface string, if present */ 89 };
struct usb_host_interface
--78-->這個interface對應的硬件interface對象
--86-->擁有的描述軟件endpoint信息的usb_host_endpoint數組
--88-->interface名稱
endpoint是USB設備IO的基本單元
64 struct usb_host_endpoint { 65 struct usb_endpoint_descriptor desc; 66 struct usb_ss_ep_comp_descriptor ss_ep_comp; 67 struct list_head urb_list; 68 void *hcpriv; 69 struct ep_device *ep_dev; /* For sysfs info */ 71 unsigned char *extra; /* Extra descriptors */ 72 int extralen; 73 int enabled; 74 };
struct usb_host_endpoint
--65-->這個usb_host_endpoint對應的硬件endpoint信息
--67-->讀寫這個endpoint的usb鏈表, 由usb核心層(drivers/usb/core/file.c)維護
--73-->這個endpoint是否被使能了
每個硬件信息對象都包含在一個軟件信息對象中, 而軟件信息對象是層層包含的, 因此雖然驅動是基於interface描述的, 可是咱們可使用"list_entry()"很容易的向上找到config和device描述, 使用其中的域很容易的找到endpoint描述, 這9個描述設備的結構關係以下圖所示:
與platform或i2c總線不一樣的是,usb總線不容許設備發起通訊,因此做爲設備驅動, 只有將須要的"材料"準備好經由核心層提交到usb控制器驅動,讓控制器驅動帶着這些"材料"去輪詢設備並將應答帶回。這些"材料"就是urb和相應的註冊參數。當usb_driver和usb設備匹配上後,咱們準備一個urb對象經過usb_fill_int_urb()/_bulk_/_control_註冊到總線,並在合適的時機經過usb_submit_urb向控制器驅動發出發送這個urb對象的命令,總線的控制器驅動收到咱們的發送命令以後,會根據咱們註冊時設置的週期不斷向匹配的設備發送請求,若是子設備響應了咱們的請求,控制器驅動就會將咱們註冊的urb對象填充好,並回調咱們的註冊函數。
對於海量存儲USB設備,若是要讓設備正常工做, 除了這些流程,還須要加上對緩存區的管理,這部分咱們下篇說。
初始化並註冊一箇中斷urb。函數原型以下:
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對象; dev表示這個urb對象的目的設備; pipe表示讀寫管道, 使用usb_sndintpipe()和usb_rcvintpipe()獲取; transfer_buffer表示傳遞數據的緩衝區首地址;buffer_length表示緩衝區長度;complete_fn表示若是咱們發出的urb有了迴應, 就回調這個函數; context是回調函數的參數, 由用戶定義, 至關於request_irq中的void *dev; interval就是發送週期, 核心層會以這個參數爲週期經過usb控制器驅動輪詢設備,
urb和xxx同樣,要用內核分配函數,其中會作一些初始化的工做
初始化並註冊一個海量存儲urb
初始化並註冊一個控制urb
通知內核發送urb對象
1048 struct usb_driver { 1049 const char *name; 1051 int (*probe) (struct usb_interface *intf, 1052 const struct usb_device_id *id); 1054 void (*disconnect) (struct usb_interface *intf); 1056 int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code, 1057 void *buf); 1059 int (*suspend) (struct usb_interface *intf, pm_message_t message); 1060 int (*resume) (struct usb_interface *intf); 1061 int (*reset_resume)(struct usb_interface *intf); 1063 int (*pre_reset)(struct usb_interface *intf); 1064 int (*post_reset)(struct usb_interface *intf); 1066 const struct usb_device_id *id_table; 1068 struct usb_dynids dynids; 1069 struct usbdrv_wrap drvwrap; 1070 unsigned int no_dynamic_id:1; 1071 unsigned int supports_autosuspend:1; 1072 unsigned int disable_hub_initiated_lpm:1; 1073 unsigned int soft_unbind:1; 1074 };
struct usb_driver
--1049-->usb設備的名字
--1051-->探測函數, 當usb_driver的id_table和usb設備信息匹配的時候會執行, 主要的工做是申請資源, 初始化, 提供接口
--1054-->當驅動模塊被卸載時或設備被拔出時會被執行
--1066-->功能依然是匹配同樣, 只是usb的設備信息由4個維度描述, 因此id_table能夠填充的內容也多種多樣
註冊一個usb_driver到內核
註銷一個usb_driver
內核提供了以下的宏來構造一個usb_device_id對象, 其實也就是對usb_device_id中的不一樣域進行了填充, 因爲設備的差別性, 不一樣的USB設備會上報不一樣的設備信息, 但不管上報哪些信息, 必定屬於下面這些宏的一種封裝. 能夠先使用lsusb -v查看設備的硬件信息, 再根據其提供的硬件信息肯定id_table編寫相應的驅動
811 #define USB_DEVICE(vend, prod) \ 812 .match_flags = USB_DEVICE_ID_MATCH_DEVICE, \ 813 .idVendor = (vend), \ 814 .idProduct = (prod)
825 #define USB_DEVICE_VER(vend, prod, lo, hi) \ 826 .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, \ 827 .idVendor = (vend), \ 828 .idProduct = (prod), \ 829 .bcdDevice_lo = (lo), \ 830 .bcdDevice_hi = (hi)
841 #define USB_DEVICE_INTERFACE_CLASS(vend, prod, cl) \ 842 .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ 843 USB_DEVICE_ID_MATCH_INT_CLASS, \ 844 .idVendor = (vend), \ 845 .idProduct = (prod), \ 846 .bInterfaceClass = (cl)
857 #define USB_DEVICE_INTERFACE_PROTOCOL(vend, prod, pr) \ 858 .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ 859 USB_DEVICE_ID_MATCH_INT_PROTOCOL, \ 860 .idVendor = (vend), \ 861 .idProduct = (prod), \ 862 .bInterfaceProtocol = (pr)
873 #define USB_DEVICE_INTERFACE_NUMBER(vend, prod, num) \ 874 .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ 875 USB_DEVICE_ID_MATCH_INT_NUMBER, \ 876 .idVendor = (vend), \ 877 .idProduct = (prod), \ 878 .bInterfaceNumber = (num)
889 #define USB_DEVICE_INFO(cl, sc, pr) \ 890 .match_flags = USB_DEVICE_ID_MATCH_DEV_INFO, \ 891 .bDeviceClass = (cl), \ 892 .bDeviceSubClass = (sc), \ 893 .bDeviceProtocol = (pr) 894
904 #define USB_INTERFACE_INFO(cl, sc, pr) \ 905 .match_flags = USB_DEVICE_ID_MATCH_INT_INFO, \ 906 .bInterfaceClass = (cl), \ 907 .bInterfaceSubClass = (sc), \ 908 .bInterfaceProtocol = (pr)
924 #define USB_DEVICE_AND_INTERFACE_INFO(vend, prod, cl, sc, pr) \ 925 .match_flags = USB_DEVICE_ID_MATCH_INT_INFO \ 926 | USB_DEVICE_ID_MATCH_DEVICE, \ 927 .idVendor = (vend), \ 928 .idProduct = (prod), \ 929 .bInterfaceClass = (cl), \ 930 .bInterfaceSubClass = (sc), \ 931 .bInterfaceProtocol = (pr)
946 #define USB_VENDOR_AND_INTERFACE_INFO(vend, cl, sc, pr) \ 947 .match_flags = USB_DEVICE_ID_MATCH_INT_INFO \ 948 | USB_DEVICE_ID_MATCH_VENDOR, \ 949 .idVendor = (vend), \ 950 .bInterfaceClass = (cl), \ 951 .bInterfaceSubClass = (sc), \ 952 .bInterfaceProtocol = (pr) 953
下面是內核"drdrivers/hid/usbhid/usbmouse.c"中的ib_table填寫方式, 能夠看出, 不只構造使用了宏, 因爲USB鼠標是標準設備, 它的屬性值也有標準的宏來標識
230 static struct usb_device_id usb_mouse_id_table [] = { 231 { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, 232 USB_INTERFACE_PROTOCOL_MOUSE) }, 233 { } /* Terminating entry */ 234 };
內核"drivers/hid/usbhid/usbmouse.c"就是一個usb鼠標的驅動, 這裏我根據本身的理解寫了一個, 採用的是"usb中斷設備驅動+input子系統"框架
#define BUF_SIZE 8 MODULE_LICENSE("GPL"); //面向對象, 根據需求封裝類 struct xj_mouse { char name[128]; struct usb_device *dev; struct urb *msg; struct input_dev *input; signed char *buf; }; struct xj_mouse *mouse; static int usb_mouse_open(struct input_dev *dev) { struct xj_mouse *mouse = input_get_drvdata(dev); mouse->msg->dev = mouse->dev; if (usb_submit_urb(mouse->msg, GFP_KERNEL)) return -EIO; return 0; } static void usb_mouse_close(struct input_dev *dev) { struct xj_mouse *mouse = input_get_drvdata(dev); usb_kill_urb(mouse->msg); } static int init_input(struct usb_interface * intf) { int err=0; struct usb_device *dev = mouse->dev; struct input_dev *input_dev = mouse->input; input_dev->name = mouse->name; usb_to_input_id(dev, &input_dev->id); input_dev->dev.parent = &intf->dev; input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |BIT_MASK(BTN_EXTRA); input_dev->relbit[0] |= BIT_MASK(REL_WHEEL); input_set_drvdata(input_dev, mouse); input_dev->open = usb_mouse_open; input_dev->close = usb_mouse_close; err = input_register_device(mouse->input); return 0; } static void completion(struct urb * msg) { int status; signed char *buf = mouse->buf; struct input_dev *input = mouse->input; input_report_key(input, BTN_LEFT, buf[0] & 0x01); input_report_key(input, BTN_RIGHT, buf[0] & 0x02); input_report_key(input, BTN_MIDDLE, buf[0] & 0x04); input_report_key(input, BTN_SIDE, buf[0] & 0x08); input_report_key(input, BTN_EXTRA, buf[0] & 0x10); input_report_rel(input, REL_X, buf[1]); input_report_rel(input, REL_Y, buf[2]); input_report_rel(input, REL_WHEEL, buf[3]); input_sync(input); status = usb_submit_urb (msg, GFP_ATOMIC); } static int probe(struct usb_interface *intf, const struct usb_device_id *id) { int pipe; struct usb_host_interface *interface; struct usb_endpoint_descriptor *endpoint; //分配、初始化個性結構 mouse = (struct xj_mouse *)kzalloc(sizeof(struct xj_mouse),GFP_KERNEL); mouse->dev=interface_to_usbdev(intf); mouse->msg=usb_alloc_urb(0,GFP_KERNEL); mouse->input=input_allocate_device(); mouse->buf=(void *)kzalloc(BUF_SIZE,GFP_KERNEL); if (mouse->dev->manufacturer){ strlcpy(mouse->name, mouse->dev->manufacturer, sizeof(mouse->name)); mouse->input->name = mouse->name; } //初始化input設備 init_input(intf); //獲取pipe interface=intf->cur_altsetting; endpoint=&interface->endpoint[0].desc; /* 使用dev和endpoint獲取端點地址 */ pipe = usb_rcvintpipe(mouse->dev,endpoint->bEndpointAddress); //註冊usb驅動 usb_fill_int_urb(mouse->msg,mouse->dev,pipe,mouse->buf,BUF_SIZE,completion,mouse->msg,endpoint->bInterval); return 0; } static void disconnect(struct usb_interface *intf) { struct xj_mouse *tmp_mouse = usb_get_intfdata (intf); usb_set_intfdata(intf, NULL); if (tmp_mouse) { usb_kill_urb(tmp_mouse->msg); input_unregister_device(tmp_mouse->input); usb_free_urb(tmp_mouse->msg); kfree(tmp_mouse->buf); kfree(tmp_mouse); } } static struct usb_device_id id_table [] ={ { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_MOUSE) }, {}, }; struct usb_driver mouse_drv = { .name = "xj_mouse_drv", .probe = probe, .disconnect = disconnect, .id_table = id_table, }; module_usb_driver(mouse_drv);