Linux那些事儿之我是UHCI(21)传说中的中断服务程序(ISR)

来源:互联网 发布:2017新款男鞋推荐知乎 编辑:程序博客网 时间:2024/05/18 09:18

想当年咱们在usb_add_hcd中使用request_irq注册了中断函数,写代码的人做每件事情都是费尽心机的,为了达到目的不择手段,他们绝不是雷锋,他们每做一件事情都是有着极强的功利心态的,每注册一个函数都是为了日后能够利用该函数,当初注册了usb_hcd_irq,这会儿就该调用这个函数了.这个函数来自drivers/usb/core/hcd.c:

   1422 /**

   1423  * usb_hcd_irq - hook IRQs to HCD framework (bus glue)

   1424  * @irq: the IRQ being raised

   1425  * @__hcd: pointer to the HCD whose IRQ is being signaled

   1426  * @r: saved hardware registers

   1427  *

   1428  * If the controller isn't HALTed, calls the driver's irq handler.

   1429  * Checks whether the controller is now dead.

   1430  */

   1431 irqreturn_t usb_hcd_irq (int irq, void *__hcd)

   1432 {

   1433         struct usb_hcd          *hcd = __hcd;

   1434         int                     start = hcd->state;

   1435

   1436         if (unlikely(start == HC_STATE_HALT ||

   1437             !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))

   1438                 return IRQ_NONE;

   1439         if (hcd->driver->irq (hcd) == IRQ_NONE)

   1440                 return IRQ_NONE;

   1441

   1442         set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);

   1443

   1444         if (unlikely(hcd->state == HC_STATE_HALT))

   1445                 usb_hc_died (hcd);

   1446         return IRQ_HANDLED;

   1447 }

对于uhci来说,driver->irq就是uhci_irq()函数.来自drivers/usb/host/uhci-hcd.c:

    377 static irqreturn_t uhci_irq(struct usb_hcd *hcd)

    378 {

    379         struct uhci_hcd *uhci = hcd_to_uhci(hcd);

    380         unsigned short status;

    381         unsigned long flags;

    382

    383         /*

    384          * Read the interrupt status, and write it back to clear the

    385          * interrupt cause.  Contrary to the UHCI specification, the

    386          * "HC Halted" status bit is persistent: it is RO, not R/WC.

    387          */

    388         status = inw(uhci->io_addr + USBSTS);

    389         if (!(status & ~USBSTS_HCH))    /* shared interrupt, not mine */

    390                 return IRQ_NONE;

    391         outw(status, uhci->io_addr + USBSTS);           /* Clear it */

    392

    393         if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) {

    394                 if (status & USBSTS_HSE)

    395                         dev_err(uhci_dev(uhci), "host system error, "

    396                                         "PCI problems?/n");

    397                 if (status & USBSTS_HCPE)

    398                         dev_err(uhci_dev(uhci), "host controller process "

    399                                         "error, something bad happened!/n");

    400                 if (status & USBSTS_HCH) {

    401                         spin_lock_irqsave(&uhci->lock, flags);

    402                         if (uhci->rh_state >= UHCI_RH_RUNNING) {

    403                                 dev_err(uhci_dev(uhci),

    404                                         "host controller halted, "

    405                                         "very bad!/n");

    406                                 if (debug > 1 && errbuf) {

    407                                         /* Print the schedule for debugging */

    408                                         uhci_sprint_schedule(uhci,

    409                                                         errbuf, ERRBUF_LEN);

    410                                         lprintk(errbuf);

    411                                 }

    412                                 uhci_hc_died(uhci);

    413

    414                                 /* Force a callback in case there are

    415                                  * pending unlinks */

    416                                 mod_timer(&hcd->rh_timer, jiffies);

    417                         }

418                         spin_unlock_irqrestore(&uhci->lock, flags);

    419                 }

    420         }

    421

    422         if (status & USBSTS_RD)

    423                 usb_hcd_poll_rh_status(hcd);

    424         else {

    425                 spin_lock_irqsave(&uhci->lock, flags);

    426                 uhci_scan_schedule(uhci);

    427                 spin_unlock_irqrestore(&uhci->lock, flags);

    428         }

    429

    430         return IRQ_HANDLED;

    431 }

USBSTS就是UHCI的状态寄存器,USBSTS_USBINT标志状态寄存器的bit0,按照UHCI spec的规定,bit0对应于IOC.USBSTS_ERROR对应于bit 1,这一位如果为1,表示传输出现了错误,USBSTS_RD则对应于bit2,RD就是Resume Detect的意思,主机控制器在收到”RESUME”的信号的时候会把这一位设置为1.所以我们很快就知道我们应该关注的就是426这么一行代码,uhci_scan_schedule这个最熟悉的陌生人.

当我们再一次踏入uhci_scan_schedule的时候,曾经那段被我们飘过的while循环现在就不得不面对了.uhci_advance_check会被调用,它来自drivers/usb/host/uhci-q.c:

   1626 /*

   1627  * Check for queues that have made some forward progress.

   1628  * Returns 0 if the queue is not Isochronous, is ACTIVE, and

   1629  * has not advanced since last examined; 1 otherwise.

   1630  *

   1631  * Early Intel controllers have a bug which causes qh->element sometimes

   1632  * not to advance when a TD completes successfully.  The queue remains

   1633  * stuck on the inactive completed TD.  We detect such cases and advance

   1634  * the element pointer by hand.

   1635  */

   1636 static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh)

   1637 {

   1638         struct urb_priv *urbp = NULL;

   1639         struct uhci_td *td;

   1640         int ret = 1;

   1641         unsigned status;

   1642

   1643         if (qh->type == USB_ENDPOINT_XFER_ISOC)

   1644                 goto done;

   1645

   1646         /* Treat an UNLINKING queue as though it hasn't advanced.

   1647          * This is okay because reactivation will treat it as though

   1648          * it has advanced, and if it is going to become IDLE then

   1649          * this doesn't matter anyway.  Furthermore it's possible

   1650          * for an UNLINKING queue not to have any URBs at all, or

   1651          * for its first URB not to have any TDs (if it was dequeued

   1652          * just as it completed).  So it's not easy in any case to

   1653          * test whether such queues have advanced. */

   1654         if (qh->state != QH_STATE_ACTIVE) {

   1655                 urbp = NULL;

   1656                 status = 0;

   1657

   1658         } else {

   1659                 urbp = list_entry(qh->queue.next, struct urb_priv, node);

   1660                 td = list_entry(urbp->td_list.next, struct uhci_td, list);

   1661                 status = td_status(td);

   1662                 if (!(status & TD_CTRL_ACTIVE)) {

   1663

   1664                         /* We're okay, the queue has advanced */

   1665                         qh->wait_expired = 0;

   1666                         qh->advance_jiffies = jiffies;

   1667                         goto done;

   1668                 }

   1669                 ret = 0;

   1670         }

   1671

   1672         /* The queue hasn't advanced; check for timeout */

   1673         if (qh->wait_expired)

   1674                 goto done;

   1675

   1676         if (time_after(jiffies, qh->advance_jiffies + QH_WAIT_TIMEOUT)) {

   1677

   1678                 /* Detect the Intel bug and work around it */

   1679                 if (qh->post_td && qh_element(qh) == LINK_TO_TD(qh->post_td)) {

   1680                         qh->element = qh->post_td->link;

   1681                         qh->advance_jiffies = jiffies;

   1682                         ret = 1;

   1683                         goto done;

   1684                 }

   1685

   1686                 qh->wait_expired = 1;

   1687

   1688                 /* If the current URB wants FSBR, unlink it temporarily

   1689                  * so that we can safely set the next TD to interrupt on

   1690                  * completion.  That way we'll know as soon as the queue

   1691                  * starts moving again. */

   1692                 if (urbp && urbp->fsbr && !(status & TD_CTRL_IOC))

   1693                         uhci_unlink_qh(uhci, qh);

   1694

   1695         } else {

   1696                 /* Unmoving but not-yet-expired queues keep FSBR alive */

   1697                 if (urbp)

   1698                         uhci_urbp_wants_fsbr(uhci, urbp);

   1699         }

   1700

   1701 done:

   1702         return ret;

   1703 }

urbp中的td_list里面取出一个td,读取它的状态,我们最初是设置了TD_CTRL_ACTIVE,如果一个td被执行完了,主机控制器会把它的TD_CTRL_ACTIVE给取消掉.所以这里1662行判断,如果已经没有了TD_CTRL_ACTIVE,说明这个TD已经被执行完了,于是咱们执行goto语句跳出去,从而uhci_advance_check函数就返回了,对于这种情况,返回值为1.uhci_advance_check顾名思义,就是检查咱们的队列有没有前进,如果一个TDACTIVE变成了非ACTIVE,这就说明队列前进了,因为主机控制器只有执行完一个TD才会把一个TDACTIVE取消,然后它就会前进去获取下一个QH或者TD.

而如果uhci_advance_check返回了1,那么接下来uhci_scan_qh会被调用,它来自drivers/usb/host/uhci-q.c:

   1536 static void uhci_scan_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)

   1537 {

   1538         struct urb_priv *urbp;

   1539         struct urb *urb;

   1540         int status;

   1541

   1542         while (!list_empty(&qh->queue)) {

   1543                 urbp = list_entry(qh->queue.next, struct urb_priv, node);

   1544                 urb = urbp->urb;

   1545

   1546                 if (qh->type == USB_ENDPOINT_XFER_ISOC)

   1547                         status = uhci_result_isochronous(uhci, urb);

   1548                 else

   1549                         status = uhci_result_common(uhci, urb);

   1550                 if (status == -EINPROGRESS)

   1551                         break;

   1552

   1553                 spin_lock(&urb->lock);

   1554                 if (urb->status == -EINPROGRESS)        /* Not dequeued */

   1555                         urb->status = status;

   1556                 else

   1557                         status = ECONNRESET;            /* Not -ECONNRESET */

   1558                 spin_unlock(&urb->lock);

   1559

   1560                 /* Dequeued but completed URBs can't be given back unless

   1561                  * the QH is stopped or has finished unlinking. */

   1562                 if (status == ECONNRESET) {

   1563                         if (QH_FINISHED_UNLINKING(qh))

   1564                                 qh->is_stopped = 1;

   1565                         else if (!qh->is_stopped)

   1566                                 return;

   1567                 }

   1568

   1569                 uhci_giveback_urb(uhci, qh, urb);

   1570                 if (status < 0 && qh->type != USB_ENDPOINT_XFER_ISOC)

   1571                         break;

   1572         }

   1573

   1574         /* If the QH is neither stopped nor finished unlinking (normal case),

   1575          * our work here is done. */

   1576         if (QH_FINISHED_UNLINKING(qh))

   1577                 qh->is_stopped = 1;

   1578         else if (!qh->is_stopped)

   1579                 return;

   1580

   1581         /* Otherwise give back each of the dequeued URBs */

   1582 restart:

   1583         list_for_each_entry(urbp, &qh->queue, node) {

   1584                 urb = urbp->urb;

   1585                 if (urb->status != -EINPROGRESS) {

   1586

   1587                         /* Fix up the TD links and save the toggles for

   1588                          * non-Isochronous queues.  For Isochronous queues,

   1589                          * test for too-recent dequeues. */

   1590                         if (!uhci_cleanup_queue(uhci, qh, urb)) {

   1591                                 qh->is_stopped = 0;

   1592                                 return;

   1593                         }

   1594                         uhci_giveback_urb(uhci, qh, urb);

   1595                         goto restart;

   1596                 }

   1597         }

   1598         qh->is_stopped = 0;

   1599

   1600         /* There are no more dequeued URBs.  If there are still URBs on the

   1601          * queue, the QH can now be re-activated. */

   1602         if (!list_empty(&qh->queue)) {

   1603                 if (qh->needs_fixup)

   1604                         uhci_fixup_toggles(qh, 0);

   1605

   1606                 /* If the first URB on the queue wants FSBR but its time

   1607                  * limit has expired, set the next TD to interrupt on

   1608                  * completion before reactivating the QH. */

   1609                 urbp = list_entry(qh->queue.next, struct urb_priv, node);

   1610                 if (urbp->fsbr && qh->wait_expired) {

   1611                         struct uhci_td *td = list_entry(urbp->td_list.next,

   1612                                         struct uhci_td, list);

   1613

   1614                         td->status |= __cpu_to_le32(TD_CTRL_IOC);

   1615                 }

   1616

   1617                 uhci_activate_qh(uhci, qh);

   1618         }

   1619

   1620         /* The queue is empty.  The QH can become idle if it is fully

   1621          * unlinked. */

   1622         else if (QH_FINISHED_UNLINKING(qh))

   1623                 uhci_make_qh_idle(uhci, qh);

   1624 }

可以看到,不管是控制传输还是Bulk传输,下一个被调用的函数都是uhci_result_common(),来自drivers/usb/host/uhci-q.c:

   1148 /*

   1149  * Common result for control, bulk, and interrupt

   1150  */

   1151 static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)

   1152 {

   1153         struct urb_priv *urbp = urb->hcpriv;

   1154         struct uhci_qh *qh = urbp->qh;

   1155         struct uhci_td *td, *tmp;

   1156         unsigned status;

   1157         int ret = 0;

   1158

   1159         list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {

   1160                 unsigned int ctrlstat;

   1161                 int len;

   1162

   1163                 ctrlstat = td_status(td);

   1164                 status = uhci_status_bits(ctrlstat);

   1165                 if (status & TD_CTRL_ACTIVE)

   1166                         return -EINPROGRESS;

   1167

   1168                 len = uhci_actual_length(ctrlstat);

   1169                 urb->actual_length += len;

   1170

   1171                 if (status) {

   1172                         ret = uhci_map_status(status,

   1173                                         uhci_packetout(td_token(td)));

   1174                         if ((debug == 1 && ret != -EPIPE) || debug > 1) {

   1175                                 /* Some debugging code */

   1176                                 dev_dbg(&urb->dev->dev,

   1177                                                 "%s: failed with status %x/n",

   1178                                                 __FUNCTION__, status);

   1179

   1180                                 if (debug > 1 && errbuf) {

   1181                                         /* Print the chain for debugging */

   1182                                         uhci_show_qh(uhci, urbp->qh, errbuf,

   1183                                                         ERRBUF_LEN, 0);

   1184                                         lprintk(errbuf);

   1185                                 }

   1186                         }

   1187

   1188                 } else if (len < uhci_expected_length(td_token(td))) {

   1189

   1190                         /* We received a short packet */

   1191                         if (urb->transfer_flags & URB_SHORT_NOT_OK)

   1192                                 ret = -EREMOTEIO;

   1193

   1194                         /* Fixup needed only if this isn't the URB's last TD */

   1195                         else if (&td->list != urbp->td_list.prev)

   1196                                 ret = 1;

   1197                 }

   1198

   1199                 uhci_remove_td_from_urbp(td);

   1200                 if (qh->post_td)

   1201                         uhci_free_td(uhci, qh->post_td);

   1202                 qh->post_td = td;

   1203

   1204                 if (ret != 0)

   1205                         goto err;

   1206         }

   1207         return ret;

   1208

   1209 err:

   1210         if (ret < 0) {

   1211                 /* In case a control transfer gets an error

   1212                  * during the setup stage */

   1213                 urb->actual_length = max(urb->actual_length, 0);

   1214

   1215                 /* Note that the queue has stopped and save

   1216                  * the next toggle value */

   1217                 qh->element = UHCI_PTR_TERM;

   1218                 qh->is_stopped = 1;

   1219                 qh->needs_fixup = (qh->type != USB_ENDPOINT_XFER_CONTROL);

   1220                 qh->initial_toggle = uhci_toggle(td_token(td)) ^

   1221                                 (ret == -EREMOTEIO);

   1222

   1223         } else          /* Short packet received */

   1224                 ret = uhci_fixup_short_transfer(uhci, qh, urbp);

   1225         return ret;

   1226 }

首先list_for_each_entry_safe就相当于传说中的list_for_each_entry,只不过戴了一个安全套而已,其作用都是遍历urbptd_list,一个一个td的处理.

1163,td_status是一个很简单的宏,来自drivers/usb/host/uhci-hcd.h:

    258 /*

    259  * We need a special accessor for the control/status word because it is

    260  * subject to asynchronous updates by the controller.

    261  */

    262 static inline u32 td_status(struct uhci_td *td) {

    263         __le32 status = td->status;

    264

    265         barrier();

    266         return le32_to_cpu(status);

    267 }

其实就是获取struct uhci_td结构体指针的status成员.

uhci_status_bits亦是来自同一个文件中的宏:

    204 #define uhci_status_bits(ctrl_sts)      ((ctrl_sts) & 0xF60000)

要看懂这个宏需要参考下面这幅图:

这就是UHCI spec中对TD的结构体的定义,我们注意到它有四个DWORD,而咱们的uhci_td中的成员status实际上指的是这里的04-07h这整个双字,而我们注意到这幅图中04-07h这个双字中,bit16bit23那一段被称为Status,即这几位表示的是状态,uhci_status_bits则是为了获得这几个bits,ctrl_sts0xF60000相与得到的是bit17bit23,因为UHCI spec中规定了bit16是保留位,没啥意义.

这其中,bit 23被称为Active,其实它就是我们一直说的那个TD_CTRL_ACTIVE.如果这一位还设置了那么就说明这个TD还是活的,那么就不去碰它.如果没有设置,那么继续往下走.

下一个宏是,uhci_actual_length,依然是来自drivers/usb/host/uhci-hcd.h:

    205 #define uhci_actual_length(ctrl_sts)    (((ctrl_sts) + 1) & /

    206                         TD_CTRL_ACTLEN_MASK)    /* 1-based */

这里TD_CTRL_ACTLEN_MASK0x7FF,我们注意到TD的定义中,04-07h,bit0bit10这么个11bits被称之为ActLen,这个field是由主机控制器来写的,表示实际传输了多少个bytes,它被以n-1的方式进行了编码,所以这里咱们解码就要加1.然后咱们在usb-storage那个故事中看到的urb->actual_length就是这么计算出来的,即每次处理一个TD就加上len.顺便提一下,我们注意到在uhci_submit_control中我们设置了urb->actual_length-8,实际上写代码的哥们儿真实意图是希望用urb->actual_length小于0来表示控制传输的setup阶段没能取得成功,至于它具体是负多少并不重要,取为负8只是图个吉利.

如果一切正常的话,status实际上应该是0.不为0就表示出错了.1171这一段就是为错误打印一些调试信息.咱们就不看了.

1188,如果虽然没有啥异常状态,但是len比期望值要小,那么首先判断是不是在urbtransfer_flags中设置了URB_SHORT_NOT_OK,如果设置了,那就返回汇报错误.如果没有设置,继续判断,看看这个td是不是咱们整个队伍中最后一个td,如果不是,那么就有问题,设置返回值为1.

1199,既然td完成了使命,那么我们就可以过河拆桥卸磨杀驴了.

1200,qh->post_td咱们第一次见,它当然是空的.如果不为空就调用uhci_free_td来释放它.struct uhci_qh结构体中的成员post_td是用来纪录刚刚完成了的那个td.它的赋值恰恰就是在1202这一行,即令qh->post_td等于现在这个td,因为这个td就是刚刚完成的td.

正常的话,应该返回0.如果不正常,那就跳到1209下面去.

如果ret小于0,则需要对qh的一些成员进行赋值.

如果ret不小于0,实际上就是对应于刚才那个ret1的情况,即传输长度小于预期长度,这种情况就调用uhci_fixup_short_transfer()这个专门为此而设计的函数.来自drivers/usb/host/uhci-q.c:

   1101 /*

   1102  * Fix up the data structures following a short transfer

   1103  */

   1104 static int uhci_fixup_short_transfer(struct uhci_hcd *uhci,

   1105                 struct uhci_qh *qh, struct urb_priv *urbp)

   1106 {

   1107         struct uhci_td *td;

   1108         struct list_head *tmp;

   1109         int ret;

   1110

   1111         td = list_entry(urbp->td_list.prev, struct uhci_td, list);

   1112         if (qh->type == USB_ENDPOINT_XFER_CONTROL) {

   1113

   1114                 /* When a control transfer is short, we have to restart

   1115                  * the queue at the status stage transaction, which is

   1116                  * the last TD. */

   1117                 WARN_ON(list_empty(&urbp->td_list));

   1118                 qh->element = LINK_TO_TD(td);

   1119                 tmp = td->list.prev;

   1120                 ret = -EINPROGRESS;

   1121

   1122         } else {

   1123

   1124                 /* When a bulk/interrupt transfer is short, we have to

   1125                  * fix up the toggles of the following URBs on the queue

   1126                  * before restarting the queue at the next URB. */

   1127                 qh->initial_toggle = uhci_toggle(td_token(qh->post_td)) ^ 1;

   1128                 uhci_fixup_toggles(qh, 1);

   1129

   1130                 if (list_empty(&urbp->td_list))

   1131                         td = qh->post_td;

   1132                 qh->element = td->link;

   1133                 tmp = urbp->td_list.prev;

   1134                 ret = 0;

   1135         }

   1136

   1137         /* Remove all the TDs we skipped over, from tmp back to the start */

   1138         while (tmp != &urbp->td_list) {

   1139                 td = list_entry(tmp, struct uhci_td, list);

   1140                 tmp = tmp->prev;

   1141

   1142                 uhci_remove_td_from_urbp(td);

   1143                 uhci_free_td(uhci, td);

   1144         }

   1145         return ret;

   1146 }

这里对于控制传输和对于Bulk传输有着不同的处理方法.

如果是控制传输,那么令tmp等于本urbtd_list中的倒数第二个td,然后一个一个往前走,见一个删一个.并且把ret设置为-EINPROGRESS然后返回ret,这样做的后果就是留下了最后一个td,而其它的td统统撤了.而对于控制传输,我们知道其最后一个td就是状态阶段的td.

而对于Bulk传输或者中断传输,咱们的做法是从最后一个td开始往前走,全都给删除掉.uhci_fixup_toggles()来自drivers/usb/host/uhci-q.c:

    373 /*

    374  * Fix up the data toggles for URBs in a queue, when one of them

    375  * terminates early (short transfer, error, or dequeued).

    376  */

    377 static void uhci_fixup_toggles(struct uhci_qh *qh, int skip_first)

    378 {

    379         struct urb_priv *urbp = NULL;

    380         struct uhci_td *td;

    381         unsigned int toggle = qh->initial_toggle;

    382         unsigned int pipe;

    383

    384         /* Fixups for a short transfer start with the second URB in the

    385          * queue (the short URB is the first). */

    386         if (skip_first)

    387                 urbp = list_entry(qh->queue.next, struct urb_priv, node);

    388

    389         /* When starting with the first URB, if the QH element pointer is

    390          * still valid then we know the URB's toggles are okay. */

    391         else if (qh_element(qh) != UHCI_PTR_TERM)

    392                 toggle = 2;

    393

    394         /* Fix up the toggle for the URBs in the queue.  Normally this

    395          * loop won't run more than once: When an error or short transfer

    396          * occurs, the queue usually gets emptied. */

    397         urbp = list_prepare_entry(urbp, &qh->queue, node);

    398         list_for_each_entry_continue(urbp, &qh->queue, node) {

    399

    400                 /* If the first TD has the right toggle value, we don't

    401                  * need to change any toggles in this URB */

    402                 td = list_entry(urbp->td_list.next, struct uhci_td, list);

    403                 if (toggle > 1 || uhci_toggle(td_token(td)) == toggle) {

    404                         td = list_entry(urbp->td_list.prev, struct uhci_td,

    405                                         list);

    406                         toggle = uhci_toggle(td_token(td)) ^ 1;

    407

    408                 /* Otherwise all the toggles in the URB have to be switched */

    409                 } else {

    410                         list_for_each_entry(td, &urbp->td_list, list) {

    411                                 td->token ^= __constant_cpu_to_le32(

    412                                                         TD_TOKEN_TOGGLE);

    413                                 toggle ^= 1;

    414                         }

    415                 }

    416         }

    417

    418         wmb();

    419         pipe = list_entry(qh->queue.next, struct urb_priv, node)->urb->pipe;

    420         usb_settoggle(qh->udev, usb_pipeendpoint(pipe),

    421                         usb_pipeout(pipe), toggle);

    422         qh->needs_fixup = 0;

    423 }

哇赛,这一段美妙的队列操作,足以让我等菜鸟们看得眼花缭乱头晕目眩了.

看这个函数之前,注意两点:

第一,在调用uhci_fixup_toggles之前的那句话,qh->initial_toggle被赋了值,而且还就是post_tdtoggle位取反.

第二,咱们传递进来的第二个参数是1.skip_first1.因此387行会被执行,urbpqhqueue队列中的第二个.因为第一个必然是刚才处理的那个,即那个出现短包问题的urb.

然后397,398行从第二个urbp开始遍历qhqueue队列.首先是获得urbp里的第一个td.注意到toggle要么为1要么为0.(除非skip_first0,咱们执行了392,那么toggle将等于2.但至少此情此景咱们没有走那条路.)如果这个tdtoggle位和qh->initial_toggle相同,即它和那个post_tdtoggle相反,那么td是正确的.咱们不用担心了.直接让td走到td_list的最后一个元素去.然后把toggle置为反.

反之如果tdtoggleqh->initial_toggle不相同,即它和之前那个post_tdtoggle相同,那么说明整个URB中的所有的TDtoggle位都反了,都得翻一次.

最后调用usb_settoggle来设置一次.

最后设置qh->needs_fixup0.

显然,这么说谁都会,关键是得理解,也许现在是时候去理解理解USB世界里的同步问题了.USB spec中是为了实现同步,定义了Data0Data1这两种序列位,如果发送者要发送多个包给接收者,则给每个包编上号,Data0Data1间隔着发送出去.发送方和接受方都维护着一张绪列位,在一次传输开始之前,发送方和接受方的这个序列位必须同步,或者说相同.即要么同为Data0,要么同为Data1.这种机制称之为Data Toggle Synchronization.举例来说,假设一开始双方都是Data0,当接收方成功的接收到了一个包它会把自己的同步位翻转,即所谓的toggle,即它变成了Data1,然后它会发送一个ACK给发送方,告诉对方,俺成功的接收到了你的包,而发送方在接收到这个ACK之后,也会翻转自己的同步位,于是也跟着变成了Data1.下一个包也是一样的做法.所以我们看到uhci_submit_common()函数中没填充一个td,就翻转一次toggle,984行那个”toggle^=1”.同样我们在uhci_submit_control()中也能看到对于toggle的处理.我们回过头去看uhci_submit_control ()879,来看一下我们是如何为控制传输设置toggle位的.

首先是Setup阶段,啥也没做,就让toggle位为0.( A SETUP always uses a DATA0 PID for the data field of the SETUP transaction.— usb spec 2.0, 8.5.3)

其次是数据阶段,在填充每一个td之前翻转toggle.850行那个destination^=TD_TOKEN_TOGGLE,第一次翻转之后toggle位是Data1.

然后是状态阶段,879,我们为状态阶段的toggle位设置为Data1,这也不是凭一种男人的直觉来设置的,而是依据usb spec中规定的来设置的.(A Status stage is delineated by a change in direction of data flow from theprevious stage and always uses a DATA1 PID.-- usb spec 2.0, 8.5.3)

网友易中天品三围问我,那么为何在uhci_submit_common中调用了usb_gettoggle()usb_settoggle,uhci_submit_control中没有调用呢?在回答这个问题之前我倒是先问一下这位网友,易中天老师的下一本书是否该叫做易中天品三点?早在usb-storage中我们就介绍过usb_settoggle这个函数,当时我们说了,struct usb_device这个结构体有这么一个成员toggle[],

    336 struct usb_device {

    337         int             devnum;         /* Address on USB bus */

    338         char            devpath [16];   /* Use in messages: /port/port/... */

    339         enum usb_device_state   state;  /* configured, not attached, etc */

    340         enum usb_device_speed   speed;  /* high/full/low (or error) */

    341

    342         struct usb_tt   *tt;            /* low/full speed dev, highspeed hub */

    343         int             ttport;         /* device port on that tt hub */

    344

    345         unsigned int toggle[2];         /* one bit for each endpoint

    346                                          * ([0] = IN, [1] = OUT) */

    347

这个toggle数组第一个元素是针对IN类型的endpoint,第二个元素是针对OUT类型的endpoint,每个endpoint都在这张表里占有一个bit.于是咱们就可以用它来记录endpointtoggle,以保证传输的同步,但是,实际上在我们这个故事里,真正使用这个数组的只有两种端点,Bulk端点和中断端点,另外两种端点并不需要这个数组,首先,等时端点是不需要使用toggle bits来进行同步的,这是usb spec中规定的,Data Toggle同步对等时传输没有意义.其次,控制传输的toggle位我们上面说了,Setup阶段总是Data0,数据阶段总是从Data1开始,Status阶段总是Data1.usb spec已经为控制传输规定好了,你不得不遵守它,所以就没有必要另外使用这么一个数组来记录端点的toggle位了.这就是为什么操作这个toggle数组的两个函数usb_gettoggle/usb_settoggle不会出现在提交控制urb的函数uhci_submit_control.而对于Bulk传输和中断传输,恰恰是因为每次在设置好一个urb的各个td之后调用了usb_settoggle来设置了这个toggle,下一次为新的urb的第一个td设置toggle位的时候才可以直接调用usb_gettoggle.这样就保证了前一个urbtdtoggle位和后一个urbtdtoggle位刚好相反,即所谓的交叉顺序,这样就保证了和设备内部的toggle位相同步.

了解了这些toggle位的设置之后,再来看我们的这段代码,来看一下这个uhci_fixup_toggles究竟是怎么个fixup.根据我们前面看到的对qh->initial_toggle的赋值可以知道,initial_toggle实际上就是接收到short包的那个tdtoggle位取反,post_tdtoggle取反,(函数uhci_fixup_short_transfer1127),403行咱们所比较的就是第二个urb的第一个tdtoken是否和现在这个一样,如果不一样,我们就把该urb的所有的td都给翻转一下,如果一样,则说明没有问题,但无论哪种情况,我们都要记录toggle本身,因为我们注意到在420,我们最后还调用了usb_settoggle来设置了该管道的toggle位的.那么如何理解这个一样就说明没有问题呢?我们知道,主机控制器处理的TD总是QH中的第一个TD,当然其所属的urb也一定是QH的第一个urb,而且该TDtoggle位是和端点相同步的,假设它们之前都是Data0,那么现在该TD结束之后,端点那边的toggle位就该变成了Data1.另一方面,根据uhci spec,我们知道,如果一个urbTD被检测到了短包,则该urb的剩下的TD就不会被处理了,而下一个urb的第一个TDtoggle得和现在这个urb的这个被处理的TDtoggle相反就说明它的toggle位也是Data1.即它是和端点相同步的.这样我们就可以理直气壮的重新开启下一个urb.反之,如果第一个TD和端点的toggle位相反,就把整个队列的所有的TD都给反一下,这个工程不可谓不浩大,但是没有办法,谁叫设备不争气发送出这种短包来呢,这就叫成长的代价.

另外提一下,uhci_submit_common()函数一样,我们也可以理解为什么在uhci_fixup_toggles最后,420,我们会再次调用usb_settoggle.我们注意一下,403415这一段,toggle的两种赋值,第一种,由于整个队伍是出于正确的同步状况,所以不用改任何一个tdtoggle,404行直接让td等于本urb队列中的最后一个td,然后toggle是它的toggle位取反.而对于整个队伍都得翻转的情况,咱们看到411让每一个td进行翻转,413toggle也跟着一次次的翻转,以保证toggle最终等于最后一个tdtoggle位的翻转.

最后我们再来看一下TD_CTRL_SPD这个flag的使用.这个flag对应于TD4个双字中的第二个双字中的bit29,uhci spec中关于这个bit是这么介绍的:

Short Packet Detect (SPD). 1=Enable. 0=Disable. When a packet has this bit set to 1 and the packet:

1. is an input packet;

2. is in a queue; and

3. successfully completes with an actual length less than the maximum length;

then the TD is marked inactive, the Queue Header is not updated and the USBINT status bit (Status Register) is set at the end of the frame. In addition, if the interrupt is enabled, the interrupt will be sent at the end of the frame.

Note that any error (e.g., babble or FIFO error) prevents the short packet from being reported. The behavior is undefined when this bit is set with output packets or packets outside of queues.

所以,对于IN方向的数据包,如果设置了这个flag,那么再主机控制器读到一个短包之后,它就会触发中断.因此我们注意到uhci_submit_common函数中,951行和952,就对IN管道设置了这个flag.即对于接下来的每一个数据包,我们都会检测一下看是否收到了短包,是的话就及时发送中断向上级汇报.而我们注意到965行我们又取消掉这个flag,因为这是最后一个包,最后一个包当然是有可能是短包的.同样,uhci_submit_control中也是如法炮制,835行设置了TD_CTRL_SPD,即保证数据阶段能够准确的汇报险情,881行又取消掉,因为这已经是状态阶段了,最后一个包当然是允许短包的.

最后我们注意到,uhci_fixup_toggles最后一行我们设置了qh->needs_fixup0.稍后我们会看到对这个变量是否为0的判断.目前我们这个上下文当然就是0.

回到uhci_fixup_short_transfer,一个需要解释的问题是,为何我们要设置qh->element,正如上面我们从uhci spec中摘取过来的那段对SPD的解释中所说的,当遇到短包的时候,qh不会被update,这也是为什么一个TD出现了短包下一个TD就不会被执行的原因.所以这里咱们就需要手工的update这个qh.对于控制传输,qhelement指向了状态传输的那个td,因为我们要让状态阶段重新执行一次,就算是短包也得汇报一下,所以最后返回的是-EINPROGRESS,而对于Bulk/中断传输, td是本urbptd_list中最后一个td(1111行的赋值),element指向了的该tdlink指针,也就是指向了下一个urb.所以最后返回的是0.

到这里我们就很清楚,uhci_fixup_short_transfer()11381144这一段while循环的意义了.把那个有问题的urb的前面那些td统统给删掉,把内存也释放掉.

至此,我们结束了uhci_fixup_short_transfer().因而,uhci_result_common也就结束了.然们回到了uhci_scan_qh,仍然在qh中按照urb一个一个的循环.如果status-EINPROGRESS,则结束循环,继续执行该urb.

没什么故障的话,urb->status应该还是-EINPROGRESS,这是我们最初提交urb的时候设置的,没毛病的话不会改的.于是这里就设置urb->statusstatus,这就是执行之后的结果.

最后1569,既然status不是-EINPROGRESS,那么uhci_giveback_urb被调用.

   1482 /*

   1483  * Finish unlinking an URB and give it back

   1484  */

   1485 static void uhci_giveback_urb(struct uhci_hcd *uhci, struct uhci_qh *qh,

   1486                 struct urb *urb)

   1487 __releases(uhci->lock)

   1488 __acquires(uhci->lock)

   1489 {

   1490         struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;

   1491

   1492         /* When giving back the first URB in an Isochronous queue,

   1493          * reinitialize the QH's iso-related members for the next URB. */

   1494         if (qh->type == USB_ENDPOINT_XFER_ISOC &&

   1495                         urbp->node.prev == &qh->queue &&

   1496                         urbp->node.next != &qh->queue) {

   1497                 struct urb *nurb = list_entry(urbp->node.next,

   1498                                 struct urb_priv, node)->urb;

   1499

   1500                 qh->iso_packet_desc = &nurb->iso_frame_desc[0];

   1501                 qh->iso_frame = nurb->start_frame;

   1502                 qh->iso_status = 0;

   1503         }

   1504

   1505         /* Take the URB off the QH's queue.  If the queue is now empty,

   1506          * this is a perfect time for a toggle fixup. */

   1507         list_del_init(&urbp->node);

   1508         if (list_empty(&qh->queue) && qh->needs_fixup) {

   1509                 usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),

   1510                                 usb_pipeout(urb->pipe), qh->initial_toggle);

   1511                 qh->needs_fixup = 0;

   1512         }

   1513

   1514         uhci_free_urb_priv(uhci, urbp);

   1515

   1516         spin_unlock(&uhci->lock);

   1517         usb_hcd_giveback_urb(uhci_to_hcd(uhci), urb);

   1518         spin_lock(&uhci->lock);

   1519

   1520         /* If the queue is now empty, we can unlink the QH and give up its

   1521          * reserved bandwidth. */

   1522         if (list_empty(&qh->queue)) {

   1523                 uhci_unlink_qh(uhci, qh);

   1524                 if (qh->bandwidth_reserved)

   1525                         uhci_release_bandwidth(uhci, qh);

   1526         }

   1527 }

首先1494行这一段if是针对等时传输的,暂时飘过.

然后把这个urbpqh的队伍中删除掉.如果队列因此就空了,并且needs_fixup设置为了1.那么咱们就调用usb_settoggle.不过咱们这个上下文里needs_fixup0,所以先不管.

然后把urbp的各个td给删除掉,td的内存给释放掉,然后把urbp本身的内存释放掉.

接下来调用usb_hcd_giveback_urb把控制权交回给设备驱动程序.这个函数我们已经不再陌生了.

最后,如果qh这整个队伍已经空了,那么就调用uhci_unlink_qhqh给撤掉.这个函数来自drivers/usb/host/uhci-q.h:

    552 /*

    553  * Take a QH off the hardware schedule

    554  */

    555 static void uhci_unlink_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)

    556 {

    557         if (qh->state == QH_STATE_UNLINKING)

    558                 return;

    559         WARN_ON(qh->state != QH_STATE_ACTIVE || !qh->udev);

    560         qh->state = QH_STATE_UNLINKING;

    561

    562         /* Unlink the QH from the schedule and record when we did it */

    563         if (qh->skel == SKEL_ISO)

    564                 ;

    565         else if (qh->skel < SKEL_ASYNC)

    566                 unlink_interrupt(uhci, qh);

    567         else

    568                 unlink_async(uhci, qh);

    569

    570         uhci_get_current_frame_number(uhci);

    571         qh->unlink_frame = uhci->frame_number;

    572

    573         /* Force an interrupt so we know when the QH is fully unlinked */

    574         if (list_empty(&uhci->skel_unlink_qh->node))

    575                 uhci_set_next_interrupt(uhci);

    576

    577         /* Move the QH from its old list to the end of the unlinking list */

    578         if (qh == uhci->next_qh)

    579                 uhci->next_qh = list_entry(qh->node.next, struct uhci_qh,

    580                                 node);

    581         list_move_tail(&qh->node, &uhci->skel_unlink_qh->node);

    582 }

对于Bulk传输或者控制传输,要调用的是unlink_async().依然是来自drivers/usb/host/uhci-q.c:

    534 /*

    535  * Unlink a period-1 interrupt or async QH from the schedule

    536  */

    537 static void unlink_async(struct uhci_hcd *uhci, struct uhci_qh *qh)

    538 {

    539         struct uhci_qh *pqh;

    540         __le32 link_to_next_qh = qh->link;

    541

    542         pqh = list_entry(qh->node.prev, struct uhci_qh, node);

    543         pqh->link = link_to_next_qh;

    544

    545         /* If this was the old first FSBR QH, link the terminating skeleton

    546          * QH to the next (new first FSBR) QH. */

    547         if (pqh->skel < SKEL_FSBR && qh->skel >= SKEL_FSBR)

    548                 uhci->skel_term_qh->link = link_to_next_qh;

    549         mb();

    550 }

打江山难而毁江山容易.这一点从link_asyncunlink_async这两个函数对比一下就知道了.540,542,543行的结果就是经典的删除队列节点的操作.pqh等于qh的前一个节点,然后让pqhlink等于原来qhlink,这样qh就没有利用价值了,它可以消失在我们眼前了.

然后547行这个if也不难理解,如果刚才这个qh是第一个FSBRqh,那么就令skel_term_qhlink指向下一个qh,因为我们前面说过,skel_term_qh总是要被设置为第一个FSBR qh.

然后调用uhci_get_current_frame_number获得当前的frame,记录在unlink_frame.

然后,调用uhci_set_next_interrupt,来自drivers/usb/host/uhci-q.c:

     28 static void uhci_set_next_interrupt(struct uhci_hcd *uhci)

     29 {

     30         if (uhci->is_stopped)

     31                 mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies);

     32         uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC);

     33 }

这个函数的行为显然是和uhci_clear_next_interrupt相反的.等于是开启中断.

如果这个qhuhci->next_qh,那么就让next_qh顺延至下一个qh.

最后把刚才unlink的这个qh插入到另外一支队伍中去,这支队伍就是uhci->skel_unlink_qh,所有的被unlinkqh都会被招入这支革命中去.很显然这是一支无产阶级革命队伍,因为进来的都是一无所有的qh.

然后uhci_giveback_urb结束了,回到uhci_scan_qh.uhci_cleanup_queue被调用.来自drivers/usb/host/uhci-q.c:

    312 /*

    313  * When a queue is stopped and a dequeued URB is given back, adjust

    314  * the previous TD link (if the URB isn't first on the queue) or

    315  * save its toggle value (if it is first and is currently executing).

    316  *

    317  * Returns 0 if the URB should not yet be given back, 1 otherwise.

    318  */

    319 static int uhci_cleanup_queue(struct uhci_hcd *uhci, struct uhci_qh *qh,

    320                 struct urb *urb)

    321 {

    322         struct urb_priv *urbp = urb->hcpriv;

    323         struct uhci_td *td;

    324         int ret = 1;

    325

    326         /* Isochronous pipes don't use toggles and their TD link pointers

    327          * get adjusted during uhci_urb_dequeue().  But since their queues

    328          * cannot truly be stopped, we have to watch out for dequeues

    329          * occurring after the nominal unlink frame. */

    330         if (qh->type == USB_ENDPOINT_XFER_ISOC) {

    331                 ret = (uhci->frame_number + uhci->is_stopped !=

    332                                 qh->unlink_frame);

    333                 goto done;

    334         }

    335

    336         /* If the URB isn't first on its queue, adjust the link pointer

    337          * of the last TD in the previous URB.  The toggle doesn't need

    338          * to be saved since this URB can't be executing yet. */

    339         if (qh->queue.next != &urbp->node) {

    340                 struct urb_priv *purbp;

    341                 struct uhci_td *ptd;

    342

    343                 purbp = list_entry(urbp->node.prev, struct urb_priv, node);

    344                 WARN_ON(list_empty(&purbp->td_list));

    345                 ptd = list_entry(purbp->td_list.prev, struct uhci_td,

    346                                 list);

    347                 td = list_entry(urbp->td_list.prev, struct uhci_td,

    348                                 list);

    349                 ptd->link = td->link;

    350                 goto done;

    351         }

    352

    353         /* If the QH element pointer is UHCI_PTR_TERM then then currently

    354          * executing URB has already been unlinked, so this one isn't it. */

    355         if (qh_element(qh) == UHCI_PTR_TERM)

    356                 goto done;

    357         qh->element = UHCI_PTR_TERM;

    358

    359         /* Control pipes don't have to worry about toggles */

    360         if (qh->type == USB_ENDPOINT_XFER_CONTROL)

    361                 goto done;

    362

    363         /* Save the next toggle value */

    364         WARN_ON(list_empty(&urbp->td_list));

    365         td = list_entry(urbp->td_list.next, struct uhci_td, list);

    366         qh->needs_fixup = 1;

    367         qh->initial_toggle = uhci_toggle(td_token(td));

    368

    369 done:

    370         return ret;

    371 }

最后,uhci_make_qh_idel被调用,来自drivers/usb/host/uhci-q.c:

    584 /*

    585  * When we and the controller are through with a QH, it becomes IDLE.

    586  * This happens when a QH has been off the schedule (on the unlinking

    587  * list) for more than one frame, or when an error occurs while adding

    588  * the first URB onto a new QH.

    589  */

    590 static void uhci_make_qh_idle(struct uhci_hcd *uhci, struct uhci_qh *qh)

    591 {

    592         WARN_ON(qh->state == QH_STATE_ACTIVE);

    593

    594         if (qh == uhci->next_qh)

    595                 uhci->next_qh = list_entry(qh->node.next, struct uhci_qh,

    596                                 node);

    597         list_move(&qh->node, &uhci->idle_qh_list);

    598         qh->state = QH_STATE_IDLE;

    599

    600         /* Now that the QH is idle, its post_td isn't being used */

    601         if (qh->post_td) {

    602                 uhci_free_td(uhci, qh->post_td);

    603                 qh->post_td = NULL;

    604         }

    605

    606         /* If anyone is waiting for a QH to become idle, wake them up */

    607         if (uhci->num_waiting)

    608                 wake_up_all(&uhci->waitqh);

    609 }

目的就一个,设置qh->stateQH_STATE_IDLE.

uhci_make_qh_idle结束之后,uhci_scan_qh也就结束了,回到了uhci_scan_schedule.

最后判断uhci->skel_unlink_qh领衔的队伍是否为空,如果为空,就调用uhci_clear_next_interrupt清中断,如果不为空,就说明又有无产阶级的qh加入了这支队伍,就调用uhci_set_next_interrupt去产生下一次中断,从而再次把qh->state设置为QH_STATE_IDLE.于是uhci_scan_

schedule也结束了.

于是,uhci_irq也就结束了.

 
原创粉丝点击