0.前言
哥們馬上就要被裁了,總得整理一下技術(shù)方面的積累,準(zhǔn)備開始下一輪的面試和找工作之旅了。。。。
1.概述
通用串行總線(USB)是主機(jī)和外圍設(shè)備之間的一種連接。
從拓?fù)渖蟻砜?,是一顆由幾個點(diǎn)對點(diǎn)的連接構(gòu)建而成的樹。這些連接是連接設(shè)備和集線器(hub)的四線電纜(底線、電源線和兩根信號線)。USB主控制器(host controller)負(fù)責(zé)詢問每一個USB設(shè)備是否有數(shù)據(jù)需要發(fā)送。
Linux內(nèi)核支持兩種主要類型的USB驅(qū)動程序:宿主(host)系統(tǒng)上的驅(qū)動程序和設(shè)備(device)上的驅(qū)動程序。宿主系統(tǒng)上的USB驅(qū)動程序控制插入其中的USB設(shè)備,USB設(shè)備的驅(qū)動程序控制該設(shè)備如何作為一個USB設(shè)備和主機(jī)通信。
USB驅(qū)動程序存在于不同的內(nèi)核子系統(tǒng)和USB硬件控制器之中,USB核心為USB驅(qū)動程序提供了一個用于訪問和控制USB硬件的接口。
2.USB設(shè)備基礎(chǔ)
USB設(shè)備的構(gòu)成,包括配置、接口和端點(diǎn),以及USB驅(qū)動程序如何綁定到USB接口上,而不是整個USB設(shè)備
- 端點(diǎn)
USB通信最基本的形式是通過一個名為端點(diǎn)(endpoint)的東西。USB端點(diǎn)只能往一個方向傳送數(shù)據(jù),從主機(jī)到設(shè)備(稱為輸出端點(diǎn))或者從設(shè)備到主機(jī)(稱為輸入端點(diǎn))。端點(diǎn)可以看作是單向的管道。
有四種不同類型的端點(diǎn): - 控制
用來控制對USB設(shè)備不同部分的訪問。通常用于配置設(shè)備、獲取設(shè)備信息、發(fā)送命令到設(shè)備或者獲取設(shè)備的狀態(tài)報告。每個USB設(shè)備都有一個名為“端點(diǎn)0”的控制端點(diǎn),USB核心使用該端點(diǎn)在插入時進(jìn)行設(shè)備的配置。 - 中斷
當(dāng)USB宿主要求設(shè)備傳輸數(shù)據(jù)時,中斷端點(diǎn)就以一個固定的速率來傳送少量的數(shù)據(jù)。通常還用于發(fā)送數(shù)據(jù)到USB設(shè)備以控制設(shè)備,一般不用來傳輸大量的數(shù)據(jù)。 - 批量
傳輸大批量的數(shù)據(jù)。常見于需要確保沒有數(shù)據(jù)丟失的傳輸?shù)脑O(shè)備。如果總線上的空間不足以發(fā)送整個批量包,它將被分割為多個包進(jìn)行傳輸。 - 等時
同樣可以傳送大批量的數(shù)據(jù),但數(shù)據(jù)是否到達(dá)是沒有保證的。實(shí)時的數(shù)據(jù)收集(例如音頻和視頻設(shè)備)幾乎毫無例外都使用這類端點(diǎn)。
控制和批量端點(diǎn)用于異步的數(shù)據(jù)傳輸。中斷和等時端點(diǎn)是周期性的。
內(nèi)核使用struct usb_host_endpoint結(jié)構(gòu)體來描述USB端點(diǎn)。該結(jié)構(gòu)體在另一個struct usb_endpoint_descriptor的結(jié)構(gòu)體中包含真正的端點(diǎn)信息。
bEndpointAddress,特定端點(diǎn)的USB地址。還包含了端點(diǎn)的方向??梢越Y(jié)合位掩碼USB_DIR_OUT和USB_DIR_IN,以確定該端點(diǎn)的數(shù)據(jù)是傳向設(shè)備還是主機(jī)。
bmAttributes,端點(diǎn)的類型。結(jié)合位掩碼USB_ENDPOINT_XFERTYPE_MASK,以確定此端點(diǎn)的類型是USB_ENDPOINT_XFER_ISOC(等時)、USB_ENDPOINT_XFER_BULK(批量)還是USB_ENDPOINT_XFER_INT(中斷)。
wMaxPacketSize,該端點(diǎn)一次可以處理的最大字節(jié)數(shù)。驅(qū)動程序可以發(fā)送數(shù)量大于此值的數(shù)據(jù)到端點(diǎn),在實(shí)際傳輸?shù)皆O(shè)備時,數(shù)據(jù)將被分割為wMaxPacketSize大小的塊。
bInterval,如果端點(diǎn)是中斷類型,該值是端點(diǎn)的間隔設(shè)置。 - 接口
USB接口只處理一種USB邏輯連接。內(nèi)核使用struct usb_interface結(jié)構(gòu)體來描述USB接口。USB核心把該結(jié)構(gòu)體傳遞給USB驅(qū)動程序,之后由USB驅(qū)動程序來負(fù)責(zé)控制該結(jié)構(gòu)體。
struct usb_host_interface *altsetting,一個接口結(jié)構(gòu)體數(shù)組,包含所有可能用于該接口的可選設(shè)置。
unsigned num_altsetting,altsetting指針?biāo)傅目蛇x設(shè)置的數(shù)量。
struct usb_host_interface *cur_altsetting,指向altsetting數(shù)組內(nèi)部的指針,表示該接口的當(dāng)前活動設(shè)置。
int minor,如果捆綁到該接口的USB驅(qū)動程序使用USB主設(shè)備號,這個變量包含USB核心分配給該接口的次設(shè)備號,僅在一個成功的usb_register_dev調(diào)用之后才有效。 - 配置
一個USB設(shè)備可以有多個配置,可以在配置之間切換以改變設(shè)備的狀態(tài),而一個時刻只能激活一個配置。
Linux使用struct usb_host_config結(jié)構(gòu)體來描述USB配置,使用struct usb_device結(jié)構(gòu)體來描述整個USB設(shè)備。
USB設(shè)備驅(qū)動程序需要把一個給定的struct usb_interface結(jié)構(gòu)體的數(shù)據(jù)轉(zhuǎn)換為struct usb_device結(jié)構(gòu)體,用于轉(zhuǎn)換功能的函數(shù)是interface_to_usbdev。
USB設(shè)備由許多不同的邏輯單元構(gòu)成,邏輯單元之間的關(guān)系:
設(shè)備通常具有一個或者更多的配置
配置經(jīng)常具有一個或者更多的接口
接口通常具有一個或者更多的設(shè)置
接口沒有或者具有一個以上的端點(diǎn)
3.USB urb
Linux內(nèi)核中的USB代碼通過一個稱為urb(USB請求塊)的東西與所有的USB設(shè)備通信,使用struct urb結(jié)構(gòu)體來描述這個請求塊。
urb被用來以一種異步的方式往/從特定的USB設(shè)備上的特定USB端點(diǎn)發(fā)送/接收數(shù)據(jù)。
一個urb的典型生命周期:
由USB設(shè)備驅(qū)動程序創(chuàng)建。
分配給一個特定的USB設(shè)備的特定端點(diǎn)。
由USB設(shè)備驅(qū)動程序遞交到USB核心。
由USB核心遞交到特定設(shè)備的特定USB中控制器驅(qū)動程序。
由USB主控制器驅(qū)動程序處理,從設(shè)備進(jìn)行USB傳送。
當(dāng)urb結(jié)束之后,USB主控制器驅(qū)動程序通知USB設(shè)備驅(qū)動程序。
- 創(chuàng)建和銷毀urb
必須使用usb_alloc_urb函數(shù)來創(chuàng)建。
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);
iso_packets,是該urb應(yīng)該包含的等時數(shù)據(jù)包的數(shù)量,如果不打算創(chuàng)建等時urb,該值為0。
mem_flags,和傳遞給用于從內(nèi)核分配內(nèi)存的kmalloc函數(shù)的標(biāo)志有相同的類型。
如果成功為urb分配了足夠的內(nèi)存空間,指向該urb的指針將被返回給調(diào)用函數(shù),如果返回為NULL,表示USB核心發(fā)生了錯誤。
必須調(diào)用usb_free_urb函數(shù)告訴USB核心驅(qū)動程序已經(jīng)使用完urb。
void usb_free_urb(struct urb *urb);
urb,指向所需釋放的struct urb的指針,在該函數(shù)被調(diào)用后,urb結(jié)構(gòu)體就會消失,驅(qū)動程序不能再訪問它。
中斷urb
usb_fill_int_urb用來正確地初始化即將被發(fā)送到USB設(shè)備的中斷端點(diǎn)的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);
struct urb *urb,指向需要初始化的urb的指針。
struct usb_device *dev,該urb所發(fā)送的目標(biāo)USB設(shè)備。
unsigned int pipe,該urb所發(fā)送的目標(biāo)USB設(shè)備的特定端點(diǎn)。使用usb_sndintpipe或者usb_rcvintpipe函數(shù)創(chuàng)建。
void *transfer_buffer,用來保存外發(fā)數(shù)據(jù)或者接收數(shù)據(jù)的緩沖區(qū)的指針。必須使用kmalloc調(diào)用來創(chuàng)建。
int buffer_length,transfer_buffer指針?biāo)赶虻木彌_區(qū)的大小。
usb_complete_t complete_fn,當(dāng)該urb結(jié)束之后調(diào)用的結(jié)束處理例程的指針。
void *context,指向一小數(shù)據(jù)塊,該塊被添加到urb結(jié)構(gòu)體中以便進(jìn)行結(jié)束處理例程后面的查找。
int interval,該urb應(yīng)該被調(diào)度的間隔。
批量urb
使用的函數(shù)usb_fill_bulk_urb。
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);
參數(shù)和usb_fill_int_urb函數(shù)一樣,不過沒有時間間隔參數(shù),pipe變量使用usb_sndbulkpipe或usb_rcvbulkpipe函數(shù)來初始化。
控制urb
調(diào)用usb_fill_control_urb函數(shù)。
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);
參數(shù)和usb_fill_bulk_urb函數(shù)一樣,setup_packet指向即將被發(fā)送到端點(diǎn)的設(shè)備數(shù)據(jù)包的數(shù)據(jù),pipe變量使用usb_sndctrlpipe或usb_rcvctrlpipe函數(shù)來初始化。
等時urb
必須在驅(qū)動程序中“手工地”進(jìn)行初始化。 - 提交urb
一旦urb被USB驅(qū)動程序正確地創(chuàng)建和初始化之后,就可以提交到USB核心以發(fā)送到USB設(shè)備了。通過調(diào)用usb_submit_urb函數(shù)來完成。
int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
urb參數(shù)指向即將被發(fā)送到設(shè)備的urb的指針。
mem_flags參數(shù)等同于傳遞給kmalloc調(diào)用的同一個參數(shù),用于告訴USB核心如何在此時及時地分配內(nèi)存緩沖區(qū)。 - 結(jié)束urb:結(jié)束回調(diào)處理例程
如果調(diào)用usb_submit_urb成功,把對urb的控制轉(zhuǎn)交給USB核心,該函數(shù)返回0,否則,返回負(fù)的錯誤號。如果函數(shù)調(diào)用成功,當(dāng) urb結(jié)束的時候,usb的結(jié)束處理例程(由結(jié)束函數(shù)指針指定)正好被調(diào)用一次。
只有三種結(jié)束urb和調(diào)用結(jié)束函數(shù)的情形:
urb被成功地發(fā)送到了設(shè)備,設(shè)備返回了正確的確認(rèn)。urb中的status變量被設(shè)置為0。
發(fā)送數(shù)據(jù)到設(shè)備或者從設(shè)備接收數(shù)據(jù)時發(fā)生了錯誤。錯誤情況由urb結(jié)構(gòu)體中的status變量的錯誤值來指示。
urb從USB核心中被“解開鏈接”。 - 取消urb
調(diào)用usb_kill_urb或usb_unlink_urb函數(shù)來終止一個已經(jīng)被提交到USB核心的urb。
void usb_kill_urb(struct urb *urb);
int usb_unlink_urb(struct urb *urb);
urb的參數(shù)指向即將被取消的urb的指針。
如果調(diào)用usb_kill_urb函數(shù),該urb的生命周期將被終止。通常是當(dāng)設(shè)備從系統(tǒng)中被斷開時,在斷開回調(diào)函數(shù)中調(diào)用該函數(shù)。
對于某些驅(qū)動程序而言,應(yīng)該使用usb_unlink_urb函數(shù)來告訴USB核心終止一個urb。該函數(shù)并不等到urb完全被終止后才返回到調(diào)用函數(shù)。
4.USB驅(qū)動程序
驅(qū)動程序把驅(qū)動程序?qū)ο笞缘経SB子系統(tǒng)中,稍后再使用制造商和設(shè)備標(biāo)識來判斷是否已經(jīng)安裝了硬件。
USB核心使用struct usb_device_id結(jié)構(gòu)體來判斷對于一個設(shè)備該使用哪一個驅(qū)動程序,熱插拔腳本使用它來確定當(dāng)一個特定的設(shè)備插入到系統(tǒng)時該自動裝載哪一個驅(qū)動程序文章來源:http://www.zghlxwxcb.cn/news/detail-828317.html
struct usb_device_id {
__u16 match_flags; /*確定設(shè)備和結(jié)構(gòu)體中下列字段中的哪一個相匹配*/
__u16 idVendor; /*設(shè)備的USB制造商ID*/
__u16 idProduct; /*設(shè)備的USB產(chǎn)品ID*/
/*定義了制造商指派的產(chǎn)品的版本號范圍的最低和最高值。bcdDevice_hi值包括在內(nèi),該值是最高編號的設(shè)備的編號*/
__u16 bcdDevice_lo;
__u16 bcdDevice_hi;
/*分別定義設(shè)備的類型、子類型和協(xié)議。這些值詳細(xì)說明了整個設(shè)備的行為,包括該設(shè)備上的所有接口*/
__u8 bDeviceClass;
__u8 bDeviceSubClass;
__u8 bDeviceProtocol;
/*分別定義了類型、子類型和單個接口的協(xié)議*/
__u8 bInterfaceClass;
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
__u8 bInterfaceNumber;
/*不是用來比較是否匹配的,包含了驅(qū)動程序在USB驅(qū)動程序的探測回調(diào)函數(shù)中可以用來區(qū)分不同設(shè)備的信息*/
kernel_ulong_t driver_info
__attribute__((aligned(sizeof(kernel_ulong_t))));
};
有許多用來初始化該結(jié)構(gòu)體的宏:
USB_DEVICE(vend, prod),僅和指定的制造商和產(chǎn)品ID值相匹配。用于需要一個特定驅(qū)動程序的USB設(shè)備。
USB_DEVICE_VER(vend, prod, lo, hi),僅和某版本范圍內(nèi)的指定制造商和產(chǎn)品ID值相匹配。
USB_DEVICE_INFO(cl, sc, pr),僅和USB設(shè)備的指定類型相匹配。
USB_INTERFACE_INFO(cl, sc, pr),僅和USB接口的指定類型相匹配。文章來源地址http://www.zghlxwxcb.cn/news/detail-828317.html
- 注冊USB驅(qū)動程序
必須創(chuàng)建的主結(jié)構(gòu)體是struct usb_driver,必須由USB驅(qū)動程序來填寫,包括許多回調(diào)函數(shù)和變量,向USB核心代碼描述了USB驅(qū)動程序。
以struct usb_driver指針為參數(shù)的usb_register_driver函數(shù)調(diào)用把struct usb_driver注冊到USB核心。struct usb_driver { const char *name; /*指向驅(qū)動程序名字的指針。在內(nèi)核的所有USB驅(qū)動程序中必須是唯一的,通常被設(shè)置為和驅(qū)動程序模塊名相同的名字。*/ int (*probe) (struct usb_interface *intf, const struct usb_device_id *id); /*指向USB驅(qū)動程序中的探測函數(shù)的指針。*/ void (*disconnect) (struct usb_interface *intf); /*指向USB驅(qū)動程序中的斷開函數(shù)的指針。當(dāng)struct usb_interface被從系統(tǒng)中移除或者驅(qū)動程序正在從USB核心中卸載時,USB核心將調(diào)用該函數(shù)。*/ int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code, void *buf); int (*suspend) (struct usb_interface *intf, pm_message_t message); int (*resume) (struct usb_interface *intf); int (*reset_resume)(struct usb_interface *intf); int (*pre_reset)(struct usb_interface *intf); int (*post_reset)(struct usb_interface *intf); const struct usb_device_id *id_table; /*指向struct usb_device_id表的指針,包含了一系列驅(qū)動程序可以支持的所有不同類型的USB設(shè)備。*/ struct usb_dynids dynids; struct usbdrv_wrap drvwrap; unsigned int no_dynamic_id:1; unsigned int supports_autosuspend:1; unsigned int disable_hub_initiated_lpm:1; unsigned int soft_unbind:1; };
當(dāng)USB驅(qū)動程序?qū)⒁恍遁d時,需要把struct usb_drvier從內(nèi)核中注銷,通過調(diào)用usb_deregister_driver來完成該工作。 - 探測和斷開
探測和斷開回調(diào)函數(shù)都是在USB集線器內(nèi)核線程的上下文中被調(diào)用的,因此在其中睡眠是合法的。
在探測回調(diào)函數(shù)中,USB驅(qū)動程序應(yīng)該初始化人任何可能用于控制USB設(shè)備的局部結(jié)構(gòu)體,還應(yīng)該把所需的任何設(shè)備相關(guān)信息保存到局部結(jié)構(gòu)體中。
USB驅(qū)動程序需要在設(shè)備生命周期的稍后時間獲取和該結(jié)構(gòu)體struct usb_interface相關(guān)聯(lián)的局部數(shù)據(jù)結(jié)構(gòu)體,可以調(diào)用usb_set_intfdata函數(shù):
void usb_set_intfdata(struct usb_interface *intf, void *data);
該函數(shù)接受一個指向任意數(shù)據(jù)類型的指針,把它保存到struct usb_interface結(jié)構(gòu)體中以便后面訪問,調(diào)用usb_get_intfdata函數(shù)來獲取數(shù)據(jù):
void *usb_get_intfdata(struct usb_interface *intf);
usb_get_intfdata通常在USB驅(qū)動程序的打開函數(shù)和斷開函數(shù)中調(diào)用。
如果USB驅(qū)動程序沒有和設(shè)備與用戶交互的另一種類型的子系統(tǒng)相關(guān)聯(lián),驅(qū)動程序可以使用USB主設(shè)備號,以便在用戶空間使用傳統(tǒng)的字符驅(qū)動程序接口。USB驅(qū)動程序需要在探測函數(shù)中調(diào)用usb_register_dev函數(shù)來把設(shè)備注冊到USB核心。
usb_register_dev函數(shù)需要一個指向struct usb_interface的指針和一個指向struct usb_class_driver結(jié)構(gòu)的指針。int usb_register_dev(struct usb_interface *intf, struct usb_class_driver *class_driver);
當(dāng)一個USB設(shè)備被斷開時,和該設(shè)備相關(guān)聯(lián)的所有資源都應(yīng)該被盡可能的清理掉。必須調(diào)用usb_deregister_dev函數(shù)把次設(shè)備號交還USB核心。struct usb_class_driver { char *name; /*sysfs用來描述設(shè)備的名字*/ char *(*devnode)(struct device *dev, umode_t *mode); /*回調(diào)函數(shù),創(chuàng)建設(shè)備節(jié)點(diǎn)*/ const struct file_operations *fops; /*指向struct file_operations的指針,驅(qū)動程序定義該結(jié)構(gòu)體,用它來注冊為字符設(shè)備*/ int minor_base; /*為驅(qū)動程序指派的次設(shè)備號范圍的開始值*/ };
在斷開函數(shù)中,從接口獲取之前調(diào)用usb_set_intfdata設(shè)置的任何數(shù)據(jù)也是很重要的,然后設(shè)置struct usb_interface結(jié)構(gòu)體中的數(shù)據(jù)指針為NULL。
在USB設(shè)備已經(jīng)被斷開之后,如果驅(qū)動程序通過調(diào)用usb_submit_urb來提交一個urb給它,提交將會失敗并返回錯誤值-EPIPE。 - 提交和控制urb
當(dāng)驅(qū)動程序有數(shù)據(jù)要發(fā)送到USB設(shè)備時,必須分配一個urb來把數(shù)據(jù)傳輸給設(shè)備(struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)),在urb被成功分配之后,創(chuàng)建一個DMA緩沖區(qū)以最高效的方式發(fā)送數(shù)據(jù)到設(shè)備,傳遞給驅(qū)動程序的數(shù)據(jù)復(fù)制到該緩沖區(qū)中(void *usb_alloc_coherent(struct usb_device *dev, size_t size, gfp_t mem_flags, dma_addr_t *dma))。一旦數(shù)據(jù)從用戶空間正確的復(fù)制到局部緩沖區(qū)中,urb必須在可以被提交給USB核心之前被正確地初始化(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)),然后就可以被提交給USB核心以傳輸?shù)皆O(shè)備(int usb_submit_urb(struct urb *urb, gfp_t mem_flags))。
在urb被成功傳輸?shù)経SB設(shè)備之后(或者傳輸沖發(fā)生了某些事情),urb回調(diào)函數(shù)將被USB核心調(diào)用。/* 創(chuàng)建一個urb */ urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { retval = -ENOMEM; goto error; } /* 創(chuàng)建DMA緩沖區(qū),并把數(shù)據(jù)拷貝到緩沖區(qū) */ buf = usb_alloc_coherent(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; } /* 初始化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); /* 把數(shù)據(jù)從批量端口發(fā)出 */ retval = usb_submit_urb(urb, GFP_KERNEL); mutex_unlock(&dev->io_mutex); if (retval) { dev_err(&dev->interface->dev, "%s - failed submitting write urb, error %d\n", __func__, retval); goto error_unanchor; }
回調(diào)函數(shù)中做的第一件事是檢查urb的狀態(tài),以確定該urb是否已經(jīng)成功地結(jié)束。之后回調(diào)函數(shù)釋放傳輸時分配給該urb的緩沖區(qū)。static void skel_write_bulk_callback(struct urb *urb) { struct usb_skel *dev; dev = urb->context; /* sync/async 解鏈接故障不是錯誤 */ if (urb->status) { if (!(urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)) dev_err(&dev->interface->dev, "%s - nonzero write bulk status received: %d\n", __func__, urb->status); spin_lock(&dev->err_lock); dev->errors = urb->status; spin_unlock(&dev->err_lock); } /* 釋放已分配的緩沖區(qū) */ usb_free_coherent(urb->dev, urb->transfer_buffer_length, urb->transfer_buffer, urb->transfer_dma); up(&dev->limit_sem); }
urb回調(diào)函數(shù)是運(yùn)行在中斷上下文中的,因此它不應(yīng)該進(jìn)行任何內(nèi)存分配、持有任何信號量或者做任何其他可能導(dǎo)致進(jìn)程睡眠的事情。 - 不使用urb的USB傳輸
有時候USB驅(qū)動程序只是要發(fā)送或者接收一些簡單的USB數(shù)據(jù),有兩個更簡單的接口函數(shù)可以使用usb_bulk_msg和 usb_control_msg。
usb_bulk_msg創(chuàng)建一個USB批量urb,把它發(fā)送到指定的設(shè)備,然后在返回調(diào)用者之前等待它的結(jié)束。
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout);
struct usb_device *usb_dev,指向批量消息所發(fā)送的目標(biāo)USB設(shè)備的指針。
unsigned int pipe,該批量消息所發(fā)送的目標(biāo)USB設(shè)備的特定端點(diǎn),調(diào)用usb_sndbulkpipe或usb_rcvbulkpipe來創(chuàng)建。
void *data,如果是一個OUT端點(diǎn),指向即將發(fā)送到設(shè)備的數(shù)據(jù)的指針。如果是一個IN端點(diǎn),指向從設(shè)備讀取的數(shù)據(jù)應(yīng)該存放的位置的指針。
int len,data參數(shù)所指緩沖區(qū)的大小。
int *actual_length,指向保存實(shí)際傳輸字節(jié)數(shù)的位置的指針。
int timeout,以jiffies為單位的應(yīng)該等待的超時時間。如果該值為0,該函數(shù)將一直等待消息的結(jié)束。
如果函數(shù)調(diào)用成功,返回值為0;否則,返回一個負(fù)的錯誤值。如果成功,actual_length參數(shù)包含從該消息發(fā)送或者接收的字節(jié)數(shù)。
不能在一個中斷上下文中或者在持有自旋鎖的情況下調(diào)用usb_bulk_msg函數(shù)。
usb_control_msg,允許驅(qū)動程序發(fā)送和接收USB控制消息。
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);
struct usb_device *dev,指向控制消息所發(fā)送的目標(biāo)USB設(shè)備的指針。
unsigned int pipe,該控制消息所發(fā)送的目標(biāo)USB設(shè)備的特定端點(diǎn),調(diào)用usb_sndctrlpipe或usb_rcvctrlpipe來創(chuàng)建。
__u8 request,控制消息的USB請求值。
__u8 requesttype,控制消息的USB請求類型值。
__u16 value,控制消息的USB消息值。
__u16 index,控制消息的USB消息索引值。
void *data,如果是一個OUT端點(diǎn),指向即將發(fā)送到設(shè)備的數(shù)據(jù)的指針。如果是一個IN端點(diǎn),指向從設(shè)備讀取的數(shù)據(jù)應(yīng)該存放的位置的指針。
__u16 size,data參數(shù)所指緩沖區(qū)的大小。
int timeout,以jiffies為單位的應(yīng)該等待的超時時間。如果該值為0,該函數(shù)將一直等待消息的結(jié)束。
如果函數(shù)調(diào)用成功,返回傳輸?shù)皆O(shè)備或者從設(shè)備讀取的字節(jié)數(shù);如果不成功,返回一個負(fù)的錯誤值。
不能在一個中斷上下文中或者在持有自旋鎖的情況下調(diào)用usb_control_msg函數(shù)。 - 其他USB數(shù)據(jù)函數(shù)
USB核心中的許多輔助函數(shù)可以用來從所有USB設(shè)備中獲取標(biāo)準(zhǔn)的信息。這些函數(shù)不能在一個中斷上下文中或者持有自旋鎖的情況下調(diào)用。
usb_get_descriptor函數(shù)從指定的設(shè)備獲取指定的USB描述符。
int usb_get_descriptor(struct usb_device *dev, unsigned char desctype, unsigned char descindex, void *buf, int size);
USB驅(qū)動程序可以使用該函數(shù)來從struct usb_device結(jié)構(gòu)體中獲取任何沒有存在于已有struct usb_device和struct usb_interface結(jié)構(gòu)體中的設(shè)備描述符。
struct usb_device *dev,指向想要獲取設(shè)備描述符的目標(biāo)USB設(shè)備的指針。
unsigned char desctype,描述符的類型。
unsigned char descindex,應(yīng)該從設(shè)備獲取的描述符的編號。
void *buf,指向復(fù)制描述符到其中的緩沖區(qū)的指針。
int size,buf變量所指內(nèi)存的大小。
如果函數(shù)調(diào)用成功,返回從設(shè)備讀取的字節(jié)數(shù)。否則,返回一個由該函數(shù)調(diào)用的底層的usb_control_msg函數(shù)返回的一個負(fù)的錯誤值。
usb_get_descriptor調(diào)用更常用于從USB設(shè)備獲取一個字符串,提供usb_string的輔助函數(shù)來完成該工作。
int usb_string(struct usb_device *dev, int index, char *buf, size_t size);
返回從USB設(shè)備讀取的已經(jīng)轉(zhuǎn)換為ISO 8859-1格式的字符串。是USB設(shè)備的字符串的典型格式。
參考資料:
《LINUX設(shè)備驅(qū)動程序第三版》
linux-4.9.88
到了這里,關(guān)于嵌入式Linux開發(fā)-USB驅(qū)動的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!