linux设备驱动之USB数据传输分析(续)

it2022-05-09  19

uhci_scan_schedule()是这个函数的核心操作.也是经常出现的一个函数.代码如下: static void uhci_scan_schedule(struct uhci_hcd *uhci) {    int i;     struct uhci_qh *qh;       /* Don't allow re-entrant calls */     //如果正在进行scan处理.设置need_rescan之后退出     if (uhci->scan_in_progress) {         uhci->need_rescan = 1;         return;     }     //设置scan_in_progess.防止被其它进程打扰     uhci->scan_in_progress = 1; rescan:     uhci->need_rescan = 0;     uhci->fsbr_is_wanted = 0;       //该TD所在的FRAME结束之后, 不要发送中断     uhci_clear_next_interrupt(uhci);     //得到当前的frame号     uhci_get_current_frame_number(uhci);     uhci->cur_iso_frame = uhci->frame_number;       /* Go through all the QH queues and process the URBs in each one */     //扫描所有的QH     for (i = 0; i < UHCI_NUM_SKELQH - 1; ++i) {         //Normal QH是链接在SKEL QH的node链表中         uhci->next_qh = list_entry(uhci->skelqh[i]->node.next,                 struct uhci_qh, node);         //遍历skelqh下的QH         while ((qh = uhci->next_qh) != uhci->skelqh[i]) {             uhci->next_qh = list_entry(qh->node.next,                     struct uhci_qh, node);               if (uhci_advance_check(uhci, qh)) {                 //uhci_advance_check返回1.则会转入此外,即表示有传输完成的TD                 uhci_scan_qh(uhci, qh);                 if (qh->state == QH_STATE_ACTIVE) {                     uhci_urbp_wants_fsbr(uhci,     list_entry(qh->queue.next, struct urb_priv, node));                 }             }         }     }       //last_iso_frame:最近的帧号     uhci->last_iso_frame = uhci->cur_iso_frame;     //需要再次扫描     if (uhci->need_rescan)         goto rescan;     //将其置为0,表示扫描完成了     uhci->scan_in_progress = 0;       if (uhci->fsbr_is_on && !uhci->fsbr_is_wanted &&             !uhci->fsbr_expiring) {         uhci->fsbr_expiring = 1;         mod_timer(&uhci->fsbr_timer, jiffies + FSBR_OFF_DELAY);     }       //如果skelqh[0]为空,清除term_td的IOC标志位     if (list_empty(&uhci->skel_unlink_qh->node))         uhci_clear_next_interrupt(uhci);     else         //允许frame 完了之后产生中断         uhci_set_next_interrupt(uhci); } 这段代码使用了标志位的方来来防止它在同一时刻被多次运行.在第一次进去的时候,将scan_in_progress设为1.此后如果有进程再 进来,判断scan_in_progress为1后,将need_rescan设为1就退出来.每次执行完这个函数的时候,会检查need_rescan 是否为1,如果是1的话,再去扫描一次. 我们来关注一下代码中的,关于FSBR的操作.代码片段如下: if (uhci->fsbr_is_on && !uhci->fsbr_is_wanted &&             !uhci->fsbr_expiring) {         uhci->fsbr_expiring = 1;         mod_timer(&uhci->fsbr_timer, jiffies + FSBR_OFF_DELAY);     } 如果if判断条件满足,就将会fsbr_expiring设为1,然后重置定时器,定时器到期之后,会调用定时器处理函数 uhci_fsbr_timeout().在定时器处理函数中,判断fsbr_expiring的值.如果大于1,就会调用 uhci_fsbr_off().将skel_term_qh构成的环断开,也就是说断开了FSBR. 那什么时候才会满足这个if条件呢? 条件1: uhci->fsbr_is_on为1.之前分析过,在启用FSBR的时候,也就是在uhci_fsbr_on()的函数中,会将uhci->fsbr_is_on设为1. 如果该值为0.说明当前并没有使用FSBR.也就不需要断开FSBR了. 条件2:uhci->fsbr_is_wanted为0.这个条件一般是会满足的,因为在刚进入uhci_scan_schedule()的时候,就将它设为了0.除非,在那几个for循环中,再次调用了uhci_urbp_wants_fsbr(). 条件3: uhci->fsbr_expiring为0.除了特殊设置, uhci_fsbr_on()处理之后,这个字段是为0的. 我们注意到,重置定时器的到期时间为FSBR_OFF_DELAY之后,内核开发者认为,在FSBR_OFF_DELAY的时间内,完全可以完成URB的传输了. 那如果,在超时定时器运行期间,又有URB开启了FSBR会怎么呢? 在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);         }     } } 在这种情况下,就会进入else if的处理流程,就将这个定时器删除了. 下次启动定时器,又会在中断处理中,又将超时设为了FSBR_OFF_DELAY.*^_^*.这样,每启动一次FSBR,就会保证它至少在FSBR_OFF_DELAY才会将FSBR关闭,让它有足够的时间完成URB的传输.   这个函数的核心操作就是在这个for循环中了.在for循环中,它依次遍历uhci->skelqh[0]~skelqh[9]上的QH.然后对每一个QH的处理如下:     1:调用uhci_advance_check()来判断每一个QH.关于uhci_advance_check()函数,先看它的注释:  Check for queues that have made some forward progress. Returns 0 if the queue is not Isochronous, is ACTIVE, and has not advanced since last examined; 1 otherwise. 注释中说道,如果不是实时传输,或者QH是ACTIVE状态而且QH自从上次检查点开始就没有被调度过就会返回0.其它的情况就会返回1. 2:如果uhci_advance_check()返回1,就会调用uhci_scan_qh()等后续处理.另外,如果当前QH是 Active的,表明QH还没有调度完.就会再次调用uhci_urbp_wants_fsbr().这样也阻止了后面fsbr_timer定时器的启 动.   挨个分析上面的函数: Uhci_advance_check()的代码如下示: static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh) {     struct urb_priv *urbp = NULL;     struct uhci_td *td;     int ret = 1;     unsigned status;     //如果是实时传输,就马上返回1     if (qh->type == USB_ENDPOINT_XFER_ISOC)         goto done;        //如果QH不为Active     if (qh->state != QH_STATE_ACTIVE) {         urbp = NULL;         status = 0;       } else {         //如果QH状态为Active.         //则取挂在下面的urbp.再到urbp下面找到要传输的td_list         urbp = list_entry(qh->queue.next, struct urb_priv, node);         td = list_entry(urbp->td_list.next, struct uhci_td, list);         //如果QH前进了,那么最先改变状态的是1个TD         status = td_status(td);         //如果QH已经前进了,返回1         if (!(status & TD_CTRL_ACTIVE)) {               /* We're okay, the queue has advanced */             //置QH的wait_expired为0.advance_jiffies为前进时间戳.             qh->wait_expired = 0;             qh->advance_jiffies = jiffies;             goto done;         }         ret = 0;     }       //下面就是对应QH没有前进,或者是QH不为Active的情况,另外QH不是ISO     /* The queue hasn't advanced; check for timeout */     if (qh->wait_expired)         goto done;     //如果QH超过QH_WAIT_TIMEOUT没有前进过了     if (time_after(jiffies, qh->advance_jiffies + QH_WAIT_TIMEOUT)) {           /* Detect the Intel bug and work around it */         //修正intel的BUG         if (qh->post_td && qh_element(qh) == LINK_TO_TD(qh->post_td)) {             qh->element = qh->post_td->link;             qh->advance_jiffies = jiffies;             ret = 1;             goto done;         }           //将qh->wait_expired置为1.表示该QH已经延迟         qh->wait_expired = 1;            //如果urbp使用FSBR,且QH中的第一个TD没有IOC标志,即不是最后要传输的TD         if (urbp && urbp->fsbr && !(status & TD_CTRL_IOC))             //将QH放到skel_unlink_qh             uhci_unlink_qh(uhci, qh);       } else {         /* Unmoving but not-yet-expired queues keep FSBR alive */         //如果还没有到延时到期. 重启FSBR         if (urbp)             uhci_urbp_wants_fsbr(uhci, urbp);     }   done:     return ret; } 如果是实时传输,马上返回1,对于不是QH_STATE_ACTIVE的QH,就要检查这个QH是否被调度到了.从前面的分析我们可以看 到,urbp链接在qh->queue上,而要传输的td又是链接在urbp->td_list上.对于所有链接在td_list上的td, 第一个TD肯定是被第一个调度的,如果第一个TD的状态变为了INACTIVE.说明这个QH已经被调度到了.立即返回1.从而退出这个函数. 而对于那些依然没有被调度到的QH.kernel设定了一个最大时间.如果在这个时间内,QH依然还没有被调度到,则认为这个QH可能发生了问题. 在前面的分析中,各种传输的提交都要经过uhci_activate_qh().在这个函数里,将qh-> advance_jiffies 设置为了提交urb的时间.而在每一次检测到qh被调度的时候,又会更新advance_jiffies值.因此,只要在检测到末调度的时候,比较当前时 间和advance_jiffies的时间戳就可以计算出,有多长时间没有得到过调度了. 我们跟进看一下,如果超时没有得到调度,会怎么处理. 将相关部份代码列出: static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh) {         ......         ......     if (time_after(jiffies, qh->advance_jiffies + QH_WAIT_TIMEOUT)) {           /* Detect the Intel bug and work around it */         //修正intel的BUG         if (qh->post_td && qh_element(qh) == LINK_TO_TD(qh->post_td)) {             qh->element = qh->post_td->link;             qh->advance_jiffies = jiffies;             ret = 1;             goto done;         }           //将qh->wait_expired置为1.表示该QH已经延迟         qh->wait_expired = 1;            //如果urbp使用FSBR,且QH中的第一个TD没有IOC标志,即不是最后要传输的TD         if (urbp && urbp->fsbr && !(status & TD_CTRL_IOC))             //将QH放到skel_unlink_qh             uhci_unlink_qh(uhci, qh);       } ...... ...... } 这段代码中的第一个if语句是为了修正intel的一个BUG.作者在注释中扫描过这个bug:  Early Intel controllers have a bug which causes qh->element sometimes not to advance when a TD completes successfully.  The queue remains stuck on the inactive completed TD.  We detect such cases and advance the element pointer by hand. 大意是说,在intel早期的控制器中,TD传输完成之后,它的QH->element项不会自动更新. 正常的处理是这样的:如果TD传输完成,就会将其从QH的链上删除,QH->element会指向下一个QH. 对应到代码中.qh->post_td是QH中上一次调度完成的TD(我们在后面可以看到),如果这个调度完成的TD依然挂在QH->element上面,那就说明它没有自动更新过来,需要手动把TD摘除.   然后,将qh->wait_expired设为1.表示该QH已经延迟了. 最后,如果urbp使用了FSBR.然且,QH中的第一个TD不是最后的TD.则会调用uhci_unlink_qh().代码如下: static void uhci_unlink_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) {     //如果QH已经是unlink了     if (qh->state == QH_STATE_UNLINKING)         return;     //有效性判断     WARN_ON(qh->state != QH_STATE_ACTIVE || !qh->udev);     //将状态置为UNLINKING     qh->state = QH_STATE_UNLINKING;       /* Unlink the QH from the schedule and record when we did it */     //如果是实时传输     if (qh->skel == SKEL_ISO)         ;     //unlink中断队列     else if (qh->skel < SKEL_ASYNC)         unlink_interrupt(uhci, qh);     //unlink控制队列或者bulk队列     else         unlink_async(uhci, qh);       //得到当前调度帧号     uhci_get_current_frame_number(uhci);     //unlink时候的QH 的帧号     qh->unlink_frame = uhci->frame_number;       /* Force an interrupt so we know when the QH is fully unlinked */     //如果unlink_qh是空的     if (list_empty(&uhci->skel_unlink_qh->node))         uhci_set_next_interrupt(uhci);       /* Move the QH from its old list to the end of the unlinking list */     //更新uhci->next_qh     if (qh == uhci->next_qh)         uhci->next_qh = list_entry(qh->node.next, struct uhci_qh,                 node);     //将qh加到unlink_qh     list_move_tail(&qh->node, &uhci->skel_unlink_qh->node); } 逻辑很简单,就是将QH链接到skel_unlink_qh中.来看下中断传输和控制/批量传输的unlink过程. 对于中断传输,调用unlink_interrupt().代码如下: static void unlink_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh) {     struct uhci_qh *pqh;       pqh = list_entry(qh->node.prev, struct uhci_qh, node);     pqh->link = qh->link;     mb(); } 很简单,就是将QH从对应的调度队列中删除了. 对于控制/批量传输,调用unlink_async(): static void unlink_async(struct uhci_hcd *uhci, struct uhci_qh *qh) {     struct uhci_qh *pqh;     __le32 link_to_next_qh = qh->link;       pqh = list_entry(qh->node.prev, struct uhci_qh, node);     pqh->link = link_to_next_qh;       /* If this was the old first FSBR QH, link the terminating skeleton      * QH to the next (new first FSBR) QH. */      //如果要删除的是第一个FSBR指向的QH,也要更新skel_term_qh的指向     if (pqh->skel < SKEL_FSBR && qh->skel >= SKEL_FSBR)         uhci->skel_term_qh->link = link_to_next_qh;     mb(); } 以中断传输不相同的是,如果要删除的QH,是第一个使用FSBR的QH,还要更新skel_term_qh的值.   到这里这后,我们知道这些队列得不到调度了,因为已经从调度系统中删除了,它们都保存在skel_unlink_qh中. 在这里再次强调一下,在uhci_advance_check()中,只有使用了FSBR的QH才会被移到skel_unlink_qh中.也就是说,只有控制传输和批量传输才会做这样的处理.   uhci_advance_check()返回之后,如果返回值是1,则会进入到uhci_scan_qh().返回1有三种情况:一种是QH 被调度过了.这种情况下,就要释放掉已经被调度过了的TD.另外的一种情况是QH不为ACTIVE状态.最后的一种是QH为实时传输.我们跟踪看下它的处 理. 这个函数会涉及到urb->unlinked成员的判断,整个USB的代码中,只有usb_kill_urb()或者是usb_unlink_urb()才会更改rub->unlinked.这两个函数是用来销毁URB的.两个函数的代码如下所示: int usb_unlink_urb(struct urb *urb) {     if (!urb)         return -EINVAL;     if (!urb->dev)         return -ENODEV;     if (!urb->ep)         return -EIDRM;     return usb_hcd_unlink_urb(urb, -ECONNRESET); } void usb_kill_urb(struct urb *urb) {     static DEFINE_MUTEX(reject_mutex);       might_sleep();     if (!(urb && urb->dev && urb->ep))         return;     mutex_lock(&reject_mutex);     ++urb->reject;     mutex_unlock(&reject_mutex);       usb_hcd_unlink_urb(urb, -ENOENT);     wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);       mutex_lock(&reject_mutex);     --urb->reject;     mutex_unlock(&reject_mutex); } 可以看到,两个函数都会调用usb_hcd_unlink_urb().只是参数不相同. 在usb_hcd_unlink_urb()àunlink1()àusb_hcd_check_unlink_urb()里,最终会更新 urb->unlinked.最后会调用uhci_unlink_qh()从调度队列中删除.这些流程都很简单,在这里不做详细分析.可以自行参阅 代码. 从上面的流程分析: usb_unlink_urb()最终会将urb->unlinked设为-ECONNRESET. usb_kill_urb()最终会将urb->unlinked设为-ENOENT. 好了,现在可以跟踪进uhci_scan_qh()的代码了: static void uhci_scan_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) {     struct urb_priv *urbp;     struct urb *urb;     int status;       //取挂在qh->queue下的urbp     while (!list_empty(&qh->queue)) {         urbp = list_entry(qh->queue.next, struct urb_priv, node);         urb = urbp->urb;           //实时传输的传理         if (qh->type == USB_ENDPOINT_XFER_ISOC)             status = uhci_result_isochronous(uhci, urb);         else             //其它类型的处理             status = uhci_result_common(uhci, urb);         //如果urb没有传输完成,就会返回-EINPROGRESS         if (status == -EINPROGRESS)             break;   遍历挂在qh上的所有urb.如果urb全部正常的传输完了,uhci_result_ isochronous()和uhci_result_common()会返回0.否则,便返回-EINPROGRESS.说明这个urb中还有末传送完的TD.             //到这里的话,QH中的TD应该全被都传输完了         /* Dequeued but completed URBs can't be given back unless          * the QH is stopped or has finished unlinking. */         if (urb->unlinked) {             if (QH_FINISHED_UNLINKING(qh))                 qh->is_stopped = 1;             else if (!qh->is_stopped)                 return;         }           uhci_giveback_urb(uhci, qh, urb, status);         if (status < 0)             break; 如果运行到这个地方的话,说明URB已经全部都传输完成了. 1:如果urb->unlinked不为0.说明这个URB已经在其它的地方调用了usb_unlink_urb()或者是 usb_kill_urb(). 这几个函数都会调用uhci_unlink_qh()将qh移放到skel_unlink_qh中,同时也会将qh->unlink_frame设 置为移除时的帧号.因为,如果遇上这种情况的话,肯定是在遍历slel_unlink_qh上的QH.在这里出现了一个比较有意思的宏: QH_FINISHED_UNLINKING().定义如下所示:             #define QH_FINISHED_UNLINKING(qh)           \         (qh->state == QH_STATE_UNLINKING && \         uhci->frame_number + uhci->is_stopped != qh->unlink_frame) 这个宏的第一个条件QH必须是已经移除的.另外,uhci->is_stopped表示uhci的状态,为0是UHCI正常运行,为1说明UHCI已经停止了. 而我们在前面分析uhci_unlink_qh()可得知.qh->unlink_frame表示当时删除QH时的帧号.而uhci->frame_number每次经过中断的时候都会更新一次.因此,第二个条件如果要满足的话,可能要以下两种情况: 1): UHCI已经停止.(uhci->is_stopped为1,而uhci->frame_number在同一个周期内只会往大的方增长) 2):删除时候的帧不是当前运行的帧.这是因为,在当前调度的帧上,不能将它上面的QH删除,必须要等它调度完了之后才可以. 在QH_FINISHED_UNLINKING()不满足且qh->is_stopped为0的情况下,函数会直接退出.等下次中断进来 的时候再来处理这些QH. 关于qh->is_stopped字段.除了本函数之外,只有uhci_result_common()中,检测到传输发生错误的时候才会将其置为 1 之后,就可以将传输完成的URB调用uhci_giveback_urb()将其删除了       }       /* If the QH is neither stopped nor finished unlinking (normal case),      * our work here is done. */     if (QH_FINISHED_UNLINKING(qh))         qh->is_stopped = 1;     else if (!qh->is_stopped)         return; 这里的判断跟上面的是差不多的,如果满足了删除的QH的条件,就将qh->is_stopped置为1.然对于qh->is_stopped的情况,是会直接返回的. 那能够进入到下面的处理流程的,只有以下这种情况: 1:调用了usb_unlink_urb()/usb_kill_urb()的URB.且符合了删除的条件. 2:由uhci_giveback_urb()删除,且满足删除条件的QH 3:之前在uhci_advance_check()中传输超时加入到skel_unlink_qh的QH且满足删除条件. 上面所说的”删除条件”是指必须要在QH调度帧完了之后,才会删除QH       /* Otherwise give back each of the dequeued URBs */ restart:     list_for_each_entry(urbp, &qh->queue, node) {         urb = urbp->urb;         if (urb->unlinked) {               /* Fix up the TD links and save the toggles for              * non-Isochronous queues.  For Isochronous queues,              * test for too-recent dequeues. */             if (!uhci_cleanup_queue(uhci, qh, urb)) {                 qh->is_stopped = 0;                 return;             }             uhci_giveback_urb(uhci, qh, urb, 0);             goto restart;         }     } 处理上述分析中的第1种情况的urb的处理.     qh->is_stopped = 0;       /* There are no more dequeued URBs.  If there are still URBs on the      * queue, the QH can now be re-activated. */     if (!list_empty(&qh->queue)) {         if (qh->needs_fixup)             uhci_fixup_toggles(qh, 0);           /* If the first URB on the queue wants FSBR but its time          * limit has expired, set the next TD to interrupt on          * completion before reactivating the QH. */         urbp = list_entry(qh->queue.next, struct urb_priv, node);         if (urbp->fsbr && qh->wait_expired) {             struct uhci_td *td = list_entry(urbp->td_list.next,                     struct uhci_td, list);               td->status |= __cpu_to_le32(TD_CTRL_IOC);         }           uhci_activate_qh(uhci, qh);     } 对于上述分析的第3种情况的处理,这种情况下的QH,是可以重新安排它调度的     /* The queue is empty.  The QH can become idle if it is fully      * unlinked. */     else if (QH_FINISHED_UNLINKING(qh))         uhci_make_qh_idle(uhci, qh); 对于上述分析的第2种情况的处理. }   里面有几个很重要的子函数,有必要详细分析如下: 1: uhci_result_isochronous().代码如下: static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) {     struct uhci_td *td, *tmp;     struct urb_priv *urbp = urb->hcpriv;     struct uhci_qh *qh = urbp->qh;       list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {         unsigned int ctrlstat;         int status;         int actlength;           //还没有调度到实时传输         if (uhci_frame_before_eq(uhci->cur_iso_frame, qh->iso_frame))             return -EINPROGRESS;           //该队列已经被调度过了,可以将它删除了         uhci_remove_tds_from_frame(uhci, qh->iso_frame);           ctrlstat = td_status(td);         if (ctrlstat & TD_CTRL_ACTIVE) {             status = -EXDEV;    /* TD was added too late? */         } else {             status = uhci_map_status(uhci_status_bits(ctrlstat),                     usb_pipeout(urb->pipe));             actlength = uhci_actual_length(ctrlstat);               urb->actual_length += actlength;             qh->iso_packet_desc->actual_length = actlength;             qh->iso_packet_desc->status = status;         }         //如果发生了错误,更新错误计数         if (status)             urb->error_count++;         //将td从urbp->td_list上删除         uhci_remove_td_from_urbp(td);         //释放这个td         uhci_free_td(uhci, td);         //更新iso_frame         qh->iso_frame += qh->period;         ++qh->iso_packet_desc;     }     return 0; } 这个函数是对实时传输的QH的处理. qh->iso_frame在提交ISO URB的时候,是设置为了调度的起始帧号.在上面的这个函数中,每过一次循环, qh->iso_frame就加上一个周期(qh->period),即遍历TD所在的调度frame. Qh-> cur_iso_frame表示当前的调度帧号. 所以,只需要比较qh->iso_frame和qh->cur_iso_frame就可以知道当前调度队列中的iso TD是否已经被调度.对于那些已经被调度的TD,要断开它的链表,并释放它的空间. 从上面的代码中也可以看到,只要ISO中的TD还没传输完,就会返回-EINPROGRESS. 另外,提醒一下:对于挂在UHCI的1024个调度frame上的TD和挂在QH下的TD,UHCI对它们的处理是不相同的.对于挂在frame上的TD,UHCI处理完之后, UHCI处理完之后只会将其标记为INACITVE.(实时传输就这样的情况) 挂在QH中的TD,运行完了之后,会将其脱链. 这个函数比较简单,就不做详细分析了.   2: uhci_result_common()函数. 代码如下: static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) {     struct urb_priv *urbp = urb->hcpriv;     struct uhci_qh *qh = urbp->qh;     struct uhci_td *td, *tmp;     unsigned status;     int ret = 0;       //遍历挂在urbp下面的td     list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {         unsigned int ctrlstat;         int len;           //取td的状态         ctrlstat = td_status(td);         status = uhci_status_bits(ctrlstat);         //如果td还没有调度完成,退出. 不需要检查它后面的TD了.肯定也没有完成         if (status & TD_CTRL_ACTIVE)             return -EINPROGRESS;           //更新actual_length (实际传输的长度)         len = uhci_actual_length(ctrlstat);         urb->actual_length += len;         //如果TD传输发生错误,status不为0         if (status) {             ret = uhci_map_status(status,                     uhci_packetout(td_token(td)));             if ((debug == 1 && ret != -EPIPE) || debug > 1) {                 /* Some debugging code */                 dev_dbg(&urb->dev->dev,                         "%s: failed with status %x\n",                         __FUNCTION__, status);                   if (debug > 1 && errbuf) {                     /* Print the chain for debugging */                     uhci_show_qh(uhci, urbp->qh, errbuf,                             ERRBUF_LEN, 0);                     lprintk(errbuf);                 }             }           /* Did we receive a short packet? */         }         //如果收到了一个短包         else if (len < uhci_expected_length(td_token(td))) {               /* For control transfers, go to the status TD if              * this isn't already the last data TD */              //如果是控制传输.有三个阶段,即SETUP,DATA,HANDSHAKE.             //在这里判断是否是最后的一个DATA TD,如果是的话,短包是正常的             if (qh->type == USB_ENDPOINT_XFER_CONTROL) {                 if (td->list.next != urbp->td_list.prev)                     ret = 1;             }               /* For bulk and interrupt, this may be an error */             //如果设置了URB_SHORT_NOT_OK,不允许短包             else if (urb->transfer_flags & URB_SHORT_NOT_OK)                 ret = -EREMOTEIO;               /* Fixup needed only if this isn't the URB's last TD */             //对于非控制传输类型,判断是否是最后一个TD.最后一个TD短包是正常的             else if (&td->list != urbp->td_list.prev)                 ret = 1;         }         //TD传输完成了.可以将它移除了         uhci_remove_td_from_urbp(td);         //qh->post_td用来何存最近完成的TD         if (qh->post_td)             uhci_free_td(uhci, qh->post_td);         qh->post_td = td;           if (ret != 0)             goto err;     }     return ret;   err: //如果传输错误,则终止QH的传输     if (ret < 0) {         /* Note that the queue has stopped and save          * the next toggle value */         qh->element = UHCI_PTR_TERM;         qh->is_stopped = 1;         qh->needs_fixup = (qh->type != USB_ENDPOINT_XFER_CONTROL);         qh->initial_toggle = uhci_toggle(td_token(td)) ^                 (ret == -EREMOTEIO);       } else      /* Short packet received */         //对于短包错误,还要进行fixup         ret = uhci_fixup_short_transfer(uhci, qh, urbp);     return ret; } 对照上面的注释,这段代码应该很好懂.重点分析一下uhci_fixup_short_transfer(): static int uhci_fixup_short_transfer(struct uhci_hcd *uhci,         struct uhci_qh *qh, struct urb_priv *urbp) {     struct uhci_td *td;     struct list_head *tmp;     int ret;       td = list_entry(urbp->td_list.prev, struct uhci_td, list);     if (qh->type == USB_ENDPOINT_XFER_CONTROL) {           /* When a control transfer is short, we have to restart          * the queue at the status stage transaction, which is          * the last TD. */          //如果是控制传输,则需要传送最后一个handshake TD         WARN_ON(list_empty(&urbp->td_list));         qh->element = LINK_TO_TD(td);         tmp = td->list.prev;         ret = -EINPROGRESS;       } else {           /* When a bulk/interrupt transfer is short, we have to          * fix up the toggles of the following URBs on the queue          * before restarting the queue at the next URB. */         qh->initial_toggle = uhci_toggle(td_token(qh->post_td)) ^ 1;         uhci_fixup_toggles(qh, 1);           if (list_empty(&urbp->td_list))             td = qh->post_td;         qh->element = td->link;         tmp = urbp->td_list.prev;         ret = 0;     }       /* Remove all the TDs we skipped over, from tmp back to the start */     //删除tmp之前的td     while (tmp != &urbp->td_list) {         td = list_entry(tmp, struct uhci_td, list);         tmp = tmp->prev;           uhci_remove_td_from_urbp(td);         uhci_free_td(uhci, td);     }     return ret; } 对于控制传输和其它类型的传输的处理是不一样的,如果是控制传输,就算前面的数据传输错了,也不能将整个传输过程中断,必须要将最后的 handshake包传过去,如果是其它类型的传输,只是将此次传输终止掉就可以了.但是,按照usb2.0 spec规定的纠错方法,必须要修改后面发包的toggle值. 也许,有人会有这样的疑问: 对于控制传输,它不也是基于toggle的纠错么,为什么它就不需要修改后续的包的toggle值呢? 这是因为,控制传输的toggle都是从1开始的,删除掉当前的urb,也不会对后面的发包造成影响. 之后,处理完之后,将无用的td删除. 跟踪一下toggle的修正过程.对应的函数为uhci_fixup_toggles().如下所示: static void uhci_fixup_toggles(struct uhci_qh *qh, int skip_first) {     struct urb_priv *urbp = NULL;     struct uhci_td *td;     unsigned int toggle = qh->initial_toggle;     unsigned int pipe;       /* Fixups for a short transfer start with the second URB in the      * queue (the short URB is the first). */     if (skip_first)         urbp = list_entry(qh->queue.next, struct urb_priv, node);       /* When starting with the first URB, if the QH element pointer is      * still valid then we know the URB's toggles are okay. */     else if (qh_element(qh) != UHCI_PTR_TERM)         toggle = 2;       /* Fix up the toggle for the URBs in the queue.  Normally this      * loop won't run more than once: When an error or short transfer      * occurs, the queue usually gets emptied. */     urbp = list_prepare_entry(urbp, &qh->queue, node);     //从第二个URB开始遍历qh上的URB     list_for_each_entry_continue(urbp, &qh->queue, node) {           /* If the first TD has the right toggle value, we don't          * need to change any toggles in this URB */          //取挂在urb上的第一个TD         td = list_entry(urbp->td_list.next, struct uhci_td, list);         //如果下一个传输的URB的起始TD就是损坏包的toggle         if (toggle > 1 || uhci_toggle(td_token(td)) == toggle) {             //取此次URB传输的最后一个td             td = list_entry(urbp->td_list.prev, struct uhci_td,                     list);             //最后一个td取反             toggle = uhci_toggle(td_token(td)) ^ 1;           /* Otherwise all the toggles in the URB have to be switched */         } else {             //如果toggle不相符合,则依次给urbp中的td转换toggle             list_for_each_entry(td, &urbp->td_list, list) {                 td->token ^= __constant_cpu_to_le32(                             TD_TOKEN_TOGGLE);                 toggle ^= 1;             }         }     }     //将最后的toggle保存进usb device中     wmb();     pipe = list_entry(qh->queue.next, struct urb_priv, node)->urb->pipe;     usb_settoggle(qh->udev, usb_pipeendpoint(pipe),             usb_pipeout(pipe), toggle);     qh->needs_fixup = 0; } 在调用这个函数之前,有这样的设置: qh->initial_toggle = uhci_toggle(td_token(qh->post_td)) ^ 1; 即将qh->initial_toggle设置成为了发生错误的TD的toggle值的相反值.也就是继错误TD之后的数据包的toggle值. 由于调用这个函数的第二个参数为1.所以,会从qh的第二个urb开始遍历.如果URB的起始包的toggle与下一个包的toggle相同,则这个URB的toggle值不需要改变,否则,就需要挨个改变URB中的TD的toggle. 最后,还需要将最后的toggle值保存到usb_dev->toggle[]中.因为下次发包的时候,还要从这里面去取相应的toggle值做为当前发包的toggle.   第三个要分析的函数是uhci_giveback_urb().代码如下示: static void uhci_giveback_urb(struct uhci_hcd *uhci, struct uhci_qh *qh,         struct urb *urb, int status) __releases(uhci->lock) __acquires(uhci->lock) {     struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;       //urb->actual_length为负,说明传输失败     if (qh->type == USB_ENDPOINT_XFER_CONTROL) {           /* urb->actual_length < 0 means the setup transaction didn't          * complete successfully.  Either it failed or the URB was          * unlinked first.  Regardless, don't confuse people with a          * negative length. */         urb->actual_length = max(urb->actual_length, 0);     }       /* When giving back the first URB in an Isochronous queue,      * reinitialize the QH's iso-related members for the next URB. */      //如果是实时传输      //如果要删除的QH是第第一个实时传输的URB,则要修改qh的iso_frame和iso_packet_decket     else if (qh->type == USB_ENDPOINT_XFER_ISOC &&             urbp->node.prev == &qh->queue &&             urbp->node.next != &qh->queue) {         struct urb *nurb = list_entry(urbp->node.next,                 struct urb_priv, node)->urb;           qh->iso_packet_desc = &nurb->iso_frame_desc[0];         qh->iso_frame = nurb->start_frame;     }       /* Take the URB off the QH's queue.  If the queue is now empty,      * this is a perfect time for a toggle fixup. */      //将urbp从QH的链表上删除,如果QH是空的,修正toggle值     list_del_init(&urbp->node);     if (list_empty(&qh->queue) && qh->needs_fixup) {         usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),                 usb_pipeout(urb->pipe), qh->initial_toggle);         qh->needs_fixup = 0;     }       //释放urbp所占的所用空间,包括它的TD     uhci_free_urb_priv(uhci, urbp);     //从ep的链表上将urb删除     usb_hcd_unlink_urb_from_ep(uhci_to_hcd(uhci), urb);       spin_unlock(&uhci->lock);     usb_hcd_giveback_urb(uhci_to_hcd(uhci), urb, status);     spin_lock(&uhci->lock);       /* If the queue is now empty, we can unlink the QH and give up its      * reserved bandwidth. */      //如果QH为空,将qh断开,将QH占用的带宽释放     if (list_empty(&qh->queue)) {         uhci_unlink_qh(uhci, qh);         if (qh->bandwidth_reserved)             uhci_release_bandwidth(uhci, qh);     } } 在控制传输的过程中,是将urb->actual_length设为-8的,这样是为了跳过前面的SETUP过程的数据包.如果 SETUP阶段发生了错误,那么urb->actual_length将会是一个负值,所以,先要将urb->actual_length修 正为大于或者等于0的数. 如果要删除的URB是实时队列QH的第一个URB.那必须更新qh->iso_packet_desc和qh->iso_frame.使其指向有效的起始位置. 另外,如果QH是空的,然后qh->needs_fixup为1,就会在usb_dev->toggle[]修正一次 toggle.(为什么要这么做?这里先放一下,后面会有分析.)这和我们在上面的分析的uhci_fixup_short_transfer()是不同 的.也不会重复.因为uhci_fixup_short_transfer()处理完了之后,会将qh->needs_fixup值设为0.这个 if判断不会满足. 将uhci_result_common()中关于need_fixup的部份列出如下: static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) {         ......         ...... if (ret < 0) {         /* Note that the queue has stopped and save          * the next toggle value */         qh->element = UHCI_PTR_TERM;         qh->is_stopped = 1;         qh->needs_fixup = (qh->type != USB_ENDPOINT_XFER_CONTROL);         qh->initial_toggle = uhci_toggle(td_token(td)) ^                 (ret == -EREMOTEIO);       } else      /* Short packet received */         ret = uhci_fixup_short_transfer(uhci, qh, urbp);     return ret; } 其中,ret<0是表示传输错误的情况,而ret=1是表示短包错误的情况.而这两种情况有什么区别呢? 对于短包错误,接收是正常的,这时,接收方会回一个ACK.然后再”倒转”自己这边验证的toggle.因为接收方的验证toggle改变了,所以,错误包之后的所有包,都要符号它的验证条件. 而对于传输错误的包,接收方接收错误,例如CRC检测错误,这里给对方回一个ACK之后,不会更新本地验证的toggle值.因此,这种情况下 的后续包没有必要更改toggle值.但是对于-EREMOTEIO就不同了,这种情况是发生在设置了URB_SHORT_NOT_OK标志的情况下,这 种情况下,接收方的接包是正常,所以也是需要”倒转”toggle. 说到这里,可能有人又会产生一个疑问,对于短包错误的,会修正它后面URB的toggle值,对于传输错误的,就不需要了么? 不着急,在后面自然会看到. 返回到uhci_giveback_urb()中,继续断开URB的一些关联以及释放和它相关结构所占的空间,另外,还会在usb_hcd_giveback_urb()中调用urb-> complete()来唤醒等待URB传输完成的进程.特别注意到,如果QH中没有URB了,就需要将QH的带宽回收了. (!!!要等到QH为空再释放带宽么?为什么不是释放URB就释放它所占的带宽?) 其实,跟踪代码可以发现,QH只会在添加第一个URB的时候,才会计算保留带宽,也就是说,不管QH中添加了多少URB,它的所占带宽都是一样的. 对于中断传输来说,这一点很好理解,QH下面挂着TD,每调度一次QH只会调度一个TD,因此,就算QH下挂了再多的TD,也不会影响带宽. 而对于等时传输来说,它的TD是直接挂在UHCI的调度数组上,每个TD相连之后再联QH.因为后面添加实时TD不会计算带宽.这样,后面就算提交了再多的等时传输也是不会去的判断(等时+中断<90%),这样做是不是欠妥?     现在,就来解释一下上面提出的问题,即对于传输错误的toggle修正问题. 我们在前面看到,对于传输错误的URB,会将它所属的qh设为: qh->element = UHCI_PTR_TERM;         qh->is_stopped = 1;         qh->needs_fixup = (qh->type != USB_ENDPOINT_XFER_CONTROL);         qh->initial_toggle = uhci_toggle(td_token(td)) ^                 (ret == -EREMOTEIO);   其中,如果是控制传输的话,是不需要修正toggle的.在这里,要特别注意,将qh的element设为了UHCI_PTR_TERM.也就是说,这个QH是一个空的,它下面没有挂任何的TD.那其它的urb是怎么继续得到调度的呢? 在uhci_scan_qh()中,有这样一段代码: static void uhci_scan_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) {         ......         ...... if (!list_empty(&qh->queue)) {         if (qh->needs_fixup)             uhci_fixup_toggles(qh, 0);           /* If the first URB on the queue wants FSBR but its time          * limit has expired, set the next TD to interrupt on          * completion before reactivating the QH. */         urbp = list_entry(qh->queue.next, struct urb_priv, node);         if (urbp->fsbr && qh->wait_expired) {             struct uhci_td *td = list_entry(urbp->td_list.next,                     struct uhci_td, list);               td->status |= __cpu_to_le32(TD_CTRL_IOC);         }           uhci_activate_qh(uhci, qh);     } ...... } 如上代码所示,传输超时和URB传输错误的QH都会由它进行处理.如果qh->needs_fixup为了,调用uhci_fixup_toggles()修正它的toggle值. 如果是一个需要FSBR的URB,但又传输超时,设定下一个TD带IOC属性,这样,在下一次中断的时候,就又会启用FSBR了. Uhci_activate_qh()已经很熟悉了,我们在之前已经分析过. 就这样,QH又会被调度起来了. 不妨思考一下,对于传输超时的QH,为什么要先将它加到skel_unlink_qh.然后再重新加到调度队列呢?为什么对于传输错误的QH,要先将qh-> element设为UHCI_PTR_TERM.然后再加入调度队列呢? 对于传输超时,它先链接在skel_unlink_qh.那,必须要等到下个frame中断的的时候,才会将qh加回调度队列.调用 Uhci_activate_qh()将其加回调度队列的时候,是加到调度队列的末尾.( 为什么是下一个frame呢?注意代码中的QH_FINISHED_UNLINKING()操作) 对于传输错误的QH,它能在本次中断加回调度队列,但也是加到调度队列的末尾.(因为传输错误的时候,在uhci_result_common()会将is_stopped设为1) 由此可见: Linux采用,是一种缓时调度的机制,将传输错误或者是传输超时的QH,放到调度队列的末尾,显然,这样的机制对实时传输是不合适的,因此,在代码中对实现传输做了特殊处理.   虽然,在这里的修正必须要满足QH不为空的情况,当QH为空的情况,它的修正就是在上面分析的uhci_giveback_urb()中完成的.   第四个要分析的函数uhci_cleanup_queue(). static int uhci_cleanup_queue(struct uhci_hcd *uhci, struct uhci_qh *qh,         struct urb *urb) {     struct urb_priv *urbp = urb->hcpriv;     struct uhci_td *td;     int ret = 1;       /* Isochronous pipes don't use toggles and their TD link pointers      * get adjusted during uhci_urb_dequeue().  But since their queues      * cannot truly be stopped, we have to watch out for dequeues      * occurring after the nominal unlink frame. */      //必须要等它调度完了才能删除     if (qh->type == USB_ENDPOINT_XFER_ISOC) {         ret = (uhci->frame_number + uhci->is_stopped !=                 qh->unlink_frame);         goto done;     }       /* If the URB isn't first on its queue, adjust the link pointer      * of the last TD in the previous URB.  The toggle doesn't need      * to be saved since this URB can't be executing yet. */      //如果不是QH中的第一个urb     if (qh->queue.next != &urbp->node) {         struct urb_priv *purbp;         struct uhci_td *ptd;           //urb的前一个urb         purbp = list_entry(urbp->node.prev, struct urb_priv, node);         WARN_ON(list_empty(&purbp->td_list));         //URB的前面urb中的最后一个TD         ptd = list_entry(purbp->td_list.prev, struct uhci_td,                 list);         //URB的最后一个TD         td = list_entry(urbp->td_list.prev, struct uhci_td,                 list);         //跳过urb的td项         ptd->link = td->link;         goto done;     }       //后面的处理,对应要删除的urb是qh上的第一个urb     //因此不要管它的链接情况,不过要更新usb_dev的toggle     /* If the QH element pointer is UHCI_PTR_TERM then then currently      * executing URB has already been unlinked, so this one isn't it. */      //如果qh->element等于UHCI_PTR_TERM.说明QH下面没有链接TD了     if (qh_element(qh) == UHCI_PTR_TERM)         goto done;     qh->element = UHCI_PTR_TERM;       /* Control pipes don't have to worry about toggles */     //如果是控制传输,不需要更新toggle     if (qh->type == USB_ENDPOINT_XFER_CONTROL)         goto done;       /* Save the next toggle value */     //initial_toggle更新为qh中起始td的toggle值.     //因为是要删除qh中的td.因此,qh之后的td因为要以这个toggle为准值     WARN_ON(list_empty(&urbp->td_list));     td = list_entry(urbp->td_list.next, struct uhci_td, list);     qh->needs_fixup = 1;     qh->initial_toggle = uhci_toggle(td_token(td));   done:     return ret; } 这个函数比较简单,对照添加的注释自行阅读即可. 不过要注意,这个函数也涉及到了toggle修正.在后面也会经过上面分析的toggle修正的流程(传输错误的QH的toggle修正部份). 疑问:为什么不是QH中的第一个URB就不需要修正toggle?   最后要分析的函数是uhci_make_qh_idle(). static void uhci_make_qh_idle(struct uhci_hcd *uhci, struct uhci_qh *qh) {     //如果QH依然是ACTIVE状态,非法...     WARN_ON(qh->state == QH_STATE_ACTIVE);     //如果要删除的QH是uhci->next_qh.则更新uhci->next_qh     if (qh == uhci->next_qh)         uhci->next_qh = list_entry(qh->node.next, struct uhci_qh,                 node);     //将QH移到uhci->idle_qh_list链表上     list_move(&qh->node, &uhci->idle_qh_list);     //将QH的状态更改为IDLE     qh->state = QH_STATE_IDLE;       /* Now that the QH is idle, its post_td isn't being used */     //现在这个QH已经没有什么用处了,如果还有一个缓冲的TD,要将其释放     if (qh->post_td) {         uhci_free_td(uhci, qh->post_td);         qh->post_td = NULL;     }       /* If anyone is waiting for a QH to become idle, wake them up */     //如果有进程在进程QH,将他们唤醒...     if (uhci->num_waiting)         wake_up_all(&uhci->waitqh); } 当QH空闲,QH也就可以完全释放了.有人或许有疑问,这个函数处理过后,QH只是回到了初始状态,并没有将QH所占空间释放.那他是在什么时候释放的呢? 首先,考虑一下,每个端点的传输类型都是相同的,因此对应每个端点,它的QH也是同一种类型,因此,以后的数据传输就会复用这个QH(参考usb2.0 spec上的端口描述符的bmAttribtes字段). 那QH要等到usb_hcd_disable_endpoint()的时候才会将其删除. 经过这样一个漫长的过程,UHCI的中断处理终于到此结束了.   五:关于复用的QH 在上面提到了第一次传输后的QH会被以后的传输复用,这部份的代码在前面的情景中都没有涉及到,现在把它串起来研究一下. 首先在uhci_urb_enqueue()中,有如下代码片段 : static int uhci_urb_enqueue(struct usb_hcd *hcd,         struct urb *urb, gfp_t mem_flags) {     ......     ...... 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;     list_add_tail(&urbp->node, &qh->queue);     if (qh->queue.next == &urbp->node && !qh->is_stopped) {         uhci_activate_qh(uhci, qh);         uhci_urbp_wants_fsbr(uhci, urbp);     }     goto done;     ......     ...... } 首先来看代码片段中的第一段 : 上一次数据传输之后,并没有将QH释放,相应的,QH仍然放置在ep->hcpriv,即传输端点的hcprive字段. 因此代码片段中的if是会满足的,也就是说会找到上次传输的QH,而不是新建一个.强调一句,还有一种情况是,上次提交的urb还没处理完成,相应ep上又提交了一个urb.这里也会找到这个相同的QH 其次,在代码片段的第二段: 它将urbp链接在qh->queue中,然后,判断urbp是是否是qh中的第一个元素,如果是,才会满足if判断,继而将QH激活.这是因为,如果urbp不是它的第一个元素的话,QH已经处理调度状态了,不需要再次激活. 有了一个大概的了解之后,分别来看一下各种操作的QH复用. 5.1:中断传输的QH复用 在uhci_submit_control()中,有如下代码片段: static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,         struct uhci_qh *qh) {     ......     ...... td = qh->dummy_td;     uhci_add_td_to_urbp(td, urbp);     uhci_fill_td(td, status, destination | uhci_explen(8),             urb->setup_dma);     plink = &td->link;     status |= TD_CTRL_ACTIVE;     ...... } 经过前面的分析可以得到,qh->dummy_td其实就是存放链接在QH上的最后一个TD,这样,urbp的对应TD直接从dummy_td开始,相应的,这些TD链接在QH的调度链表上.   5.2:批量传输的QH复用 在uhci_submit_bulk()àuhci_submit_common()中,有如下代码片段: static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,         struct uhci_qh *qh) {     ......     ...... td = qh->dummy_td;     do {    /* Allow zero length packets */         int pktsze = maxsze;           if (len <= pktsze) {        /* The last packet */             pktsze = len;             if (!(urb->transfer_flags & URB_SHORT_NOT_OK))                 status &= ~TD_CTRL_SPD;         }           if (plink) {             td = uhci_alloc_td(uhci);             if (!td)                 goto nomem;             *plink = LINK_TO_TD(td);         }         uhci_add_td_to_urbp(td, urbp);         uhci_fill_td(td, status,                 destination | uhci_explen(pktsze) |                     (toggle << TD_TOKEN_TOGGLE_SHIFT),                 data);         plink = &td->link;         status |= TD_CTRL_ACTIVE;           data += pktsze;         len -= maxsze;         toggle ^= 1;     } while (len > 0); ...... ...... } 同上面分析的中断传输情况类型,urbp的TD也是从qh->dummy_td开始存放的,相应的,也是位于QH的调度链表中.   5.3:中断传输的QH复用 在uhci_submit_interrupt()中,有以下代码片段: static int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb,         struct uhci_qh *qh) {     ......     ...... if (!qh->bandwidth_reserved) {         int exponent;        for (exponent = 7; exponent >= 0; --exponent) {             if ((1 << exponent) <= urb->interval)                 break;         }         if (exponent < 0)             return -EINVAL;         qh->period = 1 << exponent;         qh->skel = SKEL_INDEX(exponent);         qh->phase = (qh->period / 2) & (MAX_PHASE - 1);         ret = uhci_check_bandwidth(uhci, qh);         if (ret)             return ret;     } else if (qh->period > urb->interval)         return -EINVAL;     /* Can't decrease the period */     ret = uhci_submit_common(uhci, urb, qh);     ......     ...... } 对于QH复用的情况,前面的if判断中, 1:如果在QH中还有传输待传输的urb的时候是不会满足的,因为前面的urb已经分配了带宽,会将qh->bandwidth_reserved置为1. 2:如果QH中没有要传输的urb,也就是说QH已经空了,这个if判断还是会满足,因为会在uhci_giveback_urb()将它所占的带宽释放,重置qh->bandwidth_reserved为0.   对于第1种情况,它会接着去判断qh->period > urb->interva是否满足,如果满足此条件,就会直接退出.也就是说,如果调度间隔要小于urb指定的间隔,这是允许的,那如果调度的间隔 要大于urb指定的间隔,就是非法了.这在usb2.0 spec上有详细的描述.然后,流程转入uhci_submit_common(). 对于第2种情况,还是去判断带宽是否满足,然后分得带宽,最后流程转入uhci_submit_common().   uhci_submit_common()这个函数在QH复用时的差别已经在上面分析了,这里不做赘述. 这里再强调一次,在QH中已经有待调度的urb的情况下,是不会再去计算占用带宽的.   5.4:实时传输的QH复用 在uhci_submit_isochronous()中,有以下代码片段 : static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,         struct uhci_qh *qh) {     ......     ......     if (!qh->bandwidth_reserved) {         ......     } else if (qh->period != urb->interval) {         return -EINVAL;     /* Can't change the period */       } else {         /* Find the next unused frame */         if (list_empty(&qh->queue)) {             frame = qh->iso_frame;         } else {             struct urb *lurb;               lurb = list_entry(qh->queue.prev,                     struct urb_priv, node)->urb;             frame = lurb->start_frame +                     lurb->number_of_packets *                     lurb->interval;         }         if (urb->transfer_flags & URB_ISO_ASAP) {             /* Skip some frames if necessary to insure              * the start frame is in the future.              */             uhci_get_current_frame_number(uhci);             if (uhci_frame_before_eq(frame, uhci->frame_number)) {                 frame = uhci->frame_number + 1;                 frame += ((qh->phase - frame) &                     (qh->period - 1));             }         }   /* Otherwise pick up where the last URB leaves off */         urb->start_frame = frame;     }     ......     ...... } 在这里,跟上面分析的中断传输的情况类似,也有两种情况.一种是QH为空时,重新计算带宽.另外一种是QH中有待调度的urbp,这时不要计算带宽,流程会经过后面的elseif ...else中. 等时传输比控制传输要严格多了,必须要调度间隔完全相同才可以. 重要的操作就是在后面的这个else中了. 如果QH是空的,基准frame为下次会调度的frame值(qh->iso_frame的值的改变在 uhci_result_isochronous()中已经分析过了,当这个函数运行完了之后,qh->iso_frame表示下次将要调用qh的 帧号),如果QH不是空的,那么,基准frame值就是紧接在QH中最后的TD的下一个调度位置(注意 list_entry(qh->queue.prev,struct urb_priv, node)->urb取得的是挂在QH上的最后一个QH). 计算出这个基准frame之后,如果没有带URB_ISO_ASAP标志,那么该urb的起点调度帧号就是上面计算出来的基准frame. 如果带了URB_ISO_ASAP.表示要尽快调度这个URB.此时就会取当前调度帧+1后的第一个周期点.当前调度帧+1是为了给现在操作足够的时间来完成.   六:小结 在这一节里,对UHCI驱动和USB数据传输做了一个全面的分析.代码很复杂,不过,在阅读代码围绕着UHCI调度架构这一条主线,各种操作就会变得很明朗.

转载于:https://www.cnblogs.com/sdphome/archive/2011/09/29/2195797.html


最新回复(0)