天天看點

Linux那些事兒之我是UHCI(5)傳說中的DMA

下一個函數,usb_add_hcd,drivers/usb/core/hcd.c中:

   1548

   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        

   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        

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

   1594

   1595        

   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        

   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        

   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        

   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

     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

     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_alloc和dma_pool_free,而這兩個函數正是與我們說的usb_buffer_alloc以及usb_buffer_free相聯系的.于是我們來看這兩個函數的代碼,來自drivers/usb/core/usb.c:

    568

    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

    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_alloc和hcd_buffer_free,于是進一步跟蹤,來自drivers/usb/core/buffer.c:

     96

     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        

    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_alloc和dma_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_mask為NULL,說明這個主機控制器不支援DMA,那麼使用原始的方法申請記憶體,即kmalloc.然後申請好了就直接傳回,而不會繼續去執行下面的那個dma_pool_alloc函數.同樣的判斷在hcd_buffer_free中也是一樣的,沒有DMA的就直接調用kfree釋放記憶體,而不需要調用dma_pool_free了.

第二,你應該注意到這裡還有另外兩個函數我們根本沒提起, dma_alloc_coherent和dma_free_coherent,這兩個函數也是用來申請DMA記憶體的,但是它們适合申請比較大的記憶體,比如N個page的那種,而DMA池的作用本來就是提供給小打小鬧式的記憶體申請的.目前的USB子系統裡在drivers/usb/core/buffer.c中定義了一個數組:

     25

     26 static const size_t     pool_max [HCD_BUFFER_POOLS] = {

     27        

     30         32,

     31         128,

     32         512,

     33         PAGE_SIZE / 2

     34         

     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中來,繼續往下走. 

繼續閱讀