linux設備驅動(29)usb驅動-urb詳解


盡管 USB 本身所屬設備驅動的結構與其不掛在 USB 總線上時完全相同,但是在訪問方式上卻發生了很大的變化,例如,對於字符設備而言,盡管仍然是 write()、read()、ioctl()這些函數,但是在這些函數中,與 USB 設備通信時不再是 I/O 內存和 I/O 端口的訪問,而是URB 即USB 請求塊。 

USB 請求塊(USB request block,urb)是 USB 設備驅動中用來描述與 USB 設備通信所用的基本載體和核心數據結構,非常類似於網絡設備驅動中的 sk_buff 結構體。

1 urb結構體

定義位於:include\linux\usb.h

 1 struct urb {
 2     /* private: usb core and host controller only fields in the urb */
 3     struct kref kref;        /*urb 引用計數  */
 4     void *hcpriv;            /* 主機控制器私有數據 */
 5     atomic_t use_count;        /* 當前提交的總數*/
 6     atomic_t reject;        /* submissions will fail */
 7     int unlinked;            /* unlink error code */
 8 
 9     /* public: documented fields in the urb that can be used by drivers */
10     struct list_head urb_list;    /* urb鏈表頭
11                      * current owner */
12     struct list_head anchor_list;    /* the URB may be anchored */
13     struct usb_anchor *anchor;
14     struct usb_device *dev;        /*關聯的 USB 設備 */
15     struct usb_host_endpoint *ep;    /* (internal) pointer to endpoint */
16     unsigned int pipe;        /* 管道 */
17     unsigned int stream_id;        /* (in) stream ID */
18     int status;            /* urb的狀態 */
19     unsigned int transfer_flags;    /* (in) URB_SHORT_NOT_OK | ...*/
20     void *transfer_buffer;        /*發送數據到設備或從設備接收數據的緩沖區*/
21     dma_addr_t transfer_dma;    /* 用來以 DMA 方式向設備傳輸數據的緩沖區*/
22     struct scatterlist *sg;        /* (in) scatter gather buffer list */
23     int num_mapped_sgs;        /* (internal) mapped sg entries */
24     int num_sgs;            /* (in) number of entries in the sg list */
25     u32 transfer_buffer_length;    /* transfer_buffer 或 transfer_dma 指向緩沖區的大小 */
26     u32 actual_length;        /*URB 結束后,發送或接收數據的實際長度 */
27     unsigned char *setup_packet;    /*指向控制 URB 的設置數據包的指針*/
28     dma_addr_t setup_dma;        /*控制 URB 的設置數據包的 DMA 緩沖區*/
29     int start_frame;        /* 等時傳輸中用於設置或返回初始幀 */
30     int number_of_packets;        /* 等時傳輸中等時緩沖區數據 */
31     int interval;            /*URB 被輪詢到的時間間隔(對中斷和等時 urb 有效)*/
32     int error_count;        /* 等時傳輸錯誤數量  */
33     void *context;            /* completion 函數上下文 */
34     usb_complete_t complete;    /*當 URB 被完全傳輸或發生錯誤時,被調用 */
35     struct usb_iso_packet_descriptor iso_frame_desc[0];
36                     /*單個 URB 一次可定義多個等時傳輸時,描述各個等時傳輸 */
37 }

當 transfer_flags 標志中的 URB_NO_TRANSFER_DMA_MAP 被置位時,USB 核心將使用 transfer_dma指向的緩沖區而非 transfer_buffer指向的緩沖區,意味着即將傳輸 DMA緩沖區。

當 transfer_flags 標志中的 URB_NO_SETUP_DMA_MAP 被置位時,對於有 DMA緩沖區的控制 urb而言,USB核心將使用 setup_dma指向的緩沖區而非 setup_packet指向的緩沖區。
2 URB處理流程

USB 設備中的每個端點都處理一個 urb 隊列。

一個 urb 的典型生命周期如下:

(1)被一個 USB 設備驅動創建。

(2)初始化,被安排給一個特定 USB 設備的特定端點。

(3)被 USB 設備驅動提交給 USB 核心。

(4)提交由 USB 核心指定的 USB 主機控制器驅動。

(5)被 USB 主機控制器處理,進行一次到 USB 設備的傳送。

       第(4)~(5)步由 USB 核心和主機控制器完成,不受 USB 設備驅動的控制。

(6)當 urb 完成,USB 主機控制器驅動通知 USB 設備驅動。

3 urb的創建函數usb_alloc_urb

定義位於:drivers\usb\core\urb.c

 1 struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
 2 {
 3     struct urb *urb;
 4 
 5     urb = kmalloc(sizeof(struct urb) +
 6         iso_packets * sizeof(struct usb_iso_packet_descriptor),//分配urb
 7         mem_flags);
 8     if (!urb) {
 9         printk(KERN_ERR "alloc_urb: kmalloc failed\n");
10         return NULL;
11     }
12     usb_init_urb(urb);
13     return urb;
14 }

3.1 函數usb_init_urb

初始化urb中部分成員

1 void usb_init_urb(struct urb *urb)
2 {
3     if (urb) {
4         memset(urb, 0, sizeof(*urb));
5         kref_init(&urb->kref);//初始化urb的引用計數
6         INIT_LIST_HEAD(&urb->anchor_list);//初始化鏈表
7     }
8 }

iso_packets 是這個 urb 應當包含的等時數據包的數目,若為 0 表示不創建等時數據包。mem_flags 參數是分配內存的標志,和 kmalloc()函數的分配標志參數含義相同。如果分配成功,該函數返回一個 urb 結構體指針,否則返回 0。

urb 結構體在驅動中不能靜態創建,因為這可能破壞 USB 核心給 urb 使用的引用計數方法。

釋放由 usb_alloc_urb()分配的 urb 結構體時用函數void usb_free_urb(struct urb *urb)。

4 urb的初始化

定義位於:\include\linux\usb.h

4.1 中斷 urb

對於中斷 urb,使用usb_fill_int_urb()函數來初始化 urb。

 1 static inline void usb_fill_int_urb(struct urb *urb,
 2                     struct usb_device *dev,
 3                     unsigned int pipe,
 4                     void *transfer_buffer,
 5                     int buffer_length,
 6                     usb_complete_t complete_fn,
 7                     void *context,
 8                     int interval)
 9 {
10     urb->dev = dev;
11     urb->pipe = pipe;
12     urb->transfer_buffer = transfer_buffer;
13     urb->transfer_buffer_length = buffer_length;
14     urb->complete = complete_fn;
15     urb->context = context;
16     if (dev->speed == USB_SPEED_HIGH || dev->speed == USB_SPEED_SUPER)
17         urb->interval = 1 << (interval - 1);
18     else
19         urb->interval = interval;
20     urb->start_frame = -1;
21 }

urb 參數指向要被初始化的 urb 的指針;

dev 指向這個 urb 要被發送到的 USB 設備;

pipe是這個 urb要被發送到的 USB設備的特定端點;

transfer_buffer是指向發送數據或接收數據的緩沖區的指針,和 urb一樣,它也不能是靜態緩沖區,必須使用 kmalloc()來分配;

buffer_length是 transfer_buffer指針所指向緩沖區的大小;

complete指針指向當這個 urb完成時被調用的完成處理函數;

context是完成處理函數的“上下文”;

interval是這個 urb應當被調度的間隔。

(int)pipe的創建:使用usb_sndintpipe()或 usb_rcvintpipe()函數

4.2 批量urb

對於批量urb,使用 usb_fill_bulk_urb()函數來初始化 urb。

 1 static inline void usb_fill_bulk_urb(struct urb *urb,
 2                      struct usb_device *dev,
 3                      unsigned int pipe,
 4                      void *transfer_buffer,
 5                      int buffer_length,
 6                      usb_complete_t complete_fn,
 7                      void *context)
 8 {
 9     urb->dev = dev;
10     urb->pipe = pipe;
11     urb->transfer_buffer = transfer_buffer;
12     urb->transfer_buffer_length = buffer_length;
13     urb->complete = complete_fn;
14     urb->context = context;
15 }

沒有interval參數,(bulk) pipe的創建:使用 usb_sndbulkpipe()或者 usb_rcvbulkpipe()函數。

4.3 等時urb

對於等時urb,只能手動地初始化urb,而后提交給USB核心。

 1 實例(from drivers/usb/media/usbvideo.c)
 2 for (i = 0; i < USBVIDEO_NUMSBUF; i++)
 3 {
 4 int j, k;
 5 struct urb *urb = uvd->sbuf[i].urb;
 6 urb->dev = dev;
 7 urb->context = uvd;
 8 urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp);
 9 urb->interval = 1;
10 urb->transfer_flags = URB_ISO_ASAP; /*urb 被調度*/
11 urb->transfer_buffer = uvd->sbuf[i].data;/*傳輸 buffer*/
12 urb->complete = usbvideo_IsocIrq; /* 完成函數 */
13 urb->number_of_packets = FRAMES_PER_DESC; /*urb 中的等時傳輸數量*/
14 urb->transfer_buffer_length = uvd->iso_packet_len *FRAMES_PER_DESC;
15 
16 for (j = k = 0; j < FRAMES_PER_DESC; j++, k += uvd->iso_packet_len)
17 
18 {
19 urb->iso_frame_desc[j].offset = k;
20 urb->iso_frame_desc[j].length = uvd->iso_packet_len;
21  }
22 
23 }

5 urb的提交

提交給usb核心。定義位於:drivers\usb\core\urb.c

如果 usb_submit_urb()調用成功,即urb 的控制權被移交給 USB 核心,該函數返回 0;否則,返回錯誤號。在提交 urb 到 USB 核心后,直到完成函數被調用之前,不要訪問 urb 中的任何成員。

  1 int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
  2 {
  3     int                xfertype, max;
  4     struct usb_device        *dev;
  5     struct usb_host_endpoint    *ep;
  6     int                is_out;
  7 
  8     if (!urb || !urb->complete)
  9         return -EINVAL;
 10     if (urb->hcpriv) {
 11         WARN_ONCE(1, "URB %p submitted while active\n", urb);
 12         return -EBUSY;
 13     }
 14 
 15     dev = urb->dev;
 16     if ((!dev) || (dev->state < USB_STATE_UNAUTHENTICATED))
 17         return -ENODEV;
 18 
 19     /* For now, get the endpoint from the pipe.  Eventually drivers
 20      * will be required to set urb->ep directly and we will eliminate
 21      * urb->pipe.
 22      */
 23     ep = usb_pipe_endpoint(dev, urb->pipe);
 24     if (!ep)
 25         return -ENOENT;
 26 
 27     urb->ep = ep;
 28     urb->status = -EINPROGRESS;
 29     urb->actual_length = 0;
 30 
 31     /* Lots of sanity checks, so HCDs can rely on clean data
 32      * and don't need to duplicate tests
 33      */
 34     xfertype = usb_endpoint_type(&ep->desc);
 35     if (xfertype == USB_ENDPOINT_XFER_CONTROL) {
 36         struct usb_ctrlrequest *setup =
 37                 (struct usb_ctrlrequest *) urb->setup_packet;
 38 
 39         if (!setup)
 40             return -ENOEXEC;
 41         is_out = !(setup->bRequestType & USB_DIR_IN) ||
 42                 !setup->wLength;
 43     } else {
 44         is_out = usb_endpoint_dir_out(&ep->desc);
 45     }
 46 
 47     /* Clear the internal flags and cache the direction for later use */
 48     urb->transfer_flags &= ~(URB_DIR_MASK | URB_DMA_MAP_SINGLE |
 49             URB_DMA_MAP_PAGE | URB_DMA_MAP_SG | URB_MAP_LOCAL |
 50             URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL |
 51             URB_DMA_SG_COMBINED);
 52     urb->transfer_flags |= (is_out ? URB_DIR_OUT : URB_DIR_IN);
 53 
 54     if (xfertype != USB_ENDPOINT_XFER_CONTROL &&
 55             dev->state < USB_STATE_CONFIGURED)
 56         return -ENODEV;
 57 
 58     max = usb_endpoint_maxp(&ep->desc);
 59     if (max <= 0) {
 60         dev_dbg(&dev->dev,
 61             "bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",
 62             usb_endpoint_num(&ep->desc), is_out ? "out" : "in",
 63             __func__, max);
 64         return -EMSGSIZE;
 65     }
 66 
 67     /* periodic transfers limit size per frame/uframe,
 68      * but drivers only control those sizes for ISO.
 69      * while we're checking, initialize return status.
 70      */
 71     if (xfertype == USB_ENDPOINT_XFER_ISOC) {
 72         int    n, len;
 73 
 74         /* SuperSpeed isoc endpoints have up to 16 bursts of up to
 75          * 3 packets each
 76          */
 77         if (dev->speed == USB_SPEED_SUPER) {
 78             int     burst = 1 + ep->ss_ep_comp.bMaxBurst;
 79             int     mult = USB_SS_MULT(ep->ss_ep_comp.bmAttributes);
 80             max *= burst;
 81             max *= mult;
 82         }
 83 
 84         /* "high bandwidth" mode, 1-3 packets/uframe? */
 85         if (dev->speed == USB_SPEED_HIGH) {
 86             int    mult = 1 + ((max >> 11) & 0x03);
 87             max &= 0x07ff;
 88             max *= mult;
 89         }
 90 
 91         if (urb->number_of_packets <= 0)
 92             return -EINVAL;
 93         for (n = 0; n < urb->number_of_packets; n++) {
 94             len = urb->iso_frame_desc[n].length;
 95             if (len < 0 || len > max)
 96                 return -EMSGSIZE;
 97             urb->iso_frame_desc[n].status = -EXDEV;
 98             urb->iso_frame_desc[n].actual_length = 0;
 99         }
100     }
101 
102     /* the I/O buffer must be mapped/unmapped, except when length=0 */
103     if (urb->transfer_buffer_length > INT_MAX)
104         return -EMSGSIZE;
105 
106 #ifdef DEBUG
107     /* stuff that drivers shouldn't do, but which shouldn't
108      * cause problems in HCDs if they get it wrong.
109      */
110     {
111     unsigned int    allowed;
112     static int pipetypes[4] = {
113         PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT
114     };
115 
116     /* Check that the pipe's type matches the endpoint's type */
117     if (usb_pipetype(urb->pipe) != pipetypes[xfertype])
118         dev_WARN(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n",
119             usb_pipetype(urb->pipe), pipetypes[xfertype]);
120 
121     /* Check against a simple/standard policy */
122     allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT | URB_DIR_MASK |
123             URB_FREE_BUFFER);
124     switch (xfertype) {
125     case USB_ENDPOINT_XFER_BULK:
126         if (is_out)
127             allowed |= URB_ZERO_PACKET;
128         /* FALLTHROUGH */
129     case USB_ENDPOINT_XFER_CONTROL:
130         allowed |= URB_NO_FSBR;    /* only affects UHCI */
131         /* FALLTHROUGH */
132     default:            /* all non-iso endpoints */
133         if (!is_out)
134             allowed |= URB_SHORT_NOT_OK;
135         break;
136     case USB_ENDPOINT_XFER_ISOC:
137         allowed |= URB_ISO_ASAP;
138         break;
139     }
140     allowed &= urb->transfer_flags;
141 
142     /* warn if submitter gave bogus flags */
143     if (allowed != urb->transfer_flags)
144         dev_WARN(&dev->dev, "BOGUS urb flags, %x --> %x\n",
145             urb->transfer_flags, allowed);
146     }
147 #endif
148     /*
149      * Force periodic transfer intervals to be legal values that are
150      * a power of two (so HCDs don't need to).
151      *
152      * FIXME want bus->{intr,iso}_sched_horizon values here.  Each HC
153      * supports different values... this uses EHCI/UHCI defaults (and
154      * EHCI can use smaller non-default values).
155      */
156     switch (xfertype) {
157     case USB_ENDPOINT_XFER_ISOC:
158     case USB_ENDPOINT_XFER_INT:
159         /* too small? */
160         switch (dev->speed) {
161         case USB_SPEED_WIRELESS:
162             if (urb->interval < 6)
163                 return -EINVAL;
164             break;
165         default:
166             if (urb->interval <= 0)
167                 return -EINVAL;
168             break;
169         }
170         /* too big? */
171         switch (dev->speed) {
172         case USB_SPEED_SUPER:    /* units are 125us */
173             /* Handle up to 2^(16-1) microframes */
174             if (urb->interval > (1 << 15))
175                 return -EINVAL;
176             max = 1 << 15;
177             break;
178         case USB_SPEED_WIRELESS:
179             if (urb->interval > 16)
180                 return -EINVAL;
181             break;
182         case USB_SPEED_HIGH:    /* units are microframes */
183             /* NOTE usb handles 2^15 */
184             if (urb->interval > (1024 * 8))
185                 urb->interval = 1024 * 8;
186             max = 1024 * 8;
187             break;
188         case USB_SPEED_FULL:    /* units are frames/msec */
189         case USB_SPEED_LOW:
190             if (xfertype == USB_ENDPOINT_XFER_INT) {
191                 if (urb->interval > 255)
192                     return -EINVAL;
193                 /* NOTE ohci only handles up to 32 */
194                 max = 128;
195             } else {
196                 if (urb->interval > 1024)
197                     urb->interval = 1024;
198                 /* NOTE usb and ohci handle up to 2^15 */
199                 max = 1024;
200             }
201             break;
202         default:
203             return -EINVAL;
204         }
205         if (dev->speed != USB_SPEED_WIRELESS) {
206             /* Round down to a power of 2, no more than max */
207             urb->interval = min(max, 1 << ilog2(urb->interval));
208         }
209     }
210 
211     return usb_hcd_submit_urb(urb, mem_flags);
212 }

usb_submit_urb()在原子上下文和進程上下文中都可以被調用,mem_flags 變量需根據調用環境進行相應的設置,如下所示:

GFP_ATOMIC:在中斷處理函數、底半部、tasklet、定時器處理函數以及 urb

完成函數中,在調用者持有自旋鎖或者讀寫鎖時以及當驅動將 current->state修改為非 TASK_ RUNNING時,應使用此標志。

GFP_NOIO:在存儲設備的塊 I/O 和錯誤處理路徑中,應使用此標志。

GFP_KERNEL:如果沒有任何理由使用 GFP_ATOMIC 和 GFP_NOIO,就使

用 GFP_ KERNEL。

USB核心:提交由 USB 核心指定的 USB 主機控制器驅動

USB 主機控制器:進行一次到 USB 設備的傳送

當 urb 完成,USB 主機控制器驅動通知 USB 設備驅動。

以下情況下,urb完成函數將被調用

(1)urb 被成功發送給設備,並且設備返回正確的確認。如果urb->status 為 0,意味着對於一個輸出 urb,數據被成功發送;對於一個輸入urb,請求的數據被成功收到。

(2)如果發送數據到設備或從設備接收數據時發生了錯誤,urb->status 將記錄錯誤值。

(3)urb 被從 USB 核心“去除連接”,這發生在驅動通過 usb_unlink_urb()或usb_kill_urb()函數取消 urb,或 urb雖已提交,而 USB 設備被拔出的情況下。

對 usb_unlink_urb() 而 言,如 果 urb 結 構 體中的URB_ASYNC_UNLINK(即異步 unlink)的標志被置位,則對該 urb 的 usb_unlink_urb()調用將立即返回,具體的 unlink 動作將在后台進行。否則,此函數一直等到 urb 被解開鏈接或結束時才返回。

usb_kill_urb()會徹底終止 urb 的生命周期,它通常在設備的disconnect()函數中被調用。

 當 urb生命結束時(處理完成或被解除鏈接),通過 urb結構體的 status成員可以獲知其原因,如 0表示傳輸成功,-ENOENT 表示被 usb_kill_urb()殺死,-ECONNRESET表示被 usb_unlink_urb()殺死, -EPROTO 表示傳輸中發生了 bitstuff錯誤或者硬件未能及時收到響應數據包,-ENODEV 表示 USB設備已被移除,-EXDEV 表示等時傳輸僅完成了一部分等。

簡單的批量與控制 URB

(1)usb_bulk_msg()函數

(2)usb_control_msg()函數

int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout);

usb_bulk_msg()函數創建一個 USB 批量 urb 並將它發送到特定設備,這個函數是同步的,它一直等待 urb 完成后才返回。

usb_dev 參數為批量消息要發送的 USB 設備的指針,pipe 為批量消息要發送到的USB設備的端點,data參數為指向要發送或接收的數據緩沖區的指針,len參數為 data參數所指向的緩沖區的長度,actual_length用於返回實際發送或接收的字節數,timeout是發送超時,以 jiffies為單位,0意味着永遠等待。如果函數調用成功,返回 0;否則,返回1 個負的錯誤值。

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

dev 指向控制消息發往的 USB 設備,pipe 是控制消息要發往的 USB 設備的端點,request 是這個控制消息的 USB 請求值,requesttype 是這個控制消息的 USB 請求類型,value 是這個控制消息的 USB 消息值,index 是這個控制消息的 USB 消息索引值,data指向要發送或接收的數據緩沖區,size 是 data 參數所指向的緩沖區的大小,timeout是發送超時,以 jiffies 為單位,0 意味着永遠等待。參數 request、requesttype、value 和 index 與 USB 規范中定義的 USB 控制消息直接對應。

如果函數調用成功,該函數返回發送到設備或從設備接收到的字節數;否則,返回一個負的錯誤值.

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

參考博文:https://blog.csdn.net/Blazar/java/article/details/79129210


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM