`
javatome
  • 浏览: 824854 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Linux那些事儿之我是UHCI(5)传说中的DMA

 
阅读更多

下一个函数,usb_add_hcd,drivers/usb/core/hcd.c:

1548 /**

1549 * usb_add_hcd - finish generic HCD structure initialization and register

1550 * @hcd: the usb_hcd structure to initialize

1551 * @irqnum: Interrupt line to allocate

1552 * @irqflags: Interrupt type flags

1553 *

1554 * Finish the remaining parts of generic HCD initialization: allocate the

1555 * buffers of consistent memory, register the bus, request the IRQ line,

1556 * and call the driver's reset() and start() routines.

1557 */

1558 int usb_add_hcd(struct usb_hcd *hcd,

1559 unsigned int irqnum, unsigned long irqflags)

1560 {

1561 int retval;

1562 struct usb_device *rhdev;

1563

1564 dev_info(hcd->self.controller, "%s/n", hcd->product_desc);

1565

1566 set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);

1567

1568 /* HC is in reset state, but accessible. Now do the one-time init,

1569 * bottom up so that hcds can customize the root hubs before khubd

1570 * starts talking to them. (Note, bus id is assigned early too.)

1571 */

1572 if ((retval = hcd_buffer_create(hcd)) != 0) {

1573 dev_dbg(hcd->self.controller, "pool alloc failed/n");

1574 return retval;

1575 }

1576

1577 if ((retval = usb_register_bus(&hcd->self)) < 0)

1578 goto err_register_bus;

1579

1580 if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) {

1581 dev_err(hcd->self.controller, "unable to allocate root hub/n");

1582 retval = -ENOMEM;

1583 goto err_allocate_root_hub;

1584 }

1585 rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH :

1586 USB_SPEED_FULL;

1587 hcd->self.root_hub = rhdev;

1588

1589 /* wakeup flag init defaults to "everything works" for root hubs,

1590 * but drivers can override it in reset() if needed, along with

1591 * recording the overall controller's system wakeup capability.

1592 */

1593 device_init_wakeup(&rhdev->dev, 1);

1594

1595 /* "reset" is misnamed; its role is now one-time init. the controller

1596 * should already have been reset (and boot firmware kicked off etc).

1597 */

1598 if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) {

1599 dev_err(hcd->self.controller, "can't setup/n");

1600 goto err_hcd_driver_setup;

1601 }

1602

1603 /* NOTE: root hub and controller capabilities may not be the same */

1604 if (device_can_wakeup(hcd->self.controller)

1605 && device_can_wakeup(&hcd->self.root_hub->dev))

1606 dev_dbg(hcd->self.controller, "supports USB remote wakeup/n");

1607

1608 /* enable irqs just before we start the controller */

1609 if (hcd->driver->irq) {

1610 snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",

1611 hcd->driver->description, hcd->self.busnum);

1612 if ((retval = request_irq(irqnum, &usb_hcd_irq, irqflags,

1613 hcd->irq_descr, hcd)) != 0) {

1614 dev_err(hcd->self.controller,

1615 "request interrupt %d failed/n", irqnum);

1616 goto err_request_irq;

1617 }

1618 hcd->irq = irqnum;

1619 dev_info(hcd->self.controller, "irq %d, %s 0x%08llx/n", irqnum,

1620 (hcd->driver->flags & HCD_MEMORY) ?

1621 "io mem" : "io base",

1622 (unsigned long long)hcd->rsrc_start);

1623 } else {

1624 hcd->irq = -1;

1625 if (hcd->rsrc_start)

1626 dev_info(hcd->self.controller, "%s 0x%08llx/n",

1627 (hcd->driver->flags & HCD_MEMORY) ?

1628 "io mem" : "io base",

1629 (unsigned long long)hcd->rsrc_start);

1630 }

1631

1632 if ((retval = hcd->driver->start(hcd)) < 0) {

1633 dev_err(hcd->self.controller, "startup error %d/n", retval);

1634 goto err_hcd_driver_start;

1635 }

1636

1637 /* starting here, usbcore will pay attention to this root hub */

1638 rhdev->bus_mA = min(500u, hcd->power_budget);

1639 if ((retval = register_root_hub(hcd)) != 0)

1640 goto err_register_root_hub;

1641

1642 if (hcd->uses_new_polling && hcd->poll_rh)

1643 usb_hcd_poll_rh_status(hcd);

1644 return retval;

1645

1646 err_register_root_hub:

1647 hcd->driver->stop(hcd);

1648 err_hcd_driver_start:

1649 if (hcd->irq >= 0)

1650 free_irq(irqnum, hcd);

1651 err_request_irq:

1652 err_hcd_driver_setup:

1653 hcd->self.root_hub = NULL;

1654 usb_put_dev(rhdev);

1655 err_allocate_root_hub:

1656 usb_deregister_bus(&hcd->self);

1657 err_register_bus:

1658 hcd_buffer_destroy(hcd);

1659 return retval;

1660 }

1566,设置一个flag,至于设了干嘛用,等遇到了再说.

1572,hcd_buffer_create,初始化一个buffer.现在是时候说一说DMA.我们知道一个USB主机控制器控制着一条USB总线,USB主机控制器的一项重要工作是什么呢?在内存和USB总线之间传输数据.这个过程可以使用DMA或者不使用DMA,不使用DMA的方式即所谓的PIO方式.DMA代表着Direct Memory Access,即直接内存访问.那么使用DMA如何做呢?不需要CPU干预对吧,内存总是需要的吧,我比如说我有一个UHCI控制器,我告诉它,内存中某个地方放了一堆数据,你去取吧,然后它就自己去取,取完了它就跟我说一声,告诉我它取完了.

那么在整个USB子系统中是如何处理这些事情的呢?,苦等了这么久,我终于有机会来向你解释这个问题了,现在我终于可以说电视剧中男主角对女主角常说的那句话,你听我解释,你听我解释呀!回去看我们在usb-storage,hub driver,我们调用过一个函数usb_buffer_alloc,当时很多网友问我这个函数究竟是如何处理DMA或不DMA?

关于DMA,合理的做法是先创建一个内存池,然后每次都从池子里要内存.具体说就是先由HCD这边建池子,然后设备驱动那边就直接索取.我们来看代码,来自drivers/usb/core/buffer.c中的hcd_buffer_create,

40 /**

41 * hcd_buffer_create - initialize buffer pools

42 * @hcd: the bus whose buffer pools are to be initialized

43 * Context: !in_interrupt()

44 *

45 * Call this as part of initializing a host controller that uses the dma

46 * memory allocators. It initializes some pools of dma-coherent memory that

47 * will be shared by all drivers using that controller, or returns a negative

48 * errno value on error.

49 *

50 * Call hcd_buffer_destroy() to clean up after using those pools.

51 */

52 int hcd_buffer_create(struct usb_hcd *hcd)

53 {

54 char name[16];

55 int i, size;

56

57 if (!hcd->self.controller->dma_mask)

58 return 0;

59

60 for (i = 0; i < HCD_BUFFER_POOLS; i++) {

61 if (!(size = pool_max [i]))

62 continue;

63 snprintf(name, sizeof name, "buffer-%d", size);

64 hcd->pool[i] = dma_pool_create(name, hcd->self.controller,

65 size, size, 0);

66 if (!hcd->pool [i]) {

67 hcd_buffer_destroy(hcd);

68 return -ENOMEM;

69 }

70 }

71 return 0;

72 }

别的我们先不说,先看64,调用了dma_pool_create函数,这个函数就是真正去创建内存池的函数,或者更准确地讲,创建一个DMA,内核中定义了一个结构体,struct dma_pool,就是专门代表一个DMA池的,而这个函数的返回值就是生成的那个DMA.如果创建失败就调用hcd_buffer_destroy,还是来自同一个文件,

75 /**

76 * hcd_buffer_destroy - deallocate buffer pools

77 * @hcd: the bus whose buffer pools are to be destroyed

78 * Context: !in_interrupt()

79 *

80 * This frees the buffer pools created by hcd_buffer_create().

81 */

82 void hcd_buffer_destroy(struct usb_hcd *hcd)

83 {

84 int i;

85

86 for (i = 0; i < HCD_BUFFER_POOLS; i++) {

87 struct dma_pool *pool = hcd->pool[i];

88 if (pool) {

89 dma_pool_destroy(pool);

90 hcd->pool[i] = NULL;

91 }

92 }

93 }

看得出这里调用的是dma_pool_destroy,其作用不言自明.

那么创建池子和销毁池子的函数我们知道了,如何从池子里索取或者把索取的释放回去呢?对应的两个函数分别是,dma_pool_allocdma_pool_free,而这两个函数正是与我们说的usb_buffer_alloc以及usb_buffer_free相联系的.于是我们来看这两个函数的代码,来自drivers/usb/core/usb.c:

568 /**

569 * usb_buffer_alloc - allocate dma-consistent buffer for URB_NO_xxx_DMA_MAP

570 * @dev: device the buffer will be used with

571 * @size: requested buffer size

572 * @mem_flags: affect whether allocation may block

573 * @dma: used to return DMA address of buffer

574 *

575 * Return value is either null (indicating no buffer could be allocated), or

576 * the cpu-space pointer to a buffer that may be used to perform DMA to the

577 * specified device. Such cpu-space buffers are returned along with the DMA

578 * address (through the pointer provided).

579 *

580 * These buffers are used with URB_NO_xxx_DMA_MAP set in urb->transfer_flags

581 * to avoid behaviors like using "DMA bounce buffers", or tying down I/O

582 * mapping hardware for long idle periods. The implementation varies between

583 * platforms, depending on details of how DMA will work to this device.

584 * Using these buffers also helps prevent cacheline sharing problems on

585 * architectures where CPU caches are not DMA-coherent.

586 *

587 * When the buffer is no longer used, free it with usb_buffer_free().

588 */

589 void *usb_buffer_alloc(

590 struct usb_device *dev,

591 size_t size,

592 gfp_t mem_flags,

593 dma_addr_t *dma

594 )

595 {

596 if (!dev || !dev->bus)

597 return NULL;

598 return hcd_buffer_alloc(dev->bus, size, mem_flags, dma);

599 }

600

601 /**

602 * usb_buffer_free - free memory allocated with usb_buffer_alloc()

603 * @dev: device the buffer was used with

604 * @size: requested buffer size

605 * @addr: CPU address of buffer

606 * @dma: DMA address of buffer

607 *

608 * This reclaims an I/O buffer, letting it be reused. The memory must have

609 * been allocated using usb_buffer_alloc(), and the parameters must match

610 * those provided in that allocation request.

611 */

612 void usb_buffer_free(

613 struct usb_device *dev,

614 size_t size,

615 void *addr,

616 dma_addr_t dma

617 )

618 {

619 if (!dev || !dev->bus)

620 return;

621 if (!addr)

622 return;

623 hcd_buffer_free(dev->bus, size, addr, dma);

624 }

很显然,它们调用的就是hcd_buffer_allochcd_buffer_free,于是进一步跟踪,来自drivers/usb/core/buffer.c:

96 /* sometimes alloc/free could use kmalloc with GFP_DMA, for

97 * better sharing and to leverage mm/slab.c intelligence.

98 */

99

100 void *hcd_buffer_alloc(

101 struct usb_bus *bus,

102 size_t size,

103 gfp_t mem_flags,

104 dma_addr_t *dma

105 )

106 {

107 struct usb_hcd *hcd = bus_to_hcd(bus);

108 int i;

109

110 /* some USB hosts just use PIO */

111 if (!bus->controller->dma_mask) {

112 *dma = ~(dma_addr_t) 0;

113 return kmalloc(size, mem_flags);

114 }

115

116 for (i = 0; i < HCD_BUFFER_POOLS; i++) {

117 if (size <= pool_max [i])

118 return dma_pool_alloc(hcd->pool [i], mem_flags, dma);

119 }

120 return dma_alloc_coherent(hcd->self.controller, size, dma, 0);

121 }

122

123 void hcd_buffer_free(

124 struct usb_bus *bus,

125 size_t size,

126 void *addr,

127 dma_addr_t dma

128 )

129 {

130 struct usb_hcd *hcd = bus_to_hcd(bus);

131 int i;

132

133 if (!addr)

134 return;

135

136 if (!bus->controller->dma_mask) {

137 kfree(addr);

138 return;

139 }

140

141 for (i = 0; i < HCD_BUFFER_POOLS; i++) {

142 if (size <= pool_max [i]) {

143 dma_pool_free(hcd->pool [i], addr, dma);

144 return;

145 }

146 }

147 dma_free_coherent(hcd->self.controller, size, addr, dma);

148 }

看见了吧,最终调用的就是dma_pool_allocdma_pool_free.那么主机控制器到底支持不支持DMA操作呢?看见上面这个dma_mask了么?默认情况下,dma_mask在总线枚举的时候被函数pci_scan_device中设置为了0xffffffff.struct device结构体有一个成员u64 *dma_mask,如果一个PCI设备不能支持DMA,那么应该在probe函数中调用pci_set_dma_mask把这个dma_mask设置为NULL.不过一个没有精神分裂症的PCI设备通常是支持DMA.这个掩码更多的作用是,比如你的设备只能支持24位的寻址,那你就得通过设置dma_mask来告诉PCI,你需要把dma_mask设置为0x00ffffff.因为标准的PCI设备都是32位的寻址的,所以标准情况就是设置的0xffffffff.不过开发者们的建议是不要直接使用这些数字,而是使用它们定义在include/linux/dma-mapping.h中的这些宏:

16 #define DMA_64BIT_MASK 0xffffffffffffffffULL

17 #define DMA_48BIT_MASK 0x0000ffffffffffffULL

18 #define DMA_40BIT_MASK 0x000000ffffffffffULL

19 #define DMA_39BIT_MASK 0x0000007fffffffffULL

20 #define DMA_32BIT_MASK 0x00000000ffffffffULL

21 #define DMA_31BIT_MASK 0x000000007fffffffULL

22 #define DMA_30BIT_MASK 0x000000003fffffffULL

23 #define DMA_29BIT_MASK 0x000000001fffffffULL

24 #define DMA_28BIT_MASK 0x000000000fffffffULL

25 #define DMA_24BIT_MASK 0x0000000000ffffffULL

不过目前在drivers/usb/目录下面没有哪个驱动会调用pci_set_dma_mask,因为现代总线上的大部分设备都能够处理32位地址,换句话说大家的设备都还算是正经,但如果你们家生产出来一个不伦不类的设备那么你就别忘了在probe阶段用pci_set_dma_mask设置一下,否则你就甭指望设备能够正确的进行DMA传输.关于这个函数的使用,可以参考drivers/net下面的那些驱动,很多网卡驱动都调用了这个函数,虽然其中很多其实就是设置32.

要不,总结一下?以上这几个DMA函数我们就不细讲了.但需要对某些地方单独拿出来讲.

第一,hcd_buffer_alloc函数中,111,判断,如果dma_maskNULL,说明这个主机控制器不支持DMA,那么使用原始的方法申请内存,kmalloc.然后申请好了就直接返回,而不会继续去执行下面的那个dma_pool_alloc函数.同样的判断在hcd_buffer_free中也是一样的,没有DMA的就直接调用kfree释放内存,而不需要调用dma_pool_free.

第二,你应该注意到这里还有另外两个函数我们根本没提起, dma_alloc_coherentdma_free_coherent,这两个函数也是用来申请DMA内存的,但是它们适合申请比较大的内存,比如Npage的那种,DMA池的作用本来就是提供给小打小闹式的内存申请的.当前的USB子系统里在drivers/usb/core/buffer.c中定义了一个数组:

25 /* FIXME tune these based on pool statistics ... */

26 static const size_t pool_max [HCD_BUFFER_POOLS] = {

27 /* platforms without dma-friendly caches might need to

28 * prevent cacheline sharing...

29 */

30 32,

31 128,

32 512,

33 PAGE_SIZE / 2

34 /* bigger --> allocate pages */

35 };

HCD_BUFFER_POOLS这个宏的值为4.结合hcd_buffer_alloc函数里面那个循环来看,可以知道,你要是申请个32个字节以内,128个字节以内,512个字节以内,或者最多二分之一个PAGE_SIZE以内的,就直接使用这个内存池了,否则的话,就得用那个dma_alloc_coherent.释放的时候也一样.

第三,struct usb_hcd结构体有这么一个成员,struct dma_pool *pool [HCD_BUFFER_POOLS], 这个数组的值是在hcd_buffer_create中赋上的,即当时以这个pool_max为模型创建了4个池子,所以hcd_buffer_alloc里就可以这样用.

第四,至于像dma_pool_create/dma_pool_alloc/dma_alloc_coherent这些函数具体怎么实现的我想任何一个写设备驱动程序的都不用关心吧,倒是我有两个同事会比较感兴趣,因为他们是研究内存管理的.

Ok,讲完了hcd_buffer_create让我们还是回到usb_add_hcd中来,继续往下走.

分享到:
评论

相关推荐

    Linux那些事儿之我是UHCI

    写一下UHCI吧,也顺便怀念一下Intel,以及Intel的那几个女同事们,好久没联系了,你们可好? UHCI是 Intel提出来的.虽然离开 Intel一年多了,但我总觉得也许有一天我还会回到 Intel.所 以关于 Intel的东西,我多少会关注...

    Linux那些事儿

    Linux那些事儿之我是UHCI Linux那些事儿之我是EHCI控制器 Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是Block层 Linux那些事儿之我是Sysfs 今天本人将9个单独的文档整理出来,做成了一...

    Linux那些事儿1-9合集

    由复旦fudan_abc写的,风趣的文笔,深入浅...linux那些事儿之我是UHCI Linux那些事儿之我是EHCI主机控制器 Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是Block层 linux那些事儿之我是Sysfs

    Linux那些事儿系列.rar

    》包括《Linux那些事儿之我是Hub》、《Linux那些事儿之我是Sysfs》《Linux那些事儿之我是UHCI》、《Linux那些事儿之我是USB core》、《Linux那些事儿之我是U盘》,令人叹为观止的一个linux系列书籍。只能说,江山代...

    linux那些事儿(EHCI Block SCSI Sysfs PCI USB U 盘 UHCI Hub)

    Linux那些事儿之我是EHCI主机控制器.pdf Linux那些事儿之我是Block层.pdf Linux那些事儿之我是SCSI硬盘.pdf Linux那些事儿之我是Sysfs.pdf Linux那些事儿之我是PCI.pdf ...Linux那些事儿之我是UHCI.pdf

    linux的那些事儿全集

    Linux那些事儿之我是Block层 Linux那些事儿之我是EHCI主机控制器 Linux那些事儿之我是Hub Linux那些事儿之我是PCI ...Linux那些事儿之我是UHCI Linux那些事儿之我是USB core Linux那些事儿之我是U盘

    linux那些事全集

    Linux那些事儿之我是UHCI Linux那些事儿之我是Sysfs Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是PCI Linux那些事儿之我是Hub Linux那些事儿之我是EHCI主机控制器 Linux那些事儿之我是Block层

    Linux那些事儿.rar

    Linux那些Linux那些事儿之我是SCSI硬盘,Linux那些事儿之我是Block层,Linux那些事儿之我是EHCI主机控制器,Linux那些事儿之我是HUB,Linux那些事儿之我是PCI,Linux那些事儿之我是Sysfs,Linux那些事儿之我是UHCI,...

    Linux那些事儿系列

    本人整理的fudan_abc的专栏中以完结的文章,在此向原作者表示感谢,给...内容包括:linux那些事儿之我是U盘,linux那些事儿之我是USB,linux那些事儿之我是HUB,linux那些事儿之我是UHCI,linux那些事儿之我是Sysfs。

    Linux那些事儿之我是XXX全集.rar

    Linux那些事儿之我是XXX全集 包含USB core U盘 UHCI PCI SCSI硬盘 Block Hub EHCI 。 想学驱动的童鞋,不妨看看。该书主要是进行源代码的分析

    Linux那些事儿 系列之2 Block+EHCI+PCI+SCSI

    Linux那些事儿之我是Block层.pdf Linux那些事儿之我是EHCI主机控制器.pdf Linux那些事儿之我是PCI.pdf Linux那些事儿之我是SCSI硬盘.pdf 注: 之前有人已经上传了《Linux那些事儿 系列》,其已经包含了:hub,sysfs...

    linux那些事儿之我是USB.zip

    里面包含Linux那些事的九个文档,Block层,ECHI主机控制,HUB,PCI,SCSI硬盘,Sysfs,UHCI,USB+core,U盘等九个文档,内容详细,而且全面都有书签,适合系统学习!

    Linux那些事系列

    Linux那些事儿之我是Hub Linux那些事儿之我是Sysfs Linux那些事儿之我是UHCI Linux那些事儿之我是USB core Linux那些事儿之我是U盘 Linux那些事之我是HUB1

    微信生活缴费商业项目标准版

    Linux那些Linux那些事儿之我是SCSI硬盘,Linux那些事儿之我是Block层,Linux那些事儿之我是EHCI主机控制器,Linux那些事儿之我是HUB,Linux那些事儿之我是PCI,Linux那些事儿之我是Sysfs,Linux那些事儿之我是UHCI,...

    usb那些事的全集

    usb那些事的全集,包含: 1 Linux那些事儿之我是Block层 ...5 Linux那些事儿之我是SCSI硬盘 6 Linux那些事儿之我是Sysfs 7 Linux那些事儿之我是UHCI 8 Linux那些事儿之我是USB+core 9 Linux那些事儿之我是U盘

    LINUX那些事儿 linux经典之作

    到了这一阶段基本上就能回答我是U盘中所有的困惑了.(其中Block层由于过于复杂和规模庞大,主要是举例和个别函数分析,主要是辅助另外几个模块进行分析.) 以上每一阶段代码跳跃性会逐渐增强,即开始会讲的细,之后会讲的...

    Linux那些事儿[完整版]自己整理

    我是U盘 说的是2.6.10的内核 我是Sysfs 说的是2.6.10的内核 戏说USB 说的是2.6.22的内核 我是Hub/UHCI/EHCI 说的是2.6.22.1的内核

    Linux那些事儿(包括Hub, Sysfs, UHCI, USB, U盘5个部分)

    一个很经典的东东,里面的很多东西都很有启发性,值得看看

    linux那些事儿.rar

    linux那些事儿,Block层,EHCI主机控制器,Hub,PCI,SCSI硬盘,Sysfs,UHCI,USB core,U盘等文档。

Global site tag (gtag.js) - Google Analytics