linux usb声卡 submit urb,linux usb urb详解
linux usb urb詳解
謹以此文紀念過往的歲月
一.前言
在前文中看過了hub的驅動以及host的驅動還有usb設備的驅動,在把這些東西關聯起來的東東中,一個很重要的urb(usb request blk),在本文中會詳細來看urb的實現,以及具體的應用。
二.Urb
urb是什么東西,那先來看urb的定義吧。
1struct urb
2{
3/*私有的:只能由usb核心和主機控制器訪問的字段*/
4struct kref kref; /*urb引用計數*/
5spinlock_t lock; /* urb鎖*/
6void *hcpriv; /*主機控制器私有數據*/
7int bandwidth; /* int/iso請求的帶寬*/
8atomic_t use_count; /*并發傳輸計數*/
9u8 reject; /*傳輸將失敗*/
10
11/*公共的: 可以被驅動使用的字段*/
12struct list_head urb_list; /*鏈表頭*/
13struct usb_device *dev; /*關聯的usb設備*/
14unsigned int pipe; /*管道信息*/
15int status; /* urb的當前狀態*/
16unsigned int transfer_flags; /* urb_short_not_ok | ...*/
17void *transfer_buffer; /*發送數據到設備或從設備接收數據的緩沖區*/
18dma_addr_t transfer_dma; /*用來以dma方式向設備傳輸數據的緩沖區*/
19int transfer_buffer_length;/*transfer_buffer或transfer_dma指向緩沖區的大小*/
20
21int actual_length; /* urb結束后,發送或接收數據的實際長度*/
22unsigned char *setup_packet; /*指向控制urb的設置數據包的指針*/
23dma_addr_t setup_dma; /*控制urb的設置數據包的dma緩沖區*/
24int start_frame; /*等時傳輸中用于設置或返回初始幀*/
25int number_of_packets; /*等時傳輸中等時緩沖區數據*/
26int interval; /* urb被輪詢到的時間間隔(對中斷和等時urb有效)*/
27int error_count;/*等時傳輸錯誤數量 */
28void *context; /* completion函數上下文*/
29usb_complete_t complete; /*當urb被完全傳輸或發生錯誤時,被調用*/
30struct usb_iso_packet_descriptor iso_frame_desc[0];
31/*單個urb一次可定義多個等時傳輸時,描述各個等時傳輸*/
32 };
2.1 urb申請
usb_alloc_urb開辟一個urb空間并對其部分的成員初始化。
iso_packets:等時傳輸的包數
mem_flags:開辟urb空間的mem旗標,一般為GFP_KERNEL
struct urb *usb_alloc_urb(int, gfp_t )
{
struct
urb *urb;
urb
= kmalloc(sizeof(struct urb) +iso_packets * sizeof(struct
usb_iso_packet_descriptor),
mem_flags);--開辟空間
if
(!urb) {
return
NULL;
}
usb_init_urb(urb);
--初始化部分成員
return
urb;
}
void usb_init_urb(struct urb *urb)
{
if
(urb) {
memset(urb,
0, sizeof(*urb));
kref_init(&urb->kref);--初始化urb計數器
INIT_LIST_HEAD(&urb->anchor_list);--初始化鎖定鏈表
}
}
一般會在初始化之后,對urb的各個成員進行初始化。例如調用usb_fill_int_urb對urb進行成員的填充。在此不做講述,這個還是比較簡單的。
2.2 usb_submit_urb提交urb
int usb_submit_urb(struct urb *urb, gfp_t
mem_flags)
{
intxfertype, max;
struct
usb_device*dev;
struct
usb_host_endpoint*ep;
intis_out;
if
(!urb || urb->hcpriv || !urb->complete)
return
-EINVAL;
dev
= urb->dev;
if
((!dev) || (dev->state < USB_STATE_DEFAULT))
return
-ENODEV;
ep
= (usb_pipein(urb->pipe) ? dev->ep_in : dev->ep_out) [usb_pipeendpoint(urb->pipe)];
--查找設備端點
if
(!ep)
return
-ENOENT;
urb->ep
= ep;
urb->status
= -EINPROGRESS;
urb->actual_length
= 0;
xfertype
= usb_endpoint_type(&ep->desc);
if
(xfertype == USB_ENDPOINT_XFER_CONTROL) {--如果是控制類型
struct
usb_ctrlrequest *setup = (struct usb_ctrlrequest *) urb->setup_packet;
if
(!setup)
return
-ENOEXEC;
is_out
= !(setup->bRequestType & USB_DIR_IN) ||!setup->wLength;
}
else {
is_out
= usb_endpoint_dir_out(&ep->desc);
}
urb->transfer_flags
= (urb->transfer_flags & ~URB_DIR_MASK) | (is_out ? URB_DIR_OUT :
URB_DIR_IN);--緩存的方向留待后用
if
(xfertype != USB_ENDPOINT_XFER_CONTROL &&dev->state <
USB_STATE_CONFIGURED)--在設備沒有配置前,傳輸的類型必為控制類型,唯有設備配置完成后才能傳輸其他類型的urb
return
-ENODEV;
max
= le16_to_cpu(ep->desc.wMaxPacketSize);
if
(max <= 0) {
return
-EMSGSIZE;
}
if
(xfertype == USB_ENDPOINT_XFER_ISOC) {--等時傳輸
intn, len;
if
(dev->speed == USB_SPEED_HIGH) {
intmult = 1 + ((max >> 11) & 0x03);
max
&= 0x07ff;
max
*= mult;
}
if
(urb->number_of_packets <= 0)
return
-EINVAL;
for
(n = 0; n < urb->number_of_packets; n++) {
len
= urb->iso_frame_desc[n].length;
if
(len < 0 || len > max)
return
-EMSGSIZE;
urb->iso_frame_desc[n].status
= -EXDEV;
urb->iso_frame_desc[n].actual_length
= 0;
}
}
if
(urb->transfer_buffer_length < 0)
return
-EMSGSIZE;
switch
(xfertype) {--對等時傳輸和中斷傳輸的等待時間進行檢測
case
USB_ENDPOINT_XFER_ISOC:
case
USB_ENDPOINT_XFER_INT:
if
(urb->interval <= 0)
return
-EINVAL;
switch
(dev->speed) {
case
USB_SPEED_HIGH:
/*
NOTE usb handles 2^15 */
if
(urb->interval > (1024 * 8))
urb->interval
= 1024 * 8;
max
= 1024 * 8;
break;
case
USB_SPEED_FULL:
case
USB_SPEED_LOW:
if
(xfertype == USB_ENDPOINT_XFER_INT) {
if
(urb->interval > 255)
return
-EINVAL;
max
= 128;
}
else {
if
(urb->interval > 1024)
urb->interval
= 1024;
max
= 1024;
}
break;
default:
return
-EINVAL;
}
urb->interval
= min(max, 1 << ilog2(urb->interval));
}
return
(urb,
mem_flags);
}
int usb_hcd_submit_urb (struct urb *urb,
gfp_t mem_flags)
{
intstatus;
struct
usb_hcd*hcd =
bus_to_hcd(urb->dev->bus);
usb_get_urb(urb);
atomic_inc(&urb->use_count);--增減urb計數
atomic_inc(&urb->dev->urbnum);--增加dev的urb計數
usbmon_urb_submit(&hcd->self,
urb);--退化為空函數
status
= map_urb_for_dma(hcd, urb, mem_flags);--將urb的緩沖區與dma的區間映射
if
(unlikely(status)) {
usbmon_urb_submit_error(&hcd->self,
urb, status);
goto
error;
}
if
(is_root_hub(urb->dev))
status
= rh_urb_enqueue(hcd, urb);
else
status
= hcd->driver->urb_enqueue(hcd, urb, mem_flags);--將urb入隊
if
(unlikely(status)) {
usbmon_urb_submit_error(&hcd->self,
urb, status);
unmap_urb_for_dma(hcd,
urb);
error:
urb->hcpriv
= NULL;
INIT_LIST_HEAD(&urb->urb_list);
atomic_dec(&urb->use_count);
atomic_dec(&urb->dev->urbnum);
if
(atomic_read(&urb->reject))
wake_up(&usb_kill_urb_queue);
usb_put_urb(urb);
}
return
status;
}
在函數usb_hcd_submit_urb調用該hcd的urb_enqueue,在ohci中將會調用ohci_urb_enqueue。其實到此我們需要放下urb的探索,重新來看ohci中關于ed,td的應用。在前文中對ohci的probe進行了講述,但是對于其余的函數并沒有仔細說明,那在后中仔細來看關于ohci的詳情。
2.3usb_anchor_urb
當urb在處理的時候鎖定。這個函數其實實現很簡單就是將urb->anchor_list添加到anchor->urb_list中。關于該函數的具體實現我并不知道具體是做什么用的。
void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor)
{
unsigned long flags;
spin_lock_irqsave(&anchor->lock,
flags);
usb_get_urb(urb);
list_add_tail(&,
&;
urb->anchor = anchor;
if
(unlikely(anchor->poisoned)) {
atomic_inc(&urb->reject);
}
spin_unlock_irqrestore(&anchor->lock,
flags);
}
2.4 usb_unanchor_urb
該函數與上面的函數剛好相反,解鎖一個urb同時刪除urb
void(struct urb *urb)
{
unsigned long flags;
struct usb_anchor
*anchor;
if (!urb)
return;
anchor =
urb->anchor;
if (!anchor)
return;
spin_lock_irqsave(&anchor->lock,
flags);
if (unlikely(anchor !=
urb->anchor)) {
spin_unlock_irqrestore(&anchor->lock,
flags);
return;
}
urb->anchor = NULL;
list_del(&urb->anchor_list);--將urb從anchor的鏈表中刪除
spin_unlock_irqrestore(&anchor->lock,
flags);
usb_put_urb(urb);--減少urb計數同時刪除urb == usb_free_urb
if
(list_empty(&anchor->urb_list))
(&anchor->wait);
}
void (struct urb *urb)
{
if (urb)
kref_put(&urb->kref,
urb_destroy);
}
在某一方面來講anchor類似一個urb的守護鏈表,在urb被使用時是不能被刪除的。在刪除一個urb時需要調用usb_wait_anchor_empty_timeout來等待urb傳輸完全結束。下面的wait_event_timeout則與上面的wake_up相互對應。
int(struct
usb_anchor *anchor,unsigned int timeout)
{
return (anchor->wait,
list_empty(&anchor->urb_list),
msecs_to_jiffies(timeout));
}
三.總結
urb在某一方面來說是設備驅動與hcd的通信的東東,關于urb的處理其本質還是在ohci中。這個將會在后面的學習中好好學習。
總結
以上是生活随笔為你收集整理的linux usb声卡 submit urb,linux usb urb详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 购物网站推广广告语261个
- 下一篇: 0757是哪里的区号