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

想当年咱们在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顾名思义,就是检查咱们的队列有没有前进,如果一个TD从ACTIVE变成了非ACTIVE,这就说明队列前进了,因为主机控制器只有执行完一个TD才会把一个TD的ACTIVE取消,然后它就会前进去获取下一个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,只不过戴了一个安全套而已,其作用都是遍历urbp的td_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这个双字中,bit16到bit23那一段被称为Status,即这几位表示的是状态,uhci_status_bits则是为了获得这几个bits,把ctrl_sts和0xF60000相与得到的是bit17到bit23,因为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_MASK是0x7FF,我们注意到TD的定义中,04-07h中,bit0到bit10这么个11个bits被称之为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比期望值要小,那么首先判断是不是在urb的transfer_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,实际上就是对应于刚才那个ret为1的情况,即传输长度小于预期长度,这种情况就调用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等于本urb的td_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_td的toggle位取反.

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

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

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

最后调用usb_settoggle来设置一次.

最后设置qh->needs_fixup为0.

显然,这么说谁都会,关键是得理解,也许现在是时候去理解理解USB世界里的同步问题了.USB spec中是为了实现同步,定义了Data0和Data1这两种序列位,如果发送者要发送多个包给接收者,则给每个包编上号,让Data0和Data1间隔着发送出去.发送方和接受方都维护着一张绪列位,在一次传输开始之前,发送方和接受方的这个序列位必须同步,或者说相同.即要么同为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.于是咱们就可以用它来记录endpoint的toggle,以保证传输的同步,但是,实际上在我们这个故事里,真正使用这个数组的只有两种端点,即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.这样就保证了前一个urb的td的toggle位和后一个urb的td的toggle位刚好相反,即所谓的交叉顺序,这样就保证了和设备内部的toggle位相同步.

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

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

最后我们再来看一下TD_CTRL_SPD这个flag的使用.这个flag对应于TD那4个双字中的第二个双字中的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_fixup为0.稍后我们会看到对这个变量是否为0的判断.目前我们这个上下文当然就是0.

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

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

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

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

最后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是针对等时传输的,暂时飘过.

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

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

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

最后,如果qh这整个队伍已经空了,那么就调用uhci_unlink_qh把qh给撤掉.这个函数来自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_async和unlink_async这两个函数对比一下就知道了.540行,542行,543行的结果就是经典的删除队列节点的操作.让pqh等于qh的前一个节点,然后让pqh的link等于原来qh的link,这样qh就没有利用价值了,它可以消失在我们眼前了.

然后547行这个if也不难理解,如果刚才这个qh是第一个FSBR的qh,那么就令skel_term_qh的link指向下一个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相反的.等于是开启中断.

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

最后把刚才unlink的这个qh插入到另外一支队伍中去,这支队伍就是uhci->skel_unlink_qh,所有的被unlink的qh都会被招入这支革命中去.很显然这是一支无产阶级革命队伍,因为进来的都是一无所有的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->state为QH_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也就结束了.

获致幸福的不二法门是珍视你所拥有的、遗忘你所没有的

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

相关文章:

你感兴趣的文章:

标签云: