三:传输过程的实现
说到传输过程,我们必须要从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 << 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)
这个宏都是根据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 < USB_STATE_DEFAULT))
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 < USB_STATE_CONFIGURED)
return -ENODEV;
//传送/接收的最大字节.如果这个最大巧若拙字节还要小于0,那就是非法的
max = le16_to_cpu(ep->desc.wMaxPacketSize);
if (max <= 0) {
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 <= 0)
return -EINVAL;
//urb->number_of_packets: 实时数据包个数.每个实时数据包对应urb->iso_frame_desc[]中的一项
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;
}
}
/* the I/O buffer must be mapped/unmapped, except when length=0 */
//如果要传输的缓存区大小小于0.非法
if (urb->transfer_buffer_length < 0)
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 <= 0)
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 << ilog2(urb->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()函数比较简单,这里就不做详细分析.
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);
下面,就分情况来剖析,各种传输到底是怎么完成的.
3.2:控制传输过程
1:root hub的控制传输
在前面看到,对于root hub的情况,流程会转入rh_urb_enqueue().代码如下:
static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
{
//如果是中断传输的端点
if (usb_endpoint_xfer_int(&urb->ep->desc))
return rh_queue_status (hcd, urb);
//如果是控制传输的端点
if (usb_endpoint_xfer_control(&urb->ep->desc))
return rh_call_control (hcd, urb);
return -EINVAL;
}
对应是控制传输的时,流程转入了rh_call_control()中:
static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
{
struct usb_ctrlrequest *cmd;
u16 typeReq, wValue, wIndex, wLength;
u8 *ubuf = urb->transfer_buffer;
u8 tbuf [sizeof (struct usb_hub_descriptor)]
__attribute__((aligned(4)));
const u8 *bufp = tbuf;
int len = 0;
int patch_wakeup = 0;
int status;
int n;
might_sleep();
spin_lock_irq(&hcd_root_hub_lock);
//将urb加到ep的urb传输链表
status = usb_hcd_link_urb_to_ep(hcd, urb);
spin_unlock_irq(&hcd_root_hub_lock);
if (status)
return status;
//将urb的私有域指向了hcd
urb->hcpriv = hcd; /* Indicate it's queued */
cmd = (struct usb_ctrlrequest *) urb->setup_packet;
typeReq = (cmd->bRequestType << 8) | cmd->bRequest;
wValue = le16_to_cpu (cmd->wValue);
wIndex = le16_to_cpu (cmd->wIndex);
wLength = le16_to_cpu (cmd->wLength);
if (wLength > urb->transfer_buffer_length)
goto error;
urb->actual_length = 0;
switch (typeReq) {
/* DEVICE REQUESTS */
/* The root hub's remote wakeup enable bit is implemented using
* driver model wakeup flags. If this system supports wakeup
* through USB, userspace may change the default "allow wakeup"
* policy through sysfs or these calls.
*
* Most root hubs support wakeup from downstream devices, for
* runtime power management (disabling USB clocks and reducing
* VBUS power usage). However, not all of them do so; silicon,
* board, and BIOS bugs here are not uncommon, so these can't
* be treated quite like external hubs.
*
* Likewise, not all root hubs will pass wakeup events upstream,
* to wake up the whole system. So don't assume root hub and
* controller capabilities are identical.
*/
case DeviceRequest | USB_REQ_GET_STATUS:
tbuf [0] = (device_may_wakeup(&hcd->self.root_hub->dev)
<< USB_DEVICE_REMOTE_WAKEUP)
| (1 << USB_DEVICE_SELF_POWERED);
tbuf [1] = 0;
len = 2;
break;
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
if (wValue == USB_DEVICE_REMOTE_WAKEUP)
device_set_wakeup_enable(&hcd->self.root_hub->dev, 0);
else
goto error;
break;
case DeviceOutRequest | USB_REQ_SET_FEATURE:
if (device_can_wakeup(&hcd->self.root_hub->dev)
&& wValue == USB_DEVICE_REMOTE_WAKEUP)
device_set_wakeup_enable(&hcd->self.root_hub->dev, 1);
else
goto error;
break;
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
tbuf [0] = 1;
len = 1;
/* FALLTHROUGH */
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
break;
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
switch (wValue & 0xff00) {
case USB_DT_DEVICE << 8:
if (hcd->driver->flags & HCD_USB2)
bufp = usb2_rh_dev_descriptor;
else if (hcd->driver->flags & HCD_USB11)
bufp = usb11_rh_dev_descriptor;
else
goto error;
len = 18;
break;
case USB_DT_CONFIG << 8:
if (hcd->driver->flags & HCD_USB2) {
bufp = hs_rh_config_descriptor;
len = sizeof hs_rh_config_descriptor;
} else {
bufp = fs_rh_config_descriptor;
len = sizeof fs_rh_config_descriptor;
}
if (device_can_wakeup(&hcd->self.root_hub->dev))
patch_wakeup = 1;
break;
case USB_DT_STRING << 8:
n = rh_string (wValue & 0xff, hcd, ubuf, wLength);
if (n < 0)
goto error;
urb->actual_length = n;
break;
default:
goto error;
}
break;
case DeviceRequest | USB_REQ_GET_INTERFACE:
tbuf [0] = 0;
len = 1;
/* FALLTHROUGH */
case DeviceOutRequest | USB_REQ_SET_INTERFACE:
break;
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
// wValue == urb->dev->devaddr
dev_dbg (hcd->self.controller, "root hub device address %d/n",
wValue);
break;
/* INTERFACE REQUESTS (no defined feature/status flags) */
/* ENDPOINT REQUESTS */
case EndpointRequest | USB_REQ_GET_STATUS:
// ENDPOINT_HALT flag
tbuf [0] = 0;
tbuf [1] = 0;
len = 2;
/* FALLTHROUGH */
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
case EndpointOutRequest | USB_REQ_SET_FEATURE:
dev_dbg (hcd->self.controller, "no endpoint features yet/n");
break;
/* CLASS REQUESTS (and errors) */
default:
/* non-generic request */
switch (typeReq) {
case GetHubStatus:
case GetPortStatus:
len = 4;
break;
case GetHubDescriptor:
len = sizeof (struct usb_hub_descriptor);
break;
}
status = hcd->driver->hub_control (hcd,
typeReq, wValue, wIndex,
tbuf, wLength);
break;
error:
/* "protocol stall" on error */
status = -EPIPE;
}
//如果发生了错误,将len置为0,表示没有数据要返回的
if (status) {
len = 0;
if (status != -EPIPE) {
dev_dbg (hcd->self.controller,
"CTRL: TypeReq=0x%x val=0x%x "
"idx=0x%x len=%d ==> %d/n",
typeReq, wValue, wIndex,
wLength, status);
}
}
//copy返回的数据到transfer_buffer
if (len) {
if (urb->transfer_buffer_length < len)
len = urb->transfer_buffer_length;
urb->actual_length = len;
// always USB_DIR_IN, toward host
memcpy (ubuf, bufp, len);
/* report whether RH hardware supports remote wakeup */
if (patch_wakeup &&
len > offsetof (struct usb_config_descriptor,
bmAttributes))
((struct usb_config_descriptor *)ubuf)->bmAttributes
|= USB_CONFIG_ATT_WAKEUP;
}
/* any errors get returned through the urb completion */
spin_lock_irq(&hcd_root_hub_lock);
//处理完成了,可以将urb从ep->urb_list上脱落了
usb_hcd_unlink_urb_from_ep(hcd, urb);
/* This peculiar use of spinlocks echoes what real HC drivers do.
* Avoiding calls to local_irq_disable/enable makes the code
* RT-friendly.
*/
spin_unlock(&hcd_root_hub_lock);
//URB已经使用完了,对它进行后续处理.包括调用complete唤醒调用进程
usb_hcd_giveback_urb(hcd, urb, status);
spin_lock(&hcd_root_hub_lock);
spin_unlock_irq(&hcd_root_hub_lock);
return 0;
}
从这里看到,它只是将值是保存在内存中的或者是调用驱动的hub_control接口,去读取/设置相关寄存器.这些过程就不再详细分析了.
2:非root_hub的控制传输
对于非root_hub的情况,流程会转入status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);
对于UHCI,它的对应接口为uhci_urb_enqueue().代码如下:
static int uhci_urb_enqueue(struct usb_hcd *hcd,
struct urb *urb, gfp_t mem_flags)
{
int ret;
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
unsigned long flags;
struct urb_priv *urbp;
struct uhci_qh *qh;
spin_lock_irqsave(&uhci->lock, flags);
//将urb加到通信端点的传输链表 urb->urb_list 加至urb->ep->urb_list
ret = usb_hcd_link_urb_to_ep(hcd, urb);
if (ret)
goto done_not_linked;
ret = -ENOMEM;
//初始化urb->hcpriv,它的结构为struct urb_priv
//urb->hcpriv = urbp
//urbp->urb = urb
urbp = uhci_alloc_urb_priv(uhci, urb);
if (!urbp)
goto done;
//创建QH
if (urb->ep->hcpriv)
qh = urb->ep->hcpriv;
else {
qh = uhci_alloc_qh(uhci, urb->dev, urb->ep);
if (!qh)
goto err_no_qh;
}
urbp->qh = qh;
//各类型的不同操作
switch (qh->type) {
case USB_ENDPOINT_XFER_CONTROL:
ret = uhci_submit_control(uhci, urb, qh);
break;
case USB_ENDPOINT_XFER_BULK:
ret = uhci_submit_bulk(uhci, urb, qh);
break;
case USB_ENDPOINT_XFER_INT:
ret = uhci_submit_interrupt(uhci, urb, qh);
break;
case USB_ENDPOINT_XFER_ISOC:
urb->error_count = 0;
ret = uhci_submit_isochronous(uhci, urb, qh);
break;
}
if (ret != 0)
goto err_submit_failed;
/* Add this URB to the QH */
urbp->qh = qh;
//将urbp加到qh的queue链表中
list_add_tail(&urbp->node, &qh->queue);
/* If the new URB is the first and only one on this QH then either
* the QH is new and idle or else it's unlinked and waiting to
* become idle, so we can activate it right away. But only if the
* queue isn't stopped. */
//qh的queue中只有一个urbp的时候,且QH没有被禁用
if (qh->queue.next == &urbp->node && !qh->is_stopped) {
//启用这个QH进行传输了
uhci_activate_qh(uhci, qh);
//fsbr:高速传输
uhci_urbp_wants_fsbr(uhci, urbp);
}
goto done;
err_submit_failed:
if (qh->state == QH_STATE_IDLE)
uhci_make_qh_idle(uhci, qh); /* Reclaim unused QH */
err_no_qh:
uhci_free_urb_priv(uhci, urbp);
done:
if (ret)
usb_hcd_unlink_urb_from_ep(hcd, urb);
done_not_linked:
spin_unlock_irqrestore(&uhci->lock, flags);
return ret;
}
Urbp是urb的一个扩展区结构,这个urbp最后包含的很多信息,具体如下:
Urbp->urb:指向了要传输的urb
Urbp->qh:指向传输的QH
在进行传输的时候,先创建一个QH结构,然后将数据打成TD的形式,再将TD挂到QH上面.
对于QH的创建,有必要跟进去看一下:
static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci,
struct usb_device *udev, struct usb_host_endpoint *hep)
{
dma_addr_t dma_handle;
struct uhci_qh *qh;
qh = dma_pool_alloc(uhci->qh_pool, GFP_ATOMIC, &dma_handle);
if (!qh)
return NULL;
memset(qh, 0, sizeof(*qh));
qh->dma_handle = dma_handle;
qh->element = UHCI_PTR_TERM;
qh->link = UHCI_PTR_TERM;
INIT_LIST_HEAD(&qh->queue);
INIT_LIST_HEAD(&qh->node);
if (udev) { /* Normal QH */
qh->type = hep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
if (qh->type != USB_ENDPOINT_XFER_ISOC) {
qh->dummy_td = uhci_alloc_td(uhci);
if (!qh->dummy_td) {
dma_pool_free(uhci->qh_pool, qh, dma_handle);
return NULL;
}
}
qh->state = QH_STATE_IDLE;
qh->hep = hep;
qh->udev = udev;
hep->hcpriv = qh;
//如果是中断传输或者者是实时传输.计算数据传输所耗的总线时间
if (qh->type == USB_ENDPOINT_XFER_INT ||
qh->type == USB_ENDPOINT_XFER_ISOC)
qh->load = usb_calc_bus_time(udev->speed,
usb_endpoint_dir_in(&hep->desc),
qh->type == USB_ENDPOINT_XFER_ISOC,
le16_to_cpu(hep->desc.wMaxPacketSize))
/ 1000 + 1;
} else { /* Skeleton QH */
qh->state = QH_STATE_ACTIVE;
qh->type = -1;
}
return qh;
}
这个函数在UHCI的start函数中已经看到过,不过在那个地方,它的udev参数是空的.这只是一个框架,并不会用来实际的传输.
而对于实际的传输,也就是代码中注释到的所谓”Normal QH”的情况.
我们在代码中看到.如果不是一个实时传输.它还会创建一个dummy_td,这个TD是用来做什么,我们暂且不要去管,只知道有这么回事就可以了.另外,对于中断传输和实现传输,还要计算传输字节所耗的总线带宽.
返回到uhci_urb_enqueue()中,对于控制传输的情况,流程会转入到uhci_submit_control()中,这个函数的代码如下所示:
static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
struct uhci_qh *qh)
{
struct uhci_td *td;
unsigned long destination, status;
int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize);
int len = urb->transfer_buffer_length;
dma_addr_t data = urb->transfer_dma;
__le32 *plink;
struct urb_priv *urbp = urb->hcpriv;
int skel;
/* The "pipe" thing contains the destination in bits 8--18 */
//dev_num + end_num
destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
/* 3 errors, dummy TD remains inactive */
//设置TD描述符的Status的C_ERR字段为3.如果失败次数超过了3.则将TD置为inactive
status = uhci_maxerr(3);
//如果是低速设备.设置STATUS的LS位,表示是一个低速设备
if (urb->dev->speed == USB_SPEED_LOW)
status |= TD_CTRL_LS;
/*
* Build the TD for the control request setup packet
*/
//SETUP包
td = qh->dummy_td;
//将TD放到urbp的td_list链表
uhci_add_td_to_urbp(td, urbp);
//SETUP阶段的数据信息包只有8个字节
uhci_fill_td(td, status, destination | uhci_explen(8),
urb->setup_dma);
plink = &td->link;
status |= TD_CTRL_ACTIVE;
/*
* If direction is "send", change the packet ID from SETUP (0x2D)
* to OUT (0xE1). Else change it from SETUP to IN (0x69) and
* set Short Packet Detect (SPD) for all data packets.
*
* 0-length transfers always get treated as "send".
*/
//判断是往哪一个方向的
if (usb_pipeout(urb->pipe) || len == 0)
destination ^= (USB_PID_SETUP ^ USB_PID_OUT);
else {
destination ^= (USB_PID_SETUP ^ USB_PID_IN);
//如果是IN方向的,必须要置SPD位
//如果是输入方向且数据包被成功传递,但长度小于最大长度,就会将TD置为inactive
//如果短包中断被使用,就会上报一个中断
//SPD是OUT方向是无意义的
status |= TD_CTRL_SPD;
}
/*
* Build the DATA TDs
*/
//数据传输阶段
while (len > 0) {
int pktsze = maxsze;
//是后的一个包,确实是短包了,清除掉SPD
if (len <= pktsze) { /* The last data packet */
pktsze = len;
status &= ~TD_CTRL_SPD;
}
td = uhci_alloc_td(uhci);
if (!td)
goto nomem;
*plink = LINK_TO_TD(td);
/* Alternate Data0/1 (start with Data1) */
//交替TOGGLE位,是用来同步的, usb2.0 spec上有详细说明
destination ^= TD_TOKEN_TOGGLE;
//将TD添加到td_list
uhci_add_td_to_urbp(td, urbp);
uhci_fill_td(td, status, destination | uhci_explen(pktsze),
data);
plink = &td->link;
data += pktsze;
len -= pktsze;
}
/*
* Build the final TD for control status
*/
//状态阶段
td = uhci_alloc_td(uhci);
if (!td)
goto nomem;
*plink = LINK_TO_TD(td);
/* Change direction for the status transaction */
//状态阶段的方向跟前一笔事务是相反的方向
destination ^= (USB_PID_IN ^ USB_PID_OUT);
//限定为DATA1
destination |= TD_TOKEN_TOGGLE; /* End in Data1 */
uhci_add_td_to_urbp(td, urbp);
//状态阶段中,只需要传送0长度的数据
//IOC置位了,即传输完成了就会产生中断
uhci_fill_td(td, status | TD_CTRL_IOC,
destination | uhci_explen(0), 0);
plink = &td->link;
/*
* Build the new dummy TD and activate the old one
*/
td = uhci_alloc_td(uhci);
if (!td)
goto nomem;
*plink = LINK_TO_TD(td);
//起个标识作用,表示该TD已经结尾了,这个包是不会参与传输的,因为它没有置ACTIVE标志
uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0);
wmb();
//将dummy_td置为ACTIVE
qh->dummy_td->status |= __constant_cpu_to_le32(TD_CTRL_ACTIVE);
//将dummy_td指向新创建的TD
qh->dummy_td = td;
/* Low-speed transfers get a different queue, and won't hog the bus.
* Also, some devices enumerate better without FSBR; the easiest way
* to do that is to put URBs on the low-speed queue while the device
* isn't in the CONFIGURED state. */
//确定QH是属于低速还是高速
if (urb->dev->speed == USB_SPEED_LOW ||
urb->dev->state != USB_STATE_CONFIGURED)
skel = SKEL_LS_CONTROL;
else {
skel = SKEL_FS_CONTROL;
uhci_add_fsbr(uhci, urb);
}
//QH的初始状态是为QH_STATE_IDLE
if (qh->state != QH_STATE_ACTIVE)
qh->skel = skel;
//实际传输的长度置-8.正好是SETUP的包大小,以后返回的actual_length就是单纯的数据传输长度了
urb->actual_length = -8; /* Account for the SETUP packet */
return 0;
nomem:
/* Remove the dummy TD from the td_list so it doesn't get freed */
uhci_remove_td_from_urbp(qh->dummy_td);
return -ENOMEM;
}
理解这个函数需要参照usb2.0 spec.控制传输包含了三个阶段,分别是SETUP,DATA和HANDSHAKE.
Setup阶段的token信息包的PID为SETUP(1101B),数据信息包就是urb->
setup_packet部份,它的物理地址是urb->
setup_dma.为八个字节.
setup_packet部份,它的物理地址是urb->
setup_dma.为八个字节.
DATA阶段的token信息包的PID为IN/OUT(要根据传输方向来确定),后面跟的是具体的数据的数据信息包.另外,DATA阶段的数据信息包中的PID起同步作用,DATA1/DATA0轮流交换.
HANDSHAKE阶段的信息包的PID方向跟DATA阶段是相反的,数据信息包的PID值固定为DATA1,传输的一个长度为0的数据.
另外,虽然每一笔事务都有token信息包,数据信息包和联络信息包,联络信息包UHCI会自动管理,并不需要驱动进行操作.设备返回的联络信息包会反应到对应TD的STATUS段中.
其实,最后还附带有一个TQ,因为它没有置ACTIVE位,实际上他不会参与传输,只是起一个标识结尾的作用.
还有一个值得注意的地方:如果是全速的控制传输,将skel置为SKEL_FS_CONTROL后,会调用uhci_add_fsbr().这个函数代码如下:
static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb)
{
struct urb_priv *urbp = urb->hcpriv;
if (!(urb->transfer_flags & URB_NO_FSBR))
urbp->fsbr = 1;
}
从上面的代码看到,如果URB没有带URB_NO_FSBR的标志,就会将urbp->fsbr置为1.这在后面关于FSBR的分析是会用到的.在这里特别提一下.
请自行对照代码中的注释进行阅读,这里就不进行详细分析了
经过上面的操作之后,所有的TD都链在了urbp->urb_list中.qh->
dummy_td指向它的最后一个TD.
dummy_td指向它的最后一个TD.
返回到uhci_urb_enqueue().流程会转入uhci_activate_qh().这个函数是各种传输所共用的.
代码如下:
static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
{
//qh->queure为空,也就是说QH没有链接到urbp->node
WARN_ON(list_empty(&qh->queue));
/* Set the element pointer if it isn't set already.
* This isn't needed for Isochronous queues, but it doesn't hurt. */
//QH的初始值是qh->element和qh->link都是UHCI_PTR_TERM
if (qh_element(qh) == UHCI_PTR_TERM) {
//从qh导出urbp
struct urb_priv *urbp = list_entry(qh->queue.next,
struct urb_priv, node);
//经过前面的分析,所有td都是挂在td_list上面的
struct uhci_td *td = list_entry(urbp->td_list.next,
struct uhci_td, list);
//将TD挂到QH上面
qh->element = LINK_TO_TD(td);
}
/* Treat the queue as if it has just advanced */
qh->wait_expired = 0;
qh->advance_jiffies = jiffies;
if (qh->state == QH_STATE_ACTIVE)
return;
qh->state = QH_STATE_ACTIVE;
/* Move the QH from its old list to the correct spot in the appropriate
* skeleton's list */
//刚开始的时候uhci->next_qh为NULL
if (qh == uhci->next_qh)
uhci->next_qh = list_entry(qh->node.next, struct uhci_qh,
node);
//初始化时qh->node为空
list_del(&qh->node);
//ISO传输
if (qh->skel == SKEL_ISO)
link_iso(uhci, qh);
//INTER传输
else if (qh->skel < SKEL_ASYNC)
link_interrupt(uhci, qh);
else
//其它类型的
link_async(uhci, qh);
}
上面的代码中的注释注明了各项操作.
QH在初始化时,将qh->element和qh->link都设置为了UHCI_PTR_TERM.也就是说它下面没有挂上TD,也没有链接其它的QH.在这个函数中,首先将QH和TD要关联起来,即,把TD连接到QH->element.
对于控制传输,流程最后转入link_async()中,是到QH和UHCI的frame list关联起来的时候了.
代码如下示:
static void link_async(struct uhci_hcd *uhci, struct uhci_qh *qh)
{
struct uhci_qh *pqh;
__le32 link_to_new_qh;
/* Find the predecessor QH for our new one and insert it in the list.
* The list of QHs is expected to be short, so linear search won't
* take too long. */
//uhci->skel_async_qh:skelqh[9]
//各钟ASYNC的QH按照从小到大的顺序链接在skelqh[9]->node上
//注意了,list_for_each_entry_reverse()是一个反向搜索
list_for_each_entry_reverse(pqh, &uhci->skel_async_qh->node, node) {
if (pqh->skel <= qh->skel)
break;
}
//在skelqh[9]->node中找到一个合适的位置,将QH插下去
list_add(&qh->node, &pqh->node);
/* Link it into the schedule */
//接QH插入到调度队列
qh->link = pqh->link;
//这里涉及到物理地址的操作,为了防止编译器优化,插上内存屏障
wmb();
link_to_new_qh = LINK_TO_QH(qh);
pqh->link = link_to_new_qh;
/* If this is now the first FSBR QH, link the terminating skeleton
* QH to it. */
//如果是第1个fsbr,将skelqh[10]指向这个QH
//skel_term_qh: skelqh[10]
if (pqh->skel < SKEL_FSBR && qh->skel >= SKEL_FSBR)
uhci->skel_term_qh->link = link_to_new_qh;
}
Async类型的传输包括控制传输和批量传输.这样的传输是不会保证实时性的,只有在带宽空闲的时候才会进行.从上面的代码中可以看到,它们的
QH都是挂在skeqh[9].即是挂在int1的QH后面的.根据我们之前对UHCI调度初始化的分析.int1是排在最后的一个QH.所以,挂在
int1后的QH,要等前面的QH运行完之后才能够运行.
QH都是挂在skeqh[9].即是挂在int1的QH后面的.根据我们之前对UHCI调度初始化的分析.int1是排在最后的一个QH.所以,挂在
int1后的QH,要等前面的QH运行完之后才能够运行.
而对于Control传输.根据设备的传输速度又有了SKEL_LS_CONTROL和SKEL_FS_CONTROL.对于BULK有22
这几个宏的定义如下:
#define SKEL_LS_CONTROL 20
#define SKEL_FS_CONTROL 21
#define SKEL_FSBR SKEL_FS_CONTROL
#define SKEL_BULK 22
事实上,我们希望他们传殊的优先级顺序是SKEL_LS_CONTROL > SKEL_FS_CONTROL > SKEL_BULK
在这里,要特别注意上面的搜索是从后面往前面搜索的.不要被它弄迷糊了.
在最后,还将skel_term_qh指向第一个SKEL_FS_CONTROL的QH.在前面UHCI调度初始化中曾分析过.这个skel_term_qh实际上并挂在frame list中.在这里,
skel_term_qh->link指向第一个SKEL_FS_CONTROL是在做什么呢?暂且把这个问题放到这里.我们马上就会涉及到这部份内容了.
skel_term_qh->link指向第一个SKEL_FS_CONTROL是在做什么呢?暂且把这个问题放到这里.我们马上就会涉及到这部份内容了.
从uhci_activate_qh()返回之后,流程会进入到uhci_urbp_wants_fsbr().
在分析之个函数之前.先介绍一下什么叫FSBR.FSBR的全名叫:
Full Speed
Bus
Reclamtion.它是一种带宽回收机制.只有在全速传输中才会用到.所谓带宽回收,其实是跟UHCI默认遍历QH的方式有关的.在UHCI
的spec中,定义了两种遍历QH的方式.分别是广度优先搜索和深度优先搜索.深度优先搜索在PCI驱动结构中大量被用到.它类似于二叉树的先根遍历.深
度优先搜索类似于二叉树的层遍历.
Full Speed
Bus
Reclamtion.它是一种带宽回收机制.只有在全速传输中才会用到.所谓带宽回收,其实是跟UHCI默认遍历QH的方式有关的.在UHCI
的spec中,定义了两种遍历QH的方式.分别是广度优先搜索和深度优先搜索.深度优先搜索在PCI驱动结构中大量被用到.它类似于二叉树的先根遍历.深
度优先搜索类似于二叉树的层遍历.
具体到UHCI的QH遍历方式上:
深度优先:UHCI先处理完QH下面的所有TD,然后再处理下一个QH.很显然,这样的方式不会有带宽浪费.但对挂在后面的QH来说很不公平.
广度优先:UHCI先取QH下挂的TD(qh->element).先将这个TD处理完.然后再它后面链接的QH(qh->
link).这样的方式对挂在后面的QH都是公平的.但是遍历完所有QH之后.如果时间还有剩余,UHCI什么事都不会干.但很显然,每个QH下的TD并
不是都处理完了.
link).这样的方式对挂在后面的QH都是公平的.但是遍历完所有QH之后.如果时间还有剩余,UHCI什么事都不会干.但很显然,每个QH下的TD并
不是都处理完了.
所以,针对广度优先搜索,出现了带宽回收.具体怎么样回收呢?我们来看uhci_urbp_wants_fsbr()的代码:
static void uhci_urbp_wants_fsbr(struct uhci_hcd *uhci, struct urb_priv *urbp)
{
if (urbp->fsbr) {
uhci->fsbr_is_wanted = 1;
if (!uhci->fsbr_is_on)
uhci_fsbr_on(uhci);
else if (uhci->fsbr_expiring) {
uhci->fsbr_expiring = 0;
del_timer(&uhci->fsbr_timer);
}
}
}
记得在uhci_submit_control()分析的时候,提到过,如果是全速控制传输会调用uhci_add_fsbr()将urbp->fsbr置为1.
初始化的时候,会将uhci->fsbr_is_on设置为0.也就是说,在这个地方,流程会转入到uhci_fsbr_on().代码如下:
static void uhci_fsbr_on(struct uhci_hcd *uhci)
{
struct uhci_qh *lqh;
/* The terminating skeleton QH always points back to the first
* FSBR QH. Make the last async QH point to the terminating
* skeleton QH. */
uhci->fsbr_is_on = 1;
lqh = list_entry(uhci->skel_async_qh->node.prev,
struct uhci_qh, node);
lqh->link = LINK_TO_QH(uhci->skel_term_qh);
}
在这个函数中,先将fsbr_is_on设置为1.然后,取得挂在skel_async_qh上的最后一个QH.然后将这个QH的指向skel_term_qh.
结合uhci_activate_qh()中对于skel_term_qh的操作.得出以下的示意图:
在uhci_activate_qh()的分析中,我们了解到:按照SKEL_LS_CONTROL >
SKEL_FS_CONTROL >
SKEL_BULK优先级链接在skel_async_qh->node中.所以SKEL_FS_CONTROL后面可能还会跟有
SKEL_BULK类型的传送包.
SKEL_FS_CONTROL >
SKEL_BULK优先级链接在skel_async_qh->node中.所以SKEL_FS_CONTROL后面可能还会跟有
SKEL_BULK类型的传送包.
如上图所示:skel_term_qh起一个中间桥的作用,将SKEL_FS_CONTRO后的QH链接起来.这样,在UHCI有空闲带宽的时候就不会傻呆着无所事事了.它会循环处理SKEL_FS_CONTRO和SKEL_BULK的包.
另外,从上图中也可以看出.只有全速控制传和批量传输会用到FSBR.另外,对于批量传输,它无所谓低速批量传输.因为所有的BULK都会链接在上面的圆环中.
既然说到了uhci_fsbr_on().顺便说一下它的对立函数uhci_fsbr_off():
static void uhci_fsbr_off(struct uhci_hcd *uhci)
{
struct uhci_qh *lqh;
/* Remove the link from the last async QH to the terminating
* skeleton QH. */
uhci->fsbr_is_on = 0;
lqh = list_entry(uhci->skel_async_qh->node.prev,
struct uhci_qh, node);
lqh->link = UHCI_PTR_TERM;
}
它将fsbr_is_on置为0.然后断开了上图中所示的圆环.
到这里之后,TD能够被UHCI调度了.那TD调度完了之后,驱动要怎样才能够知道呢?
回忆之前,我们在最后的一个有效TD的STATUS中,置位了IOC.也就是说传输完成之后,会上报一个中断.所以,控制传输完成之后是由中断
处理函数进行处理的.事实上,无论哪一种传输方式到传输完成的时候,就会由中断处理函数都行后续的处理工作.所以中断处理这部份放到最后统一进行分析.
处理函数进行处理的.事实上,无论哪一种传输方式到传输完成的时候,就会由中断处理函数都行后续的处理工作.所以中断处理这部份放到最后统一进行分析.
上面的分析中涉及到了BULK传输.那就来看一下BULK传输的实现.
3.3:批量传输过程
Root hub没有批量传输.按照控制传输的流程.批量传输最终也会交给uhci_urb_enqueue()处理.与前面分析的控制传输不同的是,在switch的判断中,流程会转向uhci_submit_bulk().
static int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb,
struct uhci_qh *qh)
{
int ret;
/* Can't have low-speed bulk transfers */
//如果是低速设备,退出.BULK不能在低速设备中使用
if (urb->dev->speed == USB_SPEED_LOW)
return -EINVAL;
//qh的初始化state是QH_STATE_IDLE
if (qh->state != QH_STATE_ACTIVE)
qh->skel = SKEL_BULK;
ret = uhci_submit_common(uhci, urb, qh);
//uhci_add_fsbr():判断urb是否支持FSBR
if (ret == 0)
uhci_add_fsbr(uhci, urb);
return ret;
}
首先一个低速设备是不能用做BULK传输的.另外,在初始化qh的时候,将它的状态初始化成QH_STATA_IDLE.代码中再判断一次,是
为了防止操作的QH是已经挂在UHCI调度的QH.然后再调用uhci_submit_common()去填充这次传输所需要的TD.最后同控制传输的情
况一下,也要进行FSBR的判断.
为了防止操作的QH是已经挂在UHCI调度的QH.然后再调用uhci_submit_common()去填充这次传输所需要的TD.最后同控制传输的情
况一下,也要进行FSBR的判断.
uhci_submit_common()代码如下:
static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
struct uhci_qh *qh)
{
struct uhci_td *td;
unsigned long destination, status;
int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize);
int len = urb->transfer_buffer_length;
dma_addr_t data = urb->transfer_dma;
__le32 *plink;
struct urb_priv *urbp = urb->hcpriv;
unsigned int toggle;
if (len < 0)
return -EINVAL;
/* The "pipe" thing contains the destination in bits 8--18 */
destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
//取得toggle位
toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
usb_pipeout(urb->pipe));
/* 3 errors, dummy TD remains inactive */
//设置uhci spec规定的TD第二个寄存器中的C_ERR位,当错误次数超过3次,就会认为TD无效
status = uhci_maxerr(3);
//如果是低速设备,设置STATUS段的LS位,表明这是一个低速传输
if (urb->dev->speed == USB_SPEED_LOW)
status |= TD_CTRL_LS;
//如果是输入管道,设置bit27的SPD
//SPD只有在IN方向才有效
if (usb_pipein(urb->pipe))
status |= TD_CTRL_SPD;