Linux那些事儿之我是UHCI(10)一个函数引发的故事(一)

接下来,1632,下一个函数,driver->start被调用.对于咱们的uhci_driver,start指针指向的是uhci_start函数,经过了人间大炮一级准备,二级准备之后,这个函数基本上就算介于三级准备和发射之间了.这个函数算是整个故事中最重要的一个函数,理解它是理解整个uhci的关键.来自drivers/usb/host/uhci-hcd.c:

538 /*

539 * Allocate a frame list, and then setup the skeleton

540 *

541 * The hardware doesn't really know any difference

542 * in the queues, but the order does matter for the

543 * protocols higher up. The order in which the queues

544 * are encountered by the hardware is:

545 *

546 * - All isochronous events are handled before any

547 * of the queues. We don't do that here, because

548 * we'll create the actual TD entries on demand.

549 * - The first queue is the high-period interrupt queue.

550 * - The second queue is the period-1 interrupt and async

551 * (low-speed control, full-speed control, then bulk) queue.

552 * - The third queue is the terminating bandwidth reclamation queue,

553 * which contains no members, loops back to itself, and is present

554 * only when FSBR is on and there are no full-speed control or bulk QHs.

555 */

556 static int uhci_start(struct usb_hcd *hcd)

557 {

558 struct uhci_hcd *uhci = hcd_to_uhci(hcd);

559 int retval = -EBUSY;

560 int i;

561 struct dentry *dentry;

562

563 hcd->uses_new_polling = 1;

564

565 spin_lock_init(&uhci->lock);

566 setup_timer(&uhci->fsbr_timer, uhci_fsbr_timeout,

567 (unsigned long) uhci);

568 INIT_LIST_HEAD(&uhci->idle_qh_list);

569 init_waitqueue_head(&uhci->waitqh);

570

571 if (DEBUG_CONFIGURED) {

572 dentry = debugfs_create_file(hcd->self.bus_name,

573 S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root,

574 uhci, &uhci_debug_operations);

575 if (!dentry) {

576 dev_err(uhci_dev(uhci), "couldn't create uhci "

577 "debugfs entry/n");

578 retval = -ENOMEM;

579 goto err_create_debug_entry;

580 }

581 uhci->dentry = dentry;

582 }

583

584 uhci->frame = dma_alloc_coherent(uhci_dev(uhci),

585 UHCI_NUMFRAMES * sizeof(*uhci->frame),

586 &uhci->frame_dma_handle, 0);

587 if (!uhci->frame) {

588 dev_err(uhci_dev(uhci), "unable to allocate "

589 "consistent memory for frame list/n");

590 goto err_alloc_frame;

591 }

592 memset(uhci->frame, 0, UHCI_NUMFRAMES * sizeof(*uhci->frame));

593

594 uhci->frame_cpu = kcalloc(UHCI_NUMFRAMES, sizeof(*uhci->frame_cpu),

595 GFP_KERNEL);

596 if (!uhci->frame_cpu) {

597 dev_err(uhci_dev(uhci), "unable to allocate "

598 "memory for frame pointers/n");

599 goto err_alloc_frame_cpu;

600 }

601

602 uhci->td_pool = dma_pool_create("uhci_td", uhci_dev(uhci),

603 sizeof(struct uhci_td), 16, 0);

604 if (!uhci->td_pool) {

605 dev_err(uhci_dev(uhci), "unable to create td dma_pool/n");

606 goto err_create_td_pool;

607 }

608

609 uhci->qh_pool = dma_pool_create("uhci_qh", uhci_dev(uhci),

610 sizeof(struct uhci_qh), 16, 0);

611 if (!uhci->qh_pool) {

612 dev_err(uhci_dev(uhci), "unable to create qh dma_pool/n");

613 goto err_create_qh_pool;

614 }

615

616 uhci->term_td = uhci_alloc_td(uhci);

617 if (!uhci->term_td) {

618 dev_err(uhci_dev(uhci), "unable to allocate terminating TD/n");

619 goto err_alloc_term_td;

620 }

621

622 for (i = 0; i < UHCI_NUM_SKELQH; i++) {

623 uhci->skelqh[i] = uhci_alloc_qh(uhci, NULL, NULL);

624 if (!uhci->skelqh[i]) {

625 dev_err(uhci_dev(uhci), "unable to allocate QH/n");

626 goto err_alloc_skelqh;

627 }

628 }

629

630 /*

631 * 8 Interrupt queues; link all higher int queues to int1 = async

632 */

633 for (i = SKEL_ISO + 1; i < SKEL_ASYNC; ++i)

634 uhci->skelqh[i]->link = LINK_TO_QH(uhci->skel_async_qh);

635 uhci->skel_async_qh->link = UHCI_PTR_TERM;

636 uhci->skel_term_qh->link = LINK_TO_QH(uhci->skel_term_qh);

637

638 /* This dummy TD is to work around a bug in Intel PIIX controllers */

639 uhci_fill_td(uhci->term_td, 0, uhci_explen(0) |

640 (0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);

641 uhci->term_td->link = UHCI_PTR_TERM;

642 uhci->skel_async_qh->element = uhci->skel_term_qh->element =

643 LINK_TO_TD(uhci->term_td);

644

645 /*

646 * Fill the frame list: make all entries point to the proper

647 * interrupt queue.

648 */

649 for (i = 0; i < UHCI_NUMFRAMES; i++) {

650

651 /* Only place we don't use the frame list routines */

652 uhci->frame[i] = uhci_frame_skel_link(uhci, i);

653 }

654

655 /*

656 * Some architectures require a full mb() to enforce completion of

657 * the memory writes above before the I/O transfers in configure_hc().

658 */

659 mb();

660

661 configure_hc(uhci);

662 uhci->is_initialized = 1;

663 start_rh(uhci);

664 return 0;

665

666 /*

667 * error exits:

668 */

669 err_alloc_skelqh:

670 for (i = 0; i < UHCI_NUM_SKELQH; i++) {

671 if (uhci->skelqh[i])

672 uhci_free_qh(uhci, uhci->skelqh[i]);

673 }

674

675 uhci_free_td(uhci, uhci->term_td);

676

677 err_alloc_term_td:

678 dma_pool_destroy(uhci->qh_pool);

679

680 err_create_qh_pool:

681 dma_pool_destroy(uhci->td_pool);

682

683 err_create_td_pool:

684 kfree(uhci->frame_cpu);

685

686 err_alloc_frame_cpu:

687 dma_free_coherent(uhci_dev(uhci),

688 UHCI_NUMFRAMES * sizeof(*uhci->frame),

689 uhci->frame, uhci->frame_dma_handle);

690

691 err_alloc_frame:

692 debugfs_remove(uhci->dentry);

693

694 err_create_debug_entry:

695 return retval;

696 }

这个函数简直就是一个大杂烩,所有变态的代码全都集中在这一个函数里边了.我始终觉得我们看到的这些函数,谈论的这些代码,远比唐笑打武警的话题来得枯燥乏味,但是,天下没有轻松的成功,成功,要付代价.在这个浮躁的社会中,也许很难再有人能够静下心来看代码了.都说现在的程序员是做一天程序撞一天钟,我们这些读程序的又何尝不是这种心态呢?

面对这越来越枯燥的代码,我想我们不能再像过去那样分析了.记得一位泡妞大师曾经教育过我,读懂Linux内核代码和读懂女人的心一样,不是不可能,只是需要你多下点功夫,多用点时间在她们身上,多多沟通,多多了解,增进两个人的感情.这套理论我觉得很有道理,所以我想从现在开始我决定多用点时间多下点功夫来读代码,要和代码多多沟通才能对它有多多了解.所以我决定用出我的杀手锏,kdb.也许你不熟悉kdb,没有关系,我只是通过kdb来展示一些函数调用关系.我主要会使用kdbbp命令来设置一些断点,通过kdbbt命令来显示函数调用堆栈.很显然,了解了函数的调用关系对读懂代码是很有帮助的.

首先我们在加载uhci-hcd的时候设置断点uhci_start.于是我们会在uhci_start被调用的时候进入kdb,bt命令看一下Stacktraceback.

kdb>bt

Stack traceback for pid 3498

0xdd5ac550 3498 3345 1 0 R 0xdd5ac720 *modprobe

esp eip Function (args)

0xd4a89d40 0xc0110000 lapic_resume+0x185

0xd4a89d48 0xe0226e41 [uhci_hcd]uhci_start

0xd4a89d54 0xe0297132 [usbcore]usb_add_hcd+0x3fb

0xd4a89da0 0xe02a0a9b [usbcore]usb_hcd_pci_probe+0x263

其实Stack中还有更多的函数,篇幅原因,跟咱们这里没有太多关系的函数就不列出来了.但是至少从这个traceback中我们可以很清楚的知道我们目前我们的处境,我们在uhci_start,而调用它的函数是usb_add_hcd,后者则是被usb_hcd_pci_probe函数调用,usb_hcd_pci_probe函数正是我们故事的真正开始处.

但单单是kdb还不足以显示我们的决心.有人说,女人如画,不同的画中有不同的风景,代码也是如此,左看右看,上看下看,角度不同风景各异.对于uhci-hcd这样变态的模块,用常规的方法恐怕是很难看明白了,我们必须引入一种新的方法,图解法.uhci_start函数开始,我们将接触到一堆乱七八糟的庞大的复杂的数据结构,这些数据结构的关系如果不能理解,那么我们很难读懂这代码.所以我决定把uhci_start作为实验田,通过图解法把众多复杂的结构众多的链表之间的关系给描绘出来.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值