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中来,继续往下走. 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值