三:傳輸過程的實現
說到傳輸過程,我們必須要從URB開始說起,這個結構的就好比是網絡子系統中的skb,好比是I/O中的bio.USB系統的信息傳輸就是打成URB結構,然后再過行傳送的.
URB的全稱叫USB request block.下面從它的接口說起.
3.1:URB的相關接口
1:URB的創建
URB的創建是由usb_alloc_urb()完成的.這個函數會完成URB內存的分配和基本成員的初始化工作.代碼如下:
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
{
struct urb *urb;
urb = kmalloc(sizeof(struct urb) +
iso_packets * sizeof(struct usb_iso_packet_descriptor),
mem_flags);
if (!urb) {
err("alloc_urb: kmalloc failed");
return NULL;
}
usb_init_urb(urb);
return urb;
}
這個函數有兩個參數,一個是iso_packets.僅僅用於ISO傳輸.表示ISO數據包個數,如果用於其它類型的傳輸,此參數為0.另一個是mem_flags.是分配內存的參數.
Usb_init_urb()如下:
void usb_init_urb(struct urb *urb)
{
if (urb) {
memset(urb, 0, sizeof(*urb));
kref_init(&urb->kref);
INIT_LIST_HEAD(&urb->anchor_list);
}
}
由此可以看到,它的初始化只是初始化了引用計數和ahchor_list鏈表.這個鏈表在URB被鎖定的時候會用到.
2:URB的初始化
USB2.0 spec中定義了四種傳輸,為別為ISO,INTER,BULK,CONTORL.linux kernel為INTER,BULK,CONTORL的URB初始化提供了一些API,ISO的傳輸只能夠手動去初始化.這些API如下:
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)
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_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)
分別用來填充CONTORL,BULK,INT類型的URB.
觀察他們的函數原型,發現有很多相的的參數.先對這些參數做一下解釋:
Urb:是要初始化的urb
Dev:表示消息要被發送到的USB設備
Pipe:表示消息被發送到的端點
transfer_buffer:表示發送數據的緩沖區
length:就是transfer_buffer所表示的緩沖區大小
context:完成處理函數的上下文
complete_fn:傳輸完了之后要調用的函數.
usb_fill_control_urb()的setup_packet:即將被發送到端點的設備數據包
usb_fill_int_urb()中的interval:這個urb應該被調度的間隔.
函數的實際都是差不多的.以usb_fill_control_urb()為例:
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)
{
urb->dev = dev;
urb->pipe = pipe;
urb->setup_packet = setup_packet;
urb->transfer_buffer = transfer_buffer;
urb->transfer_buffer_length = buffer_length;
urb->complete = complete_fn;
urb->context = context;
}
如上所示,只是將函數的參數賦值給了URB相關的成員而已.
另外,關於ISO的URB初始化雖然沒有可以調用的API,但它的初始化也很簡單,對應就是填充幾個成員而已.
另外,對於pipe的參數.有一系列輔助的宏.如下示:
/* Create various pipes... */
#define usb_sndctrlpipe(dev,endpoint) \
((PIPE_CONTROL
#define usb_rcvctrlpipe(dev,endpoint) \
((PIPE_CONTROL
#define usb_sndisocpipe(dev,endpoint) \
((PIPE_ISOCHRONOUS
#define usb_rcvisocpipe(dev,endpoint) \
((PIPE_ISOCHRONOUS
#define usb_sndbulkpipe(dev,endpoint) \
((PIPE_BULK
#define usb_rcvbulkpipe(dev,endpoint) \
((PIPE_BULK
#define usb_sndintpipe(dev,endpoint) \
((PIPE_INTERRUPT
#define usb_rcvintpipe(dev,endpoint) \
((PIPE_INTERRUPT
這個宏都是根據usb2.0 spec的規范來設計的.
3:提交URB
提交urb的接口是usb_submit_urb().代碼如下:
int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
{
int xfertype, max;
struct usb_device *dev;
struct usb_host_endpoint *ep;
int is_out;
if (!urb || urb->hcpriv || !urb->complete)
return -EINVAL;
dev = urb->dev;
if ((!dev) || (dev->state
return -ENODEV;
/* For now, get the endpoint from the pipe. Eventually drivers
* will be required to set urb->ep directly and we will eliminate
* urb->pipe.
*/
//取得要傳輸的端口.對端地址是由方向+dev address+port number組成的
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;
/* Lots of sanity checks, so HCDs can rely on clean data
* and don't need to duplicate tests
*/
//取得ep的傳輸類型
xfertype = usb_endpoint_type(&ep->desc);
//如果是控制傳輸.端點0默認是控制傳輸
if (xfertype == USB_ENDPOINT_XFER_CONTROL) {
//控制傳輸的urb如果沒有setup_packet是非法的
struct usb_ctrlrequest *setup =
(struct usb_ctrlrequest *) urb->setup_packet;
if (!setup)
return -ENOEXEC;
//判斷是否是out方向的傳輸
is_out = !(setup->bRequestType & USB_DIR_IN) ||
!setup->wLength;
} else {
//如果不是控制傳輸,在端點描述符的bEndportAddress的bit7 包含有端點的傳輸方向
is_out = usb_endpoint_dir_out(&ep->desc);
}
/* Cache the direction for later use */
//根據傳輸方向.置urb->transfer_flags的方向位
urb->transfer_flags = (urb->transfer_flags & ~URB_DIR_MASK) |
(is_out ? URB_DIR_OUT : URB_DIR_IN);
//根據usb2.0 spec.除控制傳輸外的其它傳輸只有在config狀態的時候才能進行
if (xfertype != USB_ENDPOINT_XFER_CONTROL &&
dev->state
return -ENODEV;
//傳送/接收的最大字節.如果這個最大巧若拙字節還要小於0,那就是非法的
max = le16_to_cpu(ep->desc.wMaxPacketSize);
if (max
dev_dbg(&dev->dev,
"bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",
usb_endpoint_num(&ep->desc), is_out ? "out" : "in",
__FUNCTION__, max);
return -EMSGSIZE;
}
/* periodic transfers limit size per frame/uframe,
* but drivers only control those sizes for ISO.
* while we're checking, initialize return status.
*/
//如果是實時傳輸
if (xfertype == USB_ENDPOINT_XFER_ISOC) {
int n, len;
/* "high bandwidth" mode, 1-3 packets/uframe? */
//如果是高速傳輸.則要修正它的MAX值
//高速傳輸時, 一個微幀內可以修輸多個數據.bit 11~bit12用來表示一個微幀內
//傳輸包的個數.
//在USB1.1中是不支持HIGH的
if (dev->speed == USB_SPEED_HIGH) {
int mult = 1 + ((max >> 11) & 0x03);
max &= 0x07ff;
max *= mult;
}
//實現傳輸的數據包數目不能小於等於0
if (urb->number_of_packets
return -EINVAL;
//urb->number_of_packets: 實時數據包個數.每個實時數據包對應urb->iso_frame_desc[]中的一項
for (n = 0; n number_of_packets; n++) {
len = urb->iso_frame_desc[n].length;
if (len max)
return -EMSGSIZE;
urb->iso_frame_desc[n].status = -EXDEV;
urb->iso_frame_desc[n].actual_length = 0;
}
}
/* the I/O buffer must be mapped/unmapped, except when length=0 */
//如果要傳輸的緩存區大小小於0.非法
if (urb->transfer_buffer_length
return -EMSGSIZE;
#ifdef DEBUG
/* stuff that drivers shouldn't do, but which shouldn't
* cause problems in HCDs if they get it wrong.
*/
{
unsigned int orig_flags = urb->transfer_flags;
unsigned int allowed;
/* enforce simple/standard policy */
allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP |
URB_NO_INTERRUPT | URB_DIR_MASK | URB_FREE_BUFFER);
switch (xfertype) {
case USB_ENDPOINT_XFER_BULK:
if (is_out)
allowed |= URB_ZERO_PACKET;
/* FALLTHROUGH */
case USB_ENDPOINT_XFER_CONTROL:
allowed |= URB_NO_FSBR; /* only affects UHCI */
/* FALLTHROUGH */
default: /* all non-iso endpoints */
if (!is_out)
allowed |= URB_SHORT_NOT_OK;
break;
case USB_ENDPOINT_XFER_ISOC:
allowed |= URB_ISO_ASAP;
break;
}
urb->transfer_flags &= allowed;
/* fail if submitter gave bogus flags */
if (urb->transfer_flags != orig_flags) {
err("BOGUS urb flags, %x --> %x",
orig_flags, urb->transfer_flags);
return -EINVAL;
}
}
#endif
/*
* Force periodic transfer intervals to be legal values that are
* a power of two (so HCDs don't need to).
*
* FIXME want bus->{intr,iso}_sched_horizon values here. Each HC
* supports different values... this uses EHCI/UHCI defaults (and
* EHCI can use smaller non-default values).
*/
//關於實時傳輸和中斷傳輸的interval處理
switch (xfertype) {
case USB_ENDPOINT_XFER_ISOC:
case USB_ENDPOINT_XFER_INT:
/* too small? */
//interval不能小於或等於0
if (urb->interval
return -EINVAL;
/* too big? */
switch (dev->speed) {
case USB_SPEED_HIGH: /* units are microframes */
/* NOTE usb handles 2^15 */
if (urb->interval > (1024 * 8))
urb->interval = 1024 * 8;
max = 1024 * 8;
break;
case USB_SPEED_FULL: /* units are frames/msec */
case USB_SPEED_LOW:
if (xfertype == USB_ENDPOINT_XFER_INT) {
if (urb->interval > 255)
return -EINVAL;
/* NOTE ohci only handles up to 32 */
max = 128;
} else {
if (urb->interval > 1024)
urb->interval = 1024;
/* NOTE usb and ohci handle up to 2^15 */
max = 1024;
}
break;
default:
return -EINVAL;
}
/* Round down to a power of 2, no more than max */
urb->interval = min(max, 1 interval));
}
return usb_hcd_submit_urb(urb, mem_flags);
}
這段代碼雖然很長,但邏輯很清楚.對照代碼中的注釋理解應該是沒有問題的.在這里要注意,UHCI是屬於USB1.1的,它不支持HIGH傳輸.
對URB進行一系列處理之后,就會將urb丟給hcd進行處理了.usb_hcd_submit_urb()代碼如下:
int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
{
int status;
//從usb_bus的地址取得usb_hcd的地址
struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
/* increment urb's reference count as part of giving it to the HCD
* (which will control it). HCD guarantees that it either returns
* an error or calls giveback(), but not both.
*/
//增加有關的引用計數,usbmon*系列的函數是編譯選擇的.忽略
usb_get_urb(urb);
atomic_inc(&urb->use_count);
atomic_inc(&urb->dev->urbnum);
usbmon_urb_submit(&hcd->self, urb);
/* NOTE requirements on root-hub callers (usbfs and the hub
* driver, for now): URBs' urb->transfer_buffer must be
* valid and usb_buffer_{sync,unmap}() not be needed, since
* they could clobber root hub response data. Also, control
* URBs must be submitted in process context with interrupts
* enabled.
*/
//對傳輸的緩存區進行DMA映射
status = map_urb_for_dma(hcd, urb, mem_flags);
//出現錯誤,返回
if (unlikely(status)) {
usbmon_urb_submit_error(&hcd->self, urb, status);
goto error;
}
//如果是root hub
if (is_root_hub(urb->dev))
status = rh_urb_enqueue(hcd, urb);
else
//如果是一般的設備
status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);
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 (urb->reject)
wake_up(&usb_kill_urb_queue);
usb_put_urb(urb);
}
return status;
}
在這里函數里要注意到,urb->transfer_buffer是一個虛擬地址,用於UHCI的時候,必須要將其映射物理地址,以供設備使用.這 也就是map_urb_for_dma()要完成的工作. map_urb_for_dma()函數比較簡單,這里就不做詳細分析.
可能有人會有這樣的疑惑,對於root hub的情況,為什么不用對傳輸緩存區進行DMA映射呢?
在后面的處理中我們可以看到,其實對於root hub ,它不需要進行實際的物理傳輸,linux按照spec上的規定,將它靜態放置在內存中,在進行相關操作的時候,只要直接copy過去就可以了.
其次,要注意,這個函數不能用於中斷上下文,因為該函數是同步的,會引起睡眠.
在這里,我們看到,流程最終轉入到了下面的代碼片段中:
//如果是root hub
if (is_root_hub(urb->dev))
status = rh_urb_enqueue(hcd, urb);
else
//如果是一般的設備
status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);
下面,就分情況來剖析,各種傳輸到底是怎么完成的.
