urb的使用

Linux系統中,所有的 USB 設備通訊使用稱爲 urb 的東西( USB request block). 這個請求塊用 struct urb 結構描述並且可在 include/linux/usb.h 中找到

下面就來看看urb怎麼去使用

  • 結構體的定義
struct urb
{
	/* 私有的:只能由USB核心和主機控制器訪問的字段 */
	struct kref kref; /*urb引用計數 */
	spinlock_t lock; /* urb鎖 */
	void *hcpriv; /* 主機控制器私有數據 */
	int bandwidth; /* INT/ISO請求的帶寬 */
	atomic_t use_count; /* 併發傳輸計數 */
	u8 reject; /* 傳輸將失敗*/

	/* 公共的: 可以被驅動使用的字段 */
	struct list_head urb_list; /* 鏈表頭*/
	struct usb_device *dev; /* 關聯的USB設備 */
	unsigned int pipe; /* 管道信息 */
	int status; /* URB的當前狀態 */
	unsigned int transfer_flags; /* URB_SHORT_NOT_OK | ...*/
	void *transfer_buffer; /* 發送數據到設備或從設備接收數據的緩衝區 */
	dma_addr_t transfer_dma; /*用來以DMA方式向設備傳輸數據的緩衝區 */
	int transfer_buffer_length;/*transfer_buffer或transfer_dma 指向緩衝區的大小 */

	int actual_length; /* URB結束後,發送或接收數據的實際長度 */
	unsigned char *setup_packet; /* 指向控制URB的設置數據包的指針*/
	dma_addr_t setup_dma; /*控制URB的設置數據包的DMA緩衝區*/
	int start_frame; /*等時傳輸中用於設置或返回初始幀*/
	int number_of_packets; /*等時傳輸中等時緩衝區數據 */
	int interval; /* URB被輪詢到的時間間隔(對中斷和等時urb有效) */
	int error_count;  /* 等時傳輸錯誤數量 */
	void *context; /* completion函數上下文 */
	usb_complete_t complete; /* 當URB被完全傳輸或發生錯誤時,被調用 */
	struct usb_iso_packet_descriptor iso_frame_desc[0];
	/*單個URB一次可定義多個等時傳輸時,描述各個等時傳輸 */
};
struct usb_device *dev
指向這個 urb 要發送到的 struct usb_device 的指針. 這個變量必須被 USB 驅動初始化, 在這個 urb 被髮送到 USB 核心之前.
unsigned int pipe
端點消息,將這個 urb 發送到的特定 struct usb_device。這個變量必須被 USB 驅動初始化, 在這個 urb 被髮送到 USB 核心之前。
爲設置這個結構的成員,驅動必須依據端口的類型和流動的方向,使用恰當的函數,注意每個端點只可是一個類型
unsigned int usb_sndctrlpipe(struct usb_device *dev, unsigned int endpoint)
//指定一個控制 OUT 端點給特定的帶有特定端點號的 USB 設備.
unsigned int usb_rcvctrlpipe(struct usb_device *dev, unsigned int endpoint)
//指定一個控制 IN 端點給帶有特定端點號的特定 USB 設備.
unsigned int usb_sndbulkpipe(struct usb_device *dev, unsigned int endpoint)
//指定一個塊 OUT 端點給帶有特定端點號的特定 USB 設備
unsigned int usb_rcvbulkpipe(struct usb_device *dev, unsigned int endpoint)
//指定一個塊 IN 端點給帶有特定端點號的特定 USB 設備
unsigned int usb_sndintpipe(struct usb_device *dev, unsigned int endpoint)
//指定一箇中斷 OUT 端點給帶有特定端點號的特定 USB 設備
unsigned int usb_rcvintpipe(struct usb_device *dev, unsigned int endpoint)
//指定一箇中斷 IN 端點給帶有特定端點號的特定 USB 設備
unsigned int usb_sndisocpipe(struct usb_device *dev, unsigned int endpoint)
//指定一個同步 OUT 端點給帶有特定端點號的特定 USB 設備
unsigned int usb_rcvisocpipe(struct usb_device *dev, unsigned int endpoint)
//指定一個同步 IN 端點給帶有特定端點號的特定 USB 設備
unsigned int transfer_flags
這個變量可被設置爲不同位值, 根據這個 USB 驅動想這個 urb 發生什麼. 可用的值是:
URB_SHORT_NOT_OK
當置位, 它表示在某個 IN 端點上可能發生的短讀, 應當被 USB 當作一個錯誤. 這個值只對從 USB 設備讀 urb 有用, 不包括寫 urbs.
URB_ISO_ASAP
如果這個 urb 是同步的, 這個位可被置位. 如果驅動想這個 urb 被調度, 只要帶寬允許它這樣, 並且在此點設置這個 urb 中的 start_frame 變量. 如果對於同步 urb 這個位沒有被置位, 驅動必須指定 start_frame 值並且必須能夠正確恢復, 如果沒有在那個時刻啓動. 見下面的章節關於同步 urb 更多的消息.
URB_NO_TRANSFER_DMA_MAP
應當被置位, 當 urb 包含一個要被髮送的 DMA 緩衝. USB 核心使用這個被 transfer_dma 變量指向的緩衝, 不是被 transfer_buffer 變量指向的緩衝.
URB_NO_SETUP_DMA_MAP
同 URB_NO_TRANSFER_DMA_MAP 位, 這個位用來控制有一個已經建立的 urb DMA 緩衝. 如果它被置位, USB 核心使用這個被 setup_dma 變量而不是setup_packet 變量指向的緩衝.
URB_ASYNC_UNLINK
如果置位, 給這個 urb 的對 usb_unlink_urb 的調用幾乎立刻返回, 並且這個 urb 在後面被解除連接. 否則, 這個函數等待直到 urb 完全被去鏈並且在返回前結束. 小心使用這個位, 因爲它可有非常難於調試的同步問題.
URB_NO_FSBR
只有 UHCI USB 主機控制器驅動使用, 並且告訴它不要試圖做 Front Side Bus Reclamation 邏輯. 這個位通常應當不設置, 因爲有 UHCI 主機控制器的機器創建了許多 CPU 負擔, 並且 PCI 總線被等待設置了這個位的 urb 所飽和.
URB_ZERO_PACKET
如果置位, 一個塊 OUT urb 通過發送不包含數據的短報文而結束, 當數據對齊到一個端點報文邊界. 這被一些壞掉的 USB 設備所需要(例如一些 USB 到 IR 的設備) 爲了正確的工作..
URB_NO_INTERRUPT
如果置位, 硬件當 urb 結束時可能不產生一箇中斷. 這個位應當小心使用並且只在排隊多個到相同端點的 urb 時使用. USB 核心函數使用這個爲了做 DMA 緩衝傳送.
void *transfer_buffer
指向用在發送數據到設備(對一個 OUT urb)或者從設備中獲取數據(對於一個 IN urb)的緩衝的指針. 對主機控制器爲了正確存取這個緩衝, 它必須被使用一個對 kmalloc 調用來創建, 不是在堆棧或者靜態地. 對控制端點, 這個緩衝是給發送的數據階段.
dma_addr_t transfer_dma
用來使用 DMA 傳送數據到 USB 設備的緩衝.
int transfer_buffer_length
緩衝的長度, 被 transfer_buffer 或者 transfer_dma 變量指向(由於只有一個可被一個 urb 使用). 如果這是 0, 沒有傳送緩衝被 USB 核心所使用.
對於一個 OUT 端點, 如果這個端點最大的大小比這個變量指定的值小, 對這個 USB 設備的傳送被分成更小的塊爲了正確的傳送數據. 這種大的傳送發生在連續的 USB 幀. 提交一個大塊數據在一個 urb 中是非常快, 並且使 USB 主機控制器去劃分爲更小的快, 比以連續的順序發送小緩衝.
unsigned char *setup_packet
指向給一個控制 urb 的 setup 報文的指針. 它在位於傳送緩衝中的數據之前被傳送. 這個變量只對控制 urb 有效.
dma_addr_t setup_dma
給控制 urb 的 setupt 報文的 DMA 緩衝. 在位於正常傳送緩衝的數據之前被傳送. 這個變量只對控制 urb 有效.
usb_complete_t complete
指向完成處理者函數的指針, 它被 USB 核心調用當這個 urb 被完全傳送或者當 urb 發生一個錯誤. 在這個函數中, USB 驅動可檢查這個 urb, 釋放它, 或者重新提交它給另一次傳送.(見"completingUrbs: 完成回調處理者", 關於完成處理者的更多細節).
usb_complete_t 類型定義如此:
typedef void (*usb_complete_t)(struct urb *, struct pt_regs *);
void *context
指向數據點的指針, 它可被 USB 驅動設置. 它可在完成處理者中使用當 urb 被返回到驅動. 關於這個變量的細節見後續章節.
int actual_length
當這個 urb 被完成, 這個變量被設置爲數據的真實長度, 或者由這個 urb (對於 OUT urb)發送或者由這個 urb(對於 IN urb)接受. 對於 IN urb, 這個必須被用來替代 transfer_buffer_length 變量, 因爲接收的數據可能比整個緩衝大小小.
int status
當這個 urb 被結束, 或者開始由 USB 核心處理, 這個變量被設置爲 urb 的當前狀態. 一個 USB 驅動可安全存取這個變量的唯一時間是在 urb 完成處理者函數中(在"CompletingUrbs: 完成回調處理者"一節中描述). 這個限制是阻止競爭情況, 發生在這個 urb 被 USB 核心處理當中. 對於同步 urb, 在這個變量中的一個成功的值(0)只指示是否這個 urb 已被去鏈. 爲獲得在同步 urb 上的詳細狀態, 應當檢查 iso_frame_desc 變量.
這個變量的有效值包括:
0
這個 urb 傳送是成功的.
-ENOENT
這個 urb 被對 usb_kill_urb 的調用停止.
-ECONNRESET
urb 被對 usb_unlink_urb 的調用去鏈, 並且 transfer_flags 變量被設置爲 URB_ASYNC_UNLINK.
-EINPROGRESS
這個 urb 仍然在被 USB 主機控制器處理中. 如果你的驅動曾見到這個值, 它是一個你的驅動中的 bug.
-EPROTO
這個 urb 發生下面一個錯誤:
  • 一個 bitstuff 錯誤在傳送中發生.
  • 硬件沒有及時收到響應幀.
-EILSEQ
在這個 urb 傳送中有一個 CRC 不匹配.
-EPIPE
這個端點現在被停止. 如果這個包含的端點不是一個控制端點, 這個錯誤可被清除通過一個對函數 usb_clear_halt 的調用.
-ECOMM
在傳送中數據接收快於能被寫入系統內存. 這個錯誤值只對 IN urb.
-ENOSR
在傳送中數據不能從系統內存中獲取得足夠快, 以便可跟上請求的 USB 數據速率. 這個錯誤只對 OUT urb.
-EOVERFLOW
這個 urb 發生一個"babble"錯誤. 一個"babble"錯誤發生當端點接受數據多於端點的特定最大報文大小.
-EREMOTEIO
只發生在當 URB_SHORT_NOT_OK 標誌被設置在 urb 的 transfer_flags 變量, 並且意味着 urb 請求的完整數量的數據沒有收到.
-ENODEV
這個 USB 設備現在從系統中消失.
-EXDEV
只對同步 urb 發生, 並且意味着傳送只部分完成. 爲了決定傳送什麼, 驅動必須看單獨的幀狀態.
-EINVAL
這個 urb 發生了非常壞的事情. USB 內核文檔描述了這個值意味着什麼:
  • ISO 瘋了, 如果發生這個: 退出並回家.
  • 它也可發生, 如果一個參數在 urb 結構中被不正確地設置了, 或者如果在提交這個 urb 給 USB 核心的 usb_submit_urb 調用中, 有一個不正確的函數參數.
-ESHUTDOWN
這個 USB 主機控制器驅動有嚴重的錯誤; 它現在已被禁止, 或者設備和系統去掉連接, 並且這個urb 在設備被去除後被提交. 它也可發生當這個設備的配置改變, 而這個 urb 被提交給設備.
通常, 錯誤值 -EPROTO, -EILSEQ, 和 -EOVERFLOW 指示設備的硬件問題, 設備固件, 或者連接設備到計算機的線纜.
int start_frame
設置或返回同步傳送要使用的初始幀號.
int interval
urb 被輪詢的間隔. 這隻對中斷或者同步 urb 有效. 這個值的單位依據設備速度而不同. 對於低速和高速的設備, 單位是幀, 它等同於毫秒. 對於設備, 單位是宏幀的設備, 它等同於 1/8 微秒單位. 這個值必須被 USB 驅動設置給同步或者中斷 urb, 在這個 urb被髮送到 USB 核心之前.
int number_of_packets
只對同步 urb 有效, 並且指定這個 urb 要處理的同步傳送緩衝的編號. 這個值必須被 USB 驅動設置給同步 urb, 在這個 urb 發送給 USB 核心之前.
int error_count
被 USB 核心設置, 只給同步 urb 在它們完成之後. 它指定報告任何類型錯誤的同步傳送的號碼.
struct usb_iso_packet_descriptor iso_frame_desc[0]
只對同步 urb 有效. 這個變量是組成這個 urb 的一個 struct usb_iso_packet_descriptor 結構數組. 這個結構允許單個 urb 來一次定義多個同步傳送. 它也用來收集每個單獨傳送的傳送狀態.
結構 usb_iso_packet_descriptor 由下列成員組成:
unsigned int offset
報文數據所在的傳送緩衝中的偏移(第一個字節從 0 開始).
unsigned int length
這個報文的傳送緩衝的長度.
unsigned int actual_length
接收到給這個同步報文的傳送緩衝的數據長度.
unsigned int status
這個報文的單獨同步傳送的狀態. 它可採用同樣的返回值如同主 struct urb 結構的狀態變量.
  • urb處理流程

1.創建urb結構體

struct urb 結構在驅動中必須不被靜態創建, 或者在另一個結構中, 因爲這可能破壞 USB 核心給 urb 使用的引用計數方法
<span style="font-size:14px;">struct urb *usb_alloc_urb(int iso_packets, int mem_flags);</span>
int iso_packets
這個 urb 應當包含的同步報文的數目. 如果你不想創建一個同步 urb, 這個變量應當被設置爲 0
int mem_flags:
和傳遞給 kmalloc 函數調用來從內核分配內存的相同的標誌類型

2.初始化urb

    • 中斷urb
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,
			void *context, int interval);
struct urb *urb

指向要被初始化的 urb 的指針.

struct usb_device *dev

這個 urb 要發送到的 USB 設備.

unsigned int pipe

這個 urb 要被髮送到的 USB 設備的特定端點. 這個值被創建, 使用 usb_sndintpipe 或者 usb_rcvintpipe 函數.

void *transfer_buffer

指向緩衝的指針, 從那裏外出的數據被獲取或者進入數據被接受. 注意這不能是一個靜態的緩衝並且必須使用 kmalloc 調用來創建.

int buffer_length

緩衝的長度, 被 transfer_buffer 指針指向.

usb_complete_t complete

指針, 指向當這個 urb 完成時被調用的完成處理者.

void *context

指向數據塊的指針, 它被添加到這個 urb 結構爲以後被完成處理者函數獲取.

int interval

這個 urb 應當被調度的間隔. 見之前的 struct urb 結構的描述, 來找到這個值的正確單位.


usb_fill_int_urb 函數不設置 urb 中的 transfer_flags 變量,因此任何對這個成員的修改不得不由這個驅動自己完成。
    • 塊urb
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,
			void *context);

函數參數和 usb_fill_int_urb 函數的都相同。

沒有 interval 參數是因爲 bulk urb 沒有間隔值。

unsiged int pipe 變量必須使用 usb_sndbulkpipe 或者 usb_rcvbulkpipe 函數進行初始化。

    • 控制urb
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, void *context);

unsigned char *setup_packet

它必須指向要發送給端點的 setup 報文數據。


函數參數和 usb_fill_bulk_urb 函數都相同。

unsiged int pipe 變量必須使用 usb_sndctrlpipe 或者 usb_rcvictrlpipe 函數進行初始化。

usb_fill_control_urb 函數不設置 transfer_flags 變量在 urb 中, 因此任何對這個成員的修改必須遊驅動自己完成。

大部分驅動不使用這個函數,因爲使用在"USB 傳送不用 urb"一節中介紹的同步 API 調用更簡單.

    • 同步urb

不幸的是,同步 urb 沒有一個象中斷,控制,和塊 urb 的初始化函數。因此它們必須在驅動中"手動"初始化,在它們可被提交給 USB 核心之前。下面是一個如何正確初始化這類 urb 的例子。它是從 konicawc.c 內核驅動中取得的,它位於主內核源碼樹的 drivers/usb/media 目錄。

<pre name="code" class="cpp">urb->dev = dev;
urb->context = uvd;
urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp-1);
urb->interval = 1;
urb->transfer_flags = URB_ISO_ASAP;
urb->transfer_buffer = cam->sts_buf[i];
urb->complete = konicawc_isoc_irq;
urb->number_of_packets = FRAMES_PER_DESC;
urb->transfer_buffer_length = FRAMES_PER_DESC;
for (j=0; j < FRAMES_PER_DESC; j++) {
	urb->iso_frame_desc[j].offset = j;
	urb->iso_frame_desc[j].length = 1;
}
unsiged int pipe 變量必須使用 usb_sndisocpipe 或者 usb_rcvisocpipe 函數進行初始化。

3.提交urb

一旦 urb 被正確地創建,並且被 USB 驅動初始化,它就可以被提交給 USB 核心來發送出到 USB 設備
int usb_submit_urb(struct urb *urb, int mem_flags);
struct urb *urb 
參數是一個指向 urb 的指針,它指向要被髮送到的設備。
mem_flags 
參數等同於傳遞給 kmalloc 調用的參數,用來告訴 USB 核心這個時間如何及時分配任何內存緩衝。
GFP_ATOMIC
在中斷處理函數、底半部、tasklet、定時器處理函數以及urb完成函數中,和在調用者持有自旋鎖或者讀寫鎖時以及當驅動將current->state修改爲非 TASK_ RUNNING時,應使用此標誌。
GFP_NOIO
在存儲設備的塊I/O和錯誤處理路徑中,應使用此標誌。
GFP_KERNEL
如果沒有任何理由使用GFP_ATOMIC和GFP_NOIO,就使用GFP_ KERNEL。
在提交urb到USB核心後,直到完成函數被調用之前,不要訪問urb中的任何成員。

4.完成urb

如果對 usb_submit_urb 的調用成功,這個函數返回 0;否則,一個負錯誤值被返回。如果函數成功,urb 的完成處理者(如同被完成函數指針指定的)被確切地調用一次,當 urb 被完成。當這個函數被調用, USB 核心完成這個 urb, 並且對它的控制現在返回給設備驅動.

只有 3 個方法, 一個urb 可被結束並且使完成函數被調用:

  • urb 被成功發送給設備, 並且設備返回正確的確認. 對於一個 OUT urb, 數據被成功發送, 對於一個 IN urb, 請求的數據被成功收到. 如果發生這個, urb 中的狀態變量被設置爲 0.

  • 一些錯誤連續發生, 當發送或者接受數據從設備中. 被 urb 結構中的 status 變量中的錯誤值所記錄.

  • 這個 urb 被從 USB 核心去鏈. 這發生在要麼當驅動告知 USB 核心取消一個已提交的 urb 通過調用 usb_unlink_urb 或者 usb_kill_urb, 要麼當設備從系統中去除, 以及一個 urb 已經被提交給它.

5.取消urb

爲停止一個已經提交給 USB 核心的 urb, 函數 usb_kill_urb 或者 usb_unlink_urb 應當被調用:

<pre name="code" class="cpp">int usb_kill_urb(struct urb *urb); 
int usb_unlink_urb(struct urb *urb);

當函數是 usb_kill_urb,這個 urb 的生命循環就停止了。 這個函數常常在設備從系統去除時被使用,在去連接回調中。在usb_submit_urb 函數之後調用

對一些驅動,應當用 usb_unlink_urb 函數來告知 USB 去停止 urb。這個函數在返回到調用者之前不等待這個 urb 完全停止。這對於在中斷處理或者持有一個自旋鎖時停止 urb 時是有用的,因爲等待一個 urb 完全停止需要 USB 有能力使調用進程睡眠。爲了正確工作這個函數要求 URB_ASYNC_UNLINK 標誌值被設置在正被要求停止的 urb 中。

6.簡單的批量與控制urb

有時USB驅動程序只是從USB設備上接收或向USB設備發送一些簡單的數據,這時候,沒有必要將urb創建、初始化、提交、完成處理的整個流程走一遍,而可以使用兩個更簡單的函數,如下所示:
  • 簡單的的批量urb
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
參數爲批量消息要發送的USB 設備的指針。
unsigned int pipe
爲批量消息要發送到的USB設備的端點。
void *data
參數爲指向要發送或接收的數據緩衝區的指針。
int len
參數爲data參數所指向的緩衝區的長度。
int *actual_length
用於返回實際發送或接收的字節數。
int timeout
是發送超時,以jiffies爲單位,0意味着永遠等待。

  • 簡單的控制urb

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

指向控制消息發往的USB設備。

unsigned int pipe

控制消息要發往的USB設備的端點。

__u8 request

這個控制消息的USB請求值。

__u8 requesttype

這個控制消息的USB請求類型。

__u16 value

這個控制消息的USB消息值。

__u16 index

這個控制消息的USB消息索引值。

void *data

指向要發送或接收的數據緩衝區。

__u16 size

data參數所指向的緩衝區的大小。

int timeout

發送超時,以jiffies爲單位,0意味着永遠等待。

它們是同步的,因此不能在中斷上下文和持有自旋鎖的情況下使用。而且,該函數也不能被任何其他函數取消,因此,務必要使得驅動程序的 disconnect 函數掌握足夠的信息,以判斷和等待該調用的結束。

7.銷燬urb

爲了告訴 USB 核心驅動用完這個 urb,驅動必須調用 usb_free_urb 函數,在這個函數被調用之後,urb 結構消失,驅動不能再存取它。
void usb_free_urb(struct urb *urb);







參考:http://blog.csdn.net/kaizi318/article/details/7996073

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章