Linux裝置驅動程式學習(18)-USB 驅動程式(三)

來源:互聯網
上載者:User
USB urb (USB request block) 核心使用2.6.29.4 USB 裝置驅動代碼通過urb和所有的 USB 裝置通訊。urb用 struct urb 結構描述(include/linux/usb.h )。
urb以一種非同步方式同一個特定USB裝置的特定端點發送或接受資料。一個 USB 裝置驅動可根據驅動的需要,分配多個 urb 給一個端點或重用單個 urb 給多個不同的端點。裝置中的每個端點都處理一個 urb 隊列, 所以多個 urb 可在隊列清空之前被發送到相同的端點。 一個 urb 的典型生命迴圈如下:
(1)被建立;
(2)被分配給一個特定 USB 裝置的特定端點;
(3)被提交給 USB 核心;
(4)被 USB 核心提交給特定裝置的特定 USB 主機控制器驅動;
(5)被 USB 主機控制器驅動處理, 並傳送到裝置;
(6)以上操作完成後,USB主機控制器驅動通知 USB 裝置驅動。 urb 也可被提交它的驅動在任何時間取消;如果裝置被移除,urb 可以被USB核心取消。urb 被動態建立並包含一個內部引用計數,使它們可以在最後一個使用者釋放它們時被自動釋放。 struct urb
struct urb {/* private: usb core and host controller only fields in the urb */struct kref kref; /* URB引用計數 */void *hcpriv; /* host控制器的私人資料 */    atomic_t use_count; /* 當前提交計數 */    atomic_t reject; /* 提交失敗計數 */int unlinked; /* 串連失敗代碼 *//* public: documented fields in the urb that can be used by drivers */struct list_head urb_list; /* list head for use by the urb's                     * current owner */struct list_head anchor_list; /* the URB may be anchored */struct usb_anchor *anchor;struct usb_device *dev; /* 指向這個 urb 要發送的目標 struct usb_device 的指標,這個變數必須在這個 urb 被發送到 USB 核心之前被 USB 驅動初始化.*/struct usb_host_endpoint *ep; /* (internal) pointer to endpoint */unsigned int pipe; /* 這個 urb 所要發送到的特定struct usb_device的端點訊息,這個變數必須在這個 urb 被發送到 USB 核心之前被 USB 驅動初始化.必須由下面的函數產生*/int status; /*當 urb開始由 USB 核心處理或處理結束, 這個變數被設定為 urb 的目前狀態. USB 驅動可安全訪問這個變數的唯一時間是在 urb 結束處理常式函數中. 這個限制是為防止競態. 對於等時 urb, 在這個變數中成功值(0)只表示這個 urb 是否已被去鏈. 為獲得等時 urb 的詳細狀態, 應當檢查 iso_frame_desc 變數. */unsigned int transfer_flags; /* 傳輸設定*/void *transfer_buffer; /* 指向用於發送資料到裝置(OUT urb)或者從裝置接收資料(IN urb)的緩衝區指標。為了主機控制器驅動正確訪問這個緩衝, 它必須使用 kmalloc 調用來建立, 不是在堆棧或者靜態記憶體中。 對控制端點, 這個緩衝區用於資料中轉*/    dma_addr_t transfer_dma; /* 用於以 DMA 方式傳送資料到 USB 裝置的緩衝區*/int transfer_buffer_length; /* transfer_buffer 或者 transfer_dma 變數指向的緩衝區大小。如果這是 0, 傳送緩衝沒有被 USB 核心所使用。對於一個 OUT 端點, 如果這個端點大小比這個變數指定的值小, 對這個 USB 裝置的傳輸將被分成更小的塊,以正確地傳送資料。這種大的傳送以連續的 USB 幀進行。在一個 urb 中提交一個大塊資料, 並且使 USB 主機控制器去劃分為更小的塊, 比以連續地順序發送小緩衝的速度快得多*/int actual_length; /* 當這個 urb 完成後, 該變數被設定為這個 urb (對於 OUT urb)發送或(對於 IN urb)接受資料的真實長度.對於 IN urb, 必須是用此變數而非 transfer_buffer_length , 因為接收的資料可能比整個緩衝小*/unsigned char *setup_packet; /* 指向控制urb的設定資料包指標.它在傳送緩衝中的資料之前被傳送(用於控制 urb)*/    dma_addr_t setup_dma; /* 控制 urb 用於設定資料包的 DMA 緩衝區地址,它在傳送普通緩衝區中的資料之前被傳送(用於控制 urb)*/int start_frame; /* 設定或返回初始的幀數量(用於等時urb) */int number_of_packets; /* 指定urb所處理的等時傳輸緩衝區的數量(用於等時urb,在urb被發送到USB核心前,必須設定) */int interval; /*urb 被輪詢的時間間隔. 僅對中斷或等時 urb 有效. 這個值的單位依據裝置速度而不同. 對於低速和高速的裝置, 單位是幀, 它等同於毫秒. 對於其他裝置, 單位是微幀, 等同於 1/8 毫秒. 在 urb被發送到 USB 核心之前,此值必須設定.*/int error_count; /* 等時urb的錯誤計數,由USB核心設定 */void *context; /* 指向一個可以被USB驅動模組設定的資料區塊. 當 urb 被返回到驅動時,可在結束處理常式中使用. */    usb_complete_t complete; /* 結束處理常式函數指標, 當 urb 被完全傳送或發生錯誤,它將被 USB 核心調用. 此函數檢查這個 urb, 並決定釋放它或重新提交給另一個傳輸中*/struct usb_iso_packet_descriptor iso_frame_desc[0];/* (僅用於等時urb)usb_iso_packet_descriptor結構體允許單個urb一次定義許多等時傳輸,它用於收集每個單獨的傳輸狀態*/};struct usb_iso_packet_descriptor {unsigned int offset; /* 該資料包的資料在傳輸緩衝區中的位移量(第一個位元組為0) */unsigned int length; /* 該資料包的傳輸緩衝區大小 */unsigned int actual_length; /* 等時資料包接收到傳輸緩衝區中的資料長度 */int status; /* 該資料包的單個等時傳輸狀態。它可以把相同的傳回值作為主struct urb 結構體的狀態變數 */};typedef void (*usb_complete_t)(struct urb *);
上述結構體中unsigned int pipe;的產生函數(define):
static inline unsigned int __create_pipe(struct usb_device *dev,unsigned int endpoint){return (dev->devnum << 8) | (endpoint << 15);}/* Create various pipes... */#define usb_sndctrlpipe(dev,endpoint)    \((PIPE_CONTROL << 30) | __create_pipe(dev, endpoint))#define usb_rcvctrlpipe(dev,endpoint)    \((PIPE_CONTROL << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)#define usb_sndisocpipe(dev,endpoint)    \((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev, endpoint))#define usb_rcvisocpipe(dev,endpoint)    \((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)#define usb_sndbulkpipe(dev,endpoint)    \((PIPE_BULK << 30) | __create_pipe(dev, endpoint))#define usb_rcvbulkpipe(dev,endpoint)    \((PIPE_BULK << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)#define usb_sndintpipe(dev,endpoint)    \((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint))#define usb_rcvintpipe(dev,endpoint)    \((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)//snd:OUT rcv:IN  ctrl:控制  isoc:等時  bulk:批量 int:中斷
上述結構體中unsigned int transfer_flags;的範圍:
/* * urb->transfer_flags: * * Note: URB_DIR_IN/OUT is automatically set in usb_submit_urb(). */#define URB_SHORT_NOT_OK    0x0001    /* 置位時,任何在 IN 端點上發生的簡短讀取, 被 USB 核心當作錯誤. 僅對從 USB 裝置讀取的 urb 有用 */#define URB_ISO_ASAP        0x0002    /* 若為等時 urb , 驅動想調度這個 urb 時,可置位該位, 只要頻寬允許且想在此時設定 urb 中的 start_frame 變數. 若沒有置位,則驅動必須指定 start_frame 值,且傳輸如果不能在當時啟動的話,必須能夠正確恢複 */#define URB_NO_TRANSFER_DMA_MAP    0x0004    /* 當 urb 包含要被發送的 DMA 緩衝時,應被置位.USB 核心使用就會使用 transfer_dma 變數指向的緩衝, 而不是被 transfer_buffer 變數指向的緩衝. */#define URB_NO_SETUP_DMA_MAP    0x0008    /* 和 URB_NO_TRANSFER_DMA_MAP 類似, 這個位用來控制 DMA 緩衝已經建立的 urb. 如果它被置位, USB 核心使用 setup_dma 變數而不是 setup_packet 變數指向的緩衝. */#define URB_NO_FSBR        0x0020    /* 僅 UHCI USB 主機控制器驅動使用, 並且告訴它不要試圖使用前端匯流排回收( Front Side Bus Reclamation) 邏輯. 這個位通常應不設定, 因為有 UHCI 主機控制器的機器會增加 CPU 負擔, 且PCI 匯流排會忙於等待設定了這個位的 urb */#define URB_ZERO_PACKET        0x0040    /* 如果置位, 批量 OUT urb 通過發送不包含資料的短包來結束, 這時資料對齊到一個端點資料包邊界. 這被一些掉線的 USB 裝置需要該位才能正確工作 */#define URB_NO_INTERRUPT    0x0080    /* 如果置位, 當 urb 結束時硬體可能不產生一個中斷. 該位應當小心使用並且只在多個 urb 排隊到相同端點時才使用. USB 核心函數使用該位進行 DMA 緩衝傳送. */#define URB_FREE_BUFFER        0x0100    /* Free transfer buffer with the URB */#define URB_DIR_IN        0x0200    /* Transfer from device to host */#define URB_DIR_OUT        0#define URB_DIR_MASK        URB_DIR_IN
上述結構體中int status;的常用值(in include/asm-generic/errno.h  and errno_base.h) :
// 0     表示 urb 傳送成功*///以下各個定義在使用時為負值#define    ENOENT         2    /* urb 被 usb_kill_urb 停止 */#define    ECONNRESET    104    /* urb 被 usb_unlink_urb 去鏈, 且 transfer_flags 被設為 URB_ASYNC_UNLINK */#define    EINPROGRESS    115    /* urb 仍在 USB 主機控制器處理 */#define    EPROTO        71    /* urb 發生錯誤: 在傳送中發生bitstuff 錯誤或硬體沒有及時收到響應幀 */#define    EILSEQ        84    /* urb 傳送中出現 CRC 較驗錯 */#define    EPIPE        32    /* 端點被停止. 若此端點不是控制端點, 則這個錯誤可通過函數 usb_clear_halt 清除 */#define    ECOMM        70    /* 資料轉送時的接收速度快於寫入系統記憶體的速度. 此錯誤僅出現在 IN urb */#define    ENOSR        63    /* 從系統記憶體中擷取資料的速度趕不上USB 資料傳送速度,此錯誤僅出現在 OUT urb. */#define    EOVERFLOW    75    /* urb 發生"babble"(串擾)錯誤:端點接受的資料大於端點的最大資料包大小 */#define    EREMOTEIO    181    /* 當 urb 的 transfer_flags 變數的 URB_SHORT_NOT_OK 標誌被設定, urb 請求的資料沒有完整地收到 */#define    ENODEV        19    /* USB 裝置從系統中拔出 */#define    EXDEV        18    /* 僅發生在等時 urb 中, 表示傳送部分完成. 為了確定所傳輸的內容, 驅動必須看單獨的幀狀態. */#define    EINVAL        22    /* 如果urb的一個參數設定錯誤或在提交 urb 給 USB 核心的 usb_submit_urb 調用中, 有不正確的參數,則可能發生次錯誤 */#define    ESHUTDOWN    108    /* USB 主機控制器驅動有嚴重錯誤,它已被禁止, 或者裝置從系統中拔出。且這個urb 在裝置被移除後被提交. 它也可能發生在 urb 被提交給裝置時,裝置的配置已被改變*/
建立和登出 urbstruct urb 結構不能靜態建立,必須使用 usb_alloc_urb 函數建立. 函數原型:
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);//int iso_packets : urb 包含等時資料包的數目。如果不使用等時urb,則為0//gfp_t mem_flags : 與傳遞給 kmalloc 函數調用來從核心分配記憶體的標誌類型相同//傳回值          : 如果成功分配足夠記憶體給 urb , 傳回值為指向 urb 的指標. 如果傳回值是 NULL, 則在 USB 核心中發生了錯誤, 且驅動需要進行適當清理

如果驅動已經對 urb 使用完畢, 必須調用 usb_free_urb 函數,釋放urb。函數原型:

void usb_free_urb(struct urb *urb);//struct urb *urb : 要釋放的 struct urb 指標
根據核心源碼,可以通過自己kmalloc一個空間來建立urb,然後必須使用
void usb_init_urb(struct urb *urb);
進行初始化後才可以繼續使用。 其實usb_alloc_urb函數就是這樣實現的,所以我當然不推薦這種自找麻煩的做法。

初始化 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);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);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);//struct urb *urb :指向要被初始化的 urb 的指標//struct usb_device *dev :指向 urb 要發送到的 USB 裝置.//unsigned int pipe : urb 要被發送到的 USB 裝置的特定端點. 必須使用前面提過的 usb_******pipe 函數建立//void *transfer_buffer :指向外發資料或接收資料的緩衝區的指標.注意:不能是靜態緩衝,必須使用 kmalloc 來建立.//int buffer_length :transfer_buffer 指標指向的緩衝區的大小//usb_complete_t complete :指向 urb 結束處理常式函數指標//void *context :指向一個小資料區塊的指標, 被添加到 urb 結構中,以便被結束處理常式函數擷取使用.//int interval :中斷 urb 被調度的間隔.//函數不設定 urb 中的 transfer_flags 變數, 因此對這個成員的修改必須由驅動手動完成/*等時 urb 沒有初始化函數,必須手動初始化,以下為一個例子*/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;}
其實那三個初始化函數只是簡單的封裝,是inline函數。所以其實和等時的urb手動初始化沒什麼大的區別。 提交 urb一旦 urb 被正確地建立並初始化, 它就可以提交給 USB 核心以發送出到 USB 裝置. 這通過調用函數 usb_submit_urb 實現:
int usb_submit_urb(struct urb *urb, gfp_t mem_flags);//struct urb *urb :指向被提交的 urb 的指標 //gfp_t mem_flags :使用傳遞給 kmalloc 調用同樣的參數, 用來告訴 USB 核心如何及時分配記憶體緩衝/*因為函數 usb_submit_urb 可被在任何時候被調用(包括從一個中斷上下文), mem_flags 變數必須正確設定. 根據 usb_submit_urb 被調用的時間,只有 3 個有效值可用:GFP_ATOMIC 只要滿足以下條件,就應當使用此值:1.調用者處於一個 urb 結束處理常式,中斷處理常式,底半部,tasklet或者一個定時器回呼函數.2.調用者持有自旋鎖或者讀寫鎖. 注意如果正持有一個訊號量, 這個值不必要.3.current->state 不是 TASK_RUNNING. 除非驅動已自己改變 current 狀態,否則狀態應該一直是 TASK_RUNNING .GFP_NOIO 驅動處於塊 I/O 處理過程中. 它還應當用在所有的儲存類型的錯誤處理過程中.GFP_KERNEL 所有不屬於之前提到的其他情況*/
在 urb 被成功提交給 USB 核心之後, 直到結束處理常式函數被調用前,都不能訪問 urb 結構的任何成員. urb結束處理常式如果 usb_submit_urb 被成功調用, 並把對 urb 的控制權傳遞給 USB 核心, 函數返回 0; 否則返回一個負的錯誤碼. 如果函數調用成功, 當 urb 被結束的時候結束處理常式會被調用一次.當這個函數被調用時, USB 核心就完成了這個urb, 並將它的控制權返回給裝置驅動.只有 3 種結束urb並調用結束處理常式的情況:(1)urb 被成功發送給裝置, 且裝置返回正確的確認.如果這樣, urb 中的status變數被設定為 0.

(2)發生錯誤, 錯誤值記錄在 urb 結構中的 status 變數.

(3)urb 從 USB 核心unlink. 這發生在要麼當驅動通過調用 usb_unlink_urb 或者 usb_kill_urb告知 USB 核心取消一個已提交的 urb,或者在一個 urb 已經被提交給它時裝置從系統中去除. 取消 urb使用以下函數停止一個已經提交給 USB 核心的 urb:
void usb_kill_urb(struct urb *urb)int usb_unlink_urb(struct urb *urb);
如果調用usb_kill_urb函數,則 urb 的生命週期將被終止. 這通常在裝置從系統移除時,在斷開回呼函數(disconnect callback)中調用.

對一些驅動, 應當調用 usb_unlink_urb 函數來使 USB 核心停止 urb. 這個函數不會等待 urb 完全停止才返回. 這對於在中斷處理常式中或者持有一個自旋鎖時去停止 urb 是很有用的, 因為等待一個 urb 完全停止需要 USB 核心有使調用進程休眠的能力(wait_event()函數).
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.