USB裝置驅動調試

來源:互聯網
上載者:User

當置位, 它指出任何在一個 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 位, 這個位用來控制有一個 DMA 緩衝已經建立的 urb. 如果它被置位, 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 結構的狀態變數.

13.3.2. 建立和銷毀 urb
struct urb 結構在驅動中必須不被靜態建立, 或者在另一個結構中, 因為這可能破壞 USB 核心給 urb 使用的引用計數方法. 它必須使用對 usb_alloc_urb 函數的調用而被建立. 這個函數有這個原型:

struct urb *usb_alloc_urb(int iso_packets, int mem_flags);

第一個參數, iso_packet, 是這個 urb 應當包含的同步報文的數目. 如果你不想建立一個同步 urb, 這個變數應當被設定為 0. 第 2 個參數, mem_flags, 是和傳遞給 kmalloc 函數調用來從核心分配記憶體的相同的標誌類型(見"flags 參數"一節, 第 8 章, 關於這些標誌的細節). 如果這個函數在分配足夠記憶體給這個 urb 成功,
一個指向 urb 的指標被返回給調用者. 如果傳回值是 NULL, 某個錯誤在 USB 核心中發生了, 並且驅動需要正確地清理.

在建立了一個 urb 之後, 它必須被正確初始化在它可被 USB 核心使用之前. 如何初始化不同類型 urb 見下一節

為了告訴 USB 核心驅動用完這個 urb, 驅動必須調用 usb_free_urb 函數. 這個函數只有一個參數:

void usb_free_urb(struct urb *urb);

參數是一個指向你要釋放的 struct urb 的指標. 在這個函數被調用之後, urb 結構消失, 驅動不能再存取它.

13.3.2.1. 中斷 urb
函數 usb_fill_int_urb 是一個幫忙函數, 來正確初始化一個urb 來發送給 USB 裝置的一個中斷端點:

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 結構的描述, 來找到這個值的正確單位.

13.3.2.2. 塊 urb
塊 urb 被初始化非常象中斷 urb. 做這個的函數是 usb_fill_bulk_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 函數的調用.

usb_fill_int_urb 函數不設定 urb 中的 transfer_flags 變數, 因此任何對這個成員的修改不得不由這個驅動自己完成.

13.3.2.3. 控制 urb
控制 urb 被初始化幾乎和 塊 urb 相同的方式, 使用對函數 usb_fill_control_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);

函數參數和 usb_fill_bulk_urb 函數都相同, 除了有個新參數, unsigned char *setup_packet, 它必須指向要發送給端點的 setup 報文資料. 還有, unsigned int pipe 變數必須被初始化, 使用對 usb_sndctrlpipe 或者 usb_rcvictrlpipe 函數的調用.

usb_fill_control_urb 函數不設定 transfer_flags 變數在 urb 中, 因此任何對這個成員的修改必須遊驅動自己完成. 大部分驅動不使用這個函數, 因為使用在"USB 傳送不用 urb"一節中介紹的同步 API 呼叫更簡單.

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

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;
}

13.3.3. 提交 urb
一旦 urb 被正確地建立,並且被 USB 驅動初始化, 它已準備好被提交給 USB 核心來發送出到 USB 裝置. 這通過調用函數 usb_submit_urb 實現:

int usb_submit_urb(struct urb *urb, int mem_flags);

urb 參數是一個指向 urb 的指標, 它要被發送到裝置. mem_flags 參數等同於傳遞給 kmalloc 調用的同樣的參數, 並且用來告訴 USB 核心如何及時分配任何記憶體緩衝在這個時間.

在 urb 被成功提交給 USB 核心之後, 應當從不試圖存取 urb 結構的任何成員直到完成函數被調用.

因為函數 usb_submit_urb 可被在任何時候被調用(包括從一個中斷上下文), mem_flags 變數的指定必須正確. 真正只有 3 個有效值可用, 根據何時 usb_submit_urb 被調用:

GFP_ATOMIC

這個值應當被使用無論何時下面的是真:

調用者處於一個 urb 完成處理者, 一個中斷, 一個後半部, 一個 tasklet, 或者一個時鐘回調.

調用者持有一個自旋鎖或者讀寫鎖. 注意如果正持有一個旗標, 這個值不必要.

current->state 不是 TASK_RUNNING. 狀態一直是 TASK_RUNNING 除非驅動已自己改變 current 狀態.

GFP_NOIO

這個值應當被使用, 如果驅動在塊 I/O 補丁中. 它還應當用在所有的儲存類型的錯誤處理補丁中.

GFP_KERNEL

這應當用在所有其他的情況中, 不屬於之前提到的類別.

13.3.4. 完成 urb: 完成回調處理者
如果對 usb_submit_urb 的調用成功, 傳遞對 urb 的控制給 USB 核心, 這個函數返回 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 已經被提交給它.

一個如何測試在一個 urb 完成調用中不同傳回值的例子在本章稍後展示.

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

int usb_kill_urb(struct urb *urb);

int usb_unlink_urb(struct urb *urb);

The urb parameter for both of these functions is a pointer to the urb that is to be canceled.

當函數是 usb_kill_urb, 這個 urb 的生命迴圈就停止了. 這個函數常常在裝置從系統去除時被使用, 在去串連回調中.

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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.