編寫 USB 驅動程式
(本部分的一些示例源碼來自drivers/usb/usb-skeleton.c,它是Linux核心為我們提供的最基礎的USB驅動程式,USB骨架程式)
驅動程式把驅動對象注冊到 USB 子系統中,之後使用供應商(idVendor)和裝置(idProduct)辨別來判斷對應的硬體是否已經安裝.
驅動的裝置支援清單
struct usb_device_id 結構提供了這個驅動支援的不同類型 USB 裝置的清單. USB 核心通過此清單用來決定裝置對應的驅動,熱插拔腳本也通過此清單來決定當特定裝置被插入系統時,應該自動加載的驅動.
對于一個隻為一個供應商的一個 USB 裝置的簡單 USB 裝置驅動, 其 struct usb_device_id 可定義如下:
<code>/* Define these values to match your devices */ #define USB_SKEL_VENDOR_ID 0xfff0 #define USB_SKEL_PRODUCT_ID 0xfff0 /* table of devices that work with this driver */ static struct usb_device_id skel_table [] = { { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, skel_table);</code>
MODULE_DEVICE_TABLE 宏是必需的,它允許使用者空間工具判斷該驅動可控制什麼裝置. 對于 USB 驅動, 這個宏中的第一個值必須是 usb .
如果你需要這個驅動被系統中每個 USB 裝置調用, 建立僅需設定 driver_info 成員:
<code>static struct usb_device_id usb_ids[] = { {.driver_info = 42}, {} };</code>
注冊USB驅動程式
所有 USB 驅動都必須建立的主要結構是 struct usb_driver. 這個結構必須被 USB 驅動程式手動填充并且包含多個回調函數和變量, 并向 USB 核心描述 USB 驅動程式:
<code>struct usb_driver { const char *name; /*指向驅動程式名字的指針. 它必須在核心所有的 USB 驅動中是唯一的(通常被設為和驅動子產品名相同).當驅動在核心中運作時,會出現在/sys/bus/usb/drivers目錄中 */ int (*probe) (struct usb_interface *intf, const struct usb_device_id *id); /*指向 USB 驅動中探測函數指針. 當USB 核心認為它有一個本驅動可處理的 struct usb_interface時此函數将被調用. USB 核心用來做判斷的 struct usb_device_id 指針也被傳遞給此函數.如果這個 USB 驅動确認傳遞給它的 struct usb_interface, 它應當正确地初始化裝置并傳回 0. 如果驅動沒有确認這個裝置或發生錯誤,則傳回負錯誤值 */ void (*disconnect) (struct usb_interface *intf); /*指向 USB 驅動的斷開函數指針.當 struct usb_interface 從系統中清除或驅動 USB 核心解除安裝時,函數将被 USB 核心調用*/ int (*ioctl) (struct usb_interface *intf, unsigned int code, void *buf); /*指向 USB 驅動的 ioctl 函數指針. 若此函數存在, 在使用者空間程式對usbfs 檔案系統關聯的裝置調用 ioctl 時,此函數将被調用. 實際上,目前隻有 USB 集線器驅動使用這個 ioctl*/ int (*suspend) (struct usb_interface *intf, pm_message_t message); /*指向 USB 驅動中挂起函數的指針*/ int (*resume) (struct usb_interface *intf); /*指向 USB 驅動中恢複函數的指針*/ int (*reset_resume)(struct usb_interface *intf); /*要複位一個已經被挂起的USB裝置時調用此函數*/ int (*pre_reset)(struct usb_interface *intf); /*在裝置被複位之前由usb_reset_composite_device()調用*/ int (*post_reset)(struct usb_interface *intf); /*在裝置被複位之後由usb_reset_composite_device()調用*/ const struct usb_device_id *id_table; /*指向 struct usb_device_id 表的指針*/ struct usb_dynids dynids; struct usbdrv_wrap drvwrap; /*是struct device_driver driver的再包裝,struct device_driver 包含 struct module *owner;*/ unsigned int no_dynamic_id:1; unsigned int supports_autosuspend:1; unsigned int soft_unbind:1; }; #define to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver)</code>
建立一個簡單的 struct usb_driver 結構, 隻有 4 個成員需要初始化:
<code>static struct usb_driver skel_driver = { .name = "skeleton", .id_table = skel_table, .probe = skel_probe, .disconnect = skel_disconnect, };</code>
<code>//向 USB 核心注冊 struct usb_driver static int __init usb_skel_init(void) { int result; /* register this driver with the USB subsystem */ result = usb_register(&skel_driver); if (result) err("usb_register failed. Error number %d", result); return result; } /*當 USB 驅動被解除安裝, struct usb_driver 需要從核心登出(代碼如下). 當以下調用發生, 目前綁定到這個驅動的任何 USB 接口将會斷開, 并調用斷開函數*/ static void __exit usb_skel_exit(void) { /* deregister this driver with the USB subsystem */ usb_deregister(&skel_driver); }</code>
探測和斷開的細節
在 struct usb_driver 結構中, 有 2 個 USB 核心在适當的時候調用的函數:
(1)當裝置安裝時, 如果 USB 核心認為這個驅動可以處理,則調用探測(probe)函數,探測函數檢查傳遞給它的裝置資訊, 并判斷驅動是否真正合适這個裝置.
(2)由于某些原因,裝置被移除或驅動不再控制裝置時,調用斷開(disconnect)函數,做适當清理.
探測和斷開回調函數都在 USB 集線器核心線程上下文中被調用, 是以它們休眠是合法的. 為了縮短 USB 探測時間,大部分工作盡可能在裝置打開時完成.這是因為 USB 核心是在一個線程中處理 USB 裝置的添加和移除, 是以任何慢裝置驅動都可能使 USB 裝置探測時間變長。
探測函數分析
在探測回調函數中, USB 驅動應當初始化它可能用來管理 USB 裝置的所有本地結構并儲存所有需要的裝置資訊到本地結構, 因為在此時做這些通常更容易.為了和裝置通訊,USB 驅動通常要探測裝置的端點位址和緩沖大小. 以下是usb-skeleton的probe函數中的探測代碼:
<code> /* set up the endpoint information */ /* use only the first bulk-in and bulk-out endpoints */ iface_desc = interface->cur_altsetting; //從輸入的interface中提取目前接口的端點總數 for (i = 0; i iface_desc->desc.bNumEndpoints; ++i) { /*輪詢所有端點*/ endpoint = &iface_desc->endpoint[i].desc; //獲得端點的資料結構指針 if (!dev->bulk_in_endpointAddr && usb_endpoint_is_bulk_in(endpoint)) { //如果是批量輸入端點, /* we found a bulk in endpoint */ buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); dev->bulk_in_size = buffer_size; //獲得端點大小 dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; //獲得端點位址 dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); //為此端點建立緩沖區 if (!dev->bulk_in_buffer) { err("Could not allocate bulk_in_buffer"); goto error; } } if (!dev->bulk_out_endpointAddr && usb_endpoint_is_bulk_out(endpoint)) { 如果是批量輸出端點 /* we found a bulk out endpoint */ dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; //獲得端點位址 } } if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) { //如果不是這兩種端點,報錯 err("Could not find both bulk-in and bulk-out endpoints"); goto error; } //這裡端點判斷的函數給我們的程式設計帶來了友善: /*-------------------------------------------------------------------------*/ /** * usb_endpoint_num - get the endpoint's number * @epd: endpoint to be checked * * Returns @epd's number: 0 to 15. */ static inline int usb_endpoint_num(const struct usb_endpoint_descriptor *epd) { return epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; } /** * usb_endpoint_type - get the endpoint's transfer type * @epd: endpoint to be checked * * Returns one of USB_ENDPOINT_XFER_{CONTROL, ISOC, BULK, INT} according * to @epd's transfer type. */ static inline int usb_endpoint_type(const struct usb_endpoint_descriptor *epd) { return epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; } /** * usb_endpoint_dir_in - check if the endpoint has IN direction * @epd: endpoint to be checked * * Returns true if the endpoint is of type IN, otherwise it returns false. */ static inline int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd) { return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN); } /** * usb_endpoint_dir_out - check if the endpoint has OUT direction * @epd: endpoint to be checked * * Returns true if the endpoint is of type OUT, otherwise it returns false. */ static inline int usb_endpoint_dir_out( const struct usb_endpoint_descriptor *epd) { return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT); } /** * usb_endpoint_xfer_bulk - check if the endpoint has bulk transfer type * @epd: endpoint to be checked * * Returns true if the endpoint is of type bulk, otherwise it returns false. */ static inline int usb_endpoint_xfer_bulk( const struct usb_endpoint_descriptor *epd) { return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK); } /** * usb_endpoint_xfer_control - check if the endpoint has control transfer type * @epd: endpoint to be checked * * Returns true if the endpoint is of type control, otherwise it returns false. */ static inline int usb_endpoint_xfer_control( const struct usb_endpoint_descriptor *epd) { return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_CONTROL); } /** * usb_endpoint_xfer_int - check if the endpoint has interrupt transfer type * @epd: endpoint to be checked * * Returns true if the endpoint is of type interrupt, otherwise it returns * false. */ static inline int usb_endpoint_xfer_int( const struct usb_endpoint_descriptor *epd) { return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT); } /** * usb_endpoint_xfer_isoc - check if the endpoint has isochronous transfer type * @epd: endpoint to be checked * * Returns true if the endpoint is of type isochronous, otherwise it returns * false. */ static inline int usb_endpoint_xfer_isoc( const struct usb_endpoint_descriptor *epd) { return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC); } /** * usb_endpoint_is_bulk_in - check if the endpoint is bulk IN * @epd: endpoint to be checked * * Returns true if the endpoint has bulk transfer type and IN direction, * otherwise it returns false. */ static inline int usb_endpoint_is_bulk_in( const struct usb_endpoint_descriptor *epd) { return (usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_in(epd)); } /** * usb_endpoint_is_bulk_out - check if the endpoint is bulk OUT * @epd: endpoint to be checked * * Returns true if the endpoint has bulk transfer type and OUT direction, * otherwise it returns false. */ static inline int usb_endpoint_is_bulk_out( const struct usb_endpoint_descriptor *epd) { return (usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_out(epd)); } /** * usb_endpoint_is_int_in - check if the endpoint is interrupt IN * @epd: endpoint to be checked * * Returns true if the endpoint has interrupt transfer type and IN direction, * otherwise it returns false. */ static inline int usb_endpoint_is_int_in( const struct usb_endpoint_descriptor *epd) { return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_in(epd)); } /** * usb_endpoint_is_int_out - check if the endpoint is interrupt OUT * @epd: endpoint to be checked * * Returns true if the endpoint has interrupt transfer type and OUT direction, * otherwise it returns false. */ static inline int usb_endpoint_is_int_out( const struct usb_endpoint_descriptor *epd) { return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_out(epd)); } /** * usb_endpoint_is_isoc_in - check if the endpoint is isochronous IN * @epd: endpoint to be checked * * Returns true if the endpoint has isochronous transfer type and IN direction, * otherwise it returns false. */ static inline int usb_endpoint_is_isoc_in( const struct usb_endpoint_descriptor *epd) { return (usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_in(epd)); } /** * usb_endpoint_is_isoc_out - check if the endpoint is isochronous OUT * @epd: endpoint to be checked * * Returns true if the endpoint has isochronous transfer type and OUT direction, * otherwise it returns false. */ static inline int usb_endpoint_is_isoc_out( const struct usb_endpoint_descriptor *epd) { return (usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_out(epd)); } /*-------------------------------------------------------------------------*/</code>
在裝置注冊之後,USB 驅動的後續操作都是通過struct usb_interface 獲得裝置的端點資訊,是以要使用 usb_set_intfdata 将前面獲得的端點資訊儲存到struct usb_interface 下的struct device 中的void *driver_data;中,以友善以後的操作。在usb-skeleton的probe函數中的代碼:
<code> /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev);</code>
之後在USB的驅動程式中的打開函數和斷開函數中調用usb_get_intfdata來擷取端點資料。由于這 2 個函數, USB 驅動不需要為系統中所有目前的裝置各保持一個靜态指針數組來儲存單個裝置結構. 對裝置資訊的非直接引用使得任何 USB 驅動都支援不限數量的裝置.
若這個 USB 驅動沒有和另一種處理使用者和裝置互動的子系統(如 input, tty, video......)關聯, 驅動可使用 USB 主裝置号,以便在使用者空間使用傳統的字元驅動接口. 為此, USB 驅動必須在探測函數中調用 usb_register_dev 函數, 以注冊一個裝置到 USB 核心. 在usb-skeleton的probe函數中的代碼:
<code> /* we can register the device now, as it is ready */ retval = usb_register_dev(interface, &skel_class); if (retval) { /* something prevented us from registering this driver */ err("Not able to get a minor for this device."); usb_set_intfdata(interface, NULL); goto error; } //其中使用到的 struct usb_class_driver 結構體如下: /** * struct usb_class_driver - identifies a USB driver that wants to use the USB major number * @name: the usb class device name for this driver. Will show up in sysfs. * @fops: pointer to the struct file_operations of this driver. * @minor_base: the start of the minor range for this driver. * * This structure is used for the usb_register_dev() and * usb_unregister_dev() functions, to consolidate a number of the * parameters used for them. */ struct usb_class_driver { char *name; //sysfs 用來描述裝置的名字 const struct file_operations *fops; // struct file_operations 結構指針, 驅動定義來注冊為字元裝置 int minor_base; /*給這個驅動安排的次裝置号的起始. 所有和這個驅動相關的裝置被建立為從這個值開始的唯一的, 遞增的次裝置号. 隻有 16 個裝置被允許在任何時刻和這個驅動關聯, 除非 CONFIG_USB_DYNAMIC_MINORS 配置選項被打開. 如 果這樣, 忽略這個變量, 并且這個裝置的所有的次裝置号會以先來先服務的方式配置設定. 建議打開了這個選項的系統使用類似 udev 的程式來産生系統中的裝置節點, 因為一個靜态的 /dev 樹不會正确工作.*/ }; //而在usb-skeleton的設定如下: static const struct file_operations skel_fops = { .owner = THIS_MODULE, .read = skel_read, .write = skel_write, .open = skel_open, .release = skel_release, .flush = skel_flush, }; /* * usb class driver info in order to get a minor number from the usb core, * and to have the device registered with the driver core */ static struct usb_class_driver skel_class = { .name = "skel%d", .fops = &skel_fops, .minor_base = USB_SKEL_MINOR_BASE, };</code>
當 USB 裝置斷開, 所有關聯到這個裝置的資源都應被釋放,如果已在探測函數中調用 usb_register_dev 配置設定了 USB 裝置的次裝置号, 必須調用函數 usb_deregister_dev 來将次裝置号還回 USB 核心.在斷開函數中, 需要從接口擷取之前調用 usb_set_intfdata 所設定的資料,然後設定struct usb_interface 結構指針為 NULL,以防止錯誤的通路.而在usb-skeleton的源碼如下:
<code>static void skel_disconnect(struct usb_interface *interface) { struct usb_skel *dev; int minor = interface->minor; dev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); /* give back our minor */ usb_deregister_dev(interface, &skel_class); /* prevent more I/O from starting */ mutex_lock(&dev->io_mutex); dev->interface = NULL; mutex_unlock(&dev->io_mutex); usb_kill_anchored_urbs(&dev->submitted); /* decrement our usage count */ kref_put(&dev->kref, skel_delete); info("USB Skeleton #%d now disconnected", minor); }</code>
當一個 USB 裝置調用 disconnect 函數時, 所有目前正被傳送的 urb 可自動被 USB 核心取消, 不必顯式調用 usb_kill_urb. 在USB裝置被斷開之後,如果驅動試圖調用 usb_submit_urb 送出urb , 将會失敗, 錯誤值為-EPIPE.
送出和控制 urb 的過程
以usb-skeleton源碼中的寫函數為例:
<code>static ssize_t skel_write(struct file *file, const char *user_buffer, size_t count, loff_t *ppos) { struct usb_skel *dev; int retval = 0; struct urb *urb = NULL; char *buf = NULL; size_t writesize = min(count, (size_t)MAX_TRANSFER); dev = (struct usb_skel *)file->private_data; /* verify that we actually have some data to write */ if (count == 0) goto exit; /* limit the number of URBs in flight to stop a user from using up all RAM */ if (down_interruptible(&dev->limit_sem)) { retval = -ERESTARTSYS; goto exit; } spin_lock_irq(&dev->err_lock); if ((retval = dev->errors) 0) { /* any error is reported once */ dev->errors = 0; /* to preserve notifications about reset */ retval = (retval == -EPIPE) ? retval : -EIO; } spin_unlock_irq(&dev->err_lock); if (retval 0) goto error; /* create a urb, and a buffer for it, and copy the data to the urb */ /*當驅動有資料發送到 USB 裝置,首先配置設定一個 urb */ urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { retval = -ENOMEM; goto error; } /*以最有效的方式是建立一個 DMA 緩沖區來發送資料到裝置, 并拷貝資料到緩沖區*/ buf = usb_buffer_alloc(dev->udev, writesize, GFP_KERNEL, &urb->transfer_dma); if (!buf) { retval = -ENOMEM; goto error; } if (copy_from_user(buf, user_buffer, writesize)) { retval = -EFAULT; goto error; } /* this lock makes sure we don't submit URBs to gone devices */ mutex_lock(&dev->io_mutex); if (!dev->interface) { /* disconnect() was called */ mutex_unlock(&dev->io_mutex); retval = -ENODEV; goto error; } /* initialize the urb properly */ /*在将urb送出給 USB 核心之前,正确初始化 urb */ usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), buf, writesize, skel_write_bulk_callback, dev); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; usb_anchor_urb(urb, &dev->submitted); /* send the data out the bulk port */ /*送出 urb 給 USB 核心, 由它将 urb 傳遞給裝置*/ retval = usb_submit_urb(urb, GFP_KERNEL); mutex_unlock(&dev->io_mutex); if (retval) { err("%s - failed submitting write urb, error %d", __func__, retval); goto error_unanchor; } /* release our reference to this urb, the USB core will eventually free it entirely */ usb_free_urb(urb); return writesize; error_unanchor: usb_unanchor_urb(urb); error: if (urb) { usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma); usb_free_urb(urb); } up(&dev->limit_sem); exit: return retval; } //當urb被成功傳遞到 USB 裝置(或者在傳輸中發生了錯誤), urb 回調函數将被 USB 核心調用.也就是上面初始化 urb 中的 skel_write_bulk_callback static void skel_write_bulk_callback(struct urb *urb) { struct usb_skel *dev; dev = urb->context; /* sync/async unlink faults aren't errors */ /*檢查 urb 的狀态,判斷這個 urb 是否成功完成傳輸*/ if (urb->status) { if(!(urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)) err("%s - nonzero write bulk status received: %d", __func__, urb->status); spin_lock(&dev->err_lock); dev->errors = urb->status; spin_unlock(&dev->err_lock); } /* free up our allocated buffer */ /*釋放配置設定給這個 urb 的緩沖區.*/ usb_buffer_free(urb->dev, urb->transfer_buffer_length, urb->transfer_buffer, urb->transfer_dma); up(&dev->limit_sem); }</code>
urb 回調函數是在中斷上下文運作, 是以它不應做任何記憶體配置設定, 持有任何信号量, 或任何可導緻程序休眠的事情. 如果從回調中送出 urb 并需要配置設定新記憶體塊, 需使用 GFP_ATOMIC 标志來告知 USB 核心不要休眠.
使用簡單的函數接口(urb函數的包裝)
有時隻是要發送或接受一些簡單的 USB 資料,可以使用簡單的函數接口:
<code>int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout) /*建立批量 urb 并發送到指定的裝置, 接着在傳回之前等待完成.*/ //struct usb_device *usb_dev :目标 USB 裝置指針 //unsigned int pipe :目标 USB 裝置的特定端點. 必須使用特定的宏建立. //void *data :如果是 OUT 端點, 指向要發送到裝置的資料的指針. 如果是 IN 端點, 這是從裝置讀取的資料的緩沖區指針. //int len : data 參數指向的緩沖的長度 //int *actual_length :指向函數放置真實位元組數的指針,根據端點方向,這些位元組要麼是被發送到裝置的,要麼是從裝置中讀取的. //int timeout :時鐘嘀哒數, 應等待的時間. 如果為 0, 函數永遠等待操作完成. //成功傳回0,actual_length 參數包含被傳送或從裝置中讀取的位元組數.否則傳回負的錯誤值. int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout) /*建立控制 urb 并發送到指定的裝置, 接着在傳回之前等待完成.*/ //struct usb_device *usb_dev :目标 USB 裝置指針 //unsigned int pipe :目标 USB 裝置的特定端點. 必須使用特定的宏建立. //__u8 request :控制消息的 USB 請求值. //__u8 requesttype :控制消息的 USB 請求類型. //__u16 value :控制消息的 USB 消息值. //__u16 index :控制消息的 USB 消息索引值. //void *data :如果是 OUT 端點, 指向要發送到裝置的資料的指針. 如果是 IN 端點, 這是從裝置讀取的資料的緩沖區指針. //__u16 size : data 參數指向的緩沖的長度 //int timeout :時鐘嘀哒數, 應等待的時間. 如果為 0, 函數永遠等待操作完成. //成功傳回被傳送到或從裝置讀取的位元組數.否則傳回負的錯誤值. int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout) /*建立中斷 urb 并發送到指定的裝置, 接着在傳回之前等待完成.其實就是usb_bulk_msg的包裝,所有參數和usb_bulk_msg一樣使用*/</code>
以上的函數不能在中斷上下文或持有自旋鎖時調用. 這些函數不能被取消, 是以小心使用; 確定驅動的 disconnect 函數了解足夠的資訊, 在允許它自己從記憶體被解除安裝之前等待調用結束.
其他 USB 函數
USB 核心中的一些輔助函數用來從所有的 USB 裝置中擷取标準資訊. 這些函數不能在中斷上下文或者持有自旋鎖時調用,因為他們内部都是使用上面介紹的簡單的接口函數.這裡就不一一介紹了,包括《LDD3》介紹的這些函數,在/drivers/usb/core/message.c都有。